{
 "_comment": "generated by tools/mergeSurvey.py from survey/*.json - do not edit by hand",
 "notes": {
  "0300": {
   "routine": [
    "fastloaderMain - Entry point of the 1541 fast loader; the bootstrap sector (T1 s17) reads T1 s18-20 to $0300-$05FF and JMPs here. Idles the serial lines, sets the default track/sector ($06=18,$07=0), issues disk-controller job $B0 on buffer 0 and waits for it to finish, then falls into commandDispatchLoop.",
    "In:  none (entered from the bootstrap)",
    "Out: zp_06=$12, zp_07=$00, zp_00 job issued; falls through to $0313"
   ],
   "unit": "drive/driveFastLoader"
  },
  "0313": {
   "routine": [
    "commandDispatchLoop - Top of the drive command loop. Resets the stack (SP=$45), receives a command byte from the host via receiveCommand, and dispatches: $C0 resets the drive (JMP through reset vector), $60 goes to handleWriteCommand, anything else is a read (receive track and sector, read the sector, send it back, then report status 0). Re-entered after every operation.",
    "In:  command byte from receiveCommand",
    "Out: zp_50=0; branches to read/write/reset handlers"
   ],
   "unit": "drive/driveFastLoader"
  },
  "0342": {
   "routine": [
    "setHeadReadMode - Sets VIA2 PCR bits 1-3 (ORA #$0E) to put the read/write head electronics into read mode. Called by prepareHeadAndVerifyId before reading or writing a sector.",
    "In:  VIA2_PCR",
    "Out: VIA2_PCR updated",
    "(confidence: medium)"
   ],
   "unit": "drive/driveFastLoader"
  },
  "034B": {
   "routine": [
    "exchangeByteWithHost - Core full-duplex serial byte transfer with the C64. Clocks the byte in A out to the host one bit at a time (LSB first) on VIA1 serial bit1 while simultaneously shifting a received byte in on bit0, using a two-phase CLK/ACK handshake on bits 2/3. This is the drive-side counterpart of the C64 routine sendByteToDrive ($089C). Used for all command/parameter bytes, the status byte, and (in the write path) the 256 data bytes from the host.",
    "In:  A = byte to send (into zp_90)",
    "Out: A = byte received from host (zp_8F); uses zp_14 as handshake temp; X clobbered"
   ],
   "unit": "drive/driveFastLoader"
  },
  "0391": {
   "routine": [
    "sendStatusAndLoop - Sends the status/error byte in A to the host via exchangeByteWithHost, then JMPs back to commandDispatchLoop. Every error path (header not found=$02, checksum=$05, write protect/id error=$08) jumps here with its code in A; the normal read path jumps here with A=0.",
    "In:  A = status byte",
    "Out: status sent to host; loops to $0313"
   ],
   "unit": "drive/driveFastLoader"
  },
  "0397": {
   "routine": [
    "seekSectorAndSyncData - Finds the requested sector header on disk (findSectorHeader) then waits for and reads into the data-block sync (waitForSyncByte). Called at the start of readSectorFromDisk to position the read at the sector's data field.",
    "In:  zp_3D drive index, zp_12/zp_13, zp_32 header pointer",
    "Out: positioned at data field; V clear, Y=0; error $02 via sendStatusAndLoop",
    "(confidence: medium)"
   ],
   "unit": "drive/driveFastLoader"
  },
  "039D": {
   "routine": [
    "findSectorHeader - Builds the expected 8-byte GCR header image (from the id bytes zp_12/zp_13, the track/sector bytes via (zp_32), and their XOR checksum) with ROM_MAKE_HEADER_GCR into the zp_24 buffer, then reads raw GCR bytes from VIA2 comparing 8 bytes at a time. Returns when the matching header passes under the head; after $5A failed tries reports error $02. Also used by writeSectorToDisk to locate the sector before writing.",
    "In:  zp_3D drive index, zp_12/zp_13 disk id bytes, zp_32 header ptr",
    "Out: header located; zp_16-zp_1A header scratch; error $02 via sendStatusAndLoop",
    "(confidence: medium)"
   ],
   "unit": "drive/driveFastLoader"
  },
  "03E3": {
   "routine": [
    "waitForSyncByte - Arms VIA1 timer 1 as a timeout, waits for a SYNC mark on VIA2 (bit 7 of the disk-control port), then reads the first byte after sync from VIA2 and returns it. On timeout it branches to the error path ($02 via sendStatusAndLoop). Clears V and Y before returning.",
    "In:  VIA1_T1, VIA2 disk port",
    "Out: A = byte after sync, V clear, Y=0; timeout -> error",
    "(confidence: medium)"
   ],
   "unit": "drive/driveFastLoader"
  },
  "03FB": {
   "routine": [
    "readSectorFromDisk - Reads the sector named by zp_06/zp_07 from disk into buffer $0600: waits for the buffer-0 job, verifies the head/id (prepareHeadAndVerifyId), seeks to the data field (seekSectorAndSyncData), reads 256 GCR data bytes into $0600 plus the GCR overflow into $0100, converts GCR->binary and verifies the data checksum against zp_3A (error $05 on mismatch), then falls through to cryptSectorBuffer to decrypt. Invoked by the read branch of the command loop.",
    "In:  zp_06 track, zp_07 sector, zp_31 buffer page",
    "Out: decrypted sector in $0600; zp_3A checksum; error $05 via sendStatusAndLoop"
   ],
   "unit": "drive/driveFastLoader"
  },
  "044B": {
   "routine": [
    "sendSectorToHost - Sends the 256 decrypted bytes in buffer $0600 back to the C64. Each byte is split into two nibbles, each nibble translated through nibbleSendTable and clocked out 4 bits at a time on the VIA1 serial port with a handshake (bit0 = clock). This bulk path is received by the C64 routine receiveSectorData ($0950). Ends via finishSendByte.",
    "In:  $0600 buffer (256 decrypted bytes), nibbleSendTable",
    "Out: 256 bytes clocked to host; VIA1 serial port"
   ],
   "unit": "drive/driveFastLoader"
  },
  "0490": {
   "routine": [
    "handleWriteCommand - Write-command ($60) handler: receives the track and sector bytes from the host into zp_06/zp_07, calls writeSectorToDisk, then returns to commandDispatchLoop.",
    "In:  track/sector from host via exchangeByteWithHost",
    "Out: sector written; loops to $0313"
   ],
   "unit": "drive/driveFastLoader"
  },
  "04A0": {
   "routine": [
    "writeSectorToDisk - Receives 256 data bytes from the host into $0600, encrypts them in place (cryptSectorBuffer, symmetric with decrypt), computes the data checksum into zp_3A, checks the write-protect sense (VIA2 bit 4; error $08 if protected), converts binary->GCR, positions on the header (findSectorHeader), switches VIA2 to write mode, writes the GCR overflow ($0100) and data (zp_30) fields, restores read mode, and sends the status byte. Called by handleWriteCommand.",
    "In:  256 bytes from host, zp_06/zp_07, zp_30 buffer ptr",
    "Out: sector written to disk, zp_3A checksum; error $08 via sendStatusAndLoop"
   ],
   "unit": "drive/driveFastLoader"
  },
  "0525": {
   "routine": [
    "prepareHeadAndVerifyId - Selects read mode (setHeadReadMode) and validates the drive id bytes: if zp_12 EOR zp_13 EOR $15 is nonzero it branches to the write-protect/id error path ($08). Called at the start of both readSectorFromDisk and writeSectorToDisk. The exact meaning of the $15 constant is a DOS id/consistency check.",
    "In:  zp_12, zp_13",
    "Out: read mode set; error $08 via sendStatusAndLoop on mismatch",
    "(confidence: low)"
   ],
   "unit": "drive/driveFastLoader"
  },
  "0531": {
   "routine": [
    "cryptSectorBuffer - The sector cipher: XORs the 256 bytes in $0600 with a self-modifying rolling key (key0 = ROL((track|$C0)<<1 + sector + carry); key[y]=ROL(y EOR key)). Track 18 ($12) is left in plaintext (BAM/directory/map/setup). Symmetric, so the same routine decrypts on read and encrypts on write. This is the cipher documented in overview section 4.",
    "In:  $0600 buffer, zp_06 track, zp_07 sector",
    "Out: $0600 XORed in place (except track 18)"
   ],
   "unit": "drive/driveFastLoader"
  },
  "0553": {
   "routine": [
    "uploadBytesToDrive - Code/data injection loop reached from receiveCommand when the host sends a count byte >= $F0: receives (256 - count) bytes and stores them consecutively into drive RAM starting at $0580 (the RTS-filled scratch area) using self-modifying stores, then falls through into receiveCommand again to fetch the real command. Lets the host push a small routine or table into the drive.",
    "In:  count in patched $057E, bytes from host",
    "Out: bytes written to $0580+; re-enters receiveCommand",
    "(confidence: medium)"
   ],
   "unit": "drive/driveFastLoader"
  },
  "0561": {
   "routine": [
    "receiveCommand - Receives one command/parameter byte from the host: clears a VIA2 status bit, sends $01 and receives a byte via exchangeByteWithHost, patches it into the LDA at $057D. If the byte is >= $F0 it is a byte-count preamble and control drops to uploadBytesToDrive to inject that many bytes into $0580 before looping back for the real command; otherwise the received byte is returned in A.",
    "In:  host serial input",
    "Out: A = command byte",
    "(confidence: medium)"
   ],
   "unit": "drive/driveFastLoader"
  },
  "05DE": {
   "routine": [
    "finishSendByte - Tail of sendSectorToHost: writes the final value in A to the VIA1 serial port and returns (leaves the serial lines in the idle state after a bulk read).",
    "In:  A, VIA1 serial port",
    "Out: VIA1 serial port set; RTS",
    "(confidence: medium)"
   ],
   "unit": "drive/driveFastLoader"
  },
  "05E2": {
   "routine": [
    "checksumBufferAndReport - Small 6502 routine that the disassembler classified as data (it sits after an RTS and has no in-file caller). $05E2 does JSR $05E8 / JMP sendStatusAndLoop; $05E8 sums the 256 bytes of $0600 with a ROL+ADC accumulator and returns. Most likely a verify/checksum helper invoked by code injected via uploadBytesToDrive, or dead code. Followed by a hidden signature.",
    "In:  $0600 buffer",
    "Out: A = rolling checksum, reported to host via sendStatusAndLoop",
    "(confidence: low)"
   ],
   "unit": "drive/driveFastLoader"
  },
  "043B": {
   "unit": "drive/driveFastLoader",
   "block": [
    "nibbleSendTable: byteTable, 16 bytes. 16-entry nibble translation table used by sendSectorToHost. Entry n = complement of the nibble n with its two outer bits swapped (0->$0F,1->$07,2->$0D,...). Encodes each nibble into the 4 serial bits sent to the C64 during a bulk read."
   ]
  },
  "0582": {
   "unit": "drive/driveFastLoader",
   "block": [
    "driveUploadScratch: byteTable, 92 bytes. $0582-$05DD filled with $60 (RTS). This is the RTS-filled scratch/patch area; uploadBytesToDrive stores injected bytes starting at $0580 into this region. Pre-filling with RTS makes any un-overwritten byte a harmless return."
   ]
  },
  "05F4": {
   "unit": "drive/driveFastLoader",
   "block": [
    "hiddenSignature: text, 12 bytes. Hidden signature bytes at the very end of the loader: ASCII fragments 'DaN' and 'SaRa' (Dan Bunten / family names) mixed with a couple of opcode-looking bytes. Not referenced; an easter-egg credit like the EA message in the bootstrap sector."
   ]
  }
 }
}