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 **********************************************************/
23 FAST486_STATE EmulatorContext
;
25 static BOOLEAN A20Line
= FALSE
;
27 /* PRIVATE FUNCTIONS **********************************************************/
29 static VOID WINAPI
EmulatorReadMemory(PFAST486_STATE State
, ULONG Address
, PVOID Buffer
, ULONG Size
)
31 UNREFERENCED_PARAMETER(State
);
33 /* If the A20 line is disabled, mask bit 20 */
34 if (!A20Line
) Address
&= ~(1 << 20);
36 /* Make sure the requested address is valid */
37 if ((Address
+ Size
) >= MAX_ADDRESS
) return;
39 /* Read the data from the virtual address space and store it in the buffer */
40 RtlCopyMemory(Buffer
, (LPVOID
)((ULONG_PTR
)BaseAddress
+ Address
), Size
);
42 /* Check if we modified the console video memory */
43 if (((Address
+ Size
) >= VgaGetVideoBaseAddress())
44 && (Address
< VgaGetVideoLimitAddress()))
46 DWORD VgaAddress
= max(Address
, VgaGetVideoBaseAddress());
47 LPBYTE VgaBuffer
= (LPBYTE
)((ULONG_PTR
)Buffer
+ VgaAddress
- Address
);
49 /* Read from the VGA memory */
50 VgaReadMemory(VgaAddress
, VgaBuffer
, Size
);
54 static VOID WINAPI
EmulatorWriteMemory(PFAST486_STATE State
, ULONG Address
, PVOID Buffer
, ULONG Size
)
56 UNREFERENCED_PARAMETER(State
);
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
) >= VgaGetVideoBaseAddress())
72 && (Address
< VgaGetVideoLimitAddress()))
74 DWORD VgaAddress
= max(Address
, VgaGetVideoBaseAddress());
75 LPBYTE VgaBuffer
= (LPBYTE
)((ULONG_PTR
)Buffer
+ VgaAddress
- Address
);
77 /* Write to the VGA memory */
78 VgaWriteMemory(VgaAddress
, VgaBuffer
, Size
);
82 static VOID WINAPI
EmulatorReadIo(PFAST486_STATE State
, ULONG Port
, PVOID Buffer
, ULONG Size
)
84 LPBYTE Address
= (LPBYTE
)Buffer
;
86 UNREFERENCED_PARAMETER(State
);
87 UNREFERENCED_PARAMETER(Size
);
94 *Address
= PicReadCommand(Port
);
101 *Address
= PicReadData(Port
);
105 case PIT_DATA_PORT(0):
106 case PIT_DATA_PORT(1):
107 case PIT_DATA_PORT(2):
109 *Address
= PitReadData(Port
- PIT_DATA_PORT(0));
113 case PS2_CONTROL_PORT
:
115 *Address
= KeyboardReadStatus();
121 *Address
= KeyboardReadData();
129 case VGA_DAC_READ_INDEX
:
130 case VGA_DAC_WRITE_INDEX
:
141 *Address
= VgaReadPort(Port
);
147 DPRINT1("Read from unknown port: 0x%X\n", Port
);
152 static VOID WINAPI
EmulatorWriteIo(PFAST486_STATE State
, ULONG Port
, PVOID Buffer
, ULONG Size
)
154 BYTE Byte
= *(LPBYTE
)Buffer
;
156 UNREFERENCED_PARAMETER(State
);
157 UNREFERENCED_PARAMETER(Size
);
161 case PIT_COMMAND_PORT
:
163 PitWriteCommand(Byte
);
167 case PIT_DATA_PORT(0):
168 case PIT_DATA_PORT(1):
169 case PIT_DATA_PORT(2):
171 PitWriteData(Port
- PIT_DATA_PORT(0), Byte
);
178 PicWriteCommand(Port
, Byte
);
182 case PIC_MASTER_DATA
:
185 PicWriteData(Port
, Byte
);
189 case PS2_CONTROL_PORT
:
191 KeyboardWriteCommand(Byte
);
197 KeyboardWriteData(Byte
);
205 case VGA_DAC_READ_INDEX
:
206 case VGA_DAC_WRITE_INDEX
:
217 VgaWritePort(Port
, Byte
);
223 DPRINT1("Write to unknown port: 0x%X\n", Port
);
228 static VOID WINAPI
EmulatorBiosOperation(PFAST486_STATE State
, USHORT BopCode
)
230 WORD StackSegment
, StackPointer
, CodeSegment
, InstructionPointer
;
235 StackSegment
= State
->SegmentRegs
[FAST486_REG_SS
].Selector
;
236 StackPointer
= State
->GeneralRegs
[FAST486_REG_ESP
].LowWord
;
239 Stack
= (LPWORD
)((ULONG_PTR
)BaseAddress
+ TO_LINEAR(StackSegment
, StackPointer
));
241 if (BopCode
== 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 UCHAR WINAPI
EmulatorIntAcknowledge(PFAST486_STATE State
)
336 UNREFERENCED_PARAMETER(State
);
338 /* Get the interrupt number from the PIC */
339 return PicGetInterrupt();
342 /* PUBLIC FUNCTIONS ***********************************************************/
344 BOOLEAN
EmulatorInitialize()
346 /* Allocate memory for the 16-bit address space */
347 BaseAddress
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, MAX_ADDRESS
);
348 if (BaseAddress
== NULL
) return FALSE
;
350 /* Set the callbacks */
351 EmulatorContext
.MemReadCallback
= EmulatorReadMemory
;
352 EmulatorContext
.MemWriteCallback
= EmulatorWriteMemory
;
353 EmulatorContext
.IoReadCallback
= EmulatorReadIo
;
354 EmulatorContext
.IoWriteCallback
= EmulatorWriteIo
;
355 EmulatorContext
.BopCallback
= EmulatorBiosOperation
;
356 EmulatorContext
.IntAckCallback
= EmulatorIntAcknowledge
;
359 Fast486Reset(&EmulatorContext
);
361 /* Enable interrupts */
362 EmulatorSetFlag(EMULATOR_FLAG_IF
);
367 VOID
EmulatorSetStack(WORD Segment
, DWORD Offset
)
369 Fast486SetStack(&EmulatorContext
, Segment
, Offset
);
372 // FIXME: This function assumes 16-bit mode!!!
373 VOID
EmulatorExecute(WORD Segment
, WORD Offset
)
375 /* Tell Fast486 to move the instruction pointer */
376 Fast486ExecuteAt(&EmulatorContext
, Segment
, Offset
);
379 VOID
EmulatorInterrupt(BYTE Number
)
381 /* Call the Fast486 API */
382 Fast486Interrupt(&EmulatorContext
, Number
);
385 VOID
EmulatorInterruptSignal(VOID
)
387 /* Call the Fast486 API */
388 Fast486InterruptSignal(&EmulatorContext
);
391 ULONG
EmulatorGetRegister(ULONG Register
)
393 if (Register
< EMULATOR_REG_ES
)
395 return EmulatorContext
.GeneralRegs
[Register
].Long
;
399 return EmulatorContext
.SegmentRegs
[Register
- EMULATOR_REG_ES
].Selector
;
403 ULONG
EmulatorGetProgramCounter(VOID
)
405 return EmulatorContext
.InstPtr
.Long
;
408 VOID
EmulatorSetRegister(ULONG Register
, ULONG Value
)
410 if (Register
< EMULATOR_REG_ES
)
412 EmulatorContext
.GeneralRegs
[Register
].Long
= Value
;
416 Fast486SetSegment(&EmulatorContext
, Register
- EMULATOR_REG_ES
, (USHORT
)Value
);
420 BOOLEAN
EmulatorGetFlag(ULONG Flag
)
422 return (EmulatorContext
.Flags
.Long
& Flag
) ? TRUE
: FALSE
;
425 VOID
EmulatorSetFlag(ULONG Flag
)
427 EmulatorContext
.Flags
.Long
|= Flag
;
430 VOID
EmulatorClearFlag(ULONG Flag
)
432 EmulatorContext
.Flags
.Long
&= ~Flag
;
435 VOID
EmulatorStep(VOID
)
437 /* Dump the state for debugging purposes */
438 // Fast486DumpState(&EmulatorContext);
440 /* Execute the next instruction */
441 Fast486StepInto(&EmulatorContext
);
444 VOID
EmulatorCleanup(VOID
)
446 /* Free the memory allocated for the 16-bit address space */
447 if (BaseAddress
!= NULL
) HeapFree(GetProcessHeap(), 0, BaseAddress
);
450 VOID
EmulatorSetA20(BOOLEAN Enabled
)