; ============================================================================
; C128 auto-boot sector (track 1 sector 0, loaded by the C128 KERNAL to $0B00)
; ============================================================================
; Header: "CBM", load address 0, bank 0, 0 extra blocks, boot message "AN ELECTRONIC ARTS PRODUCTION".
; The code copies $0B89-$0BFF to $8000 (a CBM80 cartridge image), puts a 1571 into 1541 mode with
; "U0>M0" / "U0>H0" on the command channel, selects MMU bank 15 and calls GO64 ($FF4D).  In C64 mode
; the cartridge signature at $8004 autostarts the copied code (see boot/c64CartridgeStub8000.s).

        .setcpu "6502"
        .include "c64.inc"
        .include "kernal.inc"
        .include "zeropage.inc"

; ---- references to code/data outside this file ----
D_8000                   := $8000
D_FF00                   := $FF00
L_FF4D                   := $FF4D

; Contents
; --------
;    $0B27  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...".
;    $0B45  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.
;    $0B55  c128SendDriveCommand         Sends one of this sector's three DOS command strings on the already open
;                                        command channel.
;    $0B68  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.
;    $0B72  sendDriveCommandDone         Tail of c128SendDriveCommand: JMP KERNAL_CLRCHN ($FFCC).

        .org $0B00


; 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.
c128BootHeader:
        .byte   $43,$42,$4D,$00,$00,$00,$00; 0B00  CBM....  "CBM" + load address $0000 + bank 0 + 0 extra blocks: the whole boot program fits in this sector

; 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.
c128BootMessage:
        .byte   "AN ELECTRONIC ARTS PRODUCTION",$00; 0B07  "AN ELECTRONIC ARTS PRODUCTION",$00 - the message printed after "BOOTING "

; 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.
c128BootFileName:
        .byte   $A0,$00                 ; 0B25  ..  $A0,$00 - intended as the empty-filename byte plus LDY #$00; the KERNAL swallows them as a filename, so execution starts at $0B27

; ----------------------------------------------------------------------
; 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.
; 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.
; ----------------------------------------------------------------------
c128BootEntry:
        lda     c64CartImage,y          ; 0B27  read byte Y of the pseudo-cartridge image that starts at $0B89
        sta     D_8000,y                ; 0B2A  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
        iny                             ; 0B2D  next byte of the image
        bne     c128BootEntry           ; 0B2E  loop until Y wraps back to 0, i.e. exactly 256 bytes copied - the CBM80 signature must land at $8004

; ----------------------------------------------------------------------
; 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.
; ----------------------------------------------------------------------
        jsr     c128OpenCommandChannel  ; 0B30  open the DOS command channel (logical file 15, device 8, secondary 15)
        lda     #$00                    ; 0B33  command index 0 = "U0>M0"
        jsr     c128SendDriveCommand    ; 0B35  send it: a 1571 drops into 1541 mode (a plain 1541 answers with a syntax error nobody reads)
        lda     #$01                    ; 0B38  command index 1 = "U0>H0"
        jsr     c128SendDriveCommand    ; 0B3A  send it: select head 0, the only side this disk uses

; ----------------------------------------------------------------------
; 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.
; ----------------------------------------------------------------------
        lda     #$00                    ; 0B3D  MMU configuration value $00 = bank 15 (I/O + both ROMs + RAM bank 0)
        sta     D_FF00                  ; 0B3F  write the MMU configuration register so GO64 runs in the standard bank
        jmp     L_FF4D                  ; 0B42  $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


; ----------------------------------------------------------------------
; 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.
; ----------------------------------------------------------------------
c128OpenCommandChannel:
        lda     #$00                    ; 0B45  SETNAM length 0: a command channel needs no filename
        jsr     KERNAL_SETNAM           ; 0B47  KERNAL SETNAM ($FFBD)
        lda     #$0F                    ; 0B4A  logical file number 15
        ldx     #$08                    ; 0B4C  device 8 = the disk drive
        tay                             ; 0B4E  secondary address 15 as well - channel 15 is the DOS command/status channel
        jsr     KERNAL_SETLFS           ; 0B4F  KERNAL SETLFS ($FFBA) with A=15, X=8, Y=15
        jmp     KERNAL_OPEN             ; 0B52  KERNAL OPEN ($FFC0) as a tail call - its RTS returns to our caller; the error flag in C is ignored


; ----------------------------------------------------------------------
; 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).
; ----------------------------------------------------------------------
c128SendDriveCommand:
        asl     a                       ; 0B55  index * 2 - the pointer table holds two bytes per entry
        tax                             ; 0B56  use the doubled index as the byte offset into the table
        lda     driveCmdPtrTable,x      ; 0B57  low byte of this command's string pointer
        ldy     driveCmdPtrTableHi,x    ; 0B5A  high byte from the following table byte (always $0B - all three strings live in this sector)
        sta     driveCmdStrPtr          ; 0B5D  park the pointer in $FB/$FC for the (zp),Y fetch in the loop
        sty     driveCmdStrPtrHi        ; 0B5F  ...and its high byte in $FC
        ldx     #$0F                    ; 0B61  logical file 15 = the command channel opened by c128OpenCommandChannel
        jsr     KERNAL_CHKOUT           ; 0B63  KERNAL CHKOUT ($FFC9): make it the current output channel (sends LISTEN 8 + secondary 15)
        ldy     #$00                    ; 0B66  start at the first character of the command string

; ----------------------------------------------------------------------
; 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.
; ----------------------------------------------------------------------
sendDriveCommandLoop:
        lda     (driveCmdStrPtr),y      ; 0B68  next character of the DOS command string
        beq     sendDriveCommandDone    ; 0B6A  $00 terminator reached - the whole command has been sent
        jsr     KERNAL_CIOUT            ; 0B6C  KERNAL CIOUT ($FFA8): put this character on the serial bus for the listening drive
        iny                             ; 0B6F  advance to the next character
        bne     sendDriveCommandLoop    ; 0B70  always taken: the longest command is five characters, so Y never wraps to 0

; ----------------------------------------------------------------------
; 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).
; ----------------------------------------------------------------------
sendDriveCommandDone:
        jmp     KERNAL_CLRCHN           ; 0B72  KERNAL CLRCHN ($FFCC): UNLISTEN makes the drive execute the command; its RTS returns to our caller


; 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.
driveCmdPtrTable:
driveCmdPtrTableHi       = driveCmdPtrTable+1
        .addr   driveCmdSet1541Mode     ; 0B75  index 0 -> "U0>M0", index 1 -> "U0>H0", index 2 -> "I"
        .addr   driveCmdSelectHead0     ; 0B77
        .addr   driveCmdInitialize      ; 0B79

; 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.
driveCmdSet1541Mode:
        .byte   "U0>M"                  ; 0B7B  "U0>M" - first four characters of the 1571 command "U0>M0" (switch to 1541 mode)
        .byte   "0",$00                 ; 0B7F  "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

; 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.
driveCmdSelectHead0:
        .byte   "U0>H0",$00             ; 0B81  "U0>H0",$00 - 1571: use head 0

; 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".
driveCmdInitialize:
        .byte   "I",$00                 ; 0B87  "I",$00 - DOS initialise, sent only by the C64-mode code at $8046

; 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.
c64CartImage:
        .byte   $09,$80,$09,$80,$C3,$C2,$CD,$38; 0B89  .......8  107 bytes of C64-mode header and code, copied to $8000-$806A above; see boot/c64CartridgeStub8000.s
        .byte   $30,$20,$84,$FF,$A9,$00,$A8,$99; 0B91  0 ......
        .byte   $02,$00,$99,$00,$02,$99,$00,$03; 0B99  ........
        .byte   $C8,$D0,$F4,$A2,$00,$A0,$A0,$18; 0BA1  ........
        .byte   $20,$99,$FF,$A9,$08,$8D,$82,$02; 0BA9   .......
        .byte   $A9,$04,$8D,$88,$02,$20,$8A,$FF; 0BB1  ..... ..
        .byte   $20,$81,$FF,$A9,$06,$8D,$20,$D0; 0BB9   ..... .
        .byte   $8D,$21,$D0,$AD,$11,$D0,$29,$EF; 0BC1  .!....).
        .byte   $8D,$11,$D0,$20,$45,$0B,$A9,$02; 0BC9  ... E...
        .byte   $20,$55,$0B,$A9,$0F,$20,$C3,$FF; 0BD1   U... ..
        .byte   $A9,$04,$A2,$61,$A0,$80,$20,$BD; 0BD9  ...a.. .
        .byte   $FF,$A2,$F0,$9A,$A9,$00,$4C,$65; 0BE1  ......Le
        .byte   $80,$30,$3A,$45,$41,$20,$D5,$FF; 0BE9  .0:EA ..
        .byte   $4C,$B8,$02             ; 0BF1  L..

; 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.
sectorTailFiller:
        .byte   $00,$8E,$72,$17,$8E,$73,$17,$8E; 0BF4  ..r..s..  unused mastering leftovers; copied to $806B-$8076 and never used
        .byte   $70,$17,$8E,$71         ; 0BFC  p..q  tail of the filler ($8073-$8076 after the copy)
