PAGE 60,150
;***************************************************************************
;*  INT2.ASM
;*
;*      Assembly code support routines used for the TOOLHELP.DLL interrupt
;*      trapping API
;*
;***************************************************************************

        INCLUDE TOOLPRIV.INC
        INCLUDE WINDOWS.INC
        include vint.inc
.286p

;** Symbols
I_EXCEPTION             EQU     0
I_INTERRUPT             EQU     1
MAX_INTERRUPT           EQU     7
GIVE_WDEB386            EQU     8000h
BAD_STACK_FLAG          EQU     8000h
MIN_STACK_ALLOWED       EQU     128

;** Local types

INT_INFO STRUC
ii_wNumber      DW      ?               ;INT nn
ii_wType        DW      ?               ;I_EXCEPTION or I_INTERRUPT
ii_dwChain      DD      ?
ii_wHandler     DW      ?               ;Note that this is CS relative
INT_INFO ENDS

;** Data
sBegin  DATA

IntInfo         LABEL   BYTE
        public IntInfo
UD_Info         DW      6               ;Undefined opcode
                DW      I_EXCEPTION     ;This should be DPMI-hooked
                DD      0               ;Chain address (will be initialized)
                DW      OFFSET _TEXT:UD_Handler
Div0_Info       DW      0               ;Divide by zero
                DW      I_EXCEPTION     ;Hook with DPMI
                DW      OFFSET _TEXT:Div0_Handler
                DW      0
                DW      OFFSET _TEXT:Div0_Handler
Int1_Info       DW      1               ;Single step + debug register
                DW      I_INTERRUPT     ;Hook with DOS
                DD      0               ;Chain address
                DW      OFFSET _TEXT:Int1_Handler
Int3_Info       DW      3               ;Software debug int
                DW      I_INTERRUPT     ;Hook with DOS
                DD      0               ;Chain address
                DW      OFFSET _TEXT:Int3_Handler
GP_Info         DW      13              ;GP Fault
                DW      I_EXCEPTION     ;This should be DPMI-hooked
                ;** This entry is a special case entry for the Win30 std mode
                ;*      handler.  This is a separate entry point into the
                ;**     interrupt handler routine
                DW      OFFSET _TEXT:GP_StdModeHandler
                DW      0
                DW      OFFSET _TEXT:GP_Handler
SF_Info         DW      12              ;Stack fault
                DW      I_EXCEPTION     ;This should be DPMI-hooked
                ;** This entry is a special case entry for the Win30 std mode
                ;*      handler.  This is a separate entry point into the
                ;**     interrupt handler routine
                DW      OFFSET _TEXT:SF_StdModeHandler
                DW      0
                DW      OFFSET _TEXT:SF_Handler
PF_Info         DW      14              ;Page fault
                DW      I_EXCEPTION     ;This should be DPMI-hooked
                ;** This entry is a special case entry for the Win30 std mode
                ;*      handler.  This is a separate entry point into the
                ;**     interrupt handler routine
                DW      OFFSET _TEXT:PF_StdModeHandler
                DW      0
                DW      OFFSET _TEXT:PF_Handler
CASRq_Info      DW      256             ;CtlAltSysRq (fake interrupt)
                DW      I_INTERRUPT     ;Hook with DOS
                DD      0               ;Chain address
                DW      OFFSET _TEXT:CASRq_Handler

                ;** The following data is used to see if GDI wants the
                ;**     Div0 we have trapped
lpGDIFlag       DD      0
hGDI            DW      0
szGDI           DB      'GDI', 0
        public lpGDIFlag, hGDI

                ;** Points to a KERNEL routine to see if it wants the
                ;**     GP fault first
lpfnPV          DD      0               ;Call to see if PV GP fault
        public lpfnPV

                ;** Globals used for DPMI emulation
lpOldHandler    DD      0               ;Previous DPMI exception handler
lpChainCSIP     DD      0               ;Next exception handler on chain
wException      DW      0
        public lpOldHandler, lpChainCSIP

externW wCASRqFlag                      ;Set when an CASRq INT3 has been set
externD dwCASRqCSIP                     ;Holds the CS:IP of the CASRq INT3
sEnd

;** Imports
externNP TerminateApp
externNP HelperHandleToSel
externNP HelperVerifySeg
externFP AllocCStoDSAlias
externFP FreeSelector
externFP GetModuleHandle
externFP GetProcAddress
externFP GlobalEntryHandle
externFP _wsprintf
externFP OutputDebugString
externA __WinFlags

;** Functions

sBegin  CODE
        assumes CS,CODE
        assumes DS,DATA

;** Interrupt trapping API

;  InterruptInit
;       Hooks all necessary interrupts and exceptions to allow an API
;       for app-level interrupt hooks.

cProc   InterruptInit, <NEAR,PUBLIC>, <si,di>
cBegin
        ;** Loop through all possible handlers
        mov     cx,MAX_INTERRUPT        ;Number of ints to hook
        lea     si,IntInfo              ;Get the address of the array
DII_HandlerLoop:
        push    cx                      ;Save loop counter
        cmp     [si].ii_wNumber,256     ;Fake exception?
        jae     DII_Continue            ;Yes, don't hook anything!
        cmp     [si].ii_wType,I_EXCEPTION ;Exception?
        jnz     DII_Interrupt           ;Nope, hook as interrupt
        
        ;** Do a special case for 3.0 Std Mode 
        test    wTHFlags,TH_WIN30STDMODE ;Are we in Win30 std mode?
        jz      DII_NotStdMode          ;No.
        mov     ax,WORD PTR [si].ii_dwChain ;Get the secondary handler
        mov     [si].ii_wHandler,ax     ;Make sure we use it instead!
DII_NotStdMode:

        ;** Hook as an exception (DPMI)
        mov     ax,0202h                ;Get exception handler - DPMI
        mov     bl,BYTE PTR [si].ii_wNumber ;Interrupt number
        int     31h                     ;Call DPMI
        mov     WORD PTR [si].ii_dwChain,dx ;Save the old offset
        mov     WORD PTR [si].ii_dwChain + 2,cx ;Save the old selector
        mov     ax,0203h                ;Set exception handler - DPMI
        mov     bl,BYTE PTR [si].ii_wNumber ;Interrupt number
        mov     dx,[si].ii_wHandler     ;Address of exception handler
        mov     cx,cs                   ;Selector value of handler
        int     31h                     ;Call DPMI
        jmp     SHORT DII_Continue

        ;** Hook as an interrupt (DOS)
DII_Interrupt:
        mov     ah,35h                  ;Get interrrupt handler - DOS
        mov     al,BYTE PTR [si].ii_wNumber ;Interrupt number
        int     21h                     ;Call DOS
        mov     WORD PTR [si].ii_dwChain,bx ;Save the old offset
        mov     WORD PTR [si].ii_dwChain + 2,es ;Save the old selector
        mov     ah,25h                  ;Set interrupt handler - DOS
        mov     al,BYTE PTR [si].ii_wNumber ;Interrupt number
        mov     dx,[si].ii_wHandler     ;Address of exception handler
        push    ds                      ;Save static DS for later
        push    cs                      ;DS = CS
        pop     ds
        int     21h                     ;Call DOS
        pop     ds                      ;Get segment back

        ;** Prepare for next in table
DII_Continue:
        add     si,SIZE INT_INFO        ;Bump to next entry
        pop     cx                      ;Get loop counter back
        loop    DII_HandlerLoop         ;Loop back

        ;** Prepare the linked list
        mov     npIntHead,0             ;Put a NULL in the list head

        ;** Get information so we can check the GDI flag
        lea     ax,szGDI                ;Get the string
        cCall   GetModuleHandle, <ds,ax> ;Get GDI's module handle
        cCall   HelperHandleToSel, <ax> ;Convert the owner to a selector
        mov     hGDI,ax                 ;Save it for later
        cCall   GetProcAddress, <ax,0,355> ;The flag is ordinal 355
        mov     WORD PTR lpGDIFlag[0],ax ;Save it for later
        mov     WORD PTR lpGDIFlag[2],dx

DII_End:
        ;** Return TRUE
        mov     ax,1
cEnd


;  InterruptUnInit
;       Unhooks all interrupts and exceptions hooked by DebugInterruptUnInit.

cProc   InterruptUnInit, <NEAR,PUBLIC>, <si,di>
cBegin

        ;** Loop through all possible handlers
        mov     cx,MAX_INTERRUPT        ;Number of ints to hook
        lea     si,IntInfo              ;Get the address of the array
DIU_HandlerLoop:
        push    cx                      ;Save loop counter
        cmp     [si].ii_wNumber,256     ;Fake exception?
        jae     DIU_Continue            ;Yes, don't unhook anything!
        cmp     [si].ii_wType,I_EXCEPTION ;Exception?
        jnz     DIU_Interrupt           ;Nope, hook as interrupt

        ;** Unhook exception (DPMI)
        mov     ax,0203h                ;Set exception handler - DPMI
        mov     bl,BYTE PTR [si].ii_wNumber ;Interrupt number
        mov     dx,WORD PTR [si].ii_dwChain ;Put back the old offset
        mov     cx,WORD PTR [si].ii_dwChain + 2 ;Put back the old selector
        int     31h                     ;Call DPMI
        jmp     SHORT DIU_Continue

        ;** Unhook interrupt (DOS)
DIU_Interrupt:
        mov     ah,35h                  ;Get interrrupt handler - DOS
        mov     al,BYTE PTR [si].ii_wNumber ;Interrupt number
        int     21h                     ;Call DOS
        mov     ah,25h                  ;Set interrupt handler - DOS
        mov     al,BYTE PTR [si].ii_wNumber ;Interrupt number
        mov     dx,WORD PTR [si].ii_dwChain ;Put back the old offset
        push    ds
        mov     ds,WORD PTR [si].ii_dwChain + 2 ;Put back the old selector
        int     21h                     ;Call DOS
        pop     ds

        ;** Prepare for next in table
DIU_Continue:
        add     si,SIZE INT_INFO        ;Bump to next entry
        pop     cx                      ;Get loop counter back
        loop    DIU_HandlerLoop         ;Loop back

        ;** Prepare the linked list
        mov     npIntHead,0             ;Put a NULL in the list head

DIU_End:

cEnd

InterruptEntry  MACRO  Name, wBytes
        labelFP Name                    ;;Start at this address
        PUBLIC  Name
        sub     sp,wBytes               ;;Leave room on stack for return val
        push    bx                      ;;Save for the info pointer
ENDM

InterruptJump   MACRO pInfo
        mov     bx,OFFSET pInfo         ;;Point to interrupt info
        jmp     DIH_Main
ENDM

;  InterruptHandler
;       This routine is used to handle interrupts as they come in.  This
;       routine has multiple entry points; a seperate one for each int/
;       exception trapped.   Because interrupts and exceptions have
;       different stack frames, they are handled by two different code
;       sections.

cProc   InterruptHandler, <FAR,PUBLIC>
cBegin  NOGEN

        ;** All interrupt entry points here

        InterruptEntry GP_Handler, 14   ;Normal GP fault
        InterruptJump GP_Info
        InterruptEntry GP_StdModeHandler, 12 ;3.0 Std mode GP fault
        InterruptJump GP_Info
        InterruptEntry SF_Handler, 14   ;Normal Stack Fault
        InterruptJump SF_Info
        InterruptEntry SF_StdModeHandler, 12 ;3.0 Std mode Stack Fault
        InterruptJump SF_Info
        InterruptEntry PF_Handler, 14   ;Page fault
        InterruptJump PF_Info
        InterruptEntry PF_StdModeHandler, 10 ;3.0 Std mode Page fault
        InterruptJump PF_Info
        InterruptEntry UD_Handler, 14   ;Undefined opcode
        InterruptJump UD_Info
        InterruptEntry Int1_Handler, 14 ;Int 1
        InterruptJump Int1_Info
        InterruptEntry Int3_Handler, 14 ;Int 3
        InterruptJump Int3_Info
        InterruptEntry CASRq_Handler, 14 ;Ctrl-Alt-SysRq (not really an int)
        InterruptJump CASRq_Info

        ;** The divide by zero case has to include checking to make sure
        ;**     that this isn't GDI's divide by zero.

        InterruptEntry Div0_Handler, 14

        ;** Check to see if GDI wants this Div0
        push    ds                      ;Save some registers
        push    es
        mov     bx,_DATA                ;Point to our data
        mov     ds,bx                   ;  with DS
        mov     bx,WORD PTR lpGDIFlag[0];Get the low word
        push    bx
        or      bx,WORD PTR lpGDIFlag[2];Do we have a flag to look at?
        pop     bx
        jz      DIH_NoFlag              ;No.  Do this the hard way

        ;** Since we have a pointer to GDI's flag to look at, use it
        mov     es,WORD PTR lpGDIFlag[2];Get the seg value
        cmp     WORD PTR es:[bx],0      ;The flag is nonzero if GDI wants it
        je      DIH_NormalDiv0          ;GDI doesn't want it

        ;** GDI wants the Div0 so chain to it
DIH_ChainToGDI:
        pop     es                      ;Restore registers
        pop     ds                      ;  (Doesn't trash flags)
        push    bp                      ;Make the same stack frame for compat
        mov     bp,sp
        pusha                           ;Save all registers
        push    ds
        push    es
        mov     ax,_DATA                ;Get the data segment
        mov     ds,ax                   ;Point with DS
        mov     bx,OFFSET Div0_Info     ;This fault's info
        jmp     DIH_DPMIChainOn         ;Chain on (ignore the int)

DIH_NormalDiv0:
        pop     es                      ;Restore registers
        pop     ds
        InterruptJump Div0_Info

        ;** We didn't get a GDI flag (only present in 3.1) so instead, we
        ;*      check the owner of the CS where the fault occurred.  If
        ;**     the owner is GDI, we ignore the Div0.
DIH_NoFlag:
        push    bp                      ;Make a stack frame
        mov     bp,sp
        sub     sp,SIZE GLOBALENTRY     ;Make room for a structure
Global  EQU     [bp - SIZE GLOBALENTRY] ;Point to our structure
        pusha                           ;Save all registers
        mov     WORD PTR Global.ge_dwSize[0],SIZE GLOBALENTRY ;Size of struct
        mov     WORD PTR Global.ge_dwSize[2],0
        lea     bx,Global               ;Point to the structure
        test    wTHFlags,TH_WIN30STDMODE ;3.0 std mode?
        jnz     DIH_Div0_StdMode        ;Yes
        mov     ax,[bp + 1ah + 4]       ;Get the CS value (4 is extra BP,BX
        jmp     SHORT @F                ;  pushed by InterruptEntry macro)
DIH_Div0_StdMode:
        mov     ax,[bp + 14h + 4]       ;Get the CS value
@@:     cCall   GlobalEntryHandle, <ss,bx,ax> ;Get info about the CS
        or      ax,ax                   ;Did the call succeed?
        jne     @F                      ;Yes, go on
        popa                            ;No, clear stack frame and do normal
        mov     sp,bp
        pop     bp
        jmp     DIH_NormalDiv0          ;Jump to normal processing
@@:     mov     ax,Global.ge_hOwner     ;Get the owner
        cCall   HelperHandleToSel, <ax> ;Make it a selector
        cmp     hGDI,ax                 ;Is this owned by GDI?
        popa                            ;Restore the registers
        mov     sp,bp                   ;Clear stack frame
        pop     bp
        je      DIH_ChainToGDI          ;Yes, so give the interrupt to it
        jmp     DIH_NormalDiv0          ;No, do normal stuff

        ;** We now have to first find the item on the block to see if we
        ;**     want to handle the interrupt.
PubLabel CommonInterruptEntry
DIH_Main:
        push    bp                      ;Make a stack frame
        mov     bp,sp
        pusha                           ;Save all registers
        push    ds
        push    es

        ;** We check first to see if this was a GP fault received from the
        ;*      parameter validation code.  If it was, we just chain on
        ;*      just as if we don't find any handlers.
        ;**
        mov     ax,_DATA                ;Get our data segment
        mov     ds,ax
        FSTI                             ;Must have interrupts on
        cmp     bx,OFFSET GP_Info       ;GP Fault?
        jnz     DIH_NotPVGPFault        ;No.
        mov     cx,WORD PTR lpfnPV[0]   ;Get the low word
        or      cx,WORD PTR lpfnPV[2]   ;Param Validation stuff present?
        jz      DIH_NotPVGPFault        ;No, skip this

        ;** Check to see if the parameter validation code wants the fault
        push    ds
        push    bx
        push    [bp + 1Ah]              ;Push faulting CS:IP
        push    [bp + 18h]
        call    [lpfnPV]                ;Call it
        pop     bx
        pop     ds
        or      ax,ax                   ;Non-zero means this was PV fault
        je      DIH_NotPVGPFault        ;Not a PV GP fault

        ;** It is a parameter validation fault so ignore it
        jmp     DIH_DPMIChainOn         ;Chain the fault on--we don't want it

        ;** We check here to see if the INT3 we received is from the CASRq
        ;*      handler.  If it was, we have to replace the INT3 with the
        ;*      previous byte and tell the user this was actually a CASRq
        ;**     event (not an INT3).
PubLabel DIH_NotPVGPFault
        cmp     bx,OFFSET Int3_Info     ;INT3?
        jnz     DIH_NotCASRq            ;Nope, ignore all this
        cmp     wCASRqFlag,0            ;Was this because of CASRq?
        je      DIH_NotCASRq            ;No.
        mov     ax,[bp + 12h]           ;INT3 is an IRET frame.  Get bkpt IP
        dec     ax                      ;Breaks AFTER instruction
        cmp     WORD PTR dwCASRqCSIP[0],ax ;Is this the right CASRq address?
        jne     DIH_NotCASRq            ;Nope
        mov     dx,[bp + 14h]           ;Get the breakpoint CS
        cmp     WORD PTR dwCASRqCSIP[2],dx ;Is this correct?
        jne     DIH_NotCASRq            ;Nope
        push    ax                      ;Save the IP value
        cCall   AllocCStoDSAlias, <dx>  ;Get a data alias to the CS
        mov     es,ax                   ;Point with ES
        pop     si                      ;Restore the IP value
        mov     [bp + 12h],si           ;Back to instr where INT3 was
        mov     al,BYTE PTR wCASRqFlag  ;Get the saved byte
        mov     es:[si],al              ;Put it back in the code
        mov     wCASRqFlag,0            ;Clear the flag
        cCall   FreeSelector, <es>      ;Get rid of the alias
        mov     bx,OFFSET CASRq_Info    ;Point to the CASRq information

        ;** See if we have at least one handler.  We should always have one.
PubLabel DIH_NotCASRq
        mov     si,npIntHead            ;Get the list start
        or      si,si                   ;Are there any routines hooked?
        jnz     DIH_Found               ;There should ALWAYS be at least one
                                        ;  routine hooked (otherwise, the
                                        ;  interrupt hooks should have
                                        ;  already been removed)

        ;** Return the stack to its prior state and chain on.
        ;*      We only get here in an erroneous state.  We keep the code in
        ;*      to avoid GP faulting if things get wierd.
        ;*      The stack looks like this:
        ;*              ------------
        ;*              |    ES    |
        ;*              |    DS    |
        ;*              |   PUSHA  |
        ;*         BP-->|  Old BP  |  [BP + 00h]
        ;*              |    BX    |  [BP + 02h]
        ;*              |  Empty   |  [BP + 04h]
        ;*              |  Empty   |  [BP + 06h]
        ;*              |  Empty   |  [BP + 08h]
        ;*              |  Empty   |  [BP + 0Ah]
        ;*              |  Empty   |  [BP + 0Ch]
        ;*              |Our Ret IP|  [BP + 0Eh]
        ;*              |Our Ret CS|  [BP + 10h]
        ;*              |Original  |
        ;*              |  Frame   |
        ;*              |   ....   |
        ;*              ------------
        ;**
PubLabel DIH_DPMIChainOn
        mov     ax,WORD PTR [bx].ii_dwChain ;Get the LOWORD
        mov     [bp + 0eh],ax           ;Put into the frame we created
        mov     ax,WORD PTR [bx].ii_dwChain + 2 ;Get the HIWORD
        mov     [bp + 10h],ax           ;Put into the frame
        pop     es                      ;Clear the stack
        pop     ds
        popa
        pop     bp
        pop     bx
        add     sp,10                   ;Clear extra space
        retf                            ;This uses our own "return" frame
                                        ;  to chain on

        ;** Since we found the entry, we have to call the user callback.
        ;*      Because we must be reentrant at this state, we have to make
        ;*      sure that we're safe.  To do so, we must do different
        ;**     actions for DPMI and for DOS frames.
PubLabel DIH_Found
        cmp     [bx].ii_wType,I_EXCEPTION ;DPMI Exception frame?
        je      @F
        jmp     DIH_SkipDPMI            ;No.  Skip DPMI processing
@@:

        ;** If we are in Win3.0 Std Mode, the DPMI frame was broken.  It
        ;*      simply left the normal IRET frame on the stack *AND* the
        ;**     error code.
        test    wTHFlags,TH_Win30StdMode ;3.0 Std mode?
        jz      @F
        jmp     DIH_SkipDPMI            ;Yes
@@:

        ;** Tell DPMI that the exception is over.  Before we do this,
        ;*      however, save information we'll need later on the user stack.
        ;*      The stack currently looks like this:
        ;*              ------------
        ;*              |    ES    |
        ;*              |    DS    |
        ;*              |   PUSHA  |
        ;*         BP-->|  Old BP  |  [BP + 00h]
        ;*              |    BX    |  [BP + 02h]
        ;*              |  Empty   |  [BP + 04h]
        ;*              |  Empty   |  [BP + 06h]
        ;*              |  Empty   |  [BP + 08h]
        ;*              |  Empty   |  [BP + 0Ah]
        ;*              |  Empty   |  [BP + 0Ch]
        ;*              |  Empty   |  [BP + 0Eh]
        ;*              |  Empty   |  [BP + 10h]
        ;*              |  Ret IP  |  [BP + 12h]    <-
        ;*              |  Ret CS  |  [BP + 14h]      |
        ;*              |Error Code|  [BP + 16h]      | Pushed by DPMI
        ;*              |    IP    |  [BP + 18h]      |
        ;*              |    CS    |  [BP + 1Ah]      | (Locked stack)
        ;*              |   Flags  |  [BP + 1Ch]      |
        ;*              |    SP    |  [BP + 1Eh]      |
        ;*              |    SS    |  [BP + 20h]    <-
        ;*              ------------
        ;*
        ;*      Before returning to DPMI, however, we want to create a
        ;*      stack frame on the user's stack that we will be returning
        ;*      to so we can preserve information in a reentrant fashion.
        ;*      The user's stack will appear like this:
        ;*              ------------
        ;*       BP---->|  Old BP  |  [BP + 00h]
        ;*              |    BX    |  [BP + 02h]
        ;*              |Our Ret IP|  [BP + 04h]
        ;*              |Our Ret CS|  [BP + 06h]
        ;*              |  Ret IP  |  [BP + 08h]
        ;*              |  Ret CS  |  [BP + 0Ah]
        ;*              |    AX    |  [BP + 0Ch]
        ;*              |Exception#|  [BP + 0Eh]
        ;*              |  Handle  |  [BP + 10h]
        ;*              |    IP    |  [BP + 12h]
        ;*              |    CS    |  [BP + 14h]
        ;*              |   Flags  |  [BP + 16h]
        ;*              ------------
        ;**

PubLabel DIH_Exception

        ;** Check to see if we're already on the faulting stack.  If we are,
        ;**     we want to shift everything up on this stack so that we
        ;**     have room for the TOOLHELP user frame
        mov     ax,ss                   ;Get the current SS
	cmp	ax,WORD PTR ss:[bp + 20h] ;Is it the same as the user frame?
	jne	DIH_EnoughRoomOnStack    ;No, ignore all of this

        ;** Move everything up by copy everything that's on stack now to
        ;**     above where SP starts at.  This actually uses too much
        ;**     stack, but it's safe and easy.
	push	bp                      ;We use BP to do copy
	lea	bp,[bp + 20h]           ;Point to lowest WORD to copy
	mov	ax,sp                   ;Point to position to copy to
	dec	ax
	dec	ax
DIH_CopyLoop:
	push	WORD PTR [bp]           ;Copy a WORD
	dec	bp                      ;Point to next WORD to copy
	dec	bp
	cmp	bp,ax                   ;Done yet?
	jne	DIH_CopyLoop            ;No
	pop	bp                      ;Yes, compute new BP value
	sub	bp,56                   ;Point BP to right place

        ;** Put stuff on DPMI's stack
PubLabel DIH_EnoughRoomOnStack
        mov     di,[bp + 1Eh]           ;Get the old SP value
        mov     cx,[bp + 20h]           ;  and the SS value
        cmp     di,MIN_STACK_ALLOWED    ;Are we going to stack fault?
        jb      DIH_BadStack            ;Yes, so swich
        mov     ax,__WinFlags           ;Make sure we have a 386 or higher
        test    ax,WF_CPU286
        jnz     DIH_SkipBigCheck        ;No need to check big bit
.386p
        push    eax                     ;Make sure we don't trash EAX
        lar     eax,ecx                 ;Get the access rights DWORD
        test    eax,00400000h           ;Check the BIG bit
        pop     eax
        jnz     DIH_BadStack            ;Don't use this stack if BIG
.286p
DIH_SkipBigCheck:
        mov     ax,di                   ;Get the stack pointer
        add     ax,2                    ;Point just beyond
        cCall   HelperVerifySeg, <cx,ax> ;Is this seg OK?
        or      ax,ax                   ;Check for success
        jz      DIH_BadStack            ;Stack is bogus, don't change to it

PubLabel DIH_StackOK
        sub     di,20                   ;Reserve space for the user frame
        mov     ds,cx                   ;Get stack value in DS
        mov     dx,[bp + 1Ah]           ;Get the old CS value
        mov     cx,[bp + 18h]           ;Get the old IP value
        mov     ax,[bp + 1Ch]           ;Get the old flags
        mov     [bp + 1Eh],di           ;Save as new SP value
        sub     di,4                    ;Make DI equal to what BP will be
        mov     [bp + 1Ah],cs           ;Prepare to IRET to ourself
        mov     [bp + 18h],OFFSET _TEXT:DIH_DPMIRestart

        ;** Save some things on the user's stack before returning
        mov     [di + 16h],ax           ;Save the flags
        mov     [di + 14h],dx           ;Save the old CS value
        mov     [di + 12h],cx           ;Save the old IP value
        mov     [di + 0Eh],bx           ;INT_INFO pointer to new stack
        mov     [di + 10h],si           ;Handle to new stack

        ;** Clear the Trace and Ints Enabled flags
        and     [bp + 1Ch],NOT 0100h    ;Clear TF.  We don't want to trace here
        pop     es                      ;Clear the DPMI stack
        pop     ds
        popa
        pop     bp
        pop     bx
        add     sp,14                   ;Clear extra allocated space
        retf

        ;** The user stack is bad, so we want to stay on the fault handler
        ;**     stack.  In order to do this, we have to build a frame for
        ;**     the callback directly on the fault handler stack.
        ;**     We build the frame here and jump to it.
        ;**              ------------
        ;**              |    ES    |
        ;**              |    DS    |
        ;**              |   PUSHA  |
        ;**         BP-->|  Old BP  |  [BP + 00h]
        ;**              |    BX    |  [BP + 02h]
        ;**              |  Empty   |  [BP + 04h]
        ;**              |Our Ret IP|  [BP + 06h]   ; Client callback addr
        ;**              |Our Ret CS|  [BP + 08h]
        ;**              |  Ret IP  |  [BP + 0Ah]   ; TOOLHELP restart addr
        ;**              |  Ret CS  |  [BP + 0Ch]
        ;**              |    AX    |  [BP + 0Eh]   ; Saved AX for MPI
        ;**              |Exception#|  [BP + 10h]   ; Exception number
        ;**              |  Handle  |  [BP + 12h]   ; TOOLHELP handle
        ;**              |    IP    |  [BP + 14h]   ; IRET frame of fault
        ;**              |    CS    |  [BP + 16h]
        ;**              |  Flags   |  [BP + 18h]
        ;**              |    SP    |  [BP + 1Ah]   ; Faulting SS:SP
        ;**              |    SS    |  [BP + 1Ch]
        ;**              |  Ret IP  |  [BP + 1Eh]   ; DPMI return address
        ;**              |  Ret CS  |  [BP + 20h]
        ;**              ------------
PubLabel DIH_BadStack
        mov     dx,[bp + 12h]           ;DPMI return CS:IP
        mov     cx,[bp + 14h]           ;  stored in CX:DX
        mov     ax,[bp + 18h]           ;Faulting IP
        mov     [bp + 14h],ax
        mov     ax,[bp + 1Ah]           ;Faulting CS
        mov     [bp + 16h],ax
        mov     ax,[bp + 1Ch]           ;Flags
        mov     [bp + 18h],ax
        mov     ax,[bp + 1Eh]           ;Faulting SP
        mov     [bp + 1Ah],ax
        mov     ax,[bp + 20h]           ;Faulting SS
        mov     [bp + 1Ch],ax
        mov     [bp + 1Eh],dx           ;DPMI ret IP
        mov     [bp + 20h],cx           ;DPMI ret CS
        mov     [bp + 12h],si           ;Point to INTERRUPT struct
        mov     ax,[bx].ii_wNumber      ;Get the interrupt number
        or      ax,BAD_STACK_FLAG       ;Flag the client that stack is bad
        mov     [bp + 10h],ax
        mov     ax,[bp - 02h]           ;Get the AX value from the PUSHA frame
        mov     [bp + 0Eh],ax
        mov     [bp + 0Ch],cs           ;Point to callback return address
        mov     [bp + 0Ah],OFFSET _TEXT:DIH_CallbackRestart
        mov     ax,WORD PTR [si].i_lpfn ;Point to the user callback OFFSET
        mov     [bp + 06h],ax
        mov     ax,WORD PTR [si].i_lpfn + 2 ;Point to callback segment
        mov     [bp + 08h],ax
        pop     es                      ;Clear the stack
        pop     ds
        popa
        pop     bp
        pop     bx
        add     sp,2
        retf                            ;Jump to the user callback

        ;** At this point, DPMI IRETs back to us instead of to the faulting
        ;**      app.  We have to now create a stack frame identical to the
        ;**      frame used by interrupt-style hooks.  Note that we have
        ;**      already allocated the frame space (but not initialized it)
        ;**      before returning to DPMI.
        ;**      It will look like this:
        ;**              ------------
        ;**              |    ES    |  [BP - 14h]
        ;**              |    DS    |  [BP - 12h]
        ;**              |    DI    |  [BP - 10h]
        ;**              |    SI    |  [BP - 0Eh]
        ;**              |    BP    |  [BP - 0Ch]
        ;**              |    SP    |  [BP - 0Ah]
        ;**              |    BX    |  [BP - 08h]
        ;**              |    DX    |  [BP - 06h]
        ;**              |    CX    |  [BP - 04h]
        ;**              |    AX    |  [BP - 02h]
        ;**       BP---->|  Old BP  |  [BP + 00h]
        ;**              |    BX    |  [BP + 02h]
        ;**              |Our Ret IP|  [BP + 04h]
        ;**              |Our Ret CS|  [BP + 06h]
        ;**              |  Ret IP  |  [BP + 08h]
        ;**              |  Ret CS  |  [BP + 0Ah]
        ;**              |    AX    |  [BP + 0Ch]
        ;**              |Exception#|  [BP + 0Eh]
        ;**              |  Handle  |  [BP + 10h]
        ;**              |    IP    |  [BP + 12h]
        ;**              |    CS    |  [BP + 14h]
        ;**              |   Flags  |  [BP + 16h]
        ;**              ------------
        ;**
PubLabel DIH_DPMIRestart
        push    bx                      ;Save this register we're using
        push    bp                      ;Make a stack frame
        mov     bp,sp
        pusha                           ;Save all the registers
        push    ds
        push    es
        mov     bx,[bp + 0Eh]           ;Get the INT_INFO pointer back
        mov     si,[bp + 10h]           ;Get the INTERRUPT structure back
        mov     ax,_DATA                ;Get our data segment
        mov     ds,ax

        ;** We can now proceed with joint processing as we've matched the
        ;**     DOS interrupt frame
PubLabel DIH_SkipDPMI

        ;** Build our return frame and jump to the user callback
        mov     [bp + 10h],si           ;Point to INTERRUPT struct
        mov     ax,[bx].ii_wNumber      ;Get the interrupt number
        mov     [bp + 0Eh],ax           ;Put on frame
        mov     ax,[bp - 02h]           ;Get the AX value from the PUSHA frame
        mov     [bp + 0Ch],ax           ;Put on frame
        mov     [bp + 0Ah],cs           ;Point to callback return address
        mov     [bp + 08h],OFFSET _TEXT:DIH_CallbackRestart
        mov     ax,WORD PTR [si].i_lpfn ;Point to the user callback OFFSET
        mov     [bp + 04h],ax
        mov     ax,WORD PTR [si].i_lpfn + 2 ;Point to callback segment
        mov     [bp + 06h],ax
        pop     es                      ;Clear the stack
        pop     ds
        popa
        pop     bp
        pop     bx
        retf                            ;Jump to the user callback

        ;** When the callback returns, we have to know how to call the
        ;*      next matching callback or to chain on the interrupt list.
        ;*      We have to do a raft of special stuff if this was an
        ;*      exception so that the chained on handlers think it was
        ;**     DPMI that called them.
PubLabel DIH_CallbackRestart
        sub     sp,8                    ;Leave room for ret addresses
        push    bx                      ;For compat. with the above code
        push    bp                      ;Make the same stack frame
        mov     bp,sp
        pusha
        push    ds
        push    es

        ;** Get the next matching item on the list
        mov     ax,_DATA                ;Get our data segment
        mov     ds,ax
        mov     ax,[bp + 0Ch]           ;Get the saved AX value
        mov     [bp - 02h],ax           ;Put in PUSHA frame
        mov     si,[bp + 10h]           ;Get the last handle used
        or      si,si                   ;If NULL, app has messed with it
        jz      DIH_NukeIt              ;Nuke the app--it did a no-no
        mov     si,[si].i_pNext         ;Get the next item in the list
        or      si,si                   ;End of the line?
        jz      DIH_NextNotFound        ;Yes, chain on
        mov     ax,[bp + 0Eh]           ;Get the exception number
        cCall   InterruptInfo           ;Get the INT_INFO structure
        or      ax,ax                   ;If NULL return, user messed with #
        jz      DIH_NukeIt              ;  so nuke it
        mov     bx,ax                   ;Point with BX
        jmp     DIH_SkipDPMI            ;Process this one

        ;** If we don't find a match, we pass on to previous handler
PubLabel DIH_NextNotFound
        mov     ax,[bp + 0Eh]           ;Get the exception number
        and     ax,7fffh                ;Clear the new stack bit
        cCall   InterruptInfo           ;Find the INT_INFO structure
        or      ax,ax                   ;If the user messed with it,
        jz      DIH_NukeIt              ;  nuke the app.
        mov     si,ax                   ;Get the pointer in AX
        test    wTHFlags,TH_Win30StdMode ;3.0 Std mode?
        jnz     DIH_30StdChainOn        ;Always do normal chain on in 3.0sm
        cmp     [si].ii_wType,I_INTERRUPT ;Was this an interrupt?
        je      DIH_ChainOn             ;Yes, do normal chain on
        jmp     DIH_EmulateDPMI         ;No, do the DPMI chain on

PubLabel DIH_NukeIt
        push    [bp + 16h]              ;Copy the IRET frame for KERNEL
        push    [bp + 14h]
        push    [bp + 12h]

        push    0                       ;Nuke current task
        push    UAE_BOX OR GIVE_WDEB386 ;UAE box + give to wdeb
        push    cs                      ;Simulate a far jump
        call    NEAR PTR TerminateApp   ;Nuke the app

        ;** We only get here if WDEB386 is installed.  We tell it to set
        ;*      a breakpoint, then restart the app, in effect giving
        ;*      control to WDEB386.  Unfortunately, at this point, all
        ;**     fault handlers for this task have been removed
        add     sp,6                    ;Clear fake IRET frame
        mov     cx,[bp + 14h]           ;Faulting CS
        mov     bx,[bp + 12h]           ;Faulting IP
        mov     ax, 40h                 ;16 bit forced go command
        int     41h                     ;Call debugger
        pop     es                      ;Restore registers and clear stack
        pop     ds
        popa
        pop     bp
        pop     bx
        add     sp,14                   ;Clear extra words
                                        ;  all that remains is IRET frame
        iret                            ;WDEB386 will get control

PubLabel DIH_NukeApp
        push    0                       ;Nuke current task
        push    UAE_BOX                 ;Draw the UAE box
        push    cs                      ;Simulate a far jump
        call    NEAR PTR TerminateApp   ;Nuke the app
        int     1                       ;Should never return
        jmp     SHORT DIH_ChainOn

        ;** In 3.0 standard mode we have to put an error code on the stack
        ;**     if it's a GP fault or.  If not, we just chain on normally
PubLabel DIH_30StdChainOn
        cmp     si,OFFSET GP_Info       ;Is this a GP fault?
        jne     DIH_ChainOn             ;No, handle normally
        mov     ax,WORD PTR [si].ii_dwChain ;Get the LOWORD
        mov     dx,WORD PTR [si].ii_dwChain + 2 ;Get HIWORD
        mov     bx,ax                   ;Save the LOWORD
        or      ax,dx                   ;Is there a chain on address?
        jz      DIH_NoChainAddr         ;No, just restart the instruction
        mov     [bp + 0Ch],bx           ;Put on stack so we can retf to it
        mov     [bp + 0Eh],dx
        mov     WORD PTR [bp + 10h],0   ;Zero the error code
        pop     es                      ;Restore registers and clear stack
        pop     ds
        popa
        pop     bp
        pop     bx
        add     sp,8                    ;Clear extra words
        retf

PubLabel DIH_ChainOn
        mov     ax,WORD PTR [si].ii_dwChain ;Get the LOWORD
        mov     dx,WORD PTR [si].ii_dwChain + 2 ;Get HIWORD
        mov     bx,ax                   ;Save the LOWORD
        or      ax,dx                   ;Is there a chain on address?
        jz      DIH_NoChainAddr         ;No, just restart the instruction
        mov     [bp + 0Eh],bx           ;Put on stack so we can retf to it
        mov     [bp + 10h],dx
        pop     es                      ;Restore registers and clear stack
        pop     ds
        popa
        pop     bp
        pop     bx
        add     sp,10                   ;Clear extra words
        retf

        ;** No chain on address was recorded so just restart the instruction
PubLabel DIH_NoChainAddr
        pop     es
        pop     ds
        popa
        pop     bp
        pop     bx
        add     sp,14                   ;Clear all extra words
        iret                            ;  and restart instruction

        ;** Chain on a DPMI-style exception:
        ;**
        ;** The goal here is to make a fault frame that appears that DPMI
        ;**     has passed the next exception handler the interrupt.  We
        ;**     have only two important cases here:
        ;**             1) We have already told DPMI the int was finished.
        ;**             2) We have not told DPMI the int was finished and
        ;**                     have not switched off the fault handler stack
        ;**     We handle the cases differently:
        ;**     -If we have already told DPMI that the fault was handled,
        ;**      we have to make a new fault so that the next handler can see
        ;**      the frame.  This can be best accomplished by restarting the
        ;**      faulting instruction.  This will cause the same fault to
        ;**      happen and will make the same frame.
        ;**     -In the case of us still being on the fh stack, we have to
        ;**      rebuild the frame and chain on.
        ;**     The stack we're given looks like this:
        ;**              ------------
        ;**              |    ES    |  [BP - 14h]
        ;**              |    DS    |  [BP - 12h]
        ;**              |    DI    |  [BP - 10h]
        ;**              |    SI    |  [BP - 0Eh]
        ;**              |    BP    |  [BP - 0Ch]
        ;**              |    SP    |  [BP - 0Ah]
        ;**              |    BX    |  [BP - 08h]
        ;**              |    DX    |  [BP - 06h]
        ;**              |    CX    |  [BP - 04h]
        ;**              |    AX    |  [BP - 02h]
        ;**       BP---->|  Old BP  |  [BP + 00h]
        ;**              |    BX    |  [BP + 02h]
        ;**              |Our Ret IP|  [BP + 04h]
        ;**              |Our Ret CS|  [BP + 06h]
        ;**              |  Ret IP  |  [BP + 08h]
        ;**              |  Ret CS  |  [BP + 0Ah]
        ;**              |    AX    |  [BP + 0Ch]
        ;**              |Exception#|  [BP + 0Eh]
        ;**              |  Handle  |  [BP + 10h]
        ;**              |    IP    |  [BP + 12h]
        ;**              |    CS    |  [BP + 14h]
        ;**              |   Flags  |  [BP + 16h]
        ;**              |    SP    |  [BP + 18h]   ;Only here if on fh stack
        ;**              |    SS    |  [BP + 1Ah]
        ;**              |  Ret IP  |  [BP + 1Ch]   ;DPMI return address
        ;**              |  Ret CS  |  [BP + 1Eh]
        ;**              ------------
PubLabel DIH_EmulateDPMI
        mov     ax,[bp + 0Eh]           ;Get the exception number
        test    ax,BAD_STACK_FLAG       ;Still on fh stack?
        jnz     DIH_RebuildDPMIFrame    ;Yes, rebuild the frame

        ;** Rehook the exception so we're sure to get it first
        push    si                      ;Preserve handle
        mov     bx,ax                   ;Fault number in bx
        mov     wException,bx           ;Save as a static also
        mov     ax,0202h                ;Get exception handler - DPMI
        int     31h                     ;Call DPMI
        mov     WORD PTR lpOldHandler[0],dx ;Save the old exception handler
        mov     WORD PTR lpOldHandler[2],cx
        mov     ax,0203h                ;Set exception handler - DPMI
        mov     dx,OFFSET DIH_EmDPMIRestart
        mov     cx,cs                   ;Selector value of handler
        int     31h                     ;Call DPMI
        pop     si

        ;** Save the address of the next exception handler
        mov     ax,WORD PTR [si].ii_dwChain[0] ;Address to chain fault to
        mov     WORD PTR lpChainCSIP[0],ax
        mov     ax,WORD PTR [si].ii_dwChain[2]
        mov     WORD PTR lpChainCSIP[2],ax

        ;** Restart the instruction.  This will fault and jump to our
        ;**     newly-established handler at DIH_EmDPMIRestart
        pop     es
        pop     ds
        popa
        pop     bp
        pop     bx
        add     sp,14                   ;Clear all extra words
        iret                            ;  and restart instruction

        ;** Now we're on the fault handler stack with a DPMI frame.  Throw
        ;**     it to the next handler on the chain
PubLabel DIH_EmDPMIRestart
        sub     sp,4                    ;Enough room for a RETF frame
        push    bp
        mov     bp,sp
        pusha
        push    ds
        push    es
        mov     ax,_DATA                ;Point to TOOLHELP's DS
        mov     ds,ax

        ;** Restore the exception handler
        mov     ax,0203h                ;Set exception handler - DPMI
        mov     bx,wException           ;Get exception number
        mov     dx,WORD PTR lpOldHandler[0] ;Get the exception handler address
        mov     cx,WORD PTR lpOldHandler[2]
        int     31h                     ;Call DPMI

        ;** Put the chain address on the stack so we can return to it
        mov     ax,WORD PTR lpChainCSIP[0] ;Get chain address
        mov     [bp + 02h],ax
        mov     ax,WORD PTR lpChainCSIP[2]
        mov     [bp + 04h],ax

        ;** Restore registers and jump to the handler
        pop     es
        pop     ds
        popa
        pop     bp
        retf

        ;** Since we are already on the fault handler stack, there is no
        ;**     need to fault again.  All we have to do here is recreate the
        ;**     DPMI fault stack as if the fault had just occurred.  It would
        ;**     be nice to clear the exception and then make it fault again,
        ;**     but since we only get here in potentially stack-faulting
        ;**     conditions, we cannot do this.  We just build a reasonable
        ;**     facsimile of the frame and chain on.  This frame should
        ;**     look as follows when we're done:
        ;**              ------------
        ;**              |    ES    |  [BP - 14h]
        ;**              |    DS    |  [BP - 12h]
        ;**              |    DI    |  [BP - 10h]
        ;**              |    SI    |  [BP - 0Eh]
        ;**              |    BP    |  [BP - 0Ch]
        ;**              |    SP    |  [BP - 0Ah]
        ;**              |    BX    |  [BP - 08h]
        ;**              |    DX    |  [BP - 06h]
        ;**              |    CX    |  [BP - 04h]
        ;**              |    AX    |  [BP - 02h]
        ;**       BP---->|  Old BP  |  [BP + 00h]
        ;**              |    BX    |  [BP + 02h]
        ;**              |   Empty  |  [BP + 04h]
        ;**              |   Empty  |  [BP + 06h]
        ;**              |   Empty  |  [BP + 08h]
        ;**              |   Empty  |  [BP + 0Ah]
        ;**              | Chain IP |  [BP + 0Ch]
        ;**              | Chain CS |  [BP + 0Eh]
        ;**              |  Ret IP  |  [BP + 10h]
        ;**              |  Ret CS  |  [BP + 12h]
        ;**              |Error Code|  [BP + 14h]   ;Always return zero
        ;**              |    IP    |  [BP + 16h]
        ;**              |    CS    |  [BP + 18h]   ;Only here if on fh stack
        ;**              |   Flags  |  [BP + 1Ah]
        ;**              |    SP    |  [BP + 1Ch]   ;DPMI return address
        ;**              |    SS    |  [BP + 1Eh]
        ;**              ------------

PubLabel DIH_RebuildDPMIFrame
        mov     dx,[bp + 1Ch]           ;DPMI return CS:IP
        mov     cx,[bp + 1Eh]           ;  stored in CX:DX
        mov     ax,[bp + 1Ah]           ;Faulting SS
        mov     [bp + 1Eh],ax
        mov     ax,[bp + 18h]           ;Faulting SP
        mov     [bp + 1Ch],ax
        mov     ax,[bp + 16h]           ;Flags
        mov     [bp + 1Ah],ax
        mov     ax,[bp + 14h]           ;Faulting CS
        mov     [bp + 18h],ax
        mov     ax,[bp + 12h]           ;Faulting IP
        mov     [bp + 16h],ax
        xor     ax,ax                   ;Error code
        mov     [bp + 14h],ax
        mov     [bp + 12h],cx           ;DPMI ret CS
        mov     [bp + 10h],dx           ;DPMI ret IP
        mov     ax,WORD PTR [si].ii_dwChain[2] ;Address to chain fault to
        mov     [bp + 0Eh],ax
        mov     ax,WORD PTR [si].ii_dwChain[0]
        mov     [bp + 0Ch],ax
        pop     es
        pop     ds
        popa
        pop     bp
        pop     bx
        add     sp,8                    ;Clear all extra words
        retf

cEnd    NOGEN


;** Helper functions

;  InterruptInfo
;       Gets a pointer to the INT_INFO structure given the interrupt
;       number.  Accepts the int number in AX and returns the pointer in AX.
;       Preserves all other registers

cProc   InterruptInfo, <NEAR,PUBLIC>, <si,cx>
cBegin
        ;** Loop through all possible handlers
        mov     cx,MAX_INTERRUPT + 1    ;Number of ints to hook
        lea     si,IntInfo              ;Get the address of the array

        ;** Is this a match?
II_HandlerLoop:
        cmp     [si].ii_wNumber,ax      ;Match?
        jz      II_End                  ;Yes, return the pointer

        ;** Prepare for next in table
II_Continue:
        add     si,SIZE INT_INFO        ;Bump to next entry
        loop    II_HandlerLoop          ;Loop back
        xor     si,si                   ;Return NULL for not found

II_End:
        mov     ax,si                   ;Get return value
cEnd

sEnd

        END