modemwars/disassembly/boot/eaBootFile.s
2026-08-23 02:09:40 -05:00

168 lines
13 KiB
ArmAsm

; ============================================================================
; EA - 1-block BASIC-vector hijack loader (directory file "ea", loads at $02A8)
; ============================================================================
; Loaded with LOAD"EA",8,1 (or LOAD"*",8,1). The file overwrites the BASIC vectors at $0300-$030B so that
; control reaches $02B8 as soon as BASIC returns to its main loop after the LOAD completes.
; $02B8 then KERNAL-LOADs the file "load" ($9800-$C3FF) and calls the fast loader at $C000.
.setcpu "6502"
.include "c64.inc"
.include "kernal.inc"
.include "zeropage.inc"
; ---- references to code/data outside this file ----
gameEntry := $0800
bootEntry := $C000
; Contents
; --------
; $02B8 eaLoadGame Stage 1 of the boot chain.
.org $02A8
; 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.
strayBasicStub:
.byte $0D,$08 ; 02A8 .. 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
.byte $0A ; 02AA line number low byte: this is line 10
D_02AB:
.byte $00 ; 02AB 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
.byte $93 ; 02AC BASIC token $93 = LOAD
.byte $22,$45,$41,$22,$2C,$38,$2C,$31; 02AD "EA",8,1 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)
.byte $00 ; 02B5 $00 terminates the BASIC line
.byte $00,$00 ; 02B6 .. null link word = end of the BASIC program
; ----------------------------------------------------------------------
; 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.
; ----------------------------------------------------------------------
eaLoadGame:
lda #$08 ; 02B8 logical file number 8 for the KERNAL LOAD (any nonzero value would do)
tax ; 02BA X = device number 8 = the disk drive; the same $08 serves as both file and device number
ldy #$01 ; 02BB secondary address 1: load to the address in the file's own two-byte header ($9800), not to BASIC's start of program
jsr KERNAL_SETLFS ; 02BD SETLFS ($FFBA): set file 8, device 8, secondary address 1
lda #$04 ; 02C0 A = file name length: 4 characters
ldx #$EC ; 02C2 X = low byte of the name pointer -> eaFileName
ldy #$02 ; 02C4 Y = high byte: the name 'LOAD' sits at $02EC, inside this very file
jsr KERNAL_SETNAM ; 02C6 SETNAM ($FFBD): the file to fetch is 'LOAD'
lda #$00 ; 02C9 0 = LOAD rather than VERIFY for the call below, and the value stored into MSGFLG next
sta kernalMsgFlag ; 02CB MSGFLG = 0: bit 7 (control messages SEARCHING FOR / LOADING) and bit 6 (I/O ERROR messages) both off, so the KERNAL LOAD prints nothing
jsr KERNAL_LOAD ; 02CD 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
bcs jumpToGameEntry ; 02D0 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
jsr bootEntry ; 02D2 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 (also textEngineC000:bitPairHighTable)
; ----------------------------------------------------------------------
; Only reached when the KERNAL LOAD above failed - on the normal path the boot loader never returns
; here.
; ----------------------------------------------------------------------
jumpToGameEntry:
jmp gameEntry ; 02D5 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)
; 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.
orphanedStoreOperand:
.byte $00,$04 ; 02D8 .. little-endian $0400 = the screen-RAM operand of the overwritten 'STA $0400'
; 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
deadLoadErrorRetry:
.byte $A9 ; 02DA screen code $12 = 'R' (screen codes, not PETSCII: 'A' = 1)
.byte $12 ; 02DB .
.byte $8D ; 02DC second character of ERROR, column 1 of the top row of the default text screen at $0400
.byte $01,$04 ; 02DD ..
.byte $8D ; 02DF third character, column 2 - the two 'R's share one LDA
.byte $02,$04 ; 02E0 ..
.byte $8D ; 02E2 fifth character, column 4; written out of order so that all three 'R's use the same loaded value
.byte $04,$04 ; 02E3 ..
.byte $A9 ; 02E5 screen code $0F = 'O'
.byte $0F ; 02E6 .
.byte $8D ; 02E7 fourth character, column 3, completing E-R-R-O-R
.byte $03,$04 ; 02E8 ..
.byte $D0 ; 02EA always taken (A = $0F, never zero): go back and try the whole load again
.byte $CC ; 02EB .
; 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.
eaFileName:
.byte "LOAD" ; 02EC 'LOAD' - exactly the 4 bytes SETNAM is told about
; 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.
fileNamePadding:
.byte $30,$30,$00,$00,$00,$00,$00,$00; 02F0 00...... unused tail of the file name plus padding up to the BASIC vector table
.byte $00,$00,$00,$00,$00,$00,$00,$00; 02F8 ........
; 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.
basicVectorHijack:
.byte $B8 ; 0300 IERROR low byte: BASIC's error-handler vector, aimed at eaLoadGame
D_0301:
.byte $02 ; 0301 IERROR high byte - $02B8 (this label exists only because the map generator uses the same address for a variable)
D_0302:
.byte $B8 ; 0302 IMAIN low byte: the vector BASIC JMPs through after printing READY. - this is the one that really launches the game
.byte $02 ; 0303 IMAIN high byte - $02B8
D_0304:
.byte $B8 ; 0304 ICRNCH low byte: the tokenise-a-line vector, aimed at eaLoadGame as well
.byte $02 ; 0305 ICRNCH high byte - $02B8
.byte $B8 ; 0306 IQPLOP low byte: the list-a-token vector
D_0307:
.byte $02 ; 0307 IQPLOP high byte - $02B8
.byte $B8 ; 0308 IGONE low byte: the execute-next-statement vector
.byte $02 ; 0309 IGONE high byte - $02B8
.byte $B8 ; 030A IEVAL low byte: the evaluate-an-expression-element vector
.byte $02 ; 030B IEVAL high byte - $02B8; the file ends here, exactly on the last byte of the BASIC vector table