3d50713edc90fc502e76fea63c360354bab8ef74
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 *******************************************************************/
16 #include "cpu/callback.h"
25 #include "hardware/cmos.h"
26 #include "hardware/dma.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/vga.h"
39 /* Extra PSDK/NDK Headers */
40 #include <ndk/psfuncs.h>
41 #include <ndk/mmfuncs.h>
43 /* PRIVATE VARIABLES **********************************************************/
45 LPVOID BaseAddress
= NULL
;
46 BOOLEAN VdmRunning
= TRUE
;
48 static BOOLEAN A20Line
= FALSE
;
49 static BYTE Port61hState
= 0x00;
51 static HANDLE InputThread
= NULL
;
53 LPCWSTR ExceptionName
[] =
60 L
"Bound Range Exceeded",
66 #define BOP_DEBUGGER 0x56 // Break into the debugger from a 16-bit app
68 /* PRIVATE FUNCTIONS **********************************************************/
70 VOID WINAPI
EmulatorReadMemory(PFAST486_STATE State
, ULONG Address
, PVOID Buffer
, ULONG Size
)
72 UNREFERENCED_PARAMETER(State
);
74 /* Mirror 0x000FFFF0 at 0xFFFFFFF0 */
75 if (Address
>= 0xFFFFFFF0) Address
-= 0xFFF00000;
77 /* If the A20 line is disabled, mask bit 20 */
78 if (!A20Line
) Address
&= ~(1 << 20);
80 if (Address
>= MAX_ADDRESS
) return;
81 Size
= min(Size
, MAX_ADDRESS
- Address
);
83 /* Read while calling fast memory hooks */
84 MemRead(Address
, Buffer
, Size
);
87 VOID WINAPI
EmulatorWriteMemory(PFAST486_STATE State
, ULONG Address
, PVOID Buffer
, ULONG Size
)
89 UNREFERENCED_PARAMETER(State
);
91 /* If the A20 line is disabled, mask bit 20 */
92 if (!A20Line
) Address
&= ~(1 << 20);
94 if (Address
>= MAX_ADDRESS
) return;
95 Size
= min(Size
, MAX_ADDRESS
- Address
);
97 /* Write while calling fast memory hooks */
98 MemWrite(Address
, Buffer
, Size
);
101 UCHAR WINAPI
EmulatorIntAcknowledge(PFAST486_STATE State
)
103 UNREFERENCED_PARAMETER(State
);
105 /* Get the interrupt number from the PIC */
106 return PicGetInterrupt();
109 VOID WINAPI
EmulatorFpu(PFAST486_STATE State
)
111 /* The FPU is wired to IRQ 13 */
112 PicInterruptRequest(13);
115 VOID
EmulatorException(BYTE ExceptionNumber
, LPWORD Stack
)
117 WORD CodeSegment
, InstructionPointer
;
120 ASSERT(ExceptionNumber
< 8);
123 InstructionPointer
= Stack
[STACK_IP
];
124 CodeSegment
= Stack
[STACK_CS
];
125 Opcode
= (PBYTE
)SEG_OFF_TO_PTR(CodeSegment
, InstructionPointer
);
127 /* Display a message to the user */
128 DisplayMessage(L
"Exception: %s occured at %04X:%04X\n"
129 L
"Opcode: %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X",
130 ExceptionName
[ExceptionNumber
],
144 Fast486DumpState(&EmulatorContext
);
151 VOID
EmulatorTerminate(VOID
)
154 CpuUnsimulate(); // Halt the CPU
158 VOID
EmulatorInterruptSignal(VOID
)
160 /* Call the Fast486 API */
161 Fast486InterruptSignal(&EmulatorContext
);
164 VOID
EmulatorSetA20(BOOLEAN Enabled
)
169 static VOID WINAPI
EmulatorDebugBreakBop(LPWORD Stack
)
171 DPRINT1("NTVDM: BOP_DEBUGGER\n");
175 static BYTE WINAPI
Port61hRead(USHORT Port
)
180 static VOID WINAPI
Port61hWrite(USHORT Port
, BYTE Data
)
182 // BOOLEAN SpeakerStateChange = FALSE;
183 BYTE OldPort61hState
= Port61hState
;
185 /* Only the four lowest bytes can be written */
186 Port61hState
= (Port61hState
& 0xF0) | (Data
& 0x0F);
188 if ((OldPort61hState
^ Port61hState
) & 0x01)
190 DPRINT("PIT 2 Gate %s\n", Port61hState
& 0x01 ? "on" : "off");
191 PitSetGate(2, !!(Port61hState
& 0x01));
192 // SpeakerStateChange = TRUE;
195 if ((OldPort61hState
^ Port61hState
) & 0x02)
197 /* There were some change for the speaker... */
198 DPRINT("Speaker %s\n", Port61hState
& 0x02 ? "on" : "off");
199 // SpeakerStateChange = TRUE;
201 // if (SpeakerStateChange) SpeakerChange(Port61hState);
202 SpeakerChange(Port61hState
);
205 static VOID WINAPI
PitChan0Out(LPVOID Param
, BOOLEAN State
)
209 DPRINT("PicInterruptRequest\n");
210 PicInterruptRequest(0); // Raise IRQ 0
212 // else < Lower IRQ 0 >
215 static VOID WINAPI
PitChan1Out(LPVOID Param
, BOOLEAN State
)
220 /* Set bit 4 of Port 61h */
221 Port61hState
|= 1 << 4;
225 /* Clear bit 4 of Port 61h */
226 Port61hState
&= ~(1 << 4);
229 Port61hState
= (Port61hState
& 0xEF) | (State
<< 4);
233 static VOID WINAPI
PitChan2Out(LPVOID Param
, BOOLEAN State
)
235 BYTE OldPort61hState
= Port61hState
;
240 /* Set bit 5 of Port 61h */
241 Port61hState
|= 1 << 5;
245 /* Clear bit 5 of Port 61h */
246 Port61hState
&= ~(1 << 5);
249 Port61hState
= (Port61hState
& 0xDF) | (State
<< 5);
252 if ((OldPort61hState
^ Port61hState
) & 0x20)
254 DPRINT("PitChan2Out -- Port61hState changed\n");
255 SpeakerChange(Port61hState
);
262 PumpConsoleInput(LPVOID Parameter
)
264 HANDLE ConsoleInput
= (HANDLE
)Parameter
;
265 INPUT_RECORD InputRecord
;
270 /* Make sure the task event is signaled */
271 WaitForSingleObject(VdmTaskEvent
, INFINITE
);
273 /* Wait for an input record */
274 if (!ReadConsoleInput(ConsoleInput
, &InputRecord
, 1, &Count
))
276 DWORD LastError
= GetLastError();
277 DPRINT1("Error reading console input (0x%p, %lu) - Error %lu\n", ConsoleInput
, Count
, LastError
);
283 /* Check the event type */
284 switch (InputRecord
.EventType
)
290 KeyboardEventHandler(&InputRecord
.Event
.KeyEvent
);
294 MouseEventHandler(&InputRecord
.Event
.MouseEvent
);
297 case WINDOW_BUFFER_SIZE_EVENT
:
298 ScreenEventHandler(&InputRecord
.Event
.WindowBufferSizeEvent
);
305 MenuEventHandler(&InputRecord
.Event
.MenuEvent
);
309 FocusEventHandler(&InputRecord
.Event
.FocusEvent
);
320 static VOID
EnableExtraHardware(HANDLE ConsoleInput
)
324 if (GetConsoleMode(ConsoleInput
, &ConInMode
))
327 // GetNumberOfConsoleMouseButtons();
328 // GetSystemMetrics(SM_CMOUSEBUTTONS);
329 // GetSystemMetrics(SM_MOUSEPRESENT);
333 /* Support mouse input events if there is a mouse on the system */
334 ConInMode
|= ENABLE_MOUSE_INPUT
;
339 /* Do not support mouse input events if there is no mouse on the system */
340 ConInMode
&= ~ENABLE_MOUSE_INPUT
;
344 SetConsoleMode(ConsoleInput
, ConInMode
);
348 /* PUBLIC FUNCTIONS ***********************************************************/
351 DumpMemoryRaw(HANDLE hFile
)
356 /* Dump the VM memory */
357 SetFilePointer(hFile
, 0, NULL
, FILE_BEGIN
);
358 Buffer
= REAL_TO_PHYS(NULL
);
359 Size
= MAX_ADDRESS
- (ULONG_PTR
)(NULL
);
360 WriteFile(hFile
, Buffer
, Size
, &Size
, NULL
);
364 DumpMemoryTxt(HANDLE hFile
)
366 #define LINE_SIZE 75 + 2
369 CHAR LineBuffer
[LINE_SIZE
];
373 /* Dump the VM memory */
374 SetFilePointer(hFile
, 0, NULL
, FILE_BEGIN
);
375 Ptr1
= Ptr2
= REAL_TO_PHYS(NULL
);
376 while (MAX_ADDRESS
- (ULONG_PTR
)PHYS_TO_REAL(Ptr1
) > 0)
381 /* Print the address */
382 Line
+= snprintf(Line
, LINE_SIZE
+ LineBuffer
- Line
, "%08x ", PHYS_TO_REAL(Ptr1
));
384 /* Print up to 16 bytes... */
386 /* ... in hexadecimal form first... */
388 while (i
++ <= 0x0F && (MAX_ADDRESS
- (ULONG_PTR
)PHYS_TO_REAL(Ptr1
) > 0))
390 Line
+= snprintf(Line
, LINE_SIZE
+ LineBuffer
- Line
, " %02x", *Ptr1
);
394 /* ... align with spaces if needed... */
395 RtlFillMemory(Line
, 0x0F + 4 - i
, ' ');
396 Line
+= 0x0F + 4 - i
;
398 /* ... then in character form. */
400 while (i
++ <= 0x0F && (MAX_ADDRESS
- (ULONG_PTR
)PHYS_TO_REAL(Ptr2
) > 0))
402 *Line
++ = ((*Ptr2
>= 0x20 && *Ptr2
<= 0x7E) || (*Ptr2
>= 0x80 && *Ptr2
< 0xFF) ? *Ptr2
: '.');
410 /* Finally write the line to the file */
411 LineSize
= Line
- LineBuffer
;
412 WriteFile(hFile
, LineBuffer
, LineSize
, &LineSize
, NULL
);
416 VOID
DumpMemory(BOOLEAN TextFormat
)
418 static ULONG DumpNumber
= 0;
421 WCHAR FileName
[MAX_PATH
];
423 /* Build a suitable file name */
424 _snwprintf(FileName
, MAX_PATH
,
427 TextFormat
? L
"txt" : L
"dat");
430 DPRINT1("Creating memory dump file '%S'...\n", FileName
);
432 /* Always create the dump file */
433 hFile
= CreateFileW(FileName
,
438 FILE_ATTRIBUTE_NORMAL
,
441 if (hFile
== INVALID_HANDLE_VALUE
)
443 DPRINT1("Error when creating '%S' for memory dumping, GetLastError() = %u\n",
444 FileName
, GetLastError());
448 /* Dump the VM memory in the chosen format */
450 DumpMemoryTxt(hFile
);
452 DumpMemoryRaw(hFile
);
457 DPRINT1("Memory dump done\n");
460 BOOLEAN
EmulatorInitialize(HANDLE ConsoleInput
, HANDLE ConsoleOutput
)
462 /* Initialize memory */
463 if (!MemInitialize())
465 wprintf(L
"Memory initialization failed.\n");
469 /* Initialize I/O ports */
475 /* Initialize the CPU */
477 /* Initialize the internal clock */
478 if (!ClockInitialize())
480 wprintf(L
"FATAL: Failed to initialize the clock\n");
485 /* Initialize the CPU */
491 /* Initialize the PIC, the PIT, the CMOS and the PC Speaker */
497 /* Set output functions */
498 PitSetOutFunction(0, NULL
, PitChan0Out
);
499 PitSetOutFunction(1, NULL
, PitChan1Out
);
500 PitSetOutFunction(2, NULL
, PitChan2Out
);
502 /* Register the I/O Ports */
503 RegisterIoPort(CONTROL_SYSTEM_PORT61H
, Port61hRead
, Port61hWrite
);
505 /* Set the console input mode */
506 // FIXME: Activate ENABLE_WINDOW_INPUT when we will want to perform actions
507 // upon console window events (screen buffer resize, ...).
508 SetConsoleMode(ConsoleInput
, ENABLE_PROCESSED_INPUT
/* | ENABLE_WINDOW_INPUT */);
509 // SetConsoleMode(ConsoleOutput, ENABLE_PROCESSED_OUTPUT | ENABLE_WRAP_AT_EOL_OUTPUT);
511 /**/EnableExtraHardware(ConsoleInput
);/**/
513 /* Initialize the PS/2 port */
516 /* Initialize the keyboard and mouse and connect them to their PS/2 ports */
520 /**************** ATTACH INPUT WITH CONSOLE *****************/
521 /* Start the input thread */
522 InputThread
= CreateThread(NULL
, 0, &PumpConsoleInput
, ConsoleInput
, 0, NULL
);
523 if (InputThread
== NULL
)
525 DisplayMessage(L
"Failed to create the console input thread.");
529 /************************************************************/
531 /* Initialize the VGA */
532 if (!VgaInitialize(ConsoleOutput
))
534 DisplayMessage(L
"Failed to initialize VGA support.");
539 /* Initialize the software callback system and register the emulator BOPs */
541 RegisterBop(BOP_DEBUGGER
, EmulatorDebugBreakBop
);
542 // RegisterBop(BOP_UNSIMULATE, CpuUnsimulateBop);
544 /* Initialize VDD support */
550 VOID
EmulatorCleanup(VOID
)
554 /* Close the input thread handle */
555 if (InputThread
!= NULL
) CloseHandle(InputThread
);
583 VDDTerminateVDM(VOID
)
591 Sim32pGetVDMPointer(IN ULONG Address
,
592 IN BOOLEAN ProtectedMode
)
595 UNREFERENCED_PARAMETER(ProtectedMode
);
598 * HIWORD(Address) == Segment (if ProtectedMode == FALSE)
599 * or Selector (if ProtectedMode == TRUE )
600 * LOWORD(Address) == Offset
602 return (PBYTE
)FAR_POINTER(Address
);
607 MGetVdmPointer(IN ULONG Address
,
609 IN BOOLEAN ProtectedMode
)
611 UNREFERENCED_PARAMETER(Size
);
612 return Sim32pGetVDMPointer(Address
, ProtectedMode
);
617 VdmMapFlat(IN USHORT Segment
,
622 UNREFERENCED_PARAMETER(Mode
);
624 return SEG_OFF_TO_PTR(Segment
, Offset
);
629 VdmFlushCache(IN USHORT Segment
,
641 VdmUnmapFlat(IN USHORT Segment
,