modemwars/survey/game_ovl_6F00_A_7C29.json
2026-08-23 02:09:40 -05:00

1030 lines
No EOL
44 KiB
JSON
Generated

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