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 *******************************************************************/
15 #include "bios/bios.h"
16 #include "hardware/cmos.h"
17 #include "hardware/pic.h"
18 #include "hardware/ps2.h"
19 #include "hardware/speaker.h"
20 #include "hardware/timer.h"
21 #include "hardware/vga.h"
27 /* PRIVATE VARIABLES **********************************************************/
29 FAST486_STATE EmulatorContext
;
30 LPVOID BaseAddress
= NULL
;
31 BOOLEAN VdmRunning
= TRUE
;
33 static BOOLEAN A20Line
= FALSE
;
34 static BYTE Port61hState
= 0x00;
36 LPCWSTR ExceptionName
[] =
43 L
"Bound Range Exceeded",
49 #define BOP_DEBUGGER 0x56 // Break into the debugger from a 16-bit app
51 /* PRIVATE FUNCTIONS **********************************************************/
53 VOID WINAPI
EmulatorReadMemory(PFAST486_STATE State
, ULONG Address
, PVOID Buffer
, ULONG Size
)
55 UNREFERENCED_PARAMETER(State
);
57 /* If the A20 line is disabled, mask bit 20 */
58 if (!A20Line
) Address
&= ~(1 << 20);
60 /* Make sure the requested address is valid */
61 if ((Address
+ Size
) >= MAX_ADDRESS
) return;
64 * Check if we are going to read the VGA memory and
65 * copy it into the virtual address space if needed.
67 if (((Address
+ Size
) >= VgaGetVideoBaseAddress())
68 && (Address
< VgaGetVideoLimitAddress()))
70 DWORD VgaAddress
= max(Address
, VgaGetVideoBaseAddress());
71 DWORD ActualSize
= min(Address
+ Size
- 1, VgaGetVideoLimitAddress())
73 LPBYTE DestBuffer
= (LPBYTE
)((ULONG_PTR
)BaseAddress
+ VgaAddress
);
75 /* Read from the VGA memory */
76 VgaReadMemory(VgaAddress
, DestBuffer
, ActualSize
);
79 /* Read the data from the virtual address space and store it in the buffer */
80 RtlCopyMemory(Buffer
, (LPVOID
)((ULONG_PTR
)BaseAddress
+ Address
), Size
);
83 VOID WINAPI
EmulatorWriteMemory(PFAST486_STATE State
, ULONG Address
, PVOID Buffer
, ULONG Size
)
85 UNREFERENCED_PARAMETER(State
);
87 /* If the A20 line is disabled, mask bit 20 */
88 if (!A20Line
) Address
&= ~(1 << 20);
90 /* Make sure the requested address is valid */
91 if ((Address
+ Size
) >= MAX_ADDRESS
) return;
93 /* Make sure we don't write to the ROM area */
94 if ((Address
+ Size
) >= ROM_AREA_START
&& (Address
< ROM_AREA_END
)) return;
96 /* Read the data from the buffer and store it in the virtual address space */
97 RtlCopyMemory((LPVOID
)((ULONG_PTR
)BaseAddress
+ Address
), Buffer
, Size
);
100 * Check if we modified the VGA memory.
102 if (((Address
+ Size
) >= VgaGetVideoBaseAddress())
103 && (Address
< VgaGetVideoLimitAddress()))
105 DWORD VgaAddress
= max(Address
, VgaGetVideoBaseAddress());
106 DWORD ActualSize
= min(Address
+ Size
- 1, VgaGetVideoLimitAddress())
108 LPBYTE SrcBuffer
= (LPBYTE
)((ULONG_PTR
)BaseAddress
+ VgaAddress
);
110 /* Write to the VGA memory */
111 VgaWriteMemory(VgaAddress
, SrcBuffer
, ActualSize
);
115 UCHAR WINAPI
EmulatorIntAcknowledge(PFAST486_STATE State
)
117 UNREFERENCED_PARAMETER(State
);
119 /* Get the interrupt number from the PIC */
120 return PicGetInterrupt();
123 VOID WINAPI
EmulatorDebugBreak(LPWORD Stack
)
125 DPRINT1("NTVDM: BOP_DEBUGGER\n");
130 static BYTE WINAPI
Port61hRead(ULONG Port
)
135 static VOID WINAPI
Port61hWrite(ULONG Port
, BYTE Data
)
137 // BOOLEAN SpeakerChange = FALSE;
138 BYTE OldPort61hState
= Port61hState
;
140 /* Only the four lowest bytes can be written */
141 Port61hState
= (Port61hState
& 0xF0) | (Data
& 0x0F);
143 if ((OldPort61hState
^ Port61hState
) & 0x01)
145 DPRINT("PIT 2 Gate %s\n", Port61hState
& 0x01 ? "on" : "off");
146 // SpeakerChange = TRUE;
149 PitSetGate(2, !!(Port61hState
& 0x01));
151 if ((OldPort61hState
^ Port61hState
) & 0x02)
153 /* There were some change for the speaker... */
154 DPRINT("Speaker %s\n", Port61hState
& 0x02 ? "on" : "off");
155 // SpeakerChange = TRUE;
157 // if (SpeakerChange) SpeakerChange();
161 static VOID WINAPI
PitChan0Out(LPVOID Param
, BOOLEAN State
)
165 DPRINT("PicInterruptRequest\n");
166 PicInterruptRequest(0); // Raise IRQ 0
168 // else < Lower IRQ 0 >
171 static VOID WINAPI
PitChan1Out(LPVOID Param
, BOOLEAN State
)
176 /* Set bit 4 of Port 61h */
177 Port61hState
|= 1 << 4;
181 /* Clear bit 4 of Port 61h */
182 Port61hState
&= ~(1 << 4);
185 Port61hState
= (Port61hState
& 0xEF) | (State
<< 4);
189 static VOID WINAPI
PitChan2Out(LPVOID Param
, BOOLEAN State
)
191 // BYTE OldPort61hState = Port61hState;
196 /* Set bit 5 of Port 61h */
197 Port61hState
|= 1 << 5;
201 /* Clear bit 5 of Port 61h */
202 Port61hState
&= ~(1 << 5);
205 Port61hState
= (Port61hState
& 0xDF) | (State
<< 5);
207 DPRINT("Speaker PIT out\n");
208 // if ((OldPort61hState ^ Port61hState) & 0x20)
212 /* PUBLIC FUNCTIONS ***********************************************************/
214 BOOLEAN
EmulatorInitialize(HANDLE ConsoleInput
, HANDLE ConsoleOutput
)
216 /* Allocate memory for the 16-bit address space */
217 BaseAddress
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, MAX_ADDRESS
);
218 if (BaseAddress
== NULL
)
220 wprintf(L
"FATAL: Failed to allocate VDM memory.\n");
224 /* Initialize I/O ports */
227 /* Initialize the CPU */
228 Fast486Initialize(&EmulatorContext
,
234 EmulatorBiosOperation
,
235 EmulatorIntAcknowledge
,
236 NULL
/* TODO: Use a TLB */);
238 /* Enable interrupts */
241 /* Initialize the PIC, the PIT, the CMOS and the PC Speaker */
247 /* Set output functions */
248 PitSetOutFunction(0, NULL
, PitChan0Out
);
249 PitSetOutFunction(1, NULL
, PitChan1Out
);
250 PitSetOutFunction(2, NULL
, PitChan2Out
);
252 /* Register the I/O Ports */
253 RegisterIoPort(CONTROL_SYSTEM_PORT61H
, Port61hRead
, Port61hWrite
);
255 /* Initialize the PS2 port */
256 PS2Initialize(ConsoleInput
);
258 /* Set the console input mode */
259 // SetConsoleMode(ConsoleInput, ENABLE_MOUSE_INPUT | ENABLE_PROCESSED_INPUT);
261 /* Initialize the VGA */
262 // if (!VgaInitialize(ConsoleOutput)) return FALSE;
263 VgaInitialize(ConsoleOutput
);
265 /* Register the DebugBreak BOP */
266 RegisterBop(BOP_DEBUGGER
, EmulatorDebugBreak
);
268 /* Initialize VDD support */
274 VOID
EmulatorCleanup(VOID
)
286 /* Free the memory allocated for the 16-bit address space */
287 if (BaseAddress
!= NULL
) HeapFree(GetProcessHeap(), 0, BaseAddress
);
290 VOID
EmulatorException(BYTE ExceptionNumber
, LPWORD Stack
)
292 WORD CodeSegment
, InstructionPointer
;
295 ASSERT(ExceptionNumber
< 8);
298 InstructionPointer
= Stack
[STACK_IP
];
299 CodeSegment
= Stack
[STACK_CS
];
300 Opcode
= (PBYTE
)SEG_OFF_TO_PTR(CodeSegment
, InstructionPointer
);
302 /* Display a message to the user */
303 DisplayMessage(L
"Exception: %s occured at %04X:%04X\n"
304 L
"Opcode: %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X",
305 ExceptionName
[ExceptionNumber
],
324 // FIXME: This function assumes 16-bit mode!!!
325 VOID
EmulatorExecute(WORD Segment
, WORD Offset
)
327 /* Tell Fast486 to move the instruction pointer */
328 Fast486ExecuteAt(&EmulatorContext
, Segment
, Offset
);
331 VOID
EmulatorInterrupt(BYTE Number
)
333 /* Call the Fast486 API */
334 Fast486Interrupt(&EmulatorContext
, Number
);
337 VOID
EmulatorInterruptSignal(VOID
)
339 /* Call the Fast486 API */
340 Fast486InterruptSignal(&EmulatorContext
);
343 VOID
EmulatorStep(VOID
)
345 /* Dump the state for debugging purposes */
346 // Fast486DumpState(&EmulatorContext);
348 /* Execute the next instruction */
349 Fast486StepInto(&EmulatorContext
);
352 VOID
EmulatorSetA20(BOOLEAN Enabled
)
361 VDDTerminateVDM(VOID
)
369 Sim32pGetVDMPointer(IN ULONG Address
,
370 IN BOOLEAN ProtectedMode
)
373 UNREFERENCED_PARAMETER(ProtectedMode
);
376 * HIWORD(Address) == Segment (if ProtectedMode == FALSE)
377 * or Selector (if ProtectedMode == TRUE )
378 * LOWORD(Address) == Offset
380 return (PBYTE
)FAR_POINTER(Address
);
385 MGetVdmPointer(IN ULONG Address
,
387 IN BOOLEAN ProtectedMode
)
389 UNREFERENCED_PARAMETER(Size
);
390 return Sim32pGetVDMPointer(Address
, ProtectedMode
);
395 VdmMapFlat(IN USHORT Segment
,
400 UNREFERENCED_PARAMETER(Mode
);
402 return SEG_OFF_TO_PTR(Segment
, Offset
);
407 VdmFlushCache(IN USHORT Segment
,
419 VdmUnmapFlat(IN USHORT Segment
,