[BASESRV][NTVDM][TESTVDD] Improve the FILE header section. Brought to you by Adam...
[reactos.git] / reactos / subsystems / mvdm / ntvdm / emulator.c
1 /*
2 * COPYRIGHT: GPL - See COPYING in the top level directory
3 * PROJECT: ReactOS Virtual DOS Machine
4 * FILE: subsystems/mvdm/ntvdm/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 "ntvdm.h"
14 #include "emulator.h"
15 #include "memory.h"
16
17 #include "cpu/callback.h"
18 #include "cpu/cpu.h"
19 #include "cpu/bop.h"
20 #include <isvbop.h>
21
22 #include "int32.h"
23
24 #include "clock.h"
25 #include "bios/rom.h"
26 #include "hardware/cmos.h"
27 #include "hardware/dma.h"
28 #include "hardware/keyboard.h"
29 #include "hardware/mouse.h"
30 #include "hardware/pic.h"
31 #include "hardware/pit.h"
32 #include "hardware/ppi.h"
33 #include "hardware/ps2.h"
34 #include "hardware/sound/speaker.h"
35 #include "hardware/video/svga.h"
36
37 #include "vddsup.h"
38 #include "io.h"
39
40 /* PRIVATE VARIABLES **********************************************************/
41
42 LPVOID BaseAddress = NULL;
43 BOOLEAN VdmRunning = TRUE;
44
45 HANDLE VdmTaskEvent = NULL;
46 static HANDLE InputThread = NULL;
47
48 LPCWSTR ExceptionName[] =
49 {
50 L"Division By Zero",
51 L"Debug",
52 L"Unexpected Error",
53 L"Breakpoint",
54 L"Integer Overflow",
55 L"Bound Range Exceeded",
56 L"Invalid Opcode",
57 L"FPU Not Available"
58 };
59
60 /* BOP Identifiers */
61 #define BOP_DEBUGGER 0x56 // Break into the debugger from a 16-bit app
62
63 /* PRIVATE FUNCTIONS **********************************************************/
64
65 UCHAR FASTCALL EmulatorIntAcknowledge(PFAST486_STATE State)
66 {
67 UNREFERENCED_PARAMETER(State);
68
69 /* Get the interrupt number from the PIC */
70 return PicGetInterrupt();
71 }
72
73 VOID FASTCALL EmulatorFpu(PFAST486_STATE State)
74 {
75 /* The FPU is wired to IRQ 13 */
76 PicInterruptRequest(13);
77 }
78
79 VOID EmulatorException(BYTE ExceptionNumber, LPWORD Stack)
80 {
81 WORD CodeSegment, InstructionPointer;
82 PBYTE Opcode;
83
84 ASSERT(ExceptionNumber < 8);
85
86 /* Get the CS:IP */
87 InstructionPointer = Stack[STACK_IP];
88 CodeSegment = Stack[STACK_CS];
89 Opcode = (PBYTE)SEG_OFF_TO_PTR(CodeSegment, InstructionPointer);
90
91 /* Display a message to the user */
92 DisplayMessage(L"Exception: %s occurred at %04X:%04X\n"
93 L"Opcode: %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X",
94 ExceptionName[ExceptionNumber],
95 CodeSegment,
96 InstructionPointer,
97 Opcode[0],
98 Opcode[1],
99 Opcode[2],
100 Opcode[3],
101 Opcode[4],
102 Opcode[5],
103 Opcode[6],
104 Opcode[7],
105 Opcode[8],
106 Opcode[9]);
107
108 Fast486DumpState(&EmulatorContext);
109
110 /* Stop the VDM */
111 EmulatorTerminate();
112 }
113
114 VOID EmulatorInterruptSignal(VOID)
115 {
116 /* Call the Fast486 API */
117 Fast486InterruptSignal(&EmulatorContext);
118 }
119
120 static VOID WINAPI EmulatorDebugBreakBop(LPWORD Stack)
121 {
122 DPRINT1("NTVDM: BOP_DEBUGGER\n");
123 DebugBreak();
124 }
125
126 static VOID WINAPI PitChan0Out(LPVOID Param, BOOLEAN State)
127 {
128 if (State)
129 {
130 DPRINT("PicInterruptRequest\n");
131 PicInterruptRequest(0); // Raise IRQ 0
132 }
133 // else < Lower IRQ 0 >
134 }
135
136 static VOID WINAPI PitChan1Out(LPVOID Param, BOOLEAN State)
137 {
138 #if 0
139 if (State)
140 {
141 /* Set bit 4 of Port 61h */
142 Port61hState |= 1 << 4;
143 }
144 else
145 {
146 /* Clear bit 4 of Port 61h */
147 Port61hState &= ~(1 << 4);
148 }
149 #else
150 Port61hState = (Port61hState & 0xEF) | (State << 4);
151 #endif
152 }
153
154 static VOID WINAPI PitChan2Out(LPVOID Param, BOOLEAN State)
155 {
156 BYTE OldPort61hState = Port61hState;
157
158 #if 0
159 if (State)
160 {
161 /* Set bit 5 of Port 61h */
162 Port61hState |= 1 << 5;
163 }
164 else
165 {
166 /* Clear bit 5 of Port 61h */
167 Port61hState &= ~(1 << 5);
168 }
169 #else
170 Port61hState = (Port61hState & 0xDF) | (State << 5);
171 #endif
172
173 if ((OldPort61hState ^ Port61hState) & 0x20)
174 {
175 DPRINT("PitChan2Out -- Port61hState changed\n");
176 SpeakerChange(Port61hState);
177 }
178 }
179
180
181 static DWORD
182 WINAPI
183 ConsoleEventThread(LPVOID Parameter)
184 {
185 HANDLE ConsoleInput = (HANDLE)Parameter;
186 HANDLE WaitHandles[2];
187 DWORD WaitResult;
188
189 /*
190 * For optimization purposes, Windows (and hence ReactOS, too, for
191 * compatibility reasons) uses a static buffer if no more than five
192 * input records are read. Otherwise a new buffer is used.
193 * The client-side expects that we know this behaviour.
194 * See consrv/coninput.c
195 *
196 * We exploit here this optimization by also using a buffer of 5 records.
197 */
198 INPUT_RECORD InputRecords[5];
199 ULONG NumRecords, i;
200
201 WaitHandles[0] = VdmTaskEvent;
202 WaitHandles[1] = GetConsoleInputWaitHandle();
203
204 while (VdmRunning)
205 {
206 /* Make sure the task event is signaled */
207 WaitResult = WaitForMultipleObjects(ARRAYSIZE(WaitHandles),
208 WaitHandles,
209 TRUE,
210 INFINITE);
211 switch (WaitResult)
212 {
213 case WAIT_OBJECT_0 + 0:
214 case WAIT_OBJECT_0 + 1:
215 break;
216 default:
217 return GetLastError();
218 }
219
220 /* Wait for an input record */
221 if (!ReadConsoleInputExW(ConsoleInput,
222 InputRecords,
223 ARRAYSIZE(InputRecords),
224 &NumRecords,
225 CONSOLE_READ_CONTINUE))
226 {
227 DWORD LastError = GetLastError();
228 DPRINT1("Error reading console input (0x%p, %lu) - Error %lu\n", ConsoleInput, NumRecords, LastError);
229 return LastError;
230 }
231
232 // ASSERT(NumRecords != 0);
233 if (NumRecords == 0)
234 {
235 DPRINT1("Got NumRecords == 0!\n");
236 continue;
237 }
238
239 /* Dispatch the events */
240 for (i = 0; i < NumRecords; i++)
241 {
242 /* Check the event type */
243 switch (InputRecords[i].EventType)
244 {
245 /*
246 * Hardware events
247 */
248 case KEY_EVENT:
249 KeyboardEventHandler(&InputRecords[i].Event.KeyEvent);
250 break;
251
252 case MOUSE_EVENT:
253 MouseEventHandler(&InputRecords[i].Event.MouseEvent);
254 break;
255
256 case WINDOW_BUFFER_SIZE_EVENT:
257 ScreenEventHandler(&InputRecords[i].Event.WindowBufferSizeEvent);
258 break;
259
260 /*
261 * Interface events
262 */
263 case MENU_EVENT:
264 MenuEventHandler(&InputRecords[i].Event.MenuEvent);
265 break;
266
267 case FOCUS_EVENT:
268 FocusEventHandler(&InputRecords[i].Event.FocusEvent);
269 break;
270
271 default:
272 DPRINT1("Unknown input event type 0x%04x\n", InputRecords[i].EventType);
273 break;
274 }
275 }
276
277 /* Let the console subsystem queue some new events */
278 Sleep(10);
279 }
280
281 return 0;
282 }
283
284 static VOID PauseEventThread(VOID)
285 {
286 ResetEvent(VdmTaskEvent);
287 }
288
289 static VOID ResumeEventThread(VOID)
290 {
291 SetEvent(VdmTaskEvent);
292 }
293
294
295 /* PUBLIC FUNCTIONS ***********************************************************/
296
297 static VOID
298 DumpMemoryRaw(HANDLE hFile)
299 {
300 PVOID Buffer;
301 SIZE_T Size;
302
303 /* Dump the VM memory */
304 SetFilePointer(hFile, 0, NULL, FILE_BEGIN);
305 Buffer = REAL_TO_PHYS(NULL);
306 Size = MAX_ADDRESS - (ULONG_PTR)(NULL);
307 WriteFile(hFile, Buffer, Size, &Size, NULL);
308 }
309
310 static VOID
311 DumpMemoryTxt(HANDLE hFile)
312 {
313 #define LINE_SIZE 75 + 2
314 ULONG i;
315 PBYTE Ptr1, Ptr2;
316 CHAR LineBuffer[LINE_SIZE];
317 PCHAR Line;
318 SIZE_T LineSize;
319
320 /* Dump the VM memory */
321 SetFilePointer(hFile, 0, NULL, FILE_BEGIN);
322 Ptr1 = Ptr2 = REAL_TO_PHYS(NULL);
323 while (MAX_ADDRESS - (ULONG_PTR)PHYS_TO_REAL(Ptr1) > 0)
324 {
325 Ptr1 = Ptr2;
326 Line = LineBuffer;
327
328 /* Print the address */
329 Line += snprintf(Line, LINE_SIZE + LineBuffer - Line, "%08x ", PHYS_TO_REAL(Ptr1));
330
331 /* Print up to 16 bytes... */
332
333 /* ... in hexadecimal form first... */
334 i = 0;
335 while (i++ <= 0x0F && (MAX_ADDRESS - (ULONG_PTR)PHYS_TO_REAL(Ptr1) > 0))
336 {
337 Line += snprintf(Line, LINE_SIZE + LineBuffer - Line, " %02x", *Ptr1);
338 ++Ptr1;
339 }
340
341 /* ... align with spaces if needed... */
342 RtlFillMemory(Line, 0x0F + 4 - i, ' ');
343 Line += 0x0F + 4 - i;
344
345 /* ... then in character form. */
346 i = 0;
347 while (i++ <= 0x0F && (MAX_ADDRESS - (ULONG_PTR)PHYS_TO_REAL(Ptr2) > 0))
348 {
349 *Line++ = ((*Ptr2 >= 0x20 && *Ptr2 <= 0x7E) || (*Ptr2 >= 0x80 && *Ptr2 < 0xFF) ? *Ptr2 : '.');
350 ++Ptr2;
351 }
352
353 /* Newline */
354 *Line++ = '\r';
355 *Line++ = '\n';
356
357 /* Finally write the line to the file */
358 LineSize = Line - LineBuffer;
359 WriteFile(hFile, LineBuffer, LineSize, &LineSize, NULL);
360 }
361 }
362
363 VOID DumpMemory(BOOLEAN TextFormat)
364 {
365 static ULONG DumpNumber = 0;
366
367 HANDLE hFile;
368 WCHAR FileName[MAX_PATH];
369
370 /* Build a suitable file name */
371 _snwprintf(FileName, MAX_PATH,
372 L"memdump%lu.%s",
373 DumpNumber,
374 TextFormat ? L"txt" : L"dat");
375 ++DumpNumber;
376
377 DPRINT1("Creating memory dump file '%S'...\n", FileName);
378
379 /* Always create the dump file */
380 hFile = CreateFileW(FileName,
381 GENERIC_WRITE,
382 0,
383 NULL,
384 CREATE_ALWAYS,
385 FILE_ATTRIBUTE_NORMAL,
386 NULL);
387
388 if (hFile == INVALID_HANDLE_VALUE)
389 {
390 DPRINT1("Error when creating '%S' for memory dumping, GetLastError() = %u\n",
391 FileName, GetLastError());
392 return;
393 }
394
395 /* Dump the VM memory in the chosen format */
396 if (TextFormat)
397 DumpMemoryTxt(hFile);
398 else
399 DumpMemoryRaw(hFile);
400
401 /* Close the file */
402 CloseHandle(hFile);
403
404 DPRINT1("Memory dump done\n");
405 }
406
407 VOID EmulatorPause(VOID)
408 {
409 /* Pause the VDM */
410 VDDBlockUserHook();
411 VgaRefreshDisplay();
412 PauseEventThread();
413 }
414
415 VOID EmulatorResume(VOID)
416 {
417 /* Resume the VDM */
418 ResumeEventThread();
419 VgaRefreshDisplay();
420 VDDResumeUserHook();
421 }
422
423 VOID EmulatorTerminate(VOID)
424 {
425 /* Stop the VDM */
426 CpuUnsimulate(); // Halt the CPU
427 VdmRunning = FALSE;
428 }
429
430 BOOLEAN EmulatorInitialize(HANDLE ConsoleInput, HANDLE ConsoleOutput)
431 {
432 /* Initialize memory */
433 if (!MemInitialize())
434 {
435 wprintf(L"Memory initialization failed.\n");
436 return FALSE;
437 }
438
439 /* Initialize I/O ports */
440 /* Initialize RAM */
441
442 /* Initialize the CPU */
443
444 /* Initialize the internal clock */
445 if (!ClockInitialize())
446 {
447 wprintf(L"FATAL: Failed to initialize the clock\n");
448 EmulatorCleanup();
449 return FALSE;
450 }
451
452 /* Initialize the CPU */
453 CpuInitialize();
454
455 /* Initialize DMA */
456 DmaInitialize();
457
458 /* Initialize PIC, PIT, CMOS, PC Speaker and PS/2 */
459 PicInitialize();
460
461 PitInitialize();
462 PitSetOutFunction(0, NULL, PitChan0Out);
463 PitSetOutFunction(1, NULL, PitChan1Out);
464 PitSetOutFunction(2, NULL, PitChan2Out);
465
466 CmosInitialize();
467 SpeakerInitialize();
468 PpiInitialize();
469
470 PS2Initialize();
471
472 /* Initialize the keyboard and mouse and connect them to their PS/2 ports */
473 KeyboardInit(0);
474 MouseInit(1);
475
476 /**************** ATTACH INPUT WITH CONSOLE *****************/
477 /* Create the task event */
478 VdmTaskEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
479 ASSERT(VdmTaskEvent != NULL);
480
481 /* Start the input thread */
482 InputThread = CreateThread(NULL, 0, &ConsoleEventThread, ConsoleInput, 0, NULL);
483 if (InputThread == NULL)
484 {
485 wprintf(L"FATAL: Failed to create the console input thread.\n");
486 EmulatorCleanup();
487 return FALSE;
488 }
489 /************************************************************/
490
491 /* Initialize the VGA */
492 if (!VgaInitialize(ConsoleOutput))
493 {
494 wprintf(L"FATAL: Failed to initialize VGA support.\n");
495 EmulatorCleanup();
496 return FALSE;
497 }
498
499 /* Initialize the software callback system and register the emulator BOPs */
500 InitializeInt32();
501 RegisterBop(BOP_DEBUGGER , EmulatorDebugBreakBop);
502 // RegisterBop(BOP_UNSIMULATE, CpuUnsimulateBop);
503
504 /* Initialize VDD support */
505 VDDSupInitialize();
506
507 return TRUE;
508 }
509
510 VOID EmulatorCleanup(VOID)
511 {
512 VgaCleanup();
513
514 /* Close the input thread handle */
515 if (InputThread != NULL) CloseHandle(InputThread);
516 InputThread = NULL;
517
518 /* Close the task event */
519 if (VdmTaskEvent != NULL) CloseHandle(VdmTaskEvent);
520 VdmTaskEvent = NULL;
521
522 PS2Cleanup();
523
524 SpeakerCleanup();
525 CmosCleanup();
526 // PitCleanup();
527 // PicCleanup();
528
529 // DmaCleanup();
530
531 CpuCleanup();
532 MemCleanup();
533 }
534
535
536
537 VOID
538 WINAPI
539 VDDSimulate16(VOID)
540 {
541 CpuSimulate();
542 }
543
544 VOID
545 WINAPI
546 VDDTerminateVDM(VOID)
547 {
548 /* Stop the VDM */
549 EmulatorTerminate();
550 }
551
552 /* EOF */