modemwars/annotations/20_boot_c64CartridgeStub8000_8000.json
2026-08-23 02:09:40 -05:00

299 lines
15 KiB
JSON

{
"chunk": "boot_c64CartridgeStub8000_8000",
"unit": "boot/c64CartridgeStub8000",
"unitLabels": {
"boot/c64CartridgeStub8000": {
"8000": "cartHeader",
"8004": "cbm80Signature",
"8009": "cartColdStart",
"800F": "clearLowRamLoop",
"8061": "eaFileNameCart",
"8065": "cartLoadEaFile",
"806B": "cartTailFiller"
}
},
"dataTypes": {
"8000": [
"addr",
2
],
"8002": [
"addr",
2
],
"8004": [
"byte",
5
],
"8061": [
"text",
4
],
"806B": [
"byte",
8
],
"8073": [
"byte",
4
]
},
"notes": {
"8000": {
"unit": "boot/c64CartridgeStub8000",
"block": [
"cartHeader ($8000-$8008) - the 9-byte autostart cartridge header the C64 KERNAL looks for.",
"$8000/$8001 = cold-start vector, $8002/$8003 = warm-start (NMI) vector, $8004-$8008 = the",
"signature 'CBM80'. Both vectors point at $8009, so RESET and RESTORE do exactly the same thing.",
"This is RAM, not ROM: the C128 boot sector at $0B27 copies 256 bytes from $0B89 to $8000-$80FF",
"and then calls GO64 ($FF4D). The C64 reset code ($FCE4: SEI / TXS / CLD) calls the signature",
"check at $FD02, finds CBM80 here, and takes the JMP ($8000) at $FCEC - which skips IOINIT,",
"RAMTAS, RESTOR and CINT entirely. cartColdStart has to redo all four by hand.",
"On the plain C64 path (LOAD\"EA\",8,1) none of this exists and $8000 is just free RAM."
],
"line": "cold-start vector -> cartColdStart ($8009); taken by the KERNAL reset at $FCEC"
},
"8002": {
"unit": "boot/c64CartridgeStub8000",
"line": "warm-start vector, also $8009: the KERNAL NMI handler ($FE47) jumps here when RESTORE is pressed, so RESTORE restarts the whole boot"
},
"8004": {
"unit": "boot/c64CartridgeStub8000",
"block": [
"cbm80Signature ($8004-$8008) - the five magic bytes $C3 $C2 $CD $38 $30 = 'C'|$80, 'B'|$80,",
"'M'|$80, '8', '0'. The KERNAL's cartridge test at $FD02 compares these against its own copy at",
"$FD10; a match is what makes both the reset and the NMI vector through $8000/$8002. Not a",
"string in the game's own format (no bit-7 terminator) - the high bits are part of the magic."
],
"line": "'CBM80' with bit 7 set on the first three characters - the autostart magic tested by $FD02"
},
"8009": {
"unit": "boot/c64CartridgeStub8000",
"routine": [
"cartColdStart - Cold/warm start of the CBM80 pseudo-cartridge that the C128 boot sector plants at $8000 before GO64. Because the KERNAL reset jumps through the $8000 vector before it runs IOINIT/RAMTAS/RESTOR/CINT, this routine performs all of them itself (I/O init, clear $0002-$0101 and $0200-$03FF, MEMTOP=$A000, MEMSTR=$0800, HIBASE=$0400, RESTOR, CINT), blanks the screen in blue, initialises the drive with a DOS 'I' command, then SETNAMs \"0:EA\" and falls through cartLoadEaFile into the normal boot chain. It never returns.",
"In: Nothing in registers (entered from the KERNAL reset at $FCEC via JMP ($8000), or from the NMI handler at $FE4D via JMP ($8002) if RESTORE is pressed). Requires the C128 boot sector's helper routines at $0B45 (open command channel) and $0B55 (send DOS command) to still be in RAM - they are, because the reset skipped RAMTAS and the clear loop below stops at $03FF.",
"Out: $0002-$0101 and $0200-$03FF zeroed; MEMSIZ $0283/$0284 = $A000; MEMSTR high $0282 = $08; HIBASE $0288 = $04; KERNAL indirect vectors restored; screen editor initialised; VIC_BORDER = VIC_BG0 = 6 (blue); VIC_CTRL1 bit 4 (DEN) cleared so the display is blank; drive initialised and logical file 15 closed; stack pointer = $F0; A = 0 for the LOAD. Does not return - control ends up at $02B8 (eaLoadGame).",
"Called from: nobody in this disassembly - only the C64 KERNAL reset/NMI, through the vectors at $8000/$8002. (The XREF entries for L_8059/L_805C/L_8065/L_806A come from game/mapGenerator6F00 and game/comcenScreens6F00, which are different code loaded over these addresses at run time.)",
"Note: unlike the real RAMTAS this does not set the tape buffer pointer $B2/$B3 = $033C (harmless - the game never touches tape), and it never executes CLI, so the I flag set by the reset's SEI at $FCE4 is only cleared later, inside the KERNAL's own serial routines."
],
"line": "IOINIT ($FF84): set up CIA1/CIA2 (keyboard, serial bus, jiffy timer) and the SID/VIC defaults the reset skipped"
},
"800C": {
"unit": "boot/c64CartridgeStub8000",
"line": "$00 = the fill value for the low-RAM clear below"
},
"800E": {
"unit": "boot/c64CartridgeStub8000",
"line": "Y = 0: first of the 256 offsets to clear"
},
"800F": {
"unit": "boot/c64CartridgeStub8000",
"routine": [
"clearLowRamLoop - Not a callable routine, just the body of the clear loop inside cartColdStart. One pass of Y = 0..255 writes $00 through three absolute,Y stores, clearing $0002-$0101, $0200-$02FF and $0300-$03FF. It is a byte-for-byte copy of the clear loop at the head of the KERNAL's RAMTAS ($FD54), which the reset did not run.",
"In: A = 0 (fill value), Y = 0 (index)",
"Out: $0002-$0101, $0200-$03FF all zero; Y = 0 again on exit; A still 0",
"Called from: fall-through from $800E, and its own BNE at $8019.",
"The clear deliberately stops at $03FF: $0400 upwards holds the C128 boot sector's code and its still-needed helpers at $0B45/$0B55, and $0000/$0001 (the 6510 CPU port) are skipped because the first store is at $0002."
],
"line": "clear $0002+Y - absolute,Y, so the last two passes spill into $0100/$0101; $00/$01 (CPU port) are never touched"
},
"8012": {
"unit": "boot/c64CartridgeStub8000",
"line": "clear $0200+Y - BASIC input buffer and the KERNAL workspace ($0282 MEMSTR, $0288 HIBASE included; both are rewritten below)"
},
"8015": {
"unit": "boot/c64CartridgeStub8000",
"line": "clear $0300+Y - the vector page; RESTOR at $802D refills $0314-$0333 afterwards"
},
"8018": {
"unit": "boot/c64CartridgeStub8000",
"line": "advance to the next of the 256 offsets"
},
"8019": {
"unit": "boot/c64CartridgeStub8000",
"line": "loop until Y wraps back to 0, i.e. all 256 offsets of all three pages are cleared"
},
"801B": {
"unit": "boot/c64CartridgeStub8000",
"block": [
"Rebuild the power-on memory layout that RAMTAS would normally have measured and stored. The",
"values are simply hardcoded rather than RAM-tested: top of memory $A000, bottom of user memory",
"$0800, screen page $0400. RESTOR and CINT then repair the vectors and the editor that the clear",
"loop above just wiped."
],
"line": "top-of-memory low byte = $00"
},
"801D": {
"unit": "boot/c64CartridgeStub8000",
"line": "top-of-memory high byte = $A0, i.e. MEMTOP = $A000 (the standard C64 value; $A000-$BFFF later holds the title bitmap)"
},
"801F": {
"unit": "boot/c64CartridgeStub8000",
"line": "C = 0 selects the 'set' direction of MEMTOP (C = 1 would read it back)"
},
"8020": {
"unit": "boot/c64CartridgeStub8000",
"line": "MEMTOP ($FF99): store $A000 into MEMSIZ $0283/$0284"
},
"8023": {
"unit": "boot/c64CartridgeStub8000",
"line": "page $08 = the standard start of user RAM"
},
"8025": {
"unit": "boot/c64CartridgeStub8000",
"line": "MEMSTR high byte $0282 = $08 -> bottom of memory $0800 (its low byte $0281 is already 0 from the clear loop)"
},
"8028": {
"unit": "boot/c64CartridgeStub8000",
"line": "page $04 = the default text screen"
},
"802A": {
"unit": "boot/c64CartridgeStub8000",
"line": "HIBASE $0288 = $04 so CINT below points the screen editor at $0400"
},
"802D": {
"unit": "boot/c64CartridgeStub8000",
"line": "RESTOR ($FF8A): reload the KERNAL indirect vectors $0314-$0333, which the clear loop had zeroed"
},
"8030": {
"unit": "boot/c64CartridgeStub8000",
"line": "CINT ($FF81): program the VIC from the KERNAL's default table, clear screen RAM at $0400 and reset the editor"
},
"8033": {
"unit": "boot/c64CartridgeStub8000",
"block": [
"Hide the load. CINT has just switched on a normal blue-on-blue text screen, so paint border and",
"background the same colour and then turn the display off completely; the player sees a plain blue",
"screen until the boot loader at $C145 sets VIC_CTRL1 = $3B and shows the title bitmap."
],
"line": "colour 6 = blue"
},
"8035": {
"unit": "boot/c64CartridgeStub8000",
"line": "VIC_BORDER ($D020) = blue"
},
"8038": {
"unit": "boot/c64CartridgeStub8000",
"line": "VIC_BG0 ($D021) = blue as well, so no text can show through"
},
"803B": {
"unit": "boot/c64CartridgeStub8000",
"line": "read VIC_CTRL1 ($D011) - the raster/mode register CINT just initialised to $1B"
},
"803E": {
"unit": "boot/c64CartridgeStub8000",
"line": "clear bit 4 (DEN, display enable); the other bits (RSEL, YSCROLL, raster MSB) are left alone"
},
"8040": {
"unit": "boot/c64CartridgeStub8000",
"line": "write it back: screen blanked ($D011 = $0B) for the whole loading sequence"
},
"8043": {
"unit": "boot/c64CartridgeStub8000",
"block": [
"Reset the drive. Both calls go to routines that belong to the C128 boot sector and are still",
"sitting in RAM at $0B45/$0B55 - the KERNAL reset skipped RAMTAS and this stub only cleared up to",
"$03FF, so they survived GO64. The SETLFS inside $0B45 (lfn 15, device 8, secondary 15) also sets",
"up the logical-file variables that the LOAD at $8065 later inherits.",
"Note that these are NOT the game's own $0B45/$0B55 - the main program's code at $0B00 only exists",
"after the fast loader has run, long after this stub is dead."
],
"line": "c128OpenCommandChannel ($0B45): SETNAM length 0 / SETLFS 15,8,15 / OPEN -> drive command channel open"
},
"8046": {
"unit": "boot/c64CartridgeStub8000",
"line": "drive-command index 2 = the string \"I\" at $0B87 (DOS INITIALIZE); index 0/1 are the 1571 commands the C128-mode code already sent"
},
"8048": {
"unit": "boot/c64CartridgeStub8000",
"line": "c128SendDriveCommand ($0B55): CHKOUT 15, send \"I\" byte by byte, CLRCHN - the drive re-reads the BAM and clears any pending error"
},
"804B": {
"unit": "boot/c64CartridgeStub8000",
"line": "logical file 15 = the command channel just used"
},
"804D": {
"unit": "boot/c64CartridgeStub8000",
"line": "CLOSE ($FFC3): shut the command channel; the KERNAL leaves the current-file variables $B8/$B9/$BA at 15 / 15 / 8, which is exactly what the LOAD below needs"
},
"8050": {
"unit": "boot/c64CartridgeStub8000",
"block": [
"Name the stage-1 file and hand over. The filename is stored inline four bytes further on, so the",
"pointer passed to SETNAM points back into this routine. No SETLFS is issued: the device (8) and",
"the non-zero secondary address (15) left over from the command channel are what make LOAD honour",
"the load address in the file's own header."
],
"line": "filename length = 4 characters"
},
"8052": {
"unit": "boot/c64CartridgeStub8000",
"line": "filename pointer low byte $61"
},
"8054": {
"unit": "boot/c64CartridgeStub8000",
"line": "filename pointer high byte $80 -> eaFileNameCart at $8061, embedded in this very routine"
},
"8056": {
"unit": "boot/c64CartridgeStub8000",
"line": "SETNAM ($FFBD): filename = \"0:EA\""
},
"8059": {
"unit": "boot/c64CartridgeStub8000",
"line": "stack pointer value $F0 (the reset had set $FF)"
},
"805B": {
"unit": "boot/c64CartridgeStub8000",
"line": "SP = $F0, i.e. stack top $01F0; safe because nothing on the stack is ever needed again - this code never returns. Probably just copied from EA's generic cartridge template"
},
"805C": {
"unit": "boot/c64CartridgeStub8000",
"line": "A = 0 tells LOAD to load (A = 1 would mean verify)"
},
"805E": {
"unit": "boot/c64CartridgeStub8000",
"line": "jump over the four inline filename bytes at $8061 into cartLoadEaFile"
},
"8061": {
"unit": "boot/c64CartridgeStub8000",
"block": [
"eaFileNameCart ($8061-$8064) - 4 bytes of plain PETSCII, \"0:EA\": drive 0, file \"EA\" (the 1-block",
"directory file that loads to $02A8-$030B). Length-counted by SETNAM, so there is no terminator",
"and no bit-7 flag. It sits in the middle of the code and is stepped over by the JMP at $805E."
],
"line": "\"0:EA\" - drive number, colon, filename; passed to SETNAM as X=$61 / Y=$80 / A=4"
},
"8065": {
"unit": "boot/c64CartridgeStub8000",
"routine": [
"cartLoadEaFile - Tail of cartColdStart, reached by the JMP at $805E that steps over the inline filename. KERNAL-LOADs \"0:EA\" and jumps straight into it. Because the secondary address inherited from the command channel is 15 (non-zero), LOAD uses the two-byte load address in the file itself, so \"ea\" lands at $02A8-$030B; the JMP then enters eaLoadGame at $02B8 by hand. On the ordinary C64 path that entry happens indirectly, through the BASIC vectors that \"ea\" overwrites at $0300-$030B - here BASIC never runs at all, which is why the jump is explicit. The carry/error result of LOAD is discarded: a load failure runs whatever happens to be at $02B8.",
"In: A = 0 (load, not verify); filename set by the SETNAM at $8056; device 8 / secondary 15 left in $B8/$B9/$BA by $0B45 and the CLOSE at $804D",
"Out: $02A8-$030B filled from disk; control passes to $02B8 (eaLoadGame), which SETLFS 8,8,1, loads the 45-block file \"LOAD\" to $9800-$C3FF, JSRs the boot loader at $C000 and finally JMPs to the game at $0800. Never returns.",
"Called from: only the JMP at $805E."
],
"line": "LOAD ($FFD5): pull \"0:EA\" from device 8; secondary 15 means 'use the file's own load address' -> $02A8-$030B"
},
"8068": {
"unit": "boot/c64CartridgeStub8000",
"line": "enter eaLoadGame at $02B8 directly, skipping the BASIC-vector hijack that the C64 path relies on; C (LOAD error) is ignored"
},
"806B": {
"unit": "boot/c64CartridgeStub8000",
"block": [
"cartTailFiller ($806B-$8076) - 12 dead bytes, never referenced by anything. A $00 followed by",
"8E 72 17 / 8E 73 17 / 8E 70 17 / 8E 71, which disassembles as STX $1772 / STX $1773 / STX $1770 /",
"STX $1771 with the last operand byte cut off. These are simply the last bytes ($F4-$FF) of track",
"1 sector 0 that the C128 boot sector's 256-byte copy loop dragged along with the real cartridge",
"image; they look like leftovers in the mastering system's buffer, the same way track 1 sector 16",
"still holds fragments of assembler source text. Execution can never reach them (the routine",
"above ends in a JMP), and at run time the whole $8000-$87FF block is overwritten by the $6F00",
"overlay's upper half anyway."
],
"line": "leftover mastering-buffer bytes: $00, then STX $1772 / STX $1773 / STX $1770 ..."
},
"8073": {
"unit": "boot/c64CartridgeStub8000",
"line": "... and the truncated STX $1771 that ends the sector; dead filler, never referenced"
}
}
}