/* * 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 */ /* INCLUDES *******************************************************************/ #include "ntvdm.h" #define NDEBUG #include #include "emulator.h" #include "cpu.h" #include "memory.h" #include "callback.h" #include "bop.h" #include #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 (caused 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 */