/*DDK*************************************************************************/
/*                                                                           */
/* COPYRIGHT    Copyright (C) 1995 IBM Corporation                           */
/*                                                                           */
/*    The following IBM OS/2 WARP source code is provided to you solely for  */
/*    the purpose of assisting you in your development of OS/2 WARP device   */
/*    drivers. You may use this code in accordance with the IBM License      */
/*    Agreement provided in the IBM Device Driver Source Kit for OS/2. This  */
/*    Copyright statement may not be removed.                                */
/*                                                                           */
/*****************************************************************************/
/*****************************************************************************
 *
 * SOURCE FILE NAME = VRAMMAN.C
 *
 * DESCRIPTIVE NAME = VRAM Memory Manager
 *
 *
 * VERSION = V2.1
 *
 * DATE
 *
 * DESCRIPTION
 *             VRAM memory management routines.
 *
 * FUNCTIONS
 *
 * NOTES
 *
 * STRUCTURES
 *
 * EXTERNAL REFERENCES
 *
 * EXTERNAL FUNCTIONS
 *
*/
#ifdef ENDIVE

#define INCL_WINSHELLDATA
#define INCL_DOS
#define INCL_DOSERRORS
#define INCL_WINERRORS
#define INCL_SHLERRORS
#include "eddinclt.h"
#include "eddncach.h"
#include "edddtypt.h"
#include "eddmextf.h"
#include "cacheman.h"
#include "hwaccess.h"
#include "vramman.h"
#include "seamless.h"

PVRAMLIST pListHead = 0;
PHANDLELIST pHandleListHead = 0;
PSTATICLIST pStaticListHead = 0;
BOOL fVRAMInitialized = FALSE;
BOOL fVRAMDeath = FALSE;
BOOL fVRAMSeamless = FALSE;
BOOL fPrivateInUse = FALSE;
ULONG ulVRAMMan = 0;
ULONG ulScanLineBytes;
ULONG ulScanLine;
ULONG ulBPP;
ULONG ulBPPHW;
ULONG ulHandleAssign = 0;
ULONG ulIDAssign = 0;
POINTL ptlShared;
ULONG ulSizeShared;
POINTL ptlPrivate;
ULONG ulSizePrivate;
ULONG ulPrivateHandle = 0;
PVOID pSysMemDeath = 0;
FNVMIENTRY *pHWEntry;
HMODULE hMod = 0;
CHAR szName[NAME_SIZE];
BOOL fInit = FALSE;
ULONG ulAccelInUsePM = 0;
ULONG ulSerializeDAC = 0;
FBINFO FBInfo;

extern BOOL fENDIVEDebug;
extern HWMAP aResTable[];
extern CACHEMAP aFontTable[];
extern PHWMAP pHWMap;
extern PCACHEMAP pCacheMap;
extern BitmapHeader NewPatternTable[];
extern ULONG pSysCacheStartPhy;
extern ULONG pCurCacheBasePhy;
extern ULONG ColorPatPhysical;
extern DDTType DDT;
extern SEAMLESSDATA SeamlessData;

/*****************************************************************************
 *
 *  FUNCTION NAME:      ENDIVERegister()
 *
 *  DESCRIPTIVE NAME:
 *
 *  FUNCTION:
 *
 *  ENTRY POINT:
 *
 *  INPUT:
 *
 *  EXIT-NORMAL:
 *
 *  EXIT-ERROR:
 *
 *  EFFECTS:
 *
 *  NOTES:
 *
 *  INTERNAL REFERENCES:
 *    ROUTINES:
 *
 *  EXTERNAL REFERENCES:
 *    ROUTINES:
 *
 ****************************************************************************/

ULONG ENDIVERegister(PVOID pInput, PVOID pOutput)
{
   PVRAMREGISTERIN pIn = pInput;
   PVRAMREGISTEROUT pOut = pOutput;
   ULONG ulRc = DEV_OK;
   PHWMAP pVRAMHWMap;
   PCACHEMAP pVRAMCacheMap;
   VRAMINIT VRAMInit;

   if (fVRAMDeath)
   {
      /* No allocation will be allowed during death */
      /* PM is in background */
      LogError(PMERR_NOT_IN_A_PM_SESSION);
      return DEVESC_ERROR;
   }

   pOut->ulLength = sizeof(VRAMREGISTEROUT);
   pOut->ulFlags = 0;

   if (pIn->ulFlags & ENDIVE_REGISTER_HANDLE)
   {
      /* Check if accelerator is required */
      if (!(pIn->ulFlags & ENDIVE_REGISTER_VRAMONLY))
      {
         /* If PM screen group is not using the video accelerator */
         if (ulAccelInUsePM == 0)
         {
            /* If a VDM is using the video accelerator, return error */
            if (SeamlessData.ulAccelInUse != 0)
            {
               LogError(PMERR_RESOURCE_NOT_FOUND);
               return DEVESC_ERROR;
            }
            else
            {
               /* Neither PM or a VDM is using the accelerator hardware */
               /* Mark as PM using                                      */
               ulAccelInUsePM = 1;
               SeamlessData.ulAccelInUse = 1;
            }
         }

         if (InitAccel() == DEVESC_ERROR)
         {
            /* Accel failed init */
            ulAccelInUsePM = 0;
            SeamlessData.ulAccelInUse = 0;
            return DEVESC_ERROR;
         }
      }

      /* Registration for VRAM services */
      if (ulVRAMMan == 0)
      {
         /* Look at cache if SEAMLESS is enabled for amount of VRAM */
         /* which may be allocated.                                 */
         pVRAMHWMap = &(aResTable[HWResolution]);
         pVRAMCacheMap = &(aFontTable[HWResolution * 2 + 1]);
         ulBPP = pVRAMHWMap->BitCount;
         if (ulBPP == 8)
         {
            ulBPPHW = EIGHT_BPP;
         }
         else if (ulBPP == 16)
         {
            ulBPPHW = SIXTEEN_BPP;
         }
         else
         {
            ulBPPHW = TWENTYFOUR_BPP;
         }
         ulScanLineBytes = pVRAMHWMap->ulScanLineBytes;
         ulScanLine = pVRAMHWMap->ulScanLine;
         ptlShared.x = 0;
         ptlShared.y = pVRAMHWMap->phys_height - pVRAMHWMap->ulScansShared;
         ulSizeShared = ulScanLineBytes * pVRAMHWMap->ulScansShared;
         ptlPrivate.x = 0;
         ptlPrivate.y = pVRAMCacheMap->font_cache_bottom + 1;
         ulSizePrivate = ulScanLineBytes * pVRAMHWMap->ulScansPrivate;

         if ((pVRAMHWMap->ulScansShared == 0) && (pVRAMHWMap->ulScansPrivate == 0))
         {
            /* If no shared or private scans, return unsupported */
            if (ulAccelInUsePM)
            {
               /* Unload accelerator */
               (VOID)DMS32CallBack((PFN)UnLoadAccel);
               ulSerializeDAC = 0;
               ulAccelInUsePM = 0;
               SeamlessData.ulAccelInUse = 0;
            }
            return DEVESC_NOTIMPLEMENTED;
         }

         if (!fVRAMSeamless)
         {
            /* Upon first initialization, put cache in pseudo SEAMLESS mode */
            /* if SEAMLESS is not already enabled                           */
            pCurCacheBasePhy = pSysCacheStartPhy;
            shutdown_bm_cache();
            CacheManager(TRUE);
            eddt_InvalidateCache();
            max_cached_bitmaps = pCacheMap->max_bitmaps;
            (ULONG)NewPatternTable[PATSYM_DITHERED].BMPhys = pCacheMap->color_dither;
            (ULONG)NewPatternTable[PATSYM_MONODITH].BMPhys = pCacheMap->mono_dither;
            ColorPatPhysical = pCacheMap->color_dither;
            next_eviction = max_cached_bitmaps == 0 ? 0 : max_cached_bitmaps - 1;
         }

         /* Initialize VRAM memory management */
         if (pVRAMHWMap->ulScansPrivate)
         {
            VRAMInit.ptlStart.x = 0;
            VRAMInit.ptlStart.y = pVRAMCacheMap->font_cache_bottom + 1 + pVRAMHWMap->ulScansPrivate;
            VRAMInit.ulSize = ulScanLineBytes * (pVRAMHWMap->phys_height - VRAMInit.ptlStart.y);
            InitializeVRAM(&VRAMInit);
         }
         else
         {
            fVRAMInitialized = TRUE;
         }
      }

      /* Assign handle, making sure its not already assigned */
      do
      {
         ulHandleAssign++;
      }
      while (FindHandle(ulHandleAssign));

      pOut->ulHandle = ulHandleAssign;
      ulRc = AddHandle(ulHandleAssign, pIn->ulFlags);
      if (ulRc == DEV_OK)
      {
         ulVRAMMan++;
      }
   }
   else
   {
      /* Deregistration for VRAM services */
      /* Verify handle is registered */
      if (!FindHandle(pIn->ulHandle))
      {
         /* Not registered */
         LogError(PMERR_NO_HANDLE);
         return DEVESC_ERROR;
      }

      /* Delete handle registration */
      DeleteHandle(pIn->ulHandle, FALSE);
      DeleteStatic(pIn->ulHandle, FALSE);

      ulVRAMMan--;

      if (ulVRAMMan == 0)
      {
         ulIDAssign = 0;
         fVRAMInitialized = FALSE;
         FreeList();
         ulHandleAssign = 0;

         if ((fPrivateInUse) && (pIn->ulHandle == ulPrivateHandle))
         {
            fPrivateInUse = FALSE;
            ulPrivateHandle = 0;
         }

         if (ulAccelInUsePM)
         {
            /* Terminate Accelerator DLL */
            (VOID)DMS32CallBack((PFN)UnLoadAccel);
            /* Mark PM as not using the accelerator hardware */
            ulAccelInUsePM = 0;
            SeamlessData.ulAccelInUse = 0;
            ulSerializeDAC = 0;
         }

         /* Upon last termination, take cache out of pseudo SEAMLESS mode */
         /* if SEAMLESS is not already enabled                            */
         if (!fVRAMSeamless)
         {
            CacheManager(FALSE);
            eddt_InvalidateCache();
            shutdown_bm_cache();
            max_cached_bitmaps = pCacheMap->max_bitmaps;
            (ULONG)NewPatternTable[PATSYM_DITHERED].BMPhys = pCacheMap->color_dither;
            (ULONG)NewPatternTable[PATSYM_MONODITH].BMPhys = pCacheMap->mono_dither;
            ColorPatPhysical = pCacheMap->color_dither;
            next_eviction = max_cached_bitmaps == 0 ? 0 : max_cached_bitmaps - 1;
         }
      }
      else
      {
         /* If deallocation was not issued, clean up all allocations */
         FreeHandle(pIn->ulHandle);
         if ((!AccelInUse()) && (ulAccelInUsePM))
         {
            /* Terminate Accelerator DLL */
            (VOID)DMS32CallBack((PFN)UnLoadAccel);
            /* Mark PM as not using the accelerator hardware */
            ulAccelInUsePM = 0;
            SeamlessData.ulAccelInUse = 0;
            ulSerializeDAC = 0;
         }

      }
   }
   return ulRc;
}

/*****************************************************************************
 *
 *  FUNCTION NAME:    ENDIVERequestHardWare()
 *
 *
 *  DESCRIPTIVE NAME:
 *
 *  FUNCTION:
 *
 *  ENTRY POINT:
 *
 *  INPUT:
 *
 *  EXIT-NORMAL:
 *
 *  EXIT-ERROR:
 *
 *  EFFECTS:
 *
 *  NOTES:
 *
 *  INTERNAL REFERENCES:
 *    ROUTINES:
 *
 *  EXTERNAL REFERENCES:
 *    ROUTINES:
 *
 ****************************************************************************/

ULONG ENDIVERequestHardWare(PVOID pInput, PVOID pOutput)
{
   PVRAMHWREQUESTIN pIn = pInput;
   PVRAMHWREQUESTOUT pOut = pOutput;

   pOut->ulLength = sizeof(VRAMHWREQUESTOUT);
   pOut->ulFlags = 0;

   if (pIn->ulFlags & ENDIVE_REQUEST_HARDWARE)
   {
      if (RequestDriverSemaphore())
      {
         LogError(PMERR_RESOURCE_NOT_FOUND);
         return(DEVESC_ERROR);
      }

      if (fVRAMDeath)
      {
         /* No allocation will be allowed during death */
         /* PM is in background */
         LogError(PMERR_NOT_IN_A_PM_SESSION);
         ReleaseDriverSemaphore();
         return DEVESC_ERROR;
      }

      /* VRAM management must be initialized */
      if (!fVRAMInitialized)
      {
         /* Not initialized */
         LogError(PMERR_NO_HANDLE);
         ReleaseDriverSemaphore();
         return DEVESC_ERROR;
      }

      if (!fENDIVEDebug)
      {
         if (AcquireFB(0, pIn->pAcquireFB, FALSE) != DEV_OK)
         {
            /* Acquire failed */
            LogError(PMERR_RESOURCE_NOT_FOUND);
            ReleaseDriverSemaphore();
            return DEVESC_ERROR;
         }

         WaitQEmpty();
         WaitQIdle();

         if (!QueryFlags(pIn, pOut))
         {
            QueryStatic(pIn, pOut);
         }
      }
      else
      {
         if (!QueryFlags(pIn, pOut))
         {
            QueryStatic(pIn, pOut);
         }
      }
      /* DAC needs serialization */
      if (ulSerializeDAC & EXT_SERIALIZEDAC)
      {
         disable_cursor();
      }
   }
   else
   {
      /* VRAM management must be initialized */
      if (!fVRAMInitialized)
      {
         /* Not initialized */
         LogError(PMERR_NO_HANDLE);
         return DEVESC_ERROR;
      }

      /* DAC needs serialization */
      if (ulSerializeDAC & EXT_SERIALIZEDAC)
      {
         reenable_cursor();
      }
      DeacquireFB(FALSE);
      ReleaseDriverSemaphore();
   }
   return DEV_OK;
}

/*****************************************************************************
 *
 *  FUNCTION NAME:      ENDIVEAlloc()
 *
 *  DESCRIPTIVE NAME:
 *
 *  FUNCTION:
 *
 *  ENTRY POINT:
 *
 *  INPUT:
 *
 *  OUTPUT:
 *
 *  EXIT-NORMAL:
 *
 *  EXIT-ERROR:
 *
 *  EFFECTS:
 *
 *  NOTES:
 *
 *  INTERNAL REFERENCES:
 *    ROUTINES:
 *
 *  EXTERNAL REFERENCES:
 *    ROUTINES:
 *
 ****************************************************************************/

ULONG ENDIVEAlloc(PVOID pInput, PVOID pOutput)
{
   PVRAMALLOCIN pIn = pInput;
   PVRAMALLOCOUT pOut = pOutput;
   ULONG ulDelta;
   ULONG ulRc;

   /* Rectangle allocation is not implemented */
   if (pIn->ulFlags & ENDIVE_ALLOC_RECTANGLE)
   {
      return DEVESC_NOTIMPLEMENTED;
   }

   if (fVRAMDeath)
   {
      /* No allocation will be allowed during death */
      /* PM is in background */
      LogError(PMERR_NOT_IN_A_PM_SESSION);
      return DEVESC_ERROR;
   }

   /* VRAM management must be initialized */
   if (fVRAMInitialized)
   {
      pOut->ulLength = sizeof(VRAMALLOCOUT);
      pOut->ulFlags = 0;

      switch(pIn->ulFunction)
      {
         case VRAM_ALLOCATE:
            if ((pIn->ulHeight == 0) || (pIn->ulWidth == 0))
            {
               LogError(PMERR_INV_ESCAPE_DATA);
               return DEVESC_ERROR;
            }

            ulIDAssign++;
            pOut->ulID = ulIDAssign;

            /* DWORD align size and scan line size */
            pOut->ulScanLineBytes = pIn->ulSize / pIn->ulHeight;
            ulDelta = pOut->ulScanLineBytes % 4;
            if (ulDelta)
            {
               ulDelta = 4 - ulDelta;
               pOut->ulScanLineBytes += ulDelta;
               pIn->ulSize += ulDelta * pIn->ulHeight;
            }

            if (pIn->ulFlags & ENDIVE_ALLOC_SHARED)
            {
               /* Shared allocation */
               pOut->ptlStart = ptlShared;
               if (pIn->ulSize > ulSizeShared)
               {
                  if (!fVRAMSeamless)
                  {
                     ulRc = FindFree(pIn, pOut);
                     if (ulRc != DEV_OK)
                     {
                        pOut->ulSize = ulSizeShared;
                        pOut->ptlStart = ptlShared;
                        pOut->ulFlags = ENDIVE_ALLOC_WORKBUFFER;
                        return DEV_OK;
                     }
                     else
                     {
                        ulRc = AddStatic(pIn, pOut);
                        if (ulRc != DEV_OK)
                        {
                           ulIDAssign--;
                        }
                        return ulRc;
                     }
                  }
                  else
                  {
                     /* If requested size is greater than reserved, */
                     /* return work buffer */
                     pOut->ulSize = ulSizeShared;
                     pOut->ptlStart = ptlShared;
                     pOut->ulFlags = ENDIVE_ALLOC_WORKBUFFER;
                     return DEV_OK;
                  }
               }
               else
               {
                  /* Give requested size from reserved */
                  pOut->ulSize = pIn->ulSize;
                  return DEV_OK;
               }
            }
            else
            {
               /* Private request */
               if (fVRAMSeamless)
               {
                  /* When SEAMLESS is active, only requests from reserved */
                  /* can be honored */
                  if ((pIn->ulSize > ulSizePrivate) || (fPrivateInUse))
                  {
                     ulIDAssign--;
                     LogError(PMERR_RESOURCE_NOT_FOUND);
                     return DEVESC_ERROR;
                  }
                  else
                  {
                     fPrivateInUse = TRUE;
                     pOut->ptlStart = ptlPrivate;
                     pOut->ulSize = pIn->ulSize;
                     ulPrivateHandle = ulHandleAssign;
                  }
                  return DEV_OK;
               }
               else
               {
                  if ((!fPrivateInUse) && (pIn->ulFlags & ENDIVE_ALLOC_STATIC) && (ulSizePrivate))
                  {
                     fPrivateInUse = TRUE;
                     pOut->ptlStart = ptlPrivate;
                     pOut->ulSize = pIn->ulSize;
                     ulPrivateHandle = ulHandleAssign;
                     return DEV_OK;
                  }

                  if (AllocVRAM(pIn, pOut, ulIDAssign) != DEV_OK)
                  {
                     if ((pIn->ulSize > ulSizePrivate) || (fPrivateInUse))
                     {
                        ulIDAssign--;
                        LogError(PMERR_RESOURCE_NOT_FOUND);
                        return DEVESC_ERROR;
                     }
                     else
                     {
                        fPrivateInUse = TRUE;
                        pOut->ptlStart = ptlPrivate;
                        pOut->ulSize = pIn->ulSize;
                        ulPrivateHandle = ulHandleAssign;
                     }
                  }
                  return DEV_OK;
               }
            }
            break;

         case VRAM_DEALLOCATE:
            if (pIn->ulFlags & ENDIVE_ALLOC_SHARED)
            {
               return DEV_OK;
            }
            else
            {
               if ((fPrivateInUse) && (pIn->ulHandle == ulPrivateHandle))
               {
                  fPrivateInUse = FALSE;
                  ulPrivateHandle = 0;
                  return DEV_OK;
               }
               else
               {
                  return DeAllocVRAM(pIn);
               }
            }
            break;

         case VRAM_QUERY:
            return QueryVRAM(pIn, pOut);
            break;

         default:
            break;
      }
   }
   /* Not initialized */
   LogError(PMERR_NO_HANDLE);
   return DEVESC_ERROR;
}

/*****************************************************************************
 *
 *  FUNCTION NAME:      ENDIVEDeath()
 *
 *  DESCRIPTIVE NAME:
 *
 *  FUNCTION:
 *
 *  ENTRY POINT:
 *
 *  INPUT:
 *
 *  EXIT-NORMAL:
 *
 *  EXIT-ERROR:
 *
 *  EFFECTS:
 *
 *  NOTES:
 *
 *  INTERNAL REFERENCES:
 *    ROUTINES:
 *
 *  EXTERNAL REFERENCES:
 *    ROUTINES:
 *
 ****************************************************************************/

ULONG EXPENTRY ENDIVEDeath(BOOL *fDead, BOOL *f)
{
   ULONG ulRc;
   HWEXTENSIONIN HWExtIn;
   fVRAMDeath = *fDead;

   IgnoreParam(f);

   if (fVRAMInitialized)
   {
      if (ulAccelInUsePM)
      {
         ulRc = InitAccel();
      }

      if (fVRAMDeath)
      {
         SaveAllVRAM();
         HWExtIn.ulXSubFunction = ENDIVE_BACKGROUND;
      }
      else
      {
         RestoreAllVRAM();
         HWExtIn.ulXSubFunction = ENDIVE_FOREGROUND;
      }

      if ((ulAccelInUsePM) && (ulRc == DEV_OK))
      {
         /* Notify video accel driver */
         pHWEntry(0, GHI_CMD_EXTENSION, &HWExtIn, NULL);
      }
   }
   return DEV_OK;
}

/*****************************************************************************
 *
 *  FUNCTION NAME:      ENDIVESeamless()
 *
 *  DESCRIPTIVE NAME:
 *
 *  FUNCTION:
 *
 *  ENTRY POINT:
 *
 *  INPUT:
 *
 *  EXIT-NORMAL:
 *
 *  EXIT-ERROR:
 *
 *  EFFECTS:
 *
 *  NOTES:
 *
 *  INTERNAL REFERENCES:
 *    ROUTINES:
 *
 *  EXTERNAL REFERENCES:
 *    ROUTINES:
 *
 ****************************************************************************/

ULONG EXPENTRY ENDIVESeamless(BOOL *fSeamless, BOOL *f)
{
   IgnoreParam(f);

   /* Only save/restore VRAM upon first/last invocation of SEAMLESS */

   if (fVRAMSeamless == *fSeamless)
   {
      return DEV_OK;
   }

   fVRAMSeamless = *fSeamless;

   if (fVRAMInitialized)
   {
      if (fVRAMSeamless)
      {
         UpdateAllStatic();
         SaveVRAM();
      }
      else
      {
         SeamlessData.ulAccelInUse = 0;
         RestoreVRAM();
      }
   }
   return DEV_OK;
}

/*****************************************************************************
 *
 *  FUNCTION NAME:      InitializeVRAM()
 *
 *  DESCRIPTIVE NAME:
 *
 *  FUNCTION:
 *
 *  ENTRY POINT:
 *
 *  INPUT:
 *
 *  OUTPUT:
 *
 *  EXIT-NORMAL:
 *
 *  EXIT-ERROR:
 *
 *  EFFECTS:
 *
 *  NOTES:
 *
 *  INTERNAL REFERENCES:
 *    ROUTINES:
 *
 *  EXTERNAL REFERENCES:
 *    ROUTINES:
 *
 ****************************************************************************/

ULONG InitializeVRAM(PVRAMINIT pVRAMInit)
{
   ULONG ulRc;
   VRAMLIST pIn;

   /* Initialize VRAM */
   pIn.ptlStart = pVRAMInit->ptlStart;
   pIn.ulSize = pVRAMInit->ulSize;
   pIn.ulHandle = 0;
   pIn.ulID = 0;
   pIn.ulWidth = 0;
   pIn.ulHeight = 0;

   ulRc = AddLink(&pListHead, &pIn);
   fVRAMInitialized = TRUE;

   return ulRc;
}

/*****************************************************************************
 *
 *  FUNCTION NAME:      AllocVRAM()
 *
 *  DESCRIPTIVE NAME:
 *
 *  FUNCTION:
 *
 *  ENTRY POINT:
 *
 *  INPUT:
 *
 *  OUTPUT:
 *
 *  EXIT-NORMAL:
 *
 *  EXIT-ERROR:
 *
 *  EFFECTS:
 *
 *  NOTES:
 *
 *  INTERNAL REFERENCES:
 *    ROUTINES:
 *
 *  EXTERNAL REFERENCES:
 *    ROUTINES:
 *
 ****************************************************************************/

ULONG AllocVRAM(PVRAMALLOCIN pi, PVRAMALLOCOUT po, ULONG ulID)
{
   PVRAMLIST tmp = pListHead;
   VRAMLIST pIn;
   ULONG ulStart;

   if (!FindHandle(pi->ulHandle))
   {
      /* Not initialized */
      return DEVESC_ERROR;
   }

   /* Traverse the list searching for a free area >= size of allocation */
   while (tmp != 0)
   {
      /* Check only free area's */
      if (tmp->fUsed == FALSE)
      {
         /* If free area found >= size required, use it */
         if (tmp->ulSize >= pi->ulSize)
         {
            po->ptlStart = tmp->ptlStart;
            po->ulSize = pi->ulSize;
            tmp->fUsed = TRUE;
            tmp->ulID = ulID;
            tmp->ulHandle = pi->ulHandle;
            tmp->ulWidth = pi->ulWidth;
            tmp->ulHeight = pi->ulHeight;

            UpdateStatic(po->ptlStart);

            if (tmp->ulSize > pi->ulSize)
            {
               /* Add new link for remaining free area */
               pIn.ulSize = tmp->ulSize - pi->ulSize;
               ulStart = tmp->ptlStart.x + ((pi->ulSize % ulScanLineBytes) / (ulBPP / 8));
               pIn.ptlStart.x = ulStart % ulScanLine;
               pIn.ptlStart.y = tmp->ptlStart.y + pi->ulSize / ulScanLineBytes + ulStart / ulScanLine;
               pIn.ulHandle = 0;
               pIn.ulID = 0;
               pIn.ulWidth = 0;
               pIn.ulHeight = 0;
               tmp->ulSize = pi->ulSize;
               return AddLink(&tmp, &pIn);
            }
            return DEV_OK;
         }
      }
      tmp = tmp->next;
   }

   /* Cannot honor allocation request */
   return DEVESC_ERROR;
}

/*****************************************************************************
 *
 *  FUNCTION NAME:      DeAllocVRAM()
 *
 *  DESCRIPTIVE NAME:
 *
 *  FUNCTION:
 *
 *  ENTRY POINT:
 *
 *  INPUT:
 *
 *  OUTPUT:
 *
 *  EXIT-NORMAL:
 *
 *  EXIT-ERROR:
 *
 *  EFFECTS:
 *
 *  NOTES:
 *
 *  INTERNAL REFERENCES:
 *    ROUTINES:
 *
 *  EXTERNAL REFERENCES:
 *    ROUTINES:
 *
 ****************************************************************************/

ULONG DeAllocVRAM(PVRAMALLOCIN pi)
{
   PVRAMLIST tmp = pListHead;
   PVRAMLIST prev = 0;

   if (!FindHandle(pi->ulHandle))
   {
      /* Not registered */
      LogError(PMERR_NO_HANDLE);
      return DEVESC_ERROR;
   }

   /* Traverse linked list */
   while (tmp != 0)
   {
      /* Check for match in used list */
      if ((tmp->fUsed) &&
          (tmp->ulHandle == pi->ulHandle) &&
          (tmp->ulID == pi->ulID))
      {
         /* Mark link as free and consolidate free links */
         tmp->fUsed = FALSE;
         tmp->ulHandle = 0;
         tmp->ulID = 0;

         if (tmp->pSysMem)
         {
            FreeMem(tmp->pSysMem);
            tmp->pSysMem = 0;
         }

         if (prev)
         {
            MergeList(&prev, tmp);
            if (prev->next)
            {
               MergeList(&prev, prev->next);
            }
         }
         else
         {
            MergeList(&tmp, tmp->next);
         }
         return DEV_OK;
      }
      else
      {
         /* Not used, check next link */
         prev = tmp;
         tmp = tmp->next;
      }
   }

   /* Cannot dealloc if not allocated */
   LogError(PMERR_PARAMETER_OUT_OF_RANGE);
   return DEVESC_ERROR;
}

/*****************************************************************************
 *
 *  FUNCTION NAME:      QueryVRAM()
 *
 *  DESCRIPTIVE NAME:
 *
 *  FUNCTION:
 *
 *  ENTRY POINT:
 *
 *  INPUT:
 *
 *  OUTPUT:
 *
 *  EXIT-NORMAL:
 *
 *  EXIT-ERROR:
 *
 *  EFFECTS:
 *
 *  NOTES:
 *
 *  INTERNAL REFERENCES:
 *    ROUTINES:
 *
 *  EXTERNAL REFERENCES:
 *    ROUTINES:
 *
 ****************************************************************************/

ULONG QueryVRAM(PVRAMALLOCIN pi, PVRAMALLOCOUT po)
{
   PVRAMLIST tmp = pListHead;

   if (pi->ulFlags & ENDIVE_ALLOC_SHARED)
   {
      po->ulSize = ulSizeShared;
      po->ptlStart = ptlShared;
      return DEV_OK;
   }

   if (pi->ulFlags & ENDIVE_ALLOC_STATIC)
   {
      if (fPrivateInUse)
      {
         po->ulSize = 0;
      }
      else
      {
         po->ulSize = ulSizePrivate;
      }
      po->ptlStart = ptlPrivate;
      return DEV_OK;
   }

   po->ulSize = 0;

   /* Traverse the list searching for the largest free area */
   while (tmp != 0)
   {
      /* Check only free area's */
      if (tmp->fUsed == FALSE)
      {
         /* Find largest free area */
         if (tmp->ulSize > po->ulSize)
         {
            po->ulSize = tmp->ulSize;
            po->ptlStart = tmp->ptlStart;
         }
      }
      tmp = tmp->next;
   }

   if ((!fPrivateInUse) && (po->ulSize < ulSizePrivate))
   {
      po->ulSize = ulSizePrivate;
      po->ptlStart = ptlPrivate;
   }
   return DEV_OK;
}

/*****************************************************************************
 *
 *  FUNCTION NAME:      FindFree()
 *
 *  DESCRIPTIVE NAME:
 *
 *  FUNCTION:
 *
 *  ENTRY POINT:
 *
 *  INPUT:
 *
 *  OUTPUT:
 *
 *  EXIT-NORMAL:
 *
 *  EXIT-ERROR:
 *
 *  EFFECTS:
 *
 *  NOTES:
 *
 *  INTERNAL REFERENCES:
 *    ROUTINES:
 *
 *  EXTERNAL REFERENCES:
 *    ROUTINES:
 *
 ****************************************************************************/

ULONG FindFree(PVRAMALLOCIN pi, PVRAMALLOCOUT po)
{
   PVRAMLIST tmp = pListHead;

   /* Traverse the list searching for a free area >= size of allocation */
   while (tmp != 0)
   {
      /* Check only free area's */
      if (tmp->fUsed == FALSE)
      {
         /* If free area found >= size required, use it */
         if (tmp->ulSize >= pi->ulSize)
         {
            po->ptlStart = tmp->ptlStart;
            po->ulSize = pi->ulSize;
            return DEV_OK;
         }
      }
      tmp = tmp->next;
   }

   /* Cannot find free space */
   return DEVESC_ERROR;
}

/*****************************************************************************
 *
 *  FUNCTION NAME:      QueryFlags()
 *
 *  DESCRIPTIVE NAME:
 *
 *  FUNCTION:
 *
 *  ENTRY POINT:
 *
 *  INPUT:
 *
 *  OUTPUT:
 *
 *  EXIT-NORMAL:
 *
 *  EXIT-ERROR:
 *
 *  EFFECTS:
 *
 *  NOTES:
 *
 *  INTERNAL REFERENCES:
 *    ROUTINES:
 *
 *  EXTERNAL REFERENCES:
 *    ROUTINES:
 *
 ****************************************************************************/

BOOL QueryFlags(PVRAMHWREQUESTIN pIn, PVRAMHWREQUESTOUT pOut)
{
   PVRAMLIST tmp = pListHead;
   ULONG ulDelta;

   /* Traverse the list searching for a match of handle */
   while (tmp != 0)
   {
      if ((tmp->ulHandle == pIn->ulHandle) && (tmp->ulID == pIn->ulID))
      {
         if (tmp->ulFlags & ENDIVE_REQUEST_RELOCATED)
         {
            /* DWORD align size and scan line size */
            pOut->ulScanLineBytes = tmp->ulSize / tmp->ulHeight;
            ulDelta = pOut->ulScanLineBytes % 4;
            if (ulDelta)
            {
               ulDelta = 4 - ulDelta;
               pOut->ulScanLineBytes += ulDelta;
            }

            tmp->ulFlags &= ~ENDIVE_REQUEST_RELOCATED;
            pOut->pSysMem = tmp->pSysMem;

            /* Return shared area info */
            if (tmp->pSysMem)
            {
               /* Work buffer */
               pOut->ptlStart = ptlShared;
               pOut->ulSize = ulSizeShared;
            }
            else
            {
               pOut->ptlStart = tmp->ptlStart;
               pOut->ulSize = tmp->ulSize;
            }

            pOut->ulFlags |= ENDIVE_REQUEST_RELOCATED;
         }
         if (tmp->ulFlags & ENDIVE_REQUEST_UNAVAILABLE)
         {
            tmp->ulFlags &= ~ENDIVE_REQUEST_UNAVAILABLE;
            pOut->ulFlags |= ENDIVE_REQUEST_UNAVAILABLE;
         }
         return TRUE;
      }
      tmp = tmp->next;
   }
   return FALSE;
}

/*****************************************************************************
 *
 *  FUNCTION NAME:      FreeHandle()
 *
 *  DESCRIPTIVE NAME:
 *
 *  FUNCTION:
 *
 *  ENTRY POINT:
 *
 *  INPUT:
 *
 *  OUTPUT:
 *
 *  EXIT-NORMAL:
 *
 *  EXIT-ERROR:
 *
 *  EFFECTS:
 *
 *  NOTES:
 *
 *  INTERNAL REFERENCES:
 *    ROUTINES:
 *
 *  EXTERNAL REFERENCES:
 *    ROUTINES:
 *
 ****************************************************************************/

VOID FreeHandle(ULONG ulHandle)
{
   PVRAMLIST tmp = pListHead;
   PVRAMLIST prev = 0;

   /* Delete all handles */
   DeleteHandle(ulHandle, TRUE);
   DeleteStatic(ulHandle, TRUE);

   /* Traverse linked list */
   while (tmp != 0)
   {
      if (tmp->ulHandle != ulHandle)
      {
         prev = tmp;
         tmp = tmp->next;
      }
      else
      {
         /* Delete first link */
         if (prev == 0)
         {
            pListHead = tmp->next;
         }
         else
         {
            prev->next = tmp->next;
         }

         FreeMem(tmp);

         if (prev != 0)
         {
            tmp = prev->next;
         }
         else
         {
            tmp = pListHead;
         }
      }
   }
}

/*****************************************************************************
 *
 *  FUNCTION NAME:      FreeList()
 *
 *  DESCRIPTIVE NAME:
 *
 *  FUNCTION:
 *
 *  ENTRY POINT:
 *
 *  INPUT:
 *
 *  OUTPUT:
 *
 *  EXIT-NORMAL:
 *
 *  EXIT-ERROR:
 *
 *  EFFECTS:
 *
 *  NOTES:
 *
 *  INTERNAL REFERENCES:
 *    ROUTINES:
 *
 *  EXTERNAL REFERENCES:
 *    ROUTINES:
 *
 ****************************************************************************/

VOID FreeList(VOID)
{
   PVRAMLIST tmp = pListHead;

   /* Free entire list */
   while (tmp != 0)
   {
      if (tmp->pSysMem)
      {
         FreeMem(tmp->pSysMem);
      }
      pListHead = tmp->next;
      FreeMem(tmp);
      tmp = pListHead;
   }
   pListHead = 0;
}

/*****************************************************************************
 *
 *  FUNCTION NAME:      MergeList()
 *
 *  DESCRIPTIVE NAME:
 *
 *  FUNCTION:
 *
 *  ENTRY POINT:
 *
 *  INPUT:
 *
 *  EXIT-NORMAL:
 *
 *  EXIT-ERROR:
 *
 *  EFFECTS:
 *
 *  NOTES:
 *
 *  INTERNAL REFERENCES:
 *    ROUTINES:
 *
 *  EXTERNAL REFERENCES:
 *    ROUTINES:
 *
 ****************************************************************************/

VOID MergeList(PVRAMLIST *first, PVRAMLIST second)
{
   if ((*first)->fUsed == FALSE)
   {
      if (second)
      {
         (*first)->next = second->next;
         (*first)->ulSize += second->ulSize;
         FreeMem(second);
      }
   }
}

/*****************************************************************************
 *
 *  FUNCTION NAME:      AddLink()
 *
 *  DESCRIPTIVE NAME:
 *
 *  FUNCTION:
 *
 *  ENTRY POINT:
 *
 *  INPUT:
 *
 *  EXIT-NORMAL:
 *
 *  EXIT-ERROR:
 *
 *  EFFECTS:
 *
 *  NOTES:
 *
 *  INTERNAL REFERENCES:
 *    ROUTINES:
 *
 *  EXTERNAL REFERENCES:
 *    ROUTINES:
 *
 ****************************************************************************/

ULONG AddLink(PVRAMLIST *current, PVRAMLIST pi)
{
   PVRAMLIST p;

   /* Add new link */
   if (!(p = (PVRAMLIST)AllocMem(sizeof(VRAMLIST))))
   {
      LogError(PMERR_HEAP_OUT_OF_MEMORY);
      return DEVESC_ERROR;
   }

   if (*current)
   {
      /* Insert after current */
      p->next = (*current)->next;
      (*current)->next = p;
   }
   else
   {
      p->next = 0;
      *current = p;
   }

   /* Initialize with data */
   p->ptlStart = pi->ptlStart;
   p->ulSize = pi->ulSize;
   p->ulHandle = pi->ulHandle;
   p->ulID = pi->ulID;
   p->fUsed = FALSE;
   p->ulFlags = 0;
   p->pSysMem = 0;

   return DEV_OK;
}

/*****************************************************************************
 *
 *  FUNCTION NAME:      AddHandle()
 *
 *  DESCRIPTIVE NAME:
 *
 *  FUNCTION:
 *
 *  ENTRY POINT:
 *
 *  INPUT:
 *
 *  EXIT-NORMAL:
 *
 *  EXIT-ERROR:
 *
 *  EFFECTS:
 *
 *  NOTES:
 *
 *  INTERNAL REFERENCES:
 *    ROUTINES:
 *
 *  EXTERNAL REFERENCES:
 *    ROUTINES:
 *
 ****************************************************************************/

ULONG AddHandle(ULONG ulHandle, ULONG ulFlags)
{
   PHANDLELIST p;

   /* Add new link */
   if (!(p = (PHANDLELIST)AllocMem(sizeof(HANDLELIST))))
   {
      LogError(PMERR_HEAP_OUT_OF_MEMORY);
      return DEVESC_ERROR;
   }

   /* Initialize with data */
   p->ulHandle = ulHandle;
   p->ulFlags = ulFlags;

   /* Add link to list */
   if (pHandleListHead == 0)
   {
      p->next = 0;
      pHandleListHead = p;
   }
   else
   {
      p->next = pHandleListHead;
      pHandleListHead = p;
   }
   return DEV_OK;
}

/*****************************************************************************
 *
 *  FUNCTION NAME:      FindHandle()
 *
 *  DESCRIPTIVE NAME:
 *
 *  FUNCTION:
 *
 *  ENTRY POINT:
 *
 *  INPUT:
 *
 *  OUTPUT:
 *
 *  EXIT-NORMAL:
 *
 *  EXIT-ERROR:
 *
 *  EFFECTS:
 *
 *  NOTES:
 *
 *  INTERNAL REFERENCES:
 *    ROUTINES:
 *
 *  EXTERNAL REFERENCES:
 *    ROUTINES:
 *
 ****************************************************************************/

BOOL FindHandle(ULONG ulHandle)
{
   PHANDLELIST tmp = pHandleListHead;

   /* Traverse the list searching for a match of handle */
   while (tmp != 0)
   {
      if (tmp->ulHandle == ulHandle)
      {
         return TRUE;
      }
      tmp = tmp->next;
   }
   return FALSE;
}

/*****************************************************************************
 *
 *  FUNCTION NAME:      DeleteHandle()
 *
 *  DESCRIPTIVE NAME:
 *
 *  FUNCTION:
 *
 *  ENTRY POINT:
 *
 *  INPUT:
 *
 *  EXIT-NORMAL:
 *
 *  EXIT-ERROR:
 *
 *  EFFECTS:
 *
 *  NOTES:
 *
 *  INTERNAL REFERENCES:
 *    ROUTINES:
 *
 *  EXTERNAL REFERENCES:
 *    ROUTINES:
 *
 ****************************************************************************/

VOID DeleteHandle(ULONG ulHandle, BOOL fAll)
{
   HANDLELIST *tmp  = pHandleListHead;
   HANDLELIST *prev = 0;

   /* Traverse linked list */
   while (tmp != 0)
   {
      if (tmp->ulHandle != ulHandle)
      {
         prev = tmp;
         tmp = tmp->next;
      }
      else
      {
         /* Delete first link */
         if (prev == 0)
         {
            pHandleListHead = tmp->next;
         }
         else
         {
            prev->next = tmp->next;
         }

         FreeMem(tmp);

         if (prev != 0)
         {
            tmp = prev->next;
         }
         else
         {
            tmp = pHandleListHead;
         }
         if (!fAll)
         {
            return;
         }
      }
   }
}

/*****************************************************************************
 *
 *  FUNCTION NAME:      AccelInUse()
 *
 *  DESCRIPTIVE NAME:
 *
 *  FUNCTION:
 *
 *  ENTRY POINT:
 *
 *  INPUT:
 *
 *  OUTPUT:
 *
 *  EXIT-NORMAL:
 *
 *  EXIT-ERROR:
 *
 *  EFFECTS:
 *
 *  NOTES:
 *
 *  INTERNAL REFERENCES:
 *    ROUTINES:
 *
 *  EXTERNAL REFERENCES:
 *    ROUTINES:
 *
 ****************************************************************************/

BOOL AccelInUse(VOID)
{
   PHANDLELIST tmp = pHandleListHead;

   /* Traverse the list searching for accelerator in use */
   while (tmp != 0)
   {
      if (!(tmp->ulFlags & ENDIVE_REGISTER_VRAMONLY))
      {
         return TRUE;
      }
      tmp = tmp->next;
   }
   return FALSE;
}

/*****************************************************************************
 *
 *  FUNCTION NAME:      AddStatic()
 *
 *  DESCRIPTIVE NAME:
 *
 *  FUNCTION:
 *
 *  ENTRY POINT:
 *
 *  INPUT:
 *
 *  EXIT-NORMAL:
 *
 *  EXIT-ERROR:
 *
 *  EFFECTS:
 *
 *  NOTES:
 *
 *  INTERNAL REFERENCES:
 *    ROUTINES:
 *
 *  EXTERNAL REFERENCES:
 *    ROUTINES:
 *
 ****************************************************************************/

ULONG AddStatic(PVRAMALLOCIN pi, PVRAMALLOCOUT po)
{
   PSTATICLIST p;

   /* Add new link */
   if (!(p = (PSTATICLIST)AllocMem(sizeof(STATICLIST))))
   {
      LogError(PMERR_HEAP_OUT_OF_MEMORY);
      return DEVESC_ERROR;
   }

   /* Initialize with data */
   p->ulHandle = pi->ulHandle;
   p->ulID = po->ulID;
   p->ptlStart = po->ptlStart;
   p->ulSize = po->ulSize;
   p->ulFlags = 0;

   /* Add link to list */
   if (pStaticListHead == 0)
   {
      p->next = 0;
      pStaticListHead = p;
   }
   else
   {
      p->next = pStaticListHead;
      pStaticListHead = p;
   }
   return DEV_OK;
}

/*****************************************************************************
 *
 *  FUNCTION NAME:      QueryStatic()
 *
 *  DESCRIPTIVE NAME:
 *
 *  FUNCTION:
 *
 *  ENTRY POINT:
 *
 *  INPUT:
 *
 *  OUTPUT:
 *
 *  EXIT-NORMAL:
 *
 *  EXIT-ERROR:
 *
 *  EFFECTS:
 *
 *  NOTES:
 *
 *  INTERNAL REFERENCES:
 *    ROUTINES:
 *
 *  EXTERNAL REFERENCES:
 *    ROUTINES:
 *
 ****************************************************************************/

VOID QueryStatic(PVRAMHWREQUESTIN pIn, PVRAMHWREQUESTOUT pOut)
{
   PSTATICLIST tmp = pStaticListHead;

   /* Traverse the list searching for a match of handle */
   while (tmp != 0)
   {
      if ((tmp->ulHandle == pIn->ulHandle) && (tmp->ulID == pIn->ulID))
      {
         if (tmp->ulFlags & ENDIVE_REQUEST_RELOCATED)
         {
            pOut->ulFlags |= ENDIVE_REQUEST_RELOCATED;
            pOut->ptlStart = ptlShared;
            pOut->ulSize = ulSizeShared;
            FreeMem(tmp);
         }
         return;
      }
      tmp = tmp->next;
   }
}

/*****************************************************************************
 *
 *  FUNCTION NAME:      UpdateStatic()
 *
 *  DESCRIPTIVE NAME:
 *
 *  FUNCTION:
 *
 *  ENTRY POINT:
 *
 *  INPUT:
 *
 *  OUTPUT:
 *
 *  EXIT-NORMAL:
 *
 *  EXIT-ERROR:
 *
 *  EFFECTS:
 *
 *  NOTES:
 *
 *  INTERNAL REFERENCES:
 *    ROUTINES:
 *
 *  EXTERNAL REFERENCES:
 *    ROUTINES:
 *
 ****************************************************************************/

VOID UpdateStatic(POINTL ptl)
{
   PSTATICLIST tmp = pStaticListHead;

   while (tmp != 0)
   {
      if ((tmp->ptlStart.x == ptl.x) && (tmp->ptlStart.y == ptl.y))
      {
         tmp->ulFlags |= ENDIVE_REQUEST_RELOCATED;
         return;
      }
      tmp = tmp->next;
   }
}

/*****************************************************************************
 *
 *  FUNCTION NAME:      UpdateAllStatic()
 *
 *  DESCRIPTIVE NAME:
 *
 *  FUNCTION:
 *
 *  ENTRY POINT:
 *
 *  INPUT:
 *
 *  OUTPUT:
 *
 *  EXIT-NORMAL:
 *
 *  EXIT-ERROR:
 *
 *  EFFECTS:
 *
 *  NOTES:
 *
 *  INTERNAL REFERENCES:
 *    ROUTINES:
 *
 *  EXTERNAL REFERENCES:
 *    ROUTINES:
 *
 ****************************************************************************/

VOID UpdateAllStatic(VOID)
{
   PSTATICLIST tmp = pStaticListHead;

   while (tmp != 0)
   {
      tmp->ulFlags |= ENDIVE_REQUEST_RELOCATED;
      tmp = tmp->next;
   }
}

/*****************************************************************************
 *
 *  FUNCTION NAME:      DeleteStatic()
 *
 *  DESCRIPTIVE NAME:
 *
 *  FUNCTION:
 *
 *  ENTRY POINT:
 *
 *  INPUT:
 *
 *  EXIT-NORMAL:
 *
 *  EXIT-ERROR:
 *
 *  EFFECTS:
 *
 *  NOTES:
 *
 *  INTERNAL REFERENCES:
 *    ROUTINES:
 *
 *  EXTERNAL REFERENCES:
 *    ROUTINES:
 *
 ****************************************************************************/

VOID DeleteStatic(ULONG ulHandle, BOOL fAll)
{
   STATICLIST *tmp  = pStaticListHead;
   STATICLIST *prev = 0;

   /* Traverse linked list */
   while (tmp != 0)
   {
      if (tmp->ulHandle != ulHandle)
      {
         prev = tmp;
         tmp = tmp->next;
      }
      else
      {
         /* Delete first link */
         if (prev == 0)
         {
            pStaticListHead = tmp->next;
         }
         else
         {
            prev->next = tmp->next;
         }

         FreeMem(tmp);

         if (prev != 0)
         {
            tmp = prev->next;
         }
         else
         {
            tmp = pStaticListHead;
         }
         if (!fAll)
         {
            return;
         }
      }
   }
}

/*****************************************************************************
 *
 *  FUNCTION NAME:    AllocMem()
 *
 *  DESCRIPTIVE NAME:   Allocate piece of memory of size requested. Uses
 *                      PMGRE SSAllocMem() to perform the function and
 *                      is callable at rings 2 and 3.
 *
 *  FUNCTION:
 *
 *  ENTRY POINT:
 *
 *  INPUT:
 *
 *  EXIT-NORMAL:
 *
 *  EXIT-ERROR:
 *
 *  EFFECTS:
 *
 *  NOTES:
 *
 *  INTERNAL REFERENCES:
 *    ROUTINES:
 *
 *  EXTERNAL REFERENCES:
 *    ROUTINES:
 *
 ****************************************************************************/

PVOID AllocMem(ULONG ulSize)
{
   ULONG ulRc;
   PVOID pMemory = NULL;

   /* Round up to multiple of 8 bytes */
   ulSize = (ulSize + 7) & 0xFFFFFFF8;

   do
   {
      ulRc = SSAllocMem(&pMemory, ulSize, PAG_COMMIT | PAG_READ | PAG_WRITE);
   }
   while (ulRc == ERROR_INTERRUPT);

   return(pMemory);
}

/*****************************************************************************
 *
 *  FUNCTION NAME:    FreeMem()
 *
 *  DESCRIPTIVE NAME: Frees memory allocated with AllocMem
 *
 *  FUNCTION:
 *
 *  ENTRY POINT:
 *
 *  INPUT:
 *
 *  EXIT-NORMAL:
 *
 *  EXIT-ERROR:
 *
 *  EFFECTS:
 *
 *  NOTES:
 *
 *  INTERNAL REFERENCES:
 *    ROUTINES:
 *
 *  EXTERNAL REFERENCES:
 *    ROUTINES:
 *
 ****************************************************************************/

ULONG FreeMem(PVOID pMemory)
{
   ULONG ulRc;

   do
   {
      ulRc = SSFreeMem(pMemory);
   }
   while (ulRc == ERROR_INTERRUPT);

   return(ulRc);
}

/*****************************************************************************
 *
 *  FUNCTION NAME:    GetFBInfo()
 *
 *  DESCRIPTIVE NAME:
 *
 *  FUNCTION:
 *
 *  ENTRY POINT:
 *
 *  INPUT:
 *
 *  EXIT-NORMAL:
 *
 *  EXIT-ERROR:
 *
 *  EFFECTS:
 *
 *  NOTES:
 *
 *  INTERNAL REFERENCES:
 *    ROUTINES:
 *
 *  EXTERNAL REFERENCES:
 *    ROUTINES:
 *
 ****************************************************************************/

VOID GetFBInfo(VOID)
{
   PHWMAP pVRAMHWMap;

   pVRAMHWMap = &(aResTable[HWResolution]);
   FBInfo.ulLength = sizeof(FBINFO);
   FBInfo.ulXres = pVRAMHWMap->vis_width;
   FBInfo.ulYres = pVRAMHWMap->vis_height;
   FBInfo.ulBPP = pVRAMHWMap->BitCount;
   ulScanLineBytes = pVRAMHWMap->ulScanLineBytes;
   FBInfo.ulScanLineBytes = ulScanLineBytes;
   FBInfo.ulFlags = FB_SUPPORTSVRAMALLOC;
   FBInfo.ulENDIVEDrivers = 1;

   switch(FBInfo.ulBPP)
   {
      case 8:
         FBInfo.fccColorEncoding = FOURCC_LUT8;
         break;

      case 16:
         FBInfo.fccColorEncoding = FOURCC_R565;
         break;

      case 24:
         if (DDT.fScreenFlags & USE_ATTDAC)
         {
            FBInfo.fccColorEncoding = FOURCC_BGR3;
         }
         else
         {
            FBInfo.fccColorEncoding = FOURCC_RGB3;
         }
         break;

      default:
         FBInfo.fccColorEncoding = 0;
         break;
   }
}

/*****************************************************************************
 *
 *  FUNCTION NAME:    InitAccel()
 *
 *  DESCRIPTIVE NAME:
 *
 *  FUNCTION:
 *
 *  ENTRY POINT:
 *
 *  INPUT:
 *
 *  EXIT-NORMAL:
 *
 *  EXIT-ERROR:
 *
 *  EFFECTS:
 *
 *  NOTES:
 *
 *  INTERNAL REFERENCES:
 *    ROUTINES:
 *
 *  EXTERNAL REFERENCES:
 *    ROUTINES:
 *
 ****************************************************************************/

ULONG InitAccel(VOID)
{
   HWEXTENSIONIN HWExtIn;
   HWEXTENSIONOUT HWExtOut;

   /* Transistion from ring 2 to ring 3 for DosLoadModule */
   (VOID)DMS32CallBack((PFN)LoadAccel);
   if (!hMod)
   {
      /* Cannot load DLL */
      LogError(PMERR_LIBRARY_LOAD_FAILED);
      return DEVESC_ERROR;
   }

   GetFBInfo();

   HWExtIn.ulXSubFunction = ENDIVE_INIT;
   HWExtIn.pXP1 = &FBInfo;
   if (pHWEntry(0, GHI_CMD_EXTENSION, &HWExtIn, &HWExtOut))
   {
      (VOID)DMS32CallBack((PFN)UnLoadAccel);
      /* DLL failed initialization */
      LogError(PMERR_LIBRARY_LOAD_FAILED);
      return DEVESC_ERROR;
   }
   if (HWExtOut.ulFlags & EXT_SERIALIZEDAC)
   {
      ulSerializeDAC = EXT_SERIALIZEDAC;
   }

   return DEV_OK;
}

/*****************************************************************************
 *
 *  FUNCTION NAME:    LoadAccel()
 *
 *  DESCRIPTIVE NAME:
 *
 *  FUNCTION:
 *
 *  ENTRY POINT:
 *
 *  INPUT:
 *
 *  EXIT-NORMAL:
 *
 *  EXIT-ERROR:
 *
 *  EFFECTS:
 *
 *  NOTES:
 *
 *  INTERNAL REFERENCES:
 *    ROUTINES:
 *
 *  EXTERNAL REFERENCES:
 *    ROUTINES:
 *
 ****************************************************************************/

VOID LoadAccel(VOID)
{
   if (fInit == FALSE)
   {
      /* Get DLL name from INI file */
      if (!(PrfQueryProfileString(HINI_PROFILE,
                                  APP_NAME,
                                  KEY_NAME,
                                  "",
                                  (PVOID)szName,
                                  NAME_SIZE)))
      {
         return;
      }
      fInit = TRUE;
   }

   /* Initialize Accelerator DLL */
   if (DosLoadModule(NULL, 0, szName, &hMod))
   {
      hMod = 0;
      return;
   }

   if (DosQueryProcAddr(hMod, 0, "HWEntry", (PFN*)&pHWEntry))
   {
      DosFreeModule(hMod);
      hMod = 0;
   }
}

/*****************************************************************************
 *
 *  FUNCTION NAME:    UnLoadAccel()
 *
 *  DESCRIPTIVE NAME:
 *
 *  FUNCTION:
 *
 *  ENTRY POINT:
 *
 *  INPUT:
 *
 *  EXIT-NORMAL:
 *
 *  EXIT-ERROR:
 *
 *  EFFECTS:
 *
 *  NOTES:
 *
 *  INTERNAL REFERENCES:
 *    ROUTINES:
 *
 *  EXTERNAL REFERENCES:
 *    ROUTINES:
 *
 ****************************************************************************/

VOID UnLoadAccel(VOID)
{
   DosFreeModule(hMod);
   hMod = 0;
   fInit = FALSE;
}

/*****************************************************************************
 *
 *  FUNCTION NAME:    SaveVRAM()
 *
 *  DESCRIPTIVE NAME:
 *
 *  FUNCTION:
 *
 *  ENTRY POINT:
 *
 *  INPUT:
 *
 *  EXIT-NORMAL:
 *
 *  EXIT-ERROR:
 *
 *  EFFECTS:
 *
 *  NOTES:
 *
 *  INTERNAL REFERENCES:
 *    ROUTINES:
 *
 *  EXTERNAL REFERENCES:
 *    ROUTINES:
 *
 ****************************************************************************/

VOID SaveVRAM(VOID)
{
   PVRAMLIST tmp = pListHead;

   if (fVRAMDeath)
   {
      return;
   }

   while (tmp != 0)
   {
      if (tmp->fUsed)
      {
         /* Allocate system memory to copy VRAM into */
         if (tmp->pSysMem = AllocMem(tmp->ulSize))
         {
            /* Mark allocation as relocated */
            tmp->ulFlags |= ENDIVE_REQUEST_RELOCATED;

            CopyVRAM(TRUE, tmp);
         }
         else
         {
            /* Cannot allocate system memory - mark as unavailable */
            tmp->ulFlags |= ENDIVE_REQUEST_UNAVAILABLE;
         }
      }
      tmp = tmp->next;
   }
}

/*****************************************************************************
 *
 *  FUNCTION NAME:    RestoreVRAM()
 *
 *  DESCRIPTIVE NAME:
 *
 *  FUNCTION:
 *
 *  ENTRY POINT:
 *
 *  INPUT:
 *
 *  EXIT-NORMAL:
 *
 *  EXIT-ERROR:
 *
 *  EFFECTS:
 *
 *  NOTES:
 *
 *  INTERNAL REFERENCES:
 *    ROUTINES:
 *
 *  EXTERNAL REFERENCES:
 *    ROUTINES:
 *
 ****************************************************************************/

VOID RestoreVRAM(VOID)
{
   PVRAMLIST tmp = pListHead;

   while (tmp != 0)
   {
      if (tmp->fUsed)
      {
         /* Mark allocation as relocated */
         tmp->ulFlags |= ENDIVE_REQUEST_RELOCATED;

         CopyVRAM(FALSE, tmp);

         /* Free allocated system memory */
         FreeMem(tmp->pSysMem);
         tmp->pSysMem = 0;
      }
      tmp = tmp->next;
   }
}

/*****************************************************************************
 *
 *  FUNCTION NAME:    CopyVRAM()
 *
 *  DESCRIPTIVE NAME:
 *
 *  FUNCTION:
 *
 *  ENTRY POINT:
 *
 *  INPUT:
 *
 *  EXIT-NORMAL:
 *
 *  EXIT-ERROR:
 *
 *  EFFECTS:
 *
 *  NOTES:
 *
 *  INTERNAL REFERENCES:
 *    ROUTINES:
 *
 *  EXTERNAL REFERENCES:
 *    ROUTINES:
 *
 ****************************************************************************/

VOID CopyVRAM(BOOL fSave, PVRAMLIST tmp)
{
   ULONG ulVRAM;
   ULONG ulWidth;
   ULONG ulHeight;
   ULONG ulSize;
   ULONG ulStart;
   ULONG ulOffset;
   ULONG ulPels;

   /* Cases to consider:                                           */
   /*   1: Allocation start x not 0 and end x not scanline boundry */
   /*   2: Allocation start x not 0 and end x is scanline boundry  */
   /*   3: Allocation size multiple of scanline size               */
   /*   4: Allocation start x is 0 and end x not scanline boundry  */
   /*   NOTE: cases 2, 3 and 4 can apply to same allocation        */

   ulSize = tmp->ulSize;
   ulStart = tmp->ptlStart.y;
   ulOffset = 0;

   if (tmp->ptlStart.x)
   {
      ulVRAM = (tmp->ptlStart.x << 16) + tmp->ptlStart.y;
      ulHeight = 0;
      ulPels = (ulSize + 1) / (ulBPP / 8);

      if ((ulPels + tmp->ptlStart.x) < ulScanLine)
      {
         /* Case 1 - Copy starting partial, ending partial scanline */
         ulWidth = ulPels;
         ulSize = 0;
      }
      else
      {
         /* Case 2 - Copy starting partial scaneline */
         ulWidth = ulScanLine - tmp->ptlStart.x;
         ulOffset = ulWidth * (ulBPP / 8);
         ulSize -= ulOffset;
         ulStart++;
      }
      if (fSave)
      {
         CopyVRAMToMemory(tmp->pSysMem, ulVRAM, ulWidth - 1, ulHeight, ulBPPHW);
      }
      else
      {
         CopyMemoryToVRAM(tmp->pSysMem, ulVRAM, ulWidth - 1, ulHeight, ulBPPHW);
      }
   }

   if (ulSize >= ulScanLineBytes)
   {
      /* Case 3 - Copy full scanelines */
      ulVRAM = ulStart;
      ulHeight = ulSize / ulScanLineBytes;
      ulWidth = ulScanLine;
      ulSize -= ulScanLineBytes * ulHeight;
      ulOffset += ulSize;
      ulStart += ulHeight;
      if (fSave)
      {
         CopyVRAMToMemory((PBYTE)(tmp->pSysMem) + ulOffset, ulVRAM, ulWidth - 1, ulHeight - 1, ulBPPHW);
      }
      else
      {
         CopyMemoryToVRAM((PBYTE)(tmp->pSysMem) + ulOffset, ulVRAM, ulWidth - 1, ulHeight - 1, ulBPPHW);
      }
   }

   if (ulSize)
   {
      /* Case 4 - Copy ending partial scaneline */
      ulVRAM = ulStart;
      ulHeight = 0;
      ulWidth = (ulSize + 1) / (ulBPP / 8);
      if (fSave)
      {
         CopyVRAMToMemory((PBYTE)(tmp->pSysMem) + ulOffset, ulVRAM, ulWidth - 1, ulHeight, ulBPPHW);
      }
      else
      {
         CopyMemoryToVRAM((PBYTE)(tmp->pSysMem) + ulOffset, ulVRAM, ulWidth - 1, ulHeight, ulBPPHW);
      }
   }
}

/*****************************************************************************
 *
 *  FUNCTION NAME:    SaveAllVRAM()
 *
 *  DESCRIPTIVE NAME:
 *
 *  FUNCTION:
 *
 *  ENTRY POINT:
 *
 *  INPUT:
 *
 *  EXIT-NORMAL:
 *
 *  EXIT-ERROR:
 *
 *  EFFECTS:
 *
 *  NOTES:
 *
 *  INTERNAL REFERENCES:
 *    ROUTINES:
 *
 *  EXTERNAL REFERENCES:
 *    ROUTINES:
 *
 ****************************************************************************/

VOID SaveAllVRAM(VOID)
{
   ULONG ulVRAM;
   ULONG ulHeight;
   ULONG ulSize;

   if (fVRAMSeamless)
   {
      return;
   }

   if (fVRAMInitialized)
   {
      ulHeight = ptlShared.y - ptlPrivate.y;
      ulSize = ulScanLineBytes * ulHeight;
      ulVRAM = ptlPrivate.y;

      if (pSysMemDeath = AllocMem(ulSize))
      {
         CopyVRAMToMemory(pSysMemDeath, ulVRAM, ulScanLine - 1, ulHeight - 1, ulBPPHW);
      }
   }
}

/*****************************************************************************
 *
 *  FUNCTION NAME:    RestoreAllVRAM()
 *
 *  DESCRIPTIVE NAME:
 *
 *  FUNCTION:
 *
 *  ENTRY POINT:
 *
 *  INPUT:
 *
 *  EXIT-NORMAL:
 *
 *  EXIT-ERROR:
 *
 *  EFFECTS:
 *
 *  NOTES:
 *
 *  INTERNAL REFERENCES:
 *    ROUTINES:
 *
 *  EXTERNAL REFERENCES:
 *    ROUTINES:
 *
 ****************************************************************************/

VOID RestoreAllVRAM(VOID)
{
   ULONG ulVRAM;
   ULONG ulHeight;

   if (pSysMemDeath)
   {
      /* Copy memory to VRAM */
      ulHeight = ptlShared.y - ptlPrivate.y;
      ulVRAM = ptlPrivate.y;

      CopyMemoryToVRAM(pSysMemDeath, ulVRAM, ulScanLine - 1, ulHeight - 1, ulBPPHW);

      /* Free allocated system memory */
      FreeMem(pSysMemDeath);
      pSysMemDeath = 0;
   }
}

#endif
