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

Last updated: October 9, 2026

Boot

Power-on and reset, from the vectors at the top of bank $C0 (seen by the CPU as bank $00) to the start of the game loop. All of it is matched: the reset code, the interrupt stubs and vectors, the ROM header, MainInit in bank $FD, and the cold init at the start of GameLoop.

See also: Frame cycle and NMI, ROM Map, Architecture.

Vectors

The vector table RomVectors ($00:FFE0–$FFFF) uses only four entries; every other slot is $FFFF:

Vector Address Target
Native BRK $FFE6 BRK_Handler ($00:FF18)
Native NMI $FFEA NMI_Stub ($00:FF10)
Native IRQ $FFEE IRQ_Stub ($00:FF14)
Emulation COP $FFF4 BRK_Handler
Emulation RESET $FFFC Reset ($00:FF00)

The emulation-mode NMI is unused: the NMI is off after reset during the three instructions Reset runs in emulation mode.

  • Reset ($00:FF00): SEI, CLC, XCE (native mode), JML MainInit. The SEI is for the soft resets, which arrive in native mode: Menu_PollPad ($C2:8523) jumps here when L+R+Select+Start are held, and two unmatched sites in banks $C3 and $CF do too.
  • NMI_Stub / IRQ_Stub: the native vectors can only point into bank $00, so these jump on through 4-byte trampolines in WRAM ($7E:0500, $7E:0504) that the game rewrites as it changes mode.
  • BRK_Handler: a crash trap; it reads a fixed address forever and never returns (the read is what a bus trace or debugger would see, inferred).

The cartridge header ($00:FFB0–$FFDF, RomExtHeader to RomChecksum) reads: maker "C3", game code "ACTE", title "CHRONO TRIGGER", HiROM + FastROM ($31), ROM + RAM + battery, 4 MB of ROM, 8 KB of save RAM, North America, version 1.0.

MainInit

MainInit ($FD:C000, 215 bytes) puts the CPU and PPU into a known state in five steps. Every register write goes through a relocated direct page, so each store is two bytes:

  1. CPU mode, stack (S = $06FF), DB = $00, DP = $4200 ($FD:C000–$C013);
  2. the CPU I/O registers through DP = $4200 ($FD:C014–$C035);
  3. DP = $2100, the screen into forced blank ($FD:C036–$C041);
  4. the PPU registers through DP = $2100 ($FD:C042–$C0D2); most are zeroed;
  5. JML GameLoop ($FD:C0D3).

It leaves with interrupts, NMI, DMA and HDMA off. It is also reached by JML MainInit at $C0:418D, in Evt_OpFF96_Reset (event opcode $FF $96), after InitHW: the game restarts from its cold start. Ppu_SetBgLayout ($FD:C0D7), right after it, sets a fixed background layout the same way.

GameLoop's cold init

GameLoop ($C0:000E) installs the interrupt trampolines (InstallNMI: JML NmiHandler into $7E:0500; InstallIRQ: JML IrqHandler into $7E:0504), sets DP = $0100, runs InitHW (interrupts off, forced blank, NMI, DMA and HDMA off, DB = $00), and clears WRAM with ClearRAMDMA in three blocks ($7E:0000–04FF, $7E:0700–FFFF, $7F:5080–A0FF), leaving the trampolines and the stack alone. ClearRAMDMA ($C0:2DF1) fills with DMA channel 7 from the PPU's Mode 7 multiply result (MPYL), which reads 0 because both operands are 0. Then it starts the sound driver (JSL Audio_DriverInit, bank $C7) and sends bank $C2 its start-up command (JSL BankC2_Entry8004), and falls into GameLoop_Main.

GameLoop is reached a second time by BRL from Scene_Unk0283 (probably the game-over path). The review of MainInit noted that its name is probably wrong, since it starts with an init sequence rather than a loop.

5functions matched
247code bytes matched
5readable
5verified

Functions

AddressFunctionSizeStatusNotes
$C0:FF00Reset16verifiedtarget of the emulation-mode RESET vector ($FFFC)
$C0:FF10NMI_Stub4verifiedNative-mode interrupt stubs ($FF10–$FF17)
$C0:FF14IRQ_Stub4verified
$C0:FF18BRK_Handler8verifiednative BRK and emulation-mode COP both land here
$FD:C000MainInit215verifiedCalled via: JML from Reset ($00:FF03), which also carries the soft resets, and JML $FDC000 at $C0:418D (Evt_OpFF96_Reset, after JSR InitHW and S=$06FF).

Generated from functions.csv. Do not edit.