249. Ppudata read buffer
Implement PPUDATA ($2007) buffered read behavior.
Lesson 249 of 356 · tests/chapter_03_ppu_memory_and_graphics_data/test_249_ppudata_read_buffer.py
Reference
https://www.nesdev.org/wiki/PPU_registers#PPUDATAFile to update
emulator/ppu/ppu.pyState to add
ppu_data_buffer: int = 0Why this step exists
PPUDATA is not a normal simple register. It is a CPU-visible port into PPU memory. For most PPU memory reads, the NES returns the old internal read buffer, then reloads that buffer from the current PPU memory address.
Normal buffered read behavior
read $2007:
value = ppu_data_buffer
ppu_data_buffer = ppu_bus.read(vram_addr)
vram_addr += increment
return valueThis means the first read usually returns stale/old buffer data, and the second read returns the byte that was loaded by the first read.
Example
ppu_bus[$2000] = $AA
ppu_bus[$2001] = $BB
ppu_data_buffer = $00
vram_addr = $2000
read $2007 -> returns $00, buffer becomes $AA, vram_addr becomes $2001
read $2007 -> returns $AA, buffer becomes $BB, vram_addr becomes $2002Compatibility/debug field
The older tutorial model had data as a simple PPUDATA value. Keep self.data as a compatibility/debug field containing the value returned by the latest
PPUDATA read, but the real behavior should use
ppu_data_buffer
ppu_bus.read(vram_addr)Suggested implementation pseudocode
@dataclass
class PPU:
...
data: int = 0 # Compatibility/debug: last PPUDATA value
ppu_data_buffer: int = 0
def read_register(self, addr: int) -> int:
match addr:
...
case 0x2007:
value = self.ppu_data_buffer
self.ppu_data_buffer = self.ppu_bus.read(self.vram_addr)
increment = 32 if self.ctrl & CTRL_VRAM_INCREMENT_BY_32 else 1
self.vram_addr = (self.vram_addr + increment) & 0x3FFF
# Preserve old compatibility/debug field.
self.data = value
return self.dataImportant future note
Palette reads from $3F00-$3FFF are an exception on real hardware. They return the palette byte immediately instead of returning the delayed buffer value. That exception is intentionally not implemented in this test.
Out of scope
- palette read exception
- palette RAM accuracy
- PPUDATA writes, already tested earlier
Run this lesson
uv run pytest tests/chapter_03_ppu_memory_and_graphics_data/test_249_ppudata_read_buffer.py -v