#include "emulator.h"
+#include "clock.h"
#include "bios/bios.h"
#include "hardware/cmos.h"
#include "hardware/pic.h"
+#include "hardware/ps2.h"
#include "hardware/speaker.h"
#include "hardware/timer.h"
#include "hardware/vga.h"
#include "bop.h"
#include "vddsup.h"
#include "io.h"
-#include "registers.h"
+
+#include <isvbop.h>
/* PRIVATE VARIABLES **********************************************************/
FAST486_STATE EmulatorContext;
+BOOLEAN CpuSimulate = FALSE;
+
+/* No more than 'MaxCpuCallLevel' recursive CPU calls are allowed */
+const static INT MaxCpuCallLevel = 32;
+static INT CpuCallLevel = 0;
+
LPVOID BaseAddress = NULL;
BOOLEAN VdmRunning = TRUE;
-static BOOLEAN A20Line = FALSE;
+static BOOLEAN A20Line = FALSE;
+static BYTE Port61hState = 0x00;
+
+static HANDLE InputThread = NULL;
+
+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"
+};
/* BOP Identifiers */
#define BOP_DEBUGGER 0x56 // Break into the debugger from a 16-bit app
{
UNREFERENCED_PARAMETER(State);
+ // BIG HACK!!!! To make BIOS images working correctly,
+ // until Aleksander rewrites memory management!!
+ if (Address >= 0xFFFFFFF0) Address -= 0xFFF00000;
+
/* If the A20 line is disabled, mask bit 20 */
if (!A20Line) Address &= ~(1 << 20);
DWORD VgaAddress = max(Address, VgaGetVideoBaseAddress());
DWORD ActualSize = min(Address + Size - 1, VgaGetVideoLimitAddress())
- VgaAddress + 1;
- LPBYTE DestBuffer = (LPBYTE)((ULONG_PTR)BaseAddress + VgaAddress);
+ LPBYTE DestBuffer = (LPBYTE)REAL_TO_PHYS(VgaAddress);
/* Read from the VGA memory */
VgaReadMemory(VgaAddress, DestBuffer, ActualSize);
}
/* Read the data from the virtual address space and store it in the buffer */
- RtlCopyMemory(Buffer, (LPVOID)((ULONG_PTR)BaseAddress + Address), Size);
+ RtlCopyMemory(Buffer, REAL_TO_PHYS(Address), Size);
}
VOID WINAPI EmulatorWriteMemory(PFAST486_STATE State, ULONG Address, PVOID Buffer, ULONG Size)
{
UNREFERENCED_PARAMETER(State);
+ // BIG HACK!!!! To make BIOS images working correctly,
+ // until Aleksander rewrites memory management!!
+ if (Address >= 0xFFFFFFF0) Address -= 0xFFF00000;
+
/* If the A20 line is disabled, mask bit 20 */
if (!A20Line) Address &= ~(1 << 20);
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);
+ RtlCopyMemory(REAL_TO_PHYS(Address), Buffer, Size);
/*
* Check if we modified the VGA memory.
DWORD VgaAddress = max(Address, VgaGetVideoBaseAddress());
DWORD ActualSize = min(Address + Size - 1, VgaGetVideoLimitAddress())
- VgaAddress + 1;
- LPBYTE SrcBuffer = (LPBYTE)((ULONG_PTR)BaseAddress + VgaAddress);
+ LPBYTE SrcBuffer = (LPBYTE)REAL_TO_PHYS(VgaAddress);
/* Write to the VGA memory */
VgaWriteMemory(VgaAddress, SrcBuffer, ActualSize);
return PicGetInterrupt();
}
-VOID WINAPI EmulatorDebugBreak(LPWORD Stack)
+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;
+}
+
+// FIXME: This function assumes 16-bit mode!!!
+VOID EmulatorExecute(WORD Segment, WORD Offset)
+{
+ /* Tell Fast486 to move the instruction pointer */
+ Fast486ExecuteAt(&EmulatorContext, Segment, Offset);
+}
+
+VOID EmulatorStep(VOID)
+{
+ /* Dump the state for debugging purposes */
+ // Fast486DumpState(&EmulatorContext);
+
+ /* Execute the next instruction */
+ Fast486StepInto(&EmulatorContext);
+}
+
+VOID EmulatorSimulate(VOID)
+{
+ if (CpuCallLevel > MaxCpuCallLevel)
+ {
+ DisplayMessage(L"Too many CPU levels of recursion (%d, expected maximum %d)",
+ CpuCallLevel, MaxCpuCallLevel);
+
+ /* Stop the VDM */
+ VdmRunning = FALSE;
+ return;
+ }
+ CpuCallLevel++;
+
+ CpuSimulate = TRUE;
+ while (VdmRunning && CpuSimulate) ClockUpdate();
+
+ CpuCallLevel--;
+ if (CpuCallLevel < 0) CpuCallLevel = 0;
+
+ /* This takes into account for reentrance */
+ CpuSimulate = TRUE;
+}
+
+VOID EmulatorUnsimulate(VOID)
+{
+ /* Stop simulation */
+ CpuSimulate = FALSE;
+}
+
+VOID EmulatorInterrupt(BYTE Number)
+{
+ /* Call the Fast486 API */
+ Fast486Interrupt(&EmulatorContext, Number);
+}
+
+VOID EmulatorInterruptSignal(VOID)
+{
+ /* Call the Fast486 API */
+ Fast486InterruptSignal(&EmulatorContext);
+}
+
+VOID EmulatorSetA20(BOOLEAN Enabled)
+{
+ A20Line = Enabled;
+}
+
+VOID WINAPI EmulatorDebugBreakBop(LPWORD Stack)
{
DPRINT1("NTVDM: BOP_DEBUGGER\n");
DebugBreak();
}
+VOID WINAPI EmulatorUnsimulateBop(LPWORD Stack)
+{
+ EmulatorUnsimulate();
+}
+
+static BYTE WINAPI Port61hRead(ULONG Port)
+{
+ return Port61hState;
+}
+
+static VOID WINAPI Port61hWrite(ULONG Port, BYTE Data)
+{
+ // BOOLEAN SpeakerChange = FALSE;
+ BYTE OldPort61hState = Port61hState;
+
+ /* Only the four lowest bytes can be written */
+ Port61hState = (Port61hState & 0xF0) | (Data & 0x0F);
+
+ if ((OldPort61hState ^ Port61hState) & 0x01)
+ {
+ DPRINT("PIT 2 Gate %s\n", Port61hState & 0x01 ? "on" : "off");
+ // SpeakerChange = TRUE;
+ }
+
+ PitSetGate(2, !!(Port61hState & 0x01));
+
+ if ((OldPort61hState ^ Port61hState) & 0x02)
+ {
+ /* There were some change for the speaker... */
+ DPRINT("Speaker %s\n", Port61hState & 0x02 ? "on" : "off");
+ // SpeakerChange = TRUE;
+ }
+ // if (SpeakerChange) SpeakerChange();
+ SpeakerChange();
+}
+
+static VOID WINAPI PitChan0Out(LPVOID Param, BOOLEAN State)
+{
+ if (State)
+ {
+ DPRINT("PicInterruptRequest\n");
+ PicInterruptRequest(0); // Raise IRQ 0
+ }
+ // else < Lower IRQ 0 >
+}
+
+static VOID WINAPI PitChan1Out(LPVOID Param, BOOLEAN State)
+{
+#if 0
+ if (State)
+ {
+ /* Set bit 4 of Port 61h */
+ Port61hState |= 1 << 4;
+ }
+ else
+ {
+ /* Clear bit 4 of Port 61h */
+ Port61hState &= ~(1 << 4);
+ }
+#else
+ Port61hState = (Port61hState & 0xEF) | (State << 4);
+#endif
+}
+
+static VOID WINAPI PitChan2Out(LPVOID Param, BOOLEAN State)
+{
+ // BYTE OldPort61hState = Port61hState;
+
+#if 0
+ if (State)
+ {
+ /* Set bit 5 of Port 61h */
+ Port61hState |= 1 << 5;
+ }
+ else
+ {
+ /* Clear bit 5 of Port 61h */
+ Port61hState &= ~(1 << 5);
+ }
+#else
+ Port61hState = (Port61hState & 0xDF) | (State << 5);
+#endif
+ DPRINT("Speaker PIT out\n");
+ // if ((OldPort61hState ^ Port61hState) & 0x20)
+ // SpeakerChange();
+}
+
/* PUBLIC FUNCTIONS ***********************************************************/
-BOOLEAN EmulatorInitialize(VOID)
+DWORD WINAPI PumpConsoleInput(LPVOID Parameter);
+
+BOOLEAN EmulatorInitialize(HANDLE ConsoleInput, HANDLE ConsoleOutput)
{
/* Allocate memory for the 16-bit address space */
BaseAddress = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, MAX_ADDRESS);
/* Initialize I/O ports */
/* Initialize RAM */
+ /* Initialize the internal clock */
+ if (!ClockInitialize())
+ {
+ wprintf(L"FATAL: Failed to initialize the clock\n");
+ return FALSE;
+ }
+
/* Initialize the CPU */
Fast486Initialize(&EmulatorContext,
EmulatorReadMemory,
EmulatorIntAcknowledge,
NULL /* TODO: Use a TLB */);
- /* Enable interrupts */
- setIF(1);
+ /* Initialize DMA */
/* Initialize the PIC, the PIT, the CMOS and the PC Speaker */
PicInitialize();
CmosInitialize();
SpeakerInitialize();
- /* Register the DebugBreak BOP */
- RegisterBop(BOP_DEBUGGER, EmulatorDebugBreak);
+ /* Set output functions */
+ PitSetOutFunction(0, NULL, PitChan0Out);
+ PitSetOutFunction(1, NULL, PitChan1Out);
+ PitSetOutFunction(2, NULL, PitChan2Out);
+
+ /* Register the I/O Ports */
+ RegisterIoPort(CONTROL_SYSTEM_PORT61H, Port61hRead, Port61hWrite);
+
+ /* Initialize the PS2 port */
+ PS2Initialize(ConsoleInput);
+
+ /* Set the console input mode */
+ // SetConsoleMode(ConsoleInput, ENABLE_MOUSE_INPUT | ENABLE_PROCESSED_INPUT);
+
+ /* Start the input thread */
+ InputThread = CreateThread(NULL, 0, &PumpConsoleInput, ConsoleInput, 0, NULL);
+ // if (InputThread == NULL) return FALSE;
+
+ /* Initialize the VGA */
+ // if (!VgaInitialize(ConsoleOutput)) return FALSE;
+ VgaInitialize(ConsoleOutput);
+
+ /* Initialize the software callback system and register the emulator BOPs */
+ InitializeCallbacks();
+ RegisterBop(BOP_DEBUGGER , EmulatorDebugBreakBop);
+ RegisterBop(BOP_UNSIMULATE, EmulatorUnsimulateBop);
/* Initialize VDD support */
VDDSupInitialize();
VOID EmulatorCleanup(VOID)
{
+ // VgaCleanup();
+
+ /* Close the input thread handle */
+ if (InputThread != NULL) CloseHandle(InputThread);
+ InputThread = NULL;
+
+ PS2Cleanup();
+
SpeakerCleanup();
CmosCleanup();
// PitCleanup();
if (BaseAddress != NULL) HeapFree(GetProcessHeap(), 0, BaseAddress);
}
-// FIXME: This function assumes 16-bit mode!!!
-VOID EmulatorExecute(WORD Segment, WORD Offset)
-{
- /* Tell Fast486 to move the instruction pointer */
- Fast486ExecuteAt(&EmulatorContext, Segment, Offset);
-}
-VOID EmulatorInterrupt(BYTE Number)
-{
- /* Call the Fast486 API */
- Fast486Interrupt(&EmulatorContext, Number);
-}
-VOID EmulatorInterruptSignal(VOID)
-{
- /* Call the Fast486 API */
- Fast486InterruptSignal(&EmulatorContext);
-}
-
-VOID EmulatorStep(VOID)
-{
- /* Dump the state for debugging purposes */
- // Fast486DumpState(&EmulatorContext);
-
- /* Execute the next instruction */
- Fast486StepInto(&EmulatorContext);
-}
-
-VOID EmulatorSetA20(BOOLEAN Enabled)
+VOID
+WINAPI
+VDDSimulate16(VOID)
{
- A20Line = Enabled;
+ EmulatorSimulate();
}
-
-
VOID
WINAPI
VDDTerminateVDM(VOID)