brokem implementation on main.rs - no regression

This commit is contained in:
2026-08-09 18:28:42 -05:00
parent d29dea2d25
commit 2964e6fad2
5 changed files with 181 additions and 18 deletions
+138
View File
@@ -0,0 +1,138 @@
use crate::cartridge::Cartridge;
use crate::mapper::Mirroring;
pub struct Ppu {
ctrl: u8, // $2000
mask: u8, // $2001
status: u8, // $2002
oamaddr: u8, // $2003
oam: [u8; 256], // $2004
scroll_x: u8, // $2005
scroll_y: u8,
write_latch: bool, // shared W toggle for $2005/$2006, reset by $2002 read
vram_addr: u16, // 14-bit
data_buffer: u8, // $2007 read-buffer
vram: [u8; 0x1000], // 4KB nametables (FourScreen support)
palette: [u8; 32],
}
impl Ppu {
pub fn new() -> Self {
Self {
ctrl: 0, mask: 0, status: 0, oamaddr: 0,
oam: [0; 256], scroll_x: 0, scroll_y: 0,
write_latch: false, vram_addr: 0, data_buffer: 0,
vram: [0; 0x1000], palette: [0; 32],
}
}
pub fn read(&mut self, cart: &mut Cartridge, addr: u16, open_bus: u8) -> u8 {
match addr & 0x2007 {
0x2000 => open_bus, // write-only: returns bus contents
0x2001 => open_bus, // write-only
0x2002 => {
let s = (self.status & 0xE0) | (open_bus & 0x1F);
self.status &= !0x80; // reading clears the vblank flag
self.write_latch = false;
s
}
0x2003 => open_bus, // write-only
0x2004 => self.oam[self.oamaddr as usize],
0x2005 => open_bus, // write-only
0x2006 => open_bus, // write-only
0x2007 => {
let data = self.vram_read(cart, self.vram_addr);
let result = if self.vram_addr >= 0x3F00 { data } else { self.data_buffer };
self.data_buffer = data;
self.vram_addr = self.increment_addr();
result
}
_ => open_bus,
}
}
pub fn write(&mut self, cart: &mut Cartridge, addr: u16, val: u8) {
match addr & 0x2007 {
0x2000 => self.ctrl = val,
0x2001 => self.mask = val,
0x2002 => {} // write ignored
0x2003 => self.oamaddr = val,
0x2004 => {
self.oam[self.oamaddr as usize] = val;
self.oamaddr = self.oamaddr.wrapping_add(1);
}
0x2005 => {
if !self.write_latch { self.scroll_x = val; } else { self.scroll_y = val; }
self.write_latch = !self.write_latch;
}
0x2006 => {
if !self.write_latch {
self.vram_addr = (self.vram_addr & 0x00FF) | (((val & 0x3F) as u16) << 8);
} else {
self.vram_addr = (self.vram_addr & 0x7F00) | (val as u16);
}
self.write_latch = !self.write_latch;
}
0x2007 => {
self.vram_write(cart, self.vram_addr, val);
self.vram_addr = self.increment_addr();
}
_ => {}
}
}
fn increment_addr(&self) -> u16 {
let step = if self.ctrl & 0x04 != 0 { 32 } else { 1 };
self.vram_addr.wrapping_add(step) & 0x3FFF
}
fn vram_read(&mut self, cart: &mut Cartridge, addr: u16) -> u8 {
match addr {
0x0000..=0x1FFF => cart.read_chr(addr), // pattern tables -> CHR
0x2000..=0x3EFF => self.nametable_read(cart, addr),
0x3F00..=0x3FFF => self.palette_read(addr),
_ => 0,
}
}
fn vram_write(&mut self, cart: &mut Cartridge, addr: u16, val: u8) {
match addr {
0x0000..=0x1FFF => cart.write_chr(addr, val), // CHR-RAM writes through
0x2000..=0x3EFF => {
let off = self.nametable_offset(cart, addr);
self.vram[off] = val;
}
0x3F00..=0x3FFF => {
let i = palette_index(addr);
self.palette[i] = val;
}
_ => {}
}
}
fn nametable_offset(&self, cart: &Cartridge, addr: u16) -> usize {
let a = addr & 0x2FFF; // fold the $3000-$3EFF mirror into $2000-$2FFF
let table = match cart.mirroring() {
Mirroring::Horizontal => (a >> 10) & 1,
Mirroring::Vertical => (a >> 11) & 1,
Mirroring::OneScreenLow => 0,
Mirroring::OneScreenHigh => 1,
Mirroring::FourScreen => (a >> 10) & 3,
};
table as usize * 0x400 + (a & 0x3FF) as usize
}
fn nametable_read(&self, cart: &Cartridge, addr: u16) -> u8 {
self.vram[self.nametable_offset(cart, addr)]
}
fn palette_read(&self, addr: u16) -> u8 {
self.palette[palette_index(addr)]
}
}
fn palette_index(addr: u16) -> usize {
let mut i = (addr & 0x1F) as usize; // $3F20-$3FFF folds into $3F00-$3F1F
if i >= 0x10 && i % 4 == 0 { i -= 0x10; } // 10/14/18/1C mirror to 00/04/08/0C
i
}