	NAME	msxibm
; File MSXIBM.ASM
; Kermit system dependent module for IBM-PC
	include mssdef.h
;	Copyright (C) 1982, 1995, Trustees of Columbia University in the 
;	City of New York.  The MS-DOS Kermit software may not be, in whole 
;	or in part, licensed or sold for profit as a software product itself,
;	nor may it be included in or distributed with commercial products
;	or otherwise distributed by commercial concerns to their clients 
;	or customers without written permission of the Office of Kermit 
;	Development and Distribution, Columbia University.  This copyright 
;	notice must not be removed, altered, or obscured.
;
; Edit history
; 12 Jan 1995 version 3.14
; Last edit
; 12 Jan 1995
; 27 August 1992 version 3.13
; 6 Sept 1991 version 3.11
; 2 April 94 Add Meridian SuperLAT
; 17 Feb 94 Add Japanese DOS contributions of Hirofumi Fujii keibun@kek.jp
; Add Beame and Whiteside TCP/IP support, adapted from James Sturdevant.
; Add LAT support for Interconnections Inc TES v3 over LAT transport.
;  Nettype is lat, letter ident is 'I', latkind is one.
; August 1992 Split each comms path into separate structures
; Feb 1992 Add multiple internal TCP/IP Telnet sessions
; 2 Feb 1991 Add support for user specified IRQs 2..15.
; 29 August Add direct Interconnections Inc TES support.
; 12 August Add Novell TELAPI network interface.
; 11 March 1990 Add 75/1200 baud split speed from Dan Norstedt.
; 30 Sept 1989 Incorporate Opennet networking material from Fred Richter.
; 30 Sept 1989 Fred Richter (FGR) Intel Corp, Hauppauge NY
;  polyof!inthap!fred or fred@polyof.poly.edu
;  Added support for Intel Opennet Networking Virtual Terminal Services
;  added "set port opennet xxx", also modified (slightly)
;  the Netbios code (rpost) to always have the next Netbios receive posted
;  as a small efficiency gain. Added error checking for received Netbios
;  packets (there was none for async receives), also fix problem when the
;  receives are canceled, where the data was being accepted anyway.
; 3 Sept 1989 Add DECnet CTERM and LAT support, plus Novell Int 6Bh.
; 9 July 1989 Add automatic usage of National Semiconductor 16550A UART in
;  receiver fifo mode. Thanks to Dan Norstedt, danne@kicki.stacken.kth.se,
;  and Campbell Scientific, Logan Utah for urging and supporting information.
; 17 June 1989 Add transparent interrupt driven support for IBM's Extended
;  Bios via SET PORT BIOSn.
; 8 June 1989 Add Wyse-700 support from (Mikko Laanti (mjl).
; 6 March 1989 Add SET TERMINAL REPLAY FILESPEC command.
; 21 Jan 1989 Avoid initializing a port until the port is accessed for regular
;  use, baud rate setting, or modem status. portin=-1 means unaccessed.
; 2 Dec 1988 Preserve UART parity etc bits in Line Control Reg.
; 30 Nov 1988 Add SET TERM CLEAR screen clear command (uses byte VTCLEAR).
; 26 Nov 1988 Add SET TERM TAB SET/CLEAR AT start-col:spacing notation.
; 21 Nov 1988 Version 2.32
; 25 Oct 1988 Add Ungermann Bass network board status check in proc chkub,
;  thanks to Fritz Buetikofer and Rene Rehmann in Switzerland.
; 9 Oct 1988 Add byte vtemu.vtchset to hold choices for VT102 emulator
;  character sets (US, UK, or Alternate-ROM). Tnx to Baruch Cochavy. The value
;  of vtemu.vtchset must match definitions in emulator file mszibm.asm.
; 5 Oct 1988 Add SET TERMINAL DIRECTION command and status display line.
; 21 August 1988 Move tests for NUL and DEL received chars to prtchr routine
;  for uniform application of tests.
; 28 July 1988 Add more UB details from Henrik Levkowetz. Double check serial
;  port address for 02f8h with COM1, for PCjr, and shift to COM2 addresses.
; 16 July 1988 Use null interrupt routine, nulint, when resetting serial port
;  because setting OUT2 low generates stray ints on some UARTs. [jrd]
; 1 July 1988 Version 2.31
; 12 June 1988 Add small changes to handle network session failures, and
;  add carry set if serini fails to init port.
; 29 May 1988 Add Ungermann Bass NetOne NETCI support from Henrik Levkowetz
;  [ohl]. Revise same to test for actual network presence and to avoid
;  interference with NetBios operations. Use "SET PORT UB-Net1"
; 23 May 1988 Hangup now resets serial port so can be re-inited with DTR high
; 21 May 1988 Tweak serrst to allow stray interrupts while resetting UART,
;  clear outstanding network requests when changing ports.
; 6 May 1988 Introduce xmtbufx for explict network double buffering of
;   xmitted data.
; 28 April 1988 fix set port bios register problem.
; 18 April 1988 Network: do another READ when char count < low water mark.
;  If BIOSn is selected skip test of Bios 40:0h for port presence.
; 3 April 1988 Ignore NUL and DEL at serial port unless DEBUG or TRANSLATE
;  INPUT are active, but pass DEL if Tek mode is active. Replace netdone
;  with new lclexit global word to shut net when Kermit exits.
; 22 March 1988 Add global byte Tekgraf which forces graphics board type
;   0=auto-sensing, 1=cga, 2=ega, 3=VGA, 5=Hercules, 5=ATT. Tekgraf stored
;   and set in this file by Set Term Graphics <board type>. [jrd]
; 6 March 1988 Ignore received XOFF's if we have already sent one. [jrd]
; 27 Feb 1988 Add global routine getmodem to return modem status in al. [jrd]
; 9 Feb 1988 Automaticallly find Interrupt ReQuest level for a port.
;   No Modem Status for network. [jrd]
; 25 Jan 1988 Revise outchr waiting on XON to use 4 millisec increments. [jrd]
; 1 Jan 1988 version 2.30
; 24 Dec 1987 Revise selection of COM1 to use COM1 name but COM2 addresses
;  if base address of 02f8 (COM2) is found in 40:00h and display notice.
;  Restore state of IRQ interrupt line when finished with serial port. [jrd]
; 31 Oct 1987 Add terminal type Tek4010, with Tek submode Tekflg. [jrd]
; 24 Oct 1987 Enhance clrbuf to empty any intermediate (net) buffers. [jrd]
; 19 Oct 1987 Fix stray tab-set at column 32. [jrd]
; 2 Oct 1987 Add PCjr baud rate table, from Ted Medin. [jrd]
; 6 Sept 1987 Allow serial port serint to send xoff when buffer fills even
;  though user may have sent xoff by hand. [jrd]
; 27 Aug 1987 Skip timeout test in OUTCHR if receive timeout is zero. [jrd]
; 23 August 1987 Add vtemu.vtflgop to hold runtime terminal settings so that
;  a reset command restores them to the Setup values, vtemu.vtflgst. Show
;  displays the vtemu.vtflgop operational values. [jrd]
; 17 August 1987 Make timing adjustments for Token Passing and single buffered
;  network adapter boards. Byte netdbfr indicates presence of double buffering;
;  it is set in chknet as a vendor option. To test your boards force dbl buf
;  then look for missing 256 byte parts of long packets sent out; missing parts
;  mean new material overwrote not-yet-sent old == single buffering. [jrd]
; 8 August 1987 Add interrupt chaining in serint. [jrd]
; 23 July 1987 Clear xofsnt and xofrcv xon/xoff flags in ihost(s/r). [jrd]
; 9 July 1987 Cure confusion about COM1/2 for IBM PCjr (address of regular
;  COM2 in 40:0h slot for COM1) with info from John Neufeld. [jrd]
; 2 July 1987 Route NetBios cancels through separate scb for systems, such
;  as Novell NetWare, which object to having active scbs touched. [jrd]
; 25 June 1987 Add trapping of Int 14H (Bios RS232 procedure) to allow
;  CTTY command to function without too much inteference from DOS. [jrd]
; 17 June 1987 Enlarge tab setting to full 132 columns at all times. [jrd]
; 11 June 1987 Add Set Term Roll on/off to control auto roll back of screen
;  when new characters are displayed; default is off (no unwinding). [jrd]
; 20 May 1987 Remove rejection of NULL and DEL chars, let callers do it. [jrd]
; 16 May 1987 Add distinction between user typed and receiver threshold
;  controlled sending of XOFF. User level overrides buffer control.[jrd]
;  Add COM3 and COM4 support: examine memory 40:00h->40:07h for selected
;  port COM1..4, resp. If word is null then set flags.comflg to 0 to say
;  undefined port. Otherwise, use that word in seg 40h as base of UART i/o
;  ports. Assume IRQ4 for COM1 and COM3 (same except for port addresses)
;  and IRQ4 for  COM2 and COM4 (again, same except for port addresses).
;  Serial port info sturcture (not values) assumed identical for all ports.
; 25 April 1987 Add Netbios compatible local area network support. [jrd]
;  Set Port command expanded to syntax SET PORT NET nodename. Use nodename
;  if acting as a client to named remote node, leave blank if running in
;  Server mode. Byte 'ttyact' is controlled by msster.asm to indicate Connect
;  mode is being used. Byte 'netdone' (stored in mssker.asm) holds offset of
;  network hangup procedure 'netclose' to be done when leaving Kermit. Hangup
;  command extended to to network hangup as well. Network uses IBM Netbios
;  standard calls (Int 5Ch) and allows for extensions of AT&T STARLAN for
;  longer node names via Int 2Bh (the later is tested before use). Virtual
;  circuits are employed. The Redirector is not necessary. Kermit can operate
;  as either a terminal (does a CALL at Connect mode startup), a file receiver
;  (does a CALL at startup), or a Kermit server (does an anonomous LISTEN at
;  startup, hence no nodename). Clients should Set Timer Off.
; Note -
; When the Bios is used for serial port i/o the modem signals DSR and CTS
; must be asserted low before the Bios will access the hardware. Jumpers
; from pin 20 (DTR) to pin 6 (DSR) and from pin 4 (RTS) to pin 5 (CTS)
; will probably be necessary.
;       From Glenn Everhart (who suggested using the Bios alternative)
;
	public	serini, serrst, clrbuf, outchr, coms, cardet
	public	dodel, ctlu, cmblnk, locate, prtchr, baudst, clearl
	public	getbaud, beep, shomodem, getmodem, mdmhand, shownet
	public	count, xofsnt, puthlp, putmod, clrmod, poscur, holdscr
	public	sendbr, sendbl, machnam, setktab, setkhlp, lclini, showkey
	public	ihosts, ihostr, dtrlow, serhng, comptab, pcwait, portval
	public	bdtab, dupflg, peekcom, tstport, fossil_port, fossilflag
	public	savexoff, savexlen, parmsk, flowon, flowoff, flowcnt
	public	isps55		; [HF] 940130 Japanese IBM PS/55 mode
	public	ps55mod		; [HF]940206 PS/55 mode line status (0:system)
ifndef	no_network
	public	setnbios, ubhold, ubclose
endif	; no_network
ifndef	no_tcp
	public	tcpstart, tcp_status, sesdisp, seslist, sescur, winupdate
endif	; no_tcp

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
nbuflen	equ	512		; bytes in each network buffer (two of them)
				;  DEC-LAT requires 259 (256 + 3 extra)

BRKBIT	EQU	040H		; Send-break bit. 
TIMERCMD EQU	43h		; 8253/4 Timer chip command port
TIMER2DATA EQU	42h		; 8253/4 Timer 2 data port
PPI_PORT EQU	61h		; 8255 prog peripheral chip control port
VIDEO	EQU	10H		; Bios Video display software interrupt
RS232	EQU	14H		; Bios RS232 serial port s/ware interrupt

; constants used by serial port handler
MDMINP	EQU	1		; Input ready bit
MDMOVER	EQU	2		; Receiver overrun
				; 1200/75 baud split speed constants
cnt75b	equ	47721/3		; One bit of 75 baud at 1.193 Mhz clock
precomp	equ	cnt75b/8	; Precomp 12%, allows 3ms latency w 12% jitter

ifndef	no_tcp
_TEXT	segment
	extrn	ktcpopen:far, ktcpclose:far, ktcpswap:far, ktcpcom:far
_TEXT	ends
endif	; no_tcp

data 	segment
	extrn	flags:byte, trans:byte, ttyact:byte, comand:byte
	extrn	lclsusp:word, lclrest:word, lclexit:word, rxtable:byte
	extrn	rdbuf:byte, taklev:byte, scbattr:byte
	extrn	low_rgt:word, diskio:byte, crt_cols:byte
	extrn	dosnum:word, portirq:byte, dosctty:byte, decbuf:byte
	extrn	tv_mode:byte, repflg:byte
	extrn	takadr:word, taklev:byte, vtinited:byte, kbdflg:byte
	extrn	yflags:byte,  apctrap:byte, protlist:byte
	extrn	vtcpage:word, kstatus:word
ifndef	no_network
	extrn	tcpnewline:byte
ifndef	no_tcp
	extrn	tcpaddress:byte, tcpsubnet:byte, tcpdomain:byte
	extrn	tcpgateway:byte, tcpprimens:byte, tcpsecondns:byte
	extrn	tcphost:byte, tcpbcast:byte, tcpbtpserver:byte
	extrn	tcpport:word, tcppdint:word, tcpttbuf:byte
	extrn	tcpdebug:byte, tcpmode:byte, tcpmss:word
endif	; no_tcp
endif	; no_network
ifndef	no_terminal
	extrn	tekflg:byte, ftogmod:dword
endif	; no_terminal

; Modem information
mdminfo	struc
mddat	dw	03f8h		; data register, base address (03f8h)
mdiir	dw	03fah		; interrupt identification register (03fah)
mdstat	dw	03fdh		; line status register (03fdh)
mdcom	dw	03fbh		; line control register (03fbh)
mden	db	not (1 shl 4)	; mask to enable interrupt
mddis	db	(1 shl 4)	; mask to disable interrupt
mdmeoi	db	60h+4		; specific EOI
mdintv	dw	8+4		; saved interrupt vector (0ch is IRQ 4)
mdmintc	dw	20h		; interrupt controller control (20h or 0a0h)
mdfifo	db	0		; non-zero if UART is in FIFO mode
mdminfo	ends
modem	mdminfo <>

setktab	db	0		; superceded by msuibm code, return 0 here
setkhlp	db	'$'		; and add empty help string
holdscr	db	0		; Hold-Screen, non-zero to stop reading
savsci	dd	0		; old serial port interrupt vector
sav232	dd	0		; Original Bios Int 14H address, in Code seg
savirq	db	0		; Original Interrupt mask for IRQ
savier	db	0		; original UART Int enable bits (03f9)
savstat db	0		; orginal UART control reg  (03fch)
savlcr	db	0		; Original Line Control Reg (3fbh) contents
dupflg	db	0		; full (0) or half (1) duplex on port
quechar	db	0		; queued char for outchr (XOFF typically)
intkind	db	0		; cause of serial port interrupt
isps2	db	0		; non-zero if real IBM PS/2
isps55	db	0		; [HF] 940130 non-zero if Japanese PS/55 mode
ps55mod	db	0, 0		; [HF] 940202 PS/55 mode line at startup/curr.
erms40	db	cr,lf,'?Warning: Unrecognized Speed',cr,lf,'$'
badbd	db	cr,lf,'Unimplemented speed$'
badprt	db	cr,lf,'?Warning: unknown address for port. Assuming \x0$'
biosmsg	db	cr,lf,'?Warning: unknown hardware for port.'
	db	' Using the Bios as BIOS$'
badirq	db	cr,lf,'?Warning: unable to verify IRQ. Assuming $'
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$'
msmsg7	db	cr,lf,'  Modem is not used by the Network$'
msmsg8	db	cr,lf,'  COM1 address:       Port \x$'
msmsg9	db	', IRQ $'
msmsg10	db	', 16550A UART FIFO$'
msmsg11	db	cr,lf,'  Fossil port: $'
msmsg12	db	', disable-on-close: off$'
msmsg13	db	', disable-on-close: on$'
msmsg14 db	', 1 stop bit$'
msmsg20 db	'  (Set Carrier is on)$'
msmsg21 db	'  (Set Carrier is off)$'
hngmsg	db	cr,lf,' The phone or network connection should have hung up'
	db	cr,lf,'$'

machnam	db	'IBM-PC$'
crlf	db	cr,lf,'$'
delstr  db	BS,BS,'  ',BS,BS,'$' 	; Delete string
clrlin  db	BS,BS,'  ',cr,'$'	; Clear line (just the cr part)
portin	db	-1		; Has comm port been initialized, -1=not used
nettype	dw	0		; kind of local area net (vendor bit field)
xofsnt	db	0		; Say if we sent an XOFF
xofrcv	db	0		; Say if we received an XOFF
pcwcnt	dw	800		; number of loops for 1 millisec in pcwait
temp	dw	0
temp2	dw	0
tempsci dw	0		; temp storage for serint
tempdum	dw	0		; temp storage for serdum
timeract db	0		; timer in use by a routine, flag
clomsg	db	' A communications session may be active;'
	db	' exit anyway [Yes/No]? ',0
clotab	db	2		; close net on exit table
	mkeyw	'yes',0
	mkeyw	'no',1


ifdef	no_network
comptab	db	25 - 12
else
ifdef	no_tcp
comptab	db	25 - 1			; communications port options
else
comptab	db	25			; communications port options
endif	; no_tcp
endif	; no_network

	mkeyw	'Bios1','0'+1		; '0' is to flag value as forced Bios
	mkeyw	'Bios2','0'+2
	mkeyw	'Bios3','0'+3
	mkeyw	'Bios4','0'+4
	mkeyw	'COM1',1		; these go straight to the hardware
	mkeyw	'COM2',2
	mkeyw	'COM3',3
	mkeyw	'COM4',4
	mkeyw	'1',1			; straight to the hardware
	mkeyw	'2',2
	mkeyw	'3',3
	mkeyw	'4',4
ifndef	no_network
	mkeyw	'3Com(BAPI)','C'	; 3Com BAPI interface
	mkeyw	'BWTCP','b'		; [JRS] Beame & Whiteside TCP
	mkeyw	'DECnet','D'		; DECnet-DOS LAT and CTERM
	mkeyw	'EBIOS','E'		; IBM/YALE EBIOS Int 14h interceptor
	mkeyw	'NetBios','N'		; Netbios
	mkeyw	'Novell(NASI)','W'	; Novell NetWare NASI/NACS
	mkeyw	'OpenNET','O'		; Intel OpenNET support (FGR)
	mkeyw	'SuperLAT','M'		; Meridian SuperLAT
ifndef	no_tcp
	mkeyw	'TCP/IP','t'		; Telnet, internal
endif	; no_tcp
	mkeyw	'TELAPI','T'		; Novell TELAPI
	mkeyw	'TES','I'		; TES, Interconnections Inc
	mkeyw	'UB-Net1','U'		; Ungermann Bass Net One
endif	; no_network
	mkeyw	'Fossil','F'		; Fossil
	mkeyw	'   ',0			; port is not present, for Status

; port structure:
; baud rate index, local echo, parity flag, if flow control active (both ways),
; if need handshake after pkts, default handshake char, flow control char pair
; half/full duplex, port ready, send, receive, close procedures
	; UART hardware
port1	prtinfo	<-1,0,defpar,3,0,defhand,1,floxon,0,0,uartsnd,uartrcv,serrst>
port2	prtinfo	<-1,0,defpar,3,0,defhand,1,floxon,0,0,uartsnd,uartrcv,serrst>
port3 	prtinfo	<-1,0,defpar,3,0,defhand,1,floxon,0,0,uartsnd,uartrcv,serrst>
port4	prtinfo	<-1,0,defpar,3,0,defhand,1,floxon,0,0,uartsnd,uartrcv,serrst>
	; IBM PC Bios and EBIOS
portb1	prtinfo	<-1,0,defpar,3,0,defhand,1,floxon,0,0,biossnd,biosrcv,serrst>
portb2	prtinfo	<-1,0,defpar,3,0,defhand,1,floxon,0,0,biossnd,biosrcv,serrst>
portb3 	prtinfo	<-1,0,defpar,3,0,defhand,1,floxon,0,0,biossnd,biosrcv,serrst>
portb4	prtinfo	<-1,0,defpar,3,0,defhand,1,floxon,0,0,biossnd,biosrcv,serrst>

portval	dw	port1			; Default is to use port 1

bdtab	db	21			; 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	'14400',14
	mkeyw	'19200',15
	mkeyw	'28800',16
	mkeyw	'38400',17
	mkeyw	'57600',18
	mkeyw	'115200',19
	mkeyw	'75/1200',20	; split speed, use this index for Bsplit

Bsplit	equ	20		; 75/1200 baud, split-speed  [pslms]

; this table is indexed by the baud rate definitions given in bdtab.
; Unsupported baud rates should contain 0FFh.

bddat	label	word
	dw	9E4H		; 45.5 baud
	dw	900H		; 50 baud
	dw	600H		; 75 baud
	dw	417H		; 110 baud
	dw	359H		; 134.5 baud
	dw	300H		; 150 baud
	dw	180H		; 300 baud
	dw	0C0H		; 600 baud
	dw	60H		; 1200 baud
	dw	40H		; 1800 baud
	dw	3AH		; 2000 baud
	dw	30H		; 2400 baud
	dw	18H		; 4800 baud
	dw	0CH		; 9600 baud
	dw	08h		; 14400 baud
	dw	06H		; 19200 baud
	dw	04h		; 28800 baud
	dw	03H		; 38400 baud
	dw	02h		; 57600 baud
	dw	01h		; 115200 baud
	dw	5fh		; Split 75/1200, 1200+1.1 percent error
baudlen	equ	($-bddat)/2	; number of entries above

; this table is indexed by the baud rate definitions given in
; pcdefs.  Unsupported baud rates should contain FF.
; Bits are for Bios speed, no parity, 8 data bits.
clbddat   label   word
        dw      0FFH            ; 45.5 baud  -- Not supported
        dw      0FFH            ; 50 baud
        dw      0FFH            ; 75 baud
        dw      03H		; 110 baud
        dw      0FFH            ; 134.5 baud
        dw      23H		; 150 baud
        dw      43H		; 300 baud
        dw      63H		; 600 baud
        dw      83H		; 1200 baud
        dw      0ffH		; 1800 baud
        dw      0FFH		; 2000 baud
        dw      0a3H		; 2400 baud
        dw      0c3H		; 4800 baud
        dw      0e3H		; 9600 baud
        dw      0FFH		; 14400 baud
        dw      0FFH		; 19200 baud
        dw      0FFH		; 28800 baud
        dw      0FFH		; 38400 baud
	dw	0FFH		; 57600 baud
	dw	0FFH		; 115200 baud
	dw	0FFh		; Split 75/1200

defcom	dw	03f8h,02f8h,03e8h,02e8h	 ; default COMn port addresses

;;;;;;;;;;;;;; start of per session save area
	even
savexoff label	word
source	db	bufsiz+2 DUP(?)	; Buffer for data from port (+ 2 guard bytes)
srcpnt	dw	source		; Pointer in buffer
count	dw	0		; Number of chars in int buffer
cardet	db	0		; UART Carrier Detect (and network analogue)
parmsk	db	0ffh		; parity mask, 0ffh for no parity, 07fh with
flowoff	db	0		; flow-off char, Xoff or null (if no flow)
flowon	db	0		; flow-on char, Xon or null
flowcnt	db	0		; holds flowc (!= 0 using any flow control)
xmtcnt	dw	0		; occupancy in current output buffer
xmtbufx	db	nbuflen+3 dup (0) ; external version of xmtbuf (dbl buffers)
ifndef	no_network
ifndef	no_tcp
; TCP/IP Telnet internal
port_tn	prtinfo	<-1,0,defpar,3,0,defhand,1,floxon,0,0,ubsend,ubrecv,tcpclose>
else
port_tn	prtinfo	<-1,0,defpar,3,0,defhand,1,floxon,0,0,ubsend,ubrecv,ubclose>
endif	; no_tcp
endif	; no_network
savexlen dw	($-savexoff)
;;;;;;;;;;;;;;; end of per session save
tcp_status dw	0		; status from msntnd.c
					; SUCCESS	0
					; NO_DRIVER	1
					; NO_LOCAL_IP	2
					; NO_SESSION	3
					; NO_HOST	4
					; BAD_SUBNET_MASK 5
					; SESSIONS_EXCEEDED 6
					; HOST_UNKNOWN	7
					; HOST_UNREACHABLE 8
					; CONNECTION_REJECTED 9

xmtbuf	db	nbuflen dup (0) ; network buffer for transmitter
rcvbuf	db	nbuflen+3 dup (0) ; network buffer for receiver

; variables for serial interrupt handler
mst	dw	03fdh		; Modem status address
mdat	dw	03f8h		; Modem data address
miir	dw	03fah		; modem interrupt ident register
mdeoi	db	20h		; End-of-Interrupt value
mdmhand	db	0		; Modem status register, current
mdintc	dw	20h		; interrupt control address

fossil_port  dw	0		; Fossil, port number 0..98
fossilflag   db	0		; Fossil, shut port when done if non-zero
fossil_portst equ 3		; Fossil, port status
fossil_init equ	4		; Fossil, init port in dx
fossil_done equ	5		; Fossil, release port in dx
fossil_dtr equ	6		; Fossil, DTR control
fossil_blkrd equ 18h		; Fossil, block read
fossil_blkwr equ 19h		; Fossil, block write
fossil_flow equ	0fh		; Fossil, set flow control method

ifndef	no_network
; Information structures for IBM Netbios compatible Local Area Networks
				; network constants
netint	equ	5ch		; Netbios interrupt
nadd	equ	30h		; Add name
ncall	equ	10h		; CALL, open a virtual circuit session
ncancel	equ	35h		; Cancel command in scb buffer
ndelete	equ	31h		; Delete Name
nhangup	equ	12h		; Hangup virtual circuit session
nlisten	equ	11h		; Listen for session caller
naustat	equ	33h		; Network Adapter Unit, get status of
nreceive equ	15h		; Receive on virtual circuit
nreset	equ	32h		; Reset NAU and tables
nsend	equ	14h		; Send on virtual circuit
nsestat	equ	34h		; Session, get status of
netbrk	equ	70h		; STARLAN Int 5bh send Break
nowait	equ	80h		; no-wait, command modifier
npending equ	0ffh		; Pending request
exnbios	equ	0400h		; Int 2ah exec netbios call, error retry

				; nettype word bits
netbios	equ	0001h		; NetBios
netone	equ	0002h		; Ungermann-Bass Net/One
decnet	equ	0004h		; DECnet CTERM
declat	equ	0008h		; DECnet LAT
bapi	equ	0010h		; 3Com BAPI
ebios	equ	0020h		; EBIOS, IBM and YALE
telapi	equ	0040h		; TELAPI, Novell
tes	equ	0080h		; TES, Interconnections Inc and Novell
tcpnet	equ	0100h		; TCP/IP (internal)
acsi	equ	0200h		; EBIOS, ACSI direct to NetBios pathway
bwtcp	equ	0400h		; [JRS] Beame & Whiteside TCP

;xncall	equ	74h		; [ohl] Net/One extended call function
netci	equ	6Bh		; [ohl] Net/One command interface interrupt,
				; [ohl]  used for the following functions:
nciwrit equ	0000h		; [ohl] Net/One write function
nciread equ	0100h		; [ohl] Net/One read function
ncistat equ	0700h		; [ohl] Net/One status function
ncicont equ	0600h		; [ohl] Net/One control function
ncibrk	equ	02h		; [ohl] Net/One code for a break
ncidis	equ	04h		; [ohl] Net/One code for disconnect
ncihld	equ	06h		; [ohl] code for placing a connection on hold
bapiint	equ	14h		; 3Com BAPI, interrupt (Bios replacment)
bapicon	equ	0a0h		; 3Com BAPI, connect to port
bapidisc equ	0a1h		; 3Com BAPI, disconnect
bapiwrit equ	0a4h		; 3Com BAPI, write block
bapiread equ	0a5h		; 3Com BAPI, read block
bapibrk	equ	0a6h		; 3Com BAPI, send short break
bapistat equ	0a7h		; 3Com BAPI, read status (# chars to be read)
bapihere equ	0afh		; 3Com BAPI, presence check
bapieecm equ	0b0h		; 3Com BAPI, enable/disable ECM char
bapiecm	equ	0b1h		; 3Com BAPI, trap Enter Command Mode char
bapiping equ	0b2h		; KERMIT BAPI extension, Telnet, Ping host
bapito_3270 equ 0b3h		; KERMIT BAPI extension, byte to 3270
bapinaws equ	0b4h		; KERMIT BAPI extension, send NAWS update
				;
telopen	equ	0e0h		; TELAPI xtelopen a connection
telclose equ	0e1h		; xtelclose a connection
telread	equ	0e2h		; xtelread char(s)
telwrite equ	0e3h		; xtelwrite chars
telioctl equ	0e4h		; xtelioctl, ioctl the port
telreset equ	0e5h		; xtelreset, reset the whole TELAPI package
telunload equ	0e6h		; xtelunload, unload TELAPI TSR
tellist	equ	0e7h		; xtellist, list current sessions and status
telattach equ	0e8h		; xtelattach, session to COM port # 0..3
telportosn equ	0e9h		; xtelportosn, return session id for port
telunreac equ	-51		; network is unreachable
telinuse equ	-56		; socket already in use
teltmo	equ	-60		; timeout on connection attempt
telrefuse equ	-61		; connection refused
teldwnhost equ	-64		; host is down
telunkhost equ	-67		; unknown host
telfull	equ	-301		; all sessions are in use
				; TELAPI messages and misc data
telmsg1	db	cr,lf,'?Badly constructed Internet address: $'
telmsg2	db	cr,lf,'?No connection. Status = -$'
telmsg51 db	cr,lf,'?Network is unreachable$'
telmsg56 db	cr,lf,'?Socket already in use$'
telmsg60 db	cr,lf,'?Timeout on connection attempt$'
telmsg61 db	cr,lf,'?Connection refused$'
telmsg64 db	cr,lf,'?Host is down$'
telmsg67 db	cr,lf,'?Unknown host$'
telmsg301 db	cr,lf,'?All sessions are in use$'
telhostid db	2 dup (0)	; TELAPI Telnet internal host session ident

IAC	equ	255		; B&W TCP/IP Telnet Options codes
DONT	equ	254
DO	equ	253
WONT	equ	252
WILL	equ	251
SB	equ	250
SE	equ	240
TELOPT_ECHO	equ 1
TELOPT_SGA	equ 3
TELOPT_STATUS	equ 5
TELOPT_TTYPE	equ 24
TELOPT_NAWS	equ 31
NTELOPTS	equ 24
sgaflg	db	0		; B&W TCP/IP, supress go ahead flag
option1 db	0		; B&W TCP/IP, Telnet Options byte1
option2	db	0		; B&W TCP/IP, Telnet Options byte1
optstate db	0		; B&W TCP/IP, Telnet Options state variable
bwtcpnam    db	'TCP-IP10',0	; [JRS] name of Beame & Whiteside TCP driver
bwhandle    dw	0		; [JRS] handle for Beame & Whiteside TCP driver
				;
testalk	equ	4		; TES invoke interactive cmd interpreter
tesbwrite equ	6		; TES block write
tesbread equ	7		; TES block read
tesinstal equ	0a0h		; TES installation/status report
teslist	equ	0a1h		; TES get list of sessions, with status	
tesgets	equ	0a2h		; TES get list of server names
tesnews	equ	0a3h		; TES start a new session
tesholds equ	0a4h		; TES hold currently active connection
tesresume equ	0a5h		; TES resume a session (1..9)
tesdrop	equ	0a6h		; TES drop a session
tesnexts equ	0a7h		; TES skip to next active session
tesexec	equ	0a8h		; TES send string to cmd interpreter
tesport	dw	0		; TES low byte = intercepted port
tesquiet db	'ACTION NONE',0 ; TES Stop command prompting
tesses	db	0		; TES session number (1..9 is legal)
tesname	db	50 dup (0)	; TES host name asciiz
teshelp	db	cr,lf,'?Host name or "*" to see available hosts'
	db	' or press ENTER to resume a connection$'
tesnlist db	cr,lf,'  Active TES hosts:$'
tesnhost db	cr,lf,'?No existing connection.'
	db	' Please select a host with  SET PORT TES host$'
latkind	db	0		; non-zero if using TES or Meridian LAT
DEC_LAT	equ	0		; for latkind
MTC_LAT	equ	2		; for latkind
TES_LAT	equ	1		; for latkind

;; pcnet values:	0	no network available at all
;;			1	network board reports itself as present
;;			2	and session is in progress
;; extrn byte pcnet is  defined in msster.

; NetBios (StarGROUP and Opennet)
port_nb	prtinfo	<-1,0,defpar,3,0,defhand,1,floxon,0,0,send,receive,nbclose>
; Ungermann Bass, 3ComBAPI, TELAPI
port_ub	prtinfo	<-1,0,defpar,3,0,defhand,1,floxon,0,0,ubsend,ubrecv,ubclose>
; DECnet LAT and CTERM and TES-LAT
port_dec prtinfo <-1,0,defpar,3,0,defhand,1,floxon,0,0,decsnd,decrcv,decclose>
; TES
port_tes prtinfo <-1,0,defpar,3,0,defhand,1,floxon,0,0,tessnd,tesrcv,tesclose>
endif	; no_network

scbst struc			; Session (Network) Control Block [PCnet comp]
	scb_cmd		db 0	; command code for Netbios Int 5ch
	scb_err		db 0	; error return or request is pending
	scb_vcid	db 0	; virtual circuit ident number
	scb_num		db 0	; local name-number
	scb_baddr	dw 0	; buffer address, offset
			dw data ;  and segment
	scb_length	dw 0	; length of buffer data
	scb_rname	db '*               ' ; remote name, 16 chars space
	scb_lname	db '                ' ; local name      filled 
			db 0	; Receive timeout (0.5 sec units), want 0
			db 0	; Send timeout (0.5 sec units), want 0
	scb_post	dw 0	; interrupt driven post address, offset
			dw code ;  and segment
			db 0	; LAN_num (adapter #), set to zero for STARLAN
	scb_done	db 0	; command complete status
				; the 14 bytes below are normally 'reserved'
				; STARLAN uses 5 for long/special call names
				;  together with STARLAN specific Int 5bh
	scb_vrname	dw 0,0	; Variable length call name ptr offset,segment
	scb_vrlen	db 0	; length of vrname
			db 9 dup (0)	; reserved
scbst	ends			; 64 bytes overall

ifndef	no_network
rcv	scbst	<,,,,rcvbuf,,length rcvbuf,,,,,rpost>; declare scb for rcvr
else
rcv	scbst	<,,,,rcvbuf,,length rcvbuf,,,,,> ; declare scb for rcvr
endif	; no_network

ifndef	no_network
xmt	scbst	<,,,,xmtbuf,,length xmtbuf,,,,,spost>;  for xmtr
lsn	scbst	<,,,,xmtbuf,,length xmtbuf,,,,,lpost>;  for server listen
can	scbst	<>				     ;  for cancels
					; DECnet material
decint	equ	69h			; CTERM interrupt
dpresent equ	100h			; CTERM Installation check
dsend	equ	101h			; CTERM send byte
dread	equ	102h			; CTERM read byte
dcstat	equ	103h			; CTERM status
ddstat	equ	104h			; CTERM Decnet status
dopen	equ	105h			; CTERM open session
dclose	equ	106h			; CTERM close session
dgetscb	equ	10ah			; CTERM get SCB size

latint	equ	6ah			; LAT interrupt
latsend equ	1			; LAT send byte
latread	equ	2			; LAT read byte
latstat	equ	3			; LAT status
latsendb equ	4			; LAT send block (v4)
latreadb equ	5			; LAT read block (v4)
latinfo	equ	6			; LAT get miscellaneous information
latsrv	equ	0d500h			; LAT get next service name
latopen	equ	0d0ffh			; LAT open
latclose equ	0d000h			; LAT close
latbreak equ	0d100h			; LAT send a BREAK
latscbget equ	0d800h			; LAT get SCB interior to LAT
latscbfree equ	0d801h			; LAT free SCB interior to LAT
latcpyems equ	0d900h			; LAT copy to/from SCB in EMS or not

decneth	dw	0			; CTERM session handle
decseg	dw	0			; segment of CTERM SCB memory block
lathand	dw	0			; LAT session handle, high byte = 0ffh
latseg	dw	0			; LAT SCB seg in our memory
latversion db	4			; LAT major version number
latscbptr dd	0,0,0			; LAT, pointer to SCB

lcbst	struc				; LAT control block structure V4
	service	db	17 dup (0)	; 0 service     (number is offset)
		db	10 dup (0)	; 17 node, for future use
	lat_pwd	dd	0		; 27 password buffer ptr
	pwd_len	db	0		; 31 length of the buffer
		db	22 dup (0)	; 32 reserved
	stopped	dd	0		; 54 session stopped post routine addr
	overflow dd	0		; 58 service table overflow post addr
	xnotify	dd	0		; 62 transmit post routine addr
	rnotify	dd	0		; 66 receive post routine addr
	sstatus	dw	0		; 70 session status
		db	270 dup (0)	; 72 reserved
	slotqty	db	2		; 342 number receive data slots
	slotused db	0		; 343 number occupied slots
	slotnr	db	0		; 344 index of next rcv slot to use
	slotcur	db	0		; 345 index of current rcv slot
	slotptr dw	0		; 346 ptr to first received char
	slottbl	dw	0		; 348 ptrs to bufs for slot 1
		dw	0		; 350 and for slot 2
	slotbf1 db	259 dup (0)	; 352 first receive buffer
	slotbf2 db	259 dup (0)	; 611 second receive buffer
lcbst	ends				; total of 870 bytes

latservice db	17 dup (0)		; LAT host name
latpwd	db	16 dup (0),0		; LAT password, terminator
decmsg1	db	cr,lf,'Cannot create DECnet session.$'
decmsg3	db	' DECnet Error $'
decmsg4	db	cr,lf,' CTERM ready$'
decmsg5	db	cr,lf,' LAT ready$'
decmsg6 db	cr,lf,' Unable to allocate LAT SCB, trying CTERM$'
				; end of DECnet and TES-LAT

pcnet	db	0		; Network is functioning
nambuf	db	65 dup (0)	; network long name storage (STARLAN)
newnambuf db	0		; non-zero if new entry into namebuf above
internet db	4 dup (0)	; TELAPI Internet address, binary
telses	dw	0		; TELAPI session number
telport dw	23		; TELAPI Telnet port (defaults to 23)
sposted	db	0		; send interlock, 0 if no send posted
rposted	db	0		; rcv interlock, 0 if no receive posted
lposted	db	0		; listen outstanding (if non-zero)
netdbfr	db	0		; non-zero if net board is double buffered
lnamestat db	0		; status of local name 0 = default,
				;  1 = specified by user, 2 = locked in
deflname db	'mskermit.K      ' ; default local name, 16 bytes
ivt1str	db	'iVT1',0	; FGR - OpenNet VT handshake string
inettyp	db	0		; FGR - network type 'N' or 'O'
nsbrk	dw	0		; net can send Break
starlan	db	0		; non-zero if StarLAN net
chkmsg1	db	cr,lf,'?Cannot construct a local Kermit name, error = $'
setnbad db	cr,lf,'?Local Kermit NetBIOS name is already fixed.$'
chkmsg2	db	cr,lf,lf,' Name $'
chkmsg3	db	' is already in use. Please enter another of',cr,lf
setnbhlp db	' 1 - 14 letters or numbers (or nothing to quit): $'
netmsg1	db	cr,lf,' Checking if our node name is unique ...$'
netmsg2	db	cr,lf,' The network is active, our name is $'
netmsg3 db	cr,lf,'  NetBios local name: $'
netmsg4	db	'  Remote host: $'
netmsg5 db	cr,lf,'  DECnet host: $'
netmsg6 db	cr,lf,'  TELAPI Internet host: $'
netmsg6a db	'  port: $'
netmsg7	db	cr,lf,'  TES host name: $'
netmsg9	db	cr,lf,'  SuperLAT name: $'
netmsg8	db	cr,lf,'  EBIOS server port name: $'
nonetmsg db	cr,lf,'?The network is not available$'
noname	db	cr,lf,'?No name exists for the remote host.$'
dnetsrv	db	cr,lf,' Available LAT service names:',cr,lf,'$'
ngodset	db	cr,lf,' Connecting to network node: $'
nbadset	db	bell,cr,lf,'?Cannot reach network node: $'
recmsg	db	cr,lf,'?Network receive failed, status = $'
sndmsg	db	cr,lf,'?Network send failed, status = $'
naskpmt	db	cr,lf,' A network session is active.',cr,lf
	db	' Enter RESUME to resume it or NEW to start a new session:',0
nettab	db	2
	mkeyw	'New',0
	mkeyw	'Resume',1

acnop	equ	80h			; ACSI nop
		; second byte: null (ignored)
acenable equ	81h			; ACSI raise modem leads
		; second byte: DTR=01h, RTS=02h
acdisable equ	82h			; ACSI drop all modem leads
		; second byte: null (ignored)
acbreak	equ	83h			; ACSI send a BREAK
		; second byte: null (ignored)
acsetmode equ	84h			; ACSI set port Mode (speed etc)
; second byte: speed=3bits, parity=2bits, stopbits=1bit, databits=2bits
acmodem	equ	85h			; ACSI return modem leads state
		; second byte: DCD=80h, RI=40h, DSR=20h, CTS=10h
acreqmodem equ	86h			; ACSI request modem leads state
		; second byte: DCD=80h, RI=40h, DSR=20h, CTS=10h
acdelay	equ	87h			; ACSI pause transmission
		; second byte: delay in hundreths of second
acpace	equ	88h			; ACSI set flow control or pacing
	; second byte: direction send=10h, recv=20h; if pacing send=1, recv=2
acsetxon equ	89h			; ACSI set XON pacing character
		; second byte: char to represent resume transmission
acsetxoff equ	8ah			; ACSI set XOFF pacing character
		; second byte: char to represent cease transmission
; ACSI general read character status:	BREAK detected=10h, framing error=08h,
;					parity error=04h, overrun=02h

ebbufset equ	0ffh			; EBIOS set buf mode (1=send, 2=rcv)
ebbufcnt equ	0fdh			; EBIOS get buf count (1=send, 2=rcv)
ebpace	equ	0feh			; EBIOS set pacing mode (80h=send)
ebrcv	equ	0fch			; EBIOS receive, no wait
ebsend	equ	1			; EBIOS send a char
ebmodem	equ	0fbh			; EBIOS set modem leads
ebbreak	equ	0fah			; EBIOS send a BREAK
ebcontrol equ	0f9h			; EBIOS regain control
ebredir equ	0f6h			; EBIOS do port redirection
ebquery	equ	0f5h			; EBIOS get redirection info
ebpresent equ	0f4h			; EBIOS presence check

ebport	dw	0			; EBIOS equivalent serial port 0..3
ebcoms	db	0,0			; adapter (port), path (80h=network)
	db	16 dup (0)		; Call name (host)
	db	16 dup (0)		; Listen name (null)
	db	16 dup (0)		; local name (null = use lan adapter)
	db	0			; unique name (1 = group name)
ebmsg2	db	cr,lf,'?No server port name is known,'
	db	' reenter the command with a name.$'
ebmsg3	db	cr,lf,'?Unable to contact that port. Error code=$'
ebiostab db	4			; EBIOS table of local port names
	mkeyw	'1',1
	mkeyw	'2',2
	mkeyw	'3',3
	mkeyw	'4',4

ifndef	no_tcp
tcpadrst db	cr,lf,'  tcp/ip address: $'	; TCP/IP status msgs
tcpsubst db	cr,lf,'  tcp/ip subnetmask: $'
tcpdomst db	cr,lf,'  tcp/ip domain: $'
tcpgatest db	cr,lf,'  tcp/ip gateway: $'
tcppnsst db	cr,lf,'  tcp/ip primary-nameserver: $'
tcpsnsst db	cr,lf,'  tcp/ip secondary-nameserver: $'
tcpbcstst db	cr,lf,'  tcp/ip broadcast: $'
tcphostst db	cr,lf,'  tcp/ip host: $'
tcpportst db	cr,lf,'  tcp/ip port: $'
tcpdebst db	cr,lf,'  tcp/ip debug-Options: $'
tcpbinst db	cr,lf,'  telnet mode: $'
tcppdintst db	',  Packet-Driver-interrupt: \x$'
tcppdnul db	' (search for it)$'
tcpodi	db	'   using ODI interface$'
tcpttyst db	cr,lf,'  telnet term-type: $'
tcpttynul db	'(report real terminal type)',0	; ASCIIZ
tcpnlst	db	cr,lf,'  telnet newline-mode: $' 
tcpnlmsg0 db	'off (CR->CR NUL)$'
tcpnlmsg1 db	'on (CR->CR LF)$'
tcpnlmsg2 db	'raw (CR->CR)$'
tcpdeboff db	'off$'
tcpdebon db	'on$'
tcpmodemsg0 db	'NVT-ASCII$'
tcpmodemsg1 db	'Binary$'
tcpbtpst db	'        from Bootp host: $'
tcpmssst db	cr,lf,'  tcp/ip mss: $'
endif	; no_tcp

badport	db	cr,lf,'?Port 25 is forbidden, sorry. Using 23 for Telnet.$'
maxsessions	equ	6		; max sessions, also in msntnd.c
seslist	db	maxsessions dup (-1) ; list of Telnet session idents, -1=dead
seshostlen equ	61			; length of host name, asciiz
sesname	db	maxsessions*seshostlen dup (0)	; host names, asciiz.
sesport	dw	maxsessions dup (23)	; Telnet ports
sescur	dw	-1			; Current session (0..5)
sesheader db	cr,lf,'         status    session   port hostname$'
sesinact  db	cr,lf,'         inactive     $'
sesactive db	cr,lf,'         active       $'
curmsg	  db	cr,lf,'         current >    $'
curinact  db	cr,lf,'         inactive >   $'
sesnone	  db	cr,lf,' Error, all sessions are in use$'
sesnohost db	cr,lf,' Oops, no host name is available$'
seshelp	db	cr,lf,lf,' choices  R           return to active session'
	db	cr,lf,'          1..6        pick a new or existing session'
	db	cr,lf,'          N           start a new session (default)'
	db	cr,lf,'          Q	     quit this command'
	db	cr,lf
	db	cr,lf,'Choice> $'
endif	; no_network
data	ends

data1	segment
ifndef	no_network
nethlp	db	cr,lf,'  node name of remote system,'
	db	cr,lf,'  or press ENTER to use current name,'
	db	cr,lf,'  or press ENTER for server mode (NetBios only).$'
dnethlp	db	cr,lf,'  node name of remote system,'
	db	cr,lf,'  or press ENTER to use current name,'
	db	cr,lf,'  or  *  to see a list of LAT service names.$'
nethlp2	db	cr,lf,' Optional LAT password, if using a LAT connection$'
ebhlp	db	'Name of server port$'	
setnbhlp2 db	' 1 - 14 letters or numbers (or nothing to quit): $'
telhlp	db	'Internet address nnn.nnn.nnn.nnn$'
telhlp2	db	'Optional TCP port (default is 23)$'
ifndef	no_tcp
tcphlp	db	cr,lf,' Host Internet name  machine.domain  or'
	db	' Internet address  nnn.nnn.nnn.nnn'
	db	cr,lf,'  or  *  to become a Telnet server.'
	db	cr,lf,' Optional TCP port number and NEW or RESUME'
	db	' may follow the host name.$'
tcpporthlp db	cr,lf,' TCP port on host, 23 is Telnet, or'
tcpnewhlp db	cr,lf,' NEW session or RESUME current session$'
endif	; no_tcp
endif	; no_network
fossilhlp db	cr,lf,' Fossil port, 1..99. No Fossil checking is done!$'
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.',cr,lf
	db	' For networks, the active session is terminated.',cr,lf,'$'
data1	ends

code1	segment
	assume	cs:code1
	extern	atsclr:near, setpos:near, setatch:near
ifndef	no_tcp	
	extern	termswapin:far, termswapdel:far, termswapout:far
endif	; no_tcp

fatsclr	proc	far
	call	atsclr
	ret
fatsclr	endp
fsetatch proc	far
	call	setatch
	ret
fsetatch endp
fsetpos	proc	far
	call	setpos
	ret
fsetpos	endp
code1	ends

code	segment
	extrn	comnd:near, prompt:near, dopar:near, lclyini:near
	extrn	strcpy:near, strlen:near,decout:near, prtasz:near, prtscr:near
	extrn	strcat:near, valout:near, crun:near, katoi:near, tolowr:near
	extrn	iseof:near
ifndef	no_terminal
	extrn	kbsusp:near, kbrest:near		; in msuibm.asm
endif	; no_terminal

	assume	cs:code, ds:data, es:nothing

fprtasz	proc	far
	call	prtasz
	ret
fprtasz	endp
fpcwait	proc	far
	call	pcwait
	ret
fpcwait	endp
fstrlen	proc	far
	call	strlen
	ret
fstrlen	endp
fdecout	proc	far
	call	decout
	ret
fdecout	endp
fvalout	proc	far
	call	valout
	ret
fvalout	endp
foutchr	proc	far
	call	outchr
	ret
foutchr	endp
fdopar	proc	far
	call	dopar
	ret
fdopar	endp

; local initialization

lclini	proc	near
	call	pcwtst		; calibrate software timer
	call	pcwtst
	mov	flags.comflg,1	; assume COM1 for communications port
	call	model		; get model of IBM machine
	mov	lclsusp,offset suspend ; call this when suspending to DOS
	mov	lclrest,offset restore ; call this when returning from DOS
	mov	lclexit,offset finexit ; call this when exiting Kermit
	call	getcodep		; get Code Page ident
	call	lclyini		; let other modules initialize too...
	ret
lclini	endp

; Call these routines when suspending Kermit to go to DOS
suspend	proc	near
ifndef	no_terminal
	call	kbsusp		; DEC LK250 keyboard, set back to DOS mode
endif	; no_terminal
	cmp	flags.comflg,'t'; doing TCP?
	je	suspen1		; e = yes, don't touch port
	cmp	portin,0	; port initialized yet?
	jle	suspen1		; l = no, e = yes but inactive
	call	ihosts		; suspend the host
	mov	ax,20		; wait 20 millisec for port to finish
	call	pcwait
	call	serrst
suspen1:ret
suspend	endp

; Call these routines when returning to Kermit from DOS
restore	proc	near
	call	getcodep	; reset Code Page ident
ifndef	no_terminal
	call	kbrest		; DEC LK250 keyboard, set back to DEC mode
endif	; no_terminal
	cmp	flags.comflg,'t'; doing TCP?
	je	restor1		; e = yes, don't touch port
	cmp	portin,0	; port initialized yet?
	jl	restor1		; l = no
	call	serini		; reinit serial port
	call	ihostr		; resume the host
restor1:ret
restore	endp

; Call these routines when doing final exit of Kermit
finexit	proc	near
	cmp	flags.carrier,0 ; be concerned about CD?
	je	finex4		; e = no
	call	testcd		; update cardet byte
	test	cardet,80h	; is carrier detect still active?
	jnz	finex3		; nz = yes
finex4:	cmp	nettype,0	; any network connections open?
	je	finex2		; e = no, but go through the motions anyway
finex3:	cmp	flags.exitwarn,0 ; warn about active session?
	je	finex2		; e = no, just kill the sessions
	mov	dx,offset clomsg ; say connection going, ask for permission
	call	prompt
	mov	dx,offset clotab ; table of choices
	xor	bx,bx		; help
	mov	ah,cmkey	; get keyword
	call	comnd
	jc	finex5		; c = failure
	push	bx
	mov	ah,cmeol
	call	comnd
	pop	bx
	jnc	finex1		; nc = got good response
finex5:	ret			; return carry set to say don't close
finex1:	or	bx,bx		; ok to close
	jz	finex2		; e = yes
	stc			; carry set to say don't close
	ret
finex2:	call	serrst		; reset serial port
ifndef	no_network
	call	netclose	; close network connections
endif	; no_network
ifndef	no_terminal
	call	kbsusp		; DEC LK250 keyboard, set back to DOS mode
endif	; no_terminal
	clc			; return with permission to exit
	ret
finexit	endp

; The IBM PC's.
model	proc	near
	mov	isps2,0			; PS/2 present indicator
	push	es
	push	ax			; get IBM model code
	mov	al,byte ptr es:[0fffeh]	; get model id byte
	mov	ah,0ch			; AT and PS/2 configuration call
	xor	al,al
	int	15h			; IBM Bios
	jc	model4			; c = no information
	cmp	word ptr es:[bx+2],040fch ; PS/2 model 50?
	je	model3			; e = yes
	cmp	word ptr es:[bx+2],050fch ; PS/2 model 60?
	je	model3			; e = yes
	cmp	byte ptr es:[bx+2],0f8h ; PS/2 model 80?
	jne	model4			; ne = no
model3:	mov	isps2,1			; say real PS/2 for IRQ setting
model4:
;;; [HF] 940130 The following section checks Japanese PS/55 mode
;;; [HF] 940130 moved from dvtest in MSYIBM.ASM
	mov	isps55,0		; [HF] 940130 assume not PS/55
	push	ds
	mov	ax,ds			; preset es:si to start of data seg
	mov	es,ax			; as saftey factor after test
	xor	si,si
	mov	ax,6300h		; get Double Byte Char Set Lead Table
	int	dos			; returns ptr in ds:si
	mov	bx,ds			; cannot trust al as return status
	mov	es,bx
	pop	ds
	mov	ax,ds			; current DS
	cmp	ax,bx			; same seg?
	je	modelx			; e = yes, test failed
	cmp	word ptr es:[si],0	; see if both bytes are also zeros
	je	modelx			; z = test failed
	mov	isps55,1		; [HF] say Japanese PS/55 is active
	mov	ax,1402h		; [HF]940206 check the modeline status
	int	16h			; [HF]940206 Keyboard Bios
	mov	ps55mod,al		; [HF]940206 save it in current status
	mov	byte ptr ps55mod+1,al	; [HF]940206 and in startup status
modelx:	pop	ax
	pop	es
	ret
model	endp

; Get the currently active Code Page ident into flags.chrset. User defined
; table overrides DOS report.
getcodep proc	near
	cmp	flags.chrset,1		; user-defined table in use?
	je	getcod1			; e = yes
	cmp	flags.chrset,866	; forced CP866 in use?
	je	getcod1			; e = yes
	mov	flags.chrset,437	; find default global char set
	cmp	dosnum,0300h+30		; DOS version 3.30 or higher?
	jb	getcod1			; b = no, no Code Pages
	mov	ax,6601h		; get global Code Page
	int	dos			; bx=active Code Page, dx=boot CP
	jc	getcod1
	mov	flags.chrset,bx		; setup default SET FILE CHAR SET
	mov	vtcpage,bx		; set terminal Code Page too
getcod1:ret
getcodep 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. Obsolete, name here for external reference only.
showkey	proc	near
	ret				; return
showkey	endp
code	ends

code1	segment
	assume	cs:code1

ifndef	no_tcp
ftcpstats proc	far			; TCP/IP status display
	mov	ah,prstr
	mov	dx,offset tcpadrst
	int	dos
	mov	dx,offset tcpaddress	; tcpaddress string
	call	fprtasz
	mov	bx,offset tcpbtpserver
	cmp	byte ptr [bx],0		; bootp host?
	je	ftcpst4			; e = no
	mov	dx,offset tcpbtpst	; show title
	int	dos
	mov	dx,bx			; show bootp host IP
	call	fprtasz
ftcpst4:mov	dx,offset tcpsubst
	int	dos
	mov	dx,offset tcpsubnet	; tcp subnetmask string
	call	fprtasz
	mov	dx,offset tcpdomst
	int	dos
	mov	dx,offset tcpdomain	; tcp domain string
	call	fprtasz
	mov	dx,offset tcpgatest
	int	dos
	mov	dx,offset tcpgateway	; tcp gateway string
	call	fprtasz
	mov	dx,offset tcppnsst
	int	dos
	mov	dx,offset tcpprimens	; tcp primary nameserver 
	call	fprtasz
	mov	dx,offset tcpsnsst
	int	dos
	mov	dx,offset tcpsecondns	; tcp secondary nameserver
	call	fprtasz
	mov	dx,offset tcpbcstst
	int	dos
	mov	dx,offset tcpbcast	; tcp broadcast address
	call	fprtasz
	mov	dx,offset tcpportst
	int	dos
	push	ax
	push	bx
	push	cx
	mov	bx,offset tcpport	; tcp port
	mov	ax,[bx]
	call	fdecout
	mov	ax,tcppdint		; Packet Driver interrupt
	cmp	ax,'DO'			; using ODI?
	jne	ftcpst3			; ne = no
	mov	ah,prstr
	mov	dx,offset tcpodi	; say using ODI interface
	int	dos
	jmp	short ftcpst1
ftcpst3:push	ax			; save value
	mov	ah,prstr
	mov	dx,offset tcppdintst
	int	dos
	pop	ax
	mov	cx,16
	push	ax
	call	fvalout			; show value as hex
	pop	ax
	or	ax,ax			; null?
	jnz	ftcpst1			; nz = no, just show value
	mov	ah,prstr
	mov	dx,offset tcppdnul	; show search msg
	int	dos
ftcpst1:pop	cx
	pop	bx
	pop	ax
	mov	dx,offset tcpttyst
	int	dos
	mov	dx,offset tcpttbuf	; tcp term-type override
	push	bx
	mov	bx,dx
	cmp	byte ptr [bx],0
	jne	ftcpst2
	mov	dx,offset tcpttynul	; alternate msg
ftcpst2:pop	bx
	call	fprtasz
	mov	dx,offset tcpnlst	; newline mode msg
	int	dos
	push	bx
	mov	bl,tcpnewline		; tcpnewline value of 0 (off) or 1
	mov	dx,offset tcpnlmsg1	; assume on
	cmp	bl,1			; on?
	pop	bx
	je	ftcpst5			; e = yes
	mov	dx,offset tcpnlmsg0	; assume off
	jb	ftcpst5			; b = correct
	mov	dx,offset tcpnlmsg2	; then use raw
ftcpst5:
	mov	ah,prstr
	int	dos
	mov	dx,offset tcpbinst	; debug-option
	int	dos
	push	bx
	mov	bl,tcpmode		; tcpmode
	mov	dx,offset tcpmodemsg0	; assume NVT-ASCII
	or	bl,bl			; NVT?
	pop	bx
	jz	ftcpst6			; z = yes, NVT
	mov	dx,offset tcpmodemsg1	; say BINARY
ftcpst6:mov	ah,prstr
	int	dos
	mov	dx,offset tcpmssst	; MSS
	int	dos
	mov	ax,tcpmss
	call	fdecout			; show value
	mov	ah,prstr
	mov	dx,offset tcpdebst	; debug-option
	int	dos
	push	bx
	mov	bl,tcpdebug		; offset of tcpdebug
	mov	dx,offset tcpdeboff	; assume off
	or	bl,bl			; off?
	pop	bx
	jz	ftcpst7			; z = yes
	mov	dx,offset tcpdebon	; say on
ftcpst7:mov	ah,prstr
	int	dos
	mov	dx,offset tcphostst
	int	dos
	mov	dx,offset tcphost	; tcp host IP ident string
	call	fprtasz
	ret
ftcpstats endp
endif	; no_tcp
code1	ends

code	segment
	assume	cs:code

; SHOW MODEM, displays current status of lines DSR, CD, and CTS.
; Uses byte mdmhand, the modem line status register.
shomodem proc	near
	mov	ah,cmeol		; get a confirm
	call	comnd
	jnc	shomd1a			; nc = success
	ret
shomd1a:cmp	flags.comflg,'F'	; Fossil?
	je	shomd1b			; e = yes
	cmp	flags.comflg,'4'	; hardware or Bios?
	ja	shomd3c			; a = no, show nothing
shomd1b:mov	dx,offset msmsg7	; no modem status for network
	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 msmsg20	; CD sensitive
	cmp	flags.carrier,0		; be sensitive?
	jne	shomd2a			; ne = yes
	mov	dx,offset msmsg21	; CD insenstive
shomd2a: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
	mov	al,flags.comflg
	cmp	al,'1'			; UART?
	jae	shomd3c			; ae = no
	add	al,'0'			; COMnumber
	mov	msmsg8+7,al		; stuff in msg
	mov	ah,prstr
	mov	dx,offset msmsg8	; show port base address
	int	dos
	mov	ax,modem.mddat		; port address
	mov	cx,16			; in hex
	call	valout
	mov	ah,prstr
	mov	dx,offset msmsg9	; and IRQ
	int	dos
	mov	ax,modem.mdintv		; interrupt vector
	mov	cl,al
	and	ax,7			; lower three bits of IRQ
	cmp	cl,60h			; using cascaded 8259?
	jb	shomd3a			; b = no
	add	ax,8			; say IRQ 8..15
shomd3a:call	decout			; output as decimal
	cmp	modem.mdfifo,0		; UART FIFO active?
	je	shomd3c			; e = no
	mov	dx,offset msmsg10	; FIFO msg
	mov	ah,prstr
	int	dos
shomd3b:mov	bx,portval
	mov	al,[bx].stopbits	; number of stop bits (1 or 2)
	add	al,'0'
	mov	msmsg14+2,al		; change the visible number
	mov	dx,offset msmsg14
	mov	ah,prstr
	int	dos
	clc
	ret
shomd3c:cmp	flags.comflg,'F'	; Fossil?
	jne	shomd4			; ne = no
	mov	ah,prstr
	mov	dx,offset msmsg11
	int	dos
	mov	ax,fossil_port
	inc	ax			; count from 1
	xor	dx,dx
	mov	cx,10			; decimal
	call	valout
	mov	ah,prstr
	mov	dx,offset msmsg12
	cmp	fossilflag,0		; do not close on done?
	je	shomd3d			; e = yes
	mov	dx,offset msmsg13
shomd3d:int	dos
shomd4:	clc
	ret
shomodem endp

; Show status of network connections
shownet	proc	near
	mov	ah,cmeol		; get a confirm
	call	comnd
	jnc	shonet0			; nc = success
	ret
shonet0:
ifndef	no_network
ifndef	no_tcp
	call	ftcpstats		; call TCP/IP FAR worker
endif	; no_tcp
	mov	dx,offset netmsg3	; local name
	mov	ah,prstr
	int	dos
	mov	cx,16
	mov	di,offset deflname	; default Netbios name
	call	prtscr
	cmp	rcv.scb_lname,' '	; have NetBios name yet?
	jbe	shonet1			; be = no, skip this part
	mov	ah,prstr
	mov	dx,offset netmsg4	; remote name
	int	dos
	mov	cx,16
	mov	di,offset rcv.scb_rname
	call	prtscr
shonet1:cmp	latservice,0		; DECnet name available?
	je	shonet3			; e = no
	mov	ah,prstr
	mov	dx,offset netmsg5
	cmp	latkind,DEC_LAT		; DEC_LAT?
	je	shonet2			; e = yes
	mov	dx,offset netmsg7	; TES heading
	cmp	latkind,TES_LAT		; TES_LAT?
	je	shonet2			; e = yes
	mov	dx,offset netmsg9	; Meridian SuperLAT
shonet2:int	dos
	mov	dx,offset latservice	; network host name, asciiz
	call	prtasz
shonet3:cmp	word ptr internet,0	; have TELAPI Internet address?
	je	shonet6			; e = no
	mov	ah,prstr
	mov	dx,offset netmsg6
	int	dos
	xor	bx,bx			; subscript
	mov	cx,4			; four fields
shonet4:mov	al,internet[bx]		; binary internet address
	xor	ah,ah
	call	decout
	cmp	cx,1			; doing last field?
	je	shonet5			; e = yes, no dot
	mov	dl,'.'
	mov	ah,conout
	int	dos
shonet5:inc	bx
	loop	shonet4
	mov	dx,offset netmsg6a	; port:
	mov	ah,prstr
	int	dos
	mov	ax,telport		; port
	call	decout

shonet6:cmp	tesname,0		; have TES name?
	je	shonet7			; e = no
	mov	ah,prstr
	mov	dx,offset netmsg7
	int	dos
	mov	dx,offset tesname	; node name
	call	prtasz			; show, asciiz
	clc
	ret
shonet7:cmp	ebcoms+2,0		; have an EBIOS name?
	je	shonet8			; e = no
	mov	ah,prstr
	mov	dx,offset netmsg8
	int	dos
	mov	cx,16
	mov	di,offset ebcoms+2	; host port, 16 chars space filled
	call	prtscr
shonet8:
endif	; no_network
	clc
	ret
shownet	endp

; Get modem status and set global byte mdmhand. Preserve all registers.
getmodem proc	near			; gets modem status upon request
	cmp	portin,1		; port active?
	je	getmod1			; e = yes
	mov	bl,flags.comflg		; pass current port ident
	cmp	bl,'4'			; above UART and Bios?
	ja	getmod1			; a = yes, do not start the port
	call	comstrt			; do SET PORT command now
	jnc	getmod1			; nc = success
	ret				; failed to set port
getmod1:xor	al,al			; assume nothing is on
	cmp	flags.comflg,'1'	; UART?
	jae	getmod2			; ae = no
	cmp	flags.comflg,0		; null port?
	je	getmodx			; e = yes, no status
	call	serini
	mov	dx,modem.mdstat		; hardware, line status reg
	inc	dx			; modem status reg
	in	al,dx
	push	ax
	call	serrst
	pop	ax
	jmp	short getmodx
getmod2:cmp	flags.comflg,'4'	; above Bios? (networks)
	ja	getmod3			; a = yes, no status
	mov	ah,3			; ask Bios for modem status into al
	push	dx
	xor	dh,dh
	mov	dl,flags.comflg		; get port id
	sub	dl,'1'			; remove ascii bias (BIOS1 -> 0)
	int	rs232
	pop	dx
	jmp	short getmodx

getmod3:
ifndef	no_network
	cmp	flags.comflg,'E'	; IBM EBIOS?
	jne	getmodx			; ne = no
	mov	ah,3			; get Bios style modem status
	push	dx
	mov	dx,ebport		; current EBIOS port
	int	rs232
	pop	dx
endif	; no_network
getmodx:xor	ah,ah			; return status in al
	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
	cmp	repflg,0		; REPLAY?
	je	clrbuf1			; e = no
	ret
clrbuf1:call	prtchr			; read from active comms port
	jnc	clrbuf1			; nc = got a char, continue til none
	mov	ax,10			; wait 10 ms
	call	pcwait
	call	prtchr			; read from active comms port
	jnc	clrbuf1			; nc = got a char, continue til none
	clc
	ret
CLRBUF	ENDP

; Clear to the end of the current line.  Returns normally.
; Upgraded for Topview compatibility.
CLEARL	PROC	NEAR
	push	ax
	push	bx
	push	dx
	mov	ah,3			; Clear to end of line
	xor	bh,bh
	int	video			; Get current cursor position into dx
	mov	ax,dx			; Topview compatible clear line
	mov	bh,ah			; same row
	mov	bl,byte ptr low_rgt	; last column
	call	fatsclr			; clear from ax to bx, screen coord
	pop	dx
	pop	bx
	pop	ax
	ret
CLEARL	ENDP

; This routine blanks the screen.  Returns normally.
; Upgraded to Topview compatiblity.
CMBLNK	PROC	NEAR
	push	ax
	push	bx
	xor	ax,ax		; from screen loc 0,0
	mov	bx,low_rgt	; to end of text screen (lower right corner)
	cmp	isps55,0	; [HF] Japanese PS/55?
	je	cmblnk1		; [HF] e = no
	cmp	ps55mod,0	; [HF] can access modeline ?
	je	cmblnk2		; [HF] e = no, system uses it. Do not access
cmblnk1:inc	bh		; include status line
cmblnk2:call	fatsclr		; do Topview compatible clear, in msyibm
	pop	bx
	pop	ax
	ret
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
 
; Position the cursor according to contents of DX:
; DH contains row, DL contains column.  Returns normally.
POSCUR	PROC	NEAR
	push	ax
	push	bx
	mov	ah,2			; Position cursor
	xor	bh,bh			; page 0
	int	video
	pop	bx
	pop	ax
	ret
POSCUR	ENDP

; Delete a character from the screen.  This works by printing
; backspaces and spaces.

DODEL	PROC	NEAR
	mov	ah,prstr
	mov	dx,offset delstr	; Erase character
	int	dos			
	ret
DODEL	ENDP

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

CTLU	PROC	NEAR
	mov	ah,prstr
	mov	dx,offset clrlin
	int	dos
	jmp	clearl
CTLU	ENDP


BEEP	PROC	NEAR
	mov	timeract,1	; say timer chip is being used here
	push	ax
	push	cx
	mov	al,10110110B	; Gen a short beep (long one losses data.)
	out	timercmd,al	; set Timer to to mode 3
	mov	ax,1512		; divisor, for frequency
	out	timer2data,al	; send low byte first
	mov	al,ah
	out	timer2data,al
	in	al,ppi_port	; get 8255 Port B setting
	or	al,3		; turn on speaker and timer
	out	ppi_port,al	; start speaker and timer
	push	ax
	mov	ax,40		; 40 millisecond beep, calibrated time
	call	pcwait
	pop	ax
	in	al,ppi_port
	and	al,0fch		; turn off speaker and timer
	out	ppi_port,al
	pop	cx
	pop	ax
	mov	timeract,0	; say timer chip is no longer in use here
	clc
	ret
BEEP	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	ax		; save regs
	push	bx
	push	cx
	push	dx
	push	si
	mov	si,dx		; preserve message
	mov	bl,scbattr	; screen attributes
	push	bx		; save scbattr
	and	bl,77h		; get colors, omit bright and blink
	rol	bl,1		; interchange fore and background
	rol	bl,1
	rol	bl,1
	rol	bl,1
	mov	scbattr,bl
	call	clrmod		; clear mode line to inverse video
	cmp	isps55,0	; [HF]940209 Japanese PS/55?
	je	putmo3		; [HF]940209 e = no
	push	ax		; [HF]940209
	mov	ax,1401h	; [HF]940209 disable system modeline
	mov	ps55mod,al	; [HF]940209 remember it
	int	16h		; [HF]940209
	pop	ax		; [HF]940209
putmo3:	mov	dx,low_rgt	; lower right corner
	inc	dh		;  of status line
	xor	dl,dl		; start on left
	mov	ah,scbattr	; inverse video attribute
	cld
putmo1:	lodsb			; get a byte
	cmp	al,'$'		; end of string?
	je	putmo2
	call	fsetatch	; write char and attribute
	inc	dl		; increment for next write
	cmp	dl,crt_cols	; beyond physical right border?
	jb	putmo1		; b = no
putmo2:	pop	bx
	mov	scbattr,bl
	pop	si
	pop	dx
	pop	cx
	pop	bx
	pop	ax
	ret
putmod	endp

; clear the mode line written by putmod.
clrmod	proc	near
	cmp	isps55,0		; [HF]940206 is Japanese PS/55?
	je	clrmod1			; [HF]940206 e = no, skip next proc.
	cmp	ps55mod,0		; [HF]940206 can we use modeline?
	jne	clrmod1			; [HF]940206 ne = yes, keep going
	push	ax			; [HF]940206 no, change the status
	mov	ax,1401h		; [HF]940208 access to modeline
	mov	ps55mod,al		; [HF]940208 change the status
	int	16h			; [HF]940208
	pop	ax			; [HF]940208
clrmod1:push	ax			; save regs
	push	bx
	mov	bx,low_rgt		; ending loc is lower right corner
	inc	bh			;  of status line
	mov	ah,bh
	xor	al,al			; column zero
	call	fatsclr			; clear this region
	cmp	isps55,0		; [HF]940206 is Japanese PS/55?
	je	clrmod2			; [HF]940206 e = no
	mov	ah,14h			; [HF]940206
	mov	al,byte ptr ps55mod+1	; [HF]940206 return to start value
	mov	ps55mod,al		; [HF]940206
	int	16h			; [HF]940206
clrmod2:pop	bx
	pop	ax
	ret
clrmod	endp

; put a help message on the screen.  This one uses reverse video...
; pass the message in ax, terminated by a null.
puthlp	proc	near
	push	bx		; save regs
	push	cx
	push	dx
	push	si
	push	ax		; preserve this
	cld
	mov	bl,scbattr	; screen attributes at Kermit init time
	and	bl,77h		; get colors, omit bright and blink
	rol	bl,1		; interchange fore and background
	rol	bl,1
	rol	bl,1
	rol	bl,1
	xor	bh,bh		; preset page 0
	mov	temp,bx		; temp = page 0, reverse video 

	mov	si,ax		; point to it
	mov	dh,1		; init counter
puthl1:	lodsb			; get a byte
	cmp	al,lf		; linefeed?
	jne	puthl2		; no, keep going
	inc	dh		; count it
	jmp	short puthl1	; and keep looping
puthl2:	or	al,al		; end of string?
	jnz	puthl1		; nz = no, keep going
	mov	ax,600h		; scroll to clear window
	xor	cx,cx		; from top left
	mov	dl,4fh		; to bottom right of needed piece
	mov	bh,bl		; inverse video
	int	video
	call	locate		; home cursor
xor bh,bh
;	mov	bx,0070h	; bh = page 0, bl = inverse video

	mov	bx,temp
	mov	cx,1		; one char at a time
	cld			; scan direction is forward
	pop	si		; point to string again
puthl3:	lodsb			; get a byte
	or	al,al		; end of string?
	jz	puthl4		; z = yes, stop
	push	si		; save around bios call
	cmp	al,' '		; printable?
	jb	puth21		; b = no
	mov	ah,9		; write char at current cursor position
	int	video		; do the Bios int 10h call
	inc	dl		; point to next column
	jmp	short puth23	; move cursor there
puth21:	cmp	al,cr		; carriage return?
	jne	puth22		; ne = no
	xor	dl,dl		; set to column zero
	jmp	short puth23
puth22:	cmp	al,lf		; line feed?
	jne	puth23
	inc	dh		; go to next line
puth23:	mov	ah,2		; set cursor position to dx
	int	video
	pop	si		; restore pointer
	jmp	short puthl3	; and keep going
puthl4:	mov	dh,byte ptr low_rgt+1	; go to last line
	inc	dh
	cmp	isps55,0	; [HF]940211 Japanese PS/55?
	je	puthl5		; [HF]940211 e = no
	cmp	ps55mod,0	; [HF]940211 system uses bottom line?
	jne	puthl5		; [HF]940211 ne = no
	dec	dh		; [HF]940211 do not touch bottom line
puthl5:	xor	dl,dl
	call	poscur		; position cursor
	pop	si
	pop	dx
	pop	cx
	pop	bx
	ret
puthlp	endp


; set the current port.  
; Note: serial port addresses are found by looking in memory at 40:00h and
; following three words for COM1..4, resp. All UARTS are assumed to follow
; std IBM addresses relative to 03f8h for COM1, and actual address are offset
; from value found in segment 40h. Global byte flags.comflg is 1,2,3,4 for
; COM1..4, and is 'N' for NetBios, etc.
; 'O' is for Intel Opennet Network (FGR)
; If address 02f8h is found in 40:00h then name COM1 is retained but COM2
;  addressing is used to access the UART and a notice is displayed. IRQ 3
; or IRQ 4 is sensed automatically for any COMx port.
COMS	PROC	NEAR
	mov	dx,offset comptab	; table of legal comms ports
	xor	bx,bx			; no extra help text
	mov	ah,cmkey		; parse key word
	call	comnd
	jnc	coms0a			; nc = success
	ret				; failure
coms0a:	cmp	bl,'4'			; networks?
	ja	comstrt			; a = yes
	mov	ah,cmeol		; non-network
	push	bx
	call	comnd			; get a confirm
	pop	bx
	jnc	comstrt			; nc = success
	ret				; failure

COMSTRT:mov	temp,bx			; port ident is in BL
	cmp	bl,'F'			; Fossil?
	jne	comst12			; ne = no
	jmp	comsf
comst12:

ifndef	no_network
	cmp	bl,'N'			; NetBios network?
	jne	comst2a			; ne = no
  	jmp	comsn			; yes, get another item for networks
comst2a:cmp	bl,'O'			; Opennet network?
  	jne	comst2			; ne = no
	jmp	comso			; yes, get another item for networks

comst2:	cmp	bl,'U'			; Ungermann Bass net?
	jne	comst3			; ne = no
	jmp	comsub
comst3:	cmp	bl,'D'			; DECnet?
	jne	comst4			; ne = no
	jmp	comsd			; do DECnet startup
comst4:	cmp	bl,'E'			; IBM EBIOS?
	jne	comst5			; ne = no
	test	nettype,acsi		; NetBios without EBIOS.COM?
	jz	comst4a			; z = no, using EBIOS.COM
	mov	inettyp,bl		; set type code
	jmp	comso2			; do NetBios setup
comst4a:jmp	comse			; do EBIOS checkup
comst5:	cmp	bl,'W'			; Novell NASI?
	jne	comst6			; ne = no
	jmp	comsub
comst6:	cmp	bl,'C'			; 3Com BAPI?
	jne	comst7			; ne = no
	jmp	comsbapi
comst7:	cmp	bl,'T'			; Novell TELAPI
	jne	comst8			; ne = no
	jmp	comstelapi
comst8:	cmp	bl,'I'			; TES?
	jne	comst9			; ne = no
	jmp	comsteslat
comst9:	cmp	bl,'t'			; Telnet, internal?
	jne	comst10			; ne = no
ifndef	no_tcp
	jmp	comstn
endif	; no_tcp
comst10:cmp	bl,'M'			; Meridian Superlat?
	jne	comst11			; ne = no
	jmp	comsmedlat

comst11:cmp	bl,'b'			; [JRS] Beame & Whiteside TCP?
	jne	coms1c			; ne = no
	jmp	comsbw			; setup BW connection
  					; stop sources of NetBios interrupts
coms1c:	call	nbclose			; close NetBios session now
endif	; no_network
	
coms2:	call	serrst			; close current comms port
	mov	al,byte ptr temp	; get COMx (1-4)
	mov	flags.comflg,al		; remember port ident
	cmp	al,'1'			; Bios?
	jb	coms2a			; b = no, hardware
	sub	al,'0'			; remove ascii bias for portinfo
coms2a:	dec	al
	xor	ah,ah			; count ports from 0
	push	bx			; set port structure
	mov	bx,type prtinfo		; size of each portinfo structure
	mul	bl			; times port number
	pop	bx			; restore register
	add	ax,offset port1		; plus start of COM1
	mov	portval,ax		; points to our current port struct
	cmp	flags.comflg,'1'	; Bios path?
	jb	coms4			; b = no, check hardware
	add	ax,offset portb1-offset port1 ; correct to use Bios ports
	mov	portval,ax
	mov	dl,flags.comflg
	sub	dl,'1'			; port, internal from 0
	mov	ah,3			; check port status, std Bios calls
	int	rs232			; Bios call
	and	al,80h			; get CD bit
	mov	cardet,al		; preserve as global
	clc
	ret

coms4: 	cmp	portin,-1		; serial port touched yet?
	jne	coms4a			; ne = yes, else avoid init looping
	mov	portin,0		; say serial port is being touched
coms4a:	push	es
	mov	ax,40h		; look at RS232_base [bx] in Bios area 40:00h
	mov	es,ax
	mov	bx,temp			; get desired port number 1..4
	xor	bh,bh
	dec	bl			; count com1 as bl = 0, etc
	shl	bx,1			; make bx a word index
	mov	ax,es:[bx]		; get modem base address into ax
	pop	es
	push	bx			; save this index
	or	ax,ax			; is address zero?
	jnz	comsc			; nz = no, have port address
	mov	ah,prstr
	mov	dx,offset badprt	; tell them what we are doing
	int	dos
	mov	ax,defcom[bx]		; get default COMn port address
	push	ax
	mov	cx,16			; base 16
	call	valout			; show port
	pop	ax
comsc:					; hardware tests
	mov	modem.mddat,ax	; set base address (also data address) 03f8h
	add	ax,2
	mov	modem.mdiir,ax		; interrupt identification reg 03fah
	inc	ax			; increment to command port 03fbh
	mov	modem.mdcom,ax		; set line control register address
	add	ax,2			; increment to status port 03fdh
	mov	modem.mdstat,ax		; set line-status port address
	call	chkport			; get type of UART support
	pop	bx			; recover port index
	jc	comsnu			; c = not a real 8250 class UART
	mov	ax,40h
	push	es
	mov	es,ax
	mov	ax,modem.mddat		; get COMn port address
	mov	es:[bx],ax		; force into seg 40h
	pop	es
	call	chkint			; find IRQ for the port
	jnc	comsc1			; nc = found, else error condition
	ret
comsc1:	call	getbaud			; update current baud info for port
	clc
	ret				; success

					; no UART
comsnu:	mov 	ah,prstr		; tell user about Bios pathway
	mov	dx,offset biosmsg
	int	dos
	mov	dl,byte ptr temp	; selected hardware port
	add	dl,'0'			; map to Bios
	mov	flags.comflg,dl
	mov	ah,conout		; say port number
	int	dos
	stc				; say error
	ret
ifndef	no_network
                                        ; Opennet Network support (FGR)
comso:	mov	inettyp,'O'		; remember OpenNet type network
	jmp	short comso2		; do generic network code
  					; NetBios Network support
comsn:	mov	inettyp,'N'		; remember Netbios type network
comso2:	mov	ah,cmword		; get a word (remote node name)
	mov	dx,offset decbuf	; work buffer
	mov	word ptr decbuf,0	; insert terminator
	mov	bx,offset nethlp	; help message
	call	comnd			; get the name, ignore errors
	mov	newnambuf,al		; save number of chars entered
	mov	ah,cmeol
	call	comnd			; get a confirm
	jc	comsn2			; c = failure
	; Enter here from EBIOS when EBIOS.COM is not installed. Use 'E'
	; and nettype subident of abios.
comsn3:	call	serrst			; reset serial port
	cmp	newnambuf,0		; any name given?
	je	comsn1			; e = no, use current name
	mov	si,offset decbuf
	mov	di,offset nambuf	; copy to here
	call	strcpy
comsn1:	call	chknet			; start network usage
	jc	comsn2			; c = failed
	cmp	pcnet,0			; is network alive (non-zero)?
	jne	comsn4			; ne = yes
comsn2:	stc
	ret				; failure

comsn4:	mov	portval,offset port_nb 	; set Network port structure address
	mov	port_nb.portrdy,1	; say the comms port is ready
	mov	al,inettyp		; FGR - get saved network type
	mov	flags.comflg,al		; set the Netbios port flag
	clc				; return success
	ret				; End NetBios

					; Ungermann-Bass terminal port [ohl +]
comsub:	push	bx			; save net type U or W
	mov     ah,cmeol
        call    comnd                   ; get a confirm
	jc	comsub0			; c = failure
	call    serrst			; reset serial port
        call    chkub                   ; check UB network presence
	pop	bx			; recover net type U or W
	jnc	comsub1			; nc = present
comsub0:ret				; return failure

comsub1:mov     portval,offset port_ub	; set Network port data structure addr
	mov	port_ub.portrdy,1	; say the comms port is ready
        mov     flags.comflg,bl		; set the comm port flag
	mov     pcnet,2                 ; network is present and active
        clc				; return success
        ret				; End Ungermann Bass / Novell NASI

comsmedlat:				; Meridian Superlat
	mov	latkind,MTC_LAT		; say Meridian
	jmp	short comsd1
					; TES_LAT
comsteslat:
	mov	latkind,TES_LAT		; say TES over LAT (vs DECnet)
	jmp	short comsd1		; common code
					; DECnet
comsd:	mov	latkind,DEC_LAT		; DECnet over LAT or CTERM

comsd1:	mov	ah,cmword		; get a word (remote node name)
	mov	dx,offset decbuf	; work buffer
	mov	word ptr decbuf,0	; insert terminator
	mov	bx,offset dnethlp	; help message
	call	comnd			; get the name
	mov	temp,ax			; save number of chars entered
	mov	ah,cmword		; get optional LAT service name
	mov	dx,offset decbuf+80	; work near end of this buffer
	mov	word ptr decbuf+80,0	; insert terminator
	mov	bx,offset nethlp2	; help message
	mov	comand.cmblen,16	; length of buffer (password = 16 chr)
	call	comnd			; get the name, ignore if absent
	mov	decbuf+79,al		; store byte count in name here
	mov	comand.cmblen,0		; length of buffer back to normal
	mov	ah,cmeol
	call	comnd			; get a confirm
	jnc	comsd3
	ret				; did not get a confirm
comsd3:	call	serrst			; close serial port now
	cmp	temp,0			; any node name?
	je	comsd8			; e = no, assume preestablished
 	cmp	decbuf,'*'		; just show LAT names?
	jne	comsd4			; ne = no
	call	far ptr chkdec		; see if network is available
	jc	comsd3a			; c = no, no net available
	and	nettype,not decnet	; remove CTERM indicator
	test	nettype,declat		; LAT type present?
	jz	comsd3a			; z = no
	call	latlst			; show LAT names
	and	nettype,not declat	; remove this indicator
	clc				; success but do not make connection
	ret

comsd3a:cmp	latkind,DEC_LAT		; doing DEC_LAT?
	je	comsd3b			; e = yes, DECnet
	mov	latkind,DEC_LAT		; not this method of TES
	jmp	comstes1		; try older TES method
comsd3b:mov	ah,prstr
	mov	dx,offset nonetmsg	; say net is not available
	int	dos
	clc
	ret
					; put name in uppercase, strip '::'
comsd4:	mov	si,offset decbuf	; the node name, make upper case
	mov	cx,temp			; length of node name
	add	si,cx			; and add '::' if absent
	push	ds
	pop	es
	cmp	byte ptr [si-1],':'	; ended on colon?
	jne	comsd5			; ne = no
	dec	cx			; remove it
	cmp	byte ptr [si-2],':'	; first colon present?
	jne	comsd5			; e = yes
	dec	cx			; remove it
comsd5:	mov	si,offset decbuf	; uppercase and copy name
	mov	di,offset latservice	; to this structure
	jcxz	comsd7a			; z = empty name
	cld
comsd6:	lodsb				; si = new node name
	cmp	al,'a'			; in lower case?
	jb	comsd7			; b = no
	cmp	al,'z'			; in lower case?
	ja	comsd7			; a = no
	and	al,not 20h		; convert to upper case
comsd7:	stosb
	loop	comsd6
comsd7a:mov	byte ptr [di],0		; terminate latservice name
	mov	si,offset decbuf+80	; LAT password
	mov	di,offset latpwd	; where it will reside
	mov	byte ptr [di],0		; clear password now
	call	strcpy			; copy it, asciiz
	mov	cl,decbuf+79		; length of name
	xor	ch,ch
	add	di,cx			; point to trailer
	sub	cx,16			; max chars in password
	jle	comsd8			; le = filled now, or no password
	mov	al,' '			; make it space filled
	push	ds
	pop	es
	rep	stosb			; fill in spaces
comsd8:	call	far ptr chkdec		; see if network is available
	jc	comsd3a			; c = no

	mov	ah,prstr
	test	nettype,decnet		; starting CTERM?
	jz	comsd9a			; z = no
	mov	dx,offset decmsg4	; assume CTERM
	int	dos
comsd9a:test	nettype,declat		; LAT too?
	jz	comsd9b			; z = no
	cmp	latkind,DEC_LAT		; DEC_LAT?
	jne	comsd9b			; ne = no, don't say LAT on screen
	mov	dx,offset decmsg5	; say LAT connection is ready
	int	dos
comsd9b:cmp	pcnet,2			; session active?
	jb	comsd10			; b = no, start a new one
	call	chknew			; session exists, Resume or start new?
	jc	comsd11			; c = resume
	call	decclose		; close current session
comsd10:mov     portval,offset port_dec ; set Network port data structure addr
	mov	al,'D'			; assume DECnet
	cmp	latkind,DEC_LAT		; DEC_LAT?
	je	comsd10b		; e = yes
	mov	al,'I'			; TES_LAT
comsd10a:cmp	latkind,MTC_LAT		; Meridian LAT?
	jne	comsd10b		; ne = no
	mov	al,'M'			; Meridian SuperLAT
comsd10b:mov	flags.comflg,al		; set the comm port flag
comsd11:clc
	ret				; end of DECnet


comsbapi:mov	ah,bapihere		; 3Com BAPI presence check
	xor	al,al
	mov	bx,0aaaah		; undocumented magic
	int	bapiint
	cmp	ax,0af01h		; signature
	jne	comsbap1		; ne = not present
	call	serrst			; close current port
	mov	ah,bapieecm		; disable Enter Command Mode char
	xor	al,al
	int	bapiint
	mov	portval,offset port_ub	; set Network port data structure addr
	mov	port_ub.portrdy,1	; say the comms port is ready
	or	nettype,bapi		; indentify network type
        mov     flags.comflg,'C'	; set the comm port flag
	mov     pcnet,2			; network is present and active
	clc				; success
	ret
comsbap1:mov	ah,prstr
	mov	dx,offset nonetmsg	; say no network
	int	dos
comsbap3:stc				; say failure
	ret				; end 3Com BAPI

comse:	mov	dx,offset ebiostab	; table of EBIOS ports
	xor	bx,bx
	mov	ah,cmkey
	call	comnd
	jnc	comse1			; nc = success
			; failure is ok, fails for GETBAUD etc calling comstrt
	mov	bx,ebport		; use current port
	inc	bx			; here count from 1
comse1:	push	bx
	mov	ah,cmword		; get a word (remote node name)
	mov	dx,offset decbuf	; work buffer
	mov	word ptr decbuf,0	; insert terminator
	mov	bx,offset ebhlp		; help message
	call	comnd			; get the name
	mov	ah,cmeol
	call	comnd			; get a confirm
	pop	bx
	jnc	comse7
	ret				; c = failure

comse7:	dec	bx			; count from 0
	mov	ebport,bx		; port number 0..3
	call	chkebios		; EBIOS presence check
	jnc	comse7a			; nc = success
	mov	inettyp,'E'		; try NetBios directly (ABIOS)
	mov	al,decbuf		; indicator of new host name
	mov	newnambuf,al		; set for NetBios routine
	or	nettype,acsi		; say doing special subset of NetBios
	jmp	comsn3			; setup NetBios connection

comse7a:push	es
	mov	bx,ds
	mov	es,bx
	mov	bx,offset ebcoms	; es:bx to ebcoms address
	mov	dx,ebport		; port number
	mov	cx,51			; number of bytes in ebcoms
	mov	ah,ebquery		; get redirection table info
	int	rs232
	mov	ebcoms,0		; query puts ebport in LANA, clear it
	cmp	decbuf,0		; any new name given?
	je	comse4			; e = no, presume name exists in EBIOS
	mov	si,offset decbuf	; user input
	mov	di,offset ebcoms+2	; where to store it
	mov	cx,16			; 16 char NetBios name
	cld
comse2:	lodsb				; get a new char
	or	al,al			; null terminator?
	jnz	comse3			; nz = no, use it
	mov	al,' '			; replace with space
	rep	stosb			; do remaining spots
	jmp	short comse3b		; carry on when done
comse3:	stosb				; store it
	loop	comse2

comse3b:call	setnbname		; setup local Netbios name
	jc	comse4			; c = failed
	mov	si,ds
	mov	es,si
	cld
	mov	ebcoms+2+16,0		; no Listen required, just a Call
	mov	cx,8
	mov	si,offset deflname	; our Netbios name
	mov	di,offset ebcoms+2+16+16
	rep	movsw
comse4:	mov	bx,offset ebcoms	; es:bx to ebcoms structure
	mov	ebcoms+1,80h		; force a network connection
	mov	ah,ebredir		; do redirection
	xor	al,al
	mov	dx,ebport
	int	rs232
	pop	es
	or	ax,ax			; status
	jz	comse5			; z is success

	push	ax
	mov	ah,prstr
	mov	dx,offset ebmsg3	; cannot open network
	int	dos
	pop	ax
	xchg	ah,al
	xor	ah,ah
	call	decout			; show error value
	stc
	ret
comse5:	cmp	ebcoms+2,' '		; do we have a name?
	ja	comse6			; a = yes
	mov	ah,prstr
	mov	dx,offset ebmsg2	; say bad command
	int	dos
	stc				; fail
	ret
comse6:	call	serrst			; reset previous port
	mov	bx,offset portb1	; use Bios data structure
	mov	ax,type prtinfo		; portinfo item size
	mov	cx,ebport		; actual port (0..3)
	mul	cl			; times port
	add	bx,ax			; new portb<n> offset
	mov	portval,bx
	mov	[bx].portrdy,1		; say the comms port is ready
	mov	[bx].sndproc,offset ebisnd ; send processor
	mov	[bx].rcvproc,offset ebircv ; receive processor
	mov	[bx].cloproc,offset ebiclose ; close processor
	or	nettype,ebios		; indentify network type
	mov	flags.comflg,'E'	; say EBIOS
	mov	pcnet,1
	clc				; success
	ret
comsex:	mov	dx,offset nonetmsg	; say network is not available
	mov	ah,prstr
	int	dos
comsex1:stc				; failure
	ret

comstelapi:				; Novell TELAPI
	mov	ah,cmword		; get a word (remote node name)
	mov	dx,offset decbuf	; work buffer
	mov	word ptr decbuf,0	; insert terminator
	mov	bx,offset telhlp	; help message
	call	comnd			; get the name
	jc	comstel9
	mov	word ptr decbuf+80,'\' 	; separator for katoi plus NULL
	mov	dx,offset decbuf+80+1	; user text goes here
	mov	bx,offset telhlp2
	mov	ah,cmword		; get optional Telnet port
	call	comnd
	jc	comstel9
	mov	ah,cmeol
	call	comnd			; get a confirm
	jnc	comstel1		; nc = ok so far
comstel9:ret				; did not get a confirm
comstel1:cmp	decbuf,0		; got new address?
	jne	comstel2		; ne = yes
	cmp	word ptr internet,0	; have we a previous address?
	jne	comstel6		; ne = yes, use it instead
	jmp	short comstel5		; no address, say illegal
comstel2:
	mov	si,offset decbuf+80+1	; port
	cmp	byte ptr [si],'\'	; user said '\'?
	je	comstel10		; e = yes
	dec	si			; use our slash
comstel10:call	katoi			; convert to number in ax
	jc	comstel11		; c = nothing convertable
	cmp	ax,25			; verboten?
	jne	comstel12		; ne = no
comstel11:mov	ax,23			; use default
comstel12:mov	telport,ax		; port number
	cmp	portin,0		; inited port yet?
	jl	comstel8		; l = no
	test	nettype,telapi		; already doing TELAPI?
	jz	comstel7		; z = no
	call	chknew			; ask Resume or New
	jc	comstel6		; c = Resume
comstel7:call	telapiclose		; close that connection
comstel8:xor	bx,bx			; convert Internet address to binary
	xor	dx,dx			; Internet field in dl, as a temp
	mov	word ptr internet,dx	; clear Internet address
	mov	word ptr internet+2,dx
	mov	si,offset decbuf	; fresh text
	cld

comstel3:lodsb				; get ascii digit
	cmp	al,'.'			; separator?
	jne	comstel4		; ne = no
	inc	bx			; next field
	cmp	bx,3			; beyond last field
	ja	comstel5		; a = yes, error
	jmp	short comstel3
comstel4:or	al,al			; end of information?
	jz	comstel6		; z = yes
	sub	al,'0'			; strip ascii bias
	cmp	al,9			; in digits?
	ja	comstel5		; a = no
	mov	dl,internet[bx]		; current field
	xor	dh,dh
	shl	dx,1			; times two
	push	dx			; save
	shl	dx,1			; times four
	shl	dx,1			; times eight
	pop	cx			; recover times two
	add	dx,cx			; plus two = times ten
	add	al,dl			; plus current digit
	mov	internet[bx],al		; save value
	jmp	short comstel3		; next character

comstel5:mov	ah,prstr		; say bad address construction
	mov	dx,offset telmsg1
	int	dos
	mov	dx,offset decbuf	; show address
	call	prtasz
	stc
	ret
comstel6:call	serrst			; end previous async session
	mov	portval,offset port_ub	; set Network port data structure addr
	or	nettype,telapi		; indentify network type
        mov     flags.comflg,'T'	; set the comm port flag
	clc
	ret

comstes1:mov	cx,0ffffh		; old TES presence check
	mov	ah,tesinstal		; installation/status check
	call	tes_service
	jc	comstes2		; c = error of some kind
	cmp	ax,'TE'			; signature
	jne	comstes2		; ne = not present
	cmp	cx,0ffffh		; should change too
	jne	comstes3		; ne = present
comstes2:mov	ah,prstr
	mov	dx,offset nonetmsg	; say no network
	int	dos
	stc				; say failure
	ret
comstes3:mov	tesport,dx		; remember TES intercepted port
	call	serrst			; close current port
	mov	portval,offset port_tes ; set Network port data structure addr
	or	nettype,tes		; indentify network type
        mov     flags.comflg,'I'	; set the comm port flag
	mov	latkind,DEC_LAT		; not LAT kind
	cmp	decbuf,'*'		; was show-all entered?
	jne	comstes4		; ne = no
	jmp	teshosts		; show known TES hosts
comstes4:mov	ax,temp			; length of entered name
	or	al,tesname		; plus preexisting name, if any
	or	ax,ax			; anything present?
	jz	comstes9		; z = no name, find existing session
	cmp	decbuf,0		; anything to copy?
	je	comstes5		; e = no, use existing name
	call	tesclose		; close existing connection
	or	nettype,tes		; indentify network type
	mov	si,offset decbuf
	mov	di,offset tesname
	call	strcpy			; copy node name
comstes5:clc
	ret

comstes9:mov	tesses,1		; search for an active/held session
comstes10:call	tesstate		; get state of that session to AH
	test	ah,2			; active?
	jnz	comstes12		; nz = yes, use it
	inc	tesses			; next session
	cmp	tesses,9		; done all?
	jbe	comstes10		; be = no
	mov	tesses,1		; try for a held session
comstes11:call	tesstate		; get state of that session to AH
	test	ah,1			; on hold?
	jnz	comstes12		; nz = yes, use it
	inc	tesses			; next session
	cmp	tesses,9		; done all?
	jbe	comstes11		; be = no
	mov	tesses,0		; say no session
	mov	ah,prstr
	mov	dx,offset tesnhost	; give directions
	int	dos
	call	teshosts		; show known hosts
comstes12:ret

					; TCP/IP Telnet
comstn:
ifndef	no_tcp
	mov	ah,cmword		; get a word (remote node name)
	mov	comand.cmblen,60	; set 60 byte limit plus null
	mov	dx,offset decbuf+1	; work buffer, 1st byte = count
	mov	word ptr decbuf,0	; insert terminator
	mov	decbuf+80,0		; byte count of args
	mov	bx,offset tcphlp	; help message
	call	comnd			; get the name
	jc	comstnx
	or	al,al			; anything?
	jz	comstn1			; z = no
	mov	decbuf,al		; store byte count
	mov	ah,cmword		; get optional Port number
	mov	comand.cmblen,7		; set 7 byte limit plus null
	mov	dx,offset decbuf+82	; far from real node names
	mov	decbuf+80,0		; byte count of args
	mov	decbuf+81,'\'		; \number prefix, just in case
	mov	word ptr decbuf+82,0	; clear entry
	mov	bx,offset tcpporthlp
	call	comnd
	jc	comstnx
	mov	decbuf+80,al		; store arg byte count
	mov	dx,offset decbuf+83	; get optional NEW or RESUME
	add	dx,ax			; plus byte count of above
	mov	bx,dx
	mov	word ptr [bx],0		; clear count and inital byte
	inc	dx
	push	bx
	mov	ah,cmword		; get optional NEW/RESUME
	mov	comand.cmblen,7		; set 7 byte limit plus null
	mov	bx,offset tcpnewhlp
	call	comnd
	pop	bx
	jc	comstnx
	mov	[bx-1],al		; store arg byte count

comstn1:mov	ah,cmeol
	call	comnd			; get a confirm
	jnc	comstn2			; nc = ok so far
comstnx:mov	kstatus,ksgen		; global status for unsuccess
	ret				; did not get a confirm

comstn2:call	serrst			; close current comms port
	call	sesmgr			; SESSION MANAGER
	jnc	comstn3			; nc = start a session
	mov	kstatus,ksgen		; global status for unsuccess
	ret				; cannot allocate a session, fail
comstn3: mov	flags.comflg,'t'	; what we want, may not have yet
	mov	bx,offset port_tn	; set Network port data structure addr
	mov	[bx].floflg,0		; flow control kind, (none)
	mov	[bx].flowc,0		; flow control characters (none)
	mov 	portval,bx
        mov     nsbrk,1                 ; network BREAK supported
	clc
else
	stc
endif	; no_tcp
	ret
					; Beame & Whiteside TCP/IP
comsbw:	mov	ah,cmword		; get a word (remote node name)
	mov	dx,offset decbuf	; work buffer
	mov	word ptr decbuf,0	; insert terminator
	mov	decbuf+80+1,0
	mov	bx,offset telhlp	; help message
	call	comnd			; get the name
	jc	comsbw9			; c = failure, get eol
	mov	ah,cmword		; get optional port number
	mov	dx,offset decbuf+80+1	; storage spot
	mov	decbuf+80,'\'		; number prefix
	mov	decbuf+80+1,0
	mov	bx,offset telhlp2	; help for optional port
	mov	comand.cmcr,1		; bare CR's allowed
	call	comnd
comsbw9:mov	ah,cmeol
	call	comnd			; get a confirm
	jnc	comsbw1			; nc = ok so far
	mov	kstatus,ksgen		; global status for unsuccess
	ret				; did not get a confirm
comsbw1:cmp	decbuf,0		; got new address?
	jne	comsbw2			; ne = yes
	cmp	word ptr internet,0	; have we a previous address?
	je	comsbw5			; e = no address, say illegal
	jmp	comsbw6

comsbw2:mov	si,offset decbuf+80+1	; port
	cmp	byte ptr [si],'\'	; user said '\'?
	je	comsbw2a		; e = yes
	dec	si			; use our slash
comsbw2a:call	katoi			; convert to number in ax
	jc	comsbw2b		; c = nothing convertable
	cmp	ax,25			; verboten?
	jne	comsbw2c		; ne = no
comsbw2b:mov	ax,23			; use default
comsbw2c:mov	telport,ax		; port number

	cmp	portin,0		; inited port yet?
	jl	comsbw8			; l = no
	test	nettype,bwtcp		; [JRS] already doing BWTCP?
	jz	comsbw7			; [JRS] z = no
	call	chknew			; ask Resume or New
	jnc	comsbw7			; nc = New
	jmp	comsbw6
comsbw7:call	bwclose			; close current connection
comsbw8:xor	bx,bx			; convert Internet address to binary
	xor	dx,dx			; Internet field in dx, as a temp
	mov	word ptr internet,dx	; clear Internet address
	mov	word ptr internet+2,dx
	mov	si,offset decbuf	; fresh text
	cld
	cmp	byte ptr [si],'*'	; [JRS] telnet server mode?
	jne	comsbw3			; [JRS] ne = no, process IP address
	mov	word ptr internet,-1	; [JRS] bogus IP for server mode
	mov	word ptr internet+2,-1	; [JRS] bogus IP for server mode

comsbw6:call	serrst			; end previous async session
	push	bx
	mov	bx,offset port_tn
	mov	portval,bx
	mov	[bx].sndproc,offset bwsend ; [JRS] BW send routine
	mov	[bx].rcvproc,offset bwrecv ; [JRS] BW Receive routine
	mov	[bx].cloproc,offset bwclose ; [JRS] BW Close routine
	pop	bx
	or	nettype,bwtcp		; [JRS] set it to BW-TCP
        mov     flags.comflg,'b'	; [JRS] set the comm port flag
	clc
	ret

comsbw3:lodsb				; get ascii digit
	cmp	al,'.'			; separator?
	jne	comsbw4			; ne = no
	inc	bx			; next field
	cmp	bx,3			; beyond last field
	ja	comsbw5			; a = yes, error
	jmp	short comsbw3
comsbw4:or	al,al			; end of information?
	jz	comsbw6			; z = yes
	sub	al,'0'			; strip ascii bias
	cmp	al,9			; in digits?
	ja	comsbw5			; a = no
	mov	dl,internet[bx]		; current field
	xor	dh,dh
	shl	dx,1			; times two
	push	dx			; save
	shl	dx,1			; times four
	shl	dx,1			; times eight
	pop	cx			; recover times two
	add	dx,cx			; plus two = times ten
	add	al,dl			; plus current digit
	mov	internet[bx],al		; save value
	jmp	short comsbw3		; next character

comsbw5:mov	ah,prstr		; say bad address construction
	mov	dx,offset telmsg1
	int	dos
	mov	dx,offset decbuf	; show address
	call	prtasz
	stc
	ret
endif	; no_network
					; Fossil
comsf:	mov	ah,cmword		; get a word (remote node name)
	mov	dx,offset decbuf+1	; work buffer
	mov	decbuf,'\'		; numeric prefix
	mov	word ptr decbuf+1,0	; insert terminator
	mov	bx,offset fossilhlp	; help message
	call	comnd			; get the port, optional
	mov	ah,cmeol
	call	comnd			; get a confirm
	jnc	comsf2			; nc = ok so far
comsf1:	mov	kstatus,ksgen		; global status for unsuccess
	ret				; did not get a confirm
comsf2:	mov	si,offset decbuf+1	; user string
	cmp	byte ptr [si],'\'	; did user include a backslash?
	je	comsf3			; e = yes
	dec	si			; else use ours
comsf3:	call	katoi 			; convert to binary
	jc	comsf4			; c = failure
	cmp	ax,99			; largest acceptable value
	ja	comsf4			; a = failure
	dec	ax			; count internally from 0 as F1
	js	comsf4			; sign = failure
	mov	fossil_port,ax		; Fossil port (Fossil1 is value 0)
	call	serrst			; end previous async session
	mov     flags.comflg,'F'	; set the comm port flag
	mov	portval,offset portb1
	mov	bx,portval
	mov	[bx].sndproc,offset fossnd
	mov	[bx].rcvproc,offset fosrcv
	mov	portin,0		; say port is selected
	clc
	ret
comsf4:	stc
	ret
COMS	ENDP

ifndef	no_network
teshosts proc	near			; TES, show list of hosts
	mov	ah,prstr
	mov	dx,offset tesnlist	; show list of hosts
	int	dos
	push	es
	push	si
	mov	ah,tesgets		; get TES known servers
	call	tes_service		; cx gets qty
	mov	dx,offset decbuf	; work buffer
	mov	word ptr decbuf,'  '	; item spacing
	xor	di,di			; line length counter
	or	cx,cx			; any servers?
	jnz	teshost1		; nz = some servers
	inc	cx			; say one for loop
	push	cx
	mov	word ptr decbuf+2,'ON'	; prefill with NONE
	mov	word ptr decbuf+4,'EN'
	mov	decbuf+6,0		; asciiz
	mov	cx,6			; length of "  NONE"
	jmp	short teshost3		; show NONE
teshost1:push	cx			; count of servers
	push	si			; save master list pointer
	push	di
	mov	si,es:[si]		; get offset of first name
	mov	di,dx			; decbuf
	add	di,2			; move into decbuf+2
teshost2:mov	al,es:[si]		; get name char
	inc	si
	mov	[di],al			; store name char
	inc	di
	or	al,al			; at end?
	jnz	teshost2		; nz = no
	pop	di
	pop	si			; master list of offsets
	add	si,2			; next item in list
	call	strlen			; get length of name w/spaces
	add	di,cx			; length of display line with name
	cmp	di,60			; time to break line?
	jbe	teshost3		; be = no
	push	dx
	mov	ah,prstr		; break the screen line
	mov	dx,offset crlf
	int	dos
	pop	dx
	xor	di,di			; say line is empty
teshost3:add	di,cx			; current line length
	call	prtasz			; show asciiz text at ds:dx
	pop	cx
	loop	teshost1		; do all entries
	pop	si
	pop	es
	ret
teshosts endp

; Perform TES function given in AH, but tell cmd interpreter to not prompt.
tes_service     proc    near
        push    ax
        push    si
        push    es
        mov     ax,ds
        mov     es,ax
	mov	si,offset tesquiet	; cmd to say no prompting
	xor	al,al			; no visible response please
	mov	ah,tesexec
        int     rs232
        pop     es
        pop     si
        pop     ax
	push	ds			; prevent corruption by TES
        int     rs232			; do the main request
	pop	ds
        ret
tes_service     endp

; TES - start or resume a TES connection
tesstrt	proc	near
	cmp	tesses,0		; session yet?
	jne	tesstr2			; ne = yes
	cmp	tesname,0		; have a node name?
	jne	tesstr1			; ne = yes
	jmp	tesstr4			; find an existing connection, if any

tesstr1:push	si
	mov	si,offset tesname	; node name
	mov	ah,tesnews		; make a new session
	call	tes_service
	pop	si
 	jmp	short tesstr4		; double check on transistions

					; have a session, live or held
tesstr2:call	tesstate		; get session status
	test	ah,2			; active or being made so?
	jnz	tesstr3			; nz = yes, connect to it
	test	ah,1			; session on hold or being made so?
	jz	tesstr1			; z = no, make a new one
	mov	ah,tesresume		; resume a session
	mov	al,tesses		; the session number
	call	tes_service
	shl	ah,1			; get status into carry bit
	jc	tesstr4			; c = problem, try to recover

tesstr3:cmp	tesname,0		; have name yet?
	je	tesstr4			; e = no, get it now
	mov	pcnet,2			; have an active session
	mov	port_tes.portrdy,1	; say port is ready
	clc
	ret

tesstr4:push	si			; find an active session number
	push	es
	mov	temp,0			; retry counter
	mov	tesses,1		; session number
tesstr5:mov	ah,teslist		; get TES session pointers
	call	tes_service		; CX gets quantity of ACTIVE sessions
tesstr6:mov	al,es:[si]		; get session status
	cmp	al,82h			; making a session now?
	je	tesstr7			; e = yes, good enough
	test	al,80h			; in transitional status?
	jz	tesstr6b		; z = no
	mov	ah,100
	call	pcwait			; wait 100 ms
	inc	temp
	cmp	temp,10
	ja	tesstr6a		; a = too many retries, quit
	jmp	short tesstr5		; nz = yes, go around again
tesstr6b:mov	temp,0			; clear retry counter
	test	al,2			; 0=inactive, 1=on hold, 2=active?
	jnz	tesstr7			; nz = active
	add	si,3			; skip status byte, name offset
	inc	tesses			; bump session number
	cmp	tesses,9		; done all?
	jbe	tesstr6			; be = no
tesstr6a:mov	tesses,0		; no active session
	mov	port_tes.portrdy,0	; say the comms port is not ready
	mov     pcnet,1			; network is present and inactive
	stc				; signal failure
	jmp	short tesstr8
					; set port ready, get host name
tesstr7:mov	port_tes.portrdy,1	; say the comms port is ready
	mov     pcnet,2			; network is present and active
	mov	di,offset tesname	; readback asciiz name
	mov	byte ptr [di],0		; clear the name field
	mov	si,es:[si+1]		; get name ptr offset
	or	si,si			; check for null pointer
	jz	tesstr6a		; z = no name, dead session
	mov	cx,49			; limit to 48 chars plus null
tesstr7a:mov	al,es:[si]		; get a char
	inc	si
	mov	[di],al			; store
	inc	di
	or	al,al			; null?
	jz	tesstr7b		; z = yes, stop here
	loop	tesstr7a		; keep going
tesstr7b:clc				; signal success
tesstr8:pop	es
	pop	si
	ret
tesstrt	endp

; TES. Return session status in AH for session in tesses. Destroys AX
tesstate proc	near
	push	cx
	push	si
	push	es
	mov	ah,teslist		; get session list
	call	tes_service
	mov	ah,0ffh			; set up no session status
	mov	al,tesses		; our session number, 1..9
	dec	al			; base on zero here
	mov	ah,al
	shl	ah,1
	add	al,ah
	xor	ah,ah			; times three
	add	si,ax			; point to session structure es:[si]
	mov	ah,es:[si]		; get session status byte
testat1:pop	es
	pop	si
	pop	cx
	ret
tesstate endp

; Start Novell TELAPI session. Internet address is already binary
telstrt	proc	near
	mov	dx,word ptr internet	; internet address, high part
	mov	cx,word ptr internet+2
	mov	bx,telport		; Telnet port (23 decimal)
	push	es
	push	si
	xor	si,si			; use TELAPI data structures, not ours
	mov	es,si			; es:si = Telnet state record, theirs
	mov	di,offset telhostid	; two byte host identifier in ds:di
	mov	ah,telopen		; open the connection
	int	rs232
	mov	telses,ax		; save TELAPI session number
	pop	si
	pop	es
	or	ax,ax			; get status returned in AX
	jns	telstr1			; ns = success
	cmp	ax,telinuse		; socket is already connected? (-56)
 	jne	telstr2			; ne = no, else attach to it
telstr1:mov	port_ub.portrdy,1	; say the comms port is ready
	mov     pcnet,2			; network is present and active
	or	nettype,telapi
	clc
	ret
					; failure message display section
telstr2:mov	dx,offset telmsg51	; network unreachable
	cmp	ax,telunreac		; correct?
	je	telstr3			; e = yes
	mov	dx,offset telmsg56	; socket in use
	cmp	ax,telinuse
	je	telstr3
	mov	dx,offset telmsg60	; timeout on connection attempt
	cmp	ax,teltmo
	je	telstr3
	mov	dx,offset telmsg61	; connection refused
	cmp	ax,telrefuse
	je	telstr3
	mov	dx,offset telmsg64	; host is down
	cmp	ax,teldwnhost
	je	telstr3
	mov	dx,offset telmsg67	; unknown host
	cmp	ax,telunkhost
	je	telstr3
	mov	dx,offset telmsg301	; no more space
	cmp	ax,telfull
	je	telstr3
	push	ax			; unknown error, show error value
	mov	dx,offset telmsg2
	mov	ah,prstr
	int	dos
	pop	ax
	neg	ax			; make positive number again
	call	decout			; show error value, for now
	jmp	short telstr4
telstr3:mov	ah,prstr
	int	dos			; show reason msg
telstr4:mov	ax,3000			; show for three seconds
	call	pcwait
	mov	port_ub.portrdy,0	; say port is not ready
	and	nettype,not telapi	; clear network use bit
	stc				; fail
	ret
telstrt	endp

; Start Beame & Whiteside TCP connecton.  IP address is already binary [JRS]
bwstart	proc	near			; [JRS]
	push	bx			; [JRS]
	push	cx			; [JRS]
	push	dx			; [JRS]
	mov	bx,bwhandle		; [JRS] check the file handle
	or	bx,bx			; [JRS] non-zero is open
	jnz	bwstrt1			; [JRS] nz = yes, just continue

	mov	ax,3d42h		; [JRS] open file shared read/write
	mov	dx,offset bwtcpnam	; [JRS] "TCP-IP10"
	int	dos
	jc	bwstrt2			; c = failed
	mov	bwhandle,ax		; [JRS] save file handle
	mov	bx,ax			; [JRS] copy handle
	mov	ax,4401h		; [JRS] io/ctl - set dev info
	xor	cx,cx			; [JRS]
	mov	dx,60h			; [JRS] data is raw
	int	dos			; [JRS]
	jc	bwstrt2			; c = failed
	mov	xmtbuf,0		; [JRS] set up port bind address
	push	es
	mov	ax,40h			; Bios work area
	mov	es,ax
	mov	ax,es:[6ch]		; low word of Bios tod tics
	pop	es
	mov	word ptr xmtbuf+1,ax	; local port
	mov	ax,4403h		; [JRS] write to device
	mov	bx,bwhandle		; [JRS] device handle
	mov	cx,3			; [JRS] length of data
	mov	dx,offset xmtbuf	; [JRS] data buffer
	int	dos			; [JRS]
	jc	bwstrt2			; c = failed

	mov	xmtbuf,1		; [JRS] set up IP bind address
	mov	dx,word ptr internet	; [JRS] internet address, high part
	mov	cx,word ptr internet+2	; [JRS]
	cmp	dx,-1			; [JRS] check for server addr -1
	jz	bwaccept		; [JRS] z = -1, do accept processing

	mov	word ptr xmtbuf+1,dx	; [JRS] store address in buffer
	mov	word ptr xmtbuf+3,cx	; [JRS]
	mov	ax,telport
	mov	word ptr xmtbuf+5,ax	; telnet port (23)
	mov	ax,4403h		; [JRS] write to device
	mov	bx,bwhandle		; [JRS]
	mov	cx,7			; [JRS]
	mov	dx,offset xmtbuf	; [JRS]
	int	dos			; [JRS]
	jc	bwstrt2			; c = failed

bwstrt0:mov	xmtbuf,6		; [JRS] set up for no read blocking
	mov	xmtbuf+1,1		; [JRS]	control string is \6\1
	mov	ax,4403h		; [JRS] write to device
	mov	bx,bwhandle		; [JRS]
	mov	cx,2			; [JRS]
	mov	dx,offset xmtbuf	; [JRS]
	int	dos			; [JRS]
	jc	bwstrt2			; c = failed

bwstrt1:pop	dx			; [JRS] restore registers
	pop	cx			; [JRS]
	pop	bx			; [JRS]
	mov	port_tn.portrdy,1	; [JRS] say the comms port is ready
	mov     pcnet,2			; [JRS] network is present and active
	or	nettype,bwtcp		; [JRS]
	mov	optstate,0		; init Options
	mov	sgaflg,0		; assume supresss go aheads in effect
	mov	option2,TELOPT_SGA
	mov	ah,DO
	call	bwsendiac		; say do supress go aheads
	clc
	ret
bwstrt2:call	bwclose			; [JRS] failure, close "file"
	pop	dx			; [JRS]
	pop	cx			; [JRS]
	pop	bx			; [JRS]
	stc				; c = failed
	ret

bwaccept:mov	xmtbuf,2		; [JRS] set port to accept calls
	mov	ax,4403h		; [JRS] write to device
	mov	bx,bwhandle		; [JRS]
	mov	cx,1			; [JRS]
	mov	dx,offset xmtbuf	; [JRS]
	int	dos			; [JRS]
	mov	ax,4402h		; [JRS] read from device
	mov	bx,bwhandle		; [JRS] device handle
	mov	cx,11			; [JRS] buffer length
	mov	dx,offset xmtbuf	; [JRS] buffer
	int	dos			; [JRS]
bwacpt1:cmp	xmtbuf,4		; [JRS] check response 0 < xmtbuf < 4
	jge	bwacpt2			; [JRS]	complete
	cmp	xmtbuf,0		; [JRS]
	je	bwacpt2			; [JRS]
					; [JRS] waiting for a connection
	mov	ax,4402h		; [JRS] read from device
	mov	bx,bwhandle		; [JRS]
	mov	cx,11			; [JRS]
	mov	dx,offset xmtbuf	; [JRS]
	int	dos			; [JRS]
	jmp	short bwacpt1		; [JRS] got look at response
bwacpt2:cmp	xmtbuf,0		; [JRS] check for response of zero
	je	bwstrt2			; [JRS] e=yes, fail
	jmp	bwstrt0			; [JRS] success, we have a call
bwstart	endp				; [JRS]
endif	; no_network
code	ends

code1	segment
	assume	cs:code1
ifndef	no_network
; Check for presence of DECNET. Host name is in latservice.
; Try LAT then try CTERM. Sets nettype for kind found.
; Return carry clear if success, or carry set if failure.
chkdec	proc	FAR
	cmp	pcnet,2			; net active now?
	jb	chkde2			; b = no
	cmp	lathand,0		; valid LAT handle?
	jne	chkde1			; ne = yes
	cmp	decneth,0		; valid LAT handle?
	je	chkde2			; e = invalid handle
chkde1:	clc
	ret				; return to active session

chkde2:	push	es
	and	nettype,not (declat+decnet) ; clear network type bits
	call	chklat			; check for LAT
	jc	chkde3			; c = not present
	mov	latversion,4		; assume DECnet version 4 or later
	xor	bx,bx
	mov	es,bx			; clear ES:BX for text below
	mov	ax,latscbget		; try getting SCB internal to LAT
	mov	dh,0ffh
	int	latint
	jc	chkde2a			; if this kind of failure
	or	ah,ah			; success?
	jnz	chkde2a			; nz = no, use v3 SCB in Kermit
	push	es
	push	bx
	mov	ax,latscbfree		; free the SCB
	int	latint
	pop	ax			; old ES
	pop	bx
	or	ax,bx			; returned address from ES:BX, null?
	jnz	chkde4			; nz = no, LAT has internal SCBs
chkde2a:mov	latversion,3		; DECnet version 3 style LAT
	jmp	short chkde4
					; now do CTERM too
chkde3:	mov	al,decint		; CTERM interrupt 69h
	mov	ah,35h			; get vector to es:bx
	int	dos
	mov	ax,es
	or	ax,ax			; undefined interrupt?
	jz	chkde4			; z = yes
	cmp	byte ptr es:[bx],0cfh	; points at IRET?
	je	chkde4			; e = yes
	mov	ax,dpresent		; CTERM installation call
	int	decint
	cmp	al,0ffh			; CTERM installed?
	jne	chkde4			; ne = no
	or	nettype,decnet		; kind of network is CTERM
chkde4:	pop	es
	test	nettype,declat+decnet	; any DEC network found?
	jz	chkde5			; z = no
	clc				; clear means yes
	ret
chkde5:	stc				; status is no net
	ret
chkdec	endp

; Check for LAT interrupt vector, return carry clear if found else carry set.
; Sets latkind to DEC_LAT, TES_LAT or MTC_LAT.
chklat	proc	near
	push	es
	mov	al,latint		; LAT interrupt 6Ah
	mov	ah,35h			; get vector to es:bx
	int	dos
	mov	ax,es
	or	ax,ax			; undefined interrupt?
	jz	chklat1			; z = yes
	cmp	byte ptr es:[bx],0cfh	; points at IRET?
	je	chklat1			; e = yes, not installed
	cmp	word ptr es:[bx-3],'AL'	; preceeding 3 bytes spell 'LAT'?
	jne	chklat1			; ne = no, so no LAT, try CTERM
	cmp	byte ptr es:[bx-1],'T'
	jne	chklat1			; ne = no, fail
	or	nettype,declat		; kind of network is LAT
	mov	latkind,TES_LAT		; assume TES LAT
	cmp	word ptr es:[bx-6],'ET'	; Interconnections Inc "TESLAT"?
	je	chklat2			; e = yes
	mov	latkind,MTC_LAT		; Meridian
	cmp	word ptr es:[bx-6],'TM'	; Meridian "MTCLAT"?
	je	chklat2			; e = yes
	mov	latkind,DEC_LAT		; say DEC's LAT
chklat2:pop	es
	clc				; success
	ret
chklat1:pop	es
	stc				; say failure
	ret
chklat	endp

; Start DECNET link. Host name is in latservice, nettype has LAT or CTERM
; kind bits. Return carry clear if success, or carry set if failure.
decstrt	proc	FAR
	cmp	pcnet,2			; net active now?
	jb	decst2			; b = no
	cmp	lathand,0		; invalid LAT handle?
	jne	decst1			; ne = no, have a connection
	cmp	decneth,0		; invalid CTERM handle?
	je	decst2			; e = yes, start the net
decst1:	mov	port_dec.portrdy,1	; say the comms port is ready
	mov	al,'D'			; assume DECnet port flag
	cmp	latkind,DEC_LAT		; DEC_LAT?
	je	decst1b			; e = yes
	mov	al,'I'			; TES-LAT
decst1a:cmp	latkind,TES_LAT
	ja	decst1b			; a = no
	mov	al,'M'			; Meridian SuperLAT
decst1b:mov     flags.comflg,al		; set the comm port flag
	clc
	ret				; return to active session

decst2:	push	es			; used a lot here
	call	chkdec			; get net type
	jnc	decst3			; nc = have a CTERM or LAT kind
	jmp	decst16			; c = net not found

decst3:	cmp	latservice,0		; node name present?
	jne	decst4			; ne = yes
	mov	ah,prstr
	mov	dx,offset noname	; say host name is required
	int	dos
	pop	es
	stc				; fail
	ret

decst4:	test	nettype,declat		; LAT is available?
	jnz	decst6			; nz = yes
	jmp	decst13			; z = no, try CTERM

decst6:	cmp	word ptr latscbptr+2,0	; any segment allocated now?
	jne	decst8			; ne = yes, do not malloc one here
;	cmp	tv_mode,0		; running under Windows or DV?
;	je	decst6a			; e = no, ok to try local SCB
;	jmp	decst12b		; can't use local so no LAT today
decst6a:mov	bx,870+15		; size of LAT SCB, bytes, rounded up
	mov	cl,4
	shr	bx,cl			; bytes to paragraphs
	mov	temp,bx			; save requested paragraphs
	mov	ah,alloc		; allocate memory, ax gets segment
	int	dos			; bx gets # paragraphs allocated
	jnc	decst7			; nc = success
	jmp	decst13			; fail, go try CTERM
decst7:	mov	latseg,ax
	mov	word ptr latscbptr+2,ax	; remember seg of SCB
	mov	word ptr latscbptr+10,ax ; and on the "To:" side as well
	mov	es,ax
	xor	di,di			; es:di is destination
	xor	ax,ax			; get word of zeros
	mov	word ptr latscbptr+0,ax	; remember offset (0) of SCB
	mov	word ptr latscbptr+8,ax	;  as above
	mov	cx,bx			; paragraphs obtained
	shl	cx,1
	shl	cx,1
	shl	cx,1			; words
	cld
	rep	stosw			; clear the SCB
	cmp	temp,bx			; wanted vs allocated (bx) paragraphs
	jbe	decst5			; be = enough, setup structure
	jmp	decst12			; deallocate memory and try CTERM

decst5:	and	nettype,not declat	; presume failure
	cmp	latversion,4		; version 4?
	jb	decst8			; b = no, earlier, do our own SCB
	cmp	latpwd,0		; password given?
	jne	decst8			; ne = yes, must use our SCB
	mov	ax,latscbget		; get LAT interior SCB addr to es:bx
	mov	dh,0ffh
	int	latint
	or	ah,ah			; success?
	jnz	decst5a			; nz = no
	mov	ax,es
	mov	word ptr latscbptr+6,ax	; address of SCB within LAT
	mov	word ptr latscbptr+4,bx ;  offset part
	call	decfems			; copy EMS SCB to local SCB
	jmp	short decst9		; fill in local info

decst5a:mov	ah,prstr
	mov	dx,offset decmsg6	; say can't allocate LAT SCB
	int	dos
	jmp	decst13			; try CTERM

decst8:	les	bx,latscbptr		; allocate data buffers locally
	mov	es:[bx].slottbl,slotbf1	; offset of first buffer
	mov	es:[bx].slottbl+2,slotbf2 ; offset of second buffer
	mov	es:[bx].slotqty,2	; say two buffers

decst9:	les	di,latscbptr		; set es:di to local scb
	mov	si,offset latservice	; get host name
	mov	cx,17			; 17 bytes
	cld
	rep	movsb			; insert host name
	mov	ax,latopen		; open a LAT session
	mov	di,offset latpwd	; es:di optional asciiz LAT password
	cmp	byte ptr [di],0		; any name entered?
	je	decst10			; e = no
	and	al,0fh			; open as AX = 0d0fh if with password
decst10:mov	bx,word ptr latscbptr	; open needs es:bx == SCB
	cmp	latversion,4		; LAT version 4?
	jb	decst11			; b = no, use version 2
	mov	al,1			; v4 form
	push	ax
	mov	cx,ds
	mov	word ptr es:[bx].lat_pwd+2,cx ; set the pointer segment
	mov	word ptr es:[bx].lat_pwd,di  ; address of the password buffer
	mov	dx,di
	call	fstrlen			; password string length to cx
	mov	es:[bx].pwd_len,cl	; length of the password
	or	cx,cx			; is there a password?
	jnz	decst10a		; nz = yes
	mov	word ptr es:[bx].lat_pwd,cx ; no, clear fields
	mov	word ptr es:[bx].lat_pwd+2,cx
	mov	byte ptr es:[bx].pwd_len,cl
decst10a:call	dec2ems			; copy local SCB to one in EMS
	pop	ax

decst11:mov	dh,0ffh
	cmp	latversion,4		; version 4?
	jb	decst11a		; b = no, earlier, use our own SCB
	les	bx,latscbptr+4		; stuff in lat
decst11a:int	latint
	or	ah,ah			; status byte
	jnz	decst12			; nz = failure, clean up, try CTERM
	mov	dh,0ffh
	mov	lathand,dx		; handle returned in dl, 0ffh in dh
	or	nettype,declat		; say LAT session is active
	jmp	decst17			; finish startup info
					; LAT startup failure
decst12:mov	ax,latseg		; stored segment of memory block
	or	ax,ax			; did we use it?
	jz	decst12a		; z = no
	mov	es,ax			; allocated segment, unneed now
	mov	ah,freemem		; free it again
	int	dos
	jmp	short decst12b		; clear pointers
decst12a:cmp	word ptr latscbptr+2,0	; in use as LAT internal perhaps?
	je	decst12b		; e = no
	les	bx,latscbptr
	mov	ax,latscbfree		; free SCB internal to LAT
	int	latint
decst12b:xor	ax,ax
	mov	latseg,ax		; say not used
	mov	lathand,ax		; invalidate the handle
	mov	word ptr latscbptr+0,ax	; clear SCB pointer
	mov	word ptr latscbptr+2,ax
	and	nettype,not declat	; fall through to try CTERM

decst13:test	nettype,decnet		; is CTERM available?
	jz	decst16			; z = no
	and	nettype,not decnet	; presume failure
	mov	ax,decseg		; scb memory segment, if non-zero
	or	ax,ax			; allocated already?
	jnz	decst14			; nz = yes, segment is in ax
	mov	ax,dgetscb		; get CTERM SCB size
	int	decint
	add	ax,15			; round up byte count
	mov	cl,4
	shr	ax,cl			; bytes to paragraphs
	mov	bx,ax
	mov	temp,ax			; save requested paragraphs
	mov	ah,alloc		; allocate memory
	int	dos			; bx gets # paragraphs allocated
	jc	decst16			; c = failure
	mov	decseg,ax		; store address of memory block
	cmp	temp,bx			; wanted vs allocated paragraphs
	jb	decst15			; b = not enough, fail

decst14:mov	bx,offset latservice	; ds:bx = node name
	mov	es,ax			; ax holds scb segment
	xor	dx,dx			; es:dx = SCB address
	mov	ax,dopen		; open session
	int	decint
	cmp	ax,0			; > 0 means session handle, else error
	jle	decst15			; le = error
	mov	decneth,ax		; store handle
	or	nettype,decnet		; network type is DECnet
	jmp	short decst17		; success
					; CTERM startup failure
decst15:push	ax			; save error number in ax
	mov	ax,decseg		; allocated memory segment
	mov	es,ax
	mov	ah,freemem		; free allocated memory segment @ES
	int	dos			; free the block
	mov	decseg,0		; clear remembered segment address
	mov	ah,prstr
	mov	dx,offset decmsg1	; cannot create session
	int	dos
	mov	dx,offset decmsg3	; DEC Error #
	int	dos
	pop	ax			; recover error number (negative)
	neg	ax
	call	fdecout			; error number
	mov	decneth,0		; invalidate the handle
	and	nettype,not decnet

decst16:mov	pcnet,0			; no net
	mov	port_dec.portrdy,0	; port is not ready
	pop	es
	stc				; status is error
	ret
					; LAT or CTERM success
decst17:mov	pcnet,2			; say net is present and active
	mov	al,'D'			; assume DECnet
	cmp	latkind,DEC_LAT		; DECnet?
	je	decst18			; e = yes
	mov	al,'I'			; TES-LAT
	cmp	latkind,TES_LAT		; TES_LAT?
	je	decst18			; e = yes
	mov	al,'M'
decst18:mov     flags.comflg,al		; set the comm port flag
decst19:mov	port_dec.portrdy,1	; say the comms port is ready
	mov	ax,100			; wait 100 ms for DECnet to get ready
	call	fpcwait			; FAR call
	pop	es
	clc
	ret
decstrt	endp

; Copy LAT scb from local SCB to one in EMS
dec2ems	proc	near
	cmp	latversion,4		; version 4?
	jb	dec2emsx		; b = no, earlier, do our own SCB
	push	ax
	push	bx
	push	cx
	push	dx
	push	es
	mov	ax,seg latscbptr	; seg where latscbptr is located
	mov	es,ax			; to es:bx
	mov	bx,offset latscbptr
	mov	ax,latcpyems		; copy from local to EMS
	mov	dh,0ffh			; signature
	mov	cx,31			; bytes needed from LAT structure
	int	latint			; copy local LAT info to EMS version
	pop	es
	pop	dx
	pop	cx
	pop	bx
	pop	ax
dec2emsx:ret
dec2ems	endp

; Copy LAT SCB from EMS to local SCB
decfems	proc	near
	cmp	latversion,4		; version 4?
	jb	decfemsx		; b = no, earlier, do our own SCB
	push	ax
	push	bx
	push	cx
	push	dx
	push	es
	mov	ax,seg latscbptr	; seg where latscbptr is located
	mov	es,ax			; to es:bx
	mov	bx,offset latscbptr+4	;local(0),LAT(4),local(8) in latscbptr
	mov	ax,latcpyems		; copy from EMS to local
	mov	dh,0ffh			; signature
	mov	cx,31			; just enough of LAT structure
	int	latint
	pop	es
	pop	dx
	pop	cx
	pop	bx
	pop	ax
decfemsx:ret
decfems	endp
endif	; no_network
code1	ends

code	segment
	assume	cs:code
ifndef	no_network
; Display list of LAT service names. Presumes LAT presence checks have passed
latlst	proc	near
	push	es
	push	bx
	mov	ah,prstr
	mov	dx,offset dnetsrv	; header
	int	dos
	push	ds
	pop	es
	mov	si,2			; chars in line counter
latlst1:mov	bx,offset decbuf+2 	; es:bx = temp buffer for a name
	mov	word ptr [bx-2],'  '	; indent
	mov	byte ptr [bx],0		; and a null terminator
	mov	ax,latsrv		; get next LAT service name
	mov	dh,0ffh
	int	latint
	or	ah,ah			; check status
	jnz	latlst2			; nz = done (no more names)
	mov	dx,offset decbuf	; name ptr is in es:bx (our buffer)
	call	prtasz			; show asciiz name
	call	strlen			; get current length
	add	si,cx			; count chars on this line
	cmp	si,60			; enough on line already?
	jbe	latlst1			; be = no
	mov	ah,prstr		; break the screen line
	mov	dx,offset crlf
	int	dos
	mov	si,2			; reset line count
	jmp	short latlst1		; do it again
latlst2:pop	bx
	pop	es
	ret
latlst	endp
endif	; no_network

; Check which Interrupt ReQuest line the port uses. Technique: allow interrupt
; on transmitter holding register empty, test for that condition first with
; IRQ 4 and then IRQ 3. Returns with IRQ values set and carry clear if success
; or carry set if failure. [jrd]
chkint	proc	near
	call	serrst
	mov	bl,flags.comflg		; port 1..4
	dec	bl
	xor	bh,bh
	mov	al,portirq[bx]		; pre-specified IRQ, if any
	or	al,al			; IRQ specified already?
	jnz	chkint20		; nz = yes
chkint10:test	flags.comflg,1		; COM1/3?
	jz	chkint13		; z = no, COM2/4
	cmp	flags.comflg,1		; COM1?
	je	chkint10a		; e = yes, try IRQ 4
	cmp	isps2,0			; IBM PS/2 Model 50 or above?
	jne	chkint11		; ne = yes, other COM2..4 try IRQ 3
chkint10a:call	chkint5			; test for IRQ 4
	jc	chkint11		; c = failed
	ret
chkint11:call	chkint6			; else try IRQ 3
	jnc	chkint12		; nc = success
	jmp	chkint7			; fall back on defaults
chkint12:ret				; carry clear for success

chkint13:call	chkint6			; test for IRQ 3
	jc	chkint14		; c = failed
	ret
chkint14:call	chkint5			; else try IRQ 4
	jnc	chkint15		; nc = success
	jmp	chkint7			; fall back on defaults
chkint15:ret				; carry clear for success

					; IRQ specified, in AL
chkint20:mov	di,sp			; do push sp test for XT vs 286 class
	push	sp			; XT pushes sp-2, 286's push old sp
	pop	cx			; recover pushed value, clean stack
	sub	di,cx			; non-zero if < 80286, no slave 8259
	cmp	al,2			; using IRQ 2?
	jne	chkint1			; ne = no
	or	di,di			; cascaded 8259?
	jnz	chkint1			; nz = no
	add	al,7			; map IRQ 2 to IRQ 9
chkint1:cmp	al,15			; larger than legal IRQ?
	ja	chkint2			; a = yes, fail
	or	di,di			; 286 or above (cascaded 8259)?
	jz	chkint3			; z = yes
	cmp	al,7			; larger than legal for single 8259?
	jbe	chkint3			; be = no
chkint2:stc				; fail
	ret
chkint3:mov	cl,al			; IRQ 0..15
	mov	bx,1
	shl	bx,cl			; bit position of IRQ 0..15
	or	bl,bh			; copy bit to bl
	mov	modem.mddis,bl		; mask to disable IRQ
	not	bl			; 0 means enable
	mov	modem.mden,bl		; mask to enable IRQ
	and	al,7			; IRQ, lower three bits
	mov	ah,al			; make a copy
	add	ah,60h			; specific EOI control code
	mov	modem.mdmeoi,ah		; specific EOI control command
	add	al,8			; IRQ 0 starts at Int 8
	xor	ah,ah
	mov	modem.mdintv,ax		; Interrupt number
	mov	modem.mdmintc,20h	; master 8259 control address
	or	bh,bh			; on cascaded 8259?
	jz	chkint4			; z = no
	or	modem.mdmintc,80h	; slave 8259 control address (0a0h)
	add	modem.mdintv,70h-8	; Interrupt number for IRQ 8..15
chkint4:clc
	ret

					; find IRQ 4 by usage test
chkint5:mov	modem.mddis,(1 shl 4)	; IRQ 4 test. mask to disable IRQ 4
	mov	modem.mden,not (1 shl 4); mask to enable IRQ 4
	mov	modem.mdmeoi,20h	; use general in case we guess wrong
	mov	modem.mdintv,8+4	; IRQ 4 interrupt vector (0ch)
	mov	modem.mdmintc,20h	; use master 8259 here
	call	inttest
	jc	chkint5a		; c = failed
	mov	modem.mdmeoi,60h+4	; use specific EOI for IRQ4 level
	mov	bl,flags.comflg		; port 1..4
	dec	bl
	xor	bh,bh
	mov	portirq[bx],4
	clc				; this setup worked
chkint5a:ret
					; IRQ 3 test
chkint6:mov	modem.mddis,(1 shl 3)	; mask to disable IRQ 3
	mov	modem.mden,not (1 shl 3); mask to enable IRQ 3
	mov	modem.mdmeoi,20h	; use general in case we guess wrong
	mov	modem.mdintv,8+3	; IRQ 3 interrupt vector
	call	inttest
	jc	chkint6a		; c = failed
	mov	modem.mdmeoi,60h+3	; use specific EOI for IRQ3 level
	mov	bl,flags.comflg		; port 1..4
	dec	bl
	xor	bh,bh
	mov	portirq[bx],3
	clc				; this setup worked
chkint6a:ret

					; auto test did not work
chkint7:mov	modem.mdmintc,20h	; use master 8259 Int controller
	cmp	flags.comflg,1		; COM1?
	je	chkint8			; e = yes, use IRQ 4
	cmp	isps2,0			; IBM PS/2 Model 50 or above?
	jne	chkint9			; ne = yes, other COMs use IRQ 3
	cmp	flags.comflg,3		; COM2, COM3, or COM4?
	jne	short chkint9		; ne = COM2 or COM4, use IRQ 3
chkint8:mov	modem.mdmeoi,60h+4	; use specific EOI for IRQ4 level
	mov	modem.mddis,(1 shl 4)	; IRQ 4 test. mask to disable IRQ 4
	mov	modem.mden,not (1 shl 4); mask to enable IRQ 4
	mov	modem.mdintv,8+4	; IRQ 4 interrupt vector (0ch)
	mov	bl,flags.comflg		; port 1..4
	dec	bl
	xor	bh,bh
	mov	ax,4			; IRQ
	mov	portirq[bx],al
	jmp	short chkint9a		; show message

chkint9:mov	modem.mdmeoi,60h+3	; use specific EOI for IRQ 3 level
	mov	modem.mddis,(1 shl 3)	; mask to disable IRQ 3
	mov	modem.mden,not (1 shl 3); mask to enable IRQ 3
	mov	modem.mdintv,8+3	; IRQ 3 interrupt vector
	mov	bl,flags.comflg		; port 1..4
	dec	bl
	xor	bh,bh
	mov	ax,3			; IRQ
	mov	portirq[bx],al
chkint9a:push	ax
	mov	ah,prstr
	mov	dx,offset badirq	; say assuming an IRQ
	int	dos
	pop	ax
	mov	cx,10			; decimal
	call	valout			; show IRQ
	clc
	ret

inttest:call	serini			; setup port for given IRQ
	jc	inttes2			; c = failure
	mov	dx,modem.mddat
	inc	dx			; interrupt enable reg (3f9h)
	cli
	mov	intkind,0		; clear interrupt cause
	mov	al,2			; set xmtr holding reg empty interrupt
	out	dx,al
	call	delay
	mov	al,2			; set xmtr holding reg empty interrupt
	out	dx,al			; again, because first may be missed
	sti
	call	delay			; wait one millisec for interrupt
	mov	al,intkind		; interrupt kind
	push	ax
	call	serrst			; reset port
	pop	ax
	test	al,2			; check cause of interrupt, ours?
	jz	inttes2			; z = no, test failed
	clc				; this setup worked
	ret
inttes2:stc				; failure
	ret
chkint	endp

; Test presently selected serial port for having a real 8250 UART.
; Return carry clear if 8250 present,
;  else carry set and flags.comflg in ascii digits for system Bios or
;  carry set for network.
; Method is to check UART's Interrupt Identification Register for high
; five bits being zero; IBM does it this way. Assumes port structure
; has been initialized with addresses of UART.	21 Feb 1987 [jrd]
; 29 May 1987 Add double check by reading Line Status Register. [jrd]

chkport	proc	near
	cmp	flags.comflg,4		; non-UART port?
	ja	chkporx			; a = yes
	cmp	flags.comflg,0		; undefined port?
	je	chkporx			; e = yes
	push	ax
	push	dx
	mov	dx,modem.mdiir		; UART Interrupt Ident reg (3FAh/2FAh)
	in	al,dx			; read UART's IIR
	test	al,30h			; are these bits set?
	jnz	chkpor1			; nz = yes, not an 8250/16450/16550A
	mov	dx,modem.mdstat		; line status register
	in	al,dx		     ; read to clear UART BI, FE, PE, OE bits
	call	delay
	in	al,dx			; these bits should be cleared
	test	al,8eh			; are they cleared?
	jnz	chkpor1			; nz = no, not an 8250/16450/16550A
	pop	dx
	pop	ax
	clc				; clear carry (say 8250/etc)
	ret
chkpor1:pop	dx
	pop	ax
	add	flags.comflg,'0'	; set Bios usage flag (ascii digit)
chkporx:stc				; set carry (say no 8250/etc)
	ret
chkport	endp

; Test serial hardware port given in BL (1..4). Return carry set if fail.
tstport	proc	near
	push	bx
	push	es
	mov	ax,40h		; look at RS232_base [bx] in Bios area 40:00h
	mov	es,ax
	xor	bh,bh
	dec	bl			; count com1 as bl = 0, etc
	shl	bx,1			; make bx a word index
	mov	ax,es:[bx]		; get modem base address into ax
	pop	es
	or	ax,ax			; is address zero?
	jnz	tstport1		; nz = no, have port address
	mov	ax,defcom[bx]		; get default COMn port address
tstport1:				; hardware tests
	push	modem.mdiir		; save these values
	push	modem.mdstat
	add	ax,2
	mov	modem.mdiir,ax		; interrupt identification reg 03fah
	add	ax,3			; increment to status port 03fdh
	mov	modem.mdstat,ax		; set line-status port address
	call	chkport			; get type of UART support
	pop	modem.mdstat
	pop	modem.mdiir
	pop	bx
	ret				; return with carry flag from chkport
tstport	endp

ifndef	no_network
; Check for presence of IBM EBIOS
; Returns carry clear if EBIOS present, else carry set
chkebios proc	near
	mov	dx,ebport		; port 0..3
	mov	ax,ebpresent*256+0ffh	; IBM EBIOS presence check
	int	rs232
	jc	chkebios1		; c = failure
	or	ax,ax			; returns ax = 0 if present
	jnz	chkebios1		; nz = not present
	clc
	ret
chkebios1:stc				; IBM EBIOS not present
	ret
chkebios endp
endif	; no_network

; 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
	call	dobaud			; use common code
	clc
baudst1:ret
BAUDST	ENDP

DOBAUD	PROC	NEAR
	cmp	portin,-1		; port used yet?
	jne	dobd3			; ne = yes, go get rate
	push	bx			; save rate index
	mov	bl,flags.comflg		; pass current port ident
	call	comstrt			; do SET PORT command now
	pop	bx
	jnc	dobd3			; nc = success
dobd4:	stc
	ret				; failure
dobd3:	push	bx			; save baud rate index
	mov	al,flags.comflg		; comms port
	cmp	al,'E'			; EBIOS?
	je	dobd5			; e = yes
	cmp	al,'4'			; UART or Bios?
	ja	dobd1			; a = no, networks
	call	chkport			; check port for real 8250 UART
dobd5:	pop	bx			; baud rate index
	push	bx			; check if new rate is valid
	mov	ax,bx
	shl	ax,1			; make a word index
	mov	bx,offset bddat		; start of table
	cmp	flags.comflg,'0'	; Bios?
	jb	dobd0a			; b = no, UART
	mov	bx,offset clbddat	; use Bios speed parameters
dobd0a:	add	bx,ax
	mov	ax,[bx]			; data to output to port
	cmp	al,0FFh			; unimplemented baud rate?
	jne	dobd0			; ne = no
	mov	ah,prstr
	mov	dx,offset badbd		; give an error message
	int	dos
	jmp	dobd1

dobd0:	pop	bx			; get baud rate index
	push	bx
	mov	si,portval
	mov	[si].baud,bx		; set the baud rate index
	mov	dl,flags.comflg		; get coms port (1..4, letters)
	cmp	dl,4			; running on a real uart?
	jbe	dobd2			; be = yes, the real thing
	cmp	dl,'E'			; EBIOS?
	je	dobd6			; e = yes
	cmp	dl,'4'			; Bios?
	ja	dobd1			; a = no, network
	or	dl,dl			; zero (undefined port)?
	jz	dobd1			; z = yes, just exit
	and	dl,7			; use lower three bits
	dec	dl			; count ports as 0..3 for Bios
	jmp	short dobd7
dobd6:
ifndef	no_network
	test	nettype,acsi		; using EBIOS.COM?
	jnz	dobd8			; nz = not using it
	mov	dx,ebport		; get EBIOS port (0..3)
endif	; no_network
dobd7:	xor	dh,dh
	xor	ah,ah			; set serial port
	int	rs232			; Bios: set the parameters
     	jmp	short dobd1		; and exit

ifndef	no_network
dobd8:	push	bx			; ACSI
	call	send			; send current buffer first
	pop	bx
	shl	bx,1			; index words
	mov	ax,clbddat[bx]		; Bios speed settings
	mov	word ptr xmtbufx,4	; four bytes in packet
	mov	ah,acsetmode		; ACSI set mode
	mov	word ptr xmtbufx+2,ax	; Mode setting
	call	send			; send speed update to host
	jmp	short dobd1		; exit
endif	; no_network

dobd2:	pushf
	cli				; interrupts off
	push	ax			; UART, remember value to output
	mov	dx,modem.mdcom		; LCR -- Initialize baud rate
	in	al,dx			; get it
	mov	bl,al			; make a copy
	or	ax,80H		; turn on DLAB bit to access divisor part
	out	dx,al
	mov	dx,modem.mddat
	pop	ax			; set the baud rate divisor
	out	dx,al
	inc	dx			; next address for high part
	mov	al,ah			; set high part of divisor
	out	dx,al
	mov	dx,modem.mdcom		; LCR again
	mov	al,bl			; get original setting from bl
	out	dx,al			; restore it
	popf				; restore interrupt state
dobd1:	pop	bx			; restore regs
	clc
	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
	push	bx
	mov	bx,portval
	mov	al,flags.comflg
	cmp	al,4			; UART?
	ja	getb3			; a = no, Bios or Networks
	cmp	portin,-1		; port unused?
	jne	getbud			; ne = no, used, go get rate
	mov	bl,al			; pass current port ident
	call	comstrt			; do SET PORT command now
	jnc	getbud			; nc = success
getb3:	pop	bx
	pop	ax
	ret				; failure
getbud:	push	cx			; save some regs
	push	dx
	pushf
	cli				; interrupts off
	mov	dx,modem.mdcom	     ; get current Line Control Register value
	in	al,dx
	mov	bl,al			; save it
	or	ax,80H		      ; turn on to access baud rate generator
	out	dx,al
	mov	dx,modem.mddat		; Divisor latch
	inc	dx
	in	al,dx			; get high order byte
	mov	ah,al			; save here
	dec	dx
	in	al,dx			; get low order byte
	push	ax	
	mov	dx,modem.mdcom		; Line Control Register
	mov	al,bl			; restore old value
	out	dx,al
	pop	ax
	popf				; ints back to normal
	cmp	ax,0FFFFH		; if no port
	je	getb2			; e = no port, bus noise
	mov	bx,offset bddat		; find rate's offset into table
	xor	cl,cl			; keep track of index
getb0:	cmp	ax,[bx]			; observed vs table divisor
	je	getb1			; e = found a match
	inc	cl			; next table index
	cmp	cl,baudlen		; at the end of the list?
	jge	getb2			; ge = yes, quit
	add	bx,2			; next table entry
	jmp	short getb0
getb1:	xor	ch,ch
	mov	bx,portval
	mov	[bx].baud,cx		; set baud rate
getb2:	pop	dx			; restore regs
	pop	cx
	pop	bx
	pop	ax
	clc
	ret
GETBAUD	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.
; Tells a fib about SOP present if using SET CARRIER on serial links
; and cardet says loss of carrier.
peekcom proc far
	cmp	flags.comflg,'F'	; Fossil?
	je	peekc8			; e = yes
	cmp	flags.comflg,4		; UART?
	ja	peekc9			; a = no
peekc8:	cmp	flags.carrier,0		; worry about Carrier Detect?
	je	peekc9			; e = no
	cmp	dupflg,0		; full duplex?
	jne	peekc9			; e = no, half
	test	cardet,1		; 1 = "CD was on, is now off"
	jz	peekc9			; z = no
	mov	cx,6			; lie that enough bytes to be read
	clc				; say enough for SOP
	ret				; so cardet is passed upward now

peekc9:	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:	push	bx			; read from port, add to buffer
	mov	bx,portval
	push	si			; used by packet routines
	call	[bx].rcvproc		; read routine
	pop	si
	jc	peekc7			; c = nothing available
	cmp	count,0			; something put in buffer?
	jne	peekc7			; ne = yes, go read from buffer
	mov	bx,srcpnt		; where next read would be
	inc	bx			; add to buffer
	cmp	bx,offset source+bufsiz	; pointing beyond end of buffer?
	jb	peekc5			; b = no
	mov	bx,offset source	; wrap pointer, modulo bufsiz
peekc5:	cli
	mov	[bx],al			; store byte
	mov	srcpnt,bx		; update pointer to next free slot
	inc	count
	cmp	count,bufsiz		; count more that buffer size?
	jbe	peekc6			; be = no
	mov	count,bufsiz		; limit to bufsiz (tells the truth)
peekc6:	sti
peekc7:	pop	bx
	cmp	count,6			; anything now?
	jae	peekcom			; ne = yes, repeat examination
	xor	cx,cx			; return count of zero
	stc				; say no data
	ret
peekcom endp

; Get Char from	serial port buffer.
; returns carry set if no character available at port, otherwise
; returns carry clear with char in al, # of chars in buffer in dx.
prtchr  proc	near
	cmp	holdscr,0		; Holdscreen in effect?
	jne	prtch3			; ne = yes, do not read
	call	chkxon			; see if we need to send XON
	cmp	repflg,0		; REPLAY?
	je	prtch1			; e = no
	jmp	prtchrpl		; yes, do replay file reading
prtch1:	cmp	count,0			; is buffer empty?
	jne	prtch4			; ne = no, read from it
	push	bx
	mov	bx,portval
	call	[bx].rcvproc		; read routine
	pop	bx			; fall through to grab new char
	jc	prtch3			; c = nothing available
	cmp	count,0			; something put in buffer?
	jne	prtch4			; ne = yes, go read from buffer
prtch2:	inc	dx
	clc				; return single char already in AL
	ret
prtch3:	xor	dx,dx			; return count of zero
	stc				; say no data
	ret

prtch4:	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	prtch5			; ae = no
	add	si,bufsiz		; else do arithmetic modulo bufsiz
prtch5:	lodsb				; 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
	clc
	ret
prtchr  endp

uartrcv	proc	near			; UART receive
	cmp	flags.carrier,0		; worry about Carrier Detect?
	je	uartrcv1		; e = no
	cmp	dupflg,0		; full duplex?
	jne	uartrcv1		; e = no, half
	push	dx
	mov	dx,modem.mddat
	add	dx,6			; modem status reg 3feh
	in	al,dx			; 03feh, modem status reg
	pop	dx
	and	al,80h			; get CD bit
	jnz	uartrcv2		; nz = CD is on now
	test	cardet,80h		; previous CD state
	jz	uartrcv2		; z = was off
	mov	al,01h			; say was ON but is now OFF
uartrcv2:mov	cardet,al		; preserve as global
	cmp	al,1			; carrier dropped?
	jne	uartrcv1		; ne = no
	push	bx
	mov	bx,portval
	mov	[bx].portrdy,0		; say port is not ready
	pop	bx
	mov	kbdflg,'C'		; exit Connect mode
uartrcv1:stc				; say not returning char in al here
	ret				; interrupt driven so no work here
uartrcv	endp

biosrcv	proc	near			; Bios calls
	xor	dh,dh			; assume port 1, find current port
	mov	dl,flags.comflg		; get port number (1..4)
	or	dl,dl			; zero (no such port)?
	jz	biosrc1			; z = yes, don't access it
	and	dl,7			; use low three bits
	dec	dl			; address ports as 0..3 for Bios
	mov	ah,3			; check port status, std Bios calls
	int	rs232			; Bios call
	cmp	flags.carrier,0		; worry about Carrier Detect?
	je	biosrc4			; e = no
	and	al,80h			; get CD bit
	jnz	biosrc3			; nz = CD is on now
	test	cardet,80h		; previous CD state
	jz	biosrc4			; z = was off
	mov	al,01h			; say was ON but is now OFF
biosrc3:mov	cardet,al		; preserve as global

biosrc4:test	ah,mdminp		; data ready?
	jnz	biosrc2			; nz = yes, get one
biosrc1:cmp	flags.carrier,0		; worry about Carrier Detect?
	je	biosrc5			; e = no
	cmp	dupflg,0		; full duplex?
	jne	biosrc4			; e = no, half
	test	cardet,1		; 1 = "CD was on, is now off"
	jz	biosrc5			; z = no
	push	bx
	mov	bx,portval
	mov	[bx].portrdy,0
	pop	bx
	mov	kbdflg,'C'		; exit Connect mode
biosrc5:stc				; say not returning char in al here
	ret
biosrc2:mov	ah,2			; receive a char into al
	int	rs232			; Bios call
	test	ah,8ch			; timeout, framing error, parity error?
	jnz	biosrc1			; nz = error, no char
	jmp	short schrcv		; single char read post processor
biosrcv	endp

schrcv	proc	near			; single char read final filter	
	test	flowcnt,2		; using input XON/XOFF flow control?
	jz	schrcv3			; z = no
	mov	ah,al			; get copy of character
	and	ah,parmsk		; strip parity, if any, before testing
	cmp	ah,flowoff		; acting on XOFF?
	jne	schrcv2			; ne = no, go on
	cmp	xofsnt,0		; have we sent an outstanding XOFF?
	jne	schrcv1			; ne = yes, ignore (possible echo)
	mov	xofrcv,bufon		; set the flag saying XOFF received
schrcv1:stc				; say not returning char in al here
	ret
schrcv2:cmp	ah,flowon		; acting on XON?
	jne	schrcv3			; ne = no, go on
	mov	xofrcv,off		; clear the XOFF received flag
	xor	dx,dx
	stc
	ret				; no data to return
schrcv3:xor	ah,ah
	clc				; return char in al
	ret				; expect count=0
schrcv	endp

; Fossil block receive
fosrcv	proc	near
	mov	dx,fossil_port		; get port number (1..4)
	mov	ah,fossil_portst	; check port status, std Bios calls
	int	rs232			; Bios call
	cmp	flags.carrier,0		; care abour carrier?
	je	fosrcv4			; e = no
	and	al,80h			; get CD bit
	jnz	fosrcv3			; nz = CD is on now
	test	cardet,80h		; previous CD state
	jz	fosrcv4			; z = was off
	mov	al,01h			; say was ON but is now OFF
fosrcv3:mov	cardet,al		; preserve as global
fosrcv4:test	ah,mdminp		; data ready?
	jnz	fosrcv2			; nz = yes, get one
fosrcv1:cmp	flags.carrier,0		; worry about Carrier Detect?
	je	fosrcv5			; e = no
	cmp	dupflg,0		; full duplex?
	jne	fosrcv4			; e = no, half
	test	cardet,1		; 1 = "CD was on, is now off"
	jz	fosrcv5			; z = no
fosrcv6:call	serrst			; close port
	mov	kbdflg,'C'		; exit Connect mode
fosrcv5:stc				; say not returning char in al here
	ret
fosrcv2:push	es
	mov	ax,seg rcvbuf
	mov	es,ax
	mov	cx,nbuflen		; buffer length
	mov	di,offset rcvbuf
	mov	ah,fossil_blkrd		; receive block
	xor	al,al
	int	rs232			; Bios call
	pop	es
	or	ax,ax			; count received
	jz	fosrcv1			; z = no char
	cmp	ax,0ffffh		; error condition?
	je	fosrcv6			; e = yes, shut down port
	cmp	ax,fossil_blkrd*256	; complete no-op?
	je	fosrcv6			; e = yes
	mov	rcv.scb_length,ax	; count received
	mov	rcv.scb_baddr,di	; offset of receive buffer
	jmp	blkrcv			; block read post processor
fosrcv	endp

ifdef	no_terminal
					; REPLAY, read char from a file
prtchrpl proc	near
	stc
	ret
prtchrpl endp
endif	; no_terminal
ifndef	no_terminal
prtchrpl proc	near
	cmp	repflg,2		; at EOF already?
	jne	prtchrp1		; ne = no
	stc				; yes, return with no char
	ret
prtchrp1:push	bx
	push	cx
	xor	dx,dx
	test	xofsnt,usron		; user level xoff sent?
	jz	prtchrp2		; z = no
	pop	cx			; suppress reading here
	pop	bx
	stc				; say not returning char in al here
	ret
prtchrp2:test	tekflg,tek_active	; doing graphics mode?
	jnz	prtchrp3		; nz = yes, do not insert pauses
	in	al,ppi_port		; delay
	in	al,ppi_port
	in	al,ppi_port		; burn some bus cycles
	in	al,ppi_port		; because a 1 ms wait is too long
	in	al,ppi_port
prtchrp3:mov	ah,readf2
	mov	bx,diskio.handle	; file handle
	mov	cx,1			; read one char
	mov	dx,offset decbuf	; to this buffer
	int	dos
	jc	prtchrp4		; c = read failure
	cmp	ax,cx			; read the byte?
	jne	prtchrp4		; ne = no
	pop	cx
	pop	bx
	mov	al,decbuf		; get the char into al
	clc
	ret				; return it
prtchrp4:call	beep
	mov	ax,40			; wait 40 millisec
	call	pcwait
	call	beep
	mov	repflg,2		; say at EOF
	pop	cx
	pop	bx
	stc				; say not returning char in al here
	ret
prtchrpl endp
endif	; no_terminal

ifndef	no_network
ebircv	proc	near			; EBIOS calls
	mov	dx,ebport		; port 0..3
	mov	ah,ebrcv		; IBM EBIOS receive /no wait
	int	rs232			; does line check then char ready chk
	test	ah,8ch			; timeout, framing error, parity error?
	jnz	ebircv2			; nz = error, no char
	jmp	schrcv			; z = success, process char in AL
ebircv2:stc				; say not returning char in al here
	ret
ebircv	endp

; DECnet receive routine
DECRCV	PROC	NEAR
	test	nettype,declat		; LAT interface?
	jz	decrcv1			; z = not LAT
	mov	dx,lathand
	or	dx,dx			; invalid handle?
	jz	decrcv1			; z = yes, try CTERM

	mov	cx,bufsiz		; final receiver buffer length
	sub	cx,count		; minus occupancy
	cmp	cx,254			; too big for broken DEC LAT?
	jbe	decrcv5			; be = no
	mov	cx,254			; limit to byte sized length
decrcv5:push	es
	mov	ax,seg rcvbuf
	mov	es,ax			; es:bx will point to rcvbuf
	mov	bx,offset rcvbuf
	mov	ah,latreadb		; read a block to es:bx, max len cx
	mov	dx,lathand
	int	latint
	pop	es
	test	ah,80h			; ah is status
	jnz	decrcv6			; nz = error of some kind
	jcxz	decrcv6			; z = no received bytes, do status
	mov	rcv.scb_length,cx	; bytes received, setup blkrcv
	call	blkrcv			; process buffer
	stc				; say not returning char in al here
	ret

; non-SCB status check, slower and much safer
decrcv6:mov	ah,latstat		; get status
	mov	dx,lathand
	int	latint
	test	ah,4			; session not active?
	jnz	decrcv3			; nz = yes, no valid session
	stc				; say not returning char in al here
	ret
; end alterntive code
;	push	es
;	push 	bx
;	les	bx,latscbptr
;	test	es:[bx].sstatus,18h	; status: stop slot or circuit failure
;	pop	bx
;	pop	es
;	jnz	decrcv4			; nz = yes, no valid session
;	stc				; return no char
;	ret

decrcv1:test	nettype,decnet		; is CTERM active?
	jz	decrcv3			; z = no
	mov	dx,decneth		; handle
	or	dx,dx			; legal?
	jz	decrcv3			; z = no
	mov	ax,dcstat		; CTERM status
	int	decint
	test	ah,0c0h			; no session, DECnet error?
	jnz	decrcv3			; nz = yes, stop here
;	test	ah,1			; data available?
;	jz	decrcv4			; z = no
; data available test fails under flow control, maybe a Cterm bug.
	mov	ax,dread		; read char via CTERM
	int	decint
	test	ah,80h			; char received?
	jnz	decrcv4			; nz = no
decrcv2:jmp	schrcv			; use common completion code

decrcv3:call	decclose		; close connection
	test	flags.remflg,dserver	; server mode?
	jz	decrcv4			; z = no
	call	serini			; reinitialize it for new session
decrcv4:stc				; say not returning char in al here
	ret
DECRCV	ENDP

; NetBios Receive packet routine. If a new packet has been received unpack
; the data and request a new one with no-wait option. If a receive request is
; still outstanding just return with no new data.
; Return carry clear if success. If failure, reset serial port (Server mode
; reinits serial port). Return carry set. No entry setup needed.
RECEIVE PROC	NEAR			; receive network session pkt
	cmp	pcnet,1			; net ready yet?
	jbe	receiv3			; be = no, declare a broken session
	cmp	rposted,1		; is a request outstanding now?
	je	receiv4			; e = yes (1), don't do another
	jb	receiv1			; b = no (0), do one now
	call	receiv2			; have new pkt, unpack, do new recv
	jnc	receiv1			; nc = success
	ret				; else return carry set

receiv1:mov	rposted,1		; say posting a receive now
	mov	rcv.scb_length,nbuflen	; length of input buffer  
	mov	rcv.scb_cmd,nreceive+nowait   ; receive, no wait
	push	bx
	mov	bx,offset rcv		; setup pointer to scb
	call	nbsession
	pop	bx
	stc				; set carry to say no char yet
	ret				

receiv2:mov	al,rcv.scb_err		; returned status
	or	al,al			; success?
	jz	receiv5			; z = yes, get the data
	cmp	al,npending		; pending receive?
	je	receiv4			; e = yes
	cmp	al,6			; message incomplete?
	je	receiv5			; e = yes, get what we have anyway
	cmp	al,0bh			; receive cancelled?
	je	receiv3			; e = yes
	cmp	al,18h			; session ended abnormally?
	jbe	receiv3			; e = yes, b = other normal errors
	mov	ah,prstr
	mov	dx,offset recmsg	; give error message
	int	dos
	mov	al,rcv.scb_err		; get error code
	xor	ah,ah
	call	decout			; show error code
					; Error return
receiv3:mov	pcnet,1			; say session is broken
	call	serrst			; close the connection
	cmp	lposted,1		; Listen posted?
	je	receiv3a		; e = yes, stay alive
	cmp	xmt.scb_rname,'*'	; behaving as a Listner?
	je	receiv3a		; e = yes, stay alive
	test	flags.remflg,dserver	; server mode?
	jz	receiv4			; z = no
receiv3a:call	serini			; reinitialize it for new session
receiv4:stc				; say not returning char in al here
	ret
receiv5:mov	rposted,0		; clear interlock flag
	test	nettype,acsi		; ACSI?
	jnz	receiv6			; nz = yes
	jmp	blkrcv			; process block of data
receiv6:jmp	acsircv			; process special format block data
RECEIVE	ENDP
endif	; no_network

; Block receive transfer and flow control scan routine
; Enter with DS:rcv.scb_baddr pointing at source buffer (rcvbuf),
; and rcv.scb_length holding incoming byte count.
; Destroys reg BX
; Shared by NetBios, Novell, Opennet, Ungerman Bass, 3ComBAPI, TCP/IP, etc
blkrcv	proc	near
	push	cx			; new packet has been received
	push	dx			; copy contents to circ buf source
	push	si
	mov	dh,flowon
	mov	dl,flowoff
	mov	si,rcv.scb_baddr	; source of text
	mov	bx,srcpnt		; address of destination buffer slot
blkrcv6:mov	cx,rcv.scb_length	; get remaining returned byte count
	jcxz	blkrcv13		; z = nothing there
	mov	ax,offset source+bufsiz ; end of destination buffer+1
	sub	ax,bx			; space remaining at end of buffer
	jns	blkrcv7			; should never be negative
	neg	ax			; but if so invert
blkrcv7:cmp	ax,cx			; buffer ending vs incoming byte count
	jge	blkrcv8			; ge = enough for this pass
	mov	cx,ax			; limit this pass to end of the buffer
blkrcv8:sub	rcv.scb_length,cx	; deduct chars done in this pass
	add	count,cx		; add them to the count
	cld				; inner loop "block" transfer
	test	flowcnt,2		; doing input XON/XOFF flow control?
	jz	blkrcv20		; z = no
blkrcv9:lodsb				; get byte from rcvbuf to al
	mov	ah,al			; get copy of character
	and	ah,parmsk		; strip parity, if any, before testing
	cmp	ah,dl			; acting on Xoff?
	jne	blkrcv10		; ne = no
	cmp	xofsnt,0		; have we sent an XOFF?
	jne	blkrcv12		; ne = yes, ignore this XOFF char
	mov	xofrcv,bufon		; set flag saying buffer XOFF received
	dec	count			; uncount flow control
	jmp	short blkrcv12		;  and skip this character
blkrcv10:cmp	ah,dh			; acting on XON?
	jne	blkrcv11		; ne = no, go on
	mov	xofrcv,off		; clear the XOFF received flag
	dec	count			; uncount flow control
	jmp	short blkrcv12		;  and skip this character
blkrcv11:mov	[bx],al			; store new char in buffer "source"
	inc	bx
blkrcv12:loop	blkrcv9			; bottom of inner loop
	jmp	short blkrcv22

blkrcv20:push	es			; no flow control, just do copy
	push	di
	mov	ax,ds
	mov	es,ax
	mov	di,bx			; destination
	shr	cx,1			; prep for word moves
	jnc	blkrcv21		; nc = even number of bytes
	movsb
blkrcv21:rep	movsw			; do quick copy
	mov	bx,di			; update destination pointer
	pop	di
	pop	es
					; update buffer pointer for wrapping
blkrcv22:cmp	bx,offset source+bufsiz	; pointing beyond end of buffer?
	jb	blkrcv6			; b = no, do next pass
	mov	bx,offset source	; wrap pointer, modulo bufsiz
	jmp	short blkrcv6		; do next pass

blkrcv13:mov	srcpnt,bx		; update pointer to next free slot
	cmp	count,bufsiz		; count more that buffer size?
	jbe	blkrcv14		; be = no
	mov	count,bufsiz		; limit to bufsiz (tells the truth)
blkrcv14:pop	si
	pop	dx
	pop	cx
	stc				; say no char in al from us
	ret
blkrcv	endp

ifndef	no_network
; ACSI block receive transfer and flow control scan routine
; Enter with DS:rcv.scb_baddr pointing at source buffer (rcvbuf),
; and rcv.scb_length holding incoming byte count.
; Destroys regs AX and BX.
; Format of packet: int count of entire block, then [char, status] byte pairs
acsircv	proc	near
	push	cx			; new packet has been received
	push	dx			; copy contents to circ buf source
	push	si
	mov	dh,flowon
	mov	dl,flowoff
	mov	bx,srcpnt		; address of destination buffer slot
	mov	si,rcv.scb_baddr	; source of text
	cld
	lodsw				; get internal length word
	mov	cx,rcv.scb_length	; get block length
	mov	rcv.scb_length,0	; clear counter
	sub	cx,2			; data bytes remaining in block
	cmp	ax,cx			; internal vs external count
	jae	acsrcv1			; ae=internal is gt or equal to ext
	mov	cx,ax			; use shorter internal count
acsrcv1:shr	cx,1			; count words in block
	jcxz	acsrcv14		; z = nothing there

acsrcv9:lodsw				; get char+status bytes from rcvbuf
	test	ah,80h			; status, is it a command?
	jnz	acsrcv12		; nz = yes, ignore the pair
	test	flowcnt,2		; using input XON/XOFF flow control?
	jz	acsrcv11		; z = no
	mov	ah,al			; get copy of character
	and	ah,parmsk		; strip parity, if any, before testing
	cmp	ah,dl			; acting on Xoff?
	jne	acsrcv10		; ne = no
	cmp	xofsnt,0		; have we sent an XOFF?
	jne	acsrcv12		; ne = yes, ignore this XOFF char
	mov	xofrcv,bufon		; set flag saying buffer XOFF received
	jmp	short acsrcv12		;  and skip this character
acsrcv10:cmp	ah,dh			; acting on XON?
	jne	acsrcv11		; ne = no, go on
	mov	xofrcv,off		; clear the XOFF received flag
	jmp	short acsrcv12		;  and skip this character
acsrcv11:mov	[bx],al			; store new char in buffer "source"
	inc	bx
	inc	count			; add it to the count
	cmp	bx,offset source+bufsiz	; pointing beyond end of buffer?
	jb	acsrcv12		; b = no
	mov	bx,offset source	; wrap pointer, modulo bufsiz
acsrcv12:loop	acsrcv9

acsrcv13:mov	srcpnt,bx		; update pointer to next free slot
	cmp	count,bufsiz		; count more that buffer size?
	jbe	acsrcv14		; be = no
	mov	count,bufsiz		; limit to bufsiz (tells the truth)
acsrcv14:pop	si
	pop	dx
	pop	cx
	stc				; say no char in al from us
	ret
acsircv	endp

; NetBios Receive post processing interrupt routine.
; Sets rposted interlock flag
RPOST	PROC	NEAR		; NetBios receive post interrupt routine
	push	ds
	push	ax
	mov	ax,data			; reestablish data segment
	mov	ds,ax
	mov	rposted,2		; set interlock flag to completed
	pop	ax
	pop	ds
	iret				; return from interrupt
RPOST	endp

; TES block mode receive, uses blkrcv to process results
TESRCV	proc	near
	push	di
	push	cx
	push	dx
	push	es
	mov	ax,data
	mov	es,ax			; es:di will point to rcvbuf
	mov	di,offset rcvbuf
	mov	cx,nbuflen		; buffer length
	mov	dx,tesport		; operational port
	mov	ah,tesbread		; block read
	int	rs232
	jcxz	tesrcv1			; z = no characters read
	mov	rcv.scb_length,cx	; prepare for receive call
	call	blkrcv			; process rcvbuf
	jmp	short tesrcv2
tesrcv1:call	tesstate		; get session status to AH
	and	ah,7fh			; trim high bit (transition states)
	or	ah,ah			; session active?
	jnz	tesrcv2			; nz = yes, carry on
	call	tesclose		; close our end
tesrcv2:pop	es
	pop	dx
	pop	cx
	pop	di
	stc				; say not returning char in al here
	ret
TESRCV	endp

; Ungermann-Bass NETCI port receive characters routine.  Receive one or more
; characters.  Calls the blkrcv routine to transfer character to main source
; circular buffer.  Return carry clear if success. 
UBRECV	PROC	near
	push	cx
	push	es
	mov	ax,data
	mov	es,ax			; es:bx will point to rcvbuf
	mov	bx,offset rcvbuf
	mov	cx,nbuflen		; buffer length
ubrecv2:test	nettype,bapi+tcpnet	; 3Com BAPI or TCP Telnet interface?
	jz	ubrecv2a		; z = no
	mov	ah,bapiread
	xor	dh,dh			; session 0
ifndef	no_tcp
	test	nettype,tcpnet		; TCP/IP Telnet?
	jz	ubrecv2d		; z = no
	call	ktcpcom			; Far call Telnet code
	jmp	short ubrecv2e
endif	; no_tcp
ubrecv2d:int	bapiint

ubrecv2e:cmp	ah,3			; status, no session and above
	jb	ubrecv3			; b = no, successful?
	jmp	short ubrecv2b		; broken connection, terminate it

ubrecv2a:test	nettype,telapi		; Novell TELAPI?
	jz	ubrecv2c		; z = no, use Int 6Bh kind
	push	si
	push	bx
	mov	si,bx			; use es:si for buffer address
	mov	bx,telses		; session number
	mov	ah,telread
	int	rs232
	pop	bx
	pop	si
	xchg	ax,cx			; byte count returned in AX
	jcxz	ubrecv2b		; z = connection broken
	or	cx,cx
	jns	ubrecv3			; ns = no error
	cmp	cx,-35			; status of no data?
	jne	ubrecv2b		; ne = no
	xor	cx,cx			; mimic no data
	jmp	short ubrec1
ubrecv2b:call	ubclose
	jmp	short ubrec1

ubrecv2c:test	nettype,netone		; UB?
	jz	ubrec1			; z = no, do nothing
	mov	ax, nciread		; function 1 (receive) port 0	 [ohl]
	int	netci			; get characters		 [ohl]
ubrecv3:jcxz	ubrec1			; cx = z = nothing to do
	mov	rcv.scb_length,cx	; prepare for rpost call
	call	blkrcv			; process buffer
ubrec1:	pop	es
	pop	cx
	stc				; say not returning char in al here
	ret
UBRECV	ENDP

; Beame & Whiteside TCP receive routine [JRS]
bwrecv	proc	near			; [JRS]
	test	nettype,bwtcp		; active?
	jnz	bwrecv1			; nz = yes
	stc
	ret

bwrecv1:push	di			; [JRS] save the environment
	push	si			; [JRS]
	push	dx			; [JRS]
	push	cx			; [JRS]
	push	bx			; [JRS]
	mov	ah,readf2		; [JRS] read from "file"
	mov	bx,bwhandle		; [JRS] device handle
	or	bx,bx
	jz	bwrecv4			; z = invalid handle
	mov	cx,nbuflen-4		; number of bytes to read - safety
	mov	dx,offset rcvbuf	; [JRS] data buffer is DS:DX
	int	dos			; [JRS] ask dos to get it
	jc	bwrecv5			; [JRS] c = no data available
	mov	cx,ax			; [JRS] get the number of bytes read
	jcxz	bwrecv4			; [JRS] no chars read is a hangup
	cmp	ax,nbuflen-4		; check on sanity
	ja	bwrecv5			; a = error of some kind, ignore
	mov	si,offset rcvbuf
	call	bwtnopt			; do options scanning
	jmp	short bwrecv5
bwrecv4:call	bwclose			; read failed, quit
bwrecv5:pop	bx			; [JRS] restore environment
	pop	cx			; [JRS]
	pop	dx			; [JRS]
	pop	si			; [JRS]
	pop	di			; [JRS]
	stc				; [JRS] flag no characters read
	ret				; [JRS]
bwrecv	endp				; [JRS]

; Beame & Whiteside telnet options scanner and processor
; Enter with newly read material in ds:si, of length CX bytes
bwtnopt	proc	near
	or	cx,cx
	jle	bwtnopt1		; le = nothing to do
	cmp	optstate,0		; outside IAC string?
	je	bwtnopt2		; e = yes
	jmp	bwtnopt20		; do options
bwtnopt1:ret

bwtnopt2:push	cx
	push	es
	mov	ax,seg rcvbuf		; make es:di the receiver buffer
	mov	es,ax
	mov	di,si
	mov	al,IAC			; IAC
	cld
	repne	scasb			; look for IAC
	pop	es
	pop	cx
	mov	ax,di			; points one byte after break
	jne	bwtnopt3		; ne = no IAC
	dec	ax			; backup to IAC
bwtnopt3:sub	ax,si			; minus starting offset
	sub	cx,ax			; original count - <before IAC>
	mov	rcv.scb_length,ax	; data length before IAC
	mov	rcv.scb_baddr,si 	; source of text
	add	si,ax			; starting point for IAC
	or	ax,ax			; any leading text?
	jz	bwtnopt4		; z = nothing to block receive upon
	call	blkrcv			; dispose of it, saves si and cx
bwtnopt4:or	cx,cx			; bytes remaining in buffer
	jg	bwtnopt5		; if positive then si == IAC byte
	xor	cx,cx			; force zero
	ret
bwtnopt5:inc	si
	dec	cx			; skip IAC
	mov	optstate,2		; read two more chars
	jmp	bwtnopt			; repeat

bwtnopt20:cmp	optstate,2		; reading 1st char after IAC?
	jb	bwtnopt23		; b = no
	dec	cx			; read the char
	lodsb
	cmp	al,SB			; before legal options?
	jae	bwtnopt22		; ae = no
	inc	si			; recover char
	inc	cx
	mov	optstate,0		; clear Options machine
	jmp	bwtnopt			; continue

bwtnopt22:mov	option1,al		; save first Option byte
	mov	optstate,1		; get one more byte
	jmp	bwtnopt			; get the second Options byte

bwtnopt23:mov	optstate,0		; read second char after IAC
	dec	cx			; read the char
	lodsb
	mov	option2,al		; second options byte
	mov	ah,option1
	mov	bx,portval
	mov	bl,[bx].ecoflg		; echo status
					; decode options and respond
	cmp	al,TELOPT_ECHO		; Echo?
	jne	bwtnopt30		; ne = no
	cmp	ah,WILL			; remote host will echo?
	jne	bwtnopt26		; ne = no
	cmp	bl,0			; are we doing local echo?
	je	bwtnopt29		; e = no
	mov	ah,DO			; say please echo
	call	bwsendiac
	xor	bl,bl			; say we should not echo
	call	setecho			; set our echo state
	jmp	bwtnopt
bwtnopt26:cmp	ah,WONT			; remote host won't supply echo?
	jne	bwtnopt28		; ne = no
	cmp	bl,0			; are we doing local echo?
	jne	bwtnopt29		; ne = yes
	mov	ah,DONT
	call	bwsendiac
	mov	bl,lclecho		; do local echoing
	call	setecho			; set local echoing state
	jmp	bwtnopt

bwtnopt28:cmp	ah,DO			; remote host wants us to echo?
	jne	bwtnopt29		; ne = no
	mov	ah,WONT			; decline
	call	bwsendiac
bwtnopt29:jmp	bwtnopt

bwtnopt30:cmp	al,TELOPT_SGA		; SGA?
	jne	bwtnopt40		; ne = no
	cmp	ah,WONT			; host won't do SGAs?
	jne	bwtnopt33		; ne = no
	cmp	sgaflg,0		; our state is don't too?
	jne	bwtnopt35		; ne = yes
	inc	sgaflg			; change state
	mov	ah,DONT			; say please don't
	call	bwsendiac
	cmp	bl,0			; doing local echo?
	jne	bwtnopt35		; ne = yes
	mov	bl,lclecho		; change to local echoing
	call	setecho
	jmp	bwtnopt			; continue
bwtnopt33:cmp	ah,WILL			; host will use Go Aheads?
	jne	bwtnopt35		; ne = no
	cmp	sgaflg,0		; doing SGAs?
	jne	bwtnopt35		; ne = no
	mov	sgaflg,0		; change to doing them
	mov	ah,DO
	call	bwsendiac
bwtnopt35:jmp	bwtnopt

bwtnopt40:cmp	ah,WILL			; all other Options
	jne	bwtnopt41		; ne = no
	mov	ah,DONT			; say do not
	call	bwsendiac		; respond
	jmp	bwtnopt43		; continue
bwtnopt41:cmp	ah,DO
	jne	bwtnopt42
	mov	ah,WONT			; say we won't
	call	bwsendiac
	mov	ah,DONT			; and host should not either
	call	bwsendiac
	jmp	bwtnopt43		; continue
bwtnopt42:cmp	ah,DONT
	jne	bwtnopt43
	mov	ah,WONT			; say we won't
	call	bwsendiac
bwtnopt43:jmp	bwtnopt			; continue
bwtnopt endp

; Beame & Whiteside, send IAC byte1 byte2, where byte 1 is in AH
; and byte 2 is in option2
bwsendiac proc	near
	push	cx
	push	si
	push	ax
	mov	ah,IAC			; send IAC
	call	outchr
	pop	ax			; send command byte
	call	outchr
	mov	ah,option2		; send response byte
	call	outchr
	pop	si
	pop	cx
	ret
bwsendiac endp

; Update local-echo status from B&W Telnet Options negotiations
; Enter with BL = 0 or lclecho, to clear or set local-echo
setecho	proc	near
	and	yflags,not lclecho	; assume no local echo in emulator
	or	yflags,bl		; set terminal emulator
	push	si
	mov	si,portval
	mov	[si].ecoflg,bl		; set mainline SET echo flag
	pop	si
ifndef	no_terminal
	cmp	ttyact,0		; acting as a Terminal?
	je	setecho1		; e = no
	push	cx
	push	si
	call	dword ptr ftogmod	; toggle mode line
	call	dword ptr ftogmod	; and again
	pop	si
	pop	cx
endif	; no_terminal
setecho1:ret
setecho	endp
endif	; no_network

; 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	quechar,0		; char queued for transmission?
	je	outch0			; e = no, no XOFF queued
	xchg	ah,quechar		; save current char
	cmp	ah,flowoff		; really XOFF?
	jne	outch0a			; ne = no
	mov	xofsnt,bufon	   	; we are senting XOFF at buffer level
outch0a:call	outch2			; send queued char (XOFF usually)
	xor	ah,ah			; replacement for queued char, none
	xchg	ah,quechar		; recover current char, send it
outch0:	test	flowcnt,1		; doing output XON/XOFF flow control?
	jz	outch2			; z = no, just continue
	cmp	ah,flowoff		; sending xoff?
	jne	outch1			; ne = no
	mov	xofsnt,usron		; indicate user level xoff being sent
	jmp	short 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
	push	cx			; save reg
	mov	ch,15			; 15 sec timeout interval
	xor	cl,cl			;  convert to 4 millsec increments

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
	cmp	ttyact,0		; in Connect mode?
	je	outch1c			; e = no
	push	ax			; save char around the call
	call	beep			; let user know we are xoff-ed
	pop	ax			;  but are sending anyway
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				; failure, to exit playback mode
	ret
outch3:	push	bx
	mov	bx,portval		; output char is in register AH
	call	[bx].sndproc		; output processing routine
	pop	bx
	ret
outchr	endp

uartsnd	proc	near			; UART send
	push	cx
	push	dx
	push	bx
	mov	bx,portval
	cmp	[bx].portrdy,0		; is port ready?
	pop	bx
	je	uartsn8			; e = no
	cmp	flags.carrier,0		; worry about carrier detect?
	je	uartsn9a		; e = no
	cmp	dupflg,0		; full duplex?
	jne	uartsn1			; ne = no, half
	mov	dx,modem.mddat
	add	dx,6			; modem status reg 3feh
	in	al,dx			; 03feh, modem status reg
	and	al,80h			; get CD bit
	jnz	uartsn9			; nz = CD is on now
	test	cardet,80h		; previous CD state
	jz	uartsn9			; z = was off
	mov	al,01h			; say was ON but is now OFF
uartsn9:mov	cardet,al		; preserve as global
	cmp	al,1			; carrier dropped?
	je	uartsn8			; e = yes, fail the operation

uartsn9a:test	flowcnt,4		; using RTS to control incoming chars?
	jz	uartsn3			; z = no
	mov	cx,8000			; ~10 seconds worth of waiting on CTS
	jmp	short uartsn2		; do CTS test/waiting
					; Half Duplex here
uartsn1:mov	dx,modem.mdstat		; 3fdh
	dec	dx
	in	al,dx			; modem control reg 3fch
	or	al,2			; assert RTS for hardware transmit
	push	ax
	in	al,ppi_port		; delay
	pop	ax
	mov	dx,modem.mdstat		; 3fdh, waste cycles here
	dec	dx			; 3fch
	out	dx,al
	add	dx,2			; modem status register 3feh
	in	al,ppi_port		; delay
	in	al,ppi_port		; delay
	in	al,dx			; get DSR status
	test	al,20h			; ignore CTS if DSR is not asserted
	jz	uartsn3			; z = DSR not asserted
	mov	cx,8000			; ~10 seconds worth of waiting on CTS
					; Half Duplex and RTS/CTS flow cont.
uartsn2:mov	dx,modem.mdstat		; 3fdh
	inc	dx			; 3feh
	in	al,dx			; wait on CTS (ah has output char)
	test	al,10h			; is CTS asserted? (dx = 3feh)
	jnz	uartsn3			; nz = yes
	push	ax			; preserve char in ah
	mov	ax,1			; wait one millisec
	call	pcwait
	pop	ax
	loop	uartsn2			; test again
	push	ax
	call	beep			; timeout, make non-fatal
	pop	ax			; continue to send the char
	cmp	dupflg,0		; half duplex?
	jne	uartsn8			; ne = yes, fail at this point
uartsn3:push	bx
	mov	bx,portval
	cmp	[bx].baud,Bsplit	; split-speed mode?
	pop	bx
	jne	uartsn4			; ne = no
	mov     al,ah                   ; [pslms]
        call    out75b			; do split speed sending at 75 baud
        pop     dx
        pop     cx
	ret				; out75b sets/clears carry bit

uartsn4:mov	cx,0ffffh		; try counter
uartsn4a:mov	dx,modem.mdstat		; get line status
	in	al,dx
	test	al,20H			; Transmitter (THRE) ready?
	jnz	uartsn5			; nz = yes
	in	al,ppi_port		; delay
	in	al,ppi_port		; delay
	in	al,ppi_port		; delay
	loop	uartsn4a
	jmp	short uartsn8		; Timeout
uartsn5:mov	al,ah			; Now send it out
	mov	dx,modem.mddat		; use a little time
	push	ax
	in	al,ppi_port		; delay
	pop	ax
	out	dx,al
	cmp	dupflg,0		; full duplex?
	je	uartsn7			; e = yes
		  			; half duplex
	cmp	al,trans.seol		; End of Line char?
	jne	uartsn7			; ne = no
	xor	cx,cx			; loop counter
uartsn6:mov	dx,modem.mdstat		; modem line status reg
	in	al,dx			; read transmitter shift reg empty bit
	push	ax
	in	al,ppi_port		; delay, wait for char to be sent
	in	al,ppi_port
	in	al,ppi_port
	in	al,ppi_port
	pop	ax
	test	al,40h			; is it empty?
	loopz	uartsn6			; z = no, not yet
	mov	dx,modem.mdstat
	dec	dx			; modem control reg 3fch
	in	al,dx
	and	al,not 2		; unassert RTS (half duplex turn)
	push	ax
	in	al,ppi_port		; delay
	pop	ax
	out	dx,al
uartsn7:pop	dx			; exit success
	pop	cx
	clc
	ret
uartsn8:call	beep
	mov	kbdflg,'C'		; exit connect mode
	pop	dx			; exit failure
	pop	cx
	stc
	ret
uartsnd	endp

biossnd	proc	near			; Bios send
	push	cx			; find current port
	mov	cx,5			; retry counter
	push	dx
	xor	dh,dh			; assume port 1
	mov	dl,flags.comflg		; get port number (1..4)
	or	dl,dl			; zero (no such port)?
	jz	biossn3			; z = yes, don't access it
	and	dl,7			; use lower three bits
	dec	dl			; address ports as 0..3 for Bios
	mov	al,ah			; now send it out
biossn2:push	ax			; save char
	mov	ah,1			; send char
	int	rs232			; bios send
	shl	ah,1			; set carry if failure
	pop	ax			; recover char
	jnc	biossn4			; nc = success
	push	ax
	mov	ax,60			; wait 60 ms
	call	pcwait			; this must preserve cx and dx
	pop	ax			; recover char
	loop	biossn2			; try again
biossn3:push	bx
	mov	bx,portval
	mov	[bx].portrdy,0		; say port is not ready
	pop	bx
	mov	kbdflg,'C'		; exit Connect mode
	stc				; fail through here
biossn4:pop	dx
	pop	cx
	ret				; c set = failure, else success
biossnd	endp

; Fossil block send
fossnd	proc	near
	mov	bx,xmtcnt		; count of chars in buffer
	mov	xmtbufx[bx],ah		; put char in buffer
	inc	xmtcnt			; count of items in this buffer
	and	ah,7fh			; strip parity
	cmp	xmtcnt,length xmtbuf	; is buffer full now?
	jae	fossnd2			; ae = buffer is full, send it now
	cmp	ah,trans.seol		; end of packet?
	je	fossnd2			; e = yes, send buffer
	test	flowcnt,1		; using output XON/XOFF flow control?
	jz	fossnd1			; z = no
	cmp	ah,flowon		; flow control?
	je	fossnd2			; e = yes, always expedite
	cmp	ah,flowoff		; ditto for flow off
	je	fossnd2
fossnd1:cmp	ttyact,0		; are we in Connect mode?
	jne	fossnd2			; ne = yes, send now
	clc				; e = no, wait for more before sending
	ret
fossnd2:push	es
	push	di
	mov	di,seg xmtbufx
	mov	es,di
	mov	di,offset xmtbufx
fossnd3:cmp	xmtcnt,0		; buffer count
	jle	fossnd4			; le = nothing to send
	mov	ah,fossil_blkwr		; Fossil send block
	xor	al,al
	mov	dx,fossil_port		; port
	mov	cx,xmtcnt		; count
	int	rs232			; block send
	jc	fossnd5			; c = error
	cmp	ax,0ffffh		; error condition?
	je	fossnd5			; e = yes
	cmp	ax,fossil_blkwr*256	; complete no-op?
	je	fossnd5			; e = yes
	add	di,ax			; move buffer pointer
	sub	xmtcnt,ax		; minus count sent
	jnz	fossnd3			; nz = have some more to send
fossnd4:pop	di
	pop	es
	clc
	ret
fossnd5:pop	di
	pop	es
	call	serrst			; close port
	mov	kbdflg,'C'		; exit Connect mode
	stc				; say not returning char in al here
	ret
fossnd	endp

ifndef	no_network
ebisnd	proc	near			; EBIOS block send
	mov	bx,xmtcnt		; count of chars in buffer
	mov	xmtbufx[bx],ah		; put char in buffer
	inc	xmtcnt			; count of items in this buffer
	and	ah,7fh			; strip parity
	cmp	xmtcnt,length xmtbuf	; is buffer full now?
	jae	ebisnd2			; ae = buffer is full, send it now
	cmp	ah,trans.seol		; end of packet?
	je	ebisnd2			; e = yes, send buffer
	test	flowcnt,1		; using output XON/XOFF flow control?
	jz	ebisnd1			; z = no
	cmp	ah,flowon		; flow control?
	je	ebisnd2			; e = yes, always expedite
	cmp	ah,flowoff		; ditto for flow off
	je	ebisnd2
ebisnd1:cmp	ttyact,0		; are we in Connect mode?
	jne	ebisnd2			; ne = yes, send now
	clc				; e = no, wait for more before sending
	ret
ebisnd2:push	si
	mov	si,offset xmtbufx
ebisnd3:cmp	xmtcnt,0		; buffer count
	jle	ebisnd4			; le = nothing to send
	cld
	lodsb				; read next byte from buffer
	push	si
	mov	ah,ebsend		; EBIOS send char in AL
	mov	dx,ebport		; port 0..3, do here to waste time
	int	rs232			; bios send
	pop	si
	shl	ah,1			; put status high bit into carry
	jc	ebisnd4			; c = failure
	dec	xmtcnt
	jmp	short ebisnd3
ebisnd4:pop	si
	ret				; c set = failure, else success
ebisnd	endp

decsnd	proc	near			; DECnet/LAT send processor
	test	nettype,declat		; LAT?
	jz	decsnd3			; z = no, use CTERM
decsnd1:mov	dx,lathand		; LAT handle
	or	dx,dx			; legal handle?
	jz	decsnd4			; z = invalid handle
	call	latblksnd		; send char in ah, and buffer
	jc	decsnd4			; c = failure
	ret

decsnd3:mov	dx,decneth		; DECnet, handle
	or	dx,dx			; legal handle?
	jz	decsnd4			; z = invalid handle
	mov	bl,ah			; CTERM char to be sent
	mov	ax,dsend		; send byte in bl
	int	decint
	rcl	ah,1			; status 80h bit, did char get sent?
	jc	decsnd4			; c = failure
	ret
decsnd4:call	decclose		; failure, close connection
	stc
	ret
decsnd	endp

; Special block sending routine for LAT (all vendors)
; DEC's LAT breaks if we send too much at one time, TES LAT breaks much
; more readily, and MTC LAT tolerates the longest bursts. None works
; without a breathing space between block sends which don't fully
; send the block. TES LAT does not support callbacks. We arbitarily
; limit a buffer to 78 bytes to survive the various LAT troubles on
; both ends of the link. Putting DEC's LAT v4.1 in expanded memory
; under DOS 6.2 and NetWare VLM 1.1 shells causes lockups.
; Variable "temp" is borrowed as a retry counter.
latblksnd proc	near
	mov	bx,xmtcnt		; count of chars in buffer
	mov	xmtbufx[bx],ah		; put char in buffer
	inc	xmtcnt			; count of items in this buffer
	cmp	xmtcnt,78		; is buffer full now?
	jae	latsnd2			; ae = buffer is full, send it now
	and	ah,7fh			; strip parity
	cmp	ah,trans.seol		; end of packet?
	je	latsnd2			; e = yes, send buffer
	test	flowcnt,1		; using output XON/XOFF flow control?
	jz	latsnd1			; z = no
	cmp	ah,flowon		; flow control?
	je	latsnd2			; e = yes, always expedite
	cmp	ah,flowoff		; ditto for flow off
	je	latsnd2
latsnd1:cmp	ttyact,0		; are we in Connect mode?
	jne	latsnd2			; ne = yes, send now
	xor	ah,ah
	clc				; e = no, wait for more before sending
	ret

latsnd2:push	bx
	mov	bx,offset xmtbufx
	mov	temp,0			; retry counter
	mov	temp2,10		; ms time wait when not enough space
	cmp	latkind,TES_LAT		; TES?
	jne	latsnd3			; ne = no
	mov	temp2,2*55		; slower for TES

latsnd3:cmp	xmtcnt,0		; buffer count
	jle	latsnd8			; le = nothing to send
	mov	dx,lathand
	mov	ah,latstat		; get status
	int	latint
	test	ah,4			; session is inactive?
	jz	latsnd5			; z = no
	pop	bx
	stc				; inactive, exit failure
	ret

latsnd5:mov	cx,xmtcnt		; amount to be sent
	cmp	cx,78			; apply limits to fragile LATs
	jbe	latsnd7			; be = in bounds
	mov	cx,78			; so they do not mess up
latsnd7:push	es
	push	bx
	mov	ax,seg xmtbufx
	mov	es,ax			; es:bx is send buffer
	mov	ah,latsendb		; send block
	mov	dx,lathand
	int	latint
	pop	bx
	pop	es
	add	bx,cx			; move to next new output byte
	sub	xmtcnt,cx		; deduct chars sent
	jle	latsnd8			; le = all done
	or	cx,cx			; any chars accepted?
	jnz	latsnd7a		; nz = yes
	inc	temp			; retry counter
	cmp	temp,10			; exhausted retries?
	jbe	latsnd7b		; be = no
	call	decclose		; failure, close connection
	pop	bx
	stc
	ret
latsnd7a:mov	temp,0			; reset retry counter 
latsnd7b:push	bx
	mov	ax,temp2		; retry timeout
	add	temp2,ax		; new timeout (longer)
	cmp	latkind,TES_LAT		; TES LAT?
	jne	latsnd7c		; ne = no
	add	ax,100			; slower for TES
latsnd7c:inc	ax			; zero avoidance
	call	pcwait			; pause
	call	decrcv			; do a receive to clear LAT driver
	pop	bx
	jmp	latsnd3			; continue sending remainder

latsnd8:pop	bx
	clc				; exit success
	ret
latblksnd endp
endif	; no_network

; Software uart that generates output rate of 75 baud independently of
; 8250 baudrate; use with V.23 split speed modems.
; This routine outputs a character in 8,<parity>,1 format only.
; To generate good bit timing, latency is to be kept low; now set for max
; 12% bit distortion and 1.2% speed deviation (about the same as the input
; stage of a HW-uart), which requires latency < 3ms (i e the same as 8250
;  requires for receive in 9600 baud)
; Creator Dan Norstedt 1987. Implemented 18 Feb 1988 by [pslms].
out75b	proc	near
	mov	timeract,1	; say we are allocating the timer chip
	push	cx
	push	dx
	xchg	ax,bx		; save char to output
	mov	bh,1		; prepare output char
	mov	cx,cnt75b	; maximum end count
out75b1:call    read_timer2	; save previous end count in CX, read timer
				;  test for timer still decrementing ?
        jb	out75b1		; b = yes, wait (for a maximum of 1 bit)
        mov	al,0b4h		; set up counter 2 to mode 2, load LSB+MSB
        out	timercmd,al	; set mode 2 = rate generator
        jmp	$+2
        mov	ax,cnt75b*4+cnt75b*4+cnt75b*2-precomp  ; set start point
        out	timer2data,al	; output LSB
        jmp	$+2
        xchg	cx,ax		; save value in CX for compare in READ_TIMER2
        mov	al,ch		; output MSB
        cli			; timer starts counting on next instr, make
        out	timer2data,al	;  sure it's not to far of from start bit
        in 	al,ppi_port	; get Port B contents
        jmp	$+2
        and	al,0fch		; mask speaker and gate 2 bits
        inc	ax
        out	ppi_port,al	; set speaker off and gate 2 on
        mov	al,-1
        out	timer2data,al	; set counter wraparound to 0FFFFH
        jmp	$+2
        out	timer2data,al
        mov	bp,cnt75b*4+cnt75b*4+cnt75b ; set timer value for next bit
        mov	dx,modem.mddat	; get com port address
        add	dx,3		; address of it's line control register
        in 	al,dx		; get port status
        jmp	$+2
out75b2:or	al,brkbit	; set line to space (by using break bit)
out75b3:out	dx,al		; once start bit is out, we may reenable
        sti			;  without getting extra jitter
out75b4:call	read_timer2
	jns	out75b5		; ns = timer doesn't seem to run
	cmp	ax,bp		; time for next bit?
	jns	out75b4		; ns = no, wait
	sub	bp,cnt75b	; yes, step time to next event
	in 	al,dx		; get line control register
	and	al,0bfh		; remove break bit
	shr	bx,1		; carry for mark bit, none for space
	jnc	out75b2		; nc = it was a space (we know BX is non-zero)
	jnz	out75b3		; mark, and not the last one
	jmp	$+2
	out	dx,al		; last, start to send stop bit
	pop	dx
	pop	cx
	mov	timeract,0	; say we are finished with the timer chip
	cmp	quechar,0	; any char queued to be sent?
	je	out75b6		; e = no
	call	outchr		; yes, send it now
out75b6:clc
	ret
out75b5:in	al,dx		; timer doesn't function properly,
	and	al,0bfh		;  restore com port and return error
	out	dx,al
	mov	timeract,0	; say we are finished with the timer chip
	pop	dx
	pop	cx
	stc
        ret
out75b	endp
     
read_timer2	proc	near
	mov     al,80h          ; Freeze timer 2
	out     timercmd,al
	jmp     $+2
	in      al,timer2data   ; Get LSB
	jmp     $+2
	mov     ah,al
	in      al,timer2data   ; Get MSB
	xchg    al,ah           ; Get LSB and MSB right
	cmp     ax,cx           ; Compare to previous sample
	mov     cx,ax           ; Replace previous sample with current
	ret
read_timer2 endp

ifndef	no_network
; NetBios Send packet routine. Send xmt scb with no-wait option. Waits
; up to 6 seconds for current Send to complete before emitting new Send.
; Failure to Send resets serial port (Server mode allows reiniting of serial
; port). Returns carry clear for success, carry set for failure.
; Enter with xmtcnt holding length of data in xmtbuf to be sent.
SEND	PROC	NEAR			; Network. Send session packet
	mov	bx,xmtcnt		; count of chars in buffer
	test	nettype,acsi		; ACSI?
	jz	send32			; z = no
	or	bx,bx			; at start of buffer?
	jnz	send31			; nz = no
	mov	bx,2			; include internal count word
	mov	word ptr xmtbufx,bx
	mov	xmtcnt,bx
send31:	mov	xmtbufx[bx+1],0		; status
	inc	xmtcnt
	add	word ptr xmtbufx,2	; internal count word
send32:
	mov	xmtbufx[bx],ah		; put char in buffer
	inc	xmtcnt			; count of items in this buffer
	and	ah,7fh			; strip parity
	cmp	xmtcnt,length xmtbuf	; is buffer full now?
	jae	send22			; ae = buffer is full, send it now
	test	nettype,acsi		; ACSI?
	jz	send33			; z = no
	cmp	xmtcnt,512		; ACSI has hard 512 byte limit
	jb	send33			; b = not full yet
	mov	xmtcnt,512		; limit buffer
	jmp	short send22		; send only this buffer
send33:	cmp	ah,trans.seol		; end of packet?
	je	send22			; e = yes, send buffer
	test	flowcnt,1		; using output XON/XOFF flow control?
	jz	send21			; z = no
	cmp	ah,flowon		; flow control?
	je	send22			; e = yes, always expedite
	cmp	ah,flowoff		; ditto for flow off
	je	send22
send21:cmp	ttyact,0		; are we in Connect mode?
	jne	send22			; ne = yes, send now
	clc				; e = no, wait for more before sending
	ret
send22:	cmp	pcnet,1			; network ready yet?
	ja	send0b			; a = net is operational
	je	send0c			; e = net but no session, fail
	jmp	send3b			; no net, fail
send0c:	jmp	send3			; net but no session
send0b:	cmp	sposted,0		; is a send outstanding now?
	je	send1			; e = no, go ahead
	push	cx			; Timed test for old send being done
	mov	ch,trans.rtime		; receive timeout other side wants
	mov	cl,80h			; plus half a second
	shl	cx,1			; sending timeout * 512
send0:	cmp	sposted,0		; is a send outstanding now?
	je	send0a			; e = no, clean up and do send
	push	cx			; save cx
	push	ax			; and ax
	mov	ax,2			; wait 2 milliseconds
	call	pcwait			;  between retests
	pop	ax
	pop	cx			; loop counter
	loop	send0			; repeat test
	pop	cx			; recover cx
	jmp	send3b			; get here on timeout, can't send
send0a:	pop	cx			; recover cx and proceed to send

send1:	cmp	xmtcnt,0		; number of items to send
	jne	send1a			; ne = some
	clc				; else don't send null packets
	ret
send1a:	push	cx			; save these regs
	push	si
	push	di
	push	es
	push	ds
	pop	es			; set es to data segment
	mov	si,offset xmtbufx	; external buffer
	mov	di,offset xmtbuf	; copy for network packets
	mov	cx,xmtcnt		; buffer length
	mov	xmt.scb_length,cx	; tell buffer length
	shr	cx,1			; divide by two (words), set carry
	jnc	send2			; nc = even number of bytes
	movsb				; do single move
send2:	rep	movsw			; copy the data
	pop	es
	pop	di
	pop	si
	pop	cx
	mov	xmtcnt,0		; say xmtbufx is available again
	mov	xmt.scb_cmd,nsend+nowait ; send, don't wait for completion
	mov	sposted,1		; say send posted
	mov	bx,offset xmt		; set pointer to scb
	call	nbsession
					; success or failure?
	cmp	xmt.scb_err,0		; good return?
	je	send4			; e = yes
	cmp	xmt.scb_err,npending	; pending?
	je	send4			; e = yes
	cmp	xmt.scb_err,18h		; session ended abnormally?
	jbe	send3			; e = yes, b = other normal errors
	push	ax
	push	dx			; another kind of error, show message
	mov	ah,prstr
	mov	dx,offset sndmsg	; say send failed
	int	dos
	mov	al,xmt.scb_err		; show error code
	call	decout
	pop	dx
	pop	ax
					; Error return
send3:	mov	pcnet,1			; say session is broken
	call	serrst			; reset serial port
	cmp	lposted,1		; Listen posted?
	je	receiv3a		; e = yes, stay alive
	cmp	xmt.scb_rname,'*'	; behaving as a Listner?
	je	receiv3a		; e = yes, stay alive
	cmp	lposted,1		; Listen posted?
	je	send3a			; e = yes, stay alive
	cmp	xmt.scb_rname,'*'	; behaving as a Listner?
	je	send3a			; e = yes, stay alive
	test	flags.remflg,dserver	; server mode?
	jz	send3b			; z = no
send3a:	call	nbclose			; Server: purge old NAKs etc
	call	serini			; reinitialize it for new session
send3b:	stc				; set carry for failure to send
	ret
send4:	clc
	ret
SEND	ENDP

; NetBios Send packet completion interrupt routine. At entry CS is our
; code segment, es:bx points to scb, netbios stack, interrupts are off.
SPOST	PROC	NEAR			; post routine for Send packets
	push	ds
	push	ax
	mov	ax,data
	mov	ds,ax
	mov	sposted,0		; clear send interlock
	pop	ax
	pop	ds
	iret
SPOST	ENDP	

; TES block send. Destroys BX
TESSND	proc	near
	mov	bx,xmtcnt		; count of chars in buffer
	mov	xmtbufx[bx],ah		; put char in buffer
	inc	xmtcnt			; count of items in this buffer
	and	ah,7fh			; strip parity
	cmp	xmtcnt,length xmtbuf	; is buffer full now?
	jae	tessen22		; ae = buffer is full, send it now
	cmp	ah,trans.seol		; end of packet?
	je	tessen22		; e = yes, send buffer
	test	flowcnt,1		; using output XON/XOFF flow control?
	jz	tessen21		; z = no
	cmp	ah,flowon		; flow control?
	je	tessen22		; e = yes, always expedite
	cmp	ah,flowoff		; ditto for flow off
	je	tessen22
tessen21:cmp	ttyact,0		; are we in Connect mode?
	jne	tessen22		; ne = yes, send now
	clc				; e = no, wait for more before sending
	ret

tessen22:push	ax
	push	cx
	push	dx
	push	di
	push	es
	mov	cx,xmtcnt		; number of chars
	jcxz	tessnd4			; don't send zero chars
	mov	di,offset xmtbufx	; buffer address in es:di
	mov	ax,data
	mov	es,ax
tessnd1:mov	temp,0			; retry counter
tessnd2:mov	ah,tesbwrite		; block write
	mov	dx,tesport		; operational port
	int	rs232
	or	ax,ax			; number of chars sent, zero?
	jnz	tessnd3			; nz = sent some
	mov	ax,10			; wait 10ms
	call	pcwait
	inc	temp			; count retry
	cmp	temp,5			; done all retries?
	jb	tessnd2			; b = no
	call	tesclose		; close sessioin, declare failure
	stc
	jmp	short tessnd5		; exit failure

tessnd3:cmp	ax,cx			; check that all characters were sent
	je	tessnd4			; e = yes
	add	di,ax			; point to remaining chars
	sub	cx,ax			; count of remaining characters
	mov	xmtcnt,cx		; need count in xmtcnt too
	jmp	short tessnd1		; try again to send
tessnd4:clc				; success, need failure case too
tessnd5:mov	xmtcnt,0
	pop	es
	pop	di
	pop	dx
	pop	cx
	pop	ax
	ret
TESSND	endp

; Block send routine shared by many networks.
; Enter with char to be sent in AH. Destroys BX.
ubsend	proc	near
	mov	bx,xmtcnt		; count of chars in buffer
	mov	xmtbufx[bx],ah		; put char in buffer
	inc	xmtcnt			; count of items in this buffer
	and	ah,parmsk		; strip parity
ifndef	no_tcp
	test	nettype,tcpnet		; TCP/IP Telnet?
	jz	ubsen24			; z = no
	cmp	ah,CR			; carriage return?
	jne	ubsen24			; ne = no
	cmp	xmtcnt,length xmtbuf	; is buffer full?
	jb	ubsen22			; b = no, else take special setps
	call	ubsen28			; send what we have now
ubsen22:cmp	tcpnewline,2		; newline is RAW?
	je	ubsen24			; e = yes, no special steps
	mov	bx,xmtcnt
	mov	al,LF			; CR -> CR/LF
	cmp	tcpnewline,0		; newline mode is off?
	jne	ubsend23		; ne = no, send CR/LF
	cmp	tcpmode,0		; NVT-ASCII?
	jne	ubsen28			; ne = no, Binary, no CR NUL
	xor	al,al			; CR -> CR NUL
ubsend23:mov	xmtbufx[bx],al		; append this char
	inc	xmtcnt
	jmp	short ubsen28		; send buffer now
endif	; no_tcp

ubsen24:cmp	xmtcnt,length xmtbuf	; is buffer full now?
	jae	ubsen28			; ae = buffer is full, send it now
	cmp	ah,trans.seol		; end of packet?
	je	ubsen28			; e = yes, send buffer
	test	flowcnt,1		; using output XON/XOFF flow control?
	jz	ubsen25			; z = no
	cmp	ah,flowon		; flow control?
	je	ubsen28			; e = yes, always expedite
	cmp	ah,flowoff		; ditto for flow off
	je	ubsen28
ubsen25:cmp	ttyact,0		; are we in Connect mode?
	jne	ubsen28			; ne = yes, send now
	clc				; e = no, wait for more before sending
	ret
ubsen28:push	ax
	push	cx
	push	es
	mov	cx,xmtcnt		; number of chars
	or	cx,cx
	jnz	ubsen29			; nz = have something to send
	jmp	ubsend1			; z = nothing to send
ubsen29:mov	temp,5			; retry counter for can't send
	mov	bx,offset xmtbufx	; buffer address in es:bx
ubsend2:mov	ax,seg xmtbuf
	mov	es,ax
	test	nettype,bapi+tcpnet	; 3Com BAPI or TCP Telnet?
	jz	ubsend2a		; z = no
	mov	ah,bapiwrit		; 3Com block write
	xor	dh,dh			; session 0
ifndef	no_tcp
	test	nettype,tcpnet		; TCP/IP Telnet?
	jz	ubsend2d		; z = no
	push	bx
	call	ktcpcom			; Far call TCP/IP Telnet code
	pop	bx
	jmp	short ubsend2e
endif	; no_tcp
ubsend2d:int	bapiint
ubsend2e:cmp	ah,3			; status, no session and above
	jae	ubsend2b		; ae = no session and above, fail
	jmp	short ubsend3		; process data

ubsend2a:test	nettype,telapi		; Novell TELAPI?
	jz	ubsend2c		; z = no, Int 6Bh kind
	push	si
	push	bx			; preserve buffer offset
	mov	si,bx			; use es:si for buffer address
	mov	ah,telwrite
	mov	bx,telses		; session number
	int	rs232
	pop	bx
	pop	si
	mov	cx,ax			; TELAPI returns sent count in AX
	or	ax,ax			; error response (sign bit set)?
	jns	ubsend3			; ns = no error

ubsend2b:call	ubclose			; failure, close the connection
	pop	es
	pop	cx
	pop	ax
	stc				; failure
	ret

ubsend2c:test	nettype,netone		; UB?
	jz	ubsend1			; no, do nothing
	mov	ax, nciwrit		; write function, port 0	 [ohl]
	int	netci
ubsend3:cmp	cx,xmtcnt		; check that all characters sent [ohl]
	je	ubsend1			; e = yes			 [ohl]
	add	bx,cx			; point to remaining chars	 [ohl]
	sub	xmtcnt,cx		; count of remaining characters	 [ohl]
	or	cx,cx			; did we send any?
	jnz	ubsend3a		; nz = yes
	push	bx			; try to read to pass time of day
	mov	bx,bufsiz		; size of main rcv buffer
	sub	bx,count		; minus occupancy
	cmp	bx,nbuflen		; qty in next full read batch
	jb	ubsend3b		; b = not enough space
	mov	bx,portval
	call	[bx].rcvproc		; read routine
ubsend3b:pop	bx			; fall through to grab new char
	mov	ax,10			; 10 millisec pause between retries
	call	pcwait
	dec	temp			; retry counter
	jnz	ubsend3a		; nz = some retries remaing
	pop	es
	pop	cx
	pop	ax
	stc				; fail but do not close port
	ret

ubsend3a:mov	cx,xmtcnt		; need count in cx too
	jmp	ubsend2			; try again to send		 [ohl]
ubsend1:mov	xmtcnt,0
	pop	es
	pop	cx
	pop	ax
	clc				; success, need failure case too
	ret
ubsend	endp

; Invoke internal TCP/IP NAWS Telnet Option when screen size changes
winupdate proc	far
ifndef	no_tcp
	test	nettype,tcpnet		; TCP/IP Telnet?
	jz	winupda1		; z = no
	push	es
	mov	ax,seg xmtbuf
	mov	es,ax
	mov	bx,offset xmtbufx	; buffer address in es:bx
	xor	cx,cx			; no data bytes to send
	mov	ah,bapinaws		; window size update request
	call	ktcpcom			; Far call TCP/IP Telnet code
	pop	es
	cmp	ah,3			; status, no session and above
	jae	winupda2		; ae = no session and above, fail
endif 	; no_tcp
winupda1:clc				; success
	ret
winupda2:call	ubclose			; failure, close the connection
	stc				; failure
	ret
winupdate endp

; Block send routine for Beame & Whiteside TCP
; Enter with char to be sent in AH. Destroys BX. [JRS]
bwsend	proc	near
	test	nettype,bwtcp		; active?
	jnz	bwsend0			; nz = yes
	stc
	ret
bwsend0:mov	bx,xmtcnt		; [JRS] count of chars in buffer
	mov	xmtbufx[bx],ah		; [JRS] put char in buffer
	inc	xmtcnt			; [JRS] count of items in this buffer
	and	ah,7fh			; [JRS] strip parity
	cmp	ah,CR			; [JRS] carriage return?
	jne	bwsend1			; [JRS] ne = no
	inc	bx			; [JRS]
	xor	al,al			; CR -> CR NUL
	cmp	tcpnewline,0		; newline mode is off?
	je	bwsend0a		; e = yes
	mov	al,LF			; CR -> CR/LF
bwsend0a:call	dopar			; [JRS] apply parity
	mov	xmtbufx[bx],al		; [JRS] append this char
	inc	xmtcnt			; [JRS]
	jmp	short bwsend2		; [JRS]

bwsend1:cmp	ttyact,0		; [JRS] are we in Connect mode?
	jne	bwsend2			; [JRS] ne = yes, send now
	cmp	xmtcnt,length xmtbuf-1	; [JRS] is buffer full? (room for lf)
	jb	bwsend3			; [JRS] b = no, else take special step
bwsend2:mov	ah,40h			; [JRS] write to device
	mov	bx,bwhandle		; [JRS] device handle
	mov	cx,xmtcnt		; [JRS] number of bytes to write
	mov	dx,offset xmtbufx	; [JRS] data buffer
	int	dos			; [JRS] ask dos to send it
	jc	bwsend4			; c = failed
	mov	xmtcnt,0		; [JRS] clear the buffer
bwsend3:clc				; [JRS] e = no, wait for more
	ret
bwsend4:call	bwclose			; failed to send, quit
	stc
	ret
bwsend	endp

; Dispatch prebuilt NetBios session scb, enter with bx pointing to scb.
; Returns status in al (and ah too). Allows STARLAN Int 2ah for netint.
NBSESSION PROC	NEAR	
	push	es			; save es around call
	mov	ax,ds
	mov	es,ax			; make es:bx point to scb in data seg
	mov	ax,exnbios		; funct 4 execute netbios, for Int 2ah
	int	netint			; use NetBios interrupt
	pop	es			; saved registers
	ret				; exit with status in ax
NBSESSION ENDP

ifndef	no_tcp
; Start a TCP/IP Telnet session. Set nettype if successful.
; Uses sescur to determine new (-1) or old (0..MAXSESSIONS-1) session.
; Returns carry clear if success, else carry set.
tcpstart proc	near
	mov	bx,sescur		; current session index for seslist
	or	bx,bx			; non-negative means active
	jns	tcpstar1		; ns = active
	mov	decbuf,0		; ensure cmd line is cleared too
	call	sesmgr			; init a fresh session
	jnc	tcpstar0		; nc = success
	mov	kstatus,ksgen		; global status for unsuccess
	ret				; carry = failure
tcpstar0:mov	sescur,bx		; get its index
	jmp	short tcpstar2		; start fresh session
tcpstar1:mov	al,seslist[bx]		; get the state value
	xor	ah,ah
	or	al,al			; is session active now?
	jns	tcpstar3		; ns = yes, else start fresh session

tcpstar2:mov	vtinited,0		; MSY terminal emulation
	mov	tekflg,0		; clear all graphics mode material
	call	ktcpopen		; open a new TCP connection
	jmp	short tcpstar4		; check status in AX

tcpstar3:call	ktcpswap		; switch to Telnet session in AL
tcpstar4:or	al,al			; Telnet status, successful?
	jns	tcpstar5		; ns = yes
	jmp	tcpclose		; fail, close this failed session

					; started/swapped sessions ok
tcpstar5:mov	bx,sescur		; current session, local basis
	or	bx,bx			; must be 0..5 to be usable
	js	tcpstar6		; s = not usable
	mov	seslist[bx],al		; update local session mgr with status
	push	ax
	push	si
	push	di
	mov	al,seshostlen		; length of name fields
	mul	bl			; times number of entries
	add	ax,offset sesname
	mov	si,ax			; name of current host
	mov	di,offset tcphost	; update main table
	call	strcpy
	push	bx
	shl	bx,1			; address words
	mov	ax,sesport[bx]		; port
	mov	tcpport,ax		; tcp port
	pop	bx
	pop	di
	pop	si
	pop	ax
	mov	pcnet,2			; net open and going
	or	nettype,tcpnet		; say a session is active (ses = BL)
	clc				; success
	ret
tcpstar6:stc
	mov	kstatus,ksgen		; global status for unsuccess
	ret				; carry = failure
tcpstart endp

; Close/shutdown/terminate a TCP/IP Telnet session. Sescur is session
; number, -1 closes all sessions and TCP/IP.
tcpclose proc	near
	mov	ax,sescur
	or	ax,ax			; close active (>=0) or all (-1)?
	js	tcpclo1			; s = all (-1)
	mov	bx,ax
	call	termswapdel		; delete term save block for ses BX
	mov	al,-1			; session closed marker
	xchg	al,seslist[bx]		; get tcpident from local list to AL
	or	al,al			; is this Telnet session active?
	jns	tcpclo1			; ns = yes, close it
	mov	cx,maxsessions		; number session slots
tcpclo0:cmp	seslist[bx],0		; session status, active?
	jge	tcpclo3a		; ge = yes, make this the active one
	inc	bx
	cmp	bx,maxsessions		; time to wrap?
	jb	tcpclo0a		; b = no
	xor	bx,bx			; wrap around
tcpclo0a:loop	tcpclo0
	jmp	short tcpclo4		; no active sessions, quit

tcpclo1:call	ktcpclose		; AL = close this particular session
	or	al,al			; status from ktcpclose
	jns	tcpclo3			; ns = have ses, -1 = no more sessions
	call	ktcpclose		; close TCP/IP as a whole after last
	and	nettype,not tcpnet	; clear activity flag
	mov	cx,maxsessions		; clear all local table entries
	xor	bx,bx
tcpclo2:mov	seslist[bx],-1		; status is inactive
	push	ax
	call	termswapdel		; delete terminal save block
	pop	ax
	inc	bx
	loop	tcpclo2
	
tcpclo3:call	tcptoses		; convert next tcp ident AL to sescur

tcpclo3a:mov	sescur,bx		; next new session
	or	bx,bx			; closing last session?
	js	tcpclo4			; s = yes, no more sessions
	call	termswapin		; swap in next session's emulator
	mov	portin,0		; reset the serial port for reiniting
	mov	port_tn.portrdy,0	; say the comms port is not ready
	mov	kbdflg,' '		; stay in connect mode
	stc
	ret
tcpclo4:mov	al,-1			; -1 means all sessions and network
	call	ktcpclose		; close the network
	call	serrst			; close the port
	mov	pcnet,0			; say no network
	and	nettype,not tcpnet
	mov	kbdflg,'C'		; quit connect mode
	stc
	ret
tcpclose endp
endif	; no_TCP

; Make a NetBios virtual circuit Session, given preset scb's from proc chknet.
; For Server mode, does a Listen to '*', otherwise does a Call to indicated
; remote node. Updates vcid number in scb's. Shows success or fail msg.
; Updates network status byte pcnet to 2 if session is established.
; Does nothing if a session is active upon entry; otherwise, does a network
; hangup first to clear old session material from adapter board. This is
; the second procedure to call in initializing the network for usage.
; If success nettype is set to netbios and return is carry clear; else 
; if failure nbclose is called to clean up connections and remove the nettype
; bit and return is carry set.
SETNET	PROC	NEAR			; NetBios, make a connection
	cmp	lposted,1		; Listen pending?
	je	setne0			; e = yes, exit now
	cmp	pcnet,1			; session active?
	jbe	setne1			; be = no
	clc
setne0:	ret
					; No Session
setne1:	cmp	xmt.scb_rname,'*'	; wild card?
	je	setne1a			; e = yes, do a Listen
	test	flags.remflg,dserver	; Server mode?
	jz	setne2			; z = no, file xfer or Connect
					; Server mode, post a Listen (async)
setne1a:mov	lsn.scb_rname,'*'	; accept anyone
	mov	ax,500
	call	pcwait			; 0.5 sec wait
	or	nettype,netbios		; set net type
	mov	lposted,1		; set listen interlock flag
	mov	lsn.scb_cmd,nlisten+nowait ; do LISTEN command, no wait
	push	bx			; save reg
	mov	bx,offset lsn
	call	nbsession
	pop	bx
	mov	pcnet,2			; net ready, Listen is active
	clc
	ret
setne2:					; Non-server (Client) mode
	cmp	starlan,0		; STARLAN?
	je	setne2a			; e = no
	cmp	xmt.scb_vrlen,0		; yes, using long name support?
	je	setne2a			; e = no
	push	es			; save reg
	push	ds
	pop	es			; make es:bx point to xmt scb
	push	bx			; save reg
	mov	bx,offset xmt		; use xmt scb for the call
	mov	xmt.scb_cmd,ncall	; CALL_ISN, vrname + vrlen are ready
	int	5bh			; STARLAN CALL Int 5bh, wait
	pop	bx
	pop	es			; restore regs
	jmp	short setne3		; finish up

					; Regular Netbios Call
setne2a:cmp	flags.comflg,'O'	; Opennet network? (FGR)
	jne	setne2b			; ne = no
	mov	xmt.scb_rname+15,'v' ; fix name to use VT port under nameserver
	mov	rcv.scb_rname+15,'v'
setne2b:mov	xmt.scb_cmd,ncall	; CALL, wait for answer
	push	bx			; save reg
 	mov	bx,offset xmt		; setup scb pointer
	call	nbsession
	pop	bx			; restore register

setne3:					; common Call completion, show status
	test	xmt.scb_err,0ffh	; is there a non-zero return code?
	jnz	setne3a			; nz = yes, do bad return
	or	al,al			; check error return
	jnz	setne3b			; nz = bad connection
	jmp	short setne4		; good connection so far

					; We try twice to allow for R1, and R3
					; versions of the nameservers
setne3b:cmp	flags.comflg,'O'	; Opennet netnork? (FGR)
	jne	setne3a			; ne = no
	mov	xmt.scb_rname+15,' '	; try generic port under nameserver
	mov	rcv.scb_rname+15,' '
					; Regular Netbios Call
	mov	xmt.scb_cmd,ncall	; CALL, wait for answer
	mov	bx,offset xmt		; setup scb pointer
	call	nbsession

					; common Call completion, show status
	test	xmt.scb_err,0ffh	; is there a non-zero return code?
	jnz	setne3a			; nz = yes, do bad return
	or	al,al			; check error return
	jz	setne4			; z = good connection so far
setne3a:mov	dx,offset nbadset	; say can't reach remote node
	mov	ah,prstr
	int	dos
	call	saynode			; show remote host node name
	jmp	setne4c
					; keep results of Call (vcid)
setne4:	mov	al,xmt.scb_vcid		; local session number
	mov	rcv.scb_vcid,al		; for receiver too
	mov	can.scb_vcid,al		; for sending Breaks
	mov	pcnet,2			; say session has started

; Here is the real difference between Opennet and generic Netbios.
; The Opennet Virtual Terminal Services exchange a small handshake at connect
; time. After that it is just normal Netbios data transfer between the host
; and Kermit.
	cmp	flags.comflg,'O'	; Opennet netnork? (FGR)
	jne	setne4o			; ne = no
	push	si
	push	di
	mov	si,offset ivt1str	; protocol string "iVT1\0"
	mov	di,offset xmtbufx	; buffer
	call	strcpy			; copy asciiz string
	mov	xmtcnt,5		; length of asciiz string, for send
	pop	di
	pop	si
	call	send			; send signon packet
; Note to Opennet purists: this just sends the handshake string to the host
; system without checking for an appropriate response. Basically, I am just
; very willing to talk to ANY VT server, and do the host response checking
; (if desired) in a Kermit script file (so its optional).

setne4o:cmp	flags.comflg,'E'	; ACSI version of EBIOS?
	jne	setnet4p		; ne = no
	mov	word ptr xmtbufx,6	; internal word count
	mov	xmtcnt,6		; four bytes
	mov	word ptr xmtbufx+2,acenable*256+3 ; raise DTR and RTS
	mov	bx,portval
	mov	ax,[bx].baud		; get baud rate index
	cmp	al,0ffh			; unknown baud rate?
	jne	setnet4pa		; ne = no
	mov	ax,11			; 2400,n,8,1
	mov	[bx].baud,ax		; set index into port info structure
setnet4pa:shl	ax,1			; make a word index
	mov	bx,ax
	mov	ax,clbddat[bx]		; Bios style speed setting
	cmp	al,0ffh			; unimplemented baud rate?
	jne	setnet4pb		; ne = no
	mov	bx,portval		; set index into port info structure
	mov	[bx].baud,3		; 110 baud -> 19200 for ACSI
	mov	al,3			; default to 19200,n,8,1
setnet4pb:mov	ah,acsetmode		; ACSI cmd for Mode set
	mov	word ptr xmtbufx+4,ax	; Mode setting
	or	nettype,acsi		; set special net operation
	call	send
setnet4p:
	test	flags.remflg,dregular+dquiet ; regular or quiet display?
	jnz	setne4c			; nz = yes, show only no-connect msg
	mov	dx,offset ngodset	; say good connection
	mov	ah,prstr
	int	dos
	call	saynode			; show remote host name
setne4c:cmp	pcnet,1			; check connection again
	ja	setne5			; a = good so far
	call	nbclose			; shut down NetBios
	stc				; set carry for failure
	ret
setne5:	or	nettype,netbios		; set net type
	clc				; carry clear for success
	ret
SETNET	ENDP

saynode	proc	near		; display node name on screen, si=name ptr
	push	ax
	push	cx
	push	dx
	push	si
	mov	ah,conout
	mov	si,offset nambuf	; remote node string
	mov	cx,64			; up to 64 bytes long
saynod1:cld
	lodsb				; get remote node name char into al
	mov	dl,al
	int	dos			; display it
	cmp	al,' '			; was it a space?
	jbe	saynod2			; be = yes, quit here
	loop	saynod1			; do all chars
saynod2:mov	ah,prstr
	mov	dx,offset crlf
	int	dos
	pop	si
	pop	dx
	pop	cx
	pop	ax
	ret
saynode	endp

LPOST	PROC	FAR		; Interrupt Post routine for Listen call
	push	ds		; update vcid and calling node name in scb's
	push	cx	
	push	es
	push	si
	push	di
	mov	cx,seg lsn		; reestablish data segment
	mov	ds,cx
	mov	es,cx
	mov	si,offset lsn.scb_rname	; copy remote name to rcv and xmt scbs
	push	si
	mov	di,offset rcv.scb_rname
	mov	cx,8			; 16 byte field
	cld
	rep	movsw
	mov	cx,8
	pop	si
	push	si
	mov	di,offset xmt.scb_rname
	rep	movsw
	mov	cx,8
	pop	si
	mov	di,offset nambuf	; and to nambuf for display
	rep	movsw
	mov	cl,lsn.scb_vcid		; local session number
	mov	rcv.scb_vcid,cl
	mov	xmt.scb_vcid,cl
	mov	can.scb_vcid,cl
	mov	lposted,0		; clear interlock flag
	mov	pcnet,2			; say net ready due to a Listen
	pop	di
	pop	si
	pop	es
	pop	cx
	pop	ds
	iret				; return from interrupt
LPOST	ENDP

; Close all network connections
NETHANGUP PROC	NEAR		     ; disconnect network session, keep names
	call	ubclose			; close Ungermann Bass Int 6Bh class
	mov	xmtcnt,0
	call	decclose		; close DECnet connection
	call	telapiclose		; close Telapi session
ifndef	no_tcp
	mov	sescur,-1		; say close all sessions, stop TCP/IP
	call	tcpclose		; close internal TCP/IP sessions
endif	; no_tcp
	call	ebiclose		; close EBIOS session
	call	tesclose		; close TES session, ignore failures
	call	nbclose			; close NetBios and ACSI
	call	bwclose			; close BW TCP
	mov	portin,0		; reset the serial port for reiniting
	mov	kbdflg,'C'		; quit connect mode
	clc
	ret
NETHANGUP ENDP

; Close NetBios connection. Clears nettype of netbios
nbclose proc	near
	test	nettype,(netbios+acsi)	; NetBios or ACSI active?
	jz	nbclose2		; z = no
	cmp	pcnet,0			; network started?
	je	nbclose2		; e = no
	test	nettype,acsi		; ACSI?
	jz	nbclose1		; z = no
	mov	word ptr xmtbufx,4	; four bytes
	mov	word ptr xmtbufx+2,acdisable*256 ; drop all modem leads
	mov	xmtcnt,4		; four bytes
	call	send			; tell the server
	mov	ax,500			; wait 0.5 sec
	call	pcwait			; for pkt to reach host
nbclose1:push	bx			; NetBios network
	mov	bx,offset can 
	mov	can.scb_cmd,ncancel	; set cancel op code
	mov	can.scb_baddr,offset lsn ; cancel listens
	mov	lposted,0		; say no listen
	call	nbsession
	mov	can.scb_baddr,offset rcv ; cancel receives
	call	nbsession
	mov	rposted,0		; say no receives posted
	mov	can.scb_baddr,offset xmt ; cancel sends
	call	nbsession
	mov	sposted,0		; say no sends posted
	mov	xmtcnt,0		; reset output buffer counter
	mov	xmt.scb_cmd,nhangup	; hangup, and wait for completion
	mov	bx,offset xmt
	call	nbsession
	pop	bx
	and	nettype,not (netbios+acsi)
	mov	pcnet,1			; net but no connection
	mov	port_nb.portrdy,0	; say the comms port is not ready
	mov	portin,0
	mov	kbdflg,'C'		; quit connect mode
	stc
nbclose2:ret
nbclose endp

decclose proc	near
	test	nettype,declat		; DECnet LAT active?
	jz	decclos3		; z = no
	mov	dx,lathand		; LAT handle
	or	dx,dx			; invalid handle?
	jz	decclos2		; z = yes
	mov	ax,latclose
	int	latint
	mov	ax,latseg		; allocated memory segment
	or	ax,ax			; was it used?
	jz	decclos1		; z = no
	push	es
	mov	es,ax
	mov	ah,freemem		; free allocated memory segment @ES
	int	dos			; free the block
	pop	es
	mov	latseg,0		; clear remembered segment address
	mov	word ptr latscbptr+2,0	; clear this pointer (same as latseg)
decclos1:cmp	latversion,4		; version 4?
	jb	decclos2		; b = no
	push	es
	les	bx,latscbptr+4		; address of exterior SCB
	mov	dx,lathand
	mov	ax,latscbfree		; free SCB interior to LAT
	int	latint
	mov	word ptr latscbptr+2,0
	pop	es
decclos2:and	nettype,not declat	; remove net type bit
	mov	lathand,0		; invalidate the handle

decclos3:test	nettype,decnet		; DEC CTERM active?
	jz	decclos4		; z = no
	mov	dx,decneth		; DECnet CTERM handle
	or	dx,dx			; invalid handle?
	jz	decclos5		; z = yes
	mov	ax,dclose		; CTERM close
	int	decint
	mov	decneth,0		; invalidate the handle
	mov	ax,decseg		; allocated memory segment
	or	ax,ax			; ever used?
	jz	decclos4		; z = no
	mov	es,ax
	mov	ah,freemem		; free allocated memory segment @ES
	int	dos			; free the block
	mov	decseg,0		; clear remembered segment address
decclos4:and	nettype,not decnet	; remove net type bit
	mov	port_dec.portrdy,0	; say port is not ready
	mov	pcnet,0			; say no network
	mov	portin,0
	mov	kbdflg,'C'		; quit connect mode
	stc
decclos5:ret
decclose endp

telapiclose proc near
	test	nettype,telapi		; Novell TELAPI?
	jz	telapiclo1		; e = no
	mov	xmtcnt,0
	push	bx
	mov	bx,telses		; session number
	mov	ah,telclose		; close session
	int	rs232
	pop	bx
	and	nettype,not telapi	; remove active net type bit
	mov	portin,0
	mov	pcnet,1
	push	bx
	mov	bx,portval
	mov	[bx].portrdy,0		; say port is not ready
	pop	bx
	mov	kbdflg,'C'		; quit connect mode
	stc
telapiclo1:ret
telapiclose endp

; Close EBIOS communications link
ebiclose proc near
	test	nettype,ebios		; EBIOS?
	jz	ebiclos1		; z = no
	mov	dx,ebport		; EBIOS
	mov	ax,ebmodem*256+0	; reset outgoing DTR and RTS leads
	int	rs232
	push	es
	mov	bx,ds
	mov	es,bx
	mov	bx,offset ebcoms	; es:bx is parameter block ebcoms
	mov	ah,ebredir		; do redirect away from EBIOS
	mov	ebcoms+1,0		; set port to hardware
	int	rs232
	mov	dx,ebport		; port 0..3
	mov	bx,offset rcvbuf	; receive buffer for EBIOS
	xor	cx,cx			; set to zero to stop buffering
	mov	ax,ebbufset*256+2	; reset rcvr buffered mode
	int	rs232
	xor	cx,cx
	mov	bx,offset xmtbuf	; EBIOS transmitter work buffer
	mov	ax,ebbufset*256+1	; reset xmtr buffered mode
	int	rs232
	pop	es
	mov	bx,offset portb1	; use Bios data structure
	mov	ax,type prtinfo		; portinfo item size
	mul	dl			; times actual port (0..3)
	add	bx,ax			; new portb<n> offset
	mov	[bx].portrdy,0		; say port is not ready
	and 	nettype,not ebios
	mov	portin,0
	mov	kbdflg,'C'		; quit connect mode
	stc
ebiclos1:ret
ebiclose endp

; TES close session
tesclose proc	near
	test	nettype,tes		; TES?
	jz	tesclo3			; z = no
	mov	temp,0			; retry counter
	call	tesstate		; get session state to AH
	test	ah,2			; is this session active?
	jz	tesclo2			; z = no, but keep held sessions
	mov	ah,tesdrop		; drop a session
	mov	al,tesses		; the session
	call	tes_service
	or	ah,ah			; status
	jz	tesclo2			; z = success
	stc				; say failure
	ret
tesclo2:and	nettype,not tes		; successful hangup
	mov	tesses,0		; clear session
	mov	port_tes.portrdy,0	; say the comms port is not ready
	mov	pcnet,1			; say network but no session
	mov	portin,0
	mov	kbdflg,'C'		; quit connect mode
	stc
	ret
tesclo3:clc
	ret
tesclose endp

; Ungermann Bass. Do a disconnect from the current connection.
ubclose proc	near
	push	ax
	push	cx
	test	nettype,netone		; UB network has been activated?
	jz	ubclos4			; z = no
	mov	ax,ncistat		; get status			 [ohl]
	int	netci
 	or	ch,ch			; check if we have a connection	 [ohl]
	jz	ubclos2			; z = no			 [ohl]
	mov	ax,ncicont		; control function		 [ohl]
	mov	cx,ncidis		; say disconnect		 [ohl]
	int	netci
ubclos1:call	ubrecv			; read response from net cmdintpr[ohl]
	jnc	ubclos1			; continue till no chars	 [ohl]
	mov	ax,ncistat		; get status again
	int	netci
	or	ch,ch			; check if we have a connection
	jnz	ubclos3			; nz = yes, had more than one
ubclos2:and	nettype,not netone	; remove network type
	mov	pcnet,1			; net but no connection
	mov	port_ub.portrdy,0	; say the comms port is not ready
	mov	portin,0
	mov	kbdflg,'C'		; quit connect mode
	stc
ubclos3:pop	cx
	pop	ax
	ret
ubclos4:test	nettype,bapi+tcpnet	; 3Com BAPI or TCP Telnet in use?
	jz	ubclos6			; z = no
	mov	ah,bapieecm		; control Enter Command Mode char
	mov	al,1			; enable it
ifndef	no_tcp
	test	nettype,tcpnet		; TCP/IP Telnet?
	jz	ubclos5			; z = no
	mov	bx,sescur		; current session
	mov	seslist[bx],-1		; say session is closed
	call	tcpclose		; tell Telnet manager about closure
	jmp	short ubclos3
endif	; no_tcp
ubclos5:int	bapiint
	and	nettype,not bapi	; remove BAPI bit
	jmp	short ubclos3
ubclos6:test	nettype,telapi		; Novell TELAPI Int 6Bh interface?
	jz	ubclos3			; z = no
	call	telapiclose
	jmp	short ubclos3
ubclose endp

; Ungermann Bass/Novell. Put current connection on Hold. Requires keyboard
; verb \knethold to activate. Should return to Connect mode to see NASI. [jrd]
ubhold	proc	near
	push	ax
	push	cx
	test	nettype,netone		; UB/Novell network active?
	jz	ubhold1			; z = no
	mov	ax,ncistat		; get link status
	int	netci
	or	ch,ch			; connection active?
	jz	ubhold1			; z = no
	mov	ax,ncicont		; control command
	mov	cl,ncihld		; place circuit on HOLD
	int	netci
	jmp	short ubhold3
ubhold1:test	nettype,bapi		; 3Com BAPI
	jz	ubhold2			; z = no
	mov	ah,bapiecm		; do Enter Command Mode char
ubhold1b:int	bapiint
	jmp	short ubhold3
ubhold2:test	nettype,tes		; TES?
	jz	ubhold3			; z = no
	mov	ah,testalk		; TES get command interpreter
	mov	dx,tesport		; "serial port"
	int	rs232
ubhold3:pop	cx
	pop	dx
	clc
	ret
ubhold	endp

; Beame & Whiteside TCP close session [JRS]
bwclose	proc	near			; [JRS]
	mov	bx,bwhandle		; [JRS] get file handle
	or	bx,bx			; [JRS] if zero, we're done
	jz	bwclos1			; [JRS] z= done
	mov	ah,close2		; [JRS] close device
	int	dos			; [JRS]
	mov	bwhandle,0		; [JRS] clear the handle value
bwclos1:and	nettype,not bwtcp
	mov	portin,0		; say serial port is closed
	mov	pcnet,0
	mov	port_tn.portrdy,0	; say port is not ready
	mov	kbdflg,'C'		; quit connect mode
	clc				; [JRS]	flag success
	ret
bwclose	endp

; Called when Kermit exits. Name passed to mssker by initialization lclini
; in word lclexit.
NETCLOSE PROC	NEAR			; close entire network connection
	call	nethangup		; close connections
	push	bx
	mov	bx,offset xmt
	cmp	xmt.scb_lname,' '	; any local name?
	je	netclo2			; e = none
	mov	xmt.scb_cmd,ndelete	; delete our local Kermit name
	call	nbsession		;  from net adapter board
	mov	xmt.scb_lname,' '	; clear name
netclo2:pop	bx
	mov	pcnet,0			; say no network
	mov	lnamestat,0		; local name not present, inactive
	mov	port_nb.portrdy,0	; say comms port is not ready
	and	nettype,not (netbios+acsi) ; remove network kind
netclo1:clc
	ret
NETCLOSE ENDP	

; Start connection process to network. Obtains Network board local name
; and appends '.K' to form Kermit's local name (removed when Kermit exits).
; If no local name is present then use name 'mskermit.K'.
; Sets local name in scb's for xmt, rcv, lsn. (Does not need DOS 3.x)
; Sets NETDONE pointer to procedure netclose for Kermit exit.
; Verifies existance of interrupt 5ch support, verifies vendor specific
; support for BREAK and other features, sets network type bit in nettype,
; sets BREAK support in nsbrk, hangsup old session if new node name given,
; fills in local and remote node names and name number in scbs (including ISN
; names for STARLAN), and sets network status byte pcnet to 0 (no net) or
; to 1 (net ready). This is the first procedure called to init network usage.
; Byte count of new host name is in temp from COMS.
chknet	proc	near
	cmp	flags.comflg,'U'	; Ungermann Bass network?
	jb	chknea			; b = no, (ae includes U and W)
	mov	pcnet,0			; force reactivation of UB net
chknea:	cmp	pcnet,2			; session active now?
	jb	chknec			; b = no
	cmp	newnambuf,0		; non-zero if new destination name
	je	chkneb			; e = none, resume old session
	call	chknew			; Resume current session?
	jnc	chkneb			; nc = no
chknex:	ret				; resume old one
chkneb:	jmp	chknet1			; skip presence tests

chknec:				; setup addresses and clear junk in scb's
	cmp	pcnet,0			; have we been here already?
	je	chkned			; e = no
	jmp	chknet1			; yes, skip init part
chkned:	mov	xmtcnt,0		; say buffer is empty
	mov	nsbrk,0			; assume no BREAK across network
	and	nettype,not netbios	; say no NetBios network yet
	mov	starlan,0		; no Starlan yet
	call	chknetbios		; is Netbios present?
	jc	chknet0			; c = not present
	or	nettype,netbios		; say have NetBios network
	call	chkstarlan		; is AT&T StarLAN present?
	jc	chknet1			; c = no
	inc	starlan			; say using STARLAN, have int 2ah
	mov	nsbrk,1			; network BREAK supported
	jmp	short chknet1

chknet0:mov	pcnet,0			; no network yet
	push	dx
	mov	ah,prstr
	mov	dx,offset nonetmsg	; say network is not available
	int	dos
	pop	dx
	stc				; set carry for failure
	ret				; and exit now

					; net ready to operate
chknet1:mov	port_nb.portrdy,1	; say the comms port is ready
	cmp	newnambuf,0		; non-zero if new destination name
	jne	chkne1e			; ne = new name given
	jmp	chknet2			; nothing, so leave names intact
chkne1e:cmp	pcnet,2			; is session active now?
	jb	chkne1d			; b = no
	call	nbclose			; close to clear old connection

chkne1d:push	si			; start fresh connection
	push	di
	push	es
	push	ds
	pop	es			; make es:di point to data segment
	cld
	mov	cx,8			; 16 bytes for a node name
	mov	ax,'  '			; first, fill with spaces
	mov	di,offset xmt.scb_rname ; remote name field, clear it
	rep	stosw
	cmp	starlan,0		; STARLAN?
	jne	chkne1b			; ne = no
					; begin STARLAN section	
	mov	xmt.scb_vrname,0	; STARLAN var length name ptr
	mov	xmt.scb_vrname+2,0	; segement of name	
	mov	xmt.scb_vrlen,0		; and its length
	mov	di,offset nambuf	; source of text
	mov	dx,di
	call	strlen			; length of new name to cx
	cmp	cx,16			; > 16 chars in remote node name?
	ja	chkne1a			; a = yes, too long for Netbios
	mov	al,'/'			; scan for slashes in name
	cld
	repne	scasb			; look for the slash
	jne	chkne1b		; ne = none, do regular Netbios name storage
chkne1a:				; STARLAN ISN long remote name support
	mov	dx,offset nambuf	; STARLAN var length name ptr
	mov	xmt.scb_vrname,dx	
	mov	xmt.scb_vrname+2,data	; segment of remote name
	call	strlen			; get name length again (in cx)
	mov	xmt.scb_vrlen,cl	; indicate its length
	jmp	short chkne1c		; copy blanks in remote name field
					; end STARLAN section

chkne1b:				; Regular Netbios form
	mov	si,offset nambuf	; source of text
	mov	dx,si
	call	strlen			; length to cx
	cmp	cx,16
	jbe	chkne1f			; be = in bounds
	mov	cx,16			; chop to 16 (prespace filled above)
chkne1f:mov	di,offset xmt.scb_rname ; destination is remote name
	rep	movsb			; copy text to transmitter's scb
chkne1c:mov	cx,8			; 8 words
	mov	si,offset xmt.scb_rname ; from here
	mov	di,offset rcv.scb_rname ; to receiver's scb also
	rep	movsw
	pop	es
	pop	di
	pop	si
	mov	newnambuf,0		; say new name is established now

chknet2:cmp	pcnet,0			; started net?
	je	chknet2c		; e = no
	clc
	ret				; else quit here
chknet2c:call	setnbname		; establish local Netbios name
	jnc	chknet9			; nc = success
	ret
chknet9:mov	pcnet,1			; network is present (but not active)
	mov	al,xmt.scb_num		; name number
	mov	rcv.scb_num,al
	mov	lsn.scb_num,al
	push	es
	push	si
	push	di
	mov	si,ds
	mov	es,si
	mov	si,offset xmt.scb_lname
	mov	di,offset rcv.scb_lname ; put in receiver scb too
	mov	cx,8
	rep	movsw
	mov	cx,8
	mov	si,offset xmt.scb_lname
	mov	di,offset lsn.scb_lname	; in Listen scb also
	rep	movsw
	pop	si
	pop	di
	pop	es
	clc
	ret
chknet	endp

; Service SET NETBIOS-NAME name   command at Kermit prompt level
setnbios	proc	near
	mov	dx,offset decbuf	; work buffer
	mov	bx,offset setnbhlp2	; help
	mov	ah,cmword		; get netbios name
	call	comnd
	jc	setnb3			; c = failure
	push	ax			; save char count
	mov	ah,cmeol		; get a confirmation
	call	comnd
	pop	ax
	jc	setnb3			; c = failure
	cmp	lnamestat,2		; is name fixed already?
	je	setnb2			; e = yes
	mov	cx,ax			; char count
	jcxz	setnb3			; z = enter no new name
	cmp	cx,16			; too long?
	jbe	setnb1			; be = no
	mov	cx,16			; truncate to 16 chars
setnb1:	mov	si,offset decbuf	; work buffer
	mov	di,offset deflname	; default name
	push	es
	mov	ax,ds
	mov	es,ax
	cld
	rep	movsb			; copy the name
	mov	al,' '			; pad with spaces
	mov	cx,offset deflname+16	; stopping place+1
	sub	cx,di			; number of spaces to write
	rep	stosb			; won't do anything if cx == 0
	pop	es
	mov	lnamestat,1		; say have new local name
	call	setnbname		; do the real NetBios resolution
	ret				; returns carry set or clear
setnb2:	mov	ah,prstr
	mov	dx,offset setnbad	; say name is fixed already
	int	dos
setnb3:	stc				; failure
	ret
setnbios endp

chknetbios proc	near			; Test for Netbios presence, IBM way
	push	es
	mov	ah,35h			; DOS get interrupt vector
	mov	al,netint		; the netbios vector
	int	dos			; returns vector in es:bx
	mov	ax,es
	or	ax,ax			; undefined interrupt?
	jz	chknb2			; z = yes
	cmp	byte ptr es:[bx],0cfh	; points at IRET?
	je	chknb2			; e = yes
	mov	xmt.scb_cmd,7fh ; presence test, 7fh is illegal command code
	mov	xmt.scb_err,0		; clear response field
	push	bx
	mov	bx,offset xmt		; address of the session control block
	call	nbsession		; execute operation
	pop	bx
	mov	al,xmt.scb_err		; get response
	cmp	al,3	  	  ; 'illegal function', so adapter is ready
	je	chknb1			; e = success
	or	al,al
	jnz	chknb2			; nz = not "good response" either
chknb1:	pop	es
	clc				; netbios is present
	ret
chknb2:	pop	es
	stc				; netbios is not present
	ret
chknetbios endp

chkstarlan proc near			; AT&T STARLAN board check
	push	es
	mov	ah,35h			; DOS get interrupt vector
	mov	al,2ah			; PC net vector 2ah
	int	dos			; returns vector in es:bx
	mov	ax,es
	or	ax,ax			; undefined interrupt?
	jz	chkstar1		; z = yes
	cmp	byte ptr es:[bx],0cfh	; points at IRET?
	je	chkstar1		; e = yes
	xor	ah,ah			; vendor installation check on int 2ah
	xor	al,al			; do error retry
	int	2ah			; session level interrupt
	cmp	ah,0ddh			; 0ddh = magic number, success?
	jne	chkstar1		; ne = no
					; Test for vector
	mov	ah,35h			; DOS get interrupt vector
	mov	al,5bh			; 5bh = STARLAN netbios ext'd vector
	int	dos			; returns vector in es:bx
	mov	ax,es
	or	ax,ax			; undefined interrupt?
	jz	chkstar1		; z = yes
	cmp	byte ptr es:[bx],0cfh	; points to IRET?
	je	chkstar1		; e = yes
	pop	es
	clc				; StarLAN is present
	ret
chkstar1:pop	es
	stc				; StarLAN is not present
	ret
chkstarlan endp

; Put a local name into the Netbios name table, ask user if conflicts.
setnbname proc	near
	cmp	lnamestat,2		; validiated local Netbios name?
	jb	setnbn1			; b = no
	ret				; else quit here
setnbn1:call	chknetbios		; Netbios presence check
	jnc	setnbn1a		; nc = present
	ret

setnbn1a:mov	ah,prstr
	mov	dx,offset netmsg1	; say checking node name
	int	dos
	push	word ptr xmt.scb_rname	; save first two bytes (user spec)
	mov	byte ptr xmt.scb_rname,'*' ; call to local name
	push	bx
	mov	xmt.scb_cmd,naustat	; get Network Adapter Unit status
	mov	bx,offset xmt
	call	nbsession
	pop	bx
	pop	word ptr xmt.scb_rname	; restore remote name first two bytes
setnbn2:push	es
	push	si
	push	di
	mov	si,ds
	mov	es,si
	cld
	mov	si,offset deflname	; use default local name
	mov	di,offset xmt.scb_lname ; where to put it in scb
	mov	cx,14			; 16 bytes minus extension of '.K'
	cld				; append extension of '.K' to loc name
setnbn5:cmp	byte ptr[si],' ' ; find first space (end of regular node name)
	jbe	setnbn6			; be = found one (or control code)
	movsb				; copy local name to scb
	loop	setnbn5			; continue though local name
setnbn6:cmp	word ptr [di-2],'K.'	; is extension '.K' present already?
	je	setnbn7			; e = yes, nothing to add
	cmp	word ptr [di-2],'k.'	; check lower case too
	je	setnbn7			; e = yes, nothing to add
	mov	word ptr [di],'K.'	; append our extension of '.K'
	add	di,2			; step over our new extension
	sub	cx,2
					; complete field with spaces
setnbn7:add	cx,2			; 15th and 16th chars
	mov	al,' '			; space as padding
	rep	stosb
	pop	di			; clean stack from work above
	pop	si
	pop	es

	push	bx			; Put our new local name in NAU
	mov	xmt.scb_cmd,nadd	; ADD NAME, wait
	mov	bx,offset xmt
	call	nbsession
	pop	bx
	mov	al,xmt.scb_err		; get error code
	or	al,al			; success?
	jnz	setnbn8			; nz = no
	jmp	setnbn12		; success
setnbn8:cmp	al,0dh			; duplicate name in local table?
	je	setnbn9			; e = yes
	cmp	al,16h			; name used elsewhere?
	je	setnbn9			; e = yes
	cmp	al,19h			; name conflict?
	je	setnbn9			; e = yes
	push	ax
	mov	ah,prstr		; another kind of error
	mov	dx,offset chkmsg1	; say can't construct local name
	int	dos
	pop	ax
	call	decout			; display it (in al)
	mov	ah,prstr
	mov	dx,offset crlf
	int	dos
	stc				; set carry for failure
	ret

setnbn9:mov	ah,prstr		; ask for another name
	mov	dx,offset chkmsg2	; prompt message
	int	dos
	mov	ah,conout		; show name itself
	push	si
	push	cx
	mov	cx,16			; 16 bytes in name field
	mov	si,offset xmt.scb_lname
setnbn10:lodsb				; get name char into al
	mov	dl,al
	int	dos
	mov	byte ptr[si-1],' '	; clear old name as we go
	loop	setnbn10
	pop	cx
	pop	si
	mov	ah,prstr
	mov	dx,offset chkmsg3	; rest of prompt
	int	dos

	mov	ah,0ah			; read buffered line from stdin
	mov	dx,offset xmtbuf+58	; where to put text (xmtbuf+60=text)
	mov	xmtbuf+58,15		; buf capacity, including cr at end
	mov	xmtbuf+59,0		; say text in buffer = none
	int	dos
	jc	setnbn11		; c = error
	cmp	xmtbuf+59,0		; any bytes read?
	je	setnbn11		; e = no, exit failure
	mov	ah,prstr		; say rechecking name
	mov	dx,offset netmsg1
	int	dos
	push	es
	mov	si,ds
	mov	es,si
	mov	si,offset xmtbuf+60	; where text went
	mov	di,offset deflname	; where local name is stored
	mov	cx,8
	cld
	rep	movsw			; copy 14 chars to deflname
	mov	byte ptr [di],0		; null terminator, to be safe
	pop	es
	mov	lnamestat,1		; say local name specified
	jmp	setnbn2			; go reinterpret name

setnbn11:stc				; set carry for failure
	ret

setnbn12:mov	dx,offset netmsg2	 ; say net is going
	mov	ah,prstr
	int	dos
	push	si
	push	di
	push	es
	mov	si,ds
	mov	es,si
	mov	si,offset xmt.scb_lname ; display our local name
	mov	di,offset deflname	; local Netbios name
	mov	ah,conout
	mov	cx,16
	cld
setnbn13:lodsb				; byte from si to al
	stosb				; and store in local Netbios name
	mov	dl,al
	int	dos			; display it
	loop	setnbn13
	pop	es
	pop	di
	pop	si
	mov	lnamestat,2		; say local name is fixed now
	mov	ah,prstr
	mov	dx,offset crlf		; add cr/lf
	int	dos
	clc				; carry clear for success
	ret
setnbname endp

; Network session exists. Tell user and ask for new node or Resume.
; Returns carry set if Resume response, else carry clear for New.
chknew	proc	near
	mov	ax,takadr		; we could be in a macro or Take file
	push	ax			; save Take address
	mov	al,taklev
	xor	ah,ah
	push	ax			; and Take level
	push	dx
	mov	dx,size takinfo		; bytes for each current Take
	mul	dx			; times number of active Take/macros
	pop	dx
	sub	takadr,ax		; clear Take address as if no
	mov	taklev,0		;  Take/macro were active so that

	mov	dx,offset naskpmt	; prompt for New or Resume
	call	prompt
	mov	dx,offset nettab	; table of answers
	xor	bx,bx			; help for the question
	mov	ah,cmkey		; get answer keyword
	mov	comand.cmcr,1		; allow bare CR's
	call	comnd
	mov	comand.cmcr,0		; dis-allow bare CR's
	jc	chknew1			; c = failure, means Resume
	push	bx
	mov	ah,cmeol		; get a confirm
	call	comnd
	pop	bx
	jc	chknew1			; c = failure, resume session
	or	bx,bx			; 0 for new?
	jz	chknew1			; z = yes, return carry clear
	stc				; set carry for resume
chknew1:pop	ax
	mov	taklev,al		; restore Take level
	pop	takadr			; restore Take address
	ret				; carry may be set
chknew	endp

;					; [ohl] ++++
; Verifies existance of interrupt 6Bh support, verifies vendor specific
; support for BREAK and other features, sets network type bit in nettype,
; sets BREAK support in nsbrk and sets network status byte pcnet to 0
; (no net) or to 1 (net ready). This is the first procedure called to
; init Ungermann-Bass NETCI terminal port network usage.
chkub  proc    near
	push    bx
        push    es                      ; Test for vector
        mov     ah,35h                  ; DOS get interrupt vector
        mov     al,6bh                  ; 6bh = Net/One command interpreter
					;  interface, with break support
        int     dos                     ; returns vector in es:bx
	mov	ax,es			; is vector in rom bios?
	or	ax,ax			; undefined vector?
	jz	chkub0			; z = yes
	cmp	byte ptr es:[bx],0cfh	; points at IRET?
	je	chkub0			; e = yes
;some	mov	al,0ffh			; test value (anything non-zero)
;emulators mov	ah,2			; function code for testing net board
;flunk	int	netci
;this	or	al,al			; al = 0 means board is ok
;test	jnz	chkub0			; nz = not ok
	pop	es
	pop	bx
        mov     nsbrk,1                 ; network BREAK supported
        or      nettype,netone		; say have Net/One
	clc				; return success
	ret

chkub0:	pop	es			; clean stack from above
	pop	bx
	push    ax
        push    dx
        mov     ah,prstr
        mov     dx,offset nonetmsg      ; say network is not available
        int     dos
        pop     dx
        pop     ax
        stc                             ; set carry for failure
        ret                             ; and exit now
chkub  endp

endif	; no_network

; local routine to see if we have to transmit an xon
chkxon	proc	near
	test	flowcnt,1+4		; doing output/RTS flow control?
	jz	chkxo1			; z = 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
	test	flowcnt,4		; using RTS/CTS kind?
	jz	chkxo2			; z = no
	cmp	flags.comflg,4		; using uart?
	ja	chkxo1			; a = no, ignore situation
	push	ax
	push	dx
	mov	dx,modem.mddat		; serial port base address
	add	dx,4			; increment to control register
	in	al,dx
	or	al,2			; assert RTS for flow-on
	push	ax
	in	al,ppi_port		; delay
	pop	ax
	out	dx,al
	and	xofsnt,off		; remember we've sent the "xon"
	pop	dx
	pop	ax
					; do software flow control too
chkxo2:	test	flowcnt,1		; using outgoing XON/XOFF kind?
	jz	chkxo1			; z = no
	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 XOFF, or equivalent.
; Requires that the port be initialized before hand.
; Do not send flow control if doing half duplex.

IHOSTS	PROC	NEAR
	push	ax		; save the registers
	push	bx
	push	cx
	push	dx
	mov	xofrcv,off	; clear old xoff received flag
	mov	xofsnt,off	; and old xoff sent flag
	cmp	portin,0	; is a comms port active?
	jle	ihosts1		; le = no
	mov	bx,portval
	test	flowcnt,4	; using CTS/RTS?
	jnz	ihosts2		; nz = yes
	mov	ah,byte ptr [bx].flowc ; put wait flow control char in ah
	or	ah,ah		; check for null char
	jz	ihosts1		; z = null, don't send it
	cmp	dupflg,0	; full duplex?
	jne	ihosts1		; ne = no, half
	call	outchr		; send it
	jmp	short ihosts1
ihosts2:cmp	flags.comflg,4		; using uart?
	ja	ihosts1			; a = no, ignore situation
	mov	dx,modem.mddat		; serial port base address
	add	dx,4			; increment to control register
	in	al,dx
	and	al,not 2		; clear RTS for flow-off
	push	ax
	in	al,ppi_port		; delay
	pop	ax
	out	dx,al
	or	xofsnt,bufon		; remember we've sent the "xoff"
ihosts1:pop	dx
	pop	cx
	pop	bx
	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. Do not send flow control if doing half duplex.
IHOSTR	PROC	NEAR
	push	ax		; save regs
	push	bx
	push	cx
	mov	xofrcv,off	; clear old xoff received flag
	mov	xofsnt,off	; and old xoff sent flag
	cmp	portin,0	; is a comms port active?
	jle	ihostr1		; le = no
	mov	bx,portval
	test	flowcnt,4	; using CTS/RTS?
	jnz	ihostr2		; nz = yes
	mov	ah,byte ptr [bx].flowc+1; put go-ahead flow control char in ah
	or	ah,ah		; check for null char
	jz	ihostr1		; z = null, don't send it
	cmp	dupflg,0	; full duplex?
	jne	ihostr1		; ne = no, half
	call	outchr		; send it (release Host's output queue)
	jmp	short ihostr1
ihostr2:cmp	flags.comflg,4		; using uart?
	ja	ihostr1			; a = no, ignore situation
	push	dx
	mov	dx,modem.mddat		; serial port base address
	add	dx,4			; increment to control register
	in	al,dx
	or	al,2			; assert RTS for flow-on
	push	ax
	in	al,ppi_port		; delay
	pop	ax
	out	dx,al
	and	xofsnt,off		; remember we've sent the "xon"
	pop	dx
ihostr1:pop	cx
	pop	bx
	pop	ax
	ret
IHOSTR	ENDP

; Send a break out the current serial port.  Returns normally.
; Do both regular and long Break.
; Networks use flags.comflg so that more than one net can be active
SENDBR	PROC	NEAR
	push	cx		; Regular Break entry point
	mov	cx,275		; 275 milliseconds in regular Break
	call	sendbw		; call worker routine to do it
	pop	cx
	mov	flags.cxzflg,0	; clear in case Control-Break
	clc			; don't exit Connect mode
	ret
SENDBL:	push	cx		; Long Break entry point
	mov	cx,1800		; 1.8 second long break
	call	sendbw		; call worker routine to do it
	pop	cx
	mov	flags.cxzflg,0	; clear in case Control-Break
	clc			; don't exit Connect mode
	ret
				; worker - send Break for cx millisec
sendbw:	mov	al,flags.comflg	; get type of port
	cmp	al,4		; running on a UART or network?
	ja	sendbw2		; a = network
	push	dx		; UART BREAK
	mov	dx,modem.mdcom	; port address
	in	al,dx		; get current setting
	push	ax		; save setting on the stack
	or	al,brkbit	; set send-break bit(s)
	out	dx,al		; start the break
	mov	ax,cx		; # of ms to wait
	call	pcwait		; hold break for desired interval
	pop	ax		; restore Line Control Register
	out	dx,al		; stop the break
	pop	dx
	ret
sendbw2:
ifndef	no_network
	cmp	al,'N'		; is this a NetBios network port?
	jne	sendbw3		; ne = no
	cmp	starlan,0	; STARLAN: network break supported?
	jne	sendbw2a	; ne = no
	push	bx
	push	es		; save es around call
	push	ds
	pop	es		; make es:bx point to scb in data segment
	mov	bx,offset can	; use Cancel control block
	mov	can.scb_cmd,netbrk ; send net Break command
	int	5bh		; use network Break interrupt
	pop	es		; saved registers
	pop	bx
sendbw2a:ret

sendbw3:cmp	al,'E'			; EBIOS?
	jne	sendbw6			; ne = no
	test	nettype,acsi		; using EBIOS.COM?
	jnz	sendbw4			; nz = no
	push	ax
	push	dx
	mov	dx,ebport		; port 0..3
	mov	ah,ebbreak		; EBIOS send BREAK
	int	rs232			; bios send
	pop	dx
	pop	ax
	ret
sendbw4:call	send			; send current buffer first
	mov	word ptr xmtbufx,4	; four bytes in packet
	mov	word ptr xmtbufx+2,acbreak*256+0 ; BREAK cmd, null char
	call	send			; send the command
	ret

sendbw6:cmp	al,'U'			; UB NETCI or Novell NASI/NACS?
	je	sendbw6a		; e = yes
	cmp	al,'W'			; NASI/NACS?
	jne	sendbw7			; ne = no
sendbw6a:push	cx			; UB port send break		 [ohl]
	mov	ax,ncicont+0	; call control, use 0 for network port num
	mov	cl,ncibrk		; request break			 [ohl]
	int	netci		; Net/One command interface int. (6Bh)	 [ohl]
	pop	cx
	ret

sendbw7:cmp	al,'M'			; Meridian SuperLAT?
	je	sendbw7a		; e = yes
	cmp	al,'D'			; DECnet?
	jne	sendbw9			; ne = no
	test	nettype,declat		; LAT?
	jz	sendbw9			; z = no, CTERM cannot send a BREAK
sendbw7a:mov	ax,latbreak		; LAT BREAK command
	push	dx
	mov	dx,lathand		; LAT handle
	int	latint
	pop	dx
	ret

sendbw9:cmp	al,'C'			; 3Com BAPI or TCP Telnet?
	je	sendbw9a		; e = yes
	cmp	al,'t'
	jne	sendbw10		; ne = no
sendbw9a:mov	ah,bapibrk		; BAPI, send BREAK
	xor	dh,dh			; session id of 0 (external sessions)
ifndef	no_tcp
	cmp	al,'t'			; TCP/IP Telnet?
	jne	sendbw9b		; ne = no
	call	ktcpcom			; Far call TCP/IP Telnet code
	ret
endif	; no_tcp
sendbw9b:int	bapiint
	ret
sendbw10:cmp	al,'T'			; Novell TELAPI?
	jne	sendbw11		; ne = no
	mov	ah,255			; Telnet Interpret As Command char
	call	outchr			; send it
	mov	ah,244			; Telnet Interrupt Process char
	call	outchr			; send it
	ret
sendbw11:cmp	al,'I'			; TES?
	jne	sendbw12		; ne = no
	cmp	latkind,TES_LAT		; using LAT?
	jne	sendbw11a		; ne = no, older TES
	mov	ax,latbreak		; LAT BREAK command
	push	dx
	mov	dx,lathand		; LAT handle
	int	latint
	pop	dx
	ret
sendbw11a:mov	ah,testalk		; get command interpreter
	mov	dx,tesport
	int	rs232
	ret
sendbw12:mov	ax,8000h		; Artisoft Int 14h interceptor
	int	rs232			; presence check
	cmp	al,0ffh			; present?
	jne	sendbw13		; ne = no
	push	bx
	push	dx
	push	cx
	mov	dl,flags.comflg		; get type of port
	sub	dl,'4'			; Bios port
	xor	dh,dh
	push	es
	push	di
	mov	di,seg xmtbufx		; temp buf
	mov	es,di
	mov	di,offset xmtbufx
	mov	xmtbufx+37,7		; default to 9600 bps index value
	mov	ax,8007h		; Artisoft, Get_Redirected_Port
	int	rs232		;  info to es:di buffer (c set if failure)
	pop	di
	pop	es
	xor	bx,bx			; no parity (bh) one stop bit (bl)
	mov	cl,xmtbufx+37		; returned baud rate index
	mov	ch,3			; 8 data bits
	mov	ax,0400h	; extended init (4), set BREAK condition (0)
	int	rs232
	pop	cx
	mov	ax,cx			; # of ms to wait
	call	pcwait			; hold break for desired interval
	mov	dl,flags.comflg		; get type of port
	sub	dl,'4'			; Bios port
	xor	dh,dh
	xor	bx,bx			; no parity (bh) one stop bit (bl)
	mov	cl,xmtbufx+37		; returned baud rate index
	mov	ch,3			; 8 data bits
	mov	ax,0401h	; extended init (4), set no BREAK condition (1)
	int	rs232
	pop	dx
	pop	bx
sendbw13:ret
endif	; no_network
SENDBR	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.
;
; Revised slightly by Joe R. Doupnik 22 Dec 1985 to prevent interrupts
; being enabled until we're done, to stop interrupts from occurring when
; TX holding buffer becomes empty (a useless interrupt for us), and to
; shorten the time between enabling interrupts and our exit.
; Returns carry clear if success, else carry set.
; 9 July 1989 Add support for 16550/A 14 char receiver fifo.
SERINI	PROC	NEAR
	call	pcwtst			; recalibrate pcwait loop timer
	cmp	portin,0		; did we initialize port already?
	je	serin4			; e = yes
	jl	serin3			; l = no, not yet
	jmp	serin30			; yes, update flow and leave
serin3:	mov	bl,flags.comflg		; pass current port ident
	mov	portin,0		; say have been here once
	call	comstrt			; do SET PORT now
	jnc	serin4			; nc = success
	ret				; failed, exit now
serin4:	push	bx
	mov	bx,portval
	mov	bl,[bx].duplex		; get full/half duplex flag, local cpy
	mov	dupflg,bl
	pop	bx
	mov	cardet,0		; assume no Carrier is Detected
	cmp	flags.comflg,4		; UART?
	jbe	serin5			; be = yes, real thing
	jmp	serin8			; else try other port kinds

serin5:	push	bx
	push	es
	mov	dx,modem.mdmintc	; interrupt controller
	inc	dx			; look at interrupt mask
	in	al,dx			; get interrupt mask
	mov	savirq,al		; save state here for restoration
	or	al,modem.mddis		; inhibit our IRQ
	out	dx,al
	mov	al,byte ptr modem.mdintv ; desired interrupt vector
	mov	ah,35H			; Int 21H, function 35H = Get Vector
	int	dos			; get vector into es:bx
	mov	word ptr savsci,bx    ; save address offset of original vector
	mov	word ptr savsci+2,es 	;  and its segment
	mov	al,byte ptr modem.mdintv ; interrupt number for IRQ
	mov	dx,offset serint	; offset of our interrupt routine
	push	ds			; save ds around next DOS call
	mov	bx,seg serint		; compose full address of our routine
	mov	ds,bx			; segment is the code segment
	mov	ah,setintv		; set interrupt address from ds:dx
	int	dos
	pop	ds
	mov	al,rs232		; interrupt number for Bios serial port
	mov	ah,getintv		; get vector into es:bx
	int	dos
	mov	word ptr sav232,bx	; save offset
	mov	word ptr sav232+2,es	; save segment
	mov	dx,offset serdum	; offset of our interrupt routine
	push	ds			; save ds around next DOS call
	mov	bx,seg serdum		; compose full address of our routine
	mov	ds,bx			; segment is the code segment
	mov	ah,setintv		; set interrupt address from ds:dx
	int	dos
	pop	ds
	pop	es
	pop	bx
	mov	portin,1		; Remember port has been initialized
	mov	ax,modem.mdstat
	mov	mst,ax			; Use this address for status
	mov	ax,modem.mddat
	mov	mdat,ax			; Use this address for data
	mov	ax,modem.mdiir
	mov	miir,ax			; uart interrupt ident register
	mov	al,modem.mdmeoi
	mov	mdeoi,al		; Use to signify end-of-interrupt
	mov	ax,modem.mdmintc	; interrupt controller control addr
	mov	mdintc,ax		; 
	mov	dx,modem.mdstat		; uart line status register, 03fdh
	inc	dx
	in	al,dx			; 03feh, modem status reg
	mov	ah,80h+20h		; CD + DSR bits
	and	al,ah			; select bits
	cmp	al,ah			; test CD + DSR bits
	je	serin5c			; e = both are on, don't reset UART
	call	delay
	mov	dx,modem.mdcom
	inc	dx			; modem control register (3fch)
	mov	al,0fh			; set DTR, RTS, OUT1, OUT2
	out	dx,al
	call	delay
serin5c:mov	dx,modem.mdcom		; set up serial card Line Control Reg
	in	al,dx			; get present settings
	mov	savlcr,al		; save them for restoration
	call	delay			; Telepath with this delay removed
	mov	al,3			; 8 data bits. DLAB = 0
	mov	bx,portval
	cmp	[bx].stopbits,1		; one stop bit?
	je	serin5d			; e = yes
	or	al,4			; set bit 2^2 to 1 for two stop bits
serin5d:out	dx,al
	call	delay			; Telepath fails if this is removed
	mov	dx,modem.mddat
	inc	dx			; int enable reg (03f9)
	in	al,dx
	mov	savier,al		; save for restoration
					; drain UART for broken SMC FDC37C665
					; UART emulation. Must be before 16550
					; testing.
serin5a:mov	dx,modem.mdstat		; UART line status reg
	in	al,dx
	call	delay
	test	al,1			; data ready?
	jz	serin5b			; z = no
	mov	dx,modem.mddat		; UART data register
	in	al,dx			; read the received character into al
	call	delay
	jmp	short serin5a		; end SMC broken
serin5b:
	mov	dx,modem.mdiir		; Interrupt Ident reg (03fah)
	in	al,dx			; read current setting
	call	delay			; Telepath fails if this is removed
	mov	al,087h	; 8 byte trigger (80), reset fifos (2/4), Rx fifo(1) 
	out	dx,al
	mov	modem.mdfifo,al		; assume FIFO is active
	call	delay			; Telepath fails if this is removed
	in	al,dx			; read back iir
	and	al,0c0h	; select BOTH fifo bits: 16550A vs 16550 (bad fifo)
	cmp	al,0c0h			; are both fifo enabled bits set?
	je 	serin6			; e = yes, rcvr fifo is ok (16550/A)
	call	delay			; Telepath fails if this is removed
	xor	al,al			; else turn off fifo mode (16550/etc)
	out	dx,al
	mov	modem.mdfifo,al		; say no FIFO
	call	delay
serin6:	mov	dx,modem.mddat	   ; data and command port, read and flush any	
	in	al,dx			; char in UART's receive buffer
	inc	dx		  	; interrupt enable register 3f9h
	call	delay			; Telepath fails if this is removed
	mov	al,1			; set up interrupt enable register
	out	dx,al			;  for Data Available only
	call	delay			; Telepath fails if this is removed
	add	dx,3		   	; modem control register 3fch
	in	al,dx			; read original
	mov	savstat,al		; save original
	mov	al,0bh		  	; assert DTR, RTS, not OUT1, and OUT2
	cmp	dupflg,0		; full duplex?
	je	serin7			; e = yes
	mov	al,9h			; assert DTR, not RTS, not OUT1, OUT2
serin7:	cli
	out	dx,al		  ; OUT2 high turns on interrupt driver chip
	mov	dx,modem.mdiir		; Interrupt Ident reg (03fah)
	in	al,dx			; read current setting
	mov	dx,mdintc		; interrupt controller cntl address
	inc	dx			; access OCW1, interrupt mask byte
	in	al,dx			; get 8259 interrupt mask
	and	al,modem.mden		; enable IRQ. (bit=0 means enable)
	out	dx,al			; rewrite interrupt mask byte
	sti
	jmp	serin30			; finish up

serin8:	mov	al,flags.comflg
	cmp	al,'F'			; Fossil?
	jne	serin8a			; ne = no
	mov	dx,fossil_port		; trust not the shabby shells
	mov	ah,fossil_init		; Fossil, init port in dx
	xor	bx,bx			; no Control-C nonsense
	int	rs232
	mov	ah,fossil_dtr		; Fossil, DTR control
	mov	al,1			; raise DTR
	mov	dx,fossil_port		; port to use
	int	rs232
	push	bx
	mov	bx,portval		; get port data structure
	mov	al,[bx].floflg		; flow control kind
	pop	bx
	or	al,al			; if flow of none
	jz	serin8c			; z = none
	cmp	al,2			; xon/xoff?
	jne	serin8b			; ne = no
	mov	al,5			; Fossil xon/xoff
	jmp	short serin8c
serin8b:mov	al,2			; RTS/CTS
serin8c:mov	ah,fossil_flow
	int	rs232			; set flow control
	jmp	serin30			; finish up

serin8a:
ifndef	no_network
	cmp	al,'N'			; NetBios?
	je	serin9			; e = yes
	cmp	al,'O'			; Opennet Network? (FGR)
	jne	serin11			; ne = no
serin9:	mov	port_nb.portrdy,0	; say port is not ready yet
	call	setnet			; setup network session and pcnet flag
	jc	serin10			; c = failed
	jmp	serin30			; nc = success
serin10:ret				; fail, carry set, leave portin at 0
serin11:cmp	al,'E'			; using EBIOS?
	jne	serin13			; ne = no
	test	nettype,acsi		; using EBIOS.COM?
	jnz	serin9			; nz = not using it
	mov	bx,offset ebcoms	; es:bx to ebcoms structure
	mov	ebcoms+1,80h		; force a network connection
	mov	ah,ebredir		; do redirection
	xor	al,al
	mov	dx,ebport
	int	rs232
	or	ax,ax
	jz	serin12			; ax = 0 is success
	stc				; fail
	ret
serin12:mov	dx,ebport		; port 0..3
	mov	bx,offset rcvbuf	; receive buffer for EBIOS
	push	es
	mov	ax,ds
	mov	es,ax			; set es:bx to the buffer address
	mov	cx,nbuflen		; set cx to buffer's length
	mov	ax,ebbufset*256+2	; set rcvr buffered mode
	int	rs232
	mov	cx,nbuflen
	mov	bx,offset xmtbuf	; EBIOS transmitter work buffer
	mov	ax,ebbufset*256+1	; set xmtr buffered mode
	int	rs232
	mov	ax,ebmodem*256+3	; set outgoing DTR and RTS modem leads
	int	rs232			;  and ignore incoming leads
	mov	pcnet,1
	pop	es
	jmp	serin30
serin13:cmp	al,'D'			; DECnet?
	jne	serin14			; ne = no
	call	decstrt			; reinit
	jnc	serin30			; nc = success
	ret				; fail, carry set, leave portin at 0
serin14:cmp	al,'T'			; Novell TELAPI?
	jne	serin15			; ne = no
	cmp	pcnet,2			; going already?
	je	serin30			; e = yes
	call	telstrt			; start Telnet session
	jnc	serin30			; nc = success
	call	telapiclose		; close session
	stc
	ret				; fail, leave portin at 0
serin15:cmp	al,'M'			; Meridian SuperLAT?
	je	serin15a		; e = yes
	cmp	al,'I'			; TES?
	jne	serin17			; ne = no
	cmp	latkind,TES_LAT		; using LAT?
	jne	serin16			; ne = no, older style
serin15a:call	decstrt			; reinit
	jnc	serin30			; nc = success
	ret				; fail, carry set, leave portin at 0
serin16:call	tesstrt			; start a TES session
	jnc	serin30			; nc = success
	mov	ax,2000			; pause 2 sec for any msg
	call	pcwait
	stc
	ret
serin17:
ifndef	no_tcp
	cmp	al,'t'			; TCP/IP?
	jne	serin18			; ne = no
	call	tcpstart		; start TCP connection
	jnc	serin30
	push	bx
	mov	bx,portval		; get port data structure
	mov	[bx].portrdy,0		; say the comms port is not ready
	mov	portin,0
	pop	bx
	stc
	ret				; fail
endif	; no_tcp
serin18:cmp	al,'U'			; Ungermann Bass?
	je	serin20			; e = yes
	cmp	al,'C'			; 3Com BAPI?
	je	serin20			; e = yes
serin19:cmp	al,'b'			; [JRS] Beame & Whiteside TCP
	jne	serin20			; [JRS] ne = no
	call	bwstart			; [JRS] start a Telnet session w/BWTCP
	jnc	serin30			; [JRS] nc = success
	ret				; [JRS]
serin20:mov	bl,al			; preset net type
	call	comstrt			; start net
	jnc	serin30
	ret				; c = failure
endif	; no_network

serin30:push	bx
	mov	bx,portval		; get port data structure
	mov	[bx].portrdy,1		; say the comms port is ready
	mov	parmsk,0ffh		; parity mask, assume parity is None
	cmp	[bx].parflg,parnon	; is it None?
	je	serin31			; e = yes
	mov	parmsk,07fh		; no, pass lower 7 bits as data
serin31:xor	ax,ax
	mov	al,[bx].floflg		; flow control kind
	mov	flowcnt,al		; save here for active use
	mov	ax,[bx].flowc		; get flow control chars
	pop	bx
	mov	flowoff,al		; xoff or null
	mov	flowon,ah		; xon or null
	mov	xofrcv,off		; clear xoff received flag
	call	testcd			; update cardet byte
	mov	quechar,0		; clear outchr queued flow control
	mov	portin,1		; say initialized
	clc				; carry clear for success
	ret				; We're done
SERINI	ENDP

; Gateway 2000 Telepath internal modem extra delay routine
delay	proc	near
	push	ax
	mov	ax,1			; 1 millisecond
	call	pcwait
	pop	ax
	ret
delay	endp

; Reset the serial port.  This is the opposite of serini.  Calling
; this twice without intervening calls to serini should be harmless.
; Moved push/pop es code to do quicker exit before interrupts enabled.
; Returns normally.
; 22 June 1986 Leave OUT1 low to avoid resetting Hayes 1200B's. [jrd]
; 21 Feb 1987 Add support for Bios calls [jrd]
; 17 May 1987 Redo for COM3/4 support [jrd]
; 9 July 1989 Accomodate 16550/A receiver fifo mode. [jrd]
SERRST	PROC	NEAR
	cmp	portin,0		; Reset already? 
	jg	srst3			; g = no
	clc
	ret				; e = yes, l=not used yet, just leave
srst3:	cmp	flags.comflg,'0'	; Bios or networks?
	jb	srst4			; b = no, real UART
	jmp	srst6			; finish up

srst4:	push	word ptr savsci		; save original interrupt owner
	push	word ptr savsci+2	; offset and segment
	mov	word ptr savsci,offset nulint ; redirect to our null routine
	mov	ax,seg nulint		; segment of null routine is code
	mov	word ptr savsci+2,ax
	xor	cx,cx			; loop counter
srst2:	mov	dx,modem.mdstat		; status register
	in	al,dx
	call	delay			; delay
        and     al,60h			; Shift Reg Empty & Holding Reg Empty
        cmp     al,60h			; are both set?
        loopne  srst2           	; ne = no, wait until so (or timeout)
	xor	al,al
	mov	dx,modem.mdiir		; modem Interrupt Ident reg (03fah)
	out	dx,al			; turn off FIFO mode
	call	delay
	dec	dx			; point at int enable reg 3f9h
	out	dx,al			; disable interrupts from this source
	call	delay			; delay, let stray ints occur now
	add	dx,2			; point at Line Control Register 3fbh
	mov	al,savlcr		; saved bit pattern
	and	al,not 80h		; force DLAB bit to 0
	out	dx,al			; restore line control state
	call	delay
		; clear modem's delta status bits and reassert DTR etc
	inc	dx		; increment to modem control register 3fch
	mov	al,savstat		; saved modem control reg (03fch)
	or	al,3			; ensure DTR and RTS are asserted
	cmp	dupflg,0		; full duplex?
	je	srst2a			; e = yes
	xor	cx,cx
	push	dx			; save dx around test below
srst2b:	mov	dx,modem.mdstat		; modem line status reg
	in	al,dx			; read transmitter shift reg empty bit
	call	delay
	test	al,40h			; is it empty?
	loopz	srst2b			; z = no, not yet
	pop	dx
	mov	al,savstat		; saved modem control reg
	or	al,1			; assert DTR
	and	al,not 2		; unassert RTS
srst2a:	out	dx,al			; restore modem control reg (03fch)
	call	delay		; pause, in case stray interrupt is generated
	add	dx,2			; modem status register 3feh
	in	al,dx			; clear status register by reading it
	mov	mdmhand,al		; save here for Show Modem
	cli				; Disable interrupts
	mov	dx,modem.mdmintc
	inc	dx
	in	al,dx			; Interrupt controller int mask
	or	al,modem.mddis		; inhibit our IRQ line
	push	ax
	in	al,ppi_port		; delay
	pop	ax
	out	dx,al
	sti
	pop	word ptr savsci+2	; recover original int owner's addr
	pop	word ptr savsci
					; replace original IRQ intrpt vector
	push	bx
	mov	al,byte ptr modem.mdintv ; vector number to do
	mov	dx,word ptr savsci 	; offset part
	push	ds
	mov	bx,word ptr savsci+2	; segment part
	mov	ds,bx			; ds:dx has interrupt vector
	mov	ah,setintv		; set interrupt vector
	int	dos			; replaced
	pop	ds
	mov	al,rs232	; Bios serial port interrupt vector to restore
	mov	dx,word ptr sav232	; offset part
	push	ds
	mov	bx,word ptr sav232+2	; segment part
	mov	ds,bx
	mov	ah,setintv		; set interrupt vector
	int	dos
	pop	ds
	pop	bx
	mov	ah,savirq		; saved Interrupt state
	and	ah,modem.mddis		; pick out our IRQ bit
	mov	dx,modem.mdmintc	; interrupt controller cntl address
	cli
	inc	dx
	in	al,dx			; get current intrpt controller state
	push	ax
	in	al,ppi_port		; delay
	pop	ax
	and	al,modem.mden		; set our bit to zero
	or	al,ah			; set previous state of our IRQ
	out	dx,al			; reset IRQ to original state
	sti
	mov	dx,modem.mddat		; base address, 03f8h
	inc	dx	      		; Interrupt Enable Reg (03f9h)
	mov	al,savier		; saved IER
	out	dx,al			; restore setting
	jmp	short srst9
					; non-UART processes
srst6:
ifndef	no_network
	cmp	pcnet,0			; a network active?
	je	srst8			; e = no
	cmp	flags.comflg,'O'	; Opennet network? (FGR)
	je      srst7			; e = yes
 	cmp	flags.comflg,'N'	; NetBios network?
  	jne	srst8			; ne = no
srst7:	cmp	rposted,0		; receive outstanding?
	je	srst8			; e = no
	mov	can.scb_baddr,offset rcv ; cancel receives
	push	bx
	mov	bx,offset can
	call	nbsession
	pop	bx
	mov	rposted,0		; clear interlock flag no matter what
	cmp	lposted,0		; Listen posted?
	jne	srst8			; ne = yes, don't post another
	call	nbclose			; shut down NetBIOS
endif	; no_network
srst8:	cmp	flags.comflg,'F'	; Fossil?
	jne	srst9			; ne = no
	cmp	fossilflag,0		; shut down port when done?
	je	srst9			; e = no
	mov	dx,fossil_port		; port
	mov	ah,fossil_done		; fossil, done with driver
	int	rs232
srst9:	mov	portin,0		; reset flag
	push	bx
	mov	bx,portval		; port data structure
	mov	[bx].portrdy,0		; say port is not ready
	pop	bx
	mov	quechar,0		; clear any outchr queued char
	clc
	ret
SERRST	ENDP
code	ends

code1	segment
	assume	cs:code1
; Null interrupt routine, to handle strays
nulint	proc	near
	push	ax
	push	dx
	push	ds
	mov	ax,data			; set data seg addressibility
	mov	ds,ax
	mov	al,mdeoi		; specific EOI
	mov	dx,mdintc		; interrupt controller control word
	out	dx,al
	test	dl,80h			; slave controller?
	jz	nulint1			; z = no
	mov	al,20h			; general EOI
	out	20h,al			; EOI to master 8259
nulint1:pop	ds
	pop	dx
	pop	ax
	iret
nulint	endp

; Dummy Interrupt 14H to defeat DOS interference with serial port when CTTY
; and Kermit use the port simultaneously. If ports differ then chain DOS to
; original Int 14H Bios code. Else return dummy status=ok reports and
; Backspace for Read, ignore char for Write.
; Entered with AH = function request, AL = char to be sent, DX = com port num
; CS is our code segment, DS is DOS's, SS is ours or DOS's, interrupts off.
; 25 June 1987 [jrd]
SERDUM	PROC	FAR
	push	ds			; preserve all registers
	push	ax
	mov	ax,seg data		; get our data segment
	mov	ds,ax
	mov	al,flags.comflg		; get port id (COM1 = 1, COM2 = 2)
	and	al,7			; use lower three bits
	dec	al			; DOS counts COM1 as 0, etc
	mov	dosctty,0		; assume DOS not using our comms line
	cmp	dl,al		; referencing same port as Kermit is using?
	pop	ax			; recover request parameters
	jne	serdu1			; ne = no, chain to Bios routine
	mov	dosctty,1		; say DOS is using our comms line
	pop	ds
	cmp	ah,1			; send char in al?
	jb	serdu3			; b = no, init, return dummy status=ok
	ja	serdu2			; a = no, other
	mov	ah,60h			; yes, set line status=ok in ah
	iret
serdu2:	cmp	ah,2			; receive char (and wait for it)?
	jne	serdu3			; ne = no, return dummy report
	mov	al,BS			; yes, return ascii BS to DOS
	xor	ah,ah			; ah = errors (none here)
	iret
serdu3:	mov	ax,60b0h		; dummy status report:xmtr empty, CD,
	iret				;  DSR, and CTS are on

serdu1:	pop	tempdum			; save old ds
	push	word ptr sav232+2	; push Bios int 14H handler segment
	push	word ptr sav232		; push Bios int 14H handler offset
	push	tempdum			; recover old ds
	pop	ds
	ret				; do a ret far (chain to Bios)
SERDUM	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.
; 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.
; Upgraded to read cause of interrupt from interrupt ident reg (accepts only
;  data ready), chain to old interrupt if source is not our device.
; 9 Feb 1988 Add storage of interrupt cause in intkind. [jrd]
; 9 July 1989 Add support for 16550/A 14 char receiver fifo.

SERINT  PROC  FAR
 	push	ax			; save registers
	push	dx			; 
	push	ds
	mov	ax,seg data
	mov	ds,ax			; address data segment
	mov	dx,miir			; modem interrupt ident reg
	in	al,dx			; get interrupt cause
	mov	intkind,al		; save cause here
	test	al,1		; interrupt available if this bit is zero
	jz	srintc			; z = interrupt is from our source
; temporary item to side step chaining for noisy buses and funny Bios'
;	push	ds			; preserve data seg addressibility
;	pushf				; call the old int handler
;	call	dword ptr savsci	;  via pseudo INT
;	pop	ds			; recover data seg
	and	intkind,not 4		; say not-data-ready, to exit below
srintc:	mov	al,mdeoi		; specific EOI
	mov	dx,mdintc		; interrupt controller control word
	out	dx,al
	test	dl,80h			; slave controller?
	jz	srintd			; z = no
	mov	al,22h			; specific EOI for IRQ 2 (cascade)
	out	20h,al			; EOI the master 8259
srintd:	test	intkind,4		; data ready?
	jnz	srint0a			; nz = yes, else ignore
srint0:	sti				; else turn on interrupts
	jmp	retint			;  and exit now (common jump point)

srint0a:mov	dx,mst			; asynch status	port
	in	al,dx
srint0b:cli				; no interrupts permitted here
	and	al,mdmover		; select overrun bit
	mov	ah,al			; save it for later
	mov	dx,mdat
	in	al,dx			; read the received character into al
	test	flowcnt,2		; incoming XON/XOFF flow control?
	jz	srint2			; z = no
	mov	dh,al		   	; dh = working copy. Check flow cntl
	and	dh,parmsk		; strip parity temporarily, if any
	cmp	dh,flowoff		; acting on Xoff?
	jne	srint1			; ne = no, go on
	cmp	xofsnt,0		; have we sent an outstanding XOFF?
	jne	srint4e			; ne = yes, ignore (possible echo)
	mov	xofrcv,bufon		; set the flag saying XOFF received
	jmp	short srint4e		;  and continue the loop
srint1:	cmp	dh,flowon		; acting on Xon?
	jne	srint2			; ne = no, go on
	mov	xofrcv,off		; clear the XOFF received flag
	jmp	short srint4e		;  and continue the loop
srint2:	push	bx			; save register
	or	ah,ah			; overrun?
	jz	srint2a			; z = no
	mov	ah,al			; yes, save present char
	mov	al,bell			; insert control-G for missing char 
srint2a: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	srint3			; b = not past end
	mov	srcpnt,offset source 	; wrap buffer around
srint3:	cmp	count,bufsiz		; filled already?
	jae	srint4			; ae = yes
	inc	count			; no, add a char
srint4:	or	ah,ah			; anything in overrun storage?
	jz	srint4a			; z = no
	mov	al,ah			; recover any recent char from overrun
	xor	ah,ah			; clear overrun storage
	jmp	short srint2a		; yes, go store real second char
srint4a:pop	bx			; restore reg
srint4e:mov	dx,mst			; uart line status register, 03fdh
	in	al,dx			; get status
	test	al,1			; data ready?
	jnz	srint0b			; nz = yes, and preserve al
	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
	test	flowcnt,4		; using RTS/CTS flow control?
	jz	srint4b			; z = no
	mov	dx,mst			; modem status port (03fdh)
	dec	dx			; modem control reg (03fch)
	in	al,dx
	and	al,not 2		; reset RTS bit
	push	ax
	in	al,ppi_port		; delay
	pop	ax
	out	dx,al			; tell the UART to drop RTS
	mov	xofsnt,bufon	   	; remember RTS reset at buffer level
	jmp	short retint

srint4b:test	flowcnt,1		; send outgoing XON/XOFF?
	jz	retint			; z = no
	mov	al,flowoff		; get the flow off char (XOFF or null)
	push	bx
	mov	bx,portval
	cmp	[bx].baud,Bsplit	; doing 75/1200 baud stuff?
	pop	bx
	jne	srint4c			; ne = no
	mov	quechar,al		; put char in outchr queue for sending
	cmp	timeract,0		; is timer being used at task level?
	jne	srint4d			; ne = yes, just queue the char
	mov	ah,al			; put char into ah
	call	foutchr			; send the char now, non-blocking
srint4d:jmp	short retint

srint4c:call	fdopar			; set parity appropriately
	mov	ah,al		       ; don't overwrite character with status
	push	cx			; save reg
	xor	cx,cx			; loop counter
srint5:	mov	dx,mst			; get port status
	in	al,dx
	test	al,20H			; transmitter ready?
	jnz	srint6			; nz = yes
	in	al,ppi_port		; delay
	in	al,ppi_port		; delay
	in	al,ppi_port		; delay
	loop	srint5			; else wait loop, cx times
	jmp	short srint7		; timeout
srint6:	mov	al,ah			; now send out the flow control char
	mov	dx,modem.mddat
	push	ax
	in	al,ppi_port		; delay
	pop	ax
	out	dx,al
	mov	xofsnt,bufon	   ; remember we sent an XOFF at buffer level
srint7:	pop	cx			; restore reg
retint:	pop	ds
	pop	dx
	pop	ax
	iret
SERINT	ENDP

code1	ends

code	segment
	assume	cs:code

DTRLOW	PROC	NEAR		; Global proc to Hangup the Phone or Network
				; by making DTR and RTS low (phone).
	mov	ah,cmword	; allow text, to be able to display help
	mov	dx,offset rdbuf		; dummy buffer
	mov	bx,offset hnghlp	; help message
	call	comnd
	jc	dtrlow3			; c = failure
	mov	ah,cmeol
	call	comnd			; get a confirm
	jc	dtrlow3
	cmp	flags.comflg,'0'	; Bios?
	jb	dtrlow1			; b = no, UART
	cmp	flags.comflg,'4'	; Bios?
	jbe	dtrlow2			; be = yes, can't access modem lines
dtrlow1:call	serhng			; drop DTR and RTS
	cmp	taklev,0		; in a Take file or macro?
	jne	dtrlow2			; ne = yes, no message
	mov	ah,prstr		; give a nice message
	mov	dx,offset hngmsg
	int	dos
dtrlow2:clc				; success
dtrlow3: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]
; 5 April 1987 Add 500 millisec wait with lines low before returning. [jrd]
; Calling this twice without intervening calls to serini should be harmless.
; If network then call session close procedure to hangup the session without
; losing local name information; serrst does this.
; Returns normally.

serhng	proc	near	; clear modem's delta status bits and lower DTR & RTS
	cmp	apctrap,0		; APC disable?
	je	shng1			; e = no
	stc				; fail
	ret
shng1:	call	serrst			; reset port so can be opened again
	cmp	flags.comflg,'F'	; Fossil?
	je	shng2			; e = yes
ifndef	no_network
	cmp	flags.comflg,4		; UART port?
	jbe	shng2			; be = yes
	push	bx
	mov	bx,portval
	call	[bx].cloproc		; close the active network session
	pop	bx
	clc
	ret
endif	; no_network

shng2:	call	serini			; energize, maybe for first time
	cmp	flags.comflg,'F'	; Fossil?
	jne	shng3			; ne = no
	push	ax
	push	dx
	mov	ah,fossil_dtr		; Fossil, DTR control
	xor	al,al			; lower DTR
	mov	dx,fossil_port		; port to use
	int	rs232
	call	serrst
	jmp	short shng4

shng3:	call	serrst			; back to sleep
	cli				; Disable interrupts
	push	ax
	push	dx
	mov	dx,modem.mddat		; serial port base address
	add	dx,4			; increment to control register
	mov	al,08h		       ; reassert OUT2, un-assert DTR,RTS,OUT1
	out	dx,al
	in	al,ppi_port		; delay
	add	dx,2			; increment to modem status register
	in	al,dx			; Clear Status reg by reading it
	sti				; Enable interrupts
shng4:	mov	ax,1000			; 1000 millisec, for pcwait
	call	pcwait		    ; keep lines low for at least 500 millisec
	pop	dx
	pop	ax
	clc
	ret
serhng	endp

; Update cardet byte for UART, Bios and Fossil ports
testcd	proc	near
	cmp	flags.carrier,0		; worry about carrier detect?
	je	testcdx			; e = no
	push	ax
	push	dx
	mov	al,flags.comflg
	cmp	al,4			; UART?
	jbe	testcd1			; e = yes
	cmp	al,'4'			; BIOS?
	je	testcd2			; e = yes
	cmp	al,'F'			; Fossil?
	je	testcd3			; e = yes
	pop	dx
	pop	ax
	jmp	short testcdx		; can't get CD for the rest
testcd1:mov	dx,modem.mddat		; UART
	add	dx,6			; modem status reg 3feh
	in	al,dx			; 03feh, modem status reg
	jmp	short testcd4
testcd2:mov	ah,3			; check port status, std Bios calls
	mov	dl,al			; port + bias
	sub	dl,'1'			; remove bias
	int	rs232			; Bios call
	jmp	short testcd4

testcd3:mov	dx,fossil_port		; get port number (1..4)
	mov	ah,fossil_portst	; check port status, std Bios calls
	int	rs232			; Bios call

testcd4:and	al,80h			; get CD bit
	jnz	testcd5			; nz = CD is on now
	test	cardet,80h		; previous CD state
	jz	testcd5			; z = was off, still is
	mov	al,01h			; say was ON but is now OFF
testcd5:mov	cardet,al		; preserve as global
	pop	dx
	pop	ax
testcdx:ret
testcd	endp

ifndef	no_tcp
; TCP/IP Telnet session manager.
; Enter with SET PORT TCP/IP command line in buffer decbuf, 
; and <byte count>"\"<port number> + NUL + {NEW, RESUME} in decbuf+80
; Syntax of that line is
;   empty - resume current session, error return if none.
;   digit - start or resume session digit, with digit being a local number.
;   host  - ask if resume or new. If resume call ktcpswap, else tcpstart.
;    *    - server mode
; Returns sescur as index (0..maxsessions-1) for seslist action.
; Successful return is carry clear, else carry set. 

sesmgr	proc	near
	push	bx
	mov	bx,sescur		; current session
	call	termswapout		; swap out current session's emulator
	pop	bx
	cmp	decbuf,1		; byte count, anything given?
	jb	sesmgr24		; b = no, use tcphost default
	ja	sesmgr40		; ja = yes, but more than one char
	mov	bl,decbuf+1		; get single char
	mov	al,bl
	and	al,not 20h		; upper case, for letters only
	jmp	short sesmgr13
	
sesmgr10:cmp	sescur,-1		; any session yet?
	je	sesmgr22		; e = no, use command line
	call	iseof			; are we at EOF, such as from disk?
	jc	sesmgr28		; c = yes, fail the command
	call	sesdisp		; display the table for guidance, inc help
	mov	dx,offset seshelp
	mov	ah,prstr
	int	dos
	mov	flags.cxzflg,0		; clear Control-C flag
	mov	ax,0c0ah		; clear buffer and read line
	mov	decbuf+100,80		; length of our buffer
	mov	dx,offset decbuf+100	; start of buffer (len, cnt, data)
	int	dos
	cmp	flags.cxzflg,'C'	; Control-C entered?
	je	sesmgr28		; e = yes, quit
	mov	al,decbuf+101		; number of printables entered
	or	al,al			; need at least one
	jz	sesmgr15		; default is NEW
	mov	bl,decbuf+102		; get first byte
	mov	al,bl
	and	al,not 20h		; upper case, for letters only

sesmgr13:cmp	bl,'*'			; "*" for server mode?
	je	sesmgr40		; e = yes, use as host name
sesmgr13a:cmp	al,'R'			; RESUME current?
	jne	sesmgr14		; ne = no
	clc				; resume current session
	ret
sesmgr14:cmp	al,'Q'			; QUIT?
	je	sesmgr28		; e = yes, return failure
	cmp	al,'N'			; NEW?
	jne	sesmgr20		; ne = no

sesmgr15:mov	cx,maxsessions		; find an opening for NEW
	xor	bx,bx
sesmgr16:mov	al,seslist[bx]
	or	al,al			; session in use?
	jns	sesmgr18		; ns = yes
	mov	sescur,bx		; set current session ident
	jmp	short sesmgr22		; s = no
sesmgr18:inc	bx			; next slot
	loop	sesmgr16
	mov	dx,offset sesnone	; say no more sessions available
	mov	ah,prstr
	int	dos
	stc				; fail out
	ret
sesmgr20:xor	bh,bh			; ensure high byte is clear
	sub	bx,'1'			; assume digit 1..6, remove bias
	cmp	bx,6			; legal values are 0..5
	jae	sesmgr10		; ae = out of range
	mov	sescur,bx		; return slist index for action
	mov	al,bl
	mov	cx,seshostlen		; length of a table entry
	mul	cl			; skip to correct sesname
	cmp	decbuf,1		; host name given on command line?
	ja	sesmgr22		; a = yes, use it as host name
	mov	di,offset tcphost	; update active host name
	add	ax,offset sesname	; get host name from table
	mov	si,ax
	cmp	byte ptr [si],0		; any name present?
	jne	sesmgr20a		; ne = yes
	xchg	si,di			; use current name in tcphost
	jmp	short sesmgr26		; use existing tcpport too
sesmgr20a:push	bx
	shl	bx,1			; address words
	mov	ax,sesport[bx]
	mov	tcpport,ax		; set tcpport
	pop	bx
	jmp	short sesmgr26		; and switch terminal configuration

sesmgr22:push	bx
	mov	ax,tcpport		; tcp port
	mov	bx,sescur		; new session
	or	bx,bx			; negative, for no session?
	jns	sesmgr22a		; ns = not negative
	xor	bx,bx			; start session 0
	mov	sescur,bx
sesmgr22a:shl	bx,1			; address words
	mov	sesport[bx],ax		; port
	pop	bx
	mov	si,offset decbuf+1	; name of new host
	cmp	byte ptr [si-1],0	; byte count, anything given?
	je	sesmgr24		; e = no, use tcphost default
	mov	di,offset tcphost
	call	strcpy			; copy new host name

sesmgr24:mov	si,offset tcphost	; host name
	cmp	byte ptr [si],0		; is this empty too?
	je	sesmgr27		; e = yes, no host name, fail
	cmp	sescur,0		; inited current session ident?
	jge	sesmgr25		; ge = yes
	mov	sescur,0		; start with session 0
sesmgr25:mov	bx,sescur		; return slist index for action
	mov	al,bl
	mov	cx,seshostlen		; length of a table entry
	mul	cl			; skip to correct sesname
	add	ax,offset sesname	; destination table
	mov	di,ax
sesmgr26:call	strcpy
	push	bx
	mov	ax,tcpport
	mov	bx,sescur		; new session
	shl	bx,1
	mov	sesport[bx],ax
	shr	bx,1
	call	termswapin		; swap in new session's emulator
	pop	bx
	clc
	ret
sesmgr27:mov	dx,offset sesnohost	; say have no host name 
	mov	ah,prstr
	int	dos
sesmgr28:stc
	ret
					; user specified host name
					; see if name is already in use
sesmgr40:mov	si,offset decbuf+1	; user string
	mov	cl,[si-1]		; user string length
	xor	ch,ch
	cld
sesmgr41:lodsb				; read a byte
	call	tolowr			; to lower case
	mov	[si-1],al		; store letter
	loop	sesmgr41
	mov	ax,23			; default port
	mov	si,offset decbuf+82	; optional port number
	mov	temp2,si		; where to expect NEW/OLD
	cmp	byte ptr [si-2],0	; byte count, any port specified?
	je	sesmgr45		; e = no, use default
	cmp	byte ptr [si],'\'	; user specified leading backslash?
	je	sesmgr44		; e = yes
	dec	si			; then use ours
sesmgr44:call	katoi			; convert si to number in ax
	jc	sesmgr46		; c = not a number
	mov	temp2,si		; where to expect NEW/OLD

sesmgr45:or	ax,ax			; any port specified?
	jz	sesmgr46		; z = no, use 23
	cmp	ax,25			; this one?
	jne	sesmgr47		; ne = no
	mov	ah,prstr
	mov	dx,offset badport	; say bad port number
	int	dos
sesmgr46:mov	ax,23			; use official Telnet port
sesmgr47:mov	temp,ax			; remember port
	mov	cx,maxsessions
	xor	bx,bx			; session subscript, assummed
	mov	si,offset decbuf	; user len+string
	mov	di,offset sesname	; session list of host names
	cld
sesmgr42:push	cx
	push	si
	push	di
	push	es
	mov	cl,[si]			; length of user string
	xor	ch,ch
	inc	si			; skip user string count byte
	inc	cl			; include null termintors
	mov	ax,seg sesname
	mov	es,ax
	repe	cmpsb			; compare host names
	pop	es
	pop	di
	pop	si
	pop	cx
	jne	sesmgr43		; ne = no match, try next entry
	cmp	seslist[bx],0		; is session active?
	jl	sesmgr43		; l = no, ignore this entry
	shl	bx,1			; address words
	mov	ax,sesport[bx]		; current port
	shr	bx,1			; restore bx
	cmp	ax,temp			; specified port same?
	jne	sesmgr43		; ne = no, not a duplicate session

	mov	bx,temp2		; where to expect NEW/RESUME
	mov	bl,[bx]			; get first byte of NEW/RESUME
	or	bl,bl			; anything?
	jz	sesmgr10		; z = no, show menu and prompt
	mov	al,bl			; get byte
	and	al,not 20h		; to upper case, for letters only
	jmp	sesmgr13a		; decode letter

sesmgr43:add	di,seshostlen		; next entry in our table
	inc	bx			; next session subscript
	loop	sesmgr42
	mov	ax,temp
	mov	tcpport,ax		; set tcpport
	jmp	sesmgr15		; start a New session
sesmgr	endp

sesdisp	proc	near
	mov	ah,prstr
	mov	dx,offset sesheader
	int	dos
	push	bx
	xor	bx,bx			; number of sessions
sesdis1:mov	al,bl			; get our local counter
	mov	dl,seshostlen		; length of host name array row
	mul	dl
	add	ax,offset sesname	; asciiz host name
	mov	si,ax			; save host name pointer in cx
	cmp	byte ptr [si],0		; ever had a host name?
	jz	sesdis3			; z = no, don't display info
	mov	al,seslist[bx]		; get Telnet session number
	mov	dx,offset sesinact	; inactive msg
	or	al,al			; seslist status
	jns	sesdis4			; ns = active
	cmp	bx,sescur		; same as current?
	jne	sesdis2			; ne = no
	mov	dx,offset curinact	; say inactive but current
	jmp	short sesdis2

sesdis4:mov	dx,offset sesactive 	; active msg
	cmp	bx,sescur		; same as current?
	jne	sesdis2			; ne = no
	mov	dx,offset curmsg 	; current msg
sesdis2:mov	ah,prstr		; show status, ses #, host name
	int	dos
	mov	dl,bl			; count local session idents
	add	dl,'1'			; bias
	mov	ah,conout
	int	dos
	mov	dl,' '
	int	dos
	int	dos
	int	dos
	int	dos
	int	dos
	int	dos
	int	dos
	shl	bx,1			; address words
	mov	ax,sesport[bx]
	shr	bx,1			; restore bx
	call	decout
	mov	ah,conout
	mov	dl,' '
	int	dos
	int	dos
	mov	dx,si
	call	prtasz			; show host name
sesdis3:inc	bx			; next item
	cmp	bx,maxsessions		; done all?
	jb	sesdis1			; b = no
	pop	bx
	ret
sesdisp	endp

; Returns session ident in BX when given a Telnet ident in AL. Also updates
; tcphost from session table.
; Returns -1 if no correspondence
tcptoses proc	near
	push	ax
	push	cx
	mov	cx,maxsessions		; number session slots
	xor	bx,bx
tcptose1:mov	ah,seslist[bx]		; session status
	or	ah,ah			; active?
	js	tcptose2		; s = no
	cmp	ah,al			; same active session? 
	je	tcptose3		; e = yes, return BX
tcptose2:inc	bx
	loop	tcptose1
	mov	bx,-1			; return -1 for failure to find
	pop	cx
	pop	ax
	ret
tcptose3:push	si
	push	di
	mov	di,offset tcphost	; host name
	mov	al,bl
	mov	cx,seshostlen		; length of a table entry
	mul	cl			; skip to correct sesname
	add	ax,offset sesname	; source table
	mov	si,ax
	call	strcpy
	pop	di
	pop	si
	pop	cx
	pop	ax
	ret
tcptoses endp
endif	; no_tcp

; Compute number of iterations needed in procedure pcwait inner loop
; to do one millisecond delay increments. Uses Intel 8253/8254 timer chip
; (timer #2) to measure elapsed time assuming 1.193182 MHz clock.
; For IBM PC compatible machines.
pcwtst	proc	near
	push	cx
	mov	cx,10		; number of tests to perform
pcwtst1:call	pcwtst2		; do the test and new pcwcnt calculation
	loop	pcwtst1		; repeat several times for convergence
	pop	cx
	ret

pcwtst2:push	ax
	push	bx
	push	cx
	push	dx
	in	al,ppi_port	; 8255 chip port B, 61h
	and	al,0fch		; speaker off (bit 1), stop timer (bit 0)
	out	ppi_port,al	; do it
  ; 10 = timer 2, 11 = load low byte then high byte, 010 = mode 2, 0 = binary
	mov	al,10110100B	; command byte
	out	timercmd,al	; timer command port, 43h
	xor	al,al		; clear initial count for count-down
	out	timer2data,al	; low order byte of count preset, to port 42h
	out	timer2data,al	; high order byte, to the same place
	in	al,ppi_port	; get 8255 setting
	mov	dl,al		; remember it in dl
	and	al,0fch		; clear our control bits
	or	al,1	   	; start counter now (Gate = 1, speaker is off)
	out	ppi_port,al	; do it, OUT goes low
				; this is the test loop
	mov	ax,8		; wait 8 millisec
	call	pcwait		; call the software timer
				; end test loop
	mov	al,dl		; restore ppi port, stop timer
	out	ppi_port,al
	in	al,timer2data	; read count down value
	xchg	al,ah		; save low order byte
	in	al,timer2data	; get high order byte
	xchg	ah,al		; put in correct sequence
	neg	ax		; subtract from zero to get elapsed tics
	mov	bx,ax		; save observed tics
	mov	ax,pcwcnt	; current pcwcnt value
	; new pcwcnt= old pcwcnt * [1193(tics/ms) / (observed tics / loops)]
	mov	cx,8*1193
	mul	cx	
	or	bx,bx		; zero observed tics?
	jz	pcwtst3		; z = yes, divide by one
	cmp	dx,bx		; overflow?
	jb	pcwtst4		; b = not likely
	mov	ax,pcwcnt
	jmp	short pcwtst3	; bypass calculation
pcwtst4:div	bx		; divided by observed tics
pcwtst3:mov	pcwcnt,ax	; store quotient as new inner loop counter
	pop	dx
	pop	cx
	pop	bx
	pop	ax
	ret
pcwtst	endp
	
;; Wait for the # of milliseconds in ax, for non-IBM compatibles.
;; Thanks to Bernie Eiben for this one. Modified to use adjustable 
; inner loop counter (pcwcnt, adjusted by proc pcwtst) by [jrd].
pcwait	proc	near
	push	cx
pcwai0:	mov	cx,pcwcnt	; inner loop counter for 1 ms (240 @ 4.77 MHz)
pcwai1:	push	ax
	in	al,PPI_PORT	; touch bus to deal with fast caches
	pop	ax
	sub	cx,1
	jnz	pcwai1
	dec	ax		; outer loop counter
	jnz	pcwai0		; wait another millisecond
	pop	cx
	ret
pcwait	endp
code	ends 
	end
