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

244 lines
12 KiB
JSON

{
"chunk": "boot_eaBootFile_02A8",
"unit": "boot/eaBootFile",
"unitLabels": {
"boot/eaBootFile": {
"02D5": "jumpToGameEntry",
"02D8": "orphanedStoreOperand"
}
},
"notes": {
"02A8": {
"unit": "boot/eaBootFile",
"block": [
"strayBasicStub - 16 bytes of tokenised BASIC program text that are never executed. This is the line the player is told to type; it survives in the file only as filler, so that the last 12 bytes of the LOAD land exactly on BASIC's vector table at $0300. Layout: link word, line number, token $93 (LOAD), the argument text, the end-of-line $00 and a null link word.",
"It is formatted as if it lived at $0801 (BASIC's start of program), and the link word is two bytes short of this line's real end, so the ',1' was probably typed into the master by hand after the link had been computed for LOAD\"EA\",8. Nothing in the boot chain reads these bytes; once the game runs the same page holds the map generator's contour rule table (track 18 s15-16) or the BAM sector buffer of the disk-id check, which is what the XREF hits on $02AB refer to."
],
"line": "BASIC link word $080D: where the next line would begin if this program sat at $0801 - two bytes short of the end of the line as it stands here"
},
"02AA": {
"unit": "boot/eaBootFile",
"block": [],
"line": "line number low byte: this is line 10"
},
"02AB": {
"unit": "boot/eaBootFile",
"line": "line number high byte ($000A = 10). At run time this address is BAM offset $AB, the 10-byte disk serial that the copy loop at $0FF3 moves into ownPlayerName - that is the only real reader of D_02AB"
},
"02AC": {
"unit": "boot/eaBootFile",
"line": "BASIC token $93 = LOAD"
},
"02AD": {
"unit": "boot/eaBootFile",
"line": "the rest of the statement as plain PETSCII: \"EA\",8,1 - file name EA, device 8, secondary address 1 (load at the file's own address)"
},
"02B5": {
"unit": "boot/eaBootFile",
"line": "$00 terminates the BASIC line"
},
"02B6": {
"unit": "boot/eaBootFile",
"line": "null link word = end of the BASIC program"
},
"02B8": {
"unit": "boot/eaBootFile",
"routine": [
"eaLoadGame - Stage 1 of the boot chain. Reached through the BASIC vectors this file has just overwritten (normally IMAIN $0302, taken by BASIC's ready loop the moment LOAD\"EA\",8,1 finishes), or on the C128 path by a direct JMP from the CBM80 cartridge stub at $8065. It opens logical file 8 on device 8 with secondary address 1 so the file supplies its own load address, points SETNAM at the name 'LOAD' a few bytes further down this same file, silences the KERNAL's messages and KERNAL-LOADs the 45-block file 'LOAD' to $9800-$C3FF (title picture plus the boot loader).",
"It then calls the boot loader at $C000, which never comes back: that code shows the title picture, initialises the modem and the drive, block-executes the drive bootstrap and reads the rest of the game in, and its closing RTS is rigged to continue at $0461 (the checksum routine in the $0400 block), which ends with JMP $0800. The JMP gameEntry at $02D5 is therefore only reached when the KERNAL LOAD failed; the error handler that used to live there was overwritten when the JSR/JMP pair was patched in (see orphanedStoreOperand at $02D8). Everything here runs with the ROMs still banked in and interrupts still enabled.",
"In: no register inputs (entered by JMP through the BASIC vector table, KERNAL/BASIC ROMs in, BASIC's stack in place); reads the file name at eaFileName $02EC",
"Out: file 'LOAD' at $9800-$C3FF; kernalMsgFlag ($9D, KERNAL MSGFLG) = 0; control passes to bootEntry $C000 and never returns to this file. Only on a KERNAL LOAD error does it fall through to JMP gameEntry $0800 with nothing loaded.",
"Called from: the BASIC vector table $0300-$030B written by this very file (basicVectorHijack) on the C64 path, and boot/c64CartridgeStub8000 cartLoadEaFile ($8065) on the C128 path. The '(also textEngineC000:...)' alias the listing prints on the JSR at $02D2 is an artefact: the runtime text-engine overlay later replaces the whole $C000 page and XREF cannot tell the two apart."
],
"line": "logical file number 8 for the KERNAL LOAD (any nonzero value would do)"
},
"02BA": {
"unit": "boot/eaBootFile",
"line": "X = device number 8 = the disk drive; the same $08 serves as both file and device number"
},
"02BB": {
"unit": "boot/eaBootFile",
"line": "secondary address 1: load to the address in the file's own two-byte header ($9800), not to BASIC's start of program"
},
"02BD": {
"unit": "boot/eaBootFile",
"line": "SETLFS ($FFBA): set file 8, device 8, secondary address 1"
},
"02C0": {
"unit": "boot/eaBootFile",
"line": "A = file name length: 4 characters"
},
"02C2": {
"unit": "boot/eaBootFile",
"line": "X = low byte of the name pointer -> eaFileName"
},
"02C4": {
"unit": "boot/eaBootFile",
"line": "Y = high byte: the name 'LOAD' sits at $02EC, inside this very file"
},
"02C6": {
"unit": "boot/eaBootFile",
"line": "SETNAM ($FFBD): the file to fetch is 'LOAD'"
},
"02C9": {
"unit": "boot/eaBootFile",
"line": "0 = LOAD rather than VERIFY for the call below, and the value stored into MSGFLG next"
},
"02CB": {
"unit": "boot/eaBootFile",
"line": "MSGFLG = 0: bit 7 (control messages SEARCHING FOR / LOADING) and bit 6 (I/O ERROR messages) both off, so the KERNAL LOAD prints nothing"
},
"02CD": {
"unit": "boot/eaBootFile",
"line": "LOAD ($FFD5): pull the 45-block file 'LOAD' to its header address $9800-$C3FF - title colour RAM $9800, title screen $9C00, title bitmap $A000 and the boot loader $C000-$C3FF"
},
"02D0": {
"unit": "boot/eaBootFile",
"line": "branch if the KERNAL reported an error (C=1, A = error code): skip the boot loader. In the unpatched EA template this branch reached the 'ERROR' handler whose remains start at $02DA"
},
"02D2": {
"unit": "boot/eaBootFile",
"line": "enter the boot loader that was just loaded ($C000 = JMP bootMain $C145): title picture, modem/drive init, B-E of the drive bootstrap, then tracks 22-27 into $0800+. It never returns here - bootMain's faked RTS lands at $0461, which JMPs to $0800"
},
"02D5": {
"unit": "boot/eaBootFile",
"block": [
"Only reached when the KERNAL LOAD above failed - on the normal path the boot loader never returns here."
],
"line": "jump into the game at $0800 with nothing loaded. $0800 then still holds $00 = BRK and the ROMs are still in, so this probably falls through the KERNAL BRK handler to BASIC's warm start, which JMPs ($0302) straight back to eaLoadGame: a failed load quietly retries (inferred, not verified)"
},
"02D8": {
"unit": "boot/eaBootFile",
"block": [
"orphanedStoreOperand - the two operand bytes of an instruction that no longer exists. In the untouched EA loader template the load-error path started at $02D5 with LDA #$05 / STA $0400 (screen code 'E' in the top left corner) and the success path was a plain JMP at $02D2. This build patched JSR bootEntry ($02D2) and JMP gameEntry ($02D5) over $02D2-$02D7, which ate the LDA and the opcode of the STA and left its address operand $0400 stranded here (probable reconstruction: the byte counts and the missing 'E' both fit). The rest of that handler still assembles - see deadLoadErrorRetry below."
],
"line": "little-endian $0400 = the screen-RAM operand of the overwritten 'STA $0400'"
},
"02DA": {
"unit": "boot/eaBootFile",
"routine": [
"deadLoadErrorRetry - Unreachable remnant of the load-error handler of the shared EA loader template. It still decodes cleanly: it writes screen codes 'R','R','O','R' into $0401-$0404, i.e. the top left corner of the default text screen (the leading 'E' at $0400 came from the LDA/STA that the JSR/JMP patch destroyed), and then branches unconditionally back to eaLoadGame to retry the whole SETLFS/SETNAM/LOAD sequence. Nothing reaches it in this build: the BCS at $02D0 that used to land here now lands on the JMP gameEntry at $02D5. The writes only make sense this early in the boot, while the VIC still shows the text screen at $0400 and the ROMs are banked in.",
"In: nothing (would have been entered with C=1 from the BCS at $02D0 in the original build)",
"Out: would put 'ERROR' at the start of the top screen line and re-enter eaLoadGame; dead code as built",
"Called from: nothing in this build"
],
"line": "screen code $12 = 'R' (screen codes, not PETSCII: 'A' = 1)"
},
"02DC": {
"unit": "boot/eaBootFile",
"line": "second character of ERROR, column 1 of the top row of the default text screen at $0400"
},
"02DF": {
"unit": "boot/eaBootFile",
"line": "third character, column 2 - the two 'R's share one LDA"
},
"02E2": {
"unit": "boot/eaBootFile",
"line": "fifth character, column 4; written out of order so that all three 'R's use the same loaded value"
},
"02E5": {
"unit": "boot/eaBootFile",
"line": "screen code $0F = 'O'"
},
"02E7": {
"unit": "boot/eaBootFile",
"line": "fourth character, column 3, completing E-R-R-O-R"
},
"02EA": {
"unit": "boot/eaBootFile",
"line": "always taken (A = $0F, never zero): go back and try the whole load again"
},
"02EC": {
"unit": "boot/eaBootFile",
"block": [
"eaFileName - the PETSCII name of the second-stage file, 4 characters with no terminator; the length is handed to SETNAM in A at $02C0 and the pointer in X/Y at $02C2/$02C4. 'LOAD' is the 45-block directory file that carries the title picture ($9800 colour RAM, $9C00 screen, $A000 bitmap) and the boot loader at $C000-$C3FF."
],
"line": "'LOAD' - exactly the 4 bytes SETNAM is told about"
},
"02F0": {
"unit": "boot/eaBootFile",
"block": [
"fileNamePadding - '00' followed by 14 zero bytes, never read. SETNAM is told the name is 4 characters long, so the '00' is not part of it; it looks like the tail of a longer name (LOAD00) left over from the EA mastering template. The zeros pad the file out so that the next byte is $0300."
],
"line": "unused tail of the file name plus padding up to the BASIC vector table"
},
"0300": {
"unit": "boot/eaBootFile",
"block": [
"basicVectorHijack - the actual payload of this file. Six little-endian pointers, every one of them $02B8 (eaLoadGame), which the tail of the KERNAL LOAD drops straight onto BASIC's indirect vectors: IERROR $0300, IMAIN $0302, ICRNCH $0304, IQPLOP $0306, IGONE $0308 and IEVAL $030A.",
"Because LOAD\"EA\",8,1 was typed in direct mode, BASIC returns to its ready loop, which dispatches through JMP ($0302) - so the game starts by itself with no RUN. Aiming all six at the same address also makes the boot self-healing: an error, a tokenise, a LIST, the start of a statement or the evaluation of an expression all end up in eaLoadGame. The table ends at $030B, the last byte of the file. Everything the XREF lists for $0300-$030B belongs to later owners of this page (initGameSystems loaded from track 18 s15-16, the map generator's scratch variables, the film directory), not to this table."
],
"line": "IERROR low byte: BASIC's error-handler vector, aimed at eaLoadGame"
},
"0301": {
"unit": "boot/eaBootFile",
"line": "IERROR high byte - $02B8 (this label exists only because the map generator uses the same address for a variable)"
},
"0302": {
"unit": "boot/eaBootFile",
"line": "IMAIN low byte: the vector BASIC JMPs through after printing READY. - this is the one that really launches the game"
},
"0303": {
"unit": "boot/eaBootFile",
"line": "IMAIN high byte - $02B8"
},
"0304": {
"unit": "boot/eaBootFile",
"line": "ICRNCH low byte: the tokenise-a-line vector, aimed at eaLoadGame as well"
},
"0305": {
"unit": "boot/eaBootFile",
"line": "ICRNCH high byte - $02B8"
},
"0306": {
"unit": "boot/eaBootFile",
"line": "IQPLOP low byte: the list-a-token vector"
},
"0307": {
"unit": "boot/eaBootFile",
"line": "IQPLOP high byte - $02B8"
},
"0308": {
"unit": "boot/eaBootFile",
"line": "IGONE low byte: the execute-next-statement vector"
},
"0309": {
"unit": "boot/eaBootFile",
"line": "IGONE high byte - $02B8"
},
"030A": {
"unit": "boot/eaBootFile",
"line": "IEVAL low byte: the evaluate-an-expression-element vector"
},
"030B": {
"unit": "boot/eaBootFile",
"line": "IEVAL high byte - $02B8; the file ends here, exactly on the last byte of the BASIC vector table"
}
},
"dataTypes": {
"02A8": [
"byte",
2
],
"02AD": [
"byte",
8
],
"02B6": [
"byte",
2
],
"02D8": [
"byte",
2
],
"02EC": [
"text",
4
],
"02F0": [
"byte",
16
]
}
}