name mssscp ; File mssscp.asm ; Edit History ; Last edit: 1 Jan 1988 ; 6 Jan 1988 Fix pointer for out @con calls. [jrd] ; 1 Jan 1988 version 2.30 ; 26 Dec 1987 Use no-echo reading of console for OUTPUT @CON, speedup ; reading of host response for OUTPUT command. [jrd] ; 4 Dec 1987 Update global byte errlev when a Script command Fails ; (timeout or output failure or manual interruption). [jrd] ; 9 Oct 1987 Allow curly braced strings, trim trailing whitespace too. [jrd] ; 4 Oct 1987 Apply Set Display 7/8 bit mask to bytes rcv'd from serial port ; after Set Translation Input filter. Log 8-bit chars in Debug mode. [jrd] ; 26 Sept 1987 Add check for Control-C interrupt to chkkbd and ECHO, ; make Control-C in TRANSMIT command use squit exit. [jrd] ; 18 Aug 1987 Change ESC to escape for MASM 4.5+ [jrd] ; 11 Aug 1987 Add Set Send Pause plus 3 millisec wait before doing OUTPUT.[jrd] ; 31 July 1987 Open port reading to null chars et al. Correct timeofday ; routine, from Jack Bryans. [jrd] ; 22 July 1987 Rewrite time of day material for no ambiguities. [jrd] ; 15 July 1987 Terminate strings read as @filespec on first carriage return. ; Change number parsing to use decimal as default and \bddd for other bases. ; 24 May 1987 Add error recovery for outchr calls. [jrd] ; 10 May 1987 Add translation of input characters, rxtable. [jrd] ; 4 April 1987 Clear Echo's old text line, from Eberhard Lisse. [jrd] ; 18 March 1987 Add requests for command confirmation. [jrd] ; 2 March 1987 Remove test of Set Input Echo from OUTPUT command. [jrd] ; 27 Oct 1986 preserve data char in al around call to outchr in Output [jrd] ; 19 Oct 1986 Add "\b" and "\B" to Output procedure as send-a-Break command ; to serial port comms line. [jrd] ; 1 Oct 1986 Version 2.29a ; 1 Oct 1986 Add 7/8 bit display mask to displayed text. [jrd] ; 12 Sept 1986 Add changes from Frank da Cruz: one second default timeout, ; echo chars if Local Echo is On, Input command without a pattern should ; behave like a Pause command. ; ; MS Kermit Script routines, DEC-20 style. ; Extensively rewritten for MS Kermit 2.29a by Joe R. Doupnik 5 July 86 ;; ; Created June, 1986 a.d. By James Sturdevant ; A. C. Nielsen Co. Mpls. ; 8401 Wayzata Blvd. ; Minneapolis, Mn. 55426 ; (612)546-0600 ;;;;;;;; ; Kermit command level usages and this file's entry points: ; Clear - clears serial port buffers. Procedure scclr. ; Echo text - displays text on local screen. Proc scecho. ; Pause [time] - waits indicated number of seconds (default is Input ; Default-timeout, 1 second typically). Proc scpau. ; Input [time] text - waits up to time seconds while scanning serial port ; input for a match with text. Default value for time is Input ; Default-timeout, 1 second typically). Spaces or tabs are separators ; between the time and text fields. Proc scinp. ; A carriage return typed by the local user simulates a match. ; Output text - sends the text to the serial output port. Proc scout. ; Transmit text [prompt] - raw file transfer to host. Proceeds from source ; line to source line upon receipt of prompt from host or carriage ; return from us. Default prompt is linefeed. A null prompt (\0) ; causes the file to be sent with no pausing or handshaking. Note ; that linefeeds are stripped from outgoing material. Proc scxmit. ; In the above commands "text" may be replaced by "@filespec" to cause the ; one line of that file to be used instead. @CON obtains one line of ; text from the keyboard. Such "indirect command files" may be nested ; to a depth of 100. Control codes are written as decimal numbers ; in the form "\ddd" where d is a digit between 0 and 9. Carriage ; return is \13, linefeed is \10, bell is \7; the special code \255 ; is used to match (Input) either cr or lf or both. ; These commands can be given individually by hand or automatically ; in a Kermit Take file; Take files may be nested. ;;;;;;;; ; These routines expect to be invoked by the Kermit command dispatcher ; and can have their default operations controlled by the Kermit Set Input ; command (implemented in file mssset.asm). They parse their own cmd lines. ; Set Input accepts arguments of ; Case Ignore or Observe (default is ignore case when matching strings) ; Default-timeout seconds (default is 5 seconds) ; Echo On or Off controls echoing of Input cmd text (default is Off) ; Timeout-action Quit or Proceed (default is Proceed) ; These conditions are passed via global variables incasv,indfto,inecho, ; inactv, respectively, stored here. ;;;;;;;;; include mssdef.h public scout, scinp, scpau, scecho, scclr, scxmit linelen equ 134 ; length of working buffer line prtbuflen equ 128 ; serial port local buffer length maxtry equ 5 ; maximum number of output retries stat_unk equ 0 ; status return codes. stat_ok equ 1 ; have a port character stat_cc equ 2 ; control-C typed stat_tmo equ 4 ; timeout stat_cr equ 8 ; carriage return typed datas segment public 'datas' public indfto, inactv, incasv, inecho extrn taklev:byte, takadr:word, portval:word, flags:byte extrn rxtable:byte, spause:byte, errlev:byte ; global (public) variables inactv db 0 ; input action value (default proceed) incasv db 0dfh ; input case (default ignore) indfto dw 1 ; input and pause timeout (def 1 sec) inecho db 1 ; echo Input cmd text (0 = no) ; local variables line db linelen+1 dup (?) ; line of output or input + terminator prtbuf db prtbuflen dup (?) ; serial port storage buffer bufcnt dw 0 ; serial port buf byte cnt, must be 0 bufrdptr dw prtbuf ; serial port buf read ptr bufwtptr dw prtbuf ; serial port buf write ptr temptr dw ? ; temporary pointer temptr2 dw ? ; ditto, points to end of INPUT string tempd dw ? ; temp tempa db ? ; another temp retry db 0 ; number of output retries status dw ? ; general status word fhandle dw ? ; file handle storage place parmsk db 7fh ; 7/8 bit parity mask lecho db ? ; local echo of output (0 = no) timout dw ? ; work area (seconds before timeout) timhms db 4 dup (?) ; hhmmss.s tod buffer crlf db cr,lf,'$' xfrfnf db cr,lf,'?Transmit file not found$' xfrrer db cr,lf,'?error reading Transmit file$' xfrcan db cr,lf,'?transmission canceled$' indmis db '?Indirect file not found',cr,lf,'$' inderr db '?error reading indirect file',cr,lf,'$' tmomsg db cr,lf,'?Timeout$' outhlp db 'line of text to be sent to remote host$' inphlp db 'time limit and line of text expected from remote host$' echhlp db 'line of text to echo to screen$' ptshlp db 'number of seconds to pause$' xmthlp db 'File specification with optional path name$' pmthlp db 'Prompt character expected as an ACK from host (\0 for none)$' datas ends 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 assume cs:code, ds:datas ; Clear input buffer(s) of serial port ; Clear command ; SCCLR PROC NEAR 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 call comnd jmp rskp ; ignore parse error if no text mov si,offset line ; start of line mov di,si ; convert to the same place mov ah,incasv ; save current case state push ax mov incasv,0ffh ; say no case conversion call cnvlin ; convert \numbers to binary pop ax mov 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 ; Input from port command, match input with text pattern ; Input [timeout] text ; SCINP PROC NEAR mov ah,cmtxt ; get a whole line of asciiz text mov bx,offset line ; place to put text mov dx,offset inphlp ; help message call comnd ; get the pattern text jmp r ; nothing, complain mov ah,cmcfm ; get a confirm call comnd jmp r ; no confirm nop 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 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 mov status,stat_unk ; clear status flag call inptim ; get the timeout time, sets si mov di,offset line ; put text in compare buffer call cnvlin ; convert \numbers in buf line jnc input2 ; nc = no error ret ; else return on error input2: mov parmsk,0ffh ; parity mask, assume 8 bit data mov di,portval cmp [di].parflg,parnon ; parity is none? je input2a ; e = none mov parmsk,07fh ; else strip parity (8th) bit input2a: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 cmp cx,0 ; empty pattern? (cnvlin sets cx=cnt) jne input4 ; ne = not empty ; 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] jnz inputx ; nz = yes, return success [js] 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 ; 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] jnz inputx ; nz = yes, return success [js] call chktmo ; check timeout test status,stat_tmo jnz input5 ; nz = timed out, quit call bufread ; read from serial port buffer into al jc input4 ; c = nothing there, keep looking cmp al,'a' ; candidate for case conversion? [js] jb inpu4a ; b = no [js] cmp al,'z' ; in lower case set? [js] ja inpu4a ; a = no [js] and al,incasv ; apply case conversion mask inpu4a: 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 jmp squit ; exit failure: timeout or control-c 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 ; Pause [seconds] ; SCPAU PROC NEAR 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 call comnd nop ; ignore parse errors (no text) nop ; must be at least 3 bytes nop mov ah,cmcfm ; get a confirm call comnd jmp r ; no confirm nop ; call inptim ; parse pause time (or force default) cmp taklev,0 ; are we in a Take file je paus0c ; e = no, print linefeed cmp flags.takflg,0 ; are commands being echoed je paus0d ; e = no, skip this paus0c: cmp inecho,0 ; Input echoing off? je paus0d ; e = yes mov al,lf ; next line call scdisp ; display the char paus0d: call serini ; initialize the system's serial port 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 pause1 ; e = none mov parmsk,07fh ; else strip parity (8th) bit pause1: call chkport ; get and show any new port char call chkkbd ; check keyboard test status,stat_cc ; control-c? jnz pause2 ; nz = yes, quit call chktmo ; check tod for timeout test status,stat_tmo ; timeout? jz pause1 ; z = no, continue to wait jmp rskp ; timeout, take successful exit pause2: or errlev,1+2 ; set SEND and RECEIVE error condx. jmp squit ; take error exit SCPAU 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 ah,cmtxt ; get a whole line of asciiz text mov bx,offset line ; store text here mov dx,offset outhlp ; help message call comnd jmp r ; bad parse (no text) nop mov ah,cmcfm ; get a confirm call comnd jmp r ; no confirm 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 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 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,incasv ; save current case state push ax mov incasv,0ffh ; say no case conversion call cnvlin ; convert \numbers to binary pop ax mov 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 jcxz outpu2 ; empty string outpu2: cmp tempd,0 ; are we done? jg outpu2a ; g = not done yet 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 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 ; cmp flags.capflg,0 ; is capturing active? je outp4b ; e = no push ax ; save char call cptchr ; give it captured character pop ax ; restore character and keep going outp4b: cmp 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 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 call pcwait dec cx ; count down retries jge outpu5b ; ge = keep trying pop cx ; no more input, recover register jmp outpu2 ; resume command outpu6: pop cx ; recover register or errlev,1 ; set SEND failure condition 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 ah,cmfile ; get a filename, asciiz mov dx,offset line ; where to store it mov bx,offset xmthlp ; help message 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 byte ptr line+80,lf ; store default prompt (line feed) mov byte ptr line+81,0 ; add terminator mov dx,offset pmthlp ; Help in case user types "?". call comnd nop ; ignore parse error if no prompt nop nop mov ah,cmcfm ; get a confirm call comnd jmp r ; no confirm nop mov si,offset line+80 ; convert possible numeric prompt cld call katoi ; convert number to binary, if number 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 mov ah,prstr ; give file not found error message mov dx,offset xfrfnf int dos or errlev,1 ; set SEND failure condition jmp squit ; exit failure xmitx: mov ah,prstr ; error during transfer mov dx,offset xfrrer 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 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 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 jc xmitx ; c = failure mov cx,ax ; number of bytes read jcxz xmity ; z = none, end of file ; mov si,offset line ; buffer for file reads xmit3: lodsb ; get a byte 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 cmp flags.capflg,0 ; capturing active? je xmit4a ; e = 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 mov ah,prstr int dos 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,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 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 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 timeofday for timeout in timhms, and next non-space or ; non-tab source char ptr in si. ; inptim proc near push ax push bx push cx push dx cld mov si,offset line ; source pointer mov cx,10 ; multiplier xor bx,bx ; accumulated sum xor ax,ax ; source char holder cmp byte ptr [si],'9' ; start with numeric input? ja inptm3 ; a = no, use default time cmp byte ptr [si],'0' ; ditto jb inptm3 inptm1: lodsb ; get a byte into al cmp al,'9' ja inptm4 ; non-numeric, exit loop cmp al,'0' jb inptm4 ; b = non=numeric, exit loop 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 sub al,'0' ; convert to binary add bx,ax ; add to sum jmp inptm1 ; loop thru all chars inptm3: mov bx,indfto ; no numbers, use default-timeout mov si,offset line ; reset pointer to start of line jmp inptm5 inptm4: dec si ; back up to non-numeric terminator inptm4a:cmp byte ptr [si],spc ; space? je inptm4b ; e = yes, skip over it cmp byte ptr [si],tab ; tab? je inptm4b ; e = yes, skip over it jmp inptm5 ; neither inptm4b:inc si ; look at next char jmp inptm4a ; continue scanning off white space 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 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 ; convert 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 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 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_unk 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 jl chktmox ; l = we are early 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 chktmox: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 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 ;;; mov flags.cxzflg,0 ; clear interrupt flag 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:cmp flags.capflg,0 ; capturing active? je chkpor3 ; e = 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 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: test status,stat_tmo ; timeout? jz squit2 ; z = no, another kind of failure cmp inecho,0 ; Input echo allowed? je squit1 ; e = no, so skip timeout msg cmp taklev,0 ; in a Take file? jne squit1 ; ne = yes, suppress msg push dx mov dx,offset tmomsg ; say timed out mov ah,prstr int dos ; display it. pop dx squit1: cmp inactv,0 ; action to do upon timeout je squit3 ; 0 = proceed, ne = non-zero = quit squit2: mov flags.cxzflg,'C' ; simulate Control-C termination ret ; return failure, pop take level squit3: 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