windows-nt/Source/XPSP1/NT/base/mvdm/wow16/drivers/neccomm2/ibmint.asm

1540 lines
36 KiB
NASM
Raw Normal View History

2020-09-26 03:20:57 -05:00
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