	PAGE 59, 132

	TITLE MSTERM -- Terminal emulation module

; Update 9 Jan 86

IF1
 %OUT >> Starting pass 1
ELSE
 %OUT >> Starting pass 2
ENDIF

    PUBLIC clscpt, defkey, cptfcb, inicpt, clscpi, telnet, Allow_blast
    PUBLIC dopar, shokey, prkey, Telnet2, Quit_emulator

    INCLUDE MsDefs.H

DataS 	SEGMENT	PUBLIC 'DataS'

    EXTRN flags:byte, trans:byte, DTA:BYTE, portval:word
    EXTRN Preload_buffer:BYTE, Preload_flag:BYTE, TakLev:BYTE, TakAdr:WORD

    EVEN

temp	dw	0
temp1   dw      ?               ; Temporary storage.
temp2   dw      ?               ; Temporary storage.
ssp	dw	0		; Save SP in Telnet.

SCNTLEN	EQU 200		; MAX # OF DEFINITIONS ONE can have
defbsiz	EQU 400		; combined length of all definitions...

scntab	dw	scntlen dup (?)	; scan codes redefined
deftab	dw	scntlen dup (?) ; pointer to definition strings
defptr	dw	defbuf		; pointer starts at beginning
deflen	dw	defbsiz		; amt of space left in buffer

targ	termarg	<0,1,80,24,cptchr,2dch,0,scntab,deftab,0,,parnon>
crlf    db      cr,lf,'$'
tmp	db	?,'$'

erms22	db	'? No capture file open', Cr, Lf, '$'
esctl	db	'Control-$'         ; [6]

inthlp  db	' ?      Type this message',cr,lf
	db	' Space  Return to terminal emulation',cr,lf
	db	' B      Send a Break to the host',cr,lf
	db	' C      Close the connection (return to command mode)',cr,lf
	db	' M      Toggle Mode line (turn it on or off)',cr,lf
	db	' Q      Quit logging to file',cr,lf
	db	' R      Resume logging to file',cr,lf
	db	' S      Show Status of the connection',cr,lf
	db	' 0      Send a NUL to the host',cr,lf
	db	' Typing the escape character will send it to the host'
	db	0

intprm	db	'                   Press an action key, or Space to continue$'

sttmsg	db	'                            Press any key to continue$'

CPTFCB	DB	25H DUP (?)
CAPBUF	DB	200 DUP (?)
CAPBP	DW	?
CAPLFT	DB	?
Allow_blast DB 0		; Flag that rapid-fire screen updates are OK

defbuf	db	defbsiz dup (?)
shkmsg	db	cr,lf,'Press key: $'

DataS	ENDS

Code	SEGMENT	PUBLIC

	extrn 	comnd:near, outchr:near, stat0:near
	extrn	escprt:near, clrbuf:near, term:near
	extrn	cmblnk:near, locate:near, prtchr:near
	extrn	beep:near, puthlp:near, Close_Screen:near
	extrn	serini:near, sendbr:near, showkey:near
	EXTRN	PutMod:NEAR, Simulate_port_char:NEAR

    ASSUME cs:Code, ds:DataS

Quit_emulator DB ?		; Flag to quit back to command mode

; the show key command.

shokey	proc	near
	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
	push	ax
	push	cx		; save results
	mov	dx,offset crlf
	mov	ah,prstr
	int	dos
	pop	cx
	pop	ax
	call	prkey		; print the buffer
	mov	dx,offset crlf
	mov	ah,prstr
	int	dos
	jmp	rskp		; and return
shokey	endp

; pass a string pointer in ax, length in cx.
; Prints the string, quoting any unprintables, except crlf.

prkey	proc	near
	mov	si,ax		; copy string ptr
	jcxz	prke6		; no string, stop here
	cld			; Forwards
prke1:	push	cx		; save counter
	lodsb			; get a byte
	and	al,7fH		; only consider low-order 7 bits.
	cmp	al,' '		; printable?
	jb	prke2		; no, print the hard way
	cmp	al,7fH		; maybe a delete?
	jne	prke4		; no, can just put into string
prke2:	jcxz	prke3		; last char, can't be crlf
	cmp	al,cr		; carriage return?
	jne	prke3		; no, go on
	cmp	byte ptr [si],lf ; followed by linefeed?
	jne	prke3
	mov	ah,prstr
	mov	dx,offset crlf
	int	dos		; else just print crlf
	inc	si		; skip over lf
	pop	cx		; careful...
	dec	cx
	push	cx
	jmp	short prke5
prke3:	push	ax		; preserve the char
	mov	ah,conout
	mov	dl,'\'
	int	dos		; print the quote character
	pop	ax
	call	proct		; print the octal byte
	jmp	short prke5
prke4:	mov	dl,al		; normal char, just print it
	mov	ah,conout
	int	dos
prke5:	pop	cx		; restore count
	loop	prke1
prke6:	ret			; and return
prkey	endp

; print the byte in al as an octal number
proct	proc	near
	mov	dl,al			; get the byte
	and	dl,7h			; keep low-order byte
	mov	cl,3
	shr	al,cl			; shift to get next digit
	jz	proc1			; 0, no more to print
	push	dx			; else save current digit
	call	proct			; print rest
	pop	dx
proc1:	mov	ah,conout
	add	dl,'0'			; make printable
	int	dos
	ret
proct	endp

;	This is the CONNECT command.
 
TELNET 	PROC	NEAR
	mov ah,cmcfm
	call comnd		; Get a confirm.
	 jmp r			;  Didn't get a confirm.


; Pre-confirmed entry to connect command ...

Telnet2:
	mov cs:Quit_emulator, 0	; We haven't quit yet
	mov Preload_flag, 0	; There is no preloaded command yet

	mov al,targ.flgs	; get present flags
	and al,modoff		; this is only one we can keep around
	or al,havtt		; defaults (!)
	cmp flags.debug,0	; debug mode?
	jz tel0			; no, keep going
	or al,trnctl		; yes, show control chars
tel0:	cmp flags.vtflg,0	; vt52 emulation?
	jz tel1
	or al,emheath
tel1:	mov bx,portval
	cmp [bx].ecoflg,0	; echoing?
	jz tel2
	or al,lclecho
tel2:	mov targ.flgs,al	; store flags
	mov ah,flags.comflg
	mov targ.prt,ah		; Port 1 or 2
	mov ah,trans.escchr
	mov targ.escc,ah
	mov ah,[bx].parflg
	mov targ.parity,ah
	mov ax,[bx].baud
	mov targ.baudb,al
	mov ah,flags.capflg
	and ah,capt
	or targ.flgs,ah
	call serini		; init serial port

tem:	mov ax,offset targ	; Point to terminal arguments
	call term
	or targ.flgs,scrsam	; assume screen is the same.

intchr:	test cs:Quit_emulator, 1 ; Flag to quit completely?
	 jnz intch0		;  Yes, don't need another char

	mov ah, ConInQ		; Quietly read a character
	int Dos

	mov ah,al

	cmp ah,' '		; Is it a space?
	 je tem			;  Yes, go back to terminal emulation

	mov bh,ah		; Save the actual char.
	and ah,not ('a'-'A')	; Convert to upper case.

	cmp ah,'C'		; Is it close?
	jne intch1

	call Close_Screen	; Tell emulator to shut down

intch0:	call Check_for_preloaded_command ; Did host tell us to do something?
	jmp rskp		; and return

intch1: cmp ah,'S'		; Is it status?
	jnz intch2

	call stat0		; If so, call stat0.
	call puthlp		; put help on screen
	mov dx,offset sttmsg
	call PutMod		; Display a message in the mode line

	mov ah, ConInQ		; Quietly read a character
	int Dos

	and targ.flgs,not scrsam ; remember screen changed.
	jmp tem

intch2: cmp ah,'B'		; Send a break? [20g]
	jne intch3		; No. [20g]
	call sendbr		; Yes, so send a break. [20g]
	jmp tem			; And return.  [20g]
intch3:	cmp ah,'M'		; mode line?
	jne intch4
	xor targ.flgs,modoff	; toggle mode line
	jmp tem			; and reconnect

 %OUT >> About half through source file

intch4:	cmp bh,'?'		; Is it help?
	jne intch5		; If not, go to the next check.
	mov ax,offset inthlp	; If so, get the address of the help message.
	call puthlp		; write help msg
	mov dx,offset intprm
	call PutMod		; Display a message in the mode line
	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		;  No, try something else

intch6: mov ah,al		; Escape char typed twice, send it out
	call outchr
	 nop
	 nop
	 nop
	jmp tem			; Return, we are done here.

intch7:	cmp ah,'Q'		; maybe want to stop logging?
	 jne intch8

	test targ.flgs,capt	; not capturing, can't do this
	jz intc10
	and targ.flgs,not capt ; stop capturing
	jmp tem			; and resume

intch8:	cmp ah,'R'		; maybe resume?
	 jne intch9		; no, keep going

	cmp flags.capflg,0	; can we capture?
	 jz intc10		; no, forget it

	test targ.flgs,capt	; already capturing?
	 jnz intc10		; 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:	jmp tem			; Wrong key, just give up

TELNET  ENDP


; Check for preloaded command ... if there is one, set it up as a macro,
;  turning single commas into Cr's, and pushing the result onto the TAKE stack

Check_for_preloaded_command PROC

	cmp Preload_flag, 0	; Is there a preloaded command to be done?
	 je CPC_ret		;  No

	cld			; Forwards
	inc TakLev		; Bump take level
	add takadr, SIZE takinfo ; Address new take frame
	mov bx, TakAdr		; Get TAKE frame addr into bx
	mov BYTE PTR [bx].TakFcb, 0FFh ; Mark as a macro
	mov si, OFFSET Preload_buffer ; Pointer to where preloaded command is
	lea di, [bx].TakBuf	; Put it in the TAKE buffer
	sub cx, cx		; Clear counter

CPC_1:	lodsb			; Get a byte
	cmp al, ','		; Comma?
	 jne CPC_3		;  No

	mov ah, BYTE PTR [si]	; Peek at the next char
	cmp ah, ','		; Is it also a comma?
	 je CPC_2

	mov al, Cr		; Convert single comma to a Cr
	jmp SHORT CPC_3		; Store it

CPC_2:	inc si			; Skip over the "peeked" second comma

CPC_3:	stosb			; Deposit char
	inc cx			; Count this char
	or al, al		; Hit end?
	 jne CPC_1		;  Yes
	
	dec cx			; Uncount the null at the end
	lea ax, [bx].takbuf	; Pick up ptr to start of TAKE buffer
	mov [bx].takptr, ax	; Init buffer ptr
	mov [bx].takchl, cl	; Chars remaining
	mov [bx].takcnt, cx	;  and all chars
	mov [bx].takcnt+2, 0	; Clear high order half

CPC_ret:
	ret			; Done here

Check_for_preloaded_command ENDP

; Set parity for character in Register AL.

dopar:	push bx
	mov bx,portval
	cmp [bx].parflg,parnon	; No parity?			[10 start]
	je parret		; Just return
	cmp [bx].parflg,parevn	; Even parity?
	jne dopar0
	and al,07FH		; Strip parity.
	jpe parret		; Already even, leave it.
	or al,080H		; Make it even parity.
	jmp parret

dopar0:	cmp [bx].parflg,parmrk	; Mark parity?
	jne dopar1
	or al,080H		; Turn on the parity bit.
	jmp parret

dopar1:	cmp [bx].parflg,parodd	; Odd parity?	
	jne dopar2
	and al,07FH		; Strip parity.
	jpo parret		; Already odd, leave it.
	or al,080H		; Make it odd parity.
	jmp parret

dopar2: and al,07FH		; Space parity - turn off parity bit.

parret:	pop bx
	ret					; [10 end]

inicpt	proc	near
	mov	capbp,offset capbuf
	mov	caplft,128		; init buffer ptr & chrs left
	ret				; and return
inicpt	endp


;   Capture routine, char in al

CptChr	PROC
	push	di
	mov	di,capbp
	mov	byte ptr [di],al
	inc	di
	mov	capbp,di		; restore pointer
	pop	di
	dec	caplft			; decrement chars remaining
	 jnz	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	dx
	mov	ah,setdma
	mov	dx,offset capbuf	; the capture routine buffer
	int	dos
	mov	ah,writef
	mov	dx,offset cptfcb
	int	dos			; write out the block

;*** must be fixed... check error returns, disable capturing,
;*** figure out how to put dma address back

	mov	dx,offset DTA
	mov	ah,setdma
	int	dos			; put dma back

	pop	dx
	pop	ax
	ret

cptdmp	ENDP

clscpt	PROC

	mov	ah,cmcfm
	call	comnd
	 jmp	r

	test	flags.capflg,0FFH	; doing capture
	 jnz	clscpi			; yes, go ahead

	mov	dx,offset erms22
	mov	ah,prstr
	int	dos

	jmp	rskp

clscpi:	mov	al,'Z'-64		; control-z for eof...
	call	cptchr			; output to file
	mov	al,caplft
	cmp	al,128			; is buffer empty?
	je	clscp2			; yes, forget this stuff
	call	cptdmp			; dump buffer (preserves registers)
clscp2:	mov	ah,0
	sub	word ptr cptfcb+16,ax	; subtract remaining from low filsize
	sbb	word ptr cptfcb+18,0	; and from high size (with borrow)
	mov	ah,closf
	mov	dx,offset cptfcb
	int	dos			; close up file
	mov	flags.capflg,0		; 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.
;*** somewhere should check for overflow etc of defbuf, and of scntab
defkey	proc	near
	push	ax		; save scan code
	cld			; Forwards
	mov	ax,ds
	mov	es,ax		; address data segment
	mov	di,defptr	; this is where the def gets built
	inc	di		; leave a byte for length
defk1:	lodsb			; get a byte from the source
	cmp	al,'\'		; escape?
	jne	defk2		; no, just deposit him
	dec	cx		; count available is one less
	call	trnesc		; translate the escape sequence
	inc	cx		; account for '\' (loop will decrement again).
defk2:	stosb			; drop off character
	loop	defk1		; and keep going while we have more
	mov	ax,di		; get ptr to end
	dec	ax		; back up pointer to end
	mov	si,defptr	; pick up old ptr value
	sub	ax,si		; this is actual length used
	mov	byte ptr [si],al ; fill in length of entry
	mov	defptr,di	; this is next free byte
; definition address is in si
	pop	ax		; recover scan code
	mov	cx,targ.klen	; length of scan table
	jcxz	defk4		; not there, just go add it
	mov	di,offset scntab ; the scan code table
	repne	scasw		; look for this one
	jne	defk4		; not defined already
	sub	di,offset scntab + 2 ; compute index into table
	mov	deftab[di],si	; fill in address
	ret			; and return

defk4:	mov	di,targ.klen	; get length again
	inc	di
	cmp	di,scntlen
	ja	defk5		;** ignore def if over size

	mov	targ.klen,di	; update length
	shl	di,1		; double for word index
	mov	scntab[di-2],ax	; put scan code into table
	mov	deftab[di-2],si	; and fill in definition

defk5:	ret			; that's it

defkey	ENDP


; enter with si/ source pointer, cx/ count
; converts an escape sequence, updates all pointers

trnesc	proc
	push	bx
	push	dx		; preserve these
	mov	al,0		; this is current accumulation
	jcxz	trnes2		; empty string, forget it
	mov	bl,3		; this is max # of digits to use
	mov	bh,8		; this is radix

trnes1:	mov	dl,[si]
	cmp	dl,'0'
	jb	trnes2		; out of range, stop here

	cmp	dl,'7'
	ja	trnes2

	inc	si		; accept character
	sub	dl,'0'		; convert to binary
	mul	bh		; shift accumulation
	add	al,dl		; add to accumulation
	dec	bl		; decrement digit counter
	loopnz	trnes1		; and keep trying

trnes2:	pop	dx
	pop	bx
	ret			; and return

trnesc	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

