; CP4CMD.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 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 '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 'CP4CMD.ASM (5) 6-Feb-85$' ; 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 comnd: sta cmstat ;Save what we are presently parsing. call cminbf ;Get chars until an action or a erase char. 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. 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: 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 cmtxt5 ;No, put in user space. 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 cmtxt3 ;If so put it in the text. 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. cmtxt3: lxi h,cmaflg ;Point to the action flag. mvi m,0 ;Set it to zero. cmtxt5: 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. jmp cmtxt1 ;Get another char. ; ; 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. 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 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? jm cmifi0 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. 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. call bdos 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 call delchr ;Print the delete string. 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. call clrspc ;Erase the character. 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 cminb1 ;no - just store it and get another character. call clrtop cminb5: 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 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: lhld cmprmp ;Get the prompt. xchg call prtstr ret IF lasm LINK CP4UTL ENDIF ;lasm [Toad Hall]