; CPSCMD.ASM
;       KERMIT - (Celtic for "FREE")
;
;       This is the CP/M-80 implementation of the Columbia University
;       KERMIT file transfer protocol.
;
;       Version 4.0
;
;       Copyright June 1981,1982,1983,1984,1985
;       Columbia University
;
; Originally written by Bill Catchings of the Columbia University Center for
; Computing Activities, 612 W. 115th St., New York, NY 10025.
;
; Contributions by Frank da Cruz, Daphne Tzoar, Bernie Eiben,
; Bruce Tanner, Nick Bush, Greg Small, Kimmo Laaksonen, Jeff Damens, and many
; others. 
;
;
;       This file provides a user oriented way of parsing commands.
;       It is similar to that of the COMND JSYS in TOPS-20.
;
; revision history (latest first):
;
;edit 13, 17-Jan-1991 by MF. Modified "cmifil" routine to zero the entire
;	fcb (not just the extent) to fix a bug in the COPY command which
;	prevented successive COPY commands from working properly.
;edit 12, 16-Jan-1991 by MF. Modified routine "cmkeyw" to ignore leading
;	spaces/tabs before a keyword. This apparently was the intent in
;	"prompt" and "repars" (at least for command-lines) as the variable 
;	"cmsflg "is set upon command parse and reparse. The intent was ,
;	subverted, however, as "cmkeyw" did not reset the flag to ignore
;	leading white space for each search thru the key tables (even though
;	the buffer pointer to the keyword entered was reset). The fix was
;	to reset the "spaces seen" flag (cmsflg) after "cmkey2" so that
;	it is reset each time a new table entry is compared to the text
;	the user has entered from the keyboard/TAKE-file etc. The upshot
;	of all this is that the kluge code in "cminbf" at "cminb0" designed
;	to force Kermit to ignore leading white space on command-lines in
;	TAKE-files and on the CP/M command-line is no longer needed and,
;	therefore, has been eliminated. Also modify "comnd" to expect leading
;	spaces for functions other than "get keyword".
;edit 11, 26-Dec-1990 by MF. Modified routines to ignore leading white space
;	in lines from TAKE-files as well as during input from the CP/M
;	command-line  (form-feeds are now considered white space under these
;	circumstances).
;edit 10, 8-Sep-1990 by Mike Freeman.  Modified routines to ignore leading
;	spaces/tabs when processing Kermit commands from the CP/M
;	command-line.
;	Added flag CMBFLG to allow initial word on a command-line
;	to be blank (useful for Remote commands such as Remote CWD etc).
;	Added flag cmqflg to prevent character-echoing while entering
;	commands so Remote CWD etc can have nonechoing password entry.
; edit 9, 15 June, 1987 by OBSchou.  Bug fixing to allow a second filename
;	(quiet) be entered as d:<blank>.  Previous revision put the drive name 
;	in first character of FCB, I put that character back to a space.
;
; edit 8, 12 June, 1987 by OBSchou.  Addedin code in cmkeyw to print
;	20 lines of help, then pause for a key from the user befor
;	proceeding with help.
;
; edit 7, 11 March, 1987 by OBSchou for Richard Russell, BBC.  He writes:
;	Bug in cmtext which prevented use of octal characters (\nnn) fixed.
;
; edit 6, 18 June, 1986 by OBSchou, Loughborough University, Leics. UK
;       added code to parse a number from user input.  Added check to make 
;       sure the input command buffer does not overflow the limit.
;
; edit 5a: 7 March, 1986.  OBSchou. Added stuff rom MJ Carter.  He writes:
;       7th May 85, MJ Carter [majoc], Nottingham University
;       Code in cmifil() put one too many spaces in the FCB; this caused 
;       the BDOS of the British Micro Mimi to search for exteny 32,
;       rather than extent 0, so era() always said "can't find file"
;       Puttig a null at the point in question ought to fix 9 it.
;
; edit 5: 6-Feb-85 by Charles Carvalho
;       Make ffussy a runtime (rather than assembly-time) switch, to
;       eliminate conditional assembly in system-independent module.
;       Don't allow _%|()/\ in filenames if ffussy set; my CP/M manual
;       disallows those, too.
;
; edit 4: 13-Jan-85 by Vanya J.Cooper Pima Commun. College Tel: 602-884-6809
;
;pcc006 2-jan-85        VJC     modules:cp4cmd,cp4utl
;       Problems with "?" in filespecs.  On reparse, may cause action
;       flag to be reset at wrong point, requiring multiple <CR>'s
;       to terminate the line or other weird stuff.  Also need to
;       check flag and complain if wild-cards illegal.

;pcc007 2-Jan-85        vjc     modules:cp4def,cp4cmd
;       Cmifil is too fussy about what characters to accept in a
;       filespec.  My CP/M manual says any printable character is ok
;       except <>.,;:?*[], and lower case.  In practice, even those work
;       sometimes.  Kermit itself uses '&' if file warning is on,
;       and then won't let you reference the file.  Allow all
;       printable characters except those above.  Add conditional
;       ffussy, so that if not ffussy, all special characters will be
;       allowed, just convert lower to upper-case.

; edit 3: July 8, 1984 (CJC)
;       integrate Toad Hall changes for LASM compatibility: CP4CPM is linked
;       by CP4WLD, and links CP4UTL.
;       
; edit 2: June 5, 1984 (CJC)
;       formatting and documentation; delete unnecessary code at cminb7; add
;       module version string.
;
; edit 1: May, 1984 (CJC)
;       extracted from CPMBASE.M80 version 3.9; modifications are described in
;       the accompanying .UPD file.

cmdver:	db	'CPSCMD.ASM (13)  17-Jan-1991$'	; name, edit number, date

;       This routine prints the prompt in DE and specifies the reparse
;       address.
;       called by:  kermit

prompt:	pop	h		;Get the return address.
	push	h		;Put it on the stack again.
	shld	cmrprs		;Save it as the address to go to on reparse.
	lxi	h,0		;Clear out hl pair.
	dad	sp		;Get the present stack pointer.
	shld	cmostp		;Save for later restoral.
	xchg			;Save the pointer to the prompt.
	shld	cmprmp
	xchg
	lxi	h,cmdbuf
	shld	cmcptr		;Initialize the command pointer.
	shld	cmdptr
	xra	a
	sta	cmaflg		;Zero the flags.
	sta	cmccnt
;	mvi	a,0FFH		;Try it this way (Daphne.)
;	sta	cmsflg
	call	prcrlf		;Print a CR/LF [Toad Hall]
	jmp	prprmp		;Print the prompt.  [Toad Hall]
;
;       This address is jumped to on reparse.
;       here from:  cmcfrm, cmkeyw, cmifil, cminbf

repars:	lhld	cmostp		;Get the old stack pointer.
	sphl			;Make it the present one.
	lxi	h,cmdbuf
	shld	cmdptr
;	mvi	a,0FFH		;Try it this way (Daphne.)
;	sta	cmsflg
	lhld	cmrprs		;Get the reparse address.
	pchl			;Go there.

;       This address can be jumped to on a parsing error.
;       here from:  cmkeyw, cminbf

prserr:	lhld	cmostp		;Get the old stack pointer.
	sphl			;Make it the present one.
	lxi	h,cmdbuf
	shld	cmcptr		;Initialize the command pointer.
	shld	cmdptr
	xra	a
	sta	cmaflg		;Zero the flags.
	sta	cmccnt
;	mvi	a,0FFH		;Try it this way (Daphne.)
;	sta	cmsflg
	call	prcrlf		;Print a CR/LF  [Toad Hall]
	call	prprmp		;Print the prompt  [Toad Hall]
;* Instead return to before the prompt call.
	lhld	cmrprs
	pchl
;
;       This routine parses the specified function in A.  Any additional
;       information is in DE and HL.
;       Returns +1 on success
;               +4 on failure (assumes a JMP follows the call)
;       called by:  log, setcom, read, send, xmit, dir, era, keycmd, cfmcmd
;	and CPSREM

comnd:	sta	cmstat		;Save what we are presently parsing.
	call	cminbf		;Get chars until an action or a erase char.
	push	psw		;[MF]Save function
	mvi	a,0ffh		;[MF]Expect leading spaces
	sta	cmsflg		;[MF]...
	pop	psw		;[MF]Restore function
	cpi	cmcfm		;Parse a confirm?
	jz	cmcfrm		;Go get one.
	cpi	cmkey		;Parse a keyword?
	jz	cmkeyw		;Try and get one.
	cpi	cmifi		;Parse an input file spec?
	jz	cmifil		;Go get one.
	cpi	cmifin		;Input file-spec silent?
	jz	cmifil		;do as he wishes
	cpi	cmofi		;Output file spec?
	jz	cmofil		;Go get one.
	cpi	cmtxt		;Parse arbitrary text?
	jz	cmtext		;Go do it.
	cpi	cmnum		;[7] Parse a number?
	jz	cmnumb		;[7] go do it
	lxi	d,cmer00	;"?Unrecognized COMND call"
	call	prtstr
	ret
;
;       This routine parses arbitrary text up to a CR.
;       Accepts DE:     address to put text
;       Returns in A:   number of chars in text (may be 0)
;                DE:    updated pointer
;       called by: comnd

cmtext:	xra	a		; clear counters erc for slashes etc
	sta	slshsn		; if we are in a slash sequence
	sta	slashn		; the octal number being entered
	sta	slashc		; number of characters entered

	xchg			;Put the pointer to the dest in HL.
	shld	cmptab		;Save the pointer.
	mvi	b,0		;Init the char count
cmtxt1:	call	cmgtch		;Get a char.
	ora	a		;Terminator?
	jp	cmtx3		;No, put in user space. [rtr] was cmtx5
	ani	7FH		;Turn off minus bit.
	cpi	esc		;An escape?
	jnz	cmtxt2		;No.
	mvi	c,conout
	mvi	e,bell		;Get a bell.
	call	bdos
	xra	a
	sta	cmaflg		;Turn off the action flag.
	lhld	cmcptr		;Move the pointer to before the escape.
	dcx	h
	shld	cmcptr
	shld	cmdptr
	lxi	h,cmccnt	;Get the char count.
	dcr	m		;Decrement it by one.
	jmp	cmtxt1		;Try again.

cmtxt2:	cpi	'?'             ;Is it a question mark?
	jz	cmtxt4		;If so put it in the text. [rtr] was cmtx3
	cpi	ff		;Is it a formfeed?
	cz	clrtop		;If so blank the screen.
	mov	a,b		;Return the count.
	lhld	cmptab		;Return updated pointer in HL.
	xchg
	jmp	rskp		;Return success.

cmtx3:	cpi	'\'             ; slash?
	jnz	cmtx3a		; nope, so try something else
	lda	slshsn		; a slash already entered?
	ana	a
	cma			;[rtr]
	jnz	cmtx3a		; yes, so assume its a valid slash to enter
	sta	slshsn		; make sure the flag is set for next time routnd
	jmp	cmtxt1		; get another character

cmtx3a:
;	lxi	h,cmaflg	;Point to the action flag.
;	mvi	m,0		;Set it to zero.
	mov	e,a		; save it in case we are interpreting a slash
	lda	slshsn		; slash already entered?
	ana	a		; test flag
	mov	a,e		; restore it in case...
	jz	cmtx5		; not a slash seen, so enter as a normal character
	cpi	'\'
	jnz	cmtx3b		; \\ not detected
	lda	slashn		; else get number
	jmp	cmtx5b		; and enter it ( in the case of \n or \nn)
				; here if an octal number of 1 or 2 digits
				; entered instead of 3, followed by \ again

cmtx3b:
	sui	30h		; else it should be an octal number
	jm	cmtxt6		; if not a digit complain
	cpi	8		; ditto
	jp	cmtxt6		;[rtr] was cmtxt
	mov	e,a		; else add it to the number already entered
	lda	slashn
	add	a
	add	a
	add	a		; multiply by 8
	add	e
	sta	slashn
	lda	slashc		; get the count
	inr	a
	sta	slashc		; plus one.  If three then a number entered
	cpi	3
	lda	slashn		; get the number in case...
	jz	cmtx5
	jmp	cmtxt1		; else loop

cmtxt4:	lhld	cmdptr		;[rtr] Get a pointer into the buffer
	inx	h		;[rtr] Bump past '?'
	shld	cmdptr		;[rtr]
cmtx5:	call	cmtx5c
	jmp	cmtxt1		; put this into a subroutine

cmtx5b:
	call	cmtx5c		; here if we see \n\ or \nn\ rather than \nnn\
	mvi	a,'\'           ; so send slash number to buffer, 
	sta	slshsn		; re-store a slash seen
	jmp	cmtxt1		; try next one

cmtx5c:
	inr	b		;Increment the count.
	lhld	cmptab		;Get the pointer.
	mov	m,a		;Put the char in the array.
	inx	h
	shld	cmptab		;Save the updated pointer.
	xra	a		; clear slash counters etc
	sta	slashc
	sta	slashn
	sta	slshsn
	ret			; and exit

cmtxt6:	lxi	d,cmer05	; complain - not a valid \ parameter
	call	prtstr
	jmp	kermit		; and try another command
	ds	20h		; for debugging
;
;       This routine gets a number from user input.
;       Called by: comnd
;
cmnumb:	lxi	h,0		; make sure the number is zero to start with
	shld	number
cmnum0:	call	cmgtch		; get another character
	ora	a		; if negative then its an action
	jp	cmnum1		; nope, so (possibly) valid input
	ani	7fh		; else lets see what it is...
	cpi	esc		; do not know what to do with this one...
	cpi	' '             ; if it is a space then either a return or more
	jnz	cmnum2		; else
	jmp	rskp		; space is a deliminter
	dw	0		; set three bytes aside for a jump/call
	dw	0		; and then another three just in case...
	dw	0		; making 6 bytes
cmnum2:	cpi	'?'             ; user is curious
	jz	gnum2
	cpi	cr		; end of input?
	jz	cmnumx
gnum1:	jmp	prserr		; did not under stand this, so error
cmnumx:	dw	0
	dw	0
	jmp	rskp		; return ok

gnum2:	lhld	number		; get the number.. if at all entered
	mov	a,l
	ora	h		; if hl = 0 then possibly no number entered
	lxi	d,cmin02	; say confirm...or more on line
	jnz	gnum21		; else say enter a return
	lxi	d,cmin01	; say enter a number
gnum21:	call	prtstr		; say it
	call	prcrlf		; do a lf
	call	prprmp		; another reprompt
	lhld	cmdptr		; get pointer of string already entered
	mvi	m,'$'           ; dollar it to  set end of line
	lhld	cmcptr
	dcx	h		; decrement and save the buffer pointer
	shld	cmcptr
	lxi	d,cmdbuf
	call	prtstr		; print what has already been entered
	xra	a
	sta	cmaflg		; turn the action flag off
	jmp	repars		; and try again

	mvi	a,cmcfm		; parse a confirm
dw	0
dw	0
dw	0
dw	0
dw	0	; some space to patch...
dw	0

cmnum1:	ani	7fh		; here for a (potentially) valid number
	sui	'0'             ; less ascii bias
	jc	gnum3
	cpi	10		; if 10 or more its still bad
	jnc	gnum3
	cmc
	lhld	number		; now multiply number by ten and add the new value
	push	h
	pop	d
	dad	h		; hl = hl * 2
	dad	h		; * 4
	dad	d		; * 5
	dad	h		; * 10
	mvi	d,0
	mov	e,a		; add de to hl...
	dad	d
	shld	number
	jmp	cmnum0
;
gnum3:	lxi	d,cmer04	; invalid number...
	call	prtstr
	jmp	rskp
;

;
;       This routine gets a confirm.
;       called by: comnd

cmcfrm:	call	cmgtch		;Get a char.
	ora	a		;Is it negative (a terminator;a space or
				;a tab will not be returned here as they
				;will be seen as leading white space.)
	rp			;If not, return failure.
	ani	7FH		;Turn off the minus bit.
	cpi	esc		;Is it an escape?
	jnz	cmcfr2
	mvi	c,conout
	mvi	e,bell		;Get a bell.
	call	bdos
	xra	a
	sta	cmaflg		;Turn off the action flag.
	lhld	cmcptr		;Move the pointer to before the escape.
	dcx	h
	shld	cmcptr
	shld	cmdptr
	lxi	h,cmccnt	;Get the char count.
	dcr	m		;Decrement it by one.
	jmp	cmcfrm		;Try again.

cmcfr2:	cpi	'?'             ;Curious?
	jnz	cmcfr3
	lxi	d,cmin00	;Print something useful.
	call	prtstr
	call	prcrlf		;Print a crlf.  [Toad Hall]
	call	prprmp		;Reprint the prompt  [Toad Hall]
	lhld	cmdptr		;Get the pointer into the buffer.
	mvi	a,'$'           ;Put a $ there for printing.
	mov	m,a
	lhld	cmcptr
	dcx	h		;Decrement and save the buffer pointer.
	shld	cmcptr
	lxi	d,cmdbuf
	call	prtstr
	xra	a		;Turn off the action flag.
	sta	cmaflg
	jmp	repars		;Reparse everything.

cmcfr3:	cpi	ff		;Is it a form feed?
	cz	clrtop		;If so blank the screen.
	jmp	rskp
;
;       This routine parses a keyword from the table pointed
;       to in DE.  The format of the table is as follows:
;
;       addr:   db      n       ;Where n is the # of entries in the table.
;               db      m       ;M is the size of the keyword.
;               db      'string$' ;Where string is the keyword.
;               db      a,b     ;Where a & b are pieces of data
;                               ;to be returned.  (Must be two of them.)
;
;       The keywords must be in alphabetical order.
;**** Note:  The data value a is returned in registers A and E.  The
;****   data value b is returned in register D.  This allows the two data
;       bytes to be stored as:
;               dw      xxx
;       and result in a correctly formatted 16-bit value in register pair
;       DE.
;       called by: comnd

cmkeyw:	shld	cmhlp		;Save the help.
	xchg			;Get the address of the table.
	shld	cmptab		;Save the beginning of keyword tab for '?'.
	mov	b,m		;Get the number of entries in the table.
	inx	h
	shld	cmkptr
	lhld	cmdptr		;Save the command pointer.
	shld	cmsptr
cmkey2:	mov	a,b		;Get the number of entries left.
	ora	a		;Any left?
	rz			;If not we failed.
	mvi	a,0ffh		;[MF]Make sure we ignore leading spaces
	sta	cmsflg		;[MF]...
	lhld	cmkptr
	mov	e,m		;Get the length of the keyword.
	inx	h
cmkey3:	dcr	e		;Decrement the number of chars left.
	mov	a,e
	cpi	0FFH		;Have we passed the end?
	jm	cmkey5		;If so go to the next.
	call	cmgtch		;Get a char.
	ora	a		;Is it a terminator?
	jp	cmkey4		;If positive, it is not.
	ani	7FH		;Turn off the minus bit.
	cpi	'?'
	jnz	cmky31
	xra	a
	sta	cmaflg		;Turn off the action flag.
	lxi	h,cmccnt	;Decrement the char count.
	dcr	m
;* Must go through the keyword table and print them.
	lhld	cmhlp		;For now print the help text.
	xchg
	call	p20ln		;[8] print at most 20 lines then pause
;	call	prtstr
	call	prcrlf		;Print a crlf  [Toad Hall]
	call	prprmp		;Reprint the prompt  [Toad Hall]
	lhld	cmdptr		;Get the pointer into the buffer.
	mvi	a,'$'           ;Put a $ there for printing.
	mov	m,a
	lhld	cmcptr
	dcx	h		;Decrement and save the buffer pointer.
	shld	cmcptr
	lxi	d,cmdbuf
	call	prtstr
	jmp	repars		;Reparse everything.

cmky31:	cpi	esc		;Is it an escape?
	jnz	cmky35
	xra	a
	sta	cmaflg		;Turn off the action flag.
	push	d
	push	b
	push	h
	call	cmambg
	jmp	cmky32		;Not ambiguous.
	mvi	c,conout
	mvi	e,bell
	call	bdos		;Ring the bell.
	lhld	cmcptr		;Move the pointer to before the escape.
	dcx	h
	shld	cmcptr
	shld	cmdptr
	lxi	h,cmccnt	;Get the char count.
	dcr	m		;Decrement it by one.
	pop	h
	pop	b
	pop	d
	inr	e		;Increment the left to parse char count.
	jmp	cmkey3

cmky32:	lhld	cmcptr		;Pointer into buffer.
	dcx	h		;Backup to the escape.
	xchg
	pop	h
	push	h
cmky33:	mov	a,m		;Get the next char.
	cpi	'$'             ;Finished?
	jz	cmky34
	inx	h
	xchg
	mov	m,a		;Move it into the buffer.
	inx	h
	xchg
	lda	cmccnt		;Increment the char count.
	inr	a
	sta	cmccnt
	jmp	cmky33

cmky34:	lda	cmccnt		;Get the character count.
	inr	a		;Increment and save it.
	sta	cmccnt
	xchg			;Put the command buffer pointer in HL.
	mvi	a,' '           ;Get a blank.
	mov	m,a		;Put it in the command buffer.
	inx	h		;Increment the pointer
	shld	cmcptr		;Save the updated pointer.
	shld	cmdptr
	pop	h
	push	h
	xchg
	call	prtstr		;Print the rest of the keyword.
	mvi	c,conout
	mvi	e,' '
	call	bdos		;Print a blank.
	pop	h
	pop	b
	pop	d
	jmp	cmky37

cmky35:	push	h
	push	d
	call	cmambg
	jmp	cmky36
	lxi	d,cmer01
	call	prtstr		;Say its ambiguous.
	jmp	prserr		;Give up.

cmky36:	pop	d
	pop	h
cmky37:	inr	e		;Add one incase it is negative.
	mvi	d,0
	dad	d		;Increment past the keyword.
	inx	h		;Past the $.
	mov	e,m		;Get the data.
	inx	h
	mov	d,m
	mov	a,e
	jmp	rskp

cmkey4:	cpi	'a'             ;Is it less than a?
	jm	cmky41		;If so don't capitalize it.
	cpi	'z'+1           ;Is it more than z?
	jp	cmky41		;If so don't capitalize it.
	ani	137O		;Capitalize it.
cmky41:	mov	d,m		;Get the next char of the keyword.
	inx	h
	cmp	d		;Match?
	jz	cmkey3		;If so get the next letter.

cmkey5:	mvi	d,0
	mov	a,e		;Get the number of chars left.
	ora	a		;Is it negative?
	jp	cmky51
	mvi	d,0FFH		;If so, sign extend.
cmky51:	dad	d		;Increment past the keyword.
	lxi	d,0003H		;Plus the $ and data.
	dad	d
	shld	cmkptr
	dcr	b		;Decrement the number of entries left.
	lhld	cmsptr		;Get the old cmdptr.
	shld	cmdptr		;Restore it.
;* check so we don't pass it.
	jmp	cmkey2		;Go check the next keyword.
;
;       Test keyword for ambiguity.
;       returns: nonskip if ambiguous, skip if OK.
;       called by: cmkeyw

cmambg:	dcr	b		;Decrement the number of entries left.
	rm			;If none left then it is not ambiguous.
	inr	e		;This is off by one;adjust.
	mov	c,e		;Save the char count.
	mov	a,e
	ora	a		;Any chars left?
	rz			;No, it can't be ambiguous.
	mvi	d,0
	dad	d		;Increment past the keyword.
	mvi	e,3		;Plus the $ and data.
	dad	d
	mov	b,m		;Get the length of the keyword.
	inx	h
	xchg
	lhld	cmkptr		;Get pointer to keyword entry.
	mov	a,m		;Get the length of the keyword.
	sub	c		;Subtract how many left.
	mov	c,a		;Save the count.
	cmp	b
	jz	cmamb0
	rp			;If larger than the new word then not amb.
cmamb0:	lhld	cmsptr		;Get the pointer to what parsed.
cmamb1:	dcr	c		;Decrement the count.
	jm	rskp		;If we are done then it is ambiguous.
	xchg			;Exchange the pointers.
	mov	b,m		;Get the next char of the keyword
	inx	h
	xchg			;Exchange the pointers.
	mov	a,m		;Get the next parsed char.
	inx	h
	cpi	'a'             ;Is it less than a?
	jm	cmamb2		;If so don't capitalize it.
	cpi	'z'+1           ;Is it more than z?
	jp	cmamb2		;If so don't capitalize it.
	ani	137O
cmamb2:	cmp	b		;Are they equal?
	rnz			;If not then its not ambiguous.
	jmp	cmamb1		;Check the next char.
;
;       cmofil - parse output filespec
;       cmifil - parse input filespec
;       here from: comnd

cmofil:	mvi	a,0		;Don't allow wildcards.
;       jmp     cmifil          ;For now, the same as CMIFI.
cmifil:	sta	cmfwld		;Set wildcard flag
	xchg			;Get the fcb address.
	shld	cmfcb		;Save it.
	mvi	e,0		;Initialize char count.
	mvi	m,0		;Set the drive to default to current.
	inx	h
	shld	cmfcb2
	xra	a		;Initialize counter.
cmifi0:	mvi	m,' '           ;Blank the FCB.
	inx	h
	inr	a
;       cpi     0CH             ;Twelve? [5a dont use this]
	cpi	0Bh		; [majoc 850585] Eleven?
	jm	cmifi0
cmif0a:				;[MF]Zero entire fcb, not just the extent
	mvi	m,0		; [majoc 850507] Specify extent 0
	inx	h		;[MF]Increment fcb byte pointer
	inr	a		;[MF]Increment fcb byte count
	cpi	32		;[MF]Done with fcb?
	jm	cmif0a		;[MF]No, zero until done
cmifi1:	call	cmgtch		;Get another char.
	ora	a		;Is it an action character?
	jp	cmifi2
	ani	7FH		;Turn off the action bit.
	cpi	'?'             ;A question mark?
	jnz	cmif12
	lda	cmfwld		;[pcc006] Wildcards allowed?
	ora	a		;[pcc006]
	jz	cmif11		;[pcc006] complain if not
	lhld	cmdptr		;[jd] Increment buffer pointer
	inx	h		;[jd] that was decremented in cmgtch
	shld	cmdptr		;[jd] since we want this chr
	lda	cmcptr		;[pcc006] get lsb of real input pointer
	cmp	l		;[pcc006] is this the last chr input?
	jnz	cmif1a		;[pcc006] no, don't reset action flag
	xra	a		;[pcc006] yes, reset action flag
	sta	cmaflg		;[pcc006] 
cmif1a:	mvi	a,'?'           ;[pcc006] get it back in A
	jmp	cmifi8		;Treat like any other character

cmif12:	cpi	esc		;An escape?
	jnz	cmif13
;Try to recognize file-spec a'la TOPS-20
	xra	a
	sta	cmaflg		;Turn off the action flag.
	lhld	cmcptr		;Move the pointer to before the escape.
	dcx	h
	shld	cmcptr
	shld	cmdptr
	lxi	h,cmccnt	;Get the char count.
	dcr	m		;Decrement it by one.
	mov	a,e		;Save character count up to now.
	sta	temp1
	cpi	9		;Past '.'?
	jm	cmfrec		;No.
	dcr	a		;Yes, don't count point.
cmfrec:	lhld	cmfcb2		;Fill the rest with CP/M wildcards.
cmfrc1:	cpi	11		;Done?
	jp	cmfrc2		;Yes.
	mvi	m,'?'
	inx	h
	inr	a
	jmp	cmfrc1

cmfrc2:	mvi	c,sfirst	;Find first matching file?
	lhld	cmfcb
	xchg
	call	bdos
	cpi	0FFH
	jz	cmfrc9		;No, lose.
	lxi	h,fcbblk	;Copy first file spec.
	call	fspcop
	lxi	h,fcbblk+10H	;Get another copy (in case not ambiguous).
	call	fspcop
	mvi	c,snext		;More matching specs?
	lhld	cmfcb
	xchg
	call	bdos
	cpi	0FFH
	jz	cmfrc3		;Only one.
	lxi	h,fcbblk+10H	;Copy second file spec.
	call	fspcop
cmfrc3:	lxi	d,fcbblk	;Start comparing file names.
	lxi	h,fcbblk+10H
	lda	temp1		;Bypass characters typed.
	cpi	9		;Past '.'?
	jm	cmfrc4		;No.
	dcr	a		;Yes, don't count point.
cmfrc4:	mvi	c,0
cmfrl1:	cmp	c		;Bypassed?
	jz	cmfrl2		;Yes.
	inx	d
	inx	h
	inr	c
	jmp	cmfrl1		;Repeat.

cmfrl2:	mov	a,c		;Get file name characters processed.
	cpi	11		;All done?
	jz	cmfrc5		;Yes.
	cpi	8		;End of file name?
	jnz	cmfrl3		;No.
	lda	temp1		;Exactly at point?
	cpi	9
	jz	cmfrl3		;Yes, don't output a second point.
	mvi	a,'.'           ;Output separator.
	call	cmfput
cmfrl3:	ldax	d		;Get a character from first file spec.
	inx	d
	mov	b,m		;Get from second file spec.
	inx	h
	cmp	b		;Compare.
	jnz	cmfrc5		;Ambiguous.
	inr	c		;Same, count.
	cpi	' '             ;Blank?
	jz	cmfrl2		;Yes, don't output.
	call	cmfput		;Put character into buffer.
	jmp	cmfrl2		;Repeat.

cmfrc5:	mov	a,c		;Get count of characters processed.
	sta	temp1		;Save it.
	mvi	a,'$'           ;Get terminator.
	call	cmfput		;Put it into buffer.
	lhld	cmdptr		;Output recognized characters.
	xchg
	mvi	c,prstr
	call	bdos
	lhld	cmcptr		;Remove terminator from buffer.
	dcx	h
	shld	cmcptr
	lxi	h,cmccnt
	dcr	m
	lda	temp1		;Characters processed.
	cpi	11		;Complete file name.
	jz	repars		;Yes, don't beep.

cmfrc9:	mvi	c,conout
	mvi	e,bell
	call	bdos		;Ring the bell.
	jmp	repars
;
;       Continue file spec parsing.

cmif13:	mov	a,e		;It must be a terminator.
	ora	a		;Test the length of the file name.
	jz	cmifi9		;If zero complain.
	cpi	0DH
	jp	cmifi9		;If too long complain.
	jmp	rskp		;Otherwise we have succeeded.

cmifi2:	cpi	'.'
	jnz	cmifi3
	inr	e
	mov	a,e
	cpi	1H		;Any chars yet?
	jz	cmifi9		;No, give error.
	cpi	0AH		;Tenth char?
	jp	cmifi9		;Past it, give an error.
	mvi	c,9H
	mvi	b,0
	lhld	cmfcb
	dad	b		;Point to file type field.
	shld	cmfcb2
	mvi	e,9H		;Say we've gotten nine.
	jmp	cmifi1		;Get the next char.

cmifi3:	cpi	':'
	jnz	cmifi4
	inr	e
	mov	a,e
	cpi	2H		;Is it in the right place for a drive?
	jnz	cmifi9		;If not, complain.
	lhld	cmfcb2
	dcx	h		;Point to previous character.
	mov	a,m		;Get the drive name.
	sui	'@'             ;Get the drive number.
	shld	cmfcb2		;Save pointer to beginning of name field.
	mvi	m,space		;[obs] restore a space in FCB
	dcx	h		;Point to drive number.
	mov	m,a		;Put it in the fcb.
	mvi	e,0		;Start character count over.
	jmp	cmifi1

cmifi4:	cpi	'*'
	jnz	cmifi7
	lda	cmfwld		;Wildcards allowed?
	cpi	0
	jz	cmif11		;No,complain
	mov	a,e
	cpi	8H		;Is this in the name or type field?
	jz	cmifi9		;If its where the dot should be give up.
	jp	cmifi5		;Type.
	mvi	b,8H		;Eight chars.
	jmp	cmifi6

cmifi5:	mvi	b,0CH		;Three chars.
cmifi6:	lhld	cmfcb2		;Get a pointer into the FCB.
	mvi	a,'?'
	mov	m,a		;Put a question mark in.
	inx	h
	shld	cmfcb2
	inr	e
	mov	a,e
	cmp	b
	jm	cmifi6		;Go fill in another.
	jmp	cmifi1		;Get the next char.

cmifi7:	cpi	'!'             ;[pcc007] control chr or space?
	jm	cmifi9		;[pcc007] yes, illegal
	mov	h,a		;[5] stash input char for a bit
	lda	ffussy		;[5]  while we check the fussy flag
	ora	a		;[5] set the flags accordingly
	mov	a,h		;[5] restore the input character
	jz	cmif7a		;[5] if ffussy=0, allow <>.,;:?*[]
;[5] So far, we've eliminated "action characters" (including question),
;[5] period, colon, asterisk, control characters, and space.
;[5] That leaves us %(),/;<=>[\]_| to check for.
	cpi	'%'             ;[5]
	jz	cmifi9		;[5]
	cpi	'('             ;[5]
	jz	cmifi9		;[5]
	cpi	')'             ;[5]
	jz	cmifi9		;[5]
	cpi	','             ;[pcc007] weed out comma
	jz	cmifi9		;[pcc007]
	cpi	'/'             ;[5]
	jz	cmifi9		;[5]
	cpi	'9'+1           ;[pcc007] anything else 21H-39H is ok
	jm	cmifi8		;[pcc007] except '*' never gets here
	cpi	'@'             ;[pcc007] all of 3AH-3FH is illegal
	jm	cmifi9		;[pcc007]
	cpi	'['             ;[pcc007] [\] also illegal
	jm	cmifi8		;[pcc007]
	cpi	']'+1           ;[pcc007]
	jm	cmifi9		;[pcc007]
	cpi	'_'             ;[5]
	jz	cmifi9		;[5] (If I was doing CP/M, I would have
	cpi	'|'             ;[5]  just eliminated all them funny chars
	jz	cmifi9		;[5]  instead of a random selection)
cmif7a:				;[5]
	cpi	'a'             ;[pcc007] if not lower case its ok
	jm	cmifi8		;[pcc007] (DEL never gets here)
	cpi	'z'+1           ;[pcc007] only convert letters
	jp	cmifi8		;[pcc007]
	ani	137O		;Capitalize.
cmifi8:	lhld	cmfcb2		;Get the pointer into the FCB.
	mov	m,a		;Put the char there.
	inx	h
	shld	cmfcb2
	inr	e
	jmp	cmifi1

cmifi9:	lda	cmstat
	cpi	cmifin		;"silent"?
	jz	r		;Yes,let him go w/o check
	lxi	d,cmer02
cmif10:	mvi	c,prstr
	call	bdos
	ret

cmif11:	lxi	d,cmer03	;Complain about wildcards.
	jmp	cmif10

;

;       copy filename from buffer
;       called with HL = destination, A = position (0-3) in buffer
;       called by: cmifil

fspcop:	push	psw		;Save A.
	lxi	d,buff		;Get the right offset in the buffer.
	rlc
	rlc
	rlc
	rlc
	rlc
	add	e
	inr	a		;Bypass drive spec.
	mov	e,a
	mvi	b,11		;Copy file name.
fspcp1:	ldax	d
	inx	d
	mov	m,a
	inx	h
	dcr	b
	jnz	fspcp1
	pop	psw		;Restore A.
	ret

;       append character in A to command buffer
;       called by: cmifil

cmfput:	push	h		;Save H.
	lhld	cmcptr		;Get buffer pointer.
	mov	m,a		;Store in buffer.
	inx	h
	shld	cmcptr
	lxi	h,cmccnt	;Count it.
	inr	m
	pop	h		;Restore H.
	ret
;
;       Read characters from the command buffer.
;       called by:  cmtext, cmcfrm, cmkeyw, cmifil

cmgtch:	push	h
	push	b
cmgtc1:	lda	cmaflg
	ora	a		;Is it set.
	cz	cminbf		;If the action char flag is not set get more.
	lhld	cmdptr		;Get a pointer into the buffer.
	mov	a,m		;Get the next char.
	inx	h
	shld	cmdptr
	cpi	' '             ;Is it a space?
	jz	cmgtc2
	cpi	tab		;Or a tab?
	jnz	cmgtc3
cmgtc2:	lda	cmsflg		;Get the space flag.
	ora	a		;Was the last char a space?
	jnz	cmgtc1		;Yes, get another char.
	mvi	a,0FFH		;Set the space flag.
	sta	cmsflg
	mvi	a,' '
	pop	b
	pop	h
	jmp	cmgtc5

cmgtc3:	push	psw
	xra	a
	sta	cmsflg		;Zero the space flag.
	pop	psw
	pop	b
	pop	h
	cpi	esc
	jz	cmgtc5
	cpi	'?'             ;Is the user curious?
	jz	cmgtc4
	cpi	cr
	jz	cmgtc4
	cpi	lf
	jz	cmgtc4
	cpi	ff
	rnz			;Not an action char, just return.
cmgtc4:	push	h
	lhld	cmdptr
	dcx	h
	shld	cmdptr
	pop	h
cmgtc5:	ori	80H		;Make the char negative to indicate it is
	ret			;a terminator.
;
;       Read characters from console into command buffer, processing
;       editing characters (^H, ^M, ^J, ^L, ^U, ^X, ?, del).
;       called by: comnd, cmgtch

cminbf:	push	psw
	push	d
	push	h
	lda	cmaflg		;Is the action char flag set?
	ora	a
	jnz	cminb9		;If so get no more chars.
cminb1:	lxi	h,cmccnt	;Increment the char count.
	inr	m
	mvi	c,conin		;Get a char.
	lda	cmqflg		;[MF]but do we want it echoed?
	ora	a		;[MF]...
	jz	cmin1b		;[MF]Yup, proceed normally
cmin1c:	mvi	e,0ffH		;[MF]Nope, do it with Direct
	mvi	c,dconio	;[MF]Console I/O
	call	bdos		;[MF]...
	ora	a		;[MF]Did the user type anything?
	jz	cmin1c		;[MF]No, don't go on until he/she does.
	jmp	cmin1a		;[MF]We got a character
cmin1b:	call	bdos
cmin1a:	lhld	cmcptr		;Get the pointer into the buffer.
	mov	m,a		;Put it in the buffer.
	inx	h
	shld	cmcptr
	cpi	25O		;Is it a ^U?
	jz	cmnb12		;Yes.
	cpi	30O		;Is it a ^X?
	jnz	cminb2
cmnb12:	call	clrlin		;Clear the line.
	call	prprmp		;Print the prompt  [Toad Hall]
	lxi	h,cmdbuf
	shld	cmcptr		;Reset the point to the start.
	lxi	h,cmccnt	;Zero the count.
	mvi	m,0
	jmp	repars		;Go start over.

cminb2:	cpi	10O		;Backspace?
	jz	cminb3
	cpi	del		;or Delete?
	jnz	cminb4
	lda	cmqflg		;[MF]If we are echoing characters,
	ora	a		;[MF]...
	cz	delchr		;Print the delete string. [MF]
cminb3:	lda	cmccnt		;Decrement the char count by two.
	dcr	a
	dcr	a
	ora	a		;Have we gone too far?
	jp	cmnb32		;If not proceed.
	mvi	c,conout	;Ring the bell.
	mvi	e,bell
	call	bdos
	jmp	cmnb12		;Go reprint prompt and reparse.

cmnb32:	sta	cmccnt		;Save the new char count.
	lda	cmqflg		;[MF]Echoing characters?
	ora	a		;[MF]If we are, then
	cz	clrspc		;Erase the character. [MF]
	lhld	cmcptr		;Get the pointer into the buffer.
	dcx	h		;Back up in the buffer.
	dcx	h
	shld	cmcptr
	jmp	repars		;Go reparse everything.

cminb4:	cpi	'?'             ;Is it a question mark.
	jz	cminb6
	cpi	esc		;Is it an escape?
	jz	cminb6
	cpi	cr		;Is it a carriage return?
	jz	cminb5
	cpi	lf		;Is it a line feed?
	jz	cminb5
	cpi	ff		;Is it a formfeed?
	jnz	cminb8		;no - just store it and 
				;test if buffer overflowing, else get another character.
	call	clrtop
cminb5:	lda	cmbflg		;[MF]Allowing initial blank word (<cr>)?
	ora	a		;[MF]...
	jnz	cminb6		;[MF]Yes
	lda	cmccnt		;Have we parsed any chars yet?
	cpi	1
	jz	prserr		;If not, just start over.
cminb6:	mvi	a,0FFH		;Set the action flag.
	sta	cmaflg
	jmp	cminb9

cminb8:
	lda	cmccnt		; get the command character count
	cpi	cmbufl		; check for comand buffer length
	jm	cminb1		; if less, then all ok
	mvi	e,bell		; else beep at user
	call	outcon		; send it to the console
	lda	cmccnt		; back up one character
	dcr	a
	sta	cmccnt
	lhld	cmcptr		; ditto pointer
	dcx	h
	shld	cmcptr		; save it again
	jmp	cminb1		; and try again

cminb9:	pop	h
	pop	d
	pop	psw
	ret
;
;Little utility to print the prompt.  (We do a LOT of these.)  [Toad Hall]
;Enters with nothing.
;Destroys HL (and I suppose B and DE and A).
 
prprmp:	mvi	e,cr		; do a cr first
	mvi	c,dconio
	call	bdos
	lhld	cmprmp		;Get the prompt.
	xchg
	call	prtstr
	ret

; Little code to allow some expansion of code without changing
;  every futher address, only up to the end of this file.
;   TO BE REMOVED FOR RELEASE!

;	org ($+100h) AND 0FF00H


IF	lasm
LINK	CPSUTL
ENDIF	;lasm  [Toad Hall]
