Files
psxrecomp/runtime/include
Matthew Stanley b486c13c3a runtime: fix VBlank pacing to use guest cycles, not dispatch count
VBlank firing in psx_check_interrupts was gated on dispatch_count
(block-dispatch count, ~5-6 cycles each), not on guest cycles.
50000 dispatches ≈ 289k cycles per VBlank, vs real PSX 564480
cycles per VBlank. Effect: game-internal time ran at ~51% of real
PSX rate, breaking every cycle-paced game flow including FMV.

This was the root cause of TombaRecomp ISSUES.md #4 — FMV played
at 6.7 fps because Tomba's FMV state machine is driven by DMA Ch1
(MDEC-out done) IRQ, which in turn fires once per full FMV-frame
pipeline iteration, which is bounded by cycle-paced steps
(CDROM sector ready, MDEC decode "complete", DMA transfer).
At 51% guest-time rate, a Tomba-designed 15-fps FMV becomes 7-8 fps.

Fix:
- New VBLANK_CYCLES = 564480 (33.8688 MHz / 60 Hz NTSC).
- Add interrupts_advance_cycles(cycles) called from
  psx_advance_cycles() so the cycle counter for VBlank gating
  tracks emitted guest cycles.
- Change the trigger from `dispatch_count >= VBLANK_INTERVAL` to
  `cycles_since_vblank >= VBLANK_CYCLES`.
- On VBlank fire, subtract one period (not reset to 0) so cycle
  overshoot from long blocks carries forward.
- dispatch_count is kept for the SIO card-protocol stale check —
  that uses total_checks, which is the real load-bearing field
  there, dispatch_count itself is now incidental.

Verified:
- Cycles/VBlank: 289k -> 564k (exactly VBLANK_CYCLES).
- Cycles/sec: 20.6 M -> 41 M (121% of real PSX; slight wall-clock
  overshoot to investigate, but game-internal pacing now correct).
- FMV decode rate: 0.111 -> 0.250 decodes/VBlank = 15.0 fps exactly,
  matching Tomba's designed FMV rate.
- Measured via tools/_mdec_pace.py.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-13 14:09:11 -07:00
..
2026-05-07 17:57:34 -07:00