; DISK DIRECTORY AND BACKUP PROGRAM ;COPYRIGHT 1980, G. YOUNG, INC.' ;UPDATED 02/06/83 * THE PRIMARY PURPOSE OF THIS PROGRAM IS TO BACKUP HARD DISKS ON TO * MULTIPLE FLOPPIES. FIRST, IT EXTRACTS THE DIRECTORIES FROM ALL OF * THE HARD DISKS, SORTS BY TYPE AND FILE NAME, AND PRINTS A CONSOLIDATED * DIRECTORY. THEN IT DELETES DUPLICATE FILES FROM THE CONSOLIDATED * DIRECTORY, DELETES UNWANTED TYPES (PRN, BAK, ETC), AND UNWANTED * FILES TO CREATE A LIST OF FILES TO BACKUP. THESE ARE COPIED TO * MULTIPLE FLOPPIES AS NEEDED WHILE PRODUCING AN INDEX OF WHAT FILES ARE * ON EACH DISKETTE. RESTART CAPABILITY IS PROVIDED TO BEGIN THE BACKUP * AT ANY FILE. * RESTART CAPABILITY ADDED 6/20 * TO RESTART, ENTER A FILE NAME, I.E. A>BACKUP LOAD.COM * 10/1 ADDED A MORE EFFICIENT SHELL/METZER SORT WHICH DECREASED THE * SORT TIME FROM 2 MINUTES TO 2 SECONDS * 10/9/82 ADDED QUESTIONS TO MAKE IT MORE GENERAL PURPOSE * WRITTEN BY GARY YOUNG, BOX 3218, NO. HOLLYWOOD, CA 91609 TITLE '*** HARD DISK BACKUP PROGRAM ***' ORG 100H JMP START DB 'COPYRIGHT 1980, G. YOUNG, INC.' TRUE EQU 0FFH FALSE EQU 0 CLOCK EQU TRUE DEBUG EQU FALSE RECSIZE EQU 12 ;RECORD SIZE AS FOLLOWS ; TYPE = 3 BYTES ; FILE = 8 BYTES ; DISKID = 1 BYTES IDSIZE EQU 1 ;ID NOW MEANS A:, B:, C:, ETC LINESPAGE EQU 60 ;LINES PER PAGE RECLINE EQU 4 ;ENTRIES PER LINE START LXI SP,STACK+80 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 H,5DH ;SAVE THE FILE NAME FOR RESTARTING LXI D,RESTART MVI B,32 CALL MOVE IF CLOCK CALL GETDATE LXI H,DATETIME+1 ELSE MSG1 LXI D,DATEMSG ;GET CURRENT DATE FOR REPORTS CALL QUESTION ;PRINT MSG AND GET REPLY CPI 8 ;8 CHAR MUST HAVE BEEN ENTERED JNZ MSG1 ;REPEAT QUESTION IF NOT LXI H,INREC ;MOVE DATE FROM INREC ENDIF MVI B,8 ;MOVE THE DATE TO A HOLD AREA LXI D,DATE ;TO "DATE" CALL MOVE LXI D,DESTMSG ;GET DESTINATION DRIVES CALL QUESTION ORA A JZ LOGICQUS ;DEFAULT LDA INREC STA BACKUPDRV LOGICQUS LDA NODRV LXI H,DRIVES ;FORMAT THE QUESTION FOR WHICH DRIVES TO SHIFT1 INX H ;(HARD DISK DRIVES) TO BACKUP DCR A JNZ SHIFT1 MVI M,')' INX H MVI M,20H INX H MVI M,'$' LXI D,HD2BU CALL QUESTION ORA A JZ GETTYPEQUS STA NODRV MOV B,A ;NO ERROR CHECKING IS DONE ON THE DATA LXI H,INREC LXI D,DRIVES ;SO IF YOU PUT IN DRIVE Z, IT WILL CRASH CALL MOVE ;BUT YOU WOULD NOT BE THAT STUPID??? GETTYPEQUS LDA NOSKIP MOV B,A ADD A ;MULTIPLY BY 3 ADD B LXI H,SKIPTYPE SHIFT2 INX H DCR A JNZ SHIFT2 MVI M,')' INX H MVI M,20H ;FORMAT TYPE TO SKIP QUESTION INX H MVI M,'$' LXI D,SKPMSG CALL QUESTION ORA A JZ DEFAULTTYPE STA NOSKIP LXI H,INREC LXI D,SKIPTYPE MOV B,A CALL MOVE LDA NOSKIP MVI B,0 DIVBY3 INR B DCR A JZ CNTTYPE DCR A JZ CNTTYPE DCR A JNZ DIVBY3 ;CONVERT BYTE COUNT TO TYPE COUNT CNTTYPE MOV A,B STA NOSKIP DEFAULTTYPE 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 ;SORT THE MEMORY TABLE BY TYPE, FILE ;NAME, THEN DISK ID MVI A,RECSIZE ;SETUP TO REMOVE DUPLICATE ENTRIES STA COMPSIZE ;COMPARE ON ALL CHAR CALL KILLDUPS ;REMOVE DUPLICATE ENTRIES LDA RESTART CPI 20H CZ REPORT ;PRINT ALL ENTRIES MVI A,11 ;REMOVE FILES THAT ARE ON MULTIPLE DISKS STA COMPSIZE CALL KILLDUPS LDA RESTART CPI 20H CNZ POSITION ;POSITION LIST FOR A RESTART CALL SELECT ;RESTRICT THE FILES TO BACKUP MSG3 LXI D,BACKQUS ;ASK IF YOU WANT BACKUP FUNCTION CALL QUESTION CPI 1 JNZ MSG3 LDA INREC CPI 'N' JZ FINISHED CPI 'Y' JNZ MSG3 CALL BACKUP ;BACKUP THOSE FILES REPORTED ON FINISHED MVI C,0 JMP BDOS ;RESET AND RETURN TO CPM * * THE EXTRACT ROUTINE SCANS ALL THE DRIVES IN THE DRIVE TABLE * WHEN COMPLETE, IT ASKS FOR THE NEXT DRIVE ID * WHEN THE HARD DISK IS UP, IT WILL NOT ASK FOR THE DRIVE ID BUT * WILL AUTOMATICALLY SCAN THE THREE HARD DISKS AND FINISH WITHOUT * EVER ASKING FOR THE DRIVE ID. * * THE DUMMYFCB SETS UP A SKELETON TO READ ALL FILES ON THE DIRECTORY * 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 LXI D,EXTRMSG CALL OUTPUT LXI H,DRIVES ;GET ADDRESS OF THE DRIVE TABLE SHLD ADDR1 ;TO SCAN LDA NODRV ;SET UP COUNTER FOR THE NUMBER TO SCAN INR A STA MAX1 NEXTDRV LDA MAX1 ;SEE IF THERE ARE MORE DRIVES TO SCAN DCR A STA MAX1 ;RETURN WHEN ALL HAVE BEEN SCANNED RZ LHLD ADDR1 ;GET ADDRESS OF DRIVE TABLE MOV A,M ;GET DRIVE CHARACTER STA RDISK ANI 0FH ;CONVERT A=1...P=15 STA DUMMYFCB ;SET IN DR OF DUMMYFCB INX H ;SET FOR NEXT READ OF DRIVE TABLE SHLD ADDR1 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 JZ NEXTDRV ;NO MORE ENTRIES ON THIS DRIVE JMP GETFCB ;EXTRACT DATA FROM FCB NEXTFCB LXI D,DUMMYFCB ;GET NEXT ENTRY AFTER THE FIRST MVI C,12H CALL BDOS INR A JZ NEXTDRV ;NO MORE DIRECTORIES ON THIS DISK 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 MOV A,M ;GET THE EXTENT IF DEBUG ORI 30H ;DIAGNOSTIC DISPLAY STA RDISK+1 LXI D,RTYPE CALL OUTPUT LDA RDISK+1 ANI 0FH ENDIF 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 ;THIS ROUTINE IS USED TO POSITION THE LIST OF FILES FOR A RESTART POSITION LXI D,POSMSG CALL OUTPUT LXI H,RESTART+8 ;CONVERT FILE NAME TO TYP.FILENAME LXI D,REPO MVI B,3 CALL MOVE LXI H,RESTART LXI D,REPO+3 MVI B,8 CALL MOVE LXI H,RAM-RECSIZE ;SETUP FOR COMPARES SHLD ADDR1 NE1 LHLD ADDR1 LXI D,RECSIZE DAD D SHLD ADDR1 CNXT MOV A,M CPI 1AH ;EOF ON LIST? JZ LBU LHLD ADDR1 LXI D,REPO MVI B,11 CALL COMPARE JZ LBU LHLD ADDR1 LXI D,RECSIZE DAD D SHLD ADDR2 CALL MOVELIST ;TABLE MOVES DOWN SO ADDR1 IS NOT INCREMENTED LHLD ADDR1 JMP CNXT LBU SHLD ADDR2 RET SORT LXI D,SORTMSG ;PUT OUT A MESSAGE SO THE USER DOES CALL OUTPUT ;NOT THINK THIS CRASHED WHILE SORTING LHLD TABCNT SHLD N1 SHLD M1 LXI H,RECSIZE SHLD K1 LXI H,RAM SHLD J1 CALL SHELLM RET ;FINISHED SORTING ;TITLE 'IN MEMORY SHELL-metzner sort' ;sbttl 'from KILOBAUD april 1981 p164' ; ;remark 'For fixed length records stored in memory ;Put no. of records in n1 and m1. The length ;of each record is stored at k1, and the starting ;address at j1. Start sort by calling location ;"entry". To change to descending sort change the ;instruction at neq: to DAH.' ; n1: DW 0 ; number of records m1: DW 0 ; ..same here k1: DW 0 ; length of records j1: DW 0 ; starting address of strings i1: DW 0 ; ptr ml1: DW 0 ; ptr dj1: DW 0 ; ptr di1: DW 0 ;ptr ; SHELLM lhld j1 ; get start address push h ; ..save lhld k1 ; get length push h ; ..it too div: xra a ; m1=m1/2 lhld m1 mov a,h rar mov h,a mov a,l rar mov l,a shld m1 ; save new m1 ; ora h ; check if done jnz ndon pop b ; finished pop d ; ..so return ret ; ...now ; ; set k1=n1-m1 ; ndon: xchg ; m1 to de lhld n1 mov a,l sub e mov l,a mov a,h sbb d mov h,a shld k1 lxi h,1 ; set and save i=j=1 shld j1 shld i1 ; ; calc & save addr offset = m1*i1 ; dcr l pop b ; length of str=i1 push b ; ..put it back lp1: dad d dcx b mov a,b ora c jnz lp1 shld ml1 ; xchg ; calc & save d(j), d(i), d(i+m) pop b pop h push h push b lp2: shld dj1 shld di1 xchg dad d xchg ; HL has d(i), DE has d(i+m) ; ; compare strings and switch ; cp1: pop b ; len of string=l1 push b lp3: ldax d ; compare each byte sub m jnz neq ; not equal inx h ; if =, then next byte inx d dcx b mov a,b ora c jnz lp3 jmp nsw ; if done, don't switch ; ; change next instruction to jc for descending ; neq: jnc nsw ; if d(i)k ; nsw: lhld j1 inx h ; save new j=old j+1 shld j1 shld i1 xchg lhld k1 mov a,l sub e mov a,h sbb d jc div ; if j>k goto beginning and ; ..divide m1 ; ; calc new d(j), d(i) ; lhld dj1 pop d push d dad d ; new d(j)=old d(j+1) xchg lhld ml1 xchg jmp lp2 ; ; that all folks ; * REPORT PRINTS THE SORTED CONSOLIDATED DIRECTORY ENTRIES. * LINESPAGE IS THE NUMBER OF LINES PER PAGE AND RECLINE IS THE * NUMBER OF 20 BYTE ENTRIES PER LINE. EACH TYPE BEGINS ON A * NEW PAGE. REPORT LXI D,RPTMSG CALL OUTPUT LHLD TABCNT ;SET COUNT OF ENTRIES INX H ;PLUS ONE SHLD LASTTYPE ;INIT LASTTYPE TO INVALID DATA SHLD MAX1 ;TO DECREMENT FIRST LXI H,RAM-RECSIZE ;SET STARTING ADDRESS SHLD ADDR1 CALL HEADING NEXTPRT LHLD ADDR1 LXI D,RECSIZE ;SET TO ADD THE REC LENGTH DAD D SHLD ADDR1 LHLD MAX1 ;DECREMENT COUNT TO SEE WHEN DONE DCX H SHLD MAX1 LXI D,0000 ;SEE IF REACHED ZERO YET CALL COMPREG JZ TOPPAGE ;GO TO TOP OF NEXT PAGE LHLD ADDR1 ;CURRENT TYPE = LAST TYPE? MVI B,3 ;COMPARE 3 CHARACTERS LXI D,LASTTYPE CALL COMPARE JNZ SKIP3 ;NEW TYPE ON NEW PAGE PRNTNOW LDA MAX2 ;DECREMENT RECORDS PER LINE DCR A STA MAX2 JZ NEWLINE ;LINE FULL, GO TO NEW LINE CALL FORMAT LXI D,PRNTREC CALL LIST JMP NEXTPRT SKIP3 LDA LINECNT DCR A JZ NEWPAGE DCR A JZ NEWPAGE DCR A JZ NEWPAGE STA LINECNT LXI D,CRLFLFLF CALL LIST JMP CHKTYPE NEWPAGE CALL HEADING CHKTYPE LXI D,LASTTYPE ;SET LAST TYPE TO CURRENT TYPE LHLD ADDR1 MVI B,3 CALL MOVE SETCNT MVI A,RECLINE+1 STA MAX2 JMP PRNTNOW NEWLINE LXI D,CRLF CALL LIST LDA LINECNT DCR A STA LINECNT JZ NEWPAGE ;PAGE FULL JMP SETCNT TOPPAGE RET * THE PURPOSE OF SELECT IS TO CREATE A FINAL LIST OF FILES TO * BACKUP. SINCE BACKING UP 26 MEG CAN BE QUITE LONG, THIS REDUCES * THE LIST TO ONLY NECCESSARY FILES. IT ALSO ALLOWS A RESTART * AT ANY FILE. IT WILL DELETE DUPLICATE FILE NAMES (FILES ON A: * WILL BE USED SINCE THAT IS ASSUMED TO BE THE LATEST), AND * WILL DELETE THE FILE TYPES LISTED IN THE SKIP TABLE. THEN IT * WILL LIST EACH TYPE AND ASK IF YOU DO WANT TO BACK UP ALL OF * THIS TYPE (Y), SKIP BACKING UP OF THE ENTIRE TYPE (N), OR * SELECT CERTAIN FILES (S) TO BACK UP. IF THE SELECT OPTION IS * CHOSEN, EACH FILE NAME WILL BE PRINTED AND YOU BE ASKED TO * BACK UP THE FILE (Y), IGNORE THE FILE DURING THE BACKUP * (N), OR CONTINUE (C) TO BACKUP ALL OF THE REMAINING FILES * WITHIN THIS TYPE WITHOUT ASKING ANY FURTHER NAMES (USED FOR * RESTART). AFTER THE LIST HAS BEEN REDUCED, IT WILL BE LISTED * BEFORE THE BACKUP ACTUALLY BEGINS. KILLDUPS EQU $ ;KILL DUPLICATE ENTRIES FROM MULTIPLE EXTENTS LXI D,KILLMSG CALL OUTPUT 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 LDA COMPSIZE MOV B,A 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 SELECT EQU $ NOWSELECT EQU $ ;LET USER ELIMINATE SOME LXI H,RAM-RECSIZE ;INITIALIZE SHLD ADDR1 SHLD LASTTYPE ;MAKE SURE LASTTYPE DOES NOT MATCH NEXTENTRY LHLD ADDR1 ;GET NEXT RECORD IN TABLE LXI D,RECSIZE DAD D SHLD ADDR1 ;INCREMENT TABLE ENTRY CHKNEXT MOV A,M ;CHECK FOR THE END OF THE TABLE CPI 1AH ;CNTL-Z IS AT END JZ LISTBKUP ;LIST THE FINAL TABLE LXI D,LASTTYPE ;SEE IF THE CURRENT TYPE IS THE LHLD ADDR1 ;LAST TYPE MVI B,3 CALL COMPARE JZ CHKSELECT ;TYPES MATCH, CHECK FILE SELECT CALL MOVE ;TYPES DON'T MATCH, MAKE THE ;LAST TYPE = CURRENT TYPE NOW LDA NOSKIP INR A MOV C,A ;SEE IF THE NEW TYPE IS IN THE LXI D,SKIPTYPE ;LIST OF FILE TYPES TO SKIP MVI B,3 NEXTSKIP DCR C ;DECREMENT NO OF FILES TO SKIP JZ FORMQUS ;FILE IS GOOD LHLD ADDR1 CALL COMPARE JZ SKIPALLTYPE ;FILE WAS ON THE SKIP LIST INX D ;INCREMENT TO NEXT SKIP FILE INX D INX D JMP NEXTSKIP FORMQUS LXI D,QTYPE ;FORM THE QUESTION ABOUT WHETHER LHLD ADDR1 ;TO SKIP THIS FILE TYPE OR NOT MVI B,3 ;MOVE THE FILE TYPE TO THE CALL MOVE ;QUESTION PRINT LINE XRA A STA SELFLAG ;RESET THE SELECT FLAG REASK1 LXI D,QUEST1 CALL QUESTION ;ASK THE QUESTION ABOUT THE TYPE CPI 1 ;ONE CHARACTER RESPONSE ALLOWED JNZ REASK1 LDA INREC ;GET THE RESPONSE CPI 'N' JZ SKIPALLTYPE CPI 'Y' JZ SAVEALLTYPE CPI 'S' ;S=SELECT CERTAIN FILES FROM THIS JNZ REASK1 ;TYPE MVI A,1 ;SET THE SELECT FLAG STA SELFLAG ;TO ASK THE NEXT QUESTION ASKFILE LHLD ADDR1 ;FORMAT A QUESTION TO ASK IF THIS LXI D,QTYPE2 ;PARTICULAR FILE IS TO BE SAVED MVI B,3 CALL MOVE LXI D,QFILE2 ;MOVE THE FILE NAME TO THE QUESTION INX H INX H ;POSITION PAST THE FILE TYPE INX H ;IN THE TABLE MVI B,8 CALL MOVE ;MOVE THE NAME LXI D,8 ;POSITION PAST NAME TO THE DAD D ;DISK ID LXI D,QDISK2 ;MOVE THE DISK ID MVI B,IDSIZE CALL MOVE REASK2 LXI D,QUEST2 ;ASK THE FILE QUESTION CALL QUESTION CPI 1 ;ONLY A 1 CHAR RESPONSE ALLOWED LDA INREC JNZ REASK2 CPI 'C' ;CONTINUE WITH THE REST OF THE JZ SAVEALLTYPE ;FILES WITHIN THIS TYPE - RESTART CPI 'Y' ;SAVE THIS PARTICULAR FILE JZ NEXTENTRY CPI 'N' JNZ REASK2 LHLD ADDR1 ;SHORTEN THE LIST BY ONE ENTRY LXI D,RECSIZE DAD D SHLD ADDR2 ;SKIP THE LAST ONE CALL MOVELIST ;MOVES THE LIST DOWN FROM ADDR1 ;TO ADDR2 LHLD ADDR1 JMP CHKNEXT ;CHECK NEXT ENTRY CHKSELECT LDA SELFLAG ;CHECK THE SELECT FLAG ORA A ;IF NOT ZERO, ASK THE QUESTION JNZ ASKFILE ;ABOUT EACH FILE JMP NEXTENTRY SKIPALLTYPE CALL FINDTYPE ;FIND THE NEXT TYPE NOT MATCHING CALL MOVELIST ;THIS ONE AND MOVE THE LIST DOWN LHLD ADDR1 JMP CHKNEXT ;TO SKIP THIS TYPE SAVEALLTYPE CALL FINDTYPE ;SAVE ALL OF THIS TYPE LHLD ADDR2 ;RESET THE ADDRESS TO THE NEXT SHLD ADDR1 ;TYPE JMP CHKNEXT 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 FINDTYPE LHLD ADDR1 ;FIND THE NEXT ENTRY SHLD ADDR2 ;WITH A TYPE DIFFERENT FINDIT LHLD ADDR2 ;THAN THE CURRENT TYPE LXI D,RECSIZE DAD D SHLD ADDR2 MOV A,M ;STOP AT END OF THE TABLE CPI 1AH RZ XCHG LHLD ADDR1 MVI B,3 CALL COMPARE RNZ JMP FINDIT ;ADDR2 WILL HAVE THE ADDRESS OF ;THE NEXT TYPE WHEN FINISHED LISTBKUP EQU $ ;LIST THE FILES TO BE BACKED SHLD ADDR2 ;SAVE THE END OF THE TABLE FOR LATER LXI H,RAM-RECSIZE SHLD ADDR1 IF DEBUG ;PRINT LIST ONLY IF DEBUGGING CALL HEADING2 NEXTONE LHLD ADDR1 ;PRINT THE NEXT TABLE ENTRY LXI D,RECSIZE DAD D SHLD ADDR1 ;INCREMENT TO THE NEXT ENTRY MOV A,M ;CHECK FOR THE END OF THE CPI 1AH ;TABLE RZ CALL FORMAT ;FORMAT ENTRY LXI D,PRNTREC CALL LIST ;PRINT IT LDA MAX2 ;SEE MORE WILL FIT ON THIS LINE DCR A STA MAX2 JZ LINEFULL JMP NEXTONE LINEFULL LXI D,CRLF CALL LIST LDA LINECNT DCR A STA LINECNT JZ PAGEFULL JMP SETLINE PAGEFULL CALL HEADING2 MVI A,LINESPAGE STA LINECNT SETLINE MVI A,RECLINE STA MAX2 JMP NEXTONE HEADING2 LXI D,HEAD2 CALL LIST LXI D,CRLF CALL LIST MVI A,LINESPAGE STA LINECNT MVI A,RECLINE STA MAX2 ENDIF RET * BACKUP IS USED TO BACKUP THE FILES IN THE CREATED LIST. IT * WILL NOT BACK UP ANY FILE THAT WILL NOT FIT ON ONE DISK ENTIRELY. * THE FILES THAT IT BACKS UP WILL COME FROM ANY OF THE HARD DISKS, * BUT NOT NECCESSARILY IN DISK ORDER, BUT RATHER ALPHABETICAL FILE * NAME ORDER. IT WILL PROMPT FOR THE FLOPPY DISK ID AND PRINT A * REPORT SHOWING THE FLOPPY ID AND THE FILES COPIED FOR INDEX. * IF THE FILE WILL NOT FIT ON A DISK, IT WILL BE DELETED FROM THE * DISK, THAT DISK CLOSED AND REMOVED, AND PROMPTED FOR A NEW DISK * TO RECEIVE THE FILE. BLANK 1024 BYTE SECTORED DISKS SHOULD BE * USED FOR BEST PERFORMANCE. WHEN ONE DISKETTE IS FULL, IT WILL * PROMPT FOR A NEW ONE SO MANY DISKETTES CAN BE USED TO BACKUP * THE HARD DISKS. IF IT IS NECESSARY TO ABORT DURING THIS PROCESS, * USE THE RESTART CAPABILITY PROVIDED IN "SELECT". THE AREA * FOR THE DISK BUFFER IS ALL OF RAM FROM THE END OF THE TABLE * TO THE START OF BDOS. BACKUP EQU $ CALL TOPOFFORM 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 LXI D,RECSIZE ;SET AN ADDRESS FOR THE SECOND ENTRY DAD D ;NEXT ENTRY = CURRENT + RECSIZE SHLD ADDR2 ;LATER, USE MOVELIST TO MOVE THE LIST ;DOWN FROM ADDR2 TO ADDR1 AFTER EACH FILE ;HAS BEEN COPIED. THIS IS SO THAT THE ;RAM BUFFER WILL EXPAND AS THE FILES ARE ;COPIED SO THAT COPYING WILL BE FASTER. CALL MOUNT JMP PASSMOVE NEXTFILE EQU $ CALL MOVELIST INX H ;NEW START OF READ/WRITE BUFFER SHLD ADDR3 XRA A ;RESET THE FLAG FOR FILES TOO BIG STA TOOBIG ;TO FIT ON ONE FLOPPY PASSMOVE LXI H,RAM ;THE TABLE SHRINKS SO THE CURRENT ;ENTRY IS ALWAYS AT "RAM" BUT THE ;READ/WRITE BUFFER GROWS MOV A,M CPI 1AH JZ DISMOUNT ;FINISHED FORMFCB LXI D,HDFCB ;CLEAR THE FCB MVI B,36 XRA A ZEROFCB STAX D INX D DCR B JNZ ZEROFCB LXI H,RAM ;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 MOV A,M ANI 0FH ;CONVERT A=1...P=15 STA HDFCB LXI D,FPFCB ;COPY THE HDFCB TO THE FLOPPY FCB LXI H,HDFCB MVI B,36 CALL MOVE LDA BACKUPDRV ;SET THE RECEIVING FLOPPY DRIVE NUMBER ANI 0FH ;CONVERT D: TO 4 STA FPFCB LXI D,HDFCB ;OPEN THE HD FILE MVI C,0FH CALL BDOS ;OPEN THE INPUT FILE INR A JZ NOTFOUND LXI D,FPFCB ;DELETE THE FILE ON FLOPPY IF IT MVI C,13H ;EXISTS CALL BDOS LXI D,FPFCB ;CREATE THE FILE ON FLOPPY MVI C,16H CALL BDOS ;MAKE FILE INR A JZ DISKFULL LXI H,RAM CALL FORMAT LXI D,PRNTREC CALL OUTPUT COPYLOOP CALL LOADBUFF ;LOAD MEMORY WITH FILE CALL WRITEBUF ;WRITE MEMORY FILE LDA EOF ;DISPLAY THE STATUS ADI 30H MOV E,A ;CONSOLE OUTPUT MVI C,2 CALL BDOS 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 LXI H,RAM CALL PRINTFILE ;WRITE FILE NAME ON INDEX LIST JMP NEXTFILE DISKFULL LXI D,FPFCB MVI C,13H ;DELETE FILE ON FLOPPY CALL BDOS MVI C,23H ;GET FILE SIZE LXI D,HDFCB CALL BDOS ;SEE IF IT IS TOO BIG TO FIT ON A FLOPPY LDA HDFCB+34 ;GET THE HIGH ORDER BYTE OF THE FILE SIZE ANI 0F8H ;SEE IF THE FILE IS > 256K JNZ HUGE ;YEP, TOO BIG FOR SINGLE DENSITY FLOPPY CALL MOUNT ;NO, JUST MOUNT ANOTHER FLOPPY JMP FORMFCB HUGE LXI D,BIGMSG ;YES, SECOND FLOPPY. SEND MESSAGE CALL LIST ;WARNING THAT THIS FILE CANNOT LXI H,RAM CALL FORMAT ;BE BACKED UP WITH THIS PROGRAM LXI D,PRNTREC CALL LIST ;PRINT THE FILE NAME ALSO JMP NEXTFILE NOTFOUND LXI H,RAM CALL FORMAT LXI D,NFMSG CALL OUTPUT LXI D,PRNTREC CALL OUTPUT RET DISMOUNT CALL BELL LXI D,DMNTMSG ;DISMOUNT FLOPPY CALL QUESTION CALL TOPOFFORM RET * ASSORTED ROUTINES PRINTFILE CALL FORMAT LXI D,PRNTREC ;PRINT THE FILE ENTRY CALL LIST LDA MAX2 DCR A STA MAX2 ;ENTRIES PER LINE COUNTER RNZ LXI D,CRLF ;GO TO NEW LINE CALL LIST MVI A,RECLINE STA MAX2 RET HEADING3 LXI D,CRLFLFLF ;INDEX HEADING NOT AT TOP OF FORM CALL LIST LXI D,IDISKNO ;MOVE THE DISK ID NO TO HEADING LXI H,VOLSER MVI B,3 CALL MOVE LXI D,IDATE LXI H,DATE MVI B,8 CALL MOVE LXI D,INDEX CALL LIST LXI D,CRLF CALL LIST MVI A,RECLINE STA MAX2 MVI A,LINESPAGE STA LINECNT RET TOPOFFORM MVI E,0CH ;POSITION PRINTER TO TOP OF FORM MVI C,5 JMP BDOS MOUNT CALL BELL LXI D,MNTMSG CALL QUESTION CPI 3 JNZ MOUNT LXI H,INREC LXI D,ENDLIT MVI B,3 CALL COMPARE JZ FINISHED LXI D,VOLSER LXI H,INREC MVI B,3 CALL MOVE ;MOVE VOLSER ID TO VOLSER MVI C,0DH ;DO A SYSTEM RESET TO CHANGE DISKS CALL BDOS ERA LXI D,ERASE CALL QUESTION CPI 1 JNZ ERA LDA INREC CPI 'N' JZ NOERA CPI 'Y' JNZ ERA LDA BACKUPDRV ANI 0FH STA DUMMYFCB MVI C,13H LXI D,DUMMYFCB CALL BDOS ;DELETE ALL FILES ON FLOPPY NOERA EQU $ CALL HEADING3 LXI D,FPFCB ;CLEAR THE FCB TO CREATE A FILE NAME MVI B,36 XRA A CLRFCB STAX D INX D DCR B JNZ CLRFCB LDA BACKUPDRV ;SET UP THE DRIVE AS THE BACKUP ANI 0FH ;CONVERT D: TO 4 STA FPFCB LXI H,DATE ;CREATE A NULL FILE WITH THE DATE AS LXI D,FPFCB+1 ;THE FILE NAME AND THE 3 DIGIT MVI B,8 CALL MOVE LXI H,VOLSER ;3 DIGIT VOLSER AS THE FILE TYPE LXI D,FPFCB+9 MVI B,3 ;THIS FILE WILL BE USED TO IDENTIFY CALL MOVE ; THE DISK AND MARK THE DATE LXI D,FPFCB MVI C,16H ;CREATE THE FILE CALL BDOS INR A JZ MOUNT ;DISK FULL? LXI D,FPFCB MVI C,10H ;CLOSE THE FILE CALL BDOS RET 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 BELL EQU $ MVI C,9 LXI D,MSG CALL BDOS BEEP MVI C,2 ;07H ON CRT MVI E,07H CALL BDOS MVI C,0BH CALL BDOS ;GET CONSOLE STATUS ORA A JZ BEEP MVI C,01 CALL BDOS ;GET THE DUMMY CHAR RET MSG DB 0DH,0AH,0AH,'PRESS ANY KEY TO CONTINUE' DB 0DH,0AH,0AH,'$' 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 LXI D,PRNTREC ;MOVE THE DISK ID TO THE LINE MVI B,IDSIZE CALL MOVE 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 HEADING LXI D,HEAD1 CALL LIST LXI D,DATE CALL LIST LXI D,TABFULL LDA ABORT ORA A CNZ LIST MVI A,LINESPAGE STA LINECNT LXI D,CRLF CALL LIST LXI D,CRLF JMP LIST LIST PUSH H ;THIS DIFFERS FROM OUTPUT PUSH B ; IN THAT LIST GOES TO THE PUSH D ;LIST DEVICE AND OUTPUT GOES LIST1 LDAX D ;TO THE CONSOLE DEVICE CPI '$' JZ LIST2 INX D PUSH D MOV E,A MVI C,5 CALL BDOS POP D JMP LIST1 LIST2 POP D POP B POP H RET 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 IF CLOCK ; THIS ROUTINE READS THE COMPUTIME CLOCK AND FORMATS THE TIME AND DATE GETDATE EQU $ MVI A,16 ;RAISE THE HOLD SIGNAL OUT C2 MVI A,50 ;DELAY AT LEAST 50 MILLISECONDS WAIT50 DCR A JNZ WAIT50 LXI H,DATETIME ;SET MESSAGE START. NOTE 1ST CHAR IS SPACE MVI A,42 ;GET MONTH TENS CHAR CALL GETTIME MVI A,41 ;GET MONTH UNITS CHAR CALL GETTIME INX H ;SKIP SLASH MVI A,40 ;GET DAY TENS CHAR CALL GETTIME ANI 0F3H ;DROP LEAP YEAR INDICATOR MOV M,A MVI A,39 ;GET DAY UNITS CHAR CALL GETTIME INX H ;SKIP SLASH MVI A,44 ;GET YEAR TENS CHAR CALL GETTIME MVI A,43 ;GET YEAR UNITS CHAR CALL GETTIME INX H ;SKIP BLANK MVI A,37 ;GET HOUR TENS CHAR CALL GETTIME ANI 0F3H ;DROP AM/PM AND 24HR INDICATORS MOV M,A MVI A,36 ;GET HOUR UNITS CHAR CALL GETTIME INX H ;SKIP COLON MVI A,35 ;GET MINUTES TENS CHAR CALL GETTIME MVI A,34 ;GET MINUTES UNITS CHAR CALL GETTIME INX H ;SKIP COLON MVI A,33 ;GET SECONDS TENS CHAR CALL GETTIME MVI A,32 ;GET SECONDS UNITS CHAR CALL GETTIME XRA A OUT C2 ;LOWER HOLD SIGNAL RET C1 EQU 254 ;CLOCK ADDRESS PORT C2 EQU 253 ;CLOCK DATA OUTPUT PORT GETTIME INX H ;ADVANCE TO NEXT PRINT POSITION OUT C1 MVI A,6 ;DELAY AT LEAST 6 MILLISECONDS WAIT6 DCR A JNZ WAIT6 IN C1 ORI 30H ;CONVERT TO ASCII MOV M,A RET DATETIME DB ' 00/00/00 HH:MM:SS$' ENDIF SIGNON DB 'HARD DISK CATALOG AND BACKUP ' DB '10/09/82A$' RESTART DS 32 REPO DS 12 DATEMSG DB 'ENTER 8 CHAR DATE FOR REPORTS MM/DD/YY: $' ERASE DB 'ERASE FLOPPY DISK FIRST (Y/N)? $' TABFULL DB 'MEMORY FULL...REPORT NOT COMPLETE$' BACKQUS DB 'BEGIN BACKUP PROCEDURE (Y/N)? $' SORTMSG DB 'SORTING...$' RPTMSG DB 'PRINTING REPORTS...$' EXTRMSG DB 'EXTRACTING DIRECTORY...$' POSMSG DB 'POSITION FOR RESTART...$' KILLMSG DB 'REMOVING DUPLICATES...$' NFMSG DB 'FILE NOT FOUND...ABORTING$' DMNTMSG DB 'DISMOUNT BACKUP DISK AND ENTER RETURN$' MNTMSG DB 'MOUNT DISKETTE FOR BACKUP AND ENTER ' DB '3 CHAR DISK ID OR "END": $' ENDLIT DB 'END' VOLSER DS 3 QUEST1 DB 'BACKUP ALL FILES OF TYPE ' QTYPE DS 3 DB ', (Y/N/S)? $' QUEST2 DB 'BACKUP FILE NAMED ' QDISK2 DS 1 DB ':' QFILE2 DS 8 DB '.' QTYPE2 DS 3 DB ', (Y/N/C)? $' HEAD2 DB 0CH,'THE FOLLOWING FILES WILL BE COPIED TO DISKETTES$' PRNTREC DS 1 DB ':' PRNFILE DS 8 DB '.' PRNTYPE DS 3 DB ' $' CRLFLFLF DB 0DH,0AH,0AH,0AH,'$' INDEX DB 'THE FOLLOWING FILES ARE ON DISKETTE NO. ' IDISKNO DS 3 DB ' AS OF ' IDATE DS 8 DB '$' CRLF DB 0DH,0AH,'$' HEAD1 DB 0CH,'CONSOLIDATED INDEX BY FILE TYPE AS OF $' DATE DS 8 DB ' $' BIGMSG DB 0DH,0AH,'FILE TOO LARGE TO FIT ON ONE DISKETTE ' DB 'SO NOT BACKED UP **** $' DESTMSG DB 'DESTINATION FLOPPY (DEFAULT=' BACKUPDRV DB 'D) $' HD2BU DB 'LOGICAL HARD DISK DRIVES TO BACKUP (DEFAULT=' DRIVES DB 'ABCDEFGHIJKLMNOP' NODRV DB 3 SKPMSG DB 'FILE TYPES TO SKIP (DEFAULT=' SKIPTYPE DB 'PRNHEXSYMBAKTMP' DS 45 NOSKIP DB 5 HDFCB DS 1 HDFILE DS 8 HDTYPE DS 3 DS 24 FPFCB DS 36 TOP DB 0CH,'$' INBUF DB 60 INCNT DS 1 INREC DS 60 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 RAM EQU $ BDOS EQU 05H END 100H