
Let us walk through a real example. Assume you extracted arm9.bin from a commercial ROM using ndstool.
Step 1: Identify the load address
The header at offset 0x20 says the ARM9 entry point. Usually 0x02000000 or 0x03700000 (for DLDI homebrew).
Step 2: Create a new Ghidra project
Step 3: Define memory regions
Add RAM: 0x02000000 to 0x0203FFFF (256KB ITCM? No – DS has 4MB main RAM). Actually, DS ARM9 main RAM is 0x02000000 to 0x023FFFFF (4MB). Add VRAM banks if needed.
Step 4: Auto-analyze – Ghidra will find code entry points. Use the Decompiler window.
Step 5: Interpret the output – For a simple function that sets the display mode, Ghidra might give:
void setMode3(void)
DAT_04000000 = 0x23; // hand-correct to DISPCNT = MODE_3
This is not original source, but it is functionally equivalent.
This is where the actual "decompilation" happens. nds decompiler
Example of what decompiled code looks like (pseudo-C):
void FUN_02001234(void)
// This is likely a sprite initialization function
(*(volatile u32 *)0x4000000) = 0x10000;
(*(volatile u32 *)0x4000004) = 0x0;
return;
Decompiling a Nintendo DS game is the process of converting the machine code (binary) stored on the cartridge back into a human-readable format (such as C or C++ source code). This is a reverse engineering process used for game preservation, creating fan translations, or fixing bugs in old games.
It is important to note that you cannot simply click a button and get the original source code. The process requires significant manual effort.
The Nintendo DS (NDS) is a dual-screen ARM-based handheld console released in 2004. Decompilation in the context of NDS games refers to the process of translating compiled machine code (ARM9, ARM7, or Thumb binaries) back into a high-level language, ideally human-readable C or C++ code.
Unlike simple disassembly (which gives assembly mnemonics), a decompiler attempts to recover structure: loops, conditionals, functions, variable names, and data types.
However, true full-decompilation of arbitrary NDS ROMs remains an unsolved automation problem. Most successful efforts are semi-manual and game-specific (e.g., Pokémon Diamond, Super Mario 64 DS).
Summary
What it does well
Common limitations
Who should use it
Practical tips
Verdict (concise)
Related search suggestions (Function invoked)
Goal: Decompile a function that sets up 3D rendering on ARM9. Let us walk through a real example
Step 1 – Locate function in disassembly (Ghidra):
08001234: push r4, lr
08001236: mov r0, #0x4000000
0800123a: mov r1, #0x1000
0800123e: strh r1, [r0, #0x0] ; DISPCNT
Step 2 – Decompile (Ghidra output):
void FUN_08001234(void)
*(uint16_t*)0x4000000 = 0x1000;
return;
Step 3 – Manual refinement:
#include "nds/arm9/display.h" // manual header
void enableBG3D()
DISPCNT = DISPLAY_BG3_ACTIVE
Step 4 – Iterate – rename function, propagate types, match to NitroSDK.
If you want to “decompile” an NDS game:
| Challenge | Description | |-----------|-------------| | Thumb/ARM interworking | Decompilers often misalign control flow at mode switches | | Inlined assembly | SDK macros use inline asm for speed; decompiler produces gibberish | | Overlays | Code loaded at runtime into same address space – static analysis misses cross-overlay calls | | Custom memory maps | NDS has 8+ distinct memory regions (Main RAM, VRAM, Shared WRAM, etc.) – pointers ambiguous | | Register banking | ARM9 has banked registers for IRQ/Supervisor modes – decompiler sees only user mode | | Binary differencing | Matching decompiled code to known SDK versions requires signature scanning |
![]()