More WIP PPU rendering and fix a few more tests (#11)

This commit is contained in:
2025-04-14 19:14:46 -05:00
committed by GitHub
parent 2bf7cc08bc
commit bb51b998e1
9 changed files with 145 additions and 26 deletions

View File

@@ -11,15 +11,22 @@ pub struct COP {}
impl CPUInstruction for COP {
fn execute(&self, registers: &mut Registers, bus: &mut Bus) {
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;
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);
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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);
}

View File

@@ -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());
}
}

View File

@@ -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);

View File

@@ -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<u8>, byte_lo: Option<u8>) {
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)

View File

@@ -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;

View File

@@ -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();
let mut address: u8 = 0x00;
for row in 0x00..=0x0F {
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);
ui.label(format!("0x{:02X}: ", row));
for _ in 0x00..=0x0F {
paint_cgram_color_address(ui, address, ppu_registers);
address = address.wrapping_add(1);
}
});
}
});
}