; TITLE 'CCITCRC 1.1 (85/02/18/)' ; CCITCRC Version 1.1 (Based on CRCK by Keith Petersen, W8SDZ) ; update "ver" equate below ; ; CCITCRC is a program to read any file and print a cyclic- ; redundancy-check number based on the CCITT standard ; polynomial: ; ; x^16 + x^15 + x^13 + x^7 + x^4 + x^2 + x + 1 ; ; Useful for checking accuracy of file transfers, and more ; accurate than a simple checksum. Optionally will write an ; output file to the default drive, listing the CRC's of all ; files checked in a single session. A second value is ; output, which, if patched into the last 2 bytes of the ; file, will cause the CRC to be zero. Thus files with such ; patches no longer require a CRCKLIST for validation. This ; assumes the file space is available. The MSDOS implementa- ; tion allows the first value to be ADDED to the file, and ; the file length extended by 2 bytes. Again, after this ; the first CRC value output will be zero. No file extension ; is needed for text files, for example, where some 1A eof ; marks are being replaced (provided at least one is left). ; ; Commands: ccitcrc [drive:] [f] ; ; Examples: ; ; ccitcrc myfile.asm (check only myfile.asm) ; ccitcrc *.asm (check all .asm files ; ccitcrc *.* f (check all files, make result file) ; ; ; Program updates/fixes (these are written in reverse order ; to minimize reading time to find latest update): ; ; CCITCRC 1.1 85/2/18 ; 85/3/1 cbf. Inasmuch as the CPM86 version was highly ; confusing, due to the original mechanical translation, ; and the extra clarity of the CPM80 version, the MSDOS ; version has been hand-converted from the Z80 code. The ; function remains unchanged. The system calls have been ; isolated to facilitate switching between operating ; systems. To allow for MASM idiosyncracies the source ; right margin has been restricted to column 62. This ; allows listing on 94 column printers. The corresponding ; cpm80 version has been modified to isolate all system ; calls. Thus virtually identical source is used for all ; The MSDOS version allows for incomplete final records, ; albeit crudely. Note that such files (whose length is ; not a multiple of 128) will give different results under ; CPM and MSDOS, unless the CRC value is patched in and ; the file is then padded with zero bytes. In future the ; program should be revised to use file handles, paths, etc., ; which will make it diverge from the CPM implementations. ; Possibly such divergence should be conditioned on the ; MSDOS version, maintaining compatibility with CPM and ; allowing use on MSDOS 1.x. For now this at least provides ; the fundamental file checking function. ; Useful for checking accuracy of file transfers, and more ; accurate than a simple checksum. Optionally will write an ; output file to the default drive, listing the CRC's of all ; files checked in a single session. A second value is ; output, which, if patched into the last 2 bytes of the ; file, will cause the CRC to be zero. Thus files with such ; patches no longer require a CRCKLIST for validation. This ; assumes the file space is available. ; ; 1.0 ; 85/2/5 converted back to cpm80 from CRCK5.1 for CPM86, and ; added the look-back crc value. Crcs are now output as ; bytes. The name has been revised because the output of this ; system is not compatible with the widely disseminated ; earlier CRCK program. In addition the CRC value is ; initialized to 0FFFFh, so that initial runs of zeroes will ; affect the CRC. Zero initialization cannot detect ; variations in length of files of zero bytes, however the ; value of 0ffffh is sensitive to all input sequences, except ; for one that causes a 0 CRC to be generated, followed by ; zeroes. An extremely unlikely occurance. The 2nd value is ; meaningless for null files. ; C.B. Falconer, 680 Hartford Tpk, Hamden, CT. (203) 281-1438 ; ; 04/10/82 version 5.1, Kelly Smith for CPM86 ; ; Removed requirement for MAC.ASM and SEQIO.LIB for assembly ; ; 11/27/81 version 5.0, Dave Barker ; ; All earlier versions of CRCK.ASM (up to at least Ver. 4.2 ; of 10/06/80) seen by this writer (DAB) have a serious flaw ; in the algorithm used to generate the CRC value. Mr. ; Petersen used a routine from "EDN" magazine, June 5, 1979. ; Although the routine published in EDN was a workable one, ; the way in which it was applied in CRCK.ASM was incorrect ; (i.e. the routine should have been called 8 times per ; byte, each time with only one bit of the message in the A ; register, then, at the end of the file, 2 null bytes ; should have been processed as if they were part of the ; file). The method that is used in CRCK.ASM Version 5.0 is ; a table lookup method. Instead of calling a routine 8 ; times each byte of the message is processed in one short ; piece of straight line code. The table that is used in ; this method is first generated during initialization. ; ; - Validity - ; ; Version 5.0 generates exactly the same CRC value that the ; earlier versions would have generated if they had ; correctly used the algorithm. The message (the file) is ; processed in the order: MS bit of the MS byte first (if the ; file were to be processed as a serial data transmission, ; then the LS bit of the MS byte would come first --> the ; order in which it is transmitted through a UART). ; ; Note: Usually, the CRC of a message is appended to the end ; of a message when it is sent. This causes the resultant ; CRC at the receiving end to be zero (this is the reason ; that the 2 dummy null bytes are added to the end of the ; message when the CRC is generated or checked). ; ; ; ; define true and false ; FALSE EQU 0 TRUE EQU NOT FALSE VER EQU 11 ; ; conditional assembly switches ; STDCPM EQU TRUE ; True is standard cp/m ALTCPM EQU FALSE ; True is h8 or trs-80 NOSYS EQU FALSE ; True if sys files not wanted STKSIZE EQU 64 ; 64 for cpm80, 256 for 8086 ; ; system equates ; BASE EQU 0 ; ; bdos equates ; RDCON EQU 1 WRCON EQU 2 CIO EQU 6 PRINT EQU 9 CSTAT EQU 11 OPEN EQU 15 CLOSE EQU 16 SRCHF EQU 17 SRCHN EQU 18 DELET EQU 19 READ EQU 20 WRITE EQU 21 MAKE EQU 22 RENAM EQU 23 STDMA EQU 26 STBAS EQU 51 ; BDOS EQU BASE+5 ; FCB EQU BASE+5CH FCBEXT EQU FCB+12 FCBRNO EQU FCB+32 FCB2 EQU BASE+6CH ; TBUF EQU BASE+80H ; Temporary buff (default) adr IBUFSIZ EQU 80H ; Buffer size (128 bytes) ; OBUFSZ EQU 128 ; Byte output buffer ; LF EQU 0AH ; Line feed character CR EQU 0DH ; Carriage return character EOF EQU 'Z'-40H ; End-of-file character ; ; CCIT CRC polynomial mask bytes ; HIMSK EQU 0A0H ; High mask byte LOMSK EQU 097H ; Low mask byte ; ; ; ; program starts here ; ORG BASE+100H ; BEGIN: LXI SP,STKTOP ; Make local stack LDA FCB+1 CPI ' ' ; See if name there LXI D,HELPMSG JZ EXEUNT ; No, print msg, then exit LXI D,HEADER CALL PRTMSG ; Signon XRA A STA FCBCRCFILE+12 ; Clear extent STA FCBCRCFILE+32 ; And current rcd count ; " " ; generate the lookup table for fast crc MOV C,A ; Zero the table index LXI H,HITAB GLOOP: XCHG LXI H,0 ; Init the crc MOV A,C CALL LCRC XCHG ; De now has the crc, MOV M,D ; Hl pointing into table INR H ; Store the high byte of crc MOV M,E ; Store the low byte DCR H INX H ; Move to next table entry INR C ; Next index JNZ GLOOP ; " " ; Check for "F" option LDA FCB2+1 ; Get option STA FFLAG ; Save it for later CPI 'F' ; File wanted? JNZ AGAIN ; No, skip file init LXI H,OBUFSZ ; Set buffer size SHLD CRCFLGH LXI H,0 ; Set next to fill SHLD CRCFPTR LXI D,FCBCRCFILE ; Delete 'old' ccitlist file PUSH D MVI A,DELET CALL DOS POP D MVI A,MAKE ; Make 'new' crcklist file CALL DOS INR A ; Make ok? JNZ AGAIN LXI D,DIRFULL ; Directory is full CALL PRTMSG JMP FILERR ; AGAIN: LXI SP,STKTOP ; Make local stack CALL MFNAME ; Search for names JNC NAMTST ; Another found, print name LDA MFFLG1 ; Nothing found, check... ORA A ; First time flag LXI D,FNOTFND JNZ ABEXIT ; None found, msg, then exit LDA FFLAG ; See if we're making file CPI 'F' JNZ DONE ; No, skip the file stuff ; " " ; close ccitlist.$$$ CLOSECRC: LHLD CRCFPTR MOV A,L ANI 07FH JNZ CLOSE1 SHLD CRCFLGH CLOSE1: MVI A,EOF ; Fill sector with eof marks PUSH PSW CALL PUTCRCFILE POP PSW JNZ CLOSECRC LXI D,FCBCRCFILE MVI A,CLOSE CALL DOS INR A CZ MSGNOCLOSE LXI D,FCBFINAL ; Erase any existing old file MVI A,DELET CALL DOS LXI H,FCBCRCFILE ; Rename CCITLIST.$$$ LXI D,FCBFINAL ; To CCITLIST.CRC PUSH H LXI B,16 DAD B XCHG CALL MOVER POP D MVI A,RENAM CALL DOS ; " " ; now exit to system DONE: LXI D,DUNMSG JMP EXEUNT ; Print done, then exit ; ; test for names to ignore NAMTST: IF NOSYS ; If $SYS file, ignore it LDA FCB+10 ; Get $SYS file attribute ANI 080H ; Is it $SYS? JNZ AGAIN ; Yes, ignore this file ENDIF ; Nosys ; " " ; ignore files with .$$$ filetype (they are usually ; zero-length and clutter up our display. we also ; want to ignore our own ccitlist.$$$ temporary file). LXI H,FCB+9 ; Point to filetype in fcb CALL TSTBAD ; Check for .$$$ files JZ AGAIN ; If zero flag, ignore them LXI H,FCB+1 LXI D,FNAME PUSH D LXI B,8 CALL MOVER ; Move file name to fname INX D LXI B,3 ; Move file type to fname CALL MOVER POP D ; Print filename.typ CALL PRTNAME LXI D,FCB MVI A,OPEN ; Open the file CALL DOS INR A LXI D,OPENFAIL JZ ABEXIT ; " " ; initialize crc to zero and set bufad to cause initial read RDINIT: LXI H,-1 SHLD REM ; Init remainder to 0ffffh LXI H,BASE+100H SHLD BUFAD ; Init buffer adrs ; " " ; this is the read loop READIT: LHLD BUFAD MOV A,H ; Time to read? CPI BASE SHR 8 JZ NORD ; No read MVI A,CSTAT CALL DOS ; Check for operator abort ORA A JZ READ2 ; Nothing from operator MVI A,RDCON CALL DOS ; Get character inputted CPI 'C'-40H ; Control c? JZ ABEXT2 ; Yes exit ; " " READ2: LXI D,FCB MVI A,READ ; Read another sector of file CALL DOS ORA A ; Check return code JNZ FINISH ; Error or eof LXI H,TBUF ; Buffer location ; " " NORD: MOV A,M ; Get file character INX H SHLD BUFAD ; " " ; table lookup method for crc generation ; save 2 previous values for a file patch LHLD REM1BAK SHLD REM2BAK LHLD REM ; Pick up the partial remainder SHLD REM1BAK XCHG ; De now has the partial MVI B,0 XRA D MOV C,A LXI H,HITAB DAD B MOV A,M XRA E MOV D,A INR H MOV E,M XCHG SHLD REM JMP READIT ; Go read more characters ; FINISH: CPI 1 ; Normal end-of-file? JNZ FILERR ; No, it was a read error LHLD REM ; Print crc CALL T4HX MVI A,' ' CALL DISPLAY LHLD REM2BAK CALL T4HX ; Show it CALL CRLF ; Turn up new line JMP AGAIN ; See if more files to do ; ; hl contains the partial, a the character to be crc'd LCRC: PUSH B MVI B,8 XRA H MOV H,A LOOP: DAD H JNC SKIP MVI A,HIMSK XRA H MOV H,A MVI A,LOMSK XRA L MOV L,A SKIP: DCR B JNZ LOOP POP B RET ; ; Output (hl) as 2 2-digit hex values, with separator ; a,f T4HX: MVI A,' ' CALL DISPLAY MOV A,H CALL HEXO MVI A,' ' CALL DISPLAY MOV A,L ; " " ; hex output ; a,f HEXO: PUSH PSW ; Save for right digit RRC ; Right.. RRC ; Justify.. RRC ; Left.. RRC ; Digit.. CALL NIBBL ; Print left digit POP PSW ; Restore right ; " " NIBBL: ANI 0FH ; Isolate digit CPI 10 ; Is is <10? JC ISNUM ; Yes, not alpha ADI 7 ; Add alpha bias ; " " ISNUM: ADI '0' ; Make printable JMP DISPLAY ; Print it, then return ; ; send carriage return, line feed to output ; a,f CRLF: MVI A,CR ; Carriage return CALL DISPLAY MVI A,LF ; Line feed, fall into 'type' ; " " ; send character in a register to output ; a,f DISPLAY: PUSH B PUSH D PUSH H ANI 7FH ; Strip parity bit MOV E,A CALL COUTA ; Send char. to console CALL WRFILE ; Write to file if requested POP H POP D POP B RET ; ; print message to console and optional file till 0 byte PRTNAME: LDAX D INX D ORA A RZ CALL DISPLAY JMP PRTNAME ; ; write character in e register to output file WRFILE: LDA FFLAG ; Get file trigger CPI 'F' ; Is it set? RNZ ; No, return MOV A,E ; Get character back ; " " PUTCRCFILE: PUSH PSW ; Save output character LHLD CRCFLGH ; Get current buffer length XCHG ; De has length LHLD CRCFPTR ; Load next to get/put to hl MOV A,L ; Compute current length SUB E MOV A,H SBB D ; Carry if next < length JC PUTCRC4 ; Carry if length > current LXI H,0 ; End of buff, fill empty buff SHLD CRCFPTR ; Clear next to get/put ; " " ; process next disk sector PUTCRC1: XCHG ; File pointer to de LHLD CRCFLGH ; Hl is maximum buffer length MOV A,E ; Compute next length SUB L ; To get carry, if more fill MOV A,D SBB H JNC PUTCRC3 LHLD CRCFBADDR ; Got carry, more to fill yet DAD D ; Hl is next buffer address XCHG CALL SETDMA ; Set dma address LXI D,FCBCRCFILE ; Fcb address to de MVI A,WRITE ; File write CALL DOS ORA A ; Check return code JNZ PUTCRC2 ; End-of-file yet? LXI D,IBUFSIZ ; Not eof, increment lgh by 128 LHLD CRCFPTR ; Next to fill DAD D SHLD CRCFPTR ; Save new pointer JMP PUTCRC1 ; Process another sector ; ; got end-of-file PUTCRC2: LXI D,DSKFULL ; Disk is full CALL PRTMSG POP PSW ; Clean stack JMP FILERR ; File error, exit ; ; end of buffer, reset dma and pointer PUTCRC3: LXI D,TBUF ; Point to temporary buffer CALL SETDMA ; Set dma function LXI H,0 ; Reset pointer for next to get SHLD CRCFPTR ; " " ; process the next character PUTCRC4: XCHG ; Index to get/put in de LHLD CRCFBADDR ; Base of buffer DAD D ; Address of character in hl XCHG ; And swap to de POP PSW ; Get save character STAX D ; Character to buffer LHLD CRCFPTR ; Index to get/put INX H ; And update for next character SHLD CRCFPTR RET ; ; multi-file access subroutine. allows processing ; of multiple files (i.e. *.asm) from disk. this ; routine builds the proper name in the fcb each ; time it is called. carry is set if no more names ; can be found. MFNAME: LXI D,TBUF ; Init dma addr and fcb CALL SETDMA XRA A STA FCBEXT STA FCBRNO ; " " ; if first time LDA MFFLG1 LXI B,12 LXI H,FCB ORA A JZ MFN01 ; " " ; save the requested name LXI D,MFREQ CALL MOVER LDA FCB STA MFCUR ; Save disk in curr fcb MVI A,SRCHF ; Srchf requested name JMP MFN02 ; " " ; else MFN01: XCHG LXI H,MFCUR ; Srchf current name CALL MOVER LXI D,FCB MVI A,SRCHF CALL DOS MVI A,SRCHN ; Srchn requested name ; endif ; " " MFN02: PUSH PSW LXI H,MFREQ LXI D,FCB LXI B,12 CALL MOVER POP PSW ; Srchf or srchn LXI D,FCB CALL DOS ; For function (a) INR A ; Return carry if not found STC RZ ; " " ; move name found to current name DCR A ANI 3 ADD A ADD A ADD A ADD A ADD A ADI 81H ; Point to dir entry MOV L,A MVI H,0 PUSH H ; Save name pointer LXI D,MFCUR+1 CALL MOVE11 POP H ; Move name found to fcb LXI D,FCB+1 CALL MOVE11 ; " " ; setup fcb XRA A STA FCBEXT STA FCBRNO STA MFFLG1 ; Turn off 1st time sw RET ; ; check for .$$$ files TSTBAD: CALL TESTIT ; Check first one for '$' RNZ ; No, return CALL TESTIT ; Check second one RNZ ; No, return TESTIT: MOV A,M ANI 7FH ; Strip attribute CPI '$' ; Check for $ filetype INX H RET ; ; move 11 bytes from (hl) to (de) ; a,f,b,c,d,e,h,l MOVE11: LXI B,11 ; " " ; move (bc) bytes from (hl) to (de) MOVER: MOV A,M STAX D INX H INX D DCX B MOV A,B ORA C JNZ MOVER RET ; ; file read error abort FILERR: LXI D,RDERR ; " " ; aborted - print reason. if making output file, ; close the incomplete file to update cp/m's bit map, ; then erase it. ABEXIT: CALL PRTMSG ; Print msg ; " " ABEXT2: LDA FFLAG ; See if we are making file CPI 'F' JNZ ABEXT5 ; No file, skip file stuff ABEXT3: LXI H,CRCFPTR MOV A,L ANI 07FH JNZ ABEXT4 SHLD CRCFLGH ABEXT4: MVI A,EOF PUSH PSW CALL PUTCRCFILE POP PSW JNZ ABEXT3 LXI D,FCBCRCFILE PUSH D MVI A,CLOSE CALL DOS INR A CZ MSGNOCLOSE POP D MVI A,DELET ; Erase incomplete file CALL DOS ; " " ABEXT5: LXI D,ABORT ; " " ; exit with message EXEUNT: CALL PRTMSG ; " " ; exit, via system warm boot EXIT: LXI D,0 ; Don't alter subsystems MVI A,0 ; Warm boot to cpm JMP DOS ; ; Message "cant close" file MSGNOCLOSE: LXI D,NOCLOSE ; " " ; output zero terminated string (de)^ PRTMSG: LDAX D ORA A RZ INX D CALL COUTA JMP PRTMSG ; ; output (a) to console ; a,f COUTA: PUSH B PUSH D PUSH H MOV E,A MVI A,CIO CALL DOS POP H POP D POP B RET ; ; Set dma to (de) SETDMA: MVI A,STDMA ; " " ; dos call, function (a), arguments as passed ; All dos calls pass through here, to ease system change DOS: MOV C,A JMP BDOS ; DIRFULL: DB CR,LF DB '++No Directory Space for CRC File++',0 ; DSKFULL: DB CR,LF DB '++No Disk Space for CRC File++',0 ; NOCLOSE: DB CR,LF DB '++Cannot Close CRC File++',0 ; ABORT: DB CR,LF,'++Aborted++',0 DUNMSG: DB 'Done',0 FNOTFND: DB '++File Not Found++',0 OPENFAIL: DB '++Open Failed++',0 RDERR: DB '++File Read Error++',0 ; HELPMSG: DB 'USAGE: ccitcrc ambigfn [f]', CR,LF DB ' The 2nd value output is that which, if patched' DB CR,LF DB ' into the last 2 file bytes, will cause a ZERO' DB CR,LF DB ' crc. Most CRCKLIST files can thus be eliminated' DB CR,LF DB ' The "F" (optional) parameter causes the output' DB CR,LF DB ' to be written to the file "CCITLIST.CRC"' DB CR,LF,0 ; HEADER: DB '------- CCITCRC Ver. ' DB VER / 10 + '0', '.', VER MOD 10 + '0' DB ' -------',CR,LF DB 'CTRL-S to Pause, CTRL-C to Abort' DB CR,LF,LF,0 ; FNAME: DB 'xxxxxxxx.xxx CRC =',0 ; ; program storage area, initialized MFFLG1: DB 1 ; 1st time switch ; CRCFBADDR: DW BUFF ; Pointer to buffer address ; ; build fcb for final name of ccitlist.crc FCBFINAL: DB 0,'CCITLISTCRC' DB 0 DS 20 ; ; 'declare' fcb for output file ; (temporarily named ccitlist.$$$) FCBCRCFILE: DB 0,'CCITLIST$$$' ; " " ; uninitialized storage DS 21 ; Part of fcbcrcfile ; DS STKSIZE ; Local stack STKTOP: ; HITAB: DS 512 ; The 2 tables for crc lookup FFLAG: DS 1 ; File write request flag MFREQ: DS 12 ; Requested name MFCUR: DS 12 ; Current name BUFAD: DS 2 ; Read buffer address REM2BAK:DS 2 REM1BAK:DS 2 ; History, for file patching REM: DS 2 ; Crc remainder storage ; CRCFPTR:DS 2 CRCFLGH:DS 2 ; Size of "buff" BUFF: DS OBUFSZ ; Buffer crc file data here ; END