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/ps2.h"
32 #include "hardware/sound/speaker.h"
33 #include "hardware/pit.h"
34 #include "hardware/video/vga.h"
39 /* PRIVATE VARIABLES **********************************************************/
41 LPVOID BaseAddress
= NULL
;
42 BOOLEAN VdmRunning
= TRUE
;
44 static BOOLEAN A20Line
= FALSE
;
45 static BYTE Port61hState
= 0x00;
47 static HANDLE InputThread
= NULL
;
49 LPCWSTR ExceptionName
[] =
56 L
"Bound Range Exceeded",
62 #define BOP_DEBUGGER 0x56 // Break into the debugger from a 16-bit app
64 /* PRIVATE FUNCTIONS **********************************************************/
66 VOID WINAPI
EmulatorReadMemory(PFAST486_STATE State
, ULONG Address
, PVOID Buffer
, ULONG Size
)
68 UNREFERENCED_PARAMETER(State
);
70 /* Mirror 0x000FFFF0 at 0xFFFFFFF0 */
71 if (Address
>= 0xFFFFFFF0) Address
-= 0xFFF00000;
73 /* If the A20 line is disabled, mask bit 20 */
74 if (!A20Line
) Address
&= ~(1 << 20);
76 if ((Address
+ Size
- 1) >= MAX_ADDRESS
)
78 ULONG ExtraStart
= (Address
< MAX_ADDRESS
) ? MAX_ADDRESS
- Address
: 0;
80 /* Fill the memory that was above the limit with 0xFF */
81 RtlFillMemory((PVOID
)((ULONG_PTR
)Buffer
+ ExtraStart
), Size
- ExtraStart
, 0xFF);
83 if (Address
< MAX_ADDRESS
) Size
= MAX_ADDRESS
- Address
;
87 /* Read while calling fast memory hooks */
88 MemRead(Address
, Buffer
, Size
);
91 VOID WINAPI
EmulatorWriteMemory(PFAST486_STATE State
, ULONG Address
, PVOID Buffer
, ULONG Size
)
93 UNREFERENCED_PARAMETER(State
);
95 /* If the A20 line is disabled, mask bit 20 */
96 if (!A20Line
) Address
&= ~(1 << 20);
98 if (Address
>= MAX_ADDRESS
) return;
99 Size
= min(Size
, MAX_ADDRESS
- Address
);
101 /* Write while calling fast memory hooks */
102 MemWrite(Address
, Buffer
, Size
);
105 UCHAR WINAPI
EmulatorIntAcknowledge(PFAST486_STATE State
)
107 UNREFERENCED_PARAMETER(State
);
109 /* Get the interrupt number from the PIC */
110 return PicGetInterrupt();
113 VOID WINAPI
EmulatorFpu(PFAST486_STATE State
)
115 /* The FPU is wired to IRQ 13 */
116 PicInterruptRequest(13);
119 VOID
EmulatorException(BYTE ExceptionNumber
, LPWORD Stack
)
121 WORD CodeSegment
, InstructionPointer
;
124 ASSERT(ExceptionNumber
< 8);
127 InstructionPointer
= Stack
[STACK_IP
];
128 CodeSegment
= Stack
[STACK_CS
];
129 Opcode
= (PBYTE
)SEG_OFF_TO_PTR(CodeSegment
, InstructionPointer
);
131 /* Display a message to the user */
132 DisplayMessage(L
"Exception: %s occurred at %04X:%04X\n"
133 L
"Opcode: %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X",
134 ExceptionName
[ExceptionNumber
],
148 Fast486DumpState(&EmulatorContext
);
155 VOID
EmulatorTerminate(VOID
)
158 CpuUnsimulate(); // Halt the CPU
162 VOID
EmulatorInterruptSignal(VOID
)
164 /* Call the Fast486 API */
165 Fast486InterruptSignal(&EmulatorContext
);
168 VOID
EmulatorSetA20(BOOLEAN Enabled
)
173 BOOLEAN
EmulatorGetA20(VOID
)
178 static VOID WINAPI
EmulatorDebugBreakBop(LPWORD Stack
)
180 DPRINT1("NTVDM: BOP_DEBUGGER\n");
184 static BYTE WINAPI
Port61hRead(USHORT Port
)
189 static VOID WINAPI
Port61hWrite(USHORT Port
, BYTE Data
)
191 // BOOLEAN SpeakerStateChange = FALSE;
192 BYTE OldPort61hState
= Port61hState
;
194 /* Only the four lowest bytes can be written */
195 Port61hState
= (Port61hState
& 0xF0) | (Data
& 0x0F);
197 if ((OldPort61hState
^ Port61hState
) & 0x01)
199 DPRINT("PIT 2 Gate %s\n", Port61hState
& 0x01 ? "on" : "off");
200 PitSetGate(2, !!(Port61hState
& 0x01));
201 // SpeakerStateChange = TRUE;
204 if ((OldPort61hState
^ Port61hState
) & 0x02)
206 /* There were some change for the speaker... */
207 DPRINT("Speaker %s\n", Port61hState
& 0x02 ? "on" : "off");
208 // SpeakerStateChange = TRUE;
210 // if (SpeakerStateChange) SpeakerChange(Port61hState);
211 SpeakerChange(Port61hState
);
214 static VOID WINAPI
PitChan0Out(LPVOID Param
, BOOLEAN State
)
218 DPRINT("PicInterruptRequest\n");
219 PicInterruptRequest(0); // Raise IRQ 0
221 // else < Lower IRQ 0 >
224 static VOID WINAPI
PitChan1Out(LPVOID Param
, BOOLEAN State
)
229 /* Set bit 4 of Port 61h */
230 Port61hState
|= 1 << 4;
234 /* Clear bit 4 of Port 61h */
235 Port61hState
&= ~(1 << 4);
238 Port61hState
= (Port61hState
& 0xEF) | (State
<< 4);
242 static VOID WINAPI
PitChan2Out(LPVOID Param
, BOOLEAN State
)
244 BYTE OldPort61hState
= Port61hState
;
249 /* Set bit 5 of Port 61h */
250 Port61hState
|= 1 << 5;
254 /* Clear bit 5 of Port 61h */
255 Port61hState
&= ~(1 << 5);
258 Port61hState
= (Port61hState
& 0xDF) | (State
<< 5);
261 if ((OldPort61hState
^ Port61hState
) & 0x20)
263 DPRINT("PitChan2Out -- Port61hState changed\n");
264 SpeakerChange(Port61hState
);
271 PumpConsoleInput(LPVOID Parameter
)
273 HANDLE ConsoleInput
= (HANDLE
)Parameter
;
274 INPUT_RECORD InputRecord
;
279 /* Make sure the task event is signaled */
280 WaitForSingleObject(VdmTaskEvent
, INFINITE
);
282 /* Wait for an input record */
283 if (!ReadConsoleInput(ConsoleInput
, &InputRecord
, 1, &Count
))
285 DWORD LastError
= GetLastError();
286 DPRINT1("Error reading console input (0x%p, %lu) - Error %lu\n", ConsoleInput
, Count
, LastError
);
292 /* Check the event type */
293 switch (InputRecord
.EventType
)
299 KeyboardEventHandler(&InputRecord
.Event
.KeyEvent
);
303 MouseEventHandler(&InputRecord
.Event
.MouseEvent
);
306 case WINDOW_BUFFER_SIZE_EVENT
:
307 ScreenEventHandler(&InputRecord
.Event
.WindowBufferSizeEvent
);
314 MenuEventHandler(&InputRecord
.Event
.MenuEvent
);
318 FocusEventHandler(&InputRecord
.Event
.FocusEvent
);
329 /* PUBLIC FUNCTIONS ***********************************************************/
332 DumpMemoryRaw(HANDLE hFile
)
337 /* Dump the VM memory */
338 SetFilePointer(hFile
, 0, NULL
, FILE_BEGIN
);
339 Buffer
= REAL_TO_PHYS(NULL
);
340 Size
= MAX_ADDRESS
- (ULONG_PTR
)(NULL
);
341 WriteFile(hFile
, Buffer
, Size
, &Size
, NULL
);
345 DumpMemoryTxt(HANDLE hFile
)
347 #define LINE_SIZE 75 + 2
350 CHAR LineBuffer
[LINE_SIZE
];
354 /* Dump the VM memory */
355 SetFilePointer(hFile
, 0, NULL
, FILE_BEGIN
);
356 Ptr1
= Ptr2
= REAL_TO_PHYS(NULL
);
357 while (MAX_ADDRESS
- (ULONG_PTR
)PHYS_TO_REAL(Ptr1
) > 0)
362 /* Print the address */
363 Line
+= snprintf(Line
, LINE_SIZE
+ LineBuffer
- Line
, "%08x ", PHYS_TO_REAL(Ptr1
));
365 /* Print up to 16 bytes... */
367 /* ... in hexadecimal form first... */
369 while (i
++ <= 0x0F && (MAX_ADDRESS
- (ULONG_PTR
)PHYS_TO_REAL(Ptr1
) > 0))
371 Line
+= snprintf(Line
, LINE_SIZE
+ LineBuffer
- Line
, " %02x", *Ptr1
);
375 /* ... align with spaces if needed... */
376 RtlFillMemory(Line
, 0x0F + 4 - i
, ' ');
377 Line
+= 0x0F + 4 - i
;
379 /* ... then in character form. */
381 while (i
++ <= 0x0F && (MAX_ADDRESS
- (ULONG_PTR
)PHYS_TO_REAL(Ptr2
) > 0))
383 *Line
++ = ((*Ptr2
>= 0x20 && *Ptr2
<= 0x7E) || (*Ptr2
>= 0x80 && *Ptr2
< 0xFF) ? *Ptr2
: '.');
391 /* Finally write the line to the file */
392 LineSize
= Line
- LineBuffer
;
393 WriteFile(hFile
, LineBuffer
, LineSize
, &LineSize
, NULL
);
397 VOID
DumpMemory(BOOLEAN TextFormat
)
399 static ULONG DumpNumber
= 0;
402 WCHAR FileName
[MAX_PATH
];
404 /* Build a suitable file name */
405 _snwprintf(FileName
, MAX_PATH
,
408 TextFormat
? L
"txt" : L
"dat");
411 DPRINT1("Creating memory dump file '%S'...\n", FileName
);
413 /* Always create the dump file */
414 hFile
= CreateFileW(FileName
,
419 FILE_ATTRIBUTE_NORMAL
,
422 if (hFile
== INVALID_HANDLE_VALUE
)
424 DPRINT1("Error when creating '%S' for memory dumping, GetLastError() = %u\n",
425 FileName
, GetLastError());
429 /* Dump the VM memory in the chosen format */
431 DumpMemoryTxt(hFile
);
433 DumpMemoryRaw(hFile
);
438 DPRINT1("Memory dump done\n");
441 BOOLEAN
EmulatorInitialize(HANDLE ConsoleInput
, HANDLE ConsoleOutput
)
443 /* Initialize memory */
444 if (!MemInitialize())
446 wprintf(L
"Memory initialization failed.\n");
450 /* Initialize I/O ports */
453 /* Initialize the CPU */
455 /* Initialize the internal clock */
456 if (!ClockInitialize())
458 wprintf(L
"FATAL: Failed to initialize the clock\n");
463 /* Initialize the CPU */
469 /* Initialize the PIC, the PIT, the CMOS and the PC Speaker */
475 /* Set output functions */
476 PitSetOutFunction(0, NULL
, PitChan0Out
);
477 PitSetOutFunction(1, NULL
, PitChan1Out
);
478 PitSetOutFunction(2, NULL
, PitChan2Out
);
480 /* Register the I/O Ports */
481 RegisterIoPort(CONTROL_SYSTEM_PORT61H
, Port61hRead
, Port61hWrite
);
483 /* Initialize the PS/2 port */
486 /* Initialize the keyboard and mouse and connect them to their PS/2 ports */
490 /**************** ATTACH INPUT WITH CONSOLE *****************/
491 /* Start the input thread */
492 InputThread
= CreateThread(NULL
, 0, &PumpConsoleInput
, ConsoleInput
, 0, NULL
);
493 if (InputThread
== NULL
)
495 DisplayMessage(L
"Failed to create the console input thread.");
499 /************************************************************/
501 /* Initialize the VGA */
502 if (!VgaInitialize(ConsoleOutput
))
504 DisplayMessage(L
"Failed to initialize VGA support.");
509 /* Initialize the software callback system and register the emulator BOPs */
511 RegisterBop(BOP_DEBUGGER
, EmulatorDebugBreakBop
);
512 // RegisterBop(BOP_UNSIMULATE, CpuUnsimulateBop);
514 /* Initialize VDD support */
520 VOID
EmulatorCleanup(VOID
)
524 /* Close the input thread handle */
525 if (InputThread
!= NULL
) CloseHandle(InputThread
);
552 VDDTerminateVDM(VOID
)