/*++
Copyright (c) 1994 Microsoft Corporation
Module Name:
emulate.c
Abstract:
This module implements an instruction level emulator for the execution
of x86 code. It is a complete 386/486 emulator, but only implements
real mode execution. Thus 32-bit addressing and operands are supported,
but paging and protected mode operations are not supported. The code is
written with the primary goals of being complete and small. Thus speed
of emulation is not important.
Author:
David N. Cutler (davec) 2-Sep-1994
Environment:
Kernel mode only.
Revision History:
--*/
#include "nthal.h"
#include "emulate.h"
VOID
XmInitializeEmulator (
IN USHORT StackSegment,
IN USHORT StackOffset,
IN PXM_READ_IO_SPACE ReadIoSpace,
IN PXM_WRITE_IO_SPACE WriteIoSpace,
IN PXM_TRANSLATE_ADDRESS TranslateAddress
)
/*++
Routine Description:
This function initializes the state of the x86 emulator.
Arguments:
StackSegment - Supplies the stack segment value.
StackOffset - Supplies the stack offset value.
ReadIoSpace - Supplies a pointer to a the function that reads from
I/O space given a datatype and port number.
WriteIoSpace - Supplies a pointer to a function that writes to I/O
space given a datatype, port number, and value.
TranslateAddress - Supplies a pointer to the function that translates
segment/offset address pairs into a pointer to memory or I/O space.
Return Value:
None.
--*/
{
LONG Index;
PRXM_CONTEXT P = &XmContext;
PULONG Vector;
//
// Clear the emulator context.
//
memset((PCHAR)P, 0, sizeof(XM_CONTEXT));
//
// Initialize the segment registers.
//
Index = GS;
do {
P->SegmentLimit[Index] = 0xffff;
Index -= 1;
} while (Index >= ES);
//
// Initialize the stack segment register and offset.
//
P->SegmentRegister[SS] = StackSegment;
P->Gpr[ESP].Exx = StackOffset;
//
// Set the address of the read I/O space, write I/O space, and translate
// functions.
//
P->ReadIoSpace = ReadIoSpace;
P->WriteIoSpace = WriteIoSpace;
P->TranslateAddress = TranslateAddress;
//
// Get address of interrupt vector table and initialize all vector to
// point to an iret instruction at location 0x500.
//
//
// N.B. It is assumed that the vector table is contiguous in emulated
// memory.
//
Vector = (PULONG)(P->TranslateAddress)(0, 0);
Vector[0x500 / 4] = 0x000000cf;
Index = 0;
do {
Vector[Index] = 0x00000500;
Index += 1;
} while (Index < 256);
XmEmulatorInitialized = TRUE;
return;
}
XM_STATUS
XmEmulateFarCall (
IN USHORT Segment,
IN USHORT Offset,
IN OUT PXM86_CONTEXT Context
)
/*++
Routine Description:
This function emulates a far call by pushing a special exit
sequence on the stack and then starting instruction execution
at the address specified by the respective segment and offset.
Arguments:
Segment - Supplies the segment in which to start execution.
Offset - Supplies the offset within the code segment to start
execution.
Context - Supplies a pointer to an x86 context structure.
Return Value:
The emulation completion status.
--*/
{
PRXM_CONTEXT P = &XmContext;
PUSHORT Stack;
//
// If the emulator has not been initialized, return an error.
//
if (XmEmulatorInitialized == FALSE) {
return XM_EMULATOR_NOT_INITIALIZED;
}
//
// Get address of current stack pointer, push exit markers, and
// update stack pointer.
//
// N.B. It is assumed that the stack pointer is within range and
// contiguous in emulated memory.
//
Stack = (PUSHORT)(P->TranslateAddress)(P->SegmentRegister[SS], P->Gpr[SP].Xx);
*--Stack = 0xffff;
*--Stack = 0xffff;
P->Gpr[SP].Xx -= 4;
//
// Emulate the specified instruction stream and return the final status.
//
return XmEmulateStream(&XmContext, Segment, Offset, Context);
}
XM_STATUS
XmEmulateInterrupt (
IN UCHAR Interrupt,
IN OUT PXM86_CONTEXT Context
)
/*++
Routine Description:
This function emulates an interrrupt by pushing a special exit
sequence on the stack and then starting instruction execution
at the address specified by the respective interrupt vector.
Arguments:
Interrupt - Supplies the number of the interrupt that is emulated.
Context - Supplies a pointer to an x86 context structure.
Return Value:
The emulation completion status.
--*/
{
PRXM_CONTEXT P = &XmContext;
USHORT Segment;
USHORT Offset;
PUSHORT Stack;
PULONG Vector;
//
// If the emulator has not been initialized, return an error.
//
if (XmEmulatorInitialized == FALSE) {
return XM_EMULATOR_NOT_INITIALIZED;
}
//
// Get address of current stack pointer, push exit markers, and
// update stack pointer.
//
// N.B. It is assumed that the stack pointer is within range and
// contiguous in emulated memory.
//
Stack = (PUSHORT)(P->TranslateAddress)(P->SegmentRegister[SS], P->Gpr[SP].Xx);
*--Stack = 0;
*--Stack = 0xffff;
*--Stack = 0xffff;
P->Gpr[SP].Xx -= 6;
//
// Get address of interrupt vector table and set code segment and IP
// values.
//
//
// N.B. It is assumed that the vector table is contiguous in emulated
// memory.
//
Vector = (PULONG)(P->TranslateAddress)(0, 0);
Segment = (USHORT)(Vector[Interrupt] >> 16);
Offset = (USHORT)(Vector[Interrupt] & 0xffff);
//
// Emulate the specified instruction stream and return the final status.
//
return XmEmulateStream(&XmContext, Segment, Offset, Context);
}
XM_STATUS
XmEmulateStream (
PRXM_CONTEXT P,
IN USHORT Segment,
IN USHORT Offset,
IN OUT PXM86_CONTEXT Context
)
/*++
Routine Description:
This function establishes the specfied context and emulates the
specified instruction stream until exit conditions are reached..
Arguments:
Segment - Supplies the segment in which to start execution.
Offset - Supplies the offset within the code segment to start
execution.
Context - Supplies a pointer to an x86 context structure.
Return Value:
The emulation completion status.
--*/
{
XM_STATUS Status;
//
// Set the x86 emulator registers from the specified context.
//
P->Gpr[EAX].Exx = Context->Eax;
P->Gpr[ECX].Exx = Context->Ecx;
P->Gpr[EDX].Exx = Context->Edx;
P->Gpr[EBX].Exx = Context->Ebx;
P->Gpr[EBP].Exx = Context->Ebp;
P->Gpr[ESI].Exx = Context->Esi;
P->Gpr[EDI].Exx = Context->Edi;
//
// Set the code segment, offset within segment, and emulate code.
//
P->SegmentRegister[CS] = Segment;
P->Eip = Offset;
if ((Status = setjmp(&P->JumpBuffer[0])) == 0) {
//
// Emulate x86 instruction stream.
//
do {
//
// Initialize instruction decode variables.
//
P->ComputeOffsetAddress = FALSE;
P->DataSegment = DS;
P->LockPrefixActive = FALSE;
P->OpaddrPrefixActive = FALSE;
P->OpsizePrefixActive = FALSE;
P->RepeatPrefixActive = FALSE;
P->SegmentPrefixActive = FALSE;
P->OpcodeControlTable = &XmOpcodeControlTable1[0];
#if defined(XM_DEBUG)
P->OpcodeNameTable = &XmOpcodeNameTable1[0];
#endif
//
// Get the next byte from the instruction stream and decode
// operands. If the byte is a prefix or an escape, then the
// next byte will be decoded. Decoding continues until an
// opcode byte is reached with a terminal decode condition.
//
// N.B. There is no checking for legitimate sequences of prefix
// and/or two byte opcode escapes. Redundant or invalid
// prefixes or two byte escape opcodes have no effect and
// are benign.
//
do {
P->CurrentOpcode = XmGetCodeByte(P);
#if defined(XM_DEBUG)
if ((XmDebugFlags & TRACE_INSTRUCTIONS) != 0) {
DEBUG_PRINT(("\n%04lx %s %02lx ",
P->Eip - 1,
P->OpcodeNameTable[P->CurrentOpcode],
(ULONG)P->CurrentOpcode));
}
#endif
P->OpcodeControl = P->OpcodeControlTable[P->CurrentOpcode];
P->FunctionIndex = P->OpcodeControl.FunctionIndex;
} while (XmOperandDecodeTable[P->OpcodeControl.FormatType](P) == FALSE);
//
// Emulate the instruction.
//
XmTraceFlags(P);
XmOpcodeFunctionTable[P->FunctionIndex](P);
XmTraceFlags(P);
XmTraceRegisters(P);
#if defined(XM_DEBUG)
if ((XmDebugFlags & TRACE_SINGLE_STEP) != 0) {
DEBUG_PRINT(("\n"));
DbgBreakPoint();
}
#endif
} while (TRUE);
}
//
// Set the x86 return context to the current emulator registers.
//
Context->Eax = P->Gpr[EAX].Exx;
Context->Ecx = P->Gpr[ECX].Exx;
Context->Edx = P->Gpr[EDX].Exx;
Context->Ebx = P->Gpr[EBX].Exx;
Context->Ebp = P->Gpr[EBP].Exx;
Context->Esi = P->Gpr[ESI].Exx;
Context->Edi = P->Gpr[EDI].Exx;
return Status;
}