372 lines
No EOL
18 KiB
JSON
372 lines
No EOL
18 KiB
JSON
{
|
|
"chunk": "boot_c128BootSector_0B00",
|
|
"unit": "boot/c128BootSector",
|
|
"unitLabels": {
|
|
"boot/c128BootSector": {
|
|
"0B00": "c128BootHeader",
|
|
"0B07": "c128BootMessage",
|
|
"0B25": "c128BootFileName",
|
|
"0B27": "c128BootEntry",
|
|
"0B45": "c128OpenCommandChannel",
|
|
"0B55": "c128SendDriveCommand",
|
|
"0B68": "sendDriveCommandLoop",
|
|
"0B72": "sendDriveCommandDone",
|
|
"0B75": "driveCmdPtrTable",
|
|
"0B76": "driveCmdPtrTableHi",
|
|
"0B7B": "driveCmdSet1541Mode",
|
|
"0B81": "driveCmdSelectHead0",
|
|
"0B87": "driveCmdInitialize",
|
|
"0B89": "c64CartImage",
|
|
"0BF4": "sectorTailFiller"
|
|
}
|
|
},
|
|
"dataTypes": {
|
|
"0B00": [
|
|
"byte",
|
|
7
|
|
],
|
|
"0B07": [
|
|
"text",
|
|
30
|
|
],
|
|
"0B25": [
|
|
"byte",
|
|
2
|
|
],
|
|
"0B75": [
|
|
"addr",
|
|
6
|
|
],
|
|
"0B7B": [
|
|
"text",
|
|
6
|
|
],
|
|
"0B81": [
|
|
"text",
|
|
6
|
|
],
|
|
"0B87": [
|
|
"text",
|
|
2
|
|
],
|
|
"0B89": [
|
|
"byte",
|
|
107
|
|
],
|
|
"0BF4": [
|
|
"byte",
|
|
8
|
|
],
|
|
"0BFC": [
|
|
"byte",
|
|
4
|
|
]
|
|
},
|
|
"notes": {
|
|
"0B00": {
|
|
"unit": "boot/c128BootSector",
|
|
"block": [
|
|
"c128BootHeader - the fixed header the C128 KERNAL expects at the front of an auto-boot sector. At",
|
|
"reset the KERNAL reads track 1 sector 0 of every drive into $0B00 and looks at:",
|
|
" +0..+2 \"CBM\" signature - without it the sector is ignored and the C128 goes to BASIC",
|
|
" +3/+4 load address for the extra blocks that follow ($0000 = none)",
|
|
" +5 RAM bank those blocks are loaded into (0)",
|
|
" +6 number of extra blocks to load (0 - this one sector holds everything)",
|
|
" +7.. zero-terminated boot message, then a zero-terminated filename, then the boot code.",
|
|
"The KERNAL copies the four bytes $0B03-$0B06 to $AC-$AF before acting on them. Everything in this",
|
|
"file is dead once the game loads: the fast loader fills $0800-$6EFF, so at run time $0B00-$0BFF",
|
|
"belongs to the main program (loadCommTailBuild1 at $0B4F, ownerNameString, the settings block)",
|
|
"and has nothing to do with the code here."
|
|
],
|
|
"line": "\"CBM\" + load address $0000 + bank 0 + 0 extra blocks: the whole boot program fits in this sector"
|
|
},
|
|
"0B07": {
|
|
"unit": "boot/c128BootSector",
|
|
"block": [
|
|
"c128BootMessage - 29 characters plus the $00 terminator at $0B24; the KERNAL prints \"BOOTING \", this",
|
|
"text and \"...\". The KERNAL then builds the filename it believes follows the message in place and",
|
|
"overwrites $0B23/$0B24 with the drive prefix \"0:\", so a RAM dump taken after booting reads",
|
|
"\"AN ELECTRONIC ARTS PRODUCTIO0:\" rather than the text stored on disk."
|
|
],
|
|
"line": "\"AN ELECTRONIC ARTS PRODUCTION\",$00 - the message printed after \"BOOTING \""
|
|
},
|
|
"0B25": {
|
|
"unit": "boot/c128BootSector",
|
|
"block": [
|
|
"c128BootFileName - the filename field, and the sector's one real bug. It should have been $00",
|
|
"(empty filename) followed by the first instruction LDY #$00 ($A0 $00). Because the message",
|
|
"terminator is immediately followed by $A0, the KERNAL reads $A0 as a one-character filename and",
|
|
"the $00 as its terminator, tries to LOAD \"0:\"+$A0 (fails, result ignored) and sets the boot entry",
|
|
"to the byte after it: $0B27. So these two bytes are data that never execute - verified in x128,",
|
|
"where a breakpoint at $0B25 never fires and $0B27 is reached with A=X=Y=0. It is harmless only",
|
|
"because JSRFAR takes Y from the register image at $07, which a cold reset leaves at 0 - exactly",
|
|
"what the missing LDY would have set."
|
|
],
|
|
"line": "$A0,$00 - intended as the empty-filename byte plus LDY #$00; the KERNAL swallows them as a filename, so execution starts at $0B27"
|
|
},
|
|
"0B27": {
|
|
"unit": "boot/c128BootSector",
|
|
"routine": [
|
|
"c128BootEntry - Entry point of the C128 auto-boot sector, called by the KERNAL boot code (BOOT_CALL, $FF53 -> $F890) with a JSRFAR in bank 15 after it has loaded this sector to $0B00 and printed \"BOOTING AN ELECTRONIC ARTS PRODUCTION...\". It copies 256 bytes from $0B89 to $8000-$80FF (the CBM80 pseudo-cartridge disassembled in boot/c64CartridgeStub8000.s), opens the drive command channel and sends \"U0>M0\" and \"U0>H0\" so that a 1571 behaves exactly like a 1541, then selects MMU bank 15 and jumps to the KERNAL GO64 entry.",
|
|
"In: Y = 0, the index of the copy loop. Nothing else. The LDY #$00 meant to set it sits at $0B25 and is never executed (the KERNAL parsed it as a filename); Y is 0 only because JSRFAR loads the registers from the images at $05-$07, which a cold reset leaves zeroed.",
|
|
"Out: $8000-$80FF = the pseudo-cartridge image (only $8000-$8076 is meaningful); logical file 15 open to device 8 secondary 15 with SETLFS still pointing at it; a 1571 switched to 1541 mode and head 0; $FF00 = $00 (bank 15). Never returns - GO64 resets the machine into C64 mode, where the KERNAL finds \"CBM80\" at $8004 and jumps to $8009 (cartColdStart), which loads \"0:EA\" and continues at $02B8.",
|
|
"Called from: the C128 KERNAL boot code only (JSRFAR). The $0B27 references listed in XREF.txt belong to the game's main program, which overwrites this sector long before they run."
|
|
],
|
|
"block": [
|
|
"Copy the C64-mode pseudo-cartridge into place: 256 bytes $0B89,Y -> $8000,Y. Only the first 107",
|
|
"bytes mean anything; the loop also drags the sector filler at $0BF4-$0BFF and $0C00-$0C88 (the C128",
|
|
"RS-232 input buffer) up to $8077-$80FF, where nothing ever reads them."
|
|
],
|
|
"line": "read byte Y of the pseudo-cartridge image that starts at $0B89"
|
|
},
|
|
"0B2A": {
|
|
"unit": "boot/c128BootSector",
|
|
"line": "store it at $8000+Y; in bank 15 the write falls through the BASIC ROM into RAM bank 0, which is what C64 mode will see"
|
|
},
|
|
"0B2D": {
|
|
"unit": "boot/c128BootSector",
|
|
"line": "next byte of the image"
|
|
},
|
|
"0B2E": {
|
|
"unit": "boot/c128BootSector",
|
|
"line": "loop until Y wraps back to 0, i.e. exactly 256 bytes copied - the CBM80 signature must land at $8004"
|
|
},
|
|
"0B30": {
|
|
"unit": "boot/c128BootSector",
|
|
"block": [
|
|
"Put the drive into the state the game's own loader needs: a 1571 must run in 1541 mode, because the",
|
|
"drive-resident fast loader and sector cipher the game uploads later (track 1 sectors 17-20, run at",
|
|
"$0300-$05FF in the drive) are 1541 code, and it must read side 0."
|
|
],
|
|
"line": "open the DOS command channel (logical file 15, device 8, secondary 15)"
|
|
},
|
|
"0B33": {
|
|
"unit": "boot/c128BootSector",
|
|
"line": "command index 0 = \"U0>M0\""
|
|
},
|
|
"0B35": {
|
|
"unit": "boot/c128BootSector",
|
|
"line": "send it: a 1571 drops into 1541 mode (a plain 1541 answers with a syntax error nobody reads)"
|
|
},
|
|
"0B38": {
|
|
"unit": "boot/c128BootSector",
|
|
"line": "command index 1 = \"U0>H0\""
|
|
},
|
|
"0B3A": {
|
|
"unit": "boot/c128BootSector",
|
|
"line": "send it: select head 0, the only side this disk uses"
|
|
},
|
|
"0B3D": {
|
|
"unit": "boot/c128BootSector",
|
|
"block": [
|
|
"Hand the machine over to C64 mode. $FF00 is the MMU configuration register (mirrored at $D500);",
|
|
"writing $00 selects the standard \"bank 15\" configuration - I/O at $D000, BASIC and KERNAL ROM",
|
|
"in, RAM bank 0 - which is what the KERNAL's C64-mode routine expects to be called in."
|
|
],
|
|
"line": "MMU configuration value $00 = bank 15 (I/O + both ROMs + RAM bank 0)"
|
|
},
|
|
"0B3F": {
|
|
"unit": "boot/c128BootSector",
|
|
"line": "write the MMU configuration register so GO64 runs in the standard bank"
|
|
},
|
|
"0B42": {
|
|
"unit": "boot/c128BootSector",
|
|
"line": "$FF4D = KERNAL C64-mode (GO64) entry: resets into C64 mode, where the reset code finds the \"CBM80\" just copied to $8004 and jumps to $8009. Never returns"
|
|
},
|
|
"0B45": {
|
|
"unit": "boot/c128BootSector",
|
|
"routine": [
|
|
"c128OpenCommandChannel - Opens the disk DOS command channel: SETNAM with a zero-length name, SETLFS logical file 15 / device 8 / secondary address 15, then JMP KERNAL_OPEN so that OPEN's RTS returns straight to this routine's caller.",
|
|
"In: none.",
|
|
"Out: logical file 15 open on device 8 secondary 15. The SETLFS parameters also stay current, which the C64-mode code depends on: its LOAD at $8065 uses secondary address 15, and any non-zero secondary makes the KERNAL load \"0:EA\" to the address in the file header ($02A8-$030B) instead of to the caller's address. A/X/Y clobbered; C = OPEN's error flag, which neither caller tests.",
|
|
"Called from: c128BootEntry ($0B30) and, after GO64, the copied cartridge code at $8043 (boot/c64CartridgeStub8000.s) - that RAM survives because the C64 reset skips RAMTAS when it finds a cartridge, and the cartridge's own clear loop stops at $03FF."
|
|
],
|
|
"line": "SETNAM length 0: a command channel needs no filename"
|
|
},
|
|
"0B47": {
|
|
"unit": "boot/c128BootSector",
|
|
"line": "KERNAL SETNAM ($FFBD)"
|
|
},
|
|
"0B4A": {
|
|
"unit": "boot/c128BootSector",
|
|
"line": "logical file number 15"
|
|
},
|
|
"0B4C": {
|
|
"unit": "boot/c128BootSector",
|
|
"line": "device 8 = the disk drive"
|
|
},
|
|
"0B4E": {
|
|
"unit": "boot/c128BootSector",
|
|
"line": "secondary address 15 as well - channel 15 is the DOS command/status channel"
|
|
},
|
|
"0B4F": {
|
|
"unit": "boot/c128BootSector",
|
|
"line": "KERNAL SETLFS ($FFBA) with A=15, X=8, Y=15"
|
|
},
|
|
"0B52": {
|
|
"unit": "boot/c128BootSector",
|
|
"line": "KERNAL OPEN ($FFC0) as a tail call - its RTS returns to our caller; the error flag in C is ignored"
|
|
},
|
|
"0B55": {
|
|
"unit": "boot/c128BootSector",
|
|
"routine": [
|
|
"c128SendDriveCommand - Sends one of this sector's three DOS command strings on the already open command channel. It doubles the command index to index driveCmdPtrTable (two bytes per entry), copies the string pointer to $FB/$FC, makes logical file 15 the output channel with CHKOUT and CIOUTs every character up to but not including the $00 terminator; the closing CLRCHN unlistens the drive, which is what actually makes the DOS execute the command.",
|
|
"In: A = command index: 0 = \"U0>M0\" (1571 -> 1541 mode), 1 = \"U0>H0\" (head/side 0), 2 = \"I\" (initialise). Channel 15 must already be open (c128OpenCommandChannel). The routine does the doubling itself - the survey's \"pre-doubled\" wording was wrong.",
|
|
"Out: the command has been sent and executed. $FB/$FC = pointer to the string, X = $0F, Y = string length, A = the last character sent; default I/O channels restored by CLRCHN. The DOS error code is left unread in the drive's status channel, so a 1541 rejecting \"U0>\" goes unnoticed by design.",
|
|
"Called from: c128BootEntry ($0B35 with index 0, $0B3A with index 1) and the C64-mode cartridge code at $8048 with index 2 (boot/c64CartridgeStub8000.s)."
|
|
],
|
|
"line": "index * 2 - the pointer table holds two bytes per entry"
|
|
},
|
|
"0B56": {
|
|
"unit": "boot/c128BootSector",
|
|
"line": "use the doubled index as the byte offset into the table"
|
|
},
|
|
"0B57": {
|
|
"unit": "boot/c128BootSector",
|
|
"line": "low byte of this command's string pointer"
|
|
},
|
|
"0B5A": {
|
|
"unit": "boot/c128BootSector",
|
|
"line": "high byte from the following table byte (always $0B - all three strings live in this sector)"
|
|
},
|
|
"0B5D": {
|
|
"unit": "boot/c128BootSector",
|
|
"line": "park the pointer in $FB/$FC for the (zp),Y fetch in the loop"
|
|
},
|
|
"0B5F": {
|
|
"unit": "boot/c128BootSector",
|
|
"line": "...and its high byte in $FC"
|
|
},
|
|
"0B61": {
|
|
"unit": "boot/c128BootSector",
|
|
"line": "logical file 15 = the command channel opened by c128OpenCommandChannel"
|
|
},
|
|
"0B63": {
|
|
"unit": "boot/c128BootSector",
|
|
"line": "KERNAL CHKOUT ($FFC9): make it the current output channel (sends LISTEN 8 + secondary 15)"
|
|
},
|
|
"0B66": {
|
|
"unit": "boot/c128BootSector",
|
|
"line": "start at the first character of the command string"
|
|
},
|
|
"0B68": {
|
|
"unit": "boot/c128BootSector",
|
|
"routine": [
|
|
"sendDriveCommandLoop - The transmit loop of c128SendDriveCommand, not a routine of its own: fetch the next character through ($FB),Y, stop at the $00 terminator, otherwise CIOUT it to the listening command channel and advance.",
|
|
"In: $FB/$FC = start of the command string, Y = index of the next character, logical file 15 already selected as the output channel.",
|
|
"Out: falls out to sendDriveCommandDone at the terminator with Y = length of the string.",
|
|
"Called from: fallen into from $0B66; branch target of the BNE at $0B70."
|
|
],
|
|
"line": "next character of the DOS command string"
|
|
},
|
|
"0B6A": {
|
|
"unit": "boot/c128BootSector",
|
|
"line": "$00 terminator reached - the whole command has been sent"
|
|
},
|
|
"0B6C": {
|
|
"unit": "boot/c128BootSector",
|
|
"line": "KERNAL CIOUT ($FFA8): put this character on the serial bus for the listening drive"
|
|
},
|
|
"0B6F": {
|
|
"unit": "boot/c128BootSector",
|
|
"line": "advance to the next character"
|
|
},
|
|
"0B70": {
|
|
"unit": "boot/c128BootSector",
|
|
"line": "always taken: the longest command is five characters, so Y never wraps to 0"
|
|
},
|
|
"0B72": {
|
|
"unit": "boot/c128BootSector",
|
|
"routine": [
|
|
"sendDriveCommandDone - Tail of c128SendDriveCommand: JMP KERNAL_CLRCHN ($FFCC). Its UNLISTEN is what tells the drive the command is complete and makes the DOS act on it; it also restores the default input/output channels, and its RTS returns to c128SendDriveCommand's caller.",
|
|
"In: none.",
|
|
"Out: serial bus unlistened (command executed), default channels restored.",
|
|
"Called from: branch target of the BEQ at $0B6A (end of sendDriveCommandLoop)."
|
|
],
|
|
"line": "KERNAL CLRCHN ($FFCC): UNLISTEN makes the drive execute the command; its RTS returns to our caller"
|
|
},
|
|
"0B75": {
|
|
"unit": "boot/c128BootSector",
|
|
"block": [
|
|
"driveCmdPtrTable - three little-endian pointers to the DOS command strings below, one per command",
|
|
"index (2 bytes per entry). c128SendDriveCommand reads the low byte with LDA driveCmdPtrTable,X and",
|
|
"the high byte with LDY driveCmdPtrTableHi,X after doubling the index in A, which is why the",
|
|
"high-byte position carries its own label."
|
|
],
|
|
"line": "index 0 -> \"U0>M0\", index 1 -> \"U0>H0\", index 2 -> \"I\""
|
|
},
|
|
"0B7B": {
|
|
"unit": "boot/c128BootSector",
|
|
"block": [
|
|
"The three DOS command strings, each terminated with $00. \"U0>\" is the 1571 user command group; a",
|
|
"plain 1541 replies with error 31 (syntax error), which nobody reads, so one sector serves both",
|
|
"drives."
|
|
],
|
|
"line": "\"U0>M\" - first four characters of the 1571 command \"U0>M0\" (switch to 1541 mode)"
|
|
},
|
|
"0B7F": {
|
|
"unit": "boot/c128BootSector",
|
|
"line": "\"0\" and the $00 terminator that complete \"U0>M0\"; the line break at $0B7F is only a rendering artifact of the main program's ownerNameString, which starts at this address in the other unit"
|
|
},
|
|
"0B81": {
|
|
"unit": "boot/c128BootSector",
|
|
"block": [
|
|
"driveCmdSelectHead0 - \"U0>H0\": 1571 command that selects head 0, i.e. side 0 of the disk. Every",
|
|
"track the game reads is on side 0."
|
|
],
|
|
"line": "\"U0>H0\",$00 - 1571: use head 0"
|
|
},
|
|
"0B87": {
|
|
"unit": "boot/c128BootSector",
|
|
"block": [
|
|
"driveCmdInitialize - \"I\": plain DOS INITIALIZE (re-read the BAM, clear the error state). The",
|
|
"C128-mode code above never sends it; only the copied C64-mode code does, at $8046 (LDA #$02 /",
|
|
"JSR $0B55), just before it LOADs \"0:EA\"."
|
|
],
|
|
"line": "\"I\",$00 - DOS initialise, sent only by the C64-mode code at $8046"
|
|
},
|
|
"0B89": {
|
|
"unit": "boot/c128BootSector",
|
|
"block": [
|
|
"c64CartImage - bytes $89-$F3 of the sector: the CBM80 pseudo-cartridge that c128BootEntry copies to",
|
|
"$8000 before calling GO64. It is data here and code there; it is disassembled as $8000-$806A in",
|
|
"boot/c64CartridgeStub8000.s and must not be read as code in this file. Row by row, address in this",
|
|
"sector -> address after the copy:",
|
|
" 0B89 -> 8000 cold-start vector $8009, warm-start vector $8009, $C3 $C2 $CD $38 = \"CBM\"|$80 + '8'",
|
|
" 0B91 -> 8008 '0', last byte of \"CBM80\"; JSR $FF84 IOINIT; LDA #$00 / TAY; STA opcode of the loop",
|
|
" 0B99 -> 8010 operands of the clear loop at $800F: STA $0002,Y / STA $0200,Y / STA $0300,Y - the",
|
|
" RAMTAS work the C64 reset skips when a cartridge is present",
|
|
" 0BA1 -> 8018 INY / BNE $800F (256 passes), then LDX #$00 / LDY #$A0 / CLC = MEMTOP value $A000",
|
|
" 0BA9 -> 8020 JSR $FF99 MEMTOP (C=0, so it stores), LDA #$08 / STA $0282 = start-of-BASIC page $08",
|
|
" 0BB1 -> 8028 LDA #$04 / STA $0288 = screen page $04, JSR $FF8A RESTOR (default KERNAL vectors)",
|
|
" 0BB9 -> 8030 JSR $FF81 CINT (screen editor), LDA #$06 = blue / STA $D020 border",
|
|
" 0BC1 -> 8038 STA $D021 background also blue, LDA $D011 / AND #$EF clears DEN = display blanked",
|
|
" 0BC9 -> 8040 STA $D011, JSR $0B45 (this sector's open-command-channel code, still in RAM), LDA #2",
|
|
" 0BD1 -> 8048 JSR $0B55 sends \"I\", LDA #$0F / JSR $FFC3 CLOSE lfn 15",
|
|
" 0BD9 -> 8050 SETNAM arguments LDA #$04 / LDX #$61 / LDY #$80 (name \"0:EA\" at $8061) and JSR $FFBD",
|
|
" 0BE1 -> 8058 end of that JSR, LDX #$F0 / TXS, LDA #$00 = load (not verify), JMP $8065 past name",
|
|
" 0BE9 -> 8060 the four characters \"0:EA\" at $8061-$8064 and JSR $FFD5 LOAD - secondary address 15",
|
|
" from $0B45 makes it load to the file's own address $02A8-$030B",
|
|
" 0BF1 -> 8068 JMP $02B8 = eaLoadGame, which LOADs \"load\" to $9800-$C3FF, calls the boot loader at",
|
|
" $C000 and finally jumps to the game at $0800.",
|
|
"The copy loop moves 256 bytes, so $0BF4-$0BFF and $0C00-$0C88 (the C128 RS-232 input buffer) land",
|
|
"at $8077-$80FF too; nothing reads them, and the game's overlays overwrite $8000-$87FF anyway."
|
|
],
|
|
"line": "107 bytes of C64-mode header and code, copied to $8000-$806A above; see boot/c64CartridgeStub8000.s"
|
|
},
|
|
"0BF4": {
|
|
"unit": "boot/c128BootSector",
|
|
"block": [
|
|
"sectorTailFiller - the last 12 bytes of the sector ($F4-$FF): $00 followed by 8E 72 17 / 8E 73 17 /",
|
|
"8E 70 17 / 8E 71, i.e. fragments of STX $1772 / STX $1773 / STX $1770 / STX $1771. Dead leftovers",
|
|
"from the mastering system's buffer (track 1 sector 16 has similar debris - fragments of assembler",
|
|
"source text). They ride along with the 256-byte copy to $806B-$8076 and are never referenced."
|
|
],
|
|
"line": "unused mastering leftovers; copied to $806B-$8076 and never used"
|
|
},
|
|
"0BFC": {
|
|
"unit": "boot/c128BootSector",
|
|
"line": "tail of the filler ($8073-$8076 after the copy)"
|
|
}
|
|
}
|
|
} |