601da014723427c7a3d6e267048d63fb2a474b0f
[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 static VOID WINAPI EmulatorBiosOperation(PFAST486_STATE State, USHORT BopCode)
229 {
230 WORD StackSegment, StackPointer, CodeSegment, InstructionPointer;
231 BYTE IntNum;
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 (BopCode == EMULATOR_INT_BOP)
242 {
243 /* Get the interrupt number */
244 IntNum = LOBYTE(Stack[STACK_INT_NUM]);
245
246 /* Get the CS:IP */
247 InstructionPointer = Stack[STACK_IP];
248 CodeSegment = Stack[STACK_CS];
249
250 /* Check if this was an exception */
251 if (IntNum < 8)
252 {
253 /* Display a message to the user */
254 DisplayMessage(L"Exception: %s occured at %04X:%04X",
255 ExceptionName[IntNum],
256 CodeSegment,
257 InstructionPointer);
258
259 /* Stop the VDM */
260 VdmRunning = FALSE;
261 return;
262 }
263
264 /* Check if this was an PIC IRQ */
265 if (IntNum >= BIOS_PIC_MASTER_INT && IntNum < BIOS_PIC_MASTER_INT + 8)
266 {
267 /* It was an IRQ from the master PIC */
268 BiosHandleIrq(IntNum - BIOS_PIC_MASTER_INT, Stack);
269 return;
270 }
271 else if (IntNum >= BIOS_PIC_SLAVE_INT && IntNum < BIOS_PIC_SLAVE_INT + 8)
272 {
273 /* It was an IRQ from the slave PIC */
274 BiosHandleIrq(IntNum - BIOS_PIC_SLAVE_INT + 8, Stack);
275 return;
276 }
277
278 switch (IntNum)
279 {
280 case BIOS_VIDEO_INTERRUPT:
281 {
282 /* This is the video BIOS interrupt, call the BIOS */
283 BiosVideoService(Stack);
284 break;
285 }
286 case BIOS_EQUIPMENT_INTERRUPT:
287 {
288 /* This is the BIOS "get equipment" command, call the BIOS */
289 BiosEquipmentService(Stack);
290 break;
291 }
292 case BIOS_KBD_INTERRUPT:
293 {
294 /* This is the keyboard BIOS interrupt, call the BIOS */
295 BiosKeyboardService(Stack);
296 break;
297 }
298 case BIOS_TIME_INTERRUPT:
299 {
300 /* This is the time BIOS interrupt, call the BIOS */
301 BiosTimeService(Stack);
302 break;
303 }
304 case BIOS_SYS_TIMER_INTERRUPT:
305 {
306 /* BIOS timer update */
307 BiosSystemTimerInterrupt(Stack);
308 break;
309 }
310 case 0x20:
311 {
312 DosInt20h(Stack);
313 break;
314 }
315 case 0x21:
316 {
317 DosInt21h(Stack);
318 break;
319 }
320 case 0x23:
321 {
322 DosBreakInterrupt(Stack);
323 break;
324 }
325 default:
326 {
327 DPRINT1("Unhandled interrupt: 0x%02X\n", IntNum);
328 break;
329 }
330 }
331 }
332 }
333
334 static UCHAR WINAPI EmulatorIntAcknowledge(PFAST486_STATE State)
335 {
336 UNREFERENCED_PARAMETER(State);
337
338 /* Get the interrupt number from the PIC */
339 return PicGetInterrupt();
340 }
341
342 /* PUBLIC FUNCTIONS ***********************************************************/
343
344 BOOLEAN EmulatorInitialize()
345 {
346 /* Allocate memory for the 16-bit address space */
347 BaseAddress = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, MAX_ADDRESS);
348 if (BaseAddress == NULL) return FALSE;
349
350 /* Set the callbacks */
351 EmulatorContext.MemReadCallback = EmulatorReadMemory;
352 EmulatorContext.MemWriteCallback = EmulatorWriteMemory;
353 EmulatorContext.IoReadCallback = EmulatorReadIo;
354 EmulatorContext.IoWriteCallback = EmulatorWriteIo;
355 EmulatorContext.BopCallback = EmulatorBiosOperation;
356 EmulatorContext.IntAckCallback = EmulatorIntAcknowledge;
357
358 /* Reset the CPU */
359 Fast486Reset(&EmulatorContext);
360
361 /* Enable interrupts */
362 EmulatorSetFlag(EMULATOR_FLAG_IF);
363
364 return TRUE;
365 }
366
367 VOID EmulatorSetStack(WORD Segment, DWORD Offset)
368 {
369 Fast486SetStack(&EmulatorContext, Segment, Offset);
370 }
371
372 // FIXME: This function assumes 16-bit mode!!!
373 VOID EmulatorExecute(WORD Segment, WORD Offset)
374 {
375 /* Tell Fast486 to move the instruction pointer */
376 Fast486ExecuteAt(&EmulatorContext, Segment, Offset);
377 }
378
379 VOID EmulatorInterrupt(BYTE Number)
380 {
381 /* Call the Fast486 API */
382 Fast486Interrupt(&EmulatorContext, Number);
383 }
384
385 VOID EmulatorInterruptSignal(VOID)
386 {
387 /* Call the Fast486 API */
388 Fast486InterruptSignal(&EmulatorContext);
389 }
390
391 ULONG EmulatorGetRegister(ULONG Register)
392 {
393 if (Register < EMULATOR_REG_ES)
394 {
395 return EmulatorContext.GeneralRegs[Register].Long;
396 }
397 else
398 {
399 return EmulatorContext.SegmentRegs[Register - EMULATOR_REG_ES].Selector;
400 }
401 }
402
403 VOID EmulatorSetRegister(ULONG Register, ULONG Value)
404 {
405 if (Register < EMULATOR_REG_ES)
406 {
407 EmulatorContext.GeneralRegs[Register].Long = Value;
408 }
409 else
410 {
411 Fast486SetSegment(&EmulatorContext, Register - EMULATOR_REG_ES, (USHORT)Value);
412 }
413 }
414
415 ULONG EmulatorGetProgramCounter(VOID)
416 {
417 return EmulatorContext.InstPtr.Long;
418 }
419
420 BOOLEAN EmulatorGetFlag(ULONG Flag)
421 {
422 return (EmulatorContext.Flags.Long & Flag) ? TRUE : FALSE;
423 }
424
425 VOID EmulatorSetFlag(ULONG Flag)
426 {
427 EmulatorContext.Flags.Long |= Flag;
428 }
429
430 VOID EmulatorClearFlag(ULONG Flag)
431 {
432 EmulatorContext.Flags.Long &= ~Flag;
433 }
434
435 VOID EmulatorStep(VOID)
436 {
437 /* Dump the state for debugging purposes */
438 // Fast486DumpState(&EmulatorContext);
439
440 /* Execute the next instruction */
441 Fast486StepInto(&EmulatorContext);
442 }
443
444 VOID EmulatorCleanup(VOID)
445 {
446 /* Free the memory allocated for the 16-bit address space */
447 if (BaseAddress != NULL) HeapFree(GetProcessHeap(), 0, BaseAddress);
448 }
449
450 VOID EmulatorSetA20(BOOLEAN Enabled)
451 {
452 A20Line = Enabled;
453 }
454
455 /* EOF */