; ; CRCBUILD.ASM version 1.0 ; by John L. Raff WB2MDG/KAPP8107 ; (built 830408FR) ; ;---> NOTE: MUST BE ASSEMBLED BY MAC <---- ; ;CRCBUILD is a program to build a CP/M file in SIG/M ;-CATALOG format 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 ; ;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 READCONSOLE EQU 10 ;Read console buffer 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 STRLMT EQU 80 ;STRING LENGTH LIMIT ; ;Program starts here ; ORG BASE+100H ; MACLIB SEQIO ;DEFINE MACRO LIBRARY USED CRCBUILD: JMP BEGIN ;Jump around identification & alternate file name DB 'CRCBUILD.COM 1.0 830408fr',0 ; BEGIN: XRA A STA NAMQUAN ;Zero the quantity of files read LXI H,0 ;GET STACK... DAD SP ;POINTER SO WE CAN.. SHLD STACK ;SAVE IT LXI SP,STACK ;INITIALIZE LOCAL STACK BGN2: CALL CRLF ;TURN UP A NEW LINE CALL ILPRT ;Print the heading DB 'CRCBUILD Ver 1.0 830224TH',CR,LF DB 'CTL-S pauses, CTL-C aborts',CR,LF,0 LDA FCB+1 CPI ' ' ;SEE IF NAME THERE JNZ BEGIN2 ;YES, CONTINUE LXI D,FCB+2 ;Put total directory pattern LXI H,FCB+1 ;in FCB MVI M,'?' ;All "?" LXI B,10 ;4 characters CALL MOVER ;MOVE IT JMP BEGIN2 ;Go get um ; THRNUM?: ;Check for three ascii numbers CALL TWONUM? RNZ ;Return if not numbers CALL NUM? RZ ;Return if a number CPI '.' ;Check for a period RNZ ;Return if not a period DCX H ;Otherwise back up one character DCR B ;And one count CMP A ;Set zero flag RET ; TWONUM?: CALL NUM? RNZ NUM?: MOV A,M ;Get the character INX H ;For next character INR B ;Increment for next count CPI '0' ;Is it less than 0 JC NUMNOT ;Branch if it is CPI '9'+1 ;Is it greater than 9? JNC NUMNOT CMP A ;Set zero flag RET NUMNOT: CPI '0' ;Should not be equal, reset zero flag RET ; BEG2NG: CALL ILPRT DB CR,LF,'Incorrect characters',CR,LF,0 ; BEGIN2: CALL ILPRT DB 'Enter -CATALOG.xxx number (three digits) -',0 LXI D,STR4 ;Point to string 4 MVI A,5 ;Indicate size of buffer STAX D MVI C,READCONSOLE ;Read console buffer CALL BDOS ;Go get the string LXI H,STR4+1 ;Point to quantity characters entered MOV C,M ;Get the count MVI A,3 ;Check for three characters CMP C ;In buffer JNZ BEG2NG ;Branch if not 3 characters MVI B,0 ;Zero counter INX H ;Point to first character BEG2A: CALL NUM? ;Is it a decimal number? JNZ BEG2NG ;Branch if not a decimal number MOV A,B ;Check quantity of characters CPI 3 JNZ BEG2A ;Branch if not end of characters yet MVI M,0 ;Ensure termination of string LXI H,STR4+2 ;Copy number into file string LXI D,FNAME1 LXI B,3 ;Count CALL MOVER MVI A,'F' ;Indicate write file open STA FFLAG ; ;'Declare' FCB for output file ;(temporarily named -CATALOG.$$$) ; FILE OUTFILE,CATALOG,,-CATALOG,$$$,BSIZE ; AGAIN: LXI SP,STACK ;RE-INIT STACK POINTER MVI A,0 ;Indicate in building phase STA WHICH ;Save flag CALL MFNAME ;SEARCH FOR NAMES JNC NAMTST ;ANOTHER FOUND, PRINT NAME AGN2: LDA MFFLG2 ;NOTHING FOUND, CHECK... ORA A ;... NAME FOUND FLAG JZ DONE ;AT LEAST ONE WAS FOUND CALL ABEXIT ;PRINT MSG, THEN EXIT DB '++FILE NOT FOUND++$' ; DONE: ;Close -CATALOG.$$$ LXI H,FNAME1 LXI D,DONEX LXI B,3 CALL MOVER CALL ILPRT DB CR,LF,'SIG-M Library -CATALOG Volume Number-' DONEX: DB 'NNN, ',0 DONEY: LDA NAMQUAN CALL SHWDC CALL ILPRT DB ' Files cataloged.',CR,LF,0 ; FINIS CATALOG ; LXI H,STR4+2 ;Point to beginning of string LXI D,FCBFINAL+9 ;Location of Name TYPE LXI B,3 ;Quantity to move CALL MOVER ; ;Build FCB for final name of -CATALOG.nnn ; FILE SETFILE, FINAL,,-CATALOG,NNN ; ; Erase any existing old file ; ERASE FINAL ; ; Rename -CATALOG.$$$ to -CATALOG.NNN RENAME FINAL,CATALOG ; ;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 PUSH H ;May need pointer for BAD check CALL TSTBAD ;CHECK FOR .$$$ FILES JZ AGAIN ;IF ZERO FLAG, IGNORE THEM POP H ;Restore pointer CALL BADTYP ;Check for .BAD files JZ AGAIN ;Branch if it is ; NAMTS2: CALL ILPRT ;PRINT: DB CR,LF FNAME1: DB 'NNN.',0 ; LDA NAMQUAN ;Get quantity of name INR A ;Increment count STA NAMQUAN CALL SHWDC ;Show the count ; ;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 DB ' ' FNAME: DB 'XXXXXXXX.XXX ',0 FNAME2: CALL OPN ;Open file and build CRC JZ AGAIN ;Branch if bad open or read JMP ABEXT2 ;Otherwise get out ; ;Open the file ; OPN: LXI D,FCB MVI C,OPEN CALL BDOS INR A JNZ RDINIT CALL ILPRT DB '++Open Failed++',0 JMP BADRET ;Indicate Bad open ; ;Initialize CRC to zero and set Bufad to cause initial read ; RDINIT: LXI H,0 SHLD REM ;Init remainder to zero SHLD QUANEK ;Quantity of eighth k LXI H,BASE+100H SHLD BUFAD ;Init buffer address ; ;This is the read loop ; READIT: LHLD BUFAD MOV A,H ;Time to read? CPI BASE SHR 8 ;End of buffer? JZ NORD MVI C,CSTAT CALL BDOS ;Chck for operator abort ORA A JZ READ2 ;Branch if nothing from operator MVI C,RDCON CALL BDOS ;Get character from operator CPI 'C'-40h ;Control C? JZ ABEXT2 ;Branch if 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 LHLD QUANEK ;Get quantity of eighth K INX H ;Add 1 SHLD QUANEK ;Put back LXI H,BASE+80H ;Bufer location ; NORD: MOV A,M ;Get file character STA MESS ;Save for DIVP INX H SHLD BUFAD ;Update buffer address 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 LHLD QUANEK ;Get quantity of eighth K LXI B,7 DAD B ;Round up to next K DAD H ;Turn into full K DAD H ;In "H" register DAD H DAD H DAD H MOV A,H CALL SHWDEC ;Show Number of K CALL ILPRT DB 'K ',0 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 GOODRET: XRA A RET ;Return to caller with zero flag set ; FILERR: CALL ILPRT ;Print the message and return DB TAB,TAB,'++FILE READ ERROR++',CR,LF,0 BADRET: ;General bad return location XRA A ;Set zero flag INR A ;reset zero flag RET ;Return to caller ; ;--------------------------------------------- ;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 chracter 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 CATALOG ;Send it to the file RET ;to caller ; ;Multi-file access subroutine. Allows processing ;of multiple files 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 STA MFFLG2 ;FLAG INDICATING FOUND AT LEAST ONE ;<> 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 BADTYP: ;Check for BAD file type MOV A,M ANI 7FH CPI 'B' RNZ INX H MOV A,M ANI 7FH CPI 'A' RNZ INX H MOV A,M ANI 7FH CPI 'D' RET ;Return with zero flag set or not ; ;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 CATALOG ;CLOSE INCOMPLETE FILE ERASE CATALOG ;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 ; SHWDEC: ;3 Digits, up to 255, with leading 0 suppress MVI B,' ' ;Set for space MVI C,0 SHWDE: CPI 100 ;Check if greater than 100 JC NOHUNS INR C SUI 100 JMP SHWDE NOHUNS: PUSH PSW MOV A,C ORA A JZ NOHN MVI B,'0' ;Set "0" for next 0 NOHN: ADD B ;Add space or "0" CALL TYPE POP PSW MVI C,0 CKTENS: CPI 10 JC NO10S INR C SUI 10 JMP CKTENS NO10S: PUSH PSW MOV A,C ORA A ;Check if any 10s JZ NO10X MVI B,'0' NO10X: ADD B ;Add space or "0" CALL TYPE POP PSW ADI '0' JMP TYPE ; SHWDC: ;2 DIGITS ONLY, 99 only, No 0 supress MVI C,'0' ;Set up for '0' digit SHWDC1: CPI 10 ;Check if greater than 10 JC NOTENS ;Branch if no tens digits INR C ;Increment tens digit counter SUI 10 ;Subtract 10 from number JMP SHWDC1 ;Branch back to test again NOTENS: PUSH PSW ;Save the units digit quantity MOV A,C ;Get the tens digit quantity CALL TYPE POP PSW ;Restore the units digit quantity ADI '0' ;Add ascii digits numeral offset JMP TYPE ;Display units and return to caller ; ;Program storage area ; NAMPTR: DS 2 ;Storage for name start in catalog search MTCH DB 0 ;CRC Match counter NOMTCH DB 0 ;CRC Don't match counter BADPAR DB 0 ;Bad parse counter MISFIL DB 0 ;Missing files counter FFLAG: DB 0 ;FILE WRITE REQUEST FLAG FLAGAX: DB 0 ;All user check flag REM: DW 0 ;CRC REMAINDER STORAGE QUANEK: DW 0 ;Quantity of eighth K MSPREM DS 1 ;Storage for CRC MSP from file LSPREM DS 1 ;LSP from file MESS: DB 0 ;CRC MESSAGE CHAR GOES HERE MFFLG1: DB 1 ;1ST TIME SWITCH MFFLG2: DB 1 ;FLAG INDICATING ONE NAME FOUND OR NOT XFRST: DB 1 ;1st time check in read RDCK: DB 0 ;Open file CRCKLIST or -CATALOG VERNUM: DB 0 ;VERSION NUMBER OF CP/M IN USE ENTUSR: DB 0 ;ENTRY USER NUMBER NOWUSR: DB 0 ;PRESENT USER'S NUMBER NAMQUAN: DB 0 ;Quantity of files read FRSNAM: DB 1 ;FIRST NAME IN USER FLAG MFREQ: DS 12 ;REQUESTED NAME MFCUR: DS 12 ;CURRENT NAME WHICH DS 1 ;Flag storage for phase BUFAD: DS 2 ;READ BUFFER ADDRESS STRCNT DS 2 ;STRING COUNT STORAGE STRING DS 11 ;STRING STORAGE STR1: DS 12 ;Name storage STR2: DS 8 ;Characters after name STR3: DS 5 ;Ascii CRC characters STR4: DS 80 ;Buffer DS 60 ;STACK AREA STACK: EQU $ OLDSTK: DS 2 ;OLD STACK POINTER SAVED HERE ; ;Define location of file write buffer BUFFERS:EQU $ ; END CRCK