page,132
;---------------------------Module-Header-------------------------------
; Module Name: IBMINT.ASM
;
; Created: Fri 06-Feb-1987 10:45:12
; Author:  Walt Moore [waltm]
;
; Copyright (c) Microsoft Corporation 1985-1990.  All Rights Reserved
;
; General Description:
;   This file contains the interrupt time routines for the
;   IBM Windows communications driver.
;
;   The interrupt code is preloaded and fixed.
;
; History:
;
; **********************************************************************
;    Tue Dec 19 1989 09:35:15	-by-  Amit Chatterjee  [amitc]
; ----------------------------------------------------------------------
;    Added a far entry point 'FakeCOMIntFar' so that the routine 'FakeCOMInt'
; could be called from the 'InitAPort' routine in IBMCOM.ASM
;
;   26.Nov.90	richp
;
;   Changed interrupt routines to use new VPICD services for bi-modal/multi-
;   modal interrupt handling.  They now work in straight real mode for real
;   mode Windows, but can also handle interrupts in real mode or protected
;   mode for standard mode Windows, and handle interrupts in RING 0 protected
;   mode for enhanced mode Windows, even when the Windows VM is not currently
;   executing.
;
;   sudeepb 10-Jan-1993 changed the costly cli/sti with non-trapping
;           FCLI/FSTI macros
;-----------------------------------------------------------------------;

subttl  Communications Hardware Interrupt Service Routines

.xlist
include cmacros.inc
include comdev.inc
include ibmcom.inc
include ins8250.inc
include BIMODINT.INC
include vint.inc
.list

externFP GetSystemMsecCount

externW  COMptrs
externW  activeCOMs

externD  lpPostMessage

ifdef   NEC_98
externFP Comm4				;Ins 940923 KBNES
endif   ; NEC_98

sBegin Data

PUBLIC IRQhooks
IRQhooks    label byte
DefineIRQhook MACRO num
IFDEF No_DOSX_Bimodal_Services
IRQhook&num IRQ_Hook_Struc <,,,,IntCodeOFFSET DEF_COM_INT_&num,,,, \
			    IntCodeOFFSET DEF_RM_COM_INT_&num>
ELSE
IRQhook&num IRQ_Hook_Struc <,,,,IntCodeOFFSET DEF_COM_INT_&num>
ENDIF
ENDM
??portnum = 1
REPT MAXCOM+1
	DefineIRQhook %??portnum
??portnum = ??portnum+1
ENDM

PURGE DefineIRQhook

EXTRN VCD_int_callback:fword

sEnd data

createSeg _INTERRUPT,IntCode,word,public,CODE
sBegin IntCode
assumes cs,IntCode

page

IFDEF No_DOSX_Bimodal_Services
public RM_IntDataSeg
RM_IntDataSeg	dw 0
  ; this variable is written into by a routine in inicom
  ; if the 286 DOS extender is present.  This variable
  ; contains the SEGMENT value of the data selector "_DATA"
  ; so that the real mode interrupt handler may use the
  ; data segment, and not it's selector !

PUBLIC	RM_CallBack
RM_CallBack	dd  0
ENDIF


Control proc far
	ret
Control endp


IFDEF No_DOSX_Bimodal_Services
DEF_RM_Handler proc far
	push	es
	push	di
	push	ax
	mov	es, cs:[RM_IntDataSeg]
	mov	di, es:[di.First_DEB]	    ; ES:DI -> ComDEB
	add	di, SIZE ComDEB 	    ; ES:DI -> BIS
	mov	es:[di.BIS_Mode], 4
	push	cs
	call	NEAR PTR COMHandler
	mov	es:[di.BIS_Mode], 0
	pop	ax
	pop	di			    ; ES:DI -> IRQ_Hook_Struc
ifndef  NEC_98
	jc	short DEF_RM_chain
endif   ; NEC_98
	pop	es
	pop	di
	add	sp, 4
	iret

DEF_RM_chain:
        call DOCLI
	push	bp
	mov	bp, sp			    ;stack frame:
					    ;	bp+8	-> OldInt CS
					    ;	bp+6	-> OldInt IP
					    ;	bp+4	-> di
					    ;	bp+2	-> es
					    ;	bp+0	-> bp
	les	di, es:[di.RM_OldIntVec]
	mov	[bp+6], di
	mov	[bp+8], es
	pop	bp
	pop	es
	pop	di
	ret				    ; far ret to OldInt handler
DEF_RM_Handler endp
ENDIF	;No_DOSX_Bimodal_Services


Define_DEF_COM_INT MACRO num
IFDEF No_DOSX_Bimodal_Services
PUBLIC DEF_RM_COM_INT_&num
DEF_RM_COM_INT_&num proc far
	sub	sp, 4
	push	di
	mov	di, DataOFFSET IRQhook&num
        jmp     DEF_RM_Handler
DEF_RM_COM_INT_&num endp
ENDIF
PUBLIC DEF_COM_INT_&num
DEF_COM_INT_&num proc far
	sub	sp, 4
	push	di
	mov	di, DataOFFSET IRQhook&num
        jmp     DEF_Handler
DEF_COM_INT_&num endp
ENDM

??portnum = 2
REPT MAXCOM
	Define_DEF_COM_INT %??portnum
??portnum = ??portnum+1
ENDM

PURGE Define_DEF_COM_INT

IFDEF No_DOSX_Bimodal_Services
PUBLIC DEF_RM_COM_INT_1
DEF_RM_COM_INT_1 proc far
	sub	sp, 4
	push	di
	mov	di, DataOFFSET IRQhook1
        jmp     DEF_RM_Handler
DEF_RM_COM_INT_1 endp
ENDIF

PUBLIC DEF_COM_INT_1
DEF_COM_INT_1 proc far
	sub	sp, 4
	push	di
	mov	di, DataOFFSET IRQhook1
IF2
.errnz $ - OFFSET DEF_Handler
ENDIF
DEF_COM_INT_1 endp

DEF_Handler proc far
	push	es
	push	di
	push	ax
	mov	ax, _DATA
	mov	es, ax
	mov	di, es:[di.First_DEB]	    ; ES:DI -> ComDEB
	add	di, SIZE ComDEB 	    ; ES:DI -> BIS
	push	cs
	call	NEAR PTR COMHandler
	pop	ax
	pop	di			    ; ES:DI -> IRQ_Hook_Struc
ifndef  NEC_98
	jc	short DEF_chain
endif   ; NEC_98
	pop	es
	pop	di
	add	sp, 4
	iret

DEF_chain:
        call DOCLI
	push	bp
	mov	bp, sp			    ;stack frame:
					    ;	bp+8	-> OldInt CS
					    ;	bp+6	-> OldInt IP
					    ;	bp+4	-> di
					    ;	bp+2	-> es
					    ;	bp+0	-> bp
	les	di, es:[di.OldIntVec]
	mov	[bp+6], di
	mov	[bp+8], es
	pop	bp
	pop	es
	pop	di
	ret				    ; far ret to OldInt handler
DEF_Handler endp

;------------------------------------------------------------------------------
;
;   ENTER:	ES:DI -> BIS
;
;   EXIT:	Carry set, if IRQ not handled by any com ports
;
COMHandler proc far
	push	ds
	push	si
	push	ax
	push	bx
	mov	si, es
	mov	ds, si
	mov	bh, -1
ch_chk_all:
	lea	si, [di-SIZE ComDEB]	;ds:si -> ComDEB
	mov	si, [si.IRQhook]
	mov	si, [si.First_DEB]
	mov	bl, -1
ch_next_com:
	inc	bl			; first time bl = 0
	xor	ax, ax
	xchg	ax, [di.BIS_Mode]
	lea	di, [si+SIZE ComDEB]
	mov	[di.BIS_Mode], ax
	call	CommInt
	and	al, 80h
	or	bl, al

	mov	si, [si.NextDEB]
	or	si, si
	jnz	ch_next_com

	test	bl, 7Fh 		;Q: more than 1 com port?
	jnz	short ch_shared 	;   Y: check if handled
	or	bl, bl			;Q: int handled by port?
	stc
	jns	ch_exit 		;   N:

ch_eoi:
	xor	ax, ax
.errnz BIH_API_EOI
	xor	bx, bx
	xchg	bx, es:[di.BIS_Mode]
	call	es:[bx][di.BIS_User_Mode_API]
	lea	si, [di-SIZE ComDEB]	; ds:si -> ComDEB
	mov	si, [si.IRQhook]
	mov	al, [si.OldMask]
	shr	al, 1			; shift bit 0 into Carry (0, if unmasked
	cmc				;   -1, if originally masked)

ch_exit:
	pop	bx
	pop	ax
	pop	si
	pop	ds
	ret

ch_shared:
	inc	bh			; count loop
	or	bl, bl			;Q: int handled by any port?
	js	ch_chk_all		;   Y: check all ports again
	or	bh, bh			;Q: first time thru loop?
	stc
	jz	ch_exit 		;   Y: int wasn't for a COM port, so
					;      chain to next IRQ handler
	jmp	ch_eoi

COMHandler endp


IFDEF No_DOSX_Bimodal_Services

PUBLIC Entry_From_RM
Entry_From_RM proc far

;
; Simulate the far ret
;
	cld
	lodsw
	mov	es:[di.RealMode_IP], ax
	lodsw
	mov	es:[di.RealMode_CS], ax
	add	es:[di.RealMode_SP], 4

	push	es
	push	di
.286
;
; Push far addr of Ret_To_IRET to cleanup stack and return to DPMI host
;
	push	cs
	push	IntCodeOFFSET Ret_To_IRET
;
; Push far addr of proc to call, so we can do a far ret to it
;
	push	es:[di.RealMode_CX]	; segment of callback
	push	es:[di.RealMode_DX]	; offset of callback
	mov	di, es:[di.RealMode_DI]
	ret				; far ret to cx:dx
					;   called proc will do a far ret
Ret_To_IRET:				; <- to here
	pop	di
	pop	es
	iret
.8086

Entry_From_RM endp

PUBLIC RM_APIHandler
RM_APIHandler proc far
	cmp	ax, BIH_API_Call_Back
	jne	APIHandler
	call	cs:[RM_CallBack]
	ret
RM_APIHandler endp

ENDIF

;------------------------------------------------------------------------------
;
;   ENTER:	ES:DI -> BIS
;
APIHandler proc far

	or	ax, ax
	jnz	short api_not_EOI
.errnz	BIH_API_EOI
	mov	ax, es:[di.BIS_IRQ_Number]
	cmp	al,8			;Q: slave IRQ?
	mov	al,EOI
	jb	short api_master	;   N:
ifdef   NEC_98
	out	08h,al			;   Y: EOI slave
else    ; NEC_98
	out	0A0h,al 		;   Y: EOI slave
endif   ; NEC_98
api_master:
ifdef   NEC_98
	out	00h,al			; EOI master
else    ; NEC_98
	out	INTA0,al		; EOI master
endif   ; NEC_98
	ret

api_not_EOI:
	cmp	ax, BIH_API_Call_Back
	jae	short api_callme
	push	dx
	push	cx
ifdef   NEC_98
	mov	dx, 02h
else    ; NEC_98
	mov	dx, INTA1
endif   ; NEC_98
	mov	cx,  es:[di.BIS_IRQ_Number]
	cmp	cl, 8			;Q: 2nd PIC?
	jb	@f			;   N:
ifdef   NEC_98
	mov	dx, 0Ah			;   Y: dx = mask port
else    ; NEC_98
	mov	dx, 0A1h		;   Y: dx = mask port
endif   ; NEC_98
	sub	cl, 8
@@:
	cmp	al, BIH_API_Get_Mask	;Q: get IRQ mask?
	jae	api_get_mask		;   Y:
	mov	ah, al
	mov	ch, 1
	shl	ch, cl			; ch = mask byte
	pushf
        call DOCLI
	in	al, dx			; get current PIC mask state
	cmp	ah, BIH_API_Mask	;Q: mask IRQ?
	jne	@f			;   N:
	or	al, ch			;   Y: set IRQ's bit
	jmp	short api_mask_exit
@@:
	not	ch			;   N: clear IRQ's bit to unmask
	and	al, ch
api_mask_exit:
	out	dx, al
	pop	ax
	test	ah, 2			;Q: ints were enabled?
	jz	@f			;   N:
        call DOSTI
@@:
	pop	cx
	pop	dx
	ret

api_get_mask:
	in	al, dx			; get current PIC mask state
ifdef   NEC_98
	iodelay		;1994.08.01 KBNES 
endif   ; NEC_98
	inc	cl
	shr	al, cl			; move IRQ's bit into carry
					; Carry set, if IRQ masked
	pop	cx
	pop	dx
	ret

api_callme:
	push	cx
	push	dx
	ret				; far ret to call back, which will
					; do a far ret to our caller
APIHandler endp


;--------------------------Fake a Hardware Interrupt----------------------;
; FakeCOMInt
;
; This routine fakes a hardware interrupt to IRQ3 or IRQ4
; to clear out characters pending in the buffer
;
; Entry:
;   DS:SI --> DEB
;   INTERRUPTS DISABLED!
; Returns:
;   None
; Error Returns:
;   None
; Registers Preserved:
;
; Registers Destroyed:
;   AX,DX,FLAGS
; History: glenn steffler 5/17/89
;-----------------------------------------------------------------------;

FakeCOMInt proc near

      ; call DOCLI                             ;Done by caller
;
; WARNING: jumping into the middle of CommInt, so the stack must be set
;	   properly.
;
	push	dx
	push	bx
	push	cx
	push	di
	push	es
	push	EvtWord[si]
	mov	dx,Port[si]		;Get device I/O address
	add	dl, ACE_IIDR
	push	dx
	jmp	FakeXmitEmpty		;Process the fake interrupt, DS:SI is
					;  already pointing to proper DEB
;
; FakeXmitEmpty falls in XmitEmpty which jumps back into CommInt.  When CommInt
; determines that no interrupt is pending, then it will near return back to
; FakeCOMIntFar which can far ret back to its caller.
;
FakeCOMInt endp

public	FakeCOMIntFar
FakeCOMIntFar proc far

	call	FakeCOMInt
	ret

FakeCOMIntFar endp

;--------------------------Interrupt Handler----------------------------
;
; CommInt - Interrupt handler for com ports
;
; Interrupt handlers for PC com ports.	This is the communications
; interrupt service routine for RS232 communications.  When an RS232
; event occurs the interrupt vectors here.  This routine determines
; who the caller was and services the appropriate interrupt.  The
; interrupts are prioritized in the following order:
;
;     1.  line status interrupt
;     2.  read data available interrupt
;     3.  transmit buffer empty interrupt
;     4.  modem service interrupt
;
; This routine continues to service until all interrupts have been
; satisfied.
;
; Entry:
;   DS:SI --> DEB
;   INTERRUPTS DISABLED!
; Returns:
;   AL = 0, if not handled, -1, if handled
;
;-----------------------------------------------------------------------

assumes ds,Data
assumes es,nothing

;   Dispatch table for interrupt types

SrvTab label word
	dw	OFFSET ModemStatus	;[0] Modem Status Interrupt
	dw	OFFSET XmitEmpty	;[2] Tx Holding Reg. Interrupt
	dw	OFFSET DataAvail	;[4] Rx Data Available Interrupt
					;   or [C] if 16550 & 16550A
	dw	OFFSET LineStat 	;[6] Reciever Line Status Interrupt


	public	CommInt

CommInt proc near

	xor	al, al
	cmp	word ptr [VCD_int_callback+4], 0
	je	short @F			; jump if no callback (not 3.1 VCD)
	test	[si.VCDflags], fCOM_ignore_ints ;Q: we still own port?
	jnz	IntLoop40			;   N: ignore the int
.386
	push	esi
	mov	esi, [si.VCD_data]
	call	[VCD_int_callback]
	pop	esi
.8086
@@:

	push	dx
	mov	dx,Port[si]		;Get comm I/O port
	add	dl,ACE_IIDR		;--> Interrupt ID Register
	in	al, dx
ifdef   NEC_98
	iodelay		;1994.08.01 KBNES 
endif   ; NEC_98
	test	al, 1			;Q: interrupt pending?
	jnz	short IntLoop30 	;   N:

	push	bx
	push	cx
	push	di
	push	es
	mov	cx, EvtWord[si]
	push	cx
	jmp	short IntLoop10

InterruptLoop_ChkTx:
	cmp	QOutCount[si],0 	;Output queue empty?
	je	short InterruptLoop	;   Y: don't chk tx
	pop	dx
	push	dx
	dec	dx			; to IER
.errnz ACE_IIDR - ACE_IER - 1
	in	al, dx
	and	al,NOT ACE_ETBEI	; disable it
	iodelay
	out	dx, al
	or	al, ACE_ETBEI		; enable it again
	iodelay
	out	dx, al
	iodelay
	out	dx, al
ifdef   NEC_98
	iodelay		;1994.08.01 KBNES 
endif   ; NEC_98

InterruptLoop:
	pop	dx			;Get ID reg I/O address

	in	al,dx			;Get Interrupt Id
ifdef   NEC_98
	iodelay		;1994.08.01 KBNES 
endif   ; NEC_98
	test	al,1			;Interrupt need servicing?
	jnz	IntLoop20		;No, all done

IntLoop10:
	and	ax, 07h
	mov	di,ax
	push	dx			;Save Id register
	jmp	SrvTab[di]		;Service the Interrupt

IntLoop20:
	mov	ax,EvtMask[si]		;Mask the event word to only the
	and	ax, EvtWord[si] 	;  user specified bits
	mov	EvtWord[si], ax
	pop	bx
	test	[si.NotifyFlagsHI], CN_Notify
	jz	short ci_exit
	not	bx
	and	ax, bx			; bits set in ax are new events
	jnz	short ci_new_events

ci_exit:
	pop	es
	assumes es,nothing

	pop	di
	pop	cx
	pop	bx
	xor	al, al

IntLoop30:
	pop	dx
	and	al, 1
	dec	al			; 0->-1, 1->0
IntLoop40:
	ret

ci_new_events:
	mov	ax, CN_EVENT
	call	notify_owner
	jmp	ci_exit

CommInt endp

page

;----------------------------Private-Routine----------------------------;
;
; LineStat - Line Status Interrupt Handler
;
; Break detection is handled and set in the event word if
; enabled.  Other errors (overrun, parity, framing) are
; saved for the data available interrupt.
;
; This routine used to fall into DataAvail for the bulk of its processing.
; This is no longer the case...  A very popular internal modem seems to
; operate differently than a real 8250 when parity errors occur.  Falling
; into the DataAvail handler on a parity error caused the same character
; to be received twice.  Having this routine save the LSR status, and
; return to InterruptLoop fixes the problem, and still works on real COMM
; ports.  The extra overhead isn't a big deal since this routine is only
; entered when there is an exception like a parity error.
;
; This routine is jumped to, and will perform a jump back into
; the dispatch loop.
;
; Entry:
;   DS:SI --> DEB
;   DX     =  Port.IIDR
; Returns:
;   None
; Error Returns:
;   None
; Registers Destroyed:
;   AX,FLAGS
; History:
;-----------------------------------------------------------------------;


; assumes ds,Data
assumes es,nothing

public LineStat 			;Public for debugging
LineStat proc near

	or	by EvtWord[si],EV_Err	;Show line status error

	add	dl,ACE_LSR-ACE_IIDR	;--> Line Status Register
	in	al,dx
ifdef   NEC_98
	iodelay		;1994.08.01 KBNES 
endif   ; NEC_98
	test	al,ACE_PE+ACE_FE+ACE_OR ;Parity, Framing, Overrun error?
	jz	@f

	mov	LSRShadow[si],al	;yes, save status for DataAvail
@@:
	test	al,ACE_BI		;Break detect?
	jz	InterruptLoop_ChkTx	;Not break detect interrupt

	or	by EvtWord[si],EV_Break ;Show break

	jmp	short InterruptLoop_ChkTx

LineStat   endp

page

;----------------------------Private-Routine----------------------------;
;
; DataAvail - Data Available Interrupt Handler
;
; The available character is read and stored in the input queue.
; If the queue has reached the point that a handshake is needed,
; one is issued (if enabled).  EOF detection, Line Status errors,
; and lots of other stuff is checked.
;
; This routine is jumped to, and will perform a jump back into
; the dispatch loop.
;
; Entry:
;   DS:SI --> DEB
;   DX     =  Port.IIDR
; Returns:
;   None
; Error Returns:
;   None
; Registers Destroyed:
;   AX,BX,CX,DI,ES,FLAGS
; History:
;-----------------------------------------------------------------------;

; assumes ds,Data
assumes es,nothing

public DataAvail                       ;public for debugging
DataAvail   proc   near

	sub	dl,ACE_IIDR-ACE_RBR	;--> receiver buffer register
	in	al,dx			;Read received character
ifdef   NEC_98
	iodelay		;1994.08.01 KBNES 
endif   ; NEC_98

	and	[si.NotifyFlagsHI], NOT CN_Idle ; flag as not idle

	mov	ah,LSRShadow[si]	;what did the last Line Status intrpt
	mov	bh,ah			;  have to say?
	or	ah,ah
	jz	@f

	and	ah,ErrorMask[si]	;there was an error, record it
	or	by ComErr[si],ah
IFNDEF KKBUGFIX  ; 1/05/93:TakuA:Fix #1666
	mov	LSRShadow[si],0
ENDIF
	.errnz	ACE_OR-CE_OVERRUN	;Must be the same bits
	.errnz	ACE_PE-CE_RXPARITY
	.errnz	ACE_FE-CE_FRAME
	.errnz	ACE_BI-CE_BREAK
@@:

; Regardless of the character received, flag the event in case
; the user wants to see it.

	or	by EvtWord[si],EV_RxChar ;Show a character received
	.errnz HIGH EV_RxChar

; Check the input queue, and see if there is room for another
; character.  If not, or if the end of file character has already
; been received, then go declare overflow.

DataAvail00:

IFDEF KKBUGFIX  ; 1/05/93:TakuA:Fix #1666
        mov     bh,LSRShadow[si]
        mov     LSRShadow[si],0
ENDIF
	mov	cx,QInCount[si] 	;Get queue count (used later too)
	cmp	cx,QInSize[si]		;Is queue full?
	jge	DataAvail20		;  Yes, comm overrun
	test	EFlags[si],fEOF 	;Has end of file been received?
	jnz	DataAvail20		;  Yes - treat as overflow

; Test to see if there was a parity error, and replace
; the character with the parity character if so

	test	bh,ACE_PE		;Parity error
	jz	DataAvail25		;  No
	test	[si.DCB_Flags2],fPErrChar   ;Parity error replacement character?
	jz	DataAvail25		;  No
	mov	al,[si.DCB_PEChar]	;  Yes, get parity replacement char

; Skip all other processing except event checking and the queing
; of the parity error replacement character

	jmp	short DataAvail80	;Skip all but event check, queing

DataAvail20:
	or	by ComErr[si],CE_RXOVER ;Show queue overrun
	jmp	short DataAvail50

; See if we need to strip null characters, and skip
; queueing if this is one.  Also remove any parity bits.

DataAvail25:
	and	al,RxMask[si]		;Remove any parity bits
	jnz	DataAvail30		;Not a Null character
	test	[si.DCB_Flags2],fNullStrip  ;Are we stripping received nulls?
	jnz	DataAvail50		;  Yes, put char in the bit bucket

; Check to see if we need to check for EOF characters, and if so
; see if this character is it.

DataAvail30:
	test	[si.DCB_Flags],fBinary	;Is this binary stuff?
	jnz	DataAvail60		;  Yes, skip EOF check
	cmp	al,[si.DCB_EOFChar]	;Is this the EOF character?
	jnz	DataAvail60		;  No, see about queing the charcter
	or	EFlags[si],fEOF 	;Set end of file flag
DataAvail50:
	jmp	DataAvail140		;Skip the queing process

; If output XOn/XOff is enabled, see if the character just received
; is either an XOn or XOff character.  If it is, then set or
; clear the XOffReceived flag as appropriate.

DataAvail60:
	test	[si.DCB_Flags2],fOutX	;Output handshaking?
	jz	DataAvail80		;  No
	cmp	al,[si.DCB_XoffChar]	;Is this an X-Off character?
	jnz	DataAvail70		;  No, see about XOn or Ack
	or	HSFlag[si],XOffReceived ;Show XOff received, ENQ or ETX [rkh]
	test	[si.DCB_Flags],fEnqAck+fEtxAck ;Enq or Etx Ack?
	jz	DataAvail50		;  No
	cmp	cx,[si.DCB_XonLim]	;See if at XOn limit
	ja	DataAvail50		;  No
	and	HSFlag[si],NOT XOffReceived ;Show ENQ or ETX not received
	and	HSFlag[si], NOT XOnPending+XOffSent
	mov	al, [si.DCB_XonChar]
	call	OutHandshakingChar
	jmp	DataAvail50		;Done

DataAvail70:
	cmp	al,[si.DCB_XonChar]	;Is this an XOn character?
	jnz	DataAvail80		;  No, just a normal character
	and	HSFlag[si],NOT XOffReceived
	test	[si.DCB_Flags],fEnqAck+fEtxAck ;Enq or Etx Ack?
	jz	DataAvail75		;  No - jump to FakeXmitEmpty to get
					;	transmitting going again
	and	HSFlag[si],NOT EnqSent

DataAvail75:
	jmp	FakeXmitEmpty		;Restart transmit

; Now see if this is a character for which we need to set an event as
; having occured. If it is, then set the appropriate event flag


DataAvail80:
	cmp	al,[si.DCB_EVTChar]	;Is it the event generating character?
	jne	DataAvail90		;  No
	or	by EvtWord[si],EV_RxFlag   ;Show received specific character

; Finally, a valid character that we want to keep, and we have
; room in the queue. Place the character in the queue.
; If the discard flag is set, then discard the character

DataAvail90:
	test	MiscFlags[si], Discard	;Discarding characters ?
	jnz	DataAvail50		;  Yes

	lea	bx, [si+SIZE ComDEB]	; DS:BX -> BIS
	mov	bx, [bx.BIS_Mode]	; mode will be either 0 or 4
	les	di,QInAddr[si][bx]	;Get queue base pointer from either
	assumes es,nothing		;   QInAddr or AltQInAddr

	mov	bx,QInPut[si]		;Get index into queue
	mov	es:[bx][di],al		;Store the character
	inc	bx			;Update queue index
	cmp	bx,QInSize[si]		;See if time for wrap-around
	jc	DataAvail100		;Not time to wrap
	xor	bx,bx			;Wrap-around is a new zero pointer

DataAvail100:
	mov	QInPut[si],bx		;Store updated pointer
	inc	cx			;And update queue population
	mov	QInCount[si],cx

; If flow control has been enabled, see if we are within the
; limit that requires us to halt the host's transmissions

	cmp	cx,XOffPoint[si]	;Time to see about XOff?
	jc	DataAvail120		;  Not yet
	test	HSFlag[si],HSSent	;Handshake already sent?
	jnz	DataAvail120		;  Yes, don't send it again

	mov	ah,HHSLines[si] 	;Should hardware lines be dropped?
	or	ah,ah			;  (i.e. do we have HW HS enabled?)
	jz	DataAvail110		;  No
	add	dl,ACE_MCR		;  Yes
	in	al,dx			;Clear the necessary bits
ifdef   NEC_98
	iodelay		;1994.08.01 KBNES 
endif   ; NEC_98
	not	ah
	and	al,ah
	or	HSFlag[si],HHSDropped	;Show lines have been dropped
	out	dx,al			;  and drop the lines
ifdef   NEC_98
	iodelay		;1994.08.01 KBNES 
endif   ; NEC_98
	sub	dl,ACE_MCR

DataAvail110:
	test	[si.DCB_Flags2],fInX	;Input Xon/XOff handshaking
	jz	DataAvail120		;  No
	or	HSFlag[si], XOffSent
	mov	al, [si.DCB_XoffChar]
	call	OutHandshakingChar

DataAvail120:
	cmp	cx, [si.RecvTrigger]	;Q: time to call owner's callback?
	jb	short DataAvail130	;   N:

	test	[si.NotifyFlagsHI], CN_RECEIVE
	jnz	short DataAvail140	; jump if notify already sent and
					;   data in buffer hasn't dropped
					;   below threshold
	mov	ax, IntCodeOFFSET DataAvail140
	push	ax
	mov	ax, CN_RECEIVE
%OUT probably should just set a flag and notify after EOI
	jmp	notify_owner

DataAvail130:
	and	[si.NotifyFlagsHI], NOT CN_RECEIVE

DataAvail140:
	pop	dx
	push	dx
	add	dl, ACE_LSR-ACE_IIDR
	in	al, dx
ifdef   NEC_98
	iodelay		;1994.08.01 KBNES 
endif   ; NEC_98
	test	al, ACE_DR		;Q: more data available?
	jz	@F			;   N:
	sub	dl, ACE_LSR		;   Y: go read it
	in	al, dx			;Read available character
ifdef   NEC_98
	iodelay		;1994.08.01 KBNES 
endif   ; NEC_98
	jmp	DataAvail00
@@:
	jmp	InterruptLoop_ChkTx

DataAvail endp


OutHandshakingChar proc near

	add	dl, ACE_LSR
	mov	ah, al
@@:
	in	al, dx
ifdef   NEC_98
	iodelay		;1994.08.01 KBNES 
endif   ; NEC_98
	test	al, ACE_THRE
	jz	@B
	sub	dl, ACE_LSR
	mov	al, ah
	out	dx, al
ifdef   NEC_98
	iodelay		;1994.08.01 KBNES 
endif   ; NEC_98
	ret

OutHandshakingChar endp


page

;----------------------------Private-Routine----------------------------;
;
; XmitEmpty - Transmitter Register Empty
;
; Entry:
;   DS:SI --> DEB
;   DX     =  Port.IIDR
; Returns:
;   None
; Error Returns:
;   None
; Registers Destroyed:
;   AX,BX,CX,DI,ES,FLAGS
; History:
;-----------------------------------------------------------------------;

; assumes ds,Data
assumes es,nothing

public FakeXmitEmpty
FakeXmitEmpty:
	pop	dx
	push	dx

; "Kick" the transmitter interrupt routine into operation.

	dec	dl
.errnz ACE_IIDR - ACE_IER-1
	in	al,dx			;Get current IER state
ifdef   NEC_98
	iodelay		;1994.08.01 KBNES 
endif   ; NEC_98
	test	al,ACE_ETBEI		;Interrupt already enabled?
	jnz	@F			;  Yes, don't reenable it
	or	al,ACE_ETBEI		;  No, enable it
	out	dx,al
	iodelay 			;8250, 8250-B bug requires
	out	dx,al			;  writting register twice
@@:
	add	dl,ACE_LSR-ACE_IER	;--> Line Status Register
	iodelay
	in	al,dx			;Is xmit really empty?
ifdef   NEC_98
	iodelay		;1994.08.01 KBNES 
endif   ; NEC_98
	sub	dl,ACE_LSR-ACE_THR	;--> Transmitter Holding Register
	test	al,ACE_THRE
	jnz	short XmitEmpty5	;   Y: send next char
	jmp	InterruptLoop		;   N: return to processing loop

public XmitEmpty
XmitEmpty proc near

	add	dl,ACE_LSR-ACE_IIDR	;--> Line Status Register
	in	al,dx			;Is xmit really empty?
ifdef   NEC_98
	iodelay		;1994.08.01 KBNES 
endif   ; NEC_98
	sub	dl,ACE_LSR-ACE_THR	;--> Transmitter Holding Register
	test	al,ACE_THRE
	jz	Xmit_jumpto90		;Transmitter not empty, cannot send

; If the hardware handshake lines are down, then XOff/XOn cannot
; be sent.  If they are up and XOff/XOn has been received, still
; allow us to transmit an XOff/XOn character.  It will make
; a dead lock situation less possible (even though there are
; some which could happen that cannot be handled).

XmitEmpty5:
	mov	ah,HSFlag[si]		;Get handshaking flag
	test	ah,HHSDown+BreakSet	;Hardware lines down or break set?
	jnz	Xmit_jumpto100		;  Yes, cannot transmit

; Give priority to any handshake character waiting to be
; sent.  If there are none, then check to see if there is
; an "immediate" character to be sent.  If not, try the queue.

XmitEmpty10:
	test	[si.DCB_Flags],fEnqAck+fEtxAck ;Enq or Etx Ack?
	jnz	XmitEmpty40		;  Yes

XmitEmpty15:
	test	ah,HSPending		;XOff or XOn pending
	jz	XmitEmpty40		;  No

XmitEmpty20:
	and	ah,NOT XOnPending+XOffSent
	mov	al,[si.DCB_XonChar]	;Get XOn character

XmitEmpty30:
	mov	HSFlag[si],ah		;Save updated handshake flag
	jmp	XmitEmpty110		;Go output the character

Xmit_jumpto90:
	jmp	XmitEmpty90

; If any of the lines which were specified for a timeout are low, then
; don't send any characters.  Note that by putting the check here,
; XOff and Xon can still be sent even though the lines might be low.

; Also test to see if a software handshake was received.  If so,
; then transmission cannot continue.  By delaying the software check
; to here, XOn/XOff can still be issued even though the host told
; us to stop transmission.

XmitEmpty40:
	test	ah,CannotXmit		;Anything preventing transmission?
	jz	XmitEmpty45		;  No
Xmit_jumpto100:
	jmp	XmitEmpty100		;  Yes, disarm and exit

; If a character has been placed in the single character "transmit
; immediately" buffer, clear that flag and pick up that character
; without affecting the transmitt queue.

XmitEmpty45:
	test	EFlags[si],fTxImmed	;Character to xmit immediately?
	jz	XmitEmpty515		;  No, try the queue
	and	EFlags[si],NOT fTxImmed ;Clear xmit immediate flag
	mov	al,ImmedChar[si]	;Get char to xmit
	jmp	XmitEmpty110		;Transmit the character

XmitEmpty515:
	mov	cx,QOutCount[si]	;Output queue empty?
	jcxz	Xmit_jumpto90		;  Yes, go set an event

	test	[si.DCB_Flags],fEtxAck	;Etx Ack?
	jz	XmitEmpty55		;  No
	mov	cx,QOutMod[si]		;Get number bytes sent since last ETX
	cmp	cx,[si.DCB_XonLim]	;At Etx limit yet?
	jne	XmitEmpty51		;  No, inc counter
	mov	QOutMod[si],0		;  Yes, zero counter
	or	HSFlag[si],EtxSent	;Show ETX sent
	jmp	short XE_sendXOFF

XmitEmpty51:
	inc	cx			; Update counter
	mov	QOutMod[si],cx		; Save counter
	jmp	short XmitEmpty59	; Send queue character

XmitEmpty55:
	test	[si.DCB_Flags],fEnqAck	;Enq Ack?
	jz	XmitEmpty59		;  No, send queue character
	mov	cx,QOutMod[si]		;Get number bytes sent since last ENQ
	or	cx,cx			;At the front again?
	jnz	XmitEmpty56		;  No, inc counter
	mov	QOutMod[si],1		;  Yes, send ENQ
	or	HSFlag[si],EnqSent	;Show ENQ sent
XE_sendXOFF:
	mov	al,[si.DCB_XoffChar]
	jmp	short XmitEmpty110	;Go output the character

XmitEmpty56:
	inc	cx			;Update counter
	cmp	cx,[si.DCB_XonLim]	;At end of our out buffer len?
	jne	XmitEmpty58		;  No
	xor	cx,cx			;Show at front again.

XmitEmpty58:
	mov	QOutMod[si],cx		;Save counter

XmitEmpty59:
	lea	bx, [si+SIZE ComDEB]	; DS:BX -> BIS
	mov	bx, [bx.BIS_Mode]	; mode will be either 0 or 4
	les	di,QOutAddr[si][bx]	;Get queue base pointer from either
	assumes es,nothing		;   QOutAddr or AltQOutAddr

	mov	bx,QOutGet[si]		;Get pointer into queue
	mov	al,es:[bx][di]		;Get the character

	inc	bx			;Update queue pointer
	cmp	bx,QOutSize[si] 	;See if time for wrap-around
	jc	XmitEmpty60		;Not time for wrap
	xor	bx,bx			;Wrap by zeroing the index

XmitEmpty60:
	mov	QOutGet[si],bx		;Save queue index
	mov	cx,QOutCount[si]	;Output queue empty?
	dec	cx			;Dec # of bytes in queue
	mov	QOutCount[si],cx	;  and save new population

	out	dx,al			;Send char
ifdef   NEC_98
	iodelay		;1994.08.01 KBNES 
endif   ; NEC_98

	cmp	cx, [si.SendTrigger]	;Q: time to call owner's callback?
	jae	short XmitEmpty70	;   N:

	test	[si.NotifyFlagsHI], CN_TRANSMIT
	jnz	short XmitEmpty80	; jump if notify already sent and
					;   data in buffer hasn't raised
					;   above threshold
	mov	ax, IntCodeOFFSET XmitEmpty80
	push	ax
	mov	ax, CN_TRANSMIT
	jmp	short notify_owner

XmitEmpty70:
	and	[si.NotifyFlagsHI], NOT CN_TRANSMIT

XmitEmpty80:
%OUT check fNoFIFO in EFlags[si] to determine if we can queue more output
	jmp	InterruptLoop


; No more characters to transmit.  Flag this as an event.

XmitEmpty90:
	or	by EvtWord[si],EV_TxEmpty

; Cannot continue transmitting (for any of a number of reasons).
; Disable the transmit interrupt.  When it's time resume, the
; transmit interrupt will be reenabled, which will generate an
; interrupt.

XmitEmpty100:
	inc	dx			;--> Interrupt Enable Register
	.errnz	ACE_IER-ACE_THR-1
	in	al,dx			;I don't know why it has to be read
ifdef   NEC_98
	iodelay		;1994.08.01 KBNES 
endif   ; NEC_98
	and	al,NOT ACE_ETBEI	;  first, but it works this way
XmitEmpty110:
	out	dx,al
	jmp	InterruptLoop

XmitEmpty endp

page

;----------------------------Private-Routine----------------------------;
;
; ModemStatus - Modem Status Interrupt Handler
;
; Entry:
;   DS:SI --> DEB
;   DX     =  Port.IIDR
; Returns:
;   None
; Error Returns:
;   None
; Registers Destroyed:
;   AX,BX,CX,DI,ES,FLAGS
; History:
;-----------------------------------------------------------------------;


; assumes ds,Data
assumes es,nothing

public ModemStatus                     ;Public for debugging
ModemStatus proc near

; Get the modem status value and shadow it for MSRWait.

	add	dl,ACE_MSR-ACE_IIDR	;--> Modem Status Register
	in	al,dx
ifdef   NEC_98
	iodelay		;1994.08.01 KBNES 
endif   ; NEC_98
	mov	MSRShadow[si],al	;Save MSR data for others
	mov	ch,al			;Save a local copy

; Create the event mask for the delta signals

	mov	ah,al			;Just a lot of shifting
	shr	ax,1
	shr	ax,1
	shr	ah,1
	mov	cl,3
	shr	ax,cl
	and	ax,EV_CTS+EV_DSR+EV_RLSD+EV_Ring
	or	EvtWord[si],ax

	mov	ah,ch				       ;[rkh]...
	shr	ah,1
	shr	ah,1
	and	ax,EV_CTSS+EV_DSRS
	or	EvtWord[si],ax

	mov	ah,ch
	mov	cl,3
	shr	ah,cl
	and	ax,EV_RLSD
	or	EvtWord[si],ax

	mov	ah,ch
	mov	cl,3
	shl	ah,cl
	and	ax,EV_RingTe
	or	EvtWord[si],ax

	.errnz	   EV_CTS-0000000000001000b
	.errnz	   EV_DSR-0000000000010000b
	.errnz	  EV_RLSD-0000000000100000b
	.errnz	  EV_Ring-0000000100000000b

	.errnz	    EV_CTSS-0000010000000000b	    ;[rkh]
	.errnz	    EV_DSRS-0000100000000000b
	.errnz	   EV_RLSDS-0001000000000000b
	.errnz	  EV_RingTe-0010000000000000b

	.errnz	 ACE_DCTS-00000001b
	.errnz	 ACE_DDSR-00000010b
	.errnz	ACE_DRLSD-00001000b
	.errnz	   ACE_RI-01000000b

	.errnz	 ACE_TERI-00000100b		    ;[rkh]
	.errnz	  ACE_CTS-00010000b
	.errnz	  ACE_DSR-00100000b
	.errnz	 ACE_RLSD-10000000b

ModemStatus10:
	mov	al,OutHHSLines[si]	;Get output hardware handshake lines
	or	al,al			;Any lines that must be set?
	jz	ModemStatus40		;No hardware handshake on output
	and	ch,al			;Mask bits of interest
	cmp	ch,al			;Lines set for Xmit?
	je	ModemStatus20		;  Yes
	or	HSFlag[si],HHSDown	;Show hardware lines have dropped
ModemStatus30:
	jmp	InterruptLoop

ModemStatus40:
	jmp	InterruptLoop_ChkTx

; Lines are set for xmit.  Kick an xmit interrupt if needed

ModemStatus20:
	and	HSFlag[si],NOT (HHSDown OR HHSAlwaysDown)
					;Show hardware lines back up
	mov	cx,QOutCount[si]	;Output queue empty?
	jcxz	ModemStatus30		;  Yes, return to InterruptLoop
	jmp	FakeXmitEmpty		;Restart transmit

ModemStatus endp

page

;------------------------------------------------------------------------------
;
;   ENTER:  AX = message #
;	    DS:SI -> DEB
notify_owner proc near

	or	[si.NotifyFlags], ax
	lea	di, [si+SIZE ComDEB]
	mov	ax, ds
	mov	es, ax
	mov	ax, BIH_API_Call_Back	; call immediate, or in protected mode
	mov	bx, 1			; force SYS VM, if enhanced mode
	mov	cx, _INTERRUPT
	mov	dx, IntCodeOFFSET callback_event
%OUT use equate
	push	ds
	push	si
	mov	si, 1			; low priority boost
	push	bp
	mov	bp, es:[di.BIS_Mode]
	call	es:[bp][di.BIS_User_Mode_API]
	pop	bp
	pop	si
	pop	ds
	ret

notify_owner endp

;------------------------------------------------------------------------------
;
;   ENTER:  ES:DI -> BIS
;
callback_event proc far
	lea	si, [di-SIZE ComDEB]
	mov	ax, es
	mov	ds, ax
	mov	ax, [si.NotifyHandle]
	push	ax			; push hWnd
	mov	ax, WM_COMMNOTIFY
	push	ax			; push wMsg
	xor	ax, ax
	mov	al, [si.DCB_Id]
	push	ax			; push wParam = ComID
	xor	al, al
	push	ax			; push high word of lParam
	xchg	al, [si.NotifyFlagsLO]
	or	[si.NotifyFlagsHI], al
	push	ax			; push low word of lParam = event flags
	call	[lpPostMessage]
	ret
callback_event endp


PUBLIC TimerProc
TimerProc proc far

	push	ds
	mov	ax, _DATA
	mov	ds, ax
	assumes ds,data

	mov	ax, [activeCOMs]
	or	ax, ax
	jz	short tp_nonactive
	push	si
	mov	si, DataOFFSET COMptrs
	mov	cx, MAXCOM+1
tp_lp:
	push	si
	mov	si, [si]		; si -> ComDEB
	shr	ax, 1
	jnc	tp_lpend

	cmp	[si.RecvTrigger], -1	;Q: owner wants notification?
	je	short tp_lpend		;   N: skip notify
	cmp	[si.QInCount], 0	;Q: anything in input queue?
	je	short tp_lpend		;   N: skip notify
	test	[si.NotifyFlagsHI], CN_RECEIVE ;Q: timeout notify already given?
	jnz	short tp_lpend		;   N: skip notify

	xor	[si.NotifyFlagsHI], CN_Idle ;Q: first timer call?
	js	short tp_lpend		;   Y: skip notify

	push	ax
	push	cx
	mov	ax, CN_RECEIVE		;   N: notify owner
	call	notify_owner
	pop	cx
	pop	ax

tp_lpend:
	pop	si
	inc	si			; inc to ptr to next ComDEB
	inc	si
	or	ax, ax
	loopnz	tp_lp
	pop	si

tp_nonactive:
	pop	ds
	assumes ds,nothing
	ret

TimerProc endp
page

ifdef   NEC_98
;===========================================================================
;	System Timer Interrupt Routine
;
;			if ( QOutCount[si] != 0x0000 ) 
;				{
;				KickTx ();
;				}
;===========================================================================
public TickEntry4			;Ins 940923 KBNES
TickEntry4	proc	far		;for COM4
	push	si			;
	push	ds			;
	push	ax			;
	mov	si,dataOFFSET Comm4	;
	mov	ax, _DATA		;
	mov	ds, ax
					;
public	TickWork			;
TickWork:				;
        cmp     QOutCount[si],wo 00h	;Does queue empty ?
	jz	TickNoWork		;  Yes : Goto Return
	push	dx			;
        call    KickTxINT               ;
	pop	dx			;

TickNoWork:				;
	pop	ax			;
	pop	ds			;
	pop	si			;
	ret				;
TickEntry4	endp

;----------------------------Private-Routine----------------------------;
;
; KickTxInt - Kick Transmitter
;
; "Kick" the transmitter interrupt routine into operation.
; If the Transmitter Holding Register isn't empty, then
; nothing needs to be done.  If it is empty, then the xmit
; interrupt needs to enabled in the IER.
;
; Entry:
;   DS:SI --> DEB
;   INTERRUPTS DISABLED!
; Returns:
;   None
; Error Returns:
;   None
; Registers Preserved:
;   BX,CX,SI,DI,DS,ES
; Registers Destroyed:
;   AX,DX,FLAGS
; History:
;-----------------------------------------------------------------------;

;------------------------------Pseudo-Code------------------------------;
; {
; }
;-----------------------------------------------------------------------;
assumes ds,Data
assumes es,nothing

public   KickTxInt			;Ins 940923 KBNES
KickTxInt   proc   near
        mov     dx,Port[si]             ;Get device I/O address
        add     dl,ACE_IER              ;--> Interrupt enable register
        in      al,dx                   ;Get current IER state
	iodelay				;
        test    al,ACE_ETBEI            ;Interrupt already enabled?
        jnz     @F	                ;  Yes, don't reenable it
        or      al,ACE_ETBEI            ;  No, enable it
        out     dx,al
        iodelay                         ;8250, 8250-B bug requires
        out     dx,al                   ;  writting register twice
	iodelay				;
@@:
        ret
KickTxInt   endp
endif   ; NEC_98

ifdef DEBUG
	public	Control, DEF_Handler, COMHandler, APIHandler
	public	InterruptLoop, IntLoop10, IntLoop20
	public	DataAvail25, DataAvail30, DataAvail50
	public	DataAvail60, DataAvail70, DataAvail80, DataAvail90
	public	DataAvail100, DataAvail110, DataAvail120
	public	DataAvail130, DataAvail140, OutHandshakingChar
	public	XmitEmpty10, XmitEmpty20, XmitEmpty30, XmitEmpty40
	public	XmitEmpty59, XmitEmpty60
	public	XmitEmpty90, XmitEmpty100, XmitEmpty110
	public	ModemStatus10, ModemStatus20, ModemStatus30
	public	notify_owner, callback_event
endif

DOSTI proc    near
      FSTI
      ret
DOSTI endp

DOCLI proc    near
      FCLI
      ret
DOCLI endp



sEnd   IntCode
end