Menu (bank $C2)
The second half of bank $C2, from $C2:8000 on, is the menu. The field
(Field_PauseAndMenuInput, Field_SceneChangeTick, Field_RunBankC2Mode5) and the scene mode's
mode 5 call it with JSL BankC2_Entry8000 and an argument 0–6. Most of the menu's own screens are
not matched yet, so this page covers the frame around them: the entry, the thread scheduler, the
interrupt handlers and the helpers the screens share.
The menu runs with DP=$0000 and DB=$7E unless stated (names in include/ram_menu.inc and
include/constants_menu.inc, prefix Menu).
See also: Scene engine, Frame cycle and NMI.
Entry points
| Address | Vector | Goes to |
|---|---|---|
$C2:8000 |
BankC2_Entry8000 |
BankC2_MenuEntry, A = the argument (0–6) |
$C2:8002 |
BankC2_Entry8002 |
BankC2_ReadPadLong → Menu_ReadPad, the joypad reader |
$C2:8004 |
BankC2_Entry8004 |
BankC2_CommandLong → Menu_Unk8C36, A = a command (not matched) |
Entering and leaving
BankC2_MenuEntry ($C2:800E) turns interrupts off, saves DP, DB and P, keeps A and X
(Menu_EntryArg, Menu_EntryX), forces blank and turns NMI, DMA and HDMA off.
- Argument 0 on a new game (
Menu_DataInitDonestill 0): it fills the new game's data (Menu_InitNewGameData), clears two SRAM bytes and leaves throughMenu_ExitRestarttoGameLoop_Main. No menu is shown. - Otherwise: it moves the stack to
Menu_StackTop, runsMenu_InitSystems, starts the play-time clock, picks the mode fromMenu_EntryModes(arguments 0–6 give modes$0E,$00,$0C,$0D,$0F,$07,$0B), turns the NMI and interrupts on and hands over toMenu_RunThreads, which never returns.
Menu_InitSystems ($C2:8096) copies Menu_InterruptVectors ($C2:8406: JML Menu_Nmi,
JML Menu_Irq) over the interrupt trampolines at $0500, sends five sound-driver commands
("meanings not traced"), sets up the PPU and clears RAM (Menu_InitPpuAndRam), builds the party
lists (Menu_BuildPartyLists) and runs six more set-up routines that are not matched yet.
The menu leaves in one of two ways. Menu_Exit ($C2:82B2) frees every thread, fades out,
restores the stack, P, DB and DP and returns with RTL to the caller of BankC2_Entry8000.
Menu_ExitToGame ($C2:8320) fades out and jumps through ReentryVectors entry 0 to
GameLoop_Main (the warm restart).
The thread scheduler
The menu's screens run as up to eight cooperative threads. Menu_RunThreads ($C2:8190)
keeps them in eight 8-byte slots (Menu_Thread, at $00B0), each with a 64-byte stack from
$0A00 on. It starts Menu_MainThread in slot 1 and Menu_UnkE91B (not matched) in slot 3,
then once a frame:
- waits until
Menu_FrameReadyis non-zero (the menu NMI has done its upload); - for each slot in use, counts its
.Waitdown and runs the thread when it reaches 0: a new thread starts at its entry on a fresh stack, a waiting one gets its stack, X, Y, P and DP back and returns from its yield; - after the last slot, clears
Menu_FrameReady.
A thread gives the CPU back by jumping to one of three sub-entries:
| Sub-entry | What it does |
|---|---|
Menu_Yield ($C2:821E, JSR) |
Wait one frame |
Menu_YieldFrames ($C2:822B, JSR) |
Wait A frames (0 means 256) |
Menu_ThreadEnd ($C2:8216, JMP) |
Free the slot |
Menu_StartThread ($C2:8249) puts a new thread in a slot (it starts on the next pass) and
Menu_KillThreads ($C2:8255) frees the slots whose bits are set in A. Each thread also keeps
one word, Menu_ThreadVar, across its yields. The header notes that DB is set only at the start
of each frame, so a thread starts or resumes with the DB the thread before it left.
The main thread. Menu_MainThread ($C2:826C, slot 1) calls one handler a frame: entry
Menu_ThreadVar of the current mode's list in Menu_ModeLists ($C2:8292, 16 modes). A handler
moves on by changing Menu_ThreadVar or Menu_Mode. The mode lists themselves are not matched
yet; modes 8–10 point at a bare RTS and are "probably not used".
Interrupts and the frame
Menu_Nmi ($C2:840E) runs on its own stack with DP = Menu_NmiDp ($0D00). It counts frames,
forces blank and, when the threads have a frame ready, turns HDMA off, runs the upload
(Menu_Unk85D6, not matched) and sets Menu_FrameReady to $FF, which the scheduler waits for.
Then it reads the pad and ticks the play-time clock (Menu_NmiPad, with the menu's auto-repeat:
$20 frames before the first repeat, then 5), and sets the BG mode and the screen brightness.
Menu_Irq ($C2:8472) is a bare RTI.
Menu_ReadPad ($C2:84D2) reads joypad 1 (Menu_PollPad), maps it through the configurable
button map (Menu_MapButtons) and advances the play-time clock Menu_TickPlayTime ($C2:85AE:
frames, seconds, two-digit minutes and hours, with a flag once it passes 99:59:59). The scene NMI
and code in other banks call it too, the battle's play-time hit modifier among them.
Shared helpers
- Set-up.
Menu_InitPpuAndRam($C2:940D) puts the PPU into the menu's starting state and clears VRAM and most of bank$7Ewith its DMA helpers;Menu_InitNewGameDatafills the data of a new game (inferred from what it fills) andMenu_ClearConfigAndFlagsclears the flag block, the play time and the settings, then loads the default settings and button map ($C2:956E–$C2:960A). - Fades.
Menu_Fade($C2:83D0) steps the brightness by A each frame, yielding between steps, until it reaches 0 or 15;Menu_FadeOutuses a step of −2, andMenu_StartFadeInstartsMenu_FadeInThread(+2 a frame) in slot 0. - VRAM queue.
Menu_QueueVramRom/Menu_QueueVramWram($C2:8385) append an 8-byte upload record from bank$C2or$7EtoMenu_VramQueue;Menu_FlushVramQueue($C2:8663) sends them, newest first, with DMA channel 7 and empties the queue. - Palette and gradient.
Menu_UploadPalette($C2:86A9) sends the 512 bytes ofMenu_PalBufto CGRAM whenMenu_PalDirtyis set.Menu_BuildGradient($C2:86DD) builds an HDMA table of colour-math lines for channel 0 in the buffer not built last, fromMenu_GradSpecand the steps inMenu_GradientSteps. - Inventory.
Menu_AddItem,Menu_RemoveItemandMenu_FindItem($C2:8791–$C2:8815) add to and take from the 256 item slots, a count capped at 99. - Characters and party.
Menu_BuildPartyListslists the present characters and counts the active members (positions 0–2) and all of them;Menu_LoadCharRecA($C2:8839) copies a character's$50-byte record toMenu_CurCharRec, andMenu_LoadPartyCharRecdoes it for the current member. - Items and equipment.
Menu_ItemCategory($C2:8881) sorts an item id into one of five categories by id range, andMenu_ItemDataPtrA/Bgive the address of its record in bank$CC("what the records hold is not traced").Menu_CountEquippedBycounts the characters that have an item equipped, andMenu_ListEquipItems($C2:8953) lists the inventory items of one id range thatMenu_Unk93A8accepts (not matched; "probably 'the current character cannot equip it'" when it refuses). - Cursors and list arrows (
$C2:89E4–$C2:8C35). Threads in slot 6 draw the blinking pointing-hand cursor sprites at the positions ofMenu_CursorPosrecords, in four variants (Menu_StartCursorA–D; D shows one cursor per active party member).Menu_ListArrowsThread(slot 4) blinks the up and down arrows of a scrolling list: the up arrow when the list's top is not 0, the down arrow when it is belowMenu_ListRows.
Quirks the headers keep include Menu_CursorStepD calling Menu_CursorPosAB with a 16-bit A, so
its cursors sit one pixel lower than the others, and a missing CLC in Menu_ListArrowsThread
that can put the down arrow one pixel lower.
What is not matched yet
From NEXT.md, in reach order: the command dispatcher Menu_Unk8C36 (behind
BankC2_Entry8004, which calls the inventory routines), the set-up routines Menu_InitSystems
calls, thread 3 (Menu_UnkE91B) and the mode handler lists. The menu NMI's upload
Menu_Unk85D6 is also still open. After $C2:8C36 only the set-up at $C2:940D–$C2:960A
and Menu_CursorRecOffset ($C2:EA27) are matched.