[NTVDM]
[reactos.git] / reactos / subsystems / mvdm / ntvdm / hardware / video / vga.c
index 687bb0d..c3e0c19 100644 (file)
 
 #define NDEBUG
 
+#include "ntvdm.h"
 #include "emulator.h"
 #include "vga.h"
 #include <bios/vidbios.h>
 
+#include "memory.h"
 #include "io.h"
+#include "clock.h"
 
 /* PRIVATE VARIABLES **********************************************************/
 
 static CONST DWORD MemoryBase[]  = { 0xA0000, 0xA0000, 0xB0000, 0xB8000 };
-static CONST DWORD MemoryLimit[] = { 0xAFFFF, 0xAFFFF, 0xB7FFF, 0xBFFFF };
+static CONST DWORD MemoryLimit[] = { 0xBFFFF, 0xAFFFF, 0xB7FFF, 0xBFFFF };
 
 /*
  * Activate this line if you want to use the real
@@ -244,6 +247,9 @@ static HANDLE ConsoleMutex = NULL;
 static BOOLEAN DoubleWidth  = FALSE;
 static BOOLEAN DoubleHeight = FALSE;
 
+static PHARDWARE_TIMER VSyncTimer;
+static PHARDWARE_TIMER HSyncTimer;
+
 /*
  * VGA Hardware
  */
@@ -351,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;
@@ -418,6 +424,16 @@ __InvalidateConsoleDIBits(IN HANDLE hConsoleOutput,
 static inline DWORD VgaGetAddressSize(VOID);
 static VOID VgaUpdateTextCursor(VOID);
 
+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)
 {
     /*
@@ -485,10 +501,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
 
@@ -553,7 +569,7 @@ static BOOL VgaAttachToConsoleInternal(PCOORD Resolution)
     }
 
 #ifdef USE_REAL_REGISTERCONSOLEVDM
-    if (CharBuff) HeapFree(GetProcessHeap(), 0, CharBuff);
+    if (CharBuff) RtlFreeHeap(RtlGetProcessHeap(), 0, CharBuff);
 #endif
 
     VgaUpdateCursorPosition();
@@ -606,7 +622,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 */
@@ -614,13 +630,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
     {
@@ -628,31 +644,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;
 }
@@ -764,14 +776,14 @@ static BOOLEAN VgaInitializePalette(VOID)
     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 */
@@ -804,8 +816,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)
     {
@@ -1743,70 +1755,12 @@ static VOID WINAPI VgaWritePort(USHORT Port, BYTE Data)
     }
 }
 
-/* PUBLIC FUNCTIONS ***********************************************************/
-
-DWORD VgaGetVideoBaseAddress(VOID)
-{
-    return MemoryBase[(VgaGcRegisters[VGA_GC_MISC_REG] >> 2) & 0x03];
-}
-
-DWORD VgaGetVideoLimitAddress(VOID)
-{
-    return MemoryLimit[(VgaGcRegisters[VGA_GC_MISC_REG] >> 2) & 0x03];
-}
-
-COORD VgaGetDisplayResolution(VOID)
-{
-    COORD Resolution;
-    BYTE MaximumScanLine = 1 + (VgaCrtcRegisters[VGA_CRTC_MAX_SCAN_LINE_REG] & 0x1F);
-
-    /* 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;
-    }
-
-    if (VgaCrtcRegisters[VGA_CRTC_MAX_SCAN_LINE_REG] & VGA_CRTC_MAXSCANLINE_DOUBLE)
-    {
-        /* Halve the vertical resolution */
-        Resolution.Y >>= 1;
-    }
-    else
-    {
-        /* Divide the vertical resolution by the maximum scan line (== font size in text mode) */
-        Resolution.Y /= MaximumScanLine;
-    }
-
-    /* Return the resolution */
-    return Resolution;
-}
-
-VOID VgaRefreshDisplay(VOID)
+static VOID FASTCALL VgaVerticalRetrace(ULONGLONG ElapsedTime)
 {
     HANDLE ConsoleBufferHandle = NULL;
 
+    UNREFERENCED_PARAMETER(ElapsedTime);
+
     /* Set the vertical retrace flag */
     InVerticalRetrace = TRUE;
 
@@ -1875,29 +1829,129 @@ VOID VgaRefreshDisplay(VOID)
     NeedsUpdate = FALSE;
 }
 
-VOID VgaHorizontalRetrace(VOID)
+static VOID FASTCALL VgaHorizontalRetrace(ULONGLONG ElapsedTime)
 {
+    UNREFERENCED_PARAMETER(ElapsedTime);
+
     /* Set the flag */
     InHorizontalRetrace = TRUE;
 }
 
-VOID VgaReadMemory(DWORD Address, LPBYTE Buffer, DWORD Size)
+/* PUBLIC FUNCTIONS ***********************************************************/
+
+COORD VgaGetDisplayResolution(VOID)
 {
-    DWORD i;
+    COORD Resolution;
+    BYTE MaximumScanLine = 1 + (VgaCrtcRegisters[VGA_CRTC_MAX_SCAN_LINE_REG] & 0x1F);
+
+    /* 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;
+    }
+
+    if (VgaCrtcRegisters[VGA_CRTC_MAX_SCAN_LINE_REG] & VGA_CRTC_MAXSCANLINE_DOUBLE)
+    {
+        /* Halve the vertical resolution */
+        Resolution.Y >>= 1;
+    }
+    else
+    {
+        /* Divide the vertical resolution by the maximum scan line (== font size in text mode) */
+        Resolution.Y /= MaximumScanLine;
+    }
+
+    /* Return the resolution */
+    return Resolution;
+}
+
+BOOLEAN VgaGetDoubleVisionState(PBOOLEAN Horizontal, PBOOLEAN Vertical)
+{
+    if (GraphicsConsoleBuffer == NULL) return FALSE;
+    if (Horizontal) *Horizontal = DoubleWidth;
+    if (Vertical)   *Vertical   = DoubleHeight;
+    return TRUE;
+}
+
+VOID VgaRefreshDisplay(VOID)
+{
+    VgaVerticalRetrace(0);
+}
+
+VOID NTAPI VgaReadMemory(ULONG Address, PVOID Buffer, ULONG Size)
+{
+    DWORD i, j;
     DWORD VideoAddress;
+    PUCHAR BufPtr = (PUCHAR)Buffer;
 
     DPRINT("VgaReadMemory: Address 0x%08X, Size %lu\n", Address, Size);
 
     /* 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;
+
+                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 */
-        Buffer[i] = VgaMemory[VideoAddress];
+            /* Copy the value to the buffer */
+            BufPtr[i] = Result;
+        }
     }
 
     /* Load the latch registers */
@@ -1907,18 +1961,19 @@ VOID VgaReadMemory(DWORD Address, LPBYTE Buffer, DWORD Size)
     VgaLatchRegisters[3] = VgaMemory[(3 * VGA_BANK_SIZE) + LOWORD(VideoAddress)];
 }
 
-VOID VgaWriteMemory(DWORD Address, LPBYTE Buffer, DWORD Size)
+BOOLEAN NTAPI 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);
 
     /* Ignore if video RAM access is disabled */
-    if ((VgaMiscRegister & VGA_MISC_RAM_ENABLED) == 0) return;
+    if ((VgaMiscRegister & VGA_MISC_RAM_ENABLED) == 0) return TRUE;
 
     /* Also ignore if write access to all planes is disabled */
-    if ((VgaSeqRegisters[VGA_SEQ_MASK_REG] & 0x0F) == 0x00) return;
+    if ((VgaSeqRegisters[VGA_SEQ_MASK_REG] & 0x0F) == 0x00) return TRUE;
 
     /* Loop through each byte */
     for (i = 0; i < Size; i++)
@@ -1951,9 +2006,11 @@ VOID VgaWriteMemory(DWORD Address, LPBYTE Buffer, DWORD Size)
             }
 
             /* Copy the value to the VGA memory */
-            VgaMemory[VideoAddress + j * VGA_BANK_SIZE] = VgaTranslateByteForWriting(Buffer[i], j);
+            VgaMemory[VideoAddress + j * VGA_BANK_SIZE] = VgaTranslateByteForWriting(BufPtr[i], j);
         }
     }
+
+    return TRUE;
 }
 
 VOID VgaClearMemory(VOID)
@@ -2074,6 +2131,9 @@ 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
@@ -2094,12 +2154,18 @@ 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, HZ_TO_NS(31469), VgaHorizontalRetrace);
+    VSyncTimer = CreateHardwareTimer(HARDWARE_TIMER_ENABLED, HZ_TO_NS(60), VgaVerticalRetrace);
+
     /* Return success */
     return TRUE;
 }
 
 VOID VgaCleanup(VOID)
 {
+    DestroyHardwareTimer(VSyncTimer);
+    DestroyHardwareTimer(HSyncTimer);
+
     if (ScreenMode == GRAPHICS_MODE)
     {
         /* Leave the current graphics mode */
@@ -2112,6 +2178,7 @@ VOID VgaCleanup(VOID)
     }
 
     VgaDetachFromConsole(FALSE);
+    MemRemoveFastMemoryHook((PVOID)0xA0000, 0x20000);
 
     CloseHandle(AnotherEvent);
     CloseHandle(EndEvent);