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

22 September 2026: the tech and item lists, and the command menu is closed

The input handling for the tech list and the item list is matched, the last two submenus still holding stubs. Every input handler in the battle command menu is now matched code. 637 bytes in 15 routines, the last two increments of session 41.

The tech list: one cursor, two coordinate systems

BattleMenu_TechListInput ($C1:1320, 73 bytes) runs every frame the tech list is open. It works out which entry is highlighted (scroll position plus cursor row), then checks the pad for confirm, cancel or a direction. BattleMenu_TechConfirm ($C1:1369, 38 bytes) hands over to targeting, like BattleMenu_ChooseAttack does for Attack. BattleMenu_TechListCancel ($C1:138F, 30 bytes) restores the cursor and scroll saved before the list opened and goes back to the main menu.

Not every tech in the list is selectable. A character may not have learned it, or may not have the MP. Those entries carry an availability flag, and the cursor must skip them. But two things are being tracked: the tech's index in the underlying list, and where the cursor sits on screen (a row 0–2 plus a scroll offset). Press up when the previous entry is unavailable, and the cursor has to move to the next available entry, which may be two or three list slots away.

BattleMenu_TechListPrev ($C1:13AD, 71 bytes) and BattleMenu_TechListNext ($C1:13F4, 73 bytes) solve it by counting. Each walks the list index one step at a time, checking the flag, until it reaches a selectable entry, and counts the steps. Then it replays that count on the screen position (row and scroll), scrolling the window by a row whenever the cursor would leave the visible part. Two loops, same shape, same count, applied to two representations of "where you are". The list walk finds out how far you moved; the screen replay trusts the number without needing to know why. The availability flags are only checked once.

A tech list before and after pressing down, skipping two unavailable entries and scrolling the window

An example with placeholder entries: from entry 1, DOWN walks past two unavailable entries to entry 4, counting 3 steps, then replays the 3 steps on the window as one row and two scrolls.

The item list: almost the tech list, but simpler

BattleMenu_ItemListInput ($C1:143D, 91 bytes) has the same confirm/cancel/up/down checks plus two branches the tech list lacks: page the list up or down three rows at once, and a check of a flag ($A0D0) that forces a full re-render when something elsewhere has marked the display stale.

Cursor movement is where it differs most. Items don't need skipping: an unusable item still gets a cursor position and just renders greyed out. So BattleMenu_ItemCursorUp ($C1:14EC, 22 bytes) and BattleMenu_ItemCursorDown ($C1:1502, 26 bytes) move the row by one and only call a scroll helper at the top or bottom edge of the window. Same UI pattern, different implementation, because the two lists have different rules for where the cursor may stop.

Borrowing another routine's ending. BattleMenu_ItemListPageDown ($C1:151C, 27 bytes) and BattleMenu_ItemListPageUp ($C1:1537, 20 bytes) don't call the renderer themselves. They set the scroll position and JSR into the middle of BattleMenu_ItemListScrollDown ($C1:17BF, 30 bytes) / BattleMenu_ItemListScrollUp ($C1:17A1, 30 bytes), right at the instruction that calls the renderer, so that routine's tail (render, then flag the scroll arrows for a refresh) does double duty. Bank $C0's Sub_1BA7 does the same, entering Sub_1B90 partway to reuse its tail. On the matching side, that internal entry point had to be a real global label instead of a private .dot label: in asar a new global label resets the local scope, so a label jumped to from outside must be global.

A branch that can't happen. BattleMenu_ItemListScrollUp opens with two BEQs in a row on the same flag, with nothing in between that changes it. The first is a normal check. The second asks a question already answered: if the flag were set, the first branch would have fired. This is a different kind of "does nothing" from the dead block in BattleMenu_TargetSelectInput three days ago. That was code nothing can execute; this runs every time and always gets "no". Both are reproduced exactly.

BattleMenu_ItemConfirm ($C1:1498, 67 bytes), BattleMenu_ItemListCancel ($C1:14DB, 17 bytes) and BattleMenu_ItemListRefresh ($C1:154B, 22 bytes) complete the set.

Fixing an old label

Working out an item record for ItemConfirm (id, target mode, flags, quantity, in that order, five bytes apart) showed that BattleMenu_RenderItemRow, matched on 14 August, had two field comments backwards: it called the quantity field "item ID" and the id field an "unconfirmed-animation flag". That routine only reads the fields; ItemConfirm's branches reveal what they mean. The old comments are corrected. No bytes changed.

What this closes

Every submenu the command window can open, the main Attack/Tech/Item menu, the tech list, the item list and target selection, now has matched input handling and confirm logic, and the tech and item lists have matched navigation. Together with the confirm dispatch and command commit from 19 September, the whole path from "the menu needs a redraw" to "a battle command has been built and queued" is byte-exact end to end.

The day in numbers

Increment Routine Address Bytes
9 BattleMenu_TechListInput $C1:1320 73
9 BattleMenu_TechConfirm $C1:1369 38
9 BattleMenu_TechListCancel $C1:138F 30
9 BattleMenu_TechListPrev $C1:13AD 71
9 BattleMenu_TechListNext $C1:13F4 73
10 BattleMenu_ItemListInput $C1:143D 91
10 BattleMenu_ItemConfirm $C1:1498 67
10 BattleMenu_ItemListCancel $C1:14DB 17
10 BattleMenu_ItemCursorUp $C1:14EC 22
10 BattleMenu_ItemCursorDown $C1:1502 26
10 BattleMenu_ItemListPageDown $C1:151C 27
10 BattleMenu_ItemListPageUp $C1:1537 20
10 BattleMenu_ItemListRefresh $C1:154B 22
10 BattleMenu_ItemListScrollUp $C1:17A1 30
10 BattleMenu_ItemListScrollDown $C1:17BF 30
Total 637

Increment totals: 285 and 352 bytes. Bank $C1: 5,848 (9.15%, up from 8.71%) after the tech list, then 6,200 of 63,904 (9.70%, up from 6.39% at the start of session 41). Project-wide: 25,817 bytes (7.19% of all game code), then 26,169 (7.28%). make diff confirms 4,194,304 bytes identical.

Next

BattleMenu_UpdateCursorOverlay at $C1:17DD, the last single-routine stub in this neighborhood. After that, the target-mode handlers behind $C1:1FF8, and the much larger territory past $C1:1C4A: enemy logic, battle animation and most of the 481 routines scouted when the bank opened.

← Back to devlog