;********************************************************
;*							*
;*	 HARDWARE DEPENDANT DISK COPY PROGRAM		*
;*	 FOR MORROW DESIGNS DJ2D AND CP/M 2.X		*
;*							*
;********************************************************
;
;	By:	Bill Bolton
;		Software Tools
;		P.O. Box 63
;		Newport Beach
;		NSW, 2106
;		AUSTRALIA
;
;	COPYRIGHT (C) 1980,1981 SOFTWARE TOOLS
;
;	ALL COMMERCIAL RIGHTS RETAINED BY AUTHOR
;
;	History:
;			All versions prior to release 2 were
;			for North Star disk systems and
;			I'd rather forget all about them
;			so dont ask !
;
;	July 16, 1980	Version 2.0 developed as a universal
;			disk copy program for CP/M 2.X, but
;			it was too slow.
;
;	July 22,1980	Version 2.10 developed as much faster
;			faster hardware dependant version
;			for DJ2D & CP/M 2.2
;
;	July 25,1980	Version 2.11 error message bugs fixed
;			and conditional assembly for system
;			density added.
;
;	August 17,1980	Version 2.12 doublesided disk routines
;			bugs fixed
;
;	Feb 10, 1981	Version 2.13 pause before error reboot added
;
;	Mar 26, 1981	Version 2.14 correctly detects two sided disks
;			with Model A Disk Jockeys
;
;	May 22, 1981	Version 2.15 clobbers incorrect format determination
;			in some circumstances due to some "undocumented
;			feature" of the DJ2D or 1791, still don't know why it
;			happens in the first place but I found a way to 
;			stop it . Also ^C abort is now correctly detected
;			immediately. FDOS$SIZE values corrected.
;
	TITLE	'DJ2D Disk Copier Ver 2.15'
;
TRUE	EQU	0FFFFH
FALSE	EQU	NOT TRUE
SINGLE	EQU	FALSE		;Single density CP/M system with DJ2D 
MODELB	EQU	FALSE		;Model B DJ2D controller (for debugging)
;				 In practice this equate should be FALSE
;				 even for Model B DJ2Ds
HARD	EQU	FALSE		;Hard disk/floppy disk system, floppys must
;				 be drives A,B,C,D for this to work, otherwise
;				 you'll have to hack the code yourself
;
DJORG	EQU	0F800H		;DJ2D BASE ADDRESS
;
	IF	SINGLE OR MODELB
BASE	EQU	DJORG		;USE ROM FIRMWARE
	ELSE
BASE	EQU	DJORG+400H	;USE RAM IMAGE OF DJ FIRMWARE
	ENDIF
;
DJTRKSET EQU	BASE+0CH
DJSETSEC EQU	BASE+0FH
DJSETDMA EQU	BASE+12H
DJREAD	EQU	BASE+15H
DJWRITE	EQU	BASE+18H
DJSELDRV EQU	BASE+1BH
DJDMAST	EQU	BASE+24H
DJSTATUS EQU	BASE+27H
DJSETSID EQU	BASE+30H
WBOOT	EQU	00000H
BDOS	EQU	00005H
CNTRL$C	EQU	003H		;ASCII ETX (^C)
ACR	EQU	00DH		;ASCII CARRIAGE RETURN
VERSION	EQU	12		;BDOS VERSION NO. FUNCTION	
;
	IF	SINGLE AND NOT HARD
FDOS$SIZE EQU	01100H		;SIZE OF BDOS+BIOS
	ENDIF
	IF	NOT SINGLE AND NOT HARD
FDOS$SIZE EQU	01400H		;SIZE OF BDOS+BIOS
	ENDIF
	IF	HARD AND NOT SINGLE
FDOS$SIZE EQU	01700H		;SIZE OF BDOS+BIOS
	ENDIF
;
	ORG	0100H
;
	MACLIB	MACRO3		;SOFTWARE TOOLS SPECIAL MACROS
;
BEGIN:
	MVI	C,VERSION
	CALL	BDOS		;GET CP/M VERSION NO.
	MOV	A,L
	SUI	022H		;VERSION 2.2 OR LATER?
	JM	WRONG$VERSION	;NO
	LXI	H,BASE		;POINT TO ADDRESS OF FIRMWARE
	MOV	A,M		;GET FIRST BYTE OF FIRMWARE
	CPI	0C3H		;IS IT A 'JMP' INSTRUCTION
	JNZ	WRONG$VERSION	;NO
	LXI	SP,STACK$TOP	;YES, SET UP STACK
	IF	SINGLE
	PRINT	<CR,LF,'COPY - Ver 2.15 (1D)',CR,LF>
	ELSE
	PRINT	<CR,LF,'COPY - Ver 2.15 (2D)',CR,LF>
	ENDIF
	PRINT	<'Copyright (C) 1980/81 - Bill Bolton, Software Tools',CR,LF>
	PRINT	<'For Morrow Designs Disk Jockey 2D and CP/M 2.2',CR,LF,LF>
	PRINT	'BOTH Source and Destination MUST be the same format'
;
MENU:
	PRINT	<CR,LF,LF,'  *****  O P T I O N S  *****'>
	PRINT	<CR,LF,LF>
	PRINT	'  "A" = Copy ALL '
	DECOUT	TRACKS
	PRINT	<' tracks',CR,LF>
	PRINT	'  "M" = Copy all tracks UNTIL '
	PRINT	<'0E5H track',CR,LF>
	PRINT	<'  "O" = Copy ONLY CP/M system tracks',CR,LF>
	PRINT	<'  "F" = Copy FILES to end of disk',CR,LF>
	PRINT	<'  "N" = Copy files UNTIL 0E5H track',CR,LF>
	PRINT	'  "E" = EXIT to CP/M (FIRST insert system disk)'
	PRINT	<CR,LF,LF,'Enter your selection - '>
;
	CALL	GET$CONSOLE		;GET CONSOLE INPUT
	STA	COMMAND			;SAVE FOR LATER
	LXI	H,TRACKS		;GET NUMBER OF TRACKS
	MOV	H,M			;H <--- MAX NO. TRACKS
	MVI	L,0			;L <--- TRACK 0 TO START
	CPI	'A'
	JZ	SELECT$DRIVES
	CPI	'M'
	JZ	SELECT$DRIVES
	LXI	H,0200H			;H <--- TRACK 2
	CPI	'O'			;L <--- TRACK 0 TO START
	JZ	SELECT$DRIVES
	LXI	H,TRACKS
	MOV	H,M			;H <--- MAX NO. TRACKS
	MVI	L,2			;L <--- TRACK 2 TO START
	CPI	'F'
	JZ	SELECT$DRIVES
	CPI	'N'
	JZ	SELECT$DRIVES
	CPI	'E'
	JZ	WBOOT
	PRINT	<CR,LF,BEL,'Illegal selection - try again.'>
	JMP	MENU
;
SELECT$DRIVES:
	SHLD	START$TRACK	;L= TRACK TO START
				;H= LAST TRACK TO COPY
SOURCE$DRIVE:
	PRINT	<CR,LF,'Enter SOURCE drive (A, B, C or D)'>
	PRINT	<CR,LF,'    ( CR copies from A to B ) - '>
;
	CALL	GET$CONSOLE	;GET CONSOLE INPUT
	CPI	ACR
	JZ	DEFAULT
	ANI	05FH		;MAKE INTO UPPER CASE
	STA	SOURCE
	SUI	'A'		;SUBTRACT ASCII OFFSET
	CPI	4		;GREATER THAN 4?
	JNC	SOURCE$DRIVE	;YES, TRY AGAIN
DEST$DRIVE:
	PRINT	<CR,LF,'Enter DESTINATION drive (A, B, C or D) - '>
	CALL	GET$CONSOLE	;GET CONSOLE INPUT
	STA	DEST
	SUI	'A'		;REMOVE ASCII OFFSET
	CPI	4
	JNC	DEST$DRIVE
COMMENCE:
	LXI	SP,STACK$TOP	;RESET STACK
	PRINT	<CR,LF,'Insert SOURCE in '>
	CHAROUT	SOURCE
	PRINT	<', DEST in '>
	CHAROUT DEST
	PRINT	<' and press RETURN to copy.',CR,LF>
	PRINT	<'   (Press any other key to reset options) - '>
	CALL	GET$CONSOLE	;GET CONSOLE INPUT
	CPI	ACR		;IS IT A 'RETURN'
	JNZ	MENU		;NO, DISPLAY THE MENU
	LDA	START$TRACK
	STA	CURRENT$TRACK
	DISKIO	INITIAL		;RESET THE DISK SYSTEM
	LDA	SOURCE		;GET SOURCE DRIVE NO.
	SUI	'A'		;SUBTRACT ASCII OFFSET
	MVI	B,0
	MOV	C,A		;BC  <--- DRIVE NO.
	CALL	DJSELDRV
	MVI	C,1
	CALL	DJTRKSET
	MVI	C,1		;SET UP SECTOR FOR READ
	CALL	DJSETSEC
	LXI	B,STACK$TOP+1	;GET FIRST FREE MEMORY LOC.
	CALL	DJSETDMA
	CALL	DJREAD		;FIRST TIME CLEARS ?????? ON DJ2D
	CALL	DJSTATUS	;DITTO	
	CALL	DJREAD		;THIS TIME ITS FOR REAL
STATUS1:
	CALL	DJSTATUS	;GET DISK DATA
	ANI	0FCH		;DISCARD DRIVE NO. BITS
	MOV	C,A		;C <--- STATUS
	LDA	DJORG+3FAH	;GET DJ2D 1791 STATUS
	ANI	8		;MASK OUT SIDE BIT
	XRI	8		;COMPLEMENT IT
	RLC			;INTO BIT 4
	RLC			;INTO BIT 5
	ORA	C		;MERGE WITH PREVIOUS STATUS
	PUSH	PSW		;TEMP SAVE SOURCE DISK DATA (TO BE POPPED AS B)
	LDA	DEST		;GET DESTINATION DRIVE NO.
	SUI	'A'		;REMOVE ASCII OFFSET	
	MOV	C,A
	CALL DJSELDRV
	MVI	C,1		;SET UP TRACK FOR READ
	CALL	DJTRKSET
	MVI	C,1		;SET UP SECTOR FOR READ
	CALL	DJSETSEC
	CALL	DJREAD		;FIRST TIME CLEARS ?????? ON DJ2D
	CALL	DJSTATUS	;DITTO	
	CALL	DJREAD		;THIS TIME ITS FOR REAL
STATUS2:
	CALL	DJSTATUS	;GET DISK DATA
	ANI	0FCH		;DISCARD DRIVE NO. BITS
	MOV	C,A		;C <--- STATUS
	LDA	DJORG+3FAH	;GET DJ2D 1791 STATUS
	ANI	8		;MASK OUT SIDE BIT
	XRI	8		;COMPLEMENT IT
	RLC			;INTO BIT 4
	RLC			;INTO BIT 5
	ORA	C		;MERGE WITH PREVIOUS STATUS
	POP	B		;RESTORE SOURCE DISK DATA (PUSHED AS PSW)
STATUS$CHECK:
	CMP	B		;DISK DATA SAME FOR BOTH DRIVES?
	JNZ	FORMAT$ERROR	;NO, ERROR
	ANI	00CH		;MASK OUT SECTOR SIZE BITS
	STA	SEC$BITS
	MOV	A,B		;A <--- DISK DATA
	ANI	020H		;MASK OUT SIDE BIT
	CNZ	MERGE		;MERGE SIDE BITS WITH SECTOR BITS
	LDA	SEC$BITS
	LXI	H,SECTOR$LENGTH$TB
	MVI	D,0		;FORM OFFSET
	MOV	E,A
	DAD	D		;ADD OFFSET
	MOV	E,M		;GET LOW BYTE OF SEC$LENGTH
	INX	H
	MOV	D,M		;GET HIGH BYTE OF SEC$LENGTH
	INX	H
	MOV	A,M		;GET LOW BYTE OF SPT
	INX	H
	MOV	H,M		;GET HIGH BYTE OF SPT
	MOV	L,A		;FORM VALUE
	SHLD	SPT		;SAVE SECTORS/TRACK
	SDED	SEC$LENGTH	;SAVE SECTOR LENGTH
	LXI	H,0
SIZE$LOOP:
	DAD	D		;ADD 1 SECTOR LENGTH
	DCR	A		;ADJUST SECTOR COUNT
	JNZ	SIZE$LOOP
	SHLD	BUFFER$SIZE
	XCHG			;DE <--- BUFFER SIZE
	LXI	H,STACK$TOP+1	;GET FIRST FREE MEMORY LOC.
	SHLD	TRACK$BUFFER
	DAD	D		;FORM ADDRESS OF VERIFY BUFFER
	SHLD	VERIFY$BUFFER	
	DAD	D		;FORM ADDRESS OF TOP OF BUFFER
	SHLD	BUFFER$TOP
	XCHG			;DE <--- BUFFER TOP
	LHLD	BDOS+1		;GET BDOS BASE ADDRESS
	DSUB			;ENOUGH MEMORY FOR BUFFERS?
	JC	OUT$OF$MEMORY	;NO, ERROR
	LDA	SEC$BITS	;YES, GET DISK DATA
	ANI	010H		;MASK OUT DOUBLE SIDED BIT
	JZ	SIDE$FLAG
	LDA	SPT		;GET SECTORS/TRACK
	RRC			;DIVIDE BY 2
	PUSH	PSW
	ORI	080H		;SET SIDE 1 BIT
	STA	SPT		;SAVE NEW LAST SECTOR NO.
	POP	PSW
	INR	A

SIDE$FLAG:
	STA	SIDE$ONE	;SIDE CHANGE SECTOR NO.
	PRINT	<CR,LF,LF,'Copy in progress - '>
	PRINT	<'Control C to abort',CR,LF>
	CALL	COPY		;DO THE COPY
	PRINT	<CR,LF,'Copy complete - '>
	LDA	ERRORS		;CHECK FOR ERRORS
	ORA	A
	JNZ	ERROR$MESSG
	PRINT	<'NO errors were detected.'>
	JMP	COPY$AGAIN
;
MERGE:
	RRC			;ROTATE SIDE BIT INTO POSITION
	MOV	B,A		;B <--- SIDE BIT
	LDA	SEC$BITS	;GET SECTOR BITS
	ORA	B		;MERGE THEM
	STA	SEC$BITS	;KEEP STACK RIGHT
	RET
;
ERROR$MESSG:
	PRINT	<'Errors detected. *** ERRORS ***',BEL>
;
COPY$AGAIN:
	PRINT	<CR,LF,LF,'Press RETURN to copy again or'>
	PRINT	<CR,LF,'any other key to reset options. - '>
	CALL	GET$CONSOLE	;GET CONSOLE INPUT
	CPI	ACR
	JZ	COMMENCE
	JMP	MENU
;
FORMAT$ERROR:
	PRINT	<CR,LF,LF,BEL>
	PRINT	'Destination disk is NOT the same'
	PRINT	<' format as Source disk.',CR,LF>
	PRINT	'BOTH disks MUST be the same format.'
	PRINT	<CR,LF,LF,'Press any key to reset options. - '>
	CALL	GET$CONSOLE
	JMP	MENU
;
DEFAULT:
	MVI	A,'A'		;DEFAULT TO A DRIVE
	STA	SOURCE
	INR	A		;  AND B DRIVE
	STA	DEST
	JMP	COMMENCE
;
COPY:
	XRA	A		;RESET ERROR FLAG
	STA	ERRORS
	CALL	FIRST$TIME	;INITIALISE TRACK PARAMETERS
COPY$LOOP:
	CALL	ABORT$CHECK	;WANT TO QUIT?
	LDA	SOURCE		;GET SOURCE DRIVE NO.
	SUI	'A'		;REMOVE ASCII OFFSET
	CALL	SETUP$TRACK	;SET UP INITIAL PARAMETERS
	LHLD	TRACK$BUFFER
	CALL	READ$A$TRACK
	CALL	CHECK$EMPTY	;CHECK IF E5 TRACK
	RC			;CARRY SET IF E5 TRACK
	CALL	ABORT$CHECK	;CHECK FOR QUIT BEFORE BDOS SWALLOWS ANY
				; WAITING CHARACTER DURING CONSOLE OUTPUT
	PRINT	'*'		;DISPLAY TRACK DONE MARKER
	MVI	A,5		;NUMBER OF RETRIES
	STA	RETRIES
TRY$WRITE:
	LXI	H,RETRIES	;GET REMAINING RETRIES
	DCR	M		;ADJUST IT
	JM	LAST$TRY
	LDA	DEST		;GET DESTINATION DRIVE NO.
	SUI	'A'		;REMOVE ASCII OFFSET
	CALL	SETUP$TRACK
	LHLD	TRACK$BUFFER
	CALL	WRITE$A$TRACK
	LDA	DEST		;GET DESTINATION DRIVE IDENT
	SUI	'A'		;REMOVE ASCII OFFSET
	CALL	SETUP$TRACK
	LHLD	VERIFY$BUFFER
	CALL	READ$A$TRACK
	CALL	VERIFY
	JC	TRY$WRITE	;CARRY SET MEANS RETRY
LAST$TRY:
	LXI	H,CURRENT$TRACK
	INR	M		;ADJUST IT
	LDA	LAST$TRACK
	CMP	M		;LAST TRACK?
	JNZ	COPY$LOOP	;NO, COPY ANOTHER
	RET
;
READ$A$TRACK:
	LXI	B,1		;SET START SECTOR NO.
READ$TRACK:
	PUSH	B		;SAVE START SECTOR NO.
	PUSH	H		;SAVE CURRENT DMA ADDRESS
	MVI	A,1FH
	ANA	C		;MASK OF HIGH BITS
	MOV	C,A		;C <--- MASKED SECTOR
	CALL	DJSETSEC	;SET THE SECTOR
	POP	B		;BC <--- DMA ADDRESS
	PUSH	B		;SAVE IT AGAIN
	CALL	DJSETDMA
	MVI	B,5		;RETRY COUNT
READ$RETRY:
	DCR	B
	JM	READ$PARAM	;FINISHED RETRIES?
	PUSH	B		;NO, SAVE COUNT
	CALL	DJREAD		;READ A SECTOR
	POP	B		;RESTORE RETRY COUNT
	JC	READ$RETRY	;READ ERROR, TRY AGAIN
READ$PARAM:
	POP	H		;GET CURENT DMA ADDRESS
	LDED	SEC$SIZE	;GET SECTOR LENGTH
	DAD	D		;FORM NEW DMA ADDRESS
	POP	B		;GET SECTOR NO. 
	CNZ	READ$ERROR	;READ ERROR MESSAGE
	INX	B		;ADJUST SECTOR NO.
	LDA	SECTORS		;GET MAX NO SECTORS
	INR	A
	CMP	C		;DONE LAST SECTOR
	RZ			;YES
	LDA	OTHER$SIDE
	CMP	C		;TIME TO CHANGE SIDES?
	JNZ	READ$TRACK	;NO, DO ANOTHER SECTOR
	MVI	C,1
	CALL	DJSETSID	;SELECT SIDE 1
	LXI	B,081H		;SET SECTOR 1 ON SIDE 1
	JMP	READ$TRACK
;
CHECK$EMPTY:
	LDA	COMMAND		;GET CURRENT COMMAND
	CPI	'N'		;FILES UNTIL E5 TRACK?
	JZ	SETUP$E5	;YES, TEST FOR EMPTY TRACK
	CPI	'M'		;ALL UNTIL E5 TRACK?
	JNZ	RESET$CARRY	;NO,FORCE COPY TILL END
SETUP$E5:
	LDA	CURRENT$TRACK	;TRACK NUMBER
	CPI	2		;DON'T TEST TRACKS 0 & 1
	JC	RESET$CARRY	;RESET CARRY FLAG
	LHLD	BUFFER$SIZE
	PUSH	H
	POP	B		;BC <--- NO BYTES TO CHECK
	LHLD	TRACK$BUFFER
TEST$E5:
	MOV	A,M		;GET A BYTE FROM TRACK BUFFER
	CPI	0E5H		;DATA FILLER?
	JNZ	RESET$CARRY	;RESET FLAG
	INX	H		;POINT TO NEXT BYTE
	DCX	B		;ADJUST CONTROL COUNTER
	MOV	A,B		;FINISHED LOOP YET?
	ORA	C
	JNZ	TEST$E5		;NO, DO IT AGAIN
	STC			;SET CARRY AS A FLAG
	RET
;
RESET$CARRY:
	STC			;DEFINE CARRY STATUS
	CMC			;COMPLEMENT IT
	RET
;
WRITE$A$TRACK:
	LXI	B,1		;SET UP SECTOR NO.
WRITE$TRACK:
	PUSH	B		;SAVE SECTOR NO.
	PUSH	H		;SAVE DMA ADDRESS
	MVI	A,1FH		;1AH IS MAX NO. SECTORS ON ANY FORMAT
	ANA	C		;MASK OF HIGH BITS
	MOV	C,A		;C <--- MASKED SECTOR
	CALL	DJSETSEC	;NEXT SECTOR TO WRITE
	POP	B		;BC <--- DMA ADDRESS
	PUSH	B		;SAVE IT AGAIN
	CALL	DJSETDMA	;NEXT ADDRESS TO WRITE FROM
	MVI	B,5		;RETRY COUNTER
WRITE$RETRY:
	DCR	B		;ADJUST RETRY COUNT
	JM	WRITE$PARAM	;FINISHED RETRIES?
	PUSH	B		;SAVE RETRY COUNT
	CALL	DJWRITE		;WRITE THE SECTOR
	POP	B		;RESTORE RETRY COUNT
	JC	WRITE$RETRY	;YES, TRY AGAIN
WRITE$PARAM:
	POP	H		;GET CURRENT DMA ADDRESS
	LDED	SEC$SIZE	;GET SECTOR LENGTH
	DAD	D		;FORM NEW DMA ADDRESS
	POP	B		;GET LAST SECTOR DONE NO.
	CNZ	WRITE$ERROR	;MESSAGE IF WRITE ERROR
	INX	B		;ADJUST SECTOR NO.
	LDA	SECTORS		;GET MAX NO. SECTORS
	INR	A
	CMP	C		;DONE LAST SECTOR?
	RZ			;YES
	LDA	OTHER$SIDE
	CMP	C		;TIME TO CHANGE SIDES?
	JNZ	WRITE$TRACK	;NO, DO ANOTHER SECTOR
	MVI	C,1
	CALL	DJSETSID	;SELECT SIDE 1
	LXI	B,081H		;SET SECTOR 1 ON SIDE 1
	JMP	WRITE$TRACK
;
SETUP$TRACK:
	STA	DRIVE$NO	;SAVE DRIVE NO.
	MOV	C,A
	CALL	DJSELDRV	;SELECT THE DRIVE
	LDA	CURRENT$TRACK
	MOV	C,A
	CALL	DJTRKSET	;SET THE TRACK
	LXI	B,0
	CALL	DJSETSID	;SET THE SIDE
	LDA	CURRENT$TRACK
	ORA	A		;TRACK 0?
	JZ	TRACK$0
	CPI	1		;TRACK 1?
	RNZ			;NO
				;YES, RESET PARAMETERS
FIRST$TIME:
	LHLD	SPT		;SECTORS/TRACK
	SHLD	SECTORS
	LHLD	SEC$LENGTH	;BYTES/SECTOR
	SHLD	SEC$SIZE
	LDA	SIDE$ONE	;SECTOR NO. TO CHANGE SIDES
	STA	OTHER$SIDE
	RET
;
TRACK$0:
	LXI	H,26		;26 SECTORS/TRACK
	SHLD	SECTORS
	LXI	H,128		;128 BYTES/SECTOR
	SHLD	SEC$SIZE
	MVI	A,0		;FORCE SINGLE SIDED
	STA	OTHER$SIDE
	RET
;
VERIFY:
	XRA	A
	STA	ESECTOR		;RESET ERROR SECTOR
	LHLD	TRACK$BUFFER	;POINT TO BASE OF DATA WRITTEN
	LDED	VERIFY$BUFFER	;POINT TO BASE OF DATA READ
	LDA	CURRENT$TRACK
	ORA	A		;TRACK 0?
	LXI	B,26*128	;TRACK 0 PARAMETERS ARE FIXED
	JZ	SINGLE2		;YES
	PUSH	H
	LHLD	BUFFER$SIZE
	PUSH	H
	POP	B		;BC <--- NO. OF BYTES TO CHECK:
	POP	H
SINGLE2:
	LDAX	D		;GET A WRITTEN BYTE 
	CMP	M		;GET A READ BYTE
	JZ	NEXT$COMPARE	;IF THE SAME GET NEXT PAIR
	LDA	RETRIES		;GET NO RETRIES LEFT
	ORA	A		;ANY LEFT?
	STC			;SET CARRY AS RETRY FLAG
	RNZ			;YES, RETRY TRACK
	CALL	COMPARE$ERROR	;NO, HARD ERROR
NEXT$COMPARE:
	INX	H		;POINT TO NEXT WRITTEN BYTE
	INX	D		;POINT TO NEXT READ BYTE
	DCX	B		;ADJUST NUMBER DONE COUNT
	MOV	A,C		;FINISHED?
	ORA	B
	JNZ	SINGLE2		;NO, COMPARE BYTES
	RET
;
COMPARE$ERROR:
	PUSH	H		;SAVE WRITTEN DATA LOC.
	PUSH	D		;SAVE READ DATA LOC.
	MVI	A,0
	STA	SECTOR$COUNT
SECTOR$LOOP:
	LDED	SEC$SIZE	;NO. BYTES IN A SECTOR
	DSUB			;SUBTRACT SECTOR FROM ERROR LOC.
	LDA	SECTOR$COUNT
	INR	A		;ADJUST SECTOR COUNT
	STA	SECTOR$COUNT
	LDED	TRACK$BUFFER
	CPHL			;COMPARE TO BASE OF BUFFER
	JNC	SECTOR$LOOP	;FINISHED WHEN GONE MINUS
	LDA	SECTOR$COUNT
	MOV	C,A		;BC <--- SECTOR NO.
	MVI	B,0
	PUSH	B		;SAVE ERROR SECTOR
	PRINT	<CR,LF,'Compare'>
	JMP	ERROR$END
;
READ$ERROR:
	SAVE	H,D,B
	PRINT	<CR,LF,'Read'>
	JMP	ERROR$END
;
WRITE$ERROR:
	SAVE	H,D,B
	PRINT	<CR,LF,'Write'>
;
ERROR$END:
	PRINT	' error at Track '
	DECOUT	CURRENT$TRACK
	PRINT	', Physical Sector '
	POP	H		;HL <--- SECTOR NO.
	PUSH	H		;SAVE IT AGAIN
	MOV	A,L		;A <--- SECTOR NO.
	ANI	080H		;MASK OUT DOUBLE SIDED BIT
	JZ	ONE$SIDE
	LDA	SIDE$ONE	;GET SECTORS ON (SIDE 0) + 1
	DCR	A		;NO. SECTORS ON SIDE ZERO
	ADD	L		;A <--- NO. OF BAD SECTOR
	ANI	07FH		;MASK OFF BIT 7
	MOV	L,A		;HL <--- NO BAD SECTOR
ONE$SIDE:
	DECOUT
	PRINT	', on Drive '
	LDA	DRIVE$NO
	ADI	'A'
	CHAROUT
	PRINT	<BEL,' '>
	MVI	A,0FFH		;SET ERROR FLAG
	STA	ERRORS
	CALL	ABORT$CHECK
NO$ERROR:
	RESTORE	B,D,H
	RET
;
GET$CONSOLE:
	CHARIN			;GET A CHARACTER
	CPI	'B'		;MAKE INTO UPPER CASE
	RC
	CPI	'{'
	RNC
	ANI	'_'
	RET
;
ABORT$CHECK:
	CALLBIOS DSTAT		;CHECK IF KEY PRESSED
	ORA	A
	RZ
	CALLBIOS DCONIN
	CPI	CNTRL$C
	RNZ
	PRINT	<CR,LF,BEL,'^C ABORT'>
	CHARSTAT		;SEE IF ^C GOT INTO BDOS
	JZ	MENU		;NO
	CHARIN			;YES, DISCARD IT
	JMP	MENU
;
	
OUT$OF$MEMORY:
	PRINT	<BEL,CR,LF,LF,'               TRACK BUFFER OVERFLOW',CR,LF,LF>
	PRINT	<'Your present CP/M system does not have enough memory'>
	PRINT	<CR,LF,'in the Transient Program Area for copying disks'>
	PRINT	<CR,LF,'in the format you are trying to copy.'>
	PRINT	<BEL,CR,LF,LF,'You need at least a '>
	LHLD	BUFFER$TOP	;GET SIZE OF BUFFERS
	LXI	D,FDOS$SIZE	;GET SIZE OF BDOS+BIOS
	DAD	D		;MINIMUM SYSTEM SIZE IN BYTES
	MOV	A,H		;GET PAGE NO.
	RRC			;DIVIDE BY 4 TO GET KBYTES
	RRC
	ANI	03FH
	INR	A		;ADD AN EXTRA KBYTE
	MVI	H,0
	MOV	L,A
	DECOUT			;DISPLAY THE SYSTEM SIZE
	PRINT	<'K system to copy this disk.'>
	PRINT	<CR,LF,LF,'Insert a disk which has DJ2D CP/M system on it'>
	PRINT	<CR,LF,'into the A: drive, then press any key to exit to CP/M'>
WAITLP:
	DIRIN			;SET UP FOR DIRECT CONSOLE READ
	ORA	A		;ANY KEY PRESSED?
	JZ	WAITLP		;NO, WAIT UNTIL ONE IS PRESSED	
	PRINT	<CR,LF,LF,'Warm booting CP/M'>
	JMP	WBOOT
;
WRONG$VERSION:
	PRINT	<'Sorry, you need CP/M Version 2.2 or later',CR,LF>
	PRINT	<'and a Morrow Designs Disk Jockey 2D Controller'>
	PRINT	<CR,LF,'to run this disk copy utility.'>
	RET			;BACK TO CP/M
;
SECTOR$LENGTH$TB:
	DW	128		;1S/1D/128
	DW	26
	DW	256		;1S/2D/256
	DW	26
	DW	512		;1S/2D/512
	DW	15
	DW	1024		;1S/2D/1024
	DW	8
	DW	128		;2S/1D/128
	DW	52
	DW	256		;2S/2D/256
	DW	52
	DW	512		;2S/2D/512
	DW	30
	DW	1024		;2S/2D/1024
	DW	16
;
CURRENT$TRACK:
	DW	0
START$TRACK:
	DB	0
LAST$TRACK:
	DB	0
ESECTOR:
	DB	0
SECTOR$COUNT:
	DB	0
ERRORS:	DB	0
RETRIES:
	DB	0
DRIVE$NO:
	DB	0
COMMAND:
	DB	0
SIDE$ONE:
	DB	0		;1ST SECTOR ON SIDE ONE + 1
OTHER$SIDE:
	DB	0		;SECTOR NO. TO CHANGE SIDES
SEC$BITS:
	DB	0		;SECTOR SIZE BITS
SEC$LENGTH:
	DW	00		;FORMATED BYTES/SECTOR
SEC$SIZE:
	DW	00		;SEC$LENGTH
TRACKS:	DW	77		;NO TRACKS PER DISK
SOURCE:	DW	00
DEST:	DW	00
BADSEC:	DW	00
SPT:	DW	00		;FORMATTED SECTORS/TRACK
SECTORS:
	DW	00		;SECTORS/TRACK
BUFFER$SIZE:
	DW	00
TRACK$BUFFER:
	DW	00		;LOCATION OF TRACK BUFFER
VERIFY$BUFFER:
	DW	00		;LOCATION OF VERIFY BUFFER
BUFFER$TOP:
	DW	00		;LOCATION OF TOP OF BUFFERS
;
; AN INITIALISED STACK MAKES DEBUGGING EASIER
;
	DB	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
	DB	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
	DB	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
	DB	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 
;
STACK$TOP	EQU	$
;
	END	BEGIN

