[FAST486][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 "dos.h"
16 #include "vga.h"
17 #include "pic.h"
18 #include "ps2.h"
19 #include "timer.h"
20
21 /* PRIVATE VARIABLES **********************************************************/
22
23 FAST486_STATE EmulatorContext;
24
25 static BOOLEAN A20Line = FALSE;
26
27 /* PRIVATE FUNCTIONS **********************************************************/
28
29 static VOID WINAPI EmulatorReadMemory(PFAST486_STATE State, ULONG Address, PVOID Buffer, ULONG Size)
30 {
31 UNREFERENCED_PARAMETER(State);
32
33 /* If the A20 line is disabled, mask bit 20 */
34 if (!A20Line) Address &= ~(1 << 20);
35
36 /* Make sure the requested address is valid */
37 if ((Address + Size) >= MAX_ADDRESS) return;
38
39 /* Read the data from the virtual address space and store it in the buffer */
40 RtlCopyMemory(Buffer, (LPVOID)((ULONG_PTR)BaseAddress + Address), Size);
41
42 /* Check if we modified the console video memory */
43 if (((Address + Size) >= VgaGetVideoBaseAddress())
44 && (Address < VgaGetVideoLimitAddress()))
45 {
46 DWORD VgaAddress = max(Address, VgaGetVideoBaseAddress());
47 LPBYTE VgaBuffer = (LPBYTE)((ULONG_PTR)Buffer + VgaAddress - Address);
48
49 /* Read from the VGA memory */
50 VgaReadMemory(VgaAddress, VgaBuffer, Size);
51 }
52 }
53
54 static VOID WINAPI EmulatorWriteMemory(PFAST486_STATE State, ULONG Address, PVOID Buffer, ULONG Size)
55 {
56 UNREFERENCED_PARAMETER(State);
57
58 /* If the A20 line is disabled, mask bit 20 */
59 if (!A20Line) Address &= ~(1 << 20);
60
61 /* Make sure the requested address is valid */
62 if ((Address + Size) >= MAX_ADDRESS) return;
63
64 /* Make sure we don't write to the ROM area */
65 if ((Address + Size) >= ROM_AREA_START && (Address < ROM_AREA_END)) return;
66
67 /* Read the data from the buffer and store it in the virtual address space */
68 RtlCopyMemory((LPVOID)((ULONG_PTR)BaseAddress + Address), Buffer, Size);
69
70 /* Check if we modified the console video memory */
71 if (((Address + Size) >= VgaGetVideoBaseAddress())
72 && (Address < VgaGetVideoLimitAddress()))
73 {
74 DWORD VgaAddress = max(Address, VgaGetVideoBaseAddress());
75 LPBYTE VgaBuffer = (LPBYTE)((ULONG_PTR)Buffer + VgaAddress - Address);
76
77 /* Write to the VGA memory */
78 VgaWriteMemory(VgaAddress, VgaBuffer, Size);
79 }
80 }
81
82 static VOID WINAPI EmulatorReadIo(PFAST486_STATE State, ULONG Port, PVOID Buffer, ULONG Size)
83 {
84 LPBYTE Address = (LPBYTE)Buffer;
85
86 UNREFERENCED_PARAMETER(State);
87 UNREFERENCED_PARAMETER(Size);
88
89 switch (Port)
90 {
91 case PIC_MASTER_CMD:
92 case PIC_SLAVE_CMD:
93 {
94 *Address = PicReadCommand(Port);
95 break;
96 }
97
98 case PIC_MASTER_DATA:
99 case PIC_SLAVE_DATA:
100 {
101 *Address = PicReadData(Port);
102 break;
103 }
104
105 case PIT_DATA_PORT(0):
106 case PIT_DATA_PORT(1):
107 case PIT_DATA_PORT(2):
108 {
109 *Address = PitReadData(Port - PIT_DATA_PORT(0));
110 break;
111 }
112
113 case PS2_CONTROL_PORT:
114 {
115 *Address = KeyboardReadStatus();
116 break;
117 }
118
119 case PS2_DATA_PORT:
120 {
121 *Address = KeyboardReadData();
122 break;
123 }
124
125 case VGA_AC_WRITE:
126 case VGA_AC_READ:
127 case VGA_SEQ_INDEX:
128 case VGA_SEQ_DATA:
129 case VGA_DAC_READ_INDEX:
130 case VGA_DAC_WRITE_INDEX:
131 case VGA_DAC_DATA:
132 case VGA_MISC_READ:
133 case VGA_MISC_WRITE:
134 case VGA_CRTC_INDEX:
135 case VGA_CRTC_DATA:
136 case VGA_GC_INDEX:
137 case VGA_GC_DATA:
138 case VGA_STAT_MONO:
139 case VGA_STAT_COLOR:
140 {
141 *Address = VgaReadPort(Port);
142 break;
143 }
144
145 default:
146 {
147 DPRINT1("Read from unknown port: 0x%X\n", Port);
148 }
149 }
150 }
151
152 static VOID WINAPI EmulatorWriteIo(PFAST486_STATE State, ULONG Port, PVOID Buffer, ULONG Size)
153 {
154 BYTE Byte = *(LPBYTE)Buffer;
155
156 UNREFERENCED_PARAMETER(State);
157 UNREFERENCED_PARAMETER(Size);
158
159 switch (Port)
160 {
161 case PIT_COMMAND_PORT:
162 {
163 PitWriteCommand(Byte);
164 break;
165 }
166
167 case PIT_DATA_PORT(0):
168 case PIT_DATA_PORT(1):
169 case PIT_DATA_PORT(2):
170 {
171 PitWriteData(Port - PIT_DATA_PORT(0), Byte);
172 break;
173 }
174
175 case PIC_MASTER_CMD:
176 case PIC_SLAVE_CMD:
177 {
178 PicWriteCommand(Port, Byte);
179 break;
180 }
181
182 case PIC_MASTER_DATA:
183 case PIC_SLAVE_DATA:
184 {
185 PicWriteData(Port, Byte);
186 break;
187 }
188
189 case PS2_CONTROL_PORT:
190 {
191 KeyboardWriteCommand(Byte);
192 break;
193 }
194
195 case PS2_DATA_PORT:
196 {
197 KeyboardWriteData(Byte);
198 break;
199 }
200
201 case VGA_AC_WRITE:
202 case VGA_AC_READ:
203 case VGA_SEQ_INDEX:
204 case VGA_SEQ_DATA:
205 case VGA_DAC_READ_INDEX:
206 case VGA_DAC_WRITE_INDEX:
207 case VGA_DAC_DATA:
208 case VGA_MISC_READ:
209 case VGA_MISC_WRITE:
210 case VGA_CRTC_INDEX:
211 case VGA_CRTC_DATA:
212 case VGA_GC_INDEX:
213 case VGA_GC_DATA:
214 case VGA_STAT_MONO:
215 case VGA_STAT_COLOR:
216 {
217 VgaWritePort(Port, Byte);
218 break;
219 }
220
221 default:
222 {
223 DPRINT1("Write to unknown port: 0x%X\n", Port);
224 }
225 }
226 }
227
228 VOID WINAPI ControlBop(LPWORD Stack);
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 switch (BopCode)
242 {
243 case EMULATOR_INT_BOP:
244 ControlBop(Stack);
245 break;
246
247 default:
248 DPRINT1("Invalid BOP code %u\n", BopCode);
249 break;
250 }
251 }
252
253 static VOID WINAPI EmulatorIdle(PFAST486_STATE State)
254 {
255 }
256
257 static UCHAR WINAPI EmulatorIntAcknowledge(PFAST486_STATE State)
258 {
259 UNREFERENCED_PARAMETER(State);
260
261 /* Get the interrupt number from the PIC */
262 return PicGetInterrupt();
263 }
264
265 /* PUBLIC FUNCTIONS ***********************************************************/
266
267 BOOLEAN EmulatorInitialize()
268 {
269 /* Allocate memory for the 16-bit address space */
270 BaseAddress = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, MAX_ADDRESS);
271 if (BaseAddress == NULL) return FALSE;
272
273 /* Set the callbacks */
274 EmulatorContext.MemReadCallback = EmulatorReadMemory;
275 EmulatorContext.MemWriteCallback = EmulatorWriteMemory;
276 EmulatorContext.IoReadCallback = EmulatorReadIo; // Must be != NULL
277 EmulatorContext.IoWriteCallback = EmulatorWriteIo; // Must be != NULL
278 EmulatorContext.IdleCallback = EmulatorIdle; // Must be != NULL
279 EmulatorContext.BopCallback = EmulatorBiosOperation;
280 EmulatorContext.IntAckCallback = EmulatorIntAcknowledge;
281
282 /* Reset the CPU */
283 Fast486Reset(&EmulatorContext);
284
285 /* Enable interrupts */
286 EmulatorSetFlag(EMULATOR_FLAG_IF);
287
288 return TRUE;
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 EmulatorCleanup(VOID)
369 {
370 /* Free the memory allocated for the 16-bit address space */
371 if (BaseAddress != NULL) HeapFree(GetProcessHeap(), 0, BaseAddress);
372 }
373
374 VOID EmulatorSetA20(BOOLEAN Enabled)
375 {
376 A20Line = Enabled;
377 }
378
379 /* EOF */