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

1443 lines
42 KiB
ArmAsm
Executable file

.fileopt comment, "Loader install code portion"
.fileopt compiler, "CA65"
.fileopt author, "Gunnar Ruthenberg"
__NO_LOADER_SYMBOLS_IMPORT = 1
.include "loader.inc"
.include "../version.inc"
.include "cpu.inc"
.include "cia.inc"
.include "vdc.inc"
.include "basic.inc"; for PETSCII_RETURN
.include "kernal.inc"
CBM1581_8 = $a6e9; the '8' in the 1581's ID string
FD_F_HD_H = $fea4; the 'f' in the CMD FD 2000/4000's ID string or the 'h' in the CMD HD's ID string
.include "hal/hal.inc"
.include "drives/drivecode-common.inc"
.importzp loadaddrlo
.if ONLY_1541_AND_COMPATIBLE = 0
.import cmdfdfix0
.import cmdfdfix1
.import cmdfdfix2
.if !DISABLE_WATCHDOG
.import cmdfdfix3
.import cmdfdfix4
.if ::PLATFORM = diskio::platform::COMMODORE_128
.import cmdfdfix5
.import cmdfdfix6
.endif ; ::PLATFORM = diskio::platform::COMMODORE_128
.endif; !DISABLE_WATCHDOG
.endif; ONLY_1541_AND_COMPATIBLE = 0
USE_GENERIC_DRIVE = 0
.macro itoa4 value
.if (value & $0f > 9)
.byte (value & $0f) + 'a' - 10
.else
.byte (value & $0f) + '0'
.endif
.endmacro
.macro itoa1 value
itoa4 value <> 0
.endmacro
.macro itoa8 value
itoa4 value >> 4
itoa4 value & $0f
.endmacro
.macro itoa16 value
itoa8 value >> 8
itoa8 value & $ff
.endmacro
.segment "DISKIO_INSTALL"
.ifdef INSTADDR
.org INSTADDR - 2
.word * + 2; load address
.endif
.export install
; Install the loader
; in: nothing
; out: c - set on error
; a - status
; x - drive type (one of diskio::drivetype)
; y - if status is diskio::status::OK, zp address of version string address
install: jmp doinstall
; unfortunately, scopes must be defined before using them,
; this is why the actual install code is moved to after the drive code
.scope cbm1541
drivecode41:
.include "drives/drivecode1541.s"
.exportzp ID041
.exportzp CURRTRACK41
.exportzp NUMFILES41
.exportzp SECTORLINKTABLE41
.export topofstack41
.export idleloop41
.export getbytecmp41
.export BLOCKBUFFER41
; symbols used by the saver
.exportzp V1B41
.exportzp V2B41
.exportzp LEDSTATE41
.exportzp FILESECTOR41
.exportzp FILENAME41
.exportzp LINKTRACK41
.exportzp LINKSECTOR41
.exportzp REQUESTEDSECTOR41
.export trkseek41
.export initlink41
.export sertoraw41
.export getbytewdog41
.export getbyte41
.export getbyterts41
.export setbv2b41
.export getblock41
.export idxloop41
.export wdogentr41
.export findfile41
.export loadfile41
.endscope
.if ONLY_1541_AND_COMPATIBLE = 0
.scope cbm1571
drivecode71:
.include "drives/drivecode1571.s"
.exportzp SECTORLINKTABLE71
.exportzp CUSTOMZPBUFFER71
.exportzp CUSTOMUPLOADSIZE71
.export topofstack71
.export idleloop71
.export BLOCKBUFFER71
; symbols used by the saver
.exportzp CURRTRACK71
.exportzp LEDSTATE71
.exportzp CLEARSECTORLINKTABLE71
.exportzp FILENAME71
.exportzp LINKTRACK71
.exportzp LINKSECTOR71
.exportzp DIRSECTORS71
.export bsetv2b71
.export trkseek71
.export initlink71
.export onemhz71
.export getblock71
.export findfile71
.export loadfile71
.export idxloop71
.endscope
.scope cbm1581
drivecode81:
.include "drives/drivecode1581.s"
.exportzp CUSTOMPARAM81
.export dcodinit81
.export BLOCKBUFFER81
.export CUSTOMRECEIVE81
.export loadfile81
; symbols used by the saver
.export filename81
.export swapzp81
.export getblock81
.export initwdog81
.export enablwdg81
.export initcntr81
.export bsyledon81
.export findfile81
.export DIRTRACKS81
.endscope
.endif; ONLY_1541_AND_COMPATIBLE = 0
doinstall: lda #.lobyte(version)
sta loadaddrlo
lda #.hibyte(version)
sta loadaddrlo + 1
BRANCH_IF_NOT_INSTALLED :+
jmp isinstalld
: php; I flag buffer
jsr CLALL
.if PLATFORM = diskio::platform::COMMODORE_128
; set data and filename banks to current program bank
lda MMU_CR
asl
rol
rol
and #$03; BA
tax ; FNBANK
jsr SETBANK
.endif; PLATFORM = diskio::platform::COMMODORE_128
; try the drive as denoted by FA (current drive) first
lda FA
cmp #MIN_DEVICE_NO
bcc :+
cmp #MAX_DEVICE_NO + 1
bcc :++
: lda #MIN_DEVICE_NO; FA does not contain a drive address (MIN_DEVICE_NO..MAX_DEVICE_NO), try MIN_DEVICE_NO first
:
; find first available drive,
; this is done via the high-level open/read/close routines,
; so non-serial bus devices will also respond
sta FA
find1stdrv: pha; buffer active drive device number
lda #0
tax
tay
jsr SETNAM
lda #COMMAND_ERROR_CHANNEL
ldx FA
tay
jsr SETLFS
jsr OPEN
bcc openokay
cmp #OPEN_DEVICENOTPRESENT
beq trynextdev
tax
pla; buffered active drive device number
lda #diskio::status::GENERIC_KERNAL_ERROR
bne installerr; jmp
trynextdev: ; device not present, try next address
lda #COMMAND_ERROR_CHANNEL
jsr CLOSE
jsr CLRCH
ldx FA
inx
cpx #MAX_DEVICE_NO + 1
bne :+
ldx #MIN_DEVICE_NO
: stx FA
pla; buffered active drive device number
cmp FA
bne find1stdrv
lda #diskio::status::DEVICE_NOT_PRESENT
ldx #diskio::drivetype::DEVICE_NONE
installerr: ldy #loadaddrlo
plp; I flag restore
sec
rts
openokay: ldx #COMMAND_ERROR_CHANNEL
jsr CKOUT
jsr READSS
bne trynextdev
jsr CLRCH
; read error channel, this also stops potentially blinking error LED
ldx #COMMAND_ERROR_CHANNEL
jsr CHKIN
: jsr READSS
bne :+
jsr BASIN
jmp :-
: lda #COMMAND_ERROR_CHANNEL
jsr CLOSE
jsr CLRCH
pla; buffered active drive device number, leave FA at first detected present device
.if USE_GENERIC_DRIVE
jmp usegeneric
.endif
.if ONLY_1541_AND_COMPATIBLE = 0
; check if drive allows code upload and execution
jsr chkdrvcode
beq notgeneric
usegeneric: ; no compatible drive found
lda #diskio::status::DEVICE_INCOMPATIBLE
jmp nodrvcode
notgeneric: ; check which model the drive is and upload corresponding drive code
jsr getmodel
sty drivetype
tya
bmi usegeneric
lsr
lsr
lsr
lsr
tax
tya
and #diskio::drivetype::DRIVES_MASK
cmp #diskio::drivetype::DRIVES_1581_CMD
bne not1581cmd
cpy #diskio::drivetype::DRIVE_CMD_HD
beq usegeneric
cpy #diskio::drivetype::DRIVE_1581
beq is1581
lda #OPC_BIT_ABS
sta cmdfdfix0 - cbm1581::drvcodebeg81 + cbm1581::drivecode81
lda #.lobyte($54); DIRTRACKFD
sta cmdfdfix1 - cbm1581::drvcodebeg81 + cbm1581::drivecode81
lda #.hibyte($54); DIRTRACKFD
sta cmdfdfix2 - cbm1581::drvcodebeg81 + cbm1581::drivecode81
.if (::PLATFORM <> diskio::platform::COMMODORE_128) & (!DISABLE_WATCHDOG)
lda #$ff
ldy #.lobyte($1c05)
.endif
jmp iscmdfd
is1581: lda #OPC_JMP_ABS
sta cmdfdfix0 - cbm1581::drvcodebeg81 + cbm1581::drivecode81
lda #.lobyte($022b); DIRTRACK81
sta cmdfdfix1 - cbm1581::drvcodebeg81 + cbm1581::drivecode81
lda #.hibyte($022b); DIRTRACK81
sta cmdfdfix2 - cbm1581::drvcodebeg81 + cbm1581::drivecode81
.if (::PLATFORM <> diskio::platform::COMMODORE_128) & (!DISABLE_WATCHDOG)
lda #COUNT_TA_UNDF | FORCE_LOAD | ONE_SHOT | TIMER_START
ldy #.lobyte(CIA_CRB)
iscmdfd: sta cmdfdfix3 - cbm1581::drvcodebeg81 + cbm1581::drivecode81
sty cmdfdfix4 - cbm1581::drvcodebeg81 + cbm1581::drivecode81
.else
iscmdfd:
.endif
not1581cmd:
lda dcodeselt0,x
sta dcodesel0
lda dcodeselt1,x
sta dcodesel1
lda dcodeselt2,x
sta dcodesel2
lda dcodeselt3,x
sta dcodesel3
lda dcodeselt4,x
sta dcodesel4
lda dcodeselt5,x
sta dcodesel5
lda dcodeselt6,x
sta dcodesel6
lda dcodeselt7,x
sta dcodesel7
lda dcodeselt8,x
sta dcodesel8
lda dcodeselt9,x
sta family
lda dcodeselta,x
sta dirtrack
.else; ONLY_1541_AND_COMPATIBLE
jsr chkdrvcode
beq :+
; no compatible drive found
lda #diskio::status::DEVICE_INCOMPATIBLE
jmp nodrvcode
; check if 1541U
: jsr drvlistn
ldx #0
: lda drvch1541u,x
jsr CIOUT
inx
cpx #drvchkued - drvch1541u
bne :-
jsr UNLSN
lda #.lobyte($0300)
ldx #.hibyte($0300)
jsr memreadbyt
bmi :+; branch if 1541U
lda #diskio::drivetype::DRIVE_1541
SKIPWORD
: lda #diskio::drivetype::DRIVE_1541U
sta drivetype
lda #.lobyte(cbm1541::drvcodeend41 - cbm1541::drvcodebeg41 + cbm1541::drivecode41)
sta dcodesel0
lda #.hibyte(cbm1541::drvcodeend41 - cbm1541::drvcodebeg41 + cbm1541::drivecode41)
sta dcodesel1
lda #.lobyte(cbm1541::drvprgend41 - cbm1541::drvcodeend41 + cbm1541::TRAMPOLINEOFFSET)
sta dcodesel2
lda #.lobyte(cbm1541::drivecode41)
sta dcodesel3
lda #.hibyte(cbm1541::drivecode41)
sta dcodesel4
lda #.hibyte(cbm1541::drvcodeend41 - cbm1541::TRAMPOLINEOFFSET)
sta dcodesel5
lda #.lobyte(cbm1541::drvcodeend41 - cbm1541::TRAMPOLINEOFFSET)
sta dcodesel6
lda #.hibyte(cbm1541::dinstall)
sta dcodesel7
lda #.lobyte(cbm1541::dinstall)
sta dcodesel8
.endif; ONLY_1541_AND_COMPATIBLE
; check if there is more than 1 drive on the serial bus,
; upload silencing routines to the passive drives in order
; to make sure the 2bit+ATN protocol can work alright,
; detection is done via the low-level serial bus routines,
; so non-serial bus devices won't respond
; (1551 on Plus/4 does respond, though, so a little extra
; treatment is done through the drive disturbance HAL macros)
lda FA; active drive
pha
ldx #MIN_DEVICE_NO
checkbus: stx FA
pla
pha
cmp FA
beq jmpnodrive
lda #0
sta STATUS
PREPARE_DRIVE_DISTURBANCE_VALIDATION
jsr drvlistn
BRANCH_IF_DRIVE_DOES_NOT_DISTURB_SERIAL_BUS jmpnodrive
jsr READSS
bpl :+
jmpnodrive: jmp nodrive
; more than 1 drive on the bus or generic serial devices present
: jsr UNLSN
; upload and execute silencing routine
.if ONLY_1541_AND_COMPATIBLE = 0
jsr chkdrvcode
beq mute
mutefail: lda FA
ldx #'0' - 1
sec
: inx
tay
sbc #10
bcs :-
stx mutedevice
tya
adc #'0'
sta mutedevice + 1
ldx #errormute - messages
jsr message
pla
sta FA
lda #diskio::status::TOO_MANY_DEVICES
jmp nodrvcode
mute: jsr getmodel
tya
bmi mutefail
tya
and #diskio::drivetype::DRIVES_MASK
cmp #diskio::drivetype::DRIVES_1581_CMD
beq mute81cmd
pha
.endif; ONLY_1541_AND_COMPATIBLE = 0
jsr drvlistn
ldx #0
: lda drvsilencc,x
jsr CIOUT
inx
cpx #atnfallbck - drvsilencc
bne :-
jsr drvrelistn
ldx #0
: lda atnfallbck,x
jsr CIOUT
inx
cpx #atnlo - atnfallbck
bne :-
jsr drvrelistn
ldx #0
: lda atnlo,x
jsr CIOUT
inx
cpx #atnhi - atnlo
bne :-
jsr drvrelistn
ldx #0
: lda atnhi,x
jsr CIOUT
inx
cpx #atnhiend - atnhi
bne :-
.if ONLY_1541_AND_COMPATIBLE = 0
jsr UNLSN
pla
cmp #diskio::drivetype::DRIVES_157X
bne :++
jsr drvlistn
ldx #0
: lda drvslnc71,x
jsr CIOUT
inx
cpx #drvslnc71e - drvslnc71
bne :-
beq mutexecute; jmp
: jsr drvlistn
.else; ONLY_1541_AND_COMPATIBLE = 0
jsr drvrelistn
.endif; ONLY_1541_AND_COMPATIBLE = 0
ldx #0
: lda drvsilence,x
jsr CIOUT
inx
cpx #drvsilnced - drvsilence
bne :-
.if ONLY_1541_AND_COMPATIBLE = 0
beq mutexecute; jmp
mute81cmd: cpy #diskio::drivetype::DRIVE_1581
bne mutecmd
jsr drvlistn
ldx #0
: lda drvslnc81,x
jsr CIOUT
inx
cpx #drvslnc81e - drvslnc81
bne :-
beq mutexecute; jmp
mutecmd: cpy #diskio::drivetype::DRIVE_CMD_HD
beq mutecmdhd
jsr drvlistn
ldx #0
: lda drvslncfd,x
jsr CIOUT
inx
cpx #drvslncfde - drvslncfd
bne :-
beq mutexecute; jmp
mutecmdhd: jsr drvlistn
ldx #0
: lda drvslnchd,x
jsr CIOUT
inx
cpx #drvslnchde - drvslnchd
bne :-
.endif; ONLY_1541_AND_COMPATIBLE = 0
mutexecute: jsr drvrelistn
ldx #0
: lda drvmuteme,x
jsr CIOUT
inx
cpx #drvmutemed - drvmuteme
bne :-
beq nodrive; jmp
nodrvcode: pha; error code
.if LOAD_VIA_KERNAL_FALLBACK
; quicker head stepping
jsr drvlistn
ldx #6
: lda drvfaststp,x
jsr CIOUT
dex
bpl :-
jsr UNLSN
lda #.lobyte($e5c6)
ldx #.hibyte($e5c6)
jsr memreadbyt
cpx #'1' | $80; 71
bne :++
jsr drvlistn
ldx #4
: lda twosided,x
jsr CIOUT
dex
bpl :-
jsr drvrelistn; make sure the drive is done
jsr UNLSN ; when leaving the init routine
:
.endif; LOAD_VIA_KERNAL_FALLBACK
CHECK_AND_BRANCH_IF_DRIVE_PARALLEL drivepar
.if PLATFORM = diskio::platform::COMMODORE_128
lda burstflag
beq :+
ldx #diskio::drivetype::DRIVE_GENERIC_BURST
SKIPWORD
:
.endif
ldx #diskio::drivetype::DRIVE_GENERIC_SERIAL
SKIPWORD
drivepar: ldx #diskio::drivetype::DRIVE_GENERIC_PARALLEL
pla; error code
plp; I flag restore
ldy #loadaddrlo
.if LOAD_VIA_KERNAL_FALLBACK
clc; this is not to be regarded as an error
.else
sec
.endif
rts
nodrive: jsr UNLSN
ldx FA
inx
cpx #MAX_DEVICE_NO + 1
beq :+
jmp checkbus
: pla
sta FA; active drive
; install drive-side loader code
.if PLATFORM = diskio::platform::COMMODORE_128
lda #0
sta SERIAL
.endif
jsr drvlistn
ldx #0
upload: sec
stx :+ + 1
dcodesel2 = * + 1
lda #0
: sbc #0
cmp #35
bcc :+
lda #35
: sta drvrutmw
ldy #6
: lda drvrutmw - 1,y
jsr CIOUT
dey
bne :-
dcodesel0 = * + 1
dcodesel1 = * + 2
: lda $c001,x
jsr CIOUT
inx
cpx dcodesel2
beq :+
iny
cpy #35
bne :-
jsr drvrelistn
clc
lda #35
adc drvrutmw + 2
sta drvrutmw + 2
bcc upload
inc drvrutmw + 1
bne upload
: jsr drvrelistn
ldx #0
: lda droutrun,x
jsr CIOUT
inx
cpx #droutruned - droutrun
bne :-
jsr UNLSN
INIT_CLEAR_ATN_OUT_CLEAR_CLK_OUT_CLEAR_DATA_OUT
: SET_FLAGS_N_DATA_V_CLK
bvs :-
CLEAR
: SET_FLAGS_N_DATA_V_CLK
bvc :-
dcodesel3 = * + 1
ldy #0
dcodesel4 = * + 2
fastinst: lda $00,y
SENDBYTE
iny
bne :+
inc fastinst + 2
: cpy dcodesel0
bne fastinst
lda fastinst + 2
cmp dcodesel1
bne fastinst
INSTALL_IDLE
.if PLATFORM = diskio::platform::COMMODORE_128
burstflag = * + 1
lda #0
.if ONLY_1541_AND_COMPATIBLE = 0
bne burst
.endif
DISABLE_BURST_MODE
burst:
.endif; PLATFORM = diskio::platform::COMMODORE_128
; if 1541U is detected, it runs on buggy firmware
lda drivetype
cmp #diskio::drivetype::DRIVE_1541U
bne :+
ldx #warnemubug - messages
jsr message
: plp; I flag restore
isinstalld: lda #diskio::status::OK
drivetype = * + 1
ldx #0
ldy #loadaddrlo
clc
rts
message: lda BORDERCOLOUR
pha
lda BGCOLOUR
pha
lda #COLOUR_RED
sta BORDERCOLOUR
sta BGCOLOUR
.if PLATFORM = diskio::platform::COMMODORE_16
lda TED_CTRL1
pha
lda #TEXT_MODE | DISPLAY_ENABLE | LINES_25 | SCROLLY_3; $1b
sta TED_CTRL1
lda TED_CTRL2
pha
and #.lobyte(~MULTICOLOUR_MODE)
ora #COLUMNS_40
sta TED_CTRL2
lda TED_BITMAP_ADDR
pha
ora #CHARSET_BITMAP_IN_ROM
sta TED_BITMAP_ADDR
lda TED_CHARGEN_ADDR
pha
and #.lobyte(~CHARGEN_ADDR_MASK)
ora #MAKE_CHARGEN_ADDR(CHARSET_ADDR_UPPERLOWER)
sta TED_CHARGEN_ADDR
lda TED_SCREEN_ADDR
pha
and #.lobyte(~SCREEN_ADDR_MASK)
ora #MAKE_SCREEN_ADDR($0c00)
sta TED_SCREEN_ADDR
lda PALETTE
pha
lda #PALETTE_DEFAULT
sta PALETTE
.else; PLATFORM <> diskio::platform::COMMODORE_16
.if PLATFORM = diskio::platform::COMMODORE_128
lda #FG_BG
sta VDC_CR
: bit VDC_SR; wait until update ready
bpl :-
lda VDC_DR
pha
lda #RED
sta VDC_DR
.endif ; PLATFORM = diskio::platform::COMMODORE_128
lda VIC2_CTRL1
and #.lobyte(~RASTERLINE_BIT8)
pha
lda VIC2_CTRL2
pha
lda VIC2_ADDR
pha
lda CIA2_PRA
and #VIC2_BANK
pha
lda #TEXT_MODE | DISPLAY_ENABLE | LINES_25 | SCROLLY_3; $1b
sta VIC2_CTRL1
lda #SINGLECOLOUR_MODE | COLUMNS_40 | SCROLLX_0; $08
sta VIC2_CTRL2
lda #VIC2_MAKE_ADDR($0400, CHARSET_ADDR_UPPERLOWER)
sta VIC2_ADDR
lda #(VIC2_MAKE_BANK $0400) & VIC2_BANK
sta CIA2_PRA
.endif; PLATFORM <> diskio::platform::COMMODORE_16
lda DFLTO
pha
lda COLOR
pha
lda #DEVICE_SCREEN
sta DFLTO
: lda messages,x
beq :+
jsr BSOUT
.if PLATFORM = diskio::platform::COMMODORE_16
lda #BLINK
ora COLOR
sta COLOR
.endif; PLATFORM = diskio::platform::COMMODORE_16
inx
bne :-
: pla
sta COLOR
pla
sta DFLTO
lda #8
: dex
bne :-
dey
bne :-
sbc #1
bne :-
.if PLATFORM = diskio::platform::COMMODORE_16
pla
sta PALETTE
pla
sta TED_SCREEN_ADDR
pla
sta TED_CHARGEN_ADDR
pla
sta TED_BITMAP_ADDR
pla
sta TED_CTRL2
pla
sta TED_CTRL1
.else; PLATFORM <> diskio::platform::COMMODORE_16
pla
sta CIA2_PRA
pla
.if PLATFORM = diskio::platform::COMMODORE_128
sta VM1
.endif ; PLATFORM = diskio::platform::COMMODORE_128
sta VIC2_ADDR
pla
sta VIC2_CTRL2
pla
sta VIC2_CTRL1
.if PLATFORM = diskio::platform::COMMODORE_128
lda #FG_BG
sta VDC_CR
: bit VDC_SR; wait until update ready
bpl :-
pla
sta VDC_DR
.endif ; PLATFORM = diskio::platform::COMMODORE_128
.endif; PLATFORM <> diskio::platform::COMMODORE_16
pla
sta BGCOLOUR
pla
sta BORDERCOLOUR
rts
drvrelistn: jsr UNLSN
drvlistn: lda FA
jsr LISTN
.if PLATFORM = diskio::platform::COMMODORE_128
lda SERIAL
sta burstflag
.endif
lda #SA_OPENCHANNEL | COMMAND_ERROR_CHANNEL
jmp SECND
memreadbyt: sta drvchkmr + 3
stx drvchkmr + 4
lda #drvchkmred - drvchkmr
ldx #.lobyte(drvchkmr)
ldy #.hibyte(drvchkmr)
jsr SETNAM
lda #COMMAND_ERROR_CHANNEL
ldx FA
tay
jsr SETLFS
jsr OPEN
bcc :+
lda #0
tax
rts
: ldx #COMMAND_ERROR_CHANNEL
jsr CHKIN
jsr BASIN
pha
jsr BASIN
pha
lda #COMMAND_ERROR_CHANNEL
jsr CLOSE
jsr CLRCH
pla
tax
pla
clc
rts
drvchkmr: .byte "m-r", $00, $00, 2
drvchkmred:
.if ONLY_1541_AND_COMPATIBLE = 0
getmodel: ; check if running on a 1541/70/71 compatible drive
lda #.lobyte($e5c6)
ldx #.hibyte($e5c6)
jsr memreadbyt
cmp #'4'
bne not1541
; find out if 1541, 1541-C or 1541-II
lda #.lobyte($c002)
ldx #.hibyte($c002)
jsr memreadbyt
cmp #'c'
beq chk1541ii; branch if 'c' at $c002 (from 'COPYRIGHT' etc.)
; find out if 1541 or 1541-C
lda #.lobyte($eaa3)
ldx #.hibyte($eaa3)
jsr memreadbyt
ldy #diskio::drivetype::DRIVE_1541
cmp #$ff
beq check1541u
; 1541-C: no $ff at $eaa3 (but likely $fe, data direction for track 0 sensor bit)
ldy #diskio::drivetype::DRIVE_1541_C
beq check1541u
chk1541ii: lda #.lobyte($e5b7)
ldx #.hibyte($e5b7)
jsr memreadbyt
tax
lda #$ff
cpx #'c' | $80; 'CBM DOS' etc.
bne :+; treat as 1541-II if no match, so as not to detect JiffyDOS, SpeedDOS etc. as 1541-C but 1541-II instead
; find out if 1541-C or 1541-II
lda #.lobyte($eaa3)
ldx #.hibyte($eaa3)
jsr memreadbyt
: ldy #diskio::drivetype::DRIVE_1541_C
cmp #$ff
bne check1541u; 1541-C: no $ff at $eaa3 (but likely $fe, data direction for track 0 sensor bit)
iny; diskio::drivetype::DRIVE_1541_II: $ff at $eaa3
; check for discrete drive logics vs gate array
sty model1541
jsr drvlistn
ldx #0
: lda dchk1541ii,x
jsr CIOUT
inx
cpx #dch1541iie - dchk1541ii
bne :-
jsr UNLSN
lda #.lobyte($0300)
ldx #.hibyte($0300)
jsr memreadbyt
beq :+; branch if 1541-II
ldy #diskio::drivetype::DRIVE_1541
check1541u: sty model1541
: jsr drvlistn
ldx #0
: lda drvch1541u,x
jsr CIOUT
inx
cpx #drvchkued - drvch1541u
bne :-
jsr UNLSN
lda #.lobyte($0300)
ldx #.hibyte($0300)
jsr memreadbyt
bmi :+; branch if 1541U
model1541 = * + 1
ldy #0
SKIPWORD
: ldy #diskio::drivetype::DRIVE_1541U
rts
not1541: cmp #'7'
bne not157x
; find out if 1570 or 1571
cpx #'1' | $80; 71
ldy #diskio::drivetype::DRIVE_1570
bcc is1570
; 1571 or 1571CR
lda #.lobyte($e5c2)
ldx #.hibyte($e5c2)
jsr memreadbyt
cmp #'1'; 3.1
ldy #diskio::drivetype::DRIVE_1571
bcc :+
iny; diskio::drivetype::DRIVE_1571CR
:
is1570: rts
not157x: ; neither 1541 nor 157x
; try FD2000/FD4000
lda #.lobyte(FD_F_HD_H)
ldx #.hibyte(FD_F_HD_H)
jsr memreadbyt
cmp #'f'
beq :+
cmp #'h'
bne check1581
ldy #diskio::drivetype::DRIVE_CMD_HD
rts
: lda #.lobyte($fef0)
ldx #.hibyte($fef0)
jsr memreadbyt
ldy #diskio::drivetype::DRIVE_CMD_FD_2000
cmp #'4'
bne isfd2000
iny; diskio::drivetype::DRIVE_CMD_FD_4000
isfd2000: rts
; check if 1581
check1581: lda #.lobyte(CBM1581_8)
ldx #.hibyte(CBM1581_8)
jsr memreadbyt
ldy #diskio::drivetype::DRIVE_1581
cmp #'8'
bne :+
lda #diskio::status::DEVICE_INCOMPATIBLE
: rts
dchk1541ii: .byte "m-e", .lobyte($0205), .hibyte($0205)
sei
lda $1c0c
ldx #$ec; disable byte sync: clear SOE
stx $1c0c
ldy #$01
bit $1c01
ldx $1c01
: cpx $1c01; with disabled byte sync, $1c01 does not change on a 1541-II
bne :+
iny
bne :-
: sty $0300; if not 0, not a 1541-II (discrete drive logics rather than gate array)
sta $1c0c
cli
rts
dch1541iie:
.assert (* - dchk1541ii) <= 41, error, "dchk1541ii too big"
.endif ; ONLY_1541_AND_COMPATIBLE = 0
; may be executed on 1570/71 with ONLY_1541_AND_COMPATIBLE
drvch1541u: .byte "m-e", .lobyte($0205), .hibyte($0205)
sei
;ldy #0
dey
sty $0300
sty $1803; set all port pins as outputs
lda #$a4; bit 0 may be forced to GND (1541-II) or connected to track 0 sensor (1541-C, normally 0 = not on track 0)
sta $1801
cmp $1801
bne is1541u
anc #$8a; and #imm, but no asl/rol, bit 7 of result goes to carry
beq is1541u
bcc is1541u
tya
arr #$7f; bit 6 of result goes to carry
ror $0300
is1541u: lda #$66; 1570/71 data directions
sta $1803
; no cli
rts
drvchkued:
.assert (* - drvch1541u) <= 41, error, "drvch1541u too big"
drvmuteme: .byte "m-e", .lobyte($020b), .hibyte($020b), "krill."
sei
jmp $0300
drvmutemed:
drvsilence: .byte "m-w", .lobyte($0300), .hibyte($0300), drvsilnced - (* + 1)
cli
ldy #$7f
sty $180e; no IRQs from VIA1
sty $1c0e; no IRQs from VIA2
sty $400d; no IRQs from CIA/MOS5710
sei
sty $1802; set only ATN_IN as input
lda #$ff
sta $1803; set all $1801 port pins as outputs
ldx #.hibyte($0400); CLK_IN, ATNA_OUT cleared, CLK_OUT low, DATA_OUT low
stx $1801
ldy #$d0 ; JOBCODE_EXECUTE
jmp $043b; waitactive
drvsilnced:
.assert (* - drvsilence) <= 41, error, "drvsilence too big"
.if ONLY_1541_AND_COMPATIBLE = 0
drvslnc71: .byte "m-w", .lobyte($0300), .hibyte($0300), drvslnc71e - (* + 1)
cli
ldy #$7f
sty $180e; no IRQs from VIA1
sty $1c0e; no IRQs from VIA2
sty $400d; no IRQs from CIA/MOS5710
sei
sty $1802; set only ATN_IN as input
lda #$20 ; 2 MHz mode
ora $1801
sta $1801
lda #$ff
ldx #.hibyte($0400); CLK_IN, ATNA_OUT cleared, CLK_OUT low, DATA_OUT low
ldy #$d0 ; JOBCODE_EXECUTE
jmp $043b; waitactive
drvslnc71e:
.assert (* - drvslnc71) <= 41, error, "drvslnc71 too big"
.endif ; ONLY_1541_AND_COMPATIBLE = 0
drvsilencc: .byte "m-w", .lobyte($043b), .hibyte($043b), atnfallbck - (* + 1)
waitactive:;lda #$ff
sta $1c06; VIA2 timer 1 latch lo
jsr $0407; waitactpr
ldy #$90 ; ATNA_OUT set, CLK_OUT low, DATA_OUT low
: ;ldx #.hibyte($0400); CLK_IN, ATNA_OUT cleared, CLK_OUT low, DATA_OUT low
stx $1800; clear ATNA_OUT, set CLK_IN
: bit $1800
bpl :-
;ldy #$90 ; ATNA_OUT set, CLK_OUT low, DATA_OUT low
sty $1800; set ATNA_OUT, clear CLK_IN
;lda #$ff
sta $1c05; timer 1 hi
: bit $1800
bpl :---
bit $1c05; timer 1 hi
bne :-
cmp $1803
.assert (* - drvsilencc) <= 41, error, "waitactive too big"
atnfallbck: .byte "m-w", .lobyte($043b + atnfallbck - waitactive), .hibyte($043b + atnfallbck - waitactive), atnlo - (* + 1)
bne atnloop
cpx $1801; check if fast silencing using jmp ($1800) is possible
bne :+
cpy $1800
beq * + 32; silncentry: jmp ($1800)
; slower fallback silence routine, this is required on older 1541U firmware revisions at least, which do not implement VIA1 port A correctly,
; it may also be required for drives modded with a parallel connection,
; this is executed unconditionally on 1570/71/CR at 2 MHz
: inc $1803; set all $1801 port pins to input
atnloop: bit $1800
bmi atnloop
stx $1800; clear ATNA_OUT
;lda #$ff
sta $1c05; timer 1 hi
ENABLE_WATCHDOG
: bit $1800
bpl :-
sty $1800; set ATNA_OUT
.assert (* - atnfallbck) <= 41, error, "atnfallbck too big"
atnlo: .byte "m-w", .lobyte($0400), .hibyte($0400), atnhi - (* + 1)
jmp ($fffc)
.byte 0
jmp ($1800); $0404, $04 = CLK_IN, jump to $0404 or $0484
waitactpr: ;ldy #$d0 ; JOBCODE_EXECUTE
sty $01 ; JOBCODE0400
ldy #$c0 ; timer 1 IRQs from VIA2
sty $1c0e
bne :+; jmp
; $0410, $10 = ATNA_OUT
; ATN_IN is clear, but ATNA_OUT is set: clear ATNA_OUT and reset timer
stx $1800; x = $04 = CLK_IN
sta $1c05; timer 1 hi
ENABLE_WATCHDOG
jmp ($1800); jump to $0404 or $0484
: ldy #1
sty $3f; JOBN, required on 1571 so code in the correct buffer ($0400) is executed upon interrupt
rts
.assert (* - atnlo) <= 41, error, "atnlo too big"
atnhi: .byte "m-w", .lobyte($0481), .hibyte($0481), atnhiend - (* + 1)
sei
bmi * - 21; jmp, atnloop
; $0484, $84 = ATN_IN | CLK_IN
; ATN_IN is set, but ATNA_OUT is cleared: set ATNA_OUT
sty $1800; y = $90 = ATN_IN | ATNA_OUT
sei
silncentry: jmp ($1800); jump to $0410 or $0490
.byte 0, 0, 0, 0, 0
jmp ($1800); $0490, $90 = ATN_IN | ATNA_OUT, jump to $0410 or $0490
atnhiend:
.assert (* - atnhi) <= 41, error, "atnhi too big"
.if ONLY_1541_AND_COMPATIBLE = 0
drvslnc81: .byte "m-w", .lobyte($0300), .hibyte($0300), drvslnc81e - drvmute81
drvmute81: ;ldy #0
sty $4001
dey
sty $4007
lda #$19
slnc81loop: sta $400f
: bit $4001
slnc81sens: bpl slnc81loop; first, wait for a long period of ATN_IN set
ldx $4007
bne :-
ldx #OPC_BMI
cpx slnc81sens - drvmute81 + $0300
stx slnc81sens - drvmute81 + $0300
.if DISABLE_WATCHDOG
jmp slnc81loop
.else
bne slnc81loop
jmp ($fffc)
.endif
drvslnc81e:
.assert (* - drvslnc81) <= 41, error, "drvslnc81 too big"
drvslncfd: .byte "m-w", .lobyte($0300), .hibyte($0300), drvslncfde - drvmutefd
drvmutefd: ldx #0
stx $4001
dex
slncfdloop: stx $4005
: bit $4001
slncfdsens: bpl slncfdloop
lda $4005
bne :-
lda #OPC_BMI
cmp slncfdsens - drvmutefd + $0300
sta slncfdsens - drvmutefd + $0300
.if DISABLE_WATCHDOG
jmp slncfdloop
.else
bne slncfdloop
jmp ($fffc)
.endif
drvslncfde:
.assert (* - drvslncfd) <= 41, error, "drvslncfd too big"
drvslnchd: .byte "m-w", .lobyte($0300), .hibyte($0300), drvslnchde - drvmutehd
drvmutehd: ldx #0
stx $8000
dex
slnchdloop: stx $8005
: bit $8000
slnchdsens: bpl slnchdloop
lda $8005
bne :-
lda #OPC_BMI
cmp slnchdsens - drvmutehd + $0300
sta slnchdsens - drvmutehd + $0300
.if DISABLE_WATCHDOG
jmp slnchdloop
.else
bne slnchdloop
jmp ($fffc)
.endif
drvslnchde:
.assert (* - drvslnchd) <= 41, error, "drvslnchd too big"
dcodeselt0: .byte .lobyte(cbm1541::drvcodeend41 - cbm1541::drvcodebeg41 + cbm1541::drivecode41)
.byte .lobyte(cbm1571::drvcodeend71 - cbm1571::drvcodebeg71 + cbm1571::drivecode71)
.byte .lobyte(cbm1581::drvcodeend81 - cbm1581::drvcodebeg81 + cbm1581::drivecode81)
dcodeselt1: .byte .hibyte(cbm1541::drvcodeend41 - cbm1541::drvcodebeg41 + cbm1541::drivecode41)
.byte .hibyte(cbm1571::drvcodeend71 - cbm1571::drvcodebeg71 + cbm1571::drivecode71)
.byte .hibyte(cbm1581::drvcodeend81 - cbm1581::drvcodebeg81 + cbm1581::drivecode81)
dcodeselt2: .byte .lobyte(cbm1541::drvprgend41 - cbm1541::drvcodeend41 + cbm1541::TRAMPOLINEOFFSET)
.byte .lobyte(cbm1571::drvprgend71 - cbm1571::drvcodeend71 + cbm1571::TRAMPOLINEOFFSET)
.byte .lobyte(cbm1581::drvprgend81 - cbm1581::drvcodeend81)
dcodeselt3: .byte .lobyte(cbm1541::drivecode41)
.byte .lobyte(cbm1571::drivecode71)
.byte .lobyte(cbm1581::drivecode81)
dcodeselt4: .byte .hibyte(cbm1541::drivecode41)
.byte .hibyte(cbm1571::drivecode71)
.byte .hibyte(cbm1581::drivecode81)
dcodeselt5: .byte .hibyte(cbm1541::drvcodeend41 - cbm1541::TRAMPOLINEOFFSET)
.byte .hibyte(cbm1571::drvcodeend71 - cbm1571::TRAMPOLINEOFFSET)
.byte .hibyte(cbm1581::drvcodeend81)
dcodeselt6: .byte .lobyte(cbm1541::drvcodeend41 - cbm1541::TRAMPOLINEOFFSET)
.byte .lobyte(cbm1571::drvcodeend71 - cbm1571::TRAMPOLINEOFFSET)
.byte .lobyte(cbm1581::drvcodeend81)
dcodeselt7: .byte .hibyte(cbm1541::dinstall)
.byte .hibyte(cbm1571::dinstall)
.byte .hibyte(cbm1581::dinstall)
dcodeselt8: .byte .lobyte(cbm1541::dinstall)
.byte .lobyte(cbm1571::dinstall)
.byte .lobyte(cbm1581::dinstall)
dcodeselt9: .byte 41
.byte 71
.byte 81
dcodeselta: .byte DIRTRACK
.byte DIRTRACK
.byte DIRTRACK81
.endif; ONLY_1541_AND_COMPATIBLE = 0
; check if drive allows code upload and execution
chkdrvcode: lda #.lobyte($0300)
ldx #.hibyte($0300)
jsr memreadbyt
bcc :+
lda #$ff
rts
: eor #$ff
sta drvchkval + 1
jsr drvlistn
ldx #0
: lda drvchkme,x
jsr CIOUT
inx
cpx #drvchkmed - drvchkme
bne :-
jsr UNLSN
lda #.lobyte($0300)
ldx #.hibyte($0300)
jsr memreadbyt
drvchkval: cmp #0
rts
drvchkme: .byte "m-e", .lobyte($020a), .hibyte($020a), "krill"
lda #$ff
eor $0300
sta $0300
rts
drvchkmed:
.assert (* - drvchkme) <= 41, error, "drvchkme too big"
.if LOAD_VIA_KERNAL_FALLBACK
drvfaststp: .byte MINSTEPSPEED, $01, .hibyte($1c07), .lobyte($1c07), "w-m"; read backward
twosided: .byte "1m>0u"; read backward, enable 1571 mode to read two-sided disks
.endif; LOAD_VIA_KERNAL_FALLBACK
dcodesel5 = * + 1
dcodesel6 = * + 2
drvrutmw: .byte 35, 0, 0, "w-m"; read backward
droutrun: .byte "m-e", .lobyte($0209), .hibyte($0209), "krill"
dcodesel7 = * + 1
dcodesel8 = *
.byte $00, $00, .lobyte(REPOSITORY_VERSION), .hibyte(REPOSITORY_VERSION), PLATFORM
family: .byte 41
dirtrack: .byte DIRTRACK, FILENAME_MAXLENGTH, CONFIG_INTERNAL
droutruned:
messages:
.if ONLY_1541_AND_COMPATIBLE = 0
errormute: .byte PETSCII_LO_UP_CASE
.if PLATFORM = diskio::platform::COMMODORE_128
.byte PETSCII_BLINK
.endif
.if PLATFORM = diskio::platform::COMMODORE_16
.byte PETSCII_ORANGE
.else
.byte PETSCII_WHITE
.endif
.byte "ERROR: Failed to mute device #"
mutedevice: .byte "00.", 0
.endif; ONLY_1541_AND_COMPATIBLE = 0
warnemubug: .byte PETSCII_LO_UP_CASE
.if PLATFORM = diskio::platform::COMMODORE_128
.byte PETSCII_BLINK
.endif
.if PLATFORM = diskio::platform::COMMODORE_16
.byte PETSCII_ORANGE
.else
.byte PETSCII_WHITE
.endif
.byte "WARNING: Buggy "
.if PLATFORM = diskio::platform::COMMODORE_64
.byte "1541U firmware"
.else
.byte "emulator"
.endif
.byte " detected. Please update.", 0
version: .byte "Krill's Loader, revision ", REPOSITORY_VERSION_STRING, PETSCII_RETURN, "cfg "
itoa4 MIN_DEVICE_NO
itoa8 MAX_DEVICE_NO
itoa1 ONLY_1541_AND_COMPATIBLE
.byte '.'
itoa8 DIRTRACK
itoa8 DIRTRACK81
itoa8 FILENAME_MAXLENGTH
.byte '.'
itoa8 MINSTEPSPEED
itoa8 MAXSTEPSPEED
itoa4 STEPPERACC
itoa4 SINGLESTEPSPEED
.byte '.'
itoa4 (2 * LOAD_COMPD_API) + LOAD_RAW_API
itoa1 NTSC_COMPATIBILITY
itoa1 PREFER_SPEED_OVER_SIZE
itoa1 UNINSTALL_API
itoa1 FILE_EXISTS_API
itoa1 LOAD_UNDER_D000_DFFF
itoa1 ALLOW_2_MHZ_ON_C128
itoa4 (2 * MEM_DECOMP_TO_API) + MEM_DECOMP_API
itoa1 (2 * LOAD_TO_API) + END_ADDRESS_API
itoa1 LOAD_VIA_KERNAL_FALLBACK
itoa1 CLOSE_FILE_API
itoa8 CONFIG_INTERNAL
.byte '.'
itoa4 DECOMPRESSOR
.byte 0
CHECK_INSTALL_END_ADDRESS
.exportzp status_OK = diskio::status::OK
.exportzp status_DEVICE_INCOMPATIBLE = diskio::status::DEVICE_INCOMPATIBLE
.exportzp status_TOO_MANY_DEVICES = diskio::status::TOO_MANY_DEVICES
.exportzp status_GENERIC_KERNAL_ERROR = diskio::status::GENERIC_KERNAL_ERROR
.exportzp status_DEVICE_NOT_PRESENT = diskio::status::DEVICE_NOT_PRESENT
.exportzp status_FILE_NOT_FOUND = diskio::status::FILE_NOT_FOUND
.if PLATFORM <> diskio::platform::COMMODORE_16
.exportzp config_ALLOW_2_MHZ_ON_C128 = ALLOW_2_MHZ_ON_C128
.endif
.exportzp config_DECOMPRESSOR = DECOMPRESSOR
.exportzp config_DIRTRACK = DIRTRACK
.exportzp config_DIRTRACK81 = DIRTRACK81
.exportzp config_END_ADDRESS_API = END_ADDRESS_API
.exportzp config_FILENAME_MAXLENGTH = FILENAME_MAXLENGTH
.exportzp config_FILE_EXISTS_API = FILE_EXISTS_API
.exportzp config_INTERNAL = CONFIG_INTERNAL
.exportzp config_LOAD_COMPD_API = LOAD_COMPD_API
.exportzp config_LOAD_RAW_API = LOAD_RAW_API
.exportzp config_LOAD_TO_API = LOAD_TO_API
.exportzp config_LOAD_UNDER_D000_DFFF = LOAD_UNDER_D000_DFFF
.exportzp config_LOAD_VIA_KERNAL_FALLBACK = LOAD_VIA_KERNAL_FALLBACK
.exportzp config_MEM_DECOMP_API = MEM_DECOMP_API
.exportzp config_MEM_DECOMP_TO_API = MEM_DECOMP_TO_API
.exportzp config_NTSC_COMPATIBILITY = NTSC_COMPATIBILITY
.exportzp config_ONLY_1541_AND_COMPATIBLE = ONLY_1541_AND_COMPATIBLE
.exportzp config_PREFER_SPEED_OVER_SIZE = PREFER_SPEED_OVER_SIZE
.exportzp config_UNINSTALL_API = UNINSTALL_API