[NTVDM]
[reactos.git] / subsystems / ntvdm / emulator.c
index 0e5cba8..6128d5d 100644 (file)
 #define NDEBUG
 
 #include "emulator.h"
+#include "callback.h"
 
 #include "clock.h"
-#include "bios/bios.h"
+#include "bios/rom.h"
 #include "hardware/cmos.h"
 #include "hardware/pic.h"
 #include "hardware/ps2.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;
 
@@ -59,6 +66,10 @@ VOID WINAPI EmulatorReadMemory(PFAST486_STATE State, ULONG Address, PVOID Buffer
 {
     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);
 
@@ -75,20 +86,24 @@ VOID WINAPI EmulatorReadMemory(PFAST486_STATE State, ULONG Address, PVOID Buffer
         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);
 
@@ -99,7 +114,7 @@ VOID WINAPI EmulatorWriteMemory(PFAST486_STATE State, ULONG Address, PVOID Buffe
     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.
@@ -110,7 +125,7 @@ VOID WINAPI EmulatorWriteMemory(PFAST486_STATE State, ULONG Address, PVOID Buffe
         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);
@@ -155,7 +170,7 @@ VOID EmulatorException(BYTE ExceptionNumber, LPWORD Stack)
                    Opcode[9]);
 
     /* Stop the VDM */
-    VdmRunning = FALSE;
+    EmulatorTerminate();
     return;
 }
 
@@ -177,17 +192,36 @@ VOID EmulatorStep(VOID)
 
 VOID EmulatorSimulate(VOID)
 {
-    // FIXME: Do not mix VdmRunning (i.e. ntvdm running) and CpuSimulate!!
-    while (VdmRunning) ClockUpdate();
+    if (CpuCallLevel > MaxCpuCallLevel)
+    {
+        DisplayMessage(L"Too many CPU levels of recursion (%d, expected maximum %d)",
+                       CpuCallLevel, MaxCpuCallLevel);
+
+        /* Stop the VDM */
+        EmulatorTerminate();
+        return;
+    }
+    CpuCallLevel++;
+
+    VdmRunning = CpuSimulate = TRUE;
+    while (VdmRunning && CpuSimulate) ClockUpdate();
+
+    CpuCallLevel--;
+    if (CpuCallLevel < 0) CpuCallLevel = 0;
 
     /* This takes into account for reentrance */
-    VdmRunning = TRUE;
+    CpuSimulate = TRUE;
 }
 
 VOID EmulatorUnsimulate(VOID)
 {
     /* Stop simulation */
-    // FIXME: Do not mix VdmRunning (i.e. ntvdm running) and CpuSimulate!!
+    CpuSimulate = FALSE;
+}
+
+VOID EmulatorTerminate(VOID)
+{
+    /* Stop the VDM */
     VdmRunning = FALSE;
 }
 
@@ -208,13 +242,13 @@ VOID EmulatorSetA20(BOOLEAN Enabled)
     A20Line = Enabled;
 }
 
-VOID WINAPI EmulatorDebugBreakBop(LPWORD Stack)
+static VOID WINAPI EmulatorDebugBreakBop(LPWORD Stack)
 {
     DPRINT1("NTVDM: BOP_DEBUGGER\n");
     DebugBreak();
 }
 
-VOID WINAPI EmulatorUnsimulateBop(LPWORD Stack)
+static VOID WINAPI EmulatorUnsimulateBop(LPWORD Stack)
 {
     EmulatorUnsimulate();
 }
@@ -336,9 +370,6 @@ BOOLEAN EmulatorInitialize(HANDLE ConsoleInput, HANDLE ConsoleOutput)
                       EmulatorIntAcknowledge,
                       NULL /* TODO: Use a TLB */);
 
-    /* Enable interrupts */
-    setIF(1);
-
     /* Initialize DMA */
 
     /* Initialize the PIC, the PIT, the CMOS and the PC Speaker */
@@ -355,12 +386,15 @@ BOOLEAN EmulatorInitialize(HANDLE ConsoleInput, HANDLE ConsoleOutput)
     /* Register the I/O Ports */
     RegisterIoPort(CONTROL_SYSTEM_PORT61H, Port61hRead, Port61hWrite);
 
+    /* Set the console input mode */
+    // FIXME: Activate ENABLE_WINDOW_INPUT when we will want to perform actions
+    // upon console window events (screen buffer resize, ...).
+    SetConsoleMode(ConsoleInput, ENABLE_PROCESSED_INPUT /* | ENABLE_WINDOW_INPUT */);
+    // SetConsoleMode(ConsoleOutput, ENABLE_PROCESSED_OUTPUT | ENABLE_WRAP_AT_EOL_OUTPUT);
+
     /* 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;
@@ -369,7 +403,8 @@ BOOLEAN EmulatorInitialize(HANDLE ConsoleInput, HANDLE ConsoleOutput)
     // if (!VgaInitialize(ConsoleOutput)) return FALSE;
     VgaInitialize(ConsoleOutput);
 
-    /* Register the emulator BOPs */
+    /* Initialize the software callback system and register the emulator BOPs */
+    InitializeCallbacks();
     RegisterBop(BOP_DEBUGGER  , EmulatorDebugBreakBop);
     RegisterBop(BOP_UNSIMULATE, EmulatorUnsimulateBop);
 
@@ -381,7 +416,7 @@ BOOLEAN EmulatorInitialize(HANDLE ConsoleInput, HANDLE ConsoleOutput)
 
 VOID EmulatorCleanup(VOID)
 {
-    // VgaCleanup();
+    VgaCleanup();
 
     /* Close the input thread handle */
     if (InputThread != NULL) CloseHandle(InputThread);
@@ -414,7 +449,7 @@ WINAPI
 VDDTerminateVDM(VOID)
 {
     /* Stop the VDM */
-    VdmRunning = FALSE;
+    EmulatorTerminate();
 }
 
 PBYTE