dbac362a8d41ff7a0f028707f245cecf82cf6baa
[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 #include "bios.h"
15 #include "bop.h"
16 #include "dos.h"
17 #include "speaker.h"
18 #include "vga.h"
19 #include "pic.h"
20 #include "ps2.h"
21 #include "timer.h"
22 #include "cmos.h"
23
24 /* PRIVATE VARIABLES **********************************************************/
25
26 FAST486_STATE EmulatorContext;
27
28 static BOOLEAN A20Line = FALSE;
29
30 /* PRIVATE FUNCTIONS **********************************************************/
31
32 static VOID WINAPI EmulatorReadMemory(PFAST486_STATE State, ULONG Address, PVOID Buffer, ULONG Size)
33 {
34 UNREFERENCED_PARAMETER(State);
35
36 /* If the A20 line is disabled, mask bit 20 */
37 if (!A20Line) Address &= ~(1 << 20);
38
39 /* Make sure the requested address is valid */
40 if ((Address + Size) >= MAX_ADDRESS) return;
41
42 /* Read the data from the virtual address space and store it in the buffer */
43 RtlCopyMemory(Buffer, (LPVOID)((ULONG_PTR)BaseAddress + Address), Size);
44
45 /* Check if we modified the console video memory */
46 if (((Address + Size) >= VgaGetVideoBaseAddress())
47 && (Address < VgaGetVideoLimitAddress()))
48 {
49 DWORD VgaAddress = max(Address, VgaGetVideoBaseAddress());
50 LPBYTE VgaBuffer = (LPBYTE)((ULONG_PTR)Buffer + VgaAddress - Address);
51
52 /* Read from the VGA memory */
53 VgaReadMemory(VgaAddress, VgaBuffer, Size);
54 }
55 }
56
57 static VOID WINAPI EmulatorWriteMemory(PFAST486_STATE State, ULONG Address, PVOID Buffer, ULONG Size)
58 {
59 UNREFERENCED_PARAMETER(State);
60
61 /* If the A20 line is disabled, mask bit 20 */
62 if (!A20Line) Address &= ~(1 << 20);
63
64 /* Make sure the requested address is valid */
65 if ((Address + Size) >= MAX_ADDRESS) return;
66
67 /* Make sure we don't write to the ROM area */
68 if ((Address + Size) >= ROM_AREA_START && (Address < ROM_AREA_END)) return;
69
70 /* Read the data from the buffer and store it in the virtual address space */
71 RtlCopyMemory((LPVOID)((ULONG_PTR)BaseAddress + Address), Buffer, Size);
72
73 /* Check if we modified the console video memory */
74 if (((Address + Size) >= VgaGetVideoBaseAddress())
75 && (Address < VgaGetVideoLimitAddress()))
76 {
77 DWORD VgaAddress = max(Address, VgaGetVideoBaseAddress());
78 LPBYTE VgaBuffer = (LPBYTE)((ULONG_PTR)Buffer + VgaAddress - Address);
79
80 /* Write to the VGA memory */
81 VgaWriteMemory(VgaAddress, VgaBuffer, Size);
82 }
83 }
84
85 static VOID WINAPI EmulatorReadIo(PFAST486_STATE State, ULONG Port, PVOID Buffer, ULONG DataCount, UCHAR DataSize)
86 {
87 INT i, j;
88 LPBYTE Address = (LPBYTE)Buffer;
89
90 UNREFERENCED_PARAMETER(State);
91
92 for (i = 0; i < DataCount; i++) for (j = 0; j < DataSize; j++)
93 {
94 ULONG CurrentPort = Port + j;
95
96 switch (CurrentPort)
97 {
98 case PIC_MASTER_CMD:
99 case PIC_SLAVE_CMD:
100 {
101 *(Address++) = PicReadCommand(CurrentPort);
102 break;
103 }
104
105 case PIC_MASTER_DATA:
106 case PIC_SLAVE_DATA:
107 {
108 *(Address++) = PicReadData(CurrentPort);
109 break;
110 }
111
112 case PIT_DATA_PORT(0):
113 case PIT_DATA_PORT(1):
114 case PIT_DATA_PORT(2):
115 {
116 *(Address++) = PitReadData(CurrentPort - PIT_DATA_PORT(0));
117 break;
118 }
119
120 case PS2_CONTROL_PORT:
121 {
122 *(Address++) = KeyboardReadStatus();
123 break;
124 }
125
126 case PS2_DATA_PORT:
127 {
128 *(Address++) = KeyboardReadData();
129 break;
130 }
131
132 case CMOS_DATA_PORT:
133 {
134 *(Address++) = CmosReadData();
135 break;
136 }
137
138 case SPEAKER_CONTROL_PORT:
139 {
140 *(Address++) = SpeakerReadStatus();
141 break;
142 }
143
144 case VGA_AC_WRITE:
145 case VGA_AC_READ:
146 case VGA_SEQ_INDEX:
147 case VGA_SEQ_DATA:
148 case VGA_DAC_READ_INDEX:
149 case VGA_DAC_WRITE_INDEX:
150 case VGA_DAC_DATA:
151 case VGA_MISC_READ:
152 case VGA_MISC_WRITE:
153 case VGA_CRTC_INDEX:
154 case VGA_CRTC_DATA:
155 case VGA_GC_INDEX:
156 case VGA_GC_DATA:
157 case VGA_STAT_MONO:
158 case VGA_STAT_COLOR:
159 {
160 *(Address++) = VgaReadPort(CurrentPort);
161 break;
162 }
163
164 default:
165 {
166 DPRINT1("Read from unknown port: 0x%X\n", CurrentPort);
167 }
168 }
169 }
170 }
171
172 static VOID WINAPI EmulatorWriteIo(PFAST486_STATE State, ULONG Port, PVOID Buffer, ULONG DataCount, UCHAR DataSize)
173 {
174 INT i, j;
175 LPBYTE Address = (LPBYTE)Buffer;
176
177 UNREFERENCED_PARAMETER(State);
178
179 for (i = 0; i < DataCount; i++) for (j = 0; j < DataSize; j++)
180 {
181 ULONG CurrentPort = Port + j;
182
183 switch (CurrentPort)
184 {
185 case PIT_COMMAND_PORT:
186 {
187 PitWriteCommand(*(Address++));
188 break;
189 }
190
191 case PIT_DATA_PORT(0):
192 case PIT_DATA_PORT(1):
193 case PIT_DATA_PORT(2):
194 {
195 PitWriteData(CurrentPort - PIT_DATA_PORT(0), *(Address++));
196 break;
197 }
198
199 case PIC_MASTER_CMD:
200 case PIC_SLAVE_CMD:
201 {
202 PicWriteCommand(CurrentPort, *(Address++));
203 break;
204 }
205
206 case PIC_MASTER_DATA:
207 case PIC_SLAVE_DATA:
208 {
209 PicWriteData(CurrentPort, *(Address++));
210 break;
211 }
212
213 case PS2_CONTROL_PORT:
214 {
215 KeyboardWriteCommand(*(Address++));
216 break;
217 }
218
219 case PS2_DATA_PORT:
220 {
221 KeyboardWriteData(*(Address++));
222 break;
223 }
224
225 case CMOS_ADDRESS_PORT:
226 {
227 CmosWriteAddress(*(Address++));
228 break;
229 }
230
231 case CMOS_DATA_PORT:
232 {
233 CmosWriteData(*(Address++));
234 break;
235 }
236
237 case SPEAKER_CONTROL_PORT:
238 {
239 SpeakerWriteCommand(*(Address++));
240 break;
241 }
242
243 case VGA_AC_WRITE:
244 case VGA_AC_READ:
245 case VGA_SEQ_INDEX:
246 case VGA_SEQ_DATA:
247 case VGA_DAC_READ_INDEX:
248 case VGA_DAC_WRITE_INDEX:
249 case VGA_DAC_DATA:
250 case VGA_MISC_READ:
251 case VGA_MISC_WRITE:
252 case VGA_CRTC_INDEX:
253 case VGA_CRTC_DATA:
254 case VGA_GC_INDEX:
255 case VGA_GC_DATA:
256 case VGA_STAT_MONO:
257 case VGA_STAT_COLOR:
258 {
259 VgaWritePort(CurrentPort, *(Address++));
260 break;
261 }
262
263 default:
264 {
265 DPRINT1("Write to unknown port: 0x%X\n", CurrentPort);
266 }
267 }
268 }
269 }
270
271 static VOID WINAPI EmulatorBiosOperation(PFAST486_STATE State, UCHAR BopCode)
272 {
273 WORD StackSegment, StackPointer;
274 LPWORD Stack;
275
276 /* Get the SS:SP */
277 StackSegment = State->SegmentRegs[FAST486_REG_SS].Selector;
278 StackPointer = State->GeneralRegs[FAST486_REG_ESP].LowWord;
279
280 /* Get the stack */
281 Stack = (LPWORD)SEG_OFF_TO_PTR(StackSegment, StackPointer);
282
283 if (BopProc[BopCode] != NULL)
284 BopProc[BopCode](Stack);
285 else
286 DPRINT1("Invalid BOP code %u\n", BopCode);
287 }
288
289 static UCHAR WINAPI EmulatorIntAcknowledge(PFAST486_STATE State)
290 {
291 UNREFERENCED_PARAMETER(State);
292
293 /* Get the interrupt number from the PIC */
294 return PicGetInterrupt();
295 }
296
297 /* PUBLIC FUNCTIONS ***********************************************************/
298
299 BOOLEAN EmulatorInitialize(VOID)
300 {
301 /* Allocate memory for the 16-bit address space */
302 BaseAddress = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, MAX_ADDRESS);
303 if (BaseAddress == NULL) return FALSE;
304
305 /* Initialize the CPU */
306 Fast486Initialize(&EmulatorContext,
307 EmulatorReadMemory,
308 EmulatorWriteMemory,
309 EmulatorReadIo,
310 EmulatorWriteIo,
311 NULL,
312 EmulatorBiosOperation,
313 EmulatorIntAcknowledge);
314
315 /* Enable interrupts */
316 EmulatorSetFlag(EMULATOR_FLAG_IF);
317
318 return TRUE;
319 }
320
321 VOID EmulatorCleanup(VOID)
322 {
323 /* Free the memory allocated for the 16-bit address space */
324 if (BaseAddress != NULL) HeapFree(GetProcessHeap(), 0, BaseAddress);
325 }
326
327 VOID EmulatorSetStack(WORD Segment, DWORD Offset)
328 {
329 Fast486SetStack(&EmulatorContext, Segment, Offset);
330 }
331
332 // FIXME: This function assumes 16-bit mode!!!
333 VOID EmulatorExecute(WORD Segment, WORD Offset)
334 {
335 /* Tell Fast486 to move the instruction pointer */
336 Fast486ExecuteAt(&EmulatorContext, Segment, Offset);
337 }
338
339 VOID EmulatorInterrupt(BYTE Number)
340 {
341 /* Call the Fast486 API */
342 Fast486Interrupt(&EmulatorContext, Number);
343 }
344
345 VOID EmulatorInterruptSignal(VOID)
346 {
347 /* Call the Fast486 API */
348 Fast486InterruptSignal(&EmulatorContext);
349 }
350
351 ULONG EmulatorGetRegister(ULONG Register)
352 {
353 if (Register < EMULATOR_REG_ES)
354 {
355 return EmulatorContext.GeneralRegs[Register].Long;
356 }
357 else
358 {
359 return EmulatorContext.SegmentRegs[Register - EMULATOR_REG_ES].Selector;
360 }
361 }
362
363 VOID EmulatorSetRegister(ULONG Register, ULONG Value)
364 {
365 if (Register < EMULATOR_REG_ES)
366 {
367 EmulatorContext.GeneralRegs[Register].Long = Value;
368 }
369 else
370 {
371 Fast486SetSegment(&EmulatorContext, Register - EMULATOR_REG_ES, (USHORT)Value);
372 }
373 }
374
375 ULONG EmulatorGetProgramCounter(VOID)
376 {
377 return EmulatorContext.InstPtr.Long;
378 }
379
380 BOOLEAN EmulatorGetFlag(ULONG Flag)
381 {
382 return (EmulatorContext.Flags.Long & Flag) ? TRUE : FALSE;
383 }
384
385 VOID EmulatorSetFlag(ULONG Flag)
386 {
387 EmulatorContext.Flags.Long |= Flag;
388 }
389
390 VOID EmulatorClearFlag(ULONG Flag)
391 {
392 EmulatorContext.Flags.Long &= ~Flag;
393 }
394
395 VOID EmulatorStep(VOID)
396 {
397 /* Dump the state for debugging purposes */
398 // Fast486DumpState(&EmulatorContext);
399
400 /* Execute the next instruction */
401 Fast486StepInto(&EmulatorContext);
402 }
403
404 VOID EmulatorSetA20(BOOLEAN Enabled)
405 {
406 A20Line = Enabled;
407 }
408
409 /* EOF */