{
 "_comment": "Seed annotations written while reverse engineering the loader. Format: labels {hexAddr: name}, notes {hexAddr: {routine:[header lines], block:[comment lines before the address], line: 'end of line comment'}}, dataTypes {hexAddr: [type, length]} with type text|addr|word|byte, zp {hexAddr: [name, comment]}, driveZp likewise for the 1541 listings. Drive/raw-unit notes carry a 'unit' field.",
 "entries": [
  "09A6"
 ],
 "labels": {
  "0800": "gameEntry",
  "0803": "loaderStatus",
  "0804": "loadSectors",
  "0808": "loadSectorsLoop",
  "0820": "loadSectorOk",
  "0830": "loadSectorNext",
  "0840": "writeSectorsFail",
  "0843": "writeSectors",
  "0847": "writeSectorsLoop",
  "085C": "setLoadDest",
  "0863": "writeOneSector",
  "088F": "sendSectorData",
  "089C": "sendByteToDrive",
  "08E8": "sendByteShift",
  "08E9": "recvByteShift",
  "08EB": "readOneSector",
  "08FE": "readSectorBody",
  "0943": "finishTransfer",
  "0950": "receiveSectorData",
  "0957": "receiveByteLoop",
  "09A2": "loadDestLo",
  "09A3": "loadDestHi",
  "09A5": "savedSpriteEnable",
  "09A6": "manualLookupCheck",
  "0A1E": "wipeMemoryLoop",
  "0A48": "msgIdentifyMap",
  "0A6C": "mainStart",
  "0B4F": "loadCommTailBuild1",
  "0EFE": "loadRuntimeOverlays",
  "0F31": "loadCommBuild2",
  "0F41": "loadCommFinish",
  "0F55": "loadSectorsRetry",
  "0F81": "msgNotADataDisk",
  "0F91": "msgInsertGameDisk",
  "0FA2": "msgDiskError",
  "0FAD": "diskIdTable",
  "0FB1": "checkDiskId",
  "1018": "diskErrorExit",
  "1047": "loadOverlayA",
  "1060": "loadStringsAndCommTail",
  "1081": "loadOverlayBIfNeeded",
  "1086": "loadOverlayB",
  "10A4": "loadMapAndSetup",
  "10DF": "waitForGameDisk",
  "02B8": "eaLoadGame",
  "02EC": "eaFileName"
 },
 "notes": {
  "0800": {
   "routine": [
    "gameEntry - jumped to by the boot code at $02B8 after the fast loader has filled memory."
   ],
   "line": "-> mainStart"
  },
  "0803": {
   "line": "bit 7 = a read error occurred (cleared once reported)"
  },
  "0804": {
   "routine": [
    "loadSectors - read A consecutive sectors from track Y starting at sector X into the address set by setLoadDest.",
    "In:  A = sector count, Y = track, X = first sector.  Out: C = 0.  Destination advances one page per sector.",
    "Private copy of the boot loader's $C004; on an error it shows msgDiskError and retries forever."
   ]
  },
  "0843": {
   "routine": [
    "writeSectors - write A consecutive sectors to track Y from sector X, source address set by setLoadDest.",
    "In:  A = count, Y = track, X = first sector.  Out: C = 1 on failure, 0 on success."
   ]
  },
  "085C": {
   "routine": [
    "setLoadDest - X = low byte, Y = high byte of the next transfer address (patches loadDestLo/Hi)."
   ]
  },
  "0863": {
   "routine": [
    "writeOneSector - send command $60, track, sector and 256 data bytes to the drive, then read the status byte."
   ]
  },
  "089C": {
   "routine": [
    "sendByteToDrive - clock one byte to the drive 1 bit at a time through CIA2 port A bits 4/5 with a handshake on bits 6/7."
   ]
  },
  "08EB": {
   "routine": [
    "readOneSector - request track Y sector X from the drive (command $80) and receive 256 bytes. Out: C = 1 on error."
   ]
  },
  "0950": {
   "routine": [
    "receiveSectorData - the timing critical 2-bit receive loop. Sprites are disabled and bad lines avoided",
    "by waiting for a raster line that is not a multiple of 8 (the operand at $095F is patched with $D011 & 7 + $2F)."
   ]
  },
  "09A6": {
   "routine": [
    "manualLookupCheck - copy protection: ask the player to identify a map from the manual.",
    "Picks one of 32 three-byte answers from the table at $0401 (index patched into $09C4), prints msgIdentifyMap,",
    "reads the player's answer into $5A-$5C and allows three tries; after that it erases all of memory and hangs."
   ]
  },
  "0A48": {
   "line": "copy-protection prompt (bit 7 set on the last character terminates the string)"
  },
  "0A9B": {
   "line": "was JSR manualLookupCheck ($20 A6 09); the opcode is $2C = BIT, so the manual check is disabled in this build"
  },
  "0EFE": {
   "routine": [
    "loadRuntimeOverlays - called once at start-up: $C000 <- track 30, then loadOverlayA, then the communications",
    "module at $E000 (build 1 from track 35 when $0BA5 bit 7 is set, otherwise track 18 s7 + track 34)."
   ]
  },
  "0F55": {
   "routine": [
    "loadSectorsRetry - like loadSectors but first calls $C37F with A=8 (drive/UI housekeeping in the $C000 overlay)."
   ]
  },
  "0FB1": {
   "routine": [
    "checkDiskId - read the BAM (track 18 sector 0) into $0200 and compare the disk id with diskIdTable[X] (X=0 'OZ' game",
    "disk, X=2 'EA' data disk). Copies the 10-byte serial at BAM offset $AB to $FFDE-$FFE7 for the game disk.",
    "Out: C = 0 ok, C = 1 wrong disk / error (message already shown)."
   ]
  },
  "1047": {
   "routine": [
    "loadOverlayA - $6F00 <- track 31 s0-16, $8000 <- track 32 s0-7; falls through into loadStringsAndCommTail."
   ]
  },
  "1060": {
   "routine": [
    "loadStringsAndCommTail - $8800 <- track 29 s9-10 (messages), $EC00 <- track 34 s12-15."
   ]
  },
  "1086": {
   "routine": [
    "loadOverlayB - $6F00 <- track 33 s0-16, $8000 <- track 32 s8-15 (the variant the boot loader installs)."
   ]
  },
  "10A4": {
   "routine": [
    "loadMapAndSetup - $F000 <- track 18 s8-14 (battlefield map), $0200 <- track 18 s15-16 (saved setup block)."
   ]
  },
  "C004": {
   "routine": [
    "bootLoadSectors - read A sectors from track Y starting at sector X to the address set by bootSetDest.",
    "The game keeps its own identical copy at $0804 because this page is overwritten by the $C000 overlay."
   ],
   "unit": "boot/fastLoaderC000"
  },
  "C03B": {
   "routine": [
    "bootSendByte - full-duplex 1-bit exchange: shifts the command byte out LSB first on CIA2 port A and shifts the drive's",
    "reply in (LSB first, into $C088); returns the reply in A (Z set when it is 0)."
   ],
   "unit": "boot/fastLoaderC000"
  },
  "C0EF": {
   "routine": [
    "bootReceiveSectorData - 2-bit parallel receive loop; identical to $0950 in the game."
   ],
   "unit": "boot/fastLoaderC000"
  },
  "C145": {
   "routine": [
    "bootMain - entry from $02B8. Shows the title picture, initialises modem and drive, installs the drive code",
    "(B-E of track 1 sector 17), banks out the ROMs and loads the game (see docs/overview.md section 2)."
   ],
   "unit": "boot/fastLoaderC000"
  },
  "C1B1": {
   "line": "patched to PHA by bootShowTitlePicture (DEC $C1B1): pushes the RTS opcode ($60 read from bootDone) onto the stack at $01FD",
   "unit": "boot/fastLoaderC000"
  },
  "C168": {
   "line": "patched from STA abs,Y ($99) to TXS ($9A) by bootShowTitlePicture: S=$FD, so the return stack is $01FE/$01FF",
   "unit": "boot/fastLoaderC000"
  },
  "C219": {
   "line": "leftover strings from the shared EA loader ('GAMECHAR', 'HMEM'); unused here",
   "unit": "boot/fastLoaderC000"
  },
  "C229": {
   "routine": [
    "rtiVector - NMI and IRQ vector target while the loader runs with the ROMs banked out."
   ],
   "unit": "boot/fastLoaderC000"
  },
  "C23F": {
   "routine": [
    "bootShowTitlePicture - clear VIC registers, copy colour RAM from $9800 and screen RAM $9C00 -> $8C00,",
    "select VIC bank 2 and multicolour bitmap mode (bitmap at $A000)."
   ],
   "unit": "boot/fastLoaderC000"
  },
  "C2AB": {
   "routine": [
    "bootDriveInitialize - OPEN 15,8,15,\"I0\" then CLOSE (initialise the drive)."
   ],
   "unit": "boot/fastLoaderC000"
  },
  "C2DA": {
   "routine": [
    "bootModemInit - MEMTOP = $9700, OPEN 2,2,0 with RS-232 parameters $06/$00 (300 baud, 8N1) and send the",
    "Hayes command ATE0S0=0 (echo off, no auto-answer) so a connected modem is quiet while the game loads."
   ],
   "unit": "boot/fastLoaderC000"
  },
  "C32C": {
   "routine": [
    "bootBlockExecute - open a buffer channel (\"#\" on channel 2) and send \"B-E 2 0 1 17\" on the command channel:",
    "the drive loads track 1 sector 17 and executes it, which installs the fast loader in the drive."
   ],
   "unit": "boot/fastLoaderC000"
  },
  "C372": {
   "block": [
    "$C372-$C3FF: leftover code from the shared EA loader (references $BCC8/$BE6D/$CBED/$CBF2 that do not exist",
    "in this build). Not reachable from the boot path."
   ],
   "unit": "boot/fastLoaderC000"
  },
  "02B8": {
   "routine": [
    "eaLoadGame - reached through the hijacked BASIC vectors: SETLFS 8,1 / SETNAM \"LOAD\" / LOAD, then JSR $C000, JMP $0800."
   ],
   "unit": "boot/ea"
  },
  "02AA": {
   "block": [
    "$02A8-$02B7: a never-executed tokenised BASIC line (10 LOAD\"EA\",8,1) left in the file as filler."
   ],
   "unit": "boot/ea"
  },
  "C218": {
   "routine": [
    "bootDone - RTS, but the stack was rebuilt ($01FD=$60 via PHA, $01FE=$04 from $C26F): the return address on the stack is $0460,",
    "so execution continues at $0461 in the $0400 block (a memory checksum routine that ends with JMP $0800) and never",
    "returns to eaLoadGame's JMP $0800."
   ],
   "unit": "boot/fastLoaderC000"
  },
  "0B25": {
   "line": "filename field of the boot sector ($A0 = shifted space, then terminator); the C128 KERNAL skips it and enters at $0B27",
   "unit": "boot/c128boot_T1S0"
  }
 },
 "dataTypes": {
  "0A48": [
   "text",
   36
  ],
  "0F81": [
   "text",
   16
  ],
  "0F91": [
   "text",
   17
  ],
  "0FA2": [
   "text",
   11
  ],
  "0FAD": [
   "text",
   4
  ],
  "C219": [
   "text",
   9
  ],
  "C222": [
   "text",
   5
  ],
  "C2D8": [
   "text",
   2
  ],
  "C320": [
   "byte",
   2
  ],
  "C322": [
   "text",
   10
  ],
  "C35F": [
   "text",
   13
  ],
  "C36C": [
   "text",
   2
  ],
  "C3C0": [
   "text",
   4
  ],
  "02EC": [
   "text",
   4
  ],
  "0B25": [
   "byte",
   2
  ]
 },
 "zp": {
  "E0": [
   "ldTrack",
   "loader: current track"
  ],
  "E1": [
   "ldSector",
   "loader: current sector"
  ],
  "E2": [
   "ldDestPtr",
   "loader: destination pointer (lo)"
  ],
  "E3": [
   "ldDestPtrHi",
   "loader: destination pointer (hi)"
  ],
  "E4": [
   "ldCia2Bits",
   "loader: CIA2 port A bits 0-1 (VIC bank) kept during the transfer"
  ],
  "E5": [
   "wrSrcPtr",
   "loader: write source pointer (lo)"
  ],
  "E6": [
   "wrSrcPtrHi",
   "loader: write source pointer (hi)"
  ],
  "E7": [
   "ldCount",
   "loader: sectors left to transfer"
  ]
 },
 "unitLabels": {
  "boot/fastLoaderC000": {
   "C000": "bootEntry",
   "C003": "bootLoaderStatus",
   "C004": "bootLoadSectors",
   "C034": "bootSetDest",
   "C03B": "bootSendByte",
   "C08A": "bootReadOneSector",
   "C09D": "bootReadSectorBody",
   "C0EF": "bootReceiveSectorData",
   "C141": "bootDestLo",
   "C142": "bootDestHi",
   "C144": "bootSavedSprites",
   "C145": "bootMain",
   "C218": "bootDone",
   "C219": "strGameChar",
   "C222": "strHmem",
   "C229": "rtiVector",
   "C22A": "bootDisableIrqs",
   "C23F": "bootShowTitlePicture",
   "C2AB": "bootDriveInitialize",
   "C2CA": "bootWaitFrames",
   "C2D8": "strInitDrive",
   "C2DA": "bootModemInit",
   "C320": "rs232Params",
   "C322": "strHayesInit",
   "C32C": "bootBlockExecute",
   "C341": "bootSendDosCommand",
   "C35F": "strBlockExecute",
   "C36C": "strBufferChannel",
   "C371": "bootSavedCpuPort",
   "C3C0": "bootDiskIdTable"
  }
 }
}