{
 "chunk": "game_ovl_6F00_A_7C29",
 "unit": "game/ovl_6F00_A",
 "routines": [
  {
   "addr": "7C2E",
   "name": "applyGameTypeDefaults",
   "summary": "Loads the per-side game parameters for the selected game type from the two 5x5 default tables at $892C (side 0) and $8945 (side 1) in the $8800 strings overlay. The type ($0BA3 & 7) is first folded into a table column 0-4 (0 SCRIMAGE, 1 QB SNEAK, 2 THE BOMB/FACE-OFF/SLUGGERS, 3 FULL WAR, 4 DEFENDER); the column also indexes setupPhaseModeTable ($7C29) -> $92C6. Rows 0-1 (terrain points $92AC, recycler mode $92A6) are stored per absolute side, rows 2-4 (comcen speed class $92B4, drones $92AE, missiles $92B0) per relative index (0 = own side, 1 = opponent). Then QB SNEAK removes units 0-6 from play and THE BOMB removes units 0-98, re-creates unit 49 as a COMCEN (type 4) and gives both sides one extra drone. Called once from the game start code ($79E9) after the game type has been chosen.",
   "inputs": "D_0BA3 bits 0-2, D_0B9F, tables $892C/$8945, D_7C29",
   "outputs": "D_92C6, D_92AC/D_92A6 (per side), D_92B4/D_92AE/D_92B0 (own, opponent), D_F76C/D_FAF0 entries set to $FF for removed units, D_F79D=4, zp_18, zp_48/49; self-modifies $7C86 and $7CC0",
   "confidence": "high"
  },
  {
   "addr": "7CA2",
   "name": "removeUnitsUpToY",
   "summary": "Loop that marks units Y down to 0 as absent: writes $FF into unitTypeTable ($F76C) and unitUnderCell ($FAF0). Entered with Y=6 (QB SNEAK) or Y=$62 (THE BOMB) by applyGameTypeDefaults; the RTS at $7CAD is also the shared exit of applyGameTypeDefaults.",
   "inputs": "Y = highest unit index to remove",
   "outputs": "D_F76C[0..Y] = D_FAF0[0..Y] = $FF; Y = $FF",
   "confidence": "high"
  },
  {
   "addr": "7CAE",
   "name": "selectSideDefaultsTable",
   "summary": "Points zp_48/zp_49 at the game-type defaults table of side X: $892C for side 0, $8945 for side 1 (5 rows of 5 bytes each, in the $8800 strings overlay).",
   "inputs": "X = side 0/1",
   "outputs": "zp_48/zp_49 = table pointer",
   "confidence": "high"
  },
  {
   "addr": "7CBF",
   "name": "readDefaultAndAdvanceRow",
   "summary": "Reads byte [column] of the current 5-byte table row through zp_48 (the LDY #$FF operand at $7CC0 is patched with the game-type column by applyGameTypeDefaults) and advances zp_48/zp_49 to the next row (+5).",
   "inputs": "zp_48/zp_49 = current row, patched column index",
   "outputs": "A = table value, zp_48/zp_49 += 5",
   "confidence": "high"
  },
  {
   "addr": "7CD1",
   "name": "startFilmPlayback",
   "summary": "Game-start path taken when filmPlaybackFlag ($0B9B) bit 7 is set (WATCH GAME FILM). Restores the game parameters from the film header in RAM under I/O at $D000 (read with sub_5874, zp_BC/BD already = $D000): byte 4 -> linkTypeAtStart ($92FE), 5 -> playerSide ($0B9F), $0C -> gameTypeOptions ($0BA3), $11-$24 -> the two 10-byte player names at $FFDE, $2B-$2D -> mapSeed ($0B95), $2E bit 0 -> mapOrientationFlag ($0B9D) and bits 1-2 -> playsFirstFlag ($0BA1), 6-$0B -> settings text ($0BA9). Then patches the side-test branches (sub_7BC6), regenerates the map from the seed (sub_6F04), rotates it 180 degrees if the film side differs from the map orientation (sub_EC58), restores the film's saved unit/score state (sub_EE4A), writes 'CUSTOM' into the settings text when bit 6 is set (sub_86C3), places all units (placeAllUnitsOnMap), homes the view (sub_EC1C), redraws the screen, sets lastQuarterFlag and draws the opponent's recycler, then joins the common game start at L_7B8F.",
   "inputs": "film header at $D000 (zp_BC/zp_BD = $D000), D_0B9B bit 7",
   "outputs": "D_0BA8=0, D_92FC=0, D_92FE, D_0B9F, D_0BA3, D_FFDE..D_FFF1, D_0B95-D_0B97, D_0B9D, D_0BA1, D_0BA9-D_0BAE, D_90FB=0, D_92FA=$FF, map at $F000 regenerated; jumps to L_7B8F",
   "confidence": "high"
  },
  {
   "addr": "7D70",
   "name": "initUnitOrderLists",
   "summary": "Fills the two 100-entry unit order lists unitBaseOrder ($9164) and unitProcessOrder ($90FD) with the identity permutation 0..99 (99 down to 0). Called by sub_7BB2 before the lists are shuffled with the scenario RNG.",
   "inputs": "none",
   "outputs": "D_9164[0..99] = D_90FD[0..99] = index; Y=$FF",
   "confidence": "high"
  },
  {
   "addr": "7D7D",
   "name": "placeAllUnitsOnMap",
   "summary": "Calls placeUnitOnMap (sub_3DDA) for every unit 99 down to 0 (unit index passed in X and zp_18). Used at game start ($7B83) and when a film is replayed (startFilmPlayback).",
   "inputs": "unit tables",
   "outputs": "map cells at $F000 updated for all live units; zp_18 = $FF",
   "confidence": "high"
  },
  {
   "addr": "7D8B",
   "name": "setGameClockForType",
   "summary": "Looks up the game length in rounds for the game type ($0BA3 & 7) in gameClockByTypeTable ($78C3: 63,63,31,127,127,254,254) and stores it in gameClock ($91CB); also returns it in A (the caller at $7B9E shifts it right twice to get the quarter length $92EF).",
   "inputs": "D_0BA3 bits 0-2, D_78C3",
   "outputs": "A = D_91CB = rounds",
   "confidence": "high"
  },
  {
   "addr": "7D98",
   "name": "clearUnitsAndRecyclersFromMap",
   "summary": "Removes every unit 99..0 from the map (removeUnitFromMap restores the terrain under it) and then blanks the recycler cell of both sides via clearRecyclerCell. Called from the game start sequence ($7B7A) before the units are re-placed.",
   "inputs": "unit tables, D_92A8/D_92AA",
   "outputs": "map at $F000 without units and recyclers",
   "confidence": "high"
  },
  {
   "addr": "7DA7",
   "name": "clearRecyclerCell",
   "summary": "If side X has a recycler (recyclerCol $92A8,x bit 7 clear) computes the map address of (recyclerCol, recyclerRow) with readMapCell and writes the blank terrain byte $40 into it.",
   "inputs": "X = side, D_92A8/D_92AA",
   "outputs": "map cell := $40, zp_48/zp_49",
   "confidence": "high"
  },
  {
   "addr": "7DB8",
   "name": "waitForSpaceKey",
   "summary": "Clears lastKeyCode, chatState, inputLockoutTimer and chatInputLength, then loops serviceCommandExchange + nextGameRandom until the space key ($A0) is seen in lastKeyCode ($9248). Afterwards clears the message queue, sets inputLockoutTimer to 30 frames and resets lastKeyCode. Entered by JMP from showModemSetupPrompt ($0C9D) so the RTS returns to that routine's caller; stirring the RNG while waiting randomises the map seed.",
   "inputs": "D_9248 (set by the IRQ keyboard scan)",
   "outputs": "D_9248=$FF, D_929C=0, D_9277=0, D_0B7D=$1E, message queue cleared, RNG advanced",
   "confidence": "high"
  },
  {
   "addr": "7DE3",
   "name": "handleMainMenuChoice",
   "summary": "Acts on the main-menu item in menuSelectedItem ($91D5), entered by JMP from runMainMenu ($1C78). 0 COMPETE WITH MODEM OPPONENT: linkTypeFlag $0BA5=0, $0B9B=0. 1 PRACTICE WITH SOLO TRAINER: $0BA5=$FF, $0B9B=0. 2 WATCH GAME FILM: requires a valid film in memory (checkFilmInMemory), keeps $0BA5, $0B9B=$82. These three jump to startGameFromSetup ($0AC8). 3 SAVE GAME FILM (needs a valid film) and 4 LOAD GAME FILM: prompt for the data disk, suspend the comm module, checkDiskAndLoadFilmDirectory (returns on failure), resume, set defaultMainMenuItem to 2 (WATCH GAME FILM), filmDirectoryPage=0, filmMenuMode=item-3, draw the directory page and run runSaveFilmMenu or runLoadFilmMenu, then promptInsertGameDisk and return.",
   "inputs": "D_91D5, D_0BA5, film in $D000",
   "outputs": "D_0BA5, D_0B9B, D_92FD=2, D_7FA1=0, D_7FA2; jumps to $0AC8 for items 0-2",
   "confidence": "high"
  },
  {
   "addr": "7E39",
   "name": "setModeFlagsAndStartGame",
   "summary": "Tail of handleMainMenuChoice: stores X into linkTypeFlag ($0BA5) and A into filmPlaybackFlag ($0B9B) and jumps to startGameFromSetup ($0AC8).",
   "inputs": "X = new $0BA5, A = new $0B9B",
   "outputs": "D_0BA5, D_0B9B; jumps to $0AC8",
   "confidence": "high"
  },
  {
   "addr": "7E4F",
   "name": "checkDiskAndLoadFilmDirectory",
   "summary": "Identifies the disk in the drive and loads the film directory. First tries the game disk silently (checkDiskId X=0, A=$80); if that fails tries the data disk with messages (X=2, A=0) and additionally requires BAM bytes $0203-$028F to be all zero (otherwise 'NOT A DATA DISK' via L_100D). With a valid disk it falls into loadFilmDirectory (track 18 sectors 2-3 -> $0300-$04FF). diskIdIndex ($92F0) is left 0 for the game disk and 2 for a data disk, which later selects 1 or 3 directory pages.",
   "inputs": "disk in drive",
   "outputs": "C=1 no usable disk (message shown), C=0 directory loaded at $0300; D_92F0, D_0200 BAM buffer",
   "confidence": "high"
  },
  {
   "addr": "7E74",
   "name": "writeFilmDirectory",
   "summary": "Waits 8 frames, then writes the film directory $0300-$04FF (2 sectors) to track 18 sectors 2-3 with writeSectors. A write failure goes to diskErrorExit.",
   "inputs": "directory at $0300",
   "outputs": "track 18 s2-3 rewritten; C=0",
   "confidence": "high"
  },
  {
   "addr": "7E8B",
   "name": "loadFilmDirectory",
   "summary": "Reads track 18 sectors 2-3 into $0300-$04FF with loadSectorsRetry (the 30 x 16-byte film names); failure -> diskErrorExit. Tail of checkDiskAndLoadFilmDirectory.",
   "inputs": "none",
   "outputs": "$0300-$04FF = film directory, C=0",
   "confidence": "high"
  },
  {
   "addr": "7EA0",
   "name": "drawFilmDirectoryPage",
   "summary": "Draws one page of the film directory menu: clears the menu window, prints the centred title 'SAVE GAME FILM' ($CC66) or 'LOAD GAME FILM' ($CC75) depending on filmMenuMode, then from the page pointer (setFilmDirectoryPagePtr) lists 10 entries as right-aligned slot number + space + 16-character name, then ' (MORE)' ($EF81) when a data disk is in use and ' (EXIT)' ($CAB2). Sets up the selector: menuItemCount 11 or 12, window left $0B width $12, menuFirstRow 9, menuSelectedItem 0, colour $60.",
   "inputs": "D_7FA1, D_7FA2, D_92F0, directory at $0300",
   "outputs": "screen text, D_91D4, D_91D5=0, D_91D6=9, zp_3B=$0B, zp_3C=$12, zp_3D=$60, zp_48/zp_49, D_90F8 and D_9220 used as counters",
   "confidence": "high"
  },
  {
   "addr": "7F3F",
   "name": "runLoadFilmMenu",
   "summary": "LOAD GAME FILM: runs the menu selector (sub_5FD1, C=1) on the directory page. Item 10 with a data disk = next page (nextFilmDirectoryPage, then restart); item 10/11 otherwise = exit. For a slot 0-9: suspends the comm module, converts the slot to a track (filmSlotToTrack), reads the track's 16 sectors one at a time into $0200 and copies each page under the I/O area to $D000-$DFFF (copyPageUnderIo with patched pointers $58C3/$58C6), resumes the comm module and validates the film (checkFilmInMemory); a valid film clears the message queue.",
   "inputs": "D_91D5, D_92F0, D_7FA1, disk",
   "outputs": "$D000-$DFFF = film, D_91D5 = track, zp_18/zp_19, D_58C3/4, D_58C6/7",
   "confidence": "high"
  },
  {
   "addr": "7FA6",
   "name": "setFilmDirectoryPagePtr",
   "summary": "zp_48/zp_49 = $0300 + filmDirectoryPage * $A0 (10 entries of 16 bytes per page) and stores the page's first slot number (filmPageFirstSlotTable: 1, 11, 21) in $90F8 for the numbering loop.",
   "inputs": "D_7FA1, D_7FA3",
   "outputs": "zp_48/zp_49, D_90F8",
   "confidence": "high"
  },
  {
   "addr": "7FCB",
   "name": "nextFilmDirectoryPage",
   "summary": "Advances filmDirectoryPage modulo 3 and redraws the page (jumps to drawFilmDirectoryPage).",
   "inputs": "D_7FA1",
   "outputs": "D_7FA1, redrawn page",
   "confidence": "high"
  },
  {
   "addr": "7FDD",
   "name": "runSaveFilmMenu",
   "summary": "SAVE GAME FILM slot picker with inline name entry - a variant of the generic menu selector sub_5FD1. Highlights the current slot; while a slot 0-9 is selected keys are fed to the text-entry handler sub_C7D5 (max 16 chars into the line buffer $9278, length in $92E6) and the typed name is echoed on the slot's row with a cursor block ($11 in screen RAM). Joystick up/down moves the bar (with auto-repeat via setJoystickRepeatDelay), which discards the typed text. Fire or RETURN accepts: item 10 on a data disk = next page and restart, otherwise exit (EXIT); for a slot with typed text the name is copied into the 16-byte directory entry (space padded, bit 7 on the last byte), the row is redrawn and saveFilmToSlot writes directory and film.",
   "inputs": "D_91D4, D_91D5, D_91D6, D_7FA1, keyboard/joystick (D_90EA, zp_5D), zp_6C/zp_6E timers",
   "outputs": "Y = D_91D5 (track after a save), D_92E6=0, D_92C5, D_9278, directory entry, disk; zp_4A/zp_4B = entry pointer",
   "confidence": "high"
  },
  {
   "addr": "810C",
   "name": "printFilmSlotEntry",
   "summary": "Positions the text cursor on the current menu row (menuFirstRow + menuSelectedItem, column 0) with colour A, clears the line and prints a space followed by the entry text: the 16-byte name at zp_4A/zp_4B for slots 0-9, ' (MORE)' for item 10 on a data disk, else ' (EXIT)'.",
   "inputs": "A = text colour, D_91D5, D_91D6, D_92F0, zp_4A/zp_4B",
   "outputs": "screen text, zp_3D, zp_3F=0",
   "confidence": "high"
  },
  {
   "addr": "8143",
   "name": "filmSlotToTrack",
   "summary": "Converts the selected directory slot into a disk track: page*10 + item + 2, plus 1 when the result is >= 18 so the directory track is skipped (slots 0-29 -> tracks 2-17 and 19-32). The track replaces menuSelectedItem ($91D5) and is returned in A.",
   "inputs": "D_7FA1, D_91D5",
   "outputs": "A = D_91D5 = track",
   "confidence": "high"
  },
  {
   "addr": "815E",
   "name": "saveFilmToSlot",
   "summary": "Suspends the comm module, converts the slot to a track, rewrites the directory (writeFilmDirectory) and then copies the 16 pages of the film at $D000-$DFFF (RAM under I/O) one at a time to $0200 and writes each as sector 0-15 of the track with writeSectors. Finally resumes the comm module.",
   "inputs": "D_91D5 slot, film at $D000",
   "outputs": "track sectors 0-15 written, D_91D5 = track, zp_18/zp_19, D_58C3/4, D_58C6/7",
   "confidence": "high"
  },
  {
   "addr": "81A5",
   "name": "promptInsertGameDisk",
   "summary": "Patches 'GAME' ($EFD4) into the message 'INSERT xxxx DISK AND PRESS SPACE.' (promptInsertDiskAndWaitSpace), then waitForGameDiskInserted and resumeCommAfterDiskAccess. End of the save/load film flow.",
   "inputs": "none",
   "outputs": "game disk present, comm module resumed",
   "confidence": "high"
  },
  {
   "addr": "81B2",
   "name": "promptInsertDataDisk",
   "summary": "Entry of promptInsertDiskAndWaitSpace with A/Y = $EFD0 ('DATA').",
   "inputs": "none",
   "outputs": "see promptInsertDiskAndWaitSpace",
   "confidence": "high"
  },
  {
   "addr": "81B6",
   "name": "promptInsertDiskAndWaitSpace",
   "summary": "Copies the 4-character disk name at A/Y into the message template at $EFB6 ('INSERT DATA DISK AND PRESS SPACE.' at $EFAF, in the comm-module tail), installs that string as message $1E (sub_C924), shows it (statusMessageId $92A1 = $1E, sub_C566), waits for the space key while servicing the link, clicks, clears the message id and erases the message line (sub_C39F).",
   "inputs": "A/Y = address of a 4-byte name ('DATA' $EFD0 or 'GAME' $EFD4)",
   "outputs": "D_EFB6..D_EFB9 patched, message slot $1E, D_92A1, D_9248",
   "confidence": "high"
  },
  {
   "addr": "81F4",
   "name": "checkFilmInMemory",
   "summary": "Validates the film header at $D000 with sub_58DE (checksum of bytes 0-$32 in byte $33 and version byte $2F == $0B94). Valid: C=0. Invalid: queues message 9 'NOT A GAME FILM!', waits about 4 seconds (waitFramesServicingComm) and returns C=1.",
   "inputs": "film header at $D000",
   "outputs": "C flag, zp_BC/zp_BD = $D000, A = header byte 4 when valid",
   "confidence": "high"
  },
  {
   "addr": "81FE",
   "name": "waitFramesServicingComm",
   "summary": "Sets joyRepeatTimer zp_6C to $F0 and calls serviceCommandExchange until the IRQ has counted it down (about 4 seconds), then returns C=1. Also called from the main program's queue flush at $563C (that call lands in different code when overlay B is resident).",
   "inputs": "none",
   "outputs": "C=1, zp_6C=0",
   "confidence": "high"
  },
  {
   "addr": "820B",
   "name": "beginGameTypeSelection",
   "summary": "Entry used by the side that picks the game ($79A1, side 0): clears the message queue, sets currentScreen to 0, clears the CUSTOM-ON message id patch ($868D) and installs 'CUSTOM OFF' ($EFD8) as message $1E, then falls into runGameTypeMenu.",
   "inputs": "none",
   "outputs": "D_90FB=0, message slot $1E, $868D=0",
   "confidence": "high"
  },
  {
   "addr": "821F",
   "name": "runGameTypeMenu",
   "summary": "Game-type selection screen. Points the CUSTOM message patch at $EFDF, derives customModeFlag ($92F3 bit 7) from $0BA3 bit 6, clears and redraws the screen, resets the selection (resetSelectionAndView), clears the info panel; in solo-trainer mode toggles the display set (sub_EEDD) and with custom rules shows the CUSTOM status (sub_8673). Prints 'GAME TYPE:' ($CCBD) and the 7 type names ($CCC8..) and runs the selector starting at the current type. The choice selects a preset from gameTypePresetTable ($78CA): with custom rules the preset (or the unchanged $0BA3 when the same type is re-chosen) gets bit 6 and two ON/OFF sub-menus follow ('UNIT MENUS' -> bit 3, 'DAMAGE' -> bit 4 via sub_860B). Continues in runMapTypeMenu. Also reached by JMP from $2E07 (screen redraw, relevant to the overlay-B routine at this address).",
   "inputs": "D_0BA3, D_0BA5, D_92F3, menu input",
   "outputs": "D_0BA3, D_92F3, D_91D4=7, D_91D5, zp_B1=$BF (custom path), zp_3D/zp_42=9; self-modifies $829E, $8319, $869A/$869B",
   "confidence": "high"
  },
  {
   "addr": "8279",
   "name": "printGameTypeNamesLoop",
   "summary": "Not a routine in variant A: loop body of runGameTypeMenu that prints the next type name (sub_C137) and a newline 7 times (zp_18 counts down). The label exists because variant B has a routine at $8279.",
   "inputs": "zp_18",
   "outputs": "screen text",
   "confidence": "high"
  },
  {
   "addr": "82A4",
   "name": "applyCustomPresetTail",
   "summary": "Not a routine in variant A: continuation inside runGameTypeMenu that ORs the custom bit $40 into the chosen preset, stores it in $0BA3 and runs the UNIT MENUS and DAMAGE toggles. Variant B has a routine at this address.",
   "inputs": "A = preset",
   "outputs": "D_0BA3, zp_B1=$BF",
   "confidence": "high"
  },
  {
   "addr": "82BF",
   "name": "runMapTypeMenu",
   "summary": "Second half of the game setup menus: resets the selection, clears the info panel, prints 'MAP TYPE' ($EF77) with the items STANDARD/CUSTOM ($CC11) preselected from $0BA3 bit 7, and runs the selector; the result becomes $0BA3 bit 7. Then sends the game type to the opponent (sendGameTypeCommand $A4), copies the scenario layout for the type (sub_ECF3), sets gamePhase zp_B1=$FF and playsFirstFlag=1 when $0BA3 changed against pendingGameTypeOptions ($0BA4). CUSTOM map -> jumps to the map editor at L_76AD. STANDARD map -> derives mapSeed ($0B95-$0B97) from the RNG state zp_57-zp_59 (low 5 bits forced to 1 for symmetric types; for QB SNEAK/DEFENDER only when this side plays first). The main program also calls $82BF from resolveAttack ($3A30), which targets the overlay-B routine at this address.",
   "inputs": "D_0BA3, D_0BA4, D_0BA1, zp_57-zp_59, menu input",
   "outputs": "D_0BA3, D_0BA1, D_0B95-D_0B97, zp_B1=$FF, zp_73, command $A4 queued",
   "confidence": "high"
  },
  {
   "addr": "833C",
   "name": "sendGameTypeCommand",
   "summary": "Stores A in cmdParam0 (zp_73) and queues command $A4 with that single parameter (sub_561F) - tells the opponent the chosen gameTypeOptions byte.",
   "inputs": "A = game type options byte",
   "outputs": "zp_73, command queued",
   "confidence": "high"
  },
  {
   "addr": "8343",
   "name": "printMenuTitle",
   "summary": "Sets menuSelectedItem to X (the preselected item), prints the string at A/Y centred in the current window (sub_C121), sets the window left column to $1C, positions the cursor and records the cursor row as menuFirstRow ($91D6).",
   "inputs": "X = initial item, A/Y = title string",
   "outputs": "D_91D5, D_91D6, zp_3B=$1C",
   "confidence": "high"
  },
  {
   "addr": "8356",
   "name": "runMenuWidth10",
   "summary": "Runs the menu selector with a highlight width of 10 (falls into runMenuWithWidth).",
   "inputs": "menu variables",
   "outputs": "A = D_91D5 = chosen item",
   "confidence": "high"
  },
  {
   "addr": "8358",
   "name": "runMenuWithWidth",
   "summary": "Sets the text window width zp_3C to A, moves the left column one to the left, and runs the generic selector sub_5FD1 with C=0; returns the chosen item in A (from $91D5).",
   "inputs": "A = width, D_91D4, D_91D5, D_91D6",
   "outputs": "A = D_91D5, zp_3B--, zp_3C",
   "confidence": "high"
  },
  {
   "addr": "8364",
   "name": "resetSelectionAndView",
   "summary": "Clears the selection state for the setup menus: unitDrawOverride $9242 = $FE, selectedUnit $90F8 = $FE (none), currentScreen $90FB = 0, and moves the view origin zp_A5/zp_A6 to (45,45), i.e. completely off the 40x40 map so the viewport is blank.",
   "inputs": "none",
   "outputs": "D_9242, D_90F8, D_90FB, zp_A5, zp_A6",
   "confidence": "high"
  },
  {
   "addr": "8378",
   "name": "chooseRecycler",
   "summary": "Recycler setup (called from the options flow at $85B6 for game types >= 4). In solo mode both recycler modes are forced to 2 (NONE). If recyclerMenuEnabled ($8450) is set, shows the 'RECYCLER' menu with FULL/HALF/NONE (printOptionWord) preselected from recyclerMode[side]; otherwise keeps the current mode and returns immediately when it is NONE. NONE sends command $9C with column/row $FF. Otherwise prints 'LOCATING'/'RECYCLER', homes the view (sub_EC1C), shows the cursor and viewport and lets the player pick a map cell (runTargetCursorLoop with cursorTargetMode=$FF) until a cell whose terrain is blank ($40, looking under a unit if one stands there) is chosen; then hides sprites, resets the view and sends command $9C (mode, col, row) via sub_560A.",
   "inputs": "D_0BA5, D_0B9F, D_92A6, D_8450, player input, D_91D8/D_91D9",
   "outputs": "D_92A6 (solo), D_91D5, zp_73-zp_75, command $9C queued, D_9022=0, zp_BB=$FF; self-modifies $8433",
   "confidence": "high"
  },
  {
   "addr": "843B",
   "name": "printOptionWordLine",
   "summary": "Prints option word A (0 FULL, 1 HALF, 2 NONE via printOptionWord) followed by a newline.",
   "inputs": "A = option index",
   "outputs": "screen text",
   "confidence": "high"
  },
  {
   "addr": "8441",
   "name": "clearInfoPanelSetRow",
   "summary": "Sets text colour zp_3D and message colour zp_42 to 8, clears the info panel (clearInfoPanel) and sets the text cursor to row A (sub_C097).",
   "inputs": "A = cursor row",
   "outputs": "zp_3D=zp_42=8, zp_40=A, cursor positioned",
   "confidence": "high"
  }
 ],
 "variables": [
  {
   "addr": "18",
   "scope": "zp",
   "name": "tempByte18",
   "meaning": "scratch: player loop index in applyGameTypeDefaults, unit index for placeAllUnitsOnMap, sector counter in the film save/load loops, name length copy, type-name counter",
   "confidence": "high"
  },
  {
   "addr": "19",
   "scope": "zp",
   "name": "tempByte19",
   "meaning": "scratch: disk track of the film slot during save/load",
   "confidence": "high"
  },
  {
   "addr": "3B",
   "scope": "zp",
   "name": "textWindowLeft",
   "meaning": "text window left column ($C000 text engine)",
   "confidence": "high"
  },
  {
   "addr": "3C",
   "scope": "zp",
   "name": "textWindowWidth",
   "meaning": "text window width; also the width of the menu highlight bar",
   "confidence": "high"
  },
  {
   "addr": "3D",
   "scope": "zp",
   "name": "textColour",
   "meaning": "colour byte for printed text / menu highlight EOR",
   "confidence": "high"
  },
  {
   "addr": "3F",
   "scope": "zp",
   "name": "textCursorCol",
   "meaning": "text cursor column relative to the window",
   "confidence": "high"
  },
  {
   "addr": "40",
   "scope": "zp",
   "name": "textCursorRow",
   "meaning": "text cursor row",
   "confidence": "high"
  },
  {
   "addr": "42",
   "scope": "zp",
   "name": "messageTextColour",
   "meaning": "text colour used while a message is displayed",
   "confidence": "high"
  },
  {
   "addr": "48",
   "scope": "zp",
   "name": "genericPtr",
   "meaning": "pointer (lo): defaults table row in applyGameTypeDefaults, film directory page in the film menus, map cell pointer from readMapCell",
   "confidence": "high"
  },
  {
   "addr": "49",
   "scope": "zp",
   "name": "genericPtrHi",
   "meaning": "high byte of genericPtr",
   "confidence": "high"
  },
  {
   "addr": "4A",
   "scope": "zp",
   "name": "filmEntryPtr",
   "meaning": "pointer (lo) to the selected 16-byte film directory entry in runSaveFilmMenu/printFilmSlotEntry",
   "confidence": "high"
  },
  {
   "addr": "4B",
   "scope": "zp",
   "name": "filmEntryPtrHi",
   "meaning": "high byte of filmEntryPtr",
   "confidence": "high"
  },
  {
   "addr": "57",
   "scope": "zp",
   "name": "rngState0",
   "meaning": "game RNG state byte 0; copied (with low 5 bits set) into mapSeed by runMapTypeMenu",
   "confidence": "high"
  },
  {
   "addr": "58",
   "scope": "zp",
   "name": "rngState1",
   "meaning": "game RNG state byte 1 -> mapSeed byte 1",
   "confidence": "high"
  },
  {
   "addr": "59",
   "scope": "zp",
   "name": "rngState2",
   "meaning": "game RNG state byte 2 -> mapSeed byte 2",
   "confidence": "high"
  },
  {
   "addr": "5D",
   "scope": "zp",
   "name": "joyDirection",
   "meaning": "joystick direction bits (active low, $0F centred) polled by the IRQ",
   "confidence": "high"
  },
  {
   "addr": "6C",
   "scope": "zp",
   "name": "joyRepeatTimer",
   "meaning": "frame countdown gating joystick auto-repeat in the menus; also the 4-second wait of waitFramesServicingComm",
   "confidence": "high"
  },
  {
   "addr": "6E",
   "scope": "zp",
   "name": "menuBlinkTimer",
   "meaning": "frame countdown (20) used to blink the menu highlight bar",
   "confidence": "high"
  },
  {
   "addr": "73",
   "scope": "zp",
   "name": "cmdParam0",
   "meaning": "first parameter of an outgoing command (game type for $A4, recycler mode for $9C)",
   "confidence": "high"
  },
  {
   "addr": "74",
   "scope": "zp",
   "name": "cmdParam1",
   "meaning": "second command parameter (recycler column for $9C)",
   "confidence": "high"
  },
  {
   "addr": "75",
   "scope": "zp",
   "name": "cmdParam2",
   "meaning": "third command parameter (recycler row for $9C)",
   "confidence": "high"
  },
  {
   "addr": "A5",
   "scope": "zp",
   "name": "viewOriginCol",
   "meaning": "map column of the view's left edge (45 = off map in resetSelectionAndView)",
   "confidence": "high"
  },
  {
   "addr": "A6",
   "scope": "zp",
   "name": "viewOriginRow",
   "meaning": "map row of the view's top edge",
   "confidence": "high"
  },
  {
   "addr": "B1",
   "scope": "zp",
   "name": "gamePhase",
   "meaning": "0 = battle; nonzero setup/menu phases ($BF custom setup, $FF after the map is chosen)",
   "confidence": "medium"
  },
  {
   "addr": "B6",
   "scope": "zp",
   "name": "screenCellPtr",
   "meaning": "screen RAM pointer of the current text cell; runSaveFilmMenu writes the cursor block $11 through it",
   "confidence": "high"
  },
  {
   "addr": "BB",
   "scope": "zp",
   "name": "cursorTargetMode",
   "meaning": "$FF = free map-cell pick for runTargetCursorLoop (recycler placement)",
   "confidence": "high"
  },
  {
   "addr": "BC",
   "scope": "zp",
   "name": "ioRamPtr",
   "meaning": "pointer (lo) used by sub_5874/sub_5893 to read/write RAM under the I/O area; $D000 = film header",
   "confidence": "high"
  },
  {
   "addr": "BD",
   "scope": "zp",
   "name": "ioRamPtrHi",
   "meaning": "high byte of ioRamPtr",
   "confidence": "high"
  },
  {
   "addr": "0200",
   "scope": "abs",
   "name": "bamBuffer",
   "meaning": "BAM sector read by checkDiskId; a data disk must have bytes $0203-$028F all zero",
   "confidence": "high"
  },
  {
   "addr": "0300",
   "scope": "abs",
   "name": "filmDirectory",
   "meaning": "$0300-$04DF: 30 film names of 16 bytes (3 pages of 10), loaded from / written to track 18 sectors 2-3",
   "confidence": "high"
  },
  {
   "addr": "0500",
   "scope": "abs",
   "name": "messagePtrTableLo",
   "meaning": "low bytes of the status message string pointers (message $1E is patched dynamically here)",
   "confidence": "high"
  },
  {
   "addr": "0524",
   "scope": "abs",
   "name": "messagePtrTableHi",
   "meaning": "high bytes of the status message string pointers",
   "confidence": "high"
  },
  {
   "addr": "0B7D",
   "scope": "abs",
   "name": "inputLockoutTimer",
   "meaning": "frame countdown during which the IRQ ignores input; 30 after waitForSpaceKey",
   "confidence": "high"
  },
  {
   "addr": "0B94",
   "scope": "abs",
   "name": "filmFormatVersion",
   "meaning": "byte compared with film header byte $2F by sub_58DE",
   "confidence": "medium"
  },
  {
   "addr": "0B95",
   "scope": "abs",
   "name": "mapSeed",
   "meaning": "3-byte map generator seed ($0B95-$0B97); film header bytes $2B-$2D",
   "confidence": "high"
  },
  {
   "addr": "0B9B",
   "scope": "abs",
   "name": "filmPlaybackFlag",
   "meaning": "bit 7 = play back the film in $D000 ($82 set by WATCH GAME FILM: bits 0-1 = view both sides)",
   "confidence": "high"
  },
  {
   "addr": "0B9D",
   "scope": "abs",
   "name": "mapOrientationFlag",
   "meaning": "0/1: side for which the map at $F000 is currently oriented; rotated 180 degrees (sub_EC58) when it differs from playerSide; film header byte $2E bit 0",
   "confidence": "medium"
  },
  {
   "addr": "0B9F",
   "scope": "abs",
   "name": "playerSide",
   "meaning": "local side 0/1; film header byte 5",
   "confidence": "high"
  },
  {
   "addr": "0BA1",
   "scope": "abs",
   "name": "playsFirstFlag",
   "meaning": "nonzero = this side plays first; film header byte $2E bits 1-2",
   "confidence": "high"
  },
  {
   "addr": "0BA3",
   "scope": "abs",
   "name": "gameTypeOptions",
   "meaning": "bits 0-2 game type, bit 3 UNIT MENUS option, bit 4 DAMAGE option, bit 6 custom rules, bit 7 custom map; film header byte $0C",
   "confidence": "high"
  },
  {
   "addr": "0BA4",
   "scope": "abs",
   "name": "pendingGameTypeOptions",
   "meaning": "previous/pending copy of gameTypeOptions; a difference sets playsFirstFlag",
   "confidence": "high"
  },
  {
   "addr": "0BA5",
   "scope": "abs",
   "name": "soloTrainerFlag",
   "meaning": "bit 7 set = PRACTICE WITH SOLO TRAINER (comm module build 1 from track 35 + its $EC00 extension from track 29 act as the AI opponent); 0 = COMPETE WITH MODEM OPPONENT; film header byte 4",
   "confidence": "high"
  },
  {
   "addr": "0BA8",
   "scope": "abs",
   "name": "noGameInProgress",
   "meaning": "0 while a game runs (cleared by startFilmPlayback)",
   "confidence": "high"
  },
  {
   "addr": "0BA9",
   "scope": "abs",
   "name": "settingsText",
   "meaning": "6-byte settings text; film header bytes 6-$0B; sub_86C3 writes CUSTOM into the $0332 copy",
   "confidence": "medium"
  },
  {
   "addr": "58C3",
   "scope": "abs",
   "name": "copySrcPtr",
   "meaning": "self-modified source operand of copyPageUnderIo sub_58B2 (lo/hi at $58C3/$58C4)",
   "confidence": "high"
  },
  {
   "addr": "58C6",
   "scope": "abs",
   "name": "copyDstPtr",
   "meaning": "self-modified destination operand of copyPageUnderIo (lo/hi at $58C6/$58C7)",
   "confidence": "high"
  },
  {
   "addr": "7FA1",
   "scope": "abs",
   "name": "filmDirectoryPage",
   "meaning": "current film directory page 0-2 (10 slots each)",
   "confidence": "high"
  },
  {
   "addr": "7FA2",
   "scope": "abs",
   "name": "filmMenuMode",
   "meaning": "0 = SAVE GAME FILM, 1 = LOAD GAME FILM (main-menu item - 3)",
   "confidence": "high"
  },
  {
   "addr": "8450",
   "scope": "abs",
   "name": "recyclerMenuEnabled",
   "meaning": "nonzero makes chooseRecycler show the FULL/HALF/NONE menu (set by the options editor at $8564/$859C)",
   "confidence": "medium"
  },
  {
   "addr": "892C",
   "scope": "abs",
   "name": "sideDefaultsTableSide0",
   "meaning": "5 rows x 5 columns (column = game-type class): terrain points, recycler mode, comcen speed class, drones, missiles for side 0; in the $8800 strings overlay",
   "confidence": "high"
  },
  {
   "addr": "8945",
   "scope": "abs",
   "name": "sideDefaultsTableSide1",
   "meaning": "same layout as $892C for side 1",
   "confidence": "high"
  },
  {
   "addr": "90F8",
   "scope": "abs",
   "name": "selectedUnit",
   "meaning": "selected unit index ($FE = none); reused as the slot-number counter by drawFilmDirectoryPage",
   "confidence": "high"
  },
  {
   "addr": "90FB",
   "scope": "abs",
   "name": "currentScreen",
   "meaning": "active screen id (0 = battlefield)",
   "confidence": "high"
  },
  {
   "addr": "90FD",
   "scope": "abs",
   "name": "unitProcessOrder",
   "meaning": "100-byte unit processing order list",
   "confidence": "high"
  },
  {
   "addr": "9164",
   "scope": "abs",
   "name": "unitBaseOrder",
   "meaning": "100-byte base unit order list",
   "confidence": "high"
  },
  {
   "addr": "91CB",
   "scope": "abs",
   "name": "gameClock",
   "meaning": "game length in rounds (from gameClockByTypeTable)",
   "confidence": "high"
  },
  {
   "addr": "91D4",
   "scope": "abs",
   "name": "menuItemCount",
   "meaning": "number of items for the menu selector",
   "confidence": "high"
  },
  {
   "addr": "91D5",
   "scope": "abs",
   "name": "menuSelectedItem",
   "meaning": "current/selected menu item; filmSlotToTrack overwrites it with the disk track",
   "confidence": "high"
  },
  {
   "addr": "91D6",
   "scope": "abs",
   "name": "menuFirstRow",
   "meaning": "screen row of the first menu item",
   "confidence": "high"
  },
  {
   "addr": "91D8",
   "scope": "abs",
   "name": "pickedCellX",
   "meaning": "map column returned by runTargetCursorLoop (recycler position)",
   "confidence": "high"
  },
  {
   "addr": "91D9",
   "scope": "abs",
   "name": "pickedCellY",
   "meaning": "map row returned by runTargetCursorLoop",
   "confidence": "high"
  },
  {
   "addr": "9022",
   "scope": "abs",
   "name": "spriteEnableShadow",
   "meaning": "shadow of VIC $D015 (cleared after the recycler is placed)",
   "confidence": "high"
  },
  {
   "addr": "9220",
   "scope": "abs",
   "name": "loopCounter9220",
   "meaning": "generic down counter (10 directory entries in drawFilmDirectoryPage)",
   "confidence": "medium"
  },
  {
   "addr": "9242",
   "scope": "abs",
   "name": "unitDrawOverride",
   "meaning": "$FE written by resetSelectionAndView",
   "confidence": "medium"
  },
  {
   "addr": "9248",
   "scope": "abs",
   "name": "lastKeyCode",
   "meaning": "last key pressed, $A0 = space, $FF = none",
   "confidence": "high"
  },
  {
   "addr": "9277",
   "scope": "abs",
   "name": "chatInputLength",
   "meaning": "chat input length, cleared by waitForSpaceKey",
   "confidence": "medium"
  },
  {
   "addr": "9278",
   "scope": "abs",
   "name": "textInputBuffer",
   "meaning": "line buffer edited by the key handler sub_C7D5 (terminator $9E); holds the film name in runSaveFilmMenu",
   "confidence": "high"
  },
  {
   "addr": "929C",
   "scope": "abs",
   "name": "chatState",
   "meaning": "chat typing state, cleared by waitForSpaceKey",
   "confidence": "medium"
  },
  {
   "addr": "92A1",
   "scope": "abs",
   "name": "statusMessageId",
   "meaning": "id of the status message on screen ($1E = dynamic slot used for the INSERT DISK / CUSTOM prompts, 0 = none)",
   "confidence": "high"
  },
  {
   "addr": "92A6",
   "scope": "abs",
   "name": "recyclerMode",
   "meaning": "per side: 0 FULL, 1 HALF, 2 NONE",
   "confidence": "high"
  },
  {
   "addr": "92A8",
   "scope": "abs",
   "name": "recyclerCol",
   "meaning": "per side map column of the recycler, bit 7 = none",
   "confidence": "high"
  },
  {
   "addr": "92AA",
   "scope": "abs",
   "name": "recyclerRow",
   "meaning": "per side map row of the recycler",
   "confidence": "high"
  },
  {
   "addr": "92AC",
   "scope": "abs",
   "name": "terrainPointsBySide",
   "meaning": "per side TERRAIN PTS option (0-2, defaults table row 0)",
   "confidence": "high"
  },
  {
   "addr": "92AE",
   "scope": "abs",
   "name": "droneCount",
   "meaning": "2 bytes (own, opponent): DRONES option (defaults table row 3; THE BOMB adds one)",
   "confidence": "high"
  },
  {
   "addr": "92B0",
   "scope": "abs",
   "name": "missileCount",
   "meaning": "2 bytes (own, opponent): MISSILES option (defaults table row 4)",
   "confidence": "high"
  },
  {
   "addr": "92B4",
   "scope": "abs",
   "name": "comcenSpeedClass",
   "meaning": "2 bytes (own, opponent): COMCEN SPEED class (defaults table row 2, later mapped through $78BF)",
   "confidence": "high"
  },
  {
   "addr": "92C5",
   "scope": "abs",
   "name": "menuHighlightOn",
   "meaning": "1 while the menu highlight bar is drawn (toggled by sub_60EA)",
   "confidence": "high"
  },
  {
   "addr": "92C6",
   "scope": "abs",
   "name": "setupPhaseMode",
   "meaning": "from setupPhaseModeTable: 0 = no unit setup phase (SCRIMAGE, QB SNEAK), 1 = both sides set up (THE BOMB..FULL WAR), $80 = only side 0 sets up (DEFENDER); tested by $C470",
   "confidence": "medium"
  },
  {
   "addr": "92E6",
   "scope": "abs",
   "name": "filmNameLength",
   "meaning": "number of characters typed into textInputBuffer for the film name (0 = keep the old name)",
   "confidence": "high"
  },
  {
   "addr": "92F0",
   "scope": "abs",
   "name": "diskIdIndex",
   "meaning": "0 after the game disk was identified, 2 after a data disk (enables the 3-page film directory)",
   "confidence": "high"
  },
  {
   "addr": "92F3",
   "scope": "abs",
   "name": "customModeFlag",
   "meaning": "bit 7 = custom rules active ($0BA3 bit 6 at menu entry, toggled by '*' during setup, shown as CUSTOM ON/OFF)",
   "confidence": "medium"
  },
  {
   "addr": "92FA",
   "scope": "abs",
   "name": "lastQuarterFlag",
   "meaning": "$FF written by startFilmPlayback",
   "confidence": "low"
  },
  {
   "addr": "92FC",
   "scope": "abs",
   "name": "revealAllUnitsFlag",
   "meaning": "bit 7 = show all units; cleared at film start",
   "confidence": "high"
  },
  {
   "addr": "92FD",
   "scope": "abs",
   "name": "defaultMainMenuItem",
   "meaning": "initial main-menu item; set to 2 (WATCH GAME FILM) after a film save/load",
   "confidence": "high"
  },
  {
   "addr": "92FE",
   "scope": "abs",
   "name": "linkTypeAtStart",
   "meaning": "copy of soloTrainerFlag at game start; film header byte 4",
   "confidence": "high"
  },
  {
   "addr": "EFB6",
   "scope": "abs",
   "name": "insertDiskNameField",
   "meaning": "4 characters inside 'INSERT DATA DISK AND PRESS SPACE.' ($EFAF, comm-module tail from track 34) overwritten with DATA/GAME",
   "confidence": "high"
  },
  {
   "addr": "F76C",
   "scope": "abs",
   "name": "unitTypeTable",
   "meaning": "per-unit type byte, $FF = unit absent (set for removed units)",
   "confidence": "high"
  },
  {
   "addr": "F79D",
   "scope": "abs",
   "name": "unitTypeComcen0",
   "meaning": "unitTypeTable entry of unit 49 (side 0 COMCEN), restored to type 4 in THE BOMB",
   "confidence": "high"
  },
  {
   "addr": "FA70",
   "scope": "abs",
   "name": "unitUnderCellAlias",
   "meaning": "unitUnderCell indexed by map byte ($80|unit)",
   "confidence": "high"
  },
  {
   "addr": "FAF0",
   "scope": "abs",
   "name": "unitUnderCell",
   "meaning": "per-unit terrain byte under the unit, $FF = absent",
   "confidence": "high"
  },
  {
   "addr": "FFDE",
   "scope": "abs",
   "name": "playerNames",
   "meaning": "2 x 10 bytes ($FFDE own name from the BAM, $FFE8 opponent/trainer name); film header bytes $11-$24",
   "confidence": "medium"
  },
  {
   "addr": "D000",
   "scope": "abs",
   "name": "filmHeader",
   "meaning": "game film in RAM under I/O: header bytes 4 link type, 5 side, 6-$0B settings text, $0C game type options, $11-$24 names, $2B-$2D map seed, $2E orientation/plays-first, $2F version, $33 checksum of 0-$32, $34.. saved scores/unit state; 16 pages saved per slot",
   "confidence": "high"
  }
 ],
 "dataBlocks": [
  {
   "addr": "7C29",
   "length": 5,
   "type": "byteTable",
   "name": "setupPhaseModeTable",
   "description": "Indexed by the game-type class 0-4 of applyGameTypeDefaults: {0,0,1,1,$80} -> $92C6 (0 no setup phase, 1 both sides deploy, $80 only side 0 deploys)."
  },
  {
   "addr": "7FA1",
   "length": 1,
   "type": "byteTable",
   "name": "filmDirectoryPage",
   "description": "Variable: current film directory page (0-2)."
  },
  {
   "addr": "7FA2",
   "length": 1,
   "type": "byteTable",
   "name": "filmMenuMode",
   "description": "Variable: 0 = save film, 1 = load film."
  },
  {
   "addr": "7FA3",
   "length": 3,
   "type": "byteTable",
   "name": "filmPageFirstSlotTable",
   "description": "{1,11,21}: slot number printed for the first entry of each directory page."
  }
 ],
 "misclassified": [],
 "insights": [
  "Chunk boundaries: the spec's line range 2026-2895 does not match the address range $7C29-$844E (file lines 2080-3198); this survey covers the address range $7C29-$844E.",
  "Game films: a recorded game lives in the 4 KB of RAM under the I/O area at $D000-$DFFF (accessed with $01=$34 by sub_5874/sub_5893/sub_58B2). Header layout as written by $7A93-$7AD7 and read back by startFilmPlayback: +4 soloTrainerFlag, +5 playerSide, +6..+$0B settings text, +$0C gameTypeOptions, +$11..+$24 the two 10-byte player names ($FFDE/$FFE8), +$2B..+$2D mapSeed, +$2E = playsFirst<<1 | mapOrientation, +$2F format byte (must equal $0B94), +$33 checksum of bytes 0-$32, +$34.. scores/unit state restored by sub_EE4A.",
  "Film disk format: track 18 sectors 2-3 ($0300-$04DF) hold a directory of 30 names x 16 bytes (3 pages of 10, bit 7 terminates a name). Slot n of page p is stored on track 2 + 10p + n (+1 when >= 18, so tracks 2-17 and 19-32), 16 sectors = the whole $D000-$DFFF block. On the game disk only page 0 (tracks 2-11, the $01 filler tracks) is offered; a data disk (id 'EA', BAM bytes $0203-$028F all zero) gets all three pages via the ' (MORE)' item. checkDiskId leaves diskIdIndex $92F0 = 2 for a data disk and the directory code keys the page count on it.",
  "Main menu items (strings at $CC1F..): 0 COMPETE WITH MODEM OPPONENT -> $0BA5=0; 1 PRACTICE WITH SOLO TRAINER -> $0BA5=$FF; 2 WATCH GAME FILM -> $0B9B=$82; 3 SAVE GAME FILM; 4 LOAD GAME FILM; 5 DISCONNECT/SET VOICE. Hence $0BA5 bit 7 selects the SOLO TRAINER: comm-module build 1 (track 35) is the AI opponent, not a null-modem driver, and overlay A additionally loads track 29 s0-3 to $EC00 ($7B24) only when that bit is set (AI extension that references the $8800-$89FF work area and unit tables).",
  "Game-type presets (gameTypePresetTable $78CA, 7 bytes): SCRIMAGE $00, QB SNEAK $01, THE BOMB $02, FACE-OFF $13, SLUGGERS $14, FULL WAR $1D, DEFENDER $1E -> $0BA3 (bit 3 UNIT MENUS, bit 4 DAMAGE). Game length per type (gameClockByTypeTable $78C3): 63,63,31,127,127,254,254 rounds. Per-side defaults come from two 5x5 tables at $892C/$8945 in the $8800 strings overlay (rows: TERRAIN PTS, RECYCLER, COMCEN SPEED, DRONES, MISSILES; columns: SCRIMAGE, QB SNEAK, BOMB/FACE-OFF/SLUGGERS, FULL WAR, DEFENDER). QB SNEAK removes units 0-6; THE BOMB leaves only the two comcens (units 49 and 99) plus drones/missiles.",
  "The label bootReceiveSectorData ($C0EF) is stale for the runtime $C000 overlay: there $C0EF is 'print A copies of the pad character zp_3E' (repeats sub_C0C0), which is what $7F12/$7F1E call with A=3.",
  "Status-message slot $1E is a dynamic slot: sub_C924(X=$1E, A/Y) points it at 'INSERT xxxx DISK AND PRESS SPACE.' ($EFAF, xxxx patched at $EFB6 with DATA $EFD0 or GAME $EFD4), 'CUSTOM OFF' ($EFD8, with OFF/ON patched at $EFDF) or 'DESTROY OFF' ($EFE2). These strings live in the comm-module tail loaded from track 34 s12-15 together with the option names MAP TYPE $EF77, (MORE) $EF81, LOCATING $EF89, RECYCLER $EF92, UNIT MENUS $EF9B, DAMAGE $EFA7. Message 9 = 'NOT A GAME FILM!'.",
  "sub_8279 and sub_82A4 are routine starts only in overlay variant B; in variant A they are mid-routine labels of runGameTypeMenu. Conversely the main program calls $81FE ($563C), $821F ($2E07) and $82BF ($3A30) which are routine starts in both variants with unrelated code - those main-program call sites are meant for the variant-B code (battle engine), while the variant-A routines at these addresses belong to the setup menus.",
  "Setup-menu conventions used throughout overlay A: printMenuTitle (X = preselected item, A/Y title) + menuItemCount + runMenuWithWidth wrap the generic selector sub_5FD1; resetSelectionAndView ($9242=$FE, $90F8=$FE, view origin (45,45)) blanks the viewport; commands $A4 (game type), $9C (recycler mode/col/row) and $A5 (numeric options, next chunk) are sent to the opponent with sub_561F/sub_560A/sub_560F."
 ]
}