From 127b93bfbf0ae07ef1d79c34878ff0ba2921f9ee Mon Sep 17 00:00:00 2001 From: Franco Colmenarez Date: Sat, 9 Nov 2024 21:04:50 -0500 Subject: [PATCH] fix xce, plp and cop instructions --- snes-core/src/cpu/instructions/cop.rs | 13 ++++-- snes-core/src/cpu/instructions/plp.rs | 4 ++ snes-core/src/cpu/instructions/pull_common.rs | 6 ++- snes-core/src/cpu/instructions/push_common.rs | 5 ++- snes-core/src/cpu/instructions/xce.rs | 44 +++++++++++++++++++ snes-core/src/cpu/registers.rs | 35 ++++++++++++++- snes-core/src/ppu/registers.rs | 10 +++++ snes-cpu-test-runner/src/main.rs | 2 +- 8 files changed, 112 insertions(+), 7 deletions(-) diff --git a/snes-core/src/cpu/instructions/cop.rs b/snes-core/src/cpu/instructions/cop.rs index 3c5efe4..cd44727 100644 --- a/snes-core/src/cpu/instructions/cop.rs +++ b/snes-core/src/cpu/instructions/cop.rs @@ -11,15 +11,22 @@ pub struct COP {} impl CPUInstruction for COP { fn execute(&self, registers: &mut Registers, bus: &mut Bus) { - push_common::do_push(registers, bus, &[registers.pbr]); + if !registers.emulation_mode { + push_common::do_push(registers, bus, &[registers.pbr]); + } let (bytes, cycles) = cycles::increment_cycles_brk(registers.emulation_mode); registers.increment_pc(bytes); registers.cycles += cycles; push_common::do_push(registers, bus, &[(registers.pc >> 8) as u8, registers.pc as u8]); push_common::do_push(registers, bus, &[registers.p]); registers.set_irq_disable_flag(true); registers.pbr = 0x00; - let vector = (bus.read(0x00FFE4) as u16) | ((bus.read(0x00FFE5) as u16) << 8); - registers.pc = vector; + if registers.emulation_mode { + let vector = (bus.read(0x00FFF4) as u16) | ((bus.read(0x00FFF5) as u16) << 8); + registers.pc = vector; + } else { + let vector = (bus.read(0x00FFE4) as u16) | ((bus.read(0x00FFE5) as u16) << 8); + registers.pc = vector; + } registers.set_decimal_mode_flag(false); } diff --git a/snes-core/src/cpu/instructions/plp.rs b/snes-core/src/cpu/instructions/plp.rs index 7e536f9..21d1753 100644 --- a/snes-core/src/cpu/instructions/plp.rs +++ b/snes-core/src/cpu/instructions/plp.rs @@ -12,6 +12,10 @@ impl CPUInstruction for PLP { fn execute(&self, registers: &mut Registers, bus: &mut Bus) { let bytes = pull_common::do_pull(registers, bus, 1, true); registers.p = bytes[0]; + if registers.emulation_mode { + registers.set_memory_select_flag(true); + registers.set_index_register_select_flag(true); + } let (bytes, cycles) = cycles::increment_cycles_plp(); registers.increment_pc(bytes); registers.cycles += cycles; } diff --git a/snes-core/src/cpu/instructions/pull_common.rs b/snes-core/src/cpu/instructions/pull_common.rs index 6d588c1..5b6908e 100644 --- a/snes-core/src/cpu/instructions/pull_common.rs +++ b/snes-core/src/cpu/instructions/pull_common.rs @@ -6,7 +6,11 @@ pub fn do_pull(registers: &mut Registers, bus: &mut Bus, count: usize, alter_fla let mut is_zero = true; for _ in 0..count { registers.increment_sp(1); - let byte = bus.read(registers.sp as u32); + let mut address = registers.sp; + if registers.emulation_mode { + address = (registers.sp | 0x100) & 0x1FF + } + let byte = bus.read(address as u32); if byte != 0 { is_zero = false; } diff --git a/snes-core/src/cpu/instructions/push_common.rs b/snes-core/src/cpu/instructions/push_common.rs index 62c648c..98a3202 100644 --- a/snes-core/src/cpu/instructions/push_common.rs +++ b/snes-core/src/cpu/instructions/push_common.rs @@ -2,7 +2,10 @@ use crate::cpu::{registers::Registers, bus::Bus}; pub fn do_push(registers: &mut Registers, bus: &mut Bus, bytes: &[u8]) { for byte in bytes { - let address = registers.sp as u32; + let mut address = registers.sp as u32; + if registers.emulation_mode { + address = (address & 0xFF) | 0x100; + } bus.write(address, *byte); registers.decrement_sp(1); } diff --git a/snes-core/src/cpu/instructions/xce.rs b/snes-core/src/cpu/instructions/xce.rs index 9a46ad8..9b1cc39 100644 --- a/snes-core/src/cpu/instructions/xce.rs +++ b/snes-core/src/cpu/instructions/xce.rs @@ -10,7 +10,12 @@ pub struct XCE {} impl CPUInstruction for XCE { fn execute(&self, registers: &mut Registers, _bus: &mut Bus) { + let did_mode_change = registers.emulation_mode != registers.get_carry_flag(); registers.exchange_carry_and_emulation(); + if did_mode_change { + registers.set_memory_select_flag(true); + registers.set_index_register_select_flag(true); + } let (bytes, cycles) = cycles::increment_cycles_exchange(); registers.increment_pc(bytes); registers.cycles += cycles; } @@ -34,5 +39,44 @@ mod cpu_instructions_tests { instruction.execute(&mut registers, &mut bus); assert_eq!(registers.pc, 0x0001); assert_eq!(registers.cycles, 2); + assert!(registers.get_memory_select_flag()); + assert!(registers.get_index_register_select_flag()); + + let mut registers = Registers::new(); + let mut bus = Bus::new(); + registers.pc = 0x0000; + registers.emulation_mode = true; + registers.set_carry_flag(false); + let instruction = XCE{}; + instruction.execute(&mut registers, &mut bus); + assert_eq!(registers.pc, 0x0001); + assert_eq!(registers.cycles, 2); + assert!(!registers.emulation_mode); + assert!(registers.get_memory_select_flag()); + assert!(registers.get_index_register_select_flag()); + + let mut registers = Registers::new(); + let mut bus = Bus::new(); + registers.pc = 0x0000; + registers.emulation_mode = false; + registers.set_carry_flag(true); + let instruction = XCE{}; + instruction.execute(&mut registers, &mut bus); + assert_eq!(registers.pc, 0x0001); + assert_eq!(registers.cycles, 2); + assert!(registers.emulation_mode); + assert!(registers.get_memory_select_flag()); + assert!(registers.get_index_register_select_flag()); + + let mut registers = Registers::new(); + let mut bus = Bus::new(); + registers.pc = 0x0000; + registers.emulation_mode = true; + registers.set_carry_flag(false); + let instruction = XCE{}; + instruction.execute(&mut registers, &mut bus); + assert!(registers.get_carry_flag()); + assert!(registers.get_memory_select_flag()); + assert!(registers.get_index_register_select_flag()); } } diff --git a/snes-core/src/cpu/registers.rs b/snes-core/src/cpu/registers.rs index adf495f..22e703c 100644 --- a/snes-core/src/cpu/registers.rs +++ b/snes-core/src/cpu/registers.rs @@ -17,6 +17,34 @@ pub struct Registers { pub is_move_next: bool, // TODO: refactor these states with an enum pub cycles: usize, } +/* +P register flags + +Emulation mode: +NV-BDIZC +N - Negative +V - Overflow +- +B - Break Instruction +D - Decimal Mode +I - IRQ Disable +Z - Zero +C - Carry + +Native Mode + E - Emulation Mode +NVMXDIZC + +N - Negative +V - Overflow +M - Memory/Accumulator Select +X - Index Register Select +D - Decimal Mode +I - IRQ Disable +Z - Zero +C - Carry + +*/ impl Registers { pub fn new() -> Self { @@ -214,7 +242,11 @@ impl Registers { } pub fn reset_rep_byte(&mut self, byte: u8) { - self.p &= !byte; + let mut effective_byte = byte; + if self.emulation_mode { + effective_byte &= 0b1100_1111; + } + self.p &= !effective_byte; } pub fn set_sep_byte(&mut self, byte: u8) { @@ -440,6 +472,7 @@ mod registers_tests { #[test] fn test_reset_rep_byte() { let mut registers = Registers::new(); + registers.emulation_mode = false; registers.p = 0xFF; registers.reset_rep_byte(0b0000_0001); assert_eq!(registers.p, 0b1111_1110); diff --git a/snes-core/src/ppu/registers.rs b/snes-core/src/ppu/registers.rs index efb04d3..9dba27b 100644 --- a/snes-core/src/ppu/registers.rs +++ b/snes-core/src/ppu/registers.rs @@ -263,6 +263,16 @@ impl PPURegisters { 0b11 => 128, _ => unreachable!(), }; + let address_translation_rotate = match (register >> 2) & 0b11 { + 0b00 => 0, + 0b01 => 8, + 0b10 => 9, + 0b11 => 10, + _ => unreachable!(), + }; + if address_translation_rotate > 0 { + // TODO: implement address translation + } let increment_when_lo = (register >> 7) != 1; let increment_when_hi = !increment_when_lo; let current_value = self.get_current_vram_address(); diff --git a/snes-cpu-test-runner/src/main.rs b/snes-cpu-test-runner/src/main.rs index 05efa09..7ce16cd 100644 --- a/snes-cpu-test-runner/src/main.rs +++ b/snes-cpu-test-runner/src/main.rs @@ -116,7 +116,7 @@ fn main() -> Result<()> { emulator.tick(); } - let is_emu_mode = emulator.cpu.registers.emulation_mode; + let is_emu_mode = emulator.cpu.registers.emulation_mode || (test.initial.e == 1); let is_16index = emulator.cpu.registers.is_16bit_index(); let emu_mode_sp = (emulator.cpu.registers.sp as usize & 0xFF) | 0x100;