Skip to the page
It's Decomplicated
Nintendo, 1998

Game Boy Color decomp guide

An experimental free recompiler gets Color games running fast. A full port still takes many months.

What this system is like

The Game Boy Color uses the same 8-bit chip as the Game Boy. It adds color, more memory and a double-speed mode. The three reviewed recomps that state a size use ROMs of 2 to 4 MB. That is bigger than most Game Boy games, but still small.

For Path A, the free tool gb-recompiled does most of the work. Its gbrecomp program turns a ROM into C code plus a runtime. A runtime is the code that stands in for the console. Two reviewed projects used it, and Shantae Recomp used a fork of it. One more built its own translator.

Path B is different here. These games were written in assembly, not C. So the community builds a byte-matching assembly listing with RGBDS, like pret/pokecrystal. Many Color games already have one. The two best ports then rewrote the game routine by routine, with a check for each routine.

Recomp work runs on Windows or in WSL. The disassembly path is smoothest in WSL. Expect days to weeks for Path A, and many months for Path B.

Color adds hardware the Game Boy lacks

The Color has a double-speed mode, banked work RAM, a second VRAM bank and HDMA. HDMA is a fast copy into video memory. gb-recompiled supports these, but its GBC.md lists gaps. For example, it does not run the Color boot ROM.

The games were assembly, not C

The official kit was an assembler called ISAS. No reviewed project found C to match. On this system, a full decomp is a labeled assembly listing that rebuilds the same bytes.

Many games already have disassemblies

pret has Pokemon Gold, Silver, Crystal and both card games. Oracle of Ages and Seasons and Wario Land 3 have them too. A finished one gives you real names, and sometimes a ready .sym file. Check before you start.

Banks hide the real address

Only one switchable ROM bank and one work RAM bank are visible at a time. The same address can mean different code in different banks. Name every function as bank plus address, like 03:47f2.

Script engines hide code

Many Color games run small script languages inside the game. Static analysis misses the code those scripts reach. Shantae Recomp missed two script opcodes until real play showed them.

Timing can break a rewrite

Some games depend on exact cycle counts. The Oracles Project tried a static recomp first. It moved to an emulator core when interrupt timing kept breaking frames.

Two paths

Path A

Get it running on PC

You run gbrecomp on your own .gbc ROM. It writes a C project and copies in its runtime. You build it with CMake and SDL2. Then you fix missed code one address at a time. A built-in differential test checks each fix.

Go to the steps

Path B

Full decomp for porting

Color games were assembly, so the matching source is labeled assembly. You build a listing that rebuilds the exact ROM with RGBDS. Then you name and document every routine. Last, you port the game by rewriting each routine in C or Rust. A per-routine check against the original code proves each rewrite. Oracles Decomp and Pokemon TCG 2 Native Port both did this, starting from finished disassemblies.

Go to the steps

Path A: Get it running on PC

You run gbrecomp on your own .gbc ROM. It writes a C project and copies in its runtime. You build it with CMake and SDL2. Then you fix missed code one address at a time. A built-in differential test checks each fix.

Method: static recompilation

Pick this first. It gives you a playable PC build in days to weeks. If your game has a pret-style disassembly, feed its .sym file to the recompiler for real names.

  1. Pick the game and write the scope

    You pin down the exact ROM and goal. A different revision is a different program. Then you look for prior work. A finished disassembly gives you real names for free.

    • Hash your own dump in WSL: sha1sum roms/game.gbc and sha256sum roms/game.gbc. Shantae Recomp and Resident Evil Gaiden: Recompiled both check SHA-256.
    • Read the header in WSL: od -An -tx1 -j 0x143 -N 7 roms/game.gbc prints bytes $0143 to $0149. The first is the Color flag. $80 means the game also runs on the old Game Boy. $C0 means Color only. gbrecomp picks its mode from this byte.
    • The fifth byte is $0147, the cartridge type. Pan Docs lists the MBC values.
    • Search pret.github.io, GitHub and RecompLauncher for a disassembly or a recomp of your game.
    • Write SCOPE.md: game, revision, SHA-1, SHA-256, size, Color flag, MBC, goal, technique, first milestone and an out-of-scope list.
    Prompt for Claude
    GOAL: Draft SCOPE.md for a Game Boy Color static recomp of <GAME> (<REGION> <REVISION>).
    READ FIRST: the ROM header bytes $0100-$014F of `<ABSOLUTE_PATH_TO_ROM>` (read only), https://gbdev.io/pandocs/The_Cartridge_Header.html, https://pret.github.io/
    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>`, `sha256sum <ABSOLUTE_PATH_TO_ROM>` and `od -An -tx1 -j 0x143 -N 7 <ABSOLUTE_PATH_TO_ROM>`, and paste all three. The last one prints header bytes $0143 to $0149. Done means SCOPE.md has the game, revision, both hashes, size in bytes, the $0143 Color flag, the MBC type from $0147, goal, technique (gb-recompiled), first milestone and its check, and an out-of-scope list.
    BUDGET: Stop after 30 minutes of searching for prior work.
    IF BLOCKED: Stop. Do not guess a revision or a hash. Tell me what is missing.
    OTHER PROBLEMS: Write them in `notes/parking-lot.md`. Do not fix them.
    REPORT: 1) DONE, PARTIAL or BLOCKED. 2) Proof: paste the hash output and the header fields. 3) Files changed. 4) Prior work found, with links and the hash each one builds. 5) New parking-lot items.
    Done whengrep -c <SHA256> SCOPE.md prints 1 or more, and you approved SCOPE.md.

    Proof this works: Shantae Recomp, Resident Evil Gaiden: Recompiled, https://gbdev.io/pandocs/The_Cartridge_Header.html

  2. Set up the repo and the recompiler

    You get gbrecomp and lay out a small repo. The repo holds only your config and code. The ROM and the generated C stay out of git. You also write one check command.

    • In WSL: sudo apt install build-essential cmake ninja-build libsdl2-dev git python3.
    • Pin the source: git submodule add https://github.com/arcanite24/gb-recompiled. You need its tools/ folder later. Release v0.1.0 also has gb-recompiled-linux-x64.tar.gz and gb-recompiled-windows-x64.zip, but it is older than the docs.
    • To build from source: cmake -G Ninja -S gb-recompiled -B gb-recompiled/build then ninja -C gb-recompiled/build. The program is gb-recompiled/build/bin/gbrecomp; add that folder to PATH.
    • Always generate with --output-prefix game. Without it, file and program names come from the ROM's file name. With it, the program is always output/game/build/game.
    • Add *.gb, *.gbc, *.sav, roms/, output/ and logs/ to .gitignore before the first commit.
    • Write tools/check.sh. It regenerates, builds, and runs --headless --limit-frames 120. That is the smoke run the tool's own AGENTS.md asks for.
    • Write CLAUDE.md, PROGRESS.md and notes/parking-lot.md.
    Prompt for Claude
    GOAL: Make one command, `tools/check.sh`, that regenerates, builds and smoke-runs the recomp of <GAME>.
    READ FIRST: `SCOPE.md`, `gb-recompiled/README.md`, `gb-recompiled/AGENTS.md` (section "Required validation for generated behavior").
    FENCE: Edit only `tools/check.sh`, `.gitignore`, `CLAUDE.md`, `PROGRESS.md`. Do not edit anything inside `gb-recompiled/`. Do not add features or tools I did not ask for.
    CHECK: Run `bash tools/check.sh`. Done means it runs `gbrecomp <ABSOLUTE_PATH_TO_ROM> -o output/game --output-prefix game`, builds with CMake and Ninja, runs `--headless --limit-frames 120`, and exits 0. Then break the build on purpose and show the script exits non-zero.
    BUDGET: Stop after 3 failed tries at the same error.
    IF BLOCKED: Stop. Do not work around a missing package. Tell me the exact error and the command.
    OTHER PROBLEMS: Write them in `notes/parking-lot.md`. Do not fix them.
    REPORT: 1) DONE, PARTIAL or BLOCKED. 2) Proof: paste both check outputs. 3) Files changed. 4) What is left. 5) New parking-lot items.
    Done whenbash tools/check.sh exits 0, and exits non-zero when you break the build.

    Proof this works: Pokemon Pinball Recompiled, Resident Evil Gaiden: Recompiled, https://github.com/arcanite24/gb-recompiled/blob/main/AGENTS.md

  3. Map the ROM and get names

    You give the recompiler real names and real code starts. Many Color games have a disassembly whose .sym file names every routine. If none exists, you find dispatchers, script engines and RAM code in Ghidra. Each one goes in a list with its bank and address.

    • If a pret disassembly exists, get the .sym file for your exact revision. pret pushes them to a symbols branch. For example, pokecrystal11.sym is Crystal 1.1.
    • Pass it when you generate: gbrecomp roms/game.gbc --symbols notes/game.sym -o output/game --output-prefix game. The tool's AGENTS.md says to use symbols for names, not as proof that every label is code.
    • If there is none, install Ghidra 11.3.2 with JDK 21. Add the GhidraBoy zip built for 11.3.2. GhidraBoy maps banked ROM, banked work RAM and the Color-only registers.
    • GhidraBoy cannot tell which bank a call into $4000-$7FFF reaches. Its README says you set the bank by hand for each such call.
    • List interrupt vectors, rst handlers, jump tables, script interpreters and code copied into RAM. Write each in notes/map.md as bank:address with a reason.
    • Check each script interpreter's opcode table in full. Shantae's first scanner missed opcodes 2E and 32.
    Prompt for Claude
    GOAL: Write `notes/map.md`: every dispatcher, script interpreter, bank-switch routine and RAM-copied routine in <GAME>, as bank:address with evidence.
    READ FIRST: `SCOPE.md`, `notes/game.sym` if it exists, Ghidra project `<GHIDRA_PROJECT>` with GhidraBoy loaded, https://gbdev.io/pandocs/Memory_Map.html
    FENCE: Edit only `notes/map.md`. Do not edit generated C. Do not change recompiler code.
    CHECK: Write one entry per line: `bank:address type name proof: <listing or .sym line>`. Then run `grep -vc 'proof:' notes/map.md`; it must print 0. Done means the file lists the five interrupt vectors, each rst handler used, the bank-switch routine, every jump table and script opcode table found, and every routine copied to RAM, each with proof.
    BUDGET: Stop after 90 minutes, or after 20 minutes with no new entry.
    IF BLOCKED: Stop. If Ghidra or GhidraBoy is not loaded, say so and stop. Do not decode bytes by hand.
    OTHER PROBLEMS: Write them in `notes/parking-lot.md`. Do not fix them.
    REPORT: 1) DONE, PARTIAL or BLOCKED. 2) Proof: counts per entry type. 3) Files changed. 4) Addresses you could not explain. 5) New parking-lot items.
    Done whenEvery line in notes/map.md is one entry with a proof: field, so grep -vc 'proof:' notes/map.md prints 0. The list covers vectors, dispatchers, script tables, bank switches and RAM routines.

    Proof this works: Shantae Recomp, The Oracles Project, https://github.com/pret/pokecrystal/tree/symbols, https://github.com/Gekkio/GhidraBoy

  4. Boot to the first frame

    You run the recompiler on the whole ROM and build it. The game should open a window and reach its title screen. You save frame dumps as proof. This is the smallest full run.

    • Generate: gbrecomp roms/game.gbc -o output/game --output-prefix game. Add --symbols notes/game.sym if you have one.
    • Build: cmake -G Ninja -S output/game -B output/game/build then ninja -C output/game/build.
    • Make the log folder first: mkdir -p logs.
    • Run headless in Color mode with frame dumps: ./output/game/build/game --headless --model cgb --limit-frames 600 --dump-frames 60,300,600 --screenshot-prefix logs/shot. Each dump is a PPM image, like logs/shot_00300.ppm.
    • Open the same frames in SameBoy and compare. The runtime skips the Color boot ROM, so early frames can shift a little. Pokemon TCG 2 Native Port saw a similar boot-timing shift against SameBoy.
    • Keep the generated output/game/game_metadata.json file. It lists every function by bank and address.
    Prompt for Claude
    GOAL: Show <GAME> reaching its title screen in the recompiled build, in Color mode.
    READ FIRST: `SCOPE.md`, `notes/map.md`, `gb-recompiled/RUNTIME.md` (Execution options), `gb-recompiled/GBC.md` (Known gaps).
    FENCE: Edit only `tools/check.sh` and `PROGRESS.md`. Never edit files under `output/`. Generated C is a build artifact.
    CHECK: Run `bash tools/check.sh`, then `mkdir -p logs && ./output/game/build/game --headless --model cgb --limit-frames 600 --dump-frames 60,300,600 --screenshot-prefix logs/shot`. Then run `test -f logs/shot_00300.ppm`. Done means both commands exit 0 and that frame shows the title screen.
    BUDGET: Stop after 3 failed builds with the same error.
    IF BLOCKED: Stop. Do not patch generated C. Tell me the first error line, the command, and what you tried.
    OTHER PROBLEMS: Write them in `notes/parking-lot.md`. Do not fix them.
    REPORT: 1) DONE, PARTIAL or BLOCKED. 2) Proof: the run output and the paths of the three frame files. 3) Files changed. 4) Interpreter fallback lines seen in the log. 5) New parking-lot items.
    Done whenThe headless run exits 0 and test -f logs/shot_00300.ppm exits 0. Then you confirm by eye that the frame is the title screen.

    Proof this works: Pokemon Pinball Recompiled, Resident Evil Gaiden: Recompiled, https://github.com/arcanite24/gb-recompiled/blob/main/GBC.md

  5. Close the code gaps

    Code the analyzer missed runs on the built-in interpreter instead. That still works, but it hides gaps. You log each fallback address, prove it is real code, and add it as an entry point. You repeat until a full play log shows no fallbacks.

    • Record a real route through the game: ./output/game/build/game --record-input logs/route1.input. Random input rarely gets far. Pokemon TCG 2 Native Port used the game's hidden debug menu to reach duels.
    • Replay it and log fallbacks: ./output/game/build/game --headless --model cgb --limit-frames <N> --input "$(cat logs/route1.input)" --log-file logs/interp.log --log-frame-fallbacks --report-interpreter-hotspots. Set <N> to the route's length in frames.
    • Summarize: python3 gb-recompiled/tools/summarize_interpreter_log.py logs/interp.log. When it prints No interpreter fallback recorded, the route is clean.
    • Check each address in Ghidra. Then regenerate with --add-entry-point <BANK>:<ADDR>. The tool reads the address as hex. It reads the bank as decimal, or as hex with 0x. A leading zero makes the bank octal, so 0a or 08 becomes bank 0. Write bank 10 as 10 or 0x0a.
    • Code in work RAM or HRAM always runs on the interpreter. That is by design. Shantae Recomp wrote native versions of such RAM routines by hand, each with a differential check.
    • For dispatchers that hide many targets, sample addresses with python3 gb-recompiled/tools/capture_ground_truth.py roms/game.gbc --output logs/game-observed.trace --frames 18000 (needs PyBoy). Regenerate with --use-trace logs/game-observed.trace. The tool's docs call this a hint, not proof.
    • Shantae Recomp switched its fork to cover every possible ROM address. Consider that only after the harvest loop stalls.
    Prompt for Claude
    GOAL: Remove the interpreter fallback at <BANK:ADDRESS> from the replay of `logs/<ROUTE>.input`.
    READ FIRST: `notes/map.md`, `logs/interp.log` (only lines for <BANK:ADDRESS>), `gb-recompiled/GROUND_TRUTH_WORKFLOW.md`.
    FENCE: Edit only the `--add-entry-point` list in `tools/check.sh` and `notes/map.md`. Write the bank as decimal or with `0x`, never with a leading zero. Never edit generated C. Do not add an address you have not proven is code.
    CHECK: Run `bash tools/check.sh`, replay with `--headless --model cgb --limit-frames <N> --input "$(cat logs/<ROUTE>.input)" --log-file logs/interp.log --log-frame-fallbacks --report-interpreter-hotspots`, then run `python3 gb-recompiled/tools/summarize_interpreter_log.py logs/interp.log`. Done means <BANK:ADDRESS> is gone from the summary and no new fallback appears.
    BUDGET: Stop after 3 tries, or 30 minutes.
    IF BLOCKED: Stop. If the address is RAM code or data, do not force it in. Tell me what Ghidra shows there.
    OTHER PROBLEMS: Write them in `notes/parking-lot.md`. Do not fix them.
    REPORT: 1) DONE, PARTIAL or BLOCKED. 2) Proof: the Ghidra lines and the before and after summary. 3) Files changed. 4) Fallbacks left. 5) New parking-lot items.
    Done whenFor every recorded route, summarize_interpreter_log.py prints No interpreter fallback recorded, except for RAM code listed in notes/map.md.

    Proof this works: Shantae Recomp, Pokemon TCG 2 Native Port, https://github.com/arcanite24/gb-recompiled/blob/main/RUNTIME.md, https://github.com/arcanite24/gb-recompiled/blob/main/GROUND_TRUTH_WORKFLOW.md

  6. Fix runtime bugs with proof

    Now you fix wrong pictures, sound or timing. One bug per session. You prove the cause before any fix. On this system, look hard at Color-only parts: palettes, HDMA, double speed and banked RAM.

    • Run the differential check from boot: ./output/game/build/game --differential 500000 --differential-log 100000 --differential-fail-on-fallback.
    • Reproduce the bug with a recorded input file. Stop just before it and save the machine: --headless --limit-frames <N> --input "$(cat logs/<ROUTE>.input)" --dump-state logs/bug.json --save-state-file logs/bug.state.
    • Run the differential check from that point: --differential 500000 --differential-state logs/bug.state --differential-fail-on-fallback.
    • Know its limit. It compares compiled code with the tool's own interpreter. Both share the same graphics, sound and timer code, so it cannot catch a bug there.
    • For those parts, read Pan Docs first, then compare with SameBoy's debugger. The tool's GBC.md uses the same order.
    • Fix the recompiler or runtime, never the generated C. For a game-only change, use a --native-patch manifest, as Resident Evil Gaiden: Recompiled did.
    Prompt for Claude
    GOAL: Find and fix the cause of <BUG> seen at frame <N> of `logs/<ROUTE>.input`.
    READ FIRST: `notes/map.md`, `notes/findings.md`, Pan Docs page <PAGE>, the frame dump `logs/<FILE>`.
    FENCE: Edit only `<RUNTIME_OR_PATCH_FILE>` and `notes/findings.md`. Never edit generated C. No game-specific hacks unless I approve them.
    CHECK: Run `bash tools/check.sh`. Replay with `--headless --model cgb --limit-frames <N> --input "$(cat logs/<ROUTE>.input)" --dump-frames <N> --screenshot-prefix logs/fix --save-state-file logs/bug.state`. Then run `--differential 500000 --differential-state logs/bug.state --differential-fail-on-fallback`. Done means the frame matches SameBoy at the same point, the differential run passes, and the check script exits 0.
    BUDGET: Stop after 2 failed fixes. Write the cause first; no fix before the cause is proven.
    IF BLOCKED: Stop. Tell me the evidence you have, the guesses you ruled out, and what you need.
    OTHER PROBLEMS: Write them in `notes/parking-lot.md`. Do not fix them.
    REPORT: 1) DONE, PARTIAL or BLOCKED. 2) Proof: before and after frames and the differential output. 3) Files changed. 4) Cause in one sentence, with addresses and registers. 5) New parking-lot items.
    Done whenThe differential run from logs/bug.state shows no mismatch and no fallback. bash tools/check.sh exits 0. The new frame matches SameBoy at the same frame.

    Proof this works: Resident Evil Gaiden: Recompiled, Shantae Recomp, https://github.com/arcanite24/gb-recompiled/blob/main/GBC.md

  7. Build a regression set

    You turn your recorded routes into tests. Each route saves a final-state hash. Any change that alters game logic then fails a test. This keeps old fixes fixed.

    • Copy each route's input file out of the ignored logs/ folder: cp logs/route1.input tests/routes/route1.input. Input files hold your button presses, not game data.
    • Save each route's final state: --headless --input "$(cat tests/routes/route1.input)" --limit-frames <N> --dump-state tests/routes/route1.json. The dump holds registers, HRAM, work RAM banks 0 and 1, and the first 256 bytes of cartridge RAM. It skips Color work RAM banks 2 to 7, so also save the last frame with --dump-frames.
    • Pokemon Gold, Silver and Crystal use a clock chip (MBC3 RTC). Add --rtc-unix-time <SECONDS> so every replay sees the same time. Add --save-dir <DIR> so tests do not touch your real saves. Make the folder first with mkdir -p; the runtime will not create it.
    • Write tools/regress.sh. It replays every route and compares the state files.
    • Keep extras like widescreen render-only, so the state hash still proves the logic is unchanged.
    • Add tests that need no ROM, so a clean clone can run them. Resident Evil Gaiden: Recompiled and The Oracles Project do this.
    Prompt for Claude
    GOAL: Make `tools/regress.sh` replay every route in `tests/routes/` and fail on any state change.
    READ FIRST: `PROGRESS.md`, `gb-recompiled/RUNTIME.md` (--input, --dump-state, --rtc-unix-time, --save-dir).
    FENCE: Edit only `tools/regress.sh`, `tests/` and `PROGRESS.md`. Do not change expected state files except for a route I name.
    CHECK: Run `bash tools/regress.sh` on a clean clone, with the ROM path passed in. Done means every route passes. Then change one byte of one expected file and show the script fails.
    BUDGET: Stop after 3 failed tries.
    IF BLOCKED: Stop. Tell me which route is not repeatable and why.
    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) Routes not yet covered. 5) New parking-lot items.
    Done whenbash tools/regress.sh passes on a clean clone and fails when an expected file is changed.

    Proof this works: The Oracles Project, Resident Evil Gaiden: Recompiled, https://github.com/arcanite24/gb-recompiled/blob/main/RUNTIME.md

  8. Build for Windows and ship without the game

    You build a Windows program and package it. Know this first: gbrecomp writes the whole ROM into output/game/game_rom.c and builds it in. Any built program also holds the game's code as machine code. So you pick one of two safe ways to ship.

    • Way 1, as in Pokemon Pinball Recompiled: ship only a launcher. It runs gbrecomp on the player's own ROM and compiles on the player's PC.
    • Way 2, as in Shantae Recomp: remove the built-in ROM, load the player's file at start, and check its SHA-256. Shantae tests a one-bit bad dump and a short dump too.
    • Install MSYS2. In its UCRT64 shell run pacman -S mingw-w64-ucrt-x86_64-gcc mingw-w64-ucrt-x86_64-cmake mingw-w64-ucrt-x86_64-ninja mingw-w64-ucrt-x86_64-SDL2.
    • Write tools/check_release.py. It fails if any 32-byte run of the ROM appears in the package. Oracles Decomp has a similar test for its shipped program.
    • Ship a clean default settings file. An early Resident Evil Gaiden release shipped its author's own settings, with cheats on.
    • Copy the package to a fresh folder on another PC and run it with your ROM.
    Prompt for Claude
    GOAL: Produce a Windows release zip of the <GAME> recomp that runs from a clean folder and holds no ROM data.
    READ FIRST: `SCOPE.md`, `PROGRESS.md`, `tools/check_release.py`, the generated `CMakeLists.txt` (find `game_rom.c`).
    FENCE: Edit only `tools/package.sh`, `tools/check_release.py` and `PROGRESS.md`. Do not put the ROM, generated C, or files under `output/` in the zip. If the program still builds in `game_rom.c`, stop and report BLOCKED.
    CHECK: Run `python3 tools/check_release.py dist/<ZIP>`. Done means it prints zero ROM matches, and the unzipped program starts with the correct ROM and refuses a wrong one.
    BUDGET: Stop after 3 failed packaging tries.
    IF BLOCKED: Stop. Tell me which DLL or file is missing, with the exact error.
    OTHER PROBLEMS: Write them in `notes/parking-lot.md`. Do not fix them.
    REPORT: 1) DONE, PARTIAL or BLOCKED. 2) Proof: the scan output and both launch results. 3) Files changed. 4) What is left. 5) New parking-lot items.
    Done whenThe release scan finds zero ROM bytes, and the unzipped build runs on a clean PC with your ROM and refuses a wrong one.

    Proof this works: Pokemon Pinball Recompiled, Shantae Recomp, Oracles Decomp, https://packages.msys2.org/packages/mingw-w64-ucrt-x86_64-SDL2

Path B: Full decomp for porting

Color games were assembly, so the matching source is labeled assembly. You build a listing that rebuilds the exact ROM with RGBDS. Then you name and document every routine. Last, you port the game by rewriting each routine in C or Rust. A per-routine check against the original code proves each rewrite. Oracles Decomp and Pokemon TCG 2 Native Port both did this, starting from finished disassemblies.

Method: matching disassembly, then a port routine by routine

Pick this when you want the game on other systems, or deep mods. First check pret and GitHub for an existing disassembly. If a full one exists, jump to the port steps.

  1. Look for an existing disassembly

    Many Color games already have byte-matching disassemblies. Using one saves months or years. Some are only partial and still need the retail ROM to build. Either way, you write the scope.

    • Check pret.github.io and search GitHub for "<GAME> disassembly". Known ones include pret/pokecrystal, pret/pokegold, pret/poketcg, pret/poketcg2, Stewmath/oracles-disasm and ElectroDeoxys/warioland3.
    • Compare the SHA-1 it lists with sha1sum roms/game.gbc.
    • Check whether its Makefile needs a baserom.gbc. If so, it is partial. pret/pokepuzzle and telefang are partial like that.
    • Write SCOPE.md: revision, hash, start point, port target and language, and the check.
    Prompt for Claude
    GOAL: Find prior disassembly work for <GAME> and draft SCOPE.md for Path B.
    READ FIRST: https://pret.github.io/, GitHub search results for "<GAME> disassembly", my ROM hash `<SHA1>`.
    FENCE: Create only `SCOPE.md` and `notes/prior-work.md`. Do not clone or download ROMs.
    CHECK: For each disassembly found, list its URL, last commit date, assembler and version, the SHA-1 it builds, whether that SHA-1 equals `<SHA1>`, and whether it needs a base ROM. Done means SCOPE.md names the starting point (existing repo or empty) and the port target.
    BUDGET: Stop after 45 minutes.
    IF BLOCKED: Stop. Tell me what you could not find or check.
    OTHER PROBLEMS: Write them in `notes/parking-lot.md`. Do not fix them.
    REPORT: 1) DONE, PARTIAL or BLOCKED. 2) Proof: the table of repos and hashes. 3) Files changed. 4) Your recommended start. 5) New parking-lot items.
    Done whenSCOPE.md names a starting repo, or says none exists, and grep -c <SHA1> SCOPE.md prints 1 or more. You approved it.

    Proof this works: pokegold, pokepuzzle, Oracles Decomp, https://github.com/pret/pokecrystal

  2. Make a ROM that rebuilds byte for byte

    You turn the ROM into assembly that rebuilds it exactly. This is your one check. From now on, every change must keep the hash the same. You can start with a full listing, or with an overlay that fills gaps from your ROM.

    • In WSL, install the basics: sudo apt install make gcc git python3.
    • Install RGBDS 1.0.4: download rgbds-linux-x86_64.tar.xz, then mkdir rgbds && tar xf rgbds-linux-x86_64.tar.xz -C rgbds && cd rgbds && sudo ./install.sh. Check it with rgbasm -V. Write 1.0.4 into .rgbds-version.
    • Full listing: git clone https://github.com/mattcurrie/mgbdis, then python3 mgbdis/mgbdis.py roms/game.gbc. Then cd disassembly && make. It writes game.gbc.
    • Overlay start: link with rgblink -O baserom.gbc. Every byte you have not disassembled comes from your ROM. pret/pokepuzzle and telefang build this way.
    • Put compare in a top-level Makefile. It runs make -C disassembly, then sha1sum -c rom.sha1, as pret/poketcg and warioland3 check their ROMs. Do not put it in disassembly/Makefile: mgbdis rewrites that file on every run.
    Prompt for Claude
    GOAL: Make `make compare` rebuild <GAME> from RGBDS source with the same SHA-1 as my ROM.
    READ FIRST: `SCOPE.md`, `mgbdis/README.md`, https://rgbds.gbdev.io/install/linux
    FENCE: Edit only the top-level `Makefile`, `.rgbds-version` and `tools/`. Never edit `disassembly/Makefile`; `mgbdis` rewrites it. Do not edit `rom.sha1` after I approve it. Do not commit the ROM; it stays at `<ABSOLUTE_PATH_TO_ROM>`.
    CHECK: Run `make compare`. Done means it prints OK with the SHA-1 from SCOPE.md. Then change one byte in one .asm file and show `make compare` fails.
    BUDGET: Stop after 3 failed tries.
    IF BLOCKED: Stop. Tell me the exact error and the RGBDS version from `rgbasm -V`.
    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 left. 5) New parking-lot items.
    Done whenmake compare prints OK, and fails when one byte is changed.

    Proof this works: poketcg, pokepuzzle, telefang, https://github.com/mattcurrie/mgbdis

  3. Separate code from data and list every routine

    A raw disassembly guesses where code starts. Wrong guesses turn data into fake code. You feed it real evidence of what ran. Then you build the routine ledger and a progress script.

    • Record which addresses ran during play. Use --trace-entries logs/entries.txt from the Path A runtime, or the game_metadata.json file from gbrecomp. The trace writes one bank:address line per entry, with the bank in decimal.
    • Write them into a symbol file as .code blocks. Mark tables .data, text .text and tiles .image. Entries look like 03:47f2 Read_Joypad and 0d:4800 .data:200. All .sym numbers are hex, so trace bank 10 becomes 0a. Keep only ROM addresses below 8000.
    • mgbdis reads the symbol file only if it sits next to the ROM with the same name: roms/game.sym for roms/game.gbc. Keep a master copy at notes/game.sym.
    • Re-run with python3 mgbdis/mgbdis.py roms/game.gbc --overwrite. Do all re-runs before you hand-edit any .asm file, or your edits are lost.
    • Write ledger/routines.csv: bank, address, size, name, status.
    • Write tools/progress.py. It counts raw ROM bytes left per bank and unnamed symbols in the .sym file, as warioland3 does.
    Prompt for Claude
    GOAL: Build `ledger/routines.csv` with every routine in <GAME>, and a `notes/game.sym` that marks code, data, text and graphics.
    READ FIRST: `SCOPE.md`, `mgbdis/README.md` (Symbol Files), `<PATH_TO>_metadata.json` from the recomp (use a script, do not read it whole), `logs/<TRACE>`.
    FENCE: Edit only `notes/game.sym`, its copy at `<ROM_DIR>/game.sym`, `ledger/routines.csv`, `tools/progress.py` and the regenerated `disassembly/` folder. Do not hand-edit any .asm file in this task.
    CHECK: Run `python3 mgbdis/mgbdis.py <ROM_DIR>/game.gbc --overwrite`, then `make compare`, then `python3 tools/progress.py`. Done means compare prints OK, and every routine in the metadata file appears in the ledger with bank, address and status.
    BUDGET: Stop after 2 hours or 3 failed compares.
    IF BLOCKED: Stop. List the address ranges you cannot class as code or data.
    OTHER PROBLEMS: Write them in `notes/parking-lot.md`. Do not fix them.
    REPORT: 1) DONE, PARTIAL or BLOCKED. 2) Proof: the compare output, ledger row counts and progress output. 3) Files changed. 4) Unknown ranges. 5) New parking-lot items.
    Done whenmake compare prints OK and ledger/routines.csv lists every known routine with a status.

    Proof this works: warioland3, https://github.com/mattcurrie/mgbdis, https://github.com/arcanite24/gb-recompiled/blob/main/RUNTIME.md

  4. Finish one routine end to end

    You take one small routine all the way. You name it and its RAM, swap raw numbers for labels, and write a comment. The ROM must still match. This proves the whole loop works.

    • Pick a short routine, like the joypad reader.
    • Run make first, so the .sym file has current labels. pret/poketcg warns that stale symbols give wrong disassembly output.
    • Use the SameBoy or BGB debugger to watch which RAM it writes. BGB loads your .sym file.
    • Name the routine, its labels and its RAM. Use placeholders with the address, like Func_9000, for anything you do not understand yet.
    • Run make compare. Set the ledger status to named.
    Prompt for Claude
    GOAL: Fully name and document routine <BANK:ADDRESS> in the <GAME> disassembly.
    READ FIRST: `ledger/routines.csv` (row for <BANK:ADDRESS>), the routine's .asm file, `notes/ram-map.md`.
    FENCE: Edit only the routine's .asm lines, the RAM label file, `notes/ram-map.md` and its ledger row. Do not change any byte the build emits.
    CHECK: Run `make compare`. Done means OK, no raw ROM or RAM address is left inside the routine, and the ledger row says `named`.
    BUDGET: Stop after 3 failed compares or 30 minutes.
    IF BLOCKED: Stop. Say which value you cannot explain and what you checked.
    OTHER PROBLEMS: Write them in `notes/parking-lot.md`. Do not fix them.
    REPORT: 1) DONE, PARTIAL or BLOCKED. 2) Proof: compare output and the diff. 3) Files changed. 4) Names you are unsure of. 5) New parking-lot items.
    Done whenmake compare prints OK and the routine has no raw addresses left.

    Proof this works: poketcg, https://bgb.bircd.org/

  5. Name every routine, bank by bank

    Now you repeat the unit loop many times. Each worker takes routines from one bank. Easy leaf routines go first. Each change must keep make compare at OK.

    • Sort the ledger: leaf routines first, then routines whose callees are all named.
    • Give each worker one bank file and its own git worktree.
    • Do not make macros for data you do not fully understand. Do not move files around while much work remains. pret/poketcg gives both rules.
    • Run make compare after every routine, and again after each merge.
    • Record dead ends in notes/findings.md so nobody repeats them.
    Prompt for Claude
    GOAL: Name and document the next <N> `raw` routines in bank <BANK>, one at a time.
    READ FIRST: `ledger/routines.csv` (bank <BANK> rows only), `notes/ram-map.md`, `notes/findings.md`.
    FENCE: Edit only `<BANK_FILE>.asm`, the RAM label file, `notes/ram-map.md` and ledger rows for bank <BANK>. Work in worktree `<ABSOLUTE_WORKTREE_PATH>`. Do not touch other banks. Do not add macros or move files.
    CHECK: Run `make compare` after each routine. Done means OK after each one and each finished row says `named`.
    BUDGET: Skip a routine after 3 failed tries and mark it `hard`.
    IF BLOCKED: Stop. Tell me which routine and why.
    OTHER PROBLEMS: Write them in `notes/parking-lot.md`. Do not fix them.
    REPORT: 1) DONE, PARTIAL or BLOCKED. 2) Proof: the last compare output. 3) Files changed. 4) Routines named, routines marked hard. 5) New parking-lot items.
    Done whenEvery ledger row says named or hard, and make compare prints OK on the merged branch.

    Proof this works: pokegold, poketcg, https://github.com/pret/pokecrystal

  6. Pull out graphics, palettes and text

    Ports need the art and data as real files. You turn tiles into PNG images and Color palettes into palette files. The build turns them back into bytes. The hash must not change.

    • Mark tile blocks as .image in notes/game.sym before hand edits, then re-run mgbdis --overwrite. It writes PNG files and rgbgfx rules.
    • After hand edits, write each block out as PNG and add a make rule that runs rgbgfx.
    • Build Color palettes from PNG too. pret/pokegold has a .gbcpal rule for that.
    • Some files need per-file flags to match. warioland3 and pret/poketcg each list about 40 such exceptions in the Makefile.
    • Compressed data needs a compressor that copies the original's choices. pret/poketcg runs tools/compressor -m for that.
    • Keep text in .txt or CSV files built at compile time, as warioland3 and telefang do.
    Prompt for Claude
    GOAL: Move graphics block <LABEL> out of the .asm file into `gfx/<NAME>.png`, built back with `rgbgfx`.
    READ FIRST: `notes/game.sym`, `Makefile`, https://rgbds.gbdev.io/docs/v1.0.4/rgbgfx.1
    FENCE: Edit only `Makefile`, `gfx/<NAME>.png` and the lines for <LABEL>. Do not commit any other ROM-derived file outside `gfx/`.
    CHECK: Run `make clean && make compare`. Done means OK.
    BUDGET: Stop after 3 failed compares.
    IF BLOCKED: Stop. Tell me the bit depth, palette or layout you could not match.
    OTHER PROBLEMS: Write them in `notes/parking-lot.md`. Do not fix them.
    REPORT: 1) DONE, PARTIAL or BLOCKED. 2) Proof: compare output. 3) Files changed. 4) Blocks left. 5) New parking-lot items.
    Done whenmake clean && make compare prints OK with graphics built from PNG files.

    Proof this works: pokegold, warioland3, poketcg

  7. Harden the disassembly

    You make the build repeatable for anyone. A clean clone must give OK. Game files never enter git. A second session checks the work.

    • Add a CI job that builds the pinned RGBDS tag from source, then runs make DEBUG=1 compare. pret/poketcg and warioland3 do this.
    • After the build, fail CI if git diff-index --quiet HEAD finds changes. That catches generated files that drifted.
    • Publish .sym and .map files to a symbols branch, as pret does. Path A users can then pass them to gbrecomp --symbols.
    • Scan git ls-files for any .gbc file or large binary.
    • Have a fresh session re-check ten random named rows.
    Prompt for Claude
    GOAL: Prove the <GAME> disassembly builds from a clean clone and holds no game files.
    READ FIRST: `PROGRESS.md`, `.gitignore`, `Makefile`, `.rgbds-version`.
    FENCE: Edit only `.github/workflows/`, `.gitignore` and `PROGRESS.md`.
    CHECK: Clone to `<ABSOLUTE_TEMP_PATH>`, copy in my ROM from `<ABSOLUTE_PATH_TO_ROM>` only if the Makefile needs a base ROM, run `make compare`, then `git diff-index --quiet HEAD`. Done means OK, a clean tree, and `git ls-files` lists no .gb, .gbc or .sav file.
    BUDGET: Stop after 3 failed tries.
    IF BLOCKED: Stop. Tell me which file the clean build needs that git lacks.
    OTHER PROBLEMS: Write them in `notes/parking-lot.md`. Do not fix them.
    REPORT: 1) DONE, PARTIAL or BLOCKED. 2) Proof: all outputs. 3) Files changed. 4) What is left. 5) New parking-lot items.
    Done whenA clean clone gives make compare OK and a clean tree, and git holds no game files.

    Proof this works: poketcg, warioland3, https://github.com/pret/pokegold/tree/symbols

  8. Port one routine at a time, checked against the original

    Now you rewrite the game in C or Rust. The disassembly stays untouched as the reference. A development build runs the original code in an emulator core. It swaps in your new routines one by one and compares each call with the original.

    • Keep the disassembly in its own folder, as a git submodule. Pokemon TCG 2 Native Port never edits its src/decomp/.
    • Build a dev runner with an SM83 core. Hook each ported routine by bank and address. Unported code still runs from the original bytes.
    • For each call, run your routine and the original from the same state. Compare registers, memory and cycles. Oracles Decomp calls this --verify-shadow.
    • Or write a translator from the annotated assembly, then run it in lockstep with a separate interpreter. Pokemon TCG 2 Native Port did this in Rust.
    • Fix translation bugs in the translator, never in generated code.
    • Ship no ROM code. Oracles Decomp zeroes the code bytes in its cached ROM and tests that its program holds no ROM code.
    Prompt for Claude
    GOAL: Port routine <BANK:ADDRESS> (<NAME>) to <LANGUAGE> and pass the shadow check.
    READ FIRST: the routine's lines in the disassembly, its ledger row, at most 4 named port files: <FILES>.
    FENCE: Edit only <PORT_FILE> and <TEST_FILE>. Never edit the disassembly folder. Do not bulk-translate other routines.
    CHECK: Run `<SHADOW_COMMAND> --routine <BANK:ADDRESS>` over the route `<ROUTE>`. Done means every call matches the original on registers, memory and cycles, and all earlier routes still pass.
    BUDGET: After 2 failed fix tries, stop and reply ESCALATE with a short summary.
    IF BLOCKED: Stop. Tell me which instruction, table or timing 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: the shadow output with call counts. 3) Files changed. 4) What is left. 5) New parking-lot items.
    Done whenEvery ledger row is ported, and the shadow check passes on every recorded route.

    Proof this works: Oracles Decomp, Pokemon TCG 2 Native Port, https://github.com/kirby-letsgo/oracles-decomp

  9. Check the port against the real game

    Routine checks prove pieces, not the whole game. You add golden runs. The port plays fixed input and must reach the same states as the original. You compare state hashes and pictures.

    • Record full-game input routes in an emulator. Oracles Decomp replays console-verified TAS movies, which are prerecorded button inputs.
    • Hash the machine state every 60 frames in both the original and the port. Report the first frame that differs.
    • Pokemon TCG 2 Native Port also ran six random-play sessions of 100,000 frames each in lockstep.
    • Compare pictures with SameBoy, frame by frame, for key scenes.
    • Load art and data from the player's own ROM at first launch. Check its hash first.
    Prompt for Claude
    GOAL: Make `tools/golden.sh` replay route <ROUTE> in the port and in the reference core and compare state hashes every 60 frames.
    READ FIRST: `PROGRESS.md`, `notes/ram-map.md`, the route file `<ROUTE>`.
    FENCE: Edit only `tools/golden.sh`, `tools/compare_state.py` and `tests/golden/`. Do not change the port's game code in this task.
    CHECK: Run `bash tools/golden.sh <ROUTE>`. Done means it prints a per-checkpoint table and exits 0 only when every hash matches.
    BUDGET: Stop after 3 failed tries.
    IF BLOCKED: Stop. Tell me which frame cannot be lined up and why.
    OTHER PROBLEMS: Write differences you find in `notes/parking-lot.md`. Do not fix them.
    REPORT: 1) DONE, PARTIAL or BLOCKED. 2) Proof: the table. 3) Files changed. 4) Differences found. 5) New parking-lot items.
    Done whenEvery golden route matches the original at every checkpoint, and the release holds no ROM code.

    Proof this works: Oracles Decomp, Pokemon TCG 2 Native Port, https://github.com/LIJI32/SameBoy

Tools you need

ToolWhat it doesHow to get itHow we know
gb-recompiled (`gbrecomp`)
Required both
Turns a Game Boy or Game Boy Color ROM into a C project with an SDL2 runtime. It has a fallback interpreter, a differential test, input replay and Color mode.
Path A Path B
Build from source, because later steps use its `tools/` folder: `git clone https://github.com/arcanite24/gb-recompiled.git && cd gb-recompiled && cmake -G Ninja -B build . && ninja -C build` (needs CMake 3.20+, Ninja, SDL2 files and a C11/C++20 compiler). The program lands in `build/bin/gbrecomp`. Or download `gb-recompiled-windows-x64.zip` or `gb-recompiled-linux-x64.tar.gz` from release v0.1.0. That release is older than the docs, so check each flag with `gbrecomp --help`README, GBC.md, RUNTIME.md, AGENTS.md, GROUND_TRUTH_WORKFLOW.md, NATIVE_PATCHES.md and releases (v0.1.0 on 2026-08-03; main last pushed 2026-09-10) read 2026-11-13. Flag parsing read in recompiler/src/main.cpp, generated-file names in recompiler/src/codegen/c_emitter.cpp, frame-dump names in runtime/src/platform_sdl.cpp, trace format in runtime/src/gbrt.c and recompiler/src/analyzer.cpp (2026-11-13). Used by Pokemon Pinball Recompiled and Resident Evil Gaiden: Recompiled. Shantae Recomp uses vibecodekun/gbrecompiled, a fork of mstan/gbrecompiled, which calls itself a development fork of this tool. Not run here.
CMake and Ninja
Required both
Build tools. gbrecomp writes a CMake project, and Ninja compiles it.
Path A Path B
WSL: `sudo apt install build-essential cmake ninja-build`. Windows: `pacman -S mingw-w64-ucrt-x86_64-cmake mingw-w64-ucrt-x86_64-ninja` in MSYS2gb-recompiled README (CMake 3.20+, Ninja) read 2026-11-13; Ubuntu noble pages (cmake 3.28.3, ninja-build 1.11.1) and MSYS2 package pages checked 2026-11-13
SDL2 development files
Required both
The window, sound and controller library the runtime uses.
Path A
WSL: `sudo apt install libsdl2-dev`. Windows: `pacman -S mingw-w64-ucrt-x86_64-SDL2` in MSYS2gb-recompiled README; Ubuntu noble page (libsdl2-dev 2.30.0) and MSYS2 package page checked 2026-11-13; Resident Evil Gaiden: Recompiled vendors SDL2 2.30.12
MSYS2 (UCRT64 GCC)
Helpful Windows
A Windows build shell with GCC. Use it to build the Windows version, and to build oracles-disasm on Windows.
Path A Path B
Run the installer from msys2.org, then `pacman -S mingw-w64-ucrt-x86_64-gcc` in the UCRT64 shellmsys2.org read 2026-11-13 (installer msys2-x86_64-20260927.exe); Shantae Recomp and The Oracles Project build with MSYS2; oracles-disasm README requires it on Windows
Ghidra 11.3.2
Helpful Windows
A free disassembler and decompiler. With GhidraBoy it reads banked Color ROMs. Use 11.3.2, not the newest Ghidra, because GhidraBoy stops at 11.4.2 and GhidraMCP 1.4 is built for 11.3.2.
Path A Path B
Install a 64-bit JDK 21. Download `ghidra_11.3.2_PUBLIC_20250415.zip` from the Ghidra 11.3.2 release page and unzip itGhidra releases read 2026-11-13 (newest 12.2, 2026-10-22). 11.3.2 release (2025-04-15, asset ghidra_11.3.2_PUBLIC_20250415.zip) and its README (JDK 21 64-bit) read 2026-11-13. Shantae Recomp used Ghidra with GhidraBoy; Resident Evil Gaiden: Recompiled documents a Ghidra workflow.
GhidraBoy
Helpful Windows
A Ghidra add-on for the SM83 CPU. It loads banked ROMs, maps banked work RAM, and names the Color-only registers like KEY1.
Path A Path B
Download `ghidra_11.3.2_PUBLIC_20250830_GhidraBoy.zip` from the GhidraBoy releases page. In Ghidra: File, Install Extensions, plus button, pick the zip, restartREADME and release 20250830 read 2026-11-13: archived; supports Ghidra 11.1 to 11.4.2; handles GB vs GBC differences; can load CGB boot ROMs; calls into $4000-$7FFF need the bank set by hand
GhidraMCP
Helpful Windows
An MCP server that lets Claude ask Ghidra for listings, names and cross-references. MCP is the way Claude Code talks to outside tools.
Path A Path B
For Ghidra 11.3.2, use release 1.4: download `GhidraMCP-release-1-4.zip` and unzip it. In Ghidra, use File, Install Extensions, plus button, and pick the plugin zip from inside it. Restart, then turn the plugin on in File, Configure, Developer. Run `pip install mcp==1.5.0 requests==2.32.3`, then `claude mcp add --transport stdio ghidra -- python <ABSOLUTE_PATH>/bridge_mcp_ghidra.py --ghidra-server http://127.0.0.1:8080/`Releases (2.0 on 2026-10-10, 1.4 on 2025-06-23), 1.4 extension.properties (ghidraVersion=11.3.2), 1.4 README and requirements.txt read 2026-11-13. The claude mcp add line joins the README's bridge arguments with the Claude Code MCP docs form claude mcp add [options] <name> -- <command> [args...] (read 2026-11-13). It was not run.
SameBoy
Helpful Windows
A very accurate Game Boy and Color emulator with a text debugger. It has conditional breakpoints, watchpoints and a disassembler. Use it as the picture and timing reference.
Path A Path B
Download `sameboy_winsdl_v1.0.5.zip` from the releases page and unzip itREADME and v1.0.5 release (2026-11-02) read 2026-11-13; gb-recompiled GBC.md names it the second reference; The Oracles Project runs it as its core; Pokemon TCG 2 Native Port compares frames with it
BGB
Helpful Windows
A Windows emulator for Game Boy and Color with a strong debugger. It loads .sym files, so you see your labels while you step.
Path B
Download the zip from bgb.bircd.org and unzip itbgb.bircd.org read 2026-11-13: version 1.6.7, Game Boy Color support, Windows and Wine, debugger, SYM files
PyBoy
Helpful both
A Game Boy emulator you drive from Python. Its cgb=True option forces Color mode. Use it for traces and as a RAM reference in scripts.
Path A Path B
`pip install pyboy` (in a virtual environment)README, pyboy/pyboy.py (cgb argument) and PyPI (2.9.0, Python 3.9+) read 2026-11-13; gb-recompiled trace tools use it
RGBDS 1.0.4
Required both
The Game Boy and Color assembler, linker, header fixer and graphics converter (rgbasm, rgblink, rgbfix, rgbgfx). It rebuilds the ROM from your assembly.
Path B
WSL: download `rgbds-linux-x86_64.tar.xz` from the v1.0.4 release page, then `mkdir rgbds && tar xf rgbds-linux-x86_64.tar.xz -C rgbds && cd rgbds && sudo ./install.sh`, then check `rgbasm -V`. Windows: unzip `rgbds-win64.zip` into a folder on `PATH`v1.0.4 release (2026-09-22, the newest), the Linux and Windows install pages and the v1.0.4 rgblink manual (-O overlay) read 2026-11-13; pret/pokecrystal INSTALL.md asks for rgbds 1.0.4; pokegold, poketcg and pokepuzzle pin 1.0.4
mgbdis
Helpful both
A Python disassembler for Game Boy and Color ROMs. It writes RGBDS files and a makefile that rebuild the ROM.
Path B
`git clone https://github.com/mattcurrie/mgbdis` (needs Python 3.6+ and RGBDS 0.8+)README and release v3.0 (2025-04-22) read 2026-11-13: .sym blocks .code, .data, .text, .padding, .image; outputs game.gbc for Color ROMs; --overwrite exists, and every run rewrites disassembly/Makefile (mgbdis.py read 2026-11-13); the makefile needs make, so run it in WSL
WLA-DX
Helpful both
Another assembler. oracles-disasm builds with it, not RGBDS. You need it only if your game's disassembly uses it.
Path B
Download `wla_dx_v10.6_Win64.zip` from the releases page, or build it in WSL from the source. `oracles-disasm` asks for v10.6, Python 3 and `python3-yaml`. Its README says v10.6 does not rebuild Seasons byte for byte; a branch of WLA-DX is needed for thatReleases (v10.7 2026-06-28, v10.6 2023-11-19) and oracles-disasm README read 2026-11-13; Oracles Decomp builds the disassembly with make -C ref/oracles-disasm ages

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

MCP server

Claude can ask Ghidra what a bank and address really hold. Use it for the map step and for proving fallback addresses.

With Ghidra 11.3.2 and GhidraBoy: install the plugin zip from inside `GhidraMCP-release-1-4.zip`, restart, and turn it on in File, Configure, Developer. Run `pip install mcp==1.5.0 requests==2.32.3`, then `claude mcp add --transport stdio ghidra -- python <ABSOLUTE_PATH>/bridge_mcp_ghidra.py --ghidra-server http://127.0.0.1:8080/`. Not tested here.

static-recomp-scope-plan

skills.sh

It makes you write down the exact ROM, the scenes to test and the pass marks first. That fits Path A step 1.

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

ghidra-headless

skills.sh

It runs Ghidra with no window and saves functions and strings to files. Use it if the MCP plugin will not load. It needs GhidraBoy for Color ROMs. Shantae Recomp ran Ghidra headless the same way.

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

native-platform-porting

skills.sh

It guides a real port and warns Claude not to hide an emulator inside it. That matches the rule Oracles Decomp tests for in Path B.

npx skills add lemiur/game-decompilation-skill-for-claude --skill native-platform-porting -a claude-code

verification-before-completion

skills.sh

Claude may not say done without fresh proof. Here that proof is make compare, a zero-fallback log, or a passing route.

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

systematic-debugging

skills.sh

No fix until the cause is found. That fits Color bugs in palettes, HDMA and sound, where guesses waste days.

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

using-git-worktrees

skills.sh

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

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

handoff

skills.sh

Writes a short note so a fresh session can carry on. Gen2Recomped kept such handoff files. Tell it to save inside your project, not in a temp folder.

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

gbc-fallback-harvest (write your own)

write-your-own

This is the loop every Color recomp ran. A skill makes each run the same, with the same proof.

A `.claude/skills/gbc-fallback-harvest/SKILL.md` that: 1) replays every file in `tests/routes/` with `--model cgb --log-frame-fallbacks`; 2) runs `summarize_interpreter_log.py`; 3) for each new address, asks Ghidra for the listing; 4) writes proven code to the entry-point list, and RAM code or data to `notes/map.md`; 5) regenerates and replays. Rule: never add an address without a listing.

gbc-shadow-port (write your own)

write-your-own

It turns the Oracles Decomp check into one command per routine. Each port worker then has a clear pass or fail.

A `.claude/skills/gbc-shadow-port/SKILL.md` plus `tools/shadow.sh`. The script takes `--routine BANK:ADDR` and a route, runs the port with that routine hooked, and prints calls, mismatches and the first differing register or byte. The skill says: read the routine's lines only, port it, run the script, stop after 2 failed fixes.

Claude setup for this system

Cut Color work by bank and address. On Path A, one unit is one fallback address or one bug, with a recorded input file that shows it. On Path B, one unit is one routine from the ledger, first to name and later to port. Every unit has one check: a zero-fallback replay, a passing differential run, make compare, or a passing shadow check. Keep the ROM outside the repo and pass its absolute path.

Which model and effort for each job

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

JobWhat it means on this systemMax 20x (now called Ultra)Max 5x (now called Max)
Plan the projectPin the ROM by hash. Read the Color flag and MBC. Look for a disassembly, a .sym file or an existing recomp. Write SCOPE.md.Fable 5.2
effort high
One session. This choice shapes months of work.
Opus 5.5
effort high
Use Fable 5.2 for this one session if you have weekly room.
Set up the projectGet gbrecomp or RGBDS, write tools/check.sh or make compare, and set up .gitignore before any ROM is near the repo.Opus 5.5
effort medium
Sonnet 5.5
effort medium
Study the game fileFind jump tables, rst handlers, script interpreters and RAM code. Build the routine ledger from metadata, traces and any .sym file.Opus 5.5
effort high
Opus 5.5
effort medium
Raise to high for function edges and data layouts. A mistake there spreads.
Bulk unitsFallback addresses with clear Ghidra proof. Easy routines to name on Path B. Simple routines to port. One per worker, each with its check.Sonnet 5.5
effort medium
Haiku 5.5 for the plainest units.
Sonnet 5.5
effort medium
Fewer workers at once. Haiku 5.5 for the plainest units.
Hard unitsCode that mixes with data, calls that read bytes after the return address, cycle-exact routines, and units that failed three times in bulk.Fable 5.2
effort high
Go to xhigh only after a failed try at high.
Opus 5.5
effort high
Send only the last few that still fail to Fable 5.2.
Build the runtimeColor hardware in the runtime: palettes, VRAM and WRAM banks, HDMA, double speed, the RTC and sound. Pan Docs first, SameBoy second.Opus 5.5
effort high
Opus 5.5
effort medium
Raise to high for timing and graphics bugs.
Find a bugWrong frames, hangs and silent sound. Prove the cause with a recorded input, a state dump and a differential or shadow run before any fix.Opus 5.5
effort high
After two failed tries with full evidence, move to Fable 5.2.
Opus 5.5
effort high
One bug at a time.
Check the workA fresh session replays all routes, reruns make compare, and looks for edited hashes, stubs, hand-written addresses and generated-code edits.Opus 5.5
effort medium
Always a new session, never the one that did the work.
Sonnet 5.5
effort high
Always a new session, never the one that did the work.
Keep the recordsUpdate PROGRESS.md, ledger counts, notes/findings.md and the parking lot after each batch.Haiku 5.5
effort medium
Haiku 5.5
effort medium

How many sessions at once

Max 20x (now called Ultra)

Path A: 2 to 3 sessions at once, each on its own bug or fallback in its own worktree. Keep one writer per runtime file. Path B: 4 to 6 bank workers plus one lead session. Run make compare after each merge. For the port, 3 to 4 workers, each on one routine, with the shadow check after each merge.

Max 5x (now called Max)

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

Lines for this project's CLAUDE.md

Add to CLAUDE.md
- Read `SCOPE.md` and `PROGRESS.md` at the start of every session.
- Never edit generated C under `output/`. Fix the recompiler, the config, a native patch or the runtime, then regenerate.
- Never edit the disassembly folder for the port. It stays a byte-identical build of the original ROM.
- Name every function as bank:address, for example `03:47f2`. The same address in two banks is two functions.
- No hand-written game addresses in port code. Take them from the `.sym` file of the disassembly.
- Ghidra before guessing. Paste the listing for any address you make a claim about.
- Only add an entry point that ran in a trace or is proven code in Ghidra. Data decoded as code is a failure.
- The ROM lives at `<ABSOLUTE_PATH_TO_ROM>`. Never copy it, saves or extracted assets into the repo or a commit.
- The one check is `bash tools/check.sh` (Path A) or `make compare` (Path B). No pasted output means not done.
- Do not edit `*.sha1`, `.rgbds-version`, `tests/routes/*.json` or the check scripts. Ask me first.
- In a worktree, use absolute paths for the ROM and the tools. Gitignored files are not copied in.
- Record the exact commands you ran and their results. Say which checks need the private ROM.
- Write side issues in `notes/parking-lot.md` and dead ends in `notes/findings.md`. Do not act on them.
- Start every report with DONE, PARTIAL or BLOCKED.

Keep the context small

  • Point Claude at *_metadata.json or the .sym file for names and addresses, not at generated C.
  • Give Claude one route's fallback lines, not the whole log. Logs grow fast.
  • Pass a Ghidra address, not a pasted wall of assembly. Let the MCP server fetch it.
  • On Path B, give each worker one bank file and the ledger rows for that bank only.
  • Add a Read deny rule for generated C, such as Read(./output/**/*.c). Read logs and metadata with grep or a script instead.
  • Keep source files short. The Oracles Project keeps each file under 800 lines, so an agent can read it whole.
  • Clear the chat after each unit. The ledger and findings file carry the state.

More prompts for this system

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

Scope and feasibility

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

Prompt for Claude
GOAL: Tell me if <GAME> (<REVISION>, SHA-256 <SHA256>) suits Path A, Path B, or both, and draft SCOPE.md.
READ FIRST: the ROM header at `<ABSOLUTE_PATH_TO_ROM>` (bytes $0100-$014F only), https://pret.github.io/, https://github.com/arcanite24/gb-recompiled/blob/main/GBC.md
FENCE: Create only `SCOPE.md` and `notes/prior-work.md`. No code. Do not copy the ROM.
CHECK: Run `sha256sum <ABSOLUTE_PATH_TO_ROM>` and `od -An -tx1 -j 0x143 -N 7 <ABSOLUTE_PATH_TO_ROM>`, and paste both. Done means SCOPE.md lists ROM size, the $0143 Color flag, MBC type, any clock chip, prior work with links and hashes, 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. 3) Files changed. 4) Risks you see, such as known gaps in GBC.md. 5) New parking-lot items.

A good answer: A one-page SCOPE.md with the hash, the Color flag, links to any disassembly, and a plain risk list. It names Color features the game uses that the tool lists as gaps.

Map the binary

After setup, to build the ledger both paths use.

Prompt for Claude
GOAL: Build `ledger/functions.csv` from `<PATH_TO>_metadata.json`, `notes/game.sym` (if present) and `notes/map.md`.
READ FIRST: `<PATH_TO>_metadata.json` (use a script, do not read it whole), `notes/map.md`.
FENCE: Edit only `ledger/functions.csv` and `tools/make_ledger.py`.
CHECK: Run `python3 tools/make_ledger.py`. Done means one row per function with bank, address, size, name, status, and the row count equals the metadata count.
BUDGET: 3 failed tries.
IF BLOCKED: Stop. Tell me which fields are missing.
OTHER PROBLEMS: Write them in `notes/parking-lot.md`.
REPORT: 1) DONE, PARTIAL or BLOCKED. 2) Proof: both counts. 3) Files changed. 4) Odd entries. 5) New parking-lot items.

A good answer: A CSV with matching counts, built by a script you can rerun. Names come from the .sym file where it exists. Odd entries are flagged, not hidden.

Unit loop: one fallback address (Path A)

For each interpreter fallback in a route replay.

Prompt for Claude
GOAL: Remove the fallback at <BANK:ADDRESS>.
READ FIRST: `notes/map.md`, the fallback summary lines for <BANK:ADDRESS>.
FENCE: Edit only the entry-point list and `notes/map.md`. Write the bank as decimal or with `0x`, never with a leading zero. Never edit generated C.
CHECK: Run `bash tools/check.sh`. Replay with `./output/game/build/game --headless --model cgb --limit-frames <N> --input "$(cat logs/<ROUTE>.input)" --log-file logs/interp.log --log-frame-fallbacks --report-interpreter-hotspots`. Run `python3 gb-recompiled/tools/summarize_interpreter_log.py logs/interp.log`. Done means <BANK:ADDRESS> no longer appears.
BUDGET: 3 tries or 30 minutes.
IF BLOCKED: Stop. If it is RAM code or data, say so with the Ghidra listing.
OTHER PROBLEMS: Write them in `notes/parking-lot.md`.
REPORT: 1) DONE, PARTIAL or BLOCKED. 2) Proof: before and after summary. 3) Files changed. 4) Fallbacks left. 5) New parking-lot items.

A good answer: A Ghidra listing that proves the code, one added entry point, and a summary without that address.

Unit loop: one routine (Path B)

For each raw routine in the ledger.

Prompt for Claude
GOAL: Name and document routine <BANK:ADDRESS>.
READ FIRST: the ledger row, the routine's lines in `<BANK_FILE>.asm`, `notes/ram-map.md`.
FENCE: Edit only those lines, the RAM label file, `notes/ram-map.md`, and the ledger row. No new macros.
CHECK: Run `make` first so the `.sym` file is fresh. Then run `make compare`. Done means OK and the row says `named`.
BUDGET: 3 failed compares, then mark it `hard`.
IF BLOCKED: Stop. Say what you could not explain.
OTHER PROBLEMS: Write them in `notes/parking-lot.md`.
REPORT: 1) DONE, PARTIAL or BLOCKED. 2) Proof: compare output. 3) Files changed. 4) Unsure names. 5) New parking-lot items.

A good answer: Clear names, a short comment on what the routine does, no byte changes, and make compare OK.

Batch dispatch

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

Prompt for Claude
GOAL: Start <N> workers, one per unit, from the top of the ledger.
READ FIRST: `ledger/functions.csv` (status column only), `PROGRESS.md`.
FENCE: Do not edit code yourself. Give each worker its own worktree, one bank file, and the unit-loop card with its unit filled in.
CHECK: Run `make compare` (Path B) or `bash tools/check.sh` (Path A) on the merged branch. Done means it passes and every worker returned DONE, PARTIAL or BLOCKED.
BUDGET: Stop the batch if two workers fail on the same cause.
IF BLOCKED: Stop. Tell me the shared cause.
OTHER PROBLEMS: Collect workers' parking-lot items in `notes/parking-lot.md`.
REPORT: 1) DONE, PARTIAL or BLOCKED. 2) Proof: the merged check output. 3) Units done, partial, blocked. 4) What is next. 5) New parking-lot items.

A good answer: A short table of units and outcomes, and a passing check after the merge, not just in each worktree.

Debug with evidence

When a frame is wrong, colors are off, the game hangs, or sound goes silent.

Prompt for Claude
GOAL: Prove the cause of <BUG>. Do not fix it yet.
READ FIRST: `logs/<ROUTE>.input`, `logs/<STATE>.json`, `notes/findings.md`, the Pan Docs page for the hardware involved.
FENCE: Edit only `notes/findings.md`. You may add one temporary log line, and you must remove it after.
CHECK: On Path A, run `./output/game/build/game --headless --model cgb --limit-frames <N> --input "$(cat logs/<ROUTE>.input)" --dump-state logs/<STATE>.json --dump-frames <N> --screenshot-prefix logs/debug` twice. On Path B, run `<SHADOW_COMMAND> --routine <BANK:ADDRESS>` twice. Done means a cause with an address, a register or memory value, and a replay that shows it every time. Compare the same frame in SameBoy.
BUDGET: 60 minutes or 2 wrong theories.
IF BLOCKED: Stop. List what you ruled out and why.
OTHER PROBLEMS: Write them in `notes/parking-lot.md`.
REPORT: 1) DONE, PARTIAL or BLOCKED. 2) Proof: the replay output. 3) Files changed. 4) Cause in one sentence. 5) New parking-lot items.

A good answer: A cause tied to an address and a value, a replay that shows it, a SameBoy comparison, and no code change yet.

Verify and hunt for cheating

In a fresh session after any batch of claimed work.

Prompt for Claude
GOAL: Check that the work in commits <RANGE> is real.
READ FIRST: `git log --stat <RANGE>`, `PROGRESS.md`.
FENCE: Read only. Do not fix anything.
CHECK: Rerun `bash tools/check.sh`, `bash tools/regress.sh`, `make compare` or the shadow check as they apply. Look for edits to generated C, to the disassembly folder, to `*.sha1` or route files, to the check scripts, for stubs that return early, for hand-written game addresses, and for entry points with no proof.
BUDGET: 45 minutes.
IF BLOCKED: Stop. Tell me what you could not rerun.
OTHER PROBLEMS: Write them in `notes/parking-lot.md`.
REPORT: 1) DONE, PARTIAL or BLOCKED. 2) Proof: the check outputs. 3) Each suspicious change with file and line. 4) Verdict per commit. 5) New parking-lot items.

A good answer: Fresh check outputs and a verdict per commit. Any edit to an expected file, the disassembly or generated code is named.

Status report

At the end of a day, or when you return after a break.

Prompt for Claude
GOAL: Give me the project status from files only.
READ FIRST: `PROGRESS.md`, `ledger/functions.csv` (counts by status), `notes/parking-lot.md`.
FENCE: Read only.
CHECK: Run `bash tools/check.sh` or `make compare` and paste the last lines. On Path B also run `python3 tools/progress.py`.
BUDGET: 10 minutes.
IF BLOCKED: Stop. Say which file is missing.
REPORT: 1) Check output. 2) Counts by status. 3) Fallbacks or routines left. 4) Open blockers. 5) Parking-lot items waiting for me.

A good answer: Numbers from the ledger and a fresh check result. No guesses.

Session handoff

Before you close a session or switch plans.

Prompt for Claude
GOAL: Write what the next session needs to continue.
READ FIRST: this session's changes (`git diff --stat`), `PROGRESS.md`.
FENCE: Edit only `PROGRESS.md` and `notes/findings.md`.
CHECK: Run `bash tools/check.sh` (Path A) or `make compare` (Path B), then `git status --short`. Done means PROGRESS.md has: the last check output, the unit in progress, the next 3 units, dead ends tried, environment settings used, and exact commands to resume.
BUDGET: 10 minutes.
IF BLOCKED: Stop and say what you do not know.
OTHER PROBLEMS: Write them in `notes/parking-lot.md`.
REPORT: 1) DONE, PARTIAL or BLOCKED. 2) Proof: the new PROGRESS.md section. 3) Files changed.

A good answer: A short section a new session can follow without the chat history.

Blocked or cannot deliver

When a unit keeps failing, or you suspect it cannot be done.

Prompt for Claude
GOAL: Give me a plain answer on whether <UNIT> can be done now.
READ FIRST: `notes/findings.md` entries for <UNIT>, `gb-recompiled/GBC.md` (Known gaps).
FENCE: Read only. No new attempts.
CHECK: Run the unit's own check once, unchanged, such as `make compare` or the replay command in `notes/findings.md`. Paste the last 20 lines. Done means you name what blocks it, the evidence, and 2 or 3 options with cost.
BUDGET: 15 minutes.
IF BLOCKED: Say "I do not know yet" and what would tell us.
REPORT: 1) BLOCKED or NOT BLOCKED. 2) Proof. 3) Options with cost. 4) Your pick and why.

A good answer: A plain yes or no, with evidence, like a listed Color hardware gap or cycle-exact timing, and real options such as an emulator core.

Scope change request

When Claude wants to leave the fence, like adding widescreen or touching the runtime.

Prompt for Claude
GOAL: Ask me before you change scope.
READ FIRST: `SCOPE.md`.
FENCE: Do not make the change. Write the request only, in `notes/scope-requests.md`.
CHECK: Run `git status --short`. It must list only `notes/scope-requests.md`. Done means the request names the change, why it is needed, files it touches, the risk to the state-hash tests, and how to undo it.
BUDGET: 10 minutes.
IF BLOCKED: Stop and ask me.
REPORT: 1) The request. 2) What you will do if I say no.

A good answer: A short request you can approve or refuse. No code changed.

Keep Claude on task

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

The detourThe rule to give Claude
Claude patches the generated C to make a bug go away. The next regenerate wipes it out.Never edit files under output/. Fix the recompiler, the config, a native patch or the runtime, then regenerate. Add a hook that blocks edits there.
Claude edits the disassembly to make a port easier. The ROM then stops matching.The disassembly folder is read-only during the port. It must stay a byte-identical build. This is the first rule in Pokemon TCG 2 Native Port's AGENTS.md.
Claude types game RAM addresses from memory. The first cheats in Resident Evil Gaiden: Recompiled used made-up addresses and did nothing.Take every game address from the .sym file or a Ghidra listing. The Oracles Project enforces this with a script that fails on hand-written addresses.
Claude adds guessed entry points so the fallback count drops. Data then turns into fake code.Add an entry point only with a trace hit or a Ghidra listing that proves it is code. Paste the proof in the report.
Claude writes a game-specific hack, like a fake event, to get past a hang.No per-game hacks or stubs in the runtime without my approval. Find the hardware cause first, with Pan Docs and SameBoy.
Claude edits an expected hash or state file so a test turns green.Never edit *.sha1, tests/routes/*.json or check scripts. Ask me first, with the reason.
Claude keeps an old code path next to the new one, just in case.Delete before adding. A replaced feature goes in the same commit as its replacement. Both The Oracles Project and Pokemon TCG 2 Native Port use this rule.
On a port, Claude bulk-translates many routines at once.One routine per unit, with the shadow check after each one.

Problems other projects hit

A newer or older RGBDS will not build the disassembly. RGBDS changes its syntax between releases.

Fix: Pin the version in .rgbds-version and in CI. pret asks for 1.0.4 and says to fall back to it if a newer one fails.

Seen in: pokegold, poketcg, warioland3

The rebuilt compressor or graphics converter does not give the original bytes for some files.

Fix: Add per-file flags in the Makefile, like --no-trailing-repeat, instead of storing those files as raw bytes. Keep a small patch tool for the last few.

Seen in: warioland3, pokegold, poketcg

An overlay build passes its hash, but much of the game is still raw bytes from the base ROM.

Fix: Track how many raw bytes remain per bank with a progress script. Do not call the disassembly done until no base ROM is needed.

Seen in: pokepuzzle, telefang

The recompiler missed code reached by script opcodes, and calls that read data bytes right after the call.

Fix: Check every script opcode table in full. Teach the analyzer the call pattern, then replay routes until no fallbacks remain.

Seen in: Shantae Recomp

Replacing routines with new code shifted interrupts by a few cycles. The vblank handler then finished different work.

Fix: Check cycles per routine, not just results. If timing keeps breaking, run the original ROM in an accurate emulator core and hook it.

Seen in: The Oracles Project, Oracles Decomp

Sound bugs in the runtime silenced voice samples and music.

Fix: Test sound against Pan Docs and SameBoy. Add a test that needs no ROM for each sound fix.

Seen in: Resident Evil Gaiden: Recompiled

An early release shipped the author's own settings file, with infinite health on.

Fix: Ship a separate default settings file. Never package your live one.

Seen in: Resident Evil Gaiden: Recompiled

A bad dump of the game will never match, and users blame the project.

Fix: Check the ROM hash at load. Publish known bad hashes too. Test the gate with a one-bit bad dump and a short dump.

Seen in: telefang, Shantae Recomp

Random input rarely reaches deep parts of the game, so those parts stay untested.

Fix: Record real routes, or use the game's own debug menu to jump to a scene.

Seen in: Pokemon TCG 2 Native Port

On Windows, the SameBoy core does not build with Microsoft's compiler, and WSL files live apart from Windows files.

Fix: Build with MSYS2 UCRT64 on Windows. In WSL, keep the repo under /mnt/c/ as pret's guide says.

Seen in: The Oracles Project, pokegold

How long it takes

Max 20x (now called Ultra)Max 5x (now called Max)One person, no AIHow sure
Path A: running on PC1 to 6 weeks2 to 15 weeks2 to 7 monthsmedium confidence
Path B: full decomp5 to 12 months9 months to 2.5 years3 to 8 yearslow confidence

Read this first

These numbers assume one medium game and one person giving 10 to 15 hours a week. Path A is medium confidence. Several dated AI projects on this system agree on days to weeks. But most public histories start on release day, so earlier work is hidden. Two of the three gbrecomp projects have only a store AI flag. Path B is low confidence. No AI project here built a Color disassembly from zero with dates. The AI ports started from disassemblies that took the community years. The no-AI Path A range is a guess from the steps, since no pre-agent Color recomp was reviewed.

What makes it faster or slower

  • How well gbrecomp handles the game. Games with script engines, odd calls or RAM code need many harvest rounds.
  • Whether a byte-matching disassembly already exists. With one, Path A gets real names and Path B starts at the port.
  • Which Color features the game leans on. Heavy use of HDMA, double speed or exact timing hits the tool's known gaps.
  • ROM size. A 2 MB game has 128 banks, and a 4 MB game has 256, to map and name.
  • Extras like widescreen, translations or HD art. Each adds runtime work and new tests.
  • Your review time. Each unit needs a look at its proof, and that is your 10 to 15 hours a week.

The facts behind these numbers

ProjectWhat happenedLink
Pokemon Pinball RecompiledFirst commit and first release on 2026-11-12. Its 59 commits span about 68 minutes. 1 contributor. AI use declared by the developer. Only a Mac build was confirmed.source
Shantae RecompFirst commit and first release 2026-09-25. 23 releases by 2026-10-22, reaching native coverage of all 256 banks. 1 contributor. AI flag set by the store.source
Resident Evil Gaiden: RecompiledFirst commit and first release 2026-08-25. v0.4.2 on 2026-11-07 after sound and widescreen fixes. 3 contributors. AI flag set by the store.source
Oracles DecompFirst commit 2026-09-07. First release 2026-10-23, about 7 weeks. 697 commits, 3 contributors. README says AI helped. Started from a finished community disassembly.source
Pokemon TCG 2 Native PortPort work began 2026-10-31. First release 2026-11-01. Built on the finished pret/poketcg2 disassembly, with an AGENTS.md.source
Gen2RecompedFirst commit 2026-08-09. First release 2026-10-17, about 10 weeks. 5 contributors. Docs show agent session handoffs. A new engine, not a recomp.source
pret/pokegoldFirst commit 2015-07-09. 36 contributors and 1,030 commits. Builds all six outputs with matching SHA-1. A pre-agent, many-person disassembly.source
telefangFirst commit 2016-03-27. 4 contributors, 946 commits. Still a partial disassembly; last master commit 2025-10-19.source
warioland3One person. First commit 2021-03-15. 331 commits. Builds a matching ROM with no base ROM.source

Projects reviewed (13)

RecompLauncher lists fewer than 10 games for Game Boy Color: 8 projects for 8 games. All 8 were reviewed. 5 more came from outside RecompLauncher: pokegold, poketcg, telefang, pokepuzzle and warioland3. All 5 are disassemblies. More than 10 real projects exist; the outside search also found smaller disassemblies that were not reviewed.

What each project teaches

pokegold

Pokemon Gold / Pokemon Silver

From outside RecompLaunchermatching decompcomplete
RGBDS 1.0.4GNU makepret C helper toolsGitHub Actions

Started: 2015-07. First release: none. Code: SM83 assembly (RGBDS).

AI: none stated

Lesson: One source tree builds six checked files from assembler switches. make compare is the one pass or fail check.

Gen2Recomped

Pokemon Crystal (also Gold and Silver)

Top rated on RecompLauncherreimplementationin progress
LÖVE 11.xLuaJITpret disassemblies as referencePython + Pillow

Started: 2026-08. First release: 2026-10. Code: Lua.

AI: yes: docs include agent session handoffs and a committed 'Claude outputs/' folder; the catalog flag was set by the store

Lesson: A finished disassembly can be the spec for a new engine. A handoff file lets a fresh session pick up the work.

poketcg

Pokemon Trading Card Game

From outside RecompLaunchermatching decompin progress
RGBDS 1.0.4GNU maketools/tcgdisasm.pypret C helper tools

Started: 2014-04. First release: none. Code: SM83 assembly (RGBDS).

AI: none stated

Lesson: Rebuild before you disassemble more, so labels are fresh. Use address placeholders like Func_9000 until you know a name.

The Oracles Project

The Legend of Zelda: Oracle of Seasons / Oracle of Ages

Top rated on RecompLauncheremulator-basedin progress
SameBoy coremGBA coreoracles-disasm symbolsCMakeSDL 3MSYS2 UCRT64

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

AI: yes: README says the repo was built with AI help, held by CI, route replays and review

Lesson: When a recomp keeps breaking on timing, an accurate emulator core plus hooks is a valid Path A. A script bans hand-written game addresses.

Oracles Decomp

The Legend of Zelda: Oracle of Ages; The Legend of Zelda: Oracle of Seasons

Top rated on RecompLauncherreimplementationplayable
oracles-disasm (WLA-DX)own SM83 coreCMake + NinjaSDL3MinGW-w64

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

AI: yes: README AI disclosure says the native code had help from generative AI, a Claude model

Lesson: This is the Path B port model here. Each C routine runs beside the original and must match registers, memory and cycles.

telefang

Keitai Denjuu Telefang (Power Version / Speed Version)

From outside RecompLaunchermatching decompin progress
RGBDS 0.5.0 or newerGNU makePython 3 rip scriptscmp

Started: 2016-03. First release: 2018-03 (translation patch). Code: SM83 assembly (RGBDS).

AI: none stated

Lesson: Keep the matching listing on one branch and a translation on another. Then the byte check never has to bend.

LADXHD Updated

The Legend of Zelda: Link's Awakening DX

Top rated on RecompLauncherreimplementationplayable
MonoGame.NET 9 SDKxdelta3 patchesWine (shader builds)

Started: unknown. First release: 2026-07. Code: C#.

AI: yes: AI-USAGE.md says about 5% of the code came from chatbots, each answer reviewed

Lesson: This continues a PC fan port, not a decomp. It ships patches plus a patcher, so users supply their own files.

pokepuzzle

Pokemon Puzzle Challenge

From outside RecompLaunchermatching decompin progress
RGBDS 1.0.4rgblink -O overlayGNU make

Started: 2015-09. First release: none. Code: SM83 assembly (RGBDS).

AI: none stated

Lesson: The overlay start matches from day one. But a passing hash then proves only the parts you have written.

Resident Evil Gaiden: Recompiled

Resident Evil Gaiden

Top rated on RecompLauncherstatic recompplayable
gb-recompiledmgbdisGhidraCMake + Ninja + ClangSDL2Dear ImGui

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

AI: none stated in the repo; the catalog flag was set by the store; README calls the bundled HD art machine-made placeholder

Lesson: A native-patch file swaps single recompiled routines for hand-written C by bank and address. Generated code stays untouched.

warioland3

Wario Land 3

From outside RecompLaunchermatching decompin progress
RGBDS 1.0.0 (CI)GNU makeown compressor and graphics toolstools/progress.py

Started: 2021-03. First release: none. Code: SM83 assembly (RGBDS).

AI: none stated

Lesson: One person reached a full match with no base ROM. A progress script counts raw bytes and unnamed labels per bank.

Shantae Recomp

Shantae

Top rated on RecompLauncherstatic recompplayable
gbrecompiled (fork)recomp-uiGhidra + GhidraBoyMSYS2SDL2librashader

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

AI: none stated; the catalog flag was set by the store

Lesson: Play alone missed code. The project moved to covering every ROM address, with differential checks over all 256 banks.

Pokemon TCG 2 Native Port

Pokemon Card GB2: GR Dan Sanjou! (Pokemon Trading Card Game 2)

Top rated on RecompLauncherstatic recompplayable
pret/poketcg2RGBDS 1.0.4own SM83-to-Rust translatorRustNixSDL3

Started: 2026-10. First release: 2026-11. Code: Rust.

AI: yes: AGENTS.md gives rules for coding agents; the catalog flag was set by the store Agent files: AGENTS.md.

Lesson: Translate from a finished disassembly, never edit it, and run every routine in lockstep with a separate interpreter.

Pokemon Pinball Recompiled

Pokemon Pinball

Top rated on RecompLauncherstatic recompin progress
GB RecompiledCMakeNinjaZigSDL2PyInstaller

Started: 2026-11. First release: 2026-11. Code: Python (launcher).

AI: yes: AI use for code declared by the developer to the catalog

Lesson: A launcher that recompiles the player's own ROM ships no game code. But a one-hour build is not a tested one.

What we could not check

  • Nothing in this guide was run here. No ROM was used. Commands and flags were checked against the tools' docs and source code only.
  • No AI-assisted Game Boy Color disassembly built from zero, with dates, was found. The Path B times are an estimate.
  • Several repos were released on their first public day. Their real work hours are unknown.
  • Whether any commercial Game Boy Color game was written in C was not confirmed. A forum claim about one unreleased game was seen only in a search snippet.
  • The --trace-entries output and gbrecomp --use-trace use the same bank:address format in the source code. That was read, not run.
  • Shantae Recomp's cover-every-address setting is in its fork of the recompiler. It was not confirmed in the main gb-recompiled tool.
  • Ghidra 11.3.2 with GhidraBoy and GhidraMCP 1.4 together was not tested. The claude mcp add line for 1.4 was not run.
  • Which AI tools Shantae Recomp and Resident Evil Gaiden: Recompiled used is unknown. Their AI flags were set by the store.
  • Whether Oracles Decomp's C was mostly written by hand or by its transliteration script is not stated precisely.
  • The scripts in the steps, like tools/check.sh, tools/regress.sh, tools/progress.py and the shadow script, are scripts you write. Their names are suggestions.
  • LADXHD Updated is hosted on GitLab. Its first commit date and contributor count were not available.
  • The MSYS2 and Ubuntu package names were checked as web pages only. They were not installed here.
  • Release v0.1.0 of gb-recompiled is older than its docs. Some flags in the steps may be missing from it. This was not tested.
  • Whether a pret .sym file loads cleanly with gbrecomp --symbols was not run.
  • The name of the plugin zip inside GhidraMCP-release-1-4.zip was not checked.
  • Pokemon TCG 2 Native Port lists 2026-10 as its start. Its repo history begins in 2019 because it is a fork of pret/poketcg2.

Sources