fix: decouple VBlank ISR and main loop to prevent IRQ stack overflow
- Make handle_vblank strictly increment frame counter (frame++) - Move heavy frame logic (background updates, OAM updates, palette animations, and console stats) to update_frame() on the main thread synchronized with swiWaitForVBlank() - Eliminate 256-byte ARM9 IRQ stack (sp_irq) overflow caused by bg_update and iprintf that was corrupting DTCM stack and causing data aborts - Preserve all playfield entities on game over with top-screen defeat summary and smooth background deceleration Co-authored-by: Antigravity <antigravity@google.com>
This commit is contained in:
1 parent
91908d1f15
commit
496de1a31f
10 files changed
+171
-81
No files matched your search
+51
-24
@@ -153,38 +153,62 @@ void draw_apples_cursed(const GameState &state, const RenderAssets &assets,
|
||||
|
||||
} // namespace
|
||||
|
||||
void draw_game_over_screen(const GameState &state) {
|
||||
iprintf("\x1b[1;2H============================");
|
||||
iprintf("\x1b[2;2H * GAME OVER * ");
|
||||
iprintf("\x1b[3;2H============================");
|
||||
|
||||
iprintf("\x1b[5;4HFinal Score: %5i", state.score);
|
||||
iprintf("\x1b[6;4HApples Eaten: %5i", state.applesEaten);
|
||||
iprintf("\x1b[7;4HSnake Length: %5i", state.snakelength);
|
||||
iprintf("\x1b[8;4HDifficulty: %s", state.hardMode ? "Hard " : "Normal");
|
||||
|
||||
iprintf("\x1b[10;4HCause of Defeat:");
|
||||
const char *cause = "Collision";
|
||||
switch (state.gameOverReason) {
|
||||
case GameOverReason::SelfCollision:
|
||||
cause = "Bite Collision (Hit Body)";
|
||||
break;
|
||||
case GameOverReason::BarrierCollision:
|
||||
cause = "Energy Barrier Impact";
|
||||
break;
|
||||
case GameOverReason::RottenAppleDepleted:
|
||||
cause = "Depleted by Rotten Apple";
|
||||
break;
|
||||
default:
|
||||
cause = "Collision";
|
||||
break;
|
||||
}
|
||||
iprintf("\x1b[11;4H %s", cause);
|
||||
|
||||
iprintf("\x1b[14;2H----------------------------");
|
||||
iprintf("\x1b[16;4HA / START : Play Again");
|
||||
iprintf("\x1b[17;4HB : Main Menu");
|
||||
iprintf("\x1b[18;4HR : Swap Screens");
|
||||
iprintf("\x1b[20;2H============================");
|
||||
}
|
||||
|
||||
void invalidate_hud(RenderAssets &assets) { assets.hudCache.dirty = true; }
|
||||
|
||||
void draw_game(const GameState &state, const RenderAssets &assets) {
|
||||
// OAM slots below BARRIER_OAM_SLOTS are owned by draw_barriers() (VBlank)
|
||||
uint16_t oamId = BARRIER_OAM_SLOTS;
|
||||
|
||||
if (!state.gameOver) {
|
||||
draw_apples(state, assets, oamId);
|
||||
draw_apples_rotten(state, assets, oamId);
|
||||
draw_apples_cursed(state, assets, oamId);
|
||||
draw_snake(state, assets, oamId);
|
||||
draw_apples(state, assets, oamId);
|
||||
draw_apples_rotten(state, assets, oamId);
|
||||
draw_apples_cursed(state, assets, oamId);
|
||||
draw_snake(state, assets, oamId);
|
||||
|
||||
// Hide all unused sprites up to hardware limit (128) to eliminate trailing
|
||||
// ghost sprites
|
||||
if (oamId < 128) {
|
||||
oamClear(&oamSub, oamId, 128 - oamId);
|
||||
}
|
||||
return;
|
||||
// Hide all unused sprites up to hardware limit (128) to eliminate trailing
|
||||
// ghost sprites
|
||||
if (oamId < 128) {
|
||||
oamClear(&oamSub, oamId, 128 - oamId);
|
||||
}
|
||||
|
||||
// On Game Over, hide all sprites so none are left dangling
|
||||
oamClear(&oamSub, 0, 128);
|
||||
|
||||
iprintf("\x1b[11;12HGAME OVER!");
|
||||
consoleSelect(assets.topScreen);
|
||||
iprintf("\x1b[10;10H Score: %i ", state.score);
|
||||
iprintf("\x1b[11;12HGAME OVER!");
|
||||
}
|
||||
|
||||
void draw_barriers(const GameState &state, const RenderAssets &assets,
|
||||
uint8_t frame) {
|
||||
if (state.scene != Scene::Playing || state.gameOver || !state.hardMode) {
|
||||
if (state.scene != Scene::Playing || !state.hardMode) {
|
||||
oamClear(&oamSub, 0, BARRIER_OAM_SLOTS);
|
||||
return;
|
||||
}
|
||||
@@ -257,6 +281,12 @@ void draw_barriers(const GameState &state, const RenderAssets &assets,
|
||||
|
||||
void draw_stats(const GameState &state, RenderAssets &assets, bool internals,
|
||||
bool musicEnabled, uint8_t frame) {
|
||||
if (state.gameOver) {
|
||||
return;
|
||||
}
|
||||
|
||||
HudCache &cache = assets.hudCache;
|
||||
|
||||
if (internals) {
|
||||
iprintf("\x1b[0;0HFrame = %i\nTick = %i\ndir = %i", frame, state.ticks,
|
||||
state.snakeDirection);
|
||||
@@ -279,7 +309,6 @@ void draw_stats(const GameState &state, RenderAssets &assets, bool internals,
|
||||
}
|
||||
}
|
||||
|
||||
HudCache &cache = assets.hudCache;
|
||||
const bool cursed = (state.curseTicksRemaining > 0);
|
||||
|
||||
const bool needs_score_update = cache.dirty || (cache.score != state.score);
|
||||
@@ -305,9 +334,7 @@ void draw_stats(const GameState &state, RenderAssets &assets, bool internals,
|
||||
|
||||
if (needs_pause_update) {
|
||||
if (!state.paused) {
|
||||
if (state.gameOver) {
|
||||
iprintf("\x1b[23;0HB = Main Menu");
|
||||
} else if (cache.paused) {
|
||||
if (cache.paused) {
|
||||
// Clear any leftover pause strings if previously paused
|
||||
iprintf("\x1b[21;0H ");
|
||||
iprintf("\x1b[22;0H ");
|
||||
|
||||
Reference in new issue
Block a user