       name msxv90
; File MSXV90.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 VICTOR 9000/SIRIUS
; Edit History
; 2 March 1991 version 3.10
; Last edit: 17 Dec 1990 MS-DOS Kermit v3.02 editing. [jrd]
; 12 June 1988 Add error recovery if serial port fails to initialize. [jrd]
; 9 March 1988 Add procedure getmodem and global byte mdmhand for use by
;  scripts in sensing modem status. Add: ignore received XOFF if we have sent
;  an XOFF already (avoids lockouts from echoes). Add user/buffer xon/xoff
;  sensing. [jrd]
; 3 March 1988 Add shomodem routine to show status of DSR, CD, CTS lines
;   [bgp]
; 7 Sept 1987 Remove keep_delete, pass null, del chars to terminal section.
;   [jrd]
; 29 August 1987 Add capability for sending long break [bgp]
; 9 February 1987 Add flag for ignoring delete chars in SERINT - they must
;   be retained when emulating a Tektronix (graphics mode) - this is done
;   using global symbol keep_delete.
; 6 November 1986 Fix receiver overrun detection and miscellaneous minor
;   fixes
; 30 Sept 1986 Reject DEL char at serial port reception level to avoid
;   problems when DEL is used as a filler char (be Emacs). [jrd]
; 16 Sept 1986 Revise serial port routines prtchr, outchr, serini, serint
;   to use more efficient code from IBM version. [jrd]
; 4 Sept 1986 Add Bob Goeke's change to move comms port table to a system
;   dependent module (typ msx---) to allow 3+ ports and localized idents. [jrd]
; 26 August 1986 Use parity mask when testing for nulls & Xon/Xoff in serial
;   port interrupt routine. [jrd]
; 16 August 1986 Use observed screen attributes for mode line. [jrd]
; 9 August 1986 Revise SERINT to insert control-G for overrun chars, give
;  faster return of interrupts to system, remove use of BP in code. [jrd]
;   Original version, BGP, 23 November 1985
; Add global entry point vtstat for use by Status routine in mssset.
; Cleared terminal emulation flag, flags.vtflg, in procedure lclini.
; Add register save/restore in procedure getbaud.
; Joe R. Doupnik 12 March 1986
; Add some register save/restores here and there.
; Add global procedures ihosts and ihostr to handle host initialization
; when packets are to be sent or received by us,resp. 24 March 1986
; Add global procedure dtrlow to force DTR and RTS low in support of Kermit
; command Hangup.   B.G.Peterson 10 April 1986
; Add support of serial port settings through use of IOCTL DOS function
; code from Andreas Stumpf (ZRZS@DS0RUS1I), merged by B.G.Peterson 10 April 86
; Moved VTS and VTSTAT routines to MSYxxx.ASM where the terminal emulation
; is done anyway. B.G.Peterson 10 April 1986
; Last update 28 April 1986
; 30 July 1986 Corrected IHOSTS and IHOSTR to prevent sending null byte if
;  no flow control.
;  Modified SERHNG so it turns DTR and RTS off for 3 seconds and then back
;  on again so a new connection can be made easily.
;  Modified SERRST to wait until transmitter is empty before turning off
;  port.
;  Modified port controller access so that if opening it using the standard
;  Victor drivers fails, it will just go direct to the hardware. [bgp]

	public	serini, serrst, clrbuf, outchr, coms, dodel
	public	ctlu, cmblnk, locate, lclini, prtchr, dobaud
	public	clearl, getbaud, beep, puthlp, putmod
	public	clrmod, poscur, sendbr, showkey, sendbl, pcwait
	public	xofsnt, machnam, setktab, setkhlp, count
	public	ihosts, ihostr, dtrlow, comptab, serhng, mdmhand
	public	shomodem, getmodem, baudst, bdtab, portval
	public	parmsk, flowon, flowoff, peekcom

FALSE	equ	0
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 of buffer full.
DEF_BAUD EQU	8		; Default to 1200 baud

; constants used by serial port handler

SEG_7201 EQU	0E004H		; Segment for 7201 serial controller
DATAA_7201 EQU	0		; DATA A offset
STATA_7201 EQU	2		; STATUS A offset
DATAB_7201 EQU	1		; DATA B offset
STATB_7201 EQU	3		; STATUS B offset

;   no interrupts, no waits
REG1_7201 EQU	0		; 7201 Register 1 value
ENABLE_INT EQU	18H		; Mask to turn on interrupts (OR)
;   non-DMA, non-vectored interrupts, priority type 1 (Ra>Rb>Ta>Tb)
REG2_7201 EQU	14H		; 7201 Register 2 value
;   8 bits/char, no CRC, receiver enabled
REG3_7201 EQU	0C1H		; 7201 Register 3 value
;   clock/16, 1.5 stop bit, no parity
REG4_7201 EQU	48H		; 7201 Register 4 value
;   DTR low (active), 8 bits/char, transmitter enabled, RTS low, no CRC
REG5_7201 EQU	0EAH		; 7201 Register 5 value
BREAK_ON EQU	10H		; Mask to turn on break bit (OR)
DTR_RTS_OFF EQU 7DH		; Mask to turn off DTR and RTS (AND)

MDMINP	EQU	1		; Input ready bit. [jrd]
MDMOVER EQU	32		; Receiver overrun bit. [bgp]

SEG_8253 EQU	0E002H		; Segment for 8253 timer
SETA_8253 EQU	0		; Speed for port A
SETB_8253 EQU	1		; Speed for port B
CTRL_8253 EQU	3		; Control for 8253 timer

SEG_8259 EQU	0E000H		; Segment for 8259 int. controller
CW1_8259 EQU	0		; Offset for 8259 register 1
CW2_8259 EQU	1		; Offset for 8259 register 2

; external variables used:
; drives - # of disk drives on system
; 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.
; setktab - keyword table for redefining keys (should contain a 0 if
;     not implemented)
; setkhlp - help for setktab.

data	segment
	extrn	flags:byte, trans:byte

setktab db	0
setkhlp db	CR,LF,'Set Key not supported on VICTOR/SIRIUS Kermit$'
sh_key_str db	'Set Key not supported on VICTOR/SIRIUS Kermit'
sh_key_len dw	45
machnam db	'VICTOR/SIRIUS$'
clrlin	db	CR		; clears full line with next line...
clreol	db	ESCAPE,'K$'        ; clears from cursor to end of line
home	db	ESCAPE,'H$'        ; homes cursor
clrscr	db	ESCAPE,'E$'        ; clears screen and homes cursor
delstr	db	BS,BS,'  ',BS,BS,'$' ; Delete string
clr_25	db	ESCAPE,'j',ESCAPE,'x1',ESCAPE,'Y8 ',ESCAPE,'l',ESCAPE,'k'
	db	ESCAPE,'y1$'
	;clr_25 does the entire operation of clearing the mode line
start_25 db	ESCAPE,'j',ESCAPE,'x1',ESCAPE,'Y8 ',ESCAPE,'p$'
	; start_25 enables line 25 and moves to the start in reverse video
end_25	db	ESCAPE,'q',ESCAPE,'k',ESCAPE,'y1$'
	; end_25 turns off reverse video and line 25 and returns the cursor
mov_pfx db	ESCAPE,'Y$'        ; prefix for moves
mdminfo	struc
mddat	dw	0		; data register
mdiir	dw	0		; interrupt identification register
mdstat	dw	0		; line status register
mdcom	dw	0
mden	db	0
mddis	db	0
mdmeoi	db	0
mdintv	dw	0
mdminfo	ends
modem	mdminfo <DATAA_7201,0,STATA_7201,SETA_8253,0FDH,2,61H,104H>
port1	prtinfo	<0FFFH,0,defpar,1,0,defhand,floxon,0>	; UART ports
port2	prtinfo	<0FFFH,0,defpar,1,0,defhand,floxon,0>
portval	dw	port1			; Default is to use port 1
savsci	dw	?		; Save for serial port interrupt vector.
savscs	dw	?		; Ditto.
portin	db	FALSE		; Has comm port been initialized.
xofsnt	db	FALSE		; Say if we sent an XOFF.
xofrcv	db	FALSE		; Say if we received an XOFF.
parmsk	db	?		; parity mask, 0ffh for no parity, 07fh with.
flowoff db	?		; flow-off char, Xoff or null (if no flow)
flowon	db	?		; flow-on char, Xon or null
overrun db	?		; holds status of receiver overrun
mdmhand	db	0		; Modem status register, current.
erms20	db	CR,LF,'?Warning: System has no disk drives$'
badbd	db	CR,LF,'Unimplemented baud rate$'
hngmsg	db	CR,LF,' The phone should have hung up.',CR,LF,'$' ; [jrd]
hnghlp	db	CR,LF,' The modem control lines DTR and RTS for the current'
	db	' port are forced low (off)'
	db	CR,LF,' to hang up the phone.  Normally, Kermit leaves them'
	db	' high (on) when it exits.'
	db	CR,LF,' They will return high after about 3 seconds.'
	db	CR,LF,'$'                                       ; [jrd]
shmdm0	db	CR,LF,' Modem is not ready:  DSR is off$'
shmdm1	db	CR,LF,' Modem is ready:      DSR is on$'        ; [bgp]
shmdm2	db	CR,LF,' no Carrier Detect:   CD is off$'
shmdm3	db	CR,LF,' Carrier Detect:      CD is on$'
shmdm4	db	CR,LF,' no Clear to Send:    CTS is off$'
shmdm5	db	CR,LF,' Clear to Send:       CTS is on$'        ; [bgp]
tmp	db	?,'$'
temp1	dw	?		; Temporary storage.

ontab	db	02H		; Two entries.
	mkeyw	'OFF',00H
	mkeyw	'ON',01H

comptab db	0AH
	mkeyw	'1',01H
	mkeyw	'2',00H
	mkeyw	'A',01H
	mkeyw	'B',00H
	mkeyw	'COM1',01H
	mkeyw	'COM2',00H
	mkeyw	'SERIALA',01H
	mkeyw	'SERIALB',00H
	mkeyw	'TTY',01H
	mkeyw	'UL1',00H

bdtab	db	16			; Baud rate table
	mkeyw	'45.5',0
	mkeyw	'50',1
	mkeyw	'75',2
	mkeyw	'110',3
	mkeyw	'134.5',4
	mkeyw	'150',5
	mkeyw	'300',6
	mkeyw	'600',7
	mkeyw	'1200',8
	mkeyw	'1800',9
	mkeyw	'2000',10
	mkeyw	'2400',11
	mkeyw	'4800',12
	mkeyw	'9600',13
	mkeyw	'19200',14
	mkeyw	'38400',15

; This table is indexed by the baud rate definitions given above.
; Unsupported baud rates should contain FF.
;   This number is determined by 78125/(baud rate) (decimal values)

bddat	label	word
	dw	6B4H		; 45.5 baud
	dw	61AH		; 50 baud
	dw	411H		; 75 baud
	dw	2C6H		; 110 baud
	dw	244H		; 134.5 baud
	dw	208H		; 150 baud
	dw	104H		; 300 baud
	dw	82H		; 600 baud
	dw	41H		; 1200 baud
	dw	2BH		; 1800 baud
	dw	26H		; 2000 baud
	dw	20H		; 2400 baud
	dw	10H		; 4800 baud
	dw	8H		; 9600 baud
	dw	4H		; 19200 baud
	dw	2H		; 38400 baud

; variables for serial interrupt handler

source	db	BUFSIZ DUP(?)	; Buffer for data from port.
srcpnt	dw	0		; Pointer in buffer (DI).
count	dw	0		; Number of chars in int buffer.

; variables for accessing portinfo from serial drivers using IOCTL function
;
; Structure is defined according to "Systems Programmers Toolkit II",
; Appendix A.	    Structure contains baud rate and values of control registers
; 0 through 7.

pval	struc
stype	dw	11H		; port access
status	dw	(?)
blocktype dw	0		; serial
baudr	dw	(?)		; baud rate to set or get
CR0	db	(?)
CR1	db	(?)
CR2A	db	(?)
CR2B	db	(?)
CR3	db	(?)
CR4	db	(?)
CR5	db	(?)
CR6	db	(?)
CR7	db	(?)
pval	ends

erms41	db	CR,LF,'?Warning:  Cannot open com port'
	db	CR,LF,'  Going direct to serial controller hardware...$' ; [bgp]
rdbuf	db	20 dup (?)	; input buffer

plength equ	17		; length of pval structure
oldpval pval	<,,,41H,,,,,,,,,> ; default to 1200 baud
newpval pval	<,,,41H,,,,,,,,,> ; value comes from bdtab above... [bgp]

prttab	dw	com2,com1	; 0=com2, 1=com1 in flags.comflg
com1	db	'SERIALA',0     ; name string for device
com2	db	'SERIALB',0

IOread	equ	2		; read status block
IOwrite equ	3		; write status block

prthnd	dw	0		; handle for accessing port

data	ends

code	segment
	extrn	comnd:near, dopar:near, defkey:near
	extrn	lclyini:near
	assume	cs:code,ds:data,es:nothing

; local initialization

LCLINI	proc	near
	mov	flags.vtflg,TTHEATH ; BIOS does HEATH, use as default
	cmp	flags.comflg,1	; using port 1?
	jne	lclini2 	; no...
	mov	portval,offset port1
	mov	modem.mddat,DATAA_7201 ; set COM1 values
	mov	modem.mdstat,STATA_7201
	mov	modem.mdcom,SETA_8253
	jmp	lclini0
lclini2:			; using port2
	mov	portval,offset port2
	mov	modem.mddat,DATAB_7201 ; set COM2 values
	mov	modem.mdstat,STATB_7201
	mov	modem.mdcom,SETB_8253
lclini0:call	opnprt		; get file handle and init port
	call	lclyini 	; init term part too
	ret
LCLINI	endp

; procedure to get a file handle for the port.	if it fails ask the user
; for some predefined handle (3 is the usual value) (Andreas Stumpf)
; 30 July 1986 If it fails, just warn the user and go direct to the
;  hardware [bgp]

OPNPRT	proc	near
	cmp	prthnd,0	; is one open?
	jle	opnprta 	; no...
	mov	bx,prthnd	; better close this
	mov	ah,CLOSE2	; to be sure they don't accumulate
	int	DOS
	mov	prthnd,0	; done...
opnprta:
	mov	al,flags.comflg
	mov	ah,0
	mov	si,ax
	shl	si,1		; double index
	mov	dx,prttab[si]
	mov	ah,OPEN2
	mov	al,2
	int	DOS		; open port on handle
	jnc	opnprt2
	mov	ah,PRSTR	; it didn't like the string...
	mov	dx,offset erms41
	int	DOS
	mov	prthnd,-1	; no port is open! [bgp]
	push	es		; better save this
	mov	bx,SEG_7201	; point at controller
	mov	es,bx
	mov	bx,modem.mdstat
	mov	byte ptr es:[bx],1
	mov	byte ptr es:[bx],18H ; Software reset of current port
	push	ax		; kill time for four 2.5 MHz cylces
	pop	ax		; 8 processor cycles
	mov	bx,STATA_7201	; First one is always port A
	mov	byte ptr es:[bx],2 ; must be first one set
	mov	byte ptr es:[bx],REG2_7201
	mov	bx,modem.mdstat
	mov	byte ptr es:[bx],4 ; must be second
	mov	byte ptr es:[bx],REG4_7201
	mov	byte ptr es:[bx],1 ; rest any order
	mov	byte ptr es:[bx],REG1_7201
	mov	byte ptr es:[bx],3
	mov	byte ptr es:[bx],REG3_7201
	mov	byte ptr es:[bx],5
	mov	byte ptr es:[bx],REG5_7201
	pop	es
	call	getbaud 	; use this to be sure right value is used
	call	dobaud		; better set baud rate to the correct value too
	jmp	opnprt2a	; all init done... [bgp]
opnprt2:
	mov	prthnd,ax	; call succeeded - save handle
	mov	bx,ax
	mov	ah,IOCTL
	mov	al,IOread	; get old values
	mov	cx,plength
	mov	dx,offset oldpval ; place for old values
	int	DOS
	mov	ah,IOCTL
	mov	al,IOread
	mov	dx,offset newpval ; one to work on
	int	DOS

; set registers to something neat

	cli			; avoid interrupts here
	mov	bx,offset newpval
	mov	[bx].CR1,REG1_7201
	mov	[bx].CR2A,REG2_7201
	mov	[bx].CR3,REG3_7201
	mov	[bx].CR4,REG4_7201
	mov	[bx].CR5,REG5_7201
	mov	bx,prthnd	; get handle
	mov	ah,IOCTL
	mov	al,IOwrite	; set new values
	int	DOS
opnprt2a:
	push	es		; save this for a flash
	mov	bx,SEG_7201
	mov	es,bx
	mov	bx,modem.mdstat
	mov	byte ptr es:[bx],10H ; clear external/status interrupts
	mov	byte ptr es:[bx],30H ; clear special receive cond. int.
	mov	byte ptr es:[bx],38H ; set to end of interupt
	pop	es		; back again
	sti			; interrupts are okay again
	ret
OPNPRT	endp

; Show the definition of a key.  Since it isn't really necessary to redefine
; keys for the VICTOR/SIRIUS (assuming that KEYGEN is available), this isn't
; implemented, and the string returned to the calling sequence merely says so.
; Returns a string to print in AX, length of same in CX.
; Returns normally.

SHOWKEY proc	near
	mov	ax,offset sh_key_str
	mov	cx,sh_key_len
	ret
SHOWKEY 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	ax,offset source
	mov	srcpnt,ax
	mov	count,0
	sti
	ret
CLRBUF	endp

; Clear to the end of the current line.  Returns normally.

CLEARL	proc	near
	mov	dx,offset clreol
	mov	ah,PRSTR
	int	DOS
	ret
CLEARL	endp

; Put the char in AH to the serial port.  This assumes the
; port has been initialized. Skip returns on success, returns ret if the
; character cannot be written.
; 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:	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	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)
	mov	cl,0			;  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
	push	bx
	push	cx
	push	es
	xor	cx,cx
	mov	bx,SEG_7201	; point at 7201
	mov	es,bx
	mov	bx,modem.mdstat
outch3:
	mov	al,es:[bx]
	test	al,4		; ready?
	jnz	outch4		; yes
	loop	outch3
	jmp	outch5		; Timeout
outch4:
	mov	al,ah		; Now send it out
	mov	bx,modem.mddat
	mov	es:[bx],al
	pop	es
	pop	cx
	pop	bx
	ret
outch5:
	pop	es
	pop	cx
	pop	bx
	ret
OUTCHR	endp

; This routine blanks the screen.   Returns normally.

CMBLNK	proc	near
	mov	dx,offset clrscr
	mov	ah,PRSTR
	int	DOS
	ret
CMBLNK	endp

; Locate: homes the cursor.	Returns normally.

LOCATE	proc	near
	mov	dx,offset home
	mov	ah,PRSTR
	int	DOS
	ret
LOCATE	endp

; write a line in inverse video at the bottom of the screen...
; the line is passed in dx, terminated by a dollar sign.  Returns normally.

PUTMOD	proc	near
	push	si		; better save this
	push	dx		; preserve message
	mov	dx,offset start_25 ; to set up for write to 25
	mov	ah,PRSTR
	int	DOS
	mov	ah,DCONIO	; output a char at a time
	pop	si		; get back message
	cld			; better increment
putmod1:
	lodsb			; get byte
	cmp	al,'$'          ; is it end of string?
	je	putmod2
	mov	dl,al
	int	DOS
	jmp	putmod1
putmod2:
	mov	dx,offset end_25 ; back to normal
	mov	ah,PRSTR
	int	DOS
	pop	si		; and restore it
	ret
PUTMOD	endp

; clear the mode line written by putmod.  Returns normally.

CLRMOD	proc	near
	mov	dx,offset clr_25 ; to clear line 25
	mov	ah,PRSTR
	int	DOS
	ret
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	si
	mov	si,ax
	mov	ah,DCONIO	; don't check anything...
	cld			; better increment on strings
puthlp1:
	lodsb			; get byte
	cmp	al,0		; is it null (null-terminated string)
	je	puthlp2
	mov	dl,al
	int	DOS
	jmp	puthlp1
puthlp2:
	mov	dl,13		; want a crlf
	int	DOS
	mov	dl,10
	int	DOS
	pop	si
	ret
PUTHLP	endp

; Set the baud rate for the current port, based on the value
; in the portinfo structure.  Returns carry clear.

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
; Set the baud rate for the current port, based on the value
; in the portinfo structure. Returns normally. Method of setting using
; IOCTL thanks to Andreas Stumpf

DOBAUD	proc	near
	push	ax		; these too [jrd]
	push	bx
	push	cx
	push	dx
	mov	bx,portval
	mov	bx,[bx].baud
	cmp	bx,0
	jl	unk_baud	; out of range
	cmp	bl,bdtab	; number of table entries
	jge	unk_baud	; out of range
	shl	bx,1		; get index into table
	mov	ax,offset bddat ; start of table
	add	bx,ax
	mov	ax,[bx] 	; get divider
	cmp	ax,0FFH 	; unimplemented baud rate?
	je	unk_baud	; that's right
	mov	bx,offset newpval
	mov	[bx].baudr,ax	; set it in structure no matter what [bgp]
	cmp	prthnd,0	; anything open?
	jge	dobaud0 	; yes, do it nice
	push	es		; better save this...
	push	ax		; save divider
	mov	bx,SEG_8253	; point at timer
	mov	es,bx
	mov	bx,CTRL_8253	; set up function first
	mov	ax,modem.mdcom	; set up control byte
	ror	al,1		; need port number in high bits
	ror	al,1
	and	al,0C0H 	; keep only top 2 bits
	add	al,36H		; set both, Mode 3, binary
	mov	es:[bx],al
	mov	bx,modem.mdcom	; Where to write the rate
	pop	ax		; get divider back
	mov	es:[bx],al
	mov	es:[bx],ah	; done
	pop	es
	jmp	dobaud1
dobaud0:			; [bgp]
	mov	ah,IOCTL
	mov	al,IOwrite
	mov	bx,prthnd	; set the poor thing
	mov	cx,plength
	mov	dx,offset newpval
	int	DOS
dobaud1:
	pop	dx
	pop	cx
	pop	bx
	pop	ax
	ret
unk_baud:
	mov	ah,PRSTR
	mov	dx,offset badbd ; Give an error message.
	int	DOS
	pop	dx		; restore regs [jrd]
	pop	cx
	pop	bx
	pop	ax
	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.	    The method of getting the baud
; rate directly from the port handler is thanks to Andreas Stumpf.
; Note that this assumes that the thing has a defined baud rate to
; start with from the initialization and the opnprt has been called on
; the current port so that the values in newpval are defined.

GETBAUD proc	near
	push	ax
	push	bx
	push	cx
	push	dx
	cmp	prthnd,0	; opened?
	jne	go_gb		; yes, get the rate
	call	opnprt		; no, open it
go_gb:
	mov	bx,offset newpval
	mov	ax,[bx].baudr
	xor	ch,ch
	mov	cl,bdtab	; entries in baudtable
	mov	bx,offset bddat-2
	add	bx,cx
	add	bx,cx
loop_gb:
	cmp	ax,[bx]
	je	have_gb
	dec	bx
	dec	bx
	dec	cx
	jnz	loop_gb
have_gb:
	dec	cx		; value is one greater than desired
	mov	bx,portval	; cx=-1 means unrecognized (dropped off bottom)
	mov	[bx].baud,cx
	pop	dx
	pop	cx
	pop	bx
	pop	ax
	ret
GETBAUD endp

; 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]
; Direct copy from msxibm.asm [jrd]

PRTCHR	proc	near
	call	chkxon		; see if we need to xon
	cmp	count,0 	; any characters available?
	jnz	prtch1		; nz = yes, get one
	xor	dx,dx		; return count of zero
	stc
	ret			; No data
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 buffer (wrapped)?
	jae	prtch2		; ae = no
	add	si,BUFSIZ	; else do arithmetic module bufsiz
prtch2:
	mov	al,byte ptr [si] ; get a character into si
	dec	count		; one less unread char now
	sti			; interrupts back on now.
	pop	si
	mov	dx,count	; return # of chars in bufer
	clc
	ret
PRTCHR	endp

; Examine incoming communications stream for a packet SOP character.
; Return CX= count of bytes starting at the SOP character (includes SOP)
; and carry clear. Return CX = 0 and carry set if SOP is not present.
; Destroys AL.
peekcom proc far
	mov	cx,count		; qty in circular buffer
	cmp	cx,6			; basic NAK
	jb	peekc4			; b = two few chars, get more
	push	bx
	cli		; interrupts off, to keep srcpnt & count consistent
	mov	bx,srcpnt	    ; address of next available slot in buffer
	sub	bx,cx		    ; minus number of unread chars in buffer
	cmp	bx,offset source	; located before start of buf?
	jae	peekc1			; ae = no
	add	bx,bufsiz		; else do arithmetic modulo bufsiz
peekc1:	mov	al,[bx]
	cmp	al,trans.rsoh		; packet receive SOP?
	je	peekc3			; e = yes
	inc	bx
	cmp	bx,offset source+bufsiz ; beyond end of buffer?
	jb	peekc2			; b = no
	mov	bx,offset source	; wrap around
peekc2:	loop	peekc1			; keep looking
	sti
	pop	bx
	stc				; set carry for no SOP
	ret
peekc3:	sti				; interrupts back on now
	pop	bx
	inc	cx			; include SOP in count
	clc				; say SOP found
	ret				; CX has count remaining
	
peekc4:	xor	cx,cx			; return count of zero
	stc				; say no data
	ret
peekcom 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

; IHOSTS - Initialize the host by sending XON, or equivalent, and enter the
; cycle of clear input buffer, wait 1 second, test if buffer empty then exit
; else repeat cycle. Requires that the port be initialized before hand.
; Ihosts is used by the local send-file routine just after initializing
; the serial port.
; 22 March 1986 [jrd]
; 30 July 1986 Avoid sending nulls if no flow control [bgp]

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		; check for null char
	jz	ihosts1 	; z=null, don't send it
	call	outchr		; send it (release Host's output queue)
ihosts1:call	clrbuf		; clear out interrupt buffer
	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]
; 30 July 1986 Avoid sending null if no flow control [bgp]

IHOSTR	proc	near
	push	ax		; save reg
	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		; check for null char
	jz	ihostr1 	; z=null, don't send it
	call	outchr		; send it (release Host's output queue)
ihostr1:pop	ax
	ret
IHOSTR	endp

; Global proc to hang up the phone by making DTR and RTS low.

DTRLOW	proc	near
	mov	ah,cmline	; allow text to be able to display help
	mov	bx,offset rdbuf ; dummy buffer
	mov	dx,offset hnghlp ; help message
	call	comnd		; get a confirm
	jnc	dtrlow1		; nc = success
	ret			; carry set = failure
dtrlow1:call	serhng		; drop DTR and RTS
	mov	ah,PRSTR	; give a nice message
	mov	dx,offset hngmsg
	int	dos
	clc
	ret
DTRLOW	endp

; SERHNG us used to hang up the phone.	This resets the port (by calling
; serrst), and then forces DTR and RTS low to terminate the connection.
; 12 April 1986 [bgp]
; 30 July 1986 Turn them back on again after 3 seconds so new connection can
;  be made [bgp]

SERHNG	proc	near
	call	serrst		; reset the port to be sure
	push	dx
	push	cx
	push	bx
	push	ax
	cmp	prthnd,0	; nice method open? [bgp]
	jge	serhng0 	; yes
	push	es		; better save this
	mov	bx,SEG_7201	; point at controller
	mov	es,bx
	mov	bx,modem.mdstat
	cli			; no interrupts please
	mov	byte ptr es:[bx],5 ; register 5
	mov	byte ptr es:[bx],REG5_7201 and DTR_RTS_OFF
	sti			; interrupts ok again
	mov	ax,3000		; sleep for 3 seconds
	call	pcwait
	mov	bx,SEG_7201
	mov	es,bx
	mov	bx,modem.mdstat
	cli			; no interrupts please
	mov	byte ptr es:[bx],5
	mov	byte ptr es:[bx],REG5_7201
	sti			; interrupts ok again
	pop	es
	jmp	short serhng1 	; [bgp]
serhng0:
	mov	bx,offset newpval
	mov	[bx].CR5,REG5_7201 and DTR_RTS_OFF
	mov	dx,bx		; where the stuff is
	mov	cx,plength	; how long it is
	mov	bx,prthnd	; get handle
	mov	ah,IOCTL
	mov	al,IOwrite	; set new values
	int	DOS
	mov	ax,3000		; sleep for 3 seconds
	call	pcwait
	mov	bx,offset newpval ; turn them back on again...
	mov	[bx].CR5,REG5_7201
	mov	dx,bx
	mov	cx,plength
	mov	bx,prthnd
	mov	ah,IOCTL
	mov	al,IOwrite
	int	DOS
serhng1:
	pop	ax
	pop	bx
	pop	cx
	pop	dx
	ret
SERHNG	endp

; SHOW MODEM, displays current status of lines DSR, CD, and CTS.
; Uses global byte mdmhand, the modem line status register. [jrd]

SHOMODEM proc	near
	mov	ah,cmeol
	call	comnd
	jc	shomodem4		; c = failure
	mov	dx,offset shmdm0	; assume DSR is not set
	test	mdmhand,20h 		; is bit set?
	jnz	shomodem1		; z = no
	mov	dx,offset shmdm1	; DSR is asserted (line active)
shomodem1:
	mov	ah,PRSTR
	int	DOS
	test	mdmhand,80h		; test for CD
	jz	shomodem2		; z = off
	mov	dx,offset shmdm3	; actually have CD
shomodem2:
	int	DOS
	mov	dx,offset shmdm4	; assume no CTS
	test	mdmhand,10H		; test for CTS
	jz	shomodem3
	mov	dx,offset shmdm5	; actually have CTS
shomodem3:
	int	DOS
	clc
shomodem4:ret
SHOMODEM endp


; Get modem status and set global byte mdmhand. Preserve all registers.

; Check the status of the return control lines from the serial port.
; Since this function doesn't care if the port has been properly
; initialized or anything else we will always go directly to the
; hardware.  This is actually quite reasonable since the values
; returned in IOCTL from earlier will have no relation to the current
; state of reality. [bgp]
; Note that the method of getting DSR is a little weird.  There are
; no connections on the 7201 for DSR so we have to get it from one
; of the 6522s where there are a few free lines (specifically, the
; one that controls the keyboard interface and the CRT brightness/
; contrast.  This one is at E8040-E804F with line PA3 for DSRA and
; PA5 for DSRB.  Note that a zero is registered for an active line
; on the DSR sense.

getmodem proc	near			; gets modem status upon request
	mov	mdmhand,0		; assume nothing is on
	push	bx
	push	es			; better save these
	mov	bx,0E804H		; segment of 6522
	mov	es,bx
	mov	bl,8			; assume looking at port A
	cmp	flags.comflg,1
	je	getmod1			; e = it is A
	mov	bl,20H			; actually looking at B
getmod1:test	es:[1],bl 		; is DSR bit set?
	jnz	getmod2			; nz = no
	or	mdmhand,20h		; DSR is asserted (line active)
getmod2:mov	bx,SEG_7201		; point at 7201
	mov	es,bx
	mov	bx,modem.mdstat
	test	byte ptr es:[bx],8	; test for CD
	jz	getmod3			; z = off
	or	mdmhand,80h		; actually have CD
getmod3:test	byte ptr es:[bx],20H	; test for CTS
	jz	getmodx			; z = off
	or	mdmhand,10h		; actually have CTS
getmodx:pop	es
	pop	bx
getmodem endp

; Send a break out the current serial port.	Returns normally.
; Changed to use IOCTL function 12 April 1986 [bgp]
; 30 July 1986 Direct to hardware if not opened right [bgp]

SENDBR	proc	near
	push	es
	push	dx
	push	cx
	push	bx
	push	ax
	mov	ax,250		; break length in msec
	push	ax		; for later use
	jmp	sendbrz

sendbl:
	push	es
	push	dx
	push	cx
	push	bx
	push	ax
	mov	ax,3500 	; break length in msec
	push	ax		; for later use
sendbrz:
	cmp	prthnd,0	; open ok? [bgp]
	jge	sendbr0 	; yes, do it nice
	mov	bx,SEG_7201	; point at 7201
	mov	es,bx
	mov	bx,modem.mdstat
	cli			; no interrupts please
	mov	byte ptr es:[bx],1 ; register 1
	mov	byte ptr es:[bx],REG1_7201
	mov	byte ptr es:[bx],5
	mov	byte ptr es:[bx],REG5_7201 or BREAK_ON
	sti			; interrupts ok again
	pop	ax
	call	wait_msec
	cli			; no interrupts
	mov	byte ptr es:[bx],5
	mov	byte ptr es:[bx],REG5_7201
	mov	byte ptr es:[bx],1
	mov	byte ptr es:[bx],REG1_7201 or ENABLE_INT
	sti			; interrupts ok again
	jmp	sendbr1 	; [bgp]
sendbr0:
	mov	bx,offset newpval
	mov	[bx].CR1,REG1_7201 ; to disable interrupts
	mov	[bx].CR5,REG5_7201 or BREAK_ON
	mov	dx,bx		; where the stuff is
	mov	cx,plength	; how much there is
	mov	bx,prthnd	; get handle
	mov	ah,IOCTL
	mov	al,IOwrite	; set new values
	cli			; avoid interrupts here
	int	DOS
	mov	bx,SEG_7201	; have to explicitly do register 1
	mov	es,bx		; IOCTL doesn't seem to touch it
	mov	bx,modem.mdstat
	mov	byte ptr es:[bx],1 ; Register 1
	mov	byte ptr es:[bx],REG1_7201
	sti			; interrupts back on
	pop	ax
	call	wait_msec	; kill time
	mov	bx,offset newpval
	mov	[bx].CR5,REG5_7201
	mov	bx,prthnd
	mov	ah,IOCTL
	mov	al,IOwrite
	cli			; no interrupts please
	int	DOS
	mov	bx,SEG_7201	; Point at 7201 serial controller
	mov	es,bx
	mov	bx,modem.mdstat
	mov	byte ptr es:[bx],1 ; Register 1 must be done explicitly
	mov	byte ptr es:[bx],REG1_7201 or ENABLE_INT
	sti			; interrupts are okay again
sendbr1:
	pop	ax
	pop	bx
	pop	cx
	pop	dx
	pop	es
	clc			; need to stay connected
	ret			; And return.
SENDBR	endp

; Wait for the # of milliseconds in ax.  The delay is set for a 5 MHz
; clock rate.	    Actual delay for ax=1 is 1.007 msec, plus 1.005 msec
; for each increment in ax.

WAIT_MSEC proc	near
pcwait: 			; entry point for the outside world...
	push	cx		; 10 cycles
	mov	cx,ax		; 2 cycles
wait_msec1:
	push	cx		; 10 cycles
	mov	cx,294		; 4 cycles
wait_msec2:
	loop	wait_msec2	; 5+17*(CX-1) cycles
	pop	cx		; 8 cycles
	loop	wait_msec1	; 17 cycles if jump, 5 cycles if no jump
	pop	cx		; 8 cycles
	ret
WAIT_MSEC endp

; Position the cursor according to contents of DX:
; DH contains row, DL contains column.	Returns normally.

POSCUR	proc	near
	push	ax
	push	dx		; save this
	mov	dx,offset mov_pfx ; move prefix string
	mov	ah,PRSTR
	int	DOS
	pop	dx
	push	dx
	mov	dl,dh
	add	dl,' '          ; this is the row
	mov	ah,DCONIO	; no checking please
	int	DOS
	pop	dx
	push	dx
	add	dl,' '          ; this is the column
	int	DOS
	pop	dx
	pop	ax
	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 character.
	mov	ah,PRSTR
	int	DOS
	ret
DODEL	endp

; Move the cursor to the left margin, then clear to end of line.
; Returns normally.

CTLU	proc	near
	mov	dx,offset clrlin
	mov	ah,PRSTR
	int	DOS
	ret
CTLU	endp

; set the current port.

COMS	proc	near
	mov	dx,offset comptab
	mov	bx,0
	mov	ah,CMKEY
	call	comnd
	jnc	coms1		; nc = success
	ret
coms1:	push	bx
	mov	ah,cmeol
	call	comnd		; Get a confirm
	pop	bx
	jnc	coms2
	ret
coms2:	call	serrst		; reset current port
	mov	flags.comflg,bl ; Set the comm port flag.
	cmp	flags.comflg,1	; Using Com 1?
	jne	coms0		; Nope.
	mov	ax,offset port1
	mov	portval,ax
	mov	modem.mddat,DATAA_7201 ; Set COM1 defaults.
	mov	modem.mdstat,STATA_7201
	mov	modem.mdcom,SETA_8253
	call	opnprt		; open the handle
	ret
coms0:
	mov	ax,offset port2
	mov	portval,ax
	mov	modem.mddat,DATAB_7201 ; Set COM2 defaults.
	mov	modem.mdstat,STATB_7201
	mov	modem.mdcom,SETB_8253
	call	opnprt
	ret
COMS	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.	Modified to IOCTL function 12 April 1986 [bgp]

SERINI	proc	near
	push	es
	push	dx
	push	cx
	push	bx
	push	ax
	cmp	portin,FALSE	; Did we initialize port already?
	je	serin0
	jmp	serin2		; Yes, just leave
serin0:
	cli			; Disable interrupts
	xor	ax,ax		; Address low memory
	mov	es,ax
	mov	bx,modem.mdintv
	mov	ax,es:[bx]
	mov	savsci,ax
	mov	ax,offset serint ; Point at our routine
	mov	es:[bx],ax
	add	bx,2		; Now for CS value
	mov	ax,es:[bx]
	mov	savscs,ax
	mov	es:[bx],cs
	mov	portin,1	; Note that we are initialized
	mov	ax,offset source
	mov	srcpnt,ax
	mov	count,0
	mov	ax,SEG_8259	; Point at 8259 interrupt controller
	mov	es,ax
	mov	bx,CW2_8259	; Control word 2
	mov	al,es:[bx]
	and	al,modem.mden	; Enable INT1 (all from 7201)
	mov	es:[bx],al	; Save it
	mov	bx,CW1_8259	; Control word 1
	mov	al,modem.mdmeoi ; Clear any outstanding requests
	mov	es:[bx],al

;  Note that access to the serial controller here is only to register 1 which
;  must be done explicitly anyway, so there is no reason to care about
;  whether a port is open [bgp]

	mov	bx,SEG_7201	; Point at 7201 serial controller
	mov	es,bx
	mov	bx,modem.mdstat
	mov	byte ptr es:[bx],1 ; Register 1 must be done explicitly
	mov	byte ptr es:[bx],REG1_7201 or ENABLE_INT
	mov	byte ptr es:[bx],10H ; Clear external/status interrupts
	mov	byte ptr es:[bx],30H ; Clear special receive cond. int.
	mov	byte ptr es:[bx],38H ; Set to end of interrupt
	sti			; Allow interrupts
	mov	bx,portval	; get port [jrd]
	mov	parmsk,0FFH	; parity mask, assume parity is None. [jrd]
	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
serin2:	pop	ax
	pop	bx
	pop	cx
	pop	dx
	pop	es
	clc			; carry clear for success
	ret
SERINI	endp

; Reset the serial port.  This is the opposite of serini.   Calling
; this twice without intervening calls to serini should be harmless.
; Returns normally.	Modified to use IOCTL function 12 April 1986 [bgp]
; 30 July 1986 Make it go direct to hardware if handle open failed [bgp]
;   Also, don't reset the port until the transmit buffer is empty

SERRST	proc	near
	push	es		; preserve this
	push	dx
	push	cx
	push	bx
	push	ax
	cmp	portin,FALSE	; Reset already?
	je	srst0		; Yes, just leave.
	mov	bx,SEG_7201	; point at 7201 [bgp]
	mov	es,bx
	mov	bx,modem.mdstat
sersta:
	cli
	mov	byte ptr es:[bx],1 ; want status register 1
	mov	al,es:[bx]	; get status
	sti
	test	al,1		; all sent? (transmitter and shift reg. empty)
	jz	sersta		; no, wait...
	cli			; Disable interrupts
	mov	bx,SEG_8259	; Point at 8259 interrupt controller
	mov	es,bx
	mov	bx,CW2_8259
	mov	al,es:[bx]
	or	al,modem.mddis	; Turn off INT1
	mov	es:[bx],al
	xor	bx,bx		; Address low memory
	mov	es,bx
	mov	bx,modem.mdintv ; Restore the serial card int vector
	mov	ax,savsci
	mov	es:[bx],ax
	add	bx,2		; Restore CS too.
	mov	ax,savscs
	mov	es:[bx],ax

; As in SERINI, the only access to the serial controller is to register 1
; which must be done explicitly anyway, so we don't care about the handle [bgp]

	mov	bx,SEG_7201	; Point at 7201 serial controller
	mov	es,bx
	mov	bx,modem.mdstat
	mov	byte ptr es:[bx],1 ; Register 1 has to be done explicitly
	mov	byte ptr es:[bx],REG1_7201
	mov	portin,FALSE	; Reset flag.
	sti
srst0:
	pop	ax
	pop	bx
	pop	cx
	pop	dx
	pop	es		; All done.
	ret			; All done.
SERRST	endp

; serial port interrupt routine.  This is not accessible outside this
; module, handles serial port receiver interrupts.
; Revised on 22 May 1986, again 2 August 1986 to run at 38.4kb on PC's. [jrd]
; Srcpnt holds offset, within buffer Source, where next rcv'd char goes.
; Count is number of chars now in buffer, and oldest char is srcpnt-count
; done modulo size of Source.  All pointer management is handled here.
; Control-G char substituted for char(s) lost in overrun condition. [jrd]
; Adapted from msxibm.asm code. [jrd]
; Fixed test for receiver overrun. [bgp]

SERINT	proc	  near
	push	ax		; [jrd]
	push	ds		; do ds and ax first [jrd]
	push	es
	push	bx
	push	dx
	mov	ax,seg data
	mov	ds,ax		; address data segment
	mov	bx,SEG_7201	; point at 7201
	mov	es,bx
	mov	bx,modem.mdstat
	mov	al,es:[bx]	; get status (register 0)
	mov	byte ptr es:[bx],1 ; want status register 1 [bgp]
	mov	ah,es:[bx]	; get status [bgp]
	mov	bx,STATA_7201	; this one is always channel A
	mov	byte ptr es:[bx],38H ; Notify 7201 of end of interrupt
	mov	bx,SEG_8259	; point at 8259
	mov	es,bx
	mov	bx,CW1_8259
	mov	dl,modem.mdmeoi
	mov	es:[bx],dl	; Clear interrupt
	test	al,MDMINP	; anything there (status register 0)?
	jnz	srint0		; nz = yes
	jmp	retint		; and exit now (common jump point)
srint0:
	mov	bx,SEG_7201
	mov	es,bx
	and	ah,MDMOVER	; select overrun bit (status register 1)[jrd]
	mov	overrun,ah	; save it for later [jrd]
	jz	srint0a 	; no overrun
	mov	bx,modem.mdstat
	mov	byte ptr es:[bx],30H ; Clear overrun error status
srint0a:
	mov	bx,modem.mddat
	mov	al,es:[bx]	; get data byte
	cmp	flowoff,0	; flow control active?
	je	srint4		; e = no
	mov	ah,al		; ah = working copy. Check flow cntl.
	and	ah,parmsk	; strip parity temporarily, if any. [jrd]
	cmp	ah,flowoff	; acting on Xoff?
	jne	srint3		; ne = Nope, go on
	cmp	xofsnt,0	; have we sent an outstanding XOFF? [jrd]
	jne	srint1		; ne = yes, ignore (possible echo)
	mov	xofrcv,bufon	; Set the flag saying XOFF received
srint1:	jmp	retint		; and exit
srint3:
	cmp	ah,flowon	; acting on Xon?
	jne	srint4		; ne = Nope, go on
	mov	xofrcv,off	; Clear the XOFF received flag.
	jmp	retint		; and exit
srint4:
	mov	ah,overrun	; get overrun flag [jrd]
	or	ah,ah		; overrun?
	jz	srint5		; z = no
	mov	ah,al		; yes, save present char
	mov	al,BELL 	; insert control-G for missing character
srint5:
	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	srint6		; b = not past end
	mov	srcpnt,offset source ; wrap buffer arount
srint6:
	cmp	count,BUFSIZ	; filled already?
	jae	srint7		; ae = yes
	inc	count		; no, add a char
srint7:
	or	ah,ah		; anything in overrun storage?
	jz	srint8		; z = no
	mov	al,ah		; recover any recent char from overrun
	xor	ah,ah		; clear overrun storage
	jmp	srint5		; yes, go store real second char
srint8:
	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 [jrd]
	xor	cx,cx		; loop counter [jrd]
	mov	bx,SEG_7201	; point at 7201
	mov	es,bx
	mov	bx,modem.mdstat
srint9:
	mov	al,es:[bx]
	test	al,4		; ready?
	jnz	srint10 	; yes
	loop	srint9
	jmp	srint11 	; Timeout
srint10:
	mov	al,ah		; now send it out
	mov	bx,modem.mddat
	mov	es:[bx],al
	mov	xofsnt,bufon	   ; Remember we sent an XOFF at buffer level
srint11:
	pop	cx		; [jrd]
retint:
	sti			; be sure that this made it on
	pop	dx
	pop	bx
	pop	es
	pop	ds
	pop	ax		; [jrd]
	iret
SERINT	endp

; Produce a beep.   Returns normally.

BEEP	proc	near
	mov	dl,BELL
	mov	ah,DCONIO	; No checks, just do it
	int	DOS
	ret
BEEP	endp
CODE	ends
	end
