240. Ppuaddr second write toggle
Implement PPUADDR ($2006) two-write address behavior.
Lesson 240 of 356 · tests/chapter_03_ppu_memory_and_graphics_data/test_240_ppuaddr_second_write_toggle.py
Reference
https://www.nesdev.org/wiki/PPU_registers#PPUADDRFile to update
emulator/ppu/ppu.pyState to add
vram_addr: int = 0
temp_vram_addr: int = 0
second_write_toggle: bool = FalseWhy this step exists
PPUADDR is the CPU-visible register at $2006. The CPU uses it to set the PPU's internal VRAM address. That internal address is later used by PPUDATA ($2007) to read/write PPU memory.
Important idea
$2006 is not just a simple one-byte storage register. It is a two-write port.
The CPU writes the address in two parts
first write -> high byte
second write -> low byteExample
write $20 to $2006
write $00 to $2006Result after both writes
vram_addr == $2000Important internal-register model
The first $2006 write updates temp_vram_addr, not vram_addr. The second $2006 write completes temp_vram_addr and copies it into vram_addr.
Why second_write_toggle: The PPU must remember whether the next $2006 write is the first or second write. In this tutorial, second_write_toggle means:
False -> next $2006 write is the first write / high byte
True -> next $2006 write is the second write / low byteWhy the high byte is masked with 0x3F
The PPU address space is 14-bit
$0000-$3FFFOnly the lower 6 bits of the high byte are useful for this address range. So the first write should use:
(value & 0x3F) << 8Suggested implementation pseudocode
@dataclass
class PPU:
...
addr: int = 0
vram_addr: int = 0
temp_vram_addr: int = 0
second_write_toggle: bool = False
def write_register(self, addr: int, value: int) -> None:
value = value & 0xFF
match addr:
...
case 0x2006:
# Keep old simple register-field behavior for test compatibility.
self.addr = value
if not self.second_write_toggle:
self.temp_vram_addr = (
(self.temp_vram_addr & 0x00FF)
| ((value & 0x3F) << 8)
)
self.second_write_toggle = True
else:
self.temp_vram_addr = (
(self.temp_vram_addr & 0x3F00)
| value
)
self.vram_addr = self.temp_vram_addr
self.second_write_toggle = False
...Out of scope
- PPUDATA ($2007) writing through PpuBus
- PPUSTATUS ($2002) resetting the write toggle
- PPUSCROLL ($2005) using this same toggle
- increment-by-32 behavior from PPUCTRL
Run this lesson
uv run pytest tests/chapter_03_ppu_memory_and_graphics_data/test_240_ppuaddr_second_write_toggle.py -v