Phase 5: raster IRQ + frame timing
Replace busy-wait frame counter with a 50 Hz raster IRQ at line 311 (PAL stable line). The IRQ handler increments a 16-bit frame_count and calls game_step() once per frame. Per-state timing now uses enter_frame timestamps + frame_count comparisons. - New tick.h/tick.c: install one RIRQ via Oscar64's rirq library, call a __interrupt handler that bumps frame_count and runs the state machine. Mask CIA 1 + CIA 2 IRQs and set RST8 (the high bit of the 9-bit raster register) so the IRQ fires at line 311 not line 55. - game.h: expose volatile frame_count, replace per-state 'frame' counter with enter_frame timestamps. - game.c: use frame_count - enter_frame everywhere; sample SID $D41B at READY enter for a random 100..250 frame WAIT duration; trigger a low-square-wave stinger on SID voice 1 when DRAW faults out (no fire for 500 frames) and gate it off ~0.2 sec later via a counter decremented every frame. - main.c: replace the busy-wait loop with rasterirq_setup() and an empty for(;;); idle. (Filename is tick.c/.h not rasterirq.c/.h because the oscar64 library's own rasterirq.c does '#include "rasterirq.h"' to pull in its own header, and that include would otherwise pick up ours and lose NUM_IRQS.)
This commit is contained in:
+18
-13
@@ -1,4 +1,4 @@
|
||||
// main.c — Whack Hare! entry point (Phase 4: state machine skeleton).
|
||||
// main.c — Whack Hare! entry point (Phase 5: raster IRQ + idle loop).
|
||||
//
|
||||
// Flow:
|
||||
// 1. memmap_setup() — bank out KERNAL/BASIC/CHAR ROM.
|
||||
@@ -6,21 +6,23 @@
|
||||
// 3. game_init() — enter the TITLE state (which also loads
|
||||
// the title screen and renders the score
|
||||
// bar).
|
||||
// 4. while (1) game_step() — the state machine runs forever.
|
||||
// 4. rasterirq_setup() — install the single RIRQ at line 311.
|
||||
// The IRQ handler increments frame_count
|
||||
// and calls game_step() once per frame.
|
||||
// 5. while (1) {} — idle. All per-frame work happens in
|
||||
// the IRQ handler.
|
||||
//
|
||||
// In Phase 4 this is a busy-wait loop: game_step() is called in a
|
||||
// tight loop with no real timing. The "frame counter" inside game.c
|
||||
// is just a count of how many times game_step() has been called, so
|
||||
// the state durations are CPU-bound, not wall-clock-bound.
|
||||
//
|
||||
// In Phase 5 the busy-wait body of the main loop becomes a raster
|
||||
// IRQ handler that runs at 50 Hz, and game_step() is called from
|
||||
// the IRQ. The function signature doesn't change — only the call
|
||||
// site does — so the game logic is the same in both phases.
|
||||
// In Phase 4 step 5 was `while (1) game_step();` — a busy-wait that
|
||||
// called game_step as fast as the CPU could, so the "frame counter"
|
||||
// was CPU-bound. In Phase 5 the busy-wait is gone: game_step is
|
||||
// called from the raster IRQ at exactly 50 Hz, so state durations
|
||||
// are wall-clock-bound (60 frames = 1.2 sec, etc.) regardless of
|
||||
// what the CPU is doing between IRQs.
|
||||
|
||||
#include "memmap.h"
|
||||
#include "game.h"
|
||||
#include "score.h"
|
||||
#include "tick.h"
|
||||
|
||||
// We don't malloc, so the heap is unused. Setting it to 0 frees the
|
||||
// space for the screen data in the main region.
|
||||
@@ -31,9 +33,12 @@ int main(void)
|
||||
memmap_setup();
|
||||
score_init();
|
||||
game_init();
|
||||
rasterirq_setup();
|
||||
|
||||
while (1)
|
||||
game_step();
|
||||
// The raster IRQ does all the per-frame work. The main loop
|
||||
// is a deliberate spin: nothing to do between IRQs.
|
||||
for (;;)
|
||||
;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user