*/beginfile FLP1_asm
; --------------------------------------------------------------
; FLP1_asm - floppy disk device driver for QDOS
;	  - last modified 24/02/98

; Floppy disk driver for CST    (c) 1984  Tony Tebby	  QJUMP
; Modified for CST maintenance  (c) 1986  David Oliver  CST.
; Modified for Amiga floppies   (c) 1989  Rainer Kowallik
;				      Public Domain
; --------------------------------------------------------------

; keys for floppy disc system

fs_next	equ	$18
fs_acces equ	$1c
fs_drive equ	$1d
fs_filnr equ	$1e
fs_nblok equ	$20
fs_nbyte equ	$22
fs_eblok equ	$24
fs_ebyte equ	$26
fs_cblok equ	$28
fs_updt	equ	$2c

fs_fname equ	$32
fs_spare equ	$58
fs_end	equ	$a0

fs.nmlen equ	$24
fs.hdlen equ	$40

; floppy disk physical layer

fdd_xilk equ	$00		; link for external
				; interrupt 2
fdd_pllk equ	$08		; link for polling interrupt
fdd_shlk equ	$10		; link for schedular
fdd_ddlk equ	$18		; link for directory devices
fdd_iolk equ	$1c		; link to io routine
;
fdd_name equ	$3e		; 4*b name (ends with 0)
fdd_side equ	$42		; b side number
fdd_driv equ	$43		; b drive number
fdd_sadd equ	$44		; b side number to add to
				; read/write command
fdd_pend equ	$45		; b flag for pending ops (<0
				; start drive, >0 do not
				; start)
fdd_fint equ	$46		; b set if forced interrupt
fdd_nset equ	$47		; b set if name set
fdd_step equ	$48		; 4*b step rates per drive
				; (-1 is not set)
fdd_slen equ	$4c		; 4*b sector length per
				; drive (0=128)

fdd_wprt equ	$50		; 4*b write protect per
				; drive (also 40/80 if +ve)
fdd_sden equ	$54		; 4*b single density flag
				; per drive
fdd_rbeg equ	$58		; w number of bytes to skip
				; at beginning of record
fdd_rend equ	$5a		; w number of bytes to skip
				; at end of record
fdd_time equ	$5c		; b time_out for watchdog
				; (set by any action)
fdd_rnup equ	$5d		; b run-up counter
fdd_rndn equ	$5e		; b run_down counter
fdd_wait equ	$5f		; b timer for pending ops.
fdd_scty equ	$60		; b security level
fdd_ntrk equ	$61		; b number of tracks
fdd_stim equ	$62		; b start up time
fdd_sord equ	$63		; b step rate order 0 =
				; 6,12,20,30, 2=6,12,2,3
fdd_chck equ	$64		; 4*b -ve if drive has been
				; checked since
				; stopped/deselected
fdd_pact equ	$68		; b flag, if polled task is
				; already active
fdd_end	equ	$6A

fs_drivr equ	$10
fs_drivn equ	$14

fs_mname equ	$16		; medium name
fs_files equ	$22		; number of files open

fd_estat equ	$23		; error status 0=ok, -1=bad,
				; 1=ignore
fd_fail	equ	$24		; failure count
fd_mupdt equ	$25		; map updated
fd_sflag equ	$26		; sector read/write flag
fd_mwrit equ	$27		; map to be written
fd_pend	equ	$28		; pending operation list
fd.npend equ	$0A		; 10 ops max
fd_mhead equ	$50		; medium header
fd_fmtid equ	$50		; format ID
fd.fmtid equ	'QL5A'
fd_mdnam equ	$54		; ... medium name
fd_mdupd equ	$60		; ... count of updates
fd_mfree equ	$64		; ... free sectors in map
fd_mgood equ	$66		; ... good sectors in map
fd_mtotl equ	$68		; ... total sectors in map
fd_mstrk equ	$6a		; ... sectors per track
fd_mscyl equ	$6c		; ... sectors per cylinders
fd_mtrak equ	$6e		; ... number of tracks
				; (cylinders)
fd_mallc equ	$70		; ... sectors per group
fd_meodr equ	$72		; ... current end of
				; directory (block/byte
				; format)
fd_msoff equ	$76		; ... sector offset
fd_mlgph equ	$78		; ... logical to physical
				; sector translate
fd_mphlg equ	$8a		; ... physical to logical
				; sector translate

fd_map	equ	$b0		; sector map in 3 byte
				; entries
fd_end	equ	fd_mhead+3*512

fd_delen equ	$00
fd_deacs equ	$04
fd_detyp equ	$05
fd_deinf equ	$06
fd_denam equ	$0e
fd_deupd equ	$34
fd_deend equ	$40
fd.desft equ	$6		; shift to convert entry
				; number to position

fds..bsy equ	0		; status busy bit
fds..drq equ	1		; status data request bit
fds..ind equ	1		; status index pin bit
fds..lst equ	2		; status lost data bit
fds..tr0 equ	2		; status track 0 bit
fds..crc equ	3		; status crc error bit
fds..rnf equ	4		; status record not found
				; bit
fds..spn equ	5		; status spun up bit
fds..wpr equ	6		; status write protect bit
fds..mot equ	7		; status motor on bit (1770)
fds..nrd equ	7		; status not ready bit
				; (2793)

fds.bsy	equ	%00000001	; busy
fds.drq	equ	%00000010	; data request
fds.ind	equ	%00000010	; index pin
fds.rwok equ	%01011100	; read/write ok mask
fds.raok equ	%00011000	; read address ok mask

; --------------------------------------------------------------
; Keys for CST QDisc controller (specific).

fd_statr equ	0		; ... assumed 0!!!
fd_comdr equ	0		; ... assumed 0!!!
fd_trakr equ	2
fd_sectr equ	1
fd_datar equ	3
fd_ctrlr equ	8

fdf.rate equ	%00000000	; 6 ms step rate
fdf.slow equ	%00000011	; 30 ms step rate
fdf.prec equ	%00000010	; precompensate no tracks
fdf.veri equ	%00000100
fdf.setl equ	%00000000	; no settling time
fdf.strt equ	%00001000	; no 6 cycle start up

fdc.rest equ	%00000000+fdf.strt+fdf.rate
fdc.seek equ	%00010000+fdf.strt+fdf.rate
fdc.stin equ	%01010000+fdf.strt+fdf.rate
fdc.read equ	$ffffff00+%10000000+fdf.strt+fdf.setl
fdc.writ equ	$ffffff00+%10100000+fdf.strt+fdf.setl+fdf.prec
fdc.radd equ	$ffffff00+%11000000+fdf.strt+fdf.setl
fdc.fint equ	$ffffff00+%11010000
fdc.rtrk equ	$ffffff00+%11100000+fdf.strt+fdf.setl
fdc.wtrk equ	$ffffff00+%11110000+fdf.strt+fdf.setl+fdf.prec

fdd.rnup equ	30		; write run up time
fdd.wait equ	50		; wait for write time
fdd.rndn equ	20		; run down after motor off

fd.ndriv equ	2		; max number of drives
fd.singl equ	'S'

fdc.add	equ	%00010000	; constant to write to
				; control register
fdc.init equ	%00010010	; initial control reg value
				; (drive one selected)
fdc.desl equ	%00000000	; deselected control reg
				; value
fdc.oops equ	%00000000	; error control reg value no
				; drive, no motor
fdc.sing equ	%00001000	; constant to add for single
				; density
fdd.name equ	'FLP0'

; --------------------------------------------------------------
rom_base
	dc.l	$4afb0001
	dc.w	proc_tab-rom_base
	dc.w	rom_init-rom_base
	dc.b	0,27,'FLP device driver v1.17:05',$a,0
	dc.w	0
; --------------------------------------------------------------

fds_fo_mess dc.b	0,18,' files still open',$a
	ds.w	0

fds_rw_mess dc.b	0,19,' read/write failed',$a
	ds.w	0
; --------------------------------------------------------------
rom_init

	bra.l	fd_init

; --------------------------------------------------------------
proc_tab
	ifd	extras

	dc.w	13		; 7 procedures

	dc.w	flp_sec-*	; FLP_SEC security_level
	dc.b	7,'FLP_SEC'	; (0 to 2)
	dc.w	flp_start-*	; FLP_START start_up_time
	dc.b	9,'FLP_START'	; (in 20 ms)
	dc.w	flp_track-*	; FLP_TRACK nr_of_tracks
	dc.b	9,'FLP_TRACK'

	endc

	ifnd	extras

	dc.w	8		; 4 procedures

	endc

	dc.w	flp_use-*
	dc.b	7,'FLP_USE'
	dc.w	prog_use-*
	dc.b	8,'PROG_USE',0
	dc.w	data_use-*
	dc.b	8,'DATA_USE',0
	dc.w	dest_use-*
	dc.b	8,'DEST_USE',0
	dc.w	spl_use-*
	dc.b	7,'SPL_USE'
	dc.w	0		; end of procedures

	dc.w	3		; no functions
	dc.w	prog_d$-*
	dc.b	6,'PROGD$',0
	dc.w	data_d$-*
	dc.b	6,'DATAD$',0
	dc.w	dest_d$-*
	dc.b	6,'DESTD$',0
	dc.w	0		; end of functions


; --------------------------------------------------------------
fd_init
	movem.l	a0/a3,-(sp)

	BSR	user_ini

	moveq	#fdd_end,d1
	moveq	#MT.ALCHP,d0
	moveq	#0,d2
	trap	#1

	lea	fd_poll(pc),a2
	move.l	a2,fdd_pllk+4(a0) ; !

	lea	fdd_iolk(a0),a3
	lea	fd_io(pc),a2
	move.l	a2,(a3)+ 	; input/output... at $1c
	lea	fd_opn(pc),a2
	move.l	a2,(a3)+ 	; open... at $20
	lea	fd_clos(pc),a2
	move.l	a2,(a3)+ 	; close... at $24
	lea	fd_slave(pc),a2	; slave
	move.l	a2,(a3)+
	addq.l	#8,a3		; two spare
	lea	fd_format(pc),a2	; format
	move.l	a2,(a3)+
	move.l	#fd_end,(a3)+	; length
	move.w	#3,(a3)+
	move.l	#'MDV0',(a3)+
	addq.l	#6,a3		; side/drive/side
				; add/empty/fint/name set
	subq.l	#1,(a3)+ 	; all step rates unset
	move.l	#$02020202,(a3)	; 512 byte sectors

	addq.b	#1,fdd_scty(a0)	; set security level
	move.b	#fdd.rnup,fdd_stim(a0) ; and default motor
				     ; start time
	move.l	a0,a3

	lea	fdd_pllk(a3),a0	; link into
	moveq	#MT.LPOLL,d0	; polling list !
	trap	#1

	lea	fdd_ddlk(a3),a0	; link into
	moveq	#MT.LDD,d0	; dd driver list
	trap	#1
*
* now start up drive 1
*
	trap	#0			supervisor mode
	or.w	#$0700,sr		no interrupts
	moveq	#1,d1			select drive one
	bsr.l	fd_select
	bsr.l	fd_ckrdy
*
	and.w	#$d8ff,sr		user mode
	beq.s	fdini_arel
	move.l	#fdd.name,fdd_name(a3)	... none there, forget about MDV
	st	fdd_nset(a3)

fdini_arel
	st	fdd_driv(a3)	; set silly drive number
	clr.l	fdd_chck(a3)	; mark drives not selected
	bsr.l	fd_arel

	moveq	#MT.ALCHP,d0	; make space for defaults
	moveq	#3*36,d1 	; ** 1.17 **
	moveq	#0,d2
	trap	#1
	move.l	a0,a4		; save pointer
	moveq	#MT.INF,d0	; find the system variables
	trap	#1
	lea	SV_PROGD(a0),a0	; and set the pointers to
				; the defaults
	move.l	#$00050000+'FL',d1
	move.l	#'P1_ ',d2
	move.l	a4,(a0)+ 	; program default FLP1_
	move.l	d1,(a4)+
	move.l	d2,(a4)
	add.w	#32,a4
	move.l	a4,(A0)+ 	; data default FLP1_
	move.l	d1,(a4)+
	move.l	d2,(a4)
;	 add.w	 #1,(a4) 	 ; Data default now FLP2_
	add.w	#32,a4
	move.l	a4,(a0)+ 	; spool default PAR
	move.l	#$00030000+'PA',(a4)+
	move.b	#'R',(a4)+

	movem.l	(sp)+,a0/a3
	rts

; --------------------------------------------------------------
; internal adaption to user routines
; --------------------------------------------------------------
fd_selct:
	move.b	d1,fdd_driv(a3)
	bra.l	fd_select
fd_side1:
	move.b	d1,fdd_side(a3)
	bra.l	fd_side
fd_crdy:
	bsr	fd_ckrdy
	tst.b	d0
	beq.s	crdy_rts
	move.w	d0,-(a7)
	bsr	fd_restore
	move.w	(a7)+,d0
	tst.b	d0
crdy_rts rts
; --------------------------------------------------------------
; Floppy disc utilities, read, write, seek	 1984 Tony Tebby
; 1770/1793 version

;       d1 c s  track or sector to seek
;	  length of read/write -1
;       a1 cr   pointer to data buffer
;       a2  r   ptr to data reg (read/write/write track only)
;       a3 c p  pointer to physical definition
;       a4 c p  pointer to status/command register
;       !!!!! a4 and a2 are not used in the AMIGA routines !!!!

; seek using 40/80 flag

fd_seek40
	moveq	#0,d0		; get drive number
	move.b	fdd_driv(a3),d0
	tst.b	fdd_wprt-1(a3,d0.w) ; is it 40 in 80?
	ble	fd_seek		; ... no
	move.b	d1,-(sp) 	; save real track
	add.b	d1,d1		; seek twice as far
; move.b fd_trakr(a4),d0
; add.b	d0,fd_trakr(a4)
	bsr	fd_seek
	move.b	(sp)+,d1 	; \\fd_trakr(a4)	   set
				; real track
	rts

; seek to track

fd_seekr
	BRA	fd_seek		; otherwise use fd_seek
				; anyway
fd_poll
	tst.b	fdd_wait(a3)	; are we waiting for do all
				; pending
	blt.s	fdp_rts		; ... waitng for ever
	beq.s	fdp_pend 	; ... no
	subq.b	#1,fdd_wait(a3)	; ... yes, decrement wait
fdp_rts
	rts
fdp_pend
	tst.b	fdd_pend(a3)	; are there any pending
				; operations?
	beq.s	fdp_rts		; ... no
	bsr.l	fd_do_all	; do operations
	bsr	FLUSHALL
	rts

; --------------------------------------------------------------
; Allocation routines for floppy disk IO
;  1984 Tony Tebby  QJUMP

; routine to find the slave block for a sector

fdb_find
	move.l	fs_cblok(a0),a4	; get pointer to current
				; block
	move.l	a4,d0		; is it set?
	bne.s	fdb_fstrt
	move.l	SV_BTBAS(a6),a4	; start at base of tables
fdb_fstrt
	move.l	a4,a5		; ... and keep a copy

fdb_check
	moveq	#BT.INUSE,d0	; set mask of in use bits
	and.b	BT_STAT(a4),d0	; check if this block is in
				; use
	beq.s	fdb_next 	; ... no
	moveq	#$fffffff1,d0	; set mask of drive id
	and.b	BT_STAT(a4),d0	; get drive id
	cmp.b	d0,d6		; is it the right drive
	bne.s	fdb_next 	; ... no
	moveq	#0,d0		; preset error flag
	cmp.l	BT_FILNR(a4),d5	; is it the right file/block
	beq.s	fdb_rts		; ... yes

fdb_next
	addq.l	#BT_END,a4	; move to next entry in
				; slave block tables
	cmp.l	SV_BTTOP(a6),a4	; ... is it off top
	blt.s	fdb_last 	; ... no
	move.l	SV_BTBAS(a6),a4	; ... yes - start again at
				; bottom
fdb_last
	cmp.l	a4,a5		; have we been right the way
				; round
	bne.s	fdb_check	; ... no - look at this next
				; entry
; sector is not in slave blocks

	bsr.s	fdas_get 	; ... find the sector
	bne.s	fdb_rts
	bsr.s	fdb_new		; ... allocate a new block
	bne.s	fdb_rts
	move.w	d2,BT_SECTR(a4)	; ... set the sector number

	tst.w	d3		; is operation send
	bge.s	fdb_read 	; ... no
	tst.w	d4		; is this a first byte in a
				; block?
	bne.s	fdb_read 	; ... no
	move.l	d7,d0		; is end
	sub.l	a1,d0		; ... less start
	sub.l	#$200,d0 	; >= one sector?
	blt.s	fdb_read 	; ... no
	bset	#BT..ACCS,BT_STAT(a4) ; ... yes, all will be
				    ; overwritten
	bra.s	fdb_ok

fdb_read
	or.b	#BT.RREQ,BT_STAT(a4) ; tell fd to read it
	bsr.l	fds_read 	; read it - now!
	beq.s	fdb_ok		; ... done

fdb_ncs
	moveq	#ERR.NC,d0	; not complete
fdb_rts
	rts

; find a new block

fdb_new
	move.l	SV_BTPNT(a6),a4	; get current slave block
				; pointer
	move.l	a4,a5		; ... save it
fdb_nnext
	addq.l	#8,a4		; move to next
	cmp.l	SV_BTTOP(a6),a4	; off end yet?
	blt.s	fdb_nchk 	; ... no
	move.l	SV_BTBAS(a6),a4	; ... yes, reset to base
fdb_nchk
	moveq	#%00001111,d1	; mask out drive bits
	and.b	BT_STAT(a4),d1
	subq.b	#BT.EMPTY,d1	; and check for empty
	beq.s	fdb_nset 	; ... yes
	subq.b	#BT.TRUE-BT.EMPTY,d1 ; ... not empty, check
				   ; for true copy
	beq.s	fdb_nset 	; ... yes true copy, reuse
				; it
	cmp.l	a5,a4		; have we gone through all
				; blocks
	bne.s	fdb_nnext	; ... no
	bsr.l	fd_slave 	; $$$$$$$$$$$$$ temporary
	bra.s	fdb_ncs		; ... yes

fdb_nset
	move.l	a4,fs_cblok(a0)	; set current block
	move.l	a4,SV_BTPNT(a6)	; set block pointer
	move.b	d6,BT_STAT(a4)	; set empty
	move.l	d5,BT_FILNR(a4)	; ... set the file/block
fdb_ok
	moveq	#0,d0		; no errors
	rts

; routine to find a sector group in the map

fdas_get
	move.l	a4,-(sp)
	lea	fd_map+2(a2),a5	; get start of map+2
	lea	fd_end(a2),a4	; and end of map
	bsr.s	fdas_comp
	move.l	d0,d2		; set sector number MOD
				; alloc in top end
	clr.w	d2
fdasg_loop
	cmp.b	(a5),d1		; group matches?
	bne.s	fdasg_lend
	ror.l	#8,d1
	cmp.b	-1(a5),d1	; and next bit of file/group
				; ?
	bne.s	fdasg_l1
	ror.l	#8,d1
	cmp.b	-2(a5),d1	; and last bit?
	beq.s	fdasg_done
	rol.l	#8,d1		; restore comparison
				; register
fdasg_l1
	rol.l	#8,d1
fdasg_lend
	addq.w	#1,d2		; next group
	addq.l	#3,a5
	cmp.l	a4,a5		; off end yet?
	blt.s	fdasg_loop	; ... no
	moveq	#ERR.FE,d0	; oops, not found
fdasg_done
	subq.l	#2,a5		; set a5 to point to start
	move.l	(sp)+,a4
	rts

; routine to calculate compressed form of file/group

fdas_comp
	move.l	d5,d1		; get file / block in d1
	moveq	#0,d0		; and convert to file /
				; group
	move.w	d1,d0
	divu	fd_mallc(a2),d0
	move.w	d0,d1
	lsl.w	#4,d1		; and stick them together
	lsr.l	#4,d1
	rts

; routine to allocate a new sector

fdas_new
	tst.w	d5		; is this first sector?
	beq.s	fdas_first	; ... yes
	subq.w	#1,d5		; ... no, first find
				; previous sector
	bsr.s	fdas_get
	bne.s	fdas_rts
	addq.w	#1,d5		; now set this sector
	bsr.s	fdas_comp	; compressed form in d1
	swap	d0
	tst.w	d0		; block MOD alloc is zero?
	bne.s	fdas_ok		; ... no, then we've got new
				; sector in old group
	bsr.s	fdas_look	; look for empty hole
	beq.s	fdas_set 	; ... found
	bra.s	fdas_retry	; try again from start of
				; disk
fdas_first
	bsr.s	fdas_comp	; set compressed form in d1
	moveq	#0,d2
	move.w	fd_mscyl(a2),d2	; ... no, keep clear of
				; track 0
	divu	fd_mallc(a2),d2
	bsr.s	fdas_try 	; try once
	beq.s	fdas_rts 	; ... ok

fdas_retry
	moveq	#0,d2		; try from start
fdas_try
	lea	fd_map(a2),a5	; base of map
	add.w	d2,a5		; + sector offset
	add.w	d2,a5
	add.w	d2,a5
	bsr.s	fdas_look	; looking for an empty
				; sector
	bne.s	fdas_rts
fdas_set
	swap	d1
	move.b	d1,(a5)+ 	; set file/block in sector
				; table
	rol.l	#8,d1
	move.b	d1,(a5)+
	rol.l	#8,d1
	move.b	d1,(a5)+

	move.w	fd_mallc(a2),d0
	sub.w	d0,fd_mfree(a2)	; one fewer free allocation
				; blocks

	st	fd_mupdt(a2)	; map updated
fdas_ok
	moveq	#0,d0
fdas_rts
	rts

; look for an empty sector

fdas_look
	move.l	a4,-(sp) 	; save a4
	lea	fd_end(a2),a4	; and set end pointer
	moveq	#$fffffffd,d0
fdasl_loop
	cmp.b	(a5),d0		; free?
	beq.s	fdasl_done	; ... yes
	addq.w	#1,d2		; next sector group
	addq.l	#3,a5
	cmp.l	a4,a5		; off end?
	blt.s	fdasl_loop
	moveq	#ERR.DF,d0	; no empty groups
fdasl_done
	move.l	(sp)+,a4
	rts
; --------------------------------------------------------------
; Check all aspects of a drive    V0.3    1985  Tony Tebby
; Modified for maintenance by CST V 1.17  1986  David Oliver

; write error messages

fds_err_mess
	move.l	a1,-(sp)
	lea	fs_mname-2(a2),a1
	move.w	(a1),-(sp)
	move.w	#10,(a1)
	bsr.s	fds_w_mess
	move.w	(sp)+,fs_mname-2(a2)
	move.l	(sp)+,a1
fds_w_mess
	movem.l	d3/a0/a2,-(sp)
	sub.l	a0,a0
	move.w	UT.MTEXT,a2
	jsr	(a2)
	movem.l	(sp)+,d3/a0/a2
	rts

; Check drive set registers and select

; called internally and from SECTIO and FORMT

;       d2  r   current drive running
;       d4  r   drive required
;       a2 c p  drive definition block
;       a3 c p  device linkage block
;       a4  r   disk control chip address

;       smashes d0,d1,d2,d4,a4

fd_ck_sel
	bsr.s	fdc_rset 	; set up registers etc.
fdc_sel
	move.b	d4,d1
	cmp.b	d1,d2		; is selection required
	bne.l	fd_selct 	; ... yes
	rts

fdc_rset
	bsr.l	fd_ahold 	; hold up asynchronous task
	clr.w	d4
	move.b	fs_drivn(a2),d4	; set drive number required
	move.b	fdd_driv(a3),d2	; save drive number running
	rts

; check drive for read/write ops

;       d5 c p  read/write flag
;       a2 c p  drive definition block
;       a3 c p  device linkage block

fd_ck_rw
	movem.l	d1-d5/a0/a1/a4,-(sp) ; save registers

	move.l	a7,d4		;*/begininsert
	trap	#0
	move.w	sr,-(sp)
	subq.l	#2,d4
	cmpa.l	d4,a7
	beq.s	fd_ck_rw_sv
	bclr	#5,0(a7) 	;User mode upon return
fd_ck_rw_sv:			;*/endinsert
	or.w	#$0700,sr	; disable interrupts

	moveq	#0,d4		; changed medium not
				; permitted
	bsr.s	fdc_rset 	; set registers
	tst.b	fdd_scty(a3)	; which security level?
	blt.s	fdc_rwerr	; ... low, only check if it
				; has errored
	bgt.s	fdc_rwdc 	; ... high, check if not
				; checked
	tst.b	d5		; ... middling, is it write?
	beq.s	fdc_rwerr	; ... read, only check if ot
				; has errored
fdc_rwdc
	tst.b	fdd_chck-1(a3,d4.l) ; is drive already
				  ; checked?
	beq.s	chk_do		; ... no, check it
fdc_rwerr
	tst.b	fd_estat(a2)	; has it errored?
	bra.s	chk_do		; ... yes, check it

; check drive find track

	cmp.b	d4,d2		; is drive changed?
	beq.s	fdc_ok1		; ... no
	bsr.s	fdc_sel		; ... yes, select
; bne.s	 fdc_fe1
	bsr.l	fd_raddr 	; read address
fdc_fe1
; bne.l	 fdc_fe
; move.b  d1,fd_trakr(a4) ; set track number
fdc_ok1
	bra.l	fdc_exok

; check drive for open

fd_ck_op
	movem.l	d1-d5/a0/a1/a4,-(sp) ; save registers

	move.l	a7,d4		;*/begininsert
	trap	#0
	move.w	sr,-(sp)
	subq.l	#2,d4
	cmpa.l	d4,a7
	beq.s	fd_ck_op_sv
	bclr	#5,0(a7) 	;User mode upon return
fd_ck_op_sv:			;*/endinsert
	or.w	#$0700,sr	; disable interrupts

	moveq	#1,d4
	tst.b	fs_files(a2)	; any files open?
	bne.s	ck_op_rset	; ... yes
	ror.l	#1,d4		; ... no, set msb to flag
				; change ok
ck_op_rset
	bsr.s	fdc_rset 	; set registers
; cmp.b	d2,d4	; is required drive running?
; beq.s	fdc_operr;... yes, only check if errored
	tst.b	fdd_scty(a3)	; which security level?
	bgt.s	chk_do		; ... high, always check
	beq.s	fdc_ck_ck	; ... middling, check if not
				; already checked
	tst.l	d4		; ... low, only check if
				; there are no f open
	bge.s	fdc_ok1		; ... files open
fdc_ck_ck
	tst.b	fdd_chck-1(a3,d4.w) ; has drive been checked?
	beq.s	chk_do		; ... no
fdc_operr
	tst.b	fd_estat(a2)	; has it errored?
	beq.s	fdc_ok1		; ... no

; drive does require checking

chk_do
	bsr.l	fdc_sel		; select drive
	bsr.l	fd_crdy		; check if drive has disk in
fdc_est1
	bne.l	fdc_estat	; ... no
	bsr.l	fdc_check	; check if disk changed
	blt.s	fdc_est1 	; ... bad
	st	fdd_chck-1(a3,d4.w) ; ... checked
	beq.s	fdc_wprot	; ... not changed
	tst.l	d4		; is changed disk ok?
	bge.l	fdc_fo		; ... no

	lea	fd_pend(a2),a1	; changed disk
	moveq	#fd.npend-1,d0	; ... clear out pending
				; list!!!
fdc_pdclr
	clr.L	(a1)+		; ensure pending list is
				; empty
	dbra	d0,fdc_pdclr

	move.l	SV_BTBAS(a6),a1
fdc_sbclr
	moveq	#$fffffff1,d0	; mask out all odd bits
	and.b	BT_STAT(a1),d0	; is this a block for this
				; drive?
	cmp.b	d0,d6
	bne.s	fdc_sbnxt	; ... no
	move.b	#BT.EMPTY,BT_STAT(a1) ; ... yes, clear it
fdc_sbnxt
	addq.l	#8,a1		; next block
	cmp.l	SV_BTTOP(a6),a1
	blt.s	fdc_sbclr

	moveq	#0,d5		; read sectors
	bsr.l	fd_do_ms 	; ... of map
	bne.s	fdc_bad_map	; ... oops

	lea	fd_mdnam(a2),a1	; transfer medium name
	lea	fs_mname(a2),a0
	move.l	(a1)+,(a0)+
	move.l	(a1)+,(a0)+
	move.l	(a1)+,(a0)+

; check for 40 track in 80 track drive and double sided in
; single sided drive

	move.w	fd_mstrk(a2),d1	; is sectors/track
	sub.w	fd_mscyl(a2),d1	; ... the same as
				; sectors/cylinder
	beq.s	fdc_40_side	; ... yes, read from side 0
	moveq	#1,d1		; ... no, read from side 1
fdc_40_side
	bsr.l	fd_side1 	; set side
	moveq	#2,d1		; goto track 2
	bsr.l	fd_seek
; !!!!	bsr  fd_raddr	; read address
; bne.s	fdc_bad_map
	subq.b	#2,d1		; track should be 2
	beq.s	fdc_40_ok
	addq.b	#1,d1		; was it 1?
	bne.s	fdc_bad_map	; ... no, give up
	moveq	#1,d1		; ... yes, 40 on 80 track
fdc_40_ok
	move.b	d1,fdd_wprt-1(a3,d4.w) ; set write protect
				     ; positive or zero
	bsr.l	fd_restore	; restore drive

; test write protect

fdc_wprot
	tst.b	fdd_wprt-1(a3,d4.w) ; is it a 40 track in an
				  ; 80?
	bgt.s	fdc_exok 	; ... yes so implicitly
				; write protected
	BSR	fd_wpro
	TST.B	D0
	sne	fdd_wprt-1(a3,d4.w) ; set if it is write
				  ; protected
fdc_exok
	moveq	#0,d0
fdc_rst
	move.w	(sp)+,sr
	movem.l	(sp)+,d1-d5/a0/a1/a4
	tst.l	d0
	rts

fdc_bad_map
	clr.l	fd_mhead(a2)	; set map header to not
				; correct format
fdc_estat
fdc_fe
	st	fd_estat(a2)	; set error occurred
	bsr.l	fd_arel		; release
	moveq	#ERR.FE,d0
	bra.s	fdc_rst
fdc_fo
	lea	fds_fo_mess(pc),a1
	bsr.l	fds_err_mess
	bra.s	fdc_estat

; check if drive defined / disk changed

fdc_check
	BSR	fd_chng
	TST.B	D0
	BNE.S	fdc_name
	MOVEQ	#0,D0		; signal ok if no change
	RTS

; Check if the name has changed

fdc_name
	sf	fd_estat(a2)	; clear error status
	move.w	d4,d1
	sf	fd_mlgph(a2)	; set sector zero
				; translation to zero
	tst.l	fd_mstrk(a2)	; is sector allocation set?
	bne.s	fdc_ckd1
	subq.l	#1,fd_mstrk(a2)	; ensure that first fetch
				; does not div check
fdc_ckd1
	sub.w	#$14,sp		; use stack to read name etc
	move.l	sp,a1
	move.l	fdd_rbeg(a3),-(sp) ; save current read limits
	move.l	#$200-$14,fdd_rbeg(a3) ; number of bytes to
				     ; skip at end of read
	moveq	#0,d1
	moveq	#0,d5
	bsr.l	fd_do_d1 	; and read it
	sne	d0		; save error return
	move.l	(sp)+,fdd_rbeg(a3) ; reset record read limits
	tst.b	d0		; test error return
	bne.s	fdc_fe14 	; ... oops
	move.l	sp,a1
	cmp.l	#fd.fmtid,(a1)	; is it correctly formatted?
	bne.s	fdc_fe14 	; ... no
	lea	fd_mhead(a2),a0	; check against previous
				; header
	moveq	#4,d1
fdc_ckdloop
	cmp.l	(a1)+,(a0)+
	dbne	d1,fdc_ckdloop
	beq.s	fdc_ex14 	; ... name the same
	moveq	#1,d0		; ... name changed
	bra.s	fdc_ex14
fdc_fe14
	moveq	#ERR.FE,d0	; ... bad medium
fdc_ex14
	add.w	#$14,sp
	rts
; --------------------------------------------------------------
; Find the floppy disc definition block  V0.1
;  1985  Tony Tebby  QJUMP

flp_find
	moveq	#MT.INF,d0	; find system vars
	trap	#1
	move.l	SV_DDLST(a0),a0	; ... and linked list of
				; directory drivers
	lea	fd_io(pc),a2	; set entry point for io
				; routines
flf_look
	cmp.l	fdd_iolk-fdd_ddlk(a0),a2 ; the right driver?
	beq.s	flf_rts		; ... yes
	move.l	(a0),a0		; ... no, try the next
	move.l	a0,d1		; ... the last?
	bne.s	flf_look

	addq.l	#4,sp		; remove return address
flf_bp
	moveq	#ERR.BP,d0	; bad, bad, bad
flf_rts
	rts
; --------------------------------------------------------------
; Open a file on floppy disk   v0.6  1984  Tony Tebby   QJUMP
; Modified for CST maintenance V 1.15   1986  David Oliver CST

fd_opn
	BSET	#7,fdd_nset(a3)	; is name set?
	bne.s	fd_opn1
	move.l	#fdd.name,fdd_name(a3) ; set to FLP
fd_opn1
	move.b	fs_drive(a0),d6	; a2 for phys def and d6 for
				; empty slave block
	bsr.l	fd_phys_def

	lea	fs_fname(a0),a4
	moveq	#$ffffffdf,d0	; make second character of
				; name UC
	and.l	(a4)+,d0
	cmp.l	#$00042a44,d0	; is it a '*D..' file name
	bne.s	fdo_normal
	tst.b	fs_files(a2)	; any files open
	bne.s	fdo_iu		; ... yes, give up

	moveq	#-$33,d5 	; check digit following
	add.b	(a4)+,d5
	bgt.s	fdo_nf1		; ... greater than 3
	addq.b	#3,d5
	blt.s	fdo_nf1		; ... less than 0
	bclr	#5,(a4)		; make next upper case
	cmp.b	#'D',(a4)	; is it double density
	sne	d6		; (density flag)
	beq.s	fdo_dset 	; ... yes
	cmp.b	#fd.singl,(a4)	; is it single density
	bne.s	fdo_nf1
fdo_dset
	st	fd_sflag(a2)	; say that it is sector
				; read/writes
	clr.l	fs_nblok(a0)	; set sector/side/track 0
	bsr.l	fd_ck_sel	; select
	move.b	d6,fdd_sden-1(a3,d4.w) ; set density
	clr.b	fdd_wprt-1(a3,d4.w) ; set no protection / not
				  ; 40 on 80
	move.b	d5,fdd_slen-1(a3,d4.w) ; set sector length
	bsr.l	fd_restore	; ... and restore
	moveq	#0,d0
fdo_arel
	bra.l	fd_arel		; release asynch tasks

fdo_iu
	moveq	#ERR.IU,d0
	rts

fdo_normal
	tst.b	fd_sflag(a2)	; check if in use for sector
				; read/writes
	bne.s	fdo_iu

	bsr.l	fd_ck_op 	; check for medium changed
fdo_nf1
	bne.s	fdo_nf		; ... no medium
	bsr.s	fdo_arel 	; release interrupt task

	moveq	#1,d0		; check read only access for
				; delete, new and over
	add.b	fs_acces(a0),d0
	moveq	#%00011001,d1
	btst	d0,d1
	beq.s	fdo_a4		; ro permitted
	bsr.l	fdio_fro 	; check just the RO flag
	bne.s	fdo_err		; ... oops
fdo_a4
	lea	fs_spare(a0),a4	; use spare for io
	moveq	#fd_deend,d2	; length of entry
	move.l	d2,fs_nblok(a0)	; set start pointer
	move.l	fd_meodr(a2),fs_eblok(a0) ; set end of file
				        ; for directory
	cmp.b	#IO.DIR,fs_acces(a0) ; if open directory
	beq.l	fdo_dir		; ... done

	moveq	#0,d4		; first empty slot
	moveq	#0,d5		; first file number
	lea	fd_denam+2(a4),a5 ; set up for compare

fdo_find
	addq.w	#1,d5		; next file
	bsr.l	fdo_read 	; read directory entry
	bne.s	fdo_derr 	; ... not there
	tst.l	fd_delen(a4)	; is entry vacant?
	beq.s	fdo_empty	; ... yes
	lea	fs_fname(a0),a1	; set address of name
	move.w	(a1)+,d3 	; and length
	bsr.l	fdut_cmps	; and compare against (a5)
	bne.s	fdo_find 	; ... it was not the same
	bra	fdo_found	; ... it was the same
fdo_empty
	tst.w	d4		; have we already found an
				; empty entry?
	bne.s	fdo_find 	; ... yes
	move.w	d5,d4		; ... no, save the pointer
				; to this one
	bra.s	fdo_find
fdo_nf
	moveq	#ERR.NF,d0
fdo_err
	rts
fdo_df
	moveq	#ERR.DF,d0
	rts

; error reading directory

fdo_derr
	cmp.l	#ERR.EF,d0	; end of file is ok
	bne	fdo_exit 	; ... anything else is not

; file not found

	move.b	fs_acces(a0),d0	; is it delete?
	blt	fdo_ok		; ... not found is ok
	subq.b	#IO.NEW,d0	; is it new or overwrite?
	blt.s	fdo_nf		; ... no!
	moveq	#0,d6		; genuine new file (eof is
				; zero)
	tst.w	fd_mfree(a2)	; any free sectors for new
				; file?
	beq.s	fdo_df		; ... no!
	tst.w	d4		; was an empty entry found
	beq.s	fdo_new		; ... no put the new entry
				; at the end

fdo_sdir
	move.w	d4,d5		; set the file number
	lsl.l	#fd.desft,d4	; and calculate the byte
				; position
	lsl.l	#7,d4		; ... and so block/byte
	lsr.w	#7,d4
	move.l	d4,fs_nblok(a0)	; set next pointer

; new entry at eof or (d4)

fdo_new
	move.l	a4,a5		; create new entry in spare
				; bit
	move.l	d2,(a5)+ 	; length
	clr.w	(a5)+		; attribute flags
	clr.l	(a5)+		; data space
	clr.l	(a5)+		; extra inf
	moveq	#18,d0		; copy 19 words (1 word + 36
				; bytes)
	lea	fs_fname(a0),a1	; of name
fdo_name
	move.w	(a1)+,(a5)+
	dbra	d0,fdo_name

	movem.l	a0/d0-d2,-(sp)
	moveq	#MT.RCLCK,d0	; get date
	trap	#1

	move.l	d1,(a5)+ 	; date of last update
	clr.l	(a5)+
	move.l	d1,(a5)+ 	; backup date (written once)

	movem.l	(sp)+,a0/d0-d2

	bsr.s	fdo_write	; write directory header

	clr.l	$3C(a4)		; leave date of 1st update

	move.l	fs_eblok(a0),fd_meodr(a2) ; reset dir len

	clr.l	fs_nblok(a0)	; preset file
	move.l	d6,fs_eblok(a0)
	move.w	d5,fs_filnr(a0)
	bsr.s	fdo_write	; and write header (never to
				; be updated)
	bra.s	fdo_exit

fdo_found
	move.b	fs_acces(a0),d0	; check access key
	blt.s	fdo_del		; ... delete
	cmp.b	#IO.NEW,d0	; new or overwrite?
	beq.s	fdo_ex		; ... oops
	bgt.s	fdo_over 	; ... overwrite
	move.l	fd_delen(a4),d1	; find end of file
	lsl.l	#7,d1		; ... in block/byte form
	lsr.w	#7,d1
	move.w	d5,fs_filnr(a0)	; set file number
	move.l	d2,fs_nblok(a0)	; set next
	move.l	d1,fs_eblok(a0)	; set end of file
fdo_ok
	moveq	#0,d0
fdo_exit
	rts
fdo_ex
	moveq	#ERR.EX,d0
	rts

; open directory

fdo_dir
	clr.w	fs_fname(a0)
	bra.s	fdo_ok

; overwrite file

fdo_over
	move.w	d5,d4		; use existing entry as
				; empty entry
	moveq	#fd_deend,d6	; end of file at end of
				; header
	bsr	fdo_sdir 	; open as if new
	bra.s	fd_trun1 	; and truncate

; delete file

fdo_del
	moveq	#0,d4
	bsr.l	fdo_trunc	; truncate to zero

	lsl.l	#fd.desft,d5	; get byte position of
				; directory entry
	lsl.l	#7,d5		; and so block/byte
	lsr.w	#7,d5
	move.l	d5,fs_nblok(a0)
	moveq	#$40,d0		; clear $40 bytes
	lea	$40+fs_spare(a0),a4 ; in spare
fdd_clr
	clr.l	-(a4)
	subq.w	#4,d0
	bgt.s	fdd_clr

	bsr.s	fdo_write	; and write it
	bra.l	fd_msave 	; and write map

; open file read/write utilities

fdo_read
	moveq	#IO.FSTRG,d0
	bra.s	fdo_rdwr
fdo_write
	moveq	#IO.SSTRG,d0
fdo_rdwr
	move.l	a4,a1
	bra.l	fd_ior

; compare string (a5) against (a1), lengths in -2(a5) and d3
; smashes d0,d1,d3

fdut_cmps
	cmp.w	-2(a5),d3	; number of characters the
				; same?
	bne.s	fdut_rts
	bra.s	fdut_clend
fdut_cloop
	bsr.s	fdut_uc		; get upper case char
	move.b	d1,d0
	bsr.s	fdut_uc		; and the other
	cmp.b	d1,d0		; are they different?
fdut_clend
	dbne	d3,fdut_cloop
fdut_rts
	rts
fdut_uc
	exg	a5,a1		; swap registers
	move.b	0(a5,d3.w),d1	; get char
	cmp.b	#'a',d1		; between 'a'
	blt.s	fdut_uc_rts	; ... no
	cmp.b	#'z',d1		; and 'z'
	bgt.s	fdut_uc_rts	; ... no
	sub.b	#$20,d1
fdut_uc_rts
	rts

; truncate file d5 to block group d4: remove sectors from map

fd_trunc
	bsr.l	fdio_ckro	; check if read only
	bne.s	fdut_rts
fd_trun1
	move.l	fs_nblok(a0),d4	; get new end of file
	move.l	d4,fs_eblok(a0)	; and set it
	subq.l	#1,d4		; and get block holding last
				; byte
	clr.w	d4
	swap	d4		; into d4
	move.w	d4,d0		; block?
	addq.w	#1,d0
	divu	fd_mallc(a2),d4	; ... no, block group
	addq.w	#1,d4
	swap	d4
	move.w	d0,d4
	swap	d4
	move.w	fs_filnr(a0),d5	; set file number

fdo_trunc
	lea	fd_map(a2),a4	; bottom of sector map
	lea	fd_end(a2),a5	; top of sector map
fdt_mloop
	moveq	#0,d0
	move.b	(a4),d0
	lsl.w	#8,d0
	move.b	1(a4),d0
	ror.l	#4,d0
	cmp.w	d0,d5		; is this the right file
				; number?
	bne.s	fdt_mnext	; ... no
	swap	d0
	lsr.w	#4,d0
	move.b	2(a4),d0
	cmp.w	d0,d4		; is the block off the end
				; of file?
	bhi.s	fdt_mnext	; ... no
	move.b	#$fd,(a4)	; free the sector
	move.w	fd_mallc(a2),d0
	add.w	d0,fd_mfree(a2)	; ... one more free
	st	fd_mupdt(a2)	; map updated
fdt_mnext
	addq.l	#3,a4		; next sector
	cmp.l	a5,a4		; last sector?
	blt.s	fdt_mloop	; ... no

; clear out the slave blocks

	swap	d4
	move.b	fs_drive(a0),d1	; get drive id
	lsl.b	#4,d1		; id /
	addq.b	#1,d1		; file block

	move.l	SV_BTBAS(a6),a4	; get pointer to base of
				; slave block area
fdt_bloop
	moveq	#$fffffff1,d0	; mask out all but drive id
				; and file system flag
	and.b	BT_STAT(a4),d0	; from status
	cmp.b	d0,d1		; is the the right drive?
	bne.s	fdt_bnext	; ... no
	cmp.w	BT_FILNR(a4),d5	; is it the right file?
	bne.s	fdt_bnext	; ... no
	cmp.w	BT_BLOCK(a4),d4	; is it off the end?
	bhi.s	fdt_bnext	; ... no
	move.b	#BT.EMPTY,BT_STAT(a4)
fdt_bnext
	addq.l	#8,a4		; move to next block
	cmp.l	SV_BTTOP(a6),a4	; off top?
	blt.s	fdt_bloop	; ... no

	st	fs_updt(a0)	; file updated
	moveq	#0,d0		; and no error
	rts
; --------------------------------------------------------------
;  Close a file on floppy disk      1984   Tony Tebby  QJUMP

fd_clos
	move.b	fs_drive(a0),d6	; get drive number
	bsr.l	fd_phys_def	; ... and all else

	tst.b	fd_sflag(a2)	; was it direct sector IO?
	beq.s	fdc_flush	; ... no

	moveq	#0,d0		; ... yes
	move.b	fs_drivn(a2),d0	; set drive number
	move.b	#$02,fdd_slen-1(a3,d0.w) ; reset to 512 byte
				       ; sectors
	clr.b	fdd_sden-1(a3,d0.w) ; and to double density
	clr.b	fdd_chck-1(a3,d0.w) ; mark drive not selected
	clr.l	fd_mhead(a2)	; drive not previously used
	sf	fs_files(a2)	; no files open
	sf	fd_sflag(a2)	; normal operation
	bra.s	fdc_unlk

fdc_flush
	moveq	#FS.FLUSH,d0	; flush out everything
	bsr.l	fd_ior
	subq.b	#1,fs_files(a2)	; ... one fewer files

fdc_unlk
	clr.l	fdd_chck(a3)	; mark drives not selected
	move.l	a0,-(sp) 	; save base address of
				; channel
	lea	fs_next(a0),a0	; and point to next
	lea	SV_FSLST(a6),a1	; start of linked list of
				; channels
	move.w	UT.UNLNK,a2	; and unlink this one
	jsr	(a2)
	move.l	(sp)+,a0 	; restore base address of
				; channel
	move.w	MM.RECHP,a2	; and remove
	jmp	(a2)
; --------------------------------------------------------------
; IO routines for the floppy disc system  V1.02
;  1985  Tony Tebby  QJUMP

;       d0   s scratch / error return
;       d1 cr  input/output byte
;       d2 c s number of bytes to transfer / scratch
;       d6   s drive id * 16 + 1
;       a0 cr  pointer to channel definition
;       a1 crs pointer to read/write buffer
;       a3 cr  pointer to linkage block
;       a2   s pointer to physical definition
;       a4   s pointer to slave block tables
;       a5   s

; scatter load from floppy disk

fd_load
	move.l	a1,-(sp)
	bsr.l	fd_flush 	; ensure medium is up to
				; date (no write ops)
	move.l	(sp)+,a1
	bne.l	fdl_rts

	move.l	fs_eblok(a0),d7	; get length
	lsl.w	#7,d7		; ... convert to byte form
	lsr.l	#7,d7
	moveq	#fs.hdlen,d0
	sub.l	d0,d7		; address offset
	beq.l	fdl_ok
	add.l	a1,d7		; end address of load
	move.l	a1,a4		; start address of load

	moveq	#0,d3		; start looking at track 0
fdl_tr_loop
	moveq	#0,d4		; start at physical sector 0
				; (offset)
fdl_se_loop
	move.l	d4,d1		; set pointer to
				; physical/logical xlate
	add.b	#fd_mphlg,d1
	move.b	0(a2,d1.w),d1	; logical sector in cylinder
	move.w	d3,d0		; track * nr of sectors
	mulu	fd_mscyl(a2),d0	; (upper end d0=0)
	add.w	d0,d1		; logical sector on drive

	move.l	d1,d2
	divu	fd_mallc(a2),d2	; position in map (upper end
				; is posn in group)
	move.w	d2,d0
	add.w	d2,d2
	add.w	d0,d2		; address in map

	lea	fd_map(a2),a1
	add.w	d2,a1
	move.b	(a1)+,d0 	; get 12 bits of map
	lsl.w	#8,d0
	move.b	(a1)+,d0
	ror.l	#4,d0
	cmp.w	fs_filnr(a0),d0	; is the file the same?
	bne.s	fdl_se_next	; ... no
	swap	d0		; ... yes
	lsr.w	#4,d0
	move.b	(a1)+,d0 	; get group number
	mulu	fd_mallc(a2),d0	; as sector number
	swap	d2
	add.w	d2,d0		; + sector within group
	lsl.l	#8,d0
	add.l	d0,d0		; gives address from base of
				; load

	bne.s	fdl_sa1		; not the first sector
	move.w	#fs.hdlen,fdd_rbeg(a3) ; first sector includes
				     ; header
	lea	(a4),a1
	bra.s	fdl_ckend
fdl_sa1
	lea	-fs.hdlen(a4,d0.l),a1 ; set start address
				    ; (less header)
fdl_ckend
	cmp.l	d7,a1		; is start of sector off end
				; of file?
	bge.s	fdl_se_next	; ... yes
	add.l	a4,d0
	add.l	#$200-fs.hdlen,d0
	sub.l	d7,d0		; is end of sector off end
				; of file?
	ble.s	fdl_read 	; ... no
	move.w	d0,fdd_rend(a3)	; ... yes, skip bytes at the
				; end
fdl_read
	moveq	#0,d5
	bsr.l	fd_do_sd1	; read sector
	sne	d0		; save error return
	clr.l	fdd_rbeg(a3)	; and clear part read flags
	tst.b	d0
	bne.l	fdio_fe		; ... oops

fdl_se_next
	addq.l	#1,d4		; next physical sector
	cmp.w	fd_mscyl(a2),d4	; off end?
	blt.l	fdl_se_loop	; ... no

	addq.l	#1,d3		; next track (cylinder)
	cmp.w	fd_mtrak(a2),d3	; off end?
	blt.l	fdl_tr_loop	; ... no

	move.l	d7,a1
fdl_ok
	moveq	#0,d0
fdl_rts
	rts

; rename a file (atomic)

fd_renam
	bsr.l	fdio_ckro	; check read only
	bne.s	fdrn_rts1
	move.w	(a1)+,d4
	subq.w	#5,d4		; is name too short?
	bls.s	fd_bn
	cmp.w	#fs.nmlen+5,d4	; is name too long?
	bhi.s	fd_bn		; ... yes
	move.l	#$dfdfdfff,d0	; mask out lc bits from name
	and.l	(a1)+,d0
	sub.b	fs_drivn(a2),d0	; and take away drive number
	cmp.l	fdd_name(a3),d0	; is it now FLP0?
	bne.s	fd_bn		; ... no, bad
	cmp.b	#'_',(a1)+	; is it FLP0_?
	beq.s	fdrn_1		; ... yes, good
fd_bn
	moveq	#ERR.BN,d0
fdrn_rts1
	rts
fdrn_1
	lea	fs_spare(a0),a4	; set up working addresses
	lea	fd_denam+2(a4),a5
	move.l	a1,d7		; and the new name pointer
	move.w	fs_filnr(a0),d5	; save the file number
	clr.w	fs_filnr(a0)	; and clear it
fdrn_dup
	addq.w	#1,fs_filnr(a0)	; look at next file
	moveq	#FS.HEADR,d0
	moveq	#fd_deend,d2
	move.l	a4,a1		; use spare area to ...
	bsr.s	fd_ior		; read the next header
	beq.s	fdrn_cname	; found
	cmp.w	#ERR.EF,d0	; end of directory?
	beq.s	fdrn_sname	; ... off end
	bra.s	fdrn_rest
fdrn_cname
	move.w	d4,d3		; set length
	move.l	d7,a1		; and new name pointer
	bsr.l	fdut_cmps	; compare the strings
	bne.s	fdrn_dup 	; not the same, try the next

	moveq	#ERR.EX,d0	; otherwise error exists
fdrn_rest
	move.w	d5,fs_filnr(a0)	; restore the file number
	rts

fdrn_sname
	lea	fs_fname+fs.nmlen+2(a0),a2 ; set up to clear
					; the name
	moveq	#fs.nmlen/2,d0
fdrn_clr
	clr.w	-(a2)
	dbra	d0,fdrn_clr
	move.l	a2,a1		; save start pointer

	move.l	d7,a5		; now set the new filename
				; in channel
	move.w	d4,(a2)+ 	; set length
fdrn_snlp
	move.b	(a5)+,(a2)+	; copy a char at a time
	sub.w	#1,d4
	bgt.s	fdrn_snlp

	move.w	d5,fs_filnr(a0)	; restore the file number
	moveq	#fd_denam,d1	; offset of name in header
	moveq	#fs.nmlen+2,d2
	move.b	fs_updt(a0),-(sp) ; rename does not set update
				; flag
	bsr.s	fd_ihds
	move.b	(sp)+,fs_updt(a0)
	rts

; internal header set

fd_ihds
	moveq	#-1,d0

; routine version of fd_io for internal calls from fd_op & fd_cl

fd_ior
	movem.l	d0/d2-d7/a4/a5,-(sp)
fdior_loop
	movem.l	(sp),d0/d2	; restore d0/d2
				; operation/count
	moveq	#1,d3		; all calls are treated as
				; initial entry
	bsr.s	fd_io
	addq.l	#-ERR.NC,d0	; is it ERR.NC?
	beq.s	fdior_loop	; ... yes try again
	subq.l	#-ERR.NC,d0	; restore error code
	addq.l	#4,sp		; and skip action
	movem.l	(sp)+,d2-d7/a4/a5
	rts
fd_io

; set up address of physical definition

	move.b	fs_drive(a0),d6
	bsr.l	fd_phys_def

	tst.b	fd_sflag(a2)	; is it sector reads?
	bne.l	fd_sectio

; clear the error status

	move.l	d0,d4		; save action
	tst.b	fd_estat(a2)	; has it errored?
	beq.s	fdio_action	; no
	tst.b	d3		; initial entry?
	bne.s	fdio_fe2 	; no, (or internal)
	bsr.l	fd_ck_rw 	; check the drive again
fdio_fe2
	bne.l	fdio_fe		; not ok

; look at action

fdio_action
	move.l	d4,d0		; is it internal header set?
	blt.l	fd_hdsx		; ... yes
	cmp.b	#FS.CHECK,d0	; is it a file operation?
	bcs.l	fd_serw		; ... no, simple serial
	cmp.b	#FS.TRUNC,d0	; is it valid?
	bhi.s	fdio_bp		; ... no

	add.w	d0,d0
	move.w	fd_op_tab-2*FS.CHECK(pc,d0.w),d0 ; branch to
					      ; file op.
	jmp	fd_op_tab(pc,d0.w)
fd_op_tab
	dc.w	fd_check-fd_op_tab
	dc.w	fd_flush-fd_op_tab
	dc.w	fd_posab-fd_op_tab
	dc.w	fd_posre-fd_op_tab
	dc.w	fdio_bp-fd_op_tab
	dc.w	fd_mdinf-fd_op_tab
	dc.w	fd_heads-fd_op_tab
	dc.w	fd_headr-fd_op_tab
	dc.w	fd_load-fd_op_tab
	dc.w	fd_save-fd_op_tab
	dc.w	fd_renam-fd_op_tab
	dc.w	fd_trunc-fd_op_tab
err_bp
fdio_bp
	moveq	#ERR.BP,d0
	rts

fd_check
fd_cf_ok
	moveq	#0,d0
fd_cf_rts
	rts
fd_flush
	tst.b	fs_updt(a0)	; is the file updated?
	beq.s	fd_cf_ok 	; ... no, done

	move.l	fs_eblok(a0),d0	; find end of file
	lsl.w	#7,d0		; in block/byte form
	lsr.l	#7,d0

	lea	fs_spare(a0),a1	; put in spare
	move.l	d0,(a1)
	moveq	#0,d1		; write to start of header
	moveq	#4,d2		; 4 bytes
	bsr	fd_ihds		;*/mend bsr hdsx - set header
	bne.s	fd_cf_rts
	move.l	a0,-(sp)
	moveq	#MT.RCLCK,d0	; get date
	trap	#1
	move.l	(sp)+,a0
	move.l	d1,(a1)
	moveq	#fd_deupd,d1	; put in update date
	moveq	#4,d2		; 4 bytes
	bsr	fd_ihds		;*/mend bsr hdsx - set header
	bne.s	fd_cf_rts

	sf	fs_updt(a0)	; now not updated
	bsr.l	fd_msave 	; slave and update map
	bra.s	fd_cf_ok

fd_posab
	bsr.l	fd_spt		; set pointer
	bra.s	fd_pos
fd_posre
	tst.l	d3		; do not move pointer if it
				; is re-entry
	blt.s	fd_pos
	bsr.l	fd_apt		; adjust pointer
fd_pos
	moveq	#IO.PEND,d0	; do a pending to prefetch
	bra.s	fd_ser_1

fd_mdinf
	lea	fs_mname(a2),a5	; copy name
	move.l	(a5)+,(a1)+
	move.l	(a5)+,(a1)+
	move.w	(a5)+,(a1)+

	move.l	fd_mfree(a2),d1	; set free/good sectors
	moveq	#0,d0
	rts
fd_save
	moveq	#IO.SSTRG,d0	; use send string
fd_ser_1
	bra.l	fd_serio

; read and set header calls are assumed to complete in one
; operation as the header is all in one block

fd_headr
	moveq	#IO.FSTRG,d5	; to read header - read
				; string
	cmp.w	#fd_deend,d2	; max length is header
				; length
	bgt.s	fdio_or
	move.l	a1,-(sp) 	; save pointer to start
	bsr.s	fd_head_do	; read header from directory
	move.l	(sp)+,a2 	; get start pointer
	sub.l	#fd_deend,(a2)	; and take away header
				; length
	rts
fdio_or
	moveq	#ERR.OR,d0	; ... oops
	rts

; internal set header

fd_hdsx
	moveq	#IO.SSTRG,d5	; send string
	bra.s	fd_dir_do

fd_heads
	moveq	#IO.SSTRG,d5	; to set header - send
				; string
	moveq	#$e,d2		; of 14 bytes
fd_head_do
	moveq	#0,d1		; header starts at first
				; entry

fd_dir_do
	moveq	#0,d4		; get file number
	move.w	fs_filnr(a0),d4
	beq.l	fdio_bp		; ... cant do header of dir
	move.w	d4,-(sp) 	; save it
	move.l	fs_eblok(a0),-(sp) ; and eof
	move.l	fs_nblok(a0),-(sp) ; and next
	clr.w	fs_filnr(a0)	; set file zero
	move.l	fd_meodr(a2),fs_eblok(a0) ; and eof
				        ; (directory)
	subq.w	#1,d4
	lsl.l	#fd.desft,d4	; and next (64xnumber-1)
	add.l	d4,d1		; plus offset from start
	bsr.l	fd_spt		; set pointer

	move.l	d5,d0		; set action
	moveq	#0,d1		; ... nothing moved so far
	bsr.s	fd_serw		; and do serial op

	move.l	(sp)+,fs_nblok(a0) ; restore next
	move.l	(sp)+,fs_eblok(a0) ; and eof
	move.w	(sp)+,fs_filnr(a0) ; and file number
	tst.l	d0
	rts
; --------------------------------------------------------------
;  Serial IO operations for floppy disk  1984  Tony Tebby QJUMP

;       d0   s scratch / error return
;       d1 cr  input/output byte
;       d2 c s number of bytes to transfer / scratch
;       d3   s action -ve send, 0 check, +ve fetch
;	    ($a fetch line)
;       d4   s block number msw, byte number lsw
;       d5   s file number msw, block number lsw
;       d6 cr  drive id * 16 + 1
;       a0 cr  pointer to channel definition
;       a1 crs pointer to read/write buffer
;       a3 cr  pointer to linkage block
;       a2   s pointer to physical definition
;       a4   s pointer to slave block tables
;       a5   s

fdio_ckro
	move.b	fs_acces(a0),d3	; check for access
	subq.b	#IO.SHARE,d3	; is it share?
	beq.s	fdio_ro		; ... yes
	subq.b	#IO.DIR-IO.SHARE,d3 ; is it dir?
	beq.s	fdio_ro
fdio_fro
	moveq	#0,d3		; get drive number
	move.b	fs_drivn(a2),d3
	tst.b	fdd_wprt-1(a3,d3.w) ; is it write protected?
	beq.s	fdio_rt1 	; ... no
fdio_ro
	moveq	#ERR.RO,d0	; read only
fdio_rt1
	rts

fd_serw
	ext.l	d1		; normal io calls use bottom
				; word of D2
	ext.l	d2
fd_serio
	cmp.b	#IO.SSTRG,d0	; is operation serial?
	bhi.l	err_bp
	moveq	#0,d7		; set d7 to end of string to
				; be read
	tst.l	d3		; is it reentry?
	bge.s	fd_ser_do	; ... no
	sub.l	d1,d7		; ... yes, take away bytes
				; read
fd_ser_do
	subq.b	#IO.EDLIN,d0	; check operation
fdio_bpe
	beq.l	err_bp		; ... oops
	blt.s	fdio_fetch	; ... it's a read
	bsr.s	fdio_ckro	; ... it's a write, check
				; read only
	bne.s	fdio_rt1 	; ... no

fdio_send
	moveq	#-1,d3		; a send operation
	subq.b	#6-IO.EDLIN,d0	; which send?
	beq.s	fdio_bpe 	; ... undefined
	blt.s	fdio_byte	; sbyte
	bgt.s	fdio_string	; sstrg

fdio_fetch
	moveq	#0,d3		; a fetch, assume pending
	addq.b	#IO.EDLIN,d0	; restore key
	beq.s	fdio_byte	; ... zero is pending
	moveq	#$a,d3		; now assume fline
				; (terminator $a)
	subq.b	#IO.FLINE,d0
	beq.s	fdio_string	; ... it is
	blt.s	fdio_fbyte	; ... no, it's byte
	lsl.w	#8,d3		; ... no, it's a string

fdio_string
	add.l	a1,d7		; find start of string
	move.l	d7,-(sp) 	; and save it
	add.l	d2,d7		; find end of string
	bsr.s	fdio_buf
	move.l	a1,d1		; find length written
	sub.l	(sp)+,d1
	rts

fdio_fbyte
	lsl.w	#8,d3		; lsbyte =0
fdio_byte
	move.l	d1,-(sp) 	; put pointer/write byte on
				; stack
	lea	3(sp),a1 	; ... and point to byte
	move.l	a1,d7		; fetch / write 1 byte
	addq.l	#1,d7
	bsr.s	fdio_buf
	move.l	(sp)+,d1 	; get byte read/restore
				; pointer
	rts

;       buffer/unbuffer strings, start a1 end d7

fdio_buf
	tst.b	fd_estat(a2)	; is medium ok?
	bne.s	fdio_fe		; ... oops
	move.l	fs_filnr(a0),d5	; get file number/block
				; number
	move.l	fs_nblok(a0),d4	; get block number/byte
				; number
	cmp.l	fs_eblok(a0),d4	; end of file?
	blt.s	fd_get_block	; no, get the slave block
				; for this operation

	bgt.s	fdio_ef		; yes, pointer is beyond eof
	tst.b	d3		; is operation fetch or
				; inquire?
	blt.s	fdio_eof 	; ... no
fdio_ef
	moveq	#ERR.EF,d0	; end of file
	rts
fdio_fe
	moveq	#ERR.FE,d0	; file error
fdio_rts
	rts

fdio_eof
	tst.w	d4		; the first byte in a new
				; block?
	beq.s	fdio_ext_block	; ... yes

fd_get_block
	bsr.l	fdb_find 	; get the slave block for
				; this sector
	bne.s	fdio_rts 	; ... no room (or error)
; put prefetch here
	bra.s	fdio_cblk

fdio_ext_block
	cmp.l	a1,d7		; is there actually anything
				; to go in block?
	bls.l	fdio_ok		; ... no so exit
	bsr.l	fdb_new		; find space for a new block
	bne.s	fdio_rts
	bsr.l	fdas_new 	; find new sector
	bne.s	fdio_rts
	move.w	d2,BT_SECTR(a4)	; set sector number
	or.b	#BT.TRUE,BT_STAT(a4) ; ... and say it is a
				   ; true buffer
fdio_cblk
	move.l	a4,fs_cblok(a0)	; ... set pointer to this
				; slave block
	btst	#BT..ACCS,BT_STAT(a4) ; are contents
				    ; accessible
	beq.l	fdb_ncs		; ... not complete

	tst.w	d3		; was it just IO.PEND?
	beq.s	fdio_ok		; ... yes, done

	move.l	a4,d0		; get address of next block
	sub.l	SV_BTBAS(a6),d0	; - base of tables
	lsl.l	#6,d0		; * 512/8
	move.l	d0,a5
	add.l	a6,a5		; + base of sysvar
	add.w	d4,a5		; + byte pointer

	tst.w	d3		; fetch bytes?
	bgt.s	fdio_get 	; ... yes

fdio_put
	cmp.l	a1,d7		; end of string?
	bls.s	fdio_pexit
	move.b	(a1)+,(a5)+	; put a byte in the block

	addq.w	#1,d4		; add 1 to byte pointer
	btst	#9,d4		; off end of block?
	beq.s	fdio_put 	; ... no
	addq.w	#1,d5		; add 1 to block
	add.l	#$fe00,d4	; add 1 to block, take 512
				; off byte
fdio_pexit
	st	fs_updt(a0)	; mark file updated
	bsr.s	fdio_swrit	; set pending op to write

	cmp.l	fs_eblok(a0),d4	; is this new eof?
	blt.s	fdio_sptr	; ... no
	move.l	d4,fs_eblok(a0)	; ... yes, update eof
	bra.s	fdio_sptr

fdio_get
	moveq	#0,d0		; we need to compare words
fdio_gloop
	cmp.l	a1,d7		; end of string?
	bls.s	fdio_sptr	; ... yes
	cmp.l	fs_eblok(a0),d4	; beyond end of file?
	bge.s	fd_ex_eof	; ... yes
	move.b	(a5)+,d0 	; get a byte
	move.b	d0,(a1)+ 	; and put it in buffer
	cmp.w	d0,d3		; is it terminating
				; character?
	bne.s	fdio_gnext	; ... no,
	move.l	a1,d7		; reset end pointer to stop
				; loop
fdio_gnext
	addq.w	#1,d4		; add 1 to byte pointer
	btst	#9,d4		; off end of block?
	beq.s	fdio_gloop	; ... no
	addq.w	#1,d5		; add 1 to block
	add.l	#$fe00,d4	; add 1 to block, take 512
				; off byte
fdio_sptr
	move.l	d4,fs_nblok(a0)	; set next block / byte
				; pointer
	cmp.l	a1,d7		; any more bytes to
				; transfer?
	bhi.l	fdio_buf 	; ... yes, go back to Buffer
				; to get new slave

	cmp.w	#$a,d3		; was it fetch line?
	bne.s	fdio_ok		; ... no
	cmp.b	d0,d3		; was new line read?
	beq.s	fdio_ok		; ... yes
fdio_bo
	moveq	#ERR.BO,d0	; buffer overflow
	rts
fdio_ok
	moveq	#0,d0
	rts
fd_ex_eof
	move.l	d4,fs_nblok(a0)	; set current block / byte
				; pointer
	bra.l	fdio_ef

; routines to initiate slaving

fdio_swrit
	moveq	#BT.UPDT,d0	; get update bits
	or.b	d6,d0		; put drive id in
	move.b	d0,BT_STAT(a4)	; set status

	sub.l	SV_BTBAS(a6),a4	; calculate slave block
				; pointer
	lea	fd_pend(a2),a5
	moveq	#fd.npend-1,d0
fd_sw_dup
	cmp.L	(a5)+,a4 	; check for block already in
				; list
	beq.s	fd_sw_rts	; ... it is, all is OK
	dbra	d0,fd_sw_dup

	lea	fd_pend(a2),a5
	moveq	#fd.npend-1,d0
fd_sw_empty
	tst.L	(a5)+		; check for hole in list
	beq.s	fd_sw_set	; ... found one
	dbra	d0,fd_sw_empty

	bsr.l	fd_slavr 	; list is full, empty it
	lea	fd_pend+4(a2),a5
fd_sw_set
	move.L	a4,-(a5) 	; put this block into list
	st	fdd_pend(a3)	; and set pending operation
fd_sw_rts
	rts
; --------------------------------------------------------------
; Routines for slaving	       V2.1    1984 Tony Tebby QJUMP
; Modified for maintenance by CST V 1.15  1986 David Oliver CST

; internal forced slaving (from formt/serio)

fd_slavf
	tst.b	fdd_pend(a3)
	beq.s	fd_slrts
fd_slavr
	bsr.s	fd_slave
fd_slavw
	tst.b	fdd_pend(a3)
	bne.s	fd_slavw
fd_slrts
	rts

; external slaving entry

fd_slave
	sf	fdd_wait(a3)	; do not wait
	st	fdd_pend(a3)	; force pending operations
	bsr	fd_do_all
	bsr	FLUSHALL
	rts

; hold asynch task

fd_ahold
	st	fdd_wait(a3)	; hold up
	rts

; release asynch task

fd_arel
	move.b	#fdd.wait,fdd_wait(a3)
	st	fdd_time(a3)
	BCLR	#7,fdd_pact(a3)
	rts

; save the map

fd_msave
	tst.b	fd_mupdt(a2)	; is map updated?
	beq.s	fds_ms_rts
	st	fd_mwrit(a2)	; mark map to be written
	st	fdd_pend(a3)	; ... force pending ops
	bsr	fd_do_all
	bsr	FLUSHALL
	tst.b	fdd_scty(a3)	; check security level
	bgt.s	fd_slavr 	; clear out all
fds_ms_rts
	rts

; do a read operation directly, a4 is pointer to slave block

fds_read
	movem.l	d5/a1,-(sp)
	moveq	#0,d5		; do a read operation
	bsr.l	fd_do_a4
	movem.l	(sp)+,d5/a1	; restore the registers
	rts
; --------------------------------------------------------------
; Sector IO  (position read/write)	V0.3  1985 Tony Tebby QJUMP
; Modified for maintenance by CST V 1.15  1986 David Oliver CST

fd_sectio
	subq.b	#IO.FSTRG,d0	; is it fetch string?
	beq.s	sio_read
	subq.b	#IO.SSTRG-IO.FSTRG,d0 ; is it send string?
	beq.s	sio_write
	sub.b	#FS.POSAB-IO.SSTRG,d0 ; is it position?
	beq.l	sio_posab
	subq.b	#FS.POSRE-FS.POSAB,d0
	beq.l	sio_posre
	moveq	#ERR.BP,d0	; ... no
	rts

; read a sector

sio_read
	move.l	a1,-(sp) 	; save pointer

	lea	fd_read(pc),a5	; load address of read
				; routine

	bclr	#1,d2		; is there a word length at
				; the start?
	beq.s	sio_set

	moveq	#0,d0		; find the drive
	move.b	fs_drivn(a2),d0
	move.b	fdd_slen-1(a3,d0.w),d0 ; ... thus sector
				     ; length
	clr.w	(a1)
	bset	d0,(a1)		; set length * 2
	lsr.w	(a1)+		; set length
	bra.s	sio_length

; write a sector

sio_write
	move.l	a1,-(sp) 	; save pointer
	lea	fd_write(pc),a5
	bclr	#1,d2		; is there a word length at
				; the start?
	beq.s	sio_set
	addq.l	#2,a1		; skip it
sio_length
	tst.w	d2		; was it just length?
	beq.s	sio_a1_ok

; set up for read/write

sio_set
	bsr.l	fd_ck_sel	; select it and set
				; registers

	move.l	a7,d4		;*/begininsert
	trap	#0
	move.w	sr,-(sp)
	subq.l	#2,d4
	cmpa.l	d4,a7
	beq.s	sio_set_sv
	bclr	#5,0(a7) 	;User mode upon return
sio_set_sv:			;*/endinsert
	or.w    #$0700,sr 	; ... no interrupts

	move.b	fs_nblok+2(a0),d1 ; set side
	bsr.l	fd_side1
; move.b fs_spare(a0),fd_trakr(a4)	; set old track
	move.w	fs_nblok(a0),d1	; set new track
	move.b	d1,fs_spare(a0)	; and save it
	bsr.l	fd_seekr 	; seek or restore
	bne.s	sio_fe1
; move.b d1,fd_trakr(a4)	; and set the track we are on!!!
	move.b	fs_nblok+3(a0),d1 ; read/write sector
	subq.b	#1,d1		; ... allowing for internal
				; offset
	move.b	d1,d6		; save pointers
	move.l	a1,d7
	jsr	(a5)		; do it
	TST.B	D2
	beq.s	sio_fe1		; ... ok
	move.b	d6,d1		; ... bad, restore pointers
	move.l	d7,a1
	jsr	(a5)		; and do again
sio_fe1
	sne	d0
	bsr.l	fd_arel		; release asynchronous task
	tst.b	d0
	beq.s	sio_a1_ok	; check for errors
sio_fe
	lea	fds_rw_mess(pc),a2 ; set error message
	move.l	a2,d0
	bset	#31,d0
	bra.s	sio_a1
sio_a1_ok
	moveq	#0,d0
sio_a1
	move.l	a1,d1		; set d1 to difference in a1
	move.w	(sp)+,sr 	; restore interrupts
	sub.l	(sp)+,d1
	rts

; set the file position

sio_posab
	move.l	d1,fs_nblok(a0)	; set position
sio_posre
	move.l	fs_nblok(a0),d1	; read position
	moveq	#0,d0
	rts
; --------------------------------------------------------------
; Physical layer for floppy disc	V2.1  1985 Tony Tebby QJUMP
; Modified for maintenance by CST. V1.14  1986 David Oliver CST

; do all pending write operations

;       d6   s  empty status for drive
;       a2   s  pointer to physical definition
;       a3 c p  pointer to linkage block

;       smashes d0,d1,d2,d3,d6,a1,a2,a4

fd_do_all:
	MOVEM.L	D0-D3/A1-A2/A4/A6,-(A7)
	BSR.S	HILF_DO_ALL
	MOVEM.L	(A7)+,D0-D3/A1-A2/A4/A6
	RTS
HILF_DO_ALL:
	move.l	a5,-(sp)
	move.b	fdd_driv(a3),d1
	bsr.l	fd_selct
	moveq	#$f,d6		; look at all 16 drives
fd_do_drive
	bsr.s	fd_phys_def
	lea	fdd_ddlk(a3),a1
	cmp.l	fs_drivr(a2),a1	; is this the right type of
				; device?
	bne.s	fd_do_ndrive
	bsr.s	fd_do_1		; all ops for this drive
fd_do_ndrive
	lsr.w	#4,d6		; restore drive number
	dbra	d6,fd_do_drive	; next drive
	sf	fdd_pend(a3)	; clear pending flag
	bsr.l	fd_arel		; and reset the timers
	move.l	(sp)+,a5
	rts

; do write operations for one drive
;       d5   s  -1 (write)
;       d6 c p  empty status for drive

;       smashes d0,d1,d2,d3,d5,a1,a4,a5

fd_do_1
	lea	fd_pend(a2),a5	; get address of pending
				; list
	moveq	#fd.npend-1,d3	; max number of pending
				; operations
fd_do_loop
	move.L	(a5),d0		; get slave block offset
	beq.s	fd_do_map	; ... no more operations
	move.l	SV_BTBAS(a6),a4	; base of sb tables
	add.L	d0,a4		; + offset
	btst	#BT..WREQ,BT_STAT(a4) ; is a write operation
				    ; required?
	beq.s	fd_do_lend	; ... no (so why is it in
				; the list?)
	moveq	#-1,d5		; set write operation
	bsr.s	fd_do_a4 	; ... and do it
fd_do_lend
	clr.L	(a5)+		; clear pending
	dbra	d3,fd_do_loop
fd_do_map
	tst.b	fd_mwrit(a2)	; is map required to be
				; written?
	beq.s	fd_do_rts
	sf	fd_mwrit(a2)	; clear flag
	bsr.l	fd_ck_rw 	; check read/write ok
	bne.s	fd_do_rts

	addq.l	#1,fd_mdupd(a2)	; increment update count
fd_do_mw
	moveq	#-1,d0		; write sectors
fd_do_ms
	lea	fd_mhead(a2),a1	; set address to save map
				; from
	moveq	#0,d3		; put sector 0
fdp_msloop
	move.l	d3,d1
	MOVEM.L	A1,-(A7) 	; save pointer to map
	bsr.s	fd_do_d1 	; (number in d1)
	MOVEM.L	(A7)+,A1 	; restore pointer to map
	TST.B	D0
	bne.s	fd_do_mw
	ADD.L	#512,A1		; point to next sector of
				; map
	addq.l	#1,d3		; next sector number in d3
	cmp.b	#3,d3
	blt.s	fdp_msloop
	sf	fd_mupdt(a2)	; say map is up to date
fd_do_rts
	rts

; set physical definitions
;       d6 c r  drive id / empty status for drive
;       a2   r  address of physical definition block for drive

fd_phys_def
	ext.w	d6
	lsl.w	#2,d6
	lea	SV_FSDEF(a6),a2
	move.l	0(a2,d6.w),a2
	lsl.w	#2,d6
	addq.w	#BT.EMPTY,d6
	rts

; read or write one sector using slave blocks
;       d5 c p  =0 read, <>0 write
;       d6 c p  empty status for drive
;       a1   s  pointer to read/write buffer
;       a2   p  pointer to physical definition block
;       a3   p  pointer to linkage block
;       a4 c p  pointer to slave block tables

;       smashes d0,d1,d2,a1

fd_do_a4
	move.l	a4,d0		; calculate base of block
	sub.l	SV_BTBAS(a6),d0
	lsl.l	#6,d0
	lea	0(a6,d0.l),a1	; in a1

	move.w	BT_SECTR(a4),d1	; set sector number
	mulu	fd_mallc(a2),d1
	moveq	#0,d0		; plus block MOD alloc
	move.w	BT_BLOCK(a4),d0
	divu	fd_mallc(a2),d0
	swap	d0
	add.w	d0,d1
	bsr.s	fd_do_sd1
	beq.s	fd_do_ok

	and.b	#BT.NACTN,BT_STAT(a4) ; clear actions but do
				    ; not set access
	lea	fds_rw_mess(pc),a1 ; write message
	bsr.l	fds_err_mess
	move.b	#1,fd_estat(a2)	; and set status read/write
				; failure
	rts

fd_do_ok
	move.b	d6,BT_STAT(a4)	; set status
	bset	#BT..ACCS,BT_STAT(a4) ; ... ok
	moveq	#0,d0
	rts

; select and do one read/write

fd_do_sd1
	bsr.l	fd_ck_rw 	; select (and hold)
	bne.s	fd_do_rts	; (released on error)

; do one read/write

fd_do_d1
	clr.w	-(sp)		; clear failure count
fd_do_again
	movem.l	d1/d3/a2/a4/a5,-(sp) ; save registers
	move.l	a1,a5		; save BUffer pointer
	ext.l	d1
	divu	fd_mscyl(a2),d1	; get track
	move.w	d1,-(sp)
	move.w	d1,d3
	mulu	fd_msoff(a2),d3	; get track*offset
	clr.w	d1
	swap	d1
	move.b	fd_mlgph(a2,d1.w),d1 ; and sector/side
	bclr	#7,d1
	sne	d2		; side
	add.w	d3,d1		; sector
	divu	fd_mstrk(a2),d1
	swap	d1		; MOD mscyl
	move.w	d1,-(sp)

	moveq	#1,d1
	and.w	d2,d1
	bsr.l	fd_side1 	; set side
	move.w	2(sp),d1 	; get track
;   cmp.b   fd_trakr(a4),d1 is it the right track?
;   beq.s   fd_do_rw
	bsr.l	fd_seek40

fd_do_rw

	move.l	a7,d1		;*/begininsert
	trap	#0
	move.w	sr,-(sp)
	subq.l	#2,d1
	cmpa.l	d1,a7
	beq.s	fd_do_rw_sv
	bclr	#5,0(a7) 	;User mode upon return
fd_do_rw_sv:			;*/endinsert
	or.w	#$0700,sr	; disable interrupts

	moveq	#$1f,d1		; get physical sector (-1)
	and.w	2(sp),d1
	tst.b	d5
	beq.s	fd_do_rd
	bsr.l	fd_write 	; write
	bra.s	fd_do_rint
fd_do_rd
	bsr.l	fd_read
fd_do_rint
	move.w	(sp)+,sr 	; restore interrupts
	movem.l	(sp)+,d0/d1/d3/a2/a4/a5 ; remove 4 bytes from
				      ; sp and restore regs
	move.b	d2,d0		; get error return
	ble.s	fd_do_x8 	; operation ok or timed out
; subq.b #1,d2		; seek error?
; bne.s	fd_inc_fail ;... not a seek error
; tst.b	(sp)		;... seek error, first one?
; beq.s	fd_inc_fail ;... yes

fd_inc_fail
; addq.b #1,(sp)		;increment failure count
; cmp.b	#3,(sp)
; ble.l	fd_do_again	;and retry up to three times
	NOP

fd_do_x8
	bsr.l	fd_arel		; release asynch task
	addq.l	#2,sp		; remove failure count

	tst.b	d0		; and test error return

	rts
; --------------------------------------------------------------
; Set the next byte pointers     1985  Tony Tebby   QJUMP
;
;       d0   s  scratch
;       d1 cr   byte pointer to file (returned absolute)
;       a0 c p  channel definition block

; adjust pointer by d1

fd_apt
	move.l	fs_nblok(a0),d0	; get current pointer

; calculate pointer

fd_cpt
	lsl.w	#7,d0		; in byte pointer form
	lsr.l	#7,d0
	sub.l	#fd_deend,d0	; relative to start
	add.l	d0,d1		; add to offset
	bvs.s	fd_pt_eof

; set pointer to d1

fd_spt
	move.l	d1,d0		; preserve updated address
				; (in d1)
	bmi.s	fd_pt_bof	; ... it's off the beginning
	add.l	#fd_deend,d0
	bvs.s	fd_pt_eof
	asl.l	#6,d0		; shift most of the way
	bvs.s	fd_pt_eof	; ... to check for sign
				; change
	add.l	d0,d0		; and the last little bit
	lsr.w	#7,d0		; ... it's now in block/byte
				; form
	cmp.l	fs_eblok(a0),d0	; but is it within the file?
	ble.s	fd_setnb 	; ... yes
fd_pt_eof
	moveq	#0,d1		; if off the end of file
	move.l	fs_eblok(a0),d0	; ... set it to eof
	bra.s	fd_cpt
fd_pt_bof
	moveq	#fd_deend,d0	; beginning of file is at
				; end of header
	moveq	#0,d1		; but appears to be zero
fd_setnb
	move.l	d0,fs_nblok(a0)
	rts
; --------------------------------------------------------------
; Format medium. Changed in some aspects to support amiga
; hardware
; at increased speed.
; --------------------------------------------------------------
; Format procedure for floppy disks  V2.3    1985  Tony Tebby
;
;       d1 cr   drive number / good sectors
;       d2  r   total sectors
;       a0 c    medium name
;       a3 c    linkage block

fdf.group equ	3

fd_format
	move.l	a0,a5		; save call params
	move.w	d1,d6

	move.l	a3,-(sp) 	; save base of linkage block
	moveq	#MT.ALCHP,d0	; and allocate space
	move.l	#fd_end+$200,d1	; $28+3*512 bytes + one
				; sector
	moveq	#0,d2
	trap	#1
	move.l	(sp)+,a3
	tst.l	d0
	beq.s	fdf_set		; ... ok
	rts	...		; oops
fdf_set

	move.l	a7,d1		;*/begininsert
	trap	#0
	move.w	sr,-(sp)
	subq.l	#2,d1
	cmpa.l	d1,a7
	beq.s	fdf_set_sv
	bclr	#5,0(a7) 	;User mode upon return
fdf_set_sv:			;*/endinsert
	or.w	#$0700,sr	; disable interrupts

	bsr.l	fd_slavf 	; do all pending ops and
				; stop interrupt task
	move.l	a0,a2		; set base of pseudo
				; definition block
	move.b	d6,fs_drivn(a2)	; set drive number
	move.l	#$90009,-(sp)	; ... and set number of
				; sectors track/cylinder
	bsr.l	fd_ck_sel	; select (and hold) drive,
				; set registers
	sf	fdd_wprt-1(a3,d6.w) ; clear 40/80 flag
	sf	fdd_chck-1(a3,d6.w) ; clear the checked flag

	bsr.l	fd_restore	; and restore drive

	moveq	#0,d4
	move.b	fdd_ntrk(a3),d4	; get number of tracks
	bne.s	fdf_sets 	; ... it is set
	moveq	#80,d4		; 80 track on amiga

; set number of sides

fdf_sets
	cmp.w	#5+10,(a5)	; is name at least 11
				; characters long?
	ble.s	fdf_chkt 	; ... no
	cmp.b	#'*',2+5+10(a5)	; is it forced single sided?
				; (11th character=*)
	beq.s	fdf_blank	; ... yes

; check number of tracks

fdf_chkt
	lsl.w	(sp)		; increment number of sides
				; on amiga

; set up blank map

fdf_blank
	move.w	#$5ff,d0 	; fill medium header buffer
				; with $ff
	lea	fd_mhead+$600(a0),a1
fdf_bloop
	st	-(a1)
	dbra	d0,fdf_bloop

	move.l	#'QL5A',(a1)+

	move.w	(a5)+,d0 	; length of medium name
	addq.l	#5,a5		; less fdkn_
	subq.w	#5,d0
	moveq	#10,d1
	sub.w	d0,d1		; >10?
	bge.s	fdf_snend
	moveq	#9,d0		; yes, take first 10
fdf_snloop
	move.b	(a5)+,(a1)+	; copy it into map
fdf_snend
	dbra	d0,fdf_snloop

	bra.s	fdf_spend
fdf_sploop
	move.b	#' ',(a1)+	; now pad with spaces
fdf_spend
	subq.w	#1,d1
	bge.s	fdf_sploop

	move.w	SV_RAND(a6),(a1)+ ; random number
	clr.l	(a1)+		; update count
	move.w	(sp),d1		; calculate total sectors
	mulu	d4,d1
	move.w	d1,(a1)		; number of sectors
	subq.w	#6,(a1)+ 	; (6 taken)
	move.w	d1,(a1)+ 	; good
	move.w	d1,(a1)+ 	; total
	move.w	2(sp),(a1)+	; sectors per track
	move.w	(sp),(a1)+	; sectors per cylinder
	move.w	d4,(a1)+ 	; number of tracks
	move.w	#fdf.group,(a1)+	; sectors per allocation
				; group
	move.l	#fd_deend,(a1)+	; length of directory
	moveq	#18,d0
	lea	fdf_9trans(pc),a5 ; set sector translate
				; tables for amiga
fdf_stran
	move.w	(a5)+,(a1)+
	dbra	d0,fdf_stran

; now format and check all the tracks

	moveq	#0,d6		; start at track 0
	move.w	(sp),d4		; number of sectors /
				; cylinder
	lea	fd_map(a0),a5	; set address of map
fdf_tr_loop
	moveq	#0,d7		; side 1
	bsr.s	fdf_fmt_chk	; format and check
fdf_tr_s0
	moveq	#1,d7		; side 0
	bsr.s	fdf_fmt_chk	; format and check
	moveq	#$fffffffd,d0	; ... good cylinder, mark
				; vacant
fdf_mset
	moveq	#0,d1		; set number of map entries
				; per cylinder
	move.w	d4,d1
	divu	#fdf.group,d1
fdf_msloop
	move.b	d0,(a5)		; and set all good or bad
	addq.l	#3,a5
	subq.w	#1,d1
	bgt.s	fdf_msloop

	addq.w	#1,d6		; move on one track
	cmp.w	fd_mtrak(a0),d6	; end of map?
	blt.s	fdf_tr_loop	; ... no

	lea	fd_map(a0),a5
	cmp.l	#$fdfffffd,(a5)	; are the first two groups
				; free?
	bne.s	fdf_ff		; ... no

	move.w	#$f800,(a5)+	; set it to medium header /
				; directory
	clr.l	(a5)

	lea	(a0),a2		; set pseudo definition
				; block pointer
	bsr.l	fd_do_mw 	; write map sectors
	bne.s	fdf_ff		; ... oops
	moveq	#0,d0
	bra.s	fdf_exit

; error returns

fdf_ff
	moveq	#ERR.FF,d0
fdf_exit
	bsr.l	fd_dskcng	; force a disc change signal
				; from drive
	st	fdd_driv(a3)	; change the drive so next
				; open reads header
	bsr.l	fd_arel		; release asynch tasks
	move.l	fd_mgood(a0),d7	; save sector counts
	move.l	d0,d4		; save error flag
	moveq	#MT.RECHP,d0	; return space to common
				; heap
	trap	#1
	move.l	d4,d0		; restore error flag
	move.w	d7,d2		; set sector counts
	swap	d7
	move.w	d7,d1
	addq.l	#4,sp
	move.w	(a7)+,sr
	rts

; subroutine to format and write a track

fdf_fmt_chk
	bsr.s	fdf_sk_trk	; seek and write track
	bne.s	fdf_wr_err	; ... oops
	moveq	#0,d5
fdf_read
	move.b	d5,d1		; read next sector
	lea	fd_end(a0),a1	; ... into spare bit at end
	bsr.l	fd_read
	bne.s	fdf_rd_err	; ... oops
	addq.w	#1,d5
	cmp.b	#9,d5		; last?
	blt.s	fdf_read 	; ... no

;	 movem.l  d0/a0,-(a7)	 ; temporary aberration
;	 move.l	 #0,a0
;	 move.l	 #$00010000+'. ',d0 ; signal OK
;	 bsr	 IOD0
;	 movem.l  (a7)+,d0/a0

	rts			; all ok

; read / verify failed

fdf_rd_err
;	 movem.l  d0/a0,-(a7)	 ; temporary aberration
;	 move.l	 #0,a0
;	 move.l	 #$00010000+'R ',d0 ; signal read error
;	 bsr	 IOD0
;	 movem.l  (a7)+,d0/a0

	addq.l	#4,sp		; remove return
	moveq	#$fffffffe,d0	; bad track
	sub.w	d4,fd_mfree(a0)	; decrement sector counts
	sub.w	d4,fd_mgood(a0)
	bra	fdf_mset 	; and set map entries

; write track failed

fdf_wr_err
;	 movem.l  d0/a0,-(a7)	 ; temporary aberration
;	 move.l	 #0,a0
;	 move.l	 #$00010000+'W ',d0 ; signal write error
;	 bsr	 IOD0
;	 movem.l  (a7)+,d0/a0

	addq.l	#4,sp		; remove return
	bra	fdf_ff		; format failed (short?)
; write a track

fdf_sk_trk
	move.b	d6,d1		; seek to track
	bsr.l	fd_seek
fdf_track
	move.b	d7,d1		; select side in d1
	bsr.l	fd_side

	bsr.l	fd_ftrack

fdf_trkx
	tst.b	d0

	rts

fdf_9trans
	dc.w	5
	dc.b	$00,$03,$06,$01,$04,$07,$02,$05,$08
	dc.b	$80,$83,$86,$81,$84,$87,$82,$85,$88
	dc.b	$00,$03,$06,$01,$04,$07,$02,$05,$08
	dc.b	$09,$0c,$0f,$0a,$0d,$10,$0b,$0e,$11

; --------------------------------------------------------------
;
;      BASIC extensions start here
;
; --------------------------------------------------------------
prog_use
	moveq	#$00,d5
	bra.s	xxx_use

data_use
	moveq	#$04,d5
	bra.s	xxx_use

dest_use
	moveq	#$08,d5
	bra.s	xxx_use

spl_use
	move.w	#$88,d5

xxx_use
	bsr.l	ut_stos		; get a string
	bne.s	xxx_rts		; ... oops
	cmp.w	#30,0(a6,a1.l)	; <=30 characters long
	bgt	flp_bp		; ... oops

	moveq	#MT.INF,d0	; find the system variables
	trap	#1
	lea	SV_PROGD(a0),a0	; and set the pointers to
				; the defaults
	move.w	d5,d0
	andi.b	#$7F,d0
	move.l	0(a0,d0.w),a4

	move.w	0(a6,a1.l),d1
	addq.l	#2,a1
	move.w	d1,(a4)+

	tst.b	d5
	bmi.s	xxx_dec

	lea	-1(a1,d1.w),a2
	cmpi.b	#'_',0(a6,a2.l)

	beq.s	xxx_dec

	cmpi.w	#30,d1
	beq	flp_bp		; name too long

	move.b	#'_',0(a4,d1.w)	; append underline

	addq.w	#1,d1
	move.w	d1,-2(a4)	; increment length
	subq.w	#1,d1

	bra.s	xxx_dec

xxx_lup
	move.b	0(a6,a1.l),d0
	addq.l	#1,a1
	move.b	d0,(a4)+

xxx_dec
	dbra	d1,xxx_lup

	moveq	#0,d0

xxx_rts
	rts

prog_d$
	moveq	#0,d5
	bra.s	xxx_d$

data_d$
	moveq	#4,d5
	bra.s	xxx_d$

dest_d$
	moveq	#8,d5
	bra.s	xxx_d$

spl_d$
	moveq	#8,d5

xxx_d$
	cmp.l	a3,a5
	bne	flp_bp		; ... oops

	moveq	#MT.INF,d0	; find the system variables
	trap	#1
	lea	SV_PROGD(a0),a0	; and set the pointers to
				; the defaults
	move.l	0(a0,d5),a4

	move.w	(a4)+,d4

	move.l	d4,d1
	addq.l	#1,d1
	and.b	#$FE,d1
	move.w	BV.CHRIX,a2
	jsr	(a2)

	sub.l	d1,BV_RIP(a6)
	move.l	BV_RIP(a6),a1

	move.w	d4,0(a6,a1.l)
	addq.l	#2,a1
	bra.s	xxx_dec$

xxx_lup$
	move.b	(a4)+,d0
	move.b	d0,0(a6,a1.l)
	addq.l	#1,a1

xxx_dec$
	dbra	d4,xxx_lup$

	move.l	BV_RIP(a6),a1
	moveq	#1,d4
	moveq	#0,d0
	rts

; Set the name of the floppy disk system  1985 Tony Tebby QJUMP
;       bra.s   dev_use	    * Go to it.		** 1.17 **

flp_use
	lea	fd_io(pc),a4	; Get entry point for io
				; routines       ** 1.17 **
dev_use
	bsr.l	ut_stos		; get a string
	bne.s	flp_rts		; ... oops
	subq.w	#3,0(a6,a1.l)	; 3 characters long
	bne.s	flp_bp		; ... oops
	move.l	2(a6,a1.l),d6	; get new name
	and.l	#$5f5f5f00,d6	; in upper case
	add.b	#'0',d6		; ending with '0'

	moveq	#MT.INF,d0	; find system vars
	trap	#1
	move.l	SV_DDLST(a0),a0	; ... and linked list of
				; directory drivers

flp_look
	cmp.l	fdd_iolk-fdd_ddlk(a0),a4 ; the right driver?
				       ;    ** 1.17 **
	beq.s	flp_set		; ... yes
	move.l	(a0),a0		; ... no, try the next
	move.l	a0,d1		; ... the last?
	bne.s	flp_look
flp_bp
	moveq	#ERR.BP,d0
flp_rts
	rts
flp_set
	move.l	d6,fdd_name-fdd_ddlk(a0) ; set new name
	BSET	#7,fdd_nset-fdd_ddlk(a0) ; flag name as set
	rts

	ifd	extras

; --------------------------------------------------------------
flp_opt
	move.w	CA.GTINT,a2
	jsr	(a2)
	bne.s	flo_rts
	subq.w	#1,d3
	blt.s	flo_rts
	bsr	flp_find
	movem.w	0(a6,a1.l),d4/d5/d6 ; get 3 parameters

	subq.b	#1,d4
	move.b	d4,fdd_scty-fdd_ddlk(a0) ; set security level

	subq.w	#1,d3
	blt.s	flo_rts
	move.b	d5,fdd_stim-fdd_ddlk(a0) ; set start up time

	subq.w	#1,d3
	blt.s	flo_rts
	move.b	d6,fdd_ntrk-fdd_ddlk(a0) ; set number of
				       ; tracks
flo_rts
	rts
flp_sec
	moveq	#fdd_scty-fdd_ddlk,d7 ; set security level
	bsr.s	flo_int
	subq.b	#1,(a0)		; -1 to 1
	rts

flp_start
	moveq	#fdd_stim-fdd_ddlk,d7 ; set start up time
	bra.s	flo_dcall

flp_track
	moveq	#fdd_ntrk-fdd_ddlk,d7 ; set number of tracks
flo_dcall
	bsr.s	flo_int
	rts

flo_int
	move.l	(sp)+,a4 	; remove return address
	move.w	CA.GTINT,a2	; get an integer
	jsr	(a2)
	bne.s	flo_rts
	subq.w	#1,d3		; just one
	bne	flf_bp
	bsr	flp_find 	; find the definition block
	add.w	d7,a0		; and the item to set
	move.b	1(a6,a1.l),(a0)	; and set the byte
	jmp	(a4)

	endc

; --------------------------------------------------------------
; Get a string on the stack V0.2  1985 Tony Tebby QJUMP
; Modified to accept numbers and expressions
; (C) 1986 David Oliver CST V 4.00

ut_stos
	tst.w	2(a6,a3.l)	; Get name of parameter. If
				; none, it must be exprssn.
	bmi.s	get_string	; ... so convert the value
				; to a string.  ** 4.00 **
	moveq	#$0f,d0		; extract type of parameter.
	and.b	1(a6,a3.l),d0
	subq.b	#1,d0		; is it a string?
	bne.s	ut_gtnam 	; ... no, get the name
				; instead
get_string
	move.l	a5,-(sp) 	; ... yes, save the top
				; pointer
	lea	8(a3),a5 	; get just one string
	move.w	CA.GTSTR,a2
	jsr	(a2)
	move.l	(sp)+,a5 	; restore top pointer
	bne.s	utils_rts
	moveq	#3,d1		; get total length of string
	add.w	0(a6,a1.l),d1
	bclr	#0,d1
	add.l	d1,BV_RIP(a6)	; and reset ri stack pointer
	bra.s	utils_ok
ut_gtnam
	moveq	#ERR.BP,d0	; assume bad parameter
	moveq	#0,d1
	move.w	2(a6,a3.l),d1	; get the pointer to the
				; real entry
	bmi.s	utils_rts	; ... expression is no good
	lsl.l	#3,d1		; in multiples of 8 bytes
	add.l	BV_NTBAS(a6),d1
ut_ntnam
	moveq	#0,d6
	move.w	2(a6,d1.l),d6	; thus the pointer to the
				; name
	add.l	BV_NLBAS(a6),d6
	moveq	#0,d1		; get the length of the name
				; as a long word
	move.b	0(a6,d6.l),d1
	addq.l	#1,d1		; rounded up
	bclr	#0,d1
	move.w	d1,d4		; and save it
	addq.l	#2,d1		; space required is +2 bytes
	move.w	BV.CHRIX,a2	; on ri stack
	jsr	(a2)
	move.l	BV_RIP(a6),a1

	add.w	d4,d6		; move to end of string
				; (ish)
ut_nam_loop
	subq.l	#1,a1		; and copy one byte at a
				; time
	move.b	0(a6,d6.l),0(a6,a1.l)
	subq.l	#1,d6
	dbra	d4,ut_nam_loop	; including the (byte) name
				; length
	subq.l	#1,a1		; put a zero on to make it a
				; word
	clr.b	0(a6,a1.l)
utils_ok
	moveq	#0,d0
utils_rts
	rts
; --------------------------------------------------------------
*/endfile
