;
;		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 <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
;<<if first time>>
	LDA	MFFLG1
	ORA	A
	JZ	MFN01
;<<save the requested name>>
;save orig request
	LXI	H,FCB
	LXI	D,MFREQ
	LXI	B,12
	CALL	MOVER
	LDA	FCB
	STA	MFCUR	;Save disk in curr FCB
;<<srchf requested namd>>
	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,BASE SHR 8
	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
	STA	MFFLG2	;FLAG INDICATING FOUND AT LEAST ONE
;<<return>>
	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
