	TITLE	COM_PKG2 -- Last updated 10/6/85
	PAGE	75,132
;
; Modified to use COM1 thru COM4 - William W. Plummer, 2/21/91
; Eliminate (incomplete) support for DOS1 - William W. Plummer, 11/13/90
;
;	modified to use MSC calling sequence.
;	jrr 3/86
;
; Communications Package for the IBM PC, XT, AT and PCjr
; and strict compatibles.
; May be copied and used freely -- This is a public domain program
; Developed by Richard Gillmann, John Romkey, Jerry Saltzer,
; Craig Milo Rogers, Dave Mitton and Larry Afrin.
;
; We'd sure like to see any improvements you might make.
; Please send all comments and queries about this package
; to GILLMANN@USC-ISIB.ARPA
;
; o Supports both serial ports simultaneously
; o All speeds to 19200 baud
; o Compatible with PC, XT, AT and PCjr.
; o Built in XON/XOFF flow control option
; o Assembly language calling conventions
; o Logs all comm errors
; o Direct connect or modem protocol
;
; MAXIMUM BUFFER SIZES
R_SIZE	EQU	2048		; SIZE OF RECEIVE BUFFERS skl/88-05-13
S_SIZE	EQU	4096		; SIZE OF TRANSMIT BUFFERS ahd 01/92
; INTERRUPT NUMBERS
INT_COM1 EQU	0CH		; COM1: FROM 8259
INT_COM2 EQU	0BH		; COM2: FROM 8259
INT_COM3 EQU	0CH		; COM3: FROM 8259
INT_COM4 EQU	0BH		; COM4: FROM 8259
; 8259 PORTS
INTA00	EQU	20H		; 8259A PORT, A0 = 0
INTA01	EQU	21H		; 8259A PORT, A0 = 1
; COM1: & COM3: LEVEL 4
IRQ4	EQU	2*2*2*2 		; 8259A OCW1 MASK, M4=1, A0=0
NIRQ4	EQU	NOT IRQ4 AND 0FFH	; COMPLEMENT OF ABOVE
EOI4	EQU	4 OR 01100000B		; 8259A OCW2 SPECIFIC IRQ4 EOI, A0=0
; COM2: & COM4: LEVEL 3
IRQ3	EQU	2*2*2			; 8259A OCW1 MASK, M3=1, A0=0
NIRQ3	EQU	NOT IRQ3 AND 0FFH	; COMPLEMENT OF ABOVE
EOI3	EQU	3 OR 01100000B		; 8259A OCW2 SPECIFIC IRQ3 EOI, A0=0

; FLOW CONTROL CHARACTERS
CONTROL_Q EQU	11H		; XON
CONTROL_S EQU	13H		; XOFF
; MISC.
DOS	EQU	21H		; DOS FUNCTION CALLS
	PAGE
;
; ROM BIOS Data Area
;
RBDA	SEGMENT AT 40H
RS232_BASE DW	4 DUP(?)	; ADDRESSES OF RS232 ADAPTERS
RBDA	ENDS
;
; ROM PC-Type IDENT
;
ROM	SEGMENT AT 0F000H
	ORG	0FFFEH
ROMID	DB	?		; 0FFH=PC OR EARLY XT, 0FEH=XT, 0FDH=JR
ROM	ENDS
	PAGE
;
; TABLE FOR EACH SERIAL PORT
;
SP_TAB		STRUC
PORT		DB	?	; 1 OR 2 OR 3 OR 4
; PARAMETERS FOR THIS INTERRUPT LEVEL
INT_COM 	DB	?	; INTERRUPT NUMBER
IRQ		DB	?	; 8259A OCW1 MASK
NIRQ		DB	?	; COMPLEMENT OF ABOVE
EOI		DB	?	; 8259A OCW2 SPECIFIC END OF INTERRUPT
; INTERRUPT HANDLERS FOR THIS LEVEL
INT_HNDLR	DW	?	; OFFSET TO INTERRUPT HANDLER
OLD_COM_OFF	DW	?	; OLD HANDLER'S OFFSET
OLD_COM_SEG	DW	?	; OLD HANDLER'S SEGMENT
; ATTRIBUTES
INSTALLED	DB	?	; IS PORT INSTALLED ON THIS PC? (1=YES,0=NO)
BAUD_RATE	DW	?	; 19200 MAX
CONNECTION	DB	?	; M(ODEM), D(IRECT)
PARITY		DB	?	; N(ONE), O(DD), E(VEN), S(PACE), M(ARK)
STOP_BITS	DB	?	; 1, 2
XON_XOFF	DB	?	; E(NABLED), D(ISABLED)
; FLOW CONTROL STATE
HOST_OFF	DB	?	; HOST XOFF'ED (1=YES,0=NO)
PC_OFF		DB	?	; PC XOFF'ED (1=YES,0=NO)
; ERROR COUNTS
ERROR_BLOCK	DW	8 DUP(?)	; EIGHT ERROR COUNTERS
; 8250 PORTS
DATREG		DW	?	; DATA REGISTER
IER		DW	?	; INTERRUPT ENABLE REGISTER
IIR		DW	?	; INTERRUPT IDENTIFICATION REGISTER
LCR		DW	?	; LINE CONTROL REGISTER
MCR		DW	?	; MODEM CONTROL REGISTER
LSR		DW	?	; LINE STATUS REGISTER
MSR		DW	?	; MODEM STATUS REGISTER
; BUFFER POINTERS
START_TDATA	DW	?	; INDEX TO FIRST CHARACTER IN X-MIT BUFFER
END_TDATA	DW	?	; INDEX TO FIRST FREE SPACE IN X-MIT BUFFER
START_RDATA	DW	?	; INDEX TO FIRST CHARACTER IN REC. BUFFER
END_RDATA	DW	?	; INDEX TO FIRST FREE SPACE IN REC. BUFFER
; BUFFER COUNTS
SIZE_TDATA	DW	?	; NUMBER OF CHARACTERS IN X-MIT BUFFER
SIZE_RDATA	DW	?	; NUMBER OF CHARACTERS IN REC. BUFFER
; BUFFERS
TDATA		DB	S_SIZE DUP(?)	; SEND BUFFER
RDATA		DB	R_SIZE DUP(?)	; RECEIVE BUFFER
SP_TAB		ENDS
; SP_TAB EQUATES
; WE HAVE TO USE THESE BECAUSE OF PROBLEMS WITH STRUC
EOVFLOW 	EQU	ERROR_BLOCK	; BUFFER OVERFLOWS
EOVRUN		EQU	ERROR_BLOCK+2	; RECEIVE OVERRUNS
EBREAK		EQU	ERROR_BLOCK+4	; BREAK CHARS
EFRAME		EQU	ERROR_BLOCK+6	; FRAMING ERRORS
EPARITY 	EQU	ERROR_BLOCK+8	; PARITY ERRORS
EXMIT		EQU	ERROR_BLOCK+10	; TRANSMISSION ERRORS
EDSR		EQU	ERROR_BLOCK+12	; DATA SET READY ERRORS
ECTS		EQU	ERROR_BLOCK+14	; CLEAR TO SEND ERRORS
DLL		EQU	DATREG		; LOW DIVISOR LATCH
DLH		EQU	IER		; HIGH DIVISOR LATCH
	PAGE
;	put the data in the DGROUP segment
;	far calls enter with DS pointing to DGROUP
;
DGROUP	GROUP _DATA
_DATA	SEGMENT PUBLIC 'DATA'
;
DIV50PC 	EQU	2304		; DIVISOR FOR 50 BAUD (PC,XT)
DIV50JR 	EQU	2237		; DIVISOR FOR 50 BAUD (JR)
DIV50		DW	2304		; ACTUAL DIVISOR FOR 50 BAUD IN USE
CURRENT_AREA	DW	AREA1		; CURRENTLY SELECTED AREA
; DATA AREAS FOR EACH PORT
AREA1	SP_TAB	<1,INT_COM1,IRQ4,NIRQ4,EOI4>	; COM1 DATA AREA
AREA2	SP_TAB	<2,INT_COM2,IRQ3,NIRQ3,EOI3>	; COM2 DATA AREA
AREA3	SP_TAB	<3,INT_COM3,IRQ4,NIRQ4,EOI4>	; COM3 DATA AREA
AREA4	SP_TAB	<4,INT_COM4,IRQ3,NIRQ3,EOI3>	; COM4 DATA AREA
_DATA	 ENDS
	PAGE
COM_TEXT    SEGMENT PARA PUBLIC 'CODE'
	ASSUME	CS:COM_TEXT,DS:DGROUP,ES:NOTHING

	PUBLIC	_select_port
	PUBLIC	_save_com
	PUBLIC	_install_com
	PUBLIC	_restore_com
	PUBLIC	_open_com
	 PUBLIC _ioctl_com
	PUBLIC	_close_com
	PUBLIC	_dtr_on
	PUBLIC	_dtr_off
	PUBLIC	_r_count
	PUBLIC	_s_count
	PUBLIC	_receive_com
	PUBLIC	_send_com
	PUBLIC	_sendi_com
	PUBLIC	_send_local
	PUBLIC	_break_com
	PUBLIC	_com_errors
	PUBLIC	_modem_status
	PAGE


; Here's the order of calls in UUPC.  The routines in COMM.ASM are called
; from ulib.c.

; First (when a line in system has been read?), ulib&openline calls
;		select_port()		; Sets up CURRENT_AREA
; then, 	save_com()		; Save INT vector
; then, 	install_com()		; Init area, hook INT
; then, 	open_com(&cmd, 'D', 'N', STOP*T, 'D') ; Init UART, clr bufs
; then, 	dtr_on().

; At that point the line is up and running.  UUPC calls ulib&sread()
; which calls,	receive_com();

; And UUPC calls ulib&swrite()
; which calls,	send_com();

; To cause an error that the receiver will see, UUPC calls ulib&ssendbrk();
; which calls,	break_com();

; When all done with the line, UUPC calls ulib&closeline()
; which calls,	dtr_off();
; then, 	close_com();
; then, 	restore_com();		; Unhook INT
; and,		stat_errors();


; Note that on the PC COM1 and COM3 share IRQ4, while COM2 and COM4 share IRQ3.
; BUT, only one device on a given IRQ line can be active at a time.  So it is
; sufficient for UUPC to hook whatever IRQ INT its modem is on as long as it
; unhooks it when it is done with that COM port.  COMM cannot be an installed
; device driver since it must go away when UUPC is done so that other devices
; on the same INTs will come back to life.  Also, it is OK to have a static
; CURRENT_AREA containing the current area that UUPC is using.

	PAGE
;
; SELECT WHICH PORT IS TO BE "ACTIVE"
;	[bp+6] = port number
_select_port	PROC FAR
	push bp
	mov bp,sp
	mov AX,[bp+6]	; get aguement
	CMP	AL,1		; Port 1?
	 JE	SP1		; Yes
	CMP	AL,2		; Port 2?
	 JE	SP2		; Yes
	CMP	AL,3		; Port 3?
	 JE	SP3		; Yes
	CMP	AL,4		; Port 4?
	 JE	SP4		; Yes
	INT 20H 		; N.O.T.A. ????? Halt for debugging!
	; Assume port 1 if continued
SP1:	MOV	AX,OFFSET DGROUP:AREA1	; SELECT COM1 DATA AREA
	JMP	SHORT SPX	; CONTINUE
SP2:	MOV	AX,OFFSET DGROUP:AREA2	; SELECT COM2 DATA AREA
	JMP	SHORT SPX	; CONTINUE
SP3:	MOV	AX,OFFSET DGROUP:AREA3	; SELECT COM3 DATA AREA
	JMP	SHORT SPX	; CONTINUE
SP4:	MOV	AX,OFFSET DGROUP:AREA4	; SELECT COM4 DATA AREA
	;Fall into SPX
SPX:	MOV	CURRENT_AREA,AX ; SET SELECTION IN MEMORY
	mov sp,bp
	pop bp
	RET			; DONE
_select_port	ENDP
	PAGE
;
; SAVE ORIGINAL COM VECTOR
;
_save_com	PROC FAR
	push bp
	mov bp,sp
	push si
	MOV	SI,CURRENT_AREA ; SI POINTS TO DATA AREA
	PUSH	ES		; SAVE EXTRA SEGMENT
	MOV	AREA1.INT_HNDLR,OFFSET INT_HNDLR1
	MOV	AREA2.INT_HNDLR,OFFSET INT_HNDLR2
	MOV	AREA3.INT_HNDLR,OFFSET INT_HNDLR3
	MOV	AREA4.INT_HNDLR,OFFSET INT_HNDLR4

; Save old interrupt vector
	MOV	AH,35H		; FETCH INTERRUPT VECTOR CONTENTS
	MOV	AL,INT_COM[SI]	; INTERRUPT NUMBER
	INT	DOS		; DOS 2 FUNCTION
	MOV	OLD_COM_OFF[SI],BX	; SAVE
	MOV	BX,ES			; ES:BX
	MOV	OLD_COM_SEG[SI],BX	; FOR LATER RESTORATION
	POP	ES		; RESTORE ES
	pop si
	mov sp,bp
	pop bp
	RET			; DONE
_save_com	ENDP
	PAGE
;
; INSTALL_COM:	INSTALL THE ACTIVE PORT
;
; SET 8250 PORTS FROM RS-232 BASE IN ROM BIOS DATA AREA
; INITIALIZE PORT CONSTANTS AND ERROR COUNTS
; INSTALL INTERRUPT VECTOR
;
;	return ax=1 on success ax=0 on failure
;
_install_com	PROC FAR
	push bp
	mov bp,sp
	push si
	MOV	SI,CURRENT_AREA ; SI POINTS TO DATA AREA
	PUSH	ES		; SAVE EXTRA SEGMENT
	CMP	INSTALLED[SI],1 ; Is port installed on this machine?
	 JNE	INSTOK		; NO, CONTINUE
	JMP	INSTX		; ELSE JUMP IF ALREADY INSTALLED
; CLEAR ERROR COUNTS
INSTOK: MOV	WORD PTR EOVFLOW[SI],0	; BUFFER OVERFLOWS
	MOV	WORD PTR EOVRUN[SI],0	; RECEIVE OVERRUNS
	MOV	WORD PTR EBREAK[SI],0	; BREAK CHARS
	MOV	WORD PTR EFRAME[SI],0	; FRAMING ERRORS
	MOV	WORD PTR EPARITY[SI],0	; PARITY ERRORS
	MOV	WORD PTR EXMIT[SI],0	; TRANSMISSION ERRORS
	MOV	WORD PTR EDSR[SI],0	; DATA SET READY ERRORS
	MOV	WORD PTR ECTS[SI],0	; CLEAR TO SEND ERRORS
; SENSE PC TYPE AND SET DIVISOR ACCORDINGLY
	MOV	BX,ROM		; HIGH ROM SEGMENT
	MOV	ES,BX		; TO ES
	ASSUME	ES:ROM
	MOV	DIV50,DIV50PC	; ASSUME PC OR XT
	CMP	ROMID,0FDH	; IS IT A PCjr?
	 JNE	INST0		; JUMP IF NOT
	MOV	DIV50,DIV50JR	; ELSE SET JR DIVISOR
; SET 8250 PORT ADDRESSES
INST0:	MOV	BX,RBDA 	; ROM BIOS DATA AREA
	MOV	ES,BX		; TO ES
	ASSUME	ES:RBDA
	CMP	PORT[SI],1	; PORT 1?
	 JE	INST3F8 	; Yes
	CMP	PORT[SI],2	; PORT 2?
	 JE	INST2F8 	; Yes
	CMP	PORT[SI],3	; PORT 3?
	 JE	INST3E8 	; Yes
	CMP	PORT[SI],4	; PORT 4?
	 JE	INST2E8 	; Yes
	INT	20H		; N.O.T.A. (Caller is screwed up badly!)

INST3F8:MOV	AX,3F8H 	; COM1 BASE PORT ADDRESS
	JMP	SHORT INST2	; CONTINUE

INST2F8:MOV	AX,2F8H 	; COM2 BASE PORT ADDRESS
	JMP	SHORT INST2	; CONTINUE

INST3E8:
	CMP	RS232_BASE+4,0000H     ; We have information?
	JE	INST3E8_A	       ; No --> Use default
	MOV	AX,RS232_BASE+4        ; Yes --> Use provided info
	JMP	SHORT INST2	; CONTINUE

INST3E8_A:
	MOV	AX,3E8H 	; COM3 BASE PORT ADDRESS
	JMP	SHORT INST2	; CONTINUE

INST2E8:
	CMP	RS232_BASE+6,0000H     ; We have information?
	JE	INST2E8_A	       ; No --> Use default
	MOV	AX,RS232_BASE+6        ; Yes --> Use provided info
	JMP	SHORT INST2	; CONTINUE

INST2E8_A:
	MOV	AX,2E8H 	; COM4 BASE PORT ADDRESS
	; Fall into INST2

INST2:	CMP	AX,RS232_BASE	; INSTALLED?
	 JE	INST2A		; JUMP IF SO
	CMP	AX,RS232_BASE+2 ; INSTALLED?
	 JE	INST2A		; JUMP IF SO
	CMP	AX,RS232_BASE+4 ; INSTALLED?
	 JE	INST2A		; JUMP IF SO
	CMP	AX,RS232_BASE+6 ; INSTALLED?
	 JNE	INST666 	; JUMP IF NOT
	; Fall into INST2A

INST2A: MOV	BX,DATREG	; OFFSET OF TABLE OF PORTS
	MOV	CX,7		; LOOP SIX TIMES
INST3:	MOV	WORD PTR [SI][BX],AX ; SET PORT ADDRESS
	INC	AX		; NEXT PORT
	ADD	BX,2		; NEXT WORD ADDRESS
	LOOP	INST3		; RS232 BASE LOOP
; RESET VECTOR TO POINT TO OUR HANDLER
	MOV	AREA1.INT_HNDLR,OFFSET INT_HNDLR1   ;;;Aready done in SaveCom
	MOV	AREA2.INT_HNDLR,OFFSET INT_HNDLR2
	MOV	AREA3.INT_HNDLR,OFFSET INT_HNDLR3
	MOV	AREA4.INT_HNDLR,OFFSET INT_HNDLR4
	MOV	AH,25H		; SET INTERRUPT VECTOR CONTENTS
	MOV	AL,INT_COM[SI]	; INTERRUPT NUMBER
	MOV	DX,OFFSET DGROUP:INT_HNDLR[SI] ; OUR INTERRUPT HANDLER
	PUSH	DS		; SAVE DATA SEGMENT
	PUSH	CS		; COPY CS
	POP	DS		; TO DS
	INT	DOS		; DOS FUNCTION
	POP	DS		; RECOVER DATA SEGMENT
; PORT INSTALLED
INSTX:	MOV	INSTALLED[SI],1 ; PORT INSTALLED
	POP	ES		; RESTORE ES
	mov ax,1
	pop si
	mov sp,bp
	pop bp
	RET			; DONE

; PORT NOT INSTALLED
INST666:MOV	INSTALLED[SI],0 ; PORT NOT INSTALLED ON THIS PC
	POP	ES		; RESTORE ES
	mov ax,0
	pop si
	mov sp,bp
	pop bp
	RET			; DONE
_install_com	ENDP
	PAGE
;
; RESTORE ORIGINAL INTERRUPT VECTOR
;
_restore_com	PROC FAR
	push bp
	mov bp,sp
	push si
	MOV	SI,CURRENT_AREA ; SI POINTS TO DATA AREA
	MOV	INSTALLED[SI],0 ; PORT IS NO LONGER INSTALLED
	MOV	AH,25H		; SET INTERRUPT VECTOR FUNCTION
	MOV	AL,INT_COM[SI]	; INTERRUPT NUMBER
	MOV	DX,OLD_COM_OFF[SI] ; OLD OFFSET TO DX
	MOV	BX,OLD_COM_SEG[SI] ; OLD SEG
	PUSH	DS		; SAVE DS
	MOV	DS,BX		; TO DS
	INT	DOS		; DOS FUNCTION
	POP	DS		; RECOVER DS
	pop si
	mov sp,bp
	pop bp
	RET			; DONE
_restore_com	ENDP
	PAGE
;
; OPEN_COM ON CURRENT PORT
;
; CLEAR BUFFERS
; RE-INITIALIZE THE INTEL 8250 UART
; ENABLE INTERRUPTS ON THE INTEL 8259 INTERRUPT CONTROL CHIP
;
; [bp+6] = BAUD RATE
; [bp+8] = CONNECTION: M(ODEM), D(IRECT)
; [bp+10] = PARITY:	N(ONE), O(DD), E(VEN), S(PACE), M(ARK)
; [bp+12] = STOP BITS:	1, 2
; [bp+14] = XON/XOFF:	E(NABLED), D(ISABLED)
;
_open_com	PROC FAR
	push bp
	mov bp,sp
	push si
	MOV	SI,CURRENT_AREA 	; SI POINTS TO DATA AREA

	CLI				; INTERRUPTS OFF
	mov ax,[bp+6]
	MOV	BAUD_RATE[SI],AX	; SET
	mov bh,[bp+8]
	MOV	CONNECTION[SI],BH	;     ARGS
	mov bl,[bp+10]
	MOV	PARITY[SI],BL		;	   IN
	mov ch,[bp+12]
	MOV	STOP_BITS[SI],CH	;	      MEMORY
	mov cl,[bp+14]
	MOV	XON_XOFF[SI],CL

; RESET FLOW CONTROL
	MOV	HOST_OFF[SI],0		; HOST FLOWING
	MOV	PC_OFF[SI],0		; PC FLOWING

; RESET BUFFER COUNTS AND POINTERS
	MOV	START_TDATA[SI],0
	MOV	END_TDATA[SI],0
	MOV	START_RDATA[SI],0
	MOV	END_RDATA[SI],0
	MOV	SIZE_TDATA[SI],0
	MOV	SIZE_RDATA[SI],0

	TEST	INSTALLED[SI],1 	; PORT INSTALLED?
	JNZ	OC1			; SKIP IF SO
	JMP	OCX			; ELSE ABORT
OC1:
; RESET THE 8250
	MOV	AL,0
	MOV	DX,MCR[SI]
	OUT	DX,AL
	JMP	$+2		; I/O DELAY FOR JR

	MOV	DX,LSR[SI]	; RESET LINE STATUS CONDITION
	IN	AL,DX
	JMP	$+2		; I/O DELAY FOR JR
	MOV	DX,DATREG[SI]	; RESET RECSIVE DATA CONDITION
	IN	AL,DX
	JMP	$+2		; I/O DELAY FOR JR
	MOV	DX,MSR[SI]	; RESET MODEM DELTAS AND CONDITIONS
	IN	AL,DX

; CONVERT PASSED BAUD RATE TO 8250 DIVISOR
	MOV	AX,50		; 50 BAUD
	MUL	DIV50		; TIMES ITS DIVISOR
	DIV	BAUD_RATE[SI]	; OTHER SPEEDS ARE PROPORTIONAL
	MOV	BX,AX		; RESULT TO BX

; SET 8250 DIVISOR
	MOV	DX,LCR[SI]	; LINE CONTROL REGISTER
	MOV	AL,80H		; HI BIT ON
	OUT	DX,AL		; SET DLAB = 1
	JMP	$+2		; I/O DELAY FOR JR
	MOV	DX,WORD PTR DLL[SI]	; LEAST SIGNIFICANT BYTE
	MOV	AL,BL		; LSB FROM TABLE
	OUT	DX,AL		; SET LSB ON 8250
	JMP	$+2		; I/O DELAY FOR JR
	MOV	DX,WORD PTR DLH[SI]	; MOST SIGNIFICANT BYTE
	MOV	AL,BH		; MSB FROM TABLE
	OUT	DX,AL		; SET MSB ON 8250
	JMP	$+2		; I/O DELAY FOR JR

; SET PARITY AND NUMBER OF STOP BITS
	MOV	AL,03H		; NONE OR SPACE PARITY IS THE DEFAULT
	CMP	PARITY[SI],'O'  ; ODD PARITY REQUESTED?
	JNE	P1		; JUMP IF NOT
	MOV	AL,0AH		; SELECT ODD PARITY
	JMP	SHORT P3	; CONTINUE
P1:	CMP	PARITY[SI],'E'  ; EVEN PARITY REQUESTED?
	JNE	P2		; JUMP IF NOT
	MOV	AL,1AH		; SELECT EVEN PARITY
	JMP	SHORT P3	; CONTINUE
P2:	CMP	PARITY[SI],'M'  ; MARK PARITY REQUESTED?
	JNE	P3		; JUMP IF NOT
	MOV	AL,2AH		; SELECT MARK PARITY
P3:	TEST	STOP_BITS[SI],2 ; 2 STOP BITS REQUESTED?
	JZ	STOP1		; NO
	OR	AL,4		; YES
STOP1:	MOV	DX,LCR[SI]	; LINE CONTROL REGISTER
	OUT	DX,AL		; SET 8250 PARITY MODE AND DLAB=0

; ENABLE INTERRUPTS ON 8259 AND 8250
	IN	AL,INTA01	; SET ENABLE BIT ON 8259
	AND	AL,NIRQ[SI]
	OUT	INTA01,AL
	MOV	DX,IER[SI]	; ENABLE INTERRUPTS ON 8250
	MOV	AL,5		; RECEIVE & LINE ERROR
	OUT	DX,AL
	JMP	$+2		; I/O DELAY FOR JR
	MOV	DX,MCR[SI]	; SET DTR AND ENABLE INT DRIVER
	MOV	AL,0BH
	OUT	DX,AL

OCX:	STI			; INTERRUPTS ON
	pop si
	mov sp,bp
	pop bp
	RET			; DONE
_open_com	ENDP
	PAGE;
;
; void far ioctl_com(int Flags, int Arg1, ...)
;	Flags have bits saying what to do or change (IGNORED TODAY)
;	Arg1, ...  are the new values
;
_ioctl_com PROC FAR
	PUSH BP
	MOV BP,SP
	PUSHF				; Save interrupt context
	PUSH SI
	MOV SI,CURRENT_AREA		; Pointer to COMi private area
	CLI				; Prevent surprises
	TEST INSTALLED[SI],1
	 JE IOCTLX			; No good.  Just return.
	MOV AX,[BP+6]			; Flags
	; Check bits here...
	MOV AX,[BP+8]			; Line speed
	MOV BAUD_RATE[SI],AX		; Save in parameter block
	CALL Set_Baud			; Set the baud rate in UART
IOCTLX: POP SI
	POPF				; Restore interrupt state
	MOV SP,BP
	POP BP
	RET
_ioctl_com	ENDP
	PAGE;
; ioctl-called routines (internal) ...

; SI:	COMi private block
;	CALL Set_Baud
; Returns: (nothing)
; Clobber: AX, BX, DX

Set_Baud PROC NEAR
	MOV AX,50
	MUL DIV50			; Could be different on a PCJr!
	DIV BAUD_RATE[SI]		; Get right number for the UART
	MOV BX,AX			; Save it
	MOV DX,LCR[SI]			; Line Control Register
	IN AL,DX			; Get current size, stops, parity,...
	PUSH AX
	OR AL,80H			; DLAB bit
	OUT DX,AL			; Talk to the baud rate regs now
	MOV DX,WORD PTR DLL[SI] 	; Least significant byte
	MOV AL,BL			; New value
	OUT DX,AL			; To UART
	MOV DX,WORD PTR DLH[SI] 	; Most signifiant byte
	MOV AL,BH			; New value
	OUT DX,AL
	MOV DX,LCR[SI]			; Line Control Register
	POP AX
	OUT DX,AL			; Turn off DLAB, keep saved settings
	RET
Set_Baud ENDP
	PAGE;
;
; CLOSE_COM - TURNS OFF INTERRUPTS FROM THE COMMUNICATIONS PORT
;
_close_com	PROC FAR
	push bp
	mov bp,sp
	push si
	MOV	SI,CURRENT_AREA ; SI POINTS TO DATA AREA
	TEST	INSTALLED[SI],1 ; PORT INSTALLED?
	JZ	CCX		; ABORT IF NOT

; TURN OFF 8250
	MOV	DX,IER[SI]
	MOV	AL,0
	OUT	DX,AL

; TURN OFF 8259
	MOV	DX,INTA01
	IN	AL,DX
	OR	AL,IRQ[SI]
	JMP	$+2		; DELAY FOR AT
	OUT	DX,AL

CCX:	pop si
	mov sp,bp
	pop bp
	RET
_close_com	ENDP
	PAGE
;
; DTR_OFF - TURNS OFF DTR TO TELL MODEMS THAT THE TERMINAL HAS GONE AWAY
;	    AND TO HANG UP THE PHONE
;
_dtr_off	PROC FAR
	push bp
	mov bp,sp
	push si
	PUSHF			; SAVE FLAGS
	PUSH	AX		; SAVE REGS
	PUSH	DX
	PUSH	SI
	MOV	SI,CURRENT_AREA ; SI POINTS TO DATA AREA
	TEST	INSTALLED[SI],1 ; PORT INSTALLED?
	JZ	DFX		; ABORT IF NOT

	MOV	DX,MCR[SI]
	MOV	AL,08H		; DTR OFF, RTS OFF, OUT2 ON
	OUT	DX,AL
DFX:	POP	SI		; RECOVER REGS
	POP	DX
	POP	AX
	POPF			; RECOVER FLAGS
	pop si
	mov sp,bp
	pop bp
	RET
_dtr_off	ENDP
;
; DTR_ON - TURNS DTR ON
;
_dtr_on PROC FAR
	push bp
	mov bp,sp
	push si
	PUSHF			; SAVE FLAGS
	PUSH	AX		; SAVE REGS
	PUSH	DX
	PUSH	SI
	MOV	SI,CURRENT_AREA ; SI POINTS TO DATA AREA
	TEST	INSTALLED[SI],1 ; PORT INSTALLED?
	JZ	DNX		; ABORT IF NOT

	MOV	DX,MCR[SI]
	MOV	AL,0BH
	OUT	DX,AL
DNX:	POP	SI		; RECOVER REGS
	POP	DX
	POP	AX
	POPF			; RECOVER FLAGS
	pop si
	mov sp,bp
	pop bp
	RET			; DONE
_dtr_on ENDP
	PAGE
;
; R_COUNT - RETURNS NUMBER OF BYTES IN THE RECEIVE BUFFER IN AX
;	      total in DX
;
_r_count	PROC FAR
	push bp
	mov bp,sp
	push si
	PUSHF			; SAVE FLAGS
	PUSH	SI		; SAVE SI
	MOV	SI,CURRENT_AREA ; SI POINTS TO DATA AREA
	MOV	AX,0		; NOTHING RECEIVED IF NOT INSTALLED
	mov dx,R_SIZE

	TEST	INSTALLED[SI],1 ; PORT INSTALLED?
	JZ	RCX		; ABORT IF NOT

	MOV	AX,SIZE_RDATA[SI] ; GET NUMBER OF BYTES USED
RCX:	POP	SI		; RESTORE SI
	POPF			; RESTORE FLAGS
	pop si
	mov sp,bp
	pop bp
	RET
_r_count	ENDP
	PAGE
;
; RECEIVE - RETURNS THE NEXT CHARACTER FROM THE RECEIVE BUFFER IN AL
;	     AND REMOVES IT FROM THE BUFFER
;	     THE PARITY BIT IS STRIPPED OFF
;
_receive_com	PROC FAR
	push bp
	mov bp,sp
	push si
	PUSHF				; SAVE FLAGS
	PUSH	BX			; SAVE REGS
	PUSH	SI
	MOV	SI,CURRENT_AREA 	; SI POINTS TO DATA AREA
	mov	ax,-1 ; -1 if bad call
	TEST	INSTALLED[SI],1 	; PORT INSTALLED?
	JZ	RCVX			; ABORT IF NOT
	CMP	SIZE_RDATA[SI],0	; ANY CHARACTERS?
	JE	RCVX			; ABORT IF NOT

; GOOD CALL
	mov ah,0	; good call
	MOV	BX,START_RDATA[SI]	; GET POINTER TO OLDEST CHAR
	MOV	AL,RDATA[SI][BX]	; GET CHAR FROM BUFFER
; MOD BY LBA, 10/6/85 FOR PROPER SUPPORT OF NO PARITY COMMUNICATIONS
	CMP	PARITY[SI],'N'          ; ARE WE RUNNING WITH NO PARITY?
	JE	L11			; IF SO, DON'T STRIP HIGH BIT
; END OF MOD
	AND	AL,7FH			; STRIP PARITY BIT
L11:	INC	BX			; BUMP START_RDATA
	CMP	BX,R_SIZE		; SEE IF PAST END
	JB	L12			; IF NOT THEN SKIP
	MOV	BX,0			; ADJUST TO BEGINNING
L12:	MOV	START_RDATA[SI],BX	; SAVE THE NEW START_RDATA VALUE
	DEC	SIZE_RDATA[SI]		; ONE LESS CHARACTER
	CMP	XON_XOFF[SI],'E'        ; FLOW CONTROL ENABLED?
	JNE	RCVX			; DO NOTHING IF DISABLED
	CMP	HOST_OFF[SI],1		; HOST TURNED OFF?
	JNE	RCVX			; JUMP IF NOT
	CMP	SIZE_RDATA[SI],R_SIZE/20 ; RECEIVE BUFFER NEARLY EMPTY?
	JGE	RCVX			; DONE IF NOT
	MOV	HOST_OFF[SI],0		; TURN ON HOST IF SO
	PUSH	AX			; SAVE RECEIVED CHAR
	MOV	AL,CONTROL_Q		; TELL HIM TO TALK
	CLI				; TURN OFF INTERRUPTS
	CALL	SENDII			; SEND IMMEDIATELY INTERNAL
	STI				; INTERRUPTS BACK ON
	POP	AX			; RESTORE RECEIVED CHAR
RCVX:	POP	SI			; RECOVER REGS
	POP	BX
	POPF				; RECOVER FLAGS
	pop si
	mov sp,bp
	pop bp
	RET				; DONE
_receive_com	ENDP
	PAGE
;
; S_COUNT - RETURNS IN AX THE AMOUNT OF FREE SPACE
;	       REMAINING IN THE TRANSMIT BUFFER
;	 DX total size
;
_s_count	PROC FAR
	push bp
	mov bp,sp
	push si
	PUSHF			; SAVE FLAGS
	PUSH	SI		; SAVE SI
	MOV	SI,CURRENT_AREA ; SI POINTS TO DATA AREA
	MOV	AX,0		; NO SPACE LEFT IF NOT INSTALLED
	mov dx,S_SIZE

	TEST	INSTALLED[SI],1 ; PORT INSTALLED?
	JZ	SCX		; ABORT IF NOT

	MOV	AX,S_SIZE	; GET THE SIZE OF THE X-MIT BUFFER
	SUB	AX,SIZE_TDATA[SI] ; SUBTRACT THE NUMBER OF BYTES USED
SCX:	POP	SI		; RECOVER SI
	POPF			; RESTORE FLAGS
	pop si
	mov sp,bp
	pop bp
	RET
_s_count	ENDP
	PAGE
;
; SEND - SEND A CHARACTER
;	[bp+6] = char
;
_send_com	PROC FAR
	push bp
	mov bp,sp
	push si
	mov al,[bp+6]
	PUSHF				; SAVE FLAGS
	PUSH	AX			; SAVE REGS
	PUSH	BX
	PUSH	DX
	PUSH	SI
	MOV	SI,CURRENT_AREA 	; SI POINTS TO DATA AREA
	TEST	INSTALLED[SI],1 	; PORT INSTALLED?
	JZ	L44			; ABORT IF NOT

	CMP	SIZE_TDATA[SI],S_SIZE	; BUFFER FULL?
	JL	L4A			; JUMP IF NOT
	INC	WORD PTR EOVFLOW[SI]	; BUMP ERROR COUNT
	JMP	SHORT L44		; PUNT
L4A:	MOV	BX,END_TDATA[SI]	; BX POINTS TO FREE SPACE
	MOV	TDATA[SI][BX],AL	; MOVE CHAR TO BUFFER
	INC	BX			; INCREMENT END_TDATA
	CMP	BX,S_SIZE		; SEE IF PAST END
	JL	L4			; IF NOT THEN SKIP
	MOV	BX,0			; ADJUST TO BEGINNING
L4:	MOV	END_TDATA[SI],BX	; SAVE NEW END_TDATA
	INC	SIZE_TDATA[SI]		; ONE MORE CHARACTER IN X-MIT BUFFER
	MOV	DX,IER[SI]		; INTERRUPT ENABLE REGISTER
	IN	AL,DX			; GET IT
	TEST	AL,2			; SEE IF TX INTERRUPTS ARE ENABLED
	JNZ	L44			; JUMP IF SO
	MOV	AL,7			; IF NOT THEN RCV, TX, LINE ERROR
	OUT	DX,AL			; ARE ENABLED
L44:	POP	SI			; RESTORE REGS
	POP	DX
	POP	BX
	POP	AX
	POPF				; RESTORE FLAGS
	pop si
	mov sp,bp
	pop bp
	RET				; DONE
_send_com	ENDP
	PAGE
;
; SENDI - SEND A CHARACTER IMMEDIATELY
; [bp+6] = char to send
;
_sendi_com	PROC FAR
	push bp
	mov bp,sp
	push si
	mov al,[bp+6]
	PUSHF				; SAVE FLAGS
	PUSH	AX			; SAVE REGS
	PUSH	BX
	PUSH	DX
	PUSH	SI
	MOV	SI,CURRENT_AREA 	; SI POINTS TO DATA AREA
	TEST	INSTALLED[SI],1 	; PORT INSTALLED?
	JZ	LQ44			; ABORT IF NOT

	CLI				; MASK INTERRUPTS
	CALL	SENDII			; CALL INTERNAL SEND IMMEDIATE
	STI				; INTERRRUPTS BACK ON

LQ44:	POP	SI			; RESTORE REGS
	POP	DX
	POP	BX
	POP	AX
	POPF				; RESTORE FLAGS
	pop si
	mov sp,bp
	pop bp
	RET				; DONE
_sendi_com	ENDP
	PAGE
;
; INTERNAL ROUTINE
; DEPENDS ON CALLER TO KEEP INTERRUPTS CLEARED AND SET SI
; SENDI - SEND A CHARACTER IMMEDIATELY (PUT AT BEGINNING OF QUEUE)
; AL = CHAR TO WRITE
;
SENDII	PROC	NEAR
	PUSH	DX			; SAVE DX
	CMP	SIZE_TDATA[SI],S_SIZE	; BUFFER FULL?
	JB	LI4A			; JUMP IF NOT
	INC	WORD PTR EOVFLOW[SI]	; BUMP ERROR COUNT
	MOV	BX,START_TDATA[SI]	; BX POINTS TO FIRST CHAR IN BUFFER
	MOV	TDATA[SI][BX],AL	; CLOBBER FIRST CHAR IN BUFFER
	JMP	SHORT LI4B		; CONTINUE
LI4A:	MOV	BX,START_TDATA[SI]	; BX POINTS TO FIRST CHAR IN BUFFER
	DEC	BX			; BACKUP THE PTR
	CMP	BX,-1			; BEFORE BEGINNING?
	JNE	LI4			; JUMP IF NOT
	MOV	BX,S_SIZE-1		; POINT TO END IF SO
LI4:	MOV	TDATA[SI][BX],AL	; MOVE CHAR TO BUFFER
	MOV	START_TDATA[SI],BX	; SAVE NEW START_TDATA
	INC	SIZE_TDATA[SI]		; ONE MORE CHARACTER IN X-MIT BUFFER
LI4B:	MOV	DX,IER[SI]		; INTERRUPT ENABLE REGISTER
	IN	AL,DX			; GET IT
	TEST	AL,2			; SEE IF TX INTERRUPTS ARE ENABLED
	JNZ	LI44			; JUMP IF SO
	MOV	AL,7			; IF NOT THEN RCV, TX, LINE ERROR
	OUT	DX,AL			; ARE ENABLED
LI44:	POP	DX			; RECOVER DX
	RET				; DONE
SENDII	ENDP
	PAGE
;
; S_LOCAL
;
_send_local	PROC FAR
	push bp
	mov bp,sp
	push si
	mov al,[bp+6]
	PUSHF				; SAVE FLAGS
	PUSH	AX			; SAVE REGS
	PUSH	BX
	PUSH	SI
	MOV	SI,CURRENT_AREA 	; SI POINTS TO DATA AREA
	TEST	INSTALLED[SI],1 	; PORT INSTALLED?
	JZ	SLX			; ABORT IF NOT

	CLI				; INTERRUPTS OFF
	CMP	SIZE_RDATA[SI],R_SIZE	; SEE IF ANY ROOM
	JB	L13A			; SKIP IF ROOM
	INC	WORD PTR EOVFLOW[SI]	; BUMP OVERFLOW COUNT
	JMP	SHORT L14		; PUNT
L13A:	MOV	BX,END_RDATA[SI]	; BX POINTS TO FREE SPACE
	MOV	RDATA[SI][BX],AL	; SEND DATA TO BUFFER
	INC	BX			; INCREMENT END_RDATA POINTER
	CMP	BX,R_SIZE		; SEE IF GONE PAST END
	JL	L13			; IF NOT THEN SKIP
	MOV	BX,0			; ELSE ADJUST TO BEGINNING
L13:	MOV	END_RDATA[SI],BX	; SAVE VALUE
	INC	SIZE_RDATA[SI]		; GOT ONE MORE CHARACTER
L14:	STI				; INTERRUPTS BACK ON

SLX:	POP	SI			; RECOVER REGS
	POP	BX
	POP	AX
	POPF				; RECOVER FLAGS
	pop si
	mov sp,bp
	pop bp
	RET				; DONE
_send_local	ENDP
	PAGE
;
; BREAK - CAUSES A BREAK TO BE SENT OUT ON THE LINE
;
_break_com	PROC FAR
	push bp
	mov bp,sp
	push si
	PUSHF			; SAVE FLAGS
	PUSH	AX		; SAVE REGS
	PUSH	CX
	PUSH	DX
	MOV	SI,CURRENT_AREA ; SI POINTS TO DATA AREA
	TEST	INSTALLED[SI],1 ; PORT INSTALLED?
	JZ	BRX		; ABORT IF NOT

	MOV	DX,LCR[SI]	; LINE CONTROL REGISTER
	IN	AL,DX		; GET CURRENT SETTING
	JMP	$+2		; I/O DELAY FOR JR
	OR	AL,40H		; TURN ON BREAK BIT
	OUT	DX,AL		; SET IT ON THE 8250
	MOV	CX,0C000H	; WAIT APPROX. 1/4 SEC.
BREAK1: LOOP	BREAK1		; BUSY WAIT
	AND	AL,0BFH 	; TURN OFF BREAK BIT
	OUT	DX,AL		; RESTORE LINE CONTROL REGISTER
BRX:	POP	DX		; RECOVER REGS
	POP	CX
	POP	AX
	POPF			; RECOVER FLAGS
	pop si
	mov sp,bp
	pop bp
	RET			; DONE
_break_com	ENDP
	PAGE
;
; COM_ERRORS - RETURN POINTER IN dx:ax TO ERROR COUNTS
;
_com_errors	PROC FAR
	push bp
	mov bp,sp
	mov ax,OFFSET DGROUP:CURRENT_AREA
	add ax,ERROR_BLOCK
	mov dx,ds
	mov sp,bp
	pop bp
	RET			; DONE
_com_errors	ENDP
	PAGE
;
; INTERNAL ROUTINE
; BUMP ERROR COUNTS FROM LINE STATUS IN AL
;
E_BUMP	PROC	NEAR
	TEST	AL,2		; OVERRUN ERROR?
	JZ	LSI1		; JUMP IF NOT
	INC	WORD PTR EOVRUN[SI]	; ELSE BUMP ERROR COUNT
LSI1:	TEST	AL,4		; PARITY ERROR?
	JZ	LSI2		; JUMP IF NOT
	INC	WORD PTR EPARITY[SI]	; ELSE BUMP ERROR COUNT
LSI2:	TEST	AL,8		; FRAMING ERROR?
	JZ	LSI3		; JUMP IF NOT
	INC	WORD PTR EFRAME[SI]	; ELSE BUMP ERROR COUNT
LSI3:	TEST	AL,16		; BREAK RECEIVED?
	JZ	LSI4		; JUMP IF NOT
	INC	WORD PTR EBREAK[SI]	; ELSE BUMP ERROR COUNT
LSI4:	RET			; DONE
E_BUMP	ENDP
	PAGE
;
; INTERNAL ROUTINE
; MODEM SEND PROTOCOL
;
M_PROTOCOL PROC NEAR
	CMP	CONNECTION[SI],'M' ; MODEM CONNECTION?
	JNE	S3		; IF NOT, SKIP DSR & CTS PROTOCOL

; TELL MODEM WE'RE READY TO SEND
	MOV	DX,MCR[SI]	; MODEM CONTROL REGISTER
	MOV	AL,00001011B	; OUT 2, RTS, DTR
	OUT	DX,AL		; TERMINAL READY, REQUEST TO SEND
	JMP	$+2		; I/O DELAY FOR JR

; WAIT UNTIL MODEM SAYS DATA SET READY
	MOV	CX,1000 	; TIMEOUT COUNT
	MOV	DX,MSR[SI]	; MODEM STATUS REGISTER
S1:	IN	AL,DX		; GET MODEM STATUS
	TEST	AL,20H		; DATA SET READY?
	JNZ	S1X		; YES, TEST CLEAR TO SEND
	LOOP	S1		; NO, BUSY WAIT
	INC	WORD PTR EDSR[SI]	; BUMP ERROR COUNT
	JMP	SHORT S3	; WE TIMED OUT
S1X:
; WAIT UNTIL MODEM SAYS IT'S CLEAR TO SEND
	MOV	CX,1000 	; TIMEOUT COUNT
S2:	IN	AL,DX		; GET MODEM STATUS
	TEST	AL,10H		; CLEAR TO SEND?
	JNZ	S2X		; YES
	LOOP	S2		; NO, KEEP TRYING
	INC	WORD PTR ECTS[SI]	; BUMP ERROR COUNT - WE TIMED OUT
S2X:
; TEST FOR TRANSMITTER READY
S3:	MOV	DX,LSR[SI]	; LINE STATUS REGISTER
	IN	AL,DX		; GET LINE STATUS
	TEST	AL,20H		; TRANSMITTER READY?
	JNZ	S4		; SKIP IF SO
	INC	WORD PTR EXMIT[SI]	; ELSE BUMP ERROR COUNT
S4:	RET			; DONE
M_PROTOCOL ENDP
	PAGE
;
; INTERNAL ROUTINES FOR FLOW CONTROL
;
; FLOW_IN - RESPOND TO FLOW CONTROL COMMANDS FROM HOST
; FLOW_OUT - ISSUE FLOW CONTROL COMMANDS TO HOST
;
FLOW_IN PROC	NEAR
	PUSH	AX		; SAVE CHAR
	CMP	XON_XOFF[SI],'E'; FLOW CONTROL ENABLED?
	JNE	FI_NOCARRY	; DO NOTHING IF DISABLED
	AND	AL,7FH		; STRIP PARITY
	CMP	AL,CONTROL_S	; STOP COMMAND RECEIVED?
	JNE	FI_1		; JUMP IF NOT
	MOV	PC_OFF[SI],1	; WE MUST SHUT UP
	STC			; Flag we got special character
	JMP	SHORT FI_EXIT	; Continue

FI_1:	CMP	AL,CONTROL_Q	; GO COMMAND RECEIVED?
	JNE	FI_NOCARRY	; NO, MUST BE NORMAL CHAR
	MOV	PC_OFF[SI],0	; WE START TALKING AGAIN
	STC			; Flag we got special character
	JMP	SHORT FI_EXIT	; Continue

FI_NOCARRY:
	CLC			; Not special character, no carry
FI_EXIT:
	POP	AX		; RESTORE CHAR
	RET			; DONE
FLOW_IN ENDP
;
FLOW_OUT PROC	NEAR
	CMP	XON_XOFF[SI],'E'; FLOW CONTROL ENABLED?
	JNE	FO_X		; DO NOTHING IF DISABLED
	CMP	HOST_OFF[SI],1	; HOST TURNED OFF?
	JE	FO_X		; JUMP IF SO
	CMP	SIZE_RDATA[SI],R_SIZE/2 ; RECEIVE BUFFER NEARLY FULL?
	JLE	FO_X		; DONE IF NOT
	MOV	AL,CONTROL_S	; TURN OFF HOST IF SO
	CALL	SENDII		; SEND IMMEDIATELY INTERNAL
	MOV	HOST_OFF[SI],1	; HOST IS NOW OFF
FO_X:	RET			; DONE
FLOW_OUT ENDP
	PAGE ;
;
; _MODEM_STATUS - Returns the modem status register in AL
;
; Bits are:	0x80:	-CD	(Carrier Detect, inverted)
;		0x40:	-RI	(Ring Indicator, inverted)
;		0x20:	-DSR	(Data Set Ready, inverted)
;		0x10:	-CTS	(Clear to Send, inverted)
;		0x08:	Delta Carrier Detect	(CD changed)
;		0x04:	Trailing edge of RI	(RI went OFF)
;		0x02:	Delta DSR		(DSR changed)
;		0x01:	Delta CTS		(CTS changed)
;
_modem_status	PROC FAR
	push bp
	mov bp,sp
	PUSH DX
	PUSH SI
	MOV SI,CURRENT_AREA		; Point to block for selected port
	MOV DX,MSR[SI]			; IO Addr of Modem Status Register
	IN AL,DX			; Get the live value
	XOR AH,AH			; Flush unwanted bits
	POP SI
	POP DX
	mov sp,bp
	pop bp
	RET
_modem_status	ENDP
	PAGE
;
; INT_HNDLR1 - HANDLES INTERRUPTS GENERATED BY COM1:
;
INT_HNDLR1 PROC  FAR
	PUSH	SI		; SAVE SI
	MOV	SI,OFFSET DGROUP:AREA1	; DATA AREA FOR COM1:
	JMP	SHORT INT_COMMON ; CONTINUE
;
; INT_HNDLR2 - HANDLES INTERRUPTS GENERATED BY COM2:
;
INT_HNDLR2 PROC  FAR
	PUSH	SI		; SAVE SI
	MOV	SI,OFFSET DGROUP:AREA2	; DATA AREA FOR COM2:
	JMP	SHORT INT_COMMON ; CONTINUE
;
; INT_HNDLR3 - HANDLES INTERRUPTS GENERATED BY COM3:
;
INT_HNDLR3 PROC  FAR
	PUSH	SI		; SAVE SI
	MOV	SI,OFFSET DGROUP:AREA3	; DATA AREA FOR COM3:
	JMP	SHORT INT_COMMON ; CONTINUE
;
; INT_HNDLR4 - HANDLES INTERRUPTS GENERATED BY COM4:
;
INT_HNDLR4 PROC  FAR
	PUSH	SI		; SAVE SI
	MOV	SI,OFFSET DGROUP:AREA4	; DATA AREA FOR COM4:
	; Fall into INT_COMMON

;
; BODY OF INTERRUPT HANDLER
;
INT_COMMON:
	PUSH	AX		; SAVE REGS
	PUSH	BX
	PUSH	CX
	PUSH	DX
	PUSH	BP
	PUSH	DI
	PUSH	DS
	PUSH	ES

	MOV AX,DGROUP		; Offsets are relative to DGROUP [WWP]
	MOV	DS,AX		; TO DS

; CLEAR THE INTERRUPT CONTROLLER FLAG
	MOV	DX,INTA00	; 8259 CONTROL PORT
	MOV	AL,EOI[SI]	; SPECIFIC END OF INTERRUPT
	OUT	DX,AL		; CLEAR FLAG

; FIND OUT WHERE INTERRUPT CAME FROM AND JUMP TO ROUTINE TO HANDLE IT
REPOLL:
	MOV	DX,IIR[SI]	; READ INTERRUPT STATUS REGISTER
	IN	AL,DX
	CMP	AL,4
	JE	RX_INT		; IF FROM THE RECEIVER
	CMP	AL,2
	JE	TX_INT		; IF FROM THE TRANSMITTER
	CMP	AL,6
	JE	LSTAT_INT	; INTERRUPT BECAUSE OF LINE STATUS
	CMP	AL,0
	JE	MSTAT_INT	; INTERRUPT BECAUSE OF MODEM STATUS
	JMP	FAR PTR INT_END ; DONE, EXIT (DON'T FIX FAR PTR STUFF)

LSTAT_INT:
	MOV	DX,LSR[SI]	; READ AND IGNORE LINE STATUS
	IN	AL,DX		;
	CALL	E_BUMP		; JUST BUMP ERROR COUNTS
	JMP	REPOLL		; SEE IF ANY MORE INTERRUPTS

MSTAT_INT:
	MOV	DX,MSR[SI]	; READ AND IGNORE MODEM STATUS
	IN	AL,DX		;
	JMP	REPOLL		; SEE IF ANY MORE INTERRUPTS

TX_INT:
	CMP	PC_OFF[SI],1	; HAVE WE BEEN TOLD TO SHUT UP?
	JNE	GOODTX1 	; JUMP IF NOT
	CMP	HOST_OFF[SI],1	; HAS HOST ALSO SHUT UP?
	JNE	SEND_NO_MORE	; JUMP IF NOT

; CLEAR XON/XOFF DEADLOCK
	MOV	PC_OFF[SI],0	; WE SPEAK
	MOV	HOST_OFF[SI],0	; THEY REPLY
	MOV	AL,CONTROL_Q	; BUT ONLY WHEN
	CALL	SENDII		; WE LET THEM

GOODTX1:
	CMP	SIZE_TDATA[SI],0 ; SEE IF ANY MORE DATA TO SEND
	JG	HAVE_DATA	; IF POSITIVE THEN THERE IS DATA TO SEND

; IF NO DATA TO SEND THEN RESET TX INTERRUPT AND RETURN
SEND_NO_MORE:
	MOV	DX,IER[SI]		;
	MOV	AL,5			; JUST RCV AND LINE ERROR
	OUT	DX,AL			; ARE SET
	JMP	REPOLL			;

HAVE_DATA:
	CALL	M_PROTOCOL		; DO MODEM PROTOCOL IF NECESSARY

	MOV	BX,START_TDATA[SI]	; BX POINTS TO NEXT CHAR. TO BE SENT
	MOV	AL,TDATA[SI][BX]	; GET DATA FROM BUFFER
	MOV	DX,DATREG[SI]		; DX EQUALS PORT TO SEND DATA TO
	OUT	DX,AL			; SEND DATA
	INC	BX			; INCREMENT START_TDATA
	CMP	BX,S_SIZE		; SEE IF GONE PAST END
	JB	NTADJ			; IF NOT THEN SKIP
	MOV	BX,0			; RESET TO BEGINNING
NTADJ:	MOV	START_TDATA[SI],BX	; SAVE START_TDATA
	DEC	SIZE_TDATA[SI]		; ONE LESS CHARACTER IN X-MIT BUFFER
	JMP	REPOLL

RX_INT:
	MOV	DX,DATREG[SI]		; 8250 DATA REGISTER
	IN	AL,DX			; GET DATA
	CALL	FLOW_IN 		; RESPOND TO F.C. COMMANDS FROM HOST
	JC	REPOLL			; If flow control, do not save
					; ahd
	CMP	SIZE_RDATA[SI],R_SIZE	; SEE IF ANY ROOM
	JL	GOOD_RX1		; CONTINUE IF SO
	INC	WORD PTR EOVFLOW[SI]	; BUMP OVERFLOW ERROR COUNT
	JMP	REPOLL			; PUNT
GOOD_RX1:
	MOV	BX,END_RDATA[SI]	; BX POINTS TO FREE SPACE
	MOV	RDATA[SI][BX],AL	; MOVE DATA TO BUFFER
	INC	SIZE_RDATA[SI]		; GOT ONE MORE CHARACTER
	INC	BX			; INCREMENT END_RDATA POINTER
	CMP	BX,R_SIZE		; SEE IF GONE PAST END
	JB	NRADJ			; IF NOT THEN SKIP
	MOV	BX,0			; ELSE ADJUST TO BEGINNING
NRADJ:	MOV	END_RDATA[SI],BX	; SAVE VALUE
DO_FLOW:				; ahd
	CALL	FLOW_OUT		; ISSUE FLOW CONTROL COMMANDS TO HOST
	JMP	REPOLL			;

INT_END:
	POP	ES		; RESTORE REGS
	POP	DS
	POP	DI
	POP	BP
	POP	DX
	POP	CX
	POP	BX
	POP	AX

	POP	SI		; RESTORE SI, TOO
	IRET
INT_HNDLR4 ENDP
INT_HNDLR3 ENDP
INT_HNDLR2 ENDP
INT_HNDLR1 ENDP
COM_TEXT	ENDS
	END
