summaryrefslogtreecommitdiffstats
path: root/private/mvdm/dpmi/ntintr32.asm
diff options
context:
space:
mode:
authorAdam <you@example.com>2020-05-17 05:51:50 +0200
committerAdam <you@example.com>2020-05-17 05:51:50 +0200
commite611b132f9b8abe35b362e5870b74bce94a1e58e (patch)
treea5781d2ec0e085eeca33cf350cf878f2efea6fe5 /private/mvdm/dpmi/ntintr32.asm
downloadNT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar
NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.gz
NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.bz2
NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.lz
NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.xz
NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.zst
NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.zip
Diffstat (limited to 'private/mvdm/dpmi/ntintr32.asm')
-rw-r--r--private/mvdm/dpmi/ntintr32.asm937
1 files changed, 937 insertions, 0 deletions
diff --git a/private/mvdm/dpmi/ntintr32.asm b/private/mvdm/dpmi/ntintr32.asm
new file mode 100644
index 000000000..cea70632c
--- /dev/null
+++ b/private/mvdm/dpmi/ntintr32.asm
@@ -0,0 +1,937 @@
+include intmac.inc
+ public rgw032Stack
+
+ dw 64 dup (?) ; DOSX Ring -> Ring 0 transition stack
+;
+; Interrupts in the range 0-1fh cause a ring transition and leave
+; an outer ring IRET frame right here.
+;
+Ring0_EH_DS dw ? ; place to put user DS
+Ring0_EH_AX dw ? ; place to put user AX
+Ring0_EH_BX dw ? ; place to put user BX
+Ring0_EH_CX dw ? ; place to put user CX
+Ring0_EH_BP dw ? ; place to put user BP
+Ring0_EH_PEC dw ? ; lsw of error code for 386 page fault
+ ; also near return to PMFaultEntryVector
+Ring0_EH_EC dw ? ; error code passed to EH
+Ring0_EH_IP dw ? ; interrupted code IP
+ dw ?
+Ring0_EH_CS dw ? ; interrupted code CS
+ dw ?
+Ring0_EH_Flags dw ? ; interrupted code flags
+ dw ?
+Ring0_EH_SP dw ? ; interrupted code SP
+ dw ?
+Ring0_EH_SS dw ? ; interrupted code SS
+ dw ?
+rgw032Stack label word
+
+; ------------------------------------------------------------------
+; PMFaultAnalyzer -- This routine is the entry point for
+; the protected mode fault/trap/exception handler. It tries
+; to distinguish between bona fide processor faults and
+; hardware/software interrupts which use the range of
+; interrupts that is reserved by Intel. If a fault is
+; detected, then format the stack for a DPMI fault handler,
+; then vector to the handler whose address is stored in
+; PMFaultVector. If it looks more like an interrupt, then
+; set up the stack for an interrupt handler, jump to the
+; handler whose address is stored in PMIntelVector.
+;
+; Input: none
+; Output: none
+
+ assume ds:NOTHING,es:NOTHING,ss:DGROUP
+ public PMFaultAnalyzer
+
+PMFaultAnalyzer proc near
+
+;
+; Make sure we are on the right stack. Else, something fishy is going on.
+; Note that stack faults won't do too well here.
+;
+ push ax
+ mov ax,ss
+ cmp ax,SEL_DXDATA or STD_RING
+ pop ax
+ je pmfa_stack_passed
+ jmp pmfa_bad_stack
+pmfa_stack_passed:
+;
+; Is the stack pointer pointing at a word error code (minus 2)?
+;
+ cmp sp,offset DGROUP:Ring0_EH_PEC
+ je pmfa_fault ; Yes, processor fault.
+
+;
+; Is it pointing to where it is supposed to be for a hardware or
+; software interrupt?
+;
+ cmp sp,offset DGROUP:Ring0_EH_EC
+ je pmfa_20
+ jmp pmfa_bad_stack
+pmfa_20:jmp pmfa_inspect
+
+pmfa_fault:
+;
+; Getting here, we have a known exception with a word error code of some
+; sort on the stack. Perform an outward ring transition, switch to the
+; client stack, then vector through the exception handler vector to the
+; appropriate handler.
+;
+ push bp
+ push cx
+ push bx
+ push ax
+ push ds
+ lea bp,Ring0_EH_SS
+ mov ax,word ptr [bp]
+ mov cx,selEHStack
+ cmp ax,cx
+ jne pmfa_stack_OK
+ mov bx,[bp-2]
+ jmp pmfa_copy_stack
+pmfa_stack_OK:
+ mov bx,npEHStackLimit
+pmfa_copy_stack:
+ mov ds,cx ; DS:BX = user SS:SP
+ mov cx,13
+ add bp,2 ; put both halves of ss
+pmfa_copy_stack_loop:
+ dec bx
+ dec bx
+ mov ax,word ptr [bp]
+ mov word ptr [bx],ax
+ dec bp
+ dec bp
+ loop pmfa_copy_stack_loop
+
+; DS:BX points to stack on entry to PMFaultReflector
+
+;
+; Build a far return frame on user stack, switch to user stack, and return
+;
+ lea bp,Ring0_EH_PEC
+ mov word ptr [bp+6],ds
+ mov word ptr [bp+4],bx
+
+ sub bx,2
+ mov word ptr ds:[bx],SEL_DXPMCODE OR STD_RING; push cs
+ sub bx,2
+ mov ds:[bx],offset DXPMCODE:PMFaultReflector; push ip
+ lea bp,Ring0_EH_PEC
+ mov ax,Ring0_EH_CX ; get BP value
+ sub bx,2
+ mov ds:[bx],ax ; push bp
+ mov Ring0_EH_PEC,bx ; sp for lss
+ mov bx,ds
+ mov Ring0_EH_EC,bx ; ss for lss
+ pop ds
+ pop ax
+ pop bx
+ pop cx
+.386p
+ lss sp,[bp] ; switch stack
+.286p
+ pop bp
+ retf
+
+pmfa_inspect:
+;
+; Stack is set up as for an interrupt or exception without error code.
+; Adjust the stack pointer and put an error code of zero. Then try to
+; determine whether we have an exception or an interrupt. First test
+; is the interrupt number.
+;
+ push Ring0_EH_EC
+ mov Ring0_EH_EC,0
+ cmp Ring0_EH_PEC,offset PMFaultEntryVector + ((7 + 1) * 3)
+ ja pfma_50
+ push Ring0_EH_PEC
+ mov Ring0_EH_PEC,0
+ jmp pmfa_fault ; Yes, definitely a fault.
+pfma_50:
+;
+; At this point, all valid exceptions have been eliminated except for
+; exception 9, coprocessor segment overrun, and exception 16, coprocessor
+; error.
+;
+
+; **********************************************
+; * Your code to detect these exceptions here. *
+; **********************************************
+
+ push bp
+ push cx
+ push bx
+ push ax
+ push ds ; SP -> Ring0_EH_DS
+;
+; Point to the user's stack.
+;
+ lea bp,Ring0_EH_SS
+ mov cx,[bp]
+ mov ds,cx
+ mov bx,[bp-2] ; DS:[BX] -> user stack
+;
+; Copy the IRET frame to the user stack.
+;
+ lea bp,Ring0_EH_Flags
+ mov cx,6
+pmfa_copy_IRET:
+ mov ax,[bp]
+ dec bx
+ dec bx
+ mov [bx],ax
+ dec bp
+ dec bp
+ loop pmfa_copy_IRET
+;
+; Point BP at vector entry for this (reserved) interrupt.
+;
+ mov ax,Ring0_EH_PEC ; fetch near return address
+ sub ax,offset DXPMCODE:PMFaultEntryVector+3
+ mov cl,3
+ div cl ; AX = interrupt number
+ shl ax,2 ; AX = vector entry offset
+ lea bp,PMIntelVector
+ add bp,ax ; BP -> interrupt handler address
+ mov ax,[bp] ; AX = IP of handler
+ mov cx,[bp+2] ; CX = CS of handler
+;
+; Build a far return frame on user stack, switch to user stack, and return
+;
+
+ sub bx,2
+ mov ds:[bx],cx ; push cs
+ sub bx,2
+ mov ds:[bx],ax ; push ip
+ lea bp,Ring0_EH_PEC
+ mov ax,Ring0_EH_BP
+ sub bx,2
+ mov ds:[bx],ax ; push bp
+ mov Ring0_EH_PEC,bx ; sp for lss
+ mov bx,ds
+ mov Ring0_EH_EC,bx ; ss for lss
+ pop ds
+ pop ax
+ pop bx
+ pop cx
+.386p
+ lss sp,[bp] ; switch stack
+.286p
+ pop bp
+ retf ; Out of here.
+
+pmfa_bad_stack:
+
+if DEBUG
+ mov ax,ss
+ mov bx,sp
+ Trace_Out "Fault Handler Aborting with SS:SP = #AX:#BX"
+ pop ax
+ sub ax, (offset DXPMCODE:PMFaultEntryVector) + 3
+ mov bx,3
+ div bl
+ Trace_Out "Fault Number #AX"
+ pop ax
+ pop bx
+ pop cx
+ pop dx
+ Trace_Out "First four stack words: #AX #BX #CX #DX."
+endif
+ push selDgroupPM
+ push offset DGROUP:rgwStack
+ rpushf
+ push SEL_DXPMCODE or STD_RING
+ push offset DXPMCODE:pmfr_death
+ riret
+pmfr_death:
+ mov ax,cs
+ mov ds,ax
+ mov dx,offset szRing0FaultMessage
+ pmdossvc 09h
+ jmp PMAbort
+
+PMFaultAnalyzer endp
+
+FR_Stack Struc
+ FR_BP dd ?
+ FR_AX dd ?
+ FR_BX dd ?
+ FR_DS dw ?
+ FR_ENTRY dd ? ; SS:[SP] points here on entry
+ ; to PMReservedReflector
+ FR_Toss dd ? ; DPMI return IP
+ FR_Ret_IP dd ? ; actual fault handler gets put
+ FR_Ret_CS dd ? ; here to return to
+ FR_IP dd ?
+ FR_CS dd ?
+ FR_FL dd ?
+ FR_SP dd ?
+ FR_SS dd ?
+FR_Stack Ends
+;
+; Alternate names so the structure above makes more sense to
+; PMFaultReflector.
+;
+FR_Handler_IP equ FR_DS
+FR_Handler_CS equ FR_ENTRY
+FR_Handler_Ret_IP equ FR_Toss
+FR_Handler_Ret_CS equ FR_Ret_IP
+FR_Handler_Entry equ FR_Handler_Ret_CS
+FR_EC equ FR_Ret_CS
+
+; ------------------------------------------------------------------
+; PMFaultReflector -- Dispatch a fault to a fault handler installed
+; in PMFaultVector. When the fault handler returns, return
+; to the faulting code, using the addresses placed on the
+; DPMI fault handler stack by the last called fault handler.
+;
+; Input:
+; Entry is by a NEAR call, with an IP within the range
+; of PMFaultEntryVector on the stack. The stack has been
+; set up for use by a DPMI fault handler.
+;
+; Output:
+; Controlled by fault handler.
+;
+; Uses:
+; Controlled by fault handler.
+;
+; Notes:
+; Fault handlers are called on a static stack. This routine
+; is NOT REENTRANT.
+;
+ public PMFaultReflector
+ public PMFaultReflectorIRET
+PMFaultReflector proc near
+ assume ss:nothing,ds:nothing,es:nothing
+
+ sub sp,6
+ push bx
+ push eax
+ push bp
+ mov bp,sp
+ push ds
+ mov ax,SEL_DXDATA or STD_RING
+ mov ds,ax
+ assume ds:dgroup
+ mov ax,[bp.FR_Handler_Entry]
+ sub ax,offset DXPMCODE:PMFaultEntryVector+3
+ mov bl,3
+ div bl ; AX = interrupt number
+ shl ax,3 ; AX = offset of fault handler
+
+ lea bx,PMFaultVector
+ add bx,ax ; SS:[BX] -> fault vector entry
+ mov eax,word ptr ds:[bx]
+ mov [bp.FR_Handler_IP],eax
+ mov ax,word ptr ds:[bx+2]
+ mov [bp.FR_Handler_CS],ax
+
+ lea ax,pmfr_cleanup
+ movzx eax,ax
+ mov [bp.FR_Handler_Ret_IP],eax
+ push cs
+ pop [bp.FR_Handler_Ret_CS]
+
+ pop ds
+ assume ds:nothing
+ pop bp
+ pop ax
+ pop bx
+ db 066h,0CBh ; This calls the fault handler.
+
+PMFaultReflectorIRETCall:
+ dd (SEL_RZIRET or STD_RING) shl 10h
+
+pmfr_cleanup:
+;
+; Unwind the fault handler stack. Return to the faulting code.
+; This works by calling a Ring 0 procedure to do the actual IRET.
+; If we do it that way, we can return to the faulting code without
+; actually touching the faulting code's stack.
+;
+PMFaultReflectorIRET:
+
+ ; BUGBUG Daveh using user stack this way is less robust!!!
+
+.386p
+ add sp,4 ; pop error code
+ push bp
+ mov bp,sp
+ push ebx
+ push ds
+ push eax
+ mov ax,[bp + 18]
+ mov ds,ax
+ mov ebx,[bp + 14] ; ds:bx -> user stack
+ sub ebx,4
+ mov eax,[bp + 10]
+ mov ds:[ebx],eax ; push flags
+ sub ebx,4
+ mov eax,[bp + 4]
+ mov ds:[ebx],eax ; push cs
+ sub ebx,4
+ mov eax,[bp + 2]
+ mov ds:[ebx],eax ; push ip
+ sub ebx,2
+ mov ax,[bp]
+ mov ds:[ebx],ax ; push bp
+ mov [bp + 8],bx
+ pop eax
+ pop ds
+ pop ebx
+ pop bp
+
+ add sp,6 ; point to ss:sp
+ mov bp,sp
+
+ lss esp,[bp]
+.286p
+ pop bp ; restore bp
+ riretd
+endif
+PMFaultReflector endp
+;
+; -------------------------------------------------------
+; PMReservedReflector -- This routine is for reflecting
+; exceptions to a protected mode interrupt handler.
+; The default for exceptions 1, 2, and 3 is to have
+; a near call to this routine placed in the PMFaultVector.
+;
+; This routine strips off the fault handler stack set
+; up by PMFaultAnalyzer, switches to the stack pointed
+; to by the pushed SS and SP values, sets up an IRET
+; frame for use by PMIntrReflector, and jumps to
+; PMIntrReflector. Eventual return is via an IRET
+; from PMIntrReflector.
+;
+; Input:
+; Entry is by a NEAR call, with an IP within the range
+; of PMReservedEntryVector on the stack. The stack has been
+; set up for use by a DPMI fault handler.
+;
+; Output:
+; Switch to stack registers set up by any previous fault
+; handler, jump to PMIntrReflector with an IRET frame set up
+; for direct return to the interrupted code.
+;
+; Errors: none
+;
+; Uses: Modifies SS, SP. Does not return to caller.
+;
+
+ assume ds:NOTHING,es:NOTHING,ss:NOTHING
+ public PMReservedReflector
+PMReservedReflector:
+
+ push ds
+ push ebx
+ push eax
+ push ebp
+ mov bp,sp
+;
+; BP now points to a stack frame described by the structure
+; above. This will be copied to a stack frame on the stack pointed to
+; by FR_SS:FR_SS. In most cases, the destination stack is actually
+; the same as the present stack, offset by four bytes. The following
+; block of code is therefore very likely an overlapping copy. Think
+; carefully before modifying how it works.
+;
+
+ mov bx,[bp.FR_SS]
+ mov ds,bx
+ mov ebx,[bp.FR_SP] ; DS:[BX] -> interrupted code's stack
+ sub bx, (size FR_Stack) - 8 ; (not copying SP or SS)
+ ; DS:[BX] -> place to copy our stack frame
+
+ mov eax,[bp.FR_FL] ; Push user IRET frame onto the destination
+ mov [ebx.FR_FL],eax ; stack.
+ mov eax,[bp.FR_CS]
+ mov [ebx.FR_CS],eax
+ mov eax,[bp.FR_IP]
+ mov [ebx.FR_IP],eax
+
+ mov eax,[bp.FR_ENTRY] ; Copy our caller's near return.
+ mov [ebx.FR_ENTRY],eax
+
+ mov ax,[bp.FR_DS] ; Copy saved registers.
+ mov [ebx.FR_DS],ax
+ mov eax,[bp.FR_BX]
+ mov [ebx.FR_BX],eax
+ mov eax,[bp.FR_AX]
+ mov [ebx.FR_AX],eax
+ mov eax,[bp.FR_BP]
+ mov [ebx.FR_BP],eax
+
+ mov ax,ds ; Switch to user stack.
+ mov ss,ax
+ mov esp,ebx
+ mov ebp,esp
+
+ mov ax,[ebp.FR_ENTRY] ; AX = offset of caller
+ sub ax,offset DXPMCODE:PMReservedEntryVector + 3
+ mov bl,3
+ div bl ; AX = interrupt number
+ shl ax,2 ; AX = offset into PMIntelVector
+ mov ds,SelDgroupPM
+ assume ds:DGROUP
+ lea bx,PMIntelVector
+ add bx,ax ; DS:[BX] -> interrupt handler
+
+ mov eax,[bx] ; Place vector entry just below
+ mov [ebp.FR_Ret_IP],eax ; IRET frame.
+ mov eax,[bx+4]
+ mov [ebp.FR_Ret_CS],eax
+
+ lea esp,[ebp.FR_BP] ; Point to saved registers.
+ pop ebp ; Pop 'em.
+ pop eax
+ pop ebx
+ pop ds
+ add esp,4 ; Fix up stack.
+
+ db 066h,0CBh ; jump to interrupt handler via far return
+
+
+DXPMCODE ends
+
+; -------------------------------------------------------
+ subttl Real Mode Interrupt Reflector
+ page
+; -------------------------------------------------------
+; REAL MODE INTERRUPT REFLECTOR
+; -------------------------------------------------------
+
+DXCODE segment
+ assume cs:DXCODE
+; -------------------------------------------------------
+; RMIntrEntryVector -- This table contains a vector of
+; near jump instructions to the real mode interrupt
+; reflector. Real mode interrupts that have been hooked
+; by the protected mode application have their vector
+; set to entry the real mode reflector through this table.
+
+ public RMIntrEntryVector
+
+RMIntrEntryVector:
+
+ rept 256
+ call RMIntrReflector
+ endm
+
+; -------------------------------------------------------
+; RMIntrReflector -- This routine is the entry point for
+; the real mode interrupt reflector. This routine
+; is entered when an interrupt occurs (either software
+; or hardware) that has been hooked by the protected mode
+; application. It switches the processor to protected mode
+; and transfers control to the appropriate interrupt
+; service routine for the interrupt. After the interrupt
+; service routine completes, it switches back to real
+; mode and returns control to the originally interrupted
+; real mode code.
+; Entry to this routine comes from the RMIntrEntryVector,
+; which contains a vector of near call instructions, which
+; all call here. The interrupt number is determined from
+; the return address of the near call from the interrupt
+; entry vector.
+; The address of the protected mode interrupt service routine
+; to execute is determined from the protected mode interrupt
+; descriptor tabel and the interrupt number.
+;
+; Input: none
+; Output: none
+; Errors: none
+; Uses: The segment registers are explicitly preserved by
+; this routine. Other registers are as preserved or
+; modified by the interrutp service routine.
+
+ assume ds:NOTHING,es:NOTHING,ss:NOTHING
+ public RMIntrReflector
+
+RMIntrReflector:
+;
+; On entry, the stack layout is:
+; [6] FLAGS - "
+; [4] CS - "
+; [2] IP - from original interrupt
+; [0] IP - from interrupt entry vector call
+;
+ FCLI
+ cld
+ push ds
+IFDEF ROM
+ SetRMDataSeg
+ELSE
+ mov ds,selDgroup
+ENDIF
+ assume ds:DGROUP
+if DEBUG
+;
+; Are we on a DOSX interrupt reflector stack?
+;
+ push ax
+ push cx
+ mov ax,ss
+ mov cx,ds
+ cmp ax,cx
+ pop cx
+ jne @F
+
+ cmp sp,offset bReflStack
+ jb @F
+ cmp sp,offset pbReflStack
+ jnb @F
+;
+; If so, have we overflowed a stacklet?
+;
+ mov ax,pbReflStack
+ cmp sp,ax
+ ja @F
+ add ax,CB_STKFRAME
+ cmp sp,ax
+ jb @F
+ pop ax
+ Real_Debug_Out "DOSX:RMIntrReflector--Reflector stack overflow."
+ push ax
+@@:
+ pop ax
+endif ;DEBUG
+ mov regUserAX,ax ;save user AX for later
+ push bp ;stack -> BP DS IP IP CS FL
+ mov bp,sp ; [0] [2] [4] [6] [8] [A]
+ mov ax,[bp+0Ah] ;get the interrupted routine's flags
+ and ax,NOT 4100h ;clear the trace flag in case we got
+ ; an interrupt on an instruction about
+ ; to be single stepped
+ mov regUserFL,ax ;and save for later
+ mov ax,es
+ xchg ax,[bp+4] ;save ES and get entry vector address
+ pop bp
+
+; Some software (like older versions of Smartdrv.sys) may enable A20 on
+; their own, and get very 'suprised' to find it turned off by our PM->RM
+; mode switch. If they used Himem.sys, this wouldn't be necessary, but...
+
+if VCPI
+ cmp fVCPI,0
+ jnz @f
+endif
+ push ax ;get/save current A20 state on stack
+ push bx
+ xmssvc 7
+ mov regUserSP,ax ;use regUserSP as a temp var
+ pop bx
+ pop ax
+@@:
+ push regUserSP
+
+; The state that we want to save on the user's stack has been set up.
+; Convert the entry vector return address into an interrupt number.
+
+ sub ax,offset RMIntrEntryVector+3
+ push cx
+ mov cl,3
+ div cl
+ pop cx
+
+if DEBUG
+ mov PMIntNo,ax
+endif
+
+; Allocate a new stack frame, and then switch to the reflector stack
+; frame.
+
+ mov regUserSP,sp ;save entry stack pointer so we can
+ mov regUSerSS,ss ; switch to our own stack
+IFDEF ROM
+ push ds
+ pop ss
+ELSE
+ mov ss,selDgroup ;switch to the reflector stack frame
+ENDIF
+ mov sp,pbReflStack
+ push pbReflStack ;save stack frame ptr on stack
+ sub pbReflStack,CB_STKFRAME ;adjust pointer to next stack frame
+
+; We are now running on our own stack, so we can switch into protected mode.
+
+ push ax ;save interrupt vector table offset
+ SwitchToProtectedMode
+ pop ax
+
+if DEBUG ;--------------------------------------------------------
+
+ push 0DEADh ;debugging id & interrupt number
+ push PMIntNo
+
+ cmp fTraceReflect,0
+ jz @f
+ push ax
+ mov ax,PMIntNo
+ Trace_Out "(rp#AL)",x
+ pop ax
+@@:
+
+; Perform a too-late-to-save-us-now-but-we-want-to-know check on the
+; reflector stack.
+
+ cmp StackGuard,1022h
+ jz @f
+ Debug_Out "DOSX:RMIntrReflector--Global reflector stack overflow."
+@@:
+endif ;DEBUG ---------------------------------------------------------
+
+; Build an IRET frame on the stack so that the protected mode interrupt service
+; routine will return to us when it is finished.
+
+ push regUserSS ;save user stack address on our own stack
+ push regUserSP ; frame so we can restore it later
+ push ds
+ push regUserFL
+ push cs
+ push offset rmrf50
+
+; Build an IRET frame on the stack to use to transfer control to the
+; protected mode ISR
+
+ and byte ptr regUserFL+1,not 02h ;use entry flags less the
+ push 0 ; high half esp
+ push regUserFL ; interrupt flag (IF)
+
+ xchg bx,ax ;interrupt vector offset to BX, preserve BX
+ cmp bx,CRESERVED ;Interrupt in reserved range?
+ jc rmrf_reserved
+ shl bx,3
+ mov es,selIDT
+ jmp rmrf_setISR
+rmrf_reserved:
+ shl bx,2
+ mov es,SelDgroupPM
+ add bx,offset DGROUP:PMIntelVector
+rmrf_setISR:
+ push dword ptr es:[bx+4] ;push segment of isr
+ push dword ptr es:[bx] ;push offset of isr
+ xchg bx,ax
+ mov ax,regUserAX ;restore entry value of AX
+ push ds
+ pop es
+
+; At this point the interrupt reflector stack looks like this:
+;
+; [18] previous stack frame pointer
+; [16] stack segment of original stack
+; [14] stack pointer of original stack
+; [12] protected mode dos extender data segment
+; [10] dos extender flags
+; [8] segment of return address back to interupt reflector
+; [6] offset of return address back to interrupt reflector
+; [4] user flags as on entry from original interrupt
+; [2] segment of protected mode ISR
+; [0] offset of protected mode ISR
+;
+; Execute the protected mode interrupt service routine
+
+ iretd
+
+; The protected mode ISR will return here after it is finsished.
+
+rmrf50: pop ds
+ pushf ;save flags as returned by PM Int routine
+
+ FCLI ;We have to clear interrupts here, because
+ cld ; the interrupt routine may have returned
+ ; with interrupts on and our code that uses
+ ; static variables must be protected. We
+ ; turn them off after to pushf instruction so
+ ; that we can preserve the state of the
+ ; interrupt flag as returned by the ISR.
+ mov regUserAX,ax
+ pop ax
+ pop regUserSP
+ pop regUserSS
+
+if DEBUG
+ add sp,4 ;'pop' off debugging info
+endif
+
+ pop pbReflStack ;deallocate stack frame(s)
+
+; Switch back to real mode.
+
+ push ax ;preserve AX
+ SwitchToRealMode
+ pop ax
+
+; Switch back to the original stack.
+
+ mov ss,regUserSS
+ mov sp,regUserSP
+
+; Make sure the A20 line matches whatever state it was when the int occured.
+; This is for the benefit of any software that diddles A20 without using
+; an XMS driver
+
+ pop regUserSP ;A20 state at time of interrupt to temp var
+if VCPI
+ cmp fVCPI,0
+ jnz rmrf75
+endif
+ push ax ;save current ax
+ mov ax,regUserSP ;ax = A20 state at time of interrupt
+ or ax,ax ;if it was off, don't sweat it
+ jz rmrf70
+ push bx ;save bx (XMS calls destroy bl)
+ push ax
+ xmssvc 7 ;ax = current A20 state
+ pop bx ;bx = old A20 state
+ cmp ax,bx ;if A20 is still on, don't need to diddle
+ jz @f
+ xmssvc 5 ;force A20 back on
+ inc A20EnableCount ; and remember that we did this
+if DEBUG
+ or fA20,04h
+endif
+@@:
+ pop bx
+rmrf70:
+ pop ax
+rmrf75:
+
+; Put the flags returned by the real mode interrupt routine back into
+; the caller's stack so that they will be returned properly.
+
+ push bp ;stack -> BP DS ES IP CS FL
+ mov bp,sp ; [0] [2] [4] [6] [8] [10]
+ and [bp+10],0300h ;clear all but the interrupt and trace flags
+ ; in the caller's original flags
+ or [bp+10],ax ;combine in the flags returned by the
+ ; interrupt service routine. This will cause
+ ; us to return to the original routine with
+ ; interrupts on if they were on when the
+ ; interrupt occured, or if the ISR returned
+ ; with them on.
+ pop bp
+
+; And return to the original interrupted program.
+
+ mov ax,regUserAX
+ pop ds
+ pop es
+ iret
+
+DXCODE ends
+
+WowIntr3216 proc
+
+ FCLI
+ push ebp
+ mov ebp,esp
+ mov regUserAX,eax
+ mov regUserBX,ebx
+ mov regUserDS,ds
+ mov regUserES,es
+ mov regUserFlags,[ebp + 16]
+ mov ax,SEL_DXDATA OR STD_RING
+ mov ds,ax
+ assume ds:dgroup
+ mov ebx,esp
+ mov regUserSp,eax
+ mov ax,ss
+ mov regUserSs,ax
+ mov bx,pbReflStack ; get pointer to new frame
+ sub bpReflStack,CB_STKFRAME
+
+;
+; put user stack pointer on new stack
+;
+ sub bx,4
+ mov [bx],regUserSs
+ sub bx,4
+ mov [bx],dword ptr regUserSp
+
+ mov ax,[ebp + 4]
+
+;
+; switch to new stack
+;
+
+ push ds
+ pop ss
+ movzx esp,bx
+
+;
+; Save ss:esp for lss esp
+;
+ push regUserSs
+ push regUserSp
+;
+; Create an int frame
+;
+ pushf
+ push cs
+ push offset wi30
+
+;
+; Put handler address on stack
+;
+
+ shl ax,3
+ mov es,selDgroupPM
+ add bx,offset DGROUP:Intr16Vector
+ push [bx + 2]
+ push [bx]
+
+;
+; Restore Registers
+;
+ mov eax,regUserAx
+ mov ebx,regUserBX
+ mov es,regUserES
+ mov ds,regUserDS
+ assume ds:nothing
+;
+; call handler
+;
+ retf
+
+wi30:
+;
+; handler will return here
+;
+
+;
+; Switch stacks
+;
+ mov ebp,esp
+ lss esp,[ebp]
+ mov ebp,esp
+
+ push eax
+ push ds
+ mov ds,SEL_DXDATA OR STD_RING
+ assume ds:DGROUP
+
+;
+; Deallocate stack frame
+;
+ add pbReflStack,CB_STKFRAME
+ pop eax
+ pop ds
+
+;
+; Return flags from int handler
+;
+ pushf
+ pop [ebp + 16]
+
+;
+; Return to interrupted code
+;
+ pop ebp
+ riretd
+
+WowIntr3216 endp