168 lines
13 KiB
ArmAsm
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
|