Skip to content

Account the 7-cycle hardware interrupt entry cost in cycle accounting - #290

Merged
highbyte merged 1 commit into
masterfrom
fix/interrupt-entry-cycles
Aug 13, 2026
Merged

Account the 7-cycle hardware interrupt entry cost in cycle accounting#290
highbyte merged 1 commit into
masterfrom
fix/interrupt-entry-cycles

Conversation

@highbyte

Copy link
Copy Markdown
Owner

Hardware IRQ/NMI entry previously consumed 0 cycles in cycle accounting; real 6502-family entry takes 7 (two dummy opcode reads, three stack pushes, two vector reads — the bus sequence itself was implemented in #289). Follow-up scheduled from that work.

What changes

  • CPU.ProcessPendingInterrupts (and the internal boundary servicing) returns the cycles consumed: new CPU.InterruptEntryCycles (7) when an interrupt was serviced, else 0.
  • The entry cost is folded into the preceding instruction's InstructionExecResult (new WithAdditionalCycles) before statistics update, so every cycle-paced consumer sees real elapsed time with no further changes: ExecState totals (Apple II disk motor/speaker/game-port clocks), LastInstructionExecResult (Apple II / VIC-20 frame budgets and VIA ticking), evaluators, and the InstructionExecuted event.
  • The C64's post-device-tick interrupt flush explicitly ticks CIA1/CIA2 and the cartridge slot with the entry cycles and includes them in the raster advance and frame budget. The freeze-button NMI delivery deliberately ignores them (one-shot event outside frame pacing; commented).
  • Note: on the full Execute path, NmiAcknowledging now fires before InstructionExecuted (servicing moved before stats update); the only NmiAcknowledging consumer (C64) uses the minimal path and is unaffected.

Impact

Frame budgets now fit ~7 fewer CPU cycles per serviced interrupt (~0.04% per raster-IRQ frame on C64) — matching real hardware. All existing C64/VIC-20/Apple II timing tests pass unchanged; benchmarks at parity (hot path gained only a return-value check).

Tests

+4 in CPUInterruptBoundaryTests: IRQ entry costs 2+7 on the minimal path (result and ExecState views), NMI costs 2+7 on the full path, ProcessPendingInterrupts returns 7/0, and a no-interrupt control keeps plain instruction cycles.

Public API note: ProcessPendingInterrupts changed voidulong (source-compatible for callers ignoring the result; package is alpha).

@sonarqubecloud

Copy link
Copy Markdown

@highbyte
highbyte merged commit 1137705 into master Aug 13, 2026
10 checks passed
@highbyte
highbyte deleted the fix/interrupt-entry-cycles branch August 13, 2026 10:20
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant