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 const static 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 DWORD WINAPI
PumpConsoleInput(LPVOID Parameter
);
342 BOOLEAN
EmulatorInitialize(HANDLE ConsoleInput
, HANDLE ConsoleOutput
)
344 /* Allocate memory for the 16-bit address space */
345 BaseAddress
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, MAX_ADDRESS
);
346 if (BaseAddress
== NULL
)
348 wprintf(L
"FATAL: Failed to allocate VDM memory.\n");
352 /* Initialize I/O ports */
355 /* Initialize the internal clock */
356 if (!ClockInitialize())
358 wprintf(L
"FATAL: Failed to initialize the clock\n");
362 /* Initialize the CPU */
363 Fast486Initialize(&EmulatorContext
,
369 EmulatorBiosOperation
,
370 EmulatorIntAcknowledge
,
371 NULL
/* TODO: Use a TLB */);
375 /* Initialize the PIC, the PIT, the CMOS and the PC Speaker */
381 /* Set output functions */
382 PitSetOutFunction(0, NULL
, PitChan0Out
);
383 PitSetOutFunction(1, NULL
, PitChan1Out
);
384 PitSetOutFunction(2, NULL
, PitChan2Out
);
386 /* Register the I/O Ports */
387 RegisterIoPort(CONTROL_SYSTEM_PORT61H
, Port61hRead
, Port61hWrite
);
389 /* Set the console input mode */
390 // FIXME: Activate ENABLE_WINDOW_INPUT when we will want to perform actions
391 // upon console window events (screen buffer resize, ...).
392 SetConsoleMode(ConsoleInput
, ENABLE_PROCESSED_INPUT
/* | ENABLE_WINDOW_INPUT */);
393 // SetConsoleMode(ConsoleOutput, ENABLE_PROCESSED_OUTPUT | ENABLE_WRAP_AT_EOL_OUTPUT);
395 /* Initialize the PS2 port */
396 PS2Initialize(ConsoleInput
);
398 /* Start the input thread */
399 InputThread
= CreateThread(NULL
, 0, &PumpConsoleInput
, ConsoleInput
, 0, NULL
);
400 // if (InputThread == NULL) return FALSE;
402 /* Initialize the VGA */
403 // if (!VgaInitialize(ConsoleOutput)) return FALSE;
404 VgaInitialize(ConsoleOutput
);
406 /* Initialize the software callback system and register the emulator BOPs */
407 InitializeCallbacks();
408 RegisterBop(BOP_DEBUGGER
, EmulatorDebugBreakBop
);
409 RegisterBop(BOP_UNSIMULATE
, EmulatorUnsimulateBop
);
411 /* Initialize VDD support */
417 VOID
EmulatorCleanup(VOID
)
421 /* Close the input thread handle */
422 if (InputThread
!= NULL
) CloseHandle(InputThread
);
434 /* Free the memory allocated for the 16-bit address space */
435 if (BaseAddress
!= NULL
) HeapFree(GetProcessHeap(), 0, BaseAddress
);
449 VDDTerminateVDM(VOID
)
457 Sim32pGetVDMPointer(IN ULONG Address
,
458 IN BOOLEAN ProtectedMode
)
461 UNREFERENCED_PARAMETER(ProtectedMode
);
464 * HIWORD(Address) == Segment (if ProtectedMode == FALSE)
465 * or Selector (if ProtectedMode == TRUE )
466 * LOWORD(Address) == Offset
468 return (PBYTE
)FAR_POINTER(Address
);
473 MGetVdmPointer(IN ULONG Address
,
475 IN BOOLEAN ProtectedMode
)
477 UNREFERENCED_PARAMETER(Size
);
478 return Sim32pGetVDMPointer(Address
, ProtectedMode
);
483 VdmMapFlat(IN USHORT Segment
,
488 UNREFERENCED_PARAMETER(Mode
);
490 return SEG_OFF_TO_PTR(Segment
, Offset
);
495 VdmFlushCache(IN USHORT Segment
,
507 VdmUnmapFlat(IN USHORT Segment
,