Kajar notes of ChronoRET, rebuilding Chrono Trigger one matched routine at a time

16 October 2026: the credits, and a future that refused to change

The fifth and sixth batches of the packed $C3 module take it from half to 70%. The module is a block of code the game keeps compressed in the ROM and unpacks into RAM at $7E:3000 when it needs it; the jet-bike race was its first part to be matched. This batch is three of its smaller programs, each a scene of its own.

Where the module stands

A map of the unpacked module from $7E:3000, one row per 4 KB: the first four rows are almost all matched code and data, the rows from $7000 to $9FFF are still mostly unmatched, and the data from $A000 on is matched

The module as it unpacks, drawn from the project's own list of its routines: green is matched code, gold is matched data (tables, scripts and fonts), dark blue is still the ROM's own bytes. Everything below $78DD is now done; the next batch starts there.

Three small scenes

Programs 7 and 8 draw a screen of text with sparks drifting across it, fade it in step by step, and wait. Its text is drawn as sprites from a small font the module expands from 2 to 4 bits per pixel as it loads. A button press ends it with a soft reset: the module jumps straight back to the game's reset routine.

Program 3 takes whatever picture the field left on screen and makes it wave, by giving each column of tiles its own vertical offset and each line its own horizontal shift. One of its variants then wipes the picture away with a window that closes in. A small surprise here: program 3 writes a zero over an operand inside another routine's code before calling it, which changes where that routine copies its map. The reviewers caught that the original description of it was incomplete, and the source now spells the write out.

Program 1 shows the staff credits, one page per run: the names are drawn on a background layer and the roles as sprites, and the page fades out after 90 frames.

Program 5, and three writes past the end

The sixth batch, later the same day, is program 5: a mode-7 scene where the background map rotates under a set of characters who circle and spin around the middle of the screen, inside two round windows that open and close. It took the module to 70% of its code. Placing the characters uses the polar maths the module already had (sine, cosine and a square root), and turning the map uses the console's own multiplier, which takes a signed 16-bit number times a signed 8-bit one; much of the review was about which of those values are signed and where each carry comes from.

The round windows are drawn by a routine in bank $C3 that fills a table with the left and right edge of the window for each of the screen's 224 lines. The reviews found that the original writes past both tables:

A strip of memory from $7E:EFCE to $7E:F442: the two 448-byte window tables, with 50 bytes written before the first, 50 after it, and 3 after the second

The first window is a circle of radius $88 (136 lines) around line $70 (112), so it reaches 25 lines above the top of the screen and 25 below the bottom, and the routine, which never clips, writes those lines too: 50 bytes before the table and 50 after it. The second window is fine at every size except 0, where the routine's "empty the table" path copies three bytes more than the table holds. Nothing in the scene reads those bytes, so the game shows no sign of it; the headers now say exactly which bytes are hit.

The event scripts, readable

Every location in the game runs on event scripts: small programs in the game's own bytecode that place characters, move them, open dialogue, play sounds and branch on story flags. There are 462 packs of them, about 568 KB once unpacked, and until today they were rebuilt from packed bytes copied out of the player's ROM. They are now rebuilt from a readable disassembly: one opcode per line, named after the routine that runs it, with jumps written as labels.

The hard part is knowing how long each opcode is. Nothing in the ROM says so; only the code of each opcode's handler knows, by how far it moves the script position. So the project's tools now include a small interpreter that runs each handler's matched code on symbols instead of numbers: the script bytes are known, everything else is unknown, and a branch on an unknown value is followed both ways. What comes out is which script bytes the handler reads, where it leaves the script position, and whether it goes on, waits or jumps.

A toy script of five bytes and a toy handler for opcode $12: the handler reads the bytes at Y+1 and Y+2 and moves Y on by 3, so the opcode has two operands and a length of 3

The idea on a toy example: the handler's own reads and its last move of Y give the opcode's operands, its length and its exits.

Every one of the 461 non-empty packs comes back byte for byte from its readable form, and the assembler now checks the structure, not only the bytes: each line must have exactly as many operands as its handler reads, and every jump must land on an opcode. That last check came from the independent audit, which showed that without it any regrouping of the same bytes would still have passed. The audit took two rounds and found six weak spots in all; each is closed and has a test.

Reviews

The round sent back 7 of 96 headers. All of them were descriptions that said more than the code does: a map size given as 64x32 where the register value means 32x64, a timing list off by one step, the odd and even lines of the wave swapped, and an exit that leaves the direct page register pointing at the hardware registers rather than at zero. The second round approved all 18 changed headers.

Where that leaves things

The packed $C3 module is at 14,750 of its 21,020 code bytes (70.17%), all 321 of its matched routines verified. The 65816 code outside it and the SPC700 sound driver are still complete and verified.

The devlog itself also changed today: every post now has diagrams, and checking them against the code corrected eight small facts in older posts, each marked where it was fixed.

Next

The rest of the module, programs 6, 10, 12 and 13, and the battle scripts, the next step of the data plan.

← Back to devlog