#include "bios.h"
#include "emulator.h"
+#include "vga.h"
#include "pic.h"
#include "ps2.h"
#include "timer.h"
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)
{
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;
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);
/* 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;
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 */
/* 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;
}
/* 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;
}
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);
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));
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;
}
/* 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;
}
/* 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;
}
--- /dev/null
+/*
+ * 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 */
+