#include "emulator.h"
#include "vga.h"
+#include "../bios/vidbios.h"
#include "io.h"
static CONST DWORD MemoryBase[] = { 0xA0000, 0xA0000, 0xB0000, 0xB8000 };
static CONST DWORD MemoryLimit[] = { 0xAFFFF, 0xAFFFF, 0xB7FFF, 0xBFFFF };
+/*
+ * Activate this line if you want to use the real
+ * RegisterConsoleVDM API of ReactOS/Windows.
+ */
+// #define USE_REAL_REGISTERCONSOLEVDM
+
#define USE_REACTOS_COLORS
// #define USE_DOSBOX_COLORS
#endif
-static CONSOLE_SCREEN_BUFFER_INFO ConsoleInfo;
+/*
+ * Default 16-color palette for foreground and background
+ * (corresponding flags in comments).
+ * Taken from subsystems/win32/winsrv/consrv/frontends/gui/conwnd.c
+ */
+static const COLORREF ConsoleColors[16] =
+{
+ RGB(0, 0, 0), // (Black)
+ RGB(0, 0, 128), // BLUE
+ RGB(0, 128, 0), // GREEN
+ RGB(0, 128, 128), // BLUE | GREEN
+ RGB(128, 0, 0), // RED
+ RGB(128, 0, 128), // BLUE | RED
+ RGB(128, 128, 0), // GREEN | RED
+ RGB(192, 192, 192), // BLUE | GREEN | RED
+
+ RGB(128, 128, 128), // (Grey) INTENSITY
+ RGB(0, 0, 255), // BLUE | INTENSITY
+ RGB(0, 255, 0), // GREEN | INTENSITY
+ RGB(0, 255, 255), // BLUE | GREEN | INTENSITY
+ RGB(255, 0, 0), // RED | INTENSITY
+ RGB(255, 0, 255), // BLUE | RED | INTENSITY
+ RGB(255, 255, 0), // GREEN | RED | INTENSITY
+ RGB(255, 255, 255) // BLUE | GREEN | RED | INTENSITY
+};
-static BYTE VgaMemory[VGA_NUM_BANKS * VGA_BANK_SIZE];
-static LPVOID ConsoleFramebuffer = NULL;
+/*
+ * Console interface -- VGA-mode-agnostic
+ */
+typedef struct _CHAR_CELL
+{
+ CHAR Char;
+ BYTE Attributes;
+} CHAR_CELL, *PCHAR_CELL;
+C_ASSERT(sizeof(CHAR_CELL) == 2);
+
+static LPVOID ConsoleFramebuffer = NULL; // Active framebuffer, points to
+ // either TextFramebuffer or a valid
+ // graphics framebuffer.
+static HPALETTE TextPaletteHandle = NULL;
+static HPALETTE PaletteHandle = NULL;
+
+static HANDLE StartEvent = NULL;
+static HANDLE EndEvent = NULL;
+static HANDLE AnotherEvent = NULL;
+
+static CONSOLE_CURSOR_INFO OrgConsoleCursorInfo;
+static CONSOLE_SCREEN_BUFFER_INFO OrgConsoleBufferInfo;
+
+
+/*
+ * Text mode -- we always keep a valid text mode framebuffer
+ * even if we are in graphics mode. This is needed in order to
+ * keep a consistent VGA state.
+ */
+static CONSOLE_SCREEN_BUFFER_INFO ConsoleInfo;
+static COORD TextResolution = {0};
+static PCHAR_CELL TextFramebuffer = NULL;
static HANDLE TextConsoleBuffer = NULL;
+
+/* Graphics mode */
static HANDLE GraphicsConsoleBuffer = NULL;
static HANDLE ConsoleMutex = NULL;
-static HPALETTE PaletteHandle = NULL;
-static BOOLEAN DoubleVision = FALSE;
+/* DoubleVision support */
+static BOOLEAN DoubleWidth = FALSE;
+static BOOLEAN DoubleHeight = FALSE;
+
+/*
+ * VGA Hardware
+ */
+static BYTE VgaMemory[VGA_NUM_BANKS * VGA_BANK_SIZE];
static BYTE VgaLatchRegisters[VGA_NUM_BANKS] = {0, 0, 0, 0};
static BOOLEAN InHorizontalRetrace = FALSE;
static BOOLEAN NeedsUpdate = FALSE;
-static BOOLEAN ModeChanged = TRUE;
+static BOOLEAN ModeChanged = FALSE;
static BOOLEAN CursorMoved = FALSE;
static BOOLEAN PaletteChanged = FALSE;
static SMALL_RECT UpdateRectangle = { 0, 0, 0, 0 };
+/* RegisterConsoleVDM EMULATION ***********************************************/
+
+#include <ntddvdeo.h>
+
+#ifdef USE_REAL_REGISTERCONSOLEVDM
+
+#define __RegisterConsoleVDM RegisterConsoleVDM
+#define __InvalidateConsoleDIBits InvalidateConsoleDIBits
+
+#else
+
+/*
+ * This private buffer, per-console, is used by
+ * RegisterConsoleVDM and InvalidateConsoleDIBits.
+ */
+static COORD VDMBufferSize = {0};
+static PCHAR_CELL VDMBuffer = NULL;
+
+static PCHAR_INFO CharBuff = NULL; // This is a hack, which is unneeded
+ // for the real RegisterConsoleVDM and
+ // InvalidateConsoleDIBits
+
+BOOL
+WINAPI
+__RegisterConsoleVDM(IN DWORD dwRegisterFlags,
+ IN HANDLE hStartHardwareEvent,
+ IN HANDLE hEndHardwareEvent,
+ IN HANDLE hErrorHardwareEvent,
+ IN DWORD dwUnusedVar,
+ OUT LPDWORD lpVideoStateLength,
+ OUT PVOID* lpVideoState, // PVIDEO_HARDWARE_STATE_HEADER*
+ IN PVOID lpUnusedBuffer,
+ IN DWORD dwUnusedBufferLength,
+ IN COORD dwVDMBufferSize,
+ OUT PVOID* lpVDMBuffer)
+{
+ UNREFERENCED_PARAMETER(hErrorHardwareEvent);
+ UNREFERENCED_PARAMETER(dwUnusedVar);
+ UNREFERENCED_PARAMETER(lpVideoStateLength);
+ UNREFERENCED_PARAMETER(lpVideoState);
+ UNREFERENCED_PARAMETER(lpUnusedBuffer);
+ UNREFERENCED_PARAMETER(dwUnusedBufferLength);
+
+ SetLastError(0);
+ DPRINT1("__RegisterConsoleVDM(%d)\n", dwRegisterFlags);
+
+ if (lpVDMBuffer == NULL) return FALSE;
+
+ if (dwRegisterFlags != 0)
+ {
+ // if (hStartHardwareEvent == NULL || hEndHardwareEvent == NULL) return FALSE;
+ if (VDMBuffer != NULL) return FALSE;
+
+ VDMBufferSize = dwVDMBufferSize;
+
+ /* HACK: Cache -- to be removed in the real implementation */
+ CharBuff = HeapAlloc(GetProcessHeap(),
+ HEAP_ZERO_MEMORY,
+ VDMBufferSize.X * VDMBufferSize.Y
+ * sizeof(CHAR_INFO));
+ ASSERT(CharBuff);
+
+ VDMBuffer = HeapAlloc(GetProcessHeap(),
+ HEAP_ZERO_MEMORY,
+ VDMBufferSize.X * VDMBufferSize.Y
+ * sizeof(CHAR_CELL));
+ *lpVDMBuffer = VDMBuffer;
+ return (VDMBuffer != NULL);
+ }
+ else
+ {
+ /* HACK: Cache -- to be removed in the real implementation */
+ if (CharBuff) HeapFree(GetProcessHeap(), 0, CharBuff);
+ CharBuff = NULL;
+
+ if (VDMBuffer) HeapFree(GetProcessHeap(), 0, VDMBuffer);
+ VDMBuffer = NULL;
+
+ VDMBufferSize.X = VDMBufferSize.Y = 0;
+
+ return TRUE;
+ }
+}
+
+BOOL
+__InvalidateConsoleDIBits(IN HANDLE hConsoleOutput,
+ IN PSMALL_RECT lpRect)
+{
+ if ((hConsoleOutput == TextConsoleBuffer) && (VDMBuffer != NULL))
+ {
+ /* HACK: Write the cached data to the console */
+
+ COORD Origin = { lpRect->Left, lpRect->Top };
+ SHORT i, j;
+
+ ASSERT(CharBuff);
+
+ for (i = 0; i < VDMBufferSize.Y; i++)
+ {
+ for (j = 0; j < VDMBufferSize.X; j++)
+ {
+ CharBuff[i * VDMBufferSize.X + j].Char.AsciiChar = VDMBuffer[i * VDMBufferSize.X + j].Char;
+ CharBuff[i * VDMBufferSize.X + j].Attributes = VDMBuffer[i * VDMBufferSize.X + j].Attributes;
+ }
+ }
+
+ WriteConsoleOutputA(hConsoleOutput,
+ CharBuff,
+ VDMBufferSize,
+ Origin,
+ lpRect);
+ }
+
+ return InvalidateConsoleDIBits(hConsoleOutput, lpRect);
+}
+
+#endif
+
/* PRIVATE FUNCTIONS **********************************************************/
+static inline DWORD VgaGetAddressSize(VOID);
+static VOID VgaUpdateTextCursor(VOID);
+
+static VOID VgaUpdateCursorPosition(VOID)
+{
+ /*
+ * Update the cursor position in the VGA registers.
+ */
+ WORD Offset = ConsoleInfo.dwCursorPosition.Y * TextResolution.X +
+ ConsoleInfo.dwCursorPosition.X;
+
+ VgaCrtcRegisters[VGA_CRTC_CURSOR_LOC_LOW_REG] = LOBYTE(Offset);
+ VgaCrtcRegisters[VGA_CRTC_CURSOR_LOC_HIGH_REG] = HIBYTE(Offset);
+
+ // VidBiosSyncCursorPosition();
+ VgaUpdateTextCursor();
+}
+
+static BOOL VgaAttachToConsoleInternal(PCOORD Resolution)
+{
+ BOOL Success;
+ ULONG Length = 0;
+ PVIDEO_HARDWARE_STATE_HEADER State;
+
+#ifdef USE_REAL_REGISTERCONSOLEVDM
+ PCHAR_INFO CharBuff = NULL;
+#endif
+ SHORT i, j;
+ DWORD AddressSize, ScanlineSize;
+ DWORD Address = 0;
+ DWORD CurrentAddr;
+ SMALL_RECT ConRect;
+ COORD Origin = { 0, 0 };
+
+ ASSERT(TextFramebuffer == NULL);
+
+ TextResolution = *Resolution;
+
+ /*
+ * Windows 2k3 winsrv.dll calls NtVdmControl(VdmQueryVdmProcess == 14, &ConsoleHandle);
+ * in the two following APIs:
+ * SrvRegisterConsoleVDM (corresponding Win32 API: RegisterConsoleVDM)
+ * SrvVDMConsoleOperation (corresponding Win32 API: )
+ * to check whether the current process is a VDM process, and fails otherwise with the
+ * error 0xC0000022 ().
+ *
+ * It is worth it to notice that also basesrv.dll does the same only for the
+ * BaseSrvIsFirstVDM API...
+ */
+
+ Success =
+ __RegisterConsoleVDM(1,
+ StartEvent,
+ EndEvent,
+ AnotherEvent, // NULL,
+ 0,
+ &Length, // NULL, <-- putting this (and null in the next var) makes the API returning error 12 "ERROR_INVALID_ACCESS"
+ (PVOID*)&State, // NULL,
+ NULL,
+ 0,
+ TextResolution,
+ (PVOID*)&TextFramebuffer);
+ if (!Success)
+ {
+ DisplayMessage(L"RegisterConsoleVDM failed with error %d\n", GetLastError());
+ EmulatorTerminate();
+ return FALSE;
+ }
+
+#ifdef USE_REAL_REGISTERCONSOLEVDM
+ CharBuff = HeapAlloc(GetProcessHeap(),
+ HEAP_ZERO_MEMORY,
+ TextResolution.X * TextResolution.Y
+ * sizeof(CHAR_INFO));
+ ASSERT(CharBuff);
+#endif
+
+ /*
+ * Resize the console
+ */
+ ConRect.Left = 0;
+ ConRect.Top = ConsoleInfo.srWindow.Top;
+ ConRect.Right = ConRect.Left + Resolution->X - 1;
+ ConRect.Bottom = ConRect.Top + Resolution->Y - 1;
+ /*
+ * Use this trick to effectively resize the console buffer and window,
+ * because:
+ * - SetConsoleScreenBufferSize fails if the new console screen buffer size
+ * is smaller than the current console window size, and:
+ * - SetConsoleWindowInfo fails if the new console window size is larger
+ * than the current console screen buffer size.
+ */
+ SetConsoleScreenBufferSize(TextConsoleBuffer, *Resolution);
+ SetConsoleWindowInfo(TextConsoleBuffer, TRUE, &ConRect);
+ SetConsoleScreenBufferSize(TextConsoleBuffer, *Resolution);
+ /* Update the saved console information */
+ GetConsoleScreenBufferInfo(TextConsoleBuffer, &ConsoleInfo);
+
+ /*
+ * Copy console data into VGA memory
+ */
+
+ /* Get the data */
+ AddressSize = VgaGetAddressSize();
+ ConRect.Left = ConRect.Top = 0;
+ ConRect.Right = TextResolution.X;
+ ConRect.Bottom = TextResolution.Y;
+ ScanlineSize = (DWORD)VgaCrtcRegisters[VGA_CRTC_OFFSET_REG] * 2;
+
+ /* Read the data from the console into the framebuffer... */
+ ReadConsoleOutputA(TextConsoleBuffer,
+ CharBuff,
+ TextResolution,
+ Origin,
+ &ConRect);
+
+ /* ... and copy the framebuffer into the VGA memory */
+
+ /* Loop through the scanlines */
+ for (i = 0; i < TextResolution.Y; i++)
+ {
+ /* Loop through the characters */
+ for (j = 0; j < TextResolution.X; j++)
+ {
+ CurrentAddr = LOWORD((Address + j) * AddressSize);
+
+ /* Store the character in plane 0 */
+ VgaMemory[CurrentAddr] = CharBuff[i * TextResolution.X + j].Char.AsciiChar;
+
+ /* Store the attribute in plane 1 */
+ VgaMemory[CurrentAddr + VGA_BANK_SIZE] = (BYTE)CharBuff[i * TextResolution.X + j].Attributes;
+ }
+
+ /* Move to the next scanline */
+ Address += ScanlineSize;
+ }
+
+#ifdef USE_REAL_REGISTERCONSOLEVDM
+ if (CharBuff) HeapFree(GetProcessHeap(), 0, CharBuff);
+#endif
+
+ VgaUpdateCursorPosition();
+
+ return TRUE;
+}
+
+static BOOL IsConsoleHandle(HANDLE hHandle)
+{
+ DWORD dwMode;
+
+ /* Check whether the handle may be that of a console... */
+ if ((GetFileType(hHandle) & ~FILE_TYPE_REMOTE) != FILE_TYPE_CHAR)
+ return FALSE;
+
+ /*
+ * It may be. Perform another test... The idea comes from the
+ * MSDN description of the WriteConsole API:
+ *
+ * "WriteConsole fails if it is used with a standard handle
+ * that is redirected to a file. If an application processes
+ * multilingual output that can be redirected, determine whether
+ * the output handle is a console handle (one method is to call
+ * the GetConsoleMode function and check whether it succeeds).
+ * If the handle is a console handle, call WriteConsole. If the
+ * handle is not a console handle, the output is redirected and
+ * you should call WriteFile to perform the I/O."
+ */
+ return GetConsoleMode(hHandle, &dwMode);
+}
+
static inline DWORD VgaGetAddressSize(VOID)
{
if (VgaCrtcRegisters[VGA_CRTC_UNDERLINE_REG] & VGA_CRTC_UNDERLINE_DWORD)
static VOID VgaWriteDac(BYTE Data)
{
- INT PaletteIndex;
+ INT i, PaletteIndex;
PALETTEENTRY Entry;
/* Set the value */
Entry.peBlue = VGA_DAC_TO_COLOR(VgaDacRegisters[PaletteIndex * 3 + 2]);
Entry.peFlags = 0;
- /* Update the palette entry and set the palette change flag */
+ /* Update the palette entry */
SetPaletteEntries(PaletteHandle, PaletteIndex, 1, &Entry);
+
+ /* Check which text palette entries are affected */
+ for (i = 0; i <= VGA_AC_PAL_F_REG; i++)
+ {
+ if (VgaAcRegisters[i] == PaletteIndex)
+ {
+ /* Update the text palette entry */
+ SetPaletteEntries(TextPaletteHandle, i, 1, &Entry);
+ }
+ }
+
+ /* Set the palette changed flag */
PaletteChanged = TRUE;
/* Update the index */
static VOID VgaWriteAc(BYTE Data)
{
+ PALETTEENTRY Entry;
+
ASSERT(VgaAcIndex < VGA_AC_MAX_REG);
/* Save the value */
// DbgPrint(" AC Palette Writing %d to index %d\n", Data, VgaAcIndex);
if (VgaAcRegisters[VgaAcIndex] != Data)
{
- /* Update the AC register and set the palette change flag */
+ /* Update the AC register */
VgaAcRegisters[VgaAcIndex] = Data;
+
+ /* Fill the entry structure */
+ Entry.peRed = VGA_DAC_TO_COLOR(VgaDacRegisters[Data * 3]);
+ Entry.peGreen = VGA_DAC_TO_COLOR(VgaDacRegisters[Data * 3 + 1]);
+ Entry.peBlue = VGA_DAC_TO_COLOR(VgaDacRegisters[Data * 3 + 2]);
+ Entry.peFlags = 0;
+
+ /* Update the palette entry and set the palette change flag */
+ SetPaletteEntries(TextPaletteHandle, VgaAcIndex, 1, &Entry);
PaletteChanged = TRUE;
}
}
static BOOLEAN VgaInitializePalette(VOID)
{
- LPLOGPALETTE Palette;
+ INT i;
+ BOOLEAN Result = FALSE;
+ LPLOGPALETTE Palette, TextPalette;
- /* Allocate storage space for the palette */
+ /* Allocate storage space for the palettes */
Palette = (LPLOGPALETTE)HeapAlloc(GetProcessHeap(),
HEAP_ZERO_MEMORY,
sizeof(LOGPALETTE) +
- VGA_MAX_COLORS * sizeof(PALETTEENTRY));
- if (Palette == NULL) return FALSE;
-
- /* Initialize the palette */
- Palette->palVersion = 0x0300;
+ VGA_MAX_COLORS * sizeof(PALETTEENTRY));
+ TextPalette = (LPLOGPALETTE)HeapAlloc(GetProcessHeap(),
+ HEAP_ZERO_MEMORY,
+ sizeof(LOGPALETTE) +
+ (VGA_AC_PAL_F_REG + 1) * sizeof(PALETTEENTRY));
+ if ((Palette == NULL) || (TextPalette == NULL)) goto Cleanup;
+
+ /* Initialize the palettes */
+ Palette->palVersion = TextPalette->palVersion = 0x0300;
Palette->palNumEntries = VGA_MAX_COLORS;
+ TextPalette->palNumEntries = VGA_AC_PAL_F_REG + 1;
- /* Restore the default palette */
+ /* Restore the default graphics palette */
VgaRestoreDefaultPalette(Palette->palPalEntry, Palette->palNumEntries);
- /* Create the palette */
+ /* Set the default text palette */
+ for (i = 0; i < TextPalette->palNumEntries; i++)
+ {
+ /* Set the palette entries */
+ TextPalette->palPalEntry[i].peRed = GetRValue(ConsoleColors[i]);
+ TextPalette->palPalEntry[i].peGreen = GetGValue(ConsoleColors[i]);
+ TextPalette->palPalEntry[i].peBlue = GetBValue(ConsoleColors[i]);
+ TextPalette->palPalEntry[i].peFlags = 0;
+ }
+
+ /* Create the palettes */
PaletteHandle = CreatePalette(Palette);
+ TextPaletteHandle = CreatePalette(TextPalette);
- /* Free the palette */
- HeapFree(GetProcessHeap(), 0, Palette);
+ if (PaletteHandle != NULL && TextPaletteHandle != NULL)
+ {
+ /* The palettes have been created successfully */
+ Result = TRUE;
+ }
- /* Fail if the palette wasn't successfully created... */
- if (PaletteHandle == NULL) return FALSE;
+Cleanup:
+ /* Free the palettes */
+ if (Palette) HeapFree(GetProcessHeap(), 0, Palette);
+ if (TextPalette) HeapFree(GetProcessHeap(), 0, TextPalette);
- /* ... otherwise return success */
- return TRUE;
+ if (!Result)
+ {
+ /* Something failed, delete the palettes */
+ if (PaletteHandle) DeleteObject(PaletteHandle);
+ if (TextPaletteHandle) DeleteObject(TextPaletteHandle);
+ }
+
+ return Result;
+}
+
+static VOID VgaSetActiveScreenBuffer(HANDLE ScreenBuffer)
+{
+ /* Set the active buffer */
+ SetConsoleActiveScreenBuffer(ScreenBuffer);
+
+ /* Reinitialize the VDM menu */
+ DestroyVdmMenu();
+ CreateVdmMenu(ScreenBuffer);
}
static BOOL VgaEnterGraphicsMode(PCOORD Resolution)
LONG Height = Resolution->Y;
/* Use DoubleVision mode if the resolution is too small */
- if (Width < VGA_MINIMUM_WIDTH && Height < VGA_MINIMUM_HEIGHT)
- {
- DoubleVision = TRUE;
- Width *= 2;
- Height *= 2;
- }
- else
- {
- DoubleVision = FALSE;
- }
+ DoubleWidth = (Width < VGA_MINIMUM_WIDTH);
+ if (DoubleWidth) Width *= 2;
+ DoubleHeight = (Height < VGA_MINIMUM_HEIGHT);
+ if (DoubleHeight) Height *= 2;
/* Fill the bitmap info header */
- ZeroMemory(&BitmapInfo->bmiHeader, sizeof(BITMAPINFOHEADER));
- BitmapInfo->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
+ RtlZeroMemory(&BitmapInfo->bmiHeader, sizeof(BitmapInfo->bmiHeader));
+ BitmapInfo->bmiHeader.biSize = sizeof(BitmapInfo->bmiHeader);
BitmapInfo->bmiHeader.biWidth = Width;
BitmapInfo->bmiHeader.biHeight = Height;
BitmapInfo->bmiHeader.biBitCount = 8;
ConsoleMutex = GraphicsBufferInfo.hMutex;
/* Clear the framebuffer */
- ZeroMemory(ConsoleFramebuffer, BitmapInfo->bmiHeader.biSizeImage);
+ RtlZeroMemory(ConsoleFramebuffer, BitmapInfo->bmiHeader.biSizeImage);
/* Set the active buffer */
- SetConsoleActiveScreenBuffer(GraphicsConsoleBuffer);
+ VgaSetActiveScreenBuffer(GraphicsConsoleBuffer);
/* Set the graphics mode palette */
SetConsolePalette(GraphicsConsoleBuffer,
ReleaseMutex(ConsoleMutex);
/* Switch back to the default console text buffer */
- // SetConsoleActiveScreenBuffer(TextConsoleBuffer);
+ // VgaSetActiveScreenBuffer(TextConsoleBuffer);
/* Cleanup the video data */
CloseHandle(ConsoleMutex);
ConsoleFramebuffer = NULL;
CloseHandle(GraphicsConsoleBuffer);
GraphicsConsoleBuffer = NULL;
- DoubleVision = FALSE;
+
+ DoubleWidth = FALSE;
+ DoubleHeight = FALSE;
}
static BOOL VgaEnterTextMode(PCOORD Resolution)
{
- SMALL_RECT ConRect;
+ DPRINT1("VgaEnterTextMode\n");
/* Switch to the text buffer */
- SetConsoleActiveScreenBuffer(TextConsoleBuffer);
+ VgaSetActiveScreenBuffer(TextConsoleBuffer);
- /* Resize the console */
- ConRect.Left = 0; // ConsoleInfo.srWindow.Left;
- ConRect.Top = ConsoleInfo.srWindow.Top;
- ConRect.Right = ConRect.Left + Resolution->X - 1;
- ConRect.Bottom = ConRect.Top + Resolution->Y - 1;
- /*
- * Use this trick to effectively resize the console buffer and window,
- * because:
- * - SetConsoleScreenBufferSize fails if the new console screen buffer size
- * is smaller than the current console window size, and:
- * - SetConsoleWindowInfo fails if the new console window size is larger
- * than the current console screen buffer size.
- */
- SetConsoleScreenBufferSize(TextConsoleBuffer, *Resolution);
- SetConsoleWindowInfo(TextConsoleBuffer, TRUE, &ConRect);
- SetConsoleScreenBufferSize(TextConsoleBuffer, *Resolution);
- /* Update the saved console information */
- GetConsoleScreenBufferInfo(TextConsoleBuffer, &ConsoleInfo);
+ /* Adjust the text framebuffer if we changed the resolution */
+ if (TextResolution.X != Resolution->X ||
+ TextResolution.Y != Resolution->Y)
+ {
+ VgaDetachFromConsole(TRUE);
- /* Allocate a framebuffer */
- ConsoleFramebuffer = HeapAlloc(GetProcessHeap(),
- HEAP_ZERO_MEMORY,
- Resolution->X * Resolution->Y
- * sizeof(CHAR_INFO));
- if (ConsoleFramebuffer == NULL)
+ /*
+ * VgaAttachToConsoleInternal sets TextResolution to the
+ * new resolution and updates ConsoleInfo.
+ */
+ if (!VgaAttachToConsoleInternal(Resolution))
+ {
+ DisplayMessage(L"An unexpected error occurred!\n");
+ EmulatorTerminate();
+ return FALSE;
+ }
+ }
+ else
{
- DisplayMessage(L"An unexpected error occurred!\n");
- VdmRunning = FALSE;
- return FALSE;
+ VgaUpdateCursorPosition();
}
+ /* The active framebuffer is now the text framebuffer */
+ ConsoleFramebuffer = TextFramebuffer;
+
/*
* Set the text mode palette.
*
* screen-buffers, which is a new feature on ReactOS).
*/
SetConsolePalette(TextConsoleBuffer,
- PaletteHandle,
+ TextPaletteHandle,
SYSPAL_NOSTATIC256);
/* Set the screen mode flag */
static VOID VgaLeaveTextMode(VOID)
{
- /* Free the old framebuffer */
- HeapFree(GetProcessHeap(), 0, ConsoleFramebuffer);
+ /* Reset the active framebuffer */
ConsoleFramebuffer = NULL;
}
/* Enter new text mode */
if (!VgaEnterTextMode(&Resolution))
{
- DisplayMessage(L"An unexpected VGA error occurred while switching into text mode.");
- VdmRunning = FALSE;
+ DisplayMessage(L"An unexpected VGA error occurred while switching into text mode. Error: %u", GetLastError());
+ EmulatorTerminate();
return;
}
}
/* Enter graphics mode */
if (!VgaEnterGraphicsMode(&Resolution))
{
- DisplayMessage(L"An unexpected VGA error occurred while switching into graphics mode.");
- VdmRunning = FALSE;
+ DisplayMessage(L"An unexpected VGA error occurred while switching into graphics mode. Error: %u", GetLastError());
+ EmulatorTerminate();
return;
}
}
static VOID VgaUpdateFramebuffer(VOID)
{
- INT i, j, k;
+ SHORT i, j, k;
COORD Resolution = VgaGetDisplayResolution();
DWORD AddressSize = VgaGetAddressSize();
DWORD Address = MAKEWORD(VgaCrtcRegisters[VGA_CRTC_START_ADDR_LOW_REG],
{
/* Graphics mode */
PBYTE GraphicsBuffer = (PBYTE)ConsoleFramebuffer;
+ DWORD InterlaceHighBit = VGA_INTERLACE_HIGH_BIT;
/*
* Synchronize access to the graphics framebuffer
*/
WaitForSingleObject(ConsoleMutex, INFINITE);
+ /* Shift the high bit right by 1 in odd/even mode */
+ if (VgaGcRegisters[VGA_GC_MODE_REG] & VGA_GC_MODE_OE)
+ {
+ InterlaceHighBit >>= 1;
+ }
+
/* Loop through the scanlines */
for (i = 0; i < Resolution.Y; i++)
{
+ if ((VgaGcRegisters[VGA_GC_MISC_REG] & VGA_GC_MISC_OE) && (i & 1))
+ {
+ /* Odd-numbered line in interlaced mode - set the high bit */
+ Address |= InterlaceHighBit;
+ }
+
/* Loop through the pixels */
for (j = 0; j < Resolution.X; j++)
{
* 2 bits shifted from plane 0 and 2 for the first 4 pixels,
* then 2 bits shifted from plane 1 and 3 for the next 4
*/
- BYTE LowPlaneData = VgaMemory[((j / 4) % 2) * VGA_BANK_SIZE
- + (Address + (j / 8)) * AddressSize];
- BYTE HighPlaneData = VgaMemory[(((j / 4) % 2) + 2) * VGA_BANK_SIZE
- + (Address + (j / 8)) * AddressSize];
+ DWORD BankNumber = (j / 4) % 2;
+ DWORD Offset = Address + (j / 8);
+ BYTE LowPlaneData = VgaMemory[BankNumber * VGA_BANK_SIZE + Offset * AddressSize];
+ BYTE HighPlaneData = VgaMemory[(BankNumber + 2) * VGA_BANK_SIZE + Offset * AddressSize];
/* Extract the two bits from each plane */
LowPlaneData = (LowPlaneData >> (6 - ((j % 4) * 2))) & 3;
}
/* Take into account DoubleVision mode when checking for pixel updates */
- if (DoubleVision)
+ if (DoubleWidth && DoubleHeight)
{
/* Now check if the resulting pixel data has changed */
- if (GraphicsBuffer[(i * Resolution.X * 4) + (j * 2)] != PixelData)
+ if (GraphicsBuffer[(i * 2 * Resolution.X * 2) + (j * 2)] != PixelData)
{
/* Yes, write the new value */
- GraphicsBuffer[(i * Resolution.X * 4) + (j * 2)] = PixelData;
- GraphicsBuffer[(i * Resolution.X * 4) + (j * 2 + 1)] = PixelData;
+ GraphicsBuffer[(i * 2 * Resolution.X * 2) + (j * 2)] = PixelData;
+ GraphicsBuffer[(i * 2 * Resolution.X * 2) + (j * 2 + 1)] = PixelData;
GraphicsBuffer[((i * 2 + 1) * Resolution.X * 2) + (j * 2)] = PixelData;
GraphicsBuffer[((i * 2 + 1) * Resolution.X * 2) + (j * 2 + 1)] = PixelData;
VgaMarkForUpdate(i, j);
}
}
- else
+ else if (DoubleWidth && !DoubleHeight)
+ {
+ /* Now check if the resulting pixel data has changed */
+ if (GraphicsBuffer[(i * Resolution.X * 2) + (j * 2)] != PixelData)
+ {
+ /* Yes, write the new value */
+ GraphicsBuffer[(i * Resolution.X * 2) + (j * 2)] = PixelData;
+ GraphicsBuffer[(i * Resolution.X * 2) + (j * 2 + 1)] = PixelData;
+
+ /* Mark the specified pixel as changed */
+ VgaMarkForUpdate(i, j);
+ }
+ }
+ else if (!DoubleWidth && DoubleHeight)
+ {
+ /* Now check if the resulting pixel data has changed */
+ if (GraphicsBuffer[(i * 2 * Resolution.X) + j] != PixelData)
+ {
+ /* Yes, write the new value */
+ GraphicsBuffer[(i * 2 * Resolution.X) + j] = PixelData;
+ GraphicsBuffer[((i * 2 + 1) * Resolution.X) + j] = PixelData;
+
+ /* Mark the specified pixel as changed */
+ VgaMarkForUpdate(i, j);
+ }
+ }
+ else // if (!DoubleWidth && !DoubleHeight)
{
/* Now check if the resulting pixel data has changed */
if (GraphicsBuffer[i * Resolution.X + j] != PixelData)
}
}
- /* Move to the next scanline */
- Address += ScanlineSize;
+ if ((VgaGcRegisters[VGA_GC_MISC_REG] & VGA_GC_MISC_OE) && (i & 1))
+ {
+ /* Clear the high bit */
+ Address &= ~InterlaceHighBit;
+ }
+
+ if (!(VgaGcRegisters[VGA_GC_MISC_REG] & VGA_GC_MISC_OE) || (i & 1))
+ {
+ /* Move to the next scanline */
+ Address += ScanlineSize;
+ }
}
/*
{
/* Text mode */
DWORD CurrentAddr;
- PCHAR_INFO CharBuffer = (PCHAR_INFO)ConsoleFramebuffer;
- CHAR_INFO CharInfo;
+ PCHAR_CELL CharBuffer = (PCHAR_CELL)ConsoleFramebuffer;
+ CHAR_CELL CharInfo;
/* Loop through the scanlines */
for (i = 0; i < Resolution.Y; i++)
CurrentAddr = LOWORD((Address + j) * AddressSize);
/* Plane 0 holds the character itself */
- CharInfo.Char.AsciiChar = VgaMemory[CurrentAddr];
+ CharInfo.Char = VgaMemory[CurrentAddr];
/* Plane 1 holds the attribute */
CharInfo.Attributes = VgaMemory[CurrentAddr + VGA_BANK_SIZE];
/* Now check if the resulting character data has changed */
- if ((CharBuffer[i * Resolution.X + j].Char.AsciiChar != CharInfo.Char.AsciiChar)
- || (CharBuffer[i * Resolution.X + j].Attributes != CharInfo.Attributes))
+ if ((CharBuffer[i * Resolution.X + j].Char != CharInfo.Char) ||
+ (CharBuffer[i * Resolution.X + j].Attributes != CharInfo.Attributes))
{
/* Yes, write the new value */
CharBuffer[i * Resolution.X + j] = CharInfo;
Location += (VgaCrtcRegisters[VGA_CRTC_CURSOR_END_REG] >> 5) & 3;
/* Find the coordinates of the new position */
- Position.X = (WORD)(Location % ScanlineSize);
- Position.Y = (WORD)(Location / ScanlineSize);
+ Position.X = (SHORT)(Location % ScanlineSize);
+ Position.Y = (SHORT)(Location / ScanlineSize);
+
+ DPRINT1("VgaUpdateTextCursor: X = %d ; Y = %d\n", Position.X, Position.Y);
/* Update the physical cursor */
SetConsoleCursorInfo(TextConsoleBuffer, &CursorInfo);
CursorMoved = FALSE;
}
-static BYTE WINAPI VgaReadPort(ULONG Port)
+static BYTE WINAPI VgaReadPort(USHORT Port)
{
DPRINT("VgaReadPort: Port 0x%X\n", Port);
return 0;
}
-static VOID WINAPI VgaWritePort(ULONG Port, BYTE Data)
+static VOID WINAPI VgaWritePort(USHORT Port, BYTE Data)
{
DPRINT("VgaWritePort: Port 0x%X, Data 0x%02X\n", Port, Data);
{
/* Graphics mode */
ConsoleBufferHandle = GraphicsConsoleBuffer;
+
+ /* In DoubleVision mode, scale the update rectangle */
+ if (DoubleWidth)
+ {
+ UpdateRectangle.Left *= 2;
+ UpdateRectangle.Right = UpdateRectangle.Right * 2 + 1;
+ }
+ if (DoubleHeight)
+ {
+ UpdateRectangle.Top *= 2;
+ UpdateRectangle.Bottom = UpdateRectangle.Bottom * 2 + 1;
+ }
}
else
{
/* Text mode */
- COORD Origin = { UpdateRectangle.Left, UpdateRectangle.Top };
ConsoleBufferHandle = TextConsoleBuffer;
-
- /* Write the data to the console */
- WriteConsoleOutputA(TextConsoleBuffer,
- (PCHAR_INFO)ConsoleFramebuffer,
- Resolution,
- Origin,
- &UpdateRectangle);
- }
-
- /* In DoubleVision mode, scale the update rectangle */
- if (DoubleVision)
- {
- UpdateRectangle.Left *= 2;
- UpdateRectangle.Top *= 2;
- UpdateRectangle.Right = UpdateRectangle.Right * 2 + 1;
- UpdateRectangle.Bottom = UpdateRectangle.Bottom * 2 + 1;
}
/* Redraw the screen */
- InvalidateConsoleDIBits(ConsoleBufferHandle, &UpdateRectangle);
+ __InvalidateConsoleDIBits(ConsoleBufferHandle, &UpdateRectangle);
/* Clear the update flag */
NeedsUpdate = FALSE;
VOID VgaClearMemory(VOID)
{
- ZeroMemory(VgaMemory, sizeof(VgaMemory));
+ RtlZeroMemory(VgaMemory, sizeof(VgaMemory));
}
VOID VgaResetPalette(VOID)
PaletteChanged = TRUE;
}
+VOID VgaWriteFont(UINT FontNumber, CONST UCHAR *FontData, UINT Height)
+{
+ UINT i, j;
+ PUCHAR FontMemory = (PUCHAR)&VgaMemory[VGA_BANK_SIZE * VGA_FONT_BANK + (FontNumber * VGA_FONT_SIZE)];
+
+ ASSERT(Height <= VGA_MAX_FONT_HEIGHT);
+
+ for (i = 0 ; i < VGA_FONT_CHARACTERS; i++)
+ {
+ /* Write the character */
+ for (j = 0; j < Height; j++)
+ {
+ FontMemory[i * VGA_MAX_FONT_HEIGHT + j] = FontData[i * Height + j];
+ }
+
+ /* Clear the unused part */
+ for (j = Height; j < VGA_MAX_FONT_HEIGHT; j++)
+ {
+ FontMemory[i * VGA_MAX_FONT_HEIGHT + j] = 0;
+ }
+ }
+}
+
+VOID ScreenEventHandler(PWINDOW_BUFFER_SIZE_RECORD ScreenEvent)
+{
+ DPRINT1("Screen events not handled\n");
+}
+
+BOOL VgaAttachToConsole(VOID)
+{
+ //
+ // FIXME: We should go back to the saved screen state
+ //
+ if (TextResolution.X == 0 || TextResolution.Y == 0)
+ DPRINT1("VgaAttachToConsole -- TextResolution uninitialized\n");
+
+ if (TextResolution.X == 0) TextResolution.X = 80;
+ if (TextResolution.Y == 0) TextResolution.Y = 25;
+
+ return VgaAttachToConsoleInternal(&TextResolution);
+}
+
+VOID VgaDetachFromConsole(BOOL ChangingMode)
+{
+ ULONG dummyLength;
+ PVOID dummyPtr;
+ COORD dummySize = {0};
+
+ //
+ // FIXME: We should save the screen state
+ //
+
+ __RegisterConsoleVDM(0,
+ NULL,
+ NULL,
+ NULL,
+ 0,
+ &dummyLength,
+ &dummyPtr,
+ NULL,
+ 0,
+ dummySize,
+ &dummyPtr);
+
+ TextFramebuffer = NULL;
+
+ if (!ChangingMode)
+ {
+ SMALL_RECT ConRect;
+
+ /* Restore the old screen buffer */
+ VgaSetActiveScreenBuffer(TextConsoleBuffer);
+
+ /* Restore the original console size */
+ ConRect.Left = 0;
+ ConRect.Top = 0;
+ ConRect.Right = ConRect.Left + OrgConsoleBufferInfo.srWindow.Right - OrgConsoleBufferInfo.srWindow.Left;
+ ConRect.Bottom = ConRect.Top + OrgConsoleBufferInfo.srWindow.Bottom - OrgConsoleBufferInfo.srWindow.Top ;
+ /*
+ * See the following trick explanation in VgaAttachToConsoleInternal.
+ */
+ SetConsoleScreenBufferSize(TextConsoleBuffer, OrgConsoleBufferInfo.dwSize);
+ SetConsoleWindowInfo(TextConsoleBuffer, TRUE, &ConRect);
+ SetConsoleScreenBufferSize(TextConsoleBuffer, OrgConsoleBufferInfo.dwSize);
+
+ /* Restore the original cursor shape */
+ SetConsoleCursorInfo(TextConsoleBuffer, &OrgConsoleCursorInfo);
+ }
+}
+
BOOLEAN VgaInitialize(HANDLE TextHandle)
{
/* Save the default text-mode console output handle */
- if (TextHandle == INVALID_HANDLE_VALUE) return FALSE;
+ if (!IsConsoleHandle(TextHandle)) return FALSE;
TextConsoleBuffer = TextHandle;
- /* Save the console information */
- if (!GetConsoleScreenBufferInfo(TextConsoleBuffer, &ConsoleInfo))
+ /* Save the original cursor and console screen buffer information */
+ if (!GetConsoleCursorInfo(TextConsoleBuffer, &OrgConsoleCursorInfo) ||
+ !GetConsoleScreenBufferInfo(TextConsoleBuffer, &OrgConsoleBufferInfo))
{
return FALSE;
}
+ ConsoleInfo = OrgConsoleBufferInfo;
/* Initialize the VGA palette and fail if it isn't successfully created */
if (!VgaInitializePalette()) return FALSE;
/***/ VgaResetPalette(); /***/
/* Switch to the text buffer */
- SetConsoleActiveScreenBuffer(TextConsoleBuffer);
+ VgaSetActiveScreenBuffer(TextConsoleBuffer);
/* Clear the VGA memory */
VgaClearMemory();
return TRUE;
}
+VOID VgaCleanup(VOID)
+{
+ if (ScreenMode == GRAPHICS_MODE)
+ {
+ /* Leave the current graphics mode */
+ VgaLeaveGraphicsMode();
+ }
+ else
+ {
+ /* Leave the current text mode */
+ VgaLeaveTextMode();
+ }
+
+ VgaDetachFromConsole(FALSE);
+
+ CloseHandle(AnotherEvent);
+ CloseHandle(EndEvent);
+ CloseHandle(StartEvent);
+}
+
/* EOF */