[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 #ifndef NEW_EMULATOR
24 softx86_ctx EmulatorContext;
25 softx87_ctx FpuEmulatorContext;
26 #else
27 SOFT386_STATE EmulatorContext;
28 #endif
29
30 static BOOLEAN A20Line = FALSE;
31
32 /* PRIVATE FUNCTIONS **********************************************************/
33
34 #ifndef NEW_EMULATOR
35
36 static VOID EmulatorReadMemory(PVOID Context, UINT Address, LPBYTE Buffer, INT Size)
37 {
38 UNREFERENCED_PARAMETER(Context);
39
40 /* If the A20 line is disabled, mask bit 20 */
41 if (!A20Line) Address &= ~(1 << 20);
42
43 /* Make sure the requested address is valid */
44 if ((Address + Size) >= MAX_ADDRESS) return;
45
46 /* Read the data from the virtual address space and store it in the buffer */
47 RtlCopyMemory(Buffer, (LPVOID)((ULONG_PTR)BaseAddress + Address), Size);
48
49 /* Check if we modified the console video memory */
50 if (((Address + Size) >= VgaGetVideoBaseAddress())
51 && (Address < VgaGetVideoLimitAddress()))
52 {
53 DWORD VgaAddress = max(Address, VgaGetVideoBaseAddress());
54 LPBYTE VgaBuffer = &Buffer[VgaAddress - Address];
55
56 /* Read from the VGA memory */
57 VgaReadMemory(VgaAddress, VgaBuffer, Size);
58 }
59 }
60
61 static VOID EmulatorWriteMemory(PVOID Context, UINT Address, LPBYTE Buffer, INT Size)
62 {
63 UNREFERENCED_PARAMETER(Context);
64
65 /* If the A20 line is disabled, mask bit 20 */
66 if (!A20Line) Address &= ~(1 << 20);
67
68 /* Make sure the requested address is valid */
69 if ((Address + Size) >= MAX_ADDRESS) return;
70
71 /* Make sure we don't write to the ROM area */
72 if ((Address + Size) >= ROM_AREA_START && (Address < ROM_AREA_END)) return;
73
74 /* Read the data from the buffer and store it in the virtual address space */
75 RtlCopyMemory((LPVOID)((ULONG_PTR)BaseAddress + Address), Buffer, Size);
76
77 /* Check if we modified the console video memory */
78 if (((Address + Size) >= VgaGetVideoBaseAddress())
79 && (Address < VgaGetVideoLimitAddress()))
80 {
81 DWORD VgaAddress = max(Address, VgaGetVideoBaseAddress());
82 LPBYTE VgaBuffer = &Buffer[VgaAddress - Address];
83
84 /* Write to the VGA memory */
85 VgaWriteMemory(VgaAddress, VgaBuffer, Size);
86 }
87 }
88
89 static VOID EmulatorReadIo(PVOID Context, UINT Address, LPBYTE Buffer, INT Size)
90 {
91 UNREFERENCED_PARAMETER(Context);
92 UNREFERENCED_PARAMETER(Size);
93
94 switch (Address)
95 {
96 case PIC_MASTER_CMD:
97 case PIC_SLAVE_CMD:
98 {
99 *Buffer = PicReadCommand(Address);
100 break;
101 }
102
103 case PIC_MASTER_DATA:
104 case PIC_SLAVE_DATA:
105 {
106 *Buffer = PicReadData(Address);
107 break;
108 }
109
110 case PIT_DATA_PORT(0):
111 case PIT_DATA_PORT(1):
112 case PIT_DATA_PORT(2):
113 {
114 *Buffer = PitReadData(Address - PIT_DATA_PORT(0));
115 break;
116 }
117
118 case PS2_CONTROL_PORT:
119 {
120 *Buffer = KeyboardReadStatus();
121 break;
122 }
123
124 case PS2_DATA_PORT:
125 {
126 *Buffer = KeyboardReadData();
127 break;
128 }
129
130 case VGA_AC_WRITE:
131 case VGA_AC_READ:
132 case VGA_SEQ_INDEX:
133 case VGA_SEQ_DATA:
134 case VGA_DAC_READ_INDEX:
135 case VGA_DAC_WRITE_INDEX:
136 case VGA_DAC_DATA:
137 case VGA_MISC_READ:
138 case VGA_MISC_WRITE:
139 case VGA_CRTC_INDEX:
140 case VGA_CRTC_DATA:
141 case VGA_GC_INDEX:
142 case VGA_GC_DATA:
143 case VGA_STAT_MONO:
144 case VGA_STAT_COLOR:
145 {
146 *Buffer = VgaReadPort(Address);
147 break;
148 }
149
150 default:
151 {
152 DPRINT1("Read from unknown port: 0x%X\n", Address);
153 }
154 }
155 }
156
157 static VOID EmulatorWriteIo(PVOID Context, UINT Address, LPBYTE Buffer, INT Size)
158 {
159 BYTE Byte = *Buffer;
160
161 UNREFERENCED_PARAMETER(Context);
162 UNREFERENCED_PARAMETER(Size);
163
164 switch (Address)
165 {
166 case PIT_COMMAND_PORT:
167 {
168 PitWriteCommand(Byte);
169 break;
170 }
171
172 case PIT_DATA_PORT(0):
173 case PIT_DATA_PORT(1):
174 case PIT_DATA_PORT(2):
175 {
176 PitWriteData(Address - PIT_DATA_PORT(0), Byte);
177 break;
178 }
179
180 case PIC_MASTER_CMD:
181 case PIC_SLAVE_CMD:
182 {
183 PicWriteCommand(Address, Byte);
184 break;
185 }
186
187 case PIC_MASTER_DATA:
188 case PIC_SLAVE_DATA:
189 {
190 PicWriteData(Address, Byte);
191 break;
192 }
193
194 case PS2_CONTROL_PORT:
195 {
196 KeyboardWriteCommand(Byte);
197 break;
198 }
199
200 case PS2_DATA_PORT:
201 {
202 KeyboardWriteData(Byte);
203 break;
204 }
205
206 case VGA_AC_WRITE:
207 case VGA_AC_READ:
208 case VGA_SEQ_INDEX:
209 case VGA_SEQ_DATA:
210 case VGA_DAC_READ_INDEX:
211 case VGA_DAC_WRITE_INDEX:
212 case VGA_DAC_DATA:
213 case VGA_MISC_READ:
214 case VGA_MISC_WRITE:
215 case VGA_CRTC_INDEX:
216 case VGA_CRTC_DATA:
217 case VGA_GC_INDEX:
218 case VGA_GC_DATA:
219 case VGA_STAT_MONO:
220 case VGA_STAT_COLOR:
221 {
222 VgaWritePort(Address, Byte);
223 break;
224 }
225
226 default:
227 {
228 DPRINT1("Write to unknown port: 0x%X\n", Address);
229 }
230 }
231 }
232
233 static VOID EmulatorBop(WORD Code)
234 {
235 WORD StackSegment, StackPointer, CodeSegment, InstructionPointer;
236 BYTE IntNum;
237 LPWORD Stack;
238
239 /* Get the SS:SP */
240 #ifndef NEW_EMULATOR
241 StackSegment = EmulatorContext.state->segment_reg[SX86_SREG_SS].val;
242 StackPointer = EmulatorContext.state->general_reg[SX86_REG_SP].val;
243 #else
244 StackSegment = EmulatorContext.SegmentRegs[SOFT386_REG_SS].LowWord;
245 StackPointer = EmulatorContext.SegmentRegs[SOFT386_REG_SP].LowWord;
246 #endif
247
248 /* Get the stack */
249 Stack = (LPWORD)((ULONG_PTR)BaseAddress + TO_LINEAR(StackSegment, StackPointer));
250
251 if (Code == EMULATOR_INT_BOP)
252 {
253 /* Get the interrupt number */
254 IntNum = LOBYTE(Stack[STACK_INT_NUM]);
255
256 /* Get the CS:IP */
257 InstructionPointer = Stack[STACK_IP];
258 CodeSegment = Stack[STACK_CS];
259
260 /* Check if this was an exception */
261 if (IntNum < 8)
262 {
263 /* Display a message to the user */
264 DisplayMessage(L"Exception: %s occured at %04X:%04X",
265 ExceptionName[IntNum],
266 CodeSegment,
267 InstructionPointer);
268
269 /* Stop the VDM */
270 VdmRunning = FALSE;
271 return;
272 }
273
274 /* Check if this was an PIC IRQ */
275 if (IntNum >= BIOS_PIC_MASTER_INT && IntNum < BIOS_PIC_MASTER_INT + 8)
276 {
277 /* It was an IRQ from the master PIC */
278 BiosHandleIrq(IntNum - BIOS_PIC_MASTER_INT, Stack);
279 return;
280 }
281 else if (IntNum >= BIOS_PIC_SLAVE_INT && IntNum < BIOS_PIC_SLAVE_INT + 8)
282 {
283 /* It was an IRQ from the slave PIC */
284 BiosHandleIrq(IntNum - BIOS_PIC_SLAVE_INT + 8, Stack);
285 return;
286 }
287
288 switch (IntNum)
289 {
290 case BIOS_VIDEO_INTERRUPT:
291 {
292 /* This is the video BIOS interrupt, call the BIOS */
293 BiosVideoService(Stack);
294 break;
295 }
296 case BIOS_EQUIPMENT_INTERRUPT:
297 {
298 /* This is the BIOS "get equipment" command, call the BIOS */
299 BiosEquipmentService(Stack);
300 break;
301 }
302 case BIOS_KBD_INTERRUPT:
303 {
304 /* This is the keyboard BIOS interrupt, call the BIOS */
305 BiosKeyboardService(Stack);
306 break;
307 }
308 case BIOS_TIME_INTERRUPT:
309 {
310 /* This is the time BIOS interrupt, call the BIOS */
311 BiosTimeService(Stack);
312 break;
313 }
314 case BIOS_SYS_TIMER_INTERRUPT:
315 {
316 /* BIOS timer update */
317 BiosSystemTimerInterrupt(Stack);
318 break;
319 }
320 case 0x20:
321 {
322 DosInt20h(Stack);
323 break;
324 }
325 case 0x21:
326 {
327 DosInt21h(Stack);
328 break;
329 }
330 case 0x23:
331 {
332 DosBreakInterrupt(Stack);
333 break;
334 }
335 default:
336 {
337 DPRINT1("Unhandled interrupt: 0x%02X\n", IntNum);
338 break;
339 }
340 }
341 }
342 }
343
344 static VOID EmulatorSoftwareInt(PVOID Context, BYTE Number)
345 {
346 UNREFERENCED_PARAMETER(Context);
347 UNREFERENCED_PARAMETER(Number);
348
349 /* Do nothing */
350 }
351
352 static VOID EmulatorHardwareInt(PVOID Context, BYTE Number)
353 {
354 UNREFERENCED_PARAMETER(Context);
355 UNREFERENCED_PARAMETER(Number);
356
357 /* Do nothing */
358 }
359
360 static VOID EmulatorHardwareIntAck(PVOID Context, BYTE Number)
361 {
362 UNREFERENCED_PARAMETER(Context);
363 UNREFERENCED_PARAMETER(Number);
364
365 /* Do nothing */
366 }
367
368 #endif
369
370 /* PUBLIC FUNCTIONS ***********************************************************/
371
372 BOOLEAN EmulatorInitialize()
373 {
374 /* Allocate memory for the 16-bit address space */
375 BaseAddress = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, MAX_ADDRESS);
376 if (BaseAddress == NULL) return FALSE;
377
378 #ifndef NEW_EMULATOR
379 /* Initialize the softx86 CPU emulator */
380 if (!softx86_init(&EmulatorContext, SX86_CPULEVEL_80286))
381 {
382 HeapFree(GetProcessHeap(), 0, BaseAddress);
383 return FALSE;
384 }
385
386 /* Initialize the softx87 FPU emulator*/
387 if(!softx87_init(&FpuEmulatorContext, SX87_FPULEVEL_8087))
388 {
389 softx86_free(&EmulatorContext);
390 HeapFree(GetProcessHeap(), 0, BaseAddress);
391 return FALSE;
392 }
393
394 /* Set memory read/write callbacks */
395 EmulatorContext.callbacks->on_read_memory = EmulatorReadMemory;
396 EmulatorContext.callbacks->on_write_memory = EmulatorWriteMemory;
397
398 /* Set MMIO read/write callbacks */
399 EmulatorContext.callbacks->on_read_io = EmulatorReadIo;
400 EmulatorContext.callbacks->on_write_io = EmulatorWriteIo;
401
402 /* Set interrupt callbacks */
403 EmulatorContext.callbacks->on_sw_int = EmulatorSoftwareInt;
404 EmulatorContext.callbacks->on_hw_int = EmulatorHardwareInt;
405 EmulatorContext.callbacks->on_hw_int_ack = EmulatorHardwareIntAck;
406
407 /* Connect the emulated FPU to the emulated CPU */
408 softx87_connect_to_CPU(&EmulatorContext, &FpuEmulatorContext);
409 #else
410 // TODO: NOT IMPLEMENTED
411 #endif
412
413 /* Enable interrupts */
414 EmulatorSetFlag(EMULATOR_FLAG_IF);
415
416 return TRUE;
417 }
418
419 VOID EmulatorSetStack(WORD Segment, DWORD Offset)
420 {
421 #ifndef NEW_EMULATOR
422 /* Call the softx86 API */
423 softx86_set_stack_ptr(&EmulatorContext, Segment, Offset);
424 #else
425 // TODO: NOT IMPLEMENTED
426 #endif
427 }
428
429 VOID EmulatorExecute(WORD Segment, WORD Offset)
430 {
431 #ifndef NEW_EMULATOR
432 /* Call the softx86 API */
433 softx86_set_instruction_ptr(&EmulatorContext, Segment, Offset);
434 #else
435 // TODO: NOT IMPLEMENTED
436 #endif
437 }
438
439 VOID EmulatorInterrupt(BYTE Number)
440 {
441 LPDWORD IntVecTable = (LPDWORD)((ULONG_PTR)BaseAddress);
442 UINT Segment, Offset;
443
444 /* Get the segment and offset */
445 Segment = HIWORD(IntVecTable[Number]);
446 Offset = LOWORD(IntVecTable[Number]);
447
448 #ifndef NEW_EMULATOR
449 /* Call the softx86 API */
450 softx86_make_simple_interrupt_call(&EmulatorContext, &Segment, &Offset);
451 #else
452 UNREFERENCED_PARAMETER(Segment);
453 UNREFERENCED_PARAMETER(Offset);
454 // TODO: NOT IMPLEMENTED
455 #endif
456 }
457
458 VOID EmulatorExternalInterrupt(BYTE Number)
459 {
460 #ifndef NEW_EMULATOR
461 /* Call the softx86 API */
462 softx86_ext_hw_signal(&EmulatorContext, Number);
463 #endif
464 }
465
466 ULONG EmulatorGetRegister(ULONG Register)
467 {
468 #ifndef NEW_EMULATOR
469 if (Register < EMULATOR_REG_ES)
470 {
471 return EmulatorContext.state->general_reg[Register].val;
472 }
473 else
474 {
475 return EmulatorContext.state->segment_reg[Register - EMULATOR_REG_ES].val;
476 }
477 #else
478 if (Register < EMULATOR_REG_ES)
479 {
480 return EmulatorContext.GeneralRegs[Register].Long;
481 }
482 else
483 {
484 return EmulatorContext.SegmentRegs[Register - EMULATOR_REG_ES].Selector;
485 }
486 #endif
487 }
488
489 ULONG EmulatorGetProgramCounter(VOID)
490 {
491 #ifndef NEW_EMULATOR
492 return EmulatorContext.state->reg_ip;
493 #else
494 return EmulatorContext.InstPtr.Long;
495 #endif
496 }
497
498 VOID EmulatorSetRegister(ULONG Register, ULONG Value)
499 {
500 #ifndef NEW_EMULATOR
501 if (Register < EMULATOR_REG_CS)
502 {
503 EmulatorContext.state->general_reg[Register].val = Value;
504 }
505 else
506 {
507 EmulatorContext.state->segment_reg[Register - EMULATOR_REG_ES].val = (WORD)Value;
508 }
509 #else
510 // TODO: NOT IMPLEMENTED
511 #endif
512 }
513
514 BOOLEAN EmulatorGetFlag(ULONG Flag)
515 {
516 #ifndef NEW_EMULATOR
517 return (EmulatorContext.state->reg_flags.val & Flag) ? TRUE : FALSE;
518 #else
519 return (EmulatorContext.Flags.Long & Flag) ? TRUE : FALSE;
520 #endif
521 }
522
523 VOID EmulatorSetFlag(ULONG Flag)
524 {
525 #ifndef NEW_EMULATOR
526 EmulatorContext.state->reg_flags.val |= Flag;
527 #else
528 EmulatorContext.Flags.Long |= Flag;
529 #endif
530 }
531
532 VOID EmulatorClearFlag(ULONG Flag)
533 {
534 #ifndef NEW_EMULATOR
535 EmulatorContext.state->reg_flags.val &= ~Flag;
536 #else
537 EmulatorContext.Flags.Long &= ~Flag;
538 #endif
539 }
540
541 VOID EmulatorStep(VOID)
542 {
543 #ifndef NEW_EMULATOR
544 LPWORD Instruction;
545
546 /* Print the current position - useful for debugging */
547 DPRINT("Executing at CS:IP = %04X:%04X\n",
548 EmulatorGetRegister(EMULATOR_REG_CS),
549 EmulatorContext.state->reg_ip);
550
551 Instruction = (LPWORD)((ULONG_PTR)BaseAddress
552 + TO_LINEAR(EmulatorGetRegister(EMULATOR_REG_CS),
553 EmulatorContext.state->reg_ip));
554
555 /* Check for the BIOS operation (BOP) sequence */
556 if (Instruction[0] == EMULATOR_BOP)
557 {
558 /* Skip the opcodes */
559 EmulatorContext.state->reg_ip += 4;
560
561 // HACK: Refresh the display because the called function may wait.
562 VgaRefreshDisplay();
563
564 /* Call the BOP handler */
565 EmulatorBop(Instruction[1]);
566 }
567
568 /* Call the softx86 API */
569 if (!softx86_step(&EmulatorContext))
570 {
571 /* Invalid opcode */
572 EmulatorInterrupt(EMULATOR_EXCEPTION_INVALID_OPCODE);
573 }
574 #else
575 // TODO: NOT IMPLEMENTED
576 #endif
577 }
578
579 VOID EmulatorCleanup(VOID)
580 {
581 #ifndef NEW_EMULATOR
582 /* Free the softx86 CPU and FPU emulator */
583 softx87_free(&FpuEmulatorContext);
584 softx86_free(&EmulatorContext);
585 #endif
586
587 /* Free the memory allocated for the 16-bit address space */
588 if (BaseAddress != NULL) HeapFree(GetProcessHeap(), 0, BaseAddress);
589 }
590
591 VOID EmulatorSetA20(BOOLEAN Enabled)
592 {
593 A20Line = Enabled;
594 }
595
596 /* EOF */