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
],
170 Fast486DumpState(&EmulatorContext
);
177 VOID
EmulatorTerminate(VOID
)
183 VOID
EmulatorInterrupt(BYTE Number
)
185 /* Call the Fast486 API */
186 Fast486Interrupt(&EmulatorContext
, Number
);
189 VOID
EmulatorInterruptSignal(VOID
)
191 /* Call the Fast486 API */
192 Fast486InterruptSignal(&EmulatorContext
);
195 VOID
EmulatorSetA20(BOOLEAN Enabled
)
200 static VOID WINAPI
EmulatorDebugBreakBop(LPWORD Stack
)
202 DPRINT1("NTVDM: BOP_DEBUGGER\n");
206 static BYTE WINAPI
Port61hRead(ULONG Port
)
211 static VOID WINAPI
Port61hWrite(ULONG Port
, BYTE Data
)
213 // BOOLEAN SpeakerStateChange = FALSE;
214 BYTE OldPort61hState
= Port61hState
;
216 /* Only the four lowest bytes can be written */
217 Port61hState
= (Port61hState
& 0xF0) | (Data
& 0x0F);
219 if ((OldPort61hState
^ Port61hState
) & 0x01)
221 DPRINT("PIT 2 Gate %s\n", Port61hState
& 0x01 ? "on" : "off");
222 PitSetGate(2, !!(Port61hState
& 0x01));
223 // SpeakerStateChange = TRUE;
226 if ((OldPort61hState
^ Port61hState
) & 0x02)
228 /* There were some change for the speaker... */
229 DPRINT("Speaker %s\n", Port61hState
& 0x02 ? "on" : "off");
230 // SpeakerStateChange = TRUE;
232 // if (SpeakerStateChange) SpeakerChange();
236 static VOID WINAPI
PitChan0Out(LPVOID Param
, BOOLEAN State
)
240 DPRINT("PicInterruptRequest\n");
241 PicInterruptRequest(0); // Raise IRQ 0
243 // else < Lower IRQ 0 >
246 static VOID WINAPI
PitChan1Out(LPVOID Param
, BOOLEAN State
)
251 /* Set bit 4 of Port 61h */
252 Port61hState
|= 1 << 4;
256 /* Clear bit 4 of Port 61h */
257 Port61hState
&= ~(1 << 4);
260 Port61hState
= (Port61hState
& 0xEF) | (State
<< 4);
264 static VOID WINAPI
PitChan2Out(LPVOID Param
, BOOLEAN State
)
266 BYTE OldPort61hState
= Port61hState
;
271 /* Set bit 5 of Port 61h */
272 Port61hState
|= 1 << 5;
276 /* Clear bit 5 of Port 61h */
277 Port61hState
&= ~(1 << 5);
280 Port61hState
= (Port61hState
& 0xDF) | (State
<< 5);
283 if ((OldPort61hState
^ Port61hState
) & 0x20)
285 DPRINT("PitChan2Out -- Port61hState changed\n");
293 PumpConsoleInput(LPVOID Parameter
)
295 HANDLE ConsoleInput
= (HANDLE
)Parameter
;
296 INPUT_RECORD InputRecord
;
301 /* Make sure the task event is signaled */
302 WaitForSingleObject(VdmTaskEvent
, INFINITE
);
304 /* Wait for an input record */
305 if (!ReadConsoleInput(ConsoleInput
, &InputRecord
, 1, &Count
))
307 DWORD LastError
= GetLastError();
308 DPRINT1("Error reading console input (0x%p, %lu) - Error %lu\n", ConsoleInput
, Count
, LastError
);
314 /* Check the event type */
315 switch (InputRecord
.EventType
)
321 KeyboardEventHandler(&InputRecord
.Event
.KeyEvent
);
325 MouseEventHandler(&InputRecord
.Event
.MouseEvent
);
328 case WINDOW_BUFFER_SIZE_EVENT
:
329 ScreenEventHandler(&InputRecord
.Event
.WindowBufferSizeEvent
);
336 MenuEventHandler(&InputRecord
.Event
.MenuEvent
);
340 FocusEventHandler(&InputRecord
.Event
.FocusEvent
);
351 static VOID
EnableExtraHardware(HANDLE ConsoleInput
)
355 if (GetConsoleMode(ConsoleInput
, &ConInMode
))
358 // GetNumberOfConsoleMouseButtons();
359 // GetSystemMetrics(SM_CMOUSEBUTTONS);
360 // GetSystemMetrics(SM_MOUSEPRESENT);
364 /* Support mouse input events if there is a mouse on the system */
365 ConInMode
|= ENABLE_MOUSE_INPUT
;
370 /* Do not support mouse input events if there is no mouse on the system */
371 ConInMode
&= ~ENABLE_MOUSE_INPUT
;
375 SetConsoleMode(ConsoleInput
, ConInMode
);
379 /* PUBLIC FUNCTIONS ***********************************************************/
382 DumpMemoryRaw(HANDLE hFile
)
387 /* Dump the VM memory */
388 SetFilePointer(hFile
, 0, NULL
, FILE_BEGIN
);
389 Buffer
= REAL_TO_PHYS(NULL
);
390 Size
= MAX_ADDRESS
- (ULONG_PTR
)(NULL
);
391 WriteFile(hFile
, Buffer
, Size
, &Size
, NULL
);
395 DumpMemoryTxt(HANDLE hFile
)
397 #define LINE_SIZE 75 + 2
400 CHAR LineBuffer
[LINE_SIZE
];
404 /* Dump the VM memory */
405 SetFilePointer(hFile
, 0, NULL
, FILE_BEGIN
);
406 Ptr1
= Ptr2
= REAL_TO_PHYS(NULL
);
407 while (MAX_ADDRESS
- (ULONG_PTR
)PHYS_TO_REAL(Ptr1
) > 0)
412 /* Print the address */
413 Line
+= snprintf(Line
, LINE_SIZE
+ LineBuffer
- Line
, "%08x ", PHYS_TO_REAL(Ptr1
));
415 /* Print up to 16 bytes... */
417 /* ... in hexadecimal form first... */
419 while (i
++ <= 0x0F && (MAX_ADDRESS
- (ULONG_PTR
)PHYS_TO_REAL(Ptr1
) > 0))
421 Line
+= snprintf(Line
, LINE_SIZE
+ LineBuffer
- Line
, " %02x", *Ptr1
);
425 /* ... align with spaces if needed... */
426 RtlFillMemory(Line
, 0x0F + 4 - i
, ' ');
427 Line
+= 0x0F + 4 - i
;
429 /* ... then in character form. */
431 while (i
++ <= 0x0F && (MAX_ADDRESS
- (ULONG_PTR
)PHYS_TO_REAL(Ptr2
) > 0))
433 *Line
++ = ((*Ptr2
>= 0x20 && *Ptr2
<= 0x7E) || (*Ptr2
>= 0x80 && *Ptr2
< 0xFF) ? *Ptr2
: '.');
441 /* Finally write the line to the file */
442 LineSize
= Line
- LineBuffer
;
443 WriteFile(hFile
, LineBuffer
, LineSize
, &LineSize
, NULL
);
447 VOID
DumpMemory(BOOLEAN TextFormat
)
449 static ULONG DumpNumber
= 0;
452 WCHAR FileName
[MAX_PATH
];
454 /* Build a suitable file name */
455 _snwprintf(FileName
, MAX_PATH
,
458 TextFormat
? L
"txt" : L
"dat");
461 DPRINT1("Creating memory dump file '%S'...\n", FileName
);
463 /* Always create the dump file */
464 hFile
= CreateFileW(FileName
,
469 FILE_ATTRIBUTE_NORMAL
,
472 if (hFile
== INVALID_HANDLE_VALUE
)
474 DPRINT1("Error when creating '%S' for memory dumping, GetLastError() = %u\n",
475 FileName
, GetLastError());
479 /* Dump the VM memory in the chosen format */
481 DumpMemoryTxt(hFile
);
483 DumpMemoryRaw(hFile
);
488 DPRINT1("Memory dump done\n");
491 BOOLEAN
EmulatorInitialize(HANDLE ConsoleInput
, HANDLE ConsoleOutput
)
493 /* Allocate memory for the 16-bit address space */
494 BaseAddress
= HeapAlloc(GetProcessHeap(), /*HEAP_ZERO_MEMORY*/ 0, MAX_ADDRESS
);
495 if (BaseAddress
== NULL
)
497 wprintf(L
"FATAL: Failed to allocate VDM memory.\n");
501 * For diagnostics purposes, we fill the memory with INT 0x03 codes
502 * so that if a program wants to execute random code in memory, we can
503 * retrieve the exact CS:IP where the problem happens.
505 RtlFillMemory(BaseAddress
, MAX_ADDRESS
, 0xCC);
507 /* Initialize I/O ports */
510 /* Initialize the CPU */
512 /* Initialize the internal clock */
513 if (!ClockInitialize())
515 wprintf(L
"FATAL: Failed to initialize the clock\n");
519 /* Initialize the CPU */
521 // Fast486Initialize(&EmulatorContext,
522 // EmulatorReadMemory,
523 // EmulatorWriteMemory,
527 // EmulatorBiosOperation,
528 // EmulatorIntAcknowledge,
529 // NULL /* TODO: Use a TLB */);
533 /* Initialize the PIC, the PIT, the CMOS and the PC Speaker */
539 /* Set output functions */
540 PitSetOutFunction(0, NULL
, PitChan0Out
);
541 PitSetOutFunction(1, NULL
, PitChan1Out
);
542 PitSetOutFunction(2, NULL
, PitChan2Out
);
544 /* Register the I/O Ports */
545 RegisterIoPort(CONTROL_SYSTEM_PORT61H
, Port61hRead
, Port61hWrite
);
547 /* Set the console input mode */
548 // FIXME: Activate ENABLE_WINDOW_INPUT when we will want to perform actions
549 // upon console window events (screen buffer resize, ...).
550 SetConsoleMode(ConsoleInput
, ENABLE_PROCESSED_INPUT
/* | ENABLE_WINDOW_INPUT */);
551 // SetConsoleMode(ConsoleOutput, ENABLE_PROCESSED_OUTPUT | ENABLE_WRAP_AT_EOL_OUTPUT);
553 /**/EnableExtraHardware(ConsoleInput
);/**/
555 /* Initialize the PS/2 port */
558 /* Initialize the keyboard and mouse and connect them to their PS/2 ports */
562 /**************** ATTACH INPUT WITH CONSOLE *****************/
563 /* Start the input thread */
564 InputThread
= CreateThread(NULL
, 0, &PumpConsoleInput
, ConsoleInput
, 0, NULL
);
565 if (InputThread
== NULL
)
567 DisplayMessage(L
"Failed to create the console input thread.");
570 /************************************************************/
572 /* Initialize the VGA */
573 if (!VgaInitialize(ConsoleOutput
))
575 DisplayMessage(L
"Failed to initialize VGA support.");
579 /* Initialize the software callback system and register the emulator BOPs */
581 RegisterBop(BOP_DEBUGGER
, EmulatorDebugBreakBop
);
582 // RegisterBop(BOP_UNSIMULATE, CpuUnsimulateBop);
584 /* Initialize VDD support */
590 VOID
EmulatorCleanup(VOID
)
594 /* Close the input thread handle */
595 if (InputThread
!= NULL
) CloseHandle(InputThread
);
607 /* Free the memory allocated for the 16-bit address space */
608 if (BaseAddress
!= NULL
) HeapFree(GetProcessHeap(), 0, BaseAddress
);
622 VDDTerminateVDM(VOID
)
630 Sim32pGetVDMPointer(IN ULONG Address
,
631 IN BOOLEAN ProtectedMode
)
634 UNREFERENCED_PARAMETER(ProtectedMode
);
637 * HIWORD(Address) == Segment (if ProtectedMode == FALSE)
638 * or Selector (if ProtectedMode == TRUE )
639 * LOWORD(Address) == Offset
641 return (PBYTE
)FAR_POINTER(Address
);
646 MGetVdmPointer(IN ULONG Address
,
648 IN BOOLEAN ProtectedMode
)
650 UNREFERENCED_PARAMETER(Size
);
651 return Sim32pGetVDMPointer(Address
, ProtectedMode
);
656 VdmMapFlat(IN USHORT Segment
,
661 UNREFERENCED_PARAMETER(Mode
);
663 return SEG_OFF_TO_PTR(Segment
, Offset
);
668 VdmFlushCache(IN USHORT Segment
,
680 VdmUnmapFlat(IN USHORT Segment
,