ys2-intro/loader/include/loader.inc
2025-11-13 19:07:39 +03:00

708 lines
21 KiB
PHP
Executable file

.ifndef _LOADER_INC_
_LOADER_INC_ = 1
DISABLE_WATCHDOG = 0 ; disable the drive-side watchdog - the watchdog will reset the drive safely when the host resets at any point,
; however, the drive hardware (1541 and 1571) only allows for a maximum time-out period of 65536 cycles: this means
; that letting the loader starve for a few video frames (about 1.5 frames with a 1571 running at 2 MHz) will reset
; the drive, which can be prevented using this option.
; C-128 only: for burst transfers, use the CLK line for the incoming data-sent signal rather than the
; SERIAL_IRQ flag in CIA1_ICR ($dc0d) in order to avoid interference by CIA1 interrupt handlers,
; note that this slows down transfer speed
ASYNCHRONOUS_BURST_HANDSHAKE = 0
; set to 0 if loading via KERNAL fallback is problematic with non-standard KERNALs, VICE virtual drives, IEEE-488 interfaces or devices like IDE64
KERNAL_FALLBACK_SEI_WORKAROUNDS = 1
KERNAL_FALLBACK_OPEN_SEI_WORKAROUNDS = 0; this does not work with VICE virtual device traps
CONFIG_INTERNAL = ((KERNAL_FALLBACK_SEI_WORKAROUNDS = 0) << 3) | (KERNAL_FALLBACK_OPEN_SEI_WORKAROUNDS << 2) | (ASYNCHRONOUS_BURST_HANDSHAKE << 1) | DISABLE_WATCHDOG
.pushseg
.segment "CODE"; import symbols as absolute by default
.scope diskio
.scope platform
COMMODORE_64 = 64
COMMODORE_128 = 128
COMMODORE_16 = 16
.endscope; platform
.scope drivetype
DRIVES_1541 = $00
DRIVES_157X = $10
DRIVES_1581_CMD = $20
DRIVES_MASK = $f0
DRIVE_1541 = $01
DRIVE_1541_C = $02
DRIVE_1541_II = $03
DRIVE_1541U = $04
DRIVE_1570 = $15
DRIVE_1571 = $16
DRIVE_1571CR = $17
DRIVE_1581 = $28
DRIVE_CMD_FD_2000 = $29
DRIVE_CMD_FD_4000 = $2a
DRIVE_CMD_HD = $2b
DRIVE_GENERIC_SERIAL = $fc ; -4
DRIVE_GENERIC_BURST = $fd ; -3
DRIVE_GENERIC_PARALLEL = $fe ; -2
DEVICE_NONE = $ff ; -1
.endscope; drivetype
.scope status
OK = 0
FILE_TOO_LARGE = $f7 ; -9
FILE_ON_DISK_TOO_SMALL = $f8 ; -8
FILE_ON_DISK_TOO_LARGE = $f9 ; -7
WRITE_PROTECT_ON = $fa ; -6
DEVICE_INCOMPATIBLE = $fb ; -5, cannot upload loader drive code to active device, if LOAD_VIA_KERNAL_FALLBACK != 0, do not regard this as an error
TOO_MANY_DEVICES = $fc ; -4, a device on the bus other than the active device is not compatible, if LOAD_VIA_KERNAL_FALLBACK != 0, do not regard this as an error
GENERIC_KERNAL_ERROR = $fd ; -3, an error occured during installation or while loading without installed drive code via KERNAL fallback, check the x register for further information
DEVICE_NOT_PRESENT = $fe ; -2
FILE_NOT_FOUND = $ff ; -1
.endscope; status
.endscope; diskio
.enum DECOMPRESSORS
BITNAX = 1 ; Doynax and Bitbreaker's Bitnax
BYTEBOOZER2 = 2 ; HCL/Booze Design's ByteBoozer2
DOYNAX_LZ = 3 ; Doynax LZ, aka Doynamite
EXOMIZER = 4 ; Magnus Lind's Exomizer
LEVELCRUSH = 5 ; Taboo Levelcrush
LZSA2 = 6 ; Emmanuel Marty's LZSA2 with modifications by Bitbreaker
NUCRUNCH = 7 ; ChristopherJam aka Shrydar's NuCrunch
PUCRUNCH = 8 ; Pasi Ojala's Pucrunch
SUBSIZER = 9 ; tlr's Subsizer
TINYCRUNCH = 10 ; ChristopherJam aka Shrydar's tinycrunch
TSCRUNCH = 11 ; Antonio Savona's TSCrunch
ZX0 = 12 ; Einar Saukas' ZX0/Salvador with Dali modifications by Bitbreaker
NONE = 0
.endenum
.ifdef EXTCONFIGPATH
.include "loaderconfig.inc"
.else
.include "config.inc"
.endif; !EXTCONFIGPATH
.if .defined(FORCE_ASYNCHRONOUS_BURST_HANDSHAKE)
USE_ASYNCHRONOUS_BURST_HANDSHAKE = 1
.else
USE_ASYNCHRONOUS_BURST_HANDSHAKE = ASYNCHRONOUS_BURST_HANDSHAKE
.endif
.ifndef __NO_LOADER_SYMBOLS_IMPORT
.ifndef install
.if PLATFORM <> diskio::platform::COMMODORE_16
; Set the VIC bank
; in: a - VIC bank (0..3)
; out: undefined
.macro SET_VIC_BANK bank
lda #bank & 3
sta CIA2_PRA
.endmacro
; allow for arbitrary $DD00 writes ($00-$FF) when the loader is idle,
; good for raster routines with LDA #value:STA $D018:STA $DD00, e.g.
; call after load
.macro ENTER_BUS_LOCK
; nothing to do
.endmacro
; call before load
.macro LEAVE_BUS_LOCK
; when the loader is idle, the user is
; allowed to write anything to CIA2_DDRA ($DD00) -
; set it to a known and valid state here.
;php; without these, there's a race condition and thus a small
;sei; chance for video glitches, but with these, there's a
INIT_CLEAR_ATN_OUT_CLEAR_CLK_OUT_CLEAR_DATA_OUT
;plp; chance for actual crashes with stable raster routines
.endmacro
.if ALLOW_2_MHZ_ON_C128 | (PLATFORM = diskio::platform::COMMODORE_128)
; For 2 MHz in interrupt handlers during loading,
; to be run at the start of every IRQ handler
.macro LOADER_IRQ_HANDLER_PROLOGUE
; buffer clock setting and continue at 2 MHz
lda VIC2_C128_CLOCK
pha
lda #C128_TWO_MHZ
sta VIC2_C128_CLOCK
.endmacro
; For 2 MHz in interrupt handlers during loading,
; to be run at the end of every IRQ handler
.macro LOADER_IRQ_HANDLER_EPILOGUE
pla
sta VIC2_C128_CLOCK
.endmacro
.endif
.else
; To be run at the start of every IRQ handler
.macro LOADER_IRQ_HANDLER_PROLOGUE
; buffer force single clock flag
; and allow double clock
lda TED_CHARGEN_ADDR
pha
and #255 - FORCE_SINGLE_CLOCK
sta TED_CHARGEN_ADDR
.endmacro
; To be run at the end of every IRQ handler
.macro LOADER_IRQ_HANDLER_EPILOGUE
; restore force single clock flag
.local fast
pla
lsr
lsr
lda TED_CHARGEN_ADDR
and #255 - FORCE_SINGLE_CLOCK
bcc fast
ora #FORCE_SINGLE_CLOCK
fast: sta TED_CHARGEN_ADDR
.endmacro
.endif
; Install the loader
; note: KERNAL ROM must be enabled, and the I flag will be cleared (CLI)
; in: nothing
; out: c - set on error
; a - status
; x - drive type (one of diskio::drivetypes)
; y - if status is diskio::status::OK, zeropage address of version string address
.import install
.macro LOADER_INSTALL
jsr install
.endmacro
.if LOAD_RAW_API
; Load a file without decompression
; in: x/y - x: lo, y: hi to 0-terminated filename string,
; zero-length file name will load next file
; c - if LOAD_TO_API != 0, c = 0: load to address as stored in the file
; c = 1: load to caller-specified address (loadaddrlo/hi)
; out: c - set on error
; a - status
.import loadraw
.macro LOADRAW name
.if LOAD_TO_API
clc
.endif
ldx #<(name)
ldy #>(name)
jsr loadraw
.endmacro
.macro LOADNEXTRAW
lda #0
LOADRAW * - 1
.endmacro
.if LOAD_TO_API
.macro LOADRAW_LOADTO name, dest
sec
lda #<(dest)
sta loadaddrlo
lda #>(dest)
sta loadaddrhi
ldx #<(name)
ldy #>(name)
jsr loadraw
.endmacro
.macro LOADNEXTRAW_LOADTO dest
lda #0
LOADRAW_LOADTO * - 1, dest
.endmacro
.endif; LOAD_TO_API
.endif; LOAD_RAW_API
.if LOAD_COMPD_API
; Load a compressed file
; in: x/y - x: lo, y: hi to 0-terminated filename string,
; zero-length file name will load next file
; c - if DECOMPLOAD_TO_API != 0, c = 0: load to address as stored in the file
; c = 1: load with caller-specified address offset (loadaddroffslo/hi)*
; out: c - set on error
; a - status
; x/y - if DECOMPRESSOR = DECOMPRESSORS::PUCRUNCH, x: lo, y: hi of the loaded file's execution address
; * this is an offset rather than an absolute destination, as the original destination address required for calculating this offset is
; stored in the file, but to retrieve it, the first file block would have to be loaded first to the original load address or to an extra buffer
.import loadcompd
.import loadcompdopen
.import loadcompdexecute
.macro LOADCOMPD name
ldx #<(name)
ldy #>(name)
jsr loadcompd
.endmacro
.macro LOADNEXTCOMPD
lda #0
LOADCOMPD * - 1
.endmacro
.if LOAD_TO_API
.macro LOADCOMPD_RELTO name, load_address_offset
sec
lda #<(load_address_offset)
sta loadaddroffslo
lda #>(load_address_offset)
sta loadaddroffshi
ldx #<(name)
ldy #>(name)
jsr loadcompd
.endmacro
.macro LOADNEXTCOMPD_RELTO dest_lo, dest_hi
lda #0
LOADCOMPD_RELTO #<(* - 1), #>(* - 1), dest_lo, dest_hi
.endmacro
.endif; LOAD_TO_API
.endif; LOAD_COMPD_API
.if FILE_EXISTS_API
; Check if file exists
; in: x/y - x: lo, y: hi to 0-terminated filename string
; out: c - set on file not found or error
; a - status
.import fileexists
.macro FILEEXISTS name
ldx #<(name)
ldy #>(name)
jsr fileexists
.endmacro
.endif; FILE_EXISTS_API
.if MEM_DECOMP_API
; Decompress a compressed file from memory
; in: x/y - x: lo, y: hi of compressed file in memory
; c - if MEMDECOMP_TO_API != 0, c = 0: decompress to address as stored in the file
; c = 1: decompress to caller-specified address (loadaddrlo/hi)
; out: x/y - if DECOMPRESSOR = DECOMPRESSORS::PUCRUNCH, x: lo, y: hi of the file's execution address
.import memdecomp
.macro MEMDECOMP source_lo, source_hi
.if MEM_DECOMP_TO_API
clc
.endif
ldx source_lo
ldy source_hi
jsr memdecomp
.endmacro
.if MEM_DECOMP_TO_API
.macro MEMDECOMP_TO source_lo, source_hi, dest
sec
lda #<(dest)
sta decdestlo
lda #>(dest)
sta decdesthi
ldx source_lo
ldy source_hi
jsr memdecomp
.endmacro
.endif; MEM_DECOMP_TO_API
.endif; MEM_DECOMP_API
.endif; !install
.endif; !__NO_LOADER_SYMBOLS_IMPORT
.struct drivecode
entry .word; drive
to .word; drive
length .word; bytes
from .word; host
.endstruct
.struct swapparams
buffer .word; $0800 bytes for swapped loader drive code
drivecode41 .tag drivecode
.if ONLY_1541_AND_COMPATIBLE = 0
drivecode71 .tag drivecode
drivecode81 .tag drivecode
.endif; ONLY_1541_AND_COMPATIBLE = 0
.endstruct
.ifndef __NO_CUSTOMDRIVECODE_SYMBOLS_IMPORT
.ifndef swapdrvcod
.import swapdrvcod
.import restoreldr
.endif; !swapdrvcod
; Execute custom code in the drive, buffer loader code on host side before
; in: x/y - x: lo, y: hi of swapparams structure
; out: c - set on error
; a - status
.macro SWAP_DRIVECODE params
ldx #<(params)
ldy #>(params)
jsr swapdrvcod
.endmacro
; Restore drive-side loader code
; in: nothing
; out: undefined
.macro RESTORE_LOADER
jsr restoreldr
.endmacro
.endif; !__NO_CUSTOMDRIVECODE_SYMBOLS_IMPORT
.struct saveparams
filename .word; existing file to overwrite, pointer to 0-terminated filename string
from .word; pointer to file data
length .word; length of file data in bytes
loadaddress .word; usually same as from
buffer .word; pointer to $0800 bytes for swapped loader drive code
.endstruct
.ifndef __NO_SAVE_SYMBOLS_IMPORT
.ifndef save
.import save
; Save a file by overwriting an existing file of the same block-size
; in: x/y - x: lo, y: hi of saveparams structure
; out: c - set on error
; a - status
.macro SAVE_OVERWRITE params
ldx #<(params)
ldy #>(params)
jsr save
.endmacro
.endif; !save
.endif; !__NO_SAVE_SYMBOLS_IMPORT
.ifndef __NO_LOADER_SYMBOLS_IMPORT
.if UNINSTALL_API
; Uninstall the loader
; in: nothing
; out: undefined
.import uninstall
.macro LOADER_UNINSTALL
jsr uninstall
.endmacro
.endif; UNINSTALL_API
.if CLOSE_FILE_API
; Close an open file
; in: nothing
; out: undefined
.import closefile
.endif; CLOSE_FILE_API
; linker-generated loader segments symbols
.import __DISKIO_ZP_START__
.import __DISKIO_ZP_END__
.import __DISKIO_ZP_LOAD__
.import __DISKIO_ZP_RUN__
.import __DISKIO_ZP_SIZE__
.import __DISKIO_INSTALL_START__
.import __DISKIO_INSTALL_END__
.import __DISKIO_INSTALL_LOAD__
.import __DISKIO_INSTALL_RUN__
.import __DISKIO_INSTALL_SIZE__
.import __DISKIO_START__
.import __DISKIO_END__
.import __DISKIO_LOAD__
.import __DISKIO_RUN__
.import __DISKIO_SIZE__
.endif; !__NO_LOADER_SYMBOLS_IMPORT
.ifndef __NOIMPORTVARS
.ifndef loadaddrlo
.importzp loadaddrlo
.importzp loadaddrhi
.importzp loadaddroffslo
.importzp loadaddroffshi
.importzp decdestlo
.importzp decdesthi
.importzp endaddrlo
.importzp endaddrhi
.importzp bytesloadedlo
.importzp bytesloadedhi
.endif; !loadaddrlo
.endif; !__NOIMPORTVARS
.if PLATFORM <> diskio::platform::COMMODORE_16
.macro INIT_CLEAR_ATN_OUT_CLEAR_CLK_OUT_CLEAR_DATA_OUT
; this is executed during install and LEAVE_BUS_LOCK,
; hence it accesses the port register rather than the data direction register
lda #3
and CIA2_PRA
sta CIA2_PRA
.endmacro
.endif
; custom drive code API
.ifndef ID041
.importzp ID041
.importzp CURRTRACK41
.importzp NUMFILES41
.importzp SECTORLINKTABLE41
.import topofstack41
.import idleloop41
.import getbytecmp41
.import BLOCKBUFFER41
.importzp V1B41
.importzp V2B41
.importzp LEDSTATE41
.importzp FILESECTOR41
.importzp FILENAME41
.importzp LINKTRACK41
.importzp LINKSECTOR41
.importzp REQUESTEDSECTOR41
.import trkseek41
.import initlink41
.import sertoraw41
.import getbytewdog41
.import getbyte41
.import getbyterts41
.import setbv2b41
.import getblock41
.import idxloop41
.import wdogentr41
.import findfile41
.import loadfile41
.endif
; Return-to-loader macros
; upon execution, i-flag must be set, ATN must be asserted, DATA and CLK must be cleared
.macro RETURNTOLOADER41 hook
.scope
.assert * >= $0100, error, "Return to 1541 loader code too low in memory"
lda #$1a; ATNA_OUT | CLK_OUT | DATA_OUT
sta $1800; VIA1_PRB
lda $1800; VIA1_PRB
and #$60; DEVICE_NUMBER
ora #$94; ATN_IN | ATNA_OUT | CLK_IN
sta compare + 1
ldx #0
lda #$10; ATNA_OUT
sta $1800; VIA1_PRB
ldy $1800; VIA1_PRB
getbyte: lda #$80
getbit: cpy $1800; VIA1_PRB
beq getbit
ldy $1800; VIA1_PRB
compare: cpy #0
ror
bcc getbit
sta $00,x
inx
bne getbyte
hook
jmp $00da
.assert * <= $0800, error, "Return to 1541 loader code too high in memory"
.endscope
.endmacro
.ifndef NUMFILES71
.importzp CURRTRACK71
.importzp LEDSTATE71
.importzp CLEARSECTORLINKTABLE71
.importzp FILENAME71
.importzp LINKTRACK71
.importzp LINKSECTOR71
.importzp SECTORLINKTABLE71
.importzp DIRSECTORS71
.importzp CUSTOMZPBUFFER71
.importzp CUSTOMUPLOADSIZE71
.importzp getbytrt71
.importzp CUSTOMPARAM71
.importzp bsetv2b71
.importzp trkseek71
.import initlink71
.import onemhz71
.import topofstack71
.import getblock71
.import idleloop71
.import findfile71
.import loadfile71
.import idxloop71
.import BLOCKBUFFER71
.endif
.macro RETURNTOLOADER71 hook
.scope
.assert * >= $0100, error, "Return to 1571 loader code too low in memory"
ldx #0
lda #$10; ATNA_OUT
sta $1800
ldy $1800; VIA1_PRB
getbyte: lda #$80
getbit: cpy $1800; VIA1_PRB
beq getbit
ldy $1800; VIA1_PRB
cpy #$94; ATN_IN | ATNA_OUT | CLK_IN
ror
bcc getbit
sta $00,x
inx
bne getbyte
hook
jmp $0037
.assert * <= $0800, error, "Return to 1571 loader code too high in memory"
.endscope
.endmacro
.ifndef dcodinit81
.importzp CUSTOMPARAM81
.import dcodinit81
.import DRVCODEND81
.import BLOCKBUFFER81
.import CUSTOMRECEIVE81
.import filename81
.import swapzp81
.import getblock81
.import initwdog81
.import enablwdg81
.import initcntr81
.import bsyledon81
.import findfile81
.import loadfile81
.import DIRTRACKS81
.endif
.macro RETURNTOLOADER81 hook
.scope
.assert * >= DRVCODEND81, error, "Return to 1581 loader code too low in memory"
ldy #0
sty $4001; CIA_PRB
getbyte: lda #$80
getbit: pha
lda $4001; CIA_PRB
waitbit: cmp $4001; CIA_PRB
beq waitbit
lda $4001; CIA_PRB
and #$04; CLK_IN
cmp #$04; CLK_IN
pla
ror
bcc getbit
restore: sta $0300 & $ff00,y
iny
bne :+
inc restore + 2
: cpy #<DRVCODEND81
bne getbyte
lda restore + 2
cmp #>DRVCODEND81
bne getbyte
hook
waitrun: lda $4001; CIA_PRB
lsr
bcc waitrun
jmp dcodinit81
.endscope
.endmacro
.popseg
.if (ALLOW_2_MHZ_ON_C128 & (PLATFORM = diskio::platform::COMMODORE_64)) | (PLATFORM = diskio::platform::COMMODORE_128)
USE_2_MHZ = 1
.else
USE_2_MHZ = 0
.endif
.if LOAD_VIA_KERNAL_FALLBACK
.if DIRTRACK <> 18
.error "***** Option LOAD_VIA_KERNAL_FALLBACK requires DIRTRACK to be 18 *****"
.endif
.if DIRTRACK81 <> 40
.error "***** Option LOAD_VIA_KERNAL_FALLBACK requires DIRTRACK81 to be 40 *****"
.endif
.if (PLATFORM = diskio::platform::COMMODORE_128) & ASYNCHRONOUS_BURST_HANDSHAKE
.error "***** Option LOAD_VIA_KERNAL_FALLBACK requires ASYNCHRONOUS_BURST_HANDSHAKE = 0 *****"
.endif
.macro CHECK_LOADER_ZP_ADDRESSES
.assert (loader_zp_first > STATUS) || (loader_zp_last < STATUS), error, "Loader zeropage variables cross KERNAL STATUS register at $90"
.assert (loader_zp_first > C3PO) || (loader_zp_last < C3PO), error, "Loader zeropage variables cross KERNAL serial buffer output char buffered flag C3PO at $94"
.assert (loader_zp_first > BSOUR) || (loader_zp_last < BSOUR), error, "Loader zeropage variables cross KERNAL serial buffer byte BSOUR at $95"
.if PLATFORM = diskio::platform::COMMODORE_16
.assert (loader_zp_first > LDTND) || (loader_zp_last < LDTND), error, "Loader zeropage variables cross KERNAL logical file index/open files count at $97"
.assert (loader_zp_first > DFLTN) || (loader_zp_last < DFLTN), error, "Loader zeropage variables cross KERNAL input device variable DFLTN at $98"
.assert (loader_zp_first > DFLTO) || (loader_zp_last < DFLTO), error, "Loader zeropage variables cross KERNAL output device variable DFLTO at $99"
.assert (loader_zp_first > R2D2) || (loader_zp_last < R2D2), error, "Loader zeropage variables cross KERNAL serial bus EOI flag R2D2 at $a6"
.assert (loader_zp_first > BSOUR1) || (loader_zp_last < BSOUR1), error, "Loader zeropage variables cross KERNAL serial bus shift counter BSOUR1 at $a8"
.assert (loader_zp_first > COUNT) || (loader_zp_last < COUNT), error, "Loader zeropage variables cross KERNAL serial bus counter COUNT at $aa"
.assert (loader_zp_first > USE4DY) || (loader_zp_last < USE4DY), error, "Loader zeropage variables cross KERNAL parallel drive state register USE4DY at $f9"
.else; PLATFORM <> diskio::platform::COMMODORE_16
.assert (loader_zp_first > LDTND) || (loader_zp_last < LDTND), error, "Loader zeropage variables cross KERNAL logical file index/open files count at $98"
.assert (loader_zp_first > DFLTN) || (loader_zp_last < DFLTN), error, "Loader zeropage variables cross KERNAL input device variable DFLTN at $99"
.assert (loader_zp_first > DFLTO) || (loader_zp_last < DFLTO), error, "Loader zeropage variables cross KERNAL output device variable DFLTO at $9a"
.assert (loader_zp_first > R2D2) || (loader_zp_last < R2D2), error, "Loader zeropage variables cross KERNAL serial bus EOI flag R2D2 at $a3"
.assert (loader_zp_first > BSOUR1) || (loader_zp_last < BSOUR1), error, "Loader zeropage variables cross KERNAL serial bus shift counter BSOUR1 at $a4"
.assert (loader_zp_first > COUNT) || (loader_zp_last < COUNT), error, "Loader zeropage variables cross KERNAL serial bus counter COUNT at $a5"
.endif; PLATFORM <> diskio::platform::COMMODORE_16
.endmacro
.else
.macro CHECK_LOADER_ZP_ADDRESSES
.endmacro
.endif
.endif; _LOADER_INC_