	TITLE   COM_PKG2 -- Last updated 10/6/85
        PAGE    75,132
;
;	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     500		; SIZE OF RECEIVE BUFFERS
S_SIZE	EQU     500		; SIZE OF TRANSMIT BUFFERS rhl/4/86
; INTERRUPT NUMBERS
INT_COM1 EQU	0CH		; COM1: FROM 8259
INT_COM2 EQU	0BH		; COM2: FROM 8259
; 8259 PORTS
INTA00  EQU     20H             ; 8259A PORT, A0 = 0
INTA01  EQU     21H             ; 8259A PORT, A0 = 1
; COM1: 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: 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
; 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
_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	_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
	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
	TEST	AL,1		; FIRST PORT?
	JZ	SP1		; IF NOT, IT MUST BE SECOND PORT
	MOV	AX,OFFSET DGROUP:AREA1	; SELECT COM1 DATA AREA
	JMP	SHORT SPX	; CONTINUE
SP1:	MOV	AX,OFFSET DGROUP:AREA2	; SELECT COM2 DATA AREA
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	AH,30H		; GET DOS VERSION NUMBER
	INT	DOS		; DOS FUNCTION
	CMP	AL,2		; AT LEAST DOS 2?
	JB	SVC1		; JUMP IF DOS 1
; SAVE OLD VECTOR WITH DOS 2 CALLS
	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
	JMP	SHORT SVCX	; DONE
; SAVE OLD VECTOR WITH DOS 1 CALLS
SVC1:	MOV	AX,0		; ZERO SEGMENT
	MOV	ES,AX		; ES POINTS TO INTERRUPT VECTORS
	MOV	BL,INT_COM[SI]	; OUR INTERRUPT NUMBER
	MOV	BH,0		; BL -> BX
	SHL	BX,1		; TIMES FOUR
	SHL	BX,1		; IS OFFSET OF VECTOR IN MEMORY
	MOV	AX,WORD PTR ES:[BX]	; SAVE
	MOV	OLD_COM_OFF[SI],AX	; THE
	ADD	BX,2			; OLD
	MOV	AX,WORD PTR ES:[BX]	; INTERRUPT
	MOV	OLD_COM_SEG[SI],AX	; VECTOR
SVCX:	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	; ALREADY INSTALLED?
	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
	TEST	PORT[SI],1	; PORT 1?
	JZ	INST1		; JUMP IF NOT
	MOV	AX,3F8H		; COM1 BASE PORT ADDRESS
	JMP	SHORT INST2	; CONTINUE
INST1:	MOV	AX,2F8H		; COM2 BASE PORT ADDRESS
INST2:	CMP	AX,RS232_BASE	; INSTALLED?
	JE	INST2A		; JUMP IF SO
	CMP	AX,RS232_BASE+2	; INSTALLED?
	JNE	INST666		; JUMP IF NOT
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
	MOV	AREA2.INT_HNDLR,OFFSET INT_HNDLR2
	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
;
; 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_2		; 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
	JMP	SHORT FI_2	; CONTINUE
FI_1:	CMP	AL,CONTROL_Q	; GO COMMAND RECEIVED?
	JNE	FI_2		; NO, MUST BE NORMAL CHAR
	MOV	PC_OFF[SI],0	; WE START TALKING AGAIN
FI_2:	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
;
; INT_HNDLR1 - HANDLES INTERRUPTS GENERATED BY COM1:
;
INT_HNDLR1 PROC  FAR
	PUSH	SI		; SAVE SI
	MOV	SI,OFFSET 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 AREA2	; DATA AREA FOR COM2:
;
; 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,SEG _DATA		; OUR DATA SEG
	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
        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
	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_HNDLR2 ENDP
INT_HNDLR1 ENDP
COM_TEXT	ENDS
        END
