;  PROGRAM:  MCOPY
;  AUTHOR:  RICHARD CONN
;  VERSION:  4.0
;  DATE:  18 MAY 84
;  PREVIOUS VERSIONS:  3.0 (16 JAN 83)
;  PREVIOUS VERSIONS:  NUMEROUS

VERS    equ     42      ; Use 16k buffer  14 Dec 84  jww
                       ; Fix some bugs  Joe Wright  28 Aug 84
                       ; 1. Add check for directory full after f$make

z3env   SET     0F400H

;
;       MCOPY is a program which repeatedly copies a file from drive
; A: onto drive B:.  It prompts the user to mount a disk in drive B:,
; copies the file from drive A: to drive B:, verifies the copy (if not
; overridden), and then performs the function again.
;
;       MCOPY performs its function in the following steps:
;               1.  MCOPY determines the attributes
; of the destination file (if it exists) and clears them (file becomes
; R/W and DIR)
;               2.  MCOPY deletes the destination file (if it exists)
;               3.  MCOPY copies the source file to the destination
;               4.  MCOPY determines the attributes
; of the source file and makes the attributes of the destination file
; identical to those of the source
;               5.  MCOPY reads both the source and destination files and
; compares them byte-for-byte
;

;  SPECIAL Constants
PLIM    EQU     4*16    ; SIZE OF BUFFER IN PAGES (4 * nK) [may be changed]
ESIZE   EQU     16      ; NUMBER OF BYTES/ENTRY

;  CP/M Constants
WB      EQU     0       ; CP/M WARM BOOT
BDOSE   EQU     WB+5    ; BDOS ENTRY POINT
FCB     EQU     WB+5CH  ; SPECIFIED FCB
BUFF    EQU     WB+80H  ; DEFAULT BUFFER AND INPUT LINE
SDMA    EQU     26      ; SET DMA ADDRESS

;  ASCII Constants, et al
ON      EQU     0FFH    ; ON CODE
OFF     EQU     0       ; OFF CODE
CR      EQU     0DH     ; <CR>
LF      EQU     0AH     ; <LF>
CTRLC   EQU     'C'-'@' ; ^C
CTRLZ   EQU     'Z'-'@' ; ^Z
OPTC    EQU     '/'     ; OPTION DELIMITER

;
;  LOAD @DE MACRO
;
LDED    MACRO   ?ADR
       XCHG
       LHLD    ?ADR
       XCHG
       ENDM

;
;  SYSLIB ROUTINES
;
       EXT     Z3INIT,ZFNAME,GETQUIET
       EXT     COMPHD,RETUD,LOGUD,PUTUD,GETUD
       EXT     DIRQ,DIRPACK,DIRTDU
       EXT     INITFCB,F$EXIST
       EXT     CRCCLR,CRCUPD,CRCDONE
       EXT     BDOS,CIN,COUT,CONDIN
       EXT     F$DELETE,F$OPEN,F$MAKE,F$CLOSE,F$READ,F$WRITE
       EXT     PADC,EPSTR,EPRINT
       EXT     MOVEB,CAPS,CRLF
       EXT     CODEND

;
; Environment Definition
;
       if      z3env ne 0
;
; External ZCPR3 Environment Descriptor
;
       jmp     start
       db      'Z3ENV' ;This is a ZCPR3 Utility
       db      1       ;External Environment Descriptor
z3eadr:
       dw      z3env
start:
       lhld    z3eadr  ;pt to ZCPR3 environment
;
       else
;
; Internal ZCPR3 Environment Descriptor
;
       MACLIB  Z3BASE.LIB
       MACLIB  SYSENV.LIB
z3eadr:
       jmp     start
       SYSENV
start:
       lxi     h,z3eadr        ;pt to ZCPR3 environment
       endif

;
; Start of Program -- Initialize ZCPR3 Environment
;
       call    z3init  ;initialize the ZCPR3 Env and the VLIB Env
       jmp     startx

;
;  USER-DEFINABLE INITIAL FLAG CONDITIONS
;    THE DEFAULT CONDITIONS FOR MCOPY MAY BE READILY PATCHED BY THE USER
; VIA DDT FOR HIS DESIRED DEFAULT VALUES
;
DVERFLG:
       DB      ON      ; SET VERIFY
DINSP:
       DB      OFF     ; SET NO INSPECT
DNCOPY:
       DB      OFF     ; SET NO MULTIPLE COPIES BY DEFAULT
DDDISK:
       DB      'B'-'A' ; DEFAULT DESTINATION DISK IS B
DDUSER:
       DB      0       ; DEFAULT DESTINATION USER IS 0
BACKDIR:
       DB      'BACKUP  '      ; NAME OF BACKUP DIRECTORY

;
;  BEGINNING OF MCOPY PROGRAM
;
STARTX:
;
;  PRINT BANNER
;
       CALL    EPRINT
       DB      'MCOPY  Version '
       DB      VERS/10+'0','.',(VERS MOD 10)+'0',0
;
;  SET UP DYNAMIC BUFFERS
;
       LXI     H,0     ; GET SP
       DAD     SP
       SHLD    STACK   ; SAVE IT
       CALL    CODEND  ; DETERMINE FREE SPACE
       SHLD    INLINE  ; PTR TO INPUT LINE
       INR     H
       SHLD    FCBT    ; PTR TO FCB TEMP
       INR     H
       SHLD    FCBS    ; PTR TO SOURCE FCB
       INR     H
       SHLD    FCBD    ; PTR TO DEST FCB
       INR     H
       SHLD    FREEBUF ; FREE SPACE BUFFER

;
;  SET DEFAULT FLAGS
;
       CALL    GETQUIET        ; GET QUIET FLAG
       STA     QUIET
       LDA     DVERFLG ; VERIFY
       STA     VERFLG
       LDA     DINSP   ; INSPECT
       STA     INSP
       LDA     DNCOPY  ; MULTIPLE COPIES
       STA     NCOPY

;
;  CHECK FOR BACKUP DIRECTORY AND ESTABLISH IT AS DEFAULT
;    IF NO BACKUP DIRECTORY, SELECT DEFAULT STORED
;
       LXI     H,BACKDIR       ; PT TO DIRECTORY NAME
       CALL    DIRTDU
       JZ      DEFBACK         ; NAME NOT FOUND, SO SELECT DEFAULT
       MOV     A,B     ; SET DEST DISK
       STA     DDISK
       MOV     A,C     ; SET DEST USER
       STA     DUSER
       JMP     BACKSET
DEFBACK:
       LDA     DDDISK  ; GET DEFAULT DEST DISK
       STA     DDISK   ; SET DEST DISK
       LDA     DDUSER  ; GET DEFAULT DEST USER
       STA     DUSER   ; SET DEST USER
;
;  OBTAIN AND SAVE CURRENT USER AND DISK
;
BACKSET:
       CALL    PUTUD   ; SAVE POSITION
       CALL    RETUD   ; GET USER/DISK
       MOV     A,B     ; SAVE DISK
       STA     CDISK
       LHLD    INLINE  ; INPUT LINE SAVE BUFFER
       XCHG            ; ... IN DE
       LXI     H,BUFF+1        ; PT TO COMMAND LINE CHAR COUNT
       MVI     B,128   ; SAVE 128 BYTES (ARBITRARY)
       CALL    MOVEB
       XCHG            ; HL PTS TO INPUT LINE
;
;  SET OTHER FLAGS
;
       XRA     A       ; A=0
       STA     EXIST   ; TURN OFF EXIST TEST
;
;  CHECK FOR EMPTY COMMAND LINE AND PROCESS COMMAND MODE IF SO
;    ON ENTRY, HL PTS TO FIRST CHAR OF STRING FROM CLINE
;
START1:
       MOV     A,M     ; GET CHAR
       ORA     A       ; EOL?
       JZ      MHELP   ; PRINT HELP MESSAGE IF NO INPUT
       INX     H       ; PT TO NEXT
       CPI     ' '     ; JUST SPACES?
       JZ      START1
;
;  COMMAND LINE WAS NOT EMPTY -- CHECK FOR HELP REQUEST
;
       DCX     H       ; PT TO FIRST CHAR
       CPI     '/'     ; IF OPENING OPTION, MUST BE HELP
       JZ      MHELP
;
;  SEE IF OPTIONS ARE AVAILABLE IN THE COMMAND LINE
;
       SHLD    MFPTR   ; SET PTR TO FIRST CHAR OF FILE NAME SPECS
;
;  SKIP TO END OF FILE NAME SPECS
;
START2:
       MOV     A,M     ; SKIP TO <SP> OR EOL
       INX     H       ; PT TO NEXT
       CPI     ' '+1   ; <SP> OR LESS?
       JNC     START2
       ORA     A       ; AT EOL?
       JZ      MCOPY0  ; PERFORM DEFAULT MCOPY FUNCTION IF AT EOL
;
;  SCAN FOR OPTION
;
OPTION:
       MOV     A,M     ; GET OPTION CHAR
       ORA     A       ; EOL?
       JZ      MCOPY0  ; DO MCOPY
       INX     H       ; PT TO NEXT
       PUSH    H       ; SAVE PTR
       LXI     H,OPTTAB        ; PT TO OPTION TABLE
       CALL    CMDER   ; PROCESS COMMAND
       POP     H       ; GET PTR
       JMP     OPTION

;
;  COMMAND PROCESSOR -- COMMAND LETTER IN A, HL PTS TO TABLE
;
CMDER:
       PUSH    B       ; SAVE BC
       MOV     B,A     ; COMMAND IN B
CMDER1:
       MOV     A,M     ; GET COMMAND LETTER
       ORA     A       ; DONE?
       JZ      CMDER2
       CMP     B       ; MATCH?
       JNZ     CMDER3
CMDER2:
       INX     H       ; PT TO ADDRESS
       MOV     E,M     ; GET IT IN DE
       INX     H
       MOV     D,M
       XCHG            ; HL PTS TO COMMAND ADDRESS
       POP     B       ; RESTORE BC
       PCHL            ; RUN COMMAND
CMDER3:
       INX     H       ; SKIP TO NEXT ENTRY IN TABLE
       INX     H
       INX     H
       JMP     CMDER1

;  OPTION COMMAND TABLE
OPTTAB:
       DB      ' '     ; DONE
       DW      OPTS
       DB      OPTC    ; SKIP OPTC
       DW      OPTS
       DB      'E'     ; EXIST TEST
       DW      OPTE
       DB      'I'     ; INSPECT
       DW      OPTI
       DB      'M'     ; MULTIPLE COPY
       DW      OPTM
       DB      'Q'     ; QUIET
       DW      OPTQ
       DB      'V'     ; VERIFY
       DW      OPTV
       DB      0       ; END OF TABLE
       DW      OHELP

;  INVALID OPTION CHAR -- CLEAR STACK (RET ADR AND HL) AND PRINT HELP
OHELP:
       POP     H       ; CLEAR RET ADR
       POP     H       ; CLEAR HL

;  PRINT HELP MESSAGE
MHELP:
       CALL    EPRINT
       DB      CR,LF,'Syntax:'
       DB      cr,lf,'  MCOPY dir:=dir:filename.typ,... o...'
       db      cr,lf,'Options:'
       DB      cr,lf,'  E -- Existence Test'
       DB      cr,lf,'  I -- Inspect Files'
       DB      cr,lf,'  M -- Multiple Copy'
       DB      cr,lf,'  Q -- Toggle Quiet'
       DB      cr,lf,'  V -- No Verify'
       DB      0
       RET             ; RETURN TO ZCPR3

;  VERIFY FLAG TOGGLE OPTION
OPTV:
       LDA     VERFLG  ; GET FLAG
       CMA             ; FLIP IT
       STA     VERFLG  ; PUT FLAG
;  SKIP OPTION
OPTS:
       RET

;  EXIST TEST TOGGLE OPTION
OPTE:
       LDA     EXIST   ; GET FLAG
       CMA             ; FLIP IT
       STA     EXIST   ; PUT FLAG
       RET

;  NCOPY FLAG TOGGLE OPTION
OPTM:
       LDA     NCOPY   ; GET FLAG
       CMA             ; FLIP IT
       STA     NCOPY   ; PUT FLAG
       RET

;  INSPECT FLAG TOGGLE OPTION
OPTI:
       LDA     INSP    ; GET FLAG
       CMA             ; FLIP IT
       STA     INSP    ; PUT FLAG
       RET

;  QUIET FLAG TOGGLE OPTION
OPTQ:
       LDA     QUIET   ; GET FLAG
       CMA             ; FLIP IT
       STA     QUIET   ; PUT FLAG
       RET

;
;  **** MCOPY of COMMAND LINE ****
;
MCOPY0:
       LHLD    FREEBUF ; STACK RESET
       SPHL
       LDA     NCOPY   ; MULTIPLE COPIES?
       ORA     A       ; 0=NO
       JZ      NOPAUSE
       CALL    SAKCHK  ; STRIKE ANY KEY CHECK
       JZ      CPM     ; WARM BOOT IF ABORT
NOPAUSE:
       CALL    COPY    ; DO THE COPY
CPM:
       LHLD    STACK   ; RESET STACK
       SPHL
       RET             ; RETURN TO OPSYS
CPMA:
       CALL    EPRINT
       DB      CR,LF,'Abort',0
       JMP     CPM

;
;  **** Begin Multiple Copy Procedure ****
;
COPY:
       LHLD    MFPTR   ; PT TO FIRST FILE NAME
       SHLD    NXTPTR  ; SET PTR TO NEXT FILE NAME
       XRA     A       ; A=0
       STA     VERCNT  ; ZERO ERROR COUNT
       LDA     EXIST   ; IF EXIST, THEN MUST NOT BE QUIET
       ORA     A       ; 0=NO EXIST
       JZ      MCOPY
       XRA     A       ; SET NO QUIET
       STA     QUIET
;
;  **** MAIN COPY LOOP ****
;
MCOPY:
       LHLD    NXTPTR  ; GET PTR TO NEXT FILE NAME
       MOV     A,M     ; GET FIRST CHAR
       CPI     ' '+1   ; DONE IF <SP> OR LESS
       JNC     MCOPY1  ; CONTINUE WITH PROCEDURE
;
;  MCOPY OF FILE SPECS IS NOW DONE
;  DONE WITH COPY PROCEDURE -- CONTINUE?
;
COPYT:
       LDA     VERFLG  ; VERIFY?
       ORA     A       ; 0=NO
       JZ      COPYT1
       CALL    CRLF    ; NEW LINE
       LDA     VERCNT  ; GET ERROR COUNT
       CALL    PADC    ; PRINT AS DECIMAL
       CALL    EPRINT
       DB      ' Errors',0
COPYT1:
       LDA     NCOPY   ; MULTIPLE COPIES?
       ORA     A       ; 0=NO
       RZ
       CALL    SAKCHK  ; CHECK FOR STRIKE OF ANY KEY
       RZ              ; RETURN IF ABORT
       JMP     COPY    ; COPY AGAIN FROM THE BEGINNING
;
;  BEGIN COPY OF FILE GROUP
;
MCOPY1:
       CPI     ','     ; SKIP COMMA SEPARATOR IF THERE
       JNZ     MCPY0
       INX     H       ; PT TO CHAR AFTER COMMA
MCPY0:
       MOV     A,M     ; GET NEXT CHAR
       CPI     ' '+1   ; CHECK FOR ERROR
       JC      FORMERR
       CALL    GETUD   ; RETURN HOME
       LDED    FCBS    ; PT TO SOURCE FCB
       MVI     A,0     ; DIR BEFORE DU
       CALL    ZFNAME  ; EXTRACT FILE NAME DATA
       CALL    DUCVRT  ; CONVERT DU INTO BC
       MOV     A,M     ; GET DELIMITER
       CPI     '='     ; IF '=', WE HAVE A NEW DISK/USER
       JNZ     MCOPY2  ; FORM IS DIRS:FN.FT IF NO '='
;
;  FORM IS DIRD:=DIRS:FN.FT, SO SET DEST DISK/USER
;
       MOV     A,B     ; GET DISK
       STA     DDISK   ; SET NEW DEFAULT DISK
       MOV     A,C     ; GET USER
       STA     DUSER   ; SET NEW DEFAULT USER
;
;  NOW DERIVE DIRS:FN.FT FORM AFTER THE '='
;
MCPY2:
       INX     H       ; PT TO CHAR BEYOND '='
       MOV     A,M     ; GET CHAR
       CPI     ' '+1   ; FORMAT ERROR?
       JC      FORMERR
       LDED    FCBS    ; LOAD FCB
       MVI     A,0     ; DIR BEFORE DU
       CALL    ZFNAME  ; GET SOURCE NAME
       CALL    DUCVRT  ; CONVERT TO DU IN BC
;
;  SAVE PTR TO NEXT CHAR AFTER DIRS:FN.FT, AND SET SOURCE DISK/USER
;
MCOPY2:
       SHLD    NXTPTR  ; SAVE PTR TO NEXT CHAR
       MOV     A,B     ; GET DISK
       STA     SDISK   ; SET NEW DEFAULT DISK
       MOV     A,C     ; GET USER
       STA     SUSER   ; SET NEW DEFAULT USER
MCPY22:
       LDA     DDISK   ; DEST DIR MUST NOT EQUAL SOURCE DIR
       MOV     B,A
       LDA     SDISK
       CMP     B
       JNZ     MCPYOK
       LDA     DUSER
       MOV     B,A
       LDA     SUSER
       CMP     B
       JNZ     MCPYOK
       CALL    EPRINT
       DB      CR,LF,'Src=Dest Err',0
       RET
MCPYOK:
       CALL    EPRINT
       DB      CR,LF,'Copy ',0
       LDA     SDISK   ; GET NUMBER
       ADI     'A'     ; CONVERT TO LETTER
       CALL    COUT    ; PRINT
       LDA     SUSER   ; PRINT USER NUMBER
       CALL    PADC
       MVI     A,':'   ; SEPARATOR
       CALL    COUT
       MVI     A,' '
       CALL    COUT
       LHLD    FCBS    ; PRINT FILE SPEC
       INX     H       ; PT TO FILE NAME
       CALL    PRFN
       CALL    EPRINT
       DB      ' to ',0
       LDA     DDISK   ; GET NUMBER
       ADI     'A'     ; CONVERT TO LETTER
       CALL    COUT    ; PRINT
       LDA     DUSER   ; PRINT USER NUMBER
       CALL    PADC
       MVI     A,':'
       CALL    COUT
       MVI     C,13    ; RESET DISK SYSTEM
       CALL    BDOS
       CALL    LOGS    ; LOG IN SOURCE USER/DISK
       LDED    FCBS    ; PT TO SOURCE FCB
       CALL    INITFCB ; INIT FCB
       LHLD    FREEBUF ; PT TO BUFFER AREA
       MVI     A,0C0H  ; SELECT NON-SYS AND SYS FILES
       CALL    DIRQ    ; LOAD DIR, SELECT FILES, SORT, ETC
       JZ      TPAOVFL ; TPA OVERFLOW ERROR?
       LDA     INSP    ; INSPECT FILES?
       ORA     A       ; 0=NO
       CNZ     INSPF   ; INSPECT FILES IF OPTION SELECTED
       MOV     A,B     ; CHECK FOR ANY FILES TO COPY
       ORA     C       ; 0=NONE
       JNZ     MCPY24
MCPY23:
       CALL    EPRINT
       DB      CR,LF,' NO Files -- ^C to Abort ',0
       CALL    CIN     ; GET RESPONSE
       CPI     'C'-'@' ; ABORT?
       JZ      COPYT   ; END TEST
       JMP     MCOPY   ; CONTINUE WITH NEXT
MCPY24:
       PUSH    H       ; SAVE PTR AND COUNT
       PUSH    B
       LXI     D,ESIZE ; SKIP TO END OF LOADED FILES AND MARK BEGINNING OF
                       ;   WORK AREA
MCPY25:
       DAD     D       ; PT TO NEXT
       DCX     B       ; COUNT DOWN
       MOV     A,B     ; DONE?
       ORA     C
       JNZ     MCPY25
       MVI     A,PLIM  ; SET PAGE LIMIT
       STA     PAGLIM
       SHLD    WORKBF  ; SAVE PTR TO BEGINNING OF WORK BUFFER
       LDA     BDOSE+2 ; GET BASE PAGE OF BDOS
       SUI     10      ; GET BELOW BASE PAGE OF CCP
       SUB     H       ; COMPUTE SIZE OF BUFFER AREA
       CPI     PLIM    ; PLIM PAGES LEFT?
       JNC     PAGOK
       STA     PAGLIM  ; SET PAGE LIMIT
PAGOK:
       POP     B       ; RESTORE PTRS
       POP     H
;
;  MAIN COPYING LOOP
;    FILE NAMES ARE PTED TO BY HL AND BC=NUMBER OF FILES
;
MCPY26:
       PUSH    H       ; SAVE REGS
       PUSH    B
       CALL    ABORTCK ; CHECK FOR ABORT
MCPY27:
       CALL    MCOPYX  ; COPY SOURCE (HL) TO DESTINATION USING WORK BUFFER
       CALL    PRDONE  ; PRINT DONE MESSAGE
       CALL    ABORTCK ; CHECK FOR ABORT
       LDA     LSTCPY  ; LAST FILE COPIED?
       ORA     A       ; 0=NO
       JZ      MCPY28
       LDA     VERFLG  ; VERIFY?
       ORA     A       ; 0=NO
       CNZ     MCOPYV  ; DO VERIFY
MCPY28:
       POP     B       ; GET REGS
       POP     H
       LXI     D,ESIZE ; PT TO NEXT FILE
       DAD     D       ; HL PTS TO NEXT FILE
       DCX     B       ; COUNT DOWN
       MOV     A,B
       ORA     C
       JNZ     MCPY26
       JMP     MCOPY   ; COPY NEXT FILE SPEC
;
;  CHECK FOR ABORT
;
ABORTCK:
       CALL    CONDIN  ; CONDITIONAL INPUT
       RZ
       CPI     CTRLC   ; ABORT?
       JZ      CPMA
       RET
;
;  PRINT DONE MESSAGE
;
PRDONE:
       LDA     QUIET   ; CHECK FOR QUIET
       ORA     A       ; NZ=QUIET
       RNZ
       CALL    EPRINT
       DB      ' Done',0
       RET
;
;  COPY SOURCE FILE PTED TO BY HL TO DESTINATION
;
MCOPYX:
       XRA     A       ; SET NO COPY OF LAST FILE
       STA     LSTCPY  ; SET FLAG
       LDED    FCBS    ; SET SOURCE FCB
       MVI     B,12    ; 12 BYTES
       CALL    MOVEB
       CALL    INITFCB ; INIT SOURCE FCB
       LDED    FCBD    ; SET DESTINATION FCB
       MVI     B,12    ; 12 BYTES
       CALL    MOVEB
       CALL    DRW     ; CLEAR ATTRIBUTES IN FCB
       CALL    INITFCB ; INIT DESTINATION FCB
       CALL    LOGD    ; LOG IN DESTINATION
       CALL    EPRINT
       DB      CR,LF,' File ',0
       LHLD    FCBD    ; PRINT FILE NAME
       INX     H       ; PT TO FILE NAME
       CALL    PRFN
       LDED    FCBD    ; PT TO FCB
       CALL    F$EXIST ; DOES DEST EXIST?
       JZ      FNF     ; FILE NOT FOUND IF ZERO
       LDA     QUIET   ; QUIET?
       ORA     A       ; 0=NO
       JNZ     FFND
       CALL    EPRINT
       DB      ' Replace',0
FFND:
       CALL    EATEST  ; EXIST APPROVED TEST?
       RZ              ; NOT APPROVED, SO ABORT
       CALL    DESTRW  ; MAKE DESTINATION R/W IF NOT ALREADY
       CALL    F$DELETE        ; DELETE FILE
       CALL    INITFCB         ; REINIT FCB
       JMP     FNF1    ; CREATE NEW FILE AND CONTINUE
FNF:
       LDA     QUIET   ; QUIET?
       ORA     A       ; 0=NO
       JNZ     FNF1
       CALL    EATEST  ; EXIST APPROVED?
       RZ              ; NO?
FNF1:
       CALL    EPRINT
       DB      ' ...',0
       MVI     A,0FFH  ; SET COPY OF LAST FILE
       STA     LSTCPY  ; SET FLAG
       CALL    F$MAKE  ; CREATE NEW FILE
       inr     a       ; check for full directory
       jz      dirful  ; report it
;
;  OPEN SOURCE FILE IN PREP FOR COPY
;
       CALL    CRCCLR  ; CLEAR CRC VALUE
       CALL    LOGS    ; LOG IN SOURCE DISK
       LDED    FCBS    ; INIT FCB
       CALL    INITFCB
       CALL    F$OPEN  ; OPEN FILE
;
;  THIS LOOP, WHICH STARTS AT MCPYX, COPIES THE FILE FROM SOURCE TO DEST
;
MCPYX:
       CALL    LOGS    ; LOG IN SOURCE
       LDED    FCBS    ; PT TO SOURCE FCB
       LHLD    WORKBF  ; PT TO BUFFER TO COPY INTO
       CALL    LOAD    ; LOAD FILE INTO WORKBF
       LDA     BCNT    ; IF COUNT=0, THEN DONE
       ORA     A
       JZ      MC2DONE
;
;  COPY TO DISK
;
MCPYD:
       CALL    LOGD    ; LOG IN DESTINATION
       LHLD    WORKBF  ; PT TO BUFFER
MCPYD1:
       CALL    SETDMA  ; SET DMA ADDRESS PTED TO BY HL
       LXI     D,128   ; INCR HL BY 128
       DAD     D       ; HL PTS TO NEXT BLOCK
       LDED    FCBD    ; WRITE TO DESTINATION FILE
       CALL    F$WRITE
       ORA     A       ; OK?
       JNZ     MCPYDERR

;  COUNT DOWN TO NEXT BLOCK
       LDA     BCNT    ; GET BLOCK COUNT
       DCR     A       ; COUNT DOWN
       STA     BCNT
       JNZ     MCPYD1
       LDA     CONT    ; CONTINUE?
       ORA     A       ; CONT IF NOT ZERO
       JNZ     MCPYX
;
;  END OF COPY LOOP
;
MC2DONE:
       CALL    LOGS    ; LOG IN SOURCE
       LDED    FCBS    ; CLOSE SOURCE
       CALL    F$CLOSE
       CALL    LOGD    ; LOG IN DESTINATION
       LDED    FCBD    ; CLOSE DESTINATION
       CALL    F$CLOSE
       CALL    CRCDONE ; GET CRCK VALUE
       SHLD    CRCVAL  ; SAVE CRC VALUE
;
;  SET ATTRIBUTES OF DESTINATION TO BE THE SAME AS THOSE OF SOURCE
;
       CALL    LOGS    ; LOG IN SOURCE DRIVE
       LDED    FCBS    ; FIND SOURCE
       MVI     C,17    ; SEARCH FOR FIRST
       CALL    BDOS
       RLC             ; MULTIPLY BY 32 TO GET OFFSET
       RLC
       RLC
       RLC
       RLC
       ANI     0E0H    ; MASK OUT LSB
       MOV     L,A     ; VALUE IN L
       MVI     H,0
       LXI     D,BUFF  ; ADD IN BUFFER BASE
       DAD     D
       XCHG            ; PT TO FCBT IN DE
       LHLD    FCBT
       XCHG
       MVI     B,16    ; MOVE 16 BYTES
       CALL    MOVEB
       CALL    LOGD    ; LOG IN DESTINATION DRIVE
       CALL    INITFCB ; INIT FCB PTED TO BY DE (FCBT)
       MVI     C,30    ; SET FILE ATTRIBUTES
       CALL    BDOS
       RET             ; MCOPYX RETURN

;
;  CONVERT Z3 FCB DU INTO DU IN BC
;
DUCVRT:
       PUSH    H       ; SAVE REGS
       PUSH    D
       LDAX    D       ; GET DISK
       ORA     A       ; CURRENT?
       JNZ     DUCV1
       LDA     CDISK   ; GET CURRENT
       INR     A       ; ADD 1 FOR A=1
DUCV1:
       DCR     A       ; A=0
       MOV     B,A
       LXI     H,13    ; OFFSET TO USER
       DAD     D
       MOV     C,M     ; GET USER
       POP     D       ; RESTORE REGS
       POP     H
       RET

;  FORMAT ERROR
FORMERR:
       CALL    EPRINT
       DB      CR,LF,' Error: ',0
       CALL    EPSTR   ; PRINT ERROR
       RET

;  TPA OVERFLOW
TPAOVFL:
       CALL    EPRINT
       DB      CR,LF,'TPA Ovfl',0
       JMP     CPM

;  WRITE ERROR
MCPYDERR:
       CALL    EPRINT
       DB      CR,LF,'Disk Full',0
       JMP     CPM

;  Directory Full Error
dirful:
       call    eprint
       db      cr,lf,'Directory Full',0
       jmp     cpm

;  TEST FOR EXISTENCE REQUIREMENT AND GET USER RESPONSE
EATEST:
       LDA     EXIST   ; EXISTENCE TEST ON?
       ORA     A       ; 0=NO
       JZ      EAT1
       CALL    EPRINT
       DB      ' -- (Y/N)? ',0
       CALL    CIN     ; GET RESPONSE
       CALL    CAPS
       CPI     CR      ; YES?
       JZ      EAT1    ; COPY IF SO
       CALL    COUT
       CPI     'N'     ; NO?
       JNZ     EAT1    ; COPY IF NOT NO
       XRA     A       ; ZERO FOR NOT APPROVED
       RET
EAT1:
       MVI     A,0FFH  ; SET NZ FOR APPROVED
       ORA     A
       ; SET FLAGS
       RET
;
;  MAKE DESTINATION FCB ENTRY R/W AND DIR
;
DRW:
       PUSH    D
       LHLD    FCBD    ; CLEAR ATTRIBUTES OF DEST
       LXI     D,9
       DAD     D
       POP     D
       MOV     A,M     ; GET IT
       ANI     7FH     ; CLEAR IT
       MOV     M,A
       INX     H       ; SAME TO NEXT
       MOV     A,M     ; GET IT AND CLEAR IT
       ANI     7FH
       MOV     M,A
       RET
DESTRW:
       CALL    DRW     ; MAKE ATTRIBUTES R/W AND NON-SYS
       LDED    FCBD    ; SET ATTRIBUTES
       MVI     C,30
       CALL    BDOS
       RET

;
;  LOAD BUFFER PTED TO BY HL FROM FILE WHOSE FCB IS PTED TO BY DE
;    ON OUTPUT, BCNT=NUMBER OF BLOCKS LOADED (UP TO 128) AND
;    CONT=0 IF DONE OR 128 IF NOT DONE
;
LOAD:
       XRA     A       ; A=0
       STA     BCNT    ; SET BLOCK COUNT
       STA     CONT    ; TURN OFF CONTINUATION FLAG

;  MAIN COPY LOOP
MCPY:
       CALL    SETDMA  ; SET DMA TO BLOCK PTED TO BY HL
       CALL    F$READ  ; READ BLOCK
       ORA     A       ; END OF FILE?
       RNZ             ; RETURN
       PUSH    D       ; SAVE PTR TO FCB
       XCHG            ; SAVE PTR TO DESTINATION BUFFER IN DE
       LHLD    BDOSE+1 ; GET TOP OF TPA
       XCHG            ; ... IN DE, DEST IN HL
       MOV     A,H     ; IF SAME PAGE, WE ARE IN OVERFLOW
       CMP     D       ; D MUST BE > H
       JNC     TPAOVFL ; OVERFLOW IF D<=H
       MVI     B,128   ; UPDATE CRC FOR 128 BYTES
MCPYCRC:
       MOV     A,M     ; GET BYTE
       CALL    CRCUPD  ; UPDATE CRC
       INX     H       ; PT TO NEXT
       DCR     B       ; COUNT DOWN
       JNZ     MCPYCRC
       POP     D       ; GET PTR TO FCB
       LDA     BCNT    ; GET BLOCK COUNT
       INR     A       ; INCREMENT IT
       STA     BCNT    ; SET IT
       MOV     B,A     ; BLOCK COUNT IN B
       LDA     PAGLIM  ; GET PAGE LIMIT
       ADD     A       ; DOUBLE IT FOR BLOCKS
       CMP     B       ; BUFFER FULL?
       JNZ     MCPY
       STA     CONT    ; SET CONTINUATION FLAG
       RET

;
;  SET DMA ADDRESS TO THAT PTED TO BY HL
;
SETDMA:
       PUSH    H       ; SAVE REGS
       PUSH    D
       PUSH    B
       XCHG            ; ADDRESS IN DE
       MVI     C,SDMA
       CALL    BDOSE
       POP     B       ; RESTORE REGS
       POP     D
       POP     H
       RET

;
;  VERIFY PHASE
;
MCOPYV:
       LDA     QUIET   ; CHECK FOR QUIET
       ORA     A       ; NZ=QUIET
       JNZ     MCPYV
       CALL    EPRINT
       DB      '  Verify ...',0
MCPYV:
       CALL    CRCCLR  ; CLEAR CRCK VALUE
       CALL    LOGD    ; LOG IN DESTINATION
       LDED    FCBD    ; CLEAR DESTINATION FCB
       CALL    INITFCB ; INIT FCB
       CALL    F$OPEN  ; OPEN FILE

;  **** MAIN VERIFY LOOP ****
VERLOOP:
       LHLD    WORKBF  ; LOAD INPUT BUFFER FROM DESTINATION
       LDED    FCBD
       CALL    LOAD    ; LOAD AND COMPUTE CRC VALUE
       LDA     BCNT    ; DONE IF NO BYTES LOADED
       ORA     A
       JZ      VERCRC
       LDA     CONT    ; CONTINUE?
       ORA     A       ; 0=NO
       JNZ     VERLOOP
;  VERIFY DONE
VERCRC:
       LHLD    CRCVAL  ; GET OLD CRC VALUE
       XCHG            ; ... IN DE
       CALL    CRCDONE ; UPDATE COMPLETE
       CALL    COMPHD  ; COMPARE HL TO DE
       JZ      PRDONE  ; PRINT DONE MESSAGE OR FALL THRU TO ERROR MSG

;  VERIFY ERROR
VERERR:
       LXI     H,VERCNT        ; INCREMENT ERROR COUNT
       INR     M
       CALL    EPRINT
       DB      ' Error',0
       RET

;
;  **** MCOPY Utilities ****
;

;
;  CHECK TO SEE IF USER WANTS TO CONTINUE
;
SAKCHK:
       CALL    EPRINT
       DB      ' ^C to Quit - ',0
       CALL    CIN     ; GET RESPONSE
       CALL    CRLF    ; NEW LINE
       CALL    CAPS    ; CAPITALIZE
       CPI     'C'-'@' ; ^C?
       RET
;
;  ALLOW USER TO INSPECT FILES FOR COPY
;    FIRST FILE NAME PTED TO BY HL, BC = NUMBER OF FILES
;    ON EXIT, BC = NUMBER OF SELECTED FILES
;
INSPF:
       CALL    EPRINT
       DB      CR,LF,' Inspect -- '
       db      'Yes, No (def), Skip Rest'
       db      0
       PUSH    H       ; SAVE PTR TO FIRST FILE
       PUSH    B       ; SAVE FILE COUNT
       LXI     D,ESIZE ; ENTRIES ARE ESIZE BYTES APART
INSPF0:
       MOV     A,M     ; MARK FILE FOR NO COPY
       ANI     7FH     ; CLEAR MSB FOR NO COPY
       MOV     M,A
       DAD     D       ; PT TO NEXT
       DCX     B       ; COUNT DOWN
       MOV     A,B     ; DONE?
       ORA     C
       JNZ     INSPF0
       POP     B       ; RESTORE AND SAVE AGAIN
       POP     H
       PUSH    H
       PUSH    B
INSPF1:
       PUSH    H       ; SAVE PTR TO FILE
       INX     H       ; PT TO FN
       CALL    CRLF    ; NEW LINE
       CALL    PRFN    ; PRINT IT
       POP     H       ; GET PTR TO FILE
       CALL    EPRINT
       DB      ' - (Y/N/S)? ',0
       CALL    CIN     ; GET RESPONSE
       CALL    CAPS    ; CAPITALIZE
       CALL    COUT    ; ECHO
       CPI     'S'     ; SKIP?
       JZ      INSPFA
       CPI     'Y'     ; Yes?
       JNZ     INSPF2
       MOV     A,M     ; GET USER NUMBER
       ORI     80H     ; MARK FILE
       MOV     M,A     ; SET USER NUMBER
INSPF2:
       LXI     D,ESIZE ; PT TO NEXT FILE
       DAD     D
       DCX     B       ; COUNT DOWN
       MOV     A,B     ; DONE?
       ORA     C
       JNZ     INSPF1
INSPFA:
       POP     B       ; GET COUNT
       POP     H       ; GET PTR TO FIRST FILE
       JMP     DIRPACK ; REPACK DIRECTORY

;
;  LOG IN SOURCE USER/DISK
;
LOGS:
       LDA     SUSER   ; USER
       MOV     C,A     ; ... IN C
       LDA     SDISK   ; DISK
       MOV     B,A     ; ... IN B
       JMP     LOGUD   ; LOG IN USER/DISK

;
;  LOG IN DESTINATION USER/DISK
;
LOGD:
       LDA     DUSER   ; USER
       MOV     C,A     ; ... IN C
       LDA     DDISK   ; DISK
       MOV     B,A     ; ... IN B
       JMP     LOGUD   ; LOG IN USER/DISK

;
;  PRINT FILE NAME
;
PRFN:
       PUSH    H       ; SAVE REGS
       PUSH    B
       MVI     B,8     ; PRINT 8 CHARS
       CALL    PRFN1
       MVI     A,'.'   ; DOT
       CALL    COUT
       MVI     B,3     ; PRINT 3 CHARS
       CALL    PRFN1
       POP     B       ; GET REGS
       POP     H
       RET
PRFN1:
       MOV     A,M     ; GET CHAR
       INX     H       ; PT TO NEXT
       CALL    COUT    ; PRINT IT
       DCR     B       ; COUNT DOWN
       JNZ     PRFN1
       RET

;
;  **** BUFFERS ****
;

;  POINTERS
MFPTR:  DS      2       ; PTR TO FIRST CHAR OF NEXT FN SPEC
NXTPTR: DS      2       ; PTR TO NEXT FN SPEC IN LINE
WORKBF: DS      2       ; PTR TO BEGINNING OF WORK BUFFER

;  FLAGS COPIED FROM DEFAULTS
VERFLG: DS      1       ; VERIFY
INSP:   DS      1       ; INSPECT
QUIET:  DS      1       ; QUIET
NCOPY:  DS      1       ; MULTIPLE COPY

;  DISKS AND USERS
CDISK:  DS      1       ; CURRENT DISK
SDISK:  DS      1       ; SOURCE DISK
SUSER:  DS      1       ; SOURCE USER
DDISK:  DS      1       ; DESTINATION DISK
DUSER:  DS      1       ; DESTINATION USER

;  CRC VALUE
CRCVAL: DS      2       ; CRC CHECK VALUE

;  FCBS
FCBS:   DS      2       ; SOURCE FCB
FCBD:   DS      2       ; DESTINATION FCB
FCBT:   DS      2       ; PTR TO TEMPORARY FCB FOR ATTRIBUTE SETTINGS

;  COUNTS AND FLAGS
PAGLIM: DS      1       ; MAX NUMBER OF PAGES IN WORK BUFFER
LSTCPY: DS      1       ; LAST FILE WAS COPIED FLAG
EXIST:  DS      1       ; TEST FOR EXISTENCE FLAG
VERCNT: DS      1       ; ERROR COUNT
BCNT:   DS      1       ; BLOCK COUNT
CONT:   DS      1       ; CONTINUE FLAG (0=NO, 0FFH=YES)

;  DYNAMIC BUFFERS
INLINE:
       DS      2       ; INPUT LINE BUFFER
FREEBUF:
       DS      2       ; FREE SPACE BUFFER
STACK:
       DS      2       ; OPSYS STACK PTR

       END