Phase 8: polish and end-to-end test
Banner text rendering (PRESS FIRE / HARE WINS! / SCOOT WINS!): - src/banner.c, src/banner.h: new module; renders NUL-terminated ASCII text in any character row of the multicolor bitmap, using the same 4x8 custom font as the score bar. Truncates to 40 chars, centers in the row, clears the row first. - src/score.c, src/score.h: font extended from 8 to 16 entries — added P, F, W, I, N, !, space (and one reserved). font_lookup() maps ASCII to font index. - src/game.c: TITLE renders 'PRESS FIRE' in row 1 below the score bar; GAMEOVER renders 'HARE WINS!' or 'SCOOT WINS!' based on last_winner (set by game_enter_win_p1/p2). Other states clear row 1 on entry. State transition stinger: - src/audio.c, src/audio.h: audio_play_stinger(voice, duration) starts a low square-wave burst that auto-cleans up via audio_advance_stinger (called from audio_state_step). A 5-frame stinger is fired on every state transition; the per-state voice is chosen to avoid colliding with the new state's audio (TITLE/READY: voice 0/1 free, WAIT: voice 2 free, DRAW: voice 0 free, WIN/GAMEOVER: voice 2 free). Stinger is silenced by the next audio_state_enter() via the existing audio_stop() call. TITLE border flash changed from 12.5 Hz to 1 Hz (50 on, 50 off). Build system: - src/build.sh: added -O0/-O1/-O2/-O3/-Os/-g flag handling. Both default (-O1) and -O3 builds produce a 43913-byte .prg (well under the 51308-byte LOAD"*",8,1 limit). - src/main.c: pinned #pragma stacksize(0x400) — the oscar64 default is the same, but pinning makes the layout predictable across optimization levels. -O3 needs this exact size; larger values cause 'Cannot place stack section' link errors because the optimizer's larger code section leaves less room in the stack/heap gap. - src/tick.c: marked frame_tick_handler __noinline so -O3 doesn't inline the entire state machine (6000+ bytes) into the IRQ handler. With __noinline, the handler is 136 bytes — small enough for the raster line budget. - src/game.h: marked game_step __noinline for the same reason. The .prg is 173 blocks (out of 202 max), well within the BASIC load area. Both default and -O3 builds run cleanly in the oscar64 built-in emulator.
This commit is contained in:
+9
-1
@@ -67,7 +67,15 @@ static RIRQCode frame_tick;
|
||||
// by rirq_init is the actual ISR. This function is called via JSR
|
||||
// from the rirq_isr, and returns with RTS. A/X/Y are saved by the
|
||||
// rirq_isr, so we can clobber them freely.
|
||||
__interrupt void frame_tick_handler(void)
|
||||
//
|
||||
// Also marked __noinline to prevent `-O3` from inlining the entire
|
||||
// state machine (game_step + audio_state_step + all per-state step
|
||||
// functions) into the IRQ handler. Without __noinline, the
|
||||
// optimizer's aggressive inlining makes the handler 6000+ bytes
|
||||
// long, which would overrun its raster-line budget and break the
|
||||
// 50 Hz timing. game_step is large but called only from the IRQ,
|
||||
// so the call/return overhead is negligible.
|
||||
__interrupt __noinline void frame_tick_handler(void)
|
||||
{
|
||||
frame_count++;
|
||||
game_step();
|
||||
|
||||
Reference in New Issue
Block a user