ys2-intro/loader/src/resident.s
2025-11-13 19:07:39 +03:00

1409 lines
34 KiB
ArmAsm
Executable file

.fileopt comment, "Loader resident code portion"
.fileopt compiler, "CA65"
.fileopt author, "Gunnar Ruthenberg"
__NO_LOADER_SYMBOLS_IMPORT = 1
__NOIMPORTVARS = 1
.include "loader.inc"
.include "kernal.inc"
.include "cpu.inc"
.include "hal/hal.inc"
HAVE_DECOMPRESSOR = DECOMPRESSOR <> DECOMPRESSORS::NONE
LOAD_TO_RAM_UNDER_IO = LOAD_UNDER_D000_DFFF & (PLATFORM <> diskio::platform::COMMODORE_16)
HAVE_ENDGETBLOCK = (PLATFORM <> diskio::platform::COMMODORE_64) | USE_2_MHZ
LOADCOMPD_TO = LOAD_COMPD_API & LOAD_TO_API
OPENFILE_POLLBLOCK = DISABLE_WATCHDOG | .defined(OPENFILE_POLLBLOCK_API)
.if LOAD_COMPD_API & (!HAVE_DECOMPRESSOR)
.error "***** Option LOAD_COMPD_API requires DECOMPRESSOR != DECOMPRESSORS::NONE *****"
.endif
.if MEM_DECOMP_API & (!HAVE_DECOMPRESSOR)
.error "***** Option MEM_DECOMP_API requires DECOMPRESSOR != DECOMPRESSORS::NONE *****"
.endif
.if MEM_DECOMP_TO_API & (!MEM_DECOMP_API)
.error "***** Option MEM_DECOMP_TO_API requires MEM_DECOMP_API *****"
.endif
.if HAVE_DECOMPRESSOR & (!LOAD_COMPD_API) & (!MEM_DECOMP_API)
.error "***** Decompressor included but not used, as neither LOAD_COMPD_API nor MEM_DECOMP_API are enabled *****"
.endif
.if (!LOAD_RAW_API) & (!LOAD_COMPD_API)
.error "***** No actual loading calls enabled, please select LOAD_RAW_API and/or LOAD_COMPD_API *****"
.endif
.if (FILENAME_MAXLENGTH < 1) | (FILENAME_MAXLENGTH > 16)
.error "***** Invalid FILENAME_MAXLENGTH setting, please set it to 1..16 *****"
.endif
.segment "DISKIO_ZP" : zeropage
; all zeropage variables can be
; overwritten when the loader is idle
.macro alloc_zpvar symbol
symbol: .res 1
.exportzp symbol
.endmacro
.macro alloc_zpvar_2 symbol
symbol: .res 2
.exportzp symbol
.endmacro
loader_zp_first = *
.export loader_zp_first
alloc_zpvar loadaddrlo
alloc_zpvar loadaddrhi
.if LOADCOMPD_TO
alloc_zpvar loadaddroffslo
alloc_zpvar loadaddroffshi
.endif
.if HAVE_DECOMPRESSOR
.if MEM_DECOMP_TO_API
alloc_zpvar decdestlo
alloc_zpvar decdesthi
.else
decdestlo: .res 1
decdesthi: .res 1
.endif
.if DECOMPRESSOR = DECOMPRESSORS::TSCRUNCH
.res 2; dummy variables for init loop
.elseif DECOMPRESSOR = DECOMPRESSORS::ZX0
.res 1; dummy variable for init loop
.endif
.endif
.if LOAD_VIA_KERNAL_FALLBACK | END_ADDRESS_API
alloc_zpvar endaddrlo
alloc_zpvar endaddrhi
.endif
alloc_zpvar_2 BLOCKDESTLO
BLOCKINDEX = BLOCKDESTLO
.if HAVE_DECOMPRESSOR
alloc_zpvar BLOCKSIZE
.else
BLOCKSIZE = BLOCKINDEX + 1
.endif
.if LOAD_COMPD_API | LOAD_TO_RAM_UNDER_IO
alloc_zpvar STACKPOINTER
.endif
.if HAVE_DECOMPRESSOR
alloc_zpvar STREAMAVAIL
.endif
.if PLATFORM <> diskio::platform::COMMODORE_16
alloc_zpvar GETBYTE_CLOCK_ATN_HI
.endif
.if LOAD_VIA_KERNAL_FALLBACK
alloc_zpvar KERNALBLKC
.endif
DECOMPVARS:
.exportzp DECOMPVARS
.if DECOMPRESSOR = DECOMPRESSORS::NONE
.elseif DECOMPRESSOR = DECOMPRESSORS::BITNAX
NO_ZP_READ_PTR = 1
POLLBLOCK_PRESERVE_X = 1
.res 7
.elseif DECOMPRESSOR = DECOMPRESSORS::BYTEBOOZER2
NO_ZP_READ_PTR = 1
POLLBLOCK_PRESERVE_X = 1
.res 1
.elseif DECOMPRESSOR = DECOMPRESSORS::DOYNAX_LZ
NO_ZP_READ_PTR = 1
POLLBLOCK_PRESERVE_X = 1
.res 3
.elseif DECOMPRESSOR = DECOMPRESSORS::EXOMIZER
NO_BLOCK_INTERFACE = 1
.res 9
.elseif DECOMPRESSOR = DECOMPRESSORS::LEVELCRUSH
NO_BLOCK_INTERFACE = 1
.res 2
.elseif DECOMPRESSOR = DECOMPRESSORS::LZSA2
.res 13
.elseif DECOMPRESSOR = DECOMPRESSORS::NUCRUNCH
NO_ZP_READ_PTR = 1
POLLBLOCK_PRESERVE_X = 1
.res 5
.elseif DECOMPRESSOR = DECOMPRESSORS::PUCRUNCH
NO_BLOCK_INTERFACE = 1
.res 3
.elseif DECOMPRESSOR = DECOMPRESSORS::SUBSIZER
NO_BLOCK_INTERFACE = 1
.res 8
.elseif DECOMPRESSOR = DECOMPRESSORS::TINYCRUNCH
EXTRAREADAHEAD = 1
.res 6
.elseif DECOMPRESSOR = DECOMPRESSORS::TSCRUNCH
.res 5
.elseif DECOMPRESSOR = DECOMPRESSORS::ZX0
USE_DALI = 1
.res 5
.endif
.ifndef POLLBLOCK_PRESERVE_X
POLLBLOCK_PRESERVE_X = 0
.endif
.ifdef NO_BLOCK_INTERFACE
HAVE_BLOCK_INTERFACE = 0
NO_ZP_READ_PTR = 1
decompsrc = getstrmbyt + 1
.else
HAVE_BLOCK_INTERFACE = 1
.endif
.ifndef EXTRAREADAHEAD
EXTRAREADAHEAD = 0
.endif
; base read-ahead of 1 due to read 254/use 256 bytes STREAMAVAIL inaccuracy (plus 1 for >= ~128 blocks, see getblock)
BASEREADAHEAD = 1
READAHEAD = BASEREADAHEAD + EXTRAREADAHEAD
.ifdef NO_ZP_READ_PTR
ZP_READ_PTR = 0
.else
ZP_READ_PTR = 1
.endif
HAVE_GETBYTE = !HAVE_BLOCK_INTERFACE
.macro SET_DECSRC_LO
.if LOAD_COMPD_API
.if ZP_READ_PTR
sta .lobyte(decompsrc)
.endif
.if HAVE_GETBYTE
sta decompsrc
.endif
.endif; LOAD_COMPD_API
.endmacro
.macro SET_DECSRC_HI
.if LOAD_COMPD_API
.if ZP_READ_PTR
sta .lobyte(decompsrc + 1)
.else
sta decompsrc + 1
.if DECOMPRESSOR = DECOMPRESSORS::ZX0
.if READ2_OPT
sta lz_read_2 + 2
.endif
sta lz_read_3 + 2
.endif
.endif
.endif; LOAD_COMPD_API
.endmacro
loader_zp_last = * - 1
.export loader_zp_last
CHECK_LOADER_ZP_ADDRESSES
.segment "DISKIO"
.if PLATFORM = diskio::platform::COMMODORE_16
KERNAL_FALLBACK_WAIT_FOR_BLOCK_READY_DELAY = $0c
.else
KERNAL_FALLBACK_WAIT_FOR_BLOCK_READY_DELAY = $07
.endif
.ifdef RESIADDR
.org RESIADDR - 2
.word * + 2; load address
.endif
CHECK_RESIDENT_START_ADDRESS
.if LOAD_RAW_API
.export loadraw
; --- load 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
; C-64/128: when LOAD_UNDER_D000_DFFF is non-0, this call assumes that the IO space at $d000 is enabled
loadraw: jsr openfile
.if LOAD_VIA_KERNAL_FALLBACK
bcs openerror; only with kernal fallback because only then the call might fail
.endif; LOAD_VIA_KERNAL_FALLBACK
.if LOAD_TO_RAM_UNDER_IO
BUFFER_MEMCONFIG
.endif; LOAD_TO_RAM_UNDER_IO
.if LOAD_COMPD_API | LOAD_TO_RAM_UNDER_IO
tsx
stx STACKPOINTER
.endif; LOAD_COMPD_API
:
.if LOAD_VIA_KERNAL_FALLBACK & LOAD_COMPD_API
lda #0
sta STREAMAVAIL
.endif; LOAD_VIA_KERNAL_FALLBACK & LOAD_COMPD_API
jsr pollblock
bcc :-
.if LOAD_TO_RAM_UNDER_IO
RESTORE_MEMCONFIG_Y
.endif
cmp #diskio::status::OK + 1
.if LOAD_VIA_KERNAL_FALLBACK
openerror:
.endif; LOAD_VIA_KERNAL_FALLBACK
rts
.endif; LOAD_RAW_API
.if LOAD_COMPD_API
.export loadcompd
.if DISABLE_WATCHDOG
.export loadcompdopen
.export loadcompdexecute
.endif; DISABLE_WATCHDOG
; --- 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 LOAD_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
; C-64/128: when LOAD_UNDER_D000_DFFF is non-0, this call assumes that the IO space at $d000 is enabled
loadcompd:
.if LOAD_TO_API
bcs :+
clc
jsr openfile
jmp compdfileopen
:
.endif; LOAD_TO_API
jsr loadcompdopen
compdfileopen:
.if LOAD_VIA_KERNAL_FALLBACK
; only with kernal fallback because only then the call can fail
bcc :+
rts
:
.endif; LOAD_VIA_KERNAL_FALLBACK
loadcompdexecute:
.if LOAD_TO_RAM_UNDER_IO
BUFFER_MEMCONFIG
.endif; LOAD_TO_RAM_UNDER_IO
tsx
stx STACKPOINTER
.if LOAD_TO_RAM_UNDER_IO
ENABLE_ALL_RAM
.endif; LOAD_TO_RAM_UNDER_IO
jsr decompress
.if LOAD_TO_RAM_UNDER_IO
ENABLE_IO_SPACE
.endif; LOAD_TO_RAM_UNDER_IO
; decompression is finished
; handle case that decompressing is as quick as loading,
; this will fetch the loading finished status and ack loading,
; and also loads any remaining uncompressed blob with in-place decompression
: jsr pollblock
bcc :-
; loading and decompression is done
OK_CLC; a = #diskio::status::OK; $00, clc = all ok
.endif; !LOAD_COMPD_API
pollfail:
.if LOAD_VIA_KERNAL_FALLBACK
stx :+ + 1; x holds KERNAL error code
.endif; LOAD_VIA_KERNAL_FALLBACK
.if LOAD_COMPD_API | LOAD_TO_RAM_UNDER_IO
ldx STACKPOINTER
txs
.endif; !LOAD_COMPD_API
.if LOAD_TO_RAM_UNDER_IO
RESTORE_MEMCONFIG_Y
.endif
.if LOAD_VIA_KERNAL_FALLBACK
php
tay
BRANCH_IF_NOT_INSTALLED :+
plp
polldone: php
IDLE
: ldx #0; x holds KERNAL error code
tya
plp
.else; !LOAD_VIA_KERNAL_FALLBACK
polldone: IDLE
.endif; !LOAD_VIA_KERNAL_FALLBACK
eofidle:
.if DECOMPRESSOR = DECOMPRESSORS::PUCRUNCH
; return execution address in x/y
ldx lo + 1
ldy hi + 1
.endif; DECOMPRESSOR = DECOMPRESSORS::PUCRUNCH
blknotrdy:
loaddone: rts
POLL_RESTORE = LOAD_VIA_KERNAL_FALLBACK | HAVE_ENDGETBLOCK
.if POLL_RESTORE
pollfailr: tay
ENDGETBLOCK
IDLE
tya
jmp pollfail
polldoner: tay
ENDGETBLOCK
tya
jmp polldone
.endif; POLL_RESTORE
.if OPENFILE_POLLBLOCK
.export pollblock
.endif; OPENFILE_POLLBLOCK
pollblock:
.if LOAD_VIA_KERNAL_FALLBACK
BRANCH_IF_INSTALLED :+
jmp kernalgblk
:
.endif; LOAD_VIA_KERNAL_FALLBACK
POLL_BLOCK eofidle, blknotrdy, pollfail
SEND_BLOCK_SIGNAL
lda #OPC_RTS; disable getblock store and loop
jsr getbyteswt; get next contiguous block index/block size or error/eof code
;sec
tax
inx
.if POLL_RESTORE
beq pollfailr; branch if a = $ff = diskio::status::FILE_NOT_FOUND
tay
beq polldoner
.else; !POLL_RESTORE
beq pollfail; branch if a = $ff = diskio::status::FILE_NOT_FOUND
tay
beq polldone
.endif; !POLL_RESTORE
dey
jsr get1byte; get block index and last block size/next contiguous block index flag
cmp #$80; sign extension
ror
bcs :+; branch if file's last block
.if LOAD_COMPD_API
sty STREAMAVAIL
.endif; LOAD_COMPD_API
ldy #$01; block size
: clc
adc BLOCKINDEX
sta BLOCKINDEX
clc
bne calcaddr
; first block: get load address
jsr get1byte; load address lo
.if LOADCOMPD_TO
clc
adc loadaddroffslo
php
.endif; LOADCOMPD_TO
storeladrl: sta loadaddrlo; is changed to lda on load_to
SET_DECSRC_LO
jsr get1byte; load address hi
.if LOADCOMPD_TO
plp
adc loadaddroffshi
sec
.endif; LOADCOMPD_TO
storeladrh: sta loadaddrhi; is changed to lda on load_to
SET_DECSRC_HI
;sec
calcaddr: ; calculate the position in memory according to the block index,
; this is performing: pos = loadaddr + BLOCKINDEX * 254 - (BLOCKINDEX ? 2 : 0)
php
lda loadaddrlo
sbc BLOCKINDEX
pha
lda loadaddrhi
adc BLOCKINDEX
tax
pla
plp
sbc BLOCKINDEX
bcs :+
dex
: clc
sty BLOCKSIZE
sbc BLOCKSIZE
sta storebyte + 1
; getblock loop/get1byte subroutine
.if LOAD_TO_RAM_UNDER_IO
; trading off size for speed: choose either of two getblock loops
; depending on the destination of the data to be downloaded
php
txa
sbc #1
cmp #.hibyte($cf00)
bcc :+
cmp #.hibyte($e000)
.if (PLATFORM = diskio::platform::COMMODORE_128) & USE_ASYNCHRONOUS_BURST_HANDSHAKE
bcs :+
jmp getblockio
.else; !((PLATFORM = diskio::platform::COMMODORE_128) & USE_ASYNCHRONOUS_BURST_HANDSHAKE)
bcc getblockio
.endif; !((PLATFORM = diskio::platform::COMMODORE_128) & USE_ASYNCHRONOUS_BURST_HANDSHAKE)
: plp
.endif; LOAD_TO_RAM_UNDER_IO
lda #OPC_STA_ABSY; enable getblock store and loop
getbyteswt: sta storebyte
txa
sbc #1
sta storebyte + 2
.macro STORE
storebyte: sta $0000,y
.endmacro; STORE
get1byte: RECEIVE STORE, recvdblock
.if LOAD_TO_RAM_UNDER_IO
recvdblock: jmp gotblock
getblockio: plp
sta storebytio + 2
lda storebyte + 1
sta storebytio + 1
RECEIVE STOREBYTE_ALLRAM, gotblock
.endif; LOAD_TO_RAM_UNDER_IO
EARLY_RECEIVE_RTS = HAVE_ENDGETBLOCK | END_ADDRESS_API | OPENFILE_POLLBLOCK
.if EARLY_RECEIVE_RTS
.ifdef recvdblock
gotblock:
.else
recvdblock:
.endif
.endif; EARLY_RECEIVE_RTS
.if END_ADDRESS_API
ldx storebyte + 1
cpx endaddrlo
ldy storebyte + 2
iny
tya
sbc endaddrhi
bcc :+
stx endaddrlo
sty endaddrhi
: ldy #0
.endif; END_ADDRESS_API
.if HAVE_ENDGETBLOCK
ENDGETBLOCK
.endif; HAVE_ENDGETBLOCK
.if EARLY_RECEIVE_RTS
.if OPENFILE_POLLBLOCK
lda #diskio::status::OK
.endif; OPENFILE_POLLBLOCK
clc
rts
.endif; EARLY_RECEIVE_RTS
.if OPENFILE_POLLBLOCK
.export openfile
.endif; OPENFILE_POLLBLOCK
openfile:
.if LOADCOMPD_TO
lda #$00
sta loadaddroffslo
sta loadaddroffshi
.endif; LOADCOMPD_TO
.if LOAD_TO_API
lda #OPC_LDA_ZP
bcs :+
loadcompdopen:
lda #OPC_STA_ZP
: sta storeladrl
sta storeladrh
.else; !LOAD_TO_API
loadcompdopen:
.endif; !LOAD_TO_API
.if MEM_DECOMP_TO_API
lda #OPC_STA_ZP
sta storedadrl
sta storedadrh
.endif; MEM_DECOMP_TO_API
.if LOAD_VIA_KERNAL_FALLBACK
.if LOAD_COMPD_API
lda #0
sta STREAMAVAIL
.endif; LOAD_COMPD_API
BRANCH_IF_INSTALLED :+
jmp kernalopen
:
.endif; LOAD_VIA_KERNAL_FALLBACK
jsr wakeup
: BRANCH_IF_BLOCK_NOT_READY :-
;ldy #0
sendname: lda (BLOCKDESTLO),y
pha
SENDBYTE sendstore
iny
pla
bne sendname
.if LOAD_VIA_KERNAL_FALLBACK
; check whether the loader is still installed
ldx #KERNAL_FALLBACK_WAIT_FOR_BLOCK_READY_DELAY - 1; some delay until the drive side is ready
: dex
bpl :-
SET_FLAGS_N_DATA_V_CLK
bvc :+
; if not, try again with KERNAL routines
ldx BLOCKDESTLO
ldy BLOCKDESTLO + 1
jmp kernalopen
:
.endif; LOAD_VIA_KERNAL_FALLBACK
.if DECOMPRESSOR = DECOMPRESSORS::ZX0
.if USE_DALI
.else
ldy #$40; .lz_bits
.endif
.endif; DECOMPRESSOR = DECOMPRESSORS::ZX0
wakeup: stx BLOCKINDEX; = BLOCKDESTLO, filename pointer lobyte parameter
sty BLOCKINDEX + 1
ldy #0
.if HAVE_GETBYTE
stx getstrmbyt + 1
.endif; HAVE_GETBYTE
.if LOAD_COMPD_API
sty STREAMAVAIL
.endif; LOAD_COMPD_API
.if END_ADDRESS_API
sty endaddrlo
sty endaddrhi
.endif; END_ADDRESS_API
CLEAR sendstore
RECEIVE_SETUP
.if LOAD_VIA_KERNAL_FALLBACK
clc
.endif; LOAD_VIA_KERNAL_FALLBACK
.ifndef recvdblock
recvdblock:
.endif
.ifndef gotblock
gotblock:
.endif
rts
.macro POLLBLOCK
.local noblock
.if LOAD_COMPD_API
.if LOAD_TO_RAM_UNDER_IO
BUFFER_MEMCONFIG
ENABLE_IO_SPACE
.endif; LOAD_TO_RAM_UNDER_IO
.if LOAD_VIA_KERNAL_FALLBACK
BRANCH_IF_NOT_INSTALLED noblock
.endif; LOAD_VIA_KERNAL_FALLBACK
BRANCH_IF_BLOCK_NOT_READY noblock
.if POLLBLOCK_PRESERVE_X
txa
pha
.endif; POLLBLOCK_PRESERVE_X
jsr pollblock
.if POLLBLOCK_PRESERVE_X
pla
tax
.endif; POLLBLOCK_PRESERVE_X
noblock:
.if LOAD_TO_RAM_UNDER_IO
RESTORE_MEMCONFIG_Y
ldy #0
.endif; LOAD_TO_RAM_UNDER_IO
.endif; LOAD_COMPD_API
.endmacro
.macro GETBLOCK page
.local getblock
.local eof
.if LOAD_TO_RAM_UNDER_IO
BUFFER_MEMCONFIG
ENABLE_IO_SPACE
.endif; LOAD_TO_RAM_UNDER_IO
getblock: jsr pollblock
bcs eof
lda STREAMAVAIL; add 1 for read 254/use 256 bytes STREAMAVAIL
asl ; inaccuracy starting with 128 file blocks
lda #READAHEAD + 1; +1 because the following sbc will subtract one too many due to cleared carry
adc page
;clc
sbc loadaddrhi
cmp STREAMAVAIL
bcs getblock
eof:
.if LOAD_TO_RAM_UNDER_IO
RESTORE_MEMCONFIG_Y
ldy #0
.endif; LOAD_TO_RAM_UNDER_IO
.endmacro
.if HAVE_GETBYTE
getbyte: inc getstrmbyt + 1
beq :+
.if LOAD_TO_RAM_UNDER_IO
ENABLE_IO_SPACE
.endif; LOAD_TO_RAM_UNDER_IO
.if LOAD_VIA_KERNAL_FALLBACK
BRANCH_IF_NOT_INSTALLED retstrmbyt
.endif; LOAD_VIA_KERNAL_FALLBACK
BRANCH_IF_BLOCK_READY :++
retstrmbyt:
.if LOAD_TO_RAM_UNDER_IO
ENABLE_ALL_RAM
.endif; LOAD_TO_RAM_UNDER_IO
getstrmbyt: lda $ff00
rts
: inc getstrmbyt + 2
: php
txa
pha
tya
pha
GETBLOCK getstrmbyt + 2
pla
tay
pla
tax
plp
jmp retstrmbyt
.endif; HAVE_GETBYTE
.if FILE_EXISTS_API
.export fileexists
; --- 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
fileexists:
.if LOAD_VIA_KERNAL_FALLBACK
BRANCH_IF_INSTALLED xinstalled
BUFFER_MEMCONFIG
ENABLE_KERNAL_SERIAL_ROUTINES
jsr :+
RESTORE_MEMCONFIG_Y
rts
: jsr openfile
bcs xreturn
ldx LA
jsr CHKIN
jsr BASIN
lda STATUS
pha
jsr KERNLCLOSE
pla
beq :+
lda #diskio::status::FILE_NOT_FOUND
sec
rts
: OK_CLC
rts
xinstalled:
.endif; LOAD_VIA_KERNAL_FALLBACK
jsr openfile
xblknotrdy: POLL_BLOCK xeofidle, xblknotrdy, xpollfail
CLOSE_FILE
lda #diskio::status::FILE_NOT_FOUND
bcc xreturn
lda #diskio::status::OK
SKIPWORD
xeofidle:
xpollfail: lda #diskio::status::DEVICE_NOT_PRESENT
xreturn: cmp #diskio::status::OK + 1
rts
.endif; FILE_EXISTS_API
.if CLOSE_FILE_API
.export closefile
; ----- close an open file -----
; in: nothing
; out: undefined
closefile:
.if LOAD_VIA_KERNAL_FALLBACK
BRANCH_IF_INSTALLED cinstalled
BUFFER_MEMCONFIG
ENABLE_KERNAL_SERIAL_ROUTINES
jsr KERNLCLOSE
RESTORE_MEMCONFIG_Y
rts
cinstalled:
.endif; LOAD_VIA_KERNAL_FALLBACK
cblknotrdy: POLL_BLOCK ceofidle, cblknotrdy, cpollfail
CLOSE_FILE
ceofidle:
cpollfail: rts
.endif; CLOSE_FILE_API
.if LOAD_VIA_KERNAL_FALLBACK
.export KERNLCLOSE
kernalopen: BUFFER_MEMCONFIG
ENABLE_KERNAL_SERIAL_ROUTINES
jsr :+
RESTORE_MEMCONFIG_Y
rts
:
.if KERNAL_FALLBACK_SEI_WORKAROUNDS
ENABLE_WAITBUSY_KERNAL
.endif; KERNAL_FALLBACK_SEI_WORKAROUNDS
stx namestrpos
sty namestrpos + 1
ldx #$ff
: inx
namestrpos = * + 1
lda a:$00,x
bne :-
txa
pha; name length
.if PLATFORM = diskio::platform::COMMODORE_128
lda #0
sta FNLEN
lda #COMMAND_ERROR_CHANNEL
ldx FA
tay
jsr SETLFS
jsr OPEN
bcs burstopner
.if KERNAL_FALLBACK_OPEN_SEI_WORKAROUNDS
PUSH_CLOCKCONFIG_AND_FORCE_SLOW_CLOCK
CLEAR_DATA_OUT_CLEAR_CLK_OUT_ASSERT_ATN
: SET_FLAGS_N_DATA_V_CLK
bpl :-
ldx #$b8; delay
: dex ; of
bne :- ; ~1 ms
lda FA
sta DFLTO
ora #SA_LISTEN
sta BSOUR
sei
jsr NOEOI; send byte
lda #SA_OPENCHANNEL | COMMAND_ERROR_CHANNEL
jsr SECND; send listen secondary address
POP_CLOCKCONFIG
.else; !KERNAL_FALLBACK_SEI_WORKAROUNDS
ldx LA
jsr CKOUT
.endif; !KERNAL_FALLBACK_SEI_WORKAROUNDS
bcc :+
burstopner: tax
pla; name length
txa
jmp kernlopner
: ldy #burstldcme - burstldcmd
: lda burstldcmd - 1,y
jsr BSOUT
dey
bne :-
pla; name length
sta FNLEN
lda namestrpos
sta FNADR
lda namestrpos + 1
sta FNADR + 1
: lda (FNADR),y
jsr BSOUT
iny
cpy FNLEN
bne :-
jsr CLRCH
bit SERIAL
bvs burstload
lda FNLEN
pha
lda LA
jsr CLOSE
jmp noburstlod
burstload: START_BURST_LOAD
jmp kernalfinp
getburstby: GET_BURST_BYTE
rts
burstldcmd: .byte $1f, "0u"; read backwards
burstldcme:
noburstlod:
.endif; PLATFORM = diskio::platform::COMMODORE_128
PREPARE_PARALLEL_CHECK
lda #KERNALFILENO
ldx FA
ldy #$00
jsr SETLFS
pla; name length
ldx namestrpos
ldy namestrpos + 1
jsr SETNAM
jsr OPEN
bcs kernlopner
.if KERNAL_FALLBACK_OPEN_SEI_WORKAROUNDS
BRANCH_IF_DRIVE_PARALLEL noseiwrkar
.if USE_2_MHZ
PUSH_CLOCKCONFIG_AND_FORCE_SLOW_CLOCK
.endif
CLEAR_DATA_OUT_CLEAR_CLK_OUT_ASSERT_ATN
: SET_FLAGS_N_DATA_V_CLK
bpl :-
ldx #$b8; delay
: dex ; of
bne :- ; ~1 ms
lda FA
sta DFLTN
ora #SA_TALK
sta BSOUR
sei
jsr NOEOI; send byte
lda #SA_OPENCHANNEL; secondary address
jsr TKSA; send talk secondary address
.if USE_2_MHZ
POP_CLOCKCONFIG
.endif
jmp kernalfinp
noseiwrkar:
.endif; !KERNAL_FALLBACK_SEI_WORKAROUNDS
ldx LA
jsr CHKIN
kernalfinp: ldx #$ff
stx KERNALBLKC
.if HAVE_GETBYTE
stx getstrmbyt + 1
.endif
; file not found is not detected at this point
; but after "getting" the load address,
; the busy LED will keep flashing
OK_CLC
rts
kernlopner: tax
lda #diskio::status::DEVICE_NOT_PRESENT
cpx #OPEN_DEVICENOTPRESENT
beq :+
lda #diskio::status::GENERIC_KERNAL_ERROR
: sec; error
rts
kernalgblk:
.if LOAD_COMPD_API
clc
lda #READAHEAD + 1; +1 because the following sbc will subtract one too many due to cleared carry
.if ZP_READ_PTR
adc .lobyte(decompsrc + 1)
.else
adc decompsrc + 1
.endif
;clc
sbc loadaddrhi
cmp STREAMAVAIL
bcs :+
rts; requested page is already in memory
:
.endif; LOAD_COMPD_API
BUFFER_MEMCONFIG
ENABLE_KERNAL_SERIAL_ROUTINES
jsr dokernalgb
RESTORE_MEMCONFIG_Y
.if LOAD_COMPD_API
bcc kernalgblk
beq :+
.else; !LOAD_COMPD_API
beq :+
bcc :+
.endif; !LOAD_COMPD_API
jmp pollfail
: rts
dokernalgb: ldx KERNALBLKC
bne kernalgble
ldx #$fe; $0100 bytes minus 2 bytes for track/sector link
.if PLATFORM = diskio::platform::COMMODORE_128
bit SERIAL
bvc :+
jsr gburstatus
bcc :+
jmp kernlgberr
:
.endif; PLATFORM = diskio::platform::COMMODORE_128
stx KERNALBLKC
kernalgble: cpx #$ff
bne kernalgblp
inc KERNALBLKC; EOF may happen in first block
; retrieve load address
ldx #$fc; $0100 bytes minus 2 bytes track/sector link and 2 bytes load address
.if PLATFORM = diskio::platform::COMMODORE_128
bit SERIAL
bvc burstopnok
jsr getburstby; status
cmp #KERNAL_STATUS_ERROR_BURST
bcc burstopnok
bne :+
lda #diskio::status::FILE_NOT_FOUND
: cmp #KERNAL_STATUS_EOF_BURST
beq :+
; this is a job error code
jmp KERNLCLOSE
: jsr getburstby
tax
dex; load address
dex
burstopnok:
.endif; PLATFORM = diskio::platform::COMMODORE_128
.if LOAD_TO_API
lda #OPC_STA_ZP
cmp storeladrl
beq nokrnlodto
lda loadaddrlo
sta endaddrlo
lda loadaddrhi
sta endaddrhi
.if PLATFORM = diskio::platform::COMMODORE_128
bit SERIAL
bvc :+
jsr getburstby; skip load
jsr getburstby; address
jmp kern1stblk
:
.endif; PLATFORM = diskio::platform::COMMODORE_128
jsr BASIN; skip load
jsr BASIN; address
jmp kern1stblk
nokrnlodto:
.endif; LOAD_TO_API
.if PLATFORM = diskio::platform::COMMODORE_128
bit SERIAL
bvc :+
jsr getburstby
jmp :++
: jsr BASIN
:
.else; !PLATFORM = diskio::platform::COMMODORE_128
jsr BASIN
.endif; !PLATFORM = diskio::platform::COMMODORE_128
.if LOADCOMPD_TO
clc
adc loadaddroffslo
php
.endif; LOADCOMPD_TO
sta loadaddrlo
kernalstrl: sta endaddrlo
SET_DECSRC_LO
.if PLATFORM = diskio::platform::COMMODORE_128
bit SERIAL
bvc :+
jsr getburstby
jmp :++
: jsr BASIN
:
.else; !PLATFORM = diskio::platform::COMMODORE_128
jsr BASIN
.endif; !PLATFORM = diskio::platform::COMMODORE_128
.if LOADCOMPD_TO
plp
adc loadaddroffshi
.endif; LOADCOMPD_TO
sta loadaddrhi
kernalstrh: sta endaddrhi
SET_DECSRC_HI
kern1stblk: lda STATUS
bne kernfnotf; #diskio::status::FILE_NOT_FOUND; cannot determine type of error
kernalgblp:
.if PLATFORM = diskio::platform::COMMODORE_128
bit SERIAL
bvc :+
jsr getburstby
jmp kernbytldd
:
.endif; PLATFORM = diskio::platform::COMMODORE_128
lda STATUS
bne kernlgberr
.if KERNAL_FALLBACK_SEI_WORKAROUNDS
BRANCH_IF_DRIVE_PARALLEL :++
: BRANCH_IF_BLOCK_NOT_READY :-
:
.endif; KERNAL_FALLBACK_SEI_WORKAROUNDS
jsr BASIN
ldy STATUS
beq kernbytldd
cpy #KERNAL_STATUS_EOF; $40
bne KERNLCLOSE
pha
tya
jsr KERNLCLOSE
pla
kernbytldd:
.if LOAD_TO_RAM_UNDER_IO
ENABLE_ALL_RAM_Y
.endif; LOAD_TO_RAM_UNDER_IO
ldy #0
sta (endaddrlo),y
.if LOAD_TO_RAM_UNDER_IO
ENABLE_KERNAL_SERIAL_ROUTINES
.endif; LOAD_TO_RAM_UNDER_IO
inc endaddrlo
bne :+
inc endaddrhi
: dex
bne kernalgblp
stx KERNALBLKC
.if LOAD_COMPD_API
clc
lda endaddrhi
sbc loadaddrhi
bcs :+
lda #0
: sta STREAMAVAIL
.endif; LOAD_COMPD_API
clc
kernalbeof: lda #diskio::status::OK
rts
kernlgberr: cmp #KERNAL_STATUS_EOF; $40
beq kernalbeof; carry is set when branching
sec
rts
.if PLATFORM = diskio::platform::COMMODORE_128
gburstatus: lda STATUS
cmp #KERNAL_STATUS_ERROR_BURST
bcc :++
cmp #KERNAL_STATUS_EOF_BURST
bne :+
lda #KERNAL_STATUS_EOF; $40
sta STATUS
: jsr KERNLCLOSE
bcs :++
: jsr getburstby
cmp #KERNAL_STATUS_ERROR_BURST
bcc :+
sta STATUS
cmp #KERNAL_STATUS_EOF_BURST
bne :--; branch if not EOF, this is a job error code
jsr getburstby
tax
clc
: rts
.endif; PLATFORM = diskio::platform::COMMODORE_128
kernfnotf: lda #diskio::status::FILE_NOT_FOUND; cannot determine type of error
; EOF or error, close file
KERNLCLOSE: pha; KERNAL status byte
lda #KERNALFILENO
jsr CLOSE
jsr CLALL
jsr CLRCH
pla; KERNAL status byte
.if PLATFORM = diskio::platform::COMMODORE_16
sta STATUS; the CLRCH call above sets STATUS ($90)
.endif; PLATFORM = diskio::platform::COMMODORE_16
cmp #KERNAL_STATUS_EOF; $40
bne kernaloerr
;sec
rts; EOF
kernaloerr: sec
tax
cmp #diskio::status::DEVICE_NOT_PRESENT
bcs :+
lda #diskio::status::GENERIC_KERNAL_ERROR
sec
: rts
.endif; LOAD_VIA_KERNAL_FALLBACK
.if MEM_DECOMP_API
.export memdecomp
; --- decompress a compressed file from memory ---
; in: x/y - lo/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 (decdestlo/hi)
; out: undefined
memdecomp:
.if MEM_DECOMP_TO_API
lda #OPC_STA_ZP
bcc :+
lda #OPC_LDA_ZP
: sta storedadrl
sta storedadrh
.endif; MEM_DECOMP_TO_API
stx loadaddrlo
sty loadaddrhi
.if DECOMPRESSOR = DECOMPRESSORS::ZX0
txa
bne :+
dey
: dex
.endif
.if ZP_READ_PTR
stx .lobyte(decompsrc)
.endif; ZP_READ_PTR
.if HAVE_GETBYTE
dex
stx decompsrc
.endif; HAVE_GETBYTE
.if ZP_READ_PTR
sty .lobyte(decompsrc + 1)
.else; !ZP_READ_PTR
.if HAVE_GETBYTE
cpx #$ff
bne :+
dey
:
.endif; HAVE_GETBYTE
sty decompsrc + 1
.endif; !ZP_READ_PTR
ldy #0
.if DECOMPRESSOR = DECOMPRESSORS::PUCRUNCH
jsr decompress
; return the execution address in x/y
ldx lo + 1
ldy hi + 1
rts
.else
.if DECOMPRESSOR = DECOMPRESSORS::ZX0
.if USE_DALI
.else
ldx #$40
stx BLOCKINDEX + 1; .lz_bits
.endif
.endif
jmp decompress
.endif
.endif; MEM_DECOMP_API
.if UNINSTALL_API
.export uninstall
; --- uninstall the loader ---
; in: nothing
; out: undefined
uninstall: DO_UNINSTALL
rts
.endif; UNINSTALL_API
.if DECOMPRESSOR = DECOMPRESSORS::BITNAX
.export DECOMPRESSOR_BITNAX = DECOMPRESSOR
.include "decompress/bitnaxdecomp.s"
.elseif DECOMPRESSOR = DECOMPRESSORS::BYTEBOOZER2
.export DECOMPRESSOR_BYTEBOOZER2 = DECOMPRESSOR
.include "decompress/b2decomp.s"
.elseif DECOMPRESSOR = DECOMPRESSORS::DOYNAX_LZ
.export DECOMPRESSOR_DOYNAX_LZ = DECOMPRESSOR
.include "decompress/doynaxdecomp.s"
.elseif DECOMPRESSOR = DECOMPRESSORS::EXOMIZER
.export DECOMPRESSOR_EXOMIZER = DECOMPRESSOR
.include "decompress/exodecomp.s"
.elseif DECOMPRESSOR = DECOMPRESSORS::LEVELCRUSH
.export DECOMPRESSOR_LEVELCRUSH = DECOMPRESSOR
.include "decompress/lcdecomp.s"
.elseif DECOMPRESSOR = DECOMPRESSORS::LZSA2
.export DECOMPRESSOR_LZSA2 = DECOMPRESSOR
.include "decompress/lzsa2decomp.s"
.elseif DECOMPRESSOR = DECOMPRESSORS::NUCRUNCH
.export DECOMPRESSOR_NUCRUNCH = DECOMPRESSOR
.include "decompress/ncdecomp.s"
.elseif DECOMPRESSOR = DECOMPRESSORS::PUCRUNCH
.export DECOMPRESSOR_PUCRUNCH = DECOMPRESSOR
.include "decompress/pudecomp.s"
.elseif DECOMPRESSOR = DECOMPRESSORS::SUBSIZER
.export DECOMPRESSOR_SUBSIZER = DECOMPRESSOR
.include "decompress/subsizerdecomp.s"
.elseif DECOMPRESSOR = DECOMPRESSORS::TINYCRUNCH
.export DECOMPRESSOR_TINYCRUNCH = DECOMPRESSOR
.include "decompress/tcdecomp.s"
.elseif DECOMPRESSOR = DECOMPRESSORS::TSCRUNCH
.export DECOMPRESSOR_TSCRUNCH = DECOMPRESSOR
.include "decompress/tsdecomp.s"
.elseif DECOMPRESSOR = DECOMPRESSORS::ZX0
.export DECOMPRESSOR_ZX0 = DECOMPRESSOR
.include "decompress/zx0decomp.s"
.else
.export DECOMPRESSOR_NONE = DECOMPRESSORS::NONE
.endif
CHECK_RESIDENT_END_ADDRESS