; CN8ICM.A86
; * * * * * * * * * * * * * * *  version 2.8  * * * * * * * * * * * * * * *
; [32c] remove check for 0-length filename
; [32b] fix minor bugs
; [32a] fix prompt to show default drive and user
; RonB, 09/13/84
; * * * * * * * * * * * * * * *  version 2.7  * * * * * * * * * * * * * * *
; [fdc] Fix small glitch w/CMLEVL that issued msg spuriously sometimes.
; [] Introduced CMLEVL flag to intercept "empty" command-options
; Reset by PRSERR, SET in 86KERMIT KERMIT:
;  B.E.; EIBEN at DEC-MARLBORO 2-May-84
; [30c] Isolate ANSI escape sequences for machine independence.
; [30b] Make DEL work like BS and ^X like ^U in command input.
; RonB, 04/18/84
; * * * * * * * * * * * * * * *  version 2.6  * * * * * * * * * * * * * * *
; [28d] Improve filename special character processing
; RonB, 03/27/84
; [25] Move logic for "seteol" and "escape" (from KERSYS) into here so those
; routines need not use internal CMD routines and variables.  For this
; purpose add 2 parse routines and codes: "cmcha" and "cmnum".  The point
; of this is to keep calls to CMD modular since I want to eventually
; replace the whole thing.
; R. Garland 9-Mar-1984
; * * * * * * * * * * * * * * *  version 2.1  * * * * * * * * * * * * * * *
;  [9] Fix filename parsing, and add wildcard ability.
; RonB,12/26/83
;  [8] Show choices for ambiguous keywords, finish keyword on '?'
; RonB,12/26/83
; * * * * * * * * * * * * * * *  version 2.0  * * * * * * * * * * * * * * *
; This module contains all the routines and storage necessary for the
; command parser.  The command parser approximates that of the Tops-20
; COMND% JSYS.  This code is adapted from the IBM PC Kermit code which
; was adapted from the CP/M-80 Kermit code.
; COMND definitions.

cmcfm equ 01H
cmkey equ 02H
cmifi equ 03H
cmofi equ 04H
cmtxt equ 05H
cmcha equ 06H
cmnum equ 07H

 DSEG $   ; Resume the data segment.

; COMND storage.

cmer00 db bell,'?Program error -- Invalid COMND call$'
cmer01 db bell,'?Ambiguous command$'
cmer02 db bell,'?Illegal input file spec$'
cmer03 db bell,'?Unrecognized instruction$'
cmer04 db bell,'?Invalid command or operand$'
cmer05 db bell,'?Missing command-option$'
cmin00 db ' Confirm with carriage return$'
cmin01  db      ' Input file spec (possibly wild) $'
cmin02 db ' One of the following:$'   ;[8]

cmlevl db 0  ;0 at main-level, 1 otherwise
cmstat db 0  ;What is presently being parsed.
cmaflg db 0  ;Non-zero when an action char has been found.
cmccnt db 0  ;Non-zero if a significant char is found.
cmsflg db 0  ;Non-zero when the last char was a space.
cmostp dw 0  ;Old stack pointer for reparse.
cmrprs dw 0  ;Address to go to on reparse.
cmprmp dw 0  ;Address of prompt.
cmptab dw 0  ;Address of present keyword table.
cmhlp dw 0  ;Address of present help.
cmdbuf rb 80H  ;Buffer for command parsing.
cmfcb dw 0  ;Pointer to FCB.
cmfcb2 dw 0  ;Pointer to position in FCB.
cmcptr dw 0  ;Pointer for next char input.
cmdptr dw 0  ;Pointer into the command buffer.
cmsiz dw 0  ;Size info of user input.
cmkptr dw 0  ;Pointer to keyword.
cmsptr dw 0  ;Place to save a pointer.
cmchr db 0  ;Save char when checking ambiguity.
cmten dw 10  ;the number "10"

spchar db '!#$%&()+-/@\^`|~0000' ;Valid special characters ;[8][28d]

; This set of routines provides a user oriented way of parsing
; commands.  It is similar to that of the COMND JSYS in TOPS-20.

 CSEG $   ;Resume coding.

; This routine prints the prompt in DX and specifies the reparse
; address.

prompt: pop bx  ;Get the return address.
 push bx  ;Put it on the stack again.
 mov cmrprs, bx ;Save as addr to go to on reparse.
 mov bx, 0  ;Clear out register.
 add bx, sp  ;Get the present stack pointer.
 mov cmostp, bx ;Save for later restoral.
 mov cmprmp, dx ;Save pointer to the prompt.
 mov bx, offset cmdbuf
 mov cmcptr, bx ;Initialize the command pointer.
 mov cmdptr, bx
 mov cmaflg, 0 ;Zero the flags.
 mov cmlevl, 0 ;[fdc]Including the level-flag
 mov cmccnt, 0
 mov cmsflg, 0FFH
reprompt:       ;[32a] begin
 call tcrlf
repmt2: mov dx, cmprmp ;Print the prompt.
 call tmsg
 mov dl,defdrv ;Print the default drive and user
 add dl,'A'
 call bout
 mov al,defusr
 cbw
 or ax,ax  ;Only print the user number if nonzero
 jz repmt3
 call nout
repmt3: mov dl,'>'
 call bout      ;[32a] end
 ret


; This address is jumped to on reparse.

repars: mov sp, cmostp ;new sp <-- old sp
 mov bx, offset cmdbuf
 mov cmdptr, bx
 mov cmsflg, 0FFH
 mov bx, cmrprs ;Get the reparse address.
 jmp bx  ;Go there.

; This address can be jumped to on a parsing error.

prserr: mov ah, cmlevl ;What level are we in?
 cmp ah, 0  ;
 jz prser1  ;skip error-message
 mov dx, offset cmer05 ;we're out of main-commands
 call tcrmsg  ;and got an empty option
 mov cmlevl, 0 ;reset level-flag
prser1: mov sp, cmostp ;Set new sp to old one.
 mov bx, offset cmdbuf
 mov cmcptr, bx ;Initialize the command pointer.
 mov cmdptr, bx
 mov cmaflg, 0 ;Zero the flags.
 mov cmccnt, 0
 mov cmsflg, 0FFH
 call reprompt     ;[32a]
 mov bx, cmrprs
 jmp bx


; This routine parses the specified function in AH. Any additional
; information is in DX and BX.
; Returns +1 on success
;  +4 on failure (assumes a JMP follows the call)

comnd: mov cmstat, ah ;Save what we are presently parsing.
 call cminbf  ;Get chars until an action or a erase char.
 mov ah, cmstat ;Restore 'ah' for upcoming checks.
 cmp ah, cmcfm ;Parse a confirm?
 jz cmcfrm  ;Go get one.
 cmp ah, cmkey ;Parse a keyword?
 jnz cm1
 jmp cmkeyw  ;Try and get one.
cm1: cmp ah, cmifi ;Parse an input file spec?
 jnz cm2
 jmp cmifil  ;Go get one.
cm2: cmp ah, cmofi ;Output file spec?
 jnz cm3
 jmp cmofil  ;Go get one.
cm3: cmp ah, cmtxt ;Parse arbitrary text.
 jnz cm4
 jmp cmtext
cm4: cmp ah, cmcha ;[25] parse a single character?
 jnz cm5  ;[25]
 jmp cmchar  ;[25] go do it.
cm5: cmp ah, cmnum ;[25] parse a (decimal) number?
 jnz cm99  ;[25]
 jmp cmnumr  ;[25] go do it.
cm99: mov dx, offset cmer00 ;"?Unrecognized COMND call" [25]
 call tcrmsg
 ret

; This routine gets a confirm.

cmcfrm: call cmgtch  ;Get a char.
 cmp ah, 0  ;Is it negative (a terminator;a space or
    ;a tab will not be returned here as they
    ;will be seen as leading white space)?
 js cmcfr0
 ret   ;If not, return failure.
cmcfr0: and ah, 7FH  ;Turn off the minus bit.
 cmp ah, esc  ;Is it an escape?
 jne cmcfr2
 mov dl, bell ;Get a bell.
 call bout  ;Output the char.
 mov cmaflg, 0 ;Turn off the action flag.
 mov bx, cmcptr ;Move the pointer to before the escape.
 dec bx
 mov cmcptr, bx
 mov cmdptr, bx
 dec cmccnt  ;Decrement the char count.
 jmp cmcfrm  ;Try again.
cmcfr2: cmp ah, '?'  ;Curious?
 jne cmcfr3
 mov dx, offset cmin00 ;Print something useful.
 call tmsg
 call reprompt ;Reprint the prompt  ;[32a]
 mov bx, cmdptr ;Get the pointer into the buffer.
 mov ah, '$'  ;Put a $ there for printing.
 mov [bx], ah
 mov bx, cmcptr
 dec bx  ;Decrement & save the buffer pointer.
 mov cmcptr, bx
 mov dx, offset cmdbuf
 call tmsg
 mov cmaflg, 0 ;Turn off the action flag.
 jmp repars  ;Reparse everything.

cmcfr3:        ;[8] begin
cmcfr4: jmp rskp


; This routine parses a keyword from the table pointed
; to in DX.  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.
;  dw ab ;Where ab is data to be returned.
;
; The keywords must be in alphabetical order.

cmkeyw: mov cmhlp, bx ;Save the help string.
 mov cmptab, dx ;Save the beginning of keyword table.
 mov bx, dx
 mov ch, [bx] ;Get number of entries in table.
 inc bx
 mov dx, cmdptr ;Save command pointer.
 mov cmsptr, dx ;Save pointer's here.
cmky1: cmp ch, 0  ;Any commands left to check?
 jne cmky2
 ret
cmky2: dec ch
 mov cl, 0  ;Keep track of how many chars read in so far.
 call cmgtch  ;Get a char.
 cmp ah, 0  ;Do we have a terminator?
 jns cmky2x
 jmp cmky4  ;Negative number means we do.
cmky2x: inc bx  ;Point to first letter of keyword.
 inc cl  ;Read in another char.
 mov al, [bx]
 cmp ah, 'a'  ;Less than a?
 jl cmky21  ;If so, don't capitalize.
 cmp ah, 'z'+1 ;More than z?
 jns cmky21
 and ah, 137O ;Capitalize the letter.
cmky21: cmp ah, al
 je cmky3
 jg cmky2y
 jmp cmky41  ;Fail if ah preceeds al alphabetically.
cmky2y: jmp cmky6  ;Not this keyword - try the next.
cmky3: inc bx  ;We match here, how 'bout next char?
 mov al, [bx]
 cmp al, '$'  ;End of keyword?
 jne cmky3x
 jmp cmky7  ;Succeed.
cmky3x: mov dl, al  ;Save al's char here.
 call cmgtch
 inc cl  ;Read in another char.
 mov al, dl
 cmp ah, 'a'
 jl cmky31
 cmp ah, 'z'+1
 jns cmky31
 and ah, 137O
cmky31: cmp ah, esc+80H ;Escape Recognition (escape w/minus bit on)?
 je cmky3y
 cmp ah, '?'+80H ;A question mark?
 je cmky3y
 cmp ah, ' '+80H ;A space?
 je cmky3y
 cmp ah, cr+80H ;Carriage return?
 je cmky3y
 jmp cmky38
cmky3y: mov cmkptr, bx ;Save bx here.
 mov cmsiz, cx ;Save size info.
 mov cmchr, ah ;Save char for latter.
 call cmambg  ;See if input is ambiguous or not.
  jmp cmky32 ;Succeeded (not ambiguous).
 mov ah, cmchr
 cmp ah, esc+80H ;Escape?

; Display keyword choices and reparse if ambiguous  ;[8] begin
 je cmky3a
 cmp ah, ' '+80H ;Space?
 jne cmky3b
cmky3a: dec cmdptr  ;If so, back up over it.
cmky3b: mov dx, offset cmin02 ;'One of the following:'
 call tcmsgc
 mov bx, cmkptr ;Find beginning of current keyword
 mov cx, cmsiz
cmky3c: dec bx  ;We are 'cl' characters into it
 dec cl
 jnz cmky3c
 inc bx
 mov cmkptr, bx ;Save beginning of keyword
cmky3d: mov dl, tab  ;Precede each keyword with a tab
 call bout
 mov dx,cmkptr ;and display the keyword
 call tmsg
 mov bx, cmkptr ;Move to the next keyword
cmky3e: inc bx
 cmp byte ptr [bx], '$'
 jnz cmky3e
 add bx,4  ;Bypass '$', 2-byte return value, next length
 mov di, cmkptr ;Get previous keyword for comparison
 mov cmkptr, bx ;and save beginning of this keyword
 mov cx, cmsiz ;Get number of characters to match
 dec ch  ;Are we at end of table?
 js cmky3g  ;  Yes, quit displaying
 mov cmsiz, cx
cmky3f: dec cl
 jz cmky3d  ;This keyword also matches to 'cl' places
 mov ah,[bx]  ;Compare this keyword to last
 cmp ah,[di]
 jne cmky3g
 inc di
 inc bx
 jmps cmky3f
cmky3g: jmp cmky50  ;Not equal or end of table, redisplay prompt
        ;[8] end

cmky32: mov cx, cmsiz ;Restore info.
 mov bx, cmkptr ;Our place in the keyword table.
cmk32a: cmp cmchr, ' '+80H ;Space?   ;[8]
 je cmky35
 cmp cmchr, cr+80H ;Carriage return?
 je cmky35
 dec cmcptr  ;Pointer into buffer of input.
 mov dx, cmcptr
cmky33: mov ah, [bx] ;Get next char in keyword.
 cmp ah, '$'  ;Are we done yet?
 jz cmky34
 mov di,dx
 mov [di], ah
 inc bx
 inc dx
 inc cmccnt
 jmp cmky33
cmky34: push bx  ;Save pointer to return value ;[8]
 mov ah, ' '
 mov di, dx
 mov [di], ah ;Put a blank in the buffer.
 inc dx
 mov cmdptr, dx      ;[8] begin
 cmp cmchr, '?'+80H ;Question mark?
 jne cmk34a
 mov ah, '?'
 mov di,dx
 mov [di], ah
 inc dx
 inc cmccnt
 push dx
 mov dl, 08H  ;Erase question mark from display
 call bout
 pop dx
cmk34a: mov cx, cmcptr ;Remember where we were (for printing below).
 mov cmcptr, dx ;Update our pointers.  ;[8] end
 mov ah, '$'
 mov di, dx
 mov [di], ah ;Add '$' for printing.
 mov dx, cx  ;Point to beginning of filled in data.
 call tmsg
 pop bx  ;Recover pointer to return value ;[8]
 inc bx  ;Point to address we'll need.
 mov bx, [bx]
 cmp cmchr, 0BFH ;Question mark?  ;[8] begin
 je cmk34b
 mov cmaflg, 0  ;If esc, turn off action flag
 mov cmsflg, 0FFH ;   and pretend they typed a space
cmk34b: jmp rskp      ;[8] end

cmky35: mov ah, [bx] ;Find end of keyword.
 inc bx
 cmp ah, '$'
 jne cmky35
 mov bx, [bx] ;Address of next routine to call.
 jmp rskp

cmky38: cmp ah, al
 jne cmky6  ;Go to end of keyword and try next.
 jmp cmky3

cmky4: and ah, 7FH  ;Turn off minus bit.
 cmp ah, '?'  ;Need help?
 je cmky5
 cmp ah, ' '  ;Just a space - no error.
 je cmky51
 cmp ah, cr
 je cmky51
 cmp ah, esc  ;Ignore escape?
 je cmky43
cmky41: mov dx, offset cmer03
 call tcrmsg
 jmp prserr  ;Parse error - give up.

cmky43: mov dl, bell ;Ring a bell.
 call bout
 mov bx, cmcptr
 dec bx
 mov cmcptr, bx
 mov cmdptr, bx
 dec cmccnt  ;Don't count the escape.
 mov cmaflg, 0 ;Reset action flag.
 inc ch  ;Account for a previous 'dec'.
 jmp cmky1  ;Start over.

cmky5: mov dx,cmhlp ;Print the help text.
 call tcmsgc
cmky50: call reprompt ;Reprint the prompt  ;[32a]
 mov bx,cmdptr ;Get pointer into buffer.
 mov al, '$'
 mov [bx], al ;Add dollar sign for printing.
 mov dx, offset cmdbuf
 call tmsg
 mov bx, cmdptr ;[8] begin
 mov cmcptr, bx
 mov dx, offset cmdbuf
 sub bx, dx
 mov cmccnt, bl
 mov cmaflg, 0 ;Turn off the action flag.
 jmp repars

cmky51: jmp prserr

cmky6: inc bx  ;Find end of keyword.
 mov al, [bx]
 cmp al, '$'
 jne cmky6
 add bx, 3  ;Beginning of next command.
 mov dx, cmsptr ;Get old cmdptr.
 mov cmdptr, dx ;Restore.
 mov cmsflg, 0FFH
 jmp cmky1  ;Keep trying.

cmky7: call cmgtch  ;Get char.
 cmp ah, 0
 js cmky71  ;Ok if a terminator.
 dec bx
 jmp cmky6  ;No match - try next keyword.
cmky71: mov cmchr, ah      ;[8] begin
 jmp cmk32a


; See if keyword is ambiguous from what the user has typed in.

cmambg: cmp ch, 0  ;Any keywords left to check?
 jne cmamb0
 ret   ;If not then not ambiguous.
cmamb0: inc bx  ;Go to end of keyword ...
 mov al, [bx] ;So we can check the next one.
 cmp al, '$'
 jne cmamb0
 add bx, 4  ;Point to start of next keyword.
 dec cl  ;Don't count escape.
 mov dx, cmsptr ;Buffer with input typed by user.
cmamb1: mov ah, [bx] ;Keyword char.
 mov di, dx
 mov al, [di] ;Input char.
 cmp al, 'a'  ;Do capitalizing.
 jl cmam11
 cmp al, 'z'+1
 jns cmam11
 and al, 137O
cmam11: cmp ah, al  ;Keyword bigger than input (alphabetically)?
 jle cmamb2  ;No - keep checking.
 ret   ;Yes - not ambiguous.
cmamb2: inc bx  ;Advance one char.
 inc dx
 dec cl
 jnz cmamb1
 jmp rskp  ;Fail - it's ambiguous.

; Parse an input file spec.

cmifil: mov wldflg, 0 ;Set to no wildcards.  ;[9]
 mov bx, dx  ;Get the fcb address in bx.
 mov cmfcb, bx ;Save it.
 mov ch, 0  ;Initialize char count.
 mov ah, 0
 mov [bx], ah ;Set the drive to default to current.
 inc bx
 mov cmfcb2, bx
 mov cl, ' '
cmifi0: mov [bx], cl ;Blank the FCB.
 inc bx
 inc ah
 cmp ah, 0BH  ;Twelve?
 jl cmifi0
cmifi1: call cmgtch  ;Get another char.
 cmp ah, 0  ;Is it an action character.
 jns cmifi2
 and ah, 7FH  ;Turn off the action bit.
 cmp ah, '?'  ;A question mark?
 jne cmif12
 mov cmaflg, 0 ;Blank the action flag.
; '?' is a legal character in wildcard filenames.  ;[9] begin
; Make ESC take its place by giving info instead of beeping. ;[32b]
 mov wldflg, 0FFH ;Say we have a wildcard.
 inc cmdptr
 jmp cmifi8  ;Accept a '?'
cmif12: cmp ah, esc  ;An escape?
 jne cmif13
 dec cmdptr
cmf12a: mov cmaflg, 0 ;Turn off the action flag ;[9] end
 dec cmcptr  ;Decrement the buffer pointer.
 dec cmccnt  ;Decrement count.
 mov dx, offset cmin01 ;Help message.
 call tmsg
 call reprompt ;Reprint the prompt  ;[32a]
 mov bx, cmdptr
 mov al, '$'
 mov [bx], al ;Put in dollar sign for printing.
 mov dx, offset cmdbuf
 call tmsg
 jmp repars

cmif13: mov ah, ch  ;It must be a terminator.
; The check for 0-length filenames will be performed by the ;[32c]
; caller so as to allow the file specification to be optional.
 cmp ah, 0DH
 js cmf3y
 jmp cmifi9  ;If too long complain.
cmf3y: jmp rskp  ;Otherwise we have succeeded.
cmifi2: cmp ah, '.'
 jne cmifi3
 inc ch
 mov ah, ch
 cmp ah, 1H  ;Any chars yet?
       jnz cmf2x
 jmp cmifi9  ;No, give error.
cmf2x: cmp ah, 0AH  ;Tenth char?
       js cmf2y
 jmp cmifi9  ;Past it, give an error.
cmf2y: mov dl, 9H
 mov dh, 0
 mov bx, cmfcb
 add bx, dx  ;Point to file type field.
 mov cmfcb2, bx
 mov ch, 9H  ;Say we've gotten nine.
 jmp cmifi1  ;Get the next char.
cmifi3: cmp ah, ':'
 jne cmifi4
 inc ch
 cmp ch, 2H  ;Is it in right place for a drive?
 je cmif3x
 jmp cmifi9  ;If not, complain.
cmif3x: mov ch, 0  ;Reset char count.
 mov bx, cmfcb2
 dec bx
 mov ah, [bx] ;Get the drive name.
 cmp ah,'A'  ;Make sure it's in range A-P ;[9] begin
 jb cmif3y
 cmp ah,'P'
 jbe cmif3z
cmif3y: jmp cmifi9
cmif3z: sub ah,'@'  ;Get the drive number.  ;[9] end
 mov cmfcb2, bx
  mov bx, cmfcb
 mov [bx], ah ;Put it in the fcb.
 jmp cmifi1
cmifi4: cmp ah, '*'
 jne cmifi7
 mov ah, ch
 cmp ah, 8H  ;Is this in the name or type field?
 jz cmifi9  ;If its where the dot should be give up.
 jns cmifi5  ;Type.
 mov cl, 8H  ;Eight chars.
 jmp cmifi6
cmifi5: mov cl, 0CH  ;Three chars.
cmifi6: mov wldflg, 0FFH ;Remember we had a wildcard.
 mov bx, cmfcb2 ;Get a pointer into the FCB.
 mov ah, '?'
 mov [bx], ah ;Put a question mark in.
 inc bx
 mov cmfcb2, bx
 inc ch
 mov ah, ch
 cmp ah, cl
 jl cmifi6  ;Go fill in another.
 jmp cmifi1  ;Get the next char.
cmifi7:

cmif7x: cmp ah,'0'
 jb cmif8x
 cmp ah,'9'
 jbe cmifi8
 cmp ah,'A'
 jb cmif8x
 cmp ah,'Z'
 jbe cmifi8
 cmp ah,'a'
 jb cmif8x
 cmp ah,'z'
 ja cmif8x      ;[9] end
 and ah, 137O ;Capitalize.
cmifi8: mov bx, cmfcb2 ;Get the pointer into the FCB.
 mov [bx], ah ;Put the char there.
 inc bx
 mov cmfcb2, bx
 inc ch
 jmp cmifi1

cmif8x: push es  ;Check list of special characters
 mov cx, ds  ;   which are legal in filenames
 mov es, cx  ;Scan uses ES register.
 mov di, offset spchar ;Special chars.
 mov cx, 20  ;Twenty of them.
 mov al, ah  ;Char is in al.
 repnz scasb  ;Search string for input char.
 cmp cx, 0  ;Was it there?
 pop es
 jnz cmifi8

cmifi9: mov dx, offset cmer02
 call tcrmsg
 ret

cmofil: jmp cmifil  ;For now, the same as CMIFI.

; Parse arbitrary text up to a CR.  Put chars into data buffer sent to
; the host (pointed to by BX).  Return updated pointer in BX and
; input size in AH.

cmtext: mov cmptab, bx ;Save pointer to data buffer.
 mov cl, 0  ;Init the char count.
cmtxt1: call cmgtch  ;Get a char.
 cmp ah, 0  ;Terminator?
 jns cmtxt5  ;Nope, put into the buffer.
 and ah, 07FH
 cmp ah, esc  ;An escape?
 jne cmtxt2
 mov dl, bell ;Ring a bell.
 call bout
 mov cmaflg, 0 ;Reset action flag.
 dec cmcptr  ;Move pointer to before the escape.
 dec cmdptr
 dec cmccnt  ;Decrement count.
 jmp cmtxt1  ;Try again.
cmtxt2: cmp ah, '?'  ;Asking a question?
 jz cmtx2y      ;[32b]
 cmp ah, ' '  ;Space?    ;[32b]
 jz cmtxt3
 cmp ah, ff  ;Formfeed?
 jne cmtx2x
 call clrscr
cmtx2x: mov ah, cl  ;Return count in AH.
 mov bx, cmptab ;Return updated pointer.
 jmp rskp
cmtx2y: inc cmdptr      ;[32b]
cmtxt3: mov cmaflg, 0 ;Reset action flag to zero.
cmtxt5: inc cl  ;Increment the count.
 mov bx, cmptab ;Pointer into destination array.
 mov [bx], ah ;Put char into the buffer.
 inc bx
 mov cmptab, bx
 jmp cmtxt1


cminbf: push dx
 push bx
 mov cx, dx  ;Save value here too.
 mov ah, cmaflg ;Is the action char flag set?
 cmp ah, 0
 je cminb1
 jmp cminb9  ;If so get no more chars.
cminb1: inc cmccnt  ;Increment the char count.
 call bin
 mov ah, al  ;Keep char in 'ah'.
 mov bx, cmcptr ;Get the pointer into the buffer.
 mov [bx], ah ;Put it in the buffer.
 inc bx
 mov cmcptr, bx
 cmp ah, 15h  ;Is it a ^U?
 je cmnb12      ;[30b]
 cmp ah, 18h  ;     or ^X?   ;[30b]
 jne cminb2
cmnb12: call clrlin      ;[30c]
 call repmt2  ;Reprint the prompt (no crlf) ;[32a]
 mov bx, offset cmdbuf
 mov cmcptr, bx ;Reset the point to the start.
 mov cmccnt, 0 ;Zero the count.
 mov dx, cx  ;Preserve original value of dx.
 jmp repars  ;Go start over.
cminb2: cmp ah, 08h  ;Is it a backspace?  ;[30b]
 jz cminb3
 cmp ah, 7fh  ;     or delete?  ;[30b]
 jne cminb4
 mov dx, offset delstr
 call tmsg
cminb3: mov ah, cmccnt ;Decrement the char count by two.
 dec ah
 dec ah
 cmp ah, 0  ;Have we gone too far?
 jns cmnb32  ;If not proceed.
 mov dl, bell ;Ring the bell.
 call bout
 jmp cmnb12  ;Go reprint prompt and reparse.
cmnb32: mov cmccnt, ah ;Save the new char count.
 mov dx, offset clrspc ;Erase the character.
 call tmsg
 mov bx, cmcptr ;Get the pointer into the buffer.
 dec bx  ;Back up in the buffer.
 dec bx
 mov cmcptr, bx
 jmp repars  ;Go reparse everything.
cminb4: cmp ah, '?'  ;Is it a question mark.
 jz cminb6
 cmp ah, esc  ;Is it an escape?
 jz cminb6
 cmp ah, cr  ;Is it a carriage return?
 jz cminb5
 cmp ah, lf  ;Is it a line feed?
 jz cminb5
 cmp ah, ff  ;Is it a formfeed?
 jne cminb7
 call clrscr
cminb5: mov ah, cmccnt ;Have we parsed any chars yet?
 cmp ah, 1
 jnz cminb6
 jmp prserr  ;If not, just start over.
cminb6: mov cmaflg, 0FFH ;Set the action flag.
 jmp cminb9
cminb7: jmp cminb1  ;Get another char.

cminb9: pop bx
 pop dx
 ret

cmgtch: push cx
 push bx
 push dx
cmgtc1: mov ah, cmaflg
 cmp ah, 0  ;Is it set.
 jne cmgt10
 call cminbf  ;If the action char flag is not set get more.
cmgt10: mov bx, cmdptr ;Get a pointer into the buffer.
 mov ah, [bx] ;Get the next char.
 inc bx
 mov cmdptr, bx
 cmp ah, ' '  ;Is it a space?
 jz cmgtc2
 cmp ah, tab  ;Or a tab?
 jne cmgtc3
cmgtc2: mov ah, cmsflg ;Get the space flag.
 cmp ah, 0  ;Was the last char a space?
 jne cmgtc1  ;Yes, get another char.
 mov cmsflg, 0FFH ;Set the space flag.
 mov ah, ' '
 pop dx
 pop bx
 jmp cmgtc5
cmgtc3: mov cmsflg, 0 ;Zero the space flag.
 pop dx
 pop bx
 cmp ah, esc
 jz cmgtc5
 cmp ah, '?'  ;Is the user curious?
 jz cmgtc4
 cmp ah, cr
 jz cmgtc4
 cmp ah, lf
 jz cmgtc6      ;[8]
 cmp ah, ff
 je cmgtc6      ;[8]
 pop cx
 ret   ;Not an action char, just return.
cmgtc6: mov ah, cr  ;Convert lf & ff to cr  ;[8]
cmgtc4: dec cmdptr
cmgtc5: or ah, 80H  ;Make the char negative to indicate
 pop cx  ;it is a terminator.
 ret

; Parse a single character ;[25] start
; this is for setting the escape character

cmchar:
 call cmgtch  ;get a char
 cmp ah, 0
 jns cmchr1  ;go if not negative
 and ah, 7FH  ;turn off sign bit
 cmp ah, '?'  ;user curious?
 jne cmchr0  ;no - an error
 mov dx, bx  ;help string pointer was in bx
 call tmsg  ;print help stuff  ;[32a]
 call reprompt ;Reprint the prompt  ;[32a]
 mov bx, cmdptr
 mov al, '$'
 mov [bx], al ;add a "$" to what was typed
 mov dx, offset cmdbuf
 call tmsg  ;type it again
 dec cmcptr  ;but don't leave "$" ..
 dec cmccnt  ;in buffer
 mov cmaflg, 0 ;turn off action flag
 jmp repars  ;try again
cmchr0: mov dx, offset erms20
 call tcrmsg  ;"illegal value" error
 ret
cmchr1: mov temp, ax
 call cmcfrm  ;get a confirm
  jmp cmchr0  ;or else complain
 mov ax, temp
 mov bl, ah  ;return the character
 jmp rskp

; parse a (decimal) number.  Maximum allowed value in dx
cmnumr:
 mov temp1, 001H ;initial multiplier of 1
 mov temp2, dx ;storage for maximum
 mov temp, 0  ;zero running sum
 call cmgtch  ;get a char
 cmp ah, 0
 jns cmnum1  ;go if not negative
 and ah, 7FH  ;turn off sign bit
 cmp ah, '?'  ;user curious?
 jne cmnum0  ;no - an error
 mov dx, bx  ;help string pointer was in bx
 call tmsg  ;print help stuff  ;[32a]
 call reprompt ;Reprint the prompt  ;[32a]
 mov bx, cmdptr
 mov al, '$'
 mov [bx], al ;add a "$" to what was typed
 mov dx, offset cmdbuf
 call tmsg  ;type it again
 dec cmcptr  ;but don't leave "$" ..
 dec cmccnt  ;in buffer
 mov cmaflg, 0 ;turn off action flag
 jmp repars  ;try again
 call cmcfrm  ;get character (or confirm)
  jmp cmnum1  ;got a character
    ;fall through - too early for confirm
cmnum0: mov dx, offset erms20
 call tcrmsg  ;"illegal value" message
 ret
cmnum1: sub ah, 030H ;ASCII -> binary
 jl cmnum0  ;too small
 cmp ah, 09H
 jg cmnum0  ;too big
 mov bl, ah
 mov bh, 0  ;get number in low part of bx
 mov ax, temp ;get running sum
 mul temp1  ;multiply by decimal place value
 add ax, bx  ;add in this digit
 cmp ax, temp2 ;over the maximum
 jg cmnum0  ;yes - error
 mov temp, ax ;save running sum
 mov ax, temp1 ;get multiplier
 mul cmten  ;multiply multiplier by 10
 mov temp1, ax ;save it
 call cmcfrm  ;get another character
  jmp cmnum1  ;not terminator - process it
 mov bx, temp ;get value of number
 jmp rskp  ;return success
      ;[25] end
