;******************************************
;*                                        *
;* SINGLE DRIVE FILECOPY V81.1  4 MAY 81  *
;*                                        *
;* WILL COPY FILES UP TO 65535 RECORDS    *
;* LONG (128 BYTES/REC) ONLY LIMITED BY   *
;* THE CONSTRAINTS OF AVAILABLE MEMORY    *
;* ALL CONSOLE AND DISK I/O               *
;* THROUGH BDOS CALL AT LOC 5             *
;*                                        *
;* REVISED BY SHELDON EICHENBAUM          *
;*                                        *
;* ORGINAL BY KEN BARBIER AS PUBLISHED IN *
;* MICROCOMPUTING  SEPT. 1980             *
;*                                        *
;******************************************
; CP/M BDOS ADDRESSES
RBOOT	EQU	0	;RE-BOOT CP/M
BDOS	EQU	5	;BDOS CALL ENTRY
FCB	EQU	5CH	;DEFAULT FILE CONTROL BLOCK
INBUF	EQU	80H	;DEFAULT DMA ADDRESS
;CP/M BDOS FUNCTIONS
READF	EQU	1	;READ CONSOLE INTO (A)
TYPEF	EQU	2	;READ CONSOLE FROM (E)
INIT	EQU	13	;INITIALIZE DISC IN DRIVE A:
OPEN	EQU	15	;OPEN FILE
CLOS	EQU	16	;CLOSE FILE
FIND	EQU	17	;FIND FILE IN DIRECTORY
DELE	EQU	19	;DELETE FILE
READ	EQU	20	;READ FILE
WRIT	EQU	21	;WRITE FILE
MAKE	EQU	22	;CREATE FILE DIRECTORY ENTRY
	ORG	0100H	;TPA PROGRAM START ADDRESS
	JMP	START	;GOTO PROGRAM START
; CONSOLE I/O THROUGH BDOS CALL
CI	PUSH	H	;SAVE REGISTERS
	PUSH	D
	PUSH	B
	MVI	C,READF	;READ FUNCTION
	CALL	BDOS	;RETURN CHAR IN (A)
	POP	B	;RESTORE OTHER REGISTERS
	POP	D
	POP	H
	RET
CO	PUSH	H
	PUSH	D
	PUSH	B
	MOV	E,A	;MOVE PRINT CHAR TO (E)
	MVI	C,TYPEF
	CALL	BDOS
	POP	B
	POP	D
	POP	H
	RET
CCRLF	MVI	A,0DH	;CR LF TO CONSOLE
	CALL	CO
	MVI	A,0AH
	JMP	CO
MSGXP	POP	H	;OUTPUT MESSAGE AND RETURN
MSGX1	MOV	A,M	;THROUGH INDEX (H,L)
	CPI	0	;TEXT TERMINATOR = 0
	JZ	MSGEX
	CALL	CO
	INX	H
	JMP	MSGX1
MSGEX	INX	H	;POINT TO TEXT + 1
	PCHL		;AND RETURN THERE
; FILECOPY CONSOLE MESSAGE SUBROUTINE
RDMSG	CALL	CCRLF	;PROMPT FOR READ DISK
	CALL	MSGXP
	DB	'READ DISK IN DRIVE, THEN CR'
	DB	0
RDMS1	CALL	CI	;GET RESPONSE
	CPI	'X'	;ALLOW EXIT
	JZ	RBOOT	;BACK TO CP/M
	CPI	0DH	;ACCEPT CR ONLY
	JNZ	RDMS1
	CALL	CCRLF	;ACKNOWLEDGE
	RET		;AND RETURN
WRMSG	CALL	CCRLF	;PROMPT FOR WRITE DISK
	CALL	MSGXP
	DB	'WRITE DISK IN DRIVE, THEN CR'
	DB	0
WRMS1	CALL	CI
	CPI	'X'
	JZ	RBOOT
	CPI	0DH
	JNZ	WRMS1
	CALL	CCRLF
	RET
RDERR	CALL	CCRLF	;SHOW READ ERROR
	CALL	MSGXP
	DB	'READ ERROR!  ENTER X TO ABORT'
	DB	0DH,0AH
	DB	'                   CR TO IGNORE'
	DB	0
RDER1	CALL	CI	;ACCEPT CR OR X
	CPI	'X'
	JZ	EXIT
	CPI	0DH
	RZ		;RETURN MEANS IGNORE
	JMP	RDER1
WRERR	CALL	CCRLF	;SHOW WRITE ERROR
	CALL	MSGXP
	DB	'PERMANENT WRITE ERROR!'
	DB	0
EXIT	CALL	MSGXP
	DB	'BACK TO CP/M?'
	DB	0
WRER1	CALL	CI	;WAIT FOR CR	X
	CPI	0DH
	JZ	RBOOT
	CPI	'X'
	JZ	RBOOT
	JNZ	WRER1	;AS ONLY LEGAL RESPONSE
; BEGIN FILECOPY PROGRAM
START	CALL	CCRLF	;SIGN ON MESSAGE
	CALL	MSGXP
	DB	'SINGLE DRIVE FILECOPY  V81.1   4 MAY 81'
	DB	0DH,0AH
	DB	0
	CALL	RDMSG	;PROMPT FOR READ DISK
	LXI	D,FCB	;LOOK FOR FILE
	MVI	C,FIND	;BEFORE GOING AHEAD
	CALL	BDOS
	CPI	255	;DOES FILE EXITS?
	JNZ	RUN	;YES. READ IT
	CALL	CCRLF	;NO. GIVE UP
	CALL	MSGXP
	DB	'FILE DOES NOT EXIST!'
	DB	0
	JMP	EXIT	;REBOOT CP/M
RUN	LXI	H,FCB	;SET UP FCB'S FOR
	LXI	D,RFCB	;READ AND WRITE
	MVI	C,16
RUN1	MOV	A,M
	STAX	D
	INX	H
	INX	D
	DCR	C
	JNZ	RUN1
	LXI	H,FCB
	LXI	D,WFCB
	MVI	C,16
RUN2	MOV	A,M
	STAX	D
	INX	H
	INX	D
	DCR	C
	JNZ	RUN2
	LXI	H,BUFFR	;INITIALIZE POINTER
	SHLD	HSAVE	;INTO BUFFER
	XRA	A	;ZERO RECORD COUNTS
	STA	ASAVE
	STA	ASAVE+1
	STA	RFCBN
	STA	WFCBN
; READ THE FILE INTO RAM
RFILE	LXI	D,RFCB	;USE READ FCB
	MVI	C,OPEN	;AND OPEN THE FILE
	CALL	BDOS
	CPI	255	;ERROR?
	JNZ	RFIL1
	CALL	CCRLF
	CALL	MSGXP	;YES. SHOW IT
	DB	'UNABLE TO OPEN FILE!'
	DB	0
	JMP	EXIT	;AND ABORT
RFIL1	LXI	D,RFCB	;READ A RECORD
	MVI	C,READ
	CALL	BDOS
	CPI	0	;GOOD READ?
	JZ	RFIL2	;YES. STORE IT
	CPI	1	;OR END OF FILE?
	JZ	WFILE	;YES. WRITE IT
	CALL	RDERR	;NO. SHOW ERROR
RFIL2	LHLD	HSAVE	;STORE THE RECORD
	LXI	D,INBUF
	MVI	C,80H
RFIL3	LDAX	D
	MOV	M,A
	INX	H
	INX	D
	DCR	C
	JNZ	RFIL3
	SHLD	HSAVE	;AND NEXT ADDRESS
	PUSH	H
	LHLD	ASAVE
	INX	H
	SHLD	ASAVE
	POP	H
	LDA	7	;ANY MEMORY LEFT?
	DCR	A
	CMP	H
	JNZ	RFIL1	;YES. KEEP READING
	CALL	CCRLF	;NO. ABORT
	CALL	MSGXP
	DB	'FILE IS TOO BIG!'
	DB	0
	JMP	EXIT
; WRITE THE FILE ONTO DISK
WFILE	CALL	WRMSG	;PROMPT FOR WRITE DISK
	MVI	C,INIT	;INITIALIZE DISK FOR WRITE
	CALL	BDOS
	LXI	D,WFCB	;SEE IF FILE EXITS
	MVI	C,FIND
	CALL	BDOS
	CPI	255	;WE CAN'T WRITE TWO
	JZ	WFIL1	;NO. CONTINUE
	CALL	CCRLF	;YES. ERASE OR ABORT?
	CALL	MSGXP
	DB	'FILE ALREADY EXITS. ENTER:X TO ABORT'
	DB	0DH,0AH
	DB	'                          CR TO ERASE IT'
	DB	0
WAIT1	CALL	CI
	CPI	'X'
	JZ	RBOOT
	CPI	0DH
	JNZ	WAIT1
	LXI	D,WFCB	;ERASE THE OLD FILE
	MVI	C,DELE
	CALL	BDOS
WFIL1	LXI	D,WFCB	;OPEN FILE FOR WRITE
	MVI	C,MAKE
	CALL	BDOS
	CPI	255	;OPEN OK?
	JNZ	WFIL2	;YES. CONTINUE
	CALL	CCRLF
	CALL	MSGXP	;SHOW UNABLE TO OPEN
	DB	'OUT OF DIRECTORY SPACE!'
	DB	0
	JMP	EXIT
WFIL2	LXI	H,BUFFR	;INITIALIZE POINTER
	SHLD	HSAVE
WFIL3	LHLD	HSAVE	;MOVE RECORD TO OUTPUT
	LXI	D,INBUF	;BUFFER (SAME AS INPUT)
	MVI	C,80H
WFIL4	MOV	A,M
	STAX	D
	INX	H
	INX	D
	DCR	C
	JNZ	WFIL4
	SHLD	HSAVE	;SAVE NEXT ADDRESS
	LXI	D,WFCB	;WRITE THE RECORD
	MVI	C,WRIT
	CALL	BDOS
	CPI	0
	CNZ	WRERR	;SHOW WRITE ERROR
	LHLD	ASAVE	;COUNT RECORD WRITTEN
	DCX	H
	SHLD	ASAVE
	MOV	A,H
	ORA	L
	JNZ	WFIL3	;AND WRITE ANOTHER
	LXI	D,WFCB	;DONE. CLOSE THE FILE
	MVI	C,CLOS
	CALL	BDOS
	CALL	CCRLF
	CALL	MSGXP	;PROMPT FOR REBOOT
	DB	'ALL DONE!'
	DB	0
	JMP	EXIT	;AND WE ARE ALL DONE
; RAM BUFFERS
HSAVE	DS	2	;BUFFER ADDRESS STORE
ASAVE	DS	2	;RECORD COUNT
RFCB	DS	33	;READ FILE CONTROL BLOCK
WFCB	DS	33	;WRITE FILE CONTROL BLOCK
BUFFR	DB	0	;DATA BUFFER START
RFCBN	EQU	RFCB+32	;RECORD COUNTS. READ
WFCBN	EQU	WFCB+32	;AND WRITE
	END
