4a7ed723540073753bd38049eee0662a691f20e9
[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 LPBYTE Address = (LPBYTE)Buffer;
86
87 UNREFERENCED_PARAMETER(State);
88 UNREFERENCED_PARAMETER(Size);
89
90 switch (Port)
91 {
92 case PIC_MASTER_CMD:
93 case PIC_SLAVE_CMD:
94 {
95 *Address = PicReadCommand(Port);
96 break;
97 }
98
99 case PIC_MASTER_DATA:
100 case PIC_SLAVE_DATA:
101 {
102 *Address = PicReadData(Port);
103 break;
104 }
105
106 case PIT_DATA_PORT(0):
107 case PIT_DATA_PORT(1):
108 case PIT_DATA_PORT(2):
109 {
110 *Address = PitReadData(Port - PIT_DATA_PORT(0));
111 break;
112 }
113
114 case PS2_CONTROL_PORT:
115 {
116 *Address = KeyboardReadStatus();
117 break;
118 }
119
120 case PS2_DATA_PORT:
121 {
122 *Address = KeyboardReadData();
123 break;
124 }
125
126 case VGA_AC_WRITE:
127 case VGA_AC_READ:
128 case VGA_SEQ_INDEX:
129 case VGA_SEQ_DATA:
130 case VGA_DAC_READ_INDEX:
131 case VGA_DAC_WRITE_INDEX:
132 case VGA_DAC_DATA:
133 case VGA_MISC_READ:
134 case VGA_MISC_WRITE:
135 case VGA_CRTC_INDEX:
136 case VGA_CRTC_DATA:
137 case VGA_GC_INDEX:
138 case VGA_GC_DATA:
139 case VGA_STAT_MONO:
140 case VGA_STAT_COLOR:
141 {
142 *Address = VgaReadPort(Port);
143 break;
144 }
145
146 default:
147 {
148 DPRINT1("Read from unknown port: 0x%X\n", Port);
149 }
150 }
151 }
152
153 static VOID WINAPI EmulatorWriteIo(PFAST486_STATE State, ULONG Port, PVOID Buffer, ULONG Size)
154 {
155 BYTE Byte = *(LPBYTE)Buffer;
156
157 UNREFERENCED_PARAMETER(State);
158 UNREFERENCED_PARAMETER(Size);
159
160 switch (Port)
161 {
162 case PIT_COMMAND_PORT:
163 {
164 PitWriteCommand(Byte);
165 break;
166 }
167
168 case PIT_DATA_PORT(0):
169 case PIT_DATA_PORT(1):
170 case PIT_DATA_PORT(2):
171 {
172 PitWriteData(Port - PIT_DATA_PORT(0), Byte);
173 break;
174 }
175
176 case PIC_MASTER_CMD:
177 case PIC_SLAVE_CMD:
178 {
179 PicWriteCommand(Port, Byte);
180 break;
181 }
182
183 case PIC_MASTER_DATA:
184 case PIC_SLAVE_DATA:
185 {
186 PicWriteData(Port, Byte);
187 break;
188 }
189
190 case PS2_CONTROL_PORT:
191 {
192 KeyboardWriteCommand(Byte);
193 break;
194 }
195
196 case PS2_DATA_PORT:
197 {
198 KeyboardWriteData(Byte);
199 break;
200 }
201
202 case VGA_AC_WRITE:
203 case VGA_AC_READ:
204 case VGA_SEQ_INDEX:
205 case VGA_SEQ_DATA:
206 case VGA_DAC_READ_INDEX:
207 case VGA_DAC_WRITE_INDEX:
208 case VGA_DAC_DATA:
209 case VGA_MISC_READ:
210 case VGA_MISC_WRITE:
211 case VGA_CRTC_INDEX:
212 case VGA_CRTC_DATA:
213 case VGA_GC_INDEX:
214 case VGA_GC_DATA:
215 case VGA_STAT_MONO:
216 case VGA_STAT_COLOR:
217 {
218 VgaWritePort(Port, Byte);
219 break;
220 }
221
222 default:
223 {
224 DPRINT1("Write to unknown port: 0x%X\n", Port);
225 }
226 }
227 }
228
229 static VOID WINAPI EmulatorBiosOperation(PFAST486_STATE State, UCHAR BopCode)
230 {
231 WORD StackSegment, StackPointer;
232 LPWORD Stack;
233
234 /* Get the SS:SP */
235 StackSegment = State->SegmentRegs[FAST486_REG_SS].Selector;
236 StackPointer = State->GeneralRegs[FAST486_REG_ESP].LowWord;
237
238 /* Get the stack */
239 Stack = (LPWORD)SEG_OFF_TO_PTR(StackSegment, StackPointer);
240
241 if (BopProc[BopCode] != NULL)
242 BopProc[BopCode](Stack);
243 else
244 DPRINT1("Invalid BOP code %u\n", BopCode);
245 }
246
247 static UCHAR WINAPI EmulatorIntAcknowledge(PFAST486_STATE State)
248 {
249 UNREFERENCED_PARAMETER(State);
250
251 /* Get the interrupt number from the PIC */
252 return PicGetInterrupt();
253 }
254
255 /* PUBLIC FUNCTIONS ***********************************************************/
256
257 BOOLEAN EmulatorInitialize(VOID)
258 {
259 /* Allocate memory for the 16-bit address space */
260 BaseAddress = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, MAX_ADDRESS);
261 if (BaseAddress == NULL) return FALSE;
262
263 /* Initialize the CPU */
264 Fast486Initialize(&EmulatorContext,
265 EmulatorReadMemory,
266 EmulatorWriteMemory,
267 EmulatorReadIo,
268 EmulatorWriteIo,
269 NULL,
270 EmulatorBiosOperation,
271 EmulatorIntAcknowledge);
272
273 /* Enable interrupts */
274 EmulatorSetFlag(EMULATOR_FLAG_IF);
275
276 return TRUE;
277 }
278
279 VOID EmulatorCleanup(VOID)
280 {
281 /* Free the memory allocated for the 16-bit address space */
282 if (BaseAddress != NULL) HeapFree(GetProcessHeap(), 0, BaseAddress);
283 }
284
285 VOID EmulatorSetStack(WORD Segment, DWORD Offset)
286 {
287 Fast486SetStack(&EmulatorContext, Segment, Offset);
288 }
289
290 // FIXME: This function assumes 16-bit mode!!!
291 VOID EmulatorExecute(WORD Segment, WORD Offset)
292 {
293 /* Tell Fast486 to move the instruction pointer */
294 Fast486ExecuteAt(&EmulatorContext, Segment, Offset);
295 }
296
297 VOID EmulatorInterrupt(BYTE Number)
298 {
299 /* Call the Fast486 API */
300 Fast486Interrupt(&EmulatorContext, Number);
301 }
302
303 VOID EmulatorInterruptSignal(VOID)
304 {
305 /* Call the Fast486 API */
306 Fast486InterruptSignal(&EmulatorContext);
307 }
308
309 ULONG EmulatorGetRegister(ULONG Register)
310 {
311 if (Register < EMULATOR_REG_ES)
312 {
313 return EmulatorContext.GeneralRegs[Register].Long;
314 }
315 else
316 {
317 return EmulatorContext.SegmentRegs[Register - EMULATOR_REG_ES].Selector;
318 }
319 }
320
321 VOID EmulatorSetRegister(ULONG Register, ULONG Value)
322 {
323 if (Register < EMULATOR_REG_ES)
324 {
325 EmulatorContext.GeneralRegs[Register].Long = Value;
326 }
327 else
328 {
329 Fast486SetSegment(&EmulatorContext, Register - EMULATOR_REG_ES, (USHORT)Value);
330 }
331 }
332
333 ULONG EmulatorGetProgramCounter(VOID)
334 {
335 return EmulatorContext.InstPtr.Long;
336 }
337
338 BOOLEAN EmulatorGetFlag(ULONG Flag)
339 {
340 return (EmulatorContext.Flags.Long & Flag) ? TRUE : FALSE;
341 }
342
343 VOID EmulatorSetFlag(ULONG Flag)
344 {
345 EmulatorContext.Flags.Long |= Flag;
346 }
347
348 VOID EmulatorClearFlag(ULONG Flag)
349 {
350 EmulatorContext.Flags.Long &= ~Flag;
351 }
352
353 VOID EmulatorStep(VOID)
354 {
355 /* Dump the state for debugging purposes */
356 // Fast486DumpState(&EmulatorContext);
357
358 /* Execute the next instruction */
359 Fast486StepInto(&EmulatorContext);
360 }
361
362 VOID EmulatorSetA20(BOOLEAN Enabled)
363 {
364 A20Line = Enabled;
365 }
366
367 /* EOF */