[NTVDM]
[reactos.git] / reactos / subsystems / mvdm / ntvdm / bios / vidbios.c
index f135330..cda45c7 100644 (file)
@@ -1,30 +1,40 @@
 /*
  * COPYRIGHT:       GPL - See COPYING in the top level directory
  * PROJECT:         ReactOS Virtual DOS Machine
- * FILE:            vidbios.c
- * PURPOSE:         VDM Video BIOS Support Library
+ * FILE:            subsystems/mvdm/ntvdm/bios/vidbios.c
+ * PURPOSE:         VDM 32-bit Video BIOS Support Library
  * PROGRAMMERS:     Aleksandar Andrejevic <theflash AT sdf DOT lonestar DOT org>
  *                  Hermes Belusca-Maito (hermes.belusca@sfr.fr)
  */
 
 /* INCLUDES *******************************************************************/
 
+#include "ntvdm.h"
+
 #define NDEBUG
+#include <debug.h>
+
+/* BIOS Version number and Copyright */
+#include <reactos/buildno.h>
+#include <reactos/version.h>
 
 #include "emulator.h"
 #include "cpu/cpu.h"
 #include "cpu/bop.h"
+#include "memory.h"
 
 #include "bios.h"
+#include "bios32/bios32p.h"
+#include "rom.h"
+#include "bios32/vbe.h"
 // #include "vidbios.h"
+#include "bios32/vidbios32.h"
 
 #include "io.h"
-#include "hardware/video/vga.h"
-
-/* DEFINES ********************************************************************/
-
-/* BOP Identifiers */
-#define BOP_VIDEO_INT   0x10
+#include "hardware/video/svga.h"
+/**/
+#include "../console/video.h"
+/**/
 
 /* MACROS *********************************************************************/
 
 
 /* PRIVATE VARIABLES **********************************************************/
 
+/*
+ * WARNING! For compatibility purposes the string "IBM" should be at C000:001E.
+ */
+static const CHAR BiosInfo[] =
+    "00000000000 Emulation of IBM VGA Compatible ROM\0"
+    "CL-GD5434 VGA BIOS Version 1.41  \r\n"
+    "Copyright (C) ReactOS Team 1996-"COPYRIGHT_YEAR"\r\n"
+    "The original CL-GD5434 card was created by Cirrus Logic, Inc.\r\n\0"
+    "BIOS Date: 06/17/13\0";
+
+C_ASSERT(sizeof(BiosInfo)-1 <= 0xFF-0x05); // Ensures that we won't overflow on the Video Code
+
+
 /*
  * VGA Register Configurations for BIOS Video Modes.
  * The configurations were checked against SeaBIOS VGA BIOS.
  */
+
 static VGA_REGISTERS VideoMode_40x25_text =
 {
     /* Miscellaneous Register */
@@ -254,41 +278,6 @@ static VGA_REGISTERS VideoMode_320x200_256color =
      0x0C, 0x0D, 0x0E, 0x0F, 0x41, 0x00, 0x0F, 0x00, 0x00}
 };
 
-/* See http://wiki.osdev.org/Drawing_In_Protected_Mode#Locating_Video_Memory */
-static PVGA_REGISTERS VideoModes[BIOS_MAX_VIDEO_MODE + 1] =
-{
-    &VideoMode_40x25_text,          /* Mode 00h */      // 16 color (mono)
-    &VideoMode_40x25_text,          /* Mode 01h */      // 16 color
-    &VideoMode_80x25_text,          /* Mode 02h */      // 16 color (mono)
-    &VideoMode_80x25_text,          /* Mode 03h */      // 16 color
-    &VideoMode_320x200_4color,      /* Mode 04h */      // CGA 4 color
-    &VideoMode_320x200_4color,      /* Mode 05h */      // CGA same (m) (uses 3rd CGA palette)
-    &VideoMode_640x200_2color,      /* Mode 06h */      // CGA 640*200 2 color
-    NULL,                           /* Mode 07h */      // MDA monochrome text 80*25
-    NULL,                           /* Mode 08h */      // PCjr
-    NULL,                           /* Mode 09h */      // PCjr
-    NULL,                           /* Mode 0Ah */      // PCjr
-    NULL,                           /* Mode 0Bh */      // Reserved
-    NULL,                           /* Mode 0Ch */      // Reserved
-    &VideoMode_320x200_16color,     /* Mode 0Dh */      // EGA 320*200 16 color
-    &VideoMode_640x200_16color,     /* Mode 0Eh */      // EGA 640*200 16 color
-    NULL,                           /* Mode 0Fh */      // EGA 640*350 mono
-    &VideoMode_640x350_16color,     /* Mode 10h */      // EGA 640*350 HiRes 16 color
-    &VideoMode_640x480_2color,      /* Mode 11h */      // VGA 640*480 mono
-    &VideoMode_640x480_16color,     /* Mode 12h */      // VGA
-    &VideoMode_320x200_256color,    /* Mode 13h */      // VGA
-};
-
-// FIXME: Are they computable with the previous data ??
-// Values taken from DOSBox.
-static WORD VideoModePageSize[BIOS_MAX_VIDEO_MODE + 1] =
-{
-    0x0800, 0x0800, 0x1000, 0x1000,
-    0x4000, 0x4000, 0x4000, 0x1000,
-    0x0000, 0x0000, 0x0000, 0x0000,
-    0x0000, 0x2000, 0x4000, 0x8000,
-    0x8000, 0xA000, 0xA000, 0x2000
-};
 
 /*
  * BIOS Mode Palettes
@@ -1941,6 +1930,53 @@ static CONST UCHAR Font8x16[VGA_FONT_CHARACTERS * 16] =
     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
 };
 
+
+/*
+ * Table of supported Video Modes.
+ *
+ * See http://wiki.osdev.org/Drawing_In_Protected_Mode#Locating_Video_Memory
+ * Values of PageSize taken from DOSBox.
+ */
+
+typedef struct _VGA_MODE
+{
+    PVGA_REGISTERS VgaRegisters;
+    WORD PageSize;
+    WORD CharacterWidth;
+    WORD CharacterHeight;
+    // PCOLORREF Palette;
+} VGA_MODE, *PVGA_MODE;
+
+static CONST VGA_MODE VideoModes[BIOS_MAX_VIDEO_MODE + 1] =
+{
+    {&VideoMode_40x25_text,         0x0800, 9, 16},    /* Mode 00h - 16 color (mono)                       */
+    {&VideoMode_40x25_text,         0x0800, 9, 16},    /* Mode 01h - 16 color                              */
+    {&VideoMode_80x25_text,         0x1000, 9, 16},    /* Mode 02h - 16 color (mono)                       */
+    {&VideoMode_80x25_text,         0x1000, 9, 16},    /* Mode 03h - 16 color                              */
+    {&VideoMode_320x200_4color,     0x4000, 8,  8},    /* Mode 04h - CGA 4 color                           */
+    {&VideoMode_320x200_4color,     0x4000, 8,  8},    /* Mode 05h - CGA same (m) (uses 3rd CGA palette)   */
+    {&VideoMode_640x200_2color,     0x4000, 8,  8},    /* Mode 06h - CGA 640*200 2 color                   */
+    {NULL,                          0x1000, 1,  1},    /* Mode 07h - MDA monochrome text 80*25             */
+    {NULL,                          0x0000, 1,  1},    /* Mode 08h - PCjr                                  */
+    {NULL,                          0x0000, 1,  1},    /* Mode 09h - PCjr                                  */
+    {NULL,                          0x0000, 1,  1},    /* Mode 0Ah - PCjr                                  */
+    {NULL,                          0x0000, 1,  1},    /* Mode 0Bh - Reserved                              */
+    {NULL,                          0x0000, 1,  1},    /* Mode 0Ch - Reserved                              */
+    {&VideoMode_320x200_16color,    0x2000, 8,  8},    /* Mode 0Dh - EGA 320*200 16 color                  */
+    {&VideoMode_640x200_16color,    0x4000, 8,  8},    /* Mode 0Eh - EGA 640*200 16 color                  */
+    {NULL,                          0x8000, 1,  1},    /* Mode 0Fh - EGA 640*350 mono                      */
+    {&VideoMode_640x350_16color,    0x8000, 8, 14},    /* Mode 10h - EGA 640*350 HiRes 16 color            */
+    {&VideoMode_640x480_2color,     0xA000, 8, 16},    /* Mode 11h - VGA 640*480 mono                      */
+    {&VideoMode_640x480_16color,    0xA000, 8, 16},    /* Mode 12h - VGA                                   */
+    {&VideoMode_320x200_256color,   0x2000, 8,  8},    /* Mode 13h - VGA                                   */
+};
+
+#define IS_TEXT_MODE(ModeNumber)    \
+    (((ModeNumber) >= 0x00 && (ModeNumber) <= 0x03) || ((ModeNumber) == 0x07))
+
+static PVGA_STATIC_FUNC_TABLE VgaStaticFuncTable;
+static BOOLEAN VbeInitialized = FALSE;
+
 /* PRIVATE FUNCTIONS **********************************************************/
 
 static BOOLEAN VidBiosScrollWindow(SCROLL_DIRECTION Direction,
@@ -1955,6 +1991,13 @@ static BOOLEAN VidBiosScrollWindow(SCROLL_DIRECTION Direction,
 
     WORD WindowWidth, WindowHeight;
 
+    /* TODO: This function doesn't work in non-alphanumeric modes yet */
+    if (Bda->VideoMode > 3)
+    {
+        DPRINT1("VidBiosScrollWindow: not implemented for mode 0%Xh\n", Bda->VideoMode);
+        return FALSE;
+    }
+
     /* Fixup the rectangle if needed */
     Rectangle.Left   = min(max(Rectangle.Left  , 0), Bda->ScreenColumns - 1);
     Rectangle.Right  = min(max(Rectangle.Right , 0), Bda->ScreenColumns - 1);
@@ -1991,10 +2034,10 @@ static BOOLEAN VidBiosScrollWindow(SCROLL_DIRECTION Direction,
             /* Move text lines up */
             for (i = Rectangle.Top + Amount; i <= Rectangle.Bottom; i++)
             {
-                EmulatorWriteMemory(&EmulatorContext,
-                                    VideoAddress + ((i - Amount) * Bda->ScreenColumns + Rectangle.Left) * sizeof(WORD),
-                       REAL_TO_PHYS(VideoAddress + ( i           * Bda->ScreenColumns + Rectangle.Left) * sizeof(WORD)),
-                                    (Rectangle.Right - Rectangle.Left + 1) * sizeof(WORD));
+                EmulatorCopyMemory(&EmulatorContext,
+                                   VideoAddress + ((i - Amount) * Bda->ScreenColumns + Rectangle.Left) * sizeof(WORD),
+                                   VideoAddress + ( i           * Bda->ScreenColumns + Rectangle.Left) * sizeof(WORD),
+                                   (Rectangle.Right - Rectangle.Left + 1) * sizeof(WORD));
             }
 
             /* Fill the bottom of the rectangle */
@@ -2019,10 +2062,10 @@ static BOOLEAN VidBiosScrollWindow(SCROLL_DIRECTION Direction,
             /* Move text lines down */
             for (i = Rectangle.Bottom - Amount; i >= Rectangle.Top; i--)
             {
-                EmulatorWriteMemory(&EmulatorContext,
-                                    VideoAddress + ((i + Amount) * Bda->ScreenColumns + Rectangle.Left) * sizeof(WORD),
-                       REAL_TO_PHYS(VideoAddress + ( i           * Bda->ScreenColumns + Rectangle.Left) * sizeof(WORD)),
-                                    (Rectangle.Right - Rectangle.Left + 1) * sizeof(WORD));
+                EmulatorCopyMemory(&EmulatorContext,
+                                   VideoAddress + ((i + Amount) * Bda->ScreenColumns + Rectangle.Left) * sizeof(WORD),
+                                   VideoAddress + ( i           * Bda->ScreenColumns + Rectangle.Left) * sizeof(WORD),
+                                   (Rectangle.Right - Rectangle.Left + 1) * sizeof(WORD));
             }
 
             /* Fill the top of the rectangle */
@@ -2046,10 +2089,10 @@ static BOOLEAN VidBiosScrollWindow(SCROLL_DIRECTION Direction,
             /* Move text lines left */
             for (i = Rectangle.Top; i <= Rectangle.Bottom; i++)
             {
-                EmulatorWriteMemory(&EmulatorContext,
-                                    VideoAddress + (i * Bda->ScreenColumns + Rectangle.Left         ) * sizeof(WORD),
-                       REAL_TO_PHYS(VideoAddress + (i * Bda->ScreenColumns + Rectangle.Left + Amount) * sizeof(WORD)),
-                                    (Rectangle.Right - Rectangle.Left - Amount + 1) * sizeof(WORD));
+                EmulatorCopyMemory(&EmulatorContext,
+                                   VideoAddress + (i * Bda->ScreenColumns + Rectangle.Left         ) * sizeof(WORD),
+                                   VideoAddress + (i * Bda->ScreenColumns + Rectangle.Left + Amount) * sizeof(WORD),
+                                   (Rectangle.Right - Rectangle.Left - Amount + 1) * sizeof(WORD));
             }
 
             /* Fill the right of the rectangle */
@@ -2069,19 +2112,21 @@ static BOOLEAN VidBiosScrollWindow(SCROLL_DIRECTION Direction,
 
         case SCROLL_RIGHT:
         {
+            INT Right;
+
             /* Move text lines right */
             for (i = Rectangle.Top; i <= Rectangle.Bottom; i++)
             {
-                EmulatorWriteMemory(&EmulatorContext,
-                                    VideoAddress + (i * Bda->ScreenColumns + Rectangle.Left + Amount) * sizeof(WORD),
-                       REAL_TO_PHYS(VideoAddress + (i * Bda->ScreenColumns + Rectangle.Left         ) * sizeof(WORD)),
-                                    (Rectangle.Right - Rectangle.Left - Amount + 1) * sizeof(WORD));
+                EmulatorCopyMemory(&EmulatorContext,
+                                   VideoAddress + (i * Bda->ScreenColumns + Rectangle.Left + Amount) * sizeof(WORD),
+                                   VideoAddress + (i * Bda->ScreenColumns + Rectangle.Left         ) * sizeof(WORD),
+                                   (Rectangle.Right - Rectangle.Left - Amount + 1) * sizeof(WORD));
             }
 
             /* Fill the left of the rectangle */
+            Right = Rectangle.Left + Amount - 1;
             for (i = Rectangle.Top; i <= Rectangle.Bottom; i++)
             {
-                INT Right = Rectangle.Left + Amount - 1;
                 for (j = Rectangle.Left; j <= Right; j++)
                 {
                     EmulatorWriteMemory(&EmulatorContext,
@@ -2110,7 +2155,7 @@ static __inline VOID VgaSetSinglePaletteRegister(BYTE Index, BYTE Value)
 
 static BOOLEAN VgaSetRegisters(PVGA_REGISTERS Registers)
 {
-    INT i;
+    UINT i;
 
     if (Registers == NULL) return FALSE;
 
@@ -2126,7 +2171,14 @@ static BOOLEAN VgaSetRegisters(PVGA_REGISTERS Registers)
     Bda->CrtBasePort = (Registers->Misc & 0x01) ? VGA_CRTC_INDEX_COLOR
                                                 : VGA_CRTC_INDEX_MONO;
     /* Bit 1 indicates whether display is color (0) or monochrome (1) */
-    Bda->VGAOptions  = (Bda->VGAOptions & 0xFD) | (!(Registers->Misc & 0x01) << 1);
+    Bda->VGAOptions     = (Bda->VGAOptions     & 0xFD) | (!(Registers->Misc & 0x01) << 1);
+    Bda->CrtModeControl = (Bda->CrtModeControl & 0xFB) | (!(Registers->Misc & 0x01) << 1);
+
+    /* Update blink bit in BDA */
+    if (Registers->Attribute[VGA_AC_CONTROL_REG] & VGA_AC_CONTROL_BLINK)
+        Bda->CrtModeControl |= (1 << 5);
+    else
+        Bda->CrtModeControl &= ~(1 << 5);
 
     /* Turn the video off */
     IOWriteB(VGA_SEQ_INDEX, VGA_SEQ_CLOCK_REG);
@@ -2235,13 +2287,13 @@ static VOID VgaChangePalette(BYTE ModeNumber)
     {
         /* VGA modes */
         Palette = VgaPalette;
-        Size    = sizeof(VgaPalette)/sizeof(VgaPalette[0]);
+        Size    = ARRAYSIZE(VgaPalette);
     }
     else if (ModeNumber == 0x10) // || (ModeNumber == 0x0D) || (ModeNumber == 0x0E)
     {
         /* EGA HiRes mode */
         Palette = EgaPalette__HiRes;
-        Size    = sizeof(EgaPalette__HiRes)/sizeof(EgaPalette__HiRes[0]);
+        Size    = ARRAYSIZE(EgaPalette__HiRes);
     }
 #if 0
     else if ((ModeNumber == 0x04) || (ModeNumber == 0x05))
@@ -2251,34 +2303,27 @@ static VOID VgaChangePalette(BYTE ModeNumber)
          * bright versions of CGA palettes 0 and 1
          */
         Palette = CgaPalette2;
-        Size    = sizeof(CgaPalette2)/sizeof(CgaPalette2[0]);
+        Size    = ARRAYSIZE(CgaPalette2);
     }
 #endif
     else // if ((ModeNumber == 0x0D) || (ModeNumber == 0x0E))
     {
         /* EGA modes */
         Palette = EgaPalette__16Colors;
-        Size    = sizeof(EgaPalette__16Colors)/sizeof(EgaPalette__16Colors[0]);
+        Size    = ARRAYSIZE(EgaPalette__16Colors);
     }
 
     VgaSetPalette(Palette, Size);
 }
 
-static VOID VidBiosGetCursorPosition(PBYTE Row, PBYTE Column, BYTE Page)
+static __inline VOID VidBiosGetCursorPosition(PBYTE Row, PBYTE Column, BYTE Page)
 {
-    /* Make sure the selected video page is valid */
-    if (Page >= BIOS_MAX_PAGES) return;
-
-    /* Get the cursor location */
     *Row    = HIBYTE(Bda->CursorPosition[Page]);
     *Column = LOBYTE(Bda->CursorPosition[Page]);
 }
 
 static VOID VidBiosSetCursorPosition(BYTE Row, BYTE Column, BYTE Page)
 {
-    /* Make sure the selected video page is valid */
-    if (Page >= BIOS_MAX_PAGES) return;
-
     /* Update the position in the BDA */
     Bda->CursorPosition[Page] = MAKEWORD(Column, Row);
 
@@ -2298,7 +2343,7 @@ static VOID VidBiosSetCursorPosition(BYTE Row, BYTE Column, BYTE Page)
 static VOID VidBiosSetCursorShape(WORD CursorStartEnd)
 {
     /* Only valid in text-mode */
-    if ((Bda->VideoMode > 0x03) && (Bda->VideoMode != 0x07)) return;
+    if (!IS_TEXT_MODE(Bda->VideoMode)) return;
 
     /* Update the BDA */
     Bda->CursorStartLine = HIBYTE(CursorStartEnd) & 0x1F;
@@ -2342,14 +2387,14 @@ static VOID VidBiosSetCursorShape(WORD CursorStartEnd)
     IOWriteB(VGA_CRTC_DATA , LOBYTE(CursorStartEnd));
 }
 
-VOID VidBiosSyncCursorPosition(VOID)
+static VOID VidBiosSyncCursorPosition(VOID)
 {
     BYTE Row, Column;
     BYTE Low, High;
-    SHORT ScreenColumns = VgaGetDisplayResolution().X;
+    SHORT ScreenColumns = Bda->ScreenColumns;
     WORD Offset;
 
-    /* Get the cursor location */
+    /* Get the cursor position */
     IOWriteB(VGA_CRTC_INDEX, VGA_CRTC_CURSOR_LOC_LOW_REG);
     Low  = IOReadB(VGA_CRTC_DATA);
     IOWriteB(VGA_CRTC_INDEX, VGA_CRTC_CURSOR_LOC_HIGH_REG);
@@ -2370,12 +2415,48 @@ static inline BYTE VidBiosGetVideoMode(VOID)
     return Bda->VideoMode | (Bda->VGAOptions & 0x80);
 }
 
+static inline VOID VidBiosClearScreen(VOID)
+{
+    static const DWORD MemoryMaps[4]  = { 0xA0000, 0xA0000, 0xB0000, 0xB8000 };
+    static const DWORD MemorySizes[4] = { 0x20000, 0x10000, 0x08000, 0x08000 };
+
+    DWORD VideoAddress;
+    DWORD BufferSize;
+    BYTE Misc;
+    BYTE Buffer[0x20000];
+
+    /* Read the misc register */
+    IOWriteB(VGA_GC_INDEX, VGA_GC_MISC_REG);
+    Misc = IOReadB(VGA_GC_DATA);
+
+    /* Get the video address and buffer size */
+    VideoAddress = MemoryMaps[(Misc >> 2) & 3];
+    BufferSize  = MemorySizes[(Misc >> 2) & 3];
+
+    // !IS_TEXT_MODE(Bda->VideoMode)
+    if (Misc & 1)
+    {
+        /* Graphics mode */
+        RtlZeroMemory(Buffer, BufferSize);
+    }
+    else
+    {
+        /* Text mode */
+        UINT i;
+        for (i = 0; i < (BufferSize >> 1); i++)
+        {
+            ((PWORD)Buffer)[i] = MAKEWORD(' ', DEFAULT_ATTRIBUTE);
+        }
+    }
+
+    /* Write to video memory */
+    EmulatorWriteMemory(&EmulatorContext, VideoAddress, Buffer, BufferSize);
+}
+
 static BOOLEAN VidBiosSetVideoMode(BYTE ModeNumber)
 {
     BYTE Page;
     COORD Resolution;
-    PVGA_REGISTERS VgaMode;
-
     BYTE OrgModeNumber = ModeNumber;
 
     /*
@@ -2388,33 +2469,26 @@ static BOOLEAN VidBiosSetVideoMode(BYTE ModeNumber)
 
     /* Retrieve the real mode number and check its validity */
     ModeNumber &= 0x7F;
-    // if (ModeNumber >= sizeof(VideoModes)/sizeof(VideoModes[0]))
+    // if (ModeNumber >= ARRAYSIZE(VideoModes))
     if (ModeNumber > BIOS_MAX_VIDEO_MODE)
     {
         DPRINT1("VidBiosSetVideoMode -- Mode %02Xh invalid\n", ModeNumber);
         return FALSE;
     }
 
-    VgaMode = VideoModes[ModeNumber];
-
-    DPRINT1("Switching to mode %02Xh (%02Xh) %s clearing the screen; VgaMode = 0x%p\n",
-            ModeNumber, OrgModeNumber, (DoNotClear ? "without" : "and"), VgaMode);
+    DPRINT1("Switching to mode %02Xh (%02Xh) %s clearing the screen; VgaRegisters = 0x%p\n",
+            ModeNumber, OrgModeNumber, (DoNotClear ? "without" : "and"), VideoModes[ModeNumber].VgaRegisters);
 
-    if (!VgaSetRegisters(VgaMode)) return FALSE;
+    if (!VgaSetRegisters(VideoModes[ModeNumber].VgaRegisters)) return FALSE;
 
     VgaChangePalette(ModeNumber);
 
     /* Clear the VGA memory if needed */
     if (!DoNotClear) VgaClearMemory();
 
-    // Bda->CrtModeControl;
-    // Bda->CrtColorPaletteMask;
-    // Bda->EGAFlags;
-    // Bda->VGAFlags;
-
     /* Update the values in the BDA */
     Bda->VideoMode       = ModeNumber;
-    Bda->VideoPageSize   = VideoModePageSize[ModeNumber];
+    Bda->VideoPageSize   = VideoModes[ModeNumber].PageSize;
     Bda->VideoPage       = 0;
     Bda->VideoPageOffset = Bda->VideoPage * Bda->VideoPageSize;
 
@@ -2422,27 +2496,81 @@ static BOOLEAN VidBiosSetVideoMode(BYTE ModeNumber)
     Bda->VGAOptions      = 0x60 | (Bda->VGAOptions & 0x7F) | (DoNotClear ? 0x80 : 0x00);
     Bda->VGASwitches     = 0xF9;    /* High-resolution  */
 
+    // Bda->VGAFlags;
+    // Bda->CrtModeControl;
+    // Bda->CrtColorPaletteMask;
+
     /* Set the start address in the CRTC */
     IOWriteB(VGA_CRTC_INDEX, VGA_CRTC_START_ADDR_LOW_REG);
     IOWriteB(VGA_CRTC_DATA , LOBYTE(Bda->VideoPageOffset));
     IOWriteB(VGA_CRTC_INDEX, VGA_CRTC_START_ADDR_HIGH_REG);
     IOWriteB(VGA_CRTC_DATA , HIBYTE(Bda->VideoPageOffset));
 
-    /* Update the character height */
-    IOWriteB(VGA_CRTC_INDEX, VGA_CRTC_MAX_SCAN_LINE_REG);
-    Bda->CharacterHeight = 1 + (IOReadB(VGA_CRTC_DATA) & 0x1F);
-
     /* Update the screen size */
     Resolution = VgaGetDisplayResolution();
+    // This could be simplified if the VGA helper always returned the resolution
+    // in number of pixels, instead of in number of cells for text-modes only...
+    if (!IS_TEXT_MODE(ModeNumber))
+    {
+        Resolution.X /= VideoModes[ModeNumber].CharacterWidth ;
+        Resolution.Y /= VideoModes[ModeNumber].CharacterHeight;
+    }
     Bda->ScreenColumns = Resolution.X;
     Bda->ScreenRows    = Resolution.Y - 1;
 
+    /* Update the current font */
+    Bda->CharacterHeight = VideoModes[ModeNumber].CharacterHeight;
+    switch (Bda->CharacterHeight)
+    {
+        /*
+         * Write the default font to the VGA font plane for text-modes only.
+         * Update the BIOS INT 43h vector (far pointer to the character range 00h-...).
+         */
+        case 8:
+        {
+            if (IS_TEXT_MODE(ModeNumber))
+                VgaWriteTextModeFont(0, Font8x8, ARRAYSIZE(Font8x8) / VGA_FONT_CHARACTERS);
+
+            ((PULONG)BaseAddress)[0x43] = MAKELONG(FONT_8x8_OFFSET, VIDEO_BIOS_DATA_SEG);
+            break;
+        }
+        case 14:
+        {
+            if (IS_TEXT_MODE(ModeNumber))
+                VgaWriteTextModeFont(0, Font8x14, ARRAYSIZE(Font8x14) / VGA_FONT_CHARACTERS);
+
+            ((PULONG)BaseAddress)[0x43] = MAKELONG(FONT_8x14_OFFSET, VIDEO_BIOS_DATA_SEG);
+            break;
+        }
+        case 16:
+        {
+            if (IS_TEXT_MODE(ModeNumber))
+                VgaWriteTextModeFont(0, Font8x16, ARRAYSIZE(Font8x16) / VGA_FONT_CHARACTERS);
+
+            ((PULONG)BaseAddress)[0x43] = MAKELONG(FONT_8x16_OFFSET, VIDEO_BIOS_DATA_SEG);
+            break;
+        }
+    }
+
+#if 0 // Commented, because I need to think about how to change correctly the ScreenRows
+      // in the code that really use it (the Font generator functions of INT 10h, AH=11h)
+      // so that it also changes the screen resolution *in text mode only*.
+    switch (getBL())
+    {
+        case 0x00: Bda->ScreenRows = getDL()-1; break;
+        case 0x01: Bda->ScreenRows = 13;        break;
+        case 0x03: Bda->ScreenRows = 42;        break;
+        case 0x02:
+        default  : Bda->ScreenRows = 24;        break;
+    }
+#endif
+
     /*
      * Update the cursor shape (text-mode only).
      * Use the default CGA cursor scanline values,
      * see: http://vitaly_filatov.tripod.com/ng/asm/asm_023.2.html
      */
-    if ((ModeNumber >= 0x00 && ModeNumber <= 0x03) || (ModeNumber == 0x07))
+    if (IS_TEXT_MODE(ModeNumber))
         // FIXME: we might read the CRT registers and do the adjustment?
         VidBiosSetCursorShape(MAKEWORD(0x07, 0x06));
 
@@ -2450,32 +2578,7 @@ static BOOLEAN VidBiosSetVideoMode(BYTE ModeNumber)
     for (Page = 0; Page < BIOS_MAX_PAGES; ++Page)
         VidBiosSetCursorPosition(0, 0, Page);
 
-    // FIXME: We need to reset the fonts and the font vectors. (INT 1Fh and 43h).
-
-    // HACK: We clear here all the text memory. TODO: Do it better!
-    if (!DoNotClear && ((ModeNumber >= 0x00 && ModeNumber <= 0x03) || (ModeNumber == 0x07)))
-    {
-        INT i, j;
-        DWORD VideoAddress;
-        WORD FillCharacter = MAKEWORD(' ', DEFAULT_ATTRIBUTE);
-
-        for (Page = 0; Page < BIOS_MAX_PAGES; ++Page)
-        {
-            VideoAddress = TO_LINEAR(TEXT_VIDEO_SEG, Page * Bda->VideoPageSize);
-
-            for (i = 0; i <= Bda->ScreenRows; i++)
-            {
-                for (j = 0; j <= Bda->ScreenColumns - 1; j++)
-                {
-                    /* Write to video memory */
-                    EmulatorWriteMemory(&EmulatorContext,
-                                        VideoAddress + (i * Bda->ScreenColumns + j) * sizeof(WORD),
-                                        (LPVOID)&FillCharacter,
-                                        sizeof(FillCharacter));
-                }
-            }
-        }
-    }
+    if (!DoNotClear) VidBiosClearScreen();
 
     /* Refresh display */
     VgaRefreshDisplay();
@@ -2504,24 +2607,265 @@ static BOOLEAN VidBiosSetVideoPage(BYTE PageNumber)
     IOWriteB(VGA_CRTC_DATA , HIBYTE(Bda->VideoPageOffset));
 
     /*
-     * Get the cursor location (we don't update anything on the BIOS side
-     * but we update the cursor location on the VGA side).
+     * Get the cursor position (we don't update anything on the BIOS side
+     * but we update the cursor position on the VGA side).
      */
     VidBiosGetCursorPosition(&Row, &Column, PageNumber);
-    VidBiosSetCursorPosition(Row, Column, PageNumber);
+    VidBiosSetCursorPosition( Row,  Column, PageNumber);
 
     return TRUE;
 }
 
-static VOID VidBiosPrintCharacter(CHAR Character, BYTE Attribute, BYTE Page)
+static VOID VidBiosDrawGlyph(WORD CharData, BOOLEAN UseAttr, BYTE Page, BYTE Row, BYTE Column)
+{
+    switch (Bda->VideoMode)
+    {
+        /* Alphanumeric mode */
+        case 0x00:
+        case 0x01:
+        case 0x02:
+        case 0x03:
+        case 0x07:
+        {
+            EmulatorWriteMemory(&EmulatorContext,
+                                TO_LINEAR(TEXT_VIDEO_SEG,
+                                    Page * Bda->VideoPageSize +
+                                    (Row * Bda->ScreenColumns + Column) * sizeof(WORD)),
+                                (LPVOID)&CharData,
+                                UseAttr ? sizeof(WORD) : sizeof(BYTE));
+            break;
+        }
+
+        /* 4-color CGA */
+        case 0x04:
+        case 0x05:
+        {
+            WORD i;
+            WORD CgaSegment[] = { CGA_EVEN_VIDEO_SEG, CGA_ODD_VIDEO_SEG };
+            PUCHAR Font = (PUCHAR)FAR_POINTER(((PULONG)BaseAddress)[0x43]);
+            PUCHAR Glyph = &Font[LOBYTE(CharData) * Bda->CharacterHeight];
+            BOOLEAN Xor = (HIBYTE(CharData) & 0x80) ? TRUE : FALSE;
+            BYTE OldRotate;
+            BYTE DoubledBits[] =
+            {
+                0x00, 0x03, 0x0C, 0x0F, 0x30, 0x33, 0x3C, 0x3F,
+                0xC0, 0xC3, 0xCC, 0xCF, 0xF0, 0xF3, 0xFC, 0xFF
+            };
+
+            if (Xor)
+            {
+                /* Set the logical operation to XOR */
+                IOWriteB(VGA_GC_INDEX, VGA_GC_ROTATE_REG);
+                OldRotate = IOReadB(VGA_GC_DATA);
+                IOWriteB(VGA_GC_DATA, OldRotate | 0x18);
+            }
+
+            for (i = 0; i < Bda->CharacterHeight; i++)
+            {
+                WORD Pixel = MAKEWORD(DoubledBits[Glyph[i] >> 4],
+                                      DoubledBits[Glyph[i] & 0x0F]);
+                if (Xor)
+                {
+                    USHORT Dummy;
+
+                    /* Read from VGA memory to load the latch register */
+                    EmulatorReadMemory(&EmulatorContext,
+                                       TO_LINEAR(CgaSegment[(Row * Bda->CharacterHeight + i) & 1],
+                                                 (((Row * Bda->CharacterHeight + i) >> 1)
+                                                 * Bda->ScreenColumns + Column) * 2),
+                                       (LPVOID)&Dummy,
+                                       sizeof(USHORT));
+                }
+
+                EmulatorWriteMemory(&EmulatorContext,
+                                    TO_LINEAR(CgaSegment[(Row * Bda->CharacterHeight + i) & 1],
+                                              (((Row * Bda->CharacterHeight + i) >> 1)
+                                              * Bda->ScreenColumns + Column) * 2),
+                                    (LPVOID)&Pixel,
+                                    sizeof(USHORT));
+            }
+
+            if (Xor)
+            {
+                IOWriteB(VGA_GC_INDEX, VGA_GC_ROTATE_REG);
+                IOWriteB(VGA_GC_DATA, OldRotate);
+            }
+
+            break;
+        }
+
+        /* 2-color CGA */
+        case 0x06:
+        {
+            WORD i;
+            WORD CgaSegment[] = { CGA_EVEN_VIDEO_SEG, CGA_ODD_VIDEO_SEG };
+            PUCHAR Font = (PUCHAR)FAR_POINTER(((PULONG)BaseAddress)[0x43]);
+            PUCHAR Glyph = &Font[LOBYTE(CharData) * Bda->CharacterHeight];
+            BOOLEAN Xor = (HIBYTE(CharData) & 0x80) ? TRUE : FALSE;
+            BYTE OldRotate;
+
+            if (Xor)
+            {
+                /* Set the logical operation to XOR */
+                IOWriteB(VGA_GC_INDEX, VGA_GC_ROTATE_REG);
+                OldRotate = IOReadB(VGA_GC_DATA);
+                IOWriteB(VGA_GC_DATA, OldRotate | 0x18);
+            }
+
+            for (i = 0; i < Bda->CharacterHeight; i++)
+            {
+                if (Xor)
+                {
+                    UCHAR Dummy;
+
+                    /* Read from VGA memory to load the latch register */
+                    EmulatorReadMemory(&EmulatorContext,
+                                       TO_LINEAR(CgaSegment[(Row * Bda->CharacterHeight + i) & 1],
+                                                 (((Row * Bda->CharacterHeight + i) >> 1)
+                                                 * Bda->ScreenColumns) + Column),
+                                       (LPVOID)&Dummy,
+                                       sizeof(UCHAR));
+                }
+
+                EmulatorWriteMemory(&EmulatorContext,
+                                    TO_LINEAR(CgaSegment[(Row * Bda->CharacterHeight + i) & 1],
+                                              (((Row * Bda->CharacterHeight + i) >> 1)
+                                              * Bda->ScreenColumns) + Column),
+                                    (LPVOID)&Glyph[i],
+                                    sizeof(UCHAR));
+            }
+
+            if (Xor)
+            {
+                IOWriteB(VGA_GC_INDEX, VGA_GC_ROTATE_REG);
+                IOWriteB(VGA_GC_DATA, OldRotate);
+            }
+
+            break;
+        }
+
+        /* 16-color modes */
+        case 0x0D:
+        case 0x0E:
+        case 0x10:
+        case 0x11:
+        case 0x12:
+        {
+            WORD i;
+            PUCHAR Font = (PUCHAR)FAR_POINTER(((PULONG)BaseAddress)[0x43]);
+            PUCHAR Glyph = &Font[LOBYTE(CharData) * Bda->CharacterHeight];
+            BOOLEAN Xor = (HIBYTE(CharData) & 0x80) ? TRUE : FALSE;
+            BYTE OldPlaneWrite, OldReset, OldEnableReset, OldRotate, OldMode;
+
+            /* Write to all planes */
+            IOWriteB(VGA_SEQ_INDEX, VGA_SEQ_MASK_REG);
+            OldPlaneWrite = IOReadB(VGA_SEQ_DATA);
+            IOWriteB(VGA_SEQ_DATA, 0x0F);
+
+            /* Zero the planes whose bits are set in the enable set/reset register */
+            IOWriteB(VGA_GC_INDEX, VGA_GC_RESET_REG);
+            OldReset = IOReadB(VGA_GC_DATA);
+            IOWriteB(VGA_GC_DATA, 0x00);
+
+            /* Set the enable set/reset register to the inverse of the color */
+            IOWriteB(VGA_GC_INDEX, VGA_GC_ENABLE_RESET_REG);
+            OldEnableReset = IOReadB(VGA_GC_DATA);
+            IOWriteB(VGA_GC_DATA, (~HIBYTE(CharData)) & 0x0F);
+
+            /* Make sure we're in write mode 0 */
+            IOWriteB(VGA_GC_INDEX, VGA_GC_MODE_REG);
+            OldMode = IOReadB(VGA_GC_DATA);
+            IOWriteB(VGA_GC_DATA, 0x00);
+
+            if (Xor)
+            {
+                /* Set the logical operation to XOR */
+                IOWriteB(VGA_GC_INDEX, VGA_GC_ROTATE_REG);
+                OldRotate = IOReadB(VGA_GC_DATA);
+                IOWriteB(VGA_GC_DATA, OldRotate | 0x18);
+            }
+
+            for (i = 0; i < Bda->CharacterHeight; i++)
+            {
+                if (Xor)
+                {
+                    UCHAR Dummy;
+
+                    /* Read from VGA memory to load the latch register */
+                    EmulatorReadMemory(&EmulatorContext,
+                                       TO_LINEAR(GRAPHICS_VIDEO_SEG,
+                                                 ((Row * Bda->CharacterHeight + i)
+                                                 * Bda->ScreenColumns) + Column),
+                                       (LPVOID)&Dummy,
+                                       sizeof(UCHAR));
+                }
+
+                EmulatorWriteMemory(&EmulatorContext,
+                                    TO_LINEAR(GRAPHICS_VIDEO_SEG,
+                                              ((Row * Bda->CharacterHeight + i)
+                                              * Bda->ScreenColumns) + Column),
+                                    (LPVOID)&Glyph[i],
+                                    sizeof(UCHAR));
+            }
+
+            /* Restore the registers */
+            IOWriteB(VGA_SEQ_INDEX, VGA_SEQ_MASK_REG);
+            IOWriteB(VGA_SEQ_DATA, OldPlaneWrite);
+            IOWriteB(VGA_GC_INDEX, VGA_GC_RESET_REG);
+            IOWriteB(VGA_GC_DATA, OldReset);
+            IOWriteB(VGA_GC_INDEX, VGA_GC_ENABLE_RESET_REG);
+            IOWriteB(VGA_GC_DATA, OldEnableReset);
+            IOWriteB(VGA_GC_INDEX, VGA_GC_MODE_REG);
+            IOWriteB(VGA_GC_DATA, OldMode);
+
+            if (Xor)
+            {
+                IOWriteB(VGA_GC_INDEX, VGA_GC_ROTATE_REG);
+                IOWriteB(VGA_GC_DATA, OldRotate);
+            }
+
+            break;
+        }
+
+        /* 256-color mode */
+        case 0x13:
+        {
+            WORD i, j;
+            PUCHAR Font = (PUCHAR)FAR_POINTER(((PULONG)BaseAddress)[0x43]);
+            PUCHAR Glyph = &Font[LOBYTE(CharData) * Bda->CharacterHeight];
+            BYTE PixelBuffer[8]; // 8 == CharacterWidth
+
+            for (i = 0; i < Bda->CharacterHeight; i++)
+            {
+                for (j = 0; j < ARRAYSIZE(PixelBuffer); j++)
+                {
+                    PixelBuffer[j] = (Glyph[i] & (1 << (7 - j))) ? HIBYTE(CharData) : 0;
+                }
+
+                EmulatorWriteMemory(&EmulatorContext,
+                                    TO_LINEAR(GRAPHICS_VIDEO_SEG,
+                                              ((Row * Bda->CharacterHeight + i)
+                                              * Bda->ScreenColumns + Column) * 8),
+                                    (LPVOID)PixelBuffer,
+                                    sizeof(PixelBuffer));
+            }
+
+            break;
+        }
+
+        default:
+        {
+            DPRINT1("Drawing glyphs in mode %02Xh is not supported.\n", Bda->VideoMode);
+        }
+    }
+}
+
+static VOID VidBiosPrintCharacter(CHAR Character, BYTE Attribute, BOOLEAN UseAttr, BYTE Page)
 {
     WORD CharData = MAKEWORD(Character, Attribute);
     BYTE Row, Column;
 
-    /* Make sure the page exists */
-    if (Page >= BIOS_MAX_PAGES) return;
-
-    /* Get the cursor location */
+    /* Get the cursor position */
     VidBiosGetCursorPosition(&Row, &Column, Page);
 
     if (Character == '\a')
@@ -2546,12 +2890,7 @@ static VOID VidBiosPrintCharacter(CHAR Character, BYTE Attribute, BYTE Page)
 
         /* Erase the existing character */
         CharData = MAKEWORD(' ', Attribute);
-        EmulatorWriteMemory(&EmulatorContext,
-                            TO_LINEAR(TEXT_VIDEO_SEG,
-                                Page * Bda->VideoPageSize +
-                                (Row * Bda->ScreenColumns + Column) * sizeof(WORD)),
-                            (LPVOID)&CharData,
-                            sizeof(WORD));
+        VidBiosDrawGlyph(CharData, UseAttr, Page, Row, Column);
     }
     else if (Character == '\t')
     {
@@ -2559,7 +2898,7 @@ static VOID VidBiosPrintCharacter(CHAR Character, BYTE Attribute, BYTE Page)
         do
         {
             // Taken from DOSBox
-            VidBiosPrintCharacter(' ', Attribute, Page);
+            VidBiosPrintCharacter(' ', Attribute, UseAttr, Page);
             VidBiosGetCursorPosition(&Row, &Column, Page);
         } while (Column % 8);
     }
@@ -2577,15 +2916,8 @@ static VOID VidBiosPrintCharacter(CHAR Character, BYTE Attribute, BYTE Page)
     {
         /* Default character */
 
-        /* Write the character */
-        EmulatorWriteMemory(&EmulatorContext,
-                            TO_LINEAR(TEXT_VIDEO_SEG,
-                                Page * Bda->VideoPageSize +
-                                (Row * Bda->ScreenColumns + Column) * sizeof(WORD)),
-                            (LPVOID)&CharData,
-                            sizeof(WORD));
-
-        /* Advance the cursor */
+        /* Write the character and advance the cursor */
+        VidBiosDrawGlyph(CharData, UseAttr, Page, Row, Column);
         Column++;
     }
 
@@ -2633,19 +2965,29 @@ VOID WINAPI VidBiosVideoService(LPWORD Stack)
         /* Set Cursor Position */
         case 0x02:
         {
-            VidBiosSetCursorPosition(getDH(), getDL(), getBH());
+            BYTE Page = getBH();
+
+            /* Validate the selected video page */
+            if (Page >= BIOS_MAX_PAGES) break;
+
+            VidBiosSetCursorPosition(getDH(), getDL(), Page);
             break;
         }
 
         /* Get Cursor Position and Shape */
         case 0x03:
         {
-            /* Make sure the selected video page exists */
-            if (getBH() >= BIOS_MAX_PAGES) break;
+            BYTE Page = getBH();
+
+            /* Validate the selected video page */
+            if (Page == 0xFF) // Special case: use the current video page
+                Page = Bda->VideoPage;
+            else if (Page >= BIOS_MAX_PAGES)
+                break;
 
             /* Return the result */
             setCX(MAKEWORD(Bda->CursorEndLine, Bda->CursorStartLine));
-            setDX(Bda->CursorPosition[getBH()]);
+            setDX(Bda->CursorPosition[Page]);
             break;
         }
 
@@ -2682,12 +3024,15 @@ VOID WINAPI VidBiosVideoService(LPWORD Stack)
         /* Read Character and Attribute at Cursor Position */
         case 0x08:
         {
-            WORD  CharacterData;
+            WORD  CharData;
             BYTE  Page = getBH();
             DWORD Offset;
 
-            /* Check if the page exists */
-            if (Page >= BIOS_MAX_PAGES) break;
+            /* Validate the selected video page */
+            if (Page == 0xFF) // Special case: use the current video page
+                Page = Bda->VideoPage;
+            else if (Page >= BIOS_MAX_PAGES)
+                break;
 
             /* Find the offset of the character */
             Offset = Page * Bda->VideoPageSize +
@@ -2697,11 +3042,11 @@ VOID WINAPI VidBiosVideoService(LPWORD Stack)
             /* Read from the video memory */
             EmulatorReadMemory(&EmulatorContext,
                                TO_LINEAR(TEXT_VIDEO_SEG, Offset),
-                               (LPVOID)&CharacterData,
+                               (LPVOID)&CharData,
                                sizeof(WORD));
 
             /* Return the character data in AX */
-            setAX(CharacterData);
+            setAX(CharData);
 
             break;
         }
@@ -2711,27 +3056,41 @@ VOID WINAPI VidBiosVideoService(LPWORD Stack)
         /* Write Character only (PCjr: + Attribute) at Cursor Position */
         case 0x0A:
         {
-            WORD  CharacterData = MAKEWORD(getAL(), getBL());
-            BYTE  Page = getBH();
-            DWORD Offset, Counter = getCX();
-
-            /* Check if the page exists */
-            if (Page >= BIOS_MAX_PAGES) break;
+            WORD Counter = getCX();
+            WORD CharData = MAKEWORD(getAL(), getBL());
+            BOOLEAN UseAttr = (getAH() == 0x09);
+            BYTE Page = getBH();
+            BYTE Row, Column;
+
+            /* Validate the selected video page */
+            if (Page == 0xFF) // Special case: use the current video page
+                Page = Bda->VideoPage;
+            else if (Page >= BIOS_MAX_PAGES)
+                break;
 
-            /* Find the offset of the character */
-            Offset = Page * Bda->VideoPageSize +
-                     (HIBYTE(Bda->CursorPosition[Page])  * Bda->ScreenColumns +
-                      LOBYTE(Bda->CursorPosition[Page])) * 2;
+            /* Get the cursor position */
+            VidBiosGetCursorPosition(&Row, &Column, Page);
 
             /* Write to video memory a certain number of times */
-            while (Counter > 0)
+            while (Counter-- > 0)
             {
-                EmulatorWriteMemory(&EmulatorContext,
-                                    TO_LINEAR(TEXT_VIDEO_SEG, Offset),
-                                    (LPVOID)&CharacterData,
-                                    (getAH() == 0x09) ? sizeof(WORD) : sizeof(BYTE));
-                Offset += 2;
-                Counter--;
+                /* Write the character and advance the position */
+                VidBiosDrawGlyph(CharData, UseAttr, Page, Row, Column);
+                Column++;
+
+                /* Check if it passed the end of the row */
+                if (Column >= Bda->ScreenColumns)
+                {
+                    /* Return to the first column and go to the next line */
+                    Column = 0;
+                    Row++;
+                }
+
+                /* Contrary to the "Teletype Output" function, the screen is not scrolled */
+                if (Row > Bda->ScreenRows)
+                {
+                    Row = Bda->ScreenRows;
+                }
             }
 
             break;
@@ -2822,7 +3181,15 @@ VOID WINAPI VidBiosVideoService(LPWORD Stack)
         /* Teletype Output */
         case 0x0E:
         {
-            VidBiosPrintCharacter(getAL(), getBL(), getBH());
+            BYTE Page = getBH();
+
+            /* Validate the selected video page */
+            if (Page == 0xFF) // Special case: use the current video page
+                Page = Bda->VideoPage;
+            else if (Page >= BIOS_MAX_PAGES)
+                break;
+
+            VidBiosPrintCharacter(getAL(), getBL(), !IS_TEXT_MODE(Bda->VideoMode), Page);
             break;
         }
 
@@ -2864,7 +3231,7 @@ VOID WINAPI VidBiosVideoService(LPWORD Stack)
                 /* Set All Palette Registers */
                 case 0x02:
                 {
-                    INT i;
+                    UINT i;
                     LPBYTE Buffer = SEG_OFF_TO_PTR(getES(), getDX());
 
                     /* Set the palette registers */
@@ -2884,6 +3251,35 @@ VOID WINAPI VidBiosVideoService(LPWORD Stack)
                     break;
                 }
 
+                /* Toggle Intensity/Blinking Bit */
+                case 0x03:
+                {
+                    /* Read the old AC mode control register value */
+                    BYTE VgaAcControlReg;
+                    IOWriteB(VGA_AC_INDEX, VGA_AC_CONTROL_REG);
+                    VgaAcControlReg = IOReadB(VGA_AC_READ);
+
+                    /* Toggle the blinking bit and write the new value */
+                    if (getBL())
+                    {
+                        VgaAcControlReg |= VGA_AC_CONTROL_BLINK;
+                        Bda->CrtModeControl |= (1 << 5);
+                    }
+                    else
+                    {
+                        VgaAcControlReg &= ~VGA_AC_CONTROL_BLINK;
+                        Bda->CrtModeControl &= ~(1 << 5);
+                    }
+
+                    IOWriteB(VGA_AC_INDEX, VGA_AC_CONTROL_REG);
+                    IOWriteB(VGA_AC_WRITE, VgaAcControlReg);
+
+                    /* Enable screen and disable palette access */
+                    IOReadB(VGA_INSTAT1_READ); // Put the AC register into index state
+                    IOWriteB(VGA_AC_INDEX, 0x20);
+                    break;
+                }
+
                 /* Get Single Palette Register */
                 case 0x07:
                 {
@@ -2919,7 +3315,7 @@ VOID WINAPI VidBiosVideoService(LPWORD Stack)
                 /* Get All Palette Registers */
                 case 0x09:
                 {
-                    INT i;
+                    UINT i;
                     LPBYTE Buffer = SEG_OFF_TO_PTR(getES(), getDX());
 
                     /* Get the palette registers */
@@ -2961,7 +3357,7 @@ VOID WINAPI VidBiosVideoService(LPWORD Stack)
                 /* Set Block of DAC Registers */
                 case 0x12:
                 {
-                    INT i;
+                    UINT i;
                     LPBYTE Buffer = SEG_OFF_TO_PTR(getES(), getDX());
 
                     /* Write the index */
@@ -2996,7 +3392,7 @@ VOID WINAPI VidBiosVideoService(LPWORD Stack)
                 /* Get Block of DAC Registers */
                 case 0x17:
                 {
-                    INT i;
+                    UINT i;
                     LPBYTE Buffer = SEG_OFF_TO_PTR(getES(), getDX());
 
                     /* Write the index */
@@ -3014,6 +3410,20 @@ VOID WINAPI VidBiosVideoService(LPWORD Stack)
                     break;
                 }
 
+                /* Set PEL Mask */
+                case 0x18:
+                {
+                    IOWriteB(VGA_DAC_MASK, getBL());
+                    break;
+                }
+
+                /* Get PEL Mask */
+                case 0x19:
+                {
+                    setBL(IOReadB(VGA_DAC_MASK));
+                    break;
+                }
+
                 default:
                 {
                     DPRINT1("BIOS Palette Control Sub-command AL = 0x%02X NOT IMPLEMENTED\n",
@@ -3030,6 +3440,60 @@ VOID WINAPI VidBiosVideoService(LPWORD Stack)
         {
             switch (getAL())
             {
+                // FIXME: At the moment we support only graphics-mode functions!
+
+                /* Load User-specified Patterns (Character Set) for Text Mode */
+                case 0x00:
+                case 0x10: // FIXME: 0x1x performs a full mode reset
+                {
+                    // FIXME: BL == ??
+
+                    /* Write the default font to the VGA font plane */
+                    // VgaWriteTextModeFont(0, Font8x8, ARRAYSIZE(Font8x8) / VGA_FONT_CHARACTERS);
+
+                    UNIMPLEMENTED;
+                    break;
+                }
+
+                /* Load ROM Monochrome 8x14 Patterns (Character Set) for Text Mode */
+                case 0x01:
+                case 0x11: // FIXME: 0x1x performs a full mode reset
+                {
+                    // FIXME: BL == ??
+
+                    /* Write the default font to the VGA font plane */
+                    VgaWriteTextModeFont(0, Font8x14, ARRAYSIZE(Font8x14) / VGA_FONT_CHARACTERS);
+
+                    UNIMPLEMENTED;
+                    break;
+                }
+
+                /* Load ROM 8x8 Double-dot Patterns (Character Set) for Text Mode */
+                case 0x02:
+                case 0x12: // FIXME: 0x1x performs a full mode reset
+                {
+                    // FIXME: BL == ??
+
+                    /* Write the default font to the VGA font plane */
+                    VgaWriteTextModeFont(0, Font8x8, ARRAYSIZE(Font8x8) / VGA_FONT_CHARACTERS);
+
+                    UNIMPLEMENTED;
+                    break;
+                }
+
+                /* Load ROM 8x16 Character Set for Text Mode */
+                case 0x04:
+                case 0x14: // FIXME: 0x1x performs a full mode reset
+                {
+                    // FIXME: BL == ??
+
+                    /* Write the default font to the VGA font plane */
+                    VgaWriteTextModeFont(0, Font8x16, ARRAYSIZE(Font8x16) / VGA_FONT_CHARACTERS);
+
+                    UNIMPLEMENTED;
+                    break;
+                }
+
                 /* Set User 8x8 Graphics Chars (Setup INT 1Fh Vector) */
                 case 0x20:
                 {
@@ -3039,33 +3503,95 @@ VOID WINAPI VidBiosVideoService(LPWORD Stack)
                     break;
                 }
 
-                /* Setup ROM 8x8 Font for Graphics Mode */
-                case 0x23:
+                /* Set User Graphics Characters */
+                case 0x21:
                 {
-                    // FIXME: Use BL and DL for the number of screen rows
+                    /*
+                     * Update the BIOS INT 43h vector (far pointer
+                     * to the character range 00h-...)
+                     */
+                    ((PULONG)BaseAddress)[0x43] = MAKELONG(getBP(), getES());
 
-                    /* Write the default font to the VGA font plane */
-                    VgaWriteFont(0, Font8x8, sizeof(Font8x8)/sizeof(Font8x8[0]) / VGA_FONT_CHARACTERS);
+                    /* Update BDA */
+                    Bda->CharacterHeight = getCX();
+                    switch (getBL())
+                    {
+                        case 0x00: Bda->ScreenRows = getDL()-1; break;
+                        case 0x01: Bda->ScreenRows = 13;        break;
+                        case 0x03: Bda->ScreenRows = 42;        break;
+                        case 0x02:
+                        default  : Bda->ScreenRows = 24;        break;
+                    }
+
+                    break;
+                }
 
-                    /* Update the BIOS INT 43h vector */
-                    // Far pointer to the 8x8 characters 00h-...
+                /* Setup ROM 8x14 Font for Graphics Mode */
+                case 0x22:
+                {
+                    /*
+                     * Update the BIOS INT 43h vector (far pointer
+                     * to the character range 00h-...)
+                     */
+                    ((PULONG)BaseAddress)[0x43] = MAKELONG(FONT_8x14_OFFSET, VIDEO_BIOS_DATA_SEG);
+
+                    /* Update BDA */
+                    Bda->CharacterHeight = 14;
+                    switch (getBL())
+                    {
+                        case 0x00: Bda->ScreenRows = getDL()-1; break;
+                        case 0x01: Bda->ScreenRows = 13;        break;
+                        case 0x03: Bda->ScreenRows = 42;        break;
+                        case 0x02:
+                        default  : Bda->ScreenRows = 24;        break;
+                    }
+
+                    break;
+                }
+
+                /* Setup ROM 8x8 Font for Graphics Mode */
+                case 0x23:
+                {
+                    /*
+                     * Update the BIOS INT 43h vector (far pointer
+                     * to the character range 00h-...)
+                     */
                     ((PULONG)BaseAddress)[0x43] = MAKELONG(FONT_8x8_OFFSET, VIDEO_BIOS_DATA_SEG);
 
+                    /* Update BDA */
+                    Bda->CharacterHeight = 8;
+                    switch (getBL())
+                    {
+                        case 0x00: Bda->ScreenRows = getDL()-1; break;
+                        case 0x01: Bda->ScreenRows = 13;        break;
+                        case 0x03: Bda->ScreenRows = 42;        break;
+                        case 0x02:
+                        default  : Bda->ScreenRows = 24;        break;
+                    }
+
                     break;
                 }
 
                 /* Setup ROM 8x16 Font for Graphics Mode */
                 case 0x24:
                 {
-                    // FIXME: Use BL and DL for the number of screen rows
-
-                    /* Write the default font to the VGA font plane */
-                    VgaWriteFont(0, Font8x16, sizeof(Font8x16)/sizeof(Font8x16[0]) / VGA_FONT_CHARACTERS);
-
-                    /* Update the BIOS INT 43h vector */
-                    // Far pointer to the 8x16 characters 00h-...
+                    /*
+                     * Update the BIOS INT 43h vector (far pointer
+                     * to the character range 00h-...).
+                     */
                     ((PULONG)BaseAddress)[0x43] = MAKELONG(FONT_8x16_OFFSET, VIDEO_BIOS_DATA_SEG);
 
+                    /* Update BDA */
+                    Bda->CharacterHeight = 16;
+                    switch (getBL())
+                    {
+                        case 0x00: Bda->ScreenRows = getDL()-1; break;
+                        case 0x01: Bda->ScreenRows = 13;        break;
+                        case 0x03: Bda->ScreenRows = 42;        break;
+                        case 0x02:
+                        default  : Bda->ScreenRows = 24;        break;
+                    }
+
                     break;
                 }
 
@@ -3212,7 +3738,44 @@ VOID WINAPI VidBiosVideoService(LPWORD Stack)
         /* Write String */
         case 0x13:
         {
-            DPRINT1("BIOS Function INT 13h (Write String) is UNIMPLEMENTED\n");
+            PCHAR String = (PCHAR)SEG_OFF_TO_PTR(getES(), getBP());
+            WORD Counter = getCX();
+            BYTE Row, Column;
+            BYTE OldRow, OldColumn;
+            CHAR Character;
+            BYTE Attribute = getBL(); // Default attribute in case the string contains only characters.
+            BYTE Page = getBH();
+            BYTE Flags = getAL();
+
+            /* Validate the selected video page */
+            if (Page == 0xFF) // Special case: use the current video page
+                Page = Bda->VideoPage;
+            else if (Page >= BIOS_MAX_PAGES)
+                break;
+
+            /* Get the original cursor position */
+            VidBiosGetCursorPosition(&OldRow, &OldColumn, Page);
+
+            /* Set the new cursor position */
+            Row    = getDH();
+            Column = getDL();
+            if (Row == 0xFF) // Special case: use the current cursor position
+            {
+                Row    = OldRow;
+                Column = OldColumn;
+            }
+            VidBiosSetCursorPosition(Row, Column, Page);
+
+            while (Counter-- > 0)
+            {
+                Character = *String++;
+                if (Flags & 0x02) Attribute = *String++;
+                VidBiosPrintCharacter(Character, Attribute, TRUE, Page);
+            }
+
+            /* Reset the cursor position to its original value if we don't want to update it */
+            if (!(Flags & 0x01)) VidBiosSetCursorPosition(OldRow, OldColumn, Page);
+
             break;
         }
 
@@ -3222,18 +3785,80 @@ VOID WINAPI VidBiosVideoService(LPWORD Stack)
             switch (getAL())
             {
                 case 0x00: /* Get Display combination code */
-                   setAX(MAKEWORD(0x1A, 0x1A));
-                   setBX(MAKEWORD(0x08, 0x00)); /* VGA w/ color analog display */
-                   break;
+                {
+                    setBL(Bda->VGADccIDActive);
+                    setBH(0x00); // No alternate display
+
+                    /* Return success */
+                    setAL(0x1A);
+                    break;
+                }
                 case 0x01: /* Set Display combination code */
-                   DPRINT1("Set Display combination code - Unsupported\n");
-                   break;
+                {
+                    DPRINT1("Set Display combination code - Unsupported\n");
+                    break;
+                }
                 default:
-                   break;
+                    break;
             }
             break;
         }
 
+        /* Functionality/State Information (VGA) */
+        case 0x1B:
+        {
+            PVGA_DYNAMIC_FUNC_TABLE Table = SEG_OFF_TO_PTR(getES(), getDI());
+
+            /* Check for only supported subfunction */
+            if (getBX() != 0x0000)
+            {
+                DPRINT1("INT 10h, AH=1Bh, unsupported subfunction 0x%04x\n", getBX());
+                break;
+            }
+
+            /* Fill the VGA dynamic functionality table with our information */
+
+            Table->StaticFuncTablePtr = MAKELONG(VIDEO_STATE_INFO_OFFSET, VIDEO_BIOS_DATA_SEG);
+
+            Table->VideoMode       = Bda->VideoMode;
+            Table->ScreenColumns   = Bda->ScreenColumns;
+            Table->VideoPageSize   = Bda->VideoPageSize;
+            Table->VideoPageOffset = Bda->VideoPageOffset;
+            RtlCopyMemory(Table->CursorPosition, Bda->CursorPosition, sizeof(Bda->CursorPosition));
+            Table->CursorEndLine   = Bda->CursorEndLine;
+            Table->CursorStartLine = Bda->CursorStartLine;
+            Table->VideoPage       = Bda->VideoPage;
+            Table->CrtBasePort     = Bda->CrtBasePort;
+            Table->CrtModeControl  = Bda->CrtModeControl;
+            Table->CrtColorPaletteMask = Bda->CrtColorPaletteMask;
+            Table->ScreenRows      = Bda->ScreenRows;
+            Table->CharacterHeight = Bda->CharacterHeight;
+
+            Table->VGADccIDActive    = Bda->VGADccIDActive;
+            Table->VGADccIDAlternate = 0x00; // No alternate display
+            // Table->CurrModeSupportedColorsNum;
+            // Table->CurrModeSupportedPagesNum;
+            // Table->Scanlines;
+            // Table->PrimaryCharTable;
+            // Table->SecondaryCharTable;
+            // Table->VGAFlags;
+            Table->VGAAvailMemory = (Bda->VGAOptions & 0x60) >> 5;
+            // Table->VGASavePtrStateFlags;
+            // Table->VGADispInfo;
+            UNIMPLEMENTED;
+
+            /* Return success */
+            setAL(0x1B);
+            break;
+        }
+
+        /* VESA BIOS Extensions */
+        case 0x4F:
+        {
+            if (VbeInitialized) VbeService(Stack);
+            break;
+        }
+
         default:
         {
             DPRINT1("BIOS Function INT 10h, AH = 0x%02X, AL = 0x%02X, BH = 0x%02X NOT IMPLEMENTED\n",
@@ -3251,49 +3876,46 @@ static BOOL Attached = TRUE;
 
 VOID VidBiosAttachToConsole(VOID)
 {
-    // VgaRefreshDisplay();
     if (!Attached)
     {
         VgaAttachToConsole();
         Attached = TRUE;
     }
 
+    /* Refresh display */
     VgaRefreshDisplay();
     VidBiosSyncCursorPosition();
 }
 
 VOID VidBiosDetachFromConsole(VOID)
 {
-    /* Perform another screen refresh */
+    if (!Attached) return;
+
+    /* Refresh display */
     VgaRefreshDisplay();
 
     /* Detach from the console */
-    VgaDetachFromConsole(FALSE);
+    VgaDetachFromConsole();
     Attached = FALSE;
 }
 
-BOOLEAN VidBiosInitialize(VOID)
+VOID VidBiosPost(VOID)
 {
-    /* Some interrupts are in fact addresses to tables */
-    ((PULONG)BaseAddress)[0x1D] = (ULONG)NULL;
-    // Far pointer to the 8x8 characters 80h-FFh
+    /*
+     * Initialize VGA BIOS32 RAM dynamic data
+     */
+
+    /* Some vectors are in fact addresses to tables */
+    ((PULONG)BaseAddress)[0x1D] = (ULONG)NULL; // Video Parameter Tables
+    // Far pointer to the 8x8 graphics font for the 8x8 characters 80h-FFh
     ((PULONG)BaseAddress)[0x1F] = MAKELONG(FONT_8x8_HIGH_OFFSET, VIDEO_BIOS_DATA_SEG);
-    // ((PULONG)BaseAddress)[0x42] = (ULONG)NULL;
-    // Far pointer to the 8x16 characters 00h-...
+    // Far pointer to the character table (EGA, MCGA, VGA) for the 8x16 characters 00h-...
     ((PULONG)BaseAddress)[0x43] = MAKELONG(FONT_8x16_OFFSET, VIDEO_BIOS_DATA_SEG);
-    ((PULONG)BaseAddress)[0x44] = (ULONG)NULL;
-    // ((PULONG)BaseAddress)[0x6D] = (ULONG)NULL;
+    ((PULONG)BaseAddress)[0x44] = (ULONG)NULL; // ROM BIOS Character Font, Characters 00h-7Fh (PCjr)
 
-    /* Fill the tables */
-    RtlMoveMemory(SEG_OFF_TO_PTR(VIDEO_BIOS_DATA_SEG, FONT_8x8_OFFSET),
-                  Font8x8, sizeof(Font8x8));
-    RtlMoveMemory(SEG_OFF_TO_PTR(VIDEO_BIOS_DATA_SEG, FONT_8x16_OFFSET),
-                  Font8x16, sizeof(Font8x16));
-    RtlMoveMemory(SEG_OFF_TO_PTR(VIDEO_BIOS_DATA_SEG, FONT_8x14_OFFSET),
-                  Font8x14, sizeof(Font8x14));
-
-    /* Write the default font to the VGA font plane */
-    VgaWriteFont(0, Font8x16, sizeof(Font8x16)/sizeof(Font8x16[0]) / VGA_FONT_CHARACTERS);
+    /* Relocated services by the BIOS (when needed) */
+    ((PULONG)BaseAddress)[0x42] = (ULONG)NULL; // Relocated Default INT 10h Video Services
+    ((PULONG)BaseAddress)[0x6D] = (ULONG)NULL; // Video BIOS Entry Point
 
     //
     // FIXME: At the moment we always set a VGA mode. In the future,
@@ -3304,14 +3926,76 @@ BOOLEAN VidBiosInitialize(VOID)
     //   (that should be done here, or maybe in VGA ??)
     //
 
+    Bda->CrtModeControl      = 0x00;
+    Bda->CrtColorPaletteMask = 0x00;
+    Bda->VGADccIDActive      = 0x08; // VGA w/ color analog active display
+
     /* Set the default video mode */
     VidBiosSetVideoMode(BIOS_DEFAULT_VIDEO_MODE);
 
     /* Synchronize our cursor position with VGA */
     VidBiosSyncCursorPosition();
 
-    /* Register the BIOS support BOPs */
-    RegisterBop(BOP_VIDEO_INT, VidBiosVideoService);
+    /* Register the BIOS 32-bit Interrupts */
+    RegisterBiosInt32(BIOS_VIDEO_INTERRUPT, VidBiosVideoService);
+
+    /* Vectors that should be implemented */
+    RegisterBiosInt32(0x42, NULL); // Relocated Default INT 10h Video Services
+    RegisterBiosInt32(0x6D, NULL); // Video BIOS Entry Point
+
+    /* Initialize VBE */
+    VbeInitialized = VbeInitialize();
+    if (!VbeInitialized) DPRINT1("Couldn't initialize VBE!\n");
+}
+
+BOOLEAN VidBiosInitialize(VOID)
+{
+    UCHAR Checksum;
+
+    /*
+     * Initialize VGA BIOS32 static data
+     */
+
+    /* This is a ROM of size 'VIDEO_BIOS_ROM_SIZE' */
+    *(PWORD)(SEG_OFF_TO_PTR(VIDEO_BIOS_DATA_SEG, 0x0000)) = 0xAA55;
+    *(PBYTE)(SEG_OFF_TO_PTR(VIDEO_BIOS_DATA_SEG, 0x0002)) = VIDEO_BIOS_ROM_SIZE / 512; // Size in blocks of 512 bytes
+
+    /* Bootstrap code */
+    *(PWORD)(SEG_OFF_TO_PTR(VIDEO_BIOS_DATA_SEG, 0x0003)) = 0x90CB; // retf, nop
+    // RtlCopyMemory(SEG_OFF_TO_PTR(VIDEO_BIOS_DATA_SEG, 0xFFF0), Bootstrap, sizeof(Bootstrap));
+
+    /* Video BIOS Information */
+    RtlCopyMemory(SEG_OFF_TO_PTR(VIDEO_BIOS_DATA_SEG, 0x0005), BiosInfo, sizeof(BiosInfo)-1);
+
+    /* Initialize the VGA static function table */
+    VgaStaticFuncTable = SEG_OFF_TO_PTR(VIDEO_BIOS_DATA_SEG, VIDEO_STATE_INFO_OFFSET);
+    RtlZeroMemory(VgaStaticFuncTable, sizeof(*VgaStaticFuncTable));
+    VgaStaticFuncTable->SupportedModes[0] = 0xFF; // Modes 0x00 to 0x07 supported
+    VgaStaticFuncTable->SupportedModes[1] = 0xFF; // Modes 0x08 to 0x0F supported
+    VgaStaticFuncTable->SupportedModes[2] = 0x0F; // Modes 0x10 to 0x13 supported
+    VgaStaticFuncTable->SupportedScanlines   = 0x07; // Scanlines 200, 350 and 400 supported
+    VgaStaticFuncTable->TextCharBlocksNumber = 0;
+    VgaStaticFuncTable->MaxActiveTextCharBlocksNumber = 0;
+    VgaStaticFuncTable->VGAFuncSupportFlags = 0x0CFD; // See: http://www.ctyme.com/intr/rb-0221.htm#Table46
+    VgaStaticFuncTable->VGASavePtrFuncFlags = 0x18;   // See: http://www.ctyme.com/intr/rb-0221.htm#Table47
+
+    /* Fill the font tables */
+    RtlMoveMemory(SEG_OFF_TO_PTR(VIDEO_BIOS_DATA_SEG, FONT_8x8_OFFSET),
+                  Font8x8, sizeof(Font8x8));
+    RtlMoveMemory(SEG_OFF_TO_PTR(VIDEO_BIOS_DATA_SEG, FONT_8x16_OFFSET),
+                  Font8x16, sizeof(Font8x16));
+    RtlMoveMemory(SEG_OFF_TO_PTR(VIDEO_BIOS_DATA_SEG, FONT_8x14_OFFSET),
+                  Font8x14, sizeof(Font8x14));
+
+    VidBios32Initialize();
+
+    /* Compute the ROM checksum and store it */
+    *(PBYTE)(SEG_OFF_TO_PTR(VIDEO_BIOS_DATA_SEG, VIDEO_BIOS_ROM_SIZE - 1)) = 0x00;
+    Checksum = CalcRomChecksum(TO_LINEAR(VIDEO_BIOS_DATA_SEG, 0x0000), VIDEO_BIOS_ROM_SIZE);
+    *(PBYTE)(SEG_OFF_TO_PTR(VIDEO_BIOS_DATA_SEG, VIDEO_BIOS_ROM_SIZE - 1)) = (0xFF - Checksum + 1) & 0xFF;
+
+    WriteProtectRom((PVOID)TO_LINEAR(VIDEO_BIOS_DATA_SEG, 0x0000),
+                    VIDEO_BIOS_ROM_SIZE);
 
     return TRUE;
 }