{
 "chunk": "game_main_0800_163A",
 "unit": "game/main_0800",
 "routines": [
  {
   "addr": "1643",
   "name": "drawMenuTabBar",
   "summary": "Draws the header of the in-game STATS / REPAIR / MISC screen (screen id 3): full-screen text window, prints 'STATS' (colour $CD), 'REPAIR' ($C6) and 'MISC' ($C2) on row 3 from column 10, then colours the three tab boxes with setTabNormalColour for X=2..0 and tail-calls sub_2A56 (frame/shape draw). Called once from sub_153B when the menu screen is entered.",
   "inputs": "none",
   "outputs": "screen updated; zp_21 used as loop counter; zp_39-zp_40, zp_3D, zp_3F clobbered",
   "confidence": "high"
  },
  {
   "addr": "168C",
   "name": "setTabHighlightColour",
   "summary": "Colours tab X with its bright colour from tabHighlightColourTable (falls into setTabNormalColour if the entry is 0). Used by the tab-bar loop when a tab becomes selected and by blinkSelectedTab.",
   "inputs": "X = tab index 0..2",
   "outputs": "fills a 2x2 colour rectangle at row 1, column tabColumnTable[X] via sub_2A64 after drawing the tab shape ($9804) with sub_2B18; self-modifies L_16AC/L_16AE operands",
   "confidence": "high"
  },
  {
   "addr": "1691",
   "name": "setTabNormalColour",
   "summary": "Colours tab X with its normal colour from tabColourTable: sets D_2A2D=1 (row) and D_2A2E=tabColumnTable[X] (column), draws the tab shape at $9804 with sub_2B18 and fills 2x2 colour cells with sub_2A64.",
   "inputs": "X = tab index 0..2",
   "outputs": "screen colour RAM updated; D_2A2D/D_2A2E set",
   "confidence": "high"
  },
  {
   "addr": "16B7",
   "name": "blinkSelectedTab",
   "summary": "Called every pass of the menu-screen main loop (L_1439). When countdown zp_6E reaches 0 it reloads it with 10 and toggles blinkPhase (D_1889), recolouring the selected tab (D_90F9) bright or normal so it blinks.",
   "inputs": "zp_6E countdown, D_90F9 selected tab, D_1889",
   "outputs": "zp_6E=10, D_1889 toggled, tab recoloured",
   "confidence": "high"
  },
  {
   "addr": "16D6",
   "name": "printEnergyLabel",
   "summary": "Prints the string 'ENERGY' ($CBB8) at column $1C, row 8 of the REPAIR tab using the menu text colour.",
   "inputs": "none",
   "outputs": "zp_3D, zp_3F, zp_40 set; text printed",
   "confidence": "high"
  },
  {
   "addr": "16EB",
   "name": "setMenuTextColour",
   "summary": "Sets the text colour byte zp_3D for the menu screen: $61 normally, $21 when the REPAIR tab (D_90F9==1) is shown and the comcen is stunned (D_922A bit 7).",
   "inputs": "D_922A, D_90F9",
   "outputs": "zp_3D",
   "confidence": "high"
  },
  {
   "addr": "16FF",
   "name": "printEnergyValue",
   "summary": "Prints the comcen energy as a percentage at column $22 row 9: reads unitEnergyTable[comcenUnit] & $3F, doubles it (0..100), right-aligns with leading spaces and prints it with sub_C17D only if it changed since lastEnergyShown. Then classifies it into comcenSystemStatus[0]: 0 when 100%, $40 when >= 21%, $80 (critical) below.",
   "inputs": "D_9236 comcen unit index, D_F9C4 energy table, D_16FE",
   "outputs": "D_16FE updated, D_9241 (energy status) updated, text printed; saves/restores the text window (sub_C41B/sub_C40E)",
   "confidence": "high"
  },
  {
   "addr": "175A",
   "name": "drawRepairTab",
   "summary": "Draws the REPAIR tab contents: background colour $66 (or $22 when stunned), clears the window, draws the comcen schematic (sub_8632 in overlay B), paints a 20x10 colour block at row 10 column 13, resets the blink state and energy snapshot, then draws the four system status markers (unless stunned), prints the ENERGY label/value and, when stunned, 'COMCEN STUNNED!' at column 5. Remembers the stun flag in D_1413 so the main loop can redraw on change.",
   "inputs": "D_922A stun status, D_9236, D_F9C4",
   "outputs": "D_1413, zp_6E=0, D_188A=0, D_1889, D_16FE=$FF, D_17D8 energy snapshot, D_91D5 (=4 when markers drawn), screen updated",
   "confidence": "high"
  },
  {
   "addr": "17DA",
   "name": "repairTabAction",
   "summary": "Fire-button action of the REPAIR tab (entered through the address table D_1535, index 1; the disassembly shows it as data). Only works when the DAMAGE option ($0BA3 bit 4) is on, not in film playback ($0B9B bit 7) and a game is running ($0BA8==0). If the comcen is stunned it prints 'ATTEMPTING REPAIR...', plays sound $18, waits 200 frames and, with a 1-in-8 chance while the stun counter is below 8, queues command byte $9E and plays sound $13 (else $12). Otherwise (L_1823) it queues a 3-byte command $80,unit,x,y for the comcen if its flag byte allows, blanks the markers, shows the 'REPAIR WHICH:' selection screen (sub_652E, also mis-typed as data), redraws the markers and snapshots the statuses (sub_14F0).",
   "inputs": "D_0BA3, D_0B9B, D_0BA8, D_922A, D_9236, D_FB54, D_F640/D_F6A4",
   "outputs": "command bytes queued via sub_55EC/sub_560A, sounds, screen; D_1889, D_188A, D_1412 loop counter, zp_73-zp_75",
   "confidence": "medium"
  },
  {
   "addr": "188B",
   "name": "toggleSystemMarker",
   "summary": "Blink helper used by the repair selection menu (called from $6108 and blinkRepairMarker): toggles blinkPhase and either draws the status marker of system D_91D5 (drawSystemMarker) or erases it (eraseSystemMarker with colour $16).",
   "inputs": "D_91D5 selected system 0..3, D_1889",
   "outputs": "D_1889 toggled; marker drawn/erased",
   "confidence": "high"
  },
  {
   "addr": "189D",
   "name": "drawSystemMarker",
   "summary": "Draws the status marker of comcen system Y on the schematic as a plus-shaped group of five colour cells at column systemMarkerColumnTable[Y], row systemMarkerRowTable[Y]: colour $15 (green) when comcenSystemStatus[Y]==0, $12 (red) when bit 7 set (damaged), $17 (yellow) when $40. An OK system is erased instead (3x3 of $16) unless showOkMarkers (D_188A) is set. Refreshes the energy reading first.",
   "inputs": "Y = system index 0..3, D_9241, D_188A",
   "outputs": "screen colour cells; D_2A2A, D_2A2D, D_2A2E; self-modifies L_18A3",
   "confidence": "high"
  },
  {
   "addr": "1919",
   "name": "eraseSystemMarker",
   "summary": "Fills the 3x3 colour block of system Y's marker with colour X (normally $16 = blank) via sub_2A64.",
   "inputs": "Y = system index, X = colour byte",
   "outputs": "D_2A2D/D_2A2E set, rectangle filled",
   "confidence": "high"
  },
  {
   "addr": "192D",
   "name": "sendEnergyRepairCommand",
   "summary": "Queues the 3-byte command $9A, comcenUnit, repairAmount (D_17D9) to the opponent/game engine via sub_560F. Shown as data in the disassembly; called from attemptSystemRepair.",
   "inputs": "D_9236, D_17D9",
   "outputs": "zp_74, zp_75 set; command queued",
   "confidence": "medium"
  },
  {
   "addr": "193C",
   "name": "attemptSystemRepair",
   "summary": "Called from the 'REPAIR WHICH:' menu code at $6590 (also mis-typed as data) with the chosen system in D_91D5. Prints 'ATTEMPTING REPAIR...', sound $18, then succeeds when (random & 3)==0 ((random & 1) for the energy system) and the system is damaged: energy gets (50-energy)/8+1 added (capped at 50) through sendEnergyRepairCommand, other systems have comcenSystemStatus[Y] cleared; plays sound $13 on success, $12 on failure, blinking the marker for about 60 ticks each time. Finally resets the energy snapshot, zp_6E, blinkPhase and clears the message line.",
   "inputs": "D_91D5, D_9241, D_17D8, RNG sub_FD5D",
   "outputs": "D_17D8, D_17D9, D_9241[Y], zp_6E=0, D_1889=0; sounds, command queued",
   "confidence": "medium"
  },
  {
   "addr": "19B0",
   "name": "blinkRepairMarker",
   "summary": "Blinks the selected system marker: zp_6F = 60 ticks; every 20 frames (sub_4094) calls toggleSystemMarker until zp_6F expires or the game-end flag D_92C9 is set.",
   "inputs": "D_91D5, D_92C9",
   "outputs": "zp_6F, marker toggled",
   "confidence": "high"
  },
  {
   "addr": "19C9",
   "name": "printAttemptingRepair",
   "summary": "Sets the repair message window and prints 'ATTEMPTING REPAIR...' ($CBE5), then restores the previous text window (sub_C40E).",
   "inputs": "none",
   "outputs": "text printed",
   "confidence": "high"
  },
  {
   "addr": "19D6",
   "name": "clearRepairMessage",
   "summary": "Sets the repair message window and prints 20 spaces (the runtime routine at $C0EF prints A copies of the fill character; the label bootReceiveSectorData is stale), then restores the window.",
   "inputs": "none",
   "outputs": "message line cleared",
   "confidence": "high"
  },
  {
   "addr": "19E1",
   "name": "setRepairMessageWindow",
   "summary": "Saves the current text window (sub_C41B), sets the menu text colour, the stats window geometry (sub_1578: left 2, rows 7..$16, width $24) and positions the cursor at column 10, row 20.",
   "inputs": "none",
   "outputs": "zp_39-zp_40, zp_3D set, window saved at $92B6",
   "confidence": "high"
  },
  {
   "addr": "19F3",
   "name": "printOptionsList",
   "summary": "Prints the MISC tab: window at column 6 rows 9..$1D, colour $61, '   OPTIONS'. While a game is running ($0BA8==0 and D_92C9==0) it lists 'ABORT GAME', 'VOICE PAUSE' (only when not in film playback and the modem build flag $0BA5 is clear) and ' (EXIT)'. Otherwise it lists the main menu: COMPETE WITH MODEM OPPONENT / PRACTICE WITH SOLO TRAINER / WATCH GAME FILM / SAVE GAME FILM / LOAD GAME FILM and, when the comm link is active (D_E03B), DISCONNECT/SET VOICE.",
   "inputs": "D_0BA8, D_92C9, D_0B9B, D_0BA5, D_E03B",
   "outputs": "text printed; zp_39-zp_3F set",
   "confidence": "high"
  },
  {
   "addr": "1A49",
   "name": "miscTabAction",
   "summary": "Fire-button action of the MISC tab (D_1535 entry 2). With a game running it runs the options menu (sub_65A0 with 2 or 3 items); item 0 ABORT GAME sends command $8C (reason 0) to a connected opponent, waits up to $26 ticks for the transmitter, hangs up (hangUpModem) and redisplays the status message. Then, and whenever no game is running, it loops: clear window, restoreSettingsAfterFilm, printOptionsList, runMainMenu.",
   "inputs": "D_92C9, D_0BA8, D_0B9B, D_0BA5, D_E03B, D_0B9C, D_E030, D_92A1",
   "outputs": "D_91D4/D_91D5/D_91D6 menu state, D_92A1, zp_73, D_9163, zp_7F; may end the game via endGameToMainMenu",
   "confidence": "medium"
  },
  {
   "addr": "1ADC",
   "name": "endGameToMainMenu",
   "summary": "Resets game-session state when a game ends (also jumped to from sub_C86D when D_92C9 is negative): clears D_92C9, zp_BE, the outgoing command ring indices $908D/$908E and D_92FD; reloads the runtime overlays unless D_67C8 is $0D/$0E; for comm build 1 (D_E030 bit 7) clears the link flag and makes PRACTICE the default item; marks no game in progress ($0BA8=1); when not in setup/film mode calls sub_57F2 and makes WATCH GAME FILM the default; finally sets commSessionState $0B9C to $80 if the link is active (flushing it with sub_56F1 if needed) or 0.",
   "inputs": "D_67C8, D_E030, D_E03B, zp_B1, D_0B9B, D_0B9C",
   "outputs": "D_92C9=0, zp_BE=0, D_908D/D_908E=0, D_92FD, D_0BA8, D_0B9C, D_E03B",
   "confidence": "medium"
  },
  {
   "addr": "1B33",
   "name": "hangUpModem",
   "summary": "Entry that hangs up with a resulting commSessionState of 0 (falls into hangUpModemSetState with A=0).",
   "inputs": "none",
   "outputs": "see hangUpModemSetState",
   "confidence": "high"
  },
  {
   "addr": "1B35",
   "name": "hangUpModemSetState",
   "summary": "If the link is active and this is the modem build (D_E030 bit 7 clear) shows message $18 'PRESS SPACE, WAIT, HANGUP PHONE.' and waits for space, then waits up to $26 ticks for D_E03C bit 7 to clear; calls the comm module sub_E003 with X=3 (link down) and stores A into $0B9C.",
   "inputs": "A = new value for D_0B9C, D_E03B, D_E030, D_E03C",
   "outputs": "D_0B9C, comm flags cleared by sub_E003, zp_7F; self-modifies L_1B59",
   "confidence": "high"
  },
  {
   "addr": "1B5F",
   "name": "openCommLink",
   "summary": "If no link is active: on the modem build ($0BA5 bit 7 clear) shows message $17 'PICK UP PHONE THEN PRESS SPACE.' and waits for space, then calls sub_E003 with X=0 which sets the comm flags D_E03B/D_E03C/D_E03D to $FF.",
   "inputs": "D_E03B, D_0BA5",
   "outputs": "comm link flags set",
   "confidence": "high"
  },
  {
   "addr": "1B74",
   "name": "openLinkAndWaitForConnection",
   "summary": "openCommLink followed by waitForConnection.",
   "inputs": "none",
   "outputs": "see callees",
   "confidence": "high"
  },
  {
   "addr": "1B77",
   "name": "waitForConnection",
   "summary": "On the modem build: shows persistent message $10 'WAITING FOR CONNECTION...', runs waitForCarrier (zp_18 preset to $FF), clears bit 7 of zp_BE, clears the message and restores the status area (sub_C39F).",
   "inputs": "D_0BA5",
   "outputs": "D_92A1, zp_18, zp_BE",
   "confidence": "high"
  },
  {
   "addr": "1B9A",
   "name": "waitForCarrier",
   "summary": "Polling loop: services the message/status system (sub_C4EF), handles comm status code zp_B1==$FF via sub_5A14, waits 2 frames and decrements zp_18 while D_E03D bit 6 is clear (held at 1 instead of wrapping), until D_E03C bit 6 (connected) is set; then silences sound (sub_C89A 0). The stray $60 at $1BC0 is an unreachable RTS.",
   "inputs": "zp_B1, D_E03C, D_E03D, zp_18",
   "outputs": "zp_18, sound off",
   "confidence": "medium"
  },
  {
   "addr": "1BC1",
   "name": "showMessageWaitForSpace",
   "summary": "Shows status message A (stored in D_92A1 as the persistent message), clears D_9277/D_929C/D_0B7D, then loops servicing sub_C4EF and stirring the RNG until lastKeyPressed (D_9248) == $A0 (space); clears the message (sub_C6EF), sets D_0B7D=$1E and D_9248=$FF.",
   "inputs": "A = message id",
   "outputs": "D_92A1, D_9277, D_929C, D_0B7D, D_9248",
   "confidence": "high"
  },
  {
   "addr": "1BF7",
   "name": "voicePauseReconnect",
   "summary": "Voice pause: hangUpAndPause, then openLinkAndWaitForConnection; stores A in zp_7B and clears bit 7 of D_91D5. Called from the VOICE PAUSE menu item (sub_65A0), sub_5A14 and the $C000 overlay ($C768).",
   "inputs": "D_0B9C",
   "outputs": "zp_7B, D_91D5",
   "confidence": "medium"
  },
  {
   "addr": "1C08",
   "name": "hangUpAndPause",
   "summary": "hangUpModemSetState with A = current $0B9C (or 0 if it is negative), then waits 250 frames (sub_C37F).",
   "inputs": "D_0B9C",
   "outputs": "D_0B9C",
   "confidence": "high"
  },
  {
   "addr": "1C17",
   "name": "disconnectFromOpponent",
   "summary": "If a session is active ($0B9C bit 7): queues command $8C with reason byte 5 (zp_73), flushes the outgoing queue (sub_562B) and hangs up (hangUpModem). Used for DISCONNECT/SET VOICE and before starting a solo practice game.",
   "inputs": "D_0B9C",
   "outputs": "zp_73=5, command queued, link closed, D_0B9C=0",
   "confidence": "medium"
  },
  {
   "addr": "1C2C",
   "name": "runMainMenu",
   "summary": "Runs the main menu drawn by printOptionsList: 5 items (6 = DISCONNECT/SET VOICE when $0B9C bit 7), initial item D_92FD, first row $0B; sets D_90F8 and zp_B1 to $F0, colour $60, and calls the menu selector sub_5FD1. Item 5 -> disconnectFromOpponent and return. Otherwise waits 15 frames, disconnects first for item 1 (PRACTICE WITH SOLO TRAINER), reloads the $6F00 overlay/strings (sub_1030 with $0BA7 bumped) and jumps to L_7DE3 in the overlay to act on the choice.",
   "inputs": "D_0B9C, D_92FD, zp_3B",
   "outputs": "D_91D4, D_91D5, D_91D6, D_90F8=$F0, zp_B1=$F0, zp_3D/zp_42=$60, D_0BA7",
   "confidence": "medium"
  },
  {
   "addr": "1C7B",
   "name": "isAsymmetricGameType",
   "summary": "Tests the game type (D_0BA3 & 7): returns Z=1 when it is 1 (QB SNEAK) or 6 (DEFENDER), the scenarios where '1ST PLAY'/'2ND PLAY' is shown on the STATS tab.",
   "inputs": "D_0BA3",
   "outputs": "Z flag, A = type",
   "confidence": "medium"
  },
  {
   "addr": "1C87",
   "name": "restoreSettingsAfterFilm",
   "summary": "When the film-playback flag $0B9B bit 7 is set: clears it and, if pendingGameType $0BA4 != $FF, copies the pending settings back into the current ones: $0BAF-$0BB4 -> $0BA9-$0BAE, $0B98-$0B9A -> $0B95-$0B97, $0BA2 -> $0BA1, $0B9E -> $0B9D, $0BA4 -> $0BA3.",
   "inputs": "D_0B9B, D_0BA4, D_0BAF.., D_0B98.., D_0BA2, D_0B9E",
   "outputs": "D_0B9B=0, D_0BA9.., D_0B95.., D_0BA1, D_0B9D, D_0BA3",
   "confidence": "medium"
  },
  {
   "addr": "1CC1",
   "name": "setViewVerticalLimit",
   "summary": "Sets zp_F8, the exclusive limit for the view centre row: 40 normally, 20 when zp_B1 (setup/film phase) is nonzero, in which case the incoming row delta Y is also biased by -20 (the caller adds it back).",
   "inputs": "Y = row delta, zp_B1",
   "outputs": "zp_F8, Y adjusted",
   "confidence": "medium"
  },
  {
   "addr": "1CD3",
   "name": "scrollViewBy",
   "summary": "Scrolls the battlefield view by X columns and Y rows (-1/0/+1): keeps the view centre (zp_A5+3, zp_A6+2) and, when a group box is active (D_920A bit 7), the box offsets D_922E-D_9231 inside the 40x40 map; the actually applied deltas go to zp_F0/zp_F1 (0 if clamped), zp_A5/zp_A6 are updated, D_91CC cleared, sub_3049 refreshes the cursor cell, then it continues into scrollViewBitmap.",
   "inputs": "X = dx, Y = dy, zp_A5, zp_A6, zp_B1, D_920A, D_922E-D_9231",
   "outputs": "zp_A5, zp_A6, zp_F0, zp_F1, zp_F8, D_91CC=0; screen scrolled",
   "confidence": "high"
  },
  {
   "addr": "1D4E",
   "name": "scrollViewToUnit",
   "summary": "Scrolls the view step by step along a Bresenham line from the current cursor cell (zp_92 row, zp_93 column) to unit X's position until the unit is centred. A nonzero A animates: each step waits for countdown zp_6E (reloaded with A) while polling input (sub_40B2) and aborts as soon as the joystick leaves centre (zp_5D != $0F). Callers: $25B3 (A=1, selected unit D_90F8) and $485C (A=0).",
   "inputs": "A = ticks per step (0 = immediate), X = unit index, zp_92/zp_93 current cell",
   "outputs": "D_1D4C, D_1D4D, zp_6E, zp_92-zp_97, zp_8C-zp_91, zp_A5/zp_A6, screen",
   "confidence": "high"
  },
  {
   "addr": "1D97",
   "name": "setLineTargetToUnit",
   "summary": "Loads the Bresenham target from the unit position tables: zp_95 = unitXTable[X], zp_94 = unitYTable[X].",
   "inputs": "X = unit index",
   "outputs": "zp_94, zp_95",
   "confidence": "high"
  },
  {
   "addr": "1DE4",
   "name": "scrollViewBitmap",
   "summary": "Shifts the 7x5-cell battlefield view (16x16-pixel cells at screen column 25, row 1: bitmap $A208, screen RAM $8C41, colour RAM +$4C00) by one cell in the direction given by zp_F0/zp_F1 (0/1/2 = -1/0/+1) using two self-modified copy loops parameterised from the tables at $1DA2-$1DE3, then redraws the newly exposed column (5 cells) and/or row (7 cells) from the map with sub_25FA/sub_5D1A. Entered only by jmp from scrollViewBy.",
   "inputs": "zp_F0, zp_F1, zp_A5, zp_A6",
   "outputs": "zp_F2-zp_F7, zp_54-zp_56, bitmap/screen/colour RAM; self-modifies L_1E81-L_1EBF and D_1E89/D_1EBE opcodes",
   "confidence": "high"
  },
  {
   "addr": "1F4E",
   "name": "keepGroupBoxInsideMap",
   "summary": "Nudges the view origin zp_A5/zp_A6 one cell at a time until the group box (centre + D_922E..D_9231 offsets) fits inside the map (width 40, height zp_F8), then redraws the whole view (sub_26C1) if anything moved. Called from $319D after a group box is set up.",
   "inputs": "zp_A5, zp_A6, D_922E-D_9231, zp_B1",
   "outputs": "zp_A5, zp_A6, zp_18, zp_19, zp_F8; view redrawn",
   "confidence": "high"
  }
 ],
 "variables": [
  {
   "addr": "39",
   "scope": "zp",
   "name": "textWindowTop",
   "meaning": "text window first row",
   "confidence": "high"
  },
  {
   "addr": "3A",
   "scope": "zp",
   "name": "textWindowBottom",
   "meaning": "text window last row + 1",
   "confidence": "high"
  },
  {
   "addr": "3B",
   "scope": "zp",
   "name": "textWindowLeft",
   "meaning": "text window left column",
   "confidence": "high"
  },
  {
   "addr": "3C",
   "scope": "zp",
   "name": "textWindowWidth",
   "meaning": "text window width in characters",
   "confidence": "high"
  },
  {
   "addr": "3D",
   "scope": "zp",
   "name": "textColour",
   "meaning": "colour byte used for printed text (foreground/background nibbles of the bitmap colour cell)",
   "confidence": "high"
  },
  {
   "addr": "3E",
   "scope": "zp",
   "name": "textFillChar",
   "meaning": "character used by the clear/space routines ($C06A, $C0EF)",
   "confidence": "medium"
  },
  {
   "addr": "3F",
   "scope": "zp",
   "name": "textColumn",
   "meaning": "text cursor column relative to the window",
   "confidence": "high"
  },
  {
   "addr": "40",
   "scope": "zp",
   "name": "textRow",
   "meaning": "text cursor row",
   "confidence": "high"
  },
  {
   "addr": "42",
   "scope": "zp",
   "name": "textPadColour",
   "meaning": "colour used for the padding spaces of centred text ($C13F)",
   "confidence": "medium"
  },
  {
   "addr": "18",
   "scope": "zp",
   "name": "tempByte18",
   "meaning": "scratch; carrier wait counter in waitForCarrier, moved flag / temp in keepGroupBoxInsideMap",
   "confidence": "medium"
  },
  {
   "addr": "19",
   "scope": "zp",
   "name": "tempByte19",
   "meaning": "scratch; 'view moved' flag in keepGroupBoxInsideMap",
   "confidence": "medium"
  },
  {
   "addr": "21",
   "scope": "zp",
   "name": "loopCounter21",
   "meaning": "loop counter (tab index loop in drawMenuTabBar)",
   "confidence": "high"
  },
  {
   "addr": "54",
   "scope": "zp",
   "name": "mapCellValue",
   "meaning": "map byte of the cell being drawn by sub_5D1A",
   "confidence": "high"
  },
  {
   "addr": "55",
   "scope": "zp",
   "name": "mapCellX",
   "meaning": "map column of the cell being drawn",
   "confidence": "high"
  },
  {
   "addr": "56",
   "scope": "zp",
   "name": "mapCellY",
   "meaning": "map row of the cell being drawn",
   "confidence": "high"
  },
  {
   "addr": "57",
   "scope": "zp",
   "name": "rngState0",
   "meaning": "random number generator state (sub_FD5D), 3 bytes $57-$59",
   "confidence": "high"
  },
  {
   "addr": "5D",
   "scope": "zp",
   "name": "joystickState",
   "meaning": "joystick direction bits (active low, $0F = centred; bit 2 left, bit 3 right)",
   "confidence": "high"
  },
  {
   "addr": "66",
   "scope": "zp",
   "name": "requestedScreenId",
   "meaning": "screen to switch to (bit 7 = none); compared with currentScreenId by sub_06BA",
   "confidence": "medium"
  },
  {
   "addr": "6C",
   "scope": "zp",
   "name": "joystickRepeatDelay",
   "meaning": "countdown during which joystick directions are ignored (set to $14 after a move)",
   "confidence": "high"
  },
  {
   "addr": "6E",
   "scope": "zp",
   "name": "countdownTimer6E",
   "meaning": "general frame countdown (tab blink period = 10, per-step delay of scrollViewToUnit)",
   "confidence": "high"
  },
  {
   "addr": "6F",
   "scope": "zp",
   "name": "countdownTimer6F",
   "meaning": "frame countdown used for the repair marker blink duration (60)",
   "confidence": "medium"
  },
  {
   "addr": "73",
   "scope": "zp",
   "name": "cmdParam0",
   "meaning": "first parameter byte of an outgoing command (unit index or reason code)",
   "confidence": "high"
  },
  {
   "addr": "74",
   "scope": "zp",
   "name": "cmdParam1",
   "meaning": "second parameter byte of an outgoing command (unit x / unit index)",
   "confidence": "high"
  },
  {
   "addr": "75",
   "scope": "zp",
   "name": "cmdParam2",
   "meaning": "third parameter byte of an outgoing command (unit y / amount)",
   "confidence": "high"
  },
  {
   "addr": "7B",
   "scope": "zp",
   "name": "currentMessageId",
   "meaning": "id of the status-line message currently shown (bit 7 = none)",
   "confidence": "medium"
  },
  {
   "addr": "7F",
   "scope": "zp",
   "name": "timeoutTicks",
   "meaning": "tick countdown used as timeout while waiting for the comm module ($26 or $4B)",
   "confidence": "medium"
  },
  {
   "addr": "8C",
   "scope": "zp",
   "name": "lineErrY",
   "meaning": "Bresenham error accumulator (row axis), sub_FBB8/sub_FBFA",
   "confidence": "high"
  },
  {
   "addr": "8D",
   "scope": "zp",
   "name": "lineErrX",
   "meaning": "Bresenham error accumulator (column axis)",
   "confidence": "high"
  },
  {
   "addr": "8E",
   "scope": "zp",
   "name": "lineLength",
   "meaning": "Bresenham major-axis length",
   "confidence": "high"
  },
  {
   "addr": "8F",
   "scope": "zp",
   "name": "lineDeltaY",
   "meaning": "|target row - current row|",
   "confidence": "high"
  },
  {
   "addr": "90",
   "scope": "zp",
   "name": "lineDeltaX",
   "meaning": "|target column - current column|",
   "confidence": "high"
  },
  {
   "addr": "91",
   "scope": "zp",
   "name": "lineStepsLeft",
   "meaning": "remaining Bresenham steps",
   "confidence": "high"
  },
  {
   "addr": "92",
   "scope": "zp",
   "name": "cursorRow",
   "meaning": "current map row of the view centre / line start (= zp_A6 + 2)",
   "confidence": "high"
  },
  {
   "addr": "93",
   "scope": "zp",
   "name": "cursorCol",
   "meaning": "current map column of the view centre / line start (= zp_A5 + 3)",
   "confidence": "high"
  },
  {
   "addr": "94",
   "scope": "zp",
   "name": "lineTargetRow",
   "meaning": "Bresenham target row",
   "confidence": "high"
  },
  {
   "addr": "95",
   "scope": "zp",
   "name": "lineTargetCol",
   "meaning": "Bresenham target column",
   "confidence": "high"
  },
  {
   "addr": "96",
   "scope": "zp",
   "name": "lineStepY",
   "meaning": "row step direction (+1/-1)",
   "confidence": "high"
  },
  {
   "addr": "97",
   "scope": "zp",
   "name": "lineStepX",
   "meaning": "column step direction (+1/-1)",
   "confidence": "high"
  },
  {
   "addr": "A2",
   "scope": "zp",
   "name": "stringPtr",
   "meaning": "pointer to the bit-7-terminated string being printed by $C133/$C137 (lo), $A3 hi",
   "confidence": "high"
  },
  {
   "addr": "A5",
   "scope": "zp",
   "name": "viewLeftCol",
   "meaning": "map column of the left edge of the 7x5 battlefield view",
   "confidence": "high"
  },
  {
   "addr": "A6",
   "scope": "zp",
   "name": "viewTopRow",
   "meaning": "map row of the top edge of the battlefield view (biased +20 when zp_B1 is nonzero)",
   "confidence": "high"
  },
  {
   "addr": "B1",
   "scope": "zp",
   "name": "gamePhase",
   "meaning": "0 during battle; nonzero ($F0 menu, $FE/$FF) in setup/menu phases where the view is limited to a 20-row half of the map; $FF also triggers special comm handling",
   "confidence": "low"
  },
  {
   "addr": "B6",
   "scope": "zp",
   "name": "screenPtr",
   "meaning": "pointer into screen RAM ($8C00 based) computed from row/column tables (lo), $B7 hi",
   "confidence": "high"
  },
  {
   "addr": "BE",
   "scope": "zp",
   "name": "commPauseFlags",
   "meaning": "comm/pause related flag byte (bit 7 cleared after connecting, bit 0 toggled by $C70A)",
   "confidence": "low"
  },
  {
   "addr": "F0",
   "scope": "zp",
   "name": "scrollDeltaX",
   "meaning": "applied horizontal scroll delta (0/1/2 = -1/0/+1 after the +1 in scrollViewBitmap)",
   "confidence": "high"
  },
  {
   "addr": "F1",
   "scope": "zp",
   "name": "scrollDeltaY",
   "meaning": "applied vertical scroll delta (0/1/2 = -1/0/+1)",
   "confidence": "high"
  },
  {
   "addr": "F2",
   "scope": "zp",
   "name": "bitmapRowStride",
   "meaning": "signed 16-bit stride added between bitmap rows while scrolling (lo), $F3 hi (+320 or -320)",
   "confidence": "high"
  },
  {
   "addr": "F4",
   "scope": "zp",
   "name": "screenRowStride",
   "meaning": "signed 16-bit stride between screen rows while scrolling (lo), $F5 hi (+40/-40)",
   "confidence": "high"
  },
  {
   "addr": "F6",
   "scope": "zp",
   "name": "scrollRowCount",
   "meaning": "number of character rows to copy while scrolling (8 or 10)",
   "confidence": "high"
  },
  {
   "addr": "F7",
   "scope": "zp",
   "name": "redrawCellCount",
   "meaning": "cells left to redraw after a scroll (5 or 7)",
   "confidence": "high"
  },
  {
   "addr": "F8",
   "scope": "zp",
   "name": "viewRowLimit",
   "meaning": "exclusive map-row limit for the view centre (40, or 20 in setup phase)",
   "confidence": "high"
  },
  {
   "addr": "0B7D",
   "scope": "abs",
   "name": "uiDelayTimer",
   "meaning": "key/menu delay countdown (set $14 by the menu selector, $1E after a keypress wait)",
   "confidence": "low"
  },
  {
   "addr": "0B9B",
   "scope": "abs",
   "name": "filmPlaybackFlag",
   "meaning": "bit 7 set while a game film is being played back (commands are not queued, number keys set the replay speed)",
   "confidence": "medium"
  },
  {
   "addr": "0B9C",
   "scope": "abs",
   "name": "commSessionState",
   "meaning": "0 = no session, $80 = linked session, $FF = link lost; also sent to the comm module by sub_56F1",
   "confidence": "low"
  },
  {
   "addr": "0BA1",
   "scope": "abs",
   "name": "playsFirstFlag",
   "meaning": "nonzero: this side has '1ST PLAY' in asymmetric scenarios, else '2ND PLAY'",
   "confidence": "medium"
  },
  {
   "addr": "0BA2",
   "scope": "abs",
   "name": "pendingPlaysFirstFlag",
   "meaning": "copy of playsFirstFlag restored after film playback",
   "confidence": "medium"
  },
  {
   "addr": "0BA3",
   "scope": "abs",
   "name": "gameTypeOptions",
   "meaning": "bits 0-2 game type (0 SCRIMAGE,1 QB SNEAK,2 THE BOMB,3 FACE-OFF,4 SLUGGERS,5 FULL WAR,6 DEFENDER,7 M...), bit 4 DAMAGE option on, bit 6 CUSTOM rules",
   "confidence": "medium"
  },
  {
   "addr": "0BA4",
   "scope": "abs",
   "name": "pendingGameTypeOptions",
   "meaning": "pending copy of gameTypeOptions, $FF = none",
   "confidence": "medium"
  },
  {
   "addr": "0BA5",
   "scope": "abs",
   "name": "commBuildFlag",
   "meaning": "copy of D_E030; bit 7 set = comm build 1 (T35): no phone pick-up/hang-up prompts, no VOICE PAUSE item",
   "confidence": "medium"
  },
  {
   "addr": "0BA7",
   "scope": "abs",
   "name": "diskLoadNestCount",
   "meaning": "incremented around overlay reloads (sub_1030)",
   "confidence": "low"
  },
  {
   "addr": "0BA8",
   "scope": "abs",
   "name": "noGameInProgress",
   "meaning": "nonzero when no game is running (menus); 0 during a game",
   "confidence": "medium"
  },
  {
   "addr": "0BA9",
   "scope": "abs",
   "name": "currentSettingsText",
   "meaning": "6-byte text field ($A0 padded) of the current game settings, restored from $0BAF after a film",
   "confidence": "low"
  },
  {
   "addr": "0BAF",
   "scope": "abs",
   "name": "pendingSettingsText",
   "meaning": "6-byte pending copy of currentSettingsText",
   "confidence": "low"
  },
  {
   "addr": "1412",
   "scope": "abs",
   "name": "loopCounter1412",
   "meaning": "loop counter used by repairTabAction (marker loops)",
   "confidence": "high"
  },
  {
   "addr": "1413",
   "scope": "abs",
   "name": "lastStunFlagShown",
   "meaning": "copy of D_922A taken when the REPAIR tab was drawn; bit 7 compared to detect stun changes",
   "confidence": "high"
  },
  {
   "addr": "1535",
   "scope": "abs",
   "name": "tabActionTable",
   "meaning": "3 JSR addresses for the fire action of tabs 0..2: $158F (STATS), $17DA (REPAIR), $1A49 (MISC)",
   "confidence": "high"
  },
  {
   "addr": "2A2A",
   "scope": "abs",
   "name": "fillColourValue",
   "meaning": "colour byte written by the rectangle fill sub_2A64",
   "confidence": "high"
  },
  {
   "addr": "2A2D",
   "scope": "abs",
   "name": "fillRow",
   "meaning": "screen row for sub_2A64 / sub_2B18",
   "confidence": "high"
  },
  {
   "addr": "2A2E",
   "scope": "abs",
   "name": "fillColumn",
   "meaning": "screen column for sub_2A64 / sub_2B18",
   "confidence": "high"
  },
  {
   "addr": "9065",
   "scope": "abs",
   "name": "outgoingCmdRing",
   "meaning": "40-byte ring buffer of outgoing command bytes (sub_55EF)",
   "confidence": "medium"
  },
  {
   "addr": "908D",
   "scope": "abs",
   "name": "outgoingCmdWriteIdx",
   "meaning": "write index into outgoingCmdRing",
   "confidence": "medium"
  },
  {
   "addr": "908E",
   "scope": "abs",
   "name": "outgoingCmdReadIdx",
   "meaning": "read index into outgoingCmdRing",
   "confidence": "medium"
  },
  {
   "addr": "90EF",
   "scope": "abs",
   "name": "textCentreWidth",
   "meaning": "nonzero: next string is centred in this many columns ($C137)",
   "confidence": "medium"
  },
  {
   "addr": "90F4",
   "scope": "abs",
   "name": "lineLastStepY",
   "meaning": "row step (-1/0/+1) taken by the last sub_FBFA call",
   "confidence": "high"
  },
  {
   "addr": "90F5",
   "scope": "abs",
   "name": "lineLastStepX",
   "meaning": "column step taken by the last sub_FBFA call",
   "confidence": "high"
  },
  {
   "addr": "90F8",
   "scope": "abs",
   "name": "selectedUnit",
   "meaning": "index of the currently selected unit ($F0/$FE/$FF = none)",
   "confidence": "medium"
  },
  {
   "addr": "90F9",
   "scope": "abs",
   "name": "menuTabIndex",
   "meaning": "selected tab of the in-game menu screen: 0 STATS, 1 REPAIR, 2 MISC",
   "confidence": "high"
  },
  {
   "addr": "90FB",
   "scope": "abs",
   "name": "currentScreenId",
   "meaning": "active screen: 0 battlefield, 1 ($7587), 2 ($7FE6), 3 STATS/REPAIR/MISC menu",
   "confidence": "medium"
  },
  {
   "addr": "91D4",
   "scope": "abs",
   "name": "menuItemCount",
   "meaning": "number of items for the menu selector sub_5FD1/sub_65A0",
   "confidence": "high"
  },
  {
   "addr": "91D5",
   "scope": "abs",
   "name": "menuSelectedItem",
   "meaning": "current/selected menu item (also the repair system index 0..3)",
   "confidence": "high"
  },
  {
   "addr": "91D6",
   "scope": "abs",
   "name": "menuFirstRow",
   "meaning": "screen row of the first menu item",
   "confidence": "high"
  },
  {
   "addr": "920A",
   "scope": "abs",
   "name": "groupBoxActive",
   "meaning": "bit 7 set while a group/formation box around the cursor is active",
   "confidence": "medium"
  },
  {
   "addr": "920E",
   "scope": "abs",
   "name": "statsNeedRefresh",
   "meaning": "nonzero: STATS tab must be redrawn (sub_8518)",
   "confidence": "low"
  },
  {
   "addr": "922A",
   "scope": "abs",
   "name": "comcenStunStatus",
   "meaning": "bits 0-3 stun counter of the comcen (from D_F960), bit 7 set = stunned; bit 6 set during the knock-out sequence",
   "confidence": "medium"
  },
  {
   "addr": "922E",
   "scope": "abs",
   "name": "groupBoxLeftOffset",
   "meaning": "signed column offset of the group box left edge from the cursor",
   "confidence": "medium"
  },
  {
   "addr": "922F",
   "scope": "abs",
   "name": "groupBoxRightOffset",
   "meaning": "signed column offset of the group box right edge",
   "confidence": "medium"
  },
  {
   "addr": "9230",
   "scope": "abs",
   "name": "groupBoxTopOffset",
   "meaning": "signed row offset of the group box top edge",
   "confidence": "medium"
  },
  {
   "addr": "9231",
   "scope": "abs",
   "name": "groupBoxBottomOffset",
   "meaning": "signed row offset of the group box bottom edge",
   "confidence": "medium"
  },
  {
   "addr": "9236",
   "scope": "abs",
   "name": "comcenUnit",
   "meaning": "index of the player's COMCEN unit (indexes the unit tables)",
   "confidence": "high"
  },
  {
   "addr": "9241",
   "scope": "abs",
   "name": "comcenSystemStatus",
   "meaning": "4 bytes: status of the comcen energy (0) and three systems (1-3); 0 ok, $40 degraded, $80 damaged",
   "confidence": "high"
  },
  {
   "addr": "9248",
   "scope": "abs",
   "name": "lastKeyPressed",
   "meaning": "last key as ASCII | $80 ($A0 = space, $B1-$B9 digits), $FF = none",
   "confidence": "high"
  },
  {
   "addr": "92A1",
   "scope": "abs",
   "name": "persistentMessageId",
   "meaning": "id of the status message kept on screen (0 = none)",
   "confidence": "low"
  },
  {
   "addr": "92B6",
   "scope": "abs",
   "name": "savedTextWindow",
   "meaning": "12-byte save area for zp_39-zp_44 (sub_C41B/sub_C40E)",
   "confidence": "high"
  },
  {
   "addr": "92C9",
   "scope": "abs",
   "name": "gameEndFlag",
   "meaning": "nonzero once the game is over/aborted (bit 7 = return to menu pending); aborts waits",
   "confidence": "medium"
  },
  {
   "addr": "92D6",
   "scope": "abs",
   "name": "comcenStatusSnapshot",
   "meaning": "4-byte copy of comcenSystemStatus (energy in [0]) taken when the REPAIR tab was last used; differences auto-select the REPAIR tab",
   "confidence": "high"
  },
  {
   "addr": "92FD",
   "scope": "abs",
   "name": "defaultMainMenuItem",
   "meaning": "initial main-menu item (0 COMPETE, 1 PRACTICE, 2 WATCH FILM)",
   "confidence": "medium"
  },
  {
   "addr": "E01D",
   "scope": "abs",
   "name": "commTxState",
   "meaning": "comm module transmit state byte (0 = nothing pending; $C0 set by sub_E003 default path)",
   "confidence": "low"
  },
  {
   "addr": "E030",
   "scope": "abs",
   "name": "commModuleFlags",
   "meaning": "comm module capability byte, bit 7 set in build 1 (T35) = no modem handshake prompts",
   "confidence": "medium"
  },
  {
   "addr": "E03B",
   "scope": "abs",
   "name": "commLinkActive",
   "meaning": "$FF while the link is open (set by sub_E003 X=0, cleared by X>=1)",
   "confidence": "medium"
  },
  {
   "addr": "E03C",
   "scope": "abs",
   "name": "commLineStatus",
   "meaning": "bit 6 = connected (carrier), bit 7 = line busy; $FF after link open",
   "confidence": "low"
  },
  {
   "addr": "E03D",
   "scope": "abs",
   "name": "commStatusFlags2",
   "meaning": "bit 6 polled while waiting for a carrier; $FF after link open",
   "confidence": "low"
  },
  {
   "addr": "F640",
   "scope": "abs",
   "name": "unitXTable",
   "meaning": "map column of each unit (100 entries)",
   "confidence": "high"
  },
  {
   "addr": "F6A4",
   "scope": "abs",
   "name": "unitYTable",
   "meaning": "map row of each unit (100 entries)",
   "confidence": "high"
  },
  {
   "addr": "F960",
   "scope": "abs",
   "name": "unitStunTable",
   "meaning": "per-unit byte whose low nibble is the stun counter (used for the comcen)",
   "confidence": "low"
  },
  {
   "addr": "F9C4",
   "scope": "abs",
   "name": "unitEnergyTable",
   "meaning": "per-unit energy, bits 0-5 = 0..50 (shown x2 as a percentage)",
   "confidence": "high"
  },
  {
   "addr": "FB54",
   "scope": "abs",
   "name": "unitFlagsTable",
   "meaning": "per-unit flag byte (bits 2-6 group/type, bit 6/7 tested before ordering the comcen)",
   "confidence": "low"
  }
 ],
 "dataBlocks": [
  {
   "addr": "163A",
   "length": 3,
   "type": "byteTable",
   "name": "tabColumnTable",
   "description": "screen column of tab 0..2 boxes ($0B,$13,$1B)"
  },
  {
   "addr": "163D",
   "length": 3,
   "type": "byteTable",
   "name": "tabColourTable",
   "description": "normal colour byte of tabs (green $05, blue $06, red $02)"
  },
  {
   "addr": "1640",
   "length": 3,
   "type": "byteTable",
   "name": "tabHighlightColourTable",
   "description": "bright colour byte of tabs ($0D,$0E,$0A)"
  },
  {
   "addr": "16FE",
   "length": 1,
   "type": "byteTable",
   "name": "lastEnergyShown",
   "description": "energy value last printed ($FF forces a reprint)"
  },
  {
   "addr": "17D8",
   "length": 1,
   "type": "byteTable",
   "name": "comcenEnergySnapshot",
   "description": "comcen energy (0..50) when the REPAIR tab was drawn / after a repair"
  },
  {
   "addr": "17D9",
   "length": 1,
   "type": "byteTable",
   "name": "repairAmount",
   "description": "energy added by the last repair attempt"
  },
  {
   "addr": "1881",
   "length": 4,
   "type": "byteTable",
   "name": "systemMarkerColumnTable",
   "description": "screen column of the four comcen system markers ($0D,$10,$14,$19)"
  },
  {
   "addr": "1885",
   "length": 4,
   "type": "byteTable",
   "name": "systemMarkerRowTable",
   "description": "screen row of the four comcen system markers ($0E,$0A,$0A,$0C)"
  },
  {
   "addr": "1889",
   "length": 1,
   "type": "byteTable",
   "name": "blinkPhase",
   "description": "0/1 blink phase shared by the tab blink and the marker blink"
  },
  {
   "addr": "188A",
   "length": 1,
   "type": "byteTable",
   "name": "showOkMarkers",
   "description": "nonzero: draw markers for undamaged systems too (during the REPAIR WHICH screen)"
  },
  {
   "addr": "1BC0",
   "length": 1,
   "type": "unknown",
   "name": "orphanRts1BC0",
   "description": "unreachable $60 (RTS) between waitForCarrier and showMessageWaitForSpace"
  },
  {
   "addr": "1D4C",
   "length": 1,
   "type": "byteTable",
   "name": "scrollTargetUnit",
   "description": "unit index parameter of scrollViewToUnit"
  },
  {
   "addr": "1D4D",
   "length": 1,
   "type": "byteTable",
   "name": "scrollStepDelay",
   "description": "ticks per step parameter of scrollViewToUnit (0 = immediate)"
  },
  {
   "addr": "1DA2",
   "length": 3,
   "type": "byteTable",
   "name": "scrollXStartIndex",
   "description": "per horizontal direction (0=-1,1=0,2=+1): start Y index of the bitmap byte copy ($5F,$00,$00)"
  },
  {
   "addr": "1DA5",
   "length": 3,
   "type": "byteTable",
   "name": "scrollXEndIndex",
   "description": "end Y index of the bitmap byte copy ($FF,$70,$60)"
  },
  {
   "addr": "1DA8",
   "length": 3,
   "type": "byteTable",
   "name": "scrollXStepOpcode",
   "description": "DEY ($88) or INY ($C8) opcode patched into both copy loops"
  },
  {
   "addr": "1DAB",
   "length": 3,
   "type": "byteTable",
   "name": "scrollXBitmapSrcOffset",
   "description": "byte offset added to the bitmap source address (0,0,$10)"
  },
  {
   "addr": "1DAE",
   "length": 3,
   "type": "byteTable",
   "name": "scrollXBitmapDstOffset",
   "description": "byte offset added to the bitmap destination address ($10,0,0)"
  },
  {
   "addr": "1DB1",
   "length": 3,
   "type": "byteTable",
   "name": "scrollYBitmapSrcLo",
   "description": "per vertical direction: bitmap source address lo ($AAC8,$A208,$A488)"
  },
  {
   "addr": "1DB4",
   "length": 3,
   "type": "byteTable",
   "name": "scrollYBitmapSrcHi",
   "description": "bitmap source address hi"
  },
  {
   "addr": "1DB7",
   "length": 3,
   "type": "byteTable",
   "name": "scrollYBitmapDstLo",
   "description": "bitmap destination address lo ($AD48,$A208,$A208)"
  },
  {
   "addr": "1DBA",
   "length": 3,
   "type": "byteTable",
   "name": "scrollYBitmapDstHi",
   "description": "bitmap destination address hi"
  },
  {
   "addr": "1DBD",
   "length": 3,
   "type": "byteTable",
   "name": "scrollYBitmapStrideLo",
   "description": "bitmap row stride lo ($FEC0 = -320 when scrolling up, $0140 = +320 otherwise)"
  },
  {
   "addr": "1DC0",
   "length": 3,
   "type": "byteTable",
   "name": "scrollYBitmapStrideHi",
   "description": "bitmap row stride hi"
  },
  {
   "addr": "1DC3",
   "length": 3,
   "type": "byteTable",
   "name": "scrollYRowCount",
   "description": "character rows to copy (8,10,8)"
  },
  {
   "addr": "1DC6",
   "length": 3,
   "type": "byteTable",
   "name": "scrollXScreenStartIndex",
   "description": "start Y index of the screen/colour copy ($0B,0,0)"
  },
  {
   "addr": "1DC9",
   "length": 3,
   "type": "byteTable",
   "name": "scrollXScreenEndIndex",
   "description": "end Y index of the screen/colour copy ($FF,$0E,$0C)"
  },
  {
   "addr": "1DCC",
   "length": 3,
   "type": "byteTable",
   "name": "scrollXScreenSrcOffset",
   "description": "column offset of the screen source (0,0,2)"
  },
  {
   "addr": "1DCF",
   "length": 3,
   "type": "byteTable",
   "name": "scrollXScreenDstOffset",
   "description": "column offset of the screen destination (2,0,0)"
  },
  {
   "addr": "1DD2",
   "length": 3,
   "type": "byteTable",
   "name": "scrollYScreenSrcLo",
   "description": "screen RAM source address lo ($8D59 row 8, $8C41 row 1, $8C91 row 3; column 25)"
  },
  {
   "addr": "1DD5",
   "length": 3,
   "type": "byteTable",
   "name": "scrollYScreenSrcHi",
   "description": "screen RAM source address hi (colour RAM = +$4C00)"
  },
  {
   "addr": "1DD8",
   "length": 3,
   "type": "byteTable",
   "name": "scrollYScreenDstLo",
   "description": "screen RAM destination address lo ($8DA9, $8C41, $8C41)"
  },
  {
   "addr": "1DDB",
   "length": 3,
   "type": "byteTable",
   "name": "scrollYScreenDstHi",
   "description": "screen RAM destination address hi"
  },
  {
   "addr": "1DDE",
   "length": 3,
   "type": "byteTable",
   "name": "scrollYScreenStrideLo",
   "description": "screen row stride lo ($FFD8 = -40 up, $0028 = +40)"
  },
  {
   "addr": "1DE1",
   "length": 3,
   "type": "byteTable",
   "name": "scrollYScreenStrideHi",
   "description": "screen row stride hi"
  }
 ],
 "misclassified": [
  {
   "addr": "17DA",
   "length": 167,
   "actual": "code",
   "evidence": "$17DA-$1880 disassembles cleanly (lda $0BA3 / and #$10 / beq ...; ends with jmp $14F0); $17DA is the second entry of the JSR address table D_1535 used at $1480-$148D, and the block references $1889/$188A/$1412 and calls $19C9/$19D6/$1919/$189D/$652E. Only $17D8/$17D9 are data."
  },
  {
   "addr": "192D",
   "length": 198,
   "actual": "code",
   "evidence": "$192D-$19F2 disassembles cleanly into six routines ($192D, $193C, $19B0, $19C9, $19D6, $19E1); $193C is called by 'jsr $193C' at $6590 (inside another mis-typed block $652E-$659F), $19C9/$19D6 are called from $17F0/$17FD, $19B0 and $192D from $193C."
  },
  {
   "addr": "652E",
   "length": 114,
   "actual": "code",
   "evidence": "outside this chunk but called from $1860 (jsr $652E) and containing jsr $193C at $6590; it is the 'REPAIR WHICH:' screen (prints $CBD6, runs the menu selector sub_5FD7)."
  }
 ],
 "insights": [
  "The in-game menu screen (screen id 3, entered via sub_06BA -> L_141D when zp_66 requests it) has three tabs STATS / REPAIR / MISC selected with joystick left/right and activated with fire; the main loop is L_1439 and the fire action is dispatched through the JSR address table D_1535 = {$158F, $17DA, $1A49}. sub_1507 picks the initial tab: MISC when no game is running, REPAIR when the comcen is stunned or any comcen status changed since the last visit (snapshot at $92D6), else STATS.",
  "The display is a VIC bank 2 bitmap: bitmap $A000, screen (colour) RAM $8C00, colour RAM $D800 (offset $4C00 from screen RAM). 'Text' is drawn into the bitmap by the $C000 overlay (font at $987E) and all colour operations (tabs, markers) write colour bytes with sub_2A64(A=colour, X=width, Y=height at row D_2A2D, column D_2A2E). The row address tables live at $9300/$9319 (bitmap) and $9382/$939B (screen RAM), column x8 at $9332/$935A.",
  "The battlefield view is 7x5 map cells of 16x16 pixels at screen column 25, row 1 (bitmap $A208, screen $8C41). zp_A5/zp_A6 are the map column/row of its top-left cell, the cursor/centre cell is (zp_A5+3, zp_A6+2) and is mirrored in zp_93/zp_92. scrollViewBitmap ($1DE4) shifts the bitmap and both RAMs by one cell using self-modified loops parameterised by the 22 three-byte tables at $1DA2-$1DE3, then redraws the exposed column/row via sub_25FA (map cell read, row pointers $BFB0/$BFD8) and sub_5D1A (draw cell).",
  "$C000 overlay text API as used here: zp_39/zp_3A/zp_3B/zp_3C = window top/bottom/left/width, zp_3F/zp_40 = cursor column/row, zp_3D = colour byte; sub_C099 = set cursor, sub_C097 = set row then cursor, sub_C06A = clear window, sub_C0D7 = full-screen window, sub_C041 = newline, sub_C01A = print char, sub_C133(A/Y=string) = print bit-7-terminated string, sub_C137 = print the string following the last one, sub_C100(A/Y=table,X) = print 8-byte table entry X, sub_C17D(A/Y) = print decimal, $C0EF (stale label bootReceiveSectorData) = print A fill characters, sub_C41B/sub_C40E = save/restore the window to $92B6, sub_C37F = wait A frames, sub_C89A = play sound A, sub_C6FF = show status message A (table at $0500/$0524), sub_C853 = show 'WORKING...'.",
  "Status message ids used by the comm code: $10 'WAITING FOR CONNECTION...', $17 'PICK UP PHONE THEN PRESS SPACE.', $18 'PRESS SPACE, WAIT, HANGUP PHONE.', $0C 'COMCEN STUNNED!', $01 'WORKING...'. Game-type names are an 8-byte-per-entry table at $CCC8 (SCRIMAGE, QB SNEAK, THE BOMB, FACE-OFF, SLUGGERS, FULL WAR, DEFENDER, ...) indexed by $0BA3 & 7; $CC11 holds STANDARD/CUSTOM selected by $0BA3 bit 6.",
  "Outgoing commands to the opponent/engine are queued byte-wise by sub_55EF into the 40-byte ring at $9065 (indices $908D/$908E, pending count $929F) and suppressed during film playback ($0B9B bit 7). Commands seen here: $80 unit x y (comcen order before repair), $8C reason (abort/disconnect, reason 0 = abort game, 5 = disconnect), $9A unit amount (energy repair), $9E (repair attempt while stunned).",
  "Comm module jump table entry sub_E003: X=0 opens the link (D_E03B/D_E03C/D_E03D := $FF), X>=1 closes it (:= 0, D_E01F := 0). The modem prompts are skipped when $0BA5/D_E030 bit 7 is set (comm build 1 = T35), suggesting build 1 is the direct/null-modem variant and build 2 (T34) the Hayes modem variant.",
  "Comcen damage model: D_9241[0..3] hold energy/system statuses (0 ok, $40 degraded, $80 damaged); sub_3D57 randomly damages systems 1-3 when the DAMAGE option ($0BA3 bit 4) is on; repairs succeed with probability 1/4 (1/2 for energy) and energy repairs add (50-energy)/8+1. Energy lives in unitEnergyTable $F9C4 bits 0-5 (0..50, displayed x2 as %). D_922A mirrors the comcen stun counter from $F960 with bit 7 = stunned.",
  "The file's line numbering does not match the chunk spec (lines 1607-2668 are $13C0-$1D4C); this survey covers the address range $163A-$1FA5 (file lines 1903-3019)."
 ]
}