;    **************************************************
;    **************************************************
;    **************************************************

                              .LCTL



;                         ************
	.TITLE           "COPYDISK.COM"
;                         ************


;                        Version:    1.01

;                  First Written:    08/22/89

;                  Last Modified:    12/12/89



;	LABEL DATA:
                      VERSION	=       '1'
                      MODIF	=	'01'
                      CHANGE	=	' '

                      MONTH	=	'12'
                      DAY	=	'12'
                      YEAR	=	'89'



;                            ******
;                            SAMPLE
;                            DIALOG:
;                            ******



;	>



;                           *******
;                           HISTORY:
;                           *******



;	12/12/89:	Troubleshooting Console Direct
;			function, to see why takes 2
;			^C keystrokes to halt the
;			verification phase.  Apparent
;			throwaway of console status,
;			on first character into buffer!
;			A generic glitch in the BIOS.

;	11/12/89:	     1.01 Version
;                            ------------
;			Added a"BEEP" to cue each
;			disk change, to speed the
;			operation, and to eliminate
;			need for watching screen or
;			drive load indicators.

;			Improved screen formats
;			for appearance, and smoothed
;			out interactive dialog to
;			handle terminate/repeat query
;			with <CR>'s  95% of the time.

;	08/22/89:	1.00 Version written to
;			circumvent the problem of
;			backing up on a single-drive
;			system.



;    **************************************************
;    **************************************************



;                          ********
;                          ASSEMBLY
;                          CONTROLS:
;                          ********



			.PABS
			.PHEX

			.RADIX	16
			.LADDR



;                         **********
;                          SYMBOLIC
;                         REFERENCES:
;                         **********



;                        CP/M  SYSTEM:
;                        ----  ------


		PAGE.0		==	0000
		BIOS.JUMP	==	PAGE.0 + 0001
		IO.BYTE		==	PAGE.0 + 0003
		USER.DRIVE	==	PAGE.0 + 0004
		BDOS.JUMP	==	PAGE.0 + 0005
		SYSTEM		==	BDOS.JUMP
		DEF.FCB		==	PAGE.0 + 005C
		DEF.DISK.BUFF	==	PAGE.0 + 0080
		TPA		==	PAGE.0 + 0100


;                        CHARACTER  I/O:
;                        ---------  ---


		WARM.BOOT	==	0
		CONS.INPUT	==	1
		CONS.OUTPUT	==	2
		LIST.CHAR	==	5
		CONS.DIRECT	==	6
		STRING.PRINT	==	9
		  END.MESSAGE	==	0
		READ.BUFFER	==	0A
		CONS.STATUS	==	0B

		BREAK		==	''


;                            ASCII:
;                            -----


		NULL		==	00
		BEL		==	07
		BACKSPACE	==	08
		LF		==	0A
		CR		==	0D
		SPACE		==	20
		FORMFEED	==	0C
		RUBOUT		==	7F
		DEL		==	RUBOUT
		QUESTION.MARK	==	'?'
		HUH		==	QUESTION.MARK
		CNTRL.CHAR	==	'^'


;                           UTILITY:
;                           -------


		ZERO		==	0000
		OFF		=	ZERO
		ON		=	#ZERO
		CLEAR		=	OFF
		SET		=	ON
		OK		=	ZERO
		NOT.OK		=	#ZERO


;                            MAPPING:
;                            -------


		LOAD.POINT	==	TPA

		STACK.SIZE	==	20 ; words


;                           PROGRAM:
;                           -------


		HOME		==	0F218
		SELDSK		==	HOME + 3
		SETTRK		==	HOME + 6
		SETSEC		==	HOME + 9
		SETDMA		==	HOME + 0C
		READ		==	HOME + 0F
		WRITE		==	HOME + 12
		SECTRAN		==	HOME + 18
		


;    **************************************************
;    **************************************************



;                          *********
;                          PROCEDURE:
;                          *********



		       .LOC   LOAD.POINT


			     FIRST:
	JMP	START


;                            LABEL:
;                            -----


..NAME:
		.ASCII	'   CLONE.COM'
		.BYTE	END.MESSAGE

VERS.NUMBER:
		.ASCII	'Version:  '
		.BYTE	VERSION
		.BYTE	'.'
		.BYTE	MODIF / 100
		.BYTE	MODIF @ 100
		.BYTE	CHANGE
		.BYTE	END.MESSAGE

..DATE:
		.ASCII	'Date:  '
		.BYTE	MONTH / 100
		.BYTE	MONTH @ 100
		.BYTE	'/'
		.BYTE	DAY / 100
		.BYTE	DAY @ 100
		.BYTE	'/'
		.BYTE	YEAR / 100
		.BYTE	YEAR @ 100
		.BYTE	END.MESSAGE



START:
	LXI	SP, TOP.STACK

MAIN:
	LXI	D, HELLO
	CALL	FLASH
	LXI	D, VERS.NUMBER
	CALL	FLASH
	CALL	CRLF
	CALL	LFEED
	CALL	LFEED

	LXI	H, 2	     	; BEGIN COPYING ON
	SHLD	TRACK.NUMBER	; THE DATA TRACKS

	MVI	A, ZERO
	STA	TRACKS.COMPLETED

	MVI	A, 9
	STA	$REPEATS

	LXI	D, FIRST.CUE.SOURCE
	CALL	FLASH
	JMPR	ENTRY.LOOP

LOAD8.TRACKS:
	LXI	D, CUE.SOURCE.DISK
	CALL	FLASH

ENTRY.LOOP:
	CALL	WAIT.CUE

	MVI	A, 8
	STA	$TRACKS

	LXI	H, TRANSFER.BUFFER
	SHLD	BUFFER.POINTER

..LOOP:
	CALL	LOAD.1.TRACK

	LXI	H, $TRACKS
	DCR	M
	JRNZ	..LOOP

FLUSH8.TRACKS:
	LXI	D, CUE.OBJECT.DISK
	CALL	FLASH

	CALL	WAIT.CUE
	CALL	LFEED

	MVI	A, 8
	STA	$TRACKS

	LDA	TRACK.NUMBER
	SUI	8
	STA	TRACK.NUMBER

	LXI	H, TRANSFER.BUFFER
	SHLD	BUFFER.POINTER

..LOOP:
	CALL	FLUSH.1.TRACK

	LXI	H, $TRACKS
	DCR	M
	JRNZ	..LOOP

	LXI	H, TRACKS.COMPLETED
	MOV	A, M
	ADI	8
	MOV	M, A

	CALL	SPC5
	CALL	MARK.PROGRESS
	LXI	D, PROGRESS.MESSAGE
	CALL	FLASH

	LXI	H, $REPEATS
	DCR	M
	JRNZ	LOAD8.TRACKS

LOAD5.TRACKS:
	LXI	D, CUE.SOURCE.DISK
	CALL	FLASH

	CALL	WAIT.CUE

	MVI	A, 3
	STA	$TRACKS

	LXI	H, TRANSFER.BUFFER
	SHLD	BUFFER.POINTER

..LOOP:
	CALL	LOAD.1.TRACK

	LXI	H, $TRACKS
	DCR	M
	JRNZ	..LOOP

	LXI	H, 1
	SHLD	TRACK.NUMBER
	CALL	LOAD.1.TRACK

	CALL	LOAD.TRACK.0

FLUSH5.TRACKS:
	LXI	D, CUE.OBJECT.DISK
	CALL	FLASH

	CALL	WAIT.CUE
	CALL	LFEED

	MVI	A, 3
	STA	$TRACKS

	MVI	A, 4A
	STA	TRACK.NUMBER

	LXI	H, TRANSFER.BUFFER
	SHLD	BUFFER.POINTER

..LOOP:
	CALL	FLUSH.1.TRACK

	LXI	H, $TRACKS
	DCR	M
	JRNZ	..LOOP

	LXI	H, 1
	SHLD	TRACK.NUMBER
	CALL	FLUSH.1.TRACK

	CALL	FLSH.TRACK.0

COPYING.DONE:
	LXI	D, CHECKING.COPY
	CALL	FLASH

VERIFY.OBJECT.DISK:
	MVI	A, CLEAR
	STA	ERROR.FLAG

	LXI	H, 00
	SHLD	TRACKS.COMPLETED

	LXI	D, VERIF.MESSAGE
	CALL	FLASH
	CALL	MARK.PROGRESS
	CALL	SPC1

	LXI	H, TRANSFER.BUFFER
	SHLD	BUFFER.POINTER

	CALL	LOAD.TRACK.0

	LXI	H, 01
	SHLD	TRACK.NUMBER
	SHLD	TRACKS.COMPLETED

	MVI	A, 4C			; 76 D/D Tracks
	STA	$TRACKS

..LOOP:
	LXI	H, TRANSFER.BUFFER
	SHLD	BUFFER.POINTER

	CALL	LOAD.1.TRACK

	MVI	A, BACKSPACE
	CALL	PUTCHAR
	CALL	PUTCHAR
	CALL	PUTCHAR
	CALL	MARK.PROGRESS
	CALL	SPC1

	CALL	IF.KEY.GET
	CPI	CLEAR
	JRZ	..KEEP.GOING
	CPI	BREAK
	JRNZ	..KEEP.GOING

	LXI	D, ERAS.MESSAGE
	CALL	FLASH
	LXI	D, ABORT.MESSAGE
	CALL	FLASH
	CALL	MARK.PROGRESS
	CALL	CRLF
	CALL	LFEED

	LDA	ERROR.FLAG
	CPI	CLEAR
	JRZ	EXIT

	LXI	D, FAILED.VERIFY
	CALL	FLASH
	JMPR	EXIT

..KEEP.GOING:
	LXI	H, TRACKS.COMPLETED
	INR	M

	LXI	H, $TRACKS
	DCR	M
	JRNZ	..LOOP

	LXI	D, ERAS.MESSAGE
	CALL	FLASH

	LDA	ERROR.FLAG
	CPI	CLEAR
	JRZ	..OK

	LXI	D, FAILED.VERIFY
	CALL	FLASH
	JMPR	EXIT

..OK:
	LXI	D, PASSED.VERIFY
	CALL	FLASH

EXIT:
	LXI	D, REPEAT.ASK
	CALL	FLASH

..WAIT:
	CALL	GETCH
	CALL	TO.UPPER

	CPI	'Y'
	JRZ	..YES

	CPI	'N'
	JRZ	..NO
	CPI	CR
	JRZ	..NO
	CPI	BREAK
	JRZ	..NO

	JMPR	..WAIT

..YES:
	CALL	ECHO

	JMP	MAIN

..NO:
	MVI	A, 'N'
	CALL	PUTCHAR
	CALL	CRLF
	CALL	LFEED

	LXI	D,DISK.PROMPT
	CALL	FLASH

	CALL	WAIT.CUE

	CALL	LFEED
	LXI	D, GOODBYE
	CALL	FLASH

	JMP	WARM.BOOT

	

;    **************************************************
;    **************************************************



;                           ********
;                           MESSAGES:
;                           ********



HELLO:
	.BYTE	FORMFEED
	.ASCII	'          '
	.ASCII	'      SINGLE-DRIVE BACKUP UTILITY    '
	.BYTE	END.MESSAGE

FIRST.CUE.SOURCE:
	.ASCII	'     Insert Source Disk <CR>: '
	.BYTE	END.MESSAGE

CUE.SOURCE.DISK:
	.ASCII	'     Insert Source Disk <CR>: '
	.BYTE	BEL
	.BYTE	END.MESSAGE

CUE.OBJECT.DISK:
	.ASCII	'     Insert Object Disk <CR>: '
	.BYTE	BEL
	.BYTE	END.MESSAGE

PROGRESS.MESSAGE:
	.ASCII	'  DATA TRACKS COPIED'
	.BYTE	CR, LF, LF
	.BYTE	END.MESSAGE

CHECKING.COPY:
	.ASCII	'     75   DATA  TRACKS COPIED'
	.BYTE	CR, LF, LF
	.ASCII	'     2   SYSTEM TRACKS COPIED'
	.BYTE	CR, LF, LF
	.ASCII	'     ALL TRACKS COPIED'
	.BYTE	CR, LF, LF
	.ASCII	'     VERIFYING  .  .  .  '
	.BYTE	CR, LF, LF
	.BYTE	END.MESSAGE

VERIF.MESSAGE:
	.ASCII	'     TESTING TRACK '
	.BYTE	END.MESSAGE

ERAS.MESSAGE:
	.BYTE	CR
	.ASCII	'                     '
	.BYTE	CR
	.BYTE	END.MESSAGE

ABORT.MESSAGE:
	.ASCII	'     ABORTED AT TRACK  '
	.BYTE	END.MESSAGE

BUM.READ:
	.BYTE	CR, LF, LF
	.ASCII	'             *** READ '
	.BYTE	END.MESSAGE

BUM.WRITE:
	.BYTE	CR, LF, LF
	.ASCII	'             *** WRITE'

ERROR:
		.ASCII	' ERROR AT TRACK  '

	ERR.TRACK:
		.BLKB	2

		.ASCII	'  :  SECTOR  '

	ERR.SECTOR:
		.BLKB	2

		.ASCII	' ***'
		.BYTE	CR, LF
		.BYTE	END.MESSAGE

PASSED.VERIFY:
	.ASCII	'     ALL TRACKS VERIFIED'
	.BYTE	CR, LF, LF
	.BYTE	END.MESSAGE

FAILED.VERIFY:
	.ASCII	/                        *** CAN'T VERIFY THIS COPY! ***/
	.BYTE	CR, LF, LF
	.BYTE	BEL
	.BYTE	END.MESSAGE

REPEAT.ASK:
	.ASCII	'     More Backups?   (Yes or No):     '
	.BYTE	END.MESSAGE

DISK.PROMPT:
	.ASCII	'     Re-Insert The System Disk <CR>:  '
	.BYTE	END.MESSAGE

GOODBYE:
	.ASCII	'     EXIT TO CP/M'
	.BYTE	CR, LF, LF
	.BYTE	END.MESSAGE



;    **************************************************
;    **************************************************



;                         ***********
;                         SUBROUTINES:
;                         ***********



;                     CP/M  CHARACTER  I/O:
;                     ----  ---------  ---


IF.KEY.GET:

;		GET CHARACTER, IF AVAILABLE.
;		IF NOT, RETURN ZERO IN "A":

	CALL	REG.SAVE

Š	MVI	C, CONS.DIRECT
	MVI	E, SET
	CALL	SYSTEM
	CPI	ZERO

	CALL	REG.RESTORE

	RET

GETCH:
	CALL	REG.SAVE

GETC1:
	MVI	C, CONS.DIRECT
	MVI	E, SET
	CALL	SYSTEM
	CPI	ZERO
	JZ	GETC1

	CALL	REG.RESTORE

	RET

TO.UPPER:
	CPI	'a'
	RM
	CPI	'z' + 1
	RP
	SUI	20H

        RET

PUTCHAR:
ECHO:
	PUSH	PSW
	CALL	REG.SAVE

	MOV	E, A
	MVI	C, CONS.OUTPUT
	CALL	SYSTEM

	CALL	REG.RESTORE
	POP	PSW

	RET

FLASH:
	CALL 	REG.SAVE

..FL1:
	LDAX	D
	CPI	END.MESSAGE
	JRZ	..FL2

	CALL	PUTCHAR
	INX	D
	JMPR	..FL1

..FL2:
	CALL	REG.RESTORE

	RET

REG.SAVE:
	XTHL
	PUSH	D
	PUSH	B
	PUSH	H

	RET

REG.RESTORE:
	POP	H
	POP	B
	POP	D
	XTHL

	RET


;                  CONSOLE UTILITY:
;                  ------- -------


CRLF:
	MVI	A, CR
	CALL	PUTCHAR
	MVI	A, LF
	CALL	PUTCHAR

	RET

LFEED:
	MVI	A, LF
	CALL	PUTCHAR

	RET

BEEP:
	MVI	A, BEL
	CALL	PUTCHAR

	RET

SPC1:
	PUSH	PSW

	MVI	A, SPACE
	CALL	PUTCHAR

	POP	PSW

	RET

SPC5:
	MVI	B, 5
	CALL	SSPC

	RET

SSPC:
	MVI	A, SPACE
	CALL	PUTCHAR
	DCR	B
	JNZ	SSPC

	RET


;                           PROGRAM:
;                           -------


WAIT.CUE:
	CALL	GETCH
	CPI	BREAK
	JNZ	..CUE1

	CALL	LFEED
	CALL	LFEED

	JMP	WARM.BOOT

..CUE1:
	CPI	CR
	JRZ	..CUE2

	JMPR	WAIT.CUE

..CUE2:
	LXI	D, ..VISIBLE
	CALL	FLASH
	CALL	CRLF

	RET

..VISIBLE:
	.ASCII	'<CR>'
	.BYTE	END.MESSAGE

LOAD.1.TRACK:
	LBCD	TRACK.NUMBER
	CALL	SETTRK

	MVI	A, 1
	STA	SECTOR.NUMBER

LOOP.LOAD:
	LBCD	SECTOR.NUMBER
	CALL	SETSEC

	LBCD	BUFFER.POINTER
	CALL	SETDMA

;		*** READ DELAY ISOLATION TEST ***
;
;             Beeps on Sector #1 of first 7 tracks.
;            Drive 0 appears to be struggling to get
;                       Tracks 00 and 01:
;
;	LDA	$TRACKS
;	CPI	7
;	JM	..SKIP
;
;	LDA	SECTOR.NUMBER
;	CPI	1
;	JRNZ	..SKIP
;
;	CALL	BEEP
;
; ..SKIP:
;
;		*** END OF TEST ***

	CALL	READ

	CPI	OK
	JZ	..OK

	MVI	A, SET
	STA	ERROR.FLAG

	LDA	TRACK.NUMBER
	CALL	CONVERT
	MOV	A, B
	ORI	30
	STA	ERR.TRACK
	MOV	A, C
	ORI	30
	STA	ERR.TRACK + 1

	LDA	SECTOR.NUMBER
	CALL	CONVERT
	MOV	A, B
	ORI	30
	STA	ERR.SECTOR
	MOV	A, C
	ORI	30
	STA	ERR.SECTOR + 1

	LXI	D, BUM.READ
	CALL	FLASH
	LXI	D, ERROR
	CALL	FLASH
..OK:
	LHLD	BUFFER.POINTER
	LXI	D, 80
	DAD	D
	SHLD	BUFFER.POINTER

	LXI	H, SECTOR.NUMBER
	MOV	A, M
	INR	M

CODE.1.MARKER:
	CPI	34
	JNZ	LOOP.LOAD

	LXI	H, TRACK.NUMBER
	INR	M

	RET

FLUSH.1.TRACK:
	LBCD	TRACK.NUMBER
	CALL	SETTRK

	MVI	A, 1
	STA	SECTOR.NUMBER

LOOP.FLUSH:
	LBCD	SECTOR.NUMBER
	CALL	SETSEC

	LBCD	BUFFER.POINTER
	CALL	SETDMA

	CALL	WRITE

	CPI	OK
	JZ	..OK

	MVI	A, SET
	STA	ERROR.FLAG

	LDA	TRACK.NUMBER
	CALL	CONVERT
	MOV	A, B
	ORI	30
	STA	ERR.TRACK
	MOV	A, C
	ORI	30
	STA	ERR.TRACK + 1

	LDA	SECTOR.NUMBER
	CALL	CONVERT
	MOV	A, B
	ORI	30
	STA	ERR.SECTOR
	MOV	A, C
	ORI	30
	STA	ERR.SECTOR + 1

	LXI	D, BUM.WRITE
	CALL	FLASH
	LXI	D, ERROR
	CALL	FLASH

..OK:
	LHLD	BUFFER.POINTER
	LXI	D, 80
	DAD	D
	SHLD	BUFFER.POINTER

	LXI	H, SECTOR.NUMBER
	MOV	A, M
	INR	M

CODE.2.MARKER:
	CPI	34
	JNZ	LOOP.FLUSH

	LXI	H, TRACK.NUMBER
	INR	M

	RET

MARK.PROGRESS:
	LDA	TRACKS.COMPLETED
	CALL	CONVERT
	MOV	A, B
	ORI	30
	CALL	PUTCHAR
	MOV	A, C
	ORI	30
	CALL	PUTCHAR

	RET

CONVERT:

;	CONVERTS BYTE IN "ACC" ( < 100 )
;	TO TWO 16-BIT NYBBLES IN "BC":

	MVI	B, ZERO

..LOOP:
	SUI	0A
	JM	..STOP
	INR	B
	JMPR	..LOOP

..STOP:
	ADI	0A
	MOV	C, A
	MOV	A, B

	RET

LOAD.TRACK.0:

;		Y-e-e-s  .  .  .  Self-Modifying Code:

	LXI	H, 0
	SHLD	TRACK.NUMBER
	MVI	A, 1A			; 26 Sectors
	STA	CODE.1.MARKER + 1
	CALL	LOAD.1.TRACK
	MVI	A, 34			; Normally 52
	STA	CODE.1.MARKER + 1

	RET

FLSH.TRACK.0:
	LXI	H, 0
	SHLD	TRACK.NUMBER
	MVI	A, 1A
	STA	CODE.2.MARKER + 1
	CALL	FLUSH.1.TRACK
	MVI	A, 34
	STA	CODE.2.MARKER + 1

	RET



;    **************************************************
;    **************************************************



;                             ***
;                             RAM:
;                             ***



		  .LOC   ( . + 0F ) & 0FFF0


			RAM.VARIABLES:



TRACK.NUMBER:
		.WORD	0

SECTOR.NUMBER:
		.WORD	0

SKEW.TABLE.POINTER:
		.WORD	0	; SECTOR SKEW FACTOR

BUFFER.POINTER:
		.WORD	0

ERROR.FLAG:
		.BYTE	0

TRACKS.COMPLETED:
		.BYTE	0

$REPEATS:
		.BYTE	0

$TRACKS:
		.BYTE	0



;   ***************************************************
;   ***************************************************



;                            *****
;                            STACK:
;                            *****



		    .LOC  ( . + 0F ) & 0FFF0


		   .BLKW	STACK.SIZE


			   TOP.STACK:



;    **************************************************
;    **************************************************



;                          ********
;                          TRANSFER
;                           BUFFER:
;                          ********



	               TRANSFER.BUFFER:

;		     BDOS BEGINS AT E300



;    **************************************************
;    **************************************************



;                        ************
;                          ASSEMBLY
;                        TERMINATION:
;                        ************



			     LAST:


	      SIZE.PROGRAM   ==   LAST - FIRST



;    **************************************************
;    **************************************************
;    **************************************************



			.END	FIRST


