{
 "chunk": "boot_fastLoaderC000_C000",
 "unit": "boot/fastLoaderC000",
 "unitLabels": {
  "boot/fastLoaderC000": {
   "C008": "bootReadSectorLoop",
   "C01C": "bootSectorOk",
   "C037": "bootSetDestHi",
   "C040": "bootExchangeBit",
   "C05C": "bootWaitDriveAck",
   "C06A": "bootDriveDataBit",
   "C075": "bootWaitClkAck",
   "C07A": "bootToggleDataAck",
   "C087": "bootSendShiftReg",
   "C088": "bootRecvShiftReg",
   "C0C8": "bootWaitSectorReady",
   "C0E2": "bootReadRestoreSprites",
   "C0EE": "bootReadSectorExit",
   "C0F6": "bootReceiveByteLoop",
   "C0FA": "bootWaitSafeRaster",
   "C105": "bootRequestByte",
   "C168": "bootPatchedTxs",
   "C197": "bootLoadTrackLoop",
   "C1B1": "bootPatchedPha",
   "C1B3": "bootCopyTo0400Loop",
   "C1B6": "bootCopyTo0400Store",
   "C200": "bootMoveHighLoop",
   "C203": "bootMoveHighStore",
   "C249": "bootClearSpriteRegLoop",
   "C257": "bootClearVicRegLoop",
   "C271": "bootCopyColourLoop",
   "C274": "bootCopyColourStore",
   "C288": "bootCopyScreenLoop",
   "C28B": "bootCopyScreenStore",
   "C2CF": "bootWaitRasterWrap",
   "C307": "bootHayesSendLoop",
   "C34B": "bootDosCommandLoop",
   "C356": "bootDosCommandEnd",
   "C3E0": "leftoverDiskIdLoop",
   "C3F4": "leftoverDiskIdNext",
   "C3FC": "leftoverDiskIdMatch"
  }
 },
 "notes": {
  "C000": {
   "unit": "boot/fastLoaderC000",
   "line": "JMP bootMain - the loader's one public entry",
   "routine": [
    "bootEntry - Public entry of the boot loader: JMP bootMain ($C145).  Emitted as .byte in the listing but it is a 3-byte JMP, put at the very start of the file so the caller does not have to know where bootMain lives.",
    "In:  none (the KERNAL has just loaded the file 'load' to $9800-$C3FF)",
    "Out: never comes back - see bootMain, whose RTS is redirected to $0461",
    "Called from: eaLoadGame $02D2 (boot/eaBootFile), the only caller in the whole boot chain."
   ]
  },
  "C003": {
   "unit": "boot/fastLoaderC000",
   "line": "bootLoaderStatus: the last non-zero DOS error code seen by bootLoadSectors; written, never read back"
  },
  "C004": {
   "unit": "boot/fastLoaderC000",
   "line": "keep the caller's I flag - the transfer itself must run with interrupts off",
   "routine": [
    "bootLoadSectors - Read A consecutive sectors from track Y starting at sector X into the address set by bootSetDest, one page per sector.  On a drive error it saves the status byte in bootLoaderStatus, turns the border red and asks for the same sector again forever; a good sector turns the border black.  It never steps to another track: every caller re-arms the track itself.",
    "In:  A = sector count, X = first sector, Y = track, bootDestLo/bootDestHi = destination page",
    "Out: C = 0, X = sector after the last one read, Y = track, ldCount = 0, bootDestHi advanced by A pages, I flag restored by the PHP/PLP pair, border colour left black",
    "Called from: bootMain $C19D (tracks 22-27), $C1D2 (track 33), $C1DB (track 32), $C1EB (track 28), $C1FB (track 29).  The game keeps its own identical copy at $0804."
   ]
  },
  "C005": {
   "unit": "boot/fastLoaderC000",
   "line": "no IRQ may land between two handshake edges"
  },
  "C006": {
   "unit": "boot/fastLoaderC000",
   "line": "A = how many sectors in a row to pull"
  },
  "C008": {
   "unit": "boot/fastLoaderC000",
   "line": "read one sector (Y = track, X = sector) into the page bootSetDest chose",
   "block": [
    "Sector loop.  One call per sector; a bad status byte is not an error to report but simply a reason to ask again, which is why a damaged disk shows a red border and hangs here."
   ]
  },
  "C00B": {
   "unit": "boot/fastLoaderC000",
   "line": "C = 0: the drive returned status 0, the 256 bytes are in memory"
  },
  "C00D": {
   "unit": "boot/fastLoaderC000",
   "line": "C = 1: A holds the DOS error code ($02 header not found, $03 no sync, $05 checksum, $08 write protect)"
  },
  "C010": {
   "unit": "boot/fastLoaderC000",
   "line": "colour 2 = red"
  },
  "C012": {
   "unit": "boot/fastLoaderC000",
   "line": "red border is the only sign of a read error the player ever gets"
  },
  "C015": {
   "unit": "boot/fastLoaderC000",
   "line": "same track again"
  },
  "C017": {
   "unit": "boot/fastLoaderC000",
   "line": "and the same sector"
  },
  "C019": {
   "unit": "boot/fastLoaderC000",
   "line": "retry forever - the loader has no give-up path"
  },
  "C01C": {
   "unit": "boot/fastLoaderC000",
   "line": "colour 0 = black",
   "block": [
    "The sector arrived intact: black border, then step on one sector and one destination page."
   ]
  },
  "C01E": {
   "unit": "boot/fastLoaderC000",
   "line": "border black again - this sector was good"
  },
  "C021": {
   "unit": "boot/fastLoaderC000",
   "line": "next sector of this track"
  },
  "C023": {
   "unit": "boot/fastLoaderC000",
   "line": "next destination page: one sector is exactly one page"
  },
  "C026": {
   "unit": "boot/fastLoaderC000",
   "line": "X = sector for the next call"
  },
  "C028": {
   "unit": "boot/fastLoaderC000",
   "line": "Y = track, unchanged throughout"
  },
  "C02A": {
   "unit": "boot/fastLoaderC000",
   "line": "one sector fewer to go"
  },
  "C02C": {
   "unit": "boot/fastLoaderC000",
   "line": "keep pulling until the count runs out"
  },
  "C02E": {
   "unit": "boot/fastLoaderC000",
   "line": "give the caller its I flag back"
  },
  "C02F": {
   "unit": "boot/fastLoaderC000",
   "line": "C = 0: every sector was read"
  },
  "C030": {
   "unit": "boot/fastLoaderC000",
   "line": "back to bootMain: every sector of the run is in memory"
  },
  "C031": {
   "unit": "boot/fastLoaderC000",
   "line": "PLP / SEC / RTS - the give-up exit the retry loop above never takes",
   "routine": [
    "bootLoadSectorsFail - Dead 3-byte error exit PLP / SEC / RTS, emitted as .byte because nothing reaches it.  It is the counterpart of writeSectorsFail ($0840) in the game's copy of the loader, where a failed write does report back; here a read error simply retries.",
    "In:  none",
    "Out: C = 1 (unreachable)",
    "Called from: nowhere."
   ]
  },
  "C034": {
   "unit": "boot/fastLoaderC000",
   "line": "low byte -> the operand the LDA at $C0D2 reads",
   "routine": [
    "bootSetDest - Set the address the next transfer goes to, by patching the two operand bytes bootDestLo and bootDestHi that bootReadSectorBody copies into the zero-page pointer.  Only the page ever advances, so the low byte is always 0 in practice.",
    "In:  X = destination low byte, Y = destination high byte",
    "Out: bootDestLo, bootDestHi",
    "Called from: bootMain $C194 ($0800), $C1C9 ($6F00), $C1E2 ($9300), $C1F2 ($FA00)."
   ]
  },
  "C037": {
   "unit": "boot/fastLoaderC000",
   "line": "high byte -> the page byte bootLoadSectors increments after every sector"
  },
  "C03A": {
   "unit": "boot/fastLoaderC000",
   "line": "back to bootMain"
  },
  "C03B": {
   "unit": "boot/fastLoaderC000",
   "line": "the byte to send goes into the transmit shift register",
   "block": [
    "CIA2 port A ($DD00) as this file uses it:  bits 0-1 VIC bank select (%01 = bank 2, $8000-$BFFF), bit 2 RS-232 TXD, bit 3 serial ATN OUT, bit 4 serial CLK OUT, bit 5 serial DATA OUT, bit 6 serial CLK IN, bit 7 serial DATA IN.  The two outputs are inverted - writing a 1 pulls that line low - while the two inputs read the line as it is, 1 = high = released.  At the drive both directions read inverted, which is why 'set DATA OUT' below arrives there as a 1 bit."
   ],
   "routine": [
    "bootExchangeByte - Full-duplex byte exchange with the drive over CIA2 port A, one bit per handshake, LSB first.  Our bit goes out on DATA OUT with CLK OUT as the strobe while the drive's bit comes in on DATA IN with CLK IN as its acknowledgement; the drive runs the mirror image of this in exchangeByteWithHost ($034B).  Used for the three command bytes of a read and, with a dummy byte, to collect the status afterwards.  Slow but self-timing: neither side has to count cycles.",
    "In:  A = byte to send",
    "Out: A = byte received (also in bootRecvShiftReg), Z reflects it, C = 0, X = 0, bootSendShiftReg = 0",
    "Called from: bootReadSectorBody $C09F (command $80), $C0A4 (track), $C0A9 (sector), $C0E8 (dummy byte for the status)."
   ]
  },
  "C03E": {
   "unit": "boot/fastLoaderC000",
   "line": "eight bits, one full handshake each"
  },
  "C040": {
   "unit": "boot/fastLoaderC000",
   "line": "V = bit 6 = CLK IN",
   "block": [
    "One bit per pass, in four phases: take the drive's bit, pull CLK to say we have it, put our own bit on DATA and release CLK, then wait for the drive's CLK acknowledgement and toggle DATA back."
   ]
  },
  "C043": {
   "unit": "boot/fastLoaderC000",
   "line": "wait for the drive to release CLK ($0357): its bit is now sitting on DATA"
  },
  "C045": {
   "unit": "boot/fastLoaderC000",
   "line": "read the port as it stands"
  },
  "C048": {
   "unit": "boot/fastLoaderC000",
   "line": "clear bit 5 = DATA OUT, so we stop driving DATA and can see what the drive drives"
  },
  "C04A": {
   "unit": "boot/fastLoaderC000",
   "line": "release the line"
  },
  "C04D": {
   "unit": "boot/fastLoaderC000",
   "line": "sample the port with the drive's bit on DATA IN"
  },
  "C050": {
   "unit": "boot/fastLoaderC000",
   "line": "C = bit 7 (DATA IN): set means the DATA line is high"
  },
  "C052": {
   "unit": "boot/fastLoaderC000",
   "line": "roll it into the top of the receive register; after eight rolls the first bit sits in bit 0 - LSB first"
  },
  "C055": {
   "unit": "boot/fastLoaderC000",
   "line": "set bit 4 = CLK OUT, which pulls the CLK line low"
  },
  "C057": {
   "unit": "boot/fastLoaderC000",
   "line": "CLK low means 'bit taken'; the drive spins on exactly this at $035A"
  },
  "C05A": {
   "unit": "boot/fastLoaderC000",
   "line": "clear bit 6 in the copy: CLK IN reads 0 now that we hold CLK low, so this is the port we expect"
  },
  "C05C": {
   "unit": "boot/fastLoaderC000",
   "line": "compare the expected port state against the live one"
  },
  "C05F": {
   "unit": "boot/fastLoaderC000",
   "line": "spin until something moves - the drive toggles its DATA OUT at $0366 to acknowledge"
  },
  "C061": {
   "unit": "boot/fastLoaderC000",
   "line": "shift the next outgoing bit out of the transmit register, LSB first",
   "block": [
    "Our own bit now.  The drive samples DATA while we hold CLK low and reads a low line as a 1, so a 1 bit means pulling DATA down."
   ]
  },
  "C064": {
   "unit": "boot/fastLoaderC000",
   "line": "bit = 0: leave DATA OUT clear, the line stays high and the drive samples a 0"
  },
  "C066": {
   "unit": "boot/fastLoaderC000",
   "line": "outgoing bit was 0? then $00 is the value to merge in"
  },
  "C068": {
   "unit": "boot/fastLoaderC000",
   "line": "bit = 1: set bit 5 = DATA OUT, pulling DATA low - a low line reads as 1 at the drive ($0375)"
  },
  "C06A": {
   "unit": "boot/fastLoaderC000",
   "line": "merge the data bit into the live port state, CLK OUT still held low"
  },
  "C06D": {
   "unit": "boot/fastLoaderC000",
   "line": "put our bit on the wire"
  },
  "C070": {
   "unit": "boot/fastLoaderC000",
   "line": "clear bit 4 = CLK OUT"
  },
  "C072": {
   "unit": "boot/fastLoaderC000",
   "line": "releasing CLK says the bit is valid; the drive waits for that at $036B"
  },
  "C075": {
   "unit": "boot/fastLoaderC000",
   "line": "V = CLK IN once more"
  },
  "C078": {
   "unit": "boot/fastLoaderC000",
   "line": "wait for the drive to pull CLK low ($037F) - it has taken our bit"
  },
  "C07A": {
   "unit": "boot/fastLoaderC000",
   "line": "toggle our DATA OUT bit..."
  },
  "C07C": {
   "unit": "boot/fastLoaderC000",
   "line": "...which is the acknowledgement the drive spins on at $0382"
  },
  "C07F": {
   "unit": "boot/fastLoaderC000",
   "line": "one bit done"
  },
  "C080": {
   "unit": "boot/fastLoaderC000",
   "line": "round again for all eight"
  },
  "C082": {
   "unit": "boot/fastLoaderC000",
   "line": "A = the byte the drive sent back; Z is set if it was 0 (status ok)"
  },
  "C085": {
   "unit": "boot/fastLoaderC000",
   "line": "C = 0: only bootReadSectorBody turns a non-zero status into an error flag"
  },
  "C086": {
   "unit": "boot/fastLoaderC000",
   "line": "back to bootReadSectorBody with the drive's answer in A"
  },
  "C087": {
   "unit": "boot/fastLoaderC000",
   "line": "bootSendShiftReg: the byte being sent, shifted right one bit per handshake"
  },
  "C088": {
   "unit": "boot/fastLoaderC000",
   "line": "bootRecvShiftReg: the byte being assembled, rolled in from the top ($BE is leftover); $C089 is unused"
  },
  "C08A": {
   "unit": "boot/fastLoaderC000",
   "line": "the transfer is timed by eye against the raster - nothing may interrupt it",
   "routine": [
    "bootReadOneSector - Wrapper round the protocol body: remember track and sector, stop CIA1 timer A so nothing can interrupt the cycle-counted burst, run the transfer and start the timer again.  The carry from bootReadSectorBody survives the two LDX/STX pairs and is what the caller tests.",
    "In:  Y = track, X = sector, bootDestLo/bootDestHi = destination",
    "Out: C = 1 and A = DOS status byte on error, C = 0 on success; ldTrack/ldSector set; I = 1; CIA1_CRA = 1",
    "Called from: bootLoadSectors $C008, once per sector."
   ]
  },
  "C08B": {
   "unit": "boot/fastLoaderC000",
   "line": "keep the track for the retry path in bootLoadSectors"
  },
  "C08D": {
   "unit": "boot/fastLoaderC000",
   "line": "and the sector"
  },
  "C08F": {
   "unit": "boot/fastLoaderC000",
   "line": "CRA = 0"
  },
  "C091": {
   "unit": "boot/fastLoaderC000",
   "line": "stop CIA1 timer A: its interrupt is what normally fires 60 times a second"
  },
  "C094": {
   "unit": "boot/fastLoaderC000",
   "line": "run the protocol; comes back with C = 1 and A = status on any DOS error"
  },
  "C097": {
   "unit": "boot/fastLoaderC000",
   "line": "CRA = 1: timer A running again, continuous mode"
  },
  "C099": {
   "unit": "boot/fastLoaderC000",
   "line": "LDX/STX touch no flags, so the error carry reaches the caller intact"
  },
  "C09C": {
   "unit": "boot/fastLoaderC000",
   "line": "back to bootLoadSectors, error carry intact"
  },
  "C09D": {
   "unit": "boot/fastLoaderC000",
   "line": "$80 = read a sector; the drive takes anything but $C0 (reset) and $60 (write) as a read",
   "routine": [
    "bootReadSectorBody - The protocol body of one sector read.  Sends the three command bytes $80/track/sector, prepares the machine for the burst (sprites off, VIC bank bits latched, DATA held low, raster threshold patched into the receive loop), waits for the drive to announce the sector, receives 256 bytes and finally exchanges a dummy byte to collect the status.  The drive decrypts the sector before sending it, so what arrives is plaintext.",
    "In:  ldTrack, ldSector, bootDestLo/bootDestHi, VIC_CTRL1 (its y-scroll decides where the bad lines are)",
    "Out: C = 0 and Z = 1 on success, C = 1 and A = DOS status on error; 256 bytes at the destination; bootSavedSprites, ldCia2Bits, ldDestPtr and the SBC operand at $C0FE updated",
    "Called from: bootReadOneSector $C094."
   ]
  },
  "C09F": {
   "unit": "boot/fastLoaderC000",
   "line": "send it - the drive hands back the $01 it loaded at $0569, which nobody looks at"
  },
  "C0A2": {
   "unit": "boot/fastLoaderC000",
   "line": "second command byte"
  },
  "C0A4": {
   "unit": "boot/fastLoaderC000",
   "line": "the track ($032D at the drive)"
  },
  "C0A7": {
   "unit": "boot/fastLoaderC000",
   "line": "third command byte"
  },
  "C0A9": {
   "unit": "boot/fastLoaderC000",
   "line": "the sector ($0332); the drive now hunts for that header and reads it"
  },
  "C0AC": {
   "unit": "boot/fastLoaderC000",
   "line": "$D015, the sprite enable mask",
   "block": [
    "The drive needs a few milliseconds to find the sector - use them to get the C64 into a state where 256 bytes can be taken at a fixed number of cycles each."
   ]
  },
  "C0AF": {
   "unit": "boot/fastLoaderC000",
   "line": "save it: sprite DMA has to be off while the bytes come in"
  },
  "C0B2": {
   "unit": "boot/fastLoaderC000",
   "line": "the port again"
  },
  "C0B5": {
   "unit": "boot/fastLoaderC000",
   "line": "bits 0-1 only = the VIC bank select, the only bits that must be preserved"
  },
  "C0B7": {
   "unit": "boot/fastLoaderC000",
   "line": "the receive loop rewrites these on every strobe and EORs them back out of each sample"
  },
  "C0B9": {
   "unit": "boot/fastLoaderC000",
   "line": "plus bit 5 = DATA OUT: hold DATA low, which tells the drive 'not ready for a byte'"
  },
  "C0BB": {
   "unit": "boot/fastLoaderC000",
   "line": "the same write clears bits 2-4, so TXD, ATN and CLK OUT are all released"
  },
  "C0BE": {
   "unit": "boot/fastLoaderC000",
   "line": "$D011"
  },
  "C0C1": {
   "unit": "boot/fastLoaderC000",
   "line": "bits 0-2 = y-scroll, which is what decides which raster lines are bad lines"
  },
  "C0C3": {
   "unit": "boot/fastLoaderC000",
   "line": "+ $2F; $2F mod 8 = 7, so the threshold is congruent to yscroll - 1.  C is 0 here (the CLC ending bootExchangeByte), so this really is a plain add"
  },
  "C0C5": {
   "unit": "boot/fastLoaderC000",
   "line": "patch the SBC operand at $C0FE - the receive loop now knows which line to keep clear of"
  },
  "C0C8": {
   "unit": "boot/fastLoaderC000",
   "line": "V = CLK IN",
   "block": [
    "Handshake: the drive pulls CLK low when the sector is in its buffer, decrypted and ready to go."
   ]
  },
  "C0CB": {
   "unit": "boot/fastLoaderC000",
   "line": "wait for CLK to go low - the drive's 'sector ready' at $044D"
  },
  "C0CD": {
   "unit": "boot/fastLoaderC000",
   "line": "no sprites at all"
  },
  "C0CF": {
   "unit": "boot/fastLoaderC000",
   "line": "sprite DMA would steal cycles and slide every sample point"
  },
  "C0D2": {
   "unit": "boot/fastLoaderC000",
   "line": "the destination bootSetDest patched in..."
  },
  "C0D5": {
   "unit": "boot/fastLoaderC000",
   "line": "...into the zero-page pointer the receive loop stores through"
  },
  "C0D7": {
   "unit": "boot/fastLoaderC000",
   "line": "high byte, bumped one page per sector by bootLoadSectors"
  },
  "C0DA": {
   "unit": "boot/fastLoaderC000",
   "line": "ldDestPtr now points at this sector's page"
  },
  "C0DC": {
   "unit": "boot/fastLoaderC000",
   "line": "take the 256 bytes, two bits at a time"
  },
  "C0DF": {
   "unit": "boot/fastLoaderC000",
   "line": "jump to the next instruction: the fossil of a longer error path"
  },
  "C0E2": {
   "unit": "boot/fastLoaderC000",
   "line": "the sprite mask from before the burst",
   "block": [
    "Put the machine back and collect the drive's verdict."
   ]
  },
  "C0E5": {
   "unit": "boot/fastLoaderC000",
   "line": "restore it (nothing is enabled during boot, but the game's copy of this code needs it)"
  },
  "C0E8": {
   "unit": "boot/fastLoaderC000",
   "line": "exchange a dummy byte - whatever is in A - to receive the status the drive sends at $0391"
  },
  "C0EB": {
   "unit": "boot/fastLoaderC000",
   "line": "status 0 = no error: return with the C = 0 that bootExchangeByte left"
  },
  "C0ED": {
   "unit": "boot/fastLoaderC000",
   "line": "any DOS error code: C = 1 and bootLoadSectors will ask for the sector again"
  },
  "C0EE": {
   "unit": "boot/fastLoaderC000",
   "line": "back to bootReadOneSector"
  },
  "C0EF": {
   "unit": "boot/fastLoaderC000",
   "line": "V = CLK IN",
   "routine": [
    "bootReceiveSectorData - The timing-critical half of the loader, identical to receiveSectorData ($0950) in the game.  For each of 256 bytes it releases DATA to ask for one, then reads CIA2 port A four times at fixed cycle offsets; each read carries two data bits on CLK IN and DATA IN, LSB pair first, and the four pairs are folded together with LSR LSR / EOR.  The drive answers with four port writes ($046E, $0475, $047D, $0484) and never handshakes inside a byte, so the NOPs and the 'bit loaderTimingPad' here are load-bearing padding, not filler.  Before each byte it also refuses to start on the raster line just before a bad line, because the VIC would steal 40-43 cycles in the middle of the byte.",
    "In:  ldCia2Bits (VIC bank bits), ldDestPtr/ldDestPtrHi, the raster threshold patched into $C0FE",
    "Out: 256 bytes at (ldDestPtr), Y = 0, CIA2 port A left with DATA OUT set (DATA held low)",
    "Called from: bootReadSectorBody $C0DC."
   ]
  },
  "C0F2": {
   "unit": "boot/fastLoaderC000",
   "line": "wait for the drive to release CLK ($0466): it is in its per-byte loop now"
  },
  "C0F4": {
   "unit": "boot/fastLoaderC000",
   "line": "256 bytes, indexed 0..255"
  },
  "C0F6": {
   "unit": "boot/fastLoaderC000",
   "line": "C = 1 so the SBC below subtracts the threshold exactly",
   "block": [
    "One pass per byte.  First find a raster line the byte will fit on."
   ]
  },
  "C0F7": {
   "unit": "boot/fastLoaderC000",
   "line": "X = the VIC bank bits alone: CLK OUT, DATA OUT, ATN and TXD all released"
  },
  "C0F9": {
   "unit": "boot/fastLoaderC000",
   "line": "padding"
  },
  "C0FA": {
   "unit": "boot/fastLoaderC000",
   "line": "$D012 = the low eight bits of the current raster line"
  },
  "C0FD": {
   "unit": "boot/fastLoaderC000",
   "line": "operand patched at $C0C5 to yscroll + $2F"
  },
  "C0FF": {
   "unit": "boot/fastLoaderC000",
   "line": "line is above the display window - no bad line can happen here, go ahead"
  },
  "C101": {
   "unit": "boot/fastLoaderC000",
   "line": "distance from a bad line, modulo 8"
  },
  "C103": {
   "unit": "boot/fastLoaderC000",
   "line": "0 means this is the line right before a bad line: wait, the byte would not fit in it"
  },
  "C105": {
   "unit": "boot/fastLoaderC000",
   "line": "release DATA = 'send the next byte'; the drive's $0469 loop is waiting for exactly this",
   "block": [
    "The byte itself.  Releasing DATA is the request; the drive answers with four port writes and the four reads below are spaced to land on them.  Each sample has the data pair in bits 7-6 and the VIC bank bits in 1-0; two LSRs per step drop the bank bits and slide the pairs into place, and the EOR of ldCia2Bits at $C123 cancels in advance the bank bits that ride in on the last sample."
   ]
  },
  "C108": {
   "unit": "boot/fastLoaderC000",
   "line": "the same port value..."
  },
  "C109": {
   "unit": "boot/fastLoaderC000",
   "line": "...plus DATA OUT..."
  },
  "C10B": {
   "unit": "boot/fastLoaderC000",
   "line": "...kept in X ready for the 'byte taken' write at $C128"
  },
  "C10C": {
   "unit": "boot/fastLoaderC000",
   "line": "padding: the four reads below have to line up with the drive's four writes"
  },
  "C10D": {
   "unit": "boot/fastLoaderC000",
   "line": "padding"
  },
  "C10E": {
   "unit": "boot/fastLoaderC000",
   "line": "BIT $80 - three cycles of delay, nothing is tested"
  },
  "C110": {
   "unit": "boot/fastLoaderC000",
   "line": "sample 1 (drive write at $046E): data bits 1,0 in bits 7,6"
  },
  "C113": {
   "unit": "boot/fastLoaderC000",
   "line": "slide the pair down..."
  },
  "C114": {
   "unit": "boot/fastLoaderC000",
   "line": "...to bits 5,4; the bank bits in 1,0 fall off the end"
  },
  "C115": {
   "unit": "boot/fastLoaderC000",
   "line": "padding"
  },
  "C116": {
   "unit": "boot/fastLoaderC000",
   "line": "sample 2 ($0475): data bits 3,2 arrive in bits 7,6"
  },
  "C119": {
   "unit": "boot/fastLoaderC000",
   "line": "slide both pairs down..."
  },
  "C11A": {
   "unit": "boot/fastLoaderC000",
   "line": "...so pairs 2 and 1 sit in bits 5-4 and 3-2"
  },
  "C11B": {
   "unit": "boot/fastLoaderC000",
   "line": "padding"
  },
  "C11C": {
   "unit": "boot/fastLoaderC000",
   "line": "padding"
  },
  "C11D": {
   "unit": "boot/fastLoaderC000",
   "line": "padding"
  },
  "C11E": {
   "unit": "boot/fastLoaderC000",
   "line": "sample 3 ($047D): data bits 5,4"
  },
  "C121": {
   "unit": "boot/fastLoaderC000",
   "line": "slide the three pairs down..."
  },
  "C122": {
   "unit": "boot/fastLoaderC000",
   "line": "...into bits 5-0"
  },
  "C123": {
   "unit": "boot/fastLoaderC000",
   "line": "pre-cancel the bank bits that the last sample will bring in on bits 1-0"
  },
  "C125": {
   "unit": "boot/fastLoaderC000",
   "line": "sample 4 ($0484): data bits 7,6 - A is now the finished byte"
  },
  "C128": {
   "unit": "boot/fastLoaderC000",
   "line": "pull DATA low again: 'byte taken', the drive may start the next one"
  },
  "C12B": {
   "unit": "boot/fastLoaderC000",
   "line": "padding"
  },
  "C12C": {
   "unit": "boot/fastLoaderC000",
   "line": "store it through the pointer bootReadSectorBody set up"
  },
  "C12E": {
   "unit": "boot/fastLoaderC000",
   "line": "next byte"
  },
  "C12F": {
   "unit": "boot/fastLoaderC000",
   "line": "all 256 of them"
  },
  "C131": {
   "unit": "boot/fastLoaderC000",
   "line": "back to bootReadSectorBody: 256 bytes delivered"
  },
  "C132": {
   "unit": "boot/fastLoaderC000",
   "line": "unused duplicate of the DOS command at $C35F; the live copy is the one bootSendDosCommand reads"
  },
  "C13F": {
   "unit": "boot/fastLoaderC000",
   "line": "unused duplicate of the buffer-channel filename at $C36C"
  },
  "C141": {
   "unit": "boot/fastLoaderC000",
   "line": "bootDestLo: destination low byte, read by the LDA at $C0D2"
  },
  "C142": {
   "unit": "boot/fastLoaderC000",
   "line": "bootDestHi: destination page, incremented after every good sector"
  },
  "C143": {
   "unit": "boot/fastLoaderC000",
   "line": "unused"
  },
  "C144": {
   "unit": "boot/fastLoaderC000",
   "line": "bootSavedSprites: $D015 parked here while a sector comes in"
  },
  "C145": {
   "unit": "boot/fastLoaderC000",
   "line": "binary arithmetic from here on",
   "block": [
    "Blank the screen and start the noise oscillator, whose output is sampled at the very end as the game's random seed."
   ],
   "routine": [
    "bootMain - The whole boot sequence.  Blanks the screen, starts SID voice 3 as a noise source, shows the title picture, arms the two self-modifying patches that rebuild the stack, hushes any modem, initialises the drive and block-executes the drive-side fast loader, then throws the KERNAL out of the address space and pulls the entire game off the disk while the picture is up.",
    "Load map: tracks 22-27 s0-17 -> $0800-$73FF (the main program); $6F00-$72FF copied down to $0400-$07FF; track 33 s0-16 -> $6F00 and track 32 s8-15 -> $8000 (the comcen-screen overlay); track 28 s0-12 -> $9300; track 29 s11-15 -> $FA00, then moved up $1B8 bytes so it ends at $FFD1.",
    "In:  the game disk in device 8, the title picture at $9800/$9C00/$A000 as loaded with the file 'load'",
    "Out: the game's memory image ($0400-$87FF, $9300-$9FFF, $FBB8-$FFD1), randomSeed at $09C3, $01 = $35, $00 = $2F, $FFFA/$FFFE = rtiVector, CIA and VIC interrupts masked, the title picture on screen",
    "Called from: bootEntry $C000 (so, eaLoadGame $02D2).  It never returns there: see bootDone."
   ]
  },
  "C146": {
   "unit": "boot/fastLoaderC000",
   "line": "0 into every register below"
  },
  "C148": {
   "unit": "boot/fastLoaderC000",
   "line": "$D015 = 0: no sprites"
  },
  "C14B": {
   "unit": "boot/fastLoaderC000",
   "line": "border black"
  },
  "C14E": {
   "unit": "boot/fastLoaderC000",
   "line": "background black"
  },
  "C151": {
   "unit": "boot/fastLoaderC000",
   "line": "$D011 = 0 clears DEN: the display is blanked while the picture is copied into place"
  },
  "C154": {
   "unit": "boot/fastLoaderC000",
   "line": "bit 7 = noise waveform, gate off"
  },
  "C156": {
   "unit": "boot/fastLoaderC000",
   "line": "voice 3 free-runs as a noise generator, so $D41B reads as a random byte"
  },
  "C159": {
   "unit": "boot/fastLoaderC000",
   "line": "copy the picture in and switch the VIC to it; this call also arms both stack patches"
  },
  "C15C": {
   "unit": "boot/fastLoaderC000",
   "line": "X = $FF",
   "block": [
    "$FF serves three purposes: both halves of the SID frequency, and - two DEXes later - the stack pointer the patched instruction at $C168 installs."
   ]
  },
  "C15E": {
   "unit": "boot/fastLoaderC000",
   "line": "voice 3 frequency high byte"
  },
  "C161": {
   "unit": "boot/fastLoaderC000",
   "line": "and low byte: $FFFF, the fastest the noise generator will run"
  },
  "C164": {
   "unit": "boot/fastLoaderC000",
   "line": "X = $FE"
  },
  "C165": {
   "unit": "boot/fastLoaderC000",
   "line": "X = $FD"
  },
  "C166": {
   "unit": "boot/fastLoaderC000",
   "line": "Y = 0 for the copy loop further down"
  },
  "C168": {
   "unit": "boot/fastLoaderC000",
   "line": "patched from STA $AAEA,Y ($99) to TXS ($9A) by bootShowTitlePicture ($C242).  S = $FD, which throws away eaLoadGame's return address and leaves $01FD/$01FE free for the fake one; the two orphaned operand bytes $EA $AA run harmlessly as NOP / TAX"
  },
  "C16B": {
   "unit": "boot/fastLoaderC000",
   "line": "hush any attached modem: echo off, auto-answer off",
   "block": [
    "Talk to the outside world while the KERNAL is still there: modem, drive, and the DOS command that installs the fast loader in the 1541."
   ]
  },
  "C16E": {
   "unit": "boot/fastLoaderC000",
   "line": "OPEN 15,8,15,'I0' - initialise the drive"
  },
  "C171": {
   "unit": "boot/fastLoaderC000",
   "line": "B-E track 1 sector 17: the drive loads that sector into a buffer and runs it, which pulls the fast loader into $0300-$05FF in the drive and jumps to it"
  },
  "C174": {
   "unit": "boot/fastLoaderC000",
   "line": "mask every interrupt source before the ROMs go away",
   "block": [
    "From here on there is no KERNAL and no BASIC: the loader owns the machine, and everything below $FFFA is fair game as a destination."
   ]
  },
  "C177": {
   "unit": "boot/fastLoaderC000",
   "line": "$35"
  },
  "C179": {
   "unit": "boot/fastLoaderC000",
   "line": "$01 = $35: I/O still visible, BASIC and KERNAL ROM banked out so RAM under them can be filled"
  },
  "C17B": {
   "unit": "boot/fastLoaderC000",
   "line": "$2F"
  },
  "C17D": {
   "unit": "boot/fastLoaderC000",
   "line": "$00 = $2F: the usual processor-port direction bits"
  },
  "C17F": {
   "unit": "boot/fastLoaderC000",
   "line": "low byte of rtiVector"
  },
  "C181": {
   "unit": "boot/fastLoaderC000",
   "line": "$FFFA is RAM now, so the NMI vector has to be planted by hand"
  },
  "C184": {
   "unit": "boot/fastLoaderC000",
   "line": "and $FFFE, the IRQ/BRK vector"
  },
  "C187": {
   "unit": "boot/fastLoaderC000",
   "line": "high byte of $C229"
  },
  "C189": {
   "unit": "boot/fastLoaderC000",
   "line": "NMI vector complete"
  },
  "C18C": {
   "unit": "boot/fastLoaderC000",
   "line": "IRQ vector complete: any interrupt from here just RTIs"
  },
  "C18F": {
   "unit": "boot/fastLoaderC000",
   "line": "interrupts may run again - they do nothing"
  },
  "C190": {
   "unit": "boot/fastLoaderC000",
   "line": "destination low byte",
   "block": [
    "The main program: tracks 22 to 27, all 18 sectors of each, straight into $0800 upwards.  The destination page carries on by itself from one call to the next."
   ]
  },
  "C192": {
   "unit": "boot/fastLoaderC000",
   "line": "destination page $08"
  },
  "C194": {
   "unit": "boot/fastLoaderC000",
   "line": "first byte of the game lands at $0800"
  },
  "C197": {
   "unit": "boot/fastLoaderC000",
   "line": "track 22 ($16); this operand is what the INC at $C1A0 walks forward"
  },
  "C199": {
   "unit": "boot/fastLoaderC000",
   "line": "from sector 0"
  },
  "C19B": {
   "unit": "boot/fastLoaderC000",
   "line": "18 sectors - a whole track"
  },
  "C19D": {
   "unit": "boot/fastLoaderC000",
   "line": "18 more pages of the game"
  },
  "C1A0": {
   "unit": "boot/fastLoaderC000",
   "line": "self-modifying: aim the LDY above at the next track"
  },
  "C1A3": {
   "unit": "boot/fastLoaderC000",
   "line": "read it back"
  },
  "C1A6": {
   "unit": "boot/fastLoaderC000",
   "line": "past track 27 ($1B)?"
  },
  "C1A8": {
   "unit": "boot/fastLoaderC000",
   "line": "six tracks, 108 sectors: $0800-$73FF"
  },
  "C1AA": {
   "unit": "boot/fastLoaderC000",
   "line": "read the byte $60 out of bootDone's own RTS instruction",
   "block": [
    "Build the fake return address.  bootShowTitlePicture has already put $04 at $01FE; the patched instruction below pushes $60 - the RTS opcode, read out of the code at bootDone - to $01FD.  The RTS at $C218 therefore pops $0460, adds one and continues at $0461, the memory-checksum routine that arrives with the $0400 block a few instructions later."
   ]
  },
  "C1AD": {
   "unit": "boot/fastLoaderC000",
   "line": "Y = 0 for the copy loop"
  },
  "C1AF": {
   "unit": "boot/fastLoaderC000",
   "line": "four pages to copy"
  },
  "C1B1": {
   "unit": "boot/fastLoaderC000",
   "line": "patched from EOR #$8A ($49) to PHA ($48) by bootShowTitlePicture ($C283): pushes that $60 to $01FD.  The orphaned operand byte $8A then runs as TXA"
  },
  "C1B3": {
   "unit": "boot/fastLoaderC000",
   "line": "$6F00 = where track 27 sectors 13-16 ended up (the label belongs to the later overlay at $6F00)",
   "block": [
    "Rescue the tail of track 27.  Sectors 13-16 of that track landed at $6F00-$72FF, but they belong at $0400-$07FF: the manual-check answer table, the checksum routine this loader returns into, and the status message strings.  $6F00 is then free to receive an overlay."
   ]
  },
  "C1B6": {
   "unit": "boot/fastLoaderC000",
   "line": "$0400 upwards"
  },
  "C1B9": {
   "unit": "boot/fastLoaderC000",
   "line": "next byte"
  },
  "C1BA": {
   "unit": "boot/fastLoaderC000",
   "line": "a page at a time"
  },
  "C1BC": {
   "unit": "boot/fastLoaderC000",
   "line": "self-modifying source page"
  },
  "C1BF": {
   "unit": "boot/fastLoaderC000",
   "line": "and destination page"
  },
  "C1C2": {
   "unit": "boot/fastLoaderC000",
   "line": "four pages"
  },
  "C1C3": {
   "unit": "boot/fastLoaderC000",
   "line": "$6F00-$72FF -> $0400-$07FF"
  },
  "C1C5": {
   "unit": "boot/fastLoaderC000",
   "line": "destination low byte",
   "block": [
    "Overlay variant B into $6F00-$87FF: the comcen missile and drone screens.  The game swaps the map generator (tracks 31 + 32 s0-7) in over the top of it later on."
   ]
  },
  "C1C7": {
   "unit": "boot/fastLoaderC000",
   "line": "destination page $6F"
  },
  "C1C9": {
   "unit": "boot/fastLoaderC000",
   "line": "next transfer starts at $6F00"
  },
  "C1CC": {
   "unit": "boot/fastLoaderC000",
   "line": "track 33"
  },
  "C1CE": {
   "unit": "boot/fastLoaderC000",
   "line": "17 sectors"
  },
  "C1D0": {
   "unit": "boot/fastLoaderC000",
   "line": "from sector 0"
  },
  "C1D2": {
   "unit": "boot/fastLoaderC000",
   "line": "-> $6F00-$7FFF"
  },
  "C1D5": {
   "unit": "boot/fastLoaderC000",
   "line": "track 32"
  },
  "C1D7": {
   "unit": "boot/fastLoaderC000",
   "line": "from sector 8"
  },
  "C1D9": {
   "unit": "boot/fastLoaderC000",
   "line": "8 sectors - the second half of that track"
  },
  "C1DB": {
   "unit": "boot/fastLoaderC000",
   "line": "-> $8000-$87FF, the tail of the same overlay"
  },
  "C1DE": {
   "unit": "boot/fastLoaderC000",
   "line": "destination low byte",
   "block": [
    "Tables and graphics."
   ]
  },
  "C1E0": {
   "unit": "boot/fastLoaderC000",
   "line": "destination page $93"
  },
  "C1E2": {
   "unit": "boot/fastLoaderC000",
   "line": "next transfer starts at $9300"
  },
  "C1E5": {
   "unit": "boot/fastLoaderC000",
   "line": "track 28"
  },
  "C1E7": {
   "unit": "boot/fastLoaderC000",
   "line": "from sector 0"
  },
  "C1E9": {
   "unit": "boot/fastLoaderC000",
   "line": "13 sectors"
  },
  "C1EB": {
   "unit": "boot/fastLoaderC000",
   "line": "-> $9300-$9FFF"
  },
  "C1EE": {
   "unit": "boot/fastLoaderC000",
   "line": "destination low byte",
   "block": [
    "The resident high code, loaded low and then shuffled up.  It has to end at $FFD1, just below the hardware vectors, but it cannot be loaded there directly because the last sector would run over $FFFA and destroy the vectors that were planted at $C181."
   ]
  },
  "C1F0": {
   "unit": "boot/fastLoaderC000",
   "line": "destination page $FA - RAM under the KERNAL, reachable because $01 = $35"
  },
  "C1F2": {
   "unit": "boot/fastLoaderC000",
   "line": "next transfer starts at $FA00"
  },
  "C1F5": {
   "unit": "boot/fastLoaderC000",
   "line": "track 29"
  },
  "C1F7": {
   "unit": "boot/fastLoaderC000",
   "line": "from sector 11"
  },
  "C1F9": {
   "unit": "boot/fastLoaderC000",
   "line": "5 sectors"
  },
  "C1FB": {
   "unit": "boot/fastLoaderC000",
   "line": "-> $FA00-$FEFF; X comes back as $10, which the move loop below relies on"
  },
  "C1FE": {
   "unit": "boot/fastLoaderC000",
   "line": "Y = 0",
   "block": [
    "Move the block up by $1B8 bytes, top page first so nothing overwrites itself."
   ]
  },
  "C200": {
   "unit": "boot/fastLoaderC000",
   "line": "top source page; $FD1A + $1B8 = $FED2"
  },
  "C203": {
   "unit": "boot/fastLoaderC000",
   "line": "the same byte $1B8 higher up"
  },
  "C206": {
   "unit": "boot/fastLoaderC000",
   "line": "next byte"
  },
  "C207": {
   "unit": "boot/fastLoaderC000",
   "line": "a page at a time"
  },
  "C209": {
   "unit": "boot/fastLoaderC000",
   "line": "self-modifying: walk both pointers downwards..."
  },
  "C20C": {
   "unit": "boot/fastLoaderC000",
   "line": "...so the copy never catches its own tail"
  },
  "C20F": {
   "unit": "boot/fastLoaderC000",
   "line": "X is still the $10 bootLoadSectors left behind: 16 pages"
  },
  "C210": {
   "unit": "boot/fastLoaderC000",
   "line": "$FA00-$FEFF ends up at $FBB8-$FFD1; the pages below $FA00 drag garbage into $EFD2-$FBB7, which the game overwrites long before it looks there"
  },
  "C212": {
   "unit": "boot/fastLoaderC000",
   "line": "$D41B = voice 3's oscillator output, free-running noise since $C156",
   "block": [
    "One last random byte, then the RTS that is not a return."
   ]
  },
  "C215": {
   "unit": "boot/fastLoaderC000",
   "line": "seed the game's random number generator with it"
  },
  "C218": {
   "unit": "boot/fastLoaderC000",
   "line": "pops $0460 and continues at $0461, not at $02D5",
   "routine": [
    "bootDone - An RTS that is really a jump.  The stack was rebuilt while the loader ran: $01FE = $04 (stored by bootShowTitlePicture at $C26C) and $01FD = $60 (pushed by the patched PHA at $C1B1, the value being this very RTS opcode read back as data at $C1AA).  So the RTS pops $0460, adds one and carries on at $0461 - the memory checksum routine in the $0400 block that was copied down from $6F00 - which ends with JMP $0800.  eaLoadGame's own JMP $0800 at $02D5 is never reached.",
    "In:  S = $FC, $01FD/$01FE = $60/$04",
    "Out: execution continues at $0461 with S = $FE",
    "Called from: falls in from bootMain."
   ]
  },
  "C229": {
   "unit": "boot/fastLoaderC000",
   "line": "the whole interrupt service routine",
   "routine": [
    "rtiVector - The entire interrupt handler while the loader owns the machine.  $FFFA and $FFFE both point here from $C181-$C18E, so a stray NMI (RESTORE key) or IRQ costs a few cycles and nothing else.",
    "In:  none",
    "Out: none",
    "Called from: the NMI and IRQ vectors $FFFA/$FFFE, set by bootMain."
   ]
  },
  "C22A": {
   "unit": "boot/fastLoaderC000",
   "line": "no interrupts while the interrupt hardware is being turned off",
   "routine": [
    "bootDisableIrqs - Mask and acknowledge every interrupt source in the machine: both CIA interrupt control registers and the VIC interrupt mask.  Leaves the I flag set; the callers CLI again once the vectors are safe.",
    "In:  none",
    "Out: CIA1_ICR and CIA2_ICR masked and their latches cleared, VIC_IRQ_MASK = 0, I = 1",
    "Called from: bootMain $C174 (before the ROMs are banked out) and bootShowTitlePicture $C23F."
   ]
  },
  "C22B": {
   "unit": "boot/fastLoaderC000",
   "line": "bit 7 clear = clear the mask bits named in bits 0-4, that is, all five sources"
  },
  "C22D": {
   "unit": "boot/fastLoaderC000",
   "line": "CIA1: no more timer A jiffy IRQ, no keyboard or timer B IRQ"
  },
  "C230": {
   "unit": "boot/fastLoaderC000",
   "line": "CIA2: no more NMI from its timers, its FLAG line or the RS-232 code"
  },
  "C233": {
   "unit": "boot/fastLoaderC000",
   "line": "reading an ICR clears whatever flag has already latched in it"
  },
  "C236": {
   "unit": "boot/fastLoaderC000",
   "line": "and releases a pending CIA2 NMI so it cannot fire the moment I is cleared"
  },
  "C239": {
   "unit": "boot/fastLoaderC000",
   "line": "0"
  },
  "C23B": {
   "unit": "boot/fastLoaderC000",
   "line": "$D01A = 0: no raster and no sprite-collision interrupt either"
  },
  "C23E": {
   "unit": "boot/fastLoaderC000",
   "line": "the machine can no longer interrupt itself"
  },
  "C23F": {
   "unit": "boot/fastLoaderC000",
   "line": "nothing may look at the screen while the VIC is being reprogrammed",
   "routine": [
    "bootShowTitlePicture - Puts the title picture on the screen, and on the way arms both halves of the trick ending.  The picture came in with the file 'load': colour nibbles at $9800, video matrix at $9C00 and the bitmap already in place at $A000.  Colour RAM has to be copied to $D800 because it is not part of a VIC bank, and the video matrix has to move to $8C00 because the VIC is switched to bank 2 ($8000-$BFFF), where it can see the bitmap at $A000.",
    "In:  title data at $9800 (colour), $9C00 (video matrix), $A000 (bitmap)",
    "Out: multicolour bitmap mode showing the picture; $D800-$DBFF and $8C00-$8FFF filled; $01FE = $04; the code bytes at $C168 and $C1B1 patched; the four copy loops' operands left one page past their start; I = 0",
    "Called from: bootMain $C159."
   ]
  },
  "C242": {
   "unit": "boot/fastLoaderC000",
   "line": "$99 (STA abs,Y) + 1 = $9A (TXS)",
   "block": [
    "Patch one of two: the instruction at $C168 becomes TXS, so bootMain can move the stack pointer without the byte TXS ever appearing in the loaded file."
   ]
  },
  "C245": {
   "unit": "boot/fastLoaderC000",
   "line": "0 into all of them",
   "block": [
    "Silence everything the VIC could do on its own: sprites parked and disabled, interrupt and collision registers cleared."
   ]
  },
  "C247": {
   "unit": "boot/fastLoaderC000",
   "line": "$D000-$D010: eight sprite X/Y pairs plus the X most-significant-bit register"
  },
  "C249": {
   "unit": "boot/fastLoaderC000",
   "line": "park every sprite at 0,0"
  },
  "C24C": {
   "unit": "boot/fastLoaderC000",
   "line": "backwards to $D000"
  },
  "C24D": {
   "unit": "boot/fastLoaderC000",
   "line": "BPL, so index 0 is done too"
  },
  "C24F": {
   "unit": "boot/fastLoaderC000",
   "line": "$D015 = 0: no sprite enabled"
  },
  "C252": {
   "unit": "boot/fastLoaderC000",
   "line": "$D017 = 0: no vertical expansion"
  },
  "C255": {
   "unit": "boot/fastLoaderC000",
   "line": "$D01A-$D01F"
  },
  "C257": {
   "unit": "boot/fastLoaderC000",
   "line": "IRQ mask, sprite priority, sprite multicolour, X expansion and both collision registers"
  },
  "C25A": {
   "unit": "boot/fastLoaderC000",
   "line": "backwards"
  },
  "C25B": {
   "unit": "boot/fastLoaderC000",
   "line": "including index 0"
  },
  "C25D": {
   "unit": "boot/fastLoaderC000",
   "line": "$DD02",
   "block": [
    "Hand the VIC bank 2, $8000-$BFFF, which is where the bitmap already sits."
   ]
  },
  "C260": {
   "unit": "boot/fastLoaderC000",
   "line": "bits 0-1 must be outputs or the bank select does nothing"
  },
  "C262": {
   "unit": "boot/fastLoaderC000",
   "line": "write the direction bits back"
  },
  "C265": {
   "unit": "boot/fastLoaderC000",
   "line": "%00000101: bank bits %01 = bank 2 ($8000-$BFFF), bit 2 = RS-232 TXD idle high"
  },
  "C267": {
   "unit": "boot/fastLoaderC000",
   "line": "ATN, CLK OUT and DATA OUT all released - the serial bus is idle"
  },
  "C26A": {
   "unit": "boot/fastLoaderC000",
   "line": "$04 = page of the fake return address $0460, and four pages to copy",
   "block": [
    "Patch two of three: $04, the high byte of the fake return address, goes straight into the stack page.  X = 4 then doubles as the page count of the colour copy."
   ]
  },
  "C26C": {
   "unit": "boot/fastLoaderC000",
   "line": "$01FE: the high byte the RTS at $C218 will pop"
  },
  "C26F": {
   "unit": "boot/fastLoaderC000",
   "line": "Y = 0"
  },
  "C271": {
   "unit": "boot/fastLoaderC000",
   "line": "the picture's colour nibbles, loaded to $9800 with the rest of the file"
  },
  "C274": {
   "unit": "boot/fastLoaderC000",
   "line": "$D800: colour RAM lives outside the VIC bank, so it has to be copied here"
  },
  "C277": {
   "unit": "boot/fastLoaderC000",
   "line": "next byte"
  },
  "C278": {
   "unit": "boot/fastLoaderC000",
   "line": "a page at a time"
  },
  "C27A": {
   "unit": "boot/fastLoaderC000",
   "line": "self-modifying source page"
  },
  "C27D": {
   "unit": "boot/fastLoaderC000",
   "line": "and destination page"
  },
  "C280": {
   "unit": "boot/fastLoaderC000",
   "line": "four pages"
  },
  "C281": {
   "unit": "boot/fastLoaderC000",
   "line": "$9800-$9BFF -> $D800-$DBFF; only the low nibble of each byte matters"
  },
  "C283": {
   "unit": "boot/fastLoaderC000",
   "line": "$49 (EOR #) - 1 = $48 (PHA)",
   "block": [
    "Patch three: $C1B1 becomes PHA, the instruction that pushes the low byte of the fake return address.  Then the video matrix, which must live inside the VIC bank."
   ]
  },
  "C286": {
   "unit": "boot/fastLoaderC000",
   "line": "four more pages"
  },
  "C288": {
   "unit": "boot/fastLoaderC000",
   "line": "the picture's video matrix (Y came back to 0 from the loop above)"
  },
  "C28B": {
   "unit": "boot/fastLoaderC000",
   "line": "$8C00 = the screen RAM the VIC reads at offset $0C00 of bank 2"
  },
  "C28E": {
   "unit": "boot/fastLoaderC000",
   "line": "next byte"
  },
  "C28F": {
   "unit": "boot/fastLoaderC000",
   "line": "a page at a time"
  },
  "C291": {
   "unit": "boot/fastLoaderC000",
   "line": "self-modifying source page"
  },
  "C294": {
   "unit": "boot/fastLoaderC000",
   "line": "and destination page"
  },
  "C297": {
   "unit": "boot/fastLoaderC000",
   "line": "four pages"
  },
  "C298": {
   "unit": "boot/fastLoaderC000",
   "line": "$9C00-$9FFF -> $8C00-$8FFF"
  },
  "C29A": {
   "unit": "boot/fastLoaderC000",
   "line": "%11011000: multicolour on, 40 columns, x-scroll 0",
   "block": [
    "Turn the picture on."
   ]
  },
  "C29C": {
   "unit": "boot/fastLoaderC000",
   "line": "$D016"
  },
  "C29F": {
   "unit": "boot/fastLoaderC000",
   "line": "%0011 -> video matrix at bank + $0C00, %100 -> bitmap at bank + $2000"
  },
  "C2A1": {
   "unit": "boot/fastLoaderC000",
   "line": "$D018: with bank 2 that means screen $8C00 and bitmap $A000"
  },
  "C2A4": {
   "unit": "boot/fastLoaderC000",
   "line": "%00111011: bitmap mode, display enabled, 25 rows, y-scroll 3"
  },
  "C2A6": {
   "unit": "boot/fastLoaderC000",
   "line": "$D011 - the title picture appears"
  },
  "C2A9": {
   "unit": "boot/fastLoaderC000",
   "line": "interrupts back on: the KERNAL calls that follow expect its IRQ to be running"
  },
  "C2AA": {
   "unit": "boot/fastLoaderC000",
   "line": "back to bootMain with the picture up and both stack patches armed"
  },
  "C2AB": {
   "unit": "boot/fastLoaderC000",
   "line": "filename length 2",
   "routine": [
    "bootDriveInitialize - OPEN 15,8,15,'I0', wait four frames and CLOSE 15, so the drive re-reads the BAM and forgets any state left over from the LOAD of the file 'load'.  Uses the KERNAL, which is still banked in at this point in the boot.",
    "In:  strInitDrive at $C2D8",
    "Out: drive initialised, logical file 15 closed again; A/X/Y clobbered",
    "Called from: bootMain $C16E."
   ]
  },
  "C2AD": {
   "unit": "boot/fastLoaderC000",
   "line": "name pointer high byte"
  },
  "C2AF": {
   "unit": "boot/fastLoaderC000",
   "line": "and low byte: $C2D8 = 'I0'"
  },
  "C2B1": {
   "unit": "boot/fastLoaderC000",
   "line": "SETNAM"
  },
  "C2B4": {
   "unit": "boot/fastLoaderC000",
   "line": "logical file 15"
  },
  "C2B6": {
   "unit": "boot/fastLoaderC000",
   "line": "secondary address 15 = the drive's command channel"
  },
  "C2B7": {
   "unit": "boot/fastLoaderC000",
   "line": "device 8"
  },
  "C2B9": {
   "unit": "boot/fastLoaderC000",
   "line": "SETLFS"
  },
  "C2BC": {
   "unit": "boot/fastLoaderC000",
   "line": "OPEN 15,8,15,'I0' - the drive re-reads the BAM and resets itself"
  },
  "C2BF": {
   "unit": "boot/fastLoaderC000",
   "line": "four frames"
  },
  "C2C1": {
   "unit": "boot/fastLoaderC000",
   "line": "give the drive time to finish before the channel is closed"
  },
  "C2C4": {
   "unit": "boot/fastLoaderC000",
   "line": "logical file 15"
  },
  "C2C6": {
   "unit": "boot/fastLoaderC000",
   "line": "CLOSE - bootBlockExecute opens the command channel again by hand"
  },
  "C2C9": {
   "unit": "boot/fastLoaderC000",
   "line": "back to bootMain"
  },
  "C2CA": {
   "unit": "boot/fastLoaderC000",
   "line": "N = bit 7 of $D012, set on raster lines $80-$FF",
   "routine": [
    "bootWaitFrames - Wait Y video frames by watching bit 7 of the raster register go high and then low again once per frame.  A pure delay: it needs no interrupt, which matters because the callers run with the CIAs in an odd state.",
    "In:  Y = number of frames (0 would mean 256)",
    "Out: Y = 0",
    "Called from: bootDriveInitialize $C2C1 (4 frames) and bootModemInit $C302 (20) and $C314 (60)."
   ]
  },
  "C2CD": {
   "unit": "boot/fastLoaderC000",
   "line": "wait for the beam to reach the lower half of the screen"
  },
  "C2CF": {
   "unit": "boot/fastLoaderC000",
   "line": "watch the same bit"
  },
  "C2D2": {
   "unit": "boot/fastLoaderC000",
   "line": "and wait for it to wrap past $FF: one frame has gone by"
  },
  "C2D4": {
   "unit": "boot/fastLoaderC000",
   "line": "one frame fewer"
  },
  "C2D5": {
   "unit": "boot/fastLoaderC000",
   "line": "round again"
  },
  "C2D7": {
   "unit": "boot/fastLoaderC000",
   "line": "back to the caller, Y = 0"
  },
  "C2D8": {
   "unit": "boot/fastLoaderC000",
   "line": "'I' = initialise, '0' = drive 0 of what might be a dual unit; the length is passed to SETNAM, so this string needs no terminator"
  },
  "C2DA": {
   "unit": "boot/fastLoaderC000",
   "line": "C = 0 selects 'set' rather than 'read' in the MEMTOP call",
   "routine": [
    "bootModemInit - Opens the KERNAL's RS-232 device at 300 baud, sends a carriage return and then the Hayes string 'ATE0S0=0' so that a modem sitting on the user port stops echoing and will not answer the phone during the load.  Harmless when no modem is connected.  MEMTOP is lowered to $9700 first, because OPENing device 2 makes the KERNAL carve its two 256-byte RS-232 buffers out of the top of memory - which would otherwise land inside the file that has just been loaded to $9800-$C3FF.",
    "In:  rs232Params at $C320, strHayesInit at $C322",
    "Out: KERNAL MEMTOP = $9700, logical file 2 opened and closed again, output channel back to the screen",
    "Called from: bootMain $C16B."
   ]
  },
  "C2DB": {
   "unit": "boot/fastLoaderC000",
   "line": "high byte"
  },
  "C2DD": {
   "unit": "boot/fastLoaderC000",
   "line": "low byte: top of memory = $9700"
  },
  "C2DF": {
   "unit": "boot/fastLoaderC000",
   "line": "so OPEN puts the RS-232 buffers at $9500-$96FF, clear of the loaded file at $9800"
  },
  "C2E2": {
   "unit": "boot/fastLoaderC000",
   "line": "two 'filename' bytes = the RS-232 control and command registers"
  },
  "C2E4": {
   "unit": "boot/fastLoaderC000",
   "line": "pointer high byte"
  },
  "C2E6": {
   "unit": "boot/fastLoaderC000",
   "line": "and low byte: rs232Params"
  },
  "C2E8": {
   "unit": "boot/fastLoaderC000",
   "line": "SETNAM"
  },
  "C2EB": {
   "unit": "boot/fastLoaderC000",
   "line": "logical file 2"
  },
  "C2ED": {
   "unit": "boot/fastLoaderC000",
   "line": "device 2 = the KERNAL's RS-232 pseudo device"
  },
  "C2EE": {
   "unit": "boot/fastLoaderC000",
   "line": "secondary address 0"
  },
  "C2F0": {
   "unit": "boot/fastLoaderC000",
   "line": "SETLFS"
  },
  "C2F3": {
   "unit": "boot/fastLoaderC000",
   "line": "OPEN 2,2,0,CHR$(6)+CHR$(0): 300 baud, 8N1, 3-line handshake"
  },
  "C2F6": {
   "unit": "boot/fastLoaderC000",
   "line": "logical file 2"
  },
  "C2F8": {
   "unit": "boot/fastLoaderC000",
   "line": "everything printed from here goes to the modem"
  },
  "C2FB": {
   "unit": "boot/fastLoaderC000",
   "line": "carriage return"
  },
  "C2FD": {
   "unit": "boot/fastLoaderC000",
   "line": "flush any half-typed command sitting in the modem's line buffer"
  },
  "C300": {
   "unit": "boot/fastLoaderC000",
   "line": "20 frames"
  },
  "C302": {
   "unit": "boot/fastLoaderC000",
   "line": "one character at 300 baud takes about two frames; give it room"
  },
  "C305": {
   "unit": "boot/fastLoaderC000",
   "line": "start of the string",
   "block": [
    "Then the command itself, byte by byte until the 0 terminator."
   ]
  },
  "C307": {
   "unit": "boot/fastLoaderC000",
   "line": "'ATE0S0=0' and a carriage return"
  },
  "C30A": {
   "unit": "boot/fastLoaderC000",
   "line": "0 ends the string"
  },
  "C30C": {
   "unit": "boot/fastLoaderC000",
   "line": "out it goes"
  },
  "C30F": {
   "unit": "boot/fastLoaderC000",
   "line": "next character"
  },
  "C310": {
   "unit": "boot/fastLoaderC000",
   "line": "always taken - the terminator is what ends the loop"
  },
  "C312": {
   "unit": "boot/fastLoaderC000",
   "line": "60 frames, about a second"
  },
  "C314": {
   "unit": "boot/fastLoaderC000",
   "line": "the whole string must be clocked out before the channel is closed"
  },
  "C317": {
   "unit": "boot/fastLoaderC000",
   "line": "output back to the screen"
  },
  "C31A": {
   "unit": "boot/fastLoaderC000",
   "line": "logical file 2"
  },
  "C31C": {
   "unit": "boot/fastLoaderC000",
   "line": "CLOSE - the game installs its own software UART at $E000 later"
  },
  "C31F": {
   "unit": "boot/fastLoaderC000",
   "line": "back to bootMain"
  },
  "C320": {
   "unit": "boot/fastLoaderC000",
   "line": "control byte $06 = 300 baud, 8 data bits, 1 stop bit; command byte $00 = 3-line handshake, full duplex, no parity"
  },
  "C322": {
   "unit": "boot/fastLoaderC000",
   "line": "AT = attention, E0 = stop echoing what is sent"
  },
  "C326": {
   "unit": "boot/fastLoaderC000",
   "line": "S0=0 = register 0 zero rings, that is, never auto-answer; then CR and the 0 terminator"
  },
  "C32C": {
   "unit": "boot/fastLoaderC000",
   "line": "the KERNAL's serial routines are bit-banged and cannot be interrupted",
   "routine": [
    "bootBlockExecute - Installs the fast loader in the 1541.  It opens channel 2 on the drive with the filename '#', which makes the DOS hand that channel a free 256-byte buffer, and then sends 'B-E 2 0 1 17' on the command channel: block-execute channel 2, drive 0, track 1, sector 17.  The drive reads that sector into the buffer and jumps into it; the sector is the bootstrap that reads track 1 sectors 18-20 to $0300-$05FF and jumps there (disassembly/drive/driveFastLoader.s).  From that moment the drive answers the two-bit protocol instead of the normal serial one.",
    "In:  strBufferChannel and strBlockExecute at $C35F, device number in zp $BA",
    "Out: the drive is running the fast loader; CPU_PORT restored; I = 1",
    "Called from: bootMain $C171."
   ]
  },
  "C32D": {
   "unit": "boot/fastLoaderC000",
   "line": "the current memory configuration"
  },
  "C32F": {
   "unit": "boot/fastLoaderC000",
   "line": "keep it - bootSendDosCommand puts it back"
  },
  "C332": {
   "unit": "boot/fastLoaderC000",
   "line": "bits 0-1 in = BASIC and KERNAL ROM visible, which the IEC routines need (they already are: $01 is still $37 here, so this is belt and braces)"
  },
  "C334": {
   "unit": "boot/fastLoaderC000",
   "line": "write it back"
  },
  "C336": {
   "unit": "boot/fastLoaderC000",
   "line": "secondary address $F0 + channel 2 = OPEN channel 2"
  },
  "C338": {
   "unit": "boot/fastLoaderC000",
   "line": "offset $0D into strBlockExecute is strBufferChannel, the '#'"
  },
  "C33A": {
   "unit": "boot/fastLoaderC000",
   "line": "OPEN 2,8,2,'#': give channel 2 one of the drive's buffers"
  },
  "C33D": {
   "unit": "boot/fastLoaderC000",
   "line": "secondary address $F0 + channel 15 = OPEN on the command channel",
   "block": [
    "Second command, sent by falling straight into the same routine: the block-execute itself."
   ]
  },
  "C33F": {
   "unit": "boot/fastLoaderC000",
   "line": "offset 0 = 'B-E 2 0 1 17'; falls through into bootSendDosCommand"
  },
  "C341": {
   "unit": "boot/fastLoaderC000",
   "line": "keep the secondary address across the LISTEN",
   "routine": [
    "bootSendDosCommand - Sends one 0-terminated string to the drive as an OPEN on the given channel: LISTEN the current device, SECOND with the secondary address, CIOUT every byte up to the terminator, UNLSN.  Finishes by restoring the memory configuration bootBlockExecute saved.",
    "In:  A = secondary address byte ($F0 + channel), Y = offset into strBlockExecute, zp $BA = device number, bootSavedCpuPort",
    "Out: the command has been sent and acted on; CPU_PORT restored; A/X/Y clobbered",
    "Called from: bootBlockExecute $C33A by JSR, and again by falling through from $C33F."
   ]
  },
  "C342": {
   "unit": "boot/fastLoaderC000",
   "line": "zp $BA is the KERNAL's current device number, 8 here; the game's name for that byte is unrelated"
  },
  "C344": {
   "unit": "boot/fastLoaderC000",
   "line": "make the drive listen"
  },
  "C347": {
   "unit": "boot/fastLoaderC000",
   "line": "the secondary address back"
  },
  "C348": {
   "unit": "boot/fastLoaderC000",
   "line": "$F0 + channel means OPEN, so what follows is the filename or command text"
  },
  "C34B": {
   "unit": "boot/fastLoaderC000",
   "line": "one byte of the string"
  },
  "C34E": {
   "unit": "boot/fastLoaderC000",
   "line": "0 ends it"
  },
  "C350": {
   "unit": "boot/fastLoaderC000",
   "line": "send it on the serial bus"
  },
  "C353": {
   "unit": "boot/fastLoaderC000",
   "line": "next byte"
  },
  "C354": {
   "unit": "boot/fastLoaderC000",
   "line": "always taken"
  },
  "C356": {
   "unit": "boot/fastLoaderC000",
   "line": "UNLISTEN closes the OPEN, which is when the drive actually acts on the command"
  },
  "C359": {
   "unit": "boot/fastLoaderC000",
   "line": "the memory configuration from before"
  },
  "C35C": {
   "unit": "boot/fastLoaderC000",
   "line": "put it back ($37 at this point, so the ROMs stay in for the next command)"
  },
  "C35E": {
   "unit": "boot/fastLoaderC000",
   "line": "back to bootBlockExecute, or to bootMain when this was the fall-through call"
  },
  "C35F": {
   "unit": "boot/fastLoaderC000",
   "line": "block-execute: channel 2, drive 0, track 1, sector 17"
  },
  "C367": {
   "unit": "boot/fastLoaderC000",
   "line": "sector 17 is the drive bootstrap that pulls sectors 18-20 into $0300-$05FF and jumps there"
  },
  "C36C": {
   "unit": "boot/fastLoaderC000",
   "line": "filename '#' with no number = give this channel any free buffer"
  },
  "C36E": {
   "unit": "boot/fastLoaderC000",
   "line": "padding"
  },
  "C371": {
   "unit": "boot/fastLoaderC000",
   "line": "bootSavedCpuPort: the $01 value bootBlockExecute found, $37 with both ROMs in"
  },
  "C372": {
   "unit": "boot/fastLoaderC000",
   "line": "stray byte, probably the tail of an instruction of the leftover code that used to sit here"
  },
  "C373": {
   "unit": "boot/fastLoaderC000",
   "line": "unreachable leftovers of the shared EA loader below: they reference $BE65-$BE6D and $BCC8, which do not exist in this build"
  },
  "C3C0": {
   "unit": "boot/fastLoaderC000",
   "line": "'OZ' is the id of the SPORT OF WAR game disk, 'EA' that of a data disk; the live copy of this table is diskIdTable at $0FAD"
  },
  "C3C4": {
   "unit": "boot/fastLoaderC000",
   "line": "zp $04/$05 were that build's destination pointer",
   "routine": [
    "leftoverCheckDiskId - Unreachable, and truncated by the end of the file: the original of the game's checkDiskId ($0FB1), left behind from a build in which the loader lived at $CBxx and read the BAM to $BA00.  It compares the two disk id characters of the BAM against bootDiskIdTable ('OZ' = the game disk, 'EA' = a data disk) and would return the matching index in zp $18.  The two JSRs now land in the middle of message strings of the runtime overlay, and both exits branch to $C411, past the end of the file.",
    "In:  bootDiskIdTable, a BAM image at $BA00 (in that build)",
    "Out: zp $18 = matched index, or a branch to nowhere (dead code)",
    "Called from: nowhere.",
    "(confidence: medium)"
   ]
  },
  "C3C7": {
   "unit": "boot/fastLoaderC000",
   "line": "save the low half"
  },
  "C3C8": {
   "unit": "boot/fastLoaderC000",
   "line": "and the high half"
  },
  "C3CB": {
   "unit": "boot/fastLoaderC000",
   "line": "on the stack"
  },
  "C3CC": {
   "unit": "boot/fastLoaderC000",
   "line": "destination low byte"
  },
  "C3CE": {
   "unit": "boot/fastLoaderC000",
   "line": "destination page $BA"
  },
  "C3D0": {
   "unit": "boot/fastLoaderC000",
   "line": "in that build $CBED was setDest; here it is the middle of a message string"
  },
  "C3D3": {
   "unit": "boot/fastLoaderC000",
   "line": "one sector"
  },
  "C3D5": {
   "unit": "boot/fastLoaderC000",
   "line": "track 18"
  },
  "C3D7": {
   "unit": "boot/fastLoaderC000",
   "line": "sector 0 = the BAM"
  },
  "C3D9": {
   "unit": "boot/fastLoaderC000",
   "line": "and $CBF2 was that build's bootLoadSectors"
  },
  "C3DC": {
   "unit": "boot/fastLoaderC000",
   "line": "start at table entry 1, the 'EA' pair"
  },
  "C3DE": {
   "unit": "boot/fastLoaderC000",
   "line": "zp $18 = byte offset into bootDiskIdTable"
  },
  "C3E0": {
   "unit": "boot/fastLoaderC000",
   "line": "offset of the pair being tested"
  },
  "C3E2": {
   "unit": "boot/fastLoaderC000",
   "line": "point at its second character"
  },
  "C3E3": {
   "unit": "boot/fastLoaderC000",
   "line": "second disk id byte of the BAM"
  },
  "C3E6": {
   "unit": "boot/fastLoaderC000",
   "line": "compare"
  },
  "C3E9": {
   "unit": "boot/fastLoaderC000",
   "line": "no match: try the other id"
  },
  "C3EB": {
   "unit": "boot/fastLoaderC000",
   "line": "back to the first character"
  },
  "C3EC": {
   "unit": "boot/fastLoaderC000",
   "line": "first disk id byte"
  },
  "C3EF": {
   "unit": "boot/fastLoaderC000",
   "line": "compare"
  },
  "C3F2": {
   "unit": "boot/fastLoaderC000",
   "line": "both characters matched"
  },
  "C3F4": {
   "unit": "boot/fastLoaderC000",
   "line": "step down two bytes..."
  },
  "C3F6": {
   "unit": "boot/fastLoaderC000",
   "line": "...to the previous entry, 'OZ'"
  },
  "C3F8": {
   "unit": "boot/fastLoaderC000",
   "line": "only two entries in the table"
  },
  "C3FA": {
   "unit": "boot/fastLoaderC000",
   "line": "neither id matched - branches past the end of this file"
  },
  "C3FC": {
   "unit": "boot/fastLoaderC000",
   "line": "offset / 2 = the index: 0 = 'OZ' game disk, 1 = 'EA' data disk"
  },
  "C3FE": {
   "unit": "boot/fastLoaderC000",
   "line": "'EA' would go somewhere outside this file; the listing stops here mid-routine"
  }
 }
}