diff --git a/snes-core/src/cpu/cycles.rs b/snes-core/src/cpu/cycles.rs index ade8dfc..6cf3d46 100644 --- a/snes-core/src/cpu/cycles.rs +++ b/snes-core/src/cpu/cycles.rs @@ -340,6 +340,10 @@ impl CPU { self.cycles += 1; } } + + pub fn increment_cycles_rep(&mut self) { + self.registers.increment_pc(2); self.cycles += 3; + } } #[cfg(test)] diff --git a/snes-core/src/cpu/instructions.rs b/snes-core/src/cpu/instructions.rs index e1786ec..8823e54 100644 --- a/snes-core/src/cpu/instructions.rs +++ b/snes-core/src/cpu/instructions.rs @@ -743,6 +743,46 @@ impl CPU { self.increment_cycles_rep(); } + fn rol(&mut self, bus: &mut Bus, addressing_mode: AddressingMode) { + let target = match addressing_mode { + AddressingMode::Accumulator => self.registers.a, + _ => match self.registers.is_16bit_mode() { + true => self.get_16bit_from_address(bus, addressing_mode), + false => self.get_8bit_from_address(bus, addressing_mode) as u16, + } + }; + if self.registers.is_16bit_mode() { + let (result, affected_flags) = alu::rol(target, self.registers.get_carry_flag()); + self.set_16bit_to_address(bus, addressing_mode, result); + self.registers.set_flags(&affected_flags); + } else { + let (result, affected_flags) = alu::rol(target as u8, self.registers.get_carry_flag()); + self.set_8bit_to_address(bus, addressing_mode, result); + self.registers.set_flags(&affected_flags); + } + self.increment_cycles_shift(addressing_mode); + } + + fn ror(&mut self, bus: &mut Bus, addressing_mode: AddressingMode) { + let target = match addressing_mode { + AddressingMode::Accumulator => self.registers.a, + _ => match self.registers.is_16bit_mode() { + true => self.get_16bit_from_address(bus, addressing_mode), + false => self.get_8bit_from_address(bus, addressing_mode) as u16, + } + }; + if self.registers.is_16bit_mode() { + let (result, affected_flags) = alu::ror(target, self.registers.get_carry_flag()); + self.set_16bit_to_address(bus, addressing_mode, result); + self.registers.set_flags(&affected_flags); + } else { + let (result, affected_flags) = alu::ror(target as u8, self.registers.get_carry_flag()); + self.set_8bit_to_address(bus, addressing_mode, result); + self.registers.set_flags(&affected_flags); + } + self.increment_cycles_shift(addressing_mode); + } + pub fn execute_opcode(&mut self, opcode: u8, bus: &mut Bus) { type A = AddressingMode; type I = IndexRegister; @@ -999,6 +1039,18 @@ impl CPU { 0x7A => self.ply(bus), // REP 0xC2 => self.rep(bus), + // ROL + 0x2A => self.rol(bus, AddressingMode::Accumulator), + 0x2E => self.rol(bus, AddressingMode::Immediate), + 0x26 => self.rol(bus, AddressingMode::DirectPage), + 0x3E => self.rol(bus, AddressingMode::AbsoluteIndexed(I::X)), + 0x36 => self.rol(bus, AddressingMode::DirectPageIndexed(I::X)), + // ROR + 0x6A => self.ror(bus, AddressingMode::Accumulator), + 0x6E => self.ror(bus, AddressingMode::Immediate), + 0x66 => self.ror(bus, AddressingMode::DirectPage), + 0x7E => self.ror(bus, AddressingMode::AbsoluteIndexed(I::X)), + 0x76 => self.ror(bus, AddressingMode::DirectPageIndexed(I::X)), _ => println!("Invalid opcode: {:02X}", opcode), } } @@ -2093,4 +2145,35 @@ mod cpu_instructions_tests { assert_eq!(cpu.registers.pc, 0x0002); assert_eq!(cpu.cycles, 3); } + + #[test] + fn test_rol() { + let mut cpu = CPU::new(); + let mut bus = Bus::new(); + cpu.registers.set_16bit_mode(false); + cpu.registers.a = 0b0100_0000; + cpu.registers.pc = 0x0000; + cpu.rol(&mut bus, AddressingMode::Accumulator); + assert_eq!(cpu.registers.get_negative_flag(), true); + assert_eq!(cpu.registers.get_zero_flag(), false); + assert_eq!(cpu.registers.a, 0b1000_0000); + assert_eq!(cpu.registers.pc, 0x0001); + assert_eq!(cpu.cycles, 2); + } + + #[test] + fn test_ror() { + let mut cpu = CPU::new(); + let mut bus = Bus::new(); + cpu.registers.set_16bit_mode(false); + cpu.registers.set_carry_flag(true); + cpu.registers.a = 0x00; + cpu.registers.pc = 0x0000; + cpu.ror(&mut bus, AddressingMode::Accumulator); + assert_eq!(cpu.registers.get_carry_flag(), false); + assert_eq!(cpu.registers.get_zero_flag(), false); + assert_eq!(cpu.registers.a, 0b1000_0000); + assert_eq!(cpu.registers.pc, 0x0001); + assert_eq!(cpu.cycles, 2); + } } diff --git a/snes-core/src/utils/alu.rs b/snes-core/src/utils/alu.rs index 0ea683b..1de606f 100644 --- a/snes-core/src/utils/alu.rs +++ b/snes-core/src/utils/alu.rs @@ -134,6 +134,24 @@ pub fn ora(target: T, value: T) -> (T, [Flags; 2]) { ]) } +pub fn rol(target: T, carry: bool) -> (T, [Flags; 3]) { + let result = target.rol(carry); + (result, [ + Negative(result.is_negative()), + Zero(result.is_zero()), + Carry(target.is_negative()), + ]) +} + +pub fn ror(target: T, carry: bool) -> (T, [Flags; 3]) { + let result = target.ror(carry); + (result, [ + Negative(result.is_negative()), + Zero(result.is_zero()), + Carry(target.lowest_bit()), + ]) +} + #[cfg(test)] mod alu_tests { @@ -400,4 +418,26 @@ mod alu_tests { assert_eq!(result, 0); assert_eq!(affected_flags, [Negative(false), Zero(true)]); } + + #[test] + fn test_rol() { + let (result, affected_flags) = rol(0b1000_0000_u8, false); + assert_eq!(result, 0); + assert_eq!(affected_flags, [Negative(false), Zero(true), Carry(true)]); + + let (result, affected_flags) = rol(0b0100_0000_u8, true); + assert_eq!(result, 0b10000001); + assert_eq!(affected_flags, [Negative(true), Zero(false), Carry(false)]); + } + + #[test] + fn test_ror() { + let (result, affected_flags) = ror(0b0000_0001_u8, false); + assert_eq!(result, 0); + assert_eq!(affected_flags, [Negative(false), Zero(true), Carry(true)]); + + let (result, affected_flags) = ror(0b0000_0000_u8, true); + assert_eq!(result, 0b10000000); + assert_eq!(affected_flags, [Negative(true), Zero(false), Carry(false)]); + } }