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
EmulatorInterrupt(BYTE Number
)
224 /* Call the Fast486 API */
225 Fast486Interrupt(&EmulatorContext
, Number
);
228 VOID
EmulatorInterruptSignal(VOID
)
230 /* Call the Fast486 API */
231 Fast486InterruptSignal(&EmulatorContext
);
234 VOID
EmulatorSetA20(BOOLEAN Enabled
)
239 VOID WINAPI
EmulatorDebugBreakBop(LPWORD Stack
)
241 DPRINT1("NTVDM: BOP_DEBUGGER\n");
245 VOID WINAPI
EmulatorUnsimulateBop(LPWORD Stack
)
247 EmulatorUnsimulate();
250 static BYTE WINAPI
Port61hRead(ULONG Port
)
255 static VOID WINAPI
Port61hWrite(ULONG Port
, BYTE Data
)
257 // BOOLEAN SpeakerChange = FALSE;
258 BYTE OldPort61hState
= Port61hState
;
260 /* Only the four lowest bytes can be written */
261 Port61hState
= (Port61hState
& 0xF0) | (Data
& 0x0F);
263 if ((OldPort61hState
^ Port61hState
) & 0x01)
265 DPRINT("PIT 2 Gate %s\n", Port61hState
& 0x01 ? "on" : "off");
266 // SpeakerChange = TRUE;
269 PitSetGate(2, !!(Port61hState
& 0x01));
271 if ((OldPort61hState
^ Port61hState
) & 0x02)
273 /* There were some change for the speaker... */
274 DPRINT("Speaker %s\n", Port61hState
& 0x02 ? "on" : "off");
275 // SpeakerChange = TRUE;
277 // if (SpeakerChange) SpeakerChange();
281 static VOID WINAPI
PitChan0Out(LPVOID Param
, BOOLEAN State
)
285 DPRINT("PicInterruptRequest\n");
286 PicInterruptRequest(0); // Raise IRQ 0
288 // else < Lower IRQ 0 >
291 static VOID WINAPI
PitChan1Out(LPVOID Param
, BOOLEAN State
)
296 /* Set bit 4 of Port 61h */
297 Port61hState
|= 1 << 4;
301 /* Clear bit 4 of Port 61h */
302 Port61hState
&= ~(1 << 4);
305 Port61hState
= (Port61hState
& 0xEF) | (State
<< 4);
309 static VOID WINAPI
PitChan2Out(LPVOID Param
, BOOLEAN State
)
311 // BYTE OldPort61hState = Port61hState;
316 /* Set bit 5 of Port 61h */
317 Port61hState
|= 1 << 5;
321 /* Clear bit 5 of Port 61h */
322 Port61hState
&= ~(1 << 5);
325 Port61hState
= (Port61hState
& 0xDF) | (State
<< 5);
327 DPRINT("Speaker PIT out\n");
328 // if ((OldPort61hState ^ Port61hState) & 0x20)
332 /* PUBLIC FUNCTIONS ***********************************************************/
334 DWORD WINAPI
PumpConsoleInput(LPVOID Parameter
);
336 BOOLEAN
EmulatorInitialize(HANDLE ConsoleInput
, HANDLE ConsoleOutput
)
338 /* Allocate memory for the 16-bit address space */
339 BaseAddress
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, MAX_ADDRESS
);
340 if (BaseAddress
== NULL
)
342 wprintf(L
"FATAL: Failed to allocate VDM memory.\n");
346 /* Initialize I/O ports */
349 /* Initialize the internal clock */
350 if (!ClockInitialize())
352 wprintf(L
"FATAL: Failed to initialize the clock\n");
356 /* Initialize the CPU */
357 Fast486Initialize(&EmulatorContext
,
363 EmulatorBiosOperation
,
364 EmulatorIntAcknowledge
,
365 NULL
/* TODO: Use a TLB */);
369 /* Initialize the PIC, the PIT, the CMOS and the PC Speaker */
375 /* Set output functions */
376 PitSetOutFunction(0, NULL
, PitChan0Out
);
377 PitSetOutFunction(1, NULL
, PitChan1Out
);
378 PitSetOutFunction(2, NULL
, PitChan2Out
);
380 /* Register the I/O Ports */
381 RegisterIoPort(CONTROL_SYSTEM_PORT61H
, Port61hRead
, Port61hWrite
);
383 /* Initialize the PS2 port */
384 PS2Initialize(ConsoleInput
);
386 /* Set the console input mode */
387 // FIXME: Activate ENABLE_WINDOW_INPUT when we will want to perform actions
388 // upon console window events (screen buffer resize, ...).
389 SetConsoleMode(ConsoleInput
, ENABLE_PROCESSED_INPUT
/* | ENABLE_WINDOW_INPUT */);
391 /* Start the input thread */
392 InputThread
= CreateThread(NULL
, 0, &PumpConsoleInput
, ConsoleInput
, 0, NULL
);
393 // if (InputThread == NULL) return FALSE;
395 /* Initialize the VGA */
396 // if (!VgaInitialize(ConsoleOutput)) return FALSE;
397 VgaInitialize(ConsoleOutput
);
399 /* Initialize the software callback system and register the emulator BOPs */
400 InitializeCallbacks();
401 RegisterBop(BOP_DEBUGGER
, EmulatorDebugBreakBop
);
402 RegisterBop(BOP_UNSIMULATE
, EmulatorUnsimulateBop
);
404 /* Initialize VDD support */
410 VOID
EmulatorCleanup(VOID
)
414 /* Close the input thread handle */
415 if (InputThread
!= NULL
) CloseHandle(InputThread
);
427 /* Free the memory allocated for the 16-bit address space */
428 if (BaseAddress
!= NULL
) HeapFree(GetProcessHeap(), 0, BaseAddress
);
442 VDDTerminateVDM(VOID
)
450 Sim32pGetVDMPointer(IN ULONG Address
,
451 IN BOOLEAN ProtectedMode
)
454 UNREFERENCED_PARAMETER(ProtectedMode
);
457 * HIWORD(Address) == Segment (if ProtectedMode == FALSE)
458 * or Selector (if ProtectedMode == TRUE )
459 * LOWORD(Address) == Offset
461 return (PBYTE
)FAR_POINTER(Address
);
466 MGetVdmPointer(IN ULONG Address
,
468 IN BOOLEAN ProtectedMode
)
470 UNREFERENCED_PARAMETER(Size
);
471 return Sim32pGetVDMPointer(Address
, ProtectedMode
);
476 VdmMapFlat(IN USHORT Segment
,
481 UNREFERENCED_PARAMETER(Mode
);
483 return SEG_OFF_TO_PTR(Segment
, Offset
);
488 VdmFlushCache(IN USHORT Segment
,
500 VdmUnmapFlat(IN USHORT Segment
,