2 * COPYRIGHT: GPL - See COPYING in the top level directory
3 * PROJECT: ReactOS Virtual DOS Machine
5 * PURPOSE: Minimal x86 machine emulator for the VDM
6 * PROGRAMMERS: Aleksandar Andrejevic <theflash AT sdf DOT lonestar DOT org>
9 /* INCLUDES *******************************************************************/
17 #include "cpu/callback.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"
40 /* PRIVATE VARIABLES **********************************************************/
42 LPVOID BaseAddress
= NULL
;
43 BOOLEAN VdmRunning
= TRUE
;
45 HANDLE VdmTaskEvent
= NULL
;
46 static HANDLE InputThread
= NULL
;
48 LPCWSTR ExceptionName
[] =
55 L
"Bound Range Exceeded",
61 #define BOP_DEBUGGER 0x56 // Break into the debugger from a 16-bit app
63 /* PRIVATE FUNCTIONS **********************************************************/
65 UCHAR FASTCALL
EmulatorIntAcknowledge(PFAST486_STATE State
)
67 UNREFERENCED_PARAMETER(State
);
69 /* Get the interrupt number from the PIC */
70 return PicGetInterrupt();
73 VOID FASTCALL
EmulatorFpu(PFAST486_STATE State
)
75 /* The FPU is wired to IRQ 13 */
76 PicInterruptRequest(13);
79 VOID
EmulatorException(BYTE ExceptionNumber
, LPWORD Stack
)
81 WORD CodeSegment
, InstructionPointer
;
84 ASSERT(ExceptionNumber
< 8);
87 InstructionPointer
= Stack
[STACK_IP
];
88 CodeSegment
= Stack
[STACK_CS
];
89 Opcode
= (PBYTE
)SEG_OFF_TO_PTR(CodeSegment
, InstructionPointer
);
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
],
108 Fast486DumpState(&EmulatorContext
);
114 VOID
EmulatorInterruptSignal(VOID
)
116 /* Call the Fast486 API */
117 Fast486InterruptSignal(&EmulatorContext
);
120 static VOID WINAPI
EmulatorDebugBreakBop(LPWORD Stack
)
122 DPRINT1("NTVDM: BOP_DEBUGGER\n");
126 static VOID WINAPI
PitChan0Out(LPVOID Param
, BOOLEAN State
)
130 DPRINT("PicInterruptRequest\n");
131 PicInterruptRequest(0); // Raise IRQ 0
133 // else < Lower IRQ 0 >
136 static VOID WINAPI
PitChan1Out(LPVOID Param
, BOOLEAN State
)
141 /* Set bit 4 of Port 61h */
142 Port61hState
|= 1 << 4;
146 /* Clear bit 4 of Port 61h */
147 Port61hState
&= ~(1 << 4);
150 Port61hState
= (Port61hState
& 0xEF) | (State
<< 4);
154 static VOID WINAPI
PitChan2Out(LPVOID Param
, BOOLEAN State
)
156 BYTE OldPort61hState
= Port61hState
;
161 /* Set bit 5 of Port 61h */
162 Port61hState
|= 1 << 5;
166 /* Clear bit 5 of Port 61h */
167 Port61hState
&= ~(1 << 5);
170 Port61hState
= (Port61hState
& 0xDF) | (State
<< 5);
173 if ((OldPort61hState
^ Port61hState
) & 0x20)
175 DPRINT("PitChan2Out -- Port61hState changed\n");
176 SpeakerChange(Port61hState
);
183 ConsoleEventThread(LPVOID Parameter
)
185 HANDLE ConsoleInput
= (HANDLE
)Parameter
;
186 HANDLE WaitHandles
[2];
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
196 * We exploit here this optimization by also using a buffer of 5 records.
198 INPUT_RECORD InputRecords
[5];
201 WaitHandles
[0] = VdmTaskEvent
;
202 WaitHandles
[1] = GetConsoleInputWaitHandle();
206 /* Make sure the task event is signaled */
207 WaitResult
= WaitForMultipleObjects(ARRAYSIZE(WaitHandles
),
213 case WAIT_OBJECT_0
+ 0:
214 case WAIT_OBJECT_0
+ 1:
217 return GetLastError();
220 /* Wait for an input record */
221 if (!ReadConsoleInputExW(ConsoleInput
,
223 ARRAYSIZE(InputRecords
),
225 CONSOLE_READ_CONTINUE
))
227 DWORD LastError
= GetLastError();
228 DPRINT1("Error reading console input (0x%p, %lu) - Error %lu\n", ConsoleInput
, NumRecords
, LastError
);
232 // ASSERT(NumRecords != 0);
235 DPRINT1("Got NumRecords == 0!\n");
239 /* Dispatch the events */
240 for (i
= 0; i
< NumRecords
; i
++)
242 /* Check the event type */
243 switch (InputRecords
[i
].EventType
)
249 KeyboardEventHandler(&InputRecords
[i
].Event
.KeyEvent
);
253 MouseEventHandler(&InputRecords
[i
].Event
.MouseEvent
);
256 case WINDOW_BUFFER_SIZE_EVENT
:
257 ScreenEventHandler(&InputRecords
[i
].Event
.WindowBufferSizeEvent
);
264 MenuEventHandler(&InputRecords
[i
].Event
.MenuEvent
);
268 FocusEventHandler(&InputRecords
[i
].Event
.FocusEvent
);
272 DPRINT1("Unknown input event type 0x%04x\n", InputRecords
[i
].EventType
);
277 /* Let the console subsystem queue some new events */
284 static VOID
PauseEventThread(VOID
)
286 ResetEvent(VdmTaskEvent
);
289 static VOID
ResumeEventThread(VOID
)
291 SetEvent(VdmTaskEvent
);
295 /* PUBLIC FUNCTIONS ***********************************************************/
298 DumpMemoryRaw(HANDLE hFile
)
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
);
311 DumpMemoryTxt(HANDLE hFile
)
313 #define LINE_SIZE 75 + 2
316 CHAR LineBuffer
[LINE_SIZE
];
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)
328 /* Print the address */
329 Line
+= snprintf(Line
, LINE_SIZE
+ LineBuffer
- Line
, "%08x ", PHYS_TO_REAL(Ptr1
));
331 /* Print up to 16 bytes... */
333 /* ... in hexadecimal form first... */
335 while (i
++ <= 0x0F && (MAX_ADDRESS
- (ULONG_PTR
)PHYS_TO_REAL(Ptr1
) > 0))
337 Line
+= snprintf(Line
, LINE_SIZE
+ LineBuffer
- Line
, " %02x", *Ptr1
);
341 /* ... align with spaces if needed... */
342 RtlFillMemory(Line
, 0x0F + 4 - i
, ' ');
343 Line
+= 0x0F + 4 - i
;
345 /* ... then in character form. */
347 while (i
++ <= 0x0F && (MAX_ADDRESS
- (ULONG_PTR
)PHYS_TO_REAL(Ptr2
) > 0))
349 *Line
++ = ((*Ptr2
>= 0x20 && *Ptr2
<= 0x7E) || (*Ptr2
>= 0x80 && *Ptr2
< 0xFF) ? *Ptr2
: '.');
357 /* Finally write the line to the file */
358 LineSize
= Line
- LineBuffer
;
359 WriteFile(hFile
, LineBuffer
, LineSize
, &LineSize
, NULL
);
363 VOID
DumpMemory(BOOLEAN TextFormat
)
365 static ULONG DumpNumber
= 0;
368 WCHAR FileName
[MAX_PATH
];
370 /* Build a suitable file name */
371 _snwprintf(FileName
, MAX_PATH
,
374 TextFormat
? L
"txt" : L
"dat");
377 DPRINT1("Creating memory dump file '%S'...\n", FileName
);
379 /* Always create the dump file */
380 hFile
= CreateFileW(FileName
,
385 FILE_ATTRIBUTE_NORMAL
,
388 if (hFile
== INVALID_HANDLE_VALUE
)
390 DPRINT1("Error when creating '%S' for memory dumping, GetLastError() = %u\n",
391 FileName
, GetLastError());
395 /* Dump the VM memory in the chosen format */
397 DumpMemoryTxt(hFile
);
399 DumpMemoryRaw(hFile
);
404 DPRINT1("Memory dump done\n");
407 VOID
EmulatorPause(VOID
)
415 VOID
EmulatorResume(VOID
)
423 VOID
EmulatorTerminate(VOID
)
426 CpuUnsimulate(); // Halt the CPU
430 BOOLEAN
EmulatorInitialize(HANDLE ConsoleInput
, HANDLE ConsoleOutput
)
432 /* Initialize memory */
433 if (!MemInitialize())
435 wprintf(L
"Memory initialization failed.\n");
439 /* Initialize I/O ports */
442 /* Initialize the CPU */
444 /* Initialize the internal clock */
445 if (!ClockInitialize())
447 wprintf(L
"FATAL: Failed to initialize the clock\n");
452 /* Initialize the CPU */
458 /* Initialize PIC, PIT, CMOS, PC Speaker and PS/2 */
462 PitSetOutFunction(0, NULL
, PitChan0Out
);
463 PitSetOutFunction(1, NULL
, PitChan1Out
);
464 PitSetOutFunction(2, NULL
, PitChan2Out
);
472 /* Initialize the keyboard and mouse and connect them to their PS/2 ports */
476 /**************** ATTACH INPUT WITH CONSOLE *****************/
477 /* Create the task event */
478 VdmTaskEvent
= CreateEvent(NULL
, TRUE
, FALSE
, NULL
);
479 ASSERT(VdmTaskEvent
!= NULL
);
481 /* Start the input thread */
482 InputThread
= CreateThread(NULL
, 0, &ConsoleEventThread
, ConsoleInput
, 0, NULL
);
483 if (InputThread
== NULL
)
485 wprintf(L
"FATAL: Failed to create the console input thread.\n");
489 /************************************************************/
491 /* Initialize the VGA */
492 if (!VgaInitialize(ConsoleOutput
))
494 wprintf(L
"FATAL: Failed to initialize VGA support.\n");
499 /* Initialize the software callback system and register the emulator BOPs */
501 RegisterBop(BOP_DEBUGGER
, EmulatorDebugBreakBop
);
502 // RegisterBop(BOP_UNSIMULATE, CpuUnsimulateBop);
504 /* Initialize VDD support */
510 VOID
EmulatorCleanup(VOID
)
514 /* Close the input thread handle */
515 if (InputThread
!= NULL
) CloseHandle(InputThread
);
518 /* Close the task event */
519 if (VdmTaskEvent
!= NULL
) CloseHandle(VdmTaskEvent
);
546 VDDTerminateVDM(VOID
)