; PROGRAM: COMPARE ; AUTHOR: Richard Conn ; VERSION: 1.4 ; DATE: 6 JAN 83 ; PREVIOUS VERSIONS: 1.3 (19 DEC 82) ; PREVIOUS VERSIONS: 1.2 (8 DEC 82), 1.1 (21 JULY 82), 1.0 (11 JULY 82) VERS EQU 14 ; ; This program is Copyright (c) 1982, 1983 by Richard Conn ; All Rights Reserved ; ; ZCPR2 and its utilities, including this one, are released ; to the public domain. Anyone who wishes to USE them may do so with ; no strings attached. The author assumes no responsibility or ; liability for the use of ZCPR2 and its utilities. ; ; The author, Richard Conn, has sole rights to this program. ; ZCPR2 and its utilities may not be sold without the express, ; written permission of the author. ; ; ; COMPARE is designed to provide the user with a convenient method ; to compare the contents of two files. It is invoked by one of two basic ; forms: ; ; COMPARE filename.typ ; or ; COMPARE file1.typ file2.typ ; ; The first form compares the file named "filename.typ" on drive A: ; to the file of the same name on drive B:; the second form compares the ; file named "file1.typ" on drive A: to the file named "file2.typ" on drive ; B:. ; ; CP/M Constants CPM equ 0 ; CP/M Warm Boot BUFF equ CPM+80H ; Temporary Buffer CR equ 0DH LF equ 0AH ; CRC ROUTINES EXT CRCCLR,CRCUPD,CRCDONE ; SYSLIB ROUTINES EXT COMPHD,BDOS,INITFCB,LOGUD,RETUD EXT F$OPEN,F$CLOSE,F$READ EXT CAPS,CIN,COUT,CRLF EXT PADC,MOVEB,PRINT EXT ZGPINS,ZFNAME ; ; Branch to Start of Program ; JMP START ; ;****************************************************************** ; ; SINSFORM -- ZCPR2 Utility Standard General Purpose Initialization Format ; ; This data block precisely defines the data format for ; initial features of a ZCPR2 system which are required for proper ; initialization of the ZCPR2-Specific Routines in SYSLIB. ; ; ; EXTERNAL PATH DATA ; EPAVAIL: DB 0FFH ; IS EXTERNAL PATH AVAILABLE? (0=NO, 0FFH=YES) EPADR: DW 40H ; ADDRESS OF EXTERNAL PATH IF AVAILABLE ; ; INTERNAL PATH DATA ; INTPATH: DB 0,0 ; DISK, USER FOR FIRST PATH ELEMENT ; DISK = 1 FOR A, '$' FOR CURRENT ; USER = NUMBER, '$' FOR CURRENT DB 0,0 DB 0,0 DB 0,0 DB 0,0 DB 0,0 DB 0,0 DB 0,0 ; DISK, USER FOR 8TH PATH ELEMENT DB 0 ; END OF PATH ; ; MULTIPLE COMMAND LINE BUFFER DATA ; MCAVAIL: DB 0FFH ; IS MULTIPLE COMMAND LINE BUFFER AVAILABLE? MCADR: DW 0FF00H ; ADDRESS OF MULTIPLE COMMAND LINE BUFFER IF AVAILABLE ; ; DISK/USER LIMITS ; MDISK: DB 4 ; MAXIMUM NUMBER OF DISKS MUSER: DB 31 ; MAXIMUM USER NUMBER ; ; FLAGS TO PERMIT LOG IN FOR DIFFERENT USER AREA OR DISK ; DOK: DB 0FFH ; ALLOW DISK CHANGE? (0=NO, 0FFH=YES) UOK: DB 0FFH ; ALLOW USER CHANGE? (0=NO, 0FFH=YES) ; ; PRIVILEGED USER DATA ; PUSER: DB 10 ; BEGINNING OF PRIVILEGED USER AREAS PPASS: DB 'chdir',0 ; PASSWORD FOR MOVING INTO PRIV USER AREAS DS 41-($-PPASS) ; 40 CHARS MAX IN BUFFER + 1 for ending NULL ; ; CURRENT USER/DISK INDICATOR ; CINDIC: DB '$' ; USUAL VALUE (FOR PATH EXPRESSIONS) ; ; DMA ADDRESS FOR DISK TRANSFERS ; DMADR: DW 80H ; TBUFF AREA ; ; NAMED DIRECTORY INFORMATION ; NDRADR: DW 00000H ; ADDRESS OF MEMORY-RESIDENT NAMED DIRECTORY NDNAMES: DB 64 ; MAX NUMBER OF DIRECTORY NAMES DNFILE: DB 'NAMES ' ; NAME OF DISK NAME FILE DB 'DIR' ; TYPE OF DISK NAME FILE ; ; REQUIREMENTS FLAGS ; EPREQD: DB 0FFH ; EXTERNAL PATH? MCREQD: DB 0FFH ; MULTIPLE COMMAND LINE? MXREQD: DB 0FFH ; MAX USER/DISK? UDREQD: DB 0FFH ; ALLOW USER/DISK CHANGE? PUREQD: DB 000H ; PRIVILEGED USER? CDREQD: DB 0FFH ; CURRENT INDIC AND DMA? NDREQD: DB 0FFH ; NAMED DIRECTORIES? Z2CLASS: DB 0 ; CLASS 0 DB 'ZCPR2' DS 10 ; RESERVED ; ; END OF SINSFORM -- STANDARD DEFAULT PARAMETER DATA ; ;****************************************************************** ; ; ; Start of Program ; START: CALL ZGPINS ; INIT ZCPR2 BUFFERS XRA A ; SET NO MULTIPLE RUN STA MULT CALL RETUD ; GET CURRENT USER/DISK MOV A,B ; SAVE DISK STA CDISK MOV A,C ; SAVE USER STA CUSER LXI H,BUFF ; PROCESS OPTIONS IN BUFFER MOV A,M ; GET CHAR COUNT INX H ; PT TO FIRST CHAR PUSH H ; SAVE PTR TO FIRST CHAR ADD L ; HL=HL+A MOV L,A MOV A,H ACI 0 MOV H,A MVI M,0 ; STORE ENDING ZERO POP H ; GET PTR TO FIRST CHAR LXI D,INLINE ; PT TO INPUT LINE BUFFER PUSH D ; SAVE PTR START0: MOV A,M ; COPY INPUT LINE SO BUFF MAY BE USED STAX D ; PUT BYTE INX H ; PT TO NEXT INX D ORA A ; EOL? JNZ START0 POP H ; PT TO FIRST CHAR CALL SBLANK ; SKIP SPACES ORA A ; EOL? JZ PRHELP ; PRINT HELP IF SO CPI '/' ; ASKING FOR HELP? JZ PRHELP LXI D,FCBS ; PT TO SOURCE FCB CALL ZFNAME ; EXTRACT NAME AND DIRECTORY DATA JNZ START1 UDERR: CALL PRINT DB CR,LF,'Invalid Disk or User -- Aborting',0 RET START1: MOV A,B ; SAVE SOURCE DISK CPI 0FFH ; CHECK FOR CURRENT JNZ SDISK1 LDA CDISK ; SPECIFY CURRENT DISK INSTEAD INR A ; ADD 1 FOR FOLLOWING DECREMENT SDISK1: DCR A ; DOWN 1 SO RANGE IS 0-F STA SDISK MOV A,C ; SAVE SOURCE USER CPI 0FFH ; CHECK FOR CURRENT JZ SUSER0 CPI '?' ; WILD IS CURRENT JNZ SUSER1 SUSER0: LDA CUSER ; GET CURRENT USER SUSER1: STA SUSER MOV A,M ; GET SEPARATION CHAR CPI ',' ; COMMA IF SECOND NAME SPECIFIED JZ START2 PUSH H ; SAVE PTR LXI H,FCBS+1 ; NO 2ND NAME, SO SET IT TO SAME AS FIRST LXI D,FCBD+1 MVI B,11 ; 11 BYTES CALL MOVEB LDA CDISK ; SET DISK AND USER TO CURRENT STA DDISK LDA CUSER STA DUSER POP H ; GET PTR JMP START3 START2: INX H ; PT TO NEXT CHAR AFTER COMMA LXI D,FCBD ; SET DEST FCB CALL ZFNAME ; PROCESS NAME JZ UDERR LDA FCBD+1 ; CHECK FOR AMBIGUOUS NAME CPI '?' ; ASSUME ALL IS AMBIGUOUS IF FIRST CHAR IS JNZ NOSET PUSH H ; SAVE PTR PUSH B ; SAVE USER/DISK LXI H,FCBS+1 ; SET NAMES THE SAME LXI D,FCBD+1 ; COPY SOURCE TO DEST MVI B,11 ; 11 BYTES CALL MOVEB POP B ; RESTORE BC POP H ; RESTORE PTR NOSET: MOV A,B ; GET DISK CPI 0FFH JNZ DDISK1 LDA CDISK ; SELECT CURRENT DISK IF DEFAULT INR A ; ADD 1 FOR FOLLOWING DECREMENT DDISK1: DCR A STA DDISK MOV A,C ; GET USER CPI 0FFH ; CURRENT? JZ DUSER0 CPI '?' ; WILD IS CURRENT JNZ DUSER1 DUSER0: LDA CUSER DUSER1: STA DUSER ; SET DEST USER START3: CALL SBLANK ; SKIP SPACES CPI 'M' ; MULTIPLE OPTION? JNZ START4 MVI A,0FFH ; SET FLAG STA MULT START4: LXI H,FCBS ; SET UP SOURCE FCB CALL QCHECK ; NO AMBIGUOUS ENTRIES PERMITTED LXI H,FCBD ; SET UP DESTINATION FCB CALL QCHECK ; NO AMBIGUOUS ENTRIES PERMITTED MLOOP: CALL BANNER ; PRINT BANNER LDA MULT ; MULTIPLE RUNS? ORA A ; 0=NO JZ MLOOP1 CALL PRS1 ; PRINT SOURCE FILE 1 CALL PRS2 ; PRINT SOURCE FILE 2 CALL PRINT DB CR,LF,' Change Disks if Desired and Type ^C or A to Abort or ' DB ' to Continue - ',0 CALL CIN ; GET RESPONSE CALL CAPS ; CAPITALIZE CPI 3 ; ABORT? RZ CPI 'A' ; ABORT? RZ MVI C,13 ; RESET DISKS CALL BDOS MLOOP1: CALL VERIFY ; PERFORM VERIFICATION CALL CRLF ; NEW LINE LDA MULT ; MULTIPLE RUNS? ORA A ; 0=NO JNZ MLOOP ; CONTINUE IF SO RET ; ; SKIP TO NON-BLANK CHAR ; SBLANK: MOV A,M ; GET CHAR INX H ; PT TO NEXT CPI ' ' ; BLANK? JZ SBLANK DCX H ; PT TO NON-BLANK RET ; ; PRINT HELP MESSAGE ; PRHELP: CALL BANNER ; PRINT BANNER CALL PRINT DB CR,LF DB CR,LF,'COMPARE is used to quickly compare two files for ' DB 'equality' DB CR,LF DB CR,LF,'COMPARE is invoked by a command like:' DB CR,LF,' COMPARE dir:file1.typ,dir:file2.typ M' DB CR,LF,'where:' DB CR,LF,' "file1.typ" must be specified and is unambiguous' DB CR,LF,' "dir:" is an optional named directory (or DU:)' DB CR,LF,' "file2.typ" is optional and set equal to ' DB '"file1.typ" if omitted' DB CR,LF,' "M" is optional and allows Multiple Runs if given' DB CR,LF DB CR,LF,'Examples:' DB CR,LF,'Command Files Compared' DB CR,LF,'COMPARE T.COM,A1: $$:T.COM, A1:T.COM' DB CR,LF,'COMPARE A:T.COM A$:T.COM, $$:T.COM' DB CR,LF,'COMPARE A:T.COM,ROOT: A$:T.COM, ROOT:T.COM' DB CR,LF,'COMPARE A:T.COM,B:S.COM A$:T.COM, B$:S.COM' DB 0 RET ; ; CHECK FOR ANY QUESTION MARKS FROM HL+1 TO HL+11 ; AFFECT ONLY AF REGISTERS IF OK ; QCHECK: PUSH H ; SAVE HL PUSH B ; SAVE BC INX H ; PT TO FIRST CHAR MVI B,11 ; 11 BYTES MVI C,'?' ; SCAN FOR '?' QC: MOV A,M ; GET BYTE CMP C ; '?'? JZ QC1 INX H ; PT TO NEXT DCR B ; COUNT DOWN JNZ QC POP B ; RESTORE POP H RET QC1: POP B ; RESTORE AND ABORT POP H POP D ; CLEAR RETURN ADDRESS XCHG ; FCB PTR IN DE CALL BANNER ; PRINT BANNER CALL CRLF CALL PRFN ; PRINT FILE NAME CALL PRINT DB ' Ambiguous File Name not Allowed',CR,LF,0 RET ; ; PRINT BANNER ; BANNER: CALL PRINT DB 'COMPARE Version ' DB VERS/10+'0','.',(VERS MOD 10)+'0' DB 0 RET ; ; PRINT NAMES OF SOURCE FILES ; PRS1 -- SOURCE FILE 1 ; PRS2 -- SOURCE FILE 2 ; PRS1: CALL PRINT DB CR,LF,'Source File 1 -- ',0 LXI H,SDISK ; PT TO FIRST BYTE CALL PRUD LXI D,FCBS ; COMPUTE CRC FOR SOURCE FCB JMP PRFN ; PRINT FILE NAME PRS2: CALL PRINT DB CR,LF,'Source File 2 -- ',0 LXI H,DDISK ; PT TO FIRST BYTE CALL PRUD LXI D,FCBD ; COMPUTE CRC FOR DESTINATION FCB JMP PRFN ; PRINT FILE NAME ; ; MAIN VERIFY ROUTINE ; VERIFY: CALL PRS1 ; PRINT SOURCE 1 LDA SDISK ; SELECT DISK AND USER MOV B,A LDA SUSER MOV C,A CALL LOGUD ; LOG IN DISK AND USER IN B,C CALL CRCCLR ; CLEAR CRC CALL COMPCRC ; READ IN FILE CALL CRCDONE ; GET CRCK VALUE SHLD CRCVAL ; SAVE IT LDA BCNT ; GET OLD BLOCK COUNT STA BCNT1 ; SAVE IT CALL PRS2 ; PRINT NAME OF SOURCE 2 LDA DDISK ; SELECT DISK AND USER MOV B,A LDA DUSER MOV C,A CALL LOGUD ; LOG IN DISK AND USER IN B,C CALL CRCCLR ; CLEAR CRC CALL COMPCRC ; READ IN FILE LDA BCNT ; GET BLOCK COUNT MOV B,A ; RESULT IN B LDA BCNT1 ; CHECK FOR SAME SIZE CMP B ; COMPARE BLOCK SIZES JNZ NOMATCH ; NO MATCH IF NOT SAME LHLD CRCVAL ; GET OLD CRC VALUE XCHG ; VALUE IN DE CALL CRCDONE ; UPDATE COMPLETE CALL COMPHD ; COMPARE HL TO DE JNZ NOMATCH CALL PRINT DB CR,LF,'** Files are Identical **',0 RET ; VERIFY ERROR NOMATCH: CALL PRINT DB CR,LF,'** Files are Different **',0 RET ; ; READ IN FILE AND COMPUTE ITS CRC; DE PTS TO FILE'S FCB ; COMPCRC: CALL CRCCLR ; CLEAR CRCK VALUE CALL INITFCB ; INIT FCB FIELDS CALL F$OPEN ; OPEN FILE CPI 0FFH ; FILE NOT FOUND? JNZ LOAD ; LOAD DATA AND COMPUTE CRCVAL AND BCNT POP H ; CLEAR 2 LEVELS OF RETURN ADDRESS POP H CALL CRLF CALL PRFN ; PRINT FILE NAME CALL PRINT DB ' File Not Found',CR,LF,0 RET ; ; LOAD BUFFER 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 ; MAIN CRC EVALUATION LOOP LOAD1: CALL F$READ ; READ IN BLOCK ORA A ; END OF FILE? JNZ LOAD3 ; RETURN LXI H,BUFF ; PT TO BUFFER TO COMPUTE CRC FROM MVI B,128 ; COPY 128 BYTES LOAD2: MOV A,M ; GET BYTE CALL CRCUPD ; UPDATE CRC INX H ; PT TO NEXT DCR B ; COUNT DOWN JNZ LOAD2 LDA BCNT ; GET BLOCK COUNT INR A ; INCREMENT IT STA BCNT ; SET IT JMP LOAD1 ; CONTINUE LOAD3: CALL F$CLOSE ; CLOSE FILE RET ; ; PRINT DISK/USER PTED TO BY HL (2 BYTES) ; PRUD: MOV A,M ; GET DISK ADI 'A' ; CONVERT TO LETTER CALL COUT INX H ; PT TO USER MOV A,M ; GET USER CALL PADC ; PRINT AS DEC CALL PRINT DB ': ',0 RET ; ; PRINT FILE NAME WHOSE FCB IS PTED TO BY DE ; PRFN: PUSH H ; SAVE REGS PUSH D PUSH B XCHG ; FN PTED TO BY HL INX H ; PT TO FIRST CHAR MVI B,8 ; 8 CHARS CALL PRFN1 MVI A,'.' CALL COUT MVI B,3 ; 3 CHARS FOR FILE TYPE CALL PRFN1 POP B ; RESTORE REGS POP D POP H RET PRFN1: MOV A,M ; GET CHAR INX H ; PT TO NEXT CALL COUT ; PRINT DCR B ; COUNT DOWN JNZ PRFN1 RET ; ; BUFFERS ; MULT: DS 1 ; MULTIPLE RUN FLAG (0=NO MULT RUNS) CDISK: DS 1 ; CURRENT DISK CUSER: DS 1 ; CURRENT USER SDISK: DS 1 ; SOURCE DISK (MUST BE FOLLOWED BY SUSER) SUSER: DS 1 ; SOURCE USER FCBS: DS 36 ; SOURCE FCB DDISK: DS 1 ; DEST DISK (MUST BE FOLLOWED BY DUSER) DUSER: DS 1 ; DEST USER FCBD: DS 36 ; DESTINATION FCB CRCVAL: DS 2 ; CRC VALUE CURDISK: DS 1 ; CURRENT DISK NUMBER BCNT: DS 1 ; BUFFER COUNT BCNT1: DS 1 ; SECOND BUFFER COUNT INLINE: DS 200 ; INPUT LINE BUFFER END