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 EMULATOR_CONTEXT EmulatorContext
;
30 static BOOLEAN A20Line
= FALSE
;
32 /* PRIVATE FUNCTIONS **********************************************************/
36 static VOID
EmulatorReadMemory(PVOID Context
, UINT Address
, LPBYTE Buffer
, INT Size
)
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 /* If the A20 line is disabled, mask bit 20 */
62 if (!A20Line
) Address
&= ~(1 << 20);
64 /* Make sure the requested address is valid */
65 if ((Address
+ Size
) >= MAX_ADDRESS
) return;
67 /* Make sure we don't write to the ROM area */
68 if ((Address
+ Size
) >= ROM_AREA_START
&& (Address
< ROM_AREA_END
)) return;
70 /* Read the data from the buffer and store it in the virtual address space */
71 RtlCopyMemory((LPVOID
)((ULONG_PTR
)BaseAddress
+ Address
), Buffer
, Size
);
73 /* Check if we modified the console video memory */
74 if (((Address
+ Size
) >= VgaGetVideoBaseAddress())
75 && (Address
< VgaGetVideoLimitAddress()))
77 DWORD VgaAddress
= max(Address
, VgaGetVideoBaseAddress());
78 LPBYTE VgaBuffer
= &Buffer
[VgaAddress
- Address
];
80 /* Write to the VGA memory */
81 VgaWriteMemory(VgaAddress
, VgaBuffer
, Size
);
85 static VOID
EmulatorReadIo(PVOID Context
, UINT Address
, LPBYTE Buffer
, INT Size
)
92 *Buffer
= PicReadCommand(Address
);
99 *Buffer
= PicReadData(Address
);
103 case PIT_DATA_PORT(0):
104 case PIT_DATA_PORT(1):
105 case PIT_DATA_PORT(2):
107 *Buffer
= PitReadData(Address
- PIT_DATA_PORT(0));
111 case PS2_CONTROL_PORT
:
113 *Buffer
= KeyboardReadStatus();
119 *Buffer
= KeyboardReadData();
127 case VGA_DAC_READ_INDEX
:
128 case VGA_DAC_WRITE_INDEX
:
139 *Buffer
= VgaReadPort(Address
);
145 DPRINT1("Read from unknown port: 0x%X\n", Address
);
150 static VOID
EmulatorWriteIo(PVOID Context
, UINT Address
, LPBYTE Buffer
, INT Size
)
156 case PIT_COMMAND_PORT
:
158 PitWriteCommand(Byte
);
162 case PIT_DATA_PORT(0):
163 case PIT_DATA_PORT(1):
164 case PIT_DATA_PORT(2):
166 PitWriteData(Address
- PIT_DATA_PORT(0), Byte
);
173 PicWriteCommand(Address
, Byte
);
177 case PIC_MASTER_DATA
:
180 PicWriteData(Address
, Byte
);
184 case PS2_CONTROL_PORT
:
186 KeyboardWriteCommand(Byte
);
192 KeyboardWriteData(Byte
);
200 case VGA_DAC_READ_INDEX
:
201 case VGA_DAC_WRITE_INDEX
:
212 VgaWritePort(Address
, Byte
);
218 DPRINT1("Write to unknown port: 0x%X\n", Address
);
223 static VOID
EmulatorBop(WORD Code
)
225 WORD StackSegment
, StackPointer
, CodeSegment
, InstructionPointer
;
231 StackSegment
= EmulatorContext
.state
->segment_reg
[SX86_SREG_SS
].val
;
232 StackPointer
= EmulatorContext
.state
->general_reg
[SX86_REG_SP
].val
;
234 StackSegment
= EmulatorContext
.Registers
[EMULATOR_REG_SS
].LowWord
;
235 StackPointer
= EmulatorContext
.Registers
[EMULATOR_REG_SP
].LowWord
;
239 Stack
= (LPWORD
)((ULONG_PTR
)BaseAddress
+ TO_LINEAR(StackSegment
, StackPointer
));
241 if (Code
== EMULATOR_INT_BOP
)
243 /* Get the interrupt number */
244 IntNum
= LOBYTE(Stack
[STACK_INT_NUM
]);
247 InstructionPointer
= Stack
[STACK_IP
];
248 CodeSegment
= Stack
[STACK_CS
];
250 /* Check if this was an exception */
253 /* Display a message to the user */
254 DisplayMessage(L
"Exception: %s occured at %04X:%04X",
255 ExceptionName
[IntNum
],
264 /* Check if this was an PIC IRQ */
265 if (IntNum
>= BIOS_PIC_MASTER_INT
&& IntNum
< BIOS_PIC_MASTER_INT
+ 8)
267 /* It was an IRQ from the master PIC */
268 BiosHandleIrq(IntNum
- BIOS_PIC_MASTER_INT
, Stack
);
271 else if (IntNum
>= BIOS_PIC_SLAVE_INT
&& IntNum
< BIOS_PIC_SLAVE_INT
+ 8)
273 /* It was an IRQ from the slave PIC */
274 BiosHandleIrq(IntNum
- BIOS_PIC_SLAVE_INT
+ 8, Stack
);
280 case BIOS_VIDEO_INTERRUPT
:
282 /* This is the video BIOS interrupt, call the BIOS */
283 BiosVideoService(Stack
);
286 case BIOS_EQUIPMENT_INTERRUPT
:
288 /* This is the BIOS "get equipment" command, call the BIOS */
289 BiosEquipmentService(Stack
);
292 case BIOS_KBD_INTERRUPT
:
294 /* This is the keyboard BIOS interrupt, call the BIOS */
295 BiosKeyboardService(Stack
);
298 case BIOS_TIME_INTERRUPT
:
300 /* This is the time BIOS interrupt, call the BIOS */
301 BiosTimeService(Stack
);
304 case BIOS_SYS_TIMER_INTERRUPT
:
306 /* BIOS timer update */
307 BiosSystemTimerInterrupt(Stack
);
322 DosBreakInterrupt(Stack
);
327 DPRINT1("Unhandled interrupt: 0x%02X\n", IntNum
);
334 static VOID
EmulatorSoftwareInt(PVOID Context
, BYTE Number
)
339 static VOID
EmulatorHardwareInt(PVOID Context
, BYTE Number
)
344 static VOID
EmulatorHardwareIntAck(PVOID Context
, BYTE Number
)
351 /* PUBLIC FUNCTIONS ***********************************************************/
353 BOOLEAN
EmulatorInitialize()
355 /* Allocate memory for the 16-bit address space */
356 BaseAddress
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, MAX_ADDRESS
);
357 if (BaseAddress
== NULL
) return FALSE
;
360 /* Initialize the softx86 CPU emulator */
361 if (!softx86_init(&EmulatorContext
, SX86_CPULEVEL_80286
))
363 HeapFree(GetProcessHeap(), 0, BaseAddress
);
367 /* Initialize the softx87 FPU emulator*/
368 if(!softx87_init(&FpuEmulatorContext
, SX87_FPULEVEL_8087
))
370 softx86_free(&EmulatorContext
);
371 HeapFree(GetProcessHeap(), 0, BaseAddress
);
375 /* Set memory read/write callbacks */
376 EmulatorContext
.callbacks
->on_read_memory
= EmulatorReadMemory
;
377 EmulatorContext
.callbacks
->on_write_memory
= EmulatorWriteMemory
;
379 /* Set MMIO read/write callbacks */
380 EmulatorContext
.callbacks
->on_read_io
= EmulatorReadIo
;
381 EmulatorContext
.callbacks
->on_write_io
= EmulatorWriteIo
;
383 /* Set interrupt callbacks */
384 EmulatorContext
.callbacks
->on_sw_int
= EmulatorSoftwareInt
;
385 EmulatorContext
.callbacks
->on_hw_int
= EmulatorHardwareInt
;
386 EmulatorContext
.callbacks
->on_hw_int_ack
= EmulatorHardwareIntAck
;
388 /* Connect the emulated FPU to the emulated CPU */
389 softx87_connect_to_CPU(&EmulatorContext
, &FpuEmulatorContext
);
391 // TODO: NOT IMPLEMENTED
394 /* Enable interrupts */
395 EmulatorSetFlag(EMULATOR_FLAG_IF
);
400 VOID
EmulatorSetStack(WORD Segment
, WORD Offset
)
403 /* Call the softx86 API */
404 softx86_set_stack_ptr(&EmulatorContext
, Segment
, Offset
);
406 // TODO: NOT IMPLEMENTED
410 VOID
EmulatorExecute(WORD Segment
, WORD Offset
)
413 /* Call the softx86 API */
414 softx86_set_instruction_ptr(&EmulatorContext
, Segment
, Offset
);
416 // TODO: NOT IMPLEMENTED
420 VOID
EmulatorInterrupt(BYTE Number
)
422 LPDWORD IntVecTable
= (LPDWORD
)((ULONG_PTR
)BaseAddress
);
423 UINT Segment
, Offset
;
425 /* Get the segment and offset */
426 Segment
= HIWORD(IntVecTable
[Number
]);
427 Offset
= LOWORD(IntVecTable
[Number
]);
430 /* Call the softx86 API */
431 softx86_make_simple_interrupt_call(&EmulatorContext
, &Segment
, &Offset
);
433 UNREFERENCED_PARAMETER(Segment
);
434 UNREFERENCED_PARAMETER(Offset
);
435 // TODO: NOT IMPLEMENTED
439 VOID
EmulatorExternalInterrupt(BYTE Number
)
442 /* Call the softx86 API */
443 softx86_ext_hw_signal(&EmulatorContext
, Number
);
447 ULONG
EmulatorGetRegister(ULONG Register
)
450 if (Register
< EMULATOR_REG_ES
)
452 return EmulatorContext
.state
->general_reg
[Register
].val
;
456 return EmulatorContext
.state
->segment_reg
[Register
- EMULATOR_REG_ES
].val
;
459 return EmulatorContext
.Registers
[Register
].Long
;
463 VOID
EmulatorSetRegister(ULONG Register
, ULONG Value
)
466 if (Register
< EMULATOR_REG_CS
)
468 EmulatorContext
.state
->general_reg
[Register
].val
= Value
;
472 EmulatorContext
.state
->segment_reg
[Register
- EMULATOR_REG_ES
].val
= Value
;
475 // TODO: NOT IMPLEMENTED
479 BOOLEAN
EmulatorGetFlag(ULONG Flag
)
482 return (EmulatorContext
.state
->reg_flags
.val
& Flag
) ? TRUE
: FALSE
;
484 return (EmulatorContext
.Flags
.Long
& Flag
) ? TRUE
: FALSE
;
488 VOID
EmulatorSetFlag(ULONG Flag
)
491 EmulatorContext
.state
->reg_flags
.val
|= Flag
;
493 EmulatorContext
.Flags
.Long
|= Flag
;
497 VOID
EmulatorClearFlag(ULONG Flag
)
500 EmulatorContext
.state
->reg_flags
.val
&= ~Flag
;
502 EmulatorContext
.Flags
.Long
&= ~Flag
;
506 VOID
EmulatorStep(VOID
)
511 /* Print the current position - useful for debugging */
512 DPRINT("Executing at CS:IP = %04X:%04X\n",
513 EmulatorGetRegister(EMULATOR_REG_CS
),
514 EmulatorContext
.state
->reg_ip
);
516 Instruction
= (LPWORD
)((ULONG_PTR
)BaseAddress
517 + TO_LINEAR(EmulatorGetRegister(EMULATOR_REG_CS
),
518 EmulatorContext
.state
->reg_ip
));
520 /* Check for the BIOS operation (BOP) sequence */
521 if (Instruction
[0] == EMULATOR_BOP
)
523 /* Skip the opcodes */
524 EmulatorContext
.state
->reg_ip
+= 4;
526 // HACK: Refresh the display because the called function may wait.
529 /* Call the BOP handler */
530 EmulatorBop(Instruction
[1]);
533 /* Call the softx86 API */
534 if (!softx86_step(&EmulatorContext
))
537 EmulatorInterrupt(EMULATOR_EXCEPTION_INVALID_OPCODE
);
540 // TODO: NOT IMPLEMENTED
544 VOID
EmulatorCleanup(VOID
)
547 /* Free the softx86 CPU and FPU emulator */
548 softx87_free(&FpuEmulatorContext
);
549 softx86_free(&EmulatorContext
);
552 /* Free the memory allocated for the 16-bit address space */
553 if (BaseAddress
!= NULL
) HeapFree(GetProcessHeap(), 0, BaseAddress
);
556 VOID
EmulatorSetA20(BOOLEAN Enabled
)