{
 "_comment": "generated by tools/mergeSurvey.py from survey/*.json - do not edit by hand",
 "notes": {
  "6F00": {
   "routine": [
    "drawDroneHeadingPanel - Draws the top-left panel of the drone screen: window $0D (left 1, top 3, 9x9), the 7x7 character picture at $97D1 at row 4 column 2, the 5x5 picture at $97B6 at row 5 column 3 and a 3x3 colour fill of $6E at row 6 column 4. Sprites 3 and 5 (heading line and direction arrow, positioned at X=$37/$38 Y=$64 by the screen-2 entry) sit on top of this panel, so it is the drone heading / compass indicator.",
    "In:  none (colour state D_2A29-D_2A2C as left by buildDroneScreen)",
    "Out: frame, characters and colour cells drawn; D_2A2D=6, D_2A2E=4, D_9221/D_9222 set by the block drawer",
    "(confidence: medium)"
   ],
   "unit": "game/comcenScreens6F00"
  },
  "6F36": {
   "routine": [
    "drawDroneFuelPanel - Draws the bottom-left panel of the drone screen: window $0E (left 1, top 12, 9x9), the 7x7 picture at $97D1 at row 13 column 2 in the fixed colour $BC, then the three label characters $57,$58,$59 at row 17 columns 4-6 in colour $0C (the caption of the fuel read-out that drawDroneFuelDigits later prints at row 15).",
    "In:  none",
    "Out: frame/characters drawn; D_2A29 and D_2A2A cleared on exit; D_2A2B = 0; zp_18=$11, zp_19=6",
    "(confidence: medium)"
   ],
   "unit": "game/comcenScreens6F00"
  },
  "6F89": {
   "routine": [
    "drawDroneRadarFrame - Builds the radar-noise masks (buildRadarNoiseMasks), draws the two nested frames around the radar area (windows $0F: left 11, top 4, 18x16 and $10: left 12, top 5, 16x14) and falls into clearRadarWindowBlack, which blanks the 14x12 radar area and centres the view on the own comcen. Called once by buildDroneScreen. Note: sub_6F91 ($6F91) is only a fall-through point in variant B; the JSRs to it in XREF come from overlay A.",
    "In:  D_9236 comcenUnit",
    "Out: frames drawn, D_9210 masks, radar area cleared, zp_A5/zp_A6 = comcen - (6,5), D_920F = 0, D_7134 = 0"
   ],
   "unit": "game/comcenScreens6F00"
  },
  "6F99": {
   "routine": [
    "drawDroneFuelDigits - Shows droneFuel (D_9220) as three tall two-character digits at row 15 columns 4-6: converts it to decimal with sub_C428 ($C44E hundreds, $C44F tens, $C450 ones) and draws them with drawTallGlyph in a fixed colour chosen by the level: $30 for >= 21, $B7 for 11-20, $21 below 11; for even values the two colour nibbles are swapped so a low read-out pulses as it counts down. Called at screen build, at launch (fuel 100), every radar update and when the drone is lost (fuel 0).",
    "In:  D_9220 droneFuel",
    "Out: three glyph pairs at rows 15-16; D_C44E-D_C450; D_2A29/D_2A2A cleared; zp_18=$0F, zp_19=6, zp_1A scratch"
   ],
   "unit": "game/comcenScreens6F00"
  },
  "6FF5": {
   "routine": [
    "drawDroneCountPanel - Draws the top-right panel of the drone screen: full-screen text window (sub_C0D7), window $11 (left 29, top 3, 11x7) with inner corner marks, window $12 (left 32, top 4, 5x4), a 3x2 colour fill of $30 at row 5 column 33 (the box for the drones-left digit) and the caption 'DRONE' ($868C) at row 7 column 32 in text colour $CF with OR draw mode, reset afterwards.",
    "In:  none",
    "Out: frames/text drawn; zp_3D=$CF, zp_3F=$20, zp_40=7; draw modes reset"
   ],
   "unit": "game/comcenScreens6F00"
  },
  "7036": {
   "routine": [
    "drawDroneCountDigit - Draws dronesLeftDisplay (D_9233) as one tall digit at row 5 column 34 in fixed colour $30, or in colour 0 (invisible) when no drones are left. The 'bne L_7055' at $7053 is a no-op branch (both paths continue at $7055). Called by buildDroneScreen and after each launch.",
    "In:  D_9233 dronesLeftDisplay (0-9)",
    "Out: glyph pair at rows 5-6 column 34; D_2A29/D_2A2A cleared; zp_18=5, zp_19=$22"
   ],
   "unit": "game/comcenScreens6F00"
  },
  "7061": {
   "routine": [
    "blinkDroneCountBox - Blinks the background of the drones-left box: fills the 3x2 colour cells at row 5 column 33 with $30 on odd frames (zp_45 bit 0) or with the nibble-swapped $03 on even frames. The screen-2 loops call it every 11 or 40 frames while fewer than two drones remain.",
    "In:  zp_45 frameCounter bit 0",
    "Out: 6 colour cells; D_2A2D=5, D_2A2E=$21, D_2A2A; zp_1A scratch"
   ],
   "unit": "game/comcenScreens6F00"
  },
  "708D": {
   "routine": [
    "drawLaunchStrikeButtons - Draws the right-hand button panel of the drone screen: window $13 (left 29, top 10, 14x11) with inner corner marks, the 2x2 button icon block at $9804 at row 12 column 34 coloured $05 with the caption 'LAUNCH' ($8691) at row 14 column 32 (colour $CD), and the same icon at row 16 column 34 with 'STRIKE' ($8697) at row 18 column 32 (colour $C7). The screen-2 loops later recolour these 2x2 icons ($05 when pressed, $02 for strike, XOR $08 blink).",
    "In:  none",
    "Out: frame, icons and text drawn; zp_3D, zp_3F, zp_40 set; draw modes reset"
   ],
   "unit": "game/comcenScreens6F00"
  },
  "70F5": {
   "routine": [
    "buildDroneScreen - Builds the complete drone screen (game screen 2, entered from $7FE6): clears the colour state D_2A29-D_2A2C, sets border and background to $0C, radarBackgroundColour D_9226 = $0A and zp_41 = 1, then draws the drone count panel, count digit, LAUNCH/STRIKE buttons, heading panel, fuel panel, fuel digits and the radar frames, finally the status-line window (sub_28E1) and jumps to L_C5D7 to re-show the current status message.",
    "In:  D_9220, D_9233, D_9236",
    "Out: whole screen redrawn; D_9003/D_9004 = $0C, D_9226 = $0A, zp_41 = 1, D_2A29-D_2A2C = 0"
   ],
   "unit": "game/comcenScreens6F00"
  },
  "7135": {
   "routine": [
    "clearRadarWindowGrey - Sets radarWipeColour D_7134 = $BB and falls into the shared tail at $7141: fills the 14x12 radar area (row 6 column 13) with that colour, clears the ghost countdown D_920F and sets the view origin zp_A5/zp_A6 to the own comcen minus (6,5) so the comcen sits at the radar centre. Called right after a launch; the nonzero D_7134 also makes the following drawRadarIrisWipe animate with 3-frame pauses.",
    "In:  D_9236 comcenUnit, D_F640/D_F6A4",
    "Out: D_7134 = $BB, radar area colour $BB, D_920F = 0, zp_A5/zp_A6"
   ],
   "unit": "game/comcenScreens6F00"
  },
  "713C": {
   "routine": [
    "clearRadarWindowBlack - Same as clearRadarWindowGrey but with D_7134 = 0: blanks the radar area to colour 0, clears D_920F and centres the view on the own comcen. Used at screen build and when a drone flight ends; with D_7134 = 0 a later iris wipe runs without delays.",
    "In:  D_9236, D_F640/D_F6A4",
    "Out: D_7134 = 0, radar area black, D_920F = 0, zp_A5/zp_A6 = comcen - (6,5)"
   ],
   "unit": "game/comcenScreens6F00"
  },
  "7163": {
   "routine": [
    "playDroneLostAnimation - Visual for a drone that is destroyed, detonated or out of fuel: forces fixed colour, switches sprite 4 (comcen rings) off, flashes the radar area white and wipes it (flashRadarAndWipe), then turns sprite 2 (the drone icon) into a filled disc (circles of radius 3,2,1 with spritePlotMode $40) in white and lets it shrink and fade (erase radius 3 -> colour $0F, radius 2 -> $0C, radius 1 -> $0B with 3/3/5/6 game frames between), disables sprite 2, zeroes droneFuel, redraws the fuel digits and waits 30 frames. The 'sta zp_30' at $716A and the store to D_7271 are dead (zp_30 is only used inside magnifyWindow2x, D_7271 is never read).",
    "In:  D_920B (saved but unused)",
    "Out: D_9022 bits 4 and 2 cleared, sprite 2 shape/colour changed, D_91F3 = 0, D_9220 = 0, D_2A29/D_2A2A = 0, ~50 game frames elapse"
   ],
   "unit": "game/comcenScreens6F00"
  },
  "7204": {
   "routine": [
    "flashRadarAndWipe - Fills the 14x12 radar area (row 6, column 13) with colour 1 (white), runs 2 game frames, clears the animation flag D_7134 and falls into drawRadarIrisWipe so the wipe closes instantly. Only used by playDroneLostAnimation.",
    "In:  none",
    "Out: radar area white, D_7134 = 0, then see drawRadarIrisWipe"
   ],
   "unit": "game/comcenScreens6F00"
  },
  "7221": {
   "routine": [
    "drawRadarIrisWipe - Iris effect over the radar area: draws window frame $16 (the radar area itself, 14x12 at row 6 column 13), then repeatedly draws a frame box from the 9-byte all-$03 tile set at $9848 starting at row 6 column 14 with 12x12 cells, shrinking by two and moving in by one each pass (drawFrameBox decrements the size once, this loop once more) until the height drops below 3, waiting for raster line $B0 before each frame and, when D_7134 is nonzero, running 3 game frames per pass; finally draws the 2x2 block of character $05 at $980A at the centre (row 11, column 19). Colours come from the current colour override ($AA after a launch, 1 during the loss animation).",
    "In:  D_7134 animate flag, D_2A29/D_2A2A colour",
    "Out: radar area covered by concentric frames; D_2A2D = 11, D_2A2E = $13, D_9221/D_9222 clobbered"
   ],
   "unit": "game/comcenScreens6F00"
  },
  "727A": {
   "routine": [
    "buildRadarNoiseMasks - Fills the 16-byte table radarNoiseMasks (D_9210) with $FF and clears four random bits in each byte (bit chosen by nextGameRandom & 7 through clearBitMaskTable). drawRadarGlyph ANDs glyph rows with eight consecutive masks to give unspotted enemy blips a static/noise look. Called by drawDroneRadarFrame, drawDroneRadarWindow and the screen-1 map redraw ($7926) and unit marker code ($7BA2).",
    "In:  RNG $57-$59",
    "Out: D_9210-D_921F, zp_1A = 0, Y = $FF"
   ],
   "unit": "game/comcenScreens6F00"
  },
  "72A1": {
   "routine": [
    "applyDroneBlast - Hidden as .byte in the listing but real code, called by cmdDroneDetonate ($536B) with A = $19: damages the 2x2 cells whose top-left corner is (zp_74, zp_75) - (x,y), (x+1,y), (x+1,y+1), (x,y+1) - through the inner routine at $72B3. The blast strength is stored in the operand at $7311 so that each hit ends with 'LDA #strength / JSR applyHitToUnit ($3B22)'.",
    "In:  A = blast strength, zp_74/zp_75 = top-left map cell",
    "Out: units in the four cells damaged; $7311 patched; see applyDroneBlastToCell"
   ],
   "unit": "game/comcenScreens6F00"
  },
  "72B3": {
   "routine": [
    "applyDroneBlastToCell - Inner part of applyDroneBlast for one cell (zp_74, zp_75): returns if the cell is off the map or holds no unit (readMapCell $25FA, bit 7 clear). With strength >= 15 and game type != 2 (THE BOMB) the unit is stunned (unitStunTable $F960 |= $0F) and halted (haltStunnedUnit $3FAC); if it is the own comcen: updateComcenCloakLock ($5151), then with the DAMAGE option on and not in solo/film mode the comcen is knocked out (D_9242/D_9243/D_9244 = $80, comcenStunStatus $922A = $FF), otherwise message $0C is queued (sub_C566). The 'LDY $9236 / BNE' after the message is always taken because comcenUnit is 49 or 99. Finally applyHitToUnit with A = strength, Y = unit.",
    "In:  zp_74/zp_75 cell, $7311 strength, D_0BA3 gameTypeOptions, D_0B9B, D_9236",
    "Out: unit damage/stun, possibly knock-out flags; A = strength, Y = unit"
   ],
   "unit": "game/comcenScreens6F00"
  },
  "731E": {
   "routine": [
    "steerDrone - Moves the locally controlled drone one cell per command exchange (called from sendOutgoingPacket $5639 while droneStateFlags bit 6 is set, before the $8D report is queued). Converts the last joystick direction zp_5E to a clockwise heading through D_7FCE, turns droneHeading (D_920B) one step toward it the short way round (playing sound $10 on a turn), then translates the heading through headingToDirectionTable and the direction delta tables D_20F0/D_1FB2 into dx/dy and adds them to droneCol/droneRow (D_92F6/D_92F7).",
    "In:  zp_5E joystick direction code, D_920B, D_92F6/D_92F7",
    "Out: D_920B updated (0-7), D_92F6 += dx, D_92F7 += dy, sound $10 on turn; X/Y clobbered"
   ],
   "unit": "game/comcenScreens6F00"
  },
  "7396": {
   "routine": [
    "drawRadarGlyph - Plots one 8x8 map glyph at the bitmap cell zp_B4/zp_B5 and writes its colour to (zp_B6),Y: glyph source is $9C6E + (A & $7F)*8 via setTerrainTilePtr. If radarHalfToneFlag D_922D is set (cloaked unit) the even rows are blanked and only odd rows copied; if A bit 7 is set the rows are ANDed with eight consecutive radarNoiseMasks starting at a random index (ghost blip). Then D_922D is cleared, zp_B4 advances by 8 to the next cell, and the colour is stored: on screen 1 the raw zp_32 byte, otherwise (zp_32 << 4) | radarBackgroundColour. The D_2A29 test at $73F9 can never succeed because setTerrainTilePtr has just cleared it. Also called from drawScreen1Cell ($5DF5) in the main program. A/X/Y are preserved via self-modified operands.",
    "In:  A = glyph (bit 7 = noise), X/Y preserved (Y = colour offset), zp_32 radarGlyphColour, zp_B4-zp_B7, D_922D, D_90FB, D_9226",
    "Out: bitmap cell and colour cell written; zp_B4/zp_B5 += 8; D_922D = 0; A = glyph & $7F, X, Y restored; D_7395 = Y"
   ],
   "unit": "game/comcenScreens6F00"
  },
  "741C": {
   "routine": [
    "drawDroneRadarWindow - Redraws the 14x12 radar view of the drone screen (character rows 6-17, columns 13-26, one character per map cell) from the view origin zp_A5/zp_A6: sets radarTerrainColour = 8, own/enemy unit colours from D_9224/D_9225, rebuilds the noise masks, fetches the map pointer with getViewportOriginPointer, computes the bitmap pointer from the $9300 row/column tables (row 6, column 13) and the screen RAM pointer (row 6 + 13), waits for raster line $90 and then for each row (X) and column (Y) runs getRadarCellGlyph and drawRadarGlyph. zp_A5/zp_A6 are walked cell by cell for the off-map test and restored from D_90F6/D_90F7 at the end. Called from the per-turn radar update sub_8279.",
    "In:  zp_A5/zp_A6 view origin, map at $F000, unit arrays, D_9224/D_9225",
    "Out: radar area redrawn; D_9237-D_9239, D_9210, D_90F6/D_90F7, zp_48/zp_49, zp_B4-zp_B7 clobbered; D_2A29 = 0"
   ],
   "unit": "game/comcenScreens6F00"
  },
  "74C3": {
   "routine": [
    "getRadarCellGlyph - Converts map cell byte A at (zp_A5, zp_A6) into a radar glyph and colour (X/Y preserved). Off-map coordinates (>= 40) give one of four hatch glyphs $28-$2B picked by (row+col)&3. Terrain cells >= $40 give glyph cell-$40 in radarTerrainColour (cells below $40, not expected, give cell-$20). A unit cell ($80|index) gives unitGlyphBaseTable[type]+facing in the own colour D_9237; for the enemy half of the unit array (the BCS at $750A is a side-dependent opcode copied from $206B by loadOverlayB) it uses D_9238 if the unit is spotted (unitEnergy bit 7), otherwise the terrain under the unit is shown except that every 1-8th unspotted enemy (countdown D_920F reseeded from the RNG) is drawn as a noise 'ghost' (glyph | $80) in the terrain colour. Dead units show their underlying terrain. A cloaked unit (unitCloakFlags bit 7) sets radarHalfToneFlag so drawRadarGlyph draws it on alternate scanlines.",
    "In:  A = map cell, zp_A5/zp_A6 cell coordinates, D_9237-D_9239, D_920F, unit arrays F76C/F708/F898/F9C4/FAF0, D_0799",
    "Out: A = glyph index (bit 7 = draw with noise), zp_32 = colour, D_922D set for cloaked units, D_920F updated; X/Y restored"
   ],
   "unit": "game/comcenScreens6F00"
  },
  "7552": {
   "routine": [
    "playStrikeZoomAnimation - Effect for a drone strike: zooms into the centre of the radar four times with magnifyWindow2x on the 7x6 cell block at row 9 column 16, running 12, 10, 8 and 6 game frames between steps (D_7551 counts 3..0), waits 6 more frames and then jumps to playDroneLostAnimation. Called from the strike wait loop ($81CD) after sound $11.",
    "In:  none",
    "Out: radar area magnified and then cleared; D_7551 = $FF; see playDroneLostAnimation"
   ],
   "unit": "game/comcenScreens6F00"
  },
  "7587": {
   "routine": [
    "runMissileScreen - Entry of game screen 1 (jumped to by the screen dispatcher sub_06BA): clears cursorMovedFlag, copies the own missile count D_92B0 into missilesLeftDisplay, unpacks the direction sprite shapes at $FD70 into $0200 and builds the screen with sub_7A32 (21x17 cell map window at character (3,3) centred on the own comcen at grid cell (10,8)). Continues at missileScreenResume.",
    "In:  D_92B0, D_9236",
    "Out: D_7586 = 0, D_9232, screen built; never returns (loop)"
   ],
   "unit": "game/comcenScreens6F00"
  },
  "759C": {
   "routine": [
    "missileScreenResume - Re-entry after the knock-out sequence ($0764) and main loop of the missile screen. Sets screenLockedFlag D_7585 = $80 when the game type is Scrimmage (no comcen) or the own comcen is cloaked (D_9245 bit 7); if not locked redraws the map window (sub_7688), unit markers (sub_7BA2), resets the sweep (sub_78CB) and shows the comcen cross (sprite 2, sub_3383). Loop at $75B9: updateGameFrame; screen switch via sub_06BA unless D_92F9 bit 0; every 9 frames (zp_6F) XOR-blinks the 2x2 button at row 10 column 32 and, with fewer than two missiles, the count box (sub_7AEC); when locked nothing else runs. Otherwise: D_920E refresh -> redraw map and drone detonation effect (sub_79D8); radar sweep (sub_7894); a fire press (sub_0ED5) after the cursor has moved launches a missile if missiles remain - always when missileJamFlag D_9243 is 0, only on a random bit when set: decrement the count, redraw it, fill the button $05, sound $0F, queue command $90 x y at the cursor cell (sub_7992), draw the trajectory (sub_7713), ring burst at the cursor and sub_79A1; else joystick input moves the grid cursor zp_67/zp_68 through sub_76B2 with auto-repeat (10 frames first, then 4, via D_91CA/D_91C9) and sets cursorMovedFlag = $7F.",
    "In:  D_0BA3, D_9245, D_920E, D_9243, D_9232, zp_5D, zp_6C, zp_6F, D_91C9/D_91CA, D_1FA6",
    "Out: D_7585, D_7586, zp_6C, zp_6F, D_91C9 = $7F, commands queued, sprites/screen updated; never returns"
   ],
   "unit": "game/comcenScreens6F00"
  },
  "7134": {
   "unit": "game/comcenScreens6F00",
   "block": [
    "radarWipeColour: byteTable, 1 bytes. Fill colour / slow-animation flag for the radar wipe ($BB or 0)"
   ]
  },
  "715D": {
   "unit": "game/comcenScreens6F00",
   "block": [
    "unusedBytes715D: unknown, 6 bytes. $21,$A1,$21,$81,$A1,$11 between the RTS at $715C and playDroneLostAnimation; not referenced by variant B (L_7161 is a branch target only in variant A). Probably leftover data or code fragment."
   ]
  },
  "7271": {
   "unit": "game/comcenScreens6F00",
   "block": [
    "savedDroneHeading: byteTable, 1 bytes. Dead copy of droneHeading written by playDroneLostAnimation"
   ]
  },
  "7272": {
   "unit": "game/comcenScreens6F00",
   "block": [
    "clearBitMaskTable: byteTable, 8 bytes. $FE,$FD,$FB,$F7,$EF,$DF,$BF,$7F: AND masks clearing bit n, used by buildRadarNoiseMasks"
   ]
  },
  "7316": {
   "unit": "game/comcenScreens6F00",
   "block": [
    "headingToDirectionTable: byteTable, 8 bytes. Clockwise heading 0-7 (N,NE,E,SE,S,SW,W,NW) -> direction code 0-7 (0 N,1 S,2 E,3 W,4 NW,5 NE,6 SE,7 SW): 0,5,2,6,1,7,3,4. Used by steerDrone and by the comm sub-overlay at $EF38."
   ]
  },
  "7395": {
   "unit": "game/comcenScreens6F00",
   "block": [
    "radarGlyphSavedY: byteTable, 1 bytes. Saved Y register of drawRadarGlyph"
   ]
  },
  "7551": {
   "unit": "game/comcenScreens6F00",
   "block": [
    "zoomStepCounter: byteTable, 1 bytes. Counter of playStrikeZoomAnimation"
   ]
  },
  "7585": {
   "unit": "game/comcenScreens6F00",
   "block": [
    "missileScreenLocked: byteTable, 1 bytes. Bit 7 = missile screen inactive"
   ]
  },
  "7586": {
   "unit": "game/comcenScreens6F00",
   "block": [
    "cursorMovedFlag: byteTable, 1 bytes. 0 until the missile cursor was moved, then $7F"
   ]
  },
  "7894": {
   "routine": [
    "updateRadarSweep - Per-frame driver of the radar sweep animation on screen 1 (the RADAR/MISSILE screen at $7587). When frameTick92FB reaches the self-modified threshold at $7898 it advances the threshold by 2 and performs one step: the step number radarSweepStep ($7891) is the grid row that gets lit (rows 0-16 via paintRadarSweepRow) and row step-14 (when 0..16) is repainted dark, so a 14-row light band wipes down the 21x17 radar window. Steps 0-30 take about 62 frames; at step 31 the sweep is restarted by restartRadarSweep. Called from the screen-1 main loop ($75F7) and from the missile flight animation ($77F9).",
    "In:  frameTick92FB ($92FB), radarSweepStep ($7891), radarSweepNextTick ($7898 immediate operand)",
    "Out: $7891 incremented (or reset), $7892 = row being painted, $7898 += 2, screen RAM colour cells of the radar window rows; A/X/Y clobbered"
   ],
   "unit": "game/comcenScreens6F00"
  },
  "78CB": {
   "routine": [
    "restartRadarSweep - Restarts the radar sweep: clears radarSweepStep, radarSweepDarkFlag, frameTick92FB and the tick threshold at $7898. If the comcen radar system is damaged (comcenRadarStatus $9243 nonzero) a random draw (nextGameRandom bit 7) makes the whole next sweep dark (radarSweepDarkFlag = $FF), i.e. the damaged radar fails to show anything on about half of its sweeps. Called when screen 1 is built ($75B3) and at the end of every sweep.",
    "In:  $9243 comcenRadarStatus, RNG $57-$59",
    "Out: $7891 = 0, $7893 = 0 or $FF, $92FB = 0, $7898 = 0; X clobbered"
   ],
   "unit": "game/comcenScreens6F00"
  },
  "78E7": {
   "routine": [
    "paintRadarSweepRow - Repaints the screen-RAM colour bytes of the 21 cells of radar grid row A (0-16, screen row A+3, columns 3-23): computes the screen RAM ($8C00, via the row tables D_9385/D_939E whose index 0 is row 3) and colour RAM ($D800) pointers, then for each cell writes $F0 (multicolour %01 pixels light grey = visible) when the cell's colour RAM nibble is 1 (a spotted enemy unit) and the radar is intact, or when this row is the current sweep row (radarSweepRow == radarSweepStep) and the sweep is not dark; otherwise writes $00 (pixels black = hidden). This is how the sweep band reveals and re-hides the radar blips.",
    "In:  A = grid row 0-16, $7891 radarSweepStep, $7892 radarSweepRow, $7893 radarSweepDarkFlag, $9243 comcenRadarStatus, colour RAM row",
    "Out: 21 screen RAM bytes at $8C00+(row+3)*40+3 set to $F0/$00; zp_B6/B7 = colour RAM pointer, zp_B8/B9 = screen RAM pointer; A/X/Y clobbered",
    "(confidence: medium)"
   ],
   "unit": "game/comcenScreens6F00"
  },
  "7926": {
   "routine": [
    "redrawEnemyUnitsOnRadar - Re-randomises the 16 noise masks at $9210 (sub_727A) and redraws every unit of the opposing side on the radar window: starts at unit comcenUnit EOR $52 (99 for comcen 49, 49 for comcen 99) and walks 50 units downwards, passing cell value $80|unit (alive) or the terrain byte saved under a dead unit (skipped when $FF = not on the map) with the unit's column/row in $54-$56 to drawScreen1Cell ($5D7F). Called from the screen-1 refresh sub_7688 (at screen build and whenever statsNeedRefresh $920E is set after a turn), so enemy blips move and their noise pattern changes every turn.",
    "In:  $9236 comcenUnit, unit arrays $F640/$F6A4/$F76C/$FAF0",
    "Out: cells redrawn on screen 1; $9210-$921F new masks; zp_18 = unit index, zp_19 = counter, $54-$56"
   ],
   "unit": "game/comcenScreens6F00"
  },
  "7959": {
   "routine": [
    "scrollRadarOnComcenMove - Entered by JMP from drawScreen1Cell ($5D8A) when the cell being redrawn is the player's comcen. Computes the radar grid origin from the comcen position (getRadarGridOrigin); if it equals the current view origin zp_A5/A6 nothing happens. Otherwise the comcen has moved: unless the target cursor sits on the comcen cell (10,8) it is shifted by (old origin - new origin) through $76EC so that it keeps pointing at the same map cell (left unchanged if that would leave the grid), then the whole radar map is redrawn (drawRadarMap) and the sweep row restarts at 0.",
    "In:  $9236 comcenUnit, zp_A5/A6 view origin, zp_67/68 radar cursor",
    "Out: zp_A5/A6 = new origin, zp_67/68 and sprite 2 position possibly updated, radar window redrawn, $7891 = 0",
    "(confidence: medium)"
   ],
   "unit": "game/comcenScreens6F00"
  },
  "7987": {
   "routine": [
    "isRadarCursorOnComcen - Compares the radar target cursor (zp_67 column, zp_68 row) with the comcen's fixed grid cell (10,8), the centre of the 21x17 radar window. Returns Z=1 when the cursor is on the comcen.",
    "In:  zp_67, zp_68",
    "Out: Z flag (set = cursor on comcen cell); A clobbered"
   ],
   "unit": "game/comcenScreens6F00"
  },
  "7992": {
   "routine": [
    "radarCursorToMapCoords - Converts the radar cursor grid position into map coordinates for an outgoing command: cmdParam1 (zp_74) = zp_67 + view origin column, cmdParam2 (zp_75) = zp_68 + view origin row. Used before queueing the missile strike command $90 ($7667) and by checkStrikeHitsDrone.",
    "In:  zp_67/68 radar cursor, zp_A5/A6 view origin",
    "Out: zp_74 = map column, zp_75 = map row; A clobbered"
   ],
   "unit": "game/comcenScreens6F00"
  },
  "79A1": {
   "routine": [
    "checkStrikeHitsDrone - After a missile strike is fired: if a drone is active (droneStateFlags $920D nonzero) and still in flight (droneFlags $92F9 == 0), converts the strike cursor to map coordinates and tests whether the target cell is one of the 2x2 cells occupied by the drone (droneCol - col and droneRow - row both in {-1,0}). If so it queues the 1-byte command $92 (drone destroyed/finished) and sets droneFlags bit 0. Called from the fire handler at $767E.",
    "In:  $920D, $92F9, $92F6 droneCol, $92F7 droneRow, zp_67/68, zp_A5/A6",
    "Out: command $92 queued (queueCmdBytePending), $92F9 |= 1, zp_74/75",
    "(confidence: medium)"
   ],
   "unit": "game/comcenScreens6F00"
  },
  "79D8": {
   "routine": [
    "showDroneKillBurst - Visual/sound effect on the radar screen when the local $92 command has been executed (droneFlags bit 4 set by cmdDroneArrived): not in solo/film mode ($0B9B bit 7). Moves droneFlags bit 4 to bit 5, clears droneStateFlags, plays sounds $17 and $09, positions sprite 1 on the drone's cell centre (getUnitSpriteCentreOnGrid) and plays an 11-step ring burst there. Called from the screen-1 loop when statsNeedRefresh is set ($75F4).",
    "In:  $0B9B, $92F9, $92F6/$92F7 drone position",
    "Out: $92F9 bit4 cleared/bit5 set, $920D = 0, $9011/$9019 sprite 1 position, sprite 1 animation, sounds",
    "(confidence: medium)"
   ],
   "unit": "game/comcenScreens6F00"
  },
  "7A13": {
   "routine": [
    "mapCellToRadarGrid - Converts map coordinates (X = column, Y = row) into radar grid coordinates relative to the comcen: X = col + 10 - comcenCol, Y = row + 8 - comcenRow, then falls into isRadarGridCellInRange. Called by the main program's getUnitSpriteCentreOnGrid ($340B) and by the communications module ($EE7C in build 1, $EEE1 in the $EE00 sub-overlay) to place sprites on screen 1.",
    "In:  X = map column, Y = map row, $9236 comcenUnit, $F640/$F6A4",
    "Out: X = grid column, Y = grid row, C = 0 inside the 21x17 grid, C = 1 outside"
   ],
   "unit": "game/comcenScreens6F00"
  },
  "7A2B": {
   "routine": [
    "isRadarGridCellInRange - Range test for a radar grid cell: C = 0 when Y < 17 and X < 21 (unsigned, so negative values count as outside), C = 1 otherwise. Also called directly from $33A0, $76FC and $77B9.",
    "In:  X = grid column, Y = grid row",
    "Out: C flag"
   ],
   "unit": "game/comcenScreens6F00"
  },
  "7A32": {
   "routine": [
    "initRadarScreen - Builds screen 1 (RADAR/MISSILE screen, game screen id 1 entered at $7587): clears the text draw parameters $2A29-$2A2C, sets border and background colour shadows to $0C, clears $9226, sets zp_41 = 1, then draws the MISSILES panel (drawMissilePanel), the missile count (drawMissileCount), the FIRE button (drawFireButton), the DRONE ALERT panel (drawDroneAlertPanel), the framed radar map window (redrawRadarMapWindow) and the main frame (drawMainWindowFrame), and finally jumps to the $C000 overlay's message-line redraw at L_C5D7.",
    "In:  $9232 missiles left, game state",
    "Out: whole screen drawn; $9003/$9004 = $0C, $9226 = 0, zp_41 = 1, $2A29-$2A2C = 0"
   ],
   "unit": "game/comcenScreens6F00"
  },
  "7A6B": {
   "routine": [
    "redrawRadarMapWindow - Draws the two nested frames of the radar window (window 5: 25x21 at (1,1); window 6: 23x19 at (2,2)) and then the 21x17 radar map itself (drawRadarMap). Also the target of clearCurrentScreenWindows ($2DFA) when screen 1 is active.",
    "In:  comcen position, map",
    "Out: frames and map drawn"
   ],
   "unit": "game/comcenScreens6F00"
  },
  "7A78": {
   "routine": [
    "drawMissilePanel - Draws the MISSILES panel at the bottom right: resets the text window to the full screen, draws window 7 (12x7 at (27,15)) with inner corners and window 8 (4x4 at (31,16)), fills the 2x2 count box at row 17 column 32 with colour $30, selects OR bitmap mode, text colour $CF, and prints the string 'MISSILES' ($CC9E) at column 29 row 19, then resets the draw modes.",
    "In:  -",
    "Out: panel drawn; zp_3D, zp_3F, zp_40, $2A2D/$2A2E changed"
   ],
   "unit": "game/comcenScreens6F00"
  },
  "7ABA": {
   "routine": [
    "drawMissileCount - Displays the number of missiles left ($9232) as two tall (2-row) digits at row 17, columns 32 and 33: sub_C428 splits the value into tens ($C44F) and ones ($C450), the fixed draw colour $30 is selected (and remembered in missileCountColour) and drawTallGlyph is called twice. Called at screen build, after each launch ($7647) and from the refresh routine.",
    "In:  $9232",
    "Out: digits drawn; $7AB9 = $30; $2A29/$2A2A restored to 0; zp_18/zp_19 = row/column"
   ],
   "unit": "game/comcenScreens6F00"
  },
  "7AEC": {
   "routine": [
    "blinkMissileCount - Toggles missileCountColour between $30 and $03 and fills the 2x2 colour cells of the count box (row 17, column 32) with it, which swaps foreground and background of the digits. Called every 9 frames by the screen-1 loop ($75E4) while fewer than 2 missiles are left.",
    "In:  $7AB9",
    "Out: $7AB9 toggled, screen RAM colour cells; $2A2D/$2A2E = 17/32"
   ],
   "unit": "game/comcenScreens6F00"
  },
  "7B05": {
   "routine": [
    "drawFireButton - Draws the FIRE button panel: window 9 (8x7 at (29,8)) with inner corners, the 2x2 character picture at $9804 at row 10 column 32 filled with colour 5, text colour $CD and the word 'FIRE' ($869D) at column 31 row 12, then resets the draw modes. The screen-1 loop blinks the button by XORing colour 8 into the same 2x2 cells every 9 frames ($75DA).",
    "In:  -",
    "Out: panel drawn; zp_3D, zp_3F, zp_40, text window reset"
   ],
   "unit": "game/comcenScreens6F00"
  },
  "7B44": {
   "routine": [
    "drawDroneAlertPanel - Draws the top-right DRONE ALERT panel made of three nested frames (windows $0A 11x8 at (28,0), $0B 9x6 at (29,1), $0C 7x4 at (30,2)) and falls into resetDroneAlertPanel which prints the text in its inactive colour.",
    "In:  -",
    "Out: panel drawn"
   ],
   "unit": "game/comcenScreens6F00"
  },
  "7B59": {
   "routine": [
    "resetDroneAlertPanel - Prints 'DRONE' / 'ALERT' ($86A1, two lines) at column 31 row 3 in colour $0B (replace colour mode), sets droneAlertColour ($7B43) back to 2, stores 11 in zp_70 (never read) and fills the 7x4 panel interior at (2,30) with colour $0B, i.e. the panel is shown dark/inactive. Entered by JMP from the refresh routine sub_7688 when no drone is active ($920D bit 0 clear) and at the end of drawDroneAlertPanel.",
    "In:  -",
    "Out: text and colour cells; $7B43 = 2, zp_3B = 31, zp_3D = $0B, zp_3F = 0, zp_40 = 3, zp_70 = 11"
   ],
   "unit": "game/comcenScreens6F00"
  },
  "7B86": {
   "routine": [
    "flashDroneAlertPanel - Resets the draw modes, toggles droneAlertColour between $02 and $20 (EOR $22) and repaints the 7x4 panel interior with it, making the DRONE ALERT text flash red. Entered by JMP from sub_7688 on every refresh while a drone is active ($920D bit 0 set).",
    "In:  $7B43",
    "Out: $7B43 toggled, 7x4 colour cells at (2,30)"
   ],
   "unit": "game/comcenScreens6F00"
  },
  "7B91": {
   "routine": [
    "fillDroneAlertPanel - Tail shared by the two panel states: fills the 7 column x 4 row colour rectangle at row 2, column 30 with the colour byte in A (fillScreenColourRect).",
    "In:  A = colour byte",
    "Out: $2A2D = 2, $2A2E = 30, 28 colour cells written"
   ],
   "unit": "game/comcenScreens6F00"
  },
  "7BA2": {
   "routine": [
    "drawRadarMap - Draws the 21x17 radar map centred on the comcen at screen rows 3-19, columns 3-23, one character per map cell: re-randomises the noise masks (sub_727A), sets the cell colour parameters $9237/$9239 = $0B and $9238 = 1, clears the window's colour cells to 0, sets the view origin zp_A5/A6 = comcen - (10,8) (saved in $90F6/$90F7), gets the map pointer with getViewportOriginPointer and for every row/column computes the bitmap address (row/column tables with the +3 offset) and the colour RAM address ($D800 based), then calls the cell classifier sub_74C3 and glyph plotter sub_7396 of this overlay. Restores zp_A5/A6 and clears useFixedColourFlag at the end.",
    "In:  $9236 comcenUnit, map $F000, unit arrays",
    "Out: radar window drawn; zp_A5/A6 = grid origin, $90F6/$90F7, zp_48/49 map pointer, zp_B4/B5 bitmap pointer, zp_B6/B7 colour RAM pointer, $9237-$9239, $2A29 = 0"
   ],
   "unit": "game/comcenScreens6F00"
  },
  "7C5C": {
   "routine": [
    "getComcenCentredViewOrigin - Returns the map origin that centres the player's comcen in overlay B's 21x17-cell screen-1 map window: X = unitCol[comcenUnit]-10, Y = unitRow[comcenUnit]-8. Called from $7959 and $7BC5 (screen 1 comcen drawing). Starts two bytes before the nominal chunk start ($7C6E) but is on the first line of the line range.",
    "In:  $9236 comcenUnit, unitCol $F640, unitRow $F6A4",
    "Out: X = origin column, Y = origin row, A clobbered",
    "(confidence: medium)"
   ],
   "unit": "game/comcenScreens6F00"
  },
  "7891": {
   "unit": "game/comcenScreens6F00",
   "block": [
    "radarSweepState: byteTable, 3 bytes. three state bytes of the radar sweep: radarSweepStep, radarSweepRow, radarSweepDarkFlag (all initialised to 0 on disk)"
   ]
  },
  "7A12": {
   "unit": "game/comcenScreens6F00",
   "block": [
    "padding7A12: unknown, 1 bytes. unreferenced $00 byte between the RTS of showDroneKillBurst and mapCellToRadarGrid (alignment filler)"
   ]
  },
  "7AB9": {
   "unit": "game/comcenScreens6F00",
   "block": [
    "missileCountColour: byteTable, 1 bytes. colour byte of the missile count box ($30 / $03)"
   ]
  },
  "7B43": {
   "unit": "game/comcenScreens6F00",
   "block": [
    "droneAlertColour: byteTable, 1 bytes. flash colour of the DRONE ALERT panel ($02 / $20)"
   ]
  },
  "7FE6": {
   "routine": [
    "enterDroneScreen - Entry of game screen 2 (the DRONE screen, F5), jumped to by the screen switcher sub_06BA ($06FC) when currentScreenId becomes 2. Copies dronesLeft[0] ($92AE) to $9233, unpacks the eight aircraft silhouettes at $FE24 into the $0200 shape buffer (unpackRleTo0200) and draws the whole screen with sub_70F5, then falls into resetDroneScreen.",
    "In:  $92AE dronesLeft[own]",
    "Out: $9233, $0200-$03FF shape buffer, screen 2 drawn"
   ],
   "unit": "game/comcenScreens6F00"
  },
  "7FF6": {
   "routine": [
    "resetDroneScreen - (Re)initialises the drone screen's panel state and enters the idle loop. Also the re-entry point used by the knock-out sequence ($076A) and after a drone flight ends ($81FB). Reloads $9233 from dronesLeft[0], positions sprite 0 (X $37,Y $64, colour 1), sprite 5 (X $38,Y $64, colour 2) and sprite 3 (X $38,Y $65) in the left panel, sets droneJoyHeading zp_5E=0 (north), draws the heading indicator (sub_823F), loads aircraft shape 0 into sprite 5, enables sprites 0+3, and sets droneFuel $9220 to 100 when drones are left (else 0) for the fuel read-out.",
    "In:  $92AE, sprite tables",
    "Out: $9233, $9220, zp_5E, $9010/$9013/$9015, $9018/$901B/$901D, $9008/$900D, $9022=$09, $91CC=5; falls into droneScreenIdleLoop"
   ],
   "unit": "game/comcenScreens6F00"
  },
  "803D": {
   "routine": [
    "droneScreenIdleLoop - Idle loop of the drone screen while no drone is in flight: runs updateGameFrame and the screen switcher every frame; every 11 frames (zp_6C) blinks the drones-left box (sub_7061, only when fewer than 2 drones remain) and XORs colour 8 into the 2x2 LAUNCH box at row 12 column 34. A fresh fire press (checkFirePressedNotSolo) launches a drone via launchDrone unless the own comcen is cloaked ($9245 bit 7), no drones are left ($9233 = 0), or the drone system is damaged ($9244 != 0), in which case only one press in four (random & 3 == 0) succeeds.",
    "In:  zp_6C, $9233, $9244, $9245, fire button",
    "Out: colour cells toggled; jumps to launchDrone"
   ],
   "unit": "game/comcenScreens6F00"
  },
  "808D": {
   "routine": [
    "launchDrone - Launch sequence: reads the joystick heading (sub_82A4), droneHeading $920B = joyDirToHeadingTable[zp_5E], queues the 1-byte command $97 (drone launch), paints the LAUNCH box (12,34) colour 5, droneFuel = 100 (printed by sub_6F99), decrements $9233 and reprints it (sub_7036), sets droneStateFlags $920D |= $C0 (active + locally steered), sets the view origin and droneViewOriginCol/Row $92F6/$92F7 to comcen position minus (6,5) so the drone starts over the comcen, draws the comcen ring sprite (sub_3456), loads the targeting reticle at $FF93 into sprite 2 (black, X $AC, Y $88) and enables it, puts the heading silhouette into sprite 5, plays sound $0F, clears the camera window with static (sub_7135), sets colour override $AA, runs the iris-open animation sub_7221 and enters droneFlightLoop.",
    "In:  zp_5D joystick, $9236 comcenUnit, unitCol/unitRow, $FF93 reticle shape",
    "Out: $920B, $920D, $9220, $9233, $92F6/$92F7, zp_A5/zp_A6, sprites 2/4/5, command $97 queued"
   ],
   "unit": "game/comcenScreens6F00"
  },
  "8124": {
   "routine": [
    "droneFlightLoop - Per-frame loop while the drone flies: updateGameFrame; a fire press goes to strikeWithDrone. Every 40 frames (zp_6C) blinks the drones-left box (<2 left) and XORs colour 8 into the STRIKE box at (16,34). When zp_6E has expired re-reads the joystick heading (sub_82A4). When exchangeStepFlag $920E bit 7 is set (a lock-step exchange was applied) advances the camera one step (sub_8279); if droneFuel reaches 0 or the game ended ($0BA8 != 0) the drone is released (sub_8230) and the loss animation sub_7163 + sound $1C run; if $920D bit 7 was cleared by the command handlers (drone arrived $92 / remote detonation) the same ending runs without sending $8F. Then endDroneFlight.",
    "In:  zp_6C, zp_6E, $920E, $920D, $9220, $0BA8, fire button",
    "Out: drone view redrawn per step; falls into endDroneFlight"
   ],
   "unit": "game/comcenScreens6F00"
  },
  "8182": {
   "routine": [
    "strikeWithDrone - Fire pressed during flight: droneStateFlags = ($920D & $BF) | $20 (stop local steering, detonation pending), paints the STRIKE box (16,34) colour 2, computes the drone's map position (sub_81FE) and queues command $8E col,row (detonate). Then waits: while bit 5 stays set it keeps advancing the camera on each exchange with sound $1C; once cmdDroneDetonate has cleared bit 5 (own command executed) and bit 7 is still set it plays sound $11 and runs the strike flash/loss animation sub_7552. Ends in endDroneFlight.",
    "In:  $920D, $920E, $92F6/$92F7",
    "Out: command $8E queued, $920D, $74/$75"
   ],
   "unit": "game/comcenScreens6F00"
  },
  "81D0": {
   "routine": [
    "endDroneFlight - Common tail of a drone flight: sound $1C, droneFuel = 0 (reprinted), droneStateFlags &= $3F, view origin back to comcen minus (6,5), camera window cleared without static (sub_713C), then jumps to resetDroneScreen for the next launch.",
    "In:  $9236, unitCol/unitRow",
    "Out: $9220=0, $920D, zp_A5/zp_A6"
   ],
   "unit": "game/comcenScreens6F00"
  },
  "81FE": {
   "routine": [
    "getDroneMapPosition - cmdParam1/2 ($74/$75) = droneViewOriginCol+6, droneViewOriginRow+5, i.e. the drone's own map cell (the camera window is kept centred on the drone). Used by sendOutgoingPacket ($563C) to build the $8D heading,col,row move command and by strikeWithDrone for the $8E detonate command.",
    "In:  $92F6/$92F7",
    "Out: zp_74 = drone column, zp_75 = drone row"
   ],
   "unit": "game/comcenScreens6F00"
  },
  "820F": {
   "routine": [
    "showDroneAlert - Opponent drone launch notification called by cmdDroneLaunch ($53AF): plays sound 7 and, unless playModeFlags $0B9B bit 7 (solo/film) is set, shows message $20 'DRONE ALERT!' through the $C000 overlay's sub_C566. Assembled as .byte in the listing (misclassified).",
    "In:  $0B9B",
    "Out: sound, message"
   ],
   "unit": "game/comcenScreens6F00"
  },
  "821F": {
   "routine": [
    "fillDroneViewWindow - Fills the drone camera window (14 columns x 12 rows at row 6, column 13) with colour byte A through fillScreenColourRect. Used to clear the window ($7141), to show static ($AA) when the drone system is damaged, and by the main program at $2E07.",
    "In:  A = colour byte",
    "Out: D_2A2D=6, D_2A2E=13, screen RAM cells"
   ],
   "unit": "game/comcenScreens6F00"
  },
  "8230": {
   "routine": [
    "releaseDroneControl - If a drone is active ($920D bit 7) clears bits 6-7 of droneStateFlags and queues the 1-byte command $8F (drone released) so the opponent stops tracking it. Called when fuel runs out or the game ends, and by the comcen knock-out sequence ($0709).",
    "In:  $920D",
    "Out: $920D &= $3F, command $8F queued"
   ],
   "unit": "game/comcenScreens6F00"
  },
  "823F": {
   "routine": [
    "drawDroneHeadingIndicator - Draws the heading pointer of the left panel: clears sprite 3, draws a solid line in it from the centre (col 12,row 10) to headingLineEndCol/RowTable[joyDirToHeadingTable[zp_5E]] with drawLineInPathSprite, sets sprite 3's colour to $0B (or 6 for north/south, zp_5E < 2), copies the 64-byte shape to sprite 0 (which sits one pixel up-left in white, giving a shadowed line) and enables sprites 0 and 3.",
    "In:  zp_5E droneJoyHeading, tables $7FCE/$7FD6/$7FDE",
    "Out: $8AC0 and $8A00 sprite shapes, $900B, $9022 |= $09, zp_92-zp_95 clobbered"
   ],
   "unit": "game/comcenScreens6F00"
  },
  "8279": {
   "routine": [
    "advanceDroneCamera - One drone step on the DRONE screen, run whenever exchangeStepFlag $920E bit 7 is set: clears the flag, updateMarkerSprite, redraws the 14x12 camera window from the map around droneViewOrigin (sub_741C); if the drone system is damaged ($9244) half of the steps (random bit 7 clear) fill the window with static colour $AA; decrements droneFuel, reloads the heading silhouette into sprite 5 and reprints the fuel (sub_6F99). The actual drone movement is done by sendOutgoingPacket via $731E.",
    "In:  $920B, $9220, $9244, $92F6/$92F7",
    "Out: $920E=0, $9220-1, sprite 5, camera window"
   ],
   "unit": "game/comcenScreens6F00"
  },
  "82A4": {
   "routine": [
    "readDroneHeadingFromJoystick - Reads joystickDirectionTable[zp_5D]; if a direction is held (entry >= 0) stores it in droneJoyHeading zp_5E, sets the re-read delay zp_6E = 20 frames and redraws the heading indicator (sub_823F). Called at launch and, when zp_6E has expired, during flight.",
    "In:  zp_5D joystick bits, $1FA6 table",
    "Out: zp_5E, zp_6E, heading sprites"
   ],
   "unit": "game/comcenScreens6F00"
  },
  "82BF": {
   "routine": [
    "drawShotLineInViewport - Draws the shot of resolveAttack ($3A30) in the 7x5-cell battlefield viewport (2x2 characters per cell at row 1, column 25). First forces the upper colour nibble of the viewport's 14x10 screen RAM cells to 1 (white for bit pair 01). Handles a negative view origin (zp_A5/zp_A6 < 0) by shifting all columns/rows, computes the viewport bounds $82B5-$82B8, and records in $82B9 which endpoints lie inside (attacker -> -1, target -> +2); returns if neither. Then rebases the coordinates (sub_840F), scales to fat pixels (x8, +3/+4 for the cell centre, sub_84B2), runs the line stepper from attacker (zp_93,zp_92) to target (zp_95,zp_94) and plots colour-1 fat pixels via plotFatPixel for points inside the window (X = dx+100, Y = dy+4). The first 4 steps from the shooter are skipped, the 5th gets the 4-pixel diamond (sub_84F1), unless only the target is visible ($82B9 = 2) in which case the whole line is solid. Boomers (attackerType $923D = 2) step 3 cells per plot (dotted). Finally viewRedrawTimer $9234 = $8F so the view is redrawn (line erased) 15 frames later.",
    "In:  zp_92/zp_93 attacker row/col, zp_94/zp_95 target row/col, zp_A5/zp_A6 view origin, $923D attackerType, $9382/$939B screen row tables",
    "Out: bitmap pixels, screen RAM colour nibbles, $9234 = $8F, $82B5-$82BC, zp_92-zp_95 and line stepper state clobbered, zp_B6/zp_B7"
   ],
   "unit": "game/comcenScreens6F00"
  },
  "840F": {
   "routine": [
    "rebaseLineCoordsToMin - Subtracts min(viewLeft $82B5, zp_93, zp_95) from all three column values and min(viewTop $82B7, zp_92, zp_94) from the three row values so that the smallest becomes 0. Keeps the following x8 scaling from overflowing a byte.",
    "In:  $82B5, $82B7, zp_92-zp_95",
    "Out: $82B5, $82B7, zp_92-zp_95 rebased"
   ],
   "unit": "game/comcenScreens6F00"
  },
  "84B2": {
   "routine": [
    "scaleLineCoordsToFatPixels - Converts the four line coordinates from map cells to fat-pixel units: zp_94 = row*8+4, zp_95 = col*8+3, zp_92 = row*8+4, zp_93 = col*8+3, saturating to $FF on overflow (the zp_92 case lacks the LDA #$FF and only skips the +4 when the overflowed value is negative - harmless in practice).",
    "In:  zp_92-zp_95 in cells",
    "Out: zp_92-zp_95 in fat pixels"
   ],
   "unit": "game/comcenScreens6F00"
  },
  "84F1": {
   "routine": [
    "plotDiamondMarker - Plots four colour-1 fat pixels at (X-1,Y-1), (X+1,Y-1), (X+1,Y+1), (X-1,Y+1) with plotFatPixel and restores X/Y: the small diamond drawn at the muzzle end of a shot line.",
    "In:  X, Y = fat pixel position",
    "Out: 4 pixels; X, Y preserved"
   ],
   "unit": "game/comcenScreens6F00"
  },
  "8518": {
   "routine": [
    "drawStatsTab - Fills in the STATS tab of the in-game menu (called from $1460/$14D3): text window left 10, colour $61, clears exchangeStepFlag $920E (the refresh request). Rows 9-12: number of living GRUNTS/RIDERS/BOOMERS/SPIES per side, counted by scanning the 50 unit slots down from each comcen (own comcen $9236 in column 9 = US, the other comcen $9236 EOR $52 in column 14 = THEM). Row 14 DRONES ($92AE/$92AF), row 15 MISSILES ($92B0/$92B1), row 17 COMSPEED (comSpeedDisplayTable[$92B4/$92B5]), row 18 TER PTS ($92AC[side EOR 1] under US, $92AC[side] under THEM), row 19 RECYCLER words via printOptionWord ($92A6[side], $92A6[side EOR 1]), row 20 the MAP ID string at $0BA9 in column 9 for side 0, column 14 for side 1.",
    "In:  $9236, $F76C unitTypeFlags, $92AE-$92B5, $92AC, $92A6, $0B9F playerSide, $0BA9",
    "Out: text printed; $920E = 0; zp_18, zp_1A, zp_20, zp_21, zp_3B, zp_3D, zp_3F, zp_40 used"
   ],
   "unit": "game/comcenScreens6F00"
  },
  "8601": {
   "routine": [
    "printNumberRightAligned - Prints the 8-bit value A right-aligned in a 3-character field at text column X of the current row: sets zp_3F = X, computes the number of leading fill characters (0 for >= 100, 1 for 10-99, 2 for < 10) into the self-modified operands at $8622/$8627/$8629, positions the cursor (sub_C099), prints the fill characters with the $C000 overlay routine at $C0EF and the number with sub_C17D (Y = 0 high byte). The CPY #0/BNE at $8607 is dead (Y is always 0).",
    "In:  A = value, X = column",
    "Out: text; zp_3F; operands $8622, $8627, $8629"
   ],
   "unit": "game/comcenScreens6F00"
  },
  "862D": {
   "routine": [
    "setTextColumn - Sets the text cursor column zp_3F = A and repositions the cursor through sub_C099 (X and Y preserved).",
    "In:  A = column relative to textWindowLeft",
    "Out: zp_3F, text cursor pointers"
   ],
   "unit": "game/comcenScreens6F00"
  },
  "8632": {
   "routine": [
    "drawComcenSchematic - Unpacks the RLE-compressed comcen picture at $7C6E straight into the bitmap for the REPAIR tab (called from drawRepairTab $176B): marker = first byte ($13) -> rleMarkerByte $9246, $9247 = 0, fetchRleByte's pointer ($681B/$681C) = $7C6F, destination = bitmap cell (row 10, column 13) from calcCellPointersXY. Emits 10 character rows of 160 bytes (20 characters x 8 lines) - 'marker,count,value' runs (count 0 = 256) may cross row boundaries - advancing zp_B4/zp_B5 by 320 per row. The picture is a side view of the tracked comcen vehicle with dish and antenna.",
    "In:  $7C6E compressed data",
    "Out: bitmap $A000 area rows 10-19 columns 13-32; $9246, $9247, $681B/$681C, zp_1A, zp_1B, zp_B4/zp_B5"
   ],
   "unit": "game/comcenScreens6F00"
  },
  "7C6E": {
   "unit": "game/comcenScreens6F00",
   "block": [
    "comcenSchematicRle: unknown, 864 bytes. RLE-compressed 160x80 pixel hires picture (20x10 characters, 1600 bytes unpacked) of the comcen vehicle shown on the REPAIR tab. Byte 0 = escape marker $13; stream of literals and 'marker,count,value' runs (count 0 = 256); ends at $7FCD with the run $13,$00,$00 (256 zero bytes). The labels sub_7CA2/7CAE/7CBF/7D70/7D7D/7D8B/7D98/7DA7/7E4F/7E74/7EA0/7F3F/7FA6/7FCB and the L_/D_ labels inside come from overlay A references and have no meaning in variant B."
   ]
  },
  "7FCE": {
   "unit": "game/comcenScreens6F00",
   "block": [
    "joyDirToHeadingTable: byteTable, 8 bytes. 00 04 02 06 07 01 03 05: maps the joystickDirectionTable index (N,S,E,W,NW,NE,SE,SW) to the clockwise heading 0-7 (N,NE,E,SE,S,SW,W,NW) used for sprite shapes and command $8D. Also read by $731E."
   ]
  },
  "7FD6": {
   "unit": "game/comcenScreens6F00",
   "block": [
    "headingLineEndColTable: byteTable, 8 bytes. 0C 13 16 13 0C 05 03 05: end column inside the 24x21 sprite of the heading pointer line for headings 0-7 (centre column 12)."
   ]
  },
  "7FDE": {
   "unit": "game/comcenScreens6F00",
   "block": [
    "headingLineEndRowTable: byteTable, 8 bytes. 00 03 0A 11 14 11 0A 03: end row of the heading pointer line for headings 0-7 (centre row 10)."
   ]
  },
  "82B5": {
   "unit": "game/comcenScreens6F00",
   "block": [
    "shotLineWorkVars: byteTable, 8 bytes. Eight zero-initialised work bytes of drawShotLineInViewport (see variables $82B5-$82BC)."
   ]
  },
  "82BD": {
   "unit": "game/comcenScreens6F00",
   "block": [
    "unusedBytes82BD: unknown, 2 bytes. $00,$60: no reference in either variant; filler between the shot-line work bytes and drawShotLineInViewport."
   ]
  },
  "8512": {
   "unit": "game/comcenScreens6F00",
   "block": [
    "comSpeedDisplayTable: byteTable, 6 bytes. 01 02 02 02 00 03: COMSPEED value printed on the STATS tab for comcen speed class 0-5 (class 4 = immobile shows 0)."
   ]
  },
  "868C": {
   "unit": "game/comcenScreens6F00",
   "block": [
    "strDrone: text, 5 bytes. 'DRONE' (final E has bit 7). Caption of the drones-left box on the drone screen; printed by overlay B code at $7018-$7033 with textColour $CF, column $20 (32), row $07."
   ]
  },
  "8691": {
   "unit": "game/comcenScreens6F00",
   "block": [
    "strLaunch: text, 6 bytes. 'LAUNCH'. Caption under the LAUNCH button icon of the drone screen; printed at $70B6-$70C5, column $20, row $0E (14)."
   ]
  },
  "8697": {
   "unit": "game/comcenScreens6F00",
   "block": [
    "strStrike: text, 6 bytes. 'STRIKE'. Caption under the STRIKE button icon of the drone screen; printed by sub_70D9 at $70D9-$70EF with textColour $C7, column $20, row $12 (18)."
   ]
  },
  "869D": {
   "unit": "game/comcenScreens6F00",
   "block": [
    "strFire: text, 4 bytes. 'FIRE'. Caption of the missile FIRE button on the main game screen; printed at $7B2E-$7B3D, column $1F (31), row $0C (12)."
   ]
  },
  "86A1": {
   "unit": "game/comcenScreens6F00",
   "block": [
    "strDroneAlertTwoLine: text, 11 bytes. 'DRONE' + $0D + 'ALERT' (final T has bit 7): the two-line label of the top-right DRONE ALERT panel. Printed at $7B67-$7B7A with textWindowLeft $1F, column 0, row 3; the surrounding panel colour (droneAlertColour, $02 or $20) is what flashes, not the text."
   ]
  },
  "86AC": {
   "unit": "game/comcenScreens6F00",
   "block": [
    "statsTabLabels: text, 79 bytes. One bit-7-terminated blob of CR-separated row labels for the console STATS tab: CR 'GRUNTS' CR 'RIDERS' CR 'BOOMERS' CR 'SPIES' CR CR 'DRONES' CR 'MISSILES' CR CR 'COMSPEED' CR 'TER PTS' CR 'RECYCLER' CR 'MAP ID' (the final D is $C4). Printed as a single string by the main program at $161D-$1621 right after ownerNameString, with textWindowLeft 10 and column 3; the matching numbers are drawn separately."
   ]
  },
  "86FB": {
   "unit": "game/comcenScreens6F00",
   "block": [
    "filmViewWordTable: byteTable, 12 bytes. Three fixed-width 4-byte entries used to patch msgShowViewTemplate: 'U','S'|$80,$00,$00 / 'T','H','E','M'|$80 / 'B','O','T','H'|$80. Indexed by (playModeFlags & 3) * 4 at $4116-$412A, producing the status line SHOW US / SHOW THEM / SHOW BOTH. The two padding zeroes of the first entry are copied but never printed because the terminator precedes them."
   ]
  },
  "8707": {
   "unit": "game/comcenScreens6F00",
   "block": [
    "msgEndOfFilm: text, 12 bytes. 'END OF FILM.' - message id 2 (sound 3), queued with sub_C566 at $59FC after setFilmPtrToEnd when a recorded game film has been replayed to its end."
   ]
  },
  "8713": {
   "unit": "game/comcenScreens6F00",
   "block": [
    "msgUnitRecycled: text, 14 bytes. 'UNIT RECYCLED.' - message id 5 (sound 4), queued with sub_C560 at $3EA3 after initUnitRecord, but only when the recycled unit's owner (zp_1A) equals localPlayerIndex."
   ]
  },
  "8721": {
   "unit": "game/comcenScreens6F00",
   "block": [
    "msgShowViewTemplate: text, 9 bytes. 'SHOW ****' - message id 6 template (sound 3). Bytes $8726-$8729 are overwritten with a filmViewWordTable entry, which supplies the bit-7 terminator, before the message is queued at $412C. As stored on disk the template has no terminator and would run on into msgSpeedTemplate."
   ]
  },
  "872A": {
   "unit": "game/comcenScreens6F00",
   "block": [
    "msgSpeedTemplate: text, 7 bytes. 'SPEED *' - message id 7 template (sound 3). The '*' at $8730 is replaced by the pressed digit's key code $B1-$B9, whose bit 7 doubles as the terminator, at $40DE; the message is then queued at $40E3. Also unterminated as stored."
   ]
  },
  "8731": {
   "unit": "game/comcenScreens6F00",
   "block": [
    "msgSpottedRecycler: text, 17 bytes. 'SPOTTED RECYCLER!' - message id 8 (sound 4), queued with sub_C560 at $3F8C right after drawRecyclerOnMap / drawMapCell, i.e. when the recycler cell first becomes visible."
   ]
  },
  "8742": {
   "unit": "game/comcenScreens6F00",
   "block": [
    "msgComcenStunned: text, 15 bytes. 'COMCEN STUNNED!' - message id $0C (sound 4), queued at $0741-$0743 in the $0400-page game-tick code when $9162 reads $FF. The same string is also printed directly into the console status area at column 5 by $17C4-$17D4 while comcenStunnedFlag ($922A) bit 7 is set."
   ]
  },
  "8751": {
   "unit": "game/comcenScreens6F00",
   "block": [
    "msgSpottedEnemyComcen: text, 21 bytes. 'SPOTTED ENEMY COMCEN!' - message id $0D (sound 4), queued with sub_C560 at $36EF when a newly visible enemy unit's type field ($F76C,y & 7) equals 4 (COMCEN)."
   ]
  },
  "8766": {
   "unit": "game/comcenScreens6F00",
   "block": [
    "msgSpyUnderAttack: text, 17 bytes. 'SPY UNDER ATTACK!' - message id $0E (sound 4), queued with sub_C560 at $3B3B when the unit being hit is type 3 (SPY) and its energy field ($F9C4,y & $3F) is still $32 (50 = full), so it fires once, on the first hit."
   ]
  },
  "8777": {
   "unit": "game/comcenScreens6F00",
   "block": [
    "msgSpottedEnemySpy: text, 18 bytes. 'SPOTTED ENEMY SPY!' - message id $0F (sound 4), queued with sub_C560 at $36E7 when a newly visible enemy unit's type field equals 3 (SPY). Types 0-2 (GRUNT, RIDER, BOOMER) are spotted silently."
   ]
  },
  "8789": {
   "unit": "game/comcenScreens6F00",
   "block": [
    "msgWeLostOnPoints: text, 18 bytes. 'WE LOST ON POINTS!' - message id $11 (sound $0E), selected by computeGameResult at $2963-$296B on the time-out path when the losing side equals localPlayerIndex. Overlaid by code in variant A ($8789-$879E hold 22 bytes of A code ending in jmp $C100), so the string is only intact while variant B is resident."
   ]
  },
  "879B": {
   "unit": "game/comcenScreens6F00",
   "block": [
    "msgWeWonOnPoints: text, 17 bytes. 'WE WON ON POINTS!' - message id $12 (sound $0D), the time-out win. Its first four bytes 'WE W' ($879B-$879E) are also clobbered by variant A code; from $879F on the byte content is identical in both variants."
   ]
  },
  "87AC": {
   "unit": "game/comcenScreens6F00",
   "block": [
    "msgItWasADraw: text, 14 bytes. 'IT WAS A DRAW.' - message id $13, the only result message with no sound ($80). Chosen when the 16-bit score difference is zero at time-out, or when gameOverFlags bits 5-6 equal $60 (both comcens knocked out); both sides are then set to 250 points."
   ]
  },
  "87BA": {
   "unit": "game/comcenScreens6F00",
   "block": [
    "msgWeWereKnockedOut: text, 20 bytes. 'WE WERE KNOCKED OUT!' - message id $14 (sound $0E), selected at $298D-$2995 when the knocked-out side equals localPlayerIndex."
   ]
  },
  "87CE": {
   "unit": "game/comcenScreens6F00",
   "block": [
    "msgWeWonByAKnockout: text, 21 bytes. 'WE WON BY A KNOCKOUT!' - message id $15 (sound $0D), the enemy comcen destroyed. Not written by the T35 comm module despite the XREF: see the misclassified entry for $87CE."
   ]
  },
  "87E3": {
   "unit": "game/comcenScreens6F00",
   "block": [
    "msgGameOver: text, 10 bytes. 'GAME OVER.' - message id $16 (sound $19), queued first by computeGameResult at $2901-$2903 on the time-out path, ahead of the result message."
   ]
  },
  "87ED": {
   "unit": "game/comcenScreens6F00",
   "block": [
    "msgDroneAlert: text, 12 bytes. 'DRONE ALERT!' - has no fixed message id; overlay A installs its address into run-time message slot $20 at $7BA6-$7BAC (ldx #$20 / lda #$ED / ldy #$87 / jsr sub_C924) immediately before jumping to the comm module battle-start entry $EE26. During the battle showDroneAlert ($820F, variant B, called from $53AF) plays sound 7 and queues id $20 unless playModeFlags bit 7 (solo/film) is set."
   ]
  },
  "87F9": {
   "unit": "game/comcenScreens6F00",
   "block": [
    "overlayTailPadding: unknown, 6 bytes. Six $00 bytes between msgDroneAlert and the variant flag, identical in both variants and referenced by nothing."
   ]
  },
  "87FF": {
   "unit": "game/comcenScreens6F00",
   "block": [
    "overlayVariantFlag: byteTable, 1 bytes. The overlay identity byte: $FF in variant B, $00 in variant A. Bit 7 is read at $1036 and $1080 to decide whether the other variant still has to be pulled from disk."
   ]
  }
 }
}