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