; ; CRCK.ASM version 4.2 ; by Keith Petersen, W8SDZ ; (revised 10/6/80) ; ;----> NOTE: MUST BE ASSEMBLED BY MAC <---- ; ;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. ;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): ; ;10/06/80 FIX TO ERASE TEMPORARY FILE WHEN OUTPUT FILE IS ; REQUESTED AND NAME NOT FOUND IN DIRECTORY. (KBP) ; ;10/05/80 FIX ABORT ROUTINE TO CHECK FOR FILE REQUEST ; AND TO CLOSE INCOMPLETE FILE BEFORE ERASING ; IT. ADDED TESTS FOR NO FILE NAME AND FILE ; READ ERROR. (KBP) ; ;10/04/80 ADD ROUTINE TO GIVE OPTION TO MAKE ; DISK FILE OF CRC's. FIXED MFA ROUTINE ; SO FIRST-TIME FLAG NOW SHOWS IF NO FILE ; FOUND. (KBP) ; ;04/21/80 ADD MULTIPLE FILENAME FEATURE, PRINT ; NAME OF CURRENT FILE BEING READ, AND ; ALLOW OPERATOR ABORT. (KBP) ; ;08/20/79 FIX BUG IN READIT ROUTINE WHICH ; SHOWED UP ONLY ON ALTCPM OPTION. (KBP) ; ;08/19/79 ADD CONDITIONAL ASSEMBLY FOR CP/M ; ON H8 OR TRS-80. (KBP) ; ;06/27/79 FIRST WRITTEN BY KEITH PETERSEN, W8SDZ ; ;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 SRCHF EQU 17 SRCHN EQU 18 READ EQU 20 STDMA EQU 26 BDOS EQU BASE+5 FCB EQU BASE+5CH FCBEXT EQU FCB+12 FCBRNO EQU FCB+32 FCB2 EQU BASE+6CH ; ;Program starts here ; ORG BASE+100H ; MACLIB SEQIO ;DEFINE MACRO LIBRARY USED ; CRCK: JMP BEGIN ;JUMP AROUND IDENTIFICATION DB 'CRCK.COM 4.2 10/6/80' ; 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 4.2',CR,LF DB 'CTL-S pauses, CTL-C aborts',CR,LF,0 LDA FCB2+1 ;GET OPTION STA FFLAG ;SAVE IT FOR LATER CPI 'F' ;FILE WANTED? JNZ AGAIN ;NO, SKIP FILE INIT ; ;'Declare' FCB for output file ;(temporarily named CRCKLIST.$$$) ; FILE OUTFILE,CRCFILE,,CRCKLIST,$$$,BSIZE ; 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.$$$ FINIS CRCFILE ; ;Build FCB for final name of CRCKLIST.CRC FILE SETFILE,FINAL,,CRCKLIST,CRC ; ;Erase any existing old file ERASE FINAL ; ;Rename CRCKLIST.$$$ to CRCKLIST.CRC RENAME FINAL,CRCFILE ; ;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: DB CR,LF,'--> FILE: ' FNAME: DB 'XXXXXXXX.XXX',TAB,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,BASE+80H ;BUFFER LOCATION ; NORD: MOV A,M ;GET FILE CHARACTER STA MESS ;SAVE FOR DIVP INX H SHLD BUFAD ;UPDATE BUFFER ADR CALL DIVP ;CALCULATE NEW CRC 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 MVI A,' ' CALL TYPE ;TYPE A SPACE 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++$' ; ;--------------------------------------------- ;An 8080 routine for generating a CYCLIC- ;REDUNDANCY-CHECK. Character leaves that ;character in location REM. By Fred Gutman. ;From 'EDN' magazine, June 5, 1979 issue, page 84. ; DIVP: LHLD REM ;GET REMAINDER MOV A,H ANI 128 ;Q-BIT MASK PUSH PSW ;SAVE STATUS DAD H ;2 X R(X) LDA MESS ;MESSAGE BIT IN LSB ADD L MOV L,A POP PSW JZ QB2 ;IF Q-BIT IS ZERO ; QB: MOV A,H XRI 0A0H ;MS HALF OF GEN. POLY MOV H,A MOV A,L XRI 97H ;LS HALF OF GEN. POLY MOV L,A ; QB2: SHLD REM 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 PUT CRCFILE ;SEND IT TO THE FILE 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. The routine is commented in Pseudo ;code, each Pseudo code statement is in <<...>> ; MFNAME: ;<> MVI C,STDMA LXI D,BASE+80H CALL BDOS XRA A STA FCBEXT STA FCBRNO ;<> LDA MFFLG1 ORA A JZ MFN01 ;<> ;Save orig request LXI H,FCB LXI D,MFREQ LXI B,12 CALL MOVER LDA FCB STA MFCUR ;SAVE DISK IN CURR FCB ;<> LXI H,MFREQ LXI D,FCB LXI B,12 CALL MOVER MVI C,SRCHF LXI D,FCB CALL BDOS ;<> JMP MFN02 ; MFN01: ;<> LXI H,MFCUR LXI D,FCB LXI B,12 CALL MOVER MVI C,SRCHF LXI D,FCB CALL BDOS ;<> LXI H,MFREQ LXI D,FCB LXI B,12 CALL MOVER MVI C,SRCHN LXI D,FCB CALL BDOS ;<> MFN02: ;<> INR A STC RZ ;<> DCR A ANI 3 ADD A ADD A ADD A ADD A ADD A ADI 81H MOV L,A MVI H,BASE SHR 8 PUSH H ;SAVE NAME POINTER LXI D,MFCUR+1 LXI B,11 CALL MOVER ;<> POP H LXI D,FCB+1 LXI B,11 CALL MOVER ;<> 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 ;FALL INTO TESTIT TO CHECK THIRD ; 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 ABEXT3 ;NO FILE, SKIP FILE STUFF FINIS CRCFILE ;CLOSE INCOMPLETE FILE ERASE CRCFILE ;ERASE INCOMPLETE FILE ; ABEXT3: CALL ERXIT ;PRINT MSG, EXIT DB CR,LF,CR,LF,'++ABORTED++$' ; ;Exit with message ; MSGEXIT:EQU $ ;EXIT W/"INFORMATIONAL" MSG ERXIT: POP D ;GET MSG MVI C,PRINT CALL BDOS ; ;Exit, restoring stack and return to CCP ; EXIT: LHLD STACK SPHL RET ;TO CCP ; ;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 ; ;Define location of file write buffer BUFFERS:EQU $ ; END CRCK