{
 "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."
 ]
}