;	Status byte definition (C_Status):

;	7   6   5   4   3   2   1   0
;	|   |   |   |   |   |   |   |____ Input buffer empty
;	|   |   |   |   |   |   |________ Input buffer full
;	|   |   |   |   |   |____________ Output buffer empty
;	|   |   |   |   |________________ Output buffer full
;	|   |   |   |____________________ Input buffer overflow
;	|   |   |________________________ Output buffer overflow
;	|   |____________________________ Hard handshake active (xmit stopped)
;	|________________________________ Soft handshake active (xmit stopped)

;	Control byte definition (C_Ctrl):

;	7   6   5   4   3   2   1   0
;	|   |   |   |   |   |   |   |____ Enable RTS handshake
;	|   |   |   |   |   |   |________ Enable CTS handshake
;	|   |   |   |   |   |____________ Enable software handshake
;	|   |   |   |   |________________
;	|   |   |   |____________________
;	|   |   |________________________
;	|   |____________________________
;	|________________________________

;********************************************************

; Macro:	PutChar
; Function:	Place character in transmit buffer (transmit a character)
; Entry:	AL <- Port # (0-3)
;		AH <- Status register
;		BX <- Byte array pointer
;		CH <- Character to put in buffer
;		DX <- Base address of port
; Destroyed:	ES,SI,DI,AL

; Note:	Port address passed is NOT checked for validity.
;	Subroutine ChkPort will create the proper entry environment

PUTCHAR MACRO

        test    ah, 00001000b           ;Is buffer full?
        JZ      @@putch1                ;No, OK to put character
        or      ah, 00100000b           ;set overflow flag
        JMP     @@putchx                ;and exit

@@putch1:
        shl     bl,1                    ;bx <- word array index
        mov     di, [C_outhead+bx]      ;bump the head pointer
        mov     si, [c_outtail+bx]
        inc     di
        cmp     di, [C_outsize+bx]
        jb      @@putch2
        xor     di,di
@@putch2:
        mov     [C_outhead+bx],di       ;save head pointer
        inc     [C_buffull+bx]          ;increment buffer-full word

        shl     bl,1                    ;BX <- pointer array index
        LES     BX, [C_outbufptr+bx]    ;Load ES with segment of output buf
        mov     [ES:BX+DI],ch           ;save the byte

        cmp     di, si                  ;Is output buffer full?
        jnz     @@putch3                ;no, go enable xmitter
        or      ah, 00001000b           ;Set status to output buffer full
@@putch3:
        xor     bh, bh
        mov     bl, al                  ;Save AL in BL and make BX byte index

        test    ah, 11000000b           ;soft/hard handshake on?
        jnz     @@putchx
;        test    ah, 00000100b
;        jz      @@putchx

        and     ah, 11111011b           ;Buffer is no longer empty
        inc     dl                      ;Change to IER register
        in      al, dx                  ;Read in the IER
        or      al, 00000010b           ;Turn on xmt interrupt
        NOP                             ;Slow down for slow machines
        NOP
        out     dx, al                  ;Write out the IER
        dec     dl                      ;Back to base reg
@@putchx:
        mov     [C_status+bx],ah        ;set status byte
;        mov     al,bl                   ;get port num back in AL
	ENDM

;******************************************************************
; MACRO: Test for modem status change
;
; Entry: DX - port base
;        BX - byte array index
;
; Destroyed: AX, CX

MSCHG   MACRO

;	Get modem status & control/status registers

@@Msc0:	ADD	DL,MSR			;DX <- 8250 MSR port address
	IN	AL,DX			;AL <- 8250 Modem status
	SUB	DL,MSR			;Back to base

	MOV	CL,[C_Status+BX]	;CL <- Status register
	MOV	CH,[C_Ctrl+BX]		;CH <- Control register

;	Control CTS status flag based on CTS status and handshake enable

	TEST	CH,00000010b		;CTS handshake enabled ?
	JZ	@@Msc2			; No, ignore CTS status
	TEST	AL,00010000b		;CTS asserted ?
	JNZ	@@Msc1			; Yes, enable transmit
	OR	CL,01000000b		;CTS inactive - hard handshake on
	JMP	@@Msc2			;
@@Msc1:	AND	CL,10111111b		;CTS active - hard handshake off
@@Msc2:	MOV	[C_Status+BX],CL	;Save status flags

;	Determine if transmitter should be enabled

@@Msc3:	ADD	DL,IER			;DX <- 8250 IER port address
	IN	AL,DX			;AL <- Interrupt enable register
	OR	AL,00000010b		;Enable transmitter by default
	TEST	CL,11000100b		;Enable transmitter ?
	JZ	@@Msc4			; Yes
	AND	AL,11111101b		; No - disable transmitter
@@Msc4:	OUT	DX,AL			;Update IER
        SUB     DL, IER

        ENDM

	IDEAL

	SEGMENT	DATA 	WORD PUBLIC

;	Externally accessed (TP program) variables
;	Note: 	Most of these variables are declared as arrays in the main
;		program; i.e. C_InSize : Array[1..4] Of Word

	EXTRN	C_InBufPtr :DWORD	;Pointer to input buffers
	EXTRN	C_OutBufPtr:DWORD	;Pointer to output buffers
	EXTRN	C_InSize   :WORD	;Size (bytes) of input buffers
	EXTRN	C_OutSize  :WORD	;Size (bytes) of output buffers
	EXTRN	C_InHead   :WORD	;Input (receive) head pointers
	EXTRN	C_OutHead  :WORD	;Output (transmit) head pointers
	EXTRN	C_InTail   :WORD	;Input (receive) tail pointers
	EXTRN	C_OutTail  :WORD	;Output (transmit) tail pointers
	EXTRN	C_RTSOn    :WORD	;Point at which RTS line is asserted
	EXTRN	C_RTSOff   :WORD	;Point at which RTS line is dropped
	EXTRN	C_StartChar:BYTE	;Start character for soft handshake
	EXTRN	C_StopChar :BYTE	;Stop character for soft handshake
	EXTRN	C_Status   :BYTE	;Status byte (see above)
	EXTRN	C_Ctrl     :BYTE	;Control byte (see above)
	EXTRN	C_PortOpen :BYTE	;Port-open flags
	EXTRN	C_PortAddr :WORD	;Base address of ports
	EXTRN	C_MaxCom   :BYTE	;Highest port # defined (single byte)
        EXTRN   C_bufFull  :WORD
        EXTRN   C_cascade  :BYTE
;        EXTRN   C_CharSend  :WORD
;        EXTRN   C_CharWrite :WORD
;        EXTRN   C_Temp      :WORD	;Used for debugging

;	8250 register offsets

IER	EQU	1			;Interrupt enable register
IIR	EQU	2			;Interrupt identification register
LCR	EQU	3			;Line control register
MCR	EQU	4			;Modem control register
LSR	EQU	5			;Line status register
MSR	EQU	6			;Modem status register
SCR	EQU	7			;8250 scratch register

	ENDS	DATA

;	Code segment declaration

	SEGMENT	CODE	BYTE PUBLIC

	ASSUME	CS:CODE,DS:DATA

;	Externally accessable procedures defined here

        PUBLIC  INT_Handler
	PUBLIC	ComReadCh
	PUBLIC	ComReadChW
	PUBLIC	ComWriteCh
	PUBLIC	ComWriteChW

;********************************************************
;*							*
;*	Subroutines that are used internally		*
;*							*
;********************************************************

; Subroutine:	ChkPort
; Function:	Check port parameter(s), ensure that port is OPEN
; Entry:	AL <- Port # (1 - C_MaxCom)
; Exit:		AL -> Adjusted port # (0 - 3)
;		AH -> Status register
;		BX -> Byte array index
;		DX -> Base address of port
;		Carry flag SET if parameters & port are OK
;
;	PROC	ChkPort		FAR
;
;	Determine if port # is valid
;
;	CMP	AL,[C_MaxCom]		;Port # > Maximum port # ?
;	JA	ChkErr			; Yes, exit w/error
;	CMP	AL,0			;Port # = 0 (invalid port #)
;	JZ	ChkErr			; Yes, exit w/error
;	DEC	AL			;AL <- Adjusted port #
;
;	Check if port open
;
;	XOR	BH,BH			;
;	MOV	BL,AL			;BX <- Byte array index
;	MOV	AH,[C_PortOpen+BX]	;AH <- Port-open flag
;	CMP	AH,0			;Port open ?
;	JZ	ChkErr			; No, exit w/error
;
;	Get status register and base port address in DX
;
;	MOV	AH,[C_Status+BX]	;AH <- Status register
;	SHL	BL,1			;BX <- Word array index
;	MOV	DX,[C_PortAddr+BX]	;DX <- Port address
;	SHR	BL,1			;BX <- Byte array index
;	STC				;Set carry (valid return)
;	RET				;Exit
;
;	Here if error
;
;ChkErr:	CLC				;Clear carry (invalid return)
;	RET				;Exit
;
;	ENDP	ChkPort

;	PROC	PutChar		FAR
;
;;	Check for buffer overflow
;
;	TEST	AH,00001000b		;Buffer full ?
;	JZ	PutCh1			; No, continue
;	OR	AH,00100000b		;Set buffer-overflow flag
;	JMP	PutChX			;Exit
;
;;	Increment head pointer
;
;PutCh1:	SHL	BL,1			;BX <- Word array index
;	MOV	DI,[C_OutHead+BX]	;DI <- Output head pointer
;	MOV	SI,[C_OutTail+BX]	;SI <- Output tail pointer
;	INC	DI			;Bump head pointer
;	CMP	DI,[C_OutSize+BX]	;Head >= Buffer size ?
;	JB	PutCh2			; No, continue
;	XOR	DI,DI			; Yes, reset pointer
;PutCh2:	MOV	[C_OutHead+BX],DI	;Save head pointer
;        INC     [C_buffull+bx]          ;Increment chars in buffer
;;        INC     [C_charwrite+bx]
;
;;	Place character in buffer
;
;	SHL	BL,1			;BX <- Pointer array index
;	LES	BX,[C_OutBufPtr+BX]	;ES:BX <- Pointer to output buffer
;	MOV	[ES:BX+DI],CH		;Place character in buffer
;
;;	Check for full buffer
;
;	CMP	DI,SI			;Head = Tail (buffer full) ?
;	JNZ	PutCh3			; No, buffer not full
;	OR	AH,00001000b		;Set buffer-full flag
;
;;	Determine if transmitter should be activated
;
;PutCh3:	XOR	BH,BH			;
;	MOV	BL,AL			;BX <- Byte array index
;
;	TEST	AH,11000000b		;Any inhibits (soft/hard hshake) ?
;	JNZ	PutChX			; Yes, do not activate xmit interrupt
;;	TEST	AH,00000100b		;Buffer empty ?
;;	JZ	PutChX			; No, xmit interrupt is on already
;
;	AND	AH,11111011b		;Reset buffer-empty flag
;	ADD	DL,IER			;Point to interrupt enable register
;	IN	AL,DX			;AL <- Interrupt enable register
;	OR	AL,00000010b		;Enable transmit interrupt
;       NOP                             ;Slow down for slow machines
;       NOP
;	OUT	DX,AL			;Update port
;	SUB	DL,IER			;Point back to base
;
;;	Here to exit
;
;PutChX:	MOV	[C_Status+BX],AH	;Save status byte
;	MOV	AL,BL			;
;	RET				;Exit
;
;	ENDP	PutChar


;********************************************************
;*							*
;*	Interrupt service routine for INT3,INT4		*
;*							*
;*	    INT3 typically used by COM2, COM4		*
;* 	    INT4 typically used by COM1, COM3		*
;*							*
;********************************************************

	PROC	INT_Handler 	FAR

	PUSH	AX			;Save environment
	PUSH	BX			;
	PUSH	CX			;
	PUSH	DX			;
	PUSH	SI			;
	PUSH	DI			;
	PUSH	DS			;
	PUSH	ES			;
	PUSH	BP			;

	MASM
	MOV	AX,SEG DATA		;AX <- Current data segment
	IDEAL
	MOV	DS,AX			;Set new data segment

INT_Id:	XOR	BX,BX			;BL <- Port # (start at 0)

;	Identify active port

IntID1:	MOV	AL,[C_PortOpen+BX]	;AL <- Port-open flag
	CMP	AL,0			;Port open ?
	JZ	IntID2			; No, don't check
	SHL	BL,1			;BX <- Word array index
	MOV	DX,[C_PortAddr+BX]	;DX <- Base address of port
	SHR	BL,1			;BX <- Byte array index
	ADD	DL,IIR			;Add in offset for IIR
	IN	AL,DX			;AL <- COMn IIR
	TEST	AL,00000001b		;Interrupt active on this port ?
	JZ	INT_Active		; (Bit 0 = 0 if active)
IntID2:	INC	BL			;Bump port #
	CMP	BL,[C_MaxCom]		;All ports checked ?
	JB	IntID1			; No, continue

;	Here to reset 8259 controller

	MOV	AL,20h			;AL <- EOI Acknowledge code
	OUT	20h,AL			;To 8259 PIC
        MOV     AL,[C_Cascade]          ;Move cascade flag
        CMP     AL,0                    ;No cascade?
        JE      IntID3                  ;Check it no cascaded irq

;	Reset cascade port

	MOV	DX,0A0h                 ;DX To 8259 cascade PIC
	MOV	AL,20h			;AL <- EOI Acknowledge code
	OUT	DX,AL			;To 8259 cascade PIC

;	Here to leave interrupt handler

IntID3:	POP	BP			;Restore envirionment
	POP	ES			;
	POP	DS			;
	POP	DI			;
	POP	SI			;
	POP	DX			;
	POP	CX			;
	POP	BX			;
	POP	AX			;

	IRET				;Interrupt exit

;	Active port (8250) found.
;	Determine cause of interrupt and execute appropriate handler.
;	Upon entry into routine, registers are set as follows:
;	BX <- Byte array index
;	DX <- Port address

INT_Active:
	DEC	DX			;
	DEC	DX			;DX <- Base address of COM port

	CMP	AL,0			;IIR = 0 (Modem status change) ?
	JZ	ComMsc			; Yes
	CMP	AL,2			;IIR = 2 (Character transmitted) ?
	JZ	ComXmt			; Yes
	CMP	AL,4			;IIR = 4 (Character received) ?
	JNZ	Next			;
	JMP	ComRcv			; Yes
Next:	CMP	AL,6			;IIR = 6 (Line status change) ?
	JZ	ComLsc			; Yes
	JMP	INT_Id			;Check other ports & exit

;*******************************************************
;		Line status change
;		NOTE: Currently unused
;*******************************************************

ComLsc:	ADD	DL,LSR			;Point to line status register
	IN	AL,DX			;Get LSR

;	The following code (which disables then enables the transmit
;	interrupt) is required to compensate for some "buggy" 8250's and
;	8250-emulating gate arrays.

	SUB	DL,LSR			;Back to base
	ADD	DL,IER			;Point to interrupt enable register
	IN	AL,DX			;Get IER
	MOV	AH,AL			;Save IER
	AND	AL,11111101b		;Mask transmit interrupt
	OUT	DX,AL			;Send modified to port
	MOV	AL,AH			;
        NOP                             ;slow down for slow machines
        NOP
	OUT	DX,AL			;Send original to port

	JMP	INT_Id			;Exit

;*******************************************************
;		Modem status change
;*******************************************************

ComMsc:	NOP

;	Get modem status & control/status registers

Msc0:	ADD	DL,MSR			;DX <- 8250 MSR port address
	IN	AL,DX			;AL <- 8250 Modem status
	SUB	DL,MSR			;Back to base

	MOV	CL,[C_Status+BX]	;CL <- Status register
	MOV	CH,[C_Ctrl+BX]		;CH <- Control register

;	Control CTS status flag based on CTS status and handshake enable

	TEST	CH,00000010b		;CTS handshake enabled ?
	JZ	Msc2			; No, ignore CTS status
	TEST	AL,00010000b		;CTS asserted ?
	JNZ	Msc1			; Yes, enable transmit
	OR	CL,01000000b		;CTS inactive - hard handshake on
	JMP	Msc2			;
Msc1:	AND	CL,10111111b		;CTS active - hard handshake off
Msc2:	MOV	[C_Status+BX],CL	;Save status flags

;	Determine if transmitter should be enabled

Msc3:	ADD	DL,IER			;DX <- 8250 IER port address
	IN	AL,DX			;AL <- Interrupt enable register
	OR	AL,00000010b		;Enable transmitter by default
	TEST	CL,11000100b		;Enable transmitter ?
	JZ	Msc4			; Yes
	AND	AL,11111101b		; No - disable transmitter
Msc4:	OUT	DX,AL			;Update IER
	JMP	INT_Id			;Exit

;*******************************************************
;		Character transmitted
;*******************************************************

; Register usage:
; AH : Status register
; BX : Array pointer
; DX : Port address
; SI : Output tail pointer
; DI : Output head pointer

;	Get status and control registers

ComXmt:	MOV	AH,[C_Status+BX]	;AH <- Status register
	TEST	AH,00000100b		;Buffer empty ?
	JNZ	Xmt2			; Yes, stop transmitter

;	Bump tail pointer

	PUSH	BX			;Save byte array pointer
	SHL	BL,1			;BX <- Word array pointer
	MOV	SI,[C_OutTail+BX]	;SI <- Output tail pointer
	MOV	DI,[C_OutHead+BX]	;DI <- Output head pointer

	INC	SI			;Bump tail pointer
	CMP	SI,[C_OutSize+BX]	;Tail < Buffer size ?
	JB	Xmt1			; Yes, proceed normally
	XOR	SI,SI			; No, reset to 0
Xmt1:	MOV	[C_OutTail+BX],SI	;Save tail pointer

        DEC     [C_buffull+bx]          ;Decrement chars in buffer
;        INC     [C_charsend+bx]

;	Send character

	SHL	BL,1			;BX <- Pointer array pointer
	LES	BX,[C_OutBufPtr+BX]	;ES:BX <- Pointer to output buffer
	MOV	AL,[ES:BX+SI]		;AL <- Character from buffer
	OUT	DX,AL			;Send
	POP	BX			;Recover byte array pointer

;	Determine if buffer is empty
;	Reset output-buffer-full flag & exit

	CMP	DI,SI			;Head = Tail (buffer empty) ?
	JNZ	Xmt2			; No, continue normally
	OR	AH,00000100b		;Set buffer-empty flag
Xmt2:	AND	AH,11010111b		;Reset FULL and OVERFLOW flags
	MOV	[C_Status+BX],AH	;Save status flags
	JMP	Msc0			;Exit (check xmit mask)

;*******************************************************
;		Character received
;*******************************************************

; Register usage:
; AL : Status register
; AH : Control register
; BX : Array index
; CL : Character received
; CH : Temporary storage
; DX : Port address
; SI : Input tail pointer
; DI : Input head pointer

ComRcv:	IN	AL,DX			;
	MOV	CL,AL			;CL <- Received character

;	Check for software handshake

	MOV	AL,[C_Status+BX]	;AL <- Status byte
	MOV	AH,[C_Ctrl+BX]		;AH <- Control byte
	TEST	AH,00000100b		;Software handshake enabled ?
	JZ	Rcv3			; No, don't check software handshake
	CMP	CL,[C_StopChar+BX]	;STOP TRANSMIT character ?
	JZ	Rcv1			; Yes
	CMP	CL,[C_StartChar+BX]	;START TRANSMIT character ?
	JNZ	Rcv3			; No

;	Soft-handshake character received.
;	Activate or deactive transmitter depending on status

	AND	AL,01111111b		;Reset soft-handshake flag (start)
	JMP	Rcv2			;Save status & exit
Rcv1:	OR	AL,10000000b		;Set soft-handshake flag (stop)
Rcv2:	MOV	CL,AL			;Routines in MSC2 require status in CL
	JMP	Msc2			;Exit (xmit interrupt controlled here)

;	Clear buffer empty flag / check for buffer overflow


Rcv3:	AND	AL,11111110b		;Clear buffer-empty flag
	TEST	AL,00000010b		;Buffer full ?
	JZ	Rcv4			; No, continue
	OR	AL,00010000b		;Set overflow flag
	JMP	Rcv10			;Exit

;	Bump receive buffer pointer

Rcv4:	SHL	BL,1			;BX <- Word array index
	MOV	DI,[C_InHead+BX]	;DI <- Input head pointer
	MOV	SI,[C_InTail+BX]	;SI <- Input tail pointer

	INC	DI			;Bump buffer pointer
	CMP	DI,[C_InSize+BX]	;Head > buffer size ?
	JB	Rcv5			; No, continue
	XOR	DI,DI			; Yes, reset pointer

;	Store character in buffer

Rcv5:	PUSH	BX			;Save word array pointer
	MOV	[C_InHead+BX],DI	;Save updated input head pointer
	SHL	BL,1			;BX <- Doubleword array index
	LES	BX,[C_InBufPtr+BX]	;ES:BX <- Pointer to buffer
	MOV	[ES:BX+DI],CL		;Save character in buffer
	POP	BX			;Recover word array pointer

;	Check for full buffer

	CMP	SI,DI			;Tail = Head ?
	JNZ	Rcv6			;If Tail <> Head, buffer is not full
	OR	AL,00000010b		;Set buffer-full flag
	JMP	Rcv8			;Reset RTS and exit

;	Check for near-full buffer (buffer used >= RTSOff)

Rcv6:	CMP	SI,DI			;Tail <= Head ?
	JBE	Rcv7			; Yes, use standard formula
	SUB	SI,DI			;SI <- Tail - Head
	MOV	DI,[C_InSize+BX]	;DI <- Input buffer size
Rcv7:	SUB	DI,SI			;DI <- Head - Tail (amt used)
	CMP	DI,[C_RTSOff+BX]	;Used < Limit ?
	JB	Rcv9			; Yes, leave RTS on

;	Buffer is (near) full, force RTS off & exit

Rcv8:	TEST	AH,00000001b		;RTS handshake enabled ?
	JZ	Rcv9			; No, exit now

	MOV	CH,AL			;Keep status byte
	ADD	DL,MCR			;DX <- Address of modem control reg.
	IN	AL,DX			;AL <- MCR
	AND	AL,11111101b		;Disable RTS
	OUT	DX,AL			;Update MCR
	SUB	DL,MCR			;DX <- Base of port
	MOV	AL,CH			;Recover status byte

;	Exit - receive

Rcv9:	SHR	BL,1			;BX <- Byte array index
Rcv10:	MOV	[C_Status+BX],AL	;Save status byte

;	The following code corrects a "bug" present in some 8250's

	ADD	DL,IER			;DX <- Interrupt Enable Register
	IN	AL,DX			;AL <- IER
	MOV	AH,AL			;Save IER
	AND	AL,11111101b		;Mask off transmit interrupt
	OUT	DX,AL			;Send to IER
	MOV	AL,AH			;
        NOP                             ;Slow down for slow machines
        NOP
	OUT	DX,AL			;Restore original IER state

	JMP	INT_Id			;Check for pending INTs and exit

	ENDP	INT_Handler

;********************************************************
;*							*
;*	Start of Pascal low-level procedures		*
;*							*
;********************************************************
;*							*
;*	Function ComReadCh(ComPort:Byte) : Char		*
;*							*
;********************************************************

	PROC	ComReadCh 	FAR

	CLI				;Interrupts disabled

	MOV	BX,SP			;BX <- Stack pointer
	MOV	AL,[SS:BX+4]		;AL <- Port #

        dec     al                      ;Adjust port number (COM1=0)
        xor     bh,bh                   ;Nuke BH
        mov     bl,al                   ;Get byte index into BL
        shl     bl,1                    ;Convert to word index
        mov     dx,[C_Portaddr+BX]      ;Get port base address
        shr     bl,1                    ;Convert back to byte index

;	Check buffer, return null if empty

	MOV	AH,[C_Status+BX]	;AH <- Status byte
	TEST	AH,00000001b		;Buffer empty ?
	JZ	ComRd2			; No, continue normally

;	Buffer empty, port not open or port # invalid - exit

ComRd1:	MOV	CH,0			;Return null (port error or empty)
ComRdX:	MOV	AL,CH			;AL <- Char from buffer
	STI				;Enable interrupts
	RET	2			;Exit

;	Increment tail pointer
;	NOTE: Entry point for ComReadChW

ComRd2:	SHL	BL,1			;BX <- Word array index
	MOV	SI,[C_InTail+BX]	;SI <- Tail pointer
	MOV	DI,[C_InHead+BX]	;DI <- Input head pointer
	INC	SI			;Bump tail pointer
	CMP	SI,[C_InSize+BX]	;Tail past end of buffer ?
	JB	ComRd3			; No, continue
	XOR	SI,SI			; Yes, reset pointer
ComRd3:	MOV	[C_InTail+BX],SI	;Save updated tail pointer

;	Get character from buffer

	SHL	BL,1			;BX <- Pointer array index
	LES	BX,[C_InBufPtr+BX]	;ES:BX <- Pointer to input buffer
	MOV	CH,[ES:BX+SI]		;CH <- Character from buffer

;	Clear FULL and OVERFLOW flags
;	Check for empty buffer

	XOR	BH,BH			;
	MOV	BL,AL			;Byte array index
	AND	AH,11101101b		;Reset FULL and OVERFLOW status flags
	CMP	DI,SI			;Head = Tail (buffer empty ?)
	JNZ	ComRd4			; No, continue normally
	OR	AH,00000001b		; Yes, set empty flag
ComRd4:	MOV	[C_Status+BX],AH	;Save status byte

        MOV     AL,[c_ctrl+bx]          ;get control byte in AL
        TEST    AL,00000001b            ;is RTS handshake enabled?
        JZ      ComRdX                  ;no, exit


;	Check for RTS assert (Used <= RTSOn)
;	Variable USED (# of used bytes in buffer) calculated as :
;	  IF (Head >= Tail) THEN
;	    Used = Head-Tail
;	  ELSE
;	    Used = BufferSize - (Tail-Head)

	SHL	BL,1			;BX <- Word array index
	CMP	DI,SI			;Head >= Tail ?
	JAE	ComRd5			; Yes, use alternate formula
	SUB	SI,DI			;SI <- Tail - Head
	MOV	DI,[C_InSize+BX]	;DI <- Input buffer size
ComRd5:	SUB	DI,SI			;DI <- Amount of buffer used
	CMP	DI,[C_RTSOn+BX]		;Used > Limit ?
	JA	ComRdX			; Yes, not ready for receive

;	Here to assert RTS

	ADD	DL,MCR			;DX <- 8250 MCR port address
	IN	AL,DX			;AL <- 8250 MCR
	OR	AL,00000010b		;Assert RTS
        NOP                             ;Slow down for slow machines
        NOP
	OUT	DX,AL			;Send to port
	JMP	ComRdX			;Exit

	ENDP	ComReadCh

;********************************************************
;*							*
;*	Function ComReadChW(ComPort:Byte) : Char	*
;*							*
;********************************************************

	PROC	ComReadChW	FAR

	MOV	BX,SP			;BX <- Stack pointer
	MOV	AL,[SS:BX+4]		;AL <- Port #

        dec     al                      ;Adjust port number (COM1=0)
        xor     bh,bh                   ;Nuke BH
        mov     bl,al                   ;Get byte index into BL
        shl     bl,1                    ;Convert to word index
        mov     dx,[C_Portaddr+BX]      ;Get port base address
        shr     bl,1                    ;Convert back to byte index

;	Wait for character

ComRW1:	MOV	AH,[C_Status+BX]	;AL <- Status byte
	TEST	AH,00000001b		;Input buffer empty ?
	JNZ	ComRW1			; Yes, continue waiting

	CLI				;Disable interrupts
	JMP	ComRd2			;Proceed with normal read

	ENDP	ComReadChW

;********************************************************
;*							*
;*    Procedure ComWriteCh(ComPort:Byte; Var Ch:Char)	*
;*							*
;********************************************************

	PROC	ComWriteCh	FAR

;	Check port # for validity

	MOV	BX,SP			;Point BX at parameters
	MOV	AL,[SS:BX+6]		;AL <- Port #
	MOV	CH,[SS:BX+4]		;CH <- Character to send

        dec     al                      ;Adjust port number (COM1=0)
        xor     bh,bh                   ;Nuke BH
        mov     bl,al                   ;Get byte index into BL
        shl     bl,1                    ;Convert to word index
        mov     dx,[C_Portaddr+BX]      ;Get port base address
        shr     bl,1                    ;Convert back to byte index

        mov     ah, [c_status+bx]       ;get status word for PUTCHAR

	CLI				;Disable interrupts
	PutChar	                        ;Place character in buffer (AL nuked)
	STI				;Enable interrupts

ComWr1:	RET	4			;Exit

	ENDP	ComWriteCh

;********************************************************
;*							*
;*   Procedure ComWriteChW(ComPort:Byte; Var Ch:Char)	*
;*							*
;********************************************************

	PROC	ComWriteChW	FAR

;	Check port # for validity

	MOV	BX,SP			;Point BX at parameters
	MOV	AL,[SS:BX+6]		;AL <- Port #
	MOV	CH,[SS:BX+4]		;CH <- Character to send

        dec     al                      ;Adjust port number (COM1=0)
        xor     bh,bh                   ;Nuke BH
        mov     bl,al                   ;Get byte index into BL
        shl     bl,1                    ;Convert to word index
        mov     dx,[C_Portaddr+BX]      ;Get port base address
        shr     bl,1                    ;Convert back to byte index

;	Wait for buffer to open up

ComWW1:	MOV	AH,[C_Status+BX]	;AH <- Status byte
	TEST	AH,00101000b		;Buffer filled ?
	JNZ	ComWW1			; Yes, loop until open

	CLI				;Turn off interrupts
	PutChar			        ;Place character in buffer (AL nuked)
	STI				;Enable interrupts

ComWW2:	RET	4			;Exit

	ENDP	ComWriteChW

;********************************************************

	ENDS	CODE
	END
