{
 "chunk": "game_comcenScreens6F00_7B86",
 "unit": "game/comcenScreens6F00",
 "unitLabels": {
  "game/comcenScreens6F00": {
   "7BB2": "drawRadarMapBody",
   "7BF9": "radarMapColourRamPtr",
   "7C1C": "radarMapNextMapRow",
   "7C2E": "radarMapScreenPtrCarry",
   "8143": "flightStrikeBoxOrigin",
   "81B6": "strikeWaitForExchange",
   "81F4": "endFlightRowOffset",
   "8364": "testTargetEndpointInView",
   "84EC": "scaleColAddHalfCell",
   "8511": "plotDiamondDone",
   "85DB": "statsPrintThemRecycler"
  }
 },
 "notes": {
  "7B86": {
   "unit": "game/comcenScreens6F00",
   "routine": [
    "flashDroneAlertPanel - Toggles the DRONE ALERT panel between its two colour states and repaints the",
    "7x4 colour cells behind the two-line 'DRONE / ALERT' text, so the panel blinks red while an opponent",
    "drone is airborne.  droneAlertColour ($7B43) alternates between $02 (red as the %10 bit pair) and $20",
    "(red as the %01 bit pair), which swaps the text and its background.",
    "In:  droneAlertColour $7B43",
    "Out: $7B43 toggled, 28 screen RAM colour cells at row 2 column 30 rewritten, draw modes reset,",
    "     D_2A2A = the new colour",
    "Called from: JMP at $76AF in the screen-1 refresh routine sub_7688, taken when droneControlFlags",
    "     ($920D) bit 0 says an enemy drone is in the air (the other path JMPs to resetDroneAlertPanel)"
   ],
   "line": "put the bitmap and colour draw modes back to plain replace"
  },
  "7B89": {
   "unit": "game/comcenScreens6F00",
   "line": "current state of the alert panel ($02 or $20)"
  },
  "7B8C": {
   "unit": "game/comcenScreens6F00",
   "line": "EOR $22 swaps the two red nibbles: $02 <-> $20"
  },
  "7B8E": {
   "unit": "game/comcenScreens6F00",
   "line": "remember the new state for the next refresh"
  },
  "7B91": {
   "unit": "game/comcenScreens6F00",
   "routine": [
    "fillDroneAlertPanel - Shared tail of resetDroneAlertPanel and flashDroneAlertPanel: fills the 7 x 4",
    "character interior of DRONE ALERT window $0C (top-left corner row 2, column 30) with the colour byte",
    "in A.",
    "In:  A = screen RAM colour byte",
    "Out: 28 colour cells written, D_2A2D = 2, D_2A2E = $1E, D_2A2A = A",
    "Called from: JMP at $7B83 (resetDroneAlertPanel) and by falling through from flashDroneAlertPanel"
   ],
   "line": "colour rectangle starts at character row 2"
  },
  "7B93": {
   "unit": "game/comcenScreens6F00",
   "line": "D_2A2D = fill origin row"
  },
  "7B96": {
   "unit": "game/comcenScreens6F00",
   "line": "character column 30"
  },
  "7B98": {
   "unit": "game/comcenScreens6F00",
   "line": "D_2A2E = fill origin column"
  },
  "7B9B": {
   "unit": "game/comcenScreens6F00",
   "line": "7 character columns wide"
  },
  "7B9D": {
   "unit": "game/comcenScreens6F00",
   "line": "4 character rows high"
  },
  "7B9F": {
   "unit": "game/comcenScreens6F00",
   "line": "paint them and return straight to the panel routine's caller"
  },
  "7BA2": {
   "unit": "game/comcenScreens6F00",
   "routine": [
    "drawRadarMap - Redraws the whole 21 x 17 cell strategic map of screen 1 (the RADAR/MISSILE screen)",
    "at character rows 3-19, columns 3-23, one 8x8 character per map cell, centred so that the player's",
    "own comcen sits at grid cell (10,8).  Terrain and own units are dark grey, a spotted enemy is white;",
    "the actual per-cell decision is made by getRadarCellGlyph and the plotting by drawRadarGlyph.  The",
    "colour bytes go to colour RAM $D800 (the %11 bit pair); paintRadarSweepRow later writes $F0/$00 into",
    "screen RAM $8C00 to reveal or hide each row as the sweep passes.",
    "In:  comcenUnit $9236, the 40x40 map at $F000, the unit arrays at $F640+",
    "Out: 357 bitmap cells and colour cells written; zp_A5/zp_A6 restored; $90F6/$90F7 = the saved view",
    "     origin; $9237/$9239 = $0B, $9238 = 1; zp_48/49, zp_B4-zp_B7 clobbered; textDrawParams = 0",
    "Called from: $75B0 (screen-1 build), $797E (comcen moved, via scrollRadarOnComcenMove) and by the",
    "     JMP at $7A75 in redrawRadarMapWindow"
   ],
   "line": "new random noise masks so unspotted enemy 'ghost' blips shimmer on every redraw"
  },
  "7BA5": {
   "unit": "game/comcenScreens6F00",
   "line": "$0B = dark grey"
  },
  "7BA7": {
   "unit": "game/comcenScreens6F00",
   "line": "$9237: colour used for our own units"
  },
  "7BAA": {
   "unit": "game/comcenScreens6F00",
   "line": "$9239: and for plain terrain"
  },
  "7BAD": {
   "unit": "game/comcenScreens6F00",
   "line": "1 = white"
  },
  "7BAF": {
   "unit": "game/comcenScreens6F00",
   "line": "$9238: a spotted enemy blip - the sweep only lights up colour-1 cells"
  },
  "7BB2": {
   "unit": "game/comcenScreens6F00",
   "block": [
    "Blank the window, then work out where the map has to be scrolled to keep the comcen centred."
   ],
   "line": "colour fill origin: character row 3 (top edge of the radar window)"
  },
  "7BB4": {
   "unit": "game/comcenScreens6F00",
   "line": "D_2A2D = fill origin row"
  },
  "7BB7": {
   "unit": "game/comcenScreens6F00",
   "line": "character column 3 (left edge)"
  },
  "7BB9": {
   "unit": "game/comcenScreens6F00",
   "line": "D_2A2E = fill origin column"
  },
  "7BBC": {
   "unit": "game/comcenScreens6F00",
   "line": "21 cells across"
  },
  "7BBE": {
   "unit": "game/comcenScreens6F00",
   "line": "17 cells down"
  },
  "7BC0": {
   "unit": "game/comcenScreens6F00",
   "line": "colour 0 = both multicolour nibbles black"
  },
  "7BC2": {
   "unit": "game/comcenScreens6F00",
   "line": "clear the screen RAM colour cells of the whole window first"
  },
  "7BC5": {
   "unit": "game/comcenScreens6F00",
   "line": "X/Y = map origin that puts the comcen at grid cell (10,8)"
  },
  "7BC8": {
   "unit": "game/comcenScreens6F00",
   "line": "zp_A5 = map column of the left edge"
  },
  "7BCA": {
   "unit": "game/comcenScreens6F00",
   "line": "zp_A6 = map row of the top edge"
  },
  "7BCC": {
   "unit": "game/comcenScreens6F00",
   "line": "zp_48/zp_49 = address in the $F000 map of that top-left cell (handles negative origins)"
  },
  "7BCF": {
   "unit": "game/comcenScreens6F00",
   "block": [
    "Save the origin - the drawing loop walks zp_A5/zp_A6 cell by cell so that getRadarCellGlyph can test",
    "for off-map coordinates - and build the two output pointers for character cell (row 3, column 3)."
   ],
   "line": "keep the origin column ..."
  },
  "7BD1": {
   "unit": "game/comcenScreens6F00",
   "line": "... in $90F6"
  },
  "7BD4": {
   "unit": "game/comcenScreens6F00",
   "line": "keep the origin row ..."
  },
  "7BD6": {
   "unit": "game/comcenScreens6F00",
   "line": "... in $90F7"
  },
  "7BD9": {
   "unit": "game/comcenScreens6F00",
   "line": "no carry into the address addition"
  },
  "7BDA": {
   "unit": "game/comcenScreens6F00",
   "line": "bitmap row table (real base $9300) entry for character row 3, low byte"
  },
  "7BDD": {
   "unit": "game/comcenScreens6F00",
   "line": "plus 8*column table (real base $9332) entry for character column 3"
  },
  "7BE0": {
   "unit": "game/comcenScreens6F00",
   "line": "zp_B4 = low byte of the first bitmap cell"
  },
  "7BE2": {
   "unit": "game/comcenScreens6F00",
   "line": "same tables, high bytes ($9319+3 and $935A+3)"
  },
  "7BE5": {
   "unit": "game/comcenScreens6F00",
   "line": "add with the carry from the low half"
  },
  "7BE8": {
   "unit": "game/comcenScreens6F00",
   "line": "zp_B5 = high byte; zp_B4/B5 now point into the $A000 bitmap"
  },
  "7BEA": {
   "unit": "game/comcenScreens6F00",
   "line": "clear carry for the screen RAM address"
  },
  "7BEB": {
   "unit": "game/comcenScreens6F00",
   "line": "screen RAM row table (real base $9382) entry for character row 3, low byte"
  },
  "7BEE": {
   "unit": "game/comcenScreens6F00",
   "line": "+3 characters for column 3"
  },
  "7BF0": {
   "unit": "game/comcenScreens6F00",
   "line": "zp_B6 = low byte"
  },
  "7BF2": {
   "unit": "game/comcenScreens6F00",
   "line": "row 3 high byte from the $939B table (points into screen RAM $8C00)"
  },
  "7BF5": {
   "unit": "game/comcenScreens6F00",
   "line": "propagate the carry of the column addition"
  },
  "7BF7": {
   "unit": "game/comcenScreens6F00",
   "line": "dead store: zp_B7 is overwritten three instructions later with the colour RAM value"
  },
  "7BF9": {
   "unit": "game/comcenScreens6F00",
   "line": "clear carry for the +$4C00"
  },
  "7BFA": {
   "unit": "game/comcenScreens6F00",
   "line": "$8C00 + $4C00 = $D800: turn the screen RAM address into the colour RAM address"
  },
  "7BFC": {
   "unit": "game/comcenScreens6F00",
   "line": "zp_B6/B7 now address colour RAM; on screen 1 drawRadarGlyph stores the raw colour there"
  },
  "7BFE": {
   "unit": "game/comcenScreens6F00",
   "block": [
    "Outer loop over the 17 radar rows (X), inner loop over the 21 columns (Y).  Y is also the offset",
    "added to zp_B6/B7, while zp_B4/B5 is advanced 8 bytes per cell by drawRadarGlyph itself."
   ],
   "line": "X = radar row 0..16 (character row 3+X)"
  },
  "7C00": {
   "unit": "game/comcenScreens6F00",
   "line": "Y = radar column 0..20 (character column 3+Y)"
  },
  "7C02": {
   "unit": "game/comcenScreens6F00",
   "line": "A = 0"
  },
  "7C03": {
   "unit": "game/comcenScreens6F00",
   "line": "clear carry for the column reset"
  },
  "7C04": {
   "unit": "game/comcenScreens6F00",
   "line": "restart the running map column at the left edge of the window"
  },
  "7C07": {
   "unit": "game/comcenScreens6F00",
   "line": "zp_A5 = map column of the cell about to be drawn"
  },
  "7C09": {
   "unit": "game/comcenScreens6F00",
   "line": "map byte of this cell: $80|unitIndex, or a terrain tile"
  },
  "7C0B": {
   "unit": "game/comcenScreens6F00",
   "line": "classify it: A = glyph index (bit 7 = draw through the noise masks), zp_32 = colour"
  },
  "7C0E": {
   "unit": "game/comcenScreens6F00",
   "line": "plot the 8x8 glyph, store zp_32 at (zp_B6),Y and step zp_B4/B5 on by one character"
  },
  "7C11": {
   "unit": "game/comcenScreens6F00",
   "line": "next character column of the window"
  },
  "7C12": {
   "unit": "game/comcenScreens6F00",
   "line": "and the matching map column, for the off-map test in getRadarCellGlyph"
  },
  "7C14": {
   "unit": "game/comcenScreens6F00",
   "line": "21 columns done?"
  },
  "7C16": {
   "unit": "game/comcenScreens6F00",
   "line": "no - draw the next cell of this row"
  },
  "7C18": {
   "unit": "game/comcenScreens6F00",
   "block": [
    "End of a row: advance the map pointer, the colour RAM pointer and the bitmap pointer by one row."
   ],
   "line": "the next window row shows the next map row"
  },
  "7C1A": {
   "unit": "game/comcenScreens6F00",
   "line": "map pointer low byte"
  },
  "7C1C": {
   "unit": "game/comcenScreens6F00",
   "line": "clear carry"
  },
  "7C1D": {
   "unit": "game/comcenScreens6F00",
   "line": "one map row = 40 bytes"
  },
  "7C1F": {
   "unit": "game/comcenScreens6F00",
   "line": "zp_48 updated"
  },
  "7C21": {
   "unit": "game/comcenScreens6F00",
   "line": "map pointer high byte"
  },
  "7C23": {
   "unit": "game/comcenScreens6F00",
   "line": "add the carry"
  },
  "7C25": {
   "unit": "game/comcenScreens6F00",
   "line": "zp_49 updated"
  },
  "7C27": {
   "unit": "game/comcenScreens6F00",
   "line": "clear carry"
  },
  "7C28": {
   "unit": "game/comcenScreens6F00",
   "line": "colour RAM pointer low byte"
  },
  "7C2A": {
   "unit": "game/comcenScreens6F00",
   "line": "one character row = 40 bytes"
  },
  "7C2C": {
   "unit": "game/comcenScreens6F00",
   "line": "zp_B6 updated"
  },
  "7C2E": {
   "unit": "game/comcenScreens6F00",
   "line": "colour RAM pointer high byte"
  },
  "7C30": {
   "unit": "game/comcenScreens6F00",
   "line": "add the carry"
  },
  "7C32": {
   "unit": "game/comcenScreens6F00",
   "line": "zp_B7 updated"
  },
  "7C34": {
   "unit": "game/comcenScreens6F00",
   "line": "next radar row"
  },
  "7C35": {
   "unit": "game/comcenScreens6F00",
   "line": "column index 0 = character column 3 again"
  },
  "7C37": {
   "unit": "game/comcenScreens6F00",
   "line": "clear carry"
  },
  "7C38": {
   "unit": "game/comcenScreens6F00",
   "line": "bitmap row table entry for character row 3+X, low byte"
  },
  "7C3B": {
   "unit": "game/comcenScreens6F00",
   "line": "plus the column-3 offset"
  },
  "7C3E": {
   "unit": "game/comcenScreens6F00",
   "line": "zp_B4 = start of the new row"
  },
  "7C40": {
   "unit": "game/comcenScreens6F00",
   "line": "row 3+X high byte"
  },
  "7C43": {
   "unit": "game/comcenScreens6F00",
   "line": "plus the column-3 high byte"
  },
  "7C46": {
   "unit": "game/comcenScreens6F00",
   "line": "zp_B5 = start of the new row"
  },
  "7C48": {
   "unit": "game/comcenScreens6F00",
   "line": "17 rows done?"
  },
  "7C4A": {
   "unit": "game/comcenScreens6F00",
   "line": "no - draw the next row"
  },
  "7C4C": {
   "unit": "game/comcenScreens6F00",
   "block": [
    "Finished: drop any fixed-colour override and put the view origin back the way the caller left it."
   ],
   "line": "0 = use each glyph's own colour again"
  },
  "7C4E": {
   "unit": "game/comcenScreens6F00",
   "line": "clear the fixed-colour flag $2A29"
  },
  "7C51": {
   "unit": "game/comcenScreens6F00",
   "line": "reload the saved origin column"
  },
  "7C54": {
   "unit": "game/comcenScreens6F00",
   "line": "zp_A5 restored"
  },
  "7C56": {
   "unit": "game/comcenScreens6F00",
   "line": "reload the saved origin row"
  },
  "7C59": {
   "unit": "game/comcenScreens6F00",
   "line": "zp_A6 restored"
  },
  "7C5B": {
   "unit": "game/comcenScreens6F00",
   "line": "back to the caller"
  },
  "7C5C": {
   "unit": "game/comcenScreens6F00",
   "routine": [
    "getComcenCentredViewOrigin - Returns the map origin that puts the player's own comcen in the middle",
    "of the 21 x 17 cell strategic window of screen 1: column = unitCol[comcenUnit] - 10, row =",
    "unitRow[comcenUnit] - 8.  Either value may be negative when the comcen is near an edge of the 40x40",
    "map; getViewportOriginPointer copes with that.",
    "In:  comcenUnit $9236 (49 for side 0, 99 for side 1), unitColTable $F640, unitRowTable $F6A4",
    "Out: X = origin column, Y = origin row; A clobbered",
    "Called from: $7959 (scrollRadarOnComcenMove) and $7BC5 (drawRadarMap)"
   ],
   "line": "$9236 = index of this side's comcen (49 or 99)"
  },
  "7C5F": {
   "unit": "game/comcenScreens6F00",
   "line": "its map column ($F640 + unit)"
  },
  "7C62": {
   "unit": "game/comcenScreens6F00",
   "line": "no borrow"
  },
  "7C63": {
   "unit": "game/comcenScreens6F00",
   "line": "the comcen is drawn at grid column 10 of the 21-wide window"
  },
  "7C65": {
   "unit": "game/comcenScreens6F00",
   "line": "return the origin column in X"
  },
  "7C66": {
   "unit": "game/comcenScreens6F00",
   "line": "its map row ($F6A4 + unit; the arrays are $64 apart)"
  },
  "7C69": {
   "unit": "game/comcenScreens6F00",
   "line": "no borrow"
  },
  "7C6A": {
   "unit": "game/comcenScreens6F00",
   "line": "and at grid row 8 of the 17-high window"
  },
  "7C6C": {
   "unit": "game/comcenScreens6F00",
   "line": "return the origin row in Y"
  },
  "7C6D": {
   "unit": "game/comcenScreens6F00",
   "line": "done"
  },
  "7C6E": {
   "unit": "game/comcenScreens6F00",
   "block": [
    "comcenSchematicRle - 864 bytes of RLE-compressed hires picture (20 x 10 characters = 160 x 80",
    "pixels, 1600 bytes when unpacked): the side view of the tracked comcen vehicle shown on the REPAIR",
    "tab.  Stream format as used by unpackRleTo0200: byte 0 is the escape marker ($13), afterwards any",
    "byte other than the marker is a literal and 'marker, count, value' emits value count times (count 0",
    "means 256).  The stream ends at $7FCB with $13,$13,$00 = 256 zero bytes.  drawComcenSchematic",
    "($8632) is the only reader; it unpacks straight into the bitmap at character row 10, column 13 with",
    "a 320-byte row stride.  Every sub_/L_/D_ label inside this block is an artefact of XREF merging the",
    "two $6F00 overlay variants: those addresses are code only in the map generator overlay."
   ]
  },
  "7FCE": {
   "unit": "game/comcenScreens6F00",
   "block": [
    "Three 8-byte lookup tables for the drone heading indicator, indexed 0-7."
   ],
   "line": "joyDirToHeadingTable: joystick direction code (0 N, 1 S, 2 E, 3 W, 4 NW, 5 NE, 6 SE, 7 SW) -> clockwise heading (0 N, 1 NE, 2 E, 3 SE, 4 S, 5 SW, 6 W, 7 NW); also read by steerDrone at $7320 and by launchDrone at $8092"
  },
  "7FD6": {
   "unit": "game/comcenScreens6F00",
   "line": "headingLineEndColTable[0..3]: end column of the pointer line inside the 24x21 sprite for headings N, NE, E, SE (the line starts at column 12)"
  },
  "7FDA": {
   "unit": "game/comcenScreens6F00",
   "line": "headingLineEndColTable[4..6]: headings S, SW, W"
  },
  "7FDD": {
   "unit": "game/comcenScreens6F00",
   "line": "headingLineEndColTable[7]: heading NW"
  },
  "7FDE": {
   "unit": "game/comcenScreens6F00",
   "line": "headingLineEndRowTable[0..7]: end row of the pointer line for headings N..NW (the line starts at row 10)"
  },
  "7FE6": {
   "unit": "game/comcenScreens6F00",
   "routine": [
    "enterDroneScreen - Entry point of game screen 2, the DRONE screen (F5).  Copies this side's drone",
    "stock into the display counter, unpacks the eight aircraft silhouettes into the $0200 sprite shape",
    "buffer, builds the whole screen and falls into resetDroneScreen.",
    "In:  dronesLeft $92AE",
    "Out: radarDronesLeft $9233; $0200-$03FF = 8 sprite shapes of 64 bytes; screen 2 drawn; never returns",
    "     (the idle loop is entered by falling through)",
    "Called from: JMP at $06FC in switchToRequestedScreen when the requested screen becomes 2"
   ],
   "line": "$92AE = drones this side still owns"
  },
  "7FE9": {
   "unit": "game/comcenScreens6F00",
   "line": "$9233 = the digit painted in the count box at row 5 column 34"
  },
  "7FEC": {
   "unit": "game/comcenScreens6F00",
   "line": "low byte of the compressed shape set"
  },
  "7FEE": {
   "unit": "game/comcenScreens6F00",
   "line": "$FE24 in the high-memory block = eight aircraft silhouettes, one per heading"
  },
  "7FF0": {
   "unit": "game/comcenScreens6F00",
   "line": "decompress them into $0200-$03FF (64 bytes per heading)"
  },
  "7FF3": {
   "unit": "game/comcenScreens6F00",
   "line": "draw the frames, panels, buttons, fuel read-out and the empty camera window"
  },
  "7FF6": {
   "unit": "game/comcenScreens6F00",
   "routine": [
    "resetDroneScreen - (Re)initialises the drone screen's sprites and read-outs and drops into the idle",
    "loop that waits for a launch.  Parks sprite 0 (white heading line) at ($37,$64), sprite 3 (the same",
    "line one pixel down-right, used as its shadow) at ($38,$65) and sprite 5 (aircraft silhouette) at",
    "($38,$64), all over the compass panel, and shows a full tank if a drone is still available.",
    "In:  dronesLeft $92AE",
    "Out: $9233, droneFuel $9220 (100 or 0), zp_5E = 0, sprite shadows $9008-$901D, spriteEnableShadow",
    "     $9022 = $09, activeSpriteIndex $91CC = 5; falls into droneScreenIdleLoop",
    "Called from: fall-through from enterDroneScreen, JMP at $81FB (endDroneFlight) and JMP at $076A",
    "     (the comcen knock-out sequence returning to this screen)"
   ],
   "line": "reload the drone stock ..."
  },
  "7FF9": {
   "unit": "game/comcenScreens6F00",
   "line": "... into the on-screen counter"
  },
  "7FFC": {
   "unit": "game/comcenScreens6F00",
   "block": [
    "Park the three compass-panel sprites.  Sprite 0 is white and sits one pixel up and to the left of",
    "sprite 3, so the dark line of sprite 3 reads as its shadow."
   ],
   "line": "sprite X = $37 (left panel, low byte only - MSB stays clear)"
  },
  "7FFE": {
   "unit": "game/comcenScreens6F00",
   "line": "$9010 = sprite 0 X shadow"
  },
  "8001": {
   "unit": "game/comcenScreens6F00",
   "line": "$38 = one pixel further right"
  },
  "8002": {
   "unit": "game/comcenScreens6F00",
   "line": "$9015 = sprite 5 (aircraft silhouette) X"
  },
  "8005": {
   "unit": "game/comcenScreens6F00",
   "line": "$9013 = sprite 3 (heading line) X"
  },
  "8008": {
   "unit": "game/comcenScreens6F00",
   "line": "sprite Y = $64"
  },
  "800A": {
   "unit": "game/comcenScreens6F00",
   "line": "$9018 = sprite 0 Y"
  },
  "800D": {
   "unit": "game/comcenScreens6F00",
   "line": "$901D = sprite 5 Y"
  },
  "8010": {
   "unit": "game/comcenScreens6F00",
   "line": "$65 = one pixel further down"
  },
  "8011": {
   "unit": "game/comcenScreens6F00",
   "line": "$901B = sprite 3 Y, so sprite 3 trails sprite 0 by (1,1)"
  },
  "8014": {
   "unit": "game/comcenScreens6F00",
   "line": "colour 1 = white"
  },
  "8016": {
   "unit": "game/comcenScreens6F00",
   "line": "$9008 = sprite 0 colour (the highlight of the pointer line)"
  },
  "8019": {
   "unit": "game/comcenScreens6F00",
   "line": "colour 2 = red"
  },
  "801B": {
   "unit": "game/comcenScreens6F00",
   "line": "$900D = sprite 5 colour (the aircraft)"
  },
  "801E": {
   "unit": "game/comcenScreens6F00",
   "line": "direction code 0 = due north"
  },
  "8020": {
   "unit": "game/comcenScreens6F00",
   "line": "zp_5E = the steer direction the pointer shows"
  },
  "8022": {
   "unit": "game/comcenScreens6F00",
   "line": "build the pointer line into sprites 3 and 0"
  },
  "8025": {
   "unit": "game/comcenScreens6F00",
   "line": "5 = sprite 5 is the shape target"
  },
  "8027": {
   "unit": "game/comcenScreens6F00",
   "line": "$91CC selects sprite 5 inside loadDirectionShapeSprite"
  },
  "802A": {
   "unit": "game/comcenScreens6F00",
   "line": "probable bug: A is still 5, so silhouette 5 (SW) is loaded although the pointer shows north"
  },
  "802D": {
   "unit": "game/comcenScreens6F00",
   "line": "bit 0 + bit 3 = sprites 0 and 3"
  },
  "802F": {
   "unit": "game/comcenScreens6F00",
   "line": "assigned, not ORed - this also switches sprite 5 back off, so the idle panel shows only the pointer"
  },
  "8032": {
   "unit": "game/comcenScreens6F00",
   "line": "a full tank is 100 exchanges of fuel"
  },
  "8034": {
   "unit": "game/comcenScreens6F00",
   "line": "any drone left to launch?"
  },
  "8037": {
   "unit": "game/comcenScreens6F00",
   "line": "yes - keep 100"
  },
  "8039": {
   "unit": "game/comcenScreens6F00",
   "line": "no - A is 0, so show an empty tank"
  },
  "803A": {
   "unit": "game/comcenScreens6F00",
   "line": "$9220 = fuel value printed by drawDroneFuelDigits"
  },
  "803D": {
   "unit": "game/comcenScreens6F00",
   "routine": [
    "droneScreenIdleLoop - Main loop of the DRONE screen while nothing is flying.  Each frame it runs the",
    "game engine and honours a screen change; every 11 frames it flashes the LAUNCH button and, with",
    "fewer than two drones left, the count box.  A fresh fire press launches, unless the own comcen is",
    "cloaked (never) or the drone bay is damaged (then only one press in four gets through).",
    "In:  zp_6C blink countdown, radarDronesLeft $9233, comcenDroneStatus $9244, comcenCloakedFlag $9245,",
    "     the fire button",
    "Out: never returns - either jumps to launchDrone or leaves through switchToRequestedScreen /",
    "     updateGameFrame",
    "Called from: fall-through from resetDroneScreen; looped back to from $805E, $8071, $8082 and $808A"
   ],
   "line": "one game frame: engine step, message line, sound, timers"
  },
  "8040": {
   "unit": "game/comcenScreens6F00",
   "line": "act on an F1/F3/F5/F7 request - does not return if the screen changes"
  },
  "8043": {
   "unit": "game/comcenScreens6F00",
   "line": "Z=1 only for a fresh press, and only in a live (non-solo, non-film) game"
  },
  "8046": {
   "unit": "game/comcenScreens6F00",
   "line": "no press - fall through to the blinking"
  },
  "8048": {
   "unit": "game/comcenScreens6F00",
   "line": "$9245 bit 7 = our comcen is cloaked"
  },
  "804B": {
   "unit": "game/comcenScreens6F00",
   "line": "a cloaked comcen cannot launch drones"
  },
  "804D": {
   "unit": "game/comcenScreens6F00",
   "line": "$9244 != 0 = the drone bay has been knocked out"
  },
  "8050": {
   "unit": "game/comcenScreens6F00",
   "line": "undamaged - accept the launch"
  },
  "8052": {
   "unit": "game/comcenScreens6F00",
   "line": "damaged: roll the game RNG"
  },
  "8055": {
   "unit": "game/comcenScreens6F00",
   "line": "keep two bits"
  },
  "8057": {
   "unit": "game/comcenScreens6F00",
   "line": "three presses out of four are swallowed"
  },
  "8059": {
   "unit": "game/comcenScreens6F00",
   "line": "go and check that a drone is actually in stock"
  },
  "805C": {
   "unit": "game/comcenScreens6F00",
   "block": [
    "Blinking, once every 11 frames."
   ],
   "line": "zp_6C is decremented once per frame by the IRQ"
  },
  "805E": {
   "unit": "game/comcenScreens6F00",
   "line": "not expired yet - keep polling"
  },
  "8060": {
   "unit": "game/comcenScreens6F00",
   "line": "how many drones are left?"
  },
  "8063": {
   "unit": "game/comcenScreens6F00",
   "line": "fewer than two?"
  },
  "8065": {
   "unit": "game/comcenScreens6F00",
   "line": "no - leave the count box alone"
  },
  "8067": {
   "unit": "game/comcenScreens6F00",
   "line": "yes - pulse the count box as a warning"
  },
  "806A": {
   "unit": "game/comcenScreens6F00",
   "line": "11 frames ..."
  },
  "806C": {
   "unit": "game/comcenScreens6F00",
   "line": "... until the next blink"
  },
  "806E": {
   "unit": "game/comcenScreens6F00",
   "line": "drones left?"
  },
  "8071": {
   "unit": "game/comcenScreens6F00",
   "line": "none - leave the LAUNCH button dark"
  },
  "8073": {
   "unit": "game/comcenScreens6F00",
   "line": "character row 12 ..."
  },
  "8075": {
   "unit": "game/comcenScreens6F00",
   "line": "... into the colour-fill origin row"
  },
  "8078": {
   "unit": "game/comcenScreens6F00",
   "line": "character column 34 = the LAUNCH button ..."
  },
  "807A": {
   "unit": "game/comcenScreens6F00",
   "line": "... into the colour-fill origin column"
  },
  "807D": {
   "unit": "game/comcenScreens6F00",
   "line": "colour 8 (orange)"
  },
  "807F": {
   "unit": "game/comcenScreens6F00",
   "line": "XOR it into the 2x2 button cells so the button flashes"
  },
  "8082": {
   "unit": "game/comcenScreens6F00",
   "line": "round again"
  },
  "8085": {
   "unit": "game/comcenScreens6F00",
   "line": "drones in stock?"
  },
  "8088": {
   "unit": "game/comcenScreens6F00",
   "line": "yes - go"
  },
  "808A": {
   "unit": "game/comcenScreens6F00",
   "line": "no - ignore the press"
  },
  "808D": {
   "unit": "game/comcenScreens6F00",
   "routine": [
    "launchDrone - Sends a drone on its way: latches the joystick heading, queues command $97 (drone",
    "launch) for the engine and the opponent, lights the LAUNCH button, fills the tank with 100, drops",
    "the stock by one and puts the 14 x 12 camera window over our own comcen, where the drone starts.",
    "Then it loads the targeting reticle into sprite 2, the aircraft silhouette into sprite 5, plays the",
    "launch sound and opens the camera with a grey iris wipe.",
    "In:  zp_5D joystick, comcenUnit $9236, unitColTable/unitRowTable, the reticle shape at $FF93",
    "Out: droneHeading $920B, droneControlFlags $920D |= $C0, droneFuel $9220 = 100, $9233 decremented,",
    "     droneViewOriginCol/Row $92F6/$92F7 and zp_A5/zp_A6 = comcen - (6,5), sprites 2/4/5, command $97",
    "     queued; falls into droneFlightLoop",
    "Called from: branch at $8088 in droneScreenIdleLoop"
   ],
   "line": "sample the stick that was held while fire was pressed"
  },
  "8090": {
   "unit": "game/comcenScreens6F00",
   "line": "zp_5E = direction code 0-7"
  },
  "8092": {
   "unit": "game/comcenScreens6F00",
   "line": "translate it into a clockwise heading 0-7 starting at north"
  },
  "8095": {
   "unit": "game/comcenScreens6F00",
   "line": "$920B: the heading steerDrone turns towards and $8D reports every exchange"
  },
  "8098": {
   "unit": "game/comcenScreens6F00",
   "line": "command $97 = drone launched (one byte, no arguments)"
  },
  "809A": {
   "unit": "game/comcenScreens6F00",
   "line": "queue it so the engine and the opponent both see it"
  },
  "809D": {
   "unit": "game/comcenScreens6F00",
   "line": "character row 12 ..."
  },
  "809F": {
   "unit": "game/comcenScreens6F00",
   "line": "... fill origin row"
  },
  "80A2": {
   "unit": "game/comcenScreens6F00",
   "line": "character column 34 = the LAUNCH button ..."
  },
  "80A4": {
   "unit": "game/comcenScreens6F00",
   "line": "... fill origin column"
  },
  "80A7": {
   "unit": "game/comcenScreens6F00",
   "line": "colour 5 (green)"
  },
  "80A9": {
   "unit": "game/comcenScreens6F00",
   "line": "2 character columns"
  },
  "80AB": {
   "unit": "game/comcenScreens6F00",
   "line": "2 character rows"
  },
  "80AD": {
   "unit": "game/comcenScreens6F00",
   "line": "light the button up solid"
  },
  "80B0": {
   "unit": "game/comcenScreens6F00",
   "line": "100 = a full tank (one unit per lock-step exchange)"
  },
  "80B2": {
   "unit": "game/comcenScreens6F00",
   "line": "$9220"
  },
  "80B5": {
   "unit": "game/comcenScreens6F00",
   "line": "print it as three tall digits at row 15"
  },
  "80B8": {
   "unit": "game/comcenScreens6F00",
   "line": "one drone fewer in stock"
  },
  "80BB": {
   "unit": "game/comcenScreens6F00",
   "line": "reprint the count digit"
  },
  "80BE": {
   "unit": "game/comcenScreens6F00",
   "line": "$920D drone state flags"
  },
  "80C1": {
   "unit": "game/comcenScreens6F00",
   "line": "bit 7 = one of our drones is airborne, bit 6 = we are steering it"
  },
  "80C3": {
   "unit": "game/comcenScreens6F00",
   "line": "store back (bit 0, the enemy-drone alert, is left alone)"
  },
  "80C6": {
   "unit": "game/comcenScreens6F00",
   "block": [
    "Put the camera window over our comcen: the window is 14 x 12 cells and keeps the drone in the",
    "middle, so its origin is the drone cell minus (6,5)."
   ],
   "line": "own comcen index"
  },
  "80C9": {
   "unit": "game/comcenScreens6F00",
   "line": "its map column"
  },
  "80CC": {
   "unit": "game/comcenScreens6F00",
   "line": "no borrow"
  },
  "80CD": {
   "unit": "game/comcenScreens6F00",
   "line": "6 columns to the left of the drone"
  },
  "80CF": {
   "unit": "game/comcenScreens6F00",
   "line": "zp_A5 = view origin column"
  },
  "80D1": {
   "unit": "game/comcenScreens6F00",
   "line": "$92F6 = the drone's own camera origin, stepped by steerDrone"
  },
  "80D4": {
   "unit": "game/comcenScreens6F00",
   "line": "comcen map row"
  },
  "80D7": {
   "unit": "game/comcenScreens6F00",
   "line": "no borrow"
  },
  "80D8": {
   "unit": "game/comcenScreens6F00",
   "line": "5 rows above the drone"
  },
  "80DA": {
   "unit": "game/comcenScreens6F00",
   "line": "zp_A6 = view origin row"
  },
  "80DC": {
   "unit": "game/comcenScreens6F00",
   "line": "$92F7"
  },
  "80DF": {
   "unit": "game/comcenScreens6F00",
   "line": "leftover: drawComcenRingSprite reads nothing from X"
  },
  "80E1": {
   "unit": "game/comcenScreens6F00",
   "line": "sprite 4 = the two concentric rings marking our comcen under the drone"
  },
  "80E4": {
   "unit": "game/comcenScreens6F00",
   "block": [
    "Load the targeting reticle into sprite 2 and park it in the centre of the camera window."
   ],
   "line": "colour 0 = black"
  },
  "80E6": {
   "unit": "game/comcenScreens6F00",
   "line": "$900A = sprite 2 colour"
  },
  "80E9": {
   "unit": "game/comcenScreens6F00",
   "line": "64 shape bytes, copied backwards"
  },
  "80EB": {
   "unit": "game/comcenScreens6F00",
   "line": "$FF93 in the high-memory block holds the reticle shape (the last byte read, $FFD2, is one past the loaded block but is the unused 64th sprite byte)"
  },
  "80EE": {
   "unit": "game/comcenScreens6F00",
   "line": "sprite 2 data at $8A80"
  },
  "80F1": {
   "unit": "game/comcenScreens6F00",
   "line": "next byte down"
  },
  "80F2": {
   "unit": "game/comcenScreens6F00",
   "line": "until byte 0 has been copied"
  },
  "80F4": {
   "unit": "game/comcenScreens6F00",
   "line": "X = $AC, the middle of the camera window"
  },
  "80F6": {
   "unit": "game/comcenScreens6F00",
   "line": "$9012 = sprite 2 X"
  },
  "80F9": {
   "unit": "game/comcenScreens6F00",
   "line": "Y = $88"
  },
  "80FB": {
   "unit": "game/comcenScreens6F00",
   "line": "$901A = sprite 2 Y"
  },
  "80FE": {
   "unit": "game/comcenScreens6F00",
   "line": "bit 2 = sprite 2"
  },
  "8100": {
   "unit": "game/comcenScreens6F00",
   "line": "OR it into the sprite enable shadow"
  },
  "8103": {
   "unit": "game/comcenScreens6F00",
   "line": "5 = sprite 5"
  },
  "8105": {
   "unit": "game/comcenScreens6F00",
   "line": "$91CC selects sprite 5 for the shape load"
  },
  "8108": {
   "unit": "game/comcenScreens6F00",
   "line": "silhouette index = the launch heading"
  },
  "810B": {
   "unit": "game/comcenScreens6F00",
   "line": "copy that shape to $8B40 and enable sprite 5"
  },
  "810E": {
   "unit": "game/comcenScreens6F00",
   "line": "back to plain replace draw modes"
  },
  "8111": {
   "unit": "game/comcenScreens6F00",
   "block": [
    "Launch effect: sound, grey static in the camera window, then the iris opens."
   ],
   "line": "sound $0F = launch"
  },
  "8113": {
   "unit": "game/comcenScreens6F00",
   "line": "play it"
  },
  "8116": {
   "unit": "game/comcenScreens6F00",
   "line": "fill the camera window with $BB (dark grey) and set radarWipeColour so the iris animates slowly"
  },
  "8119": {
   "unit": "game/comcenScreens6F00",
   "line": "$AA = light red in both multicolour nibbles"
  },
  "811B": {
   "unit": "game/comcenScreens6F00",
   "line": "$2A29 != 0 forces every following draw to use one fixed colour ..."
  },
  "811E": {
   "unit": "game/comcenScreens6F00",
   "line": "... and D_2A2A holds it"
  },
  "8121": {
   "unit": "game/comcenScreens6F00",
   "line": "shrinking concentric frames over the camera window"
  },
  "8124": {
   "unit": "game/comcenScreens6F00",
   "routine": [
    "droneFlightLoop - Per-frame loop while our drone is in the air.  A fire press jumps to",
    "strikeWithDrone; every 40 frames the STRIKE button (and, below two drones, the count box) flashes;",
    "when the joystick lockout has expired the heading is re-read.  The camera only advances when",
    "exchangeAppliedFlag ($920E) bit 7 says a lock-step command exchange has been applied, which keeps",
    "both machines in step.  The flight ends when the tank runs dry, when the game ends, or when the",
    "command handlers clear droneControlFlags bit 7 (drone arrived / detonated by the other side).",
    "In:  zp_6C, zp_6E, exchangeAppliedFlag $920E, droneControlFlags $920D, droneFuel $9220,",
    "     gameEndReason $0BA8, the fire button",
    "Out: camera redrawn one step per exchange; falls into endDroneFlight",
    "Called from: fall-through from launchDrone; looped back to from the JMP at $8171"
   ],
   "line": "run one game frame"
  },
  "8127": {
   "unit": "game/comcenScreens6F00",
   "line": "Z=1 on a fresh fire press (this variant also fires during solo/film)"
  },
  "812A": {
   "unit": "game/comcenScreens6F00",
   "line": "no press - carry on flying"
  },
  "812C": {
   "unit": "game/comcenScreens6F00",
   "line": "fire during flight = detonate here"
  },
  "812F": {
   "unit": "game/comcenScreens6F00",
   "line": "blink countdown"
  },
  "8131": {
   "unit": "game/comcenScreens6F00",
   "line": "not expired - skip the blinking"
  },
  "8133": {
   "unit": "game/comcenScreens6F00",
   "line": "40 frames ..."
  },
  "8135": {
   "unit": "game/comcenScreens6F00",
   "line": "... until the next blink (slower than the idle loop's 11)"
  },
  "8137": {
   "unit": "game/comcenScreens6F00",
   "line": "drones still in stock?"
  },
  "813A": {
   "unit": "game/comcenScreens6F00",
   "line": "fewer than two?"
  },
  "813C": {
   "unit": "game/comcenScreens6F00",
   "line": "no - leave the count box"
  },
  "813E": {
   "unit": "game/comcenScreens6F00",
   "line": "pulse the count box"
  },
  "8141": {
   "unit": "game/comcenScreens6F00",
   "line": "character row 16 ..."
  },
  "8143": {
   "unit": "game/comcenScreens6F00",
   "line": "... fill origin row"
  },
  "8146": {
   "unit": "game/comcenScreens6F00",
   "line": "character column 34 = the STRIKE button ..."
  },
  "8148": {
   "unit": "game/comcenScreens6F00",
   "line": "... fill origin column"
  },
  "814B": {
   "unit": "game/comcenScreens6F00",
   "line": "colour 8 (orange)"
  },
  "814D": {
   "unit": "game/comcenScreens6F00",
   "line": "XOR it into the 2x2 button cells"
  },
  "8150": {
   "unit": "game/comcenScreens6F00",
   "line": "zp_6E = the 20-frame lockout between joystick reads"
  },
  "8152": {
   "unit": "game/comcenScreens6F00",
   "line": "still locked out - do not re-read the stick"
  },
  "8154": {
   "unit": "game/comcenScreens6F00",
   "line": "latch a new steer direction and redraw the pointer"
  },
  "8157": {
   "unit": "game/comcenScreens6F00",
   "block": [
    "The camera moves exactly one cell per lock-step command exchange, never per frame."
   ],
   "line": "$920E bit 7 = an exchange was applied this frame"
  },
  "815A": {
   "unit": "game/comcenScreens6F00",
   "line": "yes - step the camera"
  },
  "815C": {
   "unit": "game/comcenScreens6F00",
   "line": "no - a plain BPL $8167 would have done; the JMP is two bytes of slack"
  },
  "815F": {
   "unit": "game/comcenScreens6F00",
   "line": "redraw the camera window and burn one unit of fuel"
  },
  "8162": {
   "unit": "game/comcenScreens6F00",
   "line": "tank empty?"
  },
  "8165": {
   "unit": "game/comcenScreens6F00",
   "line": "yes - hand the drone back to the engine"
  },
  "8167": {
   "unit": "game/comcenScreens6F00",
   "block": [
    "Decide whether the flight goes on for another frame."
   ],
   "line": "$920D drone state"
  },
  "816A": {
   "unit": "game/comcenScreens6F00",
   "line": "bit 7 cleared by cmdDroneArrived ($92) or a remote detonation - end without sending $8F"
  },
  "816C": {
   "unit": "game/comcenScreens6F00",
   "line": "$0BA8 != 0 = the game has ended"
  },
  "816F": {
   "unit": "game/comcenScreens6F00",
   "line": "then release the drone as well"
  },
  "8171": {
   "unit": "game/comcenScreens6F00",
   "line": "otherwise keep flying"
  },
  "8174": {
   "unit": "game/comcenScreens6F00",
   "line": "clear bits 7-6 and queue command $8F so the opponent stops tracking the drone"
  },
  "8177": {
   "unit": "game/comcenScreens6F00",
   "line": "white flash, iris close and the shrinking disc"
  },
  "817A": {
   "unit": "game/comcenScreens6F00",
   "line": "sound $1C = explosion ..."
  },
  "817C": {
   "unit": "game/comcenScreens6F00",
   "line": "... which endDroneFlight immediately plays a second time"
  },
  "817F": {
   "unit": "game/comcenScreens6F00",
   "line": "common tail"
  },
  "8182": {
   "unit": "game/comcenScreens6F00",
   "routine": [
    "strikeWithDrone - Fire pressed while the drone is flying: stop steering it, mark a detonation as",
    "pending (droneControlFlags bit 5), light the STRIKE button and queue command $8E col,row with the",
    "drone's current map cell.  Then it waits, still flying the camera one step per exchange, until the",
    "command handler executes our own $8E and clears bit 5; the strike then gets its sound and the",
    "zoom-in animation.",
    "In:  droneControlFlags $920D, exchangeAppliedFlag $920E, droneViewOriginCol/Row $92F6/$92F7",
    "Out: command $8E queued with cmdParam1/2, $920D bit 6 cleared and bit 5 set, sound and animation;",
    "     falls into endDroneFlight",
    "Called from: JMP at $812C in droneFlightLoop"
   ],
   "line": "$920D drone state"
  },
  "8185": {
   "unit": "game/comcenScreens6F00",
   "line": "clear bit 6 - we stop steering from now on"
  },
  "8187": {
   "unit": "game/comcenScreens6F00",
   "line": "set bit 5 - our own detonate command is queued but not yet executed"
  },
  "8189": {
   "unit": "game/comcenScreens6F00",
   "line": "store back"
  },
  "818C": {
   "unit": "game/comcenScreens6F00",
   "line": "character row 16 ..."
  },
  "818E": {
   "unit": "game/comcenScreens6F00",
   "line": "... fill origin row"
  },
  "8191": {
   "unit": "game/comcenScreens6F00",
   "line": "character column 34 = the STRIKE button ..."
  },
  "8193": {
   "unit": "game/comcenScreens6F00",
   "line": "... fill origin column"
  },
  "8196": {
   "unit": "game/comcenScreens6F00",
   "line": "colour 2 (red)"
  },
  "8198": {
   "unit": "game/comcenScreens6F00",
   "line": "2 character columns"
  },
  "819A": {
   "unit": "game/comcenScreens6F00",
   "line": "2 character rows"
  },
  "819C": {
   "unit": "game/comcenScreens6F00",
   "line": "light the STRIKE button solid"
  },
  "819F": {
   "unit": "game/comcenScreens6F00",
   "line": "zp_74/zp_75 = the drone's own map cell"
  },
  "81A2": {
   "unit": "game/comcenScreens6F00",
   "line": "command $8E = detonate a drone at col,row"
  },
  "81A4": {
   "unit": "game/comcenScreens6F00",
   "line": "queue the 3-byte command"
  },
  "81A7": {
   "unit": "game/comcenScreens6F00",
   "block": [
    "Wait for our own $8E to come back out of the command stream; keep the camera moving (with a rumble)",
    "on every exchange until then."
   ],
   "line": "one game frame"
  },
  "81AA": {
   "unit": "game/comcenScreens6F00",
   "line": "$920D drone state"
  },
  "81AD": {
   "unit": "game/comcenScreens6F00",
   "line": "bit 7 gone - the drone was destroyed before the command landed"
  },
  "81AF": {
   "unit": "game/comcenScreens6F00",
   "line": "test the 'detonation pending' bit"
  },
  "81B1": {
   "unit": "game/comcenScreens6F00",
   "line": "cleared = cmdDroneDetonate has run our command"
  },
  "81B3": {
   "unit": "game/comcenScreens6F00",
   "line": "$920E bit 7 = an exchange was applied"
  },
  "81B6": {
   "unit": "game/comcenScreens6F00",
   "line": "not yet - keep waiting"
  },
  "81B8": {
   "unit": "game/comcenScreens6F00",
   "line": "another camera step"
  },
  "81BB": {
   "unit": "game/comcenScreens6F00",
   "line": "sound $1C ..."
  },
  "81BD": {
   "unit": "game/comcenScreens6F00",
   "line": "... on every step, so the drone rumbles towards the target"
  },
  "81C0": {
   "unit": "game/comcenScreens6F00",
   "line": "keep waiting"
  },
  "81C3": {
   "unit": "game/comcenScreens6F00",
   "line": "dead test: bit 7 was already proved set at $81AD and nothing here clears it"
  },
  "81C6": {
   "unit": "game/comcenScreens6F00",
   "line": "never taken"
  },
  "81C8": {
   "unit": "game/comcenScreens6F00",
   "line": "sound $11 = the strike itself"
  },
  "81CA": {
   "unit": "game/comcenScreens6F00",
   "line": "play it"
  },
  "81CD": {
   "unit": "game/comcenScreens6F00",
   "line": "four 2x zooms into the camera centre, then the drone-lost animation"
  },
  "81D0": {
   "unit": "game/comcenScreens6F00",
   "routine": [
    "endDroneFlight - Common tail of every drone flight: explosion sound, empty the tank, clear the",
    "'airborne' and 'we steer it' bits, put the camera window back over our own comcen, blank it without",
    "static and restart the idle screen.",
    "In:  comcenUnit $9236, unitColTable/unitRowTable, droneControlFlags $920D",
    "Out: droneFuel $9220 = 0 and reprinted, $920D &= $3F, zp_A5/zp_A6 = comcen - (6,5), camera window",
    "     black; jumps to resetDroneScreen",
    "Called from: JMP at $817F and branches at $81AD / $81C6, plus fall-through from strikeWithDrone"
   ],
   "line": "sound $1C = explosion"
  },
  "81D2": {
   "unit": "game/comcenScreens6F00",
   "line": "play it"
  },
  "81D5": {
   "unit": "game/comcenScreens6F00",
   "line": "no fuel left"
  },
  "81D7": {
   "unit": "game/comcenScreens6F00",
   "line": "$9220"
  },
  "81DA": {
   "unit": "game/comcenScreens6F00",
   "line": "read-out back to 000"
  },
  "81DD": {
   "unit": "game/comcenScreens6F00",
   "line": "$920D drone state"
  },
  "81E0": {
   "unit": "game/comcenScreens6F00",
   "line": "clear bit 7 (airborne) and bit 6 (locally steered); bit 5 and bit 0 survive"
  },
  "81E2": {
   "unit": "game/comcenScreens6F00",
   "line": "store back"
  },
  "81E5": {
   "unit": "game/comcenScreens6F00",
   "line": "own comcen index"
  },
  "81E8": {
   "unit": "game/comcenScreens6F00",
   "line": "its map column"
  },
  "81EB": {
   "unit": "game/comcenScreens6F00",
   "line": "no borrow"
  },
  "81EC": {
   "unit": "game/comcenScreens6F00",
   "line": "the 14-wide window is centred 6 columns to the right of its origin"
  },
  "81EE": {
   "unit": "game/comcenScreens6F00",
   "line": "zp_A5"
  },
  "81F0": {
   "unit": "game/comcenScreens6F00",
   "line": "comcen map row"
  },
  "81F3": {
   "unit": "game/comcenScreens6F00",
   "line": "no borrow"
  },
  "81F4": {
   "unit": "game/comcenScreens6F00",
   "line": "the 12-high window is centred 5 rows below its origin"
  },
  "81F6": {
   "unit": "game/comcenScreens6F00",
   "line": "zp_A6"
  },
  "81F8": {
   "unit": "game/comcenScreens6F00",
   "line": "blank the camera window to colour 0 (no grey static this time)"
  },
  "81FB": {
   "unit": "game/comcenScreens6F00",
   "line": "back to the idle screen, ready for the next launch"
  },
  "81FE": {
   "unit": "game/comcenScreens6F00",
   "routine": [
    "getDroneMapPosition - Converts the camera origin into the drone's own map cell: the 14 x 12 window",
    "is kept centred on the drone, so the drone stands at origin + (6,5).",
    "In:  droneViewOriginCol $92F6, droneViewOriginRow $92F7",
    "Out: cmdParam1 zp_74 = drone map column, cmdParam2 zp_75 = drone map row",
    "Called from: $819F (strikeWithDrone, for the $8E detonate command) and $563C in sendOutgoingPacket",
    "     (for the $8D heading,col,row move report sent every exchange)"
   ],
   "line": "$92F6 = map column of the window's left edge"
  },
  "8201": {
   "unit": "game/comcenScreens6F00",
   "line": "no carry in"
  },
  "8202": {
   "unit": "game/comcenScreens6F00",
   "line": "the drone sits 6 columns into the 14-wide window"
  },
  "8204": {
   "unit": "game/comcenScreens6F00",
   "line": "zp_74 = command argument 1"
  },
  "8206": {
   "unit": "game/comcenScreens6F00",
   "line": "$92F7 = map row of the window's top edge"
  },
  "8209": {
   "unit": "game/comcenScreens6F00",
   "line": "no carry in"
  },
  "820A": {
   "unit": "game/comcenScreens6F00",
   "line": "and 5 rows into the 12-high window"
  },
  "820C": {
   "unit": "game/comcenScreens6F00",
   "line": "zp_75 = command argument 2"
  },
  "820E": {
   "unit": "game/comcenScreens6F00",
   "line": "done"
  },
  "820F": {
   "unit": "game/comcenScreens6F00",
   "routine": [
    "showDroneAlert - Notification that the opponent has launched a drone: plays the alert sound and, in",
    "a real two-player game, queues the 'DRONE ALERT!' status message.  In a solo or film game the",
    "message is suppressed because there is nobody to warn.",
    "In:  filmPlaybackMode $0B9B bit 7 (set = solo trainer or film playback)",
    "Out: sound 7 played, message $20 queued",
    "Called from: $53AF, the cmdDroneLaunch handler of command $97 in the main program"
   ],
   "line": "sound 7 = alert"
  },
  "8211": {
   "unit": "game/comcenScreens6F00",
   "line": "play it"
  },
  "8214": {
   "unit": "game/comcenScreens6F00",
   "line": "$0B9B bit 7 = solo trainer or film playback"
  },
  "8217": {
   "unit": "game/comcenScreens6F00",
   "line": "then say nothing"
  },
  "8219": {
   "unit": "game/comcenScreens6F00",
   "line": "message id $20 = 'DRONE ALERT!'"
  },
  "821B": {
   "unit": "game/comcenScreens6F00",
   "line": "insert it into the status message queue"
  },
  "821E": {
   "unit": "game/comcenScreens6F00",
   "line": "done (also the RTS shared by releaseDroneControl)"
  },
  "821F": {
   "unit": "game/comcenScreens6F00",
   "routine": [
    "fillDroneViewWindow - Fills the drone camera window (14 columns x 12 rows starting at character row",
    "6, column 13) with one screen RAM colour byte.",
    "In:  A = colour byte",
    "Out: 168 colour cells written, D_2A2D = 6, D_2A2E = $0D, D_2A2A = A",
    "Called from: $7141 (the shared radar-clear tail), $8290 (static when the drone bay is damaged) and",
    "     $2E07 in the main program"
   ],
   "line": "camera window starts at character row 6"
  },
  "8221": {
   "unit": "game/comcenScreens6F00",
   "line": "D_2A2D = fill origin row"
  },
  "8224": {
   "unit": "game/comcenScreens6F00",
   "line": "... and character column 13"
  },
  "8226": {
   "unit": "game/comcenScreens6F00",
   "line": "D_2A2E = fill origin column"
  },
  "8229": {
   "unit": "game/comcenScreens6F00",
   "line": "14 character columns = 14 map cells"
  },
  "822B": {
   "unit": "game/comcenScreens6F00",
   "line": "12 character rows = 12 map cells"
  },
  "822D": {
   "unit": "game/comcenScreens6F00",
   "line": "fill and return to the caller"
  },
  "8230": {
   "unit": "game/comcenScreens6F00",
   "routine": [
    "releaseDroneControl - Gives an airborne drone back to the engine: clears the 'airborne' and 'locally",
    "steered' bits and queues the single-byte command $8F so the opponent stops tracking it.  Does",
    "nothing when no drone of ours is up.",
    "In:  droneControlFlags $920D",
    "Out: $920D &= $3F, command $8F queued",
    "Called from: $8174 (fuel out or game over) and $0709 (the comcen knock-out sequence)"
   ],
   "line": "$920D drone state"
  },
  "8233": {
   "unit": "game/comcenScreens6F00",
   "line": "bit 7 clear - nothing of ours is airborne, so nothing to release"
  },
  "8235": {
   "unit": "game/comcenScreens6F00",
   "line": "clear bit 7 (airborne) and bit 6 (steered by us)"
  },
  "8237": {
   "unit": "game/comcenScreens6F00",
   "line": "store back"
  },
  "823A": {
   "unit": "game/comcenScreens6F00",
   "line": "command $8F = drone released"
  },
  "823C": {
   "unit": "game/comcenScreens6F00",
   "line": "queue it and return through the queue routine"
  },
  "823F": {
   "unit": "game/comcenScreens6F00",
   "routine": [
    "drawDroneHeadingIndicator - Redraws the compass pointer of the drone screen's left panel.  It clears",
    "sprite 3, draws a line inside it from the sprite centre (column 12, row 10) out to the end point",
    "looked up for the current heading, colours it, then copies the identical shape into sprite 0.",
    "Because sprite 0 is white and sits one pixel up and left of sprite 3, the pointer appears as a white",
    "line with a dark shadow.",
    "In:  droneJoyDirection zp_5E, joyDirToHeadingTable $7FCE, headingLineEndColTable $7FD6,",
    "     headingLineEndRowTable $7FDE",
    "Out: sprite 3 shape $8AC0 and sprite 0 shape $8A00 redrawn, sprite3ColourShadow $900B, sprites 0 and",
    "     3 enabled; zp_92-zp_95 and the line stepper state clobbered",
    "Called from: $8022 (resetDroneScreen) and $82B1 (readDroneHeadingFromJoystick)"
   ],
   "line": "sprite 3 = the path/route sprite at $8AC0"
  },
  "8241": {
   "unit": "game/comcenScreens6F00",
   "line": "zero its 64 shape bytes"
  },
  "8244": {
   "unit": "game/comcenScreens6F00",
   "line": "sprite row 10 ..."
  },
  "8246": {
   "unit": "game/comcenScreens6F00",
   "line": "... zp_92 = line start row (centre of the 24x21 sprite)"
  },
  "8248": {
   "unit": "game/comcenScreens6F00",
   "line": "sprite column 12 ..."
  },
  "824A": {
   "unit": "game/comcenScreens6F00",
   "line": "... zp_93 = line start column"
  },
  "824C": {
   "unit": "game/comcenScreens6F00",
   "line": "default line colour $0B (dark grey)"
  },
  "824E": {
   "unit": "game/comcenScreens6F00",
   "line": "current steer direction code"
  },
  "8250": {
   "unit": "game/comcenScreens6F00",
   "line": "codes 0 and 1 are due north and due south"
  },
  "8252": {
   "unit": "game/comcenScreens6F00",
   "line": "any other direction keeps dark grey"
  },
  "8254": {
   "unit": "game/comcenScreens6F00",
   "line": "N/S get colour 6 (blue) instead"
  },
  "8256": {
   "unit": "game/comcenScreens6F00",
   "line": "$900B = sprite 3 colour"
  },
  "8259": {
   "unit": "game/comcenScreens6F00",
   "line": "direction code -> clockwise heading 0-7"
  },
  "825C": {
   "unit": "game/comcenScreens6F00",
   "line": "end row of the pointer for that heading"
  },
  "825F": {
   "unit": "game/comcenScreens6F00",
   "line": "zp_94 = line end row"
  },
  "8261": {
   "unit": "game/comcenScreens6F00",
   "line": "end column of the pointer"
  },
  "8264": {
   "unit": "game/comcenScreens6F00",
   "line": "zp_95 = line end column"
  },
  "8266": {
   "unit": "game/comcenScreens6F00",
   "line": "A still holds the end column ($03-$16), so bit 7 is clear and the line is drawn solid"
  },
  "8269": {
   "unit": "game/comcenScreens6F00",
   "block": [
    "Sprite 0 carries an identical copy of the line, one pixel up and left and in white."
   ],
   "line": "64 shape bytes, copied backwards"
  },
  "826B": {
   "unit": "game/comcenScreens6F00",
   "line": "read sprite 3's shape"
  },
  "826E": {
   "unit": "game/comcenScreens6F00",
   "line": "write sprite 0's shape at $8A00"
  },
  "8271": {
   "unit": "game/comcenScreens6F00",
   "line": "next byte down"
  },
  "8272": {
   "unit": "game/comcenScreens6F00",
   "line": "until byte 0"
  },
  "8274": {
   "unit": "game/comcenScreens6F00",
   "line": "bit 0 + bit 3 = sprites 0 and 3"
  },
  "8276": {
   "unit": "game/comcenScreens6F00",
   "line": "OR them into the enable shadow and return"
  },
  "8279": {
   "unit": "game/comcenScreens6F00",
   "routine": [
    "advanceDroneCamera - One drone step on the DRONE screen, run once per lock-step command exchange.",
    "It consumes the exchange flag, redraws the 14 x 12 camera window from the map around the drone,",
    "replaces half of the pictures with static when the drone bay is damaged, burns one unit of fuel and",
    "reloads the silhouette for the drone's current heading.  The drone itself is moved by steerDrone,",
    "which sendOutgoingPacket calls just before the $8D report goes out.",
    "In:  exchangeAppliedFlag $920E, droneHeading $920B, droneFuel $9220, comcenDroneStatus $9244,",
    "     droneViewOriginCol/Row $92F6/$92F7",
    "Out: $920E = 0, $9220 decremented, camera window redrawn, sprite 5 reloaded, fuel read-out reprinted",
    "Called from: $815F (droneFlightLoop) and $81B8 (strikeWithDrone waiting for its $8E)"
   ],
   "line": "clear the flag ..."
  },
  "827B": {
   "unit": "game/comcenScreens6F00",
   "line": "... so only one camera step happens per exchange"
  },
  "827E": {
   "unit": "game/comcenScreens6F00",
   "line": "in this build this only switches sprite 4 (the comcen ring) off again"
  },
  "8281": {
   "unit": "game/comcenScreens6F00",
   "line": "redraw the 14x12 camera window from the map around zp_A5/zp_A6"
  },
  "8284": {
   "unit": "game/comcenScreens6F00",
   "line": "$9244 != 0 = the drone bay has been knocked out"
  },
  "8287": {
   "unit": "game/comcenScreens6F00",
   "line": "undamaged - show a clean picture"
  },
  "8289": {
   "unit": "game/comcenScreens6F00",
   "line": "roll the game RNG"
  },
  "828C": {
   "unit": "game/comcenScreens6F00",
   "line": "bit 7 set - this step still shows a clean picture"
  },
  "828E": {
   "unit": "game/comcenScreens6F00",
   "line": "$AA = light red in both nibbles"
  },
  "8290": {
   "unit": "game/comcenScreens6F00",
   "line": "otherwise bury the window in static"
  },
  "8293": {
   "unit": "game/comcenScreens6F00",
   "line": "one unit of fuel per exchange, so 100 exchanges of flight"
  },
  "8296": {
   "unit": "game/comcenScreens6F00",
   "line": "5 = sprite 5"
  },
  "8298": {
   "unit": "game/comcenScreens6F00",
   "line": "$91CC selects sprite 5"
  },
  "829B": {
   "unit": "game/comcenScreens6F00",
   "line": "current heading, which steerDrone turns one step per exchange"
  },
  "829E": {
   "unit": "game/comcenScreens6F00",
   "line": "reload the matching silhouette and enable sprite 5"
  },
  "82A1": {
   "unit": "game/comcenScreens6F00",
   "line": "reprint the fuel digits and return through them"
  },
  "82A4": {
   "unit": "game/comcenScreens6F00",
   "routine": [
    "readDroneHeadingFromJoystick - Latches the joystick direction as the drone's new steer direction and",
    "redraws the compass pointer.  A centred stick ($FF from joystickDirectionTable) leaves the current",
    "heading alone; after a successful read the stick is ignored for 20 frames so the pointer cannot spin.",
    "In:  joyDirection zp_5D (the IRQ-latched joystick 2 nibble), joystickDirectionTable $1FA6",
    "Out: droneJoyDirection zp_5E, menuBlinkTimer zp_6E = 20, sprites 0 and 3 redrawn",
    "Called from: $808D (launchDrone) and $8154 (droneFlightLoop, once the lockout has expired)"
   ],
   "line": "zp_5D = joystick 2 direction bits latched by the IRQ"
  },
  "82A6": {
   "unit": "game/comcenScreens6F00",
   "line": "$1FA6 turns them into a direction code 0-7, or $FF when centred"
  },
  "82A9": {
   "unit": "game/comcenScreens6F00",
   "line": "stick centred - keep steering the way we were"
  },
  "82AB": {
   "unit": "game/comcenScreens6F00",
   "line": "20 frames ..."
  },
  "82AD": {
   "unit": "game/comcenScreens6F00",
   "line": "... of lockout before the stick is read again"
  },
  "82AF": {
   "unit": "game/comcenScreens6F00",
   "line": "zp_5E = the new steer direction"
  },
  "82B1": {
   "unit": "game/comcenScreens6F00",
   "line": "redraw the compass pointer to match"
  },
  "82B4": {
   "unit": "game/comcenScreens6F00",
   "line": "done"
  },
  "82B5": {
   "unit": "game/comcenScreens6F00",
   "block": [
    "shotLineWorkVars - the eight work bytes of drawShotLineInViewport, zero in the loaded image."
   ],
   "line": "viewLeft: map column of the viewport's left edge; later rebased to 0 and multiplied by 8 (fat pixels)"
  },
  "82B6": {
   "unit": "game/comcenScreens6F00",
   "line": "column bound used by the endpoint test = viewLeft + 7 (the window is 7 cells wide)"
  },
  "82B7": {
   "unit": "game/comcenScreens6F00",
   "line": "viewTop: map row of the viewport's top edge; later rebased and multiplied by 8"
  },
  "82B8": {
   "unit": "game/comcenScreens6F00",
   "line": "row bound used by the endpoint test = viewTop + 5 (the window is 5 cells high)"
  },
  "82B9": {
   "unit": "game/comcenScreens6F00",
   "line": "endpoint visibility: $FF = only the shooter is in view, $02 = only the target, $01 = both, 0 = neither"
  },
  "82BA": {
   "unit": "game/comcenScreens6F00",
   "line": "shift added to every column when the view origin is negative (= -viewOriginCol)"
  },
  "82BB": {
   "unit": "game/comcenScreens6F00",
   "line": "shift added to every row when the view origin is negative (= -viewOriginRow)"
  },
  "82BC": {
   "unit": "game/comcenScreens6F00",
   "line": "countdown of the muzzle gap: 4 down to 1 = skip, 0 = draw the diamond, negative = draw the line"
  },
  "82BD": {
   "unit": "game/comcenScreens6F00",
   "block": [
    "unusedBytes82BD - $00,$60: not referenced from anywhere in this overlay variant, padding in front of",
    "drawShotLineInViewport."
   ],
   "line": "unreferenced filler"
  },
  "82BF": {
   "unit": "game/comcenScreens6F00",
   "routine": [
    "drawShotLineInViewport - Draws the tracer of one shot inside the 7 x 5 cell zoomed battlefield",
    "viewport (2 x 2 characters per cell at character row 1, column 25; 8 'fat' multicolour pixels per",
    "map cell, fat-pixel origin X=100, Y=4).  It first forces the %01 colour of all 14 x 10 viewport",
    "cells to white, then decides whether the shooter and/or the target cell is inside the window,",
    "rebases and scales the coordinates, walks the line with the high-memory stepper and plots colour-1",
    "fat pixels.  The first four steps out of the muzzle are left blank, the fifth carries a small",
    "diamond, and a BOOMER shot ($923D = 2) advances three cells per plotted pixel, which makes it",
    "dashed.  Finally viewRedrawTimer is armed so the view is redrawn - erasing the line - 15 frames on.",
    "In:  zp_92/zp_93 = shooter row/column, zp_94/zp_95 = target row/column, zp_A5/zp_A6 = view origin,",
    "     attackerType $923D, the screen row tables at $9382/$939B",
    "Out: bitmap pixels and viewport colour nibbles changed, viewRedrawTimer $9234 = $8F, $82B5-$82BC",
    "     used, zp_92-zp_95 and the line stepper state clobbered, zp_B6/zp_B7 clobbered",
    "Called from: $3A30, resolveAttack in the main program.  The only user of plotFatPixel ($FC58)."
   ],
   "block": [
    "Force the %01 bit pair of every character cell of the viewport (rows 1-10, columns 25-38) to white",
    "so the line about to be plotted is visible whatever the terrain colours were."
   ],
   "line": "screen row table index 1 = character row 1, the top of the viewport"
  },
  "82C1": {
   "unit": "game/comcenScreens6F00",
   "line": "screen RAM ($8C00) row address, low byte"
  },
  "82C4": {
   "unit": "game/comcenScreens6F00",
   "line": "zp_B6"
  },
  "82C6": {
   "unit": "game/comcenScreens6F00",
   "line": "row address high byte"
  },
  "82C9": {
   "unit": "game/comcenScreens6F00",
   "line": "zp_B7 - the loop writes screen RAM, not colour RAM"
  },
  "82CB": {
   "unit": "game/comcenScreens6F00",
   "line": "10 character rows = 5 map cells"
  },
  "82CD": {
   "unit": "game/comcenScreens6F00",
   "line": "start at the rightmost character column, 38"
  },
  "82CF": {
   "unit": "game/comcenScreens6F00",
   "line": "current colour byte of the cell"
  },
  "82D1": {
   "unit": "game/comcenScreens6F00",
   "line": "keep the low nibble = the %10 colour"
  },
  "82D3": {
   "unit": "game/comcenScreens6F00",
   "line": "force the high nibble to 1 = white for the %01 bit pair"
  },
  "82D5": {
   "unit": "game/comcenScreens6F00",
   "line": "store it back"
  },
  "82D7": {
   "unit": "game/comcenScreens6F00",
   "line": "next column to the left"
  },
  "82D8": {
   "unit": "game/comcenScreens6F00",
   "line": "leftmost viewport column is 25"
  },
  "82DA": {
   "unit": "game/comcenScreens6F00",
   "line": "keep going while Y >= 25"
  },
  "82DC": {
   "unit": "game/comcenScreens6F00",
   "line": "one row done"
  },
  "82DD": {
   "unit": "game/comcenScreens6F00",
   "line": "all ten rows done"
  },
  "82DF": {
   "unit": "game/comcenScreens6F00",
   "line": "clear carry"
  },
  "82E0": {
   "unit": "game/comcenScreens6F00",
   "line": "screen pointer low byte"
  },
  "82E2": {
   "unit": "game/comcenScreens6F00",
   "line": "one character row = 40 bytes"
  },
  "82E4": {
   "unit": "game/comcenScreens6F00",
   "line": "zp_B6"
  },
  "82E6": {
   "unit": "game/comcenScreens6F00",
   "line": "no carry - next row"
  },
  "82E8": {
   "unit": "game/comcenScreens6F00",
   "line": "carry into the high byte"
  },
  "82EA": {
   "unit": "game/comcenScreens6F00",
   "line": "always taken - next row"
  },
  "82EC": {
   "unit": "game/comcenScreens6F00",
   "block": [
    "The 7x5 window may hang off the left or top edge of the map, in which case zp_A5/zp_A6 are negative.",
    "Shift the origin and both endpoints right/down by -origin so all the following arithmetic stays",
    "unsigned, and work out the window bounds."
   ],
   "line": "no endpoint seen yet"
  },
  "82EE": {
   "unit": "game/comcenScreens6F00",
   "line": "$82B9 = endpoint visibility flags"
  },
  "82F1": {
   "unit": "game/comcenScreens6F00",
   "line": "view origin column"
  },
  "82F3": {
   "unit": "game/comcenScreens6F00",
   "line": "non-negative - use it as it is"
  },
  "82F5": {
   "unit": "game/comcenScreens6F00",
   "line": "negate it: EOR $FF ..."
  },
  "82F7": {
   "unit": "game/comcenScreens6F00",
   "line": "... clear carry ..."
  },
  "82F8": {
   "unit": "game/comcenScreens6F00",
   "line": "... +1 gives -viewOriginCol"
  },
  "82FA": {
   "unit": "game/comcenScreens6F00",
   "line": "$82BA = the column shift"
  },
  "82FD": {
   "unit": "game/comcenScreens6F00",
   "line": "clear carry"
  },
  "82FE": {
   "unit": "game/comcenScreens6F00",
   "line": "shooter column"
  },
  "8300": {
   "unit": "game/comcenScreens6F00",
   "line": "shift it"
  },
  "8303": {
   "unit": "game/comcenScreens6F00",
   "line": "zp_93 updated"
  },
  "8305": {
   "unit": "game/comcenScreens6F00",
   "line": "clear carry"
  },
  "8306": {
   "unit": "game/comcenScreens6F00",
   "line": "target column"
  },
  "8308": {
   "unit": "game/comcenScreens6F00",
   "line": "shift it"
  },
  "830B": {
   "unit": "game/comcenScreens6F00",
   "line": "zp_95 updated"
  },
  "830D": {
   "unit": "game/comcenScreens6F00",
   "line": "clear carry"
  },
  "830E": {
   "unit": "game/comcenScreens6F00",
   "line": "the origin itself"
  },
  "8310": {
   "unit": "game/comcenScreens6F00",
   "line": "shifted origin = 0"
  },
  "8313": {
   "unit": "game/comcenScreens6F00",
   "line": "$82B5 = left edge of the window (now never negative)"
  },
  "8316": {
   "unit": "game/comcenScreens6F00",
   "line": "clear carry"
  },
  "8317": {
   "unit": "game/comcenScreens6F00",
   "line": "the window is 7 cells wide"
  },
  "8319": {
   "unit": "game/comcenScreens6F00",
   "line": "$82B6 = right bound used below (note it is left+7, while the value tested against it is already relative to left - too permissive for a non-zero origin; the fat-pixel clip in the plot loop catches the difference)"
  },
  "831C": {
   "unit": "game/comcenScreens6F00",
   "line": "view origin row"
  },
  "831E": {
   "unit": "game/comcenScreens6F00",
   "line": "non-negative - use it as it is"
  },
  "8320": {
   "unit": "game/comcenScreens6F00",
   "line": "negate it ..."
  },
  "8322": {
   "unit": "game/comcenScreens6F00",
   "line": "... clear carry ..."
  },
  "8323": {
   "unit": "game/comcenScreens6F00",
   "line": "... +1 gives -viewOriginRow"
  },
  "8325": {
   "unit": "game/comcenScreens6F00",
   "line": "$82BB = the row shift"
  },
  "8328": {
   "unit": "game/comcenScreens6F00",
   "line": "clear carry"
  },
  "8329": {
   "unit": "game/comcenScreens6F00",
   "line": "shooter row"
  },
  "832B": {
   "unit": "game/comcenScreens6F00",
   "line": "shift it"
  },
  "832E": {
   "unit": "game/comcenScreens6F00",
   "line": "zp_92 updated"
  },
  "8330": {
   "unit": "game/comcenScreens6F00",
   "line": "clear carry"
  },
  "8331": {
   "unit": "game/comcenScreens6F00",
   "line": "target row"
  },
  "8333": {
   "unit": "game/comcenScreens6F00",
   "line": "shift it"
  },
  "8336": {
   "unit": "game/comcenScreens6F00",
   "line": "zp_94 updated"
  },
  "8338": {
   "unit": "game/comcenScreens6F00",
   "line": "clear carry"
  },
  "8339": {
   "unit": "game/comcenScreens6F00",
   "line": "the origin itself"
  },
  "833B": {
   "unit": "game/comcenScreens6F00",
   "line": "shifted origin = 0"
  },
  "833E": {
   "unit": "game/comcenScreens6F00",
   "line": "$82B7 = top edge of the window"
  },
  "8341": {
   "unit": "game/comcenScreens6F00",
   "line": "clear carry"
  },
  "8342": {
   "unit": "game/comcenScreens6F00",
   "line": "the window is 5 cells high"
  },
  "8344": {
   "unit": "game/comcenScreens6F00",
   "line": "$82B8 = bottom bound (same +top skew as $82B6)"
  },
  "8347": {
   "unit": "game/comcenScreens6F00",
   "block": [
    "Is the shooter's cell inside the 7x5 window?"
   ],
   "line": "no borrow"
  },
  "8348": {
   "unit": "game/comcenScreens6F00",
   "line": "shooter column"
  },
  "834A": {
   "unit": "game/comcenScreens6F00",
   "line": "make it relative to the left edge"
  },
  "834D": {
   "unit": "game/comcenScreens6F00",
   "line": "left of the window - not visible"
  },
  "834F": {
   "unit": "game/comcenScreens6F00",
   "line": "compare against the right bound"
  },
  "8352": {
   "unit": "game/comcenScreens6F00",
   "line": "right of the window - not visible"
  },
  "8354": {
   "unit": "game/comcenScreens6F00",
   "line": "no borrow"
  },
  "8355": {
   "unit": "game/comcenScreens6F00",
   "line": "shooter row"
  },
  "8357": {
   "unit": "game/comcenScreens6F00",
   "line": "make it relative to the top edge"
  },
  "835A": {
   "unit": "game/comcenScreens6F00",
   "line": "above the window - not visible"
  },
  "835C": {
   "unit": "game/comcenScreens6F00",
   "line": "compare against the bottom bound"
  },
  "835F": {
   "unit": "game/comcenScreens6F00",
   "line": "below the window - not visible"
  },
  "8361": {
   "unit": "game/comcenScreens6F00",
   "line": "flags = $FF: the shooter is in view"
  },
  "8364": {
   "unit": "game/comcenScreens6F00",
   "block": [
    "... and is the target's cell inside it?"
   ],
   "line": "no borrow"
  },
  "8365": {
   "unit": "game/comcenScreens6F00",
   "line": "target column"
  },
  "8367": {
   "unit": "game/comcenScreens6F00",
   "line": "relative to the left edge"
  },
  "836A": {
   "unit": "game/comcenScreens6F00",
   "line": "outside"
  },
  "836C": {
   "unit": "game/comcenScreens6F00",
   "line": "right bound"
  },
  "836F": {
   "unit": "game/comcenScreens6F00",
   "line": "outside"
  },
  "8371": {
   "unit": "game/comcenScreens6F00",
   "line": "no borrow"
  },
  "8372": {
   "unit": "game/comcenScreens6F00",
   "line": "target row"
  },
  "8374": {
   "unit": "game/comcenScreens6F00",
   "line": "relative to the top edge"
  },
  "8377": {
   "unit": "game/comcenScreens6F00",
   "line": "outside"
  },
  "8379": {
   "unit": "game/comcenScreens6F00",
   "line": "bottom bound"
  },
  "837C": {
   "unit": "game/comcenScreens6F00",
   "line": "outside"
  },
  "837E": {
   "unit": "game/comcenScreens6F00",
   "line": "flags += 1 ..."
  },
  "8381": {
   "unit": "game/comcenScreens6F00",
   "line": "... twice, so $02 = only the target, $01 = both endpoints in view"
  },
  "8384": {
   "unit": "game/comcenScreens6F00",
   "line": "any endpoint in view?"
  },
  "8387": {
   "unit": "game/comcenScreens6F00",
   "line": "yes - draw it"
  },
  "8389": {
   "unit": "game/comcenScreens6F00",
   "line": "neither - nothing to draw, and no redraw to schedule"
  },
  "838C": {
   "unit": "game/comcenScreens6F00",
   "block": [
    "Rebase the window origin and both endpoints so the smallest value is 0, then scale everything from",
    "map cells to fat pixels (8 per cell).  Rebasing is what stops the x8 from overflowing a byte."
   ],
   "line": "subtract the minimum column and the minimum row from all three values"
  },
  "838F": {
   "unit": "game/comcenScreens6F00",
   "line": "window left edge, in cells"
  },
  "8392": {
   "unit": "game/comcenScreens6F00",
   "line": "x2 ..."
  },
  "8393": {
   "unit": "game/comcenScreens6F00",
   "line": "x4 ..."
  },
  "8394": {
   "unit": "game/comcenScreens6F00",
   "line": "x8 = fat pixels per map cell"
  },
  "8395": {
   "unit": "game/comcenScreens6F00",
   "line": "$82B5 now in fat pixels"
  },
  "8398": {
   "unit": "game/comcenScreens6F00",
   "line": "window top edge, in cells"
  },
  "839B": {
   "unit": "game/comcenScreens6F00",
   "line": "x2 ..."
  },
  "839C": {
   "unit": "game/comcenScreens6F00",
   "line": "x4 ..."
  },
  "839D": {
   "unit": "game/comcenScreens6F00",
   "line": "x8"
  },
  "839E": {
   "unit": "game/comcenScreens6F00",
   "line": "$82B7 now in fat pixels"
  },
  "83A1": {
   "unit": "game/comcenScreens6F00",
   "line": "scale zp_92-zp_95 the same way and add the half-cell offsets (+4 rows, +3 columns)"
  },
  "83A4": {
   "unit": "game/comcenScreens6F00",
   "line": "set up the Bresenham walk from (zp_93,zp_92) to (zp_95,zp_94)"
  },
  "83A7": {
   "unit": "game/comcenScreens6F00",
   "line": "4 steps of muzzle gap ..."
  },
  "83A9": {
   "unit": "game/comcenScreens6F00",
   "line": "... in $82BC"
  },
  "83AC": {
   "unit": "game/comcenScreens6F00",
   "block": [
    "Walk the line one fat pixel step at a time.  A point is plotted only when it lies strictly inside",
    "the window - relative coordinate 0 is skipped, so the left column and top row are never drawn."
   ],
   "line": "no borrow"
  },
  "83AD": {
   "unit": "game/comcenScreens6F00",
   "line": "current column of the walk, in fat pixels"
  },
  "83AF": {
   "unit": "game/comcenScreens6F00",
   "line": "relative to the window's left edge"
  },
  "83B2": {
   "unit": "game/comcenScreens6F00",
   "line": "exactly on the left edge - skip it"
  },
  "83B4": {
   "unit": "game/comcenScreens6F00",
   "line": "left of the window - skip"
  },
  "83B6": {
   "unit": "game/comcenScreens6F00",
   "line": "7 cells x 8 fat pixels = 56"
  },
  "83B8": {
   "unit": "game/comcenScreens6F00",
   "line": "right of the window - skip"
  },
  "83BA": {
   "unit": "game/comcenScreens6F00",
   "line": "clear carry"
  },
  "83BB": {
   "unit": "game/comcenScreens6F00",
   "line": "character column 25 = fat pixel column 100"
  },
  "83BD": {
   "unit": "game/comcenScreens6F00",
   "line": "X = fat pixel column on screen"
  },
  "83BE": {
   "unit": "game/comcenScreens6F00",
   "line": "no borrow"
  },
  "83BF": {
   "unit": "game/comcenScreens6F00",
   "line": "current row of the walk"
  },
  "83C1": {
   "unit": "game/comcenScreens6F00",
   "line": "relative to the window's top edge"
  },
  "83C4": {
   "unit": "game/comcenScreens6F00",
   "line": "exactly on the top edge - skip"
  },
  "83C6": {
   "unit": "game/comcenScreens6F00",
   "line": "above the window - skip"
  },
  "83C8": {
   "unit": "game/comcenScreens6F00",
   "line": "5 cells x 8 fat pixels = 40"
  },
  "83CA": {
   "unit": "game/comcenScreens6F00",
   "line": "below the window - skip"
  },
  "83CC": {
   "unit": "game/comcenScreens6F00",
   "line": "clear carry"
  },
  "83CD": {
   "unit": "game/comcenScreens6F00",
   "line": "character row 1 = fat pixel row 4"
  },
  "83CF": {
   "unit": "game/comcenScreens6F00",
   "line": "Y = fat pixel row on screen"
  },
  "83D0": {
   "unit": "game/comcenScreens6F00",
   "line": "endpoint visibility flags"
  },
  "83D3": {
   "unit": "game/comcenScreens6F00",
   "line": "only the target is in view?"
  },
  "83D5": {
   "unit": "game/comcenScreens6F00",
   "line": "then the shooter is off screen and the whole visible line is solid"
  },
  "83D7": {
   "unit": "game/comcenScreens6F00",
   "line": "muzzle gap countdown"
  },
  "83DA": {
   "unit": "game/comcenScreens6F00",
   "line": "already negative - past the gap, plot the line"
  },
  "83DC": {
   "unit": "game/comcenScreens6F00",
   "line": "still positive - inside the 4-step gap, plot nothing"
  },
  "83DE": {
   "unit": "game/comcenScreens6F00",
   "line": "exactly 0 - this is the muzzle, draw the little diamond"
  },
  "83E1": {
   "unit": "game/comcenScreens6F00",
   "line": "and step on"
  },
  "83E4": {
   "unit": "game/comcenScreens6F00",
   "line": "colour 1 = white"
  },
  "83E6": {
   "unit": "game/comcenScreens6F00",
   "line": "plot one fat multicolour pixel at (X,Y)"
  },
  "83E9": {
   "unit": "game/comcenScreens6F00",
   "block": [
    "Advance the walk.  A BOOMER's shot ($923D = 2) takes three steps for every plotted pixel, so its",
    "tracer comes out dashed."
   ],
   "line": "one step of the gap countdown used up"
  },
  "83EC": {
   "unit": "game/comcenScreens6F00",
   "line": "steps left on the line"
  },
  "83EE": {
   "unit": "game/comcenScreens6F00",
   "line": "line finished"
  },
  "83F0": {
   "unit": "game/comcenScreens6F00",
   "line": "advance zp_92/zp_93 by one cell along the line"
  },
  "83F3": {
   "unit": "game/comcenScreens6F00",
   "line": "$923D = type of the shooting unit"
  },
  "83F6": {
   "unit": "game/comcenScreens6F00",
   "line": "2 = BOOMER"
  },
  "83F8": {
   "unit": "game/comcenScreens6F00",
   "line": "anything else plots every step"
  },
  "83FA": {
   "unit": "game/comcenScreens6F00",
   "line": "BOOMER: burn a second gap count ..."
  },
  "83FD": {
   "unit": "game/comcenScreens6F00",
   "line": "... and a second step ..."
  },
  "8400": {
   "unit": "game/comcenScreens6F00",
   "line": "... and a third gap count ..."
  },
  "8403": {
   "unit": "game/comcenScreens6F00",
   "line": "... and a third step, leaving gaps in the tracer"
  },
  "8406": {
   "unit": "game/comcenScreens6F00",
   "line": "back for the next point"
  },
  "8409": {
   "unit": "game/comcenScreens6F00",
   "line": "$8F = redraw pending, 15 frames of countdown (the IRQ stops at $80)"
  },
  "840B": {
   "unit": "game/comcenScreens6F00",
   "line": "$9234: the view is repainted 15 frames from now, erasing the line"
  },
  "840E": {
   "unit": "game/comcenScreens6F00",
   "line": "done (also the early exit when neither endpoint is visible)"
  },
  "840F": {
   "unit": "game/comcenScreens6F00",
   "routine": [
    "rebaseLineCoordsToMin - Subtracts the smallest of the three column values (window left $82B5,",
    "shooter zp_93, target zp_95) from all three, and likewise the smallest of the three row values",
    "($82B7, zp_92, zp_94), so the smallest becomes 0.  That is what keeps the following multiply by 8",
    "inside a byte.",
    "In:  $82B5, $82B7, zp_92-zp_95",
    "Out: the same six values, rebased",
    "Called from: $838C (drawShotLineInViewport)"
   ],
   "line": "window left edge"
  },
  "8412": {
   "unit": "game/comcenScreens6F00",
   "line": "compare with the shooter column"
  },
  "8414": {
   "unit": "game/comcenScreens6F00",
   "line": "left >= shooter, so the minimum is one of the two endpoints"
  },
  "8416": {
   "unit": "game/comcenScreens6F00",
   "line": "left < shooter: compare with the target column"
  },
  "8418": {
   "unit": "game/comcenScreens6F00",
   "line": "left is smaller than both - it is the minimum"
  },
  "841A": {
   "unit": "game/comcenScreens6F00",
   "line": "shooter column"
  },
  "841C": {
   "unit": "game/comcenScreens6F00",
   "line": "compare with the target column"
  },
  "841E": {
   "unit": "game/comcenScreens6F00",
   "line": "shooter is the minimum"
  },
  "8420": {
   "unit": "game/comcenScreens6F00",
   "line": "no borrow"
  },
  "8421": {
   "unit": "game/comcenScreens6F00",
   "line": "target column is the minimum: rebase the window edge ..."
  },
  "8424": {
   "unit": "game/comcenScreens6F00",
   "line": "... by subtracting it ..."
  },
  "8426": {
   "unit": "game/comcenScreens6F00",
   "line": "... and store it back"
  },
  "8429": {
   "unit": "game/comcenScreens6F00",
   "line": "no borrow"
  },
  "842A": {
   "unit": "game/comcenScreens6F00",
   "line": "rebase the shooter column ..."
  },
  "842C": {
   "unit": "game/comcenScreens6F00",
   "line": "... the same way ..."
  },
  "842E": {
   "unit": "game/comcenScreens6F00",
   "line": "... and store it"
  },
  "8430": {
   "unit": "game/comcenScreens6F00",
   "line": "the minimum itself becomes 0"
  },
  "8432": {
   "unit": "game/comcenScreens6F00",
   "line": "zp_95 = 0"
  },
  "8434": {
   "unit": "game/comcenScreens6F00",
   "line": "always taken - now do the rows"
  },
  "8436": {
   "unit": "game/comcenScreens6F00",
   "line": "no borrow"
  },
  "8437": {
   "unit": "game/comcenScreens6F00",
   "line": "shooter column is the minimum: rebase the window edge"
  },
  "843A": {
   "unit": "game/comcenScreens6F00",
   "line": "subtract it"
  },
  "843C": {
   "unit": "game/comcenScreens6F00",
   "line": "store it back"
  },
  "843F": {
   "unit": "game/comcenScreens6F00",
   "line": "no borrow"
  },
  "8440": {
   "unit": "game/comcenScreens6F00",
   "line": "rebase the target column"
  },
  "8442": {
   "unit": "game/comcenScreens6F00",
   "line": "subtract it"
  },
  "8444": {
   "unit": "game/comcenScreens6F00",
   "line": "store it back"
  },
  "8446": {
   "unit": "game/comcenScreens6F00",
   "line": "the minimum becomes 0"
  },
  "8448": {
   "unit": "game/comcenScreens6F00",
   "line": "zp_93 = 0"
  },
  "844A": {
   "unit": "game/comcenScreens6F00",
   "line": "always taken - now do the rows"
  },
  "844C": {
   "unit": "game/comcenScreens6F00",
   "line": "no borrow"
  },
  "844D": {
   "unit": "game/comcenScreens6F00",
   "line": "window edge is the minimum: rebase the shooter column"
  },
  "844F": {
   "unit": "game/comcenScreens6F00",
   "line": "subtract it"
  },
  "8452": {
   "unit": "game/comcenScreens6F00",
   "line": "store it back"
  },
  "8454": {
   "unit": "game/comcenScreens6F00",
   "line": "no borrow"
  },
  "8455": {
   "unit": "game/comcenScreens6F00",
   "line": "rebase the target column"
  },
  "8457": {
   "unit": "game/comcenScreens6F00",
   "line": "subtract it"
  },
  "845A": {
   "unit": "game/comcenScreens6F00",
   "line": "store it back"
  },
  "845C": {
   "unit": "game/comcenScreens6F00",
   "line": "the minimum becomes 0"
  },
  "845E": {
   "unit": "game/comcenScreens6F00",
   "line": "$82B5 = 0"
  },
  "8461": {
   "unit": "game/comcenScreens6F00",
   "block": [
    "Exactly the same three-way minimum for the rows."
   ],
   "line": "window top edge"
  },
  "8464": {
   "unit": "game/comcenScreens6F00",
   "line": "compare with the shooter row"
  },
  "8466": {
   "unit": "game/comcenScreens6F00",
   "line": "top >= shooter, so an endpoint holds the minimum"
  },
  "8468": {
   "unit": "game/comcenScreens6F00",
   "line": "compare with the target row"
  },
  "846A": {
   "unit": "game/comcenScreens6F00",
   "line": "the top edge is smaller than both"
  },
  "846C": {
   "unit": "game/comcenScreens6F00",
   "line": "shooter row"
  },
  "846E": {
   "unit": "game/comcenScreens6F00",
   "line": "compare with the target row"
  },
  "8470": {
   "unit": "game/comcenScreens6F00",
   "line": "shooter row is the minimum"
  },
  "8472": {
   "unit": "game/comcenScreens6F00",
   "line": "no borrow"
  },
  "8473": {
   "unit": "game/comcenScreens6F00",
   "line": "target row is the minimum: rebase the top edge"
  },
  "8476": {
   "unit": "game/comcenScreens6F00",
   "line": "subtract it"
  },
  "8478": {
   "unit": "game/comcenScreens6F00",
   "line": "store it back"
  },
  "847B": {
   "unit": "game/comcenScreens6F00",
   "line": "no borrow"
  },
  "847C": {
   "unit": "game/comcenScreens6F00",
   "line": "rebase the shooter row"
  },
  "847E": {
   "unit": "game/comcenScreens6F00",
   "line": "subtract it"
  },
  "8480": {
   "unit": "game/comcenScreens6F00",
   "line": "store it back"
  },
  "8482": {
   "unit": "game/comcenScreens6F00",
   "line": "the minimum becomes 0"
  },
  "8484": {
   "unit": "game/comcenScreens6F00",
   "line": "zp_94 = 0"
  },
  "8486": {
   "unit": "game/comcenScreens6F00",
   "line": "done"
  },
  "8487": {
   "unit": "game/comcenScreens6F00",
   "line": "no borrow"
  },
  "8488": {
   "unit": "game/comcenScreens6F00",
   "line": "shooter row is the minimum: rebase the top edge"
  },
  "848B": {
   "unit": "game/comcenScreens6F00",
   "line": "subtract it"
  },
  "848D": {
   "unit": "game/comcenScreens6F00",
   "line": "store it back"
  },
  "8490": {
   "unit": "game/comcenScreens6F00",
   "line": "no borrow"
  },
  "8491": {
   "unit": "game/comcenScreens6F00",
   "line": "rebase the target row"
  },
  "8493": {
   "unit": "game/comcenScreens6F00",
   "line": "subtract it"
  },
  "8495": {
   "unit": "game/comcenScreens6F00",
   "line": "store it back"
  },
  "8497": {
   "unit": "game/comcenScreens6F00",
   "line": "the minimum becomes 0"
  },
  "8499": {
   "unit": "game/comcenScreens6F00",
   "line": "zp_92 = 0"
  },
  "849B": {
   "unit": "game/comcenScreens6F00",
   "line": "done"
  },
  "849C": {
   "unit": "game/comcenScreens6F00",
   "line": "no borrow"
  },
  "849D": {
   "unit": "game/comcenScreens6F00",
   "line": "top edge is the minimum: rebase the shooter row"
  },
  "849F": {
   "unit": "game/comcenScreens6F00",
   "line": "subtract it"
  },
  "84A2": {
   "unit": "game/comcenScreens6F00",
   "line": "store it back"
  },
  "84A4": {
   "unit": "game/comcenScreens6F00",
   "line": "no borrow"
  },
  "84A5": {
   "unit": "game/comcenScreens6F00",
   "line": "rebase the target row"
  },
  "84A7": {
   "unit": "game/comcenScreens6F00",
   "line": "subtract it"
  },
  "84AA": {
   "unit": "game/comcenScreens6F00",
   "line": "store it back"
  },
  "84AC": {
   "unit": "game/comcenScreens6F00",
   "line": "the minimum becomes 0"
  },
  "84AE": {
   "unit": "game/comcenScreens6F00",
   "line": "$82B7 = 0"
  },
  "84B1": {
   "unit": "game/comcenScreens6F00",
   "line": "done"
  },
  "84B2": {
   "unit": "game/comcenScreens6F00",
   "routine": [
    "scaleLineCoordsToFatPixels - Converts the two line endpoints from map cells to fat pixels: row*8+4",
    "and column*8+3, i.e. the middle of the cell in the 8x8-fat-pixel grid the viewport uses.  Three of",
    "the four cases saturate to $FF on overflow; the zp_92 (shooter row) case is missing its LDA #$FF, so",
    "it keeps the wrapped value instead - harmless because rebaseLineCoordsToMin has already brought all",
    "four values below 32 cells in practice.",
    "In:  zp_92-zp_95 in map cells",
    "Out: zp_92-zp_95 in fat pixels",
    "Called from: $83A1 (drawShotLineInViewport)"
   ],
   "line": "target row, in cells"
  },
  "84B4": {
   "unit": "game/comcenScreens6F00",
   "line": "x2 ..."
  },
  "84B5": {
   "unit": "game/comcenScreens6F00",
   "line": "x4 ..."
  },
  "84B6": {
   "unit": "game/comcenScreens6F00",
   "line": "x8 fat pixels per cell"
  },
  "84B7": {
   "unit": "game/comcenScreens6F00",
   "line": "no overflow - add the half-cell offset"
  },
  "84B9": {
   "unit": "game/comcenScreens6F00",
   "line": "overflow - clamp to the last fat pixel row"
  },
  "84BB": {
   "unit": "game/comcenScreens6F00",
   "line": "always taken ($FF is negative)"
  },
  "84BD": {
   "unit": "game/comcenScreens6F00",
   "line": "clear carry"
  },
  "84BE": {
   "unit": "game/comcenScreens6F00",
   "line": "+4 = the vertical centre of the cell"
  },
  "84C0": {
   "unit": "game/comcenScreens6F00",
   "line": "zp_94"
  },
  "84C2": {
   "unit": "game/comcenScreens6F00",
   "line": "target column, in cells"
  },
  "84C4": {
   "unit": "game/comcenScreens6F00",
   "line": "x2 ..."
  },
  "84C5": {
   "unit": "game/comcenScreens6F00",
   "line": "x4 ..."
  },
  "84C6": {
   "unit": "game/comcenScreens6F00",
   "line": "x8"
  },
  "84C7": {
   "unit": "game/comcenScreens6F00",
   "line": "no overflow"
  },
  "84C9": {
   "unit": "game/comcenScreens6F00",
   "line": "clamp"
  },
  "84CB": {
   "unit": "game/comcenScreens6F00",
   "line": "always taken"
  },
  "84CD": {
   "unit": "game/comcenScreens6F00",
   "line": "clear carry"
  },
  "84CE": {
   "unit": "game/comcenScreens6F00",
   "line": "+3 = the horizontal centre of the cell"
  },
  "84D0": {
   "unit": "game/comcenScreens6F00",
   "line": "zp_95"
  },
  "84D2": {
   "unit": "game/comcenScreens6F00",
   "line": "shooter row, in cells"
  },
  "84D4": {
   "unit": "game/comcenScreens6F00",
   "line": "x2 ..."
  },
  "84D5": {
   "unit": "game/comcenScreens6F00",
   "line": "x4 ..."
  },
  "84D6": {
   "unit": "game/comcenScreens6F00",
   "line": "x8"
  },
  "84D7": {
   "unit": "game/comcenScreens6F00",
   "line": "no overflow - add the offset"
  },
  "84D9": {
   "unit": "game/comcenScreens6F00",
   "line": "overflowed: the LDA #$FF of the other three cases is missing here, so the wrapped value is kept"
  },
  "84DB": {
   "unit": "game/comcenScreens6F00",
   "line": "clear carry"
  },
  "84DC": {
   "unit": "game/comcenScreens6F00",
   "line": "+4 = the vertical centre of the cell"
  },
  "84DE": {
   "unit": "game/comcenScreens6F00",
   "line": "zp_92"
  },
  "84E0": {
   "unit": "game/comcenScreens6F00",
   "line": "shooter column, in cells"
  },
  "84E2": {
   "unit": "game/comcenScreens6F00",
   "line": "x2 ..."
  },
  "84E3": {
   "unit": "game/comcenScreens6F00",
   "line": "x4 ..."
  },
  "84E4": {
   "unit": "game/comcenScreens6F00",
   "line": "x8"
  },
  "84E5": {
   "unit": "game/comcenScreens6F00",
   "line": "no overflow"
  },
  "84E7": {
   "unit": "game/comcenScreens6F00",
   "line": "clamp"
  },
  "84E9": {
   "unit": "game/comcenScreens6F00",
   "line": "always taken"
  },
  "84EB": {
   "unit": "game/comcenScreens6F00",
   "line": "clear carry"
  },
  "84EC": {
   "unit": "game/comcenScreens6F00",
   "line": "+3 = the horizontal centre of the cell"
  },
  "84EE": {
   "unit": "game/comcenScreens6F00",
   "line": "zp_93"
  },
  "84F0": {
   "unit": "game/comcenScreens6F00",
   "line": "done"
  },
  "84F1": {
   "unit": "game/comcenScreens6F00",
   "routine": [
    "plotDiamondMarker - Plots the four colour-1 fat pixels at (X-1,Y-1), (X+1,Y-1), (X-1,Y+1) and",
    "(X+1,Y+1): the small diamond drawn at the muzzle end of a shot tracer.  X and Y are left exactly as",
    "they were on entry.",
    "In:  X = fat pixel column, Y = fat pixel row",
    "Out: four bitmap pixels set; X and Y preserved",
    "Called from: $83DE (drawShotLineInViewport, on the fifth step out of the muzzle)"
   ],
   "line": "one fat pixel up"
  },
  "84F2": {
   "unit": "game/comcenScreens6F00",
   "line": "one fat pixel left"
  },
  "84F3": {
   "unit": "game/comcenScreens6F00",
   "line": "colour 1 = white"
  },
  "84F5": {
   "unit": "game/comcenScreens6F00",
   "line": "top-left corner of the diamond"
  },
  "84F8": {
   "unit": "game/comcenScreens6F00",
   "line": "two to the right ..."
  },
  "84F9": {
   "unit": "game/comcenScreens6F00",
   "line": "... = one right of the centre"
  },
  "84FA": {
   "unit": "game/comcenScreens6F00",
   "line": "colour 1"
  },
  "84FC": {
   "unit": "game/comcenScreens6F00",
   "line": "top-right corner"
  },
  "84FF": {
   "unit": "game/comcenScreens6F00",
   "line": "two to the left ..."
  },
  "8500": {
   "unit": "game/comcenScreens6F00",
   "line": "... back to one left of the centre"
  },
  "8501": {
   "unit": "game/comcenScreens6F00",
   "line": "two down ..."
  },
  "8502": {
   "unit": "game/comcenScreens6F00",
   "line": "... = one below the centre"
  },
  "8503": {
   "unit": "game/comcenScreens6F00",
   "line": "colour 1"
  },
  "8505": {
   "unit": "game/comcenScreens6F00",
   "line": "bottom-left corner"
  },
  "8508": {
   "unit": "game/comcenScreens6F00",
   "line": "two to the right ..."
  },
  "8509": {
   "unit": "game/comcenScreens6F00",
   "line": "... = one right of the centre"
  },
  "850A": {
   "unit": "game/comcenScreens6F00",
   "line": "colour 1"
  },
  "850C": {
   "unit": "game/comcenScreens6F00",
   "line": "bottom-right corner"
  },
  "850F": {
   "unit": "game/comcenScreens6F00",
   "line": "back to the entry column ..."
  },
  "8510": {
   "unit": "game/comcenScreens6F00",
   "line": "... and the entry row"
  },
  "8511": {
   "unit": "game/comcenScreens6F00",
   "line": "done, X and Y unchanged"
  },
  "8512": {
   "unit": "game/comcenScreens6F00",
   "block": [
    "comSpeedDisplayTable - the COMSPEED figure shown on the STATS tab, indexed by the comcen speed class",
    "0-5 held in $92B4 (us) and $92B5 (them).  Class 4 is the immobile comcen and prints 0."
   ],
   "line": "class 0 -> 1, class 1-3 -> 2, class 4 -> 0 (immobile), class 5 -> 3"
  },
  "8518": {
   "unit": "game/comcenScreens6F00",
   "routine": [
    "drawStatsTab - Fills in the numbers of the console's STATS panel.  With the text window left edge at",
    "column 10 it prints two columns - US at column 9 (relative), THEM at column 14 - of: living GRUNTS,",
    "RIDERS, BOOMERS and SPIES (rows 9-12, counted by scanning the 50 unit slots downwards from each",
    "side's comcen), DRONES (row 14), MISSILES (row 15), COMSPEED (row 17), TER PTS (row 18), RECYCLER",
    "(row 19) and the five-character map name (row 20).  Clearing exchangeAppliedFlag $920E marks the",
    "panel as refreshed for this turn.",
    "In:  comcenUnit $9236, unitTypeTable $F76C, $92AE/$92AF drones, $92B0/$92B1 missiles, $92B4/$92B5",
    "     comcen speed, $92AC terrain points, $92A6 recycler mode, playerSide $0B9F, map name $0BA9",
    "Out: the STATS text drawn; $920E = 0; zp_18, zp_1A, zp_20, zp_21, zp_3B, zp_3D, zp_3F, zp_40 used",
    "Called from: $1460 and $14D3 in the main program (console tab drawing and refresh)"
   ],
   "line": "text window starts at screen column 10"
  },
  "851A": {
   "unit": "game/comcenScreens6F00",
   "line": "zp_3B, added to every text column below"
  },
  "851C": {
   "unit": "game/comcenScreens6F00",
   "line": "0 = refreshed"
  },
  "851E": {
   "unit": "game/comcenScreens6F00",
   "line": "$920E: consume the 'a turn was exchanged, redraw the stats' request"
  },
  "8521": {
   "unit": "game/comcenScreens6F00",
   "line": "colour byte $61: %01 pixels blue, %10 pixels white"
  },
  "8523": {
   "unit": "game/comcenScreens6F00",
   "line": "zp_3D = colour written with every printed character"
  },
  "8525": {
   "unit": "game/comcenScreens6F00",
   "line": "first data row"
  },
  "8527": {
   "unit": "game/comcenScreens6F00",
   "line": "zp_40 = text cursor row 9 (GRUNTS)"
  },
  "8529": {
   "unit": "game/comcenScreens6F00",
   "line": "unit type 0 = GRUNT"
  },
  "852B": {
   "unit": "game/comcenScreens6F00",
   "line": "zp_20 = the type being counted"
  },
  "852D": {
   "unit": "game/comcenScreens6F00",
   "block": [
    "Outer loop over the four fighting unit types (0 GRUNT, 1 RIDER, 2 BOOMER, 3 SPY); one text row each.",
    "Inner pass counts our side first (column 9) and then the opponent's (column 14)."
   ],
   "line": "one more column to print after this one"
  },
  "852F": {
   "unit": "game/comcenScreens6F00",
   "line": "zp_21 = columns still to do"
  },
  "8531": {
   "unit": "game/comcenScreens6F00",
   "line": "column 9 = the US column"
  },
  "8533": {
   "unit": "game/comcenScreens6F00",
   "line": "start the scan at our own comcen (49 or 99)"
  },
  "8536": {
   "unit": "game/comcenScreens6F00",
   "line": "position the text cursor at that column"
  },
  "8539": {
   "unit": "game/comcenScreens6F00",
   "line": "count = 0"
  },
  "853B": {
   "unit": "game/comcenScreens6F00",
   "line": "zp_18 = the running count"
  },
  "853D": {
   "unit": "game/comcenScreens6F00",
   "line": "50 unit slots per side"
  },
  "853F": {
   "unit": "game/comcenScreens6F00",
   "line": "zp_1A = slots still to scan"
  },
  "8541": {
   "unit": "game/comcenScreens6F00",
   "line": "type/flags byte of this unit ($F76C + unit)"
  },
  "8544": {
   "unit": "game/comcenScreens6F00",
   "line": "bit 7 set = the unit is dead, skip it"
  },
  "8546": {
   "unit": "game/comcenScreens6F00",
   "line": "type is in bits 0-2 (0 GRUNT, 1 RIDER, 2 BOOMER, 3 SPY, 4 COMCEN)"
  },
  "8548": {
   "unit": "game/comcenScreens6F00",
   "line": "is it the type being counted?"
  },
  "854A": {
   "unit": "game/comcenScreens6F00",
   "line": "no - next slot"
  },
  "854C": {
   "unit": "game/comcenScreens6F00",
   "line": "yes - one more of this type alive"
  },
  "854E": {
   "unit": "game/comcenScreens6F00",
   "line": "next unit index downwards (comcen is the highest of its side)"
  },
  "854F": {
   "unit": "game/comcenScreens6F00",
   "line": "one slot fewer"
  },
  "8551": {
   "unit": "game/comcenScreens6F00",
   "line": "50 slots not done yet"
  },
  "8553": {
   "unit": "game/comcenScreens6F00",
   "line": "X = the column just set"
  },
  "8555": {
   "unit": "game/comcenScreens6F00",
   "line": "the count"
  },
  "8557": {
   "unit": "game/comcenScreens6F00",
   "line": "print it right-aligned in a 3-character field"
  },
  "855A": {
   "unit": "game/comcenScreens6F00",
   "line": "one column done"
  },
  "855C": {
   "unit": "game/comcenScreens6F00",
   "line": "both columns done - next unit type"
  },
  "855E": {
   "unit": "game/comcenScreens6F00",
   "line": "own comcen index"
  },
  "8561": {
   "unit": "game/comcenScreens6F00",
   "line": "EOR $52 swaps 49 <-> 99, i.e. the other side's comcen"
  },
  "8563": {
   "unit": "game/comcenScreens6F00",
   "line": "scan starts there"
  },
  "8564": {
   "unit": "game/comcenScreens6F00",
   "line": "column 14 = the THEM column"
  },
  "8566": {
   "unit": "game/comcenScreens6F00",
   "line": "always taken - print the second column"
  },
  "8568": {
   "unit": "game/comcenScreens6F00",
   "line": "next text row"
  },
  "856A": {
   "unit": "game/comcenScreens6F00",
   "line": "next unit type"
  },
  "856C": {
   "unit": "game/comcenScreens6F00",
   "line": "which type is next?"
  },
  "856E": {
   "unit": "game/comcenScreens6F00",
   "line": "types 0-3 only; type 4 (COMCEN) is not listed"
  },
  "8570": {
   "unit": "game/comcenScreens6F00",
   "line": "more types to do"
  },
  "8572": {
   "unit": "game/comcenScreens6F00",
   "block": [
    "Fixed rows below the unit table: DRONES (14), MISSILES (15), COMSPEED (17), TER PTS (18), RECYCLER",
    "(19) and the map name (20)."
   ],
   "line": "skip a row - DRONES is on row 14"
  },
  "8574": {
   "unit": "game/comcenScreens6F00",
   "line": "US column"
  },
  "8576": {
   "unit": "game/comcenScreens6F00",
   "line": "$92AE = our drones left"
  },
  "8579": {
   "unit": "game/comcenScreens6F00",
   "line": "print it"
  },
  "857C": {
   "unit": "game/comcenScreens6F00",
   "line": "THEM column"
  },
  "857E": {
   "unit": "game/comcenScreens6F00",
   "line": "$92AF = the opponent's drones left"
  },
  "8581": {
   "unit": "game/comcenScreens6F00",
   "line": "print it"
  },
  "8584": {
   "unit": "game/comcenScreens6F00",
   "line": "row 15 = MISSILES"
  },
  "8586": {
   "unit": "game/comcenScreens6F00",
   "line": "US column"
  },
  "8588": {
   "unit": "game/comcenScreens6F00",
   "line": "$92B0 = our missiles left"
  },
  "858B": {
   "unit": "game/comcenScreens6F00",
   "line": "print it"
  },
  "858E": {
   "unit": "game/comcenScreens6F00",
   "line": "THEM column"
  },
  "8590": {
   "unit": "game/comcenScreens6F00",
   "line": "$92B1 = the opponent's missiles left"
  },
  "8593": {
   "unit": "game/comcenScreens6F00",
   "line": "print it"
  },
  "8596": {
   "unit": "game/comcenScreens6F00",
   "line": "row 16 ..."
  },
  "8598": {
   "unit": "game/comcenScreens6F00",
   "line": "... row 17 = COMSPEED"
  },
  "859A": {
   "unit": "game/comcenScreens6F00",
   "line": "US column"
  },
  "859C": {
   "unit": "game/comcenScreens6F00",
   "line": "$92B4 = our comcen speed class 0-5"
  },
  "859F": {
   "unit": "game/comcenScreens6F00",
   "line": "translate it into the printed figure"
  },
  "85A2": {
   "unit": "game/comcenScreens6F00",
   "line": "print it"
  },
  "85A5": {
   "unit": "game/comcenScreens6F00",
   "line": "THEM column"
  },
  "85A7": {
   "unit": "game/comcenScreens6F00",
   "line": "$92B5 = the opponent's speed class"
  },
  "85AA": {
   "unit": "game/comcenScreens6F00",
   "line": "translate it"
  },
  "85AD": {
   "unit": "game/comcenScreens6F00",
   "line": "print it"
  },
  "85B0": {
   "unit": "game/comcenScreens6F00",
   "line": "row 18 = TER PTS"
  },
  "85B2": {
   "unit": "game/comcenScreens6F00",
   "line": "US column"
  },
  "85B4": {
   "unit": "game/comcenScreens6F00",
   "line": "$0B9F = which side we are"
  },
  "85B7": {
   "unit": "game/comcenScreens6F00",
   "line": "the OTHER side's index - note this is the opposite indexing to the RECYCLER row below, so one of the two rows is probably swapped"
  },
  "85B9": {
   "unit": "game/comcenScreens6F00",
   "line": "index the per-side table"
  },
  "85BA": {
   "unit": "game/comcenScreens6F00",
   "line": "$92AC[side EOR 1]"
  },
  "85BD": {
   "unit": "game/comcenScreens6F00",
   "line": "print it"
  },
  "85C0": {
   "unit": "game/comcenScreens6F00",
   "line": "THEM column"
  },
  "85C2": {
   "unit": "game/comcenScreens6F00",
   "line": "our own side index"
  },
  "85C5": {
   "unit": "game/comcenScreens6F00",
   "line": "$92AC[side]"
  },
  "85C8": {
   "unit": "game/comcenScreens6F00",
   "line": "print it"
  },
  "85CB": {
   "unit": "game/comcenScreens6F00",
   "line": "row 19 = RECYCLER"
  },
  "85CD": {
   "unit": "game/comcenScreens6F00",
   "line": "US column"
  },
  "85CF": {
   "unit": "game/comcenScreens6F00",
   "line": "position the cursor there"
  },
  "85D2": {
   "unit": "game/comcenScreens6F00",
   "line": "our side"
  },
  "85D5": {
   "unit": "game/comcenScreens6F00",
   "line": "$92A6[side] = FULL / HALF / NONE"
  },
  "85D8": {
   "unit": "game/comcenScreens6F00",
   "line": "print the 4-character option word"
  },
  "85DB": {
   "unit": "game/comcenScreens6F00",
   "line": "THEM column"
  },
  "85DD": {
   "unit": "game/comcenScreens6F00",
   "line": "position the cursor there"
  },
  "85E0": {
   "unit": "game/comcenScreens6F00",
   "line": "our side ..."
  },
  "85E3": {
   "unit": "game/comcenScreens6F00",
   "line": "... EOR 1 = the opponent"
  },
  "85E5": {
   "unit": "game/comcenScreens6F00",
   "line": "index the table"
  },
  "85E6": {
   "unit": "game/comcenScreens6F00",
   "line": "$92A6[side EOR 1]"
  },
  "85E9": {
   "unit": "game/comcenScreens6F00",
   "line": "print the option word"
  },
  "85EC": {
   "unit": "game/comcenScreens6F00",
   "line": "row 20 = the map name"
  },
  "85EE": {
   "unit": "game/comcenScreens6F00",
   "line": "column 9 for side 0 ..."
  },
  "85F0": {
   "unit": "game/comcenScreens6F00",
   "line": "which side are we?"
  },
  "85F3": {
   "unit": "game/comcenScreens6F00",
   "line": "side 0 - use column 9"
  },
  "85F5": {
   "unit": "game/comcenScreens6F00",
   "line": "side 1 - the name is printed under the THEM column instead"
  },
  "85F7": {
   "unit": "game/comcenScreens6F00",
   "line": "position the cursor"
  },
  "85FA": {
   "unit": "game/comcenScreens6F00",
   "line": "$0BA9 = the five-character map name, terminated at $0BAE"
  },
  "85FC": {
   "unit": "game/comcenScreens6F00",
   "line": "high byte of the string address"
  },
  "85FE": {
   "unit": "game/comcenScreens6F00",
   "line": "print it and return through the text engine"
  }
 },
 "dataTypes": {
  "7FCE": [
   "byte",
   8
  ],
  "7FD6": [
   "byte",
   8
  ],
  "7FDE": [
   "byte",
   8
  ],
  "82B5": [
   "byte",
   8
  ],
  "82BD": [
   "byte",
   2
  ],
  "8512": [
   "byte",
   6
  ]
 },
 "zpComments": {
  "6C": "first of the six IRQ frame countdowns $6C-$71: joystick auto-repeat delay; the comcen screens reuse it as the button/box blink interval (11 frames on the idle drone screen, 40 while a drone is in flight)",
  "6E": "frame countdown between highlight toggles of the selected menu row / console tab; on the drone screen it is the 20-frame lockout between joystick heading reads"
 }
}
