/******************************Module*Header*******************************\
* Module Name: mcd.c
*
* Main file for the Matrox Millenium OpenGL MCD driver.  This file contains
* the entry points needed for an MCD driver.
*
* Copyright (c) 1996 Microsoft Corporation
\**************************************************************************/

#include "precomp.h"
#include <excpt.h>
#include "mcdhw.h"
#include "mcdutil.h"
#include "mcdmath.h"

#define FAIL_ALL_DRAWING    0
#define FORCE_SYNC          0

#define TOTAL_PIXEL_FORMATS (2 * 2)     // double-buffers * z-buffers


// Base color pixel formats

static DRVPIXELFORMAT drvFormats[] = { {8,   3, 3, 2, 0,    5, 2, 0, 0},
                                       {16,  5, 5, 5, 0,   10, 5, 0, 0},
                                       {16,  5, 6, 5, 0,   11, 5, 0, 0},
                                       {24,  8, 8, 8, 0,   16, 8, 0, 0},
                                       {32,  8, 8, 8, 0,   16, 8, 0, 0},
                                     };


LONG MCDrvDescribePixelFormat(MCDSURFACE *pMCDSurface, LONG iPixelFormat,
                              ULONG nBytes, MCDPIXELFORMAT *pMCDPixelFormat,
                              ULONG flags)
{
    BOOL zEnabled;
    BOOL doubleBufferEnabled;
    DRVPIXELFORMAT *pDrvPixelFormat;
    PDEV *ppdev = (PDEV *)pMCDSurface->pso->dhpdev;

//    MCDBG_PRINT("MCDrvDescribePixelFormat");

    if (!pMCDPixelFormat) {

        // We don't support 24bpp or the older MGA:

        if ((ppdev->ulBoardId != MGA_STORM) ||
            (ppdev->iBitmapFormat == BMF_24BPP))
            return 0;

        return TOTAL_PIXEL_FORMATS;
    }

    if (nBytes < sizeof(MCDPIXELFORMAT))
        return 0;

    if (iPixelFormat > TOTAL_PIXEL_FORMATS)
        return 0;

    // We don't support the older MGA in this driver:

    if (ppdev->ulBoardId != MGA_STORM)
        return 0;

    iPixelFormat--;

    zEnabled = iPixelFormat >= (TOTAL_PIXEL_FORMATS / 2);
    doubleBufferEnabled = (iPixelFormat % (TOTAL_PIXEL_FORMATS / 2) ) >=
                          (TOTAL_PIXEL_FORMATS / 4);

    pMCDPixelFormat->nSize = sizeof(MCDPIXELFORMAT);
    pMCDPixelFormat->dwFlags = PFD_SWAP_COPY;
    if (doubleBufferEnabled)
        pMCDPixelFormat->dwFlags |= PFD_DOUBLEBUFFER;
    pMCDPixelFormat->iPixelType = PFD_TYPE_RGBA;

    switch (ppdev->iBitmapFormat) {
        default:
        case BMF_8BPP:
            pDrvPixelFormat = &drvFormats[0];
            pMCDPixelFormat->dwFlags |= (PFD_NEED_SYSTEM_PALETTE | PFD_NEED_PALETTE);
            break;
        case BMF_16BPP:
            if (ppdev->flGreen != 0x7e0)    // not 565
                pDrvPixelFormat = &drvFormats[1];
            else
                pDrvPixelFormat = &drvFormats[2];
            break;
        case BMF_24BPP:     // The Millenium doesn't do 3D at 24bpp!
            return 0;
        case BMF_32BPP:
            pDrvPixelFormat = &drvFormats[4];
            break;
    }

    pMCDPixelFormat->cColorBits  = pDrvPixelFormat->cColorBits;
    pMCDPixelFormat->cRedBits    = pDrvPixelFormat->rBits;
    pMCDPixelFormat->cGreenBits  = pDrvPixelFormat->gBits;
    pMCDPixelFormat->cBlueBits   = pDrvPixelFormat->bBits;
    pMCDPixelFormat->cAlphaBits  = pDrvPixelFormat->aBits;
    pMCDPixelFormat->cRedShift   = pDrvPixelFormat->rShift;
    pMCDPixelFormat->cGreenShift = pDrvPixelFormat->gShift;
    pMCDPixelFormat->cBlueShift  = pDrvPixelFormat->bShift;
    pMCDPixelFormat->cAlphaShift = pDrvPixelFormat->aShift;

    if (zEnabled)
    {
        pMCDPixelFormat->cDepthBits       = 16;
        pMCDPixelFormat->cDepthBufferBits = 16;
        pMCDPixelFormat->cDepthShift      = 16;
    }
    else
    {
        pMCDPixelFormat->cDepthBits       = 0;
        pMCDPixelFormat->cDepthBufferBits = 0;
        pMCDPixelFormat->cDepthShift      = 0;
    }

    // MGA does not support stencil; generic will supply a software
    // implementation as necessary.

    pMCDPixelFormat->cStencilBits = 0;

    pMCDPixelFormat->cOverlayPlanes = 0;
    pMCDPixelFormat->cUnderlayPlanes = 0;
    pMCDPixelFormat->dwTransparentColor = 0;

    return TOTAL_PIXEL_FORMATS;
}


BOOL MCDrvDescribeLayerPlane(MCDSURFACE *pMCDSurface,
                             LONG iPixelFormat, LONG iLayerPlane,
                             ULONG nBytes, MCDLAYERPLANE *pMCDLayerPlane,
                             ULONG flags)
{
    //MCDBG_PRINT("MCDrvDescribeLayerPlane");

    return FALSE;
}


LONG MCDrvSetLayerPalette(MCDSURFACE *pMCDSurface, LONG iLayerPlane,
                          BOOL bRealize, LONG cEntries, COLORREF *pcr)
{
    //MCDBG_PRINT("MCDrvSetLayerPalette");

    return 0;
}


BOOL MCDrvInfo(MCDSURFACE *pMCDSurface, MCDDRIVERINFO *pMCDDriverInfo)
{
//    MCDBG_PRINT("MCDrvInfo");

    pMCDDriverInfo->verMajor = MCD_VER_MAJOR;
    pMCDDriverInfo->verMinor = MCD_VER_MINOR;
    pMCDDriverInfo->verDriver = 0x10000;
    strcpy(pMCDDriverInfo->idStr, "Matrox STORM (Microsoft)");
    pMCDDriverInfo->drvMemFlags = 0;
    pMCDDriverInfo->drvBatchMemSizeMax = 128000;

    return TRUE;
}


ULONG MCDrvCreateContext(MCDSURFACE *pMCDSurface, MCDRC *pMCDRc,
                         MCDRCINFO *pRcInfo)
{
    DEVRC *pRc;
    MCDWINDOW *pMCDWnd = pMCDSurface->pWnd;
    DEVWND *pDevWnd;
    PDEV *ppdev = (PDEV *)pMCDSurface->pso->dhpdev;
    DRVPIXELFORMAT *pDrvPixelFormat;
    MCDVERTEX *pv;
    BOOL zEnabled;
    BOOL doubleBufferEnabled;
    ULONG i, maxVi;

//    MCDBG_PRINT("MCDrvCreateContext");

    // We only support window surfaces:

    if (! (pMCDSurface->surfaceFlags & MCDSURFACE_HWND) )
        return FALSE;

    // We don't support the older MGA in this driver:

    if (ppdev->ulBoardId != MGA_STORM)
        return FALSE;

    if ((pMCDRc->iPixelFormat > TOTAL_PIXEL_FORMATS) ||
        (pMCDRc->iPixelFormat < 0)) {
        MCDBG_PRINT("MCDrvCreateContext: bad pixel format");
        return FALSE;
    }

    // We don't support overlay planes:

    if (pMCDRc->iLayerPlane)
        return FALSE;

    pRc = pMCDRc->pvUser = (DEVRC *)MCDAlloc(sizeof(DEVRC));

    if (!pRc) {
        MCDBG_PRINT("MCDrvCreateContext: couldn't allocate DEVRC");
        return FALSE;
    }

    zEnabled = (pMCDRc->iPixelFormat - 1) >= (TOTAL_PIXEL_FORMATS / 2);
    doubleBufferEnabled = ((pMCDRc->iPixelFormat - 1) % (TOTAL_PIXEL_FORMATS / 2) ) >=
                          (TOTAL_PIXEL_FORMATS / 4);

    pRc->zBufEnabled = zEnabled;
    pRc->backBufEnabled = doubleBufferEnabled;

    switch (ppdev->iBitmapFormat) {
        default:
        case BMF_8BPP:
            pDrvPixelFormat = &drvFormats[0];
            pRc->hwBpp = 1;
            break;
        case BMF_16BPP:
            if (ppdev->flGreen != 0x7e0)    // not 565
                pDrvPixelFormat = &drvFormats[1];
            else
                pDrvPixelFormat = &drvFormats[2];
            pRc->hwBpp = 2;
            break;
        case BMF_24BPP:     // The Millenium doesn't do 3D at 24bpp!
            MCDFree(pMCDRc->pvUser);
            pMCDRc->pvUser = NULL;
            MCDBG_PRINT("MCDrvCreateContext: device doesn't support 24 bpp");
            return FALSE;
        case BMF_32BPP:
            pDrvPixelFormat = &drvFormats[4];
            pRc->hwBpp = 4;
            break;
    }

    pRc->pixelFormat = *pDrvPixelFormat;

    // If we're not yet tracking this window, allocate the per-window DEVWND
    // structure for maintaining per-window info such as front/back/z buffer
    // resources:

    if (!pMCDWnd->pvUser) {
        pDevWnd = pMCDWnd->pvUser = (DEVWND *)MCDAlloc(sizeof(DEVWND));
        if (!pDevWnd) {
            MCDFree(pMCDRc->pvUser);
            pMCDRc->pvUser = NULL;
            MCDBG_PRINT("MCDrvCreateContext: couldn't allocate DEVWND");
            return FALSE;
        }
        pDevWnd->createFlags = pMCDRc->createFlags;
        pDevWnd->iPixelFormat = pMCDRc->iPixelFormat;
        pDevWnd->dispUnique = GetDisplayUniqueness(ppdev);
    } else {

        // We already have a per-window DEVWND structure tracking this window.
        // In this case, do a sanity-check on the pixel format for this
        // context, since a window's pixel format can not changed once it has
        // set (by the first context bound to the window).  So, if the pixel
        // format for the incoming context doesn't match the current pixel
        // format for the window, we have to fail context creation:

        pDevWnd = pMCDWnd->pvUser;

        if (pDevWnd->iPixelFormat != pMCDRc->iPixelFormat) {
            MCDFree(pMCDRc->pvUser);
            pMCDRc->pvUser = NULL;
            MCDBG_PRINT("MCDrvCreateContext: mismatched pixel formats, window = %d, context = %d",
                        pDevWnd->iPixelFormat, pMCDRc->iPixelFormat);
            return FALSE;
        }
    }

    pRc->pEnumClip = pMCDSurface->pWnd->pClip;

    // Set up our color scale values so that color components are
    // normalized to 0..7fffff

    // We also need to make sure we don't fault due to bad FL data as well...

    try {

    if (pRcInfo->redScale != (MCDFLOAT)0.0)
        pRc->rScale = (MCDFLOAT)(0x7fffff) / pRcInfo->redScale;
    else
        pRc->rScale = (MCDFLOAT)0.0;

    if (pRcInfo->greenScale != (MCDFLOAT)0.0)
        pRc->gScale = (MCDFLOAT)(0x7fffff) / pRcInfo->greenScale;
    else
        pRc->gScale = (MCDFLOAT)0.0;

    if (pRcInfo->blueScale != (MCDFLOAT)0.0)
        pRc->bScale = (MCDFLOAT)(0x7fffff) / pRcInfo->blueScale;
    else
        pRc->bScale = (MCDFLOAT)0.0;

    // Normalize alpha to 0..ff0000

    if (pRcInfo->alphaScale != (MCDFLOAT)0.0)
        pRc->aScale = (MCDFLOAT)(0xff0000) / pRcInfo->alphaScale;
    else
        pRc->aScale = (MCDFLOAT)0.0;

    } except (EXCEPTION_EXECUTE_HANDLER) {

        MCDBG_PRINT("!!Exception in MCDrvCreateContext!!");
        return FALSE;
    }

    pRc->zScale = (MCDFLOAT)32767.0;

    pRc->pickNeeded = TRUE;         // We'll definitely need to re-pick
                                    // our rendering functions
    pRc->bRGBMode = TRUE;           // We only support RGB mode

    pRc->zero = __MCDZERO;

    // Initialize the pColor pointer in the clip buffer:

    for (i = 0, pv = &pRc->clipTemp[0],
         maxVi = sizeof(pRc->clipTemp) / sizeof(MCDVERTEX);
         i < maxVi; i++, pv++) {
        pv->pColor = &pv->colors[__MCD_FRONTFACE];
    }

    // Set up those rendering functions which are state-invariant:

    pRc->clipLine = __MCDClipLine;
    pRc->clipTri = __MCDClipTriangle;
    pRc->clipPoly = __MCDClipPolygon;
    pRc->doClippedPoly = __MCDDoClippedPolygon;

    pRc->beginPointDrawing = __MCDPointBegin;

    pRc->beginLineDrawing = __MCDLineBegin;
    pRc->endLineDrawing = __MCDLineEnd;

    pRc->viewportXAdjust = pRcInfo->viewportXAdjust;
    pRc->viewportYAdjust = pRcInfo->viewportYAdjust;

#ifdef TEST_REQ_FLAGS
    pRcInfo->requestFlags = MCDRCINFO_NOVIEWPORTADJUST |
                            MCDRCINFO_Y_LOWER_LEFT |
                            MCDRCINFO_DEVCOLORSCALE |
                            MCDRCINFO_DEVZSCALE;

    pRcInfo->redScale = (MCDFLOAT)1.0;
    pRcInfo->greenScale = (MCDFLOAT)1.0;
    pRcInfo->blueScale = (MCDFLOAT)1.0;
    pRcInfo->alphaScale = (MCDFLOAT)1.0;

    pRcInfo->zScale = 0.99991;
#endif

    return TRUE;
}


ULONG MCDrvDeleteContext(MCDRC *pRc, DHPDEV dhpdev)
{
//    MCDBG_PRINT("MCDrvDeleteContext");

    if (pRc->pvUser) {
        MCDFree(pRc->pvUser);
        pRc->pvUser = NULL;
    }

    return (ULONG)TRUE;
}


ULONG MCDrvAllocBuffers(MCDSURFACE *pMCDSurface, MCDRC *pMCDRc)
{
    DEVRC *pRc = (DEVRC *)pMCDRc->pvUser;
    MCDWINDOW *pMCDWnd = pMCDSurface->pWnd;
    DEVWND *pDevWnd = (DEVWND *)(pMCDSurface->pWnd->pvUser);
    BOOL bZBuffer = (pDevWnd->pohZBuffer != NULL);
    BOOL bBackBuffer = (pDevWnd->pohBackBuffer != NULL);

//    MCDBG_PRINT("MCDrvAllocBuffers");

    // Reject the call if we've already done an allocation for this window:

    if ((bZBuffer || bBackBuffer) &&
        ((DEVWND *)pMCDWnd->pvUser)->dispUnique == GetDisplayUniqueness((PDEV *)pMCDSurface->pso->dhpdev)) {

//        MCDBG_PRINT("MCDrvAllocBuffer: warning-attemp to allocate buffers \
//without a matching free");
        return (bZBuffer == pRc->zBufEnabled) &&
               (bBackBuffer == pRc->backBufEnabled);
    }

    // Update the display resolution uniqueness for this window:

    ((DEVWND *)pMCDWnd->pvUser)->dispUnique = GetDisplayUniqueness((PDEV *)pMCDSurface->pso->dhpdev);

    return (ULONG)HWAllocResources(pMCDSurface->pWnd, pMCDSurface->pso,
                                   pRc->zBufEnabled, pRc->backBufEnabled);
}


ULONG MCDrvGetBuffers(MCDSURFACE *pMCDSurface, MCDRC *pMCDRc,
                      MCDBUFFERS *pMCDBuffers)
{
    MCDWINDOW *pMCDWnd = pMCDSurface->pWnd;
    DEVWND *pDevWnd = (DEVWND *)(pMCDSurface->pWnd->pvUser);
    PDEV *ppdev = (PDEV *)pMCDSurface->pso->dhpdev;

//    MCDBG_PRINT("MCDrvGetBuffers");

    if (pMCDRc) {
        MCD_CHECK_BUFFERS_VALID(pMCDSurface, (DEVRC *)pMCDRc->pvUser, FALSE);
    } else {
        MCD_CHECK_DEVWND(pMCDSurface, pDevWnd, FALSE);
    }

    pMCDBuffers->mcdFrontBuf.bufFlags = MCDBUF_ENABLED;
    pMCDBuffers->mcdFrontBuf.bufOffset =
        (pMCDWnd->clientRect.top * ppdev->lDelta) +
        (pMCDWnd->clientRect.left * ppdev->cjHwPel);
    pMCDBuffers->mcdFrontBuf.bufStride = ppdev->lDelta;

    if (pDevWnd->bValidBackBuffer) {
        pMCDBuffers->mcdBackBuf.bufFlags = MCDBUF_ENABLED;
        if ((ppdev->cDoubleBufferRef == 1) || (pMCDWnd->pClip->c == 1))
            pMCDBuffers->mcdBackBuf.bufFlags |= MCDBUF_NOCLIP;
    } else {
        pMCDBuffers->mcdBackBuf.bufFlags = 0;
    }
    if (ppdev->pohBackBuffer == pDevWnd->pohBackBuffer) {
        pMCDBuffers->mcdBackBuf.bufOffset =
            (pMCDWnd->clientRect.top * ppdev->lDelta) +
            (pMCDWnd->clientRect.left * ppdev->cjHwPel) +
            pDevWnd->backBufferOffset;
    } else {
        pMCDBuffers->mcdBackBuf.bufOffset =
            (pMCDWnd->clientRect.left * ppdev->cjHwPel) +
            pDevWnd->backBufferOffset;
    }
    pMCDBuffers->mcdBackBuf.bufStride = ppdev->lDelta;

    if (pDevWnd->bValidZBuffer) {
        pMCDBuffers->mcdDepthBuf.bufFlags = MCDBUF_ENABLED;
        if ((ppdev->cZBufferRef == 1) || (pMCDWnd->pClip->c == 1))
            pMCDBuffers->mcdDepthBuf.bufFlags |= MCDBUF_NOCLIP;
    } else {
        pMCDBuffers->mcdDepthBuf.bufFlags = 0;
    }
    if (ppdev->pohZBuffer == pDevWnd->pohZBuffer) {
        pMCDBuffers->mcdDepthBuf.bufOffset =
            ((pMCDWnd->clientRect.top * ppdev->cxMemory) +
             pMCDWnd->clientRect.left) * 2 +
            pDevWnd->zBufferOffset;
    } else {
        pMCDBuffers->mcdDepthBuf.bufOffset =
            (pMCDWnd->clientRect.left * 2) + pDevWnd->zBufferOffset;
    }

    // The pointer to the start of the frame buffer is adjusted for ulYDstOrg,
    // so we have to redo that adjustment for the depth buffer:

    if (ppdev->ulYDstOrg) {
        pMCDBuffers->mcdDepthBuf.bufOffset +=
            (ppdev->ulYDstOrg * 2) - (ppdev->ulYDstOrg * ppdev->cjHwPel);
    }

    pMCDBuffers->mcdDepthBuf.bufStride = ppdev->cxMemory * 2;

    return (ULONG)TRUE;
}


ULONG MCDrvCreateMem(MCDSURFACE *pMCDSurface, MCDMEM *pMCDMem)
{
//    MCDBG_PRINT("MCDrvCreateMem");
    return (ULONG)TRUE;
}


ULONG MCDrvDeleteMem(MCDMEM *pMCDMem, DHPDEV dhpdev)
{
//    MCDBG_PRINT("MCDrvDeleteMem");
    return (ULONG)TRUE;
}


ULONG_PTR MCDrvDraw(MCDSURFACE *pMCDSurface, MCDRC *pMCDRc, MCDMEM *prxExecMem,
                UCHAR *pStart, UCHAR *pEnd)
{
    MCDCOMMAND *pCmd = (MCDCOMMAND *)pStart;
    MCDCOMMAND *pCmdNext;
    DEVRC *pRc = (DEVRC *)pMCDRc->pvUser;
    DEVWND *pDevWnd = (DEVWND *)(pMCDSurface->pWnd->pvUser);

    CHOP_ROUND_ON();

#if TEST_3D_NO_DRAW
    CHOP_ROUND_OFF();
    return (ULONG)0;
#endif


//    MCDBG_PRINT("MCDrvDraw");

    // Make sure we have both a valid RC and window structure:

    if (!pRc || !pDevWnd)
        goto DrawExit;

    pRc->ppdev = (PDEV *)pMCDSurface->pso->dhpdev;

#if FAIL_ALL_DRAWING
    goto DrawExit;
#endif

    //
    // If the resolution has changed and we have not yet updated our
    // buffers, fail the call gracefully since the client won't be
    // able to perform any software simulations at this point either.
    // This applies to any of the other drawing functions as well (such
    // as spans and clears).
    //

    if (pDevWnd->dispUnique != GetDisplayUniqueness(pRc->ppdev)) {

        MCDBG_PRINT("MCDrvDraw: invalid (changed) resolution");

        CHOP_ROUND_OFF();
        return (ULONG)0;
    }

    if ((pRc->zBufEnabled && !pDevWnd->bValidZBuffer) ||
        (pRc->backBufEnabled && !pDevWnd->bValidBackBuffer)) {

        MCDBG_PRINT("MCDrvDraw has invalid buffers");

        goto DrawExit;
    }

    // re-pick the rendering functions if we've have a state change:

    if (pRc->pickNeeded) {
        __MCDPickRenderingFuncs(pRc, pDevWnd);
        __MCDPickClipFuncs(pRc);
        pRc->pickNeeded = FALSE;
    }

    // If we're completely clipped, return success:

    pRc->pEnumClip = pMCDSurface->pWnd->pClip;

    if (!pRc->pEnumClip->c) {
        CHOP_ROUND_OFF();
        return (ULONG)0;
    }

    // return here if we can't draw any primitives:

    if (pRc->allPrimFail) {
        goto DrawExit;
    }

    // Set these up in the device's RC so we can just pass a single pointer
    // to do everything:

    pRc->pMCDSurface = pMCDSurface;
    pRc->pMCDRc = pMCDRc;

    pRc->xOffset = pMCDSurface->pWnd->clientRect.left -
                   pRc->viewportXAdjust;

    pRc->yOffset = (pMCDSurface->pWnd->clientRect.top -
                    pMCDSurface->pWnd->clipBoundsRect.top) -
                   pRc->viewportYAdjust;

    pRc->pMemMin = pStart;
    pRc->pvProvoking = (MCDVERTEX *)pStart;     // bulletproofing
    pRc->pMemMax = pEnd - sizeof(MCDVERTEX);

    // warm up the hardware for drawing primitives:

    HW_INIT_DRAWING_STATE(pMCDSurface, pMCDSurface->pWnd, pRc);
    HW_INIT_PRIMITIVE_STATE(pMCDSurface, pRc);

    // If we have a single clipping rectangle, set it up in the hardware once
    // for this batch:

    if (pRc->pEnumClip->c == 1)
        (*pRc->HWSetupClipRect)(pRc, &pRc->pEnumClip->arcl[0]);

    // Now, loop through the commands and process the batch:


    try {
        while (pCmd && (UCHAR *)pCmd < pEnd) {

            volatile ULONG command = pCmd->command;

            // Make sure we can read at least the command header:

            if ((pEnd - (UCHAR *)pCmd) < sizeof(MCDCOMMAND))
                goto DrawExit;

            if (command <= GL_POLYGON) {

                if (pCmd->flags & MCDCOMMAND_RENDER_PRIMITIVE)
                    pCmdNext = (*pRc->primFunc[command])(pRc, pCmd);
                else
                    pCmdNext = pCmd->pNextCmd;

                if (pCmdNext == pCmd)
                    goto DrawExit;           // primitive failed
                if (!(pCmd = pCmdNext)) {    // we're done with the batch
                    CHOP_ROUND_OFF();
                    HW_DEFAULT_STATE(pMCDSurface);
#if FORCE_SYNC
                    HW_WAIT_DRAWING_DONE(pRc);
#endif
                    return (ULONG)0;
                }
            }
        }
    } except (EXCEPTION_EXECUTE_HANDLER) {

        MCDBG_PRINT("!!Exception in MCDrvDraw!!");

        CHOP_ROUND_OFF();
        HW_DEFAULT_STATE(pMCDSurface);
        HW_WAIT_DRAWING_DONE(pRc);

        return (ULONG_PTR)pCmd;
    }

DrawExit:
    CHOP_ROUND_OFF();

    // restore the hardware state:

    HW_DEFAULT_STATE(pMCDSurface);
    HW_WAIT_DRAWING_DONE(pRc);

    return (ULONG_PTR)pCmd;    // some sort of overrun has occurred
}


ULONG MCDrvClear(MCDSURFACE *pMCDSurface, MCDRC *pMCDRc, ULONG buffers)
{
    DEVRC *pRc = (DEVRC *)pMCDRc->pvUser;
    MCDWINDOW *pWnd;
    ULONG cClip;
    RECTL *pClip;

//    MCDBG_PRINT("MCDrvClear");

    MCD_CHECK_RC(pRc);

    pWnd = pMCDSurface->pWnd;

    MCD_CHECK_BUFFERS_VALID(pMCDSurface, pRc, TRUE);

    pRc->ppdev = (PDEV *)pMCDSurface->pso->dhpdev;

#if FAIL_ALL_DRAWING && OKOK
    {
        HW_WAIT_DRAWING_DONE(pRc);
        return FALSE;
    }
#endif

    if (buffers & ~(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT |
                    GL_ACCUM_BUFFER_BIT | GL_STENCIL_BUFFER_BIT)) {
        HW_WAIT_DRAWING_DONE(pRc);
        return FALSE;
    }

    if ((buffers & GL_DEPTH_BUFFER_BIT) && (!pRc->zBufEnabled))
    {
        MCDBG_PRINT("MCDrvClear: clear z requested with z-buffer disabled.");
        HW_WAIT_DRAWING_DONE(pRc);
        return FALSE;
    }

    if (buffers & (GL_ACCUM_BUFFER_BIT | GL_STENCIL_BUFFER_BIT)) {
        HW_WAIT_DRAWING_DONE(pRc);
        return FALSE;
    }

    // Return if we have nothing to clear:

    if (!(cClip = pWnd->pClip->c))
        return TRUE;

    // Initialize hardware state for filling operation:

    HW_INIT_DRAWING_STATE(pMCDSurface, pMCDSurface->pWnd, pRc);

    // We have to protect against bad clear colors since this can
    // potentially cause an FP exception:

    try {

        HW_START_FILL_RECT(pMCDSurface, pMCDRc, pRc, buffers);

    } except (EXCEPTION_EXECUTE_HANDLER) {

        MCDBG_PRINT("!!Exception in MCDrvClear!!");
        return FALSE;
    }


    for (pClip = &pWnd->pClip->arcl[0]; cClip; cClip--,
         pClip++)
    {
        // Do the fill:

        HW_FILL_RECT(pMCDSurface, pRc, pClip);
    }

    HW_DEFAULT_STATE(pMCDSurface);

#if FORCE_SYNC
    HW_WAIT_DRAWING_DONE(pRc);
#endif

    return (ULONG)TRUE;
}


ULONG MCDrvSwap(MCDSURFACE *pMCDSurface, ULONG flags)
{
    MCDWINDOW *pWnd;
    ULONG cClip;
    RECTL *pClip;
    POINTL ptSrc;
    ULONG vCount, vCountLast;
    ULONG scanTarget;
    PDEV *ppdev = (PDEV *)pMCDSurface->pso->dhpdev;
    ULONG scanMax;
    ULONG maxScanOffset;
    DEVWND *pDevWnd = (DEVWND *)(pMCDSurface->pWnd->pvUser);
    LONG hwBufferYBias;

//    MCDBG_PRINT("MCDrvSwap");

    pWnd = pMCDSurface->pWnd;

    // If we're not tracking this window, just return...

    if (!pWnd) {
        MCDBG_PRINT("MCDrvSwap: trying to swap an untracked window");\
        return FALSE;
    }

    if (!pDevWnd) {
        MCDBG_PRINT("MCDrvSwap: NULL buffers.");\
        return FALSE;
    }

    if (!pDevWnd->bValidBackBuffer) {
        MCDBG_PRINT("MCDrvSwap: back buffer invalid");
        return FALSE;
    }

    if (pDevWnd->dispUnique != GetDisplayUniqueness(ppdev)) {
        MCDBG_PRINT("MCDrvSwap: resolution changed but not updated");
        return FALSE;
    }

    // Just return if we have nothing to swap:
    //
    //      - no visible rectangle
    //      - per-plane swap, but none of the specified planes
    //        are supported by driver

    if (!(cClip = pWnd->pClipUnscissored->c) ||
        (flags && !(flags & MCDSWAP_MAIN_PLANE)))
        return TRUE;

    HW_START_SWAP_BUFFERS(pMCDSurface, &hwBufferYBias, flags);

    // Wait for sync if we can do a fast blt:

    if ((pDevWnd->createFlags & MCDCONTEXT_SWAPSYNC) &&
        ((pWnd->clientRect.bottom + pDevWnd->backBufferY) <= (ULONG)ppdev->ayBreak[0])) {

        LONG vCount, vCountLast;
        LONG scanTarget = pWnd->clientRect.bottom;
        LONG scanMax = ppdev->cyScreen - 1;

        scanTarget = min(scanTarget, scanMax);

        vCount = vCountLast = HW_GET_VCOUNT(ppdev->pjBase);

        while ((vCount < scanTarget) && ((vCount - vCountLast) >= 0)) {
            vCountLast = vCount;
            vCount = HW_GET_VCOUNT(ppdev->pjBase);
        }
    }

    pClip = &pWnd->pClipUnscissored->arcl[0];
    ptSrc.x = pWnd->clipBoundsRect.left;
    ptSrc.y = pWnd->clipBoundsRect.top + hwBufferYBias;

    // Swap all of the clip rectangles in the backbuffer to the front:

    ppdev->xOffset = 0;
    ppdev->yOffset = 0;

    vMilCopyBlt(ppdev, cClip, pClip, 0xcccc,
                &ptSrc, &pWnd->clipBoundsRect);

    HW_DEFAULT_STATE(pMCDSurface);

    return (ULONG)TRUE;
}


ULONG MCDrvState(MCDSURFACE *pMCDSurface, MCDRC *pMCDRc, MCDMEM *pMCDMem,
                     UCHAR *pStart, LONG length, ULONG numStates)
{
    DEVRC *pRc = (DEVRC *)pMCDRc->pvUser;
    MCDSTATE *pState = (MCDSTATE *)pStart;
    MCDSTATE *pStateEnd = (MCDSTATE *)(pStart + length);

//    MCDBG_PRINT("MCDrvState");

    MCD_CHECK_RC(pRc);

    while (pState < pStateEnd) {

        if (((UCHAR *)pStateEnd - (UCHAR *)pState) < sizeof(MCDSTATE)) {
            MCDBG_PRINT("MCDrvState: buffer too small");
            return FALSE;
        }

        switch (pState->state) {
            case MCD_RENDER_STATE:
                if (((UCHAR *)pState + sizeof(MCDRENDERSTATE)) >
                    (UCHAR *)pStateEnd)
                    return FALSE;

                memcpy(&pRc->MCDState, &pState->stateValue,
                       sizeof(MCDRENDERSTATE));

                // Flag the fact that we need to re-pick the
                // rendering functions:

                pRc->pickNeeded = TRUE;

                pState = (MCDSTATE *)((UCHAR *)pState + sizeof(MCDSTATE_RENDER));
                break;

            case MCD_PIXEL_STATE:
                // Not accelerated in this driver, so we can ignore this state
                // (which implies that we do not need to set the pick flag).

                pState = (MCDSTATE *)((UCHAR *)pState + sizeof(MCDSTATE_PIXEL));
                break;

            case MCD_SCISSOR_RECT_STATE:
                // Not needed in this driver, so we can ignore this state
                // (which implies that we do not need to set the pick flag).

                pState = (MCDSTATE *)((UCHAR *)pState + sizeof(MCDSTATE_SCISSOR_RECT));
                break;

            default:
                MCDBG_PRINT("MCDrvState: Unrecognized state %d.", pState->state);
                return FALSE;
        }
    }

    return (ULONG)TRUE;
}


ULONG MCDrvViewport(MCDSURFACE *pMCDSurface, MCDRC *pMCDRc,
                    MCDVIEWPORT *pMCDViewport)
{
    DEVRC *pRc = (DEVRC *)pMCDRc->pvUser;

//    MCDBG_PRINT("MCDrvViewport");

    MCD_CHECK_RC(pRc);

    pRc->MCDViewport = *pMCDViewport;

    return (ULONG)TRUE;
}

HDEV  MCDrvGetHdev(MCDSURFACE *pMCDSurface)
{
    PDEV *ppdev = (PDEV *)pMCDSurface->pso->dhpdev;

//    MCDBG_PRINT("MCDrvGetHdev");

    return ppdev->hdevEng;
}


ULONG MCDrvSpan(MCDSURFACE *pMCDSurface, MCDRC *pMCDRc, MCDMEM *pMCDMem,
                MCDSPAN *pMCDSpan, BOOL bRead)
{
    DEVRC *pRc = (DEVRC *)pMCDRc->pvUser;
    PDEV *ppdev = (PDEV *)pMCDSurface->pso->dhpdev;
    UCHAR *pScreen;
    UCHAR *pPixels;
    MCDWINDOW *pWnd;
    DEVWND *pDevWnd;
    LONG xLeftOrg, xLeft, xRight, y;
    LONG bufferYBias;
    ULONG bytesNeeded;
    ULONG cjHwPel;

//    MCDBG_PRINT("MCDrvSpan: read %d, (%d, %d) type %d", bRead, pMCDSpan->x, pMCDSpan->y, pMCDSpan->type);

    MCD_CHECK_RC(pRc);

    pWnd = pMCDSurface->pWnd;

    // Return if we have nothing to clip:

    if (!pWnd->pClip->c)
        return TRUE;

    // Fail if number of pixels is negative:

    if (pMCDSpan->numPixels < 0) {
        MCDBG_PRINT("MCDrvSpan: numPixels < 0");
        return FALSE;
    }

    MCD_CHECK_BUFFERS_VALID(pMCDSurface, pRc, TRUE);

    pDevWnd = (DEVWND *)pWnd->pvUser;

    xLeft = xLeftOrg = (pMCDSpan->x + pWnd->clientRect.left);
    xRight = (xLeft + pMCDSpan->numPixels);
    y = pMCDSpan->y + pWnd->clientRect.top;

    // Early-out spans which are not visible:

    if ((y < pWnd->clipBoundsRect.top) ||
        (y >= pWnd->clipBoundsRect.bottom))
        return TRUE;

    xLeft   = max(xLeft, pWnd->clipBoundsRect.left);
    xRight  = min(xRight, pWnd->clipBoundsRect.right);

    // Return if empty:

    if (xLeft >= xRight)
        return TRUE;

    switch (pMCDSpan->type) {
        case MCDSPAN_FRONT:
//MCDBG_PRINT("MCDrvSpan: MCDSPAN_FRONT");
            cjHwPel = ppdev->cjHwPel;
            pScreen = ppdev->pjScreen + (ppdev->ulYDstOrg * cjHwPel);
            bytesNeeded = pMCDSpan->numPixels * cjHwPel;
            pScreen += ((y * ppdev->lDelta) +
                        (xLeft * cjHwPel));
            break;

        case MCDSPAN_BACK:
//MCDBG_PRINT("MCDrvSpan: MCDSPAN_BACK");
            cjHwPel = ppdev->cjHwPel;
            pScreen = ppdev->pjScreen + (ppdev->ulYDstOrg * cjHwPel);
            bytesNeeded = pMCDSpan->numPixels * cjHwPel;
            if (ppdev->pohBackBuffer == pDevWnd->pohBackBuffer) {
                pScreen += ((y * ppdev->lDelta) +
                            (xLeft * cjHwPel) +
                            pDevWnd->backBufferOffset);
            } else {
                pScreen += (((y - pWnd->clipBoundsRect.top) * ppdev->lDelta) +
                            (xLeft * cjHwPel) +
                            pDevWnd->backBufferOffset);
            }

            break;

        case MCDSPAN_DEPTH:
//MCDBG_PRINT("MCDrvSpan: MCDSPAN_DEPTH");
            cjHwPel = 2;                                // Z is always 16bpp
            pScreen = ppdev->pjScreen + (ppdev->ulYDstOrg << 1);
            bytesNeeded = pMCDSpan->numPixels * 2;
            if (ppdev->pohZBuffer == pDevWnd->pohZBuffer) {
                pScreen += (((y * ppdev->cxScreen + xLeft) * 2) +
                            pDevWnd->zBufferOffset);
            } else {
                pScreen += (((((y - pWnd->clipBoundsRect.top) * ppdev->cxScreen) + xLeft) * 2) +
                             pDevWnd->zBufferOffset);
            }

            break;
        default:
            MCDBG_PRINT("MCDrvReadSpan: Unrecognized buffer %d", pMCDSpan->type);
            return FALSE;
    }

    // Make sure we don't read past the end of the buffer:

    if (((char *)pMCDSpan->pPixels + bytesNeeded) >
        ((char *)pMCDMem->pMemBase + pMCDMem->memSize)) {
        MCDBG_PRINT("MCDrvSpan: Buffer too small");
        return FALSE;
    }

    WAIT_NOT_BUSY(ppdev->pjBase);

    pPixels = pMCDSpan->pPixels;

    if (bRead) {
        if (xLeftOrg != xLeft)
            pPixels = (UCHAR *)pMCDSpan->pPixels + ((xLeft - xLeftOrg) * cjHwPel);

        RtlCopyMemory(pPixels, pScreen, (xRight - xLeft) * cjHwPel);
    } else {
        LONG xLeftClip, xRightClip, yClip;
        RECTL *pClip;
        ULONG cClip;

        for (pClip = &pWnd->pClip->arcl[0], cClip = pWnd->pClip->c; cClip;
             cClip--, pClip++)
        {
            UCHAR *pScreenClip;

            // Test for trivial cases:

            if (y < pClip->top)
                break;

            // Determine trivial rejection for just this span

            if ((xLeft >= pClip->right) ||
                (y >= pClip->bottom) ||
                (xRight <= pClip->left))
                continue;

            // Intersect current clip rect with the span:

            xLeftClip   = max(xLeft, pClip->left);
            xRightClip  = min(xRight, pClip->right);

            if (xLeftClip >= xRightClip)
                continue;

            if (xLeftOrg != xLeftClip)
                pPixels = (UCHAR *)pMCDSpan->pPixels +
                          ((xLeftClip - xLeftOrg) * cjHwPel);

            pScreenClip = pScreen + ((xLeftClip - xLeft) * cjHwPel);

            // Write the span:

            RtlCopyMemory(pScreenClip, pPixels, (xRightClip - xLeftClip) * cjHwPel);
        }
    }

    return (ULONG)TRUE;
}


VOID MCDrvTrackWindow(WNDOBJ *pWndObj, MCDWINDOW *pMCDWnd, ULONG flags)
{
//    MCDBG_PRINT("MCDrvTrackWindow");

    SURFOBJ *pso = pWndObj->psoOwner;

    //
    // Note: pMCDWnd is NULL for surface notifications, so if needed
    // they should be handled before this check:
    //

    if (!pMCDWnd)
        return;

    if (!pMCDWnd->pvUser) {
        MCDBG_PRINT("MCDrvTrackWindow: NULL pDevWnd");
        return;
    }

    switch (flags) {
        case WOC_DELETE:

            //MCDBG_PRINT("MCDrvTrackWindow: WOC_DELETE");

            // If the display resoultion has changed, the resources we had
            // bound to the tracked window are gone, so don't try to delete
            // the back- and z-buffer resources which are no longer present:

            if (((DEVWND *)pMCDWnd->pvUser)->dispUnique ==
                GetDisplayUniqueness((PDEV *)pso->dhpdev))
                HWFreeResources(pMCDWnd, pso);

            MCDFree((VOID *)pMCDWnd->pvUser);
            pMCDWnd->pvUser = NULL;

            break;

        case WOC_RGN_CLIENT:

            // The resources we had  bound to the tracked window have moved,
            // so update them:

            {
                DEVWND *pWnd = (DEVWND *)pMCDWnd->pvUser;
                BOOL bZBuffer = (pWnd->pohZBuffer != NULL);
                BOOL bBackBuffer = (pWnd->pohBackBuffer != NULL);
                PDEV *ppdev = (PDEV *)pso->dhpdev;
                ULONG height = pMCDWnd->clientRect.bottom - pMCDWnd->clientRect.top;
                BOOL bWindowBuffer = (bZBuffer && (pWnd->pohZBuffer != ppdev->pohZBuffer)) ||
                                     (bBackBuffer && (pWnd->pohBackBuffer != ppdev->pohBackBuffer));

                // If the window is using a window-sized back/z resource,
                // we need to reallocate it if there has been a size change:

                if (pWnd->dispUnique == GetDisplayUniqueness(ppdev)) {
                    if ((height != pWnd->allocatedBufferHeight) &&
                        (bWindowBuffer)) {
                        HWFreeResources(pMCDWnd, pso);
                        HWAllocResources(pMCDWnd, pso, bZBuffer, bBackBuffer);
                    }
                } else {
                    // In this case, the display has been re-initialized due
                    // to some event such as a resolution change, so we need
                    // to create our buffes from scratch:

                    pWnd->dispUnique = GetDisplayUniqueness((PDEV *)pso->dhpdev);
                    HWAllocResources(pMCDWnd, pso, bZBuffer, bBackBuffer);
                }
            }

            break;

        default:
            break;
    }
    return;
}


ULONG MCDrvBindContext(MCDSURFACE *pMCDSurface, MCDRC *pMCDRc)
{
    DEVWND *pDevWnd = (DEVWND *)(pMCDSurface->pWnd->pvUser);
    PDEV *ppdev = (PDEV *)pMCDSurface->pso->dhpdev;

    // OK, this is a new binding, so create the per-window structure and
    // set the pixel format:

    if (!pDevWnd) {

        pDevWnd = pMCDSurface->pWnd->pvUser = (DEVWND *)MCDAlloc(sizeof(DEVWND));
        if (!pDevWnd) {
            MCDBG_PRINT("MCDrvBindContext: couldn't allocate DEVWND");
            return FALSE;
        }
        pDevWnd->createFlags = pMCDRc->createFlags;
        pDevWnd->iPixelFormat = pMCDRc->iPixelFormat;
        pDevWnd->dispUnique = GetDisplayUniqueness(ppdev);

        return TRUE;
    }

    if (pMCDRc->iPixelFormat != pDevWnd->iPixelFormat) {
        MCDBG_PRINT("MCDrvBindContext: tried to bind unmatched pixel formats");
        return FALSE;
    }

    HWUpdateBufferPos(pMCDSurface->pWnd, pMCDSurface->pso, TRUE);
    return TRUE;
}


ULONG MCDrvSync(MCDSURFACE *pMCDSurface, MCDRC *pMCDRc)
{
    DEVRC *pRc = (DEVRC *)pMCDRc->pvUser;

    pRc->ppdev = (PDEV *)pMCDSurface->pso->dhpdev;
    HW_WAIT_DRAWING_DONE(pRc);

    return TRUE;
}


ULONG MCDrvCreateTexture(MCDSURFACE *pMCDSurface, MCDRC *pRc, MCDTEXTURE *pTex)
{
    //MCDBG_PRINT("MCDrvCreateTexture");

#ifdef TEST_TEXTURE
    pTex->textureKey = 0x1000;

    return 1;
#else
    return 0;
#endif
}


ULONG MCDrvUpdateSubTexture(MCDSURFACE *pMCDSurface, MCDRC *pRc,
                            MCDTEXTURE *pTex, ULONG lod, RECTL *pRect)
{
    //MCDBG_PRINT("MCDrvUpdateSubTexture");

    return TRUE;
}


ULONG MCDrvUpdateTexturePalette(MCDSURFACE *pMCDSurface, MCDRC *pRc,
                                MCDTEXTURE *pTex, ULONG start,
                                ULONG numEntries)
{
    //MCDBG_PRINT("MCDrvUpdateTexturePalette");

    return TRUE;
}


ULONG MCDrvUpdateTexturePriority(MCDSURFACE *pMCDSurface, MCDRC *pRc,
                                 MCDTEXTURE *pTex)
{
    //MCDBG_PRINT("MCDrvUpdateTexturePriority");

    return TRUE;
}


ULONG MCDrvUpdateTextureState(MCDSURFACE *pMCDSurface, MCDRC *pRc,
                              MCDTEXTURE *pTex)
{
    //MCDBG_PRINT("MCDrvUpdateTextureState");

    return TRUE;
}


ULONG MCDrvTextureStatus(MCDSURFACE *pMCDSurface, MCDRC *pRc,
                         MCDTEXTURE *pTex)
{
    //MCDBG_PRINT("MCDrvTextureStatus");

    return MCDRV_TEXTURE_RESIDENT;
}

ULONG MCDrvDeleteTexture(MCDTEXTURE *pTex, DHPDEV dhpdev)
{
    //MCDBG_PRINT("MCDrvDeleteTexture");

    return TRUE;
}

ULONG MCDrvDrawPixels(MCDSURFACE *pMcdSurface, MCDRC *pRc,
                      ULONG width, ULONG height, ULONG format,
                      ULONG type, VOID *pPixels, BOOL packed)
{
    //MCDBG_PRINT("MCDrvDrawPixels");

    return FALSE;
}

ULONG MCDrvReadPixels(MCDSURFACE *pMcdSurface, MCDRC *pRc,
                      LONG x, LONG y, ULONG width, ULONG height,
                      ULONG format, ULONG type, VOID *pPixels)
{
    //MCDBG_PRINT("MCDrvReadPixels");

    return FALSE;
}

ULONG MCDrvCopyPixels(MCDSURFACE *pMcdSurface, MCDRC *pRc,
                      LONG x, LONG y, ULONG width, ULONG height, ULONG type)
{
    //MCDBG_PRINT("MCDrvCopyPixels");

    return FALSE;
}

ULONG MCDrvPixelMap(MCDSURFACE *pMcdSurface, MCDRC *pRc,
                    ULONG mapType, ULONG mapSize, VOID *pMap)
{
    //MCDBG_PRINT("MCDrvPixelMap");

    return TRUE;
}

BOOL MCDrvGetEntryPoints(MCDSURFACE *pMCDSurface, MCDDRIVER *pMCDDriver)
{
    if (pMCDDriver->ulSize < sizeof(MCDDRIVER))
        return FALSE;

    pMCDDriver->pMCDrvInfo = MCDrvInfo;
    pMCDDriver->pMCDrvDescribePixelFormat = MCDrvDescribePixelFormat;
    pMCDDriver->pMCDrvDescribeLayerPlane = MCDrvDescribeLayerPlane;
    pMCDDriver->pMCDrvSetLayerPalette = MCDrvSetLayerPalette;
    pMCDDriver->pMCDrvCreateContext = MCDrvCreateContext;
    pMCDDriver->pMCDrvDeleteContext = MCDrvDeleteContext;
    pMCDDriver->pMCDrvCreateTexture = MCDrvCreateTexture;
    pMCDDriver->pMCDrvDeleteTexture = MCDrvDeleteTexture;
    pMCDDriver->pMCDrvCreateMem = MCDrvCreateMem;
    pMCDDriver->pMCDrvDeleteMem = MCDrvDeleteMem;
    pMCDDriver->pMCDrvDraw = MCDrvDraw;
    pMCDDriver->pMCDrvClear = MCDrvClear;
    pMCDDriver->pMCDrvSwap = MCDrvSwap;
    pMCDDriver->pMCDrvState = MCDrvState;
    pMCDDriver->pMCDrvViewport = MCDrvViewport;
    pMCDDriver->pMCDrvGetHdev = MCDrvGetHdev;
    pMCDDriver->pMCDrvSpan = MCDrvSpan;
    pMCDDriver->pMCDrvTrackWindow = MCDrvTrackWindow;
    pMCDDriver->pMCDrvAllocBuffers = MCDrvAllocBuffers;
    pMCDDriver->pMCDrvGetBuffers = MCDrvGetBuffers;
    pMCDDriver->pMCDrvBindContext = MCDrvBindContext;
    pMCDDriver->pMCDrvSync = MCDrvSync;
    pMCDDriver->pMCDrvCreateTexture = MCDrvCreateTexture;
    pMCDDriver->pMCDrvDeleteTexture = MCDrvDeleteTexture;
    pMCDDriver->pMCDrvUpdateSubTexture = MCDrvUpdateSubTexture;
    pMCDDriver->pMCDrvUpdateTexturePalette = MCDrvUpdateTexturePalette;
    pMCDDriver->pMCDrvUpdateTexturePriority = MCDrvUpdateTexturePriority;
    pMCDDriver->pMCDrvUpdateTextureState = MCDrvUpdateTextureState;
    pMCDDriver->pMCDrvTextureStatus = MCDrvTextureStatus;
    pMCDDriver->pMCDrvDrawPixels = MCDrvDrawPixels;
    pMCDDriver->pMCDrvReadPixels = MCDrvReadPixels;
    pMCDDriver->pMCDrvCopyPixels = MCDrvCopyPixels;
    pMCDDriver->pMCDrvPixelMap = MCDrvPixelMap;

    return TRUE;
}

/******************************Public*Routine******************************\
* VOID vAssertModeMCD
*
* This function is called by enable.c when entering or leaving the
* DOS full-screen character mode.
*
\**************************************************************************/

VOID vAssertModeMCD(
PDEV*   ppdev,
BOOL    bEnabled)
{
}

/******************************Public*Routine******************************\
* BOOL bEnableMCD
*
* This function is called by enable.c when the mode is first initialized,
* right after the miniport does the mode-set.
*
\**************************************************************************/

BOOL bEnableMCD(
PDEV*   ppdev)
{
    return(TRUE);
}

/******************************Public*Routine******************************\
* VOID vDisableMCD
*
* This function is called by enable.c when the driver is shutting down.
*
\**************************************************************************/

VOID vDisableMCD(
PDEV*   ppdev)
{
    if (ppdev->hMCD)
    {
        EngUnloadImage(ppdev->hMCD);
    }
}