From 10e905674b6ed3356ef4d5acbb6db93920fb87b7 Mon Sep 17 00:00:00 2001 From: transistor Date: Sat, 2 Oct 2021 21:59:28 -0700 Subject: [PATCH] Added MUL, DIV, NEG, DBcc, and Scc instructions, and fixed issue with ADD/SUB flags With ADDA, SUBA, and ADDQ/SUBQ when the target is an address register, the condition flags should not be changed, but the code was changing them, which caused problems. I've fixed it by making the ADD/SUB executions check for an address target and will not update flags in that case. This should only occur when the actual instruction was an ADDA or ADDQ with an address register target --- src/cpus/m68k/decode.rs | 49 +++++---- src/cpus/m68k/execute.rs | 207 ++++++++++++++++++++++++++------------- src/cpus/m68k/mod.rs | 8 +- src/cpus/m68k/tests.rs | 12 +++ src/main.rs | 14 ++- src/memory.rs | 1 + 6 files changed, 191 insertions(+), 100 deletions(-) diff --git a/src/cpus/m68k/decode.rs b/src/cpus/m68k/decode.rs index 940af3d..73dbaac 100644 --- a/src/cpus/m68k/decode.rs +++ b/src/cpus/m68k/decode.rs @@ -115,7 +115,7 @@ pub enum Instruction { CMP(Target, Target, Size), CMPA(Target, u8, Size), - DBcc(Condition, i16), + DBcc(Condition, u8, i16), DIV(Target, Target, Size, Sign), EOR(Target, Target, Size), @@ -165,7 +165,7 @@ pub enum Instruction { RTS, //SBCD - //Scc + Scc(Condition, Target), STOP(u16), SUB(Target, Target, Size), SWAP(u8), @@ -252,12 +252,13 @@ impl M68kDecoder { } else { let size = get_size(ins); - let target = self.decode_lower_effective_address(space, ins, size)?; let data = match size { + Some(Size::Byte) => (self.read_instruction_word(space)? as u32 & 0xFF), + Some(Size::Word) => self.read_instruction_word(space)? as u32, Some(Size::Long) => self.read_instruction_long(space)?, - Some(_) => self.read_instruction_word(space)? as u32, None => return Err(Error::processor(ERR_ILLEGAL_INSTRUCTION)), }; + let target = self.decode_lower_effective_address(space, ins, size)?; match optype { 0b0000 => Ok(Instruction::OR(Target::Immediate(data), target, size.unwrap())), @@ -337,8 +338,8 @@ impl M68kDecoder { if mode == 0b000 { Ok(Instruction::EXT(get_low_reg(ins), size)) } else { - let target = self.decode_lower_effective_address(space, ins, None)?; let data = self.read_instruction_word(space)?; + let target = self.decode_lower_effective_address(space, ins, None)?; let dir = if (ins & 0x0400) == 0 { Direction::ToTarget } else { Direction::FromTarget }; Ok(Instruction::MOVEM(target, size, dir, data)) } @@ -417,21 +418,33 @@ impl M68kDecoder { } }, OPCG_ADDQ_SUBQ => { - let size = match get_size(ins) { - Some(size) => size, - None => return Err(Error::processor(ERR_ILLEGAL_INSTRUCTION)), - }; + match get_size(ins) { + Some(size) => { + let target = self.decode_lower_effective_address(space, ins, Some(size))?; + let mut data = ((ins & 0x0E00) >> 9) as u32; + if data == 0 { + data = 8; + } - let target = self.decode_lower_effective_address(space, ins, Some(size))?; - let mut data = ((ins & 0x0E00) >> 9) as u32; - if data == 0 { - data = 8; - } + if (ins & 0x0100) == 0 { + Ok(Instruction::ADD(Target::Immediate(data), target, size)) + } else { + Ok(Instruction::SUB(Target::Immediate(data), target, size)) + } + }, + None => { + let mode = get_low_mode(ins); + let condition = get_condition(ins); - if (ins & 0x0100) == 0 { - Ok(Instruction::ADD(Target::Immediate(data), target, size)) - } else { - Ok(Instruction::SUB(Target::Immediate(data), target, size)) + if mode == 0b001 { + let reg = get_low_reg(ins); + let disp = self.read_instruction_word(space)? as i16; + Ok(Instruction::DBcc(condition, reg, disp)) + } else { + let target = self.decode_lower_effective_address(space, ins, Some(Size::Byte))?; + Ok(Instruction::Scc(condition, target)) + } + }, } }, OPCG_BRANCH => { diff --git a/src/cpus/m68k/execute.rs b/src/cpus/m68k/execute.rs index 20117a1..3642edb 100644 --- a/src/cpus/m68k/execute.rs +++ b/src/cpus/m68k/execute.rs @@ -8,6 +8,7 @@ use super::decode::{ Instruction, Target, Size, + Sign, Direction, Condition, ShiftDirection, @@ -140,7 +141,7 @@ impl MC68010 { } } - pub(crate) fn decode_next(&mut self, space: &mut AddressSpace) -> Result<(), Error> { + pub fn decode_next(&mut self, space: &mut AddressSpace) -> Result<(), Error> { self.check_breakpoints(); self.decoder = M68kDecoder::decode_at(space, self.state.pc)?; @@ -162,23 +163,16 @@ impl MC68010 { Ok(()) } - pub(crate) fn execute_current(&mut self, space: &mut AddressSpace) -> Result<(), Error> { + pub fn execute_current(&mut self, space: &mut AddressSpace) -> Result<(), Error> { match self.decoder.instruction { Instruction::ADD(src, dest, size) => { let value = self.get_target_value(space, src, size)?; let existing = self.get_target_value(space, dest, size)?; - let (result, overflow) = match size { - Size::Byte => { - let (result, overflow) = (existing as u8).overflowing_add(value as u8); - (result as u32, overflow) - }, - Size::Word => { - let (result, overflow) = (existing as u16).overflowing_add(value as u16); - (result as u32, overflow) - }, - Size::Long => existing.overflowing_add(value), - }; - self.set_compare_flags(result, size, overflow); + let (result, carry) = overflowing_add_sized(existing, value, size); + match dest { + Target::DirectAReg(_) => { }, + _ => self.set_compare_flags(result, size, carry, get_overflow(existing, value, result, size)), + } self.set_target_value(space, dest, result, size)?; }, Instruction::AND(src, dest, size) => { @@ -200,7 +194,7 @@ impl MC68010 { for _ in 0..count { pair = shift_operation(pair.0, size, shift_dir, true); } - self.set_compare_flags(pair.0, size, false); + self.set_logic_flags(pair.0, size); if pair.1 { self.state.sr |= FLAGS_EXTEND | FLAGS_CARRY; } @@ -256,17 +250,38 @@ impl MC68010 { Instruction::CMP(src, dest, size) => { let value = self.get_target_value(space, src, size)?; let existing = self.get_target_value(space, dest, size)?; - let result = self.subtract_sized_with_flags(existing, value, size); + let (result, carry) = overflowing_sub_sized(existing, value, size); + self.set_compare_flags(result, size, carry, get_overflow(existing, value, result, size)); }, Instruction::CMPA(src, reg, size) => { let value = self.get_target_value(space, src, size)?; let existing = sign_extend_to_long(*self.get_a_reg_mut(reg), size) as u32; - let result = self.subtract_sized_with_flags(existing, value, Size::Long); + let (result, carry) = overflowing_sub_sized(existing, value, Size::Long); + self.set_compare_flags(result, size, carry, get_overflow(existing, value, result, Size::Long)); + }, + Instruction::DBcc(cond, reg, offset) => { + let condition_true = self.get_current_condition(cond); + if !condition_true { + let next = (get_value_sized(self.state.d_reg[reg as usize], Size::Word) as u16) as i16 - 1; + set_value_sized(&mut self.state.d_reg[reg as usize], next as u32, Size::Word); + if next != -1 { + self.state.pc = (self.decoder.start + 2).wrapping_add(offset as u32); + } + } + }, + Instruction::DIV(src, dest, size, sign) => { + if size == Size::Long { + return Err(Error::new("Unsupported multiplication size")); + } + + let value = self.get_target_value(space, src, size)?; + let existing = self.get_target_value(space, dest, Size::Long)?; + let result = match sign { + Sign::Signed => ((existing as i16 % value as i16) as u32) << 16 | (0xFFFF & (existing as i16 / value as i16) as u32), + Sign::Unsigned => ((existing as u16 % value as u16) as u32) << 16 | (0xFFFF & (existing as u16 / value as u16) as u32), + }; + self.set_target_value(space, dest, result, Size::Long)?; }, - //Instruction::DBcc(Condition, u16) => { - //}, - //Instruction::DIV(Target, Target, Size, Sign) => { - //}, Instruction::EOR(src, dest, size) => { let value = self.get_target_value(space, src, size)?; let existing = self.get_target_value(space, dest, size)?; @@ -288,7 +303,8 @@ impl MC68010 { Size::Word => ((byte as i16) as u16) as u32, Size::Long => (byte as i32) as u32, }; - self.state.d_reg[reg as usize] = result; + set_value_sized(&mut self.state.d_reg[reg as usize], result, size); + self.set_logic_flags(result, size); }, //Instruction::ILLEGAL => { //}, @@ -318,7 +334,7 @@ impl MC68010 { for _ in 0..count { pair = shift_operation(pair.0, size, shift_dir, false); } - self.set_compare_flags(pair.0, size, false); + self.set_logic_flags(pair.0, size); if pair.1 { self.state.sr |= FLAGS_EXTEND | FLAGS_CARRY; } @@ -326,13 +342,14 @@ impl MC68010 { }, Instruction::MOVE(src, dest, size) => { let value = self.get_target_value(space, src, size)?; - self.set_compare_flags(value, size, false); + self.set_logic_flags(value, size); self.set_target_value(space, dest, value, size)?; }, Instruction::MOVEA(src, reg, size) => { let value = self.get_target_value(space, src, size)?; + let value = sign_extend_to_long(value, size) as u32; let addr = self.get_a_reg_mut(reg); - *addr = sign_extend_to_long(value, size) as u32; + *addr = value; }, Instruction::MOVEfromSR(target) => { self.set_target_value(space, target, self.state.sr as u32, Size::Word)?; @@ -404,14 +421,29 @@ impl MC68010 { Instruction::MOVEQ(data, reg) => { let value = sign_extend_to_long(data as u32, Size::Byte) as u32; self.state.d_reg[reg as usize] = value; - self.set_compare_flags(value, Size::Long, false); + self.set_logic_flags(value, Size::Long); + }, + Instruction::MUL(src, dest, size, sign) => { + if size == Size::Long { + return Err(Error::new("Unsupported multiplication size")); + } + + let value = self.get_target_value(space, src, size)?; + let existing = self.get_target_value(space, dest, size)?; + let result = match sign { + Sign::Signed => (sign_extend_to_long(existing, Size::Word) * sign_extend_to_long(value, Size::Word)) as u32, + Sign::Unsigned => existing as u32 * value as u32, + }; + self.set_target_value(space, dest, result, Size::Long)?; }, - //Instruction::MUL(Target, Target, Size, Sign) => { - //}, //Instruction::NBCD(Target) => { //}, - //Instruction::NEG(Target, Size) => { - //}, + Instruction::NEG(target, size) => { + let original = self.get_target_value(space, target, size)?; + let (value, _) = (0 as u32).overflowing_sub(original); + self.set_target_value(space, target, value, size); + self.set_compare_flags(value, size, value != 0, get_overflow(0, original, value, size)); + }, //Instruction::NEGX(Target, Size) => { //}, Instruction::NOP => { }, @@ -450,12 +482,24 @@ impl MC68010 { Instruction::RTS => { self.state.pc = self.pop_long(space)?; }, + Instruction::Scc(cond, target) => { + let condition_true = self.get_current_condition(cond); + if condition_true { + self.set_target_value(space, target, 0xFF, Size::Byte); + } else { + self.set_target_value(space, target, 0x00, Size::Byte); + } + }, //Instruction::STOP(u16) => { //}, Instruction::SUB(src, dest, size) => { let value = self.get_target_value(space, src, size)?; let existing = self.get_target_value(space, dest, size)?; - let result = self.subtract_sized_with_flags(existing, value, size); + let (result, carry) = overflowing_sub_sized(existing, value, size); + match dest { + Target::DirectAReg(_) => { }, + _ => self.set_compare_flags(result, size, carry, get_overflow(existing, value, result, size)), + } self.set_target_value(space, dest, result, size)?; }, Instruction::SWAP(reg) => { @@ -466,7 +510,7 @@ impl MC68010 { //}, Instruction::TST(target, size) => { let value = self.get_target_value(space, target, size)?; - self.set_compare_flags(value, size, false); + self.set_logic_flags(value, size); }, //Instruction::TRAP(u8) => { //}, @@ -479,7 +523,7 @@ impl MC68010 { let addr = self.get_a_reg_mut(reg); *addr = new_value; }, - _ => { panic!(""); }, + _ => { panic!("Unsupported instruction"); }, } Ok(()) @@ -524,9 +568,7 @@ impl MC68010 { Target::IndirectARegXRegOffset(reg, rtype, xreg, offset, target_size) => { let reg_offset = sign_extend_to_long(self.get_x_reg_value(rtype, xreg), target_size); let addr = self.get_a_reg_mut(reg); - let result = get_address_sized(space, (*addr).wrapping_add(reg_offset as u32).wrapping_add(offset as u32) as Address, size); -println!(">>> {:x} has {:x}", (*addr).wrapping_add(reg_offset as u32).wrapping_add(offset as u32), result.as_ref().unwrap()); - result + get_address_sized(space, (*addr).wrapping_add(reg_offset as u32).wrapping_add(offset as u32) as Address, size) }, Target::IndirectMemory(addr) => { get_address_sized(space, addr as Address, size) @@ -606,22 +648,6 @@ println!(">>> {:x} has {:x}", (*addr).wrapping_add(reg_offset as u32).wrapping_a Ok(addr) } - fn subtract_sized_with_flags(&mut self, existing: u32, diff: u32, size: Size) -> u32 { - let (result, overflow) = match size { - Size::Byte => { - let (result, overflow) = (existing as u8).overflowing_sub(diff as u8); - (result as u32, overflow) - }, - Size::Word => { - let (result, overflow) = (existing as u16).overflowing_sub(diff as u16); - (result as u32, overflow) - }, - Size::Long => existing.overflowing_sub(diff), - }; - self.set_compare_flags(result, size, overflow); - result - } - fn get_control_reg_mut(&mut self, control_reg: ControlRegister) -> &mut u32 { match control_reg { ControlRegister::VBR => &mut self.state.vbr, @@ -645,7 +671,7 @@ println!(">>> {:x} has {:x}", (*addr).wrapping_add(reg_offset as u32).wrapping_a fn get_x_reg_value(&self, rtype: RegisterType, reg: u8) -> u32 { match rtype { RegisterType::Data => self.state.d_reg[reg as usize], - RegisterType::Address => self.state.d_reg[reg as usize], + RegisterType::Address => self.state.a_reg[reg as usize], } } @@ -661,7 +687,7 @@ println!(">>> {:x} has {:x}", (*addr).wrapping_add(reg_offset as u32).wrapping_a } } - fn set_compare_flags(&mut self, value: u32, size: Size, carry: bool) { + fn set_compare_flags(&mut self, value: u32, size: Size, carry: bool, overflow: bool) { let value = sign_extend_to_long(value, size); let mut flags = 0x0000; @@ -672,7 +698,10 @@ println!(">>> {:x} has {:x}", (*addr).wrapping_add(reg_offset as u32).wrapping_a flags |= FLAGS_ZERO } if carry { - flags |= FLAGS_CARRY | FLAGS_OVERFLOW; + flags |= FLAGS_CARRY; + } + if overflow { + flags |= FLAGS_OVERFLOW; } self.state.sr = (self.state.sr & 0xFFF0) | flags; } @@ -685,7 +714,7 @@ println!(">>> {:x} has {:x}", (*addr).wrapping_add(reg_offset as u32).wrapping_a if value == 0 { flags |= FLAGS_ZERO } - self.state.sr |= (self.state.sr & 0xFFF0) | flags; + self.state.sr = (self.state.sr & 0xFFF0) | flags; } fn set_bit_test_flags(&mut self, value: u32, bitnum: u32, size: Size) -> u32 { @@ -723,6 +752,52 @@ println!(">>> {:x} has {:x}", (*addr).wrapping_add(reg_offset as u32).wrapping_a } } + +fn overflowing_add_sized(operand1: u32, operand2: u32, size: Size) -> (u32, bool) { + match size { + Size::Byte => { + let (result, carry) = (operand1 as u8).overflowing_add(operand2 as u8); + (result as u32, carry) + }, + Size::Word => { + let (result, carry) = (operand1 as u16).overflowing_add(operand2 as u16); + (result as u32, carry) + }, + Size::Long => operand1.overflowing_add(operand2), + } +} + +fn overflowing_sub_sized(operand1: u32, operand2: u32, size: Size) -> (u32, bool) { + match size { + Size::Byte => { + let (result, carry) = (operand1 as u8).overflowing_sub(operand2 as u8); + (result as u32, carry) + }, + Size::Word => { + let (result, carry) = (operand1 as u16).overflowing_sub(operand2 as u16); + (result as u32, carry) + }, + Size::Long => operand1.overflowing_sub(operand2), + } +} + +fn shift_operation(value: u32, size: Size, dir: ShiftDirection, arithmetic: bool) -> (u32, bool) { + match dir { + ShiftDirection::Left => { + match size { + Size::Byte => (((value as u8) << 1) as u32, get_msb(value, size)), + Size::Word => (((value as u16) << 1) as u32, get_msb(value, size)), + Size::Long => ((value << 1) as u32, get_msb(value, size)), + } + }, + ShiftDirection::Right => { + let mask = if arithmetic { get_msb_mask(value, size) } else { 0 }; + ((value >> 1) | mask, (value & 0x1) != 0) + }, + } +} + + fn get_value_sized(value: u32, size: Size) -> u32 { match size { Size::Byte => { 0x000000FF & value }, @@ -755,20 +830,12 @@ fn set_address_sized(space: &mut AddressSpace, addr: Address, value: u32, size: } } -fn shift_operation(value: u32, size: Size, dir: ShiftDirection, arithmetic: bool) -> (u32, bool) { - match dir { - ShiftDirection::Left => { - match size { - Size::Byte => (((value as u8) << 1) as u32, get_msb(value, size)), - Size::Word => (((value as u16) << 1) as u32, get_msb(value, size)), - Size::Long => ((value << 1) as u32, get_msb(value, size)), - } - }, - ShiftDirection::Right => { - let mask = if arithmetic { get_msb_mask(value, size) } else { 0 }; - ((value >> 1) | mask, (value & 0x1) != 0) - }, - } +fn get_overflow(operand1: u32, operand2: u32, result: u32, size: Size) -> bool { + let msb1 = get_msb(operand1, size); + let msb2 = get_msb(operand2, size); + let msb_res = get_msb(result, size); + + msb1 && msb2 && !msb_res } fn get_msb(value: u32, size: Size) -> bool { diff --git a/src/cpus/m68k/mod.rs b/src/cpus/m68k/mod.rs index e651a3c..bddb9f1 100644 --- a/src/cpus/m68k/mod.rs +++ b/src/cpus/m68k/mod.rs @@ -1,8 +1,8 @@ -mod decode; -mod execute; -mod debugger; -mod tests; +pub mod decode; +pub mod execute; +pub mod debugger; +pub mod tests; pub use self::execute::MC68010; diff --git a/src/cpus/m68k/tests.rs b/src/cpus/m68k/tests.rs index bcfb115..d9a0835 100644 --- a/src/cpus/m68k/tests.rs +++ b/src/cpus/m68k/tests.rs @@ -95,5 +95,17 @@ mod tests { cpu.execute_current(&mut space).unwrap(); assert_eq!(cpu.state.sr & 0x0F, 0x00); } + + #[test] + fn instruction_movel() { + let (mut cpu, mut space) = init_test(); + + space.write_beu16(INIT_ADDR, 0x2F49).unwrap(); + space.write_beu16(INIT_ADDR + 2, 0x0034).unwrap(); + cpu.decode_next(&mut space).unwrap(); + assert_eq!(cpu.decoder.instruction, Instruction::MOVE(Target::DirectAReg(0x01), Target::IndirectARegOffset(7, 52), Size::Long)); + //cpu.execute_current(&mut space).unwrap(); + //assert_eq!(cpu.state.sr & 0x0F, 0x00); + } } diff --git a/src/main.rs b/src/main.rs index edd551a..193bcea 100644 --- a/src/main.rs +++ b/src/main.rs @@ -25,13 +25,11 @@ fn main() { space.insert(0x00700000, Box::new(serial)); let mut cpu = MC68010::new(); - //cpu.add_breakpoint(0x07f8); - //cpu.add_breakpoint(0x0836); - //cpu.add_breakpoint(0x0838); - //cpu.add_breakpoint(0x0ea0); + //cpu.enable_tracing(); + + //cpu.add_breakpoint(0x0c94); + //cpu.add_breakpoint(0x0cf2); - cpu.add_breakpoint(0x0034); - cpu.enable_tracing(); while cpu.is_running() { match cpu.step(&mut space) { Ok(()) => { }, @@ -44,9 +42,9 @@ fn main() { //serial.step(); } - // TODO I need to add a way to decode and dump the assembly for a section of code, in debugger /* - cpu.state.pc = 0x07f8; + // TODO I need to add a way to decode and dump the assembly for a section of code, in debugger + cpu.state.pc = 0x0db4; while cpu.is_running() { cpu.decode_next(&mut space).unwrap(); } diff --git a/src/memory.rs b/src/memory.rs index 109d2e8..8aefe7a 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -111,6 +111,7 @@ impl AddressSpace { pub fn dump_memory(&mut self, mut addr: Address, mut count: Address) { while count > 0 { let mut line = format!("{:#010x}: ", addr); + let to = if count < 16 { count / 2 } else { 8 }; for i in 0..to { let word = self.read_beu16(addr);