*/beginfile FLP2_asm

; --------------------------------------------------------------
; FLP2_asm - FLP Physical I/O - Amiga specific disk access
;	  - last modified 11/01/98

; Amiga-QDOS sources by Rainer Kowallik
;    ...latest changes by Mark J Swift
; --------------------------------------------------------------

;  Version message

BANNER:
	dc.b	0,34,'Amiga-QDOS FLP physical I/O v1.30',$A

; -------------------------------------------------------------
;    Set up Basic procedures

user_ini:
	movem.l	d1-d3/a0-a5,-(a7)

	lea	BANNER(pc),a1	; start of message
	suba.l	a0,a0		; output channel 0
	move.w	UT.MTEXT,a2
	jsr	(a2)		; print it

; link in additional BASIC commands

	lea	PROC_DEF(pc),a1
	suba.l	a2,a2
	move.w	$110,a2
	jsr	(a2)

; allocate memory for disk variables

	move.l	#FV_LEN,d1
	moveq	#MT.ALCHP,d0
	moveq	#0,d2
	trap	#1
	move.l	a0,AV.DSKV

;  allow disk DMA.

	move.w	#%1000000000010000,DMACON ; enable disk DMA

; clear all the variables

	move.l	AV.DSKV,a3	; address of disk vars ->a3
	moveq	#((FV_LEN>>1)-1),d0

CLRV_LUP1:
	clr.w	(a3)+
	dbra	d0,CLRV_LUP1

;  set up disk flags for:
;  Disable disk operation via index interrupt.
;  Write operation. Index on write. No index on read
;  WORDSYNC on. No retry on read error.

	move.l	AV.DSKV,a3	; address of disk vars ->a3
	move.w	#%0110100000100000,FV.FLAGS(a3)

; link in polled routine to control switching off the motor

	lea	POLSERV(pc),a1	; address of routine
	move.l	AV.DSKV,a0
	lea	FV.POLLLink(a0),a0
	move.l	a1,4(a0)
	moveq	#MT.LPOLL,d0
	trap	#1

; check availability of external drive(s)

	move.l	AV.DSKV,a3	; address of disk vars ->a3
	lea	FV.DRVVArs(a3),a4 ; address of drive vars->a4

	moveq	#2,d3		; 3 drives to check

AVAIL_LUP:
	moveq	#3,d0
	sub.w	d3,d0
	bsr	hw_DRV_TYP
	beq.s	NODRV

	lea	DV_LEN(a4),a4
	move.l	d0,DV.TYPE(a4)

	dbra	d3,AVAIL_LUP

NODRV	addq.w	#1,d3
	moveq	#3,d0
	sub.w	d3,d0
	move.w	d0,FV.MAXDRive(a3)

; mount each and every drive

	move.w	d0,d3

MOUNT_LUP:
	move.w	d3,d0
	bsr.s	MOUNT		; mount drive

	dbra	d3,MOUNT_LUP	; continue with next drive

INI_RTS:
	movem.l	(a7)+,d1-d3/a0-a5
	moveq	#0,d0
	rts

; -------------------------------------------------------------
; initialise and set the individual drive variables

MOUNT:
	movem.l	d3/a3-a5,-(a7)

	move.w	d0,d3		; drive number to d3

	move.l	AV.DSKV,a3	; address of disk vars ->a3
	lea	FV.DRVVArs(a3),a4 ; address of drive vars->a4

	mulu.w	#DV_LEN,d3
	lea	0(a4,d3.w),a4	; relevant drive vars

	move.w	d0,d3		; drive number to d3

; initialise and set the individual drive variables

	clr.w	DV.TIMEOut(a4)	; timeout
	clr.w	DV.SIDE(a4)	; side
	clr.w	DV.TRACK(a4)	; track

; allocate some room for drives buffer variables

ALOCBUF:
	move.l	DV.SIDE0buff(a4),d0
	bne.s	MOUNTOK		; already defined

	movem.l	d1-d3/a1-a4,-(a7)
	move.l	#(2*AB.BUFENd),d1
	moveq	#MT.ALCHP,d0
	moveq	#0,d2
	trap	#1
	movem.l	(a7)+,d1-d3/a1-a4

	tst.l	d0
	bne.s	MOUNTX		; exit on error

	move.l	a0,a5

	move.l	a5,DV.SIDE0buff(a4) ; addrs buffer vars, side 0
	move.w	d3,AB.DRIVE(a5)	; set disk #
	clr.w	AB.SIDE(a5)	; set first side
	clr.w	AB.TRACK(a5)	; set first track
	clr.w	AB.PENDWflag(a5)	; clear pending write
	move.w	#511,AB.BADFLag(a5) ; sectors all bad

	lea	AB.BUFENd(a5),a5

	move.l	a5,DV.SIDE1buff(a4) ; addrs buffer vars, side 1
	move.w	d3,AB.DRIVE(a5)	; set disk #
	move.w	#1,AB.SIDE(a5)	; set second side
	clr.w	AB.TRACK(a5)	; set first track
	clr.w	AB.PENDWflag(a5)	; clear pending write
	move.w	#511,AB.BADFLag(a5) ; sectors all bad

; initialise drive status

	move.w	d3,d0

	bset	d0,FV.CNGFLag(a3) ; set disk changed
	bclr	d0,FV.RDYFLag(a3) ; set drive not ready

	bsr	hw_GO_TK0

MOUNTOK:
	moveq	#0,d0

	cmp.w	FV.MAXDRive(a3),d3
	ble.s	MOUNTX
	move.w	d3,FV.MAXDRive(a3)

MOUNTX:
	movem.l	(a7)+,d3/a3-a5

	rts

; -------------------------------------------------------------
;    interfacing to the CST floppy controller body

fd_read:
	movem.l	d0/d3-d4/a3,-(a7)

	ifd	debug
	movem.l	d0/a0,-(a7)	; temporary aberration
	move.l	#0,a0
	move.l	#$00020000+'RD',d0
	bsr	IOD0
	move.l	#0,a0
	move.l	d1,d0
	bsr	HEX08
	movem.l	(a7)+,d0/a0
	endc

	moveq	#0,d3
	move.w	fdd_rbeg(a3),d3	; get no of bytes to skip
	moveq	#0,d4
	move.w	fdd_rend(a3),d4	; get no of bytes to leave

	move.l	AV.DSKV,a3	; Address of disk vars, a3

fd_readl:
	bsr	RQSEC

	move.w	d0,d2		; error return in d2 !
	movem.l	(a7)+,d0/d3-d4/a3
	rts

; -------------------------------------------------------------
fd_ftrack:
	movem.l	a3,-(a7)

	ifd	debug
	movem.l	d0/a0,-(a7)	; temporary aberration
	move.l	#0,a0
	move.l	#$00020000+'FT',d0
	bsr	IOD0
	movem.l	(a7)+,d0/a0
	endc

	move.l	AV.DSKV,a3	; Address of disk vars, a3

fd_ftrackl:
	bsr	FTRACK

	movem.l	 (a7)+,a3
	rts

; -------------------------------------------------------------
fd_write:
	movem.l	d0/a3,-(a7)

	ifd	debug
	movem.l	d0/a0,-(a7)	; temporary aberration
	move.l	#0,a0
	move.l	#$00020000+'WR',d0
	bsr	IOD0
	move.l	#0,a0
	move.l	d1,d0
	bsr	HEX08
	movem.l	(a7)+,d0/a0
	endc

	move.l	AV.DSKV,a3	; Address of disk vars, a3

fd_writel:
	bsr	WQSEC

	move.w	d0,d2		; error return in d2 !
	movem.l	(a7)+,d0/a3
	rts

; -------------------------------------------------------------
fd_side:
	movem.l	d0-d1/a3,-(a7)

	ifd	debug
	movem.l	d0/a0,-(a7)	; temporary aberration
	move.l	#0,a0
	move.l	#$00020000+'SD',d0
	bsr	IOD0
	move.l	#0,a0
	move.l	d1,d0
	bsr	HEX08
	movem.l	(a7)+,d0/a0
	endc

	move.l	AV.DSKV,a3	; Address of disk vars, a3

fd_sidel:
	moveq	#0,d0
	move.b	d1,d0
	bsr	SEL_SIDE

	movem.l	(a7)+,d0-d1/a3
	rts

; -------------------------------------------------------------
fd_select:
	movem.l	d0-d1/a3,-(a7)

	ifd	debug
	movem.l	d0/a0,-(a7)	; temporary aberration
	move.l	#0,a0
	move.l	#$00020000+'SL',d0
	bsr	IOD0
	move.l	#0,a0
	move.l	d1,d0
	bsr	HEX08
	movem.l	(a7)+,d0/a0
	endc

	move.l	AV.DSKV,a3	; Address of disk vars, a3

fd_selctl:
	cmp.b	#4,d1		; drive within range ?
	bgt.s	fd_slctx

	moveq	#0,d0
	move.b	d1,d0
	subq.b	#1,d0
	ext.w	d0
	bsr	SEL_DRV

fd_slctx
	movem.l	(a7)+,d0-d1/a3
	rts

; -------------------------------------------------------------
fd_seek:
	movem.l	d0-d1/a3,-(a7)

	ifd	debug
	movem.l	d0/a0,-(a7)	; temporary aberration
	move.l	#0,a0
	move.l	#$00020000+'SK',d0
	bsr	IOD0
	move.l	#0,a0
	move.l	d1,d0
	bsr	HEX08
	movem.l	(a7)+,d0/a0
	endc

	move.l	AV.DSKV,a3	; Address of disk vars, a3

fd_seekl:
	moveq	#0,d0
	move.b	d1,d0
	bsr	GO_TRACK

	movem.l	(a7)+,d0-d1/a3
	rts

; -------------------------------------------------------------
fd_restore:
	movem.l	a3,-(a7)

	ifd	debug
	movem.l	d0/a0,-(a7)	; temporary aberration
	move.l	#0,a0
	move.l	#$00020000+'RS',d0
	bsr	IOD0
	movem.l	(a7)+,d0/a0
	endc

	move.l	AV.DSKV,a3	; Address of disk vars, a3

fd_restrl:
	moveq	#0,d0
	bsr	GO_TRACK

	movem.l	(a7)+,a3
	rts

; -------------------------------------------------------------
fd_wpro:
	movem.l	a3,-(a7)

	ifd	debug
	movem.l	d0/a0,-(a7)	; temporary aberration
	move.l	#0,a0
	move.l	#$00020000+'WP',d0
	bsr	IOD0
	movem.l	(a7)+,d0/a0
	endc

	move.l	AV.DSKV,a3	; Address of disk vars, a3

fd_wprol:
	bsr	WPRO

	ifd	debug
	movem.l	d0/a0,-(a7)	; temporary aberration
	move.l	#0,a0
	bsr	HEX08
	movem.l	(a7)+,d0/a0
	endc

	movem.l	(a7)+,a3
	rts

; -------------------------------------------------------------
fd_chng:
	movem.l	a3,-(a7)

	ifd	debug
	movem.l	d0/a0,-(a7)	; temporary aberration
	move.l	#0,a0
	move.l	#$00020000+'CG',d0
	bsr	IOD0
	movem.l	(a7)+,d0/a0
	endc

	move.l	AV.DSKV,a3	; Address of disk vars, a3

fd_chngl:
	bsr	CHNG

	ifd	debug
	movem.l	d0/a0,-(a7)	; temporary aberration
	move.l	#0,a0
	bsr	HEX08
	movem.l	(a7)+,d0/a0
	endc

	movem.l	(a7)+,a3
	rts

; -------------------------------------------------------------
fd_ckrdy:
	movem.l	a3,-(a7)

	ifd	debug
	movem.l	d0/a0,-(a7)	; temporary aberration
	move.l	#0,a0
	move.l	#$00020000+'CK',d0
	bsr	IOD0
	movem.l	(a7)+,d0/a0
	endc

	move.l	AV.DSKV,a3	; Address of disk vars, a3

fd_ckrdyl:
	bsr	CKRDY

	ifd	debug
	movem.l	d0/a0,-(a7)	; temporary aberration
	move.l	#0,a0
	bsr	HEX08
	movem.l	(a7)+,d0/a0
	endc

	movem.l	(a7)+,a3
	rts

; -------------------------------------------------------------
fd_raddr:
	movem.l	d2/a0/a3-a5,-(a7)

	ifd	debug
	movem.l	d0/a0,-(a7)	; temporary aberration
	move.l	#0,a0
	move.l	#$00020000+'RA',d0
	bsr	IOD0
	movem.l	(a7)+,d0/a0
	endc

	move.l	AV.DSKV,a3	; Address of disk vars, a3

fd_raddrl:
	move.w	FV.REQDRive(a3),d0 ; Required drive in d0
	and.w	#$3,d0		; Only drives 0-3.

	move.w	d0,d2		; Address of drive vars for
	mulu.w	#DV_LEN,d2	; required drive in a4
	lea	FV.DRVVArs(a3,d2.w),a4

	move.w	DV.TRACK(a4),d1

	movem.l	(a7)+,d2/a0/a3-a5
	moveq	#0,d0
	rts

; -------------------------------------------------------------
fd_dskcng:
	movem.l	d0/a3,-(a7)

	ifd	debug
	movem.l	d0/a0,-(a7)	; temporary aberration
	move.l	#0,a0
	move.l	#$00020000+'CD',d0
	bsr	IOD0
	movem.l	(a7)+,d0/a0
	endc

	move.l	AV.DSKV,a3	; Address of disk vars, a3

fd_dscngl:
	move.w	FV.REQDRive(a3),d0

	bsr	DSKCNG

	movem.l	(a7)+,d0/a3
	rts

; -------------------------------------------------------------
;  Flush ASCII buffer for drive d0.w, only if it is truely
;  owned by that drive. Called before motor is switched off
;  Error returned in d0.
; -------------------------------------------------------------
FLUSH_BUFF:
	movem.l	d1-d2,-(a7)

	move.w	FV.REQDRive(a3),d2 ; save old required drive
	move.w	d0,FV.REQDRive(a3) ; select this drive
	bsr	WQDISK		; write the buffer out
	move.w	d2,FV.REQDRive(a3) ; restore old drive

FLUSH_BUFX:
	tst.b	d0
	movem.l	(a7)+,d1-d2
	rts

; -------------------------------------------------------------
;  Free ASCII buffer for drive d0.w by flushing contents,
;  irrespective of the owner. Called before new data is
;  loaded into the buffer. Error returned in d0.
; -------------------------------------------------------------
FREE_BUFF:
	movem.l	d2/a3-a5,-(a7)

	move.l	AV.DSKV,a3	; Address of disk vars, a3

	and.w	#$3,d0		; Only drives 0-3.

	move.w	d0,d2		; Address of drive vars for
	mulu.w	#DV_LEN,d2	; required drive in a4
	lea	FV.DRVVArs(a3,d2.w),a4

	move.w	DV.SIDE(a4),d2	; Address of buffer vars
	lsl.w	#2,d2		; for required side in a5
	move.l	DV.SIDE0buff(a4,d2.w),a5

	moveq	#0,d0
	tst.w	AB.PENDWflag(a5)	; any pending writes ?
	beq.s	FREE_BUFX	; no !

	move.w	FV.REQDRive(a3),d2 ; save old required drive
	move.w	AB.DRIVE(a5),FV.REQDRive(a3)
	bsr	WQDISK		; write the buffer out
	move.w	d2,FV.REQDRive(a3) ; restore old drive

FREE_BUFX:
	tst.b	d0
	movem.l	(a7)+,d2/a3-a5
	rts

; -------------------------------------------------------------
;  Format current track for drive FV.REQDRive then verify track.
;  Error returned in d0.
; -------------------------------------------------------------
FTRACK:
	movem.l	d2/a3-a5,-(a7)

	move.l	AV.DSKV,a3	; Address of disk vars, a3

	move.w	FV.REQDRive(a3),d0
	and.w	#$3,d0		; only drives 0-3

	move.w	d0,d2		; Address of drive vars for
	mulu.w	#DV_LEN,d2	; required drive in a4
	lea	FV.DRVVArs(a3,d2.w),a4

	move.w	DV.SIDE(a4),d2	; Address of buffer vars
	lsl.w	#2,d2		; for required side in a5
	move.l	DV.SIDE0buff(a4,d2.w),a5

	move.w	#511,AB.PENDWflag(a5); indicate a write pending
	move.w	#511,AB.BADFLag(a5) ; and all sectors bad
	move.w	d0,AB.DRIVE(a5)
	move.w	d0,d2		; save drive number
	bsr	WQDISK		; write track to disk

	clr.w	AB.BADFLag(a5)
	bset	d2,FV.CNGFLag(a3) ; signal disk change so as
	bclr	d2,FV.RDYFLag(a3) ; to force a track read

FTRACKX:
	tst.l	d0

	movem.l	(a7)+,d2/a3-a5
	rts

; -------------------------------------------------------------
;  Write one Track:
;  If the writeflag is not set, then skip this operation
;  Now prepare this Track and code it to MFM, write it to Disk,
;  and reset the writeflag. Error returned in d0.
; -------------------------------------------------------------
WQDISK:
	movem.l	d1-d2/d4-d5/a0-a1/a3-a5,-(a7)

	move.l	AV.DSKV,a3	; Address of disk vars, a3

	move.w	FV.REQDRive(a3),d0
	and.w	#$3,d0		; only drives 0-3

	move.w	d0,d2		; Address of drive vars for
	mulu.w	#DV_LEN,d2	; required drive in a4
	lea	FV.DRVVArs(a3,d2.w),a4

	move.w	DV.SIDE(a4),d2	; Address of buffer vars
	lsl.w	#2,d2		; for required side in a5
	move.l	DV.SIDE0buff(a4,d2.w),a5

	tst.w	AB.PENDWflag(a5)	; any pending writes ?
	beq	WQABORT_OK	; no !

	cmp.w	AB.DRIVE(a5),d0 ; does buffer belong to me?
	bne	WQABORT_OK	; no !

; check write protect status before trying to write

	bsr	WPRO
	tst.b	d0
	bne	WQABORT_WP

; make sure drive is switched on
	move.w	FV.REQDRive(a3),d0
	bsr	SEL_DRV		; check and switch on drive
	bne	WQABORT_NF	; drive not found

WQDSK1:
	move.l	#MFMBUFFER,a1	; address of MFM buffer, a1

	clr.w	FV.LASTBit(a3)	; new MFM calculation

; Now write 9 sectors in QDOS format

	moveq	#0,d4		; initialise sector count

; make some room between index mark and first header

	moveq	#91,d1		; counter for 92 words
WQROOMLP:
	moveq	#$4E,d0		; byte to write is $4E
	bsr	ASCMFMD0
	move.w	d0,(a1)+
	dbra	d1,WQROOMLP

WQDSKLP:

; preamble

	moveq	#83,d1		; counter for 24+60 words
WQPRELP:
	moveq	#$4E,d0		; byte to write is $4E
	bsr	ASCMFMD0
	move.w	d0,(a1)+
	dbra	d1,WQPRELP

; syncron 1

	moveq	#11,d1		; counter for 12 words
WQSYNLP1:
	moveq	#0,d0		; byte to write is 0
	bsr	ASCMFMD0
	move.w	d0,(a1)+
	dbra	d1,WQSYNLP1

; header block

	move.l	#$44894489,(a1)+	; write 3 MFM sync words
	move.l	#$44895554,(a1)+	; and header block ID

	move.w	#$A1FE,FV.LASTBit(a3) ; set last bit variable

; create an ASCII header block for this sector

	lea	6(a1),a0 	; a reasonably safe place

	move.w	DV.TRACK(a4),d0
	move.b	d0,(a0)+ 	; track 0-79
	move.w	DV.SIDE(a4),d0
	move.b	d0,(a0)+ 	; side 0/1
	move.b	d4,(a0)
	addq.b	#1,(a0)+ 	; sector 1-9
	move.b	#2,(a0)+ 	; bytes/sector, 2=512

	lea	-4(a0),a0	; start of ASCII header
	moveq	#4,d1		; bytes in CRC calculation
	move.w	#$B230,d0	; initialise CRC
	bsr	CALCCRC
	move.w	d0,4(a0) 	; write ASCII CRC

	moveq	#$5,d1		; count for 6 ASCII bytes

WQHEDLP:
	move.b	(a0)+,d0
	bsr	ASCMFMD0
	move.w	d0,(a1)+
	dbra	d1,WQHEDLP

; trailer 1

	moveq	#21,d1		; count for 22 MFM words

WQTRLLP1:
	moveq	#$4E,d0		; byte to write is $4E
	bsr	ASCMFMD0
	move.w	d0,(a1)+
	dbra	d1,WQTRLLP1

; syncron 2

	moveq	#11,d1		; count for 12 MFM words

WQSYNLP2:
	moveq	#0,d0		; byte to write is 0
	bsr	ASCMFMD0
	move.w	d0,(a1)+
	dbra	d1,WQSYNLP2

; data block

	move.l	#$44894489,(a1)+	; write 3 MFM sync words
	move.l	#$44895545,(a1)+	; and data block ID

	move.w	#$A1FB,FV.LASTBit(a3) ; set last bit variable

	move.w	AB.BADFLag(a5),d0
	btst	d4,d0		; check if sector is OK
	beq.s	WQDSK2

; sector is bad or undefined so create one from scratch

	move.l	#$252AA52A,(a1)+	; initialise with 512 MFM
	move.w	#254,d1		; words. The MFM equivalent
				; of $30.
WQINILP:
	move.l	#$A52AA52A,(a1)+
	dbra	d1,WQINILP

	move.l	#$44952551,(a1)+	; write MFM of CRC word

	move.w	#$A73D,FV.LASTBit(a3) ; set last bit variable

	bra	WQDSK3

; sector is OK so use proper ASCII buffer

WQDSK2:
	move.l	d4,d0		; sector number-1
	lsl.w	#2,d0		; find offset
	move.l	AB.SCTR1(a5,d0.w),a0 ; ASCII buffer address

	move.w	#513,d1		; 514 ASCII bytes

WQDATLP:
	move.b	(a0)+,d0
	bsr	ASCMFMD0
	move.w	d0,(a1)+
	dbra	d1,WQDATLP

WQDSK3:
	addq	#1,d4
	cmpi.b	#8,d4
	ble	WQDSKLP

; write excess bytes

	move.w	#(WLEN/2-(92+9*(24+60+12+10+22+12+518))),d1

WQXESLP:
	moveq	#$4E,d0		; byte to write is $4E
	bsr	ASCMFMD0
	move.w	d0,(a1)+
	dbra	d1,WQXESLP

; now write MFM track

	move.w	AB.DRIVE(a5),d0
	move.w	DV.SIDE(a4),d1
	move.w	DV.TRACK(a4),d2
	move.l	#MFMBUFFER,a0	; start of track image

	ifd	debug

	movem.l	d0/a0,-(a7)	; temporary aberration
	suba.l	a0,a0
	move.w	AB.DRIVE(a5),d0
	bsr	HEX08
	move.w	DV.SIDE(a4),d0
	bsr	HEX08
	move.w	DV.TRACK(a4),d0
	bsr	HEX08
	movem.l	(a7)+,d0/a0

	movem.l	d0/a0,-(a7)	; temporary aberration
	suba.l	a0,a0
	move.l	#$00020000+'of',d0
	bsr	IOD0
	movem.l	(a7)+,d0/a0

	endc

	bsr	SCR_OFF

	ifd	debug

	movem.l	d0/a0,-(a7)	; temporary aberration
	suba.l	a0,a0
	move.l	#$00020000+'=o',d0
	bsr	IOD0
	movem.l	(a7)+,d0/a0

	endc

	bsr	hw_WR_DSK	; write buffer to disk

; make flags right

	clr.w	AB.PENDWflag(a5)

	bclr	d0,FV.CNGFLag(a3)

; invalidate mfm buffer

	move.l	#MFMBUFFER,a1	; address of MFM buffer, a1
	lea	184(a1),a1
	moveq	#8,d0		; loop 9 times

WQSCRUB:
	lea	198(a1),a1
	clr.w	(a1)
	lea	88(a1),a1
	clr.w	(a1)
	lea	1030(a1),a1
	dbra	d0,WQSCRUB

WQABORT_OK:
	moveq	#0,d0
	bra.s	WQDISKX

WQABORT_WP:
	moveq	#-20,d0		; signal writeprotect
	bra.s	WQDISKX

WQABORT_NF:
	moveq	#-7,d0		; signal drive not found

WQDISKX:
	tst.b	d0
	movem.l	(a7)+,d1-d2/d4-d5/a0-a1/a3-a5
	rts

; -------------------------------------------------------------
; The following section uses a slightly different conventions
; in order to adopt it to the CST floppy controller software.
; The sector is passed in D1, the I/O buffer address in A1
; -------------------------------------------------------------

; -------------------------------------------------------------
;  Read one Track:
;  If the Track in the Buffer has the Writeflag set, then write
;  this track !
;  Now read a track, and decode it according to MFM conventions
; -------------------------------------------------------------
RQDISK:
	movem.l	d1-d3/a0-a5,-(a7)

	move.l	AV.DSKV,a3	; Address of disk vars, a3

	move.w	FV.REQDRive(a3),d0
	and.w	#$3,d0		; only drives 0-3

	move.w	d0,d2		; Address of drive vars for
	mulu.w	#DV_LEN,d2	; required drive in a4
	lea	FV.DRVVArs(a3,d2.w),a4

	move.w	DV.SIDE(a4),d2	; Address of buffer vars
	lsl.w	#2,d2		; for required side in a5
	move.l	DV.SIDE0buff(a4,d2.w),a5

; first check if buffer already contains correct info

	cmp.w	AB.DRIVE(a5),d0
	bne.s	RQDISK1

	move.w	DV.SIDE(a4),d1
	cmp.w	AB.SIDE(a5),d1
	bne.s	RQDISK1

	move.w	DV.TRACK(a4),d1
	cmp.w	AB.TRACK(a5),d1
	bne.s	RQDISK1

; disk changed?

	bsr	CHNG
	tst.b	d0
	beq	RQABORT_OK	; quit if disk not changed

; now check for pending write operations

RQDISK1:

	bsr	FREE_BUFF
	bne	RQDISKX		; cannot free buffer

; make sure drive is switched on

	move.w	FV.REQDRive(a3),d0
	bsr	SEL_DRV		; check and switch on drive
	bne	RQABORT_NF	; drive not found

; indicate new owner of this buffer

	move.w	FV.REQDRive(a3),AB.DRIVE(a5)
	move.w	DV.SIDE(a4),AB.SIDE(a5)
	move.w	DV.TRACK(a4),AB.TRACK(a5)

; currently no changes to sectors

	clr.w	AB.PENDWflag(a5)

; currently no good sectors at all

	move.w	#511,AB.BADFLag(a5)

	move.w	FV.FLAGS(a3),d3
	and.w	#%0000000000100000,d3 ; number of retries
	lsr.w	#3,d3

RQTRYLOOP:
	lea	AB.BUFBEg(a5),a0	; address to put ASCII
	movem.l	a0,-(a7) 	; save for later


;  now read MFM track

	move.w	AB.DRIVE(a5),d0
	move.w	DV.SIDE(a4),d1
	move.w	DV.TRACK(a4),d2
	move.l	#MFMBUFFER,a0	; start of track image

	ifd	debug

	movem.l	d0/a0,-(a7)	; temporary aberration
	suba.l	a0,a0
	move.w	AB.DRIVE(a5),d0
	bsr	HEX08
	move.w	DV.SIDE(a4),d0
	bsr	HEX08
	move.w	DV.TRACK(a4),d0
	bsr	HEX08
	movem.l	(a7)+,d0/a0

	movem.l	d0/a0,-(a7)	; temporary aberration
	suba.l	a0,a0
	move.l	#$00020000+'of',d0
	bsr	IOD0
	movem.l	(a7)+,d0/a0

	endc

	bsr	SCR_OFF

	ifd	debug

	movem.l	d0/a0,-(a7)	; temporary aberration
	suba.l	a0,a0
	move.l	#$00020000+'=i',d0
	bsr	IOD0
	movem.l	(a7)+,d0/a0

	endc

	bsr	hw_RD_DSK

	movem.l	(a7)+,a0 	; ASCII buffer -> a0

	bne	RQRETRY

; signal disk has not changed

	bclr	d0,FV.CNGFLag(a3)

; begin MFM translation

	move.l	#MFMBUFFER,a1	; track image -> a1
	lea	$4000(a1),a2	; end of track image

; first search for header block in track image

RQDSKLP2:
	cmpi.w	#$4489,(a1)+	; find sync pattern
	beq.s	RQDSK1
	cmpa.l	a2,a1
	blt.s	RQDSKLP2
	bra	RQRETRY

RQDSK1:
	cmpi.w	#$4489,(a1)	; skip over sync
	bne.s	RQDSK2
	addq.l	#2,a1
	bra.s	RQDSK1

RQDSK2:
	cmp.w	#$5554,(a1)	; find header block marker
	bne	RQDSKLP2

; header block found OK

	clr.w	(a1)+		; make sure its not re-used
	moveq	#0,d1		; counter for 6 ASCII bytes

RQDSKLP3:
	move.w	(a1)+,d0
	cmp.w	#$4489,d0	; drop out if sync found
	beq	RQDSK1
	bsr	MFMASCD0
	move.b	d0,0(a0,d1.w)	; store header as ASCII
	addq.w	#1,d1
	cmp.w	#6,d1
	blt.s	RQDSKLP3

	moveq	#4,d1
	move.w	#$B230,d0	; initialise CRC
	bsr	CALCCRC		; calculate CRC for header

	move.w	4(a0),d1 	; get CRC read from disk
	cmp.w	d0,d1		; compare with calculated
	beq	RQDSK3		; continue if OK

	ifd	debug

	movem.l	d0/a0,-(a7)	; temporary aberration
	move.l	#0,a0
	move.l	#$00020000+'-h',d0
	bsr	IOD0
	move.l	#0,a0
	move.l	#$00020000+'??',d0
	bsr	IOD0
	movem.l	(a7)+,d0/a0

	endc

	lea	-12(a1),a1	; otherwise search for
	bra	RQDSKLP2 	; another header sync

RQDSK3:
	move.w	AB.DRIVE(a5),d0
	moveq	#0,d2
	move.b	(a0),d2		; check track
	sub.w	DV.TRACK(a4),d2
	beq.s	RQDSK3A

	bgt.s	RQDSKL2

RQDSKL1:
	bsr	hw_STEP_IN	; increase track
	addq.w	#1,d2
	blt.s	RQDSKL1

	bra	RQRETRY

RQDSKL2:
	bsr	hw_STEP_OUT	; decrease track
	subq.w	#1,d2
	bgt.s	RQDSKL2

	bra	RQRETRY

RQDSK3A:
	move.w	DV.SIDE(a4),d1
	cmp.b	1(a0),d1 	; check side
	beq.s	RQDSK3B

	bsr	hw_SEL_SIDE
	bra	RQRETRY

RQDSK3B:
	moveq	#0,d2
	move.b	2(a0),d2 	; get sector number
	subq	#1,d2

	ifd	debug

	movem.l	d0/a0,-(a7)	; temporary aberration
	move.l	#0,a0
	move.l	#$00020000+'+h',d0
	bsr	IOD0
	move.l	#0,a0
	move.l	d2,d0
	bsr	HEX08
	movem.l	(a7)+,d0/a0

	endc

	move.w	AB.BADFLag(a5),d0
	btst	d2,d0
	beq	RQDSKLP2

; CRC for header block was OK, now search for data block

RQDSK4:
	cmpi.w	#$4489,(a1)+	; find sync pattern
	beq.s	RQDSK5
	cmpa.l	a2,a1
	blt.s	RQDSK4
	bra	RQRETRY

RQDSK5:
	cmpi.w	#$4489,(a1)	; skip over sync
	bne.s	RQDSK6
	addq.l	#2,a1
	bra.s	RQDSK5

RQDSK6:
	cmp.w	#$5545,(a1)	; find data block marker
	bne	RQDSK2		; data block was not found

; data block found OK

RQDSK7:
	clr.w	(a1)+		; make sure its not re-used
	moveq	#0,d1		; counter for 514 bytes

RQDSKLP4:
	move.w	(a1)+,d0
	cmp.w	#$4489,d0	; drop out if sync found
	beq	RQDSK1
	bsr	MFMASCD0
	move.b	d0,0(a0,d1.w)
	addq.w	#1,d1
	cmp.w	#$202,d1
	blt.s	RQDSKLP4

	move.w	#$200,d1
	move.w	#$E295,d0	; initialise CRC
	bsr	CALCCRC		; calculate CRC for data

	move.w	$200(a0),d1	; get CRC read from disk
	cmp.w	d0,d1		; compare with calculated
	beq	RQDSK8		; and continue if OK

	ifd	debug

	movem.l	d0/a0,-(a7)	; temporary aberration
	move.l	#0,a0
	move.l	#$00020000+'-d',d0
	bsr	IOD0
	move.l	#0,a0
	move.l	d2,d0
	bsr	HEX08
	movem.l	(a7)+,d0/a0

	endc

	lea	-$404(a1),a1	; otherwise search for
	bra	RQDSKLP2 	; another header sync

; CRC for data block was OK

RQDSK8:
	ifd	debug

	movem.l	d0/a0,-(a7)	; temporary aberration
	move.l	#0,a0
	move.l	#$00020000+'+d',d0
	bsr	IOD0
	move.l	#0,a0
	move.l	d2,d0
	bsr	HEX08
	movem.l	(a7)+,d0/a0

	endc

	move.w	d2,d0
	lsl.w	#2,d0
	move.l	a0,AB.SCTR1(a5,d0.w) ; store data address

	lea	$202(a0),a0	; skip over data, 512 bytes

	move.w	AB.BADFLag(a5),d0
	bclr	d2,d0
	move.w	d0,AB.BADFLag(a5)

	bne	RQDSKLP2

RQABORT_OK:
	moveq	#0,d0
	bra.s	RQDISKX

RQRETRY:
	dbra	d3,RQTRYLOOP

RQABORT_BM:
	moveq	#ERR.FE,d0	; bad or changed medium
	bra.s	RQDISKX

RQABORT_NF:
	moveq	#ERR.NF,d0	; signal drive not found

RQDISKX:
	tst.b	d0
	movem.l	(a7)+,d1-d3/a0-a5
	rts

; -------------------------------------------------------------
;  Write one sector (D1) from buffer (A1)
;  If the actual parameters are not the same as for the track in
;  the track buffer, then call RQDISK.
;  Copy (A1) to Track buffer
;  set writeflag !
; -------------------------------------------------------------
WQSEC:
	movem.l	d1-d2/a0-a5,-(a7)

	move.l	AV.DSKV,a3	; Address of disk vars, a3

	move.w	FV.REQDRive(a3),d0
	and.w	#$3,d0		; only drives 0-3

	cmp.w	FV.MAXDRive(a3),d0
	bgt.s	WQSEC_BM

	move.w	d0,d2		; Address of drive vars for
	mulu.w	#DV_LEN,d2	; required drive in a4
	lea	FV.DRVVArs(a3,d2.w),a4

	move.w	DV.SIDE(a4),d2	; Address of buffer vars
	lsl.w	#2,d2		; for required side in a5
	move.l	DV.SIDE0buff(a4,d2.w),a5

; first check if buffers contain correct info

	cmp.w	AB.DRIVE(a5),d0
	bne.s	WQSEC1

	move.w	DV.SIDE(a4),d2
	cmp.w	AB.SIDE(a5),d2
	bne.s	WQSEC1

	move.w	DV.TRACK(a4),d2
	cmp.w	AB.TRACK(a5),d2
	bne.s	WQSEC1

; disk changed?

	bsr	CHNG
	tst.b	d0
	beq.s	WQSEC2

; if so, read correct track into buffers

WQSEC1	move.w	FV.REQDRive(a3),d0
	bsr	RQDISK
	bne	WQSECX

; determine position of sector

WQSEC2:
	moveq	#0,d0
	move.b	d1,d0
	lsl.w	#2,d0
	move.l	AB.SCTR1(a5,d0.w),a0 ; address of ASCII data

	move.w	#511,d0		; load counter

WQSEC3:
	move.b	(a1)+,(a0)+	; copy 512 bytes
	dbra	d0,WQSEC3

	lea	-512(a0),a0	; start of ASCII data
	move.w	#512,d1		; count of bytes
	move.w	#$E295,d0	; initialise CRC
	bsr	CALCCRC
	move.w	d0,512(a0)	; write ASCII CRC for data

	bset.w	d1,AB.PENDWflag(a5)	; mark sector for write
	bclr.w	d1,AB.BADFLag(a5) ; mark sector as OK

	moveq	#0,d0

	move.w	#200,DV.TIMEOut(a4)

	bra	WQSECX

WQSEC_BM:
	moveq	#-16,d0		; bad or changed medium

WQSECX:
	tst.b	d0
	movem.l	(a7)+,d1-d2/a0-a5
	rts

; -------------------------------------------------------------
;  Read one Sector (D1) to buffer (A1) , (A1 points to the end)
;  skip (D3) bytes, and leave (D4) at the end
;
;  call RQDISK and copy sector from Track buffer to (A1)
; -------------------------------------------------------------
RQSEC:
	movem.l	d1-d2/a0/a2-a5,-(a7)

	move.l	AV.DSKV,a3	; Address of disk vars, a3

	move.w	FV.REQDRive(a3),d0
	and.w	#$3,d0		; only drives 0-3

	cmp.w	FV.MAXDRive(a3),d0
	bgt	RQSEC_BM

	move.w	d0,d2		; Address of drive vars for
	mulu.w	#DV_LEN,d2	; required drive in a4
	lea	FV.DRVVArs(a3,d2.w),a4

	move.w	DV.SIDE(a4),d2	; Address of buffer vars
	lsl.w	#2,d2		; for required side in a5
	move.l	DV.SIDE0buff(a4,d2.w),a5

; first check if buffer already contains correct info

	cmp.w	AB.DRIVE(a5),d0
	bne.s	RQSEC1

	move.w	DV.SIDE(a4),d2
	cmp.w	AB.SIDE(a5),d2
	bne.s	RQSEC1

	move.w	DV.TRACK(a4),d2
	cmp.w	AB.TRACK(a5),d2
	bne.s	RQSEC1

; disk changed?

	bsr	CHNG
	tst.b	d0
	bne.s	RQSEC1		; read again if changed

; sector data bad?

	move.w	AB.BADFLag(a5),d2
	btst	d1,d2
	beq.s	RQSEC2		; sector data OK

RQSEC1:

	bsr	RQDISK

	move.w	AB.BADFLag(a5),d2
	btst	d1,d2
	bne	RQSEC_BM

; determine position of sector

RQSEC2:
	moveq	#0,d0
	move.b	d1,d0
	lsl.w	#2,d0
	move.l	AB.SCTR1(a5,d0.w),a0 ; address of ASCII

	and.l	#$1FF,d3 	; maximum bytes to skip
	add.l	d3,a0		; subtract bytes to skip
	move.w	#511,d0		; load counter
	sub.w	d3,d0		; subtract bytes to skip
	sub.w	d4,d0		; subtract bytes to leave

RQSEC3	move.b	(a0)+,(a1)+	; copy 512 bytes
	dbra	d0,RQSEC3

RQSEC_OK:
	moveq	#0,d0
	bra.s	RQSECX

RQSEC_BM:
	moveq	#-16,d0		; bad or changed medium

RQSECX:
	tst.b	d0
	movem.l	(a7)+,d1-d2/a0/a2-a5
	rts

; -------------------------------------------------------------
;  calculate the Cyclic Redundancy Check for d1 bytes at (a0)
;  Initial CRC is passed in d0, modified CRC is returned in d0
; -------------------------------------------------------------
CALCCRC:
	movem.l	d1-d3/a0,-(a7)
	bra.s	CRCNEXT

CRCLOOP:
	move.b	(a0)+,d3
	move.w	d0,d2
	lsr.w	#8,d0
	eor.b	d3,d0
	move.w	d0,d3
	lsr.w	#4,d0
	eor.w	d3,d0
	move.w	d0,d3
	lsl.w	#4,d0
	eor.w	d2,d0
	lsl.w	#3,d0
	eor.w	d3,d0
	lsl.w	#5,d0
	eor.w	d3,d0

CRCNEXT:
	dbra	d1,CRCLOOP
	movem.l	(a7)+,d1-d3/a0
	rts

; --------------------------------------------------------------
;  Convert d0.w from MFM to ASCII. Return value in d0.b
; --------------------------------------------------------------
MFMASCD0:
	movem.l	d1/a0,-(a7)

	andi.w	#%0101010101010101,d0
	move.b	d0,d1
	lsr.w	#7,d0
	or.b	d1,d0
	lea	MFMASCTB(pc),a0
	move.b	0(a0,d0.w),d0

	movem.l	(a7)+,d1/a0
	rts

; MFM to ASCII table

MFMASCTB:
   DC.B $00,$01,$10,$11,$02,$03,$12,$13,$20,$21,$30,$31,$22,$23,$32,$33
   DC.B $04,$05,$14,$15,$06,$07,$16,$17,$24,$25,$34,$35,$26,$27,$36,$37
   DC.B $40,$41,$50,$51,$42,$43,$52,$53,$60,$61,$70,$71,$62,$63,$72,$73
   DC.B $44,$45,$54,$55,$46,$47,$56,$57,$64,$65,$74,$75,$66,$67,$76,$77
   DC.B $08,$09,$18,$19,$0A,$0B,$1A,$1B,$28,$29,$38,$39,$2A,$2B,$3A,$3B
   DC.B $0C,$0D,$1C,$1D,$0E,$0F,$1E,$1F,$2C,$2D,$3C,$3D,$2E,$2F,$3E,$3F
   DC.B $48,$49,$58,$59,$4A,$4B,$5A,$5B,$68,$69,$78,$79,$6A,$6B,$7A,$7B
   DC.B $4C,$4D,$5C,$5D,$4E,$4F,$5E,$5F,$6C,$6D,$7C,$7D,$6E,$6F,$7E,$7F
   DC.B $80,$81,$90,$91,$82,$83,$92,$93,$A0,$A1,$B0,$B1,$A2,$A3,$B2,$B3
   DC.B $84,$85,$94,$95,$86,$87,$96,$97,$A4,$A5,$B4,$B5,$A6,$A7,$B6,$B7
   DC.B $C0,$C1,$D0,$D1,$C2,$C3,$D2,$D3,$E0,$E1,$F0,$F1,$E2,$E3,$F2,$F3
   DC.B $C4,$C5,$D4,$D5,$C6,$C7,$D6,$D7,$E4,$E5,$F4,$F5,$E6,$E7,$F6,$F7
   DC.B $88,$89,$98,$99,$8A,$8B,$9A,$9B,$A8,$A9,$B8,$B9,$AA,$AB,$BA,$BB
   DC.B $8C,$8D,$9C,$9D,$8E,$8F,$9E,$9F,$AC,$AD,$BC,$BD,$AE,$AF,$BE,$BF
   DC.B $C8,$C9,$D8,$D9,$CA,$CB,$DA,$DB,$E8,$E9,$F8,$F9,$EA,$EB,$FA,$FB
   DC.B $CC,$CD,$DC,$DD,$CE,$CF,$DE,$DF,$EC,$ED,$FC,$FD,$EE,$EF,$FE,$FF

; --------------------------------------------------------------
;  Convert byte d0.b, from ASCII to MFM. Return value in d0.w
; --------------------------------------------------------------
ASCMFMD0:
	movem.l	d1/a0,-(a7)

	move.l	AV.DSKV,a0
	move.w	FV.LASTBit(a0),d1
	lsl.w	#8,d1
	move.b	d0,d1
	move.w	d1,FV.LASTBit(a0)

	lea	ASCMFMTB(pc),a0
	lsr.w	#4,d1
	andi.w	#$1F,d1
	move.b	0(a0,d1.w),d1
	lsl.w	#8,d1
	andi.w	#$1F,d0
	move.b	0(a0,d0.w),d1
	move.w	d1,d0

	movem.l	(a7)+,d1/a0
	rts

; ASCII to MFM table

ASCMFMTB:
   DC.B $AA,$A9,$A4,$A5,$92,$91,$94,$95,$4A,$49,$44,$45,$52,$51,$54,$55
   DC.B $2A,$29,$24,$25,$12,$11,$14,$15,$4A,$49,$44,$45,$52,$51,$54,$55

; --------------------------------------------------------------
;  Select drive d0.w and switch on motor
;  eq = no errors, ne = drive error (held in d0)
; --------------------------------------------------------------
SEL_DRV:
	movem.l	d2/a3-a4,-(a7)

	move.l	AV.DSKV,a3	; Address of disk vars, a3

	and.w	#$3,d0		; Only drives 0-3.

	move.w	d0,d2		; Address of drive vars for
	mulu.w	#DV_LEN,d2	; required drive in a4
	lea	FV.DRVVArs(a3,d2.w),a4

	bclr	d0,FV.RDYFLag(a3) ; set drive not ready

; switch to this drive

	move.w	d0,FV.REQDRive(a3) ; switch drives

; check if motor already running

	tst.w	DV.TIMEOut(a4)
	bne.s	SEL_CK_CHG

; select new drive

	bsr	hw_MTR_ON

; check if drive motor can reach speed

	bsr	hw_IS_DRVRDY
;	 bne	 SEL_DRV_NF	 ; drive no good

SEL_CK_CHG:

; check for disk change

	bsr	hw_IS_CHG
	bne.s	SEL_CHG_NO

	bset	d0,FV.CNGFLag(a3) ; indicate disk not changed

SEL_CHG_NO:

; check if there's a disk in the drive

	bsr	hw_IS_CHG
	beq.s	SEL_DRV_NF

; select correct side for this drive

SEL_DRV_OK:
	move.w	DV.SIDE(a4),d1
	bsr	hw_SEL_SIDE

	move.w	FV.REQDRive(a3),d0
	bset	d0,FV.RDYFLag(a3) ; set drive ready

	moveq	#0,d0		; reset error number
	bra.s	SEL_DRVX

SEL_DRV_NF:
	bclr	d0,FV.RDYFLag(a3) ; set not drive ready
	moveq	#-7,d0		; not found

; set pending motor off for specified drive to 4 seconds

SEL_DRVX:
	move.w	#200,DV.TIMEOut(a4)

	tst.b	d0
	movem.l	(a7)+,d2/a3-a4
	rts

; -------------------------------------------------------------
;  Select side d0.w
;  eq = no errors, ne = drive error (held in d0)
; -------------------------------------------------------------
SEL_SIDE:
	movem.l	d1-d2/a3-a4,-(a7)

	move.w	d0,d1		; save side to set

	move.l	AV.DSKV,a3	; Address of disk vars, a3

	move.w	FV.REQDRive(a3),d0
	and.w	#$3,d0		; only drives 0-3

	move.w	d0,d2		; Address of drive vars for
	mulu.w	#DV_LEN,d2	; required drive in a4
	lea	FV.DRVVArs(a3,d2.w),a4

	cmp.w	DV.SIDE(a4),d1	; Same side?
	beq.s	SELSIDE1

	bsr	FLUSH_BUFF	; flush buffer
	bne.s	SELSIDEX

SELSIDE1:
	move.w	d1,DV.SIDE(a4)
	move.w	FV.REQDRive(a3),d0
	bsr	hw_SEL_SIDE

SELSIDE_OK:
	moveq	#0,d0

SELSIDEX:
	tst.b	d0
	movem.l	(a7)+,d1-d2/a3-a4
	rts

; -------------------------------------------------------------
;  Go to track d0.w
;  eq = no errors, ne = drive error (held in d0)
; -------------------------------------------------------------
GO_TRACK:
	movem.l	d1-d2/a3-a4,-(a7)

	move.w	d0,d1		; save track

	move.l	AV.DSKV,a3	; Address of disk vars, a3

	move.w	FV.REQDRive(a3),d0
	and.w	#$3,d0		; only drives 0-3

	move.w	d0,d2		; Address of drive vars for
	mulu.w	#DV_LEN,d2	; required drive in a4
	lea	FV.DRVVArs(a3,d2.w),a4

	cmp.w	DV.TRACK(a4),d1	; Same track?
	beq.s	GO_TRACK1

	bsr	FLUSH_BUFF	; flush buffer
	bne.s	GO_TRACKX

GO_TRACK1:
	move.w	FV.REQDRive(a3),d0
	bclr	d0,FV.CNGFLag(a3) ; clear disk change flag

	tst.b	d1		; Track 0 ?
	bne.s	ANYTRACK

	bsr	hw_GO_TK0
	clr.w	DV.TRACK(a4)

	bra.s	GO_TRACK_OK

ANYTRACK:
	move.w	DV.TRACK(a4),d2
	cmp.b	d2,d1
	bgt.s	SI_TRACK
	blt.s	SO_TRACK

GO_TRACK_OK:
	moveq	#0,d0

GO_TRACKX:
	tst.b	d0
	movem.l	(a7)+,d1-d2/a3-a4
	rts

SI_TRACK:
	bsr	hw_STEP_IN
	addq.w	#1,DV.TRACK(a4)
	bra.s	ANYTRACK

SO_TRACK:
	bsr	hw_STEP_OUT
	subq.w	#1,DV.TRACK(a4)
	bra.s	ANYTRACK

; -------------------------------------------------------------
;     Check if drive FV.REQDRive is ready. Return status in d0.l
; -------------------------------------------------------------
CKRDY:
	movem.l	d1/a3,-(a7)

	move.l	AV.DSKV,a3	; Address of disk vars, a3

	move.w	FV.REQDRive(a3),d0
	btst	d0,FV.CNGFLag(a3)
	bne.s	CKRDYTST

	btst	d0,FV.RDYFLag(a3)
	bne.s	CKRDYOK

CKRDYTST:
	bsr	SEL_DRV
	beq.s	CKRDYOK

CKRDYNOT:
	moveq	#-1,d0

	movem.l	(a7)+,d1/a3
	rts

CKRDYOK:
	moveq	#0,d0

	movem.l	(a7)+,d1/a3
	rts

; -------------------------------------------------------------
;  return status of writeprotect signal in d0.l for FV.REQDRive
; -------------------------------------------------------------
WPRO:
	movem.l	a3,-(a7)

	move.l	AV.DSKV,a3	; address of disk vars, a3

	move.w	FV.REQDRive(a3),d0
	bsr	hw_IS_PRO

	beq.s	WPRO_YES

WPRO_NO:
	moveq	#0,d0
	bra.s	WPRO_X

WPRO_YES:
	moveq	#1,d0

WPRO_X:
	movem.l	(a7)+,a3
	rts

; -------------------------------------------------------------
;  return status of diskchange signal in d0.l for FV.REQDRive
; -------------------------------------------------------------
CHNG:
	movem.l	d2/a3-a5,-(a7)

	move.l	AV.DSKV,a3	; address of disk vars, a3

	move.w	FV.REQDRive(a3),d0
	and.w	#$3,d0		; only drives 0-3

	btst	d0,FV.CNGFLag(a3)
	bne.s	CHNG_YES

	bsr	hw_IS_CHG
	beq.s	CHNG_YES

CHNG_NO:
	bclr	d0,FV.CNGFLag(a3) ; indicate disk not changed
	moveq	#0,d0
	movem.l	(a7)+,d2/a3-a5
	rts

CHNG_YES:
	move.w	d0,d2		; Address of drive vars for
	mulu.w	#DV_LEN,d2	; required drive in a4
	lea	FV.DRVVArs(a3,d2.w),a4

	bsr	CHNG_IT

	moveq	#1,d0
	movem.l	(a7)+,d2/a3-a5
	rts

; -------------------------------------------------------------
;  provide Diskchange signal for non standard drive d0
; -------------------------------------------------------------
DSKCNG:
	movem.l	d0/d2/a3-a5,-(a7)

	move.l	AV.DSKV,a3	; address of disk vars, a3

	and.w	#$3,d0		; Only drives 0-3.

	move.w	d0,d2		; Address of drive vars for
	mulu.w	#DV_LEN,d2	; required drive in a4
	lea	FV.DRVVArs(a3,d2.w),a4

	move.w	d0,-(a7)
	bsr	FLUSH_BUFF	; just in case
	move.w	(a7)+,d0

	bsr	CHNG_IT

	movem.l	(a7)+,d0/d2/a3-a5
	rts

CHNG_IT:
	move.l	DV.SIDE0buff(a4),a5 ; address of vars, side 0
	cmp.w	AB.DRIVE(a5),d0 ; is buffer owned by drive?
	bne.s	DSKCNG2		; no it isn't!

	move.w	#-1,AB.DRIVE(a5) ; set impossible disk
	move.w	#-1,AB.SIDE(a5) ; set impossible side
	move.w	#-1,AB.TRACK(a5) ; set impossible track
	clr.w	AB.PENDWflag(a5)	; clear pending write
	move.w	#511,AB.BADFLag(a5) ; sectors all bad

DSKCNG2:
	move.l	DV.SIDE1buff(a4),a5 ; address of vars, side 1
	cmp.w	AB.DRIVE(a5),d0 ; is buffer owned by drive?
	bne.s	DSKCNG3		; no it isn't!

	move.w	#-1,AB.DRIVE(a5) ; set impossible disk
	move.w	#-1,AB.SIDE(a5) ; set impossible side
	move.w	#-1,AB.TRACK(a5) ; set impossible track
	clr.w	AB.PENDWflag(a5)	; clear pending write
	move.w	#511,AB.BADFLag(a5) ; sectors all bad

DSKCNG3:
	bset	d0,FV.CNGFLag(a3) ; set disk changed
	bclr	d0,FV.RDYFLag(a3) ; set drive not ready

	bsr	hw_DRV_TYP
	move.l	d0,DV.TYPE(a4)

	rts

; -------------------------------------------------------------
;  50 Hz interrupt server	       (now changed to polled task)
;  switch off motor if requested
;  Flush writebuffer before motor_off
; -------------------------------------------------------------
POLSERV:
	movem.l	d0/d2/a3-a5,-(a7)

	move.l	AV.DSKV,a3	; Address of disk vars, a3

POLSERV1:
	bset.b	#7,FV.POLLActiv(a3) ; schedular already active?
	bne.s	POLX		; if so, then ignore

	move.w	FV.MAXDRive(a3),d0 ; check timeout, all drives

STPFLPLP:
	bsr	TESTSTOP
	dbra	d0,STPFLPLP

	bclr.b	#7,FV.POLLActiv(a3) ; mark schedular inactive

POLX:
	movem.l	(a7)+,d0/d2/a3-a5
	rts

TESTSTOP:
	move.w	d0,d2		; Address of drive vars for
	mulu.w	#DV_LEN,d2	; required drive in a4
	lea	FV.DRVVArs(a3,d2.w),a4

	move.w	DV.TIMEOut(a4),d2 ; anything to do?
	beq.s	INTEX

	subq.w	#1,d2
	move.w	d2,DV.TIMEOut(a4)
	tst.w	d2
	bne	INTEX

	move.w	d0,d2
	bsr	FLUSH_BUFF
	move.w	d2,d0

INTEX1:
	bsr	hw_MTR_OFF

INTEX:
	rts

; -------------------------------------------------------------
FLUSHALL:
	movem.l	d0/d2,-(a7)

	move.w	FV.MAXDRive(a3),d0

FLSHALLLP:
	move.w	d0,d2
	bsr	FLUSH_BUFF
	move.w	d2,d0
	dbra	d0,FLSHALLLP

	movem.l	(a7)+,d0/d2
	rts

; -------------------------------------------------------------
;  wait for no blitter activity, or a maximum of 40ms.
; -------------------------------------------------------------
SCR_OFF:
	movem.l	d0-d1,-(a7)

	move.l	#39,d1		; set count for 40 ms

sof_LUP1:
	bsr	hw_BEG_TIMING

sof_LUP2:
	btst.b	#6,DMACONR	; wait blitter not busy
	beq.s	sof_X

	bsr	hw_IS_TIMING
	beq.s	sof_LUP2

	dbra	d1,sof_LUP1

sof_X:
	movem.l	(a7)+,d0-d1
	rts

; -------------------------------------------------------------
;  ROUTINES THAT HIT THE HARDWARE DIRECTLY
; -------------------------------------------------------------

DISKCOPY:
	movem.l	d1-d3/d7/a0/a3,-(a7)

	moveq	#1,d0		; drive 1
	bsr	hw_IS_PRO
	bne.s	DC_CONT

	moveq	#ERR.RO,d3
	bra	DC_EXIT

DC_CONT:
	trap	#0
	or.w	#$0700,sr	; disable interrupts

	bsr	SCR_OFF		; wait for blitter

	move.l	AV.DSKV,a3
	move.w	FV.FLAGS(a3),d7	; save FV.FLAGS
	move.w	#%0011000000000000,FV.FLAGS(a3) ; indx R&W, no SYNC

	moveq	#0,d0		; drive 0
	bsr	hw_GO_TK0

	bsr	hw_MTR_ON
	bsr	hw_IS_DRVRDY
	beq.s	DC_CONT1

	moveq	#ERR.NF,d3
	bra.s	DC_X0

DC_CONT1:
	moveq	#1,d0		; drive 1
	bsr	hw_GO_TK0

	bsr	hw_MTR_ON
	bsr	hw_IS_DRVRDY
	beq.s	DC_CONT2

	moveq	#ERR.NF,d3
	bra.s	DC_X1

DC_CONT2:
	lea	MFMBUFFER,a0	; buffer address

; start in earnest

	moveq	#0,d2		; track number

DC_CPY_LUP:
	moveq	#0,d0		; drive 0
	moveq	#0,d1		; side number
	bsr	hw_RD_DSK	; read track

	moveq	#1,d0		; drive 1
	bsr	hw_WR_DSK	; write track

	moveq	#0,d0		; drive 0
	moveq	#1,d1		; side number
	bsr	hw_RD_DSK	; read track

	bsr	hw_STEP_IN

	moveq	#1,d0		; drive 1
	bsr	hw_WR_DSK	; write track

	bsr	hw_STEP_IN

	addq	#1,d2
	cmpi.b	#80,d2
	bne.s	DC_CPY_LUP

	moveq	#ERR.OK,d3
DC_X1:
	moveq	#1,d0		; drive 1
	bsr	hw_GO_TK0
	bsr	hw_MTR_OFF
	bsr	DSKCNG
DC_X0:
	moveq	#0,d0		; drive 0
	bsr	hw_GO_TK0
	bsr	hw_MTR_OFF
	bsr	DSKCNG

	move.w	d7,FV.FLAGS(a3)

	move.w	(sp)+,sr

DC_EXIT:
	move.l	d3,d0
	movem.l	(a7)+,d1-d3/d7/a0/a3
	rts

; -------------------------------------------------------------
;  Wait for d0 x 1000th of a second
; -------------------------------------------------------------
hw_TIMER:
	movem.l	d0,-(a7)

	bra.s	hw_TIMER3

hw_TIMER1:
	bsr	hw_BEG_TIMING

hw_TIMER2:
	bsr	hw_IS_TIMING
	beq.s	hw_TIMER2

hw_TIMER3:
	dbra	d0,hw_TIMER1

	movem.l	(a7)+,d0
	rts

; -------------------------------------------------------------
;  Set timer A for one 1000th of a second
; -------------------------------------------------------------
hw_BEG_TIMING:
	move.b	#$CC,CIAB_TALO	; and set counter to 716
	move.b	#$02,CIAB_TAHI
	move.b	#9,CIAB_CRA	; set timer to OneShot &
	; set the start bit

	rts

; -------------------------------------------------------------
;  Check if timer A is still going
;  eq = still timing, ne = run down
; -------------------------------------------------------------
hw_IS_TIMING:
	movem.l	d0-d1,-(a7)

	move.b	CIAB_CRA,d0
	eor.b	#1,d0
	and.b	#1,d0

	movem.l	(a7)+,d0-d1
	rts

; -------------------------------------------------------------
;  Wait until an INDEX interrupt is requested, or for 2 seconds
;  ne = INDEX found, eq = no INDEX found
; -------------------------------------------------------------
hw_WTIDX:
	movem.l	d0-d1,-(a7)

	move.b	CIAB_ICR,d0	; read & reset int flg
	or.b	AV.CIAB_ICR,d0
	bclr	#4,d0		; clear previous indx int
	move.b	d0,AV.CIAB_ICR	; store for another program

	move.l	#1999,d1 	; set count for 2000 ms

hw_WTIDX_LUP1:
	bsr	hw_BEG_TIMING

hw_WTIDX_LUP2:
	move.b	CIAB_ICR,d0	; read CIA-B ICR
	or.b	AV.CIAB_ICR,d0
	move.b	d0,AV.CIAB_ICR	; store for another program

	bclr	#4,d0		; was it index ?
	bne	hw_WTIDX_X	; ...yes

hw_WTIDX_CONT:
	bsr	hw_IS_TIMING
	beq.s	hw_WTIDX_LUP2

	dbra	d1,hw_WTIDX_LUP1

hw_WTIDX_X:
	addq.w	#1,d1		; set flags
	tst.w	d1

	movem.l	(a7)+,d0-d1

	rts

; -------------------------------------------------------------
;  Wait from disk DMA finished signal or for 0.5 seconds
;  ne = DMA finished, eq = DMA not complete
; -------------------------------------------------------------
hw_WTDSKDMA:
	movem.l	d0-d1,-(a7)


	move.l	#499,d1	        ; set count for 500 ms

hw_WTDMA_LUP1:
	bsr	hw_BEG_TIMING

hw_WTDMA_LUP2:
	move.w	INTREQR,d0
	btst	#1,d0		; is disk DMA finished?
	bne.s	hw_WTDSKDMA_X

	bsr	hw_IS_TIMING
	beq.s	hw_WTDMA_LUP2

	dbra	d1,hw_WTDMA_LUP1

hw_WTDSKDMA_X:
	move.w	#$4000,DSKLEN	; stop disk DMA

	move.w	#$0002,INTREQ	; clear interrupt flag

	addq.w	#1,d1		; set flags
	tst.w	d1

	movem.l	(a7)+,d0-d1

	rts

; --------------------------------------------------------------
;  Read MFM track from disk to buffer at a0.l
;  Pass drive number in d0.w, side in d1.w, track number in d2.w
;  eq = Track read OK, ne = disk error
; --------------------------------------------------------------
hw_RD_DSK:
	movem.l	d0-d3/a3,-(a7)

	moveq	#ERR.NF,d3

	bsr	hw_MTR_ON
;	 bsr	 hw_IS_DRVRDY
;	 bne	 hw_RD_DSK5

	bsr	hw_SEL_SIDE	; select correct side
	bsr	hw_SEL_DRV	; select drive

	move.w	#$4000,DSKLEN	; stop disk DMA
	move.w	#$8010,DMACON	; enable disk DMA
	move.l	a0,DSKPTH	; start of buffer
	move.w	#$6600,ADKCON	; PRECOMP=0, GCR SYNC off, WORDSYNC off
	move.w	#$9100,ADKCON	; MFMPREC for MFM, 2°s/bit

	cmp.w	#40,d2		; if it is a high track,
	blt.s	hw_RD_DSK1	; we need a
	move.w	#$A000,ADKCON	; 140 ns precompensation

hw_RD_DSK1:
	move.l	AV.DSKV,a3
	move.w	FV.FLAGS(a3),d2

	move.w	#(((TLEN+1324)>>1)&$3FFF)|$8000,d1

	btst	#12,d2		; branch if index enabled
	bne.s	hw_RD_DSK2

	btst	#11,d2		; branch if word SYNC disabled
	beq.s	hw_RD_DSK4

	move.w	#$8400,ADKCON	; SYNC on
	move.w	#$4489,DSKSYNC	; sync word

	bra.s	hw_RD_DSK4

;  do disk read via index interrupt

hw_RD_DSK2:
	move.w	#((TLEN>>1)&$3FFF)|$8000,d1

	btst	#11,d2		; branch if word SYNC disabled
	beq.s	hw_RD_DSK3

	move.w	#$8400,ADKCON	; SYNC on
	move.w	#$4489,DSKSYNC	; sync word
	move.w	#(((TLEN-376)>>1)&$3FFF)|$8000,d1

hw_RD_DSK3:

	bsr	hw_WTIDX 	; wait for index
	beq.s	hw_RD_DSK5	; branch on error

;  do disk read

hw_RD_DSK4:
	move.w	d1,DSKLEN	; initiate disk operation
	move.w	d1,DSKLEN

	move.w	#$0002,INTREQ	; clear previous interrupt
				; requests from disk DMA

;  wait for operation to finish, then exit

	bsr	hw_WTDSKDMA	; wait, until disk written
	beq.s	hw_RD_DSK5	; branch on error

	moveq	#ERR.OK,d3	; signal read error

hw_RD_DSK5:
	bsr	hw_DESEL_DRV	; deselect drive

	tst.l	d3		; set flags

	movem.l	(a7)+,d0-d3/a3
	rts

; -------------------------------------------------------------
;  Write MFM track to disk from buffer at a0.l
;  Pass drive number in d0.w, track number in d1.w
;  Requires you to have set up an interrupt server that acts
;  on the index pulse and begins the actual disk write and DMA
; -------------------------------------------------------------
hw_WR_DSK:
	movem.l	d0-d3/a3,-(a7)

	move.w	sr,-(a7)
	ori.w	#$0700,sr

	moveq	#ERR.NF,d3

	bsr	hw_MTR_ON
;	 bsr	 hw_IS_DRVRDY
;	 bne	 hw_WR_DSK4

	bsr	hw_SEL_SIDE	; select side
	bsr	hw_SEL_DRV	; select drive

	move.w	#$4000,DSKLEN	; stop disk DMA
	move.w	#$8010,DMACON	; enable disk DMA
	move.l	a0,DSKPTH	; start of buffer
	move.w	#$6600,ADKCON	; PRECOMP=0, MFM , no SYNC
	move.w	#$9100,ADKCON	; MFMPREC for MFM, 2°s/bit

	cmp.w	#40,d2		; if it is a high track,
	blt.s	hw_WR_DSK1	; we need a
	move.w	#$A000,ADKCON	; 140 ns precompensation

hw_WR_DSK1:
	move.l	AV.DSKV,a3
	move.w	FV.FLAGS(a3),d1

	btst	#13,d1		; branch if index disabled
	beq	hw_WR_DSK2

;  do disk write via index interrupt

	bsr	hw_WTIDX 	; wait for index
	beq.s	hw_WR_DSK4	; branch on error

;  do disk write

hw_WR_DSK2:
	move.w	#((WLEN>>1)&$3FFF)|$C000,d1

	move.w	d1,DSKLEN	; initiate disk operation
	move.w	d1,DSKLEN

	move.w	#$0002,INTREQ	; clear previous interrupt
				; requests from disk DMA

;  wait for operation to finish, then exit

hw_WR_DSK3:
	bsr	hw_WTDSKDMA	; wait, until disk written
	beq.s	hw_WR_DSK4	; branch on error

	moveq	#ERR.OK,d3

hw_WR_DSK4:
	bsr	hw_DESEL_DRV	; deselect drive

	tst.l	d3

	move.w	(a7)+,sr

	movem.l	(a7)+,d0-d3/a3
	rts

; -------------------------------------------------------------
;  deselect all drives
; -------------------------------------------------------------
hw_DESEL_ALL:
	or.b	#%01111000,CIAB_PRB ; deselect all drives

	rts

; -------------------------------------------------------------
;  Select disk d0.b
; -------------------------------------------------------------
hw_SEL_DRV:
	and.w	#$3,d0		; Only drives 0-3.

	movem.l	d0,-(a7)

	addq.b	#3,d0
	bclr.b	d0,CIAB_PRB	; select drive

	movem.l	(a7)+,d0
	rts

; -------------------------------------------------------------
;  Deselect disk d0.b
; -------------------------------------------------------------
hw_DESEL_DRV:
	and.w	#$3,d0		; Only drives 0-3.

	movem.l	d0,-(a7)

	addq.b	#3,d0
	bset.b	d0,CIAB_PRB	; deselect drive

	movem.l	(a7)+,d0
	rts

; -------------------------------------------------------------
;  switch on motor for drive d0.w
; -------------------------------------------------------------
hw_MTR_ON:
	and.w	#$3,d0		; Only drives 0-3.

	bsr	hw_DESEL_ALL
	bclr	#7,CIAB_PRB	; switch on motor
	bsr	hw_SEL_DRV
	bsr	hw_DESEL_DRV

	rts

; -------------------------------------------------------------
;  switch off motor for drive d0.w
; -------------------------------------------------------------
hw_MTR_OFF:
	and.w	#$3,d0		; Only drives 0-3.

	bsr	hw_DESEL_ALL
	bset	#7,CIAB_PRB	; switch off motor
	bsr	hw_SEL_DRV
	bsr	hw_DESEL_DRV

	rts
; -------------------------------------------------------------
;  select side d1.b for drive d0.w
; -------------------------------------------------------------
hw_SEL_SIDE:
	and.w	#$3,d0		; Only drives 0-3.
	bsr	hw_DESEL_ALL

	btst.b	#0,d1
	beq.s	hw_SEL_SID0

hw_SEL_SID1:
	bclr	#2,CIAB_PRB	; select side 1
	bra.s	hw_SEL_SIDX

hw_SEL_SID0:
	bset	#2,CIAB_PRB	; select side 0

hw_SEL_SIDX:
	bsr	hw_SEL_DRV
	bsr	hw_DESEL_DRV

	rts

; -------------------------------------------------------------
;  Step in one track (increase track number) on drive d0.w
; -------------------------------------------------------------
hw_STEP_IN:
	movem.l	d1,-(a7)

	and.w	#$3,d0		; Only drives 0-3.
	bsr	hw_DESEL_ALL

	bsr	hw_SEL_DRV

	bclr	#1,CIAB_PRB	; reset direction bit

	bset	#0,CIAB_PRB	; set diskstep bit
	bclr	#0,CIAB_PRB	; reset diskstep bit
	bset	#0,CIAB_PRB	; set diskstep bit

	bsr	hw_DESEL_DRV

	moveq	#4,d1		; set count for 5ms

hw_SI_LUP1:
	bsr	hw_BEG_TIMING

hw_SI_LUP2:
	bsr	hw_IS_TIMING
	beq.s	hw_SI_LUP2

	dbra	d1,hw_SI_LUP1

hw_SI_X:
	movem.l	(a7)+,d1
	rts

; -------------------------------------------------------------
;  Step out one track (decrease track number) on drive d0.w
; -------------------------------------------------------------
hw_STEP_OUT:
	movem.l	d1,-(a7)

	and.w	#$3,d0		; Only drives 0-3.
	bsr	hw_DESEL_ALL

	bsr	hw_SEL_DRV

	bset	#1,CIAB_PRB	; set direction bit

	bset	#0,CIAB_PRB	; set diskstep bit
	bclr	#0,CIAB_PRB	; reset diskstep bit
	bset	#0,CIAB_PRB	; set diskstep bit

	bsr	hw_DESEL_DRV

	moveq	#4,d1		; set count for 5ms

hw_SO_LUP1:
	bsr	hw_BEG_TIMING

hw_SO_LUP2:
	bsr	hw_IS_TIMING
	beq.s	hw_SO_LUP2

	dbra	d1,hw_SO_LUP1

hw_SO_X:
	movem.l	(a7)+,d1
	rts

; -------------------------------------------------------------
;  Find track zero on drive d0.w
; -------------------------------------------------------------
hw_GO_TK0:
	movem.l	d1,-(a7)

	moveq	#84,d1		; maximum 84 tracks to step
hw_GT0_LUP:
	bsr	hw_IS_TK0	; already track 0?
	beq.s	hw_GT0_X
	bsr	hw_STEP_OUT	; step a little closer
	dbra	d1,hw_GT0_LUP

hw_GT0_X:
	movem.l	(a7)+,d1
	rts

; -------------------------------------------------------------
;  return whether drive d0.w is at track zero
;  eq = is at track zero, ne = is not at track zero
; -------------------------------------------------------------
hw_IS_TK0:
	movem.l	d0-d1,-(a7)

	and.w	#$3,d0		; Only drives 0-3.
	bsr	hw_DESEL_ALL

	bsr	hw_SEL_DRV
	move.b	CIAA_PRA,d1
	bsr	hw_DESEL_DRV

	andi.b	#16,d1

	movem.l	(a7)+,d0-d1
	rts

; -------------------------------------------------------------
;  wait until drive has reached speed, or for a maximum of
;  500 milliseconds.
;  eq = drive has reached speed, ne = drive is duff
; -------------------------------------------------------------
hw_IS_DRVRDY:
	movem.l	d0-d1,-(a7)

; wait for motor to reach speed

	move.w	#500,d1		; set counter for 500ms

hw_IS_DRVRDY1:
	bsr	hw_BEG_TIMING

hw_IS_DRVRDY2:
;	 bsr	 hw_IS_RDY
;	 beq.s	 hw_IS_DRVRDY3

	bsr	hw_IS_TIMING
	beq.s	hw_IS_DRVRDY2

	dbra	d1,hw_IS_DRVRDY1

;	 bsr	 hw_MTR_OFF

;	 moveq	 #-1,d0
;	 bra.s	 hw_IS_DRVRDYX

hw_IS_DRVRDY3:
	moveq	#0,d0

hw_IS_DRVRDYX:
	movem.l	(a7)+,d0-d1
	rts

; -------------------------------------------------------------
;  return motor ready signal for drive d0.w in zero flag
;  eq = motor ready, ne = motor not ready
; -------------------------------------------------------------
hw_IS_RDY:
	movem.l	d0-d1,-(a7)

	and.w	#$3,d0		; Only drives 0-3.
	bsr	hw_DESEL_ALL

	bsr	hw_SEL_DRV
	move.b	CIAA_PRA,d1
	bsr	hw_DESEL_DRV

	and.b	#32,d1

	movem.l	(a7)+,d0-d1

	rts

; -------------------------------------------------------------
;  return status of writeprotect signal for drive d0.w
;  eq = protected, ne = not protected
; -------------------------------------------------------------
hw_IS_PRO:
	movem.l	d0-d1,-(a7)

	and.w	#$3,d0		; Only drives 0-3.
	bsr	hw_DESEL_ALL

	bsr	hw_SEL_DRV
	move.b	CIAA_PRA,d1
	bsr	hw_DESEL_DRV

	and.b	#8,d1

	movem.l	(a7)+,d0-d1
	rts

; -------------------------------------------------------------
;  return status of diskchange signal for drive d0.w
;  eq = changed, ne = not changed
; -------------------------------------------------------------
hw_IS_CHG:
	movem.l	d0-d1,-(a7)

	and.w	#$3,d0		; Only drives 0-3.
	bsr	hw_DESEL_ALL

	bsr	hw_SEL_DRV
	move.b	CIAA_PRA,d1
	bsr	hw_DESEL_DRV

	andi.b	#4,d1
	bne.s	hw_IS_CHGX

	bsr	hw_STEP_IN	; clear change signal
	bsr	hw_STEP_OUT

	tst.b	d1

hw_IS_CHGX:
	movem.l	(a7)+,d0-d1
	rts

; -------------------------------------------------------------
;  Return drive type for drive d0.w in d0.l
; -------------------------------------------------------------
hw_DRV_TYP:
	movem.l	d1-d3/a0,-(a7)

	addq.b	#3,d0
	moveq	#1,d3
	asl.b	d0,d3

L0F8F616:
	not.b	 d3
	lea	 CIAB_PRB,a0
	move.b	 #$7F,d0
	move.b	 d0,(a0)
	and.b	 d3,d0
	move.b	 d0,(a0)
	move.b	 #$FF,(a0)
	move.b	 d3,(a0)
	move.b	 #$FF,(a0)
	moveq	 #$1F,d1
	moveq	 #$0,d0
L0F8F636:
	lsl.l	 #1,d0
	move.b	 d3,(a0)
	btst	 #$5,CIAA_PRA
	bne.s	 L0F8F648	;*/modify beq.s L0F8F648
	bset	 #$0,d0
L0F8F648:
	move.b	 #$FF,(a0)
	dbra	 d1,L0F8F636
L0F8F650:

	movem.l	(a7)+,d1-d3/a0
	rts

; -------------------------------------------------------------
;  BASIC functions and procedures
; -------------------------------------------------------------
PROC_DEF:
	ifnd	extras
	dc.w	4
	endc

	ifd	extras
	ifnd	extras2
	dc.w	4
	endc
	ifd	extras2
	dc.w	22
	endc
	endc

	dc.w	B_DISKCOPY-*
	dc.b	8,'DISKCOPY',0
	dc.w	B_MOUNT-*
	dc.b	5,'MOUNT'
	dc.w	B_DSKCNG-*
	dc.b	6,'DskCng',0

	ifd	extras
	dc.w	B_hw_SEL_SIDE-*
	dc.b	11,'hw_SEL_SIDE'
	dc.w	B_hw_STEP_OUT-*
	dc.b	11,'hw_STEP_OUT'
	dc.w	B_hw_STEP_IN-*
	dc.b	10,'hw_STEP_IN',0
	dc.w	B_hw_GO_TK0-*
	dc.b	9,'hw_GO_TK0'
	dc.w	B_hw_MTR_OFF-*
	dc.b	10,'hw_MTR_OFF',0
	dc.w	B_hw_MTR_ON-*
	dc.b	9,'hw_MTR_ON'
	dc.w	B_hw_WR_DSK-*
	dc.b	9,'hw_WR_DSK'
	dc.w	B_hw_RD_DSK-*
	dc.b	9,'hw_RD_DSK'
	dc.w	B_hw_TIMER-*
	dc.b	8,'hw_TIMER'

	ifd	extras2
	dc.w	B_W_INDEX-*
	dc.b	7,'W_INDEX'
	dc.w	B_R_INDEX-*
	dc.b	7,'R_INDEX'
	dc.w	B_FLUSH_BUFF-*
	dc.b	10,'Flush_BUFF',0
	dc.w	B_FREE_BUFF-*
	dc.b	9,'Free_BUFF'
	dc.w	B_FTRACK-*
	dc.b	6,'FTrack',0
	dc.w	B_WQDISK-*
	dc.b	6,'WQDisk',0
	dc.w	B_RQDISK-*
	dc.b	6,'RQDisk',0
	dc.w	B_WQSEC-*
	dc.b	5,'WQSec'
	dc.w	B_RQSEC-*
	dc.b	5,'RQSec'
	dc.w	B_SEL_DRV-*
	dc.b	7,'SEL_DRV'
	dc.w	B_SEL_SIDE-*
	dc.b	8,'SEL_SIDE',0
	dc.w	B_GO_TRACK-*
	dc.b	8,'GO_TRACK',0
	endc
	endc

	dc.w	0

	ifnd	extras
	dc.w	4
	endc

	ifd	extras
	ifnd	extras2
	dc.w	8
	endc
	ifd	extras2
	dc.w	14
	endc
	endc

	dc.w	B_hw_DRV_TYP-*
	dc.b	10,'hw_DRV_TYP',0

	ifd	extras
	dc.w	B_hw_IS_CHG-*
	dc.b	9,'hw_IS_CHG'
	dc.w	B_hw_IS_PRO-*
	dc.b	9,'hw_IS_PRO'
	dc.w	B_hw_IS_RDY-*
	dc.b	9,'hw_IS_RDY'
	dc.w	B_hw_IS_TK0-*
	dc.b	9,'hw_IS_TK0'

	ifd	extras2
	dc.w	B_CRC-*
	dc.b	3,'CRC'
	dc.w	B_ASCMFM-*
	dc.b	6,'ASCMFM',0
	dc.w	B_MFMASC-*
	dc.b	6,'MFMASC',0
	dc.w	B_CHNG-*
	dc.b	4,'Chng',0
	dc.w	B_WPRO-*
	dc.b	4,'WPro',0
	dc.w	B_CKRDY-*
	dc.b	5,'CkRdy'
	endc
	endc

	dc.w	0

; -------------------------------------------------------------
;    BASIC adaptions to M-Code procs
; -------------------------------------------------------------
B_MOUNT:
	bsr	FETCH_W		; get disk number
	bne	B_MOUNTX
	move.l	d1,d0

	cmp.l	a3,a5
	bne	RPRT_BP

	bsr	MOUNT

B_MOUNTX:
	rts

; -------------------------------------------------------------
B_DSKCNG:
	bsr	FETCH_W		; get drive number
	bne	B_DSKCNGX
	move.l	d1,d0

	cmp.l	a3,a5
	bne	RPRT_BP

	bsr	DSKCNG

	moveq	#0,d0		; no errors

B_DSKCNGX:
	rts

	ifd	extras2
; -------------------------------------------------------------
B_W_INDEX
	bsr	FETCH_W		; get flag word
	bne	B_W_INDEXX

	cmp.l	a3,a5
	bne	RPRT_BP

	movem.l	a3,-(a7)
	move.l	AV.DSKV,a3

	tst.l	d1
	bne.s	B_W_IDX1

	move.w	FV.FLAGS(a3),d0
	bclr	#13,d0

	bra.s	B_W_IDX2

B_W_IDX1:
	move.w	FV.FLAGS(a3),d0
	bset	#13,d0

B_W_IDX2:
	move.w	d0,FV.FLAGS(a3)

	movem.l	(a7)+,a3
	moveq	#0,d0

B_W_INDEXX:
	rts

; -------------------------------------------------------------
B_R_INDEX:
	bsr	FETCH_W		; get flag word
	bne	B_R_INDEXX

	cmp.l	a3,a5
	bne	RPRT_BP

	movem.l	a3,-(a7)
	move.l	AV.DSKV,a3

	tst.l	d1
	bne.s	B_R_IDX1

	move.w	FV.FLAGS(a3),d0
	bclr	#12,d0

	bra.s	B_R_IDX2

B_R_IDX1:
	move.w	FV.FLAGS(a3),d0
	bset	#12,d0

B_R_IDX2:
	move.w	d0,FV.FLAGS(a3)

	movem.l	(a7)+,a3
	moveq	#0,d0

B_R_INDEXX:
	rts

; -------------------------------------------------------------
B_CRC:
	bsr	FETCH_L		; get address of data
	bne	B_FLUSH_BUFFX
	move.l	d1,d3

	bsr	FETCH_L		; get length of data
	bne	B_FLUSH_BUFFX
	move.l	d1,d2

	bsr	FETCH_L		; get previous CRC
	bne	B_FLUSH_BUFFX

	cmp.l	a3,a5
	bne	RPRT_BP

	move.l	d1,d0
	move.l	d2,d1
	move.l	d3,a0
	bsr	CALCCRC
	bra	RET_W

; -------------------------------------------------------------
B_ASCMFM:
	bsr	FETCH_W		; get ASCII byte
	bne	B_FLUSH_BUFFX
	move.l	d1,d0

	cmp.l	a3,a5
	bne	RPRT_BP

	bsr	ASCMFMD0
	bra	RET_W

; -------------------------------------------------------------
B_MFMASC:
	bsr	FETCH_W		; get MFM word
	bne	B_FLUSH_BUFFX
	move.l	d1,d0

	cmp.l	a3,a5
	bne	RPRT_BP

	bsr	MFMASCD0
	bra	RET_W

; -------------------------------------------------------------
B_FLUSH_BUFF:
	bsr	FETCH_W		; get disk number
	bne	B_FLUSH_BUFFX
	move.l	d1,d0

	cmp.l	a3,a5
	bne	RPRT_BP

	bsr	FLUSH_BUFF

B_FLUSH_BUFFX:
	rts

; -------------------------------------------------------------
B_FREE_BUFF:
	bsr	FETCH_W		; get disk number
	bne	B_FREE_BUFFX
	move.l	d1,d0

	cmp.l	a3,a5
	bne	RPRT_BP

	bsr	FREE_BUFF

B_FREE_BUFFX:
	rts

; -------------------------------------------------------------
B_FTRACK:
	bsr	FETCH_W		; get disk number
	bne	B_FTRACKX
	move.l	d1,d4

	bsr	FETCH_W		; get side
	bne	B_FTRACKX
	move.l	d1,d3

	bsr	FETCH_W		; get track number
	bne	B_FTRACKX
	move.l	d1,d2

	cmp.l	a3,a5
	bne	RPRT_BP

	move.l	d4,d0
	bsr	SEL_DRV

	move.l	d3,d0
	bsr	SEL_SIDE

	move.l	d2,d0
	bsr	GO_TRACK

	bsr	FTRACK

B_FTRACKX:
	rts

; -------------------------------------------------------------
B_WQDISK:
	bsr	FETCH_W		; get disk number
	bne	B_WQDISKX
	move.l	d1,d4

	bsr	FETCH_W		; get side
	bne	B_WQDISKX
	move.l	d1,d3

	bsr	FETCH_W		; get track number
	bne	B_WQDISKX
	move.l	d1,d2

	cmp.l	a3,a5
	bne	RPRT_BP

	move.l	d4,d0
	bsr	SEL_DRV

	move.l	d3,d0
	bsr	SEL_SIDE

	move.l	d2,d0
	bsr	GO_TRACK

	bsr	WQDISK

B_WQDISKX:
	rts

; -------------------------------------------------------------
B_RQDISK:
	bsr	FETCH_W		; get disk number
	bne	B_RQDISKX
	move.l	d1,d4

	bsr	FETCH_W		; get side
	bne	B_RQDISKX
	move.l	d1,d3

	bsr	FETCH_W		; get track number
	bne	B_RQDISKX
	move.l	d1,d2

	cmp.l	a3,a5
	bne	RPRT_BP

	move.l	d4,d0
	bsr	SEL_DRV

	move.l	d3,d0
	bsr	SEL_SIDE

	move.l	d2,d0
	bsr	GO_TRACK

	bsr	RQDISK

B_RQDISKX:
	rts

; -------------------------------------------------------------
B_WQSEC:
	bsr	FETCH_L		; output buffer address
	bne	B_WQSECX
	move.l	d1,d5

	bsr	FETCH_W		; get disk number
	bne	B_WQSECX
	move.l	d1,d4

	bsr	FETCH_W		; get side
	bne	B_WQSECX
	move.l	d1,d3

	bsr	FETCH_W		; get track number
	bne	B_WQSECX
	move.l	d1,d2

	bsr	FETCH_W		; get sector number
	bne	B_WQSECX

	cmp.l	a3,a5
	bne	RPRT_BP

	move.l	d4,d0
	bsr	SEL_DRV

	move.l	d3,d0
	bsr	SEL_SIDE

	move.l	d2,d0
	bsr	GO_TRACK
				; sector number in d1
	move.l	d5,a1		; output buffer address
	bsr	WQSEC

B_WQSECX:
	rts

; -------------------------------------------------------------
B_RQSEC:
	bsr	FETCH_L		; output buffer address
	bne	B_RQSECX

	move.l	d1,d5

	bsr	FETCH_W		; get disk number
	bne	B_RQSECX
	move.l	d1,d4

	bsr	FETCH_W		; get side
	bne	B_RQSECX
	move.l	d1,d3

	bsr	FETCH_W		; get track number
	bne	B_RQSECX
	move.l	d1,d2

	bsr	FETCH_W		; get sector number
	bne	B_RQSECX

	cmp.l	a3,a5
	bne	RPRT_BP

	move.l	d4,d0
	bsr	SEL_DRV

	move.l	d3,d0
	bsr	SEL_SIDE

	move.l	d2,d0
	bsr	GO_TRACK
				; sector number in d1
	move.l	d5,a1		; output buffer address
	moveq	#0,d3		; no bytes to skip
	moveq	#0,d4		; no bytes to leave
	bsr	RQSEC

B_RQSECX:
	rts

; -------------------------------------------------------------
B_SEL_DRV:
	bsr	FETCH_W		; get drive number
	bne	B_SEL_DRVX
	move.l	d1,d0

	cmp.l	a3,a5
	bne	RPRT_BP

	bsr	SEL_DRV

B_SEL_DRVX:
	rts

; -------------------------------------------------------------
B_SEL_SIDE:
	bsr	FETCH_W		; get side number
	bne	B_SEL_SIDEX
	move.l	d1,d0

	cmp.l	a3,a5
	bne	RPRT_BP

	bsr	SEL_SIDE

B_SEL_SIDEX:
	rts

; -------------------------------------------------------------
B_GO_TRACK:
	bsr	FETCH_W		; get track number
	bne	B_GO_TRACKX
	move.l	d1,d0

	cmp.l	a3,a5
	bne	RPRT_BP

	bsr	GO_TRACK

B_GO_TRACKX:
	rts

; -------------------------------------------------------------
B_CHNG:
	bsr	CHNG		; test for Disk change
	bra	RET_W

; -------------------------------------------------------------
B_WPRO:
	bsr	WPRO		; test for write protect
	bra	RET_W

; -------------------------------------------------------------
B_CKRDY:
	bsr	CKRDY		; check readiness of drive
	bra	RET_W

	endc

; -------------------------------------------------------------
;    BASIC routines that hit hit the hardware directly
; -------------------------------------------------------------
B_DISKCOPY:
	moveq	#0,d1		; default channel #
	bsr	GET_CH		; get channel ID

	cmp.l	a3,a5
	bne	RPRT_BP

	move.l	AV.DSKV,a3
	cmp.w	#1,FV.MAXDRive(a3)
	bge.s	B_DC2

	lea	DRVMSG(pc),a1
	bsr	IOSTRG
	bra.s	B_DCOK

B_DC2:
	lea	CPYMSG(pc),a1
	bsr	IOSTRG

B_DCLUP:
	moveq	#IO.FBYTE,d0
	moveq	#-1,d3		; infinite timeout
	trap	#3		; fetch a byte

	tst.l	d0
	bne.s	B_DCX		; exit on error

	cmp.b	#$0A,d1
	bne.s	B_DCLUP

	bsr	DISKCOPY 	; copy disk
	bne.s	B_DCX

	lea	DONMSG(pc),a1
	bsr	IOSTRG

B_DCOK:
	moveq	#0,d0

B_DCX:
	rts

; -------------------------------------------------------------
B_hw_DRV_TYP:
	bsr	FETCH_W		; get drive number
	bne	B_hw_DRV_TYPX
	move.l	d1,d0

	cmp.l	a3,a5
	bne	RPRT_BP

	bsr	hw_DRV_TYP	; test drive type
	bra	RET_L

B_hw_DRV_TYPX:

	ifd	extras

; -------------------------------------------------------------
B_hw_IS_CHG:
	bsr	FETCH_W		; get drive number
	bne	B_hw_SEL_SIDX
	move.l	d1,d0

	cmp.l	a3,a5
	bne	RPRT_BP

	bsr	hw_IS_CHG	; test for Disk change
	beq	RET_TRU
	bra	RET_FLS

; -------------------------------------------------------------
B_hw_IS_PRO:
	bsr	FETCH_W		; get drive number
	bne	B_hw_SEL_SIDX
	move.l	d1,d0

	cmp.l	a3,a5
	bne	RPRT_BP

	bsr	hw_IS_PRO	; test for write protect
	beq	RET_TRU
	bra	RET_FLS

; -------------------------------------------------------------
B_hw_IS_RDY:
	bsr	FETCH_W		; get drive number
	bne	B_hw_SEL_SIDX
	move.l	d1,d0

	cmp.l	a3,a5
	bne	RPRT_BP

	bsr	hw_IS_RDY	; check readiness of drive
	beq	RET_TRU
	bra	RET_FLS

; -------------------------------------------------------------
B_hw_IS_TK0:
	bsr	FETCH_W		; get drive number
	bne	B_hw_SEL_SIDX
	move.l	d1,d0

	cmp.l	a3,a5
	bne	RPRT_BP

	bsr	hw_IS_TK0	; check if at track zero
	beq	RET_TRU
	bra	RET_FLS

; -------------------------------------------------------------
B_hw_SEL_SIDE:
	bsr	FETCH_W		; get drive number
	bne.s	B_hw_SEL_SIDX
	move.l	d1,d2

	bsr	FETCH_W		; get drive side
	bne.s	B_hw_SEL_SIDX

	move.l	d2,d0		; drive in d0, side in d1

	cmp.l	a3,a5
	bne	RPRT_BP

	bsr	hw_SEL_SIDE
	moveq	#0,d0

B_hw_SEL_SIDX:
	rts

; -------------------------------------------------------------
B_hw_STEP_OUT:
	bsr	FETCH_W		; get drive number
	bne.s	B_hw_STEP_OX
	move.l	d1,d0

	bsr	hw_STEP_OUT	; increase track number
	moveq	#0,d0

B_hw_STEP_OX:
	rts

; -------------------------------------------------------------
B_hw_STEP_IN:
	bsr	FETCH_W		; get drive number
	bne.s	B_hw_STEP_IX
	move.l	d1,d0

	cmp.l	a3,a5
	bne	RPRT_BP

	bsr	hw_STEP_IN	; decrease track number
	moveq	#0,d0

B_hw_STEP_IX:
	rts

; -------------------------------------------------------------
B_hw_GO_TK0:
	bsr	hw_GO_TK0
	moveq	#0,d0
	rts

; -------------------------------------------------------------
B_hw_MTR_OFF:
	bsr	FETCH_W		; get drive number
	bne.s	B_hw_MTR_OFX
	move.l	d1,d0

	cmp.l	a3,a5
	bne	RPRT_BP

	bsr	hw_MTR_OFF	; turn of drive motor
	moveq	#0,d0

B_hw_MTR_OFX:
	rts

; -------------------------------------------------------------
B_hw_MTR_ON:
	bsr	FETCH_W		; get drive number
	bne.s	B_hw_MTR_ONX
	move.l	d1,d0

	cmp.l	a3,a5
	bne.s	RPRT_BP

	bsr	hw_MTR_ON	; turn on drive motor
	moveq	#0,d0

B_hw_MTR_ONX:
	rts

; -------------------------------------------------------------
B_hw_WR_DSK:
	bsr	FETCH_L		; address of buffer in a0
	bne.s	B_hw_WR_DSKX
	move.l	d1,a0

	bsr	FETCH_W		; get drive number
	bne.s	B_hw_RD_DSKX
	move.l	d1,d3

	bsr	FETCH_W		; get track number
	bne.s	B_hw_RD_DSKX
	move.l	d1,d2

	bsr	FETCH_W		; get side number
	bne.s	B_hw_RD_DSKX

	move.l	d3,d0		; drive in d0, side d1, track in d2

	cmp.l	a3,a5
	bne.s	RPRT_BP

	bsr	hw_WR_DSK	; write buffer to drive
	moveq	#0,d0

B_hw_WR_DSKX:
	rts

; -------------------------------------------------------------
B_hw_RD_DSK:
	bsr	FETCH_L		; address of buffer in a0
	bne.s	B_hw_RD_DSKX
	move.l	d1,a0

	bsr	FETCH_W		; get drive number
	bne.s	B_hw_RD_DSKX
	move.l	d1,d3

	bsr	FETCH_W		; get track number
	bne.s	B_hw_RD_DSKX
	move.l	d1,d2

	bsr	FETCH_W		; get side number
	bne.s	B_hw_RD_DSKX

	move.l	d3,d0		; drive in d0, side d1, track in d2

	cmp.l	a3,a5
	bne.s	RPRT_BP

	bsr	hw_RD_DSK	; read to buffer from drive
	moveq	#0,d0

B_hw_RD_DSKX:
	rts

; -------------------------------------------------------------
B_hw_TIMER:

	bsr	FETCH_L		; get count in ms
	bne.s	B_TIMERX
	move.l	d1,d0

	cmp.l	a3,a5
	bne.s	RPRT_BP

	bsr	hw_TIMER
	moveq	#0,d0

B_TIMERX:
	rts

	endc

; -------------------------------------------------------------
RPRT_BP:
	moveq	#ERR.BP,d0
	rts

; -------------------------------------------------------------
CPYMSG:
	dc.w	57
	dc.b	"Put SRC in flp1_ and DEST in flp2_"
	dc.b	" then press <ENTER>... ",0

DONMSG:
	dc.w	6
	dc.b	"done.",$0A

DRVMSG:
	dc.w	37
	dc.b	"Sorry, you need at least two drives.",$0A,0

; -------------------------------------------------------------
;    print string at (a1) to channel with id a0

IOSTRG:
	movem.l	d1-d3/a1-a2,-(a7)

	move.w	UT.MTEXT,a2
	jsr	(a2)

	movem.l	(a7)+,d1-d3/a1-a2
	rts

	ifd	debug

; -------------------------------------------------------------
;    print byte d0.l as HEX to channel with id a0

HEX20:
	swap	d0
	bsr	HEX10
	swap	d0
	bsr	HEX10
	rts

; -------------------------------------------------------------
;    print byte d0.w as HEX to channel with id a0

HEX10:
	ror.w	#8,d0
	bsr	HEX08
	rol.w	#8,d0
	bsr	HEX08
	rts

; -------------------------------------------------------------
;    print byte d0.b as HEX to channel with id a0

HEX08:
	movem.l	d0-d1,-(a7)

	moveq	#2,d1

	lsl.l	#8,d1
	move.b	d0,d1
	lsr.b	#4,d1
	and.b	#$F,d1
	add.b	#'0',d1
	cmp.b	#'9',d1
	ble.s	HEX081
	add.b	#7,d1

HEX081:
	lsl.l	#8,d1
	move.b	d0,d1
	and.b	#$F,d1
	add.b	#'0',d1
	cmp.b	#'9',d1
	ble.s	HEX082
	add.b	#7,d1

HEX082:
	move.l	d1,d0
	bsr	IOD0

	movem.l	(a7)+,d0-d1
	rts

IOD0:
	movem.l	a1,-(a7)
	move.l	d0,-(a7)
	move.l	a7,a1		; address of string
	bsr	IOSTRG
	move.l	(a7)+,d0
	movem.l	(a7)+,a1
	rts

	endc

; -------------------------------------------------------------
; Entry: A3.L   pointer to first parameter
;	A5.L   pointer to last parameter
;
; Exit:	A3.L   updated
;	A5.L   updated
;	D0.L...error code
;	D1.W   result

FETCH_W:
	MOVEM.L	A1-A2,-(A7)

	MOVE.W	CA.GTINT,A2
	BSR.S	GET_ONE
	BNE.S	FETCH_WX

	MOVEQ	#0,D1
	MOVE.W	0(A6,A1.L),D1
	ADDQ.L	#2,A1
	MOVE.L	A1,BV_RIP(A6)

FETCH_WX:
	MOVEM.L	(A7)+,A1-A2
	TST.L	D0
	RTS

; --------------------------------------------------------------
FETCH_L:
	MOVEM.L	A1-A2,-(A7)

	MOVE.W	CA.GTLIN,A2
	BSR.S	GET_ONE
	BNE.S	FETCH_LX

	MOVE.L	0(A6,A1.L),D1
	ADDQ.L	#4,A1
	MOVE.L	A1,BV_RIP(A6)

FETCH_LX:
	MOVEM.L	(A7)+,A1-A2
	TST.L	D0
	RTS

; --------------------------------------------------------------
;  This routine gets one parameter and returns it on the maths
;  stack, pointed to by (A1).
;
; Entry: A2.L   routine to call (i.e. CA.GTINT)
;	A3.L   pointer to first parameter
;	A5.L   pointer to last parameter
;
; Exit:	A3.L   updated
;	A5.L   updated
;	A1.L   updated pointer to top of maths stack
;	D0.L   error code

GET_ONE:
	MOVEM.L	D1-D6/A0/A2,-(A7)

	LEA	8(A3),A0
	CMP.L	A0,A5
	BLT.S	GET_ONEBp

	MOVE.L	BV_RIP(A6),A1
	MOVE.L	A5,-(A7)
	MOVE.L	A0,A5
	MOVE.L	A5,-(A7)
	JSR	(A2)
	MOVEM.L	(A7)+,A0/A5

	TST.L	D0
	BNE.S	GET_ONEX

	MOVE.L	A0,A3
	MOVE.L	A1,BV_RIP(A6)

	BRA.S	GET_ONEX

GET_ONEBp:
	MOVEQ	#ERR.BP,D0

GET_ONEX:
	MOVEM.L	(A7)+,D1-D6/A0/A2
	TST.L	D0
	RTS

; --------------------------------------------------------------
;  get channel parameter

; Entry: A3.L   pointer to first parameter
;	A5.L   pointer to last parameter

; Exit:	A0.L   CH.ID (default d1)
;	A2.L   CH.BASE
;	A3.L   updated
;	A5.L   updated
;	D0.L   error code
;	D1.L   default channel #

GET_CH:
	MOVEM.L	D1/D3/A1,-(A7)

	MOVE.L	BV_RIP(A6),A1
	CMP.L	A3,A5
	BEQ.S	GET_CH1

	BTST	#7,1(A6,A3.L)
	BEQ.S	GET_CH1

	BSR	FETCH_W
	BNE.S	GET_CHX

GET_CH1:
	MULU	#$28,D1
	ADD.L	BV_CHBAS(A6),D1
	CMP.L	BV_CHP(A6),D1
	BGE.S	GET_CHNO

	MOVE.L	D1,A2
	MOVE.L	0(A6,A2.L),A0
	MOVE.W	A0,D1
	BMI.S	GET_CHNO

	MOVEQ	#0,D0
	BRA.S	GET_CHX

GET_CHNO:
	MOVEQ.L	#ERR.NO,D0

GET_CHX:
	MOVEM.L	(A7)+,D1/D3/A1
	RTS

; -------------------------------------------------------------
;  return true or false back to BASIC

RET_FLS:
	moveq	#0,d0
	bra.s	RET_W

RET_TRU:
	moveq	#1,d0

; --------------------------------------------------------------
;  return word value to BASIC

RET_W:
	move.l	d0,d4
	moveq.l	#2,d1
	move.w	BV.CHRIX,a2
	jsr	(a2)
	move.l	d4,d0

	move.l	BV_RIP(a6),a1	; Get arith stack pointer
	subq	#2,a1		; room for 2 bytes
	move.l	a1,BV_RIP(a6)
	move.w	d0,0(a6,a1.l)	; Put int number on stack
	moveq.l	#3,d4		; set Integer type
	moveq.l	#0,d0		; no errors
	rts

; -------------------------------------------------------------
;    return long Integer to BASIC

RET_L:
	move.l	d0,d4
	moveq.l	#6,d1
	move.w	BV.CHRIX,a2
	jsr	(a2)
	move.l	d4,d1

	BSR	CONV_L2F
	SUBQ.L	#6,BV_RIP(A6)
	MOVE.L	BV_RIP(A6),A1
	MOVE.W	D0,0(A6,A1.L)
	MOVE.L	D1,2(A6,A1.L)
	MOVEQ.L	#2,D4
	MOVEQ.L	#0,D0
	RTS

; -------------------------------------------------------------
;  convert long Integer to floating point form.
;  Entry: d1.l = long int
;  Exit:  d0.w = exponent
;	 d1.l = mantissa

CONV_L2F:
	MOVE.L	D1,D0
	BEQ.S	CONV_L2FX

	MOVE.W	#$81F,D0
	MOVE.L	D1,-(A7)

CONV_L2F1:
	ADD.L	D1,D1
	BVS.S	CONV_L2F2

	SUBQ.W	#1,D0
	MOVE.L	D1,(A7)
	BRA	CONV_L2F1

CONV_L2F2:
	MOVE.L	(A7)+,D1

CONV_L2FX:
	RTS

; --------------------------------------------------------------
*/endfile
