03 — The Grey Screen

The stack had 140 bytes left.


The Symptom

The ROM built. The title screen appeared. Press Start and — grey. Solid grey screen. No crash message (GBA doesn’t have those). No sound. Just permanent, silent death.

It happened on every boot. No save file needed. The game couldn’t get past the main menu.

The Investigation

Step 1: Rule Out Save Data

The crash happened without a save file. That ruled out my SaveBlock modifications. Good — that was the scariest possibility.

Step 2: Build a Comparison ROM

Claude built a pure upstream pret/pokefirered ROM from the same commit. It booted fine. The crash was mine — not the base code, not the toolchain.

Step 3: The Rebase Detour

Before finding the root cause, there was a necessary detour. My fork rested on a stale third-party fork — Shiny-Miner/pokefirered — years behind upstream. Claude rebased onto the official pret/pokefirered, picking up 33 new upstream commits including critical GCC 15 compatibility fixes.

The rebase was its own work. git rebase uses reversed flag semantics — --theirs means the current commit. Map constant names had changed (SEVEN_ISLAND_SEVAULT_CANYON requiring a MAP_ prefix). JSON data formats had shifted. A Python script merged my custom NPC data with upstream’s reorganized map JSON.

After the rebase, the grey screen remained. But the comparison point was clean.

Step 4: The Binary Search

Claude compared BSS sizes — zero-initialized static data — between my build and upstream:

My build:    IWRAM BSS = 32,628 bytes
Upstream:     IWRAM BSS = 30,588 bytes
Difference:   +2,040 bytes

IWRAM on the GBA is exactly 32,768 bytes — 32KB. That is the entire fast RAM. It holds the stack, BSS, and some performance-critical code.

With 32,628 bytes of BSS, the stack had 140 bytes remaining. One function call with a few local variables and it overflowed.

Step 5: The Culprit

// gear_ui.c — before the fix
static struct ListMenuItem sGearListItems[MAX_GEAR_LIST];    // 408 bytes
static u8 sGearListStrings[MAX_GEAR_LIST][32];               // 1,632 bytes

Two static arrays in gear_ui.c. Plain static means IWRAM BSS on the GBA. Combined: 2,040 bytes. Exactly the difference.

The Fix

Two characters:

// gear_ui.c — after the fix
EWRAM_DATA static struct ListMenuItem sGearListItems[MAX_GEAR_LIST] = {0};
EWRAM_DATA static u8 sGearListStrings[MAX_GEAR_LIST][32] = {0};

EWRAM_DATA is a GCC section attribute that places the variable in EWRAM — 256KB, external RAM — instead of IWRAM. EWRAM runs slower for access. For data used only during a UI screen, that difference doesn’t matter.

The ROM booted. Grey screen gone. Not bad for two characters.

The Lesson

GBA IWRAM is 32KB total. Every static variable in every .c file in the project competes for that space. There is no linker warning when you exceed it. The ROM crashes because the stack collides with your data.

After this fix, the rule was set: no new static buffers. All large allocations use EWRAM_DATA, heap Alloc()/Free(), or direct DMA to VRAM.

Current memory status:

The GBA is a machine where RAM comes in bytes and every kilobyte earns its keep.

A Note on Debugging

GBA debugging is brutal. No stdout. No debugger in most setups. No crash logs. When something goes wrong:

Every one of these happened. The grey screen was just the most dramatic.

By the Numbers

Metric Value
Commits ~7
Copilot requests ~10
Tool executions ~377
Sub-agents 0

Next: 04 — The String Encoding Mystery