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 **********************************************************/
63 VOID WINAPI
EmulatorReadMemory(PFAST486_STATE State
, ULONG Address
, PVOID Buffer
, ULONG Size
)
65 UNREFERENCED_PARAMETER(State
);
67 // BIG HACK!!!! To make BIOS images working correctly,
68 // until Aleksander rewrites memory management!!
69 if (Address
>= 0xFFFFFFF0) Address
-= 0xFFF00000;
71 /* If the A20 line is disabled, mask bit 20 */
72 if (!A20Line
) Address
&= ~(1 << 20);
74 /* Make sure the requested address is valid */
75 if ((Address
+ Size
) >= MAX_ADDRESS
) return;
78 * Check if we are going to read the VGA memory and
79 * copy it into the virtual address space if needed.
81 if (((Address
+ Size
) >= VgaGetVideoBaseAddress())
82 && (Address
< VgaGetVideoLimitAddress()))
84 DWORD VgaAddress
= max(Address
, VgaGetVideoBaseAddress());
85 DWORD ActualSize
= min(Address
+ Size
- 1, VgaGetVideoLimitAddress())
87 LPBYTE DestBuffer
= (LPBYTE
)REAL_TO_PHYS(VgaAddress
);
89 /* Read from the VGA memory */
90 VgaReadMemory(VgaAddress
, DestBuffer
, ActualSize
);
93 /* Read the data from the virtual address space and store it in the buffer */
94 RtlCopyMemory(Buffer
, REAL_TO_PHYS(Address
), Size
);
97 VOID WINAPI
EmulatorWriteMemory(PFAST486_STATE State
, ULONG Address
, PVOID Buffer
, ULONG Size
)
99 UNREFERENCED_PARAMETER(State
);
101 // BIG HACK!!!! To make BIOS images working correctly,
102 // until Aleksander rewrites memory management!!
103 if (Address
>= 0xFFFFFFF0) Address
-= 0xFFF00000;
105 /* If the A20 line is disabled, mask bit 20 */
106 if (!A20Line
) Address
&= ~(1 << 20);
108 /* Make sure the requested address is valid */
109 if ((Address
+ Size
) >= MAX_ADDRESS
) return;
111 /* Make sure we don't write to the ROM area */
112 if ((Address
+ Size
) >= ROM_AREA_START
&& (Address
< ROM_AREA_END
)) return;
114 /* Read the data from the buffer and store it in the virtual address space */
115 RtlCopyMemory(REAL_TO_PHYS(Address
), Buffer
, Size
);
118 * Check if we modified the VGA memory.
120 if (((Address
+ Size
) >= VgaGetVideoBaseAddress())
121 && (Address
< VgaGetVideoLimitAddress()))
123 DWORD VgaAddress
= max(Address
, VgaGetVideoBaseAddress());
124 DWORD ActualSize
= min(Address
+ Size
- 1, VgaGetVideoLimitAddress())
126 LPBYTE SrcBuffer
= (LPBYTE
)REAL_TO_PHYS(VgaAddress
);
128 /* Write to the VGA memory */
129 VgaWriteMemory(VgaAddress
, SrcBuffer
, ActualSize
);
133 UCHAR WINAPI
EmulatorIntAcknowledge(PFAST486_STATE State
)
135 UNREFERENCED_PARAMETER(State
);
137 /* Get the interrupt number from the PIC */
138 return PicGetInterrupt();
141 VOID
EmulatorException(BYTE ExceptionNumber
, LPWORD Stack
)
143 WORD CodeSegment
, InstructionPointer
;
146 ASSERT(ExceptionNumber
< 8);
149 InstructionPointer
= Stack
[STACK_IP
];
150 CodeSegment
= Stack
[STACK_CS
];
151 Opcode
= (PBYTE
)SEG_OFF_TO_PTR(CodeSegment
, InstructionPointer
);
153 /* Display a message to the user */
154 DisplayMessage(L
"Exception: %s occured at %04X:%04X\n"
155 L
"Opcode: %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X",
156 ExceptionName
[ExceptionNumber
],
175 VOID
EmulatorTerminate(VOID
)
181 VOID
EmulatorInterrupt(BYTE Number
)
183 /* Call the Fast486 API */
184 Fast486Interrupt(&EmulatorContext
, Number
);
187 VOID
EmulatorInterruptSignal(VOID
)
189 /* Call the Fast486 API */
190 Fast486InterruptSignal(&EmulatorContext
);
193 VOID
EmulatorSetA20(BOOLEAN Enabled
)
198 static VOID WINAPI
EmulatorDebugBreakBop(LPWORD Stack
)
200 DPRINT1("NTVDM: BOP_DEBUGGER\n");
204 static BYTE WINAPI
Port61hRead(ULONG Port
)
209 static VOID WINAPI
Port61hWrite(ULONG Port
, BYTE Data
)
211 // BOOLEAN SpeakerStateChange = FALSE;
212 BYTE OldPort61hState
= Port61hState
;
214 /* Only the four lowest bytes can be written */
215 Port61hState
= (Port61hState
& 0xF0) | (Data
& 0x0F);
217 if ((OldPort61hState
^ Port61hState
) & 0x01)
219 DPRINT("PIT 2 Gate %s\n", Port61hState
& 0x01 ? "on" : "off");
220 PitSetGate(2, !!(Port61hState
& 0x01));
221 // SpeakerStateChange = TRUE;
224 if ((OldPort61hState
^ Port61hState
) & 0x02)
226 /* There were some change for the speaker... */
227 DPRINT("Speaker %s\n", Port61hState
& 0x02 ? "on" : "off");
228 // SpeakerStateChange = TRUE;
230 // if (SpeakerStateChange) SpeakerChange();
234 static VOID WINAPI
PitChan0Out(LPVOID Param
, BOOLEAN State
)
238 DPRINT("PicInterruptRequest\n");
239 PicInterruptRequest(0); // Raise IRQ 0
241 // else < Lower IRQ 0 >
244 static VOID WINAPI
PitChan1Out(LPVOID Param
, BOOLEAN State
)
249 /* Set bit 4 of Port 61h */
250 Port61hState
|= 1 << 4;
254 /* Clear bit 4 of Port 61h */
255 Port61hState
&= ~(1 << 4);
258 Port61hState
= (Port61hState
& 0xEF) | (State
<< 4);
262 static VOID WINAPI
PitChan2Out(LPVOID Param
, BOOLEAN State
)
264 BYTE OldPort61hState
= Port61hState
;
269 /* Set bit 5 of Port 61h */
270 Port61hState
|= 1 << 5;
274 /* Clear bit 5 of Port 61h */
275 Port61hState
&= ~(1 << 5);
278 Port61hState
= (Port61hState
& 0xDF) | (State
<< 5);
281 if ((OldPort61hState
^ Port61hState
) & 0x20)
283 DPRINT("PitChan2Out -- Port61hState changed\n");
291 PumpConsoleInput(LPVOID Parameter
)
293 HANDLE ConsoleInput
= (HANDLE
)Parameter
;
294 INPUT_RECORD InputRecord
;
299 /* Make sure the task event is signaled */
300 WaitForSingleObject(VdmTaskEvent
, INFINITE
);
302 /* Wait for an input record */
303 if (!ReadConsoleInput(ConsoleInput
, &InputRecord
, 1, &Count
))
305 DWORD LastError
= GetLastError();
306 DPRINT1("Error reading console input (0x%p, %lu) - Error %lu\n", ConsoleInput
, Count
, LastError
);
312 /* Check the event type */
313 switch (InputRecord
.EventType
)
319 KeyboardEventHandler(&InputRecord
.Event
.KeyEvent
);
323 MouseEventHandler(&InputRecord
.Event
.MouseEvent
);
326 case WINDOW_BUFFER_SIZE_EVENT
:
327 ScreenEventHandler(&InputRecord
.Event
.WindowBufferSizeEvent
);
334 MenuEventHandler(&InputRecord
.Event
.MenuEvent
);
338 FocusEventHandler(&InputRecord
.Event
.FocusEvent
);
349 static VOID
EnableExtraHardware(HANDLE ConsoleInput
)
353 if (GetConsoleMode(ConsoleInput
, &ConInMode
))
356 // GetNumberOfConsoleMouseButtons();
357 // GetSystemMetrics(SM_CMOUSEBUTTONS);
358 // GetSystemMetrics(SM_MOUSEPRESENT);
362 /* Support mouse input events if there is a mouse on the system */
363 ConInMode
|= ENABLE_MOUSE_INPUT
;
368 /* Do not support mouse input events if there is no mouse on the system */
369 ConInMode
&= ~ENABLE_MOUSE_INPUT
;
373 SetConsoleMode(ConsoleInput
, ConInMode
);
377 /* PUBLIC FUNCTIONS ***********************************************************/
380 DumpMemoryRaw(HANDLE hFile
)
385 /* Dump the VM memory */
386 SetFilePointer(hFile
, 0, NULL
, FILE_BEGIN
);
387 Buffer
= REAL_TO_PHYS(NULL
);
388 Size
= MAX_ADDRESS
- (ULONG_PTR
)(NULL
);
389 WriteFile(hFile
, Buffer
, Size
, &Size
, NULL
);
393 DumpMemoryTxt(HANDLE hFile
)
395 #define LINE_SIZE 75 + 2
398 CHAR LineBuffer
[LINE_SIZE
];
402 /* Dump the VM memory */
403 SetFilePointer(hFile
, 0, NULL
, FILE_BEGIN
);
404 Ptr1
= Ptr2
= REAL_TO_PHYS(NULL
);
405 while (MAX_ADDRESS
- (ULONG_PTR
)PHYS_TO_REAL(Ptr1
) > 0)
410 /* Print the address */
411 Line
+= snprintf(Line
, LINE_SIZE
+ LineBuffer
- Line
, "%08x ", PHYS_TO_REAL(Ptr1
));
413 /* Print up to 16 bytes... */
415 /* ... in hexadecimal form first... */
417 while (i
++ <= 0x0F && (MAX_ADDRESS
- (ULONG_PTR
)PHYS_TO_REAL(Ptr1
) > 0))
419 Line
+= snprintf(Line
, LINE_SIZE
+ LineBuffer
- Line
, " %02x", *Ptr1
);
423 /* ... align with spaces if needed... */
424 RtlFillMemory(Line
, 0x0F + 4 - i
, ' ');
425 Line
+= 0x0F + 4 - i
;
427 /* ... then in character form. */
429 while (i
++ <= 0x0F && (MAX_ADDRESS
- (ULONG_PTR
)PHYS_TO_REAL(Ptr2
) > 0))
431 *Line
++ = ((*Ptr2
>= 0x20 && *Ptr2
<= 0x7E) || (*Ptr2
>= 0x80 && *Ptr2
< 0xFF) ? *Ptr2
: '.');
439 /* Finally write the line to the file */
440 LineSize
= Line
- LineBuffer
;
441 WriteFile(hFile
, LineBuffer
, LineSize
, &LineSize
, NULL
);
445 VOID
DumpMemory(BOOLEAN TextFormat
)
447 static ULONG DumpNumber
= 0;
450 WCHAR FileName
[MAX_PATH
];
452 /* Build a suitable file name */
453 _snwprintf(FileName
, MAX_PATH
,
456 TextFormat
? L
"txt" : L
"dat");
459 DPRINT1("Creating memory dump file '%S'...\n", FileName
);
461 /* Always create the dump file */
462 hFile
= CreateFileW(FileName
,
467 FILE_ATTRIBUTE_NORMAL
,
470 if (hFile
== INVALID_HANDLE_VALUE
)
472 DPRINT1("Error when creating '%S' for memory dumping, GetLastError() = %u\n",
473 FileName
, GetLastError());
477 /* Dump the VM memory in the chosen format */
479 DumpMemoryTxt(hFile
);
481 DumpMemoryRaw(hFile
);
486 DPRINT1("Memory dump done\n");
489 BOOLEAN
EmulatorInitialize(HANDLE ConsoleInput
, HANDLE ConsoleOutput
)
491 /* Allocate memory for the 16-bit address space */
492 BaseAddress
= HeapAlloc(GetProcessHeap(), /*HEAP_ZERO_MEMORY*/ 0, MAX_ADDRESS
);
493 if (BaseAddress
== NULL
)
495 wprintf(L
"FATAL: Failed to allocate VDM memory.\n");
499 * For diagnostics purposes, we fill the memory with INT 0x03 codes
500 * so that if a program wants to execute random code in memory, we can
501 * retrieve the exact CS:IP where the problem happens.
503 RtlFillMemory(BaseAddress
, MAX_ADDRESS
, 0xCC);
505 /* Initialize I/O ports */
508 /* Initialize the CPU */
510 /* Initialize the internal clock */
511 if (!ClockInitialize())
513 wprintf(L
"FATAL: Failed to initialize the clock\n");
517 /* Initialize the CPU */
519 // Fast486Initialize(&EmulatorContext,
520 // EmulatorReadMemory,
521 // EmulatorWriteMemory,
525 // EmulatorBiosOperation,
526 // EmulatorIntAcknowledge,
527 // NULL /* TODO: Use a TLB */);
531 /* Initialize the PIC, the PIT, the CMOS and the PC Speaker */
537 /* Set output functions */
538 PitSetOutFunction(0, NULL
, PitChan0Out
);
539 PitSetOutFunction(1, NULL
, PitChan1Out
);
540 PitSetOutFunction(2, NULL
, PitChan2Out
);
542 /* Register the I/O Ports */
543 RegisterIoPort(CONTROL_SYSTEM_PORT61H
, Port61hRead
, Port61hWrite
);
545 /* Set the console input mode */
546 // FIXME: Activate ENABLE_WINDOW_INPUT when we will want to perform actions
547 // upon console window events (screen buffer resize, ...).
548 SetConsoleMode(ConsoleInput
, ENABLE_PROCESSED_INPUT
/* | ENABLE_WINDOW_INPUT */);
549 // SetConsoleMode(ConsoleOutput, ENABLE_PROCESSED_OUTPUT | ENABLE_WRAP_AT_EOL_OUTPUT);
551 /**/EnableExtraHardware(ConsoleInput
);/**/
553 /* Initialize the PS/2 port */
556 /* Initialize the keyboard and mouse and connect them to their PS/2 ports */
560 /**************** ATTACH INPUT WITH CONSOLE *****************/
561 /* Start the input thread */
562 InputThread
= CreateThread(NULL
, 0, &PumpConsoleInput
, ConsoleInput
, 0, NULL
);
563 if (InputThread
== NULL
)
565 DisplayMessage(L
"Failed to create the console input thread.");
568 /************************************************************/
570 /* Initialize the VGA */
571 if (!VgaInitialize(ConsoleOutput
))
573 DisplayMessage(L
"Failed to initialize VGA support.");
577 /* Initialize the software callback system and register the emulator BOPs */
579 RegisterBop(BOP_DEBUGGER
, EmulatorDebugBreakBop
);
580 // RegisterBop(BOP_UNSIMULATE, CpuUnsimulateBop);
582 /* Initialize VDD support */
588 VOID
EmulatorCleanup(VOID
)
592 /* Close the input thread handle */
593 if (InputThread
!= NULL
) CloseHandle(InputThread
);
605 /* Free the memory allocated for the 16-bit address space */
606 if (BaseAddress
!= NULL
) HeapFree(GetProcessHeap(), 0, BaseAddress
);
620 VDDTerminateVDM(VOID
)
628 Sim32pGetVDMPointer(IN ULONG Address
,
629 IN BOOLEAN ProtectedMode
)
632 UNREFERENCED_PARAMETER(ProtectedMode
);
635 * HIWORD(Address) == Segment (if ProtectedMode == FALSE)
636 * or Selector (if ProtectedMode == TRUE )
637 * LOWORD(Address) == Offset
639 return (PBYTE
)FAR_POINTER(Address
);
644 MGetVdmPointer(IN ULONG Address
,
646 IN BOOLEAN ProtectedMode
)
648 UNREFERENCED_PARAMETER(Size
);
649 return Sim32pGetVDMPointer(Address
, ProtectedMode
);
654 VdmMapFlat(IN USHORT Segment
,
659 UNREFERENCED_PARAMETER(Mode
);
661 return SEG_OFF_TO_PTR(Segment
, Offset
);
666 VdmFlushCache(IN USHORT Segment
,
678 VdmUnmapFlat(IN USHORT Segment
,