; TITLE 'CKCRC 1.1 (03/10/88)' ; CKCRC Version 1.0 (Based on CRCK by Keith Petersen, W8SDZ). ; Update "VER" equate below ; VER EQU 11 ; Current version number MONTH EQU 03 ; Month last modified DAY EQU 10 ; Day YEAR EQU 88 ; Year ; ; CKCRC will read any CP/M file and display a cyclic redundancy check ; value based on the CCITT standard polynomial: ; ; X^16 + X^15 + X^13 + X^7 + X^4 + X^2 + X + 1 ; ; and the value based on the standard polynomial for asynchronous use: ; ; X^16 + X^12 + X^5 + 1 ; ; Useful for checking accuracy of file transfers. Far more accurate ; than a simple checksum. The program will optionally write an output ; file to the default drive, listing the CRC values of all files ; requested. Also, optionally, the program will update the last 2 bytes ; of a file to force the CCITT calculation to zero. This immediately ; indicates that the file has been modified and avoids requiring the CRC ; list. The "CHEK" value is displayed to allow comparison of CRC values ; calculated by other programs, such as CHEK15, LU, and NULU. ; ; 08/22/87 This program is based on work done by Keith Petersen, ; Ver 1.0 Irv Hoff, C.B. Falconer, and others. ; ; 03/10/88 Added option to allow a "CHEK" CRC value to be specified ; Ver 1.1 on the command line. The program will then compare that ; value to the calculated value and pause, allowing the ; operator to abort the program, if an unequal condition ; occurs. Useful to check if a program has been corrupted. ; Bill Duerr ; FALSE EQU 0 TRUE EQU NOT FALSE ; ; System equates. ; ; BDOS equates. ; BDOS EQU 00005 ; CONIN EQU 1 ; Console input CIO EQU 6 ; Console input and output CSTAT EQU 11 ; Console input status OPEN EQU 15 ; Open a file CLOSE EQU 16 ; Close a file SRCHF EQU 17 ; Search for First Ambiguous File SRCHN EQU 18 ; Search for Next Ambiguous File DELETF EQU 19 ; Delete a File WRITEF EQU 21 ; Write a Record MAKE EQU 22 ; Make a new file READ EQU 20 ; Read sequential GETDRV EQU 25 ; Get drive number (a=0, b=1, etc) SETDMA EQU 26 ; Set Disk DMA Address CURUSR EQU 32 ; Get user number (CP/M 2.x) READRR EQU 33 ; Read Random ; FCB1 EQU 5CH ; Default FCB in low memory FCBEXT EQU FCB1+12 FCBUSR EQU FCB1+13 FCBRNO EQU FCB1+33 ; CMDLN EQU 80H ; Command line set up by CP/M CCP TBUF EQU 80H ; Temporary buffer (default) address ; LF EQU 0AH ; Line feed character CR EQU 0DH ; Carriage return character EOF EQU 'Z'-40H ; CP/M 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 100H ; START: LXI H,0 ; Make local stack SHLD DIRCNT ; Zero count of selected directory entries DAD SP ; Add the address of CCP stack SHLD STACK ; And save it for return LXI SP,STACK ; Set the stack pointer to the local stack CALL PARSE ; Parse the command line for input file name JZ START1 ; Anything left in command line LXI D,CRCFCB ; Pointer to FCB for output CALL PARSE1 ; Parse the command line for output file name START1: LHLD OPTION ; Address of option field in command line MOV A,M ; Get the option length INX H ; Point to first valid option DCR A ; Only one character specified JNZ START2 ; No, check if four characters MOV A,M ; Get the option STA OPTION ; Save it ; ; Operand on command line said "/xxxx", so get CRC ; START2: CPI 3 ; Were four characters in input line JNZ START6 ; No, invalid option XCHG ; Get address of options in DE LXI H,0 ; Clear saved CRC value START3: LDAX D ; Get character of CRC ORA A ; See if past CRC JZ START5 ; If so, go dump it INX D ; Point to next character DAD H ; HL = 2 x HL DAD H ; 4 DAD H ; 8 DAD H ; 16 CPI 'A' ; See if character is numeric? JC START4 ; No, go process it ANI 0DFH ; Make upper case SUI 7 ; Convert to HEX START4: SUI '0' ; Make binary ADD L ; Add this digit MOV L,A ; Put it back JMP START3 ; Loop until done ; START5: SHLD ICRC ; Save input CRC value MVI A,'C' ; Make the option flag a "C" STA OPTION ; START6: LDA FCB1 ; Get the drive from FCB ADI 'A'-1 ; Add ASCII offset STA DRVUSR ; Save in display area LDA FCBUSR ; Get user area for the file PUSH A ; Save the user area MOV E,A ; In C for BDOS call MVI C,CURUSR ; Update current user area CALL BDOS POP A ; Get back the user area LXI H,DRVUSR+1 ; Point to file name display area MVI M,'0' ; Assume user area is less than 10 CPI 10 ; Check and see JC START7 ; Not over 10 INR M ; Make user 10 or greater SUI 10 ; And subtract the 10 START7: ADI '0' ; Make ASCII INX H ; Point to units position of user MOV M,A ; Save in display area LXI H,FCB1+1 ; Get the address of first file name LXI D,DRVUSR+4 ; Point to file name area MVI B,8 ; Move 8 characters of file name CALL MOVE MVI A,'.' ; Separate with a dot STAX D ; Save in display area INX D ; Point to extent MVI B,3 ; Move 8 characters of file type CALL MOVE LXI D,HEADER ; Signon message and header line CALL PRTMSG ; Signon ; ; Generate the lookup table for fast CCITT CRC calculations. ; XRA A MOV C,A ; Zero the table index LXI H,CCITTB ; Point to start of table LCCIT: XCHG ; Get it in DE LXI H,0 ; Initialize the CRC MOV A,C ; Get table index in A ; ; HL contains the partial and A the character to be CRC'd. ; PUSH B ; Save the index MVI B,8 ; Initialize loop counter XRA H MOV H,A LCCIT1: DAD H ; Double it JNC LCCIT2 MVI A,HIMSK XRA H MOV H,A MVI A,LOMSK XRA L MOV L,A LCCIT2: DCR B ; One less character to go JNZ LCCIT1 ; Loop till done POP B ; Restore index value ; 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 LCCIT ; Loop till table initialized ; ; Now create the "CHEK" CRC table. ; LXI H,CHEKTB ; Address of the 512-byte look-up table MVI C,0 ; Table index ; LCHEK: XCHG LXI H,0 ; Initialize register pair MOV A,C PUSH B ; Save the index in "C" register MVI B,8 XRA H MOV H,A ; And back to H ; LCHEK1: DAD H ; Double it JNC LCHEK2 MVI A,10H ; High byte mask for SDLC XRA H MOV H,A MVI A,21H ; Low byte mask XRA L MOV L,A ; LCHEK2: DCR B JNZ LCHEK1 POP B XCHG ; DE pair now has CRC, HL pointing into table MOV M,D ; Store high byte of CRC INR H MOV M,E ; Store low byte DCR H INX H ; Move to next table entry INR C ; Get next index value JNZ LCHEK ; Loop till done ; ; Search and table the requested files. ; LXI H,BUFFER ; Get buffer address SHLD DIRPTR ; And save it as start of stored entries LXI D,FCB1 ; Address of default FCB MVI C,SRCHF ; Get first occurrence of requested file CALL BDOS LXI D,NOTFND ; Send warning message INR A ; Return 0FFH if no files found JZ ABEXIT ; Go display message and quit ; DIR1: DCR A ; Undo "INR" ADD A ; Offset to buffer address (x 2) ADD A ; (x 4) ADD A ; (x 8) ADD A ; (x 16) ADD A ; (x 32) length of each entry LXI H,TBUF ; Get address of the buffer MVI D,0 MOV E,A DAD D ; Calculate offset INX H ; Skip over user area XCHG ; Get back the table entry pointer in DE LHLD DIRCNT ; Count of entries found INX H ; Add one SHLD DIRCNT ; Store back LHLD DIRPTR PUSH H ; Save the directory pointer XCHG ; Get back the table entry pointer in DE MVI B,8 ; Move 8 bytes of file name CALL MOVE ; Move file name to table MVI A,'.' ; Separate file name and type with a "." STAX D INX D MVI B,3 ; Move 3 bytes of file type CALL MOVE ; Move file type to table XRA A ; Move in zero for display later STAX D POP H ; Get current table pointer LXI D,32 ; Make each entry 32 bytes long DAD D SHLD DIRPTR ; Save updated table pointer for next time LDA BDOS+2 ; Get start of BDOS address SUI 9 ; CCP base page minus fudge factor CMP H ; Has DIRPTR gotten up there? LXI D,OUTMEM ; Send warning message JZ ABEXIT ; Go display message and quit LXI D,FCB1 ; Find the next entry MVI C,SRCHN CALL BDOS INR A ; Done yet? JNZ DIR1 ; ; Table now contains all selected directory entries - display the file name ; and calculate CRC for each file. ; LXI H,BUFFER-32 ; Reset pointer to start of table SHLD DIRPTR ; Save it for later ; ; Display drive, current user number and file name to console. ; DIR2: LHLD DIRPTR ; Get current position in buffer LXI D,32 ; Length of table entry DAD D ; Adjust table entry pointer SHLD DIRPTR ; Save for next time PUSH H ; Save current position in buffer LXI D,FCB1+1 ; Point to default FCB MVI B,8 ; Length of file name CALL MOVE ; Move file name to FCB INX H ; Point after the "." MVI B,3 ; Length of file type CALL MOVE ; Move file type to FCB POP D ; Pointer to file name CALL PRTMSG ; ; Setup input FCB for read. ; XRA A STA FCBEXT ; Zero current extent STA FCB1+32 ; Zero current record ; ; Initialize CRC to zero and set BUFAD to cause initial read. ; LXI H,-1 SHLD FCBRNO ; Initialize relative record to -1 SHLD CCITVL ; Initialize CCIT remainder to 0FFFFH INX H SHLD CHEKVL ; Initialize CHEK remainder to low values SHLD REM2BAK ; Zero patched CRC in case zero length file LXI D,FCB1 ; Address of input FCB MVI C,OPEN ; OPEN BDOS function CALL BDOS ; Open the file INR A ; Was there an error JZ FILERR ; Display the message, then abort ; ; This is the read loop. ; READ1: LXI D,FCB1 ; Address of input FCB MVI C,READ ; READ BDOS function CALL BDOS ; Read another sector of the file ORA A ; Check return code JNZ FINISH ; Error or EOF LHLD FCBRNO ; Get the random record number INX H ; Add 1 SHLD FCBRNO ; Store back LXI H,TBUF ; Get start of input buffer ; READ2: MOV A,M ; Get character from the file SHLD BUFAD ; And save for next time ; ; Table lookup for CRC generation save 2 previous values for a file patch. ; PUSH A ; Save the character LHLD REM1BAK SHLD REM2BAK LHLD CCITVL ; Pick up the partial remainder SHLD REM1BAK LXI D,CCITTB ; Address of CCITT values CALL UPDCRC ; Update CRC for CCIT calculation SHLD CCITVL ; Save it for next time POP A ; Get back the current character LHLD CHEKVL ; Pick up the partial remainder LXI D,CHEKTB ; Address of "CHEK" values CALL UPDCRC ; Update CRC for CHEK calculation SHLD CHEKVL ; Save it for next time LHLD BUFAD ; Get current position in buffer INR L ; If buffer address less that 100 JNZ READ2 ; Don't have to fill the buffer MVI C,CSTAT ; Console status BDOS function CALL BDOS ; Check for operator abort ORA A JZ READ1 ; Nothing from operator MVI C,CONIN ; Read console BDOS function CALL BDOS ; Get character entered CPI 'C'-40H ; Control-C? JZ ABEXT1 ; Yes exit JMP READ1 ; No, go fill the buffer ; ; End of file routine, display calculated values. ; FINISH: DCR A ; Normal end-of-file return a "1" in A LXI D,RDERR JNZ ABEXIT ; No, it was a read error LHLD DIRPTR ; Get current position in buffer LXI D,12 ; Length of file name and file type DAD D ; Adjust table entry pointer PUSH H ; Save address of start of CRC values XCHG ; Get in DE LHLD CHEKVL ; Display CRC CALL HEX2 LHLD CCITVL ; Display CRC CALL HEX2 LHLD REM2BAK ; Display Patched CRC CALL HEX2 ; Show it XCHG ; Get address of end of CRC values in HL MVI M,0 ; Zero to terminate display POP D ; Get back start of CRC display area PUSH H ; Save end CALL PRTMSG ; Display the CRC values on the screen POP H ; Get back end of line MVI M,CR ; Terminate line with a CR/LF INX H MVI M,LF LHLD FCBRNO ; Get record (128 Byte sector) count INX H ; Add one MVI A,' ' ; Space to separate CALL COUT CALL DSPDEC ; Convert the record count to decimal CALL MODFL ; Check and see if we're modifying file LXI D,FCB1 ; Get the input file's FCB address MVI C,CLOSE ; CLOSE BDOS function CALL BDOS ; Close the file INR A ; Any error JZ FILERR ; Display error message and abort CALL CRLF ; Turn up new line LDA OPTION ; Get input option character CPI 'C' ; Are we checking a CRC value JNZ FIN1 ; No, go continue processing LHLD CHEKVL ; Calculated CRC XCHG ; Get it in DE LHLD ICRC ; Input CRC MOV A,H ; Check if equal XRA D JNZ ERRCRC ; Not equal, go to error routine MOV A,L XRA E JNZ ERRCRC ; Not equal, go to error routine JMP DONE ; Otherwise end the program FIN1: LHLD DIRCNT ; Get the number of entries to go DCX H SHLD DIRCNT MOV A,H ; Finished when DIRCNT reaches zero ORA L JNZ DIR2 ; Loop till all files read ; ; Check for "F" option -- CRCLIST to be written. ; LDA OPTION ; Get option CPI 'F' ; File wanted? JNZ DONE ; No, go end program LDA CRCFCB+13 ; Get specified user area or default MOV E,A MVI C,CURUSR ; Update current user area CALL BDOS LXI D,CRCFCB ; Address of CRCLIST FCB PUSH D ; Save the address of the FCB MVI C,DELETF ; DELETE BDOS function CALL BDOS ; Delete any CRCLIST.CRC POP D ; Get back the FCB address XRA A STA CRCFCB+12 ; Clear extent STA CRCFCB+32 ; And current record count MVI C,MAKE ; MAKE BDOS function CALL BDOS ; Make CRCLIST.CRC file INR A ; Make ok? JZ FILERR ; Display error message and abort LHLD DIRPTR ; Get pointer to next entry in table LXI D,32 ; Length of an entry DAD D ; Point to next available entry MOV A,L ; Low order byte of address ORI 80H ; Make multiple of 128 PTCRC1: MVI M,EOF ; Fill last sector with EOF indicators INX H ; Next position in buffer INR A JNZ PTCRC1 ; Loop till buffer filled SHLD DIRPTR ; Save end of buffer address LXI H,BUFFER ; Get start of buffer address ; PTCRC2: XCHG ; SETDMA needs buffer in DE LHLD DIRPTR ; Get end of buffer address MOV A,L ; Are we at end SUB E JNZ PTCRC3 ; No MOV A,H SBB D ; Now at end JZ PTCRC4 ; Yes, go close files ; PTCRC3: PUSH D ; Save position in buffer MVI C,SETDMA ; Set DMA to program buffer CALL BDOS LXI D,CRCFCB ; FCB address to DE MVI C,WRITEF ; File write CALL BDOS ORA A ; Check return code POP H ; Get back position in buffer JNZ FILERR ; Display error message and abort LXI D,128 ; Each sector is 128 bytes DAD D ; Adjust buffer pointer JMP PTCRC2 ; Process another sector ; ; Close CRCLIST.CRC. ; PTCRC4: LXI D,CRCFCB ; File's FCB address MVI C,CLOSE ; CLOSE BDOS function CALL BDOS ; Close the file INR A ; Any error JZ FILERR ; Display error message and abort JMP DONE ; MODFL: LDA OPTION ; Check Option field CPI 'R' ; Are we removing the CRC value in the file JZ MODFL4 ; Yes, go do it SUI 'U' ; Are we updating the CRC value in the file RC ; No, return to caller SUI 2 ; Return if not "U" or "V" RNC LHLD FCBRNO ; Get the relative record number in FCB INX H MOV A,H ; Check if zero length file ORA L RZ ; Return if zero LXI D,FCB1 MVI C,READRR ; Random read CALL BDOS LDA OPTION ; Get the option CPI 'U' ; Should CRC value be forced JZ MODFL2 ; Yes, bypass validation routine ; ; Check the last three bytes of the file for EOF or zero value. ; Update if equal, return if not. ; LHLD CCITVL ; Get the CRC value MOV A,H ; Is it zero ORA L RZ ; Return, CRC already zero LXI H,TBUF+125 ; Get position before the CRC value MOV A,M CPI EOF ; Is it end of file indicator JZ MODFL1 ORA A ; Is it a zero RNZ ; Doesn't verify, return to caller MODFL1: INX H ; Get next position CMP M ; Does it verify RNZ ; No, return to caller INX H ; Next position CMP M ; Does it verify RNZ ; No, return to caller ; MODFL2: LHLD REM2BAK ; Get patched value LXI B,UPDMSG ; Get "updated" message ; MODFL3: PUSH B ; Save address of message LXI D,TBUF+127 ; Point to last byte of buffer XCHG ; Switch DE with HL MOV M,E ; Put the value in buffer DCX H ; Point to second last byte MOV M,D ; Put the value in buffer LXI D,FCB1 ; File's FCB address MVI C,WRITEF ; WRITE BDOS function CALL BDOS ; Write out the sector ORA A ; Any error? JNZ FILERR ; Go display error message and abort POP D ; Get message back JMP PRTMSG ; Display message and return to caller ; MODFL4: LHLD CCITVL ; Get the CRC value MOV A,H ; Is it zero ORA L RNZ ; Return, can only remove from a zero CRC LXI D,FCB1 ; Get the address of the FCB MVI C,READRR ; Relative read function CALL BDOS LDA TBUF+125 ; Point to third last byte LXI B,REMMSG ; "removed" message CPI EOF ; Is it EOF indicator LXI H,01A1AH ; We'll move EOF values into file JZ MODFL3 LXI H,0000 ; We'll move zeros into file JMP MODFL3 ; ; Update the CRC with value in A, Start of Table in DE (must be on page ; boundary), Partial CRC in HL -- Return with new value in HL. ; UPDCRC: XCHG ; DE now has the partial CRC XRA D ; Update from old value MOV L,A ; HL points to indexed position in table MOV A,M ; Get the value from the table XRA E ; Update from old value MOV D,A INR H ; Position in second half of table MOV E,M ; Second value from table XCHG ; Switch DE and HL RET ; Return to caller with new value in HL ; ; Output (HL) as 2 2-digit hex values, with separator -- A,PSW. ; HEX2: MVI A,' ' ; Space to separate STAX D INX D STAX D INX D MOV A,H ; First character CALL HEXO MOV A,L ; Second character ; ; Hex output -- A,PSW. ; HEXO: PUSH PSW ; Save for right digit RRC ; Right RRC ; Justify RRC ; Left RRC ; Digit CALL NIBBL ; Display left digit POP PSW ; Restore right ; NIBBL: ANI 0FH ; Isolate digit CPI 10 ; Is is less than 10 JC ISNUM ; Yes, not alpha ADI 7 ; Add alpha bias ; ISNUM: ADI '0' ; Make displayable STAX D ; Save it in buffer INX D ; Point to next byte RET ; ; Send carriage return, line feed to output. ; CRLF: MVI A,CR ; Carriage return CALL COUT MVI A,LF ; Line feed ; ; Output "A" to console. ; COUT: PUSH B PUSH D PUSH H MOV E,A ; Get the character to be displayed MVI C,CIO ; Console I/O BDOS function CALL BDOS POP H POP D POP B RET ; ; Move "B" bytes from "HL" to "DE". ; MOVE: MOV A,M STAX D INX H INX D DCR B JNZ MOVE RET ; ; Parse drive/user from input command line pointed to by HL, output FCB ; pointed to by DE. OLDDRV will contain logged in drive, OLDUSR will ; contain logged in user area. Return with binary drive in first ; position of the FCB and binary user in 13th position of FCB. Fill in ; filename and filetype portion of the FCB. At exit, DE and HL point to ; the character after any file name specification. Drive/user validity ; checked and errors jump to "HELPER". ; PARSE: MVI C,GETDRV ; Get current drive CALL BDOS ; From BDOS (returned in A) INR A ; Make drive A=1, B=2, etc STA OLDDRV ; Save for later MVI E,0FFH MVI C,CURUSR ; Get current user number CALL BDOS ; From BDOS (returned in A) STA OLDUSR ; Save for later LXI H,CMDLN ; Get the address of command line MOV A,M ; Get length of command line ORA A ; Are there any parameters JZ HELPER ; No, display help message INR A ; Point to end of command line MOV B,A ; Save length ADD L ; Add length MOV L,A ; End of command line MVI C,0 ; Clear character count MOV M,C ; Mark end of line with zero MVI A,'/' ; Check for option indicator SCAN01: DCX H ; Go back one character INR C ; Add one to number of option characters DCR B ; One less character to go JZ SCAN02 ; No option specified, continue CMP M ; Check for slash JNZ SCAN01 ; Loop till no more characters in command DCX H ; Back up one more character MOV A,M CPI ' ' ; Is this a space JNZ SCAN02 ; No, might be part of file name MVI M,0 ; Indicate new end of line INX H ; Point back to the slash DCR C ; Subtract one from the length MOV M,C ; Store the length of the option field SHLD OPTION ; Save the start of the option field SCAN02: LXI H,CMDLN ; Point to command line LXI D,FCB1 ; Point to FCB for input ; PARSE1: INX H ; Point to next character in command line MOV A,M ; Get in A ORA A ; Are we done with command line JZ HELPER ; No parameters, display help message CPI ' '+1 ; Is it a blank or control character JC PARSE1 ; Skip leading blanks and control characters PUSH H ; Save location in the command line LDA OLDDRV ; Get the original logged in drive STAX D ; Move it to the FCB as default LXI H,13 ; Offset to user area in FCB DAD D ; HL contains address of user area LDA OLDUSR ; Get the original logged in user area MOV M,A ; Store it POP H ; Restore position in command line PUSH H ; Save again MVI B,4 ; Scan first four characters for colon ; PARSE2: INX H ; Point to next character DCR B ; One less character to go JZ PARSE8 ; No colon in the first 4 characters entered MOV A,M ; Get the character in A CPI '0' ; Is it a terminating character JC PARSE8 ; No drive/user specified CPI ':' ; Check for Colon JNZ PARSE2 ; Loop till found ; ; Colon found, process drive and user ; PARSE3: POP H ; Get back position of first valid character MOV A,M ; Get the character in A SUI 'A' ; Make drive ready for CP/M BDOS calls JC HELPER ; If not alpha, display help message ANI 1FH ; Convert lower case to upper case CPI 'P'-40H ; Check for valid drive JNC HELPER ; Invalid display help message ; ; Check for valid drive specification in A. Logged in drive always valid. ; Uses "DRVSUP" value and "OLDDRV" values ; INR A ; A=1, B=2, etc STAX D ; Save in FCB MOV B,A LDA OLDDRV ; Get the original logged in drive CMP B ; Has user specified the same drive JZ PARSE5 ; Logged in drive is ok LDA MAXDRV ; Get the maximum drives available CMP B ; Has user specified the same drive JC HELPER ; Logged in drive is ok PUSH H ; Save the address pointer LHLD DRVSUP ; Get the supported drives mask ; PARSE4: DAD H ; Shift the access bit into carry DCR B ; Number of times determined by drive JNZ PARSE4 ; Quit when counter gets to zero POP H ; Get back the address pointer JNC HELPER ; If no carry, display help message ; ; Convert the user field to binary ; PARSE5: INX H ; Point to next character MOV A,M ; Get it in A SUI '0' ; Must be numeric JC HELPER ; Invalid, display help message CPI 9+1 ; Upper limit of numeric test JZ PARSE7 ; Colon, no user specified JNC HELPER ; Invalid MOV B,A ; Save what could be tens digit INX H ; Point to next character MOV A,M ; Get it in A SUI '0' ; Must be numeric JC HELPER ; Invalid, display help message CPI 9+1 ; Upper limit of numeric test JZ PARSE6 ; If colon, binary user is in register B JNC HELPER ; Invalid MOV C,A ; Save units position MOV A,B ; Convert tens digit to binary ADD A ; 2* ADD A ; 4* ADD B ; 5* ADD A ; 10* ADD C ; Add in units digit MOV B,A ; Get it in B INX H ; Point to what should be colon PARSE6: LDA OLDUSR ; Original user CMP B ; Is it the same JZ PARSE7 ; User ok if already logged on to it LDA MAXUSR ; Get maximum user area for system CMP B JC HELPER ; Illegal user area PUSH H ; Save location in the command line LXI H,13 ; Offset to user area in FCB DAD D ; HL contains address of user area MOV M,B ; Store it POP H ; Get back location in command line PARSE7: INX H ; Point to character after colon PUSH H ; And save the address ; ; Top of stack points to first valid character (after drive/user, if ; specified). Now process file name. ; PARSE8: POP H ; Get pointer to character in command line INX D ; Point to next output character MVI B,8 ; Process up to eight characters of name CALL PRSFN CPI '.' ; Is this the "." separating name and type JNZ PARSE9 ; No extension specified INX H ; Skip over the "." separator ; PARSE9: MVI B,3 ; Process up to three characters of type ; ; Enter with address of input in HL, address of output in DE, ; length of output field in B. ; Return with HL and DE advanced, the last character (should be a ; delimiter) in A, and zero flag set if at end of line. ; Truncate any fields longer than expected. ; PRSFN: CALL QDELIM ; Load, capitalize, and check character JZ PRSFN4 ; Delimiter, go blank pad CPI '*' JNZ PRSFN1 MVI A,'?' ; Expand "*" to "?"s JMP PRSFN2 ; Without advancing input pointer ; PRSFN1: INX H ; PRSFN2: STAX D ; Store the character in the FCB INX D ; Point to next output character DCR B ; One less character to go JNZ PRSFN ; Go process next character till done ; PRSFN3: CALL QDELIM ; Skip to first delimiter JZ PRSFN5 ; At a delimiter INX H ; Else truncate JMP PRSFN3 ; PRSFN4: MVI A,' ' ; Pad with space STAX D ; Store the character in the FCB INX D ; Point to next output character DCR B ; One less character to go JNZ PRSFN4 ; Loop till field is padded ; PRSFN5: MOV A,M ; Return with last character in A ORA A ; Set zero flag if end of command line RET ; Return to caller ; ; A = character pointed to by HL. Zero flag if a delimiter for file names. ; QDELIM: MOV A,M ANI 07FH ; Strip off parity bit CPI 07FH ; Is it a rubout RZ ; A rubout is a delimiter CPI 'a' ; Convert to upper case JC QDELM1 CPI 'z'+1 JNC QDELM1 ANI 05FH QDELM1: CPI '=' RZ CPI ',' RZ CPI '_' RZ CPI '.' RZ CPI ':' RZ CPI ';' RZ CPI '<' RZ CPI '>' RZ CPI '[' RZ CPI ']' RZ CPI ' ' RZ RNC ; Return for valid characters CMP A ; All controls are delimiters RET ; Including EOL ; DSPDEC: MVI D,0 ; Set for leading zero LXI B,-1000 CALL CNVRT ; Convert to decimal ; LXI B,-100 CALL CNVRT ; Convert to decimal ; LXI B,-10 CALL CNVRT ; Convert to decimal ; MOV A,L ; One's digit was left in L JMP DSPDGT ; Display the digit and return to caller ; CNVRT: MVI A,0FFH ; Increments to zero first time used PUSH H ; CNVRT1: POP H ; Get the value in HL PUSH H ; Save as previous value INR A ; Add one for this position DAD B ; Subtract power of ten from binary value XTHL ; Get previous value back JC CNVRT1 ; Do it until we go negative POP B ; Get stack right, discard negative value MVI E,' ' ; Assume the character is a space ORA D ; Check for leading zeros JZ DSPSPC ; Return if digit is zero DSPDGT: MVI D,'0' ; Set D so all zeros will display after this ORA D ; Convert to ASCII MOV E,A DSPSPC: PUSH D PUSH H MVI C,CIO ; Console I/O BDOS function CALL BDOS POP H POP D RET ; ; Display help message and quit ; HELPER: LXI D,HELPMSG JMP EXIT ; ; Error in calculated CRC compared to input CRC value, abort the program ; ERRCRC: LXI D,CRCERR JMP ABEXIT ; FILERR: LXI D,OUTERR ; Open error message ; ; Aborted - display reason. If making output file, close the incomplete ; file to update CP/M's bit map, then erase it. ; ABEXIT: CALL PRTMSG ; Display message pointed to in DE ; ABEXT1: LXI D,ABORT ; Display "Aborting" message CALL PRTMSG ; Display message pointed to by DE ABEXT2: MVI C,CSTAT ; Console status BDOS function CALL BDOS ; Check for operator abort ORA A JZ ABEXT2 ; Nothing from operator, loop MVI C,CONIN ; Read console BDOS function CALL BDOS ; Get character entered CPI 'C'-40H ; Control-C? JNZ EXIT ; No, exit normally JMP 0 ; Exit to warm boot routine ; ; End of program. ; DONE: LXI D,DONEM ; ; Exit with message. ; EXIT: CALL PRTMSG ; Display message pointed to by DE ; ; Exit, via system warm boot. ; LDA OLDUSR ; Get original user area MOV E,A MVI C,CURUSR ; Update current user area CALL BDOS LHLD STACK ; Get original CCP return address SPHL RET ; Go back to CCP, no warm boot needed ; ; Output zero terminated string pointed to by DE. ; PRTMSG: LDAX D ; Get start of message ANI 07FH ; At end? (Zero high bit just in case) RZ ; Return to caller PUSH B PUSH D PUSH H MOV E,A ; Get the character to be displayed MVI C,CIO ; Console I/O BDOS function CALL BDOS POP H POP D POP B INX D ; Point to next character JMP PRTMSG ; Loop till done ; DRVSUP: DW 1111111111111111B ; Vector of available drives ; ABCDEFGHIJKLMNOP MAXUSR: DB 15 MAXDRV: DB 'P'-40 ABORT: DB ' -- Aborting, press any key',CR,LF,0 DONEM: DB CR,LF,'Done',0 OUTMEM: DB 'Out of Memory',0 NOTFND: DB 'File Not Found',0 OUTERR: DB ' Output Error',0 RDERR: DB ' File Read Error',0 CRCERR: DB ' Error in CRC value',0 ; UPDMSG: DB ' Updated',0 REMMSG: DB ' Removed',0 ; HELPMSG:DB 'CKCRC Version ',VER / 10 + '0','.',VER MOD 10 + '0',' -- ' DB MONTH/10+'0',MONTH MOD 10+'0','/' DB DAY/10+'0',DAY MOD 10+'0','/' DB YEAR/10+'0',YEAR MOD 10+'0' DB CR,LF,LF DB 'Usage: CKCRC [du:]ambigfn [/option]',CR,LF,LF DB 'Only one option parameter may be entered:',CR,LF DB ' "F" causes the output to be written to CRCLIST.CRC',CR,LF DB ' "V" causes checksum installation in safe files only',CR,LF DB ' "U" installs unconditionally, may foul the file',CR,LF DB ' "R" removes CRC values installed by the V option',CR,LF DB ' "xxxx" compares the calculated CRC to this input',CR,LF,LF DB 0 ; HEADER: DB 'CKCRC Version ',VER / 10 + '0','.',VER MOD 10 + '0',' -- ' DB MONTH/10+'0',MONTH MOD 10+'0','/' DB DAY/10+'0',DAY MOD 10+'0','/' DB YEAR/10+'0',YEAR MOD 10+'0',' -- ' DRVUSR: DB 'duu:filename.typ ',CR,LF DB ' File Name CHEK CCIT Patch Records',CR,LF,LF,0 ; ; Program storage area, initialized ; CRCFCB: DB 0,'CRCLIST CRC' DB 0 DS 20 ; ; Storage not initialized by assembler ; DIRPTR: DS 2 ; Pointer to current position in table OPTION: DS 2 ; Saved input option DIRCNT: DS 2 ; Count of selected directory entries OLDUSR: DS 1 OLDDRV: DS 1 BUFAD: DS 2 ; Read buffer address REM2BAK:DS 2 REM1BAK:DS 2 ; History, for file patching CCITVL: DS 2 ; CRC remainder storage for "CCIT" calculation CHEKVL: DS 2 ; CRC remainder storage for "CHEK" calculation ICRC: DS 2 ; Input CRC value ; DS 64 ; Local stack ORG ($+255)/256*256 ; Get on page boundary ; STACK EQU $-2 ; Area for CCP stack pointer ; CCITTB: DS 512 ; The 2 tables for CRC lookup CHEKTB: DS 512 ; The 2 tables for CRC lookup BUFFER EQU $ END