[NTVDM]
[reactos.git] / subsystems / ntvdm / emulator.c
1 /*
2 * COPYRIGHT: GPL - See COPYING in the top level directory
3 * PROJECT: ReactOS Virtual DOS Machine
4 * FILE: emulator.c
5 * PURPOSE: Minimal x86 machine emulator for the VDM
6 * PROGRAMMERS: Aleksandar Andrejevic <theflash AT sdf DOT lonestar DOT org>
7 */
8
9 /* INCLUDES *******************************************************************/
10
11 #define NDEBUG
12
13 #include "emulator.h"
14
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"
22
23 #include "bop.h"
24 #include "vddsup.h"
25 #include "io.h"
26
27 /* PRIVATE VARIABLES **********************************************************/
28
29 FAST486_STATE EmulatorContext;
30 LPVOID BaseAddress = NULL;
31 BOOLEAN VdmRunning = TRUE;
32
33 static BOOLEAN A20Line = FALSE;
34 static BYTE Port61hState = 0x00;
35
36 LPCWSTR ExceptionName[] =
37 {
38 L"Division By Zero",
39 L"Debug",
40 L"Unexpected Error",
41 L"Breakpoint",
42 L"Integer Overflow",
43 L"Bound Range Exceeded",
44 L"Invalid Opcode",
45 L"FPU Not Available"
46 };
47
48 /* BOP Identifiers */
49 #define BOP_DEBUGGER 0x56 // Break into the debugger from a 16-bit app
50
51 /* PRIVATE FUNCTIONS **********************************************************/
52
53 VOID WINAPI EmulatorReadMemory(PFAST486_STATE State, ULONG Address, PVOID Buffer, ULONG Size)
54 {
55 UNREFERENCED_PARAMETER(State);
56
57 /* If the A20 line is disabled, mask bit 20 */
58 if (!A20Line) Address &= ~(1 << 20);
59
60 /* Make sure the requested address is valid */
61 if ((Address + Size) >= MAX_ADDRESS) return;
62
63 /*
64 * Check if we are going to read the VGA memory and
65 * copy it into the virtual address space if needed.
66 */
67 if (((Address + Size) >= VgaGetVideoBaseAddress())
68 && (Address < VgaGetVideoLimitAddress()))
69 {
70 DWORD VgaAddress = max(Address, VgaGetVideoBaseAddress());
71 DWORD ActualSize = min(Address + Size - 1, VgaGetVideoLimitAddress())
72 - VgaAddress + 1;
73 LPBYTE DestBuffer = (LPBYTE)((ULONG_PTR)BaseAddress + VgaAddress);
74
75 /* Read from the VGA memory */
76 VgaReadMemory(VgaAddress, DestBuffer, ActualSize);
77 }
78
79 /* Read the data from the virtual address space and store it in the buffer */
80 RtlCopyMemory(Buffer, (LPVOID)((ULONG_PTR)BaseAddress + Address), Size);
81 }
82
83 VOID WINAPI EmulatorWriteMemory(PFAST486_STATE State, ULONG Address, PVOID Buffer, ULONG Size)
84 {
85 UNREFERENCED_PARAMETER(State);
86
87 /* If the A20 line is disabled, mask bit 20 */
88 if (!A20Line) Address &= ~(1 << 20);
89
90 /* Make sure the requested address is valid */
91 if ((Address + Size) >= MAX_ADDRESS) return;
92
93 /* Make sure we don't write to the ROM area */
94 if ((Address + Size) >= ROM_AREA_START && (Address < ROM_AREA_END)) return;
95
96 /* Read the data from the buffer and store it in the virtual address space */
97 RtlCopyMemory((LPVOID)((ULONG_PTR)BaseAddress + Address), Buffer, Size);
98
99 /*
100 * Check if we modified the VGA memory.
101 */
102 if (((Address + Size) >= VgaGetVideoBaseAddress())
103 && (Address < VgaGetVideoLimitAddress()))
104 {
105 DWORD VgaAddress = max(Address, VgaGetVideoBaseAddress());
106 DWORD ActualSize = min(Address + Size - 1, VgaGetVideoLimitAddress())
107 - VgaAddress + 1;
108 LPBYTE SrcBuffer = (LPBYTE)((ULONG_PTR)BaseAddress + VgaAddress);
109
110 /* Write to the VGA memory */
111 VgaWriteMemory(VgaAddress, SrcBuffer, ActualSize);
112 }
113 }
114
115 UCHAR WINAPI EmulatorIntAcknowledge(PFAST486_STATE State)
116 {
117 UNREFERENCED_PARAMETER(State);
118
119 /* Get the interrupt number from the PIC */
120 return PicGetInterrupt();
121 }
122
123 VOID WINAPI EmulatorDebugBreak(LPWORD Stack)
124 {
125 DPRINT1("NTVDM: BOP_DEBUGGER\n");
126 DebugBreak();
127 }
128
129
130 static BYTE WINAPI Port61hRead(ULONG Port)
131 {
132 return Port61hState;
133 }
134
135 static VOID WINAPI Port61hWrite(ULONG Port, BYTE Data)
136 {
137 // BOOLEAN SpeakerChange = FALSE;
138 BYTE OldPort61hState = Port61hState;
139
140 /* Only the four lowest bytes can be written */
141 Port61hState = (Port61hState & 0xF0) | (Data & 0x0F);
142
143 if ((OldPort61hState ^ Port61hState) & 0x01)
144 {
145 DPRINT("PIT 2 Gate %s\n", Port61hState & 0x01 ? "on" : "off");
146 // SpeakerChange = TRUE;
147 }
148
149 PitSetGate(2, !!(Port61hState & 0x01));
150
151 if ((OldPort61hState ^ Port61hState) & 0x02)
152 {
153 /* There were some change for the speaker... */
154 DPRINT("Speaker %s\n", Port61hState & 0x02 ? "on" : "off");
155 // SpeakerChange = TRUE;
156 }
157 // if (SpeakerChange) SpeakerChange();
158 SpeakerChange();
159 }
160
161 static VOID WINAPI PitChan0Out(LPVOID Param, BOOLEAN State)
162 {
163 if (State)
164 {
165 DPRINT("PicInterruptRequest\n");
166 PicInterruptRequest(0); // Raise IRQ 0
167 }
168 // else < Lower IRQ 0 >
169 }
170
171 static VOID WINAPI PitChan1Out(LPVOID Param, BOOLEAN State)
172 {
173 #if 0
174 if (State)
175 {
176 /* Set bit 4 of Port 61h */
177 Port61hState |= 1 << 4;
178 }
179 else
180 {
181 /* Clear bit 4 of Port 61h */
182 Port61hState &= ~(1 << 4);
183 }
184 #else
185 Port61hState = (Port61hState & 0xEF) | (State << 4);
186 #endif
187 }
188
189 static VOID WINAPI PitChan2Out(LPVOID Param, BOOLEAN State)
190 {
191 // BYTE OldPort61hState = Port61hState;
192
193 #if 0
194 if (State)
195 {
196 /* Set bit 5 of Port 61h */
197 Port61hState |= 1 << 5;
198 }
199 else
200 {
201 /* Clear bit 5 of Port 61h */
202 Port61hState &= ~(1 << 5);
203 }
204 #else
205 Port61hState = (Port61hState & 0xDF) | (State << 5);
206 #endif
207 DPRINT("Speaker PIT out\n");
208 // if ((OldPort61hState ^ Port61hState) & 0x20)
209 // SpeakerChange();
210 }
211
212 /* PUBLIC FUNCTIONS ***********************************************************/
213
214 BOOLEAN EmulatorInitialize(HANDLE ConsoleInput, HANDLE ConsoleOutput)
215 {
216 /* Allocate memory for the 16-bit address space */
217 BaseAddress = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, MAX_ADDRESS);
218 if (BaseAddress == NULL)
219 {
220 wprintf(L"FATAL: Failed to allocate VDM memory.\n");
221 return FALSE;
222 }
223
224 /* Initialize I/O ports */
225 /* Initialize RAM */
226
227 /* Initialize the CPU */
228 Fast486Initialize(&EmulatorContext,
229 EmulatorReadMemory,
230 EmulatorWriteMemory,
231 EmulatorReadIo,
232 EmulatorWriteIo,
233 NULL,
234 EmulatorBiosOperation,
235 EmulatorIntAcknowledge,
236 NULL /* TODO: Use a TLB */);
237
238 /* Enable interrupts */
239 setIF(1);
240
241 /* Initialize the PIC, the PIT, the CMOS and the PC Speaker */
242 PicInitialize();
243 PitInitialize();
244 CmosInitialize();
245 SpeakerInitialize();
246
247 /* Set output functions */
248 PitSetOutFunction(0, NULL, PitChan0Out);
249 PitSetOutFunction(1, NULL, PitChan1Out);
250 PitSetOutFunction(2, NULL, PitChan2Out);
251
252 /* Register the I/O Ports */
253 RegisterIoPort(CONTROL_SYSTEM_PORT61H, Port61hRead, Port61hWrite);
254
255 /* Initialize the PS2 port */
256 PS2Initialize(ConsoleInput);
257
258 /* Set the console input mode */
259 // SetConsoleMode(ConsoleInput, ENABLE_MOUSE_INPUT | ENABLE_PROCESSED_INPUT);
260
261 /* Initialize the VGA */
262 // if (!VgaInitialize(ConsoleOutput)) return FALSE;
263 VgaInitialize(ConsoleOutput);
264
265 /* Register the DebugBreak BOP */
266 RegisterBop(BOP_DEBUGGER, EmulatorDebugBreak);
267
268 /* Initialize VDD support */
269 VDDSupInitialize();
270
271 return TRUE;
272 }
273
274 VOID EmulatorCleanup(VOID)
275 {
276 // VgaCleanup();
277 PS2Cleanup();
278
279 SpeakerCleanup();
280 CmosCleanup();
281 // PitCleanup();
282 // PicCleanup();
283
284 // Fast486Cleanup();
285
286 /* Free the memory allocated for the 16-bit address space */
287 if (BaseAddress != NULL) HeapFree(GetProcessHeap(), 0, BaseAddress);
288 }
289
290 VOID EmulatorException(BYTE ExceptionNumber, LPWORD Stack)
291 {
292 WORD CodeSegment, InstructionPointer;
293 PBYTE Opcode;
294
295 ASSERT(ExceptionNumber < 8);
296
297 /* Get the CS:IP */
298 InstructionPointer = Stack[STACK_IP];
299 CodeSegment = Stack[STACK_CS];
300 Opcode = (PBYTE)SEG_OFF_TO_PTR(CodeSegment, InstructionPointer);
301
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],
306 CodeSegment,
307 InstructionPointer,
308 Opcode[0],
309 Opcode[1],
310 Opcode[2],
311 Opcode[3],
312 Opcode[4],
313 Opcode[5],
314 Opcode[6],
315 Opcode[7],
316 Opcode[8],
317 Opcode[9]);
318
319 /* Stop the VDM */
320 VdmRunning = FALSE;
321 return;
322 }
323
324 // FIXME: This function assumes 16-bit mode!!!
325 VOID EmulatorExecute(WORD Segment, WORD Offset)
326 {
327 /* Tell Fast486 to move the instruction pointer */
328 Fast486ExecuteAt(&EmulatorContext, Segment, Offset);
329 }
330
331 VOID EmulatorInterrupt(BYTE Number)
332 {
333 /* Call the Fast486 API */
334 Fast486Interrupt(&EmulatorContext, Number);
335 }
336
337 VOID EmulatorInterruptSignal(VOID)
338 {
339 /* Call the Fast486 API */
340 Fast486InterruptSignal(&EmulatorContext);
341 }
342
343 VOID EmulatorStep(VOID)
344 {
345 /* Dump the state for debugging purposes */
346 // Fast486DumpState(&EmulatorContext);
347
348 /* Execute the next instruction */
349 Fast486StepInto(&EmulatorContext);
350 }
351
352 VOID EmulatorSetA20(BOOLEAN Enabled)
353 {
354 A20Line = Enabled;
355 }
356
357
358
359 VOID
360 WINAPI
361 VDDTerminateVDM(VOID)
362 {
363 /* Stop the VDM */
364 VdmRunning = FALSE;
365 }
366
367 PBYTE
368 WINAPI
369 Sim32pGetVDMPointer(IN ULONG Address,
370 IN BOOLEAN ProtectedMode)
371 {
372 // FIXME
373 UNREFERENCED_PARAMETER(ProtectedMode);
374
375 /*
376 * HIWORD(Address) == Segment (if ProtectedMode == FALSE)
377 * or Selector (if ProtectedMode == TRUE )
378 * LOWORD(Address) == Offset
379 */
380 return (PBYTE)FAR_POINTER(Address);
381 }
382
383 PBYTE
384 WINAPI
385 MGetVdmPointer(IN ULONG Address,
386 IN ULONG Size,
387 IN BOOLEAN ProtectedMode)
388 {
389 UNREFERENCED_PARAMETER(Size);
390 return Sim32pGetVDMPointer(Address, ProtectedMode);
391 }
392
393 PVOID
394 WINAPI
395 VdmMapFlat(IN USHORT Segment,
396 IN ULONG Offset,
397 IN VDM_MODE Mode)
398 {
399 // FIXME
400 UNREFERENCED_PARAMETER(Mode);
401
402 return SEG_OFF_TO_PTR(Segment, Offset);
403 }
404
405 BOOL
406 WINAPI
407 VdmFlushCache(IN USHORT Segment,
408 IN ULONG Offset,
409 IN ULONG Size,
410 IN VDM_MODE Mode)
411 {
412 // FIXME
413 UNIMPLEMENTED;
414 return TRUE;
415 }
416
417 BOOL
418 WINAPI
419 VdmUnmapFlat(IN USHORT Segment,
420 IN ULONG Offset,
421 IN PVOID Buffer,
422 IN VDM_MODE Mode)
423 {
424 // FIXME
425 UNIMPLEMENTED;
426 return TRUE;
427 }
428
429 /* EOF */