{
 "_comment": "generated by tools/mergeSurvey.py from survey/*.json - do not edit by hand",
 "notes": {
  "6F04": {
   "routine": [
    "generateBattlefieldMap - Entry point of the random battlefield generator (variant A of the $6F00 overlay). Derives the map-style flags at $032E from the game type in $0BA3 bits 0-2 (type 1 QB SNEAK -> $80, type 6 DEFENDER -> $40, else 0), sets $032D bit 7 when the low 5 bits of the map seed $0B95 are all set (= map code ending in '=', produces a point-symmetric map), shows status message 1 (sub_C853), blanks the 40x40 map at $F000 to $40 (sub_EC00), copies the 3-byte map seed $0B95-$0B97 into the scenario RNG state $5A-$5C (sub_EC2B) and falls into runMapGeneratorPasses. Called by manualLookupCheck ($09E0, to show the map the player must identify), by the map/setup screens of overlay A ($76AD, $76FF, $7A02, $7D23).",
    "In:  $0BA3 bits 0-2 game type, $0B95-$0B97 map seed, $EC00 sub-overlay (T34 s12-15) present, contour rule table at $0200 (T18 s15) present",
    "Out: $032D, $032E; map at $F000-$F63F filled; $5A-$5C RNG state; $0BA9-$0BAD map-id text; $0300-$032C generator parameters; clobbers $0400-$04EF and $9025-$9028"
   ],
   "unit": "game/mapGenerator6F00"
  },
  "6F36": {
   "routine": [
    "runMapGeneratorPasses - Body of the map generator (fall-through from generateBattlefieldMap; the JSR at $7125 listed in XREF belongs to variant B). Pass order: encode the seed in A/Y/X as the 5-character map id at $0BA9 (sub_7850); carve a river (sub_7548, tiles $55-$5C, marks a keep-clear zone with $80/$81); pick hill summit sites (pickHillSummitSites); paint the summits (paintHillSummits, tile $65); turn $80 markers back into $40 (sub_74FE); applyContourTiles($65) with $0324=0 draws the upper-slope contour $41-$54; grow lower hills $61 beside the slopes (growLowerHillsBesideSlopes); clear $81 markers; applyContourTiles($61) with $0324=$20 draws the lower contour $21-$34; place forest clusters (placeForestClusters, tiles $5D-$60). For symmetric maps ($032D bit 7) the top half is then mirrored onto the bottom half (sub_EC54) and, if the river crossed the centre ($032D bit 6), cells (19,19)/(19,20)/(20,20) are patched to $55/$58/$40 so the two river halves join. Ends with jmp sub_C6EF (clear the status message). serviceLinkWhileGenerating is called between passes.",
    "In:  A/Y/X = map seed bytes (from sub_EC2B), $032D, $032E, RNG $5A-$5C",
    "Out: battlefield map $F000-$F63F, $0BA9-$0BAD map id text, $0300-$032C, map cells $F30B/$F333/$F334"
   ],
   "unit": "game/mapGenerator6F00"
  },
  "6F91": {
   "routine": [
    "serviceLinkWhileGenerating - Keeps the two-player link alive during the long map generation: unless $0B9B bit 7 (solo/film mode) is set, and if zp_B1 bit 7 (non-battle phase flag) is set, jumps to serviceCommandExchange ($4DA9). Otherwise returns immediately. Called four times from runMapGeneratorPasses and from $773A.",
    "In:  $0B9B bit 7, zp_B1 bit 7",
    "Out: side effects of sub_4DA9 (message system / command exchange)",
    "(confidence: medium)"
   ],
   "unit": "game/mapGenerator6F00"
  },
  "6F9E": {
   "routine": [
    "pickHillSummitSites - Chooses how many hill summits to place in the top half of the map and where. Count = (rnd&3)+(rnd&1), re-rolled to 1..4 if it came out 1, forced to 4 for map style $40 (DEFENDER) and masked to 0/1 for map style $80 (QB SNEAK); stored in $0300. For each site a blob size 2..5 goes to $0307[i] (mirrored into $0307[count+i]) and four candidate rows 7..14 go to $0325/$031C[0..3]. findBlankSitesOnLanes then resolves the sites; afterwards $0300 is doubled, the candidate rows are mirrored to 39-row (sub_3FA3) and findBlankSitesOnLanes runs again for the bottom half, so every summit has a point-mirrored twin. Exits early (count 0) via L_7088 with $0300 = 0.",
    "In:  $032E bits 6/7, RNG",
    "Out: $0300 total site count, $0307[0..7] sizes, $0314[i] columns, $031C[i] rows, $0325[0..3] rows, $0329[0..3] lane order; zp_28-zp_2B, zp_18",
    "(confidence: medium)"
   ],
   "unit": "game/mapGenerator6F00"
  },
  "701C": {
   "routine": [
    "findBlankSitesOnLanes - Resolves site positions for entries zp_2A .. $0300-1: shuffles the four lane columns (shuffleLaneColumns) and, for each site, takes the next lane (zp_2B cycling 3..0) and the next candidate row (zp_29 cycling zp_28..0), tries up to 20 jittered columns (addRandomJitter, +/-6, must be < 40) until the map cell is blank ($40); if a row yields nothing it tries the other candidate rows and finally drops the site (dec $0300). Column goes to $0314[i], row to $031C[i]. Also entered by fall-through for the mirrored second pass. On exit $0300 = number of sites actually placed (zp_2A).",
    "In:  zp_2A first site index, $0300 requested count, zp_28 last row-candidate index, $0325[] candidate rows",
    "Out: $0314[]/$031C[] site coordinates, $0300 corrected count, $0329[] shuffled lanes, zp_1D/zp_1E/zp_1F/zp_29/zp_2B",
    "(confidence: medium)"
   ],
   "unit": "game/mapGenerator6F00"
  },
  "708E": {
   "routine": [
    "advanceLaneIndex - zp_2B = zp_2B - 1, wrapping from 0 back to 3 (cycles through the four lane columns in $0329).",
    "In:  zp_2B",
    "Out: zp_2B"
   ],
   "unit": "game/mapGenerator6F00"
  },
  "7097": {
   "routine": [
    "advanceRowCandidate - zp_29 = zp_29 - 1, wrapping from 0 back to zp_28 (cycles through the candidate rows in $0325).",
    "In:  zp_29, zp_28",
    "Out: zp_29"
   ],
   "unit": "game/mapGenerator6F00"
  },
  "70A0": {
   "routine": [
    "shuffleLaneColumns - Builds a random permutation of 0..3 in $9025-$9028 (identity, then four random swaps) and stores laneColumnTable[perm[y]] (6, 15, 25, 34) into $0329-$032C so hills are spread over the four vertical lanes of the map in random order.",
    "In:  RNG, laneColumnTable $6F00",
    "Out: $0329-$032C, $9025-$9028 clobbered, zp_18"
   ],
   "unit": "game/mapGenerator6F00"
  },
  "70D9": {
   "routine": [
    "paintHillSummits - For every site i < $0300 grows a blob of $0307[i] (2..5) cells of summit tile $65 (random variant $65/$66) at ($0314[i], $031C[i]) via growTerrainBlob with horizontal spread $C8/256 and vertical spread $20/256, $0304 = 0 (no 2x2 constraint). Does nothing when $0300 is 0.",
    "In:  $0300, $0307[], $0314[], $031C[]",
    "Out: map cells; $0301 = $65, $0304 = 0, zp_2A, zp_A7/zp_A8"
   ],
   "unit": "game/mapGenerator6F00"
  },
  "710A": {
   "routine": [
    "growLowerHillsBesideSlopes - Scans every interior cell (x,y in 1..38). For a blank cell whose N, W, E or S neighbour is an upper-slope contour tile (terrain class 5, tiles $41-$54) it grows, with probability 1/4 (N,S) or 1/2 (W,E) per neighbour, a 5-cell blob of lower-hill tile $61 ($61-$64 variants) from that cell (growTerrainBlob, spreads $40/$10). This widens each summit into a hill body before the second contour pass. Skips entirely when $0300 is 0 (no hills).",
    "In:  $0300, map, terrainClassTable $077E, RNG",
    "Out: map cells; $0301 = $61, $0304 = 0, zp_A7/zp_A8 scan position",
    "(confidence: medium)"
   ],
   "unit": "game/mapGenerator6F00"
  },
  "71B0": {
   "routine": [
    "growTerrainBlob - Generic random blob painter. Starting at (zp_A7, zp_A8) it paints blobTile $0301 (randomised through sub_7872) on up to A blank ($40) cells using a depth-first walk with an explicit stack at $0400 (col) / $0450 (row) / $04A0 (direction flags), max 80 entries. From each cell it tries N, W, E, S once each (flag bits 0-3 of zp_6B), accepting a neighbour when it is inside the map, blank and a random byte is below $91DB (vertical) or $91DA (horizontal); a new cell never grows back toward the origin row/column (bits preset from its position relative to zp_A7/zp_A8). With $0304 bit 6 set a cell is only painted if checkAlignedBlockForForest allows it. Returns when A cells are placed, the stack is exhausted or full. Note: overwrites $0400-$04EF, the page that also holds the copy-protection answer table.",
    "In:  A = cells to place, X = horizontal spread (0-255), Y = vertical spread, zp_A7/zp_A8 origin, $0301 tile, $0304 flags",
    "Out: map cells; $91DA/$91DB, zp_18, zp_1A, zp_6B, zp_A5/zp_A6, zp_B2 stack pointer, zp_B3 placed count, $0400-$04EF"
   ],
   "unit": "game/mapGenerator6F00"
  },
  "72F6": {
   "routine": [
    "checkAlignedBlockForForest - For the blob cell (zp_A5, zp_A6) looks at the 2x2-aligned block containing it (coordinates with bit 0 cleared) and returns C = 0 only if all four cells are terrain class 0 (blank) or 2 (forest $5D-$60); otherwise C = 1. On success zp_48/49 are re-pointed at the original cell. Forest blobs are therefore confined to aligned 2x2 blocks that contain nothing but blank/forest.",
    "In:  zp_A5/zp_A6, map, terrainClassTable",
    "Out: C flag, zp_48/49, X/Y clobbered",
    "(confidence: medium)"
   ],
   "unit": "game/mapGenerator6F00"
  },
  "7340": {
   "routine": [
    "placeForestClusters - Scatters forest ($0301 = $5D, variants $5D-$60, $0304 = $40 so growth respects 2x2 alignment). Cluster count-1 = (rnd&7)+2 normally (1/32 chance of no forest at all when rnd&$1F = 0), fixed 9 for map style $80 (QB SNEAK), and additionally masked with 3 for style $40 (DEFENDER). Each cluster picks a centre (rnd&$1F)+4 in both axes and makes 6 seeds jittered around it (addRandomJitter +/-8 columns, +/-4 rows, retried while >= 40), each seed painted by paintForestClusterSeeds. Also stores $41 into $0302, which nothing reads (dead store).",
    "In:  $032E, RNG",
    "Out: map cells; $0301, $0302, $0304, zp_2A cluster counter, zp_2B seed counter, zp_92-zp_95",
    "(confidence: medium)"
   ],
   "unit": "game/mapGenerator6F00"
  },
  "73AD": {
   "routine": [
    "paintForestClusterSeeds - Paints a 4x2 block of forest blob seeds around (zp_93, zp_92): calls paintForestBlobAt at (x,y), (x+1,y), (x+1,y+1), (x,y+1), (x-2,y), (x-1,y), (x-1,y+1) and falls into it for (x-2,y+1), stepping zp_92/zp_93 in between.",
    "In:  zp_93 column, zp_92 row",
    "Out: map cells; zp_92/zp_93 restored to start values; zp_A7/zp_A8",
    "(confidence: medium)"
   ],
   "unit": "game/mapGenerator6F00"
  },
  "73D4": {
   "routine": [
    "paintForestBlobAt - If zp_93 (column) and zp_92 (row) are both < 40, copies them to zp_A7/zp_A8 and grows a 4-cell blob with growTerrainBlob (spreads $DC/$DC, tile $0301, flags $0304). Out-of-range coordinates are ignored.",
    "In:  zp_92/zp_93, $0301/$0304",
    "Out: map cells; zp_A7/zp_A8",
    "(confidence: medium)"
   ],
   "unit": "game/mapGenerator6F00"
  },
  "73EE": {
   "routine": [
    "applyContourTiles - Contour/edge pass for the hill terrain whose interior tile is A ($65 summit or $61 lower hill). zp_8E = terrain class of A, zp_91 = zp_8E-1 = class of its contour ring. Scans all 1600 cells: a cell of class zp_91 is erased to $40 and re-evaluated; cells of class >= zp_8E and river cells (class 1) are left alone; every other cell goes through applyContourRuleAtCell, which may paint a contour tile (rule value - $0324), fill the cell with the interior tile, or erase a neighbour; when it returns C = 1 the scan backs up two rows/columns and continues. Called with A = $65 ($0324 = 0 -> contour $41-$54) and A = $61 ($0324 = $20 -> contour $21-$34).",
    "In:  A = interior tile, $0324 contour offset, contour rule table $0200, terrainClassTable, map",
    "Out: map cells; $0312 = interior tile, zp_8E, zp_91, zp_2D/zp_2E scan row/col, zp_8C/zp_8D, zp_21",
    "(confidence: medium)"
   ],
   "unit": "game/mapGenerator6F00"
  },
  "7444": {
   "routine": [
    "applyContourRuleAtCell - Builds an 8-bit mask (zp_21) of the neighbours of cell (zp_2E, zp_2D) whose terrain class is >= zp_8E (bit order NW,N,NE,W,E,SW,S,SE from neighbourOffsetTable/neighbourBitTable), clears bits that point off the map, and looks the mask up in the contour rule table at $0200: 0 = leave the cell; $01-$7F = paint tile (value - $0324) with a random variant (sub_7872) at the cell, C = 0; $FF = fill the cell with the interior tile $0312 and return C = 1 (rescan from two cells back); $80-$FE = irregular pattern: with a 50% random bit (taken from zp_8D, refilled every 7 calls) erase the neighbour indexed by the low 3 bits if it is of class zp_8E (C = 1), otherwise fill the cell when the value is >= $90 (C = 1) or leave it (C = 0). On C = 1 zp_2E/zp_2D are decremented by 2 (floor 0).",
    "In:  zp_2E/zp_2D cell, zp_48 pointer to (x-1,y-1), zp_8E, $0200 table, $0324, $0312",
    "Out: C flag, map cells, zp_2D/zp_2E, zp_21, zp_18, zp_8C/zp_8D",
    "(confidence: medium)"
   ],
   "unit": "game/mapGenerator6F00"
  },
  "6F00": {
   "unit": "game/mapGenerator6F00",
   "block": [
    "laneColumnTable: byteTable, 4 bytes. $06,$0F,$19,$22: the four lane columns (6, 15, 25, 34) along which hill summits are placed; shuffled into $0329 by shuffleLaneColumns. Labelled sub_6F00 only because variant B has code here."
   ]
  },
  "71AB": {
   "unit": "game/mapGenerator6F00",
   "block": [
    "unusedBitTable71AB: byteTable, 5 bytes. $08,$04,$02,$01,$00 - not referenced by any code in variant A (the $71AC reference in XREF is variant B code)."
   ]
  },
  "74EE": {
   "unit": "game/mapGenerator6F00",
   "block": [
    "neighbourOffsetTable: byteTable, 8 bytes. Cell offsets 0,1,2,$28,$2A,$50,$51,$52 of the eight neighbours of the centre cell (+$29) of a 3x3 block; indexed with a random 0-7 by the terrain fill pass at $74A7. The label L_74F3 in the middle comes from variant B."
   ]
  },
  "74F6": {
   "unit": "game/mapGenerator6F00",
   "block": [
    "bitMaskTable: byteTable, 8 bytes. $01,$02,$04,$08,$10,$20,$40,$80 - single-bit masks used by the terrain fill pass at $745B."
   ]
  },
  "74FE": {
   "routine": [
    "clearMapMarkers - Map generator clean-up pass: scans all 1600 cells of the 40x40 map ($F000-$F63F, six full pages by self-modified pointer then the 64-byte tail at $F600) and turns every cell whose value equals the marker byte in A (bit 7 set: $80 or $81) back into blank terrain $40. generateBattlefieldMap (sub_6F04) calls it with A=$80 after the first terrain pass and with A=$81 after the second, removing the river reservation markers placed by markRiverBanks.",
    "In:  A = marker value to erase ($80 / $81); map at $F000",
    "Out: map cells equal to A become $40; operands at $7517/$7522 (page) and $751C/$753C (marker) self-modified; A/Y clobbered"
   ],
   "unit": "game/mapGenerator6F00"
  },
  "7548": {
   "routine": [
    "generateRiver - Draws the river (terrain codes $55-$5C: class 1, move cost 65, cyan) across the map from column 0 to 39 as part of generateBattlefieldMap. No river for QB Sneak (mapGenGameFlags bit 7). Start row: 20 on symmetric maps (mapSymmetryFlags bit 7), otherwise random 12..27; Defender (bit 6) always gets a river, other types skip it with probability 1/8. Each step writes $55 + current segment type at (genCol, genRow), picks the next segment (chooseNextRiverSegment), reserves the banks (markRiverBanks) and moves (advanceRiverPosition) until genCol reaches 40.",
    "In:  mapGenGameFlags $032E, mapSymmetryFlags $032D, scenario RNG $5A-$5C (nextScenarioRandom $FD4A), map at $F000",
    "Out: river tiles written to the map, $80/$81 reservation markers around them, genCol/genRow/riverCurSegment/genTileType updated, may set mapSymmetryFlags bit 6"
   ],
   "unit": "game/mapGenerator6F00"
  },
  "759E": {
   "routine": [
    "chooseNextRiverSegment - Chooses the next river segment type into genTileType ($0312). Types: 0 = bend down/right ($55), 1 = bend left/down ($56), 2 = bend up/right ($57), 3 = bend left/up ($58), 4-7 = horizontal straight variants ($59-$5C). Rules: after 1 comes 2, after 3 comes 0, after a bend a straight, after a straight 50% straight / else 1 or 3; columns 0-1 and 38-39 are forced horizontal (column 37 may not start a vertical jog). Symmetric maps stay horizontal on row 20; at column 20 with probability 7/8 the river jogs one row up (genCol -= 2, genRow = 19, mapSymmetryFlags |= $40) so that the 180-degree mirrored bottom half joins it at the centre.",
    "In:  riverCurSegment $0311, genCol $030F, genRow $0310, mapSymmetryFlags $032D, scenario RNG",
    "Out: genTileType $0312 = next segment 0-7; may modify genCol/genRow and set $032D bit 6"
   ],
   "unit": "game/mapGenerator6F00"
  },
  "761C": {
   "routine": [
    "advanceRiverPosition - Moves the river cursor according to the current segment type riverCurSegment: patches the JMP operand at $762C with riverStepLo/Hi[type] and jumps to one of the three stubs riverStepRight ($762E, genCol+1), riverStepUp ($7632, genRow-1) or riverStepDown ($7636, genRow+1). Types 1 -> down, 3 -> up, all others -> right.",
    "In:  riverCurSegment $0311, tables $763A/$7642",
    "Out: genCol or genRow changed; operand at $762C self-modified"
   ],
   "unit": "game/mapGenerator6F00"
  },
  "762E": {
   "routine": [
    "riverStepRight - Step stub reached through advanceRiverPosition: INC genCol; RTS. Listed as data (.byte) in the disassembly.",
    "In:  none",
    "Out: genCol $030F + 1"
   ],
   "unit": "game/mapGenerator6F00"
  },
  "7632": {
   "routine": [
    "riverStepUp - Step stub reached through advanceRiverPosition: DEC genRow; RTS (segment type 3). Listed as data in the disassembly.",
    "In:  none",
    "Out: genRow $0310 - 1"
   ],
   "unit": "game/mapGenerator6F00"
  },
  "7636": {
   "routine": [
    "riverStepDown - Step stub reached through advanceRiverPosition: INC genRow; RTS (segment type 1). Listed as data in the disassembly.",
    "In:  none",
    "Out: genRow $0310 + 1"
   ],
   "unit": "game/mapGenerator6F00"
  },
  "764A": {
   "routine": [
    "markRiverBanks - Reserves the cells around the current river cell so that later terrain passes keep the banks clear: for columns 2-37 it addresses the 6x6 block whose origin is (genCol & ~1) - 2, (genRow & ~1) - 2, marks the 16 inner cells (riverZoneInnerOffsets) with $81 when they are blank or already marked, and the 20 perimeter cells (riverZoneOuterOffsets) with $80 when blank. In columns 0-1 and 38-39 only the four cells of the river's own column (rows evenRow-1..evenRow+2) get $81. The markers are removed again by clearMapMarkers.",
    "In:  genCol $030F, genRow $0310, map via readMapCell ($25FA -> zp_48/49), tables $7894/$78A8",
    "Out: $80/$81 written into blank map cells; zp_48/49 pointer"
   ],
   "unit": "game/mapGenerator6F00"
  },
  "76AD": {
   "routine": [
    "mapNameEntryScreen - 'TYPE MAP NAME' screen of the game setup (entered by JMP from $8339 when the map item is chosen). Draws the current map (generateBattlefieldMap, optionally flipped 180 degrees by $EC58 when mapFlipFlag is set), the overview map, clears the name field, prints 'TYPE MAP NAME / (F7 RANDOM) / (F5 SYM/RND)' ($0677) and 'BTN TO ACCEPT / (F1 TO FLIP)' ($069F), then loops on enterMapName: each completed name (C=0) becomes the new mapSeed, the map is cleared ($EC00), redrawn and the prompt repeated; the fire button (C=1) prints 'ACCEPTED' ($EFED) centred on row 15 and returns to the setup menu.",
    "In:  mapSeed $0B95-$0B97, mapFlipFlag $0B9D, keyboard/joystick",
    "Out: mapSeed, mapFlipFlag (cleared after a new name), map regenerated, text window/colour zp_3D/zp_42 = 9"
   ],
   "unit": "game/mapGenerator6F00"
  },
  "7717": {
   "routine": [
    "enterMapName - Interactive entry of the 5-character map name at column 4, row 16 (field shown by printMapName, underlined with five $25 glyphs on the next row). Accepted characters are the 32-entry alphabet at $890C (space, A-Z, + - * / =), typed via newKeyEvent ($90EA); DEL ($88) erases, other keys beep (sound 3), a colour-$11 block marks the cursor cell. F1 (currentKey 0) toggles mapFlipFlag and flips/redraws the map. Once five characters are present they are packed 5 bits each (last character in the low bits) into $5A-$5C and the routine returns C=0. F7 (key 3) or F5 (key 2) instead take a random seed from the game RNG ($57-$59; F5 ORs $1F into the low byte = symmetric map), convert it with seedToMapName and return C=0. A fire-button press returns C=1 (accept). Also used by the copy-protection manualLookupCheck ($09F9) to read the manual answer.",
    "In:  currentKey zp_66, newKeyEvent $90EA, fire button (checkFirePressed $0EB1), rngState $57-$59, mapNameAlphabet $890C",
    "Out: C = 0: $5A-$5C = 24-bit seed and mapName $0BA9-$0BAD = its text; C = 1: fire pressed, nothing chosen; mapNameLength $0313, mapFlipFlag may toggle, zp_18 scratch, text cursor state"
   ],
   "unit": "game/mapGenerator6F00"
  },
  "782D": {
   "routine": [
    "setMapNameCursor - Positions the text cursor at the map-name field: textCursorCol = 4, row 16 (sub_C097).",
    "In:  text window zp_3B/zp_40",
    "Out: zp_3F = 4, zp_40 = $10, screen pointer zp_B6/B7"
   ],
   "unit": "game/mapGenerator6F00"
  },
  "7836": {
   "routine": [
    "clearMapNameField - Fills the five map-name characters $0BA9-$0BAD with spaces ($20).",
    "In:  none",
    "Out: $0BA9-$0BAD = $20; Y = $FF"
   ],
   "unit": "game/mapGenerator6F00"
  },
  "7841": {
   "routine": [
    "printMapName - Moves the cursor to the name field (setMapNameCursor), terminates the name with $8D (CR with the end-of-string bit) at $0BAE and prints mapName $0BA9 with the text engine ($C133).",
    "In:  mapName $0BA9-$0BAD",
    "Out: $0BAE = $8D, text printed, cursor on the next row"
   ],
   "unit": "game/mapGenerator6F00"
  },
  "7850": {
   "routine": [
    "seedToMapName - Converts the 24-bit seed in A (low), Y (mid), X (high) into the five-letter map name: five times take the low 5 bits as an index into the alphabet at $890C, store at $0BA9+4 down to $0BA9, shift the seed right by 5. Called with the map seed registers loaded by $EC2B (from generateBattlefieldMap) and by enterMapName after a random F5/F7 seed.",
    "In:  A/Y/X = seed bytes 0/1/2 (same layout as $5A/$5B/$5C)",
    "Out: mapName $0BA9-$0BAD; zp_18-zp_1A used as the shift register; X/Y clobbered"
   ],
   "unit": "game/mapGenerator6F00"
  },
  "7872": {
   "routine": [
    "randomTileVariant - Picks a random visual variant of a terrain code: A = base tile code; the mask table at $0770 (indexed by code - $40, or code - $20 for codes below $40) gives the number of extra variants (0, 1 or 3); the result is A + (nextScenarioRandom AND mask). Y is preserved through the self-modified operand at $7892. Used by the terrain fill passes of the map generator ($72A4, $74E7). Starts with a NOP (probably a disabled byte).",
    "In:  A = base tile code, Y preserved, table $0770, scenario RNG",
    "Out: A = tile code with random variant bits; operands at $788D/$7890/$7892 self-modified"
   ],
   "unit": "game/mapGenerator6F00"
  },
  "78D1": {
   "routine": [
    "startGameSession - Game-start sequence called from startGameFromSetup ($0B40) after the setup block and unit templates were loaded. Resets battle state (film limit, redraw/recycler/clock markers = $FF, unitCol[0] = $FF so the unit template gets copied, colours own=6/enemy=2, overview unit pattern $55, screen 3), then if filmPlaybackFlag bit 7 jumps to the film replay start ($7CD1). Otherwise: revealAll = trainer flag, step interval 4, decides the side (alternates via firstPlayFlag when preset, else negotiated over the link after openCommLink/waitForConnection or readyForNewGameHandshake), sends the side ($553B) and waits for the exchange, patches the own-side branches, player 0 sends the map seed with command $8B (or $A6 when flipped) while player 1 shows message $1A and waits for it (waitForMapSeedCommand), exchanges identities ($ED43, version check $90F8 == $DB else 'OPPONENT USING NON-STANDARD GAME.'), copies the unit start template for the game type ($ECF3), records the setup in the film header ($D000: +4 trainer flag, +5 side, +6 name, +$C options, +$11 owner names, +$2B seed, +$2E first-play/flip), generates the map, rotates map/units for player 1 ($EC58/$EC91), initialises all units, for the trainer build loads the $EC00 sub-overlay from track 29 and runs $EC0F, runs the unit setup phase (runMapMainLoop with gamePhase $3F) for game types other than Scrimmage and The Bomb, waits for the opponent's packet, sets the game clock from roundsByGameType, maps the comcen speed classes, writes the film snapshot, places the recyclers, shuffles the unit order, sets the last-quarter warning round and jumps into the comm tail at $EE26 to start the battle.",
    "In:  settings block $0B95-$0BB4, filmPlaybackFlag $0B9B, commSessionState $0B9C, commBuildFlag $0BA5, comm module state $E01D/$E03B, unit templates at $F000",
    "Out: entire battle state initialised (map, units, clock, film header, message slots, gamePhase zp_B1); does not return normally (ends in JMP $EE26 or $7CD1)",
    "(confidence: medium)"
   ],
   "unit": "game/mapGenerator6F00"
  },
  "7BB2": {
   "routine": [
    "shuffleInitialUnitOrder - Seeds the scenario RNG from mapSeed ($EC2B), resets unitBaseOrder $9164 and unitProcessOrder $90FD to the identity permutation ($7D70), performs 51 random swaps with shuffleUnitOrder ($47ED) and jumps to L_4657 (the round-order rebuild at the end of finishRound). Both machines get the same order because the RNG is seeded from the shared map seed.",
    "In:  mapSeed $0B95-$0B97",
    "Out: $9164/$90FD shuffled, scenario RNG $5A-$5C, zp_18 counter; continues at L_4657"
   ],
   "unit": "game/mapGenerator6F00"
  },
  "7BC6": {
   "routine": [
    "patchOwnSideBranches - Installs the local player's side into the engine's self-modifying side tests: writes BCC ($90, side 0) or BCS ($B0, side 1) into the branch opcodes at $63E2, $2840, $3F9E, $36D5, $379C, $4783, $5BF7 and the inverted opcode into $206B, $2084, $3B2C, $3BD0 (all follow a CMP/CPY #$32 'unit index < 50' test), and sets comcenUnit $9236 = comcenUnitBySide[side] (49 or 99). Called at game start and at film replay start ($7D20).",
    "In:  playerSide $0B9F, comcenUnitBySide $0C8A",
    "Out: 11 branch opcodes patched, $9236"
   ],
   "unit": "game/mapGenerator6F00"
  },
  "7BF9": {
   "routine": [
    "sendPhaseSyncAndWait - Queues the 1-byte phase-sync command $9E (queueCmdBytePending), lets $C7C3 show 'WAITING FOR OPPONENT' (message $0B) when gamePhase bit 0 is set on a modem game, then runs frames (updateGameFrame when gamePhase bits 6-7 are clear, otherwise waitForExchangeIdle) until the $9E command has been executed on both machines, which clears the low two bits of gamePhase zp_B1 via getPauseClearMask. Finally clears messageState.",
    "In:  gamePhase zp_B1, comm exchange",
    "Out: zp_B1 low bits cleared by the command handler, $92A1 = 0"
   ],
   "unit": "game/mapGenerator6F00"
  },
  "7C1C": {
   "routine": [
    "waitForMapSeedCommand - Calls waitForExchangeIdle repeatedly while gamePhase zp_B1 bit 7 is set; the bit is cleared by the handler of the map-seed command $8B/$A6 ($51EA), i.e. this waits until both machines have received the map seed. Then clears messageState $92A1.",
    "In:  zp_B1, comm exchange",
    "Out: $92A1 = 0"
   ],
   "unit": "game/mapGenerator6F00"
  },
  "763A": {
   "unit": "game/mapGenerator6F00",
   "block": [
    "riverStepLo: addrTable, 8 bytes. low bytes of the step routine for river segment types 0-7: $762E (right) for 0,2,4-7, $7636 (down) for 1, $7632 (up) for 3; high bytes in riverStepHi."
   ]
  },
  "7642": {
   "unit": "game/mapGenerator6F00",
   "block": [
    "riverStepHi: addrTable, 8 bytes. high bytes ($76) of the river step routine addresses."
   ]
  },
  "7894": {
   "unit": "game/mapGenerator6F00",
   "block": [
    "riverZoneOuterOffsets: byteTable, 20 bytes. Offsets of the 20 perimeter cells of a 6x6 block (column 0 rows 1-4, row 0 cols 0-5, column 5 rows 1-4, row 5 cols 0-5); the first four (same column, rows 1-4) are used alone near the map edges. Marked $80 by markRiverBanks. Labelled sub_7894 because variant B has code here."
   ]
  },
  "78A8": {
   "unit": "game/mapGenerator6F00",
   "block": [
    "riverZoneInnerOffsets: byteTable, 16 bytes. Offsets of the inner 4x4 cells (rows 1-4, cols 1-4) of the 6x6 block; marked $81 by markRiverBanks."
   ]
  },
  "78B8": {
   "unit": "game/mapGenerator6F00",
   "block": [
    "unitLayoutByGameType: byteTable, 7 bytes. Index (0-3) of the 400-byte unit start-position template at $F000 + 400*n used for game types 0-6: 2,2,2,1,3,0,0. Read by the comm tail routine $ECF3 (T34 build)."
   ]
  },
  "78BF": {
   "unit": "game/mapGenerator6F00",
   "block": [
    "comcenSpeedClassMap: byteTable, 4 bytes. Translates the comcen speed setting 0-3 into the engine speed class: 4,0,1,5 (applied to $92B4/$92B5 at battle start)."
   ]
  },
  "78C3": {
   "unit": "game/mapGenerator6F00",
   "block": [
    "roundsByGameType: byteTable, 7 bytes. Game length in rounds for game types 0-6: $3F,$3F,$1F,$7F,$7F,$FE,$FE (Scrimmage 63, QB Sneak 63, The Bomb 31, Face-Off 127, Sluggers 127, Full War 254, Defender 254). Read by sub_7D8B."
   ]
  },
  "78CA": {
   "unit": "game/mapGenerator6F00",
   "block": [
    "gameTypeOptionsByMenuItem: byteTable, 7 bytes. Default gameTypeOptions byte for the seven game-type menu items: $00,$01,$02,$13,$14,$1D,$1E (bits 3/4 = unit options and damage on for Face-Off, Sluggers, Full War, Defender). Read at $828F; labelled L_78CA/sub_78CB from variant B."
   ]
  },
  "7C2E": {
   "routine": [
    "applyGameTypeDefaults - 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.",
    "In:  D_0BA3 bits 0-2, D_0B9F, tables $892C/$8945, D_7C29",
    "Out: 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"
   ],
   "unit": "game/mapGenerator6F00"
  },
  "7CA2": {
   "routine": [
    "removeUnitsUpToY - 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.",
    "In:  Y = highest unit index to remove",
    "Out: D_F76C[0..Y] = D_FAF0[0..Y] = $FF; Y = $FF"
   ],
   "unit": "game/mapGenerator6F00"
  },
  "7CAE": {
   "routine": [
    "selectSideDefaultsTable - 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).",
    "In:  X = side 0/1",
    "Out: zp_48/zp_49 = table pointer"
   ],
   "unit": "game/mapGenerator6F00"
  },
  "7CBF": {
   "routine": [
    "readDefaultAndAdvanceRow - 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).",
    "In:  zp_48/zp_49 = current row, patched column index",
    "Out: A = table value, zp_48/zp_49 += 5"
   ],
   "unit": "game/mapGenerator6F00"
  },
  "7CD1": {
   "routine": [
    "startFilmPlayback - 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.",
    "In:  film header at $D000 (zp_BC/zp_BD = $D000), D_0B9B bit 7",
    "Out: 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"
   ],
   "unit": "game/mapGenerator6F00"
  },
  "7D70": {
   "routine": [
    "initUnitOrderLists - 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.",
    "In:  none",
    "Out: D_9164[0..99] = D_90FD[0..99] = index; Y=$FF"
   ],
   "unit": "game/mapGenerator6F00"
  },
  "7D7D": {
   "routine": [
    "placeAllUnitsOnMap - 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).",
    "In:  unit tables",
    "Out: map cells at $F000 updated for all live units; zp_18 = $FF"
   ],
   "unit": "game/mapGenerator6F00"
  },
  "7D8B": {
   "routine": [
    "setGameClockForType - 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).",
    "In:  D_0BA3 bits 0-2, D_78C3",
    "Out: A = D_91CB = rounds"
   ],
   "unit": "game/mapGenerator6F00"
  },
  "7D98": {
   "routine": [
    "clearUnitsAndRecyclersFromMap - 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.",
    "In:  unit tables, D_92A8/D_92AA",
    "Out: map at $F000 without units and recyclers"
   ],
   "unit": "game/mapGenerator6F00"
  },
  "7DA7": {
   "routine": [
    "clearRecyclerCell - 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.",
    "In:  X = side, D_92A8/D_92AA",
    "Out: map cell := $40, zp_48/zp_49"
   ],
   "unit": "game/mapGenerator6F00"
  },
  "7DB8": {
   "routine": [
    "waitForSpaceKey - 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.",
    "In:  D_9248 (set by the IRQ keyboard scan)",
    "Out: D_9248=$FF, D_929C=0, D_9277=0, D_0B7D=$1E, message queue cleared, RNG advanced"
   ],
   "unit": "game/mapGenerator6F00"
  },
  "7DE3": {
   "routine": [
    "handleMainMenuChoice - 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.",
    "In:  D_91D5, D_0BA5, film in $D000",
    "Out: D_0BA5, D_0B9B, D_92FD=2, D_7FA1=0, D_7FA2; jumps to $0AC8 for items 0-2"
   ],
   "unit": "game/mapGenerator6F00"
  },
  "7E39": {
   "routine": [
    "setModeFlagsAndStartGame - Tail of handleMainMenuChoice: stores X into linkTypeFlag ($0BA5) and A into filmPlaybackFlag ($0B9B) and jumps to startGameFromSetup ($0AC8).",
    "In:  X = new $0BA5, A = new $0B9B",
    "Out: D_0BA5, D_0B9B; jumps to $0AC8"
   ],
   "unit": "game/mapGenerator6F00"
  },
  "7E4F": {
   "routine": [
    "checkDiskAndLoadFilmDirectory - 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.",
    "In:  disk in drive",
    "Out: C=1 no usable disk (message shown), C=0 directory loaded at $0300; D_92F0, D_0200 BAM buffer"
   ],
   "unit": "game/mapGenerator6F00"
  },
  "7E74": {
   "routine": [
    "writeFilmDirectory - 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.",
    "In:  directory at $0300",
    "Out: track 18 s2-3 rewritten; C=0"
   ],
   "unit": "game/mapGenerator6F00"
  },
  "7E8B": {
   "routine": [
    "loadFilmDirectory - Reads track 18 sectors 2-3 into $0300-$04FF with loadSectorsRetry (the 30 x 16-byte film names); failure -> diskErrorExit. Tail of checkDiskAndLoadFilmDirectory.",
    "In:  none",
    "Out: $0300-$04FF = film directory, C=0"
   ],
   "unit": "game/mapGenerator6F00"
  },
  "7EA0": {
   "routine": [
    "drawFilmDirectoryPage - 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.",
    "In:  D_7FA1, D_7FA2, D_92F0, directory at $0300",
    "Out: 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"
   ],
   "unit": "game/mapGenerator6F00"
  },
  "7F3F": {
   "routine": [
    "runLoadFilmMenu - 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.",
    "In:  D_91D5, D_92F0, D_7FA1, disk",
    "Out: $D000-$DFFF = film, D_91D5 = track, zp_18/zp_19, D_58C3/4, D_58C6/7"
   ],
   "unit": "game/mapGenerator6F00"
  },
  "7FA6": {
   "routine": [
    "setFilmDirectoryPagePtr - 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.",
    "In:  D_7FA1, D_7FA3",
    "Out: zp_48/zp_49, D_90F8"
   ],
   "unit": "game/mapGenerator6F00"
  },
  "7FCB": {
   "routine": [
    "nextFilmDirectoryPage - Advances filmDirectoryPage modulo 3 and redraws the page (jumps to drawFilmDirectoryPage).",
    "In:  D_7FA1",
    "Out: D_7FA1, redrawn page"
   ],
   "unit": "game/mapGenerator6F00"
  },
  "7FDD": {
   "routine": [
    "runSaveFilmMenu - 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.",
    "In:  D_91D4, D_91D5, D_91D6, D_7FA1, keyboard/joystick (D_90EA, zp_5D), zp_6C/zp_6E timers",
    "Out: Y = D_91D5 (track after a save), D_92E6=0, D_92C5, D_9278, directory entry, disk; zp_4A/zp_4B = entry pointer"
   ],
   "unit": "game/mapGenerator6F00"
  },
  "810C": {
   "routine": [
    "printFilmSlotEntry - 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)'.",
    "In:  A = text colour, D_91D5, D_91D6, D_92F0, zp_4A/zp_4B",
    "Out: screen text, zp_3D, zp_3F=0"
   ],
   "unit": "game/mapGenerator6F00"
  },
  "8143": {
   "routine": [
    "filmSlotToTrack - 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.",
    "In:  D_7FA1, D_91D5",
    "Out: A = D_91D5 = track"
   ],
   "unit": "game/mapGenerator6F00"
  },
  "815E": {
   "routine": [
    "saveFilmToSlot - 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.",
    "In:  D_91D5 slot, film at $D000",
    "Out: track sectors 0-15 written, D_91D5 = track, zp_18/zp_19, D_58C3/4, D_58C6/7"
   ],
   "unit": "game/mapGenerator6F00"
  },
  "81A5": {
   "routine": [
    "promptInsertGameDisk - Patches 'GAME' ($EFD4) into the message 'INSERT xxxx DISK AND PRESS SPACE.' (promptInsertDiskAndWaitSpace), then waitForGameDiskInserted and resumeCommAfterDiskAccess. End of the save/load film flow.",
    "In:  none",
    "Out: game disk present, comm module resumed"
   ],
   "unit": "game/mapGenerator6F00"
  },
  "81B2": {
   "routine": [
    "promptInsertDataDisk - Entry of promptInsertDiskAndWaitSpace with A/Y = $EFD0 ('DATA').",
    "In:  none",
    "Out: see promptInsertDiskAndWaitSpace"
   ],
   "unit": "game/mapGenerator6F00"
  },
  "81B6": {
   "routine": [
    "promptInsertDiskAndWaitSpace - 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).",
    "In:  A/Y = address of a 4-byte name ('DATA' $EFD0 or 'GAME' $EFD4)",
    "Out: D_EFB6..D_EFB9 patched, message slot $1E, D_92A1, D_9248"
   ],
   "unit": "game/mapGenerator6F00"
  },
  "81F4": {
   "routine": [
    "checkFilmInMemory - 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.",
    "In:  film header at $D000",
    "Out: C flag, zp_BC/zp_BD = $D000, A = header byte 4 when valid"
   ],
   "unit": "game/mapGenerator6F00"
  },
  "81FE": {
   "routine": [
    "waitFramesServicingComm - 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).",
    "In:  none",
    "Out: C=1, zp_6C=0"
   ],
   "unit": "game/mapGenerator6F00"
  },
  "820B": {
   "routine": [
    "beginGameTypeSelection - 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.",
    "In:  none",
    "Out: D_90FB=0, message slot $1E, $868D=0"
   ],
   "unit": "game/mapGenerator6F00"
  },
  "821F": {
   "routine": [
    "runGameTypeMenu - 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).",
    "In:  D_0BA3, D_0BA5, D_92F3, menu input",
    "Out: 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"
   ],
   "unit": "game/mapGenerator6F00"
  },
  "8279": {
   "routine": [
    "printGameTypeNamesLoop - 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.",
    "In:  zp_18",
    "Out: screen text"
   ],
   "unit": "game/mapGenerator6F00"
  },
  "82A4": {
   "routine": [
    "applyCustomPresetTail - 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.",
    "In:  A = preset",
    "Out: D_0BA3, zp_B1=$BF"
   ],
   "unit": "game/mapGenerator6F00"
  },
  "82BF": {
   "routine": [
    "runMapTypeMenu - 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.",
    "In:  D_0BA3, D_0BA4, D_0BA1, zp_57-zp_59, menu input",
    "Out: D_0BA3, D_0BA1, D_0B95-D_0B97, zp_B1=$FF, zp_73, command $A4 queued"
   ],
   "unit": "game/mapGenerator6F00"
  },
  "833C": {
   "routine": [
    "sendGameTypeCommand - Stores A in cmdParam0 (zp_73) and queues command $A4 with that single parameter (sub_561F) - tells the opponent the chosen gameTypeOptions byte.",
    "In:  A = game type options byte",
    "Out: zp_73, command queued"
   ],
   "unit": "game/mapGenerator6F00"
  },
  "8343": {
   "routine": [
    "printMenuTitle - 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).",
    "In:  X = initial item, A/Y = title string",
    "Out: D_91D5, D_91D6, zp_3B=$1C"
   ],
   "unit": "game/mapGenerator6F00"
  },
  "8356": {
   "routine": [
    "runMenuWidth10 - Runs the menu selector with a highlight width of 10 (falls into runMenuWithWidth).",
    "In:  menu variables",
    "Out: A = D_91D5 = chosen item"
   ],
   "unit": "game/mapGenerator6F00"
  },
  "8358": {
   "routine": [
    "runMenuWithWidth - 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).",
    "In:  A = width, D_91D4, D_91D5, D_91D6",
    "Out: A = D_91D5, zp_3B--, zp_3C"
   ],
   "unit": "game/mapGenerator6F00"
  },
  "8364": {
   "routine": [
    "resetSelectionAndView - 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.",
    "In:  none",
    "Out: D_9242, D_90F8, D_90FB, zp_A5, zp_A6"
   ],
   "unit": "game/mapGenerator6F00"
  },
  "8378": {
   "routine": [
    "chooseRecycler - 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.",
    "In:  D_0BA5, D_0B9F, D_92A6, D_8450, player input, D_91D8/D_91D9",
    "Out: D_92A6 (solo), D_91D5, zp_73-zp_75, command $9C queued, D_9022=0, zp_BB=$FF; self-modifies $8433"
   ],
   "unit": "game/mapGenerator6F00"
  },
  "843B": {
   "routine": [
    "printOptionWordLine - Prints option word A (0 FULL, 1 HALF, 2 NONE via printOptionWord) followed by a newline.",
    "In:  A = option index",
    "Out: screen text"
   ],
   "unit": "game/mapGenerator6F00"
  },
  "8441": {
   "routine": [
    "clearInfoPanelSetRow - 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).",
    "In:  A = cursor row",
    "Out: zp_3D=zp_42=8, zp_40=A, cursor positioned"
   ],
   "unit": "game/mapGenerator6F00"
  },
  "7C29": {
   "unit": "game/mapGenerator6F00",
   "block": [
    "setupPhaseModeTable: byteTable, 5 bytes. 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)."
   ]
  },
  "7FA1": {
   "unit": "game/mapGenerator6F00",
   "block": [
    "filmDirectoryPage: byteTable, 1 bytes. Variable: current film directory page (0-2)."
   ]
  },
  "7FA2": {
   "unit": "game/mapGenerator6F00",
   "block": [
    "filmMenuMode: byteTable, 1 bytes. Variable: 0 = save film, 1 = load film."
   ]
  },
  "7FA3": {
   "unit": "game/mapGenerator6F00",
   "block": [
    "filmPageFirstSlotTable: byteTable, 3 bytes. {1,11,21}: slot number printed for the first entry of each directory page."
   ]
  },
  "8451": {
   "routine": [
    "editValueWithJoystick - Generic numeric option editor used by the custom-scenario setup. A = exclusive upper limit (patched into the CMP at $8492), Y = display multiplier (patched into $8469). Loops: un-highlights the one-row menu, prints editValue*Y in decimal at column 1 of the value window (multiply8x8 $FCCD -> A=lo/Y=hi, then $C17D), clears the rest of the line, services the link (serviceCommandExchange), blinks the row, and applies joystick up/down as +1/-1 (rejecting values >= limit or below 0, playing sound 2 on a change, auto-repeat via setJoystickRepeatDelay). Returns when the fire button is pressed (checkFirePressed).",
    "In:  A = limit (value must stay < A), Y = multiplier for display, D_844F initial value, zp_5D joystick, zp_6C repeat timer, zp_6E blink timer, menu window set up by beginValueEdit",
    "Out: D_844F = edited value; D_92C5 = 1; text printed in the info panel; sounds 2"
   ],
   "unit": "game/mapGenerator6F00"
  },
  "84AA": {
   "routine": [
    "applyHandicapPoints - Tail of runGameStartOptions (reached by JMP from $85B9). Computes the default handicap for the asymmetric scenarios: game type 1 (QB SNEAK) gives side 0 a handicap of 2 (20 points), game type 6 (DEFENDER) gives side 1 a handicap of $14 (200 points), everything else 0. When the custom toggle (D_92F3 bit 7) is set the player edits the value instead ('HANDICAP', 0..50 shown x10) starting from that default. A nonzero result is sent as command $A5 kind 0 (value*10 points for the sender's side, cmdSetupOption); zero is not sent.",
    "In:  D_0BA3 bits 0-2 game type, D_0B9F playerSide, D_92F3 bit 7 custom flag",
    "Out: command $A5 00 value queued via queueCmd3 (zp_74 = 0, zp_75 = value); info panel used"
   ],
   "unit": "game/mapGenerator6F00"
  },
  "84EC": {
   "routine": [
    "editTerrainPoints - Custom option 'TERRAIN PTS': starts from D_92AC[opponent side] (points awarded for entering the enemy zone), lets the player pick 0..2 with editValueWithJoystick and sends command $A5 kind 1 (cmdSetupOption stores it in $92AC of the other side).",
    "In:  D_0B9F playerSide, D_92AC",
    "Out: command $A5 01 value queued (zp_74 = 1, zp_75 = value)",
    "(confidence: medium)"
   ],
   "unit": "game/mapGenerator6F00"
  },
  "84F1": {
   "routine": [
    "editTerrainPointsForSide - Entry into editTerrainPoints with the side index already in A (A -> Y, X = D_92AC[Y]). In variant A nothing calls it; the XREF caller at $83DE belongs to overlay variant B, where $84F1 is a different routine.",
    "In:  A = side index 0/1",
    "Out: as editTerrainPoints",
    "(confidence: medium)"
   ],
   "unit": "game/mapGenerator6F00"
  },
  "8511": {
   "routine": [
    "editDronesAndMissiles - Custom options 'DRONES' (0..9, from D_92AE) and 'MISSILES' (label $CC9E, 0..36, from D_92B0), each edited with beginValueEdit + editValueWithJoystick. The two results are sent as one command $90 x=drones y=missiles; in the setup phase cmdMissileStrike ($53C0) stores them as dronesLeft/missilesLeft for the sender.",
    "In:  D_92AE dronesLeft, D_92B0 missilesLeft",
    "Out: command $90 drones missiles queued (zp_74 = drones, zp_75 = missiles)",
    "(confidence: medium)"
   ],
   "unit": "game/mapGenerator6F00"
  },
  "8518": {
   "routine": [
    "editDronesAndMissilesBody - Not a real entry point in variant A: it is the JSR beginValueEdit inside editDronesAndMissiles (A/Y/X must already hold the label and value). The label exists because the main program calls $8518 at $1460/$14D3, which is a routine of overlay variant B (menu screen).",
    "In:  A/Y = label, X = value (must be preset)",
    "Out: as editDronesAndMissiles"
   ],
   "unit": "game/mapGenerator6F00"
  },
  "8547": {
   "routine": [
    "runGameStartOptions - Called once from the new-game start sequence ($7AF7) after the setup has been saved and before unit placement. Sets gamePhase $7F, hides the viewport (sub_8364), draws the overview (side 0: drawOverviewMap, side 1: clearScreenAndRedrawGameScreen), shows the 'STANDARD/CUSTOM <type> SCENARIO - PRESS BUTTON WHEN READY.' banner (showScenarioBannerAndWaitForButton), copies gameTypeOptions bit 6 into D_8450 and (shifted to bit 7) into D_92F3. If CUSTOM rules are on it shows the custom summary message, prints the game type name and a 2-item STANDARD/CUSTOM menu (rows at column 28, width 10, runMenuLoopWithFlag); choosing CUSTOM (D_8450 = 1) runs editComcenSpeed, editTerrainPoints and editDronesAndMissiles. Then, for game types 4-6 (SLUGGERS, FULL WAR, DEFENDER), sub_8378 sets up the recycler, and finally it jumps to applyHandicapPoints.",
    "In:  D_0BA3 gameTypeOptions, D_0B9F playerSide, menu/text engine state",
    "Out: zp_B1 = $7F, D_8450, D_92F3, setup commands $A5/$90/$9C queued, screen redrawn",
    "(confidence: medium)"
   ],
   "unit": "game/mapGenerator6F00"
  },
  "85BC": {
   "routine": [
    "editComcenSpeed - Custom option 'COMCEN SPEED': starts from D_92B4 (own comcen speed setting 0..3), edits it with editValueWithJoystick (limit 4) and sends command $A5 kind 3 (cmdSetupOption writes $92B4 of the sender's side; $7B6E later maps the setting through the table at $78BF).",
    "In:  D_92B4",
    "Out: command $A5 03 value queued (zp_74 = 3, zp_75 = value)",
    "(confidence: medium)"
   ],
   "unit": "game/mapGenerator6F00"
  },
  "85DB": {
   "routine": [
    "beginValueEdit - Common prologue of the value editors: stores the label pointer (patched into $85EA/$85EC), sets editValue = X, clears the info panel in colour 8 and sets row 15 (sub_8441), prints the label centred, moves the text window left edge to column 29, starts a new line and records that row as menuFirstRow with menuSelectedItem = 0. Falls into the tail at $8601 which sets selectedUnit = $FE (plain menu, no unit) and the window width to 5 for the highlight bar.",
    "In:  A/Y = label string (bit-7 terminated), X = initial value",
    "Out: D_844F = X, D_91D5 = 0, D_91D6 = row, D_90F8 = $FE, zp_3B = $1D, zp_3C = 5, label printed"
   ],
   "unit": "game/mapGenerator6F00"
  },
  "8601": {
   "routine": [
    "setValueEditWindow - Tail of beginValueEdit: selectedUnit = $FE and textWindowWidth = 5. The sub_ label only exists because $8601 is a routine entry in overlay variant B; in A it is only reached by fall-through.",
    "In:  -",
    "Out: D_90F8 = $FE, zp_3C = 5"
   ],
   "unit": "game/mapGenerator6F00"
  },
  "860B": {
   "routine": [
    "editOptionBitOnOff - Two-row OFF/ON menu for one bit of gameTypeOptions, used by the scenario setup screen for 'UNIT MENUS' (mask $08, $82B3) and 'DAMAGE' (mask $10, $82BC). Stores the label pointer and mask in self-modifying operands, hides the viewport, clears the panel to row 14, prints the label centred with the current state pre-selected (row 0 = OFF from $064D, row 1 = ON via sub_C137), runs runMenuLoopWithFlag with width 5 at column 27 and then clears or sets the mask bit in D_0BA3 according to the chosen row.",
    "In:  A/Y = label string, X = bit mask, D_0BA3",
    "Out: D_0BA3 bit updated; D_91D4 = 2, D_91D5 = choice"
   ],
   "unit": "game/mapGenerator6F00"
  },
  "862D": {
   "routine": [
    "runOnOffMenuAndApplyBit - Tail of editOptionBitOnOff (menuItemCount = 2, print OFF/ON, run the menu, apply the mask at $8621+1). Only an entry point in overlay variant B.",
    "In:  label already printed, mask operand at $8622",
    "Out: D_0BA3 updated"
   ],
   "unit": "game/mapGenerator6F00"
  },
  "8632": {
   "routine": [
    "printOnOffMenuAndApplyBit - Inner tail of editOptionBitOnOff starting at the OFF/ON printing. The main program's JSR $8632 at $176B targets overlay variant B's routine of the same address, not this code.",
    "In:  menu state, mask operand at $8622",
    "Out: D_0BA3 updated"
   ],
   "unit": "game/mapGenerator6F00"
  },
  "8658": {
   "routine": [
    "handleStarKeyToggle - Per-frame hook called while overlay A is resident in non-battle phases (battlefield loop via the trampoline $25C4 when zp_B1 != 0, and runMenuLoop at $6002 when zp_B1 == $FF). If no chat entry is pending (D_929C bit 7 clear) and the last key is '*' ($AA) it consumes the key, toggles bit 7 of D_92F3 and falls into showToggleStateMessage (returns C = 1). Otherwise returns C = 0. During unit placement the flag means DESTROY mode (clicking an own unit sends $91 self destruct, $209F); on the scenario setup screen it means CUSTOM rules.",
    "In:  D_929C chatState, D_9248 lastKeyCode, D_92F3",
    "Out: C = 1 and D_92F3 bit 7 toggled when '*' was pressed (D_9248 = $FF), else C = 0"
   ],
   "unit": "game/mapGenerator6F00"
  },
  "8673": {
   "routine": [
    "showToggleStateMessage - Shows the state of the '*' toggle: empties the message queue (tail = head), patches the word 'ON' ($0650) or 'OFF' ($064D) into the message text (3 bytes copied to the address patched at $869A/$869B: $EFDF inside 'CUSTOM OFF' or $EFEA inside 'DESTROY OFF'), sets persistentMessageId to $1E while the mode is on (or to the operand at $868D when off: 0 on the setup screen, 10 = 'SETUP UNITS...' during placement), clears the message timer and queues message $1E. Returns C = 1. Also called directly from the setup screen ($8253) to show 'CUSTOM ON' initially.",
    "In:  D_92F3 bit 7, operands patched by $820B/$821F or setDestroyModeMessage",
    "Out: D_9252 = D_9251, D_92A1, zp_72 = 0, message $1E queued, C = 1, zp_48/49 = word pointer"
   ],
   "unit": "game/mapGenerator6F00"
  },
  "86AA": {
   "routine": [
    "setDestroyModeMessage - Prepares the '*' toggle for the unit-placement phase (called at $7B4B before the placement loop): off-state persistent message = 10 ('SETUP UNITS. PRESS F 1 WHEN DONE.'), message slot $1E -> 'DESTROY OFF' ($EFE2 in the T34 $EC00 sub-overlay), and the ON/OFF patch address = $EFEA (the 'OFF' inside that string). The setup-screen code at $820B/$821F does the same with 'CUSTOM OFF' ($EFD8/$EFDF) and off-message 0.",
    "In:  -",
    "Out: $868D = $0A, message slot $1E (D_0500/D_0524 index $1E) = $EFE2, $869A/$869B = $EFEA"
   ],
   "unit": "game/mapGenerator6F00"
  },
  "86C3": {
   "routine": [
    "showCustomSummaryMessage - Builds the status line 'CUSTOM:' [+ 'MAP,' when D_0BA3 bit 7 is set] + ' MENUS OFF,/ON,  DAMAGE OFF./ON.' into the buffer at $0332 (prefix from $CC19 'CUSTOM' and $EF77 'MAP TYPE'; the option part is filled into the template at $0636 ' MENUS XXXX DAMAGE XXX.' by appendOnOffWord with bits 3 and 4 of gameTypeOptions) and registers it as the persistent message $1E (setPersistentMessage). Called when a game starts or resumes with CUSTOM rules ($7D4B, $856D).",
    "In:  D_0BA3 bits 3, 4, 7; templates at $0636 and $064D; strings $CC19, $EF77",
    "Out: $0332.. text, $0636 template overwritten, message slot $1E = $0332, D_92A1 = $1E, message queued"
   ],
   "unit": "game/mapGenerator6F00"
  },
  "8722": {
   "routine": [
    "setPersistentMessage - Registers the string A/Y as message slot $1E (sub_C924 writes the pointer tables at $0500/$0524), makes $1E the persistent status message (D_92A1) and queues it with sub_C566. Used for 'PRESS BUTTON WHEN READY.' and the custom summary; the entry at $8722 is also the tail of showCustomSummaryMessage.",
    "In:  A/Y = string pointer",
    "Out: D_0500[$1E]/D_0524[$1E], D_92A1 = $1E, message queued, X = $1E"
   ],
   "unit": "game/mapGenerator6F00"
  },
  "872E": {
   "routine": [
    "appendOnOffWord - Writes 'OFF,' (A = 0) or 'ON, ' (A != 0) into the template at $0636 starting at index X: copies the bit-7 terminated word from $064D (OFF) or $0650 (ON), adds a comma, and for ON adds a trailing space so both forms are 4 characters; returns X pointing at the comma.",
    "In:  A = option state (0 = off), X = destination index in $0636",
    "Out: $0636+X.. written, X = index of the comma"
   ],
   "unit": "game/mapGenerator6F00"
  },
  "8756": {
   "routine": [
    "showScenarioBannerAndWaitForButton - Clears the info panel (row 15) and prints, centred in 13 columns, 'STANDARD' or 'CUSTOM' (table $CC11, chosen by D_0BA3 bit 6), the game type name (table $CCC8, bits 0-2) and the string '  SCENARIO' ($CC04). Sets 'PRESS BUTTON WHEN READY.' ($CBBE) as persistent message $1E, then loops on serviceCommandExchange until checkFirePressed reports a click, and finally clears the status line (sub_C39F).",
    "In:  D_0BA3, fire button, link",
    "Out: info panel text, D_92A1 = $1E, status line cleared"
   ],
   "unit": "game/mapGenerator6F00"
  },
  "8795": {
   "routine": [
    "printCentredTableEntry - Sets textCentreWidth (D_90EF) to 13 and prints entry X of a table of 8-byte bit-7 terminated strings at A/Y through sub_C100 (used for the STANDARD/CUSTOM words and the game type names).",
    "In:  A/Y = table base, X = entry index",
    "Out: text printed, D_90EF = $0D"
   ],
   "unit": "game/mapGenerator6F00"
  },
  "844F": {
   "unit": "game/mapGenerator6F00",
   "block": [
    "editValue: byteTable, 1 bytes. current value of the joystick value editor"
   ]
  },
  "8450": {
   "unit": "game/mapGenerator6F00",
   "block": [
    "customSetupChosen: byteTable, 1 bytes. nonzero = CUSTOM options chosen at game start"
   ]
  },
  "879F": {
   "unit": "game/mapGenerator6F00",
   "block": [
    "msgWonOnPointsTail: text, 13 bytes. 'ON ON POINTS!' - the tail of message $12 'WE WON ON POINTS!' whose first four bytes ($879B-$879E) are overlaid by code in variant A (pla / jmp sub_C100); overlay variant B holds the complete string at $879B. Message pointer table slots $11-$16 ($0500/$0524) point to $8789, $879B, $87AC, $87BA, $87CE, $87E3 and are only valid with variant B resident."
   ]
  },
  "87AC": {
   "unit": "game/mapGenerator6F00",
   "block": [
    "msgItWasADraw: text, 14 bytes. 'IT WAS A DRAW.' = message $13 (game result)"
   ]
  },
  "87BA": {
   "unit": "game/mapGenerator6F00",
   "block": [
    "msgWeWereKnockedOut: text, 20 bytes. 'WE WERE KNOCKED OUT!' = message $14"
   ]
  },
  "87CE": {
   "unit": "game/mapGenerator6F00",
   "block": [
    "msgWeWonByKnockout: text, 21 bytes. 'WE WON BY A KNOCKOUT!' = message $15. Comm build 1 (T35, solo trainer) reuses $87CE+unit as a per-unit work table ($EB29/$EB53 store the game clock there), so with the trainer resident these strings are clobbered."
   ]
  },
  "87E3": {
   "unit": "game/mapGenerator6F00",
   "block": [
    "msgGameOver: text, 10 bytes. 'GAME OVER.' = message $16"
   ]
  },
  "87ED": {
   "unit": "game/mapGenerator6F00",
   "block": [
    "msgDroneAlert: text, 12 bytes. 'DRONE ALERT!' - installed as message slot $20 by overlay A at $7BA6 (sub_C924 X=$20)"
   ]
  },
  "87F9": {
   "unit": "game/mapGenerator6F00",
   "block": [
    "overlayTailPadding: unknown, 6 bytes. six zero bytes of padding before the variant flag"
   ]
  },
  "87FF": {
   "unit": "game/mapGenerator6F00",
   "block": [
    "overlayVariantFlag: byteTable, 1 bytes. $00 = variant A, $FF = variant B (bit 7 tested by the main program at $1036/$1080)"
   ]
  }
 }
}