241. Ppustatus read resets second write toggle

Reading PPUSTATUS ($2002) resets the PPU second-write toggle.

Lesson 241 of 356 · tests/chapter_03_ppu_memory_and_graphics_data/test_241_ppustatus_read_resets_second_write_toggle.py

File to update

emulator/ppu/ppu.py

Method to update

PPU.read_register(addr)

Why this step exists

The NES PPU has two-write registers, especially

$2005 PPUSCROLL
$2006 PPUADDR

Those registers use an internal first-write / second-write toggle. In this tutorial, that state is named:

second_write_toggle

Meaning

False -> the next two-write register access is the first write
True  -> the next two-write register access is the second write

Important PPUSTATUS behavior

Reading $2002 resets this toggle back to False.

Why

Games commonly read PPUSTATUS before writing PPUSCROLL or PPUADDR so the PPU is known to be waiting for the first write again.

Example

write $23 to $2006
    second_write_toggle = True

read $2002
    second_write_toggle = False

write $20 to $2006
    treated as first write / high byte, not as low byte

Suggested implementation pseudocode

def read_register(self, addr: int) -> int:
    match addr:
        case 0x2002:
            value = self.status
            self.status &= ~VBLANK_STARTED
            self.second_write_toggle = False
            return value

        case 0x2004:
            return self.oam_data

        case 0x2007:
            return self.data

        case _:
            raise ValueError(...)

Important invariant

Reading $2002 should return the old status value, then apply side effects.

Side effects currently modeled

  • clear VBLANK_STARTED
  • reset second_write_toggle

Side effects not modeled yet

  • full scroll latch behavior
  • timing/VBlank generation
  • NMI behavior

Run this lesson

uv run pytest tests/chapter_03_ppu_memory_and_graphics_data/test_241_ppustatus_read_resets_second_write_toggle.py -v