diff --git a/src/cpus/m68k/decode.rs b/src/cpus/m68k/decode.rs index e52231e..b8b8909 100644 --- a/src/cpus/m68k/decode.rs +++ b/src/cpus/m68k/decode.rs @@ -29,6 +29,8 @@ const OPCG_RESERVED1: u8 = 0xA; const OPCG_RESERVED2: u8 = 0xF; +pub type Register = u8; + #[derive(Copy, Clone, Debug, PartialEq)] pub enum Sign { Signed, @@ -53,6 +55,13 @@ pub enum XRegister { Address(u8), } +#[derive(Copy, Clone, Debug, PartialEq)] +pub enum RegOrImmediate { + DReg(u8), + Immediate(u8), +} + + #[derive(Copy, Clone, Debug, PartialEq)] pub enum ControlRegister { VBR, @@ -88,13 +97,13 @@ pub enum Condition { #[derive(Copy, Clone, Debug, PartialEq)] pub enum Target { Immediate(u32), - DirectDReg(u8), - DirectAReg(u8), - IndirectAReg(u8), - IndirectARegInc(u8), - IndirectARegDec(u8), - IndirectARegOffset(u8, i32), - IndirectARegXRegOffset(u8, XRegister, i32, u8, Size), + DirectDReg(Register), + DirectAReg(Register), + IndirectAReg(Register), + IndirectARegInc(Register), + IndirectARegDec(Register), + IndirectARegOffset(Register, i32), + IndirectARegXRegOffset(Register, XRegister, i32, u8, Size), IndirectMemory(u32), IndirectPCOffset(i32), IndirectPCXRegOffset(XRegister, i32, u8, Size), @@ -116,41 +125,49 @@ pub enum Instruction { BCLR(Target, Target, Size), BSET(Target, Target, Size), BTST(Target, Target, Size), + BFCHG(Target, RegOrImmediate, RegOrImmediate), + BFCLR(Target, RegOrImmediate, RegOrImmediate), + BFEXTS(Target, RegOrImmediate, RegOrImmediate, Register), + BFEXTU(Target, RegOrImmediate, RegOrImmediate, Register), + BFFFO(Target, RegOrImmediate, RegOrImmediate, Register), + BFINS(Register, Target, RegOrImmediate, RegOrImmediate), + BFSET(Target, RegOrImmediate, RegOrImmediate), + BFTST(Target, RegOrImmediate, RegOrImmediate), BKPT(u8), - CHK(Target, u8, Size), + CHK(Target, Register, Size), CLR(Target, Size), CMP(Target, Target, Size), - CMPA(Target, u8, Size), + CMPA(Target, Register, Size), - DBcc(Condition, u8, i16), + DBcc(Condition, Register, i16), DIV(Target, Target, Size, Sign), EOR(Target, Target, Size), EORtoCCR(u8), EORtoSR(u16), EXG(Target, Target), - EXT(u8, Size, Size), + EXT(Register, Size, Size), ILLEGAL, JMP(Target), JSR(Target), - LEA(Target, u8), - LINK(u8, i16), + LEA(Target, Register), + LINK(Register, i16), LSd(Target, Target, Size, ShiftDirection), MOVE(Target, Target, Size), - MOVEA(Target, u8, Size), + MOVEA(Target, Register, Size), MOVEfromSR(Target), MOVEtoSR(Target), MOVEfromCCR(Target), MOVEtoCCR(Target), MOVEC(Target, ControlRegister, Direction), MOVEM(Target, Size, Direction, u16), - MOVEP(u8, Target, Size, Direction), - MOVEQ(u8, u8), + MOVEP(Register, Target, Size, Direction), + MOVEQ(u8, Register), MOVEUSP(Target, Direction), MUL(Target, Target, Size, Sign), @@ -179,14 +196,14 @@ pub enum Instruction { Scc(Condition, Target), STOP(u16), SUB(Target, Target, Size), - SWAP(u8), + SWAP(Register), TAS(Target), TST(Target, Size), TRAP(u8), TRAPV, - UNLK(u8), + UNLK(Register), } @@ -686,14 +703,44 @@ impl M68kDecoder { }, None => { let target = self.decode_lower_effective_address(memory, ins, Some(Size::Word))?; - let count = Target::Immediate(1); - match (ins & 0x0600) >> 9 { - 0b00 => Ok(Instruction::ASd(count, target, Size::Word, dir)), - 0b01 => Ok(Instruction::LSd(count, target, Size::Word, dir)), - 0b10 => Ok(Instruction::ROXd(count, target, Size::Word, dir)), - 0b11 => Ok(Instruction::ROd(count, target, Size::Word, dir)), - _ => return Err(Error::processor(Exceptions::IllegalInstruction as u32)), + let count = Target::Immediate(1); + if (ins & 0x800) == 0 { + match (ins & 0x0600) >> 9 { + 0b00 => Ok(Instruction::ASd(count, target, Size::Word, dir)), + 0b01 => Ok(Instruction::LSd(count, target, Size::Word, dir)), + 0b10 => Ok(Instruction::ROXd(count, target, Size::Word, dir)), + 0b11 => Ok(Instruction::ROd(count, target, Size::Word, dir)), + _ => return Err(Error::processor(Exceptions::IllegalInstruction as u32)), + } + } else if self.cputype > M68kType::MC68020 { + // Bitfield instructions (MC68020+) + let ext = self.read_instruction_word(memory)?; + let reg = ((ext & 0x7000) >> 12) as u8; + + let offset = match (ext & 0x0800) == 0 { + true => RegOrImmediate::Immediate(((ext & 0x07C0) >> 6) as u8), + false => RegOrImmediate::DReg(((ext & 0x01C0) >> 6) as u8), + }; + + let width = match (ext & 0x0020) == 0 { + true => RegOrImmediate::Immediate((ext & 0x001F) as u8), + false => RegOrImmediate::DReg((ext & 0x0007) as u8), + }; + + match (ins & 0x0700) >> 8 { + 0b010 => Ok(Instruction::BFCHG(target, offset, width)), + 0b100 => Ok(Instruction::BFCLR(target, offset, width)), + 0b011 => Ok(Instruction::BFEXTS(target, offset, width, reg)), + 0b001 => Ok(Instruction::BFEXTU(target, offset, width, reg)), + 0b101 => Ok(Instruction::BFFFO(target, offset, width, reg)), + 0b111 => Ok(Instruction::BFINS(reg, target, offset, width)), + 0b110 => Ok(Instruction::BFSET(target, offset, width)), + 0b000 => Ok(Instruction::BFTST(target, offset, width)), + _ => return Err(Error::processor(Exceptions::IllegalInstruction as u32)), + } + } else { + return Err(Error::processor(Exceptions::IllegalInstruction as u32)); } }, } @@ -732,7 +779,7 @@ impl M68kDecoder { let xreg = if (brief_extension & 0x8000) == 0 { XRegister::Data(xreg_num) } else { XRegister::Address(xreg_num) }; let size = if (brief_extension & 0x0800) == 0 { Size::Word } else { Size::Long }; let scale = ((brief_extension & 0x0600) >> 9) as u8; - let use_full = (brief_extension & 0x0100) == 1; + let use_full = (brief_extension & 0x0100) != 0; if !use_full { let displacement = sign_extend_to_long((brief_extension & 0x00FF) as u32, Size::Byte); @@ -1062,7 +1109,10 @@ impl fmt::Display for Instruction { Direction::ToTarget => write!(f, "movem{}\t{}, {}", size, fmt_movem_mask(*mask), target), Direction::FromTarget => write!(f, "movem{}\t{}, {}", size, target, fmt_movem_mask(*mask)), }, - //Instruction::MOVEP(reg, target, size, dir), + Instruction::MOVEP(reg, target, size, dir) => match dir { + Direction::ToTarget => write!(f, "movep{}\t%d{}, {}", size, reg, target), + Direction::FromTarget => write!(f, "movep{}\t{}, %d{}", size, target, reg), + }, Instruction::MOVEQ(value, reg) => write!(f, "moveq\t#{:02x}, %d{}", value, reg), Instruction::MOVEUSP(target, dir) => match dir { Direction::ToTarget => write!(f, "movel\t%usp, {}", target), diff --git a/src/cpus/m68k/execute.rs b/src/cpus/m68k/execute.rs index 4b39945..01e8173 100644 --- a/src/cpus/m68k/execute.rs +++ b/src/cpus/m68k/execute.rs @@ -12,7 +12,9 @@ use super::decode::{ Condition, ShiftDirection, ControlRegister, + Register, XRegister, + RegOrImmediate, sign_extend_to_long }; @@ -78,7 +80,9 @@ impl M68k { Status::Running => { match self.cycle_one(system) { Ok(()) => Ok(()), - Err(Error { err: ErrorType::Processor, native, .. }) => { + //Err(Error { err: ErrorType::Processor, native, .. }) => { + // TODO temporary: we are passing illegal instructions upward in order to fix them + Err(Error { err: ErrorType::Processor, native, .. }) if native != Exceptions::IllegalInstruction as u32 => { self.exception(system, native as u8)?; Ok(()) }, @@ -152,8 +156,6 @@ impl M68k { if self.debugger.use_tracing { self.decoder.dump_decoded(system); - // TODO for debugging temporarily - self.dump_state(system); } if self.debugger.use_debugger { @@ -249,6 +251,65 @@ impl M68k { value = value | mask; self.set_target_value(system, target, value, size)?; }, + Instruction::BFCHG(target, offset, width) => { + let (offset, width) = self.get_bit_field_args(offset, width); + let mask = get_bit_field_mask(offset, width); + let value = self.get_target_value(system, target, Size::Long)?; + let field = value & mask; + self.set_bit_field_test_flags(field, get_bit_field_msb(offset)); + self.set_target_value(system, target, (value & !mask) | (!field & mask), Size::Long)?; + }, + Instruction::BFCLR(target, offset, width) => { + let (offset, width) = self.get_bit_field_args(offset, width); + let mask = get_bit_field_mask(offset, width); + let value = self.get_target_value(system, target, Size::Long)?; + let field = value & mask; + self.set_bit_field_test_flags(field, get_bit_field_msb(offset)); + self.set_target_value(system, target, value & !mask, Size::Long)?; + }, + Instruction::BFEXTS(target, offset, width, reg) => { + let (offset, width) = self.get_bit_field_args(offset, width); + let mask = get_bit_field_mask(offset, width); + let value = self.get_target_value(system, target, Size::Long)?; + let field = value & mask; + self.set_bit_field_test_flags(field, get_bit_field_msb(offset)); + + let right_offset = 32 - offset - width; + let mut ext = 0; + for i in 0..(offset + right_offset) { + ext = (ext >> 1) | 0x80000000; + } + self.state.d_reg[reg as usize] = (field >> right_offset) | ext; + }, + Instruction::BFEXTU(target, offset, width, reg) => { + let (offset, width) = self.get_bit_field_args(offset, width); + let mask = get_bit_field_mask(offset, width); + let value = self.get_target_value(system, target, Size::Long)?; + let field = value & mask; + self.set_bit_field_test_flags(field, get_bit_field_msb(offset)); + self.state.d_reg[reg as usize] = field >> (32 - offset - width); + }, + //Instruction::BFFFO(target, offset, width, reg) => { + //}, + //Instruction::BFINS(reg, target, offset, width) => { + //}, + Instruction::BFSET(target, offset, width) => { + let (offset, width) = self.get_bit_field_args(offset, width); + let mask = get_bit_field_mask(offset, width); + let value = self.get_target_value(system, target, Size::Long)?; + let field = value & mask; + self.set_bit_field_test_flags(field, get_bit_field_msb(offset)); + self.set_target_value(system, target, value | mask, Size::Long)?; + }, + Instruction::BFTST(target, offset, width) => { + let (offset, width) = self.get_bit_field_args(offset, width); + let mask = get_bit_field_mask(offset, width); + let value = self.get_target_value(system, target, Size::Long)?; + let field = value & mask; + self.set_bit_field_test_flags(field, get_bit_field_msb(offset)); + }, + //Instruction::BKPT(u8) => { + //}, Instruction::CLR(target, size) => { self.set_target_value(system, target, 0, size)?; // Clear flags except Zero flag @@ -722,6 +783,22 @@ impl M68k { Ok(addr) } + pub fn get_bit_field_args(&self, offset: RegOrImmediate, width: RegOrImmediate) -> (u32, u32) { + let offset = self.get_reg_or_immediate(offset); + let mut width = self.get_reg_or_immediate(width) % 32; + if width == 0 { + width = 32; + } + (offset, width) + } + + fn get_reg_or_immediate(&self, value: RegOrImmediate) -> u32 { + match value { + RegOrImmediate::DReg(reg) => self.state.d_reg[reg as usize], + RegOrImmediate::Immediate(value) => value as u32, + } + } + fn get_control_reg_mut(&mut self, control_reg: ControlRegister) -> &mut u32 { match control_reg { ControlRegister::VBR => &mut self.state.vbr, @@ -734,7 +811,7 @@ impl M68k { } #[inline(always)] - fn get_a_reg_mut(&mut self, reg: u8) -> &mut u32 { + fn get_a_reg_mut(&mut self, reg: Register) -> &mut u32 { if reg == 7 { if self.is_supervisor() { &mut self.state.msp } else { &mut self.state.usp } } else { @@ -809,6 +886,16 @@ impl M68k { mask } + fn set_bit_field_test_flags(&mut self, field: u32, msb_mask: u32) { + let mut flags = 0x0000; + if (field & msb_mask) != 0 { + flags |= Flags::Negative as u16; + } + if field == 0 { + flags |= Flags::Zero as u16; + } + self.state.sr = (self.state.sr & 0xFFF0) | flags; + } fn get_current_condition(&self, cond: Condition) -> bool { match cond { @@ -959,3 +1046,16 @@ fn get_msb_mask(value: u32, size: Size) -> u32 { } } +fn get_bit_field_mask(offset: u32, width: u32) -> u32 { + let mut mask = 0; + for i in 0..width { + mask = (mask >> 1) | 0x80000000; + } + mask >> offset +} + +fn get_bit_field_msb(offset: u32) -> u32 { + 0x80000000 >> offset +} + + diff --git a/todo.txt b/todo.txt index dd1e093..10daf00 100644 --- a/todo.txt +++ b/todo.txt @@ -1,22 +1,19 @@ -* can you make Bus be an AddressableDeviceBox so that the CPU can have a ref to it instead of calling system.get_bus() - the alternative is having system.get_bus() take an argument or something to figure out what bus to return of multiple, for multibus systems -* can you eventually make this all configurable via a config file? - +* can you eventually make the system connections all configurable via a config file? * test using mpsc to pass messages with the tty IO thread, and test if it's slower than what you have now * make it possible to break out of the current execution, into the debugger, by using a certain keystroke -* add support for MC68020+ indexing modes -* add support for MMU -* add support for FPU + +* add support for MC68020+ indexing modes, implement the full extension word for MC68020+ * make tests for each instruction -* unimplemented: ABCD, ADDX, BKPT, CHK, EXG, ILLEGAL, MOVEfromCCR, MOVEP, RTR, RTD, SBCD, SUBX +* unimplemented: ABCD, ADDX, BFFFO, BFINS, BKPT, CHK, EXG, ILLEGAL, MOVEfromCCR, MOVEP, RTR, RTD, SBCD, SUBX * undecoded: ADDX, SUBX * modify execution for >=MC68020: DIVSL, DIVUL, LINK, MOVEM, MULSL, MULUL, RTM, TRAPcc, UNPK -* implement the full extension word for MC68020+ +* >=MC68020 undecoded & unimplemented: CALLM, CAS, CAS2, CHK2, CMP2, PACK -* >=MC68020 instructions: BFCHG, BFCLR, BFEXTS, BFEXTU, BFFFO, BFINS, BFSET, BFTST, CALLM, CAS, CAS2, CHK2, CMP2, PACK +* add support for MMU +* add support for FPU * Coprocessor instructions: cpBcc, cpDBcc, cpGEN, cpScc, cpTRAPcc