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 // BIG HACK!!!! To make BIOS images working correctly,
69 // until Aleksander rewrites memory management!!
70 if (Address
>= 0xFFFFFFF0) Address
-= 0xFFF00000;
72 /* If the A20 line is disabled, mask bit 20 */
73 if (!A20Line
) Address
&= ~(1 << 20);
75 /* Make sure the requested address is valid */
76 if ((Address
+ Size
) >= MAX_ADDRESS
) return;
79 * Check if we are going to read the VGA memory and
80 * copy it into the virtual address space if needed.
82 if (((Address
+ Size
) >= VgaGetVideoBaseAddress())
83 && (Address
< VgaGetVideoLimitAddress()))
85 DWORD VgaAddress
= max(Address
, VgaGetVideoBaseAddress());
86 DWORD ActualSize
= min(Address
+ Size
- 1, VgaGetVideoLimitAddress())
88 LPBYTE DestBuffer
= (LPBYTE
)REAL_TO_PHYS(VgaAddress
);
90 /* Read from the VGA memory */
91 VgaReadMemory(VgaAddress
, DestBuffer
, ActualSize
);
94 /* Read the data from the virtual address space and store it in the buffer */
95 RtlCopyMemory(Buffer
, REAL_TO_PHYS(Address
), Size
);
98 VOID WINAPI
EmulatorWriteMemory(PFAST486_STATE State
, ULONG Address
, PVOID Buffer
, ULONG Size
)
100 UNREFERENCED_PARAMETER(State
);
102 // BIG HACK!!!! To make BIOS images working correctly,
103 // until Aleksander rewrites memory management!!
104 if (Address
>= 0xFFFFFFF0) Address
-= 0xFFF00000;
106 /* If the A20 line is disabled, mask bit 20 */
107 if (!A20Line
) Address
&= ~(1 << 20);
109 /* Make sure the requested address is valid */
110 if ((Address
+ Size
) >= MAX_ADDRESS
) return;
112 /* Make sure we don't write to the ROM area */
113 if ((Address
+ Size
) >= ROM_AREA_START
&& (Address
< ROM_AREA_END
)) return;
115 /* Read the data from the buffer and store it in the virtual address space */
116 RtlCopyMemory(REAL_TO_PHYS(Address
), Buffer
, Size
);
119 * Check if we modified the VGA memory.
121 if (((Address
+ Size
) >= VgaGetVideoBaseAddress())
122 && (Address
< VgaGetVideoLimitAddress()))
124 DWORD VgaAddress
= max(Address
, VgaGetVideoBaseAddress());
125 DWORD ActualSize
= min(Address
+ Size
- 1, VgaGetVideoLimitAddress())
127 LPBYTE SrcBuffer
= (LPBYTE
)REAL_TO_PHYS(VgaAddress
);
129 /* Write to the VGA memory */
130 VgaWriteMemory(VgaAddress
, SrcBuffer
, ActualSize
);
134 UCHAR WINAPI
EmulatorIntAcknowledge(PFAST486_STATE State
)
136 UNREFERENCED_PARAMETER(State
);
138 /* Get the interrupt number from the PIC */
139 return PicGetInterrupt();
142 VOID
EmulatorException(BYTE ExceptionNumber
, LPWORD Stack
)
144 WORD CodeSegment
, InstructionPointer
;
147 ASSERT(ExceptionNumber
< 8);
150 InstructionPointer
= Stack
[STACK_IP
];
151 CodeSegment
= Stack
[STACK_CS
];
152 Opcode
= (PBYTE
)SEG_OFF_TO_PTR(CodeSegment
, InstructionPointer
);
154 /* Display a message to the user */
155 DisplayMessage(L
"Exception: %s occured at %04X:%04X\n"
156 L
"Opcode: %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X",
157 ExceptionName
[ExceptionNumber
],
176 // FIXME: This function assumes 16-bit mode!!!
177 VOID
EmulatorExecute(WORD Segment
, WORD Offset
)
179 /* Tell Fast486 to move the instruction pointer */
180 Fast486ExecuteAt(&EmulatorContext
, Segment
, Offset
);
183 VOID
EmulatorStep(VOID
)
185 /* Dump the state for debugging purposes */
186 // Fast486DumpState(&EmulatorContext);
188 /* Execute the next instruction */
189 Fast486StepInto(&EmulatorContext
);
192 VOID
EmulatorSimulate(VOID
)
194 if (CpuCallLevel
> MaxCpuCallLevel
)
196 DisplayMessage(L
"Too many CPU levels of recursion (%d, expected maximum %d)",
197 CpuCallLevel
, MaxCpuCallLevel
);
206 while (VdmRunning
&& CpuSimulate
) ClockUpdate();
209 if (CpuCallLevel
< 0) CpuCallLevel
= 0;
211 /* This takes into account for reentrance */
215 VOID
EmulatorUnsimulate(VOID
)
217 /* Stop simulation */
221 VOID
EmulatorInterrupt(BYTE Number
)
223 /* Call the Fast486 API */
224 Fast486Interrupt(&EmulatorContext
, Number
);
227 VOID
EmulatorInterruptSignal(VOID
)
229 /* Call the Fast486 API */
230 Fast486InterruptSignal(&EmulatorContext
);
233 VOID
EmulatorSetA20(BOOLEAN Enabled
)
238 VOID WINAPI
EmulatorDebugBreakBop(LPWORD Stack
)
240 DPRINT1("NTVDM: BOP_DEBUGGER\n");
244 VOID WINAPI
EmulatorUnsimulateBop(LPWORD Stack
)
246 EmulatorUnsimulate();
249 static BYTE WINAPI
Port61hRead(ULONG Port
)
254 static VOID WINAPI
Port61hWrite(ULONG Port
, BYTE Data
)
256 // BOOLEAN SpeakerChange = FALSE;
257 BYTE OldPort61hState
= Port61hState
;
259 /* Only the four lowest bytes can be written */
260 Port61hState
= (Port61hState
& 0xF0) | (Data
& 0x0F);
262 if ((OldPort61hState
^ Port61hState
) & 0x01)
264 DPRINT("PIT 2 Gate %s\n", Port61hState
& 0x01 ? "on" : "off");
265 // SpeakerChange = TRUE;
268 PitSetGate(2, !!(Port61hState
& 0x01));
270 if ((OldPort61hState
^ Port61hState
) & 0x02)
272 /* There were some change for the speaker... */
273 DPRINT("Speaker %s\n", Port61hState
& 0x02 ? "on" : "off");
274 // SpeakerChange = TRUE;
276 // if (SpeakerChange) SpeakerChange();
280 static VOID WINAPI
PitChan0Out(LPVOID Param
, BOOLEAN State
)
284 DPRINT("PicInterruptRequest\n");
285 PicInterruptRequest(0); // Raise IRQ 0
287 // else < Lower IRQ 0 >
290 static VOID WINAPI
PitChan1Out(LPVOID Param
, BOOLEAN State
)
295 /* Set bit 4 of Port 61h */
296 Port61hState
|= 1 << 4;
300 /* Clear bit 4 of Port 61h */
301 Port61hState
&= ~(1 << 4);
304 Port61hState
= (Port61hState
& 0xEF) | (State
<< 4);
308 static VOID WINAPI
PitChan2Out(LPVOID Param
, BOOLEAN State
)
310 // BYTE OldPort61hState = Port61hState;
315 /* Set bit 5 of Port 61h */
316 Port61hState
|= 1 << 5;
320 /* Clear bit 5 of Port 61h */
321 Port61hState
&= ~(1 << 5);
324 Port61hState
= (Port61hState
& 0xDF) | (State
<< 5);
326 DPRINT("Speaker PIT out\n");
327 // if ((OldPort61hState ^ Port61hState) & 0x20)
331 /* PUBLIC FUNCTIONS ***********************************************************/
333 DWORD WINAPI
PumpConsoleInput(LPVOID Parameter
);
335 BOOLEAN
EmulatorInitialize(HANDLE ConsoleInput
, HANDLE ConsoleOutput
)
337 /* Allocate memory for the 16-bit address space */
338 BaseAddress
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, MAX_ADDRESS
);
339 if (BaseAddress
== NULL
)
341 wprintf(L
"FATAL: Failed to allocate VDM memory.\n");
345 /* Initialize I/O ports */
348 /* Initialize the internal clock */
349 if (!ClockInitialize())
351 wprintf(L
"FATAL: Failed to initialize the clock\n");
355 /* Initialize the CPU */
356 Fast486Initialize(&EmulatorContext
,
362 EmulatorBiosOperation
,
363 EmulatorIntAcknowledge
,
364 NULL
/* TODO: Use a TLB */);
368 /* Initialize the PIC, the PIT, the CMOS and the PC Speaker */
374 /* Set output functions */
375 PitSetOutFunction(0, NULL
, PitChan0Out
);
376 PitSetOutFunction(1, NULL
, PitChan1Out
);
377 PitSetOutFunction(2, NULL
, PitChan2Out
);
379 /* Register the I/O Ports */
380 RegisterIoPort(CONTROL_SYSTEM_PORT61H
, Port61hRead
, Port61hWrite
);
382 /* Initialize the PS2 port */
383 PS2Initialize(ConsoleInput
);
385 /* Set the console input mode */
386 // SetConsoleMode(ConsoleInput, ENABLE_MOUSE_INPUT | ENABLE_PROCESSED_INPUT);
388 /* Start the input thread */
389 InputThread
= CreateThread(NULL
, 0, &PumpConsoleInput
, ConsoleInput
, 0, NULL
);
390 // if (InputThread == NULL) return FALSE;
392 /* Initialize the VGA */
393 // if (!VgaInitialize(ConsoleOutput)) return FALSE;
394 VgaInitialize(ConsoleOutput
);
396 /* Initialize the software callback system and register the emulator BOPs */
397 InitializeCallbacks();
398 RegisterBop(BOP_DEBUGGER
, EmulatorDebugBreakBop
);
399 RegisterBop(BOP_UNSIMULATE
, EmulatorUnsimulateBop
);
401 /* Initialize VDD support */
407 VOID
EmulatorCleanup(VOID
)
411 /* Close the input thread handle */
412 if (InputThread
!= NULL
) CloseHandle(InputThread
);
424 /* Free the memory allocated for the 16-bit address space */
425 if (BaseAddress
!= NULL
) HeapFree(GetProcessHeap(), 0, BaseAddress
);
439 VDDTerminateVDM(VOID
)
447 Sim32pGetVDMPointer(IN ULONG Address
,
448 IN BOOLEAN ProtectedMode
)
451 UNREFERENCED_PARAMETER(ProtectedMode
);
454 * HIWORD(Address) == Segment (if ProtectedMode == FALSE)
455 * or Selector (if ProtectedMode == TRUE )
456 * LOWORD(Address) == Offset
458 return (PBYTE
)FAR_POINTER(Address
);
463 MGetVdmPointer(IN ULONG Address
,
465 IN BOOLEAN ProtectedMode
)
467 UNREFERENCED_PARAMETER(Size
);
468 return Sim32pGetVDMPointer(Address
, ProtectedMode
);
473 VdmMapFlat(IN USHORT Segment
,
478 UNREFERENCED_PARAMETER(Mode
);
480 return SEG_OFF_TO_PTR(Segment
, Offset
);
485 VdmFlushCache(IN USHORT Segment
,
497 VdmUnmapFlat(IN USHORT Segment
,