[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 "clock.h"
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"
23
24 #include "bop.h"
25 #include "vddsup.h"
26 #include "io.h"
27
28 #include <isvbop.h>
29
30 /* PRIVATE VARIABLES **********************************************************/
31
32 FAST486_STATE EmulatorContext;
33 BOOLEAN CpuSimulate = FALSE;
34
35 /* No more than 'MaxCpuCallLevel' recursive CPU calls are allowed */
36 const static INT MaxCpuCallLevel = 32;
37 static INT CpuCallLevel = 0;
38
39 LPVOID BaseAddress = NULL;
40 BOOLEAN VdmRunning = TRUE;
41
42 static BOOLEAN A20Line = FALSE;
43 static BYTE Port61hState = 0x00;
44
45 static HANDLE InputThread = NULL;
46
47 LPCWSTR ExceptionName[] =
48 {
49 L"Division By Zero",
50 L"Debug",
51 L"Unexpected Error",
52 L"Breakpoint",
53 L"Integer Overflow",
54 L"Bound Range Exceeded",
55 L"Invalid Opcode",
56 L"FPU Not Available"
57 };
58
59 /* BOP Identifiers */
60 #define BOP_DEBUGGER 0x56 // Break into the debugger from a 16-bit app
61
62 /* PRIVATE FUNCTIONS **********************************************************/
63
64 VOID WINAPI EmulatorReadMemory(PFAST486_STATE State, ULONG Address, PVOID Buffer, ULONG Size)
65 {
66 UNREFERENCED_PARAMETER(State);
67
68 // BIG HACK!!!! To make BIOS images working correctly,
69 // until Aleksander rewrites memory management!!
70 if (Address >= 0xFFFFFFF0) Address -= 0xFFF00000;
71
72 /* If the A20 line is disabled, mask bit 20 */
73 if (!A20Line) Address &= ~(1 << 20);
74
75 /* Make sure the requested address is valid */
76 if ((Address + Size) >= MAX_ADDRESS) return;
77
78 /*
79 * Check if we are going to read the VGA memory and
80 * copy it into the virtual address space if needed.
81 */
82 if (((Address + Size) >= VgaGetVideoBaseAddress())
83 && (Address < VgaGetVideoLimitAddress()))
84 {
85 DWORD VgaAddress = max(Address, VgaGetVideoBaseAddress());
86 DWORD ActualSize = min(Address + Size - 1, VgaGetVideoLimitAddress())
87 - VgaAddress + 1;
88 LPBYTE DestBuffer = (LPBYTE)REAL_TO_PHYS(VgaAddress);
89
90 /* Read from the VGA memory */
91 VgaReadMemory(VgaAddress, DestBuffer, ActualSize);
92 }
93
94 /* Read the data from the virtual address space and store it in the buffer */
95 RtlCopyMemory(Buffer, REAL_TO_PHYS(Address), Size);
96 }
97
98 VOID WINAPI EmulatorWriteMemory(PFAST486_STATE State, ULONG Address, PVOID Buffer, ULONG Size)
99 {
100 UNREFERENCED_PARAMETER(State);
101
102 // BIG HACK!!!! To make BIOS images working correctly,
103 // until Aleksander rewrites memory management!!
104 if (Address >= 0xFFFFFFF0) Address -= 0xFFF00000;
105
106 /* If the A20 line is disabled, mask bit 20 */
107 if (!A20Line) Address &= ~(1 << 20);
108
109 /* Make sure the requested address is valid */
110 if ((Address + Size) >= MAX_ADDRESS) return;
111
112 /* Make sure we don't write to the ROM area */
113 if ((Address + Size) >= ROM_AREA_START && (Address < ROM_AREA_END)) return;
114
115 /* Read the data from the buffer and store it in the virtual address space */
116 RtlCopyMemory(REAL_TO_PHYS(Address), Buffer, Size);
117
118 /*
119 * Check if we modified the VGA memory.
120 */
121 if (((Address + Size) >= VgaGetVideoBaseAddress())
122 && (Address < VgaGetVideoLimitAddress()))
123 {
124 DWORD VgaAddress = max(Address, VgaGetVideoBaseAddress());
125 DWORD ActualSize = min(Address + Size - 1, VgaGetVideoLimitAddress())
126 - VgaAddress + 1;
127 LPBYTE SrcBuffer = (LPBYTE)REAL_TO_PHYS(VgaAddress);
128
129 /* Write to the VGA memory */
130 VgaWriteMemory(VgaAddress, SrcBuffer, ActualSize);
131 }
132 }
133
134 UCHAR WINAPI EmulatorIntAcknowledge(PFAST486_STATE State)
135 {
136 UNREFERENCED_PARAMETER(State);
137
138 /* Get the interrupt number from the PIC */
139 return PicGetInterrupt();
140 }
141
142 VOID EmulatorException(BYTE ExceptionNumber, LPWORD Stack)
143 {
144 WORD CodeSegment, InstructionPointer;
145 PBYTE Opcode;
146
147 ASSERT(ExceptionNumber < 8);
148
149 /* Get the CS:IP */
150 InstructionPointer = Stack[STACK_IP];
151 CodeSegment = Stack[STACK_CS];
152 Opcode = (PBYTE)SEG_OFF_TO_PTR(CodeSegment, InstructionPointer);
153
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],
158 CodeSegment,
159 InstructionPointer,
160 Opcode[0],
161 Opcode[1],
162 Opcode[2],
163 Opcode[3],
164 Opcode[4],
165 Opcode[5],
166 Opcode[6],
167 Opcode[7],
168 Opcode[8],
169 Opcode[9]);
170
171 /* Stop the VDM */
172 VdmRunning = FALSE;
173 return;
174 }
175
176 // FIXME: This function assumes 16-bit mode!!!
177 VOID EmulatorExecute(WORD Segment, WORD Offset)
178 {
179 /* Tell Fast486 to move the instruction pointer */
180 Fast486ExecuteAt(&EmulatorContext, Segment, Offset);
181 }
182
183 VOID EmulatorStep(VOID)
184 {
185 /* Dump the state for debugging purposes */
186 // Fast486DumpState(&EmulatorContext);
187
188 /* Execute the next instruction */
189 Fast486StepInto(&EmulatorContext);
190 }
191
192 VOID EmulatorSimulate(VOID)
193 {
194 if (CpuCallLevel > MaxCpuCallLevel)
195 {
196 DisplayMessage(L"Too many CPU levels of recursion (%d, expected maximum %d)",
197 CpuCallLevel, MaxCpuCallLevel);
198
199 /* Stop the VDM */
200 VdmRunning = FALSE;
201 return;
202 }
203 CpuCallLevel++;
204
205 CpuSimulate = TRUE;
206 while (VdmRunning && CpuSimulate) ClockUpdate();
207
208 CpuCallLevel--;
209 if (CpuCallLevel < 0) CpuCallLevel = 0;
210
211 /* This takes into account for reentrance */
212 CpuSimulate = TRUE;
213 }
214
215 VOID EmulatorUnsimulate(VOID)
216 {
217 /* Stop simulation */
218 CpuSimulate = FALSE;
219 }
220
221 VOID EmulatorInterrupt(BYTE Number)
222 {
223 /* Call the Fast486 API */
224 Fast486Interrupt(&EmulatorContext, Number);
225 }
226
227 VOID EmulatorInterruptSignal(VOID)
228 {
229 /* Call the Fast486 API */
230 Fast486InterruptSignal(&EmulatorContext);
231 }
232
233 VOID EmulatorSetA20(BOOLEAN Enabled)
234 {
235 A20Line = Enabled;
236 }
237
238 VOID WINAPI EmulatorDebugBreakBop(LPWORD Stack)
239 {
240 DPRINT1("NTVDM: BOP_DEBUGGER\n");
241 DebugBreak();
242 }
243
244 VOID WINAPI EmulatorUnsimulateBop(LPWORD Stack)
245 {
246 EmulatorUnsimulate();
247 }
248
249 static BYTE WINAPI Port61hRead(ULONG Port)
250 {
251 return Port61hState;
252 }
253
254 static VOID WINAPI Port61hWrite(ULONG Port, BYTE Data)
255 {
256 // BOOLEAN SpeakerChange = FALSE;
257 BYTE OldPort61hState = Port61hState;
258
259 /* Only the four lowest bytes can be written */
260 Port61hState = (Port61hState & 0xF0) | (Data & 0x0F);
261
262 if ((OldPort61hState ^ Port61hState) & 0x01)
263 {
264 DPRINT("PIT 2 Gate %s\n", Port61hState & 0x01 ? "on" : "off");
265 // SpeakerChange = TRUE;
266 }
267
268 PitSetGate(2, !!(Port61hState & 0x01));
269
270 if ((OldPort61hState ^ Port61hState) & 0x02)
271 {
272 /* There were some change for the speaker... */
273 DPRINT("Speaker %s\n", Port61hState & 0x02 ? "on" : "off");
274 // SpeakerChange = TRUE;
275 }
276 // if (SpeakerChange) SpeakerChange();
277 SpeakerChange();
278 }
279
280 static VOID WINAPI PitChan0Out(LPVOID Param, BOOLEAN State)
281 {
282 if (State)
283 {
284 DPRINT("PicInterruptRequest\n");
285 PicInterruptRequest(0); // Raise IRQ 0
286 }
287 // else < Lower IRQ 0 >
288 }
289
290 static VOID WINAPI PitChan1Out(LPVOID Param, BOOLEAN State)
291 {
292 #if 0
293 if (State)
294 {
295 /* Set bit 4 of Port 61h */
296 Port61hState |= 1 << 4;
297 }
298 else
299 {
300 /* Clear bit 4 of Port 61h */
301 Port61hState &= ~(1 << 4);
302 }
303 #else
304 Port61hState = (Port61hState & 0xEF) | (State << 4);
305 #endif
306 }
307
308 static VOID WINAPI PitChan2Out(LPVOID Param, BOOLEAN State)
309 {
310 // BYTE OldPort61hState = Port61hState;
311
312 #if 0
313 if (State)
314 {
315 /* Set bit 5 of Port 61h */
316 Port61hState |= 1 << 5;
317 }
318 else
319 {
320 /* Clear bit 5 of Port 61h */
321 Port61hState &= ~(1 << 5);
322 }
323 #else
324 Port61hState = (Port61hState & 0xDF) | (State << 5);
325 #endif
326 DPRINT("Speaker PIT out\n");
327 // if ((OldPort61hState ^ Port61hState) & 0x20)
328 // SpeakerChange();
329 }
330
331 /* PUBLIC FUNCTIONS ***********************************************************/
332
333 DWORD WINAPI PumpConsoleInput(LPVOID Parameter);
334
335 BOOLEAN EmulatorInitialize(HANDLE ConsoleInput, HANDLE ConsoleOutput)
336 {
337 /* Allocate memory for the 16-bit address space */
338 BaseAddress = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, MAX_ADDRESS);
339 if (BaseAddress == NULL)
340 {
341 wprintf(L"FATAL: Failed to allocate VDM memory.\n");
342 return FALSE;
343 }
344
345 /* Initialize I/O ports */
346 /* Initialize RAM */
347
348 /* Initialize the internal clock */
349 if (!ClockInitialize())
350 {
351 wprintf(L"FATAL: Failed to initialize the clock\n");
352 return FALSE;
353 }
354
355 /* Initialize the CPU */
356 Fast486Initialize(&EmulatorContext,
357 EmulatorReadMemory,
358 EmulatorWriteMemory,
359 EmulatorReadIo,
360 EmulatorWriteIo,
361 NULL,
362 EmulatorBiosOperation,
363 EmulatorIntAcknowledge,
364 NULL /* TODO: Use a TLB */);
365
366 /* Initialize DMA */
367
368 /* Initialize the PIC, the PIT, the CMOS and the PC Speaker */
369 PicInitialize();
370 PitInitialize();
371 CmosInitialize();
372 SpeakerInitialize();
373
374 /* Set output functions */
375 PitSetOutFunction(0, NULL, PitChan0Out);
376 PitSetOutFunction(1, NULL, PitChan1Out);
377 PitSetOutFunction(2, NULL, PitChan2Out);
378
379 /* Register the I/O Ports */
380 RegisterIoPort(CONTROL_SYSTEM_PORT61H, Port61hRead, Port61hWrite);
381
382 /* Initialize the PS2 port */
383 PS2Initialize(ConsoleInput);
384
385 /* Set the console input mode */
386 // SetConsoleMode(ConsoleInput, ENABLE_MOUSE_INPUT | ENABLE_PROCESSED_INPUT);
387
388 /* Start the input thread */
389 InputThread = CreateThread(NULL, 0, &PumpConsoleInput, ConsoleInput, 0, NULL);
390 // if (InputThread == NULL) return FALSE;
391
392 /* Initialize the VGA */
393 // if (!VgaInitialize(ConsoleOutput)) return FALSE;
394 VgaInitialize(ConsoleOutput);
395
396 /* Initialize the software callback system and register the emulator BOPs */
397 InitializeCallbacks();
398 RegisterBop(BOP_DEBUGGER , EmulatorDebugBreakBop);
399 RegisterBop(BOP_UNSIMULATE, EmulatorUnsimulateBop);
400
401 /* Initialize VDD support */
402 VDDSupInitialize();
403
404 return TRUE;
405 }
406
407 VOID EmulatorCleanup(VOID)
408 {
409 // VgaCleanup();
410
411 /* Close the input thread handle */
412 if (InputThread != NULL) CloseHandle(InputThread);
413 InputThread = NULL;
414
415 PS2Cleanup();
416
417 SpeakerCleanup();
418 CmosCleanup();
419 // PitCleanup();
420 // PicCleanup();
421
422 // Fast486Cleanup();
423
424 /* Free the memory allocated for the 16-bit address space */
425 if (BaseAddress != NULL) HeapFree(GetProcessHeap(), 0, BaseAddress);
426 }
427
428
429
430 VOID
431 WINAPI
432 VDDSimulate16(VOID)
433 {
434 EmulatorSimulate();
435 }
436
437 VOID
438 WINAPI
439 VDDTerminateVDM(VOID)
440 {
441 /* Stop the VDM */
442 VdmRunning = FALSE;
443 }
444
445 PBYTE
446 WINAPI
447 Sim32pGetVDMPointer(IN ULONG Address,
448 IN BOOLEAN ProtectedMode)
449 {
450 // FIXME
451 UNREFERENCED_PARAMETER(ProtectedMode);
452
453 /*
454 * HIWORD(Address) == Segment (if ProtectedMode == FALSE)
455 * or Selector (if ProtectedMode == TRUE )
456 * LOWORD(Address) == Offset
457 */
458 return (PBYTE)FAR_POINTER(Address);
459 }
460
461 PBYTE
462 WINAPI
463 MGetVdmPointer(IN ULONG Address,
464 IN ULONG Size,
465 IN BOOLEAN ProtectedMode)
466 {
467 UNREFERENCED_PARAMETER(Size);
468 return Sim32pGetVDMPointer(Address, ProtectedMode);
469 }
470
471 PVOID
472 WINAPI
473 VdmMapFlat(IN USHORT Segment,
474 IN ULONG Offset,
475 IN VDM_MODE Mode)
476 {
477 // FIXME
478 UNREFERENCED_PARAMETER(Mode);
479
480 return SEG_OFF_TO_PTR(Segment, Offset);
481 }
482
483 BOOL
484 WINAPI
485 VdmFlushCache(IN USHORT Segment,
486 IN ULONG Offset,
487 IN ULONG Size,
488 IN VDM_MODE Mode)
489 {
490 // FIXME
491 UNIMPLEMENTED;
492 return TRUE;
493 }
494
495 BOOL
496 WINAPI
497 VdmUnmapFlat(IN USHORT Segment,
498 IN ULONG Offset,
499 IN PVOID Buffer,
500 IN VDM_MODE Mode)
501 {
502 // FIXME
503 UNIMPLEMENTED;
504 return TRUE;
505 }
506
507 /* EOF */