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.
553 lines
12 KiB
553 lines
12 KiB
/*++
|
|
|
|
Copyright (c) 1992 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
Stack.c
|
|
|
|
Abstract:
|
|
|
|
This module implements routines for manipulating the 16 bit stack
|
|
|
|
Author:
|
|
|
|
Dave Hastings (daveh) 24-Nov-1992
|
|
|
|
Revision History:
|
|
|
|
--*/
|
|
#include "precomp.h"
|
|
#pragma hdrstop
|
|
#include "softpc.h"
|
|
|
|
#if 0 // Disable the code for now
|
|
VOID
|
|
FreePMStack(
|
|
USHORT Sel
|
|
);
|
|
|
|
USHORT
|
|
AllocatePMStack(
|
|
USHORT MemSize
|
|
);
|
|
|
|
#endif
|
|
|
|
VOID
|
|
DpmiPushRmInt(
|
|
USHORT InterruptNumber
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine pushes an interrupt frame on the stack and sets up cs:ip
|
|
for the specified interrupt.
|
|
|
|
Arguments:
|
|
|
|
InterruptNumber -- Specifies the index of the interrupt
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
DECLARE_LocalVdmContext;
|
|
PWORD16 StackPointer;
|
|
ULONG IntHandler;
|
|
|
|
// bugbug stack wrap???
|
|
|
|
ASSERT((getSP() > 6));
|
|
ASSERT((!(getMSW() & MSW_PE)));
|
|
|
|
StackPointer = (PWORD16)VdmMapFlat(getSS(), getSP(), VDM_V86);
|
|
|
|
*(StackPointer - 3) = (USHORT)(RmBopFe & 0x0000FFFF);
|
|
*(StackPointer - 2) = (USHORT)(RmBopFe >> 16);
|
|
*(StackPointer - 1) = getSTATUS();
|
|
|
|
setSP(getSP() - 6);
|
|
|
|
IntHandler = *(PDWORD16) (IntelBase + InterruptNumber*4);
|
|
setIP(LOWORD(IntHandler));
|
|
setCS(HIWORD(IntHandler));
|
|
}
|
|
|
|
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.
|
|
|
|
|
|
--*/
|
|
|
|
{
|
|
DECLARE_LocalVdmContext;
|
|
#if 0 // Disabled for now
|
|
if (LockedPMStackSel == 0) {
|
|
LockedPMStackSel = AllocatePMStack(LockedPMStackOffset); // LockedPMStackOffset is acturally the size
|
|
LockedPMStackCount = 0;
|
|
|
|
//
|
|
// Note the stack allocation may still fail. In this case, the setSS() will set SS selector
|
|
// to zero and the error will be catched during BuildStackFrame() call
|
|
//
|
|
|
|
}
|
|
#endif
|
|
if (!LockedPMStackCount++) {
|
|
|
|
DBGTRACE(VDMTR_TYPE_DPMI | DPMI_SWITCH_STACKS, (USHORT)LockedPMStackSel, LockedPMStackOffset);
|
|
|
|
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
|
|
|
|
--*/
|
|
|
|
|
|
{
|
|
DECLARE_LocalVdmContext;
|
|
|
|
if (!--LockedPMStackCount) {
|
|
|
|
//
|
|
// We probably should free the PM stack except the one passed from DOSX??
|
|
//
|
|
|
|
DBGTRACE(VDMTR_TYPE_DPMI | DPMI_SWITCH_STACKS, (USHORT)PMLockOrigSS, PMLockOrigESP);
|
|
|
|
setEIP(PMLockOrigEIP);
|
|
setSS((WORD)PMLockOrigSS);
|
|
setESP(PMLockOrigESP);
|
|
return TRUE;
|
|
}
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
|
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
|
|
--*/
|
|
|
|
{
|
|
DECLARE_LocalVdmContext;
|
|
USHORT SegSs;
|
|
ULONG VdmSp;
|
|
PUCHAR VdmStackPointer;
|
|
ULONG StackOffset;
|
|
ULONG Limit;
|
|
ULONG SelIndex;
|
|
ULONG NewSP;
|
|
BOOL bExpandDown;
|
|
BOOL rc;
|
|
|
|
rc = TRUE;
|
|
SegSs = getSS();
|
|
SelIndex = (SegSs & ~0x7)/sizeof(LDT_ENTRY);
|
|
if ((USHORT)SelIndex > (LdtMaxSel >> 3)) {
|
|
ASSERT(0);
|
|
return FALSE;
|
|
}
|
|
Limit = (ULONG) (Ldt[SelIndex].HighWord.Bits.LimitHi << 16) |
|
|
Ldt[SelIndex].LimitLow;
|
|
|
|
//
|
|
// Make it paged aligned if not 4G size stack.
|
|
//
|
|
if (Ldt[SelIndex].HighWord.Bits.Granularity) {
|
|
Limit = (Limit << 12) | 0xfff;
|
|
}
|
|
if (Limit != 0xffffffff) Limit++;
|
|
|
|
|
|
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
|
|
ASSERT(0);
|
|
rc = FALSE;
|
|
}
|
|
|
|
*pNewSP = NewSP;
|
|
VdmStackPointer = VdmMapFlat(SegSs, VdmSp, VDM_PM);
|
|
*pVdmStackPointer = VdmStackPointer;
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
|
VOID
|
|
EmulateV86Int(
|
|
UCHAR InterruptNumber
|
|
)
|
|
/*++
|
|
|
|
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
|
|
|
|
|
|
--*/
|
|
|
|
{
|
|
DECLARE_LocalVdmContext;
|
|
PVDM_INTERRUPTHANDLER Handlers = DpmiInterruptHandlers;
|
|
PUCHAR VdmStackPointer;
|
|
PWORD16 pIVT;
|
|
USHORT VdmSP;
|
|
USHORT NewCS;
|
|
USHORT NewIP;
|
|
ULONG Eflags = getEFLAGS();
|
|
|
|
VdmStackPointer = VdmMapFlat(getSS(), 0, VDM_V86);
|
|
VdmSP = getSP() - 2;
|
|
*(PWORD16)(VdmStackPointer+VdmSP) = (WORD) Eflags;
|
|
VdmSP -= 2;
|
|
*(PWORD16)(VdmStackPointer+VdmSP) = (WORD) getCS();
|
|
VdmSP -= 2;
|
|
*(PWORD16)(VdmStackPointer+VdmSP) = (WORD) getIP();
|
|
setSP(VdmSP);
|
|
|
|
//
|
|
// See if this interrupt is hooked in protect mode, and if we should
|
|
// reflect there instead.
|
|
//
|
|
if (Handlers[InterruptNumber].Flags & VDM_INT_HOOKED) {
|
|
NewCS = (USHORT) (DosxRMReflector >> 16);
|
|
NewIP = (USHORT) DosxRMReflector;
|
|
//
|
|
// now encode the interrupt number into CS
|
|
//
|
|
NewCS -= (USHORT) InterruptNumber;
|
|
NewIP += (USHORT) (InterruptNumber*16);
|
|
} else {
|
|
PWORD16 pIvtEntry = (PWORD16) (IntelBase + InterruptNumber*4);
|
|
|
|
NewIP = *pIvtEntry++;
|
|
NewCS = *pIvtEntry;
|
|
}
|
|
|
|
setIP(NewIP);
|
|
setCS(NewCS);
|
|
//
|
|
// Turn off flags like the hardware would
|
|
//
|
|
setEFLAGS(Eflags & ~(EFLAGS_TF_MASK | EFLAGS_IF_MASK));
|
|
}
|
|
|
|
|
|
VOID
|
|
SimulateFarCall(
|
|
USHORT Seg,
|
|
ULONG Offset
|
|
)
|
|
{
|
|
DECLARE_LocalVdmContext;
|
|
PUCHAR VdmStackPointer;
|
|
USHORT VdmSP;
|
|
|
|
if (getMODE() == VDM_V86) {
|
|
|
|
VdmStackPointer = VdmMapFlat(getSS(), 0, VDM_V86);
|
|
VdmSP = getSP() - 2;
|
|
*(PWORD16)(VdmStackPointer+VdmSP) = (WORD) getCS();
|
|
VdmSP -= 2;
|
|
*(PWORD16)(VdmStackPointer+VdmSP) = (WORD) getIP();
|
|
setSP(VdmSP);
|
|
setCS(Seg);
|
|
setIP((USHORT)Offset);
|
|
|
|
} else {
|
|
DbgBreakPoint();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
VOID
|
|
SimulateCallWithIretFrame(
|
|
USHORT Seg,
|
|
ULONG Offset
|
|
)
|
|
{
|
|
DECLARE_LocalVdmContext;
|
|
PUCHAR VdmStackPointer;
|
|
USHORT VdmSP;
|
|
|
|
if (getMODE() == VDM_V86) {
|
|
|
|
VdmStackPointer = VdmMapFlat(getSS(), 0, VDM_V86);
|
|
VdmSP = getSP() - 2;
|
|
*(PWORD16)(VdmStackPointer+VdmSP) = (WORD) getEFLAGS();
|
|
VdmSP -= 2;
|
|
*(PWORD16)(VdmStackPointer+VdmSP) = (WORD) getCS();
|
|
VdmSP -= 2;
|
|
*(PWORD16)(VdmStackPointer+VdmSP) = (WORD) getIP();
|
|
setSP(VdmSP);
|
|
setCS(Seg);
|
|
setIP((USHORT)Offset);
|
|
|
|
} else {
|
|
DbgBreakPoint();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
VOID
|
|
SimulateIret(
|
|
IRET_BEHAVIOR fdsp
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine simulates an IRET. The passed parameter specifies
|
|
how the flags are to be treated. In many situations, we pass the
|
|
value of the flags along, thus throwing away the flags on the stack.
|
|
|
|
In the case of PASS_FLAGS, we:
|
|
- clear all but the interrupt and trace flags in the caller's
|
|
original flags
|
|
- 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.
|
|
|
|
|
|
Arguments:
|
|
|
|
fdsp - takes the value RESTORE_FLAGS, PASS_FLAGS or PASS_CARRY_FLAG
|
|
|
|
PASS_CARRY_FLAG_16 is a special value to indicate that this
|
|
iret will always be on a 16-bit iret frame.
|
|
|
|
|
|
--*/
|
|
{
|
|
DECLARE_LocalVdmContext;
|
|
USHORT SegSs;
|
|
ULONG VdmSp;
|
|
ULONG VdmStackPointer;
|
|
USHORT Flags;
|
|
|
|
SegSs = getSS();
|
|
|
|
if (getMODE() == VDM_V86) {
|
|
VdmSp = getSP() + 6;
|
|
VdmStackPointer = (ULONG) VdmMapFlat(SegSs, VdmSp, VDM_V86);
|
|
|
|
setCS(*(PWORD16)(VdmStackPointer - 4));
|
|
setIP(*(PWORD16)(VdmStackPointer - 6));
|
|
Flags = *(PWORD16)(VdmStackPointer - 2);
|
|
|
|
} else {
|
|
if (Frame32 && (fdsp!=PASS_CARRY_FLAG_16)) {
|
|
if (SEGMENT_IS_BIG(SegSs)) {
|
|
VdmSp = getESP();
|
|
} else {
|
|
VdmSp = getSP();
|
|
}
|
|
VdmSp += 12;
|
|
VdmStackPointer = (ULONG) VdmMapFlat(SegSs, VdmSp, VDM_PM);
|
|
|
|
setCS(*(PWORD16)(VdmStackPointer - 8));
|
|
setEIP(*(PDWORD16)(VdmStackPointer - 12));
|
|
Flags = *(PWORD16)(VdmStackPointer - 4);
|
|
|
|
} else {
|
|
|
|
VdmSp = getSP() + 6;
|
|
VdmStackPointer = (ULONG) VdmMapFlat(SegSs, VdmSp, VDM_PM);
|
|
|
|
setCS(*(PWORD16)(VdmStackPointer - 4));
|
|
setIP(*(PWORD16)(VdmStackPointer - 6));
|
|
Flags = *(PWORD16)(VdmStackPointer - 2);
|
|
|
|
}
|
|
}
|
|
|
|
switch(fdsp) {
|
|
|
|
case RESTORE_FLAGS:
|
|
break;
|
|
|
|
case PASS_FLAGS:
|
|
Flags = (Flags & 0x300) | getSTATUS();
|
|
break;
|
|
|
|
case PASS_CARRY_FLAG:
|
|
case PASS_CARRY_FLAG_16:
|
|
Flags = (Flags & ~1) | (getSTATUS() & 1);
|
|
break;
|
|
}
|
|
|
|
setSTATUS(Flags);
|
|
setESP(VdmSp);
|
|
}
|
|
|
|
#if 0 // Disable the code for now
|
|
|
|
USHORT
|
|
AllocatePMStack(
|
|
USHORT MemSize
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine allocates PM stack.
|
|
|
|
Arguments:
|
|
|
|
MemSize - Must be less than 64k
|
|
|
|
Return Value:
|
|
|
|
if successful, selector of the PM stack
|
|
otherwise 0
|
|
|
|
--*/
|
|
{
|
|
PMEM_DPMI pMem;
|
|
|
|
pMem = DpmiAllocateXmem(MemSize);
|
|
|
|
if (pMem) {
|
|
|
|
pMem->SelCount = 1;
|
|
pMem->Sel = ALLOCATE_SELECTORS(1);
|
|
|
|
if (!pMem->Sel) {
|
|
pMem->SelCount = 0;
|
|
DpmiFreeXmem(pMem);
|
|
pMem = NULL;
|
|
} else {
|
|
|
|
SetDescriptorArray(pMem->Sel, (ULONG)pMem->Address, MemSize);
|
|
|
|
}
|
|
}
|
|
if (pMem) {
|
|
return pMem->Sel;
|
|
} else {
|
|
return (USHORT)0;
|
|
}
|
|
}
|
|
|
|
VOID
|
|
FreePMStack(
|
|
USHORT Sel
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine releases PM stack
|
|
|
|
Arguments:
|
|
|
|
Sel - Selector of the PM stack to be freed.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
PMEM_DPMI pMem;
|
|
|
|
if (pMem = DpmiFindXmem(Sel)) {
|
|
|
|
while(pMem->SelCount--) {
|
|
FreeSelector(Sel);
|
|
Sel+=8;
|
|
}
|
|
|
|
DpmiFreeXmem(pMem);
|
|
}
|
|
}
|
|
#endif
|