/******************************Module*Header*******************************\ * Module Name: hardware.c * * Contains all the code that touches the display hardware. * * Copyright (c) 1994-1995 Microsoft Corporation \**************************************************************************/ #include "precomp.h" /******************************Public*Routine******************************\ * BOOL bAssertModeHardware * * Sets the appropriate hardware state for graphics mode or full-screen. * \**************************************************************************/ BOOL bAssertModeHardware( PDEV* ppdev, BOOL bEnable) { DWORD ReturnedDataLength; BYTE* pjBase; RECTL rcl; pjBase = ppdev->pjBase; if (bEnable) { // Reset some state: ppdev->cjVgaOffset = 0; ppdev->iVgaPage = 0; ppdev->fpScreenOffset = 0; // Set the desired mode. if (EngDeviceIoControl(ppdev->hDriver, IOCTL_VIDEO_SET_CURRENT_MODE, &ppdev->ulMode, // input buffer sizeof(VIDEO_MODE), NULL, 0, &ReturnedDataLength)) { DISPDBG((0, "bAssertModeHardware - Failed VIDEO_SET_CURRENT_MODE")); goto ReturnFalse; } // Now blank the screen: rcl.left = 0; rcl.top = 0; rcl.right = ppdev->cxScreen; rcl.bottom = ppdev->cyScreen; vUpdate(ppdev, &rcl, NULL); DISPDBG((5, "Passed bAssertModeHardware")); } else { // Call the kernel driver to reset the device to a known state. // NTVDM will take things from there: if (EngDeviceIoControl(ppdev->hDriver, IOCTL_VIDEO_RESET_DEVICE, NULL, 0, NULL, 0, &ReturnedDataLength)) { DISPDBG((0, "bAssertModeHardware - Failed reset IOCTL")); goto ReturnFalse; } } return(TRUE); ReturnFalse: DISPDBG((0, "Failed bAssertModeHardware")); return(FALSE); } /******************************Public*Routine******************************\ * BOOL bEnableHardware * * Puts the hardware into the requested mode and initializes it. * * Note: Should be called before any access is done to the hardware from * the display driver. * \**************************************************************************/ BOOL bEnableHardware( PDEV* ppdev) { VIDEO_MEMORY VideoMemory; VIDEO_MEMORY_INFORMATION VideoMemoryInfo; VIDEO_MODE_INFORMATION VideoModeInfo; DWORD ReturnedDataLength; VIDEO_PUBLIC_ACCESS_RANGES VideoAccessRange; DWORD status; #if defined(_X86_) ppdev->pjBase = NULL; #else // Map io ports into virtual memory: VideoMemory.RequestedVirtualAddress = NULL; if (EngDeviceIoControl(ppdev->hDriver, IOCTL_VIDEO_QUERY_PUBLIC_ACCESS_RANGES, NULL, // input buffer 0, &VideoAccessRange, // output buffer sizeof (VideoAccessRange), &ReturnedDataLength)) { RIP("bEnableHardware - Initialization error mapping IO port base"); goto ReturnFalse; } ppdev->pjBase = (UCHAR*) VideoAccessRange.VirtualAddress; #endif // Set the desired mode. (Must come before IOCTL_VIDEO_MAP_VIDEO_MEMORY; // that IOCTL returns information for the current mode, so there must be a // current mode for which to return information.) if (EngDeviceIoControl(ppdev->hDriver, IOCTL_VIDEO_SET_CURRENT_MODE, &ppdev->ulMode, // input buffer sizeof(VIDEO_MODE), NULL, 0, &ReturnedDataLength)) { RIP("bEnableHardware - Set current mode"); goto ReturnFalse; } // Get the linear memory address range. VideoMemory.RequestedVirtualAddress = NULL; if (EngDeviceIoControl(ppdev->hDriver, IOCTL_VIDEO_MAP_VIDEO_MEMORY, &VideoMemory, // input buffer sizeof(VIDEO_MEMORY), &VideoMemoryInfo, // output buffer sizeof(VideoMemoryInfo), &ReturnedDataLength)) { DISPDBG((0, "bEnableHardware - Error mapping buffer address")); goto ReturnFalse; } DISPDBG((1, "FrameBufferBase: %lx", VideoMemoryInfo.FrameBufferBase)); // Record the Frame Buffer Linear Address. ppdev->pjVga = (BYTE*) VideoMemoryInfo.FrameBufferBase; // Store the width of the screen in bytes, per-plane: ppdev->lVgaDelta = ppdev->cxScreen / 4; if (!bAssertModeHardware(ppdev, TRUE)) goto ReturnFalse; DISPDBG((5, "Passed bEnableHardware")); return(TRUE); ReturnFalse: DISPDBG((0, "Failed bEnableHardware")); return(FALSE); } /******************************Public*Routine******************************\ * VOID vDisableHardware * * Undoes anything done in bEnableHardware. * * Note: In an error case, we may call this before bEnableHardware is * completely done. * \**************************************************************************/ VOID vDisableHardware( PDEV* ppdev) { DWORD ReturnedDataLength; VIDEO_MEMORY VideoMemory; VideoMemory.RequestedVirtualAddress = ppdev->pjVga; if (EngDeviceIoControl(ppdev->hDriver, IOCTL_VIDEO_UNMAP_VIDEO_MEMORY, &VideoMemory, sizeof(VIDEO_MEMORY), NULL, 0, &ReturnedDataLength)) { DISPDBG((0, "vDisableHardware failed IOCTL_VIDEO_UNMAP_VIDEO")); } #if !defined(_X86_) VideoMemory.RequestedVirtualAddress = ppdev->pjBase; if (EngDeviceIoControl(ppdev->hDriver, IOCTL_VIDEO_FREE_PUBLIC_ACCESS_RANGES, &VideoMemory, sizeof(VIDEO_MEMORY), NULL, 0, &ReturnedDataLength)) { DISPDBG((0, "vDisableHardware failed IOCTL_VIDEO_FREE_PUBLIC_ACCESS")); } #endif } /******************************Public*Routine******************************\ * VOID vUpdate(ppdev, prcl, pco) * * Updates the screen from the DIB surface for the given rectangle. * Increases the rectangle size if necessary for easy alignment. * * NOTE: Life is made complicated by the fact that we are faking DirectDraw * 'flip' surfaces. When we're asked by GDI to draw, it should be * copied from the shadow buffer to the physical screen only if * DirectDraw is currently 'flipped' to the primary surface. * \**************************************************************************/ VOID vUpdate(PDEV* ppdev, RECTL* prcl, CLIPOBJ* pco) { BYTE* pjBase; RECTL rcl; SURFOBJ* pso; LONG lSrcDelta; BYTE* pjSrcStart; BYTE* pjSrc; LONG lDstDelta; BYTE* pjDstStart; BYTE* pjDst; ULONG cy; ULONG cDwordsPerPlane; ULONG iPage; ULONG i; ULONG ul; pjBase = ppdev->pjBase; if ((pco == NULL) || (pco->iDComplexity == DC_TRIVIAL)) { // We have to clip to the screen dimensions because we may have // been a bit loose when we guessed the bounds of the drawing: rcl.left = max(0, prcl->left); rcl.top = max(0, prcl->top); rcl.right = min(ppdev->cxScreen, prcl->right); rcl.bottom = min(ppdev->cyScreen, prcl->bottom); } else { // We may as well save ourselves some blting by clipping to // the clip object's maximum extent. The clip object's bounds // are guaranteed to be contained within the dimensions of the // screen: rcl.left = max(pco->rclBounds.left, prcl->left); rcl.top = max(pco->rclBounds.top, prcl->top); rcl.right = min(pco->rclBounds.right, prcl->right); rcl.bottom = min(pco->rclBounds.bottom, prcl->bottom); } // Be paranoid: if ((rcl.left >= rcl.right) || (rcl.top >= rcl.bottom)) return; // Align to dwords to keep things simple. rcl.left = (rcl.left) & ~15; rcl.right = (rcl.right + 15) & ~15; lSrcDelta = ppdev->lScreenDelta; pjSrcStart = ppdev->pjScreen + ppdev->fpScreenOffset + (rcl.top * lSrcDelta) + rcl.left; lDstDelta = ppdev->lVgaDelta; pjDstStart = ppdev->pjVga + ppdev->cjVgaOffset + (rcl.top * lDstDelta) + (rcl.left >> 2); cy = (rcl.bottom - rcl.top); cDwordsPerPlane = (rcl.right - rcl.left) >> 4; lSrcDelta -= 4; // Account for per-plane increment WRITE_PORT_UCHAR(pjBase + VGA_BASE + SEQ_ADDR, SEQ_MAP_MASK); do { for (iPage = 0; iPage < 4; iPage++, pjSrcStart++) { WRITE_PORT_UCHAR(pjBase + VGA_BASE + SEQ_DATA, 1 << iPage); pjSrc = pjSrcStart; pjDst = pjDstStart; #if defined(_X86_) _asm { mov esi,pjSrcStart mov edi,pjDstStart mov ecx,cDwordsPerPlane PixelLoop: mov al,[esi+8] mov ah,[esi+12] shl eax,16 mov al,[esi] mov ah,[esi+4] mov [edi],eax add edi,4 add esi,16 dec ecx jnz PixelLoop } #else for (i = cDwordsPerPlane; i != 0; i--) { ul = (*(pjSrc)) | (*(pjSrc + 4) << 8) | (*(pjSrc + 8) << 16) | (*(pjSrc + 12) << 24); WRITE_REGISTER_ULONG((ULONG*) pjDst, ul); pjDst += 4; pjSrc += 16; } #endif } pjSrcStart += lSrcDelta; pjDstStart += lDstDelta; } while (--cy != 0); }