From f4c77b32d197d844c03f85a9c7d4a3a340155037 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Phillip=20K=C3=BChne?= Date: Sun, 4 Oct 2026 00:36:22 +0200 Subject: [PATCH] refactor: extract melonDS GDB debugging procedures into melonds-gdb-debugger skill Create the workspace skill .agents/skills/melonds-gdb-debugger/ with SKILL.md and diagnose_melonds.sh diagnostic automation script. Streamline AGENTS.md to link to the new skill to keep it short and concise. Co-authored-by: Antigravity --- .agents/skills/melonds-gdb-debugger/SKILL.md | 90 +++++++++++++++++++ .../scripts/diagnose_melonds.sh | 76 ++++++++++++++++ AGENTS.md | 13 +-- 3 files changed, 173 insertions(+), 6 deletions(-) create mode 100644 .agents/skills/melonds-gdb-debugger/SKILL.md create mode 100755 .agents/skills/melonds-gdb-debugger/scripts/diagnose_melonds.sh diff --git a/.agents/skills/melonds-gdb-debugger/SKILL.md b/.agents/skills/melonds-gdb-debugger/SKILL.md new file mode 100644 index 0000000..040f753 --- /dev/null +++ b/.agents/skills/melonds-gdb-debugger/SKILL.md @@ -0,0 +1,90 @@ +--- +name: melonds-gdb-debugger +description: >- + Attach to and debug Nintendo DS homebrew applications running in melonDS using arm-none-eabi-gdb. + Use when diagnosing crashes, freezes, infinite loops, data aborts (__arm_excpt_dabt, __excpt_entry), + stack corruption, or setting breakpoints and inspecting registers/memory on the ARM9 target. +--- + +# melonDS GDB Debugger Access & Diagnostics + +This skill provides step-by-step procedures and recipes for debugging Nintendo DS homebrew applications running inside the melonDS emulator using `arm-none-eabi-gdb`. + +--- + +## 1. Prerequisites & Target Setup + +- **Toolchain GDB:** `${DEVKITPRO}/devkitARM/bin/arm-none-eabi-gdb` (or system `arm-none-eabi-gdb`). +- **GDB Server Port:** `localhost:3333` (configured in melonDS when GDB stub is enabled in `melonDS.ini`). +- **ELF Symbol File:** [`snakePlus.elf`](../../../snakePlus.elf) or [`build-debug/snakePlus.elf`](../../../build-debug/snakePlus.elf). + +--- + +## 2. Quick Connection & Non-Interactive Inspection + +To inspect a running or frozen melonDS instance without an interactive prompt: + +```bash +arm-none-eabi-gdb snakePlus.elf --batch \ + -ex "target remote localhost:3333" \ + -ex "info registers" \ + -ex "detach" \ + -ex "quit" +``` + +To run a diagnosis using the bundled helper script: + +```bash +bash .agents/skills/melonds-gdb-debugger/scripts/diagnose_melonds.sh snakePlus.elf +``` + +--- + +## 3. Diagnosing Crashes, Freezes & Data Aborts + +### 3.1 Exception Vectors in Calico / devkitARM +When an unhandled ARM exception occurs, the hardware branches to vector handlers in ROM/BIOS or Calico runtime: +- **`0x01ff8560` (`__arm_excpt_dabt`):** Data Abort exception handler. +- **`0x01ff8594` (`__excpt_entry`):** Common exception entry point executing an infinite loop (`b .`). +- **`0x01ff84ec` (`armWaitForIrq + 4`):** Low-power halt state while waiting for VBlank or Timer IRQ. **Normal running state.** + +If melonDS freezes, appears unresponsive, or outputs `attempt to restore CPSR under bad mode`, attach GDB and inspect `$pc`. If `$pc` is at `0x01ff8594`, the CPU hit a fatal exception. + +### 3.2 Extracting the Faulted Instruction & SPSR +In Calico's `__excpt_entry`, the exception frame is pushed to memory at `0x02fffd80`: +- **Faulted PC (`lr_abt - 8`):** Stored at `0x02fffd98`. +- **Faulted SPSR:** Stored at `0x02fffd90`. + +Run the following commands in GDB to identify the offending source line: + +```gdb +(gdb) target remote localhost:3333 +(gdb) x/x 0x02fffd98 +0x2fffd98: 0x02004284 +(gdb) info line *0x02004284 +Line 123 of "/path/to/source/game_input.cpp" starts at address 0x2004280 ... +``` + +### 3.3 Diagnosing Stack Overflows & Stack Smash +In `calico/lib/ds9.ld`, the ARM9 IRQ stack has a total budget of only 256 bytes (`__sp_irq = __sp_usr + 0x100`), growing downward into user stack (`__sp_usr`). +- If an interrupt handler (such as `handle_vblank`) calls deep functions or formatting routines (`iprintf`), it clobbers `__sp_usr`. +- **Symptom:** In GDB, `$sp` is corrupt or misaligned (e.g. `sp = 0x02fffd8f`, with bit 0 set), or functions crash immediately on register restore / return (`ldmia sp!`). + +--- + +## 4. Interactive Debugging Session + +1. **Launch melonDS with ROM:** + ```bash + melonDS snakePlus.nds + ``` +2. **Attach GDB:** + ```bash + arm-none-eabi-gdb snakePlus.elf -ex "target remote localhost:3333" + ``` +3. **Common GDB Actions:** + - **Set Breakpoint:** `b update_frame` or `b source/game_input.cpp:50` + - **Continue Execution:** `c` + - **Step Instruction / Source:** `step` (or `next`) + - **Read Memory / Registers:** `info registers`, `x/16xw $sp` + - **Detach Safely (keeps emulator running):** `detach`, then `quit` diff --git a/.agents/skills/melonds-gdb-debugger/scripts/diagnose_melonds.sh b/.agents/skills/melonds-gdb-debugger/scripts/diagnose_melonds.sh new file mode 100755 index 0000000..1263c62 --- /dev/null +++ b/.agents/skills/melonds-gdb-debugger/scripts/diagnose_melonds.sh @@ -0,0 +1,76 @@ +#!/usr/bin/env bash +# diagnose_melonds.sh - Automated diagnostics for melonDS via GDB +set -euo pipefail + +ELF_FILE="${1:-snakePlus.elf}" + +if [ ! -f "$ELF_FILE" ]; then + if [ -f "build-debug/snakePlus.elf" ]; then + ELF_FILE="build-debug/snakePlus.elf" + elif [ -f "build/snakePlus.elf" ]; then + ELF_FILE="build/snakePlus.elf" + else + echo "[-] Error: ELF file not found: $ELF_FILE" + exit 1 + fi +fi + +# Locate arm-none-eabi-gdb +GDB_BIN="arm-none-eabi-gdb" +if [ -n "${DEVKITPRO:-}" ] && [ -x "${DEVKITPRO}/devkitARM/bin/arm-none-eabi-gdb" ]; then + GDB_BIN="${DEVKITPRO}/devkitARM/bin/arm-none-eabi-gdb" +elif ! command -v "$GDB_BIN" &>/dev/null; then + echo "[-] Error: arm-none-eabi-gdb not found on PATH or DEVKITPRO." + exit 1 +fi + +echo "[*] Connecting to melonDS on localhost:3333 using $ELF_FILE..." + +# Run GDB in batch mode to gather CPU state +GDB_OUTPUT=$("$GDB_BIN" "$ELF_FILE" --batch \ + -ex "set confirm off" \ + -ex "set pagination off" \ + -ex "target remote localhost:3333" \ + -ex "info registers" \ + -ex "x/x 0x02fffd98" \ + -ex "x/x 0x02fffd90" \ + -ex "detach" \ + -ex "quit" 2>&1 || true) + +if echo "$GDB_OUTPUT" | grep -q "Connection refused"; then + echo "[-] Could not connect to localhost:3333. Ensure melonDS is running with GDB enabled." + exit 1 +fi + +echo "=== Registers ===" +echo "$GDB_OUTPUT" | grep -E "^(r0|r4|r8|r12|sp|lr|pc|cpsr)" || true + +PC=$(echo "$GDB_OUTPUT" | awk '/^pc/ {print $2}') +SP=$(echo "$GDB_OUTPUT" | awk '/^sp/ {print $2}') + +echo "" +echo "=== CPU Analysis ===" +echo "Current PC: $PC" +echo "Current SP: $SP" + +if [ "$PC" = "0x1ff8594" ] || [ "$PC" = "0x01ff8594" ]; then + echo "[!] CPU is trapped in __excpt_entry (Fatal Exception / Data Abort / Prefetch Abort)!" + FAULT_ADDR=$(echo "$GDB_OUTPUT" | awk '/0x2fffd98:/ {print $2}') + FAULT_SPSR=$(echo "$GDB_OUTPUT" | awk '/0x2fffd90:/ {print $2}') + echo " Faulted Instruction Address (lr_abt - 8): $FAULT_ADDR" + echo " Faulted SPSR: $FAULT_SPSR" + + if [ -n "$FAULT_ADDR" ] && [ "$FAULT_ADDR" != "0x0" ]; then + echo "=== Source Location ===" + "$GDB_BIN" "$ELF_FILE" --batch \ + -ex "info line *$FAULT_ADDR" \ + -ex "quit" 2>&1 | grep -v "^(gdb)" || true + fi +elif [ "$PC" = "0x1ff84ec" ] || [ "$PC" = "0x01ff84ec" ]; then + echo "[+] CPU is sleeping in armWaitForIrq (Normal running state waiting for VBlank/Timer IRQ)." +else + echo "[*] CPU is running user code at $PC." + "$GDB_BIN" "$ELF_FILE" --batch \ + -ex "info line *$PC" \ + -ex "quit" 2>&1 | grep -v "^(gdb)" || true +fi diff --git a/AGENTS.md b/AGENTS.md index 23b22db..ff537bf 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -61,6 +61,10 @@ snakePlus/ ├── tools/ # Tooling & automation scripts │ └── clang_tidy_watch.sh # File watcher script running clang-tidy on file change │ +├── .agents/ # Workspace agent customizations +│ └── skills/ +│ └── melonds-gdb-debugger/ # GDB debugging runbook & diagnostic script +│ ├── .vscode/ # VS Code configuration │ ├── launch.json # GDB debugger launch for melonDS (port 3333) │ ├── settings.json # clangd and editor settings @@ -238,12 +242,9 @@ The repository includes preconfigured tasks in [`.vscode/tasks.json`](.vscode/ta - **Input Scanning Frequency & Bus Contention:** `scanKeys()` must be called once per frame synchronized with `swiWaitForVBlank()`. Calling `scanKeys()` in an unthrottled loop (spinning at 66 MHz) saturates ARM7 IPC queues (`touchRead`, `keypadGetState`), resets key-debounce edge detectors within microseconds, and leads to dropped inputs. - **Boot Subsystem Sequencing:** Always complete hardware configuration, console initialization, and data structure setup (`setup()`) before enabling the VBlank interrupt (`irqSet(IRQ_VBLANK, handle_vblank)`). Enabling the ISR beforehand causes VBlank interrupts to fire while `consoleLoadFont` is streaming font tiles to VRAM, triggering Data Aborts at startup. - **Safe State Re-initialization:** In `init_game()`, call `timerStop(0)` before resetting game state or clearing containers, and only call `timerStart(0, ...)` at the very end when all state members and HUD caches are cleanly initialized. -3. **Debugging Traps & Data Abort Diagnosis with GDB:** - - In Calico/devkitARM, unhandled Data Aborts route to `__arm_excpt_dabt` (`0x01ff8560`) and trap into an infinite loop at `__excpt_entry` (`0x01ff8594`). - - If melonDS appears unresponsive or logs `attempt to restore CPSR under bad mode`, attach `arm-none-eabi-gdb` to port 3333 (`target remote localhost:3333`): - - In `__excpt_entry`, inspect memory at `0x02fffd80`: the faulted instruction's address (`lr_abt - 8`) is pushed to `0x02fffd98`, and the faulted `SPSR` is at `0x02fffd90`. - - An unaligned/odd stack pointer (e.g. `sp = 0x02fffd8f`) indicates memory corruption caused by an `sp_irq` stack overflow clobbering `__sp_usr`. - - Use `info line *0x...` in GDB on the faulted PC to instantly locate the offending C++ line. +3. **Debugging with GDB & melonDS:** + - melonDS exposes a GDB stub on `localhost:3333`. + - For step-by-step instructions, exception vector addresses (`__arm_excpt_dabt`, `__excpt_entry`), stack-smash diagnosis, and automated diagnostic scripts, refer to the [`melonds-gdb-debugger`](.agents/skills/melonds-gdb-debugger/SKILL.md) skill. 4. **Hardware Registers & Memory Banking:** - Graphic tiles and palettes must be transferred using DMA (`dmaCopy`) or direct memory copies into allocated VRAM / OAM memory banks. - Do not modify VRAM allocation configurations without ensuring compatibility with console text generators and OAM sub-engines.