;	COPY - CP/M SINGLE DISK COPY UTILITY
;
	ORG	0100H
	JMP	COPY
	DB	'(C) 1979 - N D H HAMMOND'
PRGNAME	DB	'COPY VSN 2.1 OF 3JAN80',0DH,0AH,'$'
;
;
	;SYSTEM ADDRESSES
WBOOTE	EQU	0000H	;REBOOT ADDRESS
SFCB	EQU	005CH	;SOURCE FILE CONTROL BLOCK
SFCBEX	EQU	SFCB+12	;CURRENT FILE EXTENT NO
SFCBCR	EQU	SFCB+32	;NEXT RECORD COUNTER
TBUF	EQU	0080H	;SYSTEM DEFAULT BUFFER
	;CONSTANTS
CCPL	EQU	(3200H-2900H) SHR 8	;PAGES IN CCP
TRUE	EQU	0FFH
ETX	EQU	03H
LF	EQU	0AH
CR	EQU	0DH
EOM	EQU	'$'
;
;	MAIN PROGRAM
;
COPY	LXI	H,0	;SAVE OLD STACK POINTER
	DAD	SP
	SHLD	OLDSP
	LXI	SP,STACK
	LXI	D,PRGNAME
	CALL	WRITESTRING
	CALL	SETMODE
COPYLP	CALL	GETARGS
	CALL	INITVAR
	CALL	SOURCEDISK
	CALL	OPENSOURCE
	CALL	LOADBUF
	CALL	DESTDISK
	CALL	MAKETEMP
REPEAT	CALL	DUMPBUF
	LDA	FINISH
	CPI	TRUE
	JZ	ENDCOPY
	CALL	SOURCEDISK
	CALL	LOADBUF
	CALL	DESTDISK
	JMP	REPEAT
ENDCOPY	CALL	TERMINATE
	LDA	MULTI
	CPI	TRUE
	JNZ	EXIT
	CALL	PROMPT
	JMP	COPYLP
EXIT	LHLD	OLDSP
	SPHL
	RET		;RETURN TO CP/M
;
;	SUBROUTINBS
;
SETMODE		;SET SINGLE/MULTIPLE COPY MODE
	XRA	A
	STA	MULTI
	LDA	TBUF	;WAS A FILE NAME SPECIFIED?
	CPI	0
	JNZ	SM1
	;NO - SET MULTIPLE MODE
	MVI	A,TRUE
	STA	MULTI
	CALL	PROMPT
	RET
SM1	;YES - SINGLE MODE, MOVE COMMAND LINE TO RBUF
	LXI	H,TBUF
	LXI	D,RBUF+1
	MVI	B,30
	CALL	COPYSTRING
	;LIMIT TO 29 CHARACTERS
	LDA	RBUF+1
	CPI	30
	RM
	MVI	A,29
	STA	RBUF+1
	RET
;
;
INITVAR		;INITIALIZE VARIABLES
	XRA	A
	STA	FINISH
	;SET EXTENT TO CP/M CBASE
	LHLD	ENTRY+1	;HL:=FBASE
	MOV	A,H
	SBI	CCPL
	MOV	H,A	;HL:=CBASE
	MOV	A,L	;ENSURE EXTENT IS AT
	ANI	80H	;  A PAGE BOUNDARY
	MOV	L,A
	SHLD	EXTENT
	;ZERO EXTENT BIT IN SOURCE FCB
	XRA	A
	STA	SFCBEX
	RET
;
;
GETARGS		;GET FILE NAMES FROM BUFFER
	LXI	H,RBUF+1	;HL:=^BUFFER LGTH
	MOV	A,M		;A:=BUFFER LGTH
	CPI	0		;CHECK FOR EMPTY
	JZ	EXIT
	INX	H		;HL:=^BUFFER
	;SET <ETX> SENTINEL AT END OF BUFFER
	PUSH	H
	MOV	C,A
	MVI	B,0
	DAD	B
	MVI	M,ETX
	;GET SOURCE FILE NAME
	POP	H
	CALL	DEBLANK
	CPI	ETX
	JZ	ARGERR
	LXI	D,SFCB+1
	CALL	GETFILE
	JC	ARGERR
	;GET DEST FILE NAME
	CALL	DEBLANK
	CPI	ETX
	JZ	NODEST
	LXI	D,DFCB+1
	CALL	GETFILE
	JC	ARGERR
	CALL	DEBLANK
	CPI	ETX
	JNZ	ARGERR
	RET
NODEST	;SET DEST FILE = SOURCE FILE NAME
	LXI	D,DFCB+1
	LXI	H,SFCB+1
	MVI	B,11
	CALL	COPYSTRING
	RET
ARGERR	;REPORT ERROR AND RETRY
	LXI	D,AEMSG
	CALL	WRITESTRING
	CALL	PROMPT
	JMP	GETARGS
;
;
DEBLANK		;SKIP BLANKS IN RBUF
	;ENTRY:	HL=^POSITION IN RBUF
	;EXIT:	HL=^FIRST NONBLANK
	;	A=HL^
	MOV	A,M
	CPI	' '
	RNZ
	INX	H
	JMP	DEBLANK
;
;
GETFILE		;GET FILE NAME FROM RBUF
	;ENTRY:	HL=^POSITION IN RBUF
	;	DE=^FCB FOR FILE
	;EXIT:	HL,DE=^UPDATED POSITION
	;	A=HL^ (<SPACE> OR <CONTROL CHAR>)
	;	CYF=ERROR FLAG
	MVI	C,8
	CALL	GETNAME
	RC
	CPI	'.'
	JNZ	NOEXT
	INX	H
	MVI	C,3
	CALL	GETNAME
	RET
	;FILL FILE TYPE FIELD WITH BLANKS
NOEXT	MVI	A,' '
	STAX	D
	INX	D
	STAX	D
	INX	D
	STAX	D
	MOV	A,M
	ORA	A	;CLEAR CYF
	RET
;
;
GETNAME		;GET NAME FROM RBUF
	;ENTRY:	HL=^POSITION IN RBUF
	;	DE=^FCB POSITION
	;	C=MAX NO OF CHARACTERS
	;EXIT:	HL,DE=^UPDATED POSITION
	;	A=DELIMITER (HL^)
	;	CYF=ERROR FLAG
	MVI	B,0
	INR	C
	;TRANSFER NAME TO FCB
NXTCH	MOV	A,M
	CPI	' '+1
	JM	CKBLK
	CPI	'.'
	JZ	CKBLK
	CALL	UPCASE
	STAX	D
	INX	H
	INR	B
	INX	D
	MOV	A,B
	CMP	C
	JNZ	NXTCH
	STC		;TOO MANY CHARS
	RET
	;BLANK FILL IF REQD
CKBLK	DCR	C
BLKFL	MOV	A,B
	CMP	C
	JZ	GOTNM
	MVI	A,' '
	STAX	D
	INR	B
	INX	D
	JMP	BLKFL
	;EXIT
GOTNM	MOV	A,M
	ORA	A	;CLEAR CYF
	RET
;
;
UPCASE		;CONVERT ACC TO UPPER CASE
	CPI	61H	;'a'
	RM
	CPI	7BH	;'z'+1
	RP
	ANI	5FH
	RET
;
;
OPENSOURCE	;OPEN SOURCE FILE
	LXI	D,SFCB
	CALL	OPENFILE
	CPI	0FFH
	JNZ	OPEN$OK
	MVI	B,1
	JMP	ERROR
OPEN$OK	XRA	A
	STA	SFCBCR
	RET
;
;
LOADBUF		;LOAD BUFFER FROM SOURCE FILE
	LXI	D,BUFFER
RDNXT	PUSH	D
	CALL	SETDMA
	LXI	D,SFCB
	CALL	READREC
	POP	D
	ORA	A
	JZ	RD$OK
	CPI	1
	JZ	EOF
	MVI	B,3
	JMP	ERROR
RD$OK	LXI	B,80H	;UPDATE BUFFER
	XCHG
	DAD	B
	LXI	B,EXTENT
	CALL	COMPARE
	XCHG
	JNZ	RDNXT
	RET
EOF	MVI	A,TRUE
	STA	FINISH
	XCHG
	SHLD	EXTENT
	RET
;
;
MAKETEMP	;CREATE TEMPORARY FILE COPY.$$$
	LXI	D,TFCB
	CALL	MAKEFILE
	CPI	0FFH
	JNZ	MAKE$OK
	MVI	B,2
	JMP	ERROR
MAKE$OK	XRA	A
	STA	TFCBCR
	STA	TFCBEX
	RET
;
;
DUMPBUF		;DUMP BUFFER TO TEMP FILE
	LXI	D,BUFFER
WRNXT	PUSH	D
	CALL	SETDMA
	LXI	D,TFCB
	CALL	WRITEREC
	POP	D
	ORA	A
	JZ	WR$OK
	MVI	B,2
	JMP	ERROR
WR$OK	LXI	B,80H	;UPDATE BUFFER POSITION
	XCHG
	DAD	B
	LXI	B,EXTENT
	CALL	COMPARE
	XCHG
	JNZ	WRNXT
	RET
;
;
TERMINATE	;CLOSE TEMP FILE
	LXI	D,TFCB
	CALL	CLOSEFILE
	;DELETE OLD COPY OF DEST FILE (IF ANY)
	LXI	D,DFCB
	CALL	DELETEFILE
	;RENAME DEST FILE = COPY.$$$
	LXI	H,DFCB+1
	LXI	D,TFCB+17
	MVI	B,11
	CALL	COPYSTRING
	LXI	D,TFCB
	CALL	RENAMEFILE
	;ADVISE OPERATOR
	LXI	D,TERMSG
	CALL	WRITESTRING
	RET
;
;
COPYSTRING	;COPY STRING OF LENGTH B
		;FROM HL^ TO DE^
	MOV	A,M
	STAX	D
	INX	H
	INX	D
	DCR	B
	JNZ	COPYSTRING
	RET
;
;
ERROR		;PRINT ERROR MESSAGE
	LXI	D,OPMSG
	MOV	A,B
	CPI	1
	JZ	EPRINT
	LXI	D,CRMSG
	CPI	2
	JZ	EPRINT
	LXI	D,REMSG
EPRINT	CALL	WRITESTRING
	;EXIT (OR RESTART IF MULTIPLE COPY MODE)
	LDA	MULTI
	CPI	TRUE
	JNZ	EXIT
	LXI	SP,STACK
	CALL	PROMPT
	JMP	COPYLP
;
;
COMPARE		;COMPARE BC^ AND HL - SET FLAGS
	INX	B
	LDAX	B
	DCX	B
	CMP	H
	RNZ
	LDAX	B
	CMP	L
	RET
;
;
SOURCEDISK	;ADVISE OPERATOR, WAIT FOR RESPONSE,
	LXI	D,SDMSG
	CALL	WRITESTRING
	CALL	WAIT
	RET
;
;
DESTDISK	;ADVISE OPERATOR, WAIT FOR RESPONSE
	LXI	D,DDMSG
	CALL	WRITESTRING
	CALL	WAIT
	;RESET BDOS TO AVOID 'R/O ERROR'
	CALL	INITBDOS
	RET
;
;
PROMPT		;ISSUE PROMPT AND GET PARAMETERS
	MVI	E,'*'
	CALL	WRITECHAR
	LXI	D,RBUF
	CALL	READSTRING
	RET
;
;
WAIT		;WAIT FOR OPERATOR TO TYPE RETURN
	CALL	READCHAR
	CPI	03H
	JZ	WBOOTE
	CPI	CR
	JNZ	WAIT
	RET
;
;	CP/M COMMUNICATION ROUTINES
;
;
ENTRY	EQU	0005H	; BDOS ENTRY POINT
;
;
READCHAR	; READ A CHARACTER FROM CONSOLE
	MVI	C,1
	JMP	ENTRY
;
;
WRITECHAR	; WRITE A CHARACTER TO CONSOLE
	MVI	C,2
	JMP	ENTRY
;
;
WRITESTRING	; WRITE A STRING ON CONSOLE
	MVI	C,9
	JMP	ENTRY
;
;
READSTRING	; READ STRING FROM CONSOLE
	MVI	C,10
	JMP	ENTRY
;
;
INITBDOS	;INITIALIZE BDOS, LOG IN DISK A
	MVI	C,13
	JMP	ENTRY
;
;
OPENFILE	; OPEN A NEW FILE
	MVI	C,15
	JMP	ENTRY
;
;
CLOSEFILE	; CLOSE FILE
	MVI	C,16
	JMP	ENTRY
;
;
DELETEFILE	; DELETE DIRECTORY ENTRY FOR FILE
	MVI	C,19
	JMP	ENTRY
;
;
READREC		; READ NEXT 128 BYTE RECORD
	MVI	C,20
	JMP	ENTRY
;
;
WRITEREC	; WRITE NEXT 128 BYTE RECORD
	MVI	C,21
	JMP	ENTRY
;
;
MAKEFILE	; CREATE NEW DIRECTORY ENTRY
	MVI	C,22
	JMP	ENTRY
;
;
RENAMEFILE	; CHANGE NAME IN DIRECTORY
	MVI	C,23
	JMP	ENTRY
;
;
SETDMA		; SET DMA ADDRESS FOR READ OR WRITE
	MVI	C,26
	JMP	ENTRY
;
;
;
;	DATA AREA
;
OLDSP	DS	2
RBUF	DB	28	;COMMAND INPUT BUFFER
	DS	31
TFCB	DB	0	;TEMP FCB
	DB	'COPY    $$$'
TFCBEX	DB	0
	DB	0,0,0
	DS	16
TFCBCR	DS	1
DFCB	DB	0	;DEST FCB
	DS	32
EXTENT	DS	2	;EXTENT OF COPY BUFFER
MULTI	DS	1	;MULTIPLE COPY MODE FLAG
FINISH	DS	1	;LAST BUFFER FLAG
SDMSG	DB	LF,'INSERT SOURCE DISK, <CR> TO CONTINUE'
	DB	CR,LF,EOM
DDMSG	DB	'INSERT DEST DISK, <CR> TO CONTINUE'
	DB	CR,LF,EOM
TERMSG	DB	'COPY COMPLETE',CR,LF,EOM
AEMSG	DB	LF,'WHAT?',CR,LF,EOM
OPMSG	DB	LF,'SOURCE FILE NOT FOUND',CR,LF,EOM
CRMSG	DB	LF,'DISK OR DIRECTORY FULL',CR,LF,EOM
REMSG	DB	'READ ERROR ON SOURCE',CR,LF,EOM
	DS	20H	;MINIMUM STACK SPACE
	;SET START OF BUFFER TO MULTIPLE OF SECTOR SIZE
	ORG	(($ + 7FH) / 80H) * 80H
STACK	EQU	$	;GROWS DOWN
BUFFER	EQU	$	;FILLS REST OF MEMORY TO CBASE
	END
