init files

This commit is contained in:
AArt1256 2025-11-13 19:07:39 +03:00
commit 8197a022bd
1409 changed files with 139317 additions and 0 deletions

271
loader/src/Makefile Executable file
View file

@ -0,0 +1,271 @@
INSTALL ?= 1000
RESIDENT ?= 0200
TRANSIENT ?= 4000
ZP ?= e0
export _PLATFORM_ INSTALL RESIDENT TRANSIENT ZP
ifeq ($(PLATFORM),)
_PLATFORM_ = c64
else ifeq ($(PLATFORM),c128)
_PLATFORM_ = c128
else ifeq ($(PLATFORM),c16)
_PLATFORM_ = c16
else ifeq ($(PLATFORM),c116)
_PLATFORM_ = c16
else ifeq ($(PLATFORM),plus4)
_PLATFORM_ = c16
else
_PLATFORM_ = $(PLATFORM)
endif
ifneq ($(_PLATFORM_),c64)
ifneq ($(_PLATFORM_),c128)
ifneq ($(_PLATFORM_),c16)
$(error invalid platform $(_PLATFORM_) specified)
endif
endif
endif
TOP ?= .
SVNVERSION = svnversion
ifneq ($(wildcard ../../.svn/format),)
VERSION = $(shell $(SVNVERSION) | tr -d [:cntrl:])
else
VERSION = $(shell $(GREP) -oP 'VERSION_STRING "\K[^"]+' ../version.inc)
endif
CD = cd
ECHO = echo
PRINTF = printf
CAT = cat
CP = cp
MV = mv
RM = rm -f
MKDIR = mkdir
RMDIR = rmdir
GREP = ggrep
AS = ca65
LD = ld65
AR = ar65
VICE = x64
C1541 = c1541
PERL = perl
ZIP = zip -9
INCDIR = $(TOP)/../../shared
LDRINC = $(TOP)/../include
DECOMPDIR = $(TOP)/decompress
DRIVESDIR = $(TOP)/drives
HALDIR = $(TOP)/hal
BUILDDIR = $(TOP)/../build
INTERMDIR = $(TOP)/../build/intermediate
ifneq ($(EXTCONFIGPATH),)
CONFIG = $(EXTCONFIGPATH)/loaderconfig.inc
else
CONFIG = $(LDRINC)/config.inc
endif
INSTALLDEPS = $(CONFIG) $(BUILDDIR) $(INTERMDIR) \
$(DRIVESDIR)/drivecode-common.inc $(DRIVESDIR)/drivecode1541.s $(DRIVESDIR)/drivecode1571.s $(DRIVESDIR)/drivecode1581.s \
$(HALDIR)/hal.inc $(HALDIR)/hal-c64-c128.inc $(HALDIR)/hal-c16.inc \
install.s \
$(LDRINC)/loader.inc \
$(TOP)/../version.inc Makefile
RESIDENTDEPS = $(CONFIG) $(BUILDDIR) $(INTERMDIR) \
$(DECOMPDIR)/bitnaxdecomp.s \
$(DECOMPDIR)/b2decomp.s \
$(DECOMPDIR)/doynaxdecomp.s \
$(DECOMPDIR)/exodecomp.s \
$(DECOMPDIR)/lcdecomp.s \
$(DECOMPDIR)/lzsa2decomp.s \
$(DECOMPDIR)/ncdecomp.s \
$(DECOMPDIR)/pudecomp.s \
$(DECOMPDIR)/subsizerdecomp.s \
$(DECOMPDIR)/tcdecomp.s \
$(DECOMPDIR)/zx0decomp.s \
$(HALDIR)/hal.inc $(HALDIR)/hal-c64-c128.inc $(HALDIR)/hal-c16.inc \
resident.s \
customdrivecode.s \
save.s \
$(LDRINC)/loader.inc \
$(TOP)/../version.inc Makefile
BINDEPS = $(BUILDDIR)/loadersymbols-$(_PLATFORM_).inc $(BUILDDIR)/loader-$(_PLATFORM_).prg $(CONFIG)
# allow undocumented opcodes
ifeq ($(_PLATFORM_),c64)
AS_FLAGS = --cpu 6502X
else ifeq ($(_PLATFORM_),c128)
AS_FLAGS = --cpu 6502X
else ifeq ($(_PLATFORM_),c16)
AS_FLAGS = --cpu 6502X
else
# do not allow undocumented opcodes
AS_FLAGS = --cpu 6502
endif
AS_FLAGS += -g # include symbols in object files
ifeq ($(_PLATFORM_),c16)
AS_FLAGS += -t c16 -D PLATFORM=16
else ifeq ($(_PLATFORM_),c128)
AS_FLAGS += -t c128 -D PLATFORM=128
else
AS_FLAGS += -t c64 -D PLATFORM=64
endif
AS_FLAGS += -I $(TOP)/. -I $(INCDIR) -I $(LDRINC)
ifneq ($(EXTCONFIGPATH),)
AS_FLAGS += -D EXTCONFIGPATH -I $(EXTCONFIGPATH)
endif
.PHONY: lib prg bin binary prgzip binzip binaryzip customdrivecode save
default: prg
all: lib prg
# use these targets to build loader-<PLATFORM>.prg and install-<PLATFORM>.prg along with loadersymbols-<PLATFORM>.inc
prg bin binary: $(BINDEPS)
@$(ECHO) "Usage: $(MAKE) prg INSTALL=<install hexaddress> RESIDENT=<core hexaddress> ZP=<zp hexaddress> [PROJECT=<project>]"
@$(ECHO) "INSTALL=\$$$(INSTALL)"
@$(ECHO) "RESIDENT=\$$$(RESIDENT)"
@$(ECHO) "ZP=\$$$(ZP)"
@$(ECHO) "PROJECT=$(PROJECT)"
$(CAT) $(BUILDDIR)/loadersymbols-$(_PLATFORM_).inc
prgzip binzip binaryzip: $(BUILDDIR)/loader-$(_PLATFORM_).zip
customdrivecode: $(BUILDDIR)/customdrivecode-$(_PLATFORM_).prg
@$(ECHO) "Usage: $(MAKE) customdrivecode INSTALL=<install hexaddress> RESIDENT=<core hexaddress> TRANSIENT=<plug-ins hexaddress> ZP=<zp hexaddress> [PROJECT=<project>]"
@$(ECHO) "INSTALL=\$$$(INSTALL)"
@$(ECHO) "RESIDENT=\$$$(RESIDENT)"
@$(ECHO) "TRANSIENT=\$$$(TRANSIENT)"
@$(ECHO) "ZP=\$$$(ZP)"
@$(ECHO) "PROJECT=$(PROJECT)"
$(CAT) $(BUILDDIR)/loadersymbols-$(_PLATFORM_).inc
save: $(BUILDDIR)/save-$(_PLATFORM_).prg
@$(ECHO) "Usage: $(MAKE) save INSTALL=<install hexaddress> RESIDENT=<core hexaddress> TRANSIENT=<plug-ins hexaddress> ZP=<zp hexaddress> [PROJECT=<project>]"
@$(ECHO) "INSTALL=\$$$(INSTALL)"
@$(ECHO) "RESIDENT=\$$$(RESIDENT)"
@$(ECHO) "TRANSIENT=\$$$(TRANSIENT)"
@$(ECHO) "ZP=\$$$(ZP)"
@$(ECHO) "PROJECT=$(PROJECT)"
$(CAT) $(BUILDDIR)/loadersymbols-$(_PLATFORM_).inc
lib: $(BUILDDIR)/loader-$(_PLATFORM_).lib
$(TOP)/../version.inc:
$(MAKE) -C $(TOP)/.. version.inc
# directory targets
$(BUILDDIR):
$(MKDIR) $@
$(INTERMDIR):
$(MKDIR) $@
# object targets
$(INTERMDIR)/install-$(_PLATFORM_).o: $(INSTALLDEPS)
$(AS) $(AS_FLAGS) -I $(INTERMDIR) -o $@ install.s # allow undocumented opcodes for drive code
$(INTERMDIR)/customdrivecode-$(_PLATFORM_).o: $(RESIDENTDEPS)
$(AS) $(AS_FLAGS) -o $@ customdrivecode.s
$(INTERMDIR)/save-$(_PLATFORM_).o: $(RESIDENTDEPS)
$(AS) $(AS_FLAGS) -o $@ save.s
$(INTERMDIR)/resident-$(_PLATFORM_).o: $(RESIDENTDEPS)
$(AS) $(AS_FLAGS) -o $@ resident.s
# binary targets
.PHONY: $(INTERMDIR)/binary.link
.PHONY: $(BUILDDIR)/loader-$(_PLATFORM_).prg $(INTERMDIR)/loader-nonreloc-$(_PLATFORM_).o
.PHONY: $(BUILDDIR)/install-$(_PLATFORM_).prg $(INTERMDIR)/install-nonreloc-$(_PLATFORM_).o
.PHONY: $(BUILDDIR)/customdrivecode-$(_PLATFORM_).prg $(INTERMDIR)/customdrivecode-nonreloc-$(_PLATFORM_).o
.PHONY: $(BUILDDIR)/save-$(_PLATFORM_).prg $(INTERMDIR)/save-nonreloc-$(_PLATFORM_).o
.PHONY: $(INTERMDIR)/loader.map $(BUILDDIR)/loadersymbols-$(_PLATFORM_).inc
$(INTERMDIR)/loader-nonreloc-$(_PLATFORM_).o: $(RESIDENTDEPS)
$(AS) $(AS_FLAGS) -D RESIADDR=0x$(RESIDENT) -o $@ resident.s
$(INTERMDIR)/install-nonreloc-$(_PLATFORM_).o: $(RESIDENTDEPS)
$(AS) $(AS_FLAGS) -D INSTADDR=0x$(INSTALL) -o $@ install.s
$(INTERMDIR)/binary.link: Makefile
$(PERL) make-linkfile.pl $(_PLATFORM_) $(ZP) $(INSTALL) $(RESIDENT) $(TRANSIENT) > $@
$(CAT) $@
$(BUILDDIR)/loader-$(_PLATFORM_).prg: $(INTERMDIR)/loader-nonreloc-$(_PLATFORM_).o $(INTERMDIR)/install-nonreloc-$(_PLATFORM_).o $(INTERMDIR)/binary.link
$(LD) -C $(INTERMDIR)/binary.link -vm -m $(INTERMDIR)/loader-$(_PLATFORM_).map $(INTERMDIR)/loader-nonreloc-$(_PLATFORM_).o $(INTERMDIR)/install-nonreloc-$(_PLATFORM_).o
$(RM) a.out
$(BUILDDIR)/install-$(_PLATFORM_).prg: $(BUILDDIR)/loader-$(_PLATFORM_).prg
$(INTERMDIR)/loader-$(_PLATFORM_).map: $(BUILDDIR)/install-$(_PLATFORM_).prg
$(INTERMDIR)/customdrivecode-nonreloc-$(_PLATFORM_).o: customdrivecode.s
$(AS) $(AS_FLAGS) -D TRNSADDR=0x$(TRANSIENT) -o $@ $^
$(BUILDDIR)/customdrivecode-$(_PLATFORM_).prg: $(INTERMDIR)/binary.link $(INTERMDIR)/customdrivecode-nonreloc-$(_PLATFORM_).o $(INTERMDIR)/loader-nonreloc-$(_PLATFORM_).o $(INTERMDIR)/install-nonreloc-$(_PLATFORM_).o
$(LD) -vm -m $(INTERMDIR)/loader-$(_PLATFORM_).map -C $^
$(MV) $(BUILDDIR)/transient-$(_PLATFORM_).prg $@
$(PERL) make-loadersymbolsinc.pl $(VERSION) $(_PROJECT_) $(INTERMDIR)/loader-$(_PLATFORM_).map > $(BUILDDIR)/loadersymbols-$(_PLATFORM_).inc
$(INTERMDIR)/save-nonreloc-$(_PLATFORM_).o: save.s
$(AS) $(AS_FLAGS) -D TRNSADDR=0x$(TRANSIENT) -o $@ $^
$(BUILDDIR)/save-$(_PLATFORM_).prg: $(INTERMDIR)/binary.link $(INTERMDIR)/save-nonreloc-$(_PLATFORM_).o $(INTERMDIR)/customdrivecode-$(_PLATFORM_).o $(INTERMDIR)/loader-nonreloc-$(_PLATFORM_).o $(INTERMDIR)/install-nonreloc-$(_PLATFORM_).o
$(LD) -vm -m $(INTERMDIR)/loader-$(_PLATFORM_).map -C $^
$(MV) $(BUILDDIR)/transient-$(_PLATFORM_).prg $@
$(PERL) make-loadersymbolsinc.pl $(VERSION) $(_PROJECT_) $(INTERMDIR)/loader-$(_PLATFORM_).map > $(BUILDDIR)/loadersymbols-$(_PLATFORM_).inc
ifeq ($(PROJECT),)
_PROJECT_ = loader
else
_PROJECT_ = $(PROJECT)
endif
$(BUILDDIR)/loadersymbols-$(_PLATFORM_).inc: $(INTERMDIR)/loader-$(_PLATFORM_).map
$(PERL) make-loadersymbolsinc.pl $(VERSION) $(_PROJECT_) $^ > $@
$(BUILDDIR)/loader-$(_PLATFORM_).zip: $(BINDEPS)
$(CD) $(BUILDDIR) \
&& $(CP) $(BUILDDIR)/loader-$(_PLATFORM_).prg $(INTERMDIR)/loader.prg && $(ZIP) -j $@ $(INTERMDIR)/loader.prg \
&& $(CP) $(BUILDDIR)/install-$(_PLATFORM_).prg $(INTERMDIR)/install.prg && $(ZIP) -j $@ $(INTERMDIR)/install.prg \
&& $(CP) $(BUILDDIR)/loadersymbols-$(_PLATFORM_).inc $(INTERMDIR)/loadersymbols.inc && $(ZIP) -j $@ $(INTERMDIR)/loadersymbols.inc
# loader-$(_PLATFORM_).lib library target
$(BUILDDIR)/loader-$(_PLATFORM_).lib: $(INTERMDIR)/resident-$(_PLATFORM_).o $(INTERMDIR)/install-$(_PLATFORM_).o $(INTERMDIR)/customdrivecode-$(_PLATFORM_).o $(INTERMDIR)/save-$(_PLATFORM_).o
$(AR) a $@ $^
clean:
-$(RM) \
$(INTERMDIR)/* \
$(BUILDDIR)/loader-c64.lib \
$(BUILDDIR)/loader-c64.prg $(BUILDDIR)/install-c64.prg $(BUILDDIR)/loadersymbols-c64.inc \
$(BUILDDIR)/loader-c128.lib \
$(BUILDDIR)/loader-c128.prg $(BUILDDIR)/install-c128.prg $(BUILDDIR)/loadersymbols-c128.inc \
$(BUILDDIR)/loader-c16.lib \
$(BUILDDIR)/loader-c16.prg $(BUILDDIR)/install-c16.prg $(BUILDDIR)/loadersymbols-c16.inc
-$(RMDIR) $(INTERMDIR)

View file

@ -0,0 +1,660 @@
__NO_CUSTOMDRIVECODE_SYMBOLS_IMPORT = 1
.include "loader.inc"
.include "drives/drivecode-common.inc"
.include "hal/hal.inc"
.include "cpu.inc"
.scope drive
.include "via.inc"
.endscope
.include "cia.inc"
.segment "DISKIO_PLUGIN_ZP" : zeropage
CUSTOMPARAM: .res 2
DRVCODEOFS: .res 1
DRVCODEPTR: .res 2
BUFFERPTR: .res 2
.if ONLY_1541_AND_COMPATIBLE = 0
DRIVETYPE: .res 1
.endif; ONLY_1541_AND_COMPATIBLE
UPLOADENTR: .res 2
UPLOADTO: .res 2
UPLOADCNT: .res 2
UPLOADPTR: .res 2
plugin_zp_last = * - 1
.export plugin_zp_last
.segment "DISKIO_PLUGIN"
.ifdef TRNSADDR
.org TRNSADDR - 2
.word * + 2; load address
.endif
.export swapdrvcod
swapdrvcod: stx CUSTOMPARAM
sty CUSTOMPARAM + 1
stx DRVCODEPTR
sty DRVCODEPTR + 1
.if ONLY_1541_AND_COMPATIBLE = 0
lda #$80
sta DRIVETYPE
BRANCH_IF_INSTALLED :+
lda #diskio::status::DEVICE_INCOMPATIBLE
sec
rts
:
.endif; !ONLY_1541_AND_COMPATIBLE
ldy #swapparams::buffer
lda (CUSTOMPARAM),y
sta BUFFERPTR
sta bufmain41
.if ONLY_1541_AND_COMPATIBLE = 0
sta bufmain71
sta bufmain81
.endif; !ONLY_1541_AND_COMPATIBLE
iny
lda (CUSTOMPARAM),y
sta BUFFERPTR + 1
sta bufmain41 + 1
.if ONLY_1541_AND_COMPATIBLE = 0
sta bufmain71 + 1
sta bufmain81 + 1
.endif; !ONLY_1541_AND_COMPATIBLE
CLEAR
: SET_FLAGS_N_DATA_V_CLK
bvc :-
ldy #$ff
: iny
lda trampoline,y
jsr sendbyte
bne :-
ldy #swapparams::drivecode41
jsr prepmove
ldx #drivecode::from - 1
: lda UPLOADENTR,x
sta trampoln41 + payldprm41 - trmplrun41,x
dex
bpl :-
.if ONLY_1541_AND_COMPATIBLE = 0
lda #diskio::drivetype::DRIVES_1541
SET_FLAGS_N_DATA_V_CLK
bvc is1581
bmi setdrvtype
IDLE
ldy #swapparams::drivecode71
jsr prepmove
ldx #drivecode::from - 1
: lda UPLOADENTR,x
sta trampoln71 + payldprm71 - trmplrun71,x
dex
bpl :-
lda #diskio::drivetype::DRIVES_157X
bne setdrvtype; jmp
is1581: IDLE
ldy #swapparams::drivecode81
jsr prepmove
ldx #drivecode::from - 1
: lda UPLOADENTR,x
sta custom81 + payldprm81 - trampoln81,x
dex
bpl :-
lda #diskio::drivetype::DRIVES_1581_CMD
setdrvtype: sta DRIVETYPE
.endif; !ONLY_1541_AND_COMPATIBLE
jsr selectdcod
pha
ldy #drivecode::entry
jsr prepmove
pla
.if ONLY_1541_AND_COMPATIBLE = 0
cmp #swapparams::drivecode81
bne doupload
ldy #drivecode::from
: lda UPLOADENTR - 1,y
jsr sendbyte
dey
bne :-
.endif; !ONLY_1541_AND_COMPATIBLE
doupload: jsr uploadcode
ldy #ldrmain41 - drvcode41 - drivecode::length
jsr prepmove
; buffer loader drive code
CLEAR
: SET_FLAGS_N_DATA_V_CLK
bvc :-
ldy #0
getdrvcode: jsr receivbyte
sta (BUFFERPTR),y
iny
bne :+
inc BUFFERPTR + 1
: inc UPLOADCNT
bne getdrvcode
inc UPLOADCNT + 1
bmi getdrvcode
: SET_FLAGS_N_DATA_V_CLK
bpl :-
; upload custom drive code
lda CUSTOMPARAM
sta DRVCODEPTR
lda CUSTOMPARAM + 1
sta DRVCODEPTR + 1
ldy DRVCODEOFS
jsr uplodpayld
CLEAR
OK_CLC
rts
.export restoreldr
restoreldr:
.if ONLY_1541_AND_COMPATIBLE = 0
lda DRIVETYPE
bpl :+
lda #diskio::status::DEVICE_INCOMPATIBLE
sec
rts
:
.endif; !ONLY_1541_AND_COMPATIBLE
INIT_CLEAR_ATN_OUT_CLEAR_CLK_OUT_CLEAR_DATA_OUT
CLEAR
waitrestor: SET_FLAGS_N_DATA_V_CLK
bpl waitrestor
bvc waitrestor
.if ONLY_1541_AND_COMPATIBLE = 0
lda DRIVETYPE
.endif; !ONLY_1541_AND_COMPATIBLE
jsr selectdcod
ldy #ldrmain41 - drvcode41 - drivecode::length
jsr uplodpayld
INSTALL_IDLE
rts
.export sendbyte
sendbyte: pha
nop; delay
SENDBYTE
pla
rts
.export receivbyte
receivbyte: RECEIVEBYTE
rts
selectdcod:
.if ONLY_1541_AND_COMPATIBLE = 0
cmp #diskio::drivetype::DRIVES_157X
bne check1581
lda #swapparams::drivecode71
ldx #.lobyte(drvcode71)
ldy #.hibyte(drvcode71)
bne dcodselctd; jmp
check1581: cmp #diskio::drivetype::DRIVES_1581_CMD
bne upload1541
lda #swapparams::drivecode81
ldx #.lobyte(drvcode81)
ldy #.hibyte(drvcode81)
bne dcodselctd; jmp
upload1541:
.endif; ONLY_1541_AND_COMPATIBLE
lda #swapparams::drivecode41
ldx #.lobyte(drvcode41)
ldy #.hibyte(drvcode41)
dcodselctd: sta DRVCODEOFS
stx DRVCODEPTR
sty DRVCODEPTR + 1
rts
uplodpayld: jsr prepmove
uploadcode: ldy #0
: lda (UPLOADPTR),y
jsr sendbyte
iny
bne :+
inc UPLOADPTR + 1
: inc UPLOADCNT
bne :--
inc UPLOADCNT + 1
bmi :--
rts
prepmove: ldx #0
: lda (DRVCODEPTR),y
iny
sta UPLOADENTR,x
inx
cpx #8
bne :-
;sec
lda #0
sbc UPLOADCNT
sta UPLOADCNT
lda #0
sbc UPLOADCNT + 1
sta UPLOADCNT + 1
rts
trampoline:
.scope upload41
.include "via.inc"
.org $ef; FILENAME41
recvtrmpln: DRIVEGETBYTE 1541, trampolinecmp, nozeroes
sta $0105,x; BLOCKBUFFER41 + 5
inx
cpx #custm41end - trampoln41 + 1
bne recvtrmpln
.byte $ff
.word (entry - 1)
entry: lda getbytecmp41 + 1
sta .lobyte(trampolinecmp + 1)
ldx #1
ldy VIA1_PRB + 16
bcs recvtrmpln; jmp
.reloc
;.byte 0; end marker is 0 at drvcode41
.endscope
drvcode41: .word 0; entry, not used here
.word trmplrun41; to
.word custm41end - trampoln41; length
.word trampoln41; from
ldrmain41: .word $0700 + RETURNSIZE41; buffer length
bufmain41: .word 0; buffer
trampoln41:
.org $0106; = BLOCKBUFFER41 + 6
RESTORELOOP41 = $d9; with SENDVAL41
RESTOREMAIN41 = RESTORELOOP41 + 1
trmplrun41: .scope custom41
.include "drives/drivecode-common.inc"
.include "via.inc"
lda #ATNA_OUT | CLK_OUT | DATA_OUT
sta VIA1_PRB
; buffer end of read loop to make space for the receive routine
ldx #21 - 1
: lda RESTORELOOP41,x
sta SECTORLINKTABLE41,x
dex
bpl :-
lda getbytecmp41 + 1
sta customloop + (getbytecmp41 + 1) - getbyte41
; put receive routine for buffered loader code to zeropage
ldx #$0100 - (custmstore - customloop - 3)
: lda custmstore - $0100,x
sta .lobyte(restorloop - restrloope),x
inx
bne :-
ldx #$0100 - (restrloope - restorloop)
: lda restrloope - $0100,x
sta $00,x
inx
bne :-
lda #ATNA_OUT
sta VIA1_PRB
lda #CLK_IN | DATA_IN
: bit VIA1_PRB
bne :-
SENDVAL41 = RESTORELOOP41
sendzp: lda $00,x
DRIVESENDBYTE 1541, SENDVAL41
inx
bne sendzp
ldx #.lobyte(returnrun)
sendmain: lda returnrun & $ff00,x
DRIVESENDBYTE 1541, SENDVAL41
inx
bne sendmain
inc sendmain + 2
dec numpages41
bne sendmain
lda #ATNA_OUT | CLK_OUT | DATA_OUT
sta (V1B41,x); VIA1_PRB
lda #OPC_RTS
sta getbyterts41
ldx #RESTOREMAIN41
: lda .lobyte($0100 - RESTOREMAIN41),x
ldy payldpre41 - $0100,x
sty .lobyte($0100 - RESTOREMAIN41),x
sta $00,x; buffer low end of zeropage at high end of zeropage
inx
bne :-
lda #ATNA_OUT
sta ($03,x); VIA1_PRB
ldy VIA1_PRB
.byte OPC_JMP_ABS, 0; jmp $0000
numpages41: .byte 7
CUSTOMPARAM41 = $fa
restorloop: sta (.lobyte(CUSTOMPARAM41 + 2),x); x = 0
inc .lobyte(CUSTOMPARAM41 + 2)
bne :+
inc .lobyte(CUSTOMPARAM41 + 3)
: inc .lobyte(CUSTOMPARAM41 + 4)
bne restorloop + 3 - (custmstore - customloop)
inc .lobyte(CUSTOMPARAM41 + 5)
bne restorloop + 3 - (custmstore - customloop)
.byte OPC_JMP_ABS
.word $0200 - RETURNSIZE41; entry
.word $0200 - RETURNSIZE41; to
.word $10000 - ($0600 + RETURNSIZE41); length
restrloope:
UPLOADOFFS = custmstore - (customloop + 3)
; executed at $0000
customloop: DRIVEGETBYTE 1541, getbyte
custmstore: sta (.lobyte(UPLOADOFFS + 19),x); x = 0
inc .lobyte(UPLOADOFFS + 19)
bne :+
inc .lobyte(UPLOADOFFS + 20)
: inc .lobyte(UPLOADOFFS + 21)
bne customloop + 3
inc .lobyte(UPLOADOFFS + 22)
bne customloop + 3
.byte OPC_JMP_ABS
.endscope
payldprm41: .word 0; entry
.word 0; to
.word 0; -length
payldpre41:
return:
.org $01e0
returnrun: ldx #<topofstack41
lda #$18 ; ATNA_OUT | CLK_OUT
sta (<(V1B41 - topofstack41),x); $1800
txs
ldx #21 - 1
: lda SECTORLINKTABLE41,x
sta RESTORELOOP41,x
dex
bpl :-
lda (<(V2B41 - $ff),x); $1c00
and #$08 ; BUSY_LED
beq :+
lda #$ff
: sta LEDSTATE41
jsr initlink41
jmp idleloop41
.assert * = $0200, error, "***** 1541 return code at wrong position. *****"
.org * - (returnrun - return)
returnend:
.assert * = $0200, error, "***** 1541 custom swap code at wrong position. *****"
RETURNSIZE41 = returnend - return
.reloc
custm41end:
.if ONLY_1541_AND_COMPATIBLE = 0
drvcode71: .word 0; entry, not used here
.word trmplrun71; to
.word custom71end - trampoln71; length
.word trampoln71; from
.word $0700 + RETURNSIZE71; buffer size
bufmain71: .word 0; buffer
trampoln71:
.org $0700; = BLOCKBUFFER71
RETURNSIZE71 = 34
trmplrun71: .scope custom71
.include "via.inc"
lda #$18 ; ATNA_OUT | CLK_OUT
sta $1800; VIA1_PRB
ldx #$ff
lda $1c00; VIA2_PRB
and #$08 ; BUSY_LED
beq :+
txa
: sta LEDSTATE71
stx <CLEARSECTORLINKTABLE71
ldx #<topofstack71
txs
jsr initlink71
: lda $1800; VIA1_PRB
lsr
bcc :-
jmp idleloop71
sei
lda #ATNA_OUT | CLK_OUT | DATA_OUT
sta VIA1_PRB
; move receive routine for buffered loader code
ldx #0
: lda restorloop,x
sta FILENAME71,x
inx
cpx #restrloope - restorloop
bne :-
lda #ATNA_OUT
sta VIA1_PRB
lda #$05; CLK_IN | DATA_IN
: and VIA1_PRB
bne :-
SENDVAL71 = SECTORLINKTABLE71
; send loader code at $0000-$0700 plus return-to-loader code
ldx #256 - RETURNSIZE71
sendcode71: lda $ff00 + RETURNSIZE71,x
DRIVESENDBYTE 1571, SENDVAL71
inx
bne :+
inc sendcode71 + 2
: inc count
bne sendcode71
inc count + 1
bmi sendcode71
lda #ATNA_OUT | CLK_OUT | DATA_OUT
sta VIA1_PRB
CUSTOMZPBUFFSIZE71 = payldpre71 - (customloop + 3)
.export CUSTOMZPBUFFSIZE71
ldx #CUSTOMZPBUFFSIZE71
: lda customloop + 3 - 1,x
ldy .lobyte($00 - 1),x
sty .lobyte(CUSTOMZPBUFFER71 - 1),x
sta .lobyte($00 - 1),x
dex
bne :-
lda #ATNA_OUT
sta VIA1_PRB
ldy VIA1_PRB
.byte OPC_JMP_ABS
count: .word $10000 - ($0700 + RETURNSIZE71)
CUSTOMPARAM71 = $5a
restorloop: DRIVEGETBYTE 1571, getbyterestore
sta (.lobyte(CUSTOMPARAM71 + 2),x); x = 0
inc .lobyte(CUSTOMPARAM71 + 2)
bne :+
inc .lobyte(CUSTOMPARAM71 + 3)
: inc .lobyte(CUSTOMPARAM71 + 4)
bne restorloop + 3
inc .lobyte(CUSTOMPARAM71 + 5)
bne restorloop + 3
.byte OPC_JMP_ABS
.word $0700; entry
.word $0100; to
.word $10000 - ($0600 + RETURNSIZE71); length
restrloope:
UPLOADOFFS = getbytrt71 - (customloop + 3)
; executed at $0000
customloop: DRIVEGETBYTE 1571, getbytecmp
getbytrt71: sta (.lobyte(UPLOADOFFS + 19),x); x = 0
inc .lobyte(UPLOADOFFS + 19)
bne :+
inc .lobyte(UPLOADOFFS + 20)
: inc .lobyte(UPLOADOFFS + 21)
bne customloop + 3
inc .lobyte(UPLOADOFFS + 22)
bne customloop + 3
.byte OPC_JMP_ABS
.endscope
payldprm71: .word 0; entry
.word 0; to
.word 0; -length
payldpre71:
.assert (* - trmplrun71) <= CUSTOMUPLOADSIZE71, error, "***** 1571 custom upload code too large. *****"
.assert (* - trmplrun71) >= CUSTOMUPLOADSIZE71, error, "***** 1571 custom upload code too small. *****"
.reloc
custom71end:
drvcode81: .word trampoln81; entry
.word trampoln81; to
.word custom81end - custom81; length
.word custom81; from
.word DRVCODEND81 - $0300; buffer size
bufmain81: .word 0; buffer
custom81:
.org $0b00; = BLOCKBUFFER81
trampoln81: lda #payldpre81 - payldprm81 - 1; = $05 = CLK_IN | DATA_IN
tax
: ldy payldprm81,x
sty CUSTOMPARAM81,x
dex
bpl :-
;lda #CLK_IN | DATA_IN
: bit CIA_PRB
bne :-
SENDVAL81 = CUSTOMPARAM81 + 6
; send loader code at $0300
ldx #0
sendcode81: lda a:$0300 & $ff00,x
DRIVESENDBYTE 1581, SENDVAL81
inx
bne :+
inc sendcode81 + 2
: cpx #.lobyte(DRVCODEND81)
bne sendcode81
lda sendcode81 + 2
cmp #.hibyte(DRVCODEND81)
bne sendcode81
ldx #0
stx CIA_PRB
jmp CUSTOMRECEIVE81
payldprm81: .word 0; entry
.word 0; to
.word 0; -length
payldpre81:
.reloc
custom81end:
.endif; !ONLY_1541_AND_COMPATIBLE

193
loader/src/decompress/b2decomp.s Executable file
View file

@ -0,0 +1,193 @@
; ByteBoozer Decruncher /HCL May.2003
; B2 Decruncher December 2014
; with slight modifications by Krill
decompress = Decrunch
decompsrc = Get1+1
.FEATURE labels_without_colons, leading_dot_in_identifiers
;Variables.. #Bytes
zp_base = DECOMPVARS ; -
bits = zp_base ;1
put = decdestlo
.macro .GetNextBit
.local DgEnd
asl bits
bne DgEnd
jsr GetNewBits
DgEnd
.endmacro
.macro .GetLen
.local GlEnd
.local GlLoop
lda #1
GlLoop
.GetNextBit
bcc GlEnd
.GetNextBit
rol
bpl GlLoop
GlEnd
.endmacro
Decrunch
jsr Gnb
lda loadaddrhi
sta Get2+2
sta Get3+2
ldx loadaddrlo
jsr GetNewBits
tya
.if LOADCOMPD_TO
clc
adc loadaddroffslo
php
.endif
storedadrl:
sta put
jsr GetNewBits
tya
.if LOADCOMPD_TO
plp
adc loadaddroffshi
.endif
storedadrh:
sta put + 1
lda #$80
sta bits
DLoop
POLLBLOCK
.GetNextBit
bcs Match
Literal
; Literal run.. get length.
.GetLen
sta LLen+1
ldy #0
LLoop
Get3 lda $ff00,x
inx
bne *+5
jsr GnbInc
sta (put),y
iny
LLen cpy #0
bne LLoop
clc
tya
adc put
sta put
bcc *+4
inc put+1
iny
beq DLoop
; Has to continue with a match..
Match
; Match.. get length.
.GetLen
sta MLen+1
; Length 255 -> EOF
cmp #$ff
beq End
; Get num bits
cmp #2
lda #0
rol
.GetNextBit
rol
.GetNextBit
rol
tay
lda Tab,y
beq MByte
; Get bits < 8
MLoop1 .GetNextBit
rol
bcs MLoop1
bmi MShort
MByte
; Get byte
eor #$ff
tay
Get2 lda $ff00,x
inx
bne MLong
jsr GnbInc
jmp MLong
MShort
ldy #$ff
MLong
;clc
adc put
sta MLda+1
tya
adc put+1
sta MLda+2
ldy #$ff
MLoop2 iny
MLda lda $b00b,y
sta (put),y
MLen cpy #0
bne MLoop2
;sec
tya
adc put
sta put
bcc *+4
inc put+1
jmp DLoop
GetNewBits
Get1 ldy $ff00,x
sty bits
rol bits
inx
beq GnbInc
End rts
GnbInc
inc Get1+2
inc Get2+2
inc Get3+2
Gnb
php
pha
tya
pha
GETBLOCK Get1+2
pla
tay
pla
ldx #0
plp
rts
Tab
; Short offsets
.byte %11011111 ; 3
.byte %11111011 ; 6
.byte %00000000 ; 8
.byte %10000000 ; 10
; Long offsets
.byte %11101111 ; 4
.byte %11111101 ; 7
.byte %10000000 ; 10
.byte %11110000 ; 13

View file

@ -0,0 +1,266 @@
decompsrc = bitfire_lz_sector_ptr1
.if MEM_DECOMP_TO_API
; cannot copy remaining uncompressed blob of unknown size
.error "***** MEM_DECOMP_TO_API is not supported for BITNAX. Copy compressed data to original location, then use MEM_DECOMP_API to decompress in-place. *****"
.endif
BITFIRE_DECOMP_ZERO_OVERLAP = 1
.FEATURE labels_without_colons, leading_dot_in_identifiers
.lz_bits = DECOMPVARS + 0
.lz_dst = DECOMPVARS + 1
.lz_end = DECOMPVARS + 3
.lz_tmp = DECOMPVARS + 5
decompress:
jsr .lz_next_page_
lda loadaddrhi
sta bitfire_lz_sector_ptr2 + 1
ldx loadaddrlo
.if BITFIRE_DECOMP_ZERO_OVERLAP
ldy #$03; destination and end addresses
.else
ldy #$01; destination address
.endif
: lda (loadaddrlo),y
sta .lz_dst,y
inx
bne :+
jsr .lz_next_page
: dey
bpl :--
.if LOADCOMPD_TO
clc
lda loadaddroffslo
adc .lz_dst
sta .lz_dst
lda loadaddroffshi
adc .lz_dst+1
sta .lz_dst+1
.if BITFIRE_DECOMP_ZERO_OVERLAP
clc
lda loadaddroffslo
adc .lz_end
sta .lz_end
lda loadaddroffshi
adc .lz_end+1
sta .lz_end+1
.endif
.endif
sec
.lz_type_refill
jsr .lz_refill_bits ;refill bit buffer .lz_bits
;******** Start the next match/literal run ********
.lz_type_check
bcc .lz_do_match
beq .lz_type_refill ;we will fall through on entry
;******** Process literal run ********
lda #$00
:
rol ;-> a = $01 after first round
asl .lz_bits
bne *+5
jsr .lz_refill_bits ;kills y
bcc .lz_lrun_gotten
asl .lz_bits
bne :-
jsr .lz_refill_bits
bne :-
.lz_lrun_gotten
sta .lz_lcopy_len ;Store LSB of run-length
ldy #$00
.lz_lcopy
bitfire_lz_sector_ptr2 = * + 1 ;Copy the literal data, forward or overlap is getting a pain in the ass.
lda $ff00,x
sta (.lz_dst),y
inx
bne :+
clc
jsr .lz_next_page
:
iny
.lz_lcopy_len = * + 1
cpy #$00
bne .lz_lcopy
tya
;.if LOAD_VIA_KERNAL_FALLBACK | (LOAD_UNDER_D000_DFFF & (PLATFORM <> diskio::platform::COMMODORE_16))
bne *+5
jmp .lz_maximum ;maximum literal run, bump sector pointers and so on and force new type bit
;.else
; beq .lz_maximum ;maximum literal run, bump sector pointers and so on and force new type bit
;.endif
;XXX TODO can we reuse the same code? In one case continue with match, in other case redecide
clc
adc .lz_dst
sta .lz_dst
bcc *+4
inc .lz_dst+1
POLLBLOCK
;no need for a type bit, after each literal a match follows, except for maximum runlength literals
;******** Process match ********
.lz_do_match
lda #$01 ;this could be made shorter by using the last bitfetch of the upcoming loop and restoring the carry again by a cmp #$02. Saves bytes, but makes things slower, as eof check is also done with all short matches then
asl .lz_bits ;first length bit (where a one identifies
bne *+5 ;a two-byte match)
jsr .lz_refill_bits
bcc .lz_get_offs ;all done, length is 2, skip further bitfetches (and eof check)
:
asl .lz_bits
bne *+5
jsr .lz_refill_bits
rol
asl .lz_bits
bne *+5
jsr .lz_refill_bits
bcc :-
.lz_got_len
tay ;XXX TODO could this be placed elsewhere to make the tay obsolete?
beq .lz_end_of_file ;A 257-byte (=>$00) run serves as a sentinel, but not with zero-overlap, except when depacking from a non inplace address, then it is still appended
.lz_get_offs
sta .lz_mcopy_len ;store length at final destination
lda #%11000000 ;fetch 2 more prefix bits
rol ;previous bit is still in carry \o/
:
asl .lz_bits
bne *+5
jsr .lz_refill_bits
rol
bcs :-
beq .lz_8_and_more ;0 + 8 bits to fetch, branch out before table lookup to save a few cycles and one byte in the table, also save complexity on the bitfetcher
tay
lda .lz_lentab,y
: ;same as above
asl .lz_bits ;XXX same code as above, so annoying :-(
bne *+5
jsr .lz_refill_bits
rol
bcs :-
bmi .lz_less_than_8 ;either 3,4,6 or 7 bits fetched -> highbyte will be $ff
.lz_8_and_more
jsr .lz_refill_bits
eor #$ff ;5 of 13, 2 of 10, 0 of 8 bits fetched as highbyte, lowbyte still to be fetched
sta .lz_tmp ;XXX this is a pain in the arse that A and Y need to be swapped :-(
tya
ldy .lz_tmp
SKIPWORD
.lz_less_than_8
ldy #$ff ;XXX TODO silly, y is set twice in short case
adc .lz_dst ;subtract offset from lz_dst
sta .lz_m+1
tya ;hibyte
adc .lz_dst+1
sta .lz_m+2
ldy #$ff ;The copy loop. This needs to be run
;forwards since RLE-style matches can overlap the destination
.lz_mcopy
iny
.lz_m lda $face,y ;copy one byte
sta (.lz_dst),y
.lz_mcopy_len = * + 1
cpy #$ff
bne .lz_mcopy
tya ;advance destination pointer
; sec ;XXX TODO carry set = type check needed, cleared (literal) = match follows anyway
adc .lz_dst
sta .lz_dst
.if BITFIRE_DECOMP_ZERO_OVERLAP = 0
.lz_skip_poll bcc :+
.lz_maximum inc .lz_dst+1 ;this is also used by maximum length
bcs .lz_skip_end
:
.else
bcc :+ ;proceed to check
.lz_maximum
inc .lz_dst+1 ;advance hi byte
; lda .lz_dst ;if entering via .lz_maximum, a = 0, so we would pass the following check only if the endadress is @ $xx00
: ;if so, the endaddress can't be $xx00 and the highbyte check will fail, as we just successfully wrote a literal with type bit, so the end address must be greater then the current lz_dst, as either another literal or match must follow. Can you still follow me?! :-D
eor .lz_end ;check end address
.lz_skip_poll beq .lz_check_end ;all okay, poll for a new block
.endif ; BITFIRE_DECOMP_ZERO_OVERLAP
POLLBLOCK
.lz_skip_end
;literals needing an explicit type bit
asl .lz_bits ;fetch next type bit
jmp .lz_type_check
.if BITFIRE_DECOMP_ZERO_OVERLAP
.lz_check_end
lda .lz_dst+1 ;check highbyte
eor .lz_end+1
bne .lz_skip_end ;skip poll, so that only one branch needs to be manipulated
;sta .barrier ;clear barrier and force to load until EOF, XXX does not work, but will at least force one additional block before leaving as barrier will be set again upon next block being fetched. Will overlap be > than 2 blocks? most likely not? CRAP, tony taught me that there is /o\
;lda #$ff
;sta bitfire_load_addr_hi ;needed if the barrier method will not work out, plain jump to poll loop will fail on stand alone depack?
;jmp .lz_next_page ;load any remaining literal blob if there, or exit with rts in case of plain decomp (rts there instead of php). So we are forced until either the sector_ptr reaches $00xx or EOF happens, so nothing can go wrong
; fetching any remaining final literals uncompressed blob is performed by the caller (loadcompd in resident.s)
.endif ; BITFIRE_DECOMP_ZERO_OVERLAP
.lz_end_of_file
rts
.lz_refill_bits
bitfire_lz_sector_ptr1 = * + 1
bitfire_load_addr_hi = * + 2
ldy $ff00,x
inx
bne .lz_same_page
.lz_next_page
inc bitfire_load_addr_hi
inc bitfire_lz_sector_ptr2 + 1
.lz_next_page_
php
pha
sty .lz_tmp
GETBLOCK bitfire_load_addr_hi
ldx #0
ldy .lz_tmp
pla
plp
.lz_same_page
;store bits? happens on all calls, except when a whole literal is fetched
bcc :+ ;only store lz_bits if carry is set (in all cases, except when literal is fetched for offset)
sty .lz_bits
rol .lz_bits
: rts
.lz_lentab = * - 1
;short offset init values
;.byte %00000000 ;2
.byte %11011111 ;0
.byte %11111011 ;1
.byte %10000000 ;3
;long offset init values
.byte %11101111 ;offset 0
.byte %11111101 ;offset 1
.byte %10000000 ;offset 2
.byte %11110000 ;offset 3

View file

@ -0,0 +1,305 @@
decompsrc = lz_sector_ptr1
;-------------------------------------------------------------------------------
;Regular version of the Lempel-Ziv decompressor
;-------------------------------------------------------------------------------
lz_dst = decdestlo ;Decompression destination pointer.
;Initialize this to whatever address
;you want to decompress to
lz_bits = DECOMPVARS + $00 ;Shift register. Initialized to $80
;for a new file
lz_scratch = DECOMPVARS + $01 ;Temporary zeropage storage
lz_ybuffer = DECOMPVARS + $02 ;Temporary register storage when fetching sector
lz_sector = $ff00 ;The one-page buffer from which the
;compressed data is actually read,
;and which gets refilled by
;lz_fetch_sector.
;-------------------------------------------------------------------------------
;This is the user's hook to replenish the sector buffer with some new bytes.
;
;A and Y are expected to be preserved while carry must remain set on exit.
;X should point to the first byte of the new data, e.g. zero for a full 256-byte
;page of data or two to skip past the sector and track links.
;
;When fetching from a larger in-memory array rather than a single sector buffer
;the lz_sector_ptr1..3 pointers will need to be patched up
;-------------------------------------------------------------------------------
lz_fetch_sector:
inc lz_sector_ptr1 + 1
inc lz_sector_ptr2 + 1
inc lz_sector_ptr3 + 1
lz_fetch_sector_:
pha
sty lz_ybuffer
GETBLOCK lz_sector_ptr1 + 1
ldx #0
ldy lz_ybuffer
pla
sec
rts
decompress:
jsr lz_fetch_sector_
lda loadaddrhi
sta lz_sector_ptr2 + 1
sta lz_sector_ptr3 + 1
ldx loadaddrlo
jsr _lz_refill_bits
tya
storedadrl:
sta lz_dst + $00
jsr _lz_refill_bits
tya
storedadrh:
sta lz_dst + $01
.if LOADCOMPD_TO
clc
lda loadaddroffslo
adc lz_dst + $00
sta lz_dst + $00
lda loadaddroffshi
adc lz_dst + $01
sta lz_dst + $01
.endif
;-------------------------------------------------------------------------------
;This is the main lz_decrunch function which may be called to decompress an
;entire file.
;
;On entry and exit the X register points to the next available byte in the
;sector buffer, in ascending order from $00 to $ff.
;This implies that the initial sector must have already been fetched, and that a
;file ending with X wrapped to $00 will have needlessly fetched an extra sector
;(which may be taken advantage of when decoding a contiguous set of files.)
;-------------------------------------------------------------------------------
;******** Start the next match/literal run ********
lz_decrunch: sec ;This is the main entry point. Forcibly
_lz_type_refill:
jsr _lz_refill_bits ;fill up the the bit buffer on entry
bne _lz_type_cont ;(BRA)
;Wrap the high-byte of the destination pointer.
_lz_mfinish: bcc *+4
_lz_maximum: inc lz_dst+1 ;This is also used by maximum length
;literals needing an explicit type bit
POLLBLOCK
;Literal or match to follow?
asl lz_bits
_lz_type_cont: bcc _lz_do_match
beq lz_decrunch
;******** Process literal run ********
lda #%00000000 ;Decode run length
_lz_lrun_loop: rol
asl lz_bits
bcs _lz_lrun_test
_lz_lrun_back: asl lz_bits
bne _lz_lrun_loop
jsr _lz_refill_bits
bne _lz_lrun_loop ;(BRA)
_lz_lrun_test: bne _lz_lrun_gotten
jsr _lz_refill_bits
bcc _lz_lrun_back
_lz_lrun_gotten:
sta _lz_copy_cnt+1 ;Store LSB of run-length
ldy #$00
_lz_lcopy:
lz_sector_ptr2 = *+1 ;Copy the literal data. Note the
lda lz_sector,x
inx
bne *+5
jsr lz_fetch_sector ;Grab a new sector for the literal loop
sta (lz_dst),y
iny
_lz_copy_cnt: cpy #$00
bne _lz_lcopy
;Time to advance the destination pointer.
;Maximum run length literals exit here as a type-bit needs
;to be fetched afterwards
tya
beq _lz_maximum
clc
adc lz_dst+0
sta lz_dst+0
bcc *+4
inc lz_dst+1
POLLBLOCK
;One literal run following another only makes sense if the
;first run is of maximum length and had to be split. As that
;case has been taken care of we can safely omit the type bit
;here
;******** Process match ********
_lz_do_match: lda #%00100000 ;Determine offset length by a two-bit
_lz_moff_range: asl lz_bits ;prefix combined with the first run
bne *+5 ;length bit (where a one identifies
jsr _lz_refill_bits ;a two-byte match).
rol ;The rest of the length bits will
bcc _lz_moff_range ;then follow *after* the offset data
tay
lda _lz_moff_length,y
beq _lz_moff_far
_lz_moff_loop: asl lz_bits ;Load partial offset byte
bne *+9
sty lz_scratch
jsr _lz_refill_bits
ldy lz_scratch
rol
bcc _lz_moff_loop
bmi _lz_moff_near
_lz_moff_far: sta lz_scratch ;Save the bits we just read as the
;high-byte
lz_sector_ptr3 = *+1
lda lz_sector,x ;For large offsets we can load the
inx ;low-byte straight from the stream
bne *+5 ;without going throught the shift
jsr lz_fetch_sector ;register
; sec
adc _lz_moff_adjust_lo,y
bcs _lz_moff_pageok
dec lz_scratch
sec
_lz_moff_pageok:
adc lz_dst+0
sta _lz_match+0
lda lz_scratch
adc _lz_moff_adjust_hi,y
sec
bcs _lz_moff_join ;(BRA)
_lz_moff_near:
; sec ;Special case handling of <8 bit offsets.
adc _lz_moff_adjust_lo,y;We may can safely ignore the MSB from
; sec ;the base adjustment table as the
adc lz_dst+0 ;maximum base (for a 4/5/6/7 bit
sta _lz_match+0 ;length sequence) is 113
lda #$ff
_lz_moff_join: adc lz_dst+1
sta _lz_match+1
cpy #$04 ;Get any remaning run length bits
lda #%00000001
bcs _lz_mrun_gotten
_lz_mrun_loop: asl lz_bits
bne *+5
jsr _lz_refill_bits
rol
asl lz_bits
bcc _lz_mrun_loop
bne _lz_mrun_gotten
jsr _lz_refill_bits
bcc _lz_mrun_loop
_lz_mrun_gotten:
tay ;A 257-byte (=>$00) run serves as a
beq _lz_end_of_file ;sentinel
sta _lz_mcopy_len
ldy #$ff ;The copy loop. This needs to be run
_lz_mcopy: iny ;forwards since RLE-style matches can
_lz_match = *+1 ;overlap the destination
lda $ffff,y
sta (lz_dst),y
_lz_mcopy_len = *+1
cpy #$ff
bne _lz_mcopy
tya ;Advance destination pointer
; sec
adc lz_dst+0
sta lz_dst+0
jmp _lz_mfinish
;******** Fetch some more bits to work with ********
lz_sector_ptr1 = *+1
_lz_refill_bits:
ldy lz_sector,x
sty lz_bits
inx
bne *+5
jsr lz_fetch_sector
; sec
rol lz_bits
_lz_end_of_file:
rts
;******** Offset coding tables ********
;This length table is a bit funky. The idea here is to use the
;value as the initial value of the shift register instead of
;keeping a separate counter.
;In other words we iterate until the leading one is shifted out.
;Then afterwards the bit just below it (our new sign bit) is set
;if the offset is shorter than 8-bits, and conversely it's
;cleared if we need to fetch a separate low-byte
;as well.
;The fact that the sign bit is cleared as a flag is compensated
;for in the lz_moff_adjust_hi table
_lz_moff_length:
;Long (>2 byte matches)
.byte %00011111 ;4 bits
.byte %00000011 ;7 bits
.byte %01011111 ;10 bits
.byte %00001011 ;13 bits
;Short (2 byte matches)
.byte %01011111 ;10 bits
.byte %00000000 ;8 bits
.byte %00000111 ;6 bits
.byte %00111111 ;3 bits
_lz_moff_adjust_lo:
;Long (>2 byte matches)
.byte %11111110 ;1-16
.byte %11101110 ;17-144
.byte %01101110 ;145-1168
.byte %01101110 ;1169-9360
;Short (2 byte matches)
.byte %10110110 ;329-1352
.byte %10110110 ;73-328
.byte %11110110 ;9-72
.byte %11111110 ;1-8
_lz_moff_adjust_hi = *-2
;Long (>2 byte matches)
; .byte %11111111 ;1-16 (unreferenced)
; .byte %11111111 ;17-144 (unreferenced)
.byte %01111111 ;145-1168
.byte %01111011 ;1169-9360
;Short (2 byte matches)
.byte %01111110 ;329-1352
.byte %11111110 ;73-328
; .byte %11111111 ;9-72 (unreferenced)
; .byte %11111111 ;1-8 (unreferenced)

611
loader/src/decompress/exodecomp.s Executable file
View file

@ -0,0 +1,611 @@
; slightly modified by Krill/Plush for loader integration
;
; Copyright (c) 2002 - 2020 Magnus Lind.
;
; This software is provided 'as-is', without any express or implied warranty.
; In no event will the authors be held liable for any damages arising from
; the use of this software.
;
; Permission is granted to anyone to use this software for any purpose,
; including commercial applications, and to alter it and redistribute it
; freely, subject to the following restrictions:
;
; 1. The origin of this software must not be misrepresented; you must not
; claim that you wrote the original software. If you use this software in a
; product, an acknowledgment in the product documentation would be
; appreciated but is not required.
;
; 2. Altered source versions must be plainly marked as such, and must not
; be misrepresented as being the original software.
;
; 3. This notice may not be removed or altered from any distribution.
;
; 4. The names of this software and/or it's copyright holders may not be
; used to endorse or promote products derived from this software without
; specific prior written permission.
;
; -------------------------------------------------------------------
; Known quirks:
; Can't handle a sequence reference that ends at $ffff. It is left in
; since it is a corner case and fixing it impacts negatively on
; performance or backwards compatibility.
; A simple way to work around this is to not decrunch to address $ffff.
; -------------------------------------------------------------------
; Controls if the shared get_bits routines should be inlined or not.
;INLINE_GET_BITS=1
.IFNDEF INLINE_GET_BITS
INLINE_GET_BITS = 0
.ENDIF
; -------------------------------------------------------------------
; if literal sequences is not used (the data was crunched with the -c
; flag) then the following line can be uncommented for shorter and.
; slightly faster code.
;LITERAL_SEQUENCES_NOT_USED = 1
.IFNDEF LITERAL_SEQUENCES_NOT_USED
LITERAL_SEQUENCES_NOT_USED = 0
.ENDIF
; -------------------------------------------------------------------
; if the sequence length is limited to 256 (the data was crunched with
; the -M256 flag) then the following line can be uncommented for
; shorter and slightly faster code.
;MAX_SEQUENCE_LENGTH_256 = 1
.IFNDEF MAX_SEQUENCE_LENGTH_256
MAX_SEQUENCE_LENGTH_256 = 0
.ENDIF
; -------------------------------------------------------------------
; if the sequence length 3 has its own offset table (the data was
; crunched with the -P+16 flag) then the following
; line must be uncommented.
;EXTRA_TABLE_ENTRY_FOR_LENGTH_THREE = 1
.IFNDEF EXTRA_TABLE_ENTRY_FOR_LENGTH_THREE
EXTRA_TABLE_ENTRY_FOR_LENGTH_THREE = 0
.ENDIF
; -------------------------------------------------------------------
; if sequence offsets are not reused (the data was crunched with the
; -P-32 flag) then the following line must be uncommented. Uncommenting the
; line will also result in shorter and slightly faster code.
;DONT_REUSE_OFFSET = 1
.IFNDEF DONT_REUSE_OFFSET
DONT_REUSE_OFFSET = 0
.ENDIF
; -------------------------------------------------------------------
; if decrunching forwards then the following line must be uncommented.
DECRUNCH_FORWARDS = 1
.IFNDEF DECRUNCH_FORWARDS
DECRUNCH_FORWARDS = 0
.ENDIF
; -------------------------------------------------------------------
; if split encoding is used (the data is crunched with the -E flag)
; then the following line must be uncommented.
;ENABLE_SPLIT_ENCODING = 1
.IFNDEF ENABLE_SPLIT_ENCODING
ENABLE_SPLIT_ENCODING = 0
.ENDIF
; -------------------------------------------------------------------
; The decruncher jsr:s to the get_crunched_byte address when it wants to
; read a crunched byte into A. This subroutine has to preserve X and Y
; register and must not modify the state of the carry nor the overflow flag.
; -------------------------------------------------------------------
;.import get_crunched_byte
; -------------------------------------------------------------------
; This function is the heart of the decruncher. (for non split crunched files)
; It initializes the decruncher zeropage locations and precalculates the
; decrunch tables and decrunches the data
; This function will not change the interrupt status bit and it will not
; modify the memory configuration.
; -------------------------------------------------------------------
;.export decrunch
.IF ENABLE_SPLIT_ENCODING <> 0
; -------------------------------------------------------------------
; To decrunch files crunched with the split feature (-E) you can't use the
; decrunch function. Instead you call the split_decrunch function. But you
; can only do this if the decrunch table contains the encoding used by the
; file you are decrunching. To generate the correct content for the decrunch
; table call set the get_crunched_byte function to point to the encoding data
; and then call the split_gentable function.
; -------------------------------------------------------------------
.export split_gentable
.export split_decrunch
.ENDIF
; -------------------------------------------------------------------
; zero page addresses used
; -------------------------------------------------------------------
zp_len_lo = DECOMPVARS + 0; $9e
zp_len_hi = DECOMPVARS + 1; $9f
zp_src_lo = DECOMPVARS + 2; $ae
zp_src_hi = zp_src_lo + 1
zp_bits_hi = DECOMPVARS + 4; $a7
.IF DONT_REUSE_OFFSET = 0
zp_ro_state = DECOMPVARS + 5; $a8
.ENDIF
zp_bitbuf = DECOMPVARS + 6; $fd
.if MEM_DECOMP_TO_API
zp_dest_lo = decdestlo; zp_bitbuf + 1 ; dest addr lo
zp_dest_hi = decdesthi; zp_bitbuf + 2 ; dest addr hi
.else
zp_dest_lo = zp_bitbuf + 1 ; dest addr lo
zp_dest_hi = zp_bitbuf + 2 ; dest addr hi
.endif
.IF EXTRA_TABLE_ENTRY_FOR_LENGTH_THREE <> 0
encoded_entries = 68
.ELSE
encoded_entries = 52
.ENDIF
tabl_bi = decrunch_table
tabl_lo = decrunch_table + encoded_entries
tabl_hi = decrunch_table + encoded_entries * 2
decompress = decrunch
get_crunched_byte:
php
jsr getbyte
plp
rts
;; refill bits is always inlined
.MACRO mac_refill_bits
pha
jsr get_crunched_byte
rol
sta zp_bitbuf
pla
.ENDMACRO
.MACRO mac_get_bits
.IF INLINE_GET_BITS <> 0
.SCOPE
adc #$80 ; needs c=0, affects v
asl
bpl gb_skip
gb_next:
asl zp_bitbuf
bne gb_ok
mac_refill_bits
gb_ok:
rol
bmi gb_next
gb_skip:
bvc skip
gb_get_hi:
sec
sta zp_bits_hi
jsr get_crunched_byte
skip:
.ENDSCOPE
.ELSE
jsr get_bits
.ENDIF
.ENDMACRO
.MACRO mac_init_zp
;.SCOPE
; -------------------------------------------------------------------
; init zeropage and x reg. (8 bytes)
;
init_zp:
.if MEM_DECOMP_TO_API
jsr get_crunched_byte
storedadrh:
sta zp_dest_hi
jsr get_crunched_byte
storedadrl:
sta zp_dest_lo
jsr get_crunched_byte
sta zp_bitbuf
.else
jsr get_crunched_byte
sta zp_bitbuf - 1,x
dex
bne init_zp
.endif
.if LOADCOMPD_TO
clc
lda loadaddroffslo
adc zp_dest_lo
sta zp_dest_lo
lda loadaddroffshi
adc zp_dest_hi
sta zp_dest_hi
.endif
;.ENDSCOPE
.ENDMACRO
.IF INLINE_GET_BITS = 0
get_bits:
adc #$80 ; needs c=0, affects v
asl
bpl gb_skip
gb_next:
asl zp_bitbuf
bne gb_ok
mac_refill_bits
gb_ok:
rol
bmi gb_next
gb_skip:
bvs gb_get_hi
rts
gb_get_hi:
sec
sta zp_bits_hi
jmp get_crunched_byte
.ENDIF
; -------------------------------------------------------------------
; no code below this comment has to be modified in order to generate
; a working decruncher of this source file.
; However, you may want to relocate the tables last in the file to a
; more suitable address.
; -------------------------------------------------------------------
; -------------------------------------------------------------------
; jsr this label to decrunch, it will in turn init the tables and
; call the decruncher
; no constraints on register content, however the
; decimal flag has to be cleared (it almost always is, otherwise do a cld)
decrunch:
.IF ENABLE_SPLIT_ENCODING <> 0
ldx #3
jsr internal_gentable
jmp normal_decrunch
split_gentable:
ldx #1
internal_gentable:
jsr split_init_zp
.ELSE
ldx #3
mac_init_zp
.ENDIF
; -------------------------------------------------------------------
; calculate tables (64 bytes) + get_bits macro
; x must be #0 when entering
;
ldy #0
clc
table_gen:
tax
tya
and #$0f
sta tabl_lo,y
beq shortcut ; start a new sequence
; -------------------------------------------------------------------
txa
adc tabl_lo - 1,y
sta tabl_lo,y
lda zp_len_hi
adc tabl_hi - 1,y
shortcut:
sta tabl_hi,y
; -------------------------------------------------------------------
lda #$01
sta <zp_len_hi
lda #$78 ; %01111000
mac_get_bits
; -------------------------------------------------------------------
lsr
tax
beq rolled
php
rolle:
asl zp_len_hi
sec
ror
dex
bne rolle
plp
rolled:
ror
sta tabl_bi,y
bmi no_fixup_lohi
lda zp_len_hi
stx zp_len_hi
.BYTE $24
no_fixup_lohi:
txa
; -------------------------------------------------------------------
iny
cpy #encoded_entries
bne table_gen
; -------------------------------------------------------------------
.IF ENABLE_SPLIT_ENCODING <> 0
rts
split_decrunch:
ldx #3
jsr split_init_zp
; X reg must be 0 here
sec
normal_decrunch:
.ENDIF
; -------------------------------------------------------------------
; prepare for main decruncher
.IF DONT_REUSE_OFFSET = 0
ror zp_ro_state
sec
.ENDIF
ldy zp_dest_lo
stx zp_dest_lo
stx zp_bits_hi
; -------------------------------------------------------------------
; copy one literal byte to destination (11 bytes)
;
literal_start1:
.IF DECRUNCH_FORWARDS = 0
tya
bne no_hi_decr
dec zp_dest_hi
.IF DONT_REUSE_OFFSET = 0
dec zp_src_hi
.ENDIF
no_hi_decr:
dey
.ENDIF
jsr get_crunched_byte
sta (zp_dest_lo),y
.IF DECRUNCH_FORWARDS <> 0
iny
bne skip_hi_incr
inc zp_dest_hi
.IF DONT_REUSE_OFFSET = 0
inc zp_src_hi
.ENDIF
skip_hi_incr:
.ENDIF
; -------------------------------------------------------------------
; fetch sequence length index (15 bytes)
; x must be #0 when entering and contains the length index + 1
; when exiting or 0 for literal byte
next_round:
.IF DONT_REUSE_OFFSET = 0
ror zp_ro_state
.ENDIF
dex
lda zp_bitbuf
no_literal1:
asl
bne nofetch8
jsr get_crunched_byte
rol
nofetch8:
inx
bcc no_literal1
sta zp_bitbuf
; -------------------------------------------------------------------
; check for literal byte (2 bytes)
;
beq literal_start1
; -------------------------------------------------------------------
; check for decrunch done and literal sequences (4 bytes)
;
cpx #$11
.IF INLINE_GET_BITS <> 0
bcc skip_jmp
jmp exit_or_lit_seq
skip_jmp:
.ELSE
bcs exit_or_lit_seq
.ENDIF
; -------------------------------------------------------------------
; calulate length of sequence (zp_len) (18(11) bytes) + get_bits macro
;
lda tabl_bi - 1,x
mac_get_bits
adc tabl_lo - 1,x ; we have now calculated zp_len_lo
sta zp_len_lo
.IF MAX_SEQUENCE_LENGTH_256 = 0
lda zp_bits_hi
adc tabl_hi - 1,x ; c = 0 after this.
sta zp_len_hi
; -------------------------------------------------------------------
; here we decide what offset table to use (27(26) bytes) + get_bits_nc macro
; z-flag reflects zp_len_hi here
;
ldx zp_len_lo
.ELSE
tax
.ENDIF
.IF MAX_SEQUENCE_LENGTH_256 = 0
lda #0
.ENDIF
.IF DONT_REUSE_OFFSET = 0
; -------------------------------------------------------------------
; here we decide to reuse latest offset or not (13(15) bytes)
;
bit <zp_ro_state
bmi test_reuse
no_reuse:
.ENDIF
; -------------------------------------------------------------------
; here we decide what offset table to use (17(15) bytes)
;
.IF MAX_SEQUENCE_LENGTH_256 = 0
sta <zp_bits_hi
.ENDIF
lda #$e1
.IF EXTRA_TABLE_ENTRY_FOR_LENGTH_THREE <> 0
cpx #$04
.ELSE
cpx #$03
.ENDIF
bcs gbnc2_next
lda tabl_bit - 1,x
gbnc2_next:
asl zp_bitbuf
bne gbnc2_ok
tax
jsr get_crunched_byte
rol
sta zp_bitbuf
txa
gbnc2_ok:
rol
bcs gbnc2_next
tax
; -------------------------------------------------------------------
; calulate absolute offset (zp_src) (17 bytes) + get_bits macro
;
lda tabl_bi,x
mac_get_bits
.IF DECRUNCH_FORWARDS = 0
adc tabl_lo,x
sta zp_src_lo
lda zp_bits_hi
adc tabl_hi,x
adc zp_dest_hi
sta zp_src_hi
.ELSE
clc
adc tabl_lo,x
eor #$ff
sta zp_src_lo
lda zp_bits_hi
adc tabl_hi,x
eor #$ff
adc zp_dest_hi
sta zp_src_hi
clc
.ENDIF
; -------------------------------------------------------------------
; prepare for copy loop (2 bytes)
;
ldx zp_len_lo
; -------------------------------------------------------------------
; main copy loop (30 bytes)
;
copy_next:
.IF DECRUNCH_FORWARDS = 0
tya
bne copy_skip_hi
dec zp_dest_hi
dec zp_src_hi
copy_skip_hi:
dey
.ENDIF
.IF LITERAL_SEQUENCES_NOT_USED = 0
bcs get_literal_byte
.ENDIF
lda (zp_src_lo),y
literal_byte_gotten:
sta (zp_dest_lo),y
.IF DECRUNCH_FORWARDS <> 0
iny
bne copy_skip_hi
inc zp_dest_hi
inc zp_src_hi
copy_skip_hi:
.ENDIF
dex
bne copy_next
.IF MAX_SEQUENCE_LENGTH_256 = 0
lda zp_len_hi
.IF INLINE_GET_BITS <> 0
bne copy_next_hi
.ENDIF
.ENDIF
stx zp_bits_hi
.IF INLINE_GET_BITS = 0
beq next_round
.ELSE
jmp next_round
.ENDIF
.IF MAX_SEQUENCE_LENGTH_256 = 0
copy_next_hi:
dec zp_len_hi
jmp copy_next
.ENDIF
.IF DONT_REUSE_OFFSET = 0
; -------------------------------------------------------------------
; test for offset reuse (11 bytes)
;
test_reuse:
bvs no_reuse
.IF MAX_SEQUENCE_LENGTH_256 <> 0
lda #$00 ; fetch one bit
.ENDIF
asl zp_bitbuf
bne gbnc1_ok
pha
jsr get_crunched_byte
rol
sta zp_bitbuf
pla
gbnc1_ok:
rol
beq no_reuse ; bit == 0 => C=0, no reuse
bne copy_next ; bit != 0 => C=0, reuse previous offset
.ENDIF
; -------------------------------------------------------------------
; exit or literal sequence handling (16(12) bytes)
;
exit_or_lit_seq:
.IF LITERAL_SEQUENCES_NOT_USED = 0
beq decr_exit
jsr get_crunched_byte
.IF MAX_SEQUENCE_LENGTH_256 = 0
sta zp_len_hi
.ENDIF
jsr get_crunched_byte
tax
bcs copy_next
decr_exit:
.ENDIF
rts
.IF LITERAL_SEQUENCES_NOT_USED = 0
get_literal_byte:
jsr get_crunched_byte
bcs literal_byte_gotten
.ENDIF
.IF EXTRA_TABLE_ENTRY_FOR_LENGTH_THREE <> 0
; -------------------------------------------------------------------
; the static stable used for bits+offset for lengths 1, 2 and 3 (3 bytes)
; bits 2, 4, 4 and offsets 64, 48, 32 corresponding to
; %10010000, %11100011, %11100010
tabl_bit:
.BYTE $90, $e3, $e2
.ELSE
; -------------------------------------------------------------------
; the static stable used for bits+offset for lengths 1 and 2 (2 bytes)
; bits 2, 4 and offsets 48, 32 corresponding to %10001100, %11100010
tabl_bit:
.BYTE $8c, $e2
.ENDIF
.IF ENABLE_SPLIT_ENCODING <> 0
split_init_zp:
mac_init_zp
rts
.ENDIF
; -------------------------------------------------------------------
; end of decruncher
; -------------------------------------------------------------------
; -------------------------------------------------------------------
; this 156 (204) byte table area may be relocated. It may also be
; clobbered by other data between decrunches.
; -------------------------------------------------------------------
decrunch_table:
.byte 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
.byte 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
.byte 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
.IF EXTRA_TABLE_ENTRY_FOR_LENGTH_THREE <> 0
.byte 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
.byte 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
.byte 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
.ENDIF
.byte 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
.byte 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
.byte 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
.byte 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
.byte 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
.byte 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
.byte 0,0,0,0,0,0,0,0,0,0,0,0
; -------------------------------------------------------------------
; end of decruncher
; -------------------------------------------------------------------

180
loader/src/decompress/lcdecomp.s Executable file
View file

@ -0,0 +1,180 @@
; the following depack code is
; written by Marek Matula (MMS/Taboo)
; in its original form and has been
; slightly modified
; compress using taboo levelcrush or
; crush.exe by taboo,
; don't forget to convert the
; compressed files (not needed for mem-decompressing)
; Level-crusher v1.0/v1.1 depacker
; (c)1998 Taboo Productions!
; All rights reserved
LC_SPEED = 6
byte = DECOMPVARS+$00
hi = DECOMPVARS+$01
dest = decdestlo
desth = decdesthi
decompress:
jsr getbyte
.if LOADCOMPD_TO
clc
adc loadaddroffslo
php
.endif
storedadrl: sta dest
jsr getbyte
.if LOADCOMPD_TO
plp
adc loadaddroffshi
.endif
storedadrh: sta desth
ldx #$00
stx byte
jp18: stx hi
lda #$01
asl byte
bne *+$05
jsr getbit
bcs jp2
jp4: asl byte
bne *+$05
jsr getbit
bcs jp3
asl byte
bne *+$05
jsr getbit
rol a
rol hi
bpl jp4
jp3: tax
beq jp5
; literal run
ldy #$00
literalrun: jsr getbyte
sta (dest),y
inc dest
bne :+
inc desth
: dex
bne literalrun
jp5: cpx hi
dec hi
bcc literalrun
stx hi
jp2: lda #$01
asl byte
bne *+$05
jsr getbit
bcc jp9
jp8: asl byte
bne *+$05
jsr getbit
bcs jp10
asl byte
bne *+$05
jsr getbit
rol a
bcc jp8
; decompression finished
rts
jp9: inx
jp10: adc #$01
sta depseqle
txa
asl byte
bne *+$05
jsr getbit
rol a
asl byte
bne *+$05
jsr getbit
rol a
tay
lda #$00
jp12: ldx tab,y
jp11: asl byte
bne *+$05
jsr getbit
rol a
rol hi
dex
bne jp11
dey
bmi jp14
cpy #$03
clc
beq jp14
adc #$01
bcc jp12
inc hi
bcs jp12
jp14: adc depseqle
bcc jp15
inc hi
; copy sequence
jp15: clc
sbc dest
eor #$ff
sta depseqcp+$01
lda hi
sbc desth
eor #$ff
sta depseqcp+$02
ldy #$00
depseqcp: lda $00,y
sta (dest),y
iny
depseqle = *+$01
cpy #$00
bne depseqcp
tya
clc
adc dest
sta dest
bcc jp17
inc desth
jp17: jmp jp18
getbit: pha
jsr getbyte
sec
rol a
sta byte
pla
jp19: rts
tab:
.if LC_SPEED = 6
.byte 4,3,3,3,4,2,2,2
.endif
.if LC_SPEED = 5
.byte 4,2,3,3,4,2,2,2
.endif
.if LC_SPEED = 4
.byte 4,2,2,3,4,2,2,2
.endif
.if LC_SPEED = 3
.byte 4,2,2,2,4,2,2,2
.endif
.if LC_SPEED = 2
.byte 3,2,2,2,3,2,2,2
.endif
.if LC_SPEED = 1
.byte 3,1,2,2,3,1,2,2
.endif
.if LC_SPEED = 0
.byte 2,2,1,1,2,2,1,1
.endif

View file

@ -0,0 +1,334 @@
; with slight modifications by Krill/Plush
.FEATURE labels_without_colons, leading_dot_in_identifiers
decompress = .loadcomp_entry
decompsrc = lzsa_srcptr
.if MEM_DECOMP_TO_API
; cannot copy remaining uncompressed blob of unknown size
.error "***** MEM_DECOMP_TO_API is not supported for LZSA2. Copy compressed data to original location, then use MEM_DECOMP_API to decompress in-place. *****"
.endif
BITFIRE_ZP_ADDR = DECOMPVARS - 1
.define asr alr
lzsa2_get_byte:
lda (lzsa_srcptr),y ;Subroutine version for when
inc <lzsa_srcptr + 0 ;inlining isn't advantageous.
beq .lz_next_page
rts
.lz_next_page
inc <lzsa_srcptr + 1 ;Inc & test for bank overflow.
.lz_next_page_
pha
txa
pha
GETBLOCK <lzsa_srcptr + 1
pla
tax
pla
rts
.loadcomp_entry
jsr .lz_next_page_ ;shuffle in data first
; ldy #$00 ;Initialize source index.
ldx #$04
:
jsr lzsa2_get_byte
dex
sta <lzsa_dstptr + 0,x
bne :-
.if LOADCOMPD_TO
ldx #2
:
clc
lda loadaddroffslo
adc <lzsa_dstptr,x
sta <lzsa_dstptr,x
lda loadaddroffshi
adc <lzsa_dstptr + 1,x
sta <lzsa_dstptr + 1,x
dex
dex
bpl :-
ldx #0
.endif
; -----------------------------------------------------------------------------
;
; Copyright (C) 2019 Emmanuel Marty
;
; This software is provided 'as-is', without any express or implied
; warranty. In no event will the authors be held liable for any damages
; arising from the use of this software.
;
; Permission is granted to anyone to use this software for any purpose,
; including commercial applications, and to alter it and redistribute it
; freely, subject to the following restrictions:
;
; 1. The origin of this software must not be misrepresented; you must not
; claim that you wrote the original software. If you use this software
; in a product, an acknowledgment in the product documentation would be
; appreciated but is not required.
; 2. Altered source versions must be plainly marked as such, and must not be
; misrepresented as being the original software.
; 3. This notice may not be removed or altered from any source distribution.
;
; Original software by Emmanuel Marty, altered by Tobias Bindhammer
; to adopt it to bitfire, includes optimization in size and speed and
; changes to the encoded format
; -----------------------------------------------------------------------------
lzsa_cmdbuf = BITFIRE_ZP_ADDR + 1 ;1 byte.
lzsa_nibflg = BITFIRE_ZP_ADDR + 2 ;1 byte.
lzsa_nibble = BITFIRE_ZP_ADDR + 3 ;1 byte.
lzsa_offset = BITFIRE_ZP_ADDR + 4 ;1 word.
lzsa_winptr = BITFIRE_ZP_ADDR + 6 ;1 word.
lzsa_srcptr = BITFIRE_ZP_ADDR + 8 ;1 word.
lzsa_dstptr = BITFIRE_ZP_ADDR + 10 ;1 word.
lzsa_endptr = BITFIRE_ZP_ADDR + 12 ;1 word.
lzsa_length = lzsa_winptr + 0
lzsa_tmp = lzsa_winptr + 1
;command byte:
;765 432 10
;ttt mmm ll
;this saves an lsr in .asm - with xyzllmmm we would need 3 lsr, now we need two lsr
;ll literals length
;mmm match length
.depacker_start
;XXX TODO can be saved when used with filename? or if it exits cleared
lzsa2_depack
sty <lzsa_nibflg ;Initialize nibble buffer.
;
;Copy bytes from compressed source data.
;
;Hi-byte of length or offset.
.cp_length
jsr lzsa2_get_byte
sta <lzsa_cmdbuf ;Preserve this for later.
and #$03 ;Extract literal length.
beq .lz_offset ;size = 0 -> no literal, continue with match
cmp #$03 ;Extended length?
bne .got_cp_len ;1..2
jsr .get_length ;x is set by .get_length
beq .put_cp_len
.got_cp_len
inx ;Increment # of pages to copy.
.put_cp_len
stx <lzsa_length
tax ;low byte
.cp_page
;.lz_ptr1
; lda $face,x
; sta (dst),y
; inx
; bne +
; jsr .lz_next_page
;+
; iny
; cpy #$00
; bne .cp_page
; tya
; clc
; adc <lzsa_dstptr + 0
; sta <lzsa_dstptr + 0
; bcc +
; inc <özsa_dstptr + 1
;+
; dec <lzsa_length
; bne .cp_page
lda (lzsa_srcptr),y
;saves another 6 bytes if used here, then single point of change
;jsr lzsa2_get_byte
sta (lzsa_dstptr),y
inc <lzsa_srcptr + 0
bne .skip1
jsr .lz_next_page
.skip1
inc <lzsa_dstptr + 0
bne .skip2
inc <lzsa_dstptr + 1
.skip2
dex
bne .cp_page
dec <lzsa_length ;Any full pages left to copy?
bne .cp_page
;ttt token for match offset len
;codepath 1
;0m0 5-bit offset - m goes into first bit of offset + one nibble is read ;11111111 111mnnnn
;0m1 13-bit offset - m goes into first bit of offset + one nibble and on byte is read ;111mnnnn bbbbbbbb
;codepath 2
;10m 9-bit offset - m goes into first bit of offset + one byte is read ;1111111m bbbbbbbb
;110 16-bit offset - two bytes are read ;bbbbbbbb bbbbbbbb
;111 repeat offset
;fetch a nibble
;13,9,16 -> fetch a lowbyte
;9 bit fetch a bit
;16 -> fetch a highbyte
;rep, skip all
.lz_offset
lda <lzsa_cmdbuf
asl
bcc .get_5_13_bits ;prefer path with 3 options over path with 2 options
.get_9_16_rep
asl
bcc .get_9_bits
.get_16_rep
bmi .lz_length ;Repeat previous offset.
.get_16_bits
jsr lzsa2_get_byte ;Get hi-byte of offset.
bne .get_low8
.get_9_bits
asl
lda #$ff ;-> $ff/$fe
rol
bne .get_low8 ;BRA
.get_5_13_bits
sta <lzsa_tmp
jsr lzsa2_get_nibble
asl <lzsa_tmp ;shift in bit 5/13
rol
asl <lzsa_tmp ;shift in token bit to decide 5/13
dex ;x = $ff
bcc .get_low0 ;all done for 5-bit offset
sbc #2 ;Subtract 512 because 13-bit - this extends the range for 13 bits offsets, as 0..511 is covered by 9 bit matches
;offset starts at $FE00.
.get_low8
tax
jsr lzsa2_get_byte
.get_low0
sta <lzsa_offset + 0
stx <lzsa_offset + 1 ;Save new offset.
;
;Copy bytes from decompressed window.
;
;N.B. X=0 is expected and guaranteed when we get here.
;
.lz_length
ldx #$00 ;Hi-byte of length.
lda <lzsa_cmdbuf
lsr
asr #$0e ;extract match len and clear C
adc #$02 ;correct length
cmp #$09 ;Extended length?
bne .got_lz_len ;a = 2 .. 8, C = 0
jsr .get_length ;x is set by .get_length, y is still 0
clc
beq .calc_lz_addr ;only need to check for zero here
.got_lz_len ;C = 0
eor #$ff ;Negate the lo-byte of length
tay
iny
eor #$ff ;restore A, a bit ugly
inx ;Increment # of pages to copy.
;clc ;Calc destination for partial page
adc <lzsa_dstptr + 0
sta <lzsa_dstptr + 0
bcs .calc_lz_addr_
dec <lzsa_dstptr + 1
.calc_lz_addr
lda <lzsa_dstptr + 0 ;N.B. Offset is negative!
.calc_lz_addr_
clc ;Calc address of match.
adc <lzsa_offset + 0
sta <lzsa_winptr + 0
lda <lzsa_dstptr + 1
adc <lzsa_offset + 1
sta <lzsa_winptr + 1
.lz_page
lda (lzsa_winptr),y
sta (lzsa_dstptr),y
iny
bne .lz_page
inc <lzsa_winptr + 1
inc <lzsa_dstptr + 1
dex ;Any full pages left to copy?
bne .lz_page
lda <lzsa_srcptr + 0
eor <lzsa_endptr + 0
bne .lz_poll
eor <lzsa_srcptr + 1
eor <lzsa_endptr + 1
beq .finished2
.lz_skip_end
jmp .cp_length ;Loop around to the beginning.
.lz_poll
POLLBLOCK
; ldy #0
ldx #$00 ;clear x afterwards, yet a bit annoying, y stays 0
beq .lz_skip_end
.get_length
;entered with x = 0
adc #$0e ;C = 1 -> adc #$0f
sta <lzsa_tmp
jsr lzsa2_get_nibble
adc #$00 ;C = 1
beq .byte_length ;Extended length?
adc <lzsa_tmp ;C = 0 from addition above -> $10 + lzsa_tmp
.got_length ;Z-flag is set
rts ;lengths.
.byte_length
jsr lzsa2_get_byte ;So rare, this can be slow!
clc ;adc #$0f + 0
adc <lzsa_tmp
bcc .got_length
beq .finished
.word_length
;this is fetched big-endian, to avoid pha/pla, saves code
jsr lzsa2_get_byte ;So rare, this can be slow!
tax
jsr lzsa2_get_byte ;So rare, this can be slow!
and #$ff ;restore z-flag
rts
.finished
pla ;Decompression completed, pop
pla ;return address.
.finished2
rts
;
;Get a nibble value from compressed data in A.
;
;XXX TODO pack nibbles in another way? -> 10101010 -> first nibble = and #$55, second nibble = asr #$aa?
lzsa2_get_nibble
lsr <lzsa_nibflg ;Is there a nibble waiting?
lda <lzsa_nibble ;Extract the lo-nibble.
bcs .got_nibble
inc <lzsa_nibflg ;Reset the flag.
jsr lzsa2_get_byte
sta <lzsa_nibble ;Preserve for next time.
lsr ;Extract the hi-nibble.
lsr
lsr
lsr
sec
.got_nibble
ora #$f0
rts

344
loader/src/decompress/ncdecomp.s Executable file
View file

@ -0,0 +1,344 @@
;
; NuCrunch 1.0
; Christopher Jam
; May 2018
; with slight modifications by Krill
;
decompress = decrunch
decompsrc = read+1
NC_BLOCK_INTERFACE = 1
.macro getByte1
jsr get_byte
.endmacro
.macro getBit1
.local nomore
asl zbs1
bne nomore
getByte1
sec
rol
sta zbs1
nomore:
.endmacro
; get head of a pair of bits from the bitpair stream
; (must getBit2t precisely once before invoking again)
.macro getBit2h
.local nomore
asl zbs2
bne nomore
getByte1
sec
rol
sta zbs2
nomore:
.endmacro
.if NC_BLOCK_INTERFACE
; same, but preserving A
.macro getBit2hpa
.local nomore
asl zbs2
bne nomore
pha
getByte1
sec
rol
sta zbs2
pla
nomore:
.endmacro
.else
; same, but preserving A/ trashing X.
.macro getBit2hpa
.local nomore
asl zbs2
bne nomore
tax
getByte1
sec
rol
sta zbs2
txa
nomore:
.endmacro
.endif
; get tail of a pair of bits from the bitpair stream
.macro getBit2t
asl zbs2
.endmacro
; get head of a quad of bits from the quad stream
; (must getBit4t precisely three times before invoking again)
.macro getBit4h
.local nomore
asl zbs4
bne nomore
getByte1
sec
rol
sta zbs4
nomore:
.endmacro
; get tail of a quad of bits from the quad stream
.macro getBit4t
asl zbs4
.endmacro
; note, trashes X. Also, carry is clear when done
.macro getExpGoulombTail
.local ndone
ndone:
getBit2hpa
rol
getBit2t
bcs ndone
.endmacro
.macro getExpGoulombTail_odd_aligned
.local ndone
ndone:
getBit2t
rol
getBit2hpa
bcs ndone
.endmacro
.ifdef NUCRUNCH_ALIGN_FOR_SPEED
.byte <-$64-*,0 ; place decode_copy on a page boundary
.endif
decrunch_zpa=DECOMPVARS ;5 bytes required
zbs1 = decrunch_zpa+$00 ; 1 byte
zbs2 = decrunch_zpa+$01 ; 1 byte
zbs4 = decrunch_zpa+$02 ; 1 byte
zpc = decrunch_zpa+$03 ; 2 bytes
zpd = decdestlo
offsetm1 = zpc ; these are aliased, as never need both
decrunch:
; ldy #0
sty zbs1
sty zbs2
sty zbs4
decrunch_next_group:
.if NC_BLOCK_INTERFACE
jsr read_init
lda loadaddrhi
sta literal_read+2
ldx loadaddrlo
.endif
next_segment:
jsr get_byte
.if LOADCOMPD_TO
clc
adc loadaddroffslo
php
.endif
storedadrl:
sta zpd+0
jsr get_byte
.if LOADCOMPD_TO
plp
adc loadaddroffshi
.endif
storedadrh:
sta zpd+1
decode_literal:
; get count [ExpGoulomb0+1] in x
.if NC_BLOCK_INTERFACE
getBit1
lda#1
bcc ret1
getExpGoulombTail
ret1:
sta literal_len + 1
.else
ldx#1
getBit1
bcc ret1
lda#1
getExpGoulombTail
tax
ret1:
.endif
literal_loop:
.if NC_BLOCK_INTERFACE
literal_read:
lda $ff00,x
inx
bne *+5
jsr read_inc
.else
decompgetbyte:
jsr getcmem
.endif
sta (zpd),y
iny
.if NC_BLOCK_INTERFACE
literal_len:
cpy #0
.else
dex
.endif
bne literal_loop
clc
tya
adc zpd
sta zpd
bcc *+4
inc zpd+1
ldy#0
; literal is always followed by copy
decode_copy:
getBit2h
bcc short_offset
lda#1
getExpGoulombTail_odd_aligned
adc#255
sta offsetm1+1
getByte1
sta offsetm1
jmp got_high
short_offset:
lda#0
sta offsetm1+1
;ExpGoulomb k=3
getBit4h
lda#1
bcc no_tail
getExpGoulombTail_odd_aligned
no_tail:
adc#255
getBit4t
rol
getBit4t
rol
getBit4t
rol
sta offsetm1
got_high:
.if NC_BLOCK_INTERFACE
lda#1
getBit2t
bcc length_two
getExpGoulombTail
cmp#255
beq end_of_segment ; copy length of 256 marks end of segment
length_two:
sta copy_len+1
.else
ldx#1
getBit2t
bcc length_two
lda#1
getExpGoulombTail
tax
cpx#255
beq end_of_segment ; copy length of 256 marks end of segment
length_two:
.endif
; note carry is clear at this point; good as we want to subtract (offsetm1+1)
lda zpd
sbc offsetm1
sta zpc
lda zpd+1
sbc offsetm1+1
sta zpc+1
lda (zpc),y
sta (zpd),y
copy_loop:
iny
lda (zpc),y
sta (zpd),y
.if NC_BLOCK_INTERFACE
copy_len:
cpy #0
.else
dex
.endif
bne copy_loop
tya
; carry will be set from SBC above
adc zpd
sta zpd
bcc *+4
inc zpd+1
.if NC_BLOCK_INTERFACE
POLLBLOCK
.endif
ldy#0
getBit1
bcs jmp_decode_copy
jmp decode_literal
jmp_decode_copy:
jmp decode_copy
get_byte:
.if NC_BLOCK_INTERFACE
read:
lda $ff00,x
inx
beq read_inc
rts
read_inc:
inc literal_read+2
inc read+2
read_init:
pha
tya
pha
GETBLOCK read+2
pla
tay
pla
ldx#0
end_of_segment:
rts
.else
decompgetbyte1:
jmp getcmem
.endif
.if NC_BLOCK_INTERFACE
.else
end_of_file:
rts
end_of_segment:
lda offsetm1
cmp#0
beq end_of_file
jmp next_segment
.endif
decrunch_end:

261
loader/src/decompress/pudecomp.s Executable file
View file

@ -0,0 +1,261 @@
; The following depack code is
; written by Pasi Ojala (Albert/PU239)
; in its original form and has been
; slightly modified.
; The original routine was located at
; http://www.cs.tut.fi/~albert/Dev/pucrunch/sa_uncrunch.asm
; at the time of writing.
; crunch using the -c0 switch
.define OLD_VERSION 0; pre 2004/3/24, this includes http://www.cs.tut.fi/~albert/Dev/pucrunch/pucrunch_x86.zip as of 2013/4/9
LZPOS = DECOMPVARS + $00
bitstr = DECOMPVARS + $02
decompress: ldx #5
@222: jsr getbyt ; skip 'p', 'u', endAddr HI&LO, leave starting escape in A
dex
bne @222
sta esc+1 ; starting escape
jsr getbyt ; read startAddr
.if MEM_DECOMP_TO_API
ldx storedadrl
cpx #OPC_LDA_ZP
bne :+
storedadrl: lda decdestlo
:
.endif
.if LOADCOMPD_TO
clc
adc loadaddroffslo
php
.endif
sta OUTPOS
jsr getbyt
.if MEM_DECOMP_TO_API
ldx storedadrh
cpx #OPC_LDA_ZP
bne :+
storedadrh: lda decdesthi
:
.endif
.if LOADCOMPD_TO
plp
adc loadaddroffshi
.endif
sta OUTPOS+1
jsr getbyt ; read # of escape bits
sta escB0+1
sta escB1+1
lda #8
sec
sbc escB1+1
sta noesc+1 ; 8-escBits
jsr getbyt
sta mg+1 ; maxGamma + 1
lda #9
sec
sbc mg+1 ; 8 - maxGamma == (8 + 1) - (maxGamma + 1)
sta longrle+1
jsr getbyt
sta mg1+1 ; (1<<maxGamma)
asl a
clc
sbc #0
sta mg21+1 ; (2<<maxGamma) - 1
jsr getbyt
sta elzpb+1
jsr getbyt ; exec address
sta lo+1 ; lo
jsr getbyt
sta hi+1 ; hi
jsr getbyt ; rleUsed
ldx #0
tay
beq @1 ; Y == 0 ?
@0: jsr getbyt
sta table,x
inx
dey
bne @0
@1: ; setup bit store - $80 means empty
lda #$80
sta bitstr
bne main
getbyt: jsr getnew
lda bitstr
ror
rts
newesc: ldy esc+1 ; remember the old code (top bits for escaped byte)
escB0: ldx #2 ; ** PARAMETER 0..8
jsr getchkf ; get & save the new escape code
sta esc+1
tya ; pre-set the bits
; Fall through and get the rest of the bits.
noesc: ldx #6 ; ** PARAMETER 8..0
jsr getchkf
jsr putch ; output the escaped/normal byte
; Fall through and check the escape bits again
main: ldy #0 ; Reset to a defined state
tya ; A = 0
escB1: ldx #2 ; ** PARAMETER 0..8
jsr getchkf ; X = 0
esc: cmp #0
bne noesc
; Fall through to packed code
jsr getval ; X = 0
sta LZPOS ; xstore - save the length for a later time
lsr ; cmp #1 ; LEN == 2 ? (A is never 0)
bne lz77 ; LEN != 2 -> LZ77
;tya ; A = 0
jsr get1bit ; X = 0
lsr ; bit -> C, A = 0
bcc lz77_2 ; A=0 -> LZPOS+1
;***FALL THRU***
; e..e01
jsr get1bit ; X = 0
lsr ; bit -> C, A = 0
bcc newesc ; e..e010
;***FALL THRU***
; e..e011
srle: iny ; Y is 1 bigger than MSB loops
jsr getval ; Y is 1, get len, X = 0
sta LZPOS ; xstore - Save length LSB
mg1: cmp #64 ; ** PARAMETER 63-64 -> C clear, 64-64 -> C set..
bcc chrcode ; short RLE, get bytecode
longrle: ldx #2 ; ** PARAMETER 111111xxxxxx
jsr getbits ; get 3/2/1 more bits to get a full byte, X = 0
sta LZPOS ; xstore - Save length LSB
jsr getval ; length MSB, X = 0
tay ; Y is 1 bigger than MSB loops
chrcode: jsr getval ; Byte Code, X = 0
tax ; this is executed most of the time anyway
lda table-1,x; Saves one jump if done here (loses one txa)
.if OLD_VERSION
cpx #32 ; 31-32 -> C clear, 32-32 -> C set..
.else
cpx #16 ; 15-16 -> C clear, 16-16 -> C set..
.endif
bcc @1 ; 1..31, we got the right byte from the table
; Ranks 32..64 (11111°xxxxx), get byte..
txa ; get back the value (5 valid bits)
.if OLD_VERSION
ldx #3
.else
ldx #4
.endif
jsr getbits ; get 3 more bits to get a full byte, X = 0
@1: ldx LZPOS ; xstore - get length LSB
inx ; adjust for cpx#$ff;bne -> bne
dorle: jsr putch
dex
bne dorle ; xstore 0..255 -> 1..256
dey
bne dorle ; Y was 1 bigger than wanted originally
mainbeq: beq main ; reverse condition -> jump always
lz77: jsr getval ; X = 0
mg21: cmp #127 ; ** PARAMETER Clears carry (is maximum value)
bne noeof
; EOF
eof:
hi: ldx #0
lo: ldy #0
rts
noeof: sbc #0 ; C is clear -> subtract 1 (1..126 -> 0..125)
elzpb: ldx #0 ; ** PARAMETER (more bits to get)
jsr getchkf ; clears Carry, X = 0
lz77_2: sta LZPOS+1 ; offset MSB
jsr get8bit ; clears Carry, X = 0
; Note: Already eored in the compressor..
;eor #255 ; offset LSB 2's complement -1 (i.e., -X = ~X+1)
adc OUTPOS ; -offset -1 + curpos (C is clear)
ldx LZPOS ; xstore = LZLEN (read before it's overwritten)
sta LZPOS
lda OUTPOS+1
sbc LZPOS+1 ; takes C into account
sta LZPOS+1 ; copy X+1 number of chars from LZPOS to OUTPOS
;ldy #0 ; Y was 0 originally, we don't change it
inx ; adjust for cpx#$ff;bne -> bne
lzloop: lda (LZPOS),y; using abs,y is 3 bytes longer, only 1 cycle/byte faster
jsr putch ; Note: must be copied forwards!
iny ; Y does not wrap because X=0..255 and Y initially 0
dex
bne lzloop ; X loops, (256,1..255)
beq mainbeq ; jump through another beq (-1 byte, +3 cycles)
getnew: pha ; 1 Byte/3 cycles
jsr getbyte
sec
rol ; Shift out the next bit and
; shift in C=1 (last bit marker)
sta bitstr ; bitstr initial value = $80 == empty
pla ; 1 Byte/4 cycles
rts
; 25+12 = 37
; getval : Gets a 'static huffman coded' value
; ** Scratches X, returns the value in A **
getval: inx ; X <- 1
txa ; set the top bit (value is 1..255)
gv0: asl bitstr
bne @1
jsr getnew
@1: bcc getchk ; got 0-bit
inx
mg: cpx #7 ; ** PARAMETER unary code maximum length + 1
bne gv0
beq getchk ; inverse condition -> jump always
; getval: 18 bytes
; 15 + 17*n + 6+15*n+12 + 36*n/8 = 33 + 32*n + 36*n/8 cycles
; getbits: Gets X bits from the stream
; ** Scratches X, returns the value in A **
get8bit: ldx #7
get1bit: inx ;2
getbits: asl bitstr
bne @1
jsr getnew
@1: rol ;2
getchk: dex ;2 more bits to get ?
getchkf: bne getbits ;2/3
clc ;2 return carry cleared
rts ;6+6
OUTPOS = *+$01
putch: sta $aaaa ; ** parameter
inc OUTPOS ; ZP
bne @0
inc OUTPOS+1 ; ZP
@0: rts
.if OLD_VERSION
table: .res 31,0
.else
table: .res 15,0
.endif

View file

@ -0,0 +1,491 @@
;**************************************************************************
;*
;* Copyright (c) 2015, 2017 Daniel Kahlin <daniel@kahlin.net>
;* Written by Daniel Kahlin <daniel@kahlin.net>
;* Slightly modified by Gunnar Ruthenberg <krill@plush.de>
;*
;* DESCRIPTION
;* subsizer 0.6 decruncher - stand alone version
;*
;* usage:
;* You need to provide a function to get a byte from the input
;* stream. (must preserve X,Y and C)
;*
;******
decompress = decrunch
.FEATURE leading_dot_in_identifiers
;**************************************************************************
;*
;* Configuration options
;*
;******
FORWARD_DECRUNCHING = 1
HAVE_LONG_PARTS = 1
.if HAVE_LONG_PARTS
PART_MASK = %00001111
N_PARTS = 16
.else
PART_MASK = %00000111
N_PARTS = 8
.endif
len_zp = DECOMPVARS
copy_zp = DECOMPVARS + 1
hibits_zp = DECOMPVARS + 3
buf_zp = DECOMPVARS + 4
.if MEM_DECOMP_TO_API
dest_zp = decdestlo
.else
dest_zp = DECOMPVARS + 5
.endif
endm_zp = DECOMPVARS + 7
;**************************************************************************
;*
;* NAME fast macros
;*
;******
;******
;* get bit macro
.macro get_bit
.local .gb_skp1
asl buf_zp
bne .gb_skp1
; C=1 (because the marker bit was just shifted out)
jsr dc_get_byte
rol
sta buf_zp
.gb_skp1:
.endmacro
;******
;* get bits max8 macro
.macro get_bits_max8
.local .gb_lp1
.local .gb_skp1
.gb_lp1:
asl buf_zp
bne .gb_skp1
; C=1 (because the marker bit was just shifted out)
pha
jsr dc_get_byte
rol
sta buf_zp
pla
.gb_skp1:
rol
dey
bne .gb_lp1
.endmacro
;******
;* get bits max8 masked macro
.macro get_bits_max8_masked
.local .gb_lp1
.local .gb_skp1
.gb_lp1:
asl buf_zp
bne .gb_skp1
; C=1 (because the marker bit was just shifted out)
tay
jsr dc_get_byte
rol
sta buf_zp
tya
.gb_skp1:
rol
bcs .gb_lp1
.endmacro
;******
;* get bits max16 macro
.macro get_bits_max16
.local .gb_lp1
.local .gb_skp1
.gb_lp1:
asl buf_zp
bne .gb_skp1
; C=1 (because the marker bit was just shifted out)
pha
jsr dc_get_byte
rol
sta buf_zp
pla
.gb_skp1:
rol
rol hibits_zp
dey
bne .gb_lp1 ; C=0 for all Y!=0
.endmacro
;**************************************************************************
;*
;* NAME decrunch
;*
;******
decrunch:
.if MEM_DECOMP_TO_API
; Get endm_zp, dest_zp, and buf_zp
dc_lp00:
jsr dc_get_byte
sta endm_zp
jsr dc_get_byte
storedadrh:
sta dest_zp+1
jsr dc_get_byte
storedadrl:
sta dest_zp+0
jsr dc_get_byte
sta buf_zp
ldx #0
.else
ldx #4
dc_lp00:
jsr dc_get_byte
sta buf_zp-1,x
dex
bne dc_lp00
.endif
; X = 0
.if LOADCOMPD_TO
clc
lda loadaddroffslo
adc dest_zp+0
sta dest_zp+0
lda loadaddroffshi
adc dest_zp+1
sta dest_zp+1
.endif
; ldx #0
dc_lp01:
;******
;* get 4 bits
lda #%11100000
dcg_lp1:
asl buf_zp
bne dcg_skp1
; C=1 (because the marker bit was just shifted out)
tay
jsr dc_get_byte
rol
sta buf_zp
tya
dcg_skp1:
rol
bcs dcg_lp1
; Acc = 4 bits.
sta bits,x
txa
and #PART_MASK
tay
beq dc_skp01
lda #0
sta hibits_zp
ldy bits-1,x
sec
dc_lp02:
rol
rol hibits_zp
dey
bpl dc_lp02
; C = 0
; clc
adc base_l-1,x
tay
lda hibits_zp
adc base_h-1,x
dc_skp01:
sta base_h,x
tya
sta base_l,x
inx
cpx #N_PARTS*4+4
bne dc_lp01
; perform decrunch
ldy #0
; fall through
;**************************************************************************
;*
;* NAME decruncher
;*
;* DESCRIPTION
;* decruncher
;*
;******
decrunch_entry:
;******
;* single literal byte
;*
dc_literal:
.if FORWARD_DECRUNCHING
jsr dc_get_byte
; ldy #0
sta (dest_zp),y
inc dest_zp
bne dc_skp5
inc dest_zp+1
dc_skp5:
dc_common:
.else
lda dest_zp
bne dc_skp5
dec dest_zp+1
dc_skp5:
dec dest_zp
jsr dc_get_byte
; ldy #0
dc_common:
sta (dest_zp),y
.endif
; fall through
decrunch_main:
;------
; perform actual decrunch
dc_lp1:
get_bit
bcs dc_literal
; get length as bits/base.
ldx #$80-N_PARTS
dc_lp2:
inx
bmi dc_skp0
get_bit
bcc dc_lp2
clc
dc_skp0:
; C = 0, Y = 0
; lda #0
tya
ldy bits_len-$80+N_PARTS-1,x
beq dcb1_skp2
get_bits_max8
dcb1_skp2:
; C = 0
adc base_len-$80+N_PARTS-1,x
sta len_zp
; C = 0
.if FORWARD_DECRUNCHING
tax
.else
;******
;* IN: len = $01..$100 (Acc = $00..$ff)
;* OUT: dest_zp = dest_zp - len, X = len-1
;*
tax
; clc
eor #$ff
adc dest_zp
sta dest_zp
bcs dc_skp22
dec dest_zp+1
dc_skp22:
.endif
; check end marker here to avoid thrashing carry earlier
cpx endm_zp
beq done
;******
;* Get selector bits depending on length.
;*
;* IN: len = $01..$100 (X = $00..$ff)
;* OUT:
;*
cpx #4
bcc dc_skp2
ldx #3
dc_skp2:
; get offset as bits/base.
lda tabb,x
get_bits_max8_masked
tax
; C = 0
lda #0
sta hibits_zp
ldy bits_offs,x
beq dcb3_skp2
get_bits_max16
dcb3_skp2:
; C = 0, Acc/hibits_zp + base_offs,x = offset - 1
.if FORWARD_DECRUNCHING
adc base_offs_l,x
bcc dcb3_skp3
inc hibits_zp
clc
dcb3_skp3:
eor #$ff
adc dest_zp
sta copy_zp
lda dest_zp+1
sbc hibits_zp
sbc base_offs_h,x
sta copy_zp+1
.else
; perform: copy_zp = Acc/hibits_zp + base_offs,x + 1 + dest_zp
; result: copy_zp = dest_zp + offset
adc base_offs_l,x
bcc dcb3_skp3
inc hibits_zp
dcb3_skp3:
sec
adc dest_zp
sta copy_zp
lda hibits_zp
adc base_offs_h,x
; C = 0
adc dest_zp+1
sta copy_zp+1
.endif
.if FORWARD_DECRUNCHING
copy:
.if 1
; this is shorter
ldx len_zp
inx
ldy #$ff
dc_lp4:
iny
lda (copy_zp),y
sta (dest_zp),y
dex
bne dc_lp4
.else
; and this might be faster on average, as there are many 1-byte sequences
ldy len_zp
beq dc_skp4
ldx len_zp
ldy #$00
dc_lp4:
lda (copy_zp),y
sta (dest_zp),y
iny
dex
bne dc_lp4
dc_skp4:
lda (copy_zp),y
sta (dest_zp),y
.endif
; C = 1
tya
adc dest_zp
sta dest_zp
bcc dc_skp22
inc dest_zp+1
dc_skp22:
ldy #$00
.else
;******
;* Reverse fast copy
;*
;* IN: len = $01..$100 (len_zp = $00..$ff), C = 0
;*
copy:
ldy len_zp
beq dc_skp4
dc_lp4:
lda (copy_zp),y
sta (dest_zp),y
dey
bne dc_lp4
dc_skp4:
lda (copy_zp),y
; sta (dest_zp),y
.endif
jmp dc_common
; bcc dc_common ; always taken
;******
;* exit out
done:
rts
.if HAVE_LONG_PARTS
tabb:
.byte %10000000 | (48 >> 2) ; 2 bits
.byte %11100000 | (0 >> 4) ; 4 bits
.byte %11100000 | (16 >> 4) ; 4 bits
.byte %11100000 | (32 >> 4) ; 4 bits
.else
tabb:
.byte %10000000 | (24 >> 2) ; 2 bits
.byte %11000000 | (0 >> 3) ; 3 bits
.byte %11000000 | (8 >> 3) ; 3 bits
.byte %11000000 | (16 >> 3) ; 3 bits
.endif
;**************************************************************************
;*
;* NAME dc_get_byte
;*
;* DESCRIPTION
;* Get byte from the packed stream.
;*
;******
dc_get_byte = getbyte
end_decruncher:
begin_tables:
;**************************************************************************
;*
;* NAME base_l, base_h, bits
;*
;* DESCRIPTION
;* Data for bits/base decoding.
;*
;******
base_l:
base_len:
.res N_PARTS,0
base_offs_l:
.res N_PARTS*3+4,0
base_h = * - N_PARTS
; .res N_PARTS,0
base_offs_h:
.res N_PARTS*3+4,0
bits:
bits_len:
.res N_PARTS,0
bits_offs:
.res N_PARTS*3+4,0
end_tables:
; eof

View file

@ -0,0 +1,190 @@
decompress = decrunch
decompsrc = sp
TC_BLOCK_INTERFACE = 1
.if (PLATFORM = diskio::platform::COMMODORE_64) | (PLATFORM = diskio::platform::COMMODORE_128)
USE_UNINTENDED_OPCODES = 1
.else
USE_UNINTENDED_OPCODES = 0
.endif
;.export decrunch
.if USE_UNINTENDED_OPCODES
.define sbx axs
.endif
.if MEM_DECOMP_TO_API
dp=decdestlo
.else
dp=DECOMPVARS + 0 ;4
.endif
sp=DECOMPVARS + 2 ;6 ; sp must follow dp, cf init code
cs=DECOMPVARS + 4 ;8
decrunch:
.if TC_BLOCK_INTERFACE = 0
stx sp+1
.endif
ldy#2
init_loop:
.if USE_UNINTENDED_OPCODES
stx sp-2,y ;first iter stores sp-low :D
.else
sta sp-2,y ;first iter stores sp-low :D
.endif
.if TC_BLOCK_INTERFACE
; read three blocks ahead,
; - one because literal strings read up to 128 bytes past sp
; - two more to absorb up to 256 blocks worth of read 254 bytes/use 256 bytes
tya
pha
jsr tc_getblock
pla
tay
.endif
.if USE_UNINTENDED_OPCODES
lax(sp),y
.else
lda(sp),y
.endif
dey
bpl init_loop
pha
.if MEM_DECOMP_TO_API
storedadrl = * + 1
storedadrh = * + 1
lda #0
cmp #OPC_STA_ZP
beq :+
lda dp; override destination address
bne *+4
dec dp+1
dec dp
jmp :++
: lda sp-2; destination address as stored in header
sta dp
lda sp-1
sta dp+1
:
.endif
.if LOADCOMPD_TO
clc
lda loadaddroffslo
adc dp
sta dp
lda loadaddroffshi
adc dp+1
sta dp+1
.endif
lda#$02
bne update_sp
literal_run:
literal_loop:
iny
lda(sp),y
sta(dp),y
dex
bmi literal_loop
tya
pha
clc
increase_dp_by_a_and_sp_by_tos_plus_one:
adc dp
sta dp
bcc :+
inc dp+1
:
pla
update_sp:
sec
adc sp
sta sp
bcc :+
inc sp+1
.if TC_BLOCK_INTERFACE
jsr tc_getblock
.endif
:
next_command:
POLLBLOCK
ldy#0
.if USE_UNINTENDED_OPCODES
lax(sp),y
.else
lda(sp),y
tax
.endif
beq decrunch_done
; literal: x = 128+length-1
; near copy: a = %11xxxxxx
; far copy: a|0xf8 = >(~(offset-1)), x = 8*(length-2) | (some low bits)
asl
bcc far_copy
bpl literal_run
near_copy:
ldx#$07 ; clear high byte of -ve offset. Also ensures copy_loop doesn't loop.
.byt $f0 ; beq (not taken) to skip over the iny
far_copy:
iny
; carry is set for near_copy, clear for far_copy
lda(sp),y ;fetch second byte (or for near copy, refetch first). This is low 8 bits of offset.
adc dp
sta cs
txa
ora#$f8
adc dp+1
sta cs+1
.if USE_UNINTENDED_OPCODES = 0
txa
lsr
lsr
lsr
tax
.endif
tya
pha ; save opcode length to stack
ldy#1
lda(cs),y
sta(dp),y
copy_loop:
iny
lda(cs),y
sta(dp),y
.if USE_UNINTENDED_OPCODES
txa ; spend an extra 2 cycles per byte here to save 10 in the bitfield extraction. A win on average
sbx#8
bpl copy_loop
.else
dex
bpl copy_loop
clc
.endif
tya
bcc increase_dp_by_a_and_sp_by_tos_plus_one ; always taken.
decrunch_done:
pla
iny
sta(dp),y
rts
.if TC_BLOCK_INTERFACE
tc_getblock:
GETBLOCK sp+1
rts
.endif
edecrunch:

View file

@ -0,0 +1,392 @@
decompsrc = tsget
decompress = tsdecrunch
.feature c_comments, leading_dot_in_identifiers
.define .label
/*
decrunch_extreme.asm
NMOS 6502 decompressor for data stored in TSCrunch format.
Copyright Antonio Savona 2022.
*/
.define INPLACE 1 ; Enables inplace decrunching. Use -i switch when crunching.
.label tsget = DECOMPVARS + 0 ; 2 bytes
.label tstemp = DECOMPVARS + 2
.label tsput = decdestlo ; 2 bytes
.label lzput = DECOMPVARS + 3 ; 2 bytes
.if INPLACE
.macro TS_DECRUNCH src
lda #<src
sta.zp tsget
lda #>src
sta.zp tsget + 1
jsr tsdecrunch
.endmacro
.else
.macro TS_DECRUNCH(src,dst)
{
lda #<src
sta.zp tsget
lda #>src
sta.zp tsget + 1
lda #<dst
sta.zp tsput
lda #>dst
sta.zp tsput + 1
jsr tsdecrunch
}
.endif
tsdecrunch:
jsr getfirstblock
decrunch:
.if INPLACE
;ldy #0
dey
: iny
sta optRun + 1
.if PREFER_SPEED_OVER_SIZE
ldx #$d0 ; bne opcode
and #1
bne skp
ldx #$29 ; and immediate opcode
skp:
stx optOdd
.endif
.if MEM_DECOMP_TO_API
storedadrl = * + 1
storedadrh = * + 1
lda #0
cmp #OPC_LDA_ZP
lda (tsget),y
bcs :+
sta tsput , y ; last iteration trashes lzput, with no effect.
:
cpy #3
bne :--
.else
lda (tsget),y
sta tsput , y ; last iteration trashes lzput, with no effect.
cpy #3
bne :-
.endif
pha
.if LOADCOMPD_TO
clc
lda loadaddroffslo
adc tsput
sta tsput
lda loadaddroffshi
adc tsput + 1
sta tsput + 1
sec
.endif
tya
ldy #0
beq update_getonly
.else
ldy #0
lda (tsget),y
sta optRun + 1
ldx #$d0 //bne opcode
and #1
bne !skp+
ldx #$29 //and immediate opcode
!skp:
stx optOdd
inc tsget
bne entry2
inc tsget + 1
.endif
entry2:
POLLBLOCK
lax (tsget),y
bmi rleorlz
cmp #$20
bcs lz2
; literal
.if INPLACE
inc tsget
beq updatelit_hi
return_from_updatelit:
and #1
bne odd_lit
ts_delit_loop:
lda (tsget),y
sta (tsput),y
iny
dex
odd_lit:
lda (tsget),y
sta (tsput),y
iny
dex
bne ts_delit_loop
tya
tax
; carry is clear
ldy #0
.else ; not inplace
tay
and #1
bne !odd+
ts_delit_loop:
lda (tsget),y
dey
sta (tsput),y
!odd:
lda (tsget),y
dey
sta (tsput),y
bne ts_delit_loop
txa
inx
.endif
updatezp_noclc:
adc tsput
sta tsput
bcs updateput_hi
putnoof:
txa
update_getonly:
adc tsget
sta tsget
bcc entry2
jsr getblock
bcc entry2
.if INPLACE
updatelit_hi:
pha
jsr getblock
pla
tax
bcc return_from_updatelit
.endif
updateput_hi:
inc tsput+1
clc
bcc putnoof
; LZ2
lz2:
beq done
ora #$80
adc tsput
sta lzput
lda tsput + 1
sbc #$00
sta lzput + 1
; y already zero
lda (lzput),y
sta (tsput),y
iny
lda (lzput),y
sta (tsput),y
.if PREFER_SPEED_OVER_SIZE
tya
dey
adc tsput
sta tsput
bcs lz2_put_hi
skp_lz2:
inc tsget
bne entry2
jsr getblock
bcc entry2
lz2_put_hi:
inc tsput + 1
bcs skp_lz2
.else
tya ; y = a = 1.
tax ; y = a = x = 1. a + carry = 2
dey ; ldy #0
beq updatezp_noclc
.endif
rleorlz:
alr #$7f
bcc ts_delz
; RLE
beq zeroRun
plain:
ldx #2
iny
sta tstemp ; number of bytes to de-rle
lsr ; c = test parity
lda (tsget),y ; fetch rle byte
ldy tstemp
runStart:
sta (tsput),y
bcs odd_rle
sec
ts_derle_loop:
dey
sta (tsput),y
odd_rle:
dey
sta (tsput),y
bne ts_derle_loop
; update zero page with a = runlen, x = 2 , y = 0
lda tstemp
bcs updatezp_noclc
done:
.if INPLACE
pla
sta (tsput),y
.endif
rts
; LZ
ts_delz:
lsr
sta lzto + 1
iny
lda tsput
bcc long
sbc (tsget),y
sta lzput
lda tsput+1
sbc #$00
ldx #2
; lz MUST decrunch forward
lz_put:
sta lzput+1
ldy #0
lda lzto + 1
lsr
bcs odd_lz
lda (lzput),y
sta (tsput),y
ts_delz_loop:
iny
odd_lz:
lda (lzput),y
sta (tsput),y
iny
lda (lzput),y
sta (tsput),y
lzto: cpy #0
bne ts_delz_loop
tya
; update zero page with a = runlen, x = 2, y = 0
ldy #0
; clc not needed as we have len - 1 in A (from the encoder) and C = 1
jmp updatezp_noclc
zeroRun:
optRun: ldy #255
.if PREFER_SPEED_OVER_SIZE
sta (tsput),y
optOdd: bne odd_zero
ts_dezero_loop:
dey
sta (tsput),y
odd_zero:
dey
sta (tsput),y
bne ts_dezero_loop
lda optRun + 1
ldx #1
jmp updatezp_noclc
.else
sty tstemp
tya
alr #$01
ldx #1
bne runStart
.endif
long:
; carry is clear and compensated for from the encoder
adc (tsget),y
sta lzput
iny
lax (tsget),y
ora #$80
adc tsput + 1
cpx #$80
rol lzto + 1
ldx #3
bne lz_put
getblock:
inc tsget + 1
getfirstblock:
GETBLOCK tsget + 1
clc
rts

View file

@ -0,0 +1,672 @@
;
; (c) Copyright 2021 by Tobias Bindhammer. All rights reserved.
;
; Redistribution and use in source and binary forms, with or without
; modification, are permitted provided that the following conditions are met:
; * Redistributions of source code must retain the above copyright
; notice, this list of conditions and the following disclaimer.
; * Redistributions in binary form must reproduce the above copyright
; notice, this list of conditions and the following disclaimer in the
; documentation and/or other materials provided with the distribution.
; * The name of its author may not be used to endorse or promote products
; derived from this software without specific prior written permission.
;
; THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
; ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
; WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
; DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
; DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
; (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
; ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
; (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
; SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
;
.FEATURE labels_without_colons, leading_dot_in_identifiers
.if MEM_DECOMP_TO_API
; cannot perform src == dst check when not depacking in-place,
; as offsets (arbitrary src - original src) or (arbitrary dst - original dst) are not known,
; also cannot copy remaining uncompressed blob of unknown size
.error "***** MEM_DECOMP_TO_API is not supported for ZX0. Copy compressed data to original location, then use MEM_DECOMP_API to decompress in-place. *****"
.endif
.if PREFER_SPEED_OVER_SIZE
; faster but bigger decruncher
decompsrc = lz_src
lz_bits = BLOCKINDEX + 1
lz_src = DECOMPVARS + 0
lz_dst = decdestlo
lz_len_hi = DECOMPVARS + 4
; USE_DALI is defined in resident.s
.if USE_DALI
.define set_lz_bit_marker ror
.define get_lz_bit lsr <lz_bits
.else
.define set_lz_bit_marker rol
.define get_lz_bit asl <lz_bits
.endif
.define inc_src_ptr jsr lz_next_page ;sets X = 0, so all sane
;------------------
;ELIAS FETCH
;------------------
.lz_refill_bits
tax ;save bits fetched so far
lda (lz_src),y ;fetch another lz_bits byte from stream
set_lz_bit_marker
sta <lz_bits
inc <lz_src + 0 ;postponed pointer increment, so no need to save A on next_page call
beq .lz_inc_src2
.lz_inc_src2_
txa ;restore fetched bits, also postponed, so A can be trashed on lz_inc_src above
bcs .lz_lend ;last bit fetched?
.lz_get_loop
get_lz_bit ;fetch payload bit
.lz_length_16_
rol ;shift in new payload bit
bcs .lz_length_16 ;first 1 drops out from lowbyte, need to extend to 16 bit, unfortunatedly this does not work with inverted numbers
get_lz_bit ;fetch control bit
bcc .lz_get_loop ;need more bits
beq .lz_refill_bits ;buffer is empty, fetch new bits
.lz_lend ;was the last bit
rts
.lz_length_16 ;this routine happens very rarely, so we can waste cycles
pha ;save so far received lobyte
tya ;was lda #$01, but A = 0 + upcoming rol makes this also start with A = 1
jsr .lz_length_16_ ;get up to 7 more bits
sta <lz_len_hi ;and save hibyte
pla ;restore lobyte
; bne .lz_not_zero ;was the lobyte zero?
; pla ;pull lowbyte of last jsr-call from stack
; pha ;restore
; ;eor #<.lz_jsr_addr
; bmi + ;eof and match case, no need to decrement lz_len_hi
; dec <lz_len_hi ;decrement lz_len_hi
;+
; tya
bne .lz_not_zero ;was the lobyte zero?
dec <lz_len_hi ;yes, so decrement hibyte beforehand to avoid expensive checks later on, except for one case
tya ;keep Z = 0, (but not needed in case of eof, the dec also results in 0 there)
.lz_not_zero
rts
;------------------
;DECOMP INIT
;------------------
decompress
;copy over end_pos and lz_dst from stream
; ldy #$00 ;needs to be set in any case, also plain decomp enters here
ldx #$02
.if USE_DALI
stx <lz_bits
.endif
:
lda (lz_src),y
sta <lz_dst,x
inc <lz_src
jsr .lz_init
dex
bpl :-
sty .lz_offset_lo + 1 ;initialize offset with $0000
sty .lz_offset_hi + 1
sty <lz_len_hi ;reset len - XXX TODO could also be cleared upon installer, as the depacker leaves that value clean again
.if LOADCOMPD_TO
clc
lda loadaddroffslo
adc <lz_dst
sta <lz_dst
lda loadaddroffshi
adc <lz_dst + 1
sta <lz_dst + 1
jmp .lz_poll
.else
bmi .lz_poll; jmp
.endif
;------------------
;SELDOM STUFF
;------------------
.lz_inc_src2
inc_src_ptr
bne .lz_inc_src2_
.lz_inc_src3
inc_src_ptr
bcs .lz_inc_src3_
.lz_dst_inc
inc <lz_dst + 1
bcs .lz_dst_inc_
;------------------
;SELDOM STUFF
;------------------
.lz_clc
clc
bcc .lz_clc_back
.lz_cp_page
dec <lz_len_hi
txa ;much shorter this way. if we recalculate m_src and dst, endcheck also hits in if we end with an multipage match, else maybe buggy?
beq .lz_l_page ;if entered from a literal, x == 0
tya ;if entered from a match, x is anything between $01 and $ff due to inx stx <lz_dst + 1, except if we would depack to zp?
bcs .lz_m_page ;as Y = 0, we can skip the part that does Y = A xor $ff
;------------------
;POLLING
;------------------
.lz_poll
.lz_start_over
POLLBLOCK
;------------------
;ENTRY POINT DEPACKER
;------------------
lda #$01 ;we fall through this check on entry and start with literal
get_lz_bit
bcs .lz_match ;after each match check for another match or literal?
;------------------
;LITERAL
;------------------
.lz_literal
get_lz_bit
bcs :++
:
get_lz_bit ;fetch payload bit
rol ;can also moved to front and executed once on start
get_lz_bit ;fetch payload bit
bcc :-
:
bne :+
.lz_start_depack
jsr .lz_refill_bits
:
tax
.lz_l_page
.lz_cp_lit ;XXX TODO copy with Y += 1 but have lz_src + 0 eor #$ff in x and countdown x, so that lz_src + 1 can be incremented in time?
lda (lz_src),y ;/!\ Need to copy this way, or we run into danger to copy from an area that is yet blocked by barrier, this totally sucks, loading in order reveals that
sta (lz_dst),y
inc <lz_src + 0
beq .lz_inc_src3
.lz_inc_src3_
inc <lz_dst + 0
beq .lz_dst_inc
.lz_dst_inc_
dex
bne .lz_cp_lit
;XXX TODO we could also just manipulate a branch and make it go to either page handling or fall through? enable branch if 16 bits are fetched and lz_len > 0? dop or bcs to disable and enable, 80 or b0
lda <lz_len_hi ;more pages to copy?
bne .lz_cp_page ;happens very seldom
;------------------
;NEW OR OLD OFFSET
;------------------
;XXX TODO fetch length first and then decide if literal, match, repeat? But brings our checks for last bit to the end? need to check then on typebit? therefore entry for fetch is straight?
;in case of type bit == 0 we can always receive length (not length - 1), can this used for an optimization? can we fetch length beforehand? and then fetch offset? would make length fetch simpler? place some other bit with offset?
rol ;was A = 0, C = 1 -> A = 1 with rol, but not if we copy literal this way
get_lz_bit
bcs .lz_match ;either match with new offset or old offset
;------------------
;REPEAT LAST OFFSET
;------------------
.lz_repeat
get_lz_bit
bcs :++
:
get_lz_bit ;fetch payload bit
rol ;can also moved to front and executed once on start
get_lz_bit ;cheaper with 2 branches, as initial branch to .lz_literal therefore is removed
bcc :-
:
bne :+
jsr .lz_refill_bits ;fetch more bits
beq .lz_m_page ;XXX TODO sec after sbc #1 is also sufficient, but slower
:
sbc #$01 ;subtract 1, will be added again on adc as C = 1
.lz_match_big ;we enter with length - 1 here from normal match
eor #$ff
tay
.lz_m_page
eor #$ff ;restore A
adc <lz_dst + 0 ;add length
sta <lz_dst + 0
bcs .lz_clc ;/!\ branch happens very seldom, if so, clear carry
dec <lz_dst + 1 ;subtract one more in this case
.lz_clc_back
.lz_offset_lo sbc #$00 ;carry is cleared, subtract (offset + 1)
sta .lz_msrcr + 0
lax <lz_dst + 1
.lz_offset_hi sbc #$00
sta .lz_msrcr + 1
.lz_cp_match ;XXX TODO if repeated offset: add literal size to .lz_msrcr and done?
.lz_msrcr = * + 1
lda $beef,y
sta (lz_dst),y
iny
bne .lz_cp_match
inx
stx <lz_dst + 1 ;cheaper to get lz_dst + 1 into x than lz_dst + 0 for upcoming compare
lda <lz_len_hi ;check for more loop runs
.if LOAD_TO_RAM_UNDER_IO | LOAD_VIA_KERNAL_FALLBACK
beq :+
jmp .lz_cp_page ;do more page runs? Yes? Fall through
:
.else
bne .lz_cp_page ;do more page runs? Yes? Fall through
.endif
.lz_check_poll
cpx <lz_src + 1 ;check for end condition when depacking inplace, lz_dst + 0 still in X
.if LOAD_TO_RAM_UNDER_IO | LOAD_VIA_KERNAL_FALLBACK
bne :+
lda <lz_dst + 0
eor <lz_src + 0
beq lz_next_page
: jmp .lz_start_over
.else
bne .lz_start_over
lda <lz_dst + 0
eor <lz_src + 0
bne .lz_start_over
beq lz_next_page
.endif
;XXX TODO, save one byte above and the beq lz_next_page can be omitted and lz_next_page copied here again
;jmp .ld_load_raw ;but should be able to skip fetch, so does not work this way
;top ;if lz_src + 1 gets incremented, the barrier check hits in even later, so at least one block is loaded, if it was $ff, we at least load the last block @ $ffxx, it must be the last block being loaded anyway
;as last block is forced, we would always wait for last block to be loaded if we enter this loop, no matter how :-)
;------------------
;MATCH
;------------------
:
get_lz_bit ;fetch payload bit
rol ;add bit to number
.lz_match
get_lz_bit ;fetch control bit
bcc :- ;not yet done, fetch more bits
bne :+ ;last bit or bitbuffer empty? fetched 1 to 4 bits now
jsr .lz_refill_bits ;refill bitbuffer
beq .lz_eof ;so offset was $100 as lowbyte is $00, only here 4-8 bits are fetched
:
sbc #$01 ;subtract 1, elias numbers range from 1..256, we need 0..255
lsr ;set bit 15 to 0 while shifting hibyte
sta .lz_offset_hi + 1 ;hibyte of offset
lda (lz_src),y ;fetch another byte directly, same as refill_bits...
ror ;and shift -> first bit for lenth is in carry, and we have %0xxxxxxx xxxxxxxx as offset
sta .lz_offset_lo + 1 ;lobyte of offset
inc <lz_src + 0 ;postponed, so no need to save A on next_page call
beq .lz_inc_src1
.lz_inc_src1_
lda #$01 ;fetch new number, start with 1
bcs .lz_match_big ;length = 1, do it the very short way
:
get_lz_bit ;fetch more bits
rol
get_lz_bit
bcc :-
bne .lz_match_big
jsr .lz_refill_bits ;fetch remaining bits
;.lz_jsr_addr = * - 1
; bcs .lz_match_big ;lobyte != 0?
bne .lz_match_big ;lobyte != 0?
;------------------
;SELDOM STUFF
;------------------
inc <lz_len_hi ;need to correct <lz_len_hi
bcs .lz_match_big ;and enter match copy loop
.lz_inc_src1
inc_src_ptr
bne .lz_inc_src1_
;------------------
;NEXT PAGE IN STREAM
;------------------
.lz_init
bne .lz_next_page_
lz_next_page
inc <lz_src + 1
.lz_next_page_
php
txa
pha
GETBLOCK <lz_src + 1
pla
tax
plp
.lz_eof
rts
;XXX TODO
;decide upon 2 bits with bit <lz_bits? bmi + bvs + bvc? bpl/bmi decides if repeat or not, bvs = length 2/check for new bits and redecide, other lengths do not need to check, this can alos be used on other occasions?
;do a jmp ($00xx) to determine branch?
.else
decompsrc = .lz_src
OFFSET_OPT = 0; +8
.lz_bits = BLOCKINDEX + 1
.lz_src = DECOMPVARS + 0
.lz_dst = decdestlo
.if OFFSET_OPT
.else
.lz_offset = DECOMPVARS + 2
.endif
.lz_len_hi = DECOMPVARS + 4
; USE_DALI is defined in resident.s
.if USE_DALI
.define rot ror
.define shf lsr
.else
.define rot rol
.define shf asl
.endif
;------------------
;SELDOM STUFF
;------------------
.lz_l_page
.lz_dcp
dec <.lz_len_hi
bcs .lz_cp_lit
sec
bcs .lz_match_big
;------------------
;POINTER HANDLING LITERAL COPY
;------------------
.lz_dst_inc
inc <.lz_dst + 1
bcs .lz_dst_inc_
.lz_src_inc
jsr .lz_next_page ;sets X = 0, so all sane
bcs .lz_src_inc_
decompress
;copy over end_pos and lz_dst from stream
ldx #2
.if USE_DALI
stx <.lz_bits
.endif
:
lda (.lz_src),y ;copy over first two bytes
sta <.lz_dst,x
inc <.lz_src + 0
jsr .lz_init
.if OFFSET_OPT
.else
sty .lz_offset,x; reset offset and length-hi
.endif
dex
bpl :-
.if LOADCOMPD_TO
clc
lda loadaddroffslo
adc <.lz_dst
sta <.lz_dst
lda loadaddroffshi
adc <.lz_dst + 1
sta <.lz_dst + 1
.endif
.if OFFSET_OPT
sty .lz_offset_lo + 1 ;initialize offset with $0000
sty .lz_offset_hi + 1
sty <.lz_len_hi ;reset len - XXX TODO could also be cleared upon installer, as the depacker leaves that value clean again
.endif
;start with an empty lz_bits, first shf <.lz_bits leads to literal this way and bits are refilled upon next shift
.if USE_DALI
.elseif LOAD_VIA_KERNAL_FALLBACK | MEM_DECOMP_API
lda #$40
sta <.lz_bits
.endif
;------------------
;POLLING
;------------------
.lz_start_over POLLBLOCK
;------------------
;LITERAL
;------------------
lda #$01 ;we fall through this check on entry and start with literal
shf <.lz_bits
bcs .lz_match ;after each match check for another match or literal?
.lz_literal
jsr .lz_length
tax
beq .lz_l_page ;happens very seldom, so let's do that with lz_l_page that also decrements lz_len_hi, it returns on c = 1, what is always true after jsr .lz_length
.lz_cp_lit
lda (.lz_src),y ;Need to copy this way, or wie copy from area that is blocked by barrier
sta (.lz_dst),y
inc <.lz_src + 0
beq .lz_src_inc
.lz_src_inc_
inc <.lz_dst + 0
beq .lz_dst_inc
.lz_dst_inc_
dex
bne .lz_cp_lit
lda <.lz_len_hi ;more pages to copy?
bne .lz_l_page ;happens very seldom
;------------------
;NEW OR OLD OFFSET
;------------------
;in case of type bit == 0 we can always receive length (not length - 1), can this used for an optimization? can we fetch length beforehand? and then fetch offset? would make length fetch simpler? place some other bit with offset?
rol ;A = 0, C = 1 -> A = 1
shf <.lz_bits
;rol
;bne .lz_match
;else A = 0
;but only for lowbyte?!
bcs .lz_match ;either match with new offset or old offset
;------------------
;DO MATCH
;------------------
.lz_repeat
jsr .lz_length
sbc #$01
bcc .lz_dcp ;fix highbyte of length in case and set carry again (a = $ff -> compare delivers carry = 1)
;sec ;XXX TODO in fact we could save on the sbc #$01 as the sec and adc later on corrects that again, but y would turn out one too less
.lz_match_big ;we enter with length - 1 here from normal match
eor #$ff
tay
;XXX TODO save on eor #$ff and do sbc lz_dst + 0?
eor #$ff ;restore A
.lz_match_len2 ;entry from new_offset handling
adc <.lz_dst + 0
sta <.lz_dst + 0
tax ;remember for later end check, cheaper this way
bcs .lz_clc ;/!\ branch happens very seldom, if so, clear carry
dec <.lz_dst + 1 ;subtract one more in this case
.lz_clc_back
.if OFFSET_OPT
.lz_offset_lo sbc #$00 ;carry is cleared, subtract (offset + 1) in fact we could use sbx here, but would not respect carry, but a and x are same, but need x later anyway for other purpose
.else
sbc <.lz_offset
.endif
sta .lz_msrcr + 0
lda <.lz_dst + 1
.if OFFSET_OPT
.lz_offset_hi sbc #$00
.else
sbc <.lz_offset + 1
.endif
sta .lz_msrcr + 1
; ;XXX TODO would have dst + 0 and + 1 in X and A here, of any use?
.lz_cp_match
;XXX TODO if repeated offset: add literal size to .lz_msrcr and done?
.lz_msrcr = * + 1
lda $beef,y
sta (.lz_dst),y
iny
bne .lz_cp_match
inc <.lz_dst + 1
lda <.lz_len_hi ;check for more loop runs
bne .lz_m_page ;do more page runs? Yes? Fall through
.lz_check_poll
cpx <.lz_src + 0 ;check for end condition when depacking inplace, .lz_dst + 0 still in X
.lz_skip_poll bne .lz_start_over ;-> can be changed to .lz_poll, depending on decomp/loadcomp
lda <.lz_dst + 1
sbc <.lz_src + 1
bne .lz_start_over
;jmp .ld_load_raw ;but should be able to skip fetch, so does not work this way
;top ;if lz_src + 1 gets incremented, the barrier check hits in even later, so at least one block is loaded, if it was $ff, we at least load the last block @ $ffxx, it must be the last block being loaded anyway
;as last block is forced, we would always wait for last block to be loaded if we enter this loop, no matter how :-)
;------------------
;NEXT PAGE IN STREAM
;------------------
.lz_init
bne :+
.lz_next_page
inc <.lz_src + 1
:
.if LOAD_TO_RAM_UNDER_IO
jmp getblock
.else
php
txa
pha
GETBLOCK <.lz_src + 1
pla
tax
plp
rts
.endif
;------------------
;FETCH A NEW OFFSET
;------------------
: ;lz_length as inline
shf <.lz_bits ;fetch payload bit
rol ;can also moved to front and executed once on start
.lz_match
shf <.lz_bits
bcc :-
bne :+
jsr .lz_refill_bits
:
sbc #$01 ;XXX TODO can be omitted if just endposition is checked, but 0 does not exist as value?
bcc .lz_eof ;underflow. must have been 0
lsr
.if OFFSET_OPT
sta .lz_offset_hi + 1 ;hibyte of offset
.else
sta .lz_offset + 1 ;hibyte of offset
.endif
lda (.lz_src),y ;fetch another byte directly
ror
.if OFFSET_OPT
sta .lz_offset_lo + 1
.else
sta .lz_offset
.endif
inc <.lz_src + 0 ;postponed, so no need to save A on next_page call
bne :+
jsr .lz_next_page ;preserves carry, all sane
:
lda #$01
ldy #$fe
bcs .lz_match_len2 ;length = 1 ^ $ff, do it the very short way :-)
:
shf <.lz_bits ;fetch first payload bit
;XXX TODO we could check bit 7 before further shf?
rol ;can also moved to front and executed once on start
shf <.lz_bits
bcc :-
bne .lz_match_big
ldy #$00 ;only now y = 0 is needed
jsr .lz_refill_bits ;fetch remaining bits
; bcs .lz_match_big
jmp .lz_match_big
;------------------
;SELDOM STUFF
;------------------
.lz_clc
clc
bcc .lz_clc_back
.lz_m_page
dec <.lz_len_hi
inc .lz_msrcr + 1 ;XXX TODO only needed if more pages follow
bne .lz_cp_match
;------------------
;ELIAS FETCH
;------------------
.lz_refill_bits
tax
lda (.lz_src),y
rot
sta <.lz_bits
inc <.lz_src + 0 ;postponed, so no need to save A on next_page call
bne :+ ;XXX TODO if we would prefer beq, 0,2% saving
jsr .lz_next_page ;preserves carry and A, clears X, Y, all sane
:
txa
bcs .lz_lend
;lda #$00
;slo <.lz_bits
.lz_get_loop
shf <.lz_bits ;fetch payload bit
.lz_length_16_
rol ;can also moved to front and executed once on start
bcs .lz_length_16 ;first 1 drops out from lowbyte, need to extend to 16 bit, unfortunatedly this does not work with inverted numbers
.lz_length
shf <.lz_bits
bcc .lz_get_loop
beq .lz_refill_bits
.lz_lend
.lz_eof
rts
.lz_length_16 ;happens very rarely
pha ;save LSB
tya ;was lda #$01, but A = 0 + rol makes this also start with MSB = 1
jsr .lz_length_16_ ;get up to 7 more bits
sta <.lz_len_hi ;save MSB
pla ;restore LSB
rts
.if LOAD_TO_RAM_UNDER_IO
getblock php
txa
pha
GETBLOCK <.lz_src + 1
pla
tax
plp
rts
.endif
.endif

View file

@ -0,0 +1,527 @@
.ifndef _DRIVECODE_COMMON_INC_
_DRIVECODE_COMMON_INC_ = 1
MIN_DEVICE_NO = 8 ; these two settings define the device number range when scanning for devices in install.s,
MAX_DEVICE_NO = 30 ; devices beyond this range will not be recognized or useable
; 1541/41-C/41-II/41U/70/71/71CR
MINSTEPSPEED = $18 ; min. R/W head stepping speed
MAXSTEPSPEED = $10 ; max. R/W head stepping speed
STEPPERACC = $0c ; R/W head stepping acceleration, smaller is slower acceleration
SINGLESTEPSPEED = $0c ; time between two consecutive half-track steps for a single-track step
.macro ENABLE_WATCHDOG
.if ::DISABLE_WATCHDOG
sei
.else
cli
.endif
.endmacro
.macro DRIVEGETBYTE drivetype, comparelbl, nozeroes
.if .not .xmatch (drivetype, 1581)
.local OFFSET
.local getbit
; must not clobber x
.if .paramcount < 3
OFFSET = 0
ldy VIA1_PRB + OFFSET
.else
OFFSET = 16
.endif
lda #%10000000
getbit: cpy VIA1_PRB + OFFSET
beq getbit
ldy VIA1_PRB + OFFSET
comparelbl: cpy #ATN_IN | ATNA_OUT | CLK_IN
ror
bcc getbit
.assert .hibyte(*) = .hibyte(getbit), error, "***** Page boundary crossing in getbyte loop, fatal cycle loss. *****"
.else
.local getbit
.local waitbit
lda #%10000000
getbit: pha
lda CIA_PRB
waitbit: cmp CIA_PRB
beq waitbit
lda CIA_PRB
and #CLK_IN
cmp #CLK_IN
pla
ror
bcc getbit
.assert .hibyte(*) = .hibyte(getbit), error, "***** Page boundary crossing in getbyte loop, fatal cycle loss. *****"
.endif
.endmacro
.macro DRIVESENDBYTE drivetype, sendvalue
.local sendbit
.local waitbit
sta sendvalue
ldy #8
sendbit: asl sendvalue
lda #0
.if .not .xmatch (drivetype, 1581)
adc #ATNA_OUT | DATA_IN
sta VIA1_PRB
lda VIA1_PRB
waitbit: cmp VIA1_PRB
.else
adc #DATA_IN
sta CIA_PRB
lda CIA_PRB
waitbit: cmp CIA_PRB
.endif
beq waitbit
dey
bne sendbit
.endmacro
.macro GET_FILENAME drivetype
.if .not .xmatch (drivetype, 1581)
;ldx #0
.if .xmatch (drivetype, 1571)
lda #ATNA_OUT; ATNA_OUT set, CLK_OUT and DATA_OUT clear: drive is ready
sta (V1B,x); VIA1_PRB
.endif
getfilenam:
.if .xmatch (drivetype, 1541)
inx
getbytewdog41 = getbytewdg
getbyterts41 = getbyterts
getbytewdg: lda #$f5
sta VIA1_PRB; ATNA_OUT set, CLK_OUT and DATA_OUT clear: drive is ready
sta TIMER; reset watchdog time-out
ENABLE_WATCHDOG
getbyte41 = getbyte
getbyte: DRIVEGETBYTE 1541, getbytecmp41
getbyterts: sta a:FILENAME - 1,x
.else
inx
jsr getbytewdg
sta .lobyte(FILENAME - 1),x
.endif
.else; 1581
ldx #0
stx CIA_PRB; CLK_OUT and DATA_OUT clear: drive is ready
getfilenam: inx
jsr getbyte
sta filename - 1,x
.endif
bne getfilenam
sei; disable watchdog
cpx #16 + 3
.if .xmatch (drivetype, 1541)
bcs getcustom
.else
bcc :+
jmp getcustom
:
.endif
.if .not .xmatch (drivetype, 1581)
.if .xmatch (drivetype, 1541)
dec VIA1_PRB; %1xy10000 -> %1xy01111: drive busy, set CLK_OUT and DATA_OUT
.else
lda #ERR | ATNA_OUT | CLK_OUT | DATA_OUT; drive busy
sta VIA1_PRB
sta ERRORCOUNT; reset, as spinning up the motor would yield errors
.endif
.else
lda #CLK_OUT | DATA_OUT; drive busy
sta CIA_PRB
.endif
.if .xmatch (drivetype, 1571)
inc prpnxtfjmp; OPC_JMP_ABS -> OPC_EOR_ABS, disable block caching: do not jump to prpdnxtfil (idleloop) after fetching the file's first block
.endif
: dex ; if zero-length filename,
beq loadnextfile ; use hash values of next file
cpx #FILENAME_MAXLENGTH + 1
bcs :-
findfile: sec
jsr gethashval
.if .not .xmatch (drivetype, 1581)
setnextfile:
.endif
sta FILENAMEHASHLO
sty FILENAMEHASHHI
loadnextfile:
.endmacro; GET_FILENAME drivetype
; matches against hash of filename in FILENAMEHASHLO/HI
.macro FIND_FILE drivetype
.if .not .xmatch (drivetype, 1581)
chknewdisk:
.if .xmatch (drivetype, 1541)
lda #$fc
sta CYCLESTARTENDSECTOR
ldx NUMFILES
bpl findloop
; a new disk has been inserted
sta DIRBLOCKPOS
jsr getblkbam; store ID, sector link sanity check
bcc chknewdisk
jsr checkchg
lda #1; first dir sector
.else
lda #0; BAM sector
sta CYCLESTARTENDSECTOR
ldx NUMFILES
bpl findloop
; a new disk has been inserted
;lda #0; BAM sector
sta DIRBLOCKPOS
ldx #.lobyte(storeid - (idswitch + 2))
jsr getblkstid; store ID, sector link sanity check
bcs chknewdisk
jsr checkchg
lda #1; first dir sector
; set seek boundaries according to number of disk sides
ldx #MAXTRACK71 + 1
ldy #NUMTRACKS_A
bit BLOCKBUFFER71 + TWOSIDEDOFFSET
bmi :+
ldx #NUMTRACKS_SINGLESIDED + 1
ldy #NUMTRACKS_SINGLESIDED
: stx MAXTRACK
sty MAXTRACK_A
.endif
;lda LINKSECTOR
.else; 1581
inc prpnxtfjmp; OPC_JMP_ABS -> OPC_EOR_ABS, do not jump to after prepare-next-file routine
prpnxtfile: lda NUMFILES
bmi newdisk
diskchangd: lda #$00
bne newdisk
jmp samedisk
newdisk: ; a new disk has been inserted
jsr getdirtrk
ldy #$00
sty diskchangd + 1
sty CYCLESTARTENDTRACK
sty CYCLESTARTENDSECTOR
jsr getblock81
bcs newdisk
;lda LINKTRACK
;ldx LOADEDSECTOR; $00
;ldy LINKSECTOR
sty FIRSTDIRSECTOR
.endif; 1581
; directory cycling: fill the directory buffer with the next file entries,
; this is also executed upon file not found in the currently buffered directory segment
; (with accu containing NEXTDIRBLOCKSECTOR's value)
nextdirseg:
.if .not .xmatch (drivetype, 1581)
ldy #$ff
.else
ldx #$ff
.endif
; file not found (yet)
.if .xmatch (drivetype, 1541)
ldx CYCLESTARTENDSECTOR
beq filenotfnd; branch if cycle complete with z = 0 and y = $ff = diskio::status::FILE_NOT_FOUND
.else; !1541
bit CYCLESTARTENDSECTOR
bpl :+ ; branch if cycle not complete
sec
jmp filenotfnd
:
.endif; !1541
.if .not .xmatch (drivetype, 1581)
sty NUMFILES
.if .xmatch (drivetype, 1541)
nextdirsct: ; a = sector
jsr getblkdir; compare ID, sector link sanity check
bcc chknewdisk
;lda LINKSECTOR41
;ldx LOADEDSECTOR
tay
bpl :+; branch if not wrapping to first dir block
lda #1; wrap around to first dir sector
: sta NEXTDIRBLOCKSECTOR
.else; 1571
SKIPWORD; do not set CYCLESTARTENDSECTOR
dircycle: sta CYCLESTARTENDSECTOR
nextdirsct: ; a = sector
jsr getblkchid; compare ID, sector link sanity check
bcs chknewdisk
;lda LINKSECTOR
;ldy LINKTRACK
;cpy CURTRACK
beq :+; branch if not wrapping to first dir block
lda #1; wrap around to first dir sector
: sta NEXTDIRBLOCKSECTOR
.endif
.else; 1581
stx NUMFILES
nextdirsct: ; a = track
; y = sector
sta CURRDIRBLOCKTRACK
jsr getblock81
bcs newdisk; start over on error
;ldy LINKSECTOR
;lda LINKTRACK
bne :+; branch if not wrapping to first dir block
jsr getdirtrk
ldy FIRSTDIRSECTOR
: sta NEXTDIRBLOCKTRACK
sty NEXTDIRBLOCKSECTOR
ldy #0
.endif; 1581
getdirloop:
.if .xmatch (drivetype, 1541)
ldy DIRBLOCKPOS
jsr sertorawr; get file's start track
beq notafile; skip non-files denoted by track 0
cmp #MAXTRACK41 + 1
bcs notafile; or files with invalid start tracks
inc NUMFILES
ldx NUMFILES
sta DIRTRACKS,x
jsr sertorawd; get file's start sector
sta DIRSECTORS,x
lda (BLP),y; number of file blocks (low-byte)
jsr sertoraw41
sta NUMBLOCKS,x
.elseif (.xmatch (drivetype, 1571))
ldy DIRBLOCKPOS
lda BLOCKBUFFER + 3 + TRACKOFFSET,y; get file's start track
beq notafile; skip non-files denoted by track 0
cmp MAXTRACK
bcs notafile; or files with invalid start tracks
inc NUMFILES
ldx NUMFILES
sta .lobyte(DIRTRACKS),x
lda BLOCKBUFFER + 3 + SECTOROFFSET,y; get file's start sector
sta .lobyte(DIRSECTORS),x
lda BLOCKBUFFER + BLOCKSOFFSET,y
sta .lobyte(NUMBLOCKS),x
.else; 1581
lda BLOCKBUFFER + 3 + TRACKOFFSET,y; get file's start track
beq notafile; skip non-files denoted by track 0
inc NUMFILES
ldx NUMFILES
sta DIRTRACKS,x
lda BLOCKBUFFER + 3 + SECTOROFFSET,y; get file's start sector
sta DIRSECTORS,x
tya
pha
.endif; 1581
ldx #0
jsr fnamehash
ldx NUMFILES; restore x
sta FILENAMEHASHVALSLO,x
.if .not .xmatch (drivetype, 1581)
sty FILENAMEHASHVALSHI,x
.else
tya
sta FILENAMEHASHVALSHI,x
pla
tay
.endif
; advance to next file or quit loop
cpx #DIRBUFFSIZE - 1
bcs dirbuffull
notafile:
.if .xmatch (drivetype, 1541)
lax DIRBLOCKPOS
axs #$20; 8 entries per block, $20 bytes per entry
stx DIRBLOCKPOS
bcs getdirloop; process all entries in a dir block
.elseif .xmatch (drivetype, 1571)
lax DIRBLOCKPOS
axs #.lobyte(-$20); 8 entries per block, $20 bytes per entry
stx DIRBLOCKPOS
bcc getdirloop; process all entries in a dir block
.else; 1581
tya
and #%11100000; 8 entries per block, $20 bytes per entry
;clc
adc #$20
tay
bcc getdirloop; process all entries in a dir block
.endif
; process next dir block
.if .xmatch (drivetype, 1541)
lax NEXTDIRBLOCKSECTOR
tay
eor CYCLESTARTENDSECTOR
bmi :+; set CYCLESTARTENDSECTOR
bne :++
; cycle complete
tay
: sty CYCLESTARTENDSECTOR
: txa
bpl nextdirsct; jmp
.elseif .xmatch (drivetype, 1571)
lda NEXTDIRBLOCKSECTOR
ldy CYCLESTARTENDSECTOR
beq dircycle; set CYCLESTARTENDSECTOR
cmp CYCLESTARTENDSECTOR
bne nextdirsct
; cycle complete
;sec
ror CYCLESTARTENDSECTOR
.if (DIRBUFFSIZE < 16)
; always fill up a dir buffer that can hold fewer than 2 dir blocks, because if the dir is 2 blocks
; big, the dir buffer may otherwise only hold parts of 2 blocks despite having more space
bne nextdirsct; jmp
.endif
.else; 1581
jsr getdirtrk
ldy NEXTDIRBLOCKSECTOR
ldx CYCLESTARTENDTRACK
bne :+
sta CYCLESTARTENDTRACK
sty CYCLESTARTENDSECTOR
beq nextdirsct; jmp
: cmp CYCLESTARTENDTRACK
bne nextdirsct
cpy CYCLESTARTENDSECTOR
bne nextdirsct
; cycle complete
;sec
ror CYCLESTARTENDSECTOR
.endif; 1581
dirbuffull: lda LOADEDSECTOR
sta NEXTDIRBLOCKSECTOR
.if .not .xmatch (drivetype, 1581)
.if DIRBUFFSIZE < 16
;ldx NUMFILES; actually number of files - 1
.else
ldx NUMFILES; actually number of files - 1
.endif
.else
lda CURRDIRBLOCKTRACK
sta NEXTDIRBLOCKTRACK
; the disk was not changed, or the dir has just been read
samedisk: ldx NUMFILES; actually number of files - 1
.endif; 1581
findloop:
.if .not .xmatch (drivetype, 1581)
lda NEXTDIRBLOCKSECTOR
.else; !1581
lda NEXTDIRBLOCKTRACK
ldy NEXTDIRBLOCKSECTOR
.endif
dex ; skip the last file entry to keep it as an overflow entry for load-next and PREPARE_NEXT_FILE
.if .xmatch (drivetype, 1541)
stx FILEDIRBUFFERINDEX
.endif
bmi nextdirseg; if the dir buffer does not contain the file, cycle through the directory to find it
nextfile: lda FILENAMEHASHVALSLO,x
eor FILENAMEHASHLO
bne findloop
ldy FILENAMEHASHVALSHI,x
cpy FILENAMEHASHHI
bne findloop
; file found
.if .xmatch (drivetype, 1541)
;sec
.elseif (.xmatch (drivetype, 1571))
; check for illegal track or sector
;sec
stx FILEDIRBUFFERINDEX
lda .lobyte(DIRTRACKS),x
jsr getnumscts
stx TEMP
ldx FILEDIRBUFFERINDEX
ldy .lobyte(DIRTRACKS),x
lda .lobyte(DIRSECTORS),x
cmp TEMP
;bcs filenfound
filenotfnd:
.else; 1581
;lda #$00
sta CYCLESTARTENDTRACK
sta CYCLESTARTENDSECTOR
lda DIRTRACKS,x
ldy DIRSECTORS,x
clc
filenotfnd:
prpnxtfjmp: jmp prpdnxtfil; is changed to eor prpdnxtfil
dec prpnxtfjmp; OPC_EOR_ABS -> OPC_JMP_ABS
.endif; 1581
.endmacro; FIND_FILE drivetype
.macro FNAMEHASH drivetype
fnamehash:
.if .xmatch (drivetype, 1541)
jsr sertorawd
.else
lda BLOCKBUFFER + 5,y
iny
.endif
cmp #' ' | $80; $a0 = end of filename
beq gethashval
.if .not .xmatch (drivetype, 1581)
sta .lobyte(FILENAME),x
.else; 1581
sta filename,x
.endif; 1581
inx
cpx #FILENAME_MAXLENGTH
bcc fnamehash
; fall through
gethashval: txa
tay
;sec
hashloop: sta TEMP
tya
.if .not .xmatch (drivetype, 1581)
adc .lobyte(FILENAME - 1),x
.else; 1581
adc filename - 1,x
.endif; 1581
tay
adc TEMP
dex
bne hashloop
.if .xmatch (drivetype, 1541)
specdone: ;sec
ror SPECWRAPSECTOR
getcustom:
.endif
rts
.endmacro; FNAMEHASH drivetype
.endif; !_DRIVECODE_COMMON_INC_

1336
loader/src/drives/drivecode1541.s Executable file

File diff suppressed because it is too large Load diff

1336
loader/src/drives/drivecode1571.s Executable file

File diff suppressed because it is too large Load diff

903
loader/src/drives/drivecode1581.s Executable file
View file

@ -0,0 +1,903 @@
.include "cpu.inc"
.include "cia.inc"
.include "via.inc"
.include "drives/drivecode-common.inc"
ZP_FIRST = $00
BUFFER = $00
SYS_SP = $01
JOBCODESTABLE = $02; fixed in ROM
JOBTRKSCTTABLE = $0b; fixed in ROM - $0b..$1c
FILETRACK = $0b
FILESECTOR = $0c
FILENAMEHASHLO = $0d
FILENAMEHASHHI = $0e
NUMFILES = $0f
CURRDIRBLOCKTRACK = $10
CYCLESTARTENDTRACK = $11
CYCLESTARTENDSECTOR = $12
NEXTDIRBLOCKTRACK = $13
NEXTDIRBLOCKSECTOR = $14
FIRSTDIRSECTOR = $15
NEXTCONTIGUOUSBLOCK = $16
TEMP = $17
ZP_LAST = $17
;BLOCKBUFFERJOBTRACK = $1b; fixed in ROM - track for job at buffer 8 ($0b00)
;BLOCKBUFFERJOBSECTOR = $1c; fixed in ROM - sector for job at buffer 8 ($0b00)
DISKCHANGED = $25; fixed in ROM
DRIVESTATE = $26; fixed in ROM
DRIVEOFF = $00; literal
OPEN_FILE_TRACK = $4c; fixed in ROM
LEDSTATE = $79; fixed in ROM
IRQVECTOR_LO = $0192
IRQVECTOR_HI = $0193
HDRS2 = $01bc
ROMOS_DIRTRACK81 = $022b
OPEN_FILE_SECTOR = $028b
STROBE_CONTROLLER = $ff54
OK_DV = $00
READ_DV = $80
MOTOFFI_DV = $8a
SEEK_DV = $8c
RESET_TIMERB = $cb9f
CONTROLLERIRQPERIODFD = $4e20
BUFFER0 = $0300
BUFFERSIZE = $0100
SENDTABLELO = $0900
SENDTABLEHI = $0a00
BLOCKBUFFER81 = $0b00
TRACKOFFSET = 0
SECTOROFFSET = 1
REQUESTEDTRACK = JOBTRKSCTTABLE + (2 * BUFFERINDEX) + TRACKOFFSET
REQUESTEDSECTOR = JOBTRKSCTTABLE + (2 * BUFFERINDEX) + SECTOROFFSET
LOADEDSECTOR = JOBTRKSCTTABLE + (2 * BUFFERINDEX) + SECTOROFFSET
LINKTRACK = BLOCKBUFFER81 + TRACKOFFSET
LINKSECTOR = BLOCKBUFFER81 + SECTOROFFSET
BINARY_NIBBLE_MASK = %00001111
ROMOS_MAXTRACK = $8f; MAXTRACK81 - 1
ROMOS_MAXSECTOR = $75; MAXSECTOR81 + 1
MAXTRACK81 = 80; literal
MAXSECTOR81 = 39; literal
BUFFERINDEX = (BLOCKBUFFER81 - BUFFER0) / BUFFERSIZE
.org $0300
.export cmdfdfix0 : absolute
.export cmdfdfix1 : absolute
.export cmdfdfix2 : absolute
.if (::PLATFORM <> diskio::platform::COMMODORE_128) & (!::DISABLE_WATCHDOG)
.export cmdfdfix3 : absolute
.export cmdfdfix4 : absolute
.endif
drvcodebeg81: .byte .hibyte(drvcodeend81 - * + $0100 - $01); init transfer count hi-byte
sysirqvbuf: .word 0
syszpbuf: .res ZP_LAST - ZP_FIRST + 1
filename81: .res 16
.res 24; overflow area for 1541 custom code upload trampoline
swapzp81: ldx #ZP_LAST - ZP_FIRST
: lda ZP_FIRST,x
ldy syszpbuf,x
sty ZP_FIRST,x
sta syszpbuf,x
dex
bpl :-
rts
FNAMEHASH 1581
getdirtrk:
cmdfdfix1 = * + 1
cmdfdfix2 = * + 2
lda ROMOS_DIRTRACK81
.if DIRTRACK81 = 40
rts
.endif
cmp #40
bne :+
lda #DIRTRACK81
:
.if DIRTRACK81 <> 40
rts
.endif
trackseek: sta REQUESTEDTRACK
sty REQUESTEDSECTOR
tax
dex
stx HDRS2 + (2 * BUFFERINDEX)
jsr initcntr81
lda #SEEK_DV
ldx #BUFFERINDEX
jsr STROBE_CONTROLLER
lda DISKCHANGED
ora diskchangd + 1
sta diskchangd + 1
; fall through
initwdog81: ; the i-flag is set here
.if ::DISABLE_WATCHDOG
rts
.endif
lda #.lobyte(uninstall)
sta IRQVECTOR_LO
lda #.hibyte(uninstall)
sta IRQVECTOR_HI
lda cmdfdfix2; 0 for FD
beq :+
lda #$ff
sta CIA_TB_LO
sta CIA_TB_HI
:
.if !::DISABLE_WATCHDOG
rts
.endif
enablwdg81: lda cmdfdfix2; 0 for FD
beq :+
ldy #COUNT_PHI2 | FORCE_LOAD | ONE_SHOT | TIMER_START
sty CIA_CRB
bit CIA_ICR
rts
: lda #IRQ_CLEAR_FLAGS | IRQ_ALL_FLAGS
sta VIA_IER; no IRQs from VIA
lda #IRQ_SET_FLAGS | IRQ_TIMER_1
sta VIA_IER; timer 1 IRQs from VIA
ldy #$ff
sty VIA_T1C_H
rts
getblock81: sta REQUESTEDTRACK
sty REQUESTEDSECTOR
getblockag: jsr initcntr81
lda #READ_DV
ldx #BUFFERINDEX
jsr STROBE_CONTROLLER
lda DISKCHANGED
ora diskchangd + 1
sta diskchangd + 1
jsr initwdog81
lda JOBCODESTABLE + BUFFERINDEX; FD does not return the error status in the accu
cmp #OK_DV + 1
; the link track is returned last so that the z-flag
; is set if this block is the file's last one (see FIND_FILE)
ldy LINKSECTOR
ldx LOADEDSECTOR
lda LINKTRACK
rts
initcntr81: lda sysirqvbuf
sta IRQVECTOR_LO
lda sysirqvbuf + 1
sta IRQVECTOR_HI
lda cmdfdfix2; 0 for FD
beq :+
jmp RESET_TIMERB
: lda #.lobyte(CONTROLLERIRQPERIODFD)
sta VIA_T1C_L
lda #.hibyte(CONTROLLERIRQPERIODFD)
sta VIA_T1C_H
rts
fadeled: txa
tay
beq motrledoff
: nop
bit OPC_BIT_ZP
dey
bne :-
pha
jsr busyledoff
pla
tay
: nop
bit OPC_BIT_ZP
iny
bne :-
dex
bsyledon81: lda #DRIVE_LED
ora CIA_PRA
ldy #$ff
bne store_cia; jmp
motrledoff: lda DRIVESTATE
ora LEDSTATE
beq :+
txa
pha
; fill track cache
lda REQUESTEDTRACK
ldy REQUESTEDSECTOR
jsr getblock81
; turn off motor
jsr initcntr81
lda #MOTOFFI_DV
ldx #BUFFERINDEX
jsr STROBE_CONTROLLER
lda #DRIVEOFF; $00
sta DRIVESTATE
pla
tax
busyledoff: lda CIA_PRA
and #.lobyte(~DRIVE_LED); turn off drive led
ldy #$00
store_cia: sta CIA_PRA
sty LEDSTATE
: rts
uninstall: jsr getdirtrk
jsr trackseek
ldx LEDSTATE
: jsr fadeled
lda LEDSTATE
bne :-
ldx SYS_SP
txs
ldx #ZP_LAST - ZP_FIRST
: lda syszpbuf,x
sta ZP_FIRST,x
dex
bpl :-
jsr initcntr81
lda #1
sta DISKCHANGED; re-read BAM etc.
lda #CIA_SET_INTF | FLAG1_IRQ | SERIAL_IRQ | TIMERB_IRQ
sta CIA_ICR
bit CIA_ICR
cli
rts
idleloop: ldx #$00; turn off motor and busy led
lda #DRIVE_LED; check if busy led is lit
and CIA_PRA
beq driveidle
dex; fade off the busy led, then turn off motor
driveidle: jsr fadeled; fade off the busy led
lda CIA_PRB
and #ATN_IN | CLK_OUT | CLK_IN | DATA_OUT | DATA_IN
eor #ATN_IN | CLK_OUT | CLK_IN | DATA_IN
beq driveidle; wait until there is something to do
lda CIA_PRB; to be safe, read a second time
and #ATN_IN | CLK_OUT | CLK_IN | DATA_OUT | DATA_IN
eor #ATN_IN | CLK_OUT | CLK_IN | DATA_IN
lsr; check for reset, uninstallation or custom drive code upload
beq :+
jmp uninstall; check for reset or uninstallation
: jsr enablwdg81; enable watchdog, the computer might be reset while sending over a
; byte, leaving the drive waiting for handshake pulses
ENABLE_WATCHDOG
filename = filename81
GET_FILENAME 1581
; matches against hash of filename in FILENAMEHASHLO/HI
BLOCKBUFFER = BLOCKBUFFER81
findfile81 = findfile
FIND_FILE 1581
loadfile81:
bcs filenfound
lda FILENAMEHASHVALSLO + 1,x ; for load-next
sta FILENAMEHASHLO ; functionality,
lda FILENAMEHASHVALSHI + 1,x ; store hash of
sta FILENAMEHASHHI ; next file's name
lda DIRTRACKS,x
tax
; check for illegal track or sector
beq toillegal
cpx ROMOS_MAXTRACK; #MAXTRACK81 - 1
beq :+
bcs toillegal + 1
: cpy ROMOS_MAXSECTOR; #MAXSECTOR81 + 1
bcc :+
toillegal: sec
cmdfdfix0: jmp illegalts; is changed to bit illegalts on FD2000/4000 to disable illegal track or sector error,
; ROM variables for logical track/sector boundaries aren't known (probably around MAXTRACKFD = $54)
: tya
pha
jsr bsyledon81
pla
tay; FILESECTOR
txa; FILETRACK
ldx #1
stx NEXTCONTIGUOUSBLOCK
loadblock: sta REQUESTEDTRACK
sty REQUESTEDSECTOR
: jsr getblockag
bcs :-
;ldy LINKSECTOR
;lda LINKTRACK
pha
beq :+
ldy #$ff
: lda #1
ldx #0
cpx LINKTRACK
rol; last block: set lsb, clear lsb otherwise
tax
lsr
lda LINKSECTOR; the file's last block's length (last byte index)
pha
sty blocksize + 1
dey
tya
eor #$ff
bcs :+
lda NEXTCONTIGUOUSBLOCK
: stx BLOCKBUFFER81 + 1; block index and last block size/next contiguous block index flag
; accu: next contiguous block index/block size
jsr sendblock; send the block over
inc NEXTCONTIGUOUSBLOCK
pla; LINKSECTOR
tay
pla; LINKTRACK
bcs fileclosed
bne loadblock
; loading is finished
clc; all ok after loading
filenfound: ; branches here with carry set
illegalts: ; or illegal t or s
jsr sendstatus
bcs fileclosed
: lda CIA_PRB
and #ATN_IN | CLK_OUT | CLK_IN | DATA_OUT | DATA_IN
cmp #ATN_IN | CLK_OUT | CLK_IN | DATA_IN
bne :-; wait until host is in idle mode
lda BLOCKBUFFER81; offset 0: status byte
bmi :+; only after successful load
jmp prpnxtfile; jumps to prpdnxtfil
prpdnxtfil: ; always succeeds, trackseek to track in y is valid
trseekidle: jsr trackseek
: jmp idleloop
fileclosed: inc BLOCKBUFFER81
beq :+; branch if file not found: DATA_OUT remains set, clear it otherwise
lda #0
sta CIA_PRB
: bit CIA_PRB; wait for ATN_IN set,
bpl :- ; acknowledgement of the block transfer
lda #CLK_OUT; drive busy
sta CIA_PRB
jmp idleloop
inittimers: lda cmdfdfix2; 0 for FD
beq :+
.if ::PLATFORM = diskio::platform::COMMODORE_128
lda #5; set burst timer, anything below 5 yields transfer errors
sta CIA_TA_LO
lda #0
sta CIA_TA_HI
lda #IOMODE_OUTPUT | COUNT_PHI2 | FORCE_LOAD | CONTINUOUS | TIMER_START; enable burst mode
sta CIA_CRA
.endif; ::PLATFORM = diskio::platform::COMMODORE_128
.if !::DISABLE_WATCHDOG
; watchdog initialisation
jsr initwdog81
lda #CIA_CLR_INTF | EVERY_IRQ
sta CIA_ICR
lda #CIA_SET_INTF | TIMERB_IRQ
sta CIA_ICR
bne :++; jmp
: jsr initwdog81
.endif
: rts
; get custom drive code
getcustom: lda #CLK_OUT; 1581
sta CIA_PRB
lda #CLK_IN
: bit CIA_PRB; wait for CLK_IN set
beq :-
ldx SYS_SP
txs
jsr swapzp81; restore system zeropage values
jsr initcntr81
lda #CIA_SET_INTF | FLAG1_IRQ | SERIAL_IRQ | TIMERB_IRQ
sta CIA_ICR
bit CIA_ICR
customparm = TEMP
uploadrout = $2000 - customend + getbyte; as high up in RAM as possible
CUSTOMPARAM81 = customparm
CUSTOMRECEIVE81 = uploadrout
sei
ldx #customend - getbyte
: lda getbyte - 1,x
sta uploadrout - 1,x
dex
bne :-
lda #OPC_STA_ZPXI
sta uploadrout + getbyterts - getbyte
;ldx #0
stx CIA_PRB; clear DATA_OUT
ldx #6
: jsr getbyte
sta customparm - 1,x
dex
bne :-
jmp uploadrout
; must not clobber x
getbyte: DRIVEGETBYTE 1581
getbyterts: rts; is changed to sta (zp,x) for custom drive code upload
.byte customparm + 2
inc customparm + 2
bne :+
inc customparm + 3
: inc customparm + 4
bne getbyte
inc customparm + 5
bmi getbyte
jmp (customparm); execute custom drive code
customend:
; carry: clear = ok, set = load error
sendstatus: lda #0
sta blocksize + 1
lda #diskio::status::FILE_NOT_FOUND
bcs sendblock
lda #diskio::status::OK
sendblock: sta BLOCKBUFFER81; next contiguous block index/block size
jsr enablwdg81
lda #CLK_OUT | DATA_OUT; set DATA_OUT as well so there is a flank only on CLK when signalling ready,
sta CIA_PRB; such that just one serial bus read can safely determine block ready (CLK clear) and drive present (DATA set) vs device not present (both clear)
lda #DATA_OUT; clear CLK_OUT, set DATA_OUT as signal of presence
sta CIA_PRB; block ready signal
ldx #$20; here, the watchdog timer is polled manually because
; an extra-long time-out period is needed since the computer may
; still be busy decompressing a large chunk of data,
; this is the round counter: $20 * ($ff00 - $0100) = 2,080,768 cycles at 2 MHz is roughly 1 second
lda cmdfdfix2; 0 for FD
bne waitready
waitrdyfd: lda VIA_T1C_H; see if the watchdog barked
bne :+
dex ; if yes, decrease the round counter
.if ::DISABLE_WATCHDOG
beq :+
.else
beq timeout; and trigger watchdog on time-out
;ldy #$ff
sty VIA_T1C_H; reset watchdog time-out
.endif
: lda #CLK_IN
bit CIA_PRB
bmi waitrdyfd; wait for ATN_IN clear
.if !::DISABLE_WATCHDOG
sty VIA_T1C_H; reset watchdog time-out
.endif
jmp :++
closefile: sec
rts
waitready: lda CIA_TB_HI; see if the watchdog barked
bne :+
dex ; if yes, decrease the round counter
.if ::DISABLE_WATCHDOG
beq :+
.else
beq timeout; and trigger watchdog on time-out
;ldy #COUNT_PHI2 | FORCE_LOAD | ONE_SHOT | TIMER_START
sty CIA_CRB; reset watchdog time-out
.endif
: lda #CLK_IN
bit CIA_PRB
bmi waitready; wait for ATN_IN clear
.if !::DISABLE_WATCHDOG
sty CIA_CRB; reset watchdog time-out
.endif
: bne closefile
timeout: ENABLE_WATCHDOG
ldy #$00
.if ::PLATFORM = diskio::platform::COMMODORE_128
bit CIA_ICR
lda cmdfdfix2; 0 for FD
beq fdsendblk
ldx #FSM_BUS_DRIVER_OUTPUT | CLK_OUT
stx CIA_PRB
.if ::USE_ASYNCHRONOUS_BURST_HANDSHAKE
lda BLOCKBUFFER81,y
sendloop:
blocksize: cpy #$00
iny
sta CIA_SDR; clock out data byte
lda #SERIAL_IRQ
ldx #FSM_BUS_DRIVER_OUTPUT; clear CLK_OUT
: bit CIA_ICR; wait until data sent
beq :-
.if !::DISABLE_WATCHDOG
lda #COUNT_PHI2 | FORCE_LOAD | ONE_SHOT | TIMER_START
sta CIA_CRB; reset watchdog time-out
.else
nop; need some slack, as CIA sets the flag a little too early
nop
.endif
stx CIA_PRB; toggle CLK_OUT: signal data sent
lda BLOCKBUFFER81,y
: bit CIA_PRB; wait for ATN_IN set = data taken
bpl :-
bcs bcssendone
cpy blocksize + 1
iny
sta CIA_SDR; clock out data byte
lda #SERIAL_IRQ
ldx #FSM_BUS_DRIVER_OUTPUT | CLK_OUT
: bit CIA_ICR; wait until data sent
beq :-
.if !::DISABLE_WATCHDOG
lda #COUNT_PHI2 | FORCE_LOAD | ONE_SHOT | TIMER_START
sta CIA_CRB; reset watchdog time-out
.else
nop; need some slack, as CIA sets the flag a little too early
nop
.endif
stx CIA_PRB; toggle CLK_OUT: signal data sent
lda BLOCKBUFFER81,y
: bit CIA_PRB; wait for ATN_IN clear = data taken
bmi :-
bcc sendloop
bcssendone: bcs senddone; jmp
fdsendblk: lda #$38
sta VIA_PRA; set to output
lda VIA_ACR
and #.lobyte(~SHIFT_REG_CONTROL)
ora #SHIFT_OUT_T2
sta VIA_ACR
lda BLOCKBUFFER81,y
fdsendloop: sta VIA_T2C_H
eor #$ff
sta VIA_SR; clock out data byte
cpy blocksize + 1
iny
lda #IRQ_SHIFT_REG
ldx #FSM_BUS_DRIVER_OUTPUT; clear CLK_OUT
: bit CIA_ICR; wait until data sent
beq :-
.if !::DISABLE_WATCHDOG
lda #$ff
sta VIA_T1C_H; reset watchdog time-out
.else
nop; need some slack
nop
.endif
stx CIA_PRB; toggle CLK_OUT: signal data sent
lda BLOCKBUFFER81,y
: bit CIA_PRB; wait for ATN_IN set = data taken
bpl :-
bcs senddone
sta VIA_T2C_H
eor #$ff
sta VIA_SR; clock out data byte
cpy blocksize + 1
iny
lda #IRQ_SHIFT_REG
ldx #FSM_BUS_DRIVER_OUTPUT | CLK_OUT
: bit CIA_ICR; wait until data sent
beq :-
.if !::DISABLE_WATCHDOG
lda #$ff
sta VIA_T1C_H; reset watchdog time-out
.else
nop; need some slack, as CIA sets the flag a little too early
nop
.endif
stx CIA_PRB; toggle CLK_OUT: signal data sent
lda BLOCKBUFFER81,y
: bit CIA_PRB; wait for ATN_IN clear = data taken
bmi :-
bcc fdsendloop
.else ; !::USE_ASYNCHRONOUS_BURST_HANDSHAKE
lda BLOCKBUFFER81,y
sendloop: sta CIA_SDR; clock out data byte
blocksize: cpy #$00
bcs bcswaittkn
iny
.if !::DISABLE_WATCHDOG
lda #COUNT_PHI2 | FORCE_LOAD | ONE_SHOT | TIMER_START
sta CIA_CRB; reset watchdog time-out
.endif
: bit CIA_PRB; wait for ATN_IN set = data taken
bpl :-
cpy blocksize + 1
lda BLOCKBUFFER81,y
sta CIA_SDR; clock out data byte
bcs waitdtaken
iny
.if !::DISABLE_WATCHDOG
lda #COUNT_PHI2 | FORCE_LOAD | ONE_SHOT | TIMER_START
sta CIA_CRB; reset watchdog time-out
.endif
: bit CIA_PRB; wait for ATN_IN clear = data taken
bmi :-
lda BLOCKBUFFER81,y
bcc sendloop; jmp
fdsendblk: lda #$38
sta VIA_PRA; set to output
lda VIA_ACR
and #.lobyte(~SHIFT_REG_CONTROL)
ora #SHIFT_OUT_T2
sta VIA_ACR
lda BLOCKBUFFER81,y
sta VIA_T2C_H
eor #$ff
sta VIA_SR; clock out data byte
fdsendloop: cpy blocksize + 1
bcswaittkn: bcs waittaken
iny
.if !::DISABLE_WATCHDOG
lda #$ff
sta VIA_T1C_H; reset watchdog time-out
.endif
lda BLOCKBUFFER81,y
: bit CIA_PRB; wait for ATN_IN set = data taken
bpl :-
sta VIA_T2C_H
eor #$ff
sta VIA_SR; clock out data byte
cpy blocksize + 1
bcs waitdtaken
iny
.if !::DISABLE_WATCHDOG
lda #$ff
sta VIA_T1C_H; reset watchdog time-out
.endif
lda BLOCKBUFFER81,y
: bit CIA_PRB; wait for ATN_IN clear = data taken
bmi :-
sta VIA_T2C_H
eor #$ff
sta VIA_SR; clock out data byte
bcc fdsendloop; jmp
waitdtaken: bit CIA_PRB; wait for ATN_IN clear = data taken
bmi waitdtaken
bpl senddone; jmp
waittaken: bit CIA_PRB; wait for ATN_IN set = data taken
bpl waittaken
.endif; !::USE_ASYNCHRONOUS_BURST_HANDSHAKE
senddone: lda cmdfdfix2; 0 for FD
bne :+
lda #$18
sta VIA_PRA
:
.else; ::PLATFORM <> diskio::platform::COMMODORE_128
sendloop:
.if !::DISABLE_WATCHDOG
cmdfdfix3 = * + 1
lda #COUNT_PHI2 | FORCE_LOAD | ONE_SHOT | TIMER_START ; is changed to VIA access for FD
cmdfdfix4 = * + 1
sta CIA_CRB ; 2 + 4 - reset watchdog time-out
.endif
ldx BLOCKBUFFER81,y ; 4
lda SENDTABLELO,x ; 4
; = 22 (+6 with watchdog)
: bit CIA_PRB ; 4
bmi :- ; 3
sta CIA_PRB ; 4
asl ; 2
and #.lobyte(~ATNA_ENABLE_OUT) ; 2
; = 15
: bit CIA_PRB ; 4
bpl :- ; 3
sta CIA_PRB ; 4
lda SENDTABLEHI,x ; 4
; = 15
: bit CIA_PRB ; 4
bmi :- ; 3
sta CIA_PRB ; 4
asl ; 2
and #.lobyte(~ATNA_ENABLE_OUT) ; 2
blocksize: cpy #0 ; 2
iny ; 2
; = 19
: bit CIA_PRB ; 4
bpl :- ; 3
sta CIA_PRB ; 4
bcc sendloop ; 3
; = 75
: bit CIA_PRB; wait for acknowledgement
bmi :- ; of the last data byte
.endif; ::PLATFORM <> diskio::platform::COMMODORE_128
lda #CLK_OUT; drive busy
sta CIA_PRB
: bit CIA_PRB; wait for ATN_IN set,
bpl :- ; acknowledgement of the block transfer
sei; disable watchdog
clc
rts
dcodinit81: lda #CLK_OUT
sta CIA_PRB; signal idle to the host, note that ATNA response with ATN_IN low is not possible on 1581,
; so a KERNAL LISTEN command cannot be detected for automatic uninstallation
jsr swapzp81
tsx
stx SYS_SP
lda IRQVECTOR_LO
sta sysirqvbuf + 0
lda IRQVECTOR_HI
sta sysirqvbuf + 1
ldx #$00
: txa
sta TEMP
lsr
lsr
alr #%00100010
eor TEMP ; bit 3 ^ bit 0
and #%00010001; if result is 0: both bits are equal (no swap required)
;clc ; if result is 1: swap bits by inverting them
adc #%01110111; 0 -> 7, 1 -> 8
ora #%01100110; 0 -> 7, 1 -> e
eor #%10001000; 0 -> f, 1 -> 6
eor TEMP
tay
and #BINARY_NIBBLE_MASK
sta SENDTABLELO,x
tya
lsr
lsr
lsr
lsr
tay
sta SENDTABLEHI,x
inx
bne :-
jsr inittimers
lda NUMFILES
bne :+
jsr getdirtrk
ldy #0
jsr trackseek
lda #$ff
sta NUMFILES
: jmp idleloop
drvcodeend81:
DRVCODEND81 = *
.export DRVCODEND81
DIRBUFFSIZE = (SENDTABLELO - *) / 4
DIRTRACKS = *
DIRSECTORS = DIRTRACKS + DIRBUFFSIZE
FILENAMEHASHVALSLO = DIRSECTORS + DIRBUFFSIZE
FILENAMEHASHVALSHI = FILENAMEHASHVALSLO + DIRBUFFSIZE
DIRTRACKS81 = DIRTRACKS
.assert DIRBUFFSIZE >= 9, error, "***** Dir buffer too small. *****"
DIRBUFFSIZE81 = DIRBUFFSIZE
.export DIRBUFFSIZE81
.assert * <= BLOCKBUFFER81, error, "***** 1581 drive code too large. *****"
dinstall: sei
lda #CIA_ATN_IN_INPUT | WRITE_PROTECT_OUTPUT | FSM_BUS_DRIVER_DIRECTION_OUTPUT | ATNA_ENABLE_OUT_OUTPUT | CLK_OUT_OUTPUT | CLK_IN_INPUT | DATA_OUT_OUTPUT | DATA_IN_INPUT
sta CIA_DDRB
lda #CLK_OUT
sta CIA_PRB
ldx #.lobyte(drvcodebeg81 - $01)
: lda CIA_PRB; wait for ATN_IN set and DATA_IN clear
and #ATN_IN | DATA_IN
cmp #ATN_IN
bne :-
dgetrout: inx
bne :+
inc dgetputhi
:
; must not clobber x
lda #%10000000
sta BUFFER
sta CIA_PRB
: lda CIA_PRB
: cmp CIA_PRB
beq :-
lda CIA_PRB
and #CLK_IN
cmp #CLK_IN
ror BUFFER
bcc :--
lda BUFFER
dgetputhi = * + $02
sta a:.hibyte(drvcodebeg81 - $01) << 8,x
cpx #.lobyte(drvcodeend81 - $01)
bne dgetrout
dec drvcodebeg81
bne dgetrout
jmp dcodinit81
drvprgend81:
.reloc

363
loader/src/hal/hal-c16.inc Normal file
View file

@ -0,0 +1,363 @@
.ifndef _HAL_C16_INC_
_HAL_C16_INC_ = 1
.include "cpu.inc"
.include "pio.inc"
.include "ted.inc"
.macro CHECK_INSTALL_END_ADDRESS
.assert * <= $8000, error, "Install code exceeds $8000, please make sure the DISKIO_INSTALL segment ends below $8000"
.endmacro
.if LOAD_VIA_KERNAL_FALLBACK
.macro CHECK_RESIDENT_START_ADDRESS
RESIDENT_START_ADDRESS = *
.endmacro
.macro CHECK_RESIDENT_END_ADDRESS
.assert * <= $8000, error, "Resident code exceeds $8000, please make sure the DISKIO segment ends below $8000"
.endmacro
.else
.macro CHECK_RESIDENT_START_ADDRESS
.assert * <= $fd00, error, "Resident code exceeds $fd00, please make sure the DISKIO segment ends below $fd00"
.endmacro
.macro CHECK_RESIDENT_END_ADDRESS
.assert * <= $fd00, error, "Resident code exceeds $fd00, please make sure the DISKIO segment ends below $fd00"
.endmacro
.endif
.macro OK_CLC
lda #diskio::status::OK; $00
clc; all ok
.endmacro
.macro PREPARE_DRIVE_DISTURBANCE_VALIDATION
; disregard drive if it is a 1551, as
; it is not connected to the serial bus
lda #$01
sta USE4DY
.endmacro
.macro BRANCH_IF_DRIVE_DOES_NOT_DISTURB_SERIAL_BUS to
lda USE4DY; calling LISTEN will set USE4DY to $00 if 1551 at #9,
eor #$01 ; $30 if 1551 at #8, and leave it at $01 otherwise,
bne to ; then drvlistn will return with $80, $b0, or $01
.endmacro
.if LOAD_VIA_KERNAL_FALLBACK
.macro BUFFER_MEMCONFIG
GET_MEMCONFIG
pha
.endmacro
.macro RESTORE_MEMCONFIG_Y
tay
pla
SET_MEMCONFIG
tya
.endmacro
.macro PREPARE_PARALLEL_CHECK
lda #$01 ; calling LISTEN will set USE4DY to $00 if 1551 at #9,
sta USE4DY; $30 if 1551 at #8, and leave it at $01 otherwise
.endmacro
.macro ENABLE_KERNAL_SERIAL_ROUTINES
sta TED_ROM_ENABLE
.endmacro
.macro ENABLE_KERNAL_SERIAL_ROUTINES_Y
sta TED_ROM_ENABLE
.endmacro
.macro ENABLE_ALL_RAM
sta TED_RAM_ENABLE
.endmacro
.macro ENABLE_ALL_RAM_Y
sty TED_RAM_ENABLE
.endmacro
.macro GET_MEMCONFIG
lda #ROM_IS_ENABLED
and TED_CHARGEN_ADDR
.endmacro
.macro SET_MEMCONFIG
.local use_ram
sta TED_RAM_ENABLE
beq use_ram
sta TED_ROM_ENABLE
use_ram:
.endmacro
.macro SET_MEMCONFIG_Y
.local use_ram
sty TED_RAM_ENABLE
beq use_ram
sty TED_ROM_ENABLE
use_ram:
.endmacro
.endif; !LOAD_VIA_KERNAL_FALLBACK
IO_PORT_DIR_COMMON = IO_PORT_SERIAL_DATA_IN_INPUT | IO_PORT_SERIAL_CLK_IN_INPUT | IO_PORT_CST_MTR_OUTPUT | IO_PORT_SERIAL_ATN_OUT_OUTPUT | IO_PORT_SERIAL_CLK_OUT_OUTPUT | IO_PORT_SERIAL_DATA_OUT_OUTPUT; $0f
; effectively, this is the KERNAL flag:
; 0 = input = KERNAL,
; 1 = output = loader
IO_PORT_DIR_KERNAL = IO_PORT_DIR_COMMON | IO_PORT_CST_RD_INPUT ; $0f
IO_PORT_DIR_OPERATE = IO_PORT_DIR_COMMON | IO_PORT_CST_RD_OUTPUT; $1f
.macro INSTALL_IDLE
lda #IO_PORT_CST_MTR | IO_PORT_SERIAL_ATN_OUT | IO_PORT_SERIAL_CLK_OUT | IO_PORT_SERIAL_DATA_OUT; $0f
sta IO_PORT
lda #IO_PORT_DIR_OPERATE
sta IO_PORT_DIRECTION
.endmacro
.macro CLEAR store; store is ignored
lda #IO_PORT_CST_MTR | IO_PORT_SERIAL_ATN_OUT | (0 & IO_PORT_SERIAL_CLK_OUT) | (0 & IO_PORT_SERIAL_DATA_OUT); $0c
sta IO_PORT
.endmacro
.macro CLOSE_FILE
SYNC
ldx #IO_PORT_CST_MTR | (0 & IO_PORT_SERIAL_ATN_OUT) | IO_PORT_SERIAL_CLK_OUT | (0 & IO_PORT_SERIAL_DATA_OUT); $0a
stx IO_PORT
: dex
bne :-
lda IO_PORT
asl
IDLE
.endmacro
.macro SYNC
lda #IO_PORT_CST_MTR | IO_PORT_SERIAL_ATN_OUT | IO_PORT_SERIAL_CLK_OUT | (0 & IO_PORT_SERIAL_DATA_OUT); $0e
sta IO_PORT
.endmacro
.macro PUSH_CLOCKCONFIG_AND_FORCE_SLOW_CLOCK
.local singleclk
lda TED_CHARGEN_ADDR
and #FORCE_SINGLE_CLOCK
pha
bne singleclk
lda #FORCE_SINGLE_CLOCK
php
sei ; 2
ora TED_CHARGEN_ADDR ; 4
sta TED_CHARGEN_ADDR ; 4
plp ; 4
singleclk: ; = 14
.endmacro
.macro POP_CLOCKCONFIG
.local singleclk
pla
bne singleclk
lda #255 - FORCE_SINGLE_CLOCK
php
sei ; 2
and TED_CHARGEN_ADDR ; 4
sta TED_CHARGEN_ADDR ; 4
plp ; 4
singleclk: ; = 14
.endmacro
.macro SENDBYTE sendstore
; does not clobber y
.local sendbyte
.local bitset
tax
PUSH_CLOCKCONFIG_AND_FORCE_SLOW_CLOCK
txa
ldx #$07
sendbyte: lsr
pha
lda IO_PORT
ora #IO_PORT_SERIAL_ATN_OUT | IO_PORT_SERIAL_CLK_OUT
bcs bitset
and #255 - IO_PORT_SERIAL_CLK_OUT
bitset: eor #IO_PORT_SERIAL_DATA_OUT
sta IO_PORT
pla
dex
bpl sendbyte
POP_CLOCKCONFIG
.endmacro
.macro RECEIVEBYTE
PUSH_CLOCKCONFIG_AND_FORCE_SLOW_CLOCK
lda #$01
: pha
pla
pha
lda #IO_PORT_SERIAL_CLK_OUT
eor IO_PORT
ldx #IO_PORT_SERIAL_DATA_IN
cpx IO_PORT
sta IO_PORT
pla
rol
bcc :-
tax
POP_CLOCKCONFIG
txa
.endmacro
.macro SET_FLAGS_N_DATA_V_CLK
bit IO_PORT
.endmacro
CLOCK = IO_PORT_CST_MTR | (0 & IO_PORT_SERIAL_CLK_OUT) | (0 & IO_PORT_SERIAL_DATA_OUT)
CLOCK_ATN_HI = CLOCK | IO_PORT_SERIAL_ATN_OUT ; 1st and 3rd bit pairs; $0c
CLOCK_ATN_LO = CLOCK | (0 & IO_PORT_SERIAL_ATN_OUT); 2nd and 4th bit pairs; $08
.macro ENABLE_WAITBUSY_KERNAL
lda #.lobyte(~IO_PORT_SERIAL_CLK_OUT)
and IO_PORT
sta IO_PORT
.endmacro
.macro INIT_CLEAR_ATN_OUT_CLEAR_CLK_OUT_CLEAR_DATA_OUT
lda #IO_PORT_CST_MTR | (0 & IO_PORT_SERIAL_ATN_OUT) | (0 & IO_PORT_SERIAL_CLK_OUT) | (0 & IO_PORT_SERIAL_DATA_OUT); $08
sta IO_PORT
.endmacro
.macro IDLE
ldx #IO_PORT_CST_MTR | IO_PORT_SERIAL_ATN_OUT | IO_PORT_SERIAL_CLK_OUT | IO_PORT_SERIAL_DATA_OUT; $0f
stx IO_PORT
.endmacro
.if LOAD_VIA_KERNAL_FALLBACK
KERNALFILENO = 2
.macro CLEAR_DATA_OUT_CLEAR_CLK_OUT_ASSERT_ATN
lda IO_PORT
and #.lobyte(~(IO_PORT_SERIAL_CLK_OUT | IO_PORT_SERIAL_DATA_OUT))
ora #IO_PORT_SERIAL_ATN_OUT
sta IO_PORT
.endmacro
.endif
.macro BRANCH_IF_INSTALLED to
lda #IO_PORT_DIR_KERNAL
cmp IO_PORT_DIRECTION
bne to
.endmacro
.macro BRANCH_IF_NOT_INSTALLED to
lda #IO_PORT_DIR_KERNAL
cmp IO_PORT_DIRECTION
beq to
.endmacro
.macro CHECK_AND_BRANCH_IF_DRIVE_PARALLEL to
jsr CHECKPARALLEL
bcc to
.endmacro
.macro BRANCH_IF_DRIVE_PARALLEL to
lda USE4DY
lsr
bcc to
.endmacro
.macro POLL_BLOCK idle_eof, block_not_yet_ready, device_not_present
lda #IO_PORT_CST_MTR | IO_PORT_SERIAL_ATN_OUT | IO_PORT_SERIAL_CLK_OUT | IO_PORT_SERIAL_DATA_OUT; $0f
cmp IO_PORT
beq idle_eof; branches with carry set on idle/eof
clc
lda #diskio::status::DEVICE_NOT_PRESENT; $fe
SET_FLAGS_N_DATA_V_CLK
bvc block_not_yet_ready
sec
bmi device_not_present
.endmacro
.macro SEND_BLOCK_SIGNAL
ldy #CLOCK_ATN_LO; use y to ensure that y < $fe when
sty IO_PORT ; calling getbyte for the 2 control bytes
PUSH_CLOCKCONFIG_AND_FORCE_SLOW_CLOCK
pha; delay
pla
.endmacro
.macro RECEIVE_SETUP
; nothing to do
.endmacro
.macro RECEIVE store, out
.local loop
; 16 cycles per bitpair ~ 18 cycles at 1 MHz = 18 µs
; PAL: 16 / 886723 Hz = 18.04 µs
; NTSC: 16 / 894886 Hz = 17.88 µs
loop: ldx #CLOCK_ATN_HI ; 2
lda IO_PORT ; 3
stx IO_PORT; ATN high ; 3
; = 16
iny ; 2
beq out ; 2
lsr ; 2
lsr ; 2
ldx #CLOCK_ATN_LO ; 2
eor IO_PORT ; 3
stx IO_PORT; ATN low ; 3
; = 16
lsr ; 2
lsr ; 2
nop ; 2 - delay
nop ; 2 - delay
ldx #CLOCK_ATN_HI ; 2
eor IO_PORT ; 3
stx IO_PORT; ATN high ; 3
; = 16
lsr ; 2
lsr ; 2
nop ; 2 - delay
eor #CLOCK_ATN_HI | (CLOCK_ATN_LO >> 2) ; 2
ldx #CLOCK_ATN_LO ; 2
eor IO_PORT ; 3
stx IO_PORT; ATN low ; 3
; = 16
store ; 5 - sta mem16,y
jmp loop ; 3
out:
.endmacro
.macro STOREBYTE_ALLRAM
storebytio: sta $0000,y
.endmacro
.macro ENDGETBLOCK
POP_CLOCKCONFIG
.endmacro
.macro SET_IO_KERNAL
INIT_CLEAR_ATN_OUT_CLEAR_CLK_OUT_CLEAR_DATA_OUT
lda #IO_PORT_DIR_KERNAL
sta IO_PORT_DIRECTION
.endmacro
.endif; !_HAL_C16_INC_

View file

@ -0,0 +1,607 @@
.ifndef _HAL_C64_C128_INC_
_HAL_C64_C128_INC_ = 1
.include "cia.inc"
.include "vic.inc"
.macro CHECK_INSTALL_END_ADDRESS
.assert * <= $d000, error, "Install code exceeds $d000, please make sure the DISKIO_INSTALL segment ends below $d000"
.endmacro
.if LOAD_VIA_KERNAL_FALLBACK
.macro CHECK_RESIDENT_START_ADDRESS
RESIDENT_START_ADDRESS = *
.endmacro
.macro CHECK_RESIDENT_END_ADDRESS
.assert (RESIDENT_START_ADDRESS > ENABL) || (* < ENABL), error, "Resident code crosses KERNAL RS232 state variable ENABL"
.assert * <= $d000, error, "Resident code exceeds $d000, please make sure the DISKIO segment ends below $d000"
.endmacro
.else
.macro CHECK_RESIDENT_START_ADDRESS
.assert (* <= $d000) || (* >= $e000), error, "Resident code resides at $d000..$dfff, please make sure the DISKIO segment does not overlap with that memory range"
.endmacro
.macro CHECK_RESIDENT_END_ADDRESS
.assert (* <= $d000) || (* >= $e000), error, "Resident code resides at $d000..$dfff, please make sure the DISKIO segment does not overlap with that memory range"
.endmacro
.endif
.macro OK_CLC
; use illegals to save size
alr #diskio::status::OK; $00, clc = all ok
.endmacro
.macro PREPARE_DRIVE_DISTURBANCE_VALIDATION
; nothing to do
.endmacro
.macro BRANCH_IF_DRIVE_DOES_NOT_DISTURB_SERIAL_BUS to
; nothing to do
.endmacro
.if LOAD_VIA_KERNAL_FALLBACK
.macro PREPARE_PARALLEL_CHECK
; nothing to do
.endmacro
.endif
.if PLATFORM <> diskio::platform::COMMODORE_128
.if LOAD_VIA_KERNAL_FALLBACK
.macro ENABLE_KERNAL_SERIAL_ROUTINES
lda #MEMCONFIG_IO_KERNAL_BASIC
sta IO_PORT
.endmacro
.macro ENABLE_KERNAL_SERIAL_ROUTINES_Y
ldy #MEMCONFIG_IO_KERNAL_BASIC
sty IO_PORT
.endmacro
.endif; LOAD_VIA_KERNAL_FALLBACK
.if LOAD_UNDER_D000_DFFF | LOAD_VIA_KERNAL_FALLBACK
.macro BUFFER_MEMCONFIG
lda IO_PORT
pha
.endmacro
.macro RESTORE_MEMCONFIG_Y
tay
pla
sta IO_PORT
tya
.endmacro
.macro ENABLE_IO_SPACE
lda #MEMCONFIG_IO
sta IO_PORT
.endmacro
.macro ENABLE_IO_SPACE_X
ldx #MEMCONFIG_IO
stx IO_PORT
.endmacro
.macro ENABLE_IO_SPACE_Y
ldy #MEMCONFIG_IO
sty IO_PORT
.endmacro
.macro ENABLE_ALL_RAM
lda #MEMCONFIG_ALL_RAM
sta IO_PORT
.endmacro
.macro ENABLE_ALL_RAM_X
ldx #MEMCONFIG_ALL_RAM
stx IO_PORT
.endmacro
.macro ENABLE_ALL_RAM_Y
ldy #MEMCONFIG_ALL_RAM
sty IO_PORT
.endmacro
.macro GET_MEMCONFIG
lda IO_PORT
.endmacro
.macro SET_MEMCONFIG
sta IO_PORT
.endmacro
.macro SET_MEMCONFIG_X
stx IO_PORT
.endmacro
.macro SET_MEMCONFIG_Y
sty IO_PORT
.endmacro
.endif; !(LOAD_UNDER_D000_DFFF | LOAD_VIA_KERNAL_FALLBACK)
.else; PLATFORM = diskio::platform::COMMODORE_128
.include "mmu.inc"
.macro DISABLE_BURST_MODE
lda #IOMODE_OUTPUT
ora CIA1_CRA
sta CIA1_CRA
.endmacro
.if LOAD_VIA_KERNAL_FALLBACK
.macro ENABLE_KERNAL_SERIAL_ROUTINES
lda #BANK_0 | SYSTEM_ROM | MID_RAM | LOW_RAM | IO_SPACE
sta MMU_CR
.endmacro
.macro ENABLE_KERNAL_SERIAL_ROUTINES_Y
ldy #BANK_0 | SYSTEM_ROM | MID_RAM | LOW_RAM | IO_SPACE
sty MMU_CR
.endmacro
.macro START_BURST_LOAD
bit CIA1_ICR
lda #SERIAL_CLK_OUT
eor CIA2_PRA
sta CIA2_PRA
.endmacro
.macro GET_BURST_BYTE
.local waitburst
lda #SERIAL_IRQ
waitburst: bit CIA1_ICR
beq waitburst
ldy CIA1_SDR
lda #SERIAL_CLK_OUT
php
sei
eor CIA2_PRA
sta CIA2_PRA
plp
tya
.endmacro
.endif; LOAD_VIA_KERNAL_FALLBACK
.if LOAD_UNDER_D000_DFFF | LOAD_VIA_KERNAL_FALLBACK
.macro BUFFER_MEMCONFIG
lda MMU_CR
pha
.endmacro
.macro RESTORE_MEMCONFIG_Y
tay
pla
sta MMU_CR
tya
.endmacro
.macro ENABLE_IO_SPACE
lda #BANK_0 | HIGH_RAM | MID_RAM | LOW_RAM | IO_SPACE
sta MMU_CR
.endmacro
.macro ENABLE_IO_SPACE_X
ldx #BANK_0 | HIGH_RAM | MID_RAM | LOW_RAM | IO_SPACE
stx MMU_CR
.endmacro
.macro ENABLE_IO_SPACE_Y
ldy #BANK_0 | HIGH_RAM | MID_RAM | LOW_RAM | IO_SPACE
sty MMU_CR
.endmacro
.macro ENABLE_ALL_RAM
lda #BANK_0 | HIGH_RAM | MID_RAM | LOW_RAM | RAM_ROM
sta MMU_CR
.endmacro
.macro ENABLE_ALL_RAM_X
ldx #BANK_0 | HIGH_RAM | MID_RAM | LOW_RAM | RAM_ROM
stx MMU_CR
.endmacro
.macro ENABLE_ALL_RAM_Y
ldy #BANK_0 | HIGH_RAM | MID_RAM | LOW_RAM | RAM_ROM
sty MMU_CR
.endmacro
.macro GET_MEMCONFIG
lda MMU_CR
.endmacro
.macro SET_MEMCONFIG
sta MMU_CR
.endmacro
.macro SET_MEMCONFIG_X
stx MMU_CR
.endmacro
.macro SET_MEMCONFIG_Y
sty MMU_CR
.endmacro
.endif; !(LOAD_UNDER_D000_DFFF | LOAD_VIA_KERNAL_FALLBACK)
.endif; PLATFORM = diskio::platform::COMMODORE_128
; CIA2 DDRA ($DD02) definitions
CIA2_DDRA_COMMON = CIA_SERIAL_DATA_IN_INPUT | CIA_SERIAL_CLK_IN_INPUT | CIA_VIC2_BANK_OUTPUT; $03
; effectively, this is the KERNAL flag:
; 0 = input = loader,
; DATA OUT, CLK OUT, ATN OUT are clear, RS232_TXD is clear ; 1 = output = KERNAL
CIA2_DDRA_KERNAL = CIA2_DDRA_COMMON | CIA_SERIAL_ATN_OUT_OUTPUT | CIA_SERIAL_CLK_OUT_OUTPUT | CIA_SERIAL_DATA_OUT_OUTPUT | CIA_RS232_TXD_OUTPUT; $3f
; DATA OUT, CLK OUT, ATN OUT are set, RS232_TXD input, all bits except VIC bank bits are inputs so that $DD00 writes do not change the bus state
CIA2_DDRA_IDLE = CIA2_DDRA_COMMON | CIA_SERIAL_ATN_OUT_INPUT | CIA_SERIAL_CLK_OUT_INPUT | CIA_SERIAL_DATA_OUT_INPUT | CIA_RS232_TXD_INPUT ; $03
; DATA OUT and CLK OUT are clear, ATN OUT is set, RS232_TXD is output
CIA2_DDRA_CLEAR = CIA2_DDRA_COMMON | CIA_SERIAL_ATN_OUT_INPUT | CIA_SERIAL_CLK_OUT_OUTPUT | CIA_SERIAL_DATA_OUT_OUTPUT | CIA_RS232_TXD_OUTPUT; $37
; DATA OUT, CLK OUT and ATN OUT are clear, RS232_TXD is input
CIA2_DDRA_RECEIVE = CIA2_DDRA_COMMON | CIA_SERIAL_ATN_OUT_OUTPUT | CIA_SERIAL_CLK_OUT_OUTPUT | CIA_SERIAL_DATA_OUT_OUTPUT | CIA_RS232_TXD_INPUT ; $3b
; DATA OUT is clear, CLK OUT and ATN OUT are set, RS232_TXD is input
CIA2_DDRA_SYNC = CIA2_DDRA_COMMON | CIA_SERIAL_ATN_OUT_INPUT | CIA_SERIAL_CLK_OUT_INPUT | CIA_SERIAL_DATA_OUT_OUTPUT | CIA_RS232_TXD_INPUT ; $23
; DATA OUT and ATN OUT are clear, CLK OUT is set, RS232_TXD is input
CIA2_DDRA_CLOSE = CIA2_DDRA_COMMON | CIA_SERIAL_ATN_OUT_OUTPUT | CIA_SERIAL_CLK_OUT_INPUT | CIA_SERIAL_DATA_OUT_OUTPUT | CIA_RS232_TXD_INPUT ; $2b
.macro INSTALL_IDLE
lda #CIA2_DDRA_IDLE
sta CIA2_DDRA
.endmacro
.macro CLEAR store
lda #CIA2_DDRA_CLEAR
.ifnblank store
store:
.endif
sta CIA2_DDRA
.endmacro
.macro CLOSE_FILE
lda #CIA2_DDRA_SYNC
sta CIA2_DDRA
ldx #CIA2_DDRA_CLOSE
stx CIA2_DDRA
: dex
bne :-
lda CIA2_PRA
asl
IDLE
.endmacro
.macro SYNC
lda #CIA2_DDRA_SYNC
sta CIA2_DDRA
.endmacro
.macro SET_FLAGS_N_DATA_V_CLK
bit CIA2_PRA
.endmacro
.macro PUSH_CLOCKCONFIG_AND_FORCE_SLOW_CLOCK
lda VIC2_C128_CLOCK
pha
lda #0
sta VIC2_C128_CLOCK
.endmacro
.macro POP_CLOCKCONFIG
pla
sta VIC2_C128_CLOCK
.endmacro
.macro SENDBYTE sendstore
; does not clobber y
.local sendbyte
.local bitset
.if USE_2_MHZ
tax
PUSH_CLOCKCONFIG_AND_FORCE_SLOW_CLOCK
txa
.endif
ldx #$07
sendbyte: lsr
pha
.if NTSC_COMPATIBILITY
nop
.endif
.ifblank sendstore
pha; delay
lda CIA2_DDRA
and #255 - (SERIAL_ATN_OUT + SERIAL_CLK_OUT)
bcs bitset
ora #SERIAL_CLK_OUT
bitset: eor #SERIAL_DATA_OUT
sta CIA2_DDRA
pla; delay
.else
lda GETBYTE_CLOCK_ATN_HI
and #255 - (SERIAL_ATN_OUT + SERIAL_CLK_OUT)
bcs bitset
ora #SERIAL_CLK_OUT
bitset: eor #SERIAL_DATA_OUT
jsr sendstore
.endif
pla
dex
bpl sendbyte
.if USE_2_MHZ
POP_CLOCKCONFIG
.endif
.endmacro
.macro RECEIVEBYTE
.if USE_2_MHZ
PUSH_CLOCKCONFIG_AND_FORCE_SLOW_CLOCK
.endif
lda #$01
: pha
pla
pha
lda #SERIAL_CLK_OUT
eor CIA2_DDRA
ldx #SERIAL_DATA_IN
cpx CIA2_PRA
sta CIA2_DDRA
pla
rol
bcc :-
.if USE_2_MHZ
tax
POP_CLOCKCONFIG
txa
.endif
.endmacro
; RS232 TXD set to output so this bit won't interfere with byte fetch from serial bus
CLOCK = CIA2_DDRA_COMMON | CIA_SERIAL_CLK_OUT_OUTPUT | CIA_SERIAL_DATA_OUT_OUTPUT | CIA_RS232_TXD_OUTPUT; $37
CLOCK_ATN_HI = CLOCK | CIA_SERIAL_ATN_OUT_INPUT ; 1st and 3rd bit pairs, $37
CLOCK_ATN_LO = CLOCK | CIA_SERIAL_ATN_OUT_OUTPUT; 2nd and 4th bit pairs, $3f
.macro ENABLE_WAITBUSY_KERNAL
lda #.lobyte(~(SERIAL_DATA_OUT | SERIAL_CLK_OUT))
and CIA2_PRA
sta CIA2_PRA
.endmacro
.if LOAD_VIA_KERNAL_FALLBACK
KERNALFILENO = 2
.macro CLEAR_DATA_OUT_CLEAR_CLK_OUT_ASSERT_ATN
lda CIA2_PRA
and #.lobyte(~(SERIAL_DATA_OUT | SERIAL_CLK_OUT))
ora #SERIAL_ATN_OUT
sta CIA2_PRA
.endmacro
.endif
.macro BRANCH_IF_INSTALLED to
lda #CIA2_DDRA_KERNAL
cmp CIA2_DDRA
bne to
.endmacro
.macro BRANCH_IF_NOT_INSTALLED to
lda #CIA2_DDRA_KERNAL
eor CIA2_DDRA
beq to
.endmacro
.macro CHECK_AND_BRANCH_IF_DRIVE_PARALLEL to
lda IOPEN + 1
cmp #.hibyte($df00); serial: AR fastload vectors point to $DFxx, parallel: IDE64 vectors point to $DExx, IEEE-488 interfaces to $Cxxx
bcc to
.endmacro
.macro BRANCH_IF_DRIVE_PARALLEL to
CHECK_AND_BRANCH_IF_DRIVE_PARALLEL to
.endmacro
.macro IDLE
ldx #CIA2_DDRA_IDLE
stx CIA2_DDRA
.endmacro
.macro POLL_BLOCK idle_eof, block_not_yet_ready, device_not_present
lda #CIA2_DDRA_IDLE
cmp CIA2_DDRA
beq idle_eof; branches with carry set on idle/eof
;clc
lda #diskio::status::DEVICE_NOT_PRESENT; $fe
SET_FLAGS_N_DATA_V_CLK
bvc block_not_yet_ready
sec
bmi device_not_present
.endmacro
.macro SEND_BLOCK_SIGNAL
ldy #CIA2_DDRA_RECEIVE; use y to ensure that y < $fe when
sty CIA2_DDRA ; calling getbyte for the 2 control bytes
.if USE_2_MHZ
PUSH_CLOCKCONFIG_AND_FORCE_SLOW_CLOCK
.else
pha; delay
pla
.endif
.if (PLATFORM = diskio::platform::COMMODORE_128) & USE_ASYNCHRONOUS_BURST_HANDSHAKE
lda #OPC_BVS
sta burstwait
.if LOAD_UNDER_D000_DFFF
sta burstwaitio
.endif
.endif; !((PLATFORM = diskio::platform::COMMODORE_128) & USE_ASYNCHRONOUS_BURST_HANDSHAKE)
.endmacro
.macro RECEIVE_SETUP
;lda #CLOCK_ATN_HI; = CIA2_DDRA_IDLE
sta GETBYTE_CLOCK_ATN_HI
.endmacro
.macro RECEIVE store, out
.local getloop
.local merge
.local entry
sec; add at label entry below must yield bit 8 set
.if PLATFORM = diskio::platform::COMMODORE_128
.local burstloop
.local storeburst
.local waitburst
.local noburst
.if .not .xmatch (store, STOREBYTE_ALLRAM)
.define burstswit burstwait
.else
.define burstswit burstwaitio
.endif
bit CIA1_CRA
bvs noburst; branch if IO mode is set to output
.if .not .xmatch (store, STOREBYTE_ALLRAM)
lda storebyte + 0
sta storeburst + 0
lda storebyte + 1
sta storeburst + 1
lda storebyte + 2
sta storeburst + 2
.else
lda storebytio + 1
sta storbytiob + 1
lda storebytio + 2
sta storbytiob + 2
.endif
iny
burstloop: lda CIA2_DDRA ; use the ATN line for the data taken signal in
eor #SERIAL_ATN_OUT ; order to keep passive drives from leaving the
tax ; silencing loop after too long a period of ATN clear
.if USE_ASYNCHRONOUS_BURST_HANDSHAKE
lda #OPC_BVC ^ OPC_BVS; use the CLK line for the incoming data sent signal rather
eor burstswit ; than the SERIAL_IRQ flag in CIA1_ICR ($dc0d) in order to
sta burstswit ; avoid interference by CIA1 interrupt handlers
waitburst: bit CIA2_PRA
burstswit: bvs waitburst ; wait for incoming data sent signal
.else; !USE_ASYNCHRONOUS_BURST_HANDSHAKE
lda #SERIAL_IRQ
waitburst: bit CIA1_ICR
beq waitburst
.endif; !USE_ASYNCHRONOUS_BURST_HANDSHAKE
lda CIA1_SDR
stx CIA2_DDRA ; signal data taken
.if .not .xmatch (store, STOREBYTE_ALLRAM)
storeburst: sta $0000,y
.else
ENABLE_ALL_RAM_X
storbytiob: sta $0000,y
ENABLE_IO_SPACE_X
.endif
iny
bne burstloop
lda #CLOCK_ATN_HI
sta CIA2_DDRA
bne out; jmp
noburst = entry
.endif; PLATFORM = diskio::platform::COMMODORE_128
;sec; add below must yield bit 3 set
bcs entry; jmp
; note: between each store to CIA2_DDRA to signal ready for the next bit pair,
; 10 cycles must pass before read access of CIA2_PRA:
; the drive needs max. 14 cycles to respond with a loop in the form of 'bit VIA1_PRB : bpl/bmi * - 3 : sta VIA1_PRB' -
; this means that 18 cycles per bit pair are the minimum
getloop: lsr ; 2 - -3210HHH
lsr ; 2 - --3210HH:H
.if NTSC_COMPATIBILITY
ldx GETBYTE_CLOCK_ATN_HI ; $37 ; 3
.else
ldx #CLOCK_ATN_HI ; $37 ; 2
.endif
ora CIA2_PRA - CLOCK_ATN_HI,x ; 5 - 543210HH - delay
stx CIA2_DDRA; sync 3: set ATN high ; 4
; = 18
ror ; 2 - H543210H:H
ror ; 2 - HH543210:H
ldx #CLOCK_ATN_LO ; $3f ; 2
sax merge + $01 ; 4 - --543210
.if NTSC_COMPATIBILITY
and CIA2_PRA - CLOCK_ATN_LO,x ; 4 - 7654.2..
.else
and CIA2_PRA ; 4 - 7654.2..
.endif
stx CIA2_DDRA; sync 4: set ATN low ; 4
; = 18
merge: ora #$00 ; 2 - 76543210
store ; 5 - sta mem16,y
entry: lax GETBYTE_CLOCK_ATN_HI ; $37 ; 3
.if NTSC_COMPATIBILITY
adc CIA2_PRA - CLOCK_ATN_HI,x ; 5 - 10HHH... - carry must be set
.else
adc CIA2_PRA ; 4 - 10HHH... - carry must be set
.endif
stx CIA2_DDRA; sync 1: set ATN high ; 4
; = 18
iny ; 2
beq out ; 2 - clc
lsr ; 2 - -10HHH..
lsr ; 2 - --10HHH.
ldx #CLOCK_ATN_LO ; $3f ; 2
.if NTSC_COMPATIBILITY
ora CIA2_PRA - CLOCK_ATN_LO,x ; 5 - 3210HHH.
.else
ora CIA2_PRA ; 4 - 3210HHH.
.endif
stx CIA2_DDRA; sync 2: set ATN low ; 4
; = 18
jmp getloop ; 3
.endmacro
.macro STOREBYTE_ALLRAM
ENABLE_ALL_RAM_X
storebytio: sta $0000,y
ENABLE_IO_SPACE_X
.endmacro
.macro ENDGETBLOCK
.if USE_2_MHZ
POP_CLOCKCONFIG
.endif
.endmacro
.macro SET_IO_KERNAL
lda #CIA2_DDRA_KERNAL
sta CIA2_DDRA
.endmacro
.endif; !_HAL_C64_C128_INC_

33
loader/src/hal/hal.inc Executable file
View file

@ -0,0 +1,33 @@
.ifndef _HAL_INC_
_HAL_INC_ = 1
.if PLATFORM = diskio::platform::COMMODORE_16
.include "hal/hal-c16.inc"
.else; PLATFORM <> diskio::platform::COMMODORE_16
.include "hal/hal-c64-c128.inc"
.endif; PLATFORM <> diskio::platform::COMMODORE_16
.macro DO_UNINSTALL
.local waituninst
SET_IO_KERNAL
waituninst: SET_FLAGS_N_DATA_V_CLK
bpl waituninst
bvc waituninst
.endmacro
; block ready queries are the same for the loader's and KERNAL's protocols
.macro BRANCH_IF_BLOCK_READY to
SET_FLAGS_N_DATA_V_CLK
bvs to
.endmacro
.macro BRANCH_IF_BLOCK_NOT_READY to
SET_FLAGS_N_DATA_V_CLK
bvc to
.endmacro
.endif; !_HAL_INC_

1442
loader/src/install.s Executable file

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,32 @@
#!/usr/bin/env perl
my $platform = $ARGV[0];
my $zp = hex($ARGV[1]);
my $install = hex($ARGV[2]);
my $resident = hex($ARGV[3]);
my $transient = hex($ARGV[4]);
printf ("MEMORY
{
ZPRAM: start = \$%x, size = \$%x;
ZPRAM2: start = \$%x, size = \$%x;
INSTALLRAM: start = \$%x, size = \$%x, file = \"../build/install-$platform.prg\";
RESIDENTRAM: start = \$%x, size = \$%x, file = \"../build/loader-$platform.prg\";
TRANSIENTRAM: start = \$%x, size = \$%x, file = \"../build/transient-$platform.prg\";
}
SEGMENTS
{
DISKIO_ZP: load = ZPRAM, type = zp;
DISKIO_PLUGIN_ZP: load = ZPRAM2, type = zp, optional = yes;
DISKIO: load = RESIDENTRAM;
DISKIO_PLUGIN: load = TRANSIENTRAM, optional = yes;
DISKIO_INSTALL: load = INSTALLRAM;
}
",
$zp, 0x0100 - $zp,
$zp, 0x0100 - $zp,
$install - 2, 0x10002 - $install,
$resident - 2, 0x10002 - $resident,
$transient - 2, 0x10002 - $transient);

View file

@ -0,0 +1,272 @@
#!/usr/bin/env perl
=head1 NAME
make-loadersymbolsinc.pl
=head1 DESCRIPTION
This script extracts relevant symbols from a ld65-generated map file.
The resulting include file can be included in third-party assembly source code.
=head1 SYNOPSIS
make-loadersymbolsinc.pl loader-c64.map > loadersymbols-c64.inc
=cut
use strict;
use warnings;
use English qw( -no_match_vars ); # OS_ERROR etc.
main();
sub main
{
my $version = $ARGV[0];
my $project = $ARGV[1];
my @input = read_file($ARGV[2]);
my ($diskio_segments, $symbols) = extract_all_symbols(@input);
print_symbols($version, $project, $diskio_segments, filter_symbols($diskio_segments, $symbols));
return;
}
sub read_file
{
my ($filename) = @ARG;
open(my $fh, '<', $filename)
or die "cannot read file '$filename': $OS_ERROR";
my @input = <$fh>;
close $fh;
return @input;
}
sub extract_all_symbols
{
my @input = @ARG;
my $current_list;
my %diskio_segments;
my %symbols;
foreach my $i (@input) {
if ($i =~ qr{list.*?:}) {
$current_list = $i;
} elsif ($current_list =~ qr{Segment}) {
# only check against the DISKIO segments
if ($i =~ qr{(DISKIO\w*)\s+(\w+)\s+(\w+)}) {
my $label = lc $1;
$diskio_segments{$label . '_start'} = hex '0x' . $2;
$diskio_segments{$label . '_end'} = hex '0x' . $3;
}
} elsif ($current_list =~ qr{Exports}) {
# the exported symbols will be filtered by the DISKIO segments
if ($i =~ qr{(\w+)\s+(\w+)\s+\w+\s+(\w+)\s+(\w+)}) {
# double-entry line
$symbols{$1} = hex '0x' . $2;
$symbols{$3} = hex '0x' . $4;
} elsif ($i =~ qr{(\w+)\s+(\w+)\s+\w+}) {
# single-entry line
$symbols{$1} = hex '0x' . $2;
}
}
}
return \%diskio_segments, \%symbols;
}
sub filter_symbols
{
my ($diskio_segments, $symbols) = @ARG;
my %filtered_host;
my %filtered_drive;
my $decompressor;
while (my ($name, $address) = each %$symbols) {
# do not regard *fix/*fix1 symbols, all-caps or *1 (41/71/81) symbols
if ($address && ($name !~ /fix|fix1|[A-Z]|1$/)) {
# only symbols in the DISKIO segments are printed
if ((($address >= $diskio_segments->{diskio_start})
&& ($address <= $diskio_segments->{diskio_end}))
|| (exists($diskio_segments->{diskio_plugin_start})
&& ($address >= $diskio_segments->{diskio_plugin_start})
&& ($address <= $diskio_segments->{diskio_plugin_end}))
|| (($address >= $diskio_segments->{diskio_install_start})
&& ($address <= $diskio_segments->{diskio_install_end}))
|| (($address >= $diskio_segments->{diskio_zp_start})
&& ($address <= $diskio_segments->{diskio_zp_end}))
|| (exists($diskio_segments->{diskio_plugin_zp_start})
&& ($address >= $diskio_segments->{diskio_plugin_zp_start})
&& ($address <= $diskio_segments->{diskio_plugin_zp_end})))
{
$filtered_host{$name} = $address;
}
} elsif (($name =~ /config_/) || ($name =~ /status_/)) {
$filtered_host{$name} = $address;
} elsif (($name =~ /1$/) && ($name !~ /fix1$/)) {
$filtered_drive{$name} = $address;
} elsif ($name =~ /DECOMPRESSOR_(\S+)/) {
$decompressor = $1;
}
}
return \%filtered_host, \%filtered_drive, $decompressor;
}
sub print_symbols
{
my ($version, $project, $diskio_segments, $host_symbols, $drive_symbols, $decompressor) = @ARG;
my @host_symbols_sorted_by_address =
sort { $host_symbols->{$a} <=> $host_symbols->{$b} } keys %{$host_symbols};
my @drive_symbols_sorted_by_address =
sort { $drive_symbols->{$a} <=> $drive_symbols->{$b} } keys %{$drive_symbols};
my $target = ($diskio_segments->{diskio_plugin_start}) ? "customdrivecode" : "prg";
for (@host_symbols_sorted_by_address) {
if("save" eq $_ ) {
$target = "save";
last;
}
}
chomp(my $datetime = `date -R`);
print "; repository version " . $version . ", built on " . $datetime . " for project \"" . $project . "\" using\n";
print "; make PLATFORM=" . $ENV{'_PLATFORM_'} . " " . $target . " INSTALL=" . $ENV{'INSTALL'} . " RESIDENT=" . $ENV{'RESIDENT'};
unless ($target =~ /prg/) {
print " TRANSIENT=" . $ENV{'TRANSIENT'}
}
print " ZP=" . $ENV{'ZP'} . " PROJECT=" . $ENV{'PROJECT'} . "\n\n";
print "; configuration\n";
foreach my $name (sort (keys(%{$host_symbols}))) {
if ($name =~ /config_/) {
if ($name =~ /config_DECOMPRESSOR/) {
print_symbol($name, $host_symbols->{$name}, $decompressor);
} else {
print_symbol($name, $host_symbols->{$name});
}
}
}
print "\n; status codes\n";
foreach my $name (@host_symbols_sorted_by_address) {
if ($name =~ /status_/) {
print_hex_symbol($name, $host_symbols->{$name});
}
}
my $zp_end = $diskio_segments->{diskio_zp_end};
if (exists($diskio_segments->{diskio_plugin_zp_end})) {
$zp_end = $diskio_segments->{diskio_plugin_zp_end} > $diskio_segments->{diskio_zp_end} ? $diskio_segments->{diskio_plugin_zp_end} : $diskio_segments->{diskio_zp_end};
}
print "\n; zeropage ";
printf '$%.2x-$%.2x', $diskio_segments->{diskio_zp_start}, $zp_end + 1;
print "\n";
print_hex_symbol("loader_zp_first", $host_symbols->{"loader_zp_first"});
print_segment_symbols($host_symbols, $diskio_segments->{diskio_zp_start}, $zp_end, @host_symbols_sorted_by_address);
print "\n; install ";
printf '$%.4x-$%.4x', $diskio_segments->{diskio_install_start} + 2, $diskio_segments->{diskio_install_end} + 1;
print "\n";
# only one relevant symbol ("install"), stop after that so that other symbols from overlaying segments (transient) will be printed there instead
print_segment_symbols($host_symbols, $diskio_segments->{diskio_install_start} + 2, $diskio_segments->{diskio_install_start} + 2, @host_symbols_sorted_by_address);
print "\n; resident ";
printf '$%.4x-$%.4x', $diskio_segments->{diskio_start} + 2, $diskio_segments->{diskio_end} + 1;
print "\n";
print_segment_symbols($host_symbols, $diskio_segments->{diskio_start} + 2, $diskio_segments->{diskio_end}, @host_symbols_sorted_by_address);
unless ($target =~ /prg/) {
if ($diskio_segments->{diskio_plugin_start}) {
print "\n; transient ";
printf '$%.4x-$%.4x', $diskio_segments->{diskio_plugin_start} + 2, $diskio_segments->{diskio_plugin_end} + 1;
print "\n";
print_segment_symbols($host_symbols, $diskio_segments->{diskio_plugin_start} + 2, $diskio_segments->{diskio_plugin_end}, @host_symbols_sorted_by_address);
}
unless ($target =~ /save/) {
print "\n; drive\n";
print_drive_symbols($drive_symbols, "41", @drive_symbols_sorted_by_address);
print "\n";
print_drive_symbols($drive_symbols, "71", @drive_symbols_sorted_by_address);
print "\n";
print_drive_symbols($drive_symbols, "81", @drive_symbols_sorted_by_address);
}
}
return;
}
sub print_segment_symbols
{
my ($symbols, $start, $end, @symbols_sorted_by_address) = @ARG;
foreach my $name (@symbols_sorted_by_address) {
if ($name =~ /config_|status_|loader_zp_first/) {
next;
}
my $address = $symbols->{$name};
if ($address == 0) {
next;
}
if (($address >= $start)
&& ($address <= $end)) {
print_hex_symbol($name, $symbols->{$name});
$symbols->{$name} = 0;
}
}
return;
}
sub print_drive_symbols
{
my ($symbols, $filter, @symbols_sorted_by_address) = @ARG;
foreach my $name (@symbols_sorted_by_address) {
if ($name =~ /$filter$/) {
print_hex_symbol($name, $symbols->{$name});
}
}
return;
}
sub print_symbol
{
my ($name, $address) = @ARG;
my $format = "%-31s = %d%s\n";
printf $format, $name, $address, $ARG[2] ? "; " . $ARG[2] : "";
return;
}
sub print_hex_symbol
{
my ($name, $address) = @ARG;
my $address_length = ($address < 256) ? '2' : '4';
my $format = '%-31s = $%.' . $address_length . "x\n";
printf $format, $name, $address;
return;
}

1408
loader/src/resident.s Executable file

File diff suppressed because it is too large Load diff

1145
loader/src/save.s Normal file

File diff suppressed because it is too large Load diff