Commit acb613f
Implement hybrid inline PPU/APU stepping for cycle-level accuracy
Redesign PPU synchronization to advance the PPU by 3 dots after every CPU bus
operation (load/write/push/pull), keeping it in sync at the bus-cycle level.
This replaces the old lazy catch-up approach where the PPU was only advanced
when PPU registers were accessed. PPU and APU state are now always current,
enabling accurate NMI timing, VBlank edge detection, and sprite 0 hit behavior.
Key changes:
- Add ppu.step(dots) method: encapsulates dot-by-dot PPU logic with fast path
for common cases and handles VBlank set/clear, sprite 0 hits, scanline boundaries
- Wire inline PPU stepping into all CPU bus operations (load, write, push, pull)
- Simplify frame loop: removes PPU catch-up math and dot-by-dot processing
- Implement precise 0-delay vs 1-delay NMI detection using remaining PPU dots
after the edge, matching real 6502 behavior
- Add NMI handler dummy reads (7 cycles matching real hardware)
- Fix critical bug: update instrBusCycles before missing-cycles PPU step to
prevent double-counting in NMI delay formula
Results:
- AccuracyCoin: 98/134 pass (up from 97), including newly passing SHY and DMA+open-bus
- All 445 unit tests pass
- Performance: ~1621 fps (10% slower than baseline, acceptable for accuracy)
Fixes:
- NMI Control (0x0422): proper 1-delay when edge occurs late in instruction
- NMI Timing (0x0453): cycle-level PPU sync enables exact NMI edge detection
- DMA + open bus (0x046c): now passing with inline stepping
- SHY timing (0x0449): now passing after NMI/interrupt fixes
See https://www.nesdev.org/wiki/Catch-up and https://www.nesdev.org/wiki/CPU_interrupts
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>1 parent 6f71212 commit acb613f
File tree
6 files changed
+315
-294
lines changed- src
- papu
- ppu
- test
6 files changed
+315
-294
lines changed
0 commit comments