A static recompiler for original NES ROMs that translates 6502 assembly directly into portable, modern C code. Run your favorite classic games without a traditional emulator—just compile and play.
Pre-built binaries are available on the Releases page:
| Platform | Architecture | File |
|---|---|---|
| Windows | x64 | nes-recompiled-windows-x64.zip |
| Linux | x64 | nes-recompiled-linux-x64.tar.gz |
| macOS | x64 (Intel) | nes-recompiled-macos-x64.tar.gz |
| macOS | ARM64 (Apple Silicon) | nes-recompiled-macos-arm64.tar.gz |
Note: The recompiler (
nesrecomp) is what you download. After recompiling a ROM, you'll still need CMake, Ninja, SDL2, and a C compiler to build the generated project.
- Native Performance: Generated C code compiles to native machine code
- Accurate Runtime:
- Cycle-accurate 6502 instruction emulation
- Precise PPU (graphics) emulation with scanline rendering
- Audio subsystem (APU) with all 5 channels (2 pulse, triangle, noise, DMC)
- Mapper Support: MMC1 (SMB, Zelda), MMC3 (SMB3, Megaman), and more
- SDL2 Platform Layer: Ready-to-run with keyboard/controller input and window display
- Debugging Tools: Trace logging, instruction limits, and screenshot capture
- Cross-Platform: Works on macOS, Linux, and Windows (via CMake + Ninja)
- CMake 3.15+
- Ninja build system
- SDL2 development libraries
- A C/C++ compiler (Clang, GCC, or MSVC)
# Clone and enter the repository
git clone https://github.com/arcanite24/nes-recompiled.git
cd nes-recompiled
# Configure and build
cmake -G Ninja -B build .
ninja -C build# Generate C code from a ROM
./build/bin/nesrecomp path/to/game.nes -o output/game
# Build the generated project
cmake -G Ninja -S output/game -B output/game/build
ninja -C output/game/build
# Run!
./output/game/build/gamemacOS/Linux:
# Download and run the setup script
git clone https://github.com/arcanite24/nes-recompiled.git
cd nes-recompiled
chmod +x tools/setup.sh
./tools/setup.shWindows:
# Download and run the setup script (run as Administrator)
git clone https://github.com/arcanite24/nes-recompiled.git
cd nes-recompiled
powershell -ExecutionPolicy Bypass -File tools/setup.ps1Prerequisites:
- CMake 3.15+
- Ninja build system
- SDL2 development libraries
- A C/C++ compiler (Clang, GCC, or MSVC)
Building:
# Clone and enter the repository
git clone https://github.com/arcanite24/nes-recompiled.git
cd nes-recompiled
# Configure and build
cmake -G Ninja -B build .
ninja -C build./build/bin/nesrecomp <rom.nes> -o <output_dir>The recompiler will:
- Load and parse the iNES ROM header
- Analyze control flow starting from reset vector ($FFFC)
- Decode 6502 instructions and track mapper state
- Generate C source files with the runtime library
| Flag | Description |
|---|---|
--trace |
Print every instruction during analysis |
--limit <N> |
Stop analysis after N instructions |
--add-entry-point <addr> |
Manually specified entry point (e.g. C000) |
--no-scan |
Disable aggressive code scanning (enabled by default) |
--verbose |
Show detailed analysis statistics |
--use-trace <file> |
Use runtime trace to seed entry points |
Example:
# Debug a problematic ROM
./build/bin/nesrecomp game.nes -o output/game --trace --limit 5000Trace-Guided Recompilation (Recommended): Complex games often use computed jumps that static analysis cannot resolve. You can use execution traces to "seed" the analyzer with every instruction physically executed during a real emulated session.
-
Generate a trace: Run any recompiled version of the game with tracing enabled.
# Using recompiled game ./output/game/build/game --trace-entries game.trace --limit 1000000 -
Recompile with grounding: Feed the trace back into the recompiler.
./build/bin/nesrecomp roms/game.nes -o output/game --use-trace game.trace
For a detailed walkthrough, see GROUND_TRUTH_WORKFLOW.md.
Manual Entry Points:
If you see [NES] Interpreter messages in the logs at specific addresses, you can manually force the recompiler to analyze them:
./build/bin/nesrecomp roms/game.nes -o out_dir --add-entry-point C000Aggressive Scanning: The recompiler automatically scans memory for code that isn't directly reachable (e.g. unreferenced functions). This improves compatibility but may occasionally misidentify data as code. To disable it:
./build/bin/nesrecomp roms/game.nes -o out_dir --no-scanWhen running a recompiled game:
| Option | Description |
|---|---|
--input <script> |
Automate input from a script file |
--dump-frames <list> |
Dump specific frames as screenshots |
--screenshot-prefix <path> |
Set screenshot output path |
--trace-entries <file> |
Log all executed (PC) points to file |
| NES | Keyboard (Primary) | Keyboard (Alt) |
|---|---|---|
| D-Pad Up | ↑ Arrow | W |
| D-Pad Down | ↓ Arrow | S |
| D-Pad Left | ← Arrow | A |
| D-Pad Right | → Arrow | D |
| A Button | Z | J |
| B Button | X | K |
| Start | Enter | - |
| Select | Right Shift | Backspace |
| Quit | Escape | - |
The recompiler performs static control flow analysis:
- Discovers all reachable code starting from reset vector ($FFFC)
- Follows JSR calls and JMP jumps
- Detects computed jumps (e.g.,
JMP (addr)) and resolves jump tables - Separates code from data using heuristics
Instructions are translated to C:
// Original: LDA #$42
ctx->a = 0x42;
nes_set_flags_zn(ctx, ctx->a);
// Original: ADC #$01
ctx->a = nes_adc(ctx, ctx->a, 0x01);
// Original: JSR $C000
nes_call(ctx, 0xC000);
return;The generated code links against libnesrt, which provides:
- Memory-mapped I/O (
nes_read8,nes_write8) - CPU flag manipulation (N, V, Z, C, etc.)
- PPU scanline rendering (256x240)
- Audio sample generation (5 channels)
- Timer and interrupt handling (NMI, IRQ)
See COMPATIBILITY.md for the full test report.
| Status | Count | Percentage |
|---|---|---|
| ✅ SUCCESS | TBD | TBD |
| ❌ RECOMPILE_FAIL | TBD | TBD |
| TBD | TBD | |
| 🔧 EXCEPTION | TBD | TBD |
Manually confirmed working examples:
- TBD - Testing in progress
- Migration plan from GameBoy recompiler
- 6502 CPU decoder and analyzer
- PPU (graphics) emulation
- APU (audio) emulation
- Mapper support (MMC1, MMC3, etc.)
- First playable ROM
- Tools for better graphical debugging
- Android builds
- Famicom Disk System support
- Improve quality of generated code
- Reduce size of output binaries
The tools/ directory contains utilities for analysis and verification:
Automate instruction discovery using a headless emulator.
python3 tools/capture_ground_truth.py roms/game.nes --frames 3600 --random -o game.traceAudit your recompiled code against a dynamic trace to see exactly what instructions are missing.
python3 tools/compare_ground_truth.py --trace game.trace output/gameThe recompiler uses a multi-stage pipeline:
ROM → Decoder → IR Builder → Analyzer → C Emitter → Output
↓ ↓ ↓
Opcodes Intermediate Control Flow
Representation Graph
Key components:
- Decoder (
decoder_6502.h): Parses raw bytes into 6502 opcodes - IR Builder (
ir_builder.h): Converts opcodes to intermediate representation - Analyzer (
analyzer.h): Builds control flow graph from reset vector - C Emitter (
c_emitter.h): Generates C code from IR
This project is licensed under the MIT License.
Note: NES is a trademark of Nintendo. This project does not include any copyrighted ROM data. You must provide your own legally obtained ROM files.
- NesDev Wiki - The definitive NES technical reference
- 6502.org - 6502 CPU documentation
- The NesDev community for extensive documentation and test ROMs
- N64Recomp - The original recompiler that inspired this project
- gb-recompiled - The GameBoy recompiler this project was migrated from
