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!!!
CpuExecute(WORD Segment,WORD Offset)102 VOID CpuExecute(WORD Segment, WORD Offset)
103 {
104 /* Tell Fast486 to move the instruction pointer */
105 Fast486ExecuteAt(&EmulatorContext, Segment, Offset);
106 }
107
CpuStep(VOID)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
CpuExceptionFilter(IN PEXCEPTION_POINTERS ExceptionInfo)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 (caused 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_PTR)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
CpuSimulate(VOID)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
CpuUnsimulate(VOID)203 VOID CpuUnsimulate(VOID)
204 {
205 /* Stop simulation */
206 CpuRunning = FALSE;
207 }
208
CpuUnsimulateBop(LPWORD Stack)209 static VOID WINAPI CpuUnsimulateBop(LPWORD Stack)
210 {
211 CpuUnsimulate();
212 }
213
214 /* PUBLIC FUNCTIONS ***********************************************************/
215
CpuInitialize(VOID)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
CpuCleanup(VOID)243 VOID CpuCleanup(VOID)
244 {
245 // Fast486Cleanup();
246 }
247
248 /* EOF */
249