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