|
|
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
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 jc short DEF_RM_chain 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 jc short DEF_chain 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: out 0A0h,al ; Y: EOI slave api_master: out INTA0,al ; EOI master ret
api_not_EOI: cmp ax, BIH_API_Call_Back jae short api_callme push dx push cx mov dx, INTA1 mov cx, es:[di.BIS_IRQ_Number] cmp cl, 8 ;Q: 2nd PIC? jb @f ; N: mov dx, 0A1h ; Y: dx = mask port 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 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 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
InterruptLoop: pop dx ;Get ID reg I/O address
in al,dx ;Get Interrupt Id 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 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
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 mov LSRShadow[si],0 .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:
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 not ah and al,ah or HSFlag[si],HHSDropped ;Show lines have been dropped out dx,al ; and drop the lines 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 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 jmp DataAvail00 @@: jmp InterruptLoop_ChkTx
DataAvail endp
OutHandshakingChar proc near
add dl, ACE_LSR mov ah, al @@: in al, dx test al, ACE_THRE jz @B sub dl, ACE_LSR mov al, ah out dx, al 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 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? 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? 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
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 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 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 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
|