name	msxrb1
; File msxrb1.asm
	include	mssdef.h
;       Copyright (C) 1982,1991, Trustees of Columbia University in the
;       City of New York.  Permission is granted to any individual or
;       institution to use, copy, or redistribute this software as long as
;       it is not sold for profit and this copyright notice is retained.
; Kermit system dependent module for Rainbow
; Jeff Damens, July 1984
;  with additional major changes by Joe Doupnik, 1986, 1987, 1988, 1989, 1991
; Edit history:
; 2 March 1991 version 3.10
; Last edit 1 March 1991
; 27-AUG-1990 [rhw]
; 27-AUG-1990	I had added shr cx,1 to divide the CX=swidth by 2
;		but swidth is a const so I removed the shifts & simply
;		divided the const by 2.	 This saves an shift in many places.
;		[rhw-5] Robert H. Weiner
; 25-AUG-1990	Replaced MOVSB with MOVSW for all screen ops
;		Divide counts by 2 1st for byte to word conversion
;		Since swidth is always even, this can save alot of cycles:
;		REP MOVSB -> MOVSW should save 17*132/2=1122 cycles for
;		swidth char moves since iteration count is cut in half.
;		[rhw-4] Robert H. Weiner
; 24-AUG-1990	Added print screen character XLAT table to make all the
;		VT100 char graphics come out on a normal printer, xlat_tab[]
;		Added same XLAT code to dumpscr code since that too is
;		a real mess without it.  Now linedraw boxes look like boxes.
;		Added SETCHTAB definition that's now required in machine
;		specific modules
;		[rhw-3] Robert H. Weiner
; Present-
; 01-JUL-1990	MSYIBM.ASM screen rollback routines merged into MSXRB1.ASM
;		This should speed up rollback screen handling
;		Sorry, this code left out of this source release since its
;		presently incomplete.
;		[rhw-2] Robert H. Weiner
;    MAY-1990	connect mode hangup (^]-H) fixed (needed delay) RHW/JRD
;		[rhw-1] Robert H. Weiner (robert%progplus.uucp@uunet.uu.net)
; 30-Apr-1990	fix baud rate return	[gbs]
;		convert unprintable characters to "." in print screen
; 14-Feb-1990	fix VT102 initialization.  gbs
; 11 Nov Revise for MS Kermit version 3. jrd
; 11-Nov-1989 reversed print controller/autoprint.
;		Had it wrong the first time.
;		Gary B. Stebbins
; 18-Oct-1989 added minimal print controller support & autoprint
;		(module needs cleanup in incoming character handling)
;		(print stuff doesn't handle DECEXT or DECPFF)
;		(really kludged in...should be rewritten similar to
;		 input character handling for IBM PC)
;		Gary B. Stebbins
;  4-Mar-1989 several problems related to modem signal handling fixed.
;		Gary B. Stebbins, Don Metz
; 1 July 1988 Version 2.31
; 10 Jan 1988 Cleanup 8 bit display in outtty. [jrd]
; 1 Jan 1988 version 2.30
 
	public	serini, serrst, clrbuf, outchr, coms, vts, vtstat, dodel
	public	ctlu, cmblnk, locate, lclini, prtchr, dobaud, clearl
	public	getbaud, beep, pcwait, dumpscr, termtb, shomodem
	public	count, xofsnt, puthlp, putmod, clrmod, poscur, getmodem
	public	sendbr, sendbl, term, machnam, setktab, setkhlp, showkey
	public	ihosts, ihostr, serhng, dtrlow, comptab, baudst
			; action verb procedures for keyboard translator
	public	prvscr, nxtscr, prtscn
	public	uparrw, dnarrw, rtarr, lfarr, pf1, pf2, pf3, pf4
	public	kp0, kp1, kp2, kp3, kp4, kp5, kp6, kp7, kp8, kp9
	public	kpminus, kpcoma, kpenter, kpdot, chrout, cstatus, cquit
	public	cquery, prvscr, nxtscr, prvlin, nxtlin, trnprs, snull
	public	nxttop, nxtbot, klogon, klogof, cdos, chang
	public	portval, bdtab, setchtab

; rainbow-dependent screen constants
 
scrseg	equ	0ee00H		; screen segment
latofs	equ	0ef4h		; ptrs to line beginnings, used by firmware
l1ptr	equ	latofs		; ptr to first line
llptr	equ	latofs+23*2	; ptr to last line
csrcol	equ	0f41h		; current cursor column
csrlin	equ	0f42h		; current cursor line
curlin	equ	0f43h		; current line flags
wrpend	equ	2		; wrap pending
attoffs equ	1000H
rmargin equ	0f57h		; right margin limit
 
; rainbow-dependent firmware locations
nvmseg	equ	0ed00h		; segment containing NVM
xmitbd	equ	0a1h		; address of xmit baud
rcvbd	equ	0a2h		;   "	  "  receive baud
autwrp	equ	08dH		; b0 = 1 if auto wrap on (?)
newlmod equ	08eh		; b0 = 0 lf, = 1 newline (cr/lf)
bdprt	equ	06h		; baud rate port
vt52mod equ	088h		; b0 = 1 if in ansi mode
off	equ	0
bufon	equ	1		; buffer level xon/xoff on-state control flag
usron	equ	2		; user level xon/xoff on-state control flag
 
mntrgh	equ	bufsiz*3/4	; High point = 3/4 of buffer full
mntrgl	equ	bufsiz/4	; Low point = 1/4 buffer full
 
mnstata equ	042H		; Status/command port A
mnstatb equ	043H		; Status/command port B
mndata	equ	040H		; Data port
mndatb	equ	041H
mnctrl	equ	002H		; Control port
serchn	equ	0A4H		; interrupt to use
serch1	equ	044H		; use this too for older rainbows
 
txrdy	EQU	04H		;Bit for output ready
rxrdy	EQU	01H		;Bit for input ready
 
fastcon equ	29H		; fast console handler
firmwr	equ	18H		; Bios interrupt
kcurfn	equ	8h		; disable cursor
rcurfn	equ	0ah		; enable cursor
 
swidth	equ	132		; screen width
slen	equ	24		; screen length
npages	equ	10		; for use with dynamic memory allocation
 
stbrk	equ	15		; start sending a break
enbrk	equ	16		; stop sending break
 
; external variables used:
; flags - global flags as per flginfo structure defined in pcdefs
; trans - global transmission parameters, trinfo struct defined in pcdefs
; portval - pointer to current portinfo structure (currently either port1
;    or port2)
; port1, port2 - portinfo structures for the corresponding ports
 
; global variables defined in this module:
; xofsnt, xofrcv - tell whether we saw or sent an xoff.
 
; circular buffer ptr
cbuf	struc
pp	dw	?			; place ptr in buffer
bend	dw	?			; end of buffer
orig	dw	?			; buffer origin
lcnt	dw	0			; # of lines in buffer
lmax	dw	?			; max lines of buffer
cbuf	ends
 
; answerback structure
ans	struc
anspt	dw	?			; current pointer in answerback
ansct	db	?			; count of chars in answerback
ansseq	dw	?			; pointer to whole answerback
anslen	db	?			; original length
ansrtn	dw	?			; routine to call
ans	ends
 
; structure for status information table sttab.
stent	struc
sttyp	dw	?		; type (actually routine to call)
msg	dw	?		; message to print
val2	dw	?		; needed value: another message, or tbl addr
tstcel	dw	?		; address of cell to test, in data segment
basval	dw	0		; base value, if non-zero
stent	ends
 
data	segment public 'data'
	extrn	flags:byte, trans:byte, filtst:byte, dmpname:byte
	extrn	rxtable:byte, kbdflg:byte, repflg:byte, diskio:byte
	extrn	ttyact:byte, denyflg:word, prnhand:word
 
; [rhw-3] Start Definitions for Print Screen Translation Table
xlat_tab_size	equ	32	; chars 00 through 31 decimal
	; 0=null, 1=diamond, 2=blob, 3=HT, 4=FF, 5=CR, 6=LF, 7=degree, 8=+/-
	; 9=NL, 10=VT, 11=low_rt_corner, 12=up_rt_corner, 13=up_left_corner,
	; 14=low_left_corner, 15=cross, 16...20=horiz_line_scan1,3,5,7,9
	; 21=left_t, 22=right_t, 23=bott_t, 24=top_t, 25=vertical, 26=<=
	; 27=>=, 28=pi, 29=<>, 30=pound, 31=centered_dot
		;	 0 ,  1 ,  2 ,  3 ,  4 ,  5 ,  6 ,  7
xlat_tab	db	' ', '*', '#', '.', '.', '.', '.', 'o'
		;	 8 ,  9 ,  10,  11,  12,  13,  14,  15
		db	'+', '.', '.', '+', '+', '+', '+', '+'
		;	 16,  17,  18,  19,  20,  21,  22,  23
		db	'_', '_', '-', '-', '-', '+', '+', '+'
		;	 24,  25,  26,  27,  28,  29,  30,  31
		db	'+', '|', '<', '>', 'p', '!', '#', '.'
; [rhw-3] End Print Screen Translation Table

setchtab db	1			; [rhw-3] Set File Character-Set table
	mkeyw   'VT102',0		; [rhw-3] hardware default Code Page
;;	mkeyw   'User-defined',1        ; User loadable table


setktab db	0
akeyflg db	0		; non-zero if in alt keypad mode
ckeyflg db	0		; non-zero if cursor is in applications mode
ourflgs db	0		; our flags
fpscr	equ	80H		; flag definitions
vtautop equ	1		; autoprint enabled
vtcntp	equ	2		; controller print enabled
fairness dw	0		; keyboard/port sharing counter
dupflg	db	0		; full (0) or half (1) duplex on port
argadr	dw	0		; pointer to arguments from msster
crlf	db	cr,lf,'$'
setkhlp db	0
machnam db	'Rainbow$'
nyimsg	db	cr,lf,'Not yet implemented$'
dmperr	db	cr,lf,'?Cannot access screen-dump file$',cr,lf
hngmsg	db	cr,lf,' The phone should have hungup.',cr,lf,'$'
hnghlp	db	cr,lf,' The modem control lines DTR and RTS for the current'
	db	' port are forced low (off)'
	db	cr,lf,' to hangup the phone. Normally, Kermit leaves them'
	db	' high (on) when it exits.'
	db	cr,lf,'$'
msmsg1	db	cr,lf,' Modem is not ready: DSR is off$'
msmsg2	db	cr,lf,' Modem is ready:     DSR is on$'
msmsg3	db	cr,lf,' no Carrier Detect:  CD  is off$'
msmsg4	db	cr,lf,' Carrier Detect:     CD  is on$'
msmsg5	db	cr,lf,' no Clear To Send:   CTS is off$'
msmsg6	db	cr,lf,' Clear To Send:      CTS is on$'
rdbuf	db	swidth dup (?)		; temp buf
delstr	db	BS,BS,'  ',BS,BS,'$'	; Delete string
clrlin	db	cr,'$'		; Clear line (just the cr part)
oldser	dw	?		; old serial handler
oldseg	dw	?		; segment of above
old1ser dw	?		; old serial handler, alternate address
old1seg dw	?		; segment of same
portin	db	0		; Has comm port been initialized
mdstreg db	0		; Modem line status report for Show Modem
xofsnt	db	0		; Say if we sent an XOFF
xofrcv	db	0		; Say if we received an XOFF
parmsk	db	0ffh		; 7-8 bit parity mask
flowon	db	0		; flow on char (xon or null)
flowoff db	0		; flow off char (xoff or null)
mdmhand db	0		; Modem status register, current
iobuf	db	5 dup (?)	; buffer for ioctl
 
gopos	db	escape,'['
rowp	db	20 dup (?)
clrseq	db	escape,'[H',escape,'[J$'
ceolseq db	escape,'[K$'
invseq	db	escape,'[7m$'
nrmseq	db	escape,'[0m$'
ivlatt	db	swidth dup (0fH) ; a line's worth of inverse attribute
dumpbuf db	swidth+2 dup (?) ; screen dump work buffer
dumpsep db	FF,cr,lf	; screen dump image separator
dovt52	db	escape,'[?2l$'	; set VT52 mode
dovt102 db	escape,'<$'	; set VT102 mode
 
ourarg	termarg <>
comptab db	2			; communications port options
	mkeyw	'1',1
	mkeyw	'COM1',1		; only one option here
 
ontab	db	2
	mkeyw	'off',0
	mkeyw	'on',1
 
vttbl	db	3			; SET TERM table
	mkeyw	'Roll',10
	mkeyw	'VT102',ttvt100
	mkeyw	'VT52',ttvt52
 
termtb	db	2			; entries for Status, not Set
	mkeyw	'VT102',ttvt100
	mkeyw	'VT52',ttvt52
 
rolhlp	db	cr,lf,'  Roll (undo screen roll back before writing new'
	db	' chars, default=off)$'
 
vtrolst db	'Term rollback: $'
 
vtstbl	stent	<srchkw,vtrolst,ontab,vtroll>			; rollback
	dw	0		; end of table
 
 
 
; variables for serial interrupt handler
source	db	bufsiz DUP(?)	; Buffer for data from port
srcpnt	dw	source		; Pointer in buffer (DI)
count	dw	0		; Number of chars in int buffer
telflg	db	0		; non-zero if we're a terminal. NRU
ivec	dw	tranb		; transmit empty B
	dw	tranb		; status change B
	dw	tranb		; receive b
	dw	tranb		; special receive b
	dw	stxa		; transmit empty a
	dw	sstata		; status change a
	dw	srcva		; receive a
	dw	srcva		; special receive a
 
; baud rate definitions
; value is programmed into baud rate port
bdtab	db	16			; Baud rate table
	mkeyw	'50',0
	mkeyw	'75',1
	mkeyw	'110',2
	mkeyw	'134.5',3
	mkeyw	'150',4
	mkeyw	'200',5
	mkeyw	'300',6
	mkeyw	'600',7
	mkeyw	'1200',8
	mkeyw	'1800',9
	mkeyw	'2000',10
	mkeyw	'2400',11
	mkeyw	'3600',12
	mkeyw	'4800',13
	mkeyw	'9600',14
	mkeyw	'19200',15
 
; multi-screen stuff
twnd	cbuf	<>			; top screen spill-buffer struct
bwnd	cbuf	<>			; bottom screen spill buffer struct
topline dw	swidth dup (?)		; top line screen spill buffer
botline dw	swidth dup (?)		; bottom line screen spill buffer
rlbuf	dw	swidth dup (?)		; temp buffer for line scrolling
scrnbuf db	swidth*slen dup (?)	; save-screen, text
attrbuf db	swidth*slen dup (?)	; save-screen, attributes
srcseg	dw	0
 
topdwn	db	escape,'[H',escape,'M$' ; go to top, scroll down
botup	db	escape,'[24;0H',escape,'D$' ; go to bottom, scroll up
curinq	db	escape,'[6n$'	; cursor inquiry
posbuf	db	20 dup (?)	; place to store cursor position
gtobot	db	escape,'[24;0H$'	; go to bottom of screen
ourscr	dw	?
ourattr dw	?		; storage for screen and attributes
inited	db	0		; terminal handler not inited yet
dosmsg	db	'?Must be run in version 2.05 or higher$'
dmphand dw	?		; file handle for screen dump
anssq1	db	escape,'[c'
an1len	equ	$-anssq1
anssq2	db	escape,'Z'
an2len	equ	$-anssq2
eakseq	db	escape,'='
eaklen	equ	$-eakseq
dakseq	db	escape,'>'
daklen	equ	$-dakseq
cuapseq db	escape,'[?1h'
cuaplen equ	$-cuapseq
cunapseq db	escape,'[?1l'
cunaplen equ	$-cunapseq
crsseq	db	escape,'c'
crslen	equ	$-crsseq
enqseq	db	escape,'[6n'
enqlen	equ	$-enqseq
apenable db	escape,'[?5i'	;autoprint enable
apenlen equ	$-apenable
apdisabl db	escape,'[?4i'	;autoprint disable
apdislen equ	$-apdisabl
pcenable db	escape,'[5i'	;print controller enable
pcenlen equ	$-pcenable
pcdisabl db	escape,'[4i'	;print controller disable
pcdislen equ	$-pcdisabl
pscrn0	db	escape,'[i'	;print screen
psc0len equ	$-pscrn0
pscrn1	db	escape,'[0i'	;print screen
psc1len equ	$-pscrn1
 
ansbk1	ans	<anssq1,an1len,anssq1,an1len,sndans> ; two answerbacks
ansbk2	ans	<anssq2,an2len,anssq2,an2len,sndans>
ansbk3	ans	<eakseq,eaklen,eakseq,eaklen,enaaky> ; enable alt keypad
ansbk4	ans	<dakseq,daklen,dakseq,daklen,deaaky> ; disable alt keypad
ansbk5	ans	<crsseq,crslen,crsseq,crslen,sndspc> ; crash sequence (!)
ansbk6	ans	<enqseq,enqlen,enqseq,enqlen,ansenq>
ansbk7	ans	<cuapseq,cuaplen,cuapseq,cuaplen,cuapp> ; cursor application
ansbk8	ans	<cunapseq,cunaplen,cunapseq,cunaplen,cunapp>; cursor cursor
ansbk9	ans	<apenable,apenlen,apenable,apenlen,apon>; autoprint enable
ansbk10 ans	<apdisabl,apdislen,apdisabl,apdislen,apoff>;autoprint disable
ansbk11 ans	<pcenable,pcenlen,pcenable,pcenlen,prconon>;print control on
ansbk12 ans	<pcdisabl,pcdislen,pcdisabl,pcdislen,pconoff>;print control off
ansbk13 ans	<pscrn0,psc0len,pscrn0,psc0len,prtscn>	; print screen
ansbk14 ans	<pscrn1,psc1len,pscrn1,psc1len,prtscn>	; print screen
 
ansret	db	escape,'[?6c'
	db	10 dup (?)
ansrln	equ	$-ansret
vt52ret db	escape,'/Z'			; VT52 identification
vt52ln	equ	$-vt52ret
temp	dw	0
vtroll	db	0
 
port1	prtinfo <0FFFH,0,defpar,1,0,defhand,floxon,0>
portval dw	port1			; Default is to use port 1
 
;port initialization data for 7201 [ejz]
;enables Rx,Tx, CTS, DTR, 8 bits, no parity, 1.5 stop bits
prtpar	db	18H,14H,48H,13H,0C1H,15H,0EAH,11H,18H,00H

data	ends
 
code	segment public 'code'
	extrn	comnd:near, dopar:near, sleep:near, sbrk:near, isfile:near
	extrn	strlen:near, strcpy:near, msuinit:near, keybd:near
	extrn	statc:near, srchkw:near, pntchr:near, pntflsh:near
 
	assume	cs:code, ds:data, es:nothing

; local initialization routine, called by Kermit initialization.
 
lclini	proc	near
	mov	ah,dosver	; make sure this is DOS version 2.05 or higher
	int	dos
	xchg	al,ah			; put major version in ah, minor in al
	cmp	ax,205H			; is it 2.05?
	jae	lclin1			; yes, go on
	mov	dx,offset dosmsg
	call	tmsg
	cmp	flags.extflg,1		; exit now
	ret
lclin1: mov	flags.vtflg,ttvt100	; default to VT102
	call	setterm			; set terminal type to VT102
	call	msuinit			; initialize keyboard translator
	mov	ourscr,offset scrnbuf	; save-screen text buf address
	mov	ourattr,offset attrbuf	; save-screen attr buf address
 
	mov	ax,swidth*2*2		; ask for two lines (1 per buffer)
	call	sbrk			; allocate mem. Exit Kermit on failure
					;if we get here them we have the lines
	mov	bwnd.orig,ax		; memory segment, bottom window area
	mov	twnd.orig,ax		; top. same place for both buffers!
	push	es			; save this register
	mov	es,ax			; seg pointer to new memory block
	mov	bx,(swidth*slen*npages+7)/8 ; paragraphs wanted for roll back
	add	bx,24000D/16		; plus paragraphs to run Command.com
	mov	ah,setblk		; DOS Setblock. Ask for that space
	int	dos			; bx has # paragraphs available
	sub	bx,24000D/16		; deduct space for DOS 3.x Command.Com
	cmp	bx,(swidth*4+15)/16	; any room left for buffers?
	jae	lclyin2			; some space is available for buffers
	mov	bx,(swidth*4+15)/16	; else use our sbrk allocation
lclyin2:mov	ah,setblk		; ask for that many (bx) paragraphs
	int	dos			; Errors here == DOS deceived us
	pop	es			; restore reg
	mov	ax,bx			; bx = # paragraphs allocated by DOS
	mov	cl,3			; 2**3 = 8
	shl	ax,cl			; paragraphs to words (char + attrib)
	xor	dx,dx			; clear extended size
	mov	cx,swidth		; number of chars per line in buffer
	div	cx			; ax = number of lines in buffer
	mov	bwnd.lmax,ax		; max lines per buffer (quotient)
	mov	twnd.lmax,ax		; max lines per buffer
	add	cx,cx			; count char and attribute per item
	xor	dx,dx			; clear extended numerator
	mul	cx			; ax = effective # bytes per buffer
	dec	ax			; adjust for counting from zero
	mov	bwnd.bend,ax		; offset of last byte in buffer
	mov	twnd.bend,ax		; offset of last byte in buffer
	mov	bwnd.pp,0		; offset of first byte in buffer
	mov	twnd.pp,0		; offset of first byte in buffer
	mov	bwnd.lcnt,0		; number of lines occupied in buffer
	mov	twnd.lcnt,0		; number of lines occupied in buffer
	or	denyflg,tekxflg		; deny automatic Tektronix invokation
	ret
lclini	endp
 
; show the definition of a key.	 The terminal argument block (which contains
; the address and length of the definition tables) is passed in ax.
; Returns a string to print in AX, length of same in CX.
; Returns normally.
showkey proc	near
	ret
showkey endp
 
 
; SHOW MODEM, displays current status of lines DSR, CD, and CTS.
; Uses byte mdmhand, the modem line status register. [jrd]
shomodem proc	near
	mov	ah,cmeol		; get a confirm
	call	comnd
	jc	shomd5			; c = failure
	call	getmodem		; get modem status
	mov	mdmhand,al
	mov	ah,prstr
	mov	dx,offset msmsg1	; modem ready msg
	test	mdmhand,20h		; is DSR asserted?
	jz	shomd1			; z = no
	mov	dx,offset msmsg2	; say not asserted
shomd1: int	dos
	mov	dx,offset msmsg3	; CD asserted msg
	test	mdmhand,80h		; CD asserted?
	jz	shomd2			; z = no
	mov	dx,offset msmsg4	; say not asserted
shomd2: int	dos
	mov	dx,offset msmsg5	; CTS asserted msg
	test	mdmhand,10h		; CTS asserted?
	jz	shomd3			; z = no
	mov	dx,offset msmsg6	; say not asserted
shomd3: mov	ah,prstr
	int	dos
	clc
shomd5: ret
shomodem endp
 
; Get modem status and set global byte mdmhand. Preserve all registers.
; Uses byte mdstreg, the modem line status register, bits as follows:
; Int Z80, Int 8088, Hwd fail det enable, CD, CTS, DSR, SI/SecCD, RI   [jrd]
getmodem proc	near			; gets modem status upon request
	push	dx
	mov	al,0			; assume nothing is on
	mov	dx,mnctrl		; modem control port (read 02h)
	in	al,dx			; read modem control port
	not	al			; invert RB modem bits (0=true) [gbs]
	mov	mdmhand,0		; clear status byte
	test	al,4			; DSR asserted?
	jz	getmod1			; z = no
	or	mdmhand,20h		; set IBM spec DSR bit
getmod1:test	al,8			; CTS asserted?
	jz	getmod2			; z = no
	or	mdmhand,10h		; set IBM spec CTS bit
getmod2:test	al,10h			; CD asserted?			[gbs]
	jz	getmod3			; z = no
	or	mdmhand,80h		; set IBM spec CD bit		[gbs]
getmod3:mov	al,mdmhand		; setup return
	mov	ah,0			; return status in al
	pop	dx
	clc
	ret
getmodem endp
 
; Clear the input buffer. This throws away all the characters in the
; serial interrupt buffer.  This is particularly important when
; talking to servers, since NAKs can accumulate in the buffer.
; Returns normally.
 
CLRBUF	PROC	NEAR
	cli
	mov	srcpnt,offset source
	mov	count,0
	sti
	ret
CLRBUF	ENDP
 
; Clear to the end of the current line.	 Returns normally.
 
CLEARL	PROC	NEAR
	mov	dx,offset ceolseq	; clear sequence
	jmp	tmsg
CLEARL	ENDP
 
 
; Put the char in AH to the serial port, assumimg the port is active.
; Returns carry clear if success, else carry set.
; 16 May 1987 Add entry point OUTCH2 for non-flow controlled sending to
; prevent confusion of flow control logic at top of outchr; used by receiver
; buffer high/low water mark flow control code. [jrd]
OUTCHR	PROC	NEAR
	cmp	flowoff,0		; Are we doing flow control
	je	outch2			; No, just continue
	cmp	ah,flowoff		; sending xoff?
	jne	outch1			; ne = no
	mov	xofsnt,usron		; indicate user level xoff being sent
	jmp	outch1b
outch1:
	and	xofsnt,not usron	; cancel user level xoff
	cmp	ah,flowon		; user sending xon?
	jne	outch1b			; ne = no
	mov	xofsnt,off	     ; say an xon has been sent (cancels xoff)
outch1b:cmp	xofrcv,off		; Are we being held (xoff received)?
	je	outch2			; e = no - it's OK to go on
	cmp	ttyact,0		; in Connect mode?
	je	outch1d			; e = no
	call	beep			; let user know we are xoff-ed
	mov	xofrcv,off		; force off XOFF (unblock output)
	jmp	outch2			;  and send the char anyway
outch1d:cmp	flags.timflg,0		; is timer off?
	je	outch2			; e = yes, no timeout period
	push	cx			; save reg
	mov	ch,trans.rtime		; receive timeout interval (sec)
	xor	cl,cl			;  convert to 4 millsec increments
	jcxz	outch1c			; z = no timeout wanted
 
outch1a:cmp	xofrcv,off		; Are we being held (xoff received)?
	je	outch1c			; e = no - it's OK to go on
	push	ax
	mov	ax,4			; 4 millisec wait loop
	call	pcwait
	pop	ax
	loop	outch1a			; and try it again
	mov	xofrcv,off		; timed out, force it off and fall thru
outch1c:pop	cx			; end of flow control section
		     ; OUTCH2 is entry point for sending without flow control
OUTCH2: mov	al,ah			; Parity routine works on AL
	call	dopar			; Set parity appropriately
	mov	ah,al			; Don't overwrite character with status
	cmp	repflg,0		; doing REPLAY from a file?
	je	outch3			; e = no
	and	al,7fh			; strip parity
	cmp	al,'C'-40h		; Control-C? (to exit playback mode)
	je	outch2a			; e = yes, return failure
	clc				; return success, send nothing
	ret
outch2a:stc				; return failure to exit playback mode
	ret
outch3: push	cx			; save registers
	push	dx
outch3b:cmp	dupflg,0		; full duplex?
	je	outch3d			; e = yes
	mov	al,0f1h		; enable RTS and DTR Note: bits reversed [gbs]
	out	mnctrl,al		;[DTR]	      compared to documentation
	in	al,mnctrl
	test	al,4			; ignore CTS if DSR is not asserted
	jz	outch3d			; z = DSR not asserted
	mov	cx,8000			; ~10 seconds worth of waiting on CTS
outch3c:in	al,mnctrl
	test	al,8			; is CTS asserted?
	jnz	outch3d			; nz = yes
	push	cx
	push	dx
	mov	ax,1			; wait one millisec
	call	pcwait
	pop	dx
	pop	cx
	loop	outch3c			; test again
	call	beep			; half duplex timeout, make non-fatal
	pop	dx
	pop	cx
	clc
	ret
 
outch3d:xor	cx,cx
	mov	dx,mnstata		; get port status
	in	al,dx
	test	al,txrdy		; transmitter ready?
	jnz	outch4			; nz = yes
	jmp	$+2			; use time, prevent overdriving UART
	loop	outch3b
	jmp	outch5			; timeout
outch4: mov	al,ah			; send it out
	mov	dx,mndata		; use a little time
	jmp	$+2
	out	dx,al
	cmp	dupflg,0		; full duplex?
	je	outch4a			; e = yes
	cmp	al,trans.seol		; End of Line char?
	jne	outch4a			; ne = no
	mov	al,0f5h			; disable RTS (4)
	out	mnctrl,al
outch4a:pop	dx			; exit success
	pop	cx
	clc
	ret
outch5: call	beep
	pop	dx			; exit failure
	pop	cx
	stc
	ret
OUTCHR	ENDP
 
; This routine blanks the screen.
 
CMBLNK	PROC	NEAR
	mov	dx,offset clrseq	; clear screen sequence
	jmp	tmsg
CMBLNK	ENDP
 
; Locate; homes the cursor.  Returns normally.
 
LOCATE	PROC	NEAR
	xor	dx,dx			; Go to top left corner of screen
	jmp	poscur
LOCATE	ENDP
 
; write a line in inverse video at the bottom of the screen...
; the line is passed in dx, terminated by a $.	Returns normally.
putmod	proc	near
	push	dx		; preserve message
	mov	dx,24 * 100H	; line 24
	call	poscur
	mov	dx,offset invseq ; put into inverse video
	call	tmsg
	pop	dx
	call	tmsg		; print the message
	mov	dx,offset nrmseq ; normal videw
	call	tmsg
	ret			; and return
putmod	endp
 
; clear the mode line written by putmod.  Returns normally.
clrmod	proc	near
	mov	dx,24 * 100H
	call	poscur
	jmp	clearl
clrmod	endp
 
; Put a help message on the screen.  This one uses reverse video...
; pass the message in ax, terminated by a null.	 Returns normally.
puthlp	proc	near
	push	ax
	mov	dx,slen * 100H	; go to bottom line
	call	poscur
	pop	ax
	push	es
	mov	bx,ds
	mov	es,bx		; address data segment
	mov	si,ax		; convenient place for this
	mov	bx,101H		; current line/position
puthl1: mov	di,offset rdbuf ; this is destination
	xor	cx,cx		; # of chars in the line
	cld
puthl2: lodsb			; get a byte
	cmp	al,cr		; carriage return?
	je	puthl2		; yes, ignore it
	cmp	al,lf		; linefeed?
	je	puthl3		; yes, break the loop
	cmp	al,0
	je	puthl3		; ditto for null
	dec	cx		; else count the character
	stosb			; deposit into the buffer
	jmp	puthl2		; and keep going
puthl3: add	cx,80		; this is desired length of the whole
	mov	al,' '
	rep	stosb		; fill the line
	push	bx
	push	si
	push	es		; firmware likes to eat this one
	mov	ax,0		; send chars and attributes
	mov	cx,80		; this is # of chars to send
	mov	dx,offset ivlatt ; this are attributes to send
	mov	si,offset rdbuf ; the actual message
	mov	di,14H		; send direct to screen
	mov	bp,ds		; need data segment as well
	push	ax
	push	cx
	push	dx
	push	di
	mov	di,14H		; send direct to screen
	int	firmwr
	pop	di
	pop	dx
	pop	cx
	pop	ax
	pop	es
	pop	si
	pop	bx		; restore everything
	inc	bx		; next line
	cmp	byte ptr [si-1],0 ; were we ended by a 0 last time?
	jne	puthl1		; no, keep looping
	pop	es		; else restore this
	clc
	ret			; and return
puthlp	endp
 
; Set the baud rate for the current port, based on the value
; in the portinfo structure.  Returns normally.
 
BAUDST	PROC	NEAR
	mov	dx,offset bdtab		; baud rate table, ascii
	xor	bx,bx			; help is the table itself
	mov	ah,cmkey		; get keyword
	call	comnd
	jc	baudst1			; c = failure
	push	bx			; save result
	mov	ah,cmeol		; get confirmation
	call	comnd
	pop	bx
	jc	baudst1			; c = failure
	mov	si,portval
	mov	ax,[si].baud		; remember original value
	mov	[si].baud,bx		; set the baud rate
	call	dobaud			; use common code
	clc
baudst1:ret
BAUDST	ENDP
 
DOBAUD	PROC	NEAR
	push	bx
	push	ds
	pop	es			; set es to data segment
	cld
	mov	al,bl
	mov	cl,4
	shl	bl,cl			; shift constant into high nibble
	or	al,bl
	out	bdprt,al		; write into port
	or	al,0f0h			; turn on high nibble
	push	es
	mov	bx,nvmseg
	mov	es,bx
	mov	es:[xmitbd],al
	mov	es:[rcvbd],al		; set baud in nvm
	pop	es
	pop	bx
	ret
DOBAUD	ENDP
 
; Get the current baud rate from the serial card and set it
; in the portinfo structure for the current port.  Returns normally.
; This is used during initialization.
 
GETBAUD PROC	NEAR
	push	ax		; save some regs
	push	bx
	push	es
	mov	ax,nvmseg
	mov	es,ax
	mov	al,es:[xmitbd]	; get  xmit baud rate [gbs]
	pop	es
	and	ax,0fh		; only low nibble is used [gbs]
	mov	bx,portval
	mov	[bx].baud,ax	; set value
	pop	bx		; restore regs
	pop	ax
	ret			; and return
GETBAUD ENDP
 
 
; Get Char from serial port buffer.
; skip returns if no character available at port,
; otherwise returns with char in al, # of chars in buffer in dx.
; Revised 22 May 1986, and again slightly 2 August 1986 by [jrd]
; Copied from msxibm.asm [jrd]
PRTCHR	PROC	NEAR
	call	chkxon			; see if we need to xon
	cmp	repflg,0		; REPLAY?
	je	prtch0			; e = no
	jmp	prtch30			; yes, do replay file reading
prtch0: cmp	count,0			; any characters available?
	jnz	prtch1			; nz = yes, get one
prtch0a:xor	dx,dx			; return count of zero
	stc				; say no data
	ret
prtch1: push	si			; save si
	cli		; interrupts off, to keep srcpnt & count consistent
	mov	si,srcpnt	    ; address of next available slot in buffer
	sub	si,count	    ; minus number of unread chars in buffer
	cmp	si,offset source	; located before start of buf?
	jae	prtch2			; ae = no
	add	si,bufsiz		; else do arithmetic modulo bufsiz
prtch2: mov	al,byte ptr [si]	; get a character into al
	dec	count			; one less unread char now
	sti				; interrupts back on now
	pop	si
	mov	dx,count		; return # of chars in buffer
	test	flags.debug,logses	; debug mode?
	jnz	prtch14			; nz = yes, pass all chars
	cmp	rxtable+256,0		; translation turned off?
	jne	prtch14			; ne = table is on, pass all chars
	cmp	al,0			; NUL?
	je	prtch13			; e = yes, ignore it
	cmp	al,DEL			; DEL char
	jne	prtch14			; ne = no, pass char
prtch13:xor	dx,dx
	stc				; no data
	ret
prtch14:clc				; return char in al
	ret
 
prtch30:push	bx			; REPLAY, read char from a file
	push	cx
	test	xofsnt,usron		; user level xoff sent?
	jnz	prtch31			; nz = yes, suppress reading here
	xor	dx,dx
	mov	ax,100
	mov	bx,1
	jmp	$+2			; flush lookahead buffer
	div	bx			; burn some cpu cycles
	jmp	$+2			; because a 1 ms wait is too long
	div	bx
	jmp	$+2
	div	bx
	mov	ah,readf2
	mov	bx,diskio.handle	; file handle
	mov	cx,1			; read one char
	mov	dx,offset rdbuf		; to this buffer
	int	dos
	jc	prtch31			; c = read failure
	cmp	ax,cx			; read the byte?
	jne	prtch31			; ne = no
	pop	cx
	pop	bx
	mov	al,rdbuf		; get the char into al
	mov	dx,1			; external char count
	clc
	ret				; return it
prtch31:pop	cx
	mov	bx,portval
	mov	[bx].portrdy,0		; say port is not ready
	pop	bx
	xor	dx,dx
	stc				; say no char
	ret
PRTCHR	ENDP
 
 
; IHOSTS - Initialize the host by sending XON. Requires that the port be
; initialized.
IHOSTS	PROC	NEAR
	push	ax		; save the registers
	push	cx
	push	dx
	mov	xofrcv,off	; clear old xoff received flag
	mov	xofsnt,off	; and old xoff sent flag
	mov	ah,flowon	; put Go-ahead flow control char in ah
	or	ah,ah		; doing flow control?
	jz	ihosts1		; z = no, don't send a null
	call	outchr		; send it (release Host's output queue)
ihosts1:pop	dx		; empty buffer. we are done here
	pop	cx
	pop	ax
	ret
IHOSTS	ENDP
 
; IHOSTR - initialize the remote host for our reception of a file by
; sending the flow-on character (XON typically) to release any held
; data. Called by receive-file code just after initializing the serial
; port.		22 March 1986 [jrd]
; Modified 26 June 1986 to supress sending a null if no flow control. [jrd]
IHOSTR	PROC	NEAR
	push	ax		; save regs
	push	cx
	mov	xofrcv,off	; clear old xoff received flag
	mov	xofsnt,off	; and old xoff sent flag
	mov	ah,flowon	; put Go-ahead flow control char in ah
	or	ah,ah		; doing flow control?
	jz	ihostr1		; z = no, don't send a null
	call	outchr		; send it (release Host's output queue)
ihostr1:pop	cx
	pop	ax
	ret
IHOSTR	ENDP
 
; local routine to see if we have to transmit an xon
chkxon	proc	near
	cmp	flowon,0	; doing flow control?
	je	chkxo1		; no, skip all this
	test	xofsnt,usron	; did user send an xoff?
	jnz	chkxo1		; nz = yes, don't contradict it here
	test	xofsnt,bufon	; have we sent a buffer level xoff?
	jz	chkxo1		; z = no, forget it
	cmp	count,mntrgl	; below (low water mark) trigger?
	jae	chkxo1		; no, forget it
	mov	ah,flowon	; ah gets xon
	and	xofsnt,off	; remember we've sent the xon
	call	outch2		; send via non-flow controlled entry point
chkxo1: ret
chkxon	endp
 
; Send a BREAK out the current serial port.  Returns normally.
SENDBR	PROC	NEAR
	push	cx
	mov	cx,275		; 275 millisec
	call	sendbw		; let worker routine do it
	pop	cx
	clc			; don't exit Connect mode
	ret
; Send a Log BREAK out the current serial port.
SENDBL: push	cx
	mov	cx,1800		; 1800 millisec
	call	sendbw		; let worker routine do it
	pop	cx
	clc			; don't exit Connect mode
	ret
 
sendbw: push	ax		; worker routine to send a break [jrd]
	push	bx		; number of millisec is in cx
	push	dx
	mov	ah,ioctl
	mov	al,3		; write to control channel
	mov	bx,3		; aux port handle
	mov	dx,offset iobuf
	mov	iobuf,stbrk		; start sending a break
	int	dos
	mov	ax,cx		; # of ms to wait
	call	pcwait		; hold break for desired interval
	mov	ah,ioctl
	mov	al,3
	mov	bx,3
	mov	dx,offset iobuf
	mov	iobuf,enbrk		; stop sending the break
	int	dos
	pop	dx
	pop	bx
	pop	ax
	clc
	ret
SENDBR	ENDP
 
 
; wait for the # of milliseconds in ax
; thanks to Bernie Eiben for this one.
pcwait	proc	near
	push	cx
pcwai0: mov	cx,240		; inner loop counter for 1 millisecond
pcwai1: sub	cx,1		; inner loop takes 20 clock cycles
	jnz	pcwai1
	dec	ax		; outer loop counter
	jnz	pcwai0		; wait another millisecond
	pop	cx
	ret
pcwait	endp
 
 
; Position the cursor according to contents of DX:
; DH contains row, DL contains column.	Returns normally.
 
POSCUR	PROC	NEAR
	add	dx,101H		; start at 1,1
	push	es
	push	ax		; save some regs
	push	bx
	push	di
	push	dx
	cld
	mov	ax,ds
	mov	es,ax		; address data segment
	mov	di,offset rowp
	mov	al,dh		; row comes first
	mov	ah,0
	call	nout
	mov	al,';'
	stosb			; separated by a semicolon
	pop	dx
	mov	al,dl
	mov	ah,0
	call	nout
	mov	al,'H'
	stosb			; end w/H
	mov	byte ptr [di],'$' ; and dollar sign
	mov	dx,offset gopos
	call	tmsg
	pop	di		; restore regs
	pop	bx
	pop	ax
	pop	es
	ret
POSCUR	ENDP
 
; Delete a character from the terminal.	 This works by printing
; backspaces and spaces.  Returns normally.
 
DODEL	PROC	NEAR
	mov	dx,offset delstr    ; Erase weird character
	jmp	tmsg
DODEL	ENDP
 
; Move the cursor to the left margin, then clear to end of line.
; Returns normally.
 
CTLU	PROC	NEAR
	mov	dx,offset clrlin    ; this just goes to left margin
	call	tmsg
	jmp	clearl		    ; now clear line
CTLU	ENDP
 
; set the current port.
 
COMS	PROC	NEAR
	mov	dx,offset nyimsg
	jmp	tmsg
COMS	ENDP
 
; Set Terminal command
 
VTS	PROC	NEAR			; SET TERM whatever  [jrd]
	mov	ah,cmkey		; parse key word
	xor	bx,bx			; use built-in help
	mov	dx,offset vttbl		; use this table
	call	comnd
	jc	vts0
	cmp	bx,tttypes		; ROLL or more?
	ja	vts4			; a = yes
					; SET TERM {VT52 | VT102}
	push	bx
	mov	ah,cmeol
	call	comnd			; get a confirm
	pop	bx
	jnc	vts1			; nc = success
vts0:	ret
vts1:	cmp	bl,ttvt100		; set to VT102?
	je	vts2			; e = yes
	mov	flags.vtflg,ttvt52	; say VT52
	jmp	vts3
vts2:	mov	flags.vtflg,ttvt100	; say VT102
vts3:	call	setterm			; send the message to the console
	ret
vts4:	mov	ah,cmkey		; SET TERM ROLL
	mov	bx,offset rolhlp	; help message
	mov	dx,offset ontab		; table of answers
	call	comnd
	jc	vts6
	push	bx
	mov	ah,cmeol
	call	comnd			; get a confirm
	pop	bx
	jc	vts6
	mov	vtroll,bl		; set the flag
vts6:	ret
VTS	ENDP
 
VTSTAT	PROC	NEAR			; Status routine for emulation. [jrd]
	mov	bx,offset vtstbl	; table of things to show
	jmp	statc			; status common code, in mssset
VTSTAT	ENDP
 
; initialization for using serial port.	 This routine performs
; any initialization necessary for using the serial port, including
; setting up interrupt routines, setting buffer pointers, etc.
; Doing this twice in a row should be harmless (this version checks
; a flag and returns if initialization has already been done).
; SERRST below should restore any interrupt vectors that this changes.
; Returns normally.
 
SERINI	PROC	NEAR
	cmp	portin,0		; Did we initialize port already?
	jne	serin0			; ne = yes, so just leave
	cli				; Disable interrupts
	cld				; Do increments in string operations
	push	es
	push	si			; [ejz] - Save this just in case
	mov	si,offset prtpar	; [ejz]
	mov	dx,mnstata		; [ejz]
	push	ds
	pop	es			; set es to data segment
	call	prtset			; [ejz]
	xor	ax,ax			; Address low memory
	mov	es,ax
	mov	ax,es:[4*serchn]	; get old serial handler
	mov	oldser,ax		; save
	mov	ax,es:[4*serchn+2]	; get segment
	mov	oldseg,ax		; save segment as well
	mov	ax,es:[4*serch1]	; this is alternate for older rainbows
	mov	old1ser,ax
	mov	ax,es:[4*serch1+2]
	mov	old1seg,ax		; pretty silly, huh?
	mov	ax,offset serint	; point to our routine
	mov	es:[4*serchn],ax	; point at our serial routine
	mov	es:[4*serch1],ax	; have to set both of these
	mov	es:[4*serchn+2],cs	; our segment
	mov	es:[4*serch1+2],cs
	pop	si			; [ejz]
	pop	es
	mov	al,0f1h		; enable RTS and DTR Note: bits reversed [gbs]
	out	mnctrl,al		;[DTR]	      compared to documentation
	mov	portin,1		; Remember port has been initialized
	sti				; Allow interrupts
	push	bx
	mov	bx,portval		; get port
	mov	parmsk,0ffh		; parity mask, assume parity is None
	cmp	[bx].parflg,parnon	; is it None?
	je	serin1			; e = yes
	mov	parmsk,07fh		; no, pass lower 7 bits as data
serin1: mov	bx,[bx].flowc		; get flow control chars
	mov	flowoff,bl		; xoff or null
	mov	flowon,bh		; xon or null
	pop	bx
serin0: clc				; carry clear for success
	ret
SERINI	ENDP
 
; this is used to by serini
prtset	proc	near
	cld
	lodsb			; get a byte
	or	al,al
	jz	prtse1		; end of table, stop here
	out	dx,al		; else send it out
	jmp	short prtset	; and keep looping
prtse1: ret			; end of routine
prtset	endp
 
; Reset the serial port.  This is the opposite of serini.  Calling
; this twice without intervening calls to serini should be harmless.
; Returns normally.
 
SERRST	PROC	NEAR
	cmp	portin,0		; reset already?
	je	srst1			; e = yes, just leave
	cli				; disable interrupts
	push	es			; preserve this
	xor	ax,ax
	mov	es,ax			; address segment 0
	mov	ax,oldser
	mov	es:[4*serchn],ax
	mov	ax,oldseg
	mov	es:[4*serchn+2],ax
	mov	ax,old1ser
	mov	es:[4*serch1],ax
	mov	ax,old1seg
	mov	es:[4*serch1+2],ax	; restore old handlers
	mov	portin,0		; reset flag
	pop	es
	sti				; re-enable interrupts
srst1:	ret
SERRST	ENDP
 
; serial port interrupt routine.  This is not accessible outside this
; module, handles serial port receiver interrupts.
; New code, lifted from msxibm. [jrd]
serint	PROC  NEAR
	push	ax
	push	ds
	push	bx
	push	dx
	mov	ax,seg data
	mov	ds,ax			; address data segment
	mov	dx,mnstatb		; Asynch status port
	xor	al,al			; innocuous value
	out	dx,al			; send out to get into a known state
	mov	al,2			; now address register 2
	out	dx,al
	in	al,dx			; read interrupt cause
	cmp	al,7			; in range?
	ja	serin7		; no, just dismiss (what about reset error?)
	mov	bl,al
	shl	bl,1			; double for word index
	xor	bh,bh
	call	ivec[bx]		; call appropriate handler
	jmp	short serin8
serin7: mov	dx,mnstata		; reload port address
	mov	al,38H
	out	dx,al		; tell the port we finished with the interrupt
	sti				; turn on interrupts
serin8: pop	dx
	pop	bx
	pop	ds
	pop	ax
intret: iret
 
; handler for serial receive, port A
 
srcva:	mov dx,mnstata
	xor	al,al		; Asynch status port
	out	dx,al		; put into known state
	jmp	$+2
	in	al,dx
	test	al,rxrdy	; Data available?
	jnz	srcva0a		; nz = yes
srcva0: mov	dx,mnstata
	mov	al,38h		; tell port we are finished with the interrupt
	out	dx,al
	sti			; turn on interrupts
	jmp	retint		;  and exit now (common jump point)
 
srcva0a:;;and al,mdmover	; select overrun bit
;;	mov	overrun,al	; save it for later
	mov	al,30h		; clear any errors
	out	dx,al
	mov	dx,mndata	; get modem rx data
	in	al,dx		; read the received character into al
	cmp	flowoff,0	; flow control active?
	je	srcva2		; e = no
	mov	ah,al		; ah = working copy. Check null, flow cntl
	and	ah,parmsk	; strip parity temporarily, if any
	cmp	ah,flowoff	; acting on Xoff?
	jne	srcva1		; ne = Nope, go on
	mov	xofrcv,bufon	; Set the flag saying XOFF received
	jmp	srcva0		;  and exit
srcva1: cmp	ah,flowon	; acting on Xon?
	jne	srcva2		; ne = no, go on
	mov	xofrcv,off	; Clear the XOFF received flag
	jmp	srcva0		;  and exit
srcva2: push	ax		; save rcvd char around this output cmd
	mov	dx,mnstata
	mov	al,38h		; tell port we are finished with the interrupt
	out	dx,al
	pop	ax
	cli			; ensure interrupts are off, critical section
;;	mov	ah,overrun		; get overrun flag
;;	or	ah,ah		; overrun?
;;	jz	srcva2a		; z = no
;;	mov	ah,al		; yes, save present char
;;	mov	al,bell		; insert control-G for missing character
srcva2a:mov	bx,srcpnt	; address of buffer storage slot
	mov	byte ptr [bx],al ; store the new char in buffer "source"
	inc	srcpnt		; point to next slot
	inc	bx
	cmp	bx,offset source + bufsiz ; beyond end of buffer?
	jb	srcva3		; b = not past end
	mov	srcpnt,offset source ; wrap buffer around
srcva3: cmp	count,bufsiz	; filled already?
	jae	srcva4		; ae = yes
	inc	count		; no, add a char
srcva4:;;or	ah,ah		; anything in overrun storage?
;;	jz	srcva4a		; z = no
;;	mov	al,ah		; recover any recent char from overrun
;;	xor	ah,ah		; clear overrun storage
;;	jmp	srcva2a		; yes, go store real second char
srcva4a:sti			; ok to allow interrupts now, not before
	cmp	count,mntrgh	; past the high trigger point?
	jbe	retint		; be = no, we're within our limit
	test	xofsnt,bufon	; Has an XOFF been sent by buffer control?
	jnz	retint		; nz = yes
	mov	al,flowoff	; get the flow off char (Xoff or null)
	or	al,al		; don't send null chars
	jz	retint		; z = null, nothing to send
	call	dopar		; Set parity appropriately
	mov	ah,al		; Don't overwrite character with status
	push	cx		; save reg
	xor	cx,cx		; loop counter
srcva5: mov	dx,mnstata	; port status register
	in	al,dx
	test	al,txrdy	; Transmitter ready?
	jnz	srcva6		; nz = yes
	push	ax		; use time, prevent overdriving UART
	pop	ax
	loop	srcva5		; else wait loop, cx times
	jmp	short srcva7	; Timeout
srcva6: mov	al,ah		; Now send out the flow control char
	mov	dx,mndata
	out	dx,al
	mov	xofsnt,bufon	; Remember we sent an XOFF at buffer level
srcva7: pop	cx		; restore reg
retint: ret
 
; The interrupt is for the 'B' port - transfer control to
; the original handler and hope for the best.
tranb:	pushf			; put flags on stack to simulate interrupt
	call	dword ptr [old1ser] ; call old handler
	jmp	srccom		; take common exit
 
stxa:	mov	dx,mnstata
	mov	al,28H		; reset transmit interrupt
	out	dx,al
	jmp	srccom		; take common exit
 
sstata: mov	dx,mnstata
	mov	al,10H		; reset status interrupt
	out	dx,al		; fall through to common exit srccom below
 
srccom: mov	dx,mnstata	; common exit for above
	mov	al,38h		; tell port we are finished with the interrupt
	out	dx,al
	sti			; turn on interrupts
	ret
 
SERINT	ENDP
 
DTRLOW	PROC	NEAR		; Global proc to Hangup the Phone by making
				; DTR and RTS low.
	mov	ah,cmline		; allow text, to display help
	mov	bx,offset rdbuf		; dummy buffer
	mov	dx,offset hnghlp	; help message
	call	comnd			; get a confirm
	jc	dtrlow1
	call	serhng			; drop DTR and RTS
	mov	ah,prstr		; give a nice message
	mov	dx,offset hngmsg
	int	dos
	clc
dtrlow1:ret
DTRLOW	ENDP
 
; Hang up the Phone. Similar to SERRST except it just forces DTR and RTS low
; to terminate the connection. 29 March 1986 [jrd]
; Calling this twice without intervening calls to serini should be harmless.
; Returns normally.
 
serhng	proc	near
	call	serrst		; reset the port so can be restarted cleanly
	cli			; Disable interrupts
	mov	al,0ffh		; disable RTS and DTR			[gbs]
	out	mnctrl,al	; [DTR]
	sti			; Allow interrupts
	mov	ax,500
	call	pcwait		; wait 500ms
	clc
	ret
serhng	endp
 
; Produce a short beep.
; Returns normally.
 
BEEP	PROC	NEAR
	mov	dl,bell
	mov	ah,conout
	int	dos
	clc
	ret
BEEP	ENDP
 
; put the number in ax into the buffer pointed to by di.  Di is updated
nout	proc	near
	mov	dx,0		; high order is always 0
	mov	bx,10
	div	bx		; divide to get digit
	push	dx		; save remainder digit
	or	ax,ax		; test quotient
	jz	nout1		; zero, no more of number
	call	nout		; else call for rest of number
nout1:	pop	ax		; get digit back
	add	al,'0'		; make printable
	cld
	stosb			; drop it off
	ret
nout	endp
 
 
term	proc	near
	mov	si,ax			; this is source
	mov	argadr,ax		; save here too
	mov	di,offset ourarg	; place to store arguments
	mov	ax,ds
	mov	es,ax			; address destination segment
	mov	cx,size termarg
	cld
	rep	movsb			; copy into our arg blk
	cmp	inited,0		; inited yet?
	jne	term0			; ne = yes
	call	cmblnk			; clear the screen
	call	locate			; put cursor at top left corner
	mov	inited,1		; say initialized
	jmp	short term0a
term0:	call	rstscr			; restore screen
term0a:	mov	inited,1		; remember inited
	mov	parmsk,0ffh		; parity mask, assume parity = None
	cmp	ourarg.parity,parnon	; is parity None?
	je	term1			; e = yes, keep all 8 bits
	mov	parmsk,07fh		; else keep lower 7 bits

term1:	call	portchr		      ; get char from port, apply parity mask
	jnc	short term3		; nc = char
term2:	mov	fairness,0		; say kbd was examined
	call	keybd			; call keyboard translator in msu
	jnc	term1			; nc = no char or have processed it
	call	pntflsh			; flush printer buffer
	call	savscr			; save screen
	mov	dx,offset gtobot	; go to last line on screen
	call	tmsg
	stc
	ret				; carry set = quit connect mode

term3:	call	outtty			; print on terminal
	inc	fairness		; say read port but not kbd, again
	cmp	fairness,200		; this many port reads before kbd?
	jb	term1			; b = no, read port again
	call	pntflsh			; flush printer buffer
	jmp	short term2		; yes, let user have a chance too
term	endp
 
; Get a char from the serial port manager, return it in al
; returns with carry clear if a character is available, else carry set
portchr	proc	near
	call	prtchr			; character at port?
	jnc	portc1			; nc = yes
portc0:	stc				; carry = no character
	ret
portc1:	and	al,parmsk		; apply 8/7 bit parity mask
	or	al,al			; catch nulls
	jz	portc0			; z = null, ignore it
	cmp	al,del			; catch dels
	je	portc0			; e = del, ignore it
portc2:	clc				; have a character, in AL
	ret
portchr	endp
 
; put the character in al to the screen
outtty	proc	near
	push	es			; protect es around fastcon calls
	test	flags.remflg,d8bit	; keep 8 bits for displays?
	jnz	outnp5			; nz = yes, 8 bits if possible
	and	al,7fh			; remove high bit
outnp5: cmp	rxtable+256,0		; translation turned off?
	je	outnp7			; e = yes, no translation
	push	bx
	mov	bx,offset rxtable	; address of translate table
	xlatb				; new char is in al
	pop	bx
outnp7: test	ourflgs,fpscr+vtautop+vtcntp ; should we be printing?
	jz	outnop			; z = no, keep going
	call	pntchr			; queue char for printer
	jnc	outnop			; nc = successful print
	push	ax
	call	beep			; else make a noise and
	call	trnprs			;  turn off printing
	pop	ax
	and	ourflgs,not (fpscr+vtautop+vtcntp) ; turn off printing
	pop	ax
outnop: test	ourarg.flgs,capt	; capturing output?
	jz	outnoc			; z = no, forget this part
	push	ax			; save char
	call	ourarg.captr		; give it captured character
	pop	ax			; restore character and keep going
outnoc:
	cmp	vtroll,0		; auto roll back allowed?
	jz	outnp6			; z = no, leave screen as is
	cmp	bwnd.lcnt,0		; is screen rolled back? [dlk]
	je	outnp6			; e = no
	call	nxtbot			; restore screen before writing [dlk]
outnp6: push	ax
	call	scrprep			; need to save top line
	pop	ax
	test	ourarg.flgs,trnctl	; debug? if so use Bios tty mode
	jz	outnp3			; z = no
	cmp	al,7fh			; Ascii Del char or greater?
	jb	outnp1			; b = no
	je	outnp0			; e = Del char
	push	ax			; save the char
	mov	al,7eh			; output a tilde for 8th bit
	int	fastcon
	pop	ax			; restore char
	and	al,7fh			; strip high bit
outnp0: cmp	al,7fh			; is char now a DEL?
	jne	outnp1			; ne = no
	and	al,3fH			; strip next highest bit (Del --> '?')
	jmp	outnp2			; send, preceded by caret
outnp1: cmp	al,' '			; control char?
	jae	outnp3			; ae = no
	add	al,'A'-1		; make visible
outnp2: push	ax			; save char
	mov	al,5eh			; caret
	int	fastcon			; display it
	pop	ax			; recover the non-printable char
outnp3: call	ansbak			; match answerback sequences
	test	ourflgs,vtcntp		; autoprint on?		;gbs
	jnz	outnp4			; if so, don't output to screen
	push	ax
	int	fastcon			; write without intervention
	pop	ax
outnp4: pop	es
	ret
outtty	endp
 
; enter with current terminal character in al.
; calls answerback routine if necessary.
; This can be used to make the emulator recognize any sequence.
ansbak	proc	near
	push	bx
	mov	bx,offset ansbk1	; check 1st answerback
	call	ansbak0			; check for answerback
	mov	bx,offset ansbk2	; maybe second answerback
	call	ansbak0			; should probably loop thru a table
	mov	bx,offset ansbk3
	call	ansbak0
	mov	bx,offset ansbk4
	call	ansbak0
	mov	bx,offset ansbk5
	call	ansbak0
	mov	bx,offset ansbk6
	call	ansbak0
	mov	bx,offset ansbk7
	call	ansbak0
	mov	bx,offset ansbk8
	call	ansbak0
	mov	bx,offset ansbk9
	call	ansbak0
	mov	bx,offset ansbk10
	call	ansbak0
	mov	bx,offset ansbk11
	call	ansbak0
	mov	bx,offset ansbk12
	call	ansbak0
	mov	bx,offset ansbk13
	call	ansbak0
	mov	bx,offset ansbk14
	call	ansbak0
	pop	bx
	ret
ansbak0:			; worker routine for above
	push	ax		; preserve these
	push	si
	mov	si,[bx].anspt	; get current pointer
	cmp	al,[si]		; is it correct?
	jne	ansba1		; no, reset pointers and go on
	inc	[bx].anspt	; increment pointer
	dec	[bx].ansct	; decrement counter
	jnz	ansba2		; not done, go on
	push	bx
	call	[bx].ansrtn	; send answerback
	pop	bx
ansba1: mov	ax,[bx].ansseq	; get original sequence
	mov	[bx].anspt,ax
	mov	al,[bx].anslen	; and length
	mov	[bx].ansct,al
ansba2: pop	si
	pop	ax
	ret
ansbak	endp
 
; send the answerback message.
sndans	proc	near
	push	cx
	mov	si,offset ansret	; ansi ident message
	mov	cx,ansrln		; length of same
	push	es			; save seg register
	mov	ax,nvmseg		; fetch address of nvm
	mov	es,ax			; put in segment reg
	test	byte ptr es:[vt52mod],1	  ; check bit zero
	pop	es			; restore the seg register
	jnz	sndan1			; nz = ansi mode
	mov	si,offset VT52ret	; say VT52
	mov	cx,VT52ln
sndan1: cld
	lodsb				; get a byte
	mov	ah,al
	push	si
	push	cx
	call	outchr			; send ah out the serial port
	pop	cx
	pop	si
	loop	sndan1
	pop	cx
	ret
sndans	endp
 
; enable alternate keypad mode
enaaky	proc	near
	mov	akeyflg,1		; set keypad alternate mode
	ret
enaaky	endp
 
; disable alternate keypad mode
deaaky	proc	near
	mov	akeyflg,0
	ret
deaaky	endp
 
cuapp	proc	near
	mov	ckeyflg,1		; set cursor keys to applications mode
	ret
cuapp	endp
 
cunapp	proc	near
	mov	ckeyflg,0		; set cursor keys to cursor mode
	ret
cunapp	endp
 
; ESC c handler.
; Send a space so the firmware doesn't see bad escape sequences. [jrd]
sndspc	proc	near
	mov	akeyflg,0		; reset keypad applications mode
	mov	ckeyflg,0		; reset cursor applications mode
	mov	al,' '			; space separates the ESC and 'c'
	int	fastcon			; send this to upset esc parser
	push	ax
	call	nxtbot			; go to end of screen buffer
	call	cmblnk			; clear the screen too
	pop	ax
	clc
	ret
sndspc	endp
 
; answer a cursor position report
ansenq	proc	near
	push	es
	push	di
	mov	di,offset rdbuf		; convenient scratch buffer
	mov	al,escape
	cld
	push	ds
	pop	es			; set es to data segment
	stosb
	mov	al,'['
	stosb
	push	es
	mov	ax,scrseg
	mov	es,ax
	mov	al,es:[csrlin]
	mov	ah,es:[csrcol]
	pop	es
	push	ax
	xor	ah,ah
	call	nout
	mov	al,';'
	stosb
	pop	ax
	xchg	ah,al
	xor	ah,ah
	call	nout
	mov	al,'R'
	stosb
	mov	si,offset rdbuf
	sub	di,si
	mov	cx,di
ansen1: lodsb				; get a byte
	push	si
	push	cx
	mov	ah,al
	call	outchr			; send it along
	pop	cx
	pop	si
	loop	ansen1			; loop thru all
	pop	di
	pop	es
	ret				; and return
ansenq	endp
 
; set terminal to vt52 or vt102, based on current flags.vtflg [jrd]
setterm proc	near
	push	es			; save seg register
	mov	ax,nvmseg		; fetch address of nvm
	mov	es,ax			; put in segment reg
	test	byte ptr es:[vt52mod],1	  ; check bit zero
	pop	es			; restore the seg register
	push	ds
	pop	es			; set es to data segment
	cld
	jnz	setter1			; nz = ansi mode
	cmp	flags.vtflg,ttvt52	; are we already a VT52?
	je	setterx			; e = nothing to do
	mov	si,offset dovt102	; change from vt52 to VT102
	jmp	setter2
setter1:cmp	flags.vtflg,ttvt100	; already a VT102?
	je	setterx			; e = nothing to do
	mov	si,offset dovt52	; change from vt102 to vt52
setter2:lodsb
	cmp	al,'$'
	je	setterx
	push	es
	push	si
	int	fastcon
	pop	si
	pop	es
	jmp	setter2
setterx:ret
setterm endp
 
; Handle the print screen key - copy whole screen to printer.
; Rewritten 26 June 1986 by [jrd] to avoid confusion over segment addressing
;  and line length counting. Based on procedure dumpscr code.
; 30-Apr-1990 [gbs] convert line-drawing characters to ".".  Otherwise, they
;	output as control characters. (Could shift in/shift out if output
;	printer is DEC printer.)
; 24-AUG-1990 [rhw-3] added translation table for VT100 graphics to chars
;	a normal printer (even non-dec) can swollow.  At least linedraw will
;	come out looking like boxes, etc.  See xlat_tab usage below...
;	This replaces [gbs] change above.
;
prtscn	proc	near
	push	ax			; save some regs
	push	bx
	push	cx
	push	dx
	push	si
	push	di
	push	es
	call	pntflsh			; flush printer buffer now
	xor	bx,bx			; index for current line pointer
	mov	cx,slen			; number of screen lines
prtsc1: push	cx			; save outer loop (lines to do) count
	push	bx			; save index value for bottom of loop
	mov	cx,swidth		; number of screen columns = 132
	mov	di,offset dumpbuf	; data segment memory (work buffer)
	mov	ax,scrseg		; make es hold screen segment
	mov	es,ax			;  for inner read-a-row loop
	mov	si,es:[latofs+bx]	; get pointer to current line
	and	si,0fffh		; only 12 bits are significant
prtsc2: mov	al,es:byte ptr [si]	; inner loop. read text char
	mov	byte ptr [di],al	; just store char, don't use es:
	inc	si			; update pointers (si needed below)
	inc	di
	loop	prtsc2			; do for all 132 columns, fixed format
				; find end of line, replace nulls with spaces
	mov	cx,swidth		; max chars in a line
	mov	di,offset dumpbuf	; look at start of line
prtsc3: mov	al,byte ptr [di]	; get a byte into al
	cmp	al,0ffh			; end of line indicator?
	je	prtsc5			; e = yes, exit with count left in cx
;;	cmp	al,0			; is it a nasty null?		x[rhw-3]
;;	jne	prtsc4			; ne = no			x[rhw-3]
;;	mov	al,' '			; replace null with nice space	x[rhw-3]

prtsc4: cmp	al,' '			; is it a ctrl (line-draw) char? [gbs]
	jae	prtsc4a			; ae = no
;;	mov	al,'.'			; if so, replace with "."[gbs], x[rhw-3]
	mov	dx,bx			; [rhw-3] save bx, need to use it
	mov	bx,offset xlat_tab	; [rhw-3] get ptr to xlation buffer
	xlat				; [rhw-3] lookup & swap old for new char
	mov	bx,dx			; [rhw-3] restore bx

prtsc4a:mov	byte ptr [di],al	; put back into buffer
	inc	di			; look ahead for next char
	loop	prtsc3			; scan the rest of the line
prtsc5: dec	di			; make di point at eol address
	mov	ax,swidth		; max line length
	sub	ax,cx			; minus chars scanned = length of line
	mov	cx,ax			; remember it here
					; trim line of trailing spaces
	mov	ax,ds			; di needs to point to data space
	mov	es,ax			; string ops use es:di
	mov	al,' '			; thing to scan over
	std				; set scan backward
	repe	scasb			; scan until non-space, dec's di
	cld				; set direction forward
	jz	prtsc6			; z = all spaces (count was exhausted)
	inc	cx			; go forward over last non-space
	inc	di			; ditto for address
prtsc6: mov	word ptr [di+1],0A0Dh	; append cr/lf
	add	cx,2			; line count + cr/lf
	mov	dx,offset dumpbuf	; array to be written
	mov	bx,prnhand		; file handle of Connect mode printer
	mov	ah,write2		; write the line
	int	dos
	pop	bx			; restore index for line pointer
	inc	bx			; add 2 for next pointer's location
	inc	bx
	pop	cx			; get line counter again
	loop	prtsc1			; do next line (needs si preset)
	pop	es
	pop	di
	pop	si
	pop	dx
	pop	cx
	pop	bx
	pop	ax
	clc
	ret
prtscn	endp
 
; toggle print flag. If turning on printing, check for printer ready; skip
;  printing if not ready and beep the user. [jrd]
trnprs	proc	near
	test	ourflgs,fpscr		; printing currently?
	jnz	trnpr1			; nz = yes, its on and going off
	call	ckprn			; see if printer is available
	jc	trnpr2			; c = printer not ready
trnpr1: xor	ourflgs,fpscr		; flip the flag
trnpr2: clc
	ret
trnprs	endp

; autoprint on
apon	proc	near
	call	ckprn			; printer ready?
	jc	apon1			; c = printer not ready
	or	ourflgs,vtautop		; enable autoprint
apon1:	clc
	ret
apon	endp
 
; autoprint off
apoff	proc	near
	and	ourflgs,not vtautop	; disable autoprint
	ret
apoff	endp
 
; print controller on
prconon proc	near
	call	ckprn			; printer ready?
	jc	pcon1			; c = printer not ready
	or	ourflgs,vtcntp		; enable print controller
pcon1:	clc
	ret
prconon endp
 
; print controller off
pconoff proc	near
	and	ourflgs,not vtcntp	; disable print controller
	ret
pconoff endp
 
; check for printer ready -
; if not ready, return carry and beep
ckprn	proc	near
	push	ax
	mov	ah,ioctl
	mov	al,7			; get output status of printer
	push	bx
	mov	bx,prnhand		; file handle for system printer
	int	dos
	pop	bx
	jc	ckprn1			; not ready...return carry
	cmp	al,0ffh			; Ready status?
	jnz	ckprn1			; nz ==> not ready
	clc				; show ready
	jmp	ckprn2
ckprn1: call	beep			; let someone know
	stc				; indicate not ready
ckprn2: pop	ax
	ret
ckprn	endp
 
 
; Save the screen to a buffer and then append buffer to a disk file. [jrd]
; Default filename is Kermit.scn; actual file can be a device too. Filename
; is determined by mssset and is passed as pointer dmpname.
; Modified 1 Sept 1986 to do test for dump destination being ready. [jrd]
; Modified 24-AUG-90 [rhw-3] added translation table for VT100 graphics
;	to normal ascii chars.  This will make the dump file readable &
;	printable on any printer (even non-dec).  Now linedraw chars will
;	come out looking like boxes, etc.   See xlat_tab usage below...
 
dumpscr proc	near			; global dump-screen procedure
	push	ax
	push	bx
	push	cx
	push	dx
	call	savscr			; first, save screen to memory
	mov	dmphand,-1		; preset illegal handle
	mov	dx,offset dmpname	; name of disk file, from mssset
	mov	ax,dx			; where isfile wants name ptr
	call	isfile			; what kind of file is this?
	jc	dmp5			; c = no such file, create it
	test	byte ptr filtst.dta+21,1fh ; file attributes, ok to write?
	jnz	dmp0			; nz = no.
	mov	al,1			; writing
	mov	ah,open2		; open existing file
	int	dos
	jc	dmp0			; c = failure
	mov	dmphand,ax		; save file handle
	mov	bx,ax			; need handle here
	mov	cx,0ffffh		; setup file pointer
	mov	dx,-1			; and offset
	mov	al,2			; move to eof minus one byte
	mov	ah,lseek		; seek the end
	int	dos
	jmp	dmp1
 
dmp5:	test	filtst.fstat,80h	; access problem?
	jnz	dmp0			; nz = yes
	mov	ah,creat2		; file did not exist
	mov	cx,20h			; attributes, archive bit
	int	dos
	mov	dmphand,ax		; save file handle
	jnc	dmp1			; nc = ok
 
dmp0:	mov	dx,offset dmperr	; say no can do
	mov	ah,prstr
	int	dos
	pop	dx
	pop	cx
	pop	bx
	pop	ax
	clc
	ret
		; read screen buffer (132 char/line), write lines to file
dmp1:	mov	ah,ioctl		; is destination ready for output?
	mov	al,7			; test output status
	mov	bx,dmphand		; handle
	int	dos
	jc	dmp0			; c = error
	cmp	al,0ffh			; ready?
	jne	dmp0			; ne = not ready
	push	di			; save regs around this work
	push	si
	mov	si,ourscr		; buffer where screen was stored
	mov	cx,slen			; number of saved screen lines
dmp2:	push	cx			; save outer loop counter
	mov	di,offset dumpbuf	; data segement memory (work buffer)
	mov	cx,swidth		; number of screen columns = 132
dmp3:	mov	al,byte ptr [si]	; read text char
	mov	byte ptr [di],al	; store just char, don't use es:
	inc	si			; update pointers (si needed below)
	inc	di
	loop	dmp3			; do for all 132 columns, fixed format
				; find end of line, replace nulls with spaces
	mov	cx,swidth		; max chars in a line
	mov	di,offset dumpbuf	; look at start of line
dmp7:	mov	al,byte ptr [di]	; get a byte into al
	cmp	al,0ffh			; end of line indicator?
	je	dmp9			; e = yes, exit with count left in cx

;;	cmp	al,0			; is it a nasty null?		x[rhw-3]
;;	jne	dmp8			; ne = no			x[rhw-3]
;;	mov	al,' '			; replace null with nice space	x[rhw-3]
	cmp	al,' '			; [rhw-3] see if unprintable
	jae	dmp8			; [rhw-3] if its >= space then ok
	mov	dx,bx			; [rhw-3] save bx, need to use it
	mov	bx,offset xlat_tab	; [rhw-3] get ptr to xlation buffer
	xlat				; [rhw-3] lookup & swap old for new char
	mov	bx,dx			; [rhw-3] restore bx

dmp8:	mov	byte ptr [di],al	; put back into buffer
	inc	di			; look ahead for next char
	loop	dmp7			; scan the rest of the line
dmp9:	dec	di			; make di point at eol address
	mov	ax,swidth		; max line length
	sub	ax,cx			; minus chars scanned = length of line
	mov	cx,ax			; remember it here
					; trim line of trailing spaces
	push	es			; save register
	mov	ax,ds			; di needs to point to data space
	mov	es,ax			; string ops use es:di
	mov	al,' '			; thing to scan over
	std				; set scan backward
	repe	scasb			; scan until non-space, dec's di
	cld				; set direction forward
	pop	es			; restore reg
	jz	dmp3a			; z = all spaces (count was exhausted)
	inc	cx			; go forward over last non-space
	inc	di			; ditto for address
dmp3a:	mov	word ptr [di+1],0A0Dh	; append cr/lf
	add	cx,2			; line count + cr/lf
	mov	dx,offset dumpbuf	; array to be written
	mov	bx,dmphand		; need file handle
	mov	ah,write2		; write the line
	int	dos
	pop	cx			; get line counter again
	loop	dmp2			; do next line (needs si preset)
	mov	dx,offset dumpsep	; put in formfeed/cr/lf
	mov	cx,3			; three bytes overall
	mov	ah,write2
	mov	bx,dmphand
	int	dos
	mov	ah,close2		; close the file now
	int	dos
	pop	si
	pop	di
	pop	dx
	pop	cx
	pop	bx
	pop	ax
	clc
	ret
dumpscr endp
 
; Send a character to the host, handle local echo
sndhst	proc	near
	push	ax		   ; save the character
	mov	ah,al
	call	outchr		   ; send char in ah out the serial port
	pop	ax
	test	ourarg.flgs,lclecho ; echoing?
	jz	sndhs2		     ; z = no, exit
	call	outtty		   ; echo to screen
sndhs2: ret
sndhst	endp
 
 
; print a message to the screen.  Returns normally.
; also puts the terminal into vt100 mode so our escape sequences work...
tmsg	proc	near
	push	es
	push	bx
	mov	bx,nvmseg
	mov	es,bx		; address firmware
	mov	bl,0f1h		; turn on vt100 mode
	xchg	bl,es:[vt52mod] ; remember old mode
	mov	ah,prstr
	int	dos
	mov	es:[vt52mod],bl ; restore mode
	pop	bx
	pop	es
	ret
tmsg	endp
 
; save the screen for later
savscr	proc	near
	push	es
	push	ds
	call	kilcur		; turn off cursor
	mov	di,ourscr	; place to save screen
	mov	dx,ourattr	; and to save attributes
	mov	ax,ds
	mov	es,ax		; save buffer is in data space (ds seg)
	mov	cx,slen		; # of lines to do
	mov	ax,scrseg
	mov	ds,ax
	mov	bx,0		; current line #
savsc1: push	cx		; save current count
	mov	si,ds:[latofs+bx] ; get line ptr
	and	si,0fffh	; ptr is in 12 bits
	push	si		; save pointer
	mov	cx,swidth/2	; # of words/line, [RHW-5] swidth always even
	cld
	rep	movsw		; [RHW-4] copy it out by words
	pop	si		; restore pointer
	add	si,attoffs	; this is where attributes start
	xchg	dx,di		; this holds attribute ptr
	mov	cx,swidth/2	; # of words of attrs to move, [RHW-5]
	rep	movsw		;  [RHW-4] copy words at a time (quicker)
	xchg	dx,di
	pop	cx		; restore counter
	add	bx,2		; increment line ptr
	loop	savsc1		; save all lines and attributes
	pop	ds
	call	rstcur		; put cursor back
	call	savpos		; might as well save cursor pos
	pop	es
	ret
savscr	endp
 
; restore the screen saved by savscr
rstscr	proc	near
	call	cmblnk		; start by clearing screen
	mov	si,ourscr	; point to saved screen
	mov	dx,ourattr	; and attributes
	mov	cx,slen		; # of lines/screen
	mov	bx,101H		; start at top left corner
rstsc1: push	bx
	push	cx
	push	si		; save ptrs
	push	dx
	mov	ax,si		; this is source
	call	prlina		; print the line
	pop	dx
	pop	si
	pop	cx
	pop	bx
	add	si,swidth	; point to next line
	add	dx,swidth	; and next attributes
	inc	bx		; address next line
	loop	rstsc1		; keep restore lines
	call	rstpos		; don't forget position
	ret
rstscr	endp
 
; Put a line into the circular buffer.	Pass the buffer structure in bx.
; Source is srcseg:si
; Rewritten by [jrd]
putcirc proc	near
	push	es
	push	di
	push	cx
	mov	cl,swidth		; number of columns
	xor	ch,ch
	mov	es,[bx].orig		; get segment of memory area
	cmp	bx,offset bwnd		; bottom buffer?
	je	putci6			; e = yes
	mov	di,twnd.pp		; pick up buffer ptr (offset from es)
	add	di,cx			; increment to next available slot
	add	di,cx			; char and attribute
	cmp	di,twnd.bend		; would line extend beyond buffer?
	jb	putci1			; b = not beyond end
	mov	di,0			; else start at the beginning
putci1: mov	twnd.pp,di		; update ptr
	cld				; set direction to forward
	mov	cx,swidth
	push	ds			; save regular data seg reg
	mov	ds,srcseg		; use source segment for ds:si
	rep	movsw			; copy into buffer
	pop	ds			; restore regular data segment
	mov	cx,twnd.lmax		; line capacity of buffer
	dec	cx			; minus one work space line
	cmp	twnd.lcnt,cx		; can we increment line count?
	jae	putci1b			; ae = no, keep going
	inc	twnd.lcnt		; else count this line
putci1b:cmp	bwnd.lcnt,0		; any lines in bottom buffer?
	je	putci2			; e = no
	mov	cx,bwnd.pp		; see if we overlap bot buf
	cmp	cx,twnd.pp		; is this line in bot buf area?
	jne	putci2			; ne = no
	add	cl,swidth		; move bottom pointer one slot earlier
	adc	ch,0
	add	cl,swidth		; words
	adc	ch,0
	cmp	cx,bwnd.bend		; beyond end of buffer?
	jb	putci1a			; b = no
	mov	cx,0			; yes, start at beginning of buffer
putci1a:mov	bwnd.pp,cx		; new bottom pointer
	dec	bwnd.lcnt		; one less line in bottom buffer
putci2: pop	cx
	pop	di
	pop	es
	ret
putci6:					; bottom buffer
	add	cx,cx			; words worth
	cmp	bwnd.lcnt,0		; any lines in the buffer yet?
	jne	putci7			; ne = yes
	mov	di,twnd.pp		; get latest used slot of top buff
	add	di,cx			; where first free (?) slot starts
	cmp	di,bwnd.bend		; are we now beyond the buffer?
	jb	putci6a			; b = no
	mov	di,0			; yes, start at beginning of buffer
putci6a:add	di,cx			; start of second free (?) slot
	cmp	di,bwnd.bend		; are we now beyond the buffer?
	jb	putci6b			; b = no
	mov	di,0			; yes, start at beginning of buffer
putci6b:mov	cx,twnd.lmax		; buffer line capacity
	sub	cx,twnd.lcnt		; minus number used by top buffer
	sub	cx,2			; minus one work slot and one we need
	cmp	cx,0			; overused some slots?
	jge	putci8			; ge = enough to share
	add	twnd.lcnt,cx		; steal these from top window beginning
	jmp	short putci8
 
putci7: mov	es,bwnd.orig		; get segment of memory area
	mov	di,bwnd.pp		; pick up buffer ptr (offset from es)
	cmp	di,0			; would line start before buffer?
	jne	putci7a			; ne = after start of buffer
	mov	di,bwnd.bend		; else start at the end minus one slot
	inc	di
putci7a:sub	di,cx
putci8: mov	bwnd.pp,di		; update ptr (this is latest used slot)
	mov	cl,swidth
	xor	ch,ch
	cld				; set direction to forward
	push	ds			; save regular data seg reg
	mov	ds,srcseg		; use source segment for ds:si
	rep	movsw			; copy into buffer
	pop	ds			; restore regular data segment
	mov	cx,bwnd.lmax		; line capacity of buffer
	cmp	bwnd.lcnt,cx		; can we increment line count?
	jae	putci8b			; ae = no, keep going
	inc	bwnd.lcnt		; else count this line
putci8b:cmp	twnd.lcnt,0		; any lines in top line buf?
	je	putci9			; e = no
	mov	cx,twnd.pp		; yes, see if we used last top line
	cmp	cx,bwnd.pp		; where we just wrote
	jne	putci9			; not same place, so all is well
	dec	twnd.lcnt		; one less line in top window
	cmp	cx,0			; currently at start of buffer?
	jne	putci8a			; ne = no
	mov	cx,twnd.bend		; yes
	inc	cx
putci8a:sub	cl,swidth		; back up top window
	sbb	ch,0
	sub	cl,swidth		; by one line
	sbb	ch,0
	mov	twnd.pp,cx		; next place to read
putci9: pop	cx
	pop	di
	pop	es
	ret
 
putcirc endp
 
; Get a line from the circular buffer, removing it from the buffer.
; returns with carry on if the buffer is empty.
; Pass the buffer structure in bx.
; Destination preset in es:di.
; Rewritten by [jrd]
getcirc proc	near
	cmp	[bx].lcnt,0		; any lines in buffer?
	jne	getci1			; ne = yes, ok to take one out
	stc				; else set carry
	ret				; and return
getci1: push	cx			; top and bottom window common code
	push	si
	mov	cl,swidth		; # of chars to copy
	xor	ch,ch
	push	di			; save destintion offset
	push	cx			; save around calls
	mov	si,[bx].pp		; this is source
	cld				; set direction to forward
	push	ds			; save original ds
	mov	ds,[bx].orig		; use seg address of buffer for si
	rep	movsw
	pop	ds			; recover original data segment
	pop	cx			;
	pop	di			; recover destination offset
 
	push	cx			; save again
	mov	si,[bx].pp		; get ptr again
	pop	cx
	add	cx,cx			; words
	cmp	bx,offset bwnd		; bottom window?
	je	getci6			; e = yes
	sub	si,cx			; top window, move back
	jnc	getcir2			; nc = still in buffer, continue
	mov	si,twnd.bend		; else use end of buffer
	sub	si,cx			; minus length of a piece
	inc	si
getcir2:mov	twnd.pp,si		; update ptr
	dec	twnd.lcnt		; decrement # of lines in buffer
	pop	si
	pop	cx
	clc				; make sure no carry
	ret
getci6:					; bottom window
	add	si,cx			; words, move back (bot buf = reverse)
	cmp	si,bwnd.bend		; still in buffer?
	jb	getci7			; b = yes
	mov	si,0			; else use beginning of buffer
getci7: mov	bwnd.pp,si		; update ptr
	dec	bwnd.lcnt		; decrement # of lines in buffer
	pop	si
	pop	cx
	clc				; make sure no carry
	ret
getcirc endp
 
; prepares for scrolling by saving the top line in topbuf.
scrprep proc	near
	push	ax		; save regs
	push	es
	cmp	al,cr		; carriage return?
	je	scrpr3		; yes, will never change line
	cmp	al,lf		; outputting a linefeed?
	je	scrpr2		; yes, will change line
	mov	ax,scrseg
	mov	es,ax		; address screen segment
	test	es:byte ptr [curlin],wrpend ; wrap pending?
	jz	scrpr3		; no, forget it
	mov	ax,nvmseg
	mov	es,ax
	test	es:byte ptr [autwrp],1 ; auto-wrap mode?
	jz	scrpr3		; no, forget this
				; about to change lines, see if bottom line
scrpr2: mov	ax,scrseg
	mov	es,ax
	cmp	es:byte ptr [csrlin],slen ; are we at the bottom?
	je	scrpr4		; yes, have to save line
scrpr3: pop	es		; get here if not saving line
	pop	ax
	ret
scrpr4: pop	es		; restore registers
	pop	ax
; alternate entry that doesn't check if we're on the bottom row.
savtop: push	ax		; save ax
	push	bx
	push	cx
	push	si
	push	di
	call	kilcur		; kill cursor
	push	es
	push	ds
	mov	ax,ds
	mov	es,ax
	mov	ax,scrseg
	mov	ds,ax		; address screen segment
	mov	si,ds:word ptr [l1ptr] ; get ptr to top line
	and	si,0fffh	; only 12 bits are significant
	push	si
	mov	di,offset topline ; this is where it goes
	mov	cx,swidth/2	; # of words to copy,	 [RHW-5]
	cld
	rep	movsw		; [RHW-4] get the top line
	pop	si		; restore pointer
	add	si,attoffs	; add offset of attributes
	mov	cx,swidth/2	; [RHW-5] # words to copy (swidth even)
	rep	movsw		; [RHW-4] get the top line's attributes
	pop	ds
	pop	es		; restore segments
	mov	srcseg,seg topline ; set segment of source
	mov	si,offset topline  ; set offset of source
	mov	bx,offset twnd	; buffer structure pointer
	call	putcirc		; put into circular buffer
	call	rstcur
	pop	di
	pop	si
	pop	cx
	pop	bx
	pop	ax		; restore character in ax
	ret			; and return
scrprep endp
 
 
; get the screen's bottom line into the buffer in ax.
getbot	proc	near
	push	cx
	push	si
	push	di
	push	es
	push	ds
	push	ax		; save destination on the stack
	call	kilcur		; kill cursor
	push	ds
	pop	es
	mov	si,scrseg
	mov	ds,si
	mov	si,ds:word ptr [llptr] ; get ptr to bottom line
	and	si,0fffh	; only 12 bits are pointer
	pop	di		; destination is on stack
	push	si		; preserve pointer
	mov	cx,swidth/2	; # of words to copy		 [RHW-5]
	cld
	rep	movsw		; [RHW-4] get the top line
	pop	si		; get pointer back
	add	si,attoffs	; this is where attributes are
	mov	cx,swidth/2	;  [RHW-5] # words to copy
	rep	movsw		;  [RHW-4] get the top line
	pop	ds		; restore segments
	pop	es
	call	rstcur		; restore cursor
	pop	di
	pop	si
	pop	cx
	ret
getbot	endp
 
; handle the previous screen button...
 
prvscr	proc	near
	mov	cx,slen
	cmp	twnd.lcnt,cx
	jge	prvs1
	mov	cx,twnd.lcnt
prvs1:	jcxz	prvs2
	call	prvlin
	loop	prvs1
prvs2:	clc
	ret
prvscr	endp
 
; handle the next screen button...
 
nxtscr	proc	near
	mov	cx,slen
	cmp	bwnd.lcnt,cx
	jge	nxts1
	mov	cx,bwnd.lcnt
nxts1:	jcxz	nxts2
	call	nxtlin
	loop	nxts1
nxts2:	clc
	ret
nxtscr	endp
 
; save a screen by rolling them into a circular buffer.
; enter with ax/ circular buffer ptr, bx/ first line to get
; dx/ increment
 
rolscr	proc	near
	mov	cx,slen		; # of lines to save
	cmp	cx,[bx].lcnt	; enough space left in this buffer?
	jbe	rolsc0		; be = enough
	mov	cx,[bx].lcnt	; use only as many as we have
	jcxz	rolscx		; z = none, so quit here
rolsc0: shl	dx,1		; double increment for word ptr
	dec	bx		; ptr starts at 0
	shl	bx,1		; convert to word ptr
	push	es
	push	ds
	pop	es		; set es to data segment
	mov	temp,0		; save number of lines done
 
rolsc1: push	cx
	push	dx
	push	bx
	push	ax
	push	ds
	call	kilcur		; kill cursor
	mov	di,offset rlbuf
	mov	ax,scrseg
	mov	ds,ax		; address screen
	mov	si,ds:[latofs+bx] ; get current line
	and	si,0fffh	; ptr is in 12 bits
	push	si		; save pointer
	mov	cx,swidth/2	; # of words to move,		 [RHW-5]
	rep	movsw		; [RHW-4] get the line
	pop	si		; restore pointer
	add	si,attoffs	; where attributes start
	mov	cx,swidth/2	; # of bytes to move,		 [RHW-5]
	rep	movsw		; [RHW-4] move attributes as well
	pop	ds		; restore segment
	call	rstcur		; restore cursor
	pop	bx		; this is desired circ buffer ptr
	mov	srcseg,seg rlbuf ; segment of source
	mov	si,offset rlbuf ; offset of source
	call	putcirc		; save in circular buffer
	mov	ax,bx		; put buffer ptr back where it belongs
	pop	bx		; get line pos back
	pop	dx		; and increment
	pop	cx		; don't forget counter
	add	bx,dx		; move to next line
	inc	temp
	loop	rolsc1		; loop thru all lines
	pop	es
rolscx: ret			; and return
rolscr	endp
 
; move screen down a line, get one previous line back
 
prvlin	proc	near		; get the previous line back
	push	ax
	push	bx
	push	dx
	cmp	twnd.lcnt,0	; any lines in top window?
	je	prvli1		; e = no, ignore request
	mov	ax,offset botline ; place for bottom line
	call	getbot		; fetch bottom line
	mov	srcseg,seg botline ; segement of source
	mov	si,offset botline  ; offset of source
	mov	bx,offset bwnd	; buffer structure pointer
	call	putcirc		; save in circular buffer
	mov	bx,offset twnd	; source structure
	push	es
	mov	di,seg topline
	mov	es,di
	mov	di,offset topline ; es:di is address of destination
	call	getcirc		; try to get a line
	pop	es
	jc	prvli1		; no more, just return
	call	savpos		; save cursor position
	call	kilcur		; turn off cursor
	mov	dx,offset topdwn ; home, then reverse index
	call	tmsg
	mov	ax,offset topline ; point to data
	mov	bx,0101H	; print line at top of screen
	mov	dx,ax
	add	dx,swidth
	call	prlina		; print the line
	call	rstcur
	call	rstpos		; restore cursor position
prvli1: pop	dx
	pop	bx
	pop	ax
	clc
	ret			; and return
prvlin	endp
 
; move screen up a line, get one bottom line back
nxtlin	proc	near
	push	ax
	push	bx
	push	dx
	cmp	bwnd.lcnt,0	; any lines in bottom window?
	je	nxtli1		; e = no
	mov	bx,offset bwnd	; source structure
	push	es
	mov	di,seg botline
	mov	es,di
	mov	di,offset botline ; es:di is address of destination
	call	getcirc		; try to get a line
	pop	es
	jc	nxtli1		; no more, just return
	call	savtop		; save one line off of top
	call	savpos		; save cursor position
	call	kilcur		; turn off cursor
	mov	dx,offset botup ; go to bottom, then scroll up a line
	call	tmsg
	mov	ax,offset botline ; point to data
	mov	bx,0100H + slen ; print at bottom line
	mov	dx,ax
	add	dx,swidth
	call	prlina		; print the line
	call	rstcur		; turn cursor back on
	call	rstpos		; restore cursor position
nxtli1: pop	dx
	pop	bx
	pop	ax
	clc
	ret
nxtlin	endp
 
; move screen up until no more saved in bottom buffer [jrd]
nxtbot	proc	near
	push	cx
	mov	cx,bwnd.lcnt	; number of pages of scroll back memory
nxtbot1:push	cx
	call	nxtlin
	pop	cx
	loop	nxtbot1		; do all pages
	pop	cx
	clc
	ret
nxtbot	endp
 
nxttop	proc	near		; move to top of screen buffer memory [jrd]
	push	cx
	mov	cx,twnd.lcnt	; number of pages of scroll back memory
nxttop1:push	cx
	call	prvlin
	pop	cx
	loop	nxttop1		; do all pages
	pop	cx
	clc
	ret
nxttop	endp
 
; save cursor position
savpos	proc	near
	push	di
	mov	dx,offset curinq ; where is the cursor?
	call	tmsg
	mov	posbuf,escape	; put an escape in the buffer first
	mov	di,offset posbuf+1
savpo1: mov	ah,8		; read, no echo
	int	dos
	cld
	cmp	al,'R'		; end of report?
	je	savpo2		; yes
	stosb			; no, save it
	jmp	short savpo1	; and go on
savpo2: mov	al,'H'		; this ends the sequence when we send it
	stosb
	mov	byte ptr [di],'$' ; need this to print it later
	pop	di
	ret
savpos	endp
 
; restore the position saved by savpos
rstpos	proc	near
	mov	dx,offset posbuf
	call	tmsg		; just print this
	ret
rstpos	endp
 
; kill cursor so it doesn't get saved along with real data
kilcur	proc	near
	push	ax
	push	bx
	push	cx
	push	dx
	push	si
	push	es
	push	di
	mov	di,kcurfn
	int	firmwr
	pop	di
	pop	es
	pop	si
	pop	dx
	pop	cx
	pop	bx
	pop	ax
	ret
kilcur	endp
 
; restore the cursor
rstcur	proc	near
	push	ax
	push	bx
	push	cx
	push	dx
	push	si
	push	es
	push	di
	mov	di,rcurfn
	int	firmwr
	pop	di
	pop	es
	pop	si
	pop	dx
	pop	cx
	pop	bx
	pop	ax
	ret
rstcur	endp
 
; print a ff-terminated line at most swidth long...  Pass the line in ax.
; cursor position should be in bx.
; prlina writes attributes as well, which should be passed in dx.
prlin	proc	near
	mov	bp,2		; print characters only
	jmp	short prli1
prlina: xor	bp,bp		; 0 means print attributes as well
prli1:	push	es		; this trashes es!!!
	push	ax		; save regs
	push	cx
	push	es
	mov	cx,scrseg
	mov	es,cx		; address screen seg for a moment
	mov	cl,es:byte ptr [rmargin] ; get max line length
	mov	ch,0
	pop	es		; address user's segment again
	push	cx		; remember original length
	mov	si,ax		; better place for ptr
	mov	di,ax		; need it here for scan
	mov	al,0ffh		; this marks the end of the line
	cld
	repne	scasb		; look for the end
	jne	prli2		; not found
	inc	cx		; account for pre-decrement
prli2:	neg	cx
	pop	ax		; get original length back
	add	cx,ax
;	add	cx,swidth	; figure out length of line
	jcxz	prli3		; 0-length line, skip it
	mov	ax,bp		; writing characters
	mov	bp,ds		; wants segment here
	push	bx
	push	dx
	push	si
	push	di
	mov	di,14H		; fast write to screen
	int	firmwr
	pop	di
	pop	si
	pop	dx
	pop	bx
prli3:	pop	cx		; restore registers
	pop	ax
	pop	es
	ret
prlin	endp
 
; action routines for keyboard translator.
; These are invoked by a jump instruction. Return carry clear for normal
; processing, return carry set for invoking Quit (kbdflg has transfer char).
 
uparrw: mov	al,'A'			; cursor keys
	jmp	short comarr
dnarrw: mov	al,'B'
	jmp	short comarr
rtarr:	mov	al,'C'
	jmp	short comarr
lfarr:	mov	al,'D'
comarr: push	ax			; save final char
	mov	al,escape		; Output an escape
	call	sndhst			; Output, echo permitted
	push	es			; save seg register
	mov	ax,nvmseg		; fetch address of nvm
	mov	es,ax			; put in segment reg
	test	byte ptr es:[vt52mod],1 ; check bit zero
	pop	es			; restore the seg register
	jnz	comar1			; nz = ansi mode
	jmp	comar3			; VT52 mode
comar1: mov	al,'['			; Maybe this next?
	cmp	ckeyflg,0		; applications cursor mode?
	je	comar2			; e = no
	mov	al,'O'			; applications mode
comar2: call	sndhst			; Output it (echo permitted)
comar3: pop	ax			; recover final char
	call	sndhst			; Output to port (echo permitted)
	clc
	ret
 
pf1:	mov	al,'P'			; keypad function keys
	jmp	short compf
pf2:	mov	al,'Q'
	jmp	short compf
pf3:	mov	al,'R'
	jmp	short compf
pf4:	mov	al,'S'
compf:	push	ax			; save final char
	mov	al,escape		; Output an escape
	call	prtbout
	push	es			; save seg register
	mov	ax,nvmseg		; fetch address of nvm
	mov	es,ax			; put in segment reg
	test	byte ptr es:[vt52mod],1	  ; check bit zero
	pop	es			; restore the seg register
	jz	compf1			; z = VT52 mode
	mov	al,'O'			; send an "O" in ansi mode
	call	prtbout			; Output it
compf1: pop	ax			; Get the saved char back
	call	prtbout			; Output to port
	clc
	ret
 
kp0:	mov	al,'p'			; keypad numeric keys
	jmp	short comkp
kp1:	mov	al,'q'
	jmp	short comkp
kp2:	mov	al,'r'
	jmp	short comkp
kp3:	mov	al,'s'
	jmp	short comkp
kp4:	mov	al,'t'
	jmp	short comkp
kp5:	mov	al,'u'
	jmp	short comkp
kp6:	mov	al,'v'
	jmp	short comkp
kp7:	mov	al,'w'
	jmp	short comkp
kp8:	mov	al,'x'
	jmp	short comkp
kp9:	mov	al,'y'
	jmp	short comkp
kpminus:mov	al,'m'
	jmp	short comkp
kpcoma: mov	al,'l'
	jmp	short comkp
kpenter:mov	al,'M'
	jmp	short comkp
kpdot:	mov	al,'n'
comkp:	cmp	akeyflg,0		; alternate keypad mode?
	jne	comkp1			; ne = yes
	sub	al,'p'-'0'		; change to numeric codes
	jmp	comkp3			; and send just the ascii char
comkp1: push	ax			; save final char
	mov	al,escape		; Output an escape
	call	prtbout
	mov	al,3fh			; Output the "?" in VT52 mode
	push	es			; save seg register
	mov	cx,nvmseg		; fetch address of nvm
	mov	es,cx			; put in segment reg
	test	byte ptr es:[vt52mod],1	  ; check bit zero
	pop	es			; restore the seg register
	jz	comkp2			; z = VT52 mode
	mov	al,'O'			; Output the "O" in ansi mode
comkp2: call	prtbout
	pop	ax			; recover final char
comkp3: call	prtbout			; send it
	clc
	ret
 
snull	proc	near			; send a null byte
	mov	al,0			; the null
	call	prtbout			; send without logging and local echo
	clc
	ret
snull	endp
 
chrout: push	es			; save segment
	push	bx
	mov	bx,nvmseg		; point to nvm
	mov	es,bx			; set the segment
	test	byte ptr es:[newlmod],1 ; check if we are set to new line
	pop	bx
	pop	es			; restore the segment register
	jz	chrout1			; just send the cr if bit is off
	call	sndhst			; else send the carriage return first
	mov	al,lf			; then the line-feed
chrout1:call	sndhst			; send char in al to serial port
	clc				; for keyboard translator
	ret				; can echo and log
 
 
prtbout:mov	ah,al			; output char with no echo
	call	outchr			; outchr works with ah
	ret
 
cdos:	mov	al,'P'			; Connect mode help screen items
	jmp	short cmdcom
cstatus:mov	al,'S'			; these commands invoke Quit
	jmp	short cmdcom
cquit:	mov	al,'C'			; C = exit connection
	jmp	short cmdcom
cquery: mov	al,'?'			; help
	jmp	short cmdcom
chang:	mov	al,'H'			; Hangup, drop DTR & RTS
	jmp	short cmdcom
cmdcom: mov	kbdflg,al		; pass char to msster.asm via kbdflg
	stc				; signal that Quit is needed
	ret
 
klogon	proc	near			; resume logging (if any)
	test	flags.capflg,logses	; session logging enabled?
	jz	klogn			; z = no, forget it
	or	ourarg.flgs,capt	; turn on capture flag
	or	argadr.flgs,capt	; permanent argument array too
klogn:	clc
	ret
klogon	endp
 
klogof	proc	near			; suspend logging (if any)
	and	argadr.flgs,not capt	; stop capturing
	and	ourarg.flgs,not capt	; stop capturing
klogo:	clc
	ret
klogof	endp
code	ends
	end