Author: aandrejevic Date: Fri Jul 5 01:31:50 2013 New Revision: 59425
URL: http://svn.reactos.org/svn/reactos?rev=59425&view=rev Log: [NTVDM] Improve vertical refresh performance. Properly synchronize access to the framebuffer.
Modified: branches/ntvdm/subsystems/ntvdm/bios.c branches/ntvdm/subsystems/ntvdm/bios.h branches/ntvdm/subsystems/ntvdm/emulator.c branches/ntvdm/subsystems/ntvdm/ntvdm.c
Modified: branches/ntvdm/subsystems/ntvdm/bios.c URL: http://svn.reactos.org/svn/reactos/branches/ntvdm/subsystems/ntvdm/bios.c?re... ============================================================================== --- branches/ntvdm/subsystems/ntvdm/bios.c [iso-8859-1] (original) +++ branches/ntvdm/subsystems/ntvdm/bios.c [iso-8859-1] Fri Jul 5 01:31:50 2013 @@ -22,11 +22,16 @@ static BOOLEAN BiosKbdBufferEmpty = TRUE; static DWORD BiosTickCount = 0; static BOOLEAN BiosPassedMidnight = FALSE; -static HANDLE BiosConsoleInput, BiosConsoleOutput; +static HANDLE BiosConsoleInput = INVALID_HANDLE_VALUE; +static HANDLE BiosConsoleOutput = INVALID_HANDLE_VALUE; 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 HANDLE ConsoleMutexes[BIOS_MAX_PAGES] = { NULL }; +static BOOLEAN VideoNeedsUpdate = TRUE; +static SMALL_RECT UpdateRectangle = { 0, 0, 0, 0 }; +static CONSOLE_SCREEN_BUFFER_INFO BiosSavedBufferInfo; static VIDEO_MODE VideoModes[] = { /* Width | Height | Text | Colors | Gray | Pages | Segment */ @@ -54,14 +59,39 @@
/* PRIVATE FUNCTIONS **********************************************************/
+static INT BiosColorNumberToBits(DWORD Colors) +{ + INT i; + + /* Find the index of the highest-order bit */ + for (i = 31; i >= 0; i--) if (Colors & (1 << i)) break; + + /* Special case for zero */ + if (i == 0) i = 32; + + return i; +} + static COORD BiosVideoAddressToCoord(ULONG Address) { COORD Result = {0, 0}; - - Result.X = ((Address - (VideoModes[CurrentVideoMode].Segment << 4)) >> 1) - % VideoModes[CurrentVideoMode].Width; - Result.Y = ((Address - (VideoModes[CurrentVideoMode].Segment << 4)) >> 1) - / VideoModes[CurrentVideoMode].Width; + INT BitsPerPixel; + DWORD Offset = Address - (VideoModes[CurrentVideoMode].Segment << 4); + + if (VideoModes[CurrentVideoMode].Text) + { + Result.X = (Offset / sizeof(WORD)) % VideoModes[CurrentVideoMode].Width; + Result.Y = (Offset / sizeof(WORD)) / VideoModes[CurrentVideoMode].Width; + } + else + { + BitsPerPixel = BiosColorNumberToBits(VideoModes[CurrentVideoMode].Colors); + + Result.X = ((Offset * 8) / BitsPerPixel) + % VideoModes[CurrentVideoMode].Width; + Result.Y = ((Offset * 8) / BitsPerPixel) + / VideoModes[CurrentVideoMode].Width; + }
return Result; } @@ -130,6 +160,7 @@ for (i = 0; i < VideoModes[CurrentVideoMode].Pages; i++) { if (ConsoleBuffers[i] != NULL) CloseHandle(ConsoleBuffers[i]); + if (!VideoModes[CurrentVideoMode].Text) CloseHandle(ConsoleMutexes[i]); }
if (VideoModes[ModeNumber].Text) @@ -182,14 +213,7 @@ 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; + BitmapInfo->bmiHeader.biBitCount = BiosColorNumberToBits(VideoModes[ModeNumber].Colors);
/* Calculate the image size */ BitmapInfo->bmiHeader.biSizeImage = BitmapInfo->bmiHeader.biWidth @@ -220,8 +244,9 @@ CONSOLE_GRAPHICS_BUFFER, &GraphicsBufferInfo);
- /* Save the framebuffer address */ + /* Save the framebuffer address and mutex */ ConsoleFramebuffers[i] = GraphicsBufferInfo.lpBitMap; + ConsoleMutexes[i] = GraphicsBufferInfo.hMutex; }
/* Free the bitmap information */ @@ -244,22 +269,21 @@
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; + /* Ignore if there's nothing to update */ + if (!VideoNeedsUpdate) return;
/* Redraw the screen */ InvalidateConsoleDIBits(ConsoleBuffers[CurrentVideoPage], - &Region); -} - -BOOLEAN BiosInitialize(HANDLE ConsoleInput, HANDLE ConsoleOutput) + &UpdateRectangle); + + /* Clear the update flag */ + VideoNeedsUpdate = FALSE; +} + +BOOLEAN BiosInitialize() { INT i; WORD Offset = 0; @@ -290,15 +314,41 @@ BiosCode[Offset++] = 0xCF; // iret }
- /* Set global console I/O handles */ - BiosConsoleInput = ConsoleInput; - BiosConsoleOutput = ConsoleOutput; - + /* Get the input and output handles to the real console */ + BiosConsoleInput = CreateFile(TEXT("CONIN$"), + GENERIC_READ | GENERIC_WRITE, + FILE_SHARE_READ | FILE_SHARE_WRITE, + NULL, + OPEN_EXISTING, + 0, + NULL); + + BiosConsoleOutput = CreateFile(TEXT("CONOUT$"), + GENERIC_READ | GENERIC_WRITE, + FILE_SHARE_READ | FILE_SHARE_WRITE, + NULL, + OPEN_EXISTING, + 0, + NULL); + + /* Make sure it was successful */ + if ((BiosConsoleInput == INVALID_HANDLE_VALUE) + || (BiosConsoleOutput == INVALID_HANDLE_VALUE)) + { + return FALSE; + } + + /* Save the console screen buffer information */ + if (!GetConsoleScreenBufferInfo(BiosConsoleOutput, &BiosSavedBufferInfo)) + { + return FALSE; + } + /* Set the default video mode */ BiosSetVideoMode(BIOS_DEFAULT_VIDEO_MODE);
/* Set the console input mode */ - SetConsoleMode(ConsoleInput, ENABLE_MOUSE_INPUT | ENABLE_PROCESSED_INPUT); + SetConsoleMode(BiosConsoleInput, ENABLE_MOUSE_INPUT | ENABLE_PROCESSED_INPUT);
/* Initialize the PIC */ PicWriteCommand(PIC_MASTER_CMD, PIC_ICW1 | PIC_ICW1_ICW4); @@ -325,6 +375,28 @@ PitWriteData(0, 0x00);
return TRUE; +} + +VOID BiosCleanup() +{ + INT i; + + /* Restore the old screen buffer */ + SetConsoleActiveScreenBuffer(BiosConsoleOutput); + + /* Restore the screen buffer size */ + SetConsoleScreenBufferSize(BiosConsoleOutput, BiosSavedBufferInfo.dwSize); + + /* Free the buffers */ + for (i = 0; i < VideoModes[CurrentVideoMode].Pages; i++) + { + if (ConsoleBuffers[i] != NULL) CloseHandle(ConsoleBuffers[i]); + if (!VideoModes[CurrentVideoMode].Text) CloseHandle(ConsoleMutexes[i]); + } + + /* Close the console handles */ + if (BiosConsoleInput != INVALID_HANDLE_VALUE) CloseHandle(BiosConsoleInput); + if (BiosConsoleOutput != INVALID_HANDLE_VALUE) CloseHandle(BiosConsoleOutput); }
VOID BiosUpdateConsole(ULONG StartAddress, ULONG EndAddress) @@ -367,11 +439,40 @@ } else { + /* Wait for the mutex object */ + WaitForSingleObject(ConsoleMutexes[CurrentVideoPage], INFINITE); + /* Copy the data to the framebuffer */ RtlCopyMemory((LPVOID)((ULONG_PTR)ConsoleFramebuffers[CurrentVideoPage] + StartAddress - BiosGetVideoMemoryStart()), (LPVOID)((ULONG_PTR)BaseAddress + StartAddress), EndAddress - StartAddress); + + /* Release the mutex */ + ReleaseMutex(ConsoleMutexes[CurrentVideoPage]); + + /* 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; } }
@@ -415,11 +516,17 @@ } else { + /* Wait for the mutex object */ + WaitForSingleObject(ConsoleMutexes[CurrentVideoPage], INFINITE); + /* Copy the data to the emulator memory */ RtlCopyMemory((LPVOID)((ULONG_PTR)BaseAddress + StartAddress), (LPVOID)((ULONG_PTR)ConsoleFramebuffers[CurrentVideoPage] + StartAddress - BiosGetVideoMemoryStart()), EndAddress - StartAddress); + + /* Release the mutex */ + ReleaseMutex(ConsoleMutexes[CurrentVideoPage]); } }
@@ -736,6 +843,12 @@ } }
+VOID BiosSystemTimerInterrupt() +{ + /* Increase the system tick count */ + BiosTickCount++; +} + VOID BiosEquipmentService() { /* Return the equipment list */ @@ -749,11 +862,8 @@ /* PIT IRQ */ case 0: { - /* Increase the system tick count */ - BiosTickCount++; - /* Perform the system timer interrupt */ - EmulatorInterrupt(0x1C); + EmulatorInterrupt(BIOS_SYS_TIMER_INTERRUPT);
break; }
Modified: branches/ntvdm/subsystems/ntvdm/bios.h URL: http://svn.reactos.org/svn/reactos/branches/ntvdm/subsystems/ntvdm/bios.h?re... ============================================================================== --- branches/ntvdm/subsystems/ntvdm/bios.h [iso-8859-1] (original) +++ branches/ntvdm/subsystems/ntvdm/bios.h [iso-8859-1] Fri Jul 5 01:31:50 2013 @@ -25,6 +25,7 @@ #define BIOS_EQUIPMENT_INTERRUPT 0x11 #define BIOS_KBD_INTERRUPT 0x16 #define BIOS_TIME_INTERRUPT 0x1A +#define BIOS_SYS_TIMER_INTERRUPT 0x1C #define CONSOLE_FONT_HEIGHT 8 #define BIOS_KBD_BUFFER_SIZE 256 #define BIOS_EQUIPMENT_LIST 0x3C // HACK: Disable FPU for now @@ -46,6 +47,7 @@ /* FUNCTIONS ******************************************************************/
BOOLEAN BiosInitialize(); +VOID BiosCleanup(); VOID BiosUpdateConsole(ULONG StartAddress, ULONG EndAddress); VOID BiosUpdateVideoMemory(ULONG StartAddress, ULONG EndAddress); inline DWORD BiosGetVideoMemoryStart(); @@ -57,5 +59,6 @@ VOID BiosKeyboardService(); VOID BiosTimeService(); VOID BiosHandleIrq(BYTE IrqNumber); +VOID BiosSystemTimerInterrupt();
#endif
Modified: branches/ntvdm/subsystems/ntvdm/emulator.c URL: http://svn.reactos.org/svn/reactos/branches/ntvdm/subsystems/ntvdm/emulator.... ============================================================================== --- branches/ntvdm/subsystems/ntvdm/emulator.c [iso-8859-1] (original) +++ branches/ntvdm/subsystems/ntvdm/emulator.c [iso-8859-1] Fri Jul 5 01:31:50 2013 @@ -234,6 +234,12 @@ BiosTimeService(); break; } + case BIOS_SYS_TIMER_INTERRUPT: + { + /* BIOS timer update */ + BiosSystemTimerInterrupt(); + break; + } case 0x20: { DosInt20h(CodeSegment);
Modified: branches/ntvdm/subsystems/ntvdm/ntvdm.c URL: http://svn.reactos.org/svn/reactos/branches/ntvdm/subsystems/ntvdm/ntvdm.c?r... ============================================================================== --- branches/ntvdm/subsystems/ntvdm/ntvdm.c [iso-8859-1] (original) +++ branches/ntvdm/subsystems/ntvdm/ntvdm.c [iso-8859-1] Fri Jul 5 01:31:50 2013 @@ -76,43 +76,9 @@ 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); @@ -131,7 +97,7 @@ }
/* Initialize the system BIOS */ - if (!BiosInitialize(ConsoleInput, ConsoleOutput)) + if (!BiosInitialize()) { wprintf(L"FATAL: Failed to initialize the VDM BIOS.\n"); goto Cleanup; @@ -201,17 +167,8 @@ }
Cleanup: - /* Restore the old screen buffer */ - SetConsoleActiveScreenBuffer(ConsoleOutput); - - /* Restore the screen buffer size */ - SetConsoleScreenBufferSize(ConsoleOutput, SavedBufferInfo.dwSize); - + BiosCleanup(); EmulatorCleanup(); - - /* Close the console handles */ - if (ConsoleInput != INVALID_HANDLE_VALUE) CloseHandle(ConsoleInput); - if (ConsoleOutput != INVALID_HANDLE_VALUE) CloseHandle(ConsoleOutput);
return 0; }