; MULTIPLE FILE COPY PROGRAM ; COPYRIGHT 1982, G. YOUNG, INC. ; UPDATED 10/9/82 * * TO EXECUTE, ENTER THE PROGRAM NAME FOLLOWED BY THE FILE MASK: * A>MULTCOPY *.COM * * THE PURPOSE OF THIS PROGRAM IS TO COPY MULTIPLE FILES TO A FLOPPY DISK * FROM A HARD DISK. IF YOU USED PIP FOR D:=*.COM AND THER WERE MORE * COM FILES THAN WOULD FIT ON ONE DISK, PIP WOULD STOP WHEN THE FIRST * DISK WAS FULL. IF MULTCOPY, WHEN ONE DISK WAS FULL, IT WOULD ASK * YOU TO MOUNT A NEW DISK AND CONTINUE COPYING. THE FLOPPY DISK DRIVE * IS HARD CODED INTO THE PROGRAM. THE SOURCE MAY BE ANY HARD DISK. * EXAMPLE MULTCOPY B:LN*.ASM * CHANGED 10/9/82 TO ASK DESTINATION FLOPPY DRIVE * WRITTEN BY GARY YOUNG, BOX 3218, NO. HOLLYWOOD, CA 91609 TITLE '*** MULTCOPY FILE 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,DESTFLPMSG CALL QUESTION ORA A JNZ QUEST1A LDA DESTFLP JMP QUEST1B QUEST1A LDA INREC QUEST1B STA FLOPPY CALL EXTRACT ;GET DIRECTORY ENTRIES NOMORE CALL SORT CALL KILLDUPS ;REMOVE ENTRIES FROM MULTIPLE EXTENTS LDA 5CH ;SOURCE DISK STA SRCE LDA FLOPPY ;FLOPPY DISK IS ALWAYS THE DESTINATION ANI 0FH STA DEST LXI D,MNTBLANK CALL QUESTION MVI C,0DH CALL BDOS CALL CPYFILE 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 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 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,MNTBLANK CALL QUESTION MVI C,0DH CALL BDOS JMP FORMFCB NOTFOUND LHLD ADDR1 CALL FORMAT LXI D,NFMSG CALL OUTPUT LXI D,PRNTREC CALL OUTPUT RET * 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) 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 'MULTIPLE FILE COPY PROGRAM ' DB '10/9/82$' ERRNOMSK DB 'ERROR...NO FILE MASK IN COMMAND' DB CR,LF,'COMMAND MUST BE IN THE FORMAT "MULTCOPY *.*"' DB CR,LF,'$' NFMSG DB 'FILE NOT FOUND...ABORTING$' DMNTMSG DB 'DISMOUNT DISKETTE AND ENTER RETURN$' MNTBLANK DB 'MOUNT A FORMATTED BLANK DISKETTE AND ENTER RETURN$' 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 **** $' DESTFLPMSG DB 'DESTINATION FLOPPY DRIVE (DEFAULT=' DESTFLP DB 'D) $' HDFCB DS 1 HDFILE DS 8 HDTYPE DS 3 DS 24 FPFCB DS 36 FLOPPY DS 1 SRCE DS 1 DEST DS 1 INBUF DB 30 INCNT DS 1 INREC DS 30 STACK 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