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 *******************************************************************/
18 #include "hardware/cmos.h"
19 #include "hardware/pic.h"
20 #include "hardware/ps2.h"
21 #include "hardware/speaker.h"
22 #include "hardware/timer.h"
23 #include "hardware/vga.h"
31 /* PRIVATE VARIABLES **********************************************************/
33 FAST486_STATE EmulatorContext
;
34 BOOLEAN CpuSimulate
= FALSE
;
36 /* No more than 'MaxCpuCallLevel' recursive CPU calls are allowed */
37 static const INT MaxCpuCallLevel
= 32;
38 static INT CpuCallLevel
= 0;
40 LPVOID BaseAddress
= NULL
;
41 BOOLEAN VdmRunning
= TRUE
;
43 static BOOLEAN A20Line
= FALSE
;
44 static BYTE Port61hState
= 0x00;
46 static HANDLE InputThread
= NULL
;
48 LPCWSTR ExceptionName
[] =
55 L
"Bound Range Exceeded",
61 #define BOP_DEBUGGER 0x56 // Break into the debugger from a 16-bit app
63 /* PRIVATE FUNCTIONS **********************************************************/
65 VOID WINAPI
EmulatorReadMemory(PFAST486_STATE State
, ULONG Address
, PVOID Buffer
, ULONG Size
)
67 UNREFERENCED_PARAMETER(State
);
69 // BIG HACK!!!! To make BIOS images working correctly,
70 // until Aleksander rewrites memory management!!
71 if (Address
>= 0xFFFFFFF0) Address
-= 0xFFF00000;
73 /* If the A20 line is disabled, mask bit 20 */
74 if (!A20Line
) Address
&= ~(1 << 20);
76 /* Make sure the requested address is valid */
77 if ((Address
+ Size
) >= MAX_ADDRESS
) return;
80 * Check if we are going to read the VGA memory and
81 * copy it into the virtual address space if needed.
83 if (((Address
+ Size
) >= VgaGetVideoBaseAddress())
84 && (Address
< VgaGetVideoLimitAddress()))
86 DWORD VgaAddress
= max(Address
, VgaGetVideoBaseAddress());
87 DWORD ActualSize
= min(Address
+ Size
- 1, VgaGetVideoLimitAddress())
89 LPBYTE DestBuffer
= (LPBYTE
)REAL_TO_PHYS(VgaAddress
);
91 /* Read from the VGA memory */
92 VgaReadMemory(VgaAddress
, DestBuffer
, ActualSize
);
95 /* Read the data from the virtual address space and store it in the buffer */
96 RtlCopyMemory(Buffer
, REAL_TO_PHYS(Address
), Size
);
99 VOID WINAPI
EmulatorWriteMemory(PFAST486_STATE State
, ULONG Address
, PVOID Buffer
, ULONG Size
)
101 UNREFERENCED_PARAMETER(State
);
103 // BIG HACK!!!! To make BIOS images working correctly,
104 // until Aleksander rewrites memory management!!
105 if (Address
>= 0xFFFFFFF0) Address
-= 0xFFF00000;
107 /* If the A20 line is disabled, mask bit 20 */
108 if (!A20Line
) Address
&= ~(1 << 20);
110 /* Make sure the requested address is valid */
111 if ((Address
+ Size
) >= MAX_ADDRESS
) return;
113 /* Make sure we don't write to the ROM area */
114 if ((Address
+ Size
) >= ROM_AREA_START
&& (Address
< ROM_AREA_END
)) return;
116 /* Read the data from the buffer and store it in the virtual address space */
117 RtlCopyMemory(REAL_TO_PHYS(Address
), Buffer
, Size
);
120 * Check if we modified the VGA memory.
122 if (((Address
+ Size
) >= VgaGetVideoBaseAddress())
123 && (Address
< VgaGetVideoLimitAddress()))
125 DWORD VgaAddress
= max(Address
, VgaGetVideoBaseAddress());
126 DWORD ActualSize
= min(Address
+ Size
- 1, VgaGetVideoLimitAddress())
128 LPBYTE SrcBuffer
= (LPBYTE
)REAL_TO_PHYS(VgaAddress
);
130 /* Write to the VGA memory */
131 VgaWriteMemory(VgaAddress
, SrcBuffer
, ActualSize
);
135 UCHAR WINAPI
EmulatorIntAcknowledge(PFAST486_STATE State
)
137 UNREFERENCED_PARAMETER(State
);
139 /* Get the interrupt number from the PIC */
140 return PicGetInterrupt();
143 VOID
EmulatorException(BYTE ExceptionNumber
, LPWORD Stack
)
145 WORD CodeSegment
, InstructionPointer
;
148 ASSERT(ExceptionNumber
< 8);
151 InstructionPointer
= Stack
[STACK_IP
];
152 CodeSegment
= Stack
[STACK_CS
];
153 Opcode
= (PBYTE
)SEG_OFF_TO_PTR(CodeSegment
, InstructionPointer
);
155 /* Display a message to the user */
156 DisplayMessage(L
"Exception: %s occured at %04X:%04X\n"
157 L
"Opcode: %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X",
158 ExceptionName
[ExceptionNumber
],
177 // FIXME: This function assumes 16-bit mode!!!
178 VOID
EmulatorExecute(WORD Segment
, WORD Offset
)
180 /* Tell Fast486 to move the instruction pointer */
181 Fast486ExecuteAt(&EmulatorContext
, Segment
, Offset
);
184 VOID
EmulatorStep(VOID
)
186 /* Dump the state for debugging purposes */
187 // Fast486DumpState(&EmulatorContext);
189 /* Execute the next instruction */
190 Fast486StepInto(&EmulatorContext
);
193 VOID
EmulatorSimulate(VOID
)
195 if (CpuCallLevel
> MaxCpuCallLevel
)
197 DisplayMessage(L
"Too many CPU levels of recursion (%d, expected maximum %d)",
198 CpuCallLevel
, MaxCpuCallLevel
);
207 while (VdmRunning
&& CpuSimulate
) ClockUpdate();
210 if (CpuCallLevel
< 0) CpuCallLevel
= 0;
212 /* This takes into account for reentrance */
216 VOID
EmulatorUnsimulate(VOID
)
218 /* Stop simulation */
222 VOID
EmulatorTerminate(VOID
)
228 VOID
EmulatorInterrupt(BYTE Number
)
230 /* Call the Fast486 API */
231 Fast486Interrupt(&EmulatorContext
, Number
);
234 VOID
EmulatorInterruptSignal(VOID
)
236 /* Call the Fast486 API */
237 Fast486InterruptSignal(&EmulatorContext
);
240 VOID
EmulatorSetA20(BOOLEAN Enabled
)
245 static VOID WINAPI
EmulatorDebugBreakBop(LPWORD Stack
)
247 DPRINT1("NTVDM: BOP_DEBUGGER\n");
251 static VOID WINAPI
EmulatorUnsimulateBop(LPWORD Stack
)
253 EmulatorUnsimulate();
256 static BYTE WINAPI
Port61hRead(ULONG Port
)
261 static VOID WINAPI
Port61hWrite(ULONG Port
, BYTE Data
)
263 // BOOLEAN SpeakerChange = FALSE;
264 BYTE OldPort61hState
= Port61hState
;
266 /* Only the four lowest bytes can be written */
267 Port61hState
= (Port61hState
& 0xF0) | (Data
& 0x0F);
269 if ((OldPort61hState
^ Port61hState
) & 0x01)
271 DPRINT("PIT 2 Gate %s\n", Port61hState
& 0x01 ? "on" : "off");
272 // SpeakerChange = TRUE;
275 PitSetGate(2, !!(Port61hState
& 0x01));
277 if ((OldPort61hState
^ Port61hState
) & 0x02)
279 /* There were some change for the speaker... */
280 DPRINT("Speaker %s\n", Port61hState
& 0x02 ? "on" : "off");
281 // SpeakerChange = TRUE;
283 // if (SpeakerChange) SpeakerChange();
287 static VOID WINAPI
PitChan0Out(LPVOID Param
, BOOLEAN State
)
291 DPRINT("PicInterruptRequest\n");
292 PicInterruptRequest(0); // Raise IRQ 0
294 // else < Lower IRQ 0 >
297 static VOID WINAPI
PitChan1Out(LPVOID Param
, BOOLEAN State
)
302 /* Set bit 4 of Port 61h */
303 Port61hState
|= 1 << 4;
307 /* Clear bit 4 of Port 61h */
308 Port61hState
&= ~(1 << 4);
311 Port61hState
= (Port61hState
& 0xEF) | (State
<< 4);
315 static VOID WINAPI
PitChan2Out(LPVOID Param
, BOOLEAN State
)
317 // BYTE OldPort61hState = Port61hState;
322 /* Set bit 5 of Port 61h */
323 Port61hState
|= 1 << 5;
327 /* Clear bit 5 of Port 61h */
328 Port61hState
&= ~(1 << 5);
331 Port61hState
= (Port61hState
& 0xDF) | (State
<< 5);
333 DPRINT("Speaker PIT out\n");
334 // if ((OldPort61hState ^ Port61hState) & 0x20)
338 /* PUBLIC FUNCTIONS ***********************************************************/
340 VOID
DumpMemory(VOID
)
342 static ULONG DumpNumber
= 0;
345 WCHAR FileName
[MAX_PATH
];
347 #define LINE_SIZE 75 + 2
350 CHAR LineBuffer
[LINE_SIZE
];
354 /* Build a suitable file name */
355 _snwprintf(FileName
, MAX_PATH
, L
"memdump%lu.txt", DumpNumber
);
358 /* Always create the dump file */
359 hFile
= CreateFileW(FileName
,
364 FILE_ATTRIBUTE_NORMAL
,
367 if (hFile
== INVALID_HANDLE_VALUE
)
369 DPRINT1("Error when creating '%S' for memory dumping, GetLastError() = %u\n",
370 FileName
, GetLastError());
374 /* Dump the VM memory */
375 SetFilePointer(hFile
, 0, NULL
, FILE_BEGIN
);
376 Ptr1
= Ptr2
= REAL_TO_PHYS(NULL
);
377 while (MAX_ADDRESS
- (ULONG_PTR
)PHYS_TO_REAL(Ptr1
) > 0)
382 /* Print the address */
383 Line
+= snprintf(Line
, LINE_SIZE
+ LineBuffer
- Line
, "%08x ", PHYS_TO_REAL(Ptr1
));
385 /* Print up to 16 bytes... */
387 /* ... in hexadecimal form first... */
389 while (i
++ <= 0x0F && (MAX_ADDRESS
- (ULONG_PTR
)PHYS_TO_REAL(Ptr1
) > 0))
391 Line
+= snprintf(Line
, LINE_SIZE
+ LineBuffer
- Line
, " %02x", *Ptr1
);
395 /* ... align with spaces if needed... */
396 RtlFillMemory(Line
, 0x0F + 4 - i
, ' ');
397 Line
+= 0x0F + 4 - i
;
399 /* ... then in character form. */
401 while (i
++ <= 0x0F && (MAX_ADDRESS
- (ULONG_PTR
)PHYS_TO_REAL(Ptr2
) > 0))
403 *Line
++ = ((*Ptr2
>= 0x20 && *Ptr2
<= 0x7E) || (*Ptr2
>= 0x80 && *Ptr2
< 0xFF) ? *Ptr2
: '.');
411 /* Finally write the line to the file */
412 LineSize
= Line
- LineBuffer
;
413 WriteFile(hFile
, LineBuffer
, LineSize
, &LineSize
, NULL
);
420 DWORD WINAPI
PumpConsoleInput(LPVOID Parameter
);
422 BOOLEAN
EmulatorInitialize(HANDLE ConsoleInput
, HANDLE ConsoleOutput
)
424 /* Allocate memory for the 16-bit address space */
425 BaseAddress
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, MAX_ADDRESS
);
426 if (BaseAddress
== NULL
)
428 wprintf(L
"FATAL: Failed to allocate VDM memory.\n");
432 /* Initialize I/O ports */
435 /* Initialize the internal clock */
436 if (!ClockInitialize())
438 wprintf(L
"FATAL: Failed to initialize the clock\n");
442 /* Initialize the CPU */
443 Fast486Initialize(&EmulatorContext
,
449 EmulatorBiosOperation
,
450 EmulatorIntAcknowledge
,
451 NULL
/* TODO: Use a TLB */);
455 /* Initialize the PIC, the PIT, the CMOS and the PC Speaker */
461 /* Set output functions */
462 PitSetOutFunction(0, NULL
, PitChan0Out
);
463 PitSetOutFunction(1, NULL
, PitChan1Out
);
464 PitSetOutFunction(2, NULL
, PitChan2Out
);
466 /* Register the I/O Ports */
467 RegisterIoPort(CONTROL_SYSTEM_PORT61H
, Port61hRead
, Port61hWrite
);
469 /* Set the console input mode */
470 // FIXME: Activate ENABLE_WINDOW_INPUT when we will want to perform actions
471 // upon console window events (screen buffer resize, ...).
472 SetConsoleMode(ConsoleInput
, ENABLE_PROCESSED_INPUT
/* | ENABLE_WINDOW_INPUT */);
473 // SetConsoleMode(ConsoleOutput, ENABLE_PROCESSED_OUTPUT | ENABLE_WRAP_AT_EOL_OUTPUT);
475 /* Initialize the PS2 port */
476 PS2Initialize(ConsoleInput
);
478 /**************** ATTACH INPUT WITH CONSOLE *****************/
479 /* Start the input thread */
480 InputThread
= CreateThread(NULL
, 0, &PumpConsoleInput
, ConsoleInput
, 0, NULL
);
481 if (InputThread
== NULL
)
483 DisplayMessage(L
"Failed to create the console input thread.");
486 /************************************************************/
488 /* Initialize the VGA */
489 if (!VgaInitialize(ConsoleOutput
))
491 DisplayMessage(L
"Failed to initialize VGA support.");
495 /* Initialize the software callback system and register the emulator BOPs */
496 InitializeCallbacks();
497 RegisterBop(BOP_DEBUGGER
, EmulatorDebugBreakBop
);
498 RegisterBop(BOP_UNSIMULATE
, EmulatorUnsimulateBop
);
500 /* Initialize VDD support */
506 VOID
EmulatorCleanup(VOID
)
510 /* Close the input thread handle */
511 if (InputThread
!= NULL
) CloseHandle(InputThread
);
523 /* Free the memory allocated for the 16-bit address space */
524 if (BaseAddress
!= NULL
) HeapFree(GetProcessHeap(), 0, BaseAddress
);
538 VDDTerminateVDM(VOID
)
546 Sim32pGetVDMPointer(IN ULONG Address
,
547 IN BOOLEAN ProtectedMode
)
550 UNREFERENCED_PARAMETER(ProtectedMode
);
553 * HIWORD(Address) == Segment (if ProtectedMode == FALSE)
554 * or Selector (if ProtectedMode == TRUE )
555 * LOWORD(Address) == Offset
557 return (PBYTE
)FAR_POINTER(Address
);
562 MGetVdmPointer(IN ULONG Address
,
564 IN BOOLEAN ProtectedMode
)
566 UNREFERENCED_PARAMETER(Size
);
567 return Sim32pGetVDMPointer(Address
, ProtectedMode
);
572 VdmMapFlat(IN USHORT Segment
,
577 UNREFERENCED_PARAMETER(Mode
);
579 return SEG_OFF_TO_PTR(Segment
, Offset
);
584 VdmFlushCache(IN USHORT Segment
,
596 VdmUnmapFlat(IN USHORT Segment
,