Skip to the page
It's Decomplicated
Sega, 1988

Sega Genesis / Mega Drive decomp guide

A shared recompiler and strong emulators make Path A fast. Path B means rewriting assembly as C.

What this system is like

The Genesis runs on a Motorola 68000 chip. A Z80 chip runs the sound. Games come on cartridges of 4 MB or less. Most games were written by hand in 68000 assembly, not in C.

Path A is well worn. Ten of the fourteen reviewed projects began with static recompilation. A recompiler turns the game's machine code into C. The free segagenesisrecomp framework does this and ships a runtime for the graphics, sound and input. Five other projects wrote their own recompiler in Python or Node.js.

Path B is harder but proven. X-Men 2 PC rewrote all 3,164 functions as C. It found the original compiler, Apple MPW C 3.3, and matched 3,047 functions byte for byte. Two other projects rewrote whole games in C and checked each routine against a hooked emulator.

Most Path A work runs on Windows with Visual Studio. Some Path B tools are Linux first, so plan on WSL for those. Expect weeks for Path A and months for Path B.

Most games were assembly

Most Genesis games were written in 68000 assembly. So there is usually no C compiler to match. For those games, a full decomp is C that you prove with differential tests. A differential test runs the same input through the old code and the new code and compares them.

Some games were C, and that can be proven

X-Men 2 was built with Apple MPW C 3.3. Six MPW library modules sat in the ROM byte for byte. Then 24 test functions compiled to the exact bytes. That proved the compiler. Later 3,047 C functions matched exactly. Check your game for library code before you choose a route.

A shared recompiler exists

segagenesisrecomp turns 68000 code into C. It comes with a runtime for the VDP, the sound chips and the Z80. It also has an interpreter fallback for code it missed. Five reviewed projects build on it, counting X-Men 2's fork.

Great disassemblies and emulators exist

Sonic Retro keeps byte-exact disassemblies of Sonic 1, 2 and 3. Genesis Plus GX and BizHawk are accurate emulators. Their recordings make a strong check. Eight of the fourteen projects compared their build with an emulator or an interpreter, frame by frame.

The 68000 has tricky habits

Games pop return addresses, jump into tables, and push an address before rts. Word values sign-extend in odd places. A plain C call model breaks on these. PSIV Recomp, X-Men 2 and Tiny Toon each hit at least one.

Timing hides in the frame loop

Many games poll the vblank bit and run logic across frame edges. Some read the HV counter for random numbers. Recompiled code never lags, so slow scenes can run too fast. You must decide how to model time.

Two paths

Path A

Get it running on PC

You feed your own ROM and a game.toml file to GenesisRecomp. It writes C, one function per discovered routine. A runtime stands in for the console. You boot with the interpreter fallback on, then close each code gap. You check every change against an emulator or the interpreter, frame by frame.

Go to the steps

Path B

Full decomp for porting

You first make a disassembly that rebuilds the ROM byte for byte. Then you rewrite each routine in C. If the game used C, you match it with the original compiler. If it was assembly, you prove each C routine with recorded calls from a hooked emulator. Last, you remove the 68000 and port the C.

Go to the steps

Path A: Get it running on PC

You feed your own ROM and a game.toml file to GenesisRecomp. It writes C, one function per discovered routine. A runtime stands in for the console. You boot with the interpreter fallback on, then close each code gap. You check every change against an emulator or the interpreter, frame by frame.

Method: static recompilation

Pick this first for any Genesis game. Most reviewed projects reached a playable build in days to weeks. The function list and the emulator check help Path B later.

  1. Pick the game and write the scope

    A different revision is a different program. So you pin the exact ROM by hash. Then you look for prior work. A disassembly or an existing recomp can save weeks.

    • In WSL, hash your own dump: sha1sum <ABSOLUTE_PATH_TO_ROM> and sha256sum <ABSOLUTE_PATH_TO_ROM>.
    • In WSL, read the header with Python: python3 -c "d=open('<ABSOLUTE_PATH_TO_ROM>','rb').read();print(d[0x100:0x110],d[0x150:0x180],d[0x1F0:0x1F3],hex(int.from_bytes(d[0x18E:0x190],'big')))". It prints the system name, overseas title, region and checksum.
    • Check that the system field starts with SEGA. A .smd dump is interleaved, so its hash will not match the projects' lists. Use a raw .bin dump. PSIV Recomp and Mickey & Donald convert .smd files; Road Rash refuses them.
    • Search the segagenesisrecomp game list, RecompLauncher and GitHub for your game. Search Sonic Retro and GitHub for a disassembly.
    • Write SCOPE.md: game, region, revision, SHA-1, size, goal, technique, first milestone and its check, out-of-scope list.
    Prompt for Claude
    GOAL: Draft SCOPE.md for a Genesis static recomp of <GAME> (<REGION> <REVISION>).
    READ FIRST: bytes $100-$1FF of `<ABSOLUTE_PATH_TO_ROM>` (read only), https://plutiedev.com/rom-header, https://github.com/mstan/segagenesisrecomp#games
    FENCE: Create only `SCOPE.md` and `notes/prior-work.md`. Do not copy or move the ROM. Do not write code.
    CHECK: Run `sha1sum <ABSOLUTE_PATH_TO_ROM>` and paste it. Done means SCOPE.md has the game, region, revision, SHA-1, size in bytes, the header title and checksum, goal, technique (segagenesisrecomp or own recompiler), first milestone and its check, and an out-of-scope list.
    BUDGET: Stop after 30 minutes of searching for prior work.
    IF BLOCKED: Stop. Do not guess a revision or a hash. Tell me what is missing.
    OTHER PROBLEMS: Write them in `notes/parking-lot.md`. Do not fix them.
    REPORT: 1) DONE, PARTIAL or BLOCKED. 2) Proof: paste the hash and the header fields. 3) Files changed. 4) Prior work found, with links. 5) New parking-lot items.
    Done whenThe SHA-1 that sha1sum prints for your ROM appears in SCOPE.md, with the size in bytes and prior work with links. Then you approve it.

    Proof this works: PSIV Recomp (refuses any ROM but one SHA-1), Road Rash Trilogy Recomp (games/supported-roms.json), https://plutiedev.com/rom-header

  2. Set up the repo, the recompiler and the one check

    You build the recompiler and lay out a game repo. The framework sits in it as a pinned submodule. The ROM and the generated C stay out of git. You also write the one check command now, even if it does little yet.

    • Install Visual Studio 2022 with the Desktop development with C++ workload and its CMake tools. Open Developer PowerShell for VS 2022. Check git --version and cmake --version.
    • Clone the framework: git clone --recurse-submodules https://github.com/mstan/segagenesisrecomp.git.
    • Build it: cmake -S recompiler -B build/recompiler -G "Visual Studio 17 2022" -A x64 then cmake --build build/recompiler --config Release --parallel. Or download GenesisRecomp-windows-x64-v0.1.2.zip from the v0.1.2 release.
    • Make your game repo. Add the framework with git submodule add https://github.com/mstan/segagenesisrecomp. Copy the build layout from a small current game repo, such as SonicTheHedgehogRecomp.
    • Add *.bin, *.md, *.gen, *.smd, generated/ and build/ to .gitignore before the ROM is anywhere near the repo.
    • Write tools/check.ps1. It builds, runs a headless boot for a set number of frames, and prints PASS or FAIL.
    Prompt for Claude
    GOAL: Build GenesisRecomp.exe and set up a game repo with one check command for <GAME>.
    READ FIRST: https://github.com/mstan/segagenesisrecomp/blob/master/docs/GETTING_STARTED.md, the README of https://github.com/mstan/SonicTheHedgehogRecomp, SCOPE.md
    FENCE: Edit only files in this repo. Add segagenesisrecomp as a submodule; never edit inside it. Do not add features.
    CHECK: Run `pwsh tools/check.ps1 <ABSOLUTE_PATH_TO_ROM>`. Done means the recompiler builds, `git status` shows no ROM and no generated C, and the check prints PASS or a clear FAIL line.
    BUDGET: Stop after 3 failed builds of the same error.
    IF BLOCKED: Stop. Paste the exact error. Do not switch compilers or install new tools without asking.
    OTHER PROBLEMS: Write them in `notes/parking-lot.md`. Do not fix them.
    REPORT: 1) DONE, PARTIAL or BLOCKED. 2) Proof: paste the build tail and the check output. 3) Files changed. 4) What is left. 5) New parking-lot items.
    Done whenGenesisRecomp.exe exists, git status shows no ROM, and tools/check.ps1 runs and prints PASS or FAIL.

    Proof this works: Sonic the Hedgehog Recomp, Sonic the Hedgehog 2 Recomp, https://github.com/mstan/segagenesisrecomp/blob/master/docs/GETTING_STARTED.md

  3. Map the ROM into a function list

    The recompiler needs to know where each routine starts. A good disassembly gives you labels and jump tables for free. Without one, you trace code from the reset vector and use Ghidra to check. Wrong starts break the build later.

    • If a disassembly exists, pin it as a submodule. The Sonic repos turn its listing into game.toml discovery files with gen_disasm_labels.py and gen_disasm_jumptables.py.
    • If none exists, write a tracer with Capstone. It follows calls and branches from the vectors. Strike HD and Tiny Toon did this.
    • Open the ROM in Ghidra with the 68000 language. Check each doubtful start by reading its code.
    • Write notes/functions.csv with address, size, name, source of proof and a status column.
    • Keep a blacklist.txt of addresses that must never become entries, each with a reason.
    Prompt for Claude
    GOAL: Build the first function list for <GAME> and feed it to game.toml.
    READ FIRST: SCOPE.md, `<DISASM_LISTING_PATH>` if one exists, https://github.com/mstan/segagenesisrecomp/blob/master/docs/DISASSEMBLIES.md
    FENCE: Edit only `game.toml`, `notes/functions.csv`, `blacklist.txt` and scripts under `tools/map/`. Do not edit the disassembly or the framework.
    CHECK: Run `<FRAMEWORK_DIR>\build\recompiler\Release\GenesisRecomp.exe <ABSOLUTE_PATH_TO_ROM> --game game.toml --output-dir generated`. Done means it finishes, every entry in functions.csv names its proof (disassembly label, trace hit or Ghidra listing), and no entry sits inside another function.
    BUDGET: Stop after 2 hours or 3 failed recompiler runs on the same error.
    IF BLOCKED: Stop. Do not guess entry points. List the addresses you could not prove.
    OTHER PROBLEMS: Write them in `notes/parking-lot.md`. Do not fix them.
    REPORT: 1) DONE, PARTIAL or BLOCKED. 2) Proof: entry count, the recompiler's summary lines. 3) Files changed. 4) Unproven addresses. 5) New parking-lot items.
    Done whenGenesisRecomp.exe exits with code 0 on game.toml, and every row of notes/functions.csv has its proof column filled.

    Proof this works: Sonic the Hedgehog 2 Recomp (s2disasm feeds game.toml), Strike Remastered Collection HD (tools/trace.py), Tiny Toon Adventures: ACME All-Stars (analysis/*.csv with an evidence column)

  4. Build a reference you can replay

    You need a check that does not trust your own eyes. Record the real game in an emulator: inputs plus a hash of RAM and video memory each frame. Then your build replays the same inputs and must match. Build this before you chase bugs.

    • Install BizHawk 2.11.1 for Windows. It uses the Genesis Plus GX core for Genesis games.
    • Start it with your script from PowerShell, in the BizHawk folder: ./EmuHawk.exe '--lua=<ABSOLUTE_PATH_TO_SCRIPT>' '<ABSOLUTE_PATH_TO_ROM>'.
    • Write a Lua script that logs pad input and hashes of RAM, VRAM, CRAM and VSRAM at each logical frame. PSIV Recomp did this with tools/bizhawk/record.lua.
    • Count frames when the game waits for vblank, not by wall clock. Lag frames then line up.
    • Add a headless mode to your build: --replay <file>, a frame limit, and a hash log.
    • If the game reads the HV counter for random numbers, log each value read. Feed them back during replay like pad input.
    Prompt for Claude
    GOAL: Record one reference session of <GAME> in BizHawk and replay it headless in our build.
    READ FIRST: SCOPE.md, https://github.com/TASEmulators/BizHawk (Lua docs), notes on how PSIV Recomp records frames in `notes/prior-work.md`
    FENCE: Edit only `tools/ref/`, the headless front end under `src/host/`, and `tests/refs/` (hash logs only, no ROM bytes). Do not change game logic.
    CHECK: Run `<HEADLESS_EXE> --replay tests/refs/<RUN>.input --hash-log out/<RUN>.log` then `python tools/ref/compare.py tests/refs/<RUN>.ref out/<RUN>.log`. Done means the compare script runs and prints the first differing frame, or MATCH.
    BUDGET: Stop after 3 hours or 3 failed tries at frame alignment.
    IF BLOCKED: Stop. Do not loosen what the hash covers to get a match. Tell me what differs.
    OTHER PROBLEMS: Write them in `notes/parking-lot.md`. Do not fix them.
    REPORT: 1) DONE, PARTIAL or BLOCKED. 2) Proof: the compare output. 3) Files changed. 4) What the hash covers and what it skips. 5) New parking-lot items.
    Done whencompare.py runs on a recorded session and prints MATCH or the first differing frame with both hashes.

    Proof this works: PSIV Recomp (BizHawk Lua + psiv_headless --replay), X-Men 2: Clone Wars PC (instrumented Genesis Plus GX, TAS 4473M), Strike Remastered Collection HD (Musashi lockstep)

  5. First light: boot to the first frame

    You generate the C, build it and boot it. Keep the interpreter fallback on for now. It runs code the recompiler missed, so the game can start. The goal is one correct frame, such as the Sega logo.

    • Generate from your game folder: <FRAMEWORK_DIR>\build\recompiler\Release\GenesisRecomp.exe <ABSOLUTE_PATH_TO_ROM> --game game.toml --output-dir generated.
    • Build with CMake and Visual Studio: cmake -S . -B build -G "Visual Studio 17 2022" -A x64 then cmake --build build --config Release.
    • Run headless for 600 frames and save a screenshot and a hash log.
    • Compare the boot frames with your reference recording.
    • Write the result in PROGRESS.md.
    Prompt for Claude
    GOAL: Generate, build and boot <GAME> to its first correct frame with the interpreter fallback on.
    READ FIRST: SCOPE.md, PROGRESS.md, game.toml, the README of https://github.com/mstan/SonicTheHedgehogRecomp
    FENCE: Edit only the game adapter under `game/`, `game.toml` and `CMakeLists.txt`. Never edit `generated/`. Never edit the framework submodule.
    CHECK: Run `pwsh tools/check.ps1 <ABSOLUTE_PATH_TO_ROM>`. Done means the build runs 600 frames headless with no crash, and the first <N> frame hashes match `tests/refs/boot.ref`.
    BUDGET: Stop after 4 failed builds or 2 hours without a new frame.
    IF BLOCKED: Stop. Paste the last log lines and the first differing frame. Do not stub a function to get past a crash.
    OTHER PROBLEMS: Write them in `notes/parking-lot.md`. Do not fix them.
    REPORT: 1) DONE, PARTIAL or BLOCKED. 2) Proof: check output and a screenshot path. 3) Files changed. 4) What is left. 5) New parking-lot items.
    Done whenA headless run reaches the first scene with no crash, and its boot frames match the reference hashes.

    Proof this works: Sonic the Hedgehog Recomp (boot_smoke.py), Beyond Oasis Recomp (Musashi fallback keeps it playable from day one)

  6. Close the code gaps

    A dispatch miss means the game jumped to an address with no generated function. The fallback hides it. So you list every miss after each run and fix it at the source. One unit is one missed address.

    • After every run, read dispatch_misses.toml or the coverage log.
    • For each address, find it in the disassembly or Ghidra. Decide: real function start, interior label, or data.
    • Add real starts through the disassembly pipeline or game.toml. Add interior labels to blacklist.txt with a reason.
    • Regenerate and rebuild. Replay all reference sessions.
    • Repeat until the miss list is empty on every recorded session.
    Prompt for Claude
    GOAL: Resolve dispatch miss <ADDRESS> in <GAME>.
    READ FIRST: `dispatch_misses.toml`, the Ghidra listing at <ADDRESS>, `blacklist.txt`, notes/functions.csv
    FENCE: Edit only `game.toml`, `blacklist.txt`, `notes/functions.csv` and the disassembly pin file. Never edit `generated/`. Never add an address only because the miss log names it.
    CHECK: Regenerate, rebuild and run `pwsh tools/check.ps1 <ABSOLUTE_PATH_TO_ROM> --all-refs`. Done means <ADDRESS> no longer appears as a miss, no new miss appears, and every reference session still matches.
    BUDGET: Stop after 3 failed tries or 45 minutes.
    IF BLOCKED: Stop. Paste the listing and say why the start is unclear.
    OTHER PROBLEMS: Write them in `notes/parking-lot.md`. Do not fix them.
    REPORT: 1) DONE, PARTIAL or BLOCKED. 2) Proof: the listing, the miss count before and after, the check output. 3) Files changed. 4) What is left. 5) New parking-lot items.
    Done whendispatch_misses.toml is empty after every recorded session, and all reference sessions still match.

    Proof this works: Sonic the Hedgehog Recomp (PRINCIPLES #13a and #16), Beyond Oasis Recomp (coverage.bin to config/coverage.txt), Strike Remastered Collection HD (tools/add_dynamic_entries.py)

  7. Fix differences at the first divergence

    A wrong picture often starts with a wrong value many frames earlier. So you never debug the symptom. You find the first frame where your build and the reference differ. Then you narrow it to one instruction or one hardware rule.

    • Replay a session until the compare script reports the first differing frame.
    • Dump RAM, VRAM, CRAM, VSRAM and VDP registers from both sides at that frame. Diff them.
    • Turn on an instruction trace for that frame only. Find the first differing write.
    • Fix the cause in the recompiler config, the runtime or the adapter. Never in generated C.
    • Log each case in notes/divergence-log.md as frame, symptom, cause, fix.
    Prompt for Claude
    GOAL: Find and fix the first divergence in reference session <RUN> of <GAME>.
    READ FIRST: `notes/divergence-log.md`, the compare output for <RUN>, the Ghidra listing of the code that wrote the first wrong value
    FENCE: Edit only the runtime under `src/hw/` or the framework fork branch named in CLAUDE.md, `game.toml`, and `notes/divergence-log.md`. Never edit `generated/` or `tests/refs/`.
    CHECK: Run `pwsh tools/check.ps1 <ABSOLUTE_PATH_TO_ROM> --all-refs`. Done means <RUN> matches further than before, the cause is written as one instruction or one hardware rule, and no other session got worse.
    BUDGET: Stop after 2 fix tries that do not move the first divergence.
    IF BLOCKED: Stop. Report the frame, both values, and your best two causes with proof for each.
    OTHER PROBLEMS: Write them in `notes/parking-lot.md`. Do not fix them.
    REPORT: 1) DONE, PARTIAL or BLOCKED. 2) Proof: first divergent frame before and after. 3) Files changed. 4) The cause in one sentence. 5) New parking-lot items.
    Done whenEvery reference session matches to its end, and each fix has a line in notes/divergence-log.md.

    Proof this works: X-Men 2: Clone Wars PC (divergence log in docs/NOTES.md), Sonic the Hedgehog 2 Recomp (first-divergence diffing), PSIV Recomp (VSRAM and VDP status bugs found this way)

  8. Harden it and ship without the game

    You lock in what works. Old fixes must stay fixed. Then you package the build so nobody gets the game from you. The reviewed projects ship tools that rebuild the game from the player's own ROM.

    • Record golden runs: per-frame hashes of long sessions. Compare after every change.
    • Keep each enhancement off by default. Run every check with all options off.
    • Build from a fresh clone and confirm the generated C and the hashes are the same.
    • Ship an installer that checks the player's ROM by hash, generates the C and compiles it on their PC.
    • Scan the release zip for ROM bytes before you publish it.
    Prompt for Claude
    GOAL: Add a regression set and a ROM-free release package for <GAME>.
    READ FIRST: PROGRESS.md, `tests/refs/`, the release scripts of https://github.com/OriginalSwayze/beyond-oasis-recomp (README only)
    FENCE: Edit only `tests/`, `tools/release/` and `.github/`. Do not change game behaviour. Do not change any expected hash.
    CHECK: Run `pwsh tools/check.ps1 <ABSOLUTE_PATH_TO_ROM> --all-refs --golden` from a fresh clone, then `python tools/release/scan_rom_bytes.py dist/<ZIP> <ABSOLUTE_PATH_TO_ROM>`. Done means all sessions match, the golden runs match, and the scan finds no ROM bytes.
    BUDGET: Stop after 3 hours.
    IF BLOCKED: Stop. Say which check fails and paste its output.
    OTHER PROBLEMS: Write them in `notes/parking-lot.md`. Do not fix them.
    REPORT: 1) DONE, PARTIAL or BLOCKED. 2) Proof: both outputs. 3) Files changed. 4) What is still untested by a person. 5) New parking-lot items.
    Done whenA fresh clone passes every reference and golden run, and the release scan finds no ROM bytes.

    Proof this works: Strike Remastered Collection HD (15/15 MATCH before each release), Road Rash Trilogy Recomp (ROM-free local build, audit script), X-Men 2: Clone Wars PC (tools/romscan.py)

Path B: Full decomp for porting

You first make a disassembly that rebuilds the ROM byte for byte. Then you rewrite each routine in C. If the game used C, you match it with the original compiler. If it was assembly, you prove each C routine with recorded calls from a hooked emulator. Last, you remove the 68000 and port the C.

Method: byte-matching disassembly, then C checked against an emulator (matching C where the game used a C compiler)

Pick this after Path A works, or when you want the game on new systems. It takes months. Start only if your game has a disassembly or you can build one.

  1. Check prior work and pick the route

    You learn what exists and how the game was made. A finished disassembly saves months. Library code in the ROM can prove a C compiler. That choice decides how you check your C.

    • Search Sonic Retro, GitHub and RecompLauncher for a disassembly or a rebuild of your game.
    • Look for C library code, such as math helpers or memcpy. X-Men 2 found six MPW library modules byte for byte.
    • Look at the code shape. X-Men 2's C functions keep a stack frame on a6 and read arguments at 8(a6). Compare a sample of your game's functions with that.
    • Write the route in SCOPE.md: matching C, or C checked by differential tests.
    Prompt for Claude
    GOAL: Decide whether <GAME> was written in C or assembly, and record the Path B route in SCOPE.md.
    READ FIRST: SCOPE.md, Ghidra listings of 20 functions spread across the ROM, https://github.com/crabsmack/xmen2disasm/blob/main/docs/COMPILER.md
    FENCE: Edit only `SCOPE.md` and `notes/compiler.md`. No code. Do not download compilers.
    CHECK: Run `git status --short` and paste it. Done means it shows only `SCOPE.md`, `notes/compiler.md` and `notes/parking-lot.md`, and notes/compiler.md lists each sampled function with its frame pattern, any library code found with its address, and a verdict with a confidence word.
    BUDGET: 2 hours.
    IF BLOCKED: Stop. Say what evidence is missing. Do not claim a compiler without a byte-level match.
    OTHER PROBLEMS: Write them in `notes/parking-lot.md`. Do not fix them.
    REPORT: 1) DONE, PARTIAL or BLOCKED. 2) Proof: the sample table. 3) Files changed. 4) The verdict and what would change it. 5) New parking-lot items.
    Done whennotes/compiler.md has the sample table and one verdict line, SCOPE.md names the route, and git status --short shows no other changed file except notes/parking-lot.md.

    Proof this works: X-Men 2: Clone Wars PC (MPW library fingerprint), https://github.com/crabsmack/xmen2disasm/blob/main/docs/COMPILER.md, https://www.retroreversing.com/megadrive

  2. Make a disassembly that rebuilds byte for byte

    The disassembly is the base for everything else. It must rebuild the exact ROM. Then any change that breaks a byte is caught at once. Unknown parts stay as raw bytes copied from your ROM.

    • Pick an assembler. Sonic Retro and X-Men 2 use the Macroassembler AS. Tiny Toon, gunstars and ninety-four use vasm.
    • Split the ROM into one file per function, named by start address. Leave unknown spans as raw bytes read from your ROM at build time.
    • Write tools/build.py. It assembles, compares the SHA-1, and exits 0 only on a match.
    • Prove the gate works: give it a --corrupt <ADDRESS> flag that changes one byte, and confirm the build fails. xmen2disasm's build.py has such a flag for its C build.
    • Add a Claude Code hook that blocks edits to the ROM, the expected hash and the build script.
    Prompt for Claude
    GOAL: Produce a disassembly of <GAME> that rebuilds the ROM byte for byte.
    READ FIRST: SCOPE.md, https://github.com/crabsmack/xmen2disasm (README, CLAUDE.md), the AS or vasm manual
    FENCE: Edit only `src/`, `tools/` and `config/`. Never edit the ROM or `config/expected.sha1`. Never commit ROM bytes.
    CHECK: Run `uv run tools/build.py`. Done means it exits 0 with a SHA-1 match, and `uv run tools/build.py --corrupt <ADDRESS>` exits non-zero.
    BUDGET: Stop after 4 hours or 3 failed tries on the same mismatch.
    IF BLOCKED: Stop. Paste the first differing offset and the bytes on both sides.
    OTHER PROBLEMS: Write them in `notes/parking-lot.md`. Do not fix them.
    REPORT: 1) DONE, PARTIAL or BLOCKED. 2) Proof: both build outputs. 3) Files changed. 4) Share of the ROM still raw bytes. 5) New parking-lot items.
    Done whentools/build.py prints a SHA-1 match, and the corrupt test fails as it should.

    Proof this works: X-Men 2: Clone Wars PC (xmen2disasm with AS), Tiny Toon Adventures: ACME All-Stars (vasm reassembly gate with a negative test), gunstars (vasm rebuild from its own listing)

  3. Sort code from data with runtime proof

    Static tracing misses code reached through tables. It can also mistake data for code. So you run the real game in a hooked emulator and log what executes. Every name and every code or data split then rests on proof.

    • Hook Genesis Plus GX to log each executed address, each ROM byte read as data, and each DMA source.
    • Drive it with a long input file. A tool-assisted speedrun (TAS) from TASVideos reaches deep into the game.
    • Compare the log with the disassembly. List executed code still marked as data, and data never read.
    • Convert proven code to source. Name data by kind: strings, tables, art, palettes.
    • Keep the routine ledger in notes/functions.csv with a status column.
    Prompt for Claude
    GOAL: Cross-check the <GAME> disassembly against one runtime coverage log and fix code/data errors in module <MODULE>.
    READ FIRST: `notes/functions.csv`, the coverage report from `uv run tools/coverage.py --list 50`, CLAUDE.md
    FENCE: Edit only files in `src/` for <MODULE> and `notes/functions.csv`. Use the project tools to convert or label; do not hand-edit raw spans.
    CHECK: Run `uv run tools/build.py` and `uv run tools/coverage.py`. Done means the build still matches, and the count of executed bytes outside known code drops for <MODULE>.
    BUDGET: Stop after 90 minutes or 3 rollbacks.
    IF BLOCKED: Stop. A rollback means the guess was wrong. List the addresses you could not settle.
    OTHER PROBLEMS: Write them in `notes/parking-lot.md`. Do not fix them.
    REPORT: 1) DONE, PARTIAL or BLOCKED. 2) Proof: coverage counts before and after. 3) Files changed. 4) What is left. 5) New parking-lot items.
    Done whenNo executed byte in the recorded corpus is outside known code, and the build still matches.

    Proof this works: X-Men 2: Clone Wars PC (emu/ harness, TAS 4473M, tools/coverage.py), gunstars (evidence-driven disassembler from an oracle hook), Tiny Toon Adventures: ACME All-Stars (unresolved-flow loop)

  4. First light: one routine in C, end to end

    You prove the whole loop on one small routine. On the matching route, the original compiler must turn your C into the same bytes. On the differential route, your C must give the same RAM writes and hardware writes as recorded calls.

    • Matching route: compile with the original compiler and compare. X-Men 2 ran MPW C 3.3 under the mps MPW emulator with tools/cc.py --match FILE.c. Its COMPILER.md says mps runs MPW tools headless on Windows, Linux and macOS.
    • The old compiler is Apple software that is no longer sold. COMPILER.md says where X-Men 2 got it. Check that you may use it before you download it.
    • Differential route: record every call to the routine with registers, RAM and port writes. Replay them against your C. gunstars did this with make test.
    • Pick a leaf routine first. A leaf calls no other routine.
    • Write the one-unit check into notes/method.md so every worker uses it.
    Prompt for Claude
    GOAL: Write routine <ADDRESS> (<NAME>) of <GAME> as C and pass its unit check.
    READ FIRST: the disassembly file `src/f/<ADDRESS>.asm`, `notes/method.md`, `notes/compiler.md`
    FENCE: Create only `game/c/<ADDRESS>.c` and its prototype line. Do not edit the disassembly or any check script. No inline assembly.
    CHECK: Run `<UNIT_CHECK_COMMAND> game/c/<ADDRESS>.c`. Done means an exact byte match (matching route) or every recorded call passes (differential route).
    BUDGET: Stop after 5 meaningfully different tries.
    IF BLOCKED: Stop. Paste the best diff. Name the first differing instruction and your guess at why.
    OTHER PROBLEMS: Write them in `notes/parking-lot.md`. Do not fix them.
    REPORT: 1) DONE, PARTIAL or BLOCKED. 2) Proof: the check output. 3) Files changed. 4) What was learned for notes/method.md. 5) New parking-lot items.
    Done whenOne routine passes its unit check, and the method is written down for workers.

    Proof this works: X-Men 2: Clone Wars PC (tools/cc.py --match), gunstars (test/captest.c replays recorded calls), ninety-four (10,276 recorded routine calls)

  5. Grind: every routine as C, leaves first

    Now you hand out routines in batches. Each worker gets a few routines in its own git worktree. A gate script must pass before a batch merges. Hand-written assembly routines get differential tests, not a compiler match.

    • Make a queue script that lists the next routines, leaves first, with how each must be checked.
    • Give each worker one batch in its own worktree. Use absolute paths to the ROM and tools.
    • Write a gate script that builds, runs the unit checks and replays a short reference session. It stops at the first failure.
    • Merge one batch at a time. Run the full gate again after each merge.
    • Track counts in PROGRESS.md: matched, tested, left.
    Prompt for Claude
    GOAL: Write the next batch of <N> routines from `uv run tools/decomp_queue.py -n <N>` as C, each passing its check.
    READ FIRST: CLAUDE.md, `notes/method.md`, the queue output, one already-done routine of the same kind as an example
    FENCE: Work only in this worktree. Create or edit only `game/c/<ADDRESS>.c` files for this batch and their prototypes. Never edit check scripts, references or other batches.
    CHECK: Run `uv run tools/gate.py --files <FILES>`. Done means every step prints PASS for every file.
    BUDGET: 5 tries per routine. Skip a routine after that and list it.
    IF BLOCKED: Stop the routine, not the batch. Record it in `notes/hard.md` with the best diff.
    OTHER PROBLEMS: Write them in `notes/parking-lot.md`. Do not fix them.
    REPORT: 1) DONE, PARTIAL or BLOCKED. 2) Proof: the gate output. 3) Files changed. 4) Routines skipped and why. 5) New parking-lot items.
    Done whenThe queue is empty, and the gate passes on the merged branch.

    Proof this works: X-Men 2: Clone Wars PC (tools/gate.py, worktrees, 3,047 matched + 117 shadow-tested), ninety-four (one C file per asm module)

  6. Prove the whole game from the C

    Unit checks are evidence, not the verdict. Now the whole game must run from your C and equal the reference. On the matching route, the ROM must rebuild from the C. On the differential route, whole sessions must match at every frame.

    • Matching route: link the compiled C in place of the assembly and compare the SHA-1. X-Men 2 did this with tools/build.py --from-c.
    • Differential route: run whole recorded sessions and compare RAM and VDP state at every vblank. ninety-four did this with make lockstep.
    • Prove the check can fail: corrupt one function and confirm a FAIL.
    • Have a person play the build and sign it off in docs/USER_VERIFIED.md.
    Prompt for Claude
    GOAL: Show that <GAME> runs entirely from the C and equals the reference.
    READ FIRST: PROGRESS.md, the gate script, the reference sessions list
    FENCE: Edit only `tools/` scripts for this proof and `PROGRESS.md`. Do not change any C to make this pass; send failures back as units.
    CHECK: Run `<WHOLE_GAME_CHECK>` and then `<WHOLE_GAME_CHECK> --corrupt <ADDRESS>`. Done means the first passes on every session and the second fails.
    BUDGET: 3 hours.
    IF BLOCKED: Stop. List each failing session with its first divergent frame.
    OTHER PROBLEMS: Write them in `notes/parking-lot.md`. Do not fix them.
    REPORT: 1) DONE, PARTIAL or BLOCKED. 2) Proof: both outputs. 3) Files changed. 4) Sessions not covered. 5) New parking-lot items.
    Done whenThe whole-game check passes on every session and its corrupt test fails. Then a person signs off a playthrough in docs/USER_VERIFIED.md.

    Proof this works: X-Men 2: Clone Wars PC (ROM rebuilt from the C with the same SHA-1), ninety-four (make lockstep, 18 sessions)

  7. Port: remove the 68000

    The C still assumes 68000 memory. The 68000 is big-endian, and PCs are little-endian. You move game memory to native layout and host byte order. A runtime replaces the VDP, sound chips and pads.

    • Put all RAM access behind one header, such as src/mem.h. Make the build fail if code touches raw RAM elsewhere.
    • Swap byte order at the edges: ROM data reads, save files and hardware ports.
    • Use a hardware runtime. gunstars and ninety-four use pulseman. The recomp projects use the framework runtime.
    • Rewrite the Z80 sound driver in C, or keep a Z80 interpreter with ymfm for the FM chip.
    • Compare the native build with the recompiled build at every frame of the test runs, with all options off.
    Prompt for Claude
    GOAL: Remove one 68000 dependency group <GROUP> from the <GAME> C so it runs in native memory layout.
    READ FIRST: `uv run tools/port_queue.py --stats`, `src/mem.h`, CLAUDE.md
    FENCE: Edit only the files listed for <GROUP> by the port queue and `src/mem.h`. Do not change game logic.
    CHECK: Run `<NATIVE_COMPARE_COMMAND>`. Done means the native build equals the recompiled build on every test run, and the port queue no longer lists <GROUP>.
    BUDGET: Stop after 3 failed tries on the same mismatch.
    IF BLOCKED: Stop. Paste the first divergent frame and the variable that differs.
    OTHER PROBLEMS: Write them in `notes/parking-lot.md`. Do not fix them.
    REPORT: 1) DONE, PARTIAL or BLOCKED. 2) Proof: compare output and queue counts. 3) Files changed. 4) What is left. 5) New parking-lot items.
    Done whenThe port queue lists no group, and the native compare passes on every test run.

    Proof this works: X-Men 2: Clone Wars PC (phase 5: no 68000 left, host byte order), ninety-four (src/mem.h makes the big-endian view on demand; make test greps for raw RAM access), https://gendev.spritesmind.net/forum/viewtopic.php?p=7621

  8. Harden the port for other systems

    A port to other systems needs C that behaves the same under any compiler. So you build it two ways and compare. You also run a sanitizer. A sanitizer is a compiler mode that catches undefined behaviour.

    • Record golden runs: per-frame hashes of work RAM, VDP state, picture and sound.
    • Build again with a second compiler, such as Clang, and compare every frame. pulseman's README uses Zig's Clang: pip install ziglang, then CC="python -m ziglang cc" CXX="python -m ziglang c++" LDLIBS=-lc++.
    • Build with -fsanitize=undefined and replay the golden runs.
    • Build for each new system. Run the same golden runs there.
    • Scan the release for ROM bytes. Ship only your code and a ROM check.
    Prompt for Claude
    GOAL: Add second-compiler and sanitizer golden checks to the <GAME> port.
    READ FIRST: the Makefile golden targets of https://github.com/1nputs/ninety-four (README section 'How it is verified'), PROGRESS.md
    FENCE: Edit only the build files and `tools/golden.py`. Do not change game code to make a check pass; file each failure as a unit.
    CHECK: Run `make golden-check`, `make golden-cross` and `make golden-ubsan`. Done means all three pass on every golden run.
    BUDGET: 3 hours.
    IF BLOCKED: Stop. Paste the first differing frame and which build differs.
    OTHER PROBLEMS: Write them in `notes/parking-lot.md`. Do not fix them.
    REPORT: 1) DONE, PARTIAL or BLOCKED. 2) Proof: the three outputs. 3) Files changed. 4) Builds not yet run on real hardware. 5) New parking-lot items.
    Done whenGolden runs match bit for bit across two compilers and a sanitizer build, on each target system.

    Proof this works: gunstars (golden-cross with Zig's Clang, golden-ubsan), ninety-four (golden-check, golden-cross, golden-ubsan)

Tools you need

ToolWhat it doesHow to get itHow we know
segagenesisrecomp (`GenesisRecomp`)
Required both
A beta framework that turns 68000 cartridge code into C. It also gives a shared runtime for the VDP, bus, sound, Z80 and an interpreter fallback.
Path A Path B
Download `GenesisRecomp-windows-x64-v0.1.2.zip` from release v0.1.2, or `git clone --recurse-submodules https://github.com/mstan/segagenesisrecomp.git` and build with `cmake -S recompiler -B build/recompiler -G "Visual Studio 17 2022" -A x64` then `cmake --build build/recompiler --config Release --parallel`README, docs/GETTING_STARTED.md and releases read 2026-11-13; used by Sonic 1 Recomp, Sonic 2 Recomp, Road Rash Trilogy, Super Monaco Duo-Remix and X-Men 2 PC. Licence: PolyForm Noncommercial.
Visual Studio with C++ and CMake
Required Windows
The Windows compiler and build tools the recomp projects use. CMake writes the build files, and Ninja runs them on some projects.
Path A Path B
Install Visual Studio 2022 (Community or Build Tools) with the Desktop development with C++ workload and its CMake tools. The download page now leads with Visual Studio 2026; 2022 is under Older Downloads. The steps use the generator name `Visual Studio 17 2022`.segagenesisrecomp docs/GETTING_STARTED.md read 2026-11-13 (asks for Visual Studio 2022); download page read 2026-11-13 (leads with 2026, links older downloads); Road Rash Trilogy says only Visual Studio 2022 is qualified; used by Sonic 1 and 2 Recomp, X-Men 2 PC, Road Rash Trilogy
BizHawk
Helpful both
A multi-system emulator with Lua scripting and input movies. Its Genesis core is Genesis Plus GX. Use it to record reference sessions.
Path A Path B
Download `BizHawk-2.11.1-win-x64.zip` from the latest release, unzip it to its own folder, and run `EmuHawk.exe`. It needs the MSVC++ redistributable.README and releases read 2026-11-13 (2.11.1 published 2026-05-01, win-x64 zip and linux-x64 tarball, Genplus-gx core, --lua= argument); used by PSIV Recomp (record.lua) and X-Men 2 PC (BizHawk 2.4's Genesis Plus GX core as a DLL, xmen2disasm README read 2026-11-13)
Genesis Plus GX
Helpful both
An accurate open-source Genesis emulator core. Projects hook it to log executed code and to compare frames.
Path A Path B
Use it inside BizHawk or RetroArch, or `git clone https://github.com/ekeeke/Genesis-Plus-GX` and build the libretro core from its `Makefile.libretro` in WSL to add your own hooksREADME read 2026-11-13 (frontends RetroArch, BizHawk, OpenEmu; non-commercial licence); used as the oracle by X-Men 2 PC, gunstars, ninety-four and Super Monaco QA
Musashi
Helpful both
A 68000 emulator written in C. Projects link it as an interpreter fallback and as a lockstep reference.
Path A
`git submodule add https://github.com/kstenerud/Musashi third_party/musashi`. Then build and run its `m68kmake` program first. It writes `m68kops.c` and `m68kops.h` from `m68k_in.c`. Add `m68kcpu.c`, `m68kops.c` and, for disassembly, `m68kdasm.c` to your CMake build, and set options in `m68kconf.h`.README and Makefile read 2026-11-13 (68000 to 68040, ANSI C89, MIT-style licence, m68kmake generates m68kops.c); used by Strike HD, Beyond Oasis, Streets of Rage, and Tiny Toon as a test reference
Capstone
Helpful both
A disassembly library with M68K support. Python tracers and recompilers use it to decode 68000 instructions.
Path A Path B
`pip install capstone`bindings/python/README.md read 2026-11-13 (pip install capstone; M68K in the repo description); used by Strike HD, Tiny Toon, Mickey & Donald and X-Men 2 (xmen2disasm tools/peek.py, read 2026-11-13)
The Macroassembler AS (`asl`)
Required both
A cross assembler with 68000 support. Sonic Retro's disassemblies and xmen2disasm build with it.
Path B
Download the Win32 binaries or the C sources from the download page. Latest build 1.42 Bld 316.home page and download page read 2026-11-13; used by xmen2disasm, s2disasm and PSIV Recomp's disassembly rebuild
vasm
Helpful both
A portable assembler with a M680x0 module and raw binary output. Several projects rebuild the ROM with it.
Path B
Download the Windows x64 or Linux x64 vbcc/vasm/vlink binaries from the vasm pagevasm page read 2026-11-13 (binaries dated 2021-03-19); used by gunstars, ninety-four and Tiny Toon (vasm 2.0f in Docker)
Ghidra
Helpful both
A free reverse engineering tool with a built-in 68000 decompiler. Use it to check function starts and read code. ghidra_sega_ldr adds a Genesis ROM loader.
Path A Path B
Install a 64-bit JDK 25. Download `ghidra_12.2_PUBLIC_20261016.zip` from the releases page, unzip it, and run `ghidraRun.bat`. For the loader, pick File, Install Extensions, the plus button, and the `ghidra_sega_ldr` zip, then restart Ghidra.Ghidra 12.2 release, README and application.properties read 2026-11-13 (12.2 published 2026-10-22, needs JDK 25; 12.0 needed JDK 21); ghidra_sega_ldr README and releases read 2026-11-13 (v2.3 built for Ghidra 11.0.1); used by xmen2disasm (Ghidra 12 with JDK 21, tools/decomp.py) and Sonic 2 Recomp (annotation exports)
asm-differ (`diff.py`)
Helpful both
An assembly diff viewer for matching work. It supports m68k. Use it to see why a C function does not match. For m68k it reads object files with m68k-elf-objdump, so it expects ELF objects.
Path B
`pip install 'asm-differ @ git+https://github.com/simonlindholm/asm-differ.git'`, then run it as `asm-differ`. It needs a `diff_settings.py` file in your project.README and diff.py read 2026-11-13 (m68k support, pip install, m68k-elf-objdump). No reviewed Genesis project names it. Whether it reads MPW object files was not checked; X-Men 2 used its own tools/cc.py --match instead.
uv
Helpful both
A fast Python project manager. It runs project tools with pinned packages, so every session uses the same versions.
Path A Path B
`powershell -ExecutionPolicy ByPass -c "irm https://astral.sh/uv/install.ps1 | iex"` on Windows, or `curl -LsSf https://astral.sh/uv/install.sh | sh` in WSLREADME read 2026-11-13; X-Men 2 PC and xmen2disasm run every tool as uv run tools/...
ymfm
Helpful both
Free Yamaha FM sound cores. They play the YM2612 music in nearly every reviewed runtime.
Path A Path B
`git submodule add https://github.com/aaronsgiles/ymfm third_party/ymfm` (already inside the segagenesisrecomp runner)repo read 2026-11-13 (BSD-3); used by Strike HD, PSIV, Beyond Oasis, Streets of Rage, X-Men 2 and the segagenesisrecomp runner
SDL
Required both
A library for the window, input, audio and controllers. The runtimes use SDL2 or SDL3.
Path A Path B
Use the copy your framework bundles, or `sudo apt install libsdl2-dev` in WSLrepo read 2026-11-13; SDL2 in Sonic 1 and 2 Recomp, X-Men 2, gunstars; SDL3 in Strike HD, PSIV, Tiny Toon
pulseman
Helpful both
A small Mega Drive hardware runtime in C for games rewritten in C. It has no 68000 or Z80 emulation.
Path B
`git submodule add https://github.com/1nputs/pulseman ext/pulseman`, then in WSL run `make`, `make check` and `make sdl` (needs `build-essential pkg-config libsdl2-dev`). Its README also names MSYS2 or a MinGW-w64 cross build for Windows.README read 2026-11-13 (C11, no 68k or Z80 emulation, MIT, make targets, Windows through MinGW-w64 or MSYS2); used by gunstars and ninety-four
Git for Windows
Required Windows
Version control. Every step clones repos, adds submodules or checks git status.
Path A Path B
Download Git for Windows from the URL and install it. Check it with `git --version`.segagenesisrecomp docs/GETTING_STARTED.md read 2026-11-13 (asks for Git for Windows); download page answers (read 2026-11-13)
Python 3.12 or newer
Required both
Runs the map, compare, coverage and release scripts. Capstone and asm-differ are Python packages.
Path A Path B
Download Python from the URL, or run `uv python install 3.12`. In WSL: `sudo apt install python3 python3-venv python3-pip`.segagenesisrecomp docs/DISASSEMBLIES.md read 2026-11-13 (Python 3.12 or newer); uv README read 2026-11-13 (uv python install); apt line from the Strike HD card; Python tools used by PSIV, Strike HD, Tiny Toon, X-Men 2 and the Sonic repos
PowerShell 7 (`pwsh`)
Required Windows
The shell that runs tools/check.ps1 in the steps. It installs beside the built-in Windows PowerShell 5.1.
Path A
`winget install --id Microsoft.PowerShell --source winget`. Or run the scripts with `powershell -NoProfile -ExecutionPolicy Bypass -File tools/check.ps1`, as Road Rash Trilogy does.Microsoft install page read 2026-11-13 (winget command, side by side with 5.1); Road Rash Trilogy and Super Monaco cards show the powershell -NoProfile -ExecutionPolicy Bypass -File form
WSL with GCC, make and SDL2
Required WSL/Linux
Linux inside Windows. Path B's C-rewrite style, pulseman and the hashing commands in the steps run there.
Path A Path B
In PowerShell run as administrator: `wsl --install`, then restart. In the Ubuntu shell: `sudo apt install build-essential pkg-config libsdl2-dev`.Microsoft WSL install page read 2026-11-13 (wsl --install installs Ubuntu); apt line from the gunstars and ninety-four cards
mps (MPW emulator)
Helpful both
Runs old Apple MPW tools on a modern PC with no window. X-Men 2 ran Apple MPW C 3.3 in it to match C byte for byte. Only needed if your game was built with MPW C.
Path B
Install Go, clone the repo, and run `go build`. Then `mps -install` with `MPW` set to a folder fetches MPW 3.5 GM, as xmen2disasm's `COMPILER.md` describes. The C compiler itself is separate.repo read 2026-11-13 (Go, MIT, last push 2023-04-11, has filemgr_windows.go, no README); xmen2disasm docs/COMPILER.md read 2026-11-13 (build steps, runs on Windows, Linux and macOS). Not built or run here.
Zig's Clang (`ziglang`)
Helpful both
A second C compiler for the cross-compiler golden check. It comes as a Python package.
Path B
`pip install ziglang`, then build with `CC="python -m ziglang cc" CXX="python -m ziglang c++" LDLIBS=-lc++`pulseman README read 2026-11-13 (the exact command); used by gunstars (golden-cross with Zig's Clang)

Skills and add-ons

Read any skill before you install it. A skill is a set of instructions that Claude will follow. See Tools and Skills for the full list and safety notes.

verification-before-completion

skills.sh

Claude may not say done without fresh proof. Here the proof is a MATCH line from the compare script, an empty miss list, or a SHA-1 match. It also comes with the superpowers plugin, so check /skills before you install it.

npx skills add obra/superpowers --skill verification-before-completion -a claude-code

systematic-debugging

skills.sh

No fix until the cause is found. That fits the first-divergence rule every strong Genesis project used.

npx skills add obra/superpowers --skill systematic-debugging -a claude-code

matcher

skills.sh

A careful method for matching one C function. Use it only if your game was built with a C compiler, like X-Men 2. Describe your cc.py --match check in AGENTS.md first. Install the five other lemiur matching skills with it, because they call each other by name.

npx skills add lemiur/game-decompilation-skill-for-claude --skill matcher -a claude-code

orchestrator

skills.sh

Runs many workers, each in its own worktree, with a check before every merge. That is how X-Men 2 ran its Path B batches.

npx skills add lemiur/game-decompilation-skill-for-claude --skill orchestrator -a claude-code

using-git-worktrees

skills.sh

Keeps each worker in its own copy of the repo. Its setup step may run installs, so watch it the first time.

npx skills add obra/superpowers --skill using-git-worktrees -a claude-code

handoff

skills.sh

Writes a short note so a fresh session can carry on. Strike HD kept such a note in docs/HANDOFF.md. Tell the skill to save inside your project.

npx skills add mattpocock/skills --skill handoff -a claude-code

ghidra-headless

skills.sh

Runs Ghidra with no window and saves functions and code to files. Ghidra has a 68000 language, but a raw ROM needs a loader first, such as ghidra_sega_ldr. It needs bash (Git Bash on Windows), and you set GHIDRA_HOME yourself.

npx skills add trailofbits/skills-curated --skill ghidra-headless -a claude-code

genesis-first-divergence (write your own)

write-your-own

X-Men 2 used a skill like this: xmen2disasm keeps first-divergence in .claude/skills/. PSIV and Sonic 2 followed the same steps. A skill makes every session use the same steps and proof.

A `.claude/skills/genesis-first-divergence/SKILL.md` that: 1) replays one session headless with a hash log; 2) runs the compare script and reads the first differing frame; 3) dumps RAM, VRAM, CRAM, VSRAM and VDP registers from both sides at that frame; 4) traces instructions for that frame only; 5) writes frame, symptom, cause and fix to `notes/divergence-log.md`. Rule: no code change until the first differing write is named.

genesis-miss-harvest (write your own)

write-your-own

Sonic 1, Beyond Oasis and Strike HD all ran this loop. The skill keeps the proof rule from slipping.

A `.claude/skills/genesis-miss-harvest/SKILL.md` that: 1) replays every file in `tests/refs/`; 2) collects `dispatch_misses.toml` or coverage logs; 3) for each new address, reads the disassembly or Ghidra listing; 4) adds proven starts to `game.toml` and interior labels to `blacklist.txt`, each with its proof; 5) regenerates and replays. Rule: never add an address only because a log names it.

Always-on debug server (write your own runtime tool)

write-your-own

Claude can ask the running game what just happened, without pausing it or filling the disk with logs. The framework's DISASSEMBLIES.md mentions a headless Ghidra MCP server but does not name it or publish its setup, so this guide lists none.

Add a TCP debug server to your runtime, like `segagenesisrecomp`'s `DEBUG.md` design and Sonic 1's `tools/dbg.py`. It keeps ring buffers of recent frames and RAM writes. Probes connect, read a window, and exit. Document the commands in `notes/debug.md`.

Claude setup for this system

Cut Genesis work by address and by recorded session. On Path A, one unit is one dispatch miss, one divergent frame, or one runtime bug, each tied to a reference session. On Path B, one unit is one routine or a small batch of leaf routines. Every unit has one check: an empty miss list, a MATCH line, a SHA-1 match, or a passing capture replay. Keep the ROM outside the repo and pass its absolute path.

Which model and effort for each job

The model and effort names come from one shared table, so they stay the same on every guide. See Claude Setup for the reasons.

JobWhat it means on this systemMax 20x (now called Ultra)Max 5x (now called Max)
Plan the projectPin the ROM revision by hash. Find any disassembly or recomp. Decide if the game used a C compiler. Write SCOPE.md.Fable 5.2
effort high
One session. This choice shapes months of work.
Opus 5.5
effort high
Use Fable 5.2 for this one session if you have weekly room.
Set up the projectBuild GenesisRecomp, the game repo, .gitignore, the headless mode and the one check script. On Path B, the byte-identical build gate and its hooks.Opus 5.5
effort medium
Sonnet 5.5
effort medium
Study the game fileTurn a disassembly or a trace into game.toml entries. Find jump tables, stack tricks and code reached only at run time. Sort code from data with runtime logs.Opus 5.5
effort high
Opus 5.5
effort medium
Raise to high for function edges and data layouts. A mistake there spreads.
Bulk unitsDispatch misses with clear proof. Leaf routines to rewrite as C. Labels for data spans. One small unit per worker, each with its check.Sonnet 5.5
effort medium
Haiku 5.5 for the plainest units.
Sonnet 5.5
effort medium
Fewer workers at once. Haiku 5.5 for the plainest units.
Hard unitsRoutines with stack tricks or jump tables, MPW functions that will not match, and units that failed five times in bulk.Fable 5.2
effort high
Go to xhigh only after a failed try at high.
Opus 5.5
effort high
Send only the last few that still fail to Fable 5.2.
Build the runtimeVDP behaviour, YM2612 and PSG sound, the Z80 bus, interrupts and frame timing. Or a C hardware runtime like pulseman for Path B.Opus 5.5
effort high
Opus 5.5
effort medium
Raise to high for timing and graphics bugs.
Find a bugFind the first divergent frame against the reference, then the first wrong write. Prove the cause before any fix.Opus 5.5
effort high
After two failed tries with full evidence, move to Fable 5.2.
Opus 5.5
effort high
One bug at a time.
Check the workA fresh session reruns all reference sessions and the build gate. It hunts for stubs, edited references, loosened hashes and generated-code edits.Opus 5.5
effort medium
Always a new session, never the one that did the work.
Sonnet 5.5
effort high
Always a new session, never the one that did the work.
Keep the recordsUpdate PROGRESS.md, notes/functions.csv counts, notes/divergence-log.md and the parking lot after each batch.Haiku 5.5
effort medium
Haiku 5.5
effort medium

How many sessions at once

Max 20x (now called Ultra)

Path A: 2 to 3 sessions at once, each on its own miss or divergence in its own worktree. The runtime files are shared, so keep one writer per file. Path B: 4 to 8 routine workers plus one lead session, each in its own worktree with its own build folder. Copy the test exe per run, as X-Men 2 learned. Merge one batch at a time and rerun the gate.

Max 5x (now called Max)

Path A: 1 session, sometimes 2. Path B: 2 or 3 routine workers plus the lead. Send hard units to one session at a time. Batch the docs updates at the end of the day.

Lines for this project's CLAUDE.md

Add to CLAUDE.md
- Read `SCOPE.md`, `PROGRESS.md` and `notes/divergence-log.md` at the start of every session.
- Never edit generated C under `generated/` or `build/`. Fix the recompiler config, the runtime or the adapter, then regenerate.
- No stubs. If execution reaches unknown code, stop and find the real routine. Never fake its effect.
- Function starts come from the disassembly or a Ghidra listing, never from a runtime log alone. Interior labels go in `blacklist.txt`.
- After every run, `dispatch_misses.toml` must be empty. An interpreter fallback is not a finished recompilation.
- Debug the first divergent frame, never the last visible symptom.
- Run every check with all enhancements off. Enhancements stay off by default.
- The one check is `pwsh tools/check.ps1 <ABSOLUTE_PATH_TO_ROM> --all-refs` (Path A) or `uv run tools/build.py` (Path B). No pasted output means not done.
- Do not edit `tests/refs/`, expected hashes, smoke baselines or check scripts. A baseline change needs my approval in the same commit as its cause.
- The ROM lives at `<ABSOLUTE_PATH_TO_ROM>`. Never copy, paste or commit it or anything made from it.
- Game-specific code stays in this repo, never in the shared framework.
- In a worktree, use absolute paths for the ROM and tools. Never run `git submodule deinit` in a linked worktree.
- Only I write sign-off lines in `docs/USER_VERIFIED.md`.
- Write side issues in `notes/parking-lot.md` and dead ends in `notes/findings.md`. Start every report with DONE, PARTIAL or BLOCKED.

Keep the context small

  • Never read a generated C file whole. Grep for the address, then read a few lines.
  • Give Claude the first divergent frame and the two state dumps, not the whole hash log.
  • Pass addresses, and let Claude read the disassembly file for that one function.
  • On Path B, give each worker its queue lines and one finished example of the same kind.
  • Add Read deny rules for generated/**, out/** and capture folders. Captures can reach gigabytes.
  • Clear the chat after each unit. The ledgers and the divergence log carry the state.

More prompts for this system

Each step above has its own prompt. These extra prompts cover the rest of the work. Fill in the parts in <ANGLE_BRACKETS>.

Scope and feasibility

Before any work, to choose Path A, Path B or both for one game.

Prompt for Claude
GOAL: Tell me if <GAME> (<REGION> <REVISION>, SHA-1 <SHA1>) suits Path A, Path B, or both, and draft SCOPE.md.
READ FIRST: bytes $100-$1FF of `<ABSOLUTE_PATH_TO_ROM>`, https://github.com/mstan/segagenesisrecomp#games, https://plutiedev.com/rom-header
FENCE: Create only `SCOPE.md` and `notes/prior-work.md`. No code. Do not copy the ROM.
CHECK: In WSL, run `sha1sum <ABSOLUTE_PATH_TO_ROM>` and paste it. Done means the same SHA-1 is in SCOPE.md, and SCOPE.md lists ROM size, header title, region, checksum, prior work with links, a first guess at C or assembly with its evidence, the chosen path, the first milestone with its check, and an out-of-scope list.
BUDGET: 45 minutes.
IF BLOCKED: Stop. Say what you could not check.
OTHER PROBLEMS: Write them in `notes/parking-lot.md`.
REPORT: 1) DONE, PARTIAL or BLOCKED. 2) Proof: header fields and hash. 3) Files changed. 4) Risks you see. 5) New parking-lot items.

A good answer: A one-page SCOPE.md with the hash and links to any disassembly or recomp. It makes a plain guess about C or assembly, with evidence, and lists the risks.

Map the binary

After setup, to build the function ledger for Path A or Path B.

Prompt for Claude
GOAL: Produce `notes/functions.csv` for <GAME>: every routine start with size, name, proof and status.
READ FIRST: SCOPE.md, `<DISASM_LISTING_PATH>` if one exists, the Ghidra project at `<GHIDRA_PROJECT_PATH>`
FENCE: Edit only `notes/functions.csv`, `blacklist.txt` and scripts in `tools/map/`. Do not edit the disassembly or game.toml yet.
CHECK: Run `python tools/map/check_ledger.py notes/functions.csv`. Done means no overlaps, no entry inside another, and every row names its proof.
BUDGET: 2 hours.
IF BLOCKED: Stop. List the address ranges you could not classify.
OTHER PROBLEMS: Write them in `notes/parking-lot.md`. Do not fix them.
REPORT: 1) DONE, PARTIAL or BLOCKED. 2) Proof: the checker output and counts. 3) Files changed. 4) Unclassified ranges. 5) New parking-lot items.

A good answer: A ledger with a few thousand rows, each with a proof source, plus a short list of ranges still unknown.

Unit loop, Path A: one miss or one divergence

For each code gap or wrong frame during the grind.

Prompt for Claude
GOAL: Fix <UNIT> (dispatch miss at <ADDRESS>, or the first divergence in session <RUN>) in <GAME>.
READ FIRST: CLAUDE.md, `notes/divergence-log.md`, the listing at <ADDRESS>
FENCE: Edit only `game.toml`, `blacklist.txt`, the runtime file named in the unit, and the notes. Never edit `generated/` or `tests/refs/`.
CHECK: Run `pwsh tools/check.ps1 <ABSOLUTE_PATH_TO_ROM> --all-refs`. Done means the unit is gone, no session got worse, and the miss list is empty.
BUDGET: 3 tries or 45 minutes.
IF BLOCKED: Stop. Report the evidence and your two best causes.
OTHER PROBLEMS: Write them in `notes/parking-lot.md`. Do not fix them.
REPORT: 1) DONE, PARTIAL or BLOCKED. 2) Proof: check output before and after. 3) Files changed. 4) Cause in one sentence. 5) New parking-lot items.

A good answer: A small diff and a check that went from FAIL to PASS. The divergence log gains one line that names one instruction or one hardware rule.

Unit loop, Path B: one routine as C

For each routine in the Path B grind.

Prompt for Claude
GOAL: Write routine <ADDRESS> (<NAME>) as C and pass its check.
READ FIRST: `src/f/<ADDRESS>.asm`, `notes/method.md`, one finished routine of the same kind
FENCE: Create only `game/c/<ADDRESS>.c` and its prototype. No inline assembly. No edits to checks, references or other routines.
CHECK: Run `<UNIT_CHECK_COMMAND> game/c/<ADDRESS>.c`. Done means an exact byte match (C games) or every recorded call passes (assembly games).
BUDGET: 5 meaningfully different tries.
IF BLOCKED: Stop. Paste the best diff and the first differing instruction.
OTHER PROBLEMS: Write them in `notes/parking-lot.md`. Do not fix them.
REPORT: 1) DONE, PARTIAL or BLOCKED. 2) Proof: the check output. 3) Files changed. 4) Lessons for notes/method.md. 5) New parking-lot items.

A good answer: One readable C file, a PASS line, and at most one new rule for the method notes.

Batch dispatch

In the lead session, to hand out the next set of units to workers.

Prompt for Claude
GOAL: Pick the next <N> units from `<QUEUE_COMMAND>` and launch one worker per unit, each in its own worktree.
READ FIRST: CLAUDE.md, PROGRESS.md, the queue output
FENCE: Do not do unit work yourself. Each worker gets one task card, one worktree, absolute paths, and its own build folder. Do not merge a batch whose gate failed.
CHECK: After each worker reports, run `<GATE_COMMAND>` on its branch. Done means every merged branch passed the gate, and the gate passes again on the merged main branch.
BUDGET: One round of <N> workers. Stop after the round.
IF BLOCKED: Stop. List each failed unit with its report.
OTHER PROBLEMS: Write them in `notes/parking-lot.md`. Do not fix them.
REPORT: 1) DONE, PARTIAL or BLOCKED. 2) Proof: gate output for each merge. 3) Branches merged. 4) Units returned to the queue. 5) New parking-lot items.

A good answer: A table of units with PASS or FAIL, only passing branches merged, and a clean gate on main.

Debug with evidence

When the game crashes, hangs, draws wrong or sounds wrong.

Prompt for Claude
GOAL: Find the proven cause of <SYMPTOM> in <GAME>. Do not fix it yet.
READ FIRST: `notes/divergence-log.md`, the reference session <RUN>, CLAUDE.md
FENCE: Read and run only. You may add logging under `tools/debug/`. No edits to the game, runtime or references.
CHECK: Run `git status --short` and paste it. Done means only files under `tools/debug/` and `notes/parking-lot.md` changed, and you name the first divergent frame, the first wrong write (address, value, expected value), and the instruction or hardware rule behind it, with pasted dumps from both sides.
BUDGET: 90 minutes.
IF BLOCKED: Stop. Give the narrowest frame range you reached and what evidence would settle it.
OTHER PROBLEMS: Write them in `notes/parking-lot.md`. Do not fix them.
REPORT: 1) DONE, PARTIAL or BLOCKED. 2) Proof: the dumps. 3) Files changed. 4) Proposed fix and how to check it. 5) New parking-lot items.

A good answer: A cause tied to one instruction or one VDP, Z80 or interrupt rule, with dumps from both sides and a one-line fix plan.

Verify and hunt for cheating

In a fresh session after any claimed DONE, and before every merge or release.

Prompt for Claude
GOAL: Check that the work in <BRANCH> is real.
READ FIRST: CLAUDE.md, the worker's report, `git diff <BASE>..<BRANCH>`
FENCE: Read and run only. Do not fix anything.
CHECK: Rerun `<CHECK_COMMAND>` yourself. Then look for: edits under `generated/`, changed files in `tests/refs/` or expected hashes, stubs or empty functions, new `blacklist.txt` lines without proof, fallback counts that went up, enhancements left on, inline assembly, and edits outside the fence. Done means each item is marked clean or found, with file and line.
BUDGET: 45 minutes.
IF BLOCKED: Stop. Say what you could not run.
OTHER PROBLEMS: Write them in `notes/parking-lot.md`. Do not fix them.
REPORT: 1) PASS or FAIL. 2) Proof: your own check output. 3) Each finding with file and line. 4) Whether the report matched what you saw. 5) New parking-lot items.

A good answer: Your own check output, a short list of clean items, and any finding with an exact file and line.

Status report

At the end of a day or a batch.

Prompt for Claude
GOAL: Report where <GAME> stands, using numbers from the ledgers only.
READ FIRST: PROGRESS.md, `notes/functions.csv`, `dispatch_misses.toml`, `notes/divergence-log.md`, `notes/parking-lot.md`
FENCE: Edit only `PROGRESS.md`. No code.
CHECK: Run `<CHECK_COMMAND>` and paste the summary line. Done means every number in the report comes from a file or that output.
BUDGET: 15 minutes.
IF BLOCKED: Stop. Say which file is missing or stale.
OTHER PROBLEMS: Write them in `notes/parking-lot.md`.
REPORT: 1) DONE, PARTIAL or BLOCKED. 2) Counts: routines done and left, misses, sessions matching, open divergences. 3) What changed today. 4) Top three risks. 5) Parking-lot items for me to decide.

A good answer: A short table of counts, a line on what changed, and a few risks. No padding.

Session handoff

Before you clear the chat, or when usage is about to run out.

Prompt for Claude
GOAL: Write what the next session needs to continue <TASK> without this chat.
READ FIRST: PROGRESS.md, `notes/handoff.md`, `git status`, `git log -5 --oneline`
FENCE: Edit only `notes/handoff.md` and `PROGRESS.md`. Commit nothing else.
CHECK: Run `git status --short` and paste it. Done means only notes/handoff.md, PROGRESS.md and notes/parking-lot.md changed, and notes/handoff.md has: the current unit, what was tried with results, the last check output, uncommitted changes, the exact next command, and what is checked by a machine versus still to be played by a person.
BUDGET: 10 minutes.
IF BLOCKED: Stop. Write what you have.
OTHER PROBLEMS: Write them in `notes/parking-lot.md`.
REPORT: 1) DONE, PARTIAL or BLOCKED. 2) Proof: paste the new handoff section. 3) Files changed. 4) The next command. 5) New parking-lot items.

A good answer: A dated handoff block that a new session can act on in one read, with the next command spelled out.

Blocked or cannot deliver

When a unit keeps failing, or Claude keeps saying it almost works.

Prompt for Claude
GOAL: Give me a plain answer on <UNIT>: can it be done with what we have, and what would it take?
READ FIRST: the attempts in `notes/findings.md` for <UNIT>, the last check output
FENCE: No code changes. No new tries.
CHECK: Run `git status --short` and paste it. Done means it shows no change outside notes/parking-lot.md, and you state one of: CAN DO (with the plan and its check), CANNOT DO NOW (with the missing piece), or NEEDS A RULING (with the options).
BUDGET: 20 minutes.
IF BLOCKED: Say BLOCKED and why in one sentence.
OTHER PROBLEMS: Write them in `notes/parking-lot.md`.
REPORT: 1) CAN DO, CANNOT DO NOW or NEEDS A RULING. 2) Proof: the evidence behind it. 3) Options with cost and risk. 4) What you recommend. 5) New parking-lot items.

A good answer: One clear verdict, the evidence, and two or three options. No "should work".

Scope change request

When Claude finds that a fix needs work outside the fence, such as in the shared framework.

Prompt for Claude
GOAL: Ask me to approve one change outside the current fence for <UNIT>.
READ FIRST: SCOPE.md, CLAUDE.md, the evidence for the needed change
FENCE: Make no change yet. Write the request in `notes/scope-requests.md` only.
CHECK: Run `git status --short` and paste it. Done means only notes/scope-requests.md and notes/parking-lot.md changed, and the request names the files outside the fence, why the change is needed with evidence, what else it could break (other games on the framework count), and how it will be checked.
BUDGET: 15 minutes.
IF BLOCKED: Stop. Say what you need to know.
OTHER PROBLEMS: Write them in `notes/parking-lot.md`.
REPORT: 1) DONE, PARTIAL or BLOCKED. 2) Proof: the evidence. 3) The exact change proposed. 4) The risk to other games and how to test it. 5) New parking-lot items.

A good answer: A short request that I can answer yes or no, with the risk to other games spelled out.

Keep Claude on task

These are the detours Claude tends to take on this system, and the rule that stops each one. The full method is on Stay on Task.

The detourThe rule to give Claude
Claude edits the generated C to make a crash go away. The next regeneration wipes the fix and hides the real bug.Never edit generated C. Fix the recompiler config, the runtime or the adapter, then regenerate. Add a PreToolUse hook that blocks writes to generated/.
Claude writes a stub or a fake event when the game reaches code it does not know.No stubs, ever. Stop, find the real routine in the disassembly or Ghidra, and fix discovery.
Claude adds every address from the miss log as a new function start. Interior labels then break the game.Function starts come from the disassembly or a Ghidra listing. A runtime log alone is not proof. Interior labels go in blacklist.txt with a reason.
Claude lets the interpreter fallback run missed code and calls the recomp finished.A run is only clean when dispatch_misses.toml is empty. Report the fallback count in every status.
Claude updates a smoke baseline or an expected hash so a failing check passes.Never edit references, baselines or expected hashes alone. A baseline change goes in the same commit as the code that justifies it, with my approval.
Claude chases the visible glitch instead of its cause many frames earlier.Find the first frame where expected and actual differ. No fix until the first wrong write is named.
Claude puts a game-specific hack into the shared framework.Game code stays in the game repo. A framework change needs a scope request and a check that other games still work.
Claude says a fix worked because the build passed.Done means the check output is pasted. A gameplay fix also needs my playtest sign-off before it counts.

Problems other projects hit

Dispatch misses are silent. The game jumps to an address with no generated function and skips whole routines.

Fix: Read dispatch_misses.toml after every run. Keep an interpreter only as a loud floor, and resolve misses through the disassembly.

Seen in: Sonic the Hedgehog Recomp, X-Men 2: Clone Wars PC

An interior label treated as a function start gave a black screen.

Fix: Keep blacklist.txt of addresses that must never be entries, each with a one-line reason.

Seen in: Sonic the Hedgehog Recomp

68000 stack tricks break a plain C call model. Callees bump the return address with addq.l #2,(sp) or pop it with addq.w #4,sp. Code pushes an address and runs rts as a jump.

Fix: Add explicit handling in the emitter for each pattern. Keep a trace build that checks return addresses.

Seen in: PSIV Recomp, X-Men 2: Clone Wars PC, Tiny Toon Adventures: ACME All-Stars

Small 68000 rules caused wrong results. cmpa.w and short absolute addresses sign-extend. DIVU overflow sets only V. movem.w sign-extends into data registers.

Fix: Test the generated C against Musashi, one sample per instruction form the ROM uses, with edge values.

Seen in: Tiny Toon Adventures: ACME All-Stars, Strike Remastered Collection HD

Recompiled code never lags, so busy scenes ran too fast. Sonic 2's special stage ran about twice as fast as on hardware. Frame counters also drifted from the emulator within seconds.

Fix: Sync comparisons on the game's own vblank wait, not on wall frames. Decide on purpose how to model slowdown, and write it in SCOPE.md.

Seen in: Sonic the Hedgehog 2 Recomp, ninety-four, gunstars, X-Men 2: Clone Wars PC

A compare blind spot hid a bug. The stack block was hashed only from the stack pointer up, which skipped all battle state.

Fix: List what the hash covers and what it skips. Test the check by corrupting a value it should catch.

Seen in: PSIV Recomp

Random numbers came from the VDP HV counter, which depends on exact cycle timing. Replays went off course.

Fix: Log every HV counter read in the reference run. Feed the values back during replay like pad input.

Seen in: PSIV Recomp

A vendored Z80 core wrote back on BIT n,(HL). That silenced a helicopter sound in Urban Strike.

Fix: Test the Z80 core with zexdoc and zexall, and compare sound-chip writes with the reference per driver tick.

Seen in: Strike Remastered Collection HD, Tiny Toon Adventures: ACME All-Stars

PC compilers changed behaviour. MSVC evaluated operands in another order than MPW C. MSVC also merged two identical functions whose addresses the game compares.

Fix: Split expressions with two side effects into separate lines. Link with /OPT:NOICF. Compare a second compiler's build frame by frame.

Seen in: X-Men 2: Clone Wars PC

Only one ROM revision works. Renamed files from other regions are refused. Road Rash also refuses interleaved .smd dumps, while PSIV and Mickey & Donald convert them.

Fix: Identify ROMs by content hash. Read the header to tell the player which version they have. Accept .smd only if you convert it before the hash check.

Seen in: Road Rash Trilogy Recomp, Mickey & Donald Collection, PSIV Recomp

Windows build traps: the 260-character path limit broke deep submodule paths, and zip timestamps made Ninja rebuild forever.

Fix: Stage builds near the drive root and back-date unpacked files, as the CI scripts did.

Seen in: Streets of Rage Collection Recomp, Beyond Oasis Recomp

A RAM layout value copied from Sonic 1 landed inside Sonic 2's object RAM and corrupted objects.

Fix: Take every per-game layout value from that game's own disassembly constants. Never reuse another game's numbers.

Seen in: Sonic the Hedgehog 2 Recomp

How long it takes

Max 20x (now called Ultra)Max 5x (now called Max)One person, no AIHow sure
Path A: running on PC1 to 6 weeks3 to 16 weeks3 to 12 monthsmedium confidence
Path B: full decomp6 to 20 weeks14 to 50 weeks2 to 5 yearslow confidence

Read this first

These numbers assume one medium game, your first Genesis project, and one person giving 10 to 15 hours a week. Path A is medium confidence. Several dated AI projects agree on days to weeks, but most repos hide work done before the first commit. Those authors also seem to have worked full time. Path B is low confidence. It rests on one fully dated project, X-Men 2, which was a C game run by an expert setup. The two assembly-game rewrites hide their history and kept their notes outside the repo. The Max 5x ranges are the Max 20x ranges stretched about 2 to 3 times for fewer workers. No reviewed project says which plan it used. The no-AI numbers are low confidence. No pre-agent Genesis recomp exists, so they lean on sonic3air and on other systems.

What makes it faster or slower

  • Whether a disassembly exists. With one, function starts come for free and Path A starts in days.
  • Whether the game used C. A C game can be matched byte for byte. An assembly game needs recorded tests for every routine.
  • How the game keeps time. Vblank polling, lag frames and HV counter reads all need careful work in the compare.
  • How much code hides behind jump tables and stack tricks. Each pattern needs its own handling.
  • Your review time. Each unit needs a look at its proof, and that is your 10 to 15 hours a week.

The facts behind these numbers

ProjectWhat happenedLink
X-Men 2: Clone Wars PCFirst commit 2026-10-15. First release 2026-10-24. 1 contributor. README says Claude Code agents wrote almost all of it in nine days. The disassembly repo dates those days 2026-10-14 to 10-23, from disassembly to matched C to native port.source
PSIV RecompFirst commit 2026-11-04. First release 2026-11-07. 1 contributor. 77 of 78 commits carry a Claude co-author line. Matches BizHawk on a 24,635-frame session. Its plan marks milestones M0 to M6 done on 2026-11-03, a day before the first commit.source
Strike Remastered Collection HDHandoff entries from 2026-10-22. First release 2026-10-23. v1.0.0 on 2026-11-12. 3 contributors. README says Claude wrote it with the maintainer.source
Sonic the Hedgehog 2 RecompFirst commit 2026-05-05. First release v0.1.0 on 2026-05-19. Built on an existing framework. Has CLAUDE.md and AGENTS.md.source
Sonic the Hedgehog RecompFirst commit 2026-03-24. First release 2026-06-17. 1 contributor. The shared framework was built alongside it.source
gunstarsFirst commit 2026-10-16. First release 2026-10-19. 1 contributor. Whole game rewritten in C, reusing the tools and runtime of ninety-four.source
Beyond Oasis RecompFirst commit 2026-10-29. First release 2026-10-31. 1 contributor. AI flag set by the store only.source
sonic3airScripts dated from 2017. Repo first commit 2021-10-21. First release 2022-07-31. 21 contributors. No AI stated. A pre-agent rewrite built on a community disassembly.source

Projects reviewed (14)

RecompLauncher lists 21 Genesis projects for 19 games. The rule took the top-starred project for each of the top 12 games, plus two lower projects whose developers say they used AI. That gives 14 projects. None came from outside RecompLauncher.

What each project teaches

sonic3air

Sonic 3 & Knuckles

Top rated on RecompLauncherreimplementationplayable
Oxygen EnginelemonscriptVisual Studio 2022CMakeSDL2skdisasm

Started: 2021-10. First release: 2022-07. Code: C++ and lemonscript.

AI: none stated

Lesson: A port can be scripts on a small engine that fakes only the hardware the game uses. It took years by hand, with a community disassembly as the guide.

Mickey & Donald Collection

Castle of Illusion / QuackShot / World of Illusion

Top rated on RecompLauncheremulator-basedplayable
own Mega Drive coreVisual Studio 2022CMakeCapstonelibrashader

Started: 2026-10. First release: 2026-10. Code: C++ and Python.

AI: none stated

Lesson: A headless, scriptable core turns every finding into a script you can rerun. This is not a decomp, but its RAM notes mark anything unchecked as inferred.

Strike Remastered Collection HD

Desert Strike: Return to the Gulf; Jungle Strike; Urban Strike

Top rated on RecompLauncherstatic recompplayable
tools/trace.pytools/recomp.pyCapstoneMusashiymfmSDL3

Started: 2026-10. First release: 2026-10. Code: C.

AI: yes: README says Claude wrote the reverse engineering, recompiler and PC host, directed and tested by the maintainer Agent files: docs/HANDOFF.md.

Lesson: Share one hardware model between the reference emulator and the recompiled build. Then any mismatch can only come from the CPU translation.

SonicTheHedgehog2Recomp

Sonic the Hedgehog 2

Top rated on RecompLauncherstatic recompplayable
segagenesisrecomps2disasmCMakeMSVCSDL2ymfm

Started: 2026-05. First release: 2026-05. Code: C and Python.

AI: yes: CLAUDE.md and AGENTS.md for Claude Code sessions; share of AI-written code not stated Agent files: CLAUDE.md, AGENTS.md.

Lesson: Feed the recompiler labels and jump tables from a community disassembly. Write notes as measured facts versus beliefs, so a later session does not trust a guess.

X-Men 2: Clone Wars PC

X-Men 2: Clone Wars

Top rated on RecompLauncherport of a decompcomplete
segagenesisrecomp forkxmen2disasmApple MPW C 3.3Genesis Plus GXuvVisual Studio 2022

Started: 2026-10. First release: 2026-10. Code: C and C++.

AI: yes: README says Claude Code agents wrote almost all of it in nine days under measured definitions of done Agent files: CLAUDE.md, .claude/settings.json.

Lesson: Work in phases, each with a script that checks it. Find the original compiler, match the C, then port. A person signs off each playtest.

Sonic the Hedgehog Recomp

Sonic the Hedgehog

Top rated on RecompLauncherstatic recompplayable
segagenesisrecomps1disasmCMakeMSVCSDL2ymfm

Started: 2026-03. First release: 2026-06. Code: C and Python.

AI: yes: set up for Claude Code sessions (CLAUDE.md, PRINCIPLES.md, DEBUG.md); share of AI-written code not stated Agent files: AGENTS.md, CLAUDE.md, segagenesisrecomp/CLAUDE.md, segagenesisrecomp/PRINCIPLES.md.

Lesson: Put the agent rules in the shared engine and point each game's CLAUDE.md at them. Rules like no stubs and empty miss lists then apply to every game.

Road Rash Trilogy Recomp

Road Rash

Top rated on RecompLauncherstatic recompplayable
segagenesisrecomp (pinned)Visual Studio 2022CMakeNinjaSDL2Python

Started: 2026-11. First release: 2026-11. Code: C++ and Python.

AI: yes: developer flag for code and art; agent instruction files kept outside the public repo

Lesson: Pin the framework to one commit and apply your changes with patch scripts. Ship an installer that builds the game from the player's own ROM.

Streets of Rage Collection Recomp

Streets of Rage

Top rated on RecompLauncherstatic recompplayable
tools/recomp.mjsMusashiNode.jsCMakeNinjaClang (LLVM-MinGW)

Started: 2026-10. First release: 2026-11. Code: C and JavaScript.

AI: catalog flag set by the store; the repo says nothing about AI

Lesson: One engine can serve many cartridges when each game is a folder of JSON. But watch for per-game assumptions leaking across games.

Beyond Oasis Recomp

Beyond Oasis

Top rated on RecompLauncherstatic recompplayable
tools/recomp.mjsMusashiNode.jsClang (LLVM-MinGW)CMakeNinja

Started: 2026-10. First release: 2026-10. Code: C and JavaScript.

AI: catalog flag set by the store; the repo says nothing about AI

Lesson: Record which code ran while playing and feed it back to the tracer. The same build run with --interpret must give an identical hash log.

PSIV Recomp

Phantasy Star IV: The End of the Millennium

Top rated on RecompLauncherstatic recompplayable
tools/emit.pyps4disasmBizHawkVisual Studio Build ToolsCMakeNinja

Started: 2026-11. First release: 2026-11. Code: C and Python.

AI: yes: README says Claude wrote most of the code; 77 of 78 commits carry a Claude co-author line

Lesson: Build the emulator comparison early, before sound. A BizHawk script that hashes memory each logical frame found bugs no one would see by eye.

ninety-four

NHL '94

Top rated on RecompLauncherreimplementationplayable
pulsemanNHL94Genesis asm rebuildvasmGenesis Plus GXGCCClang

Started: 2026-10. First release: 2026-10. Code: C.

AI: catalog flag set by the store; README has a 'For agents' handoff section Agent files: README.md section 'For agents'.

Lesson: Replay recorded routine calls against each C routine, then whole sessions against the emulator. Let the build enforce rules, such as no raw RAM access.

gunstars

Gunstar Heroes

Top rated on RecompLauncherreimplementationplayable
pulsemanown disassemblervasmGenesis Plus GXGCCZig's Clang

Started: 2026-10. First release: 2026-10. Code: C.

AI: catalog flag set by the store; README has a 'For agents' handoff section

Lesson: Tests only cover what the recordings reach. Routines no recorded run touched are ported but untested, and the README says so.

Tiny Toon Adventures: ACME All-Stars

Tiny Toon Adventures: ACME All-Stars

Added: developer used AIstatic recompplayable
tools/recompile.pyCapstonevasmMusashiNuked-OPN2SDL3

Started: 2026-11. First release: 2026-11. Code: C and Python.

AI: yes: developer flag for code; the repo does not describe how AI was used

Lesson: With no public disassembly, make one and prove it by byte-identical reassembly first. Then test the recompiler against Musashi per instruction form.

Super Monaco Duo-Remix Recomp

Super Monaco GP and Ayrton Senna's Super Monaco GP II

Added: developer used AIstatic recompin progress
segagenesisrecomp (patched)Visual Studio 2022CMakeNinjarcheevosCapstone

Started: 2026-11. First release: 2026-11. Code: C++ and Python.

AI: yes: README says support pays for Codex and Claude Code credits; no agent files published

Lesson: Keep enhancement code read-only on game memory and keep an original-rendering switch. Write down in each release which checks were run and which were not.

What we could not check

  • Which retail Genesis games used a C compiler. Only X-Men 2 (Apple MPW C 3.3) is proven here. A secondary page names Sonic Spinball as C; that was not checked.
  • Whether ghidra_sega_ldr v2.3, built for Ghidra 11.0.1, loads in Ghidra 12.2.
  • That mps builds and runs on Windows today. The repo has no README and was last pushed in 2023. Its Windows support comes from xmen2disasm's COMPILER.md and a filemgr_windows.go file. It was not built or run here.
  • Whether Visual Studio 2026 works for these builds, and its CMake generator name. The framework guide and the reviewed projects name Visual Studio 2022 and Visual Studio 17 2022.
  • Whether asm-differ can read MPW object files. It calls m68k-elf-objdump, which reads ELF files.
  • Nothing in this guide was run on a real Genesis ROM here. No ROM was downloaded. The header one-liner, the build commands and the tool installs come from docs and reviewed repos, not from a test run.
  • The SpritesMind forum page answers a normal browser request, but a bare curl -I request gets 403.
  • How long Road Rash, Super Monaco, Tiny Toon, Strike HD, gunstars and ninety-four took before their first public commit.
  • Whether the AI-flagged projects without agent files (Beyond Oasis, Streets of Rage, Tiny Toon) used AI for most code.
  • The vasm author site sun.hasenbraten.de was under maintenance on 2026-11-13; the page at compilers.de was read instead.
  • Sega Retro's SNASM68K page was blocked by bot protection, so the SNASM68K details come from retroreversing.com. How common SNASM was is not known here.
  • The script names in the step commands follow the reviewed projects: tools/check.ps1, compare.py, gate.py, decomp_queue.py, port_queue.py and build.py --corrupt. So do the make golden-check, golden-cross and golden-ubsan targets. You write your own versions.

Sources