; * * * * * * * * * * * * * * * 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 DSEG $ ; Resume the data segment. ; COMND storage. cmer00 db '?Program error -- Invalid COMND call$' cmer01 db '?Ambiguous command$' cmer02 db '?Illegal input file spec$' cmer03 db '?Unrecognized instruction$' cmer04 db '?Invalid command or operand$' cmin00 db ' Confirm with carriage return$' cmin01 db ' Input file spec (possibly wild) $' cmin02 db ' One of the following:$' ;[8] cmcrlf db cr,lf,'$' 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. spchar db '$%&_0000000000000000' ; Special chars valid in filenames ;[8] ; 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. lea bx, cmdbuf mov cmcptr, bx ; Initialize the command pointer. mov cmdptr, bx mov cmaflg, 0 ; Zero the flags. mov cmccnt, 0 mov cmsflg, 0FFH mov dx, cmprmp ; Print the prompt. call tcrmsg ret ; This address is jumped to on reparse. repars: mov sp, cmostp ; new sp <-- old sp lea bx, 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 sp, cmostp ; Set new sp to old one. lea bx, cmdbuf mov cmcptr, bx ; Initialize the command pointer. mov cmdptr, bx mov cmaflg, 0 ; Zero the flags. mov cmccnt, 0 mov cmsflg, 0FFH mov dx, cmprmp ; Get the prompt. call tcrmsg 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: lea dx, cmer00 ; "?Unrecognized COMND call" 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 lea dx, cmin00 ; Print something useful. call tmsg mov dx, cmprmp ; Reprint the prompt. call tcrmsg 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 lea dx, 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, 9BH ; Escape Recognition (escape w/minus bit on)? je cmky3y cmp ah, 0BFH ; A question mark? je cmky3y cmp ah, 0A0H ; A space? je cmky3y cmp ah, 8DH ; 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, 9BH ; Escape? ; Display keyword choices and reparse if ambiguous ;[8] begin je cmky3a cmp ah, 0A0H ; Space? jne cmky3b cmky3a: dec cmdptr ; If so, back up over it. cmky3b: lea dx, 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, 0A0H ; Space? ;[8] je cmky35 cmp cmchr, 08DH ; 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, 0BFH ; 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: lea dx, 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 tmsg cmky50: mov dx, cmprmp ;[8] call tcrmsg mov bx,cmdptr ; Get pointer into buffer. mov al, '$' mov [bx], al ; Add dollar sign for printing. lea dx, cmdbuf call tmsg mov bx, cmdptr ;[8] begin mov cmcptr, bx lea dx, 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. If in ;[9] begin ; first position then give info, else accept it as is. ; Make ESC take its place by giving info instead of beeping cmp ch,0 ; If first, then give info je cmf12a 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. lea dx, cmin01 ; Help message. call tmsg mov dx, cmprmp call tcrmsg mov bx, cmdptr mov al, '$' mov [bx], al ; Put in dollar sign for printing. lea dx, cmdbuf call tmsg jmp repars cmif13: mov ah, ch ; It must be a terminator. cmp ah, 0 ; Test the length of the file name. jnz cmf3x jmp cmifi9 ; If zero complain. cmf3x: 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. lea di, 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: lea dx, 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 cmtxt3 cmp ah, ff ; Formfeed? jne cmtx2x call cmblnk cmtx2x: mov ah, cl ; Return count in AH. mov bx, cmptab ; Return updated pointer. jmp rskp 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, 25O ; Is it a ^U? jne cminb2 cmnb12: lea dx, clrlin call tmsg mov dx, cmprmp ; Print the prompt. call tmsg lea bx, 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, 10O ; Or backspace? jz cminb3 cmp ah, del ; Delete? jne cminb4 lea dx, 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. lea dx, 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 cmblnk 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 ; This routine blanks the screen. cmblnk: lea dx, clrscr ; Want to clear the screen. call tmsg ; Do it. ret