;*****************************************************************************
;
;  COPY.ASM
;	Disk copy, verify, and format utility for CP/M 2.2b on North Star
;	single density controller. This utility requires the raw I/O entry
; 	point in the BIOS jump table provided by this version of CP/M.
;
;	Rev	  Date	    Desc
;	1.0	11/16/15   M. Douglas, Original
;
;-----------------------------------------------------------------------------
;
;    The Raw I/O entry point in the BIOS is the 18th vector in the BIOS
;    jump table (after SECTRAN). Calls to raw I/O flush the track buffer
;    (if needed) and also update the track table so that the BIOS and
;    drives remain in sync with each other.
;
;    The following parameters are passed to the raw I/O entry point:
;    	A=number of sectors
;    	B=track
;    	C=drive (North Star 1 indexed, not CP/M 0 indexed)
;    	D=starting sector
;    	E=command (0=write, 1=read, 2=verify)
;    	HL=buffer address
;    Z flag set if the I/O operation was successful, cleared otherwise.
;    Assume all registers are modified on exit
;
;*****************************************************************************

; Drive equates

NUMDRVS	equ	3		;number of drives supported
NUMTRKS	equ	35		;35 tracks
NUMSECS	equ	10		;ten sectors per track
SECLEN	equ	256		;sector is 256 bytes
TRKLEN	equ	(NUMSECS*SECLEN)	;length of track
SYSTRKS	equ	3		;number of system tracks
DATATRK	equ	3		;starting data track

; Raw I/O equates

READ0	equ	0001h		;d=sector 0, e=read command
WRITE0	equ	0000h		;d=sector 0, e=write command
VERIFY0	equ	0002h		;d=sector 0, e=verify command

; BDOS and misc equates

CONOUT	equ	2		;BDOS console output (single char)
PRINT	equ	9		;BDOS write string to console
RDLINE	equ	10		;BDOS read a line from the console
BDOS	equ	5		;BDOS entry point
IBUFLEN	equ	80		;length of input buffer

; BIOS equates

WBOOT	equ	0		;warm boot vector
BIOS	equ	1		;address of BIOS as stored on page zero
RAWIOV	equ	(18-2)*3	;offset of raw I/O vector from warm start vector

; Misc equates

RDTRYS	equ	5		;read attempts
WRTRYS	equ	4		;write attempts
CR	equ	13		;ascii for carriage return
LF	equ	10		;ascii for line feed

	org	0100h		;CP/M load and entry address
;-----------------------------------------------------------------------------
;  Program entry and initialization
;-----------------------------------------------------------------------------
	lxi	h,0		;save stack pointer by HL=SP+0
	dad	sp		;HL=current stack pointer from CP/M
	shld	stack		;save for restore later
	lxi	sp,OURSTK	;setup our own stack

	xra	a		
	sta	reboot		;reboot not required yet

	lxi	d,mBanner	;display startup message
	call	dispMsg
	
; verify this program is running on CP/M 2.2 for the North Star SD controller
;    by looking for the "raw I/O" jump after the last normal BIOS jump vector
;    (sectran). Save the address of the Raw I/O routine in pRawIo

	lhld	BIOS		;hl->warm start jump vector in BIOS
	lxi	d,RAWIOV	;DE=offset of North Star raw I/O function
	dad	d		;hl->raw I/O jump vector
	mov	a,m		;should be a jump opcode
	cpi	0c3h
	jnz	exitOs		;not jump, exit due to wrong OS

	sta	rawIo		;save jump opcode for raw I/O call
	inx	h		;form de=raw I/O address
	mov	e,m		;e=lsb of jump address	
	inx	h
	mov	d,m		;de->raw I/O entry address
	xchg			;hl->raw I/O entry address
	shld	rawIo+1		;save address for raw I/O jump

	lda	BIOS+1		;a=msb of BIOS
	dcr	a		;compare msb of BIOS-1 to raw I/O MSB
	cmp	h		;sanity check, raw I/O msb >= BIOS MSB?
	jnc	exitOs		;not a reasonable raw I/O address, exit

; determine the number of tracks we can buffer based without clobbering
;    the BDOS. Save in maxBuf.

	lhld	BDOS+1		;hl=address of BDOS entry
	mvi	l,0		;l=number of tracks that fit
	mvi	a,trkBuf/256	;a=msb of track buffer

sizeLp	inr	l		;count a track
	adi	TRKLEN/256	;msb of track length (ok because LSB is zero)
	cmp	h		;reached or passed BDOS yet?
	jc	sizeLp		;no, keep counting

	dcr	l		;back up one track
	mov	a,l		;maxBuf = maximum tracks in trkBuf	
	sta	maxBuf

;-----------------------------------------------------------------------------
; dspMenu - Main menu loop
;-----------------------------------------------------------------------------
dspMenu	lxi	d,mMenu		;display menu
	call	dispMsg

	call	readChr		;read response
	ani	5fh		;clear bit 5 ('a' to 'A')

	cpi	'A'		;copy all tracks
	cz	cmdAll

	cpi	'S'		;copy system tracks only
	cz	cmdSys

	cpi	'D'		;copy data tracks only
	cz	cmdData

	cpi	'F'		;format disk
	cz	cmdFmt

	cpi	'V'		;verify disk
	cz	cmdVfy	

	cpi	'X'		;exit
	jz	exit		

	jmp	dspMenu

;-----------------------------------------------------------------------------
; cmdAll - Copy all tracks
;-----------------------------------------------------------------------------
cmdAll	lxi	d,mCmdAll	;display command name
	call	dispmsg

	mvi	a,NUMTRKS	;copying all tracks on the disk
	sta	remTrks		;remaining tracks = all tracks
	xra	a		;current track = track zero
	sta	srcTrk
	sta	dstTrk

; cpyTrks - copy the number of tracks in remTrks, starting at src/dstTrk

cpyTrks	call	getSrc		;get source and destination from operator
	rnz			;user aborted
	call	getDst
	rnz

; if not single drive copy, display prompt to insert disks

	lxi	h,dstDrv	;hl->destination drive
	lda	srcDrv		;source = destination?
	cmp	m
	jz	cpyLoop		;single drive copy, skip next message

	lxi	d,mRdyCpy	;display insert disks prompt
	call	getRdy		;get ready confirmation from operator
	rnz			;user aborted

; cpyLoop - filling trkBuf from source, then writing trkBuf to destination

cpyLoop	call	ldSrc		;prompt for source if single drive
	rnz			;user aborted

	call	fillBuf		;read as many tracks as possible
	jnz	rdFail		;read failed

	call	ldDst		;prompt for destination if single drive
	rnz			;user aborted

	call	wrtBuf		;write the full track buffer
	jnz	wrFail		;write error

	lda	remTrks		;any tracks left?
	dcr	a		;negative or zero -> negative
	jp	cpyLoop

; copy complete, display message and exit

	lxi	d,mCpyDon	;copy done message
	call	dispMsg
	xra	a		;clear command character
	ret

;-----------------------------------------------------------------------------
; cmdSys - Copy system tracks only
;-----------------------------------------------------------------------------
cmdSys	lxi	d,mCmdSys	;display command name
	call	dispmsg

	mvi	a,SYSTRKS	;copying just system tracks
	sta	remTrks		;remaining tracks = just system tracks
	xra	a		;current track = track zero
	sta	srcTrk
	sta	dstTrk

	jmp	cpyTrks		;do the copy (in cmdAll)

;-----------------------------------------------------------------------------
; cmdData - Copy data tracks only
;-----------------------------------------------------------------------------
cmdData	lxi	d,mCmdDat	;display command name
	call	dispmsg

	mvi	a,NUMTRKS-SYSTRKS	;copying just data tracks
	sta	remTrks		;remaining tracks = just data tracks
	mvi	a,DATATRK	;current track = first data track
	sta	srcTrk
	sta	dstTrk

	jmp	cpyTrks		;do the copy (in cmdAll)

;-----------------------------------------------------------------------------
; cmdFmt - Format the disk by writing E5h to all bytes
;-----------------------------------------------------------------------------
cmdFmt	lxi	d,mCmdFmt	;display command name
	call	dispmsg

	mvi	a,NUMTRKS	;formatting all tracks on the disk
	sta	trkCnt
	xra	a		;current track = track zero
	sta	dstTrk

	call	getDst		;get destination disk from operator
	rnz			;user aborted

	lxi	d,mRdyFmt	;display insert disk prompt
	call	getRdy		;get ready confirmation from operator
	rnz			;user aborted

; fill one track buffer with E5h

	lxi	h,trkBuf	;hl->track buffer
	lxi	b,TRKLEN	;be=length of track
	mvi	d,0e5h

fillE5	mov	m,d		;put e5 in track buffer
	inx	h
	dcx	b		;decrement byte counter
	mov	a,b		;test for zero
	ora	c
	jnz	fillE5

; fmtLoop - write all tracks on disk with E5

fmtLoop	lxi	h,trkBuf	;hl->beginning of track buffer
	call	wrtTrk		;write a track
	jnz	fmtFail		;write error, format failed

	lxi	h,dstTrk	;increment current track
	inr	m
	lxi	h,trkCnt	;decrement track counter
	dcr	m
	jnz	fmtLoop

; format complete, display message and exit

	lxi	d,mFmtDon	;de->format done message
	call	dispMsg
	xra	a		;clear command character
	ret

;-----------------------------------------------------------------------------
; cmdVfy - Verify all tracks
;-----------------------------------------------------------------------------
cmdVfy	lxi	d,mCmdVfy	;display command name
	call	dispmsg

	mvi	a,NUMTRKS	;verifying all tracks on the disk
	sta	trkCnt
	xra	a		;current track = track zero
	sta	srcTrk

	call	getSrc		;get source disk from operator
	rnz			;user aborted

	lxi	d,mRdyVfy	;display insert disk prompt
	call	getRdy		;get ready confirmation from operator
	rnz			;user aborted

; vfyLoop - read all tracks which verifies checksum

vfyLoop	lxi	h,trkBuf	;hl->beginning of track buffer
	call	readTrk		;read a track
	jnz	rdFail		;read error, verify failed

	lxi	h,srcTrk	;increment current track
	inr	m
	lxi	h,trkCnt	;decrement track counter
	dcr	m
	jnz	vfyLoop

; verify complete, display message and exit

	lxi	d,mVfyDon	;de->verify done message
	call	dispMsg
	xra	a		;clear command character
	ret

;-----------------------------------------------------------------------------
; fillBuf - fill trkBuf with as many tracks that will fit, or the
;    remaining number of tracks to process, whichever is fewer.
; On Entry:
;    srcDrv = drive to read
;    srcTrk = track to start from
;    remTrks = number of tracks left to read
; On Exit:
;    srcTrk, remTrks updated (remTrks may go negative)
;    fillCnt = number of tracks read
;    Z set if OK, cleared for error
;    Clobbers all
;-----------------------------------------------------------------------------
; compute fillCnt, the number of tracks to read into trkBuf

fillBuf	lxi	h,maxBuf	;set b=max tracks that fit in trkBuf
	mov	b,m
	lxi	h,remTrks	;set a,c=tracks remaining to process
	mov	a,m
	mov	c,a
	sta	reboot		;nonzero=reboot required because BDOS clobbered

	sub	b		;a=remaining-max in buffer
	mov	m,a		;save remaining tracks (may be negative)
	mov	a,b		;a=max tracks in buffer	
	jnc	doMax		;remaining >= max in buffer, do max in buffer
	mov	a,c		;otherwise, fill count=remaining tracks
doMax	sta	fillCnt		;fillCnt=number of tracks to read (saved)
	sta	trkCnt		;trkCnt=counter of tracks to read (decrements)

; fillLp - read fillCnt tracks.
	
	lxi	h,trkBuf	;hl->beginning of track buffer

fillLp	call	readTrk		;read a track into (hl)
	rnz			;couldn't read, exit
	
	xchg			;save hl in de
	lxi	h,srcTrk	;increment current track
	inr	m
	lxi	h,trkCnt	;decrement track counter
	dcr	m
	xchg			;restore hl->next track in trkBuf
	jnz	fillLp
	ret			;fill done, zero status

;-----------------------------------------------------------------------------
; wrtBuf - write and verify fillCnt tracks from trkbuf
; On Entry:
;    dstDrv = drive to read
;    dstTrk = track to start from
;    fillCnt = number of tracks to write
; On Exit:
;    dstTrk updated 
;    Z set if OK, cleared for error
;    Clobbers all
;-----------------------------------------------------------------------------
wrtBuf	lda	fillCnt		
	sta	trkCnt		;trkCnt=counter of tracks to write
	lxi	h,trkBuf	;hl->beginning of track buffer

; wrtLoop - write loop. Write a track and then verify. Repeat trkCnt times.

wrtLoop	call	wrtTrk		;write a track
	rnz			;exit if error

	xchg			;save hl in de
	lxi	h,dstTrk	;increment current track
	inr	m
	lxi	h,trkCnt	;decrement track counter
	dcr	m
	xchg			;restore hl->next track in trkBuf
	jnz	wrtLoop
	ret

;-----------------------------------------------------------------------------
; readTrk - read a track with retries into memory at (hl)
; On Entry:
;    srcDrv = drive to read
;    srcTrk = track to read
;    hl->track buffer
; On Exit: 
;    hl->last byte of track+1 if successful, start of bad track otherwise
;    Z set if OK, cleared for error
;    Clobbers all
;-----------------------------------------------------------------------------
readTrk	mvi	a,RDTRYS	;init retry counter
	sta	rdRtry

reRead	lda	srcDrv		;init registers for rawIo call
	mov	c,a		;c=drive
	lda	srcTrk
	mov	b,a		;b=track
	lxi	d,READ0		;read command, starting at sector 0
	mvi	a,NUMSECS	;read ten sectors

	push	h		;save track buffer pointer
	call	rawIo		;read the track
	jnz	readErr		;read error

; read successful, display "." pacifier, then update hl and exit

	mvi	e,'.'		;e=character to display
	mvi	c,CONOUT	;c=BDOS function
	call	BDOS		;display pacifier

	pop	h		;hl->start of buffer
	mov	a,h		;bump hl by one track
	adi	TRKLEN shr 8	;add msb of track length (lsb is zero)
	mov	h,a		;works because sectors are 256 bytes

	xra	a		;return with zero status
	ret

; readErr - read error on track. Decrement retry counter and try again

readErr	lxi	h,rdRtry	;decrement retry counter
	dcr	m
	pop	h		;hl->start of failed track
	jnz	reRead		;try again

	mvi	a,1		;force non-zero status
	ora	a
	ret

;-----------------------------------------------------------------------------
; wrtTrk - write a track with retries from (hl)
; On Entry:
;    dstDrv = drive to write
;    dstTrk = track to write
;    hl->track buffer
; On Exit: 
;    hl->last byte of track+1 if successful, start of bad track otherwise
;    Z set if OK, cleared for error
;    Clobbers all
;-----------------------------------------------------------------------------
wrtTrk	mvi	a,WRTRYS	;init write retry counter
	sta	wrRtry

reWrite	lda	dstDrv		;init registers for write rawIo call
	mov	c,a		;c=drive
	lda	dstTrk
	mov	b,a		;b=track
	lxi	d,WRITE0	;write command, starting at sector 0
	mvi	a,NUMSECS	;write ten sectors

	push	h		;save track buffer pointer
	call	rawIo		;write the track
	
	pop	h		;restore buffer pointer
	call	vfyTrk		;verify the track
	rz			;exit if verify successful

; verify error, retry the write WRTRYS times.

	lda	wrRtry		;decrement write retry counter
	dcr	a		;without affecting hl
	sta	wrRtry
	jnz	reWrite		;try again

	mvi	a,1		;force non-zero status
	ora	a
	ret

;-----------------------------------------------------------------------------
; vfyTrk - verify a written track at (hl)
; On Entry:
;    dstDrv = drive to read
;    dstTrk = track to read
;    hl->track buffer
; On Exit: 
;    hl->last byte of track+1 if successful, start of bad track otherwise
;    Z set if OK, cleared for error
;    Clobbers all
;-----------------------------------------------------------------------------
vfyTrk	mvi	a,RDTRYS	;init retry counter
	sta	rdRtry

reVfy	lda	dstDrv		;init registers for rawIo call
	mov	c,a		;c=drive
	lda	dstTrk
	mov	b,a		;b=track
	lxi	d,VERIFY0	;verify command, starting at sector 0
	mvi	a,NUMSECS	;verify ten sectors

	push	h		;save track buffer pointer
	call	rawIo		;verify the track
	jnz	vfyErr		;verify error

; verify successful, display "*" pacifier, then update hl and exit

	mvi	e,'*'		;e=character to display
	mvi	c,CONOUT	;c=BDOS function
	call	BDOS		;display pacifier

	pop	h		;hl->start of buffer
	mov	a,h		;bump hl by one track
	adi	TRKLEN shr 8	;add msb of track length (lsb is zero)
	mov	h,a		;works because sectors are 256 bytes

	xra	a		;return with zero status
	ret

; vfyErr - verify error on track. Decrement retry counter and try again

vfyErr	lxi	h,rdRtry	;decrement retry counter
	dcr	m
	pop	h		;hl->start of failed track
	jnz	reVfy		;try again

	mvi	a,1		;force non-zero status
	ora	a
	ret

;----------------------------------------------------------------------
; dispMsg - use the BDOS print message command to display the string
;    pointed to by DE.
; On Entry:
;    de->message to display
; On Exit
;    clobbers all
;----------------------------------------------------------------------
dispMsg	mvi	c,PRINT		;BDOS command to display a string
	jmp	BDOS		;call BDOS


;----------------------------------------------------------------------
; readChr - read and return a single character but use the BDOS input
;   line command for "normal" look and feel. Return zero if no input
;   entered (e.g., just return pressed).
; On Entry:
;    de->message to display
; On Exit
;    a = first character typed, zero if nothing typed
;    clobbers all
;----------------------------------------------------------------------
readChr	lxi	d,inBuf		;DE=pointer to input buffer
	mvi	a,IBUFLEN	;store max length to read
	stax	d
	mvi	c,RDLINE	;BDOS command, read line
	call	BDOS

	lxi	d,mCRLF
	call	dispMsg

	lda	inBuf+1		;get length read
	ora	a		;see if nothing entered
	rz			;if so, return zero

	lda	inBuf+2		;otherwise, return first character
	ret

;-----------------------------------------------------------------------------
; getSrc - get source drive from user, return in srcDrv, update drive
;    letter in message prompts.
; On Entry:
; On Exit: 
;    srcDrv = 1-3 if specified
;    Z set if valid drive entered, cleared otherwise
;    Clobbers all
;-----------------------------------------------------------------------------
getSrc	lxi	d,mSrc		;enter source drive prompt 
	call	dispMsg

	call	readChr		;read drive response
	ani	5fh		;clear bit 5 ('a' to 'A')
	sta	mSrcDrv		;update drive in messages
	sta	mVfyDrv
	sta	mSglSrc

	sui	'A'		;subtract ascii offset
	rc			;below 'A', exit with non-zero

	cpi	NUMDRVS		;valid drive number?
	jnc	getErr		;above 'C', exit

	inr	a		;0-2 -> 1-3
	sta	srcDrv		;save it

	xra	a		;return zero status
	ret

;-----------------------------------------------------------------------------
; getDst - get destination drive from user, return in dstDrv, update
;    drive letter in message prompts.
; On Entry:
; On Exit: 
;    dstDrv = 1-3 if specified
;    Z set if valid drive entered, cleared otherwise
;    Clobbers all
;-----------------------------------------------------------------------------
getDst	lxi	d,mDst		;enter source drive prompt 
	call	dispMsg

	call	readChr		;read drive response
	ani	5fh		;clear bit 5 ('a' to 'A')
	sta	mDstDrv		;update drive in messages
	sta	mFmtDrv
	sta	mSglDst

	sui	'A'		;subtract ascii offset
	rc			;below 'A', exit with non-zero

	cpi	NUMDRVS		;valid drive number?
	jnc	getErr		;above 'C', exit

	inr	a		;0-2 -> 1-3
	sta	dstDrv		;save it

	xra	a		;return zero status
	ret

; getErr - invalid response, return non-zero status

getErr	ora	a		;force non-zero staus
	ret

;-----------------------------------------------------------------------------
; getRdy - prompt user for confirmation of ready. 
; On Entry:
;    de->message prompt to display
; On Exit: 
;    Z set if user confirmed, cleared if user aborted.
;    Clobbers all
;-----------------------------------------------------------------------------
getRdy	call	dispMsg		;display passed prompt
	lxi	d,mCont		;continue? prompt
	call	dispMsg

	call	readChr		;read response
	ani	5fh		;clear bit 5 ('a' to 'A')
	cpi	'Y'		;confirmed?
	ret

;-----------------------------------------------------------------------------
; ldSrc, ldDst - prompt user to load the source or destination disk if
;    this is a single drive copy.
; On Entry:
;    srcDrv = source drive
;    dstDrv = destination drive
; On Exit: 
;    Z set if ok to continue, cleared if user aborted.
;    Clobbers all
;-----------------------------------------------------------------------------
ldSrc	lxi	d,mInsSrc	;de->prompt user to insert source disk
	jmp	chkSngl		;go see if single drive copy

ldDst	lxi	d,mInsDst	;de->prompt user to insert destination disk

chkSngl	lxi	h,dstDrv	;hl->destination drive
	lda	srcDrv		;source = destination?
	cmp	m
	jz	getRdy		;single drive copy, get ready from user

	xra	a		;not single drive, return zero status
	ret

;-------------------------------------------------------------------
; rdFail, wrFail, fmtFail - display fail message
;-------------------------------------------------------------------
rdFail	lxi	d,mRdErr
	jmp	dispMsg

wrFail	lxi	d,mWrtErr
	jmp	dispMsg

fmtFail	lxi	d,mFmtErr
	jmp	dispMsg

;-------------------------------------------------------------------
;  exitOs, exit - Exit to CP/M
;-------------------------------------------------------------------
exitOs	lxi	d,mCpmVer	;wrong CP/M version
	call	dispMsg

exit	lda	reboot		;see if we need to warm boot
	ora	a
	jnz	WBOOT		;warm boot CP/M

	lhld	stack		;restore stack pointer
	sphl
	ret			;return directly to CCP 

;-------------------------------------------------------------------
;  String Constants
;-------------------------------------------------------------------
mBanner
 db CR,LF,'COPY Version 1.0 by M. Douglas',CR,LF
 db 'Disk utility for CP/M 2.2 on North Star SD Controller',CR,LF,'$'

mMenu
 db CR,LF,LF,'======  O P T I O N   T A B L E  ======',CR,LF,LF
 db '  A = Copy all tracks (also formats the disk)',CR,LF
 db '  S = Copy only system tracks (0-2)',CR,LF
 db '  D = Copy only data tracks (3-34)',CR,LF
 db '  F = Format disk',CR,LF
 db '  V = Verify disk',CR,LF
 db '  X = Exit and return to CP/M',CR,LF,LF
 db 'Enter Selection: $'

mCmdAll
 db CR,LF,'  Copy All Tracks',CR,LF,'$'

mCmdSys
 db CR,LF,'  Copy Only System Tracks',CR,LF,'$'

mCmdDat
 db CR,LF,'  Copy Only Data Tracks',CR,LF,'$'

mCmdFmt
 db CR,LF,'  Format Disk',CR,LF,'$'

mCmdVfy
 db CR,LF,'  Verify Disk',CR,LF,'$'

mSrc
 db CR,LF,'Enter source drive (A-C or X to abort): $' 

mDst
 db CR,LF,'Enter destination drive (A-C or X to abort): $' 

mRdyCpy	db	CR,LF,'Source on drive '
mSrcDrv	db	'A, destination on drive '
mDstDrv	db	'B$'

mRdyFmt	db	CR,LF,'Disk to format in drive '
mFmtDrv	db	'A$'

mRdyVfy	db	CR,LF,'Disk to verify in drive '
mVfyDrv	db	'A$'

mInsSrc	db	CR,LF,'Insert source disk in drive '
mSglSrc	db	'A$'

mInsDst	db	CR,LF,'Insert destination disk in drive '
mSglDst	db	'A$'

mCont	db	CR,LF,'  Continue(Y/N)? $'

mCpmVer	
 db CR,LF,'COPY not supported on this version of CP/M',CR,LF,'$'

mRdErr	
 db CR,LF,LF,'** Read or Verify error **',CR,LF,'$'

mWrtErr	
 db CR,LF,LF,'** Unrecoverable write error **',CR,LF,'$'

mFmtErr	
 db CR,LF,LF,'** Not able to format this disk **',CR,LF,'$'

mCpyDon
 db CR,LF,LF,'** Copy and verify complete **',CR,LF,'$'

mVfyDon
 db CR,LF,LF,'** Verify complete, no errors found **',CR,LF,'$'

mFmtDon
 db CR,LF,LF,'** Format complete **'

mCRLF
 db CR,LF,'$'


;-------------------------------------------------------------------
;  Data Area
;-------------------------------------------------------------------	
stack	ds	2		;save area for CP/M stack pointer
inBuf	ds	IBUFLEN		;storage for command string
rawIo	ds	3		;jump instruction to raw I/O

reBoot	ds	1		;1=reboot required on exit

srcDrv	ds	1		;source drive for copy
dstDrv	ds	1		;destination drive for copy

srcTrk	ds	1		;track source disk is on
dstTrk	ds	1		;track destination disk is on

maxBuf	ds	1		;number of tracks that can be buffered
fillCnt	ds	1		;number of tracks to buffer this time
trkCnt	ds	1		;counter of tracks while processing
remTrks	ds	1		;number of tracks remaining to process

rdRtry	ds	1		;read retry counter
wrRtry	ds	1		;write retry counter

	ds	64		;our stack
OURSTK	equ	$
trkBuf	equ	$

	end
