*****************************************************************************
*
*  FASTBACK - Fast disk to disk copy for CP/68
*
*	This program is for a SWTPC 6800 with a DC-x floppy controller
*	running CP/68. It performs a very fast disk to disk copy and
*	also offers the option to format the target disk. The CP/68 disk
*	format is 35 tracks, 18 sectors per track, 128 bytes per sector.
*
*	Written by Mike Douglas
*
*	Rev	 Date	    Desc
*	1.0	09/10/22    Original
*
*****************************************************************************

* SWTBUG equates

OUTEEE	equ	$E1D1		;SWTBUG display character
INEEE	equ	$E1AC		;SWTBUG input character
PDATA1	equ	$E07E		;SWTBUG display message
GETCHR	equ	INEEE
PUTCHR	equ	OUTEEE
PSTRNG	equ	PDATA1

* Disk equates

NUMTRK	equ	35		;number of tracks
NUMSEC	equ	18		;number of sectors per track
SECLEN	equ	128		;sector length
MINDRV	equ	0		;first drive number
MAXDRV	equ	3		;max drive number 
TRKLEN	equ	NUMSEC*SECLEN	;length of track in bytes

WRTRIES	equ	4
RDTRIES	equ	4		;read retries

* Misc equates

MAXRAM	equ	$8000		;stop testing for RAM at $8000
CR	equ	$0D		;ASCII for carriage return
LF	equ	$0A		;ASCII for line feed
EOT	equ	$04		;end of string for FLEX

* Layout of raw sector in format buffer

	org	0
idMark	rmb	1		;address mark
idTrk	rmb	1		;LSB of track number
	rmb	1
idSec	rmb	1		;sector number
idSize	rmb	1		;sector length code
idCrc	rmb	1		;CRC mark
	rmb	17		;gap bytes
dataMrk	rmb	1		;data mark
	rmb	128		;sector data
dataCrc	rmb	1		;CRC on sector data
	rmb	12		;gap bytes

RSECLEN	equ	*		;raw sector length
GAP1	equ	19		;post index gap length
FMTLEN	equ	RSECLEN*NUMSEC+$100	;track space for format

	org	$2000		;load address
*-----------------------------------------------------------------------------
* Program entry
*-----------------------------------------------------------------------------
entry	bra	start
	fcb	10		;current version number

* Track length and number of tracks are fixed for CP/68

start	ldx	#TRKLEN		;init track length 
	stx	trkLen
	ldaa	#NUMTRK		;init number of tracks
	staa	numTrk
	jsr	sizeRam		;see how much RAM we can use

	ldx	#mConfm		;confirm OK to proceed
	jsr	PSTRNG
	jsr	GETCHR
	oraa	#$20		;upper case letters to lower case
	cmpa	#'y'		;yes response?
	beq	chkFmt		;yes, see if they want to format
	swi	WARMST
	 fcb	31

chkFmt	ldx	#mFormat	;see if user wants to format target
	jsr	PSTRNG
	jsr	GETCHR
	oraa	#$20		;upper case letters to lower case
	cmpa	#'y'		;format?
	bne	init		;no
	jsr	format		;else, format disk
	
init	clr	srcTrk
	clr	destTrk
	clr	fTunnel

	ldx	#mCopy		;display "Copying..."
	jsr	PSTRNG
	
*-----------------------------------------------------------------------------
*  main - Loop through all tracks on the disk reading as many tracks
*    as will fit in RAM before writing to target disk, then repeat.
*-----------------------------------------------------------------------------
main	ldx	#trkBuf		;initialize track pointer
	stx	trkPtr

	clra			;A=source drive number
	ldab	srcTrk		;set B to track drive is on
	jsr	dSelect		;select the drive
	
* Read and buffer tracks until RAM is full or all tracks have been read.

fillBuf	bsr	readTrk		;read a track into trkBuf
	ldaa	#'.'		;display pacifier
	jsr	PUTCHR

	inc	srcTrk		;increment track number
	ldaa	srcTrk		;done all tracks on the disk?
	cmpa	numTrk		
	beq	wrtDisk		;yes, go write the buffered tracks

	ldx	trkPtr		;RAM buffer full?
	cpx	bufEnd
	bne	fillBuf		;no, keep reading

* Write buffered tracks

wrtDisk	ldaa	TRKREG		;save the track source disk is on
	staa	srcTrk
	
	ldx	#trkBuf		;initialize track pointer
	stx	trkPtr
	
	ldaa	#1		;A=destination drive number
	ldab	destTrk		;set B to track drive is on
	jsr	dSelect
		
wrtBuf	jsr	wrtTrk		;write a track 
	ldaa	#'*'		;display pacifier
	jsr	PUTCHR

	inc	destTrk		;increment track number
	ldaa	destTrk
	cmpa	numTrk		;done all tracks on the disk?
	beq	allDone		;yes, we're all done

	ldx	trkPtr		;reached end of buffered tracks?
	cpx	bufEnd
	bne	wrtBuf		;no, keep writing

	ldaa	TRKREG		;save the track dest disk is on
	staa	destTrk
	bra	main		;go back and read source

allDone	ldx	#mDone		;"done" message
	
pgmExit	jsr	PSTRNG

	ldaa	#1		;restore both drives to track 0
	jsr	dSelect
	jsr	dRestor

	clra			;restore both drives to track 0
	jsr	dSelect
	jsr	dRestor

	swi	WARMST
	 fcb	31
	
*-----------------------------------------------------------------------------
* readTrk - read NUMSEC sectors from the current track into a trkBuf as
*   pointed to by trkPtr. After the track is read, trkPtr is updated
*   by the length of a track to point to the next track buffer.
*-----------------------------------------------------------------------------
readTrk	ldx	#rdSkew		;use 1:1 interleave table for reading
	stx	skewTbl

	ldaa	srcTrk		;seek to srcTrk
	jsr	dSeek
	bne	seekErr
	
	jsr	dReadAm		;get sector head is over
	bne	secErr		;fail
	jsr	getIdx		;get index of sector under head

	ldaa	#2		;init retry step
	staa	rtStep
	ldaa	#NUMSEC		;set number of sectors to read
	staa	secCnt

rtLoop	jsr	incSec		;move to next sector index
	jsr	dSecAdr		;get sector number and buffer address

rtRead	jsr	dRead		;read the sector
	bne	secRtry		;read error, run retry logic
	
rtNext	dec	secCnt		;decrement sector count
	bne	rtLoop		;loop until all sectors read

	ldaa	trkPtr+1	;increment tkrPtr by the track length
	adda	trkLen+1
	staa	trkPtr+1
	
	ldaa	trkPtr		;MSB
	adca	trkLen
	staa	trkPtr

	rts

*-----------------------------------------------------------------------------
* Seek error. This is either an actual seek error or the disk is
*    write protected. 
*-----------------------------------------------------------------------------
seekErr	ldx	#mWrtPrt	;write protect error message
	bita	#sWRPROT	;write protect?
	bne	pgmExit		;yes
	
	ldx	#mSkErr		;else, seek error message
	bra	pgmExit

readErr	ldx	#mRdErr
	bra	pgmExit
	
secErr	ldx	#mNoSec
	jmp	pgmExit

*--------------------------------------------------------------------------
* secRtry - Read error retry logic. Three retry steps are tried in
*   the order shown below:
*
*   Step    Action
*    2	Re-read RDTRIES times
*    1	Seek out one track and back, then re-read RDTRIES times
*    0	Seek in one track and back, then re-read RDTRIES times
*
*    Upon entry, if the error is "sector not found," then retries are
*    not performed since the controller has already tried to read the
*    address mark across three revolutions.
*--------------------------------------------------------------------------	
secRtry	bita	#sSECERR	;sector not found error?
	bne	srNext		;yes, skip retries, go to next step

	ldaa	#RDTRIES	;init retry counter
	staa	rdRetry

srLoop	jsr	dRead		;try the read again
	beq	srDone		;success, retry is done

	dec	rdRetry		;decrement retry counter
	bne	srLoop		;try again

* srNext - move to next retry step

srNext	dec	rtStep		;decrement to next retry step
	beq	stpPast		;step 0, step in one track and come back
	bpl	stpBack		;step 1, step out one track and come back
	bra	readErr		;else, give up

srDone	ldaa	#2		;restart the retry step counter
	staa	rtStep
	bra	rtNext		;re-enter the sector loop

* stpBack - step out then back in, retry read

stpBack	ldaa	srcTrk		;A=current track
	beq	stpPast		;already on track zero, step in instead
	deca			;else, step out one track
	bra	doBump		;go bump the head

* stpPast - step in then back out, retry read

stpPast	ldaa	srcTrk		;A=current rack
	inca			;step in one track
	cmpa	numTrk		;already at max track?	
	bcc	stpBack		;yes, seek back instead

doBump	jsr	dSeek		;see to new track
	ldaa	srcTrk		;restore track number
	jsr	dSeek		;back to original track
	jmp	rtRead		;try reads again

*-----------------------------------------------------------------------------
* wrtTrk - Write NUMSEC sectors to the current track from a trkBuf as
*   pointed to by trkPtr. After the track is written, trkPtr is updated
*   by the length of a track to point to the next track buffer. 
*-----------------------------------------------------------------------------
wrtTrk	ldx	#wrtSkew	;assume 2:1 interleave table
	ldaa	fTunnel		;tunnel erase heads?
	bne	wtSkew		;yes, 2:1 is correct
	ldx	#rdSkew		;else use 1:1 table
wtSkew	stx	skewTbl

	ldaa	destTrk		;seek to destination track
	jsr	dSeek
	bne	seekErr		;seek error or write protected

	jsr	dReadAm		;get sector head is over
	bne	secErr
	jsr	getIdx		;get index of sector under head
	
	ldaa	#WRTRIES	;init write retry counter
	staa	wrRetry
	
wtRetry	ldaa	#NUMSEC		;set number of sectors to write
	staa	secCnt
	
wtLoop	jsr	incSec		;move to next sector index
	jsr	dSecAdr		;get sector and buffer address for write
	jsr	dWrite		;write the sector
	bne	wrtErr		;write failed
	dec	secCnt		;decrement sector count
	bne	wtLoop		;loop until all sectors written

*	bra	skipVfy		;skip verify for demo

* Verify the track just written

	ldx	#rdSkew		;use 1:1 interleave to read
	stx	skewTbl

	ldaa	secNum		;A=sector just written
	bsr	gtIdxA		;get index of sector just written
	bsr	incSec		;skip 1 sector for tunnel erase

	ldaa	#NUMSEC		;B=number of sectors to verify
	staa	secCnt

vfyLoop	jsr	incSec		;move to next sector index
	jsr	dSecAdr		;get sector number and buffer address
	jsr	dVerify
	bne	vfyErr		;error, goto retry logic

vfyNext	dec	secCnt		;decrement sector count
	bne	vfyLoop		;loop until all sectors verified
	
skipVfy	ldaa	trkPtr+1	;increment tkrPtr by the track length
	adda	trkLen+1
	staa	trkPtr+1
	
	ldaa	trkPtr		;MSB
	adca	trkLen
	staa	trkPtr

	rts

* vfyErr - verify failed, retry reads followed by a re-write
*   of the track.

vfyErr	ldaa	#RDTRIES	;init read retry counter
	staa	rdRetry

vfyRtry	jsr	dVerify		;attempt to verify again
	beq	vfyNext		;worked - move on to next sector

	dec	rdRetry		;decrement the read retry counter
	bne	vfyRtry

* Read retries expired, decrement the write retry count and re-write
*   if the count is not expired

	dec	wrRetry		;decrement the write retry counter
	bne	wtRetry

wrtErr	ldx	#mWrtErr
	jmp	pgmExit	

*-----------------------------------------------------------------------------
* incSec - Increment and wrap the sector index secIdx
*-----------------------------------------------------------------------------
incSec	ldaa	secIdx
	inca
	cmpa	#NUMSEC
	bne	isExit
	clra
isExit	staa	secIdx
	rts
	
*-----------------------------------------------------------------------------
* getIdx - Search for the sector in amSec as pointed to by skewTbl. Return
*    the index of the match in secIdx
*-----------------------------------------------------------------------------
getIdx	ldaa	amSec		;A=sector number under head
	
gtIdxA	ldab	TRKREG		;on track zero?
	bne	giNot0		;no
	cmpa	#2		;sector 0-2?
	bhi	giNot0		;no
	inca			;force 0 to 1, 1 to 2, 2 to 3

giNot0	clrb			;B=index into skew table
	ldx	skewTbl

giLoop	cmpa	0,x		;sector found?
	beq	giMatch		;yes
	inx			;move to next entry
	incb
	cmpb	#NUMSEC		;past end of table?
	bne	giLoop		;no, keep looking

	jmp	secErr		;couldn't find match
	
giMatch	stab	secIdx	
	rts
		
*------------------------------------------------------------------
* sizeRam - determine how much RAM we have for buffering tracks.
*   Sets the bufEnd variable which points to end address of 
*   the last possible track buffer + 1
*------------------------------------------------------------------
sizeRam	ldx	#trkBuf+$100	;look for end of RAM starting
	stx	saveX		;    at trkBuf+$100

ramLoop	ldaa	0,x		;A=current RAM content
	inc	0,x		;change value in RAM
	ldab	0,x		;B=new value from RAM
	staa	0,x		;restore original value
	
	cba			;did RAM change?
	beq	ramEnd		;no, end of RAM found
	
	inc	saveX		;increment to next page
	ldx	saveX		;X->next page in RAM
	ldaa	saveX		;don't go past MAXRAM
	cmpa	#MAXRAM/256
	bne	ramLoop
	
* ramEnd - end of RAM found. Now determine the end address + 1 of the
*   last track buffer that will fit in RAM. Store in bufEnd

ramEnd	ldab	saveX		;B=1st page not RAM
	ldx	#trkBuf		;start saveX at trkBuf
	stx	saveX

bfEndLp	ldaa	saveX+1		;increment saveX by TRKLEN
	staa	bufEnd+1	;bufEnd has previous value of saveX
	adda	trkLen+1
	staa	saveX+1

	ldaa	saveX		;MSB
	staa	bufEnd		;bufEnd has previous value of saveX
	adca	trkLen
	staa	saveX

	cba			;saveX MSB < RAM end MSB?
	bcs	bfEndLp		;yes, still more room,

	rts			;exit with bufEnd properly set

*---------------------------------------------------------------------
* format - Format disk
*---------------------------------------------------------------------
format	ldx	#mFmting	;display "formatting..."
	jsr	PSTRNG

	ldaa	#1		;select drive 1
	jsr	dSelect		;select drive in drvNum
	jsr	dRestor		;restore to track 0
	beq	fmtInit		;restore worked
	jmp	seekErr		;else, seek error

fmtInit	clr	destTrk		;start on track 0
	ldx	#trkBuf		;x->buffer for track format data

clrLoop	clr	0,x		;init entire buffer to zero
	inx	
	cpx	#trkBuf+FMTLEN
	bne	clrLoop
	
* fmtLoop - Loop through each track writing the track buffer to
*    disk after updating ID fields in each sector for each track.

fmtLoop	ldx	#trkBuf+GAP1	;x->start of sectors in trkBuf
	clr	secIdx		;secIdx goes from 0 to 17

secLoop	bsr	secData		;insert sector data
	inc	secIdx		;repeat for all 18 sectors
	ldaa	secIdx
	cmpa	#NUMSEC
	bne	secLoop

	jsr	dWrtTrk		;write the formatted track
	ldaa	#'*'
	jsr	PUTCHR

	inc	destTrk		;move to next track
	ldaa	destTrk
	cmpa	numTrk		;last track done?
	beq	fmtDone		;yes, exit

	jsr	dSeek		;else, seek to next track
	bra	fmtLoop		;repeat

fmtDone	rts

* secData - Update the ID record for each sector in the track

secData	ldaa	#$FE		;$FE = ID address mark
	staa	idMark,x

	ldaa	destTrk		;set track in ID record
	staa	idTrk,x

	stx	saveX		;convert secIdx into sector number
	ldab	secIdx
	ldx	#rdSkew		;x->sector mapping table
	stab	*+4		;modify offset in the following read
	ldab	0,x		;offset set to secIdx
	ldx	saveX		;restore x
	stab	idSec,x		;set sector in ID record

	tst	destTrk		;on track zero?
	bne	notTrk0		;no
	cmpb	#3		;sector 1-3?
	bhi	notTrk0		;no
	dec	idSec,x		;sector 1 to 0, 2 to 1, 3, to 2

notTrk0	ldaa	#$FB		;place the data mark
	staa	dataMrk,x

	ldaa	#$F7		;generate CRC
	staa	idCrc,x		;  for ID record
	staa	dataCrc,x	;  and after data
	
	stx	saveX		;increment x by raw sector length
	ldab	#RSECLEN	;b=length of raw sector
	addb	saveX+1		;compute LSB
	stab	saveX+1
	bcc	addExit		;done if no carry

	inc	saveX		;else increment MSB

addExit	ldx	saveX		;return result in X
	rts

*---------------------------------------------------------------------
* Disk I/O subroutines
*---------------------------------------------------------------------
DRVREG	equ	$8014 		;drive select register
CMDREG	equ	$8018 		;1771 command register
TRKREG	equ	$8019 		;1771 track register
SECREG	equ	$801A 		;1771 sector register
DATAREG	equ	$801B 		;1771 data register

* Select register equates

rMOTOFF	equ	$80		;1=motor off
rSIDE1	equ	$40		;1=side 1 (top)

* 1771 Commands

cREAD	equ	$88		;read, head assumed settled
cWRITE	equ	$A8		;write, head assumed settled
cRDADDR	equ	$C4		;readd address mark
cWAITHD	equ	$04		;bit to wait for head settle

cRESTOR	equ	$0B		;restore to track 0, load head
cSEEK	equ	$1B		;seek to track, load head
cWRTRK	equ	$F4		;write track (for formatting disk)
cRESET	equ	$D0		;reset 1771 to idle

* 1771 Status Masks

sNOTRDY	equ	$80		;1=drive not ready
sWRPROT	equ	$40		;1=write protected
sHEADLD	equ	$20		;1=head is loaded
sSKERR	equ	$10		;1=seek error
sCRCERR	equ	$08		;1=CRC error
sTRACK0	equ	$04		;1=on track zero
sINDEX	equ	$02		;1=index detected
sBUSY	equ	$01		;1=1771 busy

sSECERR	equ	$10		;1=sector not found
sWRTFLT	equ	$20		;1=write fault
sDLOST	equ	$04		;1=lost data
sDRQ	equ	$02		;1=data request

* Error masks for status of a seek, read, write

eSEEK	equ	sSKERR+sNOTRDY
eREAD	equ	sSECERR+sCRCERR+sDLOST+sNOTRDY
eVERIFY	equ	sSECERR+sCRCERR+sNOTRDY
eWRITE	equ	eREAD+sWRPROT+sWRTFLT

*--------------------------------------------------------------
* dRead
*    Read sector specified by secNum into buffer at secPtr
*--------------------------------------------------------------
dRead	ldx	secPtr		;X->sector buffer
	ldaa	secNum		;give sector number to the 1771
	staa	SECREG

	ldaa	#cREAD		;1771 read command
	sei			;disable interrupts
	staa	CMDREG		;issue command to 1771
	jsr	dCmdDly		;ensure command processing time

drLoop	ldaa	CMDREG		;A=1771 status
	bita	#sBUSY		;read still in progress>?
	beq	drDone		;no, read has finished
	bita	#sDRQ		;data ready flag set?
	beq	drLoop		;no, keep waiting
	
	ldaa	DATAREG		;get the next byte
	staa	0,x		;save in buffer
	inx
	bra	drLoop

drDone	cli			;enable interrupts
	anda	#eREAD		;test and return read error bits
	rts

*--------------------------------------------------------------
* dReadAm
*    Read next address mark into amBuf
*--------------------------------------------------------------
dReadAm	ldaa	#RDTRIES	;set up retry counter
	staa	rdRetry

daRetry	ldx	#amBuf		;read into the address mark buffer
	ldaa	#cRDADDR	;1771 read address command
	sei
	staa	CMDREG		;issue command to 1771
	jsr	dCmdDly		;ensure command processing time

daLoop	ldaa	CMDREG		;A=1771 status
	bita	#sBUSY		;read still in progress>?
	beq	daDone		;no, read has finished
	bita	#sDRQ		;data ready flag set?
	beq	daLoop		;no, keep waiting
	
	ldaa	DATAREG		;get the next byte
	staa	0,x		;save in buffer
	inx
	bra	daLoop

daDone	cli			;enable interrupts
	anda	#eREAD		;any errors?
	bne	daFail		;yes

	ldaa	amSec		;see if reasonable data present
	cmpa	#NUMSEC
	bhi	daFail		;invalid sector

	clra			;exit with zero status		
daExit	rts

daFail	dec	rdRetry		;any more retries?
	bpl	daRetry		;yes, try again
	rts			;exit with non-zero status

*--------------------------------------------------------------
* dWrite
*    Write sector specified by secNum from secPtr
*--------------------------------------------------------------
dWrite	ldaa	secNum		;give sector number to the 1771
	staa	SECREG

	ldaa	#cWRITE		;1771 write command
	sei			;disable interrupts
	staa	CMDREG		;issue command to 1771
	jsr	dCmdDly		;ensure command processing time

	ldx	#1300		;timeout 23.4ms (2+sectors)

* Wait for and timeout the 1st byte request. It should happen in less
*   then 2 sector times if the drive can write consecutive sectors
*   (straddle erase heads). Otherwise, it must be a tunnel erase head
*   and the write has to interleave every other sector.

dwWait1	ldaa	CMDREG		;A=1771 status
	bita	#sDRQ		;ready for data?
	bne	dwHave1		;yes, have 1st byte
	dex			;decrement timer
	bne	dwWait1		;18us loop

	inc	fTunnel		;timeout - set tunnel erase flag
	ldx	#wrtSkew	;use write skew table instead
	stx	skewTbl
	ldx	secPtr		;X->sector buffer
	bra	dwLoop		;jump to normal loop

dwHave1	ldx	secPtr		;X->sector buffer
	ldaa	0,x		;get 1st byte
	staa	DATAREG		;give it to the 1771
	inx

* Transfer the rest of the sector

dwLoop	ldaa	CMDREG		;A=1771 status
	bita	#sBUSY		;write still in progress>?
	beq	dwDone		;no, write has finished

	bita	#sDRQ		;ready for data?
	beq	dwLoop		;no, keep waiting
	
	ldaa	0,x		;get next byte
	staa	DATAREG		;give it to the 1771
	inx
	bra	dwLoop

dwDone	cli			;re-enable interrupts
	anda	#eWRITE		;test and return write error bits
	rts

*--------------------------------------------------------------
* dWrtTrk
*    Write a 1771 full track from trkBuf for formatting
*--------------------------------------------------------------
dWrtTrk	ldx	#trkBuf		;X->formatted track buffer

	ldaa	#cWRTRK		;1771 write track command
	sei			;disable interrupts
	staa	CMDREG		;issue command to 1771
	jsr	dCmdDly		;ensure command processing time

dwtLoop	ldaa	CMDREG		;A=1771 status
	bita	#sBUSY		;write still in progress>?
	beq	dwtDone		;no, write has finished
	bita	#sDRQ		;ready for data?
	beq	dwtLoop		;no, keep waiting
	
	ldaa	0,x		;get next byte
	staa	DATAREG		;give it to the 1771
	inx
	bra	dwtLoop

dwtDone	cli			;re-enable interrupts
	anda	#eWRITE		;test and return write error bits
	rts
	
*--------------------------------------------------------------
* dVerify
*    Verify sector specified by secNum by validating its CRC
*--------------------------------------------------------------
dVerify	ldaa	secNum		;give sector number to the 1771
	staa	SECREG

	ldaa	#cREAD		;1771 read command
	bsr	dCmdWt		;issue command and wait
	anda	#eVERIFY	;test and return verify errors
	rts
	
*--------------------------------------------------------------
* dSeek
*    Seek to track in A and load the head. If A is track
*    zero (will be the 1st seek in this program), then the 
*    restore command is used to establish the track number
*    in the 1771. The returned status includes the write 
*    protect bit.
*--------------------------------------------------------------
dSeek	tsta			;requesting track zero?
	beq	dRestor		;yes, use restore instead
	
	staa	DATAREG		;give track to the 1771
	ldaa	#cSEEK		;A=seek to track and load head
	bsr	dCmdWt		;issue command and wait for it
	anda	#eSEEK
	rts
	
*--------------------------------------------------------------
* dRestore
*    Issue and wait on restore to track zero. The returned
*    status includes the write protect bit.
*--------------------------------------------------------------
dRestor	ldaa	#cRESTOR	;A=restore to track 0 and load head
	bsr	dCmdWt		;issue command and wait for it
	anda	#eSEEK		;status mask includes write protect
	rts
	
*--------------------------------------------------------------
* dCmdWt
*    Write command from A and wait for it to complete.
*--------------------------------------------------------------
dCmdWt	staa	CMDREG		;issue command from A
	bsr	dCmdDly		;ensure command processing time
	
dWtCmd	ldaa	CMDREG		;poll for busy bit to go false
	bita	#sBUSY
	bne	dWtCmd	

	rts
	
*--------------------------------------------------------------
* dSelect
*    Select the drive passed in A and set the track register
*    to the value in B. Wait 1 sec for motor if the motor is
*    not already on.
*--------------------------------------------------------------
dSelect	staa	DRVREG		;select the drive in A
	ldaa	CMDREG		;is motor already on?
	anda	#sNOTRDY	;  (ldaa also turned on the motor)
	beq	mtrIsOn		;yes

	ldx	#0		;wait 1 sec for motor start up	
motrDly	inx			;(4)
	dex			;(4)
	dex			;(4)
	bne	motrDly		;(4)
	
mtrIsOn	ldaa	#cRESET		;reset the 1771 to be safe
	bsr	dCmdWt
	stab	TRKREG		;set track drive is on
	rts

*--------------------------------------------------------------
* dCmdDly
*    Wait 58us (including the calling JSR) to ensure a 1771
*    command is accepted and the status register can be read.
*
*    This delay had to be bumped to 76us (60us in the loop)
*    to prevent occasional errors in dReadAm.
*--------------------------------------------------------------
dCmdDly	ldaa	#10		;10*6=60us in loop

dlyLoop	deca
	bne	dlyLoop
	rts
	
*--------------------------------------------------------------
* dSecAdr
*    Convert the sector index (secIdx) into the sector number
*    on the disk (secNum) and a pointer into the track buffer
*    (secPtr)
*--------------------------------------------------------------
dSecAdr	ldx	skewTbl		;X->proper skew table to use
	ldaa	secIdx		;index into skewTbl by secIdx
	staa	*+4		;modify index offset in next inst
	ldaa	0,x		;A=sector to look for on disk
	staa	secNum		;save in secNum
	
	tst	TRKREG		;on track 0?
	bne	dsNotT0		;no
	cmpa	#3		;sector 1-3?
	bhi	dsNotT0		;no
	dec	secNum		;else sector 1 to 0, 2 to 1, 3 to 2

dsNotT0	deca			;form (secNum-1)*128 in A:B
	clrb
	lsra			;MSB is *128
	rorb			;LSB is *128
	addb	trkPtr+1	;compute LSB of sector address
	stab	secPtr+1
	adca	trkPtr		;compute MSB of sector address
	staa	secPtr
	rts

*---------------------------------------------------------------------
* Message constants
*---------------------------------------------------------------------
mConfm	fcb	CR,LF
	fcc	'CP/68 FAST BACKUP v1.0'
	fcb	CR,LF
	fcc	'Source in drive 0 and destination in drive 1? '
	fcb	EOT

mFormat	fcb	CR,LF
	fcc	'Format destination? '
	fcb	EOT

mFmting	fcb	CR,LF
	fcc	'Formatting '
	fcb	EOT

mCopy	fcb	CR,LF
	fcc	'Copying'
	fcb	EOT

mRdErr	fcb	CR,LF
	fcc	'Source disk read error'
	fcb	CR,LF
	fcb	EOT

mWrtErr	fcb	CR,LF
	fcc	'Destination disk write error'
	fcb	CR,LF
	fcb	EOT

mNoSec	fcb	CR,LF
	fcc	'Sector not found - disk formatted?'
	fcb	CR,LF
	fcb	EOT

mWrtPrt	fcb	CR,LF
	fcc	'Disk is write protected'
	fcb	CR,LF
	fcb	EOT

mSkErr	fcb	CR,LF
	fcc	'Restore or seek error'
	fcb	CR,LF
	fcb	EOT

mDone	fcb	CR,LF
	fcc	'Backup complete'
	fcb	CR,LF
	fcb	EOT

*---------------------------------------------------------------------
*  Data area
*---------------------------------------------------------------------

* rdSkew - Returns logical sector numbers in the physical order
*    they appear on the disk. This allows reading the track in one
*    revolution.
*
* wrtSkew - Returns the logical sector number of every other sector
*    in physical order. Because of the small gap between sectors
*    (14 bytes), drives with tunnel erase heads (most drives other
*    than Shugart) cannot write consecutive sectors. This table
*    returns every other sector so the write can complete in two
*    revolutions.

rdSkew	fcb	1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18
wrtSkew	fcb	1,3,5,7,9,11,13,15,17,2,4,6,8,10,12,14,16,18

* disk variables

numTrk	rmb	1		;number of tracks on the disk
trkLen	rmb	2		;length of a track
skewTbl	rmb	2		;pointer to the skew table to use

srcTrk	rmb	1		;track number to read
destTrk	rmb	1	
secIdx	rmb	1		;ordinal sector on disk
secNum	rmb	1		;sector number in address mark
secCnt	rmb	1		;count of sectors to read

trkPtr	rmb	2		;pointer into trkBuf at track boundaries
secPtr	rmb	2		;pointer to current sector in trkBuf
bufEnd	rmb	2		;end address + 1 of last track spot in RAM

fTunnel	rmb	1		;1=tunnel erase drive detected

rdRetry	rmb	1		;read retry counter
rtStep	rmb	1		;retry step
wrRetry	rmb	1		;write retry counter

* address mark buffer

amBuf	equ	*
amTrk	rmb	1		;LSB of track number
	rmb	1
amSec	rmb	1		;sector number
amSize	rmb	1		;sector length code
	rmb	4		;CRC and some spare

* misc variables

saveX	rmb	2		;generic save location for X

* track buffer runs from here to the end of memory

trkBuf	equ	*

	end	entry
