mirror of https://github.com/lianthony/NT4.0
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1383 lines
31 KiB
1383 lines
31 KiB
/*++
|
|
|
|
Copyright (c) 1991 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
dpmiint.c
|
|
|
|
Abstract:
|
|
|
|
This file contains the interrupt support for DPMI. Most of this is
|
|
for supporting the 486 emulator on risc platforms, but some code
|
|
is shared with x86.
|
|
|
|
Author:
|
|
|
|
Neil Sandlin (neilsa) 1-Jun-1995
|
|
|
|
Revision History:
|
|
|
|
Comments:
|
|
|
|
DPMI stack switching is accomplished by keeping a "locked pm stack"
|
|
count, and when the count is zero, a stack switch occurs. This keeps
|
|
track of the situation with recursive interrupts where the client
|
|
may switch to its own stack. So, a stack switch to our locked stack
|
|
occurs on the first level interrupt, and on subsequent nested interrupts,
|
|
only the count is maintained. This is identical to how win31 managed
|
|
the stack.
|
|
|
|
If a client specifies that it is a 32-bit dpmi client, this only affects
|
|
the "width" of a stack frame. A 16-bit client gets 16-bit frames, and
|
|
a 32 bit client gets 32-bit frames. It is still necessary to check
|
|
the size of the stack segment to determine if SP or ESP should be used.
|
|
|
|
--*/
|
|
|
|
#include "precomp.h"
|
|
#pragma hdrstop
|
|
#include <softpc.h>
|
|
#include <dpmiint.h>
|
|
#include <intapi.h>
|
|
|
|
|
|
VOID
|
|
DpmiFatalExceptionHandler(
|
|
UCHAR XNumber,
|
|
PCHAR VdmStackPointer
|
|
);
|
|
|
|
VOID
|
|
DpmiSetProtectedmodeInterrupt(
|
|
VOID
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function services the SetProtectedmodeInterrupt bop. It retrieves
|
|
the handler information from the Dos application stack, and puts it into
|
|
the VdmTib, for use by instruction emulation.
|
|
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
PVDM_INTERRUPTHANDLER Handlers = DpmiInterruptHandlers;
|
|
USHORT IntNumber;
|
|
PCHAR StackPointer;
|
|
|
|
StackPointer = Sim32GetVDMPointer(((((ULONG)getSS()) << 16) | getSP()),
|
|
0,
|
|
(UCHAR) (getMSW() & MSW_PE)
|
|
);
|
|
|
|
IntNumber = *(PWORD16)(StackPointer + 6);
|
|
|
|
Handlers[IntNumber].Flags = *(PWORD16)(StackPointer + 8);
|
|
Handlers[IntNumber].CsSelector = *(PWORD16)(StackPointer + 4);
|
|
Handlers[IntNumber].Eip = *(PDWORD16)(StackPointer);
|
|
|
|
DBGTRACE(DPMI_SET_PMODE_INT_HANDLER, IntNumber,
|
|
Handlers[IntNumber].CsSelector,
|
|
Handlers[IntNumber].Eip);
|
|
|
|
#ifdef i386
|
|
if (IntNumber == 0x21)
|
|
{
|
|
VDMSET_INT21_HANDLER_DATA ServiceData;
|
|
NTSTATUS Status;
|
|
|
|
ServiceData.Selector = Handlers[IntNumber].CsSelector;
|
|
ServiceData.Offset = Handlers[IntNumber].Eip;
|
|
ServiceData.Gate32 = Handlers[IntNumber].Flags & VDM_INT_32;
|
|
|
|
Status = NtVdmControl(VdmSetInt21Handler, &ServiceData);
|
|
|
|
#if DBG
|
|
if (!NT_SUCCESS(Status)) {
|
|
OutputDebugString("DPMI32: Error Setting Int21handler\n");
|
|
}
|
|
#endif
|
|
}
|
|
#endif //i386
|
|
|
|
setAX(0);
|
|
}
|
|
|
|
VOID
|
|
DpmiSetFaultHandler(
|
|
VOID
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function services the SetFaultHandler bop. It retrieves
|
|
the handler information from the Dos application stack, and puts it into
|
|
the VdmTib, for use by instruction emulation.
|
|
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
PVDM_FAULTHANDLER Handlers = DpmiFaultHandlers;
|
|
USHORT IntNumber;
|
|
PCHAR StackPointer;
|
|
|
|
StackPointer = Sim32GetVDMPointer(((((ULONG)getSS()) << 16) | getSP()),
|
|
0,
|
|
(UCHAR) (getMSW() & MSW_PE)
|
|
);
|
|
|
|
IntNumber = *(PWORD16)(StackPointer + 12);
|
|
|
|
Handlers[IntNumber].Flags = *(PDWORD16)(StackPointer + 14);
|
|
Handlers[IntNumber].CsSelector = *(PWORD16)(StackPointer + 10);
|
|
Handlers[IntNumber].Eip = *(PDWORD16)(StackPointer + 6);
|
|
Handlers[IntNumber].SsSelector = *(PWORD16)(StackPointer + 4);
|
|
Handlers[IntNumber].Esp = *(PDWORD16)(StackPointer);
|
|
|
|
|
|
DBGTRACE(DPMI_SET_FAULT_HANDLER, IntNumber,
|
|
Handlers[IntNumber].CsSelector,
|
|
Handlers[IntNumber].Eip);
|
|
setAX(0);
|
|
}
|
|
|
|
VOID
|
|
DpmiUnhandledExceptionHandler(
|
|
VOID
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function gets control when a PM fault occurs that isn't handled
|
|
by an installed handler. The body of this function emulates Win31
|
|
DPMI behavior, where a fault that is reflected to the end of the
|
|
PM fault handler chain is then reflected to the PM *interrupt*
|
|
chain.
|
|
|
|
Arguments:
|
|
|
|
client SS:(E)SP points to dpmi fault stack frame
|
|
|
|
|
|
--*/
|
|
|
|
{
|
|
PVDM_INTERRUPTHANDLER Handlers = DpmiInterruptHandlers;
|
|
USHORT SegSs, SegCs;
|
|
UCHAR XNumber;
|
|
PCHAR VdmStackPointer;
|
|
PCHAR VdmCodePointer;
|
|
USHORT FaultingCS;
|
|
ULONG FaultingEip;
|
|
|
|
SegSs = getSS();
|
|
VdmStackPointer = Sim32GetVDMPointer(SegSs<<16, 1, TRUE);
|
|
|
|
if (SEGMENT_IS_BIG(SegSs)) {
|
|
VdmStackPointer += getESP();
|
|
} else {
|
|
VdmStackPointer += getSP();
|
|
}
|
|
|
|
SegCs = getCS();
|
|
VdmCodePointer = Sim32GetVDMPointer(SegCs<<16, 1, TRUE);
|
|
|
|
if (SEGMENT_IS_BIG(SegCs)) {
|
|
VdmCodePointer += getEIP();
|
|
} else {
|
|
VdmCodePointer += getIP();
|
|
}
|
|
|
|
XNumber = *(VdmCodePointer);
|
|
|
|
if ((XNumber > 7) || (XNumber == 6)) {
|
|
DpmiFatalExceptionHandler(XNumber, VdmStackPointer);
|
|
return;
|
|
}
|
|
|
|
|
|
if (Frame32) {
|
|
|
|
PCHAR VdmNewStackPointer;
|
|
ULONG FrameSS, FrameSP, FrameCS, FrameIP, FrameFlags;
|
|
|
|
//
|
|
// Build an iret frame on the faulting stack
|
|
//
|
|
FrameSS = *(PDWORD16) (VdmStackPointer+28);
|
|
FrameSP = *(PDWORD16) (VdmStackPointer+24) - 12;
|
|
*(PDWORD16) (VdmStackPointer+24) = FrameSP;
|
|
VdmNewStackPointer = Sim32GetVDMPointer((ULONG)(FrameSS << 16), 1, TRUE);
|
|
VdmNewStackPointer += FrameSP;
|
|
|
|
FrameIP = *(PDWORD16) (VdmStackPointer+12);
|
|
*(PDWORD16) (VdmStackPointer+12) = Handlers[XNumber].Eip;
|
|
*(PDWORD16) (VdmNewStackPointer) = FrameIP;
|
|
|
|
FrameCS = *(PDWORD16) (VdmStackPointer+16);
|
|
*(PDWORD16) (VdmStackPointer+16) = (ULONG) Handlers[XNumber].CsSelector;
|
|
*(PDWORD16) (VdmNewStackPointer+4) = FrameCS;
|
|
|
|
FrameFlags = *(PDWORD16) (VdmStackPointer+20);
|
|
*(PDWORD16) (VdmNewStackPointer+4) = FrameFlags;
|
|
FrameFlags &= ~(EFLAGS_INTERRUPT_MASK | EFLAGS_TF_MASK);
|
|
*(PDWORD16) (VdmStackPointer+20) = FrameFlags;
|
|
|
|
//
|
|
// Simulate a dpmi fault handler retf
|
|
//
|
|
setCS((USHORT)*(PDWORD16)(VdmStackPointer+4));
|
|
setEIP(*(PDWORD16)(VdmStackPointer));
|
|
setESP(getESP() + 8);
|
|
|
|
} else {
|
|
|
|
USHORT FrameSS, FrameSP, FrameCS, FrameIP, FrameFlags;
|
|
FrameSS = *(PWORD16) (VdmStackPointer+14);
|
|
FrameCS = *(PWORD16) (VdmStackPointer+8);
|
|
FrameFlags = *(PWORD16) (VdmStackPointer+10);
|
|
|
|
if (!SEGMENT_IS_BIG(FrameSS) && !SEGMENT_IS_BIG(FrameCS)) {
|
|
|
|
PCHAR VdmNewStackPointer;
|
|
|
|
//
|
|
// Build an iret frame on the faulting stack
|
|
//
|
|
FrameSP = *(PWORD16) (VdmStackPointer+12) - 6;
|
|
*(PWORD16) (VdmStackPointer+12) = FrameSP;
|
|
VdmNewStackPointer = Sim32GetVDMPointer((ULONG)(FrameSS << 16)+FrameSP, 1, TRUE);
|
|
|
|
FrameIP = *(PWORD16) (VdmStackPointer+6);
|
|
*(PWORD16) (VdmStackPointer+6) = (WORD) Handlers[XNumber].Eip;
|
|
*(PWORD16) (VdmNewStackPointer) = FrameIP;
|
|
|
|
*(PWORD16) (VdmStackPointer+8) = Handlers[XNumber].CsSelector;
|
|
*(PWORD16) (VdmNewStackPointer+2) = FrameCS;
|
|
|
|
*(PWORD16) (VdmNewStackPointer+4) = FrameFlags;
|
|
FrameFlags &= ~(EFLAGS_INTERRUPT_MASK | EFLAGS_TF_MASK);
|
|
*(PWORD16) (VdmStackPointer+10) = FrameFlags;
|
|
|
|
//
|
|
// Simulate a dpmi fault handler retf
|
|
//
|
|
setCS(*(PWORD16)(VdmStackPointer+2));
|
|
setEIP((DWORD)*(PWORD16)(VdmStackPointer));
|
|
setSP(getSP() + 4);
|
|
|
|
} else {
|
|
//
|
|
// Build an iret frame on the locked DPMI stack
|
|
//
|
|
|
|
FrameCS = *(PWORD16) (VdmStackPointer+2);
|
|
FrameIP = *(PWORD16) (VdmStackPointer);
|
|
FrameFlags &= ~EFLAGS_INTERRUPT_MASK;
|
|
|
|
setSP(getSP() - 2);
|
|
|
|
*(PWORD16)(VdmStackPointer-2) = FrameIP;
|
|
*(PWORD16)(VdmStackPointer) = FrameCS;
|
|
*(PWORD16)(VdmStackPointer+2) = FrameFlags;
|
|
setCS(Handlers[XNumber].CsSelector);
|
|
setEIP((DWORD)LOWORD(Handlers[XNumber].Eip));
|
|
setSTATUS((WORD) FrameFlags & ~EFLAGS_TF_MASK);
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
VOID
|
|
DpmiFatalExceptionHandler(
|
|
UCHAR XNumber,
|
|
PCHAR VdmStackPointer
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function gets control when a PM fault 6, 8-1f occurs that isn't
|
|
handled by an installed handler. It pops up an error dialog for the
|
|
user.
|
|
|
|
Arguments:
|
|
|
|
XNumber - exception number (0-1fh)
|
|
VdmStackPointer - flat pointer to stack frame
|
|
|
|
|
|
--*/
|
|
|
|
{
|
|
char szBuffer[255];
|
|
USHORT FaultingCS;
|
|
ULONG FaultingEip;
|
|
|
|
if (Frame32) {
|
|
FaultingCS = (USHORT)*(PDWORD16)(VdmStackPointer+16);
|
|
FaultingEip = *(PDWORD16)(VdmStackPointer+12);
|
|
} else {
|
|
FaultingCS = *(PWORD16)(VdmStackPointer+8);
|
|
FaultingEip = (ULONG)*(PWORD16)(VdmStackPointer+6);
|
|
}
|
|
|
|
wsprintf(szBuffer, "X#=%.02X, CS=%.04X IP=%.08X",
|
|
XNumber, FaultingCS, FaultingEip);
|
|
|
|
RcErrorDialogBox(EG_BAD_FAULT, szBuffer, NULL);
|
|
|
|
//
|
|
// Need to try to ignore it. Since we are on a dpmi exception frame
|
|
// we can just simulate a retf.
|
|
//
|
|
if (Frame32) {
|
|
setCS((USHORT)*(PDWORD16)(VdmStackPointer+4));
|
|
setEIP(*(PDWORD16)(VdmStackPointer));
|
|
setESP(getESP() + 8);
|
|
} else {
|
|
setCS(*(PWORD16)(VdmStackPointer+2));
|
|
setEIP((DWORD)*(PWORD16)(VdmStackPointer));
|
|
setSP(getSP() + 4);
|
|
}
|
|
}
|
|
|
|
VOID
|
|
DpmiPassPmStackInfo(
|
|
VOID
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is called via BOP by DOSX to initialize values related
|
|
to stack handling.
|
|
|
|
Arguments:
|
|
|
|
Client ES = selector of locked PM stack
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
Notes:
|
|
|
|
The offset of the locked pm stack is hard-coded to 0x1000, per dpmi
|
|
and win31.
|
|
|
|
--*/
|
|
{
|
|
|
|
LockedPMStackSel = getES();
|
|
LockedPMStackCount = 0;
|
|
|
|
#ifdef i386
|
|
{
|
|
ULONG pPmStackInfo;
|
|
VdmTib.PmStackInfo.Flags = CurrentAppFlags;
|
|
pPmStackInfo = (ULONG) &VdmTib.PmStackInfo;
|
|
|
|
setCX(HIWORD(pPmStackInfo));
|
|
setDX(LOWORD(pPmStackInfo));
|
|
}
|
|
#endif
|
|
}
|
|
|
|
VOID
|
|
BeginUseLockedPMStack(
|
|
VOID
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine switches to the protected DPMI stack as specified by
|
|
the DPMI spec. We remember the original values of EIP and ESP in
|
|
global variables if we are at level zero. This allows us to correctly
|
|
return to a 32 bit routine if we are dispatching a 16-bit interrupt
|
|
frame.
|
|
|
|
|
|
--*/
|
|
|
|
{
|
|
if (!LockedPMStackCount++) {
|
|
PMLockOrigEIP = getEIP();
|
|
PMLockOrigSS = getSS();
|
|
PMLockOrigESP = getESP();
|
|
setSS(LockedPMStackSel);
|
|
setESP(LockedPMStackOffset);
|
|
}
|
|
}
|
|
|
|
BOOL
|
|
EndUseLockedPMStack(
|
|
VOID
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine switches the stack back off the protected DPMI stack,
|
|
if we are popping off the last frame on the stack.
|
|
|
|
Return Value:
|
|
|
|
TRUE if the stack was switched back, FALSE otherwise
|
|
|
|
--*/
|
|
|
|
|
|
{
|
|
|
|
if (!--LockedPMStackCount) {
|
|
setEIP(PMLockOrigEIP);
|
|
setSS((WORD)PMLockOrigSS);
|
|
setESP(PMLockOrigESP);
|
|
return TRUE;
|
|
}
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
void
|
|
ReflectV86Int(
|
|
ULONG IntNumber,
|
|
ULONG Eflags
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is responsible for simulating a real mode interrupt. It
|
|
uses the real mode IVT at 0:0.
|
|
|
|
Arguments:
|
|
|
|
IntNumber - interrupt vector number
|
|
Eflags - client flags to save on the stack
|
|
|
|
|
|
--*/
|
|
|
|
{
|
|
PUCHAR VdmStackPointer;
|
|
PWORD16 pIVT;
|
|
USHORT VdmSP;
|
|
|
|
VdmStackPointer = Sim32GetVDMPointer(((ULONG)getSS())<<16, 1, FALSE);
|
|
VdmSP = getSP() - 2;
|
|
*(PWORD16)(VdmStackPointer+VdmSP) = (WORD) Eflags;
|
|
VdmSP -= 2;
|
|
*(PWORD16)(VdmStackPointer+VdmSP) = (WORD) getCS();
|
|
VdmSP -= 2;
|
|
*(PWORD16)(VdmStackPointer+VdmSP) = (WORD) getIP();
|
|
setSP(VdmSP);
|
|
pIVT = (PWORD16) (IntelBase + IntNumber*4);
|
|
setIP(*pIVT++);
|
|
setCS(*pIVT);
|
|
}
|
|
|
|
BOOL
|
|
DpmiSwIntHandler(
|
|
ULONG IntNumber
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is called by the emulator to dispatch a SW interrupt.
|
|
|
|
Arguments:
|
|
|
|
IntNumber - interrupt vector number
|
|
|
|
Return Value:
|
|
|
|
TRUE if the interrupt was dispatched, FALSE otherwise
|
|
|
|
--*/
|
|
|
|
{
|
|
PVDM_INTERRUPTHANDLER Handlers = DpmiInterruptHandlers;
|
|
PUCHAR VdmStackPointer;
|
|
ULONG SaveEFLAGS;
|
|
ULONG NewSP;
|
|
|
|
DBGTRACE(DPMI_SW_INT, IntNumber, 0, 0);
|
|
|
|
if (!SEGMENT_IS_PRESENT(Handlers[IntNumber].CsSelector)) {
|
|
return FALSE;
|
|
}
|
|
|
|
SaveEFLAGS = getEFLAGS();
|
|
//BUGBUG turn off task bits
|
|
SaveEFLAGS &= ~0x4000;
|
|
setEFLAGS(SaveEFLAGS & ~EFLAGS_TF_MASK);
|
|
|
|
if (!(getMSW() & MSW_PE)) {
|
|
|
|
ReflectV86Int(IntNumber, SaveEFLAGS);
|
|
|
|
} else {
|
|
PUCHAR VdmStackPointer;
|
|
|
|
// Protect mode
|
|
|
|
if (!BuildStackFrame(3, &VdmStackPointer, &NewSP)) {
|
|
return FALSE;
|
|
}
|
|
|
|
if (Frame32) {
|
|
|
|
*(PDWORD16)(VdmStackPointer-4) = SaveEFLAGS;
|
|
*(PDWORD16)(VdmStackPointer-8) = getCS();
|
|
*(PDWORD16)(VdmStackPointer-12) = getEIP();
|
|
setEIP(Handlers[IntNumber].Eip);
|
|
setESP(NewSP);
|
|
|
|
} else {
|
|
|
|
*(PWORD16)(VdmStackPointer-2) = (WORD) SaveEFLAGS;
|
|
*(PWORD16)(VdmStackPointer-4) = (WORD) getCS();
|
|
*(PWORD16)(VdmStackPointer-6) = (WORD) getEIP();
|
|
setEIP((DWORD)LOWORD(Handlers[IntNumber].Eip));
|
|
setSP((WORD)NewSP);
|
|
|
|
}
|
|
|
|
setCS(Handlers[IntNumber].CsSelector);
|
|
|
|
#if DBG
|
|
if (Handlers[IntNumber].CsSelector != getCS()) {
|
|
char szFormat[] = "NTVDM Dpmi Error! Can't set CS to %.4X\n";
|
|
char szMsg[sizeof(szFormat)+30];
|
|
|
|
wsprintf(szMsg, szFormat, Handlers[IntNumber].CsSelector);
|
|
OutputDebugString(szMsg);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
DBGTRACE(DPMI_DISPATCH_INT, IntNumber, 0, 0);
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL
|
|
DpmiHwIntHandler(
|
|
ULONG IntNumber
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is called by the emulator to dispatch a HW interrupt.
|
|
|
|
Arguments:
|
|
|
|
IntNumber - interrupt vector number
|
|
|
|
Return Value:
|
|
|
|
TRUE if the interrupt was dispatched, FALSE otherwise
|
|
|
|
--*/
|
|
|
|
{
|
|
PVDM_INTERRUPTHANDLER Handlers = DpmiInterruptHandlers;
|
|
PUCHAR VdmStackPointer;
|
|
ULONG SaveEFLAGS;
|
|
ULONG NewSP;
|
|
|
|
DBGTRACE(DPMI_HW_INT, IntNumber, 0, 0);
|
|
|
|
SaveEFLAGS = getEFLAGS();
|
|
//BUGBUG turn off task bits
|
|
SaveEFLAGS &= ~0x4000;
|
|
setEFLAGS(SaveEFLAGS & ~(EFLAGS_INTERRUPT_MASK | EFLAGS_TF_MASK));
|
|
|
|
if (!(getMSW() & MSW_PE)) {
|
|
|
|
ReflectV86Int(IntNumber, SaveEFLAGS);
|
|
|
|
} else {
|
|
PUCHAR VdmStackPointer;
|
|
|
|
BeginUseLockedPMStack();
|
|
|
|
if (!BuildStackFrame(6, &VdmStackPointer, &NewSP)) {
|
|
EndUseLockedPMStack();
|
|
return FALSE;
|
|
}
|
|
|
|
if (Frame32) {
|
|
*(PDWORD16)(VdmStackPointer-4) = SaveEFLAGS;
|
|
*(PDWORD16)(VdmStackPointer-8) = getCS();
|
|
*(PDWORD16)(VdmStackPointer-12) = getEIP();
|
|
*(PDWORD16)(VdmStackPointer-16) = getEFLAGS();
|
|
*(PDWORD16)(VdmStackPointer-20) = (DWORD)HIWORD(DosxIntHandlerIretd);
|
|
*(PDWORD16)(VdmStackPointer-24) = (DWORD)LOWORD(DosxIntHandlerIretd);
|
|
setEIP(Handlers[IntNumber].Eip);
|
|
setESP(NewSP);
|
|
} else {
|
|
*(PWORD16)(VdmStackPointer-2) = (WORD)SaveEFLAGS;
|
|
*(PWORD16)(VdmStackPointer-4) = (WORD)getCS();
|
|
*(PWORD16)(VdmStackPointer-6) = (WORD)getIP();
|
|
*(PWORD16)(VdmStackPointer-8) = (WORD)getEFLAGS();
|
|
*(PWORD16)(VdmStackPointer-10) = HIWORD(DosxIntHandlerIret);
|
|
*(PWORD16)(VdmStackPointer-12) = LOWORD(DosxIntHandlerIret);
|
|
setEIP((DWORD)LOWORD(Handlers[IntNumber].Eip));
|
|
setSP((WORD)NewSP);
|
|
}
|
|
setCS(Handlers[IntNumber].CsSelector);
|
|
}
|
|
|
|
DBGTRACE(DPMI_DISPATCH_INT, IntNumber, 0, 0);
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
VOID
|
|
DpmiIntHandlerIret16(
|
|
VOID
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is an IRET hook called via a BOP in dosx. It is called
|
|
at the end of a 16-bit HW or SW interrupt. The main reason we want
|
|
to come in here is to maintain the DPMI stack, and know when to restore
|
|
the original values when we pop back out to level zero.
|
|
|
|
|
|
--*/
|
|
|
|
{
|
|
PUCHAR VdmStackPointer;
|
|
ULONG NewSP;
|
|
USHORT SegSs;
|
|
BOOL bSsBig;
|
|
|
|
SegSs = getSS();
|
|
VdmStackPointer = Sim32GetVDMPointer(SegSs<<16, 1, TRUE);
|
|
|
|
if (bSsBig = SEGMENT_IS_BIG(SegSs)) {
|
|
VdmStackPointer += getESP();
|
|
} else {
|
|
VdmStackPointer += getSP();
|
|
}
|
|
|
|
//
|
|
// Fast iret (without executing final 16-bit iret)
|
|
//
|
|
#ifdef i386
|
|
|
|
setCS(*(PWORD16)(VdmStackPointer+2));
|
|
setEFLAGS((getEFLAGS()&0xffff0000) | *(PWORD16)(VdmStackPointer+4));
|
|
|
|
//
|
|
// if EndUseLockedPMStack fails, then we need to restore EIP and pop
|
|
// the stack frame
|
|
//
|
|
|
|
if (!EndUseLockedPMStack()) {
|
|
|
|
setEIP((DWORD)*(PWORD16)(VdmStackPointer));
|
|
|
|
//
|
|
// Pop iret frame off the stack
|
|
//
|
|
if (bSsBig) {
|
|
setESP(getESP()+6);
|
|
} else {
|
|
setSP(getSP()+6);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Slow iret (with executing final 16-bit iret)
|
|
//
|
|
#else
|
|
if (EndUseLockedPMStack()) {
|
|
ULONG NewEIP, NewEFLAGS, NewCS;
|
|
|
|
NewEIP = getEIP();
|
|
NewCS = (ULONG) *(PWORD16)(VdmStackPointer+2);
|
|
NewEFLAGS = (getEFLAGS()&0xffff0000) | *(PWORD16)(VdmStackPointer+4);
|
|
|
|
//
|
|
// Since EndUseLockedPMStack() has restored all of EIP, and we may be
|
|
// returning to a 32-bit code segment, build a 32-bit iret frame
|
|
// even if this is a 16-bit client. That way, EIP will be restored
|
|
// correctly.
|
|
// Pass 6 to BuildStackFrame since 6 words = 3 dwords
|
|
//
|
|
if (!BuildStackFrame(6, &VdmStackPointer, &NewSP)) {
|
|
#if DBG
|
|
OutputDebugString("NTVDM: Dpmi encountered a stack fault!\n");
|
|
#endif
|
|
DpmiFaultHandler(STACK_FAULT, 0);
|
|
return;
|
|
}
|
|
|
|
//
|
|
// SS has changed, so we need to check LDT again
|
|
//
|
|
if (SEGMENT_IS_BIG(getSS())) {
|
|
setESP(NewSP);
|
|
} else {
|
|
setSP((WORD)NewSP);
|
|
}
|
|
|
|
*(PDWORD16)(VdmStackPointer-4) = NewEFLAGS;
|
|
*(PDWORD16)(VdmStackPointer-8) = NewCS;
|
|
*(PDWORD16)(VdmStackPointer-12) = NewEIP;
|
|
setCS(HIWORD(DosxIretd));
|
|
setEIP((ULONG)LOWORD(DosxIretd));
|
|
|
|
} else {
|
|
|
|
// still on locked stack, just do a real iret (16-bit frame)
|
|
setCS(HIWORD(DosxIret));
|
|
setEIP((ULONG)LOWORD(DosxIret));
|
|
|
|
}
|
|
#endif // i386
|
|
|
|
DBGTRACE(DPMI_INT_IRET16, 0, 0, 0);
|
|
}
|
|
|
|
VOID
|
|
DpmiIntHandlerIret32(
|
|
VOID
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is an IRET hook called via a BOP in dosx. It is called
|
|
at the end of a 32-bit HW or SW interrupt. The main reason we want
|
|
to come in here is to maintain the DPMI stack, and know when to restore
|
|
the original values when we pop back out to level zero.
|
|
|
|
|
|
--*/
|
|
|
|
{
|
|
PUCHAR VdmStackPointer;
|
|
ULONG NewSP;
|
|
USHORT SegSs;
|
|
BOOL bSsBig;
|
|
|
|
SegSs = getSS();
|
|
VdmStackPointer = Sim32GetVDMPointer(SegSs<<16, 1, TRUE);
|
|
|
|
if (bSsBig = SEGMENT_IS_BIG(SegSs)) {
|
|
VdmStackPointer += getESP();
|
|
} else {
|
|
VdmStackPointer += getSP();
|
|
}
|
|
|
|
#ifdef i386
|
|
|
|
setCS(*(PDWORD16)(VdmStackPointer+4));
|
|
setEFLAGS(*(PDWORD16)(VdmStackPointer+8));
|
|
|
|
//
|
|
// if EndUseLockedPMStack succeeds, then we don't need to restore EIP
|
|
//
|
|
|
|
if (!EndUseLockedPMStack()) {
|
|
|
|
setEIP(*(PDWORD16)(VdmStackPointer));
|
|
|
|
//
|
|
// Pop iret frame off the stack
|
|
//
|
|
if (bSsBig) {
|
|
setESP(getESP()+12);
|
|
} else {
|
|
setSP(getSP()+12);
|
|
}
|
|
}
|
|
|
|
#else
|
|
if (EndUseLockedPMStack()) {
|
|
ULONG NewEIP, NewCS, NewEFLAGS;
|
|
|
|
NewEIP = getEIP();
|
|
NewCS = *(PDWORD16)(VdmStackPointer+4);
|
|
NewEFLAGS = *(PDWORD16)(VdmStackPointer+8);
|
|
|
|
|
|
if (!BuildStackFrame(3, &VdmStackPointer, &NewSP)) {
|
|
#if DBG
|
|
OutputDebugString("NTVDM: Dpmi encountered a stack fault!\n");
|
|
#endif
|
|
DpmiFaultHandler(STACK_FAULT, 0);
|
|
return;
|
|
}
|
|
|
|
//
|
|
// SS has changed, so we need to check LDT again
|
|
//
|
|
if (SEGMENT_IS_BIG(getSS())) {
|
|
setESP(NewSP);
|
|
} else {
|
|
setSP((WORD)NewSP);
|
|
}
|
|
|
|
*(PDWORD16)(VdmStackPointer-4) = NewEFLAGS;
|
|
*(PDWORD16)(VdmStackPointer-8) = NewCS;
|
|
*(PDWORD16)(VdmStackPointer-12) = NewEIP;
|
|
}
|
|
|
|
setCS(HIWORD(DosxIretd));
|
|
setEIP((ULONG)LOWORD(DosxIretd));
|
|
#endif // i386
|
|
|
|
DBGTRACE(DPMI_INT_IRET32, 0, 0, 0);
|
|
}
|
|
|
|
#ifndef i386
|
|
|
|
BOOL
|
|
DpmiFaultHandler(
|
|
ULONG IntNumber,
|
|
ULONG ErrorCode
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is called by the emulator when an exception occurs.
|
|
|
|
Arguments:
|
|
|
|
IntNumber - exception number (0-1f)
|
|
ErrorCode - exception error code to be placed on the stack
|
|
|
|
Return Value:
|
|
|
|
TRUE if the interrupt was dispatched, FALSE otherwise
|
|
|
|
--*/
|
|
|
|
{
|
|
PVDM_FAULTHANDLER Handlers = DpmiFaultHandlers;
|
|
PUCHAR VdmStackPointer;
|
|
ULONG SaveSS, SaveESP, SaveEFLAGS, SaveCS, SaveEIP;
|
|
ULONG StackOffset;
|
|
ULONG NewSP;
|
|
|
|
DBGTRACE(DPMI_FAULT, IntNumber, ErrorCode, 0);
|
|
|
|
SaveSS = getSS();
|
|
SaveESP = getESP();
|
|
SaveEFLAGS = getEFLAGS();
|
|
SaveEIP = getEIP();
|
|
SaveCS = getCS();
|
|
setEFLAGS(SaveEFLAGS & ~(EFLAGS_INTERRUPT_MASK | EFLAGS_TF_MASK));
|
|
|
|
if (!(getMSW() & MSW_PE)) {
|
|
ReflectV86Int(IntNumber, getEFLAGS());
|
|
return TRUE;
|
|
}
|
|
|
|
if ((IntNumber == 13) || (IntNumber == 6)) {
|
|
if (DpmiEmulateInstruction()) {
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
if (!SEGMENT_IS_PRESENT(Handlers[IntNumber].CsSelector)) {
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// switch stacks
|
|
//
|
|
|
|
BeginUseLockedPMStack();
|
|
|
|
//
|
|
// Win31 has an undocumented feature of creating a 32byte area on the
|
|
// stack. Krnl386 sticks stuff in there, so we emulate the behavior here.
|
|
//
|
|
|
|
setESP(getESP()-0x20);
|
|
|
|
//
|
|
// allocate space on new stack
|
|
//
|
|
|
|
if (!BuildStackFrame(8, &VdmStackPointer, &NewSP)) {
|
|
//BUGBUG Check for double fault
|
|
EndUseLockedPMStack();
|
|
return FALSE;
|
|
}
|
|
|
|
if (Frame32) {
|
|
*(PDWORD16)(VdmStackPointer-4) = SaveSS;
|
|
*(PDWORD16)(VdmStackPointer-8) = SaveESP;
|
|
*(PDWORD16)(VdmStackPointer-12) = SaveEFLAGS;
|
|
*(PDWORD16)(VdmStackPointer-16) = SaveCS;
|
|
*(PDWORD16)(VdmStackPointer-20) = SaveEIP;
|
|
*(PDWORD16)(VdmStackPointer-24) = ErrorCode;
|
|
*(PDWORD16)(VdmStackPointer-28) = (ULONG) HIWORD(DosxFaultHandlerIretd);
|
|
*(PDWORD16)(VdmStackPointer-32) = (ULONG) LOWORD(DosxFaultHandlerIretd);
|
|
setEIP(Handlers[IntNumber].Eip);
|
|
setESP(NewSP);
|
|
} else {
|
|
*(PWORD16)(VdmStackPointer-2) = (WORD) SaveSS;
|
|
*(PWORD16)(VdmStackPointer-4) = (WORD) SaveESP;
|
|
*(PWORD16)(VdmStackPointer-6) = (WORD) SaveEFLAGS;
|
|
*(PWORD16)(VdmStackPointer-8) = (WORD) SaveCS;
|
|
*(PWORD16)(VdmStackPointer-10) = (WORD) SaveEIP;
|
|
*(PWORD16)(VdmStackPointer-12) = (WORD) ErrorCode;
|
|
*(PDWORD16)(VdmStackPointer-16) = DosxFaultHandlerIret;
|
|
setEIP(LOWORD(Handlers[IntNumber].Eip));
|
|
setSP((WORD)NewSP);
|
|
}
|
|
|
|
setCS(Handlers[IntNumber].CsSelector);
|
|
|
|
#if DBG
|
|
if (Handlers[IntNumber].CsSelector != getCS()) {
|
|
char szFormat[] = "NTVDM Dpmi Error! Can't set CS to %.4X\n";
|
|
char szMsg[sizeof(szFormat)+30];
|
|
|
|
wsprintf(szMsg, szFormat, Handlers[IntNumber].CsSelector);
|
|
OutputDebugString(szMsg);
|
|
}
|
|
#endif
|
|
|
|
DBGTRACE(DPMI_DISPATCH_FAULT, IntNumber, 0, 0);
|
|
return TRUE;
|
|
}
|
|
|
|
#endif // i386
|
|
|
|
VOID
|
|
DpmiFaultHandlerIret16(
|
|
VOID
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is an IRET hook called via a BOP in dosx. It is called
|
|
at the end of the execution of a 16-bit fault handler.
|
|
|
|
|
|
--*/
|
|
|
|
{
|
|
PUCHAR VdmStackPointer;
|
|
USHORT SegSs;
|
|
|
|
SegSs = getSS();
|
|
VdmStackPointer = Sim32GetVDMPointer(SegSs<<16, 1, TRUE);
|
|
if (SEGMENT_IS_BIG(SegSs)) {
|
|
VdmStackPointer += getESP();
|
|
} else {
|
|
VdmStackPointer += getSP();
|
|
}
|
|
|
|
EndUseLockedPMStack();
|
|
|
|
setEIP((DWORD)*(PWORD16)(VdmStackPointer+2));
|
|
setCS(*(PWORD16)(VdmStackPointer+4));
|
|
setSTATUS(*(PWORD16)(VdmStackPointer+6));
|
|
setSP(*(PWORD16)(VdmStackPointer+8));
|
|
setSS(*(PWORD16)(VdmStackPointer+10));
|
|
|
|
DBGTRACE(DPMI_FAULT_IRET, 0, 0, 0);
|
|
}
|
|
|
|
VOID
|
|
DpmiFaultHandlerIret32(
|
|
VOID
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is an IRET hook called via a BOP in dosx. It is called
|
|
at the end of the execution of a 32-bit fault handler.
|
|
|
|
|
|
--*/
|
|
|
|
{
|
|
PUCHAR VdmStackPointer;
|
|
USHORT SegSs;
|
|
|
|
SegSs = getSS();
|
|
VdmStackPointer = Sim32GetVDMPointer(SegSs<<16, 1, TRUE);
|
|
if (SEGMENT_IS_BIG(SegSs)) {
|
|
VdmStackPointer += getESP();
|
|
} else {
|
|
VdmStackPointer += getSP();
|
|
}
|
|
|
|
|
|
EndUseLockedPMStack();
|
|
|
|
setEIP(*(PDWORD16)(VdmStackPointer+4));
|
|
setCS((USHORT)*(PDWORD16)(VdmStackPointer+8));
|
|
setEFLAGS(*(PDWORD16)(VdmStackPointer+12));
|
|
setESP(*(PDWORD16)(VdmStackPointer+16));
|
|
setSS((USHORT)*(PWORD16)(VdmStackPointer+20));
|
|
|
|
DBGTRACE(DPMI_FAULT_IRET, 0, 0, 0);
|
|
}
|
|
|
|
|
|
BOOL
|
|
BuildStackFrame(
|
|
ULONG StackUnits,
|
|
PUCHAR *pVdmStackPointer,
|
|
ULONG *pNewSP
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine builds stack frames for the caller. It figures if it needs
|
|
to use a 16 or 32-bit frame, and adjusts SP or ESP appropriately based
|
|
on the number of "stack units". It also returns a flat pointer to the
|
|
top of the frame to the caller.
|
|
|
|
Arguments:
|
|
|
|
StackUnits = number of registers needed to be saved on the frame. For
|
|
example, 3 is how many elements there are on an iret frame
|
|
(flags, cs, ip)
|
|
|
|
Return Value:
|
|
|
|
This function returns TRUE on success, FALSE on failure
|
|
|
|
VdmStackPointer - flat address pointing to the "top" of the frame
|
|
|
|
Notes:
|
|
|
|
BUGBUG This routine doesn't check for stack faults or 'UP' direction
|
|
stacks
|
|
--*/
|
|
|
|
{
|
|
USHORT SegSs;
|
|
ULONG VdmSp;
|
|
PUCHAR VdmStackPointer;
|
|
ULONG StackOffset;
|
|
ULONG Limit;
|
|
ULONG SelIndex;
|
|
ULONG NewSP;
|
|
BOOL bExpandDown;
|
|
|
|
SegSs = getSS();
|
|
SelIndex = (SegSs & ~0x7)/sizeof(LDT_ENTRY);
|
|
|
|
Limit = (ULONG) (Ldt[SelIndex].HighWord.Bits.LimitHi << 16) |
|
|
Ldt[SelIndex].LimitLow;
|
|
|
|
//
|
|
//
|
|
// bugbug is this really correct?
|
|
Limit++;
|
|
if (Ldt[SelIndex].HighWord.Bits.Granularity) {
|
|
Limit = (Limit << 12) | 0xfff;
|
|
}
|
|
|
|
|
|
if (Ldt[SelIndex].HighWord.Bits.Default_Big) {
|
|
VdmSp = getESP();
|
|
} else {
|
|
VdmSp = getSP();
|
|
}
|
|
|
|
if (CurrentAppFlags) {
|
|
StackOffset = StackUnits*sizeof(DWORD);
|
|
} else {
|
|
StackOffset = StackUnits*sizeof(WORD);
|
|
}
|
|
|
|
NewSP = VdmSp - StackOffset;
|
|
bExpandDown = (BOOL) (Ldt[SelIndex].HighWord.Bits.Type & 4);
|
|
if ((StackOffset > VdmSp) ||
|
|
(!bExpandDown && (VdmSp > Limit)) ||
|
|
(bExpandDown && (NewSP < Limit))) {
|
|
// failed limit check
|
|
return FALSE;
|
|
}
|
|
|
|
*pNewSP = NewSP;
|
|
VdmStackPointer = Sim32GetVDMPointer(((ULONG)SegSs)<<16, 1, TRUE);
|
|
VdmStackPointer += VdmSp;
|
|
*pVdmStackPointer = VdmStackPointer;
|
|
return(TRUE);
|
|
|
|
}
|
|
|
|
VOID
|
|
EnableIntHooks(
|
|
VOID
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is called during startup to install our handlers with
|
|
the emulator.
|
|
|
|
Arguments:
|
|
|
|
None
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
#ifndef i386
|
|
if (fDpmiHookInts) {
|
|
VdmInstallHardwareIntHandler(DpmiHwIntHandler);
|
|
VdmInstallSoftwareIntHandler(DpmiSwIntHandler);
|
|
VdmInstallFaultHandler(DpmiFaultHandler);
|
|
fDpmiIntsHaveBeenHooked = TRUE;
|
|
}
|
|
#endif // i386
|
|
}
|
|
|
|
|
|
VOID
|
|
DisableIntHooks(
|
|
VOID
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is called to uninstall the our interrupt and exception
|
|
handlers from the emulator.
|
|
|
|
Arguments:
|
|
|
|
None
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
#ifndef i386
|
|
if (fDpmiIntsHaveBeenHooked) {
|
|
VdmInstallHardwareIntHandler(NULL);
|
|
VdmInstallSoftwareIntHandler(NULL);
|
|
VdmInstallFaultHandler(NULL);
|
|
fDpmiIntsHaveBeenHooked = FALSE;
|
|
}
|
|
#endif // i386
|
|
}
|
|
|
|
|
|
BOOL
|
|
CheckEIP(
|
|
ULONG Increment
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine does a limit check on EIP.
|
|
|
|
Arguments:
|
|
|
|
None
|
|
|
|
Return Value:
|
|
|
|
TRUE if EIP is ok, FALSE otherwise
|
|
|
|
--*/
|
|
|
|
{
|
|
//BUGBUG NEED TO RETURN FALSE HERE IF EIP WOULD BE OFF THE END OF SEGMENT
|
|
return TRUE;
|
|
}
|
|
|
|
#ifndef i386
|
|
BOOL
|
|
DpmiEmulateInstruction(
|
|
VOID
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine checks to see if the instruction which caused the
|
|
fault really needs to be emulated. For example, the MS C compiler (v7.00)
|
|
uses instructions to manipulate the FP flags in CR0. The compiler
|
|
expects them to just work as they do on win31, which also emulates them.
|
|
|
|
Arguments:
|
|
|
|
None
|
|
|
|
Return Value:
|
|
|
|
TRUE if the instruction was emulated, FALSE otherwise
|
|
|
|
--*/
|
|
|
|
{
|
|
PUCHAR pCode;
|
|
UCHAR Opcode;
|
|
ULONG SegCS;
|
|
BOOL bReturn = FALSE;
|
|
|
|
SegCS = getCS();
|
|
pCode = Sim32GetVDMPointer(SegCS<<16, 1, TRUE);
|
|
|
|
if (Ldt[(SegCS & ~0x7)/sizeof(LDT_ENTRY)].HighWord.Bits.Default_Big) {
|
|
pCode += getEIP();
|
|
} else {
|
|
pCode += getIP();
|
|
}
|
|
|
|
Opcode = *pCode++;
|
|
switch (Opcode) {
|
|
case 0xf:
|
|
if (!CheckEIP(1)) {
|
|
break;
|
|
}
|
|
bReturn = DpmiOp0f(pCode);
|
|
break;
|
|
|
|
case 0x8e:
|
|
//
|
|
// This is WIN31 compatibility. If we are trying to dispatch
|
|
// the client, and we get a fault loading the segment registers,
|
|
// then zero them out.
|
|
// BUGBUG currently only looking for FS, GS
|
|
//
|
|
if (!CheckEIP(2)) {
|
|
break;
|
|
}
|
|
//
|
|
// Look for code in dxutil.asm EnterProtectedMode
|
|
//
|
|
if ((SegCS == DosxRmCodeSelector) &&
|
|
((*pCode == 0xe0) || // mov fs, ax
|
|
(*pCode == 0xe8)) // mov gs, ax
|
|
) {
|
|
setEIP(getEIP()+2);
|
|
bReturn = TRUE;
|
|
}
|
|
break;
|
|
}
|
|
|
|
DBGTRACE(DPMI_OP_EMULATION, Opcode, (ULONG) bReturn, 0);
|
|
return bReturn;
|
|
}
|
|
|
|
#define MI_GET_CRx_OPCODE 0x20
|
|
#define MI_SET_CRx_OPCODE 0x22
|
|
#define MI_MODMASK 0xC0
|
|
#define MI_MODMOVSPEC 0xC0
|
|
#define MI_REGMASK 0x38
|
|
#define MI_RMMASK 0x7
|
|
|
|
BOOL
|
|
DpmiOp0f(
|
|
PUCHAR pCode
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine emulates instructions that have 0x0F as the first byte.
|
|
|
|
Arguments:
|
|
|
|
None
|
|
|
|
Return Value:
|
|
|
|
TRUE if the instruction was emulated, FALSE otherwise
|
|
|
|
--*/
|
|
{
|
|
ULONG Value;
|
|
|
|
switch (*pCode++) {
|
|
case MI_GET_CRx_OPCODE:
|
|
|
|
if (!CheckEIP(2)) {
|
|
break;
|
|
}
|
|
|
|
if ((*pCode & MI_MODMASK) != MI_MODMOVSPEC) {
|
|
break;
|
|
}
|
|
|
|
if (*pCode & MI_REGMASK) {
|
|
Value = 0; // not CR0
|
|
} else {
|
|
Value = getCR0();
|
|
}
|
|
|
|
SetRegisterByIndex[*pCode & MI_RMMASK](Value);
|
|
setEIP(getEIP()+3);
|
|
return TRUE;
|
|
|
|
case MI_SET_CRx_OPCODE:
|
|
|
|
if (!CheckEIP(2)) {
|
|
break;
|
|
}
|
|
|
|
if ((*pCode & MI_MODMASK) != MI_MODMOVSPEC) {
|
|
break;
|
|
}
|
|
|
|
if (*pCode & MI_REGMASK) {
|
|
break; // not CR0
|
|
}
|
|
|
|
setCR0(GetRegisterByIndex[*pCode & MI_RMMASK]());
|
|
setEIP(getEIP()+3);
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
#endif
|