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
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.
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.
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.
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.gbcandsha256sum 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.gbcprints bytes$0143to$0149. The first is the Color flag.$80means the game also runs on the old Game Boy.$C0means Color only.gbrecomppicks 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 ClaudeGOAL: 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.mdprints 1 or more, and you approvedSCOPE.md.Proof this works: Shantae Recomp, Resident Evil Gaiden: Recompiled, https://gbdev.io/pandocs/The_Cartridge_Header.html
- Hash your own dump in WSL:
Set up the repo and the recompiler
You get
gbrecompand 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 itstools/folder later. Release v0.1.0 also hasgb-recompiled-linux-x64.tar.gzandgb-recompiled-windows-x64.zip, but it is older than the docs. - To build from source:
cmake -G Ninja -S gb-recompiled -B gb-recompiled/buildthenninja -C gb-recompiled/build. The program isgb-recompiled/build/bin/gbrecomp; add that folder toPATH. - Always generate with
--output-prefix game. Without it, file and program names come from the ROM's file name. With it, the program is alwaysoutput/game/build/game. - Add
*.gb,*.gbc,*.sav,roms/,output/andlogs/to.gitignorebefore the first commit. - Write
tools/check.sh. It regenerates, builds, and runs--headless --limit-frames 120. That is the smoke run the tool's ownAGENTS.mdasks for. - Write
CLAUDE.md,PROGRESS.mdandnotes/parking-lot.md.
Prompt for ClaudeGOAL: 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.shexits 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
- In WSL:
Map the ROM and get names
You give the recompiler real names and real code starts. Many Color games have a disassembly whose
.symfile 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
.symfile for your exact revision. pret pushes them to asymbolsbranch. For example,pokecrystal11.symis Crystal 1.1. - Pass it when you generate:
gbrecomp roms/game.gbc --symbols notes/game.sym -o output/game --output-prefix game. The tool'sAGENTS.mdsays 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
GhidraBoyzip 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-$7FFFreaches. Its README says you set the bank by hand for each such call. - List interrupt vectors,
rsthandlers, jump tables, script interpreters and code copied into RAM. Write each innotes/map.mdas bank:address with a reason. - Check each script interpreter's opcode table in full. Shantae's first scanner missed opcodes
2Eand32.
Prompt for ClaudeGOAL: 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 innotes/map.mdis one entry with aproof:field, sogrep -vc 'proof:' notes/map.mdprints 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
- If a pret disassembly exists, get the
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.symif you have one. - Build:
cmake -G Ninja -S output/game -B output/game/buildthenninja -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, likelogs/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.jsonfile. It lists every function by bank and address.
Prompt for ClaudeGOAL: 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 andtest -f logs/shot_00300.ppmexits 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
- Generate:
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 printsNo 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 with0x. A leading zero makes the bank octal, so0aor08becomes bank 0. Write bank 10 as10or0x0a. - 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 ClaudeGOAL: 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.pyprintsNo interpreter fallback recorded, except for RAM code listed innotes/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
- Record a real route through the game:
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.mduses the same order. - Fix the recompiler or runtime, never the generated C. For a game-only change, use a
--native-patchmanifest, as Resident Evil Gaiden: Recompiled did.
Prompt for ClaudeGOAL: 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 fromlogs/bug.stateshows no mismatch and no fallback.bash tools/check.shexits 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
- Run the differential check from boot:
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 withmkdir -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 ClaudeGOAL: 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.shpasses 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
- Copy each route's input file out of the ignored
Build for Windows and ship without the game
You build a Windows program and package it. Know this first:
gbrecompwrites the whole ROM intooutput/game/game_rom.cand 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
gbrecompon 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 ClaudeGOAL: 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
- Way 1, as in Pokemon Pinball Recompiled: ship only a launcher. It runs
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.
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-disasmandElectroDeoxys/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/pokepuzzleandtelefangare partial like that. - Write
SCOPE.md: revision, hash, start point, port target and language, and the check.
Prompt for ClaudeGOAL: 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.mdnames a starting repo, or says none exists, andgrep -c <SHA1> SCOPE.mdprints 1 or more. You approved it.Proof this works: pokegold, pokepuzzle, Oracles Decomp, https://github.com/pret/pokecrystal
- Check pret.github.io and search GitHub for "<GAME> disassembly". Known ones include
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, thenmkdir rgbds && tar xf rgbds-linux-x86_64.tar.xz -C rgbds && cd rgbds && sudo ./install.sh. Check it withrgbasm -V. Write1.0.4into.rgbds-version. - Full listing:
git clone https://github.com/mattcurrie/mgbdis, thenpython3 mgbdis/mgbdis.py roms/game.gbc. Thencd disassembly && make. It writesgame.gbc. - Overlay start: link with
rgblink -O baserom.gbc. Every byte you have not disassembled comes from your ROM.pret/pokepuzzleandtelefangbuild this way. - Put
comparein a top-levelMakefile. It runsmake -C disassembly, thensha1sum -c rom.sha1, aspret/poketcgandwarioland3check their ROMs. Do not put it indisassembly/Makefile:mgbdisrewrites that file on every run.
Prompt for ClaudeGOAL: 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 compareprints OK, and fails when one byte is changed.Proof this works: poketcg, pokepuzzle, telefang, https://github.com/mattcurrie/mgbdis
- In WSL, install the basics:
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.txtfrom the Path A runtime, or thegame_metadata.jsonfile fromgbrecomp. The trace writes onebank:addressline per entry, with the bank in decimal. - Write them into a symbol file as
.codeblocks. Mark tables.data, text.textand tiles.image. Entries look like03:47f2 Read_Joypadand0d:4800 .data:200. All.symnumbers are hex, so trace bank10becomes0a. Keep only ROM addresses below8000. mgbdisreads the symbol file only if it sits next to the ROM with the same name:roms/game.symforroms/game.gbc. Keep a master copy atnotes/game.sym.- Re-run with
python3 mgbdis/mgbdis.py roms/game.gbc --overwrite. Do all re-runs before you hand-edit any.asmfile, 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.symfile, aswarioland3does.
Prompt for ClaudeGOAL: 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 compareprints OK andledger/routines.csvlists 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
- Record which addresses ran during play. Use
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
makefirst, so the.symfile has current labels.pret/poketcgwarns that stale symbols give wrong disassembly output. - Use the SameBoy or BGB debugger to watch which RAM it writes. BGB loads your
.symfile. - 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 tonamed.
Prompt for ClaudeGOAL: 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 compareprints OK and the routine has no raw addresses left.Proof this works: poketcg, https://bgb.bircd.org/
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 compareat 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/poketcggives both rules. - Run
make compareafter every routine, and again after each merge. - Record dead ends in
notes/findings.mdso nobody repeats them.
Prompt for ClaudeGOAL: 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 saysnamedorhard, andmake compareprints OK on the merged branch.Proof this works: pokegold, poketcg, https://github.com/pret/pokecrystal
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
.imageinnotes/game.symbefore hand edits, then re-runmgbdis --overwrite. It writes PNG files andrgbgfxrules. - After hand edits, write each block out as PNG and add a make rule that runs
rgbgfx. - Build Color palettes from PNG too.
pret/pokegoldhas a.gbcpalrule for that. - Some files need per-file flags to match.
warioland3andpret/poketcgeach list about 40 such exceptions in the Makefile. - Compressed data needs a compressor that copies the original's choices.
pret/poketcgrunstools/compressor -mfor that. - Keep text in
.txtor CSV files built at compile time, aswarioland3andtelefangdo.
Prompt for ClaudeGOAL: 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 compareprints OK with graphics built from PNG files.Proof this works: pokegold, warioland3, poketcg
- Mark tile blocks as
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/poketcgandwarioland3do this. - After the build, fail CI if
git diff-index --quiet HEADfinds changes. That catches generated files that drifted. - Publish
.symand.mapfiles to asymbolsbranch, as pret does. Path A users can then pass them togbrecomp --symbols. - Scan
git ls-filesfor any.gbcfile or large binary. - Have a fresh session re-check ten random
namedrows.
Prompt for ClaudeGOAL: 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 givesmake compareOK and a clean tree, and git holds no game files.Proof this works: poketcg, warioland3, https://github.com/pret/pokegold/tree/symbols
- Add a CI job that builds the pinned RGBDS tag from source, then runs
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 ClaudeGOAL: 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
- Keep the disassembly in its own folder, as a git submodule. Pokemon TCG 2 Native Port never edits its
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 ClaudeGOAL: 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
| Tool | What it does | How to get it | How 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 MSYS2 | gb-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 MSYS2 | gb-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 shell | msys2.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 it | Ghidra 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, restart | README 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 it | README 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 it | bgb.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 that | Releases (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
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
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
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
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
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
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
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
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)
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)
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.
| Job | What it means on this system | Max 20x (now called Ultra) | Max 5x (now called Max) |
|---|---|---|---|
| Plan the project | Pin 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 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 | Get 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 file | Find 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 mediumRaise to high for function edges and data layouts. A mistake there spreads. |
| Bulk units | Fallback 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 mediumHaiku 5.5 for the plainest units. | Sonnet 5.5 effort mediumFewer workers at once. Haiku 5.5 for the plainest units. |
| Hard units | Code 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 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 | Color 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 mediumRaise to high for timing and graphics bugs. |
| Find a bug | Wrong 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 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 replays all routes, reruns make compare, and looks for edited hashes, stubs, hand-written addresses and generated-code edits. | Opus 5.5 effort mediumAlways a new session, never the one that did the work. | Sonnet 5.5 effort highAlways a new session, never the one that did the work. |
| Keep the records | Update PROGRESS.md, 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
- 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.jsonor the.symfile 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
Readdeny rule for generated C, such asRead(./output/**/*.c). Read logs and metadata withgrepor 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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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 detour | The 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 AI | How sure | |
|---|---|---|---|---|
| Path A: running on PC | 1 to 6 weeks | 2 to 15 weeks | 2 to 7 months | medium confidence |
| Path B: full decomp | 5 to 12 months | 9 months to 2.5 years | 3 to 8 years | low 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
gbrecomphandles 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
| Project | What happened | Link |
|---|---|---|
| Pokemon Pinball Recompiled | First 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 Recomp | First 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: Recompiled | First 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 Decomp | First 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 Port | Port work began 2026-10-31. First release 2026-11-01. Built on the finished pret/poketcg2 disassembly, with an AGENTS.md. | source |
| Gen2Recomped | First 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/pokegold | First 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 |
| telefang | First commit 2016-03-27. 4 contributors, 946 commits. Still a partial disassembly; last master commit 2025-10-19. | source |
| warioland3 | One 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
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)
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
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
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
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)
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
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
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
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
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
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)
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
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-entriesoutput andgbrecomp --use-traceuse the samebank:addressformat 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-recompiledtool. - Ghidra 11.3.2 with GhidraBoy and
GhidraMCP1.4 together was not tested. Theclaude mcp addline 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.pyand 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-recompiledis older than its docs. Some flags in the steps may be missing from it. This was not tested. - Whether a pret
.symfile loads cleanly withgbrecomp --symbolswas not run. - The name of the plugin zip inside
GhidraMCP-release-1-4.zipwas 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
- gb-recompiled README
- gb-recompiled GBC.md (Game Boy Color status)
- gb-recompiled RUNTIME.md
- gb-recompiled AGENTS.md
- gb-recompiled NATIVE_PATCHES.md
- gb-recompiled GROUND_TRUTH_WORKFLOW.md
- gb-recompiled command-line flags (main.cpp)
- Pan Docs: Specifications
- Pan Docs: CGB Registers
- Pan Docs: Palettes
- Pan Docs: The Cartridge Header
- Official Game Boy SDK (retroreversing)
- RGBDS README
- RGBDS Linux install
- pret/pokecrystal README
- pret/pokecrystal INSTALL.md
- pret/pokecrystal symbols branch
- oracles-disasm README
- WLA-DX releases
- mgbdis README
- GhidraBoy README
- GhidraMCP 1.4 release
- GhidraMCP 1.4 extension.properties
- Ghidra releases
- SameBoy README
- PyBoy README
- BGB emulator
- MSYS2
- MSYS2 UCRT64 SDL2 package
- Ubuntu 24.04 libsdl2-dev package
- Ghidra 11.3.2 release
- gb-recompiled code emitter (generated names and runtime flags)
- gb-recompiled SDL platform (frame dump names)
- gb-recompiled interpreter log summarizer
- mgbdis source (command-line options)
- RGBDS 1.0.4 rgblink manual (overlay option)
- Claude Code MCP docs
- mstan/gbrecompiled (fork used by Shantae Recomp's fork)