From df6424a6c42d1f508f741f80a9a44b418a40f2e4 Mon Sep 17 00:00:00 2001 From: Franco Colmenarez Date: Sat, 26 Nov 2022 19:19:27 -0500 Subject: [PATCH] CPX and CPY instructions --- snes-core/src/cpu/cycles.rs | 27 ++++- snes-core/src/cpu/instructions.rs | 157 ++++++++++++++++++++++++------ snes-core/src/cpu/registers.rs | 28 +++++- 3 files changed, 176 insertions(+), 36 deletions(-) diff --git a/snes-core/src/cpu/cycles.rs b/snes-core/src/cpu/cycles.rs index 7887ed4..7f174e4 100644 --- a/snes-core/src/cpu/cycles.rs +++ b/snes-core/src/cpu/cycles.rs @@ -6,21 +6,27 @@ type I = IndexRegister; enum Condition { MemorySelectFlag, - DirectPageZero, + DirectPageIsZero, IndexCrossesPageBoundary, DecimalMode, + IndexIs16Bit, } const ALL_CONDITIONS: [Condition; 4] = [ Condition::MemorySelectFlag, - Condition::DirectPageZero, + Condition::DirectPageIsZero, Condition::IndexCrossesPageBoundary, Condition::DecimalMode, ]; const BITWISE_CONDITIONS: [Condition; 3] = [ Condition::MemorySelectFlag, - Condition::DirectPageZero, + Condition::DirectPageIsZero, + Condition::IndexCrossesPageBoundary, +]; + +const COMP_INDEX_CONDITIONS: [Condition; 2] = [ + Condition::IndexIs16Bit, Condition::IndexCrossesPageBoundary, ]; @@ -42,7 +48,7 @@ impl CPU { } }, // Add 1 cycle if low byte of Direct Page register is other than zero (DL< >0) - Condition::DirectPageZero => { + Condition::DirectPageIsZero => { match addressing_mode { A::DirectPage | A::DirectPageIndirect | A::DirectPageIndirectLong | A::DirectPageIndexed(_) | A::DirectPageIndexedIndirect(_) | @@ -77,6 +83,12 @@ impl CPU { cycles += 1; } }, + // Add 1 byte if = 0 (16-bit index registers) + Condition::IndexIs16Bit => { + if self.registers.is_16bit_index() { + bytes += 1; cycles += 1; + } + }, }; } @@ -179,6 +191,13 @@ impl CPU { pub fn increment_cycles_branch_long(&mut self) { self.registers.increment_pc(3); self.cycles += 4; } + + pub fn increment_cycles_comp_index(&mut self, addressing_mode: AddressingMode) { + let (bytes, cycles) = CPU::common_bytes_cycles_arithmetic(addressing_mode); + self.registers.increment_pc(bytes); self.cycles += cycles; + let (bytes, cycles) = self.common_conditions(addressing_mode, &COMP_INDEX_CONDITIONS); + self.registers.increment_pc(bytes); self.cycles += cycles; + } } #[cfg(test)] diff --git a/snes-core/src/cpu/instructions.rs b/snes-core/src/cpu/instructions.rs index 5b02be3..6c984e5 100644 --- a/snes-core/src/cpu/instructions.rs +++ b/snes-core/src/cpu/instructions.rs @@ -27,8 +27,6 @@ impl CPU { } fn adc(&mut self, bus: &Bus, addressing_mode: AddressingMode) { - // if the M flag is set, perform 8 bit addition. - // Otherwise, 16 bit addition let carry_flag = self.registers.get_carry_flag(); let is_decimal_mode = self.registers.get_decimal_mode_flag(); let is_16bit = self.registers.is_16bit_mode(); @@ -54,8 +52,6 @@ impl CPU { } fn sbc(&mut self, bus: &Bus, addressing_mode: AddressingMode) { - // if the M flag is set, perform 8 bit addition. - // Otherwise, 16 bit addition let carry_flag = self.registers.get_carry_flag(); let is_decimal_mode = self.registers.get_decimal_mode_flag(); let is_16bit = self.registers.is_16bit_mode(); @@ -80,30 +76,12 @@ impl CPU { self.increment_cycles_arithmetic(addressing_mode); } - fn cmp(&mut self, bus: &Bus, addressing_mode: AddressingMode) { - // if the M flag is set, perform 8 bit addition. - // Otherwise, 16 bit addition + fn do_comp(&mut self, target: T, value: T) { let carry_flag = self.registers.get_carry_flag(); let is_decimal_mode = self.registers.get_decimal_mode_flag(); - let is_16bit = self.registers.is_16bit_mode(); - let target = self.registers.a; - let affected_flags = match is_16bit { - true => { - let value = self.get_16bit_from_address(bus, addressing_mode); - let (_, flags) = match is_decimal_mode { - true => alu::sbc_bcd(target, value, carry_flag), - false => alu::sbc_bin(target, value, carry_flag), - }; - flags.to_vec() - }, - false => { - let value = self.get_8bit_from_address(bus, addressing_mode); - let (_, flags) = match is_decimal_mode { - true => alu::sbc_bcd(target as u8, value, carry_flag), - false => alu::sbc_bin(target as u8, value, carry_flag), - }; - flags.to_vec() - } + let (_, affected_flags) = match is_decimal_mode { + true => alu::sbc_bcd(target, value, carry_flag), + false => alu::sbc_bin(target, value, carry_flag), }; for flag in affected_flags { match flag { @@ -111,12 +89,48 @@ impl CPU { _ => self.registers.set_flags(&[flag]), } } + } + + fn cmp(&mut self, bus: &Bus, addressing_mode: AddressingMode) { + let is_16bit = self.registers.is_16bit_mode(); + let target = self.registers.a; + if is_16bit { + let value = self.get_16bit_from_address(bus, addressing_mode); + self.do_comp(target, value); + } else { + let value = self.get_8bit_from_address(bus, addressing_mode); + self.do_comp(target as u8, value); + } self.increment_cycles_arithmetic(addressing_mode); } + fn cpx(&mut self, bus: &Bus, addressing_mode: AddressingMode) { + let is_16bit = self.registers.is_16bit_index(); + let target = self.registers.x; + if is_16bit { + let value = self.get_16bit_from_address(bus, addressing_mode); + self.do_comp(target, value); + } else { + let value = self.get_8bit_from_address(bus, addressing_mode); + self.do_comp(target as u8, value); + } + self.increment_cycles_comp_index(addressing_mode); + } + + fn cpy(&mut self, bus: &Bus, addressing_mode: AddressingMode) { + let is_16bit = self.registers.is_16bit_index(); + let target = self.registers.y; + if is_16bit { + let value = self.get_16bit_from_address(bus, addressing_mode); + self.do_comp(target, value); + } else { + let value = self.get_8bit_from_address(bus, addressing_mode); + self.do_comp(target as u8, value); + } + self.increment_cycles_comp_index(addressing_mode); + } + fn and(&mut self, bus: &Bus, addressing_mode: AddressingMode) { - // if the M flag is set, perform 8 bit addition. - // Otherwise, 16 bit addition let target = self.registers.a; if self.registers.is_16bit_mode() { let value = self.get_16bit_from_address(bus, addressing_mode); @@ -134,8 +148,6 @@ impl CPU { } fn asl(&mut self, bus: &Bus, addressing_mode: AddressingMode) { - // if the M flag is set, perform 8 bit addition. - // Otherwise, 16 bit addition let target = match addressing_mode { AddressingMode::Accumulator => self.registers.a, _ => match self.registers.is_16bit_mode() { @@ -407,6 +419,16 @@ impl CPU { 0xD7 => self.cmp(bus, A::DirectPageIndirectLongIndexed(I::Y)), 0xC3 => self.cmp(bus, A::StackRelative), 0xD3 => self.cmp(bus, A::StackRelativeIndirectIndexed(I::Y)), + // COP + 0x02 => unimplemented!("COP instruction not implemented yet"), + // CPX + 0xE0 => self.cpx(bus, A::Immediate), + 0xEC => self.cpx(bus, A::Absolute), + 0xE4 => self.cpx(bus, A::DirectPage), + // CPY + 0xC0 => self.cpy(bus, A::Immediate), + 0xCC => self.cpy(bus, A::Absolute), + 0xC4 => self.cpy(bus, A::DirectPage), _ => println!("Invalid opcode: {:02X}", opcode), } } @@ -880,6 +902,7 @@ mod cpu_instructions_tests { assert!(cpu.registers.get_zero_flag()); // check overflow flag is not affected + cpu.cycles = 0; cpu.registers.a = 0x0050; cpu.registers.pbr = 0x00; cpu.registers.pc = 0x0000; @@ -889,7 +912,79 @@ mod cpu_instructions_tests { cpu.cmp(&bus, AddressingMode::Immediate); assert_eq!(cpu.registers.a, 0x0050); // check A is not affected assert_eq!(cpu.registers.pc, 0x02); - assert_eq!(cpu.cycles, 4); + assert_eq!(cpu.cycles, 2); + assert!(cpu.registers.get_carry_flag()); + assert!(!cpu.registers.get_zero_flag()); + assert!(!cpu.registers.get_overflow_flag()); + } + + #[test] + fn test_cpx() { + // CMP is basically an SBC instruction but it doesn't + // store the result nor it affects the overflow flag + let mut cpu = CPU::new(); + let mut bus = Bus::new(); + cpu.registers.x = 0x01; + cpu.registers.pbr = 0x00; + cpu.registers.pc = 0x0000; + cpu.registers.set_16bit_index(false); + bus.write(0x000001, 1); + cpu.cpx(&bus, AddressingMode::Immediate); + assert_eq!(cpu.registers.x, 0x01); // check A is not affected + assert_eq!(cpu.registers.pc, 0x02); + assert_eq!(cpu.cycles, 2); + assert!(!cpu.registers.get_carry_flag()); + assert!(cpu.registers.get_zero_flag()); + + // check overflow flag is not affected + cpu.cycles = 0; + cpu.registers.x = 0x50; + cpu.registers.pbr = 0x00; + cpu.registers.pc = 0x0000; + cpu.registers.set_16bit_index(true); + cpu.registers.set_overflow_flag(false); + bus.write(0x000002, 0xB0); + bus.write(0x000001, 0x00); + cpu.cpx(&bus, AddressingMode::Immediate); + assert_eq!(cpu.registers.x, 0x50); // check X is not affected + assert_eq!(cpu.registers.pc, 0x03); + assert_eq!(cpu.cycles, 3); + assert!(cpu.registers.get_carry_flag()); + assert!(!cpu.registers.get_zero_flag()); + assert!(!cpu.registers.get_overflow_flag()); + } + + #[test] + fn test_cpy() { + // CMP is basically an SBC instruction but it doesn't + // store the result nor it affects the overflow flag + let mut cpu = CPU::new(); + let mut bus = Bus::new(); + cpu.registers.y = 0x01; + cpu.registers.pbr = 0x00; + cpu.registers.pc = 0x0000; + cpu.registers.set_16bit_index(false); + bus.write(0x000001, 1); + cpu.cpy(&bus, AddressingMode::Immediate); + assert_eq!(cpu.registers.y, 0x01); // check A is not affected + assert_eq!(cpu.registers.pc, 0x02); + assert_eq!(cpu.cycles, 2); + assert!(!cpu.registers.get_carry_flag()); + assert!(cpu.registers.get_zero_flag()); + + // check overflow flag is not affected + cpu.cycles = 0; + cpu.registers.y = 0x50; + cpu.registers.pbr = 0x00; + cpu.registers.pc = 0x0000; + cpu.registers.set_16bit_index(true); + cpu.registers.set_overflow_flag(false); + bus.write(0x000002, 0xB0); + bus.write(0x000001, 0x00); + cpu.cpy(&bus, AddressingMode::Immediate); + assert_eq!(cpu.registers.y, 0x50); // check X is not affected + assert_eq!(cpu.registers.pc, 0x03); + assert_eq!(cpu.cycles, 3); assert!(cpu.registers.get_carry_flag()); assert!(!cpu.registers.get_zero_flag()); assert!(!cpu.registers.get_overflow_flag()); diff --git a/snes-core/src/cpu/registers.rs b/snes-core/src/cpu/registers.rs index 513056d..c152409 100644 --- a/snes-core/src/cpu/registers.rs +++ b/snes-core/src/cpu/registers.rs @@ -118,6 +118,14 @@ impl Registers { ((self.pbr as u32) << 16) | (self.pc as u32) } + pub fn set_16bit_index(&mut self, val: bool) { + self.set_index_register_select_flag(!val); + } + + pub fn is_16bit_index(&self) -> bool { + !self.get_index_register_select_flag() + } + pub fn is_16bit_mode(&self) -> bool { !self.get_memory_select_flag() } @@ -161,7 +169,7 @@ mod registers_tests { use super::*; #[test] - fn test_get_16bit_mode() { + fn test_is_16bit_mode() { let mut registers = Registers::new(); registers.set_memory_select_flag(false); assert!(registers.is_16bit_mode()); @@ -178,6 +186,24 @@ mod registers_tests { assert!(!registers.is_16bit_mode()); } + #[test] + fn test_is_16bit_index() { + let mut registers = Registers::new(); + registers.set_index_register_select_flag(false); + assert!(registers.is_16bit_index()); + registers.set_index_register_select_flag(true); + assert!(!registers.is_16bit_index()); + } + + #[test] + fn test_set_16bit_index() { + let mut registers = Registers::new(); + registers.set_16bit_index(true); + assert!(registers.is_16bit_index()); + registers.set_16bit_index(false); + assert!(!registers.is_16bit_index()); + } + #[test] fn test_set_low_a() { let mut registers = Registers::new();