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
+29
-11
@@ -205,19 +205,33 @@ void setup() {
|
||||
}
|
||||
void handle_vblank() {
|
||||
frame++;
|
||||
}
|
||||
|
||||
void update_frame() {
|
||||
bg_update(bgContext, gameState, frame);
|
||||
if (gameState.scene == Scene::Playing) {
|
||||
consoleSelect(&topScreen);
|
||||
draw_stats(gameState, renderAssets, internals,
|
||||
audio_is_music_enabled(audioContext), frame);
|
||||
if (!gameState.paused) {
|
||||
tick_barriers_frame(gameState);
|
||||
if (gameState.gameOver) {
|
||||
HudCache &cache = renderAssets.hudCache;
|
||||
if (cache.dirty || (cache.gameOver != gameState.gameOver)) {
|
||||
consoleClear();
|
||||
draw_game_over_screen(gameState);
|
||||
cache.gameOver = gameState.gameOver;
|
||||
cache.dirty = false;
|
||||
}
|
||||
} else {
|
||||
draw_stats(gameState, renderAssets, internals,
|
||||
audio_is_music_enabled(audioContext), frame);
|
||||
if (!gameState.paused) {
|
||||
tick_barriers_frame(gameState);
|
||||
}
|
||||
}
|
||||
draw_barriers(gameState, renderAssets, frame);
|
||||
}
|
||||
|
||||
// Dynamic pulsing radioactive glow for Cursed Apples (smooth 60fps breathing)
|
||||
const uint8_t pulse = (frame >> 2U) & 7U; // 8-step cycle
|
||||
const auto pulse = static_cast<uint8_t>(
|
||||
(static_cast<uint32_t>(frame) >> 2U) & 7U); // 8-step cycle
|
||||
const uint8_t wave =
|
||||
(pulse < 4U) ? pulse
|
||||
: static_cast<uint8_t>(7U - pulse); // 0, 1, 2, 3, 3, 2, 1, 0
|
||||
@@ -233,8 +247,9 @@ void handle_vblank() {
|
||||
|
||||
// Dynamic festering mold & spore respiration for Rotten Apples (smooth 60fps
|
||||
// organic pulse)
|
||||
const uint8_t rot_pulse = ((frame + 3U) >> 2U) &
|
||||
7U; // 8-step cycle, phase-shifted from cursed apple
|
||||
const auto rot_pulse = static_cast<uint8_t>(
|
||||
(static_cast<uint32_t>(frame + 3U) >> 2U) &
|
||||
7U); // 8-step cycle, phase-shifted from cursed apple
|
||||
const uint8_t rot_wave =
|
||||
(rot_pulse < 4U)
|
||||
? rot_pulse
|
||||
@@ -292,6 +307,7 @@ void do_tick() {
|
||||
if (!was_game_over && gameOver) {
|
||||
audio_stop_music();
|
||||
audio_play_fail(audioContext);
|
||||
invalidate_hud(renderAssets);
|
||||
}
|
||||
// consoleSelect(&bottomScreen);
|
||||
draw_game(gameState, renderAssets);
|
||||
@@ -309,6 +325,7 @@ void init_game() {
|
||||
score = 0;
|
||||
ticks = 0;
|
||||
gameOver = false;
|
||||
gameState.gameOverReason = GameOverReason::None;
|
||||
snakeDirection = 1;
|
||||
gameState.curseTicksRemaining = 0;
|
||||
apples.clear();
|
||||
@@ -338,10 +355,6 @@ void init_game() {
|
||||
}
|
||||
|
||||
int main(void) {
|
||||
|
||||
// Set up VBlank handler
|
||||
irqSet(IRQ_VBLANK, handle_vblank);
|
||||
|
||||
setup();
|
||||
setBackdropColor(RGB15(2, 2, 6));
|
||||
setBackdropColorSub(RGB15(2, 2, 6));
|
||||
@@ -354,7 +367,12 @@ int main(void) {
|
||||
inputContext.topScreen = &topScreen;
|
||||
inputContext.init_game = init_game;
|
||||
|
||||
// Set up VBlank handler only after all subsystems, assets, and contexts are initialized
|
||||
irqSet(IRQ_VBLANK, handle_vblank);
|
||||
|
||||
while (1) {
|
||||
swiWaitForVBlank();
|
||||
update_frame();
|
||||
if (!handle_input_frame(inputContext)) {
|
||||
break;
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user