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
+64
-38
@@ -323,6 +323,14 @@ void bg_update(BackgroundContext &bg_ctx, const GameState &state,
|
||||
const bool hard = state.hardMode;
|
||||
const bool over = state.gameOver;
|
||||
|
||||
if (over) {
|
||||
if (bg_ctx.game_over_fade < 31) {
|
||||
bg_ctx.game_over_fade++;
|
||||
}
|
||||
} else {
|
||||
bg_ctx.game_over_fade = 0;
|
||||
}
|
||||
|
||||
// 1. Hardware Scrolling Update
|
||||
if (state.scene == Scene::Menu) {
|
||||
// Menu: Majestic downward drift for the cyber-horizon feel
|
||||
@@ -337,11 +345,7 @@ void bg_update(BackgroundContext &bg_ctx, const GameState &state,
|
||||
static_cast<uint16_t>(bg_ctx.scroll_sub_x_fp + 24U);
|
||||
} else {
|
||||
// In-Game Playfield:
|
||||
if (over) {
|
||||
// Gentle halt on game over
|
||||
bg_ctx.scroll_sub_x_fp =
|
||||
static_cast<uint16_t>(bg_ctx.scroll_sub_x_fp + 4U);
|
||||
} else if (cursed) {
|
||||
if (cursed && !over) {
|
||||
// High-speed warp and glitch jitter when cursed!
|
||||
const int16_t jitter =
|
||||
(bg_ctx.anim_phase % 4U == 0U) ? 0x0180 : -0x0100;
|
||||
@@ -353,7 +357,7 @@ void bg_update(BackgroundContext &bg_ctx, const GameState &state,
|
||||
bg_ctx.scroll_sub_y_fp = static_cast<uint16_t>(
|
||||
bg_ctx.scroll_sub_y_fp + 0x00E0U + (warp_intensity >> 1U));
|
||||
} else {
|
||||
// Smooth ambient drift scaling gradually with intensity
|
||||
// Smooth ambient drift scaling gradually with intensity (decelerating gently if game over)
|
||||
const uint16_t base_x = hard ? 24U : 16U;
|
||||
const uint16_t base_y = hard ? 20U : 12U;
|
||||
const uint16_t max_x = hard ? 76U : 56U;
|
||||
@@ -362,17 +366,34 @@ void bg_update(BackgroundContext &bg_ctx, const GameState &state,
|
||||
base_x + ((static_cast<uint32_t>(intensity_31) * (max_x - base_x)) / 31U));
|
||||
const uint16_t speed_y = static_cast<uint16_t>(
|
||||
base_y + ((static_cast<uint32_t>(intensity_31) * (max_y - base_y)) / 31U));
|
||||
|
||||
uint16_t sub_step_x = speed_x;
|
||||
uint16_t sub_step_y = speed_y;
|
||||
if (bg_ctx.game_over_fade > 0) {
|
||||
const uint8_t fade = bg_ctx.game_over_fade;
|
||||
sub_step_x = static_cast<uint16_t>(
|
||||
speed_x + ((static_cast<int>(4U - speed_x) * fade) / 31));
|
||||
sub_step_y = static_cast<uint16_t>(
|
||||
speed_y - ((static_cast<int>(speed_y) * fade) / 31));
|
||||
}
|
||||
bg_ctx.scroll_sub_x_fp =
|
||||
static_cast<uint16_t>(bg_ctx.scroll_sub_x_fp + speed_x);
|
||||
static_cast<uint16_t>(bg_ctx.scroll_sub_x_fp + sub_step_x);
|
||||
bg_ctx.scroll_sub_y_fp =
|
||||
static_cast<uint16_t>(bg_ctx.scroll_sub_y_fp + speed_y);
|
||||
static_cast<uint16_t>(bg_ctx.scroll_sub_y_fp + sub_step_y);
|
||||
}
|
||||
|
||||
// Top screen HUD / horizon drift scaling with intensity
|
||||
const uint16_t speed_main_y = static_cast<uint16_t>(
|
||||
// Top screen HUD / horizon drift scaling with intensity (halting on game over)
|
||||
uint16_t speed_main_y = static_cast<uint16_t>(
|
||||
16U + ((static_cast<uint32_t>(intensity_31) * 24U) / 31U));
|
||||
const uint16_t speed_main_x = static_cast<uint16_t>(
|
||||
uint16_t speed_main_x = static_cast<uint16_t>(
|
||||
8U + ((static_cast<uint32_t>(intensity_31) * 12U) / 31U));
|
||||
if (bg_ctx.game_over_fade > 0) {
|
||||
const uint8_t fade = bg_ctx.game_over_fade;
|
||||
speed_main_y = static_cast<uint16_t>(
|
||||
speed_main_y - ((static_cast<int>(speed_main_y) * fade) / 31));
|
||||
speed_main_x = static_cast<uint16_t>(
|
||||
speed_main_x - ((static_cast<int>(speed_main_x) * fade) / 31));
|
||||
}
|
||||
bg_ctx.scroll_main_y_fp =
|
||||
static_cast<uint16_t>(bg_ctx.scroll_main_y_fp + speed_main_y);
|
||||
bg_ctx.scroll_main_x_fp =
|
||||
@@ -401,18 +422,7 @@ void bg_update(BackgroundContext &bg_ctx, const GameState &state,
|
||||
uint16_t col_frame = 0;
|
||||
uint16_t col_frame_accent = 0;
|
||||
|
||||
if (over) {
|
||||
// Somber dark charcoal / muted red
|
||||
col_faint = make_rgb15(3, 2, 2);
|
||||
col_subtle = make_rgb15(6, 3, 3);
|
||||
col_primary = make_rgb15(10, 4, 4);
|
||||
col_star_dim = make_rgb15(5, 2, 2);
|
||||
col_star_med = make_rgb15(8, 3, 3);
|
||||
col_star_bright = make_rgb15(12, 4, 4);
|
||||
col_star_flare = make_rgb15(16, 6, 6);
|
||||
col_frame = make_rgb15(8, 3, 3);
|
||||
col_frame_accent = make_rgb15(12, 4, 4);
|
||||
} else if (cursed) {
|
||||
if (cursed && !over) {
|
||||
// Radioactive neon purple / violet storm
|
||||
const bool strobe = (bg_ctx.anim_phase % 6U < 3U);
|
||||
col_faint = make_rgb15(8, 1, 10);
|
||||
@@ -488,26 +498,42 @@ void bg_update(BackgroundContext &bg_ctx, const GameState &state,
|
||||
col_frame_accent = lerp_color(c_frame_accent_0, c_frame_accent_max, intensity_31);
|
||||
}
|
||||
|
||||
// Smoothly blend toward somber dark charcoal / muted red tone if game over
|
||||
if (bg_ctx.game_over_fade > 0) {
|
||||
const uint8_t fade = bg_ctx.game_over_fade;
|
||||
col_faint = lerp_color(col_faint, make_rgb15(3, 2, 2), fade);
|
||||
col_subtle = lerp_color(col_subtle, make_rgb15(6, 3, 3), fade);
|
||||
col_primary = lerp_color(col_primary, make_rgb15(10, 4, 4), fade);
|
||||
col_star_dim = lerp_color(col_star_dim, make_rgb15(5, 2, 2), fade);
|
||||
col_star_med = lerp_color(col_star_med, make_rgb15(8, 3, 3), fade);
|
||||
col_star_bright = lerp_color(col_star_bright, make_rgb15(12, 4, 4), fade);
|
||||
col_star_flare = lerp_color(col_star_flare, make_rgb15(16, 6, 6), fade);
|
||||
col_frame = lerp_color(col_frame, make_rgb15(8, 3, 3), fade);
|
||||
col_frame_accent = lerp_color(col_frame_accent, make_rgb15(12, 4, 4), fade);
|
||||
}
|
||||
|
||||
// Deep space backdrop color dynamically illuminates with intensity
|
||||
if (in_game && !over) {
|
||||
if (in_game) {
|
||||
uint16_t bg_sub_target = 0;
|
||||
uint16_t bg_main_target = 0;
|
||||
if (hard) {
|
||||
BG_PALETTE_SUB[0] = lerp_color(make_rgb15(3, 1, 1), make_rgb15(6, 2, 2), intensity_31);
|
||||
BG_PALETTE[0] = lerp_color(make_rgb15(2, 1, 1), make_rgb15(5, 1, 2), intensity_31);
|
||||
bg_sub_target = lerp_color(make_rgb15(3, 1, 1), make_rgb15(6, 2, 2), intensity_31);
|
||||
bg_main_target = lerp_color(make_rgb15(2, 1, 1), make_rgb15(5, 1, 2), intensity_31);
|
||||
} else {
|
||||
BG_PALETTE_SUB[0] = lerp_color(make_rgb15(3, 4, 7), make_rgb15(5, 7, 12), intensity_31);
|
||||
BG_PALETTE[0] = lerp_color(make_rgb15(2, 3, 6), make_rgb15(3, 5, 10), intensity_31);
|
||||
bg_sub_target = lerp_color(make_rgb15(3, 4, 7), make_rgb15(5, 7, 12), intensity_31);
|
||||
bg_main_target = lerp_color(make_rgb15(2, 3, 6), make_rgb15(3, 5, 10), intensity_31);
|
||||
}
|
||||
|
||||
if (bg_ctx.game_over_fade > 0) {
|
||||
BG_PALETTE_SUB[0] = lerp_color(bg_sub_target, make_rgb15(3, 2, 2), bg_ctx.game_over_fade);
|
||||
BG_PALETTE[0] = lerp_color(bg_main_target, make_rgb15(2, 1, 1), bg_ctx.game_over_fade);
|
||||
} else {
|
||||
BG_PALETTE_SUB[0] = bg_sub_target;
|
||||
BG_PALETTE[0] = bg_main_target;
|
||||
}
|
||||
} else if (over) {
|
||||
BG_PALETTE_SUB[0] = make_rgb15(3, 2, 2);
|
||||
BG_PALETTE[0] = make_rgb15(2, 1, 1);
|
||||
} else {
|
||||
if (hard) {
|
||||
BG_PALETTE_SUB[0] = make_rgb15(3, 1, 1);
|
||||
BG_PALETTE[0] = make_rgb15(2, 1, 1);
|
||||
} else {
|
||||
BG_PALETTE_SUB[0] = make_rgb15(2, 2, 6);
|
||||
BG_PALETTE[0] = make_rgb15(2, 2, 6);
|
||||
}
|
||||
BG_PALETTE_SUB[0] = make_rgb15(2, 2, 6);
|
||||
BG_PALETTE[0] = make_rgb15(2, 2, 6);
|
||||
}
|
||||
|
||||
// Animate twinkling stars using sine oscillations
|
||||
|
||||
Reference in new issue
Block a user