[NTVDM]
[reactos.git] / subsystems / ntvdm / vga.c
index bfff763..d4616a7 100644 (file)
 
 /* PRIVATE VARIABLES **********************************************************/
 
-static CONST DWORD MemoryBase[] = { 0xA0000, 0xA0000, 0xB0000, 0xB8000 };
+static CONST DWORD MemoryBase[]  = { 0xA0000, 0xA0000, 0xB0000, 0xB8000 };
 static CONST DWORD MemoryLimit[] = { 0xAFFFF, 0xAFFFF, 0xB7FFF, 0xBFFFF };
 
+static CONST COLORREF VgaDefaultPalette[VGA_MAX_COLORS] = 
+{
+    RGB(0x00, 0x00, 0x00), RGB(0x00, 0x00, 0xAA), RGB(0x00, 0xAA, 0x00), RGB(0x00, 0xAA, 0xAA),
+    RGB(0xAA, 0x00, 0x00), RGB(0xAA, 0x00, 0xAA), RGB(0xAA, 0x55, 0x00), RGB(0xAA, 0xAA, 0xAA),
+    RGB(0x55, 0x55, 0x55), RGB(0x55, 0x55, 0xFF), RGB(0x55, 0xFF, 0x55), RGB(0x55, 0xFF, 0xFF),
+    RGB(0xFF, 0x55, 0x55), RGB(0xFF, 0x55, 0xFF), RGB(0xFF, 0xFF, 0x55), RGB(0xFF, 0xFF, 0xFF),
+    RGB(0x00, 0x00, 0x00), RGB(0x10, 0x10, 0x10), RGB(0x20, 0x20, 0x20), RGB(0x35, 0x35, 0x35),
+    RGB(0x45, 0x45, 0x45), RGB(0x55, 0x55, 0x55), RGB(0x65, 0x65, 0x65), RGB(0x75, 0x75, 0x75),
+    RGB(0x8A, 0x8A, 0x8A), RGB(0x9A, 0x9A, 0x9A), RGB(0xAA, 0xAA, 0xAA), RGB(0xBA, 0xBA, 0xBA),
+    RGB(0xCA, 0xCA, 0xCA), RGB(0xDF, 0xDF, 0xDF), RGB(0xEF, 0xEF, 0xEF), RGB(0xFF, 0xFF, 0xFF),
+    RGB(0x00, 0x00, 0xFF), RGB(0x41, 0x00, 0xFF), RGB(0x82, 0x00, 0xFF), RGB(0xBE, 0x00, 0xFF),
+    RGB(0xFF, 0x00, 0xFF), RGB(0xFF, 0x00, 0xBE), RGB(0xFF, 0x00, 0x82), RGB(0xFF, 0x00, 0x41),
+    RGB(0xFF, 0x00, 0x00), RGB(0xFF, 0x41, 0x00), RGB(0xFF, 0x82, 0x00), RGB(0xFF, 0xBE, 0x00),
+    RGB(0xFF, 0xFF, 0x00), RGB(0xBE, 0xFF, 0x00), RGB(0x82, 0xFF, 0x00), RGB(0x41, 0xFF, 0x00),
+    RGB(0x00, 0xFF, 0x00), RGB(0x00, 0xFF, 0x41), RGB(0x00, 0xFF, 0x82), RGB(0x00, 0xFF, 0xBE),
+    RGB(0x00, 0xFF, 0xFF), RGB(0x00, 0xBE, 0xFF), RGB(0x00, 0x82, 0xFF), RGB(0x00, 0x41, 0xFF),
+    RGB(0x82, 0x82, 0xFF), RGB(0x9E, 0x82, 0xFF), RGB(0xBE, 0x82, 0xFF), RGB(0xDF, 0x82, 0xFF),
+    RGB(0xFF, 0x82, 0xFF), RGB(0xFF, 0x82, 0xDF), RGB(0xFF, 0x82, 0xBE), RGB(0xFF, 0x82, 0x9E),
+    RGB(0xFF, 0x82, 0x82), RGB(0xFF, 0x9E, 0x82), RGB(0xFF, 0xBE, 0x82), RGB(0xFF, 0xDF, 0x82),
+    RGB(0xFF, 0xFF, 0x82), RGB(0xDF, 0xFF, 0x82), RGB(0xBE, 0xFF, 0x82), RGB(0x9E, 0xFF, 0x82),
+    RGB(0x82, 0xFF, 0x82), RGB(0x82, 0xFF, 0x9E), RGB(0x82, 0xFF, 0xBE), RGB(0x82, 0xFF, 0xDF),
+    RGB(0x82, 0xFF, 0xFF), RGB(0x82, 0xDF, 0xFF), RGB(0x82, 0xBE, 0xFF), RGB(0x82, 0x9E, 0xFF),
+    RGB(0xBA, 0xBA, 0xFF), RGB(0xCA, 0xBA, 0xFF), RGB(0xDF, 0xBA, 0xFF), RGB(0xEF, 0xBA, 0xFF),
+    RGB(0xFF, 0xBA, 0xFF), RGB(0xFF, 0xBA, 0xEF), RGB(0xFF, 0xBA, 0xDF), RGB(0xFF, 0xBA, 0xCA),
+    RGB(0xFF, 0xBA, 0xBA), RGB(0xFF, 0xCA, 0xBA), RGB(0xFF, 0xDF, 0xBA), RGB(0xFF, 0xEF, 0xBA),
+    RGB(0xFF, 0xFF, 0xBA), RGB(0xEF, 0xFF, 0xBA), RGB(0xDF, 0xFF, 0xBA), RGB(0xCA, 0xFF, 0xBA),
+    RGB(0xBA, 0xFF, 0xBA), RGB(0xBA, 0xFF, 0xCA), RGB(0xBA, 0xFF, 0xDF), RGB(0xBA, 0xFF, 0xEF),
+    RGB(0xBA, 0xFF, 0xFF), RGB(0xBA, 0xEF, 0xFF), RGB(0xBA, 0xDF, 0xFF), RGB(0xBA, 0xCA, 0xFF),
+    RGB(0x00, 0x00, 0x71), RGB(0x1C, 0x00, 0x71), RGB(0x39, 0x00, 0x71), RGB(0x55, 0x00, 0x71),
+    RGB(0x71, 0x00, 0x71), RGB(0x71, 0x00, 0x55), RGB(0x71, 0x00, 0x39), RGB(0x71, 0x00, 0x1C),
+    RGB(0x71, 0x00, 0x00), RGB(0x71, 0x1C, 0x00), RGB(0x71, 0x39, 0x00), RGB(0x71, 0x55, 0x00),
+    RGB(0x71, 0x71, 0x00), RGB(0x55, 0x71, 0x00), RGB(0x39, 0x71, 0x00), RGB(0x1C, 0x71, 0x00),
+    RGB(0x00, 0x71, 0x00), RGB(0x00, 0x71, 0x1C), RGB(0x00, 0x71, 0x39), RGB(0x00, 0x71, 0x55),
+    RGB(0x00, 0x71, 0x71), RGB(0x00, 0x55, 0x71), RGB(0x00, 0x39, 0x71), RGB(0x00, 0x1C, 0x71),
+    RGB(0x39, 0x39, 0x71), RGB(0x45, 0x39, 0x71), RGB(0x55, 0x39, 0x71), RGB(0x61, 0x39, 0x71),
+    RGB(0x71, 0x39, 0x71), RGB(0x71, 0x39, 0x61), RGB(0x71, 0x39, 0x55), RGB(0x71, 0x39, 0x45),
+    RGB(0x71, 0x39, 0x39), RGB(0x71, 0x45, 0x39), RGB(0x71, 0x55, 0x39), RGB(0x71, 0x61, 0x39),
+    RGB(0x71, 0x71, 0x39), RGB(0x61, 0x71, 0x39), RGB(0x55, 0x71, 0x39), RGB(0x45, 0x71, 0x39),
+    RGB(0x39, 0x71, 0x39), RGB(0x39, 0x71, 0x45), RGB(0x39, 0x71, 0x55), RGB(0x39, 0x71, 0x61),
+    RGB(0x39, 0x71, 0x71), RGB(0x39, 0x61, 0x71), RGB(0x39, 0x55, 0x71), RGB(0x39, 0x45, 0x71),
+    RGB(0x51, 0x51, 0x71), RGB(0x59, 0x51, 0x71), RGB(0x61, 0x51, 0x71), RGB(0x69, 0x51, 0x71),
+    RGB(0x71, 0x51, 0x71), RGB(0x71, 0x51, 0x69), RGB(0x71, 0x51, 0x61), RGB(0x71, 0x51, 0x59),
+    RGB(0x71, 0x51, 0x51), RGB(0x71, 0x59, 0x51), RGB(0x71, 0x61, 0x51), RGB(0x71, 0x69, 0x51),
+    RGB(0x71, 0x71, 0x51), RGB(0x69, 0x71, 0x51), RGB(0x61, 0x71, 0x51), RGB(0x59, 0x71, 0x51),
+    RGB(0x51, 0x71, 0x51), RGB(0x51, 0x71, 0x59), RGB(0x51, 0x71, 0x61), RGB(0x51, 0x71, 0x69),
+    RGB(0x51, 0x71, 0x71), RGB(0x51, 0x69, 0x71), RGB(0x51, 0x61, 0x71), RGB(0x51, 0x59, 0x71),
+    RGB(0x00, 0x00, 0x41), RGB(0x10, 0x00, 0x41), RGB(0x20, 0x00, 0x41), RGB(0x31, 0x00, 0x41),
+    RGB(0x41, 0x00, 0x41), RGB(0x41, 0x00, 0x31), RGB(0x41, 0x00, 0x20), RGB(0x41, 0x00, 0x10),
+    RGB(0x41, 0x00, 0x00), RGB(0x41, 0x10, 0x00), RGB(0x41, 0x20, 0x00), RGB(0x41, 0x31, 0x00),
+    RGB(0x41, 0x41, 0x00), RGB(0x31, 0x41, 0x00), RGB(0x20, 0x41, 0x00), RGB(0x10, 0x41, 0x00),
+    RGB(0x00, 0x41, 0x00), RGB(0x00, 0x41, 0x10), RGB(0x00, 0x41, 0x20), RGB(0x00, 0x41, 0x31),
+    RGB(0x00, 0x41, 0x41), RGB(0x00, 0x31, 0x41), RGB(0x00, 0x20, 0x41), RGB(0x00, 0x10, 0x41),
+    RGB(0x20, 0x20, 0x41), RGB(0x28, 0x20, 0x41), RGB(0x31, 0x20, 0x41), RGB(0x39, 0x20, 0x41),
+    RGB(0x41, 0x20, 0x41), RGB(0x41, 0x20, 0x39), RGB(0x41, 0x20, 0x31), RGB(0x41, 0x20, 0x28),
+    RGB(0x41, 0x20, 0x20), RGB(0x41, 0x28, 0x20), RGB(0x41, 0x31, 0x20), RGB(0x41, 0x39, 0x20),
+    RGB(0x41, 0x41, 0x20), RGB(0x39, 0x41, 0x20), RGB(0x31, 0x41, 0x20), RGB(0x28, 0x41, 0x20),
+    RGB(0x20, 0x41, 0x20), RGB(0x20, 0x41, 0x28), RGB(0x20, 0x41, 0x31), RGB(0x20, 0x41, 0x39),
+    RGB(0x20, 0x41, 0x41), RGB(0x20, 0x39, 0x41), RGB(0x20, 0x31, 0x41), RGB(0x20, 0x28, 0x41),
+    RGB(0x2D, 0x2D, 0x41), RGB(0x31, 0x2D, 0x41), RGB(0x35, 0x2D, 0x41), RGB(0x3D, 0x2D, 0x41),
+    RGB(0x41, 0x2D, 0x41), RGB(0x41, 0x2D, 0x3D), RGB(0x41, 0x2D, 0x35), RGB(0x41, 0x2D, 0x31),
+    RGB(0x41, 0x2D, 0x2D), RGB(0x41, 0x31, 0x2D), RGB(0x41, 0x35, 0x2D), RGB(0x41, 0x3D, 0x2D),
+    RGB(0x41, 0x41, 0x2D), RGB(0x3D, 0x41, 0x2D), RGB(0x35, 0x41, 0x2D), RGB(0x31, 0x41, 0x2D),
+    RGB(0x2D, 0x41, 0x2D), RGB(0x2D, 0x41, 0x31), RGB(0x2D, 0x41, 0x35), RGB(0x2D, 0x41, 0x3D),
+    RGB(0x2D, 0x41, 0x41), RGB(0x2D, 0x3D, 0x41), RGB(0x2D, 0x35, 0x41), RGB(0x2D, 0x31, 0x41),
+    RGB(0x00, 0x00, 0x00), RGB(0x00, 0x00, 0x00), RGB(0x00, 0x00, 0x00), RGB(0x00, 0x00, 0x00),
+    RGB(0x00, 0x00, 0x00), RGB(0x00, 0x00, 0x00), RGB(0x00, 0x00, 0x00), RGB(0x00, 0x00, 0x00)
+};
+
 static BYTE VgaMemory[VGA_NUM_BANKS * VGA_BANK_SIZE];
+static BYTE VgaLatchRegisters[VGA_NUM_BANKS] = {0, 0, 0, 0};
 static BYTE VgaMiscRegister;
 static BYTE VgaSeqIndex = VGA_SEQ_RESET_REG;
 static BYTE VgaSeqRegisters[VGA_SEQ_MAX_REG];
@@ -29,9 +98,10 @@ static BYTE VgaCrtcRegisters[VGA_CRTC_MAX_REG];
 static BYTE VgaAcIndex = VGA_AC_PAL_0_REG;
 static BOOLEAN VgaAcLatch = FALSE;
 static BYTE VgaAcRegisters[VGA_AC_MAX_REG];
-static BYTE VgaDacIndex = 0;
+static WORD VgaDacIndex = 0;
 static BOOLEAN VgaDacReadWrite = FALSE;
 static BYTE VgaDacRegisters[VGA_PALETTE_SIZE];
+static HPALETTE PaletteHandle = NULL;
 static BOOLEAN InVerticalRetrace = FALSE;
 static BOOLEAN InHorizontalRetrace = FALSE;
 static HANDLE TextConsoleBuffer = NULL;
@@ -41,6 +111,7 @@ static HANDLE ConsoleMutex = NULL;
 static BOOLEAN NeedsUpdate = FALSE;
 static BOOLEAN ModeChanged = TRUE;
 static BOOLEAN CursorMoved = FALSE;
+static BOOLEAN PaletteChanged = FALSE;
 static BOOLEAN TextMode = TRUE;
 static SMALL_RECT UpdateRectangle = { 0, 0, 0, 0 };
 
@@ -117,6 +188,66 @@ static inline DWORD VgaTranslateWriteAddress(DWORD Address)
     return Offset;
 }
 
+static inline BYTE VgaTranslateByteForWriting(BYTE Data, BYTE Plane)
+{
+    BYTE WriteMode = VgaGcRegisters[VGA_GC_MODE_REG] & 3;
+    BYTE LogicalOperation = (VgaGcRegisters[VGA_GC_ROTATE_REG] >> 3) & 3;
+    BYTE RotateCount = VgaGcRegisters[VGA_GC_ROTATE_REG] & 7;
+    BYTE BitMask = VgaGcRegisters[VGA_GC_BITMASK_REG];
+
+    if (WriteMode == 1)
+    {
+        /* In write mode 1 just return the latch register */
+        return VgaLatchRegisters[Plane];
+    }
+
+    if (WriteMode != 2)
+    {
+        /* Write modes 0 and 3 rotate the data to the right first */
+        Data = LOBYTE(((DWORD)Data >> RotateCount) | ((DWORD)Data << (8 - RotateCount)));
+    }
+    else
+    {
+        /* Write mode 2 expands the appropriate bit to all 8 bits */
+        Data = (Data & (1 << Plane)) ? 0xFF : 0x00;
+    }
+
+    if (WriteMode == 0)
+    {
+        /*
+         * In write mode 0, the enable set/reset register decides if the
+         * set/reset bit should be expanded to all 8 bits.
+         */
+        if (VgaGcRegisters[VGA_GC_ENABLE_RESET_REG] & (1 << Plane))
+        {
+            /* Copy the bit from the set/reset register to all 8 bits */
+            Data = (VgaGcRegisters[VGA_GC_RESET_REG] & (1 << Plane)) ? 0xFF : 0x00;
+        }
+    }
+
+    if (WriteMode != 3)
+    {
+        /* Write modes 0 and 2 then perform a logical operation on the data and latch */
+        if (LogicalOperation == 1) Data &= VgaLatchRegisters[Plane];
+        else if (LogicalOperation == 2) Data |= VgaLatchRegisters[Plane];
+        else if (LogicalOperation == 3) Data ^= VgaLatchRegisters[Plane];
+    }
+    else
+    {
+        /* For write mode 3, we AND the bitmask with the data, which is used as the new bitmask */
+        BitMask &= Data;
+
+        /* Then we expand the bit in the set/reset field */
+        Data = (VgaGcRegisters[VGA_GC_RESET_REG] & (1 << Plane)) ? 0xFF : 0x00;
+    }
+
+    /* Bits cleared in the bitmask are replaced with latch register bits */
+    Data = (Data & BitMask) | (VgaLatchRegisters[Plane] & (~BitMask));
+
+    /* Return the byte */
+    return Data;
+}
+
 static inline VOID VgaMarkForUpdate(SHORT Row, SHORT Column)
 {
     DPRINT("VgaMarkForUpdate: Row %d, Column %d\n", Row, Column);
@@ -201,11 +332,30 @@ static VOID VgaWriteCrtc(BYTE Data)
 
 static VOID VgaWriteDac(BYTE Data)
 {
+    INT PaletteIndex;
+    PALETTEENTRY Entry;
+
     /* Set the value */
-    VgaDacRegisters[VgaDacIndex++] = Data;
-    VgaDacIndex %= VGA_PALETTE_SIZE;
+    VgaDacRegisters[VgaDacIndex] = Data;
+
+    /* Find the palette index */
+    PaletteIndex = VgaDacIndex / 3;
+
+    /* Fill the entry structure */
+    Entry.peRed = VGA_DAC_TO_COLOR(VgaDacRegisters[PaletteIndex * 3]);
+    Entry.peGreen = VGA_DAC_TO_COLOR(VgaDacRegisters[PaletteIndex * 3 + 1]);
+    Entry.peBlue = VGA_DAC_TO_COLOR(VgaDacRegisters[PaletteIndex * 3 + 2]);
+    Entry.peFlags = 0;
+
+    /* Update the palette entry */
+    SetPaletteEntries(PaletteHandle, PaletteIndex, 1, &Entry);
+
+    /* Set the palette change flag */
+    PaletteChanged = TRUE;
 
-    // TODO: Change the palette!
+    /* Update the index */
+    VgaDacIndex++;
+    VgaDacIndex %= VGA_PALETTE_SIZE;
 }
 
 static VOID VgaWriteAc(BYTE Data)
@@ -222,7 +372,7 @@ static BOOL VgaEnterGraphicsMode(PCOORD Resolution)
     CONSOLE_GRAPHICS_BUFFER_INFO GraphicsBufferInfo;
     BYTE BitmapInfoBuffer[VGA_BITMAP_INFO_SIZE];
     LPBITMAPINFO BitmapInfo = (LPBITMAPINFO)BitmapInfoBuffer;
-    LPWORD PaletteIndex = (LPWORD)(BitmapInfoBuffer + sizeof(BITMAPINFOHEADER));
+    LPWORD PaletteIndex = (LPWORD)(BitmapInfo->bmiColors);
 
     /* Fill the bitmap info header */
     ZeroMemory(&BitmapInfo->bmiHeader, sizeof(BITMAPINFOHEADER));
@@ -232,7 +382,7 @@ static BOOL VgaEnterGraphicsMode(PCOORD Resolution)
     BitmapInfo->bmiHeader.biBitCount = 8;
     BitmapInfo->bmiHeader.biPlanes = 1;
     BitmapInfo->bmiHeader.biCompression = BI_RGB;
-    BitmapInfo->bmiHeader.biSizeImage = Resolution->X * Resolution->Y;
+    BitmapInfo->bmiHeader.biSizeImage = Resolution->X * Resolution->Y /* * 1 == biBitCount / 8 */;
 
     /* Fill the palette data */
     for (i = 0; i < (VGA_PALETTE_SIZE / 3); i++) PaletteIndex[i] = (WORD)i;
@@ -260,16 +410,28 @@ static BOOL VgaEnterGraphicsMode(PCOORD Resolution)
     /* Set the active buffer */
     SetConsoleActiveScreenBuffer(GraphicsConsoleBuffer);
 
+    /* Set the graphics mode palette */
+    SetConsolePalette(GraphicsConsoleBuffer,
+                      PaletteHandle,
+                      SYSPAL_NOSTATIC256);
+
+    /* Clear the text mode flag */
+    TextMode = FALSE;
+
     return TRUE;
 }
 
 static VOID VgaLeaveGraphicsMode(VOID)
 {
+    /* Release the console framebuffer mutex if needed */
+    ReleaseMutex(ConsoleMutex);
+
     /* Switch back to the text buffer */
     SetConsoleActiveScreenBuffer(TextConsoleBuffer);
 
     /* Cleanup the video data */
     CloseHandle(ConsoleMutex);
+    ConsoleMutex = NULL;
     CloseHandle(GraphicsConsoleBuffer);
     GraphicsConsoleBuffer = NULL;
 }
@@ -291,6 +453,9 @@ static BOOL VgaEnterTextMode(PCOORD Resolution)
         return FALSE;
     }
 
+    /* Set the text mode flag */
+    TextMode = TRUE;
+
     return TRUE;
 }
 
@@ -301,10 +466,13 @@ static VOID VgaLeaveTextMode(VOID)
     ConsoleFramebuffer = NULL;
 }
 
-static VOID VgaUpdateMode(VOID)
+static VOID VgaChangeMode(VOID)
 {
     COORD Resolution = VgaGetDisplayResolution();
 
+    /* Reset the mode change flag */
+    // ModeChanged = FALSE;
+
     if (!TextMode)
     {
         /* Leave the current graphics mode */
@@ -320,26 +488,33 @@ static VOID VgaUpdateMode(VOID)
     if (!(VgaGcRegisters[VGA_GC_MISC_REG] & VGA_GC_MISC_NOALPHA))
     {
         /* Enter new text mode */
-        if (!VgaEnterTextMode(&Resolution)) return;
-
-        /* Set the text mode flag */
-        TextMode = TRUE;
+        if (!VgaEnterTextMode(&Resolution))
+        {
+            DisplayMessage(L"An unexpected VGA error occurred while switching into text mode.");
+            VdmRunning = FALSE;
+            return;
+        }
     }
     else
     {
         /* Enter 8-bit graphics mode */
-        if (!VgaEnterGraphicsMode(&Resolution)) return;
-
-        /* Clear the text mode flag */
-        TextMode = FALSE;
+        if (!VgaEnterGraphicsMode(&Resolution))
+        {
+            DisplayMessage(L"An unexpected VGA error occurred while switching into graphics mode.");
+            VdmRunning = FALSE;
+            return;
+        }
     }
 
-    /* Perform a full update */
+    /* Trigger a full update of the screen */
     NeedsUpdate = TRUE;
     UpdateRectangle.Left = 0;
     UpdateRectangle.Top = 0;
     UpdateRectangle.Right = Resolution.X;
     UpdateRectangle.Bottom = Resolution.Y;
+
+    /* Reset the mode change flag */
+    ModeChanged = FALSE;
 }
 
 static VOID VgaUpdateFramebuffer(VOID)
@@ -350,17 +525,28 @@ static VOID VgaUpdateFramebuffer(VOID)
     DWORD Address = (VgaCrtcRegisters[VGA_CRTC_START_ADDR_HIGH_REG] << 8)
                     + VgaCrtcRegisters[VGA_CRTC_START_ADDR_LOW_REG];
     DWORD ScanlineSize = (DWORD)VgaCrtcRegisters[VGA_CRTC_OFFSET_REG] * 2;
-    PCHAR_INFO CharBuffer = (PCHAR_INFO)ConsoleFramebuffer;
-    PBYTE GraphicsBuffer = (PBYTE)ConsoleFramebuffer;
 
-    /* Loop through the scanlines */
-    for (i = 0; i < Resolution.Y; i++)
+    /*
+     * If console framebuffer is NULL, that means something went wrong
+     * earlier and this is the final display refresh.
+     */
+    if (ConsoleFramebuffer == NULL) return;
+
+    /* Check if this is text mode or graphics mode */
+    if (VgaGcRegisters[VGA_GC_MISC_REG] & VGA_GC_MISC_NOALPHA)
     {
-        /* Check if this is text mode or graphics mode */
-        if (VgaGcRegisters[VGA_GC_MISC_REG] & VGA_GC_MISC_NOALPHA)
-        {
-            /* Graphics mode */
+        /* Graphics mode */
+        PBYTE GraphicsBuffer = (PBYTE)ConsoleFramebuffer;
 
+        /*
+         * Synchronize access to the graphics framebuffer
+         * with the console framebuffer mutex.
+         */
+        WaitForSingleObject(ConsoleMutex, INFINITE);
+
+        /* Loop through the scanlines */
+        for (i = 0; i < Resolution.Y; i++)
+        {
             /* Loop through the pixels */
             for (j = 0; j < Resolution.X; j++)
             {
@@ -402,13 +588,30 @@ static VOID VgaUpdateFramebuffer(VOID)
                 }
                 else if (VgaGcRegisters[VGA_GC_MODE_REG] & VGA_GC_MODE_SHIFTREG)
                 {
-                    /*
-                     * 2 bits shifted from plane 0 and 2 for the first 4 pixels,
-                     * then 2 bits shifted from plane 1 and 3 for the next 4
-                     */
+                    /* Check if this is 16 or 256 color mode */
+                    if (VgaAcRegisters[VGA_AC_CONTROL_REG] & VGA_AC_CONTROL_8BIT)
+                    {
+                        // TODO: NOT IMPLEMENTED
+                        DPRINT1("8-bit interleaved mode is not implemented!\n");
+                    }
+                    else
+                    {
+                        /*
+                         * 2 bits shifted from plane 0 and 2 for the first 4 pixels,
+                         * then 2 bits shifted from plane 1 and 3 for the next 4
+                         */
+                        BYTE LowPlaneData = VgaMemory[((j / 4) % 2) * VGA_BANK_SIZE
+                                                      + (Address + (j / 4)) * AddressSize];
+                        BYTE HighPlaneData = VgaMemory[(((j / 4) % 2) + 2) * VGA_BANK_SIZE
+                                                       + (Address + (j / 4)) * AddressSize];
 
-                    // TODO: NOT IMPLEMENTED!
-                    DPRINT1("Interleaved shift mode is not implemented!\n");
+                        /* Extract the two bits from each plane */
+                        LowPlaneData = (LowPlaneData >> (6 - ((j % 4) * 2))) & 3;
+                        HighPlaneData = (HighPlaneData >> (6 - ((j % 4) * 2))) & 3;
+
+                        /* Combine them into the pixel */
+                        PixelData = LowPlaneData | (HighPlaneData << 2);
+                    }
                 }
                 else
                 {
@@ -450,6 +653,12 @@ static VOID VgaUpdateFramebuffer(VOID)
                     }
                 }
 
+                if (!(VgaAcRegisters[VGA_AC_CONTROL_REG] & VGA_AC_CONTROL_8BIT))
+                {
+                    /* In 16 color mode, the value is an index to the AC registers */
+                    PixelData = VgaAcRegisters[PixelData];
+                }
+
                 /* Now check if the resulting pixel data has changed */
                 if (GraphicsBuffer[i * Resolution.X + j] != PixelData)
                 {
@@ -460,11 +669,25 @@ static VOID VgaUpdateFramebuffer(VOID)
                     VgaMarkForUpdate(i, j);
                 }
             }
+
+            /* Move to the next scanline */
+            Address += ScanlineSize;
         }
-        else
-        {
-            /* Text mode */
 
+        /*
+         * Release the console framebuffer mutex
+         * so that we allow for repainting.
+         */
+        ReleaseMutex(ConsoleMutex);
+    }
+    else
+    {
+        /* Text mode */
+        PCHAR_INFO CharBuffer = (PCHAR_INFO)ConsoleFramebuffer;
+
+        /* Loop through the scanlines */
+        for (i = 0; i < Resolution.Y; i++)
+        {
             /* Loop through the characters */
             for (j = 0; j < Resolution.X; j++)
             {
@@ -484,14 +707,14 @@ static VOID VgaUpdateFramebuffer(VOID)
                     /* Yes, write the new value */
                     CharBuffer[i * Resolution.X + j] = CharInfo;
 
-                    /* Mark the specified pixel as changed */
+                    /* Mark the specified cell as changed */
                     VgaMarkForUpdate(i, j);
                 }
             }
-        }
 
-        /* Move to the next scanline */
-        Address += ScanlineSize;
+            /* Move to the next scanline */
+            Address += ScanlineSize;
+        }
     }
 }
 
@@ -529,6 +752,9 @@ static VOID VgaUpdateTextCursor(VOID)
     /* Update the physical cursor */
     SetConsoleCursorInfo(TextConsoleBuffer, &CursorInfo);
     SetConsoleCursorPosition(TextConsoleBuffer, Position);
+
+    /* Reset the cursor move flag */
+    CursorMoved = FALSE;
 }
 
 /* PUBLIC FUNCTIONS ***********************************************************/
@@ -546,6 +772,7 @@ DWORD VgaGetVideoLimitAddress(VOID)
 COORD VgaGetDisplayResolution(VOID)
 {
     COORD Resolution;
+    BYTE MaximumScanLine = 1 + (VgaCrtcRegisters[VGA_CRTC_MAX_SCAN_LINE_REG] & 0x1F);
 
     /* The low 8 bits are in the display registers */
     Resolution.X = VgaCrtcRegisters[VGA_CRTC_END_HORZ_DISP_REG];
@@ -573,16 +800,11 @@ COORD VgaGetDisplayResolution(VOID)
 
         /* The horizontal resolution is halved in 8-bit mode */
         if (VgaAcRegisters[VGA_AC_CONTROL_REG] & VGA_AC_CONTROL_8BIT) Resolution.X /= 2;
-
-        /* Divide the vertical resolution by the maximum scan line */
-        Resolution.Y /= ((DWORD)VgaCrtcRegisters[VGA_CRTC_MAX_SCAN_LINE_REG] & 0x1F) + 1;
-    }
-    else
-    {
-        /* Divide the number of scanlines by the font size */
-        Resolution.Y /= 16;
     }
 
+    /* Divide the vertical resolution by the maximum scan line (== font size in text mode) */
+    Resolution.Y /= MaximumScanLine;
+
     /* Return the resolution */
     return Resolution;
 }
@@ -593,22 +815,25 @@ VOID VgaRefreshDisplay(VOID)
 
     DPRINT("VgaRefreshDisplay\n");
 
-    if (ModeChanged)
-    {
-        /* Change the display mode */
-        VgaUpdateMode();
+    /* Change the display mode */
+    if (ModeChanged) VgaChangeMode();
 
-        /* Reset the mode change flag */
-        ModeChanged = FALSE;
-    }
+    /* Change the text cursor location */
+    if (CursorMoved) VgaUpdateTextCursor();
 
-    if (CursorMoved)
+    if (PaletteChanged)
     {
-        /* Change the text cursor location */
-        VgaUpdateTextCursor();
+        if (VgaGcRegisters[VGA_GC_MISC_REG] & VGA_GC_MISC_NOALPHA)
+        {
+            /* Trigger a full update of the screen */
+            NeedsUpdate = TRUE;
+            UpdateRectangle.Left = 0;
+            UpdateRectangle.Top = 0;
+            UpdateRectangle.Right = Resolution.X;
+            UpdateRectangle.Bottom = Resolution.Y;
+        }
 
-        /* Reset the cursor move flag */
-        CursorMoved = FALSE;
+        PaletteChanged = FALSE;
     }
 
     /* Update the contents of the framebuffer */
@@ -675,6 +900,12 @@ VOID VgaReadMemory(DWORD Address, LPBYTE Buffer, DWORD Size)
     {
         VideoAddress = VgaTranslateReadAddress(Address + i);
 
+        /* Load the latch registers */
+        VgaLatchRegisters[0] = VgaMemory[LOWORD(VideoAddress)];
+        VgaLatchRegisters[1] = VgaMemory[VGA_BANK_SIZE + LOWORD(VideoAddress)];
+        VgaLatchRegisters[2] = VgaMemory[(2 * VGA_BANK_SIZE) + LOWORD(VideoAddress)];
+        VgaLatchRegisters[3] = VgaMemory[(3 * VGA_BANK_SIZE) + LOWORD(VideoAddress)];
+
         /* Copy the value to the buffer */
         Buffer[i] = VgaMemory[VideoAddress];
     }
@@ -726,7 +957,7 @@ VOID VgaWriteMemory(DWORD Address, LPBYTE Buffer, DWORD Size)
             }
 
             /* Copy the value to the VGA memory */
-            VgaMemory[VideoAddress + j * VGA_BANK_SIZE] = Buffer[i];
+            VgaMemory[VideoAddress + j * VGA_BANK_SIZE] = VgaTranslateByteForWriting(Buffer[i], j);
         }
     }
 }
@@ -765,7 +996,7 @@ BYTE VgaReadPort(WORD Port)
 
         case VGA_DAC_WRITE_INDEX:
         {
-            return VgaDacIndex;
+            return VgaDacIndex / 3;
         }
 
         case VGA_DAC_DATA:
@@ -874,7 +1105,7 @@ VOID VgaWritePort(WORD Port, BYTE Data)
         case VGA_DAC_READ_INDEX:
         {
             VgaDacReadWrite = FALSE;
-            VgaDacIndex = Data % VGA_PALETTE_SIZE;
+            VgaDacIndex = Data * 3;
 
             break;
         }
@@ -882,7 +1113,7 @@ VOID VgaWritePort(WORD Port, BYTE Data)
         case VGA_DAC_WRITE_INDEX:
         {
             VgaDacReadWrite = TRUE;
-            VgaDacIndex = Data % VGA_PALETTE_SIZE;
+            VgaDacIndex = Data * 3;
 
             break;
         }
@@ -935,7 +1166,12 @@ VOID VgaWritePort(WORD Port, BYTE Data)
     }
 }
 
-VOID VgaInitialize(HANDLE TextHandle)
+VOID VgaClearMemory(VOID)
+{
+    ZeroMemory(VgaMemory, sizeof(VgaMemory));
+}
+
+BOOLEAN VgaInitialize(HANDLE TextHandle)
 {
     INT i, j;
     COORD Resolution;
@@ -946,17 +1182,17 @@ VOID VgaInitialize(HANDLE TextHandle)
     PCHAR_INFO CharBuffer;
     DWORD Address = 0;
     DWORD CurrentAddr;
+    LPLOGPALETTE Palette;
 
     /* Set the global handle */
     TextConsoleBuffer = TextHandle;
 
     /* Clear the VGA memory */
-    ZeroMemory(VgaMemory, VGA_NUM_BANKS * VGA_BANK_SIZE);
+    VgaClearMemory();
 
     /* Set the default video mode */
     BiosSetVideoMode(BIOS_DEFAULT_VIDEO_MODE);
-    VgaUpdateMode();
-    ModeChanged = FALSE;
+    VgaChangeMode();
 
     /* Get the data */
     Resolution = VgaGetDisplayResolution();
@@ -992,6 +1228,41 @@ VOID VgaInitialize(HANDLE TextHandle)
         /* Move to the next scanline */
         Address += ScanlineSize;
     }
+
+    /* Allocate storage space for the palette */
+    Palette = (LPLOGPALETTE)HeapAlloc(GetProcessHeap(),
+                                      HEAP_ZERO_MEMORY,
+                                      sizeof(LOGPALETTE)
+                                      + VGA_MAX_COLORS * sizeof(PALETTEENTRY));
+    if (Palette == NULL) return FALSE;
+
+    /* Initialize the palette */
+    Palette->palVersion = 0x0300;
+    Palette->palNumEntries = VGA_MAX_COLORS;
+
+    /* Copy the colors of the default palette to the DAC and console palette */
+    for (i = 0; i < VGA_MAX_COLORS; i++)
+    {
+        /* Set the palette entries */
+        Palette->palPalEntry[i].peRed = GetRValue(VgaDefaultPalette[i]);
+        Palette->palPalEntry[i].peGreen = GetGValue(VgaDefaultPalette[i]);
+        Palette->palPalEntry[i].peBlue = GetBValue(VgaDefaultPalette[i]);
+        Palette->palPalEntry[i].peFlags = 0;
+
+        /* Set the DAC registers */
+        VgaDacRegisters[i * 3] = VGA_COLOR_TO_DAC(GetRValue(VgaDefaultPalette[i]));
+        VgaDacRegisters[i * 3 + 1] = VGA_COLOR_TO_DAC(GetGValue(VgaDefaultPalette[i]));
+        VgaDacRegisters[i * 3 + 2] = VGA_COLOR_TO_DAC(GetBValue(VgaDefaultPalette[i]));
+    }
+
+    /* Create the palette */
+    PaletteHandle = CreatePalette(Palette);
+
+    /* Free the palette */
+    HeapFree(GetProcessHeap(), 0, Palette);
+
+    /* Return success if the palette was successfully created */
+    return (PaletteHandle ? TRUE : FALSE);
 }
 
 /* EOF */