{
 "chunk": "game_mainProgram0800_2367",
 "unit": "game/mainProgram0800",
 "unitLabels": {
  "game/mainProgram0800": {
   "24E2": "pathMarkerCol",
   "24E3": "pathMarkerRow",
   "2703": "tileCopyStore",
   "27D3": "terrainShapeRowImm",
   "27F1": "terrainShapeRowCounter",
   "2807": "mapCellByteImm",
   "2840": "ownSideBranchOpcode",
   "2846": "unitVisibleBranchOpcode",
   "28B7": "unitGlyphEorMask",
   "28BF": "cloakSkipFlagImm"
  }
 },
 "notes": {
  "2367": {
   "unit": "game/mainProgram0800",
   "routine": [
    "checkUserInterrupt - The 'may this passive display keep running?' test used by the idle loops of the info panel and the path preview. It returns C=0 only while the text engine agrees the UI may continue (checkUiContinue $C451), no function key is waiting to switch screens, the joystick is centred and the fire button is not held. Anything else returns C=1 and freezes stick and keyboard for 30 frames so the same push is not consumed twice.",
    "In:  requestedScreenKey zp_66 ($FF = no function key), joyDirection zp_5D ($0F = centred), joyFireUp zp_5F / fireButtonArmed zp_79 through isFireHeld, plus everything checkUiContinue looks at (filmPlaybackMode, gameEndCountdown, pauseState, gamePhase, lastTypedKey)",
    "Out: C=0 keep going / C=1 stop; on C=1 inputLockoutTimer $0B7D = $1E (30 frames)",
    "Called from: the path-preview loop at $229A and the info-panel idle loop at $233F"
   ],
   "line": "ask the text engine whether a modal display may still run (A=1 = yes)"
  },
  "236A": {
   "unit": "game/mainProgram0800",
   "line": "branch if it says stop: game ending, paused, SPACE typed or a screen change pending"
  },
  "236C": {
   "unit": "game/mainProgram0800",
   "line": "function key latched by the IRQ: 0-3 = F1/F3/F5/F7, $FF = none pending"
  },
  "236E": {
   "unit": "game/mainProgram0800",
   "line": "branch if a function key is waiting to switch screens"
  },
  "2370": {
   "unit": "game/mainProgram0800",
   "line": "joystick 2 direction nibble latched by the IRQ (active low)"
  },
  "2372": {
   "unit": "game/mainProgram0800",
   "line": "$0F = no direction pulled low = stick centred"
  },
  "2374": {
   "unit": "game/mainProgram0800",
   "line": "branch if the player has pushed the stick"
  },
  "2376": {
   "unit": "game/mainProgram0800",
   "line": "level test of the fire button: A=0 while it is held down"
  },
  "2379": {
   "unit": "game/mainProgram0800",
   "line": "branch if the button is down"
  },
  "237B": {
   "unit": "game/mainProgram0800",
   "line": "nothing interrupted: C=0 tells the caller to carry on"
  },
  "237C": {
   "unit": "game/mainProgram0800",
   "line": "back to the idle loop"
  },
  "237D": {
   "unit": "game/mainProgram0800",
   "block": [
    "Something wants attention: stop the display and swallow the input for half a second so the",
    "menu the caller returns to does not immediately act on the same stick push or key."
   ],
   "line": "30 frames = about half a second"
  },
  "237F": {
   "unit": "game/mainProgram0800",
   "line": "the IRQ ignores stick and keyboard while this counts down"
  },
  "2382": {
   "unit": "game/mainProgram0800",
   "line": "C=1 = the caller must tear its display down"
  },
  "2383": {
   "unit": "game/mainProgram0800",
   "line": "return to the caller's loop, which exits on C=1"
  },
  "2384": {
   "unit": "game/mainProgram0800",
   "routine": [
    "printUnitStatusLines - Fills in the value column of the unit info panel for selectedUnit ($90F8): the group name on row 13, the energy percentage on row 17, the weapon state on row 18, the action on row 19 and one flag word (BLITZ / CLOAKED / DUG IN) on row 20, then adds up to two '*' zone markers in front of the unit name on row 12. The labels GROUP:/ENERGY:/WEAPON:/ACTION: themselves are printed once by showUnitInfoPanel from the string at $CAE9.",
    "In:  selectedUnit $90F8; the unit arrays unitDisplayFlagsTable $FB54 (bit 0 fired, bit 1 pinned, bits 2-4 group id, bit 6 grouped), unitEnergyTable $F9C4, unitTypeTable $F76C, unitStunTable $F960, unitPathColTable $F898 (waypoint column + cloak bits), unitPathRowTable $F8FC (waypoint row + blitz bits), unitDestColTable $F7D0, unitDestRowTable $F834, unitMovePointsTable $FA28; currentUnit $90FA, gameClock $91CB, markerBlinkTimer zp_6F",
    "Out: text drawn into the bitmap; textWindowLeft zp_3B / textWindowWidth zp_3C / textCursorCol zp_3F / textCursorRow zp_40 left pointing into the panel, markerBlinkTimer = 20 when FIRED starts, scratch18/scratch19 clobbered",
    "Called from: showUnitInfoPanel, once when the panel is opened ($22E7) and again on every refresh while the unit is in combat ($2330)"
   ],
   "block": [
    "Set up the narrow value column of the info panel. The panel window itself is columns 25-38;",
    "the values are printed in columns 31/32 so they line up behind the labels."
   ],
   "line": "column 32 = the value column of the panel"
  },
  "2386": {
   "unit": "game/mainProgram0800",
   "line": "text engine window left edge"
  },
  "2388": {
   "unit": "game/mainProgram0800",
   "line": "9 characters wide, i.e. out to the right frame"
  },
  "238A": {
   "unit": "game/mainProgram0800",
   "line": "text engine window width / wrap point"
  },
  "238C": {
   "unit": "game/mainProgram0800",
   "line": "the group name starts one column further left (column 31)"
  },
  "238E": {
   "unit": "game/mainProgram0800",
   "line": "first character of the window"
  },
  "2390": {
   "unit": "game/mainProgram0800",
   "line": "cursor column, relative to textWindowLeft"
  },
  "2392": {
   "unit": "game/mainProgram0800",
   "line": "screen row 13 = the GROUP: line"
  },
  "2394": {
   "unit": "game/mainProgram0800",
   "line": "move the text cursor there (recomputes the bitmap pointers)"
  },
  "2397": {
   "unit": "game/mainProgram0800",
   "block": [
    "GROUP: line - the group name, or ' - ' when the unit is not in a group."
   ],
   "line": "string index 0 = the ' - ' filler at $C9A4"
  },
  "2399": {
   "unit": "game/mainProgram0800",
   "line": "the unit the info panel is showing"
  },
  "239C": {
   "unit": "game/mainProgram0800",
   "line": "its group/status flags ($FB54)"
  },
  "239F": {
   "unit": "game/mainProgram0800",
   "line": "bit 6 = the unit belongs to a group"
  },
  "23A1": {
   "unit": "game/mainProgram0800",
   "line": "branch if it is a group member"
  },
  "23A3": {
   "unit": "game/mainProgram0800",
   "line": "low byte of the ' - ' string table entry at $C9A4"
  },
  "23A5": {
   "unit": "game/mainProgram0800",
   "line": "high byte; entry 0 of that 8-byte-per-entry table is ' - '"
  },
  "23A7": {
   "unit": "game/mainProgram0800",
   "line": "always taken"
  },
  "23A9": {
   "unit": "game/mainProgram0800",
   "line": "re-read the group flags"
  },
  "23AC": {
   "unit": "game/mainProgram0800",
   "line": "bits 2-4 hold the group id"
  },
  "23AE": {
   "unit": "game/mainProgram0800",
   "line": "shift the id down..."
  },
  "23AF": {
   "unit": "game/mainProgram0800",
   "line": "...to 0-6 = ALPHA, BAKER, CHARLIE, DELTA, EASY, FOXTROT, GROVER"
  },
  "23B0": {
   "unit": "game/mainProgram0800",
   "line": "index into the name table"
  },
  "23B1": {
   "unit": "game/mainProgram0800",
   "line": "low byte of groupNameTable $C92C (7 names of 8 bytes)"
  },
  "23B3": {
   "unit": "game/mainProgram0800",
   "line": "high byte"
  },
  "23B5": {
   "unit": "game/mainProgram0800",
   "line": "print entry X of the 8-byte-per-entry string table at A/Y"
  },
  "23B8": {
   "unit": "game/mainProgram0800",
   "block": [
    "ENERGY: line - the energy byte is 0-50 and is shown as a percentage (x2)."
   ],
   "line": "value column back to 32 for all the remaining lines"
  },
  "23BA": {
   "unit": "game/mainProgram0800",
   "line": "the GROUP: label is followed by three blank rows..."
  },
  "23BC": {
   "unit": "game/mainProgram0800",
   "line": "...row 15..."
  },
  "23BE": {
   "unit": "game/mainProgram0800",
   "line": "...row 16"
  },
  "23C0": {
   "unit": "game/mainProgram0800",
   "line": "newline lands on row 17 = the ENERGY: line, cursor at column 0"
  },
  "23C3": {
   "unit": "game/mainProgram0800",
   "line": "the selected unit again"
  },
  "23C6": {
   "unit": "game/mainProgram0800",
   "line": "energy byte ($F9C4)"
  },
  "23C9": {
   "unit": "game/mainProgram0800",
   "line": "bits 0-5 = energy 0-50; bits 6-7 are the 'spotted by the enemy' flags"
  },
  "23CB": {
   "unit": "game/mainProgram0800",
   "line": "x2 gives 0-100 percent"
  },
  "23CC": {
   "unit": "game/mainProgram0800",
   "line": "high byte of the number to print = 0"
  },
  "23CE": {
   "unit": "game/mainProgram0800",
   "line": "print it as decimal digits"
  },
  "23D1": {
   "unit": "game/mainProgram0800",
   "line": "'%'"
  },
  "23D3": {
   "unit": "game/mainProgram0800",
   "line": "print it"
  },
  "23D6": {
   "unit": "game/mainProgram0800",
   "line": "trailing space, wipes the third digit of a previous 100%"
  },
  "23D8": {
   "unit": "game/mainProgram0800",
   "line": "print it"
  },
  "23DB": {
   "unit": "game/mainProgram0800",
   "block": [
    "WEAPON: line - READY / RELOAD / FIRED, or ' - ' for a spy, a comcen or a cloaked unit,",
    "or STUN'D while the unit is stunned. Entries are 8 bytes apart in the table at $C98C:",
    "0 READY, 1 RELOAD, 2 FIRED, 3 ' - ', 4 REPAIR, 5 MOVE, 6 PINNED, 7 STUN'D."
   ],
   "line": "newline to row 18 = the WEAPON: line"
  },
  "23DE": {
   "unit": "game/mainProgram0800",
   "line": "selected unit"
  },
  "23E1": {
   "unit": "game/mainProgram0800",
   "line": "waypoint column + cloak bits ($F898; bit 7 = cloaked)"
  },
  "23E4": {
   "unit": "game/mainProgram0800",
   "line": "keep the cloak byte for the test below"
  },
  "23E6": {
   "unit": "game/mainProgram0800",
   "line": "type byte ($F76C)"
  },
  "23E9": {
   "unit": "game/mainProgram0800",
   "line": "keep it as well"
  },
  "23EB": {
   "unit": "game/mainProgram0800",
   "line": "stun / dig-in byte ($F960)"
  },
  "23EE": {
   "unit": "game/mainProgram0800",
   "line": "low nybble = clicks of stun left"
  },
  "23F0": {
   "unit": "game/mainProgram0800",
   "line": "branch if the unit is not stunned"
  },
  "23F2": {
   "unit": "game/mainProgram0800",
   "line": "table entry 7 = STUN'D"
  },
  "23F4": {
   "unit": "game/mainProgram0800",
   "line": "always taken"
  },
  "23F6": {
   "unit": "game/mainProgram0800",
   "line": "table entry 3 = ' - ' (no weapon state to show)"
  },
  "23F8": {
   "unit": "game/mainProgram0800",
   "line": "unit type byte"
  },
  "23FA": {
   "unit": "game/mainProgram0800",
   "line": "bits 0-2 = 0 GRUNT, 1 RIDER, 2 BOOMER, 3 SPY, 4 COMCEN"
  },
  "23FC": {
   "unit": "game/mainProgram0800",
   "line": "SPY and COMCEN are types 3 and 4..."
  },
  "23FE": {
   "unit": "game/mainProgram0800",
   "line": "...and carry no weapon, so leave ' - '"
  },
  "2400": {
   "unit": "game/mainProgram0800",
   "line": "the 20-frame hold started when FIRED was printed (shared blink timer zp_6F)"
  },
  "2402": {
   "unit": "game/mainProgram0800",
   "line": "while it runs skip the field entirely so FIRED stays on screen"
  },
  "2404": {
   "unit": "game/mainProgram0800",
   "line": "the cloak byte saved above"
  },
  "2406": {
   "unit": "game/mainProgram0800",
   "line": "a cloaked unit cannot fire: show ' - '"
  },
  "2408": {
   "unit": "game/mainProgram0800",
   "line": "table entry 0 = READY"
  },
  "240A": {
   "unit": "game/mainProgram0800",
   "line": "selected unit"
  },
  "240D": {
   "unit": "game/mainProgram0800",
   "line": "status flags"
  },
  "2410": {
   "unit": "game/mainProgram0800",
   "line": "bit 0 = the unit has fired and is reloading"
  },
  "2412": {
   "unit": "game/mainProgram0800",
   "line": "not fired -> READY"
  },
  "2414": {
   "unit": "game/mainProgram0800",
   "line": "table entry 1 = RELOAD"
  },
  "2415": {
   "unit": "game/mainProgram0800",
   "line": "is this the unit the simulation is stepping right now?"
  },
  "2418": {
   "unit": "game/mainProgram0800",
   "line": "no -> just RELOAD"
  },
  "241A": {
   "unit": "game/mainProgram0800",
   "line": "unit index"
  },
  "241B": {
   "unit": "game/mainProgram0800",
   "line": "mix it with the game clock..."
  },
  "241E": {
   "unit": "game/mainProgram0800",
   "line": "...and keep bit 0, so only every other click flashes"
  },
  "2420": {
   "unit": "game/mainProgram0800",
   "line": "off click -> RELOAD"
  },
  "2422": {
   "unit": "game/mainProgram0800",
   "line": "20 frames"
  },
  "2424": {
   "unit": "game/mainProgram0800",
   "line": "hold the field so FIRED stays readable"
  },
  "2426": {
   "unit": "game/mainProgram0800",
   "line": "table entry 2 = FIRED"
  },
  "2428": {
   "unit": "game/mainProgram0800",
   "line": "low byte of the weapon-word table $C98C"
  },
  "242A": {
   "unit": "game/mainProgram0800",
   "line": "high byte"
  },
  "242C": {
   "unit": "game/mainProgram0800",
   "line": "print entry X"
  },
  "242F": {
   "unit": "game/mainProgram0800",
   "block": [
    "A boomer with a locked target gets a '*' after the weapon word."
   ],
   "line": "selected unit"
  },
  "2432": {
   "unit": "game/mainProgram0800",
   "line": "boomers return their target byte, other types a small positive value"
  },
  "2435": {
   "unit": "game/mainProgram0800",
   "line": "into Y for the test"
  },
  "2436": {
   "unit": "game/mainProgram0800",
   "line": "space = nothing to mark"
  },
  "2438": {
   "unit": "game/mainProgram0800",
   "line": "bit 7 of the target byte = a target is locked"
  },
  "243A": {
   "unit": "game/mainProgram0800",
   "line": "no target -> print the space"
  },
  "243C": {
   "unit": "game/mainProgram0800",
   "line": "'*' = this boomer has a target locked"
  },
  "243E": {
   "unit": "game/mainProgram0800",
   "line": "print the marker"
  },
  "2441": {
   "unit": "game/mainProgram0800",
   "block": [
    "ACTION: line - ' - ' / REPAIR / MOVE / PINNED / STUN'D / DIGGIN, entries 0-5 of the",
    "8-byte-per-entry table at $C9A4."
   ],
   "line": "newline to row 19 = the ACTION: line"
  },
  "2444": {
   "unit": "game/mainProgram0800",
   "line": "table entry 2 = MOVE"
  },
  "2446": {
   "unit": "game/mainProgram0800",
   "line": "selected unit"
  },
  "2449": {
   "unit": "game/mainProgram0800",
   "line": "ordered destination column ($F7D0); bit 7 = no destination"
  },
  "244C": {
   "unit": "game/mainProgram0800",
   "line": "branch if the unit has nowhere to go"
  },
  "244E": {
   "unit": "game/mainProgram0800",
   "line": "status flags"
  },
  "2451": {
   "unit": "game/mainProgram0800",
   "line": "bit 1 = the unit was hit and is pinned down"
  },
  "2453": {
   "unit": "game/mainProgram0800",
   "line": "not pinned -> just check the movement points"
  },
  "2455": {
   "unit": "game/mainProgram0800",
   "line": "movement points ($FA28)"
  },
  "2458": {
   "unit": "game/mainProgram0800",
   "line": "fewer than 10 points left?"
  },
  "245A": {
   "unit": "game/mainProgram0800",
   "line": "yes -> PINNED rather than MOVE"
  },
  "245C": {
   "unit": "game/mainProgram0800",
   "line": "movement points again"
  },
  "245F": {
   "unit": "game/mainProgram0800",
   "line": "still positive -> MOVE"
  },
  "2461": {
   "unit": "game/mainProgram0800",
   "line": "table entry 3 = PINNED (out of movement)"
  },
  "2462": {
   "unit": "game/mainProgram0800",
   "line": "always taken"
  },
  "2464": {
   "unit": "game/mainProgram0800",
   "line": "table entry 0 = ' - ' (nothing to do)"
  },
  "2466": {
   "unit": "game/mainProgram0800",
   "line": "destination row ($F834); bit 7 = the unit is regaining energy"
  },
  "2469": {
   "unit": "game/mainProgram0800",
   "line": "not repairing -> leave ' - '"
  },
  "246B": {
   "unit": "game/mainProgram0800",
   "line": "table entry 1 = REPAIR"
  },
  "246C": {
   "unit": "game/mainProgram0800",
   "line": "stun / dig-in byte"
  },
  "246F": {
   "unit": "game/mainProgram0800",
   "line": "low nybble = stun clicks left"
  },
  "2471": {
   "unit": "game/mainProgram0800",
   "line": "not stunned"
  },
  "2473": {
   "unit": "game/mainProgram0800",
   "line": "table entry 4 = STUN'D"
  },
  "2475": {
   "unit": "game/mainProgram0800",
   "line": "always taken"
  },
  "2477": {
   "unit": "game/mainProgram0800",
   "line": "stun / dig-in byte again"
  },
  "247A": {
   "unit": "game/mainProgram0800",
   "line": "bit 7 = already dug in, that is a flag word not an action"
  },
  "247C": {
   "unit": "game/mainProgram0800",
   "line": "bits 5-7 also count the dig-in progress"
  },
  "247E": {
   "unit": "game/mainProgram0800",
   "line": "not digging in"
  },
  "2480": {
   "unit": "game/mainProgram0800",
   "line": "table entry 5 = DIGGIN"
  },
  "2482": {
   "unit": "game/mainProgram0800",
   "line": "low byte of the action-word table $C9A4"
  },
  "2484": {
   "unit": "game/mainProgram0800",
   "line": "high byte"
  },
  "2486": {
   "unit": "game/mainProgram0800",
   "line": "print entry X"
  },
  "2489": {
   "unit": "game/mainProgram0800",
   "block": [
    "Flag line - at most one of BLITZ, CLOAKED or DUG IN, printed further left (column 28)",
    "because the words are longer than the value column."
   ],
   "line": "column 28 for the wider flag word"
  },
  "248B": {
   "unit": "game/mainProgram0800",
   "line": "text window left edge"
  },
  "248D": {
   "unit": "game/mainProgram0800",
   "line": "newline to row 20"
  },
  "2490": {
   "unit": "game/mainProgram0800",
   "line": "selected unit"
  },
  "2493": {
   "unit": "game/mainProgram0800",
   "line": "waypoint row + blitz bits ($F8FC)"
  },
  "2496": {
   "unit": "game/mainProgram0800",
   "line": "bit 7 clear = blitz is off"
  },
  "2498": {
   "unit": "game/mainProgram0800",
   "line": "low byte of 'BLITZ' at $CB82"
  },
  "249A": {
   "unit": "game/mainProgram0800",
   "line": "high byte"
  },
  "249C": {
   "unit": "game/mainProgram0800",
   "line": "always taken"
  },
  "249E": {
   "unit": "game/mainProgram0800",
   "line": "waypoint column + cloak bits ($F898)"
  },
  "24A1": {
   "unit": "game/mainProgram0800",
   "line": "bit 7 clear = not cloaked"
  },
  "24A3": {
   "unit": "game/mainProgram0800",
   "line": "low byte of 'CLOAKED' at $CB7B"
  },
  "24A5": {
   "unit": "game/mainProgram0800",
   "line": "high byte"
  },
  "24A7": {
   "unit": "game/mainProgram0800",
   "line": "always taken"
  },
  "24A9": {
   "unit": "game/mainProgram0800",
   "line": "stun / dig-in byte"
  },
  "24AC": {
   "unit": "game/mainProgram0800",
   "line": "bit 7 clear = not dug in: print no flag word at all"
  },
  "24AE": {
   "unit": "game/mainProgram0800",
   "line": "low byte of 'DUG IN' at $CB87"
  },
  "24B0": {
   "unit": "game/mainProgram0800",
   "line": "high byte"
  },
  "24B2": {
   "unit": "game/mainProgram0800",
   "line": "print the bit-7 terminated string at A/Y"
  },
  "24B5": {
   "unit": "game/mainProgram0800",
   "line": "wipe whatever the previously shown unit left below this line"
  },
  "24B8": {
   "unit": "game/mainProgram0800",
   "block": [
    "Zone markers - unitTypeTable bits 3-4 record which half of the enemy ground this unit has",
    "already scored (bit 3 = the near zone, columns 10-19; bit 4 = the far end zone, columns",
    "0-9). One '*' is printed in front of the unit name for either, a second one for the far",
    "zone."
   ],
   "line": "value column back to 32"
  },
  "24BA": {
   "unit": "game/mainProgram0800",
   "line": "text window left edge"
  },
  "24BC": {
   "unit": "game/mainProgram0800",
   "line": "selected unit"
  },
  "24BF": {
   "unit": "game/mainProgram0800",
   "line": "type byte"
  },
  "24C2": {
   "unit": "game/mainProgram0800",
   "line": "bits 3-4 = zones this unit has already scored"
  },
  "24C4": {
   "unit": "game/mainProgram0800",
   "line": "no marker to print"
  },
  "24C6": {
   "unit": "game/mainProgram0800",
   "line": "keep the two bits over the window change"
  },
  "24C7": {
   "unit": "game/mainProgram0800",
   "line": "back to the full panel window (columns 25-38); leaves A = $0C"
  },
  "24CA": {
   "unit": "game/mainProgram0800",
   "line": "column 0 of the window = screen column 25"
  },
  "24CC": {
   "unit": "game/mainProgram0800",
   "line": "cursor column"
  },
  "24CE": {
   "unit": "game/mainProgram0800",
   "line": "A is still the $0C left by setInfoPanelWindow, so this lands on row 12 = the unit name line"
  },
  "24D1": {
   "unit": "game/mainProgram0800",
   "line": "'*'"
  },
  "24D3": {
   "unit": "game/mainProgram0800",
   "line": "print it in front of the name"
  },
  "24D6": {
   "unit": "game/mainProgram0800",
   "line": "get the zone bits back"
  },
  "24D7": {
   "unit": "game/mainProgram0800",
   "line": "bit 4 = the far, double-value end zone"
  },
  "24D9": {
   "unit": "game/mainProgram0800",
   "line": "near zone only -> one marker"
  },
  "24DB": {
   "unit": "game/mainProgram0800",
   "line": "a second '*'"
  },
  "24DD": {
   "unit": "game/mainProgram0800",
   "line": "print it"
  },
  "24E0": {
   "unit": "game/mainProgram0800",
   "line": "done: the caller redraws the sprites"
  },
  "24E1": {
   "unit": "game/mainProgram0800",
   "block": [
    "pathMarkerState - three RAM bytes living inside the code, the whole state of the animated",
    "path marker (sprite 1) that walks from the selected unit to its ordered destination on the",
    "mini-map: passes still to run, and the map cell the marker currently occupies."
   ],
   "line": "passes still to walk before the marker jumps back to the unit (reloaded with 5)"
  },
  "24E2": {
   "unit": "game/mainProgram0800",
   "line": "map column of the marker"
  },
  "24E3": {
   "unit": "game/mainProgram0800",
   "line": "map row of the marker"
  },
  "24E4": {
   "unit": "game/mainProgram0800",
   "routine": [
    "animatePathMarkerToDestination - Sets the line-stepper target to the ordered destination of selectedUnit and falls into animatePathMarkerStep, so the mini-map marker crawls from the unit towards where it has been sent. A unit with no destination just gets sprite 1 switched off.",
    "In:  selectedUnit $90F8, unitDestColTable $F7D0 (bit 7 set = no destination), unitDestRowTable $F834",
    "Out: lineEndCol zp_95 / lineEndRow zp_94 = the destination cell, then everything animatePathMarkerStep does; sprite 1 hidden when there is no destination",
    "Called from: the info-panel idle loop ($233C) and the unit menu loops at $6065 and $6214"
   ],
   "line": "the unit the panel is showing"
  },
  "24E7": {
   "unit": "game/mainProgram0800",
   "line": "its ordered destination column; bit 7 = no destination"
  },
  "24EA": {
   "unit": "game/mainProgram0800",
   "line": "nothing ordered -> just hide the marker"
  },
  "24EC": {
   "unit": "game/mainProgram0800",
   "line": "line stepper target column"
  },
  "24EE": {
   "unit": "game/mainProgram0800",
   "line": "destination row"
  },
  "24F1": {
   "unit": "game/mainProgram0800",
   "line": "line stepper target row"
  },
  "24F3": {
   "unit": "game/mainProgram0800",
   "routine": [
    "animatePathMarkerStep - Advances the mini-map path marker by one cell every 4 frames, but only after the player has been idle for 90 frames. Each step walks one cell of the Bresenham line from pathMarkerCol/Row towards lineEndCol/Row and moves sprite 1 there; at the end of the line the pass counter is decremented and the sprite is blinked, and when the counter runs past zero resetPathMarker puts the marker back on the unit.",
    "In:  inputIdleFrames $90EE, joyRepeatTimer zp_6C (used here as the step delay), pathMarkerState $24E1, pathMarkerCol/Row $24E2/$24E3, target lineEndCol zp_95 / lineEndRow zp_94",
    "Out: pathMarkerCol/Row advanced, sprite 1 position shadows $9011/$9019 and enable bit 1 of $9022, joyRepeatTimer = 4, line stepper state zp_8C-zp_97",
    "Called from: the path-preview loop at $2297, and by fall-through from animatePathMarkerToDestination"
   ],
   "line": "frames since the player last moved the stick or pressed a key"
  },
  "24F6": {
   "unit": "game/mainProgram0800",
   "line": "90 frames = 1.5 seconds of doing nothing"
  },
  "24F8": {
   "unit": "game/mainProgram0800",
   "line": "idle long enough: run the animation"
  },
  "24FA": {
   "unit": "game/mainProgram0800",
   "line": "player is active -> no marker on the map"
  },
  "24FD": {
   "unit": "game/mainProgram0800",
   "line": "shared frame countdown, used here as the delay between marker steps"
  },
  "24FF": {
   "unit": "game/mainProgram0800",
   "line": "expired: time for the next cell"
  },
  "2501": {
   "unit": "game/mainProgram0800",
   "line": "still counting: leave the marker where it stands"
  },
  "2504": {
   "unit": "game/mainProgram0800",
   "line": "4 frames per cell"
  },
  "2506": {
   "unit": "game/mainProgram0800",
   "line": "reload the step delay"
  },
  "2508": {
   "unit": "game/mainProgram0800",
   "line": "marker column"
  },
  "250B": {
   "unit": "game/mainProgram0800",
   "line": "line stepper current column = where the marker stands"
  },
  "250D": {
   "unit": "game/mainProgram0800",
   "line": "marker row"
  },
  "2510": {
   "unit": "game/mainProgram0800",
   "line": "line stepper current row"
  },
  "2512": {
   "unit": "game/mainProgram0800",
   "line": "set up the walk to the target cell; A = number of steps"
  },
  "2515": {
   "unit": "game/mainProgram0800",
   "line": "zero steps (marker already on the target) -> count this as a finished pass"
  },
  "2517": {
   "unit": "game/mainProgram0800",
   "line": "advance one cell along the line"
  },
  "251A": {
   "unit": "game/mainProgram0800",
   "line": "new row"
  },
  "251C": {
   "unit": "game/mainProgram0800",
   "line": "remember it"
  },
  "251F": {
   "unit": "game/mainProgram0800",
   "line": "new column"
  },
  "2521": {
   "unit": "game/mainProgram0800",
   "line": "remember it"
  },
  "2524": {
   "unit": "game/mainProgram0800",
   "line": "put sprite 1 on that mini-map cell"
  },
  "2527": {
   "unit": "game/mainProgram0800",
   "line": "steps still to walk"
  },
  "2529": {
   "unit": "game/mainProgram0800",
   "line": "more to come -> keep the marker visible"
  },
  "252B": {
   "unit": "game/mainProgram0800",
   "block": [
    "End of one pass over the line: blink the marker by toggling sprite 1, and after six passes",
    "(the counter starts at 5 and is reset by resetPathMarker) start over at the unit."
   ],
   "line": "one pass of the line finished"
  },
  "252E": {
   "unit": "game/mainProgram0800",
   "line": "counter ran past zero -> reload the marker onto the unit"
  },
  "2530": {
   "unit": "game/mainProgram0800",
   "line": "sprite enable shadow"
  },
  "2533": {
   "unit": "game/mainProgram0800",
   "line": "bit 1 = sprite 1, the marker"
  },
  "2535": {
   "unit": "game/mainProgram0800",
   "line": "currently visible -> hide it, which makes the line end blink"
  },
  "2537": {
   "unit": "game/mainProgram0800",
   "line": "sprite 1 bit"
  },
  "2539": {
   "unit": "game/mainProgram0800",
   "line": "show the marker (and return)"
  },
  "253C": {
   "unit": "game/mainProgram0800",
   "line": "no marker wanted: switch sprite 1 off"
  },
  "253F": {
   "unit": "game/mainProgram0800",
   "line": "nothing to do this frame"
  },
  "2540": {
   "unit": "game/mainProgram0800",
   "routine": [
    "resetPathMarker - Puts the path marker back at the start: sprite 1 off, marker cell and pass counter back to the selected unit, and sprite 1's shape reloaded. Normally that is the small grey block; when the mini-map is showing the zoomed group box ($920A bit 7) the marker instead copies sprite 0's cursor box so it matches the enlarged cells.",
    "In:  selectedUnit $90F8, groupBoxActive $920A bit 7, sprite 0 shape at $8A00",
    "Out: sprite 1 shape at $8A40 and colour shadow $9009, activeSpriteIndex $91CC left 0, pathMarkerState/Col/Row reloaded, sprite 1 enabled in the zoomed case",
    "Called from: the info-panel entry and idle loops ($227B, $22FF), the unit menu at $5F18, and from animatePathMarkerStep when the pass counter runs out ($252E)"
   ],
   "line": "marker off while its shape is swapped"
  },
  "2543": {
   "unit": "game/mainProgram0800",
   "line": "pass counter back to 5 and the marker back onto the unit"
  },
  "2546": {
   "unit": "game/mainProgram0800",
   "line": "bit 7 set = the mini-map is zoomed onto the group box"
  },
  "2549": {
   "unit": "game/mainProgram0800",
   "line": "zoomed: use a copy of the cursor box instead"
  },
  "254B": {
   "unit": "game/mainProgram0800",
   "line": "sprite 1"
  },
  "254D": {
   "unit": "game/mainProgram0800",
   "line": "tell setCursorShapeBlock which sprite to load"
  },
  "2550": {
   "unit": "game/mainProgram0800",
   "line": "load the small solid block shape ($9E92) and colour it grey"
  },
  "2553": {
   "unit": "game/mainProgram0800",
   "line": "sprite 0"
  },
  "2555": {
   "unit": "game/mainProgram0800",
   "line": "back to the default sprite for the cursor routines"
  },
  "2558": {
   "unit": "game/mainProgram0800",
   "line": "done"
  },
  "2559": {
   "unit": "game/mainProgram0800",
   "block": [
    "Zoomed mini-map: the marker has to be as large as the cursor box, so sprite 1 simply gets a",
    "copy of sprite 0's current shape."
   ],
   "line": "64 shape bytes, last one first"
  },
  "255B": {
   "unit": "game/mainProgram0800",
   "line": "byte of the sprite 0 cursor box ($8A00)"
  },
  "255E": {
   "unit": "game/mainProgram0800",
   "line": "into the sprite 1 shape ($8A40)"
  },
  "2561": {
   "unit": "game/mainProgram0800",
   "line": "next byte down"
  },
  "2562": {
   "unit": "game/mainProgram0800",
   "line": "until all 64 are copied"
  },
  "2564": {
   "unit": "game/mainProgram0800",
   "line": "sprite 1 bit"
  },
  "2566": {
   "unit": "game/mainProgram0800",
   "line": "make the marker visible"
  },
  "2569": {
   "unit": "game/mainProgram0800",
   "line": "grey"
  },
  "256B": {
   "unit": "game/mainProgram0800",
   "line": "sprite 1 colour shadow, copied to $D028 by the IRQ"
  },
  "256E": {
   "unit": "game/mainProgram0800",
   "line": "done"
  },
  "256F": {
   "unit": "game/mainProgram0800",
   "routine": [
    "resetPathMarkerToUnit - Reloads the marker's pass counter with 5 and moves the marker cell onto the selected unit's map position, then falls into positionMarkerSprite to place sprite 1 there.",
    "In:  selectedUnit $90F8, unitColTable $F640, unitRowTable $F6A4",
    "Out: pathMarkerState $24E1 = 5, pathMarkerCol/Row $24E2/$24E3, sprite 1 position shadows; X = column, Y = row",
    "Called from: the path-preview entry at $225D and resetPathMarker ($2543)"
   ],
   "line": "five more passes over the line"
  },
  "2571": {
   "unit": "game/mainProgram0800",
   "line": "reload the pass counter"
  },
  "2574": {
   "unit": "game/mainProgram0800",
   "line": "the selected unit"
  },
  "2577": {
   "unit": "game/mainProgram0800",
   "line": "its map column"
  },
  "257A": {
   "unit": "game/mainProgram0800",
   "line": "marker column"
  },
  "257D": {
   "unit": "game/mainProgram0800",
   "line": "X = column for positionMarkerSprite"
  },
  "257E": {
   "unit": "game/mainProgram0800",
   "line": "its map row"
  },
  "2581": {
   "unit": "game/mainProgram0800",
   "line": "marker row"
  },
  "2584": {
   "unit": "game/mainProgram0800",
   "line": "Y = row for positionMarkerSprite"
  },
  "2585": {
   "unit": "game/mainProgram0800",
   "routine": [
    "positionMarkerSprite - Converts a map cell to mini-map sprite coordinates (column*4+$1F, row*4+$39, or the group-box mapping when the mini-map is zoomed) and stores them in sprite 1's position shadows, which the raster IRQ copies to the VIC.",
    "In:  X = map column, Y = map row",
    "Out: sprite1XShadow $9011, sprite1YShadow $9019",
    "Called from: animatePathMarkerStep ($2524) and by fall-through from resetPathMarkerToUnit"
   ],
   "line": "cell -> sprite coordinates on the 40x40 mini-map (4 pixels per cell)"
  },
  "2588": {
   "unit": "game/mainProgram0800",
   "line": "sprite 1 X shadow"
  },
  "258B": {
   "unit": "game/mainProgram0800",
   "line": "sprite 1 Y shadow"
  },
  "258E": {
   "unit": "game/mainProgram0800",
   "line": "done"
  },
  "258F": {
   "unit": "game/mainProgram0800",
   "routine": [
    "recenterViewportOnUnit - Makes sure the selected unit sits under the battlefield cursor. The cursor is always the centre cell of the 7x5 view (viewOriginCol+3, viewOriginRow+2); if the unit is somewhere else the view is scrolled to it one cell per frame by scrollViewToUnit.",
    "In:  selectedUnit $90F8, viewOriginCol zp_A5 / viewOriginRow zp_A6, unitColTable $F640 / unitRowTable $F6A4",
    "Out: lineCurCol zp_93 / lineCurRow zp_92 = the cursor cell (the scroll starts there); the view scrolled and redrawn",
    "Called from: the info-panel entry at $2239 and the unit selection code at $4BF6 and $4C4C"
   ],
   "line": "the unit that must end up under the cursor"
  },
  "2592": {
   "unit": "game/mainProgram0800",
   "line": "the cursor sits 3 cells right of the view origin"
  },
  "2594": {
   "unit": "game/mainProgram0800",
   "line": "plain add"
  },
  "2595": {
   "unit": "game/mainProgram0800",
   "line": "map column of the left edge of the view"
  },
  "2597": {
   "unit": "game/mainProgram0800",
   "line": "cursor column, also the line stepper start"
  },
  "2599": {
   "unit": "game/mainProgram0800",
   "line": "and 2 cells below the top edge"
  },
  "259B": {
   "unit": "game/mainProgram0800",
   "line": "plain add"
  },
  "259C": {
   "unit": "game/mainProgram0800",
   "line": "map row of the top edge of the view"
  },
  "259E": {
   "unit": "game/mainProgram0800",
   "line": "cursor row"
  },
  "25A0": {
   "unit": "game/mainProgram0800",
   "line": "cursor column"
  },
  "25A2": {
   "unit": "game/mainProgram0800",
   "line": "same column as the unit?"
  },
  "25A5": {
   "unit": "game/mainProgram0800",
   "line": "no -> scroll"
  },
  "25A7": {
   "unit": "game/mainProgram0800",
   "line": "cursor row"
  },
  "25A9": {
   "unit": "game/mainProgram0800",
   "line": "same row as the unit?"
  },
  "25AC": {
   "unit": "game/mainProgram0800",
   "line": "the unit is already under the cursor: nothing to do"
  },
  "25AE": {
   "unit": "game/mainProgram0800",
   "line": "1 frame per scroll step, i.e. animated"
  },
  "25B0": {
   "unit": "game/mainProgram0800",
   "line": "the unit to scroll to"
  },
  "25B3": {
   "unit": "game/mainProgram0800",
   "line": "walk the view over to it cell by cell"
  },
  "25B6": {
   "unit": "game/mainProgram0800",
   "line": "done"
  },
  "25B7": {
   "unit": "game/mainProgram0800",
   "routine": [
    "consumeSpaceKey - Eats a pending SPACE. The IRQ leaves the last printable key in lastTypedKey ($9248) and several loops close on SPACE, so a screen that was itself closed with SPACE clears the key before returning, otherwise the next loop would close immediately as well.",
    "In:  lastTypedKey $9248",
    "Out: lastTypedKey = $FF (no key) when it held $A0 (space)",
    "Called from: $1863 and $2073"
   ],
   "line": "last printable key latched by the IRQ"
  },
  "25BA": {
   "unit": "game/mainProgram0800",
   "line": "$A0 = space (PETSCII with bit 7 set)"
  },
  "25BC": {
   "unit": "game/mainProgram0800",
   "line": "some other key: leave it for the caller"
  },
  "25BE": {
   "unit": "game/mainProgram0800",
   "line": "'no key pending'"
  },
  "25C0": {
   "unit": "game/mainProgram0800",
   "line": "swallow the space so the next loop does not act on it"
  },
  "25C3": {
   "unit": "game/mainProgram0800",
   "line": "done"
  },
  "25C4": {
   "unit": "game/mainProgram0800",
   "routine": [
    "callSetupOverlayHook - Trampoline into overlay variant A at $8658 (handleStarKeyToggle), the per-frame '*' key handler of the non-battle phases: it toggles setupToggleFlag $92F3 bit 7 (DESTROY mode during unit placement, CUSTOM rules on the setup screen) and reports C=1 when it consumed the key. It may only be called while gamePhase zp_B1 is non-zero, because in overlay variant B address $8658 is in the middle of an instruction.",
    "In:  gamePhase zp_B1 must be non-zero (overlay A resident); the hook itself reads chatState $929C and lastTypedKey $9248",
    "Out: C = 1 when '*' was pressed and the toggle changed, C = 0 otherwise",
    "Called from: the map main loop at $2026 and the info-panel loop at $2348"
   ],
   "line": "into overlay A: '*' toggles DESTROY / CUSTOM, C=1 when it acted"
  },
  "25C7": {
   "unit": "game/mainProgram0800",
   "routine": [
    "getViewportOriginPointer - Builds a map pointer for the top-left cell of the 7x5 battlefield view. The view origin may be off the map (it is clamped only for the cursor cell), so a negative row is handled by taking row 0 and stepping the pointer back 40 bytes per row, and a negative column by the sign-extended add in getViewportRowPointer.",
    "In:  viewOriginCol zp_A5, viewOriginRow zp_A6, the map row address tables $BFB0/$BFD8",
    "Out: mapCellPtr zp_48/zp_49 -> the origin cell (may point in front of $F000 when the view hangs off the map), A = that cell's byte only when the origin is really on the map, Y = 0",
    "Called from: drawViewport ($26D1) and the $6F00 overlays ($71D4, $71F9, $740D, $7430, $7BCC)"
   ],
   "line": "map row of the top edge of the view"
  },
  "25C9": {
   "unit": "game/mainProgram0800",
   "line": "row is on the map: ordinary row lookup"
  },
  "25CB": {
   "unit": "game/mainProgram0800",
   "line": "the view hangs off the top of the map: start from row 0"
  },
  "25CD": {
   "unit": "game/mainProgram0800",
   "line": "pointer to (viewOriginCol, row 0)"
  },
  "25D0": {
   "unit": "game/mainProgram0800",
   "line": "the negative row count, e.g. $FE = two rows above the map"
  },
  "25D2": {
   "unit": "game/mainProgram0800",
   "block": [
    "Step the pointer back one map row (40 cells) per row above the map, counting Y up to 0."
   ],
   "line": "pointer low byte"
  },
  "25D4": {
   "unit": "game/mainProgram0800",
   "line": "no borrow"
  },
  "25D5": {
   "unit": "game/mainProgram0800",
   "line": "one map row = 40 cells"
  },
  "25D7": {
   "unit": "game/mainProgram0800",
   "line": "store it back"
  },
  "25D9": {
   "unit": "game/mainProgram0800",
   "line": "pointer high byte"
  },
  "25DB": {
   "unit": "game/mainProgram0800",
   "line": "propagate the borrow"
  },
  "25DD": {
   "unit": "game/mainProgram0800",
   "line": "store it back"
  },
  "25DF": {
   "unit": "game/mainProgram0800",
   "line": "one row less to go"
  },
  "25E0": {
   "unit": "game/mainProgram0800",
   "line": "until the negative row count reaches 0"
  },
  "25E2": {
   "unit": "game/mainProgram0800",
   "line": "done"
  },
  "25E3": {
   "unit": "game/mainProgram0800",
   "routine": [
    "getViewportRowPointer - Map pointer for the cell (viewOriginCol, Y). A negative view column is handled by reading column 0 and then adding the signed offset to the pointer, so the caller gets a pointer that is correct even when the view sticks out over the left edge of the map.",
    "In:  Y = map row 0-39, viewOriginCol zp_A5",
    "Out: mapCellPtr zp_48/zp_49, A = the cell byte when the column is on the map, Y = 0",
    "Called from: getViewportOriginPointer ($25CD and the branch at $25C9)"
   ],
   "line": "map column of the left edge of the view"
  },
  "25E5": {
   "unit": "game/mainProgram0800",
   "line": "column is on the map: plain lookup, and its result is our result"
  },
  "25E7": {
   "unit": "game/mainProgram0800",
   "line": "the view hangs off the left edge: read column 0 instead"
  },
  "25E9": {
   "unit": "game/mainProgram0800",
   "line": "pointer to (column 0, row Y)"
  },
  "25EC": {
   "unit": "game/mainProgram0800",
   "line": "pointer low byte"
  },
  "25EE": {
   "unit": "game/mainProgram0800",
   "line": "plain add"
  },
  "25EF": {
   "unit": "game/mainProgram0800",
   "line": "add the negative column offset..."
  },
  "25F1": {
   "unit": "game/mainProgram0800",
   "line": "store it back"
  },
  "25F3": {
   "unit": "game/mainProgram0800",
   "line": "pointer high byte"
  },
  "25F5": {
   "unit": "game/mainProgram0800",
   "line": "...sign extended: -1 in the high byte"
  },
  "25F7": {
   "unit": "game/mainProgram0800",
   "line": "store it back"
  },
  "25F9": {
   "unit": "game/mainProgram0800",
   "line": "done"
  },
  "25FA": {
   "unit": "game/mainProgram0800",
   "routine": [
    "readMapCell - The map accessor of the whole game: forms mapCellPtr = rowAddress[Y] + X from the 40-entry row address tables at $BFB0/$BFD8 (built by the start-up code at $0339 as $F000 + 40*row) and returns the byte of cell (column X, row Y) of the 40x40 battlefield.",
    "In:  X = map column 0-39, Y = map row 0-39; row address tables $BFB0 (lo) / $BFD8 (hi)",
    "Out: A = the map byte (bit 7 set: bits 0-6 are the index of the unit standing there and the covered terrain is in $FAF0+unit; otherwise a terrain code, $40 = open ground), mapCellPtr zp_48/zp_49 -> that cell, Y = 0",
    "Called from: about 30 places - the main program ($1F16, $1F3F, $25E9, $36FB, $37D7, $3C2C, $3E06, $3E38, $3ECD, $3F3C, $42E7, $4571, $476A, $5400, $2619), both $6F00 overlays and the solo trainer ($E97B, $E9B2)"
   ],
   "line": "map column"
  },
  "25FB": {
   "unit": "game/mainProgram0800",
   "line": "no carry into the add"
  },
  "25FC": {
   "unit": "game/mainProgram0800",
   "line": "+ low byte of the address of map row Y ($F000 + 40*row)"
  },
  "25FF": {
   "unit": "game/mainProgram0800",
   "line": "map pointer low byte"
  },
  "2601": {
   "unit": "game/mainProgram0800",
   "line": "only the carry goes into the high byte"
  },
  "2603": {
   "unit": "game/mainProgram0800",
   "line": "+ high byte of the row address"
  },
  "2606": {
   "unit": "game/mainProgram0800",
   "line": "map pointer high byte"
  },
  "2608": {
   "unit": "game/mainProgram0800",
   "line": "index 0 for the indirect read"
  },
  "260A": {
   "unit": "game/mainProgram0800",
   "line": "the cell byte: bit 7 = a unit stands here, else a terrain code"
  },
  "260C": {
   "unit": "game/mainProgram0800",
   "line": "done"
  },
  "260D": {
   "unit": "game/mainProgram0800",
   "routine": [
    "readCellUnderCursor - Reads the map cell under the battlefield cursor. The cursor never moves: it is always the centre cell of the 7x5 view, i.e. (viewOriginCol+3, viewOriginRow+2); the joystick scrolls the view instead.",
    "In:  viewOriginCol zp_A5, viewOriginRow zp_A6",
    "Out: A = the map byte, mapCellPtr zp_48/zp_49 -> that cell, X = cursor column, Y = 0",
    "Called from: the map main loop ($2049, $207B), the info-panel loop ($2310) and the console at $63D9"
   ],
   "line": "map column of the left edge of the view"
  },
  "260F": {
   "unit": "game/mainProgram0800",
   "line": "plain add"
  },
  "2610": {
   "unit": "game/mainProgram0800",
   "line": "the cursor is the 4th of the 7 visible columns"
  },
  "2612": {
   "unit": "game/mainProgram0800",
   "line": "X = cursor column"
  },
  "2613": {
   "unit": "game/mainProgram0800",
   "line": "map row of the top edge of the view"
  },
  "2615": {
   "unit": "game/mainProgram0800",
   "line": "plain add"
  },
  "2616": {
   "unit": "game/mainProgram0800",
   "line": "the cursor is the 3rd of the 5 visible rows"
  },
  "2618": {
   "unit": "game/mainProgram0800",
   "line": "Y = cursor row"
  },
  "2619": {
   "unit": "game/mainProgram0800",
   "line": "read that cell and return its byte"
  },
  "261C": {
   "unit": "game/mainProgram0800",
   "routine": [
    "clearInfoPanel - Selects the unit info panel as the text window and blanks it with the text engine's window clear, using the current textColour zp_3D and fill glyph zp_3E.",
    "In:  textColour zp_3D, textFillGlyph zp_3E",
    "Out: the text window set to rows 12-20 / columns 25-38 and cleared; text cursor zp_3F/zp_40 homed",
    "Called from: 20 places - the manual check ($09EC/$0A44), the map loop ($2004, $22B3, $2356), redrawGameScreen ($267C), the screen switcher ($2DEE), the unit and console menus ($4BBF, $4C3D, $5E49, $5E9F, $6151, $6201, $6245, $6374, $6441, $64EE, $65FF) and overlay A"
   ],
   "line": "rows 12-20, columns 25-38"
  },
  "261F": {
   "unit": "game/mainProgram0800",
   "line": "fill the window with textFillGlyph and return"
  },
  "2622": {
   "unit": "game/mainProgram0800",
   "routine": [
    "setInfoPanelWindow - Defines the text window that covers the unit info panel: columns 25-38, rows 12 to 20 (row 21 is the exclusive limit). It resets the window to full screen first so that leftovers of a narrower window do not survive. It exits with A = $0C, which printUnitStatusLines relies on to reach row 12.",
    "In:  -",
    "Out: textWindowTop zp_39 = $0C, textWindowBottom zp_3A = $15, textWindowLeft zp_3B = $19, textWindowWidth zp_3C = $0E; A = $0C",
    "Called from: clearInfoPanel ($261C) and printUnitStatusLines ($24C7)"
   ],
   "line": "start from a full-screen window"
  },
  "2625": {
   "unit": "game/mainProgram0800",
   "line": "screen column 25"
  },
  "2627": {
   "unit": "game/mainProgram0800",
   "line": "window left edge"
  },
  "2629": {
   "unit": "game/mainProgram0800",
   "line": "14 columns -> columns 25-38"
  },
  "262B": {
   "unit": "game/mainProgram0800",
   "line": "window width / wrap point"
  },
  "262D": {
   "unit": "game/mainProgram0800",
   "line": "row 21 = the exclusive bottom limit"
  },
  "262F": {
   "unit": "game/mainProgram0800",
   "line": "window bottom"
  },
  "2631": {
   "unit": "game/mainProgram0800",
   "line": "top row 12"
  },
  "2633": {
   "unit": "game/mainProgram0800",
   "line": "window top; A stays $0C for the caller"
  },
  "2635": {
   "unit": "game/mainProgram0800",
   "line": "done"
  },
  "2636": {
   "unit": "game/mainProgram0800",
   "routine": [
    "clearScreenAndRedrawGameScreen - Rebuilds the battlefield display from scratch: sets the VIC up for the multicolour bitmap and wipes it grey, then falls into redrawGameScreen.",
    "In:  -",
    "Out: the whole screen, see initBitmapScreen and redrawGameScreen",
    "Called from: start-up ($0A95) and overlay A ($7D59, $8232, $8559)"
   ],
   "line": "VIC into multicolour bitmap mode, bitmap and colour RAM cleared to grey"
  },
  "2639": {
   "unit": "game/mainProgram0800",
   "routine": [
    "redrawGameScreen - Draws the whole battlefield screen: the three panel frames (mini-map, tactical view, info panel), the bottom status bar, the 40x40 overview map, the 7x5 tactical view and finally the current status message. gamePhase zp_B1 gates the map: bit 7 (menu phases) leaves the whole map area empty, bit 6 (unit setup) draws the overview but not the tactical view.",
    "In:  gamePhase zp_B1, currentScreen $90FB (via drawMainWindowFrame), the map at $F000 and the unit arrays, shownMessageId zp_7B",
    "Out: vicCtrl2Shadow $9001 = $D8 (multicolour), vicBg0Shadow $9004 = 7, cellBackgroundColour $9226 = 0, the glyph plotter parameters $2A29-$2A2C = 0, textColour zp_3D, the whole screen redrawn",
    "Called from: $1FCD (the battlefield screen entry), and by fall-through from clearScreenAndRedrawGameScreen"
   ],
   "line": "re-patch the own/enemy branch in renderMapTile for the current side and replay viewpoint"
  },
  "263C": {
   "unit": "game/mainProgram0800",
   "line": "$D016: multicolour on (bit 4), 40 columns (bit 3), no fine scroll"
  },
  "263E": {
   "unit": "game/mainProgram0800",
   "line": "the raster IRQ copies this shadow to $D016"
  },
  "2641": {
   "unit": "game/mainProgram0800",
   "line": "grey"
  },
  "2643": {
   "unit": "game/mainProgram0800",
   "line": "text colour used for the frames"
  },
  "2645": {
   "unit": "game/mainProgram0800",
   "line": "text window = the whole screen"
  },
  "2648": {
   "unit": "game/mainProgram0800",
   "line": "blank the entire bitmap through the text engine"
  },
  "264B": {
   "unit": "game/mainProgram0800",
   "line": "reset the glyph plotter parameter block..."
  },
  "264D": {
   "unit": "game/mainProgram0800",
   "line": "...no forced colour..."
  },
  "2650": {
   "unit": "game/mainProgram0800",
   "line": "...colour byte 0..."
  },
  "2653": {
   "unit": "game/mainProgram0800",
   "line": "...no block fill..."
  },
  "2656": {
   "unit": "game/mainProgram0800",
   "line": "...and fill character 0"
  },
  "2659": {
   "unit": "game/mainProgram0800",
   "line": "yellow"
  },
  "265B": {
   "unit": "game/mainProgram0800",
   "line": "$D021 background = the colour of the %00 pixels of the map tiles"
  },
  "265E": {
   "unit": "game/mainProgram0800",
   "line": "black"
  },
  "2660": {
   "unit": "game/mainProgram0800",
   "line": "background colour used behind drawn character cells"
  },
  "2663": {
   "unit": "game/mainProgram0800",
   "line": "redundant: the block-fill flag was already cleared at $2653"
  },
  "2665": {
   "unit": "game/mainProgram0800",
   "line": "(leftover store, no effect)"
  },
  "2668": {
   "unit": "game/mainProgram0800",
   "line": "22x22 frame at column 0 around the strategic map"
  },
  "266B": {
   "unit": "game/mainProgram0800",
   "line": "16x12 frame at column 24 around the tactical view; also clears its interior"
  },
  "266E": {
   "unit": "game/mainProgram0800",
   "line": "16x11 frame at column 24 row 11 around the unit info panel"
  },
  "2671": {
   "unit": "game/mainProgram0800",
   "line": "the 38x3 status bar across the bottom"
  },
  "2674": {
   "unit": "game/mainProgram0800",
   "line": "0 during the battle, non-zero during setup and in the menus"
  },
  "2676": {
   "unit": "game/mainProgram0800",
   "line": "bit 7 clear -> the battlefield may be drawn"
  },
  "2678": {
   "unit": "game/mainProgram0800",
   "line": "colour 0"
  },
  "267A": {
   "unit": "game/mainProgram0800",
   "line": "text colour for the clear"
  },
  "267C": {
   "unit": "game/mainProgram0800",
   "line": "menu phases: leave the info panel blank"
  },
  "267F": {
   "unit": "game/mainProgram0800",
   "line": "and skip both map drawings"
  },
  "2682": {
   "unit": "game/mainProgram0800",
   "line": "the whole 40x40 map at 4x4 pixels per cell"
  },
  "2685": {
   "unit": "game/mainProgram0800",
   "line": "test gamePhase again"
  },
  "2687": {
   "unit": "game/mainProgram0800",
   "line": "bit 6 set (unit placement) -> no tactical view"
  },
  "2689": {
   "unit": "game/mainProgram0800",
   "line": "the 7x5 cell tactical view"
  },
  "268C": {
   "unit": "game/mainProgram0800",
   "line": "put the current status message back on the bottom bar"
  },
  "268F": {
   "unit": "game/mainProgram0800",
   "routine": [
    "drawMiniMapFrame - Draws window 0: the 22x22 character frame at column 0, row 0 that surrounds the 20x20 strategic mini-map.",
    "In:  -",
    "Out: textColour zp_3D = $0C, the frame on screen, the window geometry left in $2A2D/$2A2E/$9221/$9222 by drawWindowFrame",
    "Called from: redrawGameScreen ($2668)"
   ],
   "line": "grey"
  },
  "2691": {
   "unit": "game/mainProgram0800",
   "line": "colour of the frame characters"
  },
  "2693": {
   "unit": "game/mainProgram0800",
   "line": "window 0 = column 0, row 0, 22x22"
  },
  "2695": {
   "unit": "game/mainProgram0800",
   "line": "draw it and return"
  },
  "2698": {
   "unit": "game/mainProgram0800",
   "routine": [
    "drawViewportFrame - Draws window 1: the 16x12 character frame at column 24, row 0 around the tactical view, then falls into clearViewportTextArea to blank the interior.",
    "In:  -",
    "Out: textColour zp_3D, the frame drawn and the 14x10 interior cleared",
    "Called from: redrawGameScreen ($266B)"
   ],
   "line": "grey"
  },
  "269A": {
   "unit": "game/mainProgram0800",
   "line": "colour of the frame characters"
  },
  "269C": {
   "unit": "game/mainProgram0800",
   "line": "window 1 = column 24, row 0, 16x12"
  },
  "269E": {
   "unit": "game/mainProgram0800",
   "line": "draw the frame"
  },
  "26A1": {
   "unit": "game/mainProgram0800",
   "routine": [
    "clearViewportTextArea - Declares the interior of the tactical view (rows 1-10, columns 25-38, i.e. the 7x5 map cells at 2x2 characters each) as the text window and clears it in colour 0.",
    "In:  -",
    "Out: textWindowTop zp_39 = 1, textWindowBottom zp_3A = $0B, textWindowLeft zp_3B = $19, textWindowWidth zp_3C = $0E, textColour zp_3D = 0; the area blanked",
    "Called from: the screen-switch clear at $2DF4, overlay A ($8425), and by fall-through from drawViewportFrame"
   ],
   "line": "first character row of the view"
  },
  "26A3": {
   "unit": "game/mainProgram0800",
   "line": "window top"
  },
  "26A5": {
   "unit": "game/mainProgram0800",
   "line": "screen column 25"
  },
  "26A7": {
   "unit": "game/mainProgram0800",
   "line": "window left edge"
  },
  "26A9": {
   "unit": "game/mainProgram0800",
   "line": "14 columns = 7 map cells"
  },
  "26AB": {
   "unit": "game/mainProgram0800",
   "line": "window width"
  },
  "26AD": {
   "unit": "game/mainProgram0800",
   "line": "row 11 = exclusive limit, so rows 1-10 = 5 map rows"
  },
  "26AF": {
   "unit": "game/mainProgram0800",
   "line": "window bottom"
  },
  "26B1": {
   "unit": "game/mainProgram0800",
   "line": "colour 0"
  },
  "26B3": {
   "unit": "game/mainProgram0800",
   "line": "text colour for the clear"
  },
  "26B5": {
   "unit": "game/mainProgram0800",
   "line": "blank the area and return"
  },
  "26B8": {
   "unit": "game/mainProgram0800",
   "routine": [
    "drawInfoPanelFrame - Draws window 2: the 16x11 character frame at column 24, row 11 around the unit info panel.",
    "In:  -",
    "Out: textColour zp_3D = $0C, the frame on screen",
    "Called from: redrawGameScreen ($266E)"
   ],
   "line": "grey"
  },
  "26BA": {
   "unit": "game/mainProgram0800",
   "line": "colour of the frame characters"
  },
  "26BC": {
   "unit": "game/mainProgram0800",
   "line": "window 2 = column 24, row 11, 16x11"
  },
  "26BE": {
   "unit": "game/mainProgram0800",
   "line": "draw it and return"
  },
  "26C1": {
   "unit": "game/mainProgram0800",
   "routine": [
    "drawViewport - Redraws the tactical view: 5 rows of 7 map cells, each cell a 16x16 pixel multicolour tile occupying 2x2 character cells, into character rows 1-10 and columns 25-38. It walks the map from the view origin, has renderMapTile build each tile in the buffer at $04C8-$04E7 and then copies the 32 bytes into the bitmap - 16 bytes to the upper character pair and 16 to the pair one character row (320 bytes) lower, through a store whose address is self-modified. The view origin and the text plotter's scratch bytes are saved and restored, because the loop walks the origin to tell renderMapTile which map cell it is drawing.",
    "In:  viewOriginCol zp_A5 / viewOriginRow zp_A6, the map at $F000 and the unit arrays, gamePhase zp_B1 bit 6 (suppresses units)",
    "Out: bitmap, screen RAM and colour RAM of the 14x10 character area; scanResultCount zp_2F and magnifySrcCol zp_31 used as row/column counters, mapCellPtr zp_48/zp_49, bitmapPtr zp_B4-screenPtrHi zp_B7, $2A2D/$2A2E, drawCellSaveCol/Row $90F6/$90F7; self-modifies the store at tileCopyStore ($2703)",
    "Called from: everywhere the battlefield changes - the map main loop ($1FA2, $200F), redrawGameScreen ($2689), the tick loop ($4023, $413A, $4152), the unit menus ($4BDE, $5E38, $5E7F, $605F, $61F8, $63A4, $636B, $64BB, $64CE), overlay A ($83F5), the screen switcher ($075B) and the text engine ($C797)"
   ],
   "line": "the text plotter's row byte..."
  },
  "26C3": {
   "unit": "game/mainProgram0800",
   "line": "...is borrowed as the character row counter, so save it"
  },
  "26C4": {
   "unit": "game/mainProgram0800",
   "line": "the text plotter's column byte..."
  },
  "26C6": {
   "unit": "game/mainProgram0800",
   "line": "...is borrowed as the character column counter"
  },
  "26C7": {
   "unit": "game/mainProgram0800",
   "line": "map column of the view origin"
  },
  "26C9": {
   "unit": "game/mainProgram0800",
   "line": "saved: the loop walks the origin cell by cell"
  },
  "26CC": {
   "unit": "game/mainProgram0800",
   "line": "map row of the view origin"
  },
  "26CE": {
   "unit": "game/mainProgram0800",
   "line": "saved as well"
  },
  "26D1": {
   "unit": "game/mainProgram0800",
   "line": "mapCellPtr -> the map cell at the top-left corner of the view"
  },
  "26D4": {
   "unit": "game/mainProgram0800",
   "line": "character row 1 = the first row inside the frame"
  },
  "26D6": {
   "unit": "game/mainProgram0800",
   "line": "window origin row used by the colour writes"
  },
  "26D9": {
   "unit": "game/mainProgram0800",
   "line": "row for calcCellPointers"
  },
  "26DB": {
   "unit": "game/mainProgram0800",
   "line": "start at the top"
  },
  "26DD": {
   "unit": "game/mainProgram0800",
   "line": "viewport row counter 0-4"
  },
  "26DF": {
   "unit": "game/mainProgram0800",
   "block": [
    "Outer loop: one row of 7 map cells = two character rows."
   ],
   "line": "character column 25 = the first column inside the frame"
  },
  "26E1": {
   "unit": "game/mainProgram0800",
   "line": "window origin column"
  },
  "26E4": {
   "unit": "game/mainProgram0800",
   "line": "column for calcCellPointers"
  },
  "26E6": {
   "unit": "game/mainProgram0800",
   "line": "start at the left"
  },
  "26E8": {
   "unit": "game/mainProgram0800",
   "line": "viewport column counter 0-6"
  },
  "26EA": {
   "unit": "game/mainProgram0800",
   "block": [
    "Inner loop: one map cell = one 16x16 tile = 2x2 characters."
   ],
   "line": "bitmapPtr zp_B4/B5 and screenPtr zp_B6/B7 for character (row,column)"
  },
  "26ED": {
   "unit": "game/mainProgram0800",
   "line": "offset of this cell inside the map row"
  },
  "26EF": {
   "unit": "game/mainProgram0800",
   "line": "the map byte of the cell"
  },
  "26F1": {
   "unit": "game/mainProgram0800",
   "line": "build its 32-byte tile in $04C8 and write the cell colours"
  },
  "26F4": {
   "unit": "game/mainProgram0800",
   "line": "bitmap address of the upper-left character"
  },
  "26F6": {
   "unit": "game/mainProgram0800",
   "line": "patch the destination of the copy store..."
  },
  "26F9": {
   "unit": "game/mainProgram0800",
   "line": "..."
  },
  "26FB": {
   "unit": "game/mainProgram0800",
   "line": "...so it writes into this cell"
  },
  "26FE": {
   "unit": "game/mainProgram0800",
   "line": "start at the first tile byte"
  },
  "2700": {
   "unit": "game/mainProgram0800",
   "block": [
    "Copy the tile buffer into the bitmap: bytes $00-$0F are the two upper characters (which are",
    "adjacent in a C64 bitmap), bytes $10-$1F the two characters one character row lower."
   ],
   "line": "byte of the tile buffer"
  },
  "2703": {
   "unit": "game/mainProgram0800",
   "line": "into the bitmap; the absolute address is patched at $26F6/$26FB and $2714/$271B (the unpatched operand in the file happens to read $FFFF)"
  },
  "2706": {
   "unit": "game/mainProgram0800",
   "line": "next byte"
  },
  "2707": {
   "unit": "game/mainProgram0800",
   "line": "32 bytes = four characters"
  },
  "2709": {
   "unit": "game/mainProgram0800",
   "line": "tile finished"
  },
  "270B": {
   "unit": "game/mainProgram0800",
   "line": "the first 16 bytes are the upper character pair"
  },
  "270D": {
   "unit": "game/mainProgram0800",
   "line": "not there yet"
  },
  "270F": {
   "unit": "game/mainProgram0800",
   "line": "no carry into the add"
  },
  "2710": {
   "unit": "game/mainProgram0800",
   "line": "bitmap address of the cell again"
  },
  "2712": {
   "unit": "game/mainProgram0800",
   "line": "+$130, and Y has already reached $10, so the store lands at +$140 = 320 = one character row down"
  },
  "2714": {
   "unit": "game/mainProgram0800",
   "line": "re-patch the store..."
  },
  "2717": {
   "unit": "game/mainProgram0800",
   "line": "..."
  },
  "2719": {
   "unit": "game/mainProgram0800",
   "line": "..."
  },
  "271B": {
   "unit": "game/mainProgram0800",
   "line": "...to the lower character pair"
  },
  "271E": {
   "unit": "game/mainProgram0800",
   "line": "always taken (the bitmap lives at $A000)"
  },
  "2720": {
   "unit": "game/mainProgram0800",
   "block": [
    "Cell done: step one map cell to the right = two character columns."
   ],
   "line": "one map cell is two characters wide..."
  },
  "2722": {
   "unit": "game/mainProgram0800",
   "line": "...so advance the character column by 2"
  },
  "2724": {
   "unit": "game/mainProgram0800",
   "line": "renderMapTile reads the view origin as the coordinates of the cell it draws"
  },
  "2726": {
   "unit": "game/mainProgram0800",
   "line": "next cell of this row"
  },
  "2728": {
   "unit": "game/mainProgram0800",
   "line": "the column counter"
  },
  "272A": {
   "unit": "game/mainProgram0800",
   "line": "7 cells across the view"
  },
  "272C": {
   "unit": "game/mainProgram0800",
   "line": "keep going along the row"
  },
  "272E": {
   "unit": "game/mainProgram0800",
   "line": "row done: one map cell is two characters high..."
  },
  "2730": {
   "unit": "game/mainProgram0800",
   "line": "...so advance the character row by 2"
  },
  "2732": {
   "unit": "game/mainProgram0800",
   "line": "and the map row the next line draws"
  },
  "2734": {
   "unit": "game/mainProgram0800",
   "line": "the saved view origin column"
  },
  "2737": {
   "unit": "game/mainProgram0800",
   "line": "back to the left edge of the view"
  },
  "2739": {
   "unit": "game/mainProgram0800",
   "line": "one more viewport row done"
  },
  "273B": {
   "unit": "game/mainProgram0800",
   "line": "the row counter"
  },
  "273D": {
   "unit": "game/mainProgram0800",
   "line": "5 cell rows in the view"
  },
  "273F": {
   "unit": "game/mainProgram0800",
   "line": "all rows drawn"
  },
  "2741": {
   "unit": "game/mainProgram0800",
   "line": "map pointer low byte"
  },
  "2743": {
   "unit": "game/mainProgram0800",
   "line": "plain add"
  },
  "2744": {
   "unit": "game/mainProgram0800",
   "line": "one map row = 40 cells"
  },
  "2746": {
   "unit": "game/mainProgram0800",
   "line": "store it back"
  },
  "2748": {
   "unit": "game/mainProgram0800",
   "line": "map pointer high byte"
  },
  "274A": {
   "unit": "game/mainProgram0800",
   "line": "propagate the carry"
  },
  "274C": {
   "unit": "game/mainProgram0800",
   "line": "store it back"
  },
  "274E": {
   "unit": "game/mainProgram0800",
   "line": "draw the next row of cells"
  },
  "2751": {
   "unit": "game/mainProgram0800",
   "block": [
    "Restore everything the loop borrowed: the view origin and the text plotter's scratch bytes."
   ],
   "line": "saved origin column"
  },
  "2754": {
   "unit": "game/mainProgram0800",
   "line": "restore it (the loop walked it across the view)"
  },
  "2756": {
   "unit": "game/mainProgram0800",
   "line": "saved origin row"
  },
  "2759": {
   "unit": "game/mainProgram0800",
   "line": "restore it"
  },
  "275B": {
   "unit": "game/mainProgram0800",
   "line": "saved text column"
  },
  "275C": {
   "unit": "game/mainProgram0800",
   "line": "give the text plotter its scratch byte back"
  },
  "275E": {
   "unit": "game/mainProgram0800",
   "line": "saved text row"
  },
  "275F": {
   "unit": "game/mainProgram0800",
   "line": "and the other one"
  },
  "2761": {
   "unit": "game/mainProgram0800",
   "line": "done"
  },
  "2762": {
   "unit": "game/mainProgram0800",
   "block": [
    "nybbleToMulticolourTable - expands the four pixels of one nybble of a terrain shape into one",
    "multicolour bitmap byte: every set bit becomes the pixel pair %10, i.e. bit 0 -> $02, bit 1 ->",
    "$08, bit 2 -> $20, bit 3 -> $80. %10 pixels take their colour from the low nybble of the",
    "screen RAM byte, which renderMapTile fills with the terrain colour; %00 pixels show the",
    "$D021 background (yellow) and the %11 pixels of the unit glyphs come from colour RAM."
   ],
   "line": "nybbles $0-$7: bit 0 -> $02, bit 1 -> $08, bit 2 -> $20"
  },
  "276A": {
   "unit": "game/mainProgram0800",
   "line": "nybbles $8-$F: bit 3 -> $80; $AA = all four pixels set"
  },
  "2772": {
   "unit": "game/mainProgram0800",
   "routine": [
    "renderMapTile - Builds the 16x16 pixel multicolour tile of one map cell in the 32-byte buffer at $04C8-$04E7 and writes the cell's four screen-RAM colour bytes. The cell byte in A is either a terrain code (the 8x8 one-bit shape at $9C6E + (code-$40)*8, doubled in both directions through nybbleToMulticolourTable) or, with bit 7 set, a unit index, in which case the terrain hidden underneath is drawn first and the 32-byte unit glyph is ORed on top and the unit colour written to colour RAM. Cells outside the 40x40 map use the four border shapes $68-$6B. The caller copies the buffer into the bitmap.",
    "In:  A = the map cell byte; viewOriginCol zp_A5 / viewOriginRow zp_A6 = that cell's map coordinates; screenPtr zp_B6/zp_B7 -> its screen RAM cell; gamePhase zp_B1 bit 6 suppresses units; ownUnitColour zp_77 / enemyUnitColour zp_78; filmPlaybackMode $0B9B, revealAllUnits $92FC, highlightedUnit $9204, highlightedGroupKey $9205, uiLevel $9209; the branch opcodes at ownSideBranchOpcode ($2840) and unitVisibleBranchOpcode ($2846) decide which half of the unit array is 'ours'",
    "Out: the 32-byte tile buffer $04C8-$04E7; the terrain colour in the four screen RAM bytes at (zp_B6)+0/+1/+40/+41 and, when a unit is drawn, the unit colour in the matching colour RAM bytes (zp_B7 is advanced by $4C, so screenPtr points into $D800 on exit); tileColour zp_32, glyphPtr zp_B2/B3; self-modifies mapCellByteImm+1 ($2808), terrainShapeRowImm+1 ($27D4), terrainShapeRowCounter+1 ($27F2), unitGlyphEorMask+1 ($28B8) and cloakSkipFlagImm+1 ($28C0)",
    "Called from: drawViewport ($26F1) and the single-cell redraw at $5D45"
   ],
   "block": [
    "Off-map check: the view may hang over the edge of the battlefield. Both coordinates are",
    "compared unsigned against 40, which also catches the negative (>= $80) values."
   ],
   "line": "keep the cell byte in the LDA # at mapCellByteImm for the unit pass below"
  },
  "2775": {
   "unit": "game/mainProgram0800",
   "line": "Y = cell byte"
  },
  "2776": {
   "unit": "game/mainProgram0800",
   "line": "map column of this cell"
  },
  "2778": {
   "unit": "game/mainProgram0800",
   "line": "40 = past the right edge (negative columns are >= 40 unsigned too)"
  },
  "277A": {
   "unit": "game/mainProgram0800",
   "line": "off the map"
  },
  "277C": {
   "unit": "game/mainProgram0800",
   "line": "map row of this cell"
  },
  "277E": {
   "unit": "game/mainProgram0800",
   "line": "40 = past the bottom edge"
  },
  "2780": {
   "unit": "game/mainProgram0800",
   "line": "the cell is on the battlefield"
  },
  "2782": {
   "unit": "game/mainProgram0800",
   "line": "off-map: pick one of four border patterns from the coordinates..."
  },
  "2784": {
   "unit": "game/mainProgram0800",
   "line": "plain add"
  },
  "2785": {
   "unit": "game/mainProgram0800",
   "line": "...so the border is dithered rather than uniform"
  },
  "2787": {
   "unit": "game/mainProgram0800",
   "line": "four patterns"
  },
  "2789": {
   "unit": "game/mainProgram0800",
   "line": "no carry"
  },
  "278A": {
   "unit": "game/mainProgram0800",
   "line": "shapes $68-$6B tile everything outside the battlefield"
  },
  "278C": {
   "unit": "game/mainProgram0800",
   "line": "Y = border shape code"
  },
  "278D": {
   "unit": "game/mainProgram0800",
   "line": "and make the unit pass see plain terrain, not a unit index"
  },
  "2790": {
   "unit": "game/mainProgram0800",
   "block": [
    "Terrain selection: when a unit stands on the cell the map byte holds its index, and the",
    "terrain it covers is kept in unitTerrainUnder ($FAF0 + unit). unitTerrainUnderAlias is that",
    "table biased by -$80 so it can be indexed with the raw $80|unit byte."
   ],
   "line": "the cell byte"
  },
  "2791": {
   "unit": "game/mainProgram0800",
   "line": "bit 7 clear = plain terrain, use it directly"
  },
  "2793": {
   "unit": "game/mainProgram0800",
   "line": "a unit stands here: fetch the terrain it is hiding"
  },
  "2796": {
   "unit": "game/mainProgram0800",
   "line": "Y = terrain code"
  },
  "2797": {
   "unit": "game/mainProgram0800",
   "line": "$40 = open ground, which has no shape at all"
  },
  "2799": {
   "unit": "game/mainProgram0800",
   "line": "real terrain: draw its shape"
  },
  "279B": {
   "unit": "game/mainProgram0800",
   "line": "blank tile"
  },
  "279D": {
   "unit": "game/mainProgram0800",
   "line": "32 buffer bytes"
  },
  "279F": {
   "unit": "game/mainProgram0800",
   "line": "clear one"
  },
  "27A2": {
   "unit": "game/mainProgram0800",
   "line": "next"
  },
  "27A3": {
   "unit": "game/mainProgram0800",
   "line": "until the buffer is empty"
  },
  "27A5": {
   "unit": "game/mainProgram0800",
   "line": "straight on to the unit pass; open ground keeps the colours already on screen"
  },
  "27A8": {
   "unit": "game/mainProgram0800",
   "block": [
    "Terrain colour and shape: terrainClassTable ($077E indexed by the raw cell value) maps the",
    "cell code to a terrain class 0-8, terrainColourTable gives the colour of that class",
    "(7 plain, 3 river, 5 forest, 8 contours/hills/summit, 0 for the off-map classes)."
   ],
   "line": "terrain class 0-8 of this cell code"
  },
  "27AB": {
   "unit": "game/mainProgram0800",
   "line": "colour nibble of that class"
  },
  "27AE": {
   "unit": "game/mainProgram0800",
   "line": "remember it for the screen RAM writes"
  },
  "27B0": {
   "unit": "game/mainProgram0800",
   "line": "the cell code"
  },
  "27B1": {
   "unit": "game/mainProgram0800",
   "line": "no borrow"
  },
  "27B2": {
   "unit": "game/mainProgram0800",
   "line": "shape index = code - $40 for the ordinary terrain codes..."
  },
  "27B4": {
   "unit": "game/mainProgram0800",
   "line": "..."
  },
  "27B6": {
   "unit": "game/mainProgram0800",
   "line": "...and code - $20 for the contour codes below $40"
  },
  "27B8": {
   "unit": "game/mainProgram0800",
   "line": "glyphPtr zp_B2/B3 -> the 8-byte shape at $9C6E + index*8"
  },
  "27BB": {
   "unit": "game/mainProgram0800",
   "block": [
    "Expansion loop: eight shape rows, each one byte of 8 one-bit pixels. The low nybble (right",
    "half) becomes the right-hand character, the high nybble the left-hand one, and every row is",
    "written twice so the shape is doubled vertically as well as horizontally. Buffer layout:",
    "$04C8 upper-left character, $04D0 upper-right, $04D8 lower-left, $04E0 lower-right."
   ],
   "line": "buffer offset, two bytes per shape row"
  },
  "27BD": {
   "unit": "game/mainProgram0800",
   "line": "shape row 0"
  },
  "27BF": {
   "unit": "game/mainProgram0800",
   "line": "the loop counter lives in the LDY # at terrainShapeRowCounter"
  },
  "27C2": {
   "unit": "game/mainProgram0800",
   "line": "one row of the 8x8 terrain shape"
  },
  "27C4": {
   "unit": "game/mainProgram0800",
   "line": "keep it in the LDA # at terrainShapeRowImm for the high nybble"
  },
  "27C7": {
   "unit": "game/mainProgram0800",
   "line": "the right four pixels"
  },
  "27C9": {
   "unit": "game/mainProgram0800",
   "line": "index into the expansion table"
  },
  "27CA": {
   "unit": "game/mainProgram0800",
   "line": "each set bit becomes a %10 pixel pair"
  },
  "27CD": {
   "unit": "game/mainProgram0800",
   "line": "right-hand character, first scanline"
  },
  "27D0": {
   "unit": "game/mainProgram0800",
   "line": "and again: the shape is doubled vertically"
  },
  "27D3": {
   "unit": "game/mainProgram0800",
   "line": "the shape row saved at $27C4"
  },
  "27D5": {
   "unit": "game/mainProgram0800",
   "line": "shift the left four pixels..."
  },
  "27D6": {
   "unit": "game/mainProgram0800",
   "line": "...down..."
  },
  "27D7": {
   "unit": "game/mainProgram0800",
   "line": "...into..."
  },
  "27D8": {
   "unit": "game/mainProgram0800",
   "line": "...bits 0-3"
  },
  "27D9": {
   "unit": "game/mainProgram0800",
   "line": "index into the expansion table"
  },
  "27DA": {
   "unit": "game/mainProgram0800",
   "line": "expand them the same way"
  },
  "27DD": {
   "unit": "game/mainProgram0800",
   "line": "left-hand character, first scanline"
  },
  "27E0": {
   "unit": "game/mainProgram0800",
   "line": "doubled"
  },
  "27E3": {
   "unit": "game/mainProgram0800",
   "line": "two scanlines..."
  },
  "27E4": {
   "unit": "game/mainProgram0800",
   "line": "...per shape row"
  },
  "27E5": {
   "unit": "game/mainProgram0800",
   "line": "after four rows the upper character pair is full"
  },
  "27E7": {
   "unit": "game/mainProgram0800",
   "line": "still in the upper half"
  },
  "27E9": {
   "unit": "game/mainProgram0800",
   "line": "buffer offset"
  },
  "27EA": {
   "unit": "game/mainProgram0800",
   "line": "no carry"
  },
  "27EB": {
   "unit": "game/mainProgram0800",
   "line": "skip the 8 bytes of the upper-right character to reach $04D8"
  },
  "27ED": {
   "unit": "game/mainProgram0800",
   "line": "continue in the lower character pair"
  },
  "27EE": {
   "unit": "game/mainProgram0800",
   "line": "next shape row"
  },
  "27F1": {
   "unit": "game/mainProgram0800",
   "line": "the row counter (operand patched at $27BF/$27EE)"
  },
  "27F3": {
   "unit": "game/mainProgram0800",
   "line": "eight rows in a shape"
  },
  "27F5": {
   "unit": "game/mainProgram0800",
   "line": "keep expanding"
  },
  "27F7": {
   "unit": "game/mainProgram0800",
   "block": [
    "Terrain colour into screen RAM: in multicolour mode the low nybble of the screen RAM byte is",
    "the colour of the %10 pixels the terrain shape is made of, the high nybble that of the %01",
    "pixels (0 = black here). All four characters of the tile get the same byte."
   ],
   "line": "the terrain colour"
  },
  "27F9": {
   "unit": "game/mainProgram0800",
   "line": "first character of the tile"
  },
  "27FB": {
   "unit": "game/mainProgram0800",
   "line": "upper-left"
  },
  "27FD": {
   "unit": "game/mainProgram0800",
   "line": "next column"
  },
  "27FE": {
   "unit": "game/mainProgram0800",
   "line": "upper-right"
  },
  "2800": {
   "unit": "game/mainProgram0800",
   "line": "one screen row = 40 bytes"
  },
  "2802": {
   "unit": "game/mainProgram0800",
   "line": "lower-left"
  },
  "2804": {
   "unit": "game/mainProgram0800",
   "line": "next column"
  },
  "2805": {
   "unit": "game/mainProgram0800",
   "line": "lower-right"
  },
  "2807": {
   "unit": "game/mainProgram0800",
   "block": [
    "Unit pass. mapCellByteImm+1 holds the map cell byte stored at $2772 (or the border code at",
    "$278D); bit 7 set means the low 7 bits are the index of the unit standing here."
   ],
   "line": "the map cell byte patched in at $2772/$278D"
  },
  "2809": {
   "unit": "game/mainProgram0800",
   "line": "no unit on this cell: the tile is finished"
  },
  "280B": {
   "unit": "game/mainProgram0800",
   "line": "gamePhase bit 6 = draw terrain only (unit placement phase)"
  },
  "280D": {
   "unit": "game/mainProgram0800",
   "line": "skip the unit"
  },
  "280F": {
   "unit": "game/mainProgram0800",
   "line": "unit index 0-99"
  },
  "2811": {
   "unit": "game/mainProgram0800",
   "line": "default values..."
  },
  "2813": {
   "unit": "game/mainProgram0800",
   "line": "...glyph EOR mask 0 = draw the glyph as it is..."
  },
  "2816": {
   "unit": "game/mainProgram0800",
   "line": "...and scanline-skip flag 0 = draw every line"
  },
  "2819": {
   "unit": "game/mainProgram0800",
   "line": "6 = blue, the local player's colour"
  },
  "281B": {
   "unit": "game/mainProgram0800",
   "line": "assume the unit is ours"
  },
  "281D": {
   "unit": "game/mainProgram0800",
   "line": "Y = unit index"
  },
  "281E": {
   "unit": "game/mainProgram0800",
   "line": "type byte of the unit"
  },
  "2821": {
   "unit": "game/mainProgram0800",
   "line": "bit 7 clear = the unit is alive"
  },
  "2823": {
   "unit": "game/mainProgram0800",
   "line": "dead: bits 0-2 still give the type for the glyph"
  },
  "2825": {
   "unit": "game/mainProgram0800",
   "line": "glyph selector"
  },
  "2826": {
   "unit": "game/mainProgram0800",
   "line": "white"
  },
  "2828": {
   "unit": "game/mainProgram0800",
   "line": "wrecks are drawn white..."
  },
  "282A": {
   "unit": "game/mainProgram0800",
   "line": "..."
  },
  "282C": {
   "unit": "game/mainProgram0800",
   "line": "...and inverted"
  },
  "282F": {
   "unit": "game/mainProgram0800",
   "line": "always taken: a destroyed unit is visible to both players"
  },
  "2831": {
   "unit": "game/mainProgram0800",
   "line": "alive: type 0-4"
  },
  "2833": {
   "unit": "game/mainProgram0800",
   "line": "glyph selector"
  },
  "2834": {
   "unit": "game/mainProgram0800",
   "line": "waypoint column + cloak bits ($F898)"
  },
  "2837": {
   "unit": "game/mainProgram0800",
   "line": "bit 7 clear = not cloaked"
  },
  "2839": {
   "unit": "game/mainProgram0800",
   "line": "cloaked..."
  },
  "283B": {
   "unit": "game/mainProgram0800",
   "line": "...so only every other scanline of the glyph is drawn (a see-through ghost)"
  },
  "283E": {
   "unit": "game/mainProgram0800",
   "line": "50 = the first unit of side 1; units 0-49 are side 0"
  },
  "2840": {
   "unit": "game/mainProgram0800",
   "line": "opcode patched from the local side by $7BD2 in the overlay ($90 BCC when we are side 0, $B0 BCS when side 1): branch when this is one of our units"
  },
  "2842": {
   "unit": "game/mainProgram0800",
   "line": "2 = red"
  },
  "2844": {
   "unit": "game/mainProgram0800",
   "line": "enemy units are drawn red"
  },
  "2846": {
   "unit": "game/mainProgram0800",
   "line": "same test, opcode patched by patchUnitSideBranches (inverted for the THEM replay viewpoint): our own units are always visible"
  },
  "2848": {
   "unit": "game/mainProgram0800",
   "line": "film playback state"
  },
  "284B": {
   "unit": "game/mainProgram0800",
   "line": "$82 = playing back with viewpoint 2 = BOTH, which sees everything"
  },
  "284D": {
   "unit": "game/mainProgram0800",
   "line": "omniscient: draw the enemy unit"
  },
  "284F": {
   "unit": "game/mainProgram0800",
   "line": "$92FC bit 7 = reveal every unit (set when the game is over)"
  },
  "2852": {
   "unit": "game/mainProgram0800",
   "line": "draw it"
  },
  "2854": {
   "unit": "game/mainProgram0800",
   "line": "energy byte of the enemy unit"
  },
  "2857": {
   "unit": "game/mainProgram0800",
   "line": "bit 7 = this unit has been spotted by us"
  },
  "2859": {
   "unit": "game/mainProgram0800",
   "line": "unspotted enemy: leave the terrain tile as it is"
  },
  "285C": {
   "unit": "game/mainProgram0800",
   "block": [
    "The unit is going to be drawn. First the two overlays that change the tile rather than the",
    "glyph: the dug-in bars and the white hit flash."
   ],
   "line": "stun / dig-in byte"
  },
  "285F": {
   "unit": "game/mainProgram0800",
   "line": "bit 7 clear = not dug in"
  },
  "2861": {
   "unit": "game/mainProgram0800",
   "line": "a solid row of %11 pixels"
  },
  "2863": {
   "unit": "game/mainProgram0800",
   "line": "top scanline of the upper-left character..."
  },
  "2866": {
   "unit": "game/mainProgram0800",
   "line": "...bottom scanline of the lower-left one..."
  },
  "2869": {
   "unit": "game/mainProgram0800",
   "line": "...top scanline of the upper-right..."
  },
  "286C": {
   "unit": "game/mainProgram0800",
   "line": "...and bottom of the lower-right: a bar above and below the dug-in unit"
  },
  "286F": {
   "unit": "game/mainProgram0800",
   "line": "$FA8C bit 7 = the unit has just been hit"
  },
  "2872": {
   "unit": "game/mainProgram0800",
   "line": "no flash"
  },
  "2874": {
   "unit": "game/mainProgram0800",
   "line": "white"
  },
  "2876": {
   "unit": "game/mainProgram0800",
   "line": "flash the unit white this frame"
  },
  "2878": {
   "unit": "game/mainProgram0800",
   "block": [
    "Highlight: the unit the player has selected, or any member of the selected group while the",
    "console is at group level (uiLevel 2), is drawn in reverse video."
   ],
   "line": "the unit the player currently has selected"
  },
  "287B": {
   "unit": "game/mainProgram0800",
   "line": "this is it -> invert it"
  },
  "287D": {
   "unit": "game/mainProgram0800",
   "line": "which selection level the console is in"
  },
  "2880": {
   "unit": "game/mainProgram0800",
   "line": "2 = a whole group is selected"
  },
  "2882": {
   "unit": "game/mainProgram0800",
   "line": "single-unit level: no group highlight"
  },
  "2884": {
   "unit": "game/mainProgram0800",
   "line": "group/status flags"
  },
  "2887": {
   "unit": "game/mainProgram0800",
   "line": "bit 6 = the unit is in a group"
  },
  "2889": {
   "unit": "game/mainProgram0800",
   "line": "not grouped"
  },
  "288B": {
   "unit": "game/mainProgram0800",
   "line": "flags again"
  },
  "288E": {
   "unit": "game/mainProgram0800",
   "line": "bits 2-5 = group id plus the side bit"
  },
  "2890": {
   "unit": "game/mainProgram0800",
   "line": "the selected group's key"
  },
  "2893": {
   "unit": "game/mainProgram0800",
   "line": "different group: no highlight"
  },
  "2895": {
   "unit": "game/mainProgram0800",
   "line": "invert every glyph byte..."
  },
  "2897": {
   "unit": "game/mainProgram0800",
   "line": "...by patching the EOR mask at unitGlyphEorMask"
  },
  "289A": {
   "unit": "game/mainProgram0800",
   "block": [
    "Fetch the unit glyph: 20 shapes of 32 bytes, indexed by unit type * 4 + facing through the",
    "word table unitGlyphPointerTable ($6C35). unitGlyphBaseTable gives $2C, $30, $34, $38, $3C",
    "for GRUNT, RIDER, BOOMER, SPY, COMCEN; the same numbers are the overview-map symbols, so",
    "$2C is subtracted again here."
   ],
   "line": "flags byte of the unit"
  },
  "289D": {
   "unit": "game/mainProgram0800",
   "line": "bits 0-1 = facing 0-3"
  },
  "289F": {
   "unit": "game/mainProgram0800",
   "line": "no carry"
  },
  "28A0": {
   "unit": "game/mainProgram0800",
   "line": "+ the base glyph number of the unit type"
  },
  "28A3": {
   "unit": "game/mainProgram0800",
   "line": "no borrow"
  },
  "28A4": {
   "unit": "game/mainProgram0800",
   "line": "back to 0-19 = type*4 + facing"
  },
  "28A6": {
   "unit": "game/mainProgram0800",
   "line": "two bytes per pointer"
  },
  "28A7": {
   "unit": "game/mainProgram0800",
   "line": "table index"
  },
  "28A8": {
   "unit": "game/mainProgram0800",
   "line": "low byte of the glyph address"
  },
  "28AB": {
   "unit": "game/mainProgram0800",
   "line": "glyph pointer"
  },
  "28AD": {
   "unit": "game/mainProgram0800",
   "line": "next table byte"
  },
  "28AE": {
   "unit": "game/mainProgram0800",
   "line": "high byte of the glyph address"
  },
  "28B1": {
   "unit": "game/mainProgram0800",
   "line": "glyph pointer high"
  },
  "28B3": {
   "unit": "game/mainProgram0800",
   "line": "32 glyph bytes, last first"
  },
  "28B5": {
   "unit": "game/mainProgram0800",
   "line": "one byte of the unit glyph"
  },
  "28B7": {
   "unit": "game/mainProgram0800",
   "line": "invert mask: $00 (as-is) or $FF (reverse video), patched at $2813/$282C/$2897"
  },
  "28B9": {
   "unit": "game/mainProgram0800",
   "line": "OR the unit on top of the terrain tile..."
  },
  "28BC": {
   "unit": "game/mainProgram0800",
   "line": "...in the tile buffer"
  },
  "28BF": {
   "unit": "game/mainProgram0800",
   "line": "cloak flag: $00 or $FF, patched at $2816/$283B"
  },
  "28C1": {
   "unit": "game/mainProgram0800",
   "line": "not cloaked: one step per byte"
  },
  "28C3": {
   "unit": "game/mainProgram0800",
   "line": "cloaked: skip a scanline so the unit shows as a half-drawn ghost"
  },
  "28C4": {
   "unit": "game/mainProgram0800",
   "line": "next glyph byte"
  },
  "28C5": {
   "unit": "game/mainProgram0800",
   "line": "until all 32 are merged"
  },
  "28C7": {
   "unit": "game/mainProgram0800",
   "block": [
    "Unit colour into colour RAM: the %11 pixels of a unit glyph take their colour from $D800,",
    "which is $4C00 above the screen RAM at $8C00."
   ],
   "line": "screen RAM pointer high byte ($8Cxx)"
  },
  "28C9": {
   "unit": "game/mainProgram0800",
   "line": "no carry"
  },
  "28CA": {
   "unit": "game/mainProgram0800",
   "line": "+$4C00 turns it into a colour RAM pointer ($D8xx)"
  },
  "28CC": {
   "unit": "game/mainProgram0800",
   "line": "keep the colour RAM pointer"
  },
  "28CE": {
   "unit": "game/mainProgram0800",
   "line": "redundant: Y is loaded again at $28D2"
  },
  "28D0": {
   "unit": "game/mainProgram0800",
   "line": "blue for our units, red for the enemy, white when hit or destroyed"
  },
  "28D2": {
   "unit": "game/mainProgram0800",
   "line": "first character of the tile"
  },
  "28D4": {
   "unit": "game/mainProgram0800",
   "line": "upper-left"
  },
  "28D6": {
   "unit": "game/mainProgram0800",
   "line": "next column"
  },
  "28D7": {
   "unit": "game/mainProgram0800",
   "line": "upper-right"
  },
  "28D9": {
   "unit": "game/mainProgram0800",
   "line": "one screen row = 40 bytes"
  },
  "28DB": {
   "unit": "game/mainProgram0800",
   "line": "lower-left"
  },
  "28DD": {
   "unit": "game/mainProgram0800",
   "line": "next column"
  },
  "28DE": {
   "unit": "game/mainProgram0800",
   "line": "lower-right"
  },
  "28E0": {
   "unit": "game/mainProgram0800",
   "line": "tile finished"
  },
  "28E1": {
   "unit": "game/mainProgram0800",
   "routine": [
    "drawMainWindowFrame - Draws the frame of the bottom bar: window 3 on the battlefield screen, window 4 on every other screen. Both are the same 38x3 box at column 1, row 22; they differ only in the 9-tile frame set used, so the status bar looks different in the console and menu screens.",
    "In:  currentScreen $90FB (0 = battlefield)",
    "Out: textColour zp_3D = $0C, the frame drawn, window geometry left in $2A2D/$2A2E/$9221/$9222",
    "Called from: redrawGameScreen ($2671) and both $6F00 overlays ($712E, $7A65)"
   ],
   "line": "grey"
  },
  "28E3": {
   "unit": "game/mainProgram0800",
   "line": "colour of the frame characters"
  },
  "28E5": {
   "unit": "game/mainProgram0800",
   "line": "window 3 = the battlefield status bar (column 1, row 22, 38x3)"
  },
  "28E7": {
   "unit": "game/mainProgram0800",
   "line": "which screen is up"
  },
  "28EA": {
   "unit": "game/mainProgram0800",
   "line": "0 = battlefield: use window 3"
  },
  "28EC": {
   "unit": "game/mainProgram0800",
   "line": "window 4 = the same box with the other frame style"
  },
  "28EE": {
   "unit": "game/mainProgram0800",
   "line": "draw it and return"
  },
  "28F1": {
   "unit": "game/mainProgram0800",
   "routine": [
    "computeGameResult - Turns the state at the end of a game into the two final point totals and the result message. The score pair $92A2/$92A4 and $92A3/$92A5 is indexed by the side that was scored AGAINST, so the larger entry belongs to the losing side. On points: equal totals give 250 each and message $13 'IT WAS A DRAW.', otherwise the winner gets 300 + min(difference/2, 150) and the loser 200, with message $11 'WE LOST ON POINTS!' or $12 'WE WON ON POINTS!'. On a knockout (gameEndReason bit 7): both comcens down is again a draw, otherwise the winner gets 500, the loser 150 and message $14 'WE WERE KNOCKED OUT!' or $15 'WE WON BY A KNOCKOUT!'. The chosen message is queued at the end and the status line is released. Y is preserved through a self-modified LDY # at $29B0.",
    "In:  Y (returned unchanged), gameEndReason $0BA8 ($08 = clock ran out, $A0/$C0 = the comcen of side 0/1 was knocked out), playerSide $0B9F, the running scores sideScoreLo/side1ScoreLo/sideScoreHi/side1ScoreHi $92A2-$92A5",
    "Out: $92A2-$92A5 rewritten with the final points, gameOverFlags $923F = $C0, persistentMessageId $92A1 = $9E and chatMessageLine $0548 = $9E (status line free), chatState $929C = 0, messages $16 and the result message queued; scratch18/scratch19 clobbered; self-modifies $29B0+1",
    "Called from: endGame ($4032)"
   ],
   "line": "save Y in the LDY # at $29B0 so the caller gets it back"
  },
  "28F4": {
   "unit": "game/mainProgram0800",
   "line": "throw away anything still queued for the status line"
  },
  "28F7": {
   "unit": "game/mainProgram0800",
   "line": "bits 6-7 = the game is over and player input is locked out"
  },
  "28F9": {
   "unit": "game/mainProgram0800",
   "line": "game-over flags"
  },
  "28FC": {
   "unit": "game/mainProgram0800",
   "line": "why the game ended"
  },
  "28FF": {
   "unit": "game/mainProgram0800",
   "line": "bit 7 set = a comcen was knocked out"
  },
  "2901": {
   "unit": "game/mainProgram0800",
   "line": "message $16 'GAME OVER.'"
  },
  "2903": {
   "unit": "game/mainProgram0800",
   "line": "queue it"
  },
  "2906": {
   "unit": "game/mainProgram0800",
   "block": [
    "Decide the result on points. The 16-bit totals are indexed by the side that was scored",
    "against, so subtracting them tells which side lost and by how much."
   ],
   "line": "points scored against side 0, low byte"
  },
  "2909": {
   "unit": "game/mainProgram0800",
   "line": "no borrow"
  },
  "290A": {
   "unit": "game/mainProgram0800",
   "line": "minus the points scored against side 1"
  },
  "290D": {
   "unit": "game/mainProgram0800",
   "line": "difference low byte"
  },
  "290F": {
   "unit": "game/mainProgram0800",
   "line": "high bytes..."
  },
  "2912": {
   "unit": "game/mainProgram0800",
   "line": "...with the borrow"
  },
  "2915": {
   "unit": "game/mainProgram0800",
   "line": "difference high byte"
  },
  "2917": {
   "unit": "game/mainProgram0800",
   "line": "is the difference zero?"
  },
  "2919": {
   "unit": "game/mainProgram0800",
   "line": "no: somebody won on points"
  },
  "291B": {
   "unit": "game/mainProgram0800",
   "block": [
    "A tie: both sides get 250 points. setOtherPlayerPoints flips X each time, so the two calls",
    "write slot 1 and then slot 0."
   ],
   "line": "start from side 0"
  },
  "291D": {
   "unit": "game/mainProgram0800",
   "line": "250 points"
  },
  "291F": {
   "unit": "game/mainProgram0800",
   "line": "writes the total of the other side (1) and leaves X = 1"
  },
  "2922": {
   "unit": "game/mainProgram0800",
   "line": "250 points again"
  },
  "2924": {
   "unit": "game/mainProgram0800",
   "line": "writes the total of side 0 and leaves X = 0"
  },
  "2927": {
   "unit": "game/mainProgram0800",
   "line": "message $13 'IT WAS A DRAW.'"
  },
  "2929": {
   "unit": "game/mainProgram0800",
   "line": "remember it as the result message"
  },
  "292C": {
   "unit": "game/mainProgram0800",
   "line": "queue it and finish"
  },
  "292F": {
   "unit": "game/mainProgram0800",
   "block": [
    "Somebody won: make the difference positive and remember which side lost (X). The winner's",
    "points go into the loser's score slot, because that slot counts what was scored against",
    "the loser."
   ],
   "line": "assume side 0 was outscored"
  },
  "2931": {
   "unit": "game/mainProgram0800",
   "line": "no borrow: more was scored against side 0, so side 0 lost"
  },
  "2933": {
   "unit": "game/mainProgram0800",
   "line": "otherwise negate the difference..."
  },
  "2935": {
   "unit": "game/mainProgram0800",
   "line": "..."
  },
  "2937": {
   "unit": "game/mainProgram0800",
   "line": "..."
  },
  "2938": {
   "unit": "game/mainProgram0800",
   "line": "..."
  },
  "293A": {
   "unit": "game/mainProgram0800",
   "line": "...low byte"
  },
  "293C": {
   "unit": "game/mainProgram0800",
   "line": "..."
  },
  "293E": {
   "unit": "game/mainProgram0800",
   "line": "..."
  },
  "2940": {
   "unit": "game/mainProgram0800",
   "line": "..."
  },
  "2942": {
   "unit": "game/mainProgram0800",
   "line": "...high byte"
  },
  "2944": {
   "unit": "game/mainProgram0800",
   "line": "side 1 is the loser"
  },
  "2946": {
   "unit": "game/mainProgram0800",
   "line": "half the point difference..."
  },
  "2948": {
   "unit": "game/mainProgram0800",
   "line": "...16 bits"
  },
  "294A": {
   "unit": "game/mainProgram0800",
   "line": "high byte of half the difference"
  },
  "294C": {
   "unit": "game/mainProgram0800",
   "line": "over 255: clamp"
  },
  "294E": {
   "unit": "game/mainProgram0800",
   "line": "low byte"
  },
  "2950": {
   "unit": "game/mainProgram0800",
   "line": "150 = the cap on the winning margin"
  },
  "2952": {
   "unit": "game/mainProgram0800",
   "line": "below the cap: use it"
  },
  "2954": {
   "unit": "game/mainProgram0800",
   "line": "clamp to 150"
  },
  "2956": {
   "unit": "game/mainProgram0800",
   "line": "no carry"
  },
  "2957": {
   "unit": "game/mainProgram0800",
   "line": "$012C = 300 base points for the winner"
  },
  "2959": {
   "unit": "game/mainProgram0800",
   "line": "into the loser's slot, which holds the winner's total"
  },
  "295C": {
   "unit": "game/mainProgram0800",
   "line": "high byte..."
  },
  "295E": {
   "unit": "game/mainProgram0800",
   "line": "...= 1 plus any carry from the 300 + margin add"
  },
  "2960": {
   "unit": "game/mainProgram0800",
   "line": "winner's total, high byte"
  },
  "2963": {
   "unit": "game/mainProgram0800",
   "line": "message $11 'WE LOST ON POINTS!'"
  },
  "2965": {
   "unit": "game/mainProgram0800",
   "line": "is the losing side ours?"
  },
  "2968": {
   "unit": "game/mainProgram0800",
   "line": "yes: keep message $11"
  },
  "296A": {
   "unit": "game/mainProgram0800",
   "line": "no: message $12 'WE WON ON POINTS!'"
  },
  "296B": {
   "unit": "game/mainProgram0800",
   "line": "remember the result message"
  },
  "296E": {
   "unit": "game/mainProgram0800",
   "line": "200 consolation points"
  },
  "2970": {
   "unit": "game/mainProgram0800",
   "line": "into the winner's slot, i.e. the loser's total"
  },
  "2973": {
   "unit": "game/mainProgram0800",
   "line": "queue the message, free the status line and restore Y"
  }
 },
 "dataTypes": {
  "24E1": [
   "byte",
   3
  ],
  "2762": [
   "byte",
   16
  ]
 },
 "zpComments": {
  "2F": "index of the last entry of the area-scan result lists (count-1, $FF = none); drawViewport reuses it as the counter of the 5 cell rows of the tactical view",
  "31": "source column counter of magnifyWindow2x; drawViewport reuses it as the counter of the 7 cells across the tactical view and as the offset of the cell inside the map row",
  "32": "colour byte of the map tile being rendered: first the terrain colour written into both nybbles of the screen RAM cells, then the unit colour (blue/red/white) written into colour RAM",
  "6C": "first of the six IRQ frame countdowns $6C-$71: joystick auto-repeat delay, also used as the 4-frame step delay of the animated mini-map path marker ($2506)",
  "6F": "frame countdown for the repair-marker and missile-button blink; printUnitStatusLines also loads it with 20 to hold FIRED in the WEAPON field of the info panel ($2424)",
  "B6": "pointer (lo) to the colour byte of the current cell in screen RAM $8C00; renderMapTile adds $4C to the high byte on the way out, so it points into colour RAM $D800 when a unit was drawn"
 }
}