/******************************Module*Header**********************************\
*
*                           **************************
*                           * DirectDraw SAMPLE CODE *
*                           **************************
*
* Module Name: dd.c
*
* Content:     
*
* Copyright (c) 1994-1998 3Dlabs Inc. Ltd. All rights reserved.
* Copyright (c) 1995-1999 Microsoft Corporation.  All rights reserved.
\*****************************************************************************/

#define INITGUID
#include "precomp.h"
#include <mmsystem.h>
#include "directx.h"
#include "dd.h"
#include "d3dhw.h"
#include "d3dtext.h"
#include "heap.h"

//-----------------------------------------------------------------------------
//
// use bits to indicate which ROPs you support.
//
// DWORD 0, bit 0 == ROP 0
// DWORD 8, bit 31 == ROP 255
//
//-----------------------------------------------------------------------------

static BYTE ropList[] =
{
    SRCCOPY >> 16,
};

static DWORD rops[DD_ROP_SPACE] = { 0 };



// The FourCC's we support
static DWORD fourCC[] =
{
    FOURCC_YUV422
};

//-----------------------------------------------------------------------------
//
//      setupRops
//
//      build array for supported ROPS
//
//-----------------------------------------------------------------------------

VOID 
setupRops( LPBYTE proplist, LPDWORD proptable, int cnt )
{
    INT         i;
    DWORD       idx;
    DWORD       bit;
    DWORD       rop;
    
    for(i=0; i<cnt; i++)
    {
        rop = proplist[i];
        idx = rop / 32;
        bit = 1L << ((DWORD)(rop % 32));
        proptable[idx] |= bit;
    }
    
} // setupRops 

//-----------------------------------------------------------------------------
//
//  P2DisableAllUnits
//
//  reset permedia rasterizer to known state
//
//-----------------------------------------------------------------------------

VOID
P2DisableAllUnits(PPDev ppdev)
{
    PERMEDIA_DEFS(ppdev);
    
    RESERVEDMAPTR(47);
    SEND_PERMEDIA_DATA(RasterizerMode,      __PERMEDIA_DISABLE);
    SEND_PERMEDIA_DATA(AreaStippleMode,     __PERMEDIA_DISABLE);
    SEND_PERMEDIA_DATA(ScissorMode,         __PERMEDIA_DISABLE);
    SEND_PERMEDIA_DATA(ColorDDAMode,        __PERMEDIA_DISABLE);
    SEND_PERMEDIA_DATA(FogMode,             __PERMEDIA_DISABLE);
    SEND_PERMEDIA_DATA(LBReadMode,          __PERMEDIA_DISABLE);
    SEND_PERMEDIA_DATA(Window,              __PERMEDIA_DISABLE);
    SEND_PERMEDIA_DATA(StencilMode,         __PERMEDIA_DISABLE);
    SEND_PERMEDIA_DATA(DepthMode,           __PERMEDIA_DISABLE);
    SEND_PERMEDIA_DATA(LBWriteMode,         __PERMEDIA_DISABLE);
    SEND_PERMEDIA_DATA(FBReadMode,          __PERMEDIA_DISABLE);
    SEND_PERMEDIA_DATA(DitherMode,          __PERMEDIA_DISABLE);
    SEND_PERMEDIA_DATA(LogicalOpMode,       __PERMEDIA_DISABLE);
    SEND_PERMEDIA_DATA(FBWriteMode,         __PERMEDIA_DISABLE);
    SEND_PERMEDIA_DATA(StatisticMode,       __PERMEDIA_DISABLE);
    SEND_PERMEDIA_DATA(AlphaBlendMode,      __PERMEDIA_DISABLE);
    SEND_PERMEDIA_DATA(FilterMode,          __PERMEDIA_DISABLE);
    SEND_PERMEDIA_DATA(FBSourceData,        __PERMEDIA_DISABLE);
    SEND_PERMEDIA_DATA(LBWriteFormat,       __PERMEDIA_DISABLE);
    SEND_PERMEDIA_DATA(TextureReadMode,     __PERMEDIA_DISABLE);
    SEND_PERMEDIA_DATA(TextureMapFormat,    __PERMEDIA_DISABLE);
    SEND_PERMEDIA_DATA(TextureDataFormat,   __PERMEDIA_DISABLE);
    SEND_PERMEDIA_DATA(TexelLUTMode,        __PERMEDIA_DISABLE);
    SEND_PERMEDIA_DATA(TextureColorMode,    __PERMEDIA_DISABLE);
    SEND_PERMEDIA_DATA(YUVMode,             __PERMEDIA_DISABLE);
    SEND_PERMEDIA_DATA(AStart,              PM_BYTE_COLOR(0xFF));
    SEND_PERMEDIA_DATA(TextureBaseAddress,  __PERMEDIA_DISABLE);
    SEND_PERMEDIA_DATA(TexelLUTIndex,       __PERMEDIA_DISABLE);
    SEND_PERMEDIA_DATA(TexelLUTTransfer,    __PERMEDIA_DISABLE);
    SEND_PERMEDIA_DATA(TextureAddressMode,  __PERMEDIA_DISABLE);
    SEND_PERMEDIA_DATA(AlphaMapUpperBound,  0);
    SEND_PERMEDIA_DATA(AlphaMapLowerBound,  0);
    SEND_PERMEDIA_DATA(Color,  0);
    
    SEND_PERMEDIA_DATA(FBWriteMode, __PERMEDIA_ENABLE);
    SEND_PERMEDIA_DATA(FBPixelOffset, 0x0);
    SEND_PERMEDIA_DATA(FBHardwareWriteMask, __PERMEDIA_ALL_WRITEMASKS_SET);
    SEND_PERMEDIA_DATA(FBSoftwareWriteMask, __PERMEDIA_ALL_WRITEMASKS_SET);
    
    // We sometimes use the scissor in DDRAW to scissor out unnecessary pixels.
    SEND_PERMEDIA_DATA(ScissorMinXY, 0);
    SEND_PERMEDIA_DATA(ScissorMaxXY, (ppdev->cyMemory << 16) | (ppdev->cxMemory));
    SEND_PERMEDIA_DATA(ScreenSize, (ppdev->cyMemory << 16) | (ppdev->cxMemory));
    
    SEND_PERMEDIA_DATA(WindowOrigin, 0x0);
    
    // DirectDraw might not need to set these up
    SEND_PERMEDIA_DATA(dXDom, 0x0);
    SEND_PERMEDIA_DATA(dXSub, 0x0);
    
    // set max size, no filtering
    SEND_PERMEDIA_DATA(TextureReadMode, 
        PM_TEXREADMODE_ENABLE(__PERMEDIA_ENABLE) |
        PM_TEXREADMODE_FILTER(__PERMEDIA_DISABLE) |
        PM_TEXREADMODE_WIDTH(11) |
        PM_TEXREADMODE_HEIGHT(11) );
    
    // 16 bit Z, no other buffers
    SEND_PERMEDIA_DATA(LBWriteFormat, __PERMEDIA_DEPTH_WIDTH_16);
    
    // Ensure an extra LBData message doesn't flow through the core.
    SEND_PERMEDIA_DATA(Window, PM_WINDOW_DISABLELBUPDATE(__PERMEDIA_ENABLE));

    SEND_PERMEDIA_DATA(FBReadPixel, ppdev->bPixShift);

    COMMITDMAPTR();
    FLUSHDMA();

}   // P2DisableAllUnits 

//-----------------------------------------------------------------------------
//
// GetDDHALInfo
//
// Takes a pointer to a partially or fully filled in ppdev and a pointer
// to an empty DDHALINFO and fills in the DDHALINFO.   
//
//-----------------------------------------------------------------------------

VOID
GetDDHALInfo(PPDev ppdev, DDHALINFO* pHALInfo)
{
    DWORD dwResult;
    BOOL bRet;
    
    DBG_DD(( 5, "DDraw:GetDDHalInfo"));

    // Setup the HAL driver caps.
    memset( pHALInfo, 0, sizeof(DDHALINFO));
    pHALInfo->dwSize = sizeof(DDHALINFO);
    
    // Setup the ROPS we do.
    setupRops( ropList, rops, sizeof(ropList)/sizeof(ropList[0]));
    
    // The most basic DirectDraw functionality
    pHALInfo->ddCaps.dwCaps =   DDCAPS_BLT |
                                DDCAPS_BLTQUEUE |
                                DDCAPS_BLTCOLORFILL |
                                DDCAPS_READSCANLINE;

    pHALInfo->ddCaps.ddsCaps.dwCaps =   DDSCAPS_OFFSCREENPLAIN |
                                        DDSCAPS_PRIMARYSURFACE |
                                        DDSCAPS_FLIP;

    // add caps for D3D
    pHALInfo->ddCaps.dwCaps |=  DDCAPS_3D |
                                DDCAPS_ALPHA |
                                DDCAPS_BLTDEPTHFILL;
    
    // add surface caps for D3D
    pHALInfo->ddCaps.ddsCaps.dwCaps |=  DDSCAPS_ALPHA |
                                        DDSCAPS_3DDEVICE |
                                        DDSCAPS_ZBUFFER;
    
    // Permedia can do 
    // 1. Stretching/Shrinking
    // 2. YUV->RGB conversion (only non paletted mode)
    // 3. Mirroring in X and Y 
    
    // add Permedia caps to global caps
    pHALInfo->ddCaps.dwCaps |= DDCAPS_BLTSTRETCH |
                               DDCAPS_COLORKEY |
                               DDCAPS_CANBLTSYSMEM;


#if DX7_STEREO
    // check if mode supports stereo
    DD_STEREOMODE DDStereoMode;
    DDStereoMode.dwHeight = ppdev->cyScreen;
    DDStereoMode.dwWidth  = ppdev->cxScreen;
    DDStereoMode.dwBpp    = ppdev->cBitsPerPel;
    DDStereoMode.dwRefreshRate= 0;
    ppdev->bCanDoStereo=bIsStereoMode(ppdev,&DDStereoMode);

    // Stereo caps are set if the driver can do stereo in any mode:
    pHALInfo->ddCaps.dwCaps2 |= DDCAPS2_STEREO;
    pHALInfo->ddCaps.dwSVCaps = DDSVCAPS_STEREOSEQUENTIAL;
#endif

    pHALInfo->ddCaps.dwCaps2 |= DDCAPS2_CANMANAGETEXTURE;

    //declare we can handle textures wider than the primary
    pHALInfo->ddCaps.dwCaps2 |= DDCAPS2_WIDESURFACES;

    // Special effects caps 
    pHALInfo->ddCaps.dwFXCaps = DDFXCAPS_BLTSTRETCHY |
                                DDFXCAPS_BLTSTRETCHX |
                                DDFXCAPS_BLTSTRETCHYN |
                                DDFXCAPS_BLTSTRETCHXN |
                                DDFXCAPS_BLTSHRINKY |
                                DDFXCAPS_BLTSHRINKX |
                                DDFXCAPS_BLTSHRINKYN |
                                DDFXCAPS_BLTSHRINKXN |
                                DDFXCAPS_BLTMIRRORUPDOWN |
                                DDFXCAPS_BLTMIRRORLEFTRIGHT;


    // add AlphaBlt and Filter caps
    pHALInfo->ddCaps.dwFXCaps |= DDFXCAPS_BLTALPHA |
                                 DDFXCAPS_BLTFILTER;
    
    // colorkey caps, only src color key supported
    pHALInfo->ddCaps.dwCKeyCaps =   DDCKEYCAPS_SRCBLT | 
                                    DDCKEYCAPS_SRCBLTCLRSPACE;
 
    // We can do a texture from sysmem to video mem blt.
    pHALInfo->ddCaps.dwSVBCaps = DDCAPS_BLT;
    pHALInfo->ddCaps.dwSVBCKeyCaps = 0;
    pHALInfo->ddCaps.dwSVBFXCaps = 0;
    
    // Fill in the sysmem->vidmem rops (only can copy);
    for(int i=0;i<DD_ROP_SPACE;i++ )
    {
        pHALInfo->ddCaps.dwSVBRops[i] = rops[i];
    }

    if (ppdev->iBitmapFormat != BMF_8BPP)
    {
        pHALInfo->ddCaps.dwCaps |= DDCAPS_BLTFOURCC;
        pHALInfo->ddCaps.dwCKeyCaps |=  DDCKEYCAPS_SRCBLTCLRSPACEYUV;
    }

    pHALInfo->ddCaps.ddsCaps.dwCaps |= DDSCAPS_TEXTURE;
    
    // Z Buffer is only 16 Bits on Permedia 
    pHALInfo->ddCaps.dwZBufferBitDepths = DDBD_16;
    
#if D3D_MIPMAPPING
    // Mip Mapping
    pHALInfo->ddCaps.ddsCaps.dwCaps |= DDSCAPS_MIPMAP;
#endif
    
    if (DD_P2AGPCAPABLE(ppdev))
    {
        DBG_DD((1, "GetDDHALInfo: P2 AGP board - supports NONLOCALVIDMEM"));

        pHALInfo->ddCaps.dwCaps2 |= DDCAPS2_NONLOCALVIDMEM | 
                                    DDCAPS2_NONLOCALVIDMEMCAPS;
        pHALInfo->ddCaps.ddsCaps.dwCaps |= DDSCAPS_LOCALVIDMEM | 
                                           DDSCAPS_NONLOCALVIDMEM;
    }
    else
    {
        DBG_DD((1,"GetDDHALInfo: P2 Board is NOT AGP"));
    }
    
    // Won't do Video-Sys mem Blits.
    pHALInfo->ddCaps.dwVSBCaps = 0;
    pHALInfo->ddCaps.dwVSBCKeyCaps = 0;
    pHALInfo->ddCaps.dwVSBFXCaps = 0;
    for( i=0;i<DD_ROP_SPACE;i++ )
    {
        pHALInfo->ddCaps.dwVSBRops[i] = 0;
    }
    
    // Won't do Sys-Sys mem Blits
    pHALInfo->ddCaps.dwSSBCaps = 0;
    pHALInfo->ddCaps.dwSSBCKeyCaps = 0;
    pHALInfo->ddCaps.dwSSBFXCaps = 0;
    for( i=0;i<DD_ROP_SPACE;i++ )
    {
        pHALInfo->ddCaps.dwSSBRops[i] = 0;
    }
    
    // bit depths supported for alpha and Z
    pHALInfo->ddCaps.dwAlphaBltConstBitDepths = DDBD_2 | 
                                                DDBD_4 | 
                                                DDBD_8;
    pHALInfo->ddCaps.dwAlphaBltPixelBitDepths = DDBD_1 | 
                                                DDBD_8;
    pHALInfo->ddCaps.dwAlphaBltSurfaceBitDepths = DDBD_1 | 
                                                  DDBD_2 | 
                                                  DDBD_4 | 
                                                  DDBD_8;
    pHALInfo->ddCaps.dwAlphaOverlayConstBitDepths = DDBD_2 | 
                                                    DDBD_4 | 
                                                    DDBD_8;
    pHALInfo->ddCaps.dwAlphaOverlayPixelBitDepths = DDBD_1 | 
                                                    DDBD_8;
    pHALInfo->ddCaps.dwAlphaOverlaySurfaceBitDepths = DDBD_1 | 
                                                      DDBD_2 | 
                                                      DDBD_4 | 
                                                      DDBD_8;
    
    // ROPS supported
    for( i=0;i<DD_ROP_SPACE;i++ )
    {
        pHALInfo->ddCaps.dwRops[i] = rops[i];
    }
    
    // For DX5 and beyond we support this new informational callback.
    pHALInfo->GetDriverInfo = DdGetDriverInfo;
    pHALInfo->dwFlags |= DDHALINFO_GETDRIVERINFOSET;
    
    // now setup D3D callbacks 
    D3DHALCreateDriver( ppdev, 
                        (LPD3DHAL_GLOBALDRIVERDATA*)
                            &pHALInfo->lpD3DGlobalDriverData,
                        (LPD3DHAL_CALLBACKS*) 
                            &pHALInfo->lpD3DHALCallbacks,
                        (LPDDHAL_D3DBUFCALLBACKS*) 
                            &pHALInfo->lpD3DBufCallbacks);
    
    if(pHALInfo->lpD3DGlobalDriverData == NULL)
    {
        // no D3D available - kill caps we set before
        pHALInfo->ddCaps.dwCaps &=  
            ~(DDCAPS_3D | DDCAPS_BLTDEPTHFILL);
        pHALInfo->ddCaps.ddsCaps.dwCaps &= 
            ~(DDSCAPS_3DDEVICE | DDSCAPS_ZBUFFER);
    }

}  // GetHALInfo

//-----------------------------------------------------------------------------
//
//  Global DirectDraw Callbacks
//
//-----------------------------------------------------------------------------

//-----------------------------------------------------------------------------
//
//  DdFlip
//
//  This callback is invoked whenever we are about to flip to from
//  one surface to another. lpFlipData->lpSurfCurr is the surface we were at,
//  lpFlipData->lpSurfTarg is the one we are flipping to.
// 
//  You should point the hardware registers at the new surface, and
//  also keep track of the surface that was flipped away from, so
//  that if the user tries to lock it, you can be sure that it is done
//  being displayed
//
//-----------------------------------------------------------------------------

DWORD CALLBACK
DdFlip( LPDDHAL_FLIPDATA lpFlipData)
{
    PPDev ppdev=(PPDev)lpFlipData->lpDD->dhpdev;
    PERMEDIA_DEFS(ppdev);

    DWORD       dwDDSurfaceOffset;
    HRESULT     ddrval;
    
    DBG_DD(( 3, "DDraw:Flip"));

    // Switch to DirectDraw context
    DDCONTEXT;

    // is the previous Flip already done?
    // check if the current surface is already displayed
    ddrval = updateFlipStatus(ppdev);
    if( ddrval )
    {
        lpFlipData->ddRVal = ddrval;
        return DDHAL_DRIVER_HANDLED;
    }
    
    // everything is OK, do the flip.
    // get offset for Permedia ScreenBase register
    dwDDSurfaceOffset=(DWORD)lpFlipData->lpSurfTarg->lpGbl->fpVidMem;    


#if DX7_STEREO   
    if (lpFlipData->dwFlags & DDFLIP_STEREO)   // will be stereo
    {
        DBG_DD((4,"DDraw:Flip:Stereo"));
        DBG_DD((5,"ScreenBase: %08lx", dwDDSurfaceOffset));

        if (lpFlipData->lpSurfTargLeft!=NULL)
        {
            DWORD dwDDLeftSurfaceOffset;
            dwDDLeftSurfaceOffset=(DWORD)
                lpFlipData->lpSurfTargLeft->lpGbl->fpVidMem;
            LD_PERMEDIA_REG(PREG_SCREENBASERIGHT,dwDDLeftSurfaceOffset>>3);
            DBG_DD((5,"ScreenBaseLeft: %08lx", dwDDLeftSurfaceOffset));
        }

        ULONG ulVControl=READ_PERMEDIA_REG(PREG_VIDEOCONTROL);
        if ((ulVControl&PREG_VC_STEREOENABLE)==0 ||
            !ppdev->bDdStereoMode)
        {
            ppdev->bDdStereoMode=TRUE;
            LD_PERMEDIA_REG(PREG_VIDEOCONTROL, ulVControl               
                                             | PREG_VC_STEREOENABLE);
        }
    } else 
#endif // DX7_STEREO
    {
        // append flip command to Permedia render pipeline
        // that makes sure that all buffers are flushed before
        // the flip occurs
#if DX7_STEREO 
        if (ppdev->bDdStereoMode)
        {
            ppdev->bDdStereoMode=FALSE;
            LD_PERMEDIA_REG(PREG_VIDEOCONTROL,
                READ_PERMEDIA_REG(PREG_VIDEOCONTROL)&
                ~PREG_VC_STEREOENABLE);
        }
#endif
    }

    // adjust base address according to register spec.
    dwDDSurfaceOffset>>=3;

    // add new base address to render pipeline
    RESERVEDMAPTR(1);
    LD_INPUT_FIFO(__Permedia2TagSuspendUntilFrameBlank, dwDDSurfaceOffset); 
    COMMITDMAPTR();
    FLUSHDMA();

    // remember new Surface Offset for GetFlipStatus
    ppdev->dwNewDDSurfaceOffset=dwDDSurfaceOffset;
   
    lpFlipData->ddRVal = DD_OK;
    return DDHAL_DRIVER_HANDLED;
    
} // DdFlip 

//-----------------------------------------------------------------------------
//
// DdWaitForVerticalBlank
//
// This callback is invoked to get information about the vertical blank
// status of the display or to wait until the display is at the begin or
// the end of the vertical blank
//
//-----------------------------------------------------------------------------

DWORD CALLBACK
DdWaitForVerticalBlank(LPDDHAL_WAITFORVERTICALBLANKDATA lpWaitForVerticalBlank)
{
    static int bInVBlank = FALSE;
    PPDev ppdev=(PPDev)lpWaitForVerticalBlank->lpDD->dhpdev;

    DBG_DD(( 2, "DDraw:WaitForVerticalBlank"));
    
    switch(lpWaitForVerticalBlank->dwFlags)
    {

    case DDWAITVB_I_TESTVB:

        // If the monitor is off, we don't always want to report
        // the same status or else an app polling this status
        // might hang

        if( !(READ_PERMEDIA_REG(PREG_VIDEOCONTROL) & PREG_VC_VIDEO_ENABLE))
        {
            lpWaitForVerticalBlank->bIsInVB = bInVBlank;
            bInVBlank = !bInVBlank;
        }
        else
        {
            // Just a request for current VBLANK status.

            lpWaitForVerticalBlank->bIsInVB = IN_VRETRACE(ppdev);
        }

        lpWaitForVerticalBlank->ddRVal = DD_OK;
        return DDHAL_DRIVER_HANDLED;
        
    case DDWAITVB_BLOCKBEGIN:
    
        // we don't care to wait if the monitor is off

        if( READ_PERMEDIA_REG(PREG_VIDEOCONTROL) & PREG_VC_VIDEO_ENABLE)
        {
            // if blockbegin is requested we wait until the vertical retrace
            // is over, and then wait for the display period to end.

            while(IN_VRETRACE(ppdev));
            while(IN_DISPLAY(ppdev));
        }

        lpWaitForVerticalBlank->ddRVal = DD_OK;
        return DDHAL_DRIVER_HANDLED;
        
    case DDWAITVB_BLOCKEND:
        
        // we don't care to wait if the monitor is off

        if( READ_PERMEDIA_REG(PREG_VIDEOCONTROL) & PREG_VC_VIDEO_ENABLE)
        {
            // if blockend is requested we wait for the vblank interval to end.

            if( IN_VRETRACE(ppdev) )
            {
                while( IN_VRETRACE(ppdev) );
            }
            else
            {
                while(IN_DISPLAY(ppdev));
                while(IN_VRETRACE(ppdev));
            }
        }

        lpWaitForVerticalBlank->ddRVal = DD_OK;
        return DDHAL_DRIVER_HANDLED;
    }
    
    return DDHAL_DRIVER_NOTHANDLED;
    
} // WaitForVerticalBlank 

//-----------------------------------------------------------------------------
//
//  Lock
//
//  This call is invoked to lock a DirectDraw Videomemory surface. To make
//  sure there are no pending drawing operations on the surface, flush all
//  drawing operations and wait for a flip if it is still pending.
//
//-----------------------------------------------------------------------------

DWORD CALLBACK
DdLock( LPDDHAL_LOCKDATA lpLockData )
{
    PPDev ppdev=(PPDev)lpLockData->lpDD->dhpdev;
    PERMEDIA_DEFS(ppdev);

    HRESULT     ddrval;
    DWORD pSurf;
    
    DBG_DD(( 2, "DDraw:Lock"));

    //
    // Switch to DirectDraw context
    //
    DDCONTEXT;

    // check to see if any pending physical flip has occurred
    ddrval = updateFlipStatus(ppdev);
    if( ddrval != DD_OK )
    {
        lpLockData->ddRVal = DDERR_WASSTILLDRAWING;
        return DDHAL_DRIVER_HANDLED;
    }
    
    //
    // don't allow a lock if a blt is in progress
    //

    if(DRAW_ENGINE_BUSY)
    {
        DBG_DD((2,"DDraw:Lock, DrawEngineBusy"));
        FLUSHDMA();
        lpLockData->ddRVal = DDERR_WASSTILLDRAWING;
        return DDHAL_DRIVER_HANDLED;
    }
    
   
    // send a flush and wait for outstanding operations 
    // before allowing surfaces to be locked.

    SYNC_WITH_PERMEDIA;

    // now check if the user wants to lock a texture surface,
    // which was loaded as patched! In this case we have to to
    // a blit to unpatch before we return it to the user
    // This is not expensive, since we leave it unpatched for
    // the future when the application decides to use it this way
    LPDDRAWI_DDRAWSURFACE_LCL  pLcl=lpLockData->lpDDSurface;
    LPDDRAWI_DDRAWSURFACE_GBL  pGbl=pLcl->lpGbl;
    PermediaSurfaceData       *pPrivate= 
        (PermediaSurfaceData*)pGbl->dwReserved1;

    //
    //  If the user attempts to lock a managed surface, mark it as dirty
    //  and return. 
    //

    if (pLcl->lpSurfMore->ddsCapsEx.dwCaps2 & DDSCAPS2_TEXTUREMANAGE)
    {
        DBG_DD(( 3, "DDraw:Lock %08lx %08lx",
            pLcl->lpSurfMore->dwSurfaceHandle, pGbl->fpVidMem));
        if (NULL != pPrivate)
            pPrivate->dwFlags |= P2_SURFACE_NEEDUPDATE;
        lpLockData->lpSurfData = (LPVOID)(pLcl->lpGbl->fpVidMem +
                                          (pLcl->lpGbl->lPitch * lpLockData->rArea.top) +
                                          (lpLockData->rArea.left << DDSurf_GetPixelShift(pLcl)));
        lpLockData->ddRVal = DD_OK;
        return DDHAL_DRIVER_HANDLED;
    }

    DD_CHECK_PRIMARY_SURFACE_DATA(pLcl,pPrivate);

    //
    //  We only need to unswizzle a surface if the
    //  PrivateData is in a format we know (pPrivate!=NULL)
    //

    if (pPrivate!=NULL)
    {
        //
        //  if the surface is a texture which was loaded in a swizzled
        //  format, we have to undo the swizzle before succeding the lock.
        //  In this driver, a texture remains unswizzled when a user
        //  attempted to lock it once.
        //

        if (pPrivate->dwFlags & P2_ISPATCHED)
        {
            //
            // The scratchpad must be 32 lines high and should have
            // the same width as our original surface.
            // 

            PermediaSurfaceData ScratchData=*pPrivate;
            LONG lScratchDelta;
            VIDEOMEMORY*  pvmHeap;
            ULONG ulScratchOffset=
                ulVidMemAllocate( ppdev,
                                  DDSurf_Width(pLcl),
                                  DDSurf_Height(pLcl),
                                  DDSurf_GetPixelShift(pLcl),
                                  &lScratchDelta,
                                  &pvmHeap,
                                  &ScratchData.ulPackedPP,
                                  FALSE);

            DBG_DD(( 5, "  unswizzle surface, scratchpad at: %08lx",
                           ulScratchOffset));
            if (ulScratchOffset!=0)
            {
                RECTL rSurfRect;
                RECTL rScratchRect;

                rSurfRect.left=0;
                rSurfRect.top=0;
                rSurfRect.right=DDSurf_Width(pLcl);
                rSurfRect.bottom=32;

                rScratchRect=rSurfRect;

                // scratchpad should be non patched
                ScratchData.dwFlags &= ~(P2_ISPATCHED|P2_CANPATCH);

                LONG lSurfOffset;
                DWORD dwSurfBase=(DWORD)pGbl->fpVidMem >> 
                    DDSurf_GetPixelShift(pLcl);
                DWORD dwScratchBase=ulScratchOffset >> 
                    DDSurf_GetPixelShift(pLcl);
                lScratchDelta >>= DDSurf_GetPixelShift(pLcl);
                LONG lSurfDelta=DDSurf_Pitch(pLcl)>>
                    DDSurf_GetPixelShift(pLcl);

                for (DWORD i=0; i<DDSurf_Height(pLcl); i+=32)
                {
                    lSurfOffset = dwSurfBase-dwScratchBase;
                    // first do a patched to unpatched blt to the scratchpad
                    PermediaPatchedCopyBlt( ppdev, 
                                            lScratchDelta, 
                                            lSurfDelta, 
                                            &ScratchData, 
                                            pPrivate, 
                                            &rScratchRect, 
                                            &rSurfRect, 
                                            dwScratchBase, 
                                            lSurfOffset);    
                    
                    // then do a fast copyblt back to the original
                    // Packed blit ignores the ISPATCHED flag

                    lSurfOffset = dwScratchBase-dwSurfBase;

                    PermediaPackedCopyBlt( ppdev, 
                                           lSurfDelta, 
                                           lScratchDelta, 
                                           pPrivate, 
                                           &ScratchData, 
                                           &rSurfRect, 
                                           &rScratchRect, 
                                           dwSurfBase,
                                           lSurfOffset);

                    rSurfRect.top += 32;
                    rSurfRect.bottom += 32;
                }

                pPrivate->dwFlags &= ~P2_ISPATCHED;

                //
                // free scratchpad memory
                //
                VidMemFree( pvmHeap->lpHeap, ulScratchOffset);

                SYNC_WITH_PERMEDIA;
            } else
            {
                lpLockData->ddRVal = DDERR_OUTOFMEMORY;
                return DDHAL_DRIVER_HANDLED;
            }
        }
    }


    // Because we correctly set 'fpVidMem' to be the offset into our frame
    // buffer when we created the surface, DirectDraw will automatically take
    // care of adding in the user-mode frame buffer address if we return
    // DDHAL_DRIVER_NOTHANDLED:
 
    return DDHAL_DRIVER_NOTHANDLED;
    
} // DdLock 

//-----------------------------------------------------------------------------
//
//  DdGetScanLine
//
//  This callback is invoked to get the current scanline of our video display
//
//-----------------------------------------------------------------------------

DWORD CALLBACK 
DdGetScanLine(LPDDHAL_GETSCANLINEDATA lpGetScanLine)
{
    PPDev ppdev=(PPDev)lpGetScanLine->lpDD->dhpdev;

    DBG_DD(( 2, "DDraw:GetScanLine"));

    //  If a vertical blank is in progress the scan line is 
    //  indeterminant. If the scan line is indeterminant we return
    //  the error code DDERR_VERTICALBLANKINPROGRESS.
    //  Otherwise we return the scan line and a success code

    if( IN_VRETRACE(ppdev) )
    {
        lpGetScanLine->ddRVal = DDERR_VERTICALBLANKINPROGRESS;
        lpGetScanLine->dwScanLine = 0;
    }
    else
    {
        lpGetScanLine->dwScanLine = CURRENT_VLINE(ppdev);
        lpGetScanLine->ddRVal = DD_OK;
    }
    return DDHAL_DRIVER_HANDLED;
    
} // DdGetScanLine 
  
//-----------------------------------------------------------------------------
//
//  DdGetBltStatus
//
//  This callback is invoked to get the current blit status or to ask if the
//  user can add the next blit.
//
//-----------------------------------------------------------------------------

DWORD CALLBACK 
DdGetBltStatus(LPDDHAL_GETBLTSTATUSDATA lpGetBltStatus )
{
    PPDev ppdev=(PPDev)lpGetBltStatus->lpDD->dhpdev;
    PERMEDIA_DEFS(ppdev);
    
    DBG_DD(( 2, "DDraw:DdGetBltStatus"));

    // CANBLT: can we add a blt?
    // On the Permedia we can always add blits 
    
    if( lpGetBltStatus->dwFlags == DDGBS_CANBLT )
    {
        lpGetBltStatus->ddRVal = DD_OK;
    }
    else
    {
        if( DRAW_ENGINE_BUSY )
        {
            // switch to DDraw context if necessary
            DDCONTEXT;

            FLUSHDMA();
            lpGetBltStatus->ddRVal = DDERR_WASSTILLDRAWING;
        }
        else
        {
            lpGetBltStatus->ddRVal = DD_OK;
        }
    }

    return DDHAL_DRIVER_HANDLED;
    
} // DdGetBltStatus 

//-----------------------------------------------------------------------------
//
// DdGetFlipStatus
//
// If the display has went through one refresh cycle since the flip
// occurred we return DD_OK.  If it has not went through one refresh
// cycle we return DDERR_WASSTILLDRAWING to indicate that this surface
// is still busy "drawing" the flipped page. We also return
// DDERR_WASSTILLDRAWING if the bltter is busy and the caller wanted
// to know if they could flip yet
//
//-----------------------------------------------------------------------------

DWORD CALLBACK 
DdGetFlipStatus(LPDDHAL_GETFLIPSTATUSDATA lpGetFlipStatus )
{
    PPDev ppdev=(PPDev)lpGetFlipStatus->lpDD->dhpdev;
    PERMEDIA_DEFS(ppdev);
    
    DBG_DD(( 2, "DDraw:GetFlipStatus"));

    // switch to DDraw context if necessary
    DDCONTEXT;

    // we can always flip, since the flip is pipelined
    // but we allow only one flip in advance
    if( lpGetFlipStatus->dwFlags == DDGFS_CANFLIP )
    {
        lpGetFlipStatus->ddRVal = updateFlipStatus(ppdev);

        return DDHAL_DRIVER_HANDLED;
    }

    // don't want a flip to work until after the last flip is done,
    // so we ask for the general flip status and ignore the vmem
    
    lpGetFlipStatus->ddRVal = updateFlipStatus(ppdev);
    
    return DDHAL_DRIVER_HANDLED;
    
} // DdGetFlipStatus 



//-----------------------------------------------------------------------------
//
//  DdMapMemory
//
//  This is a new DDI call specific to Windows NT that is used to map
//  or unmap all the application modifiable portions of the frame buffer
//  into the specified process's address space.
//
//-----------------------------------------------------------------------------

DWORD CALLBACK 
DdMapMemory(PDD_MAPMEMORYDATA lpMapMemory)
{
    PDev*                           ppdev;
    VIDEO_SHARE_MEMORY              ShareMemory;
    VIDEO_SHARE_MEMORY_INFORMATION  ShareMemoryInformation;
    DWORD                           ReturnedDataLength;
    
    DBG_DD(( 2, "DDraw:MapMemory"));

    ppdev = (PDev*) lpMapMemory->lpDD->dhpdev;
    
    if (lpMapMemory->bMap)
    {
        ShareMemory.ProcessHandle = lpMapMemory->hProcess;
        
        // 'RequestedVirtualAddress' isn't actually used for the SHARE IOCTL:
        
        ShareMemory.RequestedVirtualAddress = 0;
        
        // We map in starting at the top of the frame buffer:
        
        ShareMemory.ViewOffset = 0;
        
        // We map down to the end of the frame buffer.
        //
        // Note: There is a 64k granularity on the mapping (meaning that
        //       we have to round up to 64k).
        //
        // Note: If there is any portion of the frame buffer that must
        //       not be modified by an application, that portion of memory
        //       MUST NOT be mapped in by this call.  This would include
        //       any data that, if modified by a malicious application,
        //       would cause the driver to crash.  This could include, for
        //       example, any DSP code that is kept in off-screen memory.
        
        ShareMemory.ViewSize
            = ROUND_UP_TO_64K(ppdev->cyMemory * ppdev->lDelta);
        
        if (EngDeviceIoControl(ppdev->hDriver,
            IOCTL_VIDEO_SHARE_VIDEO_MEMORY,
            &ShareMemory,
            sizeof(VIDEO_SHARE_MEMORY),
            &ShareMemoryInformation,
            sizeof(VIDEO_SHARE_MEMORY_INFORMATION),
            &ReturnedDataLength))
        {
            DBG_DD((0, "Failed IOCTL_VIDEO_SHARE_MEMORY"));
            
            lpMapMemory->ddRVal = DDERR_GENERIC;

            return(DDHAL_DRIVER_HANDLED);
        }
        
        lpMapMemory->fpProcess=(FLATPTR)ShareMemoryInformation.VirtualAddress;

    }
    else
    {
        ShareMemory.ProcessHandle           = lpMapMemory->hProcess;
        ShareMemory.ViewOffset              = 0;
        ShareMemory.ViewSize                = 0;
        ShareMemory.RequestedVirtualAddress = (VOID*) lpMapMemory->fpProcess;
        
        if (EngDeviceIoControl(ppdev->hDriver,
            IOCTL_VIDEO_UNSHARE_VIDEO_MEMORY,
            &ShareMemory,
            sizeof(VIDEO_SHARE_MEMORY),
            NULL,
            0,
            &ReturnedDataLength))
        {
            RIP("Failed IOCTL_VIDEO_UNSHARE_MEMORY");
        }
    }
    
    lpMapMemory->ddRVal = DD_OK;
    
    return(DDHAL_DRIVER_HANDLED);
}


//-----------------------------------------------------------------------------
//
// DdSetExclusiveMode
//
// This function is called by DirectDraw when we switch from the GDI surface,
// to DirectDraw exclusive mode, e.g. to run a game in fullcreen mode.
// You only need to implement this function when you are using the 
// 'HeapVidMemAllocAligned' function and allocate memory for Device Bitmaps 
// and DirectDraw surfaces from the same heap. 
//
// We use this call to disable GDI DeviceBitMaps when we are running in 
// DirectDraw exclusive mode. Otherwise a DD app gets confused if both GDI and
// DirectDraw allocate memory from the same heap.
//
// See also DdFlipToGDISurface.
//
//-----------------------------------------------------------------------------


DWORD CALLBACK
DdSetExclusiveMode(PDD_SETEXCLUSIVEMODEDATA lpSetExclusiveMode)
{
    PDev*   ppdev=(PDev*)lpSetExclusiveMode->lpDD->dhpdev;

    DBG_DD((6, "DDraw::DdSetExclusiveMode called"));

    // remember setting of exclusive mode in ppdev,
    // so GDI can stop to promote DeviceBitmaps into
    // video memory

    ppdev->bDdExclusiveMode = lpSetExclusiveMode->dwEnterExcl;

    if (ppdev->bDdExclusiveMode)
    {
        // remove all GDI device bitmaps from video memory here
        // and make sure they will not be promoted to videomemory
        // until we leave exclusive mode.  

        bDemoteAll(ppdev);
    }

    lpSetExclusiveMode->ddRVal=DD_OK;

    return (DDHAL_DRIVER_HANDLED);
}

//-----------------------------------------------------------------------------
//
// DWORD DdFlipToGDISurface
//
// This function is called by DirectDraw when it flips to the surface on which
// GDI can write to.
//
//-----------------------------------------------------------------------------
  
DWORD CALLBACK
DdFlipToGDISurface(PDD_FLIPTOGDISURFACEDATA lpFlipToGDISurface)
{
    PDev*   ppdev=(PDev*)lpFlipToGDISurface->lpDD->dhpdev;

    DBG_DD((6, "DDraw::DdFlipToGDISurface called"));

    ppdev->dwNewDDSurfaceOffset=0xffffffff;

#if DX7_STEREO   
    if (ppdev->bDdStereoMode)
    {
        ppdev->bDdStereoMode=FALSE;
        LD_PERMEDIA_REG(PREG_VIDEOCONTROL, 
            READ_PERMEDIA_REG(PREG_VIDEOCONTROL) &
            ~PREG_VC_STEREOENABLE);
    }
#endif

    lpFlipToGDISurface->ddRVal=DD_OK;

    //
    //  we return NOTHANDLED, then the ddraw runtime takes
    //  care that we flip back to the primary...
    //
    return (DDHAL_DRIVER_NOTHANDLED);
}

//-----------------------------------------------------------------------------
//
// DWORD DdFreeDriverMemory
//
// This function called by DirectDraw when it's running low on memory in
// our heap.  You only need to implement this function if you use the
// DirectDraw 'HeapVidMemAllocAligned' function in your driver, and you
// can boot those allocations out of memory to make room for DirectDraw.
//
// We implement this function in the P2 driver because we have DirectDraw
// entirely manage our off-screen heap, and we use HeapVidMemAllocAligned
// to put GDI device-bitmaps in off-screen memory.  DirectDraw applications
// have a higher priority for getting stuff into video memory, though, and
// so this function is used to boot those GDI surfaces out of memory in
// order to make room for DirectDraw.
//
//-----------------------------------------------------------------------------

DWORD CALLBACK
DdFreeDriverMemory(PDD_FREEDRIVERMEMORYDATA lpFreeDriverMemory)
{
    PDev*   ppdev;

    DBG_DD((6, "DDraw::DdFreeDriverMemory called"));

    ppdev = (PDev*)lpFreeDriverMemory->lpDD->dhpdev;

    lpFreeDriverMemory->ddRVal = DDERR_OUTOFMEMORY;

    //
    // If we successfully freed up some memory, set the return value to
    // 'DD_OK'.  DirectDraw will try again to do its allocation, and
    // will call us again if there's still not enough room.  (It will
    // call us until either there's enough room for its alocation to
    // succeed, or until we return something other than DD_OK.)
    //
    if ( bMoveOldestBMPOut(ppdev) )
    {
        lpFreeDriverMemory->ddRVal = DD_OK;
    }
    
    return (DDHAL_DRIVER_HANDLED);
}// DdFreeDriverMemory()

//-----------------------------------------------------------------------------
// 
// BOOL DrvGetDirectDrawInfo
//
// Function called by DirectDraw to returns the capabilities of the graphics
// hardware
//
// Parameters:
//
// dhpdev-------Is a handle to the PDEV returned by the driver's DrvEnablePDEV
//              routine.
// pHalInfo-----Points to a DD_HALINFO structure in which the driver should
//              return the hardware capabilities that it supports. 
// pdwNumHeaps--Points to the location in which the driver should return the
//              number of VIDEOMEMORY structures pointed to by pvmList. 
// pvmList------Points to an array of VIDEOMEMORY structures in which the
//              driver should return information about each video memory chunk
//              that it controls. The driver should ignore this parameter when
//              it is NULL. 
// pdwNumFourCC-Points to the location in which the driver should return the
//              number of DWORDs pointed to by pdwFourCC. 
// pdwFourCC----Points to an array of DWORDs in which the driver should return
//              information about each FOURCC that it supports. The driver
//              should ignore this parameter when it is NULL.
//
// Return:
//  Returns TRUE if it succeeds; otherwise, it returns FALSE
//
// Note:
//  This function will be called twice before DrvEnableDirectDraw is called.
//
// Comments
//  The driver's DrvGetDirectDrawInfo routine should do the following: 
//  1)When pvmList and pdwFourCC are NULL: 
//  Reserve off-screen video memory for DirectDraw use. Write the number of
//  driver video memory heaps and supported FOURCCs in pdwNumHeaps and
//  pdwNumFourCC, respectively. 
//
//  2)When pvmList and pdwFourCC are not NULL: 
//  Write the number of driver video memory heaps and supported FOURCCs in
//  pdwNumHeaps and pdwNumFourCC, respectively.
//  Get ptr to reserved offscreen mem? 
//  For each VIDEOMEMORY structure in the list to which pvmList points, fill in
//  the appropriate members to describe a particular chunk of display memory.
//  The list of structures provides DirectDraw with a complete description of
//  the driver's off-screen memory. 
//
//  3)Initialize the members of the DD_HALINFO structure with driver-specific
//  information as follows: 
//  Initialize the appropriate members of the VIDEOMEMORYINFO structure to
//  describe the general characteristics of the display's memory. 
//  Initialize the appropriate members of the DDNTCORECAPS structure to
//  describe the capabilities of the hardware. 
//  If the driver implements a DdGetDriverInfo function, set GetDriverInfo to
//  point to it and set dwFlags to DDHALINFO_GETDRIVERINFOSET
//
//-----------------------------------------------------------------------------

BOOL 
DrvGetDirectDrawInfo(DHPDEV         dhpdev,
                     DD_HALINFO*    pHalInfo,
                     DWORD*         pdwNumHeaps,
                     VIDEOMEMORY*   pvmList,     // Will be NULL on first call
                     DWORD*         pdwNumFourCC,
                     DWORD*         pdwFourCC)   // Will be NULL on first call
{
    BOOL            bCanFlip;
    BOOL            bDefineAGPHeap = FALSE,bDefineDDrawHeap = FALSE;
    LONGLONG        li;
    VIDEOMEMORY*    pVm;
    DWORD           cHeaps;
    DWORD           dwRegistryValue;

    DBG_DD((3, "DrvGetDirectDrawInfo Called"));

    PDev *ppdev=(PDev*) dhpdev;

    
    *pdwNumFourCC = 0;
    *pdwNumHeaps = 0;
    
    //On the first call, setup the chip info

    if(!(pvmList && pdwFourCC)) {

        //
        // Fill in the DDHAL Informational caps
        //
        GetDDHALInfo(ppdev, pHalInfo);
        
        //
        // Current primary surface attributes:
        //
        pHalInfo->vmiData.pvPrimary                 = ppdev->pjScreen;
        pHalInfo->vmiData.fpPrimary                 = 0;
        pHalInfo->vmiData.dwDisplayWidth            = ppdev->cxScreen;
        pHalInfo->vmiData.dwDisplayHeight           = ppdev->cyScreen;
        pHalInfo->vmiData.lDisplayPitch             = ppdev->lDelta;
        pHalInfo->vmiData.ddpfDisplay.dwSize        = sizeof(DDPIXELFORMAT);
        pHalInfo->vmiData.ddpfDisplay.dwFlags       = DDPF_RGB;
        pHalInfo->vmiData.ddpfDisplay.dwRGBBitCount = ppdev->cjPelSize * 8;
        
        if ( ppdev->iBitmapFormat == BMF_8BPP ) {
            //
            // Tell DDRAW that the surface is 8-bit color indexed
            //
            pHalInfo->vmiData.ddpfDisplay.dwFlags |= DDPF_PALETTEINDEXED8;
        }
        
        //
        // These masks will be zero at 8bpp:
        //
        pHalInfo->vmiData.ddpfDisplay.dwRBitMask    = ppdev->flRed;
        pHalInfo->vmiData.ddpfDisplay.dwGBitMask    = ppdev->flGreen;
        pHalInfo->vmiData.ddpfDisplay.dwBBitMask    = ppdev->flBlue;
        
        //
        // We have to tell DirectDraw our preferred off-screen alignment
        //
        pHalInfo->vmiData.dwOffscreenAlign = 4;
        pHalInfo->vmiData.dwZBufferAlign = 4;
        pHalInfo->vmiData.dwTextureAlign = 4;
    
        pHalInfo->ddCaps.dwVidMemTotal = 
            (ppdev->lVidMemHeight - ppdev->cyScreen) * ppdev->lDelta;
    }

    cHeaps = 0;
    
    //
    // Determine the YUV modes for Video playback acceleration. We can do YUV
    // conversions at any depth except 8 bits...
    //
    if (ppdev->iBitmapFormat != BMF_8BPP) {
        *pdwNumFourCC = sizeof( fourCC ) / sizeof( fourCC[0] );
    }

    if(DD_P2AGPCAPABLE(ppdev)) {
        bDefineAGPHeap = TRUE;
        cHeaps++;
    }

    // Do we have sufficient videomemory to create an off-screen heap for
    // DDraw? Test how much video memory is left after we subtract
    // that which is being used for the screen. 
    
    if ( (ppdev->cxScreen < ppdev->lVidMemWidth)
       ||(ppdev->cyScreen < ppdev->lVidMemHeight)) 
    {
            bDefineDDrawHeap = TRUE;
            cHeaps++;
    }

    ppdev->cHeaps = cHeaps;
    *pdwNumHeaps  = cHeaps;
    
    // Define the fourCC's that we support
    if (pdwFourCC) {
        memcpy(pdwFourCC, fourCC, sizeof(fourCC));
    }

    // If pvmList is not NULL then we can go ahead and fill out the VIDEOMEMORY
    // structures which define our requested heaps.

    if(pvmList) {

        pVm=pvmList;

        //
        // Snag a pointer to the video-memory list so that we can use it to
        // call back to DirectDraw to allocate video memory:
        //
        ppdev->pvmList = pVm;

        //
        // Create one heap to describe the unused portion of video memory for
        // DirectDraw use   
        //
        // Note: here lVidMemWidth is in "pixel" unit. So we should multiply it
        // by cjPelSize to get actually BYTES of video memory
        //
        // fpStart---Points to the starting address of a memory range in the
        // heap. 
        // fpEnd-----Points to the ending address of a memory range if the heap
        // is linear. This address is inclusive, that is, it specifies the last
        // valid address in the range. Thus, the number of bytes specified by
        // fpStart and fpEnd is (fpEnd-fpStart+1).          
        //
        // Define the heap for DirectDraw
        //
        if ( bDefineDDrawHeap )
        {
            pVm->dwFlags        = VIDMEM_ISLINEAR ;
            pVm->fpStart        = ppdev->cyScreen * ppdev->lDelta;
            pVm->fpEnd          = ppdev->lVidMemHeight * ppdev->lDelta - 1;

            //
            // DWORD align the size, the hardware should guarantee this
            //
            ASSERTDD(((pVm->fpEnd - pVm->fpStart + 1) & 3) == 0,
                    "The off-screen heap size should be DWORD aligned");
            
            pVm->ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN;
            DBG_DD((7, "fpStart %ld fpEnd %ld", pVm->fpStart, pVm->fpEnd));
            DBG_DD((7, "DrvGetDirectDrawInfo Creates 1 heap for DDRAW"));
            
            pVm++;
            
        }

        //Define the AGP heap
        if(bDefineAGPHeap) {
            DWORD dwAGPMemBytes;
            BOOL bSuccess;
            
            // Request 32Mb of AGP Memory, DDRAW will allocate less 
            // if this amount is not available
            dwAGPMemBytes = P2_AGP_HEAPSIZE*1024*1024;
                
            DBG_DD((7, "Initialised AGP Heap for P2"));
                
            // The start address of the heap, 
            // just set to zero as DDRAW handles the allocation
            pVm->fpStart = 0;
            // Fetch the last byte of AGP memory
            pVm->fpEnd = dwAGPMemBytes - 1;

            // drivers can set VIDMEM_ISWC here, 
            // then memory will be write combined.
            // but memory on AGP buses is always uncached
            pVm->dwFlags = VIDMEM_ISNONLOCAL | VIDMEM_ISLINEAR | VIDMEM_ISWC;

            // Only use AGP memory for textures and OFFSCREENPLAIN
            pVm->ddsCaps.dwCaps =   DDSCAPS_OVERLAY | 
                                    DDSCAPS_FRONTBUFFER |
                                    DDSCAPS_BACKBUFFER | 
                                    DDSCAPS_ZBUFFER | 
                                    DDSCAPS_3DDEVICE
                                    ;

            pVm->ddsCapsAlt.dwCaps =DDSCAPS_OVERLAY | 
                                    DDSCAPS_FRONTBUFFER |
                                    DDSCAPS_BACKBUFFER | 
                                    DDSCAPS_ZBUFFER | 
                                    DDSCAPS_3DDEVICE
                                    ;
            ++pVm;
        } 

    }
            

    DBG_DD((6, "DrvGetDirectDrawInfo return TRUE"));
    return(TRUE);            
}// DrvGetDirectDrawInfo()

//-----------------------------------------------------------------------------
//
//  InitDDHAL
// 
//  do the final initialisation of the HAL:
//  setup DDraw specific variables for the ppdev and fill in all callbacks
//  for DirectDraw
//
//  No Chip register setup is done here - it is all handled in the mode
//  change code which this function calls
// 
//-----------------------------------------------------------------------------

BOOL 
InitDDHAL(PPDev ppdev)
{
    PERMEDIA_DEFS(ppdev);
    
    DBG_DD((1, "DDraw:InitDDHAL*************************************" ));
    DBG_DD((1, "    ScreenStart =%08lx", ppdev->dwScreenStart));
    DBG_DD((1, "    ScreenWidth=%08lx",  ppdev->cxScreen ));
    DBG_DD((1, "    ScreenHeight=%08lx", ppdev->cyScreen));
    DBG_DD((1, "    dwRGBBitCount=%ld", ppdev->ddpfDisplay.dwRGBBitCount ));
    DBG_DD((1, "    RMask:   0x%x", ppdev->ddpfDisplay.dwRBitMask ));
    DBG_DD((1, "    GMask:   0x%x", ppdev->ddpfDisplay.dwGBitMask ));
    DBG_DD((1, "    BMask:   0x%x", ppdev->ddpfDisplay.dwBBitMask ));
    DBG_DD((1, "*****************************************************" ));
    
    // Fill in the HAL Callback pointers
    memset(&ppdev->DDHALCallbacks, 0, sizeof(DDHAL_DDCALLBACKS));
    ppdev->DDHALCallbacks.dwSize = sizeof(DDHAL_DDCALLBACKS);
    ppdev->DDHALCallbacks.WaitForVerticalBlank = DdWaitForVerticalBlank;
    ppdev->DDHALCallbacks.CanCreateSurface = DdCanCreateSurface;
    ppdev->DDHALCallbacks.GetScanLine = DdGetScanLine;
    ppdev->DDHALCallbacks.MapMemory = DdMapMemory;
    ppdev->DDHALCallbacks.CreateSurface = DdCreateSurface;
    
    // Fill in the HAL Callback flags
    ppdev->DDHALCallbacks.dwFlags = DDHAL_CB32_WAITFORVERTICALBLANK |
                                    DDHAL_CB32_MAPMEMORY |
                                    DDHAL_CB32_GETSCANLINE |
                                    DDHAL_CB32_CANCREATESURFACE |
                                    DDHAL_CB32_CREATESURFACE;
    
    // Fill in the Surface Callback pointers
    memset(&ppdev->DDSurfCallbacks, 0, sizeof(DDHAL_DDSURFACECALLBACKS));
    ppdev->DDSurfCallbacks.dwSize = sizeof(DDHAL_DDSURFACECALLBACKS);
    ppdev->DDSurfCallbacks.DestroySurface = DdDestroySurface;
    ppdev->DDSurfCallbacks.Flip = DdFlip;
    ppdev->DDSurfCallbacks.Lock = DdLock;
    ppdev->DDSurfCallbacks.GetBltStatus = DdGetBltStatus;
    ppdev->DDSurfCallbacks.GetFlipStatus = DdGetFlipStatus;
    ppdev->DDSurfCallbacks.Blt = DdBlt;

    ppdev->DDSurfCallbacks.dwFlags =    DDHAL_SURFCB32_DESTROYSURFACE |
                                        DDHAL_SURFCB32_FLIP     |
                                        DDHAL_SURFCB32_LOCK     |
                                        DDHAL_SURFCB32_BLT |
                                        DDHAL_SURFCB32_GETBLTSTATUS |
                                        DDHAL_SURFCB32_GETFLIPSTATUS;

    ppdev->DDSurfCallbacks.SetColorKey = DdSetColorKey;
    ppdev->DDSurfCallbacks.dwFlags |= DDHAL_SURFCB32_SETCOLORKEY;

    // Fill in the DDHAL Informational caps
    GetDDHALInfo(ppdev, &ppdev->ddhi32);
    
    return (TRUE);
    
}// InitDDHAL()

//-----------------------------------------------------------------------------
//
//  bIsStereoMode
//
//  Decide if mode can be displayed as stereo mode. Here we limit stereo
//  modes so that two front and two backbuffers can be created for rendering.
//
//-----------------------------------------------------------------------------

BOOL bIsStereoMode(PDev *ppdev, PDD_STEREOMODE pDDStereoMode)
{
    pDDStereoMode->bSupported = FALSE;   

    if (pDDStereoMode->dwWidth >= 320 &&
        pDDStereoMode->dwHeight >= 240 
       )
    {
        DWORD dwLines=ppdev->FrameBufferLength/
            (pDDStereoMode->dwWidth*pDDStereoMode->dwBpp/8);
        if (dwLines > (pDDStereoMode->dwHeight*4))
        {
            pDDStereoMode->bSupported = TRUE;   
        }
    }

    return pDDStereoMode->bSupported;
}

//-----------------------------------------------------------------------------
//
// DdGetDriverInfo
//
// callback for various new HAL features, post DX3.
//
//-----------------------------------------------------------------------------

DWORD CALLBACK 
DdGetDriverInfo(LPDDHAL_GETDRIVERINFODATA lpData)
{
    PPDev ppdev=(PPDev)lpData->dhpdev;
    PERMEDIA_DEFS(ppdev);

    DWORD dwSize;

    DBG_DD(( 2, "DDraw:GetDriverInfo"));

    // Get a pointer to the chip we are on.

        
    // Default to 'not supported'
    lpData->ddRVal = DDERR_CURRENTLYNOTAVAIL;
    ppdev = (PDev*) lpData->dhpdev;
    
    // fill in supported stuff  
    if (IsEqualIID(&lpData->guidInfo, &GUID_D3DCallbacks3)) 
    {
        D3DHAL_CALLBACKS3 D3DCB3;
        DBG_DD((3,"  GUID_D3DCallbacks3"));
        
        memset(&D3DCB3, 0, sizeof(D3DHAL_CALLBACKS3));
        D3DCB3.dwSize = sizeof(D3DHAL_CALLBACKS3);
        D3DCB3.lpvReserved = NULL; 
        D3DCB3.ValidateTextureStageState = D3DValidateTextureStageState;
        D3DCB3.DrawPrimitives2 = D3DDrawPrimitives2;
        D3DCB3.dwFlags |=   D3DHAL3_CB32_DRAWPRIMITIVES2           |
                            D3DHAL3_CB32_VALIDATETEXTURESTAGESTATE |
                            0;

        lpData->dwActualSize = sizeof(D3DHAL_CALLBACKS3);
        dwSize=min(lpData->dwExpectedSize,sizeof(D3DHAL_CALLBACKS3));
        memcpy(lpData->lpvData, &D3DCB3, dwSize);
        lpData->ddRVal = DD_OK;
    } 
    else if (IsEqualIID(&lpData->guidInfo, &GUID_D3DExtendedCaps)) 
    {
        D3DNTHAL_D3DEXTENDEDCAPS D3DExtendedCaps;
        DBG_DD((3,"  GUID_D3DExtendedCaps"));
            
        memset(&D3DExtendedCaps, 0, sizeof(D3DExtendedCaps));
        dwSize=min(lpData->dwExpectedSize, sizeof(D3DExtendedCaps));

        lpData->dwActualSize = dwSize;
        D3DExtendedCaps.dwSize = dwSize;

        // number of (multi)textures we support simultaneusly for DX6
        D3DExtendedCaps.dwFVFCaps = 1;

        D3DExtendedCaps.dwMinTextureWidth  = 1;
        D3DExtendedCaps.dwMinTextureHeight = 1;
        D3DExtendedCaps.dwMaxTextureWidth  = 2048;
        D3DExtendedCaps.dwMaxTextureHeight = 2048;

        D3DExtendedCaps.dwMinStippleWidth = 8;
        D3DExtendedCaps.dwMaxStippleWidth = 8;
        D3DExtendedCaps.dwMinStippleHeight = 8;
        D3DExtendedCaps.dwMaxStippleHeight = 8;

        D3DExtendedCaps.dwTextureOpCaps =
            D3DTEXOPCAPS_DISABLE                   |
            D3DTEXOPCAPS_SELECTARG1                |
            D3DTEXOPCAPS_SELECTARG2                |
            D3DTEXOPCAPS_MODULATE                  |
            D3DTEXOPCAPS_ADD                       |
            D3DTEXOPCAPS_BLENDTEXTUREALPHA         |
            0;

        D3DExtendedCaps.wMaxTextureBlendStages = 1;
        D3DExtendedCaps.wMaxSimultaneousTextures = 1;

        // Full range of the integer (non-fractional) bits of the 
        // post-normalized texture indices. If the 
        // D3DDEVCAPS_TEXREPEATNOTSCALEDBYSIZE bit is set, the 
        // device defers scaling by the texture size until after 
        // the texture address mode is applied. If it isn't set, 
        // the device scales the texture indices by the texture size 
        // (largest level-of-detail) prior to interpolation. 
        D3DExtendedCaps.dwMaxTextureRepeat = 2048;

        // In order to support stencil buffers in DX6 we need besides
        // setting these caps and handling the proper renderstates to
        // declare the appropriate z buffer pixel formats here in 
        // response to the GUID_ZPixelFormats and implement the
        // Clear2 callback. Also , we need to be able to create the
        // appropriate ddraw surfaces.
#if D3D_STENCIL
        D3DExtendedCaps.dwStencilCaps =  0                      |
                                        D3DSTENCILCAPS_KEEP     |
                                        D3DSTENCILCAPS_ZERO     |
                                        D3DSTENCILCAPS_REPLACE  |
                                        D3DSTENCILCAPS_INCRSAT  |
                                        D3DSTENCILCAPS_DECRSAT  |
                                        D3DSTENCILCAPS_INVERT;
#endif

#if D3DDX7_TL
        // In order to use hw accelerated T&L we must declare 
        // how many simultaneously active lights we can handle.
        D3DExtendedCaps.dwMaxActiveLights = 0;
#endif //D3DDX7_TL


        memcpy(lpData->lpvData, &D3DExtendedCaps, dwSize);
        lpData->ddRVal = DD_OK;
    } 
    else if (IsEqualIID(&lpData->guidInfo, &GUID_ZPixelFormats)) 
    {
        DDPIXELFORMAT ddZBufPixelFormat[2];
        DWORD         dwNumZPixelFormats;
        
        DBG_DD((3,"  GUID_ZPixelFormats"));


        memset(&ddZBufPixelFormat, 0, sizeof(ddZBufPixelFormat));

#if D3D_STENCIL
        dwSize = min(lpData->dwExpectedSize, 2*sizeof(DDPIXELFORMAT));
        lpData->dwActualSize = 2*sizeof(DDPIXELFORMAT) + sizeof(DWORD);
#else
        dwSize = min(lpData->dwExpectedSize, 1*sizeof(DDPIXELFORMAT));
        lpData->dwActualSize = 1*sizeof(DDPIXELFORMAT) + sizeof(DWORD);
#endif

        // If we didn't support stencils, we would only fill one 16-bit
        // Z Buffer format since that is all what the Permedia supports.
        // Drivers that implement stencil buffer support (like this one)
        // have to report here all Z Buffer formats supported since they 
        // have to support the Clear2 callback (or the D3DDP2OP_CLEAR
        // token)
        
#if D3D_STENCIL
        dwNumZPixelFormats = 2;
#else
        dwNumZPixelFormats = 1;
#endif
          
        ddZBufPixelFormat[0].dwSize = sizeof(DDPIXELFORMAT);
        ddZBufPixelFormat[0].dwFlags = DDPF_ZBUFFER;
        ddZBufPixelFormat[0].dwFourCC = 0;
        ddZBufPixelFormat[0].dwZBufferBitDepth = 16;
        ddZBufPixelFormat[0].dwStencilBitDepth = 0;
        ddZBufPixelFormat[0].dwZBitMask = 0xFFFF;
        ddZBufPixelFormat[0].dwStencilBitMask = 0x0000;
        ddZBufPixelFormat[0].dwRGBZBitMask = 0;

#if D3D_STENCIL
        ddZBufPixelFormat[1].dwSize = sizeof(DDPIXELFORMAT);
        ddZBufPixelFormat[1].dwFlags = DDPF_ZBUFFER | DDPF_STENCILBUFFER;
        ddZBufPixelFormat[1].dwFourCC = 0;
        // The sum of the z buffer bit depth AND the stencil depth 
        // should be included here
        ddZBufPixelFormat[1].dwZBufferBitDepth = 16;
        ddZBufPixelFormat[1].dwStencilBitDepth = 1;
        ddZBufPixelFormat[1].dwZBitMask = 0x7FFF;
        ddZBufPixelFormat[1].dwStencilBitMask = 0x8000;
        ddZBufPixelFormat[1].dwRGBZBitMask = 0;
#endif

        memcpy(lpData->lpvData, &dwNumZPixelFormats, sizeof(DWORD));
        memcpy((LPVOID)((LPBYTE)(lpData->lpvData) + sizeof(DWORD)), 
                        &ddZBufPixelFormat, dwSize);
        
        lpData->ddRVal = DD_OK;
    } 
    else if (IsEqualIID(&(lpData->guidInfo), 
                &GUID_D3DParseUnknownCommandCallback)) 
    {
        DBG_DD((3,"  GUID_D3DParseUnknownCommandCallback"));
        ppdev->pD3DParseUnknownCommand = 
            (PFND3DNTPARSEUNKNOWNCOMMAND)(lpData->lpvData);
        lpData->ddRVal = DD_OK;
    }
    else if (IsEqualIID(&(lpData->guidInfo), &GUID_Miscellaneous2Callbacks) ) 
    {
        BOOL bRet;
        DWORD dwResult;
        
        DDHAL_DDMISCELLANEOUS2CALLBACKS MISC2_CB;
        
        DBG_DD((3,"  GUID_Miscellaneous2Callbacks2"));
        
        memset(&MISC2_CB, 0, sizeof(DDHAL_DDMISCELLANEOUS2CALLBACKS));
        MISC2_CB.dwSize = sizeof(DDHAL_DDMISCELLANEOUS2CALLBACKS);
        
        MISC2_CB.dwFlags  = 0
            | DDHAL_MISC2CB32_CREATESURFACEEX 
            | DDHAL_MISC2CB32_GETDRIVERSTATE
            | DDHAL_MISC2CB32_DESTROYDDLOCAL;

        MISC2_CB.GetDriverState = D3DGetDriverState;
        MISC2_CB.CreateSurfaceEx = D3DCreateSurfaceEx;
        MISC2_CB.DestroyDDLocal = D3DDestroyDDLocal;

        lpData->dwActualSize = sizeof(MISC2_CB);
        dwSize = min(sizeof(MISC2_CB),lpData->dwExpectedSize);
        memcpy(lpData->lpvData, &MISC2_CB, dwSize);
        lpData->ddRVal = DD_OK;
    }
    else if (IsEqualIID(&(lpData->guidInfo), &GUID_UpdateNonLocalHeap))
    {
        LPDDHAL_UPDATENONLOCALHEAPDATA pDDNonLocalHeap;

        DBG_DD((3,"  GUID_UpdateNonLocalHeap"));

        pDDNonLocalHeap = (LPDDHAL_UPDATENONLOCALHEAPDATA)lpData->lpvData; 

        ppdev->dwGARTLinBase = pDDNonLocalHeap->fpGARTLin;
        ppdev->dwGARTDevBase = pDDNonLocalHeap->fpGARTDev;

        // These values are used to specify the base address of the
        // visible 8Mb window of AGP memory

        ppdev->dwGARTLin = pDDNonLocalHeap->fpGARTLin;
        ppdev->dwGARTDev = pDDNonLocalHeap->fpGARTDev;

        DDCONTEXT;
        SYNC_WITH_PERMEDIA;

        LD_PERMEDIA_REG (PREG_AGPTEXBASEADDRESS,(ULONG)ppdev->dwGARTDev);

        DBG_DD((3,"GartLin: 0x%x, GartDev: 0x%x", 
            (ULONG)ppdev->dwGARTLin, ppdev->dwGARTDev));

        lpData->ddRVal = DD_OK;
   
    }   
    else if (IsEqualIID(&(lpData->guidInfo), &GUID_GetHeapAlignment) ) 
    {

        LPDDHAL_GETHEAPALIGNMENTDATA lpFData=
            (LPDDHAL_GETHEAPALIGNMENTDATA) lpData->lpvData;

        DBG_DD((3,"  GUID_GetHeapAlignment"));

        lpData->ddRVal = DD_OK;
    } 
    else if (IsEqualIID(&(lpData->guidInfo), &GUID_NTPrivateDriverCaps) ) 
    {
        DD_NTPRIVATEDRIVERCAPS DDPrivateDriverCaps;

        DBG_DD((3,"  GUID_NTPrivateDriverCaps"));

        memset(&DDPrivateDriverCaps, 0, sizeof(DDPrivateDriverCaps));
        DDPrivateDriverCaps.dwSize=sizeof(DDPrivateDriverCaps);

        // we want the kernel to call us when a primary surface is created
        // so that we can store some private information in the 
        // lpGbl->dwReserved1 field
        DDPrivateDriverCaps.dwPrivateCaps=DDHAL_PRIVATECAP_NOTIFYPRIMARYCREATION;
        
        lpData->dwActualSize =sizeof(DDPrivateDriverCaps);

        dwSize = min(sizeof(DDPrivateDriverCaps),lpData->dwExpectedSize);
        memcpy(lpData->lpvData, &DDPrivateDriverCaps, dwSize);
        lpData->ddRVal = DD_OK;
    } 
#if DX7_STEREO
    else if (IsEqualIID(&(lpData->guidInfo), &GUID_DDMoreSurfaceCaps) ) 
    {
        DD_MORESURFACECAPS DDMoreSurfaceCaps;
        DDSCAPSEX   ddsCapsEx, ddsCapsExAlt;
        ULONG ulCopyPointer;

        DBG_DD((3,"  GUID_DDMoreSurfaceCaps"));

        // fill in everything until expectedsize...
        memset(&DDMoreSurfaceCaps, 0, sizeof(DDMoreSurfaceCaps));

        // Caps for heaps 2..n
        memset(&ddsCapsEx, 0, sizeof(ddsCapsEx));
        memset(&ddsCapsExAlt, 0, sizeof(ddsCapsEx));

        DDMoreSurfaceCaps.dwSize=lpData->dwExpectedSize;

        DBG_DD((3,"  stereo support: %ld", ppdev->bCanDoStereo));
        if (ppdev->bCanDoStereo)
        {
            DDMoreSurfaceCaps.ddsCapsMore.dwCaps2 = 
                DDSCAPS2_STEREOSURFACELEFT;
        }
        lpData->dwActualSize = lpData->dwExpectedSize;

        dwSize = min(sizeof(DDMoreSurfaceCaps),lpData->dwExpectedSize);
        memcpy(lpData->lpvData, &DDMoreSurfaceCaps, dwSize);

        // now fill in other heaps...
        while (dwSize < lpData->dwExpectedSize)
        {
            memcpy( (PBYTE)lpData->lpvData+dwSize, 
                    &ddsCapsEx, 
                    sizeof(DDSCAPSEX));
            dwSize += sizeof(DDSCAPSEX);
            memcpy( (PBYTE)lpData->lpvData+dwSize, 
                    &ddsCapsExAlt, 
                    sizeof(DDSCAPSEX));
            dwSize += sizeof(DDSCAPSEX);
        }

        lpData->ddRVal = DD_OK;
    }
    else if (IsEqualIID(&(lpData->guidInfo), &GUID_DDStereoMode) ) {
        PDD_STEREOMODE pDDStereoMode;

        // Permedia supports all modes as stereo modes.
        // for test purposes, we restrict them to something 
        // larger than 320x240

        //
        // note: this GUID_DDStereoMode is only used on NT to
        // report stereo modes. There is no need to implement
        // it in win9x drivers. Win9x drivers report stereo
        // modes by setting the DDMODEINFO_STEREO bit in the 
        // dwFlags member of the DDHALMODEINFO structure.
        // It is also recommended to report DDMODEINFO_MAXREFRESH
        // for stereo modes when running under a runtime >= DX7 to
        // allow applications to select higher refresh rates for
        // stereo modes.
        //

        if (lpData->dwExpectedSize >= sizeof(PDD_STEREOMODE))
        {
            pDDStereoMode = (PDD_STEREOMODE) lpData->lpvData; 

            bIsStereoMode( ppdev, pDDStereoMode);

            DBG_DD((3,"  GUID_DDStereoMode(%d,%d,%d,%d=%d)",
                pDDStereoMode->dwWidth,
                pDDStereoMode->dwHeight,
                pDDStereoMode->dwBpp,
                pDDStereoMode->dwRefreshRate,
                pDDStereoMode->bSupported));

            lpData->dwActualSize = sizeof(DD_STEREOMODE);
            lpData->ddRVal = DD_OK;
        }
    }
#endif
    else if (IsEqualIID(&(lpData->guidInfo), &GUID_NonLocalVidMemCaps) ) 
    {
        DD_NONLOCALVIDMEMCAPS DDNonLocalVidMemCaps;

        DBG_DD((3,"  GUID_DDNonLocalVidMemCaps"));

        memset(&DDNonLocalVidMemCaps, 0, sizeof(DDNonLocalVidMemCaps));
        DDNonLocalVidMemCaps.dwSize=sizeof(DDNonLocalVidMemCaps);

        //fill in all supported nonlocal to videomemory blts
        //
        DDNonLocalVidMemCaps.dwNLVBCaps = DDCAPS_BLT |
                                          DDCAPS_BLTSTRETCH |
                                          DDCAPS_BLTQUEUE |
                                          DDCAPS_COLORKEY |
                                          DDCAPS_ALPHA |
                                          DDCAPS_CANBLTSYSMEM;

        DDNonLocalVidMemCaps.dwNLVBCaps2 = 0;

        DDNonLocalVidMemCaps.dwNLVBCKeyCaps=DDCKEYCAPS_SRCBLT |
                                            DDCKEYCAPS_SRCBLTCLRSPACE;
                                            

        DDNonLocalVidMemCaps.dwNLVBFXCaps = DDFXCAPS_BLTALPHA |
                                            DDFXCAPS_BLTFILTER |
                                            DDFXCAPS_BLTSTRETCHY |
                                            DDFXCAPS_BLTSTRETCHX |
                                            DDFXCAPS_BLTSTRETCHYN |
                                            DDFXCAPS_BLTSTRETCHXN |
                                            DDFXCAPS_BLTSHRINKY |
                                            DDFXCAPS_BLTSHRINKX |
                                            DDFXCAPS_BLTSHRINKYN |
                                            DDFXCAPS_BLTSHRINKXN |
                                            DDFXCAPS_BLTMIRRORUPDOWN |
                                            DDFXCAPS_BLTMIRRORLEFTRIGHT;

        if (ppdev->iBitmapFormat != BMF_8BPP)
        {
            DDNonLocalVidMemCaps.dwNLVBCaps |= DDCAPS_BLTFOURCC;
            DDNonLocalVidMemCaps.dwNLVBCKeyCaps|=DDCKEYCAPS_SRCBLTCLRSPACEYUV;
        }

        for(INT i = 0; i < DD_ROP_SPACE; i++ )
            DDNonLocalVidMemCaps.dwNLVBRops[i] = rops[i];
        
        lpData->dwActualSize =sizeof(DDNonLocalVidMemCaps);

        dwSize = min(sizeof(DDNonLocalVidMemCaps),lpData->dwExpectedSize);
        memcpy(lpData->lpvData, &DDNonLocalVidMemCaps, dwSize);
        lpData->ddRVal = DD_OK;
    } else if (IsEqualIID(&lpData->guidInfo, &GUID_NTCallbacks))
    {
        DD_NTCALLBACKS NtCallbacks;

        memset(&NtCallbacks, 0, sizeof(NtCallbacks));

        dwSize = min(lpData->dwExpectedSize, sizeof(DD_NTCALLBACKS));

        NtCallbacks.dwSize           = dwSize;
        NtCallbacks.dwFlags          =   DDHAL_NTCB32_FREEDRIVERMEMORY
                                       | DDHAL_NTCB32_SETEXCLUSIVEMODE 
                                       | DDHAL_NTCB32_FLIPTOGDISURFACE
                                       ;
        NtCallbacks.FreeDriverMemory = DdFreeDriverMemory;
        NtCallbacks.SetExclusiveMode = DdSetExclusiveMode;
        NtCallbacks.FlipToGDISurface = DdFlipToGDISurface;

        memcpy(lpData->lpvData, &NtCallbacks, dwSize);

        lpData->ddRVal = DD_OK;
    }

    // We always handled it.
    return DDHAL_DRIVER_HANDLED;

}   // GetDriverInfo


//-----------------------------------------------------------------------------
//
//  updateFlipStatus
//
//  return DD_OK when last flip has occured. 
//
//-----------------------------------------------------------------------------

HRESULT 
updateFlipStatus( PPDev ppdev )
{
    PERMEDIA_DEFS(ppdev);    
    DBG_DD((6, "DDraw:updateFlipStatus"));

    // we assume that we are already in the DDraw/D3D context.

    // read Permedia register which tells us if there is a flip pending
    if (ppdev->dwNewDDSurfaceOffset!=0xffffffff)
    {
        ULONG ulScreenBase=READ_PERMEDIA_REG(PREG_SCREENBASE);

        if (ulScreenBase!=
            ppdev->dwNewDDSurfaceOffset)
        {
            
            DBG_DD((7,"  SurfaceOffset %08lx instead of %08lx",
                ulScreenBase,
                ppdev->dwNewDDSurfaceOffset));

            //
            //  make sure all pending data was flushed!
            //
            FLUSHDMA();

            //
            // if we are busy, return
            // otherwise the pipeline is empty and we can
            // fall through and to check if the chip already flipped.
            //
            if (DRAW_ENGINE_BUSY)
                return DDERR_WASSTILLDRAWING;

        }
    }

    DWORD dwVideoControl=READ_PERMEDIA_REG(PREG_VIDEOCONTROL);

    if (dwVideoControl & PREG_VC_SCREENBASEPENDING)
    {
        DBG_DD((7,"  VideoControl still pending (%08lx)",dwVideoControl));
        return DDERR_WASSTILLDRAWING;
    }

    return DD_OK;
    
} // updateFlipStatus 



