{ "chunk": "boot_c128boot_T1S0_0B00", "unit": "boot/c128boot_T1S0", "routines": [ {"addr": "0B27", "name": "c128BootEntry", "summary": "Real entry point of the C128 auto-boot sector. The C128 KERNAL BOOT_CALL ($F890, checked in kernal-318020-05) reads track 1 sector 0 to $0B00, prints BOOTING + the message, parses the filename field, then calls the code with JSRFAR in bank 15. Because the byte after the message terminator is $A0 (the opcode of the LDY #$00 at $0B25), the KERNAL treats it as a one-character filename, tries to LOAD \"0:\"+$A0 (fails silently, result ignored) and sets the entry to $0B27 - verified in x128: the breakpoint at $0B25 never fires, $0B27 is hit with A=X=Y=0. The routine copies 256 bytes from $0B89 to $8000-$80FF (the CBM80 pseudo-cartridge, see boot/c64cart_8000.s), opens the drive command channel, sends \"U0>M0\" (1571 -> 1541 mode) and \"U0>H0\" (head/side 0), writes 0 to the MMU configuration register $FF00 (bank 15) and JMPs to the KERNAL GO64 entry $FF4D, which performs a C64-mode reset; the C64 KERNAL then finds CBM80 at $8004 and runs $8009.", "inputs": "Y must be 0 at entry for the full 256-byte copy. It is, but only because JSRFAR loads Y from the register image at $07, which is zero after a cold reset; the LDY #$00 at $0B25 is never executed.", "outputs": "$8000-$80FF <- $0B89-$0C88 (only $8000-$8076 are meaningful; the rest is C128 RS-232 buffer contents); logical file 15 opened to device 8 secondary 15; drive switched to 1541 mode; $FF00=0; never returns (C64 mode reset).", "confidence": "high"}, {"addr": "0B45", "name": "c128OpenCommandChannel", "summary": "SETNAM with length 0, SETLFS lfn 15 / device 8 / secondary 15, then JMP KERNAL_OPEN: opens the disk command channel. Called from c128BootEntry ($0B30) and again from the C64-mode cartridge code at $8043, which also relies on the SETLFS values it leaves behind for its later KERNAL_LOAD of \"0:EA\".", "inputs": "none", "outputs": "Logical file 15 open to device 8 secondary 15; A/X/Y clobbered; C = KERNAL_OPEN error flag (ignored by callers).", "confidence": "high"}, {"addr": "0B55", "name": "c128SendDriveCommand", "summary": "Sends one of three zero-terminated DOS command strings on the already open command channel. A is doubled to index the word table driveCmdPtrTable ($0B75); the pointer goes to $FB/$FC; CHKOUT lfn 15, CIOUT every byte up to (not including) the $00, then JMP KERNAL_CLRCHN. Index 0 = \"U0>M0\" (1571: 1541 mode), 1 = \"U0>H0\" (1571: head 0), 2 = \"I\" (initialise). Called with 0 and 1 by c128BootEntry and with 2 by the C64-mode cartridge code at $8048.", "inputs": "A = command index 0..2 (pre-doubled to a word-table offset in X); command channel 15 must be open.", "outputs": "$FB/$FC = pointer to the string; X/Y clobbered (Y = string length on exit); output channel restored to the screen by CLRCHN.", "confidence": "high"}, {"addr": "0B68", "name": "sendDriveCommandLoop", "summary": "Loop label inside c128SendDriveCommand: LDA ($FB),Y / BEQ done / CIOUT / INY. Not a callable routine.", "inputs": "($FB),Y = next string byte", "outputs": "Y advanced; exits to $0B72 at the $00 terminator", "confidence": "high"}, {"addr": "0B72", "name": "sendDriveCommandDone", "summary": "Exit of c128SendDriveCommand: JMP KERNAL_CLRCHN, which unlistens the drive (terminating the command) and restores the default channels.", "inputs": "none", "outputs": "Default I/O channels restored", "confidence": "high"} ], "variables": [ {"addr": "FB", "scope": "zp", "name": "driveCmdStrPtr", "meaning": "Low byte of the pointer to the current DOS command string (set from driveCmdPtrTable by c128SendDriveCommand, used by LDA ($FB),Y). Boot-sector only; the game itself never touches $FB/$FC (XREF zero-page list ends at $F8).", "confidence": "high"}, {"addr": "FC", "scope": "zp", "name": "driveCmdStrPtrHi", "meaning": "High byte of driveCmdStrPtr (always $0B here).", "confidence": "high"}, {"addr": "FF00", "scope": "abs", "name": "mmuConfigReg", "meaning": "C128 MMU configuration register (only exists on a C128). c128BootEntry writes 0 = bank 15 (BASIC/KERNAL ROM + I/O visible) right before calling GO64; defensive, since BOOT_CALL already runs in bank 15.", "confidence": "high"}, {"addr": "FF4D", "scope": "abs", "name": "kernalGo64", "meaning": "C128 KERNAL jump-table entry GO64: switches the MMU to C64 mode and performs a C64 reset. Final destination of the boot sector; execution resumes at $8009 through the CBM80 cold-start vector.", "confidence": "high"}, {"addr": "8000", "scope": "abs", "name": "cartHeader", "meaning": "Destination of the 256-byte copy: the C64 pseudo-cartridge image (cold/warm vectors $8009, signature CBM80). Same address is named cartHeader in the boot/c64cart_8000 survey.", "confidence": "high"} ], "dataBlocks": [ {"addr": "0B00", "length": 7, "type": "byteTable", "name": "c128BootHeader", "description": "C128 boot-sector header: signature \"CBM\" ($0B00-$0B02), load address $0000 for additional blocks ($0B03/$0B04), bank 0 ($0B05), 0 additional blocks ($0B06). The KERNAL copies $0B03-$0B06 to $AC-$AF."}, {"addr": "0B07", "length": 30, "type": "text", "name": "c128BootMessage", "description": "\"AN ELECTRONIC ARTS PRODUCTION\" plus $00 terminator at $0B24; the C128 prints BOOTING + this text + \"...\". At run time the KERNAL overwrites $0B23/$0B24 with the drive-number prefix \"0:\" for the filename it thinks follows (observed in x128: $0B20 = \"TIO0:\" $A0 $00)."}, {"addr": "0B25", "length": 2, "type": "unknown", "name": "c128BootFileName", "description": "Intended as the empty-filename terminator + first instruction LDY #$00 ($A0 $00), but the KERNAL parses the $A0 as a one-character filename and the $00 as its terminator. The two bytes are never executed; see misclassified."}, {"addr": "0B75", "length": 6, "type": "addrTable", "name": "driveCmdPtrTable", "description": "Three little-endian pointers to the DOS command strings: $0B7B, $0B81, $0B87. Indexed by 2*A in c128SendDriveCommand (lo via $0B75,X, hi via $0B76,X - hence the disassembler's split labels D_0B75/D_0B76)."}, {"addr": "0B7B", "length": 6, "type": "text", "name": "driveCmdSet1541Mode", "description": "\"U0>M0\",$00 - 1571 command: switch to 1541 mode (the game's drive-resident fast loader and cipher code run at $0300-$05FF in 1541 mode). Harmless on a 1541 (error 31, never read)."}, {"addr": "0B81", "length": 6, "type": "text", "name": "driveCmdSelectHead0", "description": "\"U0>H0\",$00 - 1571 command: select head 0 (side 0 of the disk)."}, {"addr": "0B87", "length": 2, "type": "text", "name": "driveCmdInitialize", "description": "\"I\",$00 - DOS initialise; sent by the C64-mode cartridge code ($8046: LDA #2 / JSR $0B55), not by the C128-mode code."}, {"addr": "0B89", "length": 107, "type": "unknown", "name": "c64CartImage", "description": "Bytes $89-$F3 of the sector = the CBM80 pseudo-cartridge ($8000-$806A after the copy): vectors $8009/$8009, \"CBM80\", then the C64-mode start-up code that IOINITs, clears low RAM, sets MEMTOP, opens the command channel via $0B45, sends \"I\" via $0B55, SETNAM \"0:EA\", LOADs it and JMPs to $02B8. Disassembled in boot/c64cart_8000.s; do not annotate as code here."}, {"addr": "0BF4", "length": 12, "type": "unknown", "name": "sectorTailFiller", "description": "$00 then 8E 72 17 8E 73 17 8E 70 17 8E 71 (fragments of STX $1772/$1773/$1770/$1771): unused tail of the sector, leftover from the mastering buffer; lands at $806B-$8076 and is never referenced."} ], "misclassified": [ {"addr": "0B25", "length": 2, "actual": "data", "evidence": "The C128 KERNAL BOOT_CALL ($F948-$F954 in kernal-318020-05) scans for the filename from the byte after the message terminator and sets the JSRFAR target to the byte after the filename's $00, i.e. $0B27. Verified in x128 (true drive, 1571): a breakpoint at $0B25 never triggers, $0B27 is entered with Y=0 and $04=$27. The LDY #$00 is dead; the bytes act as the filename field ($A0 = shifted space). Rendering them as .byte $A0,$00 with the routine header on L_0B27 would be more accurate, though the current listing assembles identically."} ], "insights": [ "C128 boot path: KERNAL BOOT_CALL loads T1S0 to $0B00, prints \"BOOTING AN ELECTRONIC ARTS PRODUCTION...\", attempts (and fails) to LOAD a bogus one-character file because the boot sector lacks the empty-filename $00 byte, then JSRFARs to $0B27. The sector copies its bytes $89-$FF to $8000 as a CBM80 cartridge, puts a 1571 into 1541 mode (U0>M0, U0>H0), sets $FF00=0 and calls GO64 ($FF4D). The C64 KERNAL reset sees CBM80 and runs $8009 (boot/c64cart_8000.s), which reuses $0B45/$0B55 still sitting in RAM, loads \"0:EA\" and jumps to $02B8 (eaLoadGame).", "The entry bug is benign only because JSRFAR takes Y from the register image at $07, which the C128 reset leaves at 0; the copy loop then covers all 256 bytes. With any other Y the CBM80 signature would not be copied and the C128 would drop into C64 BASIC.", "The only zero-page locations used are $FB/$FC (DOS command string pointer); nothing in this sector persists into the game except the RAM copies at $0B45-$0B8A, which the C64-mode cartridge code calls before the fast loader overwrites $0800-$6EFF. After that, $0B45-$0BFF belongs to the main program (loadCommTailBuild1 at $0B4F etc.) and is unrelated.", "1571 commands: \"U0>M0\" forces 1541 (1 MHz, single-sided) mode so the drive-side fast loader/cipher code on track 1 s17-20 behaves identically on 1541 and 1571; \"U0>H0\" selects head 0. A plain 1541 answers these with a syntax error that is never read.", "The copy loop takes 256 bytes starting at $0B89, so $0C00-$0C88 (C128 RS-232 buffer area) also lands at $8077-$80FF; only $8000-$8076 is meaningful and $806B-$80FF is never referenced by the cartridge code (the runtime overlay variants A/B later overwrite $8000-$87FF anyway).", "The KERNAL patches the boot buffer in place: $0B23/$0B24 become \"0:\" (drive number + colon) when it builds the filename, so a RAM dump taken after boot shows \"AN ELECTRONIC ARTS PRODUCTIO0:\" rather than the text on disk.", "Verification used ~/.local/share/vice/C128/kernal-318020-05.bin (BOOT_CALL at $F890: $9F filename length starts 0, $F94A BIT-skip loop counts filename chars, $F954 STX $04 sets the entry low byte, $F986 JSR $02CD = JSRFAR) plus an x128 run with -moncommands breakpoints; the driver script is in the session scratchpad (x128boot.py), not in the repository." ] }