[NTVDM]
[reactos.git] / reactos / subsystems / mvdm / ntvdm / hardware / video / svga.c
index f6f58b1..05fc21c 100644 (file)
@@ -1,16 +1,18 @@
 /*
  * COPYRIGHT:       GPL - See COPYING in the top level directory
  * PROJECT:         ReactOS Virtual DOS Machine
- * FILE:            svga.c
+ * FILE:            subsystems/mvdm/ntvdm/hardware/video/svga.c
  * PURPOSE:         SuperVGA hardware emulation (Cirrus Logic CL-GD5434 compatible)
  * PROGRAMMERS:     Aleksandar Andrejevic <theflash AT sdf DOT lonestar DOT org>
  */
 
 /* INCLUDES *******************************************************************/
 
+#include "ntvdm.h"
+
 #define NDEBUG
+#include <debug.h>
 
-#include "ntvdm.h"
 #include "emulator.h"
 #include "svga.h"
 #include <bios/vidbios.h>
 #include "io.h"
 #include "clock.h"
 
+#include "../../console/video.h"
+
 /* PRIVATE VARIABLES **********************************************************/
 
 static CONST DWORD MemoryBase[] = { 0xA0000, 0xA0000, 0xB0000, 0xB8000 };
 static CONST DWORD MemorySize[] = { 0x20000, 0x10000, 0x08000, 0x08000 };
 
-/*
- * Activate this line if you want to use the real
- * RegisterConsoleVDM API of ReactOS/Windows.
- */
-// #define USE_REAL_REGISTERCONSOLEVDM
-
 #define USE_REACTOS_COLORS
 // #define USE_DOSBOX_COLORS
 
@@ -206,49 +204,36 @@ static const COLORREF ConsoleColors[16] =
     RGB(255, 255, 255)  // BLUE  | GREEN | RED | INTENSITY
 };
 
-/*
- * Console interface -- VGA-mode-agnostic
- */
-typedef struct _CHAR_CELL
-{
-    CHAR Char;
-    BYTE Attributes;
-} CHAR_CELL, *PCHAR_CELL;
-C_ASSERT(sizeof(CHAR_CELL) == 2);
-
-static LPVOID ConsoleFramebuffer = NULL; // Active framebuffer, points to
-                                         // either TextFramebuffer or a
-                                         // valid graphics framebuffer.
+/// ConsoleFramebuffer
+static PVOID ActiveFramebuffer = NULL; // Active framebuffer, points to
+                                       // either TextFramebuffer or a
+                                       // valid graphics framebuffer.
 static HPALETTE TextPaletteHandle = NULL;
 static HPALETTE PaletteHandle = NULL;
 
-static HANDLE StartEvent = NULL;
-static HANDLE EndEvent   = NULL;
-static HANDLE AnotherEvent = NULL;
-
-static CONSOLE_CURSOR_INFO         OrgConsoleCursorInfo;
-static CONSOLE_SCREEN_BUFFER_INFO  OrgConsoleBufferInfo;
-
-
 /*
  * Text mode -- we always keep a valid text mode framebuffer
  * even if we are in graphics mode. This is needed in order
- * to keep a consistent VGA state.
+ * to keep a consistent VGA state. However, each time the VGA
+ * detaches from the console (and reattaches to it later on),
+ * this text mode framebuffer is recreated.
  */
-static CONSOLE_SCREEN_BUFFER_INFO ConsoleInfo;
-static COORD  TextResolution = {0};
 static PCHAR_CELL TextFramebuffer = NULL;
-static HANDLE TextConsoleBuffer = NULL;
 
-/* Graphics mode */
-static HANDLE GraphicsConsoleBuffer = NULL;
-static HANDLE ConsoleMutex = NULL;
-/* DoubleVision support */
-static BOOLEAN DoubleWidth  = FALSE;
-static BOOLEAN DoubleHeight = FALSE;
+/*
+ * Graphics mode
+ */
+static PBYTE GraphicsFramebuffer = NULL;
+
+
+// static HANDLE ConsoleMutex = NULL;
+// /* DoubleVision support */
+// static BOOLEAN DoubleWidth  = FALSE;
+// static BOOLEAN DoubleHeight = FALSE;
+
+
+
 
-static PHARDWARE_TIMER VSyncTimer;
-static PHARDWARE_TIMER HSyncTimer;
 
 /*
  * VGA Hardware
@@ -284,8 +269,10 @@ static BYTE VgaDacRegisters[VGA_PALETTE_SIZE];
 
 // static VGA_REGISTERS VgaRegisters;
 
-static ULONGLONG VerticalRetraceCycle = 0ULL;
+static ULONGLONG VerticalRetraceCycle   = 0ULL;
 static ULONGLONG HorizontalRetraceCycle = 0ULL;
+static PHARDWARE_TIMER VSyncTimer;
+static PHARDWARE_TIMER HSyncTimer;
 
 static BOOLEAN NeedsUpdate = FALSE;
 static BOOLEAN ModeChanged = FALSE;
@@ -306,301 +293,24 @@ static COORD CurrResolution   = {0};
 
 static SMALL_RECT UpdateRectangle = { 0, 0, 0, 0 };
 
-/* RegisterConsoleVDM EMULATION ***********************************************/
-
-#include <ntddvdeo.h>
-
-#ifdef USE_REAL_REGISTERCONSOLEVDM
-
-#define __RegisterConsoleVDM        RegisterConsoleVDM
-#define __InvalidateConsoleDIBits   InvalidateConsoleDIBits
-
-#else
-
-/*
- * This private buffer, per-console, is used by
- * RegisterConsoleVDM and InvalidateConsoleDIBits.
- */
-static COORD VDMBufferSize  = {0};
-static PCHAR_CELL VDMBuffer = NULL;
-
-static PCHAR_INFO CharBuff  = NULL; // This is a hack, which is unneeded
-                                    // for the real RegisterConsoleVDM and
-                                    // InvalidateConsoleDIBits
-
-BOOL
-WINAPI
-__RegisterConsoleVDM(IN DWORD dwRegisterFlags,
-                     IN HANDLE hStartHardwareEvent,
-                     IN HANDLE hEndHardwareEvent,
-                     IN HANDLE hErrorHardwareEvent,
-                     IN DWORD dwUnusedVar,
-                     OUT LPDWORD lpVideoStateLength,
-                     OUT PVOID* lpVideoState, // PVIDEO_HARDWARE_STATE_HEADER*
-                     IN PVOID lpUnusedBuffer,
-                     IN DWORD dwUnusedBufferLength,
-                     IN COORD dwVDMBufferSize,
-                     OUT PVOID* lpVDMBuffer)
-{
-    UNREFERENCED_PARAMETER(hErrorHardwareEvent);
-    UNREFERENCED_PARAMETER(dwUnusedVar);
-    UNREFERENCED_PARAMETER(lpVideoStateLength);
-    UNREFERENCED_PARAMETER(lpVideoState);
-    UNREFERENCED_PARAMETER(lpUnusedBuffer);
-    UNREFERENCED_PARAMETER(dwUnusedBufferLength);
-
-    SetLastError(0);
-    DPRINT1("__RegisterConsoleVDM(%d)\n", dwRegisterFlags);
-
-    if (lpVDMBuffer == NULL) return FALSE;
-
-    if (dwRegisterFlags != 0)
-    {
-        // if (hStartHardwareEvent == NULL || hEndHardwareEvent == NULL) return FALSE;
-        if (VDMBuffer != NULL) return FALSE;
-
-        VDMBufferSize = dwVDMBufferSize;
-
-        /* HACK: Cache -- to be removed in the real implementation */
-        CharBuff = RtlAllocateHeap(RtlGetProcessHeap(),
-                                   HEAP_ZERO_MEMORY,
-                                   VDMBufferSize.X * VDMBufferSize.Y
-                                                   * sizeof(*CharBuff));
-        ASSERT(CharBuff);
-
-        VDMBuffer = RtlAllocateHeap(RtlGetProcessHeap(),
-                                    HEAP_ZERO_MEMORY,
-                                    VDMBufferSize.X * VDMBufferSize.Y
-                                                    * sizeof(*VDMBuffer));
-        *lpVDMBuffer = VDMBuffer;
-        return (VDMBuffer != NULL);
-    }
-    else
-    {
-        /* HACK: Cache -- to be removed in the real implementation */
-        if (CharBuff) RtlFreeHeap(RtlGetProcessHeap(), 0, CharBuff);
-        CharBuff = NULL;
-
-        if (VDMBuffer) RtlFreeHeap(RtlGetProcessHeap(), 0, VDMBuffer);
-        VDMBuffer = NULL;
 
-        VDMBufferSize.X = VDMBufferSize.Y = 0;
 
-        return TRUE;
-    }
-}
 
-BOOL
-__InvalidateConsoleDIBits(IN HANDLE hConsoleOutput,
-                          IN PSMALL_RECT lpRect)
-{
-    if ((hConsoleOutput == TextConsoleBuffer) && (VDMBuffer != NULL))
-    {
-        /* HACK: Write the cached data to the console */
+/** HACK!! **/
+#include "../../console/video.c"
+/** HACK!! **/
 
-        COORD Origin = { lpRect->Left, lpRect->Top };
-        SHORT i, j;
 
-        ASSERT(CharBuff);
 
-        for (i = 0; i < VDMBufferSize.Y; i++)
-        {
-            for (j = 0; j < VDMBufferSize.X; j++)
-            {
-                CharBuff[i * VDMBufferSize.X + j].Char.AsciiChar = VDMBuffer[i * VDMBufferSize.X + j].Char;
-                CharBuff[i * VDMBufferSize.X + j].Attributes     = VDMBuffer[i * VDMBufferSize.X + j].Attributes;
-            }
-        }
 
-        WriteConsoleOutputA(hConsoleOutput,
-                            CharBuff,
-                            VDMBufferSize,
-                            Origin,
-                            lpRect);
-    }
-
-    return InvalidateConsoleDIBits(hConsoleOutput, lpRect);
-}
-
-#endif
 
 /* PRIVATE FUNCTIONS **********************************************************/
 
-static inline DWORD VgaGetAddressSize(VOID);
-static VOID VgaUpdateTextCursor(VOID);
-
 static inline DWORD VgaGetVideoBaseAddress(VOID)
 {
     return MemoryBase[(VgaGcRegisters[VGA_GC_MISC_REG] >> 2) & 0x03];
 }
 
-static VOID VgaUpdateCursorPosition(VOID)
-{
-    /*
-     * Update the cursor position in the VGA registers.
-     */
-    WORD Offset = ConsoleInfo.dwCursorPosition.Y * TextResolution.X +
-                  ConsoleInfo.dwCursorPosition.X;
-
-    VgaCrtcRegisters[VGA_CRTC_CURSOR_LOC_LOW_REG]  = LOBYTE(Offset);
-    VgaCrtcRegisters[VGA_CRTC_CURSOR_LOC_HIGH_REG] = HIBYTE(Offset);
-
-    // VidBiosSyncCursorPosition();
-    VgaUpdateTextCursor();
-}
-
-static BOOL VgaAttachToConsoleInternal(PCOORD Resolution)
-{
-    BOOL Success;
-    ULONG Length = 0;
-    PVIDEO_HARDWARE_STATE_HEADER State;
-
-#ifdef USE_REAL_REGISTERCONSOLEVDM
-    PCHAR_INFO CharBuff = NULL;
-#endif
-    SHORT i, j;
-    DWORD AddressSize, ScanlineSize;
-    DWORD Address = 0;
-    DWORD CurrentAddr;
-    SMALL_RECT ConRect;
-    COORD Origin = { 0, 0 };
-
-    ASSERT(TextFramebuffer == NULL);
-
-    TextResolution = *Resolution;
-
-    /*
-     * Windows 2k3 winsrv.dll calls NtVdmControl(VdmQueryVdmProcess == 14, &ConsoleHandle);
-     * in the two following APIs:
-     * SrvRegisterConsoleVDM  (corresponding Win32 API: RegisterConsoleVDM)
-     * SrvVDMConsoleOperation (corresponding Win32 API: )
-     * to check whether the current process is a VDM process, and fails otherwise
-     * with the error 0xC0000022 (STATUS_ACCESS_DENIED).
-     *
-     * It is worth it to notice that also basesrv.dll does the same only for the
-     * BaseSrvIsFirstVDM API...
-     */
-
-    Success =
-    __RegisterConsoleVDM(1,
-                         StartEvent,
-                         EndEvent,
-                         AnotherEvent, // NULL,
-                         0,
-                         &Length, // NULL, <-- putting this (and null in the next var) makes the API returning error 12 "ERROR_INVALID_ACCESS"
-                         (PVOID*)&State, // NULL,
-                         NULL,
-                         0,
-                         TextResolution,
-                         (PVOID*)&TextFramebuffer);
-    if (!Success)
-    {
-        DisplayMessage(L"RegisterConsoleVDM failed with error %d\n", GetLastError());
-        EmulatorTerminate();
-        return FALSE;
-    }
-
-#ifdef USE_REAL_REGISTERCONSOLEVDM
-    CharBuff = RtlAllocateHeap(RtlGetProcessHeap(),
-                               HEAP_ZERO_MEMORY,
-                               TextResolution.X * TextResolution.Y
-                                                * sizeof(*CharBuff));
-    ASSERT(CharBuff);
-#endif
-
-    /*
-     * Resize the console
-     */
-    ConRect.Left   = 0;
-    ConRect.Top    = ConsoleInfo.srWindow.Top;
-    ConRect.Right  = ConRect.Left + Resolution->X - 1;
-    ConRect.Bottom = ConRect.Top  + Resolution->Y - 1;
-    /*
-     * Use this trick to effectively resize the console buffer and window,
-     * because:
-     * - SetConsoleScreenBufferSize fails if the new console screen buffer size
-     *   is smaller than the current console window size, and:
-     * - SetConsoleWindowInfo fails if the new console window size is larger
-     *   than the current console screen buffer size.
-     */
-    SetConsoleScreenBufferSize(TextConsoleBuffer, *Resolution);
-    SetConsoleWindowInfo(TextConsoleBuffer, TRUE, &ConRect);
-    SetConsoleScreenBufferSize(TextConsoleBuffer, *Resolution);
-    /* Update the saved console information */
-    GetConsoleScreenBufferInfo(TextConsoleBuffer, &ConsoleInfo);
-
-    /*
-     * Copy console data into VGA memory
-     */
-
-    /* Get the data */
-    AddressSize = VgaGetAddressSize();
-    ConRect.Left   = ConRect.Top = 0;
-    ConRect.Right  = TextResolution.X;
-    ConRect.Bottom = TextResolution.Y;
-    ScanlineSize = (DWORD)VgaCrtcRegisters[VGA_CRTC_OFFSET_REG] * 2;
-
-    /* Read the data from the console into the framebuffer... */
-    ReadConsoleOutputA(TextConsoleBuffer,
-                       CharBuff,
-                       TextResolution,
-                       Origin,
-                       &ConRect);
-
-    /* ... and copy the framebuffer into the VGA memory */
-
-    /* Loop through the scanlines */
-    for (i = 0; i < TextResolution.Y; i++)
-    {
-        /* Loop through the characters */
-        for (j = 0; j < TextResolution.X; j++)
-        {
-            CurrentAddr = LOWORD((Address + j) * AddressSize);
-
-            /* Store the character in plane 0 */
-            VgaMemory[CurrentAddr] = CharBuff[i * TextResolution.X + j].Char.AsciiChar;
-
-            /* Store the attribute in plane 1 */
-            VgaMemory[CurrentAddr + VGA_BANK_SIZE] = (BYTE)CharBuff[i * TextResolution.X + j].Attributes;
-        }
-
-        /* Move to the next scanline */
-        Address += ScanlineSize;
-    }
-
-#ifdef USE_REAL_REGISTERCONSOLEVDM
-    if (CharBuff) RtlFreeHeap(RtlGetProcessHeap(), 0, CharBuff);
-#endif
-
-    VgaUpdateCursorPosition();
-
-    return TRUE;
-}
-
-static BOOL IsConsoleHandle(HANDLE hHandle)
-{
-    DWORD dwMode;
-
-    /* Check whether the handle may be that of a console... */
-    if ((GetFileType(hHandle) & ~FILE_TYPE_REMOTE) != FILE_TYPE_CHAR)
-        return FALSE;
-
-    /*
-     * It may be. Perform another test... The idea comes from the
-     * MSDN description of the WriteConsole API:
-     *
-     * "WriteConsole fails if it is used with a standard handle
-     *  that is redirected to a file. If an application processes
-     *  multilingual output that can be redirected, determine whether
-     *  the output handle is a console handle (one method is to call
-     *  the GetConsoleMode function and check whether it succeeds).
-     *  If the handle is a console handle, call WriteConsole. If the
-     *  handle is not a console handle, the output is redirected and
-     *  you should call WriteFile to perform the I/O."
-     */
-    return GetConsoleMode(hHandle, &dwMode);
-}
-
 static inline DWORD VgaGetAddressSize(VOID)
 {
     if (VgaCrtcRegisters[VGA_CRTC_UNDERLINE_REG] & VGA_CRTC_UNDERLINE_DWORD)
@@ -730,25 +440,6 @@ static inline BYTE VgaTranslateByteForWriting(BYTE Data, BYTE Plane)
     return Data;
 }
 
-static inline VOID VgaMarkForUpdate(SHORT Row, SHORT Column)
-{
-    /* Check if this is the first time the rectangle is updated */
-    if (!NeedsUpdate)
-    {
-        UpdateRectangle.Left = UpdateRectangle.Top = MAXSHORT;
-        UpdateRectangle.Right = UpdateRectangle.Bottom = MINSHORT;
-    }
-
-    /* Expand the rectangle to include the point */
-    UpdateRectangle.Left = min(UpdateRectangle.Left, Column);
-    UpdateRectangle.Right = max(UpdateRectangle.Right, Column);
-    UpdateRectangle.Top = min(UpdateRectangle.Top, Row);
-    UpdateRectangle.Bottom = max(UpdateRectangle.Bottom, Row);
-
-    /* Set the update request flag */
-    NeedsUpdate = TRUE;
-}
-
 static inline ULONG VgaGetClockFrequency(VOID)
 {
     BYTE Numerator, Denominator;
@@ -921,152 +612,74 @@ static VOID VgaResetPalette(VOID)
     PaletteChanged = TRUE;
 }
 
-static VOID VgaSetActiveScreenBuffer(HANDLE ScreenBuffer)
+static BOOL VgaEnterNewMode(SCREEN_MODE NewScreenMode, PCOORD Resolution)
 {
-    /* Set the active buffer */
-    SetConsoleActiveScreenBuffer(ScreenBuffer);
+    /* Check if the new mode is alphanumeric */
+    if (NewScreenMode == TEXT_MODE)
+    {
+        /* Enter new text mode */
 
-    /* Reinitialize the VDM menu */
-    DestroyVdmMenu();
-    CreateVdmMenu(ScreenBuffer);
-}
+        if (!VgaConsoleCreateTextScreen(// &TextFramebuffer,
+                                        Resolution,
+                                        TextPaletteHandle))
+        {
+            DisplayMessage(L"An unexpected VGA error occurred while switching into text mode. Error: %u", GetLastError());
+            EmulatorTerminate();
+            return FALSE;
+        }
 
-static BOOL VgaEnterGraphicsMode(PCOORD Resolution)
-{
-    DWORD i;
-    CONSOLE_GRAPHICS_BUFFER_INFO GraphicsBufferInfo;
-    BYTE BitmapInfoBuffer[VGA_BITMAP_INFO_SIZE];
-    LPBITMAPINFO BitmapInfo = (LPBITMAPINFO)BitmapInfoBuffer;
-    LPWORD PaletteIndex = (LPWORD)(BitmapInfo->bmiColors);
-
-    LONG Width  = Resolution->X;
-    LONG Height = Resolution->Y;
-
-    /* Use DoubleVision mode if the resolution is too small */
-    DoubleWidth = (Width < VGA_MINIMUM_WIDTH);
-    if (DoubleWidth) Width *= 2;
-    DoubleHeight = (Height < VGA_MINIMUM_HEIGHT);
-    if (DoubleHeight) Height *= 2;
-
-    /* Fill the bitmap info header */
-    RtlZeroMemory(&BitmapInfo->bmiHeader, sizeof(BitmapInfo->bmiHeader));
-    BitmapInfo->bmiHeader.biSize   = sizeof(BitmapInfo->bmiHeader);
-    BitmapInfo->bmiHeader.biWidth  = Width;
-    BitmapInfo->bmiHeader.biHeight = Height;
-    BitmapInfo->bmiHeader.biBitCount = 8;
-    BitmapInfo->bmiHeader.biPlanes   = 1;
-    BitmapInfo->bmiHeader.biCompression = BI_RGB;
-    BitmapInfo->bmiHeader.biSizeImage   = Width * Height /* * 1 == biBitCount / 8 */;
-
-    /* Fill the palette data */
-    for (i = 0; i < (VGA_PALETTE_SIZE / 3); i++) PaletteIndex[i] = (WORD)i;
-
-    /* Fill the console graphics buffer info */
-    GraphicsBufferInfo.dwBitMapInfoLength = VGA_BITMAP_INFO_SIZE;
-    GraphicsBufferInfo.lpBitMapInfo = BitmapInfo;
-    GraphicsBufferInfo.dwUsage = DIB_PAL_COLORS;
-
-    /* Create the buffer */
-    GraphicsConsoleBuffer = CreateConsoleScreenBuffer(GENERIC_READ | GENERIC_WRITE,
-                                                      FILE_SHARE_READ | FILE_SHARE_WRITE,
-                                                      NULL,
-                                                      CONSOLE_GRAPHICS_BUFFER,
-                                                      &GraphicsBufferInfo);
-    if (GraphicsConsoleBuffer == INVALID_HANDLE_VALUE) return FALSE;
-
-    /* Save the framebuffer address and mutex */
-    ConsoleFramebuffer = GraphicsBufferInfo.lpBitMap;
-    ConsoleMutex = GraphicsBufferInfo.hMutex;
-
-    /* Clear the framebuffer */
-    RtlZeroMemory(ConsoleFramebuffer, BitmapInfo->bmiHeader.biSizeImage);
-
-    /* Set the active buffer */
-    VgaSetActiveScreenBuffer(GraphicsConsoleBuffer);
-
-    /* Set the graphics mode palette */
-    SetConsolePalette(GraphicsConsoleBuffer,
-                      PaletteHandle,
-                      SYSPAL_NOSTATIC256);
-
-    /* Set the screen mode flag */
-    ScreenMode = GRAPHICS_MODE;
+        /* The active framebuffer is now the text framebuffer */
+        ActiveFramebuffer = TextFramebuffer;
 
-    return TRUE;
-}
+        /* Set the screen mode flag */
+        ScreenMode = TEXT_MODE;
 
-static VOID VgaLeaveGraphicsMode(VOID)
-{
-    /* Release the console framebuffer mutex */
-    ReleaseMutex(ConsoleMutex);
+        return TRUE;
+    }
+    else
+    {
+        /* Enter graphics mode */
+
+        if (!VgaConsoleCreateGraphicsScreen(// &GraphicsFramebuffer,
+                                            Resolution,
+                                            PaletteHandle))
+        {
+            DisplayMessage(L"An unexpected VGA error occurred while switching into graphics mode. Error: %u", GetLastError());
+            EmulatorTerminate();
+            return FALSE;
+        }
 
-    /* Switch back to the default console text buffer */
-    // VgaSetActiveScreenBuffer(TextConsoleBuffer);
+        /* The active framebuffer is now the graphics framebuffer */
+        ActiveFramebuffer = GraphicsFramebuffer;
 
-    /* Cleanup the video data */
-    CloseHandle(ConsoleMutex);
-    ConsoleMutex = NULL;
-    ConsoleFramebuffer = NULL;
-    CloseHandle(GraphicsConsoleBuffer);
-    GraphicsConsoleBuffer = NULL;
+        /* Set the screen mode flag */
+        ScreenMode = GRAPHICS_MODE;
 
-    DoubleWidth  = FALSE;
-    DoubleHeight = FALSE;
+        return TRUE;
+    }
 }
 
-static BOOL VgaEnterTextMode(PCOORD Resolution)
+static VOID VgaLeaveCurrentMode(VOID)
 {
-    /* Switch to the text buffer */
-    VgaSetActiveScreenBuffer(TextConsoleBuffer);
-
-    /* Adjust the text framebuffer if we changed the resolution */
-    if (TextResolution.X != Resolution->X ||
-        TextResolution.Y != Resolution->Y)
+    /* Leave the current video mode */
+    if (ScreenMode == GRAPHICS_MODE)
     {
-        VgaDetachFromConsole(TRUE);
+        VgaConsoleDestroyGraphicsScreen();
 
-        /*
-         * VgaAttachToConsoleInternal sets TextResolution to the
-         * new resolution and updates ConsoleInfo.
-         */
-        if (!VgaAttachToConsoleInternal(Resolution))
-        {
-            DisplayMessage(L"An unexpected error occurred!\n");
-            EmulatorTerminate();
-            return FALSE;
-        }
+        /* Cleanup the video data */
+        GraphicsFramebuffer = NULL;
     }
     else
     {
-        VgaUpdateCursorPosition();
-    }
-
-    /* The active framebuffer is now the text framebuffer */
-    ConsoleFramebuffer = TextFramebuffer;
-
-    /*
-     * Set the text mode palette.
-     *
-     * WARNING: This call should fail on Windows (and therefore
-     * we get the default palette and our external behaviour is
-     * just like Windows' one), but it should success on ReactOS
-     * (so that we get console palette changes even for text-mode
-     * screen-buffers, which is a new feature on ReactOS).
-     */
-    SetConsolePalette(TextConsoleBuffer,
-                      TextPaletteHandle,
-                      SYSPAL_NOSTATIC256);
+        VgaConsoleDestroyTextScreen();
 
-    /* Set the screen mode flag */
-    ScreenMode = TEXT_MODE;
-
-    return TRUE;
-}
+        /* Cleanup the video data */
+        // TextFramebuffer = NULL;
+        // NEVER SET the ALWAYS-SET TextFramebuffer pointer to NULL!!
+    }
 
-static VOID VgaLeaveTextMode(VOID)
-{
     /* Reset the active framebuffer */
-    ConsoleFramebuffer = NULL;
+    ActiveFramebuffer = NULL;
 }
 
 static VOID VgaChangeMode(VOID)
@@ -1077,52 +690,27 @@ static VOID VgaChangeMode(VOID)
                                                                  : GRAPHICS_MODE;
 
     /*
-     * No need to switch to a different screen mode + resolution
-     * if the new ones are the same as the old ones.
+     * Do not switch to a different screen mode + resolution if the new settings
+     * are the same as the old ones. Just repaint the full screen.
      */
-    if ((ScreenMode == NewScreenMode) &&
+    if ((ScreenMode == NewScreenMode) && // CurrResolution == NewResolution
         (CurrResolution.X == NewResolution.X && CurrResolution.Y == NewResolution.Y))
     {
         goto Quit;
     }
 
-    if (ScreenMode == GRAPHICS_MODE)
-    {
-        /* Leave the current graphics mode */
-        VgaLeaveGraphicsMode();
-    }
-    else
-    {
-        /* Leave the current text mode */
-        VgaLeaveTextMode();
-    }
+    // FIXME: Wouldn't it be preferrable to switch to the new console SB
+    // *ONLY* if we succeeded in setting the new mode??
+
+    /* Leave the current video mode */
+    VgaLeaveCurrentMode(); // ScreenMode
 
     /* Update the current resolution */
     CurrResolution = NewResolution;
 
-    /* The new screen mode will be updated via the VgaEnterText/GraphicsMode functions */
-
-    /* Check if the new mode is alphanumeric */
-    if (NewScreenMode == TEXT_MODE)
-    {
-        /* Enter new text mode */
-        if (!VgaEnterTextMode(&CurrResolution))
-        {
-            DisplayMessage(L"An unexpected VGA error occurred while switching into text mode. Error: %u", GetLastError());
-            EmulatorTerminate();
-            return;
-        }
-    }
-    else
-    {
-        /* Enter graphics mode */
-        if (!VgaEnterGraphicsMode(&CurrResolution))
-        {
-            DisplayMessage(L"An unexpected VGA error occurred while switching into graphics mode. Error: %u", GetLastError());
-            EmulatorTerminate();
-            return;
-        }
-    }
+    /* Change the screen mode */
+    if (!VgaEnterNewMode(NewScreenMode, &CurrResolution))
+        return;
 
 Quit:
 
@@ -1137,28 +725,51 @@ Quit:
     ModeChanged = FALSE;
 }
 
+static inline VOID VgaMarkForUpdate(SHORT Row, SHORT Column)
+{
+    /* Check if this is the first time the rectangle is updated */
+    if (!NeedsUpdate)
+    {
+        UpdateRectangle.Left = UpdateRectangle.Top = MAXSHORT;
+        UpdateRectangle.Right = UpdateRectangle.Bottom = MINSHORT;
+    }
+
+    /* Expand the rectangle to include the point */
+    UpdateRectangle.Left = min(UpdateRectangle.Left, Column);
+    UpdateRectangle.Right = max(UpdateRectangle.Right, Column);
+    UpdateRectangle.Top = min(UpdateRectangle.Top, Row);
+    UpdateRectangle.Bottom = max(UpdateRectangle.Bottom, Row);
+
+    /* Set the update request flag */
+    NeedsUpdate = TRUE;
+}
+
 static VOID VgaUpdateFramebuffer(VOID)
 {
     SHORT i, j, k;
     DWORD AddressSize = VgaGetAddressSize();
     DWORD ScanlineSize = (DWORD)VgaCrtcRegisters[VGA_CRTC_OFFSET_REG] * 2;
     BYTE PresetRowScan = VgaCrtcRegisters[VGA_CRTC_PRESET_ROW_SCAN_REG] & 0x1F;
+    BYTE BytePanning = (VgaCrtcRegisters[VGA_CRTC_PRESET_ROW_SCAN_REG] >> 5) & 3;
     DWORD Address = MAKEWORD(VgaCrtcRegisters[VGA_CRTC_START_ADDR_LOW_REG],
                              VgaCrtcRegisters[VGA_CRTC_START_ADDR_HIGH_REG])
                     + PresetRowScan * ScanlineSize
-                    + ((VgaCrtcRegisters[VGA_CRTC_PRESET_ROW_SCAN_REG] >> 5) & 3);
+                    + BytePanning;
+    WORD LineCompare = VgaCrtcRegisters[VGA_CRTC_LINE_COMPARE_REG]
+                       | ((VgaCrtcRegisters[VGA_CRTC_OVERFLOW_REG] & VGA_CRTC_OVERFLOW_LC8) << 4);
+    BYTE PixelShift = VgaAcRegisters[VGA_AC_HORZ_PANNING_REG] & 0x0F;
 
     /*
-     * If console framebuffer is NULL, that means something went wrong
-     * earlier and this is the final display refresh.
+     * If the console framebuffer is NULL, that means something
+     * went wrong earlier and this is the final display refresh.
      */
-    if (ConsoleFramebuffer == NULL) return;
+    if (ActiveFramebuffer == NULL) return;
 
     /* Check if we are in text or graphics mode */
     if (ScreenMode == GRAPHICS_MODE)
     {
         /* Graphics mode */
-        PBYTE GraphicsBuffer = (PBYTE)ConsoleFramebuffer;
+        PBYTE GraphicsBuffer = (PBYTE)ActiveFramebuffer;
         DWORD InterlaceHighBit = VGA_INTERLACE_HIGH_BIT;
         SHORT X;
 
@@ -1174,9 +785,36 @@ static VOID VgaUpdateFramebuffer(VOID)
             InterlaceHighBit >>= 1;
         }
 
+        if (VgaCrtcRegisters[VGA_CRTC_MAX_SCAN_LINE_REG] & VGA_CRTC_MAXSCANLINE_DOUBLE)
+        {
+            /* Halve the line compare value */
+            LineCompare >>= 1;
+        }
+        else
+        {
+            /* Divide the line compare value by the maximum scan line */
+            LineCompare /= 1 + (VgaCrtcRegisters[VGA_CRTC_MAX_SCAN_LINE_REG] & 0x1F);
+        }
+
         /* Loop through the scanlines */
         for (i = 0; i < CurrResolution.Y; i++)
         {
+            if (i == LineCompare)
+            {
+                if (VgaAcRegisters[VGA_AC_CONTROL_REG] & VGA_AC_CONTROL_PPM)
+                {
+                    /*
+                     * Disable the pixel shift count and byte panning
+                     * for the rest of the display cycle
+                     */
+                    PixelShift = 0;
+                    BytePanning = 0;
+                }
+
+                /* Reset the address, but assume the preset row scan is 0 */
+                Address = BytePanning;
+            }
+
             if ((VgaGcRegisters[VGA_GC_MISC_REG] & VGA_GC_MISC_OE) && (i & 1))
             {
                 /* Odd-numbered line in interlaced mode - set the high bit */
@@ -1191,11 +829,11 @@ static VOID VgaUpdateFramebuffer(VOID)
                 /* Apply horizontal pixel panning */
                 if (VgaAcRegisters[VGA_AC_CONTROL_REG] & VGA_AC_CONTROL_8BIT)
                 {
-                    X = j + ((VgaAcRegisters[VGA_AC_HORZ_PANNING_REG] & 0x0F) >> 1);
+                    X = j + (PixelShift >> 1);
                 }
                 else
                 {
-                    X = j + (VgaAcRegisters[VGA_AC_HORZ_PANNING_REG] & 0x0F);
+                    X = j + PixelShift;
                 }
 
                 /* Check the shifting mode */
@@ -1308,8 +946,26 @@ static VOID VgaUpdateFramebuffer(VOID)
                      * if external palette access is disabled, otherwise (in case
                      * of palette loading) it is a blank pixel.
                      */
-                    PixelData = (VgaAcPalDisable ? VgaAcRegisters[PixelData & 0x0F]
-                                                 : 0);
+
+                    if (VgaAcPalDisable)
+                    {
+                        if (!(VgaAcRegisters[VGA_AC_CONTROL_REG] & VGA_AC_CONTROL_P54S))
+                        {
+                            /* Bits 4 and 5 are taken from the palette register */
+                            PixelData = ((VgaAcRegisters[VGA_AC_COLOR_SEL_REG] << 4) & 0xC0)
+                                        | (VgaAcRegisters[PixelData & 0x0F] & 0x3F);
+                        }
+                        else
+                        {
+                            /* Bits 4 and 5 are taken from the color select register */
+                            PixelData = (VgaAcRegisters[VGA_AC_COLOR_SEL_REG] << 4)
+                                        | (VgaAcRegisters[PixelData & 0x0F] & 0x0F);
+                        }
+                    }
+                    else
+                    {
+                        PixelData = 0;
+                    }
                 }
 
                 /* Take into account DoubleVision mode when checking for pixel updates */
@@ -1391,7 +1047,7 @@ static VOID VgaUpdateFramebuffer(VOID)
     {
         /* Text mode */
         DWORD CurrentAddr;
-        PCHAR_CELL CharBuffer = (PCHAR_CELL)ConsoleFramebuffer;
+        PCHAR_CELL CharBuffer = (PCHAR_CELL)ActiveFramebuffer;
         CHAR_CELL CharInfo;
 
         /*
@@ -1434,9 +1090,6 @@ static VOID VgaUpdateFramebuffer(VOID)
 
 static VOID VgaUpdateTextCursor(VOID)
 {
-    COORD Position;
-    CONSOLE_CURSOR_INFO CursorInfo;
-
     BOOL CursorVisible = !(VgaCrtcRegisters[VGA_CRTC_CURSOR_START_REG] & 0x20);
     BYTE CursorStart   =   VgaCrtcRegisters[VGA_CRTC_CURSOR_START_REG] & 0x1F;
     BYTE CursorEnd     =   VgaCrtcRegisters[VGA_CRTC_CURSOR_END_REG]   & 0x1F;
@@ -1449,31 +1102,11 @@ static VOID VgaUpdateTextCursor(VOID)
     /* Just return if we are not in text mode */
     if (ScreenMode != TEXT_MODE) return;
 
-    if (CursorStart < CursorEnd)
-    {
-        /* Visible cursor */
-        CursorInfo.bVisible = CursorVisible;
-        CursorInfo.dwSize   = (100 * (CursorEnd - CursorStart)) / TextSize;
-    }
-    else
-    {
-        /* Hidden cursor */
-        CursorInfo.bVisible = FALSE;
-        CursorInfo.dwSize   = 1; // The size needs to be non-null in order SetConsoleCursorInfo to succeed.
-    }
-
     /* Add the cursor skew to the location */
     Location += (VgaCrtcRegisters[VGA_CRTC_CURSOR_END_REG] >> 5) & 0x03;
 
-    /* Find the coordinates of the new position */
-    Position.X = (SHORT)(Location % ScanlineSize);
-    Position.Y = (SHORT)(Location / ScanlineSize);
-
-    DPRINT("VgaUpdateTextCursor: X = %d ; Y = %d\n", Position.X, Position.Y);
-
-    /* Update the physical cursor */
-    SetConsoleCursorInfo(TextConsoleBuffer, &CursorInfo);
-    SetConsoleCursorPosition(TextConsoleBuffer, Position);
+    VgaConsoleUpdateTextCursor(CursorVisible, CursorStart, CursorEnd,
+                               TextSize, ScanlineSize, Location);
 
     /* Reset the cursor changed flag */
     CursorChanged = FALSE;
@@ -1979,8 +1612,6 @@ static VOID WINAPI VgaWritePort(USHORT Port, BYTE Data)
 
 static VOID FASTCALL VgaVerticalRetrace(ULONGLONG ElapsedTime)
 {
-    HANDLE ConsoleBufferHandle = NULL;
-
     UNREFERENCED_PARAMETER(ElapsedTime);
 
     /* Set the vertical retrace cycle */
@@ -2020,32 +1651,7 @@ static VOID FASTCALL VgaVerticalRetrace(ULONGLONG ElapsedTime)
            UpdateRectangle.Right,
            UpdateRectangle.Bottom);
 
-    /* Check if we are in text or graphics mode */
-    if (ScreenMode == GRAPHICS_MODE)
-    {
-        /* Graphics mode */
-        ConsoleBufferHandle = GraphicsConsoleBuffer;
-
-        /* In DoubleVision mode, scale the update rectangle */
-        if (DoubleWidth)
-        {
-            UpdateRectangle.Left *= 2;
-            UpdateRectangle.Right = UpdateRectangle.Right * 2 + 1;
-        }
-        if (DoubleHeight)
-        {
-            UpdateRectangle.Top *= 2;
-            UpdateRectangle.Bottom = UpdateRectangle.Bottom * 2 + 1;
-        }
-    }
-    else
-    {
-        /* Text mode */
-        ConsoleBufferHandle = TextConsoleBuffer;
-    }
-
-    /* Redraw the screen */
-    __InvalidateConsoleDIBits(ConsoleBufferHandle, &UpdateRectangle);
+    VgaConsoleRepaintScreen(&UpdateRectangle);
 
     /* Clear the update flag */
     NeedsUpdate = FALSE;
@@ -2086,12 +1692,20 @@ COORD VgaGetDisplayResolution(VOID)
 
     if (VgaGcRegisters[VGA_GC_MISC_REG] & VGA_GC_MISC_NOALPHA)
     {
-        /* Multiply the horizontal resolution by the 9/8 dot mode */
-        Resolution.X *= (VgaSeqRegisters[VGA_SEQ_CLOCK_REG] & VGA_SEQ_CLOCK_98DM)
-                        ? 8 : 9;
+        /* In "High Resolution" mode, the width of a character is always 8 pixels */
+        if (VgaSeqRegisters[SVGA_SEQ_EXT_MODE_REG] & SVGA_SEQ_EXT_MODE_HIGH_RES)
+        {
+            Resolution.X *= 8;
+        }
+        else
+        {
+            /* Multiply the horizontal resolution by the 9/8 dot mode */
+            Resolution.X *= (VgaSeqRegisters[VGA_SEQ_CLOCK_REG] & VGA_SEQ_CLOCK_98DM)
+                            ? 8 : 9;
 
-        /* The horizontal resolution is halved in 8-bit mode */
-        if (VgaAcRegisters[VGA_AC_CONTROL_REG] & VGA_AC_CONTROL_8BIT) Resolution.X /= 2;
+            /* The horizontal resolution is halved in 8-bit mode */
+            if (VgaAcRegisters[VGA_AC_CONTROL_REG] & VGA_AC_CONTROL_8BIT) Resolution.X /= 2;
+        }
     }
 
     if (VgaCrtcRegisters[VGA_CRTC_MAX_SCAN_LINE_REG] & VGA_CRTC_MAXSCANLINE_DOUBLE)
@@ -2109,14 +1723,6 @@ COORD VgaGetDisplayResolution(VOID)
     return Resolution;
 }
 
-BOOLEAN VgaGetDoubleVisionState(PBOOLEAN Horizontal, PBOOLEAN Vertical)
-{
-    if (GraphicsConsoleBuffer == NULL) return FALSE;
-    if (Horizontal) *Horizontal = DoubleWidth;
-    if (Vertical)   *Vertical   = DoubleHeight;
-    return TRUE;
-}
-
 VOID VgaRefreshDisplay(VOID)
 {
     VgaVerticalRetrace(0);
@@ -2263,100 +1869,20 @@ VOID VgaWriteTextModeFont(UINT FontNumber, CONST UCHAR* FontData, UINT Height)
     }
 }
 
-VOID ScreenEventHandler(PWINDOW_BUFFER_SIZE_RECORD ScreenEvent)
-{
-    DPRINT1("Screen events not handled\n");
-}
-
-BOOL VgaAttachToConsole(VOID)
-{
-    //
-    // FIXME: We should go back to the saved screen state
-    //
-    if (TextResolution.X == 0 || TextResolution.Y == 0)
-        DPRINT1("VgaAttachToConsole -- TextResolution uninitialized\n");
-
-    if (TextResolution.X == 0) TextResolution.X = 80;
-    if (TextResolution.Y == 0) TextResolution.Y = 25;
-
-    return VgaAttachToConsoleInternal(&TextResolution);
-}
-
-VOID VgaDetachFromConsole(BOOL ChangingMode)
-{
-    ULONG dummyLength;
-    PVOID dummyPtr;
-    COORD dummySize = {0};
-
-    //
-    // FIXME: We should save the screen state
-    //
-
-    __RegisterConsoleVDM(0,
-                         NULL,
-                         NULL,
-                         NULL,
-                         0,
-                         &dummyLength,
-                         &dummyPtr,
-                         NULL,
-                         0,
-                         dummySize,
-                         &dummyPtr);
-
-    TextFramebuffer = NULL;
-
-    if (!ChangingMode)
-    {
-        SMALL_RECT ConRect;
-
-        /* Restore the old screen buffer */
-        VgaSetActiveScreenBuffer(TextConsoleBuffer);
-
-        /* Restore the original console size */
-        ConRect.Left   = 0;
-        ConRect.Top    = 0;
-        ConRect.Right  = ConRect.Left + OrgConsoleBufferInfo.srWindow.Right  - OrgConsoleBufferInfo.srWindow.Left;
-        ConRect.Bottom = ConRect.Top  + OrgConsoleBufferInfo.srWindow.Bottom - OrgConsoleBufferInfo.srWindow.Top ;
-        /*
-         * See the following trick explanation in VgaAttachToConsoleInternal.
-         */
-        SetConsoleScreenBufferSize(TextConsoleBuffer, OrgConsoleBufferInfo.dwSize);
-        SetConsoleWindowInfo(TextConsoleBuffer, TRUE, &ConRect);
-        SetConsoleScreenBufferSize(TextConsoleBuffer, OrgConsoleBufferInfo.dwSize);
-
-        /* Restore the original cursor shape */
-        SetConsoleCursorInfo(TextConsoleBuffer, &OrgConsoleCursorInfo);
-    }
-}
-
 BOOLEAN VgaInitialize(HANDLE TextHandle)
 {
+    if (!VgaConsoleInitialize(TextHandle)) return FALSE;
+
     /* Clear the SEQ, GC, CRTC and AC registers */
-    RtlZeroMemory(VgaSeqRegisters, sizeof(VgaSeqRegisters));
-    RtlZeroMemory(VgaGcRegisters, sizeof(VgaGcRegisters));
+    RtlZeroMemory(VgaSeqRegisters , sizeof(VgaSeqRegisters ));
+    RtlZeroMemory(VgaGcRegisters  , sizeof(VgaGcRegisters  ));
     RtlZeroMemory(VgaCrtcRegisters, sizeof(VgaCrtcRegisters));
-    RtlZeroMemory(VgaAcRegisters, sizeof(VgaAcRegisters));
-
-    /* Save the default text-mode console output handle */
-    if (!IsConsoleHandle(TextHandle)) return FALSE;
-    TextConsoleBuffer = TextHandle;
-
-    /* Save the original cursor and console screen buffer information */
-    if (!GetConsoleCursorInfo(TextConsoleBuffer, &OrgConsoleCursorInfo) ||
-        !GetConsoleScreenBufferInfo(TextConsoleBuffer, &OrgConsoleBufferInfo))
-    {
-        return FALSE;
-    }
-    ConsoleInfo = OrgConsoleBufferInfo;
+    RtlZeroMemory(VgaAcRegisters  , sizeof(VgaAcRegisters  ));
 
     /* Initialize the VGA palette and fail if it isn't successfully created */
     if (!VgaInitializePalette()) return FALSE;
     /***/ VgaResetPalette(); /***/
 
-    /* Switch to the text buffer */
-    VgaSetActiveScreenBuffer(TextConsoleBuffer);
-
     /* Reset the sequencer */
     VgaResetSequencer();
 
@@ -2391,26 +1917,18 @@ BOOLEAN VgaInitialize(HANDLE TextHandle)
 
 VOID VgaCleanup(VOID)
 {
+    /* Do a final display refresh */
+    VgaRefreshDisplay();
+
     DestroyHardwareTimer(VSyncTimer);
     DestroyHardwareTimer(HSyncTimer);
 
-    if (ScreenMode == GRAPHICS_MODE)
-    {
-        /* Leave the current graphics mode */
-        VgaLeaveGraphicsMode();
-    }
-    else
-    {
-        /* Leave the current text mode */
-        VgaLeaveTextMode();
-    }
+    /* Leave the current video mode */
+    VgaLeaveCurrentMode(); // ScreenMode
 
-    VgaDetachFromConsole(FALSE);
     MemRemoveFastMemoryHook((PVOID)0xA0000, 0x20000);
 
-    CloseHandle(AnotherEvent);
-    CloseHandle(EndEvent);
-    CloseHandle(StartEvent);
+    VgaConsoleCleanup();
 }
 
 /* EOF */