A from-scratch Nintendo DS emulator written in C++20
A solo project to build a Nintendo DS emulator from scratch in modern C++. The primary target is Pokรฉmon HeartGold / SoulSilver, with full compatibility goals for the rest of the Gen 4 and Gen 5 Pokรฉmon DS lineup:
- ๐ Pokรฉmon Diamond / Pearl
- ๐ Pokรฉmon Platinum
- โค๏ธ Pokรฉmon HeartGold / SoulSilver (primary target)
- โซ Pokรฉmon Black / White
- โซ Pokรฉmon Black 2 / White 2
The DS isn't "GBA + more" โ it's a fundamentally different machine, so the architecture starts from scratch:
- ๐ง Two CPUs on two bus domains โ ARM9 (ARMv5TE @ 67 MHz, with caches + TCM) and ARM7 (ARMv4T @ 33 MHz). Each CPU has its own
Busobject modeling its view of memory. - โฑ๏ธ Single central event scheduler (min-heap of timed events) drives everything. Nothing advances time except
scheduler.run_until(t)โ no scanline-chunk loop, no per-subsystem clocks. - ๐จ Two 2D PPU engines (main + sub) sharing one class via an
is_mainflag โ four BG modes, sprite engine, windowing, blending, master brightness. - ๐ Two-stage 3D pipeline โ geometry engine โ
Frame3Dhand-off struct โ software rasterizer, with post-processing passes for edge marking, fog, and toon/highlight. The door is deliberately left open for a hardware backend later. - ๐งฉ Runtime-reconfigurable VRAM modeled as 9 independently-mapped banks (AโI) plus per-consumer page tables that are rebuilt on
VRAMCNTwrites. - ๐ SPU on the ARM7 side โ 16 channels (PCM8 / PCM16 / IMA-ADPCM / PSG / noise) plus sound capture units.
- ๐ Direct boot only โ no BIOS, no firmware dumps required. The cart's KEY1 decryption is handled by a hardcoded seed table, and BIOS SWIs are provided by a minimal HLE layer.
- ๐ช Three CMake targets:
ds_core(platform-free),ds_frontend(the only SDL-aware code), andds_emulator(the binary). Unit tests linkds_coreonly โ no SDL, no window. - ๐ฉป In-emulator X-Ray debug overlay โ page-cycled real-time view of CPU state, bus page tables, VRAM bank map, OAM, palette, geometry FIFO, polygon list, scheduler events, IPC state, and SPU channels.
- SDL2 โ
brew install sdl2(macOS) /apt install libsdl2-dev(Linux) - CMake 3.20+ โ
brew install cmake/apt install cmake - C++20 compiler โ clang 15+ or gcc 11+
That's it. SDL2 is the only runtime dependency.
git clone https://github.com/amyanger/ds-emulator.git
cd ds-emulator
mkdir -p build && cd build
cmake .. -DCMAKE_BUILD_TYPE=Debug
make./ds_emulator <rom.nds> # boot a ROM
./ds_emulator <rom.nds> --scale 3 # 3x window scaleROMs go in roms/. Saves are written to saves/<game_code>.sav automatically. No BIOS or firmware required.
cd build
ctest --output-on-failureUnit tests link against ds_core only โ no SDL, no window, runs in milliseconds.
First-class build target. Optional optimized build flag enables arm64-specific tuning:
mkdir -p build && cd build
cmake .. -DCMAKE_BUILD_TYPE=Release -DDS_TARGET_APPLE_SILICON=ON
makeWhen enabled, this turns on:
- ๐ ๏ธ
-mcpu=apple-m1clang tuning - ๐ฏ NEON SIMD rasterizer paths
- ๐งต
QOS_CLASS_USER_INTERACTIVEthread attribute (keeps the emulator off efficiency cores) - ๐ Codesign / JIT entitlement scaffolding
The default build stays portable across macOS arm64, macOS x86_64, Linux, and Windows. Apple-specific code is confined to src/frontend/platform/macos.cpp and src/gpu3d/simd_neon.cpp โ nothing else uses #ifdef __APPLE__.
| NDS Button | Default Key |
|---|---|
| A | Z |
| B | X |
| X | S |
| Y | A |
| Start | Enter |
| Select | Right Shift |
| L | Q |
| R | W |
| D-Pad | Arrow Keys |
| ๐ Touch | Left mouse drag |
| ๐ผ Lid open/close | L |
| ๐ค Mic (blow) | M (hold) |
Rebindable via keybinds.cfg. MFi / Xbox / DualSense controllers auto-detected via SDL2.
| Key | Action |
|---|---|
| F1 | Dump both CPUs' state to stderr |
| F2 | Toggle ๐ฉป X-Ray debug overlay |
| F3 | Step one instruction |
| F4 | Step one scanline |
| F5 | Step one frame |
| F6 | Toggle ARM9 instruction trace |
| F7 | Toggle ARM7 instruction trace |
| F8 | Dump main RAM |
| F9 | Dump VRAM (all banks) |
| F10 | Save state |
| F11 | Load state |
| Tab | Cycle X-Ray overlay pages |
| Esc | Quit |
ds-emulator/
โโโ ๐ docs/specs/ โ Design spec (source of truth)
โโโ ๐ ๏ธ cmake/ โ Build helpers
โ โโโ CompilerWarnings.cmake
โ โโโ AppleSilicon.cmake
โ โโโ Sanitizers.cmake
โโโ ๐ฆ include/ds/ โ Cross-cutting types
โ โโโ common.hpp
โ โโโ fixed.hpp โ templated Fixed<I, F>
โ โโโ ring_buffer.hpp โ CircularBuffer<T, N>
โโโ โ๏ธ src/
โ โโโ main.cpp โ Entry point
โ โโโ nds.hpp / nds.cpp โ Top-level system
โ โโโ scheduler/ โ Min-heap event scheduler
โ โโโ cpu/
โ โ โโโ arm9/ โ ARMv5TE core
โ โ โโโ arm7/ โ ARMv4T core
โ โ โโโ bios/ โ HLE SWI handlers
โ โโโ bus/ โ ARM9 + ARM7 bus page tables
โ โโโ memory/vram/ โ 9-bank VRAM controller
โ โโโ ppu/ โ 2D engines (main + sub)
โ โโโ gpu3d/ โ 3D geometry + rasterizer
โ โโโ spu/ โ 16-channel sound processor
โ โโโ dma/ timer/ ipc/ interrupt/ โ Supporting hardware
โ โโโ cartridge/ โ Slot-1 bus + KEY1 + saves
โ โโโ frontend/ โ SDL2 โ only place SDL is allowed
โ โโโ xray/ โ In-emulator debug overlay
โโโ ๐งช tests/ โ Unit tests (link ds_core only)
The full architecture spec lives at docs/specs/2026-04-12-nds-emulator-design.md โ 16 sections covering every subsystem.
The references that made this project possible:
- ๐ GBATEK โ the canonical DS hardware reference
- ๐ GBATEK Markdown fork โ same content, prettier
- ๐ Copetti's NDS Architecture write-up โ accessible deep dive
- ๐ฆ melonDS โ reference accurate DS emulator
- ๐ DeSmuME โ older but still invaluable
- ๐ awesome-nds-dev โ curated resource list
- ๐ KEY1/KEY2 cartridge encryption docs
This project does not distribute any Nintendo intellectual property:
- โ No ROMs
- โ No BIOS files
- โ No firmware dumps
- โ No proprietary game data
Direct boot is implemented from publicly documented hardware behavior. Users supply their own legally obtained ROMs of games they own. Pokรฉmon, Nintendo DS, and all related trademarks belong to Nintendo / The Pokรฉmon Company / Game Freak.
Built standing on the shoulders of giants: the GBATEK authors who reverse-engineered the DS hardware, the melonDS and DeSmuME teams for showing what's possible, and the gbadev / nds-dev homebrew communities for keeping the knowledge alive.