; 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) $' 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. cmwld db 0 ; Flag for wildcarding. ; 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 ; Decremrnt 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: cmp ah, ff ; Is it a form feed? jne cmcfr4 call cmblnk ; If so blank the screen. 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 ;* lea dx, cmer04 ; Complain and fail if not. ;* call tcrmsg 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? je cmky3z jmp cmky41 ; Fail. cmky3z: mov dl, bell ; Else, ring a bell. call bout mov bx, cmcptr ; Move pointer to before the escape. dec bx mov cmcptr, bx mov cmdptr, bx dec cmccnt ; Decrement char count. mov bx, cmkptr ; Failed - pretend user never typed .... mov cx, cmsiz ; ... in a char. dec cl ; Don't count the escape. dec bx mov cmaflg, 0 ; Reset the action flag. jmp cmky3 ; Keep checking. cmky32: mov cx, cmsiz ; Restore info. mov bx, cmkptr ; Our place in the keyword table. cmp cmchr, 0A0H ; Space? je cmky35 cmp cmchr, 0BFH ; Question mark? je cmky35 cmp cmchr, 8DH ; 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: mov ah, ' ' mov di, dx mov [di], ah ; Put a blank in the buffer. inc dx mov cx, cmcptr ; Remember where we were (for printing below). mov cmcptr, dx ; Update our pointers. mov cmdptr, dx mov ah, '$' mov di, dx mov [di], ah ; Add '$' for printing. mov dx, cx ; Point to beginning of filled in data. call tmsg inc bx ; Point to address we'll need. mov bx, [bx] mov cmaflg, 0 ; Turn off action flag. jmp rskp cmky35: inc bx mov ah, [bx] ; Find end of keyword. cmp ah, '$' jne cmky35 inc bx mov bx, [bx] ; Address of next routine to call. ;? mov cmaflg,0 ; Zero the action flag. 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, tab 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 mov dx, cmprmp call tcrmsg mov bx,cmdptr ; Get pointer into buffer. mov al, '$' mov [bx], al ; Add dollar sign for printing. lea dx, cmdbuf call tmsg dec cmcptr ; Don't keep it in the buffer. dec cmccnt ; Don't conut it. 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: inc bx ; Get necessary data. mov bx, [bx] cmp ah, 9BH ; An escape? jne cmky72 mov dl, ' ' ; Print a space. call bout mov di, cmcptr dec di mov ah, ' ' mov [di], ah ; Replace escape char with space. mov cmaflg, 0 mov cmsflg, 0FFH ; Pretend they typed a space. cmky72: jmp rskp ; 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. ;******* Begin questionable code ********* cmifil: 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. dec cmcptr ; Decrement the buffer pointer. dec cmccnt ; Decrement count. ;? jmp cmifi8 ; Treat like any other character. 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 cmif12: cmp ah, esc ; An escape? jne cmif13 mov cmaflg, 0 ; Turn off the action flag. mov dl, bell ; Ring the bell. call bout mov bx, cmcptr ; Move the pointer to before the escape. dec bx mov cmcptr, bx mov cmdptr ,bx dec cmccnt ; Decrement char count. 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. sub ah,'@' ; Get the drive number. 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 cmwld, 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: cmp ah, 03DH ; Equals sign (wildcard)? jne cmif7x mov ah, '?' mov cmwld, 0FFH ; Say we have a wildcard. jmp cmifi8 ; Put into FCB. cmif7x: cmp ah, '0' jl cmif8x cmp ah, 'z'+1 jns cmif8x cmp ah, 'A' ; Don't capitalize non-alphabetics. jl cmifi8 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 mov cx, ds 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 cmgtc4 cmp ah, ff je cmgtc4 pop cx ret ; Not an action char, just return. 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