{
 "chunk": "game_mapGenerator6F00_7BB2",
 "unit": "game/mapGenerator6F00",
 "unitLabels": {
  "game/mapGenerator6F00": {
   "7BBC": "shuffleSwapLoop",
   "7C03": "phaseSyncWaitLoop",
   "7C54": "sideDefaultsLoop",
   "7CA0": "removeQbSneakUnits",
   "7CF4": "filmNameByteLoop",
   "7D04": "filmSeedByteLoop",
   "7D35": "filmMapIdByteLoop",
   "7D72": "fillOrderListLoop",
   "7D81": "placeNextUnit",
   "7D9A": "removeNextUnit",
   "7DC8": "waitSpaceLoop",
   "7E63": "checkBamAllocatedLoop",
   "7E9D": "filmDirectoryDiskError",
   "7ECB": "printDirectoryEntryLoop",
   "7F77": "loadFilmSectorLoop",
   "7FBA": "addDirectoryPageOffsetLoop",
   "7FEC": "saveMenuRedrawRow",
   "801A": "saveMenuInputLoop",
   "80B9": "saveMenuAccept",
   "80E1": "padDirectoryNameLoop",
   "80EB": "copyTypedNameLoop",
   "8184": "writeFilmSectorLoop",
   "81C7": "patchDiskNameLoop",
   "81DD": "waitSpaceForDiskLoop",
   "8202": "waitFramesLinkLoop"
  }
 },
 "notes": {
  "7BB2": {
   "unit": "game/mapGenerator6F00",
   "routine": [
    "shuffleInitialUnitOrder - builds the order in which the 100 units are processed each round.",
    "Re-seeds the scenario RNG from the shared 24-bit map seed, resets unitBaseOrder ($9164) and",
    "unitProcessOrder ($90FD) to the identity permutation and then performs 51 random swaps of",
    "unitBaseOrder.  Because the RNG is seeded from the map seed both machines shuffle identically",
    "without exchanging a single byte.  Ends by jumping into the round-order rebuild at L_4657 (the",
    "tail of finishRound), whose RTS returns to our caller.",
    "In:  mapSeed $0B95-$0B97; scenario RNG state zp_5A-zp_5C",
    "Out: $9164 shuffled, $90FD reset to 0..99, RNG advanced, zp_18 used as the swap counter;",
    "     continues in L_4657 (recycle dead units, rebuild the group tables, age visibility)",
    "Called from: $7B48 and $7B99, the two game-start paths of this overlay"
   ],
   "line": "copy the 24-bit map seed $0B95-$0B97 into the scenario RNG state zp_5A-zp_5C"
  },
  "7BB5": {
   "unit": "game/mapGenerator6F00",
   "line": "start from the identity permutation 0..99 in both order lists"
  },
  "7BB8": {
   "unit": "game/mapGenerator6F00",
   "line": "$32 = 50, so the loop below runs 51 times (counter $32..$00)"
  },
  "7BBA": {
   "unit": "game/mapGenerator6F00",
   "line": "zp_18 = number of swaps still to do"
  },
  "7BBC": {
   "unit": "game/mapGenerator6F00",
   "line": "one swap: exchange two randomly chosen entries of unitBaseOrder ($47ED)"
  },
  "7BBF": {
   "unit": "game/mapGenerator6F00",
   "line": "count this swap off"
  },
  "7BC1": {
   "unit": "game/mapGenerator6F00",
   "line": "loop until the counter goes negative - 51 swaps in all"
  },
  "7BC3": {
   "unit": "game/mapGenerator6F00",
   "line": "continue in the round-order rebuild at the end of finishRound; its RTS returns to our caller"
  },
  "7BC6": {
   "unit": "game/mapGenerator6F00",
   "routine": [
    "patchOwnSideBranches - burns the local player's side into the engine's self-modifying",
    "'is this unit mine?' tests.  Each of those tests compares a unit index with $32 (50) and is",
    "followed by a branch whose opcode byte is a variable: BCC ($90) means 'own units are 0-49'",
    "(side 0), BCS ($B0) means 'own units are 50-99' (side 1).  Seven sites get that opcode and four",
    "sites, phrased the other way round ('skip if NOT mine'), get the inverted one.  Finally caches",
    "this side's comcen unit index.",
    "In:  playerSide $0B9F, perSideValueTable $0C8A ($31/$63 = unit 49/99)",
    "Out: branch opcodes at $63E2 $2840 $3F9E $36D5 $379C $4783 $5BF7 and the inverted opcodes at",
    "     $206B $2084 $3B2C $3BD0; comcenUnit $9236",
    "Called from: $798E (start of a normal game) and $7D20 (start of film playback)"
   ],
   "line": "$90 = BCC: for side 0 'own unit' means index < 50"
  },
  "7BC8": {
   "unit": "game/mapGenerator6F00",
   "line": "which side is the local player on?"
  },
  "7BCB": {
   "unit": "game/mapGenerator6F00",
   "line": "side 0 keeps the BCC"
  },
  "7BCD": {
   "unit": "game/mapGenerator6F00",
   "line": "$B0 = BCS: for side 1 'own unit' means index >= 50"
  },
  "7BCF": {
   "unit": "game/mapGenerator6F00",
   "line": "runGroupEditor $63E0 'cpy #$32': marks the picked unit with bit 7 when it is not ours"
  },
  "7BD2": {
   "unit": "game/mapGenerator6F00",
   "line": "renderMapTile $283E 'cpy #$32': the other side's units are drawn in the enemy colour"
  },
  "7BD5": {
   "unit": "game/mapGenerator6F00",
   "line": "initialFacingForUnit $3F9C 'cpx #$32': own units start facing up (0), enemies down (1)"
  },
  "7BD8": {
   "unit": "game/mapGenerator6F00",
   "line": "scanVisibleEnemies $36D3 'cpy #$32': only units of the opposite side enter the spotted list"
  },
  "7BDB": {
   "unit": "game/mapGenerator6F00",
   "line": "$379C is two leftover bytes ($90 $FA) in front of collectAllUnitsInRadiusDead - a dead branch, this store changes nothing"
  },
  "7BDE": {
   "unit": "game/mapGenerator6F00",
   "line": "moveUnitToCell $4781 'cpx #$32': the column is mirrored before scoring an end zone for the other side"
  },
  "7BE1": {
   "unit": "game/mapGenerator6F00",
   "line": "buildOverviewQuadrant $5BF5: inverts the mini-map pattern for the other side (that test uses the map-cell form, 'cpy #$B2' = $80|50)"
  },
  "7BE4": {
   "unit": "game/mapGenerator6F00",
   "line": "$90 EOR $20 = $B0 and back - the four sites below test 'skip if NOT mine'"
  },
  "7BE6": {
   "unit": "game/mapGenerator6F00",
   "line": "runMapMainLoop $2069 'cmp #$32': the info panel only opens for our own units"
  },
  "7BE9": {
   "unit": "game/mapGenerator6F00",
   "line": "runMapMainLoop $2082 'cmp #$32': only our own units can be picked with the joystick"
  },
  "7BEC": {
   "unit": "game/mapGenerator6F00",
   "line": "applyHitToUnit $3B2A 'cpy #$32': the spy/comcen hit reactions are for our units only"
  },
  "7BEF": {
   "unit": "game/mapGenerator6F00",
   "line": "applyHitToUnit $3BCE 'cpy #$32': decides whether endGame is entered with X=0 (we lost) or X=1"
  },
  "7BF2": {
   "unit": "game/mapGenerator6F00",
   "line": "$0C8A: comcen index of side Y - 49 for side 0, 99 for side 1"
  },
  "7BF5": {
   "unit": "game/mapGenerator6F00",
   "line": "cache it in comcenUnit $9236 for the rest of the game"
  },
  "7BF8": {
   "unit": "game/mapGenerator6F00",
   "line": "done"
  },
  "7BF9": {
   "unit": "game/mapGenerator6F00",
   "routine": [
    "sendPhaseSyncAndWait - the 'I am ready' handshake that separates the setup phases.  Queues the",
    "1-byte command $9E and then keeps running frames until both ready bits of gamePhase zp_B1 are",
    "clear: the $9E handler ($520C) clears the sending side's bit on both machines (bit 0 = us,",
    "bit 1 = the opponent).  While waiting it runs a full game frame during the battle, or only",
    "services the link when gamePhase says we are in a menu/setup phase.",
    "In:  gamePhase zp_B1 (bits 0/1 = side not ready, bits 6/7 = not in the battle), the link",
    "Out: zp_B1 low two bits cleared by the command handler, persistentMessageId $92A1 = 0",
    "Called from: $79EC, $7B04 and $7B61 - the three setup-phase boundaries"
   ],
   "line": "$9E = 'setup phase ready' (during the battle the same command means 'comcen repaired')"
  },
  "7BFB": {
   "unit": "game/mapGenerator6F00",
   "line": "queue it for the opponent and count it as one of our own pending commands"
  },
  "7BFE": {
   "unit": "game/mapGenerator6F00",
   "line": "pass the ready/pause bits to the message code in A"
  },
  "7C00": {
   "unit": "game/mapGenerator6F00",
   "line": "on a modem game with bit 0 still set this shows message $0B 'WAITING FOR OPPONENT'"
  },
  "7C03": {
   "unit": "game/mapGenerator6F00",
   "block": [
    "Wait loop: re-read the ready bits every frame until both sides have executed the $9E."
   ],
   "line": "current ready/pause bits"
  },
  "7C05": {
   "unit": "game/mapGenerator6F00",
   "line": "bits 6-7 set = menu or setup phase, the battle is not running"
  },
  "7C07": {
   "unit": "game/mapGenerator6F00",
   "line": "battle phase: run a whole game frame"
  },
  "7C09": {
   "unit": "game/mapGenerator6F00",
   "line": "setup phase: just service the link until the packet exchange is idle"
  },
  "7C0C": {
   "unit": "game/mapGenerator6F00",
   "line": "and re-test"
  },
  "7C0F": {
   "unit": "game/mapGenerator6F00",
   "line": "battle phase: one complete game update (input, movement, drawing, link)"
  },
  "7C12": {
   "unit": "game/mapGenerator6F00",
   "line": "read the ready bits again"
  },
  "7C14": {
   "unit": "game/mapGenerator6F00",
   "line": "bit 0 = our $9E not executed yet, bit 1 = the opponent's not seen yet"
  },
  "7C16": {
   "unit": "game/mapGenerator6F00",
   "line": "keep waiting while either side is still not ready"
  },
  "7C18": {
   "unit": "game/mapGenerator6F00",
   "line": "A is 0 here: drop the 'WAITING FOR OPPONENT' status message"
  },
  "7C1B": {
   "unit": "game/mapGenerator6F00",
   "line": "both sides are ready"
  },
  "7C1C": {
   "unit": "game/mapGenerator6F00",
   "routine": [
    "waitForMapSeedCommand - waits until the map seed sent by the other machine has arrived.",
    "gamePhase zp_B1 bit 7 is set by the caller and cleared by the handler of the map-seed commands",
    "$8B / $A6 ($51EA), so this loop simply services the link until that command has been executed.",
    "In:  gamePhase zp_B1 bit 7, the link",
    "Out: loops until zp_B1 bit 7 is clear; persistentMessageId $92A1 = 0",
    "Called from: $79C2 (the side that waits for its opponent to choose the map)"
   ],
   "line": "service the link until the current packet exchange goes idle"
  },
  "7C1F": {
   "unit": "game/mapGenerator6F00",
   "line": "bit 7 is only cleared by the $8B/$A6 map-seed command handler at $51EA"
  },
  "7C21": {
   "unit": "game/mapGenerator6F00",
   "line": "keep polling while the seed has not arrived"
  },
  "7C23": {
   "unit": "game/mapGenerator6F00",
   "line": "the seed is in: clear the status message"
  },
  "7C25": {
   "unit": "game/mapGenerator6F00",
   "line": "persistentMessageId $92A1 = 0"
  },
  "7C28": {
   "unit": "game/mapGenerator6F00",
   "line": "both machines now hold the same map seed"
  },
  "7C29": {
   "unit": "game/mapGenerator6F00",
   "block": [
    "setupPhaseModeTable - one byte per game-type class (the folded column 0-4 computed by",
    "applyGameTypeDefaults), copied into setupPhaseMode $92C6: 0 = no setup phase at all",
    "(SCRIMAGE, QB SNEAK), 1 = both sides deploy their units and terrain (THE BOMB, FACE-OFF,",
    "SLUGGERS, FULL WAR), $80 = only side 0 deploys (DEFENDER)."
   ],
   "line": "classes 0-4: 0, 0, 1, 1, $80"
  },
  "7C2E": {
   "unit": "game/mapGenerator6F00",
   "routine": [
    "applyGameTypeDefaults - loads the per-side game parameters of the chosen game type from the two",
    "5x5 default tables at $892C (side 0) and $8945 (side 1) in the $8800 overlay.  The game type",
    "$0BA3 bits 0-2 is first folded into a table column: 0 SCRIMAGE, 1 QB SNEAK, 2 THE BOMB /",
    "FACE-OFF / SLUGGERS, 3 FULL WAR, 4 DEFENDER; the same column selects the setup-phase mode.",
    "Table rows 0-1 (terrain points, recycler mode) are stored by absolute side, rows 2-4 (comcen",
    "speed class, drones, missiles) by relative index (0 = us, 1 = the opponent).  Afterwards the two",
    "special rosters are applied: QB SNEAK takes units 0-6 out of play, THE BOMB takes out units",
    "0-98, re-creates unit 49 as a COMCEN and gives each side an extra drone.",
    "In:  gameTypeOptions $0BA3 bits 0-2, playerSide $0B9F, tables $892C/$8945, setupPhaseModeTable",
    "Out: setupPhaseMode $92C6; terrainPointsBySide $92AC and recyclerMode $92A6 per absolute side;",
    "     comcenSpeedClass $92B4, dronesLeft $92AE, missilesLeft $92B0 per relative index; unit slots",
    "     emptied in $F76C/$FAF0; zp_18, zp_48/zp_49; self-modifies $7C85+1 and $7CBF+1",
    "Called from: $79E9, once, after the game type has been agreed"
   ],
   "line": "the agreed game type and option bits"
  },
  "7C31": {
   "unit": "game/mapGenerator6F00",
   "line": "bits 0-2 = type 0 SCRIMAGE .. 6 DEFENDER"
  },
  "7C33": {
   "unit": "game/mapGenerator6F00",
   "line": "keep the type in Y while it is folded into a table column"
  },
  "7C34": {
   "unit": "game/mapGenerator6F00",
   "line": "self-modifying: the raw game type becomes the operand of the LDA #$FF at $7C85"
  },
  "7C37": {
   "unit": "game/mapGenerator6F00",
   "line": "types 0 and 1 have a column of their own"
  },
  "7C39": {
   "unit": "game/mapGenerator6F00",
   "line": "SCRIMAGE and QB SNEAK: column = type"
  },
  "7C3B": {
   "unit": "game/mapGenerator6F00",
   "line": "types 3-6 go straight to the common DEY"
  },
  "7C3D": {
   "unit": "game/mapGenerator6F00",
   "line": "type 2 (THE BOMB) is bumped to 3 so the DEY puts it on column 2"
  },
  "7C3E": {
   "unit": "game/mapGenerator6F00",
   "line": "types 2-6: one column less"
  },
  "7C3F": {
   "unit": "game/mapGenerator6F00",
   "line": "columns 0-2 are final"
  },
  "7C41": {
   "unit": "game/mapGenerator6F00",
   "line": "THE BOMB and FACE-OFF share column 2"
  },
  "7C43": {
   "unit": "game/mapGenerator6F00",
   "line": "SLUGGERS, FULL WAR and DEFENDER lose one more: columns 2, 3 and 4"
  },
  "7C44": {
   "unit": "game/mapGenerator6F00",
   "line": "self-modifying: the column index becomes the LDY operand of readDefaultAndAdvanceRow ($7CC0)"
  },
  "7C47": {
   "unit": "game/mapGenerator6F00",
   "line": "setup-phase mode for this class of game"
  },
  "7C4A": {
   "unit": "game/mapGenerator6F00",
   "line": "$92C6: 0 = no setup phase, 1 = both sides deploy, $80 = only side 0 deploys"
  },
  "7C4D": {
   "unit": "game/mapGenerator6F00",
   "line": "first pass: X = the local player's absolute side"
  },
  "7C50": {
   "unit": "game/mapGenerator6F00",
   "line": "relative index 0 = us"
  },
  "7C52": {
   "unit": "game/mapGenerator6F00",
   "line": "zp_18 = 0 (us) then 1 (the opponent)"
  },
  "7C54": {
   "unit": "game/mapGenerator6F00",
   "block": [
    "Two passes over the 5-row default table, ours first.  Rows 0-1 are stored under the absolute",
    "side number (they describe a side of the battlefield), rows 2-4 under the relative index",
    "(they describe a player: 0 = this machine, 1 = the other one)."
   ],
   "line": "point zp_48/zp_49 at the 5x5 default table of the side in X"
  },
  "7C57": {
   "unit": "game/mapGenerator6F00",
   "line": "row 0 of the selected column = terrain points"
  },
  "7C5A": {
   "unit": "game/mapGenerator6F00",
   "line": "$92AC+side: terrain points this side may place in the setup phase"
  },
  "7C5D": {
   "unit": "game/mapGenerator6F00",
   "line": "row 1 = recycler mode"
  },
  "7C60": {
   "unit": "game/mapGenerator6F00",
   "line": "$92A6+side: whether and how that side gets a recycler"
  },
  "7C63": {
   "unit": "game/mapGenerator6F00",
   "line": "rows 2-4 are indexed by the relative side instead"
  },
  "7C65": {
   "unit": "game/mapGenerator6F00",
   "line": "row 2 = comcen speed class"
  },
  "7C68": {
   "unit": "game/mapGenerator6F00",
   "line": "$92B4+rel (later mapped through comcenSpeedClassMap at $7B6E)"
  },
  "7C6B": {
   "unit": "game/mapGenerator6F00",
   "line": "row 3 = number of drones"
  },
  "7C6E": {
   "unit": "game/mapGenerator6F00",
   "line": "$92AE+rel; the D_7C6E label only exists because the comcen-screens overlay has code here"
  },
  "7C71": {
   "unit": "game/mapGenerator6F00",
   "line": "row 4 = number of missiles"
  },
  "7C74": {
   "unit": "game/mapGenerator6F00",
   "line": "$92B0+rel"
  },
  "7C77": {
   "unit": "game/mapGenerator6F00",
   "line": "second pass: the opponent's absolute side"
  },
  "7C7A": {
   "unit": "game/mapGenerator6F00",
   "line": "side EOR 1"
  },
  "7C7C": {
   "unit": "game/mapGenerator6F00",
   "line": "X = opponent side for the next pass"
  },
  "7C7D": {
   "unit": "game/mapGenerator6F00",
   "line": "relative index 1 = the opponent"
  },
  "7C7F": {
   "unit": "game/mapGenerator6F00",
   "line": "two passes only"
  },
  "7C81": {
   "unit": "game/mapGenerator6F00",
   "line": "0 = us, 1 = them"
  },
  "7C83": {
   "unit": "game/mapGenerator6F00",
   "line": "go round again for the opponent"
  },
  "7C85": {
   "unit": "game/mapGenerator6F00",
   "block": [
    "Roster adjustments for the two game types that are not played with all 50 units a side."
   ],
   "line": "operand patched at $7C34: A = the raw game type 0-6"
  },
  "7C87": {
   "unit": "game/mapGenerator6F00",
   "line": "type 1 = QB SNEAK"
  },
  "7C89": {
   "unit": "game/mapGenerator6F00",
   "line": "QB SNEAK is played with a handful of units only"
  },
  "7C8B": {
   "unit": "game/mapGenerator6F00",
   "line": "type 2 = THE BOMB"
  },
  "7C8D": {
   "unit": "game/mapGenerator6F00",
   "line": "every other type keeps the full roster"
  },
  "7C8F": {
   "unit": "game/mapGenerator6F00",
   "line": "THE BOMB: take units 0-98 out of play ($62 = 98)"
  },
  "7C91": {
   "unit": "game/mapGenerator6F00",
   "line": "empty those unit slots"
  },
  "7C94": {
   "unit": "game/mapGenerator6F00",
   "line": "type 4 = COMCEN"
  },
  "7C96": {
   "unit": "game/mapGenerator6F00",
   "line": "$F79D = unitTypeTable+49: put side 0's comcen back into play"
  },
  "7C99": {
   "unit": "game/mapGenerator6F00",
   "line": "one extra drone for us ..."
  },
  "7C9C": {
   "unit": "game/mapGenerator6F00",
   "line": "... and one for the opponent: THE BOMB is fought with comcens and drones alone"
  },
  "7C9F": {
   "unit": "game/mapGenerator6F00",
   "line": "done"
  },
  "7CA0": {
   "unit": "game/mapGenerator6F00",
   "line": "QB SNEAK: take units 0-6 out of play"
  },
  "7CA2": {
   "unit": "game/mapGenerator6F00",
   "routine": [
    "removeUnitsUpToY - marks unit slots Y down to 0 as 'not in this game': unitTypeTable ($F76C)",
    "= $FF (bit 7 = dead / no such unit) and unitTerrainUnderTable ($FAF0) = $FF ('not standing on",
    "the map').  Entered with Y = 6 for QB SNEAK and Y = $62 for THE BOMB.  The RTS at $7CAD is also",
    "the shared exit of applyGameTypeDefaults.",
    "In:  Y = highest unit index to remove",
    "Out: $F76C[0..Y] = $FAF0[0..Y] = $FF, Y = $FF",
    "Called from: $7C91 (applyGameTypeDefaults) and by fall-through from $7CA0"
   ],
   "line": "$FF = empty unit slot"
  },
  "7CA4": {
   "unit": "game/mapGenerator6F00",
   "line": "unitTypeTable[Y] = $FF: bit 7 marks it dead, the whole byte marks it absent"
  },
  "7CA7": {
   "unit": "game/mapGenerator6F00",
   "line": "unitTerrainUnderTable[Y] = $FF: this unit occupies no map cell"
  },
  "7CAA": {
   "unit": "game/mapGenerator6F00",
   "line": "next lower unit"
  },
  "7CAB": {
   "unit": "game/mapGenerator6F00",
   "line": "down to unit 0"
  },
  "7CAD": {
   "unit": "game/mapGenerator6F00",
   "line": "shared exit of applyGameTypeDefaults"
  },
  "7CAE": {
   "unit": "game/mapGenerator6F00",
   "routine": [
    "selectSideDefaultsTable - points zp_48/zp_49 at the game-type default table of side X: $892C",
    "for side 0 and $8945 for side 1.  Both live in the $8800 modem-strings overlay and hold 5 rows",
    "of 5 bytes; the row is the parameter and the column the game-type class.",
    "In:  X = side 0/1",
    "Out: zp_48/zp_49 = address of row 0 of that table",
    "Called from: $7C54 (applyGameTypeDefaults)"
   ],
   "line": "low byte of $892C = side 0's defaults"
  },
  "7CB0": {
   "unit": "game/mapGenerator6F00",
   "line": "high byte (the $8800 strings overlay)"
  },
  "7CB2": {
   "unit": "game/mapGenerator6F00",
   "line": "side 0?"
  },
  "7CB4": {
   "unit": "game/mapGenerator6F00",
   "line": "yes: keep $892C"
  },
  "7CB6": {
   "unit": "game/mapGenerator6F00",
   "line": "low byte of $8945 = side 1's defaults, 25 bytes further on"
  },
  "7CB8": {
   "unit": "game/mapGenerator6F00",
   "line": "high byte"
  },
  "7CBA": {
   "unit": "game/mapGenerator6F00",
   "line": "zp_48 = pointer to the current table row"
  },
  "7CBC": {
   "unit": "game/mapGenerator6F00",
   "line": "zp_49 = high byte"
  },
  "7CBE": {
   "unit": "game/mapGenerator6F00",
   "line": "ready for readDefaultAndAdvanceRow"
  },
  "7CBF": {
   "unit": "game/mapGenerator6F00",
   "routine": [
    "readDefaultAndAdvanceRow - reads the byte of the current 5-byte table row that belongs to the",
    "chosen game-type column and then advances zp_48/zp_49 to the next row.  The LDY operand at",
    "$7CC0 is the column index, written by applyGameTypeDefaults at $7C44.",
    "In:  zp_48/zp_49 = current row, column index patched into $7CC0",
    "Out: A = the table value, zp_48/zp_49 += 5",
    "Called from: $7C57, $7C5D, $7C65, $7C6B, $7C71 (all inside applyGameTypeDefaults)"
   ],
   "line": "operand patched at $7C44: Y = game-type column 0-4"
  },
  "7CC1": {
   "unit": "game/mapGenerator6F00",
   "line": "the default for this parameter and this game type"
  },
  "7CC3": {
   "unit": "game/mapGenerator6F00",
   "line": "hold it while the pointer moves on"
  },
  "7CC4": {
   "unit": "game/mapGenerator6F00",
   "line": "advance the row pointer ..."
  },
  "7CC6": {
   "unit": "game/mapGenerator6F00",
   "line": "clear carry before the add"
  },
  "7CC7": {
   "unit": "game/mapGenerator6F00",
   "line": "... by 5 bytes = one table row"
  },
  "7CC9": {
   "unit": "game/mapGenerator6F00",
   "line": "store it back"
  },
  "7CCB": {
   "unit": "game/mapGenerator6F00",
   "line": "no page crossing"
  },
  "7CCD": {
   "unit": "game/mapGenerator6F00",
   "line": "carry into the high byte"
  },
  "7CCF": {
   "unit": "game/mapGenerator6F00",
   "line": "get the value back"
  },
  "7CD0": {
   "unit": "game/mapGenerator6F00",
   "line": "A = the default value"
  },
  "7CD1": {
   "unit": "game/mapGenerator6F00",
   "routine": [
    "startFilmPlayback - the game-start path taken when WATCH GAME FILM was chosen ($0B9B bit 7).",
    "Everything the film needs is in its 52-byte header in the RAM under I/O at $D000, written by",
    "$7A78-$7AD8 when the game was recorded; this routine reads it back with readFilmByte and",
    "rebuilds the game from it: link type, side, game type, both player names, map seed, map",
    "orientation and who plays first.  The battlefield itself is not stored - it is regenerated from",
    "the seed and rotated if necessary.  Then the recorded starting state is restored, the units are",
    "placed, the screen is drawn and the common game-start tail at L_7B8F is entered.",
    "In:  film header at $D000 (zp_BC/zp_BD = $D000, left there by checkFilmInMemory)",
    "Out: $0BA8=0, $92FC=0, $92FE, $0B9F, $0BA3, $FFDE-$FFF1, $0B95-$0B97, $0B9D, $0BA1, $0BA9-$0BAE,",
    "     $90FB=0, $92FA=$FF, map at $F000 regenerated, all units placed; jumps to L_7B8F",
    "Called from: $7921 (JMP, from the game-start dispatcher)"
   ],
   "line": "no game-over reason yet"
  },
  "7CD3": {
   "unit": "game/mapGenerator6F00",
   "line": "$0BA8 = 0: the game has not ended"
  },
  "7CD6": {
   "unit": "game/mapGenerator6F00",
   "line": "$92FC = 0: start the replay with the normal one-sided view"
  },
  "7CD9": {
   "unit": "game/mapGenerator6F00",
   "line": "header byte 4"
  },
  "7CDB": {
   "unit": "game/mapGenerator6F00",
   "block": [
    "Read the recorded game parameters out of the film header at $D000 (RAM under I/O)."
   ],
   "line": "read one header byte through zp_BC/zp_BD with the ROMs and I/O banked out"
  },
  "7CDE": {
   "unit": "game/mapGenerator6F00",
   "line": "$92FE: was the recorded game played against the trainer or over a modem?"
  },
  "7CE1": {
   "unit": "game/mapGenerator6F00",
   "line": "header byte 5"
  },
  "7CE2": {
   "unit": "game/mapGenerator6F00",
   "line": "read it"
  },
  "7CE5": {
   "unit": "game/mapGenerator6F00",
   "line": "the side the film was recorded from - the replay is watched through that player's eyes"
  },
  "7CE8": {
   "unit": "game/mapGenerator6F00",
   "line": "header byte $0C"
  },
  "7CEA": {
   "unit": "game/mapGenerator6F00",
   "line": "read it"
  },
  "7CED": {
   "unit": "game/mapGenerator6F00",
   "line": "$0BA3: game type and option bits of the recorded game"
  },
  "7CF0": {
   "unit": "game/mapGenerator6F00",
   "line": "header bytes $11-$24 = the two 10-byte player names"
  },
  "7CF2": {
   "unit": "game/mapGenerator6F00",
   "line": "X walks the name block at $FFDE"
  },
  "7CF4": {
   "unit": "game/mapGenerator6F00",
   "line": "next name byte"
  },
  "7CF7": {
   "unit": "game/mapGenerator6F00",
   "line": "$FFDE = our name, $FFE8 = the opponent's"
  },
  "7CFA": {
   "unit": "game/mapGenerator6F00",
   "line": "next header offset"
  },
  "7CFB": {
   "unit": "game/mapGenerator6F00",
   "line": "next destination byte"
  },
  "7CFC": {
   "unit": "game/mapGenerator6F00",
   "line": "$14 = 20 bytes = two 10-character names"
  },
  "7CFE": {
   "unit": "game/mapGenerator6F00",
   "line": "keep copying"
  },
  "7D00": {
   "unit": "game/mapGenerator6F00",
   "line": "header bytes $2B-$2D = the 24-bit map seed"
  },
  "7D02": {
   "unit": "game/mapGenerator6F00",
   "line": "X = seed byte 0-2"
  },
  "7D04": {
   "unit": "game/mapGenerator6F00",
   "line": "next seed byte"
  },
  "7D07": {
   "unit": "game/mapGenerator6F00",
   "line": "$0B95-$0B97: the number the map name is written from"
  },
  "7D0A": {
   "unit": "game/mapGenerator6F00",
   "line": "next header offset"
  },
  "7D0B": {
   "unit": "game/mapGenerator6F00",
   "line": "next seed byte"
  },
  "7D0C": {
   "unit": "game/mapGenerator6F00",
   "line": "three bytes"
  },
  "7D0E": {
   "unit": "game/mapGenerator6F00",
   "line": "keep copying"
  },
  "7D10": {
   "unit": "game/mapGenerator6F00",
   "line": "header byte $2E packs two flags"
  },
  "7D13": {
   "unit": "game/mapGenerator6F00",
   "line": "keep the packed byte in X"
  },
  "7D14": {
   "unit": "game/mapGenerator6F00",
   "line": "bit 0 = map orientation"
  },
  "7D16": {
   "unit": "game/mapGenerator6F00",
   "line": "$0B9D: the map was shown rotated 180 degrees"
  },
  "7D19": {
   "unit": "game/mapGenerator6F00",
   "line": "the packed byte again"
  },
  "7D1A": {
   "unit": "game/mapGenerator6F00",
   "line": "shift the orientation bit out"
  },
  "7D1B": {
   "unit": "game/mapGenerator6F00",
   "line": "bits 1-2 = who plays first"
  },
  "7D1D": {
   "unit": "game/mapGenerator6F00",
   "line": "$0BA1"
  },
  "7D20": {
   "unit": "game/mapGenerator6F00",
   "block": [
    "Rebuild the battlefield: the film stores only the seed, never the 1600 map cells."
   ],
   "line": "burn the film's side into the engine's self-modifying side tests"
  },
  "7D23": {
   "unit": "game/mapGenerator6F00",
   "line": "regenerate the identical 40x40 map from the seed"
  },
  "7D26": {
   "unit": "game/mapGenerator6F00",
   "line": "the generator always builds the map for side 0 ..."
  },
  "7D29": {
   "unit": "game/mapGenerator6F00",
   "line": "... so compare the viewing side with the recorded orientation"
  },
  "7D2C": {
   "unit": "game/mapGenerator6F00",
   "line": "orientations agree: leave the map alone"
  },
  "7D2E": {
   "unit": "game/mapGenerator6F00",
   "line": "turn the battlefield 180 degrees and mirror the direction-dependent terrain codes"
  },
  "7D31": {
   "unit": "game/mapGenerator6F00",
   "line": "header bytes 6-$0B = the 6-byte map-id text"
  },
  "7D33": {
   "unit": "game/mapGenerator6F00",
   "line": "X walks $0BA9-$0BAE"
  },
  "7D35": {
   "unit": "game/mapGenerator6F00",
   "line": "next character of the map name"
  },
  "7D38": {
   "unit": "game/mapGenerator6F00",
   "line": "restore it: generateBattlefieldMap has just overwritten $0BA9 with its own copy"
  },
  "7D3B": {
   "unit": "game/mapGenerator6F00",
   "line": "next header offset"
  },
  "7D3C": {
   "unit": "game/mapGenerator6F00",
   "line": "next character"
  },
  "7D3D": {
   "unit": "game/mapGenerator6F00",
   "line": "six characters"
  },
  "7D3F": {
   "unit": "game/mapGenerator6F00",
   "line": "keep copying"
  },
  "7D41": {
   "unit": "game/mapGenerator6F00",
   "line": "restore the recorded starting state (unit arrays, scores, clock) from $D034 onwards"
  },
  "7D44": {
   "unit": "game/mapGenerator6F00",
   "line": "the recorded option bits"
  },
  "7D47": {
   "unit": "game/mapGenerator6F00",
   "line": "bit 6 = CUSTOM rules"
  },
  "7D49": {
   "unit": "game/mapGenerator6F00",
   "line": "standard rules: nothing to announce"
  },
  "7D4B": {
   "unit": "game/mapGenerator6F00",
   "line": "put the 'CUSTOM: ...' summary on the status line"
  },
  "7D4E": {
   "unit": "game/mapGenerator6F00",
   "block": [
    "Put the restored units on the map and draw the battlefield screen."
   ],
   "line": "write $80|index into the map cell of every living unit"
  },
  "7D51": {
   "unit": "game/mapGenerator6F00",
   "line": "move the viewport over the player's own end of the field"
  },
  "7D54": {
   "unit": "game/mapGenerator6F00",
   "line": "screen id 0 ..."
  },
  "7D56": {
   "unit": "game/mapGenerator6F00",
   "line": "... = the battlefield screen"
  },
  "7D59": {
   "unit": "game/mapGenerator6F00",
   "line": "clear the bitmap and redraw the frames, panels and console"
  },
  "7D5C": {
   "unit": "game/mapGenerator6F00",
   "line": "draw the 20x20 overview map in the left panel"
  },
  "7D5F": {
   "unit": "game/mapGenerator6F00",
   "line": "bit 7 set ..."
  },
  "7D61": {
   "unit": "game/mapGenerator6F00",
   "line": "... lets the status line keep the clock and score up to date ($C50D)"
  },
  "7D64": {
   "unit": "game/mapGenerator6F00",
   "line": "our side"
  },
  "7D67": {
   "unit": "game/mapGenerator6F00",
   "line": "EOR 1 = the opponent's side"
  },
  "7D69": {
   "unit": "game/mapGenerator6F00",
   "line": "drawRecyclerOnMap takes the side in Y"
  },
  "7D6A": {
   "unit": "game/mapGenerator6F00",
   "line": "put the opponent's recycler glyph back on the map"
  },
  "7D6D": {
   "unit": "game/mapGenerator6F00",
   "line": "join the common game-start tail (the film's own recycler is drawn there)"
  },
  "7D70": {
   "unit": "game/mapGenerator6F00",
   "routine": [
    "initUnitOrderLists - fills the two 100-entry unit order lists unitBaseOrder ($9164) and",
    "unitProcessOrder ($90FD) with the identity permutation, 99 down to 0.",
    "In:  -",
    "Out: $9164[i] = $90FD[i] = i for i = 0..99, Y = $FF",
    "Called from: $7BB5 (shuffleInitialUnitOrder), before the base list is shuffled"
   ],
   "line": "$63 = 99, the last unit index"
  },
  "7D72": {
   "unit": "game/mapGenerator6F00",
   "line": "entry Y gets the value Y"
  },
  "7D73": {
   "unit": "game/mapGenerator6F00",
   "line": "$9164: the shuffled base order built by shuffleInitialUnitOrder"
  },
  "7D76": {
   "unit": "game/mapGenerator6F00",
   "line": "$90FD: the working order the round loop walks"
  },
  "7D79": {
   "unit": "game/mapGenerator6F00",
   "line": "next lower unit"
  },
  "7D7A": {
   "unit": "game/mapGenerator6F00",
   "line": "down to unit 0"
  },
  "7D7C": {
   "unit": "game/mapGenerator6F00",
   "line": "both lists are now 0..99"
  },
  "7D7D": {
   "unit": "game/mapGenerator6F00",
   "routine": [
    "placeAllUnitsOnMap - runs placeUnitOnMap for every unit 99 down to 0, which resets the unit's",
    "energy, move points and flags, saves the terrain under it and writes $80|index into its map",
    "cell (a dead unit becomes an empty slot instead).  The callee wants the index in zp_18 as well",
    "as in X.",
    "In:  the unit column/row arrays, the map at $F000",
    "Out: every living unit is on the map, zp_18 = $FF",
    "Called from: $7B83 (game start) and $7D4E (film playback)"
   ],
   "line": "start at unit 99"
  },
  "7D7F": {
   "unit": "game/mapGenerator6F00",
   "line": "placeUnitOnMap reads the index from zp_18 as well as from X"
  },
  "7D81": {
   "unit": "game/mapGenerator6F00",
   "line": "X = the unit to place"
  },
  "7D83": {
   "unit": "game/mapGenerator6F00",
   "line": "reset the unit and write $80|index into its map cell, saving the terrain underneath"
  },
  "7D86": {
   "unit": "game/mapGenerator6F00",
   "line": "next lower unit"
  },
  "7D88": {
   "unit": "game/mapGenerator6F00",
   "line": "down to unit 0"
  },
  "7D8A": {
   "unit": "game/mapGenerator6F00",
   "line": "all 100 slots handled"
  },
  "7D8B": {
   "unit": "game/mapGenerator6F00",
   "routine": [
    "setGameClockForType - looks the length of the game up in roundsByGameType ($78C3: 63, 63, 31,",
    "127, 127, 254, 254 rounds for types 0-6) and starts the game clock with it.",
    "In:  gameTypeOptions $0BA3 bits 0-2",
    "Out: gameClock $91CB = A = the number of rounds",
    "Called from: $7B1A, $7B69, $7B86 and $7B9E (which shifts A right twice to get the quarter length)"
   ],
   "line": "the game type and option bits"
  },
  "7D8E": {
   "unit": "game/mapGenerator6F00",
   "line": "bits 0-2 = type 0-6"
  },
  "7D90": {
   "unit": "game/mapGenerator6F00",
   "line": "index the length table"
  },
  "7D91": {
   "unit": "game/mapGenerator6F00",
   "line": "$78C3: 63, 63, 31, 127, 127, 254, 254 rounds"
  },
  "7D94": {
   "unit": "game/mapGenerator6F00",
   "line": "$91CB counts one down per round"
  },
  "7D97": {
   "unit": "game/mapGenerator6F00",
   "line": "A still holds the length for the caller"
  },
  "7D98": {
   "unit": "game/mapGenerator6F00",
   "routine": [
    "clearUnitsAndRecyclersFromMap - takes every unit 99..0 off the map (removeUnitFromMap puts the",
    "terrain that was hidden under it back) and then blanks both recycler cells, leaving a map that",
    "holds terrain only.  Falls into clearRecyclerCell for side 1.",
    "In:  the unit arrays, recyclerCol/recyclerRow $92A8/$92AA",
    "Out: the map at $F000-$F63F contains no units and no recyclers",
    "Called from: $7B7A, just before the film snapshot is written and the units are re-placed"
   ],
   "line": "start at unit 99"
  },
  "7D9A": {
   "unit": "game/mapGenerator6F00",
   "line": "restore the terrain under the unit and mark it as not on the map"
  },
  "7D9D": {
   "unit": "game/mapGenerator6F00",
   "line": "next lower unit"
  },
  "7D9E": {
   "unit": "game/mapGenerator6F00",
   "line": "down to unit 0"
  },
  "7DA0": {
   "unit": "game/mapGenerator6F00",
   "line": "side 0 ..."
  },
  "7DA2": {
   "unit": "game/mapGenerator6F00",
   "line": "... blank its recycler cell"
  },
  "7DA5": {
   "unit": "game/mapGenerator6F00",
   "line": "side 1: fall into the same code"
  },
  "7DA7": {
   "unit": "game/mapGenerator6F00",
   "routine": [
    "clearRecyclerCell - blanks side X's recycler cell: if that side has a recycler (recyclerCol bit",
    "7 clear) the map byte at (recyclerCol, recyclerRow) is set to the empty terrain code $40.",
    "In:  X = side 0/1, recyclerCol $92A8, recyclerRow $92AA",
    "Out: one map cell = $40, zp_48/zp_49 point at it",
    "Called from: $7DA2 and by fall-through from $7DA5"
   ],
   "line": "$92A8+side; bit 7 set means this side has no recycler placed"
  },
  "7DAA": {
   "unit": "game/mapGenerator6F00",
   "line": "no recycler: nothing to blank"
  },
  "7DAC": {
   "unit": "game/mapGenerator6F00",
   "line": "Y = recycler row"
  },
  "7DAF": {
   "unit": "game/mapGenerator6F00",
   "line": "X = recycler column"
  },
  "7DB0": {
   "unit": "game/mapGenerator6F00",
   "line": "zp_48/zp_49 = address of that map cell; Y comes back as 0"
  },
  "7DB3": {
   "unit": "game/mapGenerator6F00",
   "line": "$40 = empty terrain"
  },
  "7DB5": {
   "unit": "game/mapGenerator6F00",
   "line": "wipe the recycler out of the map"
  },
  "7DB7": {
   "unit": "game/mapGenerator6F00",
   "line": "done"
  },
  "7DB8": {
   "unit": "game/mapGenerator6F00",
   "routine": [
    "waitForSpaceKey - 'PRESS SPACE WHEN READY' wait used by the modem setup prompts.  Clears the",
    "keyboard and chat state and then loops servicing the link and stirring the game RNG until the",
    "space bar ($A0) turns up in lastTypedKey; afterwards it empties the message queue and locks the",
    "input out for half a second.  Stirring the RNG on every pass is what makes the map seed depend",
    "on how long the player took to press space.",
    "In:  lastTypedKey $9248 (written by the IRQ keyboard scan)",
    "Out: $9248 = $FF, chatState $929C = 0, incomingChatLength $9277 = 0, inputLockoutTimer $0B7D =",
    "     30 frames, message queue emptied, game RNG zp_57-zp_59 advanced",
    "Called from: showModemSetupPrompt ($0C9D, JMP), so this RTS returns to that routine's caller"
   ],
   "line": "$FF = no key pending"
  },
  "7DBA": {
   "unit": "game/mapGenerator6F00",
   "line": "throw away whatever was typed before"
  },
  "7DBD": {
   "unit": "game/mapGenerator6F00",
   "line": "zero for the three variables below"
  },
  "7DBF": {
   "unit": "game/mapGenerator6F00",
   "line": "leave chat entry mode"
  },
  "7DC2": {
   "unit": "game/mapGenerator6F00",
   "line": "no input lockout while we wait"
  },
  "7DC5": {
   "unit": "game/mapGenerator6F00",
   "line": "discard a half-received chat line"
  },
  "7DC8": {
   "unit": "game/mapGenerator6F00",
   "block": [
    "Wait loop: keep the link alive and stir the random generator until space is pressed."
   ],
   "line": "service the lock-step command exchange"
  },
  "7DCB": {
   "unit": "game/mapGenerator6F00",
   "line": "step the 24-bit game RNG - this is what randomises the map seed"
  },
  "7DCE": {
   "unit": "game/mapGenerator6F00",
   "line": "the last key the IRQ saw"
  },
  "7DD1": {
   "unit": "game/mapGenerator6F00",
   "line": "$A0 = the space bar"
  },
  "7DD3": {
   "unit": "game/mapGenerator6F00",
   "line": "keep waiting"
  },
  "7DD5": {
   "unit": "game/mapGenerator6F00",
   "line": "space pressed: empty the message queue and refresh the status line"
  },
  "7DD8": {
   "unit": "game/mapGenerator6F00",
   "line": "$1E = 30 frames"
  },
  "7DDA": {
   "unit": "game/mapGenerator6F00",
   "line": "ignore input for half a second so the key is not seen twice"
  },
  "7DDD": {
   "unit": "game/mapGenerator6F00",
   "line": "$FF = no key pending"
  },
  "7DDF": {
   "unit": "game/mapGenerator6F00",
   "line": "consume the space"
  },
  "7DE2": {
   "unit": "game/mapGenerator6F00",
   "line": "returns into showModemSetupPrompt's caller"
  },
  "7DE3": {
   "unit": "game/mapGenerator6F00",
   "routine": [
    "handleMainMenuChoice - acts on the item the player picked in the main menu.  0 COMPETE WITH",
    "MODEM OPPONENT and 1 PRACTICE WITH SOLO TRAINER set the opponent-module flag and start a game;",
    "2 WATCH GAME FILM needs a valid film in $D000 and starts the game in playback mode; 3 SAVE GAME",
    "FILM (also needs a valid film) and 4 LOAD GAME FILM ask for the data disk, load its film",
    "directory and run the save or load slot menu, then ask for the game disk again.",
    "In:  menuSelectedItem $91D5, isSoloTrainer $0BA5, the film buffer at $D000",
    "Out: $0BA5, $0B9B, defaultMainMenuItem $92FD = 2, filmDirectoryPage $7FA1 = 0, filmMenuMode",
    "     $7FA2; items 0-2 leave through startGameFromSetup ($0AC8)",
    "Called from: runMainMenu ($1C78, JMP)"
   ],
   "line": "the item the selector returned"
  },
  "7DE6": {
   "unit": "game/mapGenerator6F00",
   "line": "self-modifying: the item number becomes the operand of the LDA #$FF at $7E0E"
  },
  "7DE9": {
   "unit": "game/mapGenerator6F00",
   "line": "items 0-2 start a game"
  },
  "7DEB": {
   "unit": "game/mapGenerator6F00",
   "line": "handle those below"
  },
  "7DED": {
   "unit": "game/mapGenerator6F00",
   "line": "item 4 = LOAD GAME FILM: there is nothing in memory to check"
  },
  "7DEF": {
   "unit": "game/mapGenerator6F00",
   "line": "item 3 = SAVE GAME FILM: refuse unless $D000 holds a valid film"
  },
  "7DF2": {
   "unit": "game/mapGenerator6F00",
   "line": "no film: 'NOT A GAME FILM!' has been shown, give up"
  },
  "7DF4": {
   "unit": "game/mapGenerator6F00",
   "block": [
    "Save/load film: get the data disk in the drive and read its film directory."
   ],
   "line": "show 'INSERT DATA DISK AND PRESS SPACE.'"
  },
  "7DF7": {
   "unit": "game/mapGenerator6F00",
   "line": "pause the modem/trainer module - the fast loader runs with interrupts off"
  },
  "7DFA": {
   "unit": "game/mapGenerator6F00",
   "line": "identify the disk and read the 30-entry film directory to $0300"
  },
  "7DFD": {
   "unit": "game/mapGenerator6F00",
   "line": "keep the C flag across the resume"
  },
  "7DFE": {
   "unit": "game/mapGenerator6F00",
   "line": "restart the comm module and clear the status line"
  },
  "7E01": {
   "unit": "game/mapGenerator6F00",
   "line": "get the result back"
  },
  "7E02": {
   "unit": "game/mapGenerator6F00",
   "line": "no usable disk in the drive: the message has already been shown"
  },
  "7E04": {
   "unit": "game/mapGenerator6F00",
   "line": "item 2 = WATCH GAME FILM ..."
  },
  "7E06": {
   "unit": "game/mapGenerator6F00",
   "line": "... becomes the pre-selected main-menu item next time round"
  },
  "7E09": {
   "unit": "game/mapGenerator6F00",
   "line": "start on ..."
  },
  "7E0B": {
   "unit": "game/mapGenerator6F00",
   "line": "... the first directory page"
  },
  "7E0E": {
   "unit": "game/mapGenerator6F00",
   "line": "operand patched at $7DE6: A = the menu item (3 or 4)"
  },
  "7E10": {
   "unit": "game/mapGenerator6F00",
   "line": "prepare the subtraction"
  },
  "7E11": {
   "unit": "game/mapGenerator6F00",
   "line": "item 3 -> 0, item 4 -> 1"
  },
  "7E13": {
   "unit": "game/mapGenerator6F00",
   "line": "$7FA2: 0 = save film, 1 = load film"
  },
  "7E16": {
   "unit": "game/mapGenerator6F00",
   "line": "draw the ten slots of the current page"
  },
  "7E19": {
   "unit": "game/mapGenerator6F00",
   "line": "which of the two menus?"
  },
  "7E1C": {
   "unit": "game/mapGenerator6F00",
   "line": "1 = load"
  },
  "7E1E": {
   "unit": "game/mapGenerator6F00",
   "line": "SAVE: pick a slot, type a name and write the film to disk"
  },
  "7E21": {
   "unit": "game/mapGenerator6F00",
   "line": "then ask for the game disk again"
  },
  "7E24": {
   "unit": "game/mapGenerator6F00",
   "line": "LOAD: pick a slot and read the film into $D000"
  },
  "7E27": {
   "unit": "game/mapGenerator6F00",
   "line": "then ask for the game disk again"
  },
  "7E2A": {
   "unit": "game/mapGenerator6F00",
   "line": "nothing to do"
  },
  "7E2B": {
   "unit": "game/mapGenerator6F00",
   "block": [
    "Items 0-2: choose the opponent module / playback mode and start a game."
   ],
   "line": "item 2 = WATCH GAME FILM"
  },
  "7E2D": {
   "unit": "game/mapGenerator6F00",
   "line": "items 0 and 1 below"
  },
  "7E2F": {
   "unit": "game/mapGenerator6F00",
   "line": "a film can only be watched if one is loaded"
  },
  "7E32": {
   "unit": "game/mapGenerator6F00",
   "line": "invalid film: back to the menu"
  },
  "7E34": {
   "unit": "game/mapGenerator6F00",
   "line": "keep the current opponent module - a film can be watched either way"
  },
  "7E37": {
   "unit": "game/mapGenerator6F00",
   "line": "$0B9B = $82: bit 7 = playing a film back, bits 0-1 = 2 = show both sides"
  },
  "7E39": {
   "unit": "game/mapGenerator6F00",
   "routine": [
    "setModeFlagsAndStartGame - shared tail of handleMainMenuChoice: X becomes isSoloTrainer $0BA5",
    "($FF = solo trainer, 0 = modem opponent) and A becomes filmPlaybackMode $0B9B, then the game is",
    "started.",
    "In:  X = new $0BA5, A = new $0B9B",
    "Out: $0BA5, $0B9B; jumps to startGameFromSetup ($0AC8)",
    "Called from: $7E47 and $7E4D (branches), and by fall-through from $7E37"
   ],
   "line": "$0BA5: $FF = play the solo trainer, 0 = play a modem opponent"
  },
  "7E3C": {
   "unit": "game/mapGenerator6F00",
   "line": "$0B9B: bit 7 = watch a game film"
  },
  "7E3F": {
   "unit": "game/mapGenerator6F00",
   "line": "leave the menu and start the game"
  },
  "7E42": {
   "unit": "game/mapGenerator6F00",
   "line": "item 0 COMPETE WITH MODEM OPPONENT: no film ..."
  },
  "7E44": {
   "unit": "game/mapGenerator6F00",
   "line": "... and X = 0 = the modem driver"
  },
  "7E45": {
   "unit": "game/mapGenerator6F00",
   "line": "item 1 = PRACTICE WITH SOLO TRAINER?"
  },
  "7E47": {
   "unit": "game/mapGenerator6F00",
   "line": "no: start the modem game"
  },
  "7E49": {
   "unit": "game/mapGenerator6F00",
   "line": "solo trainer: no film either"
  },
  "7E4B": {
   "unit": "game/mapGenerator6F00",
   "line": "$FF selects the trainer module at $E000"
  },
  "7E4D": {
   "unit": "game/mapGenerator6F00",
   "line": "always taken (X is $FF)"
  },
  "7E4F": {
   "unit": "game/mapGenerator6F00",
   "routine": [
    "checkDiskAndLoadFilmDirectory - works out which disk is in the drive and loads its film",
    "directory.  The game disk ('OZ') is tried first and silently, because films may also be kept on",
    "it; otherwise a data disk ('EA') is required, and a data disk must additionally have every",
    "block allocated in its BAM ($0203-$028F all zero) so that CBM DOS can never overwrite the raw",
    "film tracks.  diskIdIndex $92F0 is left at 0 for the game disk and 2 for a data disk, which",
    "later decides whether the directory has one page or three.  Falls into loadFilmDirectory.",
    "In:  the disk in the drive",
    "Out: C=0 and the directory at $0300-$04FF, or C=1 with a message shown; $92F0, BAM at $0200",
    "Called from: $7DFA (handleMainMenuChoice)"
   ],
   "line": "X = 0: look for the 'OZ' game disk"
  },
  "7E51": {
   "unit": "game/mapGenerator6F00",
   "line": "bit 7 = fail silently, this is only a first guess"
  },
  "7E53": {
   "unit": "game/mapGenerator6F00",
   "line": "read the BAM and compare the disk id"
  },
  "7E56": {
   "unit": "game/mapGenerator6F00",
   "line": "the game disk can hold films too: use it"
  },
  "7E58": {
   "unit": "game/mapGenerator6F00",
   "line": "X = 2: look for the 'EA' data disk"
  },
  "7E5A": {
   "unit": "game/mapGenerator6F00",
   "line": "this time complain if it is not there"
  },
  "7E5C": {
   "unit": "game/mapGenerator6F00",
   "line": "read the BAM and compare the disk id"
  },
  "7E5F": {
   "unit": "game/mapGenerator6F00",
   "line": "neither disk: 'NOT A DATA DISK!' has been shown, return C=1"
  },
  "7E61": {
   "unit": "game/mapGenerator6F00",
   "line": "the BAM's per-track allocation entries start at $0203"
  },
  "7E63": {
   "unit": "game/mapGenerator6F00",
   "block": [
    "A real data disk has been fully allocated by the format utility, so every BAM entry must be",
    "zero (no free blocks anywhere).  Otherwise this is an ordinary CBM disk whose files would be",
    "destroyed by writing raw tracks."
   ],
   "line": "a byte of the BAM allocation map (the $0200 buffer, contourRuleTable's address)"
  },
  "7E66": {
   "unit": "game/mapGenerator6F00",
   "line": "any non-zero byte means free blocks exist: not a data disk"
  },
  "7E68": {
   "unit": "game/mapGenerator6F00",
   "line": "next BAM byte"
  },
  "7E69": {
   "unit": "game/mapGenerator6F00",
   "line": "$0203-$028F covers all 35 tracks"
  },
  "7E6B": {
   "unit": "game/mapGenerator6F00",
   "line": "keep checking"
  },
  "7E6D": {
   "unit": "game/mapGenerator6F00",
   "line": "the disk is usable: read its film directory"
  },
  "7E70": {
   "unit": "game/mapGenerator6F00",
   "line": "C=1: no usable disk"
  },
  "7E71": {
   "unit": "game/mapGenerator6F00",
   "line": "'NOT A DATA DISK!' ($0F81) and C=1"
  },
  "7E74": {
   "unit": "game/mapGenerator6F00",
   "routine": [
    "writeFilmDirectory - writes the 512-byte film directory (30 entries of 16 bytes plus slack)",
    "from $0300-$04FF back to track 18 sectors 2-3.  A write error ends in diskErrorExit.",
    "In:  the directory at $0300",
    "Out: track 18 sectors 2-3 rewritten, C=0",
    "Called from: $8164 (saveFilmToSlot), before the film itself is written"
   ],
   "line": "8 frames ..."
  },
  "7E76": {
   "unit": "game/mapGenerator6F00",
   "line": "... to let the drive settle after the previous access"
  },
  "7E79": {
   "unit": "game/mapGenerator6F00",
   "line": "destination low byte $00 ..."
  },
  "7E7B": {
   "unit": "game/mapGenerator6F00",
   "line": "... high byte $03: the buffer at $0300"
  },
  "7E7D": {
   "unit": "game/mapGenerator6F00",
   "line": "tell the fast loader where the data is"
  },
  "7E80": {
   "unit": "game/mapGenerator6F00",
   "line": "2 sectors ..."
  },
  "7E82": {
   "unit": "game/mapGenerator6F00",
   "line": "... starting at sector 2 ..."
  },
  "7E83": {
   "unit": "game/mapGenerator6F00",
   "line": "... of track 18, the directory track"
  },
  "7E85": {
   "unit": "game/mapGenerator6F00",
   "line": "the drive encrypts and writes both sectors"
  },
  "7E88": {
   "unit": "game/mapGenerator6F00",
   "line": "write failed"
  },
  "7E8A": {
   "unit": "game/mapGenerator6F00",
   "line": "directory saved"
  },
  "7E8B": {
   "unit": "game/mapGenerator6F00",
   "routine": [
    "loadFilmDirectory - reads track 18 sectors 2-3 (the 30 x 16-byte film names) into $0300-$04FF.",
    "A read error ends in diskErrorExit.  Tail of checkDiskAndLoadFilmDirectory.",
    "In:  -",
    "Out: $0300-$04FF = film directory, C=0",
    "Called from: $7E6D (JMP)"
   ],
   "line": "destination low byte $00 ..."
  },
  "7E8D": {
   "unit": "game/mapGenerator6F00",
   "line": "... high byte $03: the buffer at $0300"
  },
  "7E8F": {
   "unit": "game/mapGenerator6F00",
   "line": "tell the fast loader where to put the data"
  },
  "7E92": {
   "unit": "game/mapGenerator6F00",
   "line": "2 sectors ..."
  },
  "7E94": {
   "unit": "game/mapGenerator6F00",
   "line": "... from sector 2 ..."
  },
  "7E95": {
   "unit": "game/mapGenerator6F00",
   "line": "... of track 18"
  },
  "7E97": {
   "unit": "game/mapGenerator6F00",
   "line": "wait 8 frames, then read both sectors"
  },
  "7E9A": {
   "unit": "game/mapGenerator6F00",
   "line": "read failed"
  },
  "7E9C": {
   "unit": "game/mapGenerator6F00",
   "line": "directory loaded"
  },
  "7E9D": {
   "unit": "game/mapGenerator6F00",
   "line": "show 'DISK ERROR.' and return C=1"
  },
  "7EA0": {
   "unit": "game/mapGenerator6F00",
   "routine": [
    "drawFilmDirectoryPage - draws one page of the film menu: the centred title 'SAVE GAME FILM' or",
    "'LOAD GAME FILM', then ten entries printed as a right-aligned slot number, a space and the",
    "16-character film name, then ' (MORE)' when a three-page data disk is in the drive and finally",
    "' (EXIT)'.  Ends by setting up the menu selector: item count, window column 11 / width 18 (the",
    "name field, so that the highlight bar and the redraws leave the slot numbers alone), first row",
    "9 and the highlight EOR mask.",
    "In:  filmDirectoryPage $7FA1, filmMenuMode $7FA2, diskIdIndex $92F0, directory at $0300",
    "Out: the menu drawn; menuItemCount $91D4, menuSelectedItem $91D5 = 0, menuFirstRow $91D6 = 9,",
    "     zp_3B = 11, zp_3C = 18, zp_3D = $60, zp_48/zp_49 = entry pointer, $90F8 and $9220 counters",
    "Called from: $7E16 and $7FDA (nextFilmDirectoryPage, JMP)"
   ],
   "line": "clear the menu window (screen column 2, 36 wide, from row 7)"
  },
  "7EA3": {
   "unit": "game/mapGenerator6F00",
   "line": "'SAVE GAME FILM' at $CC66 ..."
  },
  "7EA5": {
   "unit": "game/mapGenerator6F00",
   "line": "... in the text overlay"
  },
  "7EA7": {
   "unit": "game/mapGenerator6F00",
   "line": "0 = save, 1 = load"
  },
  "7EAA": {
   "unit": "game/mapGenerator6F00",
   "line": "save: keep that title"
  },
  "7EAC": {
   "unit": "game/mapGenerator6F00",
   "line": "'LOAD GAME FILM' at $CC75 ..."
  },
  "7EAE": {
   "unit": "game/mapGenerator6F00",
   "line": "... in the text overlay"
  },
  "7EB0": {
   "unit": "game/mapGenerator6F00",
   "line": "centre the title in the 36-column menu window"
  },
  "7EB3": {
   "unit": "game/mapGenerator6F00",
   "line": "indent the list ..."
  },
  "7EB5": {
   "unit": "game/mapGenerator6F00",
   "line": "... to screen column 9"
  },
  "7EB7": {
   "unit": "game/mapGenerator6F00",
   "line": "down to the first list row (row 8)"
  },
  "7EBA": {
   "unit": "game/mapGenerator6F00",
   "line": "22 columns: 'nn ' plus a 16-character name"
  },
  "7EBC": {
   "unit": "game/mapGenerator6F00",
   "line": "also the wrap width used while printing"
  },
  "7EBE": {
   "unit": "game/mapGenerator6F00",
   "line": "dead store: setFilmDirectoryPagePtr overwrites $90F8 two instructions later"
  },
  "7EC0": {
   "unit": "game/mapGenerator6F00",
   "line": "(leftover initialisation of the slot number)"
  },
  "7EC3": {
   "unit": "game/mapGenerator6F00",
   "line": "zp_48/zp_49 = first entry of this page, $90F8 = the slot number it is called"
  },
  "7EC6": {
   "unit": "game/mapGenerator6F00",
   "line": "9 ..."
  },
  "7EC8": {
   "unit": "game/mapGenerator6F00",
   "line": "... = ten entries per page (the counter runs 9..0); $9220 is only the drone fuel during a battle"
  },
  "7ECB": {
   "unit": "game/mapGenerator6F00",
   "block": [
    "Entry loop: prints 'nn name' for the ten slots of this page, advancing zp_48/zp_49 by one",
    "16-byte directory entry each time."
   ],
   "line": "the slot number 1-30 printed in front of the name"
  },
  "7ECE": {
   "unit": "game/mapGenerator6F00",
   "line": "numbers below 10 are one digit ..."
  },
  "7ED0": {
   "unit": "game/mapGenerator6F00",
   "line": "... two digits need no padding"
  },
  "7ED2": {
   "unit": "game/mapGenerator6F00",
   "line": "$A0 = space"
  },
  "7ED4": {
   "unit": "game/mapGenerator6F00",
   "line": "pad one-digit numbers so all names start in the same column"
  },
  "7ED7": {
   "unit": "game/mapGenerator6F00",
   "line": "the slot number"
  },
  "7EDA": {
   "unit": "game/mapGenerator6F00",
   "line": "Y = middle byte of the 24-bit value printDecimal takes (the top byte zp_7E is left at 0)"
  },
  "7EDC": {
   "unit": "game/mapGenerator6F00",
   "line": "print it in decimal without leading zeros"
  },
  "7EDF": {
   "unit": "game/mapGenerator6F00",
   "line": "$A0 = space"
  },
  "7EE1": {
   "unit": "game/mapGenerator6F00",
   "line": "separate the number from the name"
  },
  "7EE4": {
   "unit": "game/mapGenerator6F00",
   "line": "pointer to this directory entry ..."
  },
  "7EE6": {
   "unit": "game/mapGenerator6F00",
   "line": "... low/high"
  },
  "7EE8": {
   "unit": "game/mapGenerator6F00",
   "line": "print the 16-character name (its last byte carries bit 7)"
  },
  "7EEB": {
   "unit": "game/mapGenerator6F00",
   "line": "next screen line"
  },
  "7EEE": {
   "unit": "game/mapGenerator6F00",
   "line": "advance the entry pointer ..."
  },
  "7EF0": {
   "unit": "game/mapGenerator6F00",
   "line": "clear carry for the 16-bit add"
  },
  "7EF1": {
   "unit": "game/mapGenerator6F00",
   "line": "... by $10 = one 16-byte directory entry"
  },
  "7EF3": {
   "unit": "game/mapGenerator6F00",
   "line": "store it back"
  },
  "7EF5": {
   "unit": "game/mapGenerator6F00",
   "line": "high byte"
  },
  "7EF7": {
   "unit": "game/mapGenerator6F00",
   "line": "carry only"
  },
  "7EF9": {
   "unit": "game/mapGenerator6F00",
   "line": "store it back"
  },
  "7EFB": {
   "unit": "game/mapGenerator6F00",
   "line": "next slot number"
  },
  "7EFE": {
   "unit": "game/mapGenerator6F00",
   "line": "one entry done"
  },
  "7F01": {
   "unit": "game/mapGenerator6F00",
   "line": "ten entries per page"
  },
  "7F03": {
   "unit": "game/mapGenerator6F00",
   "block": [
    "Trailing menu items and the selector setup."
   ],
   "line": "11 items ..."
  },
  "7F05": {
   "unit": "game/mapGenerator6F00",
   "line": "... = ten slots plus (EXIT)"
  },
  "7F08": {
   "unit": "game/mapGenerator6F00",
   "line": "0 = the game disk, 2 = a data disk"
  },
  "7F0B": {
   "unit": "game/mapGenerator6F00",
   "line": "the game disk holds only one page of ten slots"
  },
  "7F0D": {
   "unit": "game/mapGenerator6F00",
   "line": "a data disk has three pages, so there is a 12th item: (MORE)"
  },
  "7F10": {
   "unit": "game/mapGenerator6F00",
   "line": "three blank columns ..."
  },
  "7F12": {
   "unit": "game/mapGenerator6F00",
   "line": "... so that '(MORE)' lines up with the names ($C0EF is printFillChars in the text overlay)"
  },
  "7F15": {
   "unit": "game/mapGenerator6F00",
   "line": "' (MORE)' at $EF81 ..."
  },
  "7F17": {
   "unit": "game/mapGenerator6F00",
   "line": "... in the comm-module tail"
  },
  "7F19": {
   "unit": "game/mapGenerator6F00",
   "line": "print it (the string ends with a CR)"
  },
  "7F1C": {
   "unit": "game/mapGenerator6F00",
   "line": "three blank columns ..."
  },
  "7F1E": {
   "unit": "game/mapGenerator6F00",
   "line": "... for the last item too"
  },
  "7F21": {
   "unit": "game/mapGenerator6F00",
   "line": "' (EXIT)' at $CAB2 ..."
  },
  "7F23": {
   "unit": "game/mapGenerator6F00",
   "line": "... the tail of the formation-menu string block"
  },
  "7F25": {
   "unit": "game/mapGenerator6F00",
   "line": "print it"
  },
  "7F28": {
   "unit": "game/mapGenerator6F00",
   "line": "the selector window starts at screen column 11 ..."
  },
  "7F2A": {
   "unit": "game/mapGenerator6F00",
   "line": "... i.e. one column in front of the names"
  },
  "7F2C": {
   "unit": "game/mapGenerator6F00",
   "line": "18 characters wide ..."
  },
  "7F2E": {
   "unit": "game/mapGenerator6F00",
   "line": "... = the space plus the 16-character name, and the width of the highlight bar"
  },
  "7F30": {
   "unit": "game/mapGenerator6F00",
   "line": "the first slot sits on screen row 9 ..."
  },
  "7F32": {
   "unit": "game/mapGenerator6F00",
   "line": "... menuFirstRow, to which the item number is added"
  },
  "7F35": {
   "unit": "game/mapGenerator6F00",
   "line": "start the bar ..."
  },
  "7F37": {
   "unit": "game/mapGenerator6F00",
   "line": "... on the first slot"
  },
  "7F3A": {
   "unit": "game/mapGenerator6F00",
   "line": "$60 = the EOR mask that flips a row between the two text colours"
  },
  "7F3C": {
   "unit": "game/mapGenerator6F00",
   "line": "zp_3D is both the plot colour and the highlight mask"
  },
  "7F3E": {
   "unit": "game/mapGenerator6F00",
   "line": "the page is ready for the selector"
  },
  "7F3F": {
   "unit": "game/mapGenerator6F00",
   "routine": [
    "runLoadFilmMenu - LOAD GAME FILM.  Runs the standard menu selector over the directory page;",
    "(MORE) turns to the next page and starts again, (EXIT) leaves.  Picking a slot converts it to",
    "a disk track and reads that track's 16 sectors one at a time into $0200, copying each page into",
    "the film buffer $D000-$DFFF that lives under the I/O area, and finally validates the header.",
    "In:  menuSelectedItem $91D5, diskIdIndex $92F0, filmDirectoryPage $7FA1, the disk",
    "Out: $D000-$DFFF = the film, $91D5 = the track it came from, zp_18/zp_19, the copy pointers",
    "     $58C3/$58C4 (source) and $58C6/$58C7 (destination)",
    "Called from: $7E24 (handleMainMenuChoice) and $7F54 (itself, after a page turn)"
   ],
   "line": "C=1: ordinary menu behaviour (the bar pauses before it reaches the last row)"
  },
  "7F40": {
   "unit": "game/mapGenerator6F00",
   "line": "run the selector until fire or RETURN"
  },
  "7F43": {
   "unit": "game/mapGenerator6F00",
   "line": "the chosen item"
  },
  "7F46": {
   "unit": "game/mapGenerator6F00",
   "line": "items 0-9 are film slots"
  },
  "7F48": {
   "unit": "game/mapGenerator6F00",
   "line": "load that slot"
  },
  "7F4A": {
   "unit": "game/mapGenerator6F00",
   "line": "item 11 is always (EXIT)"
  },
  "7F4C": {
   "unit": "game/mapGenerator6F00",
   "line": "item 10 is (MORE) only when a data disk is in the drive"
  },
  "7F4F": {
   "unit": "game/mapGenerator6F00",
   "line": "on the game disk item 10 is (EXIT)"
  },
  "7F51": {
   "unit": "game/mapGenerator6F00",
   "line": "turn to the next page and redraw it"
  },
  "7F54": {
   "unit": "game/mapGenerator6F00",
   "line": "and run the selector again"
  },
  "7F57": {
   "unit": "game/mapGenerator6F00",
   "block": [
    "Load one film: 16 sectors of one track, each read into $0200 and then copied into the film",
    "buffer under the I/O area at $D000."
   ],
   "line": "pause the comm module for the disk access"
  },
  "7F5A": {
   "unit": "game/mapGenerator6F00",
   "line": "slot -> track number"
  },
  "7F5D": {
   "unit": "game/mapGenerator6F00",
   "line": "zp_19 = the track to read"
  },
  "7F5F": {
   "unit": "game/mapGenerator6F00",
   "line": "copyPageUnderIo destination low byte = $00 ..."
  },
  "7F61": {
   "unit": "game/mapGenerator6F00",
   "line": "... (operand of the STA at $58C5)"
  },
  "7F64": {
   "unit": "game/mapGenerator6F00",
   "line": "... high byte $D0: the film buffer under I/O"
  },
  "7F66": {
   "unit": "game/mapGenerator6F00",
   "line": "the destination page walks $D000..$DF00"
  },
  "7F69": {
   "unit": "game/mapGenerator6F00",
   "line": "copyPageUnderIo source low byte = $00 ..."
  },
  "7F6B": {
   "unit": "game/mapGenerator6F00",
   "line": "... (operand of the LDA at $58C2)"
  },
  "7F6E": {
   "unit": "game/mapGenerator6F00",
   "line": "... high byte $02: the sector buffer at $0200"
  },
  "7F70": {
   "unit": "game/mapGenerator6F00",
   "line": "the source stays at $0200 for every sector"
  },
  "7F73": {
   "unit": "game/mapGenerator6F00",
   "line": "start with ..."
  },
  "7F75": {
   "unit": "game/mapGenerator6F00",
   "line": "... sector 0 in zp_18"
  },
  "7F77": {
   "unit": "game/mapGenerator6F00",
   "line": "destination low byte $00 ..."
  },
  "7F79": {
   "unit": "game/mapGenerator6F00",
   "line": "... high byte $02: read the sector to $0200"
  },
  "7F7B": {
   "unit": "game/mapGenerator6F00",
   "line": "tell the fast loader where to put it"
  },
  "7F7E": {
   "unit": "game/mapGenerator6F00",
   "line": "one sector ..."
  },
  "7F80": {
   "unit": "game/mapGenerator6F00",
   "line": "... from the film's track ..."
  },
  "7F82": {
   "unit": "game/mapGenerator6F00",
   "line": "... at the current sector number"
  },
  "7F84": {
   "unit": "game/mapGenerator6F00",
   "line": "wait 8 frames, then read it"
  },
  "7F87": {
   "unit": "game/mapGenerator6F00",
   "line": "copy those 256 bytes into the film buffer with the I/O area banked out"
  },
  "7F8A": {
   "unit": "game/mapGenerator6F00",
   "line": "next destination page ($D000, $D100, ...)"
  },
  "7F8D": {
   "unit": "game/mapGenerator6F00",
   "line": "next sector"
  },
  "7F8F": {
   "unit": "game/mapGenerator6F00",
   "line": "sector counter"
  },
  "7F91": {
   "unit": "game/mapGenerator6F00",
   "line": "16 sectors = the whole 4 KB film buffer"
  },
  "7F93": {
   "unit": "game/mapGenerator6F00",
   "line": "keep reading"
  },
  "7F95": {
   "unit": "game/mapGenerator6F00",
   "line": "restart the comm module and clear the status line"
  },
  "7F98": {
   "unit": "game/mapGenerator6F00",
   "line": "does the loaded data have a valid film header?"
  },
  "7F9B": {
   "unit": "game/mapGenerator6F00",
   "line": "valid"
  },
  "7F9D": {
   "unit": "game/mapGenerator6F00",
   "line": "invalid: checkFilmInMemory has already shown 'NOT A GAME FILM!'"
  },
  "7F9E": {
   "unit": "game/mapGenerator6F00",
   "line": "valid: clear the message queue and return"
  },
  "7FA1": {
   "unit": "game/mapGenerator6F00",
   "block": [
    "filmDirectoryPage - which of the three directory pages is on screen (0-2).  A game disk only",
    "ever shows page 0; a data disk holds 30 films in three pages of ten."
   ],
   "line": "current page 0-2"
  },
  "7FA2": {
   "unit": "game/mapGenerator6F00",
   "block": [
    "filmMenuMode - 0 = SAVE GAME FILM, 1 = LOAD GAME FILM (set from the main-menu item - 3)."
   ],
   "line": "0 = save, 1 = load"
  },
  "7FA3": {
   "unit": "game/mapGenerator6F00",
   "block": [
    "filmPageFirstSlotTable - the slot number printed for the first entry of each page: page 0",
    "starts at 1, page 1 at 11 and page 2 at 21, so the 30 slots are numbered 1-30 on screen."
   ],
   "line": "first slot number of pages 0, 1 and 2"
  },
  "7FA6": {
   "unit": "game/mapGenerator6F00",
   "routine": [
    "setFilmDirectoryPagePtr - points zp_48/zp_49 at the first directory entry of the current page",
    "($0300 + page * $A0, ten entries of 16 bytes per page) and puts the slot number that entry is",
    "shown as (1, 11 or 21) into $90F8 for the numbering loop.",
    "In:  filmDirectoryPage $7FA1, filmPageFirstSlotTable $7FA3",
    "Out: zp_48/zp_49 = entry pointer, $90F8 = first slot number",
    "Called from: $7EC3 (drawFilmDirectoryPage) and $7FDD (runSaveFilmMenu)"
   ],
   "line": "low byte of $0300 ..."
  },
  "7FA8": {
   "unit": "game/mapGenerator6F00",
   "line": "... the directory buffer"
  },
  "7FAA": {
   "unit": "game/mapGenerator6F00",
   "line": "high byte $03 ..."
  },
  "7FAC": {
   "unit": "game/mapGenerator6F00",
   "line": "... zp_48/zp_49 = $0300"
  },
  "7FAE": {
   "unit": "game/mapGenerator6F00",
   "line": "current page 0-2"
  },
  "7FB1": {
   "unit": "game/mapGenerator6F00",
   "line": "1, 11 or 21"
  },
  "7FB4": {
   "unit": "game/mapGenerator6F00",
   "line": "$90F8 = the slot number of the first entry on this page"
  },
  "7FB7": {
   "unit": "game/mapGenerator6F00",
   "line": "page number into A"
  },
  "7FB8": {
   "unit": "game/mapGenerator6F00",
   "line": "page 0 needs no offset"
  },
  "7FBA": {
   "unit": "game/mapGenerator6F00",
   "line": "add one page ..."
  },
  "7FBC": {
   "unit": "game/mapGenerator6F00",
   "line": "clear carry for the 16-bit add"
  },
  "7FBD": {
   "unit": "game/mapGenerator6F00",
   "line": "... $A0 = 10 entries of 16 bytes"
  },
  "7FBF": {
   "unit": "game/mapGenerator6F00",
   "line": "store it back"
  },
  "7FC1": {
   "unit": "game/mapGenerator6F00",
   "line": "high byte"
  },
  "7FC3": {
   "unit": "game/mapGenerator6F00",
   "line": "carry only"
  },
  "7FC5": {
   "unit": "game/mapGenerator6F00",
   "line": "store it back"
  },
  "7FC7": {
   "unit": "game/mapGenerator6F00",
   "line": "one page done"
  },
  "7FC8": {
   "unit": "game/mapGenerator6F00",
   "line": "repeat for each page skipped"
  },
  "7FCA": {
   "unit": "game/mapGenerator6F00",
   "line": "zp_48/zp_49 now points at entry 0 of the page"
  },
  "7FCB": {
   "unit": "game/mapGenerator6F00",
   "routine": [
    "nextFilmDirectoryPage - steps filmDirectoryPage on modulo 3 and redraws the list.",
    "In:  filmDirectoryPage $7FA1",
    "Out: $7FA1 advanced (wrapping 2 -> 0); jumps to drawFilmDirectoryPage",
    "Called from: $7F51 (load menu) and $80CC (save menu), both on the (MORE) item"
   ],
   "line": "step to the next page"
  },
  "7FCE": {
   "unit": "game/mapGenerator6F00",
   "line": "read it back (the D_7FCE label only exists because the comcen-screens overlay has a table here)"
  },
  "7FD1": {
   "unit": "game/mapGenerator6F00",
   "line": "three pages of ten slots"
  },
  "7FD3": {
   "unit": "game/mapGenerator6F00",
   "line": "still in range"
  },
  "7FD5": {
   "unit": "game/mapGenerator6F00",
   "line": "wrap ..."
  },
  "7FD7": {
   "unit": "game/mapGenerator6F00",
   "line": "... back to page 0"
  },
  "7FDA": {
   "unit": "game/mapGenerator6F00",
   "line": "redraw the ten entries of the new page"
  },
  "7FDD": {
   "unit": "game/mapGenerator6F00",
   "routine": [
    "runSaveFilmMenu - SAVE GAME FILM.  A private copy of the generic menu selector",
    "(runMenuLoopWatchUnit $5FDC) with a text field built into it: while a slot 0-9 is highlighted",
    "every key goes to the line editor handleTextEntryKey (at most 16 characters into the buffer",
    "$9278) and the typed text is echoed on that row with a block cursor behind it.  Moving the bar",
    "up or down throws the typed text away; fire or RETURN accepts.  (MORE) turns the page, (EXIT)",
    "leaves, and a slot with typed text gets the name copied into its 16-byte directory entry",
    "(padded with spaces, bit 7 on the last byte) before saveFilmToSlot writes directory and film.",
    "In:  menuItemCount $91D4, menuSelectedItem $91D5, menuFirstRow $91D6, filmDirectoryPage $7FA1,",
    "     the keyboard through $90EA, the joystick zp_5D, the frame timers zp_6C and zp_6E",
    "Out: Y = $91D5 (after a save that is the disk track, not the slot), filmNameLength $92E6 = 0,",
    "     $92C5, the typed name in $9278 and in the directory entry, the film on disk;",
    "     zp_4A/zp_4B = pointer to the selected directory entry; self-modifies $809B+1 and $809F+1",
    "Called from: $7E1E (handleMainMenuChoice) and $80CF (itself, after a page turn)"
   ],
   "line": "zp_48/zp_49 = the first entry of this page"
  },
  "7FE0": {
   "unit": "game/mapGenerator6F00",
   "line": "number of menu items"
  },
  "7FE3": {
   "unit": "game/mapGenerator6F00",
   "line": "count - 1 ..."
  },
  "7FE4": {
   "unit": "game/mapGenerator6F00",
   "line": "... - 1 = the row the bar has to slow down in front of"
  },
  "7FE5": {
   "unit": "game/mapGenerator6F00",
   "line": "self-modifying: that row becomes the operand of the CMP #$FF at $809B"
  },
  "7FE8": {
   "unit": "game/mapGenerator6F00",
   "line": "$14 = 20 frames ..."
  },
  "7FEA": {
   "unit": "game/mapGenerator6F00",
   "line": "... of input lockout before the stick is read"
  },
  "7FEC": {
   "unit": "game/mapGenerator6F00",
   "block": [
    "Entered again after every move of the bar: clear the typed text, work out the address of the",
    "selected directory entry and draw the row highlighted."
   ],
   "line": "start with ..."
  },
  "7FEE": {
   "unit": "game/mapGenerator6F00",
   "line": "... an empty name ..."
  },
  "7FF1": {
   "unit": "game/mapGenerator6F00",
   "line": "... the row highlighted ($92C5 = 0) ..."
  },
  "7FF4": {
   "unit": "game/mapGenerator6F00",
   "line": "... and the high byte of the entry pointer cleared"
  },
  "7FF6": {
   "unit": "game/mapGenerator6F00",
   "line": "the selected item"
  },
  "7FF9": {
   "unit": "game/mapGenerator6F00",
   "line": "items 0-9 are slots ..."
  },
  "7FFB": {
   "unit": "game/mapGenerator6F00",
   "line": "... (MORE) and (EXIT) have no directory entry"
  },
  "7FFD": {
   "unit": "game/mapGenerator6F00",
   "line": "shift four times ..."
  },
  "7FFF": {
   "unit": "game/mapGenerator6F00",
   "line": "... to multiply the item number by 16 ..."
  },
  "8000": {
   "unit": "game/mapGenerator6F00",
   "line": "... into A (low) and zp_4B (high)"
  },
  "8002": {
   "unit": "game/mapGenerator6F00",
   "line": "four shifts"
  },
  "8003": {
   "unit": "game/mapGenerator6F00",
   "line": "keep shifting"
  },
  "8005": {
   "unit": "game/mapGenerator6F00",
   "line": "add the page pointer ..."
  },
  "8006": {
   "unit": "game/mapGenerator6F00",
   "line": "... low byte"
  },
  "8008": {
   "unit": "game/mapGenerator6F00",
   "line": "zp_4A = address of the entry"
  },
  "800A": {
   "unit": "game/mapGenerator6F00",
   "line": "high byte ..."
  },
  "800C": {
   "unit": "game/mapGenerator6F00",
   "line": "... plus the carry"
  },
  "800E": {
   "unit": "game/mapGenerator6F00",
   "line": "zp_4B = high byte of the entry pointer"
  },
  "8010": {
   "unit": "game/mapGenerator6F00",
   "line": "colour $01 = the highlighted row (black on white)"
  },
  "8012": {
   "unit": "game/mapGenerator6F00",
   "line": "draw the row (name, or (MORE)/(EXIT))"
  },
  "8015": {
   "unit": "game/mapGenerator6F00",
   "line": "$9E = the line-editor terminator ..."
  },
  "8017": {
   "unit": "game/mapGenerator6F00",
   "line": "... so the typed-name buffer starts out empty"
  },
  "801A": {
   "unit": "game/mapGenerator6F00",
   "block": [
    "Input loop: one pass per frame.  Keys go into the name field, the highlight blinks every 20",
    "frames and the joystick moves the bar or confirms the choice."
   ],
   "line": "keep the lock-step command exchange running while the player types"
  },
  "801D": {
   "unit": "game/mapGenerator6F00",
   "line": "the selected item"
  },
  "8020": {
   "unit": "game/mapGenerator6F00",
   "line": "only slots 0-9 have a name to type"
  },
  "8022": {
   "unit": "game/mapGenerator6F00",
   "line": "(MORE)/(EXIT): skip the text field"
  },
  "8024": {
   "unit": "game/mapGenerator6F00",
   "line": "Y = current cursor position in the name buffer"
  },
  "8027": {
   "unit": "game/mapGenerator6F00",
   "line": "A = 16 = maximum length, bit 7 clear = beep instead of scrolling when full"
  },
  "8029": {
   "unit": "game/mapGenerator6F00",
   "line": "let the shared line editor handle one key press"
  },
  "802C": {
   "unit": "game/mapGenerator6F00",
   "line": "nothing was accepted this frame"
  },
  "802E": {
   "unit": "game/mapGenerator6F00",
   "line": "Y comes back with bit 7 set when RETURN was pressed"
  },
  "802F": {
   "unit": "game/mapGenerator6F00",
   "line": "an ordinary character: echo the line"
  },
  "8031": {
   "unit": "game/mapGenerator6F00",
   "line": "RETURN accepts the entry just like the fire button"
  },
  "8034": {
   "unit": "game/mapGenerator6F00",
   "line": "remember the new cursor position"
  },
  "8037": {
   "unit": "game/mapGenerator6F00",
   "line": "colour $01 = the highlighted row ..."
  },
  "8039": {
   "unit": "game/mapGenerator6F00",
   "line": "... but $92C5 tells which half of the blink we are in"
  },
  "803C": {
   "unit": "game/mapGenerator6F00",
   "line": "row currently highlighted: keep $01"
  },
  "803E": {
   "unit": "game/mapGenerator6F00",
   "line": "$61 = the un-highlighted colour ($01 EOR the $60 mask)"
  },
  "8040": {
   "unit": "game/mapGenerator6F00",
   "line": "the colour the text engine writes for each cell"
  },
  "8042": {
   "unit": "game/mapGenerator6F00",
   "line": "back to ..."
  },
  "8044": {
   "unit": "game/mapGenerator6F00",
   "line": "... column 0 of the name field (screen column 11)"
  },
  "8046": {
   "unit": "game/mapGenerator6F00",
   "line": "recompute the bitmap and colour pointers for that cell"
  },
  "8049": {
   "unit": "game/mapGenerator6F00",
   "line": "erase the rest of the row"
  },
  "804C": {
   "unit": "game/mapGenerator6F00",
   "line": "$A0 = space"
  },
  "804E": {
   "unit": "game/mapGenerator6F00",
   "line": "one blank column in front of the name"
  },
  "8051": {
   "unit": "game/mapGenerator6F00",
   "line": "the typed-name buffer $9278 ..."
  },
  "8053": {
   "unit": "game/mapGenerator6F00",
   "line": "... low/high"
  },
  "8055": {
   "unit": "game/mapGenerator6F00",
   "line": "print what has been typed so far"
  },
  "8058": {
   "unit": "game/mapGenerator6F00",
   "line": "the cell the cursor now sits in"
  },
  "805A": {
   "unit": "game/mapGenerator6F00",
   "line": "$11 = both colour nibbles white = a solid block"
  },
  "805C": {
   "unit": "game/mapGenerator6F00",
   "line": "draw the block cursor behind the text"
  },
  "805E": {
   "unit": "game/mapGenerator6F00",
   "line": "blink timer, counted down by the IRQ"
  },
  "8060": {
   "unit": "game/mapGenerator6F00",
   "line": "not time yet"
  },
  "8062": {
   "unit": "game/mapGenerator6F00",
   "line": "$60 = the EOR mask ..."
  },
  "8064": {
   "unit": "game/mapGenerator6F00",
   "line": "... used as the toggle colour"
  },
  "8066": {
   "unit": "game/mapGenerator6F00",
   "line": "flip the row between $01 and $61 and restart the 20-frame timer"
  },
  "8069": {
   "unit": "game/mapGenerator6F00",
   "line": "the joystick direction latched by the IRQ (active low)"
  },
  "806B": {
   "unit": "game/mapGenerator6F00",
   "line": "$0F = centred"
  },
  "806D": {
   "unit": "game/mapGenerator6F00",
   "line": "the stick is being pushed: do not test fire"
  },
  "806F": {
   "unit": "game/mapGenerator6F00",
   "line": "edge-detect a new fire-button press"
  },
  "8072": {
   "unit": "game/mapGenerator6F00",
   "line": "fire pressed: accept the entry"
  },
  "8074": {
   "unit": "game/mapGenerator6F00",
   "line": "auto-repeat delay still running?"
  },
  "8076": {
   "unit": "game/mapGenerator6F00",
   "line": "yes: ignore the stick this frame"
  },
  "8078": {
   "unit": "game/mapGenerator6F00",
   "line": "the joystick nibble again"
  },
  "807A": {
   "unit": "game/mapGenerator6F00",
   "line": "mask the left/right bits out, only up and down move the bar"
  },
  "807C": {
   "unit": "game/mapGenerator6F00",
   "line": "-1 = move up"
  },
  "807E": {
   "unit": "game/mapGenerator6F00",
   "line": "$0E = up (bit 0 low)"
  },
  "8080": {
   "unit": "game/mapGenerator6F00",
   "line": "move up"
  },
  "8082": {
   "unit": "game/mapGenerator6F00",
   "line": "+1 = move down"
  },
  "8084": {
   "unit": "game/mapGenerator6F00",
   "line": "$0D = down (bit 1 low)"
  },
  "8086": {
   "unit": "game/mapGenerator6F00",
   "line": "neither: nothing to do"
  },
  "8088": {
   "unit": "game/mapGenerator6F00",
   "line": "A = the step, -1 or +1"
  },
  "8089": {
   "unit": "game/mapGenerator6F00",
   "line": "self-modifying: the direction becomes the operand of the LDY #$FF at $809F"
  },
  "808C": {
   "unit": "game/mapGenerator6F00",
   "line": "prepare the add"
  },
  "808D": {
   "unit": "game/mapGenerator6F00",
   "line": "new selection = old selection + step"
  },
  "8090": {
   "unit": "game/mapGenerator6F00",
   "line": "start the auto-repeat delay (16 frames first, then 6)"
  },
  "8093": {
   "unit": "game/mapGenerator6F00",
   "line": "past the last item?"
  },
  "8096": {
   "unit": "game/mapGenerator6F00",
   "line": "no: take the new selection"
  },
  "8098": {
   "unit": "game/mapGenerator6F00",
   "line": "out of range: leave the bar where it is"
  },
  "809B": {
   "unit": "game/mapGenerator6F00",
   "line": "operand patched at $7FE5: the row in front of the last one"
  },
  "809D": {
   "unit": "game/mapGenerator6F00",
   "line": "an ordinary row: take it"
  },
  "809F": {
   "unit": "game/mapGenerator6F00",
   "line": "operand patched at $8089: the direction we are moving in"
  },
  "80A1": {
   "unit": "game/mapGenerator6F00",
   "line": "moving up: no extra delay"
  },
  "80A3": {
   "unit": "game/mapGenerator6F00",
   "line": "moving down into the last-but-one row: pause ..."
  },
  "80A5": {
   "unit": "game/mapGenerator6F00",
   "line": "... 30 frames, so the bar does not run into (MORE)/(EXIT) by accident"
  },
  "80A7": {
   "unit": "game/mapGenerator6F00",
   "line": "hold the new selection"
  },
  "80A8": {
   "unit": "game/mapGenerator6F00",
   "line": "$61 = the un-highlighted colour"
  },
  "80AA": {
   "unit": "game/mapGenerator6F00",
   "line": "redraw the row we are leaving (this also wipes the typed text)"
  },
  "80AD": {
   "unit": "game/mapGenerator6F00",
   "line": "the new selection"
  },
  "80AE": {
   "unit": "game/mapGenerator6F00",
   "line": "make it current"
  },
  "80B1": {
   "unit": "game/mapGenerator6F00",
   "line": "sound 2 ..."
  },
  "80B3": {
   "unit": "game/mapGenerator6F00",
   "line": "... the menu click"
  },
  "80B6": {
   "unit": "game/mapGenerator6F00",
   "line": "start again with an empty name field on the new row"
  },
  "80B9": {
   "unit": "game/mapGenerator6F00",
   "block": [
    "The choice has been accepted with fire or RETURN: turn the page, leave, or write the film into",
    "the selected slot."
   ],
   "line": "8 frames ..."
  },
  "80BB": {
   "unit": "game/mapGenerator6F00",
   "line": "... so the button press is not seen twice"
  },
  "80BE": {
   "unit": "game/mapGenerator6F00",
   "line": "the accepted item"
  },
  "80C1": {
   "unit": "game/mapGenerator6F00",
   "line": "items 0-9 are film slots"
  },
  "80C3": {
   "unit": "game/mapGenerator6F00",
   "line": "save into that slot"
  },
  "80C5": {
   "unit": "game/mapGenerator6F00",
   "line": "item 11 is always (EXIT)"
  },
  "80C7": {
   "unit": "game/mapGenerator6F00",
   "line": "0 = game disk: item 10 is (EXIT) too"
  },
  "80CA": {
   "unit": "game/mapGenerator6F00",
   "line": "leave the menu"
  },
  "80CC": {
   "unit": "game/mapGenerator6F00",
   "line": "(MORE): turn to the next page ..."
  },
  "80CF": {
   "unit": "game/mapGenerator6F00",
   "line": "... and run the selector again"
  },
  "80D2": {
   "unit": "game/mapGenerator6F00",
   "line": "how many characters were typed?"
  },
  "80D5": {
   "unit": "game/mapGenerator6F00",
   "line": "nothing typed: keep the old name"
  },
  "80D7": {
   "unit": "game/mapGenerator6F00",
   "line": "mask the RETURN bit off (it cannot be set here - defensive)"
  },
  "80D9": {
   "unit": "game/mapGenerator6F00",
   "line": "zp_18 = length of the typed name"
  },
  "80DB": {
   "unit": "game/mapGenerator6F00",
   "line": "start at the last byte of the 16-byte entry"
  },
  "80DD": {
   "unit": "game/mapGenerator6F00",
   "line": "$A0 = space"
  },
  "80DF": {
   "unit": "game/mapGenerator6F00",
   "line": "always taken - enter the loop at the test"
  },
  "80E1": {
   "unit": "game/mapGenerator6F00",
   "line": "pad this position with a space"
  },
  "80E3": {
   "unit": "game/mapGenerator6F00",
   "line": "next lower position"
  },
  "80E4": {
   "unit": "game/mapGenerator6F00",
   "line": "pad everything from the end down to the typed length"
  },
  "80E6": {
   "unit": "game/mapGenerator6F00",
   "line": "keep padding"
  },
  "80E8": {
   "unit": "game/mapGenerator6F00",
   "line": "Y = length - 1"
  },
  "80E9": {
   "unit": "game/mapGenerator6F00",
   "line": "nothing typed at all"
  },
  "80EB": {
   "unit": "game/mapGenerator6F00",
   "line": "a character of the typed name"
  },
  "80EE": {
   "unit": "game/mapGenerator6F00",
   "line": "into the directory entry"
  },
  "80F0": {
   "unit": "game/mapGenerator6F00",
   "line": "next lower character"
  },
  "80F1": {
   "unit": "game/mapGenerator6F00",
   "line": "copy the whole name"
  },
  "80F3": {
   "unit": "game/mapGenerator6F00",
   "line": "the last byte of the entry ..."
  },
  "80F5": {
   "unit": "game/mapGenerator6F00",
   "line": "... gets ..."
  },
  "80F7": {
   "unit": "game/mapGenerator6F00",
   "line": "... bit 7 set: that is the string terminator"
  },
  "80F9": {
   "unit": "game/mapGenerator6F00",
   "line": "store it back"
  },
  "80FB": {
   "unit": "game/mapGenerator6F00",
   "line": "colour $01 = highlighted"
  },
  "80FD": {
   "unit": "game/mapGenerator6F00",
   "line": "redraw the row from the directory entry, so the saved name is shown"
  },
  "8100": {
   "unit": "game/mapGenerator6F00",
   "line": "write the directory and the 16 sectors of the film to disk"
  },
  "8103": {
   "unit": "game/mapGenerator6F00",
   "line": "clear ..."
  },
  "8105": {
   "unit": "game/mapGenerator6F00",
   "line": "... the typed length"
  },
  "8108": {
   "unit": "game/mapGenerator6F00",
   "line": "Y = the selection ($91D5, which filmSlotToTrack has replaced with the track)"
  },
  "810B": {
   "unit": "game/mapGenerator6F00",
   "line": "back to the main menu"
  },
  "810C": {
   "unit": "game/mapGenerator6F00",
   "routine": [
    "printFilmSlotEntry - redraws the currently selected row of the film menu in colour A: moves the",
    "cursor to menuFirstRow + menuSelectedItem, column 0 of the name window (screen column 11),",
    "clears the rest of the line and prints a space followed by the entry text - the 16-byte name",
    "at zp_4A/zp_4B for slots 0-9, ' (MORE)' for item 10 on a data disk, ' (EXIT)' otherwise.",
    "The slot number printed by drawFilmDirectoryPage sits in columns 9-10 and is left alone.",
    "In:  A = colour byte, menuSelectedItem $91D5, menuFirstRow $91D6, diskIdIndex $92F0, zp_4A/zp_4B",
    "Out: one menu row redrawn; zp_3D = A, zp_3F = 0",
    "Called from: $8012, $80AA and $80FD (all in runSaveFilmMenu)"
   ],
   "line": "the colour the text engine writes for each cell ($01 highlighted, $61 normal)"
  },
  "810E": {
   "unit": "game/mapGenerator6F00",
   "line": "the selected item"
  },
  "8111": {
   "unit": "game/mapGenerator6F00",
   "line": "clear carry for the add"
  },
  "8112": {
   "unit": "game/mapGenerator6F00",
   "line": "+ the first menu row = the screen row of this entry"
  },
  "8115": {
   "unit": "game/mapGenerator6F00",
   "line": "column 0 ..."
  },
  "8117": {
   "unit": "game/mapGenerator6F00",
   "line": "... of the name window (screen column 11)"
  },
  "8119": {
   "unit": "game/mapGenerator6F00",
   "line": "move the cursor there and recompute the cell pointers"
  },
  "811C": {
   "unit": "game/mapGenerator6F00",
   "line": "erase the old contents of the row"
  },
  "811F": {
   "unit": "game/mapGenerator6F00",
   "line": "$A0 = space"
  },
  "8121": {
   "unit": "game/mapGenerator6F00",
   "line": "one blank column in front of the name"
  },
  "8124": {
   "unit": "game/mapGenerator6F00",
   "line": "' (EXIT)' at $CAB2 ..."
  },
  "8126": {
   "unit": "game/mapGenerator6F00",
   "line": "... the default text for the last item"
  },
  "8128": {
   "unit": "game/mapGenerator6F00",
   "line": "the selected item"
  },
  "812B": {
   "unit": "game/mapGenerator6F00",
   "line": "items 0-9 are film slots"
  },
  "812D": {
   "unit": "game/mapGenerator6F00",
   "line": "print the name of the entry"
  },
  "812F": {
   "unit": "game/mapGenerator6F00",
   "line": "item 11 is always (EXIT)"
  },
  "8131": {
   "unit": "game/mapGenerator6F00",
   "line": "0 = game disk"
  },
  "8134": {
   "unit": "game/mapGenerator6F00",
   "line": "on the game disk item 10 is (EXIT) as well"
  },
  "8136": {
   "unit": "game/mapGenerator6F00",
   "line": "' (MORE)' at $EF81 ..."
  },
  "8138": {
   "unit": "game/mapGenerator6F00",
   "line": "... in the comm-module tail"
  },
  "813A": {
   "unit": "game/mapGenerator6F00",
   "line": "always taken"
  },
  "813C": {
   "unit": "game/mapGenerator6F00",
   "line": "pointer to the 16-byte directory entry ..."
  },
  "813E": {
   "unit": "game/mapGenerator6F00",
   "line": "... low/high"
  },
  "8140": {
   "unit": "game/mapGenerator6F00",
   "line": "print it and return through printString"
  },
  "8143": {
   "unit": "game/mapGenerator6F00",
   "routine": [
    "filmSlotToTrack - turns the selected directory slot into the disk track its film occupies:",
    "page * 10 + item + 2, plus one more when the result would be the directory track 18.  Slots",
    "0-29 therefore live on tracks 2-17 and 19-32, sixteen sectors each.  The track replaces",
    "menuSelectedItem $91D5 and is also returned in A.",
    "In:  filmDirectoryPage $7FA1, menuSelectedItem $91D5",
    "Out: A = $91D5 = track number 2-32",
    "Called from: $7F5A (load) and $8161 (save)"
   ],
   "line": "the page the slot is on"
  },
  "8146": {
   "unit": "game/mapGenerator6F00",
   "line": "page 0: no offset"
  },
  "8148": {
   "unit": "game/mapGenerator6F00",
   "line": "ten slots ..."
  },
  "814A": {
   "unit": "game/mapGenerator6F00",
   "line": "... per page; A comes back holding the low byte of page * 10"
  },
  "814D": {
   "unit": "game/mapGenerator6F00",
   "line": "add ..."
  },
  "814E": {
   "unit": "game/mapGenerator6F00",
   "line": "... the item number 0-9 ..."
  },
  "8151": {
   "unit": "game/mapGenerator6F00",
   "line": "... and 2, because tracks 0 and 1 are not used for films"
  },
  "8153": {
   "unit": "game/mapGenerator6F00",
   "line": "$12 = 18 = the directory track"
  },
  "8155": {
   "unit": "game/mapGenerator6F00",
   "line": "below it: use the track as it is"
  },
  "8157": {
   "unit": "game/mapGenerator6F00",
   "line": "at or above track 18 ..."
  },
  "8158": {
   "unit": "game/mapGenerator6F00",
   "line": "... skip the directory track"
  },
  "815A": {
   "unit": "game/mapGenerator6F00",
   "line": "the selection now holds the track number"
  },
  "815D": {
   "unit": "game/mapGenerator6F00",
   "line": "A = the track"
  },
  "815E": {
   "unit": "game/mapGenerator6F00",
   "routine": [
    "saveFilmToSlot - writes the film in the buffer under I/O to the disk: rewrites the directory",
    "(with the new name in it) and then copies the 16 pages of $D000-$DFFF one at a time into the",
    "$0200 sector buffer and writes each as one sector 0-15 of the film's track.",
    "In:  menuSelectedItem $91D5 = slot, the film at $D000-$DFFF, the directory at $0300",
    "Out: track sectors 0-15 written, $91D5 = the track used, zp_18/zp_19, the copy pointers",
    "     $58C3/$58C4 (source) and $58C6/$58C7 (destination)",
    "Called from: $8100 (runSaveFilmMenu)"
   ],
   "line": "pause the comm module for the disk access"
  },
  "8161": {
   "unit": "game/mapGenerator6F00",
   "line": "slot -> track number"
  },
  "8164": {
   "unit": "game/mapGenerator6F00",
   "line": "write the 2 directory sectors first, so the name is on disk even if the film write fails"
  },
  "8167": {
   "unit": "game/mapGenerator6F00",
   "line": "copyPageUnderIo source low byte = $00 ..."
  },
  "8169": {
   "unit": "game/mapGenerator6F00",
   "line": "... (operand of the LDA at $58C2)"
  },
  "816C": {
   "unit": "game/mapGenerator6F00",
   "line": "... high byte $D0: the film buffer under I/O"
  },
  "816E": {
   "unit": "game/mapGenerator6F00",
   "line": "the source page walks $D000..$DF00"
  },
  "8171": {
   "unit": "game/mapGenerator6F00",
   "line": "copyPageUnderIo destination low byte = $00 ..."
  },
  "8173": {
   "unit": "game/mapGenerator6F00",
   "line": "... (operand of the STA at $58C5)"
  },
  "8176": {
   "unit": "game/mapGenerator6F00",
   "line": "... high byte $02: the sector buffer"
  },
  "8178": {
   "unit": "game/mapGenerator6F00",
   "line": "the destination stays at $0200"
  },
  "817B": {
   "unit": "game/mapGenerator6F00",
   "line": "start with ..."
  },
  "817D": {
   "unit": "game/mapGenerator6F00",
   "line": "... sector 0 in zp_18"
  },
  "817F": {
   "unit": "game/mapGenerator6F00",
   "line": "the track filmSlotToTrack worked out"
  },
  "8182": {
   "unit": "game/mapGenerator6F00",
   "line": "zp_19 = the track to write"
  },
  "8184": {
   "unit": "game/mapGenerator6F00",
   "line": "copy one page of the film into $0200 with the I/O area banked out"
  },
  "8187": {
   "unit": "game/mapGenerator6F00",
   "line": "source low byte $00 ..."
  },
  "8189": {
   "unit": "game/mapGenerator6F00",
   "line": "... high byte $02: write from $0200"
  },
  "818B": {
   "unit": "game/mapGenerator6F00",
   "line": "tell the fast loader where the data is"
  },
  "818E": {
   "unit": "game/mapGenerator6F00",
   "line": "the film's track ..."
  },
  "8190": {
   "unit": "game/mapGenerator6F00",
   "line": "... and the current sector number"
  },
  "8192": {
   "unit": "game/mapGenerator6F00",
   "line": "one sector at a time"
  },
  "8194": {
   "unit": "game/mapGenerator6F00",
   "line": "the drive encrypts and writes it"
  },
  "8197": {
   "unit": "game/mapGenerator6F00",
   "line": "next source page ($D000, $D100, ...)"
  },
  "819A": {
   "unit": "game/mapGenerator6F00",
   "line": "next sector"
  },
  "819C": {
   "unit": "game/mapGenerator6F00",
   "line": "sector counter"
  },
  "819E": {
   "unit": "game/mapGenerator6F00",
   "line": "16 sectors = the whole 4 KB film"
  },
  "81A0": {
   "unit": "game/mapGenerator6F00",
   "line": "keep writing"
  },
  "81A2": {
   "unit": "game/mapGenerator6F00",
   "line": "restart the comm module and clear the status line"
  },
  "81A5": {
   "unit": "game/mapGenerator6F00",
   "routine": [
    "promptInsertGameDisk - end of the save/load film flow: shows 'INSERT GAME DISK AND PRESS",
    "SPACE.', then makes sure the 'OZ' disk really is in the drive and restarts the comm module.",
    "In:  -",
    "Out: the game disk is in the drive, the comm module runs again",
    "Called from: $7E21 and $7E27 (JMP, after the save and load menus)"
   ],
   "line": "low byte of 'GAME' at $EFD4 ..."
  },
  "81A7": {
   "unit": "game/mapGenerator6F00",
   "line": "... in the comm-module tail"
  },
  "81A9": {
   "unit": "game/mapGenerator6F00",
   "line": "patch it into the message and wait for space"
  },
  "81AC": {
   "unit": "game/mapGenerator6F00",
   "line": "keep asking until checkDiskId finds the 'OZ' disk"
  },
  "81AF": {
   "unit": "game/mapGenerator6F00",
   "line": "restart the comm module and clear the status line"
  },
  "81B2": {
   "unit": "game/mapGenerator6F00",
   "routine": [
    "promptInsertDataDisk - entry of promptInsertDiskAndWaitSpace with the word 'DATA' ($EFD0).",
    "In:  -",
    "Out: see promptInsertDiskAndWaitSpace",
    "Called from: $7DF4 (handleMainMenuChoice)"
   ],
   "line": "low byte of 'DATA' at $EFD0 ..."
  },
  "81B4": {
   "unit": "game/mapGenerator6F00",
   "line": "... in the comm-module tail; fall through"
  },
  "81B6": {
   "unit": "game/mapGenerator6F00",
   "routine": [
    "promptInsertDiskAndWaitSpace - shows 'INSERT xxxx DISK AND PRESS SPACE.' and waits for the",
    "space bar.  The four characters at $EFB6 inside the message template at $EFAF are overwritten",
    "with the word passed in A/Y ('DATA' or 'GAME'), the template is installed as message slot $1E",
    "and shown as the persistent status message; the link keeps being serviced while waiting.",
    "In:  A/Y = address of a 4-character disk name ($EFD0 'DATA' or $EFD4 'GAME')",
    "Out: $EFB6-$EFB9 patched, message slot $1E = $EFAF, $92A1, lastTypedKey $9248 = $FF, the",
    "     message line erased; self-modifies the operand of the LDA at $81C7",
    "Called from: $81A9 (promptInsertGameDisk) and by fall-through from promptInsertDataDisk"
   ],
   "line": "self-modifying: the word's low byte becomes the operand of the LDA at $81C7 ..."
  },
  "81B9": {
   "unit": "game/mapGenerator6F00",
   "line": "... and its high byte the second operand byte"
  },
  "81BC": {
   "unit": "game/mapGenerator6F00",
   "line": "message slot $1E is the free 'dynamic' slot"
  },
  "81BE": {
   "unit": "game/mapGenerator6F00",
   "line": "'INSERT DATA DISK AND PRESS SPACE.' at $EFAF ..."
  },
  "81C0": {
   "unit": "game/mapGenerator6F00",
   "line": "... in the comm-module tail"
  },
  "81C2": {
   "unit": "game/mapGenerator6F00",
   "line": "install that string as message $1E"
  },
  "81C5": {
   "unit": "game/mapGenerator6F00",
   "line": "four characters to patch"
  },
  "81C7": {
   "unit": "game/mapGenerator6F00",
   "line": "operand patched at $81B6/$81B9: reads 'DATA' or 'GAME' (assembled as an LDA $FFFF,y)"
  },
  "81CA": {
   "unit": "game/mapGenerator6F00",
   "line": "overwrite the word inside the message template"
  },
  "81CD": {
   "unit": "game/mapGenerator6F00",
   "line": "next character"
  },
  "81CE": {
   "unit": "game/mapGenerator6F00",
   "line": "all four"
  },
  "81D0": {
   "unit": "game/mapGenerator6F00",
   "line": "message $1E ..."
  },
  "81D2": {
   "unit": "game/mapGenerator6F00",
   "line": "... becomes the persistent status message"
  },
  "81D5": {
   "unit": "game/mapGenerator6F00",
   "line": "put it on the status line"
  },
  "81D8": {
   "unit": "game/mapGenerator6F00",
   "line": "$FF = no key pending"
  },
  "81DA": {
   "unit": "game/mapGenerator6F00",
   "line": "forget the last key"
  },
  "81DD": {
   "unit": "game/mapGenerator6F00",
   "line": "keep the link alive while the player swaps disks"
  },
  "81E0": {
   "unit": "game/mapGenerator6F00",
   "line": "the last key the IRQ saw"
  },
  "81E3": {
   "unit": "game/mapGenerator6F00",
   "line": "$A0 = the space bar"
  },
  "81E5": {
   "unit": "game/mapGenerator6F00",
   "line": "keep waiting"
  },
  "81E7": {
   "unit": "game/mapGenerator6F00",
   "line": "sound 2 ..."
  },
  "81E9": {
   "unit": "game/mapGenerator6F00",
   "line": "... the acknowledging click"
  },
  "81EC": {
   "unit": "game/mapGenerator6F00",
   "line": "no persistent message ..."
  },
  "81EE": {
   "unit": "game/mapGenerator6F00",
   "line": "... any more"
  },
  "81F1": {
   "unit": "game/mapGenerator6F00",
   "line": "erase the message line and return"
  },
  "81F4": {
   "unit": "game/mapGenerator6F00",
   "routine": [
    "checkFilmInMemory - is the buffer under I/O at $D000 a valid game film?  verifyFilmHeader",
    "recomputes the header checksum, compares it with header byte $33 and checks the version byte",
    "$2F against $0B94.  A valid film returns C=0 (and leaves zp_BC/zp_BD = $D000 for the readers",
    "that follow); otherwise message 9 'NOT A GAME FILM!' is shown for about four seconds and C=1",
    "comes back.",
    "In:  the film header at $D000",
    "Out: C=0 and A = header byte 4 when valid, C=1 otherwise; zp_BC/zp_BD = $D000",
    "Called from: $7DEF, $7E2F (main menu) and $7F98 (after loading a film)"
   ],
   "line": "check the header checksum and version byte"
  },
  "81F7": {
   "unit": "game/mapGenerator6F00",
   "line": "C=0: it is a film"
  },
  "81F9": {
   "unit": "game/mapGenerator6F00",
   "line": "message 9 = 'NOT A GAME FILM!'"
  },
  "81FB": {
   "unit": "game/mapGenerator6F00",
   "line": "queue it, then fall into the four-second wait"
  },
  "81FE": {
   "unit": "game/mapGenerator6F00",
   "routine": [
    "waitFramesServicingComm - waits about four seconds (240 IRQ frames counted down in zp_6C)",
    "while still servicing the command exchange, then returns C=1.  In this overlay it is only ever",
    "reached by falling out of checkFilmInMemory, so that the 'NOT A GAME FILM!' message stays",
    "readable before the caller carries on.  (The main program's call at $563C is to the drone",
    "routine that the comcen-screens overlay has at this same address, not to this code.)",
    "In:  -",
    "Out: C=1, joyRepeatTimer zp_6C = 0",
    "Called from: fall-through from $81FB"
   ],
   "line": "$F0 = 240 frames ..."
  },
  "8200": {
   "unit": "game/mapGenerator6F00",
   "line": "... about four seconds in one of the IRQ's frame countdowns"
  },
  "8202": {
   "unit": "game/mapGenerator6F00",
   "line": "keep the link alive while the message is on screen"
  },
  "8205": {
   "unit": "game/mapGenerator6F00",
   "line": "the IRQ counts the timer down"
  },
  "8207": {
   "unit": "game/mapGenerator6F00",
   "line": "wait until it reaches zero"
  },
  "8209": {
   "unit": "game/mapGenerator6F00",
   "line": "C=1 = 'no usable film'"
  },
  "820A": {
   "unit": "game/mapGenerator6F00",
   "line": "shared RTS: also the C=0 exit of checkFilmInMemory"
  }
 },
 "dataTypes": {
  "7C29": [
   "byte",
   5
  ],
  "7FA1": [
   "byte",
   1
  ],
  "7FA2": [
   "byte",
   1
  ],
  "7FA3": [
   "byte",
   3
  ]
 },
 "zpComments": {}
}