/*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 = VVPAGE.C
 *
 * DESCRIPTIVE NAME = Virtual Video Page Management
 *
 *
 * VERSION = V2.0
 *
 * DATE      11/10/88
 *
 * DESCRIPTION  This module contains the VVD's routines to install/remove
 *              page-fault hooks, and otherwise manage physical and virtual pages.
 *
 * FUNCTIONS
 *              vvSetFaultHooks()          Install/remove page hooks
 *              VVFaultHook()              VDM video page fault hook
 *              VVEnableBuffer()           Enable/disable VRAM pages
 *              vvTransferBuffer()         Transfer all pages to/from physical VRAM
 *              vvTransferLinearBuffer()   Transfer all pages to/from physical VRAM
 *              vvTransferPage()           Transfer virtual/physical page to physical/virtual
 *              vvValidatePage()           Give VDM access to physical (or virtual) VRAM
 *              vvUnvalidatePage()         Remove VDM access to physical VRAM
 *              vvUpdatePageStates()       Update page states on memory state changes
 *              vvMapPage()                Remaps a page, saving dirty info first
 *              vvFlushPages()             Flush dirty physical pages into virtual memory
 *              vvRectsFromPageMap()       Returns rectangles for a given pagemap
 *              vvRectsFromShadowBuffer()  Returns rectangles based on shadow buffer
 *              vvRectFromOffset()         Returns rectangle based on offset and size
 *
 * NOTES
 *
 *
 * STRUCTURES
 *
 * EXTERNAL REFERENCES
 *
 * EXTERNAL FUNCTIONS
 *
*/


#include <mvdm.h>
#include <vvd.h>
#include "vvdp.h"

#ifdef   VDDSTRICT
MODNAME = __FILE__;
#endif

#ifdef   SVGA
extern PSADAPTERINFO psCurAdapterInfo;                          /*          */
#endif
extern ULONG flVVD;
#pragma  BEGIN_SWAP_DATA
extern PBYTE pPhysVRAM;
extern PBVDM pvdmPhysVRAM;
extern ULONG npgPhysVRAM;
extern PSTATE apstateMap[];

#ifdef   VDDSTRICT

#ifdef   EGAVGA
/*!! This belongs in vvphys.c! */                               /*          */
extern ULONG ahPhysPage[MAX_PAGESPERPLANE];
extern ULONG aiPhysToVirt[MAX_PAGESPERPLANE];
#endif

#endif
#pragma  END_SWAP_DATA

#ifdef EGAVGA
#pragma  BEGIN_GLOBAL_DATA
extern VVDRQ vvdrqVideo;
#pragma  END_GLOBAL_DATA
#endif /* EGAVGA */

#pragma  BEGIN_SWAP_CODE

#ifdef SVGA                                                     /*            */

/***************************************************************************
 *
 * FUNCTION NAME = vvUnMapBuffer()
 *
 * DESCRIPTION   = Remove Mapping for VDM in 256-colour graphics mode.
 *
 *  Unmap the entire A0000 address range (64k) previously mapped
 *  to physical memory. This is used when a linear (256 colour)
 *  graphics mode is in effect and the video buffer is to be
 *  mapped to a virtual buffer, say on switch to background.
 *
 * INPUT         = None
 *
 * OUTPUT        = None
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 * CONTEXT - VDM Task-time
 *
 *            - Changes to use VDMData.ulMapCur array more consistently.
 *              Also map pages to invalid one at a time.
 *
 ***************************************************************************/

VOID PRIVENTRY vvUnMapBuffer( VOID )
{
  PBVDM pvdm;
  register INT iPage;
  INT iBank;

  if(! (VDMData.flVDMVideo & VDM_UNMAPPED) )
  {
    for( iPage = 0;                            /*            */
         iPage < 0x20;                         /*            */
         iPage++ )
    {
      VDHMapPages( NULL, &VDMData.avdhmt[iPage], VDHMT_INVALID );
      VDMData.ulMapCur [iPage] = VDHMT_INVALID;         /*            */
      // unmap the planar state.
      for(iBank = 0;iBank < MAX_BANKS;iBank++)         /*            */
      {
          (*(PULONG)(VDMData.aapstate[iBank][iPage])) &= ~ (ALL_VALID | ALL_INVRAM);
      }
    }
    VDMData.flVDMVideo |= VDM_UNMAPPED;
  }
}

/***************************************************************************
 *
 * FUNCTION NAME = vvMapBank()
 *
 * DESCRIPTION   = Map a bank to Physical/Virtual Memory
 *
 *  This routine is called to map a given bank of physical video
 *  memory or virtual memory to the A0000 linear address range.
 *  The range is first mapped as invalid before the new mapping
 *  is made.
 *
 *  Note: A check is made for dirty pages in the address range,
 *        which might not have been caught by vvCheckForDirtyLVB().
 *        This is only used when the range is aliased to the shadow
 *        buffer and LVB events are then generated as a result.
 *
 *
 * INPUT         = iBank == bank to be mapped to shadow buffer
 *
 * OUTPUT        = None
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 * CONTEXT - VDM Task-time
 *
 ***************************************************************************/

VOID PRIVENTRY vvMapBank(INT iBank)
{
  VDHMAPTARGET  vdhmt;
  VDHMAPSOURCE  vdhms;
  ULONG         flOption, iPage;
  FLAGS         flDirty;
  BOOL          fSuccess = FALSE;                       /*            */
  INT           iAllocateBanks = iBank;                       /*            */
  VDMData.VdmSVGA.usCurrentBank = (USHORT) iBank;       /*            */

  if( !(VDMData.flVDMVideo & VDM_FGND) )
    flDirty = VDHGetDirtyPageInfo( CURRENT_VDM,
                                   (PVOID)0xA0000L,
                                   BANKSIZE / PAGESIZE );

  vvUnMapBuffer();                                      /*            */

  /*
  ** Our definition of the BANK is 64K segment. All of our GetBank, SetBank,
  ** TransferBank code is based on the BANKSIZE=64K. However, current aperture
  ** enabled may be > BANKSIZE, so we have to take care of mapping the entire aperture.
  */
  if ((VDMData.npgPhysVRAMActive*PAGESIZE) > BANKSIZE)
    iAllocateBanks = iBank + 1;
  vdhms.vdhms_hobj  = 0;
  if (VDMData.flVDMVideo & VDM_FGND)
  {
    flOption = VDHMT_PHYSICAL;
    vdhms.vdhms_laddr = (ULONG) pvdmPhysVRAM;           /*            */
  }
  else
  {
    if ((iAllocateBanks+1) * PAGESFROMBYTES(64L*1024L) > VDMData.ulLinearPages)
    {
      if (!vvGrowLinearBuffer(CURRENT_VDM, iAllocateBanks+1))
      {
        vvFreezeVDM(CURRENT_VDM, FALSE);
        return;                      /* worst case, shouldn't happen! */
      }
      else
        flDirty = FALSE;             /* realloc probably lost it */
    }
    if (flDirty)
      vvAddEvent(CURRENT_VDM,
                 VVDEVENT_LVB,
                 SSToDS(&flDirty),
                 EVENT_ASYNC);

    vdhms.vdhms_laddr = (ULONG)VDMData.pLinearBuffer + iBank*BANKSIZE;
    flOption = VDHMT_LINEAR;
  }
  for (iPage=(INT) (VDMData.pvdmPhysVRAMActive-pvdmPhysVRAM)/PAGESIZE;
       iPage < (INT) VDMData.npgPhysVRAMActive;         /*            */
       vdhms.vdhms_hobj  = 0,                           /*            */
       vdhms.vdhms_laddr += PAGESIZE,iPage++)           /*            */
  {
     fSuccess = VDHMapPages(SSToDS(&vdhms), &VDMData.avdhmt[iPage], flOption);
     if ( !fSuccess )
        vvFreezeVDM(CURRENT_VDM, FALSE);                /*            */
     else
        VDMData.ulMapCur[iPage] = vdhms.vdhms_laddr|flOption;
  }
  /*** Lastly, update bank state table ***/
  if (flOption == VDHMT_LINEAR)
  {
    VDMData.abstate[iBank] |= BANK_VIRTUAL;
    if (iAllocateBanks != iBank)                        /*            */
      VDMData.abstate[iAllocateBanks] |= BANK_VIRTUAL;
  }
  else
  {
    VDMData.abstate[iBank] |= BANK_INVRAM;
    if (iAllocateBanks != iBank)                        /*            */
      VDMData.abstate[iAllocateBanks] |= BANK_VIRTUAL;
  }
  VDMData.flVDMVideo &= ~VDM_UNMAPPED;
}

#endif

/***************************************************************************
 *
 * FUNCTION NAME = VVEnableBuffer()
 *
 * DESCRIPTION   = Enable/disable VRAM pages
 *
 *                 For foreground MEMORY_TEXT VDMs, this is a matter of
 *                 stepping through all the entries in aabstPages[PLANE0],
 *                 and for all valid entries (ie, PAGE_TEXT), we map the
 *                 VDM's corresponding VRAM to physical memory.  For
 *                 foreground MEMORY_FONT VDMs, we walk through
 *                 aabstPages[PLANE2] instead.  For foreground MEMORY_GRFX
 *                 VDMs, we verify that all planes for a particular page are
 *                 valid (ie, PAGE_GRFX).  Any pages that are not completely
 *                 valid, or that are outside the active VRAM region, are
 *                 mapped as invalid.
 *
 *                 For background VDMs, the situation is similar, except that
 *                 we map in the appropriate virtual memory (or DONTCARE
 *                 memory for pages outside the active range), UNLESS this is
 *                 a MEMORY_GRFX VDM, in which case we simply map all VRAM to
 *                 invalid memory; any access will result in immediate
 *                 freezing.
 *
 *                 Note that mappings are performed on a "one per page"
 *                 basis, so that we always know how to unmap memory (ie,
 *                 one page at a time).
 *
 * INPUT         = fEnable == TRUE for VRAM enabling, FALSE for VRAM disabling
 *                 pcrf    -> VDM register frame (ignored)
 *
 * OUTPUT        = None
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 * PSEUDO-CODE
 *                 for every page in the VRAM address range
 *                   case inactive:
 *                     map invalid memory (to catch motion of active range)
 *                   case active:
 *                     if foreground VDM and page(s) are transferred to VRAM
 *                       map in VRAM
 *                     else if background VDM in text mode
 *                       map in virtual memory
 *                     else
 *                       map in invalid memory
 *
 *
 ***************************************************************************/

VOID HOOKENTRY VVEnableBuffer(BOOL fEnable,PCRF pcrf)
{
  ULONG flMap;
  BOOL fValid;
  INT npgTotal;
  INT npgActive;
  INT npgBeforeActive;
  register INT iPage;
  register INT iPlane;
  INT offActive;
  PBVDM pvdm;
  PVOID pMapTo;
  INT iCurrentBank = BANK0, iMaxBank= MAX_VGA_BANK,iBank;          /*            */

  /*
  ** If the VDM is still totally unmapped, we may get out quick
  */

  if (!fEnable && VDMData.flVDMVideo&VDM_UNMAPPED)
    return ;

  RequestMutexSem(VDMData.hmxVideoState);

#ifdef   SVGA                                   /*            */
  if (VDMData.mstateVideo >= MEMORY_GRFX)
  {                                             /*            */
    iCurrentBank = VDMData.VdmSVGA.usCurrentBank;
    iMaxBank = VDMData.VdmSVGA.usMaxBanks;
  }
#endif


#ifdef SVGA                                                     /*          */

  /*
  **  If we were in an enhanced mode use Bank services instead of Page
  **  services.                           
  */

  if ((VDMData.flVDMXVideo & VDMX_ENHANCEDMODE) || VDMData.pLinearBuffer)
  {
    if (!fEnable)
    {
       vvUnMapBuffer();
       vvShrinkBuffer(CURRENT_VDM, TRUE);
    }
    else
    {
       vvMapBank(iCurrentBank);
    }
    if (pvdm = VDMData.VdmSVGA.pvdmMMIOActive)
    {
       for (iPage = 0; iPage < VDMData.VdmSVGA.npgMMIOActive;iPage++,pvdm += PAGESIZE)
       {
           flMap = (fEnable) ? VDHMT_PHYSICAL : VDHMT_INVALID;  /*            */
           vvMapPage(pvdm,
                     (fEnable ? pvdm : NULL),
                     PLANE0,
                     BANK0,
                     MAX_VGA_BANK,                                     /*            */
                     flMap);
//                     vvAcceleratorUpdateMemoryMapState(CURRENT_VDM, pvdm, SSToDS( &flMap)); /*           */
        }
    }
    ReleaseMutexSem(VDMData.hmxVideoState);
    return;
  }
#endif                                                          /*          */

  pvdm = pvdmPhysVRAM;
  npgTotal = npgPhysVRAM;
  npgActive = VDMData.npgPhysVRAMActive;

  if (!npgActive)
    npgBeforeActive = 0;

  else
    npgBeforeActive = (VDMData.pvdmPhysVRAMActive-pvdmPhysVRAM)/PAGESIZE;
  iPage = 0;
  offActive = 0;
  VDMData.flVDMVideo |= VDM_UNMAPPED;

  while (npgTotal--)
  {
    pMapTo = NULL;
    iPlane = ALL_PLANES;
    flMap = VDHMT_INVALID;
    fValid = VDMData.mstateVideo;

#ifdef   EGAVGA

    if (fValid && (flVVD&VVD_MONOPRESENT))
    {

      /*
      ** If a MONO is present, EGA/VGA need to skip mapping
      ** the buffer region reserved for the MONO
      */

      fValid = ((ULONG)pvdm < MONOMEM_START || (ULONG)pvdm >= MONOMEM_START+
         MONOMEM_LEN);
    }
#endif

    /*
    ** Check for active/inactive page
    */

    if (npgBeforeActive)
      npgBeforeActive--;

    else  if (fEnable && fValid && npgActive)
    {

      /*
      ** Active page
      */

      if (VDMData.mstateVideo == MEMORY_FONT)
      {

        if ((VDMData.aapstate[BANK0][iPage][PLANE2]&PAGE_VALID) == PAGE_FONT)  /*           */
          iPlane = PLANE2;
      }

      else
      {
        if (VDMData.mstateVideo != MEMORY_GRFX)
        {

          if ((VDMData.aapstate[BANK0][iPage][PLANE0]&PAGE_VALID) ==
             VDMData.mstateVideo)              /*                         */
            iPlane = PLANE0;
        }

        else
        {

          if ((*(PULONG)(VDMData.aapstate[BANK0][iPage])&ALL_VALID&~
             ALL_PHYSICAL) == ALL_GRFX)
            iPlane = PLANE0;
        }
      }
      /*
      ** If active page is also valid
      */

      if (iPlane != ALL_PLANES)
      {

        if (VDMData.flVDMVideo&VDM_FGND)
        {

          /*
          ** Foreground VDMs get physical memory
          */

          if (VDMData.aapstate[iCurrentBank][iPage][iPlane]&PAGE_INVRAM)
          {                                    /*                         */
            pMapTo = pvdm;
            flMap = VDHMT_PHYSICAL;
          }
        }

        else
        {

          /*
          ** Background VDMs get virtual memory
          */

#ifdef   EGAVGA

          if ((*(PULONG)(VDMData.aapstate[iCurrentBank][iPage])&ALL_PHYSICAL) ==
             ALL_PHYSICAL)
          {                                    /*                         */
            iPlane = ALL_PLANES;
            /*
            ** It is possible that PM went background and the drq is NULL,
            ** while are our page states are still marked physical, since
            ** vvUpdatePhysPageMap doesn't atomically change vvdrq, vdm's flags
            ** and page states and their mappings.
            */
            if (vvdrqVideo.vvd_pPhysVRAM)       /*            */
            {
              flMap = VDHMT_PHYSICAL;
              pMapTo = vvdrqVideo.vvd_pPhysVRAM+VDMData.aiVirtToPhys[iPage]
                 *PAGESIZE;
            }
            else
            {
              pMapTo = NULL;
              flMap = VDHMT_INVALID;
            }
          }

          else
#endif

            if (VDMData.mstateVideo != MEMORY_GRFX)
            {

              if (VDMData.mstateVideo <= MEMORY_FONT || VDMData.flVDMVideo
                 &VDM_GRFXALLOWED)
              {
                flMap = VDHMT_LINEAR;
                pMapTo = VDMData.apPlane[BANK0][iPlane]+offActive;
              }
            }
        }
      }
      offActive += PAGESIZE;
      iPage++;
      npgActive--;
    }
    vvMapPage(pvdm,
              pMapTo,
              iPlane,
              iCurrentBank,                                 /*            */
              iMaxBank,                                     /*            */
              flMap);
#ifdef SVGA                                                 /*            */
    /*
    ** Notify the accelerator that the MMIO pages are being enabled/disabled.
    ** We want to be in control of the flMap so the call to the accelerator
    ** is issued after the mapping already took place. The accelerator still
    ** gets the benefit of knowing what is happening to the pages, but can't
    ** control where are the pages being mapped, as it does during the fault
    ** hook processing.
    */
//              if (VDMData.VdmSVGA.pvdmMMIOActive &&
//                 ((PBVDM)pvdm >= VDMData.VdmSVGA.pvdmMMIOActive) &&
//                 ((PBVDM)pvdm < (VDMData.VdmSVGA.pvdmMMIOActive+VDMData.VdmSVGA.npgMMIOActive*PAGESIZE)))
//                  vvAcceleratorUpdateMemoryMapState(CURRENT_VDM, pvdm, SSToDS( &flMap));
#endif
    pvdm += PAGESIZE;
  }
  ReleaseMutexSem(VDMData.hmxVideoState);
}

/***************************************************************************
 *
 * FUNCTION NAME = vvTransferBuffer()
 *
 * DESCRIPTION   = Transfer all pages to/from physical VRAM
 *
 *                 This is one of the major steps in moving a VDM from
 *                 background to foreground, or vice versa.  We step through
 *                 all the page-states of all the virtual buffer planes,
 *                 looking for pages that should be (or were) exchanged with
 *                 physical memory (ie, pages that are in PAGE_TEXT,
 *                 PAGE_FONT or PAGE_GRFX state).  Pages marked PAGE_NONE
 *                 are ignored.  See the fault hooks for descriptions of how
 *                 those types of pages are handled.
 *
 *                 Note that I do not use VDHExchangeMem to do the actual
 *                 exchanges.  If we need to yield after so many REP MOVSB
 *                 (or MOVSD if we don't have to worry about DMA overrun),
 *                 then we should handle that here.  Repeated calls to
 *                 VDHExchangeMem would be slower, and it would have no way
 *                 of knowing how often to yield.  In fact, many short calls
 *                 to VDHExchangeMem would probably never result in a yield,
 *                 and it has no way of knowing the characteristics of the
 *                 memory we're dealing with (ie, slow).
 *
 * INPUT         = hvdm  -> VDM
 *                 fVRAM == TRUE to transfer TO VRAM, FALSE to transfer FROM
 *
 * OUTPUT        = TRUE if successful, FALSE if not
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 * PSEUDO-CODE
 *                 for every plane
 *                   for every page in the current plane
 *                     if page-state is valid
 *                       if page-state differs from current copy-state
 *                         set copy-state according to page-state
 *                       exchange page with physical VRAM
 *
 ***************************************************************************/

BOOL PRIVENTRY vvTransferBuffer(register HVDM hvdm,INT iMaxBank,BOOL fVRAM)
{
  register INT iPage;
  register INT iPlane;
  register PVDMDATA pvd = pVDMData(hvdm);
  ULONG ulState;
  INT iBank;                                                    /*            */
#ifdef VGA
  if (!fVRAM && (pvd->flVDMXVideo & VDMX_CGARESTRICT))          /* 74167 */
  {                                                                  /* 74167 */
    if (pvd->mstateVideo <= MEMORY_FONT)                             /* 74167 */
    {                                                                /* 74167 */
       for (iPage = 0; iPage <= 1; iPage++)                          /* 74167 */
          pvd->aapstate[BANK0][iPage][PLANE2] |= PAGE_INVRAM;        /* 74167 */
    }                                                                /* 74167 */
  }
#endif
  /*
  ** True copy state unknown at this point
  */

  pvd->mstateCopy = MEMORY_NONE;

  for (iBank = 0;iBank < iMaxBank;iBank++)                      /*            */
  {
#ifdef   SVGA
                                                                /*          */
     (*psCurAdapterInfo->pfnSVGASetBank)                       /*          */
                        ( hvdm,
                        iBank,
                        fVRAM );
#endif /* SVGA */
     /*
     ** For every plane
     */

     for (iPlane = 0; iPlane < MAX_PLANES; iPlane++)
     {

        /*
        ** For every page in the current plane
        */

        for (iPage = 0; iPage < MAX_PAGESPERPLANE; iPage++)
        {
#ifdef SVGA
           if((pvd->aapstate[iBank] [iPage] [iPlane] & PAGE_TYPE) &&
              (fVRAM == !!(pvd->aapstate[iBank] [iPage] [iPlane] & PAGE_INVRAM)))
           {
              /*
              ** Setup the VRAM status of the non-physpage pages
              ** for the transferpage. Only the current bank was mapped
              ** into vram/virtual, so we need to expand that mapping to
              ** all the rest, since vvTransferPage will not transfer, assuming
              ** that the page is already transferred.
              */
              SETBITB( pvd->aapstate [iBank] [iPage] [iPlane],            /*            */
                       PAGE_INVRAM,
                       !fVRAM );
           }
#endif
           if (!vvTransferPage(hvdm,
                           iPage,
                           iPlane,
                           iBank,
                           fVRAM))
               return  FALSE;

        }
     }
  }
  return  TRUE;
}

/***************************************************************************
 *
 * FUNCTION NAME = vvTransferPage()
 *
 * DESCRIPTION   = Transfer virtual/physical page to physical/virtual
 *
 *                 This worker for vvTransferBuffer and vvValidatePage
 *                 performs the actual work of copying between the virtual
 *                 and physical buffers, for a given page and plane.
 *
 * INPUT         = hvdm   -> VDM
 *                 iPage  == page # to transfer
 *                 iPlane == plane # to transfer
 *                 fVRAM  == TRUE to transfer TO VRAM, FALSE to transfer FROM
 *
 * OUTPUT        = TRUE if successful, FALSE if not
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 ***************************************************************************/

BOOL PRIVENTRY vvTransferPage(
  HVDM hvdm,
  register INT iPage,
  register INT iPlane,
  INT iBank,
  BOOL fVRAM )
{
  INT s;
  PBYTE pbSrc,pbDst;
  PBYTE pbPhys,pbVirt;
  register PVDMDATA pvd = pVDMData(hvdm);

  /*
  ** Get page state
  ** If this isn't a foreground VDM or a PHYSICAL page, skip it
  */
  s = pvd->aapstate [iBank] [iPage] [iPlane];                   /*          */
  if( (s & PAGE_TYPE)
      && ((pvd->flVDMVideo & VDM_FGND)
          || (s & PAGE_PHYSICAL)) )
  {
    /*
    ** Page (not) transferred need not be (not) transferred again
    */
    if( fVRAM != !!(s & PAGE_INVRAM) )
    {
      if( iPage >= pvd->anpgPlane [iBank] [iPlane] )
      {
        if( !(pvd->flVDMXVideo&VDMX_ONDEMAND) )
          return( TRUE );
        else
          if( !vvGrowPlane( hvdm,
                            iPlane,
                            iPage + 1,
                            iBank ) )                           /*          */
            return( FALSE );
      }
      /*
      **determine physical/virtual addresses
      */
      pbPhys = pPhysVRAM;
      pbVirt = pvd->apPlane [iBank] [iPlane] + (iPage * PAGESIZE);

#ifdef   EGAVGA

      /*
      ** Prepare hardware for copy
      */
      vvSetCopyState( hvdm,
                      s & PAGE_TYPE,
                      iPlane );
#ifdef SVGA                                                     /*          */

      if( s & PAGE_PHYSICAL )
      {                                                         /*          */
        pbPhys += pvd->aiVirtToPhys [iPage] * PAGESIZE;         /*          */
#else /* SVGA */                                                /*          */
      if( (s & PAGE_PHYSICAL)                                   /*          */
          || (flVVD & VVD_VRAMISTOAST)                          /*          */
          || !vvdrqVideo.vvd_pfCtrlOwned )
      {                                                         /*          */
        if( s & PAGE_PHYSICAL )                                 /*          */
          pbPhys += pvd->aiVirtToPhys [iPage] * PAGESIZE;
        else                                                    /*          */
          pbPhys += iPage * PAGESIZE;                           /*          */
#endif /* SVGA */                                               /*          */

        if (fVRAM)
        {
          pbSrc = pbVirt;
          pbDst = pbPhys;
        }
        else
        {
          pbSrc = pbPhys;
          pbDst = pbVirt;
        }
        /*
        ** Move the page to/from physical VRAM
        */
        vdhMoveMem( pbSrc,
                    pbDst,
                    1,
                    1,
                    PAGESIZE,
                    FALSE );
      }
      else
#endif /* EGAVGA */
      {
        pbPhys += iPage * PAGESIZE;
        /*
        ** Exchange the page with physical VRAM
        */
        vdhMoveMem( pbVirt,
                    pbPhys,
                    1,
                    1,
                    PAGESIZE,
                    TRUE );
      }
    }
  }
  else
    fVRAM = FALSE;
  /*
  ** Finally, update the VRAM status of the page
  */
  SETBITB( pvd->aapstate [iBank] [iPage] [iPlane],
           PAGE_INVRAM,
           fVRAM );                                             /*          */
  return( TRUE );
}

/***************************************************************************
 *
 * FUNCTION NAME = vvValidatePage()
 *
 * DESCRIPTION   = Give VDM access to physical (or virtual) VRAM
 *
 *                 This is called strictly by the page-fault hooks to
 *                 exchange a page of virtual buffer memory with VRAM and/or
 *                 map it into the VDM.
 *
 *                 First, we have to set up the appropriate copy state,
 *                 based on the memory-state for the current VDM, using
 *                 vvSetCopyState().  Then we examine the page's state in
 *                 every plane, and exchange each plane showing an invalid
 *                 state (ie, PAGE_NONE or PAGE_BLANK).  PAGE_BLANK is
 *                 actually a special-case:  we must initialize each such
 *                 page to nulls before doing the exchange, clearing the
 *                 PAGE_BLANK flag afterward.  Each page that is exchanged
 *                 is marked with the current memory-state.  Finally, we
 *                 call vvRestoreCopyState to restore the hardware state.
 *
 * INPUT         = iPage == page index
 *                 iCurrentBank == index of the source bank we are processing
 *                 iMaxBank == number of banks the page is spanning through
 *                 (we are just propagating the page states for a mode spanning
 *                  across banks, and mapping and transferring the page in the
 *                  current bank only).                   
 * OUTPUT        = None
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 * PSEUDO-CODE
 *                 set page-state(s) to memory-state
 *                 exchange invalid page(s) with physical VRAM
 *
 ***************************************************************************/

VOID PRIVENTRY vvValidatePage(register INT iPage,
                              INT iCurrBank, INT iMaxBanks)
{
  PBVDM pvdm;
  PBYTE pbPlane;
  INT iPlaneMap,iMode, iBank;
  register INT iPlane = PLANE0;
  HVDM hvdmPrevious = VDMData.hvdmVideo;
  INT iCurrentBank = iCurrBank, iMaxBank = iMaxBanks;   /*            */

  AssertTRUE(iPage < MAX_PAGESPERPLANE);
  iPlaneMap = PLANE0;

  if (VDMData.mstateVideo == MEMORY_FONT)
    iPlaneMap = PLANE2;

#ifdef SVGA                                             /*            */
  if ((iMaxBank > MAX_BANKS) || (VDMData.flVDMXVideo & VDMX_ENHANCEDMODE))     /*            */
  {
    iMaxBank = MAX_VGA_BANK;                            /*            */
    iCurrentBank = 0;                                   /*            */
  }
#endif
#ifdef   EGAVGA
  iMode = SETIO_SHADOWED;

  /*
  ** We'll need the controller, so make sure we can use it
  */

  if (VDMData.flVDMVideo&VDM_PHYSPAGE)
  {
    iMode = SETIO_LIMIT_SHADOWED;
    hvdmPrevious = vvRequestController(VDMData.hvdmVideo,
                                       TRUE);
  }

  /*
  ** If there was no interruption in ownership, then save latches
  */

  if (hvdmPrevious == VDMData.hvdmVideo)
    vvSaveLatches(CURRENT_VDM,
                  TRUE);
#endif
  /*
  ** For every plane (that we have to process)
  */
  for( iBank = 0; iBank < iMaxBank;  iBank++ )
  {
    for (iPlane = iPlaneMap; iPlane < MAX_PLANES; iPlane++)
    {

       /*
       ** Set new page state
       */

       VDMData.aapstate[iBank][iPage][iPlane] &= ~PAGE_VALID;
       VDMData.aapstate[iBank][iPage][iPlane] |= apstateMap[VDMData.mstateVideo];
       if (iBank == iCurrentBank)
       {
          VDMData.aapstate[iBank][iPage][iPlane] |=
                        PAGE_PHYSICAL*!!(VDMData.flVDMVideo&VDM_PHYSPAGE);
          vvTransferPage(CURRENT_VDM,
                        iPage,
                        iPlane,
                        iBank,
                        TRUE);

       }
       /*
       ** We only have to do multiple planes for graphics modes
       */

       if (VDMData.mstateVideo != MEMORY_GRFX)  /*            */
          break;
    }
  }

#ifdef   EGAVGA

  /*
  ** If there was no interruption in ownership, then restore states
  */

  if (hvdmPrevious == VDMData.hvdmVideo)
  {
    vvRestoreLatches(CURRENT_VDM,
                     TRUE);
    vvRestoreCopyState(CURRENT_VDM,
                       iMode);
  }
  VDMData.mstateCopy = MEMORY_NONE;

  /*
  ** We're done with the controller, so unlock but leave owned by VDM
  */

  if (VDMData.flVDMVideo&VDM_PHYSPAGE)
    vvUnlockController();
#endif

  /*
  ** Last but not least, map the page into the VDM
  */

  pvdm = iPage *PAGESIZE+VDMData.pvdmPhysVRAMActive;

#ifdef   EGAVGA
  /*
  ** vvMapPages will setup the page state for all the banks, but map
  ** the aperture into the current bank.                  
  */
  if (VDMData.aapstate[iCurrentBank][iPage][iPlaneMap]&PAGE_PHYSICAL)   /*            */
  {
    /*
    ** It is possible that PM went background and the drq is NULL,
    ** while are our page states are still marked physical, since
    ** vvUpdatePhysPageMap doesn't atomically change vvdrq, vdm's flags
    ** and page states and their mappings.                
    */
    if (vvdrqVideo.vvd_pPhysVRAM)       /*            */
    {
      pbPlane = vvdrqVideo.vvd_pPhysVRAM+VDMData.aiVirtToPhys[iPage]*PAGESIZE;
      vvMapPage(pvdm,
                pbPlane,
                ALL_PLANES,
                iCurrentBank,                                   /*            */
                iMaxBank,
                VDHMT_PHYSICAL);
    }
    else
    {
      vvMapPage(pvdm,
                NULL,
                ALL_PLANES,
                iCurrentBank,                                   /*            */
                iMaxBank,
                VDHMT_INVALID);
    }
  }

  else
#endif

    if (VDMData.aapstate[iCurrentBank][iPage][iPlaneMap]&PAGE_INVRAM)                   /*            */
      vvMapPage(pvdm,
                pvdm,
                iPlaneMap,
                iCurrentBank,                                   /*            */
                iMaxBank,
                VDHMT_PHYSICAL);

    else
    {
      pbPlane = VDMData.apPlane[iCurrentBank][iPlaneMap]+iPage *PAGESIZE;               /*            */
      vvMapPage(pvdm,
                pbPlane,
                iPlaneMap,
                iCurrentBank,                           /*            */
                iMaxBank,
                VDHMT_LINEAR);
    }
  /*
  **   In case this happens to be a PHYSPAGE VDM, zap the PHYSPAGECHK bit to
  **   give the VDM a chance to DO something with the page we just gave it
  */

  VDMData.flVDMVideo &= ~VDM_PHYSPAGECHK;
}

#ifdef   EGAVGA

/***************************************************************************
 *
 * FUNCTION NAME = vvUnvalidatePage()
 *
 * DESCRIPTION   = Remove VDM access to physical VRAM
 *
 * INPUT         = hvdm    == VDM handle
 *                 hvdmCur == Current VDM handle
 *                 iPage   == page index
 *
 * OUTPUT        = None
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 * PSEUDO-CODE
 *                 set page-state(s) to memory-state
 *                 exchange invalid page(s) with physical VRAM
 *
 ***************************************************************************/

VOID PRIVENTRY vvUnvalidatePage(HVDM hvdm,
                                HVDM hvdmCur,
                                register INT iPage)
{
  PBVDM pvdm;
  register INT iPlane;
  HVDM hvdmPrevious;
  register PVDMDATA pvd = pVDMData(hvdm);
  INT  iBank;
  INT iCurrentBank = BANK0;
  INT iMaxBank= MAX_VGA_BANK;
  AssertTRUE(iPage < MAX_PAGESPERPLANE);
  AssertTRUE(pvd->mstateBuffer == MEMORY_GRFX);
#ifdef SVGA                                             /*            */
  if ((iMaxBank >= MAX_BANKS) || (pvd->flVDMXVideo & VDMX_ENHANCEDMODE))     /*            */
    return;
  iCurrentBank = (INT) pvd->VdmSVGA.usCurrentBank;
#endif
  if (hvdmCur)
  {
    hvdmPrevious = vvRequestController(hvdmCur,
                                       TRUE);

    if (hvdmPrevious == hvdmCur)
      vvSaveLatches(hvdmCur,
                    TRUE);
  }

  /*
  **   This has the same effect as calling TransferPage for each plane
  **   inside the loop below (ie, refreshing the virtual page from
  **   the physical page), but with the side-effect that diff information
  **   is preserved.
  */

  vvDiffPhysPage(hvdm,
                 iPage,
                 NULL,
                 NULL,
                 FALSE);

  /*
  ** For every plane (that we have to process)
  */
  for (iBank = 0; iBank < iMaxBank; iBank++)             /*            */
  {
     for (iPlane = PLANE0; iPlane < MAX_PLANES; iPlane++)
     {
        AssertTRUE(pvd->aapstate[iBank][iPage][iPlane]&PAGE_PHYSICAL);

        /*
        ** Set new page state (have to clear the INVRAM bit
        ** ourselves, since we no longer use vvTransferpage
        */

        pvd->aapstate[iBank][iPage][iPlane] &= ~(PAGE_VALID|PAGE_INVRAM);
        pvd->aapstate[iBank][iPage][iPlane] |= apstateMap[pvd->mstateVideo];
     }
  }                                                     /*            */

  if (hvdmCur)
  {

    if (hvdmPrevious == hvdmCur)
    {
      vvRestoreLatches(hvdmCur,
                       TRUE);
      vvRestoreCopyState(hvdmCur,
                         SETIO_LIMIT_SHADOWED);
    }
    vvUnlockController();
  }
  pvd->mstateCopy = MEMORY_NONE;

  /*
  ** Last but not least, unmap the page from the VDM, if possible
  */

  if (hvdm == hvdmCur)
  {
    pvdm = iPage *PAGESIZE+pvd->pvdmPhysVRAMActive;
    vvMapPage(pvdm,
              NULL,
              ALL_PLANES,
              iCurrentBank,                         /*                        */
              iMaxBank,                             /*            */
              VDHMT_INVALID);
  }
}
#endif                                           /* EGAVGA                  */

/***************************************************************************
 *
 * FUNCTION NAME = vvUpdatePageStates()
 *
 * DESCRIPTION   = Update page states on memory state changes
 *
 *                 This is called to reset the page states when the VDM has
 *                 changed its video memory mode.  The only time it will do
 *                 nothing is when the change is from FONT to TEXT or vice
 *                 versa.
 *
 * INPUT         =  hvdm -> VDM
 *
 * OUTPUT        =  TRUE if states changed, FALSE if not
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 * CALLED BY
 *      vvUpdateAll
 *
 ***************************************************************************/

BOOL PRIVENTRY vvUpdatePageStates(register HVDM hvdm)
{
  BOOL fChange = FALSE;
  MSTATE mstateTmp;
  register INT iPage;
  register PVDMDATA pvd = pVDMData(hvdm);

  mstateTmp = pvd->mstateVideo;
#ifdef SVGA
  mstateTmp |= (pvd->flVDMXVideo & VDMX_ENHANCEDMODE);    /*            */
#endif
  /*
  ** We treat FONT and TEXT the same
  */

  if (mstateTmp == MEMORY_FONT)
    mstateTmp = MEMORY_TEXT;

  /*
  ** So, if the memory state has changed, reset some things
  */

  if (mstateTmp != pvd->mstateBuffer)
  {

#ifdef   EGAVGA

    if (pvd->flVDMVideo&VDM_PHYSPAGE && (mstateTmp != MEMORY_GRFX))
      vvFreeAllPhysPages(pvd->hvdmVideo,
                         FALSE);
#endif

    for (iPage = 0; iPage < MAX_PAGESPERPLANE; iPage++)
    {

#ifdef   SVGA
      {
        INT register iBank;

        for (iBank = 0; iBank < MAX_BANKS; iBank++)
          *(PULONG)(pvd->aapstate[iBank][iPage]) &= ~ALL_VALID;
      }

#else
      *(PULONG)(pvd->aapstate[BANK0][iPage]) &= ~ALL_VALID;
#endif
      pvd->aoffPhysDiff[iPage] = PAGESIZE;
      pvd->anbPhysDiff[iPage] = 0;
    }

    if (pvd->flVDMVideo&VDM_FGND)
      flVVD |= VVD_VRAMISTOAST;

    /*
    ** Redo all the page mappings if we're in VDM's context
    ** For SVGAs: if the memory state has changes, we invalidate
    ** current mappings and enable the fault hook. This is done so
    ** that we can handle transitions between linear and non-linear
    ** (packed, planar) mode, since the two can not coexist.
    ** Transitions which do not envolve linear memory organization
    ** are still handled by transferring the old pages over to the new map.
    */
    if (hvdm == CURRENT_VDM && pvd->flVDMVideo&VDM_STATEINIT)
#ifdef SVGA                     /*            */
    if ((mstateTmp & VDMX_ENHANCEDMODE) || (pvd->mstateBuffer & VDMX_ENHANCEDMODE))
    {
      vvShrinkBuffer(CURRENT_VDM, TRUE);        //shrink first
      VVEnableBuffer(FALSE,
                   NULL);
    }
    else
#endif
      VVEnableBuffer(TRUE,
                     NULL);
    fChange++;
  }
  pvd->mstateBuffer = mstateTmp;
  return  fChange;
}

/***************************************************************************
 *
 * FUNCTION NAME = vvMapPage()
 *
 * DESCRIPTION   = Remaps a page, saving dirty info first
 *                 This function maintains and maps pages for non-ENHANCED modes,
 *                 Therefore it will fail any call which is for a bank > MAX_BANKS
 *                 vvMapBank is the equivalent for ENHANCED modes.
 *
 * INPUT         = pvdm      -> VDM page to remap
 *                 pMapTo    -> Memory address to map to
 *                 iPlaneMap == associated virtual plane # (-1 if none)
 *                 iBank     == assoc bank to update                         
 *                 flOption  == Any of the VDHMapPages options
 *
 * OUTPUT        = None
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 ***************************************************************************/

VOID PRIVENTRY vvMapPage(PBVDM pvdm,
                         PVOID pMapTo,
                         register INT iPlaneMap,
                         INT iCurrBank,                          /*                        */
                         INT iMaxBanks,                          /*                        */
                         FLAGS flOption)
{
  INT ivdmPhysPage;
  INT iPage;
  INT iBank, iCurrentBank = iCurrBank, iMaxBank = iMaxBanks;                    /*            */
  ULONG fSuccess;

#ifdef SVGA                                                     /*            */
  if ((iMaxBank > MAX_BANKS) || (VDMData.flVDMXVideo & VDMX_ENHANCEDMODE))     /*            */
  {
    iMaxBank = MAX_VGA_BANK;                                    /*            */
    iCurrentBank = BANK0;                                       /*            */
  }
#endif
  /*
  ** Get index of faulty page (absolute)
  */
  ivdmPhysPage = (LONG)(pvdm-pvdmPhysVRAM)/PAGESIZE;
  AssertTRUE(ivdmPhysPage < VRAM_PAGES);

  /*
  ** Get index of faulty page (relative)
  */

  iPage = (LONG)(pvdm-VDMData.pvdmPhysVRAMActive)/PAGESIZE;

  if (iPage < 0 || iPage >= MAX_PAGESPERPLANE)
    iPage = -1;

  /*
  ** Determine if we're undoing a valid mapping for a windowed VDM
  */

  if (VDMData.flVDMVideo&VDM_WINDOWED)
  {

    if (iPage >= 0 && VDMData.npgPhysVRAMActive)
    {

      /*
      ** Is page currently mapped to virtual memory?
      */

      if (VDMData.aapstate[iCurrentBank][iPage][PLANE0]&PAGE_VIRTUAL)/*
                                                                          */

        /*
        ** Yes, so save dirty info before the remap tosses it
        */

        VDMData.flDirtyPages |= VDHGetDirtyPageInfo(CURRENT_VDM,
                                                    pvdm,
                                                    1) << iPage;
    }
  }

  /*
  ** If ALL video pages will no longer be ALL unmapped, make a note
  */

  if (flOption != VDHMT_INVALID)
    VDMData.flVDMVideo &= ~VDM_UNMAPPED;

  /*
  ** Convert certain invalid/black-hole mappings to "bonus" mappings
  */

  if (flOption == VDHMT_INVALID && !(VDMData.flVDMVideo&VDM_MODECHANGING) ||
     flOption == VDHMT_BLACK_HOLE && (VDMData.flVDMVideo&VDM_MODECHANGING))
  {

    if (VDMData.flVDMXVideo&(VDMX_CGARESTRICT|VDMX_MONORESTRICT))
    {

      if (PAGESFROMBYTES(pvdm-pvdmPhysVRAM) < VDMData.npgBonusBuffer)
      {
        pMapTo = VDMData.pBonusBuffer+(pvdm-pvdmPhysVRAM);
        flOption = VDHMT_LINEAR;
      }
    }
  }

  /*
  ** Map in page
  */

  if (!pMapTo)                                  /*            */
    VDMData.vdhms.vdhms_laddr = (ULONG)pvdm;    /*            */
  else                                          /*            */
    VDMData.vdhms.vdhms_laddr = (ULONG)pMapTo;

  VDMData.vdhms.vdhms_hobj = 0;

  if ((VDMData.vdhms.vdhms_laddr|flOption) != VDMData.ulMapCur[ivdmPhysPage])
  {
    fSuccess = VDHMapPages(&VDMData.vdhms,                    /*            */
                        &VDMData.avdhmt[ivdmPhysPage],
                        flOption);
    if (!fSuccess)
    {
      VDHGetError();
      if (flOption != VDHMT_INVALID)      /*            */
        vvFreezeVDM(CURRENT_VDM,
                         TRUE);
      return;
    }
    VDMData.ulMapCur[ivdmPhysPage] = VDMData.vdhms.vdhms_laddr|flOption;
  }

  if (iPage >= 0)
  {
    /*
    ** Lastly, update page state table for all the banks of this mode
    */
    for (iBank = 0; iBank < iMaxBank;iBank++)         /*            */
    {

      if (flOption == VDHMT_LINEAR || ((flOption == VDHMT_PHYSICAL) &&
          VDMData.flVDMVideo&VDM_PHYSPAGE))
      {

         if (iPlaneMap == ALL_PLANES)
              *(PULONG)(VDMData.aapstate[iBank][iPage]) |= ALL_VIRTUAL;

         else
              VDMData.aapstate[iBank][iPage][iPlaneMap] |= PAGE_VIRTUAL;
      }

      else
      {

         if (iPlaneMap == ALL_PLANES)
              *(PULONG)(VDMData.aapstate[iBank][iPage]) &= ~ALL_VIRTUAL;

         else
              VDMData.aapstate[iBank][iPage][iPlaneMap] &= ~PAGE_VIRTUAL;
      }
    }                           /*            */
  }
}

/***************************************************************************
 *
 * FUNCTION NAME = vvFlushPages()
 *
 * DESCRIPTION   = Flush dirty physical pages into virtual memory
 *
 *
 * INPUT         = hvdm -> VDM
 *
 * OUTPUT        = None
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 ***************************************************************************/

VOID PRIVENTRY vvFlushPages(HVDM hvdm)
{
  INT iFirst,iLast;
  register FLAGS fl;
  register INT iPage;
  register PVDMDATA pvd = pVDMData(hvdm);

#ifdef   EGAVGA

  if (pvd->flVDMVideo&VDM_PHYSPAGE)
  {

    if (vvCheckForDirtyLVB(hvdm,
                           0) || pvd->flDirtyPageEvent)
    {
      vvRequestController(MISC_OWNER,
                          TRUE);               /*                         */
      iPage = 0;
      fl = pvd->flDirtyPageEvent;
      iFirst = pvd->offPhysVRAMVisible/PAGESIZE;
      iLast = (pvd->offPhysVRAMVisible+pvd->nbPhysVRAMVisible-1)/PAGESIZE;

      while (fl && iPage <= iLast)
      {

        if ((fl&1) && iPage >= iFirst)
          vvDiffPhysPage(hvdm,
                         iPage,
                         NULL,
                         NULL,
                         TRUE);
        fl >>= 1;
        iPage++;
      }
      vvFreeController(MISC_OWNER);
    }
  }
#endif
  pvd->flDirtyPageEvent = 0;
  vvDeleteEvent(hvdm,
                VVDEVENT_LVB,
                0);
}

#ifdef SVGA                                                     /*            */

/***************************************************************************
 *
 * FUNCTION NAME = vvRectsFromLinearBuffer()
 *
 * DESCRIPTION   = Return the dirty bank as a rectangle to update
 *                 a windowed-VDM in linear graphics mode.
 *
 *  Note that the rectangle returned does not include partial
 *  scan-lines but rounds down to the nearest start and end
 *  lines within a bank. This until vvCopyGraphicsToBitmap256()
 *  handles partial scans, but we can get better performance
 *  this way...
 *
 * INPUT         = hvdm -> VDM
 *                 flPageMap == page bits
 *                 nRects    == maximum # of rectangles we can return
 *                 pvvlvb    -> rectangle information storage
 *
 *
 * OUTPUT        = # of rectangles returned (zero if none)
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 * CONTEXT - Task-time
 *
 ***************************************************************************/

ULONG PRIVENTRY vvRectsFromLinearBuffer(HVDM hvdm,
                                        FLAGS flPageMap,
                                        ULONG nRects,
                                        register PVVLVB pvvlvb)
{
  ULONG ulBank;
  register INT i, j;
  register PVDMDATA pvd = pVDMData(hvdm);

  ulBank = pvd->ulDirtyBankEvent;

  pvvlvb->vvl_rcl.xLeft   = 0;
  pvvlvb->vvl_rcl.xRight  = pvd->vvMode.vvm_nCols - 1;
  pvvlvb->vvl_rcl.yTop    = (ulBank * BANKSIZE + (ZEROBITS(flPageMap)) * PAGESIZE) / pvd->vvMode.vvm_nCols;

  for (i=BANKSIZE/PAGESIZE, j=1<<(BANKSIZE/PAGESIZE-1); !(flPageMap & j) && i>0; i--, j>>=1);
  pvvlvb->vvl_rcl.yBottom = ((ulBank * BANKSIZE + i * PAGESIZE) / pvd->vvMode.vvm_nCols) - 1;

  if (pvvlvb->vvl_rcl.yBottom >= pvd->vvMode.vvm_nRows)
      pvvlvb->vvl_rcl.yBottom = pvd->vvMode.vvm_nRows - 1;

  pvvlvb->vvl_fCertain = TRUE;
  return (1);
}

#endif

/***************************************************************************
 *
 * FUNCTION NAME = vvRectsFromPageMap()
 *
 * DESCRIPTION   = Returns rectangles for a given pagemap
 *
 *                 Turns a bitmap of dirty pages into 1 or more rectangles
 *                 encompassing those pages.
 *
 *                 Note there are currently TWO instances where the pagemap
 *                 will be 0xffffffff (-1):  when the application has flip
 *                 CRT pages, and when a dirty LVB has been detected
 *                 following removal of a STRING or SCROLL event (meaning
 *                 the STRING or SCROLL event was likely to be invalid).
 *
 *                            SVGA: For 256-colour SVGA modes, flPageMap
 *                                  is the dirty bank number.
 *
 * INPUT         = hvdm      -> VDM
 *                 flPageMap == page bits
 *                 nRects    == maximum # of rectangles we can return
 *                 pvvlvb    -> rectangle information storage
 *
 * OUTPUT        = # of rectangles returned (zero if none)
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 ***************************************************************************/

ULONG PRIVENTRY vvRectsFromPageMap(HVDM hvdm,
                                   FLAGS flPageMap,
                                   ULONG nRects,
                                   register PVVLVB pvvlvb)
{
  BOOL f;
  ULONG nRectsFound = 0;
  INT iFirst,iLast;
  register INT iPage;
  PVVLVB pvvlvbOld = pvvlvb;
  ULONG offDiffCur,nbDiffCur;
  ULONG offDiff = 0,nbDiff = 0;
  register PVDMDATA pvd = pVDMData(hvdm);

#ifdef SVGA                                                     /*          */
  if( pvd->flVDMXVideo & VDMX_ENHANCEDMODE )                    /*          */
    return( vvRectsFromLinearBuffer( hvdm,
                                     flPageMap,
                                     nRects,
                                     pvvlvb ) );
#endif /* SVGA */                                               /*          */

  iFirst = pvd->offPhysVRAMVisible/PAGESIZE;
  iLast = (pvd->offPhysVRAMVisible+pvd->nbPhysVRAMVisible-1)/PAGESIZE;

#ifdef EGAVGA
  if( pvd->flVDMVideo & VDM_PHYSPAGE )
  {
    vvRequestController( MISC_OWNER,
                         TRUE );                                /*          */
#ifdef VDDDEBUGALL
    PRINTDEBUG("PHYSPAGE VDM dirty page vector: (%08x)\n",
                 flPageMap);
#endif
  }
#endif /* EGAVGA */

  iPage = 0;

  while (flPageMap && iPage <= iLast)
  {

    /*
    ** If page is dirty
    */

    if (flPageMap&1)
    {

      /*
      ** And page is visible
      */

      if (iPage >= iFirst)
      {

        /*
        ** For PHYSPAGE VDMs, we diff and copy each plane of this page,
        ** and determine on a page-by-page basis whether to add this as a
        ** separate rectangle or combine it with the previous rectangle
        */

#ifdef   EGAVGA

        if (pvd->flVDMVideo&VDM_PHYSPAGE)
        {
          f = vvDiffPhysPage(hvdm,
                             iPage,
                             SSToDS(&offDiffCur),
                             SSToDS(&nbDiffCur),
                             TRUE);

          /*
          ** If SHADOWINVALID set, pretend VDM rewrote every video page
          */

          if (pvd->flVDMVideo&VDM_SHADOWINVALID)
          {
            f = TRUE;
            offDiffCur = 0;
            nbDiffCur = PAGESIZE;
          }

          if (f)
          {

            #ifdef   VDDDEBUGALL
              PRINTDEBUG("Page %02x differed at %04x, %04x bytes)\n",
                         iPage,
                         offDiffCur,
                         nbDiffCur);
            #endif
            offDiffCur += iPage *PAGESIZE;

            if (nbDiff && nRects > 1 && offDiffCur-(offDiff+nbDiff) >
               PAGESIZE/2 && offDiff < pvd->offPhysVRAMVisible+
               pvd->nbPhysVRAMVisible)
            {

              /*
              ** This difference is a substantial distance from the
              ** previous difference, so add the previous difference
              ** as a new rectangle
              */

              nRects--;

              if (nRectsFound++)
                pvvlvb++;
              vvRectFromOffset(hvdm,
                               offDiff,
                               nbDiff,
                               &pvvlvb->vvl_rcl);

              #ifdef   VDDDEBUGALL
                PRINTDEBUG("Added rectangle: %04x,%04x down to %04x,%04x\n",
                           pvvlvb->vvl_rcl.xLeft,
                           pvvlvb->vvl_rcl.yTop,
                           pvvlvb->vvl_rcl.xRight,
                           pvvlvb->vvl_rcl.yBottom);
              #endif
              pvvlvb->vvl_fCertain = TRUE;
              offDiff = offDiffCur;
              nbDiff = nbDiffCur;
            }

            else
            {

              if (!nbDiff)
                offDiff = offDiffCur;
              nbDiff = offDiffCur+nbDiffCur-offDiff;
            }
          }

          #ifdef   VDDDEBUGALL

          else
          {
            PRINTDEBUG("Page %02x no differences\n",
                       iPage);
          }
          #endif
        }

        /*
        ** For all other VDMs (shadowed and unshadowed), we just
        ** accumulate the offset and size of the dirty pages and convert
        ** that into one or more rectangles later
        */

        else
        {
#endif

          if (!nbDiff)
            offDiff = iPage *PAGESIZE;
          nbDiff = iPage *PAGESIZE+PAGESIZE-offDiff;

#ifdef   EGAVGA
        }
#endif
      }
    }
    iPage++;
    flPageMap >>= 1;
  }

#ifdef   EGAVGA

  if (pvd->flVDMVideo&VDM_PHYSPAGE)
  {
    vvFreeController(MISC_OWNER);
    pvd->flVDMVideo &= ~VDM_SHADOWINVALID;
  }
#endif

  if (nbDiff && nRects > 0 && offDiff < pvd->offPhysVRAMVisible+
     pvd->nbPhysVRAMVisible)
  {

    if (nRectsFound++)
      pvvlvb++;
    vvRectFromOffset(hvdm,
                     offDiff,
                     nbDiff,
                     &pvvlvb->vvl_rcl);

#ifdef   VDDDEBUGALL

    if (pvd->flVDMVideo&VDM_PHYSPAGE)
    {
      PRINTDEBUG("Added final rectangle: %04x,%04x down to %04x,%04x\n",
                 pvvlvb->vvl_rcl.xLeft,
                 pvvlvb->vvl_rcl.yTop,
                 pvvlvb->vvl_rcl.xRight,
                 pvvlvb->vvl_rcl.yBottom);
    }
#endif
    pvvlvb->vvl_fCertain = FALSE;

    if (pvd->flVDMVideo&VDM_PHYSPAGE)
      pvvlvb->vvl_fCertain++;

    if (pvd->pShadowPlane)
      nRectsFound = vvRectsFromShadowBuffer(hvdm,
                                            nRects,
                                            pvvlvb);

    /*
    ** The goal here is to determine if the VDM is slowly updating
    ** video memory in a sequential fashion, and if so, to postpone updating
    */

    if (nRectsFound == 1 && pvd->nDiffPrev == 1)
    {

      if (pvvlvb->vvl_rcl.yTop-pvd->rclDiffPrev.yBottom <= 1)
        pvd->flVDMVideo &= ~VDM_PHYSPAGECHK;
      pvd->rclDiffPrev = pvvlvb->vvl_rcl;
    }
    pvd->nDiffPrev = nRectsFound;
  }
  AssertTRUE(pvvlvbOld->vvl_rcl.yTop >= 0);

#ifdef   VDDDEBUGALL

  if (pvd->flVDMVideo&VDM_PHYSPAGE)
  {
    PRINTDEBUG("PHYSPAGE VDM total differences: %02x\n\n",
               nRectsFound);
  }
#endif
  return  nRectsFound;
}

/***************************************************************************
 *
 * FUNCTION NAME = vvRectsFromShadowBuffer()
 *
 * DESCRIPTION   = Returns rectangles based on shadow buffer
 *                 Compares a virtual video buffer against its shadow
 *                 buffer, and returns one or more rectangles with actual
 *                 differences.
 *
 * INPUT         = hvdm      -> VDM
 *                 nRects    == maximum # of rectangles we can return
 *                 pvvlvb    -> rectangle information storage
 *
 * OUTPUT        = # of rectangles returned (zero if none)
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 ***************************************************************************/

ULONG PRIVENTRY vvRectsFromShadowBuffer(HVDM hvdm,
                                        ULONG nRects,
                                        register PVVLVB pvvlvb)
{
  ULONG nRows;
  ULONG nRectsFound;
  PVVLVB pvvlvbOld = pvvlvb;
  register PVDMDATA pvd = pVDMData(hvdm);


  if (pvd->flVDMVideo&VDM_SHADOWINVALID)
  {
    nRectsFound = 1;
    vvFillShadowBuffer(hvdm,
                       NULL);
    pvd->flVDMVideo &= ~VDM_SHADOWINVALID;
  }

  else
  {

    /*
    ** If rectangle traverses center line, divide in two
    */

    nRectsFound = 0;
    nRows = pvd->vvMode.vvm_nRows/2;

    if (nRects < 2 || pvvlvb->vvl_rcl.yBottom <= nRows ||
       pvvlvb->vvl_rcl.yTop >= nRows)
      nRows = 0;

    else
    {

      /*
      ** We know there is room for at least two rectangles,
      ** and that yBottom > middle and that yTop < middle
      */

      (pvvlvb+1)->vvl_rcl = pvvlvb->vvl_rcl;
      pvvlvb->vvl_rcl.yBottom = nRows;
    }

    while (TRUE)
    {

      if (vvDiffShadowBuffer(hvdm,
                             &pvvlvb->vvl_rcl))
      {
        vvFillShadowBuffer(hvdm,
                           &pvvlvb->vvl_rcl);
        pvvlvb->vvl_fCertain = TRUE;
        pvvlvb++;
        nRectsFound++;
      }

      else

        if (nRows)

          pvvlvb->vvl_rcl = (pvvlvb+1)->vvl_rcl;

      if (nRows)
      {
        pvvlvb->vvl_rcl.yTop = nRows+1;
        nRows = 0;
      }

      else
        break;
    }
    pvvlvb = pvvlvbOld;

    /*
    ** If we ended up with multiple side-by-side rectangles,
    ** then decide if we should convert them into one single
    ** rectangle anyway
    */

    if (nRectsFound == 2)
    {

      /*
      ** Compute some random distance that the rectangles
      ** should be within, based on the total size of both;
      ** otherwise, we don't coalesce them, for speed's sake
      */

      nRows = (pvvlvb->vvl_rcl.yBottom-pvvlvb->vvl_rcl.yTop+(pvvlvb+1)->
         vvl_rcl.yBottom-(pvvlvb+1)->vvl_rcl.yTop)/16+1;

      if ((pvvlvb+1)->vvl_rcl.yTop <= pvvlvb->vvl_rcl.yBottom+nRows)
      {
        pvvlvb->vvl_rcl.yBottom = (pvvlvb+1)->vvl_rcl.yBottom;
        nRectsFound--;
      }
    }
  }
  return  nRectsFound;
}

/***************************************************************************
 *
 * FUNCTION NAME = vvRectFromOffset()
 *
 * DESCRIPTION   = Returns rectangle based on offset and size
 *
 *
 * INPUT         =  hvdm    -> VDM
 *                  offDiff == offset within visible region
 *                  nbDiff  == size of visible region
 *                  prcl    -> destination rectangle
 *
 * OUTPUT        =  None
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 ***************************************************************************/

VOID PRIVENTRY vvRectFromOffset(HVDM hvdm,
                                ULONG offDiff,
                                ULONG nbDiff,
                                register PRECTL prcl)
{
  LONG sizeRow;
  register PVDMDATA pvd = pVDMData(hvdm);
  BYTE abOddEvenTop[4] =
  {
    0,102,1,103
  }
  ;
  BYTE abOddEvenBottom[4] =
  {
    102,198,103,199
  }
  ;
  sizeRow = pvd->vvMode.vvm_nCols;

  if (pvd->vvMode.vvm_ulFormat == FORMAT_CGA)
    sizeRow *= 2;

  else

    if (pvd->vvMode.vvm_ulFormat == FORMAT_BITMAP)

      sizeRow = (sizeRow * pvd->nBitsPixel) / 8;

  /*
  ** This is a kludge for banked modes that we fix up below
  */

  if (pvd->offOddBank)
    sizeRow = PAGESIZE;
  prcl->yTop = (LONG)(offDiff-pvd->offPhysVRAMVisible)/sizeRow;
  prcl->xLeft = 0;
  prcl->yBottom = (LONG)(offDiff+nbDiff-pvd->offPhysVRAMVisible+sizeRow-1)/
     sizeRow-1;
  prcl->xRight = pvd->vvMode.vvm_nCols-1;

  /*
  ** Keep those rectangle coordinates in bounds
  */

  if (prcl->yTop < 0)
    prcl->yTop = 0;

  if (prcl->yBottom < 0)
    prcl->yBottom = 0;

  if (prcl->yTop >= pvd->vvMode.vvm_nRows)
    prcl->yTop = pvd->vvMode.vvm_nRows-1;

  if (prcl->yBottom >= pvd->vvMode.vvm_nRows)
    prcl->yBottom = pvd->vvMode.vvm_nRows-1;

  /*
  ** See if we need to do some coordinate fixups for banked modes
  */

  if (pvd->offOddBank)
  {

    if (prcl->yTop >= sizeof(abOddEvenTop))
      prcl->yTop = sizeof(abOddEvenTop)-1;
    prcl->yTop = abOddEvenTop[prcl->yTop];

    if (prcl->yBottom >= sizeof(abOddEvenBottom))
      prcl->yBottom = sizeof(abOddEvenBottom)-1;
    prcl->yBottom = abOddEvenBottom[prcl->yBottom];
  }
}
#ifdef   SVGA                                    /*                         */
/***************************************************************************
 *
 * FUNCTION NAME = vvPrepareForTransfer
 *
 * DESCRIPTION   = Prepare to transfer mode 13 with CHAIN4 bit reset.
 *
 *                  This routine is called by vvSetBgnd or vvSetFgnd
 *                  before the transfer of BIOS mode 13 with CHAIN4
 *                  bit reset. These modes need to be treated as
 *                  planar with all the planes in use.
 *
 * INPUT         = hvdm == VDM handle
 *                 flForeground = TRUE if in the foreground, FALSE in bgnd
 *
 *
 * OUTPUT        = None
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *              This function brutally forces the X-mode page states into
 *              a page state PAGE_GRFX for all the planes for all the pages
 *              of a bank, for the number of banks passed. The algorithm is
 *              not a sophisticated one:
 *              if none of the planes of the first page in a bank were
 *              touched, the entire bank is skipped over. The mapping (INVRAM,...)
 *              used for a bank is determined based on the first valid
 *              mapping found in the first plane of the bank.
 *
 *
 **************************************************************************/
VOID EXPENTRY vvPrepareForTransfer(HVDM hvdm, BOOL flForeground, ULONG ulBanks)
{
  register PVDMDATA pvd = pVDMData(hvdm);
  ULONG *pPageState;
  INT iBank, iPage, iPlane;
  UCHAR pageMap;                                                /*          */
  ULONG pageMapMask;

#define PAGE_MAPS       0xF0F0F0F0
  /*
  ** There is a possibility that 320x200 is used as a base for an enhanced mode.
  ** Don't perform the preparation in that case. Ex: Speedway 640x480x256.
  */
  if (pvd->flVDMXVideo & VDMX_ENHANCEDMODE)                     /*          */
    return;
  /*
  ** Regardless of the direction, mark the memory state as GRFX.
  */
  pvd->mstateVideo = MEMORY_GRFX;

  if (flForeground)
    return;
  /*
  ** Mark all the planes of the banks touched as MEMORY_GRFX.
  ** Page states do not change while in the background, since we
  ** don't know how to virtualize these modes. In marking the pages,
  ** PAGE_TYPE nibble should be changed to PAGE_GRFX. Higher nibble
  ** if should become the same as the state of the plane which has
  ** a valid mapping.
  */

  for( iBank = 0, iPage = 0;
       iBank < ulBanks;
       iBank++ )
  {
    if (pageMapMask = (ULONG) *((ULONG *) &(pvd->aapstate[iBank][iPage][0])))
    {
      /*
      ** Examine the first page in each bank and determine the map
      ** value to be used. (higher nibble)
      */
      if(pageMapMask&PAGE_MAPS)
      {
        for(iPlane=0,pageMap=0;iPlane<MAX_PLANES;iPlane++)
          if (pageMap = pvd->aapstate[iBank][0][iPlane]&0xF0)
          {
            pageMap |= PAGE_GRFX;
            break;
          }
        pageMapMask = (pageMap | pageMap<< 8 | pageMap<<16 | pageMap<<24);
      }
      for(iPage=0;iPage<MAX_PAGESPERPLANE;iPage++)
      {
        pPageState = (ULONG *) &(pvd->aapstate[iBank][iPage][0]);
        *pPageState = pageMapMask;
      }
      iPage = 0;
    }
  }
}
#endif /* SVGA */

/***************************************************************************
 *
 * FUNCTION NAME = VVFaultHook()
 *
 * DESCRIPTION   = VDM video page fault hook
 *
 *
 *                 If a page outside the active VRAM area is touched, then we
 *                 map in UNDEFINED memory to allow the access proceed, and
 *                 enable trapping on the GDC data port.  As soon as the port
 *                 is accessed, we invalidate all the inactive VRAM pages
 *                 again, and disable trapping on the port.  This special GDC
 *                 logic is just for the VGA, because there is otherwise no
 *                 need to trap accesses to that port while foreground; it
 *                 shouldn't even happen unless an application is poking
 *                 around in memory to see what's there, rather than as part
 *                 of any real video operation.
 *
 *                 We assume that no real apps would switch the currently
 *                 displayed page to one that hasn't yet been validated either
 *                 at screen-switch time or through this page-fault handler.
 *                 We may have to special-case those modes that can display
 *                 more than one 4k page at a time, meaning that upon the first
 *                 invalid page access, we validate all the invalid pages.  All
 *                 this really refers to is BLANK pages, which we don't
 *                 exchange with VRAM until they are actually accessed.
 *
 *                 For faults taken on pages we have no virtual memory allocated
 *                 for, we attempt to grow the appropriate plane buffer, mark
 *                 the corresponding page as either PAGE_TEXT, PAGE_FONT, etc,
 *                 depending on the current memory state, and map it into place.
 *                 On failure to grow the virtual buffer plane, we simply freeze
 *                 the VDM; it will be awakened as soon as the user tries to
 *                 bring it foreground, and either the memory will then be
 *                 available, or the user will be informed that memory could not
 *                 be obtained (see VVGrowBuffer).
 *
 *                         Notes on the background virtualization of non-ENHANCED modes.
 *              For non-enhanced graphics modes, fault hooks will grow the page across
 *              all the banks we think the current mode takes, rather than for the
 *              current bank set. This causes a problem with the non-ENHANCED
 *              modes running in the background: all the pages will be mapped to
 *              the last bank and therefore not all the changes will be visible.
 *              For the time being, the only such known instances are 320x200x256
 *              mode on Trident adapters, which are during the set mode
 *              misidentified as requiring 4 banks, rather than 1. vvMaxBank has been
 *              changed to return 1 bank if the current BIOS mode is 13.
 *              The true solution would be to grow the page buffer for the current
 *              bank and invalidate the mapping on the set bank register changes,
 *              so that vvFaultHook occurs again when that page is touched.
 *
 *              @senja: an unpdated view on the above subject.
 *              Virtualizing planar modes which span across banks requires that
 *              the buffer is grown to the maximum at once and that page states
 *              across all the banks are set. However, only the current bank is
 *              mapped and transffered into the physical pages given to the VDM
 *              for the virtualization. Previous code was falsly mapping every page
 *              in every bank to the physical page, having the effect of leaving the
 *              maximum bank mapped and transffered instead of the current bank.
 *              I've finally put an end to it. Assuming that the MapBank works
 *              correctly in the shadow io hook functions, the virtualization of the
 *              of planar modes which require more than 1 bank should be successful.
 *              However, the virtualization will be extremelly slow, since we don't
 *              have to this day a physpagemapupdate interface which would allow us
 *              to take advantege of available memory in the banks>1 on SVGA adapters.
 *
 * INPUT         = pvdmFault -> linear address within the VDM
 *
 * OUTPUT        = None
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 * PSEUDO-CODE
 *                 update video state information
 *                 if memory-state is MEMORY_GRFX
 *                   freeze VDM
 *                 else
 *                   reallocate virtual buffer plane
 *                   if error
 *                     freeze VDM
 *                   else
 *                     map in virtual memory
 *                     set page-state to memory-state
 *
 ***************************************************************************/

VOID HOOKENTRY VVFaultHook(PVDM pvdmFault)
{

#ifdef   VDDSTRICT
  register INT i;
#endif
  BOOL fSuccess;
  register INT iPage;
  register INT iPlane;
  INT iBank = BANK0,iMaxBank = MAX_VGA_BANK, iCurrentBank = BANK0;                 /*            */


  if (VDMData.flVDMVideo&VDM_IGNOREFAULT)
  {
    vvMapPage(pvdmFault,
              NULL,
              ALL_PLANES,
              iBank,                                            /*            */
              MAX_VGA_BANK,                                     /*            */
              VDHMT_BLACK_HOLE);
    return ;
  }
  RequestMutexSem(VDMData.hmxVideoState);

  /*
  ** Update our knowledge about the VDM's memory state.
  ** Both memory state and bank settings will be captured. MMIO map updated.
  */
  vvUpdateAll( CURRENT_VDM,
               FALSE );

  /*
  ** If page is INSIDE the active VRAM range.
  */
  if ((PBVDM)pvdmFault >= VDMData.pvdmPhysVRAMActive && (PBVDM)pvdmFault <
     VDMData.pvdmPhysVRAMActive+VDMData.npgPhysVRAMActive *PAGESIZE)
  {

    /*
    ** Get index of faulty page
    */

    fSuccess = TRUE;
    iPage = (ULONG)((PBVDM)pvdmFault-VDMData.pvdmPhysVRAMActive)/PAGESIZE;

    /*
    ** Determine what virtual plane is in use right now
    */

    iPlane = PLANE0;

#ifdef   SVGA
    iCurrentBank = (INT) VDMData.VdmSVGA.usCurrentBank;       /*            */
    iMaxBank = (INT) VDMData.VdmSVGA.usMaxBanks;              /*            */
    /*
    ** If the current hardware state is not in order, delay making
    ** decisions, treat this is a VGA mode
    */
    if (iCurrentBank >= iMaxBank)
    {
      iCurrentBank = BANK0;                 /*            */
      iMaxBank = MAX_VGA_BANK;
    }

#endif /* SVGA */
    if (VDMData.mstateVideo == MEMORY_FONT)
      iPlane = PLANE2;
    else if (VDMData.mstateVideo == MEMORY_GRFX)
      iPlane = ALL_PLANES;

#ifdef EGAVGA

    if( !(VDMData.flVDMVideo & VDM_FGND) )
    {

      /*
      ** If windowed graphics is not allowed, zap the success flag
      */

      if( !(VDMData.flVDMVideo & VDM_GRFXALLOWED) )
        if( VDMData.mstateVideo > MEMORY_FONT )
          fSuccess = FALSE;

      /*
      ** If we need a physical page, get it first;  failure will
      ** generally mean that PM isn't foreground, so we can't have any
      */

      if( fSuccess )
        if( VDMData.mstateVideo == MEMORY_GRFX )
          fSuccess = vvGetPhysPage( iPage );

      if( !fSuccess && (vvdrqVideo.vvd_nbReserved != ALL_RESERVED) )
      {
        ReleaseMutexSem(VDMData.hmxVideoState);                 /*          */
        return;                                                 /*          */
      }
    }
#endif

    /*
    ** Grow the plane(s) if we're still in good shape;  if we're
    ** foreground and the "on-demand memory allocation" property isn't
    ** set, then the buffer will have already been preallocated, and the
    ** call will always succeed.
    */

#ifdef   SVGA
    if( VDMData.flVDMXVideo & VDMX_ENHANCEDMODE )
    {                                                           /*          */
      if( fSuccess =                                            /*          */
            vvGrowLinearBuffer( CURRENT_VDM,                    /*          */
                                max( iCurrentBank + 1,          /*           */
                                     iMaxBank ) ) )             /*          */
        vvMapBank( iCurrentBank );                              /*           */

    }
    else
    {
      for( iBank = 0;
           (iBank < iMaxBank) && fSuccess;
           iBank++ )
      {
         fSuccess = vvGrowBuffer( CURRENT_VDM,
                                     iPlane,
                                     iPage + 1,
                                     iBank );
      }
      /*
      ** If all allocation(s) are successful,
      ** transfer and map the page
      */
      if (fSuccess)
         vvValidatePage( iPage,
                         iCurrentBank ,         /*            */
                         iMaxBank);             /*            */
    }
    if (!fSuccess)
    {

#ifdef   EGAVGA

      /*
      ** Don't worry, this call is a no-op if no physpage was allocated
      */
      vvFreePhysPage(CURRENT_VDM,
                     iPage);
#endif /* EGAVGA */

      /*
      ** If this was due to a VDD fault, then we have no choice
      ** but to block the VDD until the session manager and/or the
      ** display driver "kick" us into retrying the operation
      */

      vvFreezeVDM(CURRENT_VDM,
                  VDMData.flVDMXVideo&VDMX_VDDFAULT);
    }

#else /* SVGA */

    if (fSuccess)
      fSuccess = vvGrowBuffer(CURRENT_VDM,
                              iPlane,
                              iPage+1,
                              BANK0);

    /*
    ** If all allocation(s) are successful, transfer and map the page
    */

    if (fSuccess)
      vvValidatePage(iPage,
                     BANK0,
                     MAX_VGA_BANK);            /*            */

    /*
    ** Something failed, so leave the page invalid and freeze the VDM
    */

    else
    {

#ifdef EGAVGA

      /*
      ** Don't worry, this call is a no-op if no physpage was allocated
      */

      vvFreePhysPage(CURRENT_VDM,
                     iPage);
#endif /* EGAVGA */

      /*
      ** If this was due to a VDD fault, then we have no choice
      ** but to block the VDD until the session manager and/or the
      ** display driver "kick" us into retrying the operation
      */

      vvFreezeVDM(CURRENT_VDM,
                  VDMData.flVDMXVideo&VDMX_VDDFAULT);
    }
#endif /* SVGA */
  }

  /*
  ** Otherwise, the page is OUTSIDE the active VRAM range...
  */

  else
#ifdef SVGA                     /*            */
    /*
    ** The page may be a MMIO page, map it in. MMIO region must be mutually
    ** exlusive of the active VRAM region and still rezide in the a0000-bffff
    ** Otherwise, there is no need to map them, since the VDM must have acquired
    ** access otherwise (DPMI) and we will never see the fault hook for that range
    ** since we only Reserve and allocate the a0000-bffff region.
    */
    if (VDMData.VdmSVGA.pvdmMMIOActive &&
       ((PBVDM)pvdmFault >= VDMData.VdmSVGA.pvdmMMIOActive) &&
       ((PBVDM)pvdmFault < (VDMData.VdmSVGA.pvdmMMIOActive+VDMData.VdmSVGA.npgMMIOActive*PAGESIZE)))

    {
      ULONG flOption;
      iPage = (ULONG)((PBVDM)pvdmFault-VDMData.VdmSVGA.pvdmMMIOActive)/PAGESIZE;
      iPlane = PLANE0;
      /*
      ** The page will be mapped according to the accelerator instructions.
      ** If the access is invalid, the accelerator mapping functions will take
      ** care of freezing and mode updates and will return VDHMT_BLACK_HOLE so
      ** that we map the page to invalid. It is also possible, that the accelerator VVID
      ** may desire to map to black hole and keep going without freezing the VDM.
      */
      vvAcceleratorUpdateMemoryMapState(CURRENT_VDM, pvdmFault, SSToDS( &flOption));
      vvMapPage(pvdmFault,
                pvdmFault,
                iPlane,
                BANK0,
                MAX_VGA_BANK,
                flOption);
    }
    else
#endif
  {

#ifdef VDDDEBUG
    PRINTDEBUG("Warning: Inactive video page access (%08x)\n",
               pvdmFault);

   #ifdef   VDDDEBUGALL
    PRINTDEBUG("Warning: Active region: %08x - %08x\n",
               VDMData.pvdmPhysVRAMActive,
               (VDMData.pvdmPhysVRAMActive+VDMData.npgPhysVRAMActive *PAGESIZE
                  ));
   #endif
#endif /* VDDDEBUG */

    /*
    ** Map page to "don't care" memory (the access is meaningless)
    */

    vvMapPage(pvdmFault,
              NULL,
              ALL_PLANES,
              BANK0,                                                    /*            */
              MAX_VGA_BANK,                             /*            */
              VDHMT_BLACK_HOLE);

    /*
    ** Enable motion trapping for unknown faults; as soon as key ports
    ** are reprogrammed, all inactive pages will be unmapped once again
    */

    VDMData.flVDMVideo |= VDM_UNKNOWNFAULT;

#ifdef VGA

    /*
    ** This only needs to be called for VGA, because
    ** we're always trapping the relevant ports on an EGA
    */

    VVSpecialCaseIO( NULL,
                     NULL );
#endif /* VGA */
  }

  ReleaseMutexSem(VDMData.hmxVideoState);

#ifdef VDDSTRICT
#ifdef EGAVGA
  for (i = 0; i < MAX_PAGESPERPLANE; i++)
    if( ahPhysPage [i] == (ULONG)(VDMData.hvdmVideo) )
      AssertTRUE( VDMData.aiVirtToPhys [aiPhysToVirt [i]] == i );
#endif /* EGAVGA */
#endif /* VDDSTRICT */
}

/***************************************************************************
 *
 * FUNCTION NAME = vvSetFaultHooks()
 *
 * DESCRIPTION   = Install/remove page hooks
 *
 *                 This routine installs/removes a VVD page-fault handler
 *                 appropriate for the current session state (ie, fgnd or
 *                 bgnd).
 *
 * INPUT         = fInstall == TRUE to install, FALSE to remove
 *                 fWait    == TRUE to wait if out of memory, FALSE to return error
 *
 * OUTPUT        = TRUE if hook installed, FALSE if error
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 ***************************************************************************/

BOOL PRIVENTRY vvSetFaultHooks(
  BOOL fInstall,
  BOOL fWait )
{

  /*
  ** Install/remove the hook
  */

  if (fInstall)
  {

#ifdef   EGAVGA

    if (flVVD&VVD_MONOPRESENT)
    {

      /*
      ** FLAG: 22-May-90
      ** VDHInstallFaultHook doesn't really return error on
      ** internal allocation failures -- it currently just panics.
      ** However, because this is inconsistent with our architecture,
      ** it'll have to be changed, so I'm taking care of this problem now.
      */

      while (!VDHInstallFaultHook(CURRENT_VDM,
                                  pvdmPhysVRAM,
                                  PAGESFROMBYTES(MONOMEM_START-EGAVGAMEM_START
                                     ),
                                  VVFaultHook,   /*                         */
                                  NULL))
      {                                          /*                         */

        if (!fWait || !vvFreezeVDM(CURRENT_VDM,
                                   TRUE))
          return  FALSE;
      }


      while (!VDHInstallFaultHook(CURRENT_VDM,
                                  (PBVDM)(MONOMEM_START+MONOMEM_LEN),
                                  npgPhysVRAM-PAGESFROMBYTES(MONOMEM_START+
                                     MONOMEM_LEN-EGAVGAMEM_START),
                                  VVFaultHook,   /*                         */
                                  NULL))
      {                                          /*                         */

        if (!fWait || !vvFreezeVDM(CURRENT_VDM,
                                   TRUE))
          return  FALSE;
      }
    }

    else
#endif


      while (!VDHInstallFaultHook(CURRENT_VDM,
                                  pvdmPhysVRAM,
                                  npgPhysVRAM,
                                  VVFaultHook,   /*                         */
                                  NULL))
      {                                          /*                         */

        if (!fWait || !vvFreezeVDM(CURRENT_VDM,
                                   TRUE))
          return  FALSE;
      }
  }

  else
  {

#ifdef   EGAVGA

    if (flVVD&VVD_MONOPRESENT)
    {
      VDHRemoveFaultHook(CURRENT_VDM,
                         pvdmPhysVRAM,
                         PAGESFROMBYTES(MONOMEM_START-EGAVGAMEM_START),
                         VVFaultHook);
      VDHRemoveFaultHook(CURRENT_VDM,
                         (PBVDM)(MONOMEM_START+MONOMEM_LEN),
                         npgPhysVRAM-PAGESFROMBYTES(MONOMEM_START+MONOMEM_LEN-
                            EGAVGAMEM_START),
                         VVFaultHook);
    }

    else
#endif
         VDHRemoveFaultHook(CURRENT_VDM,
                            pvdmPhysVRAM,
                            npgPhysVRAM,
                            VVFaultHook);
  }
  return  TRUE;
}
#pragma  END_SWAP_CODE

