Arcade decomp guide
Every board differs, but MAME runs the real code, so you can check your work frame by frame.
What this system is like
Arcade is not one system. It is hundreds of boards from Sega, Namco, Konami, SNK, Midway and others. Each board has its own main chip, sound chip and graphics chips. The 12 projects reviewed here use nine different main chips. So first find out which board your game runs on.
The good news is MAME. MAME is a free emulator that runs almost every board. Its code also shows how each chip behaves. Most strong projects here use MAME as the answer key. It runs the original code and your code with the same inputs, then compares each frame. Others check against a CPU interpreter or the Supermodel emulator instead.
Path A gets the game running on a PC. Most projects use static recompilation: a tool turns the game's machine code into C. A small interpreter runs any code the tool missed and checks the rest. For Neo Geo games, a recompiler toolkit already exists.
Path B turns the game into readable C. Byte matching is rare here, because many older games were written in assembly. So most projects check each function against MAME instead. Only newer boards with a known compiler, like Triforce, allow a true matching decomp.
Hundreds of boards, not one system
Every board has its own chips and memory map. A tool for Sega System 32 does nothing for a Namco game. Your first job is to name the board and its main chip. MAME's driver file for the board tells you both.
MAME is your answer key
MAME runs the original code on almost every board. The cheviman ports run MAME's CPU cores beside their own C. Both play the same input script and write a hash for every frame. A hash is a short fingerprint of memory or a picture. The first frame that differs points to the bug.
Small programs, many chips
Older arcade programs are small. TMNT has 2,439 functions and OutRunners has 762. But a board may have three or more chips with code: a main chip, a sound chip and a math chip. Each one needs its own translation.
Byte matching is rare
Many older games were written in assembly, so no compiler can rebuild them. The diff tool objdiff also has no support for the 68000, V60 or i960. So most Path B projects check behavior against MAME, not bytes. Triforce is the exception: Mario Kart Arcade GP2 is being matched byte for byte.
The ROM is the game
Machine-made C comes from the game's code, so it belongs to the game's owner. The best projects keep it out of git. They build it on the player's PC from the player's own ROM set. Some even run a check that no program holds ROM bytes.
Two paths
Get it running on PC
A recompiler turns each routine of the main program into C. A runtime stands in for the board: video chips, sound chips, inputs and timers. A built-in interpreter, often Musashi for the 68000, runs code the recompiler missed. The same interpreter replays every block and compares the results. You fix one mismatch at a time, found by per-frame hashes against MAME or the interpreter.
Full decomp for porting
You turn every function into readable C that does exactly what the original does. Since most arcade games cannot be byte matched, the check is behavior. MAME's CPU core runs the original, your C runs beside it, and every frame's state, picture and sound must match. Names and types live in a text file, and a decompiler writes the C from it. When it all matches, you put the board behind an interface and port it.
Path A: Get it running on PC
A recompiler turns each routine of the main program into C. A runtime stands in for the board: video chips, sound chips, inputs and timers. A built-in interpreter, often Musashi for the 68000, runs code the recompiler missed. The same interpreter replays every block and compares the results. You fix one mismatch at a time, found by per-frame hashes against MAME or the interpreter.
Method: static recompilation, checked against an interpreter and MAME
Pick this first, for any board. It is the fastest way to a running game. If your game runs on a Neo Geo, start from neogeorecomp. If it runs on Model 3 or Triforce, study SpikeOut Recomp or OpenGP first.
Scope the game and find its board
You pick one game, one MAME set name and one revision. You find the board, its main chip and its sound chips. Then you look for prior work, because arcade has several toolkits and rewrites already. The result is a one-page
SCOPE.mdyou approve.- Find the set name and board in MAME. Run
mame -listfull <SET>andmame -listsource <SET>to see the game name and its driver file. - Open that driver file under
src/mame/on GitHub. Its top comment and its machine setup name the CPUs and chips. - Check your ROM files:
mame -verifyroms <SET>. Write down each file's CRC32 from the zip. - Search for prior work:
neogeorecomp(Neo Geo),arcade-decomp(System 32, Multi 32, Konami 68000), Supermodel (Model 3), CannonBall (OutRun), OpenGP (Triforce). - Write
SCOPE.mdwith lines that startBoard:,CPUs:,Set:,Technique:andPrior work:. Add the milestone, its check, and what is out of scope. - Read it yourself. When you agree, add a line that starts
Approved:with the date. Only you write that line.
Prompt for ClaudeGOAL: Draft SCOPE.md for <GAME>, MAME set <SET>, so I can approve one technique. READ FIRST: notes/mame-listsource.txt, notes/verifyroms.txt, the MAME driver file <DRIVER_PATH> (read only its top comment and machine setup). FENCE: Edit only SCOPE.md and notes/. Do not download ROMs, BIOS files or game data. Do not start any code. CHECK: Run `findstr /b /c:"Board:" SCOPE.md && findstr /b /c:"CPUs:" SCOPE.md && findstr /b /c:"Set:" SCOPE.md && findstr /b /c:"Technique:" SCOPE.md && findstr /b /c:"Prior work:" SCOPE.md`. Done means all five lines print and the command exits 0. The CPUs line gives each CPU's role. The Technique line gives one reason against each other technique. BUDGET: Stop after 45 minutes of work. IF BLOCKED: Stop. Do not work around it. If the board has no MAME driver or the set fails verification, say so plainly and stop. OTHER PROBLEMS: Write them in notes/parking-lot.md. Do not fix them. REPORT: 1) DONE, PARTIAL or BLOCKED. 2) The technique in one line, then the evidence. 3) Files changed. 4) Open questions. 5) New parking-lot items.
Done whenThefindstrchain in the prompt exits 0 onSCOPE.md, andfindstr /b /c:"Approved:" SCOPE.mdfinds the line you added after reading it.Proof this works: OutRunners Port, KOF'98 Recomp, https://docs.mamedev.org/commandline/commandline-all.html
- Find the set name and board in MAME. Run
Set up the repo and the one check
You make a repo that holds only your own code. Your ROM set lives in a
rom/folder that git ignores. You write a script that checks the ROM by hash, and one check command that Claude runs after every change.- Install Visual Studio Build Tools with the C++ workload, CMake 3.20 or newer, SDL2, Python 3.10 or newer, and Git.
- For a Neo Geo game, clone the toolkit as a submodule:
git submodule add https://github.com/sp00nznet/neogeorecomp. - Add
rom/,build/andbuild/generated/to.gitignore. Generated C never goes in git. - Write
tools/verify_rom.py. It checks the CRC32 and SHA-1 of every chip file and refuses any other revision. - Write
check.cmd. It runscmake --build build --config Release, thenctest --test-dir build -C Release --output-on-failure, then the golden run. - Write
CLAUDE.md,PROGRESS.mdandnotes/parking-lot.md.
Prompt for ClaudeGOAL: Make `check.cmd` build the project and run every check in one command. READ FIRST: SCOPE.md, CLAUDE.md, the README of <TOOLKIT> (Getting Started only). FENCE: Edit only CMakeLists.txt, check.cmd, .gitignore and tools/. Never copy ROM files into the repo. Do not add features, refactors or tools I did not ask for. CHECK: Run `check.cmd`. Done means it exits 0 on a clean clone, and exits non-zero after you add a deliberate compile error. Run `python tools/verify_rom.py --rom-dir <ABSOLUTE_ROM_DIR>` and paste its output. BUDGET: Stop after 3 failed tries at the same error. IF BLOCKED: Stop. Do not work around it. Tell me what blocked you, what you tried, and what you need from me. OTHER PROBLEMS: Write them in notes/parking-lot.md. Do not fix them. REPORT: 1) DONE, PARTIAL or BLOCKED. 2) Paste both check outputs and the ROM check. 3) Files changed. 4) What is left. 5) New parking-lot items.
Done whencheck.cmdpasses on a clean clone, fails on a planted error, andverify_rom.pypasses on your ROM set.Proof this works: KOF'98 Recomp, tmnt-port, https://github.com/sp00nznet/neogeorecomp
Build the reference and the input scripts
Before any recompiling, you build the answer key. A reference runs the original code with the same inputs every time. You write small input scripts that reach each part of the game. Each run writes a hash for every frame.
- Pick a reference. For the 68000, link
Musashias an interpreter. For other chips, use MAME itself, or MAME's CPU core inside your own runner. - To record with MAME, write a Lua script and run
mame <SET> -autoboot_script tools/capture.lua. Useemu.add_machine_frame_notifierto save a hash of work RAM each frame. - Write input scripts in
tests/controls/: coin and start, attract mode, one level, one boss. Use RAM pokes to start a later level. - Save the reference hashes for each script in
build/reference/. - Run each script twice and confirm both runs give the same hashes. If not, find the random seed or timer first.
Prompt for ClaudeGOAL: Make a reference runner that plays tests/controls/<SCRIPT>.txt on the original <SET> code and writes one state hash per frame. READ FIRST: SCOPE.md, https://docs.mamedev.org/luascript/index.html, https://docs.mamedev.org/luascript/ref-common.html (emu.add_machine_frame_notifier), tests/controls/README.md. FENCE: Edit only tools/reference/ and tests/controls/. ROM files and MAME's own files are read-only. Do not start the recompiler. CHECK: Run the reference twice on the same script, writing build\reference\<SCRIPT>-1.txt and build\reference\<SCRIPT>-2.txt. Then run `fc /b build\reference\<SCRIPT>-1.txt build\reference\<SCRIPT>-2.txt`. Done means `fc` finds no differences, and a changed input line changes the hashes. BUDGET: Stop after 4 failed tries, or after 60 minutes with no new frame matching. IF BLOCKED: Stop. Do not work around it. If the runs differ, report the first differing frame and stop. OTHER PROBLEMS: Write them in notes/parking-lot.md. Do not fix them. REPORT: 1) DONE, PARTIAL or BLOCKED. 2) Paste the `fc` or `cmp` output of the two runs. 3) Files changed. 4) What is left. 5) New parking-lot items.
Done whenfc /bfinds no differences between two runs of the same script, and a changed input line changes the hash file.Proof this works: tmnt-port, The House of the Dead: ReWrithe, Super-Hang-On-Ultimate
- Pick a reference. For the 68000, link
Map the program and its code
You find every routine in the main program. Static search from the reset and interrupt vectors finds only part of it. A pointer scan and a profile run find the rest. The map goes into a ledger file with a status column.
- Load the main program into Ghidra. Pick the right chip and base address from the MAME driver.
- Follow code from the vectors and jump tables. Then scan memory for values that look like code pointers.
- Run each input script in a profile build that logs every entry point the interpreter had to run.
neogeorecompgames do this with--dump-misses. - Write
ledger/functions.tsv: address, size, kind (game, sound driver, copied to RAM), status. - Mark code that the game copies to RAM at boot. A static tool cannot see it.
- Write
tools/check_ledger.py. It prints every miss address that falls in no ledger row, then exits 1 if there are any.
Prompt for ClaudeGOAL: Write ledger/functions.tsv for the <CPU> main program of <SET>, with every routine found so far. READ FIRST: SCOPE.md, build/profile/misses.txt, the Ghidra project <GHIDRA_PROJECT> (through the MCP server if it is up). FENCE: Edit only ledger/, notes/map.md and tools/check_ledger.py. Do not rename anything in Ghidra yet. Do not write C. CHECK: Run `python tools/check_ledger.py ledger/functions.tsv build/profile/misses.txt`. Done means it prints 0 missing and exits 0. The ledger has address, size, kind and status columns, and its row count is in notes/map.md. BUDGET: Stop after 90 minutes. IF BLOCKED: Stop. Do not work around it. If the Ghidra MCP server is down, stop and ask. Do not guess code from memory. OTHER PROBLEMS: Write them in notes/parking-lot.md. Do not fix them. REPORT: 1) DONE, PARTIAL or BLOCKED. 2) Row counts by kind, and the miss check result. 3) Files changed. 4) Areas still unclear. 5) New parking-lot items.
Done whenpython tools/check_ledger.py ledger/functions.tsv build/profile/misses.txtprints 0 missing and exits 0.Proof this works: KOF'98 Recomp, SpikeOut Recomp, The House of the Dead: ReWrithe
First light: recompile and boot
You run the recompiler over the whole main program and link it with the runtime. Missing code falls back to the interpreter, so the game can boot early. The goal is a window showing the first frame, then attract mode.
- Generate the C into
build/generated/. For a Neo Geo game:cmake -S . -B build -D<GAME>_ROM_DIR=rom, thencmake --build build --config Release. - Stub nothing quietly. A missing chip register should log its address and stop.
- Write
tools/golden.cmd. It runs the game headless for a set number of frames with an input script and saves a screenshot. - Turn on the verify mode. Every recompiled block is replayed in the interpreter and compared:
<GAME> --headless --verify --frames 1801 --input tests/coin_start.txt.
Prompt for ClaudeGOAL: Build the recompiled <SET> and reach the first attract-mode frame with no person. READ FIRST: SCOPE.md, ledger/functions.tsv, the recompiler docs <RECOMP_DOCS>. FENCE: Edit only runtime/, src/ and tools/golden.cmd. Never edit build/generated/; change the recompiler input and regenerate. No stub may return a fake value. CHECK: Run `tools\golden.cmd`. Done means it saves the frame <N> screenshot, exits 0, and the verify line reports 0 mismatches. BUDGET: Stop after 5 failed builds, or 90 minutes with no new frame reached. IF BLOCKED: Stop. Do not work around it. Report the last log lines and the address that stopped the run. OTHER PROBLEMS: Write them in notes/parking-lot.md. Do not fix them. REPORT: 1) DONE, PARTIAL or BLOCKED. 2) Paste the golden run output and the verify line. 3) Files changed. 4) What is left. 5) New parking-lot items.
Done whentools\golden.cmdreaches the attract-mode frame, saves the screenshot, and the verify line says 0 mismatches.Proof this works: KOF'98 Recomp, https://github.com/sp00nznet/neogeorecomp
- Generate the C into
Fix one mismatch at a time
Now you grind. Each unit is one mismatch: a frame where your hash differs from the reference. Claude finds the first differing frame and the first differing byte. Then it finds which code wrote that byte, and fixes the cause.
- Run every input script on both sides and compare the hash files. The first differing frame is the bug's frame.
- Dump RAM at that frame on both sides. Find the first differing byte and the code that wrote it.
- Fix the recompiler or the runtime, never the generated C. Then rerun every script, not just the one that failed.
- Record each fix in
PROGRESS.mdwith its cause and the frame it fixed.
Prompt for ClaudeGOAL: Make tests/controls/<SCRIPT>.txt match the reference past frame <FRAME>. READ FIRST: CLAUDE.md, PROGRESS.md, build/diff/<SCRIPT>.txt, notes/findings.md. FENCE: Edit only the recompiler source in tools/recomp/ and runtime/. Never edit build/generated/, the reference hashes, or the check scripts. CHECK: Run `check.cmd`. Done means every script in tests/controls/ matches the reference hashes, including <SCRIPT> past frame <FRAME>. BUDGET: Stop after 3 failed fixes, or 60 minutes with no new matching frame. IF BLOCKED: Stop. Do not work around it. Give me the first differing frame, the byte, the code that wrote it, and your best cause. OTHER PROBLEMS: Write them in notes/parking-lot.md. Do not fix them. REPORT: 1) DONE, PARTIAL or BLOCKED. 2) Paste the check summary. 3) Files changed. 4) The proven cause in one sentence. 5) New parking-lot items.
Done whenEvery input script matches the reference hashes, andPROGRESS.mdnames the cause of the fix.Proof this works: SpikeOut Recomp, Super-Hang-On-Ultimate, Namco 22 Decompile
Finish the board: video, sound and input
You replace the debug output with real video, sound and controls. Build each chip as its own library and test it alone. Check the video against MAME's picture and the sound against MAME's samples, before you judge the game.
- Port each video chip's drawing rules from MAME's source. Keep the copyright notice and name the source file.
- Use
ymfmfor Yamaha FM chips such as the YM2151, YM2610 and YM3438. - Use SDL2 or SDL3 for the window, sound and game pads.
- Write a picture hash and a sound hash for every frame. Compare both against the reference. Add
--videoand--soundswitches tocheck.cmdfor this. - Pace frames by the clock if the screen ignores vsync.
Prompt for ClaudeGOAL: Make the <CHIP> stand-in match MAME's picture (or sound) for tests/controls/<SCRIPT>.txt. READ FIRST: CLAUDE.md, the MAME source file <MAME_FILE> for this chip, runtime/<CHIP>.c, build/diff/<SCRIPT>-video.txt. FENCE: Edit only runtime/<CHIP>.c and its header. Keep MAME's notice in any code you adapt. Do not change game logic or the reference. CHECK: Run `check.cmd --video --sound`. Done means every frame's picture hash and sound hash match for <SCRIPT>, and all older scripts still pass. BUDGET: Stop after 4 failed tries, or 90 minutes with no new matching frame. IF BLOCKED: Stop. Do not work around it. Save both frames as BMP files and tell me what differs. OTHER PROBLEMS: Write them in notes/parking-lot.md. Do not fix them. REPORT: 1) DONE, PARTIAL or BLOCKED. 2) Paste the check summary. 3) Files changed. 4) What is left. 5) New parking-lot items.
Done whencheck.cmd --video --soundexits 0, so picture and sound hashes match the reference for every script.Proof this works: segasonic-port, tmnt-port, https://github.com/aaronsgiles/ymfm
Harden and package it
You lock in your progress. Every input script becomes a regression test. You prove no ROM data is in the repo or the program. The player's PC builds the game from their own ROM set.
- Run every script on a clean clone. Add a long soak run of 5 minutes or more.
- Add a check that fails if any ROM-derived file is tracked by git.
- Add a check that no executable holds a stretch of ROM bytes.
- Build release packages with CI on clean machines. CI has no ROM, so game tests stay local.
- Keep extras like widescreen behind a switch that is off by default, so the plain build still matches.
Prompt for ClaudeGOAL: Make a clean clone pass every check, and prove no ROM data ships. READ FIRST: CLAUDE.md, PROGRESS.md, tests/controls/, .github/workflows/. FENCE: Edit only tools/, tests/ and .github/workflows/. Do not change game behavior or reference hashes. CHECK: On a fresh clone, run `check.cmd`, the soak run, and `python tools/check_game_data.py`. Done means all three pass, and the game-data check fails when you stage a test file named rom/fake.bin. BUDGET: Stop after 3 failed tries at the same error. IF BLOCKED: Stop. Do not work around it. Tell me what blocked you, what you tried, and what you need from me. OTHER PROBLEMS: Write them in notes/parking-lot.md. Do not fix them. REPORT: 1) DONE, PARTIAL or BLOCKED. 2) Paste all three outputs and the planted failure. 3) Files changed. 4) What is left. 5) New parking-lot items.
Done whenA fresh clone passes every script and the soak run, and the game-data check fails on a planted ROM file.Proof this works: OpenGP, tmnt-port, KOF'98 Recomp
Path B: Full decomp for porting
You turn every function into readable C that does exactly what the original does. Since most arcade games cannot be byte matched, the check is behavior. MAME's CPU core runs the original, your C runs beside it, and every frame's state, picture and sound must match. Names and types live in a text file, and a decompiler writes the C from it. When it all matches, you put the board behind an interface and port it.
Method: non-matching decompilation checked frame by frame against MAME (matching only where the compiler is known, as on Triforce)
Pick this after Path A works, or when you want the game on other systems. It suits small games on the boards arcade-decomp covers today: Sega System 32, Sega Multi 32 and Konami 68000 boards. On Triforce, pick true matching with decomp-toolkit instead.
Choose matching or behavior checks
You decide how each function will be proven. If you know the original compiler and can run it, you can match bytes. If not, you prove behavior against MAME. Write the choice and the reason in
SCOPE.md.- Look for a compiler mark in the program. On Triforce,
mkgp2-decompreadsmw_comment_versionto guess CodeWarrior 2.7. - If the code looks hand-written in assembly, plan for behavior checks. CannonBall's author found OutRun was 68000 and Z80 assembler.
- Check if
arcade-decompsupports your chips today: V60, V70, 68000 and Z80. - Check if
objdiffsupports your chip: PowerPC, SuperH, MIPS and x86 yes; 68000, V60 and i960 no. - Write the check for each kind of unit into
SCOPE.md, on lines that startCheck:. Startnotes/compiler.mdwith a lineCompiler:that names it or says unknown.
Prompt for ClaudeGOAL: Decide, with evidence, if <SET> can be byte matched or must be checked by behavior. READ FIRST: SCOPE.md, notes/map.md, the first 200 lines of the Ghidra listing at <ENTRY_ADDRESS>, https://github.com/cheviman/arcade-decomp, https://github.com/encounter/objdiff. FENCE: Edit only SCOPE.md and notes/compiler.md. Do not download any compiler. Do not write C. CHECK: Run `findstr /b /c:"Compiler:" notes\compiler.md && findstr /b /c:"Check:" SCOPE.md`. Done means both print and the command exits 0. The Compiler line names the compiler or says unknown, with the evidence and its addresses below it. SCOPE.md has one Check line for each unit type. BUDGET: Stop after 60 minutes. IF BLOCKED: Stop. Do not work around it. If you cannot tell, say unknown. Do not guess a compiler. OTHER PROBLEMS: Write them in notes/parking-lot.md. Do not fix them. REPORT: 1) DONE, PARTIAL or BLOCKED. 2) Matching or behavior, in one line, then the evidence. 3) Files changed. 4) Open questions. 5) New parking-lot items.
Done whenfindstr /b /c:"Compiler:" notes\compiler.md && findstr /b /c:"Check:" SCOPE.mdexits 0.Proof this works: https://github.com/naari3/mkgp2-decomp, https://github.com/djyt/cannonball, OutRunners Port
- Look for a compiler mark in the program. On Triforce,
Set up the decomp repo
You build a repo where text files hold your knowledge and a script writes the C. Names, types and comments go in
symbols.txt. The ROM stays outside git. Assets are cut from the ROM and must rebuild it byte for byte.- For a 68000, V60 or Z80 game, add the shared core:
git submodule add https://github.com/cheviman/arcade-decomp tools/core. - Write
tools/game_profile.pyto tell the core your CPU, set name and memory layout. - For Triforce, start from
dtk-template, runpython configure.py, thenninja. - Write
tools/verify_rom.pyandtools/extract_assets.py --check. The check rebuilds the chips from the assets and compares bytes. - Write
check.cmdandCLAUDE.md.
Prompt for ClaudeGOAL: Set up the decomp repo for <SET> so one command builds and checks it. READ FIRST: SCOPE.md, the arcade-decomp README sections 'A game's profile' and 'Using it in a game'. FENCE: Edit only tools/game_profile.py, tools/*.py wrappers, CMakeLists.txt, check.cmd and .gitignore. Never commit ROM files or generated C. Do not edit tools/core/. CHECK: Run `python tools/verify_rom.py`, `python tools/extract_assets.py --check` and `check.cmd`. Done means all three pass on a clean clone. BUDGET: Stop after 3 failed tries at the same error. IF BLOCKED: Stop. Do not work around it. Tell me what blocked you, what you tried, and what you need from me. OTHER PROBLEMS: Write them in notes/parking-lot.md. Do not fix them. REPORT: 1) DONE, PARTIAL or BLOCKED. 2) Paste all three outputs. 3) Files changed. 4) What is left. 5) New parking-lot items.
Done whenverify_rom.py,extract_assets.py --checkandcheck.cmdall pass on a clean clone.Proof this works: tmnt-port, segasonic-port, https://github.com/cheviman/arcade-decomp
- For a 68000, V60 or Z80 game, add the shared core:
Build the reference and the scenarios
You need a judge for every function. The reference runs the original program on MAME's CPU core against a model of the board. Your C runs the same input scripts. Both write hashes of state, picture and sound for every frame.
- Use the core's reference if it fits:
s32reffor System 32 and Multi 32,kreffor Konami 68000 boards. - Otherwise, capture in MAME. Run
mame <SET> -debugor a Lua script to save registers and RAM at a routine's entry and return. - Write scenarios that reach every area and boss. Use RAM pokes to start late areas.
- Record which addresses each scenario ran. That tells you which scenarios test which function.
Prompt for ClaudeGOAL: Make the reference play every script in tests/controls/ and write per-frame hashes and executed addresses. READ FIRST: SCOPE.md, tools/core/<FAMILY>/reference/, https://docs.mamedev.org/debugger/execution.html. FENCE: Edit only tools/reference/ and tests/controls/. MAME files, ROM files and tools/core/ are read-only. CHECK: Run the reference twice on all scripts, into build\reference\run1\ and build\reference\run2\. Then run `fc /b build\reference\run1\*.txt build\reference\run2\*.txt`. Done means `fc` finds no differences, and each script has an executed-address list. BUDGET: Stop after 4 failed tries, or 60 minutes with no progress. IF BLOCKED: Stop. Do not work around it. If two runs differ, report the first differing frame. OTHER PROBLEMS: Write them in notes/parking-lot.md. Do not fix them. REPORT: 1) DONE, PARTIAL or BLOCKED. 2) Scenario count, frame count and the repeat check. 3) Files changed. 4) What is left. 5) New parking-lot items.
Done whenfc /bfinds no differences between the two runs, and every scenario has a list of the addresses it ran.Proof this works: tmnt-port, OutRunners Port, The House of the Dead: ReWrithe
- Use the core's reference if it fits:
Map every byte and check the disassembler
You classify every byte of the program as code, data or fill. Before you trust a disassembler, you check it against MAME's at every offset. Then the static analysis must find all the code the reference ran.
- Compare your disassembler with MAME's at every byte offset of the program ROM. The core has a script for each CPU:
compare_with_mame.py(V60),compare_z80_with_mame.py(Z80) andcompare_m68k_with_mame.py(68000). - Run
python tools/coverage.pyto classify every byte. - Put names, types and comments in
symbols.txt. Give the evidence for each name. - Write
docs/unreachable.txt: each piece of code no scenario runs, with a reason. - Build
ledger/functions.tsvwith a status column: raw, decompiled, named, proven.
Prompt for ClaudeGOAL: Prove the disassembler for <CPU> matches MAME's and classify every byte of the <SET> program. READ FIRST: CLAUDE.md, tools/core/<CPU>/, build/reference/executed.txt. FENCE: Edit only symbols.txt, docs/unreachable.txt and ledger/. Do not edit tools/core/ or src/. CHECK: Run the core's compare script for <CPU> (`compare_with_mame.py`, `compare_z80_with_mame.py` or `compare_m68k_with_mame.py` under tools/core/), then `python tools/coverage.py`, then `python tools/check_scenarios.py`. Done means 0 differences at every offset, every byte is classified, and every executed address is inside a known function. BUDGET: Stop after 90 minutes. IF BLOCKED: Stop. Do not work around it. List the first 10 offsets that differ from MAME and stop. OTHER PROBLEMS: Write them in notes/parking-lot.md. Do not fix them. REPORT: 1) DONE, PARTIAL or BLOCKED. 2) Paste both summaries. 3) Files changed. 4) Unclear areas. 5) New parking-lot items.
Done whenThe disassembler matches MAME's at every offset, every byte is classified, and every executed address is in a known function.Proof this works: segasonic-port, tmnt-port, OutRunners Port
- Compare your disassembler with MAME's at every byte offset of the program ROM. The core has a script for each CPU:
First light: one unit proven
You prove the whole chain on one small unit. With behavior checks, start from a translation scaffold: one C line per instruction that already passes the scenarios. With matching, get one small function to an exact match.
- Behavior route: build the scaffold with
tools/recompile.pyand runpython tools/check_scenarios.py. - Then write the readable C for one small function with
python tools/decompile.py, and check again. - Matching route: pick one small function, write its C, run
ninja, and diff it inobjdiff. - Break the function on purpose and confirm the check fails. Then fix it back.
Prompt for ClaudeGOAL: Prove one small function, <FUNCTION> at <ADDRESS>, end to end. READ FIRST: CLAUDE.md, symbols.txt, the reference trace for <ADDRESS>. FENCE: Edit only symbols.txt (and the one source file for <FUNCTION> if matching). Never edit generated C by hand. Do not touch other functions. CHECK: Run `python tools/check_scenarios.py --video --sound` (or `ninja` plus objdiff for matching). Done means it passes, and fails when you change one constant in <FUNCTION> on purpose. BUDGET: Stop after 5 tries. IF BLOCKED: Stop. Do not work around it. Paste the first difference and stop. OTHER PROBLEMS: Write them in notes/parking-lot.md. Do not fix them. REPORT: 1) DONE, PARTIAL or BLOCKED. 2) Paste the passing check and the planted failure. 3) Files changed. 4) What is left. 5) New parking-lot items.
Done whenOne function passes its check, and a planted change makes the check fail.Proof this works: OutRunners Port, https://github.com/naari3/mkgp2-decomp
- Behavior route: build the scaffold with
Decompile function by function
Now you grind through the ledger. Each unit is one function. Claude adds names and types to
symbols.txt, runs the decompiler, and checks the scenarios that ran that function. Many workers can run at once, each on a different file.- Pick the next functions from the ledger, smallest and most-called first.
- Edit
symbols.txt, then runpython tools/decompile.py. Never editsrc/by hand. - Rerun only the scenarios that touched the change:
python tools/select_scenarios.py --run. - Track readability with
python tools/noise_report.pyagainsttools/noise_baseline.txt. A rising count fails. - Mark each function proven in the ledger only after its scenarios pass.
Prompt for ClaudeGOAL: Make <FUNCTION> at <ADDRESS> readable and proven: real names, real types, no CPU leftovers. READ FIRST: CLAUDE.md, ledger/functions.tsv row for <ADDRESS>, symbols.txt, the current C in src/<FILE>.c (read only). FENCE: Edit only symbols.txt entries for <FUNCTION> and the data it touches. Never edit src/ by hand. Do not touch functions another worker owns. CHECK: Run `python tools/decompile.py`, then `python tools/select_scenarios.py --run`, then `python tools/noise_report.py`. Done means the selected scenarios pass and the noise count does not rise. BUDGET: Stop after 5 tries without progress. IF BLOCKED: Stop. Do not work around it. Name the first differing frame or the noise line that will not go away. OTHER PROBLEMS: Write them in notes/parking-lot.md. Do not fix them. REPORT: 1) DONE, PARTIAL or BLOCKED. 2) Paste the scenario and noise summaries. 3) Files changed. 4) What is left. 5) New parking-lot items.
Done whenEvery ledger row is proven: its scenarios pass and the noise report is at or below the baseline.Proof this works: tmnt-port, OutRunners Port, https://github.com/cheviman/arcade-decomp
Work the hard tail
Some code fights back. It may be code no scenario reaches, jumps that cross in ways C loops cannot show, or math chips with odd rounding. Each hard unit gets a proven answer or a written reason.
- Reach rare code with poked scenarios. Put the proof that the real game never gets there in the script's header.
- For crossed jumps, allow a control flag and record why in the ledger.
- Model math chips exactly. The House of the Dead had to copy the coprocessor's square root and the PC's 24-bit float math.
- Work on one routine at a time. Capture its registers and RAM at entry and return in MAME. Then run your C on the same entry state.
Prompt for ClaudeGOAL: Prove <FUNCTION> at <ADDRESS>, which failed in bulk work, or give a proven reason it cannot be expressed as plain C. READ FIRST: CLAUDE.md, the ledger row and its failed-tries note, notes/findings.md, the MAME capture for <ADDRESS>. FENCE: Edit only symbols.txt for <FUNCTION> and notes/findings.md. Do not change the reference, the scenarios or tools/core/. CHECK: Run the routine compare for <ADDRESS> and `python tools/check_scenarios.py`. Done means 0 differences, or a written cause with addresses that a second session can reproduce. BUDGET: Stop after 6 tries that each test a different idea. IF BLOCKED: Stop. Do not work around it. Never call it impossible without the exact differing values. OTHER PROBLEMS: Write them in notes/parking-lot.md. Do not fix them. REPORT: 1) DONE, PARTIAL or BLOCKED. 2) Paste the compare output. 3) Files changed. 4) The cause in one sentence. 5) New parking-lot items.
Done whenpython tools/check_scenarios.pypasses, and each hard row inledger/functions.tsvsays proven or points to a cause with addresses innotes/findings.md.Proof this works: The House of the Dead: ReWrithe, tmnt-port, segasonic-port
Harden the whole-game check
You make sure no old proof breaks. Every scenario runs on a clean clone. The structure of every function is checked against the program's jumps. A big-endian build runs the same scenarios.
- Run
python tools/check_scenarios.py --video --soundon a clean clone. - Run
python tools/check_structure.pyto check every function's ifs and loops against the program's jumps. - Run the big-endian check under
qemu-ppcin WSL withpython tools/big_endian_scenarios.py. - Store the last verified state in
build/verified/(or name a commit), soselect_scenarios.pyknows what changed.
Prompt for ClaudeGOAL: Make the full check suite pass on a clean clone, including the big-endian build. READ FIRST: CLAUDE.md, PROGRESS.md, the arcade-decomp README section 'What is here' (select_scenarios.py and big_endian_scenarios.py). FENCE: Edit only tools/ wrappers, CMakeLists.txt and check.cmd. Do not change src/, symbols.txt or the reference. CHECK: On a fresh clone, run `python tools/check_scenarios.py --video --sound`, `python tools/check_structure.py`, `python tools/noise_report.py` and `python tools/big_endian_scenarios.py`. Done means all four pass, and a planted constant change in one function makes check_scenarios fail. BUDGET: Stop after 3 failed tries at the same error. IF BLOCKED: Stop. Do not work around it. Tell me what blocked you, what you tried, and what you need from me. OTHER PROBLEMS: Write them in notes/parking-lot.md. Do not fix them. REPORT: 1) DONE, PARTIAL or BLOCKED. 2) Paste all four summaries and the planted failure. 3) Files changed. 4) What is left. 5) New parking-lot items.
Done whenAll four checks pass on a fresh clone, and a planted change fails the scenario check.Proof this works: OutRunners Port, tmnt-port, https://github.com/cheviman/arcade-decomp
- Run
Port it behind a platform layer
Now the C is proven, so you can move it. Put the window, sound and input behind a small interface. Build for the new system. Keep the faithful arcade build next to any changed version, so both stay checkable.
- Make one folder per front end:
ports/sdl2,ports/headless, and one for each new system. - List every function the new version replaces in one file, such as
console/replaced.txt. The arcade build keeps the originals. - Fix 64-bit and byte-order problems with sanitizers:
-fsanitize=alignmentor AddressSanitizer. - Build device packages on the player's computer from their own ROM. Never share them.
- Write
ports/<TARGET>/check.cmd. It runs every scenario on the new system's headless build and compares each frame hash with the reference. The core'sbig_endian_scenarios.pyis a model for this.
Prompt for ClaudeGOAL: Build <SET> for <TARGET_SYSTEM> and pass the same scenarios there. READ FIRST: CLAUDE.md, ports/PLAN.md, ports/sdl2/, console/replaced.txt. FENCE: Edit only ports/<TARGET>/ and the platform interface header. Do not change src/ or symbols.txt. Do not add features I did not ask for. CHECK: Run `ports\<TARGET>\check.cmd`, then `check.cmd`. Done means the first reports every frame hash matching the reference on all scenarios, and the desktop build still passes `check.cmd`. BUDGET: Stop after 4 failed builds at the same error. IF BLOCKED: Stop. Do not work around it. Tell me what blocked you, what you tried, and what you need from me. OTHER PROBLEMS: Write them in notes/parking-lot.md. Do not fix them. REPORT: 1) DONE, PARTIAL or BLOCKED. 2) Paste both check summaries. 3) Files changed. 4) What is left. 5) New parking-lot items.
Done whenports\<TARGET>\check.cmdandcheck.cmdboth exit 0.Proof this works: tmnt-port, OutRunners Port, segasonic-port
- Make one folder per front end:
Tools you need
| Tool | What it does | How to get it | How we know |
|---|---|---|---|
| MAME Required Windows | The emulator for almost every arcade board. Its debugger traces code, and Lua scripts can save RAM and registers each frame. Its source shows how each chip works. Path A Path B | Download `mame0289b_x64.exe` from the MAME release page and run it. It unpacks to a folder. Start a game with `mame <SET> -debug`. | MAME release page, command-line, debugger and Lua docs (read 2026-11-13): version 0.289, file mame0289b_x64.exe, -debug, -listfull, -listsource, -verifyroms, -aviwrite, -autoboot_script, the trace command, and emu.add_machine_frame_notifier (Lua reference page). Used by: Namco 22 Decompile and The House of the Dead: ReWrithe as the reference, and the three cheviman ports through MAME's CPU cores. Most other projects take MAME ROM sets as input. |
| Ghidra Helpful Windows | A free disassembler and decompiler. It lists functions, shows C-like code and runs scripts with no window. For a chip it lacks, you write a processor module, as The House of the Dead did for the i960. Path A Path B | Install JDK 25 64-bit. Download the `ghidra_<version>_PUBLIC_<date>.zip` release, extract it, and run `ghidraRun.bat`. For `GhidraMCP`, use the Ghidra version its release names (12.1.4 today). | Ghidra README and latest release Ghidra_12.2_build, 2026-10-22 (read 2026-11-13). Used by: Namco 22 Decompile, The House of the Dead: ReWrithe (with its own i960 module), OpenGP's name sources. |
| arcade-decomp Required both | Shared tools that decompile arcade programs into C with no CPU left in it. It has V60, V70, 68000 and Z80 frontends, references that run MAME's cores, and frame-by-frame checks. Path B Path A | Add it to your game repo as a submodule: `git submodule add https://github.com/cheviman/arcade-decomp tools/core`. A clone made without it needs `git submodule update --init`. The games built on it ask for Python 3.10+, CMake 3.20+ and a C11 and C++17 compiler. | arcade-decomp README and repo data (read 2026-11-13): submodule commands, BSD-3-Clause, created 2026-10-25, last push 2026-11-13, frontends for V60, V70, 68000 and Z80, references s32ref and kref. The README lists no version needs; Python, CMake and compiler versions come from the tmnt-port and OutRunners Port cards. Used by: segasonic-port, OutRunners Port, tmnt-port. Only for its listed CPUs and board families. |
| neogeorecomp Helpful both | A static recompilation toolkit for Neo Geo games. Its m68krecomp turns the 68000 program into C, and its runtime models the Neo Geo board. It has a --verify mode against Musashi.Path A | `git clone --recurse-submodules https://github.com/sp00nznet/neogeorecomp`, then `cmake -S . -B build`, `cmake --build build --config Release`, `ctest --test-dir build -C Release --output-on-failure`. | neogeorecomp README (read 2026-11-13): v0.2.0 alpha, Windows (MSVC 2022) and Linux verified with games, MIT. Used by: KOF'98 Recomp (through a fork). |
| Musashi Helpful both | A small 68000 family emulator in C. Projects use it as the fallback for code the recompiler missed, and as the oracle that checks each recompiled block. Path A | Add it as a submodule: `git submodule add https://github.com/kstenerud/Musashi ext/musashi`. Toolkits like `neogeorecomp` already include it. | Musashi README (read 2026-11-13): version 4.10, 68000 to 68040, MIT-style licence. Used by: Super-Hang-On-Ultimate, KOF'98 Recomp, The House of the Dead: ReWrithe (sound board). |
| Supermodel Helpful Windows | An emulator for Sega Model 3. SpikeOut Recomp uses it as its first hardware reference, and draws the same 3D frame dump in both to compare. Path A Path B | Download a Windows build from the Supermodel releases page on GitHub. Only needed for Model 3 games. | Supermodel README and latest release v0.4-20261113 (read 2026-11-13): Windows, Linux and macOS builds on GitHub. Used by: SpikeOut Recomp. |
| decomp-toolkit (dtk) Helpful Windows | A GameCube and Wii toolkit that also fits Triforce games. dtk dol split config.yml target splits main.dol into objects. A dtk-template project keeps its settings in config.yml, symbols.txt and splits.txt.Path A Path B | Download the Windows binary from the releases page, or let a `dtk-template` project fetch a pinned copy with `python configure.py`. | decomp-toolkit README (dol split section), release v1.8.4 with dtk-windows-x86_64.exe, and dtk-template README and configure.py, which pins dtk_tag (read 2026-11-13). Used by: OpenGP, mkgp2-decomp. Only for Triforce among arcade boards. |
| objdiff Helpful Windows | A diff tool that compares your compiled function with the original, byte by byte. It supports PowerPC, SuperH, MIPS and x86, but not the 68000, V60 or i960. Path B | Download `objdiff-windows-x86_64.exe` from the latest release. The CLI is on the same releases page. | objdiff README and release v3.8.3 (read 2026-11-13). Used by: mkgp2-decomp (matching skill names objdiff). Only for boards whose chip it supports. |
| Visual Studio Build Tools and CMake Required Windows | The C and C++ compiler for Windows, and the build system every reviewed project uses. Most projects build with cmake -S . -B build and cmake --build build --config Release.Path A Path B | Install Build Tools for Visual Studio from the Visual Studio downloads page, with the C++ workload. Install CMake 3.20 or newer with the Windows `.msi` from cmake.org. | Build steps in the cards for tmnt-port, segasonic-port, KOF'98 Recomp and The House of the Dead: ReWrithe (MSVC with CMake). The cmake.org download page (Windows x86_64 .msi) and visualstudio.microsoft.com/downloads (Build Tools) were read 2026-11-13. The reviewed projects name Visual Studio 2022; the page now offers a newer Build Tools. |
| Python 3 and Git Required Windows | Python runs every helper script here: ROM checks, decompilers, scenario checks and configure.py. Git holds your code and pulls in toolkits as submodules.Path A Path B | Install Python 3.10 or newer from python.org and add it to `PATH`. Install Git for Windows from git-scm.com. | neogeorecomp README (Git, read 2026-11-13), dtk-template docs/dependencies.md (Python on PATH, read 2026-11-13), and the tmnt-port, OutRunners Port and segasonic-port cards (Python 3.10+). Both download pages answered on 2026-11-13. |
| Ninja Helpful Windows | A small, fast build runner. A dtk-template matching project builds with ninja after python configure.py.Path B | `pip install ninja`, or download it from the Ninja releases page and add it to `PATH`. Only needed for the Triforce matching route. | dtk-template docs/dependencies.md (read 2026-11-13) and the mkgp2-decomp README (python configure.py then ninja). |
| QEMU user mode and a PowerPC GCC in WSL Helpful WSL/Linux | qemu-ppc runs a 32-bit PowerPC Linux program on your PC. The big-endian check builds the headless game for PowerPC and runs every scenario under it, to catch byte-order bugs before a port.Path B | In WSL Ubuntu: `sudo apt install qemu-user gcc-powerpc-linux-gnu`. | outrunners-decomp README (read 2026-11-13) names the Debian and Ubuntu packages gcc-powerpc-linux-gnu and qemu-user. The arcade-decomp README says big_endian_scenarios.py uses qemu-ppc through WSL on Windows. The apt line itself was not run. |
| llvm-mingw Helpful Windows | A Clang compiler for Windows. Use it when a project needs GCC-style features that MSVC lacks, such as weak symbols for function overrides. Path A | Download `llvm-mingw-20261104-ucrt-x86_64.zip` from the releases page and unzip it to `C:\llvm-mingw`. | llvm-mingw latest release 20261104 with the ucrt x86_64 zip (read 2026-11-13). Used by: Super-Hang-On-Ultimate (build.ps1 -Toolchain C:\llvm-mingw), OpenGP's installer. |
| MinGW-w64 GCC in WSL Helpful WSL/Linux | GCC that builds Windows programs from Linux. Some arcade projects build only with GCC, or need a 32-bit Windows program. Path A | In WSL Ubuntu: `sudo apt install gcc-mingw-w64-i686 make python3` for 32-bit. Namco 22 Decompile uses `x86_64-w64-mingw32-gcc` for 64-bit builds. | Build steps in the VCThunder and Namco 22 Decompile cards (checked 2026-11-13). The mingw-w64.org page answered on 2026-11-13, but the install line comes from the VCThunder card, not from that page. Both projects also name an MSYS2 shell as another way. |
| SDL2 and SDL3 Required both | A library for the window, sound and game pads. SDL2 is most common here. SDL3 adds the SDL_GPU API that OpenGP and SpikeOut use for Vulkan and Metal.Path A Path B | On Windows, unpack `SDL2-devel-2.32.10-VC.zip` from the SDL releases page. tmnt-port looks for it in `build/downloads/sdl2/`. In WSL: `sudo apt install libsdl2-dev`. | SDL releases (read 2026-11-13): latest release-3.4.20 on 2026-11-03; newest SDL2 is release-2.32.10 with SDL2-devel-2.32.10-VC.zip. Setup path from the tmnt-port card. Used by: 11 of 12 reviewed projects. |
| ymfm Helpful both | BSD-licensed cores for Yamaha FM sound chips. It covers the YM2151, YM2610, YM2612 and YM3438 found on many arcade boards. Path A Path B | Add it as a submodule: `git submodule add https://github.com/aaronsgiles/ymfm ext/ymfm`. It needs only a C++14 compiler. | ymfm README (read 2026-11-13): chip list and C++14 note. Used by: KOF'98 Recomp, tmnt-port, segasonic-port, OutRunners Port. |
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.
GhidraMCP
Lets Claude list, decompile and rename functions in your open Ghidra project. The mkgp2-decomp maintainer works through a Ghidra MCP bridge. Use it for the map step on any board Ghidra supports.
Install `uv`. In Ghidra 12.1.4, use File, Install Extensions to add the `ghidra_12.1.4_PUBLIC_<date>_GhidraMCP.zip` from the latest release, and restart. Then run `claude mcp add ghidra -- uvx --from git+https://github.com/LaurieWired/GhidraMCP ghidra-mcp --ghidra-server http://127.0.0.1:8080/`.
gamedecomp-library
Searches a list of about 790 decomp and reverse engineering projects. Use it once in the scope step, because arcade has many scattered toolkits. Use with care: its scripts download data.
npx skills add madpai/game-decomp-compendium --skill gamedecomp-library -a claude-code
static-recomp-scope-plan
Makes you write down what correct means before work starts: the exact set, the scenes to test, and the numbers to hit. That is your 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 and code to files. Use it when you want no MCP server. Set GHIDRA_HOME yourself on Windows.
npx skills add trailofbits/skills-curated --skill ghidra-headless -a claude-code
static-recomp-av-compare
Scores your port's video and sound against recordings, scene by scene. Record the reference in MAME with -aviwrite. Its script paths need a fix for Claude Code.
npx skills add bgyss/switchrecomp --skill static-recomp-av-compare -a claude-code
native-platform-porting
Guides the port step of Path B. It warns Claude not to hide a small emulator inside the port, which matters when your C still reads board memory by address.
npx skills add lemiur/game-decompilation-skill-for-claude --skill native-platform-porting -a claude-code
verification-before-completion
Claude may not say done without running the check and showing it. On arcade, that means the hash compare output, not a screenshot that looks right.
npx skills add obra/superpowers --skill verification-before-completion -a claude-code
systematic-debugging
No fix until the cause is found, and a rethink after three failed fixes. It fits the first-differing-frame hunt in Path A.
npx skills add obra/superpowers --skill systematic-debugging -a claude-code
handoff
Writes a short note so a fresh session can carry on. Tell it to save inside your repo, not the temp folder. SpikeOut Recomp keeps the same kind of note in docs/handoff.md.
npx skills add mattpocock/skills --skill handoff -a claude-code
fix-one-frame-diff
This is the Path A unit loop as a skill. Every reviewed recomp found bugs this way, from SpikeOut's onepc-diff.ps1 to Super Hang-On's per-frame hashes.
A project skill in `.claude/skills/fix-one-frame-diff/SKILL.md`. It takes a script name. It runs both sides, finds the first differing frame and byte, dumps RAM there, names the code that wrote the byte, and stops. It fixes nothing until you approve the cause. It lists the files it may edit.
prove-one-function
This is the Path B unit loop as a skill. It keeps every worker on the same steps and the same check.
A project skill in `.claude/skills/prove-one-function/SKILL.md`. It takes an address. It edits only `symbols.txt`, runs `tools/decompile.py`, runs `select_scenarios.py --run` and `noise_report.py`, and writes the result to the ledger. Model its layout on mkgp2-decomp's `.claude/skills/mkgp2-match/SKILL.md`.
Claude setup for this system
For Path A, one unit is one mismatch: the first frame where your hash differs from the reference. Each unit ends with every input script matching. For Path B, one unit is one function at one address. Each unit ends with its scenarios passing and the noise count not rising. Chip stand-ins are their own units, checked against MAME's picture or sound. The ledger, not the chat, says what is next.
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 | Name the board, the chips, the MAME set and revision. Search for toolkits and rewrites. Choose matching or behavior checks. 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 | Write verify_rom.py, the asset extractor with its --check, check.cmd, and the reference runner with its input scripts. | Opus 5.5 effort medium | Sonnet 5.5 effort medium |
| Study the game file | Map every routine from the vectors, pointer scans and profile runs. Check the disassembler against MAME's. Fill symbols.txt with names and evidence. | Opus 5.5 effort high | Opus 5.5 effort mediumRaise to high for function edges and data layouts. A mistake there spreads. |
| Bulk units | Prove many small functions, one per worker, through symbols.txt and the scenarios. In Path A, fix simple missing chip registers. | 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 | Crossed jumps, code no scenario reaches, math chips with odd rounding, and code the game copies to RAM. Each gets proof or a written cause. | 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 | Write the board in C: video chips, sound chips, inputs, timers and protection chips. Each one is checked against MAME by itself. | Opus 5.5 effort high | Opus 5.5 effort mediumRaise to high for timing and graphics bugs. |
| Find a bug | Find the first differing frame, then the first differing byte, then the code that wrote it. Prove the cause 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 all scenarios on a clean clone and reads the diff. It hunts for edited hashes, skipped scenarios and edits to generated C. | 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 ledger counts, docs/unreachable.txt and notes/findings.md after each batch. | Haiku 5.5 effort medium | Haiku 5.5 effort medium |
How many sessions at once
Max 20x (now called Ultra)
Run one lead session and three to five workers, each in its own git worktree. In Path B, give each worker its own source file of functions. In Path A, give each worker one mismatch in a different part of the runtime. The House of the Dead project ran two or three sessions at a time and saw the weekly limit drain three times as fast. Run one verify session after each batch.
Max 5x (now called Max)
Run one lead session and one worker. Spend hard and debug jobs on the hours you watch. Start bulk batches at night, one function per worker. Scenario runs are slow, so let only one session run the full check at a time.
Lines for this project's CLAUDE.md
- Read SCOPE.md and PROGRESS.md at the start of every session. - Run `check.cmd` after every change. If it fails, the task is not done. - Never edit `build/generated/` or `src/`. Change `symbols.txt` or the generator, then regenerate. - ROM files, reference hashes, `tests/controls/` and `tools/core/` are read-only. - Never commit ROM data or anything made from it, including recompiled code. - Prove every same-output claim with hashes. Never judge a frame by eye. - Hardware reference order: MAME first, then <SECOND_REFERENCE>. Keep the copyright notice of any code you adapt. - No stub may return a fake value. A missing register or call stops the run and prints its address. - Enhancements default to off. The plain arcade build must still match the reference. - Every run and every wait has a time limit. Stop a batch at its first failure. - In a worktree, use the absolute path to the ROM folder: <ABSOLUTE_ROM_DIR>. - Change only what was asked. Write other problems in notes/parking-lot.md and do not fix them. - Commit only when I say so. Stage files by name, never with `git add -A`.
Keep the context small
- Give Claude one address, one ledger row and the first differing frame, not a whole trace.
- Never paste a MAME trace log. Let a subagent read it and return the first 20 lines that matter.
- Keep
CLAUDE.mdunder one page. Point todocs/hardware-notes.mdandnotes/findings.mdby name. - Clear the chat after each unit. The ledger and
PROGRESS.mdcarry the state. - Run local sessions for game work. A cloud session starts from a fresh clone and cannot see your ROM files.
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 learn which board the game uses and which path fits.
GOAL: Tell me if <GAME> (MAME set <SET>) can be brought to PC, and by which technique. READ FIRST: notes/mame-listsource.txt, notes/verifyroms.txt, the MAME driver <DRIVER_PATH> (top comment and machine setup only), notes/prior-work.md. FENCE: Edit only SCOPE.md and notes/. Do not download ROMs, BIOS files or tools. CHECK: Run `findstr /b /c:"Board:" SCOPE.md && findstr /b /c:"CPUs:" SCOPE.md && findstr /b /c:"Protection:" SCOPE.md && findstr /b /c:"Technique:" SCOPE.md && findstr /b /c:"Prior work:" SCOPE.md`. Done means all five lines print and it exits 0. CPUs gives each role and the video and sound chips. Protection says none or names the chip. Technique gives one reason against each other technique. BUDGET: Stop after 60 minutes. IF BLOCKED: If the board has no working MAME driver, or the set is encrypted with no known key, stop and say so plainly. OTHER PROBLEMS: Write them in notes/parking-lot.md. Do not fix them. REPORT: 1) DONE, PARTIAL or BLOCKED. 2) Your recommendation in one line, then the evidence. 3) Files changed. 4) Open questions. 5) New parking-lot items.
A good answer: DONE, then one technique. It cites the driver lines that name the chips, and lists toolkits like neogeorecomp or arcade-decomp if they fit.
Map the binary
After setup, to build the function ledger for the main program.
GOAL: Build ledger/functions.tsv for the <CPU> program of <SET>. READ FIRST: SCOPE.md, the Ghidra project <GHIDRA_PROJECT>, build/profile/misses.txt, build/reference/executed.txt. FENCE: Edit only ledger/, notes/map.md and tools/check_ledger.py. Do not write C. Do not rename symbols in Ghidra yet. CHECK: Run `python tools/check_ledger.py ledger/functions.tsv build/profile/misses.txt build/reference/executed.txt`. Done means it prints 0 missing and exits 0. Each row has address, size, kind and status. notes/map.md gives the row count by kind. BUDGET: Stop after 90 minutes. IF BLOCKED: If the Ghidra MCP server is down, stop and ask. Do not guess code from memory. OTHER PROBLEMS: Write them in notes/parking-lot.md. Do not fix them. REPORT: 1) DONE, PARTIAL or BLOCKED. 2) Counts and the executed-address check. 3) Files changed. 4) Unclear areas. 5) New parking-lot items.
A good answer: DONE with counts by kind. Zero executed addresses fall outside a row. Code copied to RAM is marked as its own kind.
Unit loop: fix one frame mismatch (Path A)
During the Path A grind, once per mismatch.
GOAL: Make tests/controls/<SCRIPT>.txt match the reference past frame <FRAME>. READ FIRST: CLAUDE.md, PROGRESS.md, build/diff/<SCRIPT>.txt, notes/findings.md. FENCE: Edit only tools/recomp/ and runtime/. Never edit build/generated/, reference hashes or check scripts. CHECK: Run `check.cmd`. Done means every script in tests/controls/ matches, and <SCRIPT> passes frame <FRAME>. BUDGET: Stop after 3 failed fixes or 60 minutes. IF BLOCKED: Stop. Give me the first differing frame, the byte, the code that wrote it, and your best cause. OTHER PROBLEMS: Write them in notes/parking-lot.md. Do not fix them. REPORT: 1) DONE, PARTIAL or BLOCKED. 2) Paste the check summary. 3) Files changed. 4) The proven cause. 5) New parking-lot items.
A good answer: DONE with the check summary pasted. The cause names an address and a chip register or an instruction, and the fix is in the recompiler or runtime.
Unit loop: prove one function (Path B)
During the Path B grind, once per function.
GOAL: Make <FUNCTION> at <ADDRESS> readable and proven. READ FIRST: CLAUDE.md, the ledger row for <ADDRESS>, symbols.txt, src/<FILE>.c (read only). FENCE: Edit only the symbols.txt entries for <FUNCTION> and the data it reads. Never edit src/ by hand. CHECK: Run `python tools/decompile.py`, `python tools/select_scenarios.py --run` and `python tools/noise_report.py`. Done means the scenarios pass and the noise count does not rise. BUDGET: Stop after 5 tries without progress. IF BLOCKED: Stop. Name the first differing frame or the noise line that stays. OTHER PROBLEMS: Write them in notes/parking-lot.md. Do not fix them. REPORT: 1) DONE, PARTIAL or BLOCKED. 2) Paste the scenario and noise summaries. 3) Files changed. 4) What is left. 5) New parking-lot items.
A good answer: DONE, with real names and types added, the selected scenarios passing, and the noise count the same or lower.
Batch dispatch
In a lead session, to hand out the next batch of units to workers.
GOAL: Start <N> workers on the next <N> units from ledger/functions.tsv. READ FIRST: CLAUDE.md, PROGRESS.md, ledger/functions.tsv, notes/parking-lot.md. FENCE: Edit only ledger/claims.tsv. Each worker gets its own worktree under .claude/worktrees/ and one source file nobody else owns. Do not do unit work yourself. CHECK: After the batch, merge each worker's branch one at a time and run `check.cmd` after each merge. Done means every merged unit passes on the merged branch. BUDGET: Stop the batch at its first failed merge. IF BLOCKED: Stop. Do not work around it. Tell me which merge failed and why. OTHER PROBLEMS: Write them in notes/parking-lot.md. Do not fix them. REPORT: 1) DONE, PARTIAL or BLOCKED. 2) A table of unit, worker, result. 3) Files changed. 4) Units to retry as hard jobs. 5) New parking-lot items.
A good answer: A table with one row per unit. Each merged unit passed the check after its own merge. Failed units are listed for the hard queue.
Debug with evidence
When the game crashes, hangs or draws wrong and the cause is not known.
GOAL: Find the proven cause of <SYMPTOM> in tests/controls/<SCRIPT>.txt. Do not fix it yet. READ FIRST: CLAUDE.md, the last 20 lines of build/logs/<SCRIPT>.log, build/diff/<SCRIPT>.txt. FENCE: Edit only notes/findings.md and temporary files in build/tmp/. No code changes. CHECK: Run `check.cmd` twice. Done means both runs report the same first differing frame, and notes/findings.md names that frame, the first differing byte or register, and the instruction that caused it, with addresses. BUDGET: Stop after 60 minutes. IF BLOCKED: Stop. Tell me what you ruled out and what you need next, such as a new MAME capture. OTHER PROBLEMS: Write them in notes/parking-lot.md. Do not fix them. REPORT: 1) DONE, PARTIAL or BLOCKED. 2) The cause in one sentence, then the evidence. 3) Files changed. 4) A proposed fix for me to approve. 5) New parking-lot items.
A good answer: One cause with a frame number, an address and the value on each side. No code was changed.
Verify and hunt for cheating
After every batch, in a fresh session that did not do the work.
GOAL: Check that the units claimed in PROGRESS.md since <COMMIT> are real. READ FIRST: CLAUDE.md, `git diff <COMMIT>..HEAD --stat`, PROGRESS.md. FENCE: Read only. Do not fix anything. CHECK: On a fresh clone, run `check.cmd` and the full scenario check. Then look for edited reference hashes, removed or shortened scenarios, edited generated C, stubs that return fixed values, enhancements turned on by default, and edits outside each unit's fence. BUDGET: Stop after 60 minutes. IF BLOCKED: Stop. Tell me what you could not check and why. OTHER PROBLEMS: Write them in notes/parking-lot.md. Do not fix them. REPORT: 1) PASS or FAIL. 2) Paste the check outputs. 3) Each problem with file and line. 4) Claims you could not confirm. 5) New parking-lot items.
A good answer: PASS or FAIL with pasted outputs. Every problem names a file and line. Nothing was edited.
Status report
Once a week, or before you plan the next batch.
GOAL: Report project status with numbers from the ledger. READ FIRST: PROGRESS.md, ledger/functions.tsv, tests/controls/, notes/parking-lot.md. FENCE: Read only. No prose padding. CHECK: Get each count with a command, such as `findstr /c:"proven" ledger\functions.tsv | find /c /v ""`, and get the scenario count from `check.cmd`. Done means every number has the file or command beside it. BUDGET: Stop after 15 minutes. IF BLOCKED: Stop. Say which number you could not get. REPORT: 1) Units done, in progress and blocked, by kind. 2) Scenarios passing out of total. 3) The noise count or mismatch count against last week. 4) The top three blockers. 5) Parking-lot items for me to decide.
A good answer: Short lists of numbers, each with its source. No claims without a file or command behind them.
Session handoff
At the end of a session, or when the context is getting full.
GOAL: Write what the next session needs into the repo files. READ FIRST: PROGRESS.md, notes/findings.md, the current task card. FENCE: Edit only PROGRESS.md, notes/findings.md and docs/handoff.md. CHECK: Run `findstr /b /c:"Current unit:" docs\handoff.md && findstr /b /c:"Next step:" docs\handoff.md && findstr /b /c:"Starting prompt:" docs\handoff.md`. Done means all three print. The file also holds the last check output and what was tried and failed. BUDGET: Stop after 15 minutes. IF BLOCKED: Stop. Say what you could not record. OTHER PROBLEMS: Write them in notes/parking-lot.md. Do not fix them. REPORT: 1) DONE, PARTIAL or BLOCKED. 2) The next step in one line. 3) Files changed.
A good answer: A handoff file a fresh session can follow alone. It names dead ends, so no one retries them.
Blocked or cannot deliver
When Claude keeps trying the same thing, or says something should work.
GOAL: Give me a plain answer on <TASK>: can it be done now, and how? READ FIRST: the last task card, its report, notes/findings.md. FENCE: Read only. Do not try again. CHECK: Run `check.cmd` once and paste its last 20 lines. Do not change any file first. Your answer is one of DONE, PARTIAL or BLOCKED, with that output as proof. 'Should work' is not an answer. BUDGET: Stop after 15 minutes. IF BLOCKED: Say exactly what is missing: a MAME capture, a chip document, a ROM revision, or my decision. OTHER PROBLEMS: Write them in notes/parking-lot.md. Do not fix them. REPORT: 1) DONE, PARTIAL or BLOCKED. 2) What cannot be done and why. 3) Two or three options, each with its cost in time. 4) Your pick and one reason.
A good answer: BLOCKED or PARTIAL in the first word, then a clear reason and two or three costed options.
Scope change request
When a task needs Claude to leave its fence, such as changing the reference or a toolkit.
GOAL: Ask me before you change anything outside your fence for <TASK>. READ FIRST: SCOPE.md, the current task card. FENCE: Change nothing until I answer. Read only. CHECK: Run `git status --short`. Done means it lists no file you changed, and your request names the files you want to change, why, what could break, and the check that proves it did not. BUDGET: Stop after 10 minutes. IF BLOCKED: Stop and wait for my answer. OTHER PROBLEMS: Write them in notes/parking-lot.md. Do not fix them. REPORT: 1) The change you want, in one line. 2) The files. 3) The risk. 4) The check that proves it safe. 5) What you will do if I say no.
A good answer: A short request that names files and a check. No files were changed before the owner said yes.
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 in build/generated/ or src/ to make a frame match. | Generated C is never edited by hand. Change symbols.txt, the recompiler or the runtime, then regenerate. Block both folders with a hook. |
| Claude says a picture looks right and calls the unit done. | Prove every same-output claim with hashes from a run you pasted. A screenshot is never proof. |
| Claude adds a stub that returns a fixed value, so the game gets past a chip check or protection chip. | No stub may return a fake value. Model the chip from MAME's code, or stop the run and print the address. |
| Claude turns on widescreen or another extra by default while fixing something else. | Enhancements default to off and sit behind a switch. The plain build must match the reference. |
| Claude copies ROM files or extracted assets into the repo, or into a worktree, to make a test run. | Never commit ROM data or anything made from it. Use the absolute ROM path. A git check fails on any ROM-derived file. |
| A test loop waits forever for a string that never appears, and burns hours. | Every run and every wait has a time limit. A batch stops at its first failure. Check the build is new before trusting a result. |
| Claude cleans up or refactors nearby code while fixing one mismatch. | Change only what was asked. Write other problems in notes/parking-lot.md and do not fix them. |
Problems other projects hit
Static search finds only part of the code. In SpikeOut, descent from the reset vector found about a quarter of it.
Fix: Add a pointer scan, make each instruction after a call an entry point, and run profile builds that log interpreter misses.
Seen in: SpikeOut Recomp, KOF'98 Recomp, The House of the Dead: ReWrithe
The game copies code to RAM at boot and runs it there. A static recompiler cannot see that code.
Fix: Let the interpreter run it, and mark it as its own kind in the ledger. KOF'98 interprets about 11.7 million instructions at start-up this way.
Seen in: KOF'98 Recomp
A CMake build with no build type was slow. Namco 22 ran about 1.8 times slower at -O0.
Fix: Force a default build type such as Release in CMakeLists.txt.
Seen in: Namco 22 Decompile, OutRunners Port
Translated C broke when built without -fno-strict-aliasing or -fwrapv, and clang rejected it.
Fix: Build translated code with GCC and both flags. Check the optimised build against -O0 with reference frames.
Seen in: Namco 22 Decompile
Comparing whole frames was the wrong cut for a game with a math coprocessor. Routines run mid-frame, and the chip's state is not in RAM.
Fix: Capture registers and RAM at each routine's entry and return in MAME, and prove routines one at a time.
Seen in: The House of the Dead: ReWrithe
MAME renamed chip files between versions, and merged sets put chips in other folders. ROM loading broke.
Fix: Find each chip by CRC32 and size inside the zip, not by file name.
Seen in: Namco 22 Decompile, CannonBall DX
The test tool and the shipped game ran network play in different ways. Tests passed on code players never ran.
Fix: Put every step in shared code that both the headless runner and the window use.
Seen in: SpikeOut Recomp
Some games need a protection or security chip. SegaSonic has an FD1149 chip, and Initial D needs a .pic dump.
Fix: Model the chip from MAME's notes, or require the player's own dump. List the chip in SCOPE.md on day one.
Seen in: segasonic-port, Initial D Arcade Stage 3 Recompiled
Two course files went missing because disc file names were cut short. The game read a null table and drew rainbow textures.
Fix: Check extracted disc names against the short-name limit of the disc format before you blame the renderer.
Seen in: Initial D Arcade Stage 3 Recompiled
Cloning without submodules leaves out tools/core and lan/, so tools fail or multiplayer says NO NETWORK.
Fix: Clone with --recursive, or run git submodule update --init.
Seen in: OutRunners Port, segasonic-port
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 | 4 to 12 weeks | 10 to 30 weeks | 1 to 3 years | medium confidence |
| Path B: full decomp | 3 to 9 months | 7 to 22 months | 2 to 6 years | low confidence |
Read this first
These numbers assume one medium arcade game and one person who gives 10 to 15 hours a week. Three dated AI projects reached a first release two to six weeks after their first public commit. KOF'98 took one day from its new repo, but on a toolkit begun seven months earlier. Their authors were skilled, worked more hours, and often hid earlier work. So the ranges are wider than the raw dates. The Path B numbers rest on only two projects with stated AI use, so their confidence is low. A small 68000 game with a toolkit can be much faster. A 3D board with no toolkit can be much slower.
What makes it faster or slower
- The board. A 68000 game with a toolkit is much faster than a 3D board with no toolkit.
- How many chips run code. Each sound chip, math chip or I/O chip needs its own translation.
- Protection and encryption. A security chip can block a project until someone models it.
- Whether MAME emulates the board well. A weak MAME driver means no good answer key.
- Program size. TMNT has 2,439 functions and OutRunners 762, while 3D games have thousands more.
- For Path B, the compiler. Assembly games need behavior checks, which need many scenarios to cover the code.
The facts behind these numbers
| Project | What happened | Link |
|---|---|---|
| SpikeOut Recomp (Model 3) | First commit 2026-10-19. First release 2026-11-05. Two contributors. Claude Code is a co-author on every commit. | source |
| OpenGP (Triforce) | First commit 2026-09-23. First public release 2026-11-02, after internal releases. Three contributors. Most code written by Claude Code. | source |
| VCThunder (Midway, x86) | Repo created 2026-08-19, first commit 2026-09-04, first release 2026-10-15. README says a few hundred hours, built with Claude. | source |
| KOF'98 Recomp (Neo Geo) | Repo created 2026-10-18, first release 2026-10-19, on the neogeorecomp toolkit whose repo was created 2026-03-21. Claude Code credited. | source |
| The House of the Dead: ReWrithe (Model 2) | MAME recordings dated 2026-10-07. Whole game playable at release 2026-11-01. Planned as 12 sessions, two or three at a time. Claude credited. | source |
| tmntarcade-decomp (Konami 68000) | Repo created 2026-10-29. By 2026-11-04 the README says all 2,439 functions decompiled and matching 18 scenarios. Built on a shared toolkit. The port's plan credits an agent. | source |
| outrunners-decomp (Multi 32) | First commit 2026-10-20. At review on 2026-11-13, 643 of 762 functions (84.4%) were free of CPU leftovers. AI use not stated. | source |
| mkgp2-decomp (Triforce, matching) | Repo created 2026-05-17. 94.61% of code matched on 2026-11-13, after 4,534 commits by three contributors with Claude and Codex agents. | source |
| CannonBall (OutRun, no AI) | Part-time hand port. A 2012-08-02 post says decompiling began three years earlier. The release post lists 1 year 10 months to convert the decompiled code to C++. Tag v0.02 marks a commit from 2012-09-30. | source |
| Initial D Arcade Stage 3 Recompiled (NAOMI 2) | Repo created 2026-08-16. First release 2026-09-01. One contributor. AI use not stated. | source |
Projects reviewed (12)
We took the top-starred project for each of the 12 most-starred arcade games on RecompLauncher. The catalog lists 24 arcade projects for 23 games, and the store flags 17 for AI use. For Path B we also read naari3/mkgp2-decomp from outside the catalog.
What each project teaches
Namco 22 Decompile
Namco 2x Systems (13 games)
Started: 2026-09. First release: 2026-09. Code: C.
AI: catalog flag only (set by the store); the repo says nothing about AI
Lesson: One shared engine can carry many games on one board. Translate sound and DSP chips at build time from the player's ROM, and check each against its interpreter.
Initial D Arcade Stage 3 Recompiled
Initial D Arcade Stage 3
Started: 2026-08. First release: 2026-09. Code: C++ and Python.
AI: none stated
Lesson: Tie every claim to one exact build hash, and keep a ledger of what is shown versus what is guessed. The public repo cannot build the game itself.
The House of the Dead: ReWrithe
The House of the Dead
Started: 2026-10. First release: 2026-11. Code: C++.
AI: yes: README says Claude assisted with reverse engineering and rewriting; parallel worker sessions on claude/* branches
Lesson: Prove routines one call at a time with MAME captures of entry and return state. Mark every unproven routine UNVERIFIED so a script can list them.
OutRunners Port
OutRunners
Started: 2026-10. First release: 2026-10. Code: C and Python.
AI: none stated
Lesson: Start with a one-line-per-instruction scaffold that already matches MAME, then grow readable C from it. A counted noise baseline keeps readability from sliding back.
CannonBall DX
OutRun
Started: 2012-09. First release: 2026-08. Code: C++.
AI: yes: README says the fork's extra work had help from ChatGPT and Claude
Lesson: If a mature rewrite of your game exists, build on it instead of starting over. Find ROM files by CRC32, not by name.
tmnt-port
Teenage Mutant Ninja Turtles
Started: 2026-10. First release: 2026-11. Code: C.
AI: yes: console/PLAN.md credits two fixes to an agent's work, checked by its diff; no agent files
Lesson: Build the reference first, then write scenarios that reach every area and boss. Keep the arcade build untouched and list each replaced function in one file.
VCThunder
Hydro Thunder; Offroad Thunder
Started: 2026-09. First release: 2026-10. Code: C.
AI: yes: README says it began as a personal challenge to see how far Claude could be used to vibe code it
Lesson: When the board's chip is x86, you may not need to decompile. Run the original code in place and replace only the kernel, graphics and I/O around it.
OpenGP
Mario Kart Arcade GP
Started: 2026-09. First release: 2026-11. Code: C++.
AI: yes: README 'Made with AI' says most code and docs are written by Claude through Claude Code
Lesson: Import names instead of inventing them: from a signature database, from a sister game's decomp, and from identical code in the sister game.
SpikeOut Recomp
SpikeOut: Final Edition
Started: 2026-10. First release: 2026-11. Code: C++.
AI: yes: README says Claude Code is a contributor to every commit; CLAUDE.md and docs/handoff.md hold the rules Agent files: CLAUDE.md.
Lesson: Count cycles at build time so every run is bit-identical. That one choice makes hash proofs, replays, save states and rollback netplay possible.
SegaSonic the Hedgehog: Native Port
SegaSonic the Hedgehog
Started: 2026-10. First release: 2026-10. Code: C, Python and C++.
AI: catalog flag only (set by the store); the repos do not mention AI
Lesson: Make coverage a goal. Count which instructions the scenarios run, and write down why each unrun piece of code never runs.
KOF'98 Recomp
The King of Fighters '98
Started: 2026-03. First release: 2026-10. Code: C.
AI: yes: README credits Claude Code as AI pair programmer; every change tested by the owner
Lesson: Start from an existing toolkit and its sample game. Require the --verify mismatch count, here 0 over 12.1 million blocks, in every pull request.
Super-Hang-On-Ultimate
Super Hang-On
Started: 2026-11. First release: 2026-11. Code: C, Objective-C and Python.
AI: none stated
Lesson: Reuse the interpreter's own instruction handlers in the recompiler. Then a per-frame hash of all RAM and registers can prove the two are identical.
What we could not check
- How AI was used in most projects. Only SpikeOut Recomp publishes its agent rules. The others give one README line or nothing.
- Whether Namco 22 Decompile, SegaSonic or Super Hang-On used AI. The store flags two of them, but the repos say nothing.
- The name and code of Namco 22 Decompile's main CPU translator. Only its output is public.
- The original compiler for any reviewed V60, i960, SH-4, Model 3 or Namco game. No project names one.
- That most 1980s arcade games were written in assembly. We confirmed it only for OutRun, from the CannonBall README.
- The mkgp2-decomp compiler. Its README calls CodeWarrior 2.7 a guess still to be re-checked.
- The total hours behind any project. Most hide early work, and only VCThunder gives a rough figure.
- A reported Sonic the Fighters decomp from May 2025. The news page returned an error, so we could not read it.
- The MinGW-w64
aptinstall line. It comes from the VCThunder card, not from mingw-w64.org. - That the
static-recomp-av-compareskill works with MAME's-aviwriterecordings. We did not test it. - The command-line arguments of arcade-decomp's scripts. Its README names each script and some switches (
--video,--sound,--run,--big-endian), but no full usage. No script was run here. - Which JDK Ghidra 12.1.4 needs for
GhidraMCP. The Ghidra README read here is for 12.2, which asks for JDK 25. - That
check_ledger.py,golden.cmd,check.cmdswitches andports/<TARGET>/check.cmdwork as described. They are scripts you write; no reviewed project ships them under these names. - None of the commands in this guide were run on a real game here. They come from tool docs and project READMEs.
- The 1971 date. It comes from the site's shared platform table, not from a source we read.
Sources
- MAME release page (0.289)
- MAME debugger docs
- MAME execution debugger commands (trace)
- MAME Lua scripting docs
- MAME Lua reference: common (frame notifier)
- MAME Konami TMNT driver (68000 code on even and odd chips)
- MAME Sega System 32 driver (V60 and V70)
- MAME command-line options
- MAME Namco System 22 driver
- MAME Namco System 23 driver
- MAME Sega Model 2 driver
- MAME Sega Model 3 driver
- arcade-decomp README
- tmntarcade-decomp README
- outrunners-decomp README
- segasonic-decomp README
- neogeorecomp README
- mkgp2-decomp README, CLAUDE.md and AGENTS.md
- CannonBall README
- Reassembler blog, rewrite posts
- Musashi README
- Ghidra README
- GhidraMCP README
- Supermodel README
- objdiff README
- decomp-toolkit README
- dtk-template README
- dtk-template dependencies (Python, Ninja)
- mkgp2-decomp matching skill (Ghidra MCP note, objdiff)
- Python downloads
- Ninja releases
- Visual Studio downloads (Build Tools)
- CMake downloads
- uv (needed by GhidraMCP)
- ymfm README
- llvm-mingw releases
- SDL releases
- SpikeOut Recomp repo (CLAUDE.md, docs/handoff.md)
- OpenGP repo