diff --git a/src/cpus/m68k/decode.rs b/src/cpus/m68k/decode.rs index 7887c99..a64029c 100644 --- a/src/cpus/m68k/decode.rs +++ b/src/cpus/m68k/decode.rs @@ -10,6 +10,8 @@ use super::instructions::{ Direction, ShiftDirection, XRegister, + BaseRegister, + IndexRegister, RegOrImmediate, ControlRegister, Condition, @@ -108,7 +110,7 @@ impl M68kDecoder { let dir = if (ins & 0x0800) == 0 { Direction::FromTarget } else { Direction::ToTarget }; let size = if (ins & 0x0040) == 0 { Size::Word } else { Size::Long }; let offset = sign_extend_to_long(self.read_instruction_word(memory)? as u32, Size::Word); - Ok(Instruction::MOVEP(dreg, Target::IndirectARegOffset(areg, offset), size, dir)) + Ok(Instruction::MOVEP(dreg, Target::IndirectRegOffset(BaseRegister::AReg(areg), None, offset), size, dir)) } else if (ins & 0x0100) == 0x0100 || (ins & 0x0F00) == 0x0800 { let bitnum = if (ins & 0x0100) == 0x0100 { Target::DirectDReg(get_high_reg(ins)) @@ -606,25 +608,53 @@ impl M68kDecoder { self.get_mode_as_target(memory, mode, reg, size) } + fn get_extension_displacement(&mut self, memory: &mut dyn Addressable, select: u16) -> Result { + let result = match select { + 0b00 | 0b01 => 0, + 0b10 => sign_extend_to_long(self.read_instruction_word(memory)? as u32, Size::Word), + 0b11 => self.read_instruction_long(memory)? as i32, + _ => return Err(Error::processor(Exceptions::IllegalInstruction as u32)), + }; + Ok(result) + } + fn decode_extension_word(&mut self, memory: &mut dyn Addressable, areg: Option) -> Result { let brief_extension = self.read_instruction_word(memory)?; + let use_full = (brief_extension & 0x0100) != 0; + + // Decode Index Register let xreg_num = ((brief_extension & 0x7000) >> 12) as u8; - let xreg = if (brief_extension & 0x8000) == 0 { XRegister::Data(xreg_num) } else { XRegister::Address(xreg_num) }; + let xreg = if (brief_extension & 0x8000) == 0 { XRegister::DReg(xreg_num) } else { XRegister::AReg(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) != 0; + let index_reg = IndexRegister { xreg, scale, size }; if !use_full { let displacement = sign_extend_to_long((brief_extension & 0x00FF) as u32, Size::Byte); match areg { - Some(areg) => Ok(Target::IndirectARegXRegOffset(areg, xreg, displacement, scale, size)), - None => Ok(Target::IndirectPCXRegOffset(xreg, displacement, scale, size)), + Some(areg) => Ok(Target::IndirectRegOffset(BaseRegister::AReg(areg), Some(index_reg), displacement)), + None => Ok(Target::IndirectRegOffset(BaseRegister::PC, Some(index_reg), displacement)), } } else if self.cputype >= M68kType::MC68020 { - let use_base = (brief_extension & 0x0080) == 0; + let use_base_reg = (brief_extension & 0x0080) == 0; let use_index = (brief_extension & 0x0040) == 0; + let use_sub_indirect = (brief_extension & 0x0007) != 0; + let pre_not_post = (brief_extension & 0x0004) == 0; - panic!("Not Implemented"); + let opt_base_reg = match (use_base_reg, areg) { + (false, _) => BaseRegister::None, + (true, None) => BaseRegister::PC, + (true, Some(areg)) => BaseRegister::AReg(areg), + }; + let opt_index_reg = if use_index { Some(index_reg) } else { None }; + let base_disp = self.get_extension_displacement(memory, (brief_extension & 0x0030) >> 4)?; + let outer_disp = self.get_extension_displacement(memory, brief_extension & 0x0003)?; + + match (use_sub_indirect, pre_not_post) { + (false, _) => Ok(Target::IndirectRegOffset(opt_base_reg, opt_index_reg, base_disp)), + (true, true) => Ok(Target::IndirectMemoryPreindexed(opt_base_reg, opt_index_reg, base_disp, outer_disp)), + (true, false) => Ok(Target::IndirectMemoryPostindexed(opt_base_reg, opt_index_reg, base_disp, outer_disp)), + } } else { Err(Error::processor(Exceptions::IllegalInstruction as u32)) } @@ -638,8 +668,8 @@ impl M68kDecoder { 0b011 => Target::IndirectARegInc(reg), 0b100 => Target::IndirectARegDec(reg), 0b101 => { - let data = sign_extend_to_long(self.read_instruction_word(memory)? as u32, Size::Word); - Target::IndirectARegOffset(reg, data) + let displacement = sign_extend_to_long(self.read_instruction_word(memory)? as u32, Size::Word); + Target::IndirectRegOffset(BaseRegister::AReg(reg), None, displacement) }, 0b110 => { self.decode_extension_word(memory, Some(reg))? @@ -655,8 +685,8 @@ impl M68kDecoder { Target::IndirectMemory(value) }, 0b010 => { - let data = sign_extend_to_long(self.read_instruction_word(memory)? as u32, Size::Word); - Target::IndirectPCOffset(data) + let displacement = sign_extend_to_long(self.read_instruction_word(memory)? as u32, Size::Word); + Target::IndirectRegOffset(BaseRegister::PC, None, displacement) }, 0b011 => { self.decode_extension_word(memory, None)? diff --git a/src/cpus/m68k/execute.rs b/src/cpus/m68k/execute.rs index 8b1f7c9..336c29a 100644 --- a/src/cpus/m68k/execute.rs +++ b/src/cpus/m68k/execute.rs @@ -10,6 +10,8 @@ use super::instructions::{ Direction, ShiftDirection, XRegister, + BaseRegister, + IndexRegister, RegOrImmediate, ControlRegister, Condition, @@ -389,16 +391,16 @@ impl M68k { //Instruction::ILLEGAL => { //}, Instruction::JMP(target) => { - self.state.pc = self.get_target_address(target)?; + self.state.pc = self.get_target_address(system, target)?; }, Instruction::JSR(target) => { self.push_long(system, self.state.pc)?; let sp = *self.get_stack_pointer_mut(); self.debugger.stack_tracer.push_return(sp); - self.state.pc = self.get_target_address(target)?; + self.state.pc = self.get_target_address(system, target)?; }, Instruction::LEA(target, reg) => { - let value = self.get_target_address(target)?; + let value = self.get_target_address(system, target)?; let addr = self.get_a_reg_mut(reg); *addr = value; }, @@ -472,7 +474,7 @@ impl M68k { // TODO moving words requires a sign extension to 32 bits if size != Size::Long { return Err(Error::new("Unsupported size in MOVEM instruction")); } - let mut addr = self.get_target_address(target)?; + let mut addr = self.get_target_address(system, target)?; if dir == Direction::ToTarget { let mut mask = mask; for i in (0..8).rev() { @@ -567,7 +569,7 @@ impl M68k { self.state.sr = self.state.sr | value; }, Instruction::PEA(target) => { - let value = self.get_target_address(target)?; + let value = self.get_target_address(system, target)?; self.push_long(system, value)?; }, //Instruction::RESET => { @@ -697,25 +699,29 @@ impl M68k { *addr -= size.in_bytes(); get_address_sized(system, *addr as Address, size) }, - Target::IndirectARegOffset(reg, offset) => { - let addr = self.get_a_reg_mut(reg); - get_address_sized(system, (*addr).wrapping_add(offset as u32) as Address, size) + + Target::IndirectRegOffset(base_reg, index_reg, displacement) => { + let base_value = self.get_base_reg_value(base_reg); + let index_value = self.get_index_reg_value(&index_reg); + get_address_sized(system, base_value.wrapping_add(displacement as u32).wrapping_add(index_value as u32) as Address, size) }, - Target::IndirectARegXRegOffset(reg, xreg, offset, scale, target_size) => { - let index = sign_extend_to_long(self.get_x_reg_value(xreg), target_size) << scale; - let addr = self.get_a_reg_mut(reg); - get_address_sized(system, (*addr).wrapping_add(offset as u32).wrapping_add(index as u32) as Address, size) + Target::IndirectMemoryPreindexed(base_reg, index_reg, base_disp, outer_disp) => { + let base_value = self.get_base_reg_value(base_reg); + let index_value = self.get_index_reg_value(&index_reg); + let intermediate = get_address_sized(system, base_value.wrapping_add(base_disp as u32).wrapping_add(index_value as u32) as Address, Size::Long)?; + get_address_sized(system, intermediate.wrapping_add(outer_disp as u32) as Address, size) + }, + Target::IndirectMemoryPostindexed(base_reg, index_reg, base_disp, outer_disp) => { + let base_value = self.get_base_reg_value(base_reg); + let index_value = self.get_index_reg_value(&index_reg); + let intermediate = get_address_sized(system, base_value.wrapping_add(base_disp as u32) as Address, Size::Long)?; + get_address_sized(system, intermediate.wrapping_add(index_value as u32).wrapping_add(outer_disp as u32) as Address, size) }, Target::IndirectMemory(addr) => { get_address_sized(system, addr as Address, size) }, - Target::IndirectPCOffset(offset) => { - get_address_sized(system, (self.decoder.start + 2).wrapping_add(offset as u32) as Address, size) - }, - Target::IndirectPCXRegOffset(xreg, offset, scale, target_size) => { - let index = sign_extend_to_long(self.get_x_reg_value(xreg), target_size) << scale; - get_address_sized(system, (self.decoder.start + 2).wrapping_add(offset as u32).wrapping_add(index as u32) as Address, size) - }, + + _ => return Err(Error::new(&format!("Unimplemented addressing target: {:?}", target))), } } @@ -740,14 +746,22 @@ impl M68k { *addr -= size.in_bytes(); set_address_sized(system, *addr as Address, value, size)?; }, - Target::IndirectARegOffset(reg, offset) => { - let addr = self.get_a_reg_mut(reg); - set_address_sized(system, (*addr).wrapping_add(offset as u32) as Address, value, size)?; + Target::IndirectRegOffset(base_reg, index_reg, displacement) => { + let base_value = self.get_base_reg_value(base_reg); + let index_value = self.get_index_reg_value(&index_reg); + set_address_sized(system, base_value.wrapping_add(displacement as u32).wrapping_add(index_value as u32) as Address, value, size)?; }, - Target::IndirectARegXRegOffset(reg, xreg, offset, scale, target_size) => { - let index = sign_extend_to_long(self.get_x_reg_value(xreg), target_size) << scale; - let addr = self.get_a_reg_mut(reg); - set_address_sized(system, (*addr).wrapping_add(offset as u32).wrapping_add(index as u32) as Address, value, size)?; + Target::IndirectMemoryPreindexed(base_reg, index_reg, base_disp, outer_disp) => { + let base_value = self.get_base_reg_value(base_reg); + let index_value = self.get_index_reg_value(&index_reg); + let intermediate = get_address_sized(system, base_value.wrapping_add(base_disp as u32).wrapping_add(index_value as u32) as Address, Size::Long)?; + set_address_sized(system, intermediate.wrapping_add(outer_disp as u32) as Address, value, size); + }, + Target::IndirectMemoryPostindexed(base_reg, index_reg, base_disp, outer_disp) => { + let base_value = self.get_base_reg_value(base_reg); + let index_value = self.get_index_reg_value(&index_reg); + let intermediate = get_address_sized(system, base_value.wrapping_add(base_disp as u32) as Address, Size::Long)?; + set_address_sized(system, intermediate.wrapping_add(index_value as u32).wrapping_add(outer_disp as u32) as Address, value, size); }, Target::IndirectMemory(addr) => { set_address_sized(system, addr as Address, value, size)?; @@ -757,28 +771,29 @@ impl M68k { Ok(()) } - pub fn get_target_address(&mut self, target: Target) -> Result { + pub fn get_target_address(&mut self, system: &System, target: Target) -> Result { let addr = match target { Target::IndirectAReg(reg) | Target::IndirectARegInc(reg) | Target::IndirectARegDec(reg) => *self.get_a_reg_mut(reg), - Target::IndirectARegOffset(reg, offset) => { - let addr = self.get_a_reg_mut(reg); - (*addr).wrapping_add(offset as u32) + Target::IndirectRegOffset(base_reg, index_reg, displacement) => { + let base_value = self.get_base_reg_value(base_reg); + let index_value = self.get_index_reg_value(&index_reg); + base_value.wrapping_add(displacement as u32).wrapping_add(index_value as u32) }, - Target::IndirectARegXRegOffset(reg, xreg, offset, scale, target_size) => { - let index = sign_extend_to_long(self.get_x_reg_value(xreg), target_size) << scale; - let addr = self.get_a_reg_mut(reg); - (*addr).wrapping_add(offset as u32).wrapping_add(index as u32) + Target::IndirectMemoryPreindexed(base_reg, index_reg, base_disp, outer_disp) => { + let base_value = self.get_base_reg_value(base_reg); + let index_value = self.get_index_reg_value(&index_reg); + let intermediate = get_address_sized(system, base_value.wrapping_add(base_disp as u32).wrapping_add(index_value as u32) as Address, Size::Long)?; + intermediate.wrapping_add(outer_disp as u32) + }, + Target::IndirectMemoryPostindexed(base_reg, index_reg, base_disp, outer_disp) => { + let base_value = self.get_base_reg_value(base_reg); + let index_value = self.get_index_reg_value(&index_reg); + let intermediate = get_address_sized(system, base_value.wrapping_add(base_disp as u32) as Address, Size::Long)?; + intermediate.wrapping_add(index_value as u32).wrapping_add(outer_disp as u32) }, Target::IndirectMemory(addr) => { addr }, - Target::IndirectPCOffset(offset) => { - (self.decoder.start + 2).wrapping_add(offset as u32) - }, - Target::IndirectPCXRegOffset(xreg, offset, scale, target_size) => { - let index = sign_extend_to_long(self.get_x_reg_value(xreg), target_size) << scale; - (self.decoder.start + 2).wrapping_add(offset as u32).wrapping_add(index as u32) - }, _ => return Err(Error::new(&format!("Invalid addressing target: {:?}", target))), }; Ok(addr) @@ -800,6 +815,31 @@ impl M68k { } } + fn get_x_reg_value(&self, xreg: XRegister) -> u32 { + match xreg { + XRegister::DReg(reg) => self.state.d_reg[reg as usize], + XRegister::AReg(reg) => self.state.a_reg[reg as usize], + } + } + + fn get_base_reg_value(&self, base_reg: BaseRegister) -> u32 { + match base_reg { + BaseRegister::None => 0, + BaseRegister::PC => self.decoder.start + 2, + BaseRegister::AReg(reg) if reg == 7 => if self.is_supervisor() { self.state.msp } else { self.state.usp }, + BaseRegister::AReg(reg) => self.state.a_reg[reg as usize], + } + } + + fn get_index_reg_value(&self, index_reg: &Option) -> i32 { + match index_reg { + None => 0, + Some(IndexRegister { xreg, scale, size }) => { + sign_extend_to_long(self.get_x_reg_value(*xreg), *size) << scale + } + } + } + fn get_control_reg_mut(&mut self, control_reg: ControlRegister) -> &mut u32 { match control_reg { ControlRegister::VBR => &mut self.state.vbr, @@ -820,13 +860,6 @@ impl M68k { } } - fn get_x_reg_value(&self, xreg: XRegister) -> u32 { - match xreg { - XRegister::Data(reg) => self.state.d_reg[reg as usize], - XRegister::Address(reg) => self.state.a_reg[reg as usize], - } - } - #[inline(always)] fn is_supervisor(&self) -> bool { self.state.sr & (Flags:: Supervisor as u16) != 0 diff --git a/src/cpus/m68k/instructions.rs b/src/cpus/m68k/instructions.rs index 5e95be2..bf4b719 100644 --- a/src/cpus/m68k/instructions.rs +++ b/src/cpus/m68k/instructions.rs @@ -31,8 +31,22 @@ pub enum ShiftDirection { #[derive(Copy, Clone, Debug, PartialEq)] pub enum XRegister { - Data(u8), - Address(u8), + DReg(u8), + AReg(u8), +} + +#[derive(Copy, Clone, Debug, PartialEq)] +pub enum BaseRegister { + None, + PC, + AReg(u8), +} + +#[derive(Copy, Clone, Debug, PartialEq)] +pub struct IndexRegister { + pub xreg: XRegister, + pub scale: u8, + pub size: Size, } #[derive(Copy, Clone, Debug, PartialEq)] @@ -41,11 +55,11 @@ pub enum RegOrImmediate { Immediate(u8), } - #[derive(Copy, Clone, Debug, PartialEq)] pub enum ControlRegister { VBR, } + #[derive(Copy, Clone, Debug, PartialEq)] pub enum Condition { True, @@ -74,11 +88,10 @@ pub enum Target { IndirectAReg(Register), IndirectARegInc(Register), IndirectARegDec(Register), - IndirectARegOffset(Register, i32), - IndirectARegXRegOffset(Register, XRegister, i32, u8, Size), + IndirectRegOffset(BaseRegister, Option, i32), + IndirectMemoryPreindexed(BaseRegister, Option, i32, i32), + IndirectMemoryPostindexed(BaseRegister, Option, i32, i32), IndirectMemory(u32), - IndirectPCOffset(i32), - IndirectPCXRegOffset(XRegister, i32, u8, Size), } #[derive(Clone, Debug, PartialEq)] @@ -267,12 +280,36 @@ impl fmt::Display for ControlRegister { impl fmt::Display for XRegister { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { - XRegister::Data(reg) => write!(f, "d{}", reg), - XRegister::Address(reg) => write!(f, "a{}", reg), + XRegister::DReg(reg) => write!(f, "d{}", reg), + XRegister::AReg(reg) => write!(f, "a{}", reg), } } } +impl fmt::Display for BaseRegister { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + BaseRegister::None => Ok(()), + BaseRegister::PC => write!(f, "%pc + "), + BaseRegister::AReg(reg) => write!(f, "%a{} + ", reg), + } + } +} + +fn fmt_index_disp(index: &Option) -> String { + match index { + Some(index) => { + let mut result = format!("%{}", index.xreg); + if index.scale != 0 { + result += &format!("<< {}", index.scale); + } + result += " + "; + result + }, + None => "".to_string(), + } +} + impl fmt::Display for Target { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { @@ -282,17 +319,19 @@ impl fmt::Display for Target { Target::IndirectAReg(reg) => write!(f, "(%a{})", reg), Target::IndirectARegInc(reg) => write!(f, "(%a{})+", reg), Target::IndirectARegDec(reg) => write!(f, "-(%a{})", reg), - Target::IndirectARegOffset(reg, offset) => write!(f, "(%a{} + #{:04x})", reg, offset), - Target::IndirectARegXRegOffset(reg, xreg, offset, scale, _) => { - let scale_str = if *scale != 0 { format!("<< {}", scale) } else { "".to_string() }; - write!(f, "(%a{} + %{} + #{:04x}{})", reg, xreg, offset, scale_str) + Target::IndirectRegOffset(base_reg, index_reg, offset) => { + let index_str = fmt_index_disp(index_reg); + write!(f, "({}{}#{:04x})", base_reg, index_str, offset) + }, + Target::IndirectMemoryPreindexed(base_reg, index_reg, base_disp, outer_disp) => { + let index_str = fmt_index_disp(index_reg); + write!(f, "([{}{}#{:08x}] + #{:08x})", base_reg, index_str, base_disp, outer_disp) + }, + Target::IndirectMemoryPostindexed(base_reg, index_reg, base_disp, outer_disp) => { + let index_str = fmt_index_disp(index_reg); + write!(f, "([{}#{:08x}]{} + #{:08x})", base_reg, base_disp, index_str, outer_disp) }, Target::IndirectMemory(value) => write!(f, "(#{:08x})", value), - Target::IndirectPCOffset(offset) => write!(f, "(%pc + #{:04x})", offset), - Target::IndirectPCXRegOffset(xreg, offset, scale, _) => { - let scale_str = if *scale != 0 { format!("<< {}", scale) } else { "".to_string() }; - write!(f, "(%pc + %{} + #{:04x}{})", xreg, offset, scale_str) - }, } } } diff --git a/todo.txt b/todo.txt index 10daf00..ba21cf4 100644 --- a/todo.txt +++ b/todo.txt @@ -1,4 +1,7 @@ +* rearrange the order of Target::IndirectARegXReg... +* should you make a new type for the index, c/w proper Display formatter + * 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