f29671939dfc801399953c90a02ee214e1ce1462
[reactos.git] / subsystems / ntvdm / emulator.c
1 /*
2 * COPYRIGHT: GPL - See COPYING in the top level directory
3 * PROJECT: ReactOS Virtual DOS Machine
4 * FILE: emulator.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 #define NDEBUG
12
13 #include "emulator.h"
14
15 #include "clock.h"
16 #include "bios/bios.h"
17 #include "hardware/cmos.h"
18 #include "hardware/pic.h"
19 #include "hardware/ps2.h"
20 #include "hardware/speaker.h"
21 #include "hardware/timer.h"
22 #include "hardware/vga.h"
23
24 #include "bop.h"
25 #include "vddsup.h"
26 #include "io.h"
27
28 #include <isvbop.h>
29
30 /* PRIVATE VARIABLES **********************************************************/
31
32 FAST486_STATE EmulatorContext;
33 BOOLEAN CpuSimulate = FALSE;
34
35 /* No more than 'MaxCpuCallLevel' recursive CPU calls are allowed */
36 const static INT MaxCpuCallLevel = 32;
37 static INT CpuCallLevel = 0;
38
39 LPVOID BaseAddress = NULL;
40 BOOLEAN VdmRunning = TRUE;
41
42 static BOOLEAN A20Line = FALSE;
43 static BYTE Port61hState = 0x00;
44
45 static HANDLE InputThread = NULL;
46
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
59 /* BOP Identifiers */
60 #define BOP_DEBUGGER 0x56 // Break into the debugger from a 16-bit app
61
62 /* PRIVATE FUNCTIONS **********************************************************/
63
64 VOID WINAPI EmulatorReadMemory(PFAST486_STATE State, ULONG Address, PVOID Buffer, ULONG Size)
65 {
66 UNREFERENCED_PARAMETER(State);
67
68 /* If the A20 line is disabled, mask bit 20 */
69 if (!A20Line) Address &= ~(1 << 20);
70
71 /* Make sure the requested address is valid */
72 if ((Address + Size) >= MAX_ADDRESS) return;
73
74 /*
75 * Check if we are going to read the VGA memory and
76 * copy it into the virtual address space if needed.
77 */
78 if (((Address + Size) >= VgaGetVideoBaseAddress())
79 && (Address < VgaGetVideoLimitAddress()))
80 {
81 DWORD VgaAddress = max(Address, VgaGetVideoBaseAddress());
82 DWORD ActualSize = min(Address + Size - 1, VgaGetVideoLimitAddress())
83 - VgaAddress + 1;
84 LPBYTE DestBuffer = (LPBYTE)((ULONG_PTR)BaseAddress + VgaAddress);
85
86 /* Read from the VGA memory */
87 VgaReadMemory(VgaAddress, DestBuffer, ActualSize);
88 }
89
90 /* Read the data from the virtual address space and store it in the buffer */
91 RtlCopyMemory(Buffer, (LPVOID)((ULONG_PTR)BaseAddress + Address), Size);
92 }
93
94 VOID WINAPI EmulatorWriteMemory(PFAST486_STATE State, ULONG Address, PVOID Buffer, ULONG Size)
95 {
96 UNREFERENCED_PARAMETER(State);
97
98 /* If the A20 line is disabled, mask bit 20 */
99 if (!A20Line) Address &= ~(1 << 20);
100
101 /* Make sure the requested address is valid */
102 if ((Address + Size) >= MAX_ADDRESS) return;
103
104 /* Make sure we don't write to the ROM area */
105 if ((Address + Size) >= ROM_AREA_START && (Address < ROM_AREA_END)) return;
106
107 /* Read the data from the buffer and store it in the virtual address space */
108 RtlCopyMemory((LPVOID)((ULONG_PTR)BaseAddress + Address), Buffer, Size);
109
110 /*
111 * Check if we modified the VGA memory.
112 */
113 if (((Address + Size) >= VgaGetVideoBaseAddress())
114 && (Address < VgaGetVideoLimitAddress()))
115 {
116 DWORD VgaAddress = max(Address, VgaGetVideoBaseAddress());
117 DWORD ActualSize = min(Address + Size - 1, VgaGetVideoLimitAddress())
118 - VgaAddress + 1;
119 LPBYTE SrcBuffer = (LPBYTE)((ULONG_PTR)BaseAddress + VgaAddress);
120
121 /* Write to the VGA memory */
122 VgaWriteMemory(VgaAddress, SrcBuffer, ActualSize);
123 }
124 }
125
126 UCHAR WINAPI EmulatorIntAcknowledge(PFAST486_STATE State)
127 {
128 UNREFERENCED_PARAMETER(State);
129
130 /* Get the interrupt number from the PIC */
131 return PicGetInterrupt();
132 }
133
134 VOID EmulatorException(BYTE ExceptionNumber, LPWORD Stack)
135 {
136 WORD CodeSegment, InstructionPointer;
137 PBYTE Opcode;
138
139 ASSERT(ExceptionNumber < 8);
140
141 /* Get the CS:IP */
142 InstructionPointer = Stack[STACK_IP];
143 CodeSegment = Stack[STACK_CS];
144 Opcode = (PBYTE)SEG_OFF_TO_PTR(CodeSegment, InstructionPointer);
145
146 /* Display a message to the user */
147 DisplayMessage(L"Exception: %s occured at %04X:%04X\n"
148 L"Opcode: %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X",
149 ExceptionName[ExceptionNumber],
150 CodeSegment,
151 InstructionPointer,
152 Opcode[0],
153 Opcode[1],
154 Opcode[2],
155 Opcode[3],
156 Opcode[4],
157 Opcode[5],
158 Opcode[6],
159 Opcode[7],
160 Opcode[8],
161 Opcode[9]);
162
163 /* Stop the VDM */
164 VdmRunning = FALSE;
165 return;
166 }
167
168 // FIXME: This function assumes 16-bit mode!!!
169 VOID EmulatorExecute(WORD Segment, WORD Offset)
170 {
171 /* Tell Fast486 to move the instruction pointer */
172 Fast486ExecuteAt(&EmulatorContext, Segment, Offset);
173 }
174
175 VOID EmulatorStep(VOID)
176 {
177 /* Dump the state for debugging purposes */
178 // Fast486DumpState(&EmulatorContext);
179
180 /* Execute the next instruction */
181 Fast486StepInto(&EmulatorContext);
182 }
183
184 VOID EmulatorSimulate(VOID)
185 {
186 if (CpuCallLevel > MaxCpuCallLevel)
187 {
188 DisplayMessage(L"Too many CPU levels of recursion (%d, expected maximum %d)",
189 CpuCallLevel, MaxCpuCallLevel);
190
191 /* Stop the VDM */
192 VdmRunning = FALSE;
193 return;
194 }
195 CpuCallLevel++;
196
197 CpuSimulate = TRUE;
198 while (VdmRunning && CpuSimulate) ClockUpdate();
199
200 CpuCallLevel--;
201 if (CpuCallLevel < 0) CpuCallLevel = 0;
202
203 /* This takes into account for reentrance */
204 CpuSimulate = TRUE;
205 }
206
207 VOID EmulatorUnsimulate(VOID)
208 {
209 /* Stop simulation */
210 CpuSimulate = FALSE;
211 }
212
213 VOID EmulatorInterrupt(BYTE Number)
214 {
215 /* Call the Fast486 API */
216 Fast486Interrupt(&EmulatorContext, Number);
217 }
218
219 VOID EmulatorInterruptSignal(VOID)
220 {
221 /* Call the Fast486 API */
222 Fast486InterruptSignal(&EmulatorContext);
223 }
224
225 VOID EmulatorSetA20(BOOLEAN Enabled)
226 {
227 A20Line = Enabled;
228 }
229
230 VOID WINAPI EmulatorDebugBreakBop(LPWORD Stack)
231 {
232 DPRINT1("NTVDM: BOP_DEBUGGER\n");
233 DebugBreak();
234 }
235
236 VOID WINAPI EmulatorUnsimulateBop(LPWORD Stack)
237 {
238 EmulatorUnsimulate();
239 }
240
241 static BYTE WINAPI Port61hRead(ULONG Port)
242 {
243 return Port61hState;
244 }
245
246 static VOID WINAPI Port61hWrite(ULONG Port, BYTE Data)
247 {
248 // BOOLEAN SpeakerChange = FALSE;
249 BYTE OldPort61hState = Port61hState;
250
251 /* Only the four lowest bytes can be written */
252 Port61hState = (Port61hState & 0xF0) | (Data & 0x0F);
253
254 if ((OldPort61hState ^ Port61hState) & 0x01)
255 {
256 DPRINT("PIT 2 Gate %s\n", Port61hState & 0x01 ? "on" : "off");
257 // SpeakerChange = TRUE;
258 }
259
260 PitSetGate(2, !!(Port61hState & 0x01));
261
262 if ((OldPort61hState ^ Port61hState) & 0x02)
263 {
264 /* There were some change for the speaker... */
265 DPRINT("Speaker %s\n", Port61hState & 0x02 ? "on" : "off");
266 // SpeakerChange = TRUE;
267 }
268 // if (SpeakerChange) SpeakerChange();
269 SpeakerChange();
270 }
271
272 static VOID WINAPI PitChan0Out(LPVOID Param, BOOLEAN State)
273 {
274 if (State)
275 {
276 DPRINT("PicInterruptRequest\n");
277 PicInterruptRequest(0); // Raise IRQ 0
278 }
279 // else < Lower IRQ 0 >
280 }
281
282 static VOID WINAPI PitChan1Out(LPVOID Param, BOOLEAN State)
283 {
284 #if 0
285 if (State)
286 {
287 /* Set bit 4 of Port 61h */
288 Port61hState |= 1 << 4;
289 }
290 else
291 {
292 /* Clear bit 4 of Port 61h */
293 Port61hState &= ~(1 << 4);
294 }
295 #else
296 Port61hState = (Port61hState & 0xEF) | (State << 4);
297 #endif
298 }
299
300 static VOID WINAPI PitChan2Out(LPVOID Param, BOOLEAN State)
301 {
302 // BYTE OldPort61hState = Port61hState;
303
304 #if 0
305 if (State)
306 {
307 /* Set bit 5 of Port 61h */
308 Port61hState |= 1 << 5;
309 }
310 else
311 {
312 /* Clear bit 5 of Port 61h */
313 Port61hState &= ~(1 << 5);
314 }
315 #else
316 Port61hState = (Port61hState & 0xDF) | (State << 5);
317 #endif
318 DPRINT("Speaker PIT out\n");
319 // if ((OldPort61hState ^ Port61hState) & 0x20)
320 // SpeakerChange();
321 }
322
323 /* PUBLIC FUNCTIONS ***********************************************************/
324
325 DWORD WINAPI PumpConsoleInput(LPVOID Parameter);
326
327 BOOLEAN EmulatorInitialize(HANDLE ConsoleInput, HANDLE ConsoleOutput)
328 {
329 /* Allocate memory for the 16-bit address space */
330 BaseAddress = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, MAX_ADDRESS);
331 if (BaseAddress == NULL)
332 {
333 wprintf(L"FATAL: Failed to allocate VDM memory.\n");
334 return FALSE;
335 }
336
337 /* Initialize I/O ports */
338 /* Initialize RAM */
339
340 /* Initialize the internal clock */
341 if (!ClockInitialize())
342 {
343 wprintf(L"FATAL: Failed to initialize the clock\n");
344 return FALSE;
345 }
346
347 /* Initialize the CPU */
348 Fast486Initialize(&EmulatorContext,
349 EmulatorReadMemory,
350 EmulatorWriteMemory,
351 EmulatorReadIo,
352 EmulatorWriteIo,
353 NULL,
354 EmulatorBiosOperation,
355 EmulatorIntAcknowledge,
356 NULL /* TODO: Use a TLB */);
357
358 /* Enable interrupts */
359 setIF(1);
360
361 /* Initialize DMA */
362
363 /* Initialize the PIC, the PIT, the CMOS and the PC Speaker */
364 PicInitialize();
365 PitInitialize();
366 CmosInitialize();
367 SpeakerInitialize();
368
369 /* Set output functions */
370 PitSetOutFunction(0, NULL, PitChan0Out);
371 PitSetOutFunction(1, NULL, PitChan1Out);
372 PitSetOutFunction(2, NULL, PitChan2Out);
373
374 /* Register the I/O Ports */
375 RegisterIoPort(CONTROL_SYSTEM_PORT61H, Port61hRead, Port61hWrite);
376
377 /* Initialize the PS2 port */
378 PS2Initialize(ConsoleInput);
379
380 /* Set the console input mode */
381 // SetConsoleMode(ConsoleInput, ENABLE_MOUSE_INPUT | ENABLE_PROCESSED_INPUT);
382
383 /* Start the input thread */
384 InputThread = CreateThread(NULL, 0, &PumpConsoleInput, ConsoleInput, 0, NULL);
385 // if (InputThread == NULL) return FALSE;
386
387 /* Initialize the VGA */
388 // if (!VgaInitialize(ConsoleOutput)) return FALSE;
389 VgaInitialize(ConsoleOutput);
390
391 /* Initialize the software callback system and register the emulator BOPs */
392 InitializeCallbacks();
393 RegisterBop(BOP_DEBUGGER , EmulatorDebugBreakBop);
394 RegisterBop(BOP_UNSIMULATE, EmulatorUnsimulateBop);
395
396 /* Initialize VDD support */
397 VDDSupInitialize();
398
399 return TRUE;
400 }
401
402 VOID EmulatorCleanup(VOID)
403 {
404 // VgaCleanup();
405
406 /* Close the input thread handle */
407 if (InputThread != NULL) CloseHandle(InputThread);
408 InputThread = NULL;
409
410 PS2Cleanup();
411
412 SpeakerCleanup();
413 CmosCleanup();
414 // PitCleanup();
415 // PicCleanup();
416
417 // Fast486Cleanup();
418
419 /* Free the memory allocated for the 16-bit address space */
420 if (BaseAddress != NULL) HeapFree(GetProcessHeap(), 0, BaseAddress);
421 }
422
423
424
425 VOID
426 WINAPI
427 VDDSimulate16(VOID)
428 {
429 EmulatorSimulate();
430 }
431
432 VOID
433 WINAPI
434 VDDTerminateVDM(VOID)
435 {
436 /* Stop the VDM */
437 VdmRunning = FALSE;
438 }
439
440 PBYTE
441 WINAPI
442 Sim32pGetVDMPointer(IN ULONG Address,
443 IN BOOLEAN ProtectedMode)
444 {
445 // FIXME
446 UNREFERENCED_PARAMETER(ProtectedMode);
447
448 /*
449 * HIWORD(Address) == Segment (if ProtectedMode == FALSE)
450 * or Selector (if ProtectedMode == TRUE )
451 * LOWORD(Address) == Offset
452 */
453 return (PBYTE)FAR_POINTER(Address);
454 }
455
456 PBYTE
457 WINAPI
458 MGetVdmPointer(IN ULONG Address,
459 IN ULONG Size,
460 IN BOOLEAN ProtectedMode)
461 {
462 UNREFERENCED_PARAMETER(Size);
463 return Sim32pGetVDMPointer(Address, ProtectedMode);
464 }
465
466 PVOID
467 WINAPI
468 VdmMapFlat(IN USHORT Segment,
469 IN ULONG Offset,
470 IN VDM_MODE Mode)
471 {
472 // FIXME
473 UNREFERENCED_PARAMETER(Mode);
474
475 return SEG_OFF_TO_PTR(Segment, Offset);
476 }
477
478 BOOL
479 WINAPI
480 VdmFlushCache(IN USHORT Segment,
481 IN ULONG Offset,
482 IN ULONG Size,
483 IN VDM_MODE Mode)
484 {
485 // FIXME
486 UNIMPLEMENTED;
487 return TRUE;
488 }
489
490 BOOL
491 WINAPI
492 VdmUnmapFlat(IN USHORT Segment,
493 IN ULONG Offset,
494 IN PVOID Buffer,
495 IN VDM_MODE Mode)
496 {
497 // FIXME
498 UNIMPLEMENTED;
499 return TRUE;
500 }
501
502 /* EOF */