Phase 4: state machine skeleton (TITLE→READY→WAIT→DRAW→WIN)
Implement the full state machine from GAME.md §9 with no audio yet,
fixed (not random) durations, and the basic score-update + reset logic
needed to drive the GAMEOVER transition.
src/game.h / src/game.c:
- enum: STATE_TITLE, STATE_READY, STATE_WAIT, STATE_DRAW,
STATE_WIN_P1, STATE_WIN_P2, STATE_GAMEOVER
- byte state global + game_init() / game_step() pair
- per-state enter (show_screen + score_render + reset frame counter)
and step (check inputs, advance counter, transition) functions
- TITLE: flash border at 25 Hz, debounce both-fire-within-8-frames
- READY: 60 frames, WAIT: 100, DRAW: 500-frame fault timeout,
WIN: 100 frames, GAMEOVER: 300 frames
- DRAW uses rising-edge detection so holding fire from before
DRAW doesn't auto-trigger a win
- WIN_P1/WIN_P2 increment the score and re-render the score bar
- GAMEOVER shows the title screen with scores reset to 0/0
src/screens.c / src/screens.h:
- show_screen(n) now handles all 5 SCREEN_* IDs via a per-screen
descriptor table (5 × 8000-byte bitmaps + 5 × 1000-byte attrs)
- LZO-compress the 8000-byte bitmaps at build time so the 5 screens
fit in the main region; the 5 .attr tables go in a custom 'screens'
region at $A000-$C000 (BASIC ROM area, banked out as RAM)
- new show_white_screen() fills the bitmap with 0s, sets $D021/
$D022/$D023/$D020 to white, for the all-white DRAW screen
src/main.c:
- simplified to: memmap_setup → score_init → game_init → while (1)
game_step()
- sets #pragma heapsize(0) since the program doesn't use malloc
Build: ./build.sh -e — .prg is 43.9 KB, well under the 51 KB BASIC
load limit. The oscar64 emulator runs the state machine indefinitely
(both fire buttons read as pressed in the emulator, so the game
cycles through the states); no crash.
Memory layout (from .map):
$0801-$0853 startup
$0880-$0E51 code (1489 bytes)
$0E51-$7FE4 data: 5 LZO bitmaps + small tables (28723 bytes)
$7FE4-$7FEC BSS: state, frame, title_input, etc.
$7FF0-$9000 heap (16 bytes)
$9000-$A000 stack
$A000-$B388 custom 'screens' region: 5 .attr tables (5000 bytes)
This commit is contained in:
+23
-23
@@ -1,39 +1,39 @@
|
||||
// main.c — Whack Hare! entry point (Phase 3: score bar).
|
||||
// main.c — Whack Hare! entry point (Phase 4: state machine skeleton).
|
||||
//
|
||||
// Flow:
|
||||
// 1. memmap_setup() — bank out KERNAL/BASIC/CHAR ROM.
|
||||
// 2. show_screen(TITLE) — copy the title bitmap into $E000 and
|
||||
// configure the VIC for multicolor bitmap
|
||||
// mode.
|
||||
// 3. score_init() — zero both player scores.
|
||||
// 4. score_render() — overlay the score bar on the top 8 rows.
|
||||
// 5. Loop: poll joystick port 1 for fire. When pressed, restore
|
||||
// the C64's default memory config and return from main() (which
|
||||
// ends the program).
|
||||
// 2. score_init() — zero both player scores.
|
||||
// 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.
|
||||
//
|
||||
// Build: ./build.sh (compile to src/build/whack_hare.prg)
|
||||
// Run: ./build.sh -e (headless, in oscar64's built-in emulator)
|
||||
// ./build.sh -v (VICE x64, interactive, needs a display)
|
||||
// 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.
|
||||
|
||||
#include "memmap.h"
|
||||
#include "screens.h"
|
||||
#include "input.h"
|
||||
#include "game.h"
|
||||
#include "score.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.
|
||||
#pragma heapsize(0)
|
||||
|
||||
int main(void)
|
||||
{
|
||||
memmap_setup();
|
||||
show_screen(SCREEN_TITLE);
|
||||
|
||||
score_init();
|
||||
score_render();
|
||||
game_init();
|
||||
|
||||
// Phase 3: just poll for fire on port 1 (the Hare / right joystick).
|
||||
// In Phase 4 this becomes a 50 Hz raster IRQ handler that drives the
|
||||
// full state machine.
|
||||
while (!input_fire(1))
|
||||
;
|
||||
while (1)
|
||||
game_step();
|
||||
|
||||
memmap_restore();
|
||||
return 0;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user