Skip to the page
It's Decomplicated
Sega, 1994

Sega 32X decomp guide

Three CPU types in one box. Path A works with your own recompiler. Path B is untried.

What this system is like

The 32X plugs into the top of a Genesis. It adds two Hitachi SH-2 chips, a frame buffer and new sound. The Genesis 68000 and Z80 still run too. So one game uses four processors of three kinds.

Path A has public source for one game. Chaotix Recompiled turned Knuckles' Chaotix into native C++. It reached a playable release five days after its first public commit. Its handbook says Claude co-wrote 228 of its 264 commits. A second Chaotix recomp reached a finished level in its first week. There is no shared 32X recompiler, so you build your own, as both Chaotix projects did.

Path B has never been finished for any 32X game. Byte-exact disassemblies were made for Virtua Racing Deluxe and for parts of Knuckles' Chaotix. The Virtua Racing one later changed the game on purpose, so it no longer matches. A Doom rebuild in C stalled in 2024. So Path B here is new ground, and this guide says where it is guesswork.

Most Path A work runs on Windows with MSYS2 or Visual Studio. Some Path B tools want Linux, so keep WSL ready. Expect weeks for Path A and the better part of a year for Path B.

Four chips, three instruction sets

A 32X game runs code on a 68000, two SH-2s and a Z80. Your recompiler must read both 68000 and SH-2 code. Your runtime must keep all four in time. Chaotix Recompiled cuts each scanline into four fixed time slices for this.

The CPUs signal through 16 bytes

The 68000 and the two SH-2s share a few COMM registers, 16 bytes in all. They pass commands and wait on each other there. The SH-2 cannot see the 68000's work RAM at all. Get one handshake wrong and the game hangs or crashes.

SH-2 code lives in RAM

The boot ROM copies SH-2 code from the cartridge into SDRAM. One Chaotix CPU then copies code into its cache memory and runs it there. The 68000 also writes small jump stubs into RAM. A recompiler that only reads the ROM misses all of this.

No shared framework

The Genesis has a shared recompiler. The 32X does not. Every reviewed recomp wrote its own decoder, recompiler and runtime. Plan for that work up front. Do not expect to download a tool that does it.

Few games, little prior work

Only three 32X games have any public source project: Knuckles' Chaotix, Virtua Racing Deluxe and Doom. For any other game, you start from zero. You will map the ROM yourself.

No BIOS needed if you fake the boot

The real 32X runs a boot ROM before the game. Chaotix Recompiled does the boot's job in code instead. It copies the SH-2 code and writes the ready signals. So players need only their own game ROM.

Two paths

Path A

Get it running on PC

First you write simple interpreters for the 68000, SH-2 and Z80. They are the answer key. Then a recompiler turns the game's code into C++ ahead of time. Code it misses falls back to the interpreter. You run both side by side every frame. Any difference points at one bad instruction or one hardware rule.

Go to the steps

Path B

Full decomp for porting

No full 32X decomp exists, so this path joins proven pieces. First you make a disassembly that rebuilds the ROM byte for byte, as two projects did. Then you rewrite each routine in C. You prove each one against recorded calls from your Path A interpreter. Last, you replace the CPUs and the COMM handshakes with plain code.

Go to the steps

Path A: Get it running on PC

First you write simple interpreters for the 68000, SH-2 and Z80. They are the answer key. Then a recompiler turns the game's code into C++ ahead of time. Code it misses falls back to the interpreter. You run both side by side every frame. Any difference points at one bad instruction or one hardware rule.

Method: static recompilation with your own recompiler, checked in lockstep against your own reference interpreter

Pick this first for any 32X game. It is the only route that has reached a playable native build. If you only want better graphics, the Virtua Racing Deluxe Enhanced route of wrapping PicoDrive is shorter, but it is not a decomp.

  1. Pick the game and write the scope

    You pin the exact ROM by hash. A different revision is a different program. Then you read both headers and look for prior work. A disassembly can save you weeks of mapping.

    • In WSL, hash your own dump: sha1sum <ABSOLUTE_PATH_TO_ROM>.
    • Read both headers with Python: python3 -c "d=open('<ABSOLUTE_PATH_TO_ROM>','rb').read();print(len(d),d[0x100:0x110],d[0x150:0x180],d[0x1F0:0x1F3],d[0x3C0:0x3D0],[hex(int.from_bytes(d[o:o+4],'big')) for o in range(0x3D4,0x3F0,4)])".
    • Check that $100 says SEGA 32X. The seven numbers are the SH-2 copy source, destination, size, both start addresses and both vector bases.
    • Search RecompLauncher and GitHub for your game. Check sonicretro/chaotix, matiaszanolli/sega-vr-disasm and nukeykt/Doom32x-RE if your game is one of those three.
    • Write SCOPE.md: game, region, SHA-1, size, header fields, goal, technique, first milestone and its check, and what is out of scope.
    Prompt for Claude
    GOAL: Draft SCOPE.md for a 32X static recomp of <GAME> (<REGION>).
    READ FIRST: bytes $100-$1FF and $3C0-$3EF of `<ABSOLUTE_PATH_TO_ROM>` (read only), https://plutiedev.com/rom-header, https://github.com/viciious/32XDK/blob/main/docs/32x-hardware-manual.md (section 5.1 Boot ROM)
    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 SHA-1, size, `SEGA 32X` system field, title, region, the seven user header values in hex, prior work with links, the 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: the hash and header values. 3) Files changed. 4) Prior work found, with links. 5) New parking-lot items.
    Done whenSCOPE.md names the ROM by SHA-1 and size, lists the user header values and prior work, and you approved it.

    Proof this works: Chaotix Recompiled (rejects any ROM but one SHA-1), s32xfan/virtuaracing (pins one SHA-256 and revision), https://github.com/viciious/32XDK/blob/main/docs/32x-hardware-manual.md

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

    You lay out a repo like Chaotix Recompiled's. The ROM and the generated code stay out of git. One command builds and runs every test. CI builds without the ROM, so it can only check the interpreter and unit tests.

    • Install MSYS2 from msys2.org. In the MinGW64 shell run pacman -S mingw-w64-x86_64-{gcc,cmake,ninja,sdl3,sdl3-ttf} git python.
    • Make folders like Chaotix's: src/cpu, src/runtime, tools/recompiler, tools/rom_analyzer, coverage, generated, tests/golden.
    • Add *.32x, *.bin, generated/ and build/ to .gitignore before the ROM is near the repo.
    • Pass the ROM as a CMake setting, as Chaotix does with CHAOTIX_ROM: cmake -S . -B build -G Ninja -DCMAKE_BUILD_TYPE=Release -DGAME_ROM=<ABSOLUTE_PATH_TO_ROM>.
    • In the MSYS2 shell, give the ROM path in Windows form. Chaotix's README says to use cygpath -m <ROM_PATH>.
    • Make the one check: cmake --build build && (cd build && ctest --output-on-failure).
    • Add a CI job that fails if a large file or anything under generated/ is tracked. Chaotix caps files at 512 KB.
    Prompt for Claude
    GOAL: Set up the <GAME> recomp repo with a CMake build and one check command.
    READ FIRST: SCOPE.md, the README and CMakeLists.txt of https://github.com/YuutaTsubasa/ChaotixRecompiled (layout and Build section only)
    FENCE: Edit only files in this repo. Do not write CPU or game code yet. Do not add features.
    CHECK: Run `cmake -S . -B build -G Ninja -DGAME_ROM=<ABSOLUTE_PATH_TO_ROM>` then `cmake --build build && (cd build && ctest --output-on-failure)`. Done means it builds an empty program, ctest runs one placeholder test, and `git status` shows no ROM and nothing under `generated/`.
    BUDGET: Stop after 3 failed builds of the same error.
    IF BLOCKED: Stop. Paste the exact error. Do not switch compilers or install tools without asking.
    OTHER PROBLEMS: Write them in `notes/parking-lot.md`. Do not fix them.
    REPORT: 1) DONE, PARTIAL or BLOCKED. 2) Proof: the build tail and ctest output. 3) Files changed. 4) What is left. 5) New parking-lot items.
    Done whenThe one check command builds and runs ctest, and git status shows no ROM and no generated code.

    Proof this works: Chaotix Recompiled (CMake, Ninja, MSYS2, ROM-free CI), https://www.msys2.org/

  3. Build the answer key: three small interpreters

    Before you recompile anything, you need something to compare against. Chaotix wrote its own 68000, SH-2 and Z80 interpreters first. Each instruction's meaning lives in one shared header. The interpreter and the generated code both use it, so they cannot drift apart.

    • Put SH-2 instruction meanings in src/cpu/sh2_ops.h and 68000 ones in src/cpu/m68k_ops.h. Or link Musashi for the 68000, as knuckles-chaotix-recomp did.
    • In WSL, install a reference disassembler: sudo apt install binutils-sh-elf binutils-m68k-linux-gnu.
    • Check your SH-2 decoder against sh-elf-objdump on all 65,536 possible instruction words. knuckles-chaotix-recomp found 53,752 valid ones that all agreed.
    • Write unit tests from the CPU manuals. Chaotix started with 33.
    • Fake the boot in code. Copy the bytes the user header names into SDRAM. Set both SH-2 start addresses. Write M_OK and S_OK to the COMM ports.
    Prompt for Claude
    GOAL: Write the SH-2 reference interpreter for <GAME> with its decoder checked against sh-elf-objdump.
    READ FIRST: SCOPE.md, https://github.com/viciious/32XDK/blob/main/docs/sh1-sh2-cpu-core-architecture.md, the 'How correctness is established' section of https://github.com/Flinnz/knuckles-chaotix-recomp
    FENCE: Edit only `src/cpu/sh2_*`, `tests/cpu/` and `tools/validate_sh2.py`. Do not touch the 68000 or runtime code.
    CHECK: Run `python3 tools/validate_sh2.py` in WSL and `cmake --build build && (cd build && ctest --output-on-failure)`. Done means every one of the 65,536 words decodes the same as `sh-elf-objdump`, and all SH-2 unit tests pass.
    BUDGET: Stop after 4 hours, or 3 failed tries on the same instruction.
    IF BLOCKED: Stop. List the opcodes that disagree with both outputs.
    OTHER PROBLEMS: Write them in `notes/parking-lot.md`. Do not fix them.
    REPORT: 1) DONE, PARTIAL or BLOCKED. 2) Proof: the validator summary and ctest output. 3) Files changed. 4) Instructions not yet covered by a test. 5) New parking-lot items.
    Done whenThe SH-2 decoder agrees with sh-elf-objdump on all 65,536 words, and the interpreter boots the game to its first 32X picture.

    Proof this works: Chaotix Recompiled (shared semantics headers, 33 CPU unit tests, boot done in code), knuckles-chaotix-recomp (decoder checked against sh-elf-objdump)

  4. Map the code for both CPU types

    The recompiler must know where code is. You start from the 68000 reset vector and the two SH-2 start addresses. You follow calls, branches and jump tables. Then you record which addresses really run during play, to catch code reached through pointers.

    • Write an analyzer that walks code from every known entry point and decodes jump tables. Chaotix calls this rom_analyzer.
    • Map SH-2 code at its SDRAM address, not its ROM offset. sega-vr-disasm notes SH-2 $0600xxxx came from ROM offset $20000 plus xxxx in its game.
    • Prove the map by round trip: reassemble your listing and compare it with the ROM. knuckles-chaotix-recomp got all 3,145,728 bytes back.
    • Record coverage from play sessions into coverage/*.cov. These files hold addresses only, never ROM bytes.
    • Cross-check doubtful spots in Ghidra. Import the ROM as raw binary with language SuperH:BE:32:SH-2 at base $02000000.
    • Write notes/functions.csv with CPU, address, size, proof and a status column.
    Prompt for Claude
    GOAL: Build the first code map of <GAME> for the 68000 and both SH-2s.
    READ FIRST: SCOPE.md (user header values), https://github.com/matiaszanolli/sega-vr-disasm/blob/master/CLAUDE.md (ROM Address Mapping), notes/prior-work.md
    FENCE: Edit only `tools/rom_analyzer/`, `notes/functions.csv` and `coverage/`. Do not write the recompiler yet. Never write ROM bytes into the repo.
    CHECK: Run `<ANALYZER_EXE> --rom <ABSOLUTE_PATH_TO_ROM> --out out/map` then `python3 tools/roundtrip.py`. Done means the round trip rebuilds the mapped spans byte for byte, and every row in functions.csv names its proof (entry point, jump table or coverage hit).
    BUDGET: Stop after 3 hours, or 3 failed round trips on the same span.
    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: round-trip output and function counts per CPU. 3) Files changed. 4) Unclassified ranges. 5) New parking-lot items.
    Done whennotes/functions.csv lists every routine for both CPU types with its proof, and the round trip rebuilds the mapped bytes exactly.

    Proof this works: Chaotix Recompiled (rom_analyzer, coverage/*.cov), knuckles-chaotix-recomp (247 SH-2 and 4,491 68000 functions, whole-cartridge round trip), sega-vr-disasm (ROM-to-CPU address rules)

  5. First light: recompile and boot in lockstep

    Now the recompiler turns each block into C++, with the original instruction as a comment. Anything it cannot handle drops to the interpreter. That is slower but never wrong. You boot both builds side by side and compare every frame.

    • Generate: <RECOMP_EXE> --rom <ABSOLUTE_PATH_TO_ROM> --coverage coverage/boot.cov --out generated. Chaotix's tool is chaotix_recomp.
    • Build with the one check command. Never edit anything in generated/.
    • Run headless in lockstep, like chaotix_headless --rom <ABSOLUTE_PATH_TO_ROM> --frames 600 --lockstep. It compares registers, RAM, VRAM and the 32X frame buffer.
    • Save a screenshot of the first frame with --shot and look at it yourself.
    • Write the fallback count and the result in PROGRESS.md.
    Prompt for Claude
    GOAL: Recompile <GAME> and boot it to the SEGA logo with lockstep passing.
    READ FIRST: SCOPE.md, PROGRESS.md, notes/functions.csv, the Tools section of https://github.com/YuutaTsubasa/ChaotixRecompiled
    FENCE: Edit only `tools/recompiler/`, `src/runtime/dispatch*` and `CMakeLists.txt`. Never edit `generated/`. Do not stub any hardware access.
    CHECK: Run the one check, then `<HEADLESS_EXE> --rom <ABSOLUTE_PATH_TO_ROM> --frames 600 --lockstep --shot out/first.png`. Done means 600 frames with zero lockstep differences and a picture I can see.
    BUDGET: Stop after 4 failed builds, or 2 hours without a new frame.
    IF BLOCKED: Stop. Paste the first differing frame, register and value. Do not hide a difference by skipping it.
    OTHER PROBLEMS: Write them in `notes/parking-lot.md`. Do not fix them.
    REPORT: 1) DONE, PARTIAL or BLOCKED. 2) Proof: lockstep output and screenshot path. 3) Files changed. 4) Fallback count. 5) New parking-lot items.
    Done whenA headless run shows the first 32X picture, with zero lockstep differences for 600 frames.

    Proof this works: Chaotix Recompiled (lockstep, 3,200 frames boot to level), knuckles-chaotix-recomp (draws the SEGA logo; --xcheck)

  6. Close code gaps and RAM code

    Each fallback is code the recompiler did not cover. Some is real code in RAM: SH-2 code in SDRAM or cache, and 68000 jump stubs. You handle each gap at its cause. One unit is one fallback address.

    • After each run, list fallback addresses with their counts.
    • For each one, decide: missed function start, jump table target, RAM code, or self-modifying write.
    • For a missed start, add proof to the map or record a new coverage session.
    • For RAM code, check its bytes against the ROM at run time. Chaotix keeps a code epoch counter and drops to the interpreter when the code changes.
    • Track writes per function, not globally. Chaotix cut 4,049,824 checks to 75 over 3,000 frames this way.
    • Measure native coverage on a play session you did not use for coverage. Chaotix reached 99.7 to 100% for the 68000 and 100% for the SH-2.
    Prompt for Claude
    GOAL: Remove fallback address <ADDRESS> (<CPU>) from <GAME>'s recompiled build.
    READ FIRST: CLAUDE.md, `out/fallbacks.txt`, the listing at <ADDRESS>, notes/functions.csv
    FENCE: Edit only `tools/recompiler/`, `tools/rom_analyzer/`, `coverage/` and notes/functions.csv. Never edit `generated/`. Never add an address only because a log names it.
    CHECK: Regenerate, run the one check, then `<HEADLESS_EXE> --rom <ABSOLUTE_PATH_TO_ROM> --replay-input tests/inputs/<RUN> --lockstep`. Done means <ADDRESS> no longer falls back, lockstep has zero differences, and no new fallback appears.
    BUDGET: Stop after 3 failed tries or 45 minutes.
    IF BLOCKED: Stop. Paste the listing and say which kind of gap it is.
    OTHER PROBLEMS: Write them in `notes/parking-lot.md`. Do not fix them.
    REPORT: 1) DONE, PARTIAL or BLOCKED. 2) Proof: fallback counts before and after, lockstep output. 3) Files changed. 4) Kind of gap and cause. 5) New parking-lot items.
    Done whenFallbacks are zero on recorded sessions, except proven RAM code. Native coverage on a held-out session is written in PROGRESS.md.

    Proof this works: Chaotix Recompiled (code epoch counter, per-function write tracking, held-out coverage), knuckles-chaotix-recomp (44 missed 68000 addresses, VBlank handler reached by a RAM pointer)

  7. Build the 32X hardware layer and timing

    The runtime stands in for the 32X and the Genesis. It needs the 32X VDP modes, the COMM ports, PWM sound, timers and the Genesis chips. Timing between four CPUs is the hard part. Each fix gets a lockstep run behind it.

    • Run all CPUs off one clock and slice each scanline. Chaotix uses four slices. knuckles-chaotix-recomp uses sixteen.
    • Model the COMM ports exactly. Read the hazards in sega-vr-disasm's COMM_REGISTERS_HARDWARE_ANALYSIS.md first.
    • Fast-forward idle loops only when the result is exact. Chaotix does this for COMM polls and V-blank waits.
    • Run the Z80 up to the 68000's time before the 68000 reads sound state. That fixed Chaotix's silent sound test.
    • Use Nuked-OPN2 or your own YM2612 code for FM music, as knuckles-chaotix-recomp did.
    • At each milestone, compare a screenshot with PicoDrive in BizHawk 2.11.1. Your interpreter is not proof of real hardware.
    Prompt for Claude
    GOAL: Fix hardware behaviour <SYMPTOM> in the <GAME> runtime (<SUBSYSTEM>: VDP, COMM, PWM, Z80 or timers).
    READ FIRST: CLAUDE.md, `notes/hardware-facts.md`, the matching section of https://github.com/viciious/32XDK/blob/main/docs/32x-hardware-manual.md
    FENCE: Edit only `src/runtime/<SUBSYSTEM>*` and `notes/hardware-facts.md`. Never edit `generated/` or `tests/golden/`.
    CHECK: Run the one check with long tests on, then `<HEADLESS_EXE> --rom <ABSOLUTE_PATH_TO_ROM> --replay-input tests/inputs/<RUN> --lockstep --shot out/<RUN>.png`. Done means lockstep stays clean, the golden hashes still match, and the symptom is gone in the screenshot or WAV.
    BUDGET: Stop after 2 fix tries that do not change the result.
    IF BLOCKED: Stop. Cite the manual line you relied on and what the run showed instead.
    OTHER PROBLEMS: Write them in `notes/parking-lot.md`. Do not fix them.
    REPORT: 1) DONE, PARTIAL or BLOCKED. 2) Proof: check output and file paths. 3) Files changed. 4) The hardware rule, with its source. 5) New parking-lot items.
    Done whenA full level plays with sound, lockstep stays clean on every recorded session, and a person signed off the play test.

    Proof this works: Chaotix Recompiled (issue #50 Z80 fix, exact idle fast-forward, four slices per scanline), knuckles-chaotix-recomp (sixteen slices, Nuked-OPN2), sega-vr-disasm (COMM register hazards)

  8. Harden it and ship without the game

    You lock in what works so old fixes stay fixed. Then you package it so nobody gets the game from you. The player's own ROM is checked by hash. Chaotix ships no ROM bytes and no game data.

    • Save golden frame hashes for fixed inputs. Chaotix's long test checks 720 golden frame hashes over a 36,000-frame run.
    • Run random-input sweeps that must be bit-identical every time. Chaotix runs 33 of them before each release.
    • Keep enhancements off by default. Test that widescreen leaves the centre 320 pixels unchanged.
    • Build from a fresh clone and run every test with the ROM present.
    • Make the program check the player's ROM by SHA-1 before it runs.
    Prompt for Claude
    GOAL: Add a regression set and a ROM-free release check for <GAME>.
    READ FIRST: PROGRESS.md, `tests/golden/`, the Tests section of https://github.com/YuutaTsubasa/ChaotixRecompiled
    FENCE: Edit only `tests/`, `tools/release/` and `.github/`. Do not change game behaviour. Do not change any expected hash.
    CHECK: From a fresh clone run `cmake -S . -B build -G Ninja -DGAME_ROM=<ABSOLUTE_PATH_TO_ROM> -DGAME_LONG_TESTS=ON`, `cmake --build build`, then `(cd build && ctest --output-on-failure)` and `python3 tools/release/scan_rom_bytes.py dist/<ZIP> <ABSOLUTE_PATH_TO_ROM>`. Done means all tests pass 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 lockstep, golden and sweep test, and the release scan finds no ROM bytes.

    Proof this works: Chaotix Recompiled (139 tests per release, 33 random sweeps, no-game-data CI), https://yuuta-tsubasa.studio/ChaotixRecompiled/

Path B: Full decomp for porting

No full 32X decomp exists, so this path joins proven pieces. First you make a disassembly that rebuilds the ROM byte for byte, as two projects did. Then you rewrite each routine in C. You prove each one against recorded calls from your Path A interpreter. Last, you replace the CPUs and the COMM handshakes with plain code.

Method: byte-exact disassembly of all three CPUs, then C proven by lockstep tests, then a port (no 32X game has done this yet)

Pick this only after Path A works for your game. Path A gives you the interpreter, the coverage and the lockstep check that this path needs. Expect it to take most of a year.

  1. Check prior work and choose how to prove the C

    You learn what exists and how the game was made. If the source family is known, like Doom from Jaguar Doom, you can start from C. If not, you assume assembly and prove C by tests. Matching bytes needs the original compiler, and none is known for any 32X game.

    • Search for a disassembly or rebuild of your game. Read nukeykt/Doom32x-RE if your game is Doom.
    • Look at 20 SH-2 functions spread across the ROM. Note how they start and end, and any shared helper code like division or copy loops.
    • Look for any released source of the same game on another system.
    • Write the route in SCOPE.md: C checked by lockstep tests, or byte matching if you prove a compiler.
    Prompt for Claude
    GOAL: Decide how C for <GAME> will be proven, and record the Path B route in SCOPE.md.
    READ FIRST: SCOPE.md, Ghidra listings of 20 SH-2 and 20 68000 functions spread across the ROM, notes/prior-work.md
    FENCE: Edit only `SCOPE.md` and `notes/compiler.md`. No code. Do not download compilers.
    CHECK: Run `git status --short` and `grep -c '^verdict:' notes/compiler.md`. Done means only SCOPE.md and notes/compiler.md changed, the grep prints 1, and notes/compiler.md lists all 40 sampled functions with start and end patterns and any shared helper code with its address.
    BUDGET: 2 hours.
    IF BLOCKED: Stop. Say what evidence is missing. Do not name 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 whenSCOPE.md names the route, and notes/compiler.md holds the evidence for it.

    Proof this works: Doom32x-RE (C from Jaguar Doom source), sega-vr-disasm (SH-2 functions kept as assembly)

  2. Make a disassembly that rebuilds byte for byte

    The disassembly is the base for everything else. It must rebuild the exact ROM, so any wrong change fails at once. The Macroassembler AS knows the 68000, the SH-2 family and the Z80, so one tool covers the whole cartridge.

    • Install AS: download the Win32 binaries, or build the C sources in WSL.
    • Split the ROM into sections by CPU. Switch the AS CPU setting for each part: CPU 68000, CPU SH7600 for the SH-2, and CPU Z80.
    • Build like the Chaotix prototype disassembly: asl -xx -q -A -L -U -E -i . main.asm, then p2bin main.p <OUT>.32x. That project keeps its own asl and p2bin builds in tool/linux/, with extra p2bin options.
    • Or copy sega-vr-disasm's route. It assembles 68000 code with vasm. It assembles each SH-2 function with sh-elf-as --isa=sh2, places it with a linker script, and includes the bytes.
    • Write tools/build.py. It assembles, compares the SHA-1 with SCOPE.md, and exits 0 only on a match.
    • Prove the gate works: change one byte and confirm the build fails.
    • Keep unknown spans as raw bytes read from your ROM at build time. Never commit them.
    Prompt for Claude
    GOAL: Produce a disassembly of <GAME> that rebuilds the ROM byte for byte.
    READ FIRST: SCOPE.md, notes/functions.csv, http://john.ccac.rwth-aachen.de:8000/as/ (CPU list and manual), the make.sh of https://github.com/Ushupiuck/chaotix-1207-disasm
    FENCE: Edit only `src/`, `tools/` and `config/`. Never edit `config/expected.sha1`. Never commit ROM bytes.
    CHECK: Run `python3 tools/build.py`. Done means it exits 0 with a SHA-1 match, and `python3 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, per CPU. 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: sega-vr-disasm (first commit: byte-for-byte ROM match; byte match kept until tag v5.0.0), https://github.com/matiaszanolli/sega-vr-disasm/blob/master/Makefile (sh-elf-as --isa=sh2, linker scripts), sonicretro/chaotix (68000 and Z80 side builds bit-perfect), Ushupiuck/chaotix-1207-disasm (AS build script), http://john.ccac.rwth-aachen.de:8000/as/as_EN.html (CPU list names SH7600 as the SH-2)

  3. Turn every SH-2 byte into real source

    Two Chaotix disassemblies stopped here. They left the SH-2 code in a closed IDA database, so only one small driver was buildable. You move every SH-2 routine into assembler source. Runtime coverage tells you what is code and what is data.

    • Feed the coverage files from Path A into the disassembler. Executed bytes are code. Bytes only read are data.
    • Convert SH-2 routines from raw words to instructions, one section at a time. Rebuild after each one.
    • Before you move any SH-2 code, find every MOV.L @(disp,PC) that reads a nearby literal pool. Shared pools break silently.
    • Name data by kind: tables, strings, art, palettes, sound.
    • Track progress per CPU in notes/functions.csv.
    Prompt for Claude
    GOAL: Convert SH-2 section <SECTION> of <GAME> from raw words to readable source, keeping the byte match.
    READ FIRST: notes/functions.csv, the coverage report from `python3 tools/coverage.py --list 50`, the SH2 Patching Discipline rules in https://github.com/matiaszanolli/sega-vr-disasm/blob/master/CLAUDE.md
    FENCE: Edit only files for <SECTION> under `src/sh2/` and notes/functions.csv. Use the project tools to convert. Do not hand-edit other sections.
    CHECK: Run `python3 tools/build.py` and `python3 tools/coverage.py`. Done means the SHA-1 still matches and no executed SH-2 byte in <SECTION> is still raw data.
    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: build output and coverage counts before and after. 3) Files changed. 4) What is left. 5) New parking-lot items.
    Done whenNo executed byte in the recorded sessions is outside known code, for all three CPUs, and the build still matches.

    Proof this works: sega-vr-disasm (75 SH-2 functions as source while still byte-identical at tag v5.0.0; all 92 by tag v6.2.0, after it stopped matching), sonicretro/chaotix (SH-2 left in an IDA database)

  4. Write down how the CPUs talk

    A port must replace the COMM handshakes with normal code. So you need a full written map of who sends what, through which register, and who waits. sega-vr-disasm found that each SH-2 polls its own COMM register with its own command table.

    • List every read and write of the COMM registers on every CPU. Use rom_analyzer --refs-to <ADDRESS> style queries.
    • For each command, write its sender, receiver, register, data and the wait that follows.
    • Map SDRAM: which areas hold code, shared data and buffers.
    • Save it as notes/comm-protocol.md with a source line for every claim.
    • Confirm each command with a trace from your Path A headless build.
    Prompt for Claude
    GOAL: Document the COMM command <COMMAND> of <GAME> from sender to receiver.
    READ FIRST: `notes/comm-protocol.md`, https://github.com/matiaszanolli/sega-vr-disasm/blob/master/CLAUDE.md (COMM Register Safety), the listings that touch the COMM registers
    FENCE: Edit only `notes/comm-protocol.md` and `tools/trace/`. No game or runtime code.
    CHECK: Run `<HEADLESS_EXE> --rom <ABSOLUTE_PATH_TO_ROM> --trace comm --frames 600`. Done means every step you wrote appears in the trace, in order, and each claim names its listing address.
    BUDGET: 90 minutes.
    IF BLOCKED: Stop. Mark the claim unverified and say what trace 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 trace lines. 3) Files changed. 4) Unverified claims. 5) New parking-lot items.
    Done whenEvery COMM command is in notes/comm-protocol.md, each one confirmed by a trace.

    Proof this works: sega-vr-disasm (master polls COMM0, slave polls COMM2, documented dispatch tables), https://github.com/viciious/32XDK/blob/main/docs/32x-hardware-manual.md

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

    You prove the whole loop on one small routine. Pick a leaf, which calls nothing else. Record every call to it in the interpreter: registers in, memory writes out. Your C must give the same results for every call.

    • Add a capture mode to your Path A interpreter that logs each call to one address.
    • Write the C in game/c/<CPU>/<ADDRESS>.c. Use m2c with target sh2 for a first SH-2 draft. It reads GNU as syntax, like sega-vr-disasm's SH-2 files.
    • Replay every captured call against the C, built for your PC.
    • If you proved a compiler, also compile with sh-elf-gcc from marsdev and diff with asm-differ.
    • Write the one-unit check into notes/method.md so every worker uses it.
    Prompt for Claude
    GOAL: Write routine <ADDRESS> (<CPU>, <NAME>) of <GAME> as C and pass its capture replay.
    READ FIRST: the source file for <ADDRESS>, `notes/method.md`, `notes/comm-protocol.md` if the routine touches COMM
    FENCE: Create only `game/c/<CPU>/<ADDRESS>.c` and its prototype line. Do not edit the disassembly, captures or any check script. No inline assembly.
    CHECK: Run `python3 tools/replay.py <CPU> <ADDRESS> game/c/<CPU>/<ADDRESS>.c`. Done means every recorded call gives the same registers and memory writes.
    BUDGET: Stop after 5 meaningfully different tries.
    IF BLOCKED: Stop. Paste the first failing call: inputs, expected and actual.
    OTHER PROBLEMS: Write them in `notes/parking-lot.md`. Do not fix them.
    REPORT: 1) DONE, PARTIAL or BLOCKED. 2) Proof: the replay output. 3) Files changed. 4) What was learned for notes/method.md. 5) New parking-lot items.
    Done whenOne routine passes its capture replay, and the method is written down for workers.

    Proof this works: Chaotix Recompiled (reference interpreter as the answer key), knuckles-chaotix-recomp (--xcheck compares registers, target and every byte touched), https://github.com/matt-kempster/m2c

  6. Grind: every routine as C, leaves first

    Now you hand out routines in batches. Keep 68000 and SH-2 work in separate batches, since each has its own checks. Each worker has its own git worktree. A gate script must pass before a batch merges.

    • Write a queue script that lists the next routines, leaves first, with CPU and call count.
    • Give each worker one batch in its own worktree, with absolute paths to the ROM and tools.
    • Write a gate that builds, replays captures for the batch, and runs a short lockstep session.
    • Merge one batch at a time. Run the full gate again after each merge.
    • Track counts per CPU in PROGRESS.md: done, tested, left.
    Prompt for Claude
    GOAL: Write the next batch of <N> routines from `python3 tools/queue.py --cpu <CPU> -n <N>` as C, each passing its replay.
    READ FIRST: CLAUDE.md, `notes/method.md`, the queue output, one finished routine of the same CPU
    FENCE: Work only in this worktree. Create or edit only the `game/c/<CPU>/<ADDRESS>.c` files for this batch. Never edit check scripts, captures or other batches.
    CHECK: Run `python3 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 that routine, not the batch. Record it in `notes/hard.md` with the first failing call.
    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 for all three CPUs, and the gate passes on the merged branch.

    Proof this works: Doom32x-RE (file-by-file rework, commits per file), https://github.com/cdlewis/snowboardkids2-decomp (one unit per run; from ai-decomp-practice.md)

  7. Prove the whole game runs from the C

    Unit replays are evidence, not the verdict. Now you swap every recompiled routine for its C version inside the Path A runtime. Whole recorded sessions must still match the interpreter at every frame.

    • Add a build switch that calls the C routine instead of the generated one, routine by routine.
    • Turn all C routines on and run every recorded session in lockstep.
    • Prove the check can fail: break one routine 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 hand-written C and equals the reference.
    READ FIRST: PROGRESS.md, the gate script, the list of recorded sessions
    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 differing 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 whenEvery recorded session matches with all C routines on, the corrupt test fails, and a person signed off a full play.

    Proof this works: Chaotix Recompiled (36,000-frame lockstep run, golden hashes)

  8. Port: remove the CPUs and the handshakes

    The C still assumes four CPUs and big-endian memory. You turn COMM handshakes into normal calls or threads. You move memory to the host's byte order. The frame buffer becomes a texture, and PWM becomes normal audio.

    • Put all game memory access behind one header, such as src/mem.h. Fail the build if code touches raw memory elsewhere.
    • Replace each COMM command from notes/comm-protocol.md with a direct call or a queue between two threads.
    • Swap byte order at the edges: ROM data reads, save files and hardware ports.
    • Copy the finished frame buffer to an SDL texture each frame. Send PWM samples to SDL audio.
    • Keep the Z80 sound driver on an interpreter with a YM2612 core, or rewrite it in C later.
    • Compare the native build with the Path A build at every frame of the test runs.
    Prompt for Claude
    GOAL: Replace COMM command <COMMAND> in the <GAME> C port with a direct call, keeping every frame the same.
    READ FIRST: `notes/comm-protocol.md` entry for <COMMAND>, `src/mem.h`, CLAUDE.md
    FENCE: Edit only the files the port queue lists for <COMMAND> and `src/mem.h`. Do not change game logic.
    CHECK: Run `<NATIVE_COMPARE_COMMAND>`. Done means the native build equals the Path A build on every test run, and the port queue no longer lists <COMMAND>.
    BUDGET: Stop after 3 failed tries on the same mismatch.
    IF BLOCKED: Stop. Paste the first differing frame and the value 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 whenNo CPU emulation or COMM polling is left, and the native build matches the Path A build on every test run.

    Proof this works: d32xr (one C code base split across both SH-2s for rendering), Chaotix Recompiled (same runtime built for Windows, macOS, Android and iOS)

  9. Harden the port for other systems

    A port needs C that acts the same under any compiler. So you build it two ways and compare. You also run a sanitizer, a compiler mode that catches undefined behaviour. Then you build for each new system and rerun the golden runs.

    • Record golden runs: per-frame hashes of game RAM, frame buffer and sound.
    • Build again with a second compiler, such as Clang next to GCC, and compare every frame.
    • Build with -fsanitize=undefined and replay the golden runs.
    • Build for each target system and 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: PROGRESS.md, `tests/golden/`, the platform table in https://github.com/YuutaTsubasa/ChaotixRecompiled
    FENCE: Edit only the build files and `tools/golden.py`. Do not change game code to pass a check; file each failure as a unit.
    CHECK: Run `python3 tools/golden.py --build gcc`, `python3 tools/golden.py --build clang` and `python3 tools/golden.py --build 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: Chaotix Recompiled (GCC, Clang and MSVC build warning-free; lockstep bit-identical on Apple M1)

Tools you need

ToolWhat it doesHow to get itHow we know
MSYS2 (MinGW64 GCC, CMake, Ninja)
Required Windows
A Windows build setup with GCC and package manager pacman. Chaotix Recompiled builds its Windows version this way.
Path A Path B
Run the installer `msys2-x86_64-20260927.exe` from msys2.org. In the MinGW64 shell: `pacman -S mingw-w64-x86_64-{gcc,cmake,ninja,sdl3,sdl3-ttf} git python`msys2.org home page and packages.msys2.org (sdl3 3.4.20-1, sdl3-ttf 3.2.2-3) read 2026-11-13; pacman line from the Chaotix Recompiled README read 2026-11-13
SDL3
Required both
A library for the window, input, sound and controllers. Chaotix Recompiled's front end uses SDL3 and SDL_ttf.
Path A Path B
Comes with the MSYS2 line above (`mingw-w64-x86_64-sdl3`). Or build from source with `-DCHAOTIX_SDL3_SOURCE_DIR` style optionsreleases read 2026-11-13 (release-3.4.20, 2026-11-03, Zlib); used by Chaotix Recompiled; SDL2 used by VRDE and knuckles-chaotix-recomp
binutils-sh-elf and binutils-m68k-linux-gnu
Required WSL/Linux
GNU tools for the SH-2 and the 68000. Use sh-elf-objdump to check your own decoder, instruction by instruction. sh-elf-as assembles SH-2 source, as sega-vr-disasm does.
Path A Path B
In WSL: `sudo apt install binutils-sh-elf binutils-m68k-linux-gnu`packages.ubuntu.com pages for Ubuntu 24.04 (noble) read 2026-11-13: binutils-sh-elf 2.40+2 file list has sh-elf-as, sh-elf-ld, sh-elf-objcopy and sh-elf-objdump; the 68000 tool is named m68k-linux-gnu-objdump. knuckles-chaotix-recomp validates its decoders against sh-elf-objdump and m68k-elf-objdump; sega-vr-disasm's Makefile uses sh-elf-as and sh-elf-objcopy
PicoDrive
Helpful both
An open-source emulator for the Genesis, Sega CD and 32X. It is the outside reference for real 32X behaviour. VRDE built its whole project on it.
Path A Path B
Download `PicoDrive-win32-2.05-7cbcd41.zip` from release v2.05. Or in WSL: `git clone --recurse-submodules https://github.com/irixxxx/picodrive`, then `./configure --platform=generic` and `make`. The generic build needs SDL 1.2README, release v2.05 (2025-05-31), .gitmodules (5 submodules) and configure (generic platform needs SDL) read 2026-11-13; SDL 1.2 from the VRDE card; used by VRDE (vendored), sega-vr-disasm (testing and profiling). Licence is non-commercial (VRDE card).
BizHawk
Helpful both
A multi-system emulator with Lua scripting and input movies. Its 32X core is PicoDrive. Use it to record reference sessions and screenshots.
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`. If it says a part is missing, install the MSVC++ redistributable its README linksREADME core table (Sega 32X: PicoDrive) and release 2.11.1 (2026-05-01) read 2026-11-13. No reviewed 32X project names it.
Ghidra
Helpful both
A free reverse engineering tool. It has SH-2 and 68000 decoders built in. There is no 32X loader, so you load the ROM as raw bytes.
Path A Path B
Download Ghidra 12.2 from the releases page and install JDK 25. If you will use GhidraMCP, get 12.1.4 instead, the version its plugin names. Import the ROM as Raw Binary with language `SuperH:BE:32:SH-2` at base `$02000000`README (JDK 25), release Ghidra_12.2_build (2026-10-22) and Ghidra/Processors/SuperH/data/languages/superh.ldefs (SH-1, SH-2, SH-2A) read 2026-11-13; GitHub search found no 32X loader. Doom32x-RE's FUN_ and DAT_ names suggest Ghidra (card, inferred).
Capstone
Helpful both
A disassembly library with 68000 and SuperH support. Python tracers and analyzers can use it to decode instructions.
Path A Path B
`pip install capstone`README and the arch folder at tag 5.0.6 (M68K and SH present) read 2026-11-13; PyPI shows 5.0.9. No reviewed 32X project names it; knuckles-chaotix-recomp wrote its own decoders.
Musashi
Helpful both
A 68000 interpreter written in C. Link it as your 68000 reference instead of writing your own.
Path A
`git submodule add https://github.com/kstenerud/Musashi third_party/musashi`. Build its `m68kmake` first; it writes `m68kops.c` and `m68kops.h`. Then add `m68kcpu.c`, `m68kops.c` and `m68kdasm.c` to your buildREADME read 2026-11-13 (m68kmake generates m68kops.c from m68k_in.c; host supplies m68k_read_memory_* and m68k_write_memory_*); used by knuckles-chaotix-recomp as a pinned submodule
Nuked-OPN2
Helpful both
A model of the YM3438, the later version of the Genesis YM2612 FM sound chip. It was built from die shot study. It plays the Genesis FM music.
Path A Path B
`git submodule add https://github.com/nukeykt/Nuked-OPN2 third_party/nuked-opn2`repo read 2026-11-13 (LGPL-2.1); used by knuckles-chaotix-recomp
The Macroassembler AS (`asl`, `p2bin`)
Required both
A cross assembler that knows the 68000, the SH7600 family (SH-2) and the Z80. One tool can rebuild a whole 32X cartridge.
Path B
Download the Win32 binaries or the C sources from the download page. Latest build 1.42 Bld 316.home, download and CPU list pages and the English manual (lists SH7600 (SH2), shows the cpu directive) read 2026-11-13; used by Ushupiuck/chaotix-1207-disasm (asl, p2bin) and sonicretro/chaotix for the Z80 driver (asw, p2bin)
vasm
Helpful both
A portable assembler for the 68000 and Z80. It has no SH-2 module. sega-vr-disasm pairs it with sh-elf-as for the SH-2 code.
Path B
Download the multi-target Windows x64 or Linux x64 binaries of vbcc/vasm/vlink from the vasm page (dated 2021-03-19). sega-vr-disasm's `make tools` builds `vasmm68k_mot` from a source link that returned 404 on 2026-11-13vasm page read 2026-11-13 (CPU list has M680x0 and Z80, no SuperH; binaries dated 19/03/2021); sega-vr-disasm Makefile read 2026-11-13 (vasmm68k_mot, source URL http://sun.hasenbraten.de/vasm/release/vasm.tar.gz returned 404)
m2c
Helpful both
A decompiler that turns assembly into a C draft. It has an sh2 target for big-endian SuperH. It reads GNU as syntax.
Path B
`pip install "m2c @ git+https://github.com/matt-kempster/m2c.git"`README read 2026-11-13 (targets include sh2; compilers ido, gcc, mwcc). No reviewed 32X project used it.
asm-differ (`diff.py`)
Helpful both
An assembly diff viewer for matching work. It supports SH2 and m68k. You need it only if you prove the game's compiler.
Path B
`pip install 'asm-differ @ git+https://github.com/simonlindholm/asm-differ.git'`README read 2026-11-13 (SH2, SH4, m68k listed). No reviewed 32X project used it.
marsdev
Helpful WSL/Linux
Builds GCC for the 68000 and the SH-2, so you can compile C for the 32X itself. Use it to test a compiler guess.
Path B
In WSL: `sudo apt install build-essential texinfo wget`, `git clone https://github.com/andwn/marsdev --recurse-submodules`, then `make m68k-toolchain-newlib` and `make sh-toolchain-newlib`README read 2026-11-13 (sh toolchain needed for 32X). d32xr builds with the related 32XDK sh-elf GCC.

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 clean lockstep run, a zero fallback count or a SHA-1 match.

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

systematic-debugging

skills.sh

No fix until the cause is found. That matches the research-first rule in sega-vr-disasm's CLAUDE.md.

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

static-recomp-scope-plan

skills.sh

Makes you write the exact ROM, the test scenes and the pass numbers before work starts. Use it for SCOPE.md.

npx skills add bgyss/switchrecomp --skill static-recomp-scope-plan -a claude-code

ghidra-headless

skills.sh

Runs Ghidra with no window and saves functions to files. A raw 32X ROM needs your import settings first. On Windows, set GHIDRA_HOME yourself.

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

orchestrator

skills.sh

Runs many Path B workers, each in its own worktree, with a check before every merge. Describe your capture replay gate in AGENTS.md first.

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. Tell it to save inside your project, not the temp folder.

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

GhidraMCP

MCP server

Lets Claude list, read and rename functions in a Ghidra project. Its Ghidra plugin is built for Ghidra 12.1.4, so use that version with it. No reviewed 32X project used an MCP server. The Chaotix disassembly used an IDA database instead.

claude mcp add ghidra -- uvx --from git+https://github.com/LaurieWired/GhidraMCP ghidra-mcp --ghidra-server http://127.0.0.1:8080/

s32x-lockstep-divergence (write your own)

write-your-own

Chaotix Recompiled reviews every behaviour change against a lockstep run. A skill makes each session follow the same steps.

A `.claude/skills/s32x-lockstep-divergence/SKILL.md` that: 1) replays one recorded session headless with `--lockstep`; 2) reads the first differing frame, CPU and value; 3) dumps registers, SDRAM, COMM registers and frame buffer from both sides at that frame; 4) traces that frame only; 5) writes frame, CPU, cause and fix to `notes/divergence-log.md`. Rule: no code change until the first wrong write is named.

s32x-comm-audit (write your own)

write-your-own

sega-vr-disasm runs a separate auditor for every COMM, SH-2 or expansion change, after a COMM7 broadcast crashed the game.

A `.claude/skills/s32x-comm-audit/SKILL.md` for a fresh session that reads one proposed change touching COMM registers, SDRAM layout or SH-2 code. It checks for read-during-write, missing dummy reads after SH-2 writes, cache-through addresses, shared literal pools and combined patches. It answers APPROVED or BLOCKED with the line that breaks a rule.

Claude setup for this system

Cut 32X work by CPU and by recorded session. On Path A, one unit is one fallback address, one lockstep difference or one hardware rule. On Path B, one unit is one routine on one CPU, or one COMM command. Keep 68000 and SH-2 units in separate batches. Every unit has one check: zero lockstep differences, a zero fallback count, a SHA-1 match or a passing capture replay.

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 by SHA-1. Read the 32X user header. Find any disassembly or recomp of the game. Choose Path A or Path B and 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 projectSet up MSYS2, CMake, the ROM-free CI, the one check command, and binutils-sh-elf in WSL.Opus 5.5
effort medium
Sonnet 5.5
effort medium
Study the game fileMap code for the 68000 and both SH-2s, decode jump tables, record coverage, and document the COMM protocol and SDRAM layout.Opus 5.5
effort high
Opus 5.5
effort medium
Raise to high for function edges and data layouts. A mistake there spreads.
Bulk unitsFallback addresses with clear proof, SH-2 sections to convert to source, and leaf routines to rewrite as C. One small unit per worker.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 unitsRAM-resident and self-modifying code, routines that touch COMM handshakes, 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 runtimeThe CPU interpreters, the 32X VDP modes, COMM ports, PWM, the Z80 and YM2612, timers, and keeping four CPUs in time.Opus 5.5
effort high
Opus 5.5
effort medium
Raise to high for timing and graphics bugs.
Find a bugFind the first lockstep difference, then the first wrong write. Prove the cause with dumps from both sides 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 the full ctest with the ROM. It hunts for edited golden hashes, stubbed handshakes, hidden fallbacks 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, the function ledger, notes/comm-protocol.md, the divergence log 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 or 3 sessions at once, each on its own fallback or difference, each in its own worktree and build folder. Keep one writer per runtime file, since the CPU and timing code is shared. Path B: 4 to 6 routine workers plus one lead, split by CPU. Run a fresh verify session before each merge.

Max 5x (now called Max)

Path A: 1 session, sometimes 2. Path B: 2 routine workers plus the lead. Send hard units and timing bugs to one session at a time. Update the docs once 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 anything in `generated/`. Fix the recompiler, the analyzer or the runtime, then regenerate.
- The one check is `cmake --build build && (cd build && ctest --output-on-failure)` with `GAME_ROM` set. No pasted output means not done.
- Do not guess hardware behaviour. Cite `docs/32x-hardware-manual.md` or a trace for every rule you add.
- Code the recompiler cannot handle falls back to the interpreter. Report the fallback count in every status.
- Never change a golden hash or lockstep baseline alone. A change needs its reason in the same commit and my approval.
- SH-2 addresses at `$0600xxxx` are SDRAM. Never read them as ROM offsets. The SH-2 cannot reach 68000 work RAM.
- Before changing COMM code, read `notes/comm-protocol.md`. Read-during-write is undefined, and SH-2 writes are buffered.
- Test SH-2 changes one at a time. Check shared literal pools before you overwrite any SH-2 word.
- If a second try fails for a related reason, stop coding and read the docs. Do not try random addresses.
- The ROM lives at `<ABSOLUTE_PATH_TO_ROM>`. Never commit it, its bytes, or generated code. Coverage files hold addresses only.
- In a worktree, use absolute paths for the ROM and tools, and use your own build folder.
- 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. Search for the address, then read a few lines.
  • Give Claude the first differing frame and the two state dumps, not the whole lockstep log.
  • Keep hardware facts in notes/hardware-facts.md and point to sections. The full manual is long.
  • Use separate sessions for 68000 work and SH-2 work, so only one instruction set is in context.
  • Add Read deny rules for generated/**, build/** and trace folders.
  • 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 32X game.

Prompt for Claude
GOAL: Tell me if <GAME> (SHA-1 <SHA1>) suits Path A, Path B, or both, and draft SCOPE.md.
READ FIRST: bytes $100-$1FF and $3C0-$3EF of `<ABSOLUTE_PATH_TO_ROM>`, https://github.com/YuutaTsubasa/ChaotixRecompiled (README), https://github.com/viciious/32XDK/blob/main/docs/32x-hardware-manual.md
FENCE: Create only `SCOPE.md` and `notes/prior-work.md`. No code. Do not copy the ROM.
CHECK: Run `sha1sum <ABSOLUTE_PATH_TO_ROM>` and `python3 -c "d=open('<ABSOLUTE_PATH_TO_ROM>','rb').read();print(len(d),d[0x100:0x110],[hex(int.from_bytes(d[o:o+4],'big')) for o in range(0x3D4,0x3F0,4)])"`. Done means SCOPE.md matches that output and lists size, `SEGA 32X` system field, title, region, the user header values, prior work with links, a guess at C or assembly with 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, the SH-2 load addresses and links to prior work. It says plainly that no shared 32X recompiler exists.

Map the binary

After setup, to build the function ledger for all three CPU types.

Prompt for Claude
GOAL: Produce `notes/functions.csv` for <GAME>: every routine start with CPU, address, size, proof and status.
READ FIRST: SCOPE.md, the analyzer output in `out/map`, the coverage files in `coverage/`
FENCE: Edit only `notes/functions.csv` and scripts in `tools/map/`. Do not edit the recompiler.
CHECK: Run `python3 tools/map/check_ledger.py notes/functions.csv`. Done means no overlaps, every row names a CPU and a proof, and SH-2 rows use SDRAM or cartridge addresses, never raw file offsets.
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: checker output and counts per CPU. 3) Files changed. 4) Unclassified ranges. 5) New parking-lot items.

A good answer: A ledger with thousands of rows split by CPU, each with a proof, and a short list of ranges still unknown.

Unit loop, Path A: one fallback or one difference

For each code gap or lockstep difference during the grind.

Prompt for Claude
GOAL: Fix <UNIT> (fallback at <ADDRESS> on <CPU>, or the first lockstep difference in session <RUN>) in <GAME>.
READ FIRST: CLAUDE.md, `notes/divergence-log.md`, the listing at <ADDRESS>
FENCE: Edit only the recompiler, analyzer or runtime file named in the unit, plus the notes. Never edit `generated/` or `tests/golden/`.
CHECK: Run the one check with `GAME_ROM` set. Done means the unit is gone, lockstep is clean on every recorded session, and the fallback count did not rise.
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> (<CPU>, <NAME>) as C and pass its capture replay.
READ FIRST: the source file for <ADDRESS>, `notes/method.md`, one finished routine for the same CPU
FENCE: Create only `game/c/<CPU>/<ADDRESS>.c` and its prototype. No inline assembly. No edits to checks, captures or other routines.
CHECK: Run `python3 tools/replay.py <CPU> <ADDRESS> game/c/<CPU>/<ADDRESS>.c`. Done means every recorded call gives the same registers and memory writes.
BUDGET: 5 meaningfully different tries.
IF BLOCKED: Stop. Paste the first failing call with inputs, expected and actual.
OTHER PROBLEMS: Write them in `notes/parking-lot.md`. Do not fix them.
REPORT: 1) DONE, PARTIAL or BLOCKED. 2) Proof: the replay 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. Never mix 68000 and SH-2 units in one worker. 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 hangs, crashes, 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`, `notes/comm-protocol.md`, recorded session <RUN>, CLAUDE.md
FENCE: Read and run only. You may add logging under `tools/debug/`. No edits to the game, runtime or golden files.
CHECK: Run `<HEADLESS_EXE> --rom <ABSOLUTE_PATH_TO_ROM> --replay-input tests/inputs/<RUN> --lockstep` and paste the first difference it prints. Done means you name the first differing frame, the CPU, 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, one COMM handshake or one timing 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 the one check yourself with `GAME_ROM` set. Then look for: edits under `generated/`, changed golden hashes or baselines, skipped or shortened lockstep runs, stubbed COMM waits, fallback counts that went up, enhancements left on, ROM bytes in tracked files, 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`, `out/fallbacks.txt`, `notes/divergence-log.md`, `notes/parking-lot.md`
FENCE: Edit only `PROGRESS.md`. No code.
CHECK: Run the one check and paste its 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 per CPU: routines done and left, fallbacks, sessions passing lockstep, open differences. 3) What changed today. 4) Top three risks. 5) Parking-lot items for me to decide.

A good answer: A short table of counts per CPU, 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 `git log -1 --oneline`. Done means notes/handoff.md names every file git status lists and 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: Rerun the last check command written in `notes/findings.md` for <UNIT> and paste its output. Done means you state one of: CAN DO (with the plan and its check), CANNOT DO NOW (with the missing piece, such as an unknown compiler or a hardware fact nobody has documented), 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 a fix needs work outside the fence, such as in the shared CPU cores or the timing loop.

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`. Done means it shows only `notes/scope-requests.md` changed, and the request names the files outside the fence, why the change is needed with evidence, what else it could break (all four CPUs share the clock), 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 and how to test it. 5) New parking-lot items.

A good answer: A short request that you can answer yes or no, with the timing risk 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.Never edit generated/. Fix the recompiler or runtime and regenerate. Add a PreToolUse hook that blocks writes there.
Claude skips a COMM wait or fakes a ready signal to get past a hang.Never stub a handshake. Model the COMM ports as the manual says. Fast-forward a wait loop only when lockstep proves the result is the same.
Claude tries address after address, hoping one works. sega-vr-disasm calls this address shopping and bans it.Do not guess addresses. Cite the listing, the manual or a trace. After a second related failure, stop and read.
Claude lets the interpreter run missed code and calls the recomp finished.Report the fallback count in every status. Measure native coverage on a session that was not used for coverage.
Claude updates a golden hash so a failing test passes.Never change a golden hash or baseline alone. The reason goes in the same commit, and I approve it.
Claude bundles several SH-2 patches and cannot tell which one broke the game.Test each SH-2 change alone. Check shared literal pools first. Combine changes only after each one passed.
Claude treats its own interpreter as the truth about the hardware.At each milestone, compare a screenshot or trace with PicoDrive. Write any disagreement in notes/hardware-facts.md.
Claude says a fix worked because the build passed.Done means the ctest output with GAME_ROM set is pasted. A gameplay fix also needs my play-test sign-off.

Problems other projects hit

Code that only exists in RAM was missed. SH-2 code ran from SDRAM and cache memory. The 68000 wrote jump stubs into work RAM. One VBlank handler was reached only through a pointer in RAM.

Fix: Check RAM code against ROM bytes at run time. Drop to the interpreter when it changes. Compare executed addresses with a reference emulator log.

Seen in: Chaotix Recompiled, knuckles-chaotix-recomp

Writes near SH-2 code triggered checks of every translated function. That came to 4,049,824 checks over 3,000 frames.

Fix: Track writes per function. Chaotix then needed 75 checks for the same run.

Seen in: Chaotix Recompiled

Idle loops polled COMM ports and waited for V-blank. Run as written, they wasted most of each frame.

Fix: Fast-forward each wait loop to its exact end. Prove with lockstep that the results did not change.

Seen in: Chaotix Recompiled

The sound test was silent. The Z80 had not caught up to the 68000's time when the game paused.

Fix: Run the Z80 up to the 68000's time before the 68000 touches sound state.

Seen in: Chaotix Recompiled

COMM register code broke in odd ways. Reading during a write is undefined, SH-2 writes are buffered, and sending a command to COMM7 crashed the game.

Fix: Use handshakes on both sides, do a dummy read after SH-2 writes, use cache-through addresses, and have a fresh session audit every COMM change.

Seen in: sega-vr-disasm

Wrong address maps wasted days. Reading SDRAM addresses as ROM offsets gave 68000 code instead of SH-2 code. Three tries assumed the SH-2 could reach 68000 RAM, and it cannot.

Fix: Write the address rules for each CPU in CLAUDE.md. Check every claim against the memory map in the hardware manual.

Seen in: sega-vr-disasm

The SH-2 half of a disassembly stayed in a closed IDA database for years. Only one small driver was ever buildable.

Fix: Put every SH-2 routine into assembler source early. AS assembles the SH-2, 68000 and Z80, so one build covers it all.

Seen in: sonicretro/chaotix, KatKuriN/incomplete_chaotix_disassembly

CI has no ROM, so lockstep and golden tests run only on the developer's PC.

Fix: Run the full test set with the ROM before every merge. Have a fresh verify session rerun it.

Seen in: Chaotix Recompiled

Wrapping PicoDrive made the whole project non-commercial. Its own build needed SDL 1.2, so the file list was copied by hand and may drift.

Fix: Read an emulator's licence before you build on it. Mark every edit to vendored code with a tag such as VRDE:.

Seen in: Virtua Racing Deluxe Enhanced

A reconstruction had no README, no build files and no check. Nobody can tell what builds, and it stalled after five weeks.

Fix: Write the build command and the check on day one, even if the check does little yet.

Seen in: Doom32x-RE

How long it takes

Max 20x (now called Ultra)Max 5x (now called Max)One person, no AIHow sure
Path A: running on PC3 to 10 weeks7 to 25 weeks1 to 3 yearsmedium confidence
Path B: full decomp4 to 11 months9 months to 2.5 years3 to 6 yearslow confidence

Read this first

These numbers assume one medium game, your first 32X project, and one person giving 10 to 15 hours a week. Path A is medium confidence. Two dated recomps made with AI both moved fast. But the Chaotix repo hides work done before its first commit. The other recomp reached a finished level in a week, then stopped. Both are the same game. Path B is low confidence. No 32X game has a full decomp. So it leans on one disassembly made with Claude, and on Genesis projects. The no-AI numbers are low confidence too. No 32X recomp from before AI agents exists. The one long hand-made disassembly is still unfinished after fourteen years.

What makes it faster or slower

  • Whether a disassembly exists. Only Chaotix and Virtua Racing Deluxe have one.
  • How much code runs from RAM, and how the two SH-2s split work. Each pattern needs its own handling.
  • How tightly the CPUs are timed through the COMM ports. Loose games boot fast. Tight ones need many timing fixes.
  • Whether you write your own interpreters or reuse Musashi and PicoDrive code. The answer key comes before everything else.
  • 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
Chaotix RecompiledFirst commit 2026-09-19 already held the recompiler, runtime and SDL3 front end. Milestone 3 came the same day. Playable v0.1.0 on 2026-09-24. Release 0.15.12 on 2026-10-30. 2 contributors. Handbook says 228 of 264 commits name Claude as co-author.source
knuckles-chaotix-recompFirst commit 2026-08-09. Last commit 2026-08-16 (103 commits). By then its README says the game ran title, attract mode, a finished level and a special stage, with 200,000 headless frames and no stall. 1 contributor. README says it used LLMs. No release; no commits since.source
sega-vr-disasmFirst commit 2026-01-06 already had a byte-for-byte ROM match. Tag v5.0.0 on 2026-02-07 was still byte-identical, with 75 SH-2 functions as source. After that it changed the game on purpose. All 92 SH-2 functions were in its build by tag v6.2.0 on 2026-03-01. README credits Claude Code with human guidance.source
Virtua Racing Deluxe EnhancedFirst commit and first release both 2026-10-15. 4 commits, 1 contributor. README says AI-assisted development. Emulator-based, not a decomp.source
Doom32x-RE33 commits from 2024-07-26 to 2024-08-31, 1 contributor, no AI stated. Started from the released Jaguar Doom source. Stalled, build state unknown.source
sonicretro/chaotixFirst commit 2011-02-23. 4 contributors, no AI stated. README dated 2025-07-27 still calls it very incomplete: 68000 and Z80 bit-perfect, SH-2 mostly not.source

Projects reviewed (10)

RecompLauncher lists fewer than 10 games for the 32X: just 2 projects for 2 games. One more project came from outside it (Doom32x-RE). Only 3 distinct 32X games have any public source project anywhere, so fewer than 10 real projects exist. To reach 10 entries, 7 more projects for the same three games were reviewed live with the GitHub API.

What each project teaches

DOOM 32X: Resurrection

Doom (32X)

From outside RecompLauncherotherin progress
32XDK (sh-elf and m68k-elf GCC)makeVSCode dev container

Started: 2016-05. First release: none. Code: C.

AI: none stated

Lesson: A new 32X build from released Jaguar source and Calico DOOM, not a decomp of the cartridge. It shows C code can split rendering across both SH-2s.

sega-vr-disasm

Virtua Racing Deluxe

From outside RecompLauncherotherin progress
vasm (68000)sh-elf-as and sh-elf-objcopy (SH-2)Python disassemblers (m68k_disasm.py, sh2_disasm.py)PicoDrive libretro profilermake

Started: 2026-01. First release: none. Code: 68000 and SH-2 assembly, Python.

AI: yes: README credits 'Claude Code with human guidance'; repo has CLAUDE.md; 97 of the latest 100 commits carry a Claude co-author line Agent files: CLAUDE.md, agents/README.md, agents/worker.md, agents/auditor.md.

Lesson: A byte-exact disassembly plus written hardware rules let Claude work safely on SH-2 code. It later dropped the byte match to change the game, so pick a matching tag. Its CLAUDE.md is the best 32X rule set found.

Chaotix Recompiled

Knuckles' Chaotix

Top rated on RecompLauncherstatic recompplayable
chaotix_recomprom_analyzerchaotix_headlessCMakeNinjaMSYS2 GCC or MSVC

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

AI: yes: the catalog flags it, and the Recompiled Handbook says 228 of 264 commits name Claude as co-author

Lesson: Write the interpreters first and share instruction meanings with the generated code. Then a lockstep run can prove every frame.

Knuckles' Chaotix Disassembly

Knuckles' Chaotix

From outside RecompLauncherotherin progress
asm68kasmshasw and p2bin (AS)fixheadrIDA Pro 7.7 database

Started: 2011-02. First release: none. Code: 68000, SH-2 and Z80 assembly.

AI: none stated

Lesson: The 68000 and Z80 side rebuilds bit-perfect. The SH-2 side stayed in an IDA database, so most of the game is still not source.

Virtua Racing Deluxe Enhanced

Virtua Racing Deluxe

Top rated on RecompLauncheremulator-basedplayable
PicoDrive (vendored)sh2disvrdetracemakellvm-mingwSDL2

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

AI: yes: README credits say 'AI-assisted development.'

Lesson: If you only want better 3D, catch the commands the 68000 sends the SH-2s and redraw them on the GPU. It is fast, but it is not a decomp.

Doom32x-RE

Doom (32X)

From outside RecompLaunchernon-matching decompstalled
unknown (no build files)Ghidra-style names (inferred)

Started: 2024-07. First release: none. Code: C.

AI: none stated

Lesson: Starting from a related released source saves work. Without a build command and a check, nobody can tell how far it got.

Virtua Racing Deluxe native runtime

Virtua Racing Deluxe

From outside RecompLauncherstatic recompplayable
own AOT recompiler (not in the repo)

Started: 2026-11. First release: 2026-11. Code: unknown (binary release only).

AI: none stated

Lesson: It pins one cartridge revision by SHA-256 and size. The repo holds no recompiler source, so you cannot learn the method from it.

Incomplete Chaotix Disassembly

Knuckles' Chaotix

From outside RecompLauncherotherstalled
asm68kasmshIDA Pro 7.7 database

Started: 2024-08. First release: none. Code: 68000, SH-2 and Z80 assembly.

AI: none stated

Lesson: An earlier copy of the Sonic Retro work. Its README notes the SH-2 driver is patched by hand and needs a linker to place it properly.

Knuckles Chaotix Recomp

Knuckles' Chaotix

From outside RecompLauncherstatic recompstalled
Python front ends (disasm.py, recompile.py)MusashiNuked-OPN2sh-elf binutilsSDL2make

Started: 2026-08. First release: none. Code: C and Python.

AI: yes: README says the author used 'many LLM dark spells'

Lesson: Check the decoder against sh-elf-objdump on every possible word, and prove the map by reassembling the whole cartridge. It reached a finished level in its first week.

Chaotix 1207 prototype disassembly

Knuckles' Chaotix (December 1994 prototype)

From outside RecompLauncherotherstalled
AS (asl)p2binIDA database

Started: 2025-10. First release: none. Code: Assembly.

AI: none stated

Lesson: It builds with the AS assembler on Linux. Its file list also shows a .32x image in the repo root; keep game images out of yours.

What we could not check

  • Which compiler any shipped 32X game used. The PSY-Q Development System (32X) entry was seen only in a search summary, because Sega Retro blocks automated readers.
  • Whether PicoDrive or BizHawk need a 32X BIOS file for 32X games. Do not download one; Chaotix Recompiled shows a game can boot without it.
  • Whether the GhidraMCP server works with Ghidra 12.2. Its README names Ghidra 12.1.4.
  • Whether the official AS build assembles a whole 32X cartridge. The Chaotix prototype disassembly ships its own asl and p2bin builds, and its p2bin -z option is not in the official manual.
  • Whether sega-vr-disasm's tag v5.0.0 still rebuilds the ROM byte for byte. Its README points to a v5.0-freeze branch for that, but the branch was not found on 2026-11-13.
  • Whether the 2021 vasm binaries on the vasm page include the vasmm68k_mot build that sega-vr-disasm uses.
  • Whether m2c and asm-differ give useful output on 32X SH-2 code. No reviewed 32X project used them.
  • How much work came before each project's first public commit. Chaotix Recompiled and sega-vr-disasm both started with big working commits.
  • Whether Doom32x-RE builds, or how close it is to the cartridge.
  • How Chaotix Recompiled used Claude day to day. Its docs/superpowers/ folder holds one plan file (frame pacing, 2026-09-27). The repo does not say which tool wrote it.
  • Whether s32xfan/virtuaracing used AI, and what language its recompiler is written in.
  • That the same 32X boot shortcut works for every game. It is proven only for Knuckles' Chaotix.
  • All Path B time ranges. No 32X game has a full decomp, so they are estimates from a disassembly and from Genesis projects.

Sources