;******************************************
;*                                        *
;* 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