A general-purpose static recompiler for the Super Nintendo Entertainment System (Super Famicom). SNESRecomp translates 65816 machine code into C, compiles it into a native executable, and links it against a shared SNES hardware runtime.
The game CPU runs as native code instead of inside a full-system emulator. The runtime models the hardware around it—including the PPU, APU, DSP, DMA, cartridge mapping, and supported enhancement chips—and provides a safe interpreter tier for code that cannot yet be resolved statically.
Projects built on SNESRecomp already support true widescreen views, Adaptive View, versioned mod packages, MSU-1 audio, cross-platform builds, launchers, and save states. The framework is game-agnostic: each title supplies its own analysis configuration and integration code while improvements to the CPU and hardware model benefit every project.
Super Mario World |
The Legend of Zelda: A Link to the Past |
Mega Man X |
Each public game is maintained in its own repository, pins a known framework revision, and ships its own ROM-free release. These projects are still alpha; see each repository for its supported ROM revision, controls, build instructions, and current validation status.
| Game | Repository | Latest build | Current status |
|---|---|---|---|
| Super Mario World | SuperMarioWorldRecomp | releases | Believed playable end to end; 4:3, fixed 16:9, Adaptive View, and MSU-1 audio. |
| The Legend of Zelda: A Link to the Past | ZeldaAlttPSNESRecomp | releases | Playable through the early dungeon; Adaptive View and MSU-1 audio. |
| Mega Man X | MegaManXSNESRecomp | releases | Fully playable; experimental true-widescreen mod; Windows, macOS, and Linux builds. |
SNESRecomp is not a drop-in emulator: support in this table means that the analyzer and shared runner understand the cartridge mapping or enhancement chip. Each game still needs its own analysis configuration, integration, and validation before it becomes a playable native port. The companion game repositories are authoritative for supported ROM regions and revisions.
| Cartridge mapping or chip | Status | Notes |
|---|---|---|
| Standard LoROM and FastROM | Supported | ROM and battery-backed SRAM mapping are implemented. Headerless .sfc and copier-headered .smc images are accepted. |
| Standard HiROM and FastROM | Supported | ROM and battery-backed SRAM mapping are implemented and detected automatically. |
| Capcom Cx4 / CX4 | Supported | Instruction-level support for the chip used by Mega Man X2 and Mega Man X3. Its internal data ROM is synthesized and does not require a separate firmware file. See runner/src/snes/CX4_NOTES.md. |
| Nintendo Super FX / GSU | Supported | Instruction-level core and cartridge mapping are implemented. Star Fox is the current development validation target; other Super FX games have not yet been qualified individually. |
| Nintendo DSP-1 / DSP-1B | Supported | Supported game: Super Mario Kart. Its canonical SHVC-1K1X windows, an instruction-level NEC uPD7725 core, and a firmware-free command model for the firmware-verified command set are implemented. Supplied dsp1b.rom, dsp1.rom, and word-reversed dsp1.bin images always select LLE; when none is found, HLE is used and stops loudly on an unverified command. Both backends pass unit and differential command gates, a deterministic one-player race matches Snes9x WRAM/DSP behavior and framebuffer output within renderer rounding, and the canonical 36,000-frame LLE/HLE attract qualifications pass. A title-gated Fast HiROM mapping supports converted development derivatives without broadening detection to other NEC DSP boards. |
| Nintendo SA-1 | Supported | Supported game: Super Mario RPG. Instruction-level 65816 execution, ROM-bus arbitration, Super MMC banking, IRAM/BW-RAM and bitmap windows, CPU/SA-1 interrupt communication, timers, DMA/character conversion, arithmetic, and variable-bit reading are implemented. The focused hardware suite and the canonical US 18,000-frame attract-loop qualification pass without runtime, logic-activity, video-activity, or audio-activity failures. See runner/src/snes/SA1_NOTES.md. |
| MSU-1 | Supported, opt-in | The extension's registers, data channel, and PCM audio are implemented, but a game must integrate an MSU-1 driver and pack selection. See docs/MSU1.md. |
| ExLoROM, ExHiROM, and other custom mappings | Not supported yet | The current mapper layer handles standard LoROM, HiROM, SA-1, Cx4, and Super FX layouts only. |
| Nintendo DSP-2, DSP-3, and DSP-4 | Not supported yet | These cartridge DSP firmwares use the same NEC family interface but need per-title board/firmware validation before being advertised. |
| S-DD1 | Not supported yet | No decompression chip or cartridge mapping support is present. |
| SPC7110 and SPC7110 RTC | Not supported yet | No data-decompression, mapping, or RTC model is present. |
| OBC-1, ST010, ST011, ST018, and S-RTC | Not supported yet | Their register windows and coprocessor behavior are not modeled. |
| BS-X, Sufami Turbo, and Super Game Boy cartridge adapters | Not supported yet | Their special cartridge or subsystem behavior is outside the current mapper model. |
Chips and mapping schemes not listed as supported should be assumed unsupported. Some unsupported cartridges may get far enough through header detection to load as a plain LoROM or HiROM image, but their unmodeled register windows will still prevent correct execution.
SNESRecomp turns a ROM into a recompilation project:
- The analyzer discovers code, follows control flow, tracks the 65816 M/X width state, and resolves direct and indirect dispatch where it can.
- The emitter translates discovered 65816 functions into portable C.
- A normal C compiler builds that output together with the shared runner and game-specific integration code.
- At runtime, unresolved or dynamically reached code can fall through to the interpreter tier instead of becoming a correctness hole.
This is a hybrid low-level design. The original game logic remains the source of truth: recompiled CPU code drives modeled SNES hardware at the same register boundaries a cartridge uses. Game projects can add narrowly scoped high-level helpers or presentation enhancements, but the low-level path stays available for validation.
SNESRecomp is a framework, not a collection of ROMs. It does not include copyrighted game data, and a generated project is only the starting point for a playable port. A new title still needs accurate function boundaries, indirect-dispatch configuration, validation, and a host application.
Display aspect and view width are separate controls. The shared desktop policy
supports 4:3 (CRT) with the SNES's 7:6 horizontal pixel correction,
8:7 (Square pixels) for a 1:1 pixel aspect, and a literal 1:1 (Square
frame) presentation. Games opt into the launcher control and persist it as
DisplayAspect; 4:3 remains the default.
These are genuinely wider views—not stretched 4:3 output. SNES 2D engines normally stream backgrounds, spawn objects, and cull sprites for a 256-pixel viewport. A correct widescreen integration widens those systems together while leaving game simulation and authentic 4:3 behavior unchanged.
A fixed widescreen view adds one third more horizontal logical pixels. Because the chosen pixel shape stays unchanged, that expansion presents as 16:9 from 4:3, 32:21 from 8:7, or 4:3 from a square frame.
| Super Mario World — fixed 16:9. The native 224-pixel logical height is retained while the level view expands horizontally. |
| Mega Man X — true widescreen. Background streaming, object windows, HUD placement, and sprite rendering are widened together. |
Adaptive View grows or clamps the logical viewport according to the available scene and window width. At a map boundary it can preserve an authentic edge instead of wrapping or inventing scenery.
| Authentic 4:3 | Adaptive View |
The transferable 2D-engine checklist lives in
docs/WIDESCREEN_PATTERNS.md.
SNESRecomp supports opt-in, versioned .snesmod packages. A package is an
installation, update, provenance, and trust boundary; it may expose multiple
independently toggleable features and typed options in the launcher.
Packages contain data only—never DLLs or arbitrary native code. Native behavior is owned by the game, statically linked into its executable, and registered under stable plugin IDs. Before launch, the runtime verifies the selected stock ROM, resolves enabled features, rejects missing or conflicting plugins, and then activates the trusted game-side implementations.
The system is off by default at the framework level, so a game must explicitly
enable and integrate it. Mega Man X uses this path for its built-in
true-widescreen feature. See
docs/MOD_PACKAGES.md for the package format and trust
model.
The shared runner implements the MSU-1 registers, data channel, and 44.1 kHz stereo PCM streaming mixed over the S-DSP output. SMW and ALttP integrate this with their launchers: select a compatible music-pack folder and keep using a verified stock ROM.
Their MSU-1 drivers are compiled into the executable from a temporary, locally-patched analysis image during regeneration. The user's stock ROM is not modified. With no pack selected, the games retain their authentic SPC soundtracks.
The chip is inert by default for projects that do not integrate it. Developers
can also point SNESRECOMP_MSU1 at a pack directory or base prefix directly.
See docs/MSU1.md for the register model, pack resolution, and
game-integration details.
- Download
snesrecomp-cli-windows-x86_64.zipfrom Releases. - Extract the entire archive to one folder.
- Open PowerShell there and run:
.\snesrecomp.exe build `
--rom "C:\Games\My Game\game.sfc" `
--output "C:\Projects\MyGameRecomp"Both .sfc and .smc images are accepted. Standard LoROM and HiROM mapping
are detected automatically.
The generated folder contains discovered C source, a starter bank configuration, CMake build files, and the runner sources needed for further integration. The released CLI is self-contained; generating a project does not require Python, Rust, or a source checkout.
Install CMake, Ninja, and a C compiler, then run:
powershell -ExecutionPolicy Bypass -File "C:\Projects\MyGameRecomp\build.ps1"The generated project also includes build.sh for macOS and Linux. This step
builds a static library containing the generated code; turning an arbitrary
game into a playable native port still requires the game-specific work
described above.
Use only a ROM image you obtained legally. SNESRecomp neither includes ROM data nor copies the input ROM into the generated project. Generated C is derived from that ROM, so do not redistribute it without permission.
You need Git, Python 3.9 or newer, Rust 1.85 or newer, and PyInstaller:
git clone https://github.com/mstan/snesrecomp.git
cd snesrecomp
python -m pip install pyinstaller==6.21.0
python tools/build_cli.py releaseThe ready-to-use ZIP is written to dist/.
SDL3 is the default desktop backend for CMake game hosts. SDL2 remains a supported compatibility fallback, but it must be selected explicitly. The choice applies to both the game window and the shared recomp-ui launcher, so a build never mixes SDL major versions.
Install the development package for the backend you want:
- Windows: use an SDL development package for your compiler and architecture.
For the official MinGW archives, point CMake at the contained
x86_64-w64-mingw32directory. - macOS: install
sdl3for the default build orsdl2for the fallback. - Linux: install your distribution's SDL3 development package, or its SDL2 development package for the fallback.
Keep separate build directories for the two backends. This avoids stale CMake package paths and also makes side-by-side testing straightforward.
From a game repository that consumes SNESRecomp:
$engine = 'C:\src\snesrecomp'
$ui = 'C:\src\recomp-ui'
$sdl3 = 'C:\deps\SDL3\x86_64-w64-mingw32'
cmake -S . -B build-sdl3 -G Ninja `
-DCMAKE_BUILD_TYPE=Release `
-DSNESRECOMP_ROOT="$engine" `
-DRECOMP_UI_ROOT="$ui" `
-DCMAKE_PREFIX_PATH="$sdl3"
cmake --build build-sdl3 --parallel-DSNESRECOMP_SDL_BACKEND=SDL3 is accepted but optional because SDL3 is the
default.
Configure a different build directory and select SDL2 explicitly:
$engine = 'C:\src\snesrecomp'
$ui = 'C:\src\recomp-ui'
$sdl2 = 'C:\deps\SDL2\x86_64-w64-mingw32'
cmake -S . -B build-sdl2 -G Ninja `
-DCMAKE_BUILD_TYPE=Release `
-DSNESRECOMP_ROOT="$engine" `
-DRECOMP_UI_ROOT="$ui" `
-DSNESRECOMP_SDL_BACKEND=SDL2 `
-DCMAKE_PREFIX_PATH="$sdl2"
cmake --build build-sdl2 --parallelIf SDL is installed in a standard system prefix, omit
-DCMAKE_PREFIX_PATH. On Windows, the build helper copies the selected
SDL3.dll or SDL2.dll beside each executable.
During configuration, verify that CMake prints matching lines:
<game-target>: SDL3 desktop backend
recomp-ui: SDL3 platform backend
The SDL2 build reports SDL2 compatibility backend instead. After a clean
game exit, last_run_report.json records the compiled and loaded SDL versions.
A game CMake file should use the shared helper instead of calling
find_package(SDL2) or find_package(SDL3) itself:
set(SNESRECOMP_ROOT "${CMAKE_SOURCE_DIR}/snesrecomp" CACHE PATH
"Path to SNESRecomp")
include(${SNESRECOMP_ROOT}/runner/runner.cmake)
set(RECOMP_UI_ROOT "${CMAKE_SOURCE_DIR}/recomp-ui" CACHE PATH
"Path to recomp-ui")
include(${RECOMP_UI_ROOT}/recomp_ui.cmake)
add_executable(my_game ${SNESRECOMP_RUNNER_SOURCES} ${GAME_SOURCES})
target_include_directories(my_game PRIVATE
${SNESRECOMP_RUNNER_INCLUDE_DIRS})
snesrecomp_target_sdl(my_game)
recomp_target_launcher_ui(my_game)snesrecomp_target_sdl() owns package discovery, compile definitions, linking,
and Windows runtime-DLL staging. See
docs/SDL_BACKENDS.md for the benchmark mode and
maintainer notes. Some legacy Visual Studio game projects deliberately remain
on the explicitly defined SDL2 fallback; the CMake release path is the SDL3
default.
| Path | Purpose |
|---|---|
recompiler/ |
Python reference analyzer and authoritative C emitter. |
recompiler-rs/ |
Production native whole-program analyzer; preserves the Python manifest/emitter boundary while accelerating analysis. |
runner/ |
Shared CPU state, memory map, hardware model, interpreter tier, launcher helpers, diagnostics, and enhancement hooks. |
tests/ |
Analyzer, decoder, interpreter, runtime-dispatch, launcher, and PPU tests. |
fuzz/ |
Differential fuzzing over synthetic 65816 programs. |
tools/ |
Generation, validation, trace-diff, and release tooling, including the snesref reference frontend. |
Useful technical references:
docs/MULTI_TIER.md— static and interpreted execution tiers.docs/LLE_FIRST_ANALYSIS.md— analysis policy and the low-level correctness floor.docs/TRIPWIRES.md— runtime checks for M/X state, dispatch, and control-flow failures.docs/LAUNCHER_DESIGN.md— shared launcher integration.docs/HOST_OVERLAY_EXTRACTION.md— extracting PPU layers for host-side composition.
SNESRecomp has two complementary reference paths. Both are developer tools and are excluded from release packages and normal game builds.
| Tool | Reference boundary | Best use |
|---|---|---|
tools/snesref/ |
Loads an independently developed bsnes, Snes9x, or other libretro core at runtime. | Validate framebuffer output, audio, inputs, frame timing, and memory behavior without sharing SNESRecomp's device implementation. |
cosim/ref_driver.c + tools/snes_cosim.py |
Runs interp816 in a separate process against the same runner devices and exposes the same SNES_COSIM_PORT TCP protocol as a recompiled co-sim target. |
Find the first CPU/runtime divergence with full-state and per-subsystem hashes. |
Use snesref when the symptom could be in a shared PPU, APU, cartridge, or
coprocessor model. Use the TCP co-simulation path to localize a difference
between recompiled 65816 execution and interpreted execution. A clean internal
co-sim is not proof that rendering or audio matches hardware because both sides
intentionally share those device implementations.
Build the small SDL2 frontend, then provide an emulator core DLL and a legally obtained ROM at runtime:
cd tools\snesref
.\build.bat
$env:SNESREF_FAST = "1"
$env:SNESREF_FRAMES = "1800"
$env:SNESREF_TRACE_FILE = "wram.jsonl"
$env:SNESREF_WAV = "reference.wav"
.\snesref.exe C:\cores\bsnes_libretro.dll C:\roms\game.sfcFor reproducible gameplay, set SNESREF_INPUT_FILE to a text file containing
start-frame:duration:hex-mask, one event per line. The mask layout is
B,Y,Select,Start,Up,Down,Left,Right,A,X,L,R from bit 0 through bit 11. Frame
dumps are controlled by SNESREF_FRAME_DUMP_DIR and the optional
SNESREF_FRAME_DUMP_FROM, _TO, and _STEP variables. See
tools/snesref/README.md for the complete capture
interface.
The frontend source and MIT-licensed libretro.h are tracked. Emulator cores,
SDL binaries, ROMs, firmware, captures, and snesref.exe are not tracked or
included in releases. In particular, bsnes is GPLv3 and Snes9x has a
non-commercial license; developers supply either one separately.
The reference-only target does not need a game checkout:
cmake -S cosim -B build\cosim-ref -G Ninja `
-DSNES_COSIM_REF_ONLY=ON `
-DCMAKE_BUILD_TYPE=RelWithDebInfo `
-DCMAKE_C_COMPILER=C:/msys64/mingw64/bin/gcc.exe `
-DCMAKE_MAKE_PROGRAM=C:/msys64/mingw64/bin/ninja.exe
cmake --build build\cosim-ref --target smw_cosim_ref
.\build\cosim-ref\smw_cosim_ref.exe C:\roms\game.sfc `
--frames 1800 --final-frame-dump reference.rawDespite its historical target name, smw_cosim_ref is the game-neutral
interpreter side. For lockstep comparison, build the game-specific A-side
co-sim target and let the coordinator launch both processes:
python tools\snes_cosim.py `
--a-cmd "build/cosim/smw_cosim.exe C:/roms/smw.sfc" `
--b-cmd "build/cosim/smw_cosim_ref.exe C:/roms/smw.sfc" `
--stride 1 --max 3600The coordinator injects separate SNES_COSIM_PORT values, advances both
servers at deterministic checkpoints, and stops at the first full-state
divergence. Run the A-vs-A, B-vs-B, fault-injection, and hash-audit gates in
SNES_COSIM.md before trusting an A-vs-B result.
SNESRecomp is alpha software. Multiple games run through the same shared framework, but APIs, generated-code conventions, and internal integration points can still change. Per-game maturity varies; the Games table and each project's release notes are the authoritative status sources.
The current direction is to move recurring fixes into the framework, retain an exact low-level fallback for uncertain code, and keep authentic output as the regression baseline for every optional enhancement.
Bug reports and framework improvements are welcome. For a game-specific issue, open it in that game's repository and include the ROM revision, platform, configuration, reproduction steps, and a screenshot or save state when useful.
When bringing up a new game, keep game-specific addresses and behavior in its companion repository. Reusable CPU, mapper, coprocessor, diagnostics, launcher, and presentation mechanisms belong here.
SNESRecomp did not start from scratch. Its runtime and tooling stand on prior reverse-engineering and emulation work:
- snesrev (
snesrev/zelda3,snesrev/smw) — the C runner and surrounding ecosystem were heavily based on the snesrev reverse-engineered ports. The model of recompiling or porting CPU code to C, modeling the surrounding silicon, and validating against a reference emulator comes from that work. Runtime utilities, ROM verification, function-boundary conventions, and the default input layout were also adapted from it. - The C SNES hardware core under
runner/src/snes/derives from LakeSnes by elzo-d, as vendored by snesrev, with individual algorithms credited inline to snes9x. - IsoFrieze/SMWDisX — the Super Mario World disassembly used as the symbol and RAM-map basis and as a conformance reference. SMWDisX in turn credits mikeyk's original 2013 disassembly and loveemu's SPC700 work.
SNESRecomp's original code is licensed under the
PolyForm Noncommercial License 1.0.0. Third-party components retain
their own licenses as documented in Acknowledgements and
THIRD_PARTY_ATTRIBUTION.md. The snesref tool
loads a separately supplied libretro emulator core at runtime; no emulator core
source or binary is vendored or released by this repository.
R.A.I.D. — Retro AI Development · a Discord for AI-assisted retro reverse-engineering, decomp & recomp