{ "chunk": "game_main_0800_5E14", "unit": "game/main_0800", "routines": [ { "addr": "5E17", "name": "handleOwnUnitClick", "summary": "Called from the battlefield main loop ($20CC) when the player clicks on one of his own, unstunned units (index in D_90F8). A unit that belongs to a group (FB54 bit 6) is routed to runGroupOptionsMenu ($61CF); otherwise it highlights the unit (D_9204), sets uiLevel 1, the square cursor, clears the info panel in colour 5 and shows the info panel (sub_22A2, which waits until the player moves the stick or clicks again). A deflected stick then starts SETTING DESTINATION (sub_6500); a second click (checkFirePressed) opens the unit options menu (L_5E99) only when gameTypeOptions bit 3 is set (game types 3-6). Dead units exit at once.", "inputs": "D_90F8 unit, D_FB54/D_F76C/D_F898/D_F8FC/D_F960 unit arrays, zp_5D joystick, D_0BA3 bit 3, zp_B1, D_9236", "outputs": "D_9204/D_9205/D_9209/D_612E/D_91D5 set, viewport redrawn, info panel used, commands queued via sub_561F, falls into exitUnitMenu (D_9209 = 0, D_91D7 = $FF)", "confidence": "high" }, { "addr": "5E24", "name": "runUnitOptionsMenu", "summary": "Single-unit branch of handleOwnUnitClick, also entered by JMP from the group menu item DIRECT UNIT ($62F6). After the info panel wait and the second click it builds the UNIT OPTIONS menu at rows 15+: CLOAKING ON/NO CLOAKING (F898 bit 6), DIG OUT/DIG IN when the unit is stationary (FB54 bit 7; DIG IN only if no dig is in progress, F960 bits 5-7) else SET BLITZ/BLITZ OFF (F8FC bit 6), JOIN GROUP unless the unit is the comcen, CLEAR TARGET/SET TARGET for a boomer during battle (sub_3C74), and (EXIT). Runs runMenuLoopWatchUnit and acts on the returned item id: 2/3 toggle F898 bit 6 and send $82/$84; 4 sends $86; 5 prints SETTING/TARGET and runs the target cursor (zp_BB = 5, sub_2185); 7 runs runJoinGroupMenu; 10/11 toggle F8FC bit 6 and send $93/$95; 17/18 send $A0/$9F. Y = $80 from the loop (idle flag changed) rebuilds the menu.", "inputs": "D_90F8, unit arrays, D_0BA3 bit 3, zp_5D, D_9236, zp_B1", "outputs": "commands queued (zp_73 = unit), D_F898/D_F8FC bit 6 toggled locally, D_612E = $FF, D_9209 = 1, menu state D_91CE/D_91D4/D_91D5/D_91D6, L_602A+1 patched with FB54 | 1", "confidence": "high" }, { "addr": "5FA6", "name": "exitUnitMenu", "summary": "Shared tail of all unit/group menu paths: uiLevel D_9209 = 0 and info-panel unit D_91D7 = $FF, then returns to the battlefield loop.", "inputs": "-", "outputs": "D_9209 = 0, D_91D7 = $FF", "confidence": "high" }, { "addr": "5FB1", "name": "addMenuItem", "summary": "Appends item id X to the menu list D_91CE[D_91D4++] and prints the item name on the current text line. Ids 0-11 use the 16-byte strings at $C9D2 (sub_C100 with X = id*2), ids 12-18 the 8-byte strings at $CAB2 (X = id - 12); each string ends with a CR so the cursor moves to the next row.", "inputs": "X = item id (0 FORMATION, 1 MOVE TO GOAL, 2 CLOAKING ON, 3 NO CLOAKING, 4 CLEAR TARGET, 5 SET TARGET, 6 MEMBERSHIP, 7 JOIN GROUP, 8 DIRECT UNIT, 9 SELF DESTRCT, 10 SET BLITZ, 11 BLITZ OFF, 12 (EXIT), 13 ENERGY, 14 BATTLE, 15 RADAR, 16 DRONE, 17 DIG OUT, 18 DIG IN), D_91D4, text cursor", "outputs": "D_91CE[n] = id, D_91D4 incremented, text printed; A/X/Y clobbered", "confidence": "high" }, { "addr": "5FD1", "name": "runMenuLoopWithFlag", "summary": "Entry used by the repair/misc menus and the overlays ($1C59, $7F40, $835D, $83CE): with C = 1 it behaves like runMenuLoop; with C = 0 the slow-down row operand at L_60BA+1 is set to $FF so there is no extra delay before the last item.", "inputs": "C flag, menu state D_91D4/D_91D5/D_91D6", "outputs": "see runMenuLoopWatchUnit", "confidence": "medium" }, { "addr": "5FD7", "name": "runMenuLoop", "summary": "Menu selection loop without the unit idle-flag watch (patches the operand at L_602A+1 to 0) and with the slow-down row = count - 2; falls into runMenuLoopWatchUnit. Used by the join-group, repair and formation menus.", "inputs": "menu state D_91D4/D_91D5/D_91D6, D_90F8, D_612E", "outputs": "A = D_91CE[D_91D5] item id, Y = D_91D5 selection", "confidence": "high" }, { "addr": "5FDC", "name": "runMenuLoopWatchUnit", "summary": "Generic vertical menu selector used by every in-game option menu. Sets a 20-frame input lockout (D_0B7D, zp_6C), highlights the current row and loops: steps the RNG, runs the per-tick game update (sub_40B2 in battle, sub_8658/sub_EEB9/sub_4DA9 in setup or film phases), keeps the info-panel unit, exits with the last item selected if the unit died, the game ended or sub_C451 reports an abort, returns A = Y = $80 if the unit's idle flag (FB54 bit 7) differs from the snapshot in L_602A+1, updates the map highlight according to D_612E (negative/odd: animate the path marker; else D_9206 = selection*4 | D_612E is the candidate group key and the viewport is redrawn when it changes), blinks the selected row every 20 frames (zp_6E), and handles the joystick: up/down move the selection (no wrap, sound 2, auto-repeat via sub_0D49, 30-frame pause before entering the last row), fire (checkFirePressed) confirms. On exit the selected row is left highlighted.", "inputs": "D_91D4 count, D_91D5 selection, D_91D6 first row, D_612E mode, D_90F8, zp_5D, zp_6C, zp_6E, zp_B1, D_92C9, D_0BA8, L_602A+1 idle snapshot, L_60BA+1 slow row", "outputs": "A = D_91CE[D_91D5] item id, Y = D_91D5 (or A = Y = $80 when the idle flag changed), D_91D5 updated, D_9206/D_9205 updated in group-pick mode, D_92C5, D_0B7D, zp_6C, L_60BE+1 patched with the move direction", "confidence": "high" }, { "addr": "60E5", "name": "unhighlightMenuRow", "summary": "Removes the highlight from the selected menu row if it is currently highlighted (D_92C5 == 0); otherwise returns. Falls into toggleMenuRowHighlight.", "inputs": "D_92C5, D_91D5, D_91D6, zp_3C, zp_3D", "outputs": "row colours restored, D_92C5 = 1", "confidence": "high" }, { "addr": "60EA", "name": "toggleMenuRowHighlight", "summary": "Toggles the highlight of menu row D_91D6 + D_91D5 by EORing the screen-RAM colour byte of each of the zp_3C cells with the text colour zp_3D, flips D_92C5 and restarts the 20-frame blink timer zp_6E. Special case for the REPAIR menu (D_90F8 == $FF, selection < 4): if the comcen is stunned (D_922A bit 7) it discards its return address and jumps to L_607E so the menu loop exits with the last item; otherwise it blinks the comcen system marker (sub_188B).", "inputs": "D_91D5, D_91D6, zp_3C, zp_3D, D_90F8, D_922A, D_92C5", "outputs": "zp_6E = 20, colour bytes toggled, D_92C5 ^= 1, zp_B6/zp_B7 and text cursor changed", "confidence": "high" }, { "addr": "612F", "name": "runJoinGroupMenu", "summary": "JOIN GROUP: lists the seven groups ALPHA..GROVER (8-byte names at $C92C) with their current member counts (countGroupMembers) and (EXIT) from row 13, preselecting the unit's current group number (FB54 bits 2-4). While the selection moves the menu loop highlights the candidate group on the viewport (D_612E = side bit). Choosing a group calls sub_4AA3, which sends command $88 and returns C = 0 when the unit joined; if the unit is too far from the group (C = 1, TOO FAR! shown) the list is redrawn and the choice repeated.", "inputs": "D_90F8, D_FB54, D_F76C", "outputs": "D_9209 = 2, D_91D4 = 8, D_91D5/D_91D6, D_612E = FB54 & $20, D_9207 = $FF (dead), D_9206 chosen group key, command $88 queued", "confidence": "high" }, { "addr": "61CF", "name": "runGroupOptionsMenu", "summary": "Group branch of handleOwnUnitClick for a unit that is a group member: D_9206/D_9205 = group key (FB54 & $3C), uiLevel 2, members highlighted in the viewport, header GROUP + group name. Loops with the path-marker animation until the stick moves (after waiting for pending repositions, sub_65E7, the whole group gets SETTING DESTINATION via sub_6500) or a click opens the GROUP OPTIONS menu (gameTypeOptions bit 3 required): CLOAKING ON/NO CLOAKING, DIG OUT/DIG IN or SET BLITZ/BLITZ OFF, MEMBERSHIP, FORMATION, DIRECT UNIT, (EXIT). Actions: 2/3 toggle F898 bit 6 and send $83/$85 with the group key; 6 runs runGroupEditor in membership mode (zp_BB = 0); 0 runs the formation sub-menu sub_65FF and, for CUSTOM (Y = 3), runGroupEditor with zp_BB = $80; 8 jumps to runUnitOptionsMenu; 10/11 toggle F8FC bit 6 and send $94/$96; 17/18 send $A2/$A1. Ends with clearGroupHighlight and exitUnitMenu.", "inputs": "D_90F8, D_FB54, D_F898, D_F8FC, D_F960, zp_5D, D_0BA3 bit 3", "outputs": "D_9206, D_9205, D_9204 = $FF, D_9209 = 2, D_612E = 1, commands queued with zp_73 = group key, menu state", "confidence": "high" }, { "addr": "633C", "name": "printGroupTitleAndName", "summary": "Prints GROUP (printGroupTitle) and, on the next row at column 4, the name of group (D_9206 >> 2) & 7 from the table at $C92C, followed by a newline. Used as the header of the group menu.", "inputs": "D_9206", "outputs": "text printed, zp_3F = 4", "confidence": "high" }, { "addr": "6358", "name": "printGroupTitle", "summary": "Sets the text column to 4 and prints the string GROUP ($CB1F, ends with CR).", "inputs": "text window", "outputs": "text printed, zp_3F = 4", "confidence": "high" }, { "addr": "6366", "name": "clearGroupHighlight", "summary": "Clears the viewport group highlight (D_9205 = $FF) and redraws the viewport (sub_26C1).", "inputs": "-", "outputs": "D_9205 = $FF, viewport redrawn", "confidence": "high" }, { "addr": "636E", "name": "printSettingHeader", "summary": "Clears the info panel in colour 4, positions at row 15 and prints SETTING centred followed by MEMBERSHIP ($CA32) when zp_BB >= 0 or FORMATION ($C9D2) when zp_BB is negative. Header of the group editor.", "inputs": "zp_BB", "outputs": "zp_3D = zp_42 = 4, text printed", "confidence": "high" }, { "addr": "6387", "name": "printFormationCentred", "summary": "Prints the word FORMATION ($C9D2) centred in the text window (sub_C121). Also called by the formation sub-menu sub_65FF.", "inputs": "text window", "outputs": "text printed", "confidence": "high" }, { "addr": "638E", "name": "runGroupEditor", "summary": "Interactive editor for the group in D_9206, in MEMBERSHIP mode (zp_BB >= 0) or custom FORMATION mode (zp_BB < 0). Halts the group (sendHaltGroupCmd $9B; formation mode first waits for pending repositions), then lets the player scroll the viewport (joystick auto-repeat, sub_20F4) with hover info on own units after 30 idle frames. A click on an own unit: not a member -> membership mode adds it (sub_4AA3: ADDED or TOO FAR!), formation mode refuses with sound 3 and NOT IN GROUP; a member -> membership mode removes it (sub_4AF2, REMOVED), formation mode asks for a NEW POSITION (sub_4BD3, command $98); the comcen only gives sound 3. A click on an empty cell or enemy prints FINISHED and ends the editor. sub_C451 aborts at any time. Ends with clearGroupHighlight and exitUnitMenu.", "inputs": "D_9206 group key, zp_BB mode, zp_5D, zp_6C, D_90EE, D_9236, D_FB54, D_63E2 own-side branch", "outputs": "D_90F8 = unit under the cursor (bit 7 set = none/enemy), D_9209 = 2, D_91D7 = $FF, D_9207 = $FF (dead), D_9205, D_91C9/D_91CA, D_0B7D, commands $9B/$88/$89/$98 queued, viewport scrolled/redrawn", "confidence": "high" }, { "addr": "64DA", "name": "sendHaltGroupCmd", "summary": "Queues command $9B with the group key D_9206 as parameter; the handler ($549E) stops every member of the group (destination = position, idle bit set, cloak/blitz orders cleared).", "inputs": "D_9206", "outputs": "zp_73 = D_9206, two bytes queued via sub_561F", "confidence": "high" }, { "addr": "64E4", "name": "printSettingHeaderForLevel", "summary": "Clears the info panel using the uiLevel colour from uiLevelTextColourTable[D_9209], moves to row 15 and prints SETTING centred plus a newline (falls into printSettingWord). The caller prints the object (TARGET, DESTINATION, NEW POSITION) next.", "inputs": "D_9209", "outputs": "zp_3D = zp_42 = colour, text printed", "confidence": "high" }, { "addr": "64F6", "name": "printSettingWord", "summary": "Prints SETTING ($CB4F) centred in the text window followed by a newline.", "inputs": "text window", "outputs": "text printed", "confidence": "high" }, { "addr": "6500", "name": "setUnitDestination", "summary": "SETTING / DESTINATION: prints the header (the string following SETTING, DESTINATION at $CB56, centred via sub_C12B), sets zp_BB = 0 and runs the target cursor loop sub_2185, which issues the move order for the selected unit or, with uiLevel 2, the whole group. Entered when the player moves the stick after selecting a unit or group.", "inputs": "D_90F8, D_9209, D_9206", "outputs": "zp_BB = 0, move order queued by sub_2185", "confidence": "high" }, { "addr": "650E", "name": "countGroupMembers", "summary": "Counts the live units (F76C bit 7 clear) whose FB54 & $7C equals the group key in A with the membership bit added (A | $40). Used to show the member count next to each group name in the join-group list.", "inputs": "A = group key (group number * 4 | side bit $20)", "outputs": "A = zp_18 = count, zp_19 = key | $40, Y = $FF", "confidence": "high" } ], "variables": [ { "addr": "18", "scope": "zp", "name": "tempByte18", "meaning": "scratch; member counter in countGroupMembers", "confidence": "high" }, { "addr": "19", "scope": "zp", "name": "tempByte19", "meaning": "scratch; group key being counted in countGroupMembers", "confidence": "high" }, { "addr": "22", "scope": "zp", "name": "groupListIndex", "meaning": "loop index 0-6 over the group names while runJoinGroupMenu prints the list (reuse of the distance scratch byte)", "confidence": "high" }, { "addr": "3C", "scope": "zp", "name": "textWindowWidth", "meaning": "text window width; number of colour bytes toggled per menu row and the centring width passed through D_90EF", "confidence": "high" }, { "addr": "3D", "scope": "zp", "name": "textColour", "meaning": "text colour byte; also the EOR mask used to highlight a menu row (5 for the unit menu, 4 for group menus, uiLevelTextColourTable otherwise)", "confidence": "high" }, { "addr": "3E", "scope": "zp", "name": "textFillChar", "meaning": "text engine fill character, zeroed before each menu is drawn", "confidence": "high" }, { "addr": "3F", "scope": "zp", "name": "textColumn", "meaning": "text cursor column (set to 0 for the row highlight, 4 for the GROUP header)", "confidence": "high" }, { "addr": "40", "scope": "zp", "name": "textRow", "meaning": "text cursor row (15 = first menu row)", "confidence": "high" }, { "addr": "42", "scope": "zp", "name": "textPadColour", "meaning": "colour of the centring padding; kept equal to zp_3D by the menus", "confidence": "high" }, { "addr": "43", "scope": "zp", "name": "textRawMode", "meaning": "text engine raw-code flag, cleared before each menu", "confidence": "high" }, { "addr": "5D", "scope": "zp", "name": "joystickDirection", "meaning": "joystick nibble, active low ($0F centred); the menu loop uses only up (bit 0) and down (bit 1) by ORing with $0C", "confidence": "high" }, { "addr": "6C", "scope": "zp", "name": "joystickRepeatTimer", "meaning": "frame countdown gating joystick auto-repeat; 20 at menu start, 30 before stepping onto the last menu row", "confidence": "high" }, { "addr": "6E", "scope": "zp", "name": "menuBlinkTimer", "meaning": "20-frame countdown between highlight toggles of the selected menu row", "confidence": "high" }, { "addr": "73", "scope": "zp", "name": "cmdParam0", "meaning": "first parameter of an outgoing command: unit index for the unit menu, group key (FB54 & $3C) for the group menus", "confidence": "high" }, { "addr": "B1", "scope": "zp", "name": "gamePhase", "meaning": "0 battle (sub_40B2 ticks, unit checks), $FF setup phase (sub_8658 tick), other bits 6/7 = film/menu phases (sub_4DA9 tick)", "confidence": "medium" }, { "addr": "B6", "scope": "zp", "name": "screenPtr", "meaning": "screen RAM pointer of the current text cell (set by sub_C097); the row highlight EORs (zp_B6),y", "confidence": "high" }, { "addr": "BB", "scope": "zp", "name": "cursorTargetMode", "meaning": "0 destination pick, 5 = boomer SET TARGET (range-limited crosshair), $80 = free cell pick / custom formation mode of runGroupEditor", "confidence": "high" }, { "addr": "0B7D", "scope": "abs", "name": "inputLockoutTimer", "meaning": "frames during which the IRQ keeps the old joystick state; 20 at menu start, cleared before a scroll step", "confidence": "high" }, { "addr": "0BA3", "scope": "abs", "name": "gameTypeOptions", "meaning": "bit 3 = unit/group option menus available (set in the per-game-type table at $78CA for Face-Off, Sluggers, Full War and Defender; clear for Scrimmage, QB Sneak, The Bomb)", "confidence": "high" }, { "addr": "0BA5", "scope": "abs", "name": "commBuildFlag", "meaning": "bit 7 = comm build 1 resident; the menu loop then calls sub_EEB9 each setup-phase iteration", "confidence": "medium" }, { "addr": "0BA8", "scope": "abs", "name": "gameEndReason", "meaning": "nonzero = no game running / game over; the menu loop then skips the sub_C451 abort check", "confidence": "medium" }, { "addr": "1FA6", "scope": "abs", "name": "joystickDirectionTable", "meaning": "16-entry joystick nibble -> direction code table ($FF = none) used by the group editor scroll handling", "confidence": "high" }, { "addr": "5E14", "scope": "abs", "name": "uiLevelTextColourTable", "meaning": "3 colour bytes ($09,$05,$04) for the info panel text indexed by uiLevel D_9209: 0 browsing, 1 unit selected, 2 group", "confidence": "high" }, { "addr": "602B", "scope": "abs", "name": "menuIdleFlagSnapshot", "meaning": "self-modified operand of LDA # at L_602A: FB54 | 1 of the menu's unit when the menu was built (bit 7 = idle); 0 disables the watch. A change of bit 7 makes the menu loop return $80 so the DIG/BLITZ items are rebuilt", "confidence": "high" }, { "addr": "60BB", "scope": "abs", "name": "menuSlowRowIndex", "meaning": "self-modified operand of CMP # at L_60BA: item index (count - 2) before which a 30-frame delay is inserted when moving down; $FF = none", "confidence": "high" }, { "addr": "60BF", "scope": "abs", "name": "menuMoveDirection", "meaning": "self-modified operand of LDY # at L_60BE: last joystick move direction ($FF up, $01 down)", "confidence": "high" }, { "addr": "612E", "scope": "abs", "name": "menuLoopMode", "meaning": "mode of the map highlight update inside the menu loop: $FF (unit menu) or $01 (group menu) = animate the selected unit's path marker; $00/$20 (join-group picker) = side bit ORed into the candidate group key (selection * 4) that is highlighted in the viewport", "confidence": "high" }, { "addr": "63E2", "scope": "abs", "name": "ownSideBranchOpcode63E2", "meaning": "branch opcode after CMP #$32 in the group editor, patched by overlay A ($7BCF) to BCC/BCS so that own units are index < 50 or >= 50 depending on the player side", "confidence": "high" }, { "addr": "90EE", "scope": "abs", "name": "idleFrameCounter", "meaning": "frames since the last joystick activity; the group editor shows the hover info panel after 30", "confidence": "high" }, { "addr": "90EF", "scope": "abs", "name": "textCentreWidth", "meaning": "nonzero = next string is centred in this many columns; cleared before the menus", "confidence": "high" }, { "addr": "90F8", "scope": "abs", "name": "selectedUnit", "meaning": "unit the menus act on; runGroupEditor rewrites it with the unit under the cursor (bit 7 set = none or enemy); $FF in the repair menu", "confidence": "high" }, { "addr": "91C9", "scope": "abs", "name": "joyCenteredMask", "meaning": "$7F while the stick is held, 0 after it returned to centre; masks joyHoldCounter for the auto-repeat delay", "confidence": "high" }, { "addr": "91CA", "scope": "abs", "name": "joyHoldCounter", "meaning": "consecutive auto-repeat steps of a held direction (first step 16 frames, later 6)", "confidence": "high" }, { "addr": "91CE", "scope": "abs", "name": "menuItemIds", "meaning": "list of item ids of the current menu (up to 6 bytes, $91CE-$91D3), written by addMenuItem and read back by the menu loop to return the chosen item", "confidence": "high" }, { "addr": "91D4", "scope": "abs", "name": "menuItemCount", "meaning": "number of items in the current menu", "confidence": "high" }, { "addr": "91D5", "scope": "abs", "name": "menuSelectedItem", "meaning": "index of the highlighted / chosen menu row", "confidence": "high" }, { "addr": "91D6", "scope": "abs", "name": "menuFirstRow", "meaning": "screen row of menu item 0 (15 for the unit/group menus, 13 for the join-group list)", "confidence": "high" }, { "addr": "91D7", "scope": "abs", "name": "infoPanelUnit", "meaning": "unit whose info panel is shown ($FF none); the menu loop keeps it equal to D_90F8", "confidence": "high" }, { "addr": "9204", "scope": "abs", "name": "highlightedUnit", "meaning": "unit drawn highlighted in the viewport ($FF none); set to the selected unit by the unit menu", "confidence": "high" }, { "addr": "9205", "scope": "abs", "name": "highlightedGroupKey", "meaning": "group key (FB54 & $3C) whose members are drawn highlighted in the viewport; $FF none", "confidence": "high" }, { "addr": "9206", "scope": "abs", "name": "selectedGroupKey", "meaning": "group key of the group being commanded/edited: group number in bits 2-4 plus side bit 5 (FB54 & $3C); OR $40 gives the membership compare value", "confidence": "high" }, { "addr": "9207", "scope": "abs", "name": "unusedFlag9207", "meaning": "written with $FF by runJoinGroupMenu and runGroupEditor, never read anywhere (dead store)", "confidence": "high" }, { "addr": "9209", "scope": "abs", "name": "uiLevel", "meaning": "0 browsing, 1 single unit selected, 2 group selected; selects the info panel colour and makes the target cursor act on the whole group", "confidence": "high" }, { "addr": "922A", "scope": "abs", "name": "comcenStunStatus", "meaning": "bit 7 = comcen stunned; aborts the repair menu from inside toggleMenuRowHighlight", "confidence": "high" }, { "addr": "9236", "scope": "abs", "name": "comcenUnit", "meaning": "index of the player's comcen; it gets no JOIN GROUP/target items and cannot be added to or removed from a group", "confidence": "high" }, { "addr": "92C5", "scope": "abs", "name": "menuBlinkState", "meaning": "0 while the selected menu row is drawn highlighted (colours EORed), 1 while drawn normally; toggled every 20 frames", "confidence": "high" }, { "addr": "92C9", "scope": "abs", "name": "gameEndCountdown", "meaning": "nonzero = game-over sequence running; the menu loop exits with the last item", "confidence": "high" }, { "addr": "929D", "scope": "abs", "name": "pendingRepositionGroup", "meaning": "group key of the formation repositions ($98 commands) still being processed by the engine", "confidence": "medium" }, { "addr": "929E", "scope": "abs", "name": "pendingRepositionCount", "meaning": "number of outstanding $98 repositions of pendingRepositionGroup; sub_65E7 waits for 0 before the group is moved or edited", "confidence": "medium" }, { "addr": "F76C", "scope": "abs", "name": "unitTypeTable", "meaning": "per-unit type byte; bit 7 = dead (menu exits / unit skipped)", "confidence": "high" }, { "addr": "F898", "scope": "abs", "name": "unitWaypointCol", "meaning": "per-unit; bit 6 = cloaking ordered, toggled locally by the CLOAKING ON / NO CLOAKING items and sent as $82-$85", "confidence": "high" }, { "addr": "F8FC", "scope": "abs", "name": "unitWaypointRow", "meaning": "per-unit; bit 6 = blitz ordered, toggled locally by SET BLITZ / BLITZ OFF and sent as $93-$96", "confidence": "high" }, { "addr": "F960", "scope": "abs", "name": "unitStunTable", "meaning": "per-unit; bits 5-7 nonzero = digging in or dug in, then DIG OUT is offered instead of DIG IN", "confidence": "high" }, { "addr": "FB54", "scope": "abs", "name": "unitDisplayFlags", "meaning": "per-unit; bit 7 idle/stationary (DIG items vs BLITZ items, watched by the menu loop), bit 6 group membership active, bits 2-4 group number, bit 5 side", "confidence": "high" } ], "dataBlocks": [ { "addr": "5E14", "length": 3, "type": "byteTable", "name": "uiLevelTextColourTable", "description": "Info-panel text colour per uiLevel: $09 (browsing), $05 (unit selected), $04 (group). Read at $22AE (showUnitInfoPanel) and $64E7." }, { "addr": "612E", "length": 1, "type": "byteTable", "name": "menuLoopMode", "description": "Variable byte embedded in code: map-highlight mode of the menu loop ($FF unit menu, $01 group menu, $00/$20 join-group picker side bit)." } ], "misclassified": [ { "addr": "652E", "length": 114, "actual": "code", "evidence": "Directly after the chunk end: $652E-$659F decodes cleanly as the REPAIR 'WHICH:' menu (lda #0 / sta zp_43 / ... / jsr sub_C06A / jsr sub_C041 / lda #$D6 ldy #$CB jsr sub_C133 (string REPAIR/WHICH: at $CBD6) / loop of jsr sub_5FB1 for items 13-16 then 12 / lda #$FF sta D_90F8 / jsr sub_5FD7 / cpy #4 / jsr $0EE5 / jsr $193C / jsr sub_60E5 / jmp $657C / jmp sub_C06A) and is reached by a JSR $652E hidden in the data bytes at $1860. It is the only caller that makes the D_90F8 == $FF special case in toggleMenuRowHighlight meaningful." } ], "insights": [ "Unit command UI flow: the battlefield loop calls handleOwnUnitClick ($5E17) on a fire click over an own unstunned unit. showUnitInfoPanel (sub_22A2) then waits; moving the stick starts SETTING DESTINATION (sub_6500 -> runTargetCursorLoop with zp_BB = 0, uiLevel 1 = single unit, 2 = whole group), a second click opens the UNIT OPTIONS or GROUP OPTIONS menu. The option menus exist only when gameTypeOptions ($0BA3) bit 3 is set; the per-game-type option bytes at $78CA (overlay A) are 00,01,02,13,14,1D,1E, i.e. Face-Off, Sluggers, Full War and Defender have the menus, Scrimmage/QB Sneak/The Bomb do not.", "Menu engine ($5FB1-$612D) shared by all in-game menus (unit, group, join-group, formation sub-menu at $65FF, repair menu at $652E, misc menu via $1C59 and the overlay menus at $7F40/$835D/$83CE): addMenuItem stores item ids in D_91CE (max 6) and prints the names; D_91D4 count, D_91D5 selection, D_91D6 first screen row; the selected row is highlighted by EORing the screen-RAM colour bytes of the row with zp_3D and blinks every 20 frames (zp_6E, phase D_92C5). Joystick up/down move without wrapping (sound 2, auto-repeat via sub_0D49, extra 30-frame pause before the last row which is normally (EXIT)); fire confirms; death of the unit, game end (D_92C9) or a sub_C451 abort (SPACE, function key, game over) select the last item and leave. The loop returns A = item id, Y = index, or A = Y = $80 when the unit's idle bit (FB54 bit 7) changed so the caller rebuilds the menu (DIG IN/OUT vs BLITZ items). Three self-modified operands: $602B idle snapshot, $60BB slow row, $60BF direction.", "Menu item ids and strings in the $C000 overlay: 0 FORMATION, 1 MOVE TO GOAL, 2 CLOAKING ON, 3 NO CLOAKING, 4 CLEAR TARGET, 5 SET TARGET, 6 MEMBERSHIP, 7 JOIN GROUP, 8 DIRECT UNIT, 9 SELF DESTRCT, 10 SET BLITZ, 11 BLITZ OFF (16 bytes each at $C9D2 + id*16, printed via sub_C100 with X = id*2); 12 (EXIT), 13 ENERGY, 14 BATTLE, 15 RADAR, 16 DRONE, 17 DIG OUT, 18 DIG IN (8 bytes each at $CAB2 + (id-12)*8). Items 1 and 9 are never offered by this code. Group names ALPHA, BAKER, CHARLIE, DELTA, EASY, FOXTROT, GROVER are 8-byte strings at $C92C. Other strings used: $CB0A ' UNIT'/' OPTIONS', $CB13 'OPTIONS', $CB1F 'GROUP', $CB25 'WHICH GROUP:', $CB31 ' TOO FAR!', $CB3B 'ADDED', $CB40 'REMOVED', $CB47 'FINISHED', $CB4F 'SETTING', $CB56 'DESTINATION', $CB61 'TARGET', $CB8D 'NOT IN GROUP', $CB99 'RE-POSITIONED', $CBA6 'NEW POSITION', $CBD6 'REPAIR/WHICH:'.", "Outgoing command bytes queued by the menus (sub_561F: command byte + zp_73; sub_560F/sub_560A: + zp_74 + zp_75): $82/$83 cloak on unit/group, $84/$85 cloak off unit/group, $86 clear boomer target, $87 set boomer target (from the target cursor), $88 join group (unit, group key), $89 leave group (unit), $93/$94 blitz on unit/group, $95/$96 blitz off unit/group, $98 reposition unit in formation (unit, x, y), $9B halt group (group key), $9F/$A0 dig in/out unit, $A1/$A2 dig in/out group, $A3 formation sweep left/right/reverse (unit, option 0-2). Group commands carry the group key FB54 & $3C (group number bits 2-4 | side bit 5).", "Received-command dispatch (outside this chunk, hidden as data): sub_4D9F/$4EDB takes the command byte & $3F as an index into the word table at $4D2A (entries for $80-$A6) and JSRs through the patched operand at $4EEF. Handlers $4FC4-$55EE are also shown as .byte: $5023 cloak on ($F898 |= $C0, spies excluded), $504B cloak off, $5062 blitz off, $5070 blitz on, $5098/$50AA/$50BE/$50D2 group variants, $515D clear target, $516B set target -> $3C50, $5185 join -> $4AD4, $519B leave, $5470 reposition -> $4C59 (decrements D_929E), $549E halt group, $5548/$554C dig in/out unit ($F960 bits 5-7), $556D/$5571 group dig, $5596 formation -> $4C84.", "Group bookkeeping: D_9206 = selected group key, D_9205 = key highlighted in the viewport (drawViewport inverts the members when uiLevel is 2), membership test everywhere is (FB54 & $7C) == key | $40. sub_4AA3 adds the selected unit to group D_9206 only if it is less than 9 cells away from every edge of the members' bounding box (sub_4B04/sub_4B5E; an empty group always accepts), else TOO FAR! (sound 3, message at row 16). Formation repositions are tracked in D_929D/D_929E and sub_65E7 ($65E7, outside the chunk) waits for the engine to process them before the group is moved again.", "The self-modifying own-side test pattern (CMP #$32 followed by a branch opcode patched to BCC/BCS by overlay A) appears here at $63E2; countGroupMembers ($650E) instead derives the side bit from the unit index (>= 50 -> OR $20) without patching, because the side bit in FB54 is absolute.", "Line numbers in this assignment did not match the addresses: $5E14-$652D are file lines 10644-11584 of disassembly/game/main_0800.s; the survey follows the address range." ] }