From bb51b998e19eeb427e5e008fdbda19dc205a26be Mon Sep 17 00:00:00 2001 From: Franco Date: Mon, 14 Apr 2025 19:14:46 -0500 Subject: [PATCH] More WIP PPU rendering and fix a few more tests (#11) --- snes-core/src/cpu/instructions/cop.rs | 13 +++-- snes-core/src/cpu/instructions/plp.rs | 9 ++++ 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 | 47 +++++++++++++++++++ snes-core/src/cpu/registers.rs | 35 +++++++++++++- snes-core/src/ppu/registers.rs | 33 +++++++++---- snes-cpu-test-runner/src/main.rs | 2 +- snes-frontend/src/emu_ui/debug/ppu.rs | 21 +++++---- 9 files changed, 145 insertions(+), 26 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..58eed86 100644 --- a/snes-core/src/cpu/instructions/plp.rs +++ b/snes-core/src/cpu/instructions/plp.rs @@ -12,6 +12,15 @@ 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); + } else { + if !registers.is_16bit_index() { + registers.x &= 0xFF; + registers.y &= 0xFF; + } + } 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..87aff7d 100644 --- a/snes-core/src/cpu/instructions/xce.rs +++ b/snes-core/src/cpu/instructions/xce.rs @@ -11,6 +11,14 @@ pub struct XCE {} impl CPUInstruction for XCE { fn execute(&self, registers: &mut Registers, _bus: &mut Bus) { registers.exchange_carry_and_emulation(); + if registers.emulation_mode { + registers.set_memory_select_flag(true); + registers.set_index_register_select_flag(true); + registers.x &= 0xFF; + registers.y &= 0xFF; + registers.sp &= 0xFF; + registers.sp |= 0x100; + } let (bytes, cycles) = cycles::increment_cycles_exchange(); registers.increment_pc(bytes); registers.cycles += cycles; } @@ -34,5 +42,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..06ace9e 100644 --- a/snes-core/src/ppu/registers.rs +++ b/snes-core/src/ppu/registers.rs @@ -209,8 +209,8 @@ impl PPURegisters { pub fn read(&mut self, address: u16) -> u8 { let result = self._read(address); match address { - VMDATAH => self.handle_vram_addr_auto_increment(Some(result), None), - VMDATAL => self.handle_vram_addr_auto_increment(None, Some(result)), + VMDATAH | RDVRAMH => self.handle_vram_addr_auto_increment(Some(result), None), + VMDATAL | RDVRAML => self.handle_vram_addr_auto_increment(None, Some(result)), RDCGRAM => { let value = self.get_rdcgram(); self._write(RDCGRAM, value); @@ -230,6 +230,9 @@ impl PPURegisters { self._write(address, value); self.handle_write_vram(None, Some(value)); }, + VMADDH | VMADDL => { + self._write(address, value); + }, RDVRAML | RDVRAMH => {}, CGADD => { self._write(address, value); @@ -241,14 +244,14 @@ impl PPURegisters { } fn handle_write_vram(&mut self, byte_hi: Option, byte_lo: Option) { - let address = (((self.read(VMADDH) as u16) << 8) | (self.read(VMADDL) as u16)) & 0x7FFF; - let current_word = self.vram[address as usize]; + let address = (self.get_current_vram_address() & 0x7FFF) as usize; + let current_word = self.vram[address]; if let Some(byte) = byte_hi { - self.vram[address as usize] = (current_word & 0x00FF) | ((byte as u16) << 8); + self.vram[address] = (current_word & 0x00FF) | ((byte as u16) << 8); self._write(RDVRAMH, byte); } if let Some(byte) = byte_lo { - self.vram[address as usize] = (current_word & 0xFF00) | (byte as u16); + self.vram[address] = (current_word & 0xFF00) | (byte as u16); self._write(RDVRAML, byte); } self.handle_vram_addr_auto_increment(byte_hi, byte_lo); @@ -263,7 +266,18 @@ impl PPURegisters { 0b11 => 128, _ => unreachable!(), }; - let increment_when_lo = (register >> 7) != 1; + let address_translation_rotate = match (register >> 2) & 0b11 { + 0b00 => 0, + 0b01 => 8, + 0b10 => 9, + 0b11 => 10, + _ => unreachable!(), + }; + if address_translation_rotate > 0 { + println!("ADDRESS ROTATE {}", address_translation_rotate); + // TODO: implement address translation + } + let increment_when_lo = (register >> 7) == 0; let increment_when_hi = !increment_when_lo; let current_value = self.get_current_vram_address(); if increment_when_hi && byte_hi.is_some() { @@ -279,9 +293,8 @@ impl PPURegisters { } fn set_current_vram_address(&mut self, value: u16) { - let bytes = value.to_be_bytes(); - self._write(VMADDH, bytes[0]); - self._write(VMADDL, bytes[1]); + self._write(VMADDH, (value >> 8) as u8); + self._write(VMADDL, value as u8); } /// 7 BG4 Tile Size (0=8x8, 1=16x16) ;\(BgMode0..4: variable 8x8 or 16x16) 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; diff --git a/snes-frontend/src/emu_ui/debug/ppu.rs b/snes-frontend/src/emu_ui/debug/ppu.rs index b9c342e..d4046a8 100644 --- a/snes-frontend/src/emu_ui/debug/ppu.rs +++ b/snes-frontend/src/emu_ui/debug/ppu.rs @@ -190,7 +190,8 @@ fn build_vram_window(ctx: &egui::Context, ppu_debug_options: &mut PPUDebugContro for row in chunks { let mut address_row = format!("{:04X} | ", row[0]); for address in row { - address_row = format!("{}{:04X} ", address_row, ppu_registers.vram()[((*address) & 0x7FFF) as usize]); + let effective_vram_address = ((*address) & 0x7FFF) as usize; + address_row = format!("{}{:04X} ", address_row, ppu_registers.vram()[effective_vram_address]); } ui.monospace(address_row); } @@ -233,14 +234,16 @@ fn build_cgram_window(ctx: &egui::Context, ppu_debug_options: &mut PPUDebugContr ui.separator(); - ui.horizontal(|ui| { - ui.label("0x00: "); - paint_cgram_color_address(ui, 0x00, ppu_registers); - ui.label("0x01: "); - paint_cgram_color_address(ui, 0x01, ppu_registers); - ui.label("0x02: "); - paint_cgram_color_address(ui, 0x02, ppu_registers); - }); + let mut address: u8 = 0x00; + for row in 0x00..=0x0F { + ui.horizontal(|ui| { + ui.label(format!("0x{:02X}: ", row)); + for _ in 0x00..=0x0F { + paint_cgram_color_address(ui, address, ppu_registers); + address = address.wrapping_add(1); + } + }); + } }); }