2013-06-17 00:00:36 +00:00
|
|
|
/*
|
|
|
|
* COPYRIGHT: GPL - See COPYING in the top level directory
|
|
|
|
* PROJECT: ReactOS Virtual DOS Machine
|
|
|
|
* FILE: emulator.c
|
|
|
|
* PURPOSE: Minimal x86 machine emulator for the VDM
|
|
|
|
* PROGRAMMERS: Aleksandar Andrejevic <theflash AT sdf DOT lonestar DOT org>
|
|
|
|
*/
|
|
|
|
|
|
|
|
/* INCLUDES *******************************************************************/
|
|
|
|
|
2013-07-22 13:51:26 +00:00
|
|
|
#define NDEBUG
|
|
|
|
|
2013-06-26 22:58:41 +00:00
|
|
|
#include "emulator.h"
|
2014-01-11 20:59:27 +00:00
|
|
|
|
2014-01-11 15:27:18 +00:00
|
|
|
#include "bios/bios.h"
|
2014-01-11 20:59:27 +00:00
|
|
|
#include "hardware/cmos.h"
|
|
|
|
#include "hardware/pic.h"
|
2014-01-25 00:21:51 +00:00
|
|
|
#include "hardware/ps2.h"
|
2014-01-11 20:59:27 +00:00
|
|
|
#include "hardware/speaker.h"
|
|
|
|
#include "hardware/timer.h"
|
|
|
|
#include "hardware/vga.h"
|
|
|
|
|
2013-11-01 02:28:47 +00:00
|
|
|
#include "bop.h"
|
2013-12-17 02:19:52 +00:00
|
|
|
#include "vddsup.h"
|
2013-11-26 20:20:51 +00:00
|
|
|
#include "io.h"
|
2013-12-17 23:03:23 +00:00
|
|
|
|
2013-06-26 22:58:41 +00:00
|
|
|
/* PRIVATE VARIABLES **********************************************************/
|
|
|
|
|
2013-10-19 16:55:51 +00:00
|
|
|
FAST486_STATE EmulatorContext;
|
2014-01-11 20:59:27 +00:00
|
|
|
LPVOID BaseAddress = NULL;
|
|
|
|
BOOLEAN VdmRunning = TRUE;
|
2013-07-07 20:53:23 +00:00
|
|
|
|
2013-06-26 17:15:45 +00:00
|
|
|
static BOOLEAN A20Line = FALSE;
|
2013-06-17 00:00:36 +00:00
|
|
|
|
2014-01-25 00:21:51 +00:00
|
|
|
LPCWSTR ExceptionName[] =
|
|
|
|
{
|
|
|
|
L"Division By Zero",
|
|
|
|
L"Debug",
|
|
|
|
L"Unexpected Error",
|
|
|
|
L"Breakpoint",
|
|
|
|
L"Integer Overflow",
|
|
|
|
L"Bound Range Exceeded",
|
|
|
|
L"Invalid Opcode",
|
|
|
|
L"FPU Not Available"
|
|
|
|
};
|
|
|
|
|
2013-12-16 23:57:35 +00:00
|
|
|
/* BOP Identifiers */
|
|
|
|
#define BOP_DEBUGGER 0x56 // Break into the debugger from a 16-bit app
|
|
|
|
|
2013-06-26 22:58:41 +00:00
|
|
|
/* PRIVATE FUNCTIONS **********************************************************/
|
|
|
|
|
2013-11-09 17:16:04 +00:00
|
|
|
VOID WINAPI EmulatorReadMemory(PFAST486_STATE State, ULONG Address, PVOID Buffer, ULONG Size)
|
2013-06-17 00:00:36 +00:00
|
|
|
{
|
2013-10-27 12:17:06 +00:00
|
|
|
UNREFERENCED_PARAMETER(State);
|
2013-08-07 19:56:28 +00:00
|
|
|
|
2013-06-26 17:15:45 +00:00
|
|
|
/* If the A20 line is disabled, mask bit 20 */
|
|
|
|
if (!A20Line) Address &= ~(1 << 20);
|
|
|
|
|
2013-06-17 00:00:36 +00:00
|
|
|
/* Make sure the requested address is valid */
|
|
|
|
if ((Address + Size) >= MAX_ADDRESS) return;
|
|
|
|
|
2013-12-23 18:09:18 +00:00
|
|
|
/*
|
|
|
|
* Check if we are going to read the VGA memory and
|
|
|
|
* copy it into the virtual address space if needed.
|
|
|
|
*/
|
2013-08-05 23:20:25 +00:00
|
|
|
if (((Address + Size) >= VgaGetVideoBaseAddress())
|
|
|
|
&& (Address < VgaGetVideoLimitAddress()))
|
|
|
|
{
|
|
|
|
DWORD VgaAddress = max(Address, VgaGetVideoBaseAddress());
|
2013-11-09 17:16:04 +00:00
|
|
|
DWORD ActualSize = min(Address + Size - 1, VgaGetVideoLimitAddress())
|
|
|
|
- VgaAddress + 1;
|
2013-12-23 18:09:18 +00:00
|
|
|
LPBYTE DestBuffer = (LPBYTE)((ULONG_PTR)BaseAddress + VgaAddress);
|
2013-08-05 23:20:25 +00:00
|
|
|
|
|
|
|
/* Read from the VGA memory */
|
2013-12-23 18:09:18 +00:00
|
|
|
VgaReadMemory(VgaAddress, DestBuffer, ActualSize);
|
2013-08-05 23:20:25 +00:00
|
|
|
}
|
2013-12-23 18:09:18 +00:00
|
|
|
|
|
|
|
/* Read the data from the virtual address space and store it in the buffer */
|
|
|
|
RtlCopyMemory(Buffer, (LPVOID)((ULONG_PTR)BaseAddress + Address), Size);
|
2013-06-17 00:00:36 +00:00
|
|
|
}
|
|
|
|
|
2013-11-09 17:16:04 +00:00
|
|
|
VOID WINAPI EmulatorWriteMemory(PFAST486_STATE State, ULONG Address, PVOID Buffer, ULONG Size)
|
2013-06-17 00:00:36 +00:00
|
|
|
{
|
2013-10-27 12:17:06 +00:00
|
|
|
UNREFERENCED_PARAMETER(State);
|
2013-08-07 19:56:28 +00:00
|
|
|
|
2013-06-26 17:15:45 +00:00
|
|
|
/* If the A20 line is disabled, mask bit 20 */
|
|
|
|
if (!A20Line) Address &= ~(1 << 20);
|
|
|
|
|
2013-06-17 00:00:36 +00:00
|
|
|
/* Make sure the requested address is valid */
|
|
|
|
if ((Address + Size) >= MAX_ADDRESS) return;
|
|
|
|
|
|
|
|
/* Make sure we don't write to the ROM area */
|
|
|
|
if ((Address + Size) >= ROM_AREA_START && (Address < ROM_AREA_END)) return;
|
|
|
|
|
|
|
|
/* Read the data from the buffer and store it in the virtual address space */
|
|
|
|
RtlCopyMemory((LPVOID)((ULONG_PTR)BaseAddress + Address), Buffer, Size);
|
|
|
|
|
2013-12-23 18:09:18 +00:00
|
|
|
/*
|
|
|
|
* Check if we modified the VGA memory.
|
|
|
|
*/
|
2013-08-05 23:20:25 +00:00
|
|
|
if (((Address + Size) >= VgaGetVideoBaseAddress())
|
|
|
|
&& (Address < VgaGetVideoLimitAddress()))
|
2013-06-17 00:00:36 +00:00
|
|
|
{
|
2013-08-05 23:20:25 +00:00
|
|
|
DWORD VgaAddress = max(Address, VgaGetVideoBaseAddress());
|
2013-11-09 17:16:04 +00:00
|
|
|
DWORD ActualSize = min(Address + Size - 1, VgaGetVideoLimitAddress())
|
|
|
|
- VgaAddress + 1;
|
2013-12-23 18:09:18 +00:00
|
|
|
LPBYTE SrcBuffer = (LPBYTE)((ULONG_PTR)BaseAddress + VgaAddress);
|
2013-08-05 23:20:25 +00:00
|
|
|
|
|
|
|
/* Write to the VGA memory */
|
2013-12-23 18:09:18 +00:00
|
|
|
VgaWriteMemory(VgaAddress, SrcBuffer, ActualSize);
|
2013-06-17 00:00:36 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-11-09 17:16:04 +00:00
|
|
|
UCHAR WINAPI EmulatorIntAcknowledge(PFAST486_STATE State)
|
2013-10-27 00:37:01 +00:00
|
|
|
{
|
|
|
|
UNREFERENCED_PARAMETER(State);
|
|
|
|
|
|
|
|
/* Get the interrupt number from the PIC */
|
|
|
|
return PicGetInterrupt();
|
|
|
|
}
|
|
|
|
|
2013-12-16 23:57:35 +00:00
|
|
|
VOID WINAPI EmulatorDebugBreak(LPWORD Stack)
|
|
|
|
{
|
|
|
|
DPRINT1("NTVDM: BOP_DEBUGGER\n");
|
|
|
|
DebugBreak();
|
|
|
|
}
|
|
|
|
|
2013-06-17 00:00:36 +00:00
|
|
|
/* PUBLIC FUNCTIONS ***********************************************************/
|
|
|
|
|
2014-01-25 00:21:51 +00:00
|
|
|
BOOLEAN EmulatorInitialize(HANDLE ConsoleInput, HANDLE ConsoleOutput)
|
2013-06-17 00:00:36 +00:00
|
|
|
{
|
|
|
|
/* Allocate memory for the 16-bit address space */
|
2013-07-02 02:08:30 +00:00
|
|
|
BaseAddress = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, MAX_ADDRESS);
|
2014-01-11 20:59:27 +00:00
|
|
|
if (BaseAddress == NULL)
|
|
|
|
{
|
|
|
|
wprintf(L"FATAL: Failed to allocate VDM memory.\n");
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Initialize I/O ports */
|
|
|
|
/* Initialize RAM */
|
2013-06-17 00:00:36 +00:00
|
|
|
|
2013-11-01 01:46:58 +00:00
|
|
|
/* Initialize the CPU */
|
|
|
|
Fast486Initialize(&EmulatorContext,
|
|
|
|
EmulatorReadMemory,
|
|
|
|
EmulatorWriteMemory,
|
|
|
|
EmulatorReadIo,
|
|
|
|
EmulatorWriteIo,
|
|
|
|
NULL,
|
|
|
|
EmulatorBiosOperation,
|
2013-11-10 22:27:24 +00:00
|
|
|
EmulatorIntAcknowledge,
|
|
|
|
NULL /* TODO: Use a TLB */);
|
2013-06-17 00:00:36 +00:00
|
|
|
|
2013-06-26 17:15:45 +00:00
|
|
|
/* Enable interrupts */
|
2013-12-12 20:09:24 +00:00
|
|
|
setIF(1);
|
2013-06-26 17:15:45 +00:00
|
|
|
|
2014-01-11 20:59:27 +00:00
|
|
|
/* Initialize the PIC, the PIT, the CMOS and the PC Speaker */
|
|
|
|
PicInitialize();
|
|
|
|
PitInitialize();
|
|
|
|
CmosInitialize();
|
|
|
|
SpeakerInitialize();
|
2013-12-17 02:19:52 +00:00
|
|
|
|
2014-01-25 00:21:51 +00:00
|
|
|
/* Initialize the PS2 port */
|
|
|
|
PS2Initialize(ConsoleInput);
|
|
|
|
|
|
|
|
/* Set the console input mode */
|
|
|
|
// SetConsoleMode(ConsoleInput, ENABLE_MOUSE_INPUT | ENABLE_PROCESSED_INPUT);
|
|
|
|
|
|
|
|
/* Initialize the VGA */
|
|
|
|
// if (!VgaInitialize(ConsoleOutput)) return FALSE;
|
|
|
|
VgaInitialize(ConsoleOutput);
|
|
|
|
|
2013-12-16 23:57:35 +00:00
|
|
|
/* Register the DebugBreak BOP */
|
|
|
|
RegisterBop(BOP_DEBUGGER, EmulatorDebugBreak);
|
|
|
|
|
2014-01-11 20:59:27 +00:00
|
|
|
/* Initialize VDD support */
|
|
|
|
VDDSupInitialize();
|
|
|
|
|
2013-06-17 00:00:36 +00:00
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
2013-11-01 01:46:58 +00:00
|
|
|
VOID EmulatorCleanup(VOID)
|
|
|
|
{
|
2014-01-25 00:21:51 +00:00
|
|
|
// VgaCleanup();
|
|
|
|
PS2Cleanup();
|
|
|
|
|
2014-01-11 20:59:27 +00:00
|
|
|
SpeakerCleanup();
|
|
|
|
CmosCleanup();
|
|
|
|
// PitCleanup();
|
|
|
|
// PicCleanup();
|
|
|
|
|
|
|
|
// Fast486Cleanup();
|
|
|
|
|
2013-11-01 01:46:58 +00:00
|
|
|
/* Free the memory allocated for the 16-bit address space */
|
|
|
|
if (BaseAddress != NULL) HeapFree(GetProcessHeap(), 0, BaseAddress);
|
|
|
|
}
|
|
|
|
|
2014-01-25 00:21:51 +00:00
|
|
|
VOID EmulatorException(BYTE ExceptionNumber, LPWORD Stack)
|
|
|
|
{
|
|
|
|
WORD CodeSegment, InstructionPointer;
|
|
|
|
PBYTE Opcode;
|
|
|
|
|
|
|
|
ASSERT(ExceptionNumber < 8);
|
|
|
|
|
|
|
|
/* Get the CS:IP */
|
|
|
|
InstructionPointer = Stack[STACK_IP];
|
|
|
|
CodeSegment = Stack[STACK_CS];
|
|
|
|
Opcode = (PBYTE)SEG_OFF_TO_PTR(CodeSegment, InstructionPointer);
|
|
|
|
|
|
|
|
/* Display a message to the user */
|
|
|
|
DisplayMessage(L"Exception: %s occured at %04X:%04X\n"
|
|
|
|
L"Opcode: %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X",
|
|
|
|
ExceptionName[ExceptionNumber],
|
|
|
|
CodeSegment,
|
|
|
|
InstructionPointer,
|
|
|
|
Opcode[0],
|
|
|
|
Opcode[1],
|
|
|
|
Opcode[2],
|
|
|
|
Opcode[3],
|
|
|
|
Opcode[4],
|
|
|
|
Opcode[5],
|
|
|
|
Opcode[6],
|
|
|
|
Opcode[7],
|
|
|
|
Opcode[8],
|
|
|
|
Opcode[9]);
|
|
|
|
|
|
|
|
/* Stop the VDM */
|
|
|
|
VdmRunning = FALSE;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2013-08-31 19:18:12 +00:00
|
|
|
// FIXME: This function assumes 16-bit mode!!!
|
2013-06-17 00:00:36 +00:00
|
|
|
VOID EmulatorExecute(WORD Segment, WORD Offset)
|
|
|
|
{
|
2013-10-19 16:55:51 +00:00
|
|
|
/* Tell Fast486 to move the instruction pointer */
|
|
|
|
Fast486ExecuteAt(&EmulatorContext, Segment, Offset);
|
2013-06-17 00:00:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
VOID EmulatorInterrupt(BYTE Number)
|
|
|
|
{
|
2013-10-19 16:55:51 +00:00
|
|
|
/* Call the Fast486 API */
|
|
|
|
Fast486Interrupt(&EmulatorContext, Number);
|
2013-06-17 00:00:36 +00:00
|
|
|
}
|
|
|
|
|
2013-10-27 00:37:01 +00:00
|
|
|
VOID EmulatorInterruptSignal(VOID)
|
2013-06-26 17:15:45 +00:00
|
|
|
{
|
2013-10-19 16:55:51 +00:00
|
|
|
/* Call the Fast486 API */
|
2013-10-27 00:37:01 +00:00
|
|
|
Fast486InterruptSignal(&EmulatorContext);
|
2013-06-26 17:15:45 +00:00
|
|
|
}
|
|
|
|
|
2013-07-23 19:31:00 +00:00
|
|
|
VOID EmulatorStep(VOID)
|
2013-06-17 00:00:36 +00:00
|
|
|
{
|
2013-08-31 19:18:12 +00:00
|
|
|
/* Dump the state for debugging purposes */
|
2013-10-19 16:55:51 +00:00
|
|
|
// Fast486DumpState(&EmulatorContext);
|
2013-08-31 19:18:12 +00:00
|
|
|
|
|
|
|
/* Execute the next instruction */
|
2013-10-19 16:55:51 +00:00
|
|
|
Fast486StepInto(&EmulatorContext);
|
2013-06-17 00:00:36 +00:00
|
|
|
}
|
|
|
|
|
2013-06-26 17:15:45 +00:00
|
|
|
VOID EmulatorSetA20(BOOLEAN Enabled)
|
|
|
|
{
|
|
|
|
A20Line = Enabled;
|
|
|
|
}
|
|
|
|
|
2013-12-17 22:10:58 +00:00
|
|
|
|
|
|
|
|
2013-12-24 15:52:31 +00:00
|
|
|
VOID
|
|
|
|
WINAPI
|
|
|
|
VDDTerminateVDM(VOID)
|
|
|
|
{
|
|
|
|
/* Stop the VDM */
|
|
|
|
VdmRunning = FALSE;
|
|
|
|
}
|
|
|
|
|
2013-12-17 23:03:23 +00:00
|
|
|
PBYTE
|
|
|
|
WINAPI
|
2013-12-23 18:09:18 +00:00
|
|
|
Sim32pGetVDMPointer(IN ULONG Address,
|
|
|
|
IN BOOLEAN ProtectedMode)
|
2013-12-17 22:10:58 +00:00
|
|
|
{
|
|
|
|
// FIXME
|
|
|
|
UNREFERENCED_PARAMETER(ProtectedMode);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* HIWORD(Address) == Segment (if ProtectedMode == FALSE)
|
|
|
|
* or Selector (if ProtectedMode == TRUE )
|
|
|
|
* LOWORD(Address) == Offset
|
|
|
|
*/
|
2013-12-23 18:09:18 +00:00
|
|
|
return (PBYTE)FAR_POINTER(Address);
|
2013-12-17 22:10:58 +00:00
|
|
|
}
|
|
|
|
|
2013-12-17 23:03:23 +00:00
|
|
|
PBYTE
|
|
|
|
WINAPI
|
2013-12-23 18:09:18 +00:00
|
|
|
MGetVdmPointer(IN ULONG Address,
|
|
|
|
IN ULONG Size,
|
|
|
|
IN BOOLEAN ProtectedMode)
|
2013-12-17 22:10:58 +00:00
|
|
|
{
|
|
|
|
UNREFERENCED_PARAMETER(Size);
|
|
|
|
return Sim32pGetVDMPointer(Address, ProtectedMode);
|
|
|
|
}
|
|
|
|
|
2013-12-17 23:03:23 +00:00
|
|
|
PVOID
|
|
|
|
WINAPI
|
|
|
|
VdmMapFlat(IN USHORT Segment,
|
|
|
|
IN ULONG Offset,
|
|
|
|
IN VDM_MODE Mode)
|
|
|
|
{
|
|
|
|
// FIXME
|
|
|
|
UNREFERENCED_PARAMETER(Mode);
|
|
|
|
|
|
|
|
return SEG_OFF_TO_PTR(Segment, Offset);
|
|
|
|
}
|
|
|
|
|
2014-01-11 20:59:27 +00:00
|
|
|
BOOL
|
2013-12-17 23:03:23 +00:00
|
|
|
WINAPI
|
|
|
|
VdmFlushCache(IN USHORT Segment,
|
|
|
|
IN ULONG Offset,
|
|
|
|
IN ULONG Size,
|
|
|
|
IN VDM_MODE Mode)
|
|
|
|
{
|
|
|
|
// FIXME
|
|
|
|
UNIMPLEMENTED;
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
BOOL
|
|
|
|
WINAPI
|
|
|
|
VdmUnmapFlat(IN USHORT Segment,
|
|
|
|
IN ULONG Offset,
|
|
|
|
IN PVOID Buffer,
|
|
|
|
IN VDM_MODE Mode)
|
|
|
|
{
|
|
|
|
// FIXME
|
|
|
|
UNIMPLEMENTED;
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
2013-06-17 00:00:36 +00:00
|
|
|
/* EOF */
|