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