* Sync up to trunk head (r64921).
[reactos.git] / subsystems / ntvdm / hardware / vga.c
index ab4423d..f2d3463 100644 (file)
@@ -12,6 +12,7 @@
 
 #include "emulator.h"
 #include "vga.h"
+#include "../bios/vidbios.h"
 
 #include "io.h"
 
 static CONST DWORD MemoryBase[]  = { 0xA0000, 0xA0000, 0xB0000, 0xB8000 };
 static CONST DWORD MemoryLimit[] = { 0xAFFFF, 0xAFFFF, 0xB7FFF, 0xBFFFF };
 
+/*
+ * 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
 
@@ -170,15 +177,77 @@ static CONST COLORREF VgaDefaultPalette[VGA_MAX_COLORS] =
 
 #endif
 
-static CONSOLE_SCREEN_BUFFER_INFO ConsoleInfo;
+/*
+ * Default 16-color palette for foreground and background
+ * (corresponding flags in comments).
+ * Taken from subsystems/win32/winsrv/consrv/frontends/gui/conwnd.c
+ */
+static const COLORREF ConsoleColors[16] =
+{
+    RGB(0, 0, 0),       // (Black)
+    RGB(0, 0, 128),     // BLUE
+    RGB(0, 128, 0),     // GREEN
+    RGB(0, 128, 128),   // BLUE  | GREEN
+    RGB(128, 0, 0),     // RED
+    RGB(128, 0, 128),   // BLUE  | RED
+    RGB(128, 128, 0),   // GREEN | RED
+    RGB(192, 192, 192), // BLUE  | GREEN | RED
+
+    RGB(128, 128, 128), // (Grey)  INTENSITY
+    RGB(0, 0, 255),     // BLUE  | INTENSITY
+    RGB(0, 255, 0),     // GREEN | INTENSITY
+    RGB(0, 255, 255),   // BLUE  | GREEN | INTENSITY
+    RGB(255, 0, 0),     // RED   | INTENSITY
+    RGB(255, 0, 255),   // BLUE  | RED   | INTENSITY
+    RGB(255, 255, 0),   // GREEN | RED   | INTENSITY
+    RGB(255, 255, 255)  // BLUE  | GREEN | RED | INTENSITY
+};
 
-static BYTE VgaMemory[VGA_NUM_BANKS * VGA_BANK_SIZE];
-static LPVOID ConsoleFramebuffer = NULL;
+/*
+ * 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.
+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.
+ */
+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;
-static HPALETTE PaletteHandle = NULL;
-static BOOLEAN DoubleVision = FALSE;
+/* DoubleVision support */
+static BOOLEAN DoubleWidth  = FALSE;
+static BOOLEAN DoubleHeight = FALSE;
+
+/*
+ * VGA Hardware
+ */
+static BYTE VgaMemory[VGA_NUM_BANKS * VGA_BANK_SIZE];
 
 static BYTE VgaLatchRegisters[VGA_NUM_BANKS] = {0, 0, 0, 0};
 
@@ -210,7 +279,7 @@ static BOOLEAN InVerticalRetrace = FALSE;
 static BOOLEAN InHorizontalRetrace = FALSE;
 
 static BOOLEAN NeedsUpdate = FALSE;
-static BOOLEAN ModeChanged = TRUE;
+static BOOLEAN ModeChanged = FALSE;
 static BOOLEAN CursorMoved = FALSE;
 static BOOLEAN PaletteChanged = FALSE;
 
@@ -223,8 +292,296 @@ enum SCREEN_MODE
 
 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 = HeapAlloc(GetProcessHeap(),
+                             HEAP_ZERO_MEMORY,
+                             VDMBufferSize.X * VDMBufferSize.Y
+                                             * sizeof(CHAR_INFO));
+        ASSERT(CharBuff);
+
+        VDMBuffer = HeapAlloc(GetProcessHeap(),
+                              HEAP_ZERO_MEMORY,
+                              VDMBufferSize.X * VDMBufferSize.Y
+                                              * sizeof(CHAR_CELL));
+        *lpVDMBuffer = VDMBuffer;
+        return (VDMBuffer != NULL);
+    }
+    else
+    {
+        /* HACK: Cache -- to be removed in the real implementation */
+        if (CharBuff) HeapFree(GetProcessHeap(), 0, CharBuff);
+        CharBuff = NULL;
+
+        if (VDMBuffer) HeapFree(GetProcessHeap(), 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 */
+
+        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 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 ().
+     *
+     * 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 = HeapAlloc(GetProcessHeap(),
+                         HEAP_ZERO_MEMORY,
+                         TextResolution.X * TextResolution.Y
+                                          * sizeof(CHAR_INFO));
+    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) HeapFree(GetProcessHeap(), 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)
@@ -437,7 +794,7 @@ static VOID VgaWriteCrtc(BYTE Data)
 
 static VOID VgaWriteDac(BYTE Data)
 {
-    INT PaletteIndex;
+    INT i, PaletteIndex;
     PALETTEENTRY Entry;
 
     /* Set the value */
@@ -452,8 +809,20 @@ static VOID VgaWriteDac(BYTE Data)
     Entry.peBlue = VGA_DAC_TO_COLOR(VgaDacRegisters[PaletteIndex * 3 + 2]);
     Entry.peFlags = 0;
 
-    /* Update the palette entry and set the palette change flag */
+    /* Update the palette entry */
     SetPaletteEntries(PaletteHandle, PaletteIndex, 1, &Entry);
+
+    /* Check which text palette entries are affected */
+    for (i = 0; i <= VGA_AC_PAL_F_REG; i++)
+    {
+        if (VgaAcRegisters[i] == PaletteIndex)
+        {
+            /* Update the text palette entry */
+            SetPaletteEntries(TextPaletteHandle, i, 1, &Entry);
+        }
+    }
+
+    /* Set the palette changed flag */
     PaletteChanged = TRUE;
 
     /* Update the index */
@@ -463,6 +832,8 @@ static VOID VgaWriteDac(BYTE Data)
 
 static VOID VgaWriteAc(BYTE Data)
 {
+    PALETTEENTRY Entry;
+
     ASSERT(VgaAcIndex < VGA_AC_MAX_REG);
 
     /* Save the value */
@@ -473,8 +844,17 @@ static VOID VgaWriteAc(BYTE Data)
         // DbgPrint("    AC Palette Writing %d to index %d\n", Data, VgaAcIndex);
         if (VgaAcRegisters[VgaAcIndex] != Data)
         {
-            /* Update the AC register and set the palette change flag */
+            /* Update the AC register */
             VgaAcRegisters[VgaAcIndex] = Data;
+
+            /* Fill the entry structure */
+            Entry.peRed = VGA_DAC_TO_COLOR(VgaDacRegisters[Data * 3]);
+            Entry.peGreen = VGA_DAC_TO_COLOR(VgaDacRegisters[Data * 3 + 1]);
+            Entry.peBlue = VGA_DAC_TO_COLOR(VgaDacRegisters[Data * 3 + 2]);
+            Entry.peFlags = 0;
+
+            /* Update the palette entry and set the palette change flag */
+            SetPaletteEntries(TextPaletteHandle, VgaAcIndex, 1, &Entry);
             PaletteChanged = TRUE;
         }
     }
@@ -506,33 +886,72 @@ static VOID VgaRestoreDefaultPalette(PPALETTEENTRY Entries, USHORT NumOfEntries)
 
 static BOOLEAN VgaInitializePalette(VOID)
 {
-    LPLOGPALETTE Palette;
+    INT i;
+    BOOLEAN Result = FALSE;
+    LPLOGPALETTE Palette, TextPalette;
 
-    /* Allocate storage space for the palette */
+    /* Allocate storage space for the palettes */
     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;
+                                      VGA_MAX_COLORS * sizeof(PALETTEENTRY));
+    TextPalette = (LPLOGPALETTE)HeapAlloc(GetProcessHeap(),
+                                          HEAP_ZERO_MEMORY,
+                                          sizeof(LOGPALETTE) + 
+                                          (VGA_AC_PAL_F_REG + 1) * sizeof(PALETTEENTRY));
+    if ((Palette == NULL) || (TextPalette == NULL)) goto Cleanup;
+
+    /* Initialize the palettes */
+    Palette->palVersion = TextPalette->palVersion = 0x0300;
     Palette->palNumEntries = VGA_MAX_COLORS;
+    TextPalette->palNumEntries = VGA_AC_PAL_F_REG + 1;
 
-    /* Restore the default palette */
+    /* Restore the default graphics palette */
     VgaRestoreDefaultPalette(Palette->palPalEntry, Palette->palNumEntries);
 
-    /* Create the palette */
+    /* Set the default text palette */
+    for (i = 0; i < TextPalette->palNumEntries; i++)
+    {
+        /* Set the palette entries */
+        TextPalette->palPalEntry[i].peRed   = GetRValue(ConsoleColors[i]);
+        TextPalette->palPalEntry[i].peGreen = GetGValue(ConsoleColors[i]);
+        TextPalette->palPalEntry[i].peBlue  = GetBValue(ConsoleColors[i]);
+        TextPalette->palPalEntry[i].peFlags = 0;
+    }
+
+    /* Create the palettes */
     PaletteHandle = CreatePalette(Palette);
+    TextPaletteHandle = CreatePalette(TextPalette);
 
-    /* Free the palette */
-    HeapFree(GetProcessHeap(), 0, Palette);
+    if (PaletteHandle != NULL && TextPaletteHandle != NULL)
+    {
+        /* The palettes have been created successfully */
+        Result = TRUE;
+    }
 
-    /* Fail if the palette wasn't successfully created... */
-    if (PaletteHandle == NULL) return FALSE;
+Cleanup:
+    /* Free the palettes */
+    if (Palette) HeapFree(GetProcessHeap(), 0, Palette);
+    if (TextPalette) HeapFree(GetProcessHeap(), 0, TextPalette);
 
-    /* ... otherwise return success */
-    return TRUE;
+    if (!Result)
+    {
+        /* Something failed, delete the palettes */
+        if (PaletteHandle) DeleteObject(PaletteHandle);
+        if (TextPaletteHandle) DeleteObject(TextPaletteHandle);
+    }
+
+    return Result;
+}
+
+static VOID VgaSetActiveScreenBuffer(HANDLE ScreenBuffer)
+{
+    /* Set the active buffer */
+    SetConsoleActiveScreenBuffer(ScreenBuffer);
+
+    /* Reinitialize the VDM menu */
+    DestroyVdmMenu();
+    CreateVdmMenu(ScreenBuffer);
 }
 
 static BOOL VgaEnterGraphicsMode(PCOORD Resolution)
@@ -547,20 +966,14 @@ static BOOL VgaEnterGraphicsMode(PCOORD Resolution)
     LONG Height = Resolution->Y;
 
     /* Use DoubleVision mode if the resolution is too small */
-    if (Width < VGA_MINIMUM_WIDTH && Height < VGA_MINIMUM_HEIGHT)
-    {
-        DoubleVision = TRUE;
-        Width  *= 2;
-        Height *= 2;
-    }
-    else
-    {
-        DoubleVision = FALSE;
-    }
+    DoubleWidth = (Width < VGA_MINIMUM_WIDTH);
+    if (DoubleWidth) Width *= 2;
+    DoubleHeight = (Height < VGA_MINIMUM_HEIGHT);
+    if (DoubleHeight) Height *= 2;
 
     /* Fill the bitmap info header */
-    ZeroMemory(&BitmapInfo->bmiHeader, sizeof(BITMAPINFOHEADER));
-    BitmapInfo->bmiHeader.biSize   = sizeof(BITMAPINFOHEADER);
+    RtlZeroMemory(&BitmapInfo->bmiHeader, sizeof(BitmapInfo->bmiHeader));
+    BitmapInfo->bmiHeader.biSize   = sizeof(BitmapInfo->bmiHeader);
     BitmapInfo->bmiHeader.biWidth  = Width;
     BitmapInfo->bmiHeader.biHeight = Height;
     BitmapInfo->bmiHeader.biBitCount = 8;
@@ -589,10 +1002,10 @@ static BOOL VgaEnterGraphicsMode(PCOORD Resolution)
     ConsoleMutex = GraphicsBufferInfo.hMutex;
 
     /* Clear the framebuffer */
-    ZeroMemory(ConsoleFramebuffer, BitmapInfo->bmiHeader.biSizeImage);
+    RtlZeroMemory(ConsoleFramebuffer, BitmapInfo->bmiHeader.biSizeImage);
 
     /* Set the active buffer */
-    SetConsoleActiveScreenBuffer(GraphicsConsoleBuffer);
+    VgaSetActiveScreenBuffer(GraphicsConsoleBuffer);
 
     /* Set the graphics mode palette */
     SetConsolePalette(GraphicsConsoleBuffer,
@@ -611,7 +1024,7 @@ static VOID VgaLeaveGraphicsMode(VOID)
     ReleaseMutex(ConsoleMutex);
 
     /* Switch back to the default console text buffer */
-    // SetConsoleActiveScreenBuffer(TextConsoleBuffer);
+    // VgaSetActiveScreenBuffer(TextConsoleBuffer);
 
     /* Cleanup the video data */
     CloseHandle(ConsoleMutex);
@@ -619,47 +1032,43 @@ static VOID VgaLeaveGraphicsMode(VOID)
     ConsoleFramebuffer = NULL;
     CloseHandle(GraphicsConsoleBuffer);
     GraphicsConsoleBuffer = NULL;
-    DoubleVision = FALSE;
+
+    DoubleWidth  = FALSE;
+    DoubleHeight = FALSE;
 }
 
 static BOOL VgaEnterTextMode(PCOORD Resolution)
 {
-    SMALL_RECT ConRect;
+    DPRINT1("VgaEnterTextMode\n");
 
     /* Switch to the text buffer */
-    SetConsoleActiveScreenBuffer(TextConsoleBuffer);
+    VgaSetActiveScreenBuffer(TextConsoleBuffer);
 
-    /* Resize the console */
-    ConRect.Left   = 0; // ConsoleInfo.srWindow.Left;
-    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);
+    /* Adjust the text framebuffer if we changed the resolution */
+    if (TextResolution.X != Resolution->X ||
+        TextResolution.Y != Resolution->Y)
+    {
+        VgaDetachFromConsole(TRUE);
 
-    /* Allocate a framebuffer */
-    ConsoleFramebuffer = HeapAlloc(GetProcessHeap(),
-                                   HEAP_ZERO_MEMORY,
-                                   Resolution->X * Resolution->Y
-                                   * sizeof(CHAR_INFO));
-    if (ConsoleFramebuffer == NULL)
+        /*
+         * VgaAttachToConsoleInternal sets TextResolution to the
+         * new resolution and updates ConsoleInfo.
+         */
+        if (!VgaAttachToConsoleInternal(Resolution))
+        {
+            DisplayMessage(L"An unexpected error occurred!\n");
+            EmulatorTerminate();
+            return FALSE;
+        }
+    }
+    else
     {
-        DisplayMessage(L"An unexpected error occurred!\n");
-        VdmRunning = FALSE;
-        return FALSE;
+        VgaUpdateCursorPosition();
     }
 
+    /* The active framebuffer is now the text framebuffer */
+    ConsoleFramebuffer = TextFramebuffer;
+
     /*
      * Set the text mode palette.
      *
@@ -670,7 +1079,7 @@ static BOOL VgaEnterTextMode(PCOORD Resolution)
      * screen-buffers, which is a new feature on ReactOS).
      */
     SetConsolePalette(TextConsoleBuffer,
-                      PaletteHandle,
+                      TextPaletteHandle,
                       SYSPAL_NOSTATIC256);
 
     /* Set the screen mode flag */
@@ -681,8 +1090,7 @@ static BOOL VgaEnterTextMode(PCOORD Resolution)
 
 static VOID VgaLeaveTextMode(VOID)
 {
-    /* Free the old framebuffer */
-    HeapFree(GetProcessHeap(), 0, ConsoleFramebuffer);
+    /* Reset the active framebuffer */
     ConsoleFramebuffer = NULL;
 }
 
@@ -707,8 +1115,8 @@ static VOID VgaChangeMode(VOID)
         /* Enter new text mode */
         if (!VgaEnterTextMode(&Resolution))
         {
-            DisplayMessage(L"An unexpected VGA error occurred while switching into text mode.");
-            VdmRunning = FALSE;
+            DisplayMessage(L"An unexpected VGA error occurred while switching into text mode. Error: %u", GetLastError());
+            EmulatorTerminate();
             return;
         }
     }
@@ -717,8 +1125,8 @@ static VOID VgaChangeMode(VOID)
         /* Enter graphics mode */
         if (!VgaEnterGraphicsMode(&Resolution))
         {
-            DisplayMessage(L"An unexpected VGA error occurred while switching into graphics mode.");
-            VdmRunning = FALSE;
+            DisplayMessage(L"An unexpected VGA error occurred while switching into graphics mode. Error: %u", GetLastError());
+            EmulatorTerminate();
             return;
         }
     }
@@ -736,7 +1144,7 @@ static VOID VgaChangeMode(VOID)
 
 static VOID VgaUpdateFramebuffer(VOID)
 {
-    INT i, j, k;
+    SHORT i, j, k;
     COORD Resolution = VgaGetDisplayResolution();
     DWORD AddressSize = VgaGetAddressSize();
     DWORD Address = MAKEWORD(VgaCrtcRegisters[VGA_CRTC_START_ADDR_LOW_REG],
@@ -754,6 +1162,7 @@ static VOID VgaUpdateFramebuffer(VOID)
     {
         /* Graphics mode */
         PBYTE GraphicsBuffer = (PBYTE)ConsoleFramebuffer;
+        DWORD InterlaceHighBit = VGA_INTERLACE_HIGH_BIT;
 
         /*
          * Synchronize access to the graphics framebuffer
@@ -761,9 +1170,21 @@ static VOID VgaUpdateFramebuffer(VOID)
          */
         WaitForSingleObject(ConsoleMutex, INFINITE);
 
+        /* Shift the high bit right by 1 in odd/even mode */
+        if (VgaGcRegisters[VGA_GC_MODE_REG] & VGA_GC_MODE_OE)
+        {
+            InterlaceHighBit >>= 1;
+        }
+
         /* Loop through the scanlines */
         for (i = 0; i < Resolution.Y; i++)
         {
+            if ((VgaGcRegisters[VGA_GC_MISC_REG] & VGA_GC_MISC_OE) && (i & 1))
+            {
+                /* Odd-numbered line in interlaced mode - set the high bit */
+                Address |= InterlaceHighBit;
+            }
+
             /* Loop through the pixels */
             for (j = 0; j < Resolution.X; j++)
             {
@@ -817,10 +1238,10 @@ static VOID VgaUpdateFramebuffer(VOID)
                          * 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 / 8)) * AddressSize];
-                        BYTE HighPlaneData = VgaMemory[(((j / 4) % 2) + 2) * VGA_BANK_SIZE
-                                                       + (Address + (j / 8)) * AddressSize];
+                        DWORD BankNumber = (j / 4) % 2;
+                        DWORD Offset = Address + (j / 8);
+                        BYTE LowPlaneData = VgaMemory[BankNumber * VGA_BANK_SIZE + Offset * AddressSize];
+                        BYTE HighPlaneData = VgaMemory[(BankNumber + 2) * VGA_BANK_SIZE + Offset * AddressSize];
 
                         /* Extract the two bits from each plane */
                         LowPlaneData = (LowPlaneData >> (6 - ((j % 4) * 2))) & 3;
@@ -884,14 +1305,14 @@ static VOID VgaUpdateFramebuffer(VOID)
                 }
 
                 /* Take into account DoubleVision mode when checking for pixel updates */
-                if (DoubleVision)
+                if (DoubleWidth && DoubleHeight)
                 {
                     /* Now check if the resulting pixel data has changed */
-                    if (GraphicsBuffer[(i * Resolution.X * 4) + (j * 2)] != PixelData)
+                    if (GraphicsBuffer[(i * 2 * Resolution.X * 2) + (j * 2)] != PixelData)
                     {
                         /* Yes, write the new value */
-                        GraphicsBuffer[(i * Resolution.X * 4) + (j * 2)] = PixelData;
-                        GraphicsBuffer[(i * Resolution.X * 4) + (j * 2 + 1)] = PixelData;
+                        GraphicsBuffer[(i * 2 * Resolution.X * 2) + (j * 2)] = PixelData;
+                        GraphicsBuffer[(i * 2 * Resolution.X * 2) + (j * 2 + 1)] = PixelData;
                         GraphicsBuffer[((i * 2 + 1) * Resolution.X * 2) + (j * 2)] = PixelData;
                         GraphicsBuffer[((i * 2 + 1) * Resolution.X * 2) + (j * 2 + 1)] = PixelData;
 
@@ -899,7 +1320,33 @@ static VOID VgaUpdateFramebuffer(VOID)
                         VgaMarkForUpdate(i, j);
                     }
                 }
-                else
+                else if (DoubleWidth && !DoubleHeight)
+                {
+                    /* Now check if the resulting pixel data has changed */
+                    if (GraphicsBuffer[(i * Resolution.X * 2) + (j * 2)] != PixelData)
+                    {
+                        /* Yes, write the new value */
+                        GraphicsBuffer[(i * Resolution.X * 2) + (j * 2)] = PixelData;
+                        GraphicsBuffer[(i * Resolution.X * 2) + (j * 2 + 1)] = PixelData;
+
+                        /* Mark the specified pixel as changed */
+                        VgaMarkForUpdate(i, j);
+                    }
+                }
+                else if (!DoubleWidth && DoubleHeight)
+                {
+                    /* Now check if the resulting pixel data has changed */
+                    if (GraphicsBuffer[(i * 2 * Resolution.X) + j] != PixelData)
+                    {
+                        /* Yes, write the new value */
+                        GraphicsBuffer[(i * 2 * Resolution.X) + j] = PixelData;
+                        GraphicsBuffer[((i * 2 + 1) * Resolution.X) + j] = PixelData;
+
+                        /* Mark the specified pixel as changed */
+                        VgaMarkForUpdate(i, j);
+                    }
+                }
+                else // if (!DoubleWidth && !DoubleHeight)
                 {
                     /* Now check if the resulting pixel data has changed */
                     if (GraphicsBuffer[i * Resolution.X + j] != PixelData)
@@ -913,8 +1360,17 @@ static VOID VgaUpdateFramebuffer(VOID)
                 }
             }
 
-            /* Move to the next scanline */
-            Address += ScanlineSize;
+            if ((VgaGcRegisters[VGA_GC_MISC_REG] & VGA_GC_MISC_OE) && (i & 1))
+            {
+                /* Clear the high bit */
+                Address &= ~InterlaceHighBit;
+            }
+
+            if (!(VgaGcRegisters[VGA_GC_MISC_REG] & VGA_GC_MISC_OE) || (i & 1))
+            {
+                /* Move to the next scanline */
+                Address += ScanlineSize;
+            }
         }
 
         /*
@@ -927,8 +1383,8 @@ static VOID VgaUpdateFramebuffer(VOID)
     {
         /* Text mode */
         DWORD CurrentAddr;
-        PCHAR_INFO CharBuffer = (PCHAR_INFO)ConsoleFramebuffer;
-        CHAR_INFO CharInfo;
+        PCHAR_CELL CharBuffer = (PCHAR_CELL)ConsoleFramebuffer;
+        CHAR_CELL CharInfo;
 
         /* Loop through the scanlines */
         for (i = 0; i < Resolution.Y; i++)
@@ -939,14 +1395,14 @@ static VOID VgaUpdateFramebuffer(VOID)
                 CurrentAddr = LOWORD((Address + j) * AddressSize);
 
                 /* Plane 0 holds the character itself */
-                CharInfo.Char.AsciiChar = VgaMemory[CurrentAddr];
+                CharInfo.Char = VgaMemory[CurrentAddr];
 
                 /* Plane 1 holds the attribute */
                 CharInfo.Attributes = VgaMemory[CurrentAddr + VGA_BANK_SIZE];
 
                 /* Now check if the resulting character data has changed */
-                if ((CharBuffer[i * Resolution.X + j].Char.AsciiChar != CharInfo.Char.AsciiChar)
-                    || (CharBuffer[i * Resolution.X + j].Attributes != CharInfo.Attributes))
+                if ((CharBuffer[i * Resolution.X + j].Char != CharInfo.Char) ||
+                    (CharBuffer[i * Resolution.X + j].Attributes != CharInfo.Attributes))
                 {
                     /* Yes, write the new value */
                     CharBuffer[i * Resolution.X + j] = CharInfo;
@@ -993,8 +1449,10 @@ static VOID VgaUpdateTextCursor(VOID)
     Location += (VgaCrtcRegisters[VGA_CRTC_CURSOR_END_REG] >> 5) & 3;
 
     /* Find the coordinates of the new position */
-    Position.X = (WORD)(Location % ScanlineSize);
-    Position.Y = (WORD)(Location / ScanlineSize);
+    Position.X = (SHORT)(Location % ScanlineSize);
+    Position.Y = (SHORT)(Location / ScanlineSize);
+
+    DPRINT1("VgaUpdateTextCursor: X = %d ; Y = %d\n", Position.X, Position.Y);
 
     /* Update the physical cursor */
     SetConsoleCursorInfo(TextConsoleBuffer, &CursorInfo);
@@ -1004,7 +1462,7 @@ static VOID VgaUpdateTextCursor(VOID)
     CursorMoved = FALSE;
 }
 
-static BYTE WINAPI VgaReadPort(ULONG Port)
+static BYTE WINAPI VgaReadPort(USHORT Port)
 {
     DPRINT("VgaReadPort: Port 0x%X\n", Port);
 
@@ -1096,7 +1554,7 @@ static BYTE WINAPI VgaReadPort(ULONG Port)
     return 0;
 }
 
-static VOID WINAPI VgaWritePort(ULONG Port, BYTE Data)
+static VOID WINAPI VgaWritePort(USHORT Port, BYTE Data)
 {
     DPRINT("VgaWritePort: Port 0x%X, Data 0x%02X\n", Port, Data);
 
@@ -1361,32 +1819,27 @@ VOID VgaRefreshDisplay(VOID)
     {
         /* 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 */
-        COORD Origin = { UpdateRectangle.Left, UpdateRectangle.Top };
         ConsoleBufferHandle = TextConsoleBuffer;
-
-        /* Write the data to the console */
-        WriteConsoleOutputA(TextConsoleBuffer,
-                            (PCHAR_INFO)ConsoleFramebuffer,
-                            Resolution,
-                            Origin,
-                            &UpdateRectangle);
-    }
-
-    /* In DoubleVision mode, scale the update rectangle */
-    if (DoubleVision)
-    {
-        UpdateRectangle.Left *= 2;
-        UpdateRectangle.Top  *= 2;
-        UpdateRectangle.Right  = UpdateRectangle.Right  * 2 + 1;
-        UpdateRectangle.Bottom = UpdateRectangle.Bottom * 2 + 1;
     }
 
     /* Redraw the screen */
-    InvalidateConsoleDIBits(ConsoleBufferHandle, &UpdateRectangle);
+    __InvalidateConsoleDIBits(ConsoleBufferHandle, &UpdateRectangle);
 
     /* Clear the update flag */
     NeedsUpdate = FALSE;
@@ -1475,7 +1928,7 @@ VOID VgaWriteMemory(DWORD Address, LPBYTE Buffer, DWORD Size)
 
 VOID VgaClearMemory(VOID)
 {
-    ZeroMemory(VgaMemory, sizeof(VgaMemory));
+    RtlZeroMemory(VgaMemory, sizeof(VgaMemory));
 }
 
 VOID VgaResetPalette(VOID)
@@ -1488,24 +1941,116 @@ VOID VgaResetPalette(VOID)
     PaletteChanged = TRUE;
 }
 
+VOID VgaWriteFont(UINT FontNumber, CONST UCHAR *FontData, UINT Height)
+{
+    UINT i, j;
+    PUCHAR FontMemory = (PUCHAR)&VgaMemory[VGA_BANK_SIZE * VGA_FONT_BANK + (FontNumber * VGA_FONT_SIZE)];
+
+    ASSERT(Height <= VGA_MAX_FONT_HEIGHT);
+
+    for (i = 0 ; i < VGA_FONT_CHARACTERS; i++)
+    {
+        /* Write the character */
+        for (j = 0; j < Height; j++)
+        {
+            FontMemory[i * VGA_MAX_FONT_HEIGHT + j] = FontData[i * Height + j];
+        }
+
+        /* Clear the unused part */
+        for (j = Height; j < VGA_MAX_FONT_HEIGHT; j++)
+        {
+            FontMemory[i * VGA_MAX_FONT_HEIGHT + j] = 0;
+        }
+    }
+}
+
+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)
 {
     /* Save the default text-mode console output handle */
-    if (TextHandle == INVALID_HANDLE_VALUE) return FALSE;
+    if (!IsConsoleHandle(TextHandle)) return FALSE;
     TextConsoleBuffer = TextHandle;
 
-    /* Save the console information */
-    if (!GetConsoleScreenBufferInfo(TextConsoleBuffer, &ConsoleInfo))
+    /* Save the original cursor and console screen buffer information */
+    if (!GetConsoleCursorInfo(TextConsoleBuffer, &OrgConsoleCursorInfo) ||
+        !GetConsoleScreenBufferInfo(TextConsoleBuffer, &OrgConsoleBufferInfo))
     {
         return FALSE;
     }
+    ConsoleInfo = OrgConsoleBufferInfo;
 
     /* Initialize the VGA palette and fail if it isn't successfully created */
     if (!VgaInitializePalette()) return FALSE;
     /***/ VgaResetPalette(); /***/
 
     /* Switch to the text buffer */
-    SetConsoleActiveScreenBuffer(TextConsoleBuffer);
+    VgaSetActiveScreenBuffer(TextConsoleBuffer);
 
     /* Clear the VGA memory */
     VgaClearMemory();
@@ -1529,4 +2074,24 @@ BOOLEAN VgaInitialize(HANDLE TextHandle)
     return TRUE;
 }
 
+VOID VgaCleanup(VOID)
+{
+    if (ScreenMode == GRAPHICS_MODE)
+    {
+        /* Leave the current graphics mode */
+        VgaLeaveGraphicsMode();
+    }
+    else
+    {
+        /* Leave the current text mode */
+        VgaLeaveTextMode();
+    }
+
+    VgaDetachFromConsole(FALSE);
+
+    CloseHandle(AnotherEvent);
+    CloseHandle(EndEvent);
+    CloseHandle(StartEvent);
+}
+
 /* EOF */