/*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  GALE                                                            //J-TS00V
#include "vvgale.h"                                                     //J-TS00V
#endif  //GALE                                                          //J-TS00V

#ifdef   VDDSTRICT
MODNAME = __FILE__;
#endif

#ifdef   SVGA
extern ULONG ulSVGAAdapterType;
extern PFNSVGAGETBANK apfnSVGAGetBank[];
extern PFNSVGASETBANK apfnSVGASetBank[];
#endif
extern ULONG flVVD;
#pragma  BEGIN_SWAP_DATA
extern PBYTE pPhysVRAM;
extern PBVDM pvdmPhysVRAM;
extern ULONG npgPhysVRAM;
extern PSTATE apstateMap[];

#ifdef   VDDSTRICT

   #ifdef   EGAVGA
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
#pragma  BEGIN_SWAP_CODE

/***************************************************************************
 *
 * 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;
}

/***************************************************************************
 *
 * 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.
 *
 * 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 = 1, iCurrentBank;                 /*            */

#ifdef  VTEXT                                                           //J-TS00V
  if (vvMapVtextPVB(pvdmFault))                                         //J-TS00V
    return;                                                             //J-TS00V
#endif  //VTEXT                                                         //J-TS00V
                                                                        //J-TS00V
#ifdef  GALE                                                            //J-TS00V
  if ((VDMData.flVDMVideo & VDM_IGNOREFAULT) ||                         //J-TS00V
      (VDMData.flVDMGale && !(VDMData.flVDMVideo & VDM_FGND) &&         //J-TS00V
       ((VDMData.ulBIOSMode == 0x03) ||                                 //J-TS00V
        (VDMData.ulBIOSMode == 0x73))))                                 //J-TS00V
#else   //GALE                                                          //J-TS00V
  if (VDMData.flVDMVideo&VDM_IGNOREFAULT)
#endif  //GALE                                                          //J-TS00V
  {
    vvMapPage(pvdmFault,
              NULL,
              ALL_PLANES,
              iBank,                                            /*            */
              VDHMT_BLACK_HOLE);
    return ;
  }
#ifdef  GALE                                                            //J-TS00V
  if (VDMData.flVDMGale                                    &&           //J-TS00V
      ((VDMData.ulBIOSMode == 0x03) ||                                  //J-TS00V
       (VDMData.ulBIOSMode == 0x73))                       &&           //J-TS00V
      ((PBVDM)pvdmFault >= VDMData.pvdmPhysVRAMActive)     &&           //J-TS00V
      ((PBVDM)pvdmFault <  VDMData.pvdmPhysVRAMActive +                 //J-TS00V
                           VDMData.npgPhysVRAMActive * PAGESIZE)) {     //J-TS00V
    RequestMutexSem(VDMData.hmxVideoState);                             //J-TS00V
    iPage = (ULONG)((PBVDM)pvdmFault - VDMData.pvdmPhysVRAMActive)      //J-TS00V
          / PAGESIZE;                                                   //J-TS00V
    for (iPlane = 0; iPlane < 4; iPlane++)                              //J-TS00V
      VDMData.aapstate[BANK0][iPage][iPlane]                            //J-TS00V
                            |= PAGE_INVRAM | PAGE_GRFX; //J-TS00V       //J-TS00V
    vvMapPage(pvdmFault, pvdmFault, PLANE0, BANK0, VDHMT_PHYSICAL);     //J-TS00V
    ReleaseMutexSem(VDMData.hmxVideoState);                             //J-TS00V
    return;                                                             //J-TS00V
  }                                                                     //J-TS00V
#endif  //GALE                                                          //J-TS00V
  RequestMutexSem(VDMData.hmxVideoState);
#ifdef VTEXT                                                            //J-TS0120
#ifdef SVGA                                                             //J-TS0120
  if ((VDMData.GaleData.flVtext & VDMV_VTEXT_INSTALLED) &&              //J-TS0120
      (ulSVGAAdapterType == S3_ADAPTER)                 &&              //J-TS0120
      (VDMData.flVDMXVideo & VDMX_NOIOTRAPPING))                        //J-TS0120
  vvUpdateAll(CURRENT_VDM,                                              //J-TS0120
              TRUE);                                                    //J-TS0120
  else                                                                  //J-TS0120
#endif  //SVGA                                                          //J-TS0120
#endif  //VTEXT                                                         //J-TS0120
  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;

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

#ifdef   SVGA

    else

        if ((VDMData.mstateVideo == MEMORY_GRFX) ||             /*            */
            (VDMData.mstateVideo == MEMORY_GRFX256))            /*            */
        {                                                       /*            */
          iMaxBank = vvMaxBanks(CURRENT_VDM);                   /*            */
          iPlane = ALL_PLANES;                                  /*            */
        }                                                       /*            */
    /*
    ** Get the current bank and remember it.                      
    */
    iCurrentBank = iBank = (*apfnSVGAGetBank[ulSVGAAdapterType])(CURRENT_VDM,
                                                  TRUE);/* get write bank   */

#else

    else

      if (VDMData.mstateVideo == MEMORY_GRFX)

        iPlane = ALL_PLANES;
#endif

#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(iBank+1,iMaxBank)))  /*            */
            vvMapBank(iBank);                                           /*            */
                                                                        /*            */
    } else                                                              /*            */

    for (iBank = 0; iBank < iMaxBank && fSuccess; iBank++)
    {

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

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

        vvValidatePage(iPage,
                       iBank);
    }

    /*
    ** Restore the bank, as in the process of validation it may have been set
    ** to another bank.                                                   
    */

    (*apfnSVGASetBank[ulSVGAAdapterType])(CURRENT_VDM,          /*            */
                                          iCurrentBank,
                                          TRUE);

    if (!fSuccess)
    {

   #ifdef   EGAVGA

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

      vvFreePhysPage(CURRENT_VDM,
                     iPage);
   #endif

      /*
      ** 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

    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);

    /*
    ** 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

      /*
      ** 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
  }

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

  else
  {

#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

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

    vvMapPage(pvdmFault,
              NULL,
              ALL_PLANES,
              BANK0,                                                    /*            */
              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
  }
  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
#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 iBank;                                     /*                         */

#ifdef   SVGA
  iBank = (*apfnSVGAGetBank[ulSVGAAdapterType])(CURRENT_VDM,
                                                TRUE);/* get write bank     */

#else
  iBank = BANK0;
#endif

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

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

 #ifdef SVGA                                                    /*            */

    /*
    **  If this pointer is still valid, we were previously in a 256-colour
    **  mode and should now release it, and invalidate the video buffer
    **  address range.
    */

  if (!fEnable && VDMData.pLinearBuffer) {

    vvUnMapBuffer();
    vvShrinkBuffer(CURRENT_VDM, TRUE);
    return;

  }

 #endif                                                         /*            */

  RequestMutexSem(VDMData.hmxVideoState);
  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[iBank][iPage][iPlane]&PAGE_INVRAM)
          {                                    /*                         */
            pMapTo = pvdm;
            flMap = VDHMT_PHYSICAL;
          }
        }

        else
        {

          /*
          ** Background VDMs get virtual memory
          */

#ifdef   EGAVGA

          if ((*(PULONG)(VDMData.aapstate[iBank][iPage])&ALL_PHYSICAL) ==
             ALL_PHYSICAL)
          {                                    /*                         */
            iPlane = ALL_PLANES;
            flMap = VDHMT_PHYSICAL;
            pMapTo = vvdrqVideo.vvd_pPhysVRAM+VDMData.aiVirtToPhys[iPage]
               *PAGESIZE;
          }

          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,
              BANK0,                                            /*            */
              flMap);
    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 iBank,BOOL fVRAM)
{
  register INT iPage;
  register INT iPlane;
  register PVDMDATA pvd = pVDMData(hvdm);
  ULONG ulState;

  /*
  **           
  **
  ** If we're not trapping IO, we don't know how much VRAM has
  ** been touched by mode sets etc. so ensure we save it all.
  */
  if (!fVRAM && (pvd->flVDMXVideo & VDMX_NOIOTRAPPING))
  {

    vvGrowBuffer(hvdm, ALL_PLANES, GROW_MAXIMUM, iBank);

    if (pvd->mstateVideo <= MEMORY_FONT)
      ulState = (PAGE_TEXT|PAGE_NONE<<8|PAGE_FONT<<16|PAGE_NONE<<24)|ALL_INVRAM;
    else if ((pvd->ulBIOSMode&~BIOSVINFO_DONTCLEAR) == BIOSVMODE_CO320X200X256)
      ulState = *(PULONG)(pvd->aapstate[BANK0]);
    else if (pvd->mstateVideo == MEMORY_GRFX256)
      ulState = ALL_GRFX256|ALL_INVRAM;
    else
      ulState = ALL_GRFX|ALL_INVRAM;

    for (iPage = 0; iPage < MAX_PAGESPERPLANE; iPage++)
      *(PULONG)(pvd->aapstate[iBank][iPage]) = ulState;

  }
/*75437 #ifdef SVGA                                                          74167 */
#ifdef VGA                                                           /* 75437 */
  /* Save 2 pages of plane 2 font VRAM when CGA RESTRICTION             74167 */
  else if (!fVRAM && (pvd->flVDMXVideo & VDMX_CGARESTRICT))          /* 74167 */
  {                                                                  /* 74167 */
    if (pvd->mstateVideo <= MEMORY_FONT)                             /* 74167 */
    {                                                                /* 74167 */
       for (iPage = 0; iPage <= 1; iPage++)                          /* 74167 */
          pvd->aapstate[iBank][iPage][PLANE2] |= PAGE_INVRAM;        /* 74167 */
    }                                                                /* 74167 */
  }                                                                  /* 74167 */
#endif                                                               /* 74167 */

  /*
  ** True copy state unknown at this point
  */

  pvd->mstateCopy = MEMORY_NONE;

  /*
  ** For every plane
  */

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

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

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

      if (!vvTransferPage(hvdm,
                          iPage,
                          iPlane,
                          iBank,
                          fVRAM))
        return  FALSE;
  }
  return  TRUE;
}

#ifdef SVGA

/***************************************************************************
 *
 * FUNCTION NAME = vvTransferLinearBuffer()
 *
 * DESCRIPTION   = Transfer all reqd banks to/from physical VRAM
 *
 * INPUT         = hvdm  -> VDM
 *                 iMaxBanks == no of 64k banks to transfer
 *                 fVRAM == TRUE to transfer TO VRAM, FALSE to transfer FROM
 *
 * OUTPUT        = TRUE if successful, FALSE if not
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 * PSEUDO-CODE
 *      if bank state is NOT INVALID
 *          exchange memory with buffer
 *      otherwise
 *          move memory to/from buffer
 *
 *      for all banks
 *          if bank-state is valid
 *            if bank-state differs from current copy-state
 *              set copy-state according to bank-state
 *            exchange bank with physical VRAM
 *
 ***************************************************************************/

BOOL PRIVENTRY vvTransferLinearBuffer(register HVDM hvdm,INT iMaxBanks,BOOL fVRAM)
{
        register INT    iBank;
        register PVDMDATA pvd = pVDMData(hvdm);
        PBYTE           pbSrc, pbDst;
        PBYTE           pbPhys, pbVirt;
        BYTE            bMiscOut;

        if (!(pvd->flVDMVideo & VDM_FGND) ||            /* must be foreground             */
             (!pvd->pLinearBuffer && fVRAM))            /* must have something to restore */
#ifdef  VTEXT                                                           //J-TS0826
          if (pvd->GaleData.flVtext & VDMV_SUPPORTED_TEXT_MODE)         //J-TS0826
            return TRUE;                                                //J-TS0826
          else                                                          //J-TS0826
#endif  //VTEXT                                                         //J-TS0826
            return FALSE;

        vvGrowLinearBuffer(hvdm, iMaxBanks);            /*            */

        pvd->mstateCopy = MEMORY_NONE;

        pbPhys = pPhysVRAM;
        pbVirt = pvd->pLinearBuffer;

        /*** Prepare hardware for copy ***/
        vvSetCopyState(hvdm, PAGE_GRFX256, PLANE0);

        if (!((bMiscOut = INB(0x3CC)) & 2))     /* ensure VRAM enabled */
            OUTB(PORT_MISCOUT, bMiscOut | 2);

        for (iBank=0; iBank < iMaxBanks; iBank++, pbVirt+=BANKSIZE) {

            (*apfnSVGASetBank[ulSVGAAdapterType])(hvdm, iBank, fVRAM);

            if (fVRAM) {
                pbSrc = pbVirt;
                pbDst = pbPhys;
                pvd->abstate[iBank] |= BANK_INVRAM;
            } else {
                pbSrc = pbPhys;
                pbDst = pbVirt;
                pvd->abstate[iBank] &= ~BANK_INVRAM;
            }

            if (pvd->abstate[iBank] & BANK_INVALID) {

                pvd->abstate[iBank] &= ~BANK_INVALID;
                /*** Move the page to/from physical VRAM ***/
                vdhMoveMem(pbSrc, pbDst, 1, 1, BANKSIZE, FALSE);

            } else

                /*** Exchange the page with physical VRAM ***/
                vdhMoveMem(pbSrc, pbDst, 1, 1, BANKSIZE, TRUE);

        }

        return TRUE;
}

#endif

/***************************************************************************
 *
 * 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
  */

  s = pvd->aapstate[iBank][iPage][iPlane];       /*                         */

  /*
  ** If this isn't a foreground VDM or a PHYSICAL page, skip it
  */

  if (s&PAGE_TYPE && (pvd->flVDMVideo&VDM_FGND || s&PAGE_PHYSICAL))
  {

    /*
    ** Page (not) transferred need not be (not) transferred again
    */

#ifdef  GALE                                                            //J-TS00V
    if ((fVRAM != !!(s & PAGE_INVRAM)) ||                               //J-TS00V
        (pvd->flVDMGale && pvd->GaleData.GaleInternalAccess))           //J-TS00V
#else   //GALE                                                          //J-TS00V
    if (fVRAM != !!(s&PAGE_INVRAM))
#endif                                                                  //J-TS00V
    {

      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(pvd->flVDMXVideo & VDMX_ENHANCEDMODE)        /*            */
        (*apfnSVGASetBank[ulSVGAAdapterType])(hvdm,
                                              iBank *4+iPlane,
                                              fVRAM);

      else
        (*apfnSVGASetBank[ulSVGAAdapterType])(hvdm,
                                              iBank,
                                              fVRAM);/*                  END*/
   #endif

   #ifdef   SVGA                                 /*                         */

      if (s&PAGE_PHYSICAL)
      {                                          /*                         */
        pbPhys += pvd->aiVirtToPhys[iPage]*PAGESIZE;/*                      */

   #else                                         /*                         */

      if (s&PAGE_PHYSICAL ||                     /*                         */
         flVVD&VVD_VRAMISTOAST ||                /*                         */
         !vvdrqVideo.vvd_pfCtrlOwned)
      {                                          /*                         */

        if (s&PAGE_PHYSICAL)                     /*                         */
          pbPhys += pvd->aiVirtToPhys[iPage]*PAGESIZE;

        else                                     /*                         */
          pbPhys += iPage *PAGESIZE;             /*                         */
   #endif                                        /*                         */

        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
      {
        pbPhys += iPage *PAGESIZE;

        /*
        ** Exchange the page with physical VRAM
        */

#ifdef  GALE                                                            //J-TS00V
        if (pvd->flVDMGale) {                                           //J-TS00V
          if (fVRAM)                                                    //J-TS00V
            vdhMoveMem(pbVirt, pbPhys, 1, 1, PAGESIZE, FALSE);          //J-TS00V
          else                                                          //J-TS00V
            vdhMoveMem(pbPhys, pbVirt, 1, 1, PAGESIZE, FALSE);          //J-TS00V
        } else                                                          //J-TS00V
#endif  //GALE                                                          //J-TS00V
        vdhMoveMem(pbVirt,
                   pbPhys,
                   1,
                   1,
                   PAGESIZE,
                   TRUE);
      }
    }
  }

  else
    fVRAM = FALSE;

  /*
  ** Finally, update the VRAM status of the page
  */

#ifdef  GALE                                                            //J-TS00V
  if (!pvd->GaleData.GaleInternalAccess)                                //J-TS00V
#endif  //GALE                                                          //J-TS00V
  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
 *
 * 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 iBank)
{
  PBVDM pvdm;
  PBYTE pbPlane;
  INT iPlaneMap,iMode;
  register INT iPlane;
  HVDM hvdmPrevious = VDMData.hvdmVideo;

  AssertTRUE(iPage < MAX_PAGESPERPLANE);
  iPlaneMap = PLANE0;

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

#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 (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
       ];
    VDMData.aapstate[iBank][iPage][iPlane] |= PAGE_PHYSICAL
       *!!(VDMData.flVDMVideo&VDM_PHYSPAGE);
    vvTransferPage(CURRENT_VDM,
                   iPage,
                   iPlane,
                   iBank,
                   TRUE);

#ifdef   SVGA

    /*
    ** If its not a graphics mode, or its 320x200x256, fall out the loop
    */

    if (!((VDMData.mstateVideo == MEMORY_GRFX) ||
        (VDMData.mstateVideo == MEMORY_GRFX256)) ||
        ((VDMData.mstateVideo == MEMORY_GRFX256) &&
         ((VDMData.ulBIOSMode&~BIOSVINFO_DONTCLEAR) ==
           BIOSVMODE_CO320X200X256)))      /*            */
      break;

#else

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

    if (VDMData.mstateVideo != MEMORY_GRFX)
      break;
#endif
  }

#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

  if (VDMData.aapstate[iBank][iPage][iPlaneMap]&PAGE_PHYSICAL)
  {
    pbPlane = vvdrqVideo.vvd_pPhysVRAM+VDMData.aiVirtToPhys[iPage]*PAGESIZE;
    vvMapPage(pvdm,
              pbPlane,
              ALL_PLANES,
              BANK0,                                    /*            */
              VDHMT_PHYSICAL);
  }

  else
#endif

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

    else
    {
      pbPlane = VDMData.apPlane[iBank][iPlaneMap]+iPage *PAGESIZE;
      vvMapPage(pvdm,
                pbPlane,
                iPlaneMap,
                BANK0,                                  /*            */
                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,
                                INT iBank)     /*                         */
{
  PBVDM pvdm;
  register INT iPlane;
  HVDM hvdmPrevious;
  register PVDMDATA pvd = pVDMData(hvdm);

  AssertTRUE(iPage < MAX_PAGESPERPLANE);
  AssertTRUE(pvd->mstateBuffer == MEMORY_GRFX);

  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 (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,
              BANK0,                                    /*            */
              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  =
 *
 ***************************************************************************/

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

  mstateTmp = pvd->mstateVideo;

  /*
  ** 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
    */

    if (hvdm == CURRENT_VDM && pvd->flVDMVideo&VDM_STATEINIT)
      VVEnableBuffer(TRUE,
                     NULL);
    fChange++;
  }

  /*
  ** Once the VDM becomes foreground with Multi-Plane graphics mode
  ** set, we have to insure that PM is told to repaint the user
  ** session switch back to PM.  Multi-plane grahpics modes are
  ** BIOS mode 0Dh to 13h and it should be indicated in mstateVideo
  ** with the value of MEMORY_GRFX or MEMORY_GRFX256.
  **
  ** Note:  By default DOS application using BIOS mode 13h
  **        (320 x 200 x 256 color) has the ability to update
  **        all 4 video planes, so therefore we must include
  **        MEMORY_GRFX256 as part of this check.
  */

/*  if (pvd->flVDMVideo & VDM_FGND)                                         */
/*     if (mstateTmp == MEMORY_GRFX ||                                      */
/*         mstateTmp == MEMORY_GRFX256)                                     */
/*        flVVD |= VVD_VRAMISTOAST;                                         */

  pvd->mstateBuffer = mstateTmp;
  return  fChange;
}

/***************************************************************************
 *
 * FUNCTION NAME = vvMapPage()
 *
 * DESCRIPTION   = Remaps a page, saving dirty info first
 *
 *
 * 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 iBank,                             /*            */
                         FLAGS flOption)
{
  INT ivdmPhysPage;
  register INT iPage;

  /*
  ** 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[iBank][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])
  {

    while (!VDHMapPages(&VDMData.vdhms,
                        &VDMData.avdhmt[ivdmPhysPage],
                        flOption)
           && (flOption != VDHMT_INVALID))      /*            */

      if (!vvFreezeVDM(CURRENT_VDM,
                       TRUE))
        break;

    VDMData.ulMapCur[ivdmPhysPage] = VDMData.vdhms.vdhms_laddr|flOption;
  }

  /*
  ** Lastly, update page state table
  */

  if (iPage >= 0)
  {

    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;
    }
  }
}

#ifdef SVGA                                                     /*            */

/***************************************************************************
 *
 * 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;
  FLAGS         flDirty;
  BOOL          fSuccess = FALSE;                       /*            */

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

  vdhms.vdhms_hobj  = 0;
  vdhmt.vdhmt_laddr = 0xA0000L;
  vdhmt.vdhmt_hmap  = 0;
  vdhmt.vdhmt_cpg   = BANKSIZE/PAGESIZE;

  vvUnMapBuffer();                                      /*            */

  while (!fSuccess)                                     /*            */
  {
    if (VDMData.flVDMVideo & VDM_FGND) {
       flOption = VDHMT_PHYSICAL;
       vdhms.vdhms_laddr = 0xA0000L;
    } else {
                                                        /*            start */
       if ((iBank+1) * PAGESFROMBYTES(64L*1024L) > VDMData.ulLinearPages)
         if (!vvGrowLinearBuffer(CURRENT_VDM, iBank+1))
         {
           vvFreezeVDM(CURRENT_VDM, FALSE);
           return;                      /* worst case, shouldn't happen! */
         }
         else
           flDirty = FALSE;             /* realloc probably lost it */
                                                        /*              end */
       if (flDirty)
         vvAddEvent(CURRENT_VDM,
                    VVDEVENT_LVB,
                    SSToDS(&flDirty),
                    EVENT_ASYNC);

       vdhms.vdhms_laddr = (ULONG)VDMData.pLinearBuffer + iBank*BANKSIZE;
       flOption = VDHMT_LINEAR;
    }

    if (!(fSuccess = VDHMapPages(SSToDS(&vdhms), SSToDS(&vdhmt), flOption)) )
       vvFreezeVDM(CURRENT_VDM, TRUE);
  }                                                     /*            */

  for (vdhms.vdhms_laddr = 0xA0000L;                    /*            */
       vdhms.vdhms_laddr < 0xB0000L;                    /*            */
       vdhms.vdhms_laddr += 0x1000L)                    /*            */
    VDMData.ulMapCur[(vdhms.vdhms_laddr-0xA0000L) >> 12] = vdhms.vdhms_laddr|flOption;

  /*** Lastly, update bank state table ***/
  if (flOption == VDHMT_LINEAR)
    VDMData.abstate[iBank] |= BANK_VIRTUAL;
  else
    VDMData.abstate[iBank] |= BANK_INVRAM;

  VDMData.flVDMVideo &= ~VDM_UNMAPPED;
}

/***************************************************************************
 *
 * 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)
{
  VDHMAPTARGET  vdhmt;
  PBVDM pvdm;
  register INT iPage;
  INT iPlane;

  if (VDMData.flVDMVideo & VDM_UNMAPPED)
    return;

  vdhmt.vdhmt_laddr = 0xA0000L;
  vdhmt.vdhmt_hmap = 0;
  vdhmt.vdhmt_cpg  = 1;
  for (iPage = 0;
       iPage < BANKSIZE/PAGESIZE;
       iPage++, vdhmt.vdhmt_laddr += 0x1000L) {

    VDHMapPages(NULL, SSToDS(&vdhmt), VDHMT_INVALID);
    VDMData.ulMapCur[iPage] = vdhmt.vdhmt_laddr|VDHMT_INVALID;
    VDMData.avdhmt[iPage].vdhmt_hmap = NULL;

    for (iPlane=0; iPlane<MAX_PLANES; iPlane++)
      VDMData.aapstate[BANK0][iPage][iPlane] &= ~(PAGE_VALID|PAGE_INVRAM);

  }
  VDMData.flVDMVideo |= VDM_UNMAPPED;
}

#endif

/***************************************************************************
 *
 * 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                                                        /*            */

  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
  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->VdmDataX.nDiffPrev == 1)
    {

      if (pvvlvb->vvl_rcl.yTop-pvd->rclDiffPrev.yBottom <= 1)
        pvd->flVDMVideo &= ~VDM_PHYSPAGECHK;
      pvd->rclDiffPrev = pvvlvb->vvl_rcl;
    }
    pvd->VdmDataX.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
#pragma  END_SWAP_CODE

