mirror of
https://github.com/reactos/reactos.git
synced 2024-08-19 18:02:55 +00:00
249 lines
6.7 KiB
C
249 lines
6.7 KiB
C
/*
|
|
* COPYRIGHT: GPL - See COPYING in the top level directory
|
|
* PROJECT: ReactOS Virtual DOS Machine
|
|
* FILE: subsystems/mvdm/ntvdm/cpu/cpu.c
|
|
* PURPOSE: Minimal x86 machine emulator for the VDM
|
|
* PROGRAMMERS: Aleksandar Andrejevic <theflash AT sdf DOT lonestar DOT org>
|
|
*/
|
|
|
|
/* INCLUDES *******************************************************************/
|
|
|
|
#include "ntvdm.h"
|
|
|
|
#define NDEBUG
|
|
#include <debug.h>
|
|
|
|
#include "emulator.h"
|
|
#include "cpu.h"
|
|
|
|
#include "memory.h"
|
|
#include "callback.h"
|
|
#include "bop.h"
|
|
#include <isvbop.h>
|
|
|
|
#include "clock.h"
|
|
#include "bios/rom.h"
|
|
#include "hardware/cmos.h"
|
|
#include "hardware/keyboard.h"
|
|
#include "hardware/mouse.h"
|
|
#include "hardware/pic.h"
|
|
#include "hardware/ps2.h"
|
|
#include "hardware/sound/speaker.h"
|
|
#include "hardware/pit.h"
|
|
#include "hardware/video/svga.h"
|
|
|
|
#include "io.h"
|
|
|
|
/* PRIVATE VARIABLES **********************************************************/
|
|
|
|
FAST486_STATE EmulatorContext;
|
|
BOOLEAN CpuRunning = FALSE;
|
|
|
|
/* No more than 'MaxCpuCallLevel' recursive CPU calls are allowed */
|
|
static const INT MaxCpuCallLevel = 32;
|
|
static INT CpuCallLevel = 0; // == 0: CPU stopped; >= 1: CPU running or halted
|
|
|
|
#if 0
|
|
LPCWSTR ExceptionName[] =
|
|
{
|
|
L"Division By Zero",
|
|
L"Debug",
|
|
L"Unexpected Error",
|
|
L"Breakpoint",
|
|
L"Integer Overflow",
|
|
L"Bound Range Exceeded",
|
|
L"Invalid Opcode",
|
|
L"FPU Not Available"
|
|
};
|
|
#endif
|
|
|
|
// /* BOP Identifiers */
|
|
// #define BOP_DEBUGGER 0x56 // Break into the debugger from a 16-bit app
|
|
|
|
/* PRIVATE FUNCTIONS **********************************************************/
|
|
|
|
#if 0
|
|
VOID EmulatorException(BYTE ExceptionNumber, LPWORD Stack)
|
|
{
|
|
WORD CodeSegment, InstructionPointer;
|
|
PBYTE Opcode;
|
|
|
|
ASSERT(ExceptionNumber < 8);
|
|
|
|
/* Get the CS:IP */
|
|
InstructionPointer = Stack[STACK_IP];
|
|
CodeSegment = Stack[STACK_CS];
|
|
Opcode = (PBYTE)SEG_OFF_TO_PTR(CodeSegment, InstructionPointer);
|
|
|
|
/* Display a message to the user */
|
|
DisplayMessage(L"Exception: %s occured at %04X:%04X\n"
|
|
L"Opcode: %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X",
|
|
ExceptionName[ExceptionNumber],
|
|
CodeSegment,
|
|
InstructionPointer,
|
|
Opcode[0],
|
|
Opcode[1],
|
|
Opcode[2],
|
|
Opcode[3],
|
|
Opcode[4],
|
|
Opcode[5],
|
|
Opcode[6],
|
|
Opcode[7],
|
|
Opcode[8],
|
|
Opcode[9]);
|
|
|
|
/* Stop the VDM */
|
|
EmulatorTerminate();
|
|
return;
|
|
}
|
|
#endif
|
|
|
|
// FIXME: This function assumes 16-bit mode!!!
|
|
VOID CpuExecute(WORD Segment, WORD Offset)
|
|
{
|
|
/* Tell Fast486 to move the instruction pointer */
|
|
Fast486ExecuteAt(&EmulatorContext, Segment, Offset);
|
|
}
|
|
|
|
VOID CpuStep(VOID)
|
|
{
|
|
/* Dump the state for debugging purposes */
|
|
// Fast486DumpState(&EmulatorContext);
|
|
|
|
/* Execute the next instruction */
|
|
Fast486StepInto(&EmulatorContext);
|
|
}
|
|
|
|
LONG CpuExceptionFilter(IN PEXCEPTION_POINTERS ExceptionInfo)
|
|
{
|
|
/* Get the exception record */
|
|
PEXCEPTION_RECORD ExceptionRecord = ExceptionInfo->ExceptionRecord;
|
|
|
|
switch (ExceptionRecord->ExceptionCode)
|
|
{
|
|
/* We only handle access violations so far */
|
|
case EXCEPTION_ACCESS_VIOLATION:
|
|
{
|
|
BOOLEAN Writing = (ExceptionRecord->ExceptionInformation[0] == 1);
|
|
|
|
/* Retrieve the address to which a read or write attempt was made */
|
|
ULONG_PTR Address = ExceptionRecord->ExceptionInformation[1];
|
|
|
|
/*
|
|
* Check whether the access exception was done inside the virtual memory space
|
|
* (caused by an emulated app) or outside (casued by a bug in ourselves).
|
|
*/
|
|
if (Address < (ULONG_PTR)BaseAddress ||
|
|
Address >= (ULONG_PTR)BaseAddress + MAX_ADDRESS)
|
|
{
|
|
DPRINT1("NTVDM: %s access violation at 0x%p outside the virtual memory space!\n",
|
|
(Writing ? "Write" : "Read"), Address);
|
|
return EXCEPTION_CONTINUE_SEARCH;
|
|
}
|
|
|
|
/* We are good to go, dispatch to our memory handlers */
|
|
|
|
/* Fix the CPU state */
|
|
Fast486Rewind(&EmulatorContext);
|
|
|
|
/* Call the memory handler */
|
|
MemExceptionHandler((ULONG_PTR)PHYS_TO_REAL(Address), Writing);
|
|
|
|
/* The execution of the CPU opcode handler MUST NOT continue */
|
|
return EXCEPTION_EXECUTE_HANDLER;
|
|
}
|
|
|
|
default:
|
|
{
|
|
DPRINT1("NTVDM: Exception 0x%08lx not handled!\n", ExceptionRecord->ExceptionCode);
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* Continue to search for a handler */
|
|
return EXCEPTION_CONTINUE_SEARCH;
|
|
}
|
|
|
|
VOID CpuSimulate(VOID)
|
|
{
|
|
if (CpuCallLevel > MaxCpuCallLevel)
|
|
{
|
|
DisplayMessage(L"Too many CPU levels of recursion (%d, expected maximum %d)",
|
|
CpuCallLevel, MaxCpuCallLevel);
|
|
|
|
/* Stop the VDM */
|
|
EmulatorTerminate();
|
|
return;
|
|
}
|
|
CpuCallLevel++;
|
|
DPRINT("CpuSimulate --> Level %d\n", CpuCallLevel);
|
|
|
|
CpuRunning = TRUE;
|
|
while (VdmRunning && CpuRunning)
|
|
{
|
|
_SEH2_TRY
|
|
{
|
|
while (VdmRunning && CpuRunning) ClockUpdate();
|
|
}
|
|
_SEH2_EXCEPT(CpuExceptionFilter(_SEH2_GetExceptionInformation()))
|
|
{
|
|
DPRINT("VDM exception handler called\n");
|
|
}
|
|
_SEH2_END;
|
|
}
|
|
|
|
DPRINT("CpuSimulate <-- Level %d\n", CpuCallLevel);
|
|
CpuCallLevel--;
|
|
if (!VdmRunning || CpuCallLevel < 0) CpuCallLevel = 0;
|
|
|
|
/* This takes into account for reentrance */
|
|
if (VdmRunning && (CpuCallLevel > 0)) CpuRunning = TRUE;
|
|
}
|
|
|
|
VOID CpuUnsimulate(VOID)
|
|
{
|
|
/* Stop simulation */
|
|
CpuRunning = FALSE;
|
|
}
|
|
|
|
static VOID WINAPI CpuUnsimulateBop(LPWORD Stack)
|
|
{
|
|
CpuUnsimulate();
|
|
}
|
|
|
|
/* PUBLIC FUNCTIONS ***********************************************************/
|
|
|
|
BOOLEAN CpuInitialize(VOID)
|
|
{
|
|
// /* Initialize the internal clock */
|
|
// if (!ClockInitialize())
|
|
// {
|
|
// wprintf(L"FATAL: Failed to initialize the clock\n");
|
|
// return FALSE;
|
|
// }
|
|
|
|
/* Initialize the CPU */
|
|
Fast486Initialize(&EmulatorContext,
|
|
EmulatorReadMemory,
|
|
EmulatorWriteMemory,
|
|
EmulatorReadIo,
|
|
EmulatorWriteIo,
|
|
EmulatorBiosOperation,
|
|
EmulatorIntAcknowledge,
|
|
EmulatorFpu,
|
|
NULL /* TODO: Use a TLB */);
|
|
|
|
/* Initialize the software callback system and register the emulator BOPs */
|
|
// RegisterBop(BOP_DEBUGGER , EmulatorDebugBreakBop);
|
|
RegisterBop(BOP_UNSIMULATE, CpuUnsimulateBop);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
VOID CpuCleanup(VOID)
|
|
{
|
|
// Fast486Cleanup();
|
|
}
|
|
|
|
/* EOF */
|