269. Validation console vblank nmi path
VALIDATION: Console.step drives the full VBlank NMI path.
Lesson 269 of 356 · tests/chapter_04_ppu_timing_and_vblank/test_269_VALIDATION_console_vblank_nmi_path.py
No new implementation should be required for this test if steps 259-268 are complete.
References
https://www.nesdev.org/wiki/PPU_registers#PPUCTRL
https://www.nesdev.org/wiki/PPU_registers#Vblank_NMI
https://www.nesdev.org/wiki/CPU_interruptsWhy this validation exists
Previous tests verified each mechanism separately
PPU.step() advances timing
PPU enters VBlank and sets VBLANK_STARTED
PPUCTRL bit 7 enables NMI requests
CPU.step() returns CPU cycles
Console.step() advances PPU by cpu_cycles * 3
Console consumes ppu.nmi_requested
CPU.interrupt_nmi() jumps through $FFFA/$FFFBThis test validates that those pieces work together in one small machine-level scenario.
End-to-end path being validated
CPU executes LDA #$80
-> A = $80
CPU executes STA $2000
-> CpuBus routes $2000 to PPUCTRL
-> PPUCTRL bit 7 enables NMI
Console.step() keeps advancing CPU and PPU together
-> CPU cycles advance PPU by cycles * 3
-> PPU enters VBlank
-> PPU sets nmi_requested = True
-> Console consumes the request
-> CPU.interrupt_nmi() reads $FFFA/$FFFB
-> CPU jumps to the NMI handler addressTiny program used in this validation
$8000: A9 80 LDA #$80 ; set PPUCTRL NMI-enable bit
$8002: 8D 00 20 STA $2000 ; write A to PPUCTRL
$8005: EA NOP ; wait while PPU reaches VBlank
$8006: EA NOP
...NMI vector setup
$FFFA = $00
$FFFB = $90Expected result
CPU PC becomes $9000 after Console consumes the VBlank NMI request.Important
This is still simplified timing. The test intentionally places the PPU near VBlank so the validation is fast and deterministic. This is not dot-accurate NMI latency testing.
Out of scope
- rendering pixels
- PPU sprite behavior
- OAM DMA
- controller input
- exact NMI latency
- dynamic CPU cycle penalties
Run this lesson
uv run pytest tests/chapter_04_ppu_timing_and_vblank/test_269_VALIDATION_console_vblank_nmi_path.py -v