[NTVDM]
authorAleksandar Andrejevic <aandrejevic@reactos.org>
Fri, 5 Jul 2013 01:31:50 +0000 (01:31 +0000)
committerAleksandar Andrejevic <aandrejevic@reactos.org>
Fri, 5 Jul 2013 01:31:50 +0000 (01:31 +0000)
Improve vertical refresh performance.
Properly synchronize access to the framebuffer.

svn path=/branches/ntvdm/; revision=59425

subsystems/ntvdm/bios.c
subsystems/ntvdm/bios.h
subsystems/ntvdm/emulator.c
subsystems/ntvdm/ntvdm.c

index 40632dd..6d1de0c 100644 (file)
@@ -22,11 +22,16 @@ static UINT BiosKbdBufferStart = 0, BiosKbdBufferEnd = 0;
 static BOOLEAN BiosKbdBufferEmpty = TRUE;
 static DWORD BiosTickCount = 0;
 static BOOLEAN BiosPassedMidnight = FALSE;
-static HANDLE BiosConsoleInput, BiosConsoleOutput;
+static HANDLE BiosConsoleInput = INVALID_HANDLE_VALUE;
+static HANDLE BiosConsoleOutput = INVALID_HANDLE_VALUE;
 static BYTE CurrentVideoMode = BIOS_DEFAULT_VIDEO_MODE;
 static BYTE CurrentVideoPage = 0;
 static HANDLE ConsoleBuffers[BIOS_MAX_PAGES] = { NULL };
 static LPVOID ConsoleFramebuffers[BIOS_MAX_PAGES] = { NULL };
+static HANDLE ConsoleMutexes[BIOS_MAX_PAGES] = { NULL };
+static BOOLEAN VideoNeedsUpdate = TRUE;
+static SMALL_RECT UpdateRectangle = { 0, 0, 0, 0 };
+static CONSOLE_SCREEN_BUFFER_INFO BiosSavedBufferInfo;
 static VIDEO_MODE VideoModes[] =
 {
     /* Width | Height | Text | Colors | Gray | Pages | Segment */
@@ -54,14 +59,39 @@ static VIDEO_MODE VideoModes[] =
 
 /* PRIVATE FUNCTIONS **********************************************************/
 
+static INT BiosColorNumberToBits(DWORD Colors)
+{
+    INT i;
+
+    /* Find the index of the highest-order bit */
+    for (i = 31; i >= 0; i--) if (Colors & (1 << i)) break;
+
+    /* Special case for zero */
+    if (i == 0) i = 32;
+
+    return i;
+}
+
 static COORD BiosVideoAddressToCoord(ULONG Address)
 {
     COORD Result = {0, 0};
+    INT BitsPerPixel;
+    DWORD Offset = Address - (VideoModes[CurrentVideoMode].Segment << 4);
 
-    Result.X = ((Address - (VideoModes[CurrentVideoMode].Segment << 4)) >> 1)
-               % VideoModes[CurrentVideoMode].Width;
-    Result.Y = ((Address - (VideoModes[CurrentVideoMode].Segment << 4)) >> 1)
-               / VideoModes[CurrentVideoMode].Width;
+    if (VideoModes[CurrentVideoMode].Text)
+    {
+        Result.X = (Offset / sizeof(WORD)) % VideoModes[CurrentVideoMode].Width;
+        Result.Y = (Offset / sizeof(WORD)) / VideoModes[CurrentVideoMode].Width;
+    }
+    else
+    {
+        BitsPerPixel = BiosColorNumberToBits(VideoModes[CurrentVideoMode].Colors);
+
+        Result.X = ((Offset * 8) / BitsPerPixel)
+                   % VideoModes[CurrentVideoMode].Width;
+        Result.Y = ((Offset * 8) / BitsPerPixel)
+                   / VideoModes[CurrentVideoMode].Width;
+    }
 
     return Result;
 }
@@ -130,6 +160,7 @@ BOOLEAN BiosSetVideoMode(BYTE ModeNumber)
     for (i = 0; i < VideoModes[CurrentVideoMode].Pages; i++)
     {
         if (ConsoleBuffers[i] != NULL) CloseHandle(ConsoleBuffers[i]);
+        if (!VideoModes[CurrentVideoMode].Text) CloseHandle(ConsoleMutexes[i]);
     }
 
     if (VideoModes[ModeNumber].Text)
@@ -182,14 +213,7 @@ BOOLEAN BiosSetVideoMode(BYTE ModeNumber)
         BitmapInfo->bmiHeader.biHeight = VideoModes[ModeNumber].Height;
         BitmapInfo->bmiHeader.biPlanes = 1;
         BitmapInfo->bmiHeader.biCompression = BI_RGB;
-
-        /* Calculate the number of color bits */
-        for (i = 31; i >= 0; i--)
-        {
-            if (VideoModes[ModeNumber].Colors & (1 << i)) break;
-        }
-        if (i == 0) i = 32;
-        BitmapInfo->bmiHeader.biBitCount = i;
+        BitmapInfo->bmiHeader.biBitCount = BiosColorNumberToBits(VideoModes[ModeNumber].Colors);
 
         /* Calculate the image size */
         BitmapInfo->bmiHeader.biSizeImage = BitmapInfo->bmiHeader.biWidth
@@ -220,8 +244,9 @@ BOOLEAN BiosSetVideoMode(BYTE ModeNumber)
                                                           CONSOLE_GRAPHICS_BUFFER,
                                                           &GraphicsBufferInfo);
 
-            /* Save the framebuffer address */
+            /* Save the framebuffer address and mutex */
             ConsoleFramebuffers[i] = GraphicsBufferInfo.lpBitMap;
+            ConsoleMutexes[i] = GraphicsBufferInfo.hMutex;
         }
 
         /* Free the bitmap information */
@@ -244,22 +269,21 @@ inline DWORD BiosGetVideoMemoryStart()
 
 inline VOID BiosVerticalRefresh()
 {
-    SMALL_RECT Region;
-
     /* Ignore if we're in text mode */
     if (VideoModes[CurrentVideoMode].Text) return;
 
-    /* Fill the rectangle structure */
-    Region.Left = Region.Top = 0;
-    Region.Right = VideoModes[CurrentVideoMode].Width;
-    Region.Bottom = VideoModes[CurrentVideoMode].Height;
+    /* Ignore if there's nothing to update */
+    if (!VideoNeedsUpdate) return;
 
     /* Redraw the screen */
     InvalidateConsoleDIBits(ConsoleBuffers[CurrentVideoPage],
-                            &Region);
+                            &UpdateRectangle);
+
+    /* Clear the update flag */
+    VideoNeedsUpdate = FALSE;
 }
 
-BOOLEAN BiosInitialize(HANDLE ConsoleInput, HANDLE ConsoleOutput)
+BOOLEAN BiosInitialize()
 {
     INT i;
     WORD Offset = 0;
@@ -290,15 +314,41 @@ BOOLEAN BiosInitialize(HANDLE ConsoleInput, HANDLE ConsoleOutput)
         BiosCode[Offset++] = 0xCF; // iret
     }
 
-    /* Set global console I/O handles */
-    BiosConsoleInput = ConsoleInput;
-    BiosConsoleOutput = ConsoleOutput;
+    /* Get the input and output handles to the real console */
+    BiosConsoleInput = CreateFile(TEXT("CONIN$"),
+                                  GENERIC_READ | GENERIC_WRITE,
+                                  FILE_SHARE_READ | FILE_SHARE_WRITE,
+                                  NULL,
+                                  OPEN_EXISTING,
+                                  0,
+                                  NULL);
+
+    BiosConsoleOutput = CreateFile(TEXT("CONOUT$"),
+                                   GENERIC_READ | GENERIC_WRITE,
+                                   FILE_SHARE_READ | FILE_SHARE_WRITE,
+                                   NULL,
+                                   OPEN_EXISTING,
+                                   0,
+                                   NULL);
+
+    /* Make sure it was successful */
+    if ((BiosConsoleInput == INVALID_HANDLE_VALUE)
+        || (BiosConsoleOutput == INVALID_HANDLE_VALUE))
+    {
+        return FALSE;
+    }
 
+    /* Save the console screen buffer information */
+    if (!GetConsoleScreenBufferInfo(BiosConsoleOutput, &BiosSavedBufferInfo))
+    {
+        return FALSE;
+    }
+    
     /* Set the default video mode */
     BiosSetVideoMode(BIOS_DEFAULT_VIDEO_MODE);
 
     /* Set the console input mode */
-    SetConsoleMode(ConsoleInput, ENABLE_MOUSE_INPUT | ENABLE_PROCESSED_INPUT);
+    SetConsoleMode(BiosConsoleInput, ENABLE_MOUSE_INPUT | ENABLE_PROCESSED_INPUT);
 
     /* Initialize the PIC */
     PicWriteCommand(PIC_MASTER_CMD, PIC_ICW1 | PIC_ICW1_ICW4);
@@ -327,6 +377,28 @@ BOOLEAN BiosInitialize(HANDLE ConsoleInput, HANDLE ConsoleOutput)
     return TRUE;
 }
 
+VOID BiosCleanup()
+{
+    INT i;
+
+    /* Restore the old screen buffer */
+    SetConsoleActiveScreenBuffer(BiosConsoleOutput);
+
+    /* Restore the screen buffer size */
+    SetConsoleScreenBufferSize(BiosConsoleOutput, BiosSavedBufferInfo.dwSize);
+
+    /* Free the buffers */
+    for (i = 0; i < VideoModes[CurrentVideoMode].Pages; i++)
+    {
+        if (ConsoleBuffers[i] != NULL) CloseHandle(ConsoleBuffers[i]);
+        if (!VideoModes[CurrentVideoMode].Text) CloseHandle(ConsoleMutexes[i]);
+    }
+
+    /* Close the console handles */
+    if (BiosConsoleInput != INVALID_HANDLE_VALUE) CloseHandle(BiosConsoleInput);
+    if (BiosConsoleOutput != INVALID_HANDLE_VALUE) CloseHandle(BiosConsoleOutput);
+}
+
 VOID BiosUpdateConsole(ULONG StartAddress, ULONG EndAddress)
 {
     ULONG i;
@@ -367,11 +439,40 @@ VOID BiosUpdateConsole(ULONG StartAddress, ULONG EndAddress)
     }
     else
     {
+        /* Wait for the mutex object */
+        WaitForSingleObject(ConsoleMutexes[CurrentVideoPage], INFINITE);
+
         /* Copy the data to the framebuffer */
         RtlCopyMemory((LPVOID)((ULONG_PTR)ConsoleFramebuffers[CurrentVideoPage]
                       + StartAddress - BiosGetVideoMemoryStart()),
                       (LPVOID)((ULONG_PTR)BaseAddress + StartAddress),
                       EndAddress - StartAddress);
+
+        /* Release the mutex */
+        ReleaseMutex(ConsoleMutexes[CurrentVideoPage]);
+
+        /* Check if this is the first time the rectangle is updated */
+        if (!VideoNeedsUpdate)
+        {
+            UpdateRectangle.Left = UpdateRectangle.Top = (SHORT)0x7FFF;
+            UpdateRectangle.Right = UpdateRectangle.Bottom = (SHORT)0x8000;
+        }
+
+        /* Expand the update rectangle */
+        for (i = StartAddress; i < EndAddress; i++)
+        {
+            /* Get the coordinates */
+            Coordinates = BiosVideoAddressToCoord(i);
+
+            /* Expand the rectangle to include the point */
+            UpdateRectangle.Left = min(UpdateRectangle.Left, Coordinates.X);
+            UpdateRectangle.Right = max(UpdateRectangle.Right, Coordinates.X);
+            UpdateRectangle.Top = min(UpdateRectangle.Top, Coordinates.Y);
+            UpdateRectangle.Bottom = max(UpdateRectangle.Bottom, Coordinates.Y);
+        }
+
+        /* Set the update flag */
+        VideoNeedsUpdate = TRUE;
     }
 }
 
@@ -415,11 +516,17 @@ VOID BiosUpdateVideoMemory(ULONG StartAddress, ULONG EndAddress)
     }
     else
     {
+        /* Wait for the mutex object */
+        WaitForSingleObject(ConsoleMutexes[CurrentVideoPage], INFINITE);
+
         /* Copy the data to the emulator memory */
         RtlCopyMemory((LPVOID)((ULONG_PTR)BaseAddress + StartAddress),
                       (LPVOID)((ULONG_PTR)ConsoleFramebuffers[CurrentVideoPage]
                       + StartAddress - BiosGetVideoMemoryStart()),
                       EndAddress - StartAddress);
+
+        /* Release the mutex */
+        ReleaseMutex(ConsoleMutexes[CurrentVideoPage]);
     }
 }
 
@@ -736,6 +843,12 @@ VOID BiosTimeService()
     }
 }
 
+VOID BiosSystemTimerInterrupt()
+{
+    /* Increase the system tick count */
+    BiosTickCount++;
+}
+
 VOID BiosEquipmentService()
 {
     /* Return the equipment list */
@@ -749,11 +862,8 @@ VOID BiosHandleIrq(BYTE IrqNumber)
         /* PIT IRQ */
         case 0:
         {
-            /* Increase the system tick count */
-            BiosTickCount++;
-
             /* Perform the system timer interrupt */
-            EmulatorInterrupt(0x1C);
+            EmulatorInterrupt(BIOS_SYS_TIMER_INTERRUPT);
 
             break;
         }
index 34f9f5b..04cd593 100644 (file)
@@ -25,6 +25,7 @@
 #define BIOS_EQUIPMENT_INTERRUPT 0x11
 #define BIOS_KBD_INTERRUPT 0x16
 #define BIOS_TIME_INTERRUPT 0x1A
+#define BIOS_SYS_TIMER_INTERRUPT 0x1C
 #define CONSOLE_FONT_HEIGHT 8
 #define BIOS_KBD_BUFFER_SIZE 256
 #define BIOS_EQUIPMENT_LIST 0x3C // HACK: Disable FPU for now
@@ -46,6 +47,7 @@ typedef struct
 /* FUNCTIONS ******************************************************************/
 
 BOOLEAN BiosInitialize();
+VOID BiosCleanup();
 VOID BiosUpdateConsole(ULONG StartAddress, ULONG EndAddress);
 VOID BiosUpdateVideoMemory(ULONG StartAddress, ULONG EndAddress);
 inline DWORD BiosGetVideoMemoryStart();
@@ -57,5 +59,6 @@ VOID BiosEquipmentService();
 VOID BiosKeyboardService();
 VOID BiosTimeService();
 VOID BiosHandleIrq(BYTE IrqNumber);
+VOID BiosSystemTimerInterrupt();
 
 #endif
index fb26ee1..af4a727 100644 (file)
@@ -234,6 +234,12 @@ static VOID EmulatorSoftwareInt(PVOID Context, BYTE Number)
                 BiosTimeService();
                 break;
             }
+            case BIOS_SYS_TIMER_INTERRUPT:
+            {
+                /* BIOS timer update */
+                BiosSystemTimerInterrupt();
+                break;
+            }
             case 0x20:
             {
                 DosInt20h(CodeSegment);
index 300a89c..ca5ef84 100644 (file)
@@ -76,44 +76,10 @@ INT wmain(INT argc, WCHAR *argv[])
     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;
 
     /* 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))
-    {
-        wprintf(L"FATAL: Could not get handles to the console\n");
-        goto Cleanup;
-    }
-
-    /* Save the console screen buffer information */
-    if (!GetConsoleScreenBufferInfo(ConsoleOutput, &SavedBufferInfo))
-    {
-        wprintf(L"FATAL: Could not save the console screen buffer information\n");
-        goto Cleanup;
-    }
-
     /* The DOS command line must be ASCII */
     WideCharToMultiByte(CP_ACP, 0, GetCommandLine(), -1, CommandLine, 128, NULL, NULL);
 
@@ -131,7 +97,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;
@@ -201,18 +167,9 @@ INT wmain(INT argc, WCHAR *argv[])
     }
 
 Cleanup:
-    /* Restore the old screen buffer */
-    SetConsoleActiveScreenBuffer(ConsoleOutput);
-
-    /* Restore the screen buffer size */
-    SetConsoleScreenBufferSize(ConsoleOutput, SavedBufferInfo.dwSize);
-
+    BiosCleanup();
     EmulatorCleanup();
 
-    /* Close the console handles */
-    if (ConsoleInput != INVALID_HANDLE_VALUE) CloseHandle(ConsoleInput);
-    if (ConsoleOutput != INVALID_HANDLE_VALUE) CloseHandle(ConsoleOutput);
-
     return 0;
 }