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: ld sp,stktop; make local stack ld a,(fcb+1) cp a,' '; see if name there ld de,helpmsg jp z,exeunt; no, print msg, then exit ld de,header call prtmsg; signon xor a ld (fcbcrcfile+12),a; clear extent ld (fcbcrcfile+32),a; and current rcd count ; " " ; generate the lookup table for fast crc ld c,a; zero the table index ld hl,hitab gloop: ex de,hl ld hl,0; init the crc ld a,c call lcrc ex de,hl; de now has the crc, ld (hl),d; hl pointing into table inc h; store the high byte of crc ld (hl),e; store the low byte dec h inc hl; move to next table entry inc c; next index jp nz,gloop ; " " ; Check for "F" option ld a,(fcb2+1); get option ld (fflag),a; save it for later cp a,'F'; file wanted? jp nz,again; no, skip file init ld hl,obufsz; set buffer size ld (crcflgh),hl ld hl,0; set next to fill ld (crcfptr),hl ld de,fcbcrcfile; delete 'old' ccitlist file push de ld a,delet call dos pop de ld a,make; make 'new' crcklist file call dos inc a; make ok? jp nz,again ld de,dirfull; directory is full call prtmsg jp filerr ; again: ld sp,stktop; make local stack call mfname; search for names jp nc,namtst; another found, print name ld a,(mfflg1); nothing found, check... or a; ... first time flag ld de,fnotfnd jp nz,abexit; none found, msg, then exit ld a,(fflag); see if we're making file cp a,'F' jp nz,done; no, skip the file stuff ; " " ; close ccitlist.$$$ closecrc: ld hl,(crcfptr) ld a,l and 07fh jp nz,close1 ld (crcflgh),hl close1: ld a,eof; fill sector with eof marks push af call putcrcfile pop af jp nz,closecrc ld de,fcbcrcfile ld a,close call dos inc a call z,msgnoclose ld de,fcbfinal; erase any existing old file ld a,delet call dos ld hl,fcbcrcfile; rename CCITLIST.$$$ ld de,fcbfinal; to CCITLIST.CRC push hl ld bc,16 add hl,bc ex de,hl call mover pop de ld a,renam call dos ; " " ; now exit to system done: ld de,dunmsg jp exeunt; print done, then exit ; ; test for names to ignore namtst: if nosys; if $SYS file, ignore it ld a,(fcb+10); get $SYS file attribute and 080h; is it $SYS? jp nz,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). ld hl,fcb+9; point to filetype in fcb call tstbad; check for .$$$ files jp z,again; if zero flag, ignore them ld hl,fcb+1 ld de,fname push de ld bc,8 call mover; move file name to fname inc de ld bc,3; move file type to fname call mover pop de; print filename.typ call prtname ld de,fcb ld a,open; open the file call dos inc a ld de,openfail jp z,abexit ; " " ; initialize crc to zero and set bufad to cause initial read rdinit: ld hl,-1 ld (rem),hl; init remainder to 0ffffh ld hl,base+100h ld (bufad),hl; init buffer adrs ; " " ; this is the read loop readit: ld hl,(bufad) ld a,h; time to read? cp a,base shr 8 jp z,nord; no read ld a,cstat call dos; check for operator abort or a jp z,read2; nothing from operator ld a,rdcon call dos; get character inputted cp a,'C'-40h; control c? jp z,abext2; yes exit ; " " read2: ld de,fcb ld a,read; read another sector of file call dos or a; check return code jp nz,finish; error or eof ld hl,tbuf; buffer location ; " " nord: ld a,(hl); get file character inc hl ld (bufad),hl ; " " ; table lookup method for crc generation ; save 2 previous values for a file patch ld hl,(rem1bak) ld (rem2bak),hl ld hl,(rem); pick up the partial remainder ld (rem1bak),hl ex de,hl; de now has the partial ld b,0 xor d ld c,a ld hl,hitab add hl,bc ld a,(hl) xor e ld d,a inc h ld e,(hl) ex de,hl ld (rem),hl jp readit; go read more characters ; finish: cp a,1; normal end-of-file? jp nz,filerr; no, it was a read error ld hl,(rem); print crc call t4hx ld a,' ' call display ld hl,(rem2bak) call t4hx; show it call crlf; turn up new line jp again; see if more files to do ; ; hl contains the partial, a the character to be crc'd lcrc: push bc ld b,8 xor h ld h,a loop: add hl,hl jp nc,skip ld a,himsk xor h ld h,a ld a,lomsk xor l ld l,a skip: dec b jp nz,loop pop bc ret ; ; Output (hl) as 2 2-digit hex values, with separator ; a,f t4hx: ld a,' ' call display ld a,h call hexo ld a,' ' call display ld a,l ; " " ; hex output ; a,f hexo: push af; save for right digit rrca; right.. rrca; ..justify.. rrca; ..left.. rrca; ..digit.. call nibbl; print left digit pop af; restore right ; " " nibbl: and 0fh; isolate digit cp a,10; is is <10? jp c,isnum; yes, not alpha add a,7; add alpha bias ; " " isnum: add a,'0'; make printable jp display; print it, then return ; ; send carriage return, line feed to output ; a,f crlf: ld a,cr; carriage return call display ld a,lf; line feed, fall into 'type' ; " " ; send character in a register to output ; a,f display: push bc push de push hl and 7fh; strip parity bit ld e,a call couta; send char. to console call wrfile; write to file if requested pop hl pop de pop bc ret ; ; print message to console and optional file till 0 byte prtname: ld a,(de) inc de or a ret z call display jp prtname ; ; write character in e register to output file wrfile: ld a,(fflag); get file trigger cp a,'F'; is it set? ret nz; no, return ld a,e; get character back ; " " putcrcfile: push af; save output character ld hl,(crcflgh); get current buffer length ex de,hl; de has length ld hl,(crcfptr); load next to get/put to hl ld a,l; compute current length sub a,e ld a,h sbc a,d; carry if next < length jp c,putcrc4; carry if length > current ld hl,0; end of buff, fill empty buff ld (crcfptr),hl; clear next to get/put ; " " ; process next disk sector putcrc1: ex de,hl; file pointer to de ld hl,(crcflgh); hl is maximum buffer length ld a,e; compute next length sub a,l; to get carry, if more fill ld a,d sbc a,h jp nc,putcrc3 ld hl,(crcfbaddr); got carry, more to fill yet add hl,de; hl is next buffer address ex de,hl call setdma; set dma address ld de,fcbcrcfile; fcb address to de ld a,write; file write call dos or a; check return code jp nz,putcrc2; end-of-file yet? ld de,ibufsiz; not eof, increment lgh by 128 ld hl,(crcfptr); next to fill add hl,de ld (crcfptr),hl; save new pointer jp putcrc1; process another sector ; ; got end-of-file putcrc2: ld de,dskfull; disk is full call prtmsg pop af; clean stack jp filerr; file error, exit ; ; end of buffer, reset dma and pointer putcrc3: ld de,tbuf; point to temporary buffer call setdma; set dma function ld hl,0; reset pointer for next to get ld (crcfptr),hl ; " " ; process the next character putcrc4: ex de,hl; index to get/put in de ld hl,(crcfbaddr); base of buffer add hl,de; address of character in hl ex de,hl; and swap to de pop af; get save character ld (de),a; character to buffer ld hl,(crcfptr); index to get/put inc hl; and update for next character ld (crcfptr),hl 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: ld de,tbuf; init dma addr and fcb call setdma xor a ld (fcbext),a ld (fcbrno),a ; " " ; if first time ld a,(mfflg1) ld bc,12 ld hl,fcb or a jp z,mfn01 ; " " ; save the requested name ld de,mfreq call mover ld a,(fcb) ld (mfcur),a; save disk in curr fcb ld a,srchf; srchf requested name jp mfn02 ; " " ; else mfn01: ex de,hl ld hl,mfcur; srchf current name call mover ld de,fcb ld a,srchf call dos ld a,srchn; srchn requested name ; endif ; " " mfn02: push af ld hl,mfreq ld de,fcb ld bc,12 call mover pop af; srchf or srchn ld de,fcb call dos; for function (a) inc a; return carry if not found scf ret z ; " " ; move name found to current name dec a and 3 add a,a add a,a add a,a add a,a add a,a add a,81h; point to dir entry ld l,a ld h,0 push hl; save name pointer ld de,mfcur+1 call move11 pop hl; move name found to fcb ld de,fcb+1 call move11 ; " " ; setup fcb xor a ld (fcbext),a ld (fcbrno),a ld (mfflg1),a; turn off 1st time sw ret ; ; check for .$$$ files tstbad: call testit; check first one for '$' ret nz; no, return call testit; check second one ret nz; no, return testit: ld a,(hl) and 7fh; strip attribute cp a,'$'; check for $ filetype inc hl ret ; ; move 11 bytes from (hl) to (de) ; a,f,b,c,d,e,h,l move11: ld bc,11 ; " " ; move (bc) bytes from (hl) to (de) mover: ld a,(hl) ld (de),a inc hl inc de dec bc ld a,b or c jp nz,mover ret ; ; file read error abort filerr: ld de,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: ld a,(fflag); see if we are making file cp a,'F' jp nz,abext5; no file, skip file stuff abext3: ld hl,crcfptr ld a,l and 07fh jp nz,abext4 ld (crcflgh),hl abext4: ld a,eof push af call putcrcfile pop af jp nz,abext3 ld de,fcbcrcfile push de ld a,close call dos inc a call z,msgnoclose pop de ld a,delet; erase incomplete file call dos ; " " abext5: ld de,abort ; " " ; exit with message exeunt: call prtmsg ; " " ; exit, via system warm boot exit: ld de,0; don't alter subsystems ld a,0; warm boot to cpm jp dos ; ; Message "cant close" file msgnoclose: ld de,noclose ; " " ; output zero terminated string (de)^ prtmsg: ld a,(de) or a ret z inc de call couta jp prtmsg ; ; output (a) to console ; a,f couta: push bc push de push hl ld e,a ld a,cio call dos pop hl pop de pop bc ret ; ; Set dma to (de) setdma: ld a,stdma ; " " ; dos call, function (a), arguments as passed ; All dos calls pass through here, to ease system change dos: ld c,a jp 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