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 *******************************************************************/
20 /* PRIVATE VARIABLES **********************************************************/
23 softx86_ctx EmulatorContext
;
24 softx87_ctx FpuEmulatorContext
;
26 EMULATOR_CONTEXT EmulatorContext
;
29 static BOOLEAN A20Line
= FALSE
;
31 /* PRIVATE FUNCTIONS **********************************************************/
35 static VOID
EmulatorReadMemory(PVOID Context
, UINT Address
, LPBYTE Buffer
, INT Size
)
37 /* If the A20 line is disabled, mask bit 20 */
38 if (!A20Line
) Address
&= ~(1 << 20);
40 /* Make sure the requested address is valid */
41 if ((Address
+ Size
) >= MAX_ADDRESS
) return;
43 /* Are we reading some of the console video memory? */
44 if (((Address
+ Size
) >= BiosGetVideoMemoryStart())
45 && (Address
< CONSOLE_VIDEO_MEM_END
))
47 /* Call the VDM BIOS to update the video memory */
48 BiosUpdateVideoMemory(max(Address
, BiosGetVideoMemoryStart()),
49 min(Address
+ Size
, CONSOLE_VIDEO_MEM_END
));
52 /* Read the data from the virtual address space and store it in the buffer */
53 RtlCopyMemory(Buffer
, (LPVOID
)((ULONG_PTR
)BaseAddress
+ Address
), Size
);
56 static VOID
EmulatorWriteMemory(PVOID Context
, UINT Address
, LPBYTE Buffer
, INT Size
)
58 /* If the A20 line is disabled, mask bit 20 */
59 if (!A20Line
) Address
&= ~(1 << 20);
61 /* Make sure the requested address is valid */
62 if ((Address
+ Size
) >= MAX_ADDRESS
) return;
64 /* Make sure we don't write to the ROM area */
65 if ((Address
+ Size
) >= ROM_AREA_START
&& (Address
< ROM_AREA_END
)) return;
67 /* Read the data from the buffer and store it in the virtual address space */
68 RtlCopyMemory((LPVOID
)((ULONG_PTR
)BaseAddress
+ Address
), Buffer
, Size
);
70 /* Check if we modified the console video memory */
71 if (((Address
+ Size
) >= BiosGetVideoMemoryStart())
72 && (Address
< CONSOLE_VIDEO_MEM_END
))
74 /* Call the VDM BIOS to update the screen */
75 BiosUpdateConsole(max(Address
, BiosGetVideoMemoryStart()),
76 min(Address
+ Size
, CONSOLE_VIDEO_MEM_END
));
80 static VOID
EmulatorReadIo(PVOID Context
, UINT Address
, LPBYTE Buffer
, INT Size
)
87 *Buffer
= PicReadCommand(Address
);
94 *Buffer
= PicReadData(Address
);
98 case PIT_DATA_PORT(0):
99 case PIT_DATA_PORT(1):
100 case PIT_DATA_PORT(2):
102 *Buffer
= PitReadData(Address
- PIT_DATA_PORT(0));
106 case PS2_CONTROL_PORT
:
108 *Buffer
= KeyboardReadStatus();
114 *Buffer
= KeyboardReadData();
120 static VOID
EmulatorWriteIo(PVOID Context
, UINT Address
, LPBYTE Buffer
, INT Size
)
126 case PIT_COMMAND_PORT
:
128 PitWriteCommand(Byte
);
132 case PIT_DATA_PORT(0):
133 case PIT_DATA_PORT(1):
134 case PIT_DATA_PORT(2):
136 PitWriteData(Address
- PIT_DATA_PORT(0), Byte
);
143 PicWriteCommand(Address
, Byte
);
147 case PIC_MASTER_DATA
:
150 PicWriteData(Address
, Byte
);
154 case PS2_CONTROL_PORT
:
156 KeyboardWriteCommand(Byte
);
162 KeyboardWriteData(Byte
);
168 static VOID
EmulatorBop(WORD Code
)
170 WORD StackSegment
, StackPointer
, CodeSegment
, InstructionPointer
;
176 StackSegment
= EmulatorContext
.state
->segment_reg
[SX86_SREG_SS
].val
;
177 StackPointer
= EmulatorContext
.state
->general_reg
[SX86_REG_SP
].val
;
179 StackSegment
= EmulatorContext
.Registers
[EMULATOR_REG_SS
].LowWord
;
180 StackPointer
= EmulatorContext
.Registers
[EMULATOR_REG_SP
].LowWord
;
184 Stack
= (LPWORD
)((ULONG_PTR
)BaseAddress
+ TO_LINEAR(StackSegment
, StackPointer
));
186 if (Code
== EMULATOR_INT_BOP
)
188 /* Get the interrupt number */
189 IntNum
= LOBYTE(Stack
[0]);
192 InstructionPointer
= Stack
[1];
193 CodeSegment
= Stack
[2];
195 /* Check if this was an exception */
198 /* Display a message to the user */
199 DisplayMessage(L
"Exception: %s occured at %04X:%04X",
200 ExceptionName
[IntNum
],
209 /* Check if this was an PIC IRQ */
210 if (IntNum
>= BIOS_PIC_MASTER_INT
&& IntNum
< BIOS_PIC_MASTER_INT
+ 8)
212 /* It was an IRQ from the master PIC */
213 BiosHandleIrq(IntNum
- BIOS_PIC_MASTER_INT
);
216 else if (IntNum
>= BIOS_PIC_SLAVE_INT
&& IntNum
< BIOS_PIC_SLAVE_INT
+ 8)
218 /* It was an IRQ from the slave PIC */
219 BiosHandleIrq(IntNum
- BIOS_PIC_SLAVE_INT
+ 8);
225 case BIOS_VIDEO_INTERRUPT
:
227 /* This is the video BIOS interrupt, call the BIOS */
231 case BIOS_EQUIPMENT_INTERRUPT
:
233 /* This is the BIOS "get equipment" command, call the BIOS */
234 BiosEquipmentService();
237 case BIOS_KBD_INTERRUPT
:
239 /* This is the keyboard BIOS interrupt, call the BIOS */
240 BiosKeyboardService();
243 case BIOS_TIME_INTERRUPT
:
245 /* This is the time BIOS interrupt, call the BIOS */
249 case BIOS_SYS_TIMER_INTERRUPT
:
251 /* BIOS timer update */
252 BiosSystemTimerInterrupt();
257 DosInt20h(CodeSegment
);
262 DosInt21h(CodeSegment
);
272 DPRINT1("Unhandled interrupt: 0x%02X\n", IntNum
);
277 /* Update the flags on the stack */
279 Stack
[3] = EmulatorContext
.state
->reg_flags
.val
;
281 Stack
[3] = EmulatorContext
.Flags
.LowWord
;
286 static VOID
EmulatorSoftwareInt(PVOID Context
, BYTE Number
)
291 static VOID
EmulatorHardwareInt(PVOID Context
, BYTE Number
)
296 static VOID
EmulatorHardwareIntAck(PVOID Context
, BYTE Number
)
303 /* PUBLIC FUNCTIONS ***********************************************************/
305 BOOLEAN
EmulatorInitialize()
307 /* Allocate memory for the 16-bit address space */
308 BaseAddress
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, MAX_ADDRESS
);
309 if (BaseAddress
== NULL
) return FALSE
;
312 /* Initialize the softx86 CPU emulator */
313 if (!softx86_init(&EmulatorContext
, SX86_CPULEVEL_80286
))
315 HeapFree(GetProcessHeap(), 0, BaseAddress
);
319 /* Initialize the softx87 FPU emulator*/
320 if(!softx87_init(&FpuEmulatorContext
, SX87_FPULEVEL_8087
))
322 softx86_free(&EmulatorContext
);
323 HeapFree(GetProcessHeap(), 0, BaseAddress
);
327 /* Set memory read/write callbacks */
328 EmulatorContext
.callbacks
->on_read_memory
= EmulatorReadMemory
;
329 EmulatorContext
.callbacks
->on_write_memory
= EmulatorWriteMemory
;
331 /* Set MMIO read/write callbacks */
332 EmulatorContext
.callbacks
->on_read_io
= EmulatorReadIo
;
333 EmulatorContext
.callbacks
->on_write_io
= EmulatorWriteIo
;
335 /* Set interrupt callbacks */
336 EmulatorContext
.callbacks
->on_sw_int
= EmulatorSoftwareInt
;
337 EmulatorContext
.callbacks
->on_hw_int
= EmulatorHardwareInt
;
338 EmulatorContext
.callbacks
->on_hw_int_ack
= EmulatorHardwareIntAck
;
340 /* Connect the emulated FPU to the emulated CPU */
341 softx87_connect_to_CPU(&EmulatorContext
, &FpuEmulatorContext
);
343 // TODO: NOT IMPLEMENTED
346 /* Enable interrupts */
347 EmulatorSetFlag(EMULATOR_FLAG_IF
);
352 VOID
EmulatorSetStack(WORD Segment
, WORD Offset
)
355 /* Call the softx86 API */
356 softx86_set_stack_ptr(&EmulatorContext
, Segment
, Offset
);
358 // TODO: NOT IMPLEMENTED
362 VOID
EmulatorExecute(WORD Segment
, WORD Offset
)
365 /* Call the softx86 API */
366 softx86_set_instruction_ptr(&EmulatorContext
, Segment
, Offset
);
368 // TODO: NOT IMPLEMENTED
372 VOID
EmulatorInterrupt(BYTE Number
)
374 LPDWORD IntVecTable
= (LPDWORD
)((ULONG_PTR
)BaseAddress
);
375 UINT Segment
, Offset
;
377 /* Get the segment and offset */
378 Segment
= HIWORD(IntVecTable
[Number
]);
379 Offset
= LOWORD(IntVecTable
[Number
]);
382 /* Call the softx86 API */
383 softx86_make_simple_interrupt_call(&EmulatorContext
, &Segment
, &Offset
);
385 UNREFERENCED_PARAMETER(Segment
);
386 UNREFERENCED_PARAMETER(Offset
);
387 // TODO: NOT IMPLEMENTED
391 VOID
EmulatorExternalInterrupt(BYTE Number
)
394 /* Call the softx86 API */
395 softx86_ext_hw_signal(&EmulatorContext
, Number
);
399 ULONG
EmulatorGetRegister(ULONG Register
)
402 if (Register
< EMULATOR_REG_ES
)
404 return EmulatorContext
.state
->general_reg
[Register
].val
;
408 return EmulatorContext
.state
->segment_reg
[Register
- EMULATOR_REG_ES
].val
;
411 return EmulatorContext
.Registers
[Register
].Long
;
415 VOID
EmulatorSetRegister(ULONG Register
, ULONG Value
)
418 if (Register
< EMULATOR_REG_CS
)
420 EmulatorContext
.state
->general_reg
[Register
].val
= Value
;
424 EmulatorContext
.state
->segment_reg
[Register
- EMULATOR_REG_ES
].val
= Value
;
427 // TODO: NOT IMPLEMENTED
431 BOOLEAN
EmulatorGetFlag(ULONG Flag
)
434 return (EmulatorContext
.state
->reg_flags
.val
& Flag
) ? TRUE
: FALSE
;
436 return (EmulatorContext
.Flags
.Long
& Flag
) ? TRUE
: FALSE
;
440 VOID
EmulatorSetFlag(ULONG Flag
)
443 EmulatorContext
.state
->reg_flags
.val
|= Flag
;
445 EmulatorContext
.Flags
.Long
|= Flag
;
449 VOID
EmulatorClearFlag(ULONG Flag
)
452 EmulatorContext
.state
->reg_flags
.val
&= ~Flag
;
454 EmulatorContext
.Flags
.Long
&= ~Flag
;
458 VOID
EmulatorStep(VOID
)
463 /* Print the current position - useful for debugging */
464 DPRINT("Executing at CS:IP = %04X:%04X\n",
465 EmulatorGetRegister(EMULATOR_REG_CS
),
466 EmulatorContext
.state
->reg_ip
);
468 Instruction
= (LPWORD
)((ULONG_PTR
)BaseAddress
469 + TO_LINEAR(EmulatorGetRegister(EMULATOR_REG_CS
),
470 EmulatorContext
.state
->reg_ip
));
472 /* Check for the BIOS operation (BOP) sequence */
473 if (Instruction
[0] == EMULATOR_BOP
)
475 /* Skip the opcodes */
476 EmulatorContext
.state
->reg_ip
+= 4;
478 /* Call the BOP handler */
479 EmulatorBop(Instruction
[1]);
482 /* Call the softx86 API */
483 if (!softx86_step(&EmulatorContext
))
486 EmulatorInterrupt(EMULATOR_EXCEPTION_INVALID_OPCODE
);
489 // TODO: NOT IMPLEMENTED
493 VOID
EmulatorCleanup(VOID
)
495 /* Free the memory allocated for the 16-bit address space */
496 if (BaseAddress
!= NULL
) HeapFree(GetProcessHeap(), 0, BaseAddress
);
499 /* Free the softx86 CPU and FPU emulator */
500 softx86_free(&EmulatorContext
);
501 softx87_free(&FpuEmulatorContext
);
505 VOID
EmulatorSetA20(BOOLEAN Enabled
)