; FLOPPY DISK COPY PROGRAM
; COPYRIGHT 1982, G. YOUNG, INC.
; UPDATED 10/9/82
*
* TO EXECUTE, ENTER THE PROGRAM NAME FOLLOWED BY THE FILE MASK:
*       A>FLOPCOPY *.COM
*
* THE PRIMARY PURPOSE OF THIS PROGRAM IS TO COPY A FLOPPY DISK ON
* SYSTEMS THAT HAVE ONLY ONE FLOPPY DISK AND A HARD DISK.  THIS IS DONE
* BY READING THE DIRECTORY ON THE FLOPPY AND CREATING A LIST OF FILES TO
* TO BE COPIED, THEN COPY ALL THE FILES TO THE HARD DISK. A BLANK
* DISK IS THEN INSERTED AND THE FILES ARE COPIED TO THE BLANK FLOPPY
* AND DELETED OFF THE HARD DISK.  THIS PROCEDURE IS USED INSTEAD OF
* THE "SINGLE.COM" PROGRAM BECAUSE THERE IS MORE THAN 1 FILE SO "SINGLE"
* CANNOT BE USED WITHOUT MODIFICATIONS AND EVEN THEN MANY, MANY DISKETTE
* MOUNTS WOULD HAVE TO BE DONE.  THIS WAY, ONLY 1 DISKETTE MOUNT NEEDS
* TO BE DONE FOR EACH DISK THAT IS COPIED.
* 8/26/82...IF DISKETTE IS FULL, ASK FOR ANOTHER, DO NOT ABORT
* THIS ALLOWS COPYING DOUBLE DENSITY DISKS ON TO MULTIPLE SINGLE DENSITY DISKS
* 10/9/82 ASK FOR SOURCE/DESTINATION/HARD DISK DRIVE # AND NOT HARD CODED

* WRITTEN BY GARY YOUNG, BOX 3218, NO. HOLLYWOOD, CA 91609

       TITLE   '*** FLOPPY DISK COPY PROGRAM ***'
BDOS    EQU     5
RECSIZE EQU     12
BOOT    EQU     0
       ORG     100H
       JMP     START
       DB      'COPYRIGHT 1982, G. YOUNG, INC.'
START   LXI     SP,STACK+80
       LDA     5DH             ;SEE IF A MASK WAS ENTERED
       CPI     20H             ;BUFFER WILL BE BLANK IF NOT
       JZ      NOMASK
       MVI     C,0DH           ;DISK RESET
       CALL    BDOS
       LXI     H,RAM           ;SET UP TABLE ADDRESS
       SHLD    TABADDR
       LXI     H,0000H
       SHLD    TABCNT
       XRA     A               ;THE TABLE REFERS TO THE REMAINING AREA
       STA     ABORT           ;OF RAM TO HOLD AS MANY DIRECTORY ENTRIES
       STA     EOF             ;AS POSSIBLE
       LXI     D,SIGNON        ;PRINT SIGNON MESSAGE
       CALL    OUTPUT
       LXI     D,SRCFLPMSG     ;GET SOURCE DRIVE
       CALL    QUESTION
       ORA     A
       JZ      QUEST2
       LDA     INREC
       STA     SRCFLP
QUEST2 LXI      D,DESTFLPMSG
       CALL    QUESTION
       ORA     A
       JZ      QUEST3
       LDA     INREC
       STA     SRCFLP
QUEST3  LXI     D,HDMSG
       CALL    QUESTION
       ORA     A
       JZ      QUEST4
       LDA     INREC
       STA     HDTEMP
QUEST4  LDA     HDTEMP
       ANI     0FH
       STA     HDTEMP
       LDA     SRCFLP
       ANI     0FH
       STA     SRCFLP
       LDA     DESTFLP
       ANI     0FH
       STA     DESTFLP
       CALL    EXTRACT         ;GET DIRECTORY ENTRIES
       LDA     ABORT           ;SEE IF TOO MANY ENTRIES FOR MEMORY SIZE
       ORA     A               ;SHOULD BE ZERO TO BE OK
       JZ      NOMORE
       LXI     D,TABFULL       ;REPORT WILL NOT BE COMPLETE
       CALL    OUTPUT          ;MEMORY FULL ERROR
NOMORE  CALL    SORT
       CALL    KILLDUPS        ;REMOVE ENTRIES FROM MULTIPLE EXTENTS
       LDA     SRCFLP          ;FLOPPY IS THE SOURCE DRIVE
       STA     SRCE
       LDA     HDTEMP          ;HARD DISK IS THE DESTINATION
       STA     DEST
       CALL    CPYFILE
AGAIN   LXI     D,MNTBLANK
       CALL    QUESTION
       MVI     C,0DH
       CALL    BDOS
       LDA     HDTEMP
       STA     SRCE
       LDA     DESTFLP
       STA     DEST
       CALL    CPYFILE
MSG3    LXI     D,ANOTHER
       CALL    QUESTION
       ORA     A
       JZ      WIPEOUT
       LDA     INREC
       CPI     'N'
       JZ      WIPEOUT
       CPI     'Y'
       JNZ     MSG3
       JMP     AGAIN
WIPEOUT CALL    ERASE
       JMP     BOOT            ;RESET AND RETURN TO CPM
NOMASK  LXI     D,ERRNOMSK
       CALL    OUTPUT
       JMP     BOOT
*
* THE EXTRACT ROUTINE SCANS THE DIRECTORY ON THE FLOPPY DISK
* AND CREATES A LIST OF FILES TO BE COPIED.  AFTERS SORTING,
* DUPLICATE ENTRIES RESULTING FROM MULTIPLE EXTENT ENTRIES WILL
* BE REMOVED.
*
* THE DUMMYFCB SETS UP A SKELETON TO READ ALL FILES ON THE DIRECTORY
* TO BE MODIFIED BY THE SKELATON SETUP IN THE CALL (EX: FLOPCOPY *.ASC).
*
DUMMYFCB        DB      0,'????????????',0,0,0,0,0,0,0,0,0
               DB      0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
EXTRACT EQU     $
       LXI     D,DUMMYFCB      ;COPY THE MASK SET UP BY CCP
       LXI     H,5CH           ;OVER THE DUMMY FCB TO LIMIT WHAT IS COPIED
       MVI     B,12
       CALL    MOVE
       LDA     SRCFLP          ;FORCE THE DRIVE TO BE FLOPPY DRIVE
       STA     DUMMYFCB
       ORI     40H
       STA     RDISK
       LXI     D,DISKBUF       ;SET A DMA ADDRESS FOR DIRECTORY ENTRIES
       MVI     C,1AH
       CALL    BDOS            ;SET DMA
       LXI     D,DUMMYFCB      ;GET FIRST DIRECTORY ENTRY
       MVI     C,11H
       CALL    BDOS            ;GET DIRECTORY
       INR     A
       RZ
       JMP     GETFCB          ;EXTRACT DATA FROM FCB
NEXTFCB LXI     D,DUMMYFCB      ;GET NEXT ENTRY AFTER THE FIRST
       MVI     C,12H
       CALL    BDOS
       INR     A
       RZ
GETFCB  LXI     D,32            ;EACH ENTRY IS 32 BYTES LONG
       LXI     H,DISKBUF       ;DIRECTORY RECORD IS IN DISKBUF
       CPI     1               ;FIRST ENTRY IN RECORD???
       JZ      FORMREC         ;YES
       DAD     D               ;ADD 32 TO ADDRESS IN RECORD
       CPI     2               ;SECOND ENTRY IN RECORD???
       JZ      FORMREC
       DAD     D               ;ADD 64 TO ADDRESS OF RECORD
       CPI     3               ;THIRD ENTRY IN RECORD???
       JZ      FORMREC
       DAD     D               ;ADD 96 TO ADDRESS OF RECORD
FORMREC INX     H               ;PASS DRIVE BYTE
       LXI     D,RFILE         ;MOVE FILE NAME
       MVI     B,8             ;MOVE 8 CHARACTERS
       CALL    MOVE
       LXI     D,8             ;POSITION PAST NAME TO TYPE
       DAD     D
       LXI     D,RTYPE         ;MOVE TYPE
       MVI     B,3             ;MOVE 3 CHARACTERS
       CALL    MOVE
       INX     H               ;POSITION TO THE EXTENT NUMBER
       INX     H
       INX     H
       LXI     H,RTYPE         ;STRIP OFF THE HIGH BIT SET BY
       MVI     B,11            ;THE VERSION PROGRAM
STRIP   MOV     A,M
       ANI     07FH
       MOV     M,A
       INX     H
       DCR     B
       JNZ     STRIP
       LXI     H,RTYPE         ;SKIP OVER JUNK DISK ENTRIES
       MVI     B,11            ;CONTAINING NONPRINTING CHARACTERS
NONPRNT MOV     A,M
       CPI     20H             ;ANY CHAR LESS THAN A BLANK
       JC      NEXTFCB         ;GO TO NEXT ONE IF SO
       INX     H
       DCR     B
       JNZ     NONPRNT
FIRSTEXT        EQU     $       ;GOT THE FIRST EXTENT
       LHLD    TABADDR         ;GET MEMORY TABLE ADDRESS
       LXI     D,RTYPE         ;GET MEMORY RECORD ADDRESS
       XCHG                    ;RTYPE (HL) TO TABADDR (DE)
       MVI     B,RECSIZE       ;MOVE RECSIZE BYTES
       CALL    MOVE
       LHLD    TABADDR         ;INCREMENT FOR NEXT ENTRY
       LXI     D,RECSIZE               ;RECSIZE BYTES IN RECORD
       DAD     D
       SHLD    TABADDR         ;SAVE NEW ADDRESS
       MVI     M,1AH           ;SET AN END-OF-TABLE INDICATOR
       LHLD    TABCNT          ;GET RECORD COUNT
       INX     H
       SHLD    TABCNT          ;INCREMENT RECORD COUNT
       LHLD    TABADDR         ;SEE IF NEW ADDRESS IS GREATER
       XCHG                    ;THAN THE TOP OF TPA-128
       LHLD    BDOS+1          ;HL=TOP...DE=TABLE+RECSIZE
       LXI     B,-128
       DAD     B               ;SUBTRACT 128 FROM TOP
       CALL    COMPREG         ;COMPARE REGISTER VALUES
       JNC     NEXTFCB         ;THERE IS ROOM FOR MORE
       MVI     A,1             ;NO MORE ROOM...ABORT NOW
       STA     ABORT
       RET

* COPY THE FILE FROM ONE DISK TO ANOTHER
* THIS CODE WAS TAKEN FROM BACKUP.ASM SO HDFCB REFERS TO THE SOURCE DISK
* AND FPFCB REFERS TO THE DESTINATION DISK REGARDLESS OF FLOPPY OR HARD.

CPYFILE EQU     $
       LHLD    ADDR2           ;GET THE CURRENT END OF TABLE
       INX     H               ;PLUS ONE FOR THE START OF THE
       SHLD    ADDR3           ;READ/WRITE BUFFER
       LXI     H,RAM           ;SET THE ADDRESS OF THE FIRST ENTRY
       SHLD    ADDR1
       JMP     PASSMOVE
NEXTFILE        EQU     $
       LHLD    ADDR1
       LXI     D,RECSIZE
       DAD     D
       SHLD    ADDR1
PASSMOVE LHLD   ADDR1
       MOV     A,M
       CPI     1AH
       RZ
FORMFCB LXI     D,HDFCB         ;CLEAR THE FCB
       MVI     B,36
       XRA     A
ZEROFCB STAX    D
       INX     D
       DCR     B
       JNZ     ZEROFCB
       LHLD    ADDR1           ;GET ADDRESS OF TABLE ENTRY
       LXI     D,HDTYPE        ;MOVE IN THE TYPE
       MVI     B,3
       CALL    MOVE
       INX     H
       INX     H               ;POSITION TO NAME
       INX     H
       LXI     D,HDFILE        ;MOVE THE FILE NAME
       MVI     B,8
       CALL    MOVE
       LXI     D,8
       DAD     D               ;POSITION TO DISK ID, A:, B: ETC
       LDA     SRCE
       STA     HDFCB
       LXI     D,FPFCB         ;COPY THE HDFCB TO THE FLOPPY FCB
       LXI     H,HDFCB
       MVI     B,36
       CALL    MOVE
       LDA     DEST
       STA     FPFCB
       LHLD    ADDR1
       CALL    FORMAT
       LXI     D,PRNTREC
       CALL    OUTPUT
       LXI     D,HDFCB         ;OPEN THE SOURCE FILE DRIVE
       MVI     C,0FH
       CALL    BDOS
       INR     A
       JZ      NOTFOUND
       LXI     D,FPFCB         ;SEE IF THE FILE IS ON THE DESTINATION DRV
       MVI     C,0FH
       CALL    BDOS            ;DUMMY OPEN
       INR     A
       JZ      MAKEIT          ;NOT THERE...MAKE IT
       LHLD    ADDR1           ;SET THE DRIVE BYTE (#12) TO '*' TO MARK
       LXI     D,RECSIZE-1     ;THE FILE AS ALREADY BEING THERE SO THAT
       DAD     D               ;THE ERASE ROUTINE WILL NOT ERASE IT
       MVI     A,'*'           ;LATER WHEN REMOVING FILES
       MOV     M,A
HUH2    LXI     D,ALRDYEXT      ;FILE ALREADY EXISTS ON DESTINATION.  ASK IF
       CALL    QUESTION        ;YOU WANT TO COPY THE NEW SOURCE OVER THE
       CPI     1               ;EXISTING COPY
       JNZ     HUH2
       LDA     INREC
       CPI     'Y'
       JZ      ERAIT           ;ERASE IT
       CPI     'N'             ;NO, USE THE COPY ALREADY ON THE DESTINATION
       JZ      NEXTFILE
       JMP     HUH2
ERAIT   EQU     $
       LXI     D,FPFCB         ;DELETE THE FILE ON FLOPPY IF IT
       MVI     C,13H           ;EXISTS
       CALL    BDOS
MAKEIT  EQU     $
       LXI     D,FPFCB         ;CREATE THE FILE ON FLOPPY
       MVI     C,16H
       CALL    BDOS            ;MAKE FILE
       INR     A
       JZ      DISKFULL
COPYLOOP        CALL    LOADBUFF        ;LOAD MEMORY WITH FILE
       CALL    WRITEBUF                ;WRITE MEMORY FILE
       LDA     EOF
       CPI     1
       JZ      ENDOFFILE
       CPI     2
       JZ      DISKFULL
       JMP     COPYLOOP
ENDOFFILE       LXI     D,FPFCB         ;CLOSE FLOPPY FILE
       MVI     C,10H
       CALL    BDOS                    ;CLOSE
       JMP     NEXTFILE
DISKFULL        LXI     D,FPFCB
       MVI     C,13H           ;DELETE FILE ON FLOPPY
       CALL    BDOS
       LXI     D,DSKFULL       ;PRINT DISK FULL MESSAGE
       CALL    QUESTION
       MVI     C,0DH           ;RESET DISKS
       CALL    BDOS
       JMP     FORMFCB
NOTFOUND LHLD   ADDR1
       CALL    FORMAT
       LXI     D,NFMSG
       CALL    OUTPUT
       LXI     D,PRNTREC
       CALL    OUTPUT
       RET

ERASE   EQU     $
HUH     LXI     D,ERAMSG        ;ASK IF YOU WANT THEM ERASED
       CALL    QUESTION
       ORA     A
       JZ      ERAALL
       LDA     INREC
       CPI     'Y'
       JZ      ERAALL
       CPI     'N'
       RZ
       JMP     HUH
ERAALL  EQU     $
       LXI     H,RAM           ;SET THE ADDRESS OF THE FIRST ENTRY
       SHLD    ADDR1
       JMP     PASSMOVEX
ERANEXT EQU     $
       LHLD    ADDR1
       LXI     D,RECSIZE
       DAD     D
       SHLD    ADDR1
PASSMOVEX LHLD  ADDR1
       MOV     A,M
       CPI     1AH
       RZ
       LXI     D,RECSIZE-1     ;SEE IF THE DRIVE INDICATOR IS SET TO '*'
       DAD     D               ;MEANING THE FILE WAS ALREADY ON THE HARD DISK
       MOV     A,M             ;SO IT WILL NOT BE DELETED
       CPI     '*'
       JZ      ERANEXT         ;YES, IT WAS THERE, SO DO NOT ERASE IT
FORMFCBX        LXI     D,HDFCB         ;CLEAR THE FCB
       MVI     B,36
       XRA     A
ZEROFCBX        STAX    D
       INX     D
       DCR     B
       JNZ     ZEROFCBX
       LHLD    ADDR1
       LXI     D,HDTYPE        ;MOVE IN THE TYPE
       MVI     B,3
       CALL    MOVE
       INX     H
       INX     H               ;POSITION TO NAME
       INX     H
       LXI     D,HDFILE        ;MOVE THE FILE NAME
       MVI     B,8
       CALL    MOVE
       LXI     D,8
       DAD     D               ;POSITION TO DISK ID, A:, B: ETC
       LDA     HDTEMP
       STA     HDFCB
       LHLD    ADDR1
       CALL    FORMAT
       LXI     D,PRNTREC
       CALL    OUTPUT
       LXI     D,HDFCB         ;DELETE THE FILE ON FLOPPY IF IT
       MVI     C,13H           ;EXISTS
       CALL    BDOS
       JMP     ERANEXT

* THE FOLLOWING ROUTINE IS A BUBBLE SORT.  IN PSEUDOBASIC
* IS WOULD BE DONE AS FOLLOWS:
*SORT   ADDR1=BOTTOM(RAM)-RECSIZE (RECSIZE IS COUNT OF BYTES IN ONE RECORD)
*       MAX1=0
*LOOP1  MAX1=MAX1+1
*       ADDR1=ADDR1+RECSIZE     (FIRST TIME THIS WOULD BE THE BOTTOM)
*       IF MAX1>TABCNT-1 THEN GO TO SORTED
*       ADDR2=ADDR1
*       MAX2=MAX1
*LOOP2  MAX2=MAX2+1
*       ADDR2=ADDR2+RECSIZE
*       IF MAX2>TABCNT THEN GO TO LOOP1
*       IF TABLE(ADDR1)<TABLE(ADDR2) THEN GO TO LOOP2
*       TEMP=TABLE(ADDR1)
*       TABLE(ADDR1)=TABLE(ADDR2)
*       TABLE(ADDR2)=TEMP
*       GO TO LOOP2
*SORTED RETURN
*
*NOW THAT THIS LOGIC IS INTUITIVELY OBVIOUS, HERE'S THE NOT SO
*OBVIOUS CODE:

SORT
       LXI     H,0000          ;INITIALIZE COUNT
       SHLD    MAX1            ;SINCE IT WILL DECREMENT FIRST
       LXI     D,-RECSIZE      ;SUBTRACT RECSIZE FROM THE BEGINNING
       LXI     H,RAM           ;OF THE TABLE SINCE IT WILL ADD
       DAD     D               ;RECSIZE TO IT FIRST
       SHLD    ADDR1
LOOP1   LHLD    ADDR1           ;GET THE CURRENT ADDRESS
       LXI     D,RECSIZE               ;INCREMENT IT BY RECSIZE
       DAD     D               ;PAST THE NEXT ENTRY
       SHLD    ADDR1
       LHLD    MAX1            ;SEE IF THE COUNT HAS REACHED THE LIMIT
       INX     H               ;INCREMENT THE COUNTER
       SHLD    MAX1
       XCHG                    ;MOVE TO DE TO COMPARE TO LIMIT
       LHLD    TABCNT
       DCX     H               ; IS MAX1 > TABCNT - 1 ???
       CALL    COMPREG         ;COMPARE DE (CURRENT COUNT) TO HL (TABCNT-1)
       JC      SORTED          ;FINISHED WHEN MAX1 > TABCNT-1
       LHLD    ADDR1           ;SETUP FOR THE INNER LOOP
       SHLD    ADDR2
       LHLD    MAX1
       SHLD    MAX2
LOOP2   LHLD    ADDR2           ;START WITH ONE ENTRY GREATER THEN
       LXI     D,RECSIZE               ;THE OUTER LOOP AND INCREMENT
       DAD     D               ;BY RECSIZE EACH TIME PAST THE NEXT RECORD
       SHLD    ADDR2           ;POINTER TO THE CURRENT RECORD
       LHLD    MAX2            ;LIKEWISE SEE IF THE COUNTER FOR THE
       INX     H
       SHLD    MAX2
       XCHG
       LHLD    TABCNT
       CALL    COMPREG
       JC      LOOP1           ;WHEN FINISHED, INCREMENT OUTER LOOP
       LHLD    ADDR2
       XCHG                    ;ADDR2 NOW IN DE
       LHLD    ADDR1
       CALL    COMPARE         ;COMPARE STRINGS POINTED TO BY DE/HL
       JNC     LOOP2           ;TABLE(HL) < TABLE (DE)
* EXCHANGE THE TWO ENTRIES VIA A TEMPORARY RECORD
       LXI     D,TEMP
       LHLD    ADDR1
       MVI     B,RECSIZE
       CALL    MOVE            ;TEMP=TABLE(ADDR1)
       XCHG                    ;ADDR1=DESTINATION
       LHLD    ADDR2           ;ADDR2=SOURCE
       CALL    MOVE            ;TABLE(ADDR1)=TABLE(ADDR2)
       XCHG                    ;ADDR2 = DESTINATION
       LXI     H,TEMP
       CALL    MOVE            ;TABLE(ADDR2)=TEMP
       JMP     LOOP2
SORTED  RET                     ;FINISHED SORTING



KILLDUPS EQU    $       ;KILL DUPLICATE ENTRIES FROM MULTIPLE EXTENTS
       LXI     H,RAM
       SHLD    ADDR1                   ;SET THE START OF THE TABLE
NEXTEQUAL       LHLD    ADDR1           ;CHECK EACH ENTRY AGAINST THE NEXT
       LXI     D,RECSIZE
       DAD     D
       SHLD    ADDR2
NEXTCHK MOV     A,M                     ;CHECK FOR END OF TABLE
       CPI     1AH
       RZ
       XCHG
       LHLD    ADDR1                   ;COMPARE ADDR1 WITH ADDR2
       MVI     B,RECSIZE
       CALL    COMPARE
       JNZ     SAVELAST
       PUSH    H                       ;SAVE CURRENT POSITION
       LHLD    ADDR2                   ;SET UP FOR MOVING LIST
       SHLD    ADDR1                   ;DOWN ONE ENTRY
       LXI     D,RECSIZE
       DAD     D                       ;MOVE THIRD ENTRY TO THE SECOND
       SHLD    ADDR2
       CALL    MOVELIST
       LHLD    TABCNT
       DCX     H
       SHLD    TABCNT
       POP     H                       ;RESTORE CURRENT POSITION
       SHLD    ADDR1                   ;NOW COMPARE 1ST & 3RD
       JMP     NEXTEQUAL
SAVELAST        LHLD    ADDR2           ;MAKE NEW ONE THE CURRENT ONE
       SHLD    ADDR1
       JMP     NEXTEQUAL               ;COMPARE

MOVELIST        LHLD    ADDR2           ;MOVE THE TABLE FROM ADDR2 DOWN
       XCHG                            ;TO ADDR1 THEREBY ELIMINATING
       LHLD    ADDR1                   ;UNWANTED ENTRIES
MOVENEXT        LDAX    D               ;AND MAKING MORE ROOM FOR THE
       MOV     M,A
       CPI     1AH
       RZ
       INX     H
       INX     D
       JMP     MOVENEXT


* ASSORTED ROUTINES


LOADBUFF        EQU     $       ;THIS ROUTINE LOADS AS MUCH OF
       LXI     H,0000          ;MEMORY WITH THE FILE AS POSSIBLE
       SHLD    MAX1
       LHLD    ADDR3           ;NEW TOP OF TABLE +2
       SHLD    TEMP
       XRA     A
       STA     EOF             ;CLEAR EOF FLAG
LOADNEXT        LHLD    TEMP
       XCHG                    ;SET DMA ADDRESS
       MVI     C,1AH
       CALL    BDOS
       LXI     D,HDFCB         ;READ HARD DISK
       MVI     C,14H
       CALL    BDOS
       ORA     A
       JNZ     HDEOF           ;EOF?
       LHLD    MAX1
       INX     H               ;INCREMENT RECORD COUNT
       SHLD    MAX1
       LHLD    TEMP            ;SEE IF NEXT RECORD WOULD EXCEED THE
       LXI     D,128           ;TPA AREA
       DAD     D
       SHLD    TEMP
       DAD     D               ;WILL THE NEXT RECORD OVERWRITE BDOS?
       XCHG
       LHLD    BDOS+1          ;FIND THE TOP OF MEMORY
       CALL    COMPREG         ;COMPARE REGISTERS
       RC                      ;RETURN IF MEMORY ALREADY FULL
       JMP     LOADNEXT        ;GET ANOTHER RECORD
HDEOF   MVI     A,1             ;SET FILE EOF
       STA     EOF
       RET

WRITEBUF        EQU     $       ;WRITE FROM THE MEMORY BUFFER
       LHLD    ADDR3
       SHLD    TEMP
       LHLD    MAX1            ;ALLOW FOR FILES THAT HAVE NO
       LXI     D,0000          ;RECORDS SUCH AS RESTART
       CALL    COMPREG
       RZ
WRITENEXT       LHLD    TEMP
       XCHG                    ;SET DMA ADDRESS
       MVI     C,1AH
       CALL    BDOS
       LXI     D,FPFCB
       MVI     C,15H           ;WRITE SEQUENTIAL
       CALL    BDOS
       ORA     A
       JNZ     FPFULL          ;FLOPPY DISK FULL
       LHLD    MAX1            ;DECREASE RECORD COUNT
       DCX     H
       SHLD    MAX1
       LXI     D,0000          ;CHECK FOR NO MORE TO WRITE
       CALL    COMPREG
       RZ
       LHLD    TEMP
       LXI     D,128           ;INCREMENT WRITE ADDRESS
       DAD     D
       SHLD    TEMP
       JMP     WRITENEXT
FPFULL  MVI     A,2             ;FULL DISKETTE
       STA     EOF
       RET

FORMAT  LXI     D,PRNTYPE       ;FORMAT THE ENTRY FROM THE TABLE
       MVI     B,3             ;FORMAT TO THE PRINT FORMAT
       CALL    MOVE            ;THE TABLE ADDRESS IS ASSUMMED TO BE
       LXI     D,3             ;IN HL. FIRST MOVE THE TYPE
       DAD     D               ;NOW POSITION TO THE FILE NAME
       LXI     D,PRNFILE       ;MOVE THE FILE NAME
       MVI     B,8
       CALL    MOVE
       LXI     D,8             ;POSITION TO THE DISK ID
       DAD     D
       LDA     SRCE
       ORI     40H
       STA     PRNTREC
       MVI     A,':'
       STA     PRNTREC+1
       MVI     A,'.'
       STA     PRNTYPE-1
       RET

COMPREG MOV     A,H             ;COMPARE HL TO DE
       CMP     D
       RNZ
       MOV     A,L
       CMP     E
       RET

OUTPUT  PUSH    D               ;PUT OUT A CRLF
       LXI     D,CRLF
       MVI     C,09
       CALL    BDOS
       POP     D               ;NOW PUT OUT THE MESSAGE
OUT1    MVI     C,09
       JMP     BDOS


QUESTION        CALL    OUTPUT  ;PUT OUT THE QUESTION
       LXI     D,INBUF
       MVI     C,0AH           ;INPUT THE REPLY
       CALL    BDOS
       LDA     INCNT           ;SEE IF ANYTHING WAS ENTERED
       RET

MOVE    PUSH    H               ;MOVE DATA POINTED TO IN HL
       PUSH    D               ;TO THE AREA POINTED TO IN DE
       PUSH    B               ;BY THE BYTE COUNT IN B
MOVE1   MOV     A,M
       ANI     7FH             ;RESET THE HIGH ORDER BIT BECAUSE IT
                               ;MAY HAVE BEEN TURNED ON FOR THE TYPE
       STAX    D
       INX     H
       INX     D
       DCR     B
       JNZ     MOVE1
       POP     B               ;RESTORE THE TOTAL ENVIRONMENT
       POP     D
       POP     H
       RET

COMPARE PUSH    H               ;COMPARE THE STRINGS POINTED TO IN HL
       PUSH    D               ;TO THE STRING POINTED TO IN DE
       PUSH    B               ;FOR A LENGTH OF B CHARACTERS
COMP1   LDAX    D               ; JC IF HL > DE
       CMP     M               ; JZ IF HL = DE
       JNZ     COMP2           ;JNC IF HL < DE
       INX     H
       INX     D
       DCR     B
       JNZ     COMP1
COMP2   POP     B
       POP     D
       POP     H
       RET


SIGNON  DB      'FLOPPY DISK COPY PROGRAM '
       DB      '10/9/82$'
ERRNOMSK DB     'ERROR...NO FILE MASK IN COMMAND'
       DB      CR,LF,'COMMAND MUST BE IN THE FORMAT "FLOPCOPY *.*"'
       DB      CR,LF,'$'
TABFULL DB      'MEMORY FULL...REPORT NOT COMPLETE$'
NFMSG   DB      'FILE NOT FOUND...ABORTING$'
DMNTMSG DB      'DISMOUNT DISKETTE AND ENTER RETURN$'
ERAMSG  DB      'ERASE COPIES ON THE HARD DISK (Y/N)? $'
MNTBLANK DB     'MOUNT A FORMATTED BLANK DISKETTE AND ENTER RETURN$'
ANOTHER DB      'WANT TO COPY THE SAME FILES ON TO ANOTHER DISKETTE (Y/N)? $'
ALRDYEXT DB     'FILE ALREADY EXISTS ON DESTINATION DISK.'
       DB      '  ENTER Y TO COPY OVER IT: $'
DSKFULL DB      'DISKETTE FULL...MOUNT ANOTHER DISK$'
PRNTREC DS      1
       DB      ':'
PRNFILE DS      8
       DB      '.'
PRNTYPE DS      3
       DB      '    $'
CRLFLFLF        DB      0DH,0AH,0AH,0AH,'$'
CRLF    DB      0DH,0AH,'$'
BIGMSG  DB      0DH,0AH,'FILE TOO LARGE TO FIT ON ONE DISKETTE '
       DB      'SO NOT BACKED UP **** $'
SRCFLPMSG DB    'SOURCE FLOPPY DRIVE (DEFAULT='
SRCFLP  DB      'D) $'
DESTFLPMSG DB   'DESTINATION FLOPPY DRIVE (DEFAULT='
DESTFLP DB      'D) $'
HDMSG   DB      'HARD DISK DRIVE USED AS BUFFER (DEFAULT='
HDTEMP  DB      'A) $'
HDFCB   DS      1
HDFILE  DS      8
HDTYPE  DS      3
       DS      24
FPFCB   DS      36
SRCE    DS      1
DEST    DS      1
INBUF   DB      30
INCNT   DS      1
INREC   DS      30
STAC
K       DS      80
EOF     DS      1
ABORT   DS      1
LINECNT DS      1
LASTTYPE DS     3
LASTFILE DS     8
TABADDR DS      2
TABCNT  DS      2
SELFLAG DS      1
TOOBIG  DS      1
ADDR1   DS      2
ADDR2   DS      2
ADDR3   DS      2
MAX1    DS      2
MAX2    DS      2
RTYPE   DS      3
RFILE   DS      8
RDISK   DS      3
       DB      '$'
TEMP    DS      RECSIZE
DISKBUF DS      128
COMPSIZE DS     1
CR      EQU     0DH
LF      EQU     0AH
RAM     EQU     $
       END     100H