NT4/private/mvdm/dpmi/486/dxintr.asm
2020-09-30 17:12:29 +02:00

3195 lines
99 KiB
NASM

PAGE ,132
TITLE DXINTR.ASM -- Dos Extender Interrupt Reflector
; Copyright (c) Microsoft Corporation 1988-1991. All Rights Reserved.
;****************************************************************
;* *
;* DXINTR.ASM - Dos Extender Interrupt Reflector *
;* *
;****************************************************************
;* *
;* Revision History: *
;* *
;* *
;* 09/13/90 earleh Fault handlers Ring 0 *
;* 09/06/90 earleh Fault handlers DPMI compliant *
;* PIC remapping no longer required *
;* 08/08/90 earleh DOSX and client privilege ring determined *
;* by equate in pmdefs.inc *
;* 05/09/90 jimmat Started VCPI changes. *
;* 04/02/90 jimmat Added PM Int 70h handler. *
;* 01/08/90 jimmat Don't allow nested PS/2 mouse interrupts *
;* (later removed!) *
;* 09/15/89 jimmat Support for 'Classic' HP Vectras which *
;* have 3 8259 interrupt controllers *
;* 07/28/89 jimmat Save A20 state when reflecting an int to *
;* protected mode, removed Int 30h handler *
;* that did code patch-ups, point debugger *
;* to faulting instruction, not Int 3. *
;* 07/13/89 jimmat Improved termination due to faults when *
;* not running under a debugger--also ifdef'd *
;* out code to dynamically fixup code seg *
;* references on GP faults *
;* 06/05/89 jimmat Ints 0h-1Fh are now vectored through a 2nd *
;* table. This allows Wdeb386 interaction *
;* more like Windows/386. *
;* 05/23/89 jimmat Added wParam & lParam to interrupt frame. *
;* 05/07/89 jimmat Added XMScontrol function to map protected *
;* mode XMS requests to real mode driver. *
;* 05/02/89 jimmat 8259 interrupt mask saved around changing *
;* of hardware interrupt base *
;* 04/24/89 jimmat Added support for PS/2 Int 15h/C2h/07 Set *
;* Pointing Device Handler Address function *
;* 04/12/89 jimmat Added PMIntr24 routine to support PM *
;* Critical Error Handlers *
;* 03/15/89 jimmat Added INT 31h LDT/heap interface a la *
;* Windows/386 *
;* 03/14/89 jimmat Changes to run child in ring 1 with LDT *
;* 02/24/89 (GeneA): fixed problem in IntEntryVideo and *
;* IntExitVideo for processing function 10h subfunction *
;* for reading and writing the VGA palette. *
;* 02/22/89 (GeneA): added handlers for Int 10h, Int 15h, and *
;* Int 33h. Added support for more general mechanism for *
;* handling interrupts require special servicing and *
;* allowing nesting of these interrupts. Allocation and *
;* deallocation of stack frames is supported to allow *
;* nested paths through the interrupt reflection code to *
;* a depth of 8. *
;* There is still a problem that if an interrupt handler *
;* is using a static buffer to transfer data, another *
;* interrupt that uses the same static buffer could come *
;* in and trash it. Solving the problem in a completely *
;* general way would require having a buffer allocation *
;* deallocation scheme for doing the transfers between *
;* real mode memory and protected mode memory. *
;* 02/14/89 (GeneA): added code in TrapGP to print error msg *
;* and quit when running a non-debugging version. *
;* 02/10/89 (GeneA): changed Dos Extender from small model to *
;* medium model. Added function LoaderTrap to handle *
;* loader interrupts when the program contains overlays. *
;* 11/20/88 (GeneA): changed both RM and PM interrupt reflector*
;* routines to pass the flags returned by the ISR back to *
;* the originator of the interrupt, rather than returning *
;* the original flags. *
;* 10/28/88 (GeneA): created *
; 18-Dec-1992 sudeepb Changed cli/sti to faster FCLI/FSTI
;* *
;****************************************************************
.286p
.287
; -------------------------------------------------------
; INCLUDE FILE DEFINITIONS
; -------------------------------------------------------
.xlist
.sall
include segdefs.inc
include gendefs.inc
include pmdefs.inc
include interupt.inc
if VCPI
include dxvcpi.inc
endif
IFDEF ROM
include dxrom.inc
ENDIF
ifdef WOW_x86
include vdmtib.inc
endif
.list
include intmac.inc
include stackchk.inc
include bop.inc
include dpmi.inc
; -------------------------------------------------------
; GENERAL SYMBOL DEFINITIONS
; -------------------------------------------------------
DS_ForcedGO equ 0F003h ;Wdeb386 go with breakpoint command
DebOut_Int equ 41h ;Wdeb386 pMode interface Interrupt
; Offsets to fields in DOSX header for DOS allocated memory blocks
MemCookie equ 0
MemSelector equ 2
MemSegment equ 4
MemParas equ 6
MemSelCount equ 8
MemGoodCookie equ 'SF' ;memory header magic cookie value
; -------------------------------------------------------
; EXTERNAL SYMBOL DEFINITIONS
; -------------------------------------------------------
extrn PMIntr13:NEAR
extrn PmIntrDos:NEAR
extrn EnterRealMode:NEAR
extrn EnterProtectedMode:NEAR
extrn ParaToLinear:NEAR
externFP NSetSegmentDscr
extrn GetSegmentAddress:NEAR
extrn DupSegmentDscr:NEAR
extrn ParaToLDTSelector:NEAR
extrn FreeSelector:NEAR
extrn FreeSelectorBlock:NEAR
extrn AllocateSelector:NEAR
extrn AllocateSelectorBlock:NEAR
; -------------------------------------------------------
; DATA SEGMENT DEFINITIONS
; -------------------------------------------------------
DXDATA segment
extrn fDebug:BYTE
extrn selIDT:WORD
extrn pmusrss:WORD
extrn pmusrsp:WORD
ifdef NOT_NTVDM_NOT
extrn fHPVectra:BYTE
endif
extrn idCpuType:WORD
extrn npXfrBuf0:WORD
extrn npXfrBuf1:WORD
extrn rgbXfrBuf0:BYTE
extrn rgbXfrBuf1:BYTE
extrn selPSPChild:WORD
extrn fFaultAbort:BYTE
extrn lpfnXMSFunc:DWORD
extrn Int28Filter:WORD
extrn A20EnableCount:WORD
if DEBUG
extrn fTraceReflect:WORD
endif
if VCPI
extrn fVCPI:BYTE
endif
IFDEF ROM
extrn segDXData:WORD
extrn segDXCode:WORD
ENDIF
extrn DpmiFlags:WORD
;
; Variables used to store register values while mode switching.
public regUserSS, regUserSP, regUserFL, regUserAX, regUserDS
public regUserES
regUserSS dw ?
regUserSP dw ?
regUserCS dw ?
regUserIP dw ?
regUserFL dw ?
regUserAX dw ?
regUserDS dw ?
regUserES dw ?
pfnReturnAddr dw ?
Int28Count dw -1 ;Count of idle Int 28h's not reflected to RM
;
; Far pointer to the user's mouse callback function.
public lpfnUserMouseHandler
lpfnUserMouseHandler dd 0 ;Entry point to the users mouse handler
cbMouseState dw 0 ;size of mouse state buffer in bytes
; Far pointer to PS/2 Pointing device handler address
public lpfnUserPointingHandler
lpfnUserPointingHandler dd 0 ;Sel:Off to user's handler
public PMInt24Handler
PMInt24Handler dd 0 ;Address of protect mode Int 24h handler
dd 0 ; other half
align 2
if DEBUG
extrn StackGuard:WORD
endif
extrn pbReflStack:WORD
extrn bReflStack:WORD
;
; This buffer contains the original real mode interrupt vectors. The
; PM->RM interrupt reflector uses the addresses in this vector as the
; address to receive control when it reflects an interrupt to real mode.
public rglpfnRmISR
align 2
rglpfnRmISR dd 256 dup (?)
;
; This buffer contains the real mode hardware interrupt vectors.
; If a hardware interrupt is hooked in protected mode, a reflector
; is put into the IVT. If we were trying to reflect down the real
; mode chain, we call these handlers if the IVT contains a reflector
;
public RmHwIsr
RmHwIsr dd 256 dup(0)
;
; This flag indicates if the hardware interrupts have been remapped.
public fHardwareIntMoved
fHardwareIntMoved db 0
ifdef SEG_FIXUP ;-----------------------------------------------
errGP dw ? ;this variable is used to hold the error
endif ;SEG_FIXUP ------------------------------------------------
; PMFaultVector is a table of selector:offsets for routines to process
; protected mode processor faults/traps/exceptions. If we don't handle
; the exception as an exception, we vector it through PMReservedEntryVector.
FltRtn macro off
dw DXPMCODE:off
dw 0
dw SEL_DXPMCODE or STD_RING
dw 0
endm
public PMFaultVector
align 4
PMFaultVector label DWORD
FltRtn PMFaultEntryVector+5*0h ; int 0
FltRtn PMFaultEntryVector+5*1h ; int 1
FltRtn PMFaultEntryVector+5*2h ; int 2
FltRtn PMFaultEntryVector+5*3h ; int 3
FltRtn PMFaultEntryVector+5*4h ; int 4
FltRtn PMFaultEntryVector+5*5h ; int 5
FltRtn PMFaultEntryVector+5*6h ; int 6
FltRtn PMFaultEntryVector+5*7h ; int 7
FltRtn PMFaultEntryVector+5*8h ; int 8
FltRtn PMFaultEntryVector+5*9h ; int 9
FltRtn PMFaultEntryVector+5*0Ah ; int a
FltRtn PMFaultEntryVector+5*0Bh ; int b
FltRtn PMFaultEntryVector+5*0Ch ; int c
FltRtn PMFaultEntryVector+5*0Dh ; int d
FltRtn PMFaultEntryVector+5*0Eh ; int e
FltRtn PMFaultEntryVector+5*0Fh ; int f
FltRtn PMFaultEntryVector+5*10h ; int 10h
FltRtn PMFaultEntryVector+5*11h ; int 11h
FltRtn PMFaultEntryVector+5*12h ; int 12h
FltRtn PMFaultEntryVector+5*13h ; int 13h
FltRtn PMFaultEntryVector+5*14h ; int 14h
FltRtn PMFaultEntryVector+5*15h ; int 15h
FltRtn PMFaultEntryVector+5*16h ; int 16h
FltRtn PMFaultEntryVector+5*17h ; int 17h
FltRtn PMFaultEntryVector+5*18h ; int 18h
FltRtn PMFaultEntryVector+5*19h ; int 19h
FltRtn PMFaultEntryVector+5*1Ah ; int 1ah
FltRtn PMFaultEntryVector+5*1Bh ; int 1bh
FltRtn PMFaultEntryVector+5*1Ch ; int 1ch
FltRtn PMFaultEntryVector+5*1Dh ; int 1Dh
FltRtn PMFaultEntryVector+5*1Eh ; int 1Eh
FltRtn PMFaultEntryVector+5*1Fh ; int 1Fh
ifdef OVERLAY_SUPPORT ;-----------------------------------------------
offDestination dw ?
selDestination dw ?
endif ;---------------------------------------------------------------
; if DEBUG ;------------------------------------------------------------
; For MIPS we need to see where we are faulting - remove for final release
; LATER
; extrn fA20:BYTE
; extrn fTraceFault:WORD
public PMIntNo
PMIntNo dw 0
szRegDump db 'AX=#### BX=#### CX=#### DX=#### SI=#### DI=#### BP=####',0dh,0ah
db 'DS=#### ES=#### EC=#### CS=#### IP=#### SS=#### SP=####',0dh,0ah
db '$'
; endif ;DEBUG --------------------------------------------------------
extrn rgwStack:word
extrn npEHStackLimit:word
extrn npEHStacklet:word
extrn selEHStack:word
IFDEF WOW
public WowTransitionToUserMode
public Wow16BitHandlers
WowTransitionToUserMode dw offset DXPMCODE:Wow16TransitionToUserMode
Wow16BitHandlers dw 256 dup (0,0)
ENDIF
DXDATA ends
DXSTACK segment
public rgw0Stack, rgw2FStack
dw 64 dup (?) ; INT 2Fh handler stack
rgw2FStack label word
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
Ring0_EH_EIP dw ? ; high half eip
Ring0_EH_CS dw ? ; interrupted code CS
dw ? ; high half of cs
Ring0_EH_Flags dw ? ; interrupted code flags
Ring0_EH_EFlags dw ? ; high half of flags
Ring0_EH_SP dw ? ; interrupted code SP
Rin0_EH_ESP dw ? ; high half of esp
Ring0_EH_SS dw ? ; interrupted code SS
dw ? ; high half of ss
rgw0Stack label word
dw 64 dup (?) ; stack for switching to ring0
public ResetStack
ResetStack label word
ifdef WOW_x86
dw 64 dup (?) ; wow stack for initial int field
public rgwWowStack
rgwWowStack label word
endif
DXSTACK ends
; -------------------------------------------------------
; CODE SEGMENT VARIABLES
; -------------------------------------------------------
DXCODE segment
IFNDEF ROM
extrn selDgroup:WORD
ENDIF
DXCODE ends
DXPMCODE segment
extrn selDgroupPM:WORD
extrn segDXCodePM:WORD
extrn szFaultMessage:BYTE
extrn szRing0FaultMessage:BYTE
extrn RZCall:NEAR
IFNDEF ROM
extrn segDXDataPM:WORD
ENDIF
DXPMCODE ends
; -------------------------------------------------------
page
subttl Protected Mode Interrupt Reflector
; -------------------------------------------------------
; PROTECTED MODE INTERRUPT REFLECTOR
; -------------------------------------------------------
DXPMCODE segment
assume cs:DXPMCODE
; -------------------------------------------------------
; PMIntrEntryVector -- This table contains a vector of
; near jump instructions to the protected mode interrupt
; reflector. The protected mode interrupt descriptor
; table is initialized so that all interrupts jump to
; locations in this table, which transfers control to
; the interrupt reflection code for reflecting the
; interrupt to real mode.
public PMIntrEntryVector
PMIntrEntryVector:
rept 256
call PMIntrReflector
endm
StartFaultBopTable macro
?fault = 0
endm
FaultBop macro
DPMIBOP DpmiUnhandledException
db ?fault
?fault = ?fault+1
endm
public PMFaultEntryVector
; -------------------------------------------------------
; PMFaultEntryVector -- This table contains a vector of
; near jump instructions to the protected mode fault
; analyzer.
;
PMFaultEntryVector:
StartFaultBopTable
rept 32
FaultBop
endm
assume ds:nothing,es:nothing,ss:nothing
public PMFaultHandlerIRET
PMFaultHandlerIRET:
DPMIBOP FaultHandlerIret
public PMFaultHandlerIRETD
PMFaultHandlerIRETD:
DPMIBOP FaultHandlerIretd
public PMIntHandlerIRET
PMIntHandlerIRET:
DPMIBOP IntHandlerIret
public PMIntHandlerIRETD
PMIntHandlerIRETD:
DPMIBOP IntHandlerIretd
public PMDosxIret
PMDosxIret:
iret
public PMDosxIretd
PMDosxIretd:
db 66h
iret
;
; -------------------------------------------------------
; PMIntrReflector -- This routine is the entry point for
; the protected mode interrupt reflector. This routine
; is entered when an interrupt occurs (either software
; or hardware). It switches the processor to real mode
; and transfers control to the appropriate interrupt
; service routine for the interrupt. After the interrupt
; service routine completes, it switches back to protected
; mode and returns control to the originally interrupted
; protected mode code.
; Entry to this routine comes from the PMIntrEntryVector,
; 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 real mode interrupt service routine to
; execute is determined from the real mode interrupt vector
; table 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 PMIntrReflector
PMIntrReflector:
;
; 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
mov ds,selDgroupPM
assume ds:DGROUP
mov regUserAX,ax ;save user AX for later
if DEBUG
;
; Are we on a DOSX interrupt reflector stack?
;
mov ax,ss
cmp ax,selDgroupPM
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
mov ax,regUserAX
Debug_Out "DOSX:PMIntrReflector--Reflector stack overflow."
@@:
endif; DEBUG
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
;stack -> DS ES IP CS FL
; [0] [2] [4] [6] [8]
; 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 PMIntrEntryVector+3
push cx
mov cl,3
div cl
pop cx
;if DEBUG ; debugbug
mov PMIntNo,ax
;endif
DEBUG_TRACE DBGTR_ENTRY, ax, 0, 1000h
shl ax,2 ;turn interrupt number into interrupt
; table offset
; 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
ASSERT_REFLSTK_OK
mov ss,selDgroupPM ;switch to the reflector stack frame
mov sp,pbReflStack
FIX_STACK
push pbReflStack ;save stack frame ptr on stack
sub pbReflStack,CB_STKFRAME ;adjust pointer to next stack frame
if DEBUG ;--------------------------------------------------------
push 0ABCDh ;a debugging marker & interrupt value
push PMIntNo
cmp fTraceReflect,0
jz @f
push ax
mov ax,PMIntNo
Trace_Out "[pr#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:PMIntrReflector--Global reflector stack overflow."
@@:
endif ;DEBUG ---------------------------------------------------------
; We are now running on our own stack, so we can switch into real mode.
push ax ;save interrupt vector table offset
SwitchToRealMode
assume es:nothing
xor ax,ax
mov es,ax
pop ax
; Build an IRET frame on the stack so that the real 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 pmrf50
; Build an IRET frame on the stack to use to transfer control to the
; real mode interrupt routine.
xchg bx,ax ;interrupt vector offset to BX, preserve BX
and byte ptr regUserFL+1,not 02h ;use entry flags less
push regUserFL ; the interrupt flag (IF)
push ax
mov ax,word ptr RmHwIsr[bx]
or ax,word ptr RmHwIsr[bx + 2]
je pmrf20
;
; Don't reflect to the reflector that will reflect back to pmode
;
pop ax
push word ptr RmHwIsr[bx+2]
push word ptr RmHwIsr[bx]
jmp pmrf30
pmrf20: pop ax
push word ptr es:[bx+2] ;push segment of isr
push word ptr es:[bx] ;push offset of isr
pmrf30: xchg bx,ax
mov ax,regUserAX ;restore entry value of AX
;
; 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] real 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 real mode ISR
; [0] offset of real mode ISR
; Execute the real mode interrupt service routine
iret
; The real mode ISR will return here after it is finsished.
pmrf50: pop ds
pushf
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
CHECK_STACK
ASSERT_REFLSTK_OK
pop pbReflStack ;deallocate stack frame(s)--it used to be that
; we'd simply add CB_STKFRAME to pbReflStack
; to deallocate a frame. But we found a TSR
; that would pop up on an Int 28h and iret
; on the Int 28h frame from an Int 8h! This
; left some stack allocated, and soon resulted
; in running out of frames. Keeping the frame
; pointer on the stack allows us to pop
; multiple frames at once.
ASSERT_REFLSTK_OK
; Switch back to protected mode.
push ax ;preserve AX
SwitchToProtectedMode
pop ax
DEBUG_TRACE DBGTR_EXIT, 0, 0, 1000h
; Switch back to the original stack.
mov ss,regUserSS
mov sp,regUserSP
; 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
riret
; -------------------------------------------------------
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,EndRmIntrEntry
RMIntrEntryVector:
rept 256
call RMIntrReflector
endm
EndRMIntrEntry:
; -------------------------------------------------------
; 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 ; debugbug
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
ASSERT_REFLSTK_OK
mov ss,selDgroup ;switch to the reflector stack frame
ENDIF
mov sp,pbReflStack
FIX_STACK
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
test DpmiFlags,DPMI_32BIT
jnz rmrf_32
push regUserFL
push cs
push offset rmrf50
rmrf_hwint_cont:
; 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 regUserFL ; interrupt flag (IF)
xchg bx,ax ;interrupt vector offset to BX, preserve BX
shl bx,3
mov es,selIDT
push word ptr es:[bx+2] ;push segment of isr
rmrf_setISROff:
push word 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
iret
.386p
rmrf_32:
pushfd
push ax
mov ax,regUserFL
mov word ptr [esp + 2],ax
pop ax
sub esp,2
push cs
push dword ptr (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
pushfd
push ax
mov ax,regUserFL ; interrupt flag (IF)
mov word ptr [esp + 2],ax
pop ax
xchg bx,ax ;interrupt vector offset to BX, preserve BX
shl bx,3
mov es,selIDT
rmrf_32setISR:
; bugbug this is not correct. For vectors above 32, it will
; grab the segment from the wrong part of the IDT.
push 0 ;hiword of segment
push word ptr es:[bx+2] ;segment
push word ptr es:[bx+6] ;hiword of offset
push word ptr es:[bx] ;loword of offset
xchg bx,ax
mov ax,regUserAX ;restore entry value of AX
push ds
pop es
iretd
.286p
; 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
ASSERT_REFLSTK_OK
CHECK_STACK
pop pbReflStack ;deallocate stack frame(s)
ASSERT_REFLSTK_OK
; 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
; -------------------------------------------------------
subttl INT 24h Critical Error Mapper
page
; -------------------------------------------------------
; DOS CRITICAL ERROR MAPPER
; -------------------------------------------------------
DXCODE segment
; -------------------------------------------------------
; RMDefaultInt24Handler -- Default action for a DOS critical
; error is to fail the call.
;
public RMDefaultInt24Handler
RMDefaultInt24Handler proc far
mov al,3
iret
RMDefaultInt24Handler endp
DXCODE ends
DXCODE segment
assume cs:DXCODE
; -------------------------------------------------------
; RMIntr24 -- This routine is a real-mode hook that traps
; DOS critical errors, and maps them to protect mode
; handlers. To make the critical error realistic to
; the application, we switch to the applications
; stack and copy the critical error stack frame to
; there.
; On entry, the stack layout is:
;
; [0] [2] [4] [6] [8] [10] [12] [14] [16] [18] [20] [22] [24] [26] [28]
; IP CS FL AX BX CX DX SI DI BP DS ES IP CS FL
;
; |------------| |------------------------------------------| |------------|
;
; IRET TO DOS REGS AT TIME OF INT 24H IRET TO APP
;
public RMIntr24
RMIntr24 proc far
FCLI
cld
push es
push ds
IFDEF ROM
SetRMDataSeg
ELSE
mov ds,selDgroup ;stack -> DS ES IP CS FL ...
ENDIF
assume ds:DGROUP ; [0] [2] [4] [6] [8] ...
; We need a temporary stack to do real->protect mode switching, etc.
; Allocate and use an interrupt frame for that.
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
ASSERT_REFLSTK_OK
mov ss,selDgroup ;switch to the reflector stack frame
ENDIF
mov sp,pbReflStack
sub pbReflStack,CB_STKFRAME ;adjust pointer to next stack frame
FIX_STACK
push ax ;save ax, switch to protected mode
SwitchToProtectedMode ; need to be on our stack
pop ax
; Now switch to the applications stack frame. We assume that the dos function
; generating the critical error came from a protected mode app and was passed
; through PMIntrDos, who saved the app's current stack in regusrss:regusrsp.
mov ss,pmusrss ;switch (back) to app's stack
.386p
movzx esp,word ptr pmusrsp
.286p
push regUserSS ;save prior stack address on app's stack
push regUserSP ; frame so we can restore it later
; Copy critical error stack info to application's stack
pushf ;we don't really know where the original
push cs ; int 21h service was requested, so fake
push offset rm24trap ; one to point at a routine of ours
sub sp,9*2 ;temp save the general regs further down the
pusha ; stack, they'll get poped in a little while
mov ax,regUserSS ;we need a selector to the previous stack
mov bx,STD_DATA ;(it is almost certainly the PMIntrDos
call ParaToLDTSelector ; real mode stack, but this is playing safe)
mov cx,9 ;okay, now copy the 9 register values from
mov si,regUserSP ; the DOS visable stack to the app's
add si,5*2
mov ds,ax
assume ds:NOTHING
push ss
pop es
mov di,sp
add di,8*2
cld
rep movsw
mov ds,selDgroupPM ;restore addressability to our DGROUP
assume ds:DGROUP
; On entry, BP:SI points to a device header. Map BP from a seg to a selector.
mov ax,bp
mov bx,STD_DATA
call ParaToLDTSelector
mov regUserAX,ax
popa ;restore initial register values
mov bp,regUserAX ;give them the selector, not the segment
; Invoke the protected mode handler
.386p
test DpmiFlags,word ptr DPMI_32BIT
jz ri2410
pushfd
push 0
push cs
push 0
push offset rm24ret
jmp ri2420
.286p
ri2410: pushf ;put our return address on the stack so the
push cs ; handler will return to us when it's done.
push offset rm24ret
if 0 ;; ifndef WOW_x86
pushf ;transfer control to the
push word ptr PMInt24Handler+2 ; pm handler
push word ptr PMInt24Handler
iret
else
.386p
ri2420: pushfd ;transfer control to the
push dword ptr PMInt24Handler+4 ; pm handler
push dword ptr PMInt24Handler
iretd
.286p
endif
; The protected mode critical error handler returns here when it's finished
; (at least it had better return here!)
public rm24ret
rm24ret:
assume ds:NOTHING,es:NOTHING
FCLI
cld
add sp,12*2 ;clear critical error junk from stack
rm24exit:
mov ds,selDgroupPM ;DOS extender data segment
assume ds:DGROUP
mov regUserAX,ax ;save action code from pm handler
pop regUserSP ;pop prior stack location
pop regUserSS
; Switch back to the temp interrupt stack frame, drop to real mode, back
; to the initial stack, and return to DOS.
ASSERT_REFLSTK_OK
CHECK_STACK
add pbReflStack,CB_STKFRAME ;in the reverse order from above so
ASSERT_REFLSTK_OK
mov ss,selDgroupPM ; that we wind up in the same place
.386p
movzx esp,pbReflStack
.286p
SwitchToRealMode ;gotta be on our own stack to do this
mov ax,regUserAX ;recover AX from pm critical error handler
mov ss,regUserSS ;switch back to the original stack.
mov sp,regUserSP
pop ds ;return to DOS
pop es
iret
; -------------------------------------------------------
;
; rm24trap -- This code gets executed if the protected mode critical
; error handler attempts to bypass DOS and return directly
; to the application. Currently this is not allowed, and
; we just return to DOS anyway--most likely to die!
;
; Note: THIS IS NOT SUPPORTED! DON'T DO THIS!
BeginHighSegment
public rm24trap
rm24trap:
Debug_Out "Critical error handler tried to return to application!"
jmp short rm24exit
EndHighSegment
RMIntr24 endp
; -------------------------------------------------------
DXCODE ends
; -------------------------------------------------------
subttl INT 28h Idle Handler
page
; -------------------------------------------------------
; INT 28H IDLE HANDLER
; -------------------------------------------------------
DXPMCODE segment
assume cs:DXPMCODE
; -------------------------------------------------------
; PMIntr28 -- Protected mode handler for Idle Int 28h calls.
; The purpose of this routine is simply to cut down on the
; number of protected mode to real mode switches by ignoring
; many of the Int 28h idle calls made by the Windows PM
; kernel.
assume ds:NOTHING,es:NOTHING,ss:NOTHING
public PMIntr28
PMIntr28 proc near
cld
push ds ;address our DGROUP
mov ds,selDgroupPM
assume ds:DGROUP
cmp Int28Filter,0 ;are we passing any through?
jz @f
inc Int28Count ;should this one be reflected?
jz i28_reflect
@@:
pop ds
iret ; no, just ignore it
i28_reflect: ; yes, reset count and
push ax ; reflecto to real mode
mov ax,Int28Filter
neg ax
mov Int28Count,ax
pop ax
pop ds
assume ds:NOTHING
jmp PMIntrEntryVector + 3*28h
PMIntr28 endp
; -------------------------------------------------------
subttl Real-Time Clock Int 70h Handler
page
; -------------------------------------------------------
; REAL-TIME CLOCK INT 70h HANDLER
; -------------------------------------------------------
; PMIntr70 -- Protected mode handler for Real-Time clock
; interrupts. This routine intercepts real-time clock
; interrupts, and may cause them to be ignored. On 286
; hardware, the mode switch time is a big problem in trying
; to service the 0.976 ms periodic interrupt. So, if this
; is a 286 machine, and periodic interrupts are enabaled,
; we EOI the slave & master PICs, and IRET. A Tandy 2500 XL
; machine was having a problem with the interrupt reflector
; stack overrunning because the PS/2 style mouse was causing
; mode switches while the RTC was programmed for periodic
; interrupts.
assume ds:NOTHING,es:NOTHING,ss:NOTHING
public PMIntr70
PMIntr70 proc near
cld
push ds ;address our DGROUP
mov ds,selDgroupPM
assume ds:DGROUP
cmp idCpuType,3 ;assume we can mode switch fast enough
jae i70_reflect ; on 386 + processors
push ax ;on a 286, are periodic interrupts
mov al,0Bh ; enabled? Read clock register B
call ReadCMOS
and al,40h ;periodic interrupts enabled?
jz i70_286_reflect ; no, something else, so reflect it
mov al,0Ch ;read register C to clear int bits
call ReadCMOS
mov al,20h ;EOI the slave PIC
out INTB00,al
IO_Delay
out INTA00,al ;EOI the master PIC
pop ax ;back to the shadows again...
pop ds
iret
i70_286_reflect:
pop ax
i70_reflect: ;reflect interrupt to real mode
pop ds
assume ds:NOTHING
jmp PMIntrEntryVector + 3*70h
PMIntr70 endp
; -------------------------------------------------------
; ReadCMOS -- Read selected location from CMOS ram/Real-Time clock.
;
; in: al - CMOS location to read
; out: al - CMOS valus
; uses: All registers perserved
assume ds:NOTHING,es:NOTHING,ss:NOTHING
public ReadCMOS
ReadCMOS proc near
out CMOSLoc,al
IO_Delay
in al,CMOSValue
ret
ReadCMOS endp
; -------------------------------------------------------
subttl Ignore Interrupt Handlers
page
; -------------------------------------------------------
; IGNORE INTERRUPT HANDLER
; -------------------------------------------------------
; PMIntrIgnore -- Service routine for protected mode interrupts
; that should be ignored, and not reflected to real mode.
; Currently used for:
;
; Int 30h - used to be Win/386 Virtualize I/O, now
; unused but no int handler in real mode
; Int 41h - Wdeb386 interface, no int handler in
; real mode
assume ds:NOTHING,es:NOTHING,ss:NOTHING
public PMIntrIgnore
PMIntrIgnore proc near
iret
PMIntrIgnore endp
; -------------------------------------------------------
public PMIntr19
PMIntr19 proc near
push offset DXPMCODE:Reboot
call RZCall
bpRebootIDT df 0
Reboot:
mov ax,40h
mov es,ax
mov word ptr es:[0072h],1234h
lidt bpRebootIDT
int 3
PMIntr19 endp
DXPMCODE ends
; -------------------------------------------------------
subttl XMS Driver Interface
page
; -------------------------------------------------------
DXPMCODE segment
assume cs:DXPMCODE
; -------------------------------------------------------
; XMScontrol - This function implements a protected mode
; interface to a real mode XMS driver. Unlike other
; routines in this module, this routine is called by
; the user, not invoked via an INT instruction.
;
; Input: User's regs for XMS driver
; Output: regs from XMS driver
; Uses: none
assume ds:NOTHING,es:NOTHING,ss:NOTHING
public XMScontrol
XMScontrol proc far
jmp short XMSentry ;'standard' XMS control function
nop ; just to be consistant
nop
nop
XMSentry:
; Modify the stack so it looks like we got here via an INT (except that
; we may still have interrupts enabled)
pushf
cld
push bp
mov bp,sp ;bp -> [BP] [FL] [IP] [CS]
push ax
push bx
mov ax,[bp+4]
mov bx,[bp+6]
xchg ax,[bp+2]
mov [bp+4],bx
mov [bp+6],ax ;bp -> [BP] [IP] [CS] [FL]
pop bx
pop ax
pop bp
; We don't support XMS function 0Bh (Move Extended Memory Block) because
; it requires mapping of data between hi/low memory. Maybe someday...
cmp ah,0Bh
jnz xms_2
xms_deny:
xor ax,ax ;if function 0Bh, return failure
mov bl,80h ; (ax = 0, bl = 80h-not implemented)
jmp short XMSret
xms_2:
; We are not really an Int handler, but close enough...
call EnterIntHandler ;build an interrupt stack frame
assume ds:DGROUP,es:DGROUP ; also sets up addressability
if 0
if VCPI
;
; If we're using VCPI, then fail the call. This is because XMS memory
; would not be useful in protected mode unless we paged it into our
; page tables.
;
cmp fVCPI,0
jz xms_3
call LeaveIntHandler
mov ax,0
mov dx,0
mov bl,80h ; BX = 80h-not implemented.
jmp XMSret
xms_3:
endif
endif
SwitchToRealMode
pop es ;load regs for driver
pop ds
assume ds:NOTHING,es:NOTHING,ss:DGROUP
popa
npopf
call lpfnXMSFunc ;call real mode driver
pushf ;rebuild stack frame
FCLI
cld
pusha
push ds
push es
mov bp,sp ;restore stack frame pointer
SwitchToProtectedMode
assume ds:DGROUP,es:DGROUP
call LeaveIntHandler
assume ds:NOTHING,es:NOTHING,ss:NOTHING
XMSret:
riret
XMScontrol endp
; -------------------------------------------------------
DXPMCODE ends
; -------------------------------------------------------
subttl Special Interrupt Handler Routines
page
; -------------------------------------------------------
;
; The following sets of routines handle interrupts that
; are function call interfaces and require special servicing
; by the Dos Extender. These interrupts are such things as
; the mouse driver function call interrupt, various PC BIOS
; function call interrupts, etc. Note that INT 21h (the Dos
; function call interrupt) is not handled here. These
; interrupts typically require that register values be modified
; and parameter data be copied between real mode memory and
; extended memory. The following conventions are used for these
; interrupt function handler routines.
;
; A stack is allocated from the interrupt reflector stack for these
; routines to use. This allows nested servicing of interrupts.
; A stack frame is built in the allocated stack which contains the
; following information:
; original caller's stack address
; caller's original flags and general registers (in pusha form)
; caller's original segment registers (DS & ES)
; flags and general registers to be passed to interrupt routine
; (initially the same as caller's original values)
; segment registers (DS & ES) to be passed to interrupt routine
; (initially set to the Dos Extender data segment address)
; This stack frame is built by the routine EnterIntHandler, and its
; format is defined by the structure INTRSTACK. The stack frame is
; destroyed and the processor registers set up for return to the user
; by the function LeaveIntHandler.
;
; For each interrupt, there is an entry function and an exit function.
; The entry function performs any modifications to parameter values and
; data buffering necessary before the interrupt service routine is called.
; The exit function performs any data buffering and register value
; modifications after return from the interrupt service routine.
;
; There are two sets of general registers and two sets of segment
; registers (DS & ES) on the stack frame. One set of register values
; has member names of the form intUserXX. The values in these stack
; frame members will be passed to the interrupt service routine when
; it is called, and will be loaded with the register values returned
; by the interrupt service routine. The other set of registers values
; has member names of the form pmUserXX. These stack frame members
; contain the original values in the registers on entry from the
; user program that called the interrupt.
;
; When we return to the original caller, we want to pass back the
; general registers as returned by the interrupt routine (and possibly
; modified by the exit handler), and the same segment registers as
; on entry, unless the interrupt routine returns a value in a segment
; register. (in this case, there must be some code in the exit routine
; to handle this). This means that when we return to the caller, we
; return the general register values from the intUserXX set of stack
; frame members, but we return the segment registers from the pmUserXX
; set of frame members. By doing it this way, we don't have to do
; any work for the case where the interrupt subfuntion doesn't require
; any parameter manipulation. NOTE however, this means that when
; manipulating register values to be returned to the user, the segment
; registers are treated opposite to the way the general registers are
; treated. For general registers, to return a value to the user,
; store it in a intUserXX stack frame member. To return a segment
; value to the user, store it in a pmUserXX stack frame member.
;
; -------------------------------------------------------
subttl BIOS Video Interrupt (Int 10h) Service Routine
page
; -------------------------------------------------------
; BIOS VIDEO INTERRUPT (INT 10h) SERVICE ROUTINE
; -------------------------------------------------------
DXPMCODE segment
assume cs:DXPMCODE
; -------------------------------------------------------
; PMIntrVideo - Entry point into interrupt reflector code
; for IBM PC Bios video (int 10h) calls.
;
; Input: normal registers for Bios calls
; Output: normal register returns for Bios calls
; Errors: normal Bios errors
; Uses: as per Bios calls
assume ds:NOTHING,es:NOTHING,ss:NOTHING
public PMIntrVideo
PMIntrVideo:
call EnterIntHandler ;build a stack frame and fix up the
cld ; return address so that the interrupt
;service routine will return to us.
;
; Perform fixups on the entry register values
call IntEntryVideo
;
; Execute the interrupt service routine
SwitchToRealMode
assume ss:DGROUP
pop es
pop ds
assume ds:NOTHING,es:NOTHING
popa
sub sp,8 ; make room for stack frame
push bp
mov bp,sp
push es
push ax
xor ax,ax
mov es,ax
mov [bp + 8],cs
mov [bp + 6],offset piv_10
mov ax,es:[10h*4]
mov [bp + 2],ax
mov ax,es:[10h*4 + 2]
mov [bp + 4],ax
pop ax
pop es
pop bp
retf
piv_10: pushf
FCLI
cld
pusha
push ds
push es
mov bp,sp ;restore stack frame pointer
SwitchToProtectedMode
assume ds:DGROUP,es:DGROUP
;
; Perform fixups on the return register values.
mov ax,[bp].pmUserAX ;get original function code
call IntExitVideo
;
; And return to the original caller.
call LeaveIntHandler
riret
; -------------------------------------------------------
; IntEntryVideo -- This routine performs any register
; fixups and data copying needed on entry to the
; PC BIOS video interrupt (Int 10h)
;
; Input: register values on stack frame
; Output: register values on stack frame
; Errors: none
; Uses: any registers modified,
; possibly modifies buffers rgbXfrBuf0 or rgbXfrBuf1
assume ds:DGROUP,es:DGROUP,ss:NOTHING
public IntEntryVideo
IntEntryVideo:
cmp ah,10h
jnz ienv20
;
; Video palette control function. Check for subfunctions that require
; special actions.
ienv10: cmp al,2 ;update all palette registers?
jnz @F
mov cx,17 ;palette data is 17 bytes long
jmp short ienv70 ;go copy the data
;
@@: cmp al,9 ;read all palette registers
jz ienv72
;
cmp al,12h ;update video DAC color registers
jnz @F
mov cx,[bp].pmUserCX ;count of table entries is in caller CX
add cx,cx ;each entry is 3 bytes long
add cx,[bp].pmUserCX
jmp short ienv70 ;go copy the data down
@@: cmp al,17h ;read a block of video DAC registers
jz ienv72
;
jmp short ienv90
;
;
ienv20: cmp ah,11h
jnz ienv30
;
; Character generator interface function.
; NOTE: a number of subfunctions of function 11h need to have munging
; and data buffering performed. However, function 30h is the only
; one used by Codeview, so this is the only one currently implemented.
; For this one, nothing needs to be done on entry, only on exit.
jmp short ienv90
;
;
ienv30: cmp ah,1Bh
jnz ienv40
;
; Video BIOS functionality/state information.
; On entry, we need to fix up ES:DI to point to our buffer.
mov [bp].intUserDI,offset DGROUP:rgbXfrBuf0
jmp short ienv90
;
;
ienv40:
jmp short ienv90
;
; Copy the buffer from the user ES:DX to our transfer buffer and set
; the value to DX passed to the interrupt routine to point to our buffer.
ienv70: cld
jcxz ienv90
push ds
mov si,[bp].pmUserDX
mov ds,[bp].pmUserES
mov di,offset DGROUP:rgbXfrBuf1
cld
rep movsb
pop ds
;
ienv72: mov [bp].intUserDX,offset DGROUP:rgbXfrBuf1
jmp short ienv90
;
; All done
ienv90:
ret
; -------------------------------------------------------
; IntExitVideo: This routine performs any register
; fixups and data copying needed on exit from the
; PC BIOS video interrupt (Int 10h).
;
; Input: register values on stack frame
; Output: register values on stack frame
; Errors: none
; Uses: any registers modified
; possibly modifies buffers rgbXfrBuf0 or rgbXfrBuf1
assume ds:DGROUP,es:DGROUP,ss:NOTHING
public IntExitVideo
IntExitVideo:
cmp ah,10h
jnz iexv20
;
; Palette control function.
cmp al,9 ;read palette data function
jnz @F
mov cx,17
jmp short iexv70
;
@@: cmp al,17h ;read video DAC registers
jnz @F
mov cx,[bp].pmUserCX ;each entry in table is 3 bytes long
add cx,cx
add cx,[bp].pmUserCX
jmp short iexv70
;
@@: jmp short iexv72
;
;
iexv20: cmp ah,11h
jnz iexv30
;
; Character generator interface function.
; NOTE: a number of subfunctions of function 11h need to have munging
; and data buffering performed. However, function 30h is the only
; one used by Codeview, so this is the only one currently implemented
cmp al,30h
jnz @F
mov ax,[bp].intUserES ;get the paragraph address returned by BIOS
mov bx,STD_DATA
call ParaToLDTSelector ;get a selector for that address
mov [bp].pmUserES,ax ;store the selector so that it will be
; returned to the caller
@@: jmp short iexv90
;
;
iexv30: cmp ah,1Bh
jnz iexv40
;
; Video BIOS functionality/state information.
; On exit, we need to fix up the pointer at the beginning of the
; data put in our buffer by the BIOS, and then transfer the buffer up
; to the user.
mov ax,word ptr rgbXfrBuf0[2] ;get segment of pointer to
; 'static functionallity table'
mov bx,STD_DATA
call ParaToLDTSelector ;convert paragraph to selector
mov word ptr rgbXfrBuf0[2],ax ;store back into table
push es
mov si,offset rgbXfrBuf0 ;pointer to our copy of the table
mov di,[bp].pmUserDI ;where the user wants it
mov [bp].intUserDi,di ;restore the DI returned to the user
mov es,[bp].pmUserES
mov cx,64 ;the table is 64 bytes long
cld
rep movsb ;copy the table to the user's buffer
pop es
jmp short iexv90
;
;
iexv40:
jmp short iexv90
;
; Copy data from our buffer to the caller's buffer pointed to by ES:DX
iexv70: cld
push es
mov di,[bp].pmUserDX
mov es,[bp].pmUserES
mov si,offset DGROUP:rgbXfrBuf1
rep movsb
pop es
;
; Restore the caller's DX
iexv72: mov ax,[bp].pmUserDX
mov [bp].intUserDX,ax
;
; All done
iexv90:
ret
; -------------------------------------------------------
DXPMCODE ends
; -------------------------------------------------------
subttl BIOS Misc. Interrupt (Int 15h) Service Routine
page
; -------------------------------------------------------
; BIOS MISC. INTERRUPT (INT 15h) SERVICE ROUTINE
; -------------------------------------------------------
DXPMCODE segment
assume cs:DXPMCODE
; -------------------------------------------------------
; PMIntrMisc -- Entry point into the interrupt processing code
; for the BIOS misc functions interrupt (INT 15h).
;
; Input: normal registers for Bios calls
; Output: normal register returns for Bios calls
; Errors: normal Bios errors
; Uses: as per Bios calls
assume ds:NOTHING,es:NOTHING,ss:NOTHING
public PMIntrMisc
PMIntrMisc:
;
call EnterIntHandler ;build a stack frame and fix up the
cld ; return address so that the interrupt
;service routine will return to us.
;
; Perform fixups on the entry register values
call IntEntryMisc
;
; Execute the interrupt service routine
SwitchToRealMode
assume ss:DGROUP
pop es
pop ds
assume ds:NOTHING,es:NOTHING
popa
sub sp,8 ; make room for stack frame
push bp
mov bp,sp
push es
push ax
xor ax,ax
mov es,ax
mov [bp + 8],cs
mov [bp + 6],offset pim_10
mov ax,es:[15h*4]
mov [bp + 2],ax
mov ax,es:[15h*4 + 2]
mov [bp + 4],ax
pop ax
pop es
pop bp
retf
pim_10: pushf
FCLI
cld
pusha
push ds
push es
mov bp,sp ;restore stack frame pointer
SwitchToProtectedMode
assume ds:DGROUP,es:DGROUP
;
; Perform fixups on the return register values.
mov ax,[bp].pmUserAX ;get original function code
call IntExitMisc
;
; And return to the original caller.
call LeaveIntHandler
riret
; -------------------------------------------------------
; MISC INTERRUPT SUPPORT ROUTINES
; -------------------------------------------------------
;
; IntEntryMisc -- This function performs data transfer
; and register translation on entry to the BIOS Misc.
; functions interrupt. (INT 15h).
;
; Input: AX - BIOS function being performed
; Output:
; Errors:
; Uses: All registers preserved
assume ds:DGROUP,es:DGROUP,ss:NOTHING
public IntEntryMisc
IntEntryMisc:
; Map requests to set the PS/2 Pointing Device Handler Address
cmp ax,0C207h ;PS/2 Set Pointing Device Handler adr?
jnz iem90
mov ax,[bp].pmUserBX ;User's ES:BX -> handler
mov word ptr lpfnUserPointingHandler,ax
mov ax,[bp].pmUserES
mov word ptr [lpfnUserPointingHandler+2],ax
mov ax,segDXCodePM ;pass BIOS address of our handler
mov [bp].intUserES,ax
mov ax,offset PointDeviceHandler
mov [bp].intUserBX,ax
iem90:
ret
; -------------------------------------------------------
; IntExitMisc -- This function performs data transfer
; and register translation on exit from the BIOS Misc.
; Functions interrupt (INT 15h).
;
; Input: AX - BIOS function being performed
; Output:
; Errors:
; Uses: All registers preserved
assume ds:DGROUP,es:DGROUP,ss:NOTHING
public IntExitMisc
IntExitMisc:
push ax
push bx
push cx
push dx
;
; Check for function 0C0h - Return System Configuration Parameters
cmp ah,0C0h
jnz ixmi30
test [bp].intUserFL,1 ;check if the bios call returned an error
jnz ixmi90 ;(carry flag set in returned flags)
;
; The BIOS call succeeded. This means that ES:BX points to a configuration
; vector. We need to fix up the segment to be a selector.
mov dx,[bp].intUserES
cmp dx,0F000h ;does it point to normal BIOS segment
jnz ixmi22
mov ax,SEL_BIOSCODE or STD_RING
jmp short ixmi24
ixmi22: call ParaToLinear
mov cx,0FFFFh
mov ax,SEL_USERSCR or STD_TBL_RING
cCall NSetSegmentDscr,<ax,bx,dx,0,cx,STD_DATA>
ixmi24: mov [bp].pmUserES,ax
jmp short ixmi90
; Chack for function 0C207h - PS/2 Set Pointing Device Handler Address
ixmi30:
cmp ax,0C207h
jne ixmi90
mov ax,[bp].pmUserBX ;restore user's BX
mov [bp].intUserBX,ax
; All done
ixmi90:
pop dx
pop cx
pop bx
pop ax
ret
; -------------------------------------------------------
DXPMCODE ends
; -------------------------------------------------------
subttl Mouse Function Interrupt (Int 33h) Service Routine
page
; -------------------------------------------------------
; MOUSE FUNCTION INTERRUPT (INT 33h) SERVICE ROUTINE
; -------------------------------------------------------
DXPMCODE segment
assume cs:DXPMCODE
; -------------------------------------------------------
; PMIntrMouse - Entry point into interrupt reflector code
; for mouse driver (int 33h) calls.
;
; Input: normal registers for mouse calls
; Output: normal register returns for mouse calls
; Errors: normal mouse errors
; Uses: as per mouse calls
assume ds:NOTHING,es:NOTHING,ss:NOTHING
public PMIntrMouse
PMIntrMouse:
;
call EnterIntHandler ;build a stack frame and fix up the
cld ; return address so that the interrupt
;service routine will return to us.
;
; Perform fixups on the entry register values
call IntEntryMouse
;
; Execute the interrupt service routine
SwitchToRealMode
assume ss:DGROUP
pop es
pop ds
assume ds:NOTHING,es:NOTHING
popa
sub sp,8 ; make room for stack frame
push bp
mov bp,sp
push es
push ax
xor ax,ax
mov es,ax
mov [bp + 8],cs
mov [bp + 6],offset pimo_10
mov ax,es:[33h*4]
mov [bp + 2],ax
mov ax,es:[33h*4 + 2]
mov [bp + 4],ax
pop ax
pop es
pop bp
retf
pimo_10: pushf
FCLI
cld
pusha
push ds
push es
mov bp,sp ;restore stack frame pointer
SwitchToProtectedMode
assume ds:DGROUP,es:DGROUP
;
; Perform fixups on the return register values.
mov ax,[bp].pmUserAX ;get original function code
call IntExitMouse
;
; And return to the original caller.
call LeaveIntHandler
riret
; -------------------------------------------------------
; MOUSE SUPPORT ROUTINES
; -------------------------------------------------------
; IntEntryMouse -- This function performs data transfer and
; register translation on entry to mouse driver functions.
; (INT 33h)
;
; Input: AX - mouse function being performed
; Output:
; Errors:
; Uses: NOTHING
assume ds:DGROUP,es:DGROUP,ss:NOTHING
public IntEntryMouse
IntEntryMouse:
cld
push ax
push cx
push si
push di
;
cmp al,9 ;Set graphics cursor block?
jnz ment10
;
; The user is setting a graphics cursor. We need to copy the masks
; down to low memory so that the mouse driver can get at them and then
; fix up the pointer in DX.
mov cx,32
jmp short ment92
;
; Mouse interrupt handler establishment
ment10: cmp al,12 ;Set user defined interrupt subroutine ?
jnz ment20
;
; This command has the effect of causing a call to the address es:ds
; Whenever an event of one of the types specified by the mask in cx.
; The address es:dx must be saved in lpfnUserMouseHandler and the
; real mode address of MouseInterruptHandler substituted.
mov ax,[bp].pmUserDX ; Load users handler offset
mov word ptr lpfnUserMouseHandler,ax ; Store for future use
mov ax,[bp].pmUserES ; Load users handler segment value
mov word ptr lpfnUserMouseHandler + 2,ax ; Store for future use
mov ax,segDXCodePM ; Load real mode code segment value
mov [bp].intUserES,ax ; Store in real mode es register image
mov ax,offset MouseInterruptHandler ; Load handler offset
mov [bp].intUserDX,ax ; Store in real mode dx register image
jmp short ment99 ;Return
;
ment20: cmp al,20
jc ment99
jnz ment30
;
; This is the swap interrupt subroutine function. Not currently implemented
jmp short ment99
;
ment30: cmp al,22 ;Save mouse driver state?
jnz ment40
;
; This is the save mouse driver state function. We need to pass a pointer
; to the transer buffer down to the mouse driver.
mov ax,npXfrBuf1
mov [bp].intUserDX,ax
jmp short ment99
ment40: cmp al,23 ;Restore mouse driver state?
jnz ment99
;
; This is the restore mouse driver state function. We need to copy the
; mouse state buffer from the pm user location to the transfer buffer,
; and then pass the pointer to the transfer buffer on to the mouse driver.
mov cx,cbMouseState
jcxz ment99
;
; Transfer the data pointed to by the user ES:DX to the scratch buffer, and
; fix up the pointer that is passed on to the mouse driver.
ment92: mov si,[bp].pmUserDX
mov di,npXfrBuf1
mov [bp].intUserDX,di
push ds
mov ds,[bp].pmUserES
cld
rep movs word ptr [di],word ptr [si]
pop ds
;
ment99: pop di
pop si
pop cx
pop ax
ret
; -------------------------------------------------------
; IntExitMouse -- This function performs data transfer and
; register translation on exit from mouse driver functions.
; (INT 33h)
;
; Input: AX - mouse function being performed
; Output:
; Errors:
; Uses:
assume ds:DGROUP,es:DGROUP,ss:NOTHING
public IntExitMouse
IntExitMouse:
cld
cmp al,21 ;get state buffer size?
jnz mxit20
;
; We need to remember the state buffer size, so that later we will know
; how many bytes to transfer when we do the save/restore state fucntions.
mov ax,[bp].intUserBX
mov cbMouseState,ax
return
;
mxit20: cmp al,22 ;Save mouse driver state?
jnz mxit30
;
; We need to restore the original values of ES:DX and transfer the mouse
; state data from the real mode buffer to the user's protected mode buffer.
mov cx,cbMouseState
jcxz mxit28
push es
mov si,npXfrBuf1
mov di,[bp].pmUserDX
mov [bp].intUserDX,di
mov es,[bp].pmUserES
rep movs byte ptr [di],byte ptr [si]
pop es
mxit28: return
;
mxit30: cmp al,23 ;Restore mouse driver state?
jnz mxit99
mov ax,[bp].pmUserDX
mov [bp].intUserDX,ax
;
mxit99: ret
; -------------------------------------------------------
DXPMCODE ends
; -------------------------------------------------------
subttl PM Interrupt Support Routines
page
; -------------------------------------------------------
; PM INTERRUPT SUPPORT ROUTINES
; -------------------------------------------------------
DXPMCODE segment
assume cs:DXPMCODE
; -------------------------------------------------------
; EnterIntHandler -- This routine will allocate a stack
; frame on the interrupt reflector stack and make
; a copy of the registers on the allocated stack.
;
; Note: This routine expects the current stack to contain a near
; return address and a normal [IP] [CS] [FL] interrupt stack
; frame. Don't have anything else on the stack before calling
; this routine!
;
; Note: This routine disables interrupts, and leaves them disabled.
; Most callers already have them disabled, so it doesn't
; really make a difference, except that this routine
; requires that they be disabled.
;
; Input: none
; Output: stack frame set up
; Errors: none
; Uses: all registers preserved
assume ds:NOTHING,es:NOTHING,ss:NOTHING
public EnterIntHandler
EnterIntHandler proc near
FCLI ;we really want int's disabled (and
; XMScontrol doesn't do that)
push ds
mov ds,selDgroupPM ;save user's DS and address our DGROUP
assume ds:DGROUP
pop regUserDS
push bp
mov bp,sp ;bp -> [BP] [IP] [IP] [CS] [FL]
push word ptr [bp+8]
pop regUserFL ;user's flags before doing INT
pop bp
pop pfnReturnAddr ;near return to our immediate caller
mov regUserSS,ss ;save caller's stack address
mov regUserSP,sp
ASSERT_REFLSTK_OK
mov ss,selDgroupPM ;switch to interrupt reflector stack
mov sp,pbReflStack
sub pbReflStack,CB_STKFRAME ;adjust pointer to next stack frame
FIX_STACK
; Build the stack frame. The stack frame contains the following:
; dword & word parameter locations
; original caller's stack address
; caller's original flags and general registers (in pusha form)
; caller's original segment registers (DS & ES)
; flags and general registers to be passed to interrupt routine
; (initially the same as caller's original values)
; segment registers (DS & ES) to be passed to interrupt routine
; (initially set to the Dos Extender data segment address)
;
; The parameter words and then the caller's original register values go on top.
sub sp,8 ;space for a dd & 2 dw's
push regUserSP
push regUserSS
push regUserFL
pusha
push regUserDS
push es
; Now, put all of the general registers, and values for the segment
; registers to be passed to the interrupt service routine. We pass
; the Dos Extender data segment address to the interrupt routine.
push regUserFL
pusha
IFDEF ROM
push segDXData
push segDXData
ELSE
push segDXDataPM
push segDXDataPM
ENDIF
; And we are done.
mov bp,sp ;set up frame pointer
mov es,selDgroupPM
jmp pfnReturnAddr ;return to the caller.
EnterIntHandler endp
; -------------------------------------------------------
; LeaveIntHandler -- This routine will restore the user registers,
; release the stack frame, and restore the original user's stack
; for exit from an interrupt reflector routine.
;
; Note: Interrupts must be off when this routine is called.
;
; Input: none
; Output: none
; Errors: none
; Uses: All registers modified
assume ds:DGROUP,es:NOTHING,ss:NOTHING
public LeaveIntHandler
LeaveIntHandler proc near
FCLI
pop pfnReturnAddr
; The copy of the register values returned from the interrupt routine
; (and then possibly modified by the exit handler for the particular
; interrupt) are what gets returned to the caller. We discard the original
; register values saved on entry. (They were there so that the exit
; routine could refer to them if necessary)
add sp,4 ;skip over interrupt service routine's
; segment register values
popa ;restore general register values
pop regUserFL ;flags returned by interrupt routine
pop es ;get segment registers from pmUserES
pop regUserDS ; and pmUserDS
add sp,18 ;skip over the original user registers
; and flags
pop regUserSS ;original interrupted routine's stack
pop regUserSP
mov regUserAX,ax
; Switch back to the original user's stack.
ASSERT_REFLSTK_OK
ASSERT_CLI
CHECK_STACK
mov ss,regUserSS
mov sp,regUserSP
add pbReflStack,CB_STKFRAME
ASSERT_REFLSTK_OK
; We need to replace the image of the flags in the original int return
; address on the user's stack with the new flags returned from the interrupt
; service routine.
push bp
mov bp,sp ;stack -> BP IP CS FL
mov ax,regUserFL ;flags returned by interrupt service routine
and ax,0BFFFh ;clear the nested task flag
and [bp+6],0300h ;clear all but the interrupt and trace flags
; in the caller's original flags
or [bp+6],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 now, return to the caller.
push pfnReturnAddr
mov ax,regUserAX
mov ds,regUserDS
assume ds:NOTHING
ret
LeaveIntHandler endp
; -------------------------------------------------------
DXPMCODE ends
; -------------------------------------------------------
subttl Mouse Interrupt Callback Function Handler
page
; -------------------------------------------------------
; MOUSE INTERRUPT CALLBACK FUNCTION HANDLER
; -------------------------------------------------------
DXCODE segment
assume cs:DXCODE
; -------------------------------------------------------
; MouseInterruptHandler -- This routine is the entry point for
; user requested mouse event interrupts. It switches the
; processor to protected mode and transfers control to the
; user protected mode mouse handling routine. When that
; completes, it switches back to real mode and returns control
; to the mouse driver.
; Entry to this routine will have been requested by an
; INT 33H code 12 with the real address of this routine
; substituted for the users entry point.
; The address of the user specified mouse handler as specified
; in the original INT 33H is stored in the variable
; lpfnUserMouseHandler.
;
; 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 users mouse handler.
assume ds:NOTHING,es:NOTHING,ss:NOTHING
public MouseInterruptHandler
MouseInterruptHandler proc far
;
; On entry, the stack layout is:
; [2] CS - System mouse handler code segment
; [0] IP - System mouse handler return offset
;
push es
push ds
pushf
FCLI
cld
IFDEF ROM
SetRMDataSeg
ELSE
mov ds,selDgroup
ENDIF
assume ds:DGROUP
pop regUserFL
;
; Allocate a new stack frame, and then switch to the local stack
; frame.
mov regUserSP,sp ;save entry stack pointer so we can restore it
mov regUSerSS,ss ;save segment too
IFDEF ROM
push ds
pop ss
ELSE
mov ss,selDgroup ;switch to our own stack frame
ENDIF
ASSERT_REFLSTK_OK
mov sp,pbReflStack
sub pbReflStack,CB_STKFRAME ;adjust pointer to next stack frame
FIX_STACK
;
; We are now running on our own stack, so we can switch into protected mode.
push ax ;preserve caller's AX
SwitchToProtectedMode
pop ax
;
; Build a far return frame on the stack so that the user's
; routine will return to us when it is finished.
push regUserSS ; save system mouse handler stack address
push regUserSP ; so we can restore it later
push ds
push cs
push offset mih50
;
; Build an IRET frame on the stack to use to transfer control to the
; user's protected mode routine
push regUserFL
push word ptr lpfnUserMouseHandler+2 ;push segment of user routine
push word ptr lpfnUserMouseHandler ;push offset of user routine
;
; At this point the interrupt reflector stack looks like this:
;
; [14] stack segment of original stack
; [12] stack pointer of original stack
; [10] real mode dos extender data segment
; [8] segment of return address back to here
; [6] offset of return address back here
; [4] Users flags
; [2] segment of user routine
; [0] offset of user routine
;
; Execute the users mouse handler
iret
;
; The users handler will return here after it is finsished.
mih50: FCLI
cld
pop ds
pop regUserSP
pop regUserSS
;
; Switch back to real mode.
push ax ;preserve AX
SwitchToRealMode
pop ax
CHECK_STACK
;
; Switch back to the original stack.
mov ss,regUserSS
mov sp,regUserSP
ASSERT_REFLSTK_OK
;
; Deallocate the stack frame that we are using.
add pbReflStack,CB_STKFRAME
ASSERT_REFLSTK_OK
;
; And return to the original interrupted program.
pop ds
pop es
ret
MouseInterruptHandler endp
; -------------------------------------------------------
DXCODE ends
; -------------------------------------------------------
subttl PS/2 Pointing Device Handler
page
; -------------------------------------------------------
; PS/2 POINTING DEVICE HANDLER
; -------------------------------------------------------
DXCODE segment
assume cs:DXCODE
; -------------------------------------------------------
; PointDeviceHandler -- This routine is the entry point for
; the PS/2 Pointing Device Handler. It switches the
; processor to protected mode and transfers control to the
; user pointing device handler. When that completes,
; it switches back to real mode and returns control to
; the PS/2 BIOS.
;
; Note: The BIOS calls us with interrutps enabled!
; Input: none
; Output: none
; Errors: none
assume ds:NOTHING,es:NOTHING,ss:NOTHING
public PointDeviceHandler
PointDeviceHandler proc far
; On entry, the stack layout is:
;
; [10] status
; [8] X coordinate
; [6] Y coordinate
; [4] Z coordinate
; [2] CS - PS/2 BIOS code segment
; [0] IP - PS/2 BIOS return offset
cld
push es ;save PS/2 BIOS ds/es on it's stack
push ds
IFDEF ROM
push ax
GetRMDataSeg
mov ds,ax
mov es,ax
pop ax
ELSE
mov ds,selDgroup ;addressability to DOSX DGROUP
push ds
pop es
ENDIF
assume ds:DGROUP,es:DGROUP
FCLI ;protect global regUserXX vars
; Allocate a new stack frame, and then switch to the local stack
; frame.
mov regUserSP,sp ;save entry stack pointer so we can restore it
mov regUSerSS,ss ;save segment too
IFDEF ROM
push ds
pop ss
ELSE
ASSERT_REFLSTK_OK
mov ss,selDgroup ;switch to our own stack frame
ENDIF
mov sp,pbReflStack
sub pbReflStack,CB_STKFRAME ;adjust pointer to next stack frame
FIX_STACK
push regUserSS ;save PS/2 BIOS stack address
push regUserSP ; so we can restore it later
push SEL_DXDATA or STD_RING ;DOSX DS to be poped in PM
sub sp,4*2 ;temp save the general regs further down the
pusha ; stack, they'll get poped in a little while
; Copy PS/2 pointing device stack info to our (soon to be) protected mode stack
mov si,regUserSP ;PS/2 stack pointer
mov ds,regUserSS ;PS/2 stack segment
assume ds:NOTHING
FSTI ;no more references to global regUserXX vars
add si,4*2 ;skip over es,ds,cs,ip
mov di,sp ;loc for pointing device
add di,8*2 ; data on our stack
mov cx,4
cld
rep movsw
push es ;restore ds = DGROUP
pop ds
assume ds:DGROUP
; We are now running on our own stack, so we can switch into protected mode.
SwitchToProtectedMode ;disables interrupts again
FSTI ; but we don't want them disabled
popa ;restore general registers
; At this point the stack looks like this:
;
; [12] stack segment of original stack
; [10] stack pointer of original stack
; [8] protect mode dos extender data segment
; [6] status
; [4] X coordinate
; [2] Y coordinate
; [0] Z coordinate
; Execute the user's pointing device handler
call [lpfnUserPointingHandler]
; The users handler will return here after it is finsished.
pdh50:
cld
add sp,4*2 ;discard pointing device info
pop ds
FCLI ;protect global regUserXX vars
pop regUserSP
pop regUserSS
; Switch back to real mode.
push ax ;preserve AX
SwitchToRealMode
pop ax
; Switch back to the original stack.
CHECK_STACK
mov ss,regUserSS
mov sp,regUserSP
; Deallocate the stack frame that we are using.
ASSERT_REFLSTK_OK
add pbReflStack,CB_STKFRAME
ASSERT_REFLSTK_OK
; And return to the PS/2 BIOS
FSTI ;we came in with ints enabled
pop ds
pop es
ret
PointDeviceHandler endp
; -------------------------------------------------------
;
; -------------------------------------------------------
subttl Utility Function Definitions
page
; -------------------------------------------------------
; UTILITY FUNCTION DEFINITIONS
; -------------------------------------------------------
;
; SaveRMIntrVectors -- This routine copies the current
; real mode interrupt vector table to the shadow
; vector table used by the interrupt reflector.
;
; Input: none
; Output: none
; Errors: none
; Uses; all registers preserved
;
; NOTE: This routine can only be called in REAL MODE.
assume ds:DGROUP,es:NOTHING,ss:NOTHING
public SaveRMIntrVectors
SaveRMIntrVectors:
push cx
push si
push di
push ds
push es
;
cld
push ds
pop es
xor cx,cx
mov si,cx
mov ds,cx
mov di,offset DGROUP:rglpfnRmISR
mov cx,2*256
rep movs word ptr [di],word ptr [si]
;
pop es
pop ds
pop di
pop si
pop cx
ret
; -------------------------------------------------------
; RestoreRMIntrVectors -- This routine copies the
; interrupt vectors from the real mode interrupt
; vector shadow table back down to the real interrupt
; vectors.
;
; Input: none
; Output: none
; Errors: none
; Uses; all registers preserved
;
; NOTE: This routine can only be called in REAL MODE.
assume ds:DGROUP,es:NOTHING,ss:NOTHING
public RestoreRMIntrVectors
RestoreRMIntrVectors:
push cx
push si
push di
push ds
push es
;
FCLI
cld
xor cx,cx
mov di,cx
mov es,cx
mov si,offset DGROUP:rglpfnRmISR
mov cx,2*256
rep movs word ptr [di],word ptr [si]
FSTI
;
pop es
pop ds
pop di
pop si
pop cx
ret
; -------------------------------------------------------
DXCODE ends
DXPMCODE segment
assume cs:DXPMCODE
IFDEF WOW
;--------------------------------------------------------
;
; Wow16TransitionToUserMode -- This routine simulates a
; ring transition from the kernelmode dos extender
; code to the usermode dos extender code. It does this
; by restoring the user regs from the dosx stack, restoring
; user bp from user stack, and retf
;
; Inputs: ss:sp -> user ds
; user ax
; user bx
; user cx
; user sp
; user ss
; user ss:sp -> user bp
; user ip
; user cs
; Outputs: none
;
assume ds:nothing,es:nothing,ss:DGROUP
public Wow16TransitionToUserMode
Wow16TransitionToUserMode proc
pop ds
pop ax
pop bx
pop cx
mov bp,sp
.386p
lss sp,[bp]
.286p
pop bp
retf
Wow16TransitionToUserMode endp
;--------------------------------------------------------
;
; wow32TransitionToUserMode -- This routine simulates a
; ring transition from the kernelmode dos extender
; code to the usermode dos extender code. It does this
; by restoring the user regs from the dosx stack, restoring
; user bp from user stack, and retf
;
; Inputs: ss:sp -> user ds
; user ax
; user bx
; user cx
; user esp
; user ss
; user ss:sp -> user bp
; user eip
; user cs
; Outputs: none
;
assume ds:nothing,es:nothing,ss:DGROUP
public wow32TransitionToUserMode
wow32TransitionToUserMode proc
pop ds
pop ax
pop bx
pop cx
mov bp,sp
.386p
lss esp,[bp]
.286p
pop bp
db 066h ; operand override
retf
wow32TransitionToUserMode endp
public Wow32IntrRefl
Wow32IntrRefl label word
??intnum = 0
rept 256
push word ptr ??intnum
jmp Wow32Intr16Reflector
??intnum = ??intnum + 1
endm
;--------------------------------------------------------
;
; Wow32Intr16Reflector -- This routine reflects a 32 bit
; interrupt to a 16 bit handler. It switches to the
; dos extender stack to do so.
;
; Inputs: none
; Outputs: none
;
assume ds:nothing,es:nothing,ss:nothing
public Wow32Intr16Reflector
Wow32Intr16Reflector proc
.386p
push ebp
mov ebp,esp
push ds
push eax
push ebx
push edi
mov ax,ss
movzx eax,ax
lar eax,eax
test eax,(AB_BIG SHL 8)
jnz w32i16r10
movzx ebp,bp
w32i16r10:
;
; Get a frame on the dosx stack.
;
mov ax,selDgroupPM
mov ds,ax
assume ds:DGROUP
movzx ebx,pbReflStack
sub pbReflStack,CB_STKFRAME
;
; Build a frame on the stack
;
sub bx,30
mov eax, [ebp+6] ; eip
mov [bx+20], eax
mov eax, [ebp+10] ; cs
mov [bx+24], eax
mov [bx + 18],ss ; ss for stack switch back
mov eax,ebp
add eax,6 ; ebp, int number
mov [bx + 14],eax ; esp for stack switch back
mov ax,[ebp + 14] ; get flags
mov [bx + 12],ax
mov ax,cs
mov [bx + 10],ax
mov [bx + 8],offset DXPMCODE:w3216r30
mov eax,[ebp]
mov [bx],eax ; put ebp on other stack for pop
;
; Get handler
;
mov di,[ebp + 4] ; int number
shl di,2 ; al * 4
add di,offset DGROUP:Wow16BitHandlers
mov ax,[di]
mov [bx + 4],ax ; handler ip
mov ax,[di + 2]
mov [bx + 6],ax ; handler cs
;
; Set up for stack switch
;
push ds
push ebx
;
; Restore registers
;
mov ax,[ebp - 2]
mov ds,ax
mov eax,[ebp - 6]
mov ebx,[ebp - 10]
mov edi,[ebp - 14]
;
; Switch stacks, restore ebp, and call handler
;
lss esp,[ebp - 20]
pop ebp
DEBUG_TRACE DBGTR_ENTRY, 0, 0, 2000h
retf
;
; N.B. i31_RMCall looks on the stack to get the original user stack pointer.
; if you change the stack frame the is passed to the 16 bit int
; handlers, that WILL break.
;
w3216r30:
DEBUG_TRACE DBGTR_EXIT, 0, 0, 2000h
;
; Switch stacks, deallocate frame from dosx stack and return
;
push ebx
push eax
push ds
lds ebx,[esp+10] ;get ss:esp
mov eax,[esp+16]
mov [ebx],eax ;eip
mov eax,[esp+20]
mov [ebx+4],eax ;cs
pop ds
pop eax
pop ebx
lss esp,[esp]
push ebx
pushfd
push eax
mov ax,ss
movzx eax,ax
lar eax,eax
test eax,(AB_BIG SHL 8) ; is the stack big?
jnz w32i16r40 ; jif yes, use 32bit operations
pop eax ; restore regs
popfd
rpushfd ; save flags, set virtual int bit
pop ebx
push ebp
movzx ebp, sp
mov [ebp + 16],ebx ; put flags on iret frame
pop ebp
push ds
mov bx,selDgroupPM
mov ds,bx
add pbReflStack,CB_STKFRAME
pop ds
pop ebx
riretd
w32i16r40: ; stack is big
pop eax ; restore regs
popfd
rpushfd32
pop ebx
mov [esp + 12],ebx
push ds
mov bx,selDgroupPM
mov ds,bx
add pbReflStack,CB_STKFRAME
pop ds
pop ebx
riretd32
.286p
Wow32Intr16Reflector endp
ENDIF
DXPMCODE ends
;
;****************************************************************
end