	title	'CCT.ASM	05/29/82'

;Used to combine multiple small files into one large file.

;command format:

;	CCT output [string] ]string[ = input1 , input2....

;;	blanks may be used before and after (but not
;;	within) the filenames for readability. The
;;	commas are also optional, except there must be
;;	something separating the filenames.

;;The input and output files may have the standard form
;;		drive:filename.ext
;;	where the drive may be A: to N: and is optional,
;;	and ext is the optional file extension.

;;If the output filename is * (not *.*), the program will
;;	create the file $$$.SUB on drive A in correct SUBMIT
;;	format. That is, CCT can be used as a replacement
;;	for SUBMIT, except for parameter substitution. The
;;	program will automatically stop at 128 lines of input,
;;	since that is what most CP/M systems allow.

;;The [string] is the optional prefix string, which will be
;;	put at the beginning of every line of the output file.
;;	The [ and ] are required punctuation, and the ] cannot
;;	be within the string, tho almost anything else can be.
;;	The surrounding brackets will not be included in the
;;	string. When present, the prefix string will always be
;;	the first characters of the output file, unless the
;;	output is completely empty.

;;The ]string[ is the optional suffix string, which will be
;;	put at the end of every line on the output file. The
;;	] and [ are required punctuation, and the [ cannot be
;;	within the string. The suffix string may be specified
;;	in the command line in front of the prefix string, as
;;	long as both are after the output filename and before
;;	the equal sign (=)

;;An input filename %N will give a CRLF (hex 0D 0A)
;;An input filename %F will give a Formfeed (hex 0C)
;;An input filename %T will give a Tab (^I, hex 09)

;;An input file name of n (a number from 1 to 65535) will cause
;;	an automatic CRLF after "n" characters on one line. The
;;	CR and LF are not counted in the number of characters
;;	per line, and this option will hold for all successive
;;	files until changed. Any characters being inserted
;;	by prefix or suffix strings are not counted. To turn
;;	off this option after using it on an earlier input file,
;;	use option 65535

;;An input file name of +n (a number from 1 to 65565) will cause
;;	the program to skip the first "n" lines of each following
;;	input file, until changed. To turn this option back off,
;;	specify option +0

;;An input file name of -n (a number from 1 to 65535) will cause
;;	the program to stop after the "n"th line is read from
;;	each file, until changed. To turn this option back off,
;;	specify an option of -65535

;;The "n" "+n" and "-n" options are absolutely independent of each
;;	other, and may appear many times in the command, each or
;;	together.

;;An * as an input  filename, such as A=* or A=B,*,C will
;;	switch to the console for input. All the standard
;;	line-editing functions are available, such as
;;	backspacing. Up to 254 characters may be input,
;;	and the input terminates with a null line or a
;;	EOF (Control-Z, hex 1A)

;;If the output file is omitted (no =), the console will
;;	be the destination

;; For example :

;;	CCT -60 CCT.ASM		Types the first 60 lines of CCT.ASM

;;	CCT A = B FOO.ASM	Concatenates B and FOO.ASM giving A

;;	CCT A,C:B,C		Concatenates A, B (from the C drive),
;;				 and C with output to the console

;;	CCT A=*			Create file A from your console input

;;	CCT A = *, +5 X.ASM	Accepts whatever you type in, followed
;;				by all but the first 5 lines of X.ASM

;;			Note: CCT works under XSUB and will accept
;;			the input from the "console" that XSUB
;;			fakes out. Use the ^z to terminate input.

;;	CCT NEW.ASM=OLD.ASM	Copies OLD.ASM to NEW.ASM
;;				NOTE:  only for ASCII files

;;	CCT X.ASM = Y %N Z	Concatenates Y, adds a CRLF, then Z
;;				 output going to X.ASM

;;	CCT %N %N %N %N		Sends four CRLFs to the console

;;	CCT COMMENT.ASM [; ] = COMMENT.DOC  Puts a ; and a blank at
;;				the beginning of every line in the
;;				.DOC file and puts the results in
;;				the COMMENT.ASM file

;;	CCT * = *		Accepts a series of commands from
;;				the console, and creates a submit
;;				file that will automatically start
;;				executing when CCT is done.

;;	CCT * [PIP C:=B:] = NAMES.SUB  Puts "PIP C:=B:" in front of
;;				every line from NAMES.SUB and will
;;				submit the results to CP/M. This may
;;				be useful if NAMES.SUB is a list of
;;				files on drive B:

;;			Note: Ward Christensen's FMAP utility
;;			can create a file containing a list
;;			of filenames. This is very handy.

;;	CCT -5 A B		Will type the first 5 lines of file A
;;				followed by the first 5 lines of file B.

;;	CCT B = +6 A		Will copy file A to file B, skipping
;;				the first 6 lines of A.

;;	CCT -7 A +5 B		Will copy the first 7 lines of file A
;;				followed by lines 6 and 7 of file B.

;;	CCT 80 C		Will type file C, putting an automatic
;;				CR-LF every 80 bytes on every line that
;;				is over 80 bytes long. Lines under 80
;;				bytes long will be unchanged.

;;	CCT D = -7 E -9 F	Will type the first 7 lines of E followed
;;				by the first 9 lines of F.


;;Bugs or restrictions

;;	If BLKSIZE is 16, as supplied, the program will require a
;;		a minimum 24K CP/M system, and will allow a maximum
;;		of 128 (16K/128) lines of Submit output. The BLKSIZE
;;		does not affect other types of files except for per-
;;		formance.

;;	Ambiguous filenames are not allowed (no ? in filename)
;;		The files might not be concatenated in the order
;;		you wanted anyway.

;;	As the exception to the above statement, an output filename
;;		beginning with ? will be treated as *, because it was
;;		not deemed worth the effort to put in any extra code.

;;	If the output file exists, it will be erased and replaced,
;;		so CCT A = A,B  will not do what you want

;;	A filename with nothing but an extension ( .ASM) is con-
;;		sidered an error

;;	It is possible to use a file called %N, %F, %T, +n, -n, or n
;;		- just put a period after it, e.g. %N. or +5. or 988.

;;	Multiple commas get swallowed - A,,,B is wierd but OK.
;;		This might be useful in a submit file if a parameter
;;		is missing, such as   CCT $1=$2,$3,$4

;;	The program will stop when it hits a Control-Z (hex 1A), so
;;		using it to copy .COM files is not recommended.

;;	There will be only one Control-Z at the end, but there will
;;		always be one there, even if the input files do not
;;		have one, & that will be the only one in the file.

;;	When creating a file from console input, it is possible to
;;		choose whether there will be a CR-LF (hex 0D 0A) at
;;		the end. If you want it, stop with a null line. If
;;		you don't, stop by typing a Control-Z before hitting
;;		Return on the last line.

;;	Entering an ASCII NUL (hex 00, Control-@) can cause wierd
;;		problems anywhere in CP/M, and should be avoided.

;;	Entering CCT * will echo console input back to the console,
;;		but most systems will not see anything, because the
;;		output line will exactly overlay the input line.
;;		This command might be useful for Microshell, tho.

;;	The brackets [ and ] must be paired right, and may not appear
;;		in the output or any input filename.

;;	The %N is not changed into a CRLF within the prefix and suffix
;;		strings. In fact, I haven't found a way of getting
;;		a CRLF into the strings yet.

;;	If you want prefix or suffix strings on console output, you
;;		must put an = after the strings. Under any other con-
;;		dition, you cannot use an = if you want console out.
;;		E.g. CCT [<<<]=A and CCT A are legal, CCT =A is not

;;	It is adviseable to put spaces in between the output filename
;;		and the [ or ] if a prefix or suffix string is desired.
;;		In some versions of CP/M, if you are putting "funny"
;;		characters in the strings, it may be necessary to put a 
;;		. (period) before the strings start (and after the output
;;		filename) e.g.   CCT A . [^I.A::^I::??] = B

;;	If * is the output filename, the file $$$.SUB will be erased
;;		on the A: drive and a new file will be created. This
;;		may be desireable or not, but it will happen. It is
;;		possible to use CCT to chain to another file at the
;;		end of a submit file if the CCT *=filename is the
;;		last command in the first Submit file. CCT will acc-
;;		null lines from the input file, unlike Submit.

;;	The algorithm used for the prefix and suffix strings will in-
;;		tentionally not put the strings on the last line of
;;		the output file, if that line is empty. That is, if
;;		the end of the last file is CR LF EOF, the output file
;;		will also be that way.

;;	The algorithm used for line determination also treats the CR and
;;		LF as equivalent, and basically nul - that is, it doesn't
;;		care whether there is a CR, a CR-LF, an LF, or maybe CR-
;;		LF-CR-LF-LF, they're all the same. This most seriously
;;		affects the +n and -n options if there are nul lines.

;;	If both are used, the + number must be less than the - number, or
;;		there will be no output from that file. The file(s) will
;;		be opened, though, so that might be a way of testing for
;;		files. For example,  CCT -0 +1 A B  just sees if files
;;		A and B are present.

;;	The character-per-line counter (used by the "n" option) does not
;;		treat tabs specially - that is, tabs are not expanded when
;;		computing the number of characters on the line.

;adapted from	'COMBINE.ASM'
;12/10/78 BY Ward Christensen

;Modified by Bob Van Valzah & Steve Ness on 1/9/79 to allow
;source files on any drive and remove comments preceded by blanks.

;Modified by Steve Ness on 12/20/79 to allow filetypes on source files.

;; 05/16/82 by Chuck Weingart, take out comment killer (above),
;;	add the %n inserter, rename to CCT to make a UNIX-like
;;	filter (the name CAT was already taken), add more error
;;	checks, allow console input, add prefix and suffix strings,
;;	and ignore blanks on command line.

;; 05/29/82 by Chuck Weingart, Add the %F and %T inserter from an
;;	idea of Jim Mills. Add GETNUM and the line and character
;;	counters etc. code to restrict the output. Includes code
;;	from /.ASM by John M. Kodis, CP/M U.G. contribution Novem-
;;	ber 1980, on CPMUG 78. (Based on "/.COM", CPMUG 40.1,
;;	originally written by Ward Christensen)

TEST	EQU	0
;;		The BLKSIZE parameter determines the maximum
;;		number of lines allowed in a $$$.SUB file, and
;;		is used otherwise for efficiency.
K	EQU	1024	;1K
BLKSIZE	EQU	16*K	;OUTPUT FILE BLOCKSIZE
;;
tpa	equ	100h	;transient program area
;;
EOF	EQU	1AH	;;Control-Z
CR	EQU	0DH	;;Carriage Return
LF	EQU	0AH	;;Line Feed
;
;(FROM EQU7.LIB...)
MF	SET	0	;SHOW MOVE NOT REQUESTED
CF	SET	0	;SHOW COMP NOT REQUESTED

;
;DEFINE SOME MACROS TO MAKE THINGS EASIER
;
;DEFINE DATA MOVE MACRO
;
MOVE	MACRO	?F,?T,?L,?I
	IF	NOT NUL ?F
	LXI	H,?F
	ENDIF
	IF	NOT NUL ?T
	LXI	D,?T
	ENDIF
	IF	NOT NUL ?L
	LXI	B,?L
	ENDIF
	IF	NOT NUL ?I
	LOCAL	?B,?Z
	CALL	?Z
?B	DB	?I
?Z	POP	H	;GET TO
	LXI	B,?Z-?B
	ENDIF
	CALL	MOVER
MF	SET	-1	;;SHOW EXPANSION
	ENDM
;COMPARE MACRO
COMP	MACRO	?F,?T,?L,?I
	IF	NOT NUL ?F
	LXI	H,?F
	ENDIF
	IF	NOT NUL ?T
	LXI	D,?T
	ENDIF
	IF	NOT NUL ?L
	LXI	B,?L
	ENDIF
	IF	NOT NUL ?I
	LOCAL	?B,?Z
	CALL	?Z
?B	DB	?I
?Z	POP	D	;GET TO
	LXI	B,?Z-?B
	ENDIF
	CALL	COMPR
CF	SET	-1	;;SHOW EXPANSION
	ENDM
;
;DEFINE CP/M MACRO - CPM FNC,PARM
;
CPM	MACRO	?F,?P
	PUSH	B
	PUSH	D
	PUSH	H
	IF	NOT NUL ?F
	MVI	C,?F
	ENDIF
	IF	NOT NUL ?P
	LXI	D,?P
	ENDIF
	CALL	BDOS
	POP	H
	POP	D
	POP	B
	ENDM
;
	ORG	tpa
;INIT LOCAL STACK
;
	LXI	H,0
	DAD	SP
	SHLD	STACK
	LXI	SP,STACK
	lxi	h,cibuf
	mov	a,l	;;from /.asm
	add	m
	inr	a
	mov	l,a
	mvi	m,0	;zero the byte after the end of the command line.
;
;
;START OF PROGRAM EXECUTION
;
;MOVE THE COMMAND BUFFER
	MOVE	cibuf,HOLD,128 ;FROM,TO,LENGTH
	COMP	FCB,,,<0,'?'>
	JNZ	POSITION ;;check for *, or a close resemblance
	COMP	FCB+9,,,'   '
	JNZ	POSITION
	MVI	A,0C3H	;;now flag the *
	STA	STRFLAG
POSITION	; TO FIRST NAME TO COPY (FOLLOWS =)
	LXI	H,HOLD
	SHLD	NAMEADD	;;save in case of no file
SKIPEQ	INX	H
	MOV	A,M
	ORA	A	;;check if end of line
	JZ	CONSOL
	CPI	'['	;;look for boxes
	JZ	SKIPBRK
	CPI	']'
	JZ	S2IPBRK
	CPI	'='
	JNZ	SKIPEQ
	SHLD	NAMEADD	;SAVE POINTER
	LDA	STRFLAG
	ORA	A	;;see if got the *
	JZ	QMTEST
	MOVE	XFCB,FCB,24
	lxi	h,subbuf+1;hl is the destination pointer.
	SHLD	SUBOUT
	LXI	H,BLKSIZE/128	;;maximum number of lines
	SHLD	MINSNUM
	JMP	EQDONE
QMTEST
	LXI	H,FCB+1	;;now set up to scan FCB
	MOV	A,M
	MVI	B,11
QMLOOP
	CPI	'='	;;see if got equal in filename
	JZ	EQLOOP
	CPI	'>'
	JZ	EQLOOP
	CPI	'?'	;;see if ambiguous
	JZ	QMFILE
	CPI	'['	;;kill the boxes, too
	JZ	EQLOOP
	CPI	']'
	JZ	EQLOOP
	INX	H	;;check next character
	MOV	A,M
	DCR	B	;;see if done
	JNZ	QMLOOP
;ERASE THE OUTPUT FILE
EQDONE
	LDA	FCB+1	;;see if error
	CPI	' '
	JZ	NOFILE2
	IF	NOT TEST
	CPM	delete,FCB
	CPM	makef,FCB
	INR	A
	JZ	NOROOM
	ENDIF
	CALL	NEXTFL	;PRIME THINGS
	JC	NOFILE
LOOP	CALL	WRBYTE
	CALL	RDBYTE
	JNC	LOOP
;DONE -
NOFILE0
	MVI	A,EOF	;;gotta end the file
	CALL	WRBYTE
	LDA	STRFLAG	;;buffers for $$$.sub already flushed
	ORA	A
	JNZ	DONEIT
	LHLD	WRCOUNT
	LXI	D,128
	DAD	D	;FORCE LAST SECTOR
	SHLD	WRCOUNT	;..WRITE
	CALL	WRBUFS	;WRITE THE BUFFER
DONEIT
	CPM	closef,FCB
	INR	A
	JNZ	EXIT
	CALL	ERXIT
	DB	'++CLOSE ERROR++$'
;
EQLOOP
	MVI	M,' '	;;gonna blank out rest of FCB
	INX	H
	DCR	B	;;see if done
	JNZ	EQLOOP
	JMP	EQDONE
;;
SKIPBRK
	SHLD	BRAKAD	;;save address
SKIPLUP
	INX	H	;;bump to next
	MOV	A,M
	ORA	A	;;check if end of line
	JZ	BADNAME
	SBI	']'	;;stop at bracket
	JNZ	SKIPLUP
	MOV	M,A	;;note for later use
	JMP	SKIPEQ
;;
S2IPBRK
	SHLD	B2AKAD	;;save address
S2IPLUP
	INX	H	;;bump to next
	MOV	A,M
	ORA	A	;;check if end of line
	JZ	BADNAME
	SBI	'['	;;stop at bracket
	JNZ	S2IPLUP
	MOV	M,A	;;note for later use
	JMP	SKIPEQ
;;
QMFILE
	CALL	ERXIT	;;
	DB	'++AMBIGUOUS FILE++$'
;
NOFILE
	LDA	PCTFLAG	;;see if just percents
	ORA	A
	JZ	NOFILE1
	LDA	CNFLAG	;;see if submit file out
	ORA	A
	JZ	NOFILE0
	JMP	EXIT	;;if console out, nothing to close
NOFILE1
	CALL	ERXIT	;;nothing at all
	DB	'++MISSING FILE++$'
;;
;;
NOFILE2
	LHLD	BRAKAD	;;see if prefix or suffix strings
	MOV	A,H
	ORA	L
	LHLD	B2AKAD
	ORA	H
	ORA	L
	JZ	NOFILE1	;; = is OK if had strings
;;
CONSOL
	MVI	A,0C3H
	STA	CNFLAG
	CALL	NEXTFL	;;set up loop
	JC	NOFILE
LOOPC	CALL	WRBYTE	;;now copy to console
	CALL	RDBYTE
	JNC	LOOPC	;;see if all done
	JMP	EXIT
;
;WRITE BYTE TO OUTPUT FILE
;
WRBYTE
	MOV	B,A	;;save the character
	CPI	EOF	;;see if end
	JZ	WRCRLF
	CPI	CR	;;look for CR or LF
	JZ	WRCRLF
	CPI	LF
	JZ	WRCRLF
;;	Just got a character not CR, LF, or EOF
	LHLD	WIDTH	;;bump up char. #
	INX	H
	SHLD	WIDTH
	XCHG
	LHLD	WIDNUM	;;load max width
	MOV	A,L
	SUB	E
	MOV	A,H	;;compare width to max
	SBB	D
	JNC	WRBY2
	PUSH	B	;;save the character
	MVI	A,CR
	CALL	WRBYTE	;;recursive call to end line
	MVI	B,LF
	CALL	WRBYT
	POP	B	;;now r	la;OZ0'mYacter on new line
WRBY2
	LDA	CRFLAG	;;now check if just had CR or LF
	ORA	A
	JZ	WRBYT
	LHLD	LINE	;;bump up line counter
	INX	H
	SHLD	LINE
	XRA	A	;;now turn off CRLF
	STA	CRFLAG
	LHLD	BRAKAD	;;at beginning of line, put out prefix
	MOV	A,L
	ORA	H	;;see if prefix
	JZ	WRBYT
PREFLUP
	INX	H
	MOV	A,M	;;get a character
	ORA	A
	JZ	WRBYT	;;jump if end of prefix string
	PUSH	H
	PUSH	B
	MOV	B,A
	CALL	WRBYT	;;pump out character
	POP	B
	POP	H
	JMP	PREFLUP
;;
;;	Just found a CR or LF
WRCRLF
	LXI	H,0	;;zero out line width
	SHLD	WIDTH
	LDA	CRFLAG	;;now check if just had CR or LF
	ORA	A
	JNZ	WRBYT
	CMA		;;now indicate CRLF
	STA	CRFLAG
	LHLD	B2AKAD	;;at end of line, put out suffix
	MOV	A,L
	ORA	H	;;see if suffix
	JZ	WRBYT
SUFFLUP
	INX	H
	MOV	A,M	;;get a character
	ORA	A
	JZ	WRBYT	;;jump if end of prefix string
	PUSH	H
	PUSH	B
	MOV	B,A
	CALL	WRBYT	;;pump out character
	POP	B
	POP	H
	JMP	SUFFLUP
;;
;;	Now put out character wherever it goes
WRBYT
	LHLD	LINE	;;get line #
	MOV	A,L
	ORA	H	;;if very first line, accept CR and LF
	JZ	WRBY3
	XCHG
	LHLD	PLUSNUM	;;get starting #
	MOV	A,L
	SUB	E
	MOV	A,H	;;compare line # vs start
	SBB	D
	RNC
	LHLD	MINSNUM	;;get ending line #
	DCX	D
	MOV	A,E
	SUB	L
	MOV	A,D	;;compare line # vs end
	SBB	H
	JNC	FAKEOF	;;tell RDBYTE to get next file
WRBY3
	LDA	CNFLAG
	ORA	A	;;if console output, go do it
	MOV	A,B
	JNZ	TYPE
	LDA	STRFLAG
	ORA	A	;;if submit output, go do it
	JNZ	SUBCMD
	LHLD	WRBUFAD	;;disk i/o is buffered
	MOV	M,B
	INX	H	;;bump up to next character
	SHLD	WRBUFAD
;BUMP COUNT
	LHLD	WRCOUNT
	INX	H
	SHLD	WRCOUNT
;SEE IF TIME TO WRITE BUFFER
	DAD	H	;H=# SECTORS
	MOV	A,H
	CPI	BLKSIZE/128
	RNZ
;TIME TO WRITE THE OUTPUT BUFFER
;
WRBUFS	LXI	H,WRBUFF
	SHLD	WRBUFAD
	XCHG
	LHLD	WRCOUNT
	DAD	H	;H=# SECTORS
	MOV	B,H	;SAVE SECTOR COUNT
	MOV	A,B
	ORA	A	;0 SECTORS? (POSS AT EOF)
	JNZ	WRBFLP
	INR	B	;FUDGE IN PARTIAL SECTOR
WRBFLP	CPM	dmaf
	CPM	writef,FCB
	ORA	A
	JNZ	WRERR
	LXI	H,128
	DAD	D
	XCHG
	DCR	B
	JNZ	WRBFLP
	LXI	H,0
	SHLD	WRCOUNT
	RET
;
;	all this from /.asm , with thanks
;
SUBCMD
	MOV	A,B
	CPI	LF	;;skip linefeeds
	RZ
	LHLD	SUBOUT
	CPI	CR	;;see if end of line
	jz	cmdend
	CPI	EOF	;;end of file ?
	jz	cmdend
	mov	m,a	;if neither, xfer it to the output buffer,
	inx	h	;bump pointer
	SHLD	SUBOUT
	RET		;and try again.
;
cmdend:	mvi	m,0	;zero the byte after the end of the line.
	mov	a,l
	dcr	a
	ani	7fh	;accumulator has character count.
	mov	c,a	;save it.
	mov	a,l
	ani	80h
	mov	l,a	;hl points to the character count byte.
	mov	m,c
;
	MOV	A,B
	CPI	EOF	;if we've reached the end of the input line...
	jz	fileit	;let's "file it".
	lxi	b,129
	dad	b
	SHLD	SUBOUT
	RET		;back for the next command.
;
fileit:		
;
wrloop:	push	h	;save address of last zone
	xchg
	mvi	c,dmaf
	call	bdos
;
	lxi	d,fcb
	mvi	c,writef
	call	bdos	;write the new commands, one command per
			;disk sector, writing the last command first.
	ORA	A
	JNZ	WRERR
;
	pop	h	;get current dma address
	lxi	d,-128
	dad	d
	lxi	d,subbuf
	mov	a,l
	sub	e
	mov	a,h
	sbb	d
	jnc	wrloop
	RET
;
WRERR	CALL	ERXIT
	DB	'++WRITE ERROR++$'
;;
FAKEOF
	LXI	H,RDBUFF ;;create EOF so RDBYTE will
	SHLD	RDBUFAD	 ;;get next file
	MVI	A,EOF
	MOV	M,A
	STA	RDCOUNT
	RET
;
;READ BYTE FROM INPUT FILE
;
RDBYTE	LHLD	RDBUFAD
	LDA	RDCOUNT	;GET CHAR COUNT
	ORA	A	;TIME TO READ?
	JP	NOREAD
;HAVE TO READ
	CPM	dmaf,RDBUFF
	CPM	readf,RDFCB
	ORA	A
	JNZ	NEXTFL	;;if disk EOF, go get next file
	CPM	dmaf,cibuf
	LXI	H,RDBUFF
	MVI	A,0
NOREAD	INR	A
	STA	RDCOUNT	;SAVE CHAR COUNT
	MOV	A,M	;GET CHAR
	INX	H
	SHLD	RDBUFAD
	CPI	EOF	;EOF?
	JZ	NEXTFL
	ORA	A	;CARRY OFF SHOWN NOT EOF
	RET
;
;GOT EOF - GET NEXT FILE
NEXTFL
	LXI	H,0
	SHLD	WIDTH	;;zero out counters for new file
	SHLD	LINE
	LHLD	NAMEADD	;GET NAME POINTER
	MOV	A,M	;GET DELIMITER
	ORA	A
	STC
	RZ		;RET IF ALL DONE
MOVEX
	INX	H	;SKIP DELIMITER
	MOV	A,M
	CPI	' '	;;gonna skip by blanks
	JZ	MOVEX
	CPI	','	;;   and commas
	JZ	MOVEX
	ORA	A
	STC		;;return if nothing but blanks
	RZ
	LXI	D,RDFCB ;POINT TO NAME
	xra	a	;prepare to use default drive
	stax	d
	STA	PRFLAG	;;zero out period flag
	inx	d
	PUSH	D
;BLANK THE FCB
	MVI	A,' '
	MVI	B,11
BLANK	STAX	D
	INX	D
	DCR	B
	JNZ	BLANK
	POP	D
;
	inx	h	;look ahead for colon, indicating drive
	mov	a,m
	dcx	h	;backup j.i.c. not there
	cpi	':'	;was a drive specified?
	jnz	moven	;nope - just scan off file name
	mov	a,m	;yup - insert drive name into fcb
	sui	'A'-1
	JC	MOVEN	;;skip bad drive name
	CPI	16	;;and make sure valid
	JNC	BADNAME
	sta	rdfcb
	inx	h	;move source pointer over drive spec.
	inx	h
MOVEN	MOV	A,M
	CPI	':'	;;see if extraneous colon
	JZ	BADNAME
	CPI	'>'
	JZ	BADNAME
	CPI	'?'	;;dont allow ambiguity
	JZ	QMFILE
	CPI	','
	JZ	MOVED
	CPI	' '	;;stop at comma or blank
	JZ	MOVED
	cpi	'.'
	jz	movft
	ORA	A	;ZERO @ END?
	JZ	MOVEZ
	CPI	'['	;;no bad filenames
	JZ	BADNAME
	CPI	']'
	JZ	BADNAME
	STAX	D
	MVI	A,FCBEXT ;;is it past FCB ?
	CP	E
	JNC	BADNAME
	INX	H
	INX	D
	JMP	MOVEN
movft
	LDA	PRFLAG	;;check if already got period
	ORA	A
	JNZ	BADNAME
	CMA
	STA	PRFLAG	;;indicate found period
	inx	h
	lxi	d,rdfcb+9	;address filetype of read block
	jmp	moven
;
MOVEZ
	SHLD	NAMEADD	;SAVE NAME LIST ADDR
	LDA	RDFCB+1
	CPI	' '	;;look at file name
	STC
	RZ
;ALL DONE MOVING
MOVED	SHLD	NAMEADD	;SAVE NAME LIST ADDR
	LDA	RDFCB+1	;;see if no file
	CPI	' '
	JZ	NOFILE
	LDA	PRFLAG
	ORA	A	;;if got a period, not special name
	JNZ	NOCENT
	CALL	GETNUM	;;see if have valid number
	JNC	GOTNUM
;;	check for special filenames
	COMP	RDFCB,,,<0,'*   '>
	JZ	GOTBLNK
	COMP	RDFCB,,,<0,'%F  '>
	JZ	FORMFEED
	COMP	RDFCB,,,<0,'%T  '>
	JZ	TABCHAR
	COMP	RDFCB,,,<0,'%N  '>
	JNZ	NOCENT
	MVI	A,CR	;; CR
	STA	PCTFLAG
	CALL	WRBYTE
	MVI	A,LF	;; LF
	CALL	WRBYTE
	JMP	NEXTFL	;;now go skip by filename
FORMFEED
	MVI	A,0CH	;; FF
	STA	PCTFLAG
	CALL	WRBYTE	;; put it out
	JMP	NEXTFL
TABCHAR
	MVI	A,09H	;; Tab
	STA	PCTFLAG
	CALL	WRBYTE	;; put it out
	JMP	NEXTFL
NOCENT
	XRA	A
	STA	RDEXT	;ZERO EXTENT
	STA	RDRNO	;ZERO RECORD #
	CPM	dmaf,RDBUFF
	CPM	openf,RDFCB
	INR	A
	JZ	OPENERR
	MVI	A,128	;SHOW TIME TO READ
	STA	RDCOUNT
	CPM	dmaf,cibuf
	JMP	RDBYTE
;;
;;	Do console input here
GOTBLNK
	LDA	PRFLAG
	ORA	A	;;see if bad news
	JNZ	BADNAME
	CMA
	STA	PCTFLAG	;;deactivate error message
GOTLIN
	CPM	CONIN,INAREA
	LXI	H,INAREA+1 ;;get character count
	MOV	A,M
	ORA	A	;;see if no line
	JZ	NEXTFL
	MOV	B,A	;;initialize loop
	INR	B
GOTLUP
	DCR	B	;;see if end of line
	JZ	GOTEND
	INX	H	;;get next character
	MOV	A,M
	ORA	A	;;stop if NUL
	JZ	NEXTFL
	CPI	EOF	;;see of EOF
	JZ	NEXTFL
	PUSH	B
	PUSH	H
	CALL	WRBYTE	;;pop out character
	POP	H
	POP	B
	JMP	GOTLUP	;;no go get another
GOTEND
	MVI	A,CR	;;put out CR
	CALL	WRBYTE
	MVI	A,LF	;;put out LF
	CALL	WRBYTE
	LDA	CNFLAG	;;see if console in, file out
	ORA	A
	JNZ	GOTLIN
	MVI	A,LF	;;LF after CR if console in
	CALL	TYPE
	JMP	GOTLIN	;;go get another line
;;
;;	Now process the three numeric options
GOTNUM
	XCHG		;;put number in HL
	LDA	SGNFLAG
	ORA	A	;;check sign
	JNZ	GOTNUM2
	SHLD	WIDNUM	;;no sign
	JMP	NEXTFL
GOTNUM2
	CPI	'-'
	JNZ	GOTPLS
	SHLD	MINSNUM	;;minus sign
	JMP	NEXTFL
GOTPLS
	SHLD	PLUSNUM	;;plus sign
	JMP	NEXTFL
;;
;;
OPENERR	LXI	H,RDFCB+1
	MVI	B,11
NAMELP	MOV	A,M
	CALL	TYPE
	INX	H
	DCR	B
	JNZ	NAMELP
	CALL	ERXIT
	DB	': ++OPEN FAILED++$'
;;
;;
BADNAME
	LXI	H,RDFCB+1
	MVI	B,11
NAMEZZ	MOV	A,M
	CALL	TYPE
	INX	H
	DCR	B
	JNZ	NAMEZZ
	CALL	ERXIT
	DB	'++INVALID FILENAME++$'
;
;
NOROOM	CALL	ERXIT
	DB	'++NO ROOM ON OUTPUT DISK$'
;
TYPE	MOV	E,A
	CPM	WRCON
	RET
;;
;;	This code scans RDFCB for a decimal number and converts
;;	it into a 16-bit number in DE. Leading plus and minus
;;	signs are accepted and indicated in SGNFLAG. If there
;;	are any problems (bad digits or overflows), the carry
;;	flag is set and the routine stops.
;;
GETNUM
	LXI	H,RDFCB+1	;; initialize first
	MVI	B,8
	XRA	A
	MOV	E,A
	MOV	D,A
	STA	SGNFLAG
	MOV	A,M
	CPI	'+'		;; now check for leading +
	JZ	GOTSIGN
	CPI	'-'		;; or -
	JNZ	GETDIG
GOTSIGN
	STA	SGNFLAG		;; indicate the sign
GITLUP
	INX	H		;; bump up to next char
	DCR	B
	RZ			;; return if done
	MOV	A,M
	CPI	' '
	RZ
GETDIG
	CPI	'9'+1		;; now check for valid numeric
	CMC
	RC
	SBI	'0'		;; test and subtract combined
	RC
	MOV	C,A		;; now have 0-9 in C
	PUSH	H
	MOV	H,D
	MOV	L,E		;; DE - HL
	DAD	H
	RC			;; * 2
	MOV	D,H
	MOV	E,L
	DAD	H		;; * 4
	RC
	DAD	H		;; * 8
	RC
	DAD	D		;; * 10 = * ( 8+2 )
	RC
	XRA	A
	MOV	D,A		;; now add in digit
	MOV	E,C
	DAD	D
	RC
	XCHG			;; result back in DE
	POP	H
	JMP	GITLUP
;
;FOLLOWING FROM 'EQU7.LIB'---->
;
;MOVE SUBROUTINE
;
	IF	MF	;MACRO EXPANSION FLAG SET?
MOVER	MOV	A,M
	STAX	D
	INX	H
	INX	D
	DCX	B
	MOV	A,B
	ORA	C
	JNZ	MOVER
	RET
	ENDIF
;
	IF	CF	;MACRO EXPANSION FLAG SET?
COMPR	LDAX	D
	CMP	M
	RNZ
	INX	D
	INX	H
	DCX	B
	MOV	A,B
	ORA	C
	JNZ	COMPR
	RET
	ENDIF
;
;
;EXIT WITH ERROR MESSAGE
MSGEXIT	EQU	$	;EXIT W/"INFORMATIONAL" MSG
ERXIT	POP	D	;GET MSG
	MVI	C,PRINT
	CALL	BDOS
;EXIT, RESTORING STACK AND RETURN
EXIT	LHLD	STACK
	SPHL
;
	rst	boot	;back to cp/m via a warm start.
			;changing this to a "ret" will return
			;to CP/M without the warm start, and so
			;the command file will not be executed
			;until the next boot from this disk.
;;
;;
BRAKAD
	DW	0	;;start of prefix string
B2AKAD
	DW	0	;;start of suffix string
PCTFLAG
	DB	0	;;flag to indicate percent
PRFLAG
	DB	0	;;flag to indicate period
CNFLAG
	DB	0	;;flag indicating console
CRFLAG
	DB	1	;;flag indicating CR or LF
STRFLAG
	DB	0	;;flag indicating doing $$$.sub
WIDNUM
	DW	65535	;;maximum line width
MINSNUM
	DW	65535	;;maximum line number
PLUSNUM
	DW	0	;;beginning line number
WIDTH
	DW	0	;;current line width
LINE
	DW	0	;;current line number
;;
WRBUFAD	DW	WRBUFF	;OUTPUT BUFFER ADDR
WRCOUNT	DW	0	;BYTE COUNTER
RDBUFAD	DW	RDBUFF
RDDRV	DB	0	;DRIVE NAME TO READ FROM
RDFCB	DB	0,'filenameext'
RDEXT	DB	0
	DS	19
RDRNO	DB	0
RDCOUNT	DB	128	;CHARACTER COUNT IN BUFF
;
xfcb:	db	1, '$$$     SUB'	;;note file on A: drive
	dw	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
;;
;;CONSOLE INPUT LINE BUFFER
INAREA	DB	254
	DS	255
;;
;;	Only unitialized RAM after this 
;;
	DS	40H	;STACK AREA
STACK	DS	2
;;
;POINTER TO LIST OF FILENAMES
NAMEADD	DS	2
;;
SUBOUT
	DS	2	;;pointer to submit output buffer
;;
SGNFLAG
	DS	1	;;indicates if a sign was found in GETNUM
;;
;COPY OF INPUT COMMAND BUFFER (FROM 80H)
HOLD	DS	128
;;
;OUTPUT DISK BUFFER
	ORG	($+255) AND 0FF00H ;TO PAGE
RDBUFF	DS	128
subbuf:
WRBUFF	DS	BLKSIZE
;;
;;	The end of the buffer must not overlay the BIOS
ENDBUF
;;
;BDOS/CBIOS EQUATES (VERSION 7)	
RDCON	EQU	1
WRCON	EQU	2
PRINT	EQU	9
CONIN	EQU	10
CONST	EQU	11
openf	equ	15
closef	equ	16
SRCHF	EQU	17
SRCHN	EQU	18
delete	equ	19
readf	EQU	20
writef	equ	21
makef	equ	22
REN	EQU	23
dmaf	equ	26
;
boot	equ	  0	;for a warm start
bdos	equ	  5	;cp/m entry point
FCB	EQU	5CH 
FCB2	EQU	6CH
FCBEXT	EQU	FCB+12
FCBRNO	EQU	FCB+32
cibuf	equ	080h	;console input buffer
	END
