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