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 *******************************************************************/
12 #include <softx86/softx86.h>
13 #include <softx86/softx87.h>
15 softx86_ctx EmulatorContext
;
16 softx87_ctx FpuEmulatorContext
;
17 static BOOLEAN A20Line
= FALSE
;
19 static VOID
EmulatorReadMemory(PVOID Context
, UINT Address
, LPBYTE Buffer
, INT Size
)
21 /* If the A20 line is disabled, mask bit 20 */
22 if (!A20Line
) Address
&= ~(1 << 20);
24 /* Make sure the requested address is valid */
25 if ((Address
+ Size
) >= MAX_ADDRESS
) return;
27 /* Are we reading some of the console video memory? */
28 if (((Address
+ Size
) >= CONSOLE_VIDEO_MEM_START
)
29 && (Address
< CONSOLE_VIDEO_MEM_END
))
31 /* Call the VDM BIOS to update the video memory */
32 BiosUpdateConsole(max(Address
, CONSOLE_VIDEO_MEM_START
),
33 min(Address
+ Size
, CONSOLE_VIDEO_MEM_END
));
36 /* Read the data from the virtual address space and store it in the buffer */
37 RtlCopyMemory(Buffer
, (LPVOID
)((ULONG_PTR
)BaseAddress
+ Address
), Size
);
40 static VOID
EmulatorWriteMemory(PVOID Context
, UINT Address
, LPBYTE Buffer
, INT Size
)
42 /* If the A20 line is disabled, mask bit 20 */
43 if (!A20Line
) Address
&= ~(1 << 20);
45 /* Make sure the requested address is valid */
46 if ((Address
+ Size
) >= MAX_ADDRESS
) return;
48 /* Make sure we don't write to the ROM area */
49 if ((Address
+ Size
) >= ROM_AREA_START
&& (Address
< ROM_AREA_END
)) return;
51 /* Read the data from the buffer and store it in the virtual address space */
52 RtlCopyMemory((LPVOID
)((ULONG_PTR
)BaseAddress
+ Address
), Buffer
, Size
);
54 /* Check if we modified the console video memory */
55 if (((Address
+ Size
) >= CONSOLE_VIDEO_MEM_START
)
56 && (Address
< CONSOLE_VIDEO_MEM_END
))
58 /* Call the VDM BIOS to update the screen */
59 BiosUpdateConsole(max(Address
, CONSOLE_VIDEO_MEM_START
),
60 min(Address
+ Size
, CONSOLE_VIDEO_MEM_END
));
64 static VOID
EmulatorReadIo(PVOID Context
, UINT Address
, LPBYTE Buffer
, INT Size
)
71 *Buffer
= PicReadCommand(Address
);
78 *Buffer
= PicReadData(Address
);
82 case PIT_DATA_PORT(0):
83 case PIT_DATA_PORT(1):
84 case PIT_DATA_PORT(2):
86 *Buffer
= PitReadData(Address
- PIT_DATA_PORT(0));
90 case PS2_CONTROL_PORT
:
92 *Buffer
= KeyboardReadStatus();
98 *Buffer
= KeyboardReadData();
104 static VOID
EmulatorWriteIo(PVOID Context
, UINT Address
, LPBYTE Buffer
, INT Size
)
110 case PIT_COMMAND_PORT
:
112 PitWriteCommand(Byte
);
116 case PIT_DATA_PORT(0):
117 case PIT_DATA_PORT(1):
118 case PIT_DATA_PORT(2):
120 PitWriteData(Address
- PIT_DATA_PORT(0), Byte
);
127 PicWriteCommand(Address
, Byte
);
131 case PIC_MASTER_DATA
:
134 PicWriteData(Address
, Byte
);
138 case PS2_CONTROL_PORT
:
140 KeyboardWriteCommand(Byte
);
146 KeyboardWriteData(Byte
);
152 static VOID
EmulatorSoftwareInt(PVOID Context
, BYTE Number
)
154 WORD StackSegment
, StackPointer
, CodeSegment
, InstructionPointer
;
157 /* Check if this is the special interrupt */
158 if (Number
== SPECIAL_INT_NUM
)
161 StackSegment
= EmulatorContext
.state
->segment_reg
[SX86_SREG_SS
].val
;
162 StackPointer
= EmulatorContext
.state
->general_reg
[SX86_REG_SP
].val
;
164 /* Get the interrupt number */
165 IntNum
= *(LPBYTE
)((ULONG_PTR
)BaseAddress
+ TO_LINEAR(StackSegment
, StackPointer
));
167 /* Move the stack pointer forward one word to skip the interrupt number */
168 StackPointer
+= sizeof(WORD
);
171 InstructionPointer
= *(LPWORD
)((ULONG_PTR
)BaseAddress
172 + TO_LINEAR(StackSegment
, StackPointer
));
173 CodeSegment
= *(LPWORD
)((ULONG_PTR
)BaseAddress
174 + TO_LINEAR(StackSegment
, StackPointer
+ sizeof(WORD
)));
176 /* Check if this was an exception */
179 /* Display a message to the user */
180 DisplayMessage(L
"Exception: %s occured at %04X:%04X",
181 ExceptionName
[IntNum
],
190 /* Check if this was an PIC IRQ */
191 if (IntNum
>= BIOS_PIC_MASTER_INT
&& IntNum
< BIOS_PIC_MASTER_INT
+ 8)
193 /* It was an IRQ from the master PIC */
194 BiosHandleIrq(IntNum
- BIOS_PIC_MASTER_INT
);
196 else if (IntNum
>= BIOS_PIC_SLAVE_INT
&& IntNum
< BIOS_PIC_SLAVE_INT
+ 8)
198 /* It was an IRQ from the slave PIC */
199 BiosHandleIrq(IntNum
- BIOS_PIC_SLAVE_INT
+ 8);
204 case VIDEO_BIOS_INTERRUPT
:
206 /* This is the video BIOS interrupt, call the BIOS */
212 DosInt20h(CodeSegment
);
217 DosInt21h(CodeSegment
);
229 static VOID
EmulatorHardwareInt(PVOID Context
, BYTE Number
)
234 static VOID
EmulatorHardwareIntAck(PVOID Context
, BYTE Number
)
239 /* PUBLIC FUNCTIONS ***********************************************************/
241 BOOLEAN
EmulatorInitialize()
243 /* Allocate memory for the 16-bit address space */
244 BaseAddress
= HeapAlloc(GetProcessHeap(), 0, MAX_ADDRESS
);
245 if (BaseAddress
== NULL
) return FALSE
;
247 /* Initialize the softx86 CPU emulator */
248 if (!softx86_init(&EmulatorContext
, SX86_CPULEVEL_80286
))
250 HeapFree(GetProcessHeap(), 0, BaseAddress
);
254 /* Initialize the softx87 FPU emulator*/
255 if(!softx87_init(&FpuEmulatorContext
, SX87_FPULEVEL_8087
))
257 softx86_free(&EmulatorContext
);
258 HeapFree(GetProcessHeap(), 0, BaseAddress
);
262 /* Set memory read/write callbacks */
263 EmulatorContext
.callbacks
->on_read_memory
= EmulatorReadMemory
;
264 EmulatorContext
.callbacks
->on_write_memory
= EmulatorWriteMemory
;
266 /* Set MMIO read/write callbacks */
267 EmulatorContext
.callbacks
->on_read_io
= EmulatorReadIo
;
268 EmulatorContext
.callbacks
->on_write_io
= EmulatorWriteIo
;
270 /* Set interrupt callbacks */
271 EmulatorContext
.callbacks
->on_sw_int
= EmulatorSoftwareInt
;
272 EmulatorContext
.callbacks
->on_hw_int
= EmulatorHardwareInt
;
273 EmulatorContext
.callbacks
->on_hw_int_ack
= EmulatorHardwareIntAck
;
275 /* Connect the emulated FPU to the emulated CPU */
276 softx87_connect_to_CPU(&EmulatorContext
, &FpuEmulatorContext
);
278 /* Enable interrupts */
279 EmulatorSetFlag(EMULATOR_FLAG_IF
);
284 VOID
EmulatorSetStack(WORD Segment
, WORD Offset
)
286 /* Call the softx86 API */
287 softx86_set_stack_ptr(&EmulatorContext
, Segment
, Offset
);
290 VOID
EmulatorExecute(WORD Segment
, WORD Offset
)
292 /* Call the softx86 API */
293 softx86_set_instruction_ptr(&EmulatorContext
, Segment
, Offset
);
296 VOID
EmulatorInterrupt(BYTE Number
)
298 LPDWORD IntVecTable
= (LPDWORD
)((ULONG_PTR
)BaseAddress
);
299 UINT Segment
, Offset
;
301 /* Get the segment and offset */
302 Segment
= HIWORD(IntVecTable
[Number
]);
303 Offset
= LOWORD(IntVecTable
[Number
]);
305 /* Call the softx86 API */
306 softx86_make_simple_interrupt_call(&EmulatorContext
, &Segment
, &Offset
);
309 VOID
EmulatorExternalInterrupt(BYTE Number
)
311 /* Call the softx86 API */
312 softx86_ext_hw_signal(&EmulatorContext
, Number
);
315 ULONG
EmulatorGetRegister(ULONG Register
)
317 if (Register
< EMULATOR_REG_ES
)
319 return EmulatorContext
.state
->general_reg
[Register
].val
;
323 return EmulatorContext
.state
->segment_reg
[Register
- EMULATOR_REG_ES
].val
;
327 VOID
EmulatorSetRegister(ULONG Register
, ULONG Value
)
329 if (Register
< EMULATOR_REG_CS
)
331 EmulatorContext
.state
->general_reg
[Register
].val
= Value
;
335 EmulatorContext
.state
->segment_reg
[Register
- EMULATOR_REG_ES
].val
= Value
;
339 BOOLEAN
EmulatorGetFlag(ULONG Flag
)
341 return (EmulatorContext
.state
->reg_flags
.val
& Flag
);
344 VOID
EmulatorSetFlag(ULONG Flag
)
346 EmulatorContext
.state
->reg_flags
.val
|= Flag
;
349 VOID
EmulatorClearFlag(ULONG Flag
)
351 EmulatorContext
.state
->reg_flags
.val
&= ~Flag
;
356 /* Call the softx86 API */
357 if (!softx86_step(&EmulatorContext
))
360 EmulatorInterrupt(EMULATOR_EXCEPTION_INVALID_OPCODE
);
364 VOID
EmulatorCleanup()
366 /* Free the memory allocated for the 16-bit address space */
367 if (BaseAddress
!= NULL
) HeapFree(GetProcessHeap(), 0, BaseAddress
);
369 /* Free the softx86 CPU and FPU emulator */
370 softx86_free(&EmulatorContext
);
371 softx87_free(&FpuEmulatorContext
);
374 VOID
EmulatorSetA20(BOOLEAN Enabled
)