[NTVDM]
[reactos.git] / reactos / subsystems / mvdm / ntvdm / hardware / video / vga.c
index e0226a7..bafe9ee 100644 (file)
@@ -10,6 +10,7 @@
 
 #define NDEBUG
 
+#include "ntvdm.h"
 #include "emulator.h"
 #include "vga.h"
 #include <bios/vidbios.h>
@@ -20,8 +21,8 @@
 
 /* PRIVATE VARIABLES **********************************************************/
 
-static CONST DWORD MemoryBase[]  = { 0xA0000, 0xA0000, 0xB0000, 0xB8000 };
-static CONST DWORD MemoryLimit[] = { 0xAFFFF, 0xAFFFF, 0xB7FFF, 0xBFFFF };
+static CONST DWORD MemoryBase[] = { 0xA0000, 0xA0000, 0xB0000, 0xB8000 };
+static CONST DWORD MemorySize[] = { 0x20000, 0x10000,  0x8000,  0x8000 };
 
 /*
  * Activate this line if you want to use the real
@@ -281,8 +282,8 @@ static BYTE VgaDacRegisters[VGA_PALETTE_SIZE];
 
 // static VGA_REGISTERS VgaRegisters;
 
-static BOOLEAN InVerticalRetrace   = FALSE;
-static BOOLEAN InHorizontalRetrace = FALSE;
+static ULONGLONG VerticalRetraceCycle = 0ULL;
+static ULONGLONG HorizontalRetraceCycle = 0ULL;
 
 static BOOLEAN NeedsUpdate = FALSE;
 static BOOLEAN ModeChanged = FALSE;
@@ -356,26 +357,26 @@ __RegisterConsoleVDM(IN DWORD dwRegisterFlags,
         VDMBufferSize = dwVDMBufferSize;
 
         /* HACK: Cache -- to be removed in the real implementation */
-        CharBuff = HeapAlloc(GetProcessHeap(),
-                             HEAP_ZERO_MEMORY,
-                             VDMBufferSize.X * VDMBufferSize.Y
-                                             * sizeof(CHAR_INFO));
+        CharBuff = RtlAllocateHeap(RtlGetProcessHeap(),
+                                   HEAP_ZERO_MEMORY,
+                                   VDMBufferSize.X * VDMBufferSize.Y
+                                                   * sizeof(*CharBuff));
         ASSERT(CharBuff);
 
-        VDMBuffer = HeapAlloc(GetProcessHeap(),
-                              HEAP_ZERO_MEMORY,
-                              VDMBufferSize.X * VDMBufferSize.Y
-                                              * sizeof(CHAR_CELL));
+        VDMBuffer = RtlAllocateHeap(RtlGetProcessHeap(),
+                                    HEAP_ZERO_MEMORY,
+                                    VDMBufferSize.X * VDMBufferSize.Y
+                                                    * sizeof(*VDMBuffer));
         *lpVDMBuffer = VDMBuffer;
         return (VDMBuffer != NULL);
     }
     else
     {
         /* HACK: Cache -- to be removed in the real implementation */
-        if (CharBuff) HeapFree(GetProcessHeap(), 0, CharBuff);
+        if (CharBuff) RtlFreeHeap(RtlGetProcessHeap(), 0, CharBuff);
         CharBuff = NULL;
 
-        if (VDMBuffer) HeapFree(GetProcessHeap(), 0, VDMBuffer);
+        if (VDMBuffer) RtlFreeHeap(RtlGetProcessHeap(), 0, VDMBuffer);
         VDMBuffer = NULL;
 
         VDMBufferSize.X = VDMBufferSize.Y = 0;
@@ -428,11 +429,6 @@ static inline DWORD VgaGetVideoBaseAddress(VOID)
     return MemoryBase[(VgaGcRegisters[VGA_GC_MISC_REG] >> 2) & 0x03];
 }
 
-static inline DWORD VgaGetVideoLimitAddress(VOID)
-{
-    return MemoryLimit[(VgaGcRegisters[VGA_GC_MISC_REG] >> 2) & 0x03];
-}
-
 static VOID VgaUpdateCursorPosition(VOID)
 {
     /*
@@ -500,10 +496,10 @@ static BOOL VgaAttachToConsoleInternal(PCOORD Resolution)
     }
 
 #ifdef USE_REAL_REGISTERCONSOLEVDM
-    CharBuff = HeapAlloc(GetProcessHeap(),
-                         HEAP_ZERO_MEMORY,
-                         TextResolution.X * TextResolution.Y
-                                          * sizeof(CHAR_INFO));
+    CharBuff = RtlAllocateHeap(RtlGetProcessHeap(),
+                               HEAP_ZERO_MEMORY,
+                               TextResolution.X * TextResolution.Y
+                                                * sizeof(*CharBuff));
     ASSERT(CharBuff);
 #endif
 
@@ -568,7 +564,7 @@ static BOOL VgaAttachToConsoleInternal(PCOORD Resolution)
     }
 
 #ifdef USE_REAL_REGISTERCONSOLEVDM
-    if (CharBuff) HeapFree(GetProcessHeap(), 0, CharBuff);
+    if (CharBuff) RtlFreeHeap(RtlGetProcessHeap(), 0, CharBuff);
 #endif
 
     VgaUpdateCursorPosition();
@@ -621,7 +617,7 @@ static inline DWORD VgaGetAddressSize(VOID)
 
 static inline DWORD VgaTranslateReadAddress(DWORD Address)
 {
-    DWORD Offset = Address - VgaGetVideoBaseAddress();
+    DWORD Offset = LOWORD(Address - VgaGetVideoBaseAddress());
     BYTE Plane;
 
     /* Check for chain-4 and odd-even mode */
@@ -629,13 +625,13 @@ static inline DWORD VgaTranslateReadAddress(DWORD Address)
     {
         /* The lowest two bits are the plane number */
         Plane = Offset & 0x03;
-        Offset >>= 2;
+        Offset &= ~3;
     }
     else if (VgaGcRegisters[VGA_GC_MODE_REG] & VGA_GC_MODE_OE)
     {
         /* The LSB is the plane number */
         Plane = Offset & 0x01;
-        Offset >>= 1;
+        Offset &= ~1;
     }
     else
     {
@@ -643,31 +639,27 @@ static inline DWORD VgaTranslateReadAddress(DWORD Address)
         Plane = VgaGcRegisters[VGA_GC_READ_MAP_SEL_REG] & 0x03;
     }
 
-    /* Multiply the offset by the address size */
-    Offset *= VgaGetAddressSize();
-    
-    return Offset + Plane * VGA_BANK_SIZE;
+    /* Return the offset on plane 0 for read mode 1 */
+    if (VgaGcRegisters[VGA_GC_MODE_REG] & VGA_GC_MODE_READ) return Offset;
+    else return Offset + Plane * VGA_BANK_SIZE;
 }
 
 static inline DWORD VgaTranslateWriteAddress(DWORD Address)
 {
-    DWORD Offset = Address - VgaGetVideoBaseAddress();
+    DWORD Offset = LOWORD(Address - VgaGetVideoBaseAddress());
 
     /* 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;
+        /* Clear the lowest two bits since they're used to select the bank */
+        Offset &= ~3;
     }
     else if (VgaGcRegisters[VGA_GC_MODE_REG] & VGA_GC_MODE_OE)
     {
-        /* Shift the offset to the right by 1 */
-        Offset >>= 1;
+        /* Clear the lowest bit since it's used to select odd/even */
+        Offset &= ~1;
     }
 
-    /* Multiply the offset by the address size */
-    Offset *= VgaGetAddressSize();
-
     /* Return the offset on plane 0 */
     return Offset;
 }
@@ -774,19 +766,19 @@ static VOID VgaRestoreDefaultPalette(PPALETTEENTRY Entries, USHORT NumOfEntries)
 
 static BOOLEAN VgaInitializePalette(VOID)
 {
-    INT i;
+    UINT i;
     BOOLEAN Result = FALSE;
     LPLOGPALETTE Palette, TextPalette;
 
     /* Allocate storage space for the palettes */
-    Palette = (LPLOGPALETTE)HeapAlloc(GetProcessHeap(),
-                                      HEAP_ZERO_MEMORY,
-                                      sizeof(LOGPALETTE) +
-                                      VGA_MAX_COLORS * sizeof(PALETTEENTRY));
-    TextPalette = (LPLOGPALETTE)HeapAlloc(GetProcessHeap(),
-                                          HEAP_ZERO_MEMORY,
-                                          sizeof(LOGPALETTE) + 
-                                          (VGA_AC_PAL_F_REG + 1) * sizeof(PALETTEENTRY));
+    Palette = RtlAllocateHeap(RtlGetProcessHeap(),
+                              HEAP_ZERO_MEMORY,
+                              sizeof(LOGPALETTE) +
+                                  VGA_MAX_COLORS * sizeof(PALETTEENTRY));
+    TextPalette = RtlAllocateHeap(RtlGetProcessHeap(),
+                                  HEAP_ZERO_MEMORY,
+                                  sizeof(LOGPALETTE) + 
+                                      (VGA_AC_PAL_F_REG + 1) * sizeof(PALETTEENTRY));
     if ((Palette == NULL) || (TextPalette == NULL)) goto Cleanup;
 
     /* Initialize the palettes */
@@ -819,8 +811,8 @@ static BOOLEAN VgaInitializePalette(VOID)
 
 Cleanup:
     /* Free the palettes */
-    if (Palette) HeapFree(GetProcessHeap(), 0, Palette);
-    if (TextPalette) HeapFree(GetProcessHeap(), 0, TextPalette);
+    if (Palette) RtlFreeHeap(RtlGetProcessHeap(), 0, Palette);
+    if (TextPalette) RtlFreeHeap(RtlGetProcessHeap(), 0, TextPalette);
 
     if (!Result)
     {
@@ -1062,9 +1054,12 @@ static VOID VgaUpdateFramebuffer(VOID)
 {
     SHORT i, j, k;
     DWORD AddressSize = VgaGetAddressSize();
-    DWORD Address = MAKEWORD(VgaCrtcRegisters[VGA_CRTC_START_ADDR_LOW_REG],
-                             VgaCrtcRegisters[VGA_CRTC_START_ADDR_HIGH_REG]);
     DWORD ScanlineSize = (DWORD)VgaCrtcRegisters[VGA_CRTC_OFFSET_REG] * 2;
+    BYTE PresetRowScan = VgaCrtcRegisters[VGA_CRTC_PRESET_ROW_SCAN_REG] & 0x1F;
+    DWORD Address = MAKEWORD(VgaCrtcRegisters[VGA_CRTC_START_ADDR_LOW_REG],
+                             VgaCrtcRegisters[VGA_CRTC_START_ADDR_HIGH_REG])
+                    + PresetRowScan * ScanlineSize
+                    + ((VgaCrtcRegisters[VGA_CRTC_PRESET_ROW_SCAN_REG] >> 5) & 3);
 
     /*
      * If console framebuffer is NULL, that means something went wrong
@@ -1078,6 +1073,7 @@ static VOID VgaUpdateFramebuffer(VOID)
         /* Graphics mode */
         PBYTE GraphicsBuffer = (PBYTE)ConsoleFramebuffer;
         DWORD InterlaceHighBit = VGA_INTERLACE_HIGH_BIT;
+        SHORT X;
 
         /*
          * Synchronize access to the graphics framebuffer
@@ -1105,6 +1101,16 @@ static VOID VgaUpdateFramebuffer(VOID)
             {
                 BYTE PixelData = 0;
 
+                /* Apply horizontal pixel panning */
+                if (VgaAcRegisters[VGA_AC_CONTROL_REG] & VGA_AC_CONTROL_8BIT)
+                {
+                    X = j + ((VgaAcRegisters[VGA_AC_HORZ_PANNING_REG] & 0x0F) >> 1);
+                }
+                else
+                {
+                    X = j + (VgaAcRegisters[VGA_AC_HORZ_PANNING_REG] & 0x0F);
+                }
+
                 /* Check the shifting mode */
                 if (VgaGcRegisters[VGA_GC_MODE_REG] & VGA_GC_MODE_SHIFT256)
                 {
@@ -1114,20 +1120,20 @@ static VOID VgaUpdateFramebuffer(VOID)
                     if (VgaAcRegisters[VGA_AC_CONTROL_REG] & VGA_AC_CONTROL_8BIT)
                     {
                         /* One byte per pixel */
-                        PixelData = VgaMemory[(j % VGA_NUM_BANKS) * VGA_BANK_SIZE
-                                              + LOWORD((Address + (j / VGA_NUM_BANKS))
+                        PixelData = VgaMemory[(X % VGA_NUM_BANKS) * VGA_BANK_SIZE
+                                              + LOWORD((Address + (X / VGA_NUM_BANKS))
                                               * AddressSize)];
                     }
                     else
                     {
                         /* 4-bits per pixel */
 
-                        PixelData = VgaMemory[(j % VGA_NUM_BANKS) * VGA_BANK_SIZE
-                                              + LOWORD((Address + (j / (VGA_NUM_BANKS * 2)))
+                        PixelData = VgaMemory[(X % VGA_NUM_BANKS) * VGA_BANK_SIZE
+                                              + LOWORD((Address + (X / (VGA_NUM_BANKS * 2)))
                                               * AddressSize)];
 
                         /* Check if we should use the highest 4 bits or lowest 4 */
-                        if (((j / VGA_NUM_BANKS) % 2) == 0)
+                        if (((X / VGA_NUM_BANKS) % 2) == 0)
                         {
                             /* Highest 4 */
                             PixelData >>= 4;
@@ -1153,14 +1159,14 @@ static VOID VgaUpdateFramebuffer(VOID)
                          * 2 bits shifted from plane 0 and 2 for the first 4 pixels,
                          * then 2 bits shifted from plane 1 and 3 for the next 4
                          */
-                        DWORD BankNumber = (j / 4) % 2;
-                        DWORD Offset = Address + (j / 8);
+                        DWORD BankNumber = (X / 4) % 2;
+                        DWORD Offset = Address + (X / 8);
                         BYTE LowPlaneData = VgaMemory[BankNumber * VGA_BANK_SIZE + LOWORD(Offset * AddressSize)];
                         BYTE HighPlaneData = VgaMemory[(BankNumber + 2) * VGA_BANK_SIZE + LOWORD(Offset * AddressSize)];
 
                         /* Extract the two bits from each plane */
-                        LowPlaneData  = (LowPlaneData  >> (6 - ((j % 4) * 2))) & 0x03;
-                        HighPlaneData = (HighPlaneData >> (6 - ((j % 4) * 2))) & 0x03;
+                        LowPlaneData  = (LowPlaneData  >> (6 - ((X % 4) * 2))) & 0x03;
+                        HighPlaneData = (HighPlaneData >> (6 - ((X % 4) * 2))) & 0x03;
 
                         /* Combine them into the pixel */
                         PixelData = LowPlaneData | (HighPlaneData << 2);
@@ -1179,11 +1185,11 @@ static VOID VgaUpdateFramebuffer(VOID)
                         {
                             /* The data is on plane k, 4 pixels per byte */
                             BYTE PlaneData = VgaMemory[k * VGA_BANK_SIZE
-                                                       + LOWORD((Address + (j / VGA_NUM_BANKS))
+                                                       + LOWORD((Address + (X / VGA_NUM_BANKS))
                                                        * AddressSize)];
 
                             /* The mask of the first bit in the pair */
-                            BYTE BitMask = 1 << (((3 - (j % VGA_NUM_BANKS)) * 2) + 1);
+                            BYTE BitMask = 1 << (((3 - (X % VGA_NUM_BANKS)) * 2) + 1);
 
                             /* Bits 0, 1, 2 and 3 come from the first bit of the pair */
                             if (PlaneData & BitMask) PixelData |= 1 << k;
@@ -1199,11 +1205,11 @@ static VOID VgaUpdateFramebuffer(VOID)
                         for (k = 0; k < VGA_NUM_BANKS; k++)
                         {
                             BYTE PlaneData = VgaMemory[k * VGA_BANK_SIZE
-                                                       + LOWORD((Address + (j / (VGA_NUM_BANKS * 2)))
+                                                       + LOWORD((Address + (X / (VGA_NUM_BANKS * 2)))
                                                        * AddressSize)];
 
                             /* If the bit on that plane is set, set it */
-                            if (PlaneData & (1 << (7 - (j % 8)))) PixelData |= 1 << k;
+                            if (PlaneData & (1 << (7 - (X % 8)))) PixelData |= 1 << k;
                         }
                     }
                 }
@@ -1301,6 +1307,12 @@ static VOID VgaUpdateFramebuffer(VOID)
         PCHAR_CELL CharBuffer = (PCHAR_CELL)ConsoleFramebuffer;
         CHAR_CELL CharInfo;
 
+        /*
+         * Technically, the horizontal panning and preset row count should
+         * affect text mode too. However, it works on pixels and not characters,
+         * so we can't support it currently.
+         */
+
         /* Loop through the scanlines */
         for (i = 0; i < CurrResolution.Y; i++)
         {
@@ -1396,18 +1408,44 @@ static BYTE WINAPI VgaReadPort(USHORT Port)
         case VGA_INSTAT1_READ_COLOR:
         {
             BYTE Result = 0;
+            BOOLEAN Vsync, Hsync;
+            ULONGLONG Cycles = GetCycleCount();
+            ULONG CyclesPerMicrosecond = (ULONG)((GetCycleSpeed() + 500000ULL) / 1000000ULL);
+            ULONG Dots = (VgaSeqRegisters[VGA_SEQ_CLOCK_REG] & 1) ? 9 : 8;
+            ULONG Clock = ((VgaMiscRegister >> 2) & 1) ? 28 : 25;
+            ULONG HorizTotalDots = ((ULONG)VgaCrtcRegisters[VGA_CRTC_HORZ_TOTAL_REG] + 5) * Dots;
+            ULONG VblankStart, VblankEnd, HblankStart, HblankEnd;
+            ULONG HblankDuration, VblankDuration;
+
+            /* Calculate the vertical blanking duration in cycles */
+            VblankStart = VgaCrtcRegisters[VGA_CRTC_START_VERT_BLANKING_REG] & 0x7F;
+            VblankEnd = VgaCrtcRegisters[VGA_CRTC_END_VERT_BLANKING_REG] & 0x7F;
+            if (VblankEnd < VblankStart) VblankEnd |= 0x80;
+            VblankDuration = ((VblankEnd - VblankStart) * HorizTotalDots
+                             * CyclesPerMicrosecond + (Clock >> 1)) / Clock;
+
+            /* Calculate the horizontal blanking duration in cycles */
+            HblankStart = VgaCrtcRegisters[VGA_CRTC_START_HORZ_BLANKING_REG] & 0x1F;
+            HblankEnd = VgaCrtcRegisters[VGA_CRTC_END_HORZ_BLANKING_REG] & 0x1F;
+            if (HblankEnd < HblankStart) HblankEnd |= 0x20;
+            HblankDuration = ((HblankEnd - HblankStart) * Dots
+                             * CyclesPerMicrosecond + (Clock >> 1)) / Clock;
+
+            Vsync = (Cycles - VerticalRetraceCycle) < (ULONGLONG)VblankDuration;
+            Hsync = (Cycles - HorizontalRetraceCycle) < (ULONGLONG)HblankDuration;
 
             /* Reset the AC latch */
             VgaAcLatch = FALSE;
 
+            /* Reverse the polarity, if needed */
+            if (VgaMiscRegister & VGA_MISC_VSYNCP) Vsync = !Vsync;
+            if (VgaMiscRegister & VGA_MISC_HSYNCP) Hsync = !Hsync;
+
             /* Set a flag if there is a vertical or horizontal retrace */
-            if (InVerticalRetrace || InHorizontalRetrace) Result |= VGA_STAT_DD;
+            if (Vsync || Hsync) 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;
+            if (Vsync) Result |= VGA_STAT_VRETRACE;
 
             return Result;
         }
@@ -1492,6 +1530,20 @@ static inline VOID VgaWriteGc(BYTE Data)
     {
         case VGA_GC_MISC_REG:
         {
+            /* Remove any existing VGA memory hook */
+            MemRemoveFastMemoryHook((PVOID)0xA0000, 0x20000);
+
+            if (VgaMiscRegister & VGA_MISC_RAM_ENABLED)
+            {
+                UCHAR MemoryMap = (VgaGcRegisters[VGA_GC_MISC_REG] >> 2) & 0x03;
+
+                /* Register a memory hook */
+                MemInstallFastMemoryHook((PVOID)MemoryBase[MemoryMap],
+                                         MemorySize[MemoryMap],
+                                         VgaReadMemory,
+                                         VgaWriteMemory);
+            }
+
             /* The GC misc register decides if it's text or graphics mode */
             ModeChanged = TRUE;
             break;
@@ -1533,7 +1585,7 @@ static inline VOID VgaWriteCrtc(BYTE Data)
 
 static inline VOID VgaWriteDac(BYTE Data)
 {
-    INT i, PaletteIndex;
+    UINT i, PaletteIndex;
     PALETTEENTRY Entry;
 
     /* Set the value */
@@ -1644,7 +1696,20 @@ static VOID WINAPI VgaWritePort(USHORT Port, BYTE Data)
                 UnregisterIoPort(0x3DA);    // VGA_INSTAT1_READ_COLOR, VGA_FEATURE_WRITE_COLOR
             }
 
-            // if (VgaMiscRegister & 0x02) { /* Enable RAM access */ } else { /* Disable RAM access */ }
+            /* Remove any existing VGA memory hook */
+            MemRemoveFastMemoryHook((PVOID)0xA0000, 0x20000);
+
+            if (VgaMiscRegister & VGA_MISC_RAM_ENABLED)
+            {
+                UCHAR MemoryMap = (VgaGcRegisters[VGA_GC_MISC_REG] >> 2) & 0x03;
+
+                /* Register a memory hook */
+                MemInstallFastMemoryHook((PVOID)MemoryBase[MemoryMap],
+                                         MemorySize[MemoryMap],
+                                         VgaReadMemory,
+                                         VgaWriteMemory);
+            }
+
             break;
         }
 
@@ -1764,8 +1829,8 @@ static VOID FASTCALL VgaVerticalRetrace(ULONGLONG ElapsedTime)
 
     UNREFERENCED_PARAMETER(ElapsedTime);
 
-    /* Set the vertical retrace flag */
-    InVerticalRetrace = TRUE;
+    /* Set the vertical retrace cycle */
+    VerticalRetraceCycle = GetCycleCount();
 
     /* If nothing has changed, just return */
     // if (!ModeChanged && !CursorChanged && !PaletteChanged && !NeedsUpdate)
@@ -1836,8 +1901,8 @@ static VOID FASTCALL VgaHorizontalRetrace(ULONGLONG ElapsedTime)
 {
     UNREFERENCED_PARAMETER(ElapsedTime);
 
-    /* Set the flag */
-    InHorizontalRetrace = TRUE;
+    /* Set the cycle */
+    HorizontalRetraceCycle = GetCycleCount();
 }
 
 /* PUBLIC FUNCTIONS ***********************************************************/
@@ -1903,26 +1968,58 @@ VOID VgaRefreshDisplay(VOID)
     VgaVerticalRetrace(0);
 }
 
-VOID NTAPI VgaReadMemory(ULONG Address, PVOID Buffer, ULONG Size)
+VOID FASTCALL VgaReadMemory(ULONG Address, PVOID Buffer, ULONG Size)
 {
-    DWORD i;
+    DWORD i, j;
     DWORD VideoAddress;
     PUCHAR BufPtr = (PUCHAR)Buffer;
 
     DPRINT("VgaReadMemory: Address 0x%08X, Size %lu\n", Address, Size);
-    Address = max(min(Address, VgaGetVideoLimitAddress() - 1), VgaGetVideoBaseAddress());
-    Size = min(Size, VgaGetVideoLimitAddress() - Address + 1);
 
     /* Ignore if video RAM access is disabled */
     if ((VgaMiscRegister & VGA_MISC_RAM_ENABLED) == 0) return;
 
-    /* Loop through each byte */
-    for (i = 0; i < Size; i++)
+    if (!(VgaGcRegisters[VGA_GC_MODE_REG] & VGA_GC_MODE_READ))
+    {
+        /* Loop through each byte */
+        for (i = 0; i < Size; i++)
+        {
+            VideoAddress = VgaTranslateReadAddress(Address + i);
+    
+            /* Copy the value to the buffer */
+            BufPtr[i] = VgaMemory[VideoAddress];
+        }
+    }
+    else
     {
-        VideoAddress = VgaTranslateReadAddress(Address + i);
+        /* Loop through each byte */
+        for (i = 0; i < Size; i++)
+        {
+            BYTE Result = 0xFF;
+
+            /* This should always return a plane 0 address for read mode 1 */
+            VideoAddress = VgaTranslateReadAddress(Address + i);
+
+            for (j = 0; j < VGA_NUM_BANKS; j++)
+            {
+                /* Don't consider ignored banks */
+                if (!(VgaGcRegisters[VGA_GC_COLOR_IGNORE_REG] & (1 << j))) continue;
 
-        /* Copy the value to the buffer */
-        BufPtr[i] = VgaMemory[VideoAddress];
+                if (VgaGcRegisters[VGA_GC_COLOR_COMPARE_REG] & (1 << j))
+                {
+                    /* Comparing with 11111111 */
+                    Result &= VgaMemory[j * VGA_BANK_SIZE + LOWORD(VideoAddress)];
+                }
+                else
+                {
+                    /* Comparing with 00000000 */
+                    Result &= ~(VgaMemory[j * VGA_BANK_SIZE + LOWORD(VideoAddress)]);
+                }
+            }
+
+            /* Copy the value to the buffer */
+            BufPtr[i] = Result;
+        }
     }
 
     /* Load the latch registers */
@@ -1932,15 +2029,13 @@ VOID NTAPI VgaReadMemory(ULONG Address, PVOID Buffer, ULONG Size)
     VgaLatchRegisters[3] = VgaMemory[(3 * VGA_BANK_SIZE) + LOWORD(VideoAddress)];
 }
 
-BOOLEAN NTAPI VgaWriteMemory(ULONG Address, PVOID Buffer, ULONG Size)
+BOOLEAN FASTCALL VgaWriteMemory(ULONG Address, PVOID Buffer, ULONG Size)
 {
     DWORD i, j;
     DWORD VideoAddress;
     PUCHAR BufPtr = (PUCHAR)Buffer;
 
     DPRINT("VgaWriteMemory: Address 0x%08X, Size %lu\n", Address, Size);
-    Address = max(min(Address, VgaGetVideoLimitAddress() - 1), VgaGetVideoBaseAddress());
-    Size = min(Size, VgaGetVideoLimitAddress() - Address + 1);
 
     /* Ignore if video RAM access is disabled */
     if ((VgaMiscRegister & VGA_MISC_RAM_ENABLED) == 0) return TRUE;
@@ -2104,9 +2199,6 @@ BOOLEAN VgaInitialize(HANDLE TextHandle)
 
     /* Clear the VGA memory */
     VgaClearMemory();
-    
-    /* Register the memory hook */
-    MemInstallFastMemoryHook((PVOID)0xA0000, 0x20000, VgaReadMemory, VgaWriteMemory);
 
     /* Register the I/O Ports */
     RegisterIoPort(0x3CC, VgaReadPort, NULL);           // VGA_MISC_READ
@@ -2127,8 +2219,8 @@ BOOLEAN VgaInitialize(HANDLE TextHandle)
     RegisterIoPort(0x3D8, VgaReadPort, VgaWritePort);   // CGA_MODE_CTRL_REG
     RegisterIoPort(0x3D9, VgaReadPort, VgaWritePort);   // CGA_PAL_CTRL_REG
 
-    HSyncTimer = CreateHardwareTimer(HARDWARE_TIMER_ENABLED, 0, VgaHorizontalRetrace);
-    VSyncTimer = CreateHardwareTimer(HARDWARE_TIMER_ENABLED, 16, VgaVerticalRetrace);
+    HSyncTimer = CreateHardwareTimer(HARDWARE_TIMER_ENABLED, HZ_TO_NS(31469), VgaHorizontalRetrace);
+    VSyncTimer = CreateHardwareTimer(HARDWARE_TIMER_ENABLED, HZ_TO_NS(60), VgaVerticalRetrace);
 
     /* Return success */
     return TRUE;