windows-nt/Source/XPSP1/NT/drivers/video/ms/s3/disp/bank.c

1585 lines
48 KiB
C
Raw Normal View History

2020-09-26 03:20:57 -05:00
/******************************Module*Header*******************************\
*
* *******************
* * GDI SAMPLE CODE *
* *******************
*
* Module Name: bank.c
*
* Contains all the banking code for the display driver.
*
* It's helpful not to have to implement all the DDI drawing functionality
* in a driver (who wants to write the code to support true ROP4's with
* arbitrary sized patterns?). Fortunately, we can punt to GDI for any
* drawing we don't want to do. And if GDI can write directly on the frame
* buffer bits, performance won't even be toooo bad.
*
* NT's GDI can draw on any standard format frame buffer. When the entire
* frame buffer can be mapped into main memory, it's very simple to set up:
* the display driver tells GDI the frame buffer format and location, and
* GDI can then just draw directly.
*
* When only one bank of the frame buffer can be mapped into main memory
* at one time (e.g., there is a moveable 64k aperture) things are not
* nearly so easy. For every bank spanned by a drawing operation, we have
* to set the hardware to the bank, and call back to GDI. We tell GDI
* to draw only on the mapped-in bank by mucking with the drawing call's
* CLIPOBJ.
*
* This module contains the code for doing all banking support.
*
* This code supports 8, 16 and 32bpp colour depths, arbitrary bank
* sizes, and handles 'broken rasters' (which happens when the bank size
* is not a multiple of the scan length; some scans will end up being
* split over two separate banks).
*
* Note: If you mess with this code and break it, you can expect to get
* random access violations on call-backs in internal GDI routines
* that are very hard to debug.
*
* Copyright (c) 1993-1998 Microsoft Corporation
\**************************************************************************/
#include "precomp.h"
////////////////////////////////////////////////////////////////////////////
// Old 911/924 Banking
//
// NOTE: It is the caller's responsibility to acquire the CRTC crtical
// section before calling these routines, in all cases!
VOID vOldBankSelectMode( // Note: If this function changes, must
PDEV* ppdev, // change Asm routines!
BANKDATA* pbd,
BANK_MODE bankm)
{
BYTE jMemCfg;
if (bankm == BANK_ON)
{
// Make sure the processor graphics engine is idle before we start
// drawing:
while (INPW(ppdev->pjIoBase, pbd->ulGp_stat_cmd) & 0x0200)
;
}
else if (bankm == BANK_ENABLE)
{
// Enable the memory aperture after exiting full-screen:
OUTP(ppdev->pjIoBase, CRTC_INDEX, S3R1);
jMemCfg = INP(ppdev->pjIoBase, CRTC_DATA);
OUTP(ppdev->pjIoBase, CRTC_DATA, jMemCfg | CPUA_BASE);
}
}
VOID vOldBankMap(
PDEV* ppdev,
BANKDATA* pbd,
LONG iBank)
{
OUTPW(ppdev->pjIoBase, CRTC_INDEX, pbd->ulRegisterLock_35 | ((iBank & 0x0F) << 8));
// Supposedly, there's a chip bug and we have to read this back in:
INP(ppdev->pjIoBase, CRTC_DATA);
}
VOID vOldBankInitialize(
PDEV* ppdev,
BANKDATA* pbd,
BOOL bMmIo)
{
BYTE jMemCfg;
// Enable the memory aperture:
OUTP(ppdev->pjIoBase, CRTC_INDEX, S3R1);
jMemCfg = INP(ppdev->pjIoBase, CRTC_DATA);
OUTP(ppdev->pjIoBase, CRTC_DATA, jMemCfg | CPUA_BASE);
// Read the default values of the registers that we'll be using:
OUTP(ppdev->pjIoBase, CRTC_INDEX, 0x35);
pbd->ulRegisterLock_35
= ((INP(ppdev->pjIoBase, CRTC_DATA) << 8) | 0x35) & ~0x0F00;
pbd->ulGp_stat_cmd = 0x9ae8;
}
////////////////////////////////////////////////////////////////////////////
// New 801/805/805i/928/928PCI Banking
//
// NOTE: It is the caller's responsibility to acquire the CRTC crtical
// section before calling these routines, in all cases!
VOID vNewBankSelectMode( // Note: If this function changes, must
PDEV* ppdev, // change Asm routines!
BANKDATA* pbd,
BANK_MODE bankm)
{
BYTE jMemCfg;
if ((bankm == BANK_ON) || (bankm == BANK_ON_NO_WAIT))
{
//////////////////////////////////////////////////////////////////
// Enable Banking
//
// Make sure the processor graphics engine is idle before we start
// drawing:
if (bankm != BANK_ON_NO_WAIT)
{
do {;} while (INPW(ppdev->pjIoBase, pbd->ulGp_stat_cmd) & 0x0200);
}
// Disable memory mapped I/O:
OUTPW(ppdev->pjIoBase, CRTC_INDEX, pbd->ulExtendedMemoryControl_53);
// Disable enhanced register access and enable fast write buffer:
OUTPW(ppdev->pjIoBase, CRTC_INDEX, pbd->ulSystemConfiguration_40 | 0x0800);
// Enable linear addressing:
OUTPW(ppdev->pjIoBase, CRTC_INDEX, pbd->ulLinearAddressWindowControl_58 | 0x1000);
}
else
{
//////////////////////////////////////////////////////////////////
// Disable Banking
//
// Be it BANK_OFF, BANK_ENABLE, or BANK_DISABLE, we'll turn off
// direct access to the frame buffer.
if (bankm == BANK_ENABLE)
{
// Enable the memory aperture:
OUTP(ppdev->pjIoBase, CRTC_INDEX, S3R1);
jMemCfg = INP(ppdev->pjIoBase, CRTC_DATA);
OUTP(ppdev->pjIoBase, CRTC_DATA, jMemCfg | CPUA_BASE);
}
// Disable linear addressing:
OUTPW(ppdev->pjIoBase, CRTC_INDEX, pbd->ulLinearAddressWindowControl_58);
// Enable enhanced register access and disable fast write buffer:
OUTPW(ppdev->pjIoBase, CRTC_INDEX, pbd->ulSystemConfiguration_40 | 0x0100);
// Enable memory mapped I/O:
OUTPW(ppdev->pjIoBase, CRTC_INDEX, pbd->ulExtendedMemoryControl_53 |
pbd->ulEnableMemoryMappedIo);
}
}
VOID vNewBankMap(
PDEV* ppdev,
BANKDATA* pbd,
LONG iBank)
{
OUTPW(ppdev->pjIoBase, CRTC_INDEX, pbd->ulRegisterLock_35 | ((iBank & 0x0F) << 8));
// The 801/805/928 chipsets have a timing bug where a word OUT cannot
// be used to set register 0x51:
OUTP(ppdev->pjIoBase, CRTC_INDEX, pbd->ulExtendedSystemControl2_51);
OUTP(ppdev->pjIoBase, CRTC_DATA, ((pbd->ulExtendedSystemControl2_51) >> 8) |
((iBank & 0x30) >> 2));
// Supposedly, there's another S3 chip bug and we have to read this
// back in:
INP(ppdev->pjIoBase, CRTC_DATA);
CP_EIEIO();
}
VOID vNewBankInitialize(
PDEV* ppdev,
BANKDATA* pbd,
BOOL bMmIo)
{
// Read the default values of the registers that we'll be using:
OUTP(ppdev->pjIoBase, CRTC_INDEX, 0x35);
pbd->ulRegisterLock_35
= ((INP(ppdev->pjIoBase, CRTC_DATA) << 8) | 0x35) & ~0x0F00;
OUTP(ppdev->pjIoBase, CRTC_INDEX, 0x51);
pbd->ulExtendedSystemControl2_51
= ((INP(ppdev->pjIoBase, CRTC_DATA) << 8) | 0x51) & ~0x0C00;
OUTP(ppdev->pjIoBase, CRTC_INDEX, 0x53);
pbd->ulExtendedMemoryControl_53
= ((INP(ppdev->pjIoBase, CRTC_DATA) << 8) | 0x53) & ~0x1000;
OUTP(ppdev->pjIoBase, CRTC_INDEX, 0x40);
pbd->ulSystemConfiguration_40
= ((INP(ppdev->pjIoBase, CRTC_DATA) << 8) | 0x40) & ~0x0900;
// Only enable memory-mapped I/O if we're really going to use it
// (some cards would crash when memory-mapped I/O was enabled):
pbd->ulEnableMemoryMappedIo = (bMmIo) ? 0x1000 : 0x0000;
// Make sure we use the current window size:
OUTP(ppdev->pjIoBase, CRTC_INDEX, 0x58);
pbd->ulLinearAddressWindowControl_58
= ((INP(ppdev->pjIoBase, CRTC_DATA) << 8) | 0x58) & ~0x1000;
pbd->ulGp_stat_cmd = 0x9ae8;
}
////////////////////////////////////////////////////////////////////////////
// Newer 864/964 Banking
//
// NOTE: It is the caller's responsibility to acquire the CRTC crtical
// section before calling these routines, in all cases!
VOID vNewerBankSelectMode( // Note: If this function changes, must
PDEV* ppdev, // change Asm routines!
BANKDATA* pbd,
BANK_MODE bankm)
{
BYTE jMemCfg;
if ((bankm == BANK_ON) || (bankm == BANK_ON_NO_WAIT))
{
//////////////////////////////////////////////////////////////////
// Enable Banking
//
// Make sure the processor graphics engine is idle before we start
// drawing:
if (bankm != BANK_ON_NO_WAIT)
{
do {;} while (INPW(ppdev->pjIoBase, pbd->ulGp_stat_cmd) & 0x0200);
}
// Disable memory mapped I/O:
OUTPW(ppdev->pjIoBase, CRTC_INDEX, pbd->ulExtendedMemoryControl_53);
// Disable enhanced register access and enable fast write buffer:
OUTPW(ppdev->pjIoBase, CRTC_INDEX, pbd->ulSystemConfiguration_40 | 0x0800);
// Enable linear addressing:
OUTPW(ppdev->pjIoBase, CRTC_INDEX, pbd->ulLinearAddressWindowControl_58 | 0x1000);
}
else
{
//////////////////////////////////////////////////////////////////
// Disable Banking
//
// Be it BANK_OFF, BANK_ENABLE, or BANK_DISABLE, we'll turn off
// direct access to the frame buffer.
if (bankm == BANK_ENABLE)
{
// Enable the memory aperture:
OUTP(ppdev->pjIoBase, CRTC_INDEX, S3R1);
jMemCfg = INP(ppdev->pjIoBase, CRTC_DATA);
OUTP(ppdev->pjIoBase, CRTC_DATA, jMemCfg | CPUA_BASE);
// Since a zero in 'CR6A' causes 'CR31' and 'CR51' to be used
// as the bank index, we have to make sure they map to bank zero:
OUTPW(ppdev->pjIoBase, CRTC_INDEX, pbd->ulRegisterLock_35);
OUTPW(ppdev->pjIoBase, CRTC_INDEX, pbd->ulExtendedSystemControl2_51);
}
// Disable linear addressing:
OUTPW(ppdev->pjIoBase, CRTC_INDEX, pbd->ulLinearAddressWindowControl_58);
// Enable enhanced register access:
OUTPW(ppdev->pjIoBase, CRTC_INDEX, pbd->ulSystemConfiguration_40 | 0x0100);
// Enable memory mapped I/O:
OUTPW(ppdev->pjIoBase, CRTC_INDEX, pbd->ulExtendedMemoryControl_53 | 0x1000);
}
}
VOID vNewerBankMap(
PDEV* ppdev,
BANKDATA* pbd,
LONG iBank)
{
OUTPW(ppdev->pjIoBase, CRTC_INDEX, pbd->ulExtendedSystemControl4_6a | (iBank << 8));
}
VOID vNewerBankInitialize(
PDEV* ppdev,
BANKDATA* pbd,
BOOL bMmIo)
{
// Read the default values of the registers that we'll be using:
pbd->ulExtendedSystemControl4_6a = 0x6a;
OUTP(ppdev->pjIoBase, CRTC_INDEX, 0x35);
pbd->ulRegisterLock_35
= ((INP(ppdev->pjIoBase, CRTC_DATA) << 8) | 0x35) & ~0x0F00;
OUTP(ppdev->pjIoBase, CRTC_INDEX, 0x51);
pbd->ulExtendedSystemControl2_51
= ((INP(ppdev->pjIoBase, CRTC_DATA) << 8) | 0x51) & ~0x0C00;
OUTP(ppdev->pjIoBase, CRTC_INDEX, 0x53);
pbd->ulExtendedMemoryControl_53
= ((INP(ppdev->pjIoBase, CRTC_DATA) << 8) | 0x53) & ~0x1000;
OUTP(ppdev->pjIoBase, CRTC_INDEX, 0x40);
pbd->ulSystemConfiguration_40
= ((INP(ppdev->pjIoBase, CRTC_DATA) << 8) | 0x40) & ~0x0100;
// Make sure we select the current window size:
OUTP(ppdev->pjIoBase, CRTC_INDEX, 0x58);
pbd->ulLinearAddressWindowControl_58
= ((INP(ppdev->pjIoBase, CRTC_DATA) << 8) | 0x58) & ~0x1000;
pbd->ulGp_stat_cmd = 0x9ae8;
}
////////////////////////////////////////////////////////////////////////////
// New MM I/O Banking
//
// NOTE: It is the caller's responsibility to acquire the CRTC crtical
// section before calling these routines, in all cases!
VOID vNwBankSelectMode(
PDEV* ppdev,
BANKDATA* pbd,
BANK_MODE bankm)
{
BYTE jMemCfg;
if (bankm == BANK_ON)
{
do {;} while (INPW(ppdev->pjIoBase, pbd->ulGp_stat_cmd) & 0x0200);
}
else if (bankm == BANK_ENABLE)
{
// Enable the memory aperture:
OUTP(ppdev->pjIoBase, CRTC_INDEX, S3R1);
jMemCfg = INP(ppdev->pjIoBase, CRTC_DATA);
OUTP(ppdev->pjIoBase, CRTC_DATA, jMemCfg | CPUA_BASE);
// Since a zero in 'CR6A' causes 'CR31' and 'CR51' to be used
// as the bank index, we have to make sure they map to bank zero:
OUTPW(ppdev->pjIoBase, CRTC_INDEX, pbd->ulRegisterLock_35);
OUTPW(ppdev->pjIoBase, CRTC_INDEX, pbd->ulExtendedSystemControl2_51);
// Enable linear addressing:
OUTPW(ppdev->pjIoBase, CRTC_INDEX, pbd->ulLinearAddressWindowControl_58 | 0x1000);
// Enable enhanced register access:
OUTPW(ppdev->pjIoBase, CRTC_INDEX, pbd->ulSystemConfiguration_40 | 0x0100);
// Enable memory-mapped I/O:
OUTPW(ppdev->pjIoBase, CRTC_INDEX, pbd->ulExtendedMemoryControl_53 | 0x1000);
}
}
VOID vNwBankMap(
PDEV* ppdev,
BANKDATA* pbd,
LONG iBank)
{
}
VOID vNwBankInitialize(
PDEV* ppdev,
BANKDATA* pbd,
BOOL bMmIo)
{
// Read the default values of the registers that we'll be using:
pbd->ulExtendedSystemControl4_6a = 0x6a;
OUTP(ppdev->pjIoBase, CRTC_INDEX, 0x35);
pbd->ulRegisterLock_35
= ((INP(ppdev->pjIoBase, CRTC_DATA) << 8) | 0x35) & ~0x0F00;
OUTP(ppdev->pjIoBase, CRTC_INDEX, 0x51);
pbd->ulExtendedSystemControl2_51
= ((INP(ppdev->pjIoBase, CRTC_DATA) << 8) | 0x51) & ~0x0C00;
OUTP(ppdev->pjIoBase, CRTC_INDEX, 0x53);
pbd->ulExtendedMemoryControl_53
= ((INP(ppdev->pjIoBase, CRTC_DATA) << 8) | 0x53) & ~0x1000;
OUTP(ppdev->pjIoBase, CRTC_INDEX, 0x40);
pbd->ulSystemConfiguration_40
= ((INP(ppdev->pjIoBase, CRTC_DATA) << 8) | 0x40);
// Make sure we select the current window size:
OUTP(ppdev->pjIoBase, CRTC_INDEX, 0x58);
pbd->ulLinearAddressWindowControl_58
= ((INP(ppdev->pjIoBase, CRTC_DATA) << 8) | 0x58) & ~0x1000;
pbd->ulGp_stat_cmd = 0x9ae8;
}
/******************************Public*Routine******************************\
* BOOL bEnableBanking
*
\**************************************************************************/
BOOL bEnableBanking(
PDEV* ppdev)
{
CLIPOBJ* pcoBank;
SURFOBJ* psoBank;
SIZEL sizl;
HSURF hsurf;
FNBANKINITIALIZE* pfnBankInitialize;
LONG lDelta;
LONG cjBank;
LONG cPower2;
// Create a temporary clip object that we'll use for the bank
// when we're given a Null or DC_TRIVIAL clip object:
pcoBank = EngCreateClip();
if (pcoBank == NULL)
goto ReturnFalse;
// We break every per-bank GDI call-back into simple rectangles:
pcoBank->iDComplexity = DC_RECT;
pcoBank->fjOptions = OC_BANK_CLIP;
// Create a GDI surface that we'll wrap around our bank in
// call-backs:
sizl.cx = ppdev->cxMemory;
sizl.cy = ppdev->cyMemory;
hsurf = (HSURF) EngCreateBitmap(sizl,
ppdev->lDelta,
ppdev->iBitmapFormat,
BMF_TOPDOWN,
ppdev->pjScreen);
// Note that we hook zero calls -- after all, the entire point
// of all this is to have GDI do all the drawing on the bank.
// Once we're done the association, we can leave the surface
// permanently locked:
if ((hsurf == 0) ||
(!EngAssociateSurface(hsurf, ppdev->hdevEng, 0)) ||
(!(psoBank = EngLockSurface(hsurf))))
{
DISPDBG((0, "Failed wrapper surface creation"));
EngDeleteSurface(hsurf);
EngDeleteClip(pcoBank);
goto ReturnFalse;
}
ppdev->pcoBank = pcoBank;
ppdev->psoBank = psoBank;
ppdev->pvBankData = &ppdev->aulBankData[0];
if (ppdev->flCaps & CAPS_NEW_MMIO)
{
ppdev->bankmOnOverlapped = BANK_ON;
ppdev->pfnBankMap = vNwBankMap;
ppdev->pfnBankSelectMode = vNwBankSelectMode;
pfnBankInitialize = vNwBankInitialize;
}
else if (ppdev->flCaps & CAPS_NEWER_BANK_CONTROL)
{
ppdev->bankmOnOverlapped = BANK_ON;
ppdev->pfnBankMap = vNewerBankMap;
ppdev->pfnBankSelectMode = vNewerBankSelectMode;
pfnBankInitialize = vNewerBankInitialize;
}
else if (ppdev->flCaps & CAPS_NEW_BANK_CONTROL)
{
ppdev->bankmOnOverlapped = BANK_ON;
ppdev->pfnBankMap = vNewBankMap;
ppdev->pfnBankSelectMode = vNewBankSelectMode;
pfnBankInitialize = vNewBankInitialize;
}
else
{
ppdev->bankmOnOverlapped = BANK_ON;
ppdev->pfnBankMap = vOldBankMap;
ppdev->pfnBankSelectMode = vOldBankSelectMode;
pfnBankInitialize = vOldBankInitialize;
}
lDelta = ppdev->lDelta;
cjBank = ppdev->cjBank;
ASSERTDD(lDelta > 0, "Bad things happen with negative lDeltas");
ASSERTDD(cjBank > lDelta, "Worse things happen with bad bank sizes");
if (((lDelta & (lDelta - 1)) != 0) || ((cjBank & (cjBank - 1)) != 0))
{
// When either the screen stride or the bank size is not a power
// of two, we have to use the slower 'bBankComputeNonPower2'
// function for bank calculations, 'cause there can be broken
// rasters and stuff:
ppdev->pfnBankCompute = bBankComputeNonPower2;
}
else
{
// We can use the super duper fast bank calculator. Yippie,
// yahoo! (I am easily amused.)
cPower2 = 0;
while (cjBank != lDelta)
{
cjBank >>= 1;
cPower2++;
}
// We've just calculated that cjBank / lDelta = 2 ^ cPower2:
ppdev->cPower2ScansPerBank = cPower2;
while (cjBank != 1)
{
cjBank >>= 1;
cPower2++;
}
// Continuing on, we've calculated that cjBank = 2 ^ cPower2:
ppdev->cPower2BankSizeInBytes = cPower2;
ppdev->pfnBankCompute = bBankComputePower2;
}
// Warm up the hardware:
ACQUIRE_CRTC_CRITICAL_SECTION(ppdev);
pfnBankInitialize(ppdev, ppdev->pvBankData,
ppdev->flCaps & (CAPS_MM_TRANSFER | CAPS_MM_IO));
ppdev->pfnBankSelectMode(ppdev, ppdev->pvBankData, BANK_ENABLE);
RELEASE_CRTC_CRITICAL_SECTION(ppdev);
DISPDBG((5, "Passed bEnableBanking"));
return(TRUE);
ReturnFalse:
DISPDBG((0, "Failed bEnableBanking!"));
return(FALSE);
}
/******************************Public*Routine******************************\
* VOID vDisableBanking
*
\**************************************************************************/
VOID vDisableBanking(PDEV* ppdev)
{
HSURF hsurf;
if (ppdev->psoBank != NULL)
{
hsurf = ppdev->psoBank->hsurf;
EngUnlockSurface(ppdev->psoBank);
EngDeleteSurface(hsurf);
}
if (ppdev->pcoBank != NULL)
EngDeleteClip(ppdev->pcoBank);
}
/******************************Public*Routine******************************\
* VOID vAssertModeBanking
*
\**************************************************************************/
VOID vAssertModeBanking(
PDEV* ppdev,
BOOL bEnable)
{
// Inform the miniport bank code about the change in state:
ACQUIRE_CRTC_CRITICAL_SECTION(ppdev);
ppdev->pfnBankSelectMode(ppdev, ppdev->pvBankData,
bEnable ? BANK_ENABLE : BANK_DISABLE);
RELEASE_CRTC_CRITICAL_SECTION(ppdev);
}
/******************************Public*Routine******************************\
* BOOL bBankComputeNonPower2
*
* Given the bounds of the drawing operation described by 'prclDraw',
* computes the bank number and rectangle bounds for the first engine
* call back.
*
* Returns the bank number, 'prclBank' is the bounds for the first
* call-back, and 'pcjOffset' is the adjustment for 'pvScan0'.
*
* This routine does a couple of divides for the bank calculation. We
* don't use a look-up table for banks because it's not straight forward
* to use with broken rasters, and with large amounts of video memory
* and small banks, the tables could get large. We'd probably use it
* infrequently enough that the memory manager would be swapping it
* in and out whenever we touched it.
*
* Returns TRUE if prclDraw is entirely contained in one bank; FALSE if
* prclDraw spans multiple banks.
*
\**************************************************************************/
BOOL bBankComputeNonPower2( // Type FNBANKCOMPUTE
PDEV* ppdev,
RECTL* prclDraw, // Extents of drawing operation, in absolute
// coordinates
RECTL* prclBank, // Returns bounds of drawing operation for this
// bank, in absolute coordinates
LONG* pcjOffset, // Returns the byte offset for this bank
LONG* piBank) // Returns the bank number
{
LONG cjBufferOffset;
LONG iBank;
LONG cjBank;
LONG cjBankOffset;
LONG cjBankRemainder;
LONG cjScan;
LONG cScansInBank;
LONG cjScanRemainder;
LONG lDelta;
BOOL bOneBank;
bOneBank = FALSE;
lDelta = ppdev->lDelta;
cjBufferOffset = prclDraw->top * lDelta
+ CONVERT_TO_BYTES(prclDraw->left, ppdev);
cjBank = ppdev->cjBank;
// iBank = cjBufferOffset / cjBank;
// cjBankOffset = cjBufferOffset % cjBank;
QUOTIENT_REMAINDER(cjBufferOffset, cjBank, iBank, cjBankOffset);
*piBank = iBank;
*pcjOffset = iBank * cjBank;
cjBankRemainder = cjBank - cjBankOffset;
cjScan = CONVERT_TO_BYTES((prclDraw->right - prclDraw->left),
ppdev);
if (cjBankRemainder < cjScan)
{
// Oh no, we've got a broken raster!
prclBank->left = prclDraw->left;
prclBank->right = prclDraw->left +
CONVERT_FROM_BYTES(cjBankRemainder, ppdev);
prclBank->top = prclDraw->top;
prclBank->bottom = prclDraw->top + 1;
}
else
{
// cScansInBank = cjBankRemainder / lDelta;
// cjScanRemainder = cjBankRemainder % lDelta;
ASSERTDD(lDelta > 0, "We assume positive lDelta here");
QUOTIENT_REMAINDER(cjBankRemainder, lDelta,
cScansInBank, cjScanRemainder);
if (cjScanRemainder >= cjScan)
{
// The bottom scan of the bank may be broken, but it breaks after
// any drawing we'll be doing on that scan. So we can simply
// add the scan to this bank:
cScansInBank++;
}
prclBank->left = prclDraw->left;
prclBank->right = prclDraw->right;
prclBank->top = prclDraw->top;
prclBank->bottom = prclDraw->top + cScansInBank;
if (prclBank->bottom >= prclDraw->bottom)
{
prclBank->bottom = prclDraw->bottom;
bOneBank = TRUE;
}
}
return(bOneBank);
}
/******************************Public*Routine******************************\
* BOOL bBankComputePower2
*
* Functions the same as 'bBankComputeNonPower2', except that it is
* an accelerated special case for when both the screen stride and bank
* size are powers of 2.
*
\**************************************************************************/
BOOL bBankComputePower2( // Type FNBANKCOMPUTE
PDEV* ppdev,
RECTL* prclDraw, // Extents of drawing operation, in absolute
// coordinates
RECTL* prclBank, // Returns bounds of drawing operation for this
// bank, in absolute coordinates
LONG* pcjOffset, // Returns the byte offset for this bank
LONG* piBank) // Returns the bank number
{
LONG iBank;
LONG yTopNextBank;
BOOL bOneBank;
iBank = prclDraw->top >> ppdev->cPower2ScansPerBank;
yTopNextBank = (iBank + 1) << ppdev->cPower2ScansPerBank;
*piBank = iBank;
*pcjOffset = iBank << ppdev->cPower2BankSizeInBytes;
prclBank->left = prclDraw->left;
prclBank->right = prclDraw->right;
prclBank->top = prclDraw->top;
prclBank->bottom = yTopNextBank;
bOneBank = FALSE;
if (prclBank->bottom >= prclDraw->bottom)
{
prclBank->bottom = prclDraw->bottom;
bOneBank = TRUE;
}
return(bOneBank);
}
/******************************Public*Routine******************************\
* VOID vBankStart
*
* Given the bounds of the drawing operation described by 'prclDraw' and
* the original clip object, maps in the first bank and returns in
* 'pbnk->pco' and 'pbnk->pso' the CLIPOBJ and SURFOBJ to be passed to the
* engine for the first banked call-back.
*
* Note: This routine only supports the screen being the destination, and
* not the source. We have a separate, faster routine for doing
* SRCCOPY reads from the screen, so it isn't worth the extra code
* size to implement.
*
\**************************************************************************/
VOID vBankStart(
PDEV* ppdev, // Physical device information.
RECTL* prclDraw, // Rectangle bounding the draw area, in relative
// coordinates. Note that 'left' and 'right'
// should be set for correct handling with broken
// rasters.
CLIPOBJ* pco, // Original drawing clip object (may be modified).
BANK* pbnk) // Resulting bank information.
{
LONG cjOffset;
LONG xOffset;
LONG yOffset;
xOffset = ppdev->xOffset;
yOffset = ppdev->yOffset;
if ((pco == NULL) || (pco->iDComplexity == DC_TRIVIAL))
{
pco = ppdev->pcoBank;
// Reset the clipping flag to trivial because we may have left
// it as rectangular in a previous call:
pco->iDComplexity = DC_TRIVIAL;
// At the same time we convert to absolute coordinates, make sure
// we won't try to enumerate past the bounds of the screen:
pbnk->rclDraw.left = prclDraw->left + xOffset;
pbnk->rclDraw.right = prclDraw->right + xOffset;
pbnk->rclDraw.top
= max(0, prclDraw->top + yOffset);
pbnk->rclDraw.bottom
= min(ppdev->cyMemory, prclDraw->bottom + yOffset);
}
else
{
pbnk->rclSaveBounds = pco->rclBounds;
pbnk->iSaveDComplexity = pco->iDComplexity;
pbnk->fjSaveOptions = pco->fjOptions;
// Let GDI know that it has to pay attention to the clip object:
pco->fjOptions |= OC_BANK_CLIP;
// We have to honour the original clip object's rclBounds, so
// intersect the drawing region with it, then convert to absolute
// coordinates:
pbnk->rclDraw.left
= max(prclDraw->left, pco->rclBounds.left) + xOffset;
pbnk->rclDraw.right
= min(prclDraw->right, pco->rclBounds.right) + xOffset;
pbnk->rclDraw.top
= max(prclDraw->top, pco->rclBounds.top) + yOffset;
pbnk->rclDraw.bottom
= min(prclDraw->bottom, pco->rclBounds.bottom) + yOffset;
}
if ((pbnk->rclDraw.left > pbnk->rclDraw.right)
|| (pbnk->rclDraw.top > pbnk->rclDraw.bottom))
{
// It's conceivable that we could get a situation where we have
// an empty draw rectangle.
pbnk->rclDraw.left = 0;
pbnk->rclDraw.right = 0;
pbnk->rclDraw.top = 0;
pbnk->rclDraw.bottom = 0;
}
if (!ppdev->pfnBankCompute(ppdev, &pbnk->rclDraw, &pco->rclBounds,
&cjOffset, &pbnk->iBank))
{
// The drawing operation spans multiple banks. If the original
// clip object was marked as trivial, we have to make sure to
// change it to rectangular so that GDI knows to pay attention
// to the bounds of the bank:
if (pco->iDComplexity == DC_TRIVIAL)
pco->iDComplexity = DC_RECT;
}
pbnk->ppdev = ppdev;
pbnk->pco = pco;
pbnk->pso = ppdev->psoBank;
// Convert rclBounds and pvScan0 from absolute coordinates back to
// relative. When GDI calculates where to start drawing, it computes
// pjDst = pso->pvScan0 + y * pso->lDelta + CONVERT_TO_BYTES(x, ppdev), where 'x'
// and 'y' are relative coordinates. We'll muck with pvScan0 to get
// it pointing to the correct spot in the bank:
pbnk->pso->pvScan0 = ppdev->pjScreen - cjOffset
+ yOffset * ppdev->lDelta
+ CONVERT_TO_BYTES(xOffset, ppdev);
pbnk->pso->lDelta = ppdev->lDelta; // Other functions muck with this value
ASSERTDD((((ULONG_PTR) pbnk->pso->pvScan0) & 3) == 0,
"Off-screen bitmaps must be dword aligned");
pco->rclBounds.left -= xOffset;
pco->rclBounds.right -= xOffset;
pco->rclBounds.top -= yOffset;
pco->rclBounds.bottom -= yOffset;
// Enable banking and map in bank iBank:
ACQUIRE_CRTC_CRITICAL_SECTION(ppdev);
ppdev->pfnBankSelectMode(ppdev, ppdev->pvBankData, BANK_ON);
ppdev->pfnBankMap(ppdev, ppdev->pvBankData, pbnk->iBank);
RELEASE_CRTC_CRITICAL_SECTION(ppdev);
}
/******************************Public*Routine******************************\
* BOOL bBankEnum
*
* If there is another bank to be drawn on, maps in the bank and returns
* TRUE and the CLIPOBJ and SURFOBJ to be passed in the banked call-back.
*
* If there were no more banks to be drawn, returns FALSE.
*
\**************************************************************************/
BOOL bBankEnum(
BANK* pbnk)
{
LONG iBank;
LONG cjOffset;
PDEV* ppdev;
CLIPOBJ* pco;
LONG xOffset;
LONG yOffset;
ppdev = pbnk->ppdev;
pco = pbnk->pco;
xOffset = ppdev->xOffset;
yOffset = ppdev->yOffset;
// We check here to see if we have to handle the second part of
// a broken raster. Recall that pbnk->rclDraw is in absolute
// coordinates, but pco->rclBounds is in relative coordinates:
if (pbnk->rclDraw.right - xOffset != pco->rclBounds.right)
{
// The clip object's 'top' and 'bottom' are already correct:
pco->rclBounds.left = pco->rclBounds.right;
pco->rclBounds.right = pbnk->rclDraw.right - xOffset;
pbnk->pso->pvScan0 = (BYTE*) pbnk->pso->pvScan0 - ppdev->cjBank;
pbnk->iBank++;
ACQUIRE_CRTC_CRITICAL_SECTION(ppdev);
ppdev->pfnBankMap(ppdev, ppdev->pvBankData, pbnk->iBank);
RELEASE_CRTC_CRITICAL_SECTION(ppdev);
return(TRUE);
}
if (pbnk->rclDraw.bottom > pco->rclBounds.bottom + yOffset)
{
// Advance the drawing area 'top' to account for the bank we've
// just finished, and map in the new bank:
pbnk->rclDraw.top = pco->rclBounds.bottom + yOffset;
ppdev->pfnBankCompute(ppdev, &pbnk->rclDraw, &pco->rclBounds,
&cjOffset, &iBank);
// Convert rclBounds back from absolute to relative coordinates:
pco->rclBounds.left -= xOffset;
pco->rclBounds.right -= xOffset;
pco->rclBounds.top -= yOffset;
pco->rclBounds.bottom -= yOffset;
// If we just finished handling a broken raster, we've already
// got the bank mapped in:
if (iBank != pbnk->iBank)
{
pbnk->iBank = iBank;
pbnk->pso->pvScan0 = (BYTE*) pbnk->pso->pvScan0 - ppdev->cjBank;
ACQUIRE_CRTC_CRITICAL_SECTION(ppdev);
ppdev->pfnBankMap(ppdev, ppdev->pvBankData, iBank);
RELEASE_CRTC_CRITICAL_SECTION(ppdev);
}
return(TRUE);
}
// We're done! Turn off banking and reset the clip object if necessary:
ACQUIRE_CRTC_CRITICAL_SECTION(ppdev);
ppdev->pfnBankSelectMode(ppdev, ppdev->pvBankData, BANK_OFF);
RELEASE_CRTC_CRITICAL_SECTION(ppdev);
if (pco != ppdev->pcoBank)
{
pco->rclBounds = pbnk->rclSaveBounds;
pco->iDComplexity = pbnk->iSaveDComplexity;
pco->fjOptions = pbnk->fjSaveOptions;
}
return(FALSE);
}
/******************************Public*Routine******************************\
* VOID vAlignedCopy
*
* Copies the given portion of a bitmap, using dword alignment for the
* screen. Note that this routine has no notion of banking.
*
* Updates ppjDst and ppjSrc to point to the beginning of the next scan.
*
\**************************************************************************/
VOID vAlignedCopy(
PDEV* ppdev,
BYTE** ppjDst,
LONG lDstDelta,
BYTE** ppjSrc,
LONG lSrcDelta,
LONG cjScan,
LONG cyScan,
BOOL bDstIsScreen)
{
BYTE* pjDst;
BYTE* pjSrc;
LONG cjMiddle;
LONG culMiddle;
LONG cjStartPhase;
LONG cjEndPhase;
pjSrc = *ppjSrc;
pjDst = *ppjDst;
cjStartPhase = (LONG)((0 - ((bDstIsScreen) ? (ULONG_PTR) pjDst
: (ULONG_PTR) pjSrc)) & 3);
cjMiddle = cjScan - cjStartPhase;
if (cjMiddle < 0)
{
cjStartPhase = 0;
cjMiddle = cjScan;
}
lSrcDelta -= cjScan;
lDstDelta -= cjScan; // Account for middle
cjEndPhase = cjMiddle & 3;
culMiddle = cjMiddle >> 2;
if (DIRECT_ACCESS(ppdev))
{
LONG i;
///////////////////////////////////////////////////////////////////
// Portable bus-aligned copy
//
// 'memcpy' usually aligns to the destination, so we could call
// it for that case, but unfortunately we can't be sure. We
// always want to align to the frame buffer:
CP_MEMORY_BARRIER();
if (bDstIsScreen)
{
// Align to the destination (implying that the source may be
// unaligned):
for (; cyScan > 0; cyScan--)
{
for (i = cjStartPhase; i > 0; i--)
{
*pjDst++ = *pjSrc++;
}
for (i = culMiddle; i > 0; i--)
{
*((ULONG*) pjDst) = *((ULONG UNALIGNED *) pjSrc);
pjSrc += sizeof(ULONG);
pjDst += sizeof(ULONG);
}
for (i = cjEndPhase; i > 0; i--)
{
*pjDst++ = *pjSrc++;
}
pjSrc += lSrcDelta;
pjDst += lDstDelta;
}
}
else
{
// Align to the source (implying that the destination may be
// unaligned):
for (; cyScan > 0; cyScan--)
{
for (i = cjStartPhase; i > 0; i--)
{
*pjDst++ = *pjSrc++;
}
if (ppdev->flCaps & CAPS_BAD_DWORD_READS)
{
// #9 and Diamond 764 boards randomly fail in different
// spots on the HCTs, unless we do byte reads:
for (i = culMiddle; i > 0; i--)
{
*(pjDst) = *(pjSrc);
*(pjDst + 1) = *(pjSrc + 1);
*(pjDst + 2) = *(pjSrc + 2);
*(pjDst + 3) = *(pjSrc + 3);
pjSrc += sizeof(ULONG);
pjDst += sizeof(ULONG);
}
}
else
{
for (i = culMiddle; i > 0; i--)
{
if (ppdev->flCaps & CAPS_FORCE_DWORD_REREADS)
{
//
// On fast MIPS machines, the cpu overdrives
// the card, so this code slows it down as
// little as possible while checking for
// consistency.
//
ULONG cnt = 4;
while (cnt)
{
ULONG tmp = *((volatile ULONG*) (pjSrc));
*((ULONG UNALIGNED *) pjDst) =
*((volatile ULONG*) (pjSrc));
if (tmp == *((volatile ULONG UNALIGNED *) pjDst))
break;
--cnt;
}
}
else
{
*((ULONG UNALIGNED *) pjDst) = *((ULONG*) (pjSrc));
}
pjSrc += sizeof(ULONG);
pjDst += sizeof(ULONG);
}
}
for (i = cjEndPhase; i > 0; i--)
{
*pjDst++ = *pjSrc++;
}
pjSrc += lSrcDelta;
pjDst += lDstDelta;
}
}
*ppjSrc = pjSrc; // Save the updated pointers
*ppjDst = pjDst;
}
else
{
LONG i;
///////////////////////////////////////////////////////////////////
// No direct dword reads bus-aligned copy
//
// Because we support the S3 on ancient Jensen Alpha's, we also
// have to support a sparse view of the frame buffer -- which
// means using the 'ioaccess.h' macros.
//
// We also go through this code path if doing dword reads would
// crash a non-x86 system.
MEMORY_BARRIER();
if (bDstIsScreen)
{
// Align to the destination (implying that the source may be
// unaligned):
for (; cyScan > 0; cyScan--)
{
for (i = cjStartPhase; i > 0; i--)
{
WRITE_REGISTER_UCHAR(pjDst, *pjSrc);
pjSrc++;
pjDst++;
}
for (i = culMiddle; i > 0; i--)
{
WRITE_REGISTER_ULONG(pjDst, *((ULONG UNALIGNED *) pjSrc));
pjSrc += sizeof(ULONG);
pjDst += sizeof(ULONG);
}
for (i = cjEndPhase; i > 0; i--)
{
WRITE_REGISTER_UCHAR(pjDst, *pjSrc);
pjSrc++;
pjDst++;
}
pjSrc += lSrcDelta;
pjDst += lDstDelta;
}
}
else
{
// Align to the source (implying that the destination may be
// unaligned):
for (; cyScan > 0; cyScan--)
{
for (i = cjStartPhase; i > 0; i--)
{
*pjDst = READ_REGISTER_UCHAR(pjSrc);
pjSrc++;
pjDst++;
}
for (i = culMiddle; i > 0; i--)
{
// There are some board 864/964 boards where we can't
// do dword reads from the frame buffer without
// crashing the system.
*((ULONG UNALIGNED *) pjDst) =
((ULONG) READ_REGISTER_UCHAR(pjSrc + 3) << 24) |
((ULONG) READ_REGISTER_UCHAR(pjSrc + 2) << 16) |
((ULONG) READ_REGISTER_UCHAR(pjSrc + 1) << 8) |
((ULONG) READ_REGISTER_UCHAR(pjSrc));
pjSrc += sizeof(ULONG);
pjDst += sizeof(ULONG);
}
for (i = cjEndPhase; i > 0; i--)
{
*pjDst = READ_REGISTER_UCHAR(pjSrc);
pjSrc++;
pjDst++;
}
pjSrc += lSrcDelta;
pjDst += lDstDelta;
}
}
*ppjSrc = pjSrc; // Save the updated pointers
*ppjDst = pjDst;
}
}
/******************************Public*Routine******************************\
* VOID vPutBits
*
* Copies the bits from the given surface to the screen, using the memory
* aperture. Must be pre-clipped.
*
\**************************************************************************/
VOID vPutBits(
PDEV* ppdev,
SURFOBJ* psoSrc,
RECTL* prclDst, // Absolute coordinates!
POINTL* pptlSrc) // Absolute coordinates!
{
RECTL rclDraw;
RECTL rclBank;
LONG iBank;
LONG cjOffset;
LONG cyScan;
LONG lDstDelta;
LONG lSrcDelta;
BYTE* pjDst;
BYTE* pjSrc;
LONG cjScan;
LONG iNewBank;
LONG cjRemainder;
// We need a local copy of 'rclDraw' because we'll be iteratively
// modifying 'top' and passing the modified rectangle back into
// bBankComputeNonPower2:
rclDraw = *prclDst;
ASSERTDD((rclDraw.left >= 0) &&
(rclDraw.top >= 0) &&
(rclDraw.right <= ppdev->cxMemory) &&
(rclDraw.bottom <= ppdev->cyMemory),
"Rectangle wasn't fully clipped");
//
// Wait for engine idle.
//
IO_GP_WAIT(ppdev);
// Compute the first bank, enable banking, then map in iBank:
ACQUIRE_CRTC_CRITICAL_SECTION(ppdev);
ppdev->pfnBankCompute(ppdev, &rclDraw, &rclBank, &cjOffset, &iBank);
ppdev->pfnBankSelectMode(ppdev, ppdev->pvBankData, BANK_ON);
ppdev->pfnBankMap(ppdev, ppdev->pvBankData, iBank);
RELEASE_CRTC_CRITICAL_SECTION(ppdev);
// Calculate the pointer to the upper-left corner of both rectangles:
lDstDelta = ppdev->lDelta;
pjDst = ppdev->pjScreen + rclDraw.top * lDstDelta
+ CONVERT_TO_BYTES(rclDraw.left, ppdev)
- cjOffset;
lSrcDelta = psoSrc->lDelta;
pjSrc = (BYTE*) psoSrc->pvScan0 + pptlSrc->y * lSrcDelta
+ CONVERT_TO_BYTES(pptlSrc->x, ppdev);
while (TRUE)
{
cjScan = CONVERT_TO_BYTES((rclBank.right - rclBank.left), ppdev);
cyScan = (rclBank.bottom - rclBank.top);
vAlignedCopy(ppdev, &pjDst, lDstDelta, &pjSrc, lSrcDelta, cjScan, cyScan,
TRUE); // Screen is the destination
if (rclDraw.right != rclBank.right)
{
// Handle the second part of the broken raster:
iBank++;
ACQUIRE_CRTC_CRITICAL_SECTION(ppdev);
ppdev->pfnBankMap(ppdev, ppdev->pvBankData, iBank);
RELEASE_CRTC_CRITICAL_SECTION(ppdev);
// Number of bytes we've yet to do on the broken scan:
cjRemainder = CONVERT_TO_BYTES((rclDraw.right - rclBank.right),
ppdev);
// Account for the fact that we're now one bank lower in the
// destination:
pjDst -= ppdev->cjBank;
// Implicitly back up the source and destination pointers to the
// unfinished portion of the scan:
if (DIRECT_ACCESS(ppdev))
{
memcpy(pjDst + (cjScan - lDstDelta),
pjSrc + (cjScan - lSrcDelta),
cjRemainder);
}
else
{
BYTE* pjTmpDst = pjDst + (cjScan - lDstDelta);
BYTE* pjTmpSrc = pjSrc + (cjScan - lSrcDelta);
vAlignedCopy(ppdev, &pjTmpDst, 0, &pjTmpSrc, 0, cjRemainder, 1,
TRUE); // Screen is the destination
}
}
if (rclDraw.bottom > rclBank.bottom)
{
rclDraw.top = rclBank.bottom;
ppdev->pfnBankCompute(ppdev, &rclDraw, &rclBank, &cjOffset,
&iNewBank);
// If we just handled the second part of a broken raster,
// then we've already got the bank correctly mapped in:
if (iNewBank != iBank)
{
pjDst -= ppdev->cjBank;
iBank = iNewBank;
ACQUIRE_CRTC_CRITICAL_SECTION(ppdev);
ppdev->pfnBankMap(ppdev, ppdev->pvBankData, iBank);
RELEASE_CRTC_CRITICAL_SECTION(ppdev);
}
}
else
{
// We're done! Turn off banking and leave:
ACQUIRE_CRTC_CRITICAL_SECTION(ppdev);
ppdev->pfnBankSelectMode(ppdev, ppdev->pvBankData, BANK_OFF);
RELEASE_CRTC_CRITICAL_SECTION(ppdev);
return;
}
}
}
/******************************Public*Routine******************************\
* VOID vGetBits
*
* Copies the bits to the given surface from the screen, using the memory
* aperture. Must be pre-clipped.
*
\**************************************************************************/
VOID vGetBits(
PDEV* ppdev,
SURFOBJ* psoDst,
RECTL* prclDst, // Absolute coordinates!
POINTL* pptlSrc) // Absolute coordinates!
{
RECTL rclDraw;
RECTL rclBank;
LONG iBank;
LONG cjOffset;
LONG cyScan;
LONG lDstDelta;
LONG lSrcDelta;
BYTE* pjDst;
BYTE* pjSrc;
LONG cjScan;
LONG iNewBank;
LONG cjRemainder;
rclDraw.left = pptlSrc->x;
rclDraw.top = pptlSrc->y;
rclDraw.right = rclDraw.left + (prclDst->right - prclDst->left);
rclDraw.bottom = rclDraw.top + (prclDst->bottom - prclDst->top);
ASSERTDD((rclDraw.left >= 0) &&
(rclDraw.top >= 0) &&
(rclDraw.right <= ppdev->cxMemory) &&
(rclDraw.bottom <= ppdev->cyMemory),
"Rectangle wasn't fully clipped");
//
// Wait for engine idle.
//
IO_GP_WAIT(ppdev);
// Compute the first bank, enable banking, then map in iBank.
ACQUIRE_CRTC_CRITICAL_SECTION(ppdev);
ppdev->pfnBankCompute(ppdev, &rclDraw, &rclBank, &cjOffset, &iBank);
ppdev->pfnBankSelectMode(ppdev, ppdev->pvBankData, BANK_ON);
ppdev->pfnBankMap(ppdev, ppdev->pvBankData, iBank);
RELEASE_CRTC_CRITICAL_SECTION(ppdev);
// Calculate the pointer to the upper-left corner of both rectangles:
lSrcDelta = ppdev->lDelta;
pjSrc = ppdev->pjScreen + rclDraw.top * lSrcDelta
+ CONVERT_TO_BYTES(rclDraw.left, ppdev)
- cjOffset;
lDstDelta = psoDst->lDelta;
pjDst = (BYTE*) psoDst->pvScan0 + prclDst->top * lDstDelta
+ CONVERT_TO_BYTES(prclDst->left, ppdev);
while (TRUE)
{
cjScan = CONVERT_TO_BYTES((rclBank.right - rclBank.left), ppdev);
cyScan = (rclBank.bottom - rclBank.top);
vAlignedCopy(ppdev, &pjDst, lDstDelta, &pjSrc, lSrcDelta, cjScan, cyScan,
FALSE); // Screen is the source
if (rclDraw.right != rclBank.right)
{
// Handle the second part of the broken raster:
iBank++;
ACQUIRE_CRTC_CRITICAL_SECTION(ppdev);
ppdev->pfnBankMap(ppdev, ppdev->pvBankData, iBank);
RELEASE_CRTC_CRITICAL_SECTION(ppdev);
// Number of bytes we've yet to do on the broken scan:
cjRemainder = CONVERT_TO_BYTES((rclDraw.right - rclBank.right),
ppdev);
// Account for the fact that we're now one bank lower in the
// source:
pjSrc -= ppdev->cjBank;
// Implicitly back up the source and destination pointers to the
// unfinished portion of the scan. Note that we don't have to
// advance the pointers because they're already pointing to the
// beginning of the next scan:
if (DIRECT_ACCESS(ppdev))
{
memcpy(pjDst + (cjScan - lDstDelta),
pjSrc + (cjScan - lSrcDelta),
cjRemainder);
}
else
{
BYTE* pjTmpDst = pjDst + (cjScan - lDstDelta);
BYTE* pjTmpSrc = pjSrc + (cjScan - lSrcDelta);
vAlignedCopy(ppdev, &pjTmpDst, 0, &pjTmpSrc, 0, cjRemainder, 1,
FALSE); // Screen is the source
}
}
if (rclDraw.bottom > rclBank.bottom)
{
rclDraw.top = rclBank.bottom;
ppdev->pfnBankCompute(ppdev, &rclDraw, &rclBank, &cjOffset,
&iNewBank);
// If we just handled the second part of a broken raster,
// then we've already got the bank correctly mapped in:
if (iNewBank != iBank)
{
pjSrc -= ppdev->cjBank;
iBank = iNewBank;
ACQUIRE_CRTC_CRITICAL_SECTION(ppdev);
ppdev->pfnBankMap(ppdev, ppdev->pvBankData, iBank);
RELEASE_CRTC_CRITICAL_SECTION(ppdev);
}
}
else
{
// We're done! Turn off banking and leave:
ACQUIRE_CRTC_CRITICAL_SECTION(ppdev);
ppdev->pfnBankSelectMode(ppdev, ppdev->pvBankData, BANK_OFF);
RELEASE_CRTC_CRITICAL_SECTION(ppdev);
return;
}
}
}