NAME ccsscp ; File CCSSCP.ASM ;CHINESE ifdef MSDOS include mssscp.dat else include ccsscp.dat endif code segment public 'code' extrn comnd:near, clrbuf:near, prtchr:near, outchr:near, sendbr:near extrn cptchr:near, serini:near, serrst:near, pcwait:near, katoi:near extrn cnvstr:near, getmodem:near, isdev:near, isfile:near extrn takrd:near, takclos:near, tolowr:near, prtasz:near,strlen:near assume cs:code, ds:datas, es:nothing ; Clear input buffer(s) of serial port ; Clear command ; SCCLR PROC NEAR mov kstatus,0 ; global status mov ah,cmcfm ; get a confirm call comnd jmp r ; no confirm nop call bufclear ; clear our serial port circular buf call clrbuf ; clear system serial port buffer too jmp rskp ; return success SCCLR ENDP ; ; Echo a line of text to our screen ; Echo text ; SCECHO PROC NEAR mov ah,cmtxt ; get a whole line of asciiz text mov bx,offset line ; where to store in mov word ptr [bx],0 ; clear line ; mov dx,offset echhlp ; help mcmsg echhlp,cechhlp call comnd jmp rskp ; ignore parse error if no text nop mov si,offset line ; start of line mov di,si ; convert to the same place mov ah,script.incasv ; save current case state push ax mov script.incasv,0ffh ; say no case conversion call cnvlin ; convert \numbers to binary pop ax mov script.incasv,ah ; recover case state jc echo3 ; carry set means error mov al,lf ; start with a linefeed call scdisp ; show it jcxz echo2 ; z = nothing to show echo1: push cx ; save loop counter cld lodsb ; get a source char into al push si call scdisp ; display the char pop si pop cx loop echo1 ; get another echo2: jmp rskp ; return success echo3: ret ; error SCECHO ENDP ; Extract label from command line. Store in LINE, length in slablen. ; Jump to line in Take or Macro following that label. GOTO PROC NEAR mov kstatus,0 ; global status mov ah,cmfile ; get a word mov dx,offset line ; buffer to hold label mov bx,0 ; no help (non-interactive command) mov slablen,bx ; clear label holding buffer mov comand.cmkeep,1 ; keep Take/macro open after EOF call comnd jmp r nop mov al,ah ; byte count mov ah,0 mov slablen,ax ; save count here mov comand.cmkeep,1 mov ah,cmcfm call comnd jmp r nop cmp taklev,0 ; in a Take file or Macro? jne GETTO ; ne = yes, find the label jmp rskp ; ignore interactive command ; Find line starting just after ":label". Label is in variable LINE ; (length in slablen). Readjust Take read pointer to start of that line. ; Performs file search from beginning of file. ; Exit carry clear if success, carry set otherwise. Local worker routine. getto proc near push bx ; global save of bx mov bx,takadr cmp [bx].taktyp,0ffh ; get type of take (a Macro?) je gett2 ; e = yes, a macro ; scan from start of Take file mov eolchr,LF ; file lines end on LF mov bx,[bx].takhnd ; rewind the file mov cx,0 mov dx,0 mov al,0 ; zero displacement from start of file mov ah,lseek int dos jnc gett1 jmp gett20 ; c = failure gett1: call takrd ; get a buffer of data mov bx,takadr ; restore bx to working value jmp short gett4 ; Take a Macro gett2: mov eolchr,CR ; Macro lines end on CR mov ax,[bx].takptr ; offset of next char to be read sub ax,[bx].takbuf ; offset of start of buffer dec ax ; omit count byte at start of buffer add [bx].takcnt,ax ; original length of text in buffer mov ax,[bx].takbuf inc ax ; omit count byte at start of buffer mov [bx].takptr,ax ; set read pointer to beginning gett4: call getch ; get a character jc gett14 ; c = end of file, no char cmp al,':' ; start of label? je gett8 ; e = yes gett6: cmp al,eolchr ; end of line? je gett4 ; e = yes, seek colon for label call getch ; get a character jc gett14 ; c = end of file, no char jmp short gett6 ; read until end of line gett8: mov si,offset line ; label to search for mov cx,slablen ; its length jcxz gett12 ; no chars to match cmp byte ptr[si],':' ; user label starts with colon jne gett10 ; ne = no inc si ; skip user's colon dec cx jcxz gett12 ; no chars to match gett10: call getch ; read file char into al jc gett14 ; c = end of file mov ah,al cld lodsb call tolowr ; convert al and ah to lower case cmp al,ah ; match? jne gett6 ; ne = no, goto end of line loop gett10 ; continue matching ; match obtained call getch ; read next file character jc gett13 ; c = end of file, no char cmp al,' ' ; separator? je gett12 ; e = yes, unique label found cmp al,eolchr ; or end of line? je gett13 ; e = yes cmp al,CR ; macro eol, also file start eol pair je gett12 ; e = found match jmp gett6 ; longer label than target gett12: call getch ; read past end of line jc gett13 ; c = end of file, no char cmp al,eolchr ; end of line character? jne gett12 ; ne = no, keep reading gett13: pop bx clc ; return carry clear jmp rskp ; Take pointers are ready to read line gett14: mov ah,prstr ; say label not found ; mov dx,offset laberr ; first part of error message mcmsg laberr,claberr int dos mov dx,offset line cmp line,':' ; label starts with ":"? jne gett15 ; ne = no inc dx ; yes, skip it gett15: call prtasz ; print asciiz string mov ah,prstr ; mov dx,offset laberr2 ; trailer of error message mcmsg laberr2,claberr2 int dos gett20: pop bx mov kstatus,1 ; command status, failure stc ; set carry for failure ret getto endp GOTO ENDP ; Read char from Take buffer. Returns carry clear and char in al, or if end ; of file returns carry set. Enter with bx holding takadr. Local worker. getch proc near cmp [bx].takcnt,0 ; buffer empty? jg getch2 ; g = no cmp [bx].taktyp,0ffh ; macro? je getch1 ; e = yes, that's the end call takrd ; read another buffer cmp [bx].takcnt,0 ; end of file? jne getch2 ; ne = no getch1: stc ; e = yes, exit error ret getch2: push si mov si,[bx].takptr ; read a char from Take buffer cld lodsb mov [bx].takptr,si ; move buffer pointer pop si dec [bx].takcnt ; decrease number of bytes remaining clc ; return carry clear ret getch endp ; IF [NOT] {ALARM | COUNT | FAILURE | SUCCESS | ERRORLEVEL \number ; | EQUAL string string | EXIST filespec} command IFCMD PROC NEAR mov notflag,0 ; assume NOT keyword is absent ifcmd1: mov ah,cmkey ; parse keyword mov dx,offset iftable ; table of keywords mov bx,0 ; help is the table call comnd jmp r nop cmp bx,ifnot ; NOT keyword? jne ifcmd2 ; ne = no xor notflag,1 ; toggle not flag jmp short ifcmd1 ; and get next keyword ifcmd2: cmp bx,ifsuc ; IF SUCCESS? jne ifcmd4 ; ne = no cmp kstatus,0 ; do we have success? jne ifcmdf ; ne = no, no jump jmp ifcmdp ; yes ifcmd4: cmp bx,iferr ; IF ERRORLEVEL? jne ifcmd5 ; ne = no jmp ifnum ; parse number to binary in line ifcmd5: cmp bx,ifext ; IF EXIST filespec? jne ifcmd6 ; ne = no mov ah,cmfile ; read a filespec mov dx,offset line ; buffer for filespec mov bx,0 call comnd jmp r nop mov ax,offset line ; isfile wants pointer in ds:ax call isfile ; see if file exists jc ifcmdf ; c = no jmp short ifcmdp ; yes, do following command ifcmd6: cmp bx,iffail ; IF FAIL? jne ifcmd7 test kstatus,not (0) ; check all bits jz ifcmdf ; z = not that condition, no jump jmp short ifcmdp ifcmd7: cmp bx,ifctr ; IF COUNT? jne ifcmd8 ; ne = no cmp taklev,0 ; in a Take file? je ifcmdf ; e = no, fail push bx mov bx,takadr ; current Take structure cmp [bx].takctr,0 ; exhausted count? je ifcmd7a ; e = yes, dec no more ye counter dec [bx].takctr ; dec COUNT if non-zero cmp [bx].takctr,0 ; exhausted now? je ifcmd7a ; e = yes pop bx jmp short ifcmdp ; COUNT > 0 at entry, execute command ifcmd7a:pop bx jmp short ifcmdf ; do not execute command ifcmd8: cmp bx,ifmdf ; IF DEF? jne ifcmd9 ; ne = no jmp ifmdef ; do further parsing below ifcmd9: cmp bx,ifalarm ; IF ALARM? jne ifcmd10 ; ne = no jmp ifalrm ; do further parsing below ifcmd10:cmp bx,ifequal ; IF EQUAL? jne ifcmdf ; ne = no jmp ifequ ; do further parsing below ; Jump points for worker routines ; failure ifcmdf: cmp notflag,0 ; need to apply not condition? jne ifcmdp2 ; ne = yes, take other exit ifcmdf2:mov ah,cmtxt ; fail, read and discard rest of line mov bx,offset line ; mov dx,offset discard ; say not doing anything mcmsg discard,cdiscard call comnd nop nop nop jmp rskp ; success (pass) ifcmdp: cmp notflag,0 ; need to apply not condition? jne ifcmdf2 ; ne = yes, take other exit ifcmdp2:jmp rskp ; do command IFCMD ENDP ; Compare errlev against user number. Jump successfully if errlev >= number. ; Worker for IF [NOT] ERRORLEVEL number ifnum proc near mov ah,cmfile ; get following number mov dx,offset line+1 mov line,'\' ; in case user forgets backslash mov word ptr line+1,0 ; clear buffer ; mov bx,offset ifnhlp ; help mcmsgb ifnhlp,cifnhlp call comnd jmp r nop mov si,offset line ; put text in compare buffer cmp line+1,'\' ; did user include backslash? jne ifnum2 ; ne = no inc si ; yes, skip our helpful backslash ifnum2: call katoi ; convert number to binary in ax jc ifnum4 ; c = failed to convert a number cmp errlev,al ; at or above this level? jae ifnum3 ; ae = yes, succeed jmp ifcmdf ; else fail ifnum3: jmp ifcmdp ; jump to main command Success exit ifnum4: mov dx,offset line+1 ; pointer to bad word mov tempd,dx ; remember starting place for text call strlen ; get its length add dx,cx ; skip over current word mov bx,dx mov byte ptr [bx],' ' ; space, chopped by parser inc bx ; new text goes here mov dx,0 ; help mov ah,cmtxt ; read rest of line call comnd jmp r nop cmp ah,0 ; returned byte count ja ifnum5 ; a = got some mov byte ptr[bx-1],0 ; remove space separator from above ifnum5: mov ah,prstr ; mov dx,offset ifnmsg ; error message header mcmsg ifnmsg,cifnmsg int dos mov dx,offset line+1 ; start of user text call prtasz ; display asciiz string mov ah,prstr mov dx,offset ifnmsg2 ; trailer of message int dos jmp ifcmdf ; jump to main command Failure exit ifnum endp ; Process IF [NOT] DEF ifmdef proc near mov dx,offset line+1 ; point to work buffer ; mov bx,offset ifdfhlp ; help mcmsgb ifdfhlp,cifdfhlp mov ah,cmfile ; get macro name mov comand.cmper,1 ; do not react to \%x call comnd jmp r nop mov line,ah ; store length in buffer ifmde2: mov bx,offset mcctab+1 ; table of macro keywords mov tempd,0 ; tempd = current keyword cmp byte ptr [bx-1],0 ; any macros defined? je ifmde9 ; e = no, failure, exit now ; match table keyword and user word ifmde3: mov si,offset line ; pointer to user's cnt+name mov cl,[si] ; length of user's macro name xor ch,ch inc si ; point to macro name cmp cl,[bx] ; compare length vs table keyword jne ifmde7 ; ne = not equal lengths, try another push si ; lengths match, how about spelling? push bx inc bx ; point at start of keyword ifmde4: mov ah,[bx] ; keyword char mov al,[si] ; new text char cmp al,'a' ; map lower case to upper jb ifmde5 cmp al,'z' ja ifmde5 sub al,'a'-'A' ifmde5: cmp al,ah ; test characters jne ifmde6 ; ne = no match inc si ; move to next char inc bx loop ifmde4 ; loop through entire length ifmde6: pop bx pop si jcxz ifmde10 ; z: cx = 0, found the name ; select next keyword ifmde7: inc tempd ; number of keyword to test next mov cx,tempd cmp cl,mcctab ; all done? Recall, tempd starts at 0 jae ifmde9 ; ae = yes, no match ifmde8: mov al,[bx] ; cnt (keyword length from macro) xor ah,ah add ax,4 ; skip over '$' and two byte value add bx,ax ; bx = start of next keyword slot jmp short ifmde3 ; do another comparison ifmde9: jmp ifcmdf ; jump to main command Failure exit ifmde10:jmp ifcmdp ; jump to main command Success exit ifmdef endp ; IF [not] ALARM hh:mm:ss command ifalrm proc near call chkkbd ; check keyboard for override test status,stat_cc ; Control-C? jz ifalr1 ; z = no ret ; yes, return failure now ifalr1: push word ptr timhms push word ptr timhms+2 ; save working timeouts mov ax,word ptr alrhms mov word ptr timhms,ax mov ax,word ptr alrhms+2 mov word ptr timhms+2,ax ; set alarm value call chktmo ; check for timeout pop word ptr timhms+2 ; restore working timeouts pop word ptr timhms test status,stat_tmo ; tod past user time (alarm sounded)? jnz ifalr4 ; nz = yes, succeed ; failure (not at alarm time yet) ifalr2: cmp notflag,0 ; need to apply not condition? jne ifalr5 ; ne = yes, take other exit ifalr3: mov ah,cmtxt ; fail, read and discard rest of line mov bx,offset line mov dx,0 call comnd jmp r nop jmp rskp ; success (at or past alarm time) ifalr4: cmp notflag,0 ; need to apply not condition? jne ifalr3 ; ne = yes, take other exit ifalr5: jmp rskp ; pass, do command ifalrm endp ; IF [NOT] EQUAL word word command ; Permits use of \number, {string}, @filespec ifequ proc near mov ah,cmfile ; get a word mov dx,offset line ; where to store mov line,0 ; clear first entry ; mov bx,offset ifehlp1 ; help mcmsgb ifehlp1,cifehlp1 call comnd jmp rskp ; ignore parse error if no text nop mov si,offset line ; start of line mov di,si ; convert to the same place call cnvlin ; convert \numbers to binary jc ifequ9 ; carry set means error jcxz ifequ9 ; z = empty word mov tempa,0 ; line length, so far cld ifequ1: lodsb cmp al,' ' ; space or control code? jbe ifequ2 ; be = yes, end of word inc tempa ; count char in word loop ifequ1 ; continue ifequ2: mov byte ptr[si],0 ; plant terminator inc si ; skip null terminator mov temptr,si ; place to start second part mov dx,si mov word ptr[si],0 ; clear second part mov ah,cmfile ; get a word of text ; mov bx,offset ifehlp2 ; help mcmsgb ifehlp2,cifehlp2 call comnd jmp rskp ; ignore parse error if no text nop mov si,temptr ; start of second line mov di,si ; convert to the same place call cnvlin ; convert \numbers to binary jc ifequ9 ; carry set means error jcxz ifequ9 ; z = empty word mov si,temptr ; point at second word mov dx,0 ; word length so far cld ifequ3: lodsb cmp al,' ' ; space or control code? jbe ifequ4 ; be = yes, end of word inc dx ; count char in word loop ifequ3 ; continue ifequ4: mov byte ptr[si],0 ; plant terminator mov cx,dx ; length of second word cmp tempa,dl ; same lengths? jne ifequ9 ; ne = no jcxz ifequ9 ; both are null push es push ds pop es mov si,offset line ; first word mov di,temptr ; second word cld repe cmpsb ; see if they are the same pop es jne ifequ9 ; ne = not the same, fail jmp ifcmdp ; do IF cmd success ifequ9: jmp ifcmdf ; do IF cmd failure ifequ endp ; SET ALARM SETALRM PROC NEAR mov dx,offset line ; point to work buffer mov word ptr line,0 mov word ptr line+2,0 ; mov bx,offset alrmhlp ; help mcmsgb alrmhlp,calrmhlp mov ah,cmfile ; get macro name call comnd jmp r nop mov ah,cmcfm ; get a confirm call comnd jmp r nop push word ptr timhms push word ptr timhms+2 ; save working timeouts mov si,offset line ; source pointer call inptim ; get the timeout time, sets si mov ax,word ptr timhms ; save time in alarm area mov word ptr alrhms,ax mov ax,word ptr timhms+2 mov word ptr alrhms+2,ax pop word ptr timhms+2 ; restore working timeouts pop word ptr timhms jmp rskp SETALRM ENDP ; REINPUT ; Reread material in serial port buffer, seeking a match with user's text ; pattern. If user's pattern is longer than material in buffer then read ; additional characters from the serial port. Use SCINP to do the main work. SCREINP PROC NEAR mov reinflg,1 ; say doing REINPUT, not INPUT jmp short input10 SCREINP ENDP ; Input from port command, match input with text pattern ; Input [timeout] text ; SCINP PROC NEAR mov reinflg,0 ; say doing INPUT, not REINPUT jmp short input10 input10:mov kstatus,0 mov ah,cmtxt ; get a whole line of asciiz text mov bx,offset line ; place to put text ; mov dx,offset inphlp ; help message mcmsg inphlp,cinphlp call comnd ; get the pattern text jmp r ; nothing, complain nop cmp reinflg,0 ; Input command? jne input1 ; ne = no, Reinput cmp taklev,0 ; are we in a Take file? je input0 ; e = no, display linefeed cmp flags.takflg,0 ; are Take commands being echoed? je input1 ; e = no, skip display input0: cmp script.inecho,0 ; Input echo off? je input1 ; e = yes mov al,lf ; next line call scdisp ; display the char input1: call serini ; initialize the system's serial port jc input1a ; c = failure mov status,stat_unk ; clear status flag mov si,offset line ; source pointer call inptim ; get the timeout time, sets si jnc input1b ; nc = legal time value or none input1a:jmp input5 ; else fail on error input1b:mov di,offset line ; put text in compare buffer call cnvlin ; convert \numbers in buf line mov inplen,cx ; cx = number of bytes in final string mov parmsk,0ffh ; parity mask, assume 8 bit data mov di,portval cmp [di].parflg,parnon ; parity is none? je input1c ; e = none mov parmsk,07fh ; else strip parity (8th) bit input1c:mov di,offset line mov temptr,di ; pointer to pattern char mov temptr2,di ; and we need pointer to end of string add temptr2,cx ; offset of end of string ; setup reinput read pointer & count mov ax,bufwtptr ; where next new char goes mov bufpkptr,ax ; set peek-read pointer at oldest char mov bufpkcnt,prtbuflen ; always look back one full buffer ; see if a pattern needs matching cmp inplen,0 ; empty pattern? (cnvlin sets cx=cnt) jne input4 ; ne = not empty cmp reinflg,0 ; Input command? je input3 ; e = yes, read and discard chars jmp input5 ; reinput, just exit timeout ; empty. read, display, and discard input3: call chkkbd ; check keyboard test status,stat_cc ; did user type control-c? jnz input5 ; nz = yes, quit test status,stat_cr ; did user type cr? [js] jz input3a ; z = no jmp inputx ; nz = yes, return success [js] input3a:call chktmo ; check timeout test status,stat_tmo jnz input5 ; nz = timed out, quit call bufread ; read from serial port buffer into al jmp input3 ; loop until timeout, exit timeout ; start main read and compare loop input4: mov di,temptr ; pointer to current pattern char cmp di,temptr2 ; at end of pattern? jae inputx ; ae = yes, return success call chkkbd ; check keyboard test status,stat_cc ; did user type control-c? jnz input5 ; nz = yes, quit test status,stat_cr ; did user type cr? [js] jz input4a ; z = no jmp inputx ; nz = yes, return success [js] input4a:call chktmo ; check timeout test status,stat_tmo+stat_ok ; timeout or user override jnz input5 ; nz = timed out, quit cmp reinflg,0 ; Input command? jne input4b ; ne = no, a reinput cmd call bufread ; read from serial port buffer into al jc input4 ; c = nothing there, keep looking jmp short input4c ; analyze character input4b:call peekbuf ; reinput: peek-read from buffer jc input4 ; c = failed to get a character ; got a char from buffer/port input4c:cmp al,'a' ; candidate for case conversion? [js] jb input4d ; b = no [js] cmp al,'z' ; in lower case set? [js] ja input4d ; a = no [js] and al,script.incasv ; apply case conversion mask input4d:mov di,temptr mov ah,byte ptr [di] ; get current pattern char again call matchr ; al=rcvd, ah=pattern, do they match? jc inpm ; c = no match, try substring inc temptr ; matched, point to next pattern char jmp input4 input5: or errlev,2 ; set RECEIVE failure condition or fsta.xstatus,2 ; set status cmp reinflg,0 ; Input command? jne input6 ; ne = no jmp squit ; exit failure: timeout or control-c input6: mov kstatus,2 ; failure jmp squit1 ; skip timeout message, if any inputx: jmp rskp ; return success ; See if a trailing-subset of the matched chars + new port char can match ; the beginning part of the pattern. That is, if we were to simply "forget" ; the oldest of the matched chars and slide left the apparent port string ; then could we eventually find a match? Example: "Input 10 memema" ; gives the pattern of "memema"; suppose the received chars were "mememema". ; Forgetting one left-most rcv'd char at a time (two in this case) finally ; yields a match, from which we should continue to compare fresh port chars ; with successive pattern chars until either they match through all pattern ; chars or we encounter another break. If there is a later break, repeat this ; algorithm. ; Since we really have only the latest char from the port then pointers to ; the matched pattern chars are used to mimic the earlier received chars: ; they must have been identical to produce a match to date. The quick way ; to "forget" oldest received chars is to scan backward through the matched ; pattern chars looking for the current port char; if the first such find does ; not yield a matching substring then look back further. ; no or partial match then break ; di = temptr = pattern break char ; al = port char causing break ; di - offset line = # chars matched thus far ; avoid cpu-brand side effects with "repne scasb" inpm: mov tempa,al ; save port char here inpm1: mov tempd,di ; pattern break loc, where matching failed mov cx,di ; char at di does not match current port char sub cx,offset line ; compute count of matched bytes jcxz inpm4 ; z = 0 = mismatch on the initial pattern char mov al,tempa ; port char to find (in case we looped here) inpm2: dec di ; back up one pattern char mov ah,byte ptr [di]; current pattern character to consider call matchr ; is port char = earlier pattern char? [js] jnc inpm3 ; nc = equal values, go construct substring loop inpm2 ; do cx times, max. (length of match to date) jmp inpm4 ; get here when there are no matches [js] inpm3: mov bx,tempd ; get last break location sub bx,di ; displacement = break - new find of port char mov tempd,di ; remember new location of a port-like char ; cx has number of chars in test substring dec cx ; matched one char already [jrs] jcxz inpm3a ; is there anything left? [jrs] call matstr ; does this substring match the pattern? jc inpm1 ; c = no match, try making substring smaller inpm3a: mov di,tempd ; sub-string matched. Use this shorter match mov temptr,di ; set di for exit (matstr messes up di) inc temptr ; matched, point to next pattern char jmp input4 ; continue with fresh port info inpm4: mov temptr,offset line; complete failure, restart scanning jmp input4 ; get something from the port ; worker for SCINP ; compare strings. One starts at offset line, the other starts bx bytes later. ; cx = # chars to compare. Return carry clear if match, else carry set. matstr: mov si,offset line ; start of pattern string matstr1:mov ah,byte ptr [si] ; pattern char mov al,byte ptr [si+bx] ; "old port char" (same as pattern char) call matchr ; check match of these two characters jc matstr2 ; c = no match (exit with carry flag set) inc si ; match, consider next pair loop matstr1 ; consider rest of substring (cx is counter) clc ; clear c bit (substrings do match) matstr2:ret ; preserves flags (c set = no match) ; worker for SCINP ; compare single characters, one in ah and the other in al. Allow the 0ffh ; wild card to match CR and LF individually. Return carry clear if match, ; or carry set if they do not match. Registers preserved. matchr: cmp ah,al ; do these match? je matchr6 ; e = yes cmp ah,0ffh ; the match cr/lf indicator? je matchr2 ; e = yes cmp al,0ffh ; the match cr/lf indicator? jne matchr5 ; ne = no match at all. matchr2:push ax ; save both chars again and ah,al ; make a common byte for testing cmp ah,cr je matchr4 ; e = cr matches 0ffh cmp ah,lf je matchr4 ; e = lf matches 0ffh pop ax ; recover chars matchr5:stc ; set carry (no match) ret matchr4:pop ax ; recover chars matchr6:clc ; clear carry (match) ret SCINP ENDP ; ; Pause for the specified number of seconds or until a time of day ; Pause [seconds or hh:mm:ss] ; SCPAU PROC NEAR mov kstatus,0 mov ah,cmfile ; get a word (number) mov dx,offset line ; where to store it mov byte ptr line,0 ; terminate line incase no text ; mov bx,offset ptshlp ; help msg mcmsgb ptshlp,cptshlp call comnd jmp r nop ; must be at least 3 bytes mov ah,cmcfm ; get a confirm call comnd jmp r nop mov si,offset line ; source pointer call inptim ; parse pause time (or force default) jc scpau1 ; c = bad time value mov tempa,0 ; no modem status to detect jmp swait4 ; finish in common code scpau1: ret SCPAU ENDP ; ; Wait for the indicated signal for the specified number of seconds or tod ; WAIT [seconds] \signal where \signal is \cd, \dsr modem status lines. ; Use INPUT-TIMEOUT ACTION for failures. ; SCWAIT PROC NEAR mov kstatus,0 mov ah,cmtxt ; get a word (number) mov bx,offset line ; where to store it mov byte ptr line,0 ; terminate line incase no text ; mov dx,offset wthlp ; help msg mcmsg wthlp,cwthlp call comnd jmp r nop ; must be at least 3 bytes mov tempa,0 ; clear modem status test byte mov si,offset line ; source pointer call inptim ; parse pause time (or force default) jc swait1a ; c = bad time value mov di,offset line ; put text in compare buffer call cnvlin ; convert \numbers in buf line mov si,di ; code below uses si jnc swait1 ; nc = no error swait1a:ret ; else return on error swait1: cmp cx,0 ; number of chars to examine jle swait4 ; le = none cld lodsb ; get a character dec cx ; reduce count remaining cmp al,' ' ; white space? jbe swait1 ; be = yes, skip over it cmp al,'\' ; backslash signal introducer? jne swait1 ; ne = no, keep searching cmp cx,2 ; at least two chars in signal? jl swait3 ; l = no mov ax,[si] or ax,2020h ; upper case to lower, two chars cmp ax,'dc' ; carrier detect? jne swait2 ; ne = no, try next signal or tempa,modcd ; look for the CD bit add si,2 ; skip this field sub cx,2 ; three less chars left in the line jmp short swait1 ; continue the scan swait2: cmp ax,'sd' ; data set ready? jne swait3 ; ne = no mov al,[si+2] ; third letter or al,20h ; to lower case cmp al,'r' ; r for dsr? jne swait3 ; ne = no or tempa,moddsr ; look for the DSR bit add si,3 ; skip this field sub cx,3 ; four less chars left in the line swait3: cmp ax,'tc' ; clear to send? jne swait3a ; ne = no mov al,[si+2] ; third letter or al,20h ; to lower case cmp al,'s' ; r for dsr? jne swait3a ; ne = no or tempa,modcts ; look for the CTS bit add si,3 ; skip this field sub cx,3 ; four less chars left in the line swait3a:jmp short swait1 ; continue the scan ; SWAIT4 is used by PAUSE command SWAIT4: cmp taklev,0 ; are we in a Take file je swait5 ; e = no, print linefeed cmp flags.takflg,0 ; are commands being echoed je swait6 ; e = no, skip this swait5: cmp script.inecho,0 ; Input echoing off? je swait6 ; e = yes mov al,lf ; next line call scdisp ; display the char swait6: call serini ; initialize the system's serial port jc swait9 ; c = failure mov status,stat_unk ; clear status flag push si mov parmsk,0ffh ; parity mask, assume 8 bit data mov si,portval cmp [si].parflg,parnon ; parity is none? pop si je swait7 ; e = none mov parmsk,07fh ; else strip parity (8th) bit swait7: call getmodem ; modem handshake status to AL and al,tempa ; keep only bits to be tested cmp tempa,0 ; anything to be tested? je swait7a ; e = no, just do the wait part cmp al,tempa ; check selected status bits jne swait7a ; ne = not all selected bits match jmp rskp ; all match. take successful exit swait7a:call chkport ; get and show any new port char call chkkbd ; check keyboard test status,stat_cc ; control-c? jnz swait9 ; nz = yes, quit call chktmo ; check tod for timeout test status,stat_tmo+stat_ok ; timeout or user override? jz swait7 ; z = no, continue to wait cmp tempa,0 ; were we waiting on anything? jne swait9 ; ne = yes, timeout = failure jmp rskp ; else timeout = success swait9: or errlev,2 ; set RECEIVE error condx or fsta.xstatus,2 ; set status jmp squit ; take error exit SCWAIT ENDP ; Output line of text to port, detect \b and \B as commands to send a Break ; on the serial port line. ; Output text SCOUT PROC NEAR mov kstatus,0 mov ah,cmtxt ; get a whole line of asciiz text mov bx,offset line ; store text here ; mov dx,offset outhlp ; help message mcmsg outhlp,couthlp call comnd jmp r nop cmp taklev,0 ; is this being done in a Take file? je outpu0 ; e = no, display linefeed cmp flags.takflg,0 ; are commands being echoed? je outp0a ; e = no, skip the display outpu0: cmp script.inecho,0 ; Input echoing off? je outp0a ; e = yes mov al,lf ; next line call scdisp ; display the char outp0a: mov al,spause ; wait three millisec or more add al,3 xor ah,ah call pcwait ; breathing space for HDX systems call serini ; initialize the system's serial port jnc outp0c ; nc = success or errlev,1 ; set SEND failure condition or fsta.xstatus,1 ; set status jmp squit outp0c: mov status,stat_unk ; clear status flag mov parmsk,0ffh ; parity mask, assume 8 bit data mov si,portval cmp [si].parflg,parnon ; parity is none? je outp0b ; e = none mov parmsk,07fh ; else strip parity (8th) bit outp0b: mov si,portval ; serial port structure mov bl,[si].ecoflg ; Get the local echo flag mov lecho,bl ; our copy mov si,offset line ; get start of line mov di,offset line ; put results in the same place mov ah,script.incasv ; save current case state push ax mov script.incasv,0ffh ; say no case conversion call cnvlin ; convert \numbers to binary pop ax mov script.incasv,ah ; recover case state jnc outpu1 ; nc = no error ret ; return on error outpu1: mov temptr,offset line ; save pointer here mov tempd,cx ; save byte count here mov ttyact,1 ; say interactive style output outpu2: cmp tempd,0 ; are we done? jg outpu2a ; g = not done yet mov ttyact,0 ; reset interactive output flag jmp rskp ; return success outpu2a:mov si,temptr ; recover pointer cld lodsb ; get the character dec tempd ; one less char to send mov temptr,si ; save position on line mov tempa,al ; save char here for outchr mov retry,0 ; number of output retries cmp al,5ch ; backslash? jne outpu4d ; ne = no cmp byte ptr [si],'b' ; "\b" for Break? je outpu4c ; e = yes cmp byte ptr [si],'B' ; "\B" ? jne outpu4d ; ne = no outpu4c:inc temptr ; move scan ptr beyond "\b" dec tempd call sendbr ; call msx send-a-break procedure jmp outpu5 ; resume beyond echoing outpu4d:inc retry ; count output attempts cmp retry,maxtry ; too many retries? jle outpu4g ; le = no or errlev,1 ; set SEND failure condition or fsta.xstatus,1 ; set status jmp squit ; return failure outpu4g:mov ah,tempa ; outchr gets fed from ah call outchr ; send the character to the port jmp outpu4d ; failure to send char nop ; ensure 3 bytes for rskp of outchr cmp lecho,0 ; is Local echo active? je outpu5 ; e = no mov al,tempa ; test flags.capflg,logses ; is capturing active? jz outp4b ; z = no push ax ; save char call cptchr ; give it captured character pop ax ; restore character and keep going outp4b: cmp script.inecho,0 ; Input echo off? je outpu5 ; e = yes call scdisp ; echo character to the screen ; outpu5: push cx outpu5a:mov cx,10 ; reset retry counter outpu5b:call chkkbd ; check keyboard for interruption test status,stat_cc ; control c interrupt? jnz outpu6 ; nz = yes, quit now cmp script.inecho,0 ; Input echo off? je outpu5c ; e = yes, skip port reading/display call chkport ; check for char at serial port test status,stat_ok ; and put any in buffer jnz outpu5a ; nz = have a char, look for another mov ax,1 ; wait 1 millisec between rereads push cx ; protect counter call pcwait pop cx dec cx ; count down retries jge outpu5b ; ge = keep trying outpu5c:pop cx ; no more input, recover register jmp outpu2 ; resume command outpu6: pop cx ; recover register or errlev,1 ; set SEND failure condition or fsta.xstatus,1 ; set status mov ttyact,0 ; reset interactive output flag jmp squit ; quit on control c SCOUT ENDP ; Raw file transfer to host (strips linefeeds) ; Transmit filespec [prompt] ; Optional prompt is the single char expected from the host to ACK each line. ; Default prompt is a linefeed (or a carriage return from us). ; SCXMIT PROC NEAR mov kstatus,0 mov ah,cmfile ; get a filename, asciiz mov dx,offset line ; where to store it ; mov bx,offset xmthlp ; help message mcmsgb xmthlp,cxmthlp call comnd jmp r ; exit on failure nop mov ah,cmtxt ; get a prompt string, asciiz mov bx,offset line+80 ; where to keep it (end of "line") mov word ptr [bx],0 ; clear and add terminator ; mov dx,offset pmthlp ; Help in case user types "?". mcmsg pmthlp,cpmthlp call comnd jmp r nop cmp line,0 ; filename given? je xmit0a ; e = no cmp ah,0 ; prompt given? je xmit0b ; e = no, use line feed default mov si,offset line+80 ; convert possible numeric prompt call katoi ; convert number to binary, if number jnc xmit0 ; nc = got number mov al,line+80 ; get ascii char from user's prompt jmp short xmit0 xmit0b: mov al,lf ; default prompt xmit0: mov tempa,al ; save the code here mov dx,offset line ; point to filename mov ah,open2 ; DOS 2 open file mov al,0 ; open for reading int dos mov fhandle,ax ; store file handle here jnc xmit1 ; nc = successful opening xmit0a: mov ah,prstr ; give file not found error message ; mov dx,offset xfrfnf mcmsg xfrfnf,cxfrfnf int dos or errlev,1 ; set SEND failure condition or fsta.xstatus,1 ; set status jmp squit ; exit failure xmitx: mov ah,prstr ; error during transfer ; mov dx,offset xfrrer mcmsg xfrrer,cxfrrer int dos xmitx2: mov bx,fhandle ; file handle mov ah,close2 ; close file int dos ;; call serrst ; reset serial port call bufclear ; clear script buffer call clrbuf ; clear local serial port buffer or errlev,1 ; set SEND failure condition or fsta.xstatus,1 ; set status jmp squit ; exit failure ; xmity: mov bx,fhandle ; file handle mov ah,close2 ; close file int dos ;; call serrst ; reset serial port call bufclear ; clear buffers call clrbuf jmp rskp ; and return success xmit1: call serini ; initialize serial port jnc xmit1b ; nc = success or errlev,1 ; set SEND failure condition or fsta.xstatus,1 ; set status jmp squit xmit1b: call bufclear ; clear script input buffer call clrbuf ; clear serial port buffer mov status,stat_unk ; clear status flag mov parmsk,0ffh ; parity mask, assume 8 bit data mov si,portval cmp [si].parflg,parnon ; parity is none? je xmit1a ; e = none mov parmsk,07fh ; else strip parity (8th) bit xmit1a: mov bl,[si].ecoflg ; Get the local echo flag. mov lecho,bl ; our copy mov dx,offset crlf ; display cr/lf mov ah,prstr int dos xmit2: mov dx,offset line ; buffer to read into mov cx,linelen ; # of bytes to read mov ah,readf2 ; read bytes from file mov bx,fhandle ; file handle is stored here int dos jnc xmit2a ; nc = success jmp xmitx ; exit failure xmit2a: mov cx,ax ; number of bytes read jcxz xmity ; z = none, end of file ; mov si,offset line ; buffer for file reads cld xmit3: lodsb ; get a byte cmp al,ctlz ; is this a Control-Z? jne xmit3a ; ne = no cmp flags.eofcz,0 ; ignore Control-Z as EOF? jne xmity ; ne = no, we are at EOF xmit3a: push si ; save position on line push cx ; and byte count push ax ; save char around outchr call xmit4: mov retry,0 ; clear retry counter xmit4f: pop ax ; recover saved char push ax ; and save it again mov ah,al ; outchr wants char in ah inc retry ; count number of attempts cmp retry,maxtry ; too many retries? jle xmit4g ; le = no or status,stat_cc ; simulate control-c abort pop ax ; clean stack xor al,al ; clear char jmp xmita ; and abort transfer xmit4g: cmp al,lf ; line feed? je xmit4h ; e = yes, don't send it call outchr ; send the character to the port jmp xmit4f ; failed, try again nop xmit4h: pop ax ; recover saved char cmp lecho,0 ; is local echoing active? je xmit5 ; e = no test flags.capflg,logses ; capturing active? jz xmit4a ; z = no push ax ; save char call cptchr ; give it the character just sent pop ax ; restore character and keep going xmit4a: call scdisp ; display char on screen xmit5: cmp al,cr ; did we send a carriage return? je xmit8 ; e = yes, time to check keyboard xmit7: pop cx pop si loop xmit3 ; finish this buffer full jmp xmit2 ; read next buffer xmit8: test status,stat_cc ; Control-C seen? jnz xmita ; nz = yes call chkkbd ; check keyboard (returns char in al) test status,stat_ok ; have a char? jnz xmita ; nz = yes cmp tempa,0 ; is prompt char a null? jne xmit8b ; ne = no call bufread ; check for char from serial port buf jnc xmit8 ; nc = a char, read til none jmp xmit7 ; continue transfer xmit8b: call bufread ; check for char from serial port buf jc xmit8 ; c = none cmp al,tempa ; is port char the ack? jne xmit8 ; ne = no, just ignore the char jmp xmit7 ; yes, continue transfer xmita: test status,stat_cc ; control-c? jnz xmitc ; nz = yes test status,stat_cr ; a local ack? jz xmit8 ; no, ignore local char mov dx,offset crlf ; display cr/lf mov ah,prstr int dos jmp xmit7 ; continue transfer xmitc: pop cx ; Control-C, clear stack pop si ; ... ; mov dx,offset xfrcan ; say canceling transfer mcmsg xfrcan,cxfrcan mov ah,prstr int dos mov flags.cxzflg,0 ; clear Control-C flag jmp xmitx2 ; ctrl-c, quit SCXMIT ENDP ;;;;;;;;;;;;;;;;;; local support procedures ;;;;;;;;;; ; ;worker: copy line from si to di, converting \nnn strings to single chars ; returns carry set if error, else carry clear. Detects leading at-sign ; as an indicator to read command file for one line of text; command files ; may be nested to a depth of 100. ; Items of the form \chars which are not numbers are copied verbatium ; to the output string (ex: \a is copied as \a). The string is first trimmed ; of trailing spaces, then the possible curly brace delimiter pair is ; removed, and finally \numbers are converted to binary. [jrd] cnvlin proc near push si ; source ptr push di ; destination ptr push ax mov ax,ds mov es,ax ; use data segment for es:di pop ax mov tempd,0 ; count indirection depth cnvln0: cmp tempd,100 ; limit to 100 deep jbe cnvln0a ; be = not too deep yet jmp cnvln8 ; too deep, quit cnvln0a:cld xor cx,cx ; initialize returned byte count lodsb ; get the first character cmp al,40h ; at-sign indirection? je cnvln5 ; e = yes, open the file dec si ; no, push back char just read call cnvstr ; convert string's curly braces cnvln1: xor ah,ah ; clear high byte of number call katoi ; get a char into al, convert number jnc cnvln4 ; nc = binary number converted cmp al,0ffh ; cr/lf wild card? je cnvln4 ; e = yes, store it cmp al,0 ; end of line? jne cnvln3 ; ne = no jmp cnvlnx ; yes, exit now cnvln3: cmp al,'a' ; candidate for conversion? [js] jb cnvln4 ; b = no cmp al,'z' ; still in lower case set? [js] ja cnvln4 ; a = no and al,script.incasv ; else apply case conversion mask cnvln4: stosb ; save the char inc cx ; and count it cmp ah,0 ; was number larger than one byte? je cnvln1 ; e = no xchg ah,al ; put high byte into al stosb ; store it too inc cx ; count storage jmp short cnvln1 ; read more cnvln5: mov dx,si ; get filename ptr from source line push si inc tempd ; count indirection depth mov cx,64 ; max length of a filename. cnvln5a:cmp byte ptr [si],' ' ; whitespace or control code? jbe cnvln5b ; be = yes, found termination inc si ; else look at next char loop cnvln5a ; limit search cnvln5b:mov byte ptr [si],0 ; make asciiz pop si mov ah,open2 ; DOS 2 open file mov al,0 ; open for reading int dos mov word ptr fhandle,ax ; store file handle jnc cnvln7 ; nc = open ok, read from file mov ah,prstr ; mov dx,offset indmis ; file open error msg mcmsg indmis,cindmis int dos xor cx,cx ; say zero bytes read pop di ; destination ptr pop si ; source ptr stc ; set c bit, failure ret cnvln7: mov bx,word ptr fhandle ; file handle mov cx,linelen ; # of bytes to read mov ah,ioctl ; ioctl, is this the console device? mov al,0 ; get device info int dos and dl,81h ; ISDEV and ISCIN bits needed together cmp dl,81h ; Console input device? jne cnvln7d ; ne = no, use regular file i/o push ds pop es ; set es:di to datas segment push di ; save starting pointer cnvln7b:mov ah,coninq ; read console, no echo int dos stosb cmp al,cr ; end of the line yet? loopne cnvln7b ; keep reading cnvln7c:mov byte ptr [di],0 ; insert terminator pop di ; recover starting pointer mov dx,di ; simulate read file read mov ax,linelen sub ax,cx ; ax = number of chars read jmp cnvln7e ; close file, finish processing cnvln7d:mov dx,di ; destination ptr mov byte ptr [di],0 ; insert null terminator, clears line mov ah,readf2 ; DOS 2 read from file int dos cnvln7e:pushf ; save flags push ax ; save byte count read mov ah,close2 ; close file (wanted just one line) int dos pop ax popf ; recover flags now jc cnvln8 ; c = error mov cx,ax ; ax = number of bytes read jcxz cnvln8a ; cx = z = no bytes read mov al,cr ; look for cr as terminator cld repne scasb ; scan while not a cr and cx not zero jne cnvln7a ; ne = no cr found dec di ; point at cr cnvln7a:mov byte ptr [di],0 ; plant terminator on the cr ; or after last read char, if no cr. pop di ; get original destination ptr push di ; and save it again mov si,dx ; new source = this line ; go convert text, as necessary, and jmp cnvln0 ; allow nested indirection cnvln8: mov ah,prstr ; mov dx,offset inderr ; error reading file message mcmsg inderr,cinderr int dos cnvln8a:xor cx,cx ; say zero bytes read pop di pop si stc ; set carry for failure ret ; and do a real return cnvlnx: pop di ; destination ptr pop si ; source ptr clc ; clear c bit, success ret cnvlin endp ; ; worker: read the number of seconds to pause or timeout ; returns time of day for timeout in timhms, and next non-space or ; non-tab source char ptr in si. Time is either elapsed seconds or ; a specific hh:mm:ss, determined from context of colons being present. ; Last form can be abbreviated as hh:[mm[:ss]]. Returns carry set if ; hh:mm:ss form has bad construction (invalid time). inptim proc near push ax push bx push cx push dx push di cld ; decode pure seconds construction mov di,si ; remember source pointer mov cx,10 ; multiplier mov bx,script.indfto ; no numbers yet, use default-timeout mov al,byte ptr[si] cmp al,':' ; stray hh:mm:ss separator? je inptm8 ; e = yes cmp al,'9' ; start with numeric input? ja inptm4 ; a = no, use default time cmp al,'0' ; ditto jb inptm4 xor ah,ah ; source char holder xor bx,bx ; accumulated sum inptm1: mov al,byte ptr[si] ; get a byte into al cmp al,':' ; hh:mm:ss construction? je inptm8 ; e = yes sub al,'0' ; remove ascii bias cmp al,9 ; numeric? ja inptm4 ; a = non-numeric, exit loop, bx = sum xchg ax,bx ; put sum into ax, char in bl mul cx ; sum times ten xchg ax,bx ; put char into al, sum in bx add bx,ax ; add to sum inc si ; next char jmp short inptm1 ; loop thru all chars inptm4: cmp bx,12*60*60 ; half a day, in seconds jb inptm5 ; b = less than jmp inptm13 ; more than, error inptm5: push si ; save ending scan position for return mov timout,bx ; # seconds of timeout desired mov ah,gettim ; read DOS tod clock int dos mov timhms[0],ch ; hours mov timhms[1],cl ; minutes mov timhms[2],dh ; seconds mov timhms[3],dl ; hundredths of seconds mov bx,2 ; start with seconds field inptm6: mov ax,timout ; our desired timeout interval add al,timhms[bx] ; add current tod digit to interval adc ah,0 xor dx,dx ; clear high order part thereof mov cx,60 ; divide by 60 div cx ; compute number of minutes or hours mov timout,ax ; quotient mov timhms[bx],dl ; put remainder in timeout tod digit dec bx ; look at next higher order time field cmp bx,0 ; done all time fields? jge inptm6 ; ge = no cmp timhms[0],24 ; normalize hours jl inptm7 ; l = not 24 hours sub timhms[0],24 ; discard part over 24 hours inptm7: pop si ; return ptr to next source char jmp inptm11 ; trim trailing whitespace inptm8: ; decode hh:[mm[:ss]] to timhms mov si,di ; recall starting source pointer mov word ptr timhms[0],0 ; clear time out tod mov word ptr timhms[2],0 mov bx,0 ; three groups possible inptm9: mov dl,byte ptr[si] ; get a char cmp dl,':' ; field separator? je inptm10 ; e = a separator, step fields sub dl,'0' ; remove ascii bias cmp dl,9 ja short inptm11 ; a = failure to get expected digit mov al,timhms[bx] ; get sum to al mov ah,10 mul ah ; sum times ten add al,dl ; sum = 10 * previous + current mov timhms[bx],al ; current sum cmp timhms[bx],60 ; more than legal? jae inptm13 ; ae = illegal cmp bx,0 ; doing hours? ja inptm9a ; a = no, min or sec cmp timhms[bx],24 ; more than legal? jae inptm13 ; ae = illegal inptm9a:inc si ; next char jmp short inptm9 ; continue analysis inptm10:inc bx ; point to next field inc si ; next char cmp bx,2 ; last subscript to use (secs) jbe inptm9 ; be = get more text inptm11:cmp byte ptr [si],spc ; examine break char, remove spaces jne inptm12 ; ne = no, stay at this char inc si ; look at next char jmp short inptm11 ; continue scanning off white space inptm12:clc ; carry clear for success jnc inptm14 inptm13:stc ; carry set for illegal value inptm14:pop di ; return with si beyond our text pop dx pop cx pop bx pop ax ret inptim endp ; worker: display the char in al on screen ; use caret-char notation for control codes scdisp proc near push dx push ax mov ah,conout ; our desired function test flags.remflg,d8bit ; show all 8 bits? jnz scdisp0 ; nz = yes and al,7fh ; apply 7 bit display mask scdisp0:cmp al,0 ; null? je scdis2 ; e = yes, ignore cmp al,del ; delete code? je scdis2 ; e = yes, ignore cmp al,spc ; control char? jae scdis1 ; ae = no, display as-is cmp al,cr ; carriage return? je scdis1 ; e = yes, display as-is cmp al,lf ; line feed? je scdis1 cmp al,tab ; horizontal tab? je scdis1 cmp al,bell ; bell? je scdis1 cmp al,bs ; backspace? je scdis1 cmp al,escape ; escape? je scdis1 or al,40h ; control code to printable char push ax mov dl,5eh ; display caret first int dos pop ax scdis1: mov dl,al ; the char to be displayed int dos scdis2: pop ax pop dx ret scdisp endp ; workers ; Circular buffer for data from serial port. Written by Joe R. Doupnik ; Entry points - ; bufread: read serial port for latest char (invokes bufwrite, sets ; status), get a char into al, return carry set if none. ; bufwrite: put a char from al into buf. If this overwrites an unread ; character then: we lose the old char, the read pointer ; is moved to the next oldest unread char, and the ; number of chars in the buffer is decreased by one. ; bufclear: empties the buffer. ; The buffer is prtbuf, of size prtbuflen bytes. Internally, integer bufcnt ; holds the number of buffer locations occupied, pointer bufrdptr is the ; offset of the char to be read, pointer bufwtptr is the offset of the ; place to store the next incoming char. ; bufclear proc near mov bufcnt,0 ; clear count of bytes in buffer mov bufrdptr,offset prtbuf ; move read pointer to start of buf mov bufwtptr,offset prtbuf ; move write pointer to start of buf mov cx,prtbuflen push es ; physically clear the buffer push di mov ax,datas mov es,ax mov al,0 ; write prtbuflen nulls mov di,offset prtbuf cld rep stosb pop di pop es ret bufclear endp bufread proc near call chkport ; get any oldest char from port cmp bufcnt,0 ; empty buffer? jne bufrd1 ; ne = no stc ; yes, set carry flag (no char) ret ; and quit (chkport sets status) bufrd1: push si mov si,bufrdptr mov al,byte ptr [si] ; extract a char into al pop si inc bufrdptr ; move pointer to next byte dec bufcnt ; say have extracted a char cmp bufrdptr,offset prtbuf+prtbuflen ; beyond end? jb bufrd2 ; b = not yet, just return mov bufrdptr,offset prtbuf ; reset to start of buf (wrapping) bufrd2: clc ; clear carry flag (have read a char) ret ; chkport sets status bufread endp ; Non-destructive read of serial port circular buffer. Requires external ; initialization of peek read pointer bufpkptr and count remaining bufpkcnt. ; Returns character in register al. peekbuf proc near cmp bufpkcnt,0 ; peek counter, empty buffer? jne peekbu2 ; ne = no, so look in buffer cmp bufcnt,prtbuflen ; is real buffer full? jae peekbu1 ; ae = yes, have examined everything call chkport ; get a char from port cmp bufcnt,0 ; still nothing from port? je peekbu1 ; e = no char, report fact inc bufpkcnt ; got one, increase peek counter jmp peekbu2 ; go extract it peekbu1:stc ; return nothing to see ret peekbu2:push si mov si,bufpkptr ; buffer peek pointer mov al,byte ptr [si] ; extract a char into al pop si inc bufpkptr ; move pointer to next byte dec bufpkcnt ; say have extracted a char cmp bufpkptr,offset prtbuf+prtbuflen ; beyond end? jb peekbu3 ; b = not yet, just return mov bufpkptr,offset prtbuf ; reset to start of buf (wrapping) peekbu3:clc ; clear carry flag (have read a char) ret peekbuf endp bufwrite proc near push si mov si,bufwtptr mov byte ptr [si],al ; store char held in al pop si inc bufwtptr ; move pointer to next byte cmp bufwtptr,offset prtbuf+prtbuflen ; beyond end? jb bufwt1 ; b = not yet mov bufwtptr,offset prtbuf ; reset to start of buf (wrapping) bufwt1: inc bufcnt ; say have added a char to the buf cmp bufcnt,prtbuflen ; more than we can hold? jbe bufwt3 ; be = not overflowing push bufwtptr ; read ptr can't alias write ptr pop bufrdptr ; move up read pointer mov bufcnt,prtbuflen ; limit count to max buffer length bufwt3: ret bufwrite endp ; worker: check for timeout, return status=stat_tmo if timeout, else bit ; stat_tmo is cleared. chktmo: and status,not stat_tmo mov ah,gettim ; get the time of day int dos sub ch,timhms[0] ; hours difference, ch = (now-timeout) je chktmo2 ; e = same, check mmss.s jg chktmo1 ; g = past target hour add ch,24 ; we are early, see by how much chktmo1:cmp ch,12 ; hours difference, large or small? jge chktmox ; ge = not that time yet jl chktmo3 ; l = beyond that time chktmo2:cmp cl,timhms[1] ; minutes, hours match jb chktmox ; b = early ja chktmo3 ; a = late cmp dh,timhms[2] ; seconds, hhmm match jb chktmox ; b = early ja chktmo3 ; a = late cmp dl,timhms[3] ; fractions, hhmmss match jb chktmox ; b = early chktmo3:or status,stat_tmo ; say timeout stc ret chktmox:clc ret ; ; worker: check keyboard for char. Return status = stat_cc if control-C typed, ; stat_cr if carriage return, or stat_ok if any other char typed. Else return ; with these status bits cleared. chkkbd: and status,not (stat_ok+stat_cc+stat_cr) ; clear status bits cmp flags.cxzflg,'C' ; Control-C interrupt seen? je chkkbd0 ; e = yes call isdev ; is stdin a device, not disk file? jnc chkkbd2 ; nc = not device so do not read here mov ah,dconio ; keyboard char present? mov dl,0ffH int dos je chkkbd1 ; e = none or status,stat_ok ; have a char, return it in al cmp al,3 ; control c? jne chkkbd1 ; ne = not control c chkkbd0:or status,stat_cc ; say control c chkkbd1:cmp al,cr ; carriage return? [js] jne chkkbd2 ; ne = no or status,stat_cr ; say carriage return [js] chkkbd2:ret ; ; worker: check serial port for received char. Return status = stat_ok if ; char received, otherwise stat_ok cleared. Can echo char to screen. Will ; write char to local circular buffer. chkport:and status,not stat_ok ; clear status bit call prtchr ; char at port (in al)? jmp chkpor1 ; yes, analyze it nop ; ensure 3 bytes for rskp of prtchr ret ; no, return chkpor1:and al,parmsk ; strip parity, if any cmp rxtable+256,0 ; is translation turned off? je chkpor0 ; e = yes, no translation push bx ; translate incoming character mov bx,offset rxtable ; the translation table xlatb pop bx chkpor0:test flags.capflg,logses ; capturing active? jz chkpor3 ; z = no test flags.remflg,d8bit ; keep 8 bits for displays? jnz chkpo0a ; nz = yes, 8 bits if possible cmp flags.debug,0 ; is debug mode active? jne chkpo0a ; ne = yes, record 8 bits and al,7fh ; remove high bit chkpo0a:push ax ; save char call cptchr ; give it captured character pop ax ; restore character and keep going chkpor3:test flags.remflg,d8bit ; keep 8 bits for displays? jnz chkpo3a ; nz = yes, 8 bits if possible and al,7fh ; remove high bit chkpo3a:cmp script.inecho,0 ; input echoing off? je chkpor4 ; e = yes call scdisp ; display the char chkpor4:call bufwrite ; put char in buffer or status,stat_ok ; say have a char (still in al) ret ; ; Squit is the script error exit pathway. ; squit: mov kstatus,2 ; general command status, failure test status,stat_tmo ; timeout? jz squit2 ; z = no, another kind of failure cmp taklev,0 ; in a Take/macro? jne squit1 ; ne = yes, skip timeout message push dx ; mov dx,offset tmomsg ; say timed out mcmsg tmomsg,ctmomsg mov ah,prstr int dos ; display it. pop dx squit1: cmp script.inactv,0 ; action to do upon timeout je squit4 ; 0 = proceed, ne = non-zero = quit call takclos ; close Take file or macro squit2: call isdev ; stdin is a device (vs file)? jc squit3 ; c = device, not a file mov flags.extflg,1 ; set Kermit exit flag squit3: ret ; return failure squit4: jmp rskp ; return success, ignore error ; ; Jumping to this location is like retskp. It assumes the instruction ; after the call is a jmp addr. RSKP PROC NEAR pop bp add bp,3 push bp ret RSKP ENDP ; Jumping here is the same as a ret. R PROC NEAR ret R ENDP code ends end