; ; PROGRAM: DIFF ; AUTHOR: Richard Conn ; VERSION: 1.6 ; DATE: 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 16 ; ; 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. ; ; ; 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. ; ; TERMINAL CONSTANT LSET EQU 22 ; NUMBER OF LINES/SCREEN ; 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 ; ; CRC ROUTINES EXT CRCCLR,CRCUPD,CRCDONE ; SYSLIB ROUTINES 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 ZGPINS,ZFNAME EXT CODEND ; ; 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 000H ; MULTIPLE COMMAND LINE? MXREQD: DB 000H ; MAX USER/DISK? UDREQD: DB 000H ; 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 CALL CODEND ; ALLOCATE BUFFER SPACE SHLD BUFF1 ; SOURCE 1 BUFFER LXI D,BSIZE ; SIZE OF BUFFER INR D ; ADD 1 DAD D SHLD BUFF2 ; SOURCE 2 BUFFER 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 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 PRS1 ; PRINT FILE NAMES CALL PRS2 CALL LOGS ; LOG IN SOURCE DISK/USER 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,'NO Differences Noted in Files',0 CALL CRLF ; NEW LINE MLOOP2: CALL CRLF LDA MULT ; CHECK FOR MULTIPLE RUNS ORA A ; 0=NO JNZ MLOOP RET FERR: CALL PRINT DB CR,LF,'File Not Found -- ',0 CALL PRFN JMP MLOOP2 ; ; 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,'DIFF is used to display all relative differences ' DB 'between two files' DB CR,LF DB CR,LF,'DIFF is invoked by a command like:' DB CR,LF,' DIFF 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,'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 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 '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 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: 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 File ',0 MOV A,C CALL COUT ; PRINT LETTER CALL PRINT DB ' has terminated before the other -- DIFF Aborting',0 RET ; MATCH ERROR NOMATCH: 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 -- Type to Continue or ' DB '^C or A to Abort - ',0 CALL CIN ; GET RESPONSE CALL CAPS CPI 'A' ; ABORT? JZ NMAT00 CPI 3 ; ABORT? JNZ NMAT0 NMAT00: POP B ; CLEAR REGS POP D POP H POP D ; CLEAR STACK POP D CALL PRINT DB CR,LF,'DIFF Aborting',0 RET 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 MVI A,LSET ; SET LINE COUNT SUI 3 ; 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 MOV B,A LDA SUSER ; GET USER MOV C,A CALL LOGUD ; LOG IN RET LOGD: LDA DDISK ; GET DISK 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' ; 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 LCNT: DS 1 ; LINE COUNT 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