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

Last updated: October 9, 2026

Text windows

The text decoder in bank $C2 ($C2:57DF–$C2:6262) turns a string of bytes into glyphs drawn pixel by pixel into a graphics buffer, a few characters per call. It is reached from other banks through two fixed vectors, so the field and the scene mode share it. All of it is matched, its tables included.

See also: Scene engine, Field event scripts.

Calling it

Both entries work on a parameter block at $0200 (they set DP=$0200 themselves), which the caller fills first: the string number and string table, the output buffer, the mode, and before each step the number of characters to draw.

  • TextWin_Init ($C2:57DF, through BankC2_Entry0003) looks the string up in the table, sets state 0 and puts the pen at the left margin (or at 0 in some modes).
  • TextWin_Step ($C2:5823, through BankC2_Entry0009) draws one step: TextWin_CheckStatus runs the handler of the current status (TextWin_StatusTable, statuses $00–$20), which says whether to go on; if so, the handler of the current state runs.

Two callers use it. In the scene mode, the label task (C2Scene_LabelWait, C2Scene_LabelDraw) draws one character per frame. On the field, the message window step Field_Unk1F87 starts it with Field_MsgStart ($C0:20F2: the string from the treasure string table at $DE:FF00 for a treasure, else from the pointer Evt_OpB8_SetMsgPtr set; buffer $7E:F000, mode 0) and draws one glyph a step with Field_MsgStep ($C0:2126), acting on the status each step leaves: a new line, a new page, a timed wait, a wait ("for a press, probably") or the end. See Field event scripts.

The string format

A string is a run of bytes at TextWin_TextPtr:

Bytes Meaning
$A0–$FF A glyph, drawn by TextWin_DrawGlyph
$21–$9F A dictionary entry (TextWin_DictPtrs, bank $DE): a sub-string of glyphs, drawn in state 1
$00–$20 A control code (TextWin_CodeTable, $C2:5903, 33 words)

The hex digit table TextWin_HexGlyphs gives "0"–"9" as $D4–$DD and "A"–"F" as $A0–$A5, so the banner reads $A0–$B9 as probably "A"–"Z" and $BA–$D3 as "a"–"z" (the names in ROM read as words that way).

The control codes matched include:

  • $01, $02 (TextWin_CodeWide): the next byte, with the code as its high byte, is a 16-bit glyph;
  • $00 and $04–$0C (TextWin_CodeSetStatus): set TextWin_Status to the code, which ends the step (0 is idle, "probably the end"; 5–12 reset the pen); $03 does the same after storing an argument byte;
  • $0D, $0E, $0F (TextWin_CodeNum8/16/24): a number in decimal (3, 5 or 8 digits, leading zeros trimmed) or in hex, drawn in states 2 and 3;
  • $11, $13–$1E, $20: names: the characters' names kept in bank $7E ($13–$1A, $20, and $11 with an argument), the party members by position ($1B–$1D, TextWin_CodeMemberName), and a fixed "Nadia" string ($1E, TextWin_CodeNadia);
  • $12 $00 / $12 $01 (TextWin_ExtTechName, TextWin_ExtEnemyName): a tech or enemy name from the ROM name tables;
  • $1F (TextWin_CodeItemName): the name of the item in Treasure_ItemId.

The states

State Handler Draws
0 TextWin_State0 ($C2:58B2) Glyphs from the string; starts dictionary entries and control codes
1 TextWin_State1 ($C2:5BF5) A sub-string: a dictionary entry or a name
2 TextWin_State2 ($C2:5C3E) Number digits
3 TextWin_State3 ($C2:5C77) Number digits

Each glyph drawn uses up one of TextWin_StepCount; when it runs out the step ends with TextWin_StatusStepDone, and the next step goes on in the same state. Each of states 1–3 leaves through a small exit table chosen by what is left of the sub-string and of the step.

Drawing a glyph

TextWin_DrawGlyph ($C2:5DC4) reads the glyph's 24 bytes from the font in bank $FF, shifts them to the pen's pixel position within its 8-pixel tile column, and ORs them into the output buffer: TextWin_Blit2bpp or TextWin_Blit4bpp by the mode, each with a table of eight shift entry points. The pen then moves on by the glyph's width from TextWin_GlyphWidths (96 entries, one per glyph $A0–$FF), so the font is proportional.

Numbers go through TextWin_Dec8, TextWin_Dec16 and TextWin_Dec24 ($C2:615E–$C2:6230), which divide by powers of ten with shift-and-subtract loops (TextWin_DivBits, TextWin_Div24Bits), and TextWin_HexByte for hex.

Quirks kept

TextWin_DrawGlyph indexes the width table with the glyph's low byte only, so a 16-bit glyph from codes $01/$02 with a low byte under $A0 reads past the table. TextWin_StateTable's verified header still calls its handlers unmatched; NEXT.md lists it as a stale header to fix at its next edit.