469 lines
7.6 KiB
C
469 lines
7.6 KiB
C
/*++
|
||
|
||
Copyright (c) 1994 Microsoft Corporation
|
||
|
||
Module Name:
|
||
|
||
ctrlops.c
|
||
|
||
Abstract:
|
||
|
||
This module implements the code to emulate call, retunr, and various
|
||
control operations.
|
||
|
||
Author:
|
||
|
||
David N. Cutler (davec) 10-Nov-1994
|
||
|
||
Environment:
|
||
|
||
Kernel mode only.
|
||
|
||
Revision History:
|
||
|
||
--*/
|
||
|
||
#include "nthal.h"
|
||
#include "emulate.h"
|
||
|
||
VOID
|
||
XmCallOp (
|
||
PRXM_CONTEXT P
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function emulates a call opcode.
|
||
|
||
Arguments:
|
||
|
||
P - Supplies a pointer to an emulator context structure.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
|
||
ULONG Target;
|
||
ULONG Source;
|
||
|
||
//
|
||
// Save the target address, push the current segment, if required, and
|
||
// push the current IP, set the destination segment, if required, and
|
||
// set the new IP.
|
||
//
|
||
|
||
Target = P->DstValue.Long;
|
||
if (P->OpsizePrefixActive != FALSE) {
|
||
P->DataType = LONG_DATA;
|
||
|
||
} else {
|
||
P->DataType = WORD_DATA;
|
||
}
|
||
|
||
if ((P->CurrentOpcode == 0x9a) || (P->FunctionIndex != X86_CALL_OP)) {
|
||
XmPushStack(P, P->SegmentRegister[CS]);
|
||
XmPushStack(P, P->Eip);
|
||
P->SegmentRegister[CS] = P->DstSegment;
|
||
|
||
} else {
|
||
XmPushStack(P, P->Eip);
|
||
}
|
||
|
||
P->Eip = Target;
|
||
XmTraceJumps(P);
|
||
return;
|
||
}
|
||
|
||
VOID
|
||
XmEnterOp (
|
||
PRXM_CONTEXT P
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function emulates an enter opcode.
|
||
|
||
Arguments:
|
||
|
||
P - Supplies a pointer to an emulator context structure.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
|
||
ULONG Allocate;
|
||
ULONG Frame;
|
||
ULONG Number;
|
||
|
||
//
|
||
// set the number of bytes to allocate on the stack and the number
|
||
// of nesting levels.
|
||
//
|
||
|
||
Allocate = P->SrcValue.Long;
|
||
Number = P->DstValue.Long;
|
||
|
||
//
|
||
// Set the data type and save the frame pointer on the stack.
|
||
//
|
||
|
||
if (P->OpsizePrefixActive != FALSE) {
|
||
P->DataType = LONG_DATA;
|
||
XmPushStack(P, P->Gpr[EBP].Exx);
|
||
Frame = P->Gpr[ESP].Exx;
|
||
|
||
} else {
|
||
P->DataType = WORD_DATA;
|
||
XmPushStack(P, P->Gpr[BP].Xx);
|
||
Frame = P->Gpr[SP].Xx;
|
||
}
|
||
|
||
//
|
||
// Save the current stack pointer and push parameters on the stack.
|
||
//
|
||
|
||
if (Number != 0) {
|
||
|
||
//
|
||
// If the level number is not one, then raise an exception.
|
||
//
|
||
// N.B. Level numbers greater than one are not supported.
|
||
//
|
||
|
||
if (Number != 1) {
|
||
longjmp(&P->JumpBuffer[0], XM_ILLEGAL_LEVEL_NUMBER);
|
||
}
|
||
|
||
XmPushStack(P, Frame);
|
||
}
|
||
|
||
//
|
||
// Allocate local storage on stack.
|
||
//
|
||
|
||
if (P->OpsizePrefixActive != FALSE) {
|
||
P->Gpr[EBP].Exx = Frame;
|
||
P->Gpr[ESP].Exx = P->Gpr[ESP].Exx - Allocate;
|
||
|
||
} else {
|
||
P->Gpr[BP].Xx = (USHORT)Frame;
|
||
P->Gpr[SP].Xx = (USHORT)(P->Gpr[SP].Xx - Allocate);
|
||
}
|
||
|
||
return;
|
||
}
|
||
|
||
VOID
|
||
XmHltOp (
|
||
PRXM_CONTEXT P
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function emulates a hlt opcode.
|
||
|
||
Arguments:
|
||
|
||
P - Supplies a pointer to an emulator context structure.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
|
||
//
|
||
// Halt instructions are not supported by the emulator.
|
||
//
|
||
|
||
longjmp(&P->JumpBuffer[0], XM_HALT_INSTRUCTION);
|
||
return;
|
||
}
|
||
|
||
VOID
|
||
XmIntOp (
|
||
PRXM_CONTEXT P
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function emulates an int opcode.
|
||
|
||
Arguments:
|
||
|
||
P - Supplies a pointer to an emulator context structure.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
|
||
ULONG Number;
|
||
PULONG Vector;
|
||
|
||
//
|
||
// If the int instruction is an int 3, then set the interrupt vector
|
||
// to 3. Otherwise, if the int instruction is an into, then set the
|
||
// vector to 4 if OF is set. use the source interrupt vector.
|
||
//
|
||
|
||
if (P->OpsizePrefixActive != FALSE) {
|
||
P->DataType = LONG_DATA;
|
||
|
||
} else {
|
||
P->DataType = WORD_DATA;
|
||
}
|
||
|
||
if (P->CurrentOpcode == 0xcc) {
|
||
Number = 3;
|
||
|
||
} else if (P->CurrentOpcode == 0xce) {
|
||
if (P->Eflags.EFLAG_OF == 0) {
|
||
return;
|
||
}
|
||
|
||
Number = 4;
|
||
|
||
} else {
|
||
Number = P->SrcValue.Byte;
|
||
}
|
||
|
||
//
|
||
// If the vector number is 0x42, then nop the interrupt. This is the
|
||
// standard EGA video driver entry point in a PC's motherboard BIOS
|
||
// for which there is no code.
|
||
//
|
||
|
||
#if !defined(_PURE_EMULATION_)
|
||
|
||
if (Number == 0x42) {
|
||
return;
|
||
}
|
||
|
||
#endif
|
||
|
||
//
|
||
// If the vector number is 0x1a, then attempt to emulate the PCI BIOS
|
||
// if it is enabled.
|
||
//
|
||
|
||
#if !defined(_PURE_EMULATION_)
|
||
|
||
if ((Number == 0x1a) && (XmExecuteInt1a(P) != FALSE)) {
|
||
return;
|
||
}
|
||
|
||
#endif
|
||
|
||
//
|
||
// Push the current flags, code segment, and EIP on the stack.
|
||
//
|
||
|
||
XmPushStack(P, P->AllFlags);
|
||
XmPushStack(P, P->SegmentRegister[CS]);
|
||
XmPushStack(P, P->Eip);
|
||
|
||
//
|
||
// Set the new coded segment and IP from the specified interrupt
|
||
// vector.
|
||
//
|
||
|
||
Vector = (PULONG)(P->TranslateAddress)(0, 0);
|
||
P->SegmentRegister[CS] = (USHORT)(Vector[Number] >> 16);
|
||
P->Eip = (USHORT)(Vector[Number] & 0xffff);
|
||
XmTraceJumps(P);
|
||
return;
|
||
}
|
||
|
||
VOID
|
||
XmIretOp (
|
||
PRXM_CONTEXT P
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function emulates an iret opcode.
|
||
|
||
Arguments:
|
||
|
||
P - Supplies a pointer to an emulator context structure.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
|
||
//
|
||
// Set the data type and restore the return address, code segment,
|
||
// and flags.
|
||
//
|
||
|
||
if (P->OpsizePrefixActive != FALSE) {
|
||
P->DataType = LONG_DATA;
|
||
|
||
} else {
|
||
P->DataType = WORD_DATA;
|
||
}
|
||
|
||
P->Eip = XmPopStack(P);
|
||
P->SegmentRegister[CS] = (USHORT)XmPopStack(P);
|
||
P->AllFlags = XmPopStack(P);
|
||
XmTraceJumps(P);
|
||
|
||
//
|
||
// Check for emulator exit conditions.
|
||
//
|
||
|
||
if ((P->Eip == 0xffff) && (P->SegmentRegister[CS] == 0xffff)) {
|
||
longjmp(&P->JumpBuffer[0], XM_SUCCESS);
|
||
}
|
||
|
||
return;
|
||
}
|
||
|
||
VOID
|
||
XmLeaveOp (
|
||
PRXM_CONTEXT P
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function emulates a leave opcode.
|
||
|
||
Arguments:
|
||
|
||
P - Supplies a pointer to an emulator context structure.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
|
||
//
|
||
// Set the data type, restore the stack pointer, and restore the frame
|
||
// pointer.
|
||
//
|
||
|
||
if (P->OpsizePrefixActive != FALSE) {
|
||
P->DataType = LONG_DATA;
|
||
P->Gpr[ESP].Exx = P->Gpr[EBP].Exx;
|
||
P->Gpr[EBP].Exx = XmPopStack(P);
|
||
|
||
} else {
|
||
P->DataType = WORD_DATA;
|
||
P->Gpr[SP].Xx = P->Gpr[BP].Xx;
|
||
P->Gpr[BP].Xx = (USHORT)XmPopStack(P);
|
||
}
|
||
|
||
return;
|
||
}
|
||
|
||
VOID
|
||
XmRetOp (
|
||
PRXM_CONTEXT P
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function emulates a ret opcode.
|
||
|
||
Arguments:
|
||
|
||
P - Supplies a pointer to an emulator context structure.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
|
||
ULONG Adjust;
|
||
|
||
//
|
||
// Compute the number of bytes that are to be removed from the stack
|
||
// after having removed the return address and optionally the new CS
|
||
// segment value.
|
||
//
|
||
|
||
if ((P->CurrentOpcode & 0x1) == 0) {
|
||
Adjust = XmGetWordImmediate(P);
|
||
|
||
} else {
|
||
Adjust = 0;
|
||
}
|
||
|
||
//
|
||
// Remove the return address from the stack and set the new IP.
|
||
//
|
||
|
||
if (P->OpsizePrefixActive != FALSE) {
|
||
P->DataType = LONG_DATA;
|
||
|
||
} else {
|
||
P->DataType = WORD_DATA;
|
||
}
|
||
|
||
P->Eip = XmPopStack(P);
|
||
|
||
//
|
||
// If the current opcode is a far return, then remove the new CS segment
|
||
// value from the stack.
|
||
//
|
||
|
||
if ((P->CurrentOpcode & 0x8) != 0) {
|
||
P->SegmentRegister[CS] = (USHORT)XmPopStack(P);
|
||
}
|
||
|
||
//
|
||
// Remove the specified number of bytes from the stack.
|
||
//
|
||
|
||
P->Gpr[ESP].Exx += Adjust;
|
||
XmTraceJumps(P);
|
||
|
||
//
|
||
// Check for emulator exit conditions.
|
||
//
|
||
|
||
if ((P->Eip == 0xffff) && (P->SegmentRegister[CS] == 0xffff)) {
|
||
longjmp(&P->JumpBuffer[0], XM_SUCCESS);
|
||
}
|
||
|
||
return;
|
||
}
|