Kajar
Chrono Trigger
A byte-exact decompilation of the 1995 classic, one matched routine at a time
But… the future refused to change.
Game Over screen
Welcome to ChronoRET, a byte-exact matching decompilation of Chrono Trigger (SNES, US 1.0).
The goal is hand-labeled 65816 assembly that assembles back to a ROM that is bit-for-bit identical to the original 1995 cartridge. A routine only reaches the main branch once it is matched byte for byte, written to the project's readability standard, and approved by an independent review.
This project is not affiliated with or endorsed by Square or Square Enix in any way.
Project at a glance
| Bank | What is matched there |
|---|---|
$C0 |
The field engine: game loop, frame wait and NMI handler, location loading, map scrolling, object motion, the event script opcodes, the sprite system, and the reset code, header and vectors at the top of the bank |
$C1 |
The battle engine: BattleSys_Main and its turn lists, the enemy behaviour scripts, the PC command runner, action scripts, the battle menu, targeting and status bar |
$C2 |
The scene mode (tasks, scripts, sprite list, scene loading) and the text window decoder |
$FD |
MainInit, the battle helpers bank $C1 calls with JSL, the field HDMA set-up and the location animation set-up |
$CC, $CF, $FF |
Copies of matched code kept in other banks: the 8×8 multiply ($CC), a scaled sine and a four-digit number formatter ($CF), the wave tables ($FF) |
What the 100% covers, and what is left
The 100% is the game's 65C816 code: every byte of it that can run is matched, readable and verified. It is not the whole project. Still to do:
- The packed
$C3module (the jet-bike race), about 34 KB once unpacked, held compressed in the ROM. It is built from source and packed back to the exact bytes, and about 70% of its code is matched so far (the race, its display and several smaller scenes); the rest is still the ROM's bytes. - The game's data: graphics, music, maps, text, scripts and tables, about 3.9 MB. Every
byte of it is mapped and placed by the source, and the compressed blocks are rebuilt by the
project's own packer, but the contents still come from files extracted from the player's
own ROM. The game's text, 101 tables and palettes, and the location event scripts are now in
readable form, rebuilt to the exact bytes; turning the rest into readable form (battle and
scene scripts, graphics, maps, music) is the long part that remains; see
DATA_PLAN.mdin the code repository.
The build no longer starts from a copy of the original: it assembles everything onto a blank image, and needs the player's own ROM only to extract the data files. No game data is stored in the project.
How much of each bank is matched, and at which level, is generated on the ROM Map and Progress pages.
Start here
Reference
Matched regions
What is matched, bank by bank, is on the ROM Map, generated from the code repository on every update, so it is never out of date. The figures by level, bank and day are on Progress, and every routine of a system is listed on its page under Systems.
The 4 MB ROM is mostly graphics and music data; only about 240 KB is executable 65816 code.
Key documents
| Document | What it covers |
|---|---|
| Architecture | How the banks fit together: boot, the field frame, bank $C2, the battle |
| Systems | One page per game system, with its routines |
| Glossary | 65816 and SNES terms explained |
| asar Quirks | Assembler gotchas that affect byte-exactness |
| Contributing | Toolchain setup, match workflow, commit rules |
Methodology
A routine goes through these steps before it reaches the main branch:
- Draft —
make draft ADDR=C1:3714(tools/draft.py) disassembles the routine from its address, tracking the M/X flags, DP and DB, and writes asar source that already uses every name the project has for its operands. It checks that the draft assembles to the original bytes. - Make it readable — the matcher names what the draft could not (RAM, constants, calls, the routine itself) and writes the header: what the routine is for, its CPU state on entry and exit, and anything a reader would not guess. Unknown meanings stay hedged ("probably",
Unknames). - Gate —
make gateruns the byte-for-byte diff (make diff), proves that every byte the source claims is really emitted by it (make verify), and runs the readability lint (tools/lint_readability.py). The; Callersblock of each header is generated bytools/callers.py, never written by hand. - Review — someone who did not write the routine checks that its names and comments tell the truth. The verdict is a row in a review round file (
symbols/reviews/rNNN.csv) that carries the routine's source hash, so any later edit sends it back for review.
Each level includes the one before: matched → readable → verified. The figures are computed by tools/progress.py from the source and the review log; generated files are not committed, and no number is typed by hand.
Legal
Tools and source only — no ROM data, no extracted assets. To contribute or verify matches you need your own legally obtained copy of Chrono Trigger (SNES, US 1.0).
ROM SHA-256: