File:  [WindowsNT SDKs] / ntddk / src / video / displays / s3 / bitblt.c
Revision 1.1.1.1 (vendor branch): download - view: text, annotated - select for diffs
Thu Aug 9 18:31:12 2018 UTC (7 years, 9 months ago) by root
Branches: msft, MAIN
CVS tags: ntddk-nov-1993, HEAD
Microsoft Windows NT Build 511 (DDK SDK) 11-01-1993

/******************************Module*Header*******************************\
* Module Name: bitblt.c
*
* Banked Frame Buffer bitblit
*
* Copyright (c) 1992 Microsoft Corporation
*
\**************************************************************************/

#include "driver.h"

#define SRCBM_CACHE 1

BOOL    bTest = FALSE;

ULONG   nColorBrushes,
        nColorBrushCacheHit,
        nColorBrushExpansionCacheHit,
        nColorBrushCacheInvalidations;

ULONG   nMonoBrushes,
        nMonoBrushCacheHit,
        nMonoBrushExpansionCacheHit,
        nMonoBrushCacheInvalidations;

ULONG   n8BppBitmaps,
        n8BppBmCacheHits;

ULONG   n1BppBitmaps,
        n1BppBmCacheHits;


extern  ULONG   nSsbMovedToHostFromSrcBmCache;

BYTE   ajMonoPatPlanes[] = {0x1, 0x2, 0x4, 0x8, 0x10, 0x20, 0x40, 0x80};


/// Define the A vector polynomial bits
//
// Each bit corresponds to one of the terms in the polynomial
//
// Rop(D,S,P) = a + a D + a S + a P + a  DS + a  DP + a  SP + a   DSP
//               0   d     s     p     ds      dp      sp      dsp

#define AVEC_NOT    0x01
#define AVEC_D      0x02
#define AVEC_S      0x04
#define AVEC_P      0x08
#define AVEC_DS     0x10
#define AVEC_DP     0x20
#define AVEC_SP     0x40
#define AVEC_DSP    0x80

#define AVEC_NEED_SOURCE  (AVEC_S | AVEC_DS | AVEC_SP | AVEC_DSP)
#define AVEC_NEED_PATTERN (AVEC_P | AVEC_DP | AVEC_SP | AVEC_DSP)

#define BB_TARGET_SCREEN    0x0001
#define BB_TARGET_ONLY      0x0002
#define BB_SOURCE_COPY      0x0004
#define BB_PATTERN_COPY     0x0008


/******************************Public*Data*********************************\
* ROP translation table
*
* Translates the usual ternary rop into A-vector notation.  Each bit in
* this new notation corresponds to a term in a polynomial translation of
* the rop.
*
* Rop(D,S,P) = a + a D + a S + a P + a  DS + a  DP + a  SP + a   DSP
*               0   d     s     p     ds      dp      sp      dsp
\**************************************************************************/

BYTE gajRop[] =
{
    0x00, 0xff, 0xb2, 0x4d, 0xd4, 0x2b, 0x66, 0x99,
    0x90, 0x6f, 0x22, 0xdd, 0x44, 0xbb, 0xf6, 0x09,
    0xe8, 0x17, 0x5a, 0xa5, 0x3c, 0xc3, 0x8e, 0x71,
    0x78, 0x87, 0xca, 0x35, 0xac, 0x53, 0x1e, 0xe1,
    0xa0, 0x5f, 0x12, 0xed, 0x74, 0x8b, 0xc6, 0x39,
    0x30, 0xcf, 0x82, 0x7d, 0xe4, 0x1b, 0x56, 0xa9,
    0x48, 0xb7, 0xfa, 0x05, 0x9c, 0x63, 0x2e, 0xd1,
    0xd8, 0x27, 0x6a, 0x95, 0x0c, 0xf3, 0xbe, 0x41,
    0xc0, 0x3f, 0x72, 0x8d, 0x14, 0xeb, 0xa6, 0x59,
    0x50, 0xaf, 0xe2, 0x1d, 0x84, 0x7b, 0x36, 0xc9,
    0x28, 0xd7, 0x9a, 0x65, 0xfc, 0x03, 0x4e, 0xb1,
    0xb8, 0x47, 0x0a, 0xf5, 0x6c, 0x93, 0xde, 0x21,
    0x60, 0x9f, 0xd2, 0x2d, 0xb4, 0x4b, 0x06, 0xf9,
    0xf0, 0x0f, 0x42, 0xbd, 0x24, 0xdb, 0x96, 0x69,
    0x88, 0x77, 0x3a, 0xc5, 0x5c, 0xa3, 0xee, 0x11,
    0x18, 0xe7, 0xaa, 0x55, 0xcc, 0x33, 0x7e, 0x81,
    0x80, 0x7f, 0x32, 0xcd, 0x54, 0xab, 0xe6, 0x19,
    0x10, 0xef, 0xa2, 0x5d, 0xc4, 0x3b, 0x76, 0x89,
    0x68, 0x97, 0xda, 0x25, 0xbc, 0x43, 0x0e, 0xf1,
    0xf8, 0x07, 0x4a, 0xb5, 0x2c, 0xd3, 0x9e, 0x61,
    0x20, 0xdf, 0x92, 0x6d, 0xf4, 0x0b, 0x46, 0xb9,
    0xb0, 0x4f, 0x02, 0xfd, 0x64, 0x9b, 0xd6, 0x29,
    0xc8, 0x37, 0x7a, 0x85, 0x1c, 0xe3, 0xae, 0x51,
    0x58, 0xa7, 0xea, 0x15, 0x8c, 0x73, 0x3e, 0xc1,
    0x40, 0xbf, 0xf2, 0x0d, 0x94, 0x6b, 0x26, 0xd9,
    0xd0, 0x2f, 0x62, 0x9d, 0x04, 0xfb, 0xb6, 0x49,
    0xa8, 0x57, 0x1a, 0xe5, 0x7c, 0x83, 0xce, 0x31,
    0x38, 0xc7, 0x8a, 0x75, 0xec, 0x13, 0x5e, 0xa1,
    0xe0, 0x1f, 0x52, 0xad, 0x34, 0xcb, 0x86, 0x79,
    0x70, 0x8f, 0xc2, 0x3d, 0xa4, 0x5b, 0x16, 0xe9,
    0x08, 0xf7, 0xba, 0x45, 0xdc, 0x23, 0x6e, 0x91,
    0x98, 0x67, 0x2a, 0xd5, 0x4c, 0xb3, 0xfe, 0x01
};

BOOL b8BppHostToScrnCachedWithRop(
    SURFOBJ  *psoTrg,
    SURFOBJ  *psoSrc,
    CLIPOBJ  *pco,
    XLATEOBJ *pxlo,
    RECTL    *prclTrg,
    POINTL   *pptlSrc,
    WORD     s3Rop);

BOOL b1BppHostToScrnCachedWithRop(
    SURFOBJ  *psoTrg,
    SURFOBJ  *psoSrc,
    CLIPOBJ  *pco,
    XLATEOBJ *pxlo,
    RECTL    *prclTrg,
    POINTL   *pptlSrc,
    WORD     s3Rop);

BOOL b8BppHostToScrnWithRop(
    SURFOBJ  *psoTrg,
    SURFOBJ  *psoSrc,
    CLIPOBJ  *pco,
    XLATEOBJ *pxlo,
    RECTL    *prclTrg,
    POINTL   *pptlSrc,
    WORD     s3Rop);

VOID vLowLevel8BppHostToScrnWithRop(
    PPDEV   ppdev,
    PPOINT  pptTrg,
    PSIZE   psizBlt,
    PWORD   pwFirstWord,
    INT     lSrcDelta,
    XLATEOBJ *pxlo,
    WORD    s3Rop);

BOOL b1BppHostToScrnWithRop(
    SURFOBJ  *psoTrg,
    SURFOBJ  *psoSrc,
    CLIPOBJ  *pco,
    XLATEOBJ *pxlo,
    RECTL    *prclTrg,
    POINTL   *pptlSrc,
    WORD     s3Rop);

VOID vLowLevel1BppHostToScrnWithRop(
    PPDEV   ppdev,
    PPOINT  pptTrg,
    PSIZE   psizBlt,
    INT     xSrc,
    PWORD   pwFirstWord,
    INT     lSrcDelta,
    XLATEOBJ *pxlo,
    WORD    s3Rop);

BOOL b4BppHostToScrnWithRop(
    SURFOBJ  *psoTrg,
    SURFOBJ  *psoSrc,
    CLIPOBJ  *pco,
    XLATEOBJ *pxlo,
    RECTL    *prclTrg,
    POINTL   *pptlSrc,
    WORD     s3Rop);


VOID vLowLevel4BppHostToScrnWithRop(
    PPDEV   ppdev,
    PPOINT  pptTrg,
    PSIZE   psizBlt,
    INT     xSrc,
    PWORD   pwFirstWord,
    INT     lSrcDelta,
    XLATEOBJ *pxlo,
    WORD    s3Rop);


WORD wXlateSrcRop3toS3Rop(PPDEV pdev, WORD rop3);

ROP4 bXlatePatRop4toS3Rop(
    PPDEV pdev,
    ROP4  rop4);

BOOL bDownLoadBrushIntoColorCache(
    PPDEV      ppdev,
    PS3BRUSH   ps3Brush,
    PPOINT     ppt);

BOOL bDownLoadBrushIntoMonoCache(
    PPDEV      ppdev,
    PS3BRUSH   p3Brush,
    PXYZPOINT  pxyzPt);

BOOL bExpandColorBrushIntoHorzCache(
    PPDEV      ppdev,
    PPOINT     ppt);

BOOL bExpandMonoBrushIntoHorzCache(
    PPDEV      ppdev,
    PXYZPOINT  pxyzPtl,
    INT        zHorz);

BOOL bExpandColorBrushIntoVertCache(
    PPDEV      ppdev,
    PPOINT     ppt);

BOOL bExpandMonoBrushIntoVertCache(
    PPDEV      ppdev,
    PXYZPOINT  pxyzPt,
    INT        zVert);

BOOL bColorHorzCacheToScreen(
    PPDEV       ppdev,
    PS3BRUSH    ps3Brush,
    PPOINT      pptBrushOrg,
    PPOINT      pptDest,
    PSIZE       psizDest,
    WORD        s3ForeRop,
    WORD        s3BackRop);

BOOL bColorExpandHorzCacheToScreen(
    PPDEV       ppdev,
    PS3BRUSH    ps3Brush,
    INT         zHorz,
    PPOINT      pptBrushOrg,
    PPOINT      pptDest,
    PSIZE       psizDest,
    WORD        s3ForeRop,
    WORD        s3BackRop);

BOOL bCpyColorHorzCacheToScreen(
    PPDEV       ppdev,
    PS3BRUSH    ps3Brush,
    PPOINT      pptBrushOrg,
    PPOINT      pptDest,
    PSIZE       psizDest);

BOOL bCpyColorExpandHorzCacheToScreen(
    PPDEV       ppdev,
    PS3BRUSH    ps3Brush,
    INT         zHorz,
    PPOINT      pptBrushOrg,
    PPOINT      pptDest,
    PSIZE       psizDest);

BOOL bColorVertCacheToScreen(
    PPDEV       ppdev,
    PS3BRUSH    ps3Brush,
    PPOINT      pptBrushOrg,
    PPOINT      pptDest,
    PSIZE       psizDest,
    WORD        s3ForeRop,
    WORD        s3BackRop);

BOOL bColorExpandVertCacheToScreen(
    PPDEV       ppdev,
    PS3BRUSH    ps3Brush,
    INT         zHorz,
    PPOINT      pptBrushOrg,
    PPOINT      pptDest,
    PSIZE       psizDest,
    WORD        s3ForeRop,
    WORD        s3BackRop);


BOOL bPuntBlit(
    SURFOBJ  *psoTrg,
    SURFOBJ  *psoSrc,
    SURFOBJ  *psoMask,
    CLIPOBJ  *pco,
    XLATEOBJ *pxlo,
    RECTL    *prclTrg,
    POINTL   *pptlSrc,
    POINTL   *pptlMask,
    BRUSHOBJ *pbo,
    POINTL   *pptlBrush,
    ROP4     rop4);

BOOL bScrnToScrnPuntBlit(
    SURFOBJ  *psoTrg,
    SURFOBJ  *psoSrc,
    SURFOBJ  *psoMask,
    CLIPOBJ  *pco,
    XLATEOBJ *pxlo,
    RECTL    *prclTrg,
    POINTL   *pptlSrc,
    POINTL   *pptlMask,
    BRUSHOBJ *pbo,
    POINTL   *pptlBrush,
    ROP4     rop4);


BOOL bSpecialBlits(
    SURFOBJ  *psoTrg,
    SURFOBJ  *psoSrc,
    SURFOBJ  *psoMask,
    CLIPOBJ  *pco,
    XLATEOBJ *pxlo,
    RECTL    *prclTrg,
    POINTL   *pptlSrc,
    POINTL   *pptlMask,
    BRUSHOBJ *pbo,
    POINTL   *pptlBrush,
    ROP4     rop4);

BOOL bScrnToScrnWithRop(
    SURFOBJ  *psoTrg,
    SURFOBJ  *psoSrc,
    CLIPOBJ  *pco,
    RECTL    *prclTrg,
    POINTL   *pptlSrc,
    WORD     s3Rop);


VOID S3ScrnToScrnBlt(
    PPDEV   ppdev,
    INT     xSrc,
    INT     ySrc,
    INT     xTrg,
    INT     yTrg,
    INT     width,
    INT     height,
    WORD    s3Rop,
    WORD    Cmd);

BOOL bPatternSolid(
    SURFOBJ  *psoTrg,
    SURFOBJ  *psoSrc,
    SURFOBJ  *psoMask,
    CLIPOBJ  *pco,
    XLATEOBJ *pxlo,
    RECTL    *prclTrg,
    POINTL   *pptlSrc,
    POINTL   *pptlMask,
    BRUSHOBJ *pbo,
    POINTL   *pptlBrush,
    ROP4     rop4);

VOID vS3SolidPattern(
    PPDEV   ppdev,
    INT     left,
    INT     top,
    INT     width,
    INT     height,
    INT     color,
    WORD    s3Mix);


BOOL bPatternBrush(
    SURFOBJ  *psoTrg,
    CLIPOBJ  *pco,
    RECTL    *prclTrg,
    BRUSHOBJ *pbo,
    POINTL   *pptlBrush,
    ROP4     rop4);



/*****************************************************************************
 * S3 (Banked Frame Buffer) DrvCopyBits
 *
 *  An I/O through the transfer register take 43 clocks.
 *  A Memory access on the S3 takes 23 clocks, the memory window wins,
 *  so this transfers bits through the memory window for some things.
 ****************************************************************************/
BOOL DrvCopyBits(
    SURFOBJ  *psoDest,
    SURFOBJ  *psoSrc,
    CLIPOBJ  *pco,
    XLATEOBJ *pxlo,
    RECTL    *prclDest,
    POINTL   *pptlSrc)
{
#if defined(_X86_) || defined(i386)

    INT     cy;
    RECTL   rclScans;
    RECTL   rclDest, rclTmp;
    SURFOBJ *pso;
    PPDEV   ppdev;
    BOOL    bRet, bResetBounds;

    DISPDBG((3,"S3.DLL!DrvCopyBits - Entry\n"));

    bResetBounds = FALSE;

    rclDest = *prclDest;

    cy = prclDest->bottom - prclDest->top;

    if (psoDest->iType == STYPE_DEVICE)
    {
        ppdev = (PPDEV) psoDest->dhsurf;

        GPWAIT();

        if (pco == NULL)
            pco = ppdev->pcoDefault;

        if (psoSrc->iType == STYPE_DEVICE)
        {
            // At this point we know it's a screen to screen copy.
            // If a color translation needs to be applied then go through ScrnToScrnPuntBlit

            if ((pxlo == NULL) || (pxlo->flXlate & XO_TRIVIAL))
            {
                bScrnToScrnWithRop(psoDest, psoSrc, pco, prclDest, pptlSrc, OVERPAINT);
                return (TRUE);
            }
            else
            {
                bRet = bScrnToScrnPuntBlit(psoDest,
                                           psoSrc,
                                           NULL,
                                           pco,
                                           pxlo,
                                           prclDest,
                                           pptlSrc,
                                           NULL,
                                           NULL,
                                           NULL,
                                           0x0000CCCC);

                return (bRet);
            }
        }

        else if (psoSrc->lDelta < 0)
        {
            rclScans.top    = prclDest->top;
            rclScans.bottom = rclScans.top + cy;

            pso = ppdev->pSurfObj;
            psoDest = pso;
            bBankEnumStart(ppdev, &rclScans, pso, pco);
        }

        else
        {
            switch (psoSrc->iBitmapFormat)
            {
                case BMF_DEVICE:
                case BMF_8BPP:
                    bRet = b8BppHostToScrnCachedWithRop(psoDest, psoSrc,
                                                        pco, pxlo,
                                                        prclDest, pptlSrc,
                                                        OVERPAINT);
                    break;

                case BMF_1BPP:
                    bRet = b1BppHostToScrnCachedWithRop(psoDest, psoSrc,
                                                        pco, pxlo,
                                                        prclDest, pptlSrc,
                                                        OVERPAINT);
                    break;

                case BMF_4BPP:
                    bRet = b4BppHostToScrnWithRop(psoDest, psoSrc, pco, pxlo,
                                              prclDest, pptlSrc, OVERPAINT);
                    break;

                case BMF_16BPP:
                case BMF_24BPP:
                case BMF_32BPP:
                case BMF_4RLE:
                case BMF_8RLE:
                default:
                    bRet = FALSE;
            }

            if (bRet == TRUE)
            {
                return (TRUE);
            }

            rclScans.top    = prclDest->top;
            rclScans.bottom = rclScans.top + cy;

            pso = ppdev->pSurfObj;
            psoDest = pso;
            bBankEnumStart(ppdev, &rclScans, pso, pco);
        }
    }
    else
    {
        rclScans.top    = pptlSrc->y;
        rclScans.bottom = pptlSrc->y + cy;

        ppdev = (PPDEV) psoSrc->dhsurf;

        GPWAIT();

        if (pco == NULL)
        {
            pco = ppdev->pcoDefault;

            // BUGBUG: This mucking around with pcoDefaults's rclBounds is
            // no longer required by bSrcBankEnumStart:

            rclTmp = pco->rclBounds;
            pco->rclBounds = *prclDest;
            bResetBounds = TRUE;
        }

        pso = ppdev->pSurfObj;
        psoSrc = pso;
        bSrcBankEnumStart(ppdev, &rclScans, pso, pco, prclDest);
    }

    do
    {
        bRet = EngCopyBits(psoDest, psoSrc, pco, pxlo, &rclDest, pptlSrc);

    } while (bRet && (bBankEnum(ppdev, &rclScans, pso, pco)));

    bBankEnumEnd(ppdev, pso, pco);

    if (bResetBounds)
    {
        ppdev->pcoDefault->rclBounds = rclTmp;
    }

    return(bRet);

#else

    // A DrvCopyBits is just a simplified DrvBitBlt:

    return(DrvBitBlt(psoDest,
                     psoSrc,
                     NULL,              // psoMask
                     pco,
                     pxlo,
                     prclDest,
                     pptlSrc,
                     NULL,              // pptlMask
                     NULL,              // pbo
                     NULL,              // pptlBrush
                     0x0000CCCC));      // mix = SRCCOPY

#endif
}




/*****************************************************************************
 * S3 DrvBitBlt
 ****************************************************************************/
BOOL DrvBitBlt(
SURFOBJ  *psoTrg,
SURFOBJ  *psoSrc,
SURFOBJ  *psoMask,
CLIPOBJ  *pco,
XLATEOBJ *pxlo,
RECTL  *prclTrg,
POINTL    *pptlSrc,
POINTL    *pptlMask,
BRUSHOBJ *pbo,
POINTL    *pptlBrush,
ROP4    rop4)

{
    BOOL    b;
    PPDEV   ppdev;

    DISPDBG((3,"S3.DLL!DrvBitBlt - Entry\n"));

    // Since we don't support Device-Format Bitmaps, either the source
    // or the destination (or both) will be the device surface:

    if (psoTrg->iType == STYPE_DEVICE)
        ppdev = (PPDEV) psoTrg->dhpdev;
    else
        ppdev = (PPDEV) psoSrc->dhpdev;

    // Protect the driver from a potentially NULL clip object.

    if (pco == NULL)
    {
        pco = ppdev->pcoDefault;
    }

    if (bTest == FALSE)
    {
        b = bSpecialBlits(psoTrg, psoSrc, psoMask,
                          pco, pxlo,
                          prclTrg, pptlSrc, pptlMask,
                          pbo, pptlBrush,
                          rop4);
    }
    else
    {
        b = FALSE;
    }

    if (b != TRUE)
    {
        bPuntBlit(psoTrg,
                  psoSrc,
                  psoMask,
                  pco,
                  pxlo,
                  prclTrg,
                  pptlSrc,
                  pptlMask,
                  pbo,
                  pptlBrush,
                  rop4);
    }

    return (TRUE);
}

/*****************************************************************************
 * S3 General purpose blit handler.  This routine will handle any blit.
 * Albeit slow, but it will be handled.
 *
 *  Returns TRUE if the blit was handled.
 ****************************************************************************/
BOOL bPuntBlit(
    SURFOBJ  *psoTrg,
    SURFOBJ  *psoSrc,
    SURFOBJ  *psoMask,
    CLIPOBJ  *pco,
    XLATEOBJ *pxlo,
    RECTL    *prclTrg,
    POINTL   *pptlSrc,
    POINTL   *pptlMask,
    BRUSHOBJ *pbo,
    POINTL   *pptlBrush,
    ROP4     rop4)
{


#if defined(_X86_) || defined(i386)

    RECTL   rclScans;
    RECTL   rclTrg;
    PPDEV   ppdevTrg, ppdevSrc, ppdevBank;
    SURFOBJ *psoBank;
    BOOL    bRet;
    PPDEV   ppdev;

    DISPDBG((2,"S3.DLL!bPuntBlit - Entry\n"));

    if (psoTrg->iType == STYPE_DEVICE)
        ppdev = (PPDEV) psoTrg->dhsurf;
    else
        ppdev = (PPDEV) psoSrc->dhsurf;

    // Wait for the S3 coprocessor to finish with the VRAM before we touch it through
    // the apperature.

    GPWAIT();

    rclTrg = *prclTrg;

    rclScans.top    = prclTrg->top;
    rclScans.bottom = prclTrg->bottom;

    // Get the correct surface object for the target and the source

    if ((psoTrg->iType == STYPE_DEVICE) &&
        ((psoSrc == NULL) || (psoSrc->iType != STYPE_DEVICE)))
    {
        ppdevTrg  = (PPDEV) psoTrg->dhsurf;
        psoTrg    = ppdevTrg->pSurfObj;

        bBankEnumStart(ppdevTrg, &rclScans, psoTrg, pco);
        ppdevBank = ppdevTrg;
        psoBank = psoTrg;

    }

    else if ((psoTrg->iType != STYPE_DEVICE) &&
             (psoSrc != NULL) && (psoSrc->iType == STYPE_DEVICE))
    {
        ppdevSrc  = (PPDEV) psoSrc->dhsurf;
        psoSrc    = ppdevSrc->pSurfObj;

        rclScans.top    = pptlSrc->y;
        rclScans.bottom = pptlSrc->y + (prclTrg->bottom - prclTrg->top);

        bSrcBankEnumStart(ppdevSrc, &rclScans, psoSrc, pco, prclTrg);
        ppdevBank = ppdevSrc;
        psoBank = psoSrc;

    }
    else
    {
        bRet = bScrnToScrnPuntBlit(psoTrg,
                                   psoSrc,
                                   psoMask,
                                   pco,
                                   pxlo,
                                   prclTrg,
                                   pptlSrc,
                                   pptlMask,
                                   pbo,
                                   pptlBrush,
                                   rop4);
        return (bRet);
    }

    do
    {
        bRet = EngBitBlt(psoTrg,
                  psoSrc,
                  psoMask,
                  pco,
                  pxlo,
                  &rclTrg,
                  pptlSrc,
                  pptlMask,
                  pbo,
                  pptlBrush,
                  rop4);

    } while ((bBankEnum(ppdevBank, &rclScans, psoBank, pco)) && bRet);

    bBankEnumEnd(ppdevBank, psoBank, pco);

    return bRet;

#else

    BOOL    bRet;
    PPDEV   ppdev;
    RECTL   rclSrc;

    if (psoTrg->iType == STYPE_DEVICE)
    {
        ppdev = (PPDEV) psoTrg->dhsurf;

        if ((psoSrc != NULL) && (psoSrc->iType == STYPE_DEVICE))
        {
            // -------------------------------------------------------
            // Handle screen-to-screen blit:

            // We have to copy both the source rectangle and the destination
            // rectangle to the temporary bitmap (the destination rectangle
            // is needed if there's complex clipping or if there's a ROP that
            // affects need the destination).
            //
            // We simply copy the smallest entire rectangle containing both
            // the source and the destination rectangles:

            rclSrc.left   = min(pptlSrc->x, prclTrg->left);
            rclSrc.top    = min(pptlSrc->y, prclTrg->top);
            rclSrc.right  = max(pptlSrc->x + prclTrg->right - prclTrg->left,
                                prclTrg->right);
            rclSrc.bottom = max(pptlSrc->y + prclTrg->bottom - prclTrg->top,
                                prclTrg->bottom);

            vPuntGetBits(ppdev, psoSrc, &rclSrc);

            // Now do the copy entirely on the temporary bitmap surface:

            bRet = EngBitBlt(ppdev->psoTemp,
                     ppdev->psoTemp,
                     psoMask,
                     pco,
                     pxlo,
                     prclTrg,
                     pptlSrc,
                     pptlMask,
                     pbo,
                     pptlBrush,
                     rop4);

            // Just have to copy the destination back to the surface:

            if (bRet)
            {
                vPuntPutBits(ppdev, psoTrg, prclTrg);
            }
        }
        else
        {
            // -------------------------------------------------------
            // Handle bitmap-to-screen blit:

            // Make a copy of the destination rectangle in case the ROP
            // modifies the destination:

            vPuntGetBits(ppdev, psoTrg, prclTrg);

            // Do the blit operation on the temporary bitmap:

            bRet = EngBitBlt(ppdev->psoTemp,
                     psoSrc,
                     psoMask,
                     pco,
                     pxlo,
                     prclTrg,
                     pptlSrc,
                     pptlMask,
                     pbo,
                     pptlBrush,
                     rop4);

            // Copy everything back to the surface:

            if (bRet)
            {
                vPuntPutBits(ppdev, psoTrg, prclTrg);
            }
        }
    }
    else
    {
        // -------------------------------------------------------
        // Handle screen-to-bitmap blit:

        ppdev = (PPDEV) psoSrc->dhsurf;

        // Copy the source rectangle to the temporary bitmap, then get
        // GDI to blit to the target surface:

        rclSrc.left   = pptlSrc->x;
        rclSrc.top    = pptlSrc->y;
        rclSrc.right  = pptlSrc->x + (prclTrg->right - prclTrg->left);
        rclSrc.bottom = pptlSrc->y + (prclTrg->bottom - prclTrg->top);

        vPuntGetBits(ppdev, psoSrc, &rclSrc);

        bRet = EngBitBlt(psoTrg,
                 ppdev->psoTemp,
                 psoMask,
                 pco,
                 pxlo,
                 prclTrg,
                 pptlSrc,
                 pptlMask,
                 pbo,
                 pptlBrush,
                 rop4);
    }

    vResetS3Clipping(ppdev);    // Always reset after using vPunt routines

    return(bRet);

#endif
}

/******************************************************************************
 * bScrnToScrnPuntBlit - Screen To Screen Punt Blit
 *****************************************************************************/
BOOL bScrnToScrnPuntBlit(
    SURFOBJ  *psoTrg,
    SURFOBJ  *psoSrc,
    SURFOBJ  *psoMask,
    CLIPOBJ  *pco,
    XLATEOBJ *pxlo,
    RECTL    *prclTrg,
    POINTL   *pptlSrc,
    POINTL   *pptlMask,
    BRUSHOBJ *pbo,
    POINTL   *pptlBrush,
    ROP4     rop4)
{

    SIZEL       sizl;
    HSURF       hsurfBm;
    SURFOBJ     *psoTempSrc;
    WORD        cmd;
    INT         i, cxInWords;
    PBYTE       pb;
    PWORD       pw;
    PPDEV       ppdev;
    RECTL       rclScans, rclTrg;
    BOOL        bRet;
    POINTL      ptlTempSrc;

    DISPDBG((2,"S3.DLL!bScrnToScrnPuntBlit - Entry\n"));

    // Pickup our pdev and a pointer to the surface object.

    ppdev  = (PPDEV) psoTrg->dhsurf;
    psoTrg = ppdev->pSurfObj;

    // Set the default bounding rectangle for the bank manager.

    rclTrg = *prclTrg;

    // Create a temporary surface and bitmap.

    sizl.cx = prclTrg->right  - prclTrg->left;
    sizl.cy = prclTrg->bottom - prclTrg->top;

    hsurfBm = (HSURF) EngCreateBitmap(sizl,
                                      sizl.cx,
                                      BMF_8BPP,
                                      BMF_TOPDOWN,
                                      NULL);
    if (hsurfBm == NULL)
    {
        DISPDBG((0, "S3.DLL!bScrnToScrnPuntBlit - EngCreateBitmap failed\n"));
        return(FALSE);
    }

    // Get a pointer to the surface object.

    psoTempSrc = EngLockSurface(hsurfBm);

    // Copy the bits into it.

    if (!(sizl.cx & 0x01))
    {
        cmd =   RECTANGLE_FILL     | BYTE_SWAP      | BUS_SIZE_16 |
                DRAWING_DIR_TBLRXM | DIR_TYPE_XY    | WAIT |
                DRAW               | LAST_PIXEL_ON  | READ;
    }
    else
    {
        cmd =   RECTANGLE_FILL     | BUS_SIZE_8 |
                DRAWING_DIR_TBLRXM | DIR_TYPE_XY    | WAIT |
                DRAW               | LAST_PIXEL_ON  | READ;
    }

    FIFOWAIT(FIFO_7_EMPTY);

    TEST_AND_SET_RD_MASK(0xff);
    OUTPW (MULTIFUNC_CNTL, (DATA_EXTENSION | ALL_ONES));

    OUTPW (CUR_X, pptlSrc->x);
    OUTPW (CUR_Y, pptlSrc->y);
    OUTPW (RECT_WIDTH, (sizl.cx - 1));
    OUTPW (MULTIFUNC_CNTL, (RECT_HEIGHT | (sizl.cy - 1)));

    GPWAIT();

    OUTPW (CMD, cmd);

    // Wait for the Data Available.

    while (!(inpw(GP_STAT) & READ_DATA_AVAILABLE));

    // Now transfer the data from the screen to the host memory bitmap.

    if (!(sizl.cx & 0x01))
    {
        cxInWords = (sizl.cx + 1) / 2;
        pw = (PWORD) psoTempSrc->pvScan0;
        for (i = 0; i < sizl.cy; i++)
        {
            vDataPortIn(ppdev, pw, cxInWords);
            ((PBYTE) pw) += psoTempSrc->lDelta;
        }
    }
    else
    {
        pb = psoTempSrc->pvScan0;
        for (i = 0; i < sizl.cy; i++)
        {
            vDataPortInB(ppdev, pb, sizl.cx);
            pb += psoTempSrc->lDelta;
        }
    }

    // Set the new source position

    ptlTempSrc.x = 0;
    ptlTempSrc.y = 0;

    // Do the banked blit operation.

    rclScans.top    = prclTrg->top;
    rclScans.bottom = prclTrg->bottom;

    bBankEnumStart(ppdev, &rclScans, psoTrg, pco);

    do
    {
        bRet = EngBitBlt(psoTrg,
                  psoTempSrc,
                  psoMask,
                  pco,
                  pxlo,
                  &rclTrg,
                  &ptlTempSrc,
                  pptlMask,
                  pbo,
                  pptlBrush,
                  rop4);

    } while ((bBankEnum(ppdev, &rclScans, psoTrg, pco)) && bRet);

    bBankEnumEnd(ppdev, psoTrg, pco);

    // free the temp bitmap.

    EngUnlockSurface(psoTempSrc);
    EngDeleteSurface(hsurfBm);

    return (bRet);

}

/*****************************************************************************
 * S3 Special case Blit handler
 *
 *  Returns TRUE if the blit was handled.
 ****************************************************************************/
BOOL bSpecialBlits(
    SURFOBJ     *psoTrg,
    SURFOBJ     *psoSrc,
    SURFOBJ     *psoMask,
    CLIPOBJ     *pco,
    XLATEOBJ    *pxlo,
    RECTL   *prclTrg,
    POINTL  *pptlSrc,
    POINTL  *pptlMask,
    BRUSHOBJ    *pbo,
    POINTL  *pptlBrush,
    ROP4  rop4)
{
    BOOL    bRet;
    PPDEV   ppdev;
    ROP4    s3Rop4;
    WORD    avecRop, s3Rop;
    BRUSHOBJ bo;

    bRet = FALSE;

    if (psoTrg->iType == STYPE_DEVICE)
        ppdev = (PPDEV) psoTrg->dhsurf;
    else
        ppdev = (PPDEV) psoSrc->dhsurf;

    // NOTE: If the ForeRop and BackRop are the same implicitly
    //       there is no mask.

    // First test for copy opperations.

    if (rop4 == 0x0000CCCC)
    {
        if ((psoTrg->iType == STYPE_DEVICE) && (psoSrc->iType == STYPE_DEVICE))
        {
            if ((pxlo == NULL) || (pxlo->flXlate & XO_TRIVIAL))
            {
                bRet = bScrnToScrnWithRop(psoTrg, psoSrc, pco, prclTrg, pptlSrc, OVERPAINT);
                return(bRet);
            }
            else
            {
                return (FALSE);
            }
        }

    }

    // Check for a mask.  If there is one, at this time, reject the
    // acceleration.

    if (psoMask != NULL)
    {
        return (FALSE);
    }

    // If the background and foreground rops are not the same
    // reject the blit.

    if (((rop4 & 0xFF00) >> 8) != (rop4 & 0xff))
    {
        return (FALSE);
    }

    // Pickup the avec for some quick decision later.

    avecRop = gajRop[(rop4 & 0xff)];

    // Check for a DIB.  If this is a DIB reject the blit.
    // BUGBUG: At some future date (post BETA2) this should be optimized.

    if ((avecRop & AVEC_NEED_SOURCE) && (psoSrc->lDelta < 0))
    {
        return (FALSE);
    }

    // Check for whitness or blackness

    if (((rop4 & 0xff) == 0xff) ||
        ((rop4 & 0xff) == 0x00))
    {
        if ((rop4 & 0xff) == 0xff)
            s3Rop4 = LOGICAL_1;
        else
            s3Rop4 = LOGICAL_0;

        bRet = bPatternSolid(psoTrg, psoSrc, psoMask,
                          pco, pxlo,
                          prclTrg, pptlSrc, pptlMask,
                          pbo, pptlBrush,
                          s3Rop4);
        return (bRet);
    }

    // Check for Dest Invert

    if ((rop4 & 0xff) == 0x55)
    {
        s3Rop4 = SCREEN_XOR_NEW;
        bo.iSolidColor = 0xffff;

        bRet = bPatternSolid(psoTrg, psoSrc, psoMask,
                          pco, pxlo,
                          prclTrg, pptlSrc, pptlMask,
                          &bo, pptlBrush,
                          s3Rop4);
        return (bRet);

    }


    // Check for brushes.

    if ((avecRop & AVEC_NEED_PATTERN) && (!(avecRop & AVEC_NEED_SOURCE)))
    {
        // Translate the rop from GDI to S3.

        s3Rop4 = bXlatePatRop4toS3Rop(ppdev, rop4);

        // Check for a Solid Brush.

        if (pbo == NULL || pbo->iSolidColor != -1)
        {
            bRet = bPatternSolid(psoTrg, psoSrc, psoMask,
                              pco, pxlo,
                              prclTrg, pptlSrc, pptlMask,
                              pbo, pptlBrush,
                              s3Rop4);
        }

        // Handle this as a Pattern Brush.

        else
        {
            bRet = bPatternBrush(psoTrg, pco, prclTrg, pbo, pptlBrush, s3Rop4);
        }

    }

    // Check if we may be able to optimize for screen to screen or
    // host to screen blits.

    else if ((!(avecRop & AVEC_NEED_PATTERN)) &&
            (avecRop & AVEC_NEED_SOURCE))
    {
        s3Rop = wXlateSrcRop3toS3Rop(ppdev, LOWORD(rop4));
        if (s3Rop != 0)
        {
            if ((psoTrg->iType == STYPE_DEVICE) &&
                (psoSrc->iType == STYPE_DEVICE) &&
                ((pxlo == NULL) || (pxlo->flXlate & XO_TRIVIAL)))
            {
                bRet = bScrnToScrnWithRop(psoTrg, psoSrc, pco, prclTrg,
                                       pptlSrc, s3Rop);
            }
            else if (psoSrc->iType == STYPE_BITMAP)
            {
                switch (psoSrc->iBitmapFormat)
                {
                    case BMF_DEVICE:
                    case BMF_8BPP:
                        bRet = b8BppHostToScrnCachedWithRop(psoTrg,
                                                            psoSrc,
                                                            pco,
                                                            pxlo,
                                                            prclTrg,
                                                            pptlSrc,
                                                            s3Rop);
                        break;

                    case BMF_1BPP:
                        bRet = b1BppHostToScrnCachedWithRop(psoTrg,
                                                            psoSrc,
                                                            pco,
                                                            pxlo,
                                                            prclTrg,
                                                            pptlSrc,
                                                            s3Rop);
                        break;

                    case BMF_4BPP:
                        bRet = b4BppHostToScrnWithRop(psoTrg,
                                                      psoSrc,
                                                      pco,
                                                      pxlo,
                                                      prclTrg,
                                                      pptlSrc,
                                                      s3Rop);
                        break;

                    case BMF_16BPP:
                    case BMF_24BPP:
                    case BMF_32BPP:
                    case BMF_4RLE:
                    case BMF_8RLE:
                    default:
                        bRet = FALSE;
                        break;
                }
            }
        }

        else
        {
            DISPDBG((0,"S3.DLL!bSpecialBlits - Missed SrcBlt opportunity - rop: 0x%x\n",
                        rop4));
        }
    }

    else if ((rop4 & 0xff) == 0xc0)     // Merge Copy
    {
        if ((psoTrg->iType == STYPE_DEVICE) &&
            (psoSrc->iType == STYPE_DEVICE) &&
            ((pxlo == NULL) || (pxlo->flXlate & XO_TRIVIAL)))

        {
            bRet = bScrnToScrnWithRop(psoTrg, psoSrc,
                                      pco, prclTrg, pptlSrc,
                                      OVERPAINT);

            if (bRet)
            {
                if (pbo == NULL || pbo->iSolidColor != -1)
                {
                    bRet = bPatternSolid(psoTrg, psoSrc, psoMask,
                                      pco, pxlo,
                                      prclTrg, pptlSrc, pptlMask,
                                      pbo, pptlBrush,
                                      SCREEN_AND_NEW);
                }
                else
                {
                    bRet = bPatternBrush(psoTrg, pco, prclTrg,
                                         pbo, pptlBrush,
                                         (SCREEN_AND_NEW << 8) | SCREEN_AND_NEW);
                }
            }
        }
        else if (psoSrc->iType == STYPE_BITMAP)
        {
            switch (psoSrc->iBitmapFormat)
            {
                case BMF_DEVICE:
                case BMF_8BPP:
                    bRet = b8BppHostToScrnCachedWithRop(psoTrg,
                                                        psoSrc,
                                                        pco,
                                                        pxlo,
                                                        prclTrg,
                                                        pptlSrc,
                                                        OVERPAINT);
                    break;

                case BMF_1BPP:
                    bRet = b1BppHostToScrnCachedWithRop(psoTrg,
                                                        psoSrc,
                                                        pco,
                                                        pxlo,
                                                        prclTrg,
                                                        pptlSrc,
                                                        OVERPAINT);
                    break;

                case BMF_4BPP:
                    bRet = b4BppHostToScrnWithRop(psoTrg,
                                                  psoSrc,
                                                  pco,
                                                  pxlo,
                                                  prclTrg,
                                                  pptlSrc,
                                                  OVERPAINT);
                    break;

                case BMF_16BPP:
                case BMF_24BPP:
                case BMF_32BPP:
                case BMF_4RLE:
                case BMF_8RLE:
                default:
                    bRet = FALSE;
                    break;
            }

            if (bRet)
            {
                if (pbo == NULL || pbo->iSolidColor != -1)
                {
                    bRet = bPatternSolid(psoTrg, psoSrc, psoMask,
                                      pco, pxlo,
                                      prclTrg, pptlSrc, pptlMask,
                                      pbo, pptlBrush,
                                      SCREEN_AND_NEW);
                }
                else
                {
                    bRet = bPatternBrush(psoTrg, pco, prclTrg,
                                         pbo, pptlBrush,
                                         (SCREEN_AND_NEW << 8) | SCREEN_AND_NEW);
                }
            }
        }
    }



    return (bRet);
}


/*****************************************************************************
 * S3 Screen to Screen with a Rop
 *
 *  Returns TRUE if the blit was handled.
 ****************************************************************************/
BOOL bScrnToScrnWithRop(
    SURFOBJ  *psoTrg,
    SURFOBJ  *psoSrc,
    CLIPOBJ  *pco,
    RECTL    *prclTrg,
    POINTL   *pptlSrc,
    WORD     s3Rop)
{
    INT     width, height, xTrg, yTrg, xSrc, ySrc, cx, cy;
    WORD    cmd, wDirCode;
    RECTL   rclSrc, rclTrg;
    ULONG   DirClip;
    BOOL    bMore, bIntersect, bClipRequired;
    UINT    i;
    ENUMRECTS8   EnumRects8;
    PPDEV   ppdev;
    RECTL   rclBounds;
    BYTE    iDComplexity;

    // Get the pdev.

    ppdev = (PPDEV) psoTrg->dhpdev;

    // There are two completely different code paths.
    // for the complex and non-complex clip cases.
    // This is because most of the direction and intersection
    // calculations need to be done after the clipping.

    if ((iDComplexity = pco->iDComplexity) != DC_COMPLEX)
    {
        // Make a copy of the target, since we will have to change
        // it for clipping.

        rclTrg = *prclTrg;

        if (iDComplexity == DC_RECT)
        {
            rclBounds = pco->rclBounds;

            // First handle the trivial rejection.

            bClipRequired = bIntersectTest(&rclTrg, &rclBounds);

            // define the clipped target rectangle.

            if (bClipRequired)
            {
                rclTrg.left   = max (rclTrg.left, rclBounds.left);
                rclTrg.top    = max (rclTrg.top, rclBounds.top);
                rclTrg.right  = min (rclTrg.right, rclBounds.right);
                rclTrg.bottom = min (rclTrg.bottom, rclBounds.bottom);
            }
            else
            {
                // The destination rectangle is completely clipped out,
                // so just return.

                return (TRUE);
            }
        }

        // define the cx & cy.

        width  = (rclTrg.right  - rclTrg.left) - 1;
        height = (rclTrg.bottom - rclTrg.top)  - 1;

        // calculate the cx & cy shift for the source.

        cx = rclTrg.left - prclTrg->left;
        cy = rclTrg.top  - prclTrg->top;

        // define the source rectangle.

        rclSrc.left   = pptlSrc->x + cx;
        rclSrc.top    = pptlSrc->y + cy;
        rclSrc.right  = pptlSrc->x + width + 1;
        rclSrc.bottom = pptlSrc->y + height + 1;

        // Determine the direction of the blit.
        // First, check for an intersection of the souce and dest rects.

        bIntersect = bIntersectTest(&rclTrg, &rclSrc);

        // Set defaults for the S3 blit direction and the
        // initial coordinates

        wDirCode = DRAWING_DIR_TBLRXM;

        xTrg = rclTrg.left;
        yTrg = rclTrg.top;
        xSrc = rclSrc.left;
        ySrc = rclSrc.top;

        // If the source & dest rects intersect adjust the
        // blit direction and initial coordinates.

        if (bIntersect)
        {
            // The horizontal copy direction.

            if (rclTrg.left > rclSrc.left)
            {
                // R to L

                xTrg = rclTrg.right - 1;
                xSrc = rclSrc.left + width;
                wDirCode &= ~PLUS_X;
            }

            // The vertical copy direction.

            if (rclTrg.top > rclSrc.top)
            {
                // B to T

                yTrg = rclTrg.bottom - 1;
                ySrc = rclSrc.top + height;
                wDirCode &= ~PLUS_Y;
            }
        }

        // Create the S3 Command.

        cmd  = BITBLT | DRAW | DIR_TYPE_XY | WRITE;
        cmd |= wDirCode;

        // Do the blit.

        S3ScrnToScrnBlt(ppdev, xSrc, ySrc, xTrg, yTrg, width, height, s3Rop, cmd);

    }
    else
    {
        // This a complex clip.

        DirClip  = CD_ANY;
        wDirCode = DRAWING_DIR_TBLRXM;

        if (prclTrg->left > pptlSrc->x)
            wDirCode &= ~PLUS_X;

        if (prclTrg->top > pptlSrc->y)
            wDirCode &= ~PLUS_Y;

        switch (wDirCode)
        {
            case DRAWING_DIR_TBLRXM:
                DirClip = CD_RIGHTDOWN;
                break;

            case DRAWING_DIR_TBRLXM:
                DirClip = CD_LEFTDOWN;
                break;

            case DRAWING_DIR_BTLRXM:
                DirClip = CD_RIGHTUP;
                break;

            case DRAWING_DIR_BTRLXM:
                DirClip = CD_LEFTUP;
                break;
        }

        CLIPOBJ_cEnumStart(pco, FALSE, CT_RECTANGLES, DirClip, 0);

        do
        {
            bMore = CLIPOBJ_bEnum(pco, sizeof (ENUMRECTS8),
                                  (PULONG) &EnumRects8);

            for (i = 0; i < EnumRects8.c; i++)
            {
                rclTrg    = *prclTrg;
                rclBounds = EnumRects8.arcl[i];

                // handle the trivial rejection.

                bClipRequired = bIntersectTest(&rclTrg, &rclBounds);

                // define the clipped target rectangle.

                if (bClipRequired)
                {
                    rclTrg.left   = max (rclTrg.left, rclBounds.left);
                    rclTrg.top    = max (rclTrg.top, rclBounds.top);
                    rclTrg.right  = min (rclTrg.right, rclBounds.right);
                    rclTrg.bottom = min (rclTrg.bottom, rclBounds.bottom);
                }
                else
                {
                    // The destination rectangle is completely clipped out,
                    // so use as an early out.

                    continue;
                }

                // define the cx & cy.

                width  = (rclTrg.right  - rclTrg.left) - 1;
                height = (rclTrg.bottom - rclTrg.top)  - 1;

                // calculate the cx & cy shift for the source.

                cx = rclTrg.left - prclTrg->left;
                cy = rclTrg.top  - prclTrg->top;

                // define the source rectangle.

                rclSrc.left   = pptlSrc->x  + cx;
                rclSrc.top    = pptlSrc->y  + cy;
                rclSrc.right  = rclSrc.left + width + 1;
                rclSrc.bottom = rclSrc.top  + height + 1;

                // Determine the direction of the blit.
                // First, check for an intersection of the souce and dest rects.

                bIntersect = bIntersectTest(&rclTrg, &rclSrc);

                // Set defaults for the S3 blit direction and the
                // initial coordinates

                wDirCode = DRAWING_DIR_TBLRXM;

                xTrg = rclTrg.left;
                yTrg = rclTrg.top;
                xSrc = rclSrc.left;
                ySrc = rclSrc.top;

                // If the source & dest rects intersect adjust the
                // blit direction and initial coordinates.

                if (bIntersect)
                {
                    // The horizontal copy direction.

                    if (rclTrg.left > rclSrc.left)
                    {
                        // R to L

                        xTrg = rclTrg.right - 1;
                        xSrc = rclSrc.left + width;
                        wDirCode &= ~PLUS_X;
                    }

                    // The vertical copy direction.

                    if (rclTrg.top > rclSrc.top)
                    {
                        // B to T

                        yTrg = rclTrg.bottom - 1;
                        ySrc = rclSrc.top + height;
                        wDirCode &= ~PLUS_Y;
                    }
                }

                // Create the S3 Command.

                cmd  = BITBLT | DRAW | DIR_TYPE_XY | WRITE;
                cmd |= wDirCode;

                // Do the blit.

                S3ScrnToScrnBlt(ppdev, xSrc, ySrc, xTrg, yTrg, width, height, s3Rop, cmd);

            }

        } while (bMore);
    }
    return(TRUE);
}


/*****************************************************************************
 * S3ScrnToScrnBlt
 ****************************************************************************/
VOID S3ScrnToScrnBlt(
    PPDEV   ppdev,
    INT     xSrc,
    INT     ySrc,
    INT     xTrg,
    INT     yTrg,
    INT     width,
    INT     height,
    WORD    s3Rop,
    WORD    cmd)
{

#if DBG

    if (cmd & 0x20)
    {
        ASSERTS3(((xTrg + width) < (INT) ppdev->cxScreen),
                 "S3.DLL!S3ScrnToScrnBlt - Blit Operation over right edge (1)\n");
    }

#endif
    // This is where the actual S3 registers are set.

    FIFOWAIT(FIFO_2_EMPTY);

    TEST_AND_SET_FRGD_MIX(SRC_DISPLAY_MEMORY | s3Rop);

    OUTPW(MULTIFUNC_CNTL, (DATA_EXTENSION | ALL_ONES));

    FIFOWAIT(FIFO_7_EMPTY);

    OUTPW(RECT_WIDTH, width);
    OUTPW(MULTIFUNC_CNTL, (RECT_HEIGHT | height));
    OUTPW(CUR_X, xSrc);
    OUTPW(CUR_Y, ySrc);
    OUTPW(DEST_X, xTrg);
    OUTPW(DEST_Y, yTrg);

    OUTPW(CMD, cmd);
}


/*****************************************************************************
 * S3 Brush Pattern
 *
 *  Returns TRUE if the blit was handled.
 ****************************************************************************/
BOOL bPatternBrush(
    SURFOBJ  *psoTrg,
    CLIPOBJ  *pco,
    RECTL    *prclTrg,
    BRUSHOBJ *pbo,
    POINTL   *pptlBrush,
    ROP4      s3Rop4)           // Note that the least significant byte is
                                // the S3 foreground ROP, and the next byte
                                // is the S3 background ROP.  So you can't
                                // just pass in a plain S3 ROP constant!

{
    PPDEV       ppdev;
    PS3BRUSH    ps3Brush;
    POINT       pt;
    XYZPOINT    xyzPt;
    INT         zHorz, zVert, i;
    POINT       ptDest;
    SIZE        sizDest;
    WORD        s3ForeRop, s3BackRop;
    BOOL        bClipRequired, bMore;
    RECTL       rclTrg, rclBounds;
    BYTE        iDComplexity;
    ENUMRECTS8  EnumRects8;

    DISPDBG((3, "S3.DLL!bPatternBrush - Entry\n"));

    // Get the pdev.

    ppdev = (PPDEV) psoTrg->dhpdev;

    // Get the pointer to our drivers realization of the brush.

    if (pbo->pvRbrush != NULL)
    {
        ps3Brush = pbo->pvRbrush;
    }
    else
    {
        ps3Brush = BRUSHOBJ_pvGetRbrush(pbo);

        // Fail if we do not handle the brush.

        if (ps3Brush == NULL)
            return (FALSE);
    }

    s3ForeRop = (WORD) (s3Rop4 & 0xFF);
    s3BackRop = (WORD) ((s3Rop4 >> 8) & 0xFF);

    if (ps3Brush->iBitmapFormat == BMF_1BPP)
    {
        DISPDBG((3, "S3.DLL!bPatternBrush - 1BPP brush\n"));

        // If we have never seen this brush before then allocate a slot
        // for it.

        // Keep a count of number of brushes we have seen.
        // This will be used to determine if the cache system is adaquate.

        nMonoBrushes++;

        // If we have never seen this brush before or if the cache has
        // changed since the last time
        // we have seen it then allocate a new cache entry for it.

        if ((ps3Brush->iBrushCacheID == -1) ||
            (ps3Brush->iPatternID != ppdev->pulMonoBrushCacheEntries[ps3Brush->iBrushCacheID]))
        {
            // If we have run out of cache entries invalidate the entire cache.

            if (ppdev->iNextMonoBrushCacheSlot >= ppdev->iMaxCachedMonoBrushes)
            {
                nMonoBrushCacheInvalidations++;

                memset(ppdev->pulMonoBrushCacheEntries, 0, ppdev->iMaxCachedMonoBrushes * sizeof(ULONG));
                ppdev->iNextMonoBrushCacheSlot = 0;
            }

            // Get a slot in the cache for this brush.

            i = (ppdev->iNextMonoBrushCacheSlot)++;
            ps3Brush->iBrushCacheID = i;

            // Calculate the address of the brush in the cache.

            xyzPt.x = MONO_PATTERN_CACHE_X + ((i >>  3) * 16);
            xyzPt.y = MONO_PATTERN_CACHE_Y;
            xyzPt.z = ajMonoPatPlanes[i & 0x7];

            // Associate the brush with this cache entry.

            ppdev->pulMonoBrushCacheEntries[i] = ps3Brush->iPatternID;

            // Get an expansion cache slot for this brush.

            i = ppdev->iNextMonoBrushExpansionSlot;
            i = ++i % MAX_MONO_BRUSH_EXPANSION_SLOTS;
            ppdev->iNextMonoBrushExpansionSlot = i;
            ps3Brush->iExpansionCacheID = i;

            // Associate this brush with this expansion cache slot.

            ppdev->aulMonoExpansionCacheTag[i] = ps3Brush->iPatternID;

            // Set the expansion caches planes

            zHorz = zVert = ajMonoPatPlanes[i];

            // Down load the brush to the cache and expand it into
            // the horizontal and vertical caches.

            bDownLoadBrushIntoMonoCache(ppdev, ps3Brush, &xyzPt);
            bExpandMonoBrushIntoHorzCache(ppdev, &xyzPt, zHorz);
            bExpandMonoBrushIntoVertCache(ppdev, &xyzPt, zVert);

        }
        else
        {
            // We got a cache hit, so keep the statistic.

            nMonoBrushCacheHit++;

            // Calculate the address of the brush in the cache.

            i = ps3Brush->iBrushCacheID;
            xyzPt.x = MONO_PATTERN_CACHE_X + ((i >>  3) * 16);
            xyzPt.y = MONO_PATTERN_CACHE_Y;
            xyzPt.z = ajMonoPatPlanes[i & 0x7];

            // Check for an hit in the expansion cache.

            if (ps3Brush->iPatternID == ppdev->aulMonoExpansionCacheTag[(i = ps3Brush->iExpansionCacheID)])
            {
                zHorz = zVert = ajMonoPatPlanes[i];
                nMonoBrushExpansionCacheHit++;
            }
            else
            {
                // Get an expansion cache slot for this brush.

                i = ppdev->iNextMonoBrushExpansionSlot;
                i = ++i % MAX_MONO_BRUSH_EXPANSION_SLOTS;
                ppdev->iNextMonoBrushExpansionSlot = i;
                ps3Brush->iExpansionCacheID = i;

                // Associate this brush with this expansion cache slot.

                ppdev->aulMonoExpansionCacheTag[i] = ps3Brush->iPatternID;

                // Set the expansion caches planes

                zHorz = zVert = ajMonoPatPlanes[i];

                bExpandMonoBrushIntoHorzCache(ppdev, &xyzPt, zHorz);
                bExpandMonoBrushIntoVertCache(ppdev, &xyzPt, zVert);
            }
        }
    }
    else
    {
        DISPDBG((3, "S3.DLL!bPatternBrush - 8BPP brush\n"));

        // If we have never seen this brush before then allocate a slot
        // for it.

        // Keep a count of number of brushes we have seen.
        // This will be used to determine if the cache system is adaquate.

        nColorBrushes++;

        // If we have never seen this brush before or if the cache
        // has changed since the last time
        // we have seen it then allocate a new cache entry for it.

        if ((ps3Brush->iBrushCacheID == -1) ||
            (ps3Brush->iPatternID != ppdev->pulColorBrushCacheEntries[ps3Brush->iBrushCacheID]))
        {
            // If we have run out of cache entries invalidate the entire cache.

            if (ppdev->iNextColorBrushCacheSlot >= ppdev->iMaxCachedColorBrushes)
            {
                nColorBrushCacheInvalidations++;

                memset(ppdev->pulColorBrushCacheEntries, 0, ppdev->iMaxCachedColorBrushes * sizeof(ULONG));
                ppdev->iNextColorBrushCacheSlot = 0;
            }

            // Get a slot in the cache for this brush.

            i = (ppdev->iNextColorBrushCacheSlot)++;
            ps3Brush->iBrushCacheID = i;

            // Calculate the address of the brush in the cache.

            pt.x = COLOR_PATTERN_CACHE_X + ((i >>  2) * 16);
            pt.y = COLOR_PATTERN_CACHE_Y + ((i & 0x3) * 16);

            // Associate the brush with this cache entry.

            ppdev->pulColorBrushCacheEntries[i] = ps3Brush->iPatternID;
            ppdev->ulColorExpansionCacheTag     = ps3Brush->iPatternID;

            // Down load the brush to the cache and expand it into
            // the horizontal and vertical caches.

            bDownLoadBrushIntoColorCache(ppdev, ps3Brush, &pt);
            bExpandColorBrushIntoHorzCache(ppdev, &pt);
            bExpandColorBrushIntoVertCache(ppdev, &pt);
        }
        else
        {
            // We got a cache hit, so keep the statistic.

            nColorBrushCacheHit++;

            // Calculate the address of the brush in the cache.

            i = ps3Brush->iBrushCacheID;
            pt.x = COLOR_PATTERN_CACHE_X + ((i >>  2) * 16);
            pt.y = COLOR_PATTERN_CACHE_Y + ((i & 0x3) * 16);

            if (ps3Brush->iPatternID == ppdev->ulColorExpansionCacheTag)
            {
                nColorBrushExpansionCacheHit++;
            }
            else
            {
                ppdev->ulColorExpansionCacheTag = ps3Brush->iPatternID;

                bExpandColorBrushIntoHorzCache(ppdev, &pt);
                bExpandColorBrushIntoVertCache(ppdev, &pt);
            }
        }
    }

    // Handle the clipping.

    if ((iDComplexity = pco->iDComplexity) != DC_COMPLEX)
    {
        if (iDComplexity == DC_TRIVIAL)
        {
            rclTrg = *prclTrg;
        }
        else
        {
            rclTrg    = *prclTrg;
            rclBounds = pco->rclBounds;

            // First handle the trivial rejection.

            bClipRequired = bIntersectTest(&rclTrg, &rclBounds);

            if (bClipRequired)
            {
                rclTrg.left   = max (rclTrg.left,  rclBounds.left);
                rclTrg.top    = max (rclTrg.top,   rclBounds.top);
                rclTrg.right  = min(rclTrg.right,  rclBounds.right);
                rclTrg.bottom = min(rclTrg.bottom, rclBounds.bottom);
            }
            else
            {
                // The brush is completely clipped out.
                // so just return sucessfull.

                return (TRUE);
            }
        }

        ptDest.x   = rclTrg.left;
        ptDest.y   = rclTrg.top;
        sizDest.cx = rclTrg.right  - rclTrg.left;
        sizDest.cy = rclTrg.bottom - rclTrg.top;

        // Color expand the monochrome brush in the Hoizontal Cache to the
        // screen.

        if (s3ForeRop == OVERPAINT && s3BackRop == OVERPAINT)
        {
            if (ps3Brush->iBitmapFormat == BMF_1BPP)
            {
                bCpyColorExpandHorzCacheToScreen(ppdev, ps3Brush, zHorz,
                                                 (PPOINT) pptlBrush,
                                                 &ptDest, &sizDest);
            }
            else
            {
                // NOTE: The S3-911/924 requries the cx of any "rolling blt"
                //       to have a cx of >= 64 pels.  This is due to how the
                //       chip's internal FIFO works.  This test will catch all
                //       the souce copy operations that can be accelerated,
                //       with the rolling blit.

                if ((sizDest.cx >= 64) ||
                    (sizDest.cx > sizDest.cy))
                {
                    bCpyColorHorzCacheToScreen(ppdev, ps3Brush,
                                               (PPOINT) pptlBrush,
                                               &ptDest, &sizDest);
                }
                else
                {
                    bColorVertCacheToScreen(ppdev, ps3Brush,
                                            (PPOINT) pptlBrush,
                                            &ptDest, &sizDest,
                                            s3ForeRop, s3BackRop);
                }
            }
        }
        else
        {
            if (sizDest.cy > sizDest.cx)
            {
                if (ps3Brush->iBitmapFormat == BMF_1BPP)
                {
                    bColorExpandVertCacheToScreen(ppdev, ps3Brush, zVert,
                                                  (PPOINT) pptlBrush,
                                                  &ptDest, &sizDest,
                                                  s3ForeRop, s3BackRop);
                }
                else
                {
                    bColorVertCacheToScreen(ppdev, ps3Brush,
                                            (PPOINT) pptlBrush,
                                            &ptDest, &sizDest,
                                            s3ForeRop, s3BackRop);
                }
            }
            else
            {
                if (ps3Brush->iBitmapFormat == BMF_1BPP)
                {
                    bColorExpandHorzCacheToScreen(ppdev, ps3Brush, zHorz,
                                                  (PPOINT) pptlBrush,
                                                  &ptDest, &sizDest,
                                                  s3ForeRop, s3BackRop);
                }
                else
                {
                    bColorHorzCacheToScreen(ppdev, ps3Brush,
                                            (PPOINT) pptlBrush,
                                            &ptDest, &sizDest,
                                            s3ForeRop, s3BackRop);
                }
            }
        }
    }
    else
    {
        CLIPOBJ_cEnumStart(pco, FALSE, CT_RECTANGLES, CD_ANY, 0);

        do
        {
            bMore = CLIPOBJ_bEnum(pco, sizeof (ENUMRECTS8),
                                  (PULONG) &EnumRects8);

            for (i = 0; i < (INT) EnumRects8.c; i++)
            {
                rclBounds = EnumRects8.arcl[i];

                if (rclBounds.left < prclTrg->left)
                    rclBounds.left = prclTrg->left;

                if (rclBounds.right > prclTrg->right)
                    rclBounds.right = prclTrg->right;

                if (rclBounds.top < prclTrg->top)
                    rclBounds.top = prclTrg->top;

                if (rclBounds.bottom > prclTrg->bottom)
                    rclBounds.bottom = prclTrg->bottom;

                // Get the height and width for the blit.

                ptDest.x   = rclBounds.left;
                ptDest.y   = rclBounds.top;
                sizDest.cx = rclBounds.right -  rclBounds.left;
                sizDest.cy = rclBounds.bottom - rclBounds.top;

                // Color expand the monochrome brush in the Hoizontal Cache
                // to the screen.

                if (s3ForeRop == OVERPAINT && s3BackRop == OVERPAINT)
                {
                    if (ps3Brush->iBitmapFormat == BMF_1BPP)
                    {
                        bCpyColorExpandHorzCacheToScreen(ppdev, ps3Brush, zHorz,
                                                         (PPOINT) pptlBrush,
                                                         &ptDest, &sizDest);

                    }
                    else
                    {
                        if ((sizDest.cx >= 64) ||
                            (sizDest.cx > sizDest.cy))
                        {
                            bCpyColorHorzCacheToScreen(ppdev, ps3Brush,
                                                       (PPOINT) pptlBrush,
                                                       &ptDest, &sizDest);
                        }
                        else
                        {
                            bColorVertCacheToScreen(ppdev, ps3Brush,
                                                    (PPOINT) pptlBrush,
                                                    &ptDest, &sizDest,
                                                    s3ForeRop, s3BackRop);
                        }
                    }
                }
                else
                {
                    if (sizDest.cy > sizDest.cx)
                    {
                        if (ps3Brush->iBitmapFormat == BMF_1BPP)
                        {
                            bColorExpandVertCacheToScreen(ppdev, ps3Brush, zVert,
                                                          (PPOINT) pptlBrush,
                                                          &ptDest, &sizDest,
                                                          s3ForeRop, s3BackRop);
                        }
                        else
                        {
                            bColorVertCacheToScreen(ppdev, ps3Brush,
                                                    (PPOINT) pptlBrush,
                                                    &ptDest, &sizDest,
                                                    s3ForeRop, s3BackRop);
                        }
                    }
                    else
                    {
                        if (ps3Brush->iBitmapFormat == BMF_1BPP)
                        {
                            bColorExpandHorzCacheToScreen(ppdev, ps3Brush, zHorz,
                                                          (PPOINT) pptlBrush,
                                                          &ptDest, &sizDest,
                                                          s3ForeRop, s3BackRop);
                        }
                        else
                        {
                            bColorHorzCacheToScreen(ppdev, ps3Brush,
                                                    (PPOINT) pptlBrush,
                                                    &ptDest, &sizDest,
                                                    s3ForeRop, s3BackRop);
                        }

                    }
                }
            }
        } while (bMore);

    }

    return (TRUE);

}

/*****************************************************************************
 * wXlateSrcRop3toS3Rop - Translate the Source to Dest Rop 3 to an S3 rop
 ****************************************************************************/
WORD wXlateSrcRop3toS3Rop(PPDEV pdev, WORD rop3)
{
WORD    s3Rop;

    switch (LOBYTE(rop3))
    {
        case 0xBB:                      // DSno
            s3Rop = SCREEN_OR_NOT_NEW;
            break;

        case 0x33:                      // Sn
            s3Rop = NOT_NEW;
            break;

        case 0x44:                      // SDna
            s3Rop = NOT_SCREEN_AND_NEW;
            break;

        case 0x66:                      // DSx
            s3Rop = SCREEN_XOR_NEW;
            break;

        case 0x77:                      // DSan
            s3Rop = NOT_SCREEN_OR_NOT_NEW;
            break;

        case 0x88:                      // DSa
            s3Rop = SCREEN_AND_NEW;
            break;

        case 0x99:                      // DSxn
            s3Rop = NOT_SCREEN_XOR_NEW;
            break;

        case 0xDD:                      // SDno
            s3Rop = NOT_SCREEN_OR_NEW;
            break;

        case 0xCC:                      // S
            s3Rop = OVERPAINT;
            break;

        case 0x11:                      // DSon
            s3Rop = NOT_SCREEN_AND_NOT_NEW;
            break;

        case 0x22:                      // DSna
            s3Rop = SCREEN_AND_NOT_NEW;
            break;

        case 0xEE:                      // SDo
            s3Rop = SCREEN_OR_NEW;
            break;

        default:
            s3Rop = 0;
            break;
    }

    return (s3Rop);

}


/*****************************************************************************
 * Translate the ROP4 to S3 rops.
 ****************************************************************************/

ROP4 bXlatePatRop4toS3Rop(
    PPDEV pdev,
    ROP4  rop4)
{
    WORD    s3Rop;
    ROP4    s3Rop4;

    switch (rop4 & 0xff)
    {
        case 0x00:                      // 0
            s3Rop = LOGICAL_0;
            break;

        case 0x05:                      // ~(P | D)
            s3Rop = NOT_SCREEN_AND_NOT_NEW;
            break;

        case 0x0A:                      // ~P & D
            s3Rop = SCREEN_AND_NOT_NEW;
            break;

        case 0x0F:                      // ~P
            s3Rop = NOT_NEW;
            break;

        case 0x50:                      // P & ~D
            s3Rop = NOT_SCREEN_AND_NEW;
            break;

        case 0x55:                      // ~D
            s3Rop = NOT_SCREEN;
            break;

        case 0x5A:                      // P ^ D
            s3Rop = SCREEN_XOR_NEW;
            break;

        case 0x5F:                      // ~(P & D)
            s3Rop = NOT_SCREEN_OR_NOT_NEW;
            break;

        case 0xA0:                      // P & D
            s3Rop = SCREEN_AND_NEW;
            break;

        case 0xA5:                      // ~(P ^ D)
            s3Rop = NOT_SCREEN_XOR_NEW;
            break;

        case 0xAA:                      // D
            s3Rop = LEAVE_ALONE;
            break;

        case 0xAF:                      // ~P | D
            s3Rop = SCREEN_OR_NOT_NEW;
            break;

        case 0xF0:                      // P
            s3Rop = OVERPAINT;
            break;

        case 0xF5:                      // P | ~D
            s3Rop = NOT_SCREEN_OR_NEW;
            break;

        case 0xFA:                      // P | D
            s3Rop = SCREEN_OR_NEW;
            break;

        case 0xFF:                      // 1
            s3Rop = LOGICAL_1;
            break;
    }

    s3Rop4 = s3Rop | (s3Rop << 8);

    return(s3Rop4);
}

/*****************************************************************************
 * Download the Color Brush to the Color brush cache in
 * graphics memory.
 ****************************************************************************/
BOOL bDownLoadBrushIntoColorCache(
    PPDEV      ppdev,
    PS3BRUSH   ps3Brush,
    PPOINT     ppt)
{
    WORD    Cmd;

    DISPDBG((3, "S3.DLL!bDownLoadBrushIntoColorCache - Entry\n"));

    // Down load the initial pattern image, the upper left 8 X 8 cell.

    Cmd = RECTANGLE_FILL | BUS_SIZE_8         | WAIT |
          DRAW           | DRAWING_DIR_TBLRXM | DIR_TYPE_XY |
          LAST_PIXEL_ON  | WRITE;

    // Setup the S3 chip.

    FIFOWAIT(FIFO_6_EMPTY);

    TEST_AND_SET_FRGD_MIX(SRC_CPU_DATA | OVERPAINT);
    OUTPW(MULTIFUNC_CNTL, (DATA_EXTENSION | ALL_ONES));

    OUTPW(CUR_X, ppt->x);
    OUTPW(CUR_Y, ppt->y);

    OUTPW(RECT_WIDTH, 7);
    OUTPW(MULTIFUNC_CNTL, (RECT_HEIGHT | 7));

    GPWAIT();
    OUTPW(CMD, Cmd);

    // Now transfer the data from host memory to graphics memory.

    CHECK_DATA_READY;

    vDataPortOutB(ppdev, ps3Brush->ajPattern, 64);

    CHECK_DATA_COMPLETE;

    // Make the pattern double wide. Make copy of the pattern to the right
    // of the original pattern.

    Cmd  = BITBLT          | DRAW | DIR_TYPE_XY | WRITE |
           MULTIPLE_PIXELS | DRAWING_DIR_TBLRXM;

    FIFOWAIT(FIFO_8_EMPTY);

    TEST_AND_SET_FRGD_MIX(SRC_DISPLAY_MEMORY | OVERPAINT);

    OUTPW(MULTIFUNC_CNTL, (DATA_EXTENSION | ALL_ONES));

    OUTPW(CUR_X,  ppt->x);
    OUTPW(CUR_Y,  ppt->y);
    OUTPW(DEST_X, ppt->x + 8);
    OUTPW(DEST_Y, ppt->y);

    OUTPW(CMD, Cmd);

    // Make the pattern double high. Make a copy of the double pattern
    // pattern below the original one.

    FIFOWAIT(FIFO_6_EMPTY);

    OUTPW(RECT_WIDTH, 15);

    OUTPW(CUR_X,  ppt->x);
    OUTPW(CUR_Y,  ppt->y);
    OUTPW(DEST_X, ppt->x);
    OUTPW(DEST_Y, ppt->y + 8);

    OUTPW(CMD, Cmd);

    return(TRUE);
}



/*****************************************************************************
 * Download the Monochrome Brush to the Monochrome brush cache in
 * graphics memory.
 ****************************************************************************/
BOOL bDownLoadBrushIntoMonoCache(
    PPDEV      ppdev,
    PS3BRUSH   p3Brush,
    PXYZPOINT  pxyzPt)
{
    WORD    Cmd;
    INT     i;
    BYTE    ajPattern[8];

    DISPDBG((3, "S3.DLL!bDownLoadBrushIntoMonoCache - Entry\n"));

    // Compress the dword aligned pattern to a byte aligned pattern.

    for (i = 0; i < 8; i++)
    {
        ajPattern[i] = p3Brush->ajPattern[i * p3Brush->lDeltaPattern];
    }

    // Down load the initial pattern image, the upper left 8 X 8 cell.

    Cmd = RECTANGLE_FILL | BUS_SIZE_8         | WAIT |
          DRAW           | DRAWING_DIR_TBLRXM | DIR_TYPE_XY |
          LAST_PIXEL_ON  | MULTIPLE_PIXELS    | WRITE;

    // Setup the S3 chip.

    FIFOWAIT(FIFO_8_EMPTY);

    TEST_AND_SET_FRGD_MIX(LOGICAL_1);
    TEST_AND_SET_BKGD_MIX(LOGICAL_0);

    TEST_AND_SET_WRT_MASK(LOWORD(pxyzPt->z));

    OUTPW(CUR_X, pxyzPt->x);
    OUTPW(CUR_Y, pxyzPt->y);

    OUTPW(MULTIFUNC_CNTL, (DATA_EXTENSION | CPU_DATA));
    OUTPW(RECT_WIDTH, 7);
    OUTPW(MULTIFUNC_CNTL, (RECT_HEIGHT | 7));

    GPWAIT();
    OUTPW(CMD, Cmd);

    // Now transfer the data from host memory to graphics memory.

    CHECK_DATA_READY;

    vDataPortOutB(ppdev, ajPattern, 8);

    CHECK_DATA_COMPLETE;

    // Make the pattern double wide. Make copy of the pattern to the right
    // of the original pattern.

    Cmd  = BITBLT          | DRAW | DIR_TYPE_XY | WRITE |
           MULTIPLE_PIXELS | DRAWING_DIR_TBLRXM;

    FIFOWAIT(FIFO_8_EMPTY);

    TEST_AND_SET_RD_MASK (LOWORD(pxyzPt->z));
    TEST_AND_SET_FRGD_MIX(SRC_DISPLAY_MEMORY | OVERPAINT);

    OUTPW(MULTIFUNC_CNTL, (DATA_EXTENSION | ALL_ONES));

    OUTPW(CUR_X,  pxyzPt->x);
    OUTPW(CUR_Y,  pxyzPt->y);
    OUTPW(DEST_X, pxyzPt->x + 8);
    OUTPW(DEST_Y, pxyzPt->y);

    OUTPW(CMD, Cmd);

    // Make the pattern double high. Make a copy of the double pattern
    // pattern below the original one.

    FIFOWAIT(FIFO_6_EMPTY);

    OUTPW(RECT_WIDTH, 15);

    OUTPW(CUR_X,  pxyzPt->x);
    OUTPW(CUR_Y,  pxyzPt->y);
    OUTPW(DEST_X, pxyzPt->x);
    OUTPW(DEST_Y, pxyzPt->y + 8);

    OUTPW(CMD, Cmd);

    // Reset the read and write masks.

    FIFOWAIT(FIFO_2_EMPTY);

    TEST_AND_SET_WRT_MASK(0xff);
    TEST_AND_SET_RD_MASK(0xff);

    return(TRUE);
}


/*****************************************************************************
 * Expand the color brush in the vertical dimension.
 ****************************************************************************/
BOOL bExpandColorBrushIntoVertCache(
    PPDEV      ppdev,
    PPOINT     ppt)
{
    WORD    Cmd;

    DISPDBG((3, "S3.DLL!bExpandColorBrushIntoVertCache - Entry\n"));

    // Copy the cached double wide, double high pattern to the Horizontal
    // expansion cache area.

    Cmd  = BITBLT          | DRAW | DIR_TYPE_XY | WRITE |
           MULTIPLE_PIXELS | DRAWING_DIR_TBLRXM;

    FIFOWAIT(FIFO_1_EMPTY);

    TEST_AND_SET_FRGD_MIX(SRC_DISPLAY_MEMORY | OVERPAINT);

    FIFOWAIT(FIFO_8_EMPTY);

    OUTPW(MULTIFUNC_CNTL, (DATA_EXTENSION | ALL_ONES));
    OUTPW(MULTIFUNC_CNTL, (RECT_HEIGHT | 15));
    OUTPW(RECT_WIDTH, 15);

    OUTPW(CUR_X,  ppt->x);
    OUTPW(CUR_Y,  ppt->y);
    OUTPW(DEST_X, COLOR_VERT_EXPANSION_CACHE_X);
    OUTPW(DEST_Y, COLOR_VERT_EXPANSION_CACHE_Y);

    OUTPW(CMD, Cmd);

    // Now the rolling blit to fill all the way.

    FIFOWAIT(FIFO_6_EMPTY);

    OUTPW(MULTIFUNC_CNTL, (RECT_HEIGHT | COLOR_VERT_EXPANSION_CACHE_CY - 17));

    OUTPW(CUR_X,  COLOR_VERT_EXPANSION_CACHE_X);
    OUTPW(CUR_Y,  COLOR_VERT_EXPANSION_CACHE_Y);
    OUTPW(DEST_X, COLOR_VERT_EXPANSION_CACHE_X);
    OUTPW(DEST_Y, COLOR_VERT_EXPANSION_CACHE_Y + 16);

    OUTPW(CMD, Cmd);

    return(TRUE);

}



/*****************************************************************************
 * Expand the monochrome brush in the vertical dimension.
 ****************************************************************************/
BOOL bExpandMonoBrushIntoVertCache(
    PPDEV      ppdev,
    PXYZPOINT  pxyzPt,
    INT        zVert)
{
    WORD    Cmd;

    DISPDBG((3, "S3.DLL!bExpandMonoBrushIntoVertCache - Entry\n"));

    // Copy the cached double wide, double high pattern to the Horizontal
    // expansion cache area.

    Cmd  = BITBLT          | DRAW | DIR_TYPE_XY | WRITE |
           MULTIPLE_PIXELS | DRAWING_DIR_TBLRXM;

    FIFOWAIT(FIFO_7_EMPTY);

    OUTPW(MULTIFUNC_CNTL, (DATA_EXTENSION | DISPLAY_MEMORY));

    TEST_AND_SET_FRGD_MIX(FOREGROUND_COLOR | OVERPAINT);
    TEST_AND_SET_FRGD_COLOR(0xff);
    TEST_AND_SET_BKGD_MIX(BACKGROUND_COLOR | OVERPAINT);
    SET_BKGD_COLOR(0);

    TEST_AND_SET_RD_MASK(LOWORD(pxyzPt->z));
    TEST_AND_SET_WRT_MASK(LOWORD(zVert));

    FIFOWAIT(FIFO_7_EMPTY);

    OUTPW(RECT_WIDTH, 15);
    OUTPW(MULTIFUNC_CNTL, (RECT_HEIGHT | 15));

    OUTPW(CUR_X,  pxyzPt->x);
    OUTPW(CUR_Y,  pxyzPt->y);
    OUTPW(DEST_X, MONO_VERT_EXPANSION_CACHE_X);
    OUTPW(DEST_Y, MONO_VERT_EXPANSION_CACHE_Y);

    OUTPW(CMD, Cmd);

    // Now the rolling blit to fill all the way.

    FIFOWAIT(FIFO_7_EMPTY);

    TEST_AND_SET_RD_MASK(zVert);

    OUTPW(MULTIFUNC_CNTL, (RECT_HEIGHT | MONO_VERT_EXPANSION_CACHE_CY - 17));

    OUTPW(CUR_X,  MONO_VERT_EXPANSION_CACHE_X);
    OUTPW(CUR_Y,  MONO_VERT_EXPANSION_CACHE_Y);
    OUTPW(DEST_X, MONO_VERT_EXPANSION_CACHE_X);
    OUTPW(DEST_Y, MONO_VERT_EXPANSION_CACHE_Y + 16);

    OUTPW(CMD, Cmd);

    // Now reset the read and write plan masks.

    FIFOWAIT(FIFO_2_EMPTY);

    TEST_AND_SET_WRT_MASK(0xff);
    TEST_AND_SET_RD_MASK(0xff);

    return(TRUE);

}

/*****************************************************************************
 * Expand the color brush in the horizontal dimension.
 ****************************************************************************/
BOOL bExpandColorBrushIntoHorzCache(
    PPDEV      ppdev,
    PPOINT     ppt)
{
    WORD    Cmd;

    DISPDBG((3, "S3.DLL!bExpandColorBrushIntoHorzCache - Entry\n"));

    // Copy the cached double wide, double high pattern to the Horizontal
    // expansion cache area.

    Cmd  = BITBLT          | DRAW | DIR_TYPE_XY | WRITE |
           MULTIPLE_PIXELS | DRAWING_DIR_TBLRXM;

    FIFOWAIT(FIFO_1_EMPTY);

    TEST_AND_SET_FRGD_MIX(SRC_DISPLAY_MEMORY | OVERPAINT);

    FIFOWAIT(FIFO_8_EMPTY);

    OUTPW(MULTIFUNC_CNTL, (DATA_EXTENSION | ALL_ONES));
    OUTPW(MULTIFUNC_CNTL, (RECT_HEIGHT | 15));
    OUTPW(RECT_WIDTH, 15);

    OUTPW(CUR_X,  ppt->x);
    OUTPW(CUR_Y,  ppt->y);
    OUTPW(DEST_X, COLOR_HORZ_EXPANSION_CACHE_X);
    OUTPW(DEST_Y, COLOR_HORZ_EXPANSION_CACHE_Y);

    OUTPW(CMD, Cmd);

    // Expand the 16 X 16 double wide, double high.
    // First to a 32 X 16

    FIFOWAIT(FIFO_5_EMPTY);

    OUTPW(CUR_X,  COLOR_HORZ_EXPANSION_CACHE_X);
    OUTPW(CUR_Y,  COLOR_HORZ_EXPANSION_CACHE_Y);
    OUTPW(DEST_X, COLOR_HORZ_EXPANSION_CACHE_X + 16);
    OUTPW(DEST_Y, COLOR_HORZ_EXPANSION_CACHE_Y);

    OUTPW(CMD, Cmd);

    // Now 64 X 16

    FIFOWAIT(FIFO_6_EMPTY);

    OUTPW(RECT_WIDTH, 31);

    OUTPW(CUR_X,  COLOR_HORZ_EXPANSION_CACHE_X);
    OUTPW(CUR_Y,  COLOR_HORZ_EXPANSION_CACHE_Y);
    OUTPW(DEST_X, COLOR_HORZ_EXPANSION_CACHE_X + 32);
    OUTPW(DEST_Y, COLOR_HORZ_EXPANSION_CACHE_Y);

    OUTPW(CMD, Cmd);

    // Now the rolling blit to fill all the way to 512 pels.

    FIFOWAIT(FIFO_7_EMPTY);

    OUTPW(RECT_WIDTH, COLOR_HORZ_EXPANSION_CACHE_CX - 65);

    OUTPW(CUR_X,  COLOR_HORZ_EXPANSION_CACHE_X);
    OUTPW(CUR_Y,  COLOR_HORZ_EXPANSION_CACHE_Y);
    OUTPW(DEST_X, COLOR_HORZ_EXPANSION_CACHE_X + 64);
    OUTPW(DEST_Y, COLOR_HORZ_EXPANSION_CACHE_Y);

    OUTPW(CMD, Cmd);

    return(TRUE);
}




/*****************************************************************************
 * Expand the monochrome brush in the horizontal dimension.
 ****************************************************************************/
BOOL bExpandMonoBrushIntoHorzCache(
    PPDEV      ppdev,
    PXYZPOINT  pxyzPt,
    INT        zHorz)
{
    WORD    Cmd;

    DISPDBG((3, "S3.DLL!bExpandMonoBrushIntoHorzCache - Entry\n"));

    // Copy the cached double wide, double high pattern to the Horizontal
    // expansion cache area.

    Cmd  = BITBLT          | DRAW | DIR_TYPE_XY | WRITE |
           MULTIPLE_PIXELS | DRAWING_DIR_TBLRXM;

    FIFOWAIT(FIFO_7_EMPTY);

    OUTPW(MULTIFUNC_CNTL, (DATA_EXTENSION | DISPLAY_MEMORY));

    TEST_AND_SET_FRGD_MIX(FOREGROUND_COLOR | OVERPAINT);
    TEST_AND_SET_FRGD_COLOR(0xff);
    TEST_AND_SET_BKGD_MIX(BACKGROUND_COLOR | OVERPAINT);
    SET_BKGD_COLOR(0);

    TEST_AND_SET_RD_MASK(LOWORD(pxyzPt->z));
    TEST_AND_SET_WRT_MASK(LOWORD(zHorz));

    FIFOWAIT(FIFO_7_EMPTY);

    OUTPW(RECT_WIDTH, 15);
    OUTPW(MULTIFUNC_CNTL, (RECT_HEIGHT | 15));

    OUTPW(CUR_X,  pxyzPt->x);
    OUTPW(CUR_Y,  pxyzPt->y);
    OUTPW(DEST_X, MONO_HORZ_EXPANSION_CACHE_X);
    OUTPW(DEST_Y, MONO_HORZ_EXPANSION_CACHE_Y);

    OUTPW(CMD, Cmd);

    // Expand the 16 X 16 double wide, double high.
    // First to a 32 X 16

    FIFOWAIT(FIFO_7_EMPTY);

    TEST_AND_SET_RD_MASK(LOWORD(zHorz));

    OUTPW(RECT_WIDTH, 15);

    OUTPW(CUR_X,  MONO_HORZ_EXPANSION_CACHE_X);
    OUTPW(CUR_Y,  MONO_HORZ_EXPANSION_CACHE_Y);
    OUTPW(DEST_X, MONO_HORZ_EXPANSION_CACHE_X + 16);
    OUTPW(DEST_Y, MONO_HORZ_EXPANSION_CACHE_Y);

    OUTPW(CMD, Cmd);

    // Now 64 X 16

    FIFOWAIT(FIFO_6_EMPTY);

    OUTPW(RECT_WIDTH, 31);

    OUTPW(CUR_X,  MONO_HORZ_EXPANSION_CACHE_X);
    OUTPW(CUR_Y,  MONO_HORZ_EXPANSION_CACHE_Y);
    OUTPW(DEST_X, MONO_HORZ_EXPANSION_CACHE_X + 32);
    OUTPW(DEST_Y, MONO_HORZ_EXPANSION_CACHE_Y);

    OUTPW(CMD, Cmd);

    // Now the rolling blit to fill all the way to 512 pels.

    FIFOWAIT(FIFO_7_EMPTY);

    OUTPW(RECT_WIDTH, MONO_HORZ_EXPANSION_CACHE_CX - 65);

    OUTPW(CUR_X,  MONO_HORZ_EXPANSION_CACHE_X);
    OUTPW(CUR_Y,  MONO_HORZ_EXPANSION_CACHE_Y);
    OUTPW(DEST_X, MONO_HORZ_EXPANSION_CACHE_X + 64);
    OUTPW(DEST_Y, MONO_HORZ_EXPANSION_CACHE_Y);

    OUTPW(CMD, Cmd);

    // Now reset the read and write plan masks.

    FIFOWAIT(FIFO_2_EMPTY);

    TEST_AND_SET_WRT_MASK(0xff);
    TEST_AND_SET_RD_MASK(0xff);

    return(TRUE);
}


/*****************************************************************************
 * Color brush in the Vertical Cache to the screen.
 ****************************************************************************/
BOOL bColorVertCacheToScreen(
    PPDEV       ppdev,
    PS3BRUSH    ps3Brush,
    PPOINT      pptBrushOrg,
    PPOINT      pptDest,
    PSIZE       psizDest,
    WORD        s3ForeRop,
    WORD        s3BackRop)
{
    WORD    Cmd;
    INT     ixBlits, ixLast,
            iyFirst, iyBlits, iyLast,
            xOffset, yOffset,
            i, j;

    DISPDBG((3, "S3.DLL!bColorVertCacheToScreen - Entry\n"));

    // Take into account the pptBrushOrg.

    xOffset = pptDest->x - pptBrushOrg->x;
    yOffset = pptDest->y - pptBrushOrg->y;

    if (xOffset < 0)
        xOffset  = 8 - (-xOffset % 8);
    else
        xOffset %= 8;

    if (yOffset < 0)
        yOffset  = 8 - (-yOffset % 8);
    else
        yOffset %= 8;

    // Color Expand the monochrome vertical cache to the screen.
    // This is done in a loop to handle the general case ROPs.

    if (psizDest->cy < 8 - yOffset)
    {
        iyFirst = psizDest->cy;
        iyBlits = 0;
        iyLast  = 0;

    }
    else if (psizDest->cy - yOffset < COLOR_VERT_EXPANSION_CACHE_CY - 8)
    {
        iyFirst = 8 - yOffset;
        iyBlits = 0;
        iyLast  = psizDest->cy - iyFirst;
    }
    else
    {
        iyFirst = 8 - yOffset;
        iyBlits = (psizDest->cy - iyFirst) / (COLOR_VERT_EXPANSION_CACHE_CY - 8);
        iyLast  = (psizDest->cy - iyFirst) % (COLOR_VERT_EXPANSION_CACHE_CY - 8);
    }

    ixBlits = psizDest->cx / 8;
    ixLast  = psizDest->cx % 8;

    Cmd  = BITBLT         | DRAW | DIR_TYPE_XY | WRITE |
           MULTIPLE_PIXELS| DRAWING_DIR_TBLRXM;

    FIFOWAIT(FIFO_4_EMPTY);

    TEST_AND_SET_FRGD_MIX(SRC_DISPLAY_MEMORY | s3ForeRop);
    OUTPW(MULTIFUNC_CNTL, (DATA_EXTENSION | ALL_ONES));
    OUTPW(MULTIFUNC_CNTL, (RECT_HEIGHT | 7));
    OUTPW(RECT_WIDTH, 7);

    for(i = 0; i < ixBlits; i++)
    {
        ASSERTS3(((pptDest->x + (i * 8) + 7) < (INT) ppdev->cxScreen),
                  "S3.DLL!bColorVertCacheToScreen - Blit Operation over right edge (1)\n");

        if (iyFirst != 0)
        {
            FIFOWAIT(FIFO_6_EMPTY);

            OUTPW(MULTIFUNC_CNTL, (RECT_HEIGHT | iyFirst - 1));

            OUTPW(CUR_X,  COLOR_VERT_EXPANSION_CACHE_X + xOffset);
            OUTPW(CUR_Y,  COLOR_VERT_EXPANSION_CACHE_Y + yOffset);
            OUTPW(DEST_X, pptDest->x + (i * 8));
            OUTPW(DEST_Y, pptDest->y);

            OUTPW(CMD, Cmd);
        }

        if (iyBlits != 0)
        {
            FIFOWAIT(FIFO_1_EMPTY);
            OUTPW(MULTIFUNC_CNTL, (RECT_HEIGHT | (COLOR_VERT_EXPANSION_CACHE_CY - 8) - 1));
        }

        for (j = 0; j <iyBlits; j++)
        {
            FIFOWAIT(FIFO_5_EMPTY);

            OUTPW(CUR_X,  COLOR_VERT_EXPANSION_CACHE_X + xOffset);
            OUTPW(CUR_Y,  COLOR_VERT_EXPANSION_CACHE_Y);
            OUTPW(DEST_X, pptDest->x + (i * 8));
            OUTPW(DEST_Y, pptDest->y + iyFirst +
                          (j * (COLOR_VERT_EXPANSION_CACHE_CY - 8)));

            OUTPW(CMD, Cmd);
        }

        if (iyLast != 0)
        {
            FIFOWAIT(FIFO_6_EMPTY);

            OUTPW(MULTIFUNC_CNTL, (RECT_HEIGHT | iyLast - 1));

            OUTPW(CUR_X,  COLOR_VERT_EXPANSION_CACHE_X + xOffset);
            OUTPW(CUR_Y,  COLOR_VERT_EXPANSION_CACHE_Y);
            OUTPW(DEST_X, pptDest->x + (i * 8));
            OUTPW(DEST_Y, pptDest->y + iyFirst +
                          (iyBlits * (COLOR_VERT_EXPANSION_CACHE_CY - 8)));

            OUTPW(CMD, Cmd);
        }
    }

    if (ixLast != 0)
    {
        FIFOWAIT(FIFO_1_EMPTY);
        OUTPW(RECT_WIDTH, ixLast - 1);

        ASSERTS3(((pptDest->x + (ixBlits * 8) + (ixLast - 1)) < (INT) ppdev->cxScreen),
                  "S3.DLL!bColorVertCacheToScreen - Blit Operation over right edge (2)\n");

        if (iyFirst != 0)
        {
            FIFOWAIT(FIFO_6_EMPTY);

            OUTPW(MULTIFUNC_CNTL, (RECT_HEIGHT | iyFirst - 1));

            OUTPW(CUR_X,  COLOR_VERT_EXPANSION_CACHE_X + xOffset);
            OUTPW(CUR_Y,  COLOR_VERT_EXPANSION_CACHE_Y + yOffset);
            OUTPW(DEST_X, pptDest->x + (ixBlits * 8));
            OUTPW(DEST_Y, pptDest->y);

            OUTPW(CMD, Cmd);
        }

        if (iyBlits != 0)
        {
            FIFOWAIT(FIFO_1_EMPTY);
            OUTPW(MULTIFUNC_CNTL, (RECT_HEIGHT | (COLOR_VERT_EXPANSION_CACHE_CY - 8) - 1));
        }

        for (j = 0; j <iyBlits; j++)
        {
            FIFOWAIT(FIFO_5_EMPTY);

            OUTPW(CUR_X,  COLOR_VERT_EXPANSION_CACHE_X + xOffset);
            OUTPW(CUR_Y,  COLOR_VERT_EXPANSION_CACHE_Y);
            OUTPW(DEST_X, pptDest->x + (ixBlits * 8));
            OUTPW(DEST_Y, pptDest->y + iyFirst +
                          (j * (COLOR_VERT_EXPANSION_CACHE_CY - 8)));

            OUTPW(CMD, Cmd);
        }

        if (iyLast != 0)
        {
            FIFOWAIT(FIFO_6_EMPTY);

            OUTPW(MULTIFUNC_CNTL, (RECT_HEIGHT | iyLast - 1));

            OUTPW(CUR_X,  COLOR_VERT_EXPANSION_CACHE_X + xOffset);
            OUTPW(CUR_Y,  COLOR_VERT_EXPANSION_CACHE_Y);
            OUTPW(DEST_X, pptDest->x + (ixBlits * 8));
            OUTPW(DEST_Y, pptDest->y + iyFirst +
                          (iyBlits * (COLOR_VERT_EXPANSION_CACHE_CY - 8)));

            OUTPW(CMD, Cmd);
        }
    }

    return(TRUE);
}

/*****************************************************************************
 * Color expand the monochrome brush in the Vertical Cache to the screen.
 ****************************************************************************/
BOOL bColorExpandVertCacheToScreen(
    PPDEV       ppdev,
    PS3BRUSH    ps3Brush,
    INT         zVert,
    PPOINT      pptBrushOrg,
    PPOINT      pptDest,
    PSIZE       psizDest,
    WORD        s3ForeRop,
    WORD        s3BackRop)
{
    WORD    Cmd;
    INT     ixBlits, ixLast,
            iyFirst, iyBlits, iyLast,
            xOffset, yOffset,
            i, j;

    DISPDBG((3, "S3.DLL!bColorExpandVertCacheToScreen - Entry\n"));

    // Take into account the pptBrushOrg.

    xOffset = pptDest->x - pptBrushOrg->x;
    yOffset = pptDest->y - pptBrushOrg->y;

    if (xOffset < 0)
        xOffset  = 8 - (-xOffset % 8);
    else
        xOffset %= 8;

    if (yOffset < 0)
        yOffset  = 8 - (-yOffset % 8);
    else
        yOffset %= 8;

    // Color Expand the monochrome vertical cache to the screen.
    // This is done in a loop to handle the general case ROPs.

    if (psizDest->cy < 8 - yOffset)
    {
        iyFirst = psizDest->cy;
        iyBlits = 0;
        iyLast  = 0;

    }
    else if (psizDest->cy - yOffset < MONO_VERT_EXPANSION_CACHE_CY - 8)
    {
        iyFirst = 8 - yOffset;
        iyBlits = 0;
        iyLast  = psizDest->cy - iyFirst;
    }
    else
    {
        iyFirst = 8 - yOffset;
        iyBlits = (psizDest->cy - iyFirst) / (MONO_VERT_EXPANSION_CACHE_CY - 8);
        iyLast  = (psizDest->cy - iyFirst) % (MONO_VERT_EXPANSION_CACHE_CY - 8);
    }

    ixBlits = psizDest->cx / 8;
    ixLast  = psizDest->cx % 8;

    Cmd  = BITBLT         | DRAW | DIR_TYPE_XY | WRITE |
           MULTIPLE_PIXELS| DRAWING_DIR_TBLRXM;

    FIFOWAIT(FIFO_7_EMPTY);

    TEST_AND_SET_RD_MASK(zVert);

    TEST_AND_SET_FRGD_MIX(FOREGROUND_COLOR | s3ForeRop);
    TEST_AND_SET_FRGD_COLOR(LOWORD(ps3Brush->ulForeColor));

    TEST_AND_SET_BKGD_MIX(BACKGROUND_COLOR | s3BackRop);
    SET_BKGD_COLOR(LOWORD(ps3Brush->ulBackColor));

    OUTPW(MULTIFUNC_CNTL, (DATA_EXTENSION | DISPLAY_MEMORY));

    OUTPW(RECT_WIDTH, 7);

    for(i = 0; i < ixBlits; i++)
    {
        ASSERTS3(((pptDest->x + (i * 8) + 7) < (INT) ppdev->cxScreen),
                  "S3.DLL!bColorExpandVertCacheToScreen - Blit Operation over right edge (1)\n");

        if (iyFirst != 0)
        {
            FIFOWAIT(FIFO_6_EMPTY);

            OUTPW(MULTIFUNC_CNTL, (RECT_HEIGHT | iyFirst - 1));

            OUTPW(CUR_X,  MONO_VERT_EXPANSION_CACHE_X + xOffset);
            OUTPW(CUR_Y,  MONO_VERT_EXPANSION_CACHE_Y + yOffset);
            OUTPW(DEST_X, pptDest->x + (i * 8));
            OUTPW(DEST_Y, pptDest->y);

            OUTPW(CMD, Cmd);
        }

        if (iyBlits != 0)
        {
            FIFOWAIT(FIFO_1_EMPTY);
            OUTPW(MULTIFUNC_CNTL, (RECT_HEIGHT | (MONO_VERT_EXPANSION_CACHE_CY - 8) - 1));
        }

        for (j = 0; j <iyBlits; j++)
        {
            FIFOWAIT(FIFO_5_EMPTY);

            OUTPW(CUR_X,  MONO_VERT_EXPANSION_CACHE_X + xOffset);
            OUTPW(CUR_Y,  MONO_VERT_EXPANSION_CACHE_Y);
            OUTPW(DEST_X, pptDest->x + (i * 8));
            OUTPW(DEST_Y, pptDest->y + iyFirst +
                          (j * (MONO_VERT_EXPANSION_CACHE_CY - 8)));

            OUTPW(CMD, Cmd);
        }

        if (iyLast != 0)
        {
            FIFOWAIT(FIFO_6_EMPTY);

            OUTPW(MULTIFUNC_CNTL, (RECT_HEIGHT | iyLast - 1));

            OUTPW(CUR_X,  MONO_VERT_EXPANSION_CACHE_X + xOffset);
            OUTPW(CUR_Y,  MONO_VERT_EXPANSION_CACHE_Y);
            OUTPW(DEST_X, pptDest->x + (i * 8));
            OUTPW(DEST_Y, pptDest->y + iyFirst +
                          (iyBlits * (MONO_VERT_EXPANSION_CACHE_CY - 8)));

            OUTPW(CMD, Cmd);
        }
    }

    if (ixLast != 0)
    {
        FIFOWAIT(FIFO_1_EMPTY);
        OUTPW(RECT_WIDTH, ixLast - 1);

        ASSERTS3(((pptDest->x + (ixBlits * 8) + (ixLast - 1)) < (INT) ppdev->cxScreen),
                  "S3.DLL!bColorExpandVertCacheToScreen - Blit Operation over right edge (2)\n");

        if (iyFirst != 0)
        {
            FIFOWAIT(FIFO_6_EMPTY);

            OUTPW(MULTIFUNC_CNTL, (RECT_HEIGHT | iyFirst - 1));

            OUTPW(CUR_X,  MONO_VERT_EXPANSION_CACHE_X + xOffset);
            OUTPW(CUR_Y,  MONO_VERT_EXPANSION_CACHE_Y + yOffset);
            OUTPW(DEST_X, pptDest->x + (ixBlits * 8));
            OUTPW(DEST_Y, pptDest->y);

            OUTPW(CMD, Cmd);
        }

        if (iyBlits != 0)
        {
            FIFOWAIT(FIFO_1_EMPTY);
            OUTPW(MULTIFUNC_CNTL, (RECT_HEIGHT | (MONO_VERT_EXPANSION_CACHE_CY - 8) - 1));
        }

        for (j = 0; j <iyBlits; j++)
        {
            FIFOWAIT(FIFO_5_EMPTY);

            OUTPW(CUR_X,  MONO_VERT_EXPANSION_CACHE_X + xOffset);
            OUTPW(CUR_Y,  MONO_VERT_EXPANSION_CACHE_Y);
            OUTPW(DEST_X, pptDest->x + (ixBlits * 8));
            OUTPW(DEST_Y, pptDest->y + iyFirst +
                          (j * (MONO_VERT_EXPANSION_CACHE_CY - 8)));

            OUTPW(CMD, Cmd);
        }

        if (iyLast != 0)
        {
            FIFOWAIT(FIFO_6_EMPTY);

            OUTPW(MULTIFUNC_CNTL, (RECT_HEIGHT | iyLast - 1));

            OUTPW(CUR_X,  MONO_VERT_EXPANSION_CACHE_X + xOffset);
            OUTPW(CUR_Y,  MONO_VERT_EXPANSION_CACHE_Y);
            OUTPW(DEST_X, pptDest->x + (ixBlits * 8));
            OUTPW(DEST_Y, pptDest->y + iyFirst +
                          (iyBlits * (MONO_VERT_EXPANSION_CACHE_CY - 8)));

            OUTPW(CMD, Cmd);
        }
    }

    // Reset the Read Mask

    FIFOWAIT(FIFO_2_EMPTY);

    TEST_AND_SET_WRT_MASK(0xff);
    TEST_AND_SET_RD_MASK(0xff);

    return(TRUE);
}


/*****************************************************************************
 * Color brush in the Hoizontal Cache to the screen.
 ****************************************************************************/
BOOL bColorHorzCacheToScreen(
    PPDEV       ppdev,
    PS3BRUSH    ps3Brush,
    PPOINT      pptBrushOrg,
    PPOINT      pptDest,
    PSIZE       psizDest,
    WORD        s3ForeRop,
    WORD        s3BackRop)
{
    WORD    Cmd;
    INT     xLeft, cxLeft, cxRight, ixBlits, i, j, x, cx,
            iyBlits, iyLast,
            xOffset, yOffset;

    DISPDBG((3, "S3.DLL!bColorHorzCacheToScreen - Entry\n"));

    // Take into account the pptBrushOrg.

    xOffset = pptDest->x - pptBrushOrg->x;
    yOffset = pptDest->y - pptBrushOrg->y;

    if (xOffset < 0)
        xOffset  = 8 - (-xOffset % 8);
    else
        xOffset %= 8;

    if (yOffset < 0)
        yOffset  = 8 - (-yOffset % 8);
    else
        yOffset %= 8;

    // Color Expand the monochrome horizontal cache to the screen.
    // This is done in a loop to handle the general case ROPs.

    xLeft  = pptDest->x;
    cxLeft = COLOR_HORZ_EXPANSION_CACHE_CX - xOffset;

    if ((cx = psizDest->cx - cxLeft) < 0)
    {
        cxLeft  = psizDest->cx;
        ixBlits = 0;
        cxRight = 0;
    }
    else
    {
        ixBlits = cx / COLOR_HORZ_EXPANSION_CACHE_CX;
        cxRight = cx % COLOR_HORZ_EXPANSION_CACHE_CX;
    }

    iyBlits = psizDest->cy / 8;
    iyLast  = psizDest->cy % 8;

    Cmd  = BITBLT         | DRAW | DIR_TYPE_XY | WRITE |
           MULTIPLE_PIXELS| DRAWING_DIR_TBLRXM;

    FIFOWAIT(FIFO_3_EMPTY);

    TEST_AND_SET_FRGD_MIX(SRC_DISPLAY_MEMORY | s3ForeRop);
    OUTPW(MULTIFUNC_CNTL, (DATA_EXTENSION | ALL_ONES));
    OUTPW(MULTIFUNC_CNTL, (RECT_HEIGHT | 7));

    for (i = 0; i < iyBlits; i++)
    {
        ASSERTS3(((pptDest->x + (cxLeft - 1)) < (INT) ppdev->cxScreen),
                  "S3.DLL!bColorHorzCacheToScreen - Blit Operation over right edge (1)\n");

        FIFOWAIT(FIFO_6_EMPTY);

        OUTPW(RECT_WIDTH, cxLeft - 1);

        OUTPW(CUR_X,  COLOR_HORZ_EXPANSION_CACHE_X + xOffset);
        OUTPW(CUR_Y,  COLOR_HORZ_EXPANSION_CACHE_Y + yOffset);
        OUTPW(DEST_X, pptDest->x);
        OUTPW(DEST_Y, pptDest->y + (i * 8));

        OUTPW(CMD, Cmd);

        // If the the cxDest is wider than the cxHorzCache then fill in the
        // right side of the fill area.

        if (cx > 0)
        {
            FIFOWAIT(FIFO_1_EMPTY);
            OUTPW(RECT_WIDTH, COLOR_HORZ_EXPANSION_CACHE_CX - 1);

            for (j = 0; j < ixBlits; j++)
            {
                x = pptDest->x + cxLeft + (j * COLOR_HORZ_EXPANSION_CACHE_CX);

                ASSERTS3(((x + (COLOR_HORZ_EXPANSION_CACHE_CX - 1)) < (INT) ppdev->cxScreen),
                          "S3.DLL!bColorHorzCacheToScreen - Blit Operation over right edge (2)\n");

                FIFOWAIT(FIFO_5_EMPTY);

                OUTPW(CUR_X,  COLOR_HORZ_EXPANSION_CACHE_X);
                OUTPW(CUR_Y,  COLOR_HORZ_EXPANSION_CACHE_Y + yOffset);
                OUTPW(DEST_X, x);
                OUTPW(DEST_Y, pptDest->y + (i * 8));

                OUTPW(CMD, Cmd);
            }

            if (cxRight != 0)
            {
                x = pptDest->x + cxLeft + (ixBlits * COLOR_HORZ_EXPANSION_CACHE_CX);

                ASSERTS3(((x + (cxRight - 1)) < (INT) ppdev->cxScreen),
                          "S3.DLL!bColorHorzCacheToScreen - Blit Operation over right edge (3)\n");

                FIFOWAIT(FIFO_6_EMPTY);

                OUTPW(RECT_WIDTH, cxRight - 1);

                OUTPW(CUR_X,  COLOR_HORZ_EXPANSION_CACHE_X);
                OUTPW(CUR_Y,  COLOR_HORZ_EXPANSION_CACHE_Y + yOffset);
                OUTPW(DEST_X, x);
                OUTPW(DEST_Y, pptDest->y + (i * 8));

                OUTPW(CMD, Cmd);
            }
        }
    }

    if (iyLast != 0)
    {
        ASSERTS3(((pptDest->x + (cxLeft - 1)) < (INT) ppdev->cxScreen),
                  "S3.DLL!bColorHorzCacheToScreen - Blit Operation over right edge (4)\n");

        FIFOWAIT(FIFO_7_EMPTY);

        OUTPW(RECT_WIDTH, cxLeft - 1);
        OUTPW(MULTIFUNC_CNTL, (RECT_HEIGHT | iyLast - 1));

        OUTPW(CUR_X,  COLOR_HORZ_EXPANSION_CACHE_X + xOffset);
        OUTPW(CUR_Y,  COLOR_HORZ_EXPANSION_CACHE_Y + yOffset);
        OUTPW(DEST_X, pptDest->x);
        OUTPW(DEST_Y, pptDest->y + (iyBlits * 8));

        OUTPW(CMD, Cmd);

        // If the the cxDest is wider than the cxHorzCache then fill in the
        // right side of the fill area.

        if (cx > 0)
        {
            FIFOWAIT(FIFO_1_EMPTY);
            OUTPW(RECT_WIDTH, COLOR_HORZ_EXPANSION_CACHE_CX - 1);

            for (j = 0; j < ixBlits; j++)
            {
                x = pptDest->x + cxLeft + (j * COLOR_HORZ_EXPANSION_CACHE_CX);

                ASSERTS3(((x + (COLOR_HORZ_EXPANSION_CACHE_CX - 1)) < (INT) ppdev->cxScreen),
                          "S3.DLL!bColorHorzCacheToScreen - Blit Operation over right edge (5)\n");

                FIFOWAIT(FIFO_5_EMPTY);

                OUTPW(CUR_X,  COLOR_HORZ_EXPANSION_CACHE_X);
                OUTPW(CUR_Y,  COLOR_HORZ_EXPANSION_CACHE_Y + yOffset);
                OUTPW(DEST_X, x);
                OUTPW(DEST_Y, pptDest->y + (iyBlits * 8));

                OUTPW(CMD, Cmd);
            }

            if (cxRight != 0)
            {
                x = pptDest->x + cxLeft + (ixBlits * COLOR_HORZ_EXPANSION_CACHE_CX);

                ASSERTS3(((x + (cxRight - 1)) < (INT) ppdev->cxScreen),
                          "S3.DLL!bColorHorzCacheToScreen - Blit Operation over right edge (6)\n");

                FIFOWAIT(FIFO_6_EMPTY);

                OUTPW(RECT_WIDTH, cxRight - 1);

                OUTPW(CUR_X,  COLOR_HORZ_EXPANSION_CACHE_X);
                OUTPW(CUR_Y,  COLOR_HORZ_EXPANSION_CACHE_Y + yOffset);
                OUTPW(DEST_X, x);
                OUTPW(DEST_Y, pptDest->y + (iyBlits * 8));

                OUTPW(CMD, Cmd);
            }
        }
    }

    return(TRUE);
}

/*****************************************************************************
 * Color expand the monochrome brush in the Hoizontal Cache to the screen.
 ****************************************************************************/
BOOL bColorExpandHorzCacheToScreen(
    PPDEV       ppdev,
    PS3BRUSH    ps3Brush,
    INT         zHorz,
    PPOINT      pptBrushOrg,
    PPOINT      pptDest,
    PSIZE       psizDest,
    WORD        s3ForeRop,
    WORD        s3BackRop)
{
    WORD    Cmd;
    INT     xLeft, cxLeft, xMiddle, cxMiddle, xRight, cxRight, i,
            iyBlits, iyLast,
            xOffset, yOffset;

    DISPDBG((3, "S3.DLL!bColorExpandHorzCacheToScreen - Entry\n"));

    // Take into account the pptBrushOrg.

    xOffset = pptDest->x - pptBrushOrg->x;
    yOffset = pptDest->y - pptBrushOrg->y;

    if (xOffset < 0)
        xOffset  = 8 - (-xOffset % 8);
    else
        xOffset %= 8;

    if (yOffset < 0)
        yOffset  = 8 - (-yOffset % 8);
    else
        yOffset %= 8;

    // Color Expand the monochrome horizontal cache to the screen.
    // This is done in a loop to handle the general case ROPs.

    xLeft  = pptDest->x;
    cxLeft = MONO_HORZ_EXPANSION_CACHE_CX - xOffset;

    if (psizDest->cx < cxLeft)
    {
        cxLeft  = psizDest->cx;
        xRight  = 0;
        cxRight = 0;
    }
    else
    {
        xRight  = pptDest->x + cxLeft;
        cxRight = psizDest->cx - cxLeft;
    }

    // This little hack is for 1280 mode.
    // Really the correct solution is to use the H/W support.

    if (cxRight > MONO_HORZ_EXPANSION_CACHE_CX)
    {
        xMiddle  = xRight;
        xRight  += MONO_HORZ_EXPANSION_CACHE_CX;
        cxMiddle = MONO_HORZ_EXPANSION_CACHE_CX;
        cxRight -= MONO_HORZ_EXPANSION_CACHE_CX;
    }
    else
    {
        xMiddle  = 0;
        cxMiddle = 0;
    }

    iyBlits = psizDest->cy / 8;
    iyLast  = psizDest->cy % 8;

    Cmd  = BITBLT         | DRAW | DIR_TYPE_XY | WRITE |
           MULTIPLE_PIXELS| DRAWING_DIR_TBLRXM;

    FIFOWAIT(FIFO_7_EMPTY);

    TEST_AND_SET_RD_MASK(zHorz);

    TEST_AND_SET_FRGD_MIX(FOREGROUND_COLOR | s3ForeRop);
    TEST_AND_SET_FRGD_COLOR(LOWORD(ps3Brush->ulForeColor));

    TEST_AND_SET_BKGD_MIX(BACKGROUND_COLOR | s3BackRop);
    SET_BKGD_COLOR(LOWORD(ps3Brush->ulBackColor));

    OUTPW(MULTIFUNC_CNTL, (DATA_EXTENSION | DISPLAY_MEMORY));

    OUTPW(MULTIFUNC_CNTL, (RECT_HEIGHT | 7));

    for (i = 0; i < iyBlits; i++)
    {
        ASSERTS3(((pptDest->x + (cxLeft - 1)) < (INT) ppdev->cxScreen),
                  "S3.DLL!bColorExpandHorzCacheToScreen - Blit Operation over right edge (1)\n");

        FIFOWAIT(FIFO_6_EMPTY);

        OUTPW(RECT_WIDTH, cxLeft - 1);

        OUTPW(CUR_X,  MONO_HORZ_EXPANSION_CACHE_X + xOffset);
        OUTPW(CUR_Y,  MONO_HORZ_EXPANSION_CACHE_Y + yOffset);
        OUTPW(DEST_X, pptDest->x);
        OUTPW(DEST_Y, pptDest->y + (i * 8));

        OUTPW(CMD, Cmd);

        if (cxMiddle != 0)
        {
            ASSERTS3(((xMiddle + (cxMiddle - 1)) < (INT) ppdev->cxScreen),
                      "S3.DLL!bColorExpandHorzCacheToScreen - Blit Operation over right edge (2)\n");

            FIFOWAIT(FIFO_6_EMPTY);

            OUTPW(RECT_WIDTH, cxMiddle - 1);

            OUTPW(CUR_X,  MONO_HORZ_EXPANSION_CACHE_X);
            OUTPW(CUR_Y,  MONO_HORZ_EXPANSION_CACHE_Y + yOffset);
            OUTPW(DEST_X, xMiddle);
            OUTPW(DEST_Y, pptDest->y + (i * 8));

            OUTPW(CMD, Cmd);
        }

        // If the the cxDest is wider than the cxHorzCache then fill in the
        // right side of the fill area.

        if (cxRight != 0)
        {
            ASSERTS3(((xRight + (cxRight - 1)) < (INT) ppdev->cxScreen),
                      "S3.DLL!bColorExpandHorzCacheToScreen - Blit Operation over right edge (3)\n");

            FIFOWAIT(FIFO_6_EMPTY);

            OUTPW(RECT_WIDTH, cxRight - 1);

            OUTPW(CUR_X,  MONO_HORZ_EXPANSION_CACHE_X);
            OUTPW(CUR_Y,  MONO_HORZ_EXPANSION_CACHE_Y + yOffset);
            OUTPW(DEST_X, xRight);
            OUTPW(DEST_Y, pptDest->y + (i * 8));

            OUTPW(CMD, Cmd);
        }
    }

    if (iyLast != 0)
    {
        ASSERTS3(((pptDest->x + (cxLeft - 1)) < (INT) ppdev->cxScreen),
                  "S3.DLL!bColorExpandHorzCacheToScreen - Blit Operation over right edge (3)\n");

        FIFOWAIT(FIFO_7_EMPTY);

        OUTPW(RECT_WIDTH, cxLeft - 1);
        OUTPW(MULTIFUNC_CNTL, (RECT_HEIGHT | iyLast - 1));

        OUTPW(CUR_X,  MONO_HORZ_EXPANSION_CACHE_X + xOffset);
        OUTPW(CUR_Y,  MONO_HORZ_EXPANSION_CACHE_Y + yOffset);
        OUTPW(DEST_X, pptDest->x);
        OUTPW(DEST_Y, pptDest->y + (iyBlits * 8));

        OUTPW(CMD, Cmd);

        if (cxMiddle != 0)
        {
            ASSERTS3(((xMiddle + (cxMiddle - 1)) < (INT) ppdev->cxScreen),
                      "S3.DLL!bColorExpandHorzCacheToScreen - Blit Operation over right edge (4)\n");

            FIFOWAIT(FIFO_6_EMPTY);

            OUTPW(RECT_WIDTH, cxMiddle - 1);

            OUTPW(CUR_X,  MONO_HORZ_EXPANSION_CACHE_X);
            OUTPW(CUR_Y,  MONO_HORZ_EXPANSION_CACHE_Y + yOffset);
            OUTPW(DEST_X, xMiddle);
            OUTPW(DEST_Y, pptDest->y + (iyBlits * 8));

            OUTPW(CMD, Cmd);
        }

        // If the the cxDest is wider than the cxHorzCache then fill in the
        // right side of the fill area.

        if (cxRight != 0)
        {
            ASSERTS3(((xRight + (cxRight - 1)) < (INT) ppdev->cxScreen),
                      "S3.DLL!bColorExpandHorzCacheToScreen - Blit Operation over right edge (4)\n");

            FIFOWAIT(FIFO_6_EMPTY);

            OUTPW(RECT_WIDTH, cxRight - 1);

            OUTPW(CUR_X,  MONO_HORZ_EXPANSION_CACHE_X);
            OUTPW(CUR_Y,  MONO_HORZ_EXPANSION_CACHE_Y + yOffset);
            OUTPW(DEST_X, xRight);
            OUTPW(DEST_Y, pptDest->y + (iyBlits * 8));

            OUTPW(CMD, Cmd);
        }
    }

    // Reset the Read Mask

    FIFOWAIT(FIFO_2_EMPTY);

    TEST_AND_SET_WRT_MASK(0xff);
    TEST_AND_SET_RD_MASK(0xff);

    return(TRUE);
}


/*****************************************************************************
 * Copy Optimized
 * Color brush in the Hoizontal Cache to the screen.
 ****************************************************************************/
BOOL bCpyColorHorzCacheToScreen(
    PPDEV       ppdev,
    PS3BRUSH    ps3Brush,
    PPOINT      pptBrushOrg,
    PPOINT      pptDest,
    PSIZE       psizDest)
{
    WORD    Cmd;
    INT     cx, cxLeft, ixBlits, ixLast, i,
            cy, x, xOffset, yOffset;

    DISPDBG((3, "S3.DLL!bCpyColorHorzCacheToScreen - Entry\n"));

    // Take into account the pptBrushOrg.

    xOffset = pptDest->x - pptBrushOrg->x;
    yOffset = pptDest->y - pptBrushOrg->y;

    if (xOffset < 0)
        xOffset  = 8 - (-xOffset % 8);
    else
        xOffset %= 8;

    if (yOffset < 0)
        yOffset  = 8 - (-yOffset % 8);
    else
        yOffset %= 8;

    // First Color Expand the monochrome horizontal cache to the screen.

    cxLeft = COLOR_HORZ_EXPANSION_CACHE_CX - xOffset;
    if (psizDest->cx < cxLeft)
        cxLeft = psizDest->cx;

    cy = 8;
    if (psizDest->cy < cy)
        cy = psizDest->cy;

    Cmd  = BITBLT         | DRAW | DIR_TYPE_XY | WRITE |
           MULTIPLE_PIXELS| DRAWING_DIR_TBLRXM;

    ASSERTS3(((pptDest->x + (cxLeft - 1)) < (INT) ppdev->cxScreen),
              "S3.DLL!bCpyColorHorzCacheToScreen - Blit Operation over right edge (1)\n");

    FIFOWAIT(FIFO_2_EMPTY);

    TEST_AND_SET_FRGD_MIX(SRC_DISPLAY_MEMORY | OVERPAINT);
    OUTPW(MULTIFUNC_CNTL, (DATA_EXTENSION | ALL_ONES));

    FIFOWAIT(FIFO_7_EMPTY);

    OUTPW(RECT_WIDTH, cxLeft - 1);
    OUTPW(MULTIFUNC_CNTL, (RECT_HEIGHT | (cy - 1)));

    OUTPW(CUR_X,  COLOR_HORZ_EXPANSION_CACHE_X + xOffset);
    OUTPW(CUR_Y,  COLOR_HORZ_EXPANSION_CACHE_Y + yOffset);
    OUTPW(DEST_X, pptDest->x);
    OUTPW(DEST_Y, pptDest->y);

    OUTPW(CMD, Cmd);

    // If the the cxDest is wider than the cxHorzCache then fill in the
    // right side of the fill area.

    if ((cx = psizDest->cx - cxLeft) > 0)
    {
        ixBlits = cx / COLOR_HORZ_EXPANSION_CACHE_CX;
        ixLast  = cx % COLOR_HORZ_EXPANSION_CACHE_CX;

        FIFOWAIT(FIFO_1_EMPTY);
        OUTPW(RECT_WIDTH, COLOR_HORZ_EXPANSION_CACHE_CX - 1);

        for (i = 0; i < ixBlits; i++)
        {
            x = pptDest->x + cxLeft + (i * COLOR_HORZ_EXPANSION_CACHE_CX);

            ASSERTS3(((x + (COLOR_HORZ_EXPANSION_CACHE_CX - 1)) < (INT) ppdev->cxScreen),
                      "S3.DLL!bCpyColorHorzCacheToScreen - Blit Operation over right edge (2)\n");

            FIFOWAIT(FIFO_5_EMPTY);

            OUTPW(CUR_X,  COLOR_HORZ_EXPANSION_CACHE_X);
            OUTPW(CUR_Y,  COLOR_HORZ_EXPANSION_CACHE_Y + yOffset);
            OUTPW(DEST_X, x);
            OUTPW(DEST_Y, pptDest->y);

            OUTPW(CMD, Cmd);
        }

        if (ixLast != 0)
        {
            x = pptDest->x + cxLeft + (ixBlits * COLOR_HORZ_EXPANSION_CACHE_CX);

            ASSERTS3(((x + (ixLast - 1)) < (INT) ppdev->cxScreen),
                      "S3.DLL!bCpyColorHorzCacheToScreen - Blit Operation over right edge (3)\n");

            FIFOWAIT(FIFO_6_EMPTY);

            OUTPW(RECT_WIDTH, ixLast - 1);

            OUTPW(CUR_X,  COLOR_HORZ_EXPANSION_CACHE_X);
            OUTPW(CUR_Y,  COLOR_HORZ_EXPANSION_CACHE_Y + yOffset);
            OUTPW(DEST_X, x);
            OUTPW(DEST_Y, pptDest->y);

            OUTPW(CMD, Cmd);
        }
    }

    // If the cyDest is taller than 8 pels do a rolling blit to fill in
    // the bottom of the fill area.

    if (psizDest->cy > 8)
    {
        ASSERTS3(((pptDest->x + (psizDest->cx - 1)) < (INT) ppdev->cxScreen),
                  "S3.DLL!bCpyColorHorzCacheToScreen - Blit Operation over right edge (4)\n");

        FIFOWAIT(FIFO_7_EMPTY);

        OUTPW(RECT_WIDTH, psizDest->cx - 1);
        OUTPW(MULTIFUNC_CNTL, (RECT_HEIGHT | psizDest->cy - (cy + 1)));

        OUTPW(CUR_X,  pptDest->x);
        OUTPW(CUR_Y,  pptDest->y);
        OUTPW(DEST_X, pptDest->x);
        OUTPW(DEST_Y, pptDest->y + 8);

        OUTPW(CMD, Cmd);
    }

    return(TRUE);
}

/*****************************************************************************
 * Copy Optimized
 * Color expand the monochrome brush in the Hoizontal Cache to the screen.
 ****************************************************************************/
BOOL bCpyColorExpandHorzCacheToScreen(
    PPDEV       ppdev,
    PS3BRUSH    ps3Brush,
    INT         zHorz,
    PPOINT      pptBrushOrg,
    PPOINT      pptDest,
    PSIZE       psizDest)
{
    WORD    Cmd;
    INT     cx, cxLeft, ixBlits, ixLast, i,
            cy, x, xOffset, yOffset;

    DISPDBG((3, "S3.DLL!bCpyColorExpandHorzCacheToScreen - Entry\n"));

    // Take into account the pptBrushOrg.

    xOffset = pptDest->x - pptBrushOrg->x;
    yOffset = pptDest->y - pptBrushOrg->y;

    if (xOffset < 0)
        xOffset  = 8 - (-xOffset % 8);
    else
        xOffset %= 8;

    if (yOffset < 0)
        yOffset  = 8 - (-yOffset % 8);
    else
        yOffset %= 8;

    // First Color Expand the monochrome horizontal cache to the screen.

    cxLeft = MONO_HORZ_EXPANSION_CACHE_CX - xOffset;
    if (psizDest->cx < cxLeft)
        cxLeft = psizDest->cx;

    cy = 8;
    if (psizDest->cy < cy)
        cy = psizDest->cy;

    Cmd  = BITBLT         | DRAW | DIR_TYPE_XY | WRITE |
           MULTIPLE_PIXELS| DRAWING_DIR_TBLRXM;

    ASSERTS3(((pptDest->x + (cxLeft - 1)) < (INT) ppdev->cxScreen),
              "S3.DLL!bCpyColorExpandHorzCacheToScreen - Blit Operation over right edge (1)\n");

    FIFOWAIT(FIFO_6_EMPTY);

    TEST_AND_SET_RD_MASK(zHorz);

    TEST_AND_SET_FRGD_MIX(FOREGROUND_COLOR | OVERPAINT);
    TEST_AND_SET_FRGD_COLOR(LOWORD(ps3Brush->ulForeColor));

    TEST_AND_SET_BKGD_MIX(BACKGROUND_COLOR | OVERPAINT);
    SET_BKGD_COLOR(LOWORD(ps3Brush->ulBackColor));

    OUTPW(MULTIFUNC_CNTL, (DATA_EXTENSION | DISPLAY_MEMORY));

    FIFOWAIT(FIFO_7_EMPTY);

    OUTPW(RECT_WIDTH, cxLeft - 1);
    OUTPW(MULTIFUNC_CNTL, (RECT_HEIGHT | (cy - 1)));

    OUTPW(CUR_X,  MONO_HORZ_EXPANSION_CACHE_X + xOffset);
    OUTPW(CUR_Y,  MONO_HORZ_EXPANSION_CACHE_Y + yOffset);
    OUTPW(DEST_X, pptDest->x);
    OUTPW(DEST_Y, pptDest->y);

    OUTPW(CMD, Cmd);

    // If the the cxDest is wider than the cxHorzCache then fill in the
    // right side of the fill area.

    if ((cx = psizDest->cx - cxLeft) > 0)
    {
        ixBlits = cx / MONO_HORZ_EXPANSION_CACHE_CX;
        ixLast  = cx % MONO_HORZ_EXPANSION_CACHE_CX;

        FIFOWAIT(FIFO_1_EMPTY);
        OUTPW(RECT_WIDTH, MONO_HORZ_EXPANSION_CACHE_CX - 1);

        for (i = 0; i < ixBlits; i++)
        {
            x = pptDest->x + cxLeft + (i * MONO_HORZ_EXPANSION_CACHE_CX);

            ASSERTS3(((x + (COLOR_HORZ_EXPANSION_CACHE_CX - 1)) < (INT) ppdev->cxScreen),
                      "S3.DLL!bCpyColorExpandHorzCacheToScreen - Blit Operation over right edge (2)\n");

            FIFOWAIT(FIFO_5_EMPTY);

            OUTPW(CUR_X,  MONO_HORZ_EXPANSION_CACHE_X);
            OUTPW(CUR_Y,  MONO_HORZ_EXPANSION_CACHE_Y + yOffset);
            OUTPW(DEST_X, x);
            OUTPW(DEST_Y, pptDest->y);

            OUTPW(CMD, Cmd);
        }

        if (ixLast != 0)
        {
            x = pptDest->x + cxLeft + (ixBlits * MONO_HORZ_EXPANSION_CACHE_CX);

            ASSERTS3(((x + (ixLast - 1)) < (INT) ppdev->cxScreen),
                      "S3.DLL!bCpyColorExpandHorzCacheToScreen - Blit Operation over right edge (3)\n");

            FIFOWAIT(FIFO_6_EMPTY);

            OUTPW(RECT_WIDTH, ixLast - 1);

            OUTPW(CUR_X,  MONO_HORZ_EXPANSION_CACHE_X);
            OUTPW(CUR_Y,  MONO_HORZ_EXPANSION_CACHE_Y + yOffset);
            OUTPW(DEST_X, x);
            OUTPW(DEST_Y, pptDest->y);

            OUTPW(CMD, Cmd);
        }
    }

    // If the cyDest is taller than 8 pels do a rolling blit to fill in
    // the bottom of the fill area.

    if (psizDest->cy > 8)
    {
        ASSERTS3(((pptDest->x + (psizDest->cx - 1)) < (INT) ppdev->cxScreen),
                  "S3.DLL!bCpyColorExpandHorzCacheToScreen - Blit Operation over right edge (4)\n");

        FIFOWAIT(FIFO_2_EMPTY);

        TEST_AND_SET_RD_MASK(0xff);
        TEST_AND_SET_FRGD_MIX(SRC_DISPLAY_MEMORY | OVERPAINT);

        FIFOWAIT(FIFO_8_EMPTY);

        OUTPW(MULTIFUNC_CNTL, (DATA_EXTENSION | ALL_ONES));

        OUTPW(RECT_WIDTH, psizDest->cx - 1);
        OUTPW(MULTIFUNC_CNTL, (RECT_HEIGHT | psizDest->cy - (cy + 1)));

        OUTPW(CUR_X,  pptDest->x);
        OUTPW(CUR_Y,  pptDest->y);
        OUTPW(DEST_X, pptDest->x);
        OUTPW(DEST_Y, pptDest->y + 8);

        OUTPW(CMD, Cmd);
    }

    // Reset the Read Mask

    FIFOWAIT(FIFO_2_EMPTY);

    TEST_AND_SET_WRT_MASK(0xff);
    TEST_AND_SET_RD_MASK(0xff);

    return(TRUE);
}


/*****************************************************************************
 * S3 Solid Pattern
 *
 *  Returns TRUE if the blit was handled.
 ****************************************************************************/
BOOL bPatternSolid(
    SURFOBJ  *psoTrg,
    SURFOBJ  *psoSrc,
    SURFOBJ  *psoMask,
    CLIPOBJ  *pco,
    XLATEOBJ *pxlo,
    RECTL    *prclTrg,
    POINTL   *pptlSrc,
    POINTL   *pptlMask,
    BRUSHOBJ *pbo,
    POINTL   *pptlBrush,
    ROP4     s3Rop4)
{
    INT     width,height;
    WORD    s3Mix;
    BOOL    bMore;
    UINT        i;
    ENUMRECTS8  EnumRects8;

    LONG    rleft;                          // render coordinates
    LONG    rtop;
    LONG    rright;
    LONG    rbottom;
    LONG    cleft;                          // clip coorindates
    LONG    ctop;
    LONG    cright;
    LONG    cbottom;
    LONG    left;
    LONG    top;
    RECTL   rclBounds;
    BOOL    bClipRequired, bDrawRequired;
    PPDEV   ppdev;

    DISPDBG((3,"S3.DLL!bPatternSolid - Entry\n"));

    // Get the pdev.

    ppdev = (PPDEV) psoTrg->dhpdev;

    // Just use the for ground mix.

    s3Mix = (WORD) (s3Rop4 & 0xFF);

    // Dereference the target rectangle.

    rleft   = prclTrg->left;
    rtop    = prclTrg->top;
    rright  = prclTrg->right;
    rbottom = prclTrg->bottom;

    // Use software clipping. It's faster than H/W clipping.

    if (pco->iDComplexity != DC_COMPLEX)
    {
        if (pco->iDComplexity == DC_TRIVIAL)
        {
            width  = (rright - rleft) - 1;
            height = (rbottom - rtop) - 1;

            bDrawRequired = TRUE;
        }
        else
        {
            bDrawRequired = FALSE;

            rclBounds = pco->rclBounds;

            // First handle the trivial rejection.

            bClipRequired = bIntersectTest(prclTrg, &rclBounds);

            if (bClipRequired)
            {
                rleft   = max (rleft, rclBounds.left);
                rtop    = max (rtop, rclBounds.top);
                width  = (min(rright, rclBounds.right) - rleft) - 1;
                height = (min(rbottom, rclBounds.bottom) - rtop) - 1;

                bDrawRequired = TRUE;
            }
        }

        if (bDrawRequired && width >= 0 && height >= 0)
        {
            vS3SolidPattern(ppdev,
                            rleft, rtop, width, height,
                            (pbo != NULL)? pbo->iSolidColor: 0, s3Mix);
        }
    }
    else
    {
        CLIPOBJ_cEnumStart(pco, FALSE, CT_RECTANGLES, CD_ANY, 0);

        do
        {
            bMore = CLIPOBJ_bEnum(pco, sizeof (ENUMRECTS8),
                                  (PULONG) &EnumRects8);
            for (i = 0; i < EnumRects8.c; i++)
            {
                // Do a trivial reject on Y.

                if (rtop > (cbottom = EnumRects8.arcl[i].bottom))
                    continue;
                if (rbottom < (ctop = EnumRects8.arcl[i].top))
                    continue;

                // Do a trivial reject on X.

                if (rleft > (cright = EnumRects8.arcl[i].right))
                    continue;
                if (rright < (cleft = EnumRects8.arcl[i].left))
                    continue;

                // It's not a trivial reject, so calculate the
                // minimal clip area.

                left   = max (rleft, cleft);
                top    = max (rtop, ctop);
                width  = (min(rright, cright) - left) - 1;
                height = (min(rbottom, cbottom) - top) - 1;

                if (width < 0 || height < 0)
                    continue;

                vS3SolidPattern(ppdev,
                                left, top,
                                width, height,
                                (pbo != NULL)? pbo->iSolidColor: 0, s3Mix);
            }
        } while (bMore);
    }

    return (TRUE);
}

/*****************************************************************************
 * vS3SolidPattern
 ****************************************************************************/
VOID vS3SolidPattern(
    PPDEV   ppdev,
    INT     left,
    INT     top,
    INT     width,
    INT     height,
    INT     color,
    WORD    s3Mix)
{
    FIFOWAIT(FIFO_8_EMPTY);

    ASSERTS3 (((left + width) < (INT) ppdev->cxScreen),
              "S3.DLL!vS3SolidPattern - (left + width) > (ppdev->cxScreen - 1)\n");

    TEST_AND_SET_FRGD_MIX(FOREGROUND_COLOR | s3Mix);
    TEST_AND_SET_FRGD_COLOR(color);

    OUTPW (MULTIFUNC_CNTL, (DATA_EXTENSION | ALL_ONES));

    OUTPW (CUR_X, left);
    OUTPW (CUR_Y, top);
    OUTPW (RECT_WIDTH, width);
    OUTPW (MULTIFUNC_CNTL, (RECT_HEIGHT | height));

    OUTPW (CMD, RECTANGLE_FILL | DRAWING_DIR_TBLRXM |
                DRAW           | DIR_TYPE_XY        |
                LAST_PIXEL_ON  | MULTIPLE_PIXELS    |
                WRITE);
}

/*****************************************************************************
 * S3 8bpp Cached Managed Host to Screen Copy
 *
 *  Returns TRUE if the blit was handled.
 ****************************************************************************/
BOOL b8BppHostToScrnCachedWithRop(
    SURFOBJ  *psoTrg,
    SURFOBJ  *psoSrc,
    CLIPOBJ  *pco,
    XLATEOBJ *pxlo,
    RECTL    *prclTrg,
    POINTL   *pptlSrc,
    WORD     s3Rop)
{
    BOOL    bRet;
    HSURF   hsurf;
    PPDEV   ppdev;
    POINTL  ptlSrc;
    RECTL   rclTrg;
    SIZEL   sizlBitmap;

    PSAVEDSCRNBITSHDR   pssbhInPdev;
    PSAVEDSCRNBITS      pssbNewNode, pssbTemp;

    DISPDBG((3, "S3.DLL!b8BppHostToScrnCachedWithRop - entry\n"));

    n8BppBitmaps++;

    ppdev = (PPDEV) psoTrg->dhpdev;
    hsurf = psoSrc->hsurf;

#if 0
    DISPDBG((2, "\thsurf                : %x\n", hsurf));
    DISPDBG((2, "\tpsoSrc->iUniq        : %x\n", psoSrc->iUniq));
    DISPDBG((2, "\tpsoSrc->sizlBitmap.cx: %d\n", psoSrc->sizlBitmap.cx));
    DISPDBG((2, "\tpsoSrc->sizlBitmap.cy: %d\n", psoSrc->sizlBitmap.cy));
#endif

#if SRCBM_CACHE

    // Is this bitmap in the cache?

    if ((hsurf == ppdev->hsurfCachedBitmap) &&
        (psoSrc->iUniq == ppdev->iUniqCachedBitmap))
    {
        if ((((pxlo == NULL) ||
              (pxlo->flXlate & XO_TRIVIAL)) && (ppdev->iUniqXlate == 1)) ||
            (pxlo->iUniq == ppdev->iUniqXlate))
        {
            // The bitmap is in the cache
            // Keep a cache hit count.

            n8BppBmCacheHits++;

            // Blt from the cache.

            ptlSrc.x = pptlSrc->x + OFF_SCREEN_BITMAP_X;
            ptlSrc.y = pptlSrc->y + OFF_SCREEN_BITMAP_Y;

            return(bScrnToScrnWithRop(psoTrg, psoTrg, pco, prclTrg,
                                      &ptlSrc, s3Rop));
        }
    }

    // The bitmap is not in the cache.
    // Is it small enough to fit into the cache?

    sizlBitmap = psoSrc->sizlBitmap;

    if ((sizlBitmap.cx <= OFF_SCREEN_BITMAP_CX) &&
        (sizlBitmap.cy <= OFF_SCREEN_BITMAP_CY))
    {
        // It will fit in the cache.
        // If the cache is being used for some saved screen bits
        // move them to host memory.

        if (ppdev->SavedScreenBitsHeader.iUniq != -1)
        {
            nSsbMovedToHostFromSrcBmCache++;

            DISPDBG((1, "S3.DLL - Saved Screen Bits Moved to Host Memory from Source Bitmap Cache Manager \n"));

            // Move the actual bits to host memory.

            bRet = bMoveSaveScreenBitsToHost(ppdev, &pssbNewNode);

            if (bRet == FALSE)
                return(FALSE);

            // Connect this newNode to the beginning of the list of
            // save screen bits nodes.

            pssbhInPdev = &(ppdev->SavedScreenBitsHeader);

            pssbTemp                   = pssbhInPdev->pssbLink;
            pssbhInPdev->pssbLink      = pssbNewNode;
            pssbNewNode->ssbh.pssbLink = pssbTemp;

            // Invalidate the Save Screen bits in off screen memory

            ppdev->SavedScreenBitsHeader.iUniq = (ULONG) -1;
        }


        // Set the cache tags.

        ppdev->hsurfCachedBitmap = hsurf;
        ppdev->iUniqCachedBitmap = psoSrc->iUniq;

        if ((pxlo == NULL) || (pxlo->flXlate & XO_TRIVIAL))
        {
            ppdev->iUniqXlate = 1;
        }
        else
        {
            ppdev->iUniqXlate = pxlo->iUniq;
        }

        // Put the entire bitmap into the cache.

        rclTrg.left   = OFF_SCREEN_BITMAP_X;
        rclTrg.top    = OFF_SCREEN_BITMAP_Y;
        rclTrg.right  = OFF_SCREEN_BITMAP_X + sizlBitmap.cx;
        rclTrg.bottom = OFF_SCREEN_BITMAP_Y + sizlBitmap.cy;

        ptlSrc.x = 0;
        ptlSrc.y = 0;

        bRet = b8BppHostToScrnWithRop(psoTrg, psoSrc, ppdev->pcoFullRam,
                                      pxlo, &rclTrg, &ptlSrc, OVERPAINT);

        if (bRet == TRUE)
        {
            // Blt from the cache.

            ptlSrc.x = pptlSrc->x + OFF_SCREEN_BITMAP_X;
            ptlSrc.y = pptlSrc->y + OFF_SCREEN_BITMAP_Y;

            bRet = bScrnToScrnWithRop(psoTrg, psoTrg, pco, prclTrg,
                                      &ptlSrc, s3Rop);

        }
    }
    else
    {
        // The bitmap was too large to cache.
        // So, just blt it directly to the screen.

        bRet = b8BppHostToScrnWithRop(psoTrg, psoSrc, pco, pxlo,
                                      prclTrg, pptlSrc, s3Rop);
    }
#else

    bRet = b8BppHostToScrnWithRop(psoTrg, psoSrc, pco, pxlo,
                                  prclTrg, pptlSrc, s3Rop);

#endif

    return (bRet);
}

/*****************************************************************************
 * S3 8bpp Host to Screen Copy
 *
 *  Returns TRUE if the blit was handled.
 ****************************************************************************/
BOOL b8BppHostToScrnWithRop(
    SURFOBJ  *psoTrg,
    SURFOBJ  *psoSrc,
    CLIPOBJ  *pco,
    XLATEOBJ *pxlo,
    RECTL    *prclTrg,
    POINTL   *pptlSrc,
    WORD     s3Rop)
{
    LONG    lSrcDelta;
    PWORD   pwFirstWord;
    BOOL    bMore;
    PPDEV   ppdev;
    BYTE    iDComplexity;
    RECTL   rclTrg, rclBounds;
    POINT   ptSrc;
    INT     i;
    SIZE    sizBlt;

    ENUMRECTS8  EnumRects8;

    DISPDBG((3, "S3.DLL!b8BppHostToScrnWithRop - entry\n"));

    ppdev = (PPDEV) psoTrg->dhpdev;

    // Pickup some convienent locals.

    lSrcDelta = psoSrc->lDelta;

    if ((iDComplexity = pco->iDComplexity) != DC_COMPLEX)
    {
        // Make a copy of the target, since we will have to change
        // it for clipping.

        rclTrg = *prclTrg;

        if (iDComplexity == DC_RECT)
        {
            rclBounds = pco->rclBounds;

            // Handle the trivial rejection and
            // define the clipped target rectangle.

            if (bIntersectTest(&rclTrg, &rclBounds))
            {
                rclTrg.left   = max (rclTrg.left, rclBounds.left);
                rclTrg.top    = max (rclTrg.top, rclBounds.top);
                rclTrg.right  = min (rclTrg.right, rclBounds.right);
                rclTrg.bottom = min (rclTrg.bottom, rclBounds.bottom);
            }
            else
            {
                // The destination rectangle is completely clipped out,
                // so just return.

                return (TRUE);
            }
        }

        // define the cx & cy for the blit.

        sizBlt.cx = rclTrg.right  - rclTrg.left;
        sizBlt.cy = rclTrg.bottom - rclTrg.top;

        // calculate the cx & cy shift (due to clipping) for the source.
        // and define the upper left corner of the source.

        ptSrc.x = pptlSrc->x + (rclTrg.left - prclTrg->left);
        ptSrc.y = pptlSrc->y + (rclTrg.top  - prclTrg->top);

        // Calculate the first word to blit

        pwFirstWord = (PWORD) (((PBYTE) psoSrc->pvScan0)
                     + (ptSrc.y * lSrcDelta) + ptSrc.x);

        vLowLevel8BppHostToScrnWithRop(ppdev, (PPOINT) &rclTrg, &sizBlt,
                                       pwFirstWord, lSrcDelta, pxlo, s3Rop);

    }
    else
    {
        CLIPOBJ_cEnumStart(pco, FALSE, CT_RECTANGLES, CD_ANY, 0);

        do
        {
            bMore = CLIPOBJ_bEnum(pco, sizeof (ENUMRECTS8), (PULONG) &EnumRects8);

            for (i = 0; i < (INT) EnumRects8.c; i++)
            {
                rclTrg = *prclTrg;
                rclBounds = EnumRects8.arcl[i];

                // Handle the trivial rejection and
                // define the clipped target rectangle.

                if (bIntersectTest(&rclTrg, &rclBounds))
                {
                    rclTrg.left   = max (rclTrg.left, rclBounds.left);
                    rclTrg.top    = max (rclTrg.top, rclBounds.top);
                    rclTrg.right  = min (rclTrg.right, rclBounds.right);
                    rclTrg.bottom = min (rclTrg.bottom, rclBounds.bottom);

                    // define the cx & cy for the blit.

                    sizBlt.cx = rclTrg.right  - rclTrg.left;
                    sizBlt.cy = rclTrg.bottom - rclTrg.top;

                    // calculate the cx & cy shift (due to clipping) for the source.
                    // and define the upper left corner of the source.

                    ptSrc.x = pptlSrc->x + (rclTrg.left - prclTrg->left);
                    ptSrc.y = pptlSrc->y + (rclTrg.top  - prclTrg->top);

                    // Calculate the first word to blit

                    pwFirstWord = (PWORD) (((PBYTE) psoSrc->pvScan0)
                                 + (ptSrc.y * lSrcDelta) + ptSrc.x);

                    vLowLevel8BppHostToScrnWithRop(ppdev,
                                                   (PPOINT) &rclTrg,
                                                   &sizBlt,
                                                   pwFirstWord,
                                                   lSrcDelta,
                                                   pxlo,
                                                   s3Rop);
                }
            }
        } while (bMore);
    }

    return (TRUE);
}



/*****************************************************************************
 * Low Level 8bpp host to screen copy.
 ****************************************************************************/
VOID vLowLevel8BppHostToScrnWithRop(
    PPDEV   ppdev,
    PPOINT  pptTrg,
    PSIZE   psizBlt,
    PWORD   pwFirstWord,
    INT     lSrcDelta,
    XLATEOBJ *pxlo,
    WORD    s3Rop)
{
    PWORD   pw;
    INT     i, j;
    WORD    Cmd;
    PBYTE   pbSrc;
    INT     nSrc;
    PULONG  pulXlate;
    INT     cy, wpl;
    BYTE    LineBuff[DRIVERS_MAX_CX];

    // Setup the S3 chip.

    cy = psizBlt->cy;

    Cmd = RECTANGLE_FILL | BYTE_SWAP          | BUS_SIZE_16 | WAIT |
          DRAW           | DRAWING_DIR_TBLRXM | DIR_TYPE_XY |
          LAST_PIXEL_ON  | SINGLE_PIXEL       | WRITE;

    FIFOWAIT(FIFO_7_EMPTY);

    TEST_AND_SET_FRGD_MIX(SRC_CPU_DATA | s3Rop);
    OUTPW(MULTIFUNC_CNTL, (DATA_EXTENSION | ALL_ONES));

    OUTPW(CUR_X, pptTrg->x);
    OUTPW(CUR_Y, pptTrg->y);
    OUTPW(RECT_WIDTH, psizBlt->cx - 1);
    OUTPW(MULTIFUNC_CNTL, (RECT_HEIGHT | cy - 1));

    GPWAIT();

    OUTPW(CMD, Cmd);

    CHECK_DATA_READY;

    wpl = (psizBlt->cx + 1) >> 1;

    // We may have to do some color translation.

    if ((pxlo == NULL) || (pxlo->flXlate & XO_TRIVIAL))
    {
        // Now transfer the data.

        // Note: It would nice to do the entire bitmap in one
        //       fell swoop, but there is no gaurantee source will
        //       wrap on a bitmap boundary.

        pw = pwFirstWord;
        for (i = 0; i < cy; i++)
        {
            vDataPortOut(ppdev, (PWORD) pw, wpl);
            ((PBYTE) pw) += lSrcDelta;
        }
    }
    else
    {
        if (pxlo->flXlate & XO_TABLE)
        {
            pulXlate = pxlo->pulXlate;
        }
        else
        {
            pulXlate = XLATEOBJ_piVector(pxlo);
        }

        pbSrc = (PBYTE) pwFirstWord;
        nSrc  = wpl * 2;

        for (i = 0; i < cy; i++)
        {
            for (j = 0; j < nSrc; j++)
            {
                LineBuff[j] = LOBYTE(pulXlate[pbSrc[j]]);
            }

            vDataPortOut(ppdev, (PWORD) LineBuff, wpl);
            pbSrc += lSrcDelta;
        }
    }

    CHECK_DATA_COMPLETE;

    return;

}


/*****************************************************************************
 * S3 1bpp Cached Managed Host to Screen With Rop
 *
 *  Returns TRUE if the blit was handled.
 ****************************************************************************/
BOOL b1BppHostToScrnCachedWithRop(
    SURFOBJ  *psoTrg,
    SURFOBJ  *psoSrc,
    CLIPOBJ  *pco,
    XLATEOBJ *pxlo,
    RECTL    *prclTrg,
    POINTL   *pptlSrc,
    WORD     s3Rop)
{
    BOOL    bRet;
    HSURF   hsurf;
    PPDEV   ppdev;
    POINTL  ptlSrc;
    RECTL   rclTrg;
    SIZEL   sizlBitmap;

    PSAVEDSCRNBITSHDR   pssbhInPdev;
    PSAVEDSCRNBITS      pssbNewNode, pssbTemp;

    DISPDBG((3, "S3.DLL!b1BppHostToScrnCachedWithRop - entry\n"));

    n1BppBitmaps++;

    ppdev = (PPDEV) psoTrg->dhpdev;
    hsurf = psoSrc->hsurf;

#if 1
    DISPDBG((2, "\thsurf                : %x\n", hsurf));
    DISPDBG((2, "\tpsoSrc->iUniq        : %x\n", psoSrc->iUniq));
    DISPDBG((2, "\tpsoSrc->sizlBitmap.cx: %d\n", psoSrc->sizlBitmap.cx));
    DISPDBG((2, "\tpsoSrc->sizlBitmap.cy: %d\n", psoSrc->sizlBitmap.cy));
#endif

#if SRCBM_CACHE

    // Is this bitmap in the cache?

    if ((hsurf == ppdev->hsurfCachedBitmap) &&
        (psoSrc->iUniq == ppdev->iUniqCachedBitmap))
    {
        if ((((pxlo == NULL) ||
              (pxlo->flXlate & XO_TRIVIAL)) && (ppdev->iUniqXlate == 1)) ||
            (pxlo->iUniq == ppdev->iUniqXlate))
        {
            // The bitmap is in the cache
            // Keep a cache hit count.

            n1BppBmCacheHits++;

            // Blt from the cache.

            ptlSrc.x = pptlSrc->x + OFF_SCREEN_BITMAP_X;
            ptlSrc.y = pptlSrc->y + OFF_SCREEN_BITMAP_Y;

            return(bScrnToScrnWithRop(psoTrg, psoTrg, pco, prclTrg,
                                      &ptlSrc, s3Rop));
        }
    }

    // The bitmap is not in the cache.
    // Is it small enough to fit into the cache?

    sizlBitmap = psoSrc->sizlBitmap;

    if ((sizlBitmap.cx <= OFF_SCREEN_BITMAP_CX) &&
        (sizlBitmap.cy <= OFF_SCREEN_BITMAP_CY))
    {
        // It will fit in the cache.
        // If the cache is being used for some saved screen bits
        // move them to host memory.

        if (ppdev->SavedScreenBitsHeader.iUniq != -1)
        {
            nSsbMovedToHostFromSrcBmCache++;

            DISPDBG((1, "S3.DLL - Saved Screen Bits Moved to Host Memory from Source Bitmap Cache Manager \n"));

            // Move the actual bits to host memory.

            bRet = bMoveSaveScreenBitsToHost(ppdev, &pssbNewNode);

            if (bRet == FALSE)
                return(FALSE);

            // Connect this newNode to the beginning of the list of
            // save screen bits nodes.

            pssbhInPdev = &(ppdev->SavedScreenBitsHeader);

            pssbTemp                   = pssbhInPdev->pssbLink;
            pssbhInPdev->pssbLink      = pssbNewNode;
            pssbNewNode->ssbh.pssbLink = pssbTemp;

            // Invalidate the Save Screen bits in off screen memory

            ppdev->SavedScreenBitsHeader.iUniq = (ULONG) -1;
        }


        // Set the cache tags.

        ppdev->hsurfCachedBitmap = hsurf;
        ppdev->iUniqCachedBitmap = psoSrc->iUniq;

        if ((pxlo == NULL) || (pxlo->flXlate & XO_TRIVIAL))
        {
            ppdev->iUniqXlate = 1;
        }
        else
        {
            ppdev->iUniqXlate = pxlo->iUniq;
        }

        // Put the entire bitmap into the cache.

        rclTrg.left   = OFF_SCREEN_BITMAP_X;
        rclTrg.top    = OFF_SCREEN_BITMAP_Y;
        rclTrg.right  = OFF_SCREEN_BITMAP_X + sizlBitmap.cx;
        rclTrg.bottom = OFF_SCREEN_BITMAP_Y + sizlBitmap.cy;

        ptlSrc.x = 0;
        ptlSrc.y = 0;

        bRet = b1BppHostToScrnWithRop(psoTrg, psoSrc, ppdev->pcoFullRam,
                                      pxlo, &rclTrg, &ptlSrc, OVERPAINT);

        if (bRet == TRUE)
        {
            // Blt from the cache.

            ptlSrc.x = pptlSrc->x + OFF_SCREEN_BITMAP_X;
            ptlSrc.y = pptlSrc->y + OFF_SCREEN_BITMAP_Y;

            bRet = bScrnToScrnWithRop(psoTrg, psoTrg, pco, prclTrg,
                                      &ptlSrc, s3Rop);

        }
    }
    else
    {
        // The bitmap was too large to cache.
        // So, just blt it directly to the screen.

        bRet = b1BppHostToScrnWithRop(psoTrg, psoSrc, pco, pxlo,
                                      prclTrg, pptlSrc, s3Rop);
    }
#else

    bRet = b1BppHostToScrnWithRop(psoTrg, psoSrc, pco, pxlo,
                                  prclTrg, pptlSrc, s3Rop);

#endif

    return (bRet);
}





/*****************************************************************************
 * S3 1bpp Host to Screen With ROP
 *
 *  Returns TRUE if the blit was handled.
 ****************************************************************************/
BOOL b1BppHostToScrnWithRop(
    SURFOBJ  *psoTrg,
    SURFOBJ  *psoSrc,
    CLIPOBJ  *pco,
    XLATEOBJ *pxlo,
    RECTL    *prclTrg,
    POINTL   *pptlSrc,
    WORD     s3Rop)
{
    LONG    lSrcDelta;
    PWORD   pwFirstWord;
    BOOL    bMore;
    PPDEV   ppdev;
    BYTE    iDComplexity;
    RECTL   rclTrg, rclBounds;
    POINT   ptSrc;
    INT     i;
    SIZE    sizBlt;

    ENUMRECTS8  EnumRects8;

    DISPDBG((3, "S3.DLL!b1BppHostToScrnWithRop - entry\n"));

    ppdev = (PPDEV) psoTrg->dhpdev;

    // Pickup some convienent locals.

    lSrcDelta = psoSrc->lDelta;

    if ((iDComplexity = pco->iDComplexity) != DC_COMPLEX)
    {
        // Make a copy of the target, since we will have to change
        // it for clipping.

        rclTrg = *prclTrg;

        if (iDComplexity == DC_RECT)
        {
            rclBounds = pco->rclBounds;

            // Handle the trivial rejection and
            // define the clipped target rectangle.

            if (bIntersectTest(&rclTrg, &rclBounds))
            {
                rclTrg.left   = max (rclTrg.left, rclBounds.left);
                rclTrg.top    = max (rclTrg.top, rclBounds.top);
                rclTrg.right  = min (rclTrg.right, rclBounds.right);
                rclTrg.bottom = min (rclTrg.bottom, rclBounds.bottom);
            }
            else
            {
                // The destination rectangle is completely clipped out,
                // so just return.

                return (TRUE);
            }
        }

        // define the cx & cy for the blit.

        sizBlt.cx = rclTrg.right  - rclTrg.left;
        sizBlt.cy = rclTrg.bottom - rclTrg.top;

        // calculate the cx & cy shift (due to clipping) for the source.
        // and define the upper left corner of the source.

        ptSrc.x = pptlSrc->x + (rclTrg.left - prclTrg->left);
        ptSrc.y = pptlSrc->y + (rclTrg.top  - prclTrg->top);

        // Calculate the first word to blit

        pwFirstWord = (PWORD) (((PBYTE) psoSrc->pvScan0)
                     + (ptSrc.y * lSrcDelta) + (ptSrc.x / 8));

        vSetS3ClipRect(ppdev, &rclTrg);
        vLowLevel1BppHostToScrnWithRop(ppdev,
                                       (PPOINT) &rclTrg,
                                       &sizBlt,
                                       ptSrc.x,
                                       pwFirstWord,
                                       lSrcDelta,
                                       pxlo,
                                       s3Rop);

    }
    else
    {
        CLIPOBJ_cEnumStart(pco, FALSE, CT_RECTANGLES, CD_ANY, 0);

        do
        {
            bMore = CLIPOBJ_bEnum(pco, sizeof (ENUMRECTS8), (PULONG) &EnumRects8);

            for (i = 0; i < (INT) EnumRects8.c; i++)
            {
                rclTrg = *prclTrg;
                rclBounds = EnumRects8.arcl[i];

                // Handle the trivial rejection and
                // define the clipped target rectangle.

                if (bIntersectTest(&rclTrg, &rclBounds))
                {
                    rclTrg.left   = max (rclTrg.left, rclBounds.left);
                    rclTrg.top    = max (rclTrg.top, rclBounds.top);
                    rclTrg.right  = min (rclTrg.right, rclBounds.right);
                    rclTrg.bottom = min (rclTrg.bottom, rclBounds.bottom);

                    // define the cx & cy for the blit.

                    sizBlt.cx = rclTrg.right  - rclTrg.left;
                    sizBlt.cy = rclTrg.bottom - rclTrg.top;

                    // calculate the cx & cy shift (due to clipping) for the source.
                    // and define the upper left corner of the source.

                    ptSrc.x = pptlSrc->x + (rclTrg.left - prclTrg->left);
                    ptSrc.y = pptlSrc->y + (rclTrg.top  - prclTrg->top);

                    // Calculate the first word to blit

                    pwFirstWord = (PWORD) (((PBYTE) psoSrc->pvScan0)
                                 + (ptSrc.y * lSrcDelta) + (ptSrc.x / 8));

                    vSetS3ClipRect(ppdev, &rclTrg);
                    vLowLevel1BppHostToScrnWithRop(ppdev,
                                                   (PPOINT) &rclTrg,
                                                   &sizBlt,
                                                   ptSrc.x,
                                                   pwFirstWord,
                                                   lSrcDelta,
                                                   pxlo,
                                                   s3Rop);
                }
            }
        } while (bMore);
    }

    vResetS3Clipping(ppdev);

    return (TRUE);
}


/*****************************************************************************
 * Low Level 1bpp host to screen copy.
 *
 * Note: This routine should be modified to handle separate foreground
 *       and background mix modes.
 ****************************************************************************/
VOID vLowLevel1BppHostToScrnWithRop(
    PPDEV   ppdev,
    PPOINT  pptTrg,
    PSIZE   psizBlt,
    INT     xSrc,
    PWORD   pwFirstWord,
    INT     lSrcDelta,
    XLATEOBJ *pxlo,
    WORD    s3Rop)
{
PBYTE   pb1bpp;
INT     i, x, cx, cy, nSrcBytes, xClip;
WORD    Cmd;
PULONG  pulXlate;

        if (pxlo->flXlate & XO_TABLE)
        {
            pulXlate = pxlo->pulXlate;
        }
        else
        {
            pulXlate = XLATEOBJ_piVector(pxlo);
        }


        x = pptTrg->x;
        cx = psizBlt->cx;

        // Take care of the non-byte aligned source case.

        if (xSrc & 0x7)
        {
            xClip = x;
            x  -= (xSrc & 0x7);
            cx += (xSrc & 0x7);

            FIFOWAIT(FIFO_1_EMPTY);
          outpw (MULTIFUNC_CNTL, (CLIP_LEFT   | xClip));
        }

        Cmd = RECTANGLE_FILL | BUS_SIZE_8         | WAIT |
              DRAW           | DRAWING_DIR_TBLRXM | DIR_TYPE_XY |
              LAST_PIXEL_ON  | MULTIPLE_PIXELS    | WRITE;

        // Setup the S3 chip.

        cy = psizBlt->cy;

        FIFOWAIT(FIFO_4_EMPTY);

        TEST_AND_SET_FRGD_MIX(FOREGROUND_COLOR | s3Rop);
        TEST_AND_SET_FRGD_COLOR(LOWORD(pulXlate[1]));
        TEST_AND_SET_BKGD_MIX(BACKGROUND_COLOR | s3Rop);
        SET_BKGD_COLOR(LOWORD(pulXlate[0]));

        FIFOWAIT(FIFO_5_EMPTY);

        OUTPW(MULTIFUNC_CNTL, (DATA_EXTENSION | CPU_DATA));

        OUTPW(CUR_X, x);
        OUTPW(CUR_Y, pptTrg->y);
        OUTPW(RECT_WIDTH, cx - 1);
        OUTPW(MULTIFUNC_CNTL, (RECT_HEIGHT | cy - 1));

        GPWAIT();

        OUTPW(CMD, Cmd);

        CHECK_DATA_READY;

        pb1bpp = (PBYTE) pwFirstWord;
        nSrcBytes = (cx + 7) / 8;

        for (i = 0; i < cy; i++)
        {
            vDataPortOutB(ppdev, pb1bpp, nSrcBytes);
            pb1bpp += lSrcDelta;
        }

        CHECK_DATA_COMPLETE;

        return;

}


/*****************************************************************************
 * S3 4bpp Host to Screen Copy
 *
 *  Returns TRUE if the blit was handled.
 ****************************************************************************/
BOOL b4BppHostToScrnWithRop(
    SURFOBJ  *psoTrg,
    SURFOBJ  *psoSrc,
    CLIPOBJ  *pco,
    XLATEOBJ *pxlo,
    RECTL    *prclTrg,
    POINTL   *pptlSrc,
    WORD     s3Rop)
{
    LONG    lSrcDelta;
    PWORD   pwFirstWord;
    BOOL    bMore;
    PPDEV   ppdev;
    BYTE    iDComplexity;
    RECTL   rclTrg, rclBounds;
    POINT   ptSrc;
    INT     i;
    SIZE    sizBlt;

    ENUMRECTS8  EnumRects8;

    DISPDBG((2, "S3.DLL!b4BppHostToScrnWithRop - entry\n"));

    ppdev = (PPDEV) psoTrg->dhpdev;

    // Pickup some convienent locals.

    lSrcDelta = psoSrc->lDelta;

    if ((iDComplexity = pco->iDComplexity) != DC_COMPLEX)
    {
        // Make a copy of the target, since we will have to change
        // it for clipping.

        rclTrg = *prclTrg;

        if (iDComplexity == DC_RECT)
        {
            rclBounds = pco->rclBounds;

            // Handle the trivial rejection and
            // define the clipped target rectangle.

            if (bIntersectTest(&rclTrg, &rclBounds))
            {
                rclTrg.left   = max (rclTrg.left, rclBounds.left);
                rclTrg.top    = max (rclTrg.top, rclBounds.top);
                rclTrg.right  = min (rclTrg.right, rclBounds.right);
                rclTrg.bottom = min (rclTrg.bottom, rclBounds.bottom);
            }
            else
            {
                // The destination rectangle is completely clipped out,
                // so just return.

                return (TRUE);
            }
        }

        // define the cx & cy for the blit.

        sizBlt.cx = rclTrg.right  - rclTrg.left;
        sizBlt.cy = rclTrg.bottom - rclTrg.top;

        // calculate the cx & cy shift (due to clipping) for the source.
        // and define the upper left corner of the source.

        ptSrc.x = pptlSrc->x + (rclTrg.left - prclTrg->left);
        ptSrc.y = pptlSrc->y + (rclTrg.top  - prclTrg->top);

        // Calculate the first word to blit

        pwFirstWord = (PWORD) (((PBYTE) psoSrc->pvScan0)
                     + (ptSrc.y * lSrcDelta) + (ptSrc.x / 2));

        vSetS3ClipRect(ppdev, &rclTrg);
        vLowLevel4BppHostToScrnWithRop(ppdev, (PPOINT) &rclTrg, &sizBlt,
                                   ptSrc.x,
                                   pwFirstWord, lSrcDelta, pxlo, s3Rop);

    }
    else
    {
        CLIPOBJ_cEnumStart(pco, FALSE, CT_RECTANGLES, CD_ANY, 0);

        do
        {
            bMore = CLIPOBJ_bEnum(pco, sizeof (ENUMRECTS8), (PULONG) &EnumRects8);

            for (i = 0; i < (INT) EnumRects8.c; i++)
            {
                rclTrg = *prclTrg;
                rclBounds = EnumRects8.arcl[i];

                // Handle the trivial rejection and
                // define the clipped target rectangle.

                if (bIntersectTest(&rclTrg, &rclBounds))
                {
                    rclTrg.left   = max (rclTrg.left, rclBounds.left);
                    rclTrg.top    = max (rclTrg.top, rclBounds.top);
                    rclTrg.right  = min (rclTrg.right, rclBounds.right);
                    rclTrg.bottom = min (rclTrg.bottom, rclBounds.bottom);

                    // define the cx & cy for the blit.

                    sizBlt.cx = rclTrg.right  - rclTrg.left;
                    sizBlt.cy = rclTrg.bottom - rclTrg.top;

                    // calculate the cx & cy shift (due to clipping) for the source.
                    // and define the upper left corner of the source.

                    ptSrc.x = pptlSrc->x + (rclTrg.left - prclTrg->left);
                    ptSrc.y = pptlSrc->y + (rclTrg.top  - prclTrg->top);

                    // Calculate the first word to blit

                    pwFirstWord = (PWORD) (((PBYTE) psoSrc->pvScan0)
                                 + (ptSrc.y * lSrcDelta) + (ptSrc.x / 2));

                    vSetS3ClipRect(ppdev, &rclTrg);
                    vLowLevel4BppHostToScrnWithRop(ppdev, (PPOINT) &rclTrg, &sizBlt,
                                               ptSrc.x,
                                               pwFirstWord, lSrcDelta, pxlo, s3Rop);
                }
            }
        } while (bMore);
    }

    vResetS3Clipping(ppdev);

    return (TRUE);

}




/*****************************************************************************
 * Low Level 4bpp host to screen copy.
 ****************************************************************************/
VOID vLowLevel4BppHostToScrnWithRop(
    PPDEV   ppdev,
    PPOINT  pptTrg,
    PSIZE   psizBlt,
    INT     xSrc,
    PWORD   pwFirstWord,
    INT     lSrcDelta,
    XLATEOBJ *pxlo,
    WORD    s3Rop)
{
PBYTE   pb4Bpp;
INT     i, j, k, x, cx, cy, nSrcBytes, xClip;
WORD    Cmd;
PULONG  pulXlate;

BYTE    LineBuff8Bpp[DRIVERS_MAX_CX];

        if (pxlo->flXlate & XO_TABLE)
        {
            pulXlate = pxlo->pulXlate;
        }
        else
        {
            pulXlate = XLATEOBJ_piVector(pxlo);
        }

        pb4Bpp = (PBYTE) pwFirstWord;

        x = pptTrg->x;
        cx = psizBlt->cx;
        cy = psizBlt->cy;

        // Take care of the non-byte aligned source case.

        if (xSrc & 0x1)
        {
            xClip = x;
            x--;
            cx++;

            FIFOWAIT(FIFO_1_EMPTY);
            outpw (MULTIFUNC_CNTL, (CLIP_LEFT   | xClip));
        }

        nSrcBytes = (cx + 1) / 2;

        // Setup the S3 chip.

        Cmd = RECTANGLE_FILL | BYTE_SWAP          | BUS_SIZE_16 | WAIT |
              DRAW           | DRAWING_DIR_TBLRXM | DIR_TYPE_XY |
              LAST_PIXEL_ON  | SINGLE_PIXEL       | WRITE;

        FIFOWAIT(FIFO_7_EMPTY);

        TEST_AND_SET_FRGD_MIX(SRC_CPU_DATA | s3Rop);
        outpw(MULTIFUNC_CNTL, (DATA_EXTENSION | ALL_ONES));

        outpw(CUR_X, x);
        outpw(CUR_Y, pptTrg->y);
        outpw(RECT_WIDTH, cx - 1);
        outpw(MULTIFUNC_CNTL, (RECT_HEIGHT | cy - 1));

        GPWAIT();

        outpw(CMD, Cmd);

        CHECK_DATA_READY;

        // Now transfer the data.

        for (i = 0; i < cy; i++)
        {
            for (k = 0, j = 0; j < nSrcBytes; j++)
            {
                LineBuff8Bpp[k++] = (BYTE) pulXlate[(pb4Bpp[j] & 0xF0) >> 4];
                LineBuff8Bpp[k++] = (BYTE) pulXlate[pb4Bpp[j] & 0x0F];
            }

            vDataPortOut(ppdev, (PWORD) LineBuff8Bpp, nSrcBytes);
            pb4Bpp += lSrcDelta;
        }

        CHECK_DATA_COMPLETE;

        return;

}

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.