; ; PROGRAM: DIFF ; AUTHOR: Richard Conn ; VERSION: 2.0 ; DATE: 18 MAY 84 ; PREVIOUS VERSIONS: 1.6 (16 JAN 83) ; PREVIOUS VERSIONS: 1.5 (9 JAN 83), 1.4 (6 JAN 83), 1.3 (4 JAN 83) ; PREVIOUS VERSIONS: 1.2 (19 DEC 82), 1.1 (8 DEC 82), 1.0 (24 JULY 82) ; DERIVATION: COMPARE, VERSION 1.1 ; VERS EQU 20 z3env SET 0f400h ; ; DIFF 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: ; ; DIFF filename.typ ; or ; DIFF 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:. Wild cards may NOT be used. The listing generated by the program gives ; relative offsets (in hex and decimal) as well as the different byte values ; in hex, decimal, and ASCII. ; ; SIZE OF BUFFER BLIMIT EQU 32 ; NUMBER OF 128-BYTE BLOCKS BSIZE EQU BLIMIT*128 ; 4K ; CP/M Constants CPM equ 0 ; CP/M Warm Boot BUFF equ CPM+80H ; Temporary Buffer CR equ 0DH ; LF equ 0AH ; CTRLC EQU 'C'-'@' CTRLX EQU 'X'-'@' ; SYSLIB and Z3LIB ROUTINES EXT Z3INIT,ZFNAME,GETCRT EXT PHL4HC,PHLDC,PA2HC,PADC EXT BDOS,INITFCB EXT LOGUD,RETUD EXT F$OPEN,F$CLOSE,F$READ EXT CAPS,CIN,COUT,CRLF,MOVEB,PRINT 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 ; ; SET BUFFER LOCATIONS ; CALL CODEND ; ALLOCATE BUFFER SPACE SHLD INLINE ; SET PTR TO INPUT LINE LXI D,100H ; BUFFER SIZE DAD D SHLD BUFF1 ; SOURCE 1 BUFFER LXI D,BSIZE ; SIZE OF BUFFER INR D ; ADD 1 DAD D SHLD BUFF2 ; SOURCE 2 BUFFER ; ; SET OPERATIONS FLAGS ; XRA A ; A=0 STA MULT ; SET NO MULTIPLE RUN STA COMP ; SET NO COMPARE ONLY CALL GETCRT ; GET CRT CHARACTERISTICS INX H ; PT TO TEXT LINE COUNT INX H MOV A,M ; GET COUNT STA LSET ; SET COUNTER ; ; EXTRACT COMMAND LINE INFORMATION ; LHLD INLINE ; PT TO INPUT LINE BUFFER XCHG ; ... IN DE LXI H,BUFF+1 ; PROCESS OPTIONS IN BUFFER MVI B,80H ; ARBITRARY 80H BYTES CALL MOVEB XCHG ; PT TO FIRST CHAR WITH HL 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 MVI A,0 ; DIR BEFORE DU CALL ZFNAME ; EXTRACT NAME AND DIRECTORY DATA MOV A,M ; GET NEXT CHAR CPI ',' 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 CALL RETUD ; GET CURRENT USER/DISK MOV A,B ; GET DISK INR A ; A=1 STA FCBD ; SET DISK MOV A,C ; GET USER STA FCBD+13 ; PT TO S1 FOR USER POP H ; GET PTR JMP START3 START2: INX H ; PT TO NEXT CHAR AFTER COMMA LXI D,FCBD ; SET DEST FCB MVI A,0 ; DIR BEFORE DU CALL ZFNAME ; PROCESS NAME LDA FCBD+1 ; CHECK FOR NO NAME CPI ' ' ; SPACE MEANS NO NAME JNZ START3 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 START3: CALL SBLANK ; SKIP SPACES CALL OPTS ; PROCESS OPTIONS 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 CALL RETUD ; GET CURRENT DU IN BC LXI H,FCBS ; PT TO SOURCE DISK MOV A,M ; GET SOURCE DISK ORA A ; CURRENT? JNZ START5 MOV A,B ; SET CURRENT DISK INR A ; ADJUST FOR A=1 START5: STA SDISK MVI M,0 ; CLEAR SOURCE DISK LDA FCBS+13 ; GET USER STA SUSER LXI H,FCBD ; GET DESTINATION DISK MOV A,M ; GET DEST DISK ORA A ; CURRENT? JNZ START6 MOV A,B ; SET CURRENT DISK INR A ; ADJUST FOR A=1 START6: STA DDISK MVI M,0 ; CLEAR DEST DISK LDA FCBD+13 ; GET USER STA DUSER MLOOP: CALL BANNER ; PRINT BANNER CALL PRS1 ; PRINT SOURCE FILE NAMES CALL PRS2 LDA MULT ; MULTIPLE RUNS ORA A JZ MLOOP1 CALL PRINT DB CR,LF,' Type ^C to Abort or RETURN to Proceed - ',0 CALL CIN ; GET RESPONSE CALL CAPS ; CAPITALIZE CPI CTRLC ; ABORT? RZ MVI C,13 ; RESET DISKS CALL BDOS MLOOP1: CALL LOGS ; LOG IN SOURCE LXI D,FCBS ; TRY TO OPEN SOURCE 1 CALL INITFCB ; INIT FCB CALL F$OPEN ; Z IF NO ERROR JNZ FERR CALL LOGD ; LOG IN DEST DISK/USER LXI D,FCBD ; TRY TO OPEN SOURCE 2 CALL INITFCB ; INIT FCB CALL F$OPEN JNZ FERR XRA A ; A=0 STA FIRST ; SET FLAG FOR FIRST ERROR LXI H,0 ; INIT OFFSET SHLD OFFSET CALL VERIFY ; PERFORM VERIFICATION LDA FIRST ; ANY ERRORS? ORA A ; 0=NO ERRORS JNZ MLOOP2 CALL PRINT DB CR,LF,' Files are Identical',0 MLOOP2: LDA MULT ; CHECK FOR MULTIPLE RUNS ORA A ; 0=NO RZ CALL CRLF ; NEW LINES CALL CRLF JMP MLOOP FERR: CALL PRINT DB CR,LF,' File Not Found -- ',0 CALL PRFN JMP MLOOP2 ; ; PROCESS OPTIONS ; OPTS: MOV A,M ; GET NEXT OPTION CHAR INX H ; PT TO NEXT ORA A ; END OF LINE? RZ CPI ' ' ; SKIP SPACES JZ OPTS LXI D,OTAB ; PT TO OPTION TABLE MOV B,A ; OPTION CHAR IN B OPTS1: LDAX D ; GET TABLE CHAR ORA A ; OPTION NOT FOUND? JZ OPTSE ; PROCESS ERROR CMP B ; MATCH? JZ OPTS2 INX D ; SKIP TO NEXT INX D INX D JMP OPTS1 OPTS2: XCHG ; USE HL INX H ; GET ADDRESS MOV A,M ; GET LOW INX H MOV H,M ; GET HIGH MOV L,A ; PUT LOW XCHG ; DE PTS TO OPTION ADDRESS, HL TO NEXT BYTE LXI B,OPTS ; SET UP RETURN ADDRESS PUSH B PUSH D ; SET UP OPTION ADDRESS RET ; "RUN" OPTION OPTSE: CALL PRHELP ; PRINT HELP MESSAGE POP PSW ; CLEAR RETURN ADDRESS RET ; RETURN TO OPSYS ; ; OPTION TABLE ; OTAB: DB 'C' ; COMPARE ONLY DW SCOMPF DB 'M' ; MULTIPLE RUN DW SMULTF DB 0 ; END OF TABLE ; ; SET COMPARE FLAG ; SCOMPF: MVI A,0FFH ; SET FLAG STA COMP RET ; ; SET MULTIPLE RUN FLAG ; SMULTF: MVI A,0FFH ; SET FLAG STA MULT 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,'Syntax:' DB CR,LF,' DIFF ufn1,ufn2 o... -or- DIFF ufn o...' db cr,lf,'Options:' db cr,lf,' C Compare Files Only (Stop at First Difference)' db cr,lf,' M Multiple Runs (Keep on prompting for disks)' DB CR,LF,'Examples:' DB CR,LF,' Command Files Compared' DB CR,LF,' DIFF T.COM,A1: $$:T.COM, A1:T.COM' DB CR,LF,' DIFF A:T.COM A$:T.COM, $$:T.COM' DB CR,LF,' DIFF A:T.COM,ROOT: A$:T.COM, ROOT:T.COM' DB CR,LF,' DIFF 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 CRLF CALL PRFN ; PRINT FILE NAME CALL PRINT DB ' AFN not Allowed',CR,LF,0 RET ; ; PRINT BANNER ; BANNER: CALL PRINT DB 'DIFF 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 1 -- ',0 LXI H,SDISK ; PT TO FIRST BYTE CALL PRUD LXI D,FCBS ; SOURCE FCB JMP PRFN ; PRINT FILE NAME PRS2: CALL PRINT DB CR,LF,'Source 2 -- ',0 LXI H,DDISK ; PT TO FIRST BYTE CALL PRUD LXI D,FCBD ; DESTINATION FCB JMP PRFN ; PRINT FILE NAME ; ; MAIN VERIFY ROUTINE ; VERIFY: LHLD BUFF1 ; PT TO BUFFER 1 PUSH H ; SAVE PTR CALL LOGS ; LOG IN SOURCE 1 LXI D,FCBS ; SOURCE 1 FCB CALL LOAD ; READ IN BLOCK LDA BCNT ; GET OLD BLOCK COUNT STA BCNT1 ; SAVE IT LHLD BUFF2 ; PT TO BUFFER 2 PUSH H ; SAVE PTR CALL LOGD ; LOG IN SOURCE 2 LXI D,FCBD ; SOURCE 2 FCB CALL LOAD ; READ IN BLOCK POP D ; DE PTS TO BUFF 2 POP H ; HL PTS TO BUFF 1 LDA BCNT ; CHECK FOR NO BLOCK READ MOV B,A LDA BCNT1 ORA B RZ ; DONE IF NONE READ ; ; VERIFY LOADED BUFFERS BY COMPARING THEM AND PRINTING DIFFERENCES ; VERBLOCK: MVI B,128 ; SCAN ONE BLOCK VERBL: LDAX D ; GET BYTE CMP M ; COMPARE CNZ NOMATCH ; PRINT DIFFERENCE PUSH H ; INC OFFSET LHLD OFFSET INX H SHLD OFFSET POP H INX H ; PT TO NEXT INX D DCR B ; COUNT DOWN JNZ VERBL LDA BCNT ; COUNT DOWN DCR A STA BCNT LDA BCNT1 DCR A STA BCNT1 JZ VEREQ LDA BCNT ; CHECK FIRST BUFFER COUNT ORA A JNZ VERBLOCK ; CONTINUE COMPARE IF NOT EMPTY VEREQ: LDA BCNT ; CHECK FOR BOTH DONE MOV B,A LDA BCNT1 ORA B ; IF ZERO, BOTH DONE AT SAME TIME AND CONTINUE JZ VERIFY LDA BCNT1 ; CHECK FOR ONE DONE BEFORE THE OTHER ORA A ; 2ND DONE? MVI C,'2' ; GET LETTER JZ DONE1 MVI C,'1' ; GET LETTER ; ONE FILE IS SHORTER THAN THE OTHER -- SAY SO DONE1: CALL PRINT DB CR,LF,' Source ',0 MOV A,C CALL COUT ; PRINT LETTER CALL PRINT DB ' has terminated prematurely',0 JMP DABORT ; MATCH ERROR NOMATCH: LDA COMP ; GET COMPARE FLAG ORA A ; NZ=SIMPLE COMPARE JNZ CABORT PUSH H ; SAVE REGS PUSH D PUSH B LDA FIRST ; FIRST TIME THRU? ORA A ; 0=YES JZ NMAT0 LDA LCNT ; CHECK FOR NEW SCREEN ORA A ; ZERO IF DONE JNZ NMAT1 CALL PRINT DB CR,LF,' DIFF Pause -- Strike RETURN to Continue, ' DB '^C to Abort, or ^X to Advance - ',0 CALL CIN ; GET RESPONSE CALL CAPS CPI CTRLC ; ABORT? JZ NMAT00 CPI CTRLX ; ADVANCE? JNZ NMAT0 POP B ; CLEAR REGS POP D POP H POP D ; CLEAR STACK CALL PRINT DB CR,LF,' DIFF Advancing',0 RET ; RETURN TO VERIFY CALLER NMAT00: POP B ; CLEAR REGS POP D POP H POP D ; CLEAR STACK POP D DABORT: CALL PRINT DB CR,LF,' DIFF Aborting',0 RET ; RETURN TO OPSYS CABORT: POP D ; CLEAR STACK MVI A,1 ; SET ERROR FLAG STA FIRST CALL PRINT DB CR,LF,' Files are Different',0 RET ; RETURN TO VERIFY CALLER NMAT0: MVI A,0FFH ; CLEAR FIRST TIME FLAG STA FIRST CALL HEADER ; PRINT HEADING AND RETURN NEW LINE COUNT NMAT1: DCR A ; COUNT DOWN 1 LINE STA LCNT ; NEW LINE COUNT CALL CRLF PUSH H ; SAVE HL LHLD OFFSET ; PRINT OFFSET VALUE CALL PHL4HC ; PRINT AS HEX CALL SPACER ; PRINT SPACES CALL PHLDC ; PRINT AS DEC POP H ; RESTORE HL CALL SPACER CALL SPACER MVI A,' ' CALL COUT MOV A,M ; GET SOURCE 1 VALUE CALL PRVAL ; PRINT AS HEX, DEC, ASCII CALL SPACER ; 10 SPACES CALL SPACER CALL SPACER CALL SPACER CALL SPACER LDAX D ; GET SOURCE 2 VALUE CALL PRVAL ; PRINT AS HEX, DEC, ASCII POP B ; RESTORE REGS POP D POP H RET ; PRINT HEADER AND RETURN NEW LINE COUNT IN A HEADER: PUSH D ; SAVE REGS PUSH H CALL PRINT DB CR,LF,' Rel Offset ',0 LXI H,SDISK ; PRINT DISK/USER CALL PRUD LXI D,FCBS CALL PRFN ; PRINT FILE NAME CALL SPACER ; 5 SPACES CALL SPACER CALL SPACE1 LXI H,DDISK ; PRINT DISK/USER CALL PRUD LXI D,FCBD CALL PRFN ; PRINT FILE NAME CALL PRINT DB CR,LF,' Hex Dec Hex Dec Asc Hex Dec Asc',0 LDA LSET ; SET LINE COUNT SUI 1 ; ADJUST FOR HEADING AND FOOTER STA LCNT POP H POP D ; RESTORE REGS RET ; PRINT A AS HEX, DEC, AND ASCII PRVAL: CALL SPACER ; 3 SPACES CALL SPACE1 CALL PA2HC ; PRINT AS HEX CALL SPACER CALL PADC ; PRINT AS DEC CALL SPACER ANI 7FH ; MASK OUT MSB CPI 7FH ; DOT FOR JZ PRDOT CPI ' ' ; PRINT DOT IF LESS THAN JNC COUT PRDOT: MVI A,'.' ; PRINT DOT JMP COUT ; PRINT 2 SPACES SPACER: PUSH PSW ; SAVE A MVI A,' ' ; CALL COUT POP PSW SPACE1: PUSH PSW MVI A,' ' CALL COUT POP PSW RET ; ; LOAD BUFFER FROM FILE WHOSE FCB IS PTED TO BY DE ; ON OUTPUT, BCNT=NUMBER OF BLOCKS LOADED (UP TO BLIMIT) ; LOAD: XRA A ; A=0 STA BCNT ; SET BLOCK COUNT ; MAIN LOAD LOOP LOAD1: CALL F$READ ; READ A BLOCK ORA A ; END OF FILE? RNZ ; RETURN IF DONE PUSH D ; SAVE FCB PTR LXI D,BUFF ; PT TO BUFFER MVI B,128 ; COPY 128 BYTES LOAD2: LDAX D ; GET BYTE READ MOV M,A ; PUT BYTE INX H ; PT TO NEXT INX D DCR B ; COUNT DOWN JNZ LOAD2 POP D ; GET FCB PTR LDA BCNT ; GET BLOCK COUNT INR A ; INCREMENT IT STA BCNT ; SET IT CPI BLIMIT ; LAST BLOCK READ? JNZ LOAD1 RET ; ; LOG IN SOURCE (LOGS) AND DESTINATION (LOGD) DRIVES/USERS ; LOGS: LDA SDISK ; GET DISK DCR A ; A=0 MOV B,A LDA SUSER ; GET USER MOV C,A CALL LOGUD ; LOG IN RET LOGD: LDA DDISK ; GET DISK DCR A ; A=0 MOV B,A LDA DUSER ; GET USER MOV C,A CALL LOGUD ; LOG IN RET ; ; PRINT DISK/USER PTED TO BY HL (2 BYTES) ; PRUD: MOV A,M ; GET DISK ADI 'A'-1 ; 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 ; BUFF1: DS 2 ; PTR TO BUFFER 1 BUFF2: DS 2 ; PTR TO BUFFER 2 OFFSET: DS 2 ; RELATIVE OFFSET FIRST: DS 1 ; ERROR INDIC LSET: DS 1 ; NUMBER OF TEXT LINES ON SCREEN LCNT: DS 1 ; LINE COUNT COMP: DS 1 ; COMPARE FLAG (0=NO SIMPLE COMPARE) MULT: DS 1 ; MULTIPLE RUN FLAG (0=NO MULT RUNS) 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 BCNT: DS 1 ; BUFFER COUNT BCNT1: DS 1 ; SECOND BUFFER COUNT INLINE: DS 2 ; PTR TO INPUT LINE BUFFER END