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 *******************************************************************/
18 /* PRIVATE VARIABLES **********************************************************/
20 static softx86_ctx EmulatorContext
;
21 static softx87_ctx FpuEmulatorContext
;
22 static BOOLEAN A20Line
= FALSE
;
24 /* PRIVATE FUNCTIONS **********************************************************/
26 static VOID
EmulatorReadMemory(PVOID Context
, UINT Address
, LPBYTE Buffer
, INT Size
)
28 /* If the A20 line is disabled, mask bit 20 */
29 if (!A20Line
) Address
&= ~(1 << 20);
31 /* Make sure the requested address is valid */
32 if ((Address
+ Size
) >= MAX_ADDRESS
) return;
34 /* Are we reading some of the console video memory? */
35 if (((Address
+ Size
) >= CONSOLE_VIDEO_MEM_START
)
36 && (Address
< CONSOLE_VIDEO_MEM_END
))
38 /* Call the VDM BIOS to update the video memory */
39 BiosUpdateConsole(max(Address
, CONSOLE_VIDEO_MEM_START
),
40 min(Address
+ Size
, CONSOLE_VIDEO_MEM_END
));
43 /* Read the data from the virtual address space and store it in the buffer */
44 RtlCopyMemory(Buffer
, (LPVOID
)((ULONG_PTR
)BaseAddress
+ Address
), Size
);
47 static VOID
EmulatorWriteMemory(PVOID Context
, UINT Address
, LPBYTE Buffer
, INT Size
)
49 /* If the A20 line is disabled, mask bit 20 */
50 if (!A20Line
) Address
&= ~(1 << 20);
52 /* Make sure the requested address is valid */
53 if ((Address
+ Size
) >= MAX_ADDRESS
) return;
55 /* Make sure we don't write to the ROM area */
56 if ((Address
+ Size
) >= ROM_AREA_START
&& (Address
< ROM_AREA_END
)) return;
58 /* Read the data from the buffer and store it in the virtual address space */
59 RtlCopyMemory((LPVOID
)((ULONG_PTR
)BaseAddress
+ Address
), Buffer
, Size
);
61 /* Check if we modified the console video memory */
62 if (((Address
+ Size
) >= CONSOLE_VIDEO_MEM_START
)
63 && (Address
< CONSOLE_VIDEO_MEM_END
))
65 /* Call the VDM BIOS to update the screen */
66 BiosUpdateConsole(max(Address
, CONSOLE_VIDEO_MEM_START
),
67 min(Address
+ Size
, CONSOLE_VIDEO_MEM_END
));
71 static VOID
EmulatorReadIo(PVOID Context
, UINT Address
, LPBYTE Buffer
, INT Size
)
78 *Buffer
= PicReadCommand(Address
);
85 *Buffer
= PicReadData(Address
);
89 case PIT_DATA_PORT(0):
90 case PIT_DATA_PORT(1):
91 case PIT_DATA_PORT(2):
93 *Buffer
= PitReadData(Address
- PIT_DATA_PORT(0));
97 case PS2_CONTROL_PORT
:
99 *Buffer
= KeyboardReadStatus();
105 *Buffer
= KeyboardReadData();
111 static VOID
EmulatorWriteIo(PVOID Context
, UINT Address
, LPBYTE Buffer
, INT Size
)
117 case PIT_COMMAND_PORT
:
119 PitWriteCommand(Byte
);
123 case PIT_DATA_PORT(0):
124 case PIT_DATA_PORT(1):
125 case PIT_DATA_PORT(2):
127 PitWriteData(Address
- PIT_DATA_PORT(0), Byte
);
134 PicWriteCommand(Address
, Byte
);
138 case PIC_MASTER_DATA
:
141 PicWriteData(Address
, Byte
);
145 case PS2_CONTROL_PORT
:
147 KeyboardWriteCommand(Byte
);
153 KeyboardWriteData(Byte
);
159 static VOID
EmulatorSoftwareInt(PVOID Context
, BYTE Number
)
161 WORD StackSegment
, StackPointer
, CodeSegment
, InstructionPointer
;
164 /* Check if this is the special interrupt */
165 if (Number
== SPECIAL_INT_NUM
)
168 StackSegment
= EmulatorContext
.state
->segment_reg
[SX86_SREG_SS
].val
;
169 StackPointer
= EmulatorContext
.state
->general_reg
[SX86_REG_SP
].val
;
171 /* Get the interrupt number */
172 IntNum
= *(LPBYTE
)((ULONG_PTR
)BaseAddress
+ TO_LINEAR(StackSegment
, StackPointer
));
174 /* Move the stack pointer forward one word to skip the interrupt number */
175 StackPointer
+= sizeof(WORD
);
178 InstructionPointer
= *(LPWORD
)((ULONG_PTR
)BaseAddress
179 + TO_LINEAR(StackSegment
, StackPointer
));
180 CodeSegment
= *(LPWORD
)((ULONG_PTR
)BaseAddress
181 + TO_LINEAR(StackSegment
, StackPointer
+ sizeof(WORD
)));
183 /* Check if this was an exception */
186 /* Display a message to the user */
187 DisplayMessage(L
"Exception: %s occured at %04X:%04X",
188 ExceptionName
[IntNum
],
197 /* Check if this was an PIC IRQ */
198 if (IntNum
>= BIOS_PIC_MASTER_INT
&& IntNum
< BIOS_PIC_MASTER_INT
+ 8)
200 /* It was an IRQ from the master PIC */
201 BiosHandleIrq(IntNum
- BIOS_PIC_MASTER_INT
);
204 else if (IntNum
>= BIOS_PIC_SLAVE_INT
&& IntNum
< BIOS_PIC_SLAVE_INT
+ 8)
206 /* It was an IRQ from the slave PIC */
207 BiosHandleIrq(IntNum
- BIOS_PIC_SLAVE_INT
+ 8);
213 case BIOS_VIDEO_INTERRUPT
:
215 /* This is the video BIOS interrupt, call the BIOS */
219 case BIOS_EQUIPMENT_INTERRUPT
:
221 /* This is the BIOS "get equipment" command, call the BIOS */
222 BiosEquipmentService();
225 case BIOS_KBD_INTERRUPT
:
227 /* This is the keyboard BIOS interrupt, call the BIOS */
228 BiosKeyboardService();
231 case BIOS_TIME_INTERRUPT
:
233 /* This is the time BIOS interrupt, call the BIOS */
239 DosInt20h(CodeSegment
);
244 DosInt21h(CodeSegment
);
254 DPRINT1("Unhandled interrupt: 0x%02X\n", IntNum
);
261 static VOID
EmulatorHardwareInt(PVOID Context
, BYTE Number
)
266 static VOID
EmulatorHardwareIntAck(PVOID Context
, BYTE Number
)
271 /* PUBLIC FUNCTIONS ***********************************************************/
273 BOOLEAN
EmulatorInitialize()
275 /* Allocate memory for the 16-bit address space */
276 BaseAddress
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, MAX_ADDRESS
);
277 if (BaseAddress
== NULL
) return FALSE
;
279 /* Initialize the softx86 CPU emulator */
280 if (!softx86_init(&EmulatorContext
, SX86_CPULEVEL_80286
))
282 HeapFree(GetProcessHeap(), 0, BaseAddress
);
286 /* Initialize the softx87 FPU emulator*/
287 if(!softx87_init(&FpuEmulatorContext
, SX87_FPULEVEL_8087
))
289 softx86_free(&EmulatorContext
);
290 HeapFree(GetProcessHeap(), 0, BaseAddress
);
294 /* Set memory read/write callbacks */
295 EmulatorContext
.callbacks
->on_read_memory
= EmulatorReadMemory
;
296 EmulatorContext
.callbacks
->on_write_memory
= EmulatorWriteMemory
;
298 /* Set MMIO read/write callbacks */
299 EmulatorContext
.callbacks
->on_read_io
= EmulatorReadIo
;
300 EmulatorContext
.callbacks
->on_write_io
= EmulatorWriteIo
;
302 /* Set interrupt callbacks */
303 EmulatorContext
.callbacks
->on_sw_int
= EmulatorSoftwareInt
;
304 EmulatorContext
.callbacks
->on_hw_int
= EmulatorHardwareInt
;
305 EmulatorContext
.callbacks
->on_hw_int_ack
= EmulatorHardwareIntAck
;
307 /* Connect the emulated FPU to the emulated CPU */
308 softx87_connect_to_CPU(&EmulatorContext
, &FpuEmulatorContext
);
310 /* Enable interrupts */
311 EmulatorSetFlag(EMULATOR_FLAG_IF
);
316 VOID
EmulatorSetStack(WORD Segment
, WORD Offset
)
318 /* Call the softx86 API */
319 softx86_set_stack_ptr(&EmulatorContext
, Segment
, Offset
);
322 VOID
EmulatorExecute(WORD Segment
, WORD Offset
)
324 /* Call the softx86 API */
325 softx86_set_instruction_ptr(&EmulatorContext
, Segment
, Offset
);
328 VOID
EmulatorInterrupt(BYTE Number
)
330 LPDWORD IntVecTable
= (LPDWORD
)((ULONG_PTR
)BaseAddress
);
331 UINT Segment
, Offset
;
333 /* Get the segment and offset */
334 Segment
= HIWORD(IntVecTable
[Number
]);
335 Offset
= LOWORD(IntVecTable
[Number
]);
337 /* Call the softx86 API */
338 softx86_make_simple_interrupt_call(&EmulatorContext
, &Segment
, &Offset
);
341 VOID
EmulatorExternalInterrupt(BYTE Number
)
343 /* Call the softx86 API */
344 softx86_ext_hw_signal(&EmulatorContext
, Number
);
347 ULONG
EmulatorGetRegister(ULONG Register
)
349 if (Register
< EMULATOR_REG_ES
)
351 return EmulatorContext
.state
->general_reg
[Register
].val
;
355 return EmulatorContext
.state
->segment_reg
[Register
- EMULATOR_REG_ES
].val
;
359 VOID
EmulatorSetRegister(ULONG Register
, ULONG Value
)
361 if (Register
< EMULATOR_REG_CS
)
363 EmulatorContext
.state
->general_reg
[Register
].val
= Value
;
367 EmulatorContext
.state
->segment_reg
[Register
- EMULATOR_REG_ES
].val
= Value
;
371 BOOLEAN
EmulatorGetFlag(ULONG Flag
)
373 return (EmulatorContext
.state
->reg_flags
.val
& Flag
);
376 VOID
EmulatorSetFlag(ULONG Flag
)
378 EmulatorContext
.state
->reg_flags
.val
|= Flag
;
381 VOID
EmulatorClearFlag(ULONG Flag
)
383 EmulatorContext
.state
->reg_flags
.val
&= ~Flag
;
388 /* Call the softx86 API */
389 if (!softx86_step(&EmulatorContext
))
392 EmulatorInterrupt(EMULATOR_EXCEPTION_INVALID_OPCODE
);
396 VOID
EmulatorCleanup()
398 /* Free the memory allocated for the 16-bit address space */
399 if (BaseAddress
!= NULL
) HeapFree(GetProcessHeap(), 0, BaseAddress
);
401 /* Free the softx86 CPU and FPU emulator */
402 softx86_free(&EmulatorContext
);
403 softx87_free(&FpuEmulatorContext
);
406 VOID
EmulatorSetA20(BOOLEAN Enabled
)