From 01fd2f9aa3b4dc43d1199bf2ead3d9bf15170f53 Mon Sep 17 00:00:00 2001 From: Aleksandar Andrejevic Date: Fri, 5 Jul 2013 00:08:18 +0000 Subject: [PATCH] [NTVDM] Implement BIOS video mode setting. Fix bugs in video memory access emulation. svn path=/branches/ntvdm/; revision=59424 --- subsystems/ntvdm/bios.c | 344 +++++++++++++++++++++++++++++------- subsystems/ntvdm/bios.h | 19 +- subsystems/ntvdm/emulator.c | 8 +- subsystems/ntvdm/ntvdm.c | 56 +++++- 4 files changed, 354 insertions(+), 73 deletions(-) diff --git a/subsystems/ntvdm/bios.c b/subsystems/ntvdm/bios.c index 518ebcb962c..40632dd3a47 100644 --- a/subsystems/ntvdm/bios.c +++ b/subsystems/ntvdm/bios.c @@ -22,6 +22,35 @@ static UINT BiosKbdBufferStart = 0, BiosKbdBufferEnd = 0; static BOOLEAN BiosKbdBufferEmpty = TRUE; static DWORD BiosTickCount = 0; static BOOLEAN BiosPassedMidnight = FALSE; +static HANDLE BiosConsoleInput, BiosConsoleOutput; +static BYTE CurrentVideoMode = BIOS_DEFAULT_VIDEO_MODE; +static BYTE CurrentVideoPage = 0; +static HANDLE ConsoleBuffers[BIOS_MAX_PAGES] = { NULL }; +static LPVOID ConsoleFramebuffers[BIOS_MAX_PAGES] = { NULL }; +static VIDEO_MODE VideoModes[] = +{ + /* Width | Height | Text | Colors | 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, 4, FALSE, 4, 0xB800}, /* Mode 04h */ + { 320, 200, FALSE, 4, TRUE, 4, 0xB800}, /* Mode 05h */ + { 640, 200, FALSE, 2, FALSE, 2, 0xB800}, /* Mode 06h */ + { 80, 25, TRUE, 3, 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, 16, FALSE, 8, 0xA000}, /* Mode 0Dh */ + { 640, 200, FALSE, 16, FALSE, 4, 0xA000}, /* Mode 0Eh */ + { 640, 350, FALSE, 3, FALSE, 2, 0xA000}, /* Mode 0Fh */ + { 640, 350, FALSE, 4, FALSE, 2, 0xA000}, /* Mode 10h */ + { 640, 480, FALSE, 2, FALSE, 1, 0xA000}, /* Mode 11h */ + { 640, 480, FALSE, 16, FALSE, 1, 0xA000}, /* Mode 12h */ + { 640, 480, FALSE, 256, FALSE, 1, 0xA000} /* Mode 13h */ +}; /* PRIVATE FUNCTIONS **********************************************************/ @@ -29,8 +58,10 @@ static COORD BiosVideoAddressToCoord(ULONG Address) { COORD Result = {0, 0}; - Result.X = ((Address - CONSOLE_VIDEO_MEM_START) >> 1) % CONSOLE_WIDTH; - Result.Y = ((Address - CONSOLE_VIDEO_MEM_START) >> 1) / CONSOLE_WIDTH; + Result.X = ((Address - (VideoModes[CurrentVideoMode].Segment << 4)) >> 1) + % VideoModes[CurrentVideoMode].Width; + Result.Y = ((Address - (VideoModes[CurrentVideoMode].Segment << 4)) >> 1) + / VideoModes[CurrentVideoMode].Width; return Result; } @@ -78,13 +109,160 @@ static BOOLEAN BiosKbdBufferPop() /* PUBLIC FUNCTIONS ***********************************************************/ -BOOLEAN BiosInitialize() +BYTE BiosGetVideoMode() +{ + return CurrentVideoMode; +} + +BOOLEAN BiosSetVideoMode(BYTE ModeNumber) +{ + INT i; + COORD Coord; + CONSOLE_GRAPHICS_BUFFER_INFO GraphicsBufferInfo; + LPBITMAPINFO BitmapInfo; + LPWORD PaletteIndex; + + /* Make sure this is a valid video mode */ + if (ModeNumber > BIOS_MAX_VIDEO_MODE) return FALSE; + if (VideoModes[ModeNumber].Pages == 0) return FALSE; + + /* Free the current buffers */ + for (i = 0; i < VideoModes[CurrentVideoMode].Pages; i++) + { + if (ConsoleBuffers[i] != NULL) CloseHandle(ConsoleBuffers[i]); + } + + if (VideoModes[ModeNumber].Text) + { + /* Page 0 is CONOUT$ */ + ConsoleBuffers[0] = CreateFile(TEXT("CONOUT$"), + GENERIC_READ | GENERIC_WRITE, + FILE_SHARE_READ | FILE_SHARE_WRITE, + NULL, + OPEN_EXISTING, + 0, + NULL); + + /* Set the current page to page 0 */ + CurrentVideoPage = 0; + + /* Create console buffers for other pages */ + for (i = 1; i < VideoModes[ModeNumber].Pages; i++) + { + ConsoleBuffers[i] = CreateConsoleScreenBuffer(GENERIC_READ | GENERIC_WRITE, + FILE_SHARE_READ | FILE_SHARE_WRITE, + NULL, + CONSOLE_TEXTMODE_BUFFER, + NULL); + } + + /* Set the size for the buffers */ + for (i = 0; i < VideoModes[ModeNumber].Pages; i++) + { + Coord.X = VideoModes[ModeNumber].Width; + Coord.Y = VideoModes[ModeNumber].Height; + + SetConsoleScreenBufferSize(ConsoleBuffers[i], Coord); + } + } + else + { + /* Allocate a bitmap info structure */ + BitmapInfo = (LPBITMAPINFO)HeapAlloc(GetProcessHeap(), + HEAP_ZERO_MEMORY, + sizeof(BITMAPINFOHEADER) + + VideoModes[ModeNumber].Colors + * 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; + + /* Calculate the number of color bits */ + for (i = 31; i >= 0; i--) + { + if (VideoModes[ModeNumber].Colors & (1 << i)) break; + } + if (i == 0) i = 32; + BitmapInfo->bmiHeader.biBitCount = i; + + /* 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 < VideoModes[ModeNumber].Colors; i++) + { + PaletteIndex[i] = i; + } + + /* Create a console buffer for each page */ + for (i = 0; i < VideoModes[ModeNumber].Pages; i++) + { + /* Fill the console graphics buffer info */ + GraphicsBufferInfo.dwBitMapInfoLength = sizeof(BITMAPINFOHEADER) + + VideoModes[ModeNumber].Colors + * sizeof(WORD); + GraphicsBufferInfo.lpBitMapInfo = BitmapInfo; + GraphicsBufferInfo.dwUsage = DIB_PAL_COLORS; + + /* Create the buffer */ + ConsoleBuffers[i] = CreateConsoleScreenBuffer(GENERIC_READ | GENERIC_WRITE, + FILE_SHARE_READ | FILE_SHARE_WRITE, + NULL, + CONSOLE_GRAPHICS_BUFFER, + &GraphicsBufferInfo); + + /* Save the framebuffer address */ + ConsoleFramebuffers[i] = GraphicsBufferInfo.lpBitMap; + } + + /* Free the bitmap information */ + HeapFree(GetProcessHeap(), 0, BitmapInfo); + } + + /* Set the active page console buffer */ + SetConsoleActiveScreenBuffer(ConsoleBuffers[CurrentVideoPage]); + + /* Update the mode number */ + CurrentVideoMode = ModeNumber; + + return TRUE; +} + +inline DWORD BiosGetVideoMemoryStart() +{ + return (VideoModes[CurrentVideoMode].Segment << 4); +} + +inline VOID BiosVerticalRefresh() +{ + SMALL_RECT Region; + + /* Ignore if we're in text mode */ + if (VideoModes[CurrentVideoMode].Text) return; + + /* Fill the rectangle structure */ + Region.Left = Region.Top = 0; + Region.Right = VideoModes[CurrentVideoMode].Width; + Region.Bottom = VideoModes[CurrentVideoMode].Height; + + /* Redraw the screen */ + InvalidateConsoleDIBits(ConsoleBuffers[CurrentVideoPage], + &Region); +} + +BOOLEAN BiosInitialize(HANDLE ConsoleInput, HANDLE ConsoleOutput) { INT i; WORD Offset = 0; - COORD Size = { CONSOLE_WIDTH, CONSOLE_HEIGHT}; - HANDLE ConsoleInput = GetStdHandle(STD_INPUT_HANDLE); - HANDLE ConsoleOutput = GetStdHandle(STD_OUTPUT_HANDLE); LPWORD IntVecTable = (LPWORD)((ULONG_PTR)BaseAddress); LPBYTE BiosCode = (LPBYTE)((ULONG_PTR)BaseAddress + TO_LINEAR(BIOS_SEGMENT, 0)); @@ -112,8 +290,12 @@ BOOLEAN BiosInitialize() BiosCode[Offset++] = 0xCF; // iret } - /* Set the console buffer size */ - if (!SetConsoleScreenBufferSize(ConsoleOutput, Size)) return FALSE; + /* Set global console I/O handles */ + BiosConsoleInput = ConsoleInput; + BiosConsoleOutput = ConsoleOutput; + + /* Set the default video mode */ + BiosSetVideoMode(BIOS_DEFAULT_VIDEO_MODE); /* Set the console input mode */ SetConsoleMode(ConsoleInput, ENABLE_MOUSE_INPUT | ENABLE_PROCESSED_INPUT); @@ -153,27 +335,43 @@ VOID BiosUpdateConsole(ULONG StartAddress, ULONG EndAddress) COORD UnitSize = { 1, 1 }; CHAR_INFO Character; SMALL_RECT Rect; - HANDLE ConsoleOutput = GetStdHandle(STD_OUTPUT_HANDLE); /* Start from the character address */ StartAddress &= ~1; - /* Loop through all the addresses */ - for (i = StartAddress; i < EndAddress; i += 2) + if (VideoModes[CurrentVideoMode].Text) { - /* Get the coordinates */ - Coordinates = BiosVideoAddressToCoord(i); + /* Loop through all the addresses */ + for (i = StartAddress; i < EndAddress; i += 2) + { + /* Get the coordinates */ + Coordinates = BiosVideoAddressToCoord(i); - /* Fill the rectangle structure */ - Rect.Left = Coordinates.X; - Rect.Top = Coordinates.Y; + /* 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)); + /* Fill the character data */ + Character.Char.AsciiChar = *((PCHAR)((ULONG_PTR)BaseAddress + i)); + Character.Attributes = *((PBYTE)((ULONG_PTR)BaseAddress + i + 1)); - /* Write the character */ - WriteConsoleOutputA(ConsoleOutput, &Character, UnitSize, Origin, &Rect); + /* Write the character */ + WriteConsoleOutputA(BiosConsoleOutput, + &Character, + UnitSize, + Origin, + &Rect); + } + } + else + { + /* Copy the data to the framebuffer */ + RtlCopyMemory((LPVOID)((ULONG_PTR)ConsoleFramebuffers[CurrentVideoPage] + + StartAddress - BiosGetVideoMemoryStart()), + (LPVOID)((ULONG_PTR)BaseAddress + StartAddress), + EndAddress - StartAddress); } } @@ -183,36 +381,46 @@ VOID BiosUpdateVideoMemory(ULONG StartAddress, ULONG EndAddress) COORD Coordinates; WORD Attribute; DWORD CharsWritten; - HANDLE ConsoleOutput = GetStdHandle(STD_OUTPUT_HANDLE); - /* Loop through all the addresses */ - for (i = StartAddress; i < EndAddress; i++) + if (VideoModes[CurrentVideoMode].Text) { - /* Get the coordinates */ - Coordinates = BiosVideoAddressToCoord(i); - - /* Check if this is a character byte or an attribute byte */ - if ((i - CONSOLE_VIDEO_MEM_START) % 2 == 0) + /* Loop through all the addresses */ + for (i = StartAddress; i < EndAddress; i++) { - /* This is a regular character */ - ReadConsoleOutputCharacterA(ConsoleOutput, - (LPSTR)((ULONG_PTR)BaseAddress + i), - sizeof(CHAR), - Coordinates, - &CharsWritten); - } - else - { - /* This is an attribute */ - ReadConsoleOutputAttribute(ConsoleOutput, - &Attribute, - sizeof(CHAR), - Coordinates, - &CharsWritten); + /* Get the coordinates */ + Coordinates = BiosVideoAddressToCoord(i); - *(PCHAR)((ULONG_PTR)BaseAddress + i) = LOBYTE(Attribute); + /* Check if this is a character byte or an attribute byte */ + if ((i - (VideoModes[CurrentVideoMode].Segment << 4)) % 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 + { + /* Copy the data to the emulator memory */ + RtlCopyMemory((LPVOID)((ULONG_PTR)BaseAddress + StartAddress), + (LPVOID)((ULONG_PTR)ConsoleFramebuffers[CurrentVideoPage] + + StartAddress - BiosGetVideoMemoryStart()), + EndAddress - StartAddress); + } } WORD BiosPeekCharacter() @@ -231,7 +439,6 @@ WORD BiosPeekCharacter() WORD BiosGetCharacter() { WORD CharacterData; - HANDLE ConsoleInput = GetStdHandle(STD_INPUT_HANDLE); INPUT_RECORD InputRecord; DWORD Count; @@ -247,10 +454,10 @@ WORD BiosGetCharacter() while (TRUE) { /* Wait for a console event */ - WaitForSingleObject(ConsoleInput, INFINITE); + WaitForSingleObject(BiosConsoleInput, INFINITE); /* Read the event, and make sure it's a keypress */ - if (!ReadConsoleInput(ConsoleInput, &InputRecord, 1, &Count)) continue; + if (!ReadConsoleInput(BiosConsoleInput, &InputRecord, 1, &Count)) continue; if (InputRecord.EventType != KEY_EVENT) continue; if (!InputRecord.Event.KeyEvent.bKeyDown) continue; @@ -267,7 +474,6 @@ WORD BiosGetCharacter() VOID BiosVideoService() { - HANDLE ConsoleOutput = GetStdHandle(STD_OUTPUT_HANDLE); INT CursorHeight; BOOLEAN Invisible = FALSE; COORD Position; @@ -282,6 +488,13 @@ VOID BiosVideoService() switch (HIBYTE(Eax)) { + /* Set Video Mode */ + case 0x00: + { + BiosSetVideoMode(LOBYTE(Eax)); + break; + } + /* Set Text-Mode Cursor Shape */ case 0x01: { @@ -294,7 +507,7 @@ VOID BiosVideoService() /* Set the cursor */ CursorInfo.dwSize = (CursorHeight * 100) / CONSOLE_FONT_HEIGHT; CursorInfo.bVisible = !Invisible; - SetConsoleCursorInfo(ConsoleOutput, &CursorInfo); + SetConsoleCursorInfo(BiosConsoleOutput, &CursorInfo); break; } @@ -305,7 +518,7 @@ VOID BiosVideoService() Position.X = LOBYTE(Edx); Position.Y = HIBYTE(Edx); - SetConsoleCursorPosition(ConsoleOutput, Position); + SetConsoleCursorPosition(BiosConsoleOutput, Position); break; } @@ -315,8 +528,8 @@ VOID BiosVideoService() INT StartLine; /* Retrieve the data */ - GetConsoleCursorInfo(ConsoleOutput, &CursorInfo); - GetConsoleScreenBufferInfo(ConsoleOutput, &ScreenBufferInfo); + GetConsoleCursorInfo(BiosConsoleOutput, &CursorInfo); + GetConsoleScreenBufferInfo(BiosConsoleOutput, &ScreenBufferInfo); /* Find the first line */ StartLine = 32 - ((CursorInfo.dwSize * 32) / 100); @@ -344,7 +557,7 @@ VOID BiosVideoService() if (HIBYTE(Eax) == 0x06) Position.Y = Rect.Top - LOBYTE(Eax); else Position.Y = Rect.Top + LOBYTE(Eax); - ScrollConsoleScreenBuffer(ConsoleOutput, + ScrollConsoleScreenBuffer(BiosConsoleOutput, &Rect, &Rect, Position, @@ -358,14 +571,14 @@ VOID BiosVideoService() COORD BufferSize = { 1, 1 }, Origin = { 0, 0 }; /* Get the cursor position */ - GetConsoleScreenBufferInfo(ConsoleOutput, &ScreenBufferInfo); + GetConsoleScreenBufferInfo(BiosConsoleOutput, &ScreenBufferInfo); /* Read at cursor position */ Rect.Left = ScreenBufferInfo.dwCursorPosition.X; Rect.Top = ScreenBufferInfo.dwCursorPosition.Y; /* Read the console output */ - ReadConsoleOutput(ConsoleOutput, &Character, BufferSize, Origin, &Rect); + ReadConsoleOutput(BiosConsoleOutput, &Character, BufferSize, Origin, &Rect); /* Return the result */ EmulatorSetRegister(EMULATOR_REG_AX, @@ -381,17 +594,17 @@ VOID BiosVideoService() DWORD CharsWritten; /* Get the cursor position */ - GetConsoleScreenBufferInfo(ConsoleOutput, &ScreenBufferInfo); + GetConsoleScreenBufferInfo(BiosConsoleOutput, &ScreenBufferInfo); /* Write the attribute to the output */ - FillConsoleOutputAttribute(ConsoleOutput, + FillConsoleOutputAttribute(BiosConsoleOutput, LOBYTE(Ebx), LOWORD(Ecx), ScreenBufferInfo.dwCursorPosition, &CharsWritten); /* Write the character to the output */ - FillConsoleOutputCharacterA(ConsoleOutput, + FillConsoleOutputCharacterA(BiosConsoleOutput, LOBYTE(Eax), LOWORD(Ecx), ScreenBufferInfo.dwCursorPosition, @@ -406,10 +619,10 @@ VOID BiosVideoService() DWORD CharsWritten; /* Get the cursor position */ - GetConsoleScreenBufferInfo(ConsoleOutput, &ScreenBufferInfo); + GetConsoleScreenBufferInfo(BiosConsoleOutput, &ScreenBufferInfo); /* Write the character to the output */ - FillConsoleOutputCharacterA(ConsoleOutput, + FillConsoleOutputCharacterA(BiosConsoleOutput, LOBYTE(Eax), LOWORD(Ecx), ScreenBufferInfo.dwCursorPosition, @@ -418,11 +631,14 @@ VOID BiosVideoService() break; } + /* Get Current Video Mode */ case 0x0F: { - /* Return just text mode information, for now */ - EmulatorSetRegister(EMULATOR_REG_AX, 0x5003); - EmulatorSetRegister(EMULATOR_REG_BX, 0x0000); + EmulatorSetRegister(EMULATOR_REG_AX, + (VideoModes[CurrentVideoMode].Width << 8) + | CurrentVideoMode); + EmulatorSetRegister(EMULATOR_REG_BX, + (CurrentVideoPage << 8) | LOBYTE(Ebx)); break; } diff --git a/subsystems/ntvdm/bios.h b/subsystems/ntvdm/bios.h index 0e4e49927bd..34f9f5b85ed 100644 --- a/subsystems/ntvdm/bios.h +++ b/subsystems/ntvdm/bios.h @@ -15,7 +15,6 @@ /* DEFINES ********************************************************************/ -#define CONSOLE_VIDEO_MEM_START 0xB8000 #define CONSOLE_VIDEO_MEM_END 0xBFFFF #define ROM_AREA_START 0xC0000 #define ROM_AREA_END 0xFFFFF @@ -26,17 +25,31 @@ #define BIOS_EQUIPMENT_INTERRUPT 0x11 #define BIOS_KBD_INTERRUPT 0x16 #define BIOS_TIME_INTERRUPT 0x1A -#define CONSOLE_WIDTH 80 -#define CONSOLE_HEIGHT 25 #define CONSOLE_FONT_HEIGHT 8 #define BIOS_KBD_BUFFER_SIZE 256 #define BIOS_EQUIPMENT_LIST 0x3C // HACK: Disable FPU for now +#define BIOS_DEFAULT_VIDEO_MODE 0x03 +#define BIOS_MAX_PAGES 8 +#define BIOS_MAX_VIDEO_MODE 0x13 + +typedef struct +{ + DWORD Width; + DWORD Height; + BOOLEAN Text; + DWORD Colors; + BOOLEAN Gray; + BYTE Pages; + WORD Segment; +} VIDEO_MODE; /* FUNCTIONS ******************************************************************/ BOOLEAN BiosInitialize(); VOID BiosUpdateConsole(ULONG StartAddress, ULONG EndAddress); VOID BiosUpdateVideoMemory(ULONG StartAddress, ULONG EndAddress); +inline DWORD BiosGetVideoMemoryStart(); +inline VOID BiosVerticalRefresh(); WORD BiosPeekCharacter(); WORD BiosGetCharacter(); VOID BiosVideoService(); diff --git a/subsystems/ntvdm/emulator.c b/subsystems/ntvdm/emulator.c index e5b22fea206..fb26ee1fab8 100644 --- a/subsystems/ntvdm/emulator.c +++ b/subsystems/ntvdm/emulator.c @@ -32,11 +32,11 @@ static VOID EmulatorReadMemory(PVOID Context, UINT Address, LPBYTE Buffer, INT S if ((Address + Size) >= MAX_ADDRESS) return; /* Are we reading some of the console video memory? */ - if (((Address + Size) >= CONSOLE_VIDEO_MEM_START) + if (((Address + Size) >= BiosGetVideoMemoryStart()) && (Address < CONSOLE_VIDEO_MEM_END)) { /* Call the VDM BIOS to update the video memory */ - BiosUpdateVideoMemory(max(Address, CONSOLE_VIDEO_MEM_START), + BiosUpdateVideoMemory(max(Address, BiosGetVideoMemoryStart()), min(Address + Size, CONSOLE_VIDEO_MEM_END)); } @@ -59,11 +59,11 @@ 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) >= CONSOLE_VIDEO_MEM_START) + if (((Address + Size) >= BiosGetVideoMemoryStart()) && (Address < CONSOLE_VIDEO_MEM_END)) { /* Call the VDM BIOS to update the screen */ - BiosUpdateConsole(max(Address, CONSOLE_VIDEO_MEM_START), + BiosUpdateConsole(max(Address, BiosGetVideoMemoryStart()), min(Address + Size, CONSOLE_VIDEO_MEM_END)); } } diff --git a/subsystems/ntvdm/ntvdm.c b/subsystems/ntvdm/ntvdm.c index f3ea8b009f9..300a89c0d5b 100644 --- a/subsystems/ntvdm/ntvdm.c +++ b/subsystems/ntvdm/ntvdm.c @@ -73,19 +73,54 @@ INT wmain(INT argc, WCHAR *argv[]) DWORD LastTickCount = GetTickCount(); DWORD Cycles = 0; DWORD LastCyclePrintout = GetTickCount(); + DWORD LastVerticalRefresh = GetTickCount(); LARGE_INTEGER Frequency, LastTimerTick, Counter; LONGLONG TimerTicks; + HANDLE ConsoleInput = INVALID_HANDLE_VALUE; + HANDLE ConsoleOutput = INVALID_HANDLE_VALUE; + CONSOLE_SCREEN_BUFFER_INFO SavedBufferInfo; /* Set the handler routine */ SetConsoleCtrlHandler(ConsoleCtrlHandler, TRUE); + /* Get the input and output handles to the real console */ + ConsoleInput = CreateFile(TEXT("CONIN$"), + GENERIC_READ | GENERIC_WRITE, + FILE_SHARE_READ | FILE_SHARE_WRITE, + NULL, + OPEN_EXISTING, + 0, + NULL); + + ConsoleOutput = CreateFile(TEXT("CONOUT$"), + GENERIC_READ | GENERIC_WRITE, + FILE_SHARE_READ | FILE_SHARE_WRITE, + NULL, + OPEN_EXISTING, + 0, + NULL); + + if ((ConsoleInput == INVALID_HANDLE_VALUE) + || (ConsoleOutput == INVALID_HANDLE_VALUE)) + { + wprintf(L"FATAL: Could not get handles to the console\n"); + goto Cleanup; + } + + /* Save the console screen buffer information */ + if (!GetConsoleScreenBufferInfo(ConsoleOutput, &SavedBufferInfo)) + { + wprintf(L"FATAL: Could not save the console screen buffer information\n"); + goto Cleanup; + } + /* The DOS command line must be ASCII */ WideCharToMultiByte(CP_ACP, 0, GetCommandLine(), -1, CommandLine, 128, NULL, NULL); if (!EmulatorInitialize()) { wprintf(L"FATAL: Failed to initialize the CPU emulator\n"); - return 1; + goto Cleanup; } /* Initialize the performance counter (needed for hardware timers) */ @@ -96,7 +131,7 @@ INT wmain(INT argc, WCHAR *argv[]) } /* Initialize the system BIOS */ - if (!BiosInitialize()) + if (!BiosInitialize(ConsoleInput, ConsoleOutput)) { wprintf(L"FATAL: Failed to initialize the VDM BIOS.\n"); goto Cleanup; @@ -142,6 +177,13 @@ INT wmain(INT argc, WCHAR *argv[]) CheckForInputEvents(); LastTickCount = CurrentTickCount; } + + /* Check for vertical refresh */ + if ((CurrentTickCount - LastVerticalRefresh) >= 16) + { + BiosVerticalRefresh(); + LastVerticalRefresh = CurrentTickCount; + } /* Continue CPU emulation */ for (i = 0; (i < STEPS_PER_CYCLE) && VdmRunning; i++) @@ -159,8 +201,18 @@ INT wmain(INT argc, WCHAR *argv[]) } Cleanup: + /* Restore the old screen buffer */ + SetConsoleActiveScreenBuffer(ConsoleOutput); + + /* Restore the screen buffer size */ + SetConsoleScreenBufferSize(ConsoleOutput, SavedBufferInfo.dwSize); + EmulatorCleanup(); + /* Close the console handles */ + if (ConsoleInput != INVALID_HANDLE_VALUE) CloseHandle(ConsoleInput); + if (ConsoleOutput != INVALID_HANDLE_VALUE) CloseHandle(ConsoleOutput); + return 0; }