[FAST486][NTVDM]
[reactos.git] / subsystems / ntvdm / ntvdm.c
index 300a89c..c1efd21 100644 (file)
@@ -8,29 +8,27 @@
 
 /* INCLUDES *******************************************************************/
 
+#define NDEBUG
+
 #include "ntvdm.h"
 #include "emulator.h"
 #include "bios.h"
+#include "vga.h"
 #include "dos.h"
 #include "timer.h"
 #include "pic.h"
 #include "ps2.h"
 
+/*
+ * Activate this line if you want to be able to test NTVDM with:
+ * ntvdm.exe <program>
+ */
+#define TESTING
+
 /* PUBLIC VARIABLES ***********************************************************/
 
 BOOLEAN VdmRunning = TRUE;
 LPVOID BaseAddress = 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"
-};
 
 /* PUBLIC FUNCTIONS ***********************************************************/
 
@@ -41,7 +39,7 @@ VOID DisplayMessage(LPCWSTR Format, ...)
 
     va_start(Parameters, Format);
     _vsnwprintf(Buffer, 256, Format, Parameters);
-    MessageBox(NULL, Buffer, L"NTVDM Subsystem", MB_OK);
+    MessageBoxW(NULL, Buffer, L"NTVDM Subsystem", MB_OK);
     va_end(Parameters);
 }
 
@@ -68,54 +66,36 @@ BOOL WINAPI ConsoleCtrlHandler(DWORD ControlType)
 INT wmain(INT argc, WCHAR *argv[])
 {
     INT i;
-    CHAR CommandLine[128];
+    CHAR CommandLine[DOS_CMDLINE_LENGTH];
     DWORD CurrentTickCount;
-    DWORD LastTickCount = GetTickCount();
     DWORD Cycles = 0;
     DWORD LastCyclePrintout = GetTickCount();
     DWORD LastVerticalRefresh = GetTickCount();
     LARGE_INTEGER Frequency, LastTimerTick, Counter;
     LONGLONG TimerTicks;
-    HANDLE ConsoleInput = INVALID_HANDLE_VALUE;
-    HANDLE ConsoleOutput = INVALID_HANDLE_VALUE;
-    CONSOLE_SCREEN_BUFFER_INFO SavedBufferInfo;
+    HANDLE InputThread = NULL;
 
     /* Set the handler routine */
     SetConsoleCtrlHandler(ConsoleCtrlHandler, TRUE);
 
-    /* Get the input and output handles to the real console */
-    ConsoleInput = CreateFile(TEXT("CONIN$"),
-                              GENERIC_READ | GENERIC_WRITE,
-                              FILE_SHARE_READ | FILE_SHARE_WRITE,
-                              NULL,
-                              OPEN_EXISTING,
-                              0,
-                              NULL);
-
-    ConsoleOutput = CreateFile(TEXT("CONOUT$"),
-                               GENERIC_READ | GENERIC_WRITE,
-                               FILE_SHARE_READ | FILE_SHARE_WRITE,
-                               NULL,
-                               OPEN_EXISTING,
-                               0,
-                               NULL);
-
-    if ((ConsoleInput == INVALID_HANDLE_VALUE)
-        || (ConsoleOutput == INVALID_HANDLE_VALUE))
+#ifndef TESTING
+    UNREFERENCED_PARAMETER(argc);
+    UNREFERENCED_PARAMETER(argv);
+
+    /* The DOS command line must be ASCII */
+    WideCharToMultiByte(CP_ACP, 0, GetCommandLine(), -1, CommandLine, sizeof(CommandLine), NULL, NULL);
+#else
+    if (argc == 2 && argv[1] != NULL)
     {
-        wprintf(L"FATAL: Could not get handles to the console\n");
-        goto Cleanup;
+        WideCharToMultiByte(CP_ACP, 0, argv[1], -1, CommandLine, sizeof(CommandLine), NULL, NULL);
     }
-
-    /* Save the console screen buffer information */
-    if (!GetConsoleScreenBufferInfo(ConsoleOutput, &SavedBufferInfo))
+    else
     {
-        wprintf(L"FATAL: Could not save the console screen buffer information\n");
-        goto Cleanup;
+        wprintf(L"\nReactOS Virtual DOS Machine\n\n"
+                L"Usage: NTVDM <executable>\n");
+        return 0;
     }
-
-    /* The DOS command line must be ASCII */
-    WideCharToMultiByte(CP_ACP, 0, GetCommandLine(), -1, CommandLine, 128, NULL, NULL);
+#endif
 
     if (!EmulatorInitialize())
     {
@@ -131,7 +111,7 @@ INT wmain(INT argc, WCHAR *argv[])
     }
 
     /* Initialize the system BIOS */
-    if (!BiosInitialize(ConsoleInput, ConsoleOutput))
+    if (!BiosInitialize())
     {
         wprintf(L"FATAL: Failed to initialize the VDM BIOS.\n");
         goto Cleanup;
@@ -150,69 +130,81 @@ INT wmain(INT argc, WCHAR *argv[])
         DisplayMessage(L"Could not start program: %S", CommandLine);
         return -1;
     }
-    
+
+    /* Start the input thread */
+    InputThread = CreateThread(NULL, 0, &InputThreadProc, NULL, 0, NULL);
     /* Set the last timer tick to the current time */
     QueryPerformanceCounter(&LastTimerTick);
 
     /* Main loop */
     while (VdmRunning)
     {
+        /* Get the resolution of the system timer */
+        DWORD TimerResolution = PitGetResolution();
+
         /* Get the current number of ticks */
         CurrentTickCount = GetTickCount();
-        
-        /* Get the current performance counter value */
-        QueryPerformanceCounter(&Counter);
-        
-        /* Get the number of PIT ticks that have passed */
-        TimerTicks = ((Counter.QuadPart - LastTimerTick.QuadPart)
-                     * PIT_BASE_FREQUENCY) / Frequency.QuadPart;
-        
+        if (TimerResolution > 1000)
+        {
+            /* Get the current performance counter value */
+            QueryPerformanceCounter(&Counter);
+            /* Get the number of PIT ticks that have passed */
+            TimerTicks = ((Counter.QuadPart - LastTimerTick.QuadPart)
+                         * PIT_BASE_FREQUENCY) / Frequency.QuadPart;
+        }
+        else
+        {
+            /* Use the standard tick count */
+            Counter.QuadPart = CurrentTickCount;
+
+            /* Get the number of PIT ticks that have passed */
+            TimerTicks = ((Counter.QuadPart - LastTimerTick.QuadPart)
+                         * PIT_BASE_FREQUENCY) / 1000;
+        }
         /* Update the PIT */
-        for (i = 0; i < TimerTicks; i++) PitDecrementCount();
-        LastTimerTick = Counter;
-        
-        /* Check for console input events every millisecond */
-        if (CurrentTickCount != LastTickCount)
+        if (TimerTicks > 0)
         {
-            CheckForInputEvents();
-            LastTickCount = CurrentTickCount;
+            for (i = 0; i < TimerTicks; i++) PitDecrementCount();
+            LastTimerTick = Counter;
         }
 
-        /* Check for vertical refresh */
+        /* Check for vertical retrace */
         if ((CurrentTickCount - LastVerticalRefresh) >= 16)
         {
-            BiosVerticalRefresh();
+            VgaRefreshDisplay();
             LastVerticalRefresh = CurrentTickCount;
         }
-        
+
+        /* Horizontal retrace occurs as fast as possible */
+        VgaHorizontalRetrace();
+
         /* Continue CPU emulation */
         for (i = 0; (i < STEPS_PER_CYCLE) && VdmRunning; i++)
         {
             EmulatorStep();
             Cycles++;
         }
-        
+
         if ((CurrentTickCount - LastCyclePrintout) >= 1000)
         {
-            DPRINT1("NTVDM: %d Instructions Per Second\n", Cycles);
+            DPRINT1("NTVDM: %lu Instructions Per Second\n", Cycles);
             LastCyclePrintout = CurrentTickCount;
             Cycles = 0;
         }
     }
 
-Cleanup:
-    /* Restore the old screen buffer */
-    SetConsoleActiveScreenBuffer(ConsoleOutput);
-
-    /* Restore the screen buffer size */
-    SetConsoleScreenBufferSize(ConsoleOutput, SavedBufferInfo.dwSize);
+    /* Perform another screen refresh */
+    VgaRefreshDisplay();
 
+Cleanup:
+    if (InputThread != NULL) CloseHandle(InputThread);
+    BiosCleanup();
     EmulatorCleanup();
 
-    /* Close the console handles */
-    if (ConsoleInput != INVALID_HANDLE_VALUE) CloseHandle(ConsoleInput);
-    if (ConsoleOutput != INVALID_HANDLE_VALUE) CloseHandle(ConsoleOutput);
-
     return 0;
 }