*####################################################################### * PROGRAM COMP...File Compare Utility * * Dr. David C. Wilcox * DCW Industries, Inc. * 5354 Palm Dr., La Canada, CA 91011 * 818/790-3844 * * April 13, 1986 *##################################################################### boot equ 00 *warm boot rchar equ 01 *console input wchar equ 02 *console output print equ 09 *print string constat equ 11 *console status openf equ 15 *open file readf equ 20 *sequential read setdma equ 26 *set dma address userf equ 32 *get/set user number break equ 03 *ascii etx (^C) lf equ 10 *line feed cr equ 13 *carriage return xoff equ 19 *ascii dc3 (^S) esc equ 27 *ascii escape space equ 32 *ascii space upmask equ $5f *upper case mask bdos equ $0002 *BDOS entry point *##################################################################### * Special registers: * * a4 = address of 2nd parsed fcb * a5 = address of dma buffer * a6 = address of 1st parsed fcb * d4 = number of records in file2 * d5 = total number of records * d6 = number of records in file1 * d7 = current user number *##################################################################### * * Locate fcb and dma (for portability) * link a6,#0 *mark stack frame move.l 8(a6),a0 *get base page address lea $80(a0),a5 *get address of dma buffer lea $5c(a0),a6 *get address of 1st parsed file name lea $38(a0),a4 *get address of 2nd parsed file name jsr clear *clear all data registers * * Check for no files specified * cmpi.b #space,$1(a6) bne start move.l #usage,d1 *if no parameters... jsr pstring *display instructions jsr quit *and exit to CP/M * start: movea.l a5,a1 *point to dma move.b (a1)+,d2 *put total characters in d2 * move.l #0,d5 *get the name of file1 movea.l #file1,a0 sfile1: move.b (a1)+,(a0)+ addq #1,d5 subq #1,d2 *make sure we're not out of characters beq error *before we find a space cmpi.b #space,(a1) bne sfile1 adda.l #1,a1 *skip over the space * move.l #0,d4 *now get the name of file2 movea.l #file2,a0 dfile1: move.b (a1)+,(a0)+ addq #1,d4 subq #1,d2 *make sure we're not out of characters blt error *before we find the null cmpi.b #0,(a1) bne dfile1 * * Parse the filespecs and make valid fcb's if necessary * jsr chkwld *check for wildcards jsr usrset *set default user numbers jsr usrchk *check for compare across user areas * * Open the files * jsr open1 *open file1 jsr open2 *open file2 move.b #0,32(a6) *set current record to zero for move.b #0,32(a4) *both file1 and file2 * * Fill the buffers and compare 16k at a time * move.l #0,d5 *initialize record count fillem: jsr chkcon *check console jsr filbuf2 *fill buffer2 jsr chkcon *check console jsr filbuf1 *fill buffer1 jsr compare *compare byte by byte cmpi.w #0,d5 *any more extents? beq quit *no....all done bra fillem *yes...go back for more * *####################################################################### * Subroutines *####################################################################### * * Change user number if different from current user * chgusr: cmp.b d7,d1 beq donusr move.b d1,d7 move.w #userf,d0 trap #bdos donusr: rts * * Check for character entered at keyboard * chkcon: move.w #constat,d0 *check console status trap #bdos cmpi.b #0,d0 *anything there? beq chkdon *no....return getchr: move.w #rchar,d0 *yes...get the character trap #bdos cmpi.b #break,d0 *is it an abort (^C)? beq uabort *yes...say so and return to CP/M cmpi.b #xoff,d0 *is it a pause (^S)? bne chkdon *no....ignore it and return bra getchr *yes...hang until another char entered uabort: move.l #ustop,d1 *display user abort message jsr pstring bra quit *and return to CP/M chkdon: rts * * Check for wildcards in parsed fcb's * chkwld: move.l #11,d2 movea.l a6,a2 movea.l a4,a3 adda #1,a2 adda #1,a3 cloop: cmpi.b #'?',(a2)+ beq nowild cmpi.b #'?',(a3)+ beq nowild subq.b #1,d2 bne cloop rts * * Clear all data registers * clear: clr.l d0 clr.l d1 clr.l d2 clr.l d3 clr.l d4 clr.l d5 clr.l d6 clr.l d7 rts * * Clear fcb * clrfcb: movea.l a0,a2 move.l #11,d3 blank1: move.b #space,(a2)+ subq #1,d3 bne blank1 move.l #20,d3 blank2: move.b #0,(a2)+ subq #1,d3 bne blank2 rts * * Compare the two files and report any differences found * compare: move.l d4,d3 cmp.l d4,d6 *same number of sectors? beq samlen *yes...continue bgt eof2 move.b #1,flag1 *mark file1 as shortest move.l d6,d3 bra samlen eof2: move.b #1,flag2 *mark file2 as shortest samlen: add.l d3,d5 *update record count cmpi.b #128,d3 *is this the last extent? beq skip *no....continue move.b #1,lastex *yes...set flag skip: clr.l d2 *zero the byte count movea.l #buffer1,a1 *point to file1 movea.l #buffer2,a2 *point to file2 compr: cmpm.b (a1)+,(a2)+ *compare a byte beq same jsr shodif *show difference message addi.b #1,count *increment difference count movea.l #count,a0 *check for 10 differences cmpi.b #10,(a0) beq abort *give up if 10 differences same: addq.b #1,d2 *increment byte count cmpi.b #128,d2 *end of record? bne compr *no....go back for another character subq.b #1,d3 *decrement record count beq nomore *quit if all records compared jsr chkcon *check the console clr.l d2 *reset the byte counter bra compr *and go back for another record nomore: movea.l #lastex,a0 *is this the last extent? cmpi.b #1,(a0) beq finish *yes...print final message rts finish: movea.l #count,a0 *check difference count cmpi.b #0,(a0) *any found? bne unequal *yes...say so and quit move.l #crlf,d1 jsr pstring *no....display identical message move.l #file1,d1 jsr pstring move.l #eqmsg1,d1 jsr pstring move.l #file2,d1 jsr pstring move.l #eqmsg2,d1 jsr pstring unequal:movea.l #flag1,a0 *check for early eof on file1 cmpi.b #1,(a0) *flag set? bne check2 *no....check file2 move.l #efmsg1,d1 *yes...say so and quit jsr pstring move.l #file1,d1 jsr pstring move.l #efmsg2,d1 jsr pstring move.l #file2,d1 jsr pstring move.l #crlf,d1 jsr pstring bra alldone check2: movea.l #flag2,a0 *check for early eof on file2 cmpi.b #1,(a0) *flag set? bne alldone *no...exit move.l #efmsg1,d1 *yes...say so and quit jsr pstring move.l #file2,d1 jsr pstring move.l #efmsg2,d1 jsr pstring move.l #file1,d1 jsr pstring move.l #crlf,d1 jsr pstring bra alldone abort: move.l #abtmsg,d1 jsr pstring alldone:move.l #0,d5 *d5 = 0 causes exit rts * * Show record and byte where difference exists * shodif: move.l #dfmsg1,d1 *print first part of message jsr pstring move.l d5,d1 *compute record number sub.l d3,d1 jsr decprt *display it move.l #dfmsg2,d1 *print second part of message jsr pstring move.l d2,d1 *display byte number jsr decprt move.l #crlf,d1 *conclude with cr,lf jsr pstring rts * * Print d1 in decimal * decprt: move.l d3,d3save *save register d3 move.l d4,d4save *save register d4 move.l d1,d4 clr.l d3 *print 1000's digit move.w d4,d3 divu #1000,d3 move.l d3,d4 jsr digit swap d4 clr.l d3 *print 100's digit move.w d4,d3 divu #100,d3 move.l d3,d4 jsr digit swap d4 clr.l d3 *print 10's digit move.w d4,d3 divu #10,d3 move.l d3,d4 jsr digit swap d4 move.w d4,d1 *print units digit addi.b #'0',d1 jsr type movea.l #d3save,a0 *restore d3 and d4 move.l (a0),d3 movea.l #d4save,a0 move.l (a0),d4 rts digit: move.b d3,d1 addi.b #'0',d1 *make it ascii type: move.w #wchar,d0 *and print it trap #bdos rts * * Fill buffer1 * filbuf1: clr.l d1 *select appropriate user number movea.l #user1,a0 move.b (a0),d1 jsr chgusr move.l #0,d6 *initialize record count movea.l #buffer1,a3 *point to start of buffer loopf1: move.l a3,d1 *set dma to current location move.w #setdma,d0 *in buffer1 trap #bdos move.l a6,d1 *point to fcb1 move.w #readf,d0 *and read a record trap #bdos cmpi.b #1,d0 *end of file? beq donfl1 *yes...stop reading addq.b #1,d6 *no....increment record count cmpi.b #128,d6 *have we done a complete extent? beq donfl1 *yes...stop reading, buffer is full adda.l #128,a3 *no....add 128 to buffer address bra loopf1 *and go back for another record donfl1: rts * * Fill buffer2 * filbuf2: clr.l d1 *select appropriate user number movea.l #user2,a0 move.b (a0),d1 jsr chgusr move.l #0,d4 *initialize record count movea.l #buffer2,a3 *point to start of buffer loopf2: move.l a3,d1 *set dma to current location move.w #setdma,d0 *in buffer2 trap #bdos move.l a4,d1 *point to fcb1 move.w #readf,d0 *and read a record trap #bdos cmpi.b #1,d0 *end of file? beq donfl2 *yes...stop reading addq.b #1,d4 *no....increment record count cmpi.b #128,d4 *have we done a complete extent? beq donfl2 *yes...stop reading, buffer is full adda.l #128,a3 *no....add 128 to buffer address bra loopf2 *and go back for another record donfl2: rts * * Open file1 * open1: clr.l d1 movea.l #user1,a0 move.b (a0),d1 jsr chgusr move.l a6,d1 move.w #openf,d0 trap #bdos cmpi.b #$ff,d0 bne donop1 move.l #nofile,d1 jsr pstring move.l #file1,d1 jsr pstring move.l #crlf,d1 jsr pstring bra quit donop1: rts * * Open file2 * open2: clr.l d1 movea.l #user2,a0 move.b (a0),d1 jsr chgusr move.l a4,d1 move.w #openf,d0 trap #bdos cmpi.b #$ff,d0 bne donop2 move.l #nofile,d1 jsr pstring move.l #file2,d1 jsr pstring move.l #crlf,d1 jsr pstring bra quit donop2: rts * * Display the string addressed by d1 on the console * pstring: move.w #print,d0 trap #bdos rts * * Display error message and quit * error: move.l #errmsg,d1 jsr pstring * * Quit to CP/M * quit: clr.l d1 movea.l #user0,a0 move.b (a0),d1 jsr chgusr move.w #boot,d0 trap #bdos * * Display no wildcard message * nowild: move.l #wldmsg,d1 jsr pstring bra quit * * Fill the fcb's * filfcb: movea.l a3,a0 adda #1,a0 *point to first char in file name jsr clrfcb *fill fcb with spaces move.l #8,d3 *d3 is the counter lname: move.b (a1)+,d0 *get character from dma string cmpi.b #'*',d0 *disallow wildcards beq nowild cmpi.b #'?',d0 beq nowild cmpi.b #'.',d0 *filetype delimiter? beq lnamef cmpi.b #'a',d0 *lower case? blt nsave andi.b #upmask,d0 *make it upper case nsave: move.b d0,(a0)+ *store it in fcb subq #1,d3 beq lnamef *all 8 chars processed subq #1,d2 beq fildon *no more chars in dma string bra lname *go back for another character lnamef: subq #1,d2 beq fildon *no more chars in dma string movea.l a3,a0 *make a0 point to first adda #9,a0 *char in type field cmpi.b #'.',(a1) *are we pointing to delimiter? bne fnext *no...carry on adda #1,a1 *yes...skip over it subq #1,d2 beq fildon *no more chars in dma string fnext: move.l #3,d3 *d3 is the counter ltype: move.b (a1)+,d0 *get character from dma string cmpi.b #'*',d0 *disallow wildcards beq nowild cmpi.b #'?',d0 beq nowild cmpi.b #'a',d0 *lower case? blt msave andi.b #upmask,d0 *make it upper case msave: move.b d0,(a0)+ *store it in fcb subq #1,d3 beq fildon *all 3 chars processed subq #1,d2 beq fildon *no more chars in dma string bra ltype *go back for another character fildon: rts * * Check for file1 user area * usrchk: movea.l #file1,a1 move.b d5,d2 move.w #1,d1 schek: cmpi.b #':',(a1) beq sdrv adda #1,a1 addq #1,d1 subq #1,d2 bne schek bra dchek0 *no user specified...check file2 sdrv: cmpi.b #2,d1 *is it the logged user? bne sdig1 *no...check number of digits bra dchek0 *yes...further action not required sdig1: cmpi.b #3,d1 *is it a single digit user? bne sdig2 *no...check for two digit user movea.l #file1,a1 *get the drive number move.b (a1)+,d0 andi.b #upmask,d0 *make it upper case subi.b #64,d0 *make it a number move.b d0,(a6) *save it in fcb1 move.b (a1),d0 *get user number subi.b #'0',d0 *make it a number move.b d0,user1 *save user number in proper location move.b d5,d2 subq #3,d2 movea.l #file1+3,a1 *copy the rest to fcb1 movea.l a6,a3 jsr filfcb bra dchek0 sdig2: cmpi.b #4,d1 *is it a two digit user? bne error *no...incorrect user number movea.l #file1,a1 *get the drive number move.b (a1)+,d0 andi.b #upmask,d0 *make it upper case subi.b #64,d0 *make it a number move.b d0,(a6) *save it in fcb1 movea.l #file1+2,a1 *get the second digit of user number move.b (a1),d0 *put it in d0 subi.b #'0',d0 *make it a number addi.b #10,d0 *add 10 to it move.b d0,user1 *save user number in proper location move.b d5,d2 subq #4,d2 movea.l #file1+4,a1 *copy the rest to fcb1 movea.l a6,a3 jsr filfcb dchek0: movea.l #file2,a1 *check for file2 user area move.b d4,d2 move.w #1,d1 dchek: cmpi.b #':',(a1) beq ddrv adda #1,a1 addq #1,d1 subq #1,d2 bne dchek move.b d4,d2 *no drive or user specified movea.l #file2,a1 *fill fcb2 in case a user was movea.l a4,a3 *given for file1 which sometimes jsr filfcb *zaps the second fcb rts ddrv: cmpi.b #2,d1 *is it the logged user? bne ddig1 *no...check number of digits movea.l #file2,a1 *get the drive number move.b (a1)+,d0 andi.b #upmask,d0 *make it upper case subi.b #64,d0 *make it a number move.b d0,(a4) *save it in fcb2 move.b d4,d2 *no user specified subq #2,d2 movea.l #file2+2,a1 *fill fcb2 in case a user was movea.l a4,a3 *given for file1 which sometimes jsr filfcb *zaps the second fcb rts ddig1: cmpi.b #3,d1 *is it a single digit user? bne ddig2 *no...check for two digit user movea.l #file2,a1 *get the drive number move.b (a1)+,d0 andi.b #upmask,d0 *make it upper case subi.b #64,d0 *make it a number move.b d0,(a4) *save it in fcb2 move.b (a1),d0 *get user number subi.b #'0',d0 *make it a number move.b d0,user2 *save user number in proper location move.b d4,d2 subq #3,d2 movea.l #file2+3,a1 *copy the rest to fcb2 movea.l a4,a3 jsr filfcb rts ddig2: cmpi.b #4,d1 *is it a two digit user? bne error *no...incorrect user number movea.l #file2,a1 *get the drive number move.b (a1)+,d0 andi.b #upmask,d0 *make it upper case subi.b #64,d0 *make it a number move.b d0,(a4) *save it in fcb2 movea.l #file2+2,a1 *get the 2nd digit of user number move.b (a1),d0 *put it in d0 subi.b #'0',d0 *make it a number addi.b #10,d0 *add 10 to it move.b d0,user2 *and store it in proper location move.b d4,d2 subq #4,d2 movea.l #file2+4,a1 *copy the rest to fcb2 movea.l a4,a3 jsr filfcb rts * * Set default user numbers * usrset: move.w #$ff,d1 move.w #userf,d0 trap #bdos move.b d0,d7 move.b d0,user0 move.b d0,user1 move.b d0,user2 rts *##################################################################### * Console Messages *##################################################################### even d3save: ds.l 1 d4save: ds.l 1 count: dc.b 0 *mismatch count flag1: dc.b 0 *file1 eof flag flag2: dc.b 0 *file2 eof flag lastex: dc.b 0 *last extent flag user0: ds.b 1 *user number at start of run user1: ds.b 1 *user number for file1 file1: dc.b '$$$$$$$$$$$$$$$$' dc.b '$$$$$$$$$$$$$$$$' *name of file1 user2: ds.b 1 *user number for file2 file2: dc.b '$$$$$$$$$$$$$$$$' dc.b '$$$$$$$$$$$$$$$$' *name of file2 even usage: dc.b lf,cr dc.b 'Correct usage:',cr,lf,lf dc.b ' COMP {d1:}file1 {d2:}file2' dc.b cr,lf,lf dc.b ' d1 = file1 drive',cr,lf dc.b ' u1 = file1 user number',cr,lf dc.b ' d2 = file2 drive',cr,lf dc.b ' u2 = file2 user number',cr,lf,lf dc.b 'COMPare terminates if 10 differences are found' dc.b cr,lf,lf,'$' even errmsg: dc.b 'File name error...COMP aborted',cr,lf,'$' wldmsg: dc.b 'Error...Wildcards NOT supported',cr,lf,'$' nofile: dc.b 'Error...Unable to open $' eqmsg1: dc.b ' and $' eqmsg2: dc.b ' are identical',cr,lf,'$' efmsg1: dc.b cr,lf,'End of file on $' efmsg2: dc.b ' before $' dfmsg1: dc.b 'Files differ in sector $' dfmsg2: dc.b ', byte $' crlf: dc.b cr,lf,'$' abtmsg: dc.b cr,lf dc.b ' 10 differences found..COMP aborted ' dc.b cr,lf,'$' ustop: dc.b cr,lf dc.b ' COMP aborted by user ' dc.b cr,lf,'$' even buffer1 equ * buffer2 equ *+16384 *##################################################################### end