diff --git a/snes-core/src/cpu/cycles.rs b/snes-core/src/cpu/cycles.rs index 2d77dbb..362cc40 100644 --- a/snes-core/src/cpu/cycles.rs +++ b/snes-core/src/cpu/cycles.rs @@ -25,6 +25,18 @@ const BITWISE_CONDITIONS: [Condition; 3] = [ Condition::IndexCrossesPageBoundary, ]; +const LDA_CONDITIONS: [Condition; 3] = [ + Condition::MemorySelectFlag, + Condition::DirectPageIsZero, + Condition::IndexCrossesPageBoundary, +]; + +const LD_INDEX_CONDITIONS: [Condition; 3] = [ + Condition::IndexIs16Bit, + Condition::DirectPageIsZero, + Condition::IndexCrossesPageBoundary, +]; + const COMP_INDEX_CONDITIONS: [Condition; 2] = [ Condition::IndexIs16Bit, Condition::IndexCrossesPageBoundary, @@ -241,6 +253,20 @@ impl CPU { // self.registers.increment_pc(bytes); // TODO: consider above comment self.registers.increment_pc(bytes); self.cycles += cycles; } + + pub fn increment_cycles_lda(&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, &LDA_CONDITIONS); + self.registers.increment_pc(bytes); self.cycles += cycles; + } + + pub fn increment_cycles_ld_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, &LD_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 3d23432..3019ec0 100644 --- a/snes-core/src/cpu/instructions.rs +++ b/snes-core/src/cpu/instructions.rs @@ -558,7 +558,40 @@ impl CPU { self.registers.set_low_a(value); self.do_bit(self.registers.a as u8, value, addressing_mode); } - self.increment_cycles_bit(addressing_mode); + self.increment_cycles_lda(addressing_mode); + } + + fn do_ld_index(&mut self, bus: &Bus, index: IndexRegister, addressing_mode: AddressingMode) { + if self.registers.is_16bit_index() { + let value = self.get_16bit_from_address(bus, addressing_mode); + match index { + IndexRegister::X => self.registers.x = value, + IndexRegister::Y => self.registers.y = value, + } + self.registers.set_flags(&[ + Flags::Negative(value >> 7 == 1), + Flags::Zero(value == 0), + ]); + } else { + let value = self.get_8bit_from_address(bus, addressing_mode); + match index { + IndexRegister::X => self.registers.set_low_x(value), + IndexRegister::Y => self.registers.set_low_y(value), + } + self.registers.set_flags(&[ + Flags::Negative(value >> 7 == 1), + Flags::Zero(value == 0), + ]); + } + self.increment_cycles_ld_index(addressing_mode); + } + + fn ldx(&mut self, bus: &Bus, addressing_mode: AddressingMode) { + self.do_ld_index(bus, IndexRegister::X, addressing_mode); + } + + fn ldy(&mut self, bus: &Bus, addressing_mode: AddressingMode) { + self.do_ld_index(bus, IndexRegister::Y, addressing_mode); } pub fn execute_opcode(&mut self, opcode: u8, bus: &mut Bus) { @@ -767,6 +800,18 @@ impl CPU { 0xB7 => self.lda(bus, A::DirectPageIndirectLongIndexed(I::Y)), 0xA3 => self.lda(bus, A::StackRelative), 0xB3 => self.lda(bus, A::StackRelativeIndirectIndexed(I::Y)), + // LDX + 0xA2 => self.ldx(bus, A::Immediate), + 0xAE => self.ldx(bus, A::Absolute), + 0xA6 => self.ldx(bus, A::DirectPage), + 0xBE => self.ldx(bus, A::AbsoluteIndexed(I::Y)), + 0xB6 => self.ldx(bus, A::DirectPageIndexed(I::Y)), + // LDY + 0xA0 => self.ldy(bus, A::Immediate), + 0xAC => self.ldy(bus, A::Absolute), + 0xA4 => self.ldy(bus, A::DirectPage), + 0xB4 => self.ldy(bus, A::AbsoluteIndexed(I::Y)), + 0xBC => self.ldy(bus, A::DirectPageIndexed(I::Y)), _ => println!("Invalid opcode: {:02X}", opcode), } } @@ -1543,4 +1588,42 @@ mod cpu_instructions_tests { assert!(cpu.registers.get_negative_flag()); assert!(!cpu.registers.get_zero_flag()); } + + #[test] + fn test_ldx() { + let mut cpu = CPU::new(); + let mut bus = Bus::new(); + cpu.registers.x = 0x0000; + cpu.registers.pc = 0x0000; + cpu.registers.pbr = 0x00; + cpu.registers.set_negative_flag(false); + cpu.registers.set_zero_flag(true); + cpu.registers.set_16bit_index(false); + bus.write(0x0001, 0xFF); + cpu.ldx(&bus, AddressingMode::Immediate); + assert_eq!(cpu.registers.pc, 0x0002); + assert_eq!(cpu.registers.x, 0x00FF); + assert_eq!(cpu.cycles, 2); + assert!(cpu.registers.get_negative_flag()); + assert!(!cpu.registers.get_zero_flag()); + } + + #[test] + fn test_ldy() { + let mut cpu = CPU::new(); + let mut bus = Bus::new(); + cpu.registers.y = 0x0000; + cpu.registers.pc = 0x0000; + cpu.registers.pbr = 0x00; + cpu.registers.set_negative_flag(false); + cpu.registers.set_zero_flag(true); + cpu.registers.set_16bit_index(false); + bus.write(0x0001, 0xFF); + cpu.ldy(&bus, AddressingMode::Immediate); + assert_eq!(cpu.registers.pc, 0x0002); + assert_eq!(cpu.registers.y, 0x00FF); + assert_eq!(cpu.cycles, 2); + assert!(cpu.registers.get_negative_flag()); + assert!(!cpu.registers.get_zero_flag()); + } }