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 *******************************************************************/
15 #include "cpu/callback.h"
24 #include "hardware/cmos.h"
25 #include "hardware/keyboard.h"
26 #include "hardware/mouse.h"
27 #include "hardware/pic.h"
28 #include "hardware/ps2.h"
29 #include "hardware/speaker.h"
30 #include "hardware/timer.h"
31 #include "hardware/vga.h"
36 /* PRIVATE VARIABLES **********************************************************/
38 LPVOID BaseAddress
= NULL
;
39 BOOLEAN VdmRunning
= TRUE
;
41 static BOOLEAN A20Line
= FALSE
;
42 static BYTE Port61hState
= 0x00;
44 static HANDLE InputThread
= NULL
;
46 LPCWSTR ExceptionName
[] =
53 L
"Bound Range Exceeded",
59 #define BOP_DEBUGGER 0x56 // Break into the debugger from a 16-bit app
61 /* PRIVATE FUNCTIONS **********************************************************/
64 EmulatorMoveMemory(OUT VOID UNALIGNED
*Destination
,
65 IN
const VOID UNALIGNED
*Source
,
70 * We use a switch here to detect small moves of memory, as these
71 * constitute the bulk of our moves.
72 * Using RtlMoveMemory for all these small moves would be slow otherwise.
80 *(PUCHAR
)Destination
= *(PUCHAR
)Source
;
84 *(PUSHORT
)Destination
= *(PUSHORT
)Source
;
88 *(PULONG
)Destination
= *(PULONG
)Source
;
91 case sizeof(ULONGLONG
):
92 *(PULONGLONG
)Destination
= *(PULONGLONG
)Source
;
97 __builtin_memmove(Destination
, Source
, Length
);
99 RtlMoveMemory(Destination
, Source
, Length
);
103 #else // defined(_MSC_VER)
105 PUCHAR Dest
= (PUCHAR
)Destination
;
106 PUCHAR Src
= (PUCHAR
)Source
;
108 SIZE_T Count
, NewSize
= Length
;
111 Count
= NewSize
>> 2; // NewSize / sizeof(ULONG);
112 NewSize
= NewSize
& 3; // NewSize % sizeof(ULONG);
113 __movsd(Dest
, Src
, Count
);
114 Dest
+= Count
<< 2; // Count * sizeof(ULONG);
118 Count
= NewSize
>> 1; // NewSize / sizeof(USHORT);
119 NewSize
= NewSize
& 1; // NewSize % sizeof(USHORT);
120 __movsw(Dest
, Src
, Count
);
121 Dest
+= Count
<< 1; // Count * sizeof(USHORT);
125 Count
= NewSize
; // NewSize / sizeof(UCHAR);
126 // NewSize = NewSize; // NewSize % sizeof(UCHAR);
127 __movsb(Dest
, Src
, Count
);
132 VOID WINAPI
EmulatorReadMemory(PFAST486_STATE State
, ULONG Address
, PVOID Buffer
, ULONG Size
)
134 UNREFERENCED_PARAMETER(State
);
136 // BIG HACK!!!! To make BIOS images working correctly,
137 // until Aleksander rewrites memory management!!
138 if (Address
>= 0xFFFFFFF0) Address
-= 0xFFF00000;
140 /* If the A20 line is disabled, mask bit 20 */
141 if (!A20Line
) Address
&= ~(1 << 20);
143 /* Make sure the requested address is valid */
144 if ((Address
+ Size
) >= MAX_ADDRESS
) return;
147 * Check if we are going to read the VGA memory and
148 * copy it into the virtual address space if needed.
150 if (((Address
+ Size
) >= VgaGetVideoBaseAddress())
151 && (Address
< VgaGetVideoLimitAddress()))
153 DWORD VgaAddress
= max(Address
, VgaGetVideoBaseAddress());
154 DWORD ActualSize
= min(Address
+ Size
- 1, VgaGetVideoLimitAddress())
156 LPBYTE DestBuffer
= (LPBYTE
)REAL_TO_PHYS(VgaAddress
);
158 /* Read from the VGA memory */
159 VgaReadMemory(VgaAddress
, DestBuffer
, ActualSize
);
162 /* Read the data from the virtual address space and store it in the buffer */
163 EmulatorMoveMemory(Buffer
, REAL_TO_PHYS(Address
), Size
);
166 VOID WINAPI
EmulatorWriteMemory(PFAST486_STATE State
, ULONG Address
, PVOID Buffer
, ULONG Size
)
168 UNREFERENCED_PARAMETER(State
);
170 // BIG HACK!!!! To make BIOS images working correctly,
171 // until Aleksander rewrites memory management!!
172 if (Address
>= 0xFFFFFFF0) Address
-= 0xFFF00000;
174 /* If the A20 line is disabled, mask bit 20 */
175 if (!A20Line
) Address
&= ~(1 << 20);
177 /* Make sure the requested address is valid */
178 if ((Address
+ Size
) >= MAX_ADDRESS
) return;
180 /* Make sure we don't write to the ROM area */
181 if ((Address
+ Size
) >= ROM_AREA_START
&& (Address
< ROM_AREA_END
)) return;
183 /* Read the data from the buffer and store it in the virtual address space */
184 EmulatorMoveMemory(REAL_TO_PHYS(Address
), Buffer
, Size
);
187 * Check if we modified the VGA memory.
189 if (((Address
+ Size
) >= VgaGetVideoBaseAddress())
190 && (Address
< VgaGetVideoLimitAddress()))
192 DWORD VgaAddress
= max(Address
, VgaGetVideoBaseAddress());
193 DWORD ActualSize
= min(Address
+ Size
- 1, VgaGetVideoLimitAddress())
195 LPBYTE SrcBuffer
= (LPBYTE
)REAL_TO_PHYS(VgaAddress
);
197 /* Write to the VGA memory */
198 VgaWriteMemory(VgaAddress
, SrcBuffer
, ActualSize
);
202 UCHAR WINAPI
EmulatorIntAcknowledge(PFAST486_STATE State
)
204 UNREFERENCED_PARAMETER(State
);
206 /* Get the interrupt number from the PIC */
207 return PicGetInterrupt();
210 VOID
EmulatorException(BYTE ExceptionNumber
, LPWORD Stack
)
212 WORD CodeSegment
, InstructionPointer
;
215 ASSERT(ExceptionNumber
< 8);
218 InstructionPointer
= Stack
[STACK_IP
];
219 CodeSegment
= Stack
[STACK_CS
];
220 Opcode
= (PBYTE
)SEG_OFF_TO_PTR(CodeSegment
, InstructionPointer
);
222 /* Display a message to the user */
223 DisplayMessage(L
"Exception: %s occured at %04X:%04X\n"
224 L
"Opcode: %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X",
225 ExceptionName
[ExceptionNumber
],
239 Fast486DumpState(&EmulatorContext
);
246 VOID
EmulatorTerminate(VOID
)
249 CpuUnsimulate(); // Halt the CPU
253 VOID
EmulatorInterrupt(BYTE Number
)
255 /* Call the Fast486 API */
256 Fast486Interrupt(&EmulatorContext
, Number
);
259 VOID
EmulatorInterruptSignal(VOID
)
261 /* Call the Fast486 API */
262 Fast486InterruptSignal(&EmulatorContext
);
265 VOID
EmulatorSetA20(BOOLEAN Enabled
)
270 static VOID WINAPI
EmulatorDebugBreakBop(LPWORD Stack
)
272 DPRINT1("NTVDM: BOP_DEBUGGER\n");
276 static BYTE WINAPI
Port61hRead(ULONG Port
)
281 static VOID WINAPI
Port61hWrite(ULONG Port
, BYTE Data
)
283 // BOOLEAN SpeakerStateChange = FALSE;
284 BYTE OldPort61hState
= Port61hState
;
286 /* Only the four lowest bytes can be written */
287 Port61hState
= (Port61hState
& 0xF0) | (Data
& 0x0F);
289 if ((OldPort61hState
^ Port61hState
) & 0x01)
291 DPRINT("PIT 2 Gate %s\n", Port61hState
& 0x01 ? "on" : "off");
292 PitSetGate(2, !!(Port61hState
& 0x01));
293 // SpeakerStateChange = TRUE;
296 if ((OldPort61hState
^ Port61hState
) & 0x02)
298 /* There were some change for the speaker... */
299 DPRINT("Speaker %s\n", Port61hState
& 0x02 ? "on" : "off");
300 // SpeakerStateChange = TRUE;
302 // if (SpeakerStateChange) SpeakerChange();
306 static VOID WINAPI
PitChan0Out(LPVOID Param
, BOOLEAN State
)
310 DPRINT("PicInterruptRequest\n");
311 PicInterruptRequest(0); // Raise IRQ 0
313 // else < Lower IRQ 0 >
316 static VOID WINAPI
PitChan1Out(LPVOID Param
, BOOLEAN State
)
321 /* Set bit 4 of Port 61h */
322 Port61hState
|= 1 << 4;
326 /* Clear bit 4 of Port 61h */
327 Port61hState
&= ~(1 << 4);
330 Port61hState
= (Port61hState
& 0xEF) | (State
<< 4);
334 static VOID WINAPI
PitChan2Out(LPVOID Param
, BOOLEAN State
)
336 BYTE OldPort61hState
= Port61hState
;
341 /* Set bit 5 of Port 61h */
342 Port61hState
|= 1 << 5;
346 /* Clear bit 5 of Port 61h */
347 Port61hState
&= ~(1 << 5);
350 Port61hState
= (Port61hState
& 0xDF) | (State
<< 5);
353 if ((OldPort61hState
^ Port61hState
) & 0x20)
355 DPRINT("PitChan2Out -- Port61hState changed\n");
363 PumpConsoleInput(LPVOID Parameter
)
365 HANDLE ConsoleInput
= (HANDLE
)Parameter
;
366 INPUT_RECORD InputRecord
;
371 /* Make sure the task event is signaled */
372 WaitForSingleObject(VdmTaskEvent
, INFINITE
);
374 /* Wait for an input record */
375 if (!ReadConsoleInput(ConsoleInput
, &InputRecord
, 1, &Count
))
377 DWORD LastError
= GetLastError();
378 DPRINT1("Error reading console input (0x%p, %lu) - Error %lu\n", ConsoleInput
, Count
, LastError
);
384 /* Check the event type */
385 switch (InputRecord
.EventType
)
391 KeyboardEventHandler(&InputRecord
.Event
.KeyEvent
);
395 MouseEventHandler(&InputRecord
.Event
.MouseEvent
);
398 case WINDOW_BUFFER_SIZE_EVENT
:
399 ScreenEventHandler(&InputRecord
.Event
.WindowBufferSizeEvent
);
406 MenuEventHandler(&InputRecord
.Event
.MenuEvent
);
410 FocusEventHandler(&InputRecord
.Event
.FocusEvent
);
421 static VOID
EnableExtraHardware(HANDLE ConsoleInput
)
425 if (GetConsoleMode(ConsoleInput
, &ConInMode
))
428 // GetNumberOfConsoleMouseButtons();
429 // GetSystemMetrics(SM_CMOUSEBUTTONS);
430 // GetSystemMetrics(SM_MOUSEPRESENT);
434 /* Support mouse input events if there is a mouse on the system */
435 ConInMode
|= ENABLE_MOUSE_INPUT
;
440 /* Do not support mouse input events if there is no mouse on the system */
441 ConInMode
&= ~ENABLE_MOUSE_INPUT
;
445 SetConsoleMode(ConsoleInput
, ConInMode
);
449 /* PUBLIC FUNCTIONS ***********************************************************/
452 DumpMemoryRaw(HANDLE hFile
)
457 /* Dump the VM memory */
458 SetFilePointer(hFile
, 0, NULL
, FILE_BEGIN
);
459 Buffer
= REAL_TO_PHYS(NULL
);
460 Size
= MAX_ADDRESS
- (ULONG_PTR
)(NULL
);
461 WriteFile(hFile
, Buffer
, Size
, &Size
, NULL
);
465 DumpMemoryTxt(HANDLE hFile
)
467 #define LINE_SIZE 75 + 2
470 CHAR LineBuffer
[LINE_SIZE
];
474 /* Dump the VM memory */
475 SetFilePointer(hFile
, 0, NULL
, FILE_BEGIN
);
476 Ptr1
= Ptr2
= REAL_TO_PHYS(NULL
);
477 while (MAX_ADDRESS
- (ULONG_PTR
)PHYS_TO_REAL(Ptr1
) > 0)
482 /* Print the address */
483 Line
+= snprintf(Line
, LINE_SIZE
+ LineBuffer
- Line
, "%08x ", PHYS_TO_REAL(Ptr1
));
485 /* Print up to 16 bytes... */
487 /* ... in hexadecimal form first... */
489 while (i
++ <= 0x0F && (MAX_ADDRESS
- (ULONG_PTR
)PHYS_TO_REAL(Ptr1
) > 0))
491 Line
+= snprintf(Line
, LINE_SIZE
+ LineBuffer
- Line
, " %02x", *Ptr1
);
495 /* ... align with spaces if needed... */
496 RtlFillMemory(Line
, 0x0F + 4 - i
, ' ');
497 Line
+= 0x0F + 4 - i
;
499 /* ... then in character form. */
501 while (i
++ <= 0x0F && (MAX_ADDRESS
- (ULONG_PTR
)PHYS_TO_REAL(Ptr2
) > 0))
503 *Line
++ = ((*Ptr2
>= 0x20 && *Ptr2
<= 0x7E) || (*Ptr2
>= 0x80 && *Ptr2
< 0xFF) ? *Ptr2
: '.');
511 /* Finally write the line to the file */
512 LineSize
= Line
- LineBuffer
;
513 WriteFile(hFile
, LineBuffer
, LineSize
, &LineSize
, NULL
);
517 VOID
DumpMemory(BOOLEAN TextFormat
)
519 static ULONG DumpNumber
= 0;
522 WCHAR FileName
[MAX_PATH
];
524 /* Build a suitable file name */
525 _snwprintf(FileName
, MAX_PATH
,
528 TextFormat
? L
"txt" : L
"dat");
531 DPRINT1("Creating memory dump file '%S'...\n", FileName
);
533 /* Always create the dump file */
534 hFile
= CreateFileW(FileName
,
539 FILE_ATTRIBUTE_NORMAL
,
542 if (hFile
== INVALID_HANDLE_VALUE
)
544 DPRINT1("Error when creating '%S' for memory dumping, GetLastError() = %u\n",
545 FileName
, GetLastError());
549 /* Dump the VM memory in the chosen format */
551 DumpMemoryTxt(hFile
);
553 DumpMemoryRaw(hFile
);
558 DPRINT1("Memory dump done\n");
561 BOOLEAN
EmulatorInitialize(HANDLE ConsoleInput
, HANDLE ConsoleOutput
)
563 /* Allocate memory for the 16-bit address space */
564 BaseAddress
= HeapAlloc(GetProcessHeap(), /*HEAP_ZERO_MEMORY*/ 0, MAX_ADDRESS
);
565 if (BaseAddress
== NULL
)
567 wprintf(L
"FATAL: Failed to allocate VDM memory.\n");
571 * For diagnostics purposes, we fill the memory with INT 0x03 codes
572 * so that if a program wants to execute random code in memory, we can
573 * retrieve the exact CS:IP where the problem happens.
575 RtlFillMemory(BaseAddress
, MAX_ADDRESS
, 0xCC);
577 /* Initialize I/O ports */
580 /* Initialize the CPU */
582 /* Initialize the internal clock */
583 if (!ClockInitialize())
585 wprintf(L
"FATAL: Failed to initialize the clock\n");
589 /* Initialize the CPU */
591 // Fast486Initialize(&EmulatorContext,
592 // EmulatorReadMemory,
593 // EmulatorWriteMemory,
597 // EmulatorBiosOperation,
598 // EmulatorIntAcknowledge,
599 // NULL /* TODO: Use a TLB */);
603 /* Initialize the PIC, the PIT, the CMOS and the PC Speaker */
609 /* Set output functions */
610 PitSetOutFunction(0, NULL
, PitChan0Out
);
611 PitSetOutFunction(1, NULL
, PitChan1Out
);
612 PitSetOutFunction(2, NULL
, PitChan2Out
);
614 /* Register the I/O Ports */
615 RegisterIoPort(CONTROL_SYSTEM_PORT61H
, Port61hRead
, Port61hWrite
);
617 /* Set the console input mode */
618 // FIXME: Activate ENABLE_WINDOW_INPUT when we will want to perform actions
619 // upon console window events (screen buffer resize, ...).
620 SetConsoleMode(ConsoleInput
, ENABLE_PROCESSED_INPUT
/* | ENABLE_WINDOW_INPUT */);
621 // SetConsoleMode(ConsoleOutput, ENABLE_PROCESSED_OUTPUT | ENABLE_WRAP_AT_EOL_OUTPUT);
623 /**/EnableExtraHardware(ConsoleInput
);/**/
625 /* Initialize the PS/2 port */
628 /* Initialize the keyboard and mouse and connect them to their PS/2 ports */
632 /**************** ATTACH INPUT WITH CONSOLE *****************/
633 /* Start the input thread */
634 InputThread
= CreateThread(NULL
, 0, &PumpConsoleInput
, ConsoleInput
, 0, NULL
);
635 if (InputThread
== NULL
)
637 DisplayMessage(L
"Failed to create the console input thread.");
640 /************************************************************/
642 /* Initialize the VGA */
643 if (!VgaInitialize(ConsoleOutput
))
645 DisplayMessage(L
"Failed to initialize VGA support.");
649 /* Initialize the software callback system and register the emulator BOPs */
651 RegisterBop(BOP_DEBUGGER
, EmulatorDebugBreakBop
);
652 // RegisterBop(BOP_UNSIMULATE, CpuUnsimulateBop);
654 /* Initialize VDD support */
660 VOID
EmulatorCleanup(VOID
)
664 /* Close the input thread handle */
665 if (InputThread
!= NULL
) CloseHandle(InputThread
);
677 /* Free the memory allocated for the 16-bit address space */
678 if (BaseAddress
!= NULL
) HeapFree(GetProcessHeap(), 0, BaseAddress
);
692 VDDTerminateVDM(VOID
)
700 Sim32pGetVDMPointer(IN ULONG Address
,
701 IN BOOLEAN ProtectedMode
)
704 UNREFERENCED_PARAMETER(ProtectedMode
);
707 * HIWORD(Address) == Segment (if ProtectedMode == FALSE)
708 * or Selector (if ProtectedMode == TRUE )
709 * LOWORD(Address) == Offset
711 return (PBYTE
)FAR_POINTER(Address
);
716 MGetVdmPointer(IN ULONG Address
,
718 IN BOOLEAN ProtectedMode
)
720 UNREFERENCED_PARAMETER(Size
);
721 return Sim32pGetVDMPointer(Address
, ProtectedMode
);
726 VdmMapFlat(IN USHORT Segment
,
731 UNREFERENCED_PARAMETER(Mode
);
733 return SEG_OFF_TO_PTR(Segment
, Offset
);
738 VdmFlushCache(IN USHORT Segment
,
750 VdmUnmapFlat(IN USHORT Segment
,