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 NTVDMCALL
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 NTVDMCALL
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 NTVDMCALL
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 NTVDMCALL
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
);
231 static VOID
EmulatorBop(WORD Code
)
233 WORD StackSegment
, StackPointer
, CodeSegment
, InstructionPointer
;
239 StackSegment
= EmulatorContext
.state
->segment_reg
[SX86_SREG_SS
].val
;
240 StackPointer
= EmulatorContext
.state
->general_reg
[SX86_REG_SP
].val
;
242 StackSegment
= EmulatorContext
.SegmentRegs
[SOFT386_REG_SS
].Selector
;
243 StackPointer
= EmulatorContext
.GeneralRegs
[SOFT386_REG_ESP
].LowWord
;
247 Stack
= (LPWORD
)((ULONG_PTR
)BaseAddress
+ TO_LINEAR(StackSegment
, StackPointer
));
249 if (Code
== EMULATOR_INT_BOP
)
251 /* Get the interrupt number */
252 IntNum
= LOBYTE(Stack
[STACK_INT_NUM
]);
255 InstructionPointer
= Stack
[STACK_IP
];
256 CodeSegment
= Stack
[STACK_CS
];
258 /* Check if this was an exception */
261 /* Display a message to the user */
262 DisplayMessage(L
"Exception: %s occured at %04X:%04X",
263 ExceptionName
[IntNum
],
272 /* Check if this was an PIC IRQ */
273 if (IntNum
>= BIOS_PIC_MASTER_INT
&& IntNum
< BIOS_PIC_MASTER_INT
+ 8)
275 /* It was an IRQ from the master PIC */
276 BiosHandleIrq(IntNum
- BIOS_PIC_MASTER_INT
, Stack
);
279 else if (IntNum
>= BIOS_PIC_SLAVE_INT
&& IntNum
< BIOS_PIC_SLAVE_INT
+ 8)
281 /* It was an IRQ from the slave PIC */
282 BiosHandleIrq(IntNum
- BIOS_PIC_SLAVE_INT
+ 8, Stack
);
288 case BIOS_VIDEO_INTERRUPT
:
290 /* This is the video BIOS interrupt, call the BIOS */
291 BiosVideoService(Stack
);
294 case BIOS_EQUIPMENT_INTERRUPT
:
296 /* This is the BIOS "get equipment" command, call the BIOS */
297 BiosEquipmentService(Stack
);
300 case BIOS_KBD_INTERRUPT
:
302 /* This is the keyboard BIOS interrupt, call the BIOS */
303 BiosKeyboardService(Stack
);
306 case BIOS_TIME_INTERRUPT
:
308 /* This is the time BIOS interrupt, call the BIOS */
309 BiosTimeService(Stack
);
312 case BIOS_SYS_TIMER_INTERRUPT
:
314 /* BIOS timer update */
315 BiosSystemTimerInterrupt(Stack
);
330 DosBreakInterrupt(Stack
);
335 DPRINT1("Unhandled interrupt: 0x%02X\n", IntNum
);
343 static VOID WINAPI
EmulatorBiosOperation(PSOFT386_STATE State
, WORD Code
)
346 * HACK: To maintain softx86 compatbility, just call the old EmulatorBop here.
347 * Later on, when softx86 is no longer needed, the code from EmulatorBop should
348 * be moved here and should use the "State" variable.
357 static VOID
EmulatorSoftwareInt(PVOID Context
, BYTE Number
)
359 UNREFERENCED_PARAMETER(Context
);
360 UNREFERENCED_PARAMETER(Number
);
365 static VOID
EmulatorHardwareInt(PVOID Context
, BYTE Number
)
367 UNREFERENCED_PARAMETER(Context
);
368 UNREFERENCED_PARAMETER(Number
);
373 static VOID
EmulatorHardwareIntAck(PVOID Context
, BYTE Number
)
375 UNREFERENCED_PARAMETER(Context
);
376 UNREFERENCED_PARAMETER(Number
);
383 /* PUBLIC FUNCTIONS ***********************************************************/
385 BOOLEAN
EmulatorInitialize()
387 /* Allocate memory for the 16-bit address space */
388 BaseAddress
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, MAX_ADDRESS
);
389 if (BaseAddress
== NULL
) return FALSE
;
392 /* Initialize the softx86 CPU emulator */
393 if (!softx86_init(&EmulatorContext
, SX86_CPULEVEL_80286
))
395 HeapFree(GetProcessHeap(), 0, BaseAddress
);
399 /* Initialize the softx87 FPU emulator*/
400 if(!softx87_init(&FpuEmulatorContext
, SX87_FPULEVEL_8087
))
402 softx86_free(&EmulatorContext
);
403 HeapFree(GetProcessHeap(), 0, BaseAddress
);
407 /* Set memory read/write callbacks */
408 EmulatorContext
.callbacks
->on_read_memory
= EmulatorReadMemory
;
409 EmulatorContext
.callbacks
->on_write_memory
= EmulatorWriteMemory
;
411 /* Set MMIO read/write callbacks */
412 EmulatorContext
.callbacks
->on_read_io
= EmulatorReadIo
;
413 EmulatorContext
.callbacks
->on_write_io
= EmulatorWriteIo
;
415 /* Set interrupt callbacks */
416 EmulatorContext
.callbacks
->on_sw_int
= EmulatorSoftwareInt
;
417 EmulatorContext
.callbacks
->on_hw_int
= EmulatorHardwareInt
;
418 EmulatorContext
.callbacks
->on_hw_int_ack
= EmulatorHardwareIntAck
;
420 /* Connect the emulated FPU to the emulated CPU */
421 softx87_connect_to_CPU(&EmulatorContext
, &FpuEmulatorContext
);
423 /* Set the callbacks */
424 EmulatorContext
.MemReadCallback
= (SOFT386_MEM_READ_PROC
)EmulatorReadMemory
;
425 EmulatorContext
.MemWriteCallback
= (SOFT386_MEM_WRITE_PROC
)EmulatorWriteMemory
;
426 EmulatorContext
.IoReadCallback
= (SOFT386_IO_READ_PROC
)EmulatorReadIo
;
427 EmulatorContext
.IoWriteCallback
= (SOFT386_IO_WRITE_PROC
)EmulatorWriteIo
;
428 EmulatorContext
.BopCallback
= (SOFT386_BOP_PROC
)EmulatorBiosOperation
;
431 Soft386Reset(&EmulatorContext
);
434 /* Enable interrupts */
435 EmulatorSetFlag(EMULATOR_FLAG_IF
);
440 VOID
EmulatorSetStack(WORD Segment
, DWORD Offset
)
443 /* Call the softx86 API */
444 softx86_set_stack_ptr(&EmulatorContext
, Segment
, Offset
);
446 Soft386SetStack(&EmulatorContext
, Segment
, Offset
);
450 // FIXME: This function assumes 16-bit mode!!!
451 VOID
EmulatorExecute(WORD Segment
, WORD Offset
)
454 /* Call the softx86 API */
455 softx86_set_instruction_ptr(&EmulatorContext
, Segment
, Offset
);
457 /* Tell Soft386 to move the instruction pointer */
458 Soft386ExecuteAt(&EmulatorContext
, Segment
, Offset
);
462 VOID
EmulatorInterrupt(BYTE Number
)
465 LPDWORD IntVecTable
= (LPDWORD
)((ULONG_PTR
)BaseAddress
);
466 UINT Segment
, Offset
;
468 /* Get the segment and offset */
469 Segment
= HIWORD(IntVecTable
[Number
]);
470 Offset
= LOWORD(IntVecTable
[Number
]);
472 /* Call the softx86 API */
473 softx86_make_simple_interrupt_call(&EmulatorContext
, &Segment
, &Offset
);
475 /* Call the Soft386 API */
476 Soft386Interrupt(&EmulatorContext
, Number
);
480 VOID
EmulatorExternalInterrupt(BYTE Number
)
483 /* Call the softx86 API */
484 softx86_ext_hw_signal(&EmulatorContext
, Number
);
486 /* Call the Soft386 API */
487 Soft386Interrupt(&EmulatorContext
, Number
);
491 ULONG
EmulatorGetRegister(ULONG Register
)
494 if (Register
< EMULATOR_REG_ES
)
496 return EmulatorContext
.state
->general_reg
[Register
].val
;
500 return EmulatorContext
.state
->segment_reg
[Register
- EMULATOR_REG_ES
].val
;
503 if (Register
< EMULATOR_REG_ES
)
505 return EmulatorContext
.GeneralRegs
[Register
].Long
;
509 return EmulatorContext
.SegmentRegs
[Register
- EMULATOR_REG_ES
].Selector
;
514 ULONG
EmulatorGetProgramCounter(VOID
)
517 return EmulatorContext
.state
->reg_ip
;
519 return EmulatorContext
.InstPtr
.Long
;
523 VOID
EmulatorSetRegister(ULONG Register
, ULONG Value
)
526 if (Register
< EMULATOR_REG_ES
)
528 EmulatorContext
.state
->general_reg
[Register
].val
= Value
;
532 EmulatorContext
.state
->segment_reg
[Register
- EMULATOR_REG_ES
].val
= (USHORT
)Value
;
535 if (Register
< EMULATOR_REG_ES
)
537 EmulatorContext
.GeneralRegs
[Register
].Long
= Value
;
541 Soft386SetSegment(&EmulatorContext
, Register
- EMULATOR_REG_ES
, (USHORT
)Value
);
546 BOOLEAN
EmulatorGetFlag(ULONG Flag
)
549 return (EmulatorContext
.state
->reg_flags
.val
& Flag
) ? TRUE
: FALSE
;
551 return (EmulatorContext
.Flags
.Long
& Flag
) ? TRUE
: FALSE
;
555 VOID
EmulatorSetFlag(ULONG Flag
)
558 EmulatorContext
.state
->reg_flags
.val
|= Flag
;
560 EmulatorContext
.Flags
.Long
|= Flag
;
564 VOID
EmulatorClearFlag(ULONG Flag
)
567 EmulatorContext
.state
->reg_flags
.val
&= ~Flag
;
569 EmulatorContext
.Flags
.Long
&= ~Flag
;
573 VOID
EmulatorStep(VOID
)
578 /* Print the current position - useful for debugging */
579 DPRINT("Executing at CS:IP = %04X:%04X\n",
580 EmulatorGetRegister(EMULATOR_REG_CS
),
581 EmulatorContext
.state
->reg_ip
);
583 Instruction
= (LPWORD
)((ULONG_PTR
)BaseAddress
584 + TO_LINEAR(EmulatorGetRegister(EMULATOR_REG_CS
),
585 EmulatorContext
.state
->reg_ip
));
587 /* Check for the BIOS operation (BOP) sequence */
588 if (Instruction
[0] == EMULATOR_BOP
)
590 /* Skip the opcodes */
591 EmulatorContext
.state
->reg_ip
+= 4;
593 // HACK: Refresh the display because the called function may wait.
596 /* Call the BOP handler */
597 EmulatorBop(Instruction
[1]);
600 /* Call the softx86 API */
601 if (!softx86_step(&EmulatorContext
))
604 EmulatorInterrupt(EMULATOR_EXCEPTION_INVALID_OPCODE
);
607 /* Dump the state for debugging purposes */
608 // Soft386DumpState(&EmulatorContext);
610 /* Execute the next instruction */
611 Soft386StepInto(&EmulatorContext
);
615 VOID
EmulatorCleanup(VOID
)
618 /* Free the softx86 CPU and FPU emulator */
619 softx87_free(&FpuEmulatorContext
);
620 softx86_free(&EmulatorContext
);
623 /* Free the memory allocated for the 16-bit address space */
624 if (BaseAddress
!= NULL
) HeapFree(GetProcessHeap(), 0, BaseAddress
);
627 VOID
EmulatorSetA20(BOOLEAN Enabled
)