NAME msster ; File MSSTER.ASM ; Last edit: 1 Jan 1988 ; 1 Jan 1988 version 2.30 ; 24 Dec 1987 Speed up Status display. [jrd] ; 9 Oct 1987 Add cnvstr routine for curly braced strings. [jrd] ; 2 Oct 1987 Make TARG structure global, clean out old Set Key material. [jrd] ; 15 July 1987 Fix xon/xoff bug when Pushing to DOS. Tnx to Mark Zinzow. [jrd] ; 7 July 1986 Remove prkey routine. [jrd] ; 2 July 1987 Slight modification to cnvlin permitting \0 in text. ; Remove Control-Z at end of capture file. [jrd] ; 22 June 1987 Add XOFF host when Pushing to DOS, from Paul Amaranth. [jrdd ; 13 June 1987 Use decimal printing for showing Macros. Move many number ; conversion routines here from mssset.asm so latter will assemble under ; MASM 1.25. [jrd] ; 19 April 1987 Add global byte pcnet for support of local area nets. [jrd] ; Add global byte ttyact to indicate, by non-zero, that Connect mode ; is active; mostly for system dependent i/o modules. [jrd] ; 5 April 1987 Keep Domsg message on screen for 1 second minimum. ; Add L (Long Break) and H (Hangup) to keyboard operations [jrd] ; 24 March 1987 Add logging details for separate packet and session. [jrd] ; 9 Jan 1987 Add hooks for keyboard translator in system dependent modules. ; Byte kbdflg, defined here, receives a char from the sys dep routines ; instead of from keyboard during breaks from Connect mode. Word Shkadr ; is defined here as zero. If it is set to the address of a replacement ; Show Key procedure then the regular code here is bypassed. [jrd] ; 11 Dec 1986 Turn of serial port when Pushing to DOS. Add char transfer ; from keyboard translator. [jrd] ; 19 Oct 1986 Output cr/lf before status display. [rjd] ; 1 Oct 1986 Version 2.29a ; [2.29] code frozen on 6 May 1986 [jrd] Public clscpt, defkey, inicpt, clscpi public dopar, shokey, cptchr, targ public kbdflg, shkadr, telnet, pcnet, ttyact public cnvlin, katoi, decout, valout, atoi, cnvstr include mssdef.h braceop equ 7bh ; opening curly brace bracecl equ 7dh ; closing curly brace datas segment public 'datas' extrn flags:byte, trans:byte, buff:byte, portval:word extrn cptio:byte targ termarg <0,1,80,24,cptchr,2dch,0,scntab,deftab,0,,parnon> crlf db cr,lf,'$' tmsg1 db cr,lf,'(Connecting to host, type $' tmsg3 db ' C to return to PC)',cr,lf,cr,lf,cr,lf,'$' erms25 db cr,lf,'?Input must be numeric$' erms22 db cr,lf,'?No capture file open$' ;[jd] erms23 db cr,lf,'?Error writing capture file, suspending capture' db cr,lf,'$' esctl db 'Control-$' inthlp db cr,lf,' ? This message F Dump screen to file' db cr,lf,' C Close the connection P Push to DOS' db cr,lf,' S Status of the connection Q Quit logging' db cr,lf,' M Toggle mode line R Resume logging' db cr,lf,' B Send a Break 0 Send a null' db cr,lf,' L Send a long 1.8 s Break H Hangup phone' db cr,lf,' Typing the escape character will send it to the host' db 0 ; this short-form obscures less screen area [jrd] intprm db 'Command> $' CAPBUF DB cptsiz DUP (?) CAPBP DW ? CAPLFT dw ? scntab dw 0 deftab dw 0 sttmsg db cr,lf,'Type space to continue ...$' shkmsg db cr,lf,'Press key: $' kbdflg db 0 ; non-zero means char here from Term pcnet db 0 ; Network is functioning properly ttyact db 0 ; Connect mode active, if non-zero shkadr dw 0 ; offset of replacement Show Key command ten dw 10 ; multiplier for setatoi nbase dw ? ; currently active number base numset db '0123456789ABCDEF' ; number conversion alphabet tmp db ? datas ends code segment public 'code' extrn comnd:near, outchr:near, stat0:near extrn clrbuf:near, term:near, strlen:near extrn prtchr:near, ihostr:near extrn beep:near, puthlp:near, getbaud:near, serhng:near extrn serini:near,serrst:near, sendbr:near, showkey:near extrn fpush:near, dumpscr:near, pcwait:near, sendbl:near assume cs:code, ds:datas ; the show key command. shokey proc near cmp shkadr,0 ; keyboard translator present? je shokey1 ; e = no, use regular routines mov bx,shkadr ; get offset of replacement routine jmp bx ; and execute it rather than us shokey1: ; mov ah,cmcfm ; confirm with carriage return ; call comnd ; jmp r ; uh oh... ; mov dx,offset shkmsg ; mov ah,prstr ; int dos ; print a prompt for it ; mov ax,offset targ ; give it terminal arg block. ; call showkey ; show them the key definition jmp rskp ; and return shokey endp ; This is the CONNECT command. TELNET PROC NEAR mov ah,cmcfm call comnd ; Get a confirm. jmp r ; Didn't get a confirm. mov ah,prstr ; Output mov dx,offset crlf ; a crlf. int dos cmp flags.vtflg,0 ; emulating a terminal? jne teln1 ; ne= yes, skip flashing message call domsg ; Reassure user teln1: mov al,0 ; initial flags mov ttyact,1 ; say telnet is active cmp flags.modflg,0 ; mode line enabled? jne tel010 ; yes, go on or al,modoff ; no, make sure it stays off tel010: or al,havtt ; defaults (!) test flags.debug,logses ; debug mode? jz tel0 ; z = no, keep going or al,trnctl ; yes, show control chars tel0: cmp flags.wrpflg,0 ; wrap mode? jz tel011 ; no, keep going or al,lnwrap tel011: cmp flags.vtflg,0 ; emulation a terminal? jz tel1 or al,emheath ; say emulating some kind of terminal tel1: mov bx,portval cmp [bx].ecoflg,0 ; echoing? jz tel2 or al,lclecho tel2: call getbaud ; pickup current baud rate for port mov targ.flgs,al ; store flags mov ah,flags.comflg ; COMs port identifier mov targ.prt,ah ; Port 1 or 2 mov ah,trans.escchr ; escape character mov targ.escc,ah mov ah,[bx].parflg ; parity flag mov targ.parity,ah mov ax,[bx].baud ; baud rate identifier mov targ.baudb,al xor ah,ah test flags.capflg,logses ; select session logging flag bit jz tel3 ; z = no logging mov ah,capt ; set capture flag tel3: or targ.flgs,ah tem: call serini ; init serial port cmp flags.comflg,'N' ; Network port? jne tem0 ; ne = no cmp pcnet,1 ; did net intialize? ja tem0 ; a=yes (0=no net,1=net/no session,2=session) mov kbdflg,'C' ; no, say Close the connection jmp tem2 ; do it tem0: mov dx,offset crlf ; give user an indication that we are mov ah,prstr ; entering terminal mode int dos mov ax,offset targ ; Point to terminal arguments call term ; Call the main Terminal procedure or targ.flgs,scrsam ; assume screen is the same. tem1: mov flags.wrpflg,0 ; assume not wrap mode test targ.flgs,lnwrap ; in wrap mode? jz tem2 ; no, continue mov flags.wrpflg,1 tem2: mov al,kbdflg ; get the char from Term, if any mov kbdflg,0 ; clear the flag cmp al,0 ; was there a char from Term? jne intch0 ; ne = yes, else ask for one from kbd intchr: mov ah,dconio ; Direct console I/O. mov dl,0FFH ; Input. int dos ; Get a char. jz intchr ; no char, keep looking intch0: mov ah,al cmp ah,' ' ; space - ignore it je tem mov bh,ah ; Save the actual char. and ah,not ('a'-'A') ; Convert to upper case. or ah,40H ; convert ctl-char to actual char. cmp ah,'C' ; Is it close? jne intch1 call serrst ; reset serial port mov ttyact,0 ; say we are no longer active jmp rskp ; and return intch1: cmp ah,'S' ; Is it status? jne intch2 ; ne = no call stat0 ; If so, call stat0. call puthlp ; put help on screen mov dx,offset sttmsg mov ah,prstr int dos intch1a:mov ah,coninq ; console input, no echo int dos cmp al,' ' ; space? jne intch1a and targ.flgs,not scrsam ; remember screen changed. jmp tem intch2: cmp ah,'B' ; Send a break? jne intch3 ; No call sendbr ; Yes, so send a break jmp tem ; And return intch3: cmp ah,'M' ; mode line? jne intch4 cmp bh,cr ; check ^M (cr) against plain ascii M je intch3a ; exit on cr cmp flags.modflg,1 ; mode line enabled? jne intch3a ; ne = no, leave it alone xor targ.flgs,modoff ; enabled, toggle its state intch3a:jmp tem ; and reconnect intch4: cmp ah,'F' ; Dump screen to filespec? jne intch4a ; ne = no call dumpscr ; call global proc in MSX or MSY jmp tem ; and return intch4a:cmp bh,'?' ; Is it help? jne intch5 ; If not, go to the next check. mov ax,offset inthlp ; get the address of the help message. call puthlp ; write help msg mov dx,offset intprm mov ah,prstr ; Print it. int dos and targ.flgs,not scrsam ; remember screen changed jmp intchr ; Get another char. intch5: cmp bh,trans.escchr ; Is it the escape char? jne intch7 ; ne = no intch6: mov ah,al call outchr nop nop nop jmp tem ; Return, we are done here. intch7: cmp ah,'Q' ; 'Q' maybe want to stop logging? jne intch8 test targ.flgs,capt ; not capturing, can't do this jz intc11 and targ.flgs,not capt ; stop capturing jmp tem ; and resume intch8: cmp ah,'R' ; maybe resume? jne intch9 ; no, keep going test flags.capflg,logses ; can we capture? jz intc11 ; no, forget it test targ.flgs,capt ; already capturing? jnz intc11 ; yes, can't toggle back on then or targ.flgs,capt ; else turn flag on jmp tem ; and resume intch9: cmp bh,'0' ; perhaps want a null (note original chr in bh) jne intc10 mov ah,0 call outchr nop nop nop jmp tem intc10: cmp ah,'P' ; maybe want to push? jne intc11 ; no, go on push bx ; send XOFF, if possible, to hold host mov bx,portval ; from Paul Amaranth mov ah,byte ptr [bx].flowc ; get XOFF char, or null pop bx or ah,ah ; check for null (no flow control) jz intch10a ; z = null, don't send one call outchr ; send XOFF to host while we are away nop nop nop intch10a:call serrst ; turn off serial interrupts call fpush ; try pushing nop nop nop mov dx,offset sttmsg mov ah,prstr int dos call serini ; init serial port again call ihostr ; XON the host (if using flow control) jmp intch1a ; wait for space intc11: cmp ah,'L' ; 'L', send a long Break? jne intc12 ; ne = no call sendbl ; send a long one jmp tem intc12: cmp ah,'H' ; 'H', hangup the phone jne intc13 ; ne = no call serhng call serrst ; turn off port jmp tem intc13: call beep ; say illegal character jmp tem TELNET ENDP ; Reassure user about connection to the host. Tell him what escape sequence ; to use to return and the communications port and baud; rate being used. DOMSG PROC NEAR mov ah,prstr mov dx,offset tmsg1 int dos call escprt mov ah,prstr mov dx,offset tmsg3 int dos ret DOMSG ENDP ; print the escape character in readable format. ESCPRT PROC NEAR mov dl,trans.escchr cmp dl,' ' jge escpr2 push dx mov ah,prstr mov dx,offset esctl int dos pop dx add dl,040H ; Make it printable. escpr2: mov ah,conout int dos ret ESCPRT ENDP ; Set parity for character in Register AL. dopar: push bx mov bx,portval mov bl,[bx].parflg ; get parity flag byte cmp bl,parnon ; No parity? je parret ; Just return and al,07FH ; Strip parity. Same as Space parity cmp bl,parspc ; Space parity? je parret ; e = yes, then we are done here cmp bl,parevn ; Even parity? jne dopar0 ; ne = no or al,al jpe parret ; pe = even parity now xor al,080H ; Make it even parity. jmp short parret dopar0: cmp bl,parmrk ; Mark parity? jne dopar1 ; ne = no or al,080H ; Turn on the parity bit. jmp short parret dopar1: cmp bl,parodd ; Odd parity? or al,al jpo parret ; Already odd, leave it. xor al,080H ; Make it odd parity. parret: pop bx ret inicpt proc near mov capbp,offset capbuf mov caplft,cptsiz ; init buffer ptr & chrs left ret ; and return inicpt endp cptchr proc near ; capture routine, char in al push di mov di,capbp mov byte ptr [di],al inc capbp pop di dec caplft ; decrement chars remaining jg cptch1 ; more room, forget this part call cptdmp ; dump the info call inicpt ; re-init ptrs. cptch1: ret ; and return cptchr endp cptdmp proc near ; empty the capture buffer push ax push bx push cx push dx mov dx,offset capbuf ; the capture routine buffer mov bx,cptio.handle ; get file handle mov cx,cptsiz ; original buffer size sub cx,caplft ; minus number remaining jl cptdm2 ; means error mov ah,write2 ; write with filehandle int dos ; write out the block jnc cptdm1 ; carry set means error cptdm2: and targ.flgs,not capt ; so please stop capturing mov dx,offset erms23 ; tell user the bad news mov ah,prstr int dos cptdm1: pop dx pop cx pop bx pop ax ret cptdmp endp clscpt proc near test flags.capflg,0FFH ; doing capture jnz clscp1 ; yes, go ahead mov dx,offset erms22 mov ah,prstr int dos jmp rskp clscp1: mov ah,cmcfm call comnd jmp r clscpi: mov ax,caplft cmp ax,cptsiz ; is buffer empty? je clscp2 ; yes, forget this stuff call cptdmp ; dump buffer (preserves registers) clscp2: mov ah,close2 ; DOS 2.0 close push bx mov bx,cptio.handle ; file handle int dos ; close up file pop bx mov flags.capflg,logoff ; no longer capturing... jmp rskp ; and return clscpt endp ; enter with ax/scan code to define, si/ pointer to definition, cx/ length ; of definition. Defines it in definition table. defkey proc near ret defkey endp ; worker: copy line from si to di, first removing trailing spaces, second ; parsing out curly braced strings, then third converting \{b##} in strings ; to binary numbers. Returns carry set if error; else carry clear, with byte ; count in cx. Braces are optional but must occur in pairs. ; Items which cannot be converted to legal numbers are copied verbatium ; to the output string (ex: \{c} is copied as \{c} but \{x0d} is hex 0dh). cnvlin proc near push ax push si ; source ptr push di ; destination ptr push es ; end of save regs push ds ; move ds into es pop es ; use datas segment for es:di call cnvstr ; trim trailing, parse curly braces xor cx,cx ; initialize returned byte count cnvln1: cmp byte ptr [si],0 ; at end of string? je cnvln2 ; e = yes, exit call katoi ; read char, convert ascii to binary cld stosb ; save the char inc cx ; and count it or ah,ah ; is returned number > 255? jz cnvln1 ; z = no, do more chars push ax stosb ; save high order byte next pop ax inc cx jmp short cnvln1 ; do more chars cnvln2: mov byte ptr [di],0 ; plant terminator clc ; clear c bit, success cnvlnx: pop es ; restore regs pop di ; destination ptr pop si ; source ptr pop ax ret cnvlin endp ; Convert string by first remove trailing spaces and then removing surrounding ; curly brace delimiter pair. Converts text in place. ; Enter with source ptr in si. ; Preserves all registers, uses byte tmp. 9 Oct 1987 [jrd] ; cnvstr proc near push ax push cx push dx push si ; save start of source string push di push es ; 1. Trim trailing spaces mov dx,si ; source address call strlen ; get current length to cx jcxz cnvst4 ; z = nothing there mov di,si ; set di to source address add di,cx ; start at end of string dec di ; ignore terminator mov al,spc ; scan while spaces push ds pop es ; set es to datas segment std ; search backward repe scasb ; scan off trailing spaces mov byte ptr [di+2],0 ; terminate string after last text cld mov di,si ; set destination address to source ; 2. Parse off curly brace delimiters cmp byte ptr [si],braceop ; opening brace? jne cnvst4 ; ne = no, ignore brace-matching code inc si ; skip opening brace mov dl,braceop ; opening brace (we count them up) mov dh,bracecl ; closing brace (we count them down) mov tmp,1 ; we are at brace level 1 cnvst1: cld ; search forward lodsb ; read a string char stosb ; store char (skips opening brace) cmp al,0 ; at end of string? je cnvst4 ; e = yes, we are done cmp al,dl ; an opening brace? jne cnvst2 ; ne = no inc tmp ; yes, increment brace level jmp short cnvst1 ; and continue scanning cnvst2: cmp al,dh ; closing brace? jne cnvst1 ; ne = no, continue scanning dec tmp ; yes, decrement brace level cmp byte ptr [si],0 ; have we just read the last char? jne cnvst3 ; no, continue scanning mov tmp,0 ; yes, this is the closing brace cnvst3: cmp tmp,0 ; at level 0? jne cnvst1 ; ne = no, #opening > #closing braces mov byte ptr [di-1],0 ; plant terminator on closing brace cnvst4: pop es ; recover original registers pop di pop si pop dx pop cx pop ax ret cnvstr endp ; Convert ascii strings of the form "\{bnnn}" to a binary word in ax. ; The braces are optional but must occur in pairs. Numeric base indicator "b" ; is O or o or X or x or D or d or missing, for octal, hex, or decimal (def). ; Enter with si pointing at "\". ; Returns binary value in ax with carry clear and si to right of "}" or at ; terminating non-numeric char if successful; otherwise, a failure, ; return carry set with si = entry value + 1 and first read char in al. katoi proc near cld lodsb ; get first char xor ah,ah ; clear high order field push cx ; save working reg push si ; save entry si+1 push bx push ax ; save read char katoi1: cmp al,0 ; end of text? je katoi1a ; e = yes, exit failure cmp al,'\' ; escape char? je katoi1b ; e = yes katoi1a:jmp katoix ; common jump point to exit failure katoi1b:lodsb ; get next char, maybe brace cmp al,0 ; premature end? je katoi1a ; e = yes, exit failure xor bx,bx ; no conv yet, assume no opening brace cmp al,braceop ; opening brace? jne katoi2 ; ne = no, have number or base mov bl,bracecl ; remember a closing brace is needed lodsb ; get number base, if any katoi2: xor cx,cx ; temporary place for binary value mov nbase,10 ; assume decimal numbers cmp al,0 ; premature end? je katoix ; e = yes, exit failure cmp al,'a' ; lower case? jb katoi3 ; b = no cmp al,'z' ; in range of lower case? ja katoi3 ; a = no and al,5fh ; map to upper case katoi3: cmp al,'O' ; octal? jne katoi4 ; ne = no mov nbase,8 ; set number base jmp short katoi6 katoi4: cmp al,'X' ; hex? jne katoi5 ; ne = no mov nbase,16 jmp short katoi6 katoi5: cmp al,'D' ; decimal? jne katoi7 ; ne = no base char, assume decimal mov nbase,10 katoi6: lodsb ; get a digit katoi7: cmp al,0 ; premature end? je katoi8a ; e = yes, use it as a normal end cmp al,bl ; closing brace? je katoi9 ; e = yes call cnvdig ; convert ascii to binary digit jc katoi8 ; c = cannot convert inc bh ; say we did a successful conversion xor ah,ah ; clear high order value push ax ; save this byte's value xchg ax,cx ; put binary summation in ax mul nbase ; scale up current sum xchg ax,cx ; put binary back in cx pop ax ; recover binary digit add cx,ax ; form running sum jmp short katoi6 ; get more katoi8: cmp bl,0 ; closing brace needed? jne katoix ; ne = yes, but not found katoi8a:dec si ; backup to reread terminator katoi9: cmp bh,0 ; did we do any conversion? je katoix ; e = no, exit failure pop ax ; throw away old saved ax pop bx ; restore bx pop ax ; throw away starting si, keep current mov ax,cx ; return final value in ax pop cx ; restore old cx clc ; clear carry for success ret katoix: pop ax ; restore first read al pop bx pop si ; restore start value + 1 pop cx ; restore old cx stc ; set carry for failure ret katoi endp cnvdig proc near ; convert ascii code in al to binary push cx ; return carry set if cannot. push es ; nbase has numeric base. push di push ax cmp al,'a' ; lower case? jb cnvdig1 ; b = no cmp al,'f' ; highest hex digit ja cnvdigx ; a = illegal symbol sub al,'a'-'A' ; convert 'a' to 'f' to upper case cnvdig1:mov di,offset numset ; set of legal number symbols mov cx,nbase ; number of legal symbols in this base cmp cx,cx ; preset z flag push ds pop es ; point es at data segment cld ; scan forward repne scasb ; find character in set jne cnvdigx ; ne = not found cnvdig2:inc cx ; offset auto-dec of repne scasb above sub cx,nbase ; counted off minus length neg cx ; two's complement = final value pop ax ; saved ax mov ax,cx ; return binary in al clc ; c clear for success jmp short cnvdixx ; exit cnvdigx:stc ; c set for failure pop ax cnvdixx:pop di pop es pop cx ret cnvdig endp decout proc near ; display decimal number in ax push ax push cx push dx mov cx,10 ; set the numeric base call valout ; convert and output value pop dx pop cx pop ax ret decout endp valout proc near ; output number in ax using base in cx ; corrupts ax and dx xor dx,dx ; clear high word of numerator div cx ; (ax / cx), remainder = dx, quotient = ax push dx ; save remainder for outputting later or ax,ax ; any quotient left? jz valout1 ; z = no call valout ; yes, recurse valout1:pop dx ; get remainder add dl,'0' ; make digit printable cmp dl,'9' ; above 9? jbe valout2 ; be = no add dl,'A'-1-'9' ; use 'A'--'F' for values above 9 valout2:mov ah,conout int dos ret valout endp ; Convert input in buffer pointed to by SI to real number which is returned ; in AX. Enter with string size in AX. ; Return on failure, return skip on success. Revised by [jrd] ATOI PROC NEAR mov bx,0 ; high order of this stays 0 mov tmp,0 ; No input yet mov cl,ah ; Number of chars of input. mov ch,0 ; size of string mov ax,0 ; init sum cld atoi0: jcxz atoi4 ; Fail on no input. lodsb ; get an input char dec cx ; count number remaining cmp al,';' ; start of comment? jne atoi0a ; ne = no dec si ; backup to semicolon mov byte ptr [si],0 ; plant terminator xor cx,cx ; clear bytes remaining field jmp atoi4 ; and exit atoi0a: cmp al,'0' ; got punctuation? jb atoi0 ; e = yes, keep scanning dec si ; back up source pointer for reread below. inc cx ; and readjust byte counter mov ax,0 ; clear sum atoi1: push ax ; save sum lodsb ; read a byte into al mov bl,al ; put it into bl pop ax ; regain sum cmp bl,'9' ; check range for '0' to '9' ja atoi2 ; above '9' cmp bl,'0' jb atoi2 ; below '0' sub bl,'0' ; take away ascii bias mul ten ; sum * 10. dx = high, ax = low add ax,bx ; add current value mov tmp,1 ; say have sum being computed loop atoi1 inc si ; inc for dec below atoi2: dec si ; point at terminator cmp tmp,0 ; were any digits discovered? je atoi4 ; No so fail jmp rskp ; success exit, sum is in ax atoi4: mov dx,offset erms25 ; Input must be numeric ret ATOI ENDP ; 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