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
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.
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.
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.
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
$100saysSEGA 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-disasmandnukeykt/Doom32x-REif 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 ClaudeGOAL: 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.mdnames 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
- In WSL, hash your own dump:
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/andbuild/to.gitignorebefore 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 ClaudeGOAL: 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, andgit statusshows no ROM and no generated code.Proof this works: Chaotix Recompiled (CMake, Ninja, MSYS2, ROM-free CI), https://www.msys2.org/
- Install MSYS2 from msys2.org. In the MinGW64 shell run
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.hand 68000 ones insrc/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-objdumpon 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_OKandS_OKto the COMM ports.
Prompt for ClaudeGOAL: 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 withsh-elf-objdumpon 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)
- Put SH-2 instruction meanings in
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-disasmnotes SH-2$0600xxxxcame from ROM offset$20000plusxxxxin 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-2at base$02000000. - Write
notes/functions.csvwith CPU, address, size, proof and a status column.
Prompt for ClaudeGOAL: 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.csvlists 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)
- Write an analyzer that walks code from every known entry point and decodes jump tables. Chaotix calls this
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 ischaotix_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
--shotand look at it yourself. - Write the fallback count and the result in
PROGRESS.md.
Prompt for ClaudeGOAL: 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)
- Generate:
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 ClaudeGOAL: 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 inPROGRESS.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)
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'sCOMM_REGISTERS_HARDWARE_ANALYSIS.mdfirst. - 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 ClaudeGOAL: 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)
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 ClaudeGOAL: 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.
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-REif 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 ClaudeGOAL: 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.mdnames the route, andnotes/compiler.mdholds the evidence for it.Proof this works: Doom32x-RE (C from Jaguar Doom source), sega-vr-disasm (SH-2 functions kept as assembly)
- Search for a disassembly or rebuild of your game. Read
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
CPUsetting for each part:CPU 68000,CPU SH7600for the SH-2, andCPU Z80. - Build like the Chaotix prototype disassembly:
asl -xx -q -A -L -U -E -i . main.asm, thenp2bin main.p <OUT>.32x. That project keeps its ownaslandp2binbuilds intool/linux/, with extrap2binoptions. - Or copy sega-vr-disasm's route. It assembles 68000 code with
vasm. It assembles each SH-2 function withsh-elf-as --isa=sh2, places it with a linker script, and includes the bytes. - Write
tools/build.py. It assembles, compares the SHA-1 withSCOPE.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 ClaudeGOAL: 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.pyprints 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)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 ClaudeGOAL: 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)
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.mdwith a source line for every claim. - Confirm each command with a trace from your Path A headless build.
Prompt for ClaudeGOAL: 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 innotes/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
- List every read and write of the COMM registers on every CPU. Use
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. Usem2cwith targetsh2for a first SH-2 draft. It reads GNUassyntax, 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-gccfrommarsdevand diff withasm-differ. - Write the one-unit check into
notes/method.mdso every worker uses it.
Prompt for ClaudeGOAL: 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
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 ClaudeGOAL: 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)
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 ClaudeGOAL: 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)
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.mdwith 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 ClaudeGOAL: 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)
- Put all game memory access behind one header, such as
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=undefinedand 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 ClaudeGOAL: 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
| Tool | What it does | How to get it | How 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 options | releases 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.2 | README, 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 links | README 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 build | README 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-13 | vasm 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
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
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
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
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
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
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
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
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)
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)
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.
| Job | What it means on this system | Max 20x (now called Ultra) | Max 5x (now called Max) |
|---|---|---|---|
| Plan the project | Pin 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 highOne session. This choice shapes months of work. | Opus 5.5 effort highUse Fable 5.2 for this one session if you have weekly room. |
| Set up the project | Set 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 file | Map 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 mediumRaise to high for function edges and data layouts. A mistake there spreads. |
| Bulk units | Fallback 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 mediumHaiku 5.5 for the plainest units. | Sonnet 5.5 effort mediumFewer workers at once. Haiku 5.5 for the plainest units. |
| Hard units | RAM-resident and self-modifying code, routines that touch COMM handshakes, and units that failed five times in bulk. | Fable 5.2 effort highGo to xhigh only after a failed try at high. | Opus 5.5 effort highSend only the last few that still fail to Fable 5.2. |
| Build the runtime | The 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 mediumRaise to high for timing and graphics bugs. |
| Find a bug | Find the first lockstep difference, then the first wrong write. Prove the cause with dumps from both sides before any fix. | Opus 5.5 effort highAfter two failed tries with full evidence, move to Fable 5.2. | Opus 5.5 effort highOne bug at a time. |
| Check the work | A 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 mediumAlways a new session, never the one that did the work. | Sonnet 5.5 effort highAlways a new session, never the one that did the work. |
| Keep the records | Update 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
- 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.mdand 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
Readdeny rules forgenerated/**,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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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 detour | The 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 AI | How sure | |
|---|---|---|---|---|
| Path A: running on PC | 3 to 10 weeks | 7 to 25 weeks | 1 to 3 years | medium confidence |
| Path B: full decomp | 4 to 11 months | 9 months to 2.5 years | 3 to 6 years | low 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
| Project | What happened | Link |
|---|---|---|
| Chaotix Recompiled | First 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-recomp | First 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-disasm | First 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 Enhanced | First commit and first release both 2026-10-15. 4 commits, 1 contributor. README says AI-assisted development. Emulator-based, not a decomp. | source |
| Doom32x-RE | 33 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/chaotix | First 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)
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
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
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
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
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)
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
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
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
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)
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
aslandp2binbuilds, and itsp2bin -zoption is not in the official manual. - Whether sega-vr-disasm's tag
v5.0.0still rebuilds the ROM byte for byte. Its README points to av5.0-freezebranch for that, but the branch was not found on 2026-11-13. - Whether the 2021 vasm binaries on the vasm page include the
vasmm68k_motbuild that sega-vr-disasm uses. - Whether
m2candasm-differgive 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/virtuaracingused 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
- Chaotix Recompiled README
- The Recompiled Handbook (Chaotix Recompiled)
- Virtua Racing Deluxe Enhanced README
- Doom32x-RE repository
- sega-vr-disasm README
- sega-vr-disasm CLAUDE.md
- sega-vr-disasm Makefile
- The Macroassembler AS user manual (English)
- Musashi README
- binutils-sh-elf file list (noble)
- knuckles-chaotix-recomp README
- Knuckles' Chaotix Disassembly (Sonic Retro)
- Chaotix 1207 prototype disassembly
- s32xfan/virtuaracing README
- DOOM 32X: Resurrection README
- 32X hardware manual (32XDK transcription)
- 32XDK wiki: SH2 on the 32X
- Plutiedev: ROM header
- PicoDrive README
- BizHawk README
- Ghidra SuperH language definitions
- GhidraMCP README
- The Macroassembler AS
- vasm
- MSYS2
- Ubuntu package binutils-sh-elf (noble)
- marsdev README
- m2c README
- asm-differ README
- Capstone README