; CRCK.ASM Version 5.1 (Originally by: Keith Petersen, W8SDZ) ; ; CRCK is a program to read any CP/M file and print a cyclic- ; redundancy-check number based on the CCITT standard polynominal: ; ; 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. ; ; Commands: crck [drive:] [f] ; ; Examples: ; ; crck myfile.asm (check only myfile.asm) ; crck *.asm (check all .asm files ; crck *.* f (check all files, make file of results) ; ; ; Program updates/fixes (these are written in reverse order to ; minimize reading time to find latest update): ; ; 04/10/82 version 5.1, Kelly Smith ; ; 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 ; ; 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 ; ; system equates ; base set 0 if altcpm base set 4200h endif ; altcpm ; ; define write buffer size (presently set for 8k) ; bsize equ 8*1024 ; disk write buffer size ; ; bdos equates ; rdcon equ 1 wrcon equ 2 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 ; 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 buffer (default) address buf$siz equ 80h ; buffer size (128 bytes) ; crcfilesiz equ 2000h ; tab equ 09h ; tab character 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 h,0 ; get stack... dad sp ; pointer so we can... shld stack ; save it lxi sp,stack ; initialize local stack call crlf ; turn up a new line lda fcb+1 cpi ' ' ; see if name there jnz begin2 ; yes, continue call erxit ; print msg, then exit db '++No File Name Specified++',cr,lf,'$' ; begin2: call ilprt ; print: db '--------- CRCK Ver 5.1 ---------' db cr,lf db 'CTRL-S to Pause, CTRL-C to Abort' db cr,lf,cr,lf,0 ; ; generate the lookup table for fast crc ; lxi h,hitab mvi c,0 ; the table index gloop: xchg lxi h,0 ; init the crc mov a,c call lcrc xchg ; de now has the crc, hl pointing into table mov m,d ; store the high byte of crc inr h mov m,e ; store the low byte dcr h inx h ; move to next table entry inr c ; next index jnz gloop lda fcb2+1 ; get option sta fflag ; save it for later cpi 'F' ; file wanted? jnz again ; no, skip file init xra a sta fcbcrcfile+12 ; clear extent sta fcbcrcfile+32 ; clear current record count lxi h,crcfilesiz ; set buffer size shld crcfilelen lxi h,0 ; set next to fill shld crcfileptr mvi c,delet ; delete file function lxi d,fcbcrcfile ; delete 'old' crcklist file call bdos mvi c,make ; make file function lxi d,fcbcrcfile ; make 'new' crcklist file call bdos inr a ; make ok? jnz again mvi c,print ; print string function lxi d,dir$full ; indicate that directory is full call bdos jmp filerr ; ; ; putcrcfile: ; push psw ; save output character lhld crcfilelen ; get current buffer length xchg ; de has length lhld crcfileptr ; 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 buffer, fill (empty) buffers shld crcfileptr ; clear next to get/put ; putcrc1: ; process next disk sector ; xchg ; file pointer to de lhld crcfilelen ; 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 crcfileadr ; got carry, more to fill yet dad d ; hl is next buffer address xchg mvi c,stdma ; set dma address call bdos lxi d,fcbcrcfile ; fcb address to de mvi c,write ; file write call bdos ora a ; check return code jnz putcrc2 ; end-of-file yet? lxi d,buf$siz ; not eof, increment length by 128 lhld crcfileptr ; next to fill dad d shld crcfileptr ; save new pointer jmp putcrc1 ; process another sector ; putcrc2: ; got end-of-file ; mvi c,print ; print string function lxi d,dsk$full ; disk is full call bdos pop psw ; clean stack jmp filerr ; file error, exit ; putcrc3: ; end of buffer, reset dma and pointer ; lxi d,tbuf ; point to temporary buffer mvi c,stdma ; set dma function call bdos lxi h,0 ; reset pointer for next to get shld crcfileptr ; putcrc4: ; process the next character ; xchg ; index to get/put in de lhld crcfileadr ; 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 crcfileptr ; index to get/put inx h ; and update for next character shld crcfileptr ret ; again: lxi sp,stack; re-init stack pointer call mfname ; search for names jnc namtst ; another found, print name lda mfflg1 ; nothing found, check... ora a ; ... first time flag jz done ; at least one was found call abexit ; print msg, then exit db '++File Not Found++$' ; done: lda fflag ; see if we're making file cpi 'F' jnz done2 ; no, skip the file stuff ; ; close crcklist.$$$ ; closecrc: ; lhld crcfileptr mov a,l ani 07fh jnz close1 shld crcfilelen close1: mvi a,eof push psw call putcrcfile pop psw jnz closecrc mvi c,close lxi d,fcbcrcfile call bdos inr a jnz erase mvi c,print lxi d,no$close call bdos ; ; erase any existing old file ; erase: mvi c,delet lxi d,fcbfinal call bdos ; ; rename crcklist.$$$ to crcklist.crc ; lxi h,fcbcrcfile lxi d,fcbfinal push h lxi b,16 dad b ; mov$name: ; ldax d mov m,a inx d inx h dcr c jnz mov$name pop d mvi c,renam call bdos ; ; now exit to cp/m ; done2: call msgexit ; print done, then exit db cr,lf,'Done$' ; ; test for names to ignore ; namtst: if nosys lda fcb+10 ; get sys attribute ani 80h ; 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 crcklist.$$$ temporary file). ; lxi h,fcb+9 ; point to filetype in fcb call tstbad ; check for .$$$ files jz again ; if zero flag, ignore them ; ; move 8 characters from fcb+1 to fname ; lxi h,fcb+1 lxi d,fname lxi b,8 call mover ; ; move 3 characters from fcb+9 to fname+9 ; lxi h,fcb+9 lxi d,fname+9 lxi b,3 call mover ; ; now print filename.type ; call ilprt ; print: ; fname: db 'xxxxxxxx.xxx',tab,'CRC = ',0 ; ; open the file ; lxi d,fcb mvi c,open call bdos inr a jnz rdinit call abexit db '++Open Failed++$' ; ; initialize crc to zero and set bufad to cause initial read ; rdinit: lxi h,0 shld rem ; init remainder to zero 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 c,cstat call bdos ; check for operator abort ora a jz read2 ; nothing from operator mvi c,rdcon call bdos ; get character inputted cpi 'C'-40h ; control c? jz abext2 ; yes exit ; read2: lxi d,fcb mvi c,read ; read another sector of file call bdos 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 lhld rem ; pick up the partial remainder ; ; table lookup method for crc generation ; 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 lda rem+1 ; get msp of crc call hexo ; print it lda rem ; get lsp of crc call hexo ; print it call crlf ; turn up new line jmp again ; see if more files to do ; filerr: call abexit ; abort because of file read error db '++File Read Error++$' ; ; 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 ; ; hex output ; hexo: push psw ; save for right digit rar ; right.. rar ; ..justify.. rar ; ..left.. rar ; ..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 type ; print it, then return ; ; ; ; inline print routine ; ilprt: xthl ; save hl, get msg ; ilplp: mov a,m ; get char call type ; output it inx h ; point to next mov a,m ; test ora a ; ..for end jnz ilplp xthl ; restore hl, ret addr ret ; ret past msg ; ; ; ; send carriage return, line feed to output ; crlf: mvi a,cr ; carriage return call type mvi a,lf ; line feed, fall into 'type' ; ; send character in a register to output ; type: push b push d push h ani 7fh ; strip parity bit mov e,a push d call wrfile ; write to file if requested pop d mvi c,wrcon ; send character to console call bdos pop h pop d pop b ret ; ; ; ; 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 call putcrcfile 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: ; init dma addr and fcb mvi c,stdma lxi d,tbuf call bdos xra a sta fcbext sta fcbrno ; ; if first time ; lda mfflg1 ora a jz mfn01 ; ; save the requested name ; lxi h,fcb lxi d,mfreq lxi b,12 call mover lda fcb sta mfcur ; save disk in curr fcb ; ; srchf requested name ; lxi h,mfreq lxi d,fcb lxi b,12 call mover mvi c,srchf lxi d,fcb call bdos ; ; else ; jmp mfn02 ; mfn01: ; srchf current name lxi h,mfcur lxi d,fcb lxi b,12 call mover mvi c,srchf lxi d,fcb call bdos ; ; srchn requested name ; lxi h,mfreq lxi d,fcb lxi b,12 call mover mvi c,srchn lxi d,fcb call bdos ; ; endif ; mfn02: ; return carry if not found inr a stc rz ; ; move name found to current name ; dcr a ani 3 add a add a add a add a add a adi 81h mov l,a mvi h,0 push h ; save name pointer lxi d,mfcur+1 lxi b,11 call mover ; ; move name found to fcb ; pop h lxi d,fcb+1 lxi b,11 call mover ; ; 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 (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 ; ; ; ; aborted - print reason. if making output file, ; close the incomplete file to update cp/m's bit map, ; then erase it. ; abexit: pop d ; get msg adrs mvi c,print call bdos ; print msg ; abext2: lda fflag ; see if we are making file cpi 'F' jnz abext5 ; no file, skip file stuff abext3: lhld crcfileptr mov a,l ani 07fh jnz abext4 shld crcfilelen abext4: mvi a,eof push psw call putcrcfile pop psw jnz abext3 mvi c,close lxi d,fcbcrcfile call bdos inr a jnz era$crc mvi c,print lxi d,no$close call bdos ; ; erase incomplete file ; era$crc: ; mvi c,delet lxi d,fcbcrcfile call bdos ; abext5: call erxit ; print msg, exit db cr,lf,cr,lf,'++Aborted++$' ; ; exit with message ; msgexit:equ $ ; exit with "informational" message erxit: pop d ; get msg mvi c,print call bdos ; ; exit, restoring stack and return to ccp ; exit: lhld stack sphl ret ; to ccp ; ; dir$full: db cr,lf db '++No Directory Space for CRC File++' db '$' ; dsk$full: db cr,lf db '++No Disk Space for CRC File++' db '$' ; no$close: db cr,lf db '++Cannot Close CRC File++' db '$' ; ; program storage area ; fflag: db 0 ; file write request flag rem: dw 0 ; crc remainder storage mess: db 0 ; crc message char goes here mfflg1: db 1 ; 1st time switch mfreq: ds 12 ; requested name mfcur: ds 12 ; current name bufad: ds 2 ; read buffer address ; ds 60 ; stack area stack: equ $ oldstk: ds 2 ; old stack pointer saved here ; hitab: ds 512 ; the 2 tables for crc lookup ; crcfilelen: ; dw crcfilesiz ; crcfileptr: ; ds 2 ; ; build fcb for final name of crcklist.crc ; fcbfinal: ; db 0,'CRCKLISTCRC' db 0 ds 20 ; ; 'declare' fcb for output file (temporarily named crcklist.$$$) ; fcbcrcfile: ; db 0,'CRCKLIST$$$' db 0 ds 20 ; crcfileadr: ; dw crcfileadr+2 ; buffer all crc file data here ; ; ; end