2 * COPYRIGHT: GPL - See COPYING in the top level directory
3 * PROJECT: ReactOS Virtual DOS Machine
5 * PURPOSE: Minimal x86 machine emulator for the VDM
6 * PROGRAMMERS: Aleksandar Andrejevic <theflash AT sdf DOT lonestar DOT org>
9 /* INCLUDES *******************************************************************/
21 /* PRIVATE VARIABLES **********************************************************/
24 softx86_ctx EmulatorContext
;
25 softx87_ctx FpuEmulatorContext
;
27 SOFT386_STATE EmulatorContext
;
30 static BOOLEAN A20Line
= FALSE
;
32 /* PRIVATE FUNCTIONS **********************************************************/
34 static VOID
EmulatorReadMemory(PVOID Context
, UINT Address
, LPBYTE Buffer
, INT Size
)
36 UNREFERENCED_PARAMETER(Context
);
38 /* If the A20 line is disabled, mask bit 20 */
39 if (!A20Line
) Address
&= ~(1 << 20);
41 /* Make sure the requested address is valid */
42 if ((Address
+ Size
) >= MAX_ADDRESS
) return;
44 /* Read the data from the virtual address space and store it in the buffer */
45 RtlCopyMemory(Buffer
, (LPVOID
)((ULONG_PTR
)BaseAddress
+ Address
), Size
);
47 /* Check if we modified the console video memory */
48 if (((Address
+ Size
) >= VgaGetVideoBaseAddress())
49 && (Address
< VgaGetVideoLimitAddress()))
51 DWORD VgaAddress
= max(Address
, VgaGetVideoBaseAddress());
52 LPBYTE VgaBuffer
= &Buffer
[VgaAddress
- Address
];
54 /* Read from the VGA memory */
55 VgaReadMemory(VgaAddress
, VgaBuffer
, Size
);
59 static VOID
EmulatorWriteMemory(PVOID Context
, UINT Address
, LPBYTE Buffer
, INT Size
)
61 UNREFERENCED_PARAMETER(Context
);
63 /* If the A20 line is disabled, mask bit 20 */
64 if (!A20Line
) Address
&= ~(1 << 20);
66 /* Make sure the requested address is valid */
67 if ((Address
+ Size
) >= MAX_ADDRESS
) return;
69 /* Make sure we don't write to the ROM area */
70 if ((Address
+ Size
) >= ROM_AREA_START
&& (Address
< ROM_AREA_END
)) return;
72 /* Read the data from the buffer and store it in the virtual address space */
73 RtlCopyMemory((LPVOID
)((ULONG_PTR
)BaseAddress
+ Address
), Buffer
, Size
);
75 /* Check if we modified the console video memory */
76 if (((Address
+ Size
) >= VgaGetVideoBaseAddress())
77 && (Address
< VgaGetVideoLimitAddress()))
79 DWORD VgaAddress
= max(Address
, VgaGetVideoBaseAddress());
80 LPBYTE VgaBuffer
= &Buffer
[VgaAddress
- Address
];
82 /* Write to the VGA memory */
83 VgaWriteMemory(VgaAddress
, VgaBuffer
, Size
);
87 static VOID
EmulatorReadIo(PVOID Context
, UINT Address
, LPBYTE Buffer
, INT Size
)
89 UNREFERENCED_PARAMETER(Context
);
90 UNREFERENCED_PARAMETER(Size
);
97 *Buffer
= PicReadCommand(Address
);
101 case PIC_MASTER_DATA
:
104 *Buffer
= PicReadData(Address
);
108 case PIT_DATA_PORT(0):
109 case PIT_DATA_PORT(1):
110 case PIT_DATA_PORT(2):
112 *Buffer
= PitReadData(Address
- PIT_DATA_PORT(0));
116 case PS2_CONTROL_PORT
:
118 *Buffer
= KeyboardReadStatus();
124 *Buffer
= KeyboardReadData();
132 case VGA_DAC_READ_INDEX
:
133 case VGA_DAC_WRITE_INDEX
:
144 *Buffer
= VgaReadPort(Address
);
150 DPRINT1("Read from unknown port: 0x%X\n", Address
);
155 static VOID
EmulatorWriteIo(PVOID Context
, UINT Address
, LPBYTE Buffer
, INT Size
)
159 UNREFERENCED_PARAMETER(Context
);
160 UNREFERENCED_PARAMETER(Size
);
164 case PIT_COMMAND_PORT
:
166 PitWriteCommand(Byte
);
170 case PIT_DATA_PORT(0):
171 case PIT_DATA_PORT(1):
172 case PIT_DATA_PORT(2):
174 PitWriteData(Address
- PIT_DATA_PORT(0), Byte
);
181 PicWriteCommand(Address
, Byte
);
185 case PIC_MASTER_DATA
:
188 PicWriteData(Address
, Byte
);
192 case PS2_CONTROL_PORT
:
194 KeyboardWriteCommand(Byte
);
200 KeyboardWriteData(Byte
);
208 case VGA_DAC_READ_INDEX
:
209 case VGA_DAC_WRITE_INDEX
:
220 VgaWritePort(Address
, Byte
);
226 DPRINT1("Write to unknown port: 0x%X\n", Address
);
233 static VOID
EmulatorBop(WORD Code
)
235 WORD StackSegment
, StackPointer
, CodeSegment
, InstructionPointer
;
241 StackSegment
= EmulatorContext
.state
->segment_reg
[SX86_SREG_SS
].val
;
242 StackPointer
= EmulatorContext
.state
->general_reg
[SX86_REG_SP
].val
;
244 StackSegment
= EmulatorContext
.SegmentRegs
[SOFT386_REG_SS
].LowWord
;
245 StackPointer
= EmulatorContext
.SegmentRegs
[SOFT386_REG_SP
].LowWord
;
249 Stack
= (LPWORD
)((ULONG_PTR
)BaseAddress
+ TO_LINEAR(StackSegment
, StackPointer
));
251 if (Code
== EMULATOR_INT_BOP
)
253 /* Get the interrupt number */
254 IntNum
= LOBYTE(Stack
[STACK_INT_NUM
]);
257 InstructionPointer
= Stack
[STACK_IP
];
258 CodeSegment
= Stack
[STACK_CS
];
260 /* Check if this was an exception */
263 /* Display a message to the user */
264 DisplayMessage(L
"Exception: %s occured at %04X:%04X",
265 ExceptionName
[IntNum
],
274 /* Check if this was an PIC IRQ */
275 if (IntNum
>= BIOS_PIC_MASTER_INT
&& IntNum
< BIOS_PIC_MASTER_INT
+ 8)
277 /* It was an IRQ from the master PIC */
278 BiosHandleIrq(IntNum
- BIOS_PIC_MASTER_INT
, Stack
);
281 else if (IntNum
>= BIOS_PIC_SLAVE_INT
&& IntNum
< BIOS_PIC_SLAVE_INT
+ 8)
283 /* It was an IRQ from the slave PIC */
284 BiosHandleIrq(IntNum
- BIOS_PIC_SLAVE_INT
+ 8, Stack
);
290 case BIOS_VIDEO_INTERRUPT
:
292 /* This is the video BIOS interrupt, call the BIOS */
293 BiosVideoService(Stack
);
296 case BIOS_EQUIPMENT_INTERRUPT
:
298 /* This is the BIOS "get equipment" command, call the BIOS */
299 BiosEquipmentService(Stack
);
302 case BIOS_KBD_INTERRUPT
:
304 /* This is the keyboard BIOS interrupt, call the BIOS */
305 BiosKeyboardService(Stack
);
308 case BIOS_TIME_INTERRUPT
:
310 /* This is the time BIOS interrupt, call the BIOS */
311 BiosTimeService(Stack
);
314 case BIOS_SYS_TIMER_INTERRUPT
:
316 /* BIOS timer update */
317 BiosSystemTimerInterrupt(Stack
);
332 DosBreakInterrupt(Stack
);
337 DPRINT1("Unhandled interrupt: 0x%02X\n", IntNum
);
344 static VOID
EmulatorSoftwareInt(PVOID Context
, BYTE Number
)
346 UNREFERENCED_PARAMETER(Context
);
347 UNREFERENCED_PARAMETER(Number
);
352 static VOID
EmulatorHardwareInt(PVOID Context
, BYTE Number
)
354 UNREFERENCED_PARAMETER(Context
);
355 UNREFERENCED_PARAMETER(Number
);
360 static VOID
EmulatorHardwareIntAck(PVOID Context
, BYTE Number
)
362 UNREFERENCED_PARAMETER(Context
);
363 UNREFERENCED_PARAMETER(Number
);
370 /* PUBLIC FUNCTIONS ***********************************************************/
372 BOOLEAN
EmulatorInitialize()
374 /* Allocate memory for the 16-bit address space */
375 BaseAddress
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, MAX_ADDRESS
);
376 if (BaseAddress
== NULL
) return FALSE
;
379 /* Initialize the softx86 CPU emulator */
380 if (!softx86_init(&EmulatorContext
, SX86_CPULEVEL_80286
))
382 HeapFree(GetProcessHeap(), 0, BaseAddress
);
386 /* Initialize the softx87 FPU emulator*/
387 if(!softx87_init(&FpuEmulatorContext
, SX87_FPULEVEL_8087
))
389 softx86_free(&EmulatorContext
);
390 HeapFree(GetProcessHeap(), 0, BaseAddress
);
394 /* Set memory read/write callbacks */
395 EmulatorContext
.callbacks
->on_read_memory
= EmulatorReadMemory
;
396 EmulatorContext
.callbacks
->on_write_memory
= EmulatorWriteMemory
;
398 /* Set MMIO read/write callbacks */
399 EmulatorContext
.callbacks
->on_read_io
= EmulatorReadIo
;
400 EmulatorContext
.callbacks
->on_write_io
= EmulatorWriteIo
;
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
;
407 /* Connect the emulated FPU to the emulated CPU */
408 softx87_connect_to_CPU(&EmulatorContext
, &FpuEmulatorContext
);
410 /* Set the callbacks */
411 EmulatorContext
.MemReadCallback
= (SOFT386_MEM_READ_PROC
)EmulatorReadMemory
;
412 EmulatorContext
.MemWriteCallback
= (SOFT386_MEM_WRITE_PROC
)EmulatorWriteMemory
;
413 EmulatorContext
.IoReadCallback
= (SOFT386_IO_READ_PROC
)EmulatorReadIo
;
414 EmulatorContext
.IoWriteCallback
= (SOFT386_IO_WRITE_PROC
)EmulatorWriteIo
;
417 Soft386Reset(&EmulatorContext
);
420 /* Enable interrupts */
421 EmulatorSetFlag(EMULATOR_FLAG_IF
);
426 VOID
EmulatorSetStack(WORD Segment
, DWORD Offset
)
429 /* Call the softx86 API */
430 softx86_set_stack_ptr(&EmulatorContext
, Segment
, Offset
);
432 Soft386SetStack(&EmulatorContext
, Segment
, Offset
);
436 // FIXME: This function assumes 16-bit mode!!!
437 VOID
EmulatorExecute(WORD Segment
, WORD Offset
)
440 /* Call the softx86 API */
441 softx86_set_instruction_ptr(&EmulatorContext
, Segment
, Offset
);
443 /* Tell Soft386 to move the instruction pointer */
444 Soft386ExecuteAt(&EmulatorContext
, Segment
, Offset
);
448 VOID
EmulatorInterrupt(BYTE Number
)
450 LPDWORD IntVecTable
= (LPDWORD
)((ULONG_PTR
)BaseAddress
);
451 UINT Segment
, Offset
;
453 /* Get the segment and offset */
454 Segment
= HIWORD(IntVecTable
[Number
]);
455 Offset
= LOWORD(IntVecTable
[Number
]);
458 /* Call the softx86 API */
459 softx86_make_simple_interrupt_call(&EmulatorContext
, &Segment
, &Offset
);
461 UNREFERENCED_PARAMETER(Segment
);
462 UNREFERENCED_PARAMETER(Offset
);
463 // TODO: NOT IMPLEMENTED
467 VOID
EmulatorExternalInterrupt(BYTE Number
)
470 /* Call the softx86 API */
471 softx86_ext_hw_signal(&EmulatorContext
, Number
);
475 ULONG
EmulatorGetRegister(ULONG Register
)
478 if (Register
< EMULATOR_REG_ES
)
480 return EmulatorContext
.state
->general_reg
[Register
].val
;
484 return EmulatorContext
.state
->segment_reg
[Register
- EMULATOR_REG_ES
].val
;
487 if (Register
< EMULATOR_REG_ES
)
489 return EmulatorContext
.GeneralRegs
[Register
].Long
;
493 return EmulatorContext
.SegmentRegs
[Register
- EMULATOR_REG_ES
].Selector
;
498 ULONG
EmulatorGetProgramCounter(VOID
)
501 return EmulatorContext
.state
->reg_ip
;
503 return EmulatorContext
.InstPtr
.Long
;
507 VOID
EmulatorSetRegister(ULONG Register
, ULONG Value
)
510 if (Register
< EMULATOR_REG_CS
)
512 EmulatorContext
.state
->general_reg
[Register
].val
= Value
;
516 EmulatorContext
.state
->segment_reg
[Register
- EMULATOR_REG_ES
].val
= (WORD
)Value
;
519 // TODO: NOT IMPLEMENTED
523 BOOLEAN
EmulatorGetFlag(ULONG Flag
)
526 return (EmulatorContext
.state
->reg_flags
.val
& Flag
) ? TRUE
: FALSE
;
528 return (EmulatorContext
.Flags
.Long
& Flag
) ? TRUE
: FALSE
;
532 VOID
EmulatorSetFlag(ULONG Flag
)
535 EmulatorContext
.state
->reg_flags
.val
|= Flag
;
537 EmulatorContext
.Flags
.Long
|= Flag
;
541 VOID
EmulatorClearFlag(ULONG Flag
)
544 EmulatorContext
.state
->reg_flags
.val
&= ~Flag
;
546 EmulatorContext
.Flags
.Long
&= ~Flag
;
550 VOID
EmulatorStep(VOID
)
555 /* Print the current position - useful for debugging */
556 DPRINT("Executing at CS:IP = %04X:%04X\n",
557 EmulatorGetRegister(EMULATOR_REG_CS
),
558 EmulatorContext
.state
->reg_ip
);
560 Instruction
= (LPWORD
)((ULONG_PTR
)BaseAddress
561 + TO_LINEAR(EmulatorGetRegister(EMULATOR_REG_CS
),
562 EmulatorContext
.state
->reg_ip
));
564 /* Check for the BIOS operation (BOP) sequence */
565 if (Instruction
[0] == EMULATOR_BOP
)
567 /* Skip the opcodes */
568 EmulatorContext
.state
->reg_ip
+= 4;
570 // HACK: Refresh the display because the called function may wait.
573 /* Call the BOP handler */
574 EmulatorBop(Instruction
[1]);
577 /* Call the softx86 API */
578 if (!softx86_step(&EmulatorContext
))
581 EmulatorInterrupt(EMULATOR_EXCEPTION_INVALID_OPCODE
);
584 /* Dump the state for debugging purposes */
585 Soft386DumpState(&EmulatorContext
);
587 /* Execute the next instruction */
588 Soft386StepInto(&EmulatorContext
);
592 VOID
EmulatorCleanup(VOID
)
595 /* Free the softx86 CPU and FPU emulator */
596 softx87_free(&FpuEmulatorContext
);
597 softx86_free(&EmulatorContext
);
600 /* Free the memory allocated for the 16-bit address space */
601 if (BaseAddress
!= NULL
) HeapFree(GetProcessHeap(), 0, BaseAddress
);
604 VOID
EmulatorSetA20(BOOLEAN Enabled
)