[NTVDM]
authorAleksandar Andrejevic <aandrejevic@reactos.org>
Mon, 5 Aug 2013 23:20:25 +0000 (23:20 +0000)
committerAleksandar Andrejevic <aandrejevic@reactos.org>
Mon, 5 Aug 2013 23:20:25 +0000 (23:20 +0000)
Implement Video Graphics Array (VGA) support.
Replace the old BIOS video code with new code that uses the VGA.

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

subsystems/ntvdm/CMakeLists.txt
subsystems/ntvdm/bios.c
subsystems/ntvdm/bios.h
subsystems/ntvdm/dos.h
subsystems/ntvdm/emulator.c
subsystems/ntvdm/ntvdm.c
subsystems/ntvdm/vga.c [new file with mode: 0644]
subsystems/ntvdm/vga.h [new file with mode: 0644]

index 7381ec4..7d0691e 100644 (file)
@@ -8,6 +8,7 @@ list(APPEND SOURCE
     pic.c
     timer.c
     ps2.c
+    vga.c
     ntvdm.c
     ntvdm.rc)
 
index c5a27b5..a4d1ce1 100644 (file)
@@ -12,6 +12,7 @@
 
 #include "bios.h"
 #include "emulator.h"
+#include "vga.h"
 #include "pic.h"
 #include "ps2.h"
 #include "timer.h"
@@ -22,81 +23,138 @@ static PBIOS_DATA_AREA Bda;
 static BYTE BiosKeyboardMap[256];
 static HANDLE BiosConsoleInput = INVALID_HANDLE_VALUE;
 static HANDLE BiosConsoleOutput = INVALID_HANDLE_VALUE;
-static HANDLE BiosGraphicsOutput = NULL;
-static LPVOID ConsoleFramebuffer = NULL;
-static HANDLE ConsoleMutex = NULL;
-static BYTE CurrentVideoMode, CurrentVideoPage;
-static BOOLEAN VideoNeedsUpdate = TRUE;
-static SMALL_RECT UpdateRectangle = { 0, 0, 0, 0 };
 static CONSOLE_SCREEN_BUFFER_INFO BiosSavedBufferInfo;
 
-static VIDEO_MODE VideoModes[] =
+static BYTE VideoMode_40x25_text[] =
 {
-    /* Width | Height | Text | Bpp   | Gray | Pages | Segment */
-    { 40,       25,     TRUE,   16,     TRUE,   8,      0xB800}, /* Mode 00h */
-    { 40,       25,     TRUE,   16,     FALSE,  8,      0xB800}, /* Mode 01h */
-    { 80,       25,     TRUE,   16,     TRUE,   8,      0xB800}, /* Mode 02h */
-    { 80,       25,     TRUE,   16,     FALSE,  8,      0xB800}, /* Mode 03h */
-    { 320,      200,    FALSE,  2,      FALSE,  1,      0xB800}, /* Mode 04h */
-    { 320,      200,    FALSE,  2,      TRUE,   1,      0xB800}, /* Mode 05h */
-    { 640,      200,    FALSE,  1,      FALSE,  1,      0xB800}, /* Mode 06h */
-    { 80,       25,     TRUE,   8,      FALSE,  1,      0xB000}, /* Mode 07h */
-    { 0,        0,      FALSE,  0,      FALSE,  0,      0x0000}, /* Mode 08h - not used */
-    { 0,        0,      FALSE,  0,      FALSE,  0,      0x0000}, /* Mode 09h - not used */
-    { 0,        0,      FALSE,  0,      FALSE,  0,      0x0000}, /* Mode 0Ah - not used */
-    { 0,        0,      FALSE,  0,      FALSE,  0,      0x0000}, /* Mode 0Bh - not used */
-    { 0,        0,      FALSE,  0,      FALSE,  0,      0x0000}, /* Mode 0Ch - not used */
-    { 320,      200,    FALSE,  4,      FALSE,  1,      0xA000}, /* Mode 0Dh */
-    { 640,      200,    FALSE,  4,      FALSE,  1,      0xA000}, /* Mode 0Eh */
-    { 640,      350,    FALSE,  1,      FALSE,  1,      0xA000}, /* Mode 0Fh */
-    { 640,      350,    FALSE,  4,      FALSE,  1,      0xA000}, /* Mode 10h */
-    { 640,      480,    FALSE,  1,      FALSE,  1,      0xA000}, /* Mode 11h */
-    { 640,      480,    FALSE,  4,      FALSE,  1,      0xA000}, /* Mode 12h */
-    { 320,      200,    FALSE,  8,      FALSE,  1,      0xA000}  /* Mode 13h */
+    /* Miscellaneous Register */
+    0x67,
+
+    /* Sequencer Registers */
+    0x03, 0x08, 0x03, 0x00, 0x02,
+
+    /* GC Registers */
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x0E, 0x0F, 0xFF,
+
+    /* CRTC Registers */
+    0x2D, 0x27, 0x28, 0x90, 0x2B, 0xA0, 0xBF, 0x1F, 0x00, 0x4F, 0x0E, 0x0F,
+    0x00, 0x00, 0x00, 0x00, 0x9C, 0x8E, 0x8F, 0x14, 0x1F, 0x96, 0xB9, 0xA3,
+    0xFF,
+
+    /* AC Registers */
+    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x14, 0x07, 0x38, 0x39, 0x3A, 0x3B,
+    0x3C, 0x3D, 0x3E, 0x3F, 0x0C, 0x01, 0x0F, 0x13, 0x00
 };
 
-/* PRIVATE FUNCTIONS **********************************************************/
+static BYTE VideoMode_80x25_text[] =
+{
+    /* Miscellaneous Register */
+    0x67,
+
+    /* Sequencer Registers */
+    0x03, 0x00, 0x03, 0x00, 0x02,
 
-static DWORD BiosGetVideoPageSize(VOID)
+    /* GC Registers */
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x0E, 0x0F, 0xFF,
+
+    /* CRTC Registers */
+    0x5F, 0x4F, 0x50, 0x82, 0x55, 0x81, 0xBF, 0x1F, 0x00, 0x4F, 0x0E, 0x0F,
+    0x00, 0x00, 0x00, 0x00, 0x9C, 0x8E, 0x8F, 0x28, 0x1F, 0x96, 0xB9, 0xA3,
+    0xFF,
+
+    /* AC Registers */
+    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x14, 0x07, 0x38, 0x39, 0x3A, 0x3B,
+    0x3C, 0x3D, 0x3E, 0x3F, 0x0C, 0x01, 0x0F, 0x13, 0x00
+};
+
+static BYTE VideoMode_320x200_4color[] =
 {
-    INT i;
-    DWORD BufferSize = VideoModes[CurrentVideoMode].Width
-                       * VideoModes[CurrentVideoMode].Height
-                       * VideoModes[CurrentVideoMode].Bpp
-                       / 8;
-    
-    for (i = 0; i < 32; i++) if ((1 << i) >= BufferSize) break;
+    /* Miscellaneous Register */
+    0x63,
 
-    return 1 << i;
-}
+    /* Sequencer Registers */
+    0x03, 0x09, 0x03, 0x00, 0x02,
+
+    /* GC Registers */
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x0F, 0x0F, 0xFF,
 
-static BYTE BiosVideoAddressToPage(ULONG Address)
+    /* CRTC Registers */
+    0x2D, 0x27, 0x28, 0x90, 0x2B, 0x80, 0xBF, 0x1F, 0x00, 0xC1, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x9C, 0x8E, 0x8F, 0x14, 0x00, 0x96, 0xB9, 0xA2,
+    0xFF,
+
+    /* AC Registers */
+    0x00, 0x13, 0x15, 0x17, 0x02, 0x04, 0x06, 0x07, 0x10, 0x11, 0x12, 0x13,
+    0x14, 0x15, 0x16, 0x17, 0x01, 0x00, 0x03, 0x00, 0x00
+};
+
+static BYTE VideoMode_640x480_16color[] =
 {
-    return (Address - BiosGetVideoMemoryStart())
-            / BiosGetVideoPageSize();
-}
+    /* Miscellaneous Register */
+    0xE3,
+
+    /* Sequencer Registers */
+    0x03, 0x01, 0x08, 0x00, 0x06,
 
-static COORD BiosVideoAddressToCoord(ULONG Address)
+    /* GC Registers */
+    0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x05, 0x0F, 0xFF,
+
+    /* CRTC Registers */
+    0x5F, 0x4F, 0x50, 0x82, 0x54, 0x80, 0x0B, 0x3E, 0x00, 0x40, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0xEA, 0x0C, 0xDF, 0x28, 0x00, 0xE7, 0x04, 0xE3,
+    0xFF,
+
+    /* AC Registers */
+    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x14, 0x07, 0x38, 0x39, 0x3A, 0x3B,
+    0x3C, 0x3D, 0x3E, 0x3F, 0x01, 0x00, 0x0F, 0x00, 0x00
+};
+
+static BYTE VideoMode_320x200_256color[] =
 {
-    COORD Result = {0, 0};
-    DWORD PageStart = BiosVideoAddressToPage(Address) * BiosGetVideoPageSize();
-    DWORD Offset = Address - BiosGetVideoMemoryStart() - PageStart;
+    /* Miscellaneous Register */
+    0x63,
 
-    if (VideoModes[CurrentVideoMode].Text)
-    {
-        Result.X = (Offset / sizeof(WORD)) % VideoModes[CurrentVideoMode].Width;
-        Result.Y = (Offset / sizeof(WORD)) / VideoModes[CurrentVideoMode].Width;
-    }
-    else
-    {
-        Result.X = ((Offset * 8) / VideoModes[CurrentVideoMode].Bpp)
-                   % VideoModes[CurrentVideoMode].Width;
-        Result.Y = ((Offset * 8) / VideoModes[CurrentVideoMode].Bpp)
-                   / VideoModes[CurrentVideoMode].Width;
-    }
+    /* Sequencer Registers */
+    0x03, 0x01, 0x0F, 0x00, 0x0E,
 
-    return Result;
-}
+    /* GC Registers */
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x05, 0x0F, 0xFF,
+
+    /* CRTC Registers */
+    0x5F, 0x4F, 0x50, 0x82, 0x54, 0x80, 0xBF, 0x1F, 0x00, 0x41, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x9C, 0x0E, 0x8F, 0x28, 0x40, 0x96, 0xB9, 0xA3,
+    0xFF,
+
+    /* AC Registers */
+    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B,
+    0x0C, 0x0D, 0x0E, 0x0F, 0x41, 0x00, 0x0F, 0x00, 0x00
+};
+
+static LPBYTE VideoModes[] =
+{
+    VideoMode_40x25_text,       /* Mode 00h */
+    VideoMode_40x25_text,       /* Mode 01h */
+    VideoMode_80x25_text,       /* Mode 02h */
+    VideoMode_80x25_text,       /* Mode 03h */
+    VideoMode_320x200_4color,   /* Mode 04h */
+    VideoMode_320x200_4color,   /* Mode 05h */
+    NULL,                       /* Mode 06h */
+    NULL,                       /* Mode 07h */
+    NULL,                       /* Mode 08h */
+    NULL,                       /* Mode 09h */
+    NULL,                       /* Mode 0Ah */
+    NULL,                       /* Mode 0Bh */
+    NULL,                       /* Mode 0Ch */
+    NULL,                       /* Mode 0Dh */
+    NULL,                       /* Mode 0Eh */
+    NULL,                       /* Mode 0Fh */
+    NULL,                       /* Mode 10h */
+    NULL,                       /* Mode 11h */
+    VideoMode_640x480_16color,  /* Mode 12h */
+    VideoMode_320x200_256color, /* Mode 13h */
+};
+
+/* PRIVATE FUNCTIONS **********************************************************/
 
 static BOOLEAN BiosKbdBufferPush(WORD Data)
 {
@@ -154,207 +212,127 @@ static BOOLEAN BiosKbdBufferPop(VOID)
     return TRUE;
 }
 
-static BOOLEAN BiosCreateGraphicsBuffer(BYTE ModeNumber)
+static VOID BiosReadWindow(LPWORD Buffer, SMALL_RECT Rectangle, BYTE Page)
 {
-    INT i;
-    CONSOLE_GRAPHICS_BUFFER_INFO GraphicsBufferInfo;
-    LPBITMAPINFO BitmapInfo;
-    LPWORD PaletteIndex;
-
-    /* Allocate a bitmap info structure */
-    BitmapInfo = (LPBITMAPINFO)HeapAlloc(GetProcessHeap(),
-                                         HEAP_ZERO_MEMORY,
-                                         sizeof(BITMAPINFOHEADER)
-                                         + (1 << VideoModes[ModeNumber].Bpp)
-                                         * sizeof(WORD));
-    if (BitmapInfo == NULL) return FALSE;
-
-    /* Fill the bitmap info header */
-    ZeroMemory(&BitmapInfo->bmiHeader, sizeof(BITMAPINFOHEADER));
-    BitmapInfo->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
-    BitmapInfo->bmiHeader.biWidth = VideoModes[ModeNumber].Width;
-    BitmapInfo->bmiHeader.biHeight = VideoModes[ModeNumber].Height;
-    BitmapInfo->bmiHeader.biPlanes = 1;
-    BitmapInfo->bmiHeader.biCompression = BI_RGB;
-    BitmapInfo->bmiHeader.biBitCount = VideoModes[ModeNumber].Bpp;
-
-    /* Calculate the image size */
-    BitmapInfo->bmiHeader.biSizeImage = BitmapInfo->bmiHeader.biWidth
-                                        * BitmapInfo->bmiHeader.biHeight
-                                        * (BitmapInfo->bmiHeader.biBitCount >> 3);
-
-    /* Fill the palette data */
-    PaletteIndex = (LPWORD)((ULONG_PTR)BitmapInfo + sizeof(BITMAPINFOHEADER));
-    for (i = 0; i < (1 << VideoModes[ModeNumber].Bpp); i++)
-    {
-        PaletteIndex[i] = i;
-    }
-
-    /* Fill the console graphics buffer info */
-    GraphicsBufferInfo.dwBitMapInfoLength = sizeof(BITMAPINFOHEADER)
-                                            + (1 << VideoModes[ModeNumber].Bpp)
-                                            * sizeof(WORD);
-    GraphicsBufferInfo.lpBitMapInfo = BitmapInfo;
-    GraphicsBufferInfo.dwUsage = DIB_PAL_COLORS;
+    INT i, j;
+    INT Counter = 0;
+    DWORD VideoAddress = TO_LINEAR(TEXT_VIDEO_SEG, Page * Bda->VideoPageSize);
 
-    /* Create the buffer */
-    BiosGraphicsOutput = CreateConsoleScreenBuffer(GENERIC_READ | GENERIC_WRITE,
-                                                   FILE_SHARE_READ | FILE_SHARE_WRITE,
-                                                   NULL,
-                                                   CONSOLE_GRAPHICS_BUFFER,
-                                                   &GraphicsBufferInfo);
-
-    /* Save the framebuffer address and mutex */
-    ConsoleFramebuffer = GraphicsBufferInfo.lpBitMap;
-    ConsoleMutex = GraphicsBufferInfo.hMutex;
+    for (i = Rectangle.Top; i <= Rectangle.Bottom; i++)
+    {
+        for (j = Rectangle.Left; j <= Rectangle.Right; j++)
+        {
+            WORD Character;
 
-    /* Free the bitmap information */
-    HeapFree(GetProcessHeap(), 0, BitmapInfo);
+            /* Read from video memory */
+            VgaReadMemory(VideoAddress + (i * Bda->ScreenColumns + j) * sizeof(WORD),
+                          (LPVOID)&Character,
+                          sizeof(WORD));
 
-    return TRUE;
+            /* Write the data to the buffer in row order */
+            Buffer[Counter++] = Character;
+        }
+    }
 }
 
-static VOID BiosDestroyGraphicsBuffer(VOID)
+static VOID BiosWriteWindow(LPWORD Buffer, SMALL_RECT Rectangle, BYTE Page)
 {
-    CloseHandle(ConsoleMutex);
-    CloseHandle(BiosGraphicsOutput);
+    INT i, j;
+    INT Counter = 0;
+    DWORD VideoAddress = TO_LINEAR(TEXT_VIDEO_SEG, Page * Bda->VideoPageSize);
+
+    for (i = Rectangle.Top; i <= Rectangle.Bottom; i++)
+    {
+        for (j = Rectangle.Left; j <= Rectangle.Right; j++)
+        {
+            WORD Character = Buffer[Counter++];
+
+            /* Read from video memory */
+            VgaWriteMemory(VideoAddress + (i * Bda->ScreenColumns + j) * sizeof(WORD),
+                           (LPVOID)&Character,
+                           sizeof(WORD));
+        }
+    }
 }
 
 /* PUBLIC FUNCTIONS ***********************************************************/
 
 BYTE BiosGetVideoMode(VOID)
 {
-    return CurrentVideoMode;
+    return Bda->VideoMode;
 }
 
 BOOLEAN BiosSetVideoMode(BYTE ModeNumber)
 {
-    COORD Coord;
+    INT i;
+    COORD Resolution;
+    LPBYTE Values = VideoModes[ModeNumber];
 
-    /* Make sure this is a valid video mode */
-    if (ModeNumber > BIOS_MAX_VIDEO_MODE) return FALSE;
-    if (VideoModes[ModeNumber].Pages == 0) return FALSE;
+    if (Values == NULL) return FALSE;
 
-    /* Set the new video mode size */
-    Coord.X = VideoModes[ModeNumber].Width;
-    Coord.Y = VideoModes[ModeNumber].Height;
+    /* Write the misc register */
+    VgaWritePort(VGA_MISC_WRITE, *(Values++));
 
-    if (VideoModes[ModeNumber].Text && VideoModes[CurrentVideoMode].Text)
+    /* Write the sequencer registers */
+    for (i = 0; i < VGA_SEQ_MAX_REG; i++)
     {
-        /* Switching from text mode to another text mode */
-
-        /* Resize the text buffer */
-        SetConsoleScreenBufferSize(BiosConsoleOutput, Coord);
+        VgaWritePort(VGA_SEQ_INDEX, i);
+        VgaWritePort(VGA_SEQ_DATA, *(Values++));
     }
-    else if (VideoModes[ModeNumber].Text && !VideoModes[CurrentVideoMode].Text)
-    {
-        /* Switching from graphics mode to text mode */
 
-        /* Resize the text buffer */
-        SetConsoleScreenBufferSize(BiosConsoleOutput, Coord);
-
-        /* Change the active screen buffer to the text buffer */
-        SetConsoleActiveScreenBuffer(BiosConsoleOutput);
-
-        /* Cleanup the graphics buffer */
-        BiosDestroyGraphicsBuffer();
-    }
-    else if (!VideoModes[ModeNumber].Text && VideoModes[CurrentVideoMode].Text)
+    /* Write the GC registers */
+    for (i = 0; i < VGA_GC_MAX_REG; i++)
     {
-        /* Switching from text mode to graphics mode */
-        if (!BiosCreateGraphicsBuffer(ModeNumber)) return FALSE;
-
-        SetConsoleActiveScreenBuffer(BiosGraphicsOutput);
+        VgaWritePort(VGA_GC_INDEX, i);
+        VgaWritePort(VGA_GC_DATA, *(Values++));
     }
-    else if (!VideoModes[ModeNumber].Text && !VideoModes[CurrentVideoMode].Text)
-    {
-        /* Switching from graphics mode to another graphics mode */
-    
-        /* Temporarily switch to the text mode buffer */
-        SetConsoleActiveScreenBuffer(BiosConsoleOutput);
 
-        /* Cleanup the current graphics mode buffer */
-        BiosDestroyGraphicsBuffer();
-
-        /* Create a new graphics mode buffer */
-        if (!BiosCreateGraphicsBuffer(ModeNumber)) return FALSE;
-
-        /* Switch to it */
-        SetConsoleActiveScreenBuffer(BiosGraphicsOutput);
+    /* Write the CRTC registers */
+    for (i = 0; i < VGA_CRTC_MAX_REG; i++)
+    {
+        VgaWritePort(VGA_CRTC_INDEX, i);
+        VgaWritePort(VGA_CRTC_DATA, *(Values++));
     }
 
-    /* Change the mode number */
-    CurrentVideoMode = ModeNumber;
-    CurrentVideoPage = 0;
+    /* Write the AC registers */
+    for (i = 0; i < VGA_AC_MAX_REG; i++)
+    {
+        VgaWritePort(VGA_AC_INDEX, i);
+        VgaWritePort(VGA_AC_WRITE, *(Values++));
+    }
 
-    /* Update the BDA */
-    Bda->VideoMode = CurrentVideoMode;
-    Bda->VideoPage = CurrentVideoPage;
-    Bda->VideoPageSize = BiosGetVideoPageSize();
+    /* Update the values in the BDA */
+    Bda->VideoMode = ModeNumber;
+    Bda->VideoPage = 0;
+    Bda->VideoPageSize = BIOS_PAGE_SIZE;
     Bda->VideoPageOffset = 0;
-    Bda->ScreenColumns = VideoModes[ModeNumber].Width;
+    Bda->CharacterHeight = 16;
+
+    Resolution = VgaGetDisplayResolution();
+    Bda->ScreenColumns = Resolution.X;
+    Bda->ScreenRows = Resolution.Y - 1;
 
     return TRUE;
 }
 
 BOOLEAN BiosSetVideoPage(BYTE PageNumber)
 {
-    ULONG PageStart;
-    COORD Coordinates;
-    CONSOLE_SCREEN_BUFFER_INFO BufferInfo;
-
-    /* Make sure this is a valid page number */
-    if (PageNumber >= VideoModes[CurrentVideoMode].Pages) return FALSE;
+    if (PageNumber >= BIOS_MAX_PAGES) return FALSE;
 
-    /* Save the current console buffer in the video memory */
-    PageStart = BiosGetVideoMemoryStart() + CurrentVideoPage * BiosGetVideoPageSize();
-    BiosUpdateVideoMemory(PageStart, PageStart + BiosGetVideoPageSize());
+    /* Set the values in the BDA */
+    Bda->VideoPage = PageNumber;
+    Bda->VideoPageSize = BIOS_PAGE_SIZE;
+    Bda->VideoPageOffset = PageNumber * BIOS_PAGE_SIZE;
 
-    /* Save the cursor */
-    if (!GetConsoleScreenBufferInfo(BiosConsoleOutput, &BufferInfo)) return FALSE;
-    Bda->CursorPosition[CurrentVideoPage] = MAKEWORD(BufferInfo.dwCursorPosition.X,
-                                                     BufferInfo.dwCursorPosition.Y);
-
-    /* Set the page */
-    CurrentVideoPage = PageNumber;
-
-    /* Update the BDA */
-    Bda->VideoPage = CurrentVideoPage;
-    Bda->VideoPageSize = BiosGetVideoPageSize();
-    Bda->VideoPageOffset = CurrentVideoPage * Bda->VideoPageSize;
-
-    /* Update the console */
-    PageStart = BiosGetVideoMemoryStart() + Bda->VideoPage * BiosGetVideoPageSize();
-    BiosUpdateConsole(PageStart, PageStart + BiosGetVideoPageSize());
-
-    /* Set the cursor */
-    Coordinates.X = LOBYTE(Bda->CursorPosition[Bda->VideoPage]);
-    Coordinates.Y = HIBYTE(Bda->CursorPosition[Bda->VideoPage]);
-    SetConsoleCursorPosition(BiosConsoleOutput, Coordinates);
+    /* Set the start address in the CRTC */
+    VgaWritePort(VGA_CRTC_INDEX, VGA_CRTC_CURSOR_LOC_LOW_REG);
+    VgaWritePort(VGA_CRTC_DATA, LOBYTE(Bda->VideoPageOffset));
+    VgaWritePort(VGA_CRTC_INDEX, VGA_CRTC_CURSOR_LOC_HIGH_REG);
+    VgaWritePort(VGA_CRTC_DATA, HIBYTE(Bda->VideoPageOffset));
 
     return TRUE;
 }
 
-inline DWORD BiosGetVideoMemoryStart(VOID)
-{
-    return (VideoModes[CurrentVideoMode].Segment << 4);
-}
-
-inline VOID BiosVerticalRefresh(VOID)
-{
-    /* Ignore if we're in text mode */
-    if (VideoModes[CurrentVideoMode].Text) return;
-
-    /* Ignore if there's nothing to update */
-    if (!VideoNeedsUpdate) return;
-
-    /* Redraw the screen */
-    InvalidateConsoleDIBits(BiosGraphicsOutput, &UpdateRectangle);
-
-    /* Clear the update flag */
-    VideoNeedsUpdate = FALSE;
-}
-
 BOOLEAN BiosInitialize(VOID)
 {
     INT i;
@@ -425,8 +403,7 @@ BOOLEAN BiosInitialize(VOID)
     Bda->CursorPosition[0] = MAKEWORD(BiosSavedBufferInfo.dwCursorPosition.X,
                                       BiosSavedBufferInfo.dwCursorPosition.Y);
     
-    /* Set the default video mode */
-    BiosSetVideoMode(BIOS_DEFAULT_VIDEO_MODE);
+    VgaInitialize(BiosConsoleOutput);
 
     /* Set the console input mode */
     SetConsoleMode(BiosConsoleInput, ENABLE_MOUSE_INPUT | ENABLE_PROCESSED_INPUT);
@@ -466,151 +443,11 @@ VOID BiosCleanup(VOID)
     /* Restore the screen buffer size */
     SetConsoleScreenBufferSize(BiosConsoleOutput, BiosSavedBufferInfo.dwSize);
 
-    /* Free the graphics buffer */
-    if (!VideoModes[CurrentVideoMode].Text) BiosDestroyGraphicsBuffer();
-
     /* Close the console handles */
     if (BiosConsoleInput != INVALID_HANDLE_VALUE) CloseHandle(BiosConsoleInput);
     if (BiosConsoleOutput != INVALID_HANDLE_VALUE) CloseHandle(BiosConsoleOutput);
 }
 
-VOID BiosUpdateConsole(ULONG StartAddress, ULONG EndAddress)
-{
-    ULONG i;
-    COORD Coordinates;
-    COORD Origin = { 0, 0 };
-    COORD UnitSize = { 1, 1 };
-    CHAR_INFO Character;
-    SMALL_RECT Rect;
-
-    /* Start from the character address */
-    StartAddress &= ~1;
-
-    if (VideoModes[CurrentVideoMode].Text)
-    {
-        /* Loop through all the addresses */
-        for (i = StartAddress; i < EndAddress; i += 2)
-        {
-            /* Get the coordinates */
-            Coordinates = BiosVideoAddressToCoord(i);
-
-            /* Make sure this is the current page */
-            if (BiosVideoAddressToPage(i) != CurrentVideoPage) continue;
-
-            /* Fill the rectangle structure */
-            Rect.Left = Coordinates.X;
-            Rect.Top = Coordinates.Y;
-            Rect.Right = Rect.Left;
-            Rect.Bottom = Rect.Top;
-
-            /* Fill the character data */
-            Character.Char.AsciiChar = *((PCHAR)((ULONG_PTR)BaseAddress + i));
-            Character.Attributes = *((PBYTE)((ULONG_PTR)BaseAddress + i + 1));
-
-            /* Write the character */
-            WriteConsoleOutputA(BiosConsoleOutput,
-                                &Character,
-                                UnitSize,
-                                Origin,
-                                &Rect);
-        }
-    }
-    else
-    {
-        /* Wait for the mutex object */
-        WaitForSingleObject(ConsoleMutex, INFINITE);
-
-        /* Copy the data to the framebuffer */
-        RtlCopyMemory((LPVOID)((ULONG_PTR)ConsoleFramebuffer
-                      + StartAddress - BiosGetVideoMemoryStart()),
-                      (LPVOID)((ULONG_PTR)BaseAddress + StartAddress),
-                      EndAddress - StartAddress);
-
-        /* Release the mutex */
-        ReleaseMutex(ConsoleMutex);
-
-        /* Check if this is the first time the rectangle is updated */
-        if (!VideoNeedsUpdate)
-        {
-            UpdateRectangle.Left = UpdateRectangle.Top = (SHORT)0x7FFF;
-            UpdateRectangle.Right = UpdateRectangle.Bottom = (SHORT)0x8000;
-        }
-
-        /* Expand the update rectangle */
-        for (i = StartAddress; i < EndAddress; i++)
-        {
-            /* Get the coordinates */
-            Coordinates = BiosVideoAddressToCoord(i);
-
-            /* Expand the rectangle to include the point */
-            UpdateRectangle.Left = min(UpdateRectangle.Left, Coordinates.X);
-            UpdateRectangle.Right = max(UpdateRectangle.Right, Coordinates.X);
-            UpdateRectangle.Top = min(UpdateRectangle.Top, Coordinates.Y);
-            UpdateRectangle.Bottom = max(UpdateRectangle.Bottom, Coordinates.Y);
-        }
-
-        /* Set the update flag */
-        VideoNeedsUpdate = TRUE;
-    }
-}
-
-VOID BiosUpdateVideoMemory(ULONG StartAddress, ULONG EndAddress)
-{
-    ULONG i;
-    COORD Coordinates;
-    WORD Attribute;
-    DWORD CharsWritten;
-
-    if (VideoModes[CurrentVideoMode].Text)
-    {
-        /* Loop through all the addresses */
-        for (i = StartAddress; i < EndAddress; i++)
-        {
-            /* Get the coordinates */
-            Coordinates = BiosVideoAddressToCoord(i);
-
-            /* Make sure this is the current page */
-            if (BiosVideoAddressToPage(i) != CurrentVideoPage) continue;
-
-            /* Check if this is a character byte or an attribute byte */
-            if ((i - BiosGetVideoMemoryStart()) % 2 == 0)
-            {
-                /* This is a regular character */
-                ReadConsoleOutputCharacterA(BiosConsoleOutput,
-                                            (LPSTR)((ULONG_PTR)BaseAddress + i),
-                                            sizeof(CHAR),
-                                            Coordinates,
-                                            &CharsWritten);
-            }
-            else
-            {
-                /*  This is an attribute */
-                ReadConsoleOutputAttribute(BiosConsoleOutput,
-                                           &Attribute,
-                                           sizeof(CHAR),
-                                           Coordinates,
-                                           &CharsWritten);
-
-                *(PCHAR)((ULONG_PTR)BaseAddress + i) = LOBYTE(Attribute);
-            }
-        }
-    }
-    else
-    {
-        /* Wait for the mutex object */
-        WaitForSingleObject(ConsoleMutex, INFINITE);
-
-        /* Copy the data to the emulator memory */
-        RtlCopyMemory((LPVOID)((ULONG_PTR)BaseAddress + StartAddress),
-                      (LPVOID)((ULONG_PTR)ConsoleFramebuffer
-                      + StartAddress - BiosGetVideoMemoryStart()),
-                      EndAddress - StartAddress);
-
-        /* Release the mutex */
-        ReleaseMutex(ConsoleMutex);
-    }
-}
-
 WORD BiosPeekCharacter(VOID)
 {
     WORD CharacterData;
@@ -660,19 +497,124 @@ WORD BiosGetCharacter(VOID)
     return CharacterData;
 }
 
+VOID BiosSetCursorPosition(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] = (Row << 8) | Column;
+
+    /* Check if this is the current video page */
+    if (Page == Bda->VideoPage)
+    {
+        WORD Offset = Row * Bda->ScreenColumns + Column;
+
+        /* Modify the CRTC registers */
+        VgaWritePort(VGA_CRTC_INDEX, VGA_CRTC_CURSOR_LOC_LOW_REG);
+        VgaWritePort(VGA_CRTC_DATA, LOBYTE(Offset));
+        VgaWritePort(VGA_CRTC_INDEX, VGA_CRTC_CURSOR_LOC_HIGH_REG);
+        VgaWritePort(VGA_CRTC_DATA, HIBYTE(Offset));
+    }
+}
+
+BOOLEAN BiosScrollWindow(INT Direction,
+                         DWORD Amount,
+                         SMALL_RECT Rectangle,
+                         BYTE Page,
+                         BYTE FillAttribute)
+{
+    INT i;
+    LPWORD WindowData;
+    DWORD WindowSize = (Rectangle.Bottom - Rectangle.Top + 1)
+                       * (Rectangle.Right - Rectangle.Left + 1);
+
+    /* Allocate a buffer for the window */
+    WindowData = (LPWORD)HeapAlloc(GetProcessHeap(),
+                                   HEAP_ZERO_MEMORY,
+                                   WindowSize * sizeof(WORD));
+    if (WindowData == NULL) return FALSE;
+
+    /* Read the window data */
+    BiosReadWindow(WindowData, Rectangle, Page);
+
+    if (Amount == 0)
+    {
+        /* Fill the window */
+        for (i = 0; i < WindowSize; i++)
+        {
+            WindowData[i] = ' ' | (FillAttribute << 8);
+        }
+
+        goto Done;
+    }
+
+    // TODO: Scroll the window!
+
+Done:
+    /* Write back the window data */
+    BiosWriteWindow(WindowData, Rectangle, Page);
+
+    /* Free the window buffer */
+    HeapFree(GetProcessHeap(), 0, WindowData);
+
+    return TRUE;
+}
+
+VOID BiosPrintCharacter(CHAR Character, BYTE Attribute, BYTE Page)
+{
+    WORD CharData = (Attribute << 8) | Character;
+    BYTE Row, Column;
+
+    /* Make sure the page exists */
+    if (Page >= BIOS_MAX_PAGES) return;
+
+    /* Get the cursor location */
+    Row = HIBYTE(Bda->CursorPosition[Page]);
+    Column = LOBYTE(Bda->CursorPosition[Page]);
+
+    /* Write the character */
+    VgaWriteMemory(TO_LINEAR(TEXT_VIDEO_SEG,
+                   (Row * Bda->ScreenColumns + Column) * sizeof(WORD)),
+                   (LPVOID)&CharData,
+                   sizeof(WORD));
+
+    /* Advance the cursor */
+    Column++;
+
+    /* Check if it passed the end of the row */
+    if (Column == Bda->ScreenColumns)
+    {
+        /* Return to the first column */
+        Column = 0;
+
+        if (Row == Bda->ScreenRows)
+        {
+            /* The screen must be scrolled */
+            SMALL_RECT Rectangle = { 0, 0, Bda->ScreenColumns - 1, Bda->ScreenRows };
+
+            BiosScrollWindow(SCROLL_DIRECTION_UP,
+                             1,
+                             Rectangle,
+                             Page,
+                             DEFAULT_ATTRIBUTE);
+        }
+        else Row++;
+    }
+
+    /* Set the cursor position */
+    BiosSetCursorPosition(Row, Column, Page);
+}
+
 VOID BiosVideoService(LPWORD Stack)
 {
-    INT i, CursorHeight;
-    BOOLEAN Invisible = FALSE;
-    COORD Position;
-    CONSOLE_CURSOR_INFO CursorInfo;
-    CHAR_INFO Character;
-    SMALL_RECT Rect;
     DWORD Eax = EmulatorGetRegister(EMULATOR_REG_AX);
     DWORD Ecx = EmulatorGetRegister(EMULATOR_REG_CX);
     DWORD Edx = EmulatorGetRegister(EMULATOR_REG_DX);
     DWORD Ebx = EmulatorGetRegister(EMULATOR_REG_BX);
 
+    UNREFERENCED_PARAMETER(Ecx);
+
     switch (HIBYTE(Eax))
     {
         /* Set Video Mode */
@@ -685,20 +627,15 @@ VOID BiosVideoService(LPWORD Stack)
         /* Set Text-Mode Cursor Shape */
         case 0x01:
         {
-            /* Retrieve and validate the input */
-            Invisible = ((HIBYTE(Ecx) >> 5) & 0x03) ? TRUE : FALSE;
-            CursorHeight = (HIBYTE(Ecx) & 0x1F) - (LOBYTE(Ecx) & 0x1F);
-            if (CursorHeight < 1) CursorHeight = 1;
-            if (CursorHeight > 100) CursorHeight = 100;
-
             /* Update the BDA */
             Bda->CursorStartLine = HIBYTE(Ecx);
-            Bda->CursorEndLine = LOBYTE(Ecx) & 0x1F;
+            Bda->CursorEndLine = LOBYTE(Ecx);
 
-            /* Set the cursor */
-            CursorInfo.dwSize = (CursorHeight * 100) / CONSOLE_FONT_HEIGHT;
-            CursorInfo.bVisible = !Invisible;
-            SetConsoleCursorInfo(BiosConsoleOutput, &CursorInfo);
+            /* Modify the CRTC registers */
+            VgaWritePort(VGA_CRTC_INDEX, VGA_CRTC_CURSOR_START_REG);
+            VgaWritePort(VGA_CRTC_DATA, Bda->CursorStartLine);
+            VgaWritePort(VGA_CRTC_INDEX, VGA_CRTC_CURSOR_END_REG);
+            VgaWritePort(VGA_CRTC_DATA, Bda->CursorEndLine);
 
             break;
         }
@@ -706,20 +643,7 @@ VOID BiosVideoService(LPWORD Stack)
         /* Set Cursor Position */
         case 0x02:
         {
-            /* Make sure the selected video page exists */
-            if (HIBYTE(Ebx) >= VideoModes[CurrentVideoMode].Pages) break;
-
-            Bda->CursorPosition[HIBYTE(Ebx)] = LOWORD(Edx);
-
-            /* Check if this is the current video page */
-            if (HIBYTE(Ebx) == CurrentVideoPage)
-            {
-                /* Yes, change the actual cursor */
-                Position.X = LOBYTE(Edx);
-                Position.Y = HIBYTE(Edx);
-                SetConsoleCursorPosition(BiosConsoleOutput, Position);
-            }
-
+            BiosSetCursorPosition(HIBYTE(Edx), LOBYTE(Edx), HIBYTE(Ebx));
             break;
         }
 
@@ -727,7 +651,7 @@ VOID BiosVideoService(LPWORD Stack)
         case 0x03:
         {
             /* Make sure the selected video page exists */
-            if (HIBYTE(Ebx) >= VideoModes[CurrentVideoMode].Pages) break;
+            if (HIBYTE(Ebx) >= BIOS_MAX_PAGES) break;
 
             /* Return the result */
             EmulatorSetRegister(EMULATOR_REG_AX, 0);
@@ -742,10 +666,10 @@ VOID BiosVideoService(LPWORD Stack)
         case 0x05:
         {
             /* Check if the page exists */
-            if (LOBYTE(Eax) >= VideoModes[CurrentVideoMode].Pages) break;
+            if (LOBYTE(Eax) >= BIOS_MAX_PAGES) break;
 
             /* Check if this is the same page */
-            if (LOBYTE(Eax) == CurrentVideoPage) break;
+            if (LOBYTE(Eax) == Bda->VideoPage) break;
 
             /* Change the video page */
             BiosSetVideoPage(LOBYTE(Eax));
@@ -753,110 +677,63 @@ VOID BiosVideoService(LPWORD Stack)
             break;
         }
 
-        /* Scroll Up/Down Window */
-        // TODO: Implement for different pages
+        /* Scroll Window Up/Down */
         case 0x06:
         case 0x07:
         {
-            BYTE Lines = LOBYTE(Eax);
-
-            Rect.Top = HIBYTE(Ecx);
-            Rect.Left = LOBYTE(Ecx);
-            Rect.Bottom = HIBYTE(Edx);
-            Rect.Right = LOBYTE(Edx);
-            Character.Char.UnicodeChar = L' ';
-            Character.Attributes = HIBYTE(Ebx);
-            Position.X = Rect.Left;
-
-            /* 0 means clear entire window */
-            if (Lines == 0) Lines = Rect.Bottom - Rect.Top;
-
-            if (HIBYTE(Eax) == 0x06) Position.Y = Rect.Top - Lines;
-            else Position.Y = Rect.Top + Lines;
-
-            ScrollConsoleScreenBuffer(BiosConsoleOutput,
-                                      &Rect,
-                                      &Rect,
-                                      Position,
-                                      &Character);
+            SMALL_RECT Rectangle =
+            {
+                LOBYTE(Ecx),
+                HIBYTE(Ecx),
+                LOBYTE(Edx),
+                HIBYTE(Edx)
+            };
+
+            /* Call the internal function */
+            BiosScrollWindow((HIBYTE(Eax)== 0x06)
+                             ? SCROLL_DIRECTION_UP : SCROLL_DIRECTION_DOWN,
+                             LOBYTE(Eax),
+                             Rectangle,
+                             Bda->VideoPage,
+                             HIBYTE(Ebx));
 
             break;
         }
 
-        /* Read Character And Attribute At Cursor Position */
+        /* Read/Write Character From Cursor Position */
         case 0x08:
-        {
-            DWORD Address;
-            
-            /* Make sure this is text mode */
-            if (!VideoModes[CurrentVideoMode].Text) break;
-
-            /* Make sure the selected video page exists */
-            if (HIBYTE(Ebx) >= VideoModes[CurrentVideoMode].Pages) break;
-            
-            /* Find the address */
-            Address = BiosGetVideoMemoryStart()
-                      + HIBYTE(Ebx) * BiosGetVideoPageSize()
-                      + (HIBYTE(Bda->CursorPosition[HIBYTE(Ebx)])
-                      * VideoModes[CurrentVideoMode].Height
-                      + LOBYTE(Bda->CursorPosition[HIBYTE(Ebx)]))
-                      * VideoModes[CurrentVideoMode].Bpp / 8;
-
-            /* Update the video memory at that address */
-            BiosUpdateVideoMemory(Address,
-                                  Address + VideoModes[CurrentVideoMode].Bpp / 8);
-
-            /* Return the result in AX */
-            EmulatorSetRegister(EMULATOR_REG_AX,
-                                *((LPWORD)((ULONG_PTR)BaseAddress + Address)));
-
-            break;
-        }                EmulatorSetFlag(EMULATOR_FLAG_ZF);
-
-
-        /* Write Character And Attribute At Cursor Position */
         case 0x09:
         case 0x0A:
         {
-            BYTE PixelSize = VideoModes[CurrentVideoMode].Bpp / 8;
-            WORD Data = (LOBYTE(Ebx) << 8) | LOBYTE(Eax);
-            WORD Repeat = LOWORD(Ecx);
-            DWORD Address = BiosGetVideoMemoryStart()
-                            + CurrentVideoPage * BiosGetVideoPageSize()
-                            + (HIBYTE(Bda->CursorPosition[CurrentVideoPage])
-                            * VideoModes[CurrentVideoMode].Height
-                            + LOBYTE(Bda->CursorPosition[CurrentVideoPage]))
-                            * PixelSize;
-
-            /* Make sure this is text mode */
-            if (!VideoModes[CurrentVideoMode].Text) break;
+            WORD CharacterData = MAKEWORD(LOBYTE(Eax), LOBYTE(Ebx));
+            BYTE Page = HIBYTE(Ebx);
+            DWORD Offset;
 
-            /* Make sure the selected video page exists */
-            if (HIBYTE(Ebx) >= VideoModes[CurrentVideoMode].Pages) break;
+            /* Check if the page exists */
+            if (Page >= BIOS_MAX_PAGES) break;
 
-            /* Make sure we don't write over the end of video memory */
-            Repeat = min(Repeat,
-                        (CONSOLE_VIDEO_MEM_END - Address)
-                        / PixelSize);
+            /* Find the offset of the character */
+            Offset = Page * Bda->VideoPageSize
+                     + (HIBYTE(Bda->CursorPosition[Page]) * Bda->ScreenColumns
+                     + LOBYTE(Bda->CursorPosition[Page])) * 2;
 
-            /* Copy the values to the memory */
-            for (i = 0; i < Repeat; i++)
+            if (HIBYTE(Eax) == 0x08)
             {
-                if (PixelSize == sizeof(BYTE) || HIBYTE(Eax) == 0x0A)
-                {
-                    /* Just characters, no attributes */
-                    *((LPBYTE)((ULONG_PTR)BaseAddress + Address) + i * PixelSize) = LOBYTE(Data);
-                }
-                else if (PixelSize == sizeof(WORD))
-                {
-                    /* First byte for characters, second for attributes */
-                    *((LPWORD)((ULONG_PTR)BaseAddress + Address) + i) = Data;
-                }
-            }
+                /* Read from the video memory */
+                VgaReadMemory(TO_LINEAR(TEXT_VIDEO_SEG, Offset),
+                              (LPVOID)&CharacterData,
+                              sizeof(WORD));
 
-            /* Update the range */
-            BiosUpdateConsole(Address,
-                              Address + Repeat * (VideoModes[CurrentVideoMode].Bpp / 8));
+                /* Return the character in AX */
+                EmulatorSetRegister(EMULATOR_REG_AX, CharacterData);
+            }
+            else
+            {
+                /* Write to video memory */
+                VgaWriteMemory(TO_LINEAR(TEXT_VIDEO_SEG, Offset),
+                               (LPVOID)&CharacterData,
+                               (HIBYTE(Ebx) == 0x09) ? sizeof(WORD) : sizeof(BYTE));
+            }
 
             break;
         }
@@ -864,22 +741,7 @@ VOID BiosVideoService(LPWORD Stack)
         /* Teletype Output */
         case 0x0E:
         {
-            CHAR Character = LOBYTE(Eax);
-            DWORD NumWritten;
-
-            /* Make sure the page exists */
-            if (HIBYTE(Ebx) >= VideoModes[CurrentVideoMode].Pages) break;
-
-            /* Set the attribute */
-            SetConsoleTextAttribute(BiosConsoleOutput, LOBYTE(Ebx));
-
-            /* Write the character */
-            WriteConsoleA(BiosConsoleOutput,
-                          &Character,
-                          sizeof(CHAR),
-                          &NumWritten,
-                          NULL);
-
+            BiosPrintCharacter(LOBYTE(Eax), LOBYTE(Ebx), HIBYTE(Ebx));
             break;
         }
 
@@ -897,25 +759,20 @@ VOID BiosVideoService(LPWORD Stack)
         /* Scroll Window */
         case 0x12:
         {
-            Rect.Top = HIBYTE(Ecx);
-            Rect.Left = LOBYTE(Ecx);
-            Rect.Bottom = HIBYTE(Edx);
-            Rect.Right = LOBYTE(Edx);
-            Character.Char.UnicodeChar = L' ';
-            Character.Attributes = 0x07;
-            Position.X = Rect.Left;
-            Position.Y = Rect.Top;
-
-            if (LOBYTE(Ebx) == 0) Position.Y -= LOBYTE(Eax);
-            else if (LOBYTE(Ebx) == 1) Position.Y += LOBYTE(Eax);
-            else if (LOBYTE(Ebx) == 2) Position.X -= LOBYTE(Eax);
-            else if (LOBYTE(Ebx) == 3) Position.X += LOBYTE(Eax);
-
-            ScrollConsoleScreenBuffer(BiosConsoleOutput,
-                                      &Rect,
-                                      &Rect,
-                                      Position,
-                                      &Character);
+            SMALL_RECT Rectangle =
+            {
+                LOBYTE(Ecx),
+                HIBYTE(Ecx),
+                LOBYTE(Edx),
+                HIBYTE(Edx)
+            };
+
+            /* Call the internal function */
+            BiosScrollWindow(LOBYTE(Ebx),
+                             LOBYTE(Eax),
+                             Rectangle,
+                             Bda->VideoPage,
+                             DEFAULT_ATTRIBUTE);
 
             break;
         }
index 3644f3b..a31f9cf 100644 (file)
@@ -15,7 +15,6 @@
 
 /* DEFINES ********************************************************************/
 
-#define CONSOLE_VIDEO_MEM_END 0xBFFFF
 #define ROM_AREA_START 0xE0000
 #define ROM_AREA_END 0xFFFFF
 #define BDA_SEGMENT 0x40
 #define BIOS_EQUIPMENT_LIST 0x2C // HACK: Disable FPU for now
 #define BIOS_DEFAULT_VIDEO_MODE 0x03
 #define BIOS_MAX_PAGES 8
+#define BIOS_PAGE_SIZE 0x1000
 #define BIOS_MAX_VIDEO_MODE 0x13
+#define DEFAULT_ATTRIBUTE 0x07
+#define GRAPHICS_VIDEO_SEG 0xA000
+#define TEXT_VIDEO_SEG 0xB800
 
-typedef struct
+enum
 {
-    DWORD Width;
-    DWORD Height;
-    BOOLEAN Text;
-    BYTE Bpp;
-    BOOLEAN Gray;
-    BYTE Pages;
-    WORD Segment;
-} VIDEO_MODE;
+    SCROLL_DIRECTION_UP,
+    SCROLL_DIRECTION_DOWN,
+    SCROLL_DIRECTION_LEFT,
+    SCROLL_DIRECTION_RIGHT
+};
 
 #pragma pack(push, 1)
 
@@ -91,6 +91,8 @@ typedef struct
     BYTE ComTimeOut[4];
     WORD KeybdBufferStart;
     WORD KeybdBufferEnd;
+    BYTE ScreenRows;
+    WORD CharacterHeight;
 } BIOS_DATA_AREA, *PBIOS_DATA_AREA;
 
 #pragma pack(pop)
@@ -99,10 +101,8 @@ typedef struct
 
 BOOLEAN BiosInitialize(VOID);
 VOID BiosCleanup(VOID);
-VOID BiosUpdateConsole(ULONG StartAddress, ULONG EndAddress);
-VOID BiosUpdateVideoMemory(ULONG StartAddress, ULONG EndAddress);
-inline DWORD BiosGetVideoMemoryStart(VOID);
-inline VOID BiosVerticalRefresh(VOID);
+BYTE BiosGetVideoMode(VOID);
+BOOLEAN BiosSetVideoMode(BYTE ModeNumber);
 WORD BiosPeekCharacter(VOID);
 WORD BiosGetCharacter(VOID);
 VOID BiosVideoService(LPWORD Stack);
@@ -111,5 +111,12 @@ VOID BiosKeyboardService(LPWORD Stack);
 VOID BiosTimeService(LPWORD Stack);
 VOID BiosHandleIrq(BYTE IrqNumber, LPWORD Stack);
 VOID BiosSystemTimerInterrupt(LPWORD Stack);
+BOOLEAN BiosScrollWindow(
+    INT Direction,
+    DWORD Amount,
+    SMALL_RECT Rectangle,
+    BYTE Page,
+    BYTE FillAttribute
+);
 
 #endif
index 67cf885..38b225e 100644 (file)
@@ -98,6 +98,15 @@ typedef struct _DOS_INPUT_BUFFER
     CHAR Buffer[ANYSIZE_ARRAY];
 } DOS_INPUT_BUFFER, *PDOS_INPUT_BUFFER;
 
+typedef struct _DOS_DRIVER_HEADER
+{
+    DWORD NextDriver;
+    WORD Attributes;
+    WORD StrategyEntry;
+    WORD InterruptEntry;
+    CHAR DeviceName[8];
+} DOS_DRIVER_HEADER, *PDOS_DRIVER_HEADER;
+
 #pragma pack(pop)
 
 /* FUNCTIONS ******************************************************************/
index abb8684..2f471f3 100644 (file)
@@ -13,6 +13,7 @@
 #include "emulator.h"
 #include "bios.h"
 #include "dos.h"
+#include "vga.h"
 #include "pic.h"
 #include "ps2.h"
 #include "timer.h"
@@ -40,17 +41,19 @@ static VOID EmulatorReadMemory(PVOID Context, UINT Address, LPBYTE Buffer, INT S
     /* Make sure the requested address is valid */
     if ((Address + Size) >= MAX_ADDRESS) return;
 
-    /* Are we reading some of the console video memory? */
-    if (((Address + Size) >= BiosGetVideoMemoryStart())
-        && (Address < CONSOLE_VIDEO_MEM_END))
-    {
-        /* Call the VDM BIOS to update the video memory */
-        BiosUpdateVideoMemory(max(Address, BiosGetVideoMemoryStart()),
-                              min(Address + Size, CONSOLE_VIDEO_MEM_END));
-    }
-
     /* Read the data from the virtual address space and store it in the buffer */
     RtlCopyMemory(Buffer, (LPVOID)((ULONG_PTR)BaseAddress + Address), Size);
+
+    /* Check if we modified the console video memory */
+    if (((Address + Size) >= VgaGetVideoBaseAddress())
+        && (Address < VgaGetVideoLimitAddress()))
+    {
+        DWORD VgaAddress = max(Address, VgaGetVideoBaseAddress());
+        LPBYTE VgaBuffer = &Buffer[VgaAddress - Address];
+
+        /* Read from the VGA memory */
+        VgaReadMemory(VgaAddress, VgaBuffer, Size);
+    }
 }
 
 static VOID EmulatorWriteMemory(PVOID Context, UINT Address, LPBYTE Buffer, INT Size)
@@ -68,12 +71,14 @@ static VOID EmulatorWriteMemory(PVOID Context, UINT Address, LPBYTE Buffer, INT
     RtlCopyMemory((LPVOID)((ULONG_PTR)BaseAddress + Address), Buffer, Size);
 
     /* Check if we modified the console video memory */
-    if (((Address + Size) >= BiosGetVideoMemoryStart())
-        && (Address < CONSOLE_VIDEO_MEM_END))
+    if (((Address + Size) >= VgaGetVideoBaseAddress())
+        && (Address < VgaGetVideoLimitAddress()))
     {
-        /* Call the VDM BIOS to update the screen */
-        BiosUpdateConsole(max(Address, BiosGetVideoMemoryStart()),
-                          min(Address + Size, CONSOLE_VIDEO_MEM_END));
+        DWORD VgaAddress = max(Address, VgaGetVideoBaseAddress());
+        LPBYTE VgaBuffer = &Buffer[VgaAddress - Address];
+
+        /* Write to the VGA memory */
+        VgaWriteMemory(VgaAddress, VgaBuffer, Size);
     }
 }
 
@@ -115,6 +120,26 @@ static VOID EmulatorReadIo(PVOID Context, UINT Address, LPBYTE Buffer, INT Size)
             break;
         }
 
+        case VGA_AC_WRITE:
+        case VGA_AC_READ:
+        case VGA_SEQ_INDEX:
+        case VGA_SEQ_DATA:
+        case VGA_DAC_READ_INDEX:
+        case VGA_DAC_WRITE_INDEX:
+        case VGA_DAC_DATA:
+        case VGA_MISC_READ:
+        case VGA_MISC_WRITE:
+        case VGA_CRTC_INDEX:
+        case VGA_CRTC_DATA:
+        case VGA_GC_INDEX:
+        case VGA_GC_DATA:
+        case VGA_STAT_MONO:
+        case VGA_STAT_COLOR:
+        {
+            *Buffer = VgaReadPort(Address);
+            break;
+        }
+
         default:
         {
             DPRINT1("Read from unknown port: 0x%X\n", Address);
@@ -168,6 +193,26 @@ static VOID EmulatorWriteIo(PVOID Context, UINT Address, LPBYTE Buffer, INT Size
             break;
         }
 
+        case VGA_AC_WRITE:
+        case VGA_AC_READ:
+        case VGA_SEQ_INDEX:
+        case VGA_SEQ_DATA:
+        case VGA_DAC_READ_INDEX:
+        case VGA_DAC_WRITE_INDEX:
+        case VGA_DAC_DATA:
+        case VGA_MISC_READ:
+        case VGA_MISC_WRITE:
+        case VGA_CRTC_INDEX:
+        case VGA_CRTC_DATA:
+        case VGA_GC_INDEX:
+        case VGA_GC_DATA:
+        case VGA_STAT_MONO:
+        case VGA_STAT_COLOR:
+        {
+            VgaWritePort(Address, Byte);
+            break;
+        }
+
         default:
         {
             DPRINT1("Write to unknown port: 0x%X\n", Address);
@@ -478,6 +523,9 @@ VOID EmulatorStep(VOID)
         /* Skip the opcodes */
         EmulatorContext.state->reg_ip += 4;
 
+        // HACK: Refresh the display because the called function may wait.
+        VgaRefreshDisplay();
+
         /* Call the BOP handler */
         EmulatorBop(Instruction[1]);
     }
index 603774f..f4898c8 100644 (file)
@@ -13,6 +13,7 @@
 #include "ntvdm.h"
 #include "emulator.h"
 #include "bios.h"
+#include "vga.h"
 #include "dos.h"
 #include "timer.h"
 #include "pic.h"
@@ -137,7 +138,7 @@ INT wmain(INT argc, WCHAR *argv[])
         DisplayMessage(L"Could not start program: %S", CommandLine);
         return -1;
     }
-    
     /* Set the last timer tick to the current time */
     QueryPerformanceCounter(&LastTimerTick);
 
@@ -146,18 +147,18 @@ INT wmain(INT argc, WCHAR *argv[])
     {
         /* Get the current number of ticks */
         CurrentTickCount = GetTickCount();
-        
         /* Get the current performance counter value */
         QueryPerformanceCounter(&Counter);
-        
         /* Get the number of PIT ticks that have passed */
         TimerTicks = ((Counter.QuadPart - LastTimerTick.QuadPart)
                      * PIT_BASE_FREQUENCY) / Frequency.QuadPart;
-        
         /* Update the PIT */
         for (i = 0; i < TimerTicks; i++) PitDecrementCount();
         LastTimerTick = Counter;
-        
+
         /* Check for console input events every millisecond */
         if (CurrentTickCount != LastTickCount)
         {
@@ -165,20 +166,23 @@ INT wmain(INT argc, WCHAR *argv[])
             LastTickCount = CurrentTickCount;
         }
 
-        /* Check for vertical refresh */
+        /* Check for vertical retrace */
         if ((CurrentTickCount - LastVerticalRefresh) >= 16)
         {
-            BiosVerticalRefresh();
+            VgaRefreshDisplay();
             LastVerticalRefresh = CurrentTickCount;
         }
-        
+
+        /* Horizontal retrace occurs as fast as possible */
+        VgaHorizontalRetrace();
+
         /* Continue CPU emulation */
         for (i = 0; (i < STEPS_PER_CYCLE) && VdmRunning; i++)
         {
             EmulatorStep();
             Cycles++;
         }
-        
+
         if ((CurrentTickCount - LastCyclePrintout) >= 1000)
         {
             DPRINT1("NTVDM: %d Instructions Per Second\n", Cycles);
@@ -187,6 +191,9 @@ INT wmain(INT argc, WCHAR *argv[])
         }
     }
 
+    /* Perform another screen refresh */
+    VgaRefreshDisplay();
+
 Cleanup:
     BiosCleanup();
     EmulatorCleanup();
diff --git a/subsystems/ntvdm/vga.c b/subsystems/ntvdm/vga.c
new file mode 100644 (file)
index 0000000..fd57fd6
--- /dev/null
@@ -0,0 +1,974 @@
+/*
+ * COPYRIGHT:       GPL - See COPYING in the top level directory
+ * PROJECT:         ReactOS Virtual DOS Machine
+ * FILE:            vga.c
+ * PURPOSE:         VGA hardware emulation
+ * PROGRAMMERS:     Aleksandar Andrejevic <theflash AT sdf DOT lonestar DOT org>
+ */
+
+/* INCLUDES *******************************************************************/
+
+#define NDEBUG
+
+#include "vga.h"
+#include "bios.h"
+
+/* PRIVATE VARIABLES **********************************************************/
+
+static BYTE VgaMemory[VGA_NUM_BANKS * VGA_BANK_SIZE];
+static BYTE VgaMiscRegister;
+static BYTE VgaSeqIndex = VGA_SEQ_RESET_REG;
+static BYTE VgaSeqRegisters[VGA_SEQ_MAX_REG];
+static BYTE VgaGcIndex = VGA_GC_RESET_REG;
+static BYTE VgaGcRegisters[VGA_GC_MAX_REG];
+static BYTE VgaCrtcIndex = VGA_CRTC_HORZ_TOTAL_REG;
+static BYTE VgaCrtcRegisters[VGA_CRTC_MAX_REG];
+static BYTE VgaAcIndex = VGA_AC_PAL_0_REG;
+static BOOLEAN VgaAcLatch = FALSE;
+static BYTE VgaAcRegisters[VGA_AC_MAX_REG];
+static BYTE VgaDacIndex = 0;
+static BOOLEAN VgaDacReadWrite = FALSE;
+static BYTE VgaDacRegisters[VGA_PALETTE_SIZE];
+static BOOLEAN InVerticalRetrace = FALSE;
+static BOOLEAN InHorizontalRetrace = FALSE;
+static HANDLE TextConsoleBuffer = NULL;
+static HANDLE GraphicsConsoleBuffer = NULL;
+static LPVOID ConsoleFramebuffer = NULL;
+static HANDLE ConsoleMutex = NULL;
+static BOOLEAN NeedsUpdate = FALSE;
+static BOOLEAN ModeChanged = TRUE;
+static BOOLEAN CursorMoved = FALSE;
+static BOOLEAN TextMode = TRUE;
+static SMALL_RECT UpdateRectangle = { 0, 0, 0, 0 };
+
+/* PRIVATE FUNCTIONS **********************************************************/
+
+static inline INT VgaGetAddressSize(VOID)
+{
+    if (VgaCrtcRegisters[VGA_CRTC_UNDERLINE_REG] & VGA_CRTC_UNDERLINE_DWORD)
+    {
+        /* Double-word addressing */
+        return 4;
+    }
+    
+    if (VgaCrtcRegisters[VGA_CRTC_MODE_CONTROL_REG] & VGA_CRTC_MODE_CONTROL_BYTE)
+    {
+        /* Byte addressing */
+        return 1;
+    }
+
+    /* Word addressing */
+    return 2;
+}
+
+static inline DWORD VgaTranslateReadAddress(DWORD Address)
+{
+    CONST DWORD MemoryBase[] = { 0xA0000, 0xA0000, 0xB0000, 0xB8000 };
+    DWORD Offset = Address - MemoryBase[(VgaGcRegisters[VGA_GC_MISC_REG] >> 2) & 0x03];
+    BYTE Plane;
+
+    /* Check for chain-4 and odd-even mode */
+    if (VgaSeqRegisters[VGA_SEQ_MEM_REG] & VGA_SEQ_MEM_C4)
+    {
+        /* The lowest two bits are the plane number */
+        Plane = Offset & 3;
+        Offset >>= 2;
+    }
+    else if (VgaGcRegisters[VGA_GC_MODE_REG] & VGA_GC_MODE_OE)
+    {
+        /* The LSB is the plane number */
+        Plane = Offset & 1;
+        Offset >>= 1;
+    }
+    else
+    {
+        /* Use the read mode */
+        Plane = VgaGcRegisters[VGA_GC_READ_MAP_SEL_REG] & 0x03;
+    }
+
+    /* Multiply the offset by the address size */
+    Offset *= VgaGetAddressSize();
+    
+    return Offset + Plane * VGA_BANK_SIZE;
+}
+
+static inline DWORD VgaTranslateWriteAddress(DWORD Address)
+{
+    CONST DWORD MemoryBase[] = { 0xA0000, 0xA0000, 0xB0000, 0xB8000 };
+    DWORD Offset = Address - MemoryBase[(VgaGcRegisters[VGA_GC_MISC_REG] >> 2) & 0x03];
+
+    /* Check for chain-4 and odd-even mode */
+    if (VgaSeqRegisters[VGA_SEQ_MEM_REG] & VGA_SEQ_MEM_C4)
+    {
+        /* Shift the offset to the right by 2 */
+        Offset >>= 2;
+    }
+    else if (VgaGcRegisters[VGA_GC_MODE_REG] & VGA_GC_MODE_OE)
+    {
+        /* Shift the offset to the right by 1 */
+        Offset >>= 1;
+    }
+
+    /* Multiply the offset by the address size */
+    Offset *= VgaGetAddressSize();
+
+    /* Return the offset on plane 0 */
+    return Offset;
+}
+
+static inline VOID VgaMarkForUpdate(SHORT Row, SHORT Column)
+{
+    DPRINT("VgaMarkForUpdate: Row %d, Column %d\n", Row, Column);
+
+    /* Check if this is the first time the rectangle is updated */
+    if (!NeedsUpdate)
+    {
+        UpdateRectangle.Left = UpdateRectangle.Top = (SHORT)0x7FFF;
+        UpdateRectangle.Right = UpdateRectangle.Bottom = (SHORT)0x8000;
+    }
+
+    /* Expand the rectangle to include the point */
+    UpdateRectangle.Left = min(UpdateRectangle.Left, Column);
+    UpdateRectangle.Right = max(UpdateRectangle.Right, Column);
+    UpdateRectangle.Top = min(UpdateRectangle.Top, Row);
+    UpdateRectangle.Bottom = max(UpdateRectangle.Bottom, Row);
+
+    /* Set the update request flag */
+    NeedsUpdate = TRUE;
+}
+
+static VOID VgaWriteSequencer(BYTE Data)
+{
+    ASSERT(VgaSeqIndex < VGA_SEQ_MAX_REG);
+
+    /* Save the value */
+    VgaSeqRegisters[VgaSeqIndex] = Data;
+}
+
+static VOID VgaWriteGc(BYTE Data)
+{
+    ASSERT(VgaGcIndex < VGA_GC_MAX_REG);
+
+    /* Save the value */
+    VgaGcRegisters[VgaGcIndex] = Data;
+
+    /* Check the index */
+    switch (VgaGcIndex)
+    {
+        case VGA_GC_MISC_REG:
+        {
+            /* The GC misc register decides if it's text or graphics mode */
+            ModeChanged = TRUE;
+
+            break;
+        }
+    }
+}
+
+static VOID VgaWriteCrtc(BYTE Data)
+{
+    ASSERT(VgaGcIndex < VGA_CRTC_MAX_REG);
+
+    /* Save the value */
+    VgaCrtcRegisters[VgaCrtcIndex] = Data;
+
+    /* Check the index */
+    switch (VgaCrtcIndex)
+    {
+        case VGA_CRTC_END_HORZ_DISP_REG:
+        case VGA_CRTC_VERT_DISP_END_REG:
+        case VGA_CRTC_OVERFLOW_REG:
+        {
+            /* The video mode has changed */
+            ModeChanged = TRUE;
+
+            break;
+        }
+
+        case VGA_CRTC_CURSOR_LOC_LOW_REG:
+        case VGA_CRTC_CURSOR_LOC_HIGH_REG:
+        case VGA_CRTC_CURSOR_START_REG:
+        case VGA_CRTC_CURSOR_END_REG:
+        {
+            /* Set the cursor moved flag */
+            CursorMoved = TRUE;
+
+            break;
+        }
+    }
+}
+
+static VOID VgaWriteDac(BYTE Data)
+{
+    /* Set the value */
+    VgaDacRegisters[VgaDacIndex++] = Data;
+    VgaDacIndex %= VGA_PALETTE_SIZE;
+
+    // TODO: Change the palette!
+}
+
+static VOID VgaWriteAc(BYTE Data)
+{
+    ASSERT(VgaAcIndex < VGA_AC_MAX_REG);
+
+    /* Save the value */
+    VgaAcRegisters[VgaAcIndex] = Data;
+}
+
+static VOID VgaEnterGraphicsMode(UINT Width, UINT Height, UINT BitDepth)
+{
+    INT i;
+    CONSOLE_GRAPHICS_BUFFER_INFO GraphicsBufferInfo;
+    BYTE BitmapInfoBuffer[VGA_BITMAP_INFO_SIZE];
+    LPBITMAPINFO BitmapInfo = (LPBITMAPINFO)BitmapInfoBuffer;
+    LPWORD PaletteIndex = (LPWORD)(BitmapInfoBuffer + sizeof(BITMAPINFOHEADER));
+
+    /* Fill the bitmap info header */
+    ZeroMemory(&BitmapInfo->bmiHeader, sizeof(BITMAPINFOHEADER));
+    BitmapInfo->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
+    BitmapInfo->bmiHeader.biWidth = Width;
+    BitmapInfo->bmiHeader.biHeight = Height;
+    BitmapInfo->bmiHeader.biBitCount = 8;
+    BitmapInfo->bmiHeader.biPlanes = 1;
+    BitmapInfo->bmiHeader.biCompression = BI_RGB;
+    BitmapInfo->bmiHeader.biSizeImage = Width * Height * (BitDepth / 8);
+
+    /* Fill the palette data */
+    for (i = 0; i < BitDepth; i++) PaletteIndex[i] = i;
+
+    /* Fill the console graphics buffer info */
+    GraphicsBufferInfo.dwBitMapInfoLength = VGA_BITMAP_INFO_SIZE;
+    GraphicsBufferInfo.lpBitMapInfo = BitmapInfo;
+    GraphicsBufferInfo.dwUsage = DIB_PAL_COLORS;
+
+    /* Create the buffer */
+    GraphicsConsoleBuffer = CreateConsoleScreenBuffer(GENERIC_READ | GENERIC_WRITE,
+                                                      FILE_SHARE_READ | FILE_SHARE_WRITE,
+                                                      NULL,
+                                                      CONSOLE_GRAPHICS_BUFFER,
+                                                      &GraphicsBufferInfo);
+
+    /* Save the framebuffer address and mutex */
+    ConsoleFramebuffer = GraphicsBufferInfo.lpBitMap;
+    ConsoleMutex = GraphicsBufferInfo.hMutex;
+
+    /* Set the active buffer */
+    SetConsoleActiveScreenBuffer(GraphicsConsoleBuffer);
+}
+
+static VOID VgaLeaveGraphicsMode()
+{   
+    /* Switch back to the text buffer */
+    SetConsoleActiveScreenBuffer(TextConsoleBuffer);
+
+    /* Cleanup the video data */
+    CloseHandle(ConsoleMutex);
+    CloseHandle(GraphicsConsoleBuffer);
+    GraphicsConsoleBuffer = NULL;
+}
+
+static VOID VgaUpdateMode(VOID)
+{
+    COORD Resolution = VgaGetDisplayResolution();
+
+    if (!TextMode)
+    {
+        /* Switching from graphics mode to text mode */
+        VgaLeaveGraphicsMode();
+    }
+    else
+    {
+        /* Free the old framebuffer */
+        HeapFree(GetProcessHeap(), 0, ConsoleFramebuffer);
+        ConsoleFramebuffer = NULL;
+    }
+
+    /* Check if the new mode is alphanumeric */
+    if (!(VgaGcRegisters[VGA_GC_MISC_REG] & VGA_GC_MISC_NOALPHA))
+    {
+        /* Resize the console */
+        SetConsoleScreenBufferSize(TextConsoleBuffer, Resolution);
+
+        /* Allocate a framebuffer */
+        ConsoleFramebuffer = HeapAlloc(GetProcessHeap(),
+                                        HEAP_ZERO_MEMORY,
+                                        sizeof(CHAR_INFO)
+                                        * Resolution.X
+                                        * Resolution.Y);
+        if (ConsoleFramebuffer == NULL)
+        {
+            DisplayMessage(L"An unexpected error occurred!\n");
+            VdmRunning = FALSE;
+            return;
+        }
+
+        /* Set the text mode flag */
+        TextMode = TRUE;
+    }
+    else
+    {
+        /* Enter 8-bit graphics mode */
+        VgaEnterGraphicsMode(Resolution.X, Resolution.Y, 8);
+
+        /* Clear the text mode flag */
+        TextMode = FALSE;
+    }
+
+    /* Perform a full update */
+    NeedsUpdate = TRUE;
+    UpdateRectangle.Left = 0;
+    UpdateRectangle.Top = 0;
+    UpdateRectangle.Right = Resolution.X;
+    UpdateRectangle.Bottom = Resolution.Y;
+}
+
+static VOID VgaUpdateFramebuffer(VOID)
+{
+    INT i, j, k;
+    COORD Resolution = VgaGetDisplayResolution();
+    INT AddressSize = VgaGetAddressSize();
+    DWORD Address = (VgaCrtcRegisters[VGA_CRTC_START_ADDR_HIGH_REG] << 8)
+                    + VgaCrtcRegisters[VGA_CRTC_START_ADDR_LOW_REG];
+    DWORD ScanlineSize = (DWORD)VgaCrtcRegisters[VGA_CRTC_OFFSET_REG] * 2;
+    PCHAR_INFO CharBuffer = (PCHAR_INFO)ConsoleFramebuffer;
+    PBYTE GraphicsBuffer = (PBYTE)ConsoleFramebuffer;
+
+    /* Loop through the scanlines */
+    for (i = 0; i < Resolution.Y; i++)
+    {
+        /* Check if this is text mode or graphics mode */
+        if (VgaGcRegisters[VGA_GC_MISC_REG] & VGA_GC_MISC_NOALPHA)
+        {
+            /* Graphics mode */
+
+            /* Loop through the pixels */
+            for (j = 0; j < Resolution.X; j++)
+            {
+                BYTE PixelData = 0;
+
+                /* Check the shifting mode */
+                if (VgaGcRegisters[VGA_GC_MODE_REG] & VGA_GC_MODE_SHIFT256)
+                {
+                    /* 4 bits shifted from each plane */
+
+                    /* Check if this is 16 or 256 color mode */
+                    if (VgaAcRegisters[VGA_AC_CONTROL_REG] & VGA_AC_CONTROL_8BIT)
+                    {
+                        /* One byte per pixel */
+                        PixelData = VgaMemory[(j % VGA_NUM_BANKS) * VGA_BANK_SIZE
+                                              + (Address + (j / VGA_NUM_BANKS))
+                                              * AddressSize];
+                    }
+                    else
+                    {
+                        /* 4-bits per pixel */
+
+                        PixelData = VgaMemory[(j % VGA_NUM_BANKS) * VGA_BANK_SIZE
+                                              + (Address + (j / (VGA_NUM_BANKS * 2)))
+                                              * AddressSize];
+
+                        /* Check if we should use the highest 4 bits or lowest 4 */
+                        if (((j / VGA_NUM_BANKS) % 2) == 0)
+                        {
+                            /* Highest 4 */
+                            PixelData >>= 4;
+                        }
+                        else
+                        {
+                            /* Lowest 4 */
+                            PixelData &= 0x0F;
+                        }
+                    }
+                }
+                else if (VgaGcRegisters[VGA_GC_MODE_REG] & VGA_GC_MODE_SHIFTREG)
+                {
+                    /*
+                     * 2 bits shifted from plane 0 and 2 for the first 4 pixels,
+                     * then 2 bits shifted from plane 1 and 3 for the next 4
+                     */
+
+                    // TODO: NOT IMPLEMENTED!
+                    DPRINT1("Interleaved shift mode is not implemented!\n");
+                }
+                else
+                {
+                    /* 1 bit shifted from each plane */
+
+                    /* Check if this is 16 or 256 color mode */
+                    if (VgaAcRegisters[VGA_AC_CONTROL_REG] & VGA_AC_CONTROL_8BIT)
+                    {
+                        /* 8 bits per pixel, 2 on each plane */
+
+                        for (k = 0; k < VGA_NUM_BANKS; k++)
+                        {
+                            /* The data is on plane k, 4 pixels per byte */
+                            BYTE PlaneData = VgaMemory[k * VGA_BANK_SIZE
+                                                       + (Address + (j / 4)) * AddressSize];
+
+                            /* The mask of the first bit in the pair */
+                            BYTE BitMask = 1 << (((3 - (j % 4)) * 2) + 1);
+
+                            /* Bits 0, 1, 2 and 3 come from the first bit of the pair */
+                            if (PlaneData & BitMask) PixelData |= 1 << k;
+
+                            /* Bits 4, 5, 6 and 7 come from the second bit of the pair */
+                            if (PlaneData & (BitMask >> 1)) PixelData |= 1 << (k + 4);
+                        }
+                    }
+                    else
+                    {
+                        /* 4 bits per pixel, 1 on each plane */
+
+                        for (k = 0; k < VGA_NUM_BANKS; k++)
+                        {
+                            BYTE PlaneData = VgaMemory[k * VGA_BANK_SIZE
+                                                       + (Address + (j / 8)) * AddressSize];
+                        
+                            /* If the bit on that plane is set, set it */
+                            if (PlaneData & (1 << (7 - (j % 8)))) PixelData |= 1 << k;
+                        }
+                    }
+                }
+
+                /* Now check if the resulting pixel data has changed */
+                if (GraphicsBuffer[i * Resolution.X + j] != PixelData)
+                {
+                    /* Yes, write the new value */
+                    GraphicsBuffer[i * Resolution.X + j] = PixelData;
+
+                    /* Mark the specified pixel as changed */
+                    VgaMarkForUpdate(i, j);
+                }
+            }
+        }
+        else
+        {
+            /* Text mode */
+
+            /* Loop through the characters */
+            for (j = 0; j < Resolution.X; j++)
+            {
+                DWORD CurrentAddr = LOWORD((Address + j) * AddressSize);
+                CHAR_INFO CharInfo;
+
+                /* Plane 0 holds the character itself */
+                CharInfo.Char.AsciiChar = 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))
+                {
+                    /* Yes, write the new value */
+                    CharBuffer[i * Resolution.X + j] = CharInfo;
+
+                    /* Mark the specified pixel as changed */
+                    VgaMarkForUpdate(i, j);
+                }
+            }
+        }
+
+        /* Move to the next scanline */
+        Address += ScanlineSize;
+    }
+}
+
+static VOID VgaUpdateTextCursor(VOID)
+{
+    COORD Position;
+    CONSOLE_CURSOR_INFO CursorInfo;
+    BYTE CursorStart = VgaCrtcRegisters[VGA_CRTC_CURSOR_START_REG] & 0x3F;
+    BYTE CursorEnd = VgaCrtcRegisters[VGA_CRTC_CURSOR_END_REG] & 0x1F;
+    DWORD ScanlineSize = (DWORD)VgaCrtcRegisters[VGA_CRTC_OFFSET_REG] * 2;
+    WORD Location = MAKEWORD(VgaCrtcRegisters[VGA_CRTC_CURSOR_LOC_LOW_REG],
+                             VgaCrtcRegisters[VGA_CRTC_CURSOR_LOC_HIGH_REG]);
+
+    if (CursorStart < CursorEnd)
+    {
+        /* Visible cursor */
+        CursorInfo.bVisible = TRUE;
+        CursorInfo.dwSize = (100 * (CursorEnd - CursorStart)) >> 5;
+    }
+    else
+    {
+        /* No cursor */
+        CursorInfo.bVisible = FALSE;
+        CursorInfo.dwSize = 0;
+    }
+
+    /* Add the cursor skew to the location */
+    Location += (VgaCrtcRegisters[VGA_CRTC_CURSOR_END_REG] >> 5) & 3;
+
+    /* Find the coordinates of the new position */
+    Position.X = Location % ScanlineSize;
+    Position.Y = Location / ScanlineSize;
+
+    /* Update the physical cursor */
+    SetConsoleCursorInfo(TextConsoleBuffer, &CursorInfo);
+    SetConsoleCursorPosition(TextConsoleBuffer, Position);
+}
+
+/* PUBLIC FUNCTIONS ***********************************************************/
+
+DWORD VgaGetVideoBaseAddress(VOID)
+{
+    CONST DWORD MemoryBase[] = { 0xA0000, 0xA0000, 0xB0000, 0xB8000 };
+    return MemoryBase[(VgaGcRegisters[VGA_GC_MISC_REG] >> 2) & 0x03];
+}
+
+DWORD VgaGetVideoLimitAddress(VOID)
+{
+    CONST DWORD MemoryLimit[] = { 0xA7FFF, 0xA7FFF, 0xB7FFF, 0xBFFFF };
+    return MemoryLimit[(VgaGcRegisters[VGA_GC_MISC_REG] >> 2) & 0x03];
+}
+
+COORD VgaGetDisplayResolution(VOID)
+{
+    COORD Resolution;
+
+    /* The low 8 bits are in the display registers */
+    Resolution.X = VgaCrtcRegisters[VGA_CRTC_END_HORZ_DISP_REG];
+    Resolution.Y = VgaCrtcRegisters[VGA_CRTC_VERT_DISP_END_REG];
+
+    /* Set the top bits from the overflow register */
+    if (VgaCrtcRegisters[VGA_CRTC_OVERFLOW_REG] & VGA_CRTC_OVERFLOW_VDE8)
+    {
+        Resolution.Y |= 1 << 8;
+    }
+    if (VgaCrtcRegisters[VGA_CRTC_OVERFLOW_REG] & VGA_CRTC_OVERFLOW_VDE9)
+    {
+        Resolution.Y |= 1 << 9;
+    }
+
+    /* Increase the values by 1 */
+    Resolution.X++;
+    Resolution.Y++;
+
+    if (VgaGcRegisters[VGA_GC_MISC_REG] & VGA_GC_MISC_NOALPHA)
+    {
+        /* Multiply the horizontal resolution by the 9/8 dot mode */
+        Resolution.X *= (VgaSeqRegisters[VGA_SEQ_CLOCK_REG] & VGA_SEQ_CLOCK_98DM)
+                        ? 8 : 9;
+
+        /* The horizontal resolution is halved in 8-bit mode */
+        if (VgaAcRegisters[VGA_AC_CONTROL_REG] & VGA_AC_CONTROL_8BIT) Resolution.X /= 2;
+
+        /* Divide the vertical resolution by the maximum scan line */
+        Resolution.Y /= ((DWORD)VgaCrtcRegisters[VGA_CRTC_MAX_SCAN_LINE_REG] & 0x1F) + 1;
+    }
+    else
+    {
+        /* Divide the number of scanlines by the font size */
+        Resolution.Y /= 16;
+    }
+
+    /* Return the resolution */
+    return Resolution;
+}
+
+VOID VgaRefreshDisplay(VOID)
+{
+    COORD Resolution = VgaGetDisplayResolution();
+
+    DPRINT("VgaRefreshDisplay\n");
+
+    if (ModeChanged)
+    {
+        /* Change the display mode */
+        VgaUpdateMode();
+
+        /* Reset the mode change flag */
+        ModeChanged = FALSE;
+    }
+
+    if (CursorMoved)
+    {
+        /* Change the text cursor location */
+        VgaUpdateTextCursor();
+
+        /* Reset the cursor move flag */
+        CursorMoved = FALSE;
+    }
+
+    /* Update the contents of the framebuffer */
+    VgaUpdateFramebuffer();
+
+    /* Set the vertical retrace flag */
+    InVerticalRetrace = TRUE;
+
+    /* Ignore if there's nothing to update */
+    if (!NeedsUpdate) return;
+
+    DPRINT("Updating screen rectangle (%d, %d, %d, %d)\n",
+           UpdateRectangle.Left,
+           UpdateRectangle.Top,
+           UpdateRectangle.Right,
+           UpdateRectangle.Bottom);
+
+    /* Check if this is text mode or graphics mode */
+    if (VgaGcRegisters[VGA_GC_MISC_REG] & VGA_GC_MISC_NOALPHA)
+    {
+        /* Graphics mode */
+
+        /* Redraw the screen */
+        InvalidateConsoleDIBits(GraphicsConsoleBuffer, &UpdateRectangle);
+    }
+    else
+    {
+        /* Text mode */
+        COORD Origin = { UpdateRectangle.Left, UpdateRectangle.Top };
+
+        /* Write the data to the console */
+        WriteConsoleOutputA(TextConsoleBuffer,
+                            (PCHAR_INFO)ConsoleFramebuffer,
+                            Resolution,
+                            Origin,
+                            &UpdateRectangle);
+
+    }
+
+    /* Clear the update flag */
+    NeedsUpdate = FALSE;
+}
+
+VOID VgaHorizontalRetrace(VOID)
+{
+    /* Set the flag */
+    InHorizontalRetrace = TRUE;
+}
+
+VOID VgaReadMemory(DWORD Address, LPBYTE Buffer, DWORD Size)
+{
+    INT i;
+
+    DPRINT("VgaReadMemory: Address 0x%08X, Size %lu\n",
+           Address,
+           Size);
+
+    /* Ignore if video RAM access is disabled */
+    if (!(VgaMiscRegister & VGA_MISC_RAM_ENABLED)) return;
+
+    /* Loop through each byte */
+    for (i = 0; i < Size; i++)
+    {
+        DWORD VideoAddress = VgaTranslateReadAddress(Address + i);
+
+        /* Copy the value to the buffer */
+        Buffer[i] = VgaMemory[VideoAddress];
+    }
+}
+
+VOID VgaWriteMemory(DWORD Address, LPBYTE Buffer, DWORD Size)
+{
+    INT i, j;
+
+    DPRINT("VgaWriteMemory: Address 0x%08X, Size %lu\n",
+           Address,
+           Size);
+
+    /* Ignore if video RAM access is disabled */
+    if (!(VgaMiscRegister & VGA_MISC_RAM_ENABLED)) return;
+
+    /* Also ignore if write access to all planes is disabled */
+    if ((VgaSeqRegisters[VGA_SEQ_MASK_REG] & 0x0F) == 0x00) return;
+
+    /* Loop through each byte */
+    for (i = 0; i < Size; i++)
+    {
+        DWORD VideoAddress = VgaTranslateWriteAddress(Address + i);
+
+        for (j = 0; j < VGA_NUM_BANKS; j++)
+        {
+            /* Make sure the page is writeable */
+            if (!(VgaSeqRegisters[VGA_SEQ_MASK_REG] & (1 << j))) continue;
+
+            /* Check if this is chain-4 mode */
+            if (VgaSeqRegisters[VGA_SEQ_MEM_REG] & VGA_SEQ_MEM_C4)
+            {
+                if (((Address + i) & 3) != j)
+                {
+                    /* This plane will not be accessed */
+                    continue;
+                }
+            }
+
+            /* Check if this is odd-even mode */
+            if (VgaGcRegisters[VGA_GC_MODE_REG] & VGA_GC_MODE_OE)
+            {
+                if (((Address + i) & 1) != (j & 1))
+                {
+                    /* This plane will not be accessed */
+                    continue;
+                }
+            }
+
+            /* Copy the value to the VGA memory */
+            VgaMemory[VideoAddress + j * VGA_BANK_SIZE] = Buffer[i];
+        }
+    }
+}
+
+BYTE VgaReadPort(WORD Port)
+{
+    DPRINT("VgaReadPort: Port 0x%04X\n", Port);
+
+    switch (Port)
+    {
+        case VGA_AC_INDEX:
+        {
+            return VgaAcIndex;
+        }
+
+        case VGA_AC_READ:
+        {
+            return VgaAcRegisters[VgaAcIndex];
+        }
+
+        case VGA_SEQ_INDEX:
+        {
+            return VgaSeqIndex;
+        }
+        
+        case VGA_SEQ_DATA:
+        {
+            return VgaSeqRegisters[VgaSeqIndex];
+        }
+
+        case VGA_DAC_READ_INDEX:
+        {
+            /* This returns the read/write state */
+            return VgaDacReadWrite ? 0 : 3;
+        }
+
+        case VGA_DAC_WRITE_INDEX:
+        {
+            return VgaDacIndex;
+        }
+
+        case VGA_DAC_DATA:
+        {
+            /* Ignore reads in write mode */
+            if (!VgaDacReadWrite)
+            {
+                BYTE Data = VgaDacRegisters[VgaDacIndex++];
+                VgaDacIndex %= VGA_PALETTE_SIZE;
+                return Data;
+            }
+
+            break;
+        }
+
+        case VGA_MISC_READ:
+        {
+            return VgaMiscRegister;
+        }
+
+        case VGA_CRTC_INDEX:
+        {
+            return VgaCrtcIndex;
+        }
+
+        case VGA_CRTC_DATA:
+        {
+            return VgaCrtcRegisters[VgaCrtcIndex];
+        }
+
+        case VGA_GC_INDEX:
+        {
+            return VgaGcIndex;
+        }
+
+        case VGA_GC_DATA:
+        {
+            return VgaGcRegisters[VgaGcIndex];
+        }
+
+        case VGA_STAT_MONO:
+        case VGA_STAT_COLOR:
+        {
+            BYTE Result = 0;
+
+            /* Reset the AC latch */
+            VgaAcLatch = FALSE;
+
+            /* Set a flag if there is a vertical or horizontal retrace */
+            if (InVerticalRetrace || InHorizontalRetrace) Result |= VGA_STAT_DD;
+
+            /* Set an additional flag if there was a vertical retrace */
+            if (InVerticalRetrace) Result |= VGA_STAT_VRETRACE;
+
+            /* Clear the flags */
+            InHorizontalRetrace = InVerticalRetrace = FALSE;
+
+            return Result;
+        }
+    }
+    
+    return 0;
+}
+
+VOID VgaWritePort(WORD Port, BYTE Data)
+{
+    DPRINT("VgaWritePort: Port 0x%04X, Data 0x%02X\n", Port, Data);
+
+    switch (Port)
+    {
+        case VGA_AC_INDEX:
+        {
+            if (!VgaAcLatch)
+            {
+                /* Change the index */
+                if (Data < VGA_AC_MAX_REG) VgaAcIndex = Data;
+            }
+            else
+            {
+                /* Write the data */
+                VgaWriteAc(Data);
+            }
+
+            /* Toggle the latch */
+            VgaAcLatch = !VgaAcLatch;
+
+            break;
+        }
+
+        case VGA_SEQ_INDEX:
+        {
+            /* Set the sequencer index register */
+            if (Data < VGA_SEQ_MAX_REG) VgaSeqIndex = Data;
+            
+            break;
+        }
+
+        case VGA_SEQ_DATA:
+        {
+            /* Call the sequencer function */
+            VgaWriteSequencer(Data);
+            
+            break;
+        }
+
+        case VGA_DAC_READ_INDEX:
+        {
+            VgaDacReadWrite = FALSE;
+            VgaDacIndex = Data % VGA_PALETTE_SIZE;
+
+            break;
+        }
+
+        case VGA_DAC_WRITE_INDEX:
+        {
+            VgaDacReadWrite = TRUE;
+            VgaDacIndex = Data % VGA_PALETTE_SIZE;
+
+            break;
+        }
+
+        case VGA_DAC_DATA:
+        {
+            /* Ignore writes in read mode */
+            if (VgaDacReadWrite) VgaWriteDac(Data & 0x3F);
+
+            break;
+        }
+
+        case VGA_MISC_WRITE:
+        {
+            VgaMiscRegister = Data;
+
+            break;
+        }
+
+        case VGA_CRTC_INDEX:
+        {
+            /* Set the CRTC index register */
+            if (Data < VGA_CRTC_MAX_REG) VgaCrtcIndex = Data;
+
+            break;
+        }
+
+        case VGA_CRTC_DATA:
+        {
+            /* Call the CRTC function */
+            VgaWriteCrtc(Data);
+
+            break;
+        }
+
+        case VGA_GC_INDEX:
+        {
+            /* Set the GC index register */
+            if (Data < VGA_GC_MAX_REG) VgaGcIndex = Data;
+            break;
+        }
+
+        case VGA_GC_DATA:
+        {
+            /* Call the GC function */
+            VgaWriteGc(Data);
+
+            break;
+        }
+    }
+}
+
+VOID VgaInitialize(HANDLE TextHandle)
+{
+    INT i, j;
+    COORD Resolution;
+    INT AddressSize;
+    DWORD ScanlineSize;
+    COORD Origin = { 0, 0 };
+    SMALL_RECT ScreenRect;
+    PCHAR_INFO CharBuffer;
+    DWORD Address = 0;
+
+    /* Set the global handle */
+    TextConsoleBuffer = TextHandle;
+
+    /* Set the default video mode */
+    BiosSetVideoMode(BIOS_DEFAULT_VIDEO_MODE);
+    VgaUpdateMode();
+    ModeChanged = FALSE;
+
+    /* Get the data */
+    Resolution = VgaGetDisplayResolution();
+    CharBuffer = (PCHAR_INFO)ConsoleFramebuffer;
+    AddressSize = VgaGetAddressSize();
+    ScreenRect.Left = ScreenRect.Top = 0;
+    ScreenRect.Right = Resolution.X;
+    ScreenRect.Bottom = Resolution.Y;
+    ScanlineSize = (DWORD)VgaCrtcRegisters[VGA_CRTC_OFFSET_REG] * 2;
+
+    /* Read the data from the console into the framebuffer */
+    ReadConsoleOutputA(TextConsoleBuffer,
+                       ConsoleFramebuffer,
+                       Resolution,
+                       Origin,
+                       &ScreenRect);
+
+
+    /* Loop through the scanlines */
+    for (i = 0; i < Resolution.Y; i++)
+    {
+        /* Loop through the characters */
+        for (j = 0; j < Resolution.X; j++)
+        {
+            DWORD CurrentAddr = LOWORD((Address + j) * AddressSize);
+
+            /* Store the character in plane 0 */
+            VgaMemory[CurrentAddr] = CharBuffer[i * Resolution.X + j].Char.AsciiChar;
+
+            /* Store the attribute in plane 1 */
+            VgaMemory[CurrentAddr + VGA_BANK_SIZE] = CharBuffer[i * Resolution.X + j].Attributes;
+        }
+
+        /* Move to the next scanline */
+        Address += ScanlineSize;
+    }
+}
+
+/* EOF */
+
diff --git a/subsystems/ntvdm/vga.h b/subsystems/ntvdm/vga.h
new file mode 100644 (file)
index 0000000..e8769e3
--- /dev/null
@@ -0,0 +1,200 @@
+/*
+ * COPYRIGHT:       GPL - See COPYING in the top level directory
+ * PROJECT:         ReactOS Virtual DOS Machine
+ * FILE:            vga.h
+ * PURPOSE:         VGA hardware emulation (header file)
+ * PROGRAMMERS:     Aleksandar Andrejevic <theflash AT sdf DOT lonestar DOT org>
+ */
+
+#ifndef _VGA_H_
+#define _VGA_H_
+
+/* INCLUDES *******************************************************************/
+
+#include "ntvdm.h"
+
+/* DEFINES ********************************************************************/
+
+/* Register I/O ports */
+#define VGA_AC_INDEX 0x3C0
+#define VGA_AC_WRITE 0x3C0
+#define VGA_AC_READ 0x3C1
+#define VGA_SEQ_INDEX 0x3C4
+#define VGA_SEQ_DATA 0x3C5
+#define VGA_DAC_READ_INDEX 0x3C7
+#define VGA_DAC_WRITE_INDEX 0x3C8
+#define VGA_DAC_DATA 0x3C9
+#define VGA_MISC_READ 0x3CC
+#define VGA_MISC_WRITE 0x3C2
+#define VGA_CRTC_INDEX 0x3D4
+#define VGA_CRTC_DATA 0x3D5
+#define VGA_GC_INDEX 0x3CE
+#define VGA_GC_DATA 0x3CF
+#define VGA_STAT_MONO 0x3BA
+#define VGA_STAT_COLOR 0x3DA
+
+#define VGA_NUM_BANKS 4
+#define VGA_BANK_SIZE 0x10000
+#define VGA_PALETTE_SIZE 768
+#define VGA_BITMAP_INFO_SIZE (sizeof(BITMAPINFOHEADER) + 2 * (VGA_PALETTE_SIZE / 3))
+
+/* Sequencer reset register bits */
+#define VGA_SEQ_RESET_AR (1 << 0)
+#define VGA_SEQ_RESET_SR (1 << 1)
+
+/* Sequencer clock register bits */
+#define VGA_SEQ_CLOCK_98DM (1 << 0)
+#define VGA_SEQ_CLOCK_SLR (1 << 2)
+#define VGA_SEQ_CLOCK_DCR (1 << 3)
+#define VGA_SEQ_CLOCK_S4 (1 << 4)
+#define VGA_SEQ_CLOCK_SD (1 << 5)
+
+/* Sequencer memory register bits */
+#define VGA_SEQ_MEM_EXT (1 << 1)
+#define VGA_SEQ_MEM_OE (1 << 2)
+#define VGA_SEQ_MEM_C4 (1 << 3)
+
+/* Graphics controller mode register bits */
+#define VGA_GC_MODE_READ (1 << 3)
+#define VGA_GC_MODE_OE (1 << 4)
+#define VGA_GC_MODE_SHIFTREG (1 << 5)
+#define VGA_GC_MODE_SHIFT256 (1 << 6)
+
+/* Graphics controller miscellaneous register bits */
+#define VGA_GC_MISC_NOALPHA (1 << 0)
+#define VGA_GC_MISC_OE (1 << 1)
+
+/* CRTC overflow register bits */
+#define VGA_CRTC_OVERFLOW_VT8 (1 << 0)
+#define VGA_CRTC_OVERFLOW_VDE8 (1 << 1)
+#define VGA_CRTC_OVERFLOW_VRS8 (1 << 2)
+#define VGA_CRTC_OVERFLOW_SVB8 (1 << 3)
+#define VGA_CRTC_OVERFLOW_LC8 (1 << 4)
+#define VGA_CRTC_OVERFLOW_VT9 (1 << 5)
+#define VGA_CRTC_OVERFLOW_VDE9 (1 << 6)
+#define VGA_CRTC_OVERFLOW_VRS9 (1 << 7)
+
+/* CRTC underline register bits */
+#define VGA_CRTC_UNDERLINE_DWORD (1 << 6)
+
+/* CRTC mode control register bits */
+#define VGA_CRTC_MODE_CONTROL_WRAP (1 << 5)
+#define VGA_CRTC_MODE_CONTROL_BYTE (1 << 6)
+#define VGA_CRTC_MODE_CONTROL_SYNC (1 << 7)
+
+/* AC control register bits */
+#define VGA_AC_CONTROL_ATGE (1 << 0)
+#define VGA_AC_CONTROL_MONO (1 << 1)
+#define VGA_AC_CONTROL_LGE (1 << 2)
+#define VGA_AC_CONTROL_BLINK (1 << 3)
+#define VGA_AC_CONTROL_PPM (1 << 5)
+#define VGA_AC_CONTROL_8BIT (1 << 6)
+#define VGA_AC_CONTROL_P54S (1 << 7)
+
+/* Miscellaneous register bits */
+#define VGA_MISC_COLOR (1 << 0)
+#define VGA_MISC_RAM_ENABLED (1 << 1)
+#define VGA_MISC_OE_INVERT (1 << 5)
+#define VGA_MISC_HSYNCP (1 << 6)
+#define VGA_MISC_VSYNCP (1 << 7)
+
+/* Status register flags */
+#define VGA_STAT_DD (1 << 0)
+#define VGA_STAT_VRETRACE (1 << 3)
+
+enum
+{
+    VGA_SEQ_RESET_REG,
+    VGA_SEQ_CLOCK_REG,
+    VGA_SEQ_MASK_REG,
+    VGA_SEQ_CHAR_REG,
+    VGA_SEQ_MEM_REG,
+    VGA_SEQ_MAX_REG
+};
+
+enum
+{
+    VGA_GC_RESET_REG,
+    VGA_GC_ENABLE_RESET_REG,
+    VGA_GC_COLOR_COMPARE_REG,
+    VGA_GC_ROTATE_REG,
+    VGA_GC_READ_MAP_SEL_REG,
+    VGA_GC_MODE_REG,
+    VGA_GC_MISC_REG,
+    VGA_GC_COLOR_IGNORE_REG,
+    VGA_GC_BITMASK_REG,
+    VGA_GC_MAX_REG
+};
+
+enum
+{
+    VGA_CRTC_HORZ_TOTAL_REG,
+    VGA_CRTC_END_HORZ_DISP_REG,
+    VGA_CRTC_START_HORZ_BLANKING_REG,
+    VGA_CRTC_END_HORZ_BLANKING_REG,
+    VGA_CRTC_START_HORZ_RETRACE_REG,
+    VGA_CRTC_END_HORZ_RETRACE_REG,
+    VGA_CRTC_VERT_TOTAL_REG,
+    VGA_CRTC_OVERFLOW_REG,
+    VGA_CRTC_PRESET_ROW_SCAN_REG,
+    VGA_CRTC_MAX_SCAN_LINE_REG,
+    VGA_CRTC_CURSOR_START_REG,
+    VGA_CRTC_CURSOR_END_REG,
+    VGA_CRTC_START_ADDR_HIGH_REG,
+    VGA_CRTC_START_ADDR_LOW_REG,
+    VGA_CRTC_CURSOR_LOC_HIGH_REG,
+    VGA_CRTC_CURSOR_LOC_LOW_REG,
+    VGA_CRTC_VERT_RETRACE_START_REG,
+    VGA_CRTC_VERT_RETRACE_END_REG,
+    VGA_CRTC_VERT_DISP_END_REG,
+    VGA_CRTC_OFFSET_REG,
+    VGA_CRTC_UNDERLINE_REG,
+    VGA_CRTC_START_VERT_BLANKING_REG,
+    VGA_CRTC_END_VERT_BLANKING,
+    VGA_CRTC_MODE_CONTROL_REG,
+    VGA_CRTC_LINE_COMPARE_REG,
+    VGA_CRTC_MAX_REG
+};
+
+enum
+{
+    VGA_AC_PAL_0_REG,
+    VGA_AC_PAL_1_REG,
+    VGA_AC_PAL_2_REG,
+    VGA_AC_PAL_3_REG,
+    VGA_AC_PAL_4_REG,
+    VGA_AC_PAL_5_REG,
+    VGA_AC_PAL_6_REG,
+    VGA_AC_PAL_7_REG,
+    VGA_AC_PAL_8_REG,
+    VGA_AC_PAL_9_REG,
+    VGA_AC_PAL_A_REG,
+    VGA_AC_PAL_B_REG,
+    VGA_AC_PAL_C_REG,
+    VGA_AC_PAL_D_REG,
+    VGA_AC_PAL_E_REG,
+    VGA_AC_PAL_F_REG,
+    VGA_AC_CONTROL_REG,
+    VGA_AC_OVERSCAN_REG,
+    VGA_AC_COLOR_PLANE_REG,
+    VGA_AC_HORZ_PANNING_REG,
+    VGA_AC_COLOR_SEL_REG,
+    VGA_AC_MAX_REG
+};
+
+/* FUNCTIONS ******************************************************************/
+
+DWORD VgaGetVideoBaseAddress(VOID);
+DWORD VgaGetVideoLimitAddress(VOID);
+COORD VgaGetDisplayResolution(VOID);
+VOID VgaRefreshDisplay(VOID);
+VOID VgaHorizontalRetrace(VOID);
+VOID VgaReadMemory(DWORD Address, LPBYTE Buffer, DWORD Size);
+VOID VgaWriteMemory(DWORD Address, LPBYTE Buffer, DWORD Size);
+BYTE VgaReadPort(WORD Port);
+VOID VgaWritePort(WORD Port, BYTE Data);
+VOID VgaInitialize(HANDLE TextHandle);
+
+#endif
+
+/* EOF */