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 *******************************************************************/
16 #include "bios/bios.h"
17 #include "hardware/cmos.h"
18 #include "hardware/pic.h"
19 #include "hardware/ps2.h"
20 #include "hardware/speaker.h"
21 #include "hardware/timer.h"
22 #include "hardware/vga.h"
30 /* PRIVATE VARIABLES **********************************************************/
32 FAST486_STATE EmulatorContext
;
33 BOOLEAN CpuSimulate
= FALSE
;
35 /* No more than 'MaxCpuCallLevel' recursive CPU calls are allowed */
36 const static INT MaxCpuCallLevel
= 32;
37 static INT CpuCallLevel
= 0;
39 LPVOID BaseAddress
= NULL
;
40 BOOLEAN VdmRunning
= TRUE
;
42 static BOOLEAN A20Line
= FALSE
;
43 static BYTE Port61hState
= 0x00;
45 static HANDLE InputThread
= NULL
;
47 LPCWSTR ExceptionName
[] =
54 L
"Bound Range Exceeded",
60 #define BOP_DEBUGGER 0x56 // Break into the debugger from a 16-bit app
62 /* PRIVATE FUNCTIONS **********************************************************/
64 VOID WINAPI
EmulatorReadMemory(PFAST486_STATE State
, ULONG Address
, PVOID Buffer
, ULONG Size
)
66 UNREFERENCED_PARAMETER(State
);
68 /* If the A20 line is disabled, mask bit 20 */
69 if (!A20Line
) Address
&= ~(1 << 20);
71 /* Make sure the requested address is valid */
72 if ((Address
+ Size
) >= MAX_ADDRESS
) return;
75 * Check if we are going to read the VGA memory and
76 * copy it into the virtual address space if needed.
78 if (((Address
+ Size
) >= VgaGetVideoBaseAddress())
79 && (Address
< VgaGetVideoLimitAddress()))
81 DWORD VgaAddress
= max(Address
, VgaGetVideoBaseAddress());
82 DWORD ActualSize
= min(Address
+ Size
- 1, VgaGetVideoLimitAddress())
84 LPBYTE DestBuffer
= (LPBYTE
)((ULONG_PTR
)BaseAddress
+ VgaAddress
);
86 /* Read from the VGA memory */
87 VgaReadMemory(VgaAddress
, DestBuffer
, ActualSize
);
90 /* Read the data from the virtual address space and store it in the buffer */
91 RtlCopyMemory(Buffer
, (LPVOID
)((ULONG_PTR
)BaseAddress
+ Address
), Size
);
94 VOID WINAPI
EmulatorWriteMemory(PFAST486_STATE State
, ULONG Address
, PVOID Buffer
, ULONG Size
)
96 UNREFERENCED_PARAMETER(State
);
98 /* If the A20 line is disabled, mask bit 20 */
99 if (!A20Line
) Address
&= ~(1 << 20);
101 /* Make sure the requested address is valid */
102 if ((Address
+ Size
) >= MAX_ADDRESS
) return;
104 /* Make sure we don't write to the ROM area */
105 if ((Address
+ Size
) >= ROM_AREA_START
&& (Address
< ROM_AREA_END
)) return;
107 /* Read the data from the buffer and store it in the virtual address space */
108 RtlCopyMemory((LPVOID
)((ULONG_PTR
)BaseAddress
+ Address
), Buffer
, Size
);
111 * Check if we modified the VGA memory.
113 if (((Address
+ Size
) >= VgaGetVideoBaseAddress())
114 && (Address
< VgaGetVideoLimitAddress()))
116 DWORD VgaAddress
= max(Address
, VgaGetVideoBaseAddress());
117 DWORD ActualSize
= min(Address
+ Size
- 1, VgaGetVideoLimitAddress())
119 LPBYTE SrcBuffer
= (LPBYTE
)((ULONG_PTR
)BaseAddress
+ VgaAddress
);
121 /* Write to the VGA memory */
122 VgaWriteMemory(VgaAddress
, SrcBuffer
, ActualSize
);
126 UCHAR WINAPI
EmulatorIntAcknowledge(PFAST486_STATE State
)
128 UNREFERENCED_PARAMETER(State
);
130 /* Get the interrupt number from the PIC */
131 return PicGetInterrupt();
134 VOID
EmulatorException(BYTE ExceptionNumber
, LPWORD Stack
)
136 WORD CodeSegment
, InstructionPointer
;
139 ASSERT(ExceptionNumber
< 8);
142 InstructionPointer
= Stack
[STACK_IP
];
143 CodeSegment
= Stack
[STACK_CS
];
144 Opcode
= (PBYTE
)SEG_OFF_TO_PTR(CodeSegment
, InstructionPointer
);
146 /* Display a message to the user */
147 DisplayMessage(L
"Exception: %s occured at %04X:%04X\n"
148 L
"Opcode: %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X",
149 ExceptionName
[ExceptionNumber
],
168 // FIXME: This function assumes 16-bit mode!!!
169 VOID
EmulatorExecute(WORD Segment
, WORD Offset
)
171 /* Tell Fast486 to move the instruction pointer */
172 Fast486ExecuteAt(&EmulatorContext
, Segment
, Offset
);
175 VOID
EmulatorStep(VOID
)
177 /* Dump the state for debugging purposes */
178 // Fast486DumpState(&EmulatorContext);
180 /* Execute the next instruction */
181 Fast486StepInto(&EmulatorContext
);
184 VOID
EmulatorSimulate(VOID
)
186 if (CpuCallLevel
> MaxCpuCallLevel
)
188 DisplayMessage(L
"Too many CPU levels of recursion (%d, expected maximum %d)",
189 CpuCallLevel
, MaxCpuCallLevel
);
198 while (VdmRunning
&& CpuSimulate
) ClockUpdate();
201 if (CpuCallLevel
< 0) CpuCallLevel
= 0;
203 /* This takes into account for reentrance */
207 VOID
EmulatorUnsimulate(VOID
)
209 /* Stop simulation */
213 VOID
EmulatorInterrupt(BYTE Number
)
215 /* Call the Fast486 API */
216 Fast486Interrupt(&EmulatorContext
, Number
);
219 VOID
EmulatorInterruptSignal(VOID
)
221 /* Call the Fast486 API */
222 Fast486InterruptSignal(&EmulatorContext
);
225 VOID
EmulatorSetA20(BOOLEAN Enabled
)
230 VOID WINAPI
EmulatorDebugBreakBop(LPWORD Stack
)
232 DPRINT1("NTVDM: BOP_DEBUGGER\n");
236 VOID WINAPI
EmulatorUnsimulateBop(LPWORD Stack
)
238 EmulatorUnsimulate();
241 static BYTE WINAPI
Port61hRead(ULONG Port
)
246 static VOID WINAPI
Port61hWrite(ULONG Port
, BYTE Data
)
248 // BOOLEAN SpeakerChange = FALSE;
249 BYTE OldPort61hState
= Port61hState
;
251 /* Only the four lowest bytes can be written */
252 Port61hState
= (Port61hState
& 0xF0) | (Data
& 0x0F);
254 if ((OldPort61hState
^ Port61hState
) & 0x01)
256 DPRINT("PIT 2 Gate %s\n", Port61hState
& 0x01 ? "on" : "off");
257 // SpeakerChange = TRUE;
260 PitSetGate(2, !!(Port61hState
& 0x01));
262 if ((OldPort61hState
^ Port61hState
) & 0x02)
264 /* There were some change for the speaker... */
265 DPRINT("Speaker %s\n", Port61hState
& 0x02 ? "on" : "off");
266 // SpeakerChange = TRUE;
268 // if (SpeakerChange) SpeakerChange();
272 static VOID WINAPI
PitChan0Out(LPVOID Param
, BOOLEAN State
)
276 DPRINT("PicInterruptRequest\n");
277 PicInterruptRequest(0); // Raise IRQ 0
279 // else < Lower IRQ 0 >
282 static VOID WINAPI
PitChan1Out(LPVOID Param
, BOOLEAN State
)
287 /* Set bit 4 of Port 61h */
288 Port61hState
|= 1 << 4;
292 /* Clear bit 4 of Port 61h */
293 Port61hState
&= ~(1 << 4);
296 Port61hState
= (Port61hState
& 0xEF) | (State
<< 4);
300 static VOID WINAPI
PitChan2Out(LPVOID Param
, BOOLEAN State
)
302 // BYTE OldPort61hState = Port61hState;
307 /* Set bit 5 of Port 61h */
308 Port61hState
|= 1 << 5;
312 /* Clear bit 5 of Port 61h */
313 Port61hState
&= ~(1 << 5);
316 Port61hState
= (Port61hState
& 0xDF) | (State
<< 5);
318 DPRINT("Speaker PIT out\n");
319 // if ((OldPort61hState ^ Port61hState) & 0x20)
323 /* PUBLIC FUNCTIONS ***********************************************************/
325 DWORD WINAPI
PumpConsoleInput(LPVOID Parameter
);
327 BOOLEAN
EmulatorInitialize(HANDLE ConsoleInput
, HANDLE ConsoleOutput
)
329 /* Allocate memory for the 16-bit address space */
330 BaseAddress
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, MAX_ADDRESS
);
331 if (BaseAddress
== NULL
)
333 wprintf(L
"FATAL: Failed to allocate VDM memory.\n");
337 /* Initialize I/O ports */
340 /* Initialize the internal clock */
341 if (!ClockInitialize())
343 wprintf(L
"FATAL: Failed to initialize the clock\n");
347 /* Initialize the CPU */
348 Fast486Initialize(&EmulatorContext
,
354 EmulatorBiosOperation
,
355 EmulatorIntAcknowledge
,
356 NULL
/* TODO: Use a TLB */);
358 /* Enable interrupts */
363 /* Initialize the PIC, the PIT, the CMOS and the PC Speaker */
369 /* Set output functions */
370 PitSetOutFunction(0, NULL
, PitChan0Out
);
371 PitSetOutFunction(1, NULL
, PitChan1Out
);
372 PitSetOutFunction(2, NULL
, PitChan2Out
);
374 /* Register the I/O Ports */
375 RegisterIoPort(CONTROL_SYSTEM_PORT61H
, Port61hRead
, Port61hWrite
);
377 /* Initialize the PS2 port */
378 PS2Initialize(ConsoleInput
);
380 /* Set the console input mode */
381 // SetConsoleMode(ConsoleInput, ENABLE_MOUSE_INPUT | ENABLE_PROCESSED_INPUT);
383 /* Start the input thread */
384 InputThread
= CreateThread(NULL
, 0, &PumpConsoleInput
, ConsoleInput
, 0, NULL
);
385 // if (InputThread == NULL) return FALSE;
387 /* Initialize the VGA */
388 // if (!VgaInitialize(ConsoleOutput)) return FALSE;
389 VgaInitialize(ConsoleOutput
);
391 /* Initialize the software callback system and register the emulator BOPs */
392 InitializeCallbacks();
393 RegisterBop(BOP_DEBUGGER
, EmulatorDebugBreakBop
);
394 RegisterBop(BOP_UNSIMULATE
, EmulatorUnsimulateBop
);
396 /* Initialize VDD support */
402 VOID
EmulatorCleanup(VOID
)
406 /* Close the input thread handle */
407 if (InputThread
!= NULL
) CloseHandle(InputThread
);
419 /* Free the memory allocated for the 16-bit address space */
420 if (BaseAddress
!= NULL
) HeapFree(GetProcessHeap(), 0, BaseAddress
);
434 VDDTerminateVDM(VOID
)
442 Sim32pGetVDMPointer(IN ULONG Address
,
443 IN BOOLEAN ProtectedMode
)
446 UNREFERENCED_PARAMETER(ProtectedMode
);
449 * HIWORD(Address) == Segment (if ProtectedMode == FALSE)
450 * or Selector (if ProtectedMode == TRUE )
451 * LOWORD(Address) == Offset
453 return (PBYTE
)FAR_POINTER(Address
);
458 MGetVdmPointer(IN ULONG Address
,
460 IN BOOLEAN ProtectedMode
)
462 UNREFERENCED_PARAMETER(Size
);
463 return Sim32pGetVDMPointer(Address
, ProtectedMode
);
468 VdmMapFlat(IN USHORT Segment
,
473 UNREFERENCED_PARAMETER(Mode
);
475 return SEG_OFF_TO_PTR(Segment
, Offset
);
480 VdmFlushCache(IN USHORT Segment
,
492 VdmUnmapFlat(IN USHORT Segment
,