SJLOAD/SJLOAD.ASM
From C64-Wiki
Jump to navigationJump to search
; SJLOAD - C64 floppy speeder and disk utility
; based on VDOS by Edward Carroll 1986
; modified for Jiffy compatibility by 1570 in 2008
; todo
; move core routines above $e000 instead of $cb00
; much cleanup
; xa65 assembler format
; Usage:
; LOAD"!*PROGRAM",8,1 - autostart VDOS, fastload PROGRAM
; LOAD"!",8 RUN - save autostarting VDOS to (new) disk
; VERIFY - send commands, read floppy status
; VERIFY"$" - display directory
; VDOS uses memory at CB00 and therefore limits length
; of programs loaded to basic start to about 195 blocks.
.word $031a
* = $031a
; BASIC header SYS 2169 ($0879)
; In case this gets loaded to BASIC start (,8)
; Will overwrite IOPEN, ICLOSE, ICHKIN, ICHKOUT,
; ICLRCH, IBASIN vectors otherwise
firstbyte:
.byt $0b,$08,$d8
.byt $07,$9e,$32
.byt $31,$36,$39
.byt $00,$00,$00
; IBASOUT/CHROUT vector ($0326)
.word autostart
; ISTOP ($0328)
.word $f6ed
; IGETIN ($032A)
.word $f13e
; ICLALL ($032C)
.word $f32f
; USRCMD ($032E)
.word $fe66
; ILOAD ($0330)
iload: .word $f4a5
; ISAVE ($0332)
.word $f5ed
; $DD00 bits
; 0..1 VIC bank
; 2 RS-232 TXD Ausgang, User PA 2
; 3..5 IEC OUT. 0=High/Inactive, 1=Low/Active.
; 3 ATN OUT
; 4 CLOCK OUT
; 5 DATA OUT
; 6..7 IEC IN. 0=Low/Active, 1=High/Inactive.
; 6 CLOCK IN
; 7 DATA IN
autostart:
jsr $fd15 ; restore kernal vectors
sei
lda #$30
sta $01
l33c ldy #$00
l33e lda da80code,y
l341 sta $da80,y
iny
bne l33e
inc l33e+2
inc l341+2
lda l341+2
cmp #$e0
bne l33c
lda #$00
sta $0800 ; remove garbage in $0800 confusing RUN
l359 jsr ldace ; install helpers, display message
jsr $a533 ; rechain basic lines
lda $22
clc
adc #$02
sta $2d
lda $23
adc #$00
sta $2e
jsr $a660 ; clear screen
ldy $b7 ; number of characters in filename
dey
bne autoload ; b.i. more than 1 character in filename
l374 jmp ($a002) ; basic warm start
autoload:
dey
sty $b7 ; number of characters in filename
lda $bb
clc
adc #$02
sta $bb ; advance filename pointer by two chars ("!*")
bcc l385
l383 inc $bc
l385 lda #$01
sta $b9 ; current sa
lda #$a4
pha
lda #$7f
pha
jmp $e16f ; load (will jump through iload)
; Code called by BASIC stub.
; Just copies everything to standard location and saves.
; Located at $0879.
l392 lda #$61
sta $b9 ; Secondary address ($61 = SAVE)
lda #$01
ldx #<filename
ldy #>filename
jsr $ffbd ; Set Filename
ldy #$00
l3a1 lda $0801,y
sta firstbyte+$0000,y
lda $0901,y
sta firstbyte+$0100,y
lda $0a01,y
sta firstbyte+$0200,y
lda $0b01,y
sta firstbyte+$0300,y
lda $0c01,y
sta firstbyte+$0400,y
iny
bne l3a1
l3c2 jsr $f3d5 ; Send Secondary Address
lda #<firstbyte
sta $c1
lda #>firstbyte
sta $c2 ; Set start address
lda #<$07ff ; TODO use proper end address
sta $ae
lda #>$07ff
sta $af ; Set end address
jsr $f60b ; Save (without printing "SAVING")
lda #$01
sta $b7 ; Number of characters in filename
jmp autostart
filename:
da80code = * + 1
.byt $21
* = $da80
lda80 jsr lda89 ; swap cb00.. and db00..
jsr ldaf2 ; move helper to 02e1
jmp lcd6a ; display message, install load vector, exit
; swap cb00.. and db00..
lda89 php
pha
stx ldafe
sty ldaff
lda #$db
sta ldaa7
sta ldaae
lda #$cb
sta ldaab
sta ldab2
ldx #$05
ldy #$00
ldaa5 ldaa7 = * + 2
lda $db00,y
pha
ldaab = * + 2
lda $cb00,y
ldaae = * + 2
sta $db00,y
pla
ldab2 = * + 2
sta $cb00,y
iny
bne ldaa5
ldab6 inc ldaa7
inc ldaae
inc ldaab
inc ldab2
dex
bne ldaa5
ldac5 ldx ldafe
ldy ldaff
pla
plp
ldace = * + 1
rts
; this code portion is relocatable and called both at $dace and $02e1
* = $02e1
clc
.byt $24 ; bit $xx
; iload vector
vload: sec
sei
pha
lda $01
sta $92
lda #$30
sta $01
bcc vlnm
jmp ldaec ; swap cb00/db00, load/verify
vlnm: jmp lda80 ; install helper, display message
l2f6 jsr lda89 ; swap cb00.. and db00..
lda $92
sta $01
cli
rts
* = $daec
ldaec jsr lda89 ; swap cb00.. and db00..
jmp lcc05 ; load/verify
; move helper to 02e1
ldaf2 ldy #$1e
ldaf4 lda ldace,y
sta $02e1,y
dey
bpl ldaf4
ldafd rts
ldafe .byt $00
ldaff .byt $00
; db00
* = $cb00
lcb00 rts
lcb01 ora $fd,x
listDir:
bit $c5
bvc listDir
lda #$60
sta $b9 ; current sa
jsr $f3d5 ; Send Secondary Address
lcd96 lda $ba ; device number
jsr $ffb4 ; Command Serial Bus TALK
lda $b9 ; current sa
jsr $ff96 ; Send SA After Talk
; jsr lcd96 (was doubled in VDOS?)
jsr ldgc
jsr ldgc
lcb13 lda #$0d
jsr $e10c ; Output character
lda #$0a
jsr $e10c ; Output character
lda #$20
jsr $e10c ; Output character
jsr ldgc
jsr ldgc
jsr ldgc
tax
jsr ldgc
jsr $bdcd ; output number in FAC
lda #$20
jsr $e10c ; Output character
lcb2c jsr ldgc
beq lcb13
lcb31 ldy $d3
cpy #$19
bne lcb42
lcb37 cmp #$20
beq lcb42
lcb3b pha
lda #$3a ; ":"
jsr $ffd2 ; chrout
pla
lcb42 jsr $ffd2 ; chrout
jmp lcb2c
; get serial byte (with error handling)
ldgc: jsr $ffa5
bit $90
bvs lcb68
lcb4f bit $91
bpl readError
lcb53 bit $c5
bvs lcb63
lcb57 bit $c5
bvc lcb57
lcb5b bit $c5
bvs lcb5b
lcb5f bit $c5
bvc lcb5f
lcb63 tay
rts
; read error channel
readError:
jsr $aad7 ; output cr/lf
lcb68 jsr $ffab ; untalk
jsr $f642 ; in save (send LISTEN?)
lcb6e jsr $aad7 ; output cr/lf
jsr $ab3f ; output format character
lda $ba ; device number
jsr $ffb4 ; talk
lda #$6f
jsr $ff96 ; send sa after talk
lcb7e jsr $ffa5 ; handhake serial byte in
jsr $ffd2 ; chrout
cmp #$0d
bne lcb7e
lcb88 jsr $ffab ; untalk
lda #$00
sta $c6 ; number of chars in keyboard buffer
vExit: ldx #$fb
txs
lda #>$a473
ldy #<$a473 ; Restart BASIC ($a474)
jmp returnOut
; send drive command
sendCmd:
lda #$6f
pha
lda $ba ; device number
jsr $ffb1 ; Command Serial Bus LISTEN
pla
jsr $ff93 ; Send SA After Listen
ldy #$00
lcd4d lda ($bb),y
jsr $ffa8 ; handshake serial byte out
iny
cpy $b7 ; number of characters in filename
bcc lcd4d
jsr $ffae ; unlisten
jmp vExit
; load/verify
lcc05 lda #$37
sta $01
cli
pla
load sta $93 ; load/verify flag
lda #$00
sta $90 ; iec status
lda $ba ; device number
bne cdn
jmp $f713
cdn cmp #$03
bne lload
bcs lload
jmp $f4af
; actual LOAD routine
lload ldx $ba ; device number
cpx #$01 ; device 1 (default)? Override with 8.
bne vnstd
ldx #$08
stx $ba ; device number
vnstd: lda $93 ; load/verify
beq lnv ; b.i. load
lda $b7 ; number of characters in filename
bne vsc
jmp readError
vsc: ldy #$00
lda ($bb),y ; file name
cmp #$24
bne sendCmd
jmp listDir
lnv: ldx $b7 ; length of filename
bne lfnok
lda #>$f712
ldy #<$f712
jmp returnOut ; illegal device number ($f713)
lfnok ldx $b9 ; secondary address
jsr $f5af ; searching for filename
sei
lda $d011
and #$ef
sta $d011 ; disable screen
ld1 lda $d011 ; wait for next screen
bpl ld1
ld2 lda $d011
bmi ld2
lda #$60 ; sa for load
sta $b9 ; secondary address
jsr $f3d5 ; open file/open secondary address
sei ; $f3d5 does cli
lda $ba ; device number
jsr ltalk ; talk
lda $b9 ; secondary address
jsr lsendsa ; send sa
jsr lgiecin ; iecin (get load address lo)
sta $ae ; load address lo
lda $90 ; iec status
lsr
lsr
bcc liecok
lda #>$f703
ldy #<$f703
jmp returnOut ; file not found ($f704)
liecok jsr lgiecin ; iecin (get load address hi)
sta $af ; load address hi
inc $b9 ; secondary address 61 = JD load
jsr luntalk ; untalk
lda $ba ; device number
jsr ltalk ; talk
lda $b9 ; secondary address
jsr lsendsa ; send sa
dec $b9 ; secondary address
cpx #$00 ; original secondary address
bne lloadabs2 ; branch if load absolute
lda $c3 ; basic start lo
sta $ae ; load address lo
lda $c4 ; basic start hi
sta $af ; load address hi
lloadabs2 jsr $f5d2 ; loading message
ldy #$00
ldx #$00
ljlw: dex
bne ljlw
lloadinnerloop:
lda #$03
sta $dd00 ; IEC lines inactive/high
lwch1 bit $dd00
bvc lwch1 ; wait until 1541 sets clock inactive/high
bmi lprocessdrivesignal ; branch if data inactive/high (some signal to process)
ltransferblock:
bit $dd00
bpl ltransferblock ; wait until 1541 sets data inactive/high
ltransferbyte:
nop ; timing critical section
nop
nop
nop
lda #$03
ldx #$23
stx $dd00 ; data=active,clock=inactive,ATN=inactive
bit $dd00
bvc lloadinnerloop ; branch if 1541 sets clock active (needs to load next block)
nop
sta $dd00 ; set data inactive
lda $dd00 ; read bits 1/0
nop
lsr
lsr
eor $dd00 ; read bits 3/2
bit $00 ; burn cycles
lsr
lsr
eor $dd00 ; read bits 5/4
bit $00 ; burn cycles
lsr
lsr
eor $dd00 ; read bits 7/6
eor #$03
sta ($ae),y ; store byte
inc $ae ; load address lo
bne ltransferbyte
inc $af ; load address hi
jmp ltransferbyte
lprocessdrivesignal:
ldx #$64
lwok1 bit $dd00
bvc lend2 ; 1541 sets clock active/low: everything ok
dex
bne lwok1 ; wait for ok signal or timeout
lda #$42 ; end, error
.byt $2c
lend2 lda #$40 ; end, okay
jsr $fe1c ; set iec status ($90)
jsr luntalk ; UNTALK
jsr $f642 ; In Save (close file)
bcc lend3
lda #>$f703
ldy #<$f703
jmp returnOut ; file not found ($f704)
lend3 lda #>$f5a8
ldy #<$f5a8
jmp returnOut ; ok ($f509)
; TALK
ltalk ora #$40
lsendb sta $95 ; byte to send
jsr $ee97 ; Set Data inactive
cmp #$3f
bne lca1
jsr $ee85 ; Set Clock inactive
lca1 lda $dd00
ora #$08
sta $dd00 ; Set ATN active
; send IEC byte
lwiecs jsr $ee8e ; Set Clock active
jsr $ee97 ; Set Data inactive
jsr $eeb3 ; Delay 1 ms
jsr $eea9 ; Data => Carry, Clock => M
bcc lcont1 ; branch if data active
lda #>$edac
ldy #<$edac
jmp returnOut ; device not found ($edad)
lcont1 jsr $ee85 ; Set Clock inactive
lcont4 jsr $eea9 ; Data => Carry, Clock => M
bcc lcont4 ; Wait until data inactive
jsr $ee8e ; Set Clock active
txa
pha ; save X
ldx #$08 ; 8 bits to send
lsendbits nop
nop
nop
bit $dd00
bmi lcont5
pla
tax ; restore X
lda #>$edaf
ldy #<$edaf
jmp returnOut ; timeout ($edb0)
lcont5 jsr $ee97 ; Set Data inactive
ror $95 ; byte to send
bcs lci1
jsr $eea0 ; Set Data active
lci1 jsr $ee85 ; Set Clock inactive
lda $dd00
and #$df
ora #$10
; send two bits
sta $dd00
and #$08
beq ltwobitssent
lda $95 ; byte to send
ror
ror
cpx #$02
bne ltwobitssent
ldx #$1e
lwack1 bit $dd00
bpl lwack2
dex
bne lwack1
beq lcont6
lwack2 bit $dd00
bpl lwack2
; when we are here JD is present in floppy
lcont6 ldx #$02
ltwobitssent:
dex ; other dex is above
bne lsendbits ; branch if still bits to send
ldx #$56
lcont7: dex
beq ltbtimeout
lda $dd00
bmi lcont7 ; wait until data active
ltbok: pla
tax ; restore X
rts
ltbtimeout:
pla
tax ; restore X
lda #>$edaf
ldy #<$edaf
jmp returnOut ; Write timeout ($edb0)
; SEND SECONDARY ADDRESS
lsendsa sta $95 ; byte to send
jsr lwiecs ; send IEC byte
lda #$23 ; Data active, ATN/clock inactive
sta $dd00
lwca1 bit $dd00
bvs lwca1 ; Wait until clock active
rts
; UNTALK
luntalk lda $dd00
ora #$08
sta $dd00 ; Set ATN active
jsr $ee8e ; Set Clock active
lda #$5f ; UNTALK command
jsr lsendb ; send byte
jsr $edbe ; Clear ATN
txa
ldx #$0a
ll2 dex
bne ll2
tax
jsr $ee85 ; Set Clock inactive
jmp $ee97 ; Set Data inactive
; IECIN (jumped to from EE13)
lgiecin:
ll3 lda $dd00
cmp #$40
bcc ll3
nop
nop
nop
nop
nop
nop
nop
nop
lda #$03
nop
nop
sta $dd00
nop
nop
nop
nop
nop
nop
ora $dd00
lsr
lsr
nop
ora $dd00
lsr
lsr
eor #$03
eor $dd00
lsr
lsr
eor #$03
nop
eor $dd00
pha
lda #$23
bit $dd00
sta $dd00
bvc lend1
bpl lerr1
pla
lda #$42
jmp $edb2
lerr1 lda #$40 ; Too few bytes
jsr $fe1c ; In Control OS Messages
lend1 pla
clc
rts
; return to vector in a/y+1
returnOut:
pha
tya
pha
lcd27 sei
lda $d011
ora #$10
sta $d011 ; enable screen
lda #$30
sta $01
jsr ldaf2 ; move helper to 02e1
jmp l2f6 ; swap cb00/db00, cli, exit
; display message, install load vector, exit
lcd6a pla
lda #$37
sta $01
sta $92
lda #$00
sta $d021
lda #$06
sta $d020
lda #<lcdac
ldy #>lcdac
jsr $ab1e ; output string
lda #<vload
ldy #>vload
sta iload
sty iload+1
jmp lcd27 ; install 02xx helper, swap cb00/db00, exit
; Message
lcdac .byt $93,$11,$9e
.asc " SJLOAD 0.96 - 2009-10-03"
.byt $0d,$00
.byt $08