1 /* 2 * COPYRIGHT: GPL - See COPYING in the top level directory 3 * PROJECT: ReactOS Virtual DOS Machine 4 * FILE: subsystems/mvdm/ntvdm/cpu/cpu.c 5 * PURPOSE: Minimal x86 machine emulator for the VDM 6 * PROGRAMMERS: Aleksandar Andrejevic <theflash AT sdf DOT lonestar DOT org> 7 */ 8 9 /* INCLUDES *******************************************************************/ 10 11 #include "ntvdm.h" 12 13 #define NDEBUG 14 #include <debug.h> 15 16 #include "emulator.h" 17 #include "cpu.h" 18 19 #include "memory.h" 20 #include "callback.h" 21 #include "bop.h" 22 #include <isvbop.h> 23 24 #include "clock.h" 25 #include "bios/rom.h" 26 #include "hardware/cmos.h" 27 #include "hardware/keyboard.h" 28 #include "hardware/mouse.h" 29 #include "hardware/pic.h" 30 #include "hardware/ps2.h" 31 #include "hardware/sound/speaker.h" 32 #include "hardware/pit.h" 33 #include "hardware/video/svga.h" 34 35 #include "io.h" 36 37 /* PRIVATE VARIABLES **********************************************************/ 38 39 FAST486_STATE EmulatorContext; 40 BOOLEAN CpuRunning = FALSE; 41 42 /* No more than 'MaxCpuCallLevel' recursive CPU calls are allowed */ 43 static const INT MaxCpuCallLevel = 32; 44 static INT CpuCallLevel = 0; // == 0: CPU stopped; >= 1: CPU running or halted 45 46 #if 0 47 LPCWSTR ExceptionName[] = 48 { 49 L"Division By Zero", 50 L"Debug", 51 L"Unexpected Error", 52 L"Breakpoint", 53 L"Integer Overflow", 54 L"Bound Range Exceeded", 55 L"Invalid Opcode", 56 L"FPU Not Available" 57 }; 58 #endif 59 60 // /* BOP Identifiers */ 61 // #define BOP_DEBUGGER 0x56 // Break into the debugger from a 16-bit app 62 63 /* PRIVATE FUNCTIONS **********************************************************/ 64 65 #if 0 66 VOID EmulatorException(BYTE ExceptionNumber, LPWORD Stack) 67 { 68 WORD CodeSegment, InstructionPointer; 69 PBYTE Opcode; 70 71 ASSERT(ExceptionNumber < 8); 72 73 /* Get the CS:IP */ 74 InstructionPointer = Stack[STACK_IP]; 75 CodeSegment = Stack[STACK_CS]; 76 Opcode = (PBYTE)SEG_OFF_TO_PTR(CodeSegment, InstructionPointer); 77 78 /* Display a message to the user */ 79 DisplayMessage(L"Exception: %s occured at %04X:%04X\n" 80 L"Opcode: %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X", 81 ExceptionName[ExceptionNumber], 82 CodeSegment, 83 InstructionPointer, 84 Opcode[0], 85 Opcode[1], 86 Opcode[2], 87 Opcode[3], 88 Opcode[4], 89 Opcode[5], 90 Opcode[6], 91 Opcode[7], 92 Opcode[8], 93 Opcode[9]); 94 95 /* Stop the VDM */ 96 EmulatorTerminate(); 97 return; 98 } 99 #endif 100 101 // FIXME: This function assumes 16-bit mode!!! 102 VOID CpuExecute(WORD Segment, WORD Offset) 103 { 104 /* Tell Fast486 to move the instruction pointer */ 105 Fast486ExecuteAt(&EmulatorContext, Segment, Offset); 106 } 107 108 VOID CpuStep(VOID) 109 { 110 /* Dump the state for debugging purposes */ 111 // Fast486DumpState(&EmulatorContext); 112 113 /* Execute the next instruction */ 114 Fast486StepInto(&EmulatorContext); 115 } 116 117 LONG CpuExceptionFilter(IN PEXCEPTION_POINTERS ExceptionInfo) 118 { 119 /* Get the exception record */ 120 PEXCEPTION_RECORD ExceptionRecord = ExceptionInfo->ExceptionRecord; 121 122 switch (ExceptionRecord->ExceptionCode) 123 { 124 /* We only handle access violations so far */ 125 case EXCEPTION_ACCESS_VIOLATION: 126 { 127 BOOLEAN Writing = (ExceptionRecord->ExceptionInformation[0] == 1); 128 129 /* Retrieve the address to which a read or write attempt was made */ 130 ULONG_PTR Address = ExceptionRecord->ExceptionInformation[1]; 131 132 /* 133 * Check whether the access exception was done inside the virtual memory space 134 * (caused by an emulated app) or outside (casued by a bug in ourselves). 135 */ 136 if (Address < (ULONG_PTR)BaseAddress || 137 Address >= (ULONG_PTR)BaseAddress + MAX_ADDRESS) 138 { 139 DPRINT1("NTVDM: %s access violation at 0x%p outside the virtual memory space!\n", 140 (Writing ? "Write" : "Read"), Address); 141 return EXCEPTION_CONTINUE_SEARCH; 142 } 143 144 /* We are good to go, dispatch to our memory handlers */ 145 146 /* Fix the CPU state */ 147 Fast486Rewind(&EmulatorContext); 148 149 /* Call the memory handler */ 150 MemExceptionHandler((ULONG)PHYS_TO_REAL(Address), Writing); 151 152 /* The execution of the CPU opcode handler MUST NOT continue */ 153 return EXCEPTION_EXECUTE_HANDLER; 154 } 155 156 default: 157 { 158 DPRINT1("NTVDM: Exception 0x%08lx not handled!\n", ExceptionRecord->ExceptionCode); 159 break; 160 } 161 } 162 163 /* Continue to search for a handler */ 164 return EXCEPTION_CONTINUE_SEARCH; 165 } 166 167 VOID CpuSimulate(VOID) 168 { 169 if (CpuCallLevel > MaxCpuCallLevel) 170 { 171 DisplayMessage(L"Too many CPU levels of recursion (%d, expected maximum %d)", 172 CpuCallLevel, MaxCpuCallLevel); 173 174 /* Stop the VDM */ 175 EmulatorTerminate(); 176 return; 177 } 178 CpuCallLevel++; 179 DPRINT("CpuSimulate --> Level %d\n", CpuCallLevel); 180 181 CpuRunning = TRUE; 182 while (VdmRunning && CpuRunning) 183 { 184 _SEH2_TRY 185 { 186 while (VdmRunning && CpuRunning) ClockUpdate(); 187 } 188 _SEH2_EXCEPT(CpuExceptionFilter(_SEH2_GetExceptionInformation())) 189 { 190 DPRINT("VDM exception handler called\n"); 191 } 192 _SEH2_END; 193 } 194 195 DPRINT("CpuSimulate <-- Level %d\n", CpuCallLevel); 196 CpuCallLevel--; 197 if (!VdmRunning || CpuCallLevel < 0) CpuCallLevel = 0; 198 199 /* This takes into account for reentrance */ 200 if (VdmRunning && (CpuCallLevel > 0)) CpuRunning = TRUE; 201 } 202 203 VOID CpuUnsimulate(VOID) 204 { 205 /* Stop simulation */ 206 CpuRunning = FALSE; 207 } 208 209 static VOID WINAPI CpuUnsimulateBop(LPWORD Stack) 210 { 211 CpuUnsimulate(); 212 } 213 214 /* PUBLIC FUNCTIONS ***********************************************************/ 215 216 BOOLEAN CpuInitialize(VOID) 217 { 218 // /* Initialize the internal clock */ 219 // if (!ClockInitialize()) 220 // { 221 // wprintf(L"FATAL: Failed to initialize the clock\n"); 222 // return FALSE; 223 // } 224 225 /* Initialize the CPU */ 226 Fast486Initialize(&EmulatorContext, 227 EmulatorReadMemory, 228 EmulatorWriteMemory, 229 EmulatorReadIo, 230 EmulatorWriteIo, 231 EmulatorBiosOperation, 232 EmulatorIntAcknowledge, 233 EmulatorFpu, 234 NULL /* TODO: Use a TLB */); 235 236 /* Initialize the software callback system and register the emulator BOPs */ 237 // RegisterBop(BOP_DEBUGGER , EmulatorDebugBreakBop); 238 RegisterBop(BOP_UNSIMULATE, CpuUnsimulateBop); 239 240 return TRUE; 241 } 242 243 VOID CpuCleanup(VOID) 244 { 245 // Fast486Cleanup(); 246 } 247 248 /* EOF */ 249