bc3f101766577031ec819e3328e87e310acd0375
[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 BYTE OldPort61hState = Port61hState;
138
139 /* Only the four lowest bytes can be written */
140 Port61hState = (Port61hState & 0xF0) | (Data & 0x0F);
141
142 if ((OldPort61hState ^ Port61hState) & 0x01)
143 {
144 DPRINT1("PIT 2 Gate %s\n", Port61hState & 0x01 ? "on" : "off");
145 }
146
147 PitSetGate(2, !!(Port61hState & 0x01));
148
149 if ((OldPort61hState ^ Port61hState) & 0x02)
150 {
151 /* There were some change for the speaker... */
152 DPRINT1("Speaker %s\n", Port61hState & 0x02 ? "on" : "off");
153 }
154 }
155
156 static VOID WINAPI PitChan0Out(LPVOID Param, BOOLEAN State)
157 {
158 if (State)
159 {
160 DPRINT("PicInterruptRequest\n");
161 PicInterruptRequest(0); // Raise IRQ 0
162 }
163 // else < Lower IRQ 0 >
164 }
165
166 static VOID WINAPI PitChan1Out(LPVOID Param, BOOLEAN State)
167 {
168 #if 0
169 if (State)
170 {
171 /* Set bit 4 of Port 61h */
172 Port61hState |= 1 << 4;
173 }
174 else
175 {
176 /* Clear bit 4 of Port 61h */
177 Port61hState &= ~(1 << 4);
178 }
179 #else
180 Port61hState = (Port61hState & 0xEF) | (State << 4);
181 #endif
182 }
183
184 static VOID WINAPI PitChan2Out(LPVOID Param, BOOLEAN State)
185 {
186 #if 0
187 if (State)
188 {
189 /* Set bit 5 of Port 61h */
190 Port61hState |= 1 << 5;
191 }
192 else
193 {
194 /* Clear bit 5 of Port 61h */
195 Port61hState &= ~(1 << 5);
196 }
197 #else
198 Port61hState = (Port61hState & 0xDF) | (State << 5);
199 #endif
200 }
201
202 /* PUBLIC FUNCTIONS ***********************************************************/
203
204 BOOLEAN EmulatorInitialize(HANDLE ConsoleInput, HANDLE ConsoleOutput)
205 {
206 /* Allocate memory for the 16-bit address space */
207 BaseAddress = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, MAX_ADDRESS);
208 if (BaseAddress == NULL)
209 {
210 wprintf(L"FATAL: Failed to allocate VDM memory.\n");
211 return FALSE;
212 }
213
214 /* Initialize I/O ports */
215 /* Initialize RAM */
216
217 /* Initialize the CPU */
218 Fast486Initialize(&EmulatorContext,
219 EmulatorReadMemory,
220 EmulatorWriteMemory,
221 EmulatorReadIo,
222 EmulatorWriteIo,
223 NULL,
224 EmulatorBiosOperation,
225 EmulatorIntAcknowledge,
226 NULL /* TODO: Use a TLB */);
227
228 /* Enable interrupts */
229 setIF(1);
230
231 /* Initialize the PIC, the PIT, the CMOS and the PC Speaker */
232 PicInitialize();
233 PitInitialize();
234 CmosInitialize();
235 SpeakerInitialize();
236
237 /* Set output functions */
238 PitSetOutFunction(0, NULL, PitChan0Out);
239 PitSetOutFunction(1, NULL, PitChan1Out);
240 PitSetOutFunction(2, NULL, PitChan2Out);
241
242 /* Register the I/O Ports */
243 RegisterIoPort(CONTROL_SYSTEM_PORT61H, Port61hRead, Port61hWrite);
244
245 /* Initialize the PS2 port */
246 PS2Initialize(ConsoleInput);
247
248 /* Set the console input mode */
249 // SetConsoleMode(ConsoleInput, ENABLE_MOUSE_INPUT | ENABLE_PROCESSED_INPUT);
250
251 /* Initialize the VGA */
252 // if (!VgaInitialize(ConsoleOutput)) return FALSE;
253 VgaInitialize(ConsoleOutput);
254
255 /* Register the DebugBreak BOP */
256 RegisterBop(BOP_DEBUGGER, EmulatorDebugBreak);
257
258 /* Initialize VDD support */
259 VDDSupInitialize();
260
261 return TRUE;
262 }
263
264 VOID EmulatorCleanup(VOID)
265 {
266 // VgaCleanup();
267 PS2Cleanup();
268
269 SpeakerCleanup();
270 CmosCleanup();
271 // PitCleanup();
272 // PicCleanup();
273
274 // Fast486Cleanup();
275
276 /* Free the memory allocated for the 16-bit address space */
277 if (BaseAddress != NULL) HeapFree(GetProcessHeap(), 0, BaseAddress);
278 }
279
280 VOID EmulatorException(BYTE ExceptionNumber, LPWORD Stack)
281 {
282 WORD CodeSegment, InstructionPointer;
283 PBYTE Opcode;
284
285 ASSERT(ExceptionNumber < 8);
286
287 /* Get the CS:IP */
288 InstructionPointer = Stack[STACK_IP];
289 CodeSegment = Stack[STACK_CS];
290 Opcode = (PBYTE)SEG_OFF_TO_PTR(CodeSegment, InstructionPointer);
291
292 /* Display a message to the user */
293 DisplayMessage(L"Exception: %s occured at %04X:%04X\n"
294 L"Opcode: %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X",
295 ExceptionName[ExceptionNumber],
296 CodeSegment,
297 InstructionPointer,
298 Opcode[0],
299 Opcode[1],
300 Opcode[2],
301 Opcode[3],
302 Opcode[4],
303 Opcode[5],
304 Opcode[6],
305 Opcode[7],
306 Opcode[8],
307 Opcode[9]);
308
309 /* Stop the VDM */
310 VdmRunning = FALSE;
311 return;
312 }
313
314 // FIXME: This function assumes 16-bit mode!!!
315 VOID EmulatorExecute(WORD Segment, WORD Offset)
316 {
317 /* Tell Fast486 to move the instruction pointer */
318 Fast486ExecuteAt(&EmulatorContext, Segment, Offset);
319 }
320
321 VOID EmulatorInterrupt(BYTE Number)
322 {
323 /* Call the Fast486 API */
324 Fast486Interrupt(&EmulatorContext, Number);
325 }
326
327 VOID EmulatorInterruptSignal(VOID)
328 {
329 /* Call the Fast486 API */
330 Fast486InterruptSignal(&EmulatorContext);
331 }
332
333 VOID EmulatorStep(VOID)
334 {
335 /* Dump the state for debugging purposes */
336 // Fast486DumpState(&EmulatorContext);
337
338 /* Execute the next instruction */
339 Fast486StepInto(&EmulatorContext);
340 }
341
342 VOID EmulatorSetA20(BOOLEAN Enabled)
343 {
344 A20Line = Enabled;
345 }
346
347
348
349 VOID
350 WINAPI
351 VDDTerminateVDM(VOID)
352 {
353 /* Stop the VDM */
354 VdmRunning = FALSE;
355 }
356
357 PBYTE
358 WINAPI
359 Sim32pGetVDMPointer(IN ULONG Address,
360 IN BOOLEAN ProtectedMode)
361 {
362 // FIXME
363 UNREFERENCED_PARAMETER(ProtectedMode);
364
365 /*
366 * HIWORD(Address) == Segment (if ProtectedMode == FALSE)
367 * or Selector (if ProtectedMode == TRUE )
368 * LOWORD(Address) == Offset
369 */
370 return (PBYTE)FAR_POINTER(Address);
371 }
372
373 PBYTE
374 WINAPI
375 MGetVdmPointer(IN ULONG Address,
376 IN ULONG Size,
377 IN BOOLEAN ProtectedMode)
378 {
379 UNREFERENCED_PARAMETER(Size);
380 return Sim32pGetVDMPointer(Address, ProtectedMode);
381 }
382
383 PVOID
384 WINAPI
385 VdmMapFlat(IN USHORT Segment,
386 IN ULONG Offset,
387 IN VDM_MODE Mode)
388 {
389 // FIXME
390 UNREFERENCED_PARAMETER(Mode);
391
392 return SEG_OFF_TO_PTR(Segment, Offset);
393 }
394
395 BOOL
396 WINAPI
397 VdmFlushCache(IN USHORT Segment,
398 IN ULONG Offset,
399 IN ULONG Size,
400 IN VDM_MODE Mode)
401 {
402 // FIXME
403 UNIMPLEMENTED;
404 return TRUE;
405 }
406
407 BOOL
408 WINAPI
409 VdmUnmapFlat(IN USHORT Segment,
410 IN ULONG Offset,
411 IN PVOID Buffer,
412 IN VDM_MODE Mode)
413 {
414 // FIXME
415 UNIMPLEMENTED;
416 return TRUE;
417 }
418
419 /* EOF */