/*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 = VVPHYS.C
 *
 * DESCRIPTIVE NAME = Virtual Video Physical Page Management
 *
 *
 * VERSION = V2.0
 *
 * DATE      10/18/89
 *
 * DESCRIPTION  This module contains the VVD's routines to manage physical
 *              video pages on behalf of background/windowed VDMs and the PM
 *              display driver.
 *
 *              ONLY INCLUDED IF EGAVGA!
 *
 * FUNCTIONS
 *              vvUpdatePhysPageMap()  Update the physical page usage table
 *              vvGetPhysPage()        Allocate (or steal) physical page for VDM
 *              vvFreePhysPage()       Free physical page for VDM
 *              vvFreeAllPhysPages()   Free all physical pages (for a dying VDM)
 *              vvGetPhysBytes()       Allocate physical video memory for PM
 *              vvFreePhysBytes()      Free physical video memory for PM
 *              VVUnmapPhysContext()   Unmap a physical page in current VDM
 *              VVUpdateContext()      Update VDM page mappings in context
 *              vvSetIOState()         Set hardware according to specified register list
 *              VVUpdatePhysContext()  Set up I/O state for current VDM
 *              vvQueryLatchAddress()  Determine suitable latch addres
 *              vvSaveLatches()        Save physical latch contents
 *              vvRestoreLatches()     Restore physical latch contents
 *
 * NOTES
 *
 *
 * STRUCTURES
 *
 * EXTERNAL REFERENCES
 *
 * EXTERNAL FUNCTIONS
 *
*/


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

#ifdef   VDDSTRICT
MODNAME = __FILE__;
#endif

extern ULONG flVVD;

#pragma  BEGIN_SWAP_DATA

extern ULONG nPMWindowedVDMs;
extern HHOOK hhookEventTimer;
extern LSENTRY lseHeadActive;
extern PBYTE pPhysVRAM;
extern PBYTE pvdmPhysVRAM;
extern HMXSEM hmxWindowedEvent;

extern PLE pleSEQIndx;                                          /*          */
extern PLE pleSEQData;
extern PLE pleGDCIndx;                                          /*          */
extern PLE pleGDCData;

#ifdef SVGA                                                     /*          */
extern RLE rleATIMemATI30;                                      /*          */
extern RLE rleCirrusMemSEQ07;                                   /*          */
extern RLE rleS3MemCR3A;                                        /*          */
extern RLE rleS3MemCR40;                                        /*          */
extern RLE rleVideo7MemSEQFC;                                   /*          */
extern PSACCELERATORINFO psCurAcceleratorInfo;                  /*          */
#endif /* SVGA */                                               /*          */

extern PSADAPTERINFO psCurAdapterInfo;                          /*          */

 /*
 **  Warning Note: SVGAGetBank() can return either the read or
 **  the write bank that is currently active. They may well
 **  not be the same. Most of the time in this module we guess.
 */
 /*
 **
 **  amstateMap maps MEMORY_* constants to MEMMODE_constants, which in
 **  turn serve as indexes in the bMode array of an rle (register list entry).
 **
 */

CONST MSTATE amstateMap[] =
{
    -1,                                 /* MEMORY_NONE (invalid) */
    MEMMODE_TEXT,                       /* MEMORY_TEXT */
    MEMMODE_FONT,                       /* MEMORY_FONT */
    MEMMODE_TEXT,                       /* MEMORY_CGAX */
    MEMMODE_GRFX,                       /* MEMORY_GRFX */
    MEMMODE_GRFX,                       /* MEMORY_GRFX2 */
    MEMMODE_GRFX256,                    /* MEMORY_GRFX256 */
};

 /*
 ** list of memory state-related ports
 **  aprleMemory lists the minimum set of registers that are needed to
 **  determine (or change) a VDM's memory state.  These lists are used by
 **  vvUpdateIOState(), as well as vvSetCopyState() and vvRestoreCopyState().
 */

RLE rleMemSEQMemMode =                                          /*          */
{ /* See vvUpdateMemoryState */
  &pleSEQData,
  PORTF_REQUIRED,
  REG_SEQMEMMODE, 0,
  REGCOND_RESETSEQ,
  /*!! How come "| SEQMEM_CHAIN4" is not below? */              /*          */
  /*!! How come not "~(SEQMEM_ODDEVENSAME)" below? */           /*          */
  SEQMEM_ODDEVENSAME,
  {!SEQMEM_ODDEVENSAME | SEQMEM_EXTENDED,
    SEQMEM_ODDEVENSAME | SEQMEM_EXTENDED,
    SEQMEM_ODDEVENSAME | SEQMEM_EXTENDED,
    SEQMEM_ODDEVENSAME | SEQMEM_EXTENDED | SEQMEM_CHAIN4},
  NULL,
  NULL,
};

RLE rleMemGDCMisc =                                             /*          */
{ /* See vvEGAHighResMode & vvUpdateMemoryState */
  &pleGDCData,                          /* 0x03ce (R/W) */
  PORTF_REQUIRED,
  REG_GDCMISC, 0,
  REGCOND_RESETSEQ,
  GDCMISC_ODDTOEVEN | GDCMISC_ADDRMASK,
  { GDCMISC_ODDTOEVEN | GDCMISC_64K_A0000,   /* MEMMODE_TEXT */
   !GDCMISC_ODDTOEVEN | GDCMISC_64K_A0000,   /* MEMMODE_FONT */
   !GDCMISC_ODDTOEVEN | GDCMISC_64K_A0000,   /* MEMMODE_GRFX */
   !GDCMISC_ODDTOEVEN | GDCMISC_64K_A0000},  /* MEMMODE_GRFX256 */
  NULL,
  NULL,
};

RLE rleMemSEQMapMask =                                          /*          */
{ /* See vvUpdateMemoryState */
  &pleSEQData,
  PORTF_REQUIRED,
  REG_SEQMAPMASK, 0,
  0,
  SEQMAP_ALL,
  {SEQMAP_PLANE0|SEQMAP_PLANE1,
   SEQMAP_PLANE2,
   SEQMAP_ALL,
   SEQMAP_ALL},
  NULL,
  NULL,
};

RLE rleMemGDCSR =                                               /*          */
{ /* probably not needed */
  &pleGDCData,                          /* 0x03ce (R/W) */
  0,
  REG_GDCSR, 0,
  0,
  GDCSR_ALL,
  {0x00, 0x00, 0x00, 0x00},
  NULL,
  NULL,
};

RLE rleMemGDCESR =                                              /*          */
{ /* See vvUpdateMemoryState - probably not needed */
  &pleGDCData,                          /* 0x03ce (R/W) */
  PORTF_REQUIRED,
  REG_GDCESR, 0,
  0,
  GDCESR_ALL,
  {0x00, 0x00, 0x00, 0x00},
  NULL,
  NULL,
};

RLE rleMemGDCClrCmp =                                           /*          */
{ /* probably not needed */
  &pleGDCData,                          /* 0x03ce (R/W) */
  0,
  REG_GDCCLRCMP, 0,
  0,
  GDCCLRCMP_ALL,
  {0x00, 0x00, 0x00, 0x00},
  NULL,
  NULL,
};

RLE rleMemGDCRotate =                                           /*          */
{ /* See vvUpdateMemoryState - probably not needed */
  &pleGDCData,                          /* 0x03ce (R/W) */
  PORTF_REQUIRED,
  REG_GDCROTATE, 0,
  0,
  GDCROTATE_ALL,
  {0x00, 0x00, 0x00, 0x00},
  NULL,
  NULL,
};

RLE rleMemGDCReadMap =                                          /*          */
{ /* See vvUpdateMemoryState - probably not needed */
  &pleGDCData,
  PORTF_REQUIRED,
  REG_GDCREADMAP, 0,
  0,
  GDCREADMAP_ALL,
  {PLANE0, PLANE2, 0x00, 0x00},
  NULL,
  NULL,
};

RLE rleMemGDCMode =                                             /*          */
{ /* See vvEGAOddEvenMode & vvUpdateMemoryState */
  &pleGDCData,                          /* 0x03ce (R/W) */
  PORTF_REQUIRED,
  REG_GDCMODE, 0,
  0,
  /*!! How come "| GDCMODE_256COLOR" is not below? */           /*          */
  GDCMODE_WRITE3 | GDCMODE_READ1 | GDCMODE_ODDEVENDIFF,
  { GDCMODE_WRITE0 |
     GDCMODE_READ0 |
      GDCMODE_ODDEVENDIFF,
    GDCMODE_WRITE0 |
     GDCMODE_READ0 |
     !GDCMODE_ODDEVENDIFF,
    GDCMODE_WRITE0 |
     GDCMODE_READ0 |
     !GDCMODE_ODDEVENDIFF,
    GDCMODE_WRITE0 |
     GDCMODE_READ0 |
     !GDCMODE_ODDEVENDIFF |
       GDCMODE_256COLOR},
  NULL,
  NULL,
};

RLE rleMemGDCDontCare =                                         /*          */
{ /* probably not needed */
  &pleGDCData,
  0,
  REG_GDCDONTCARE, 0,
  0,
  GDCDONTCARE_ALL,
  {0x00, 0x00, 0x00, 0x00},
  NULL,
  NULL,
};

RLE rleMemGDCBitMask =                                          /*          */
{ /* See vvUpdateMemoryState - probably not needed */
  &pleGDCData,
  PORTF_REQUIRED,
  REG_GDCBITMASK, 0,
  0,
  GDCBITMASK_ALL,
  {GDCBITMASK_ALL, GDCBITMASK_ALL, GDCBITMASK_ALL, GDCBITMASK_ALL},
  NULL,
  NULL,
};

/* Many of these are not needed any more */                     /*          */
/* Do to restructuring of vvUpdateMemoryMode */                 /*          */
/* Most of these registers which SHOULD be in this list */      /*          */
/* are referred to directly or indirectly by            */      /*          */
/* vvUpdateMemoryMode and routines it calls             */      /*          */

PRLE aprleMemory[] =                                            /*          */
{
  &rleMemSEQMemMode,
  &rleMemGDCMisc,
  &rleMemSEQMapMask,
  &rleMemGDCSR,
  &rleMemGDCESR,
  &rleMemGDCClrCmp,
  &rleMemGDCRotate,
  &rleMemGDCReadMap,
  &rleMemGDCMode,
  &rleMemGDCDontCare,
  &rleMemGDCBitMask,
#ifdef SVGA                                                     /*          */
  /*
  ** Ports in this list are marked as PORTF_NOTVALIDSVGAPORT if they
  ** do not apply to the current hardware. The EditTable SVGA function
  ** as well as the PMI processing unmarks the valid ports                
  */
  &rleATIMemATI30,                      /* 1CFh (R/W) */        /*          */
  &rleCirrusMemSEQ07,                   /* 3c5h (R/W) */        /*          */
  &rleS3MemCR3A,                        /* 3d5h (R/W) */        /*          */
  &rleS3MemCR40,                        /* 3d5h (R/W) */        /*          */
  &rleVideo7MemSEQFC,                   /* 3c5h (R/W) */        /*          */
#endif /* SVGA */
  NULL,
};

PBYTE   pbSEQExtShadowIndx;             /* pointers to external shadow bytes */
PRLE    prleSEQExtShadowData[MAX_SEQREGS];
PBYTE   pbGDCExtShadowIndx;
PRLE    prleGDCExtShadowData[MAX_GDCREGS];

 /*
 **  Windowed multi-plane graphics data
 **
 **  ahPhysPage describes current off-screen memory consumption.  -1
 **  indicates the page is reserved and cannot be used;  0 indicates an
 **  available page, 1..4096 indicates the page was allocated to PM (and
 **  how many bytes from the top of the page PM allocated), and anything
 **  else better be a VDM handle (of the VDM using the page).
 **
 **  aiPhysToVirt maps each physical page to the virtual page in the VDM
 **  whose handle is stored in ahPhysPage.
 **
 **  atimePhysPageAlloc records the last time the corresponding physical
 **  page was assigned to a VDM.
 */

ULONG   ahPhysPage [MAX_PAGESPERPLANE];
ULONG   aiPhysToVirt [MAX_PAGESPERPLANE];
ULONG   atimePhysPageAlloc [MAX_PAGESPERPLANE];

HMXSEM  hmxPhysPageEvent = (HMXSEM)0;   /* controls access to physpage data */
HMXSEM  hmxPhysLatch = (HMXSEM)0;       /* controls access to latch data */

#pragma  END_SWAP_DATA

#ifdef   EGAVGA
   #pragma  BEGIN_GLOBAL_DATA
extern VVDRQ vvdrqVideo;
   #pragma  END_GLOBAL_DATA
#endif
#pragma  BEGIN_SWAP_CODE

/***************************************************************************
 *
 * FUNCTION NAME = vvUpdatePhysPageMap()
 *
 * DESCRIPTION   = Update the physical page usage table
 *
 * INPUT         = pdrq -> display requirements (NULL if none)
 *
 * OUTPUT        = None
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 * CALLED BY
 *      VDDInit
 *      vvSysSetDisplayReq
 *
 ****************************************************************************/

VOID PRIVENTRY vvUpdatePhysPageMap(PVVDRQ pdrq)
{
  PORT port;
  ULONG offPage = 0;
  register INT i;
  register PPRLE pprle;                                         /*          */
  PVVREG pvvreg;
  BOOL fAvail = FALSE;
  register PVDMDATA pvd;
  PLSENTRY plse;
  RequestMutexSem(hmxPhysPageEvent);
  flVVD |= VVD_NOPHYSPAGES;

  /*
  ** Make a copy of the new display requirements
  */

  if (pdrq)
    vvdrqVideo = *pdrq;

  /*
  ** Adjust the physical page map as needed
  */

  for (i = 0; i < MAX_PAGESPERPLANE; i++,
       offPage += PAGESIZE)
  {

    if (offPage < vvdrqVideo.vvd_nbReserved)
    {

      if (VDMOWNSPHYSPAGE(i))
      {
        pvd = pVDMData((HVDM)(ahPhysPage[i]));

        /*
        ** Claim semaphores in proper order to avoid deadlock
        */

        ReleaseMutexSem(hmxPhysPageEvent);
        RequestMutexSem(pvd->hmxVideoState);

        /*
        ** Then make sure VDM still owns page
        */

        if (THISVDMOWNSPHYSPAGE(pvd->hvdmVideo,
                                i))
        {
          VDHFreezeVDM(pvd->hvdmVideo);
          vvUnvalidatePage(
            pvd->hvdmVideo,
            NULL,
            aiPhysToVirt[i]);                   /*            */
          vvFreePhysPage(pvd->hvdmVideo,
                         aiPhysToVirt[i]);

          if (!vdhBTS(&pvd->flVDMVideo,
                      LOG2(VDM_UPDATEHOOK)))
            VDHArmContextHook(pvd->hhookUpdateContext,
                              pvd->hvdmVideo);
          VDHThawVDM(pvd->hvdmVideo);
        }
        ReleaseMutexSem(pvd->hmxVideoState);
        RequestMutexSem(hmxPhysPageEvent);
      }
      ahPhysPage[i] = PHYSPAGE_RESERVED;
    }
    else
      if (ahPhysPage[i] == PHYSPAGE_RESERVED)
      {
        ahPhysPage[i] = PHYSPAGE_AVAILABLE;
        fAvail++;
      }
  }
  flVVD &= ~VVD_NOPHYSPAGES;

  /*
  ** Record external index and data pointers
  */
  pbSEQExtShadowIndx = NULL;
  pbGDCExtShadowIndx = NULL;

  for( pprle = aprleMemory;                                     /*          */
       (*pprle);                                                /*          */
       pprle++ )                                                /*          */
  {
#ifdef SVGA
    /*
    ** If port is valid only on specific SVGA, skip it.                    
    */
    if (((*pprle)->rle_pple->sTrapBRegInfo.fTrapType & PORTF_NOTVALIDSVGAPORT)  ||
       ((*pprle)->rle_flags & PORTF_NOTVALIDSVGAPORT))
       continue;
#endif
    (*pprle)->rle_pbExtShadowData = NULL;                       /*          */
    (*pprle)->rle_pbExtShadowIndx = NULL;                       /*          */
    if( (*pprle)->rle_pple == &pleSEQData )                     /*          */
      prleSEQExtShadowData [(*pprle)->rle_indx] = (*pprle);     /*          */
    else if( (*pprle)->rle_pple == &pleGDCData )                /*          */
      prleGDCExtShadowData [(*pprle)->rle_indx] = (*pprle);     /*          */
    if (vvdrqVideo.vvd_nbReserved != ALL_RESERVED)
    {
      vvSetupShadowPtrs( vvdrqVideo.vvd_pShadowData,
                         vvdrqVideo.vvd_nShadowRegs,
                         (*pprle) );                            /*          */
    }
  }
  if (pdrq)
  {
    if (vvdrqVideo.vvd_pPhysVRAM != 0)
    {
      flVVD &= ~VVD_PMBGND;
      RequestMutexSem(hmxWindowedEvent);
      for( plse = lseHeadActive.lse_plseNext;
           plse;
           plse = plse->lse_plseNext )
      {
        pvd = pVDMData(plse->lse_hvdm);
        /*
        ** See if there are any suspended VDMs we could wake up
        */
        if (fAvail && pvd->mstateVideo == MEMORY_GRFX)
        {
#ifdef   SVGA                                    /* Don't thaw SVGA modes   */

//                    if (!(((pvd->vvMode.vvm_nRows > 480) || ((pvd->vvMode.vvm_nRows >
//                       200) && (pvd->mstateVideo == MEMORY_GRFX256)))))
           if (!(pvd->vvMode.vvm_nRows > 600 && pvd->vvMode.vvm_nCols > 800))
#endif
               vvThawVDM(pvd->hvdmVideo);
        }
        /*
        ** See if there are any unminimized         we should refresh
        */
        if( (pvd->flVDMVideo & (VDM_WINDOWED | VDM_MINIMIZED | VDM_STATEINIT))
            == (VDM_WINDOWED | VDM_STATEINIT) )
        {
          vvUpdateScreenState( pvd->hvdmVideo,
                               TRUE );
        }
      } /* endfor plse */
      if( nPMWindowedVDMs )
      {
        VDHDisarmTimerHook( hhookEventTimer );
        VDHArmTimerHook( hhookEventTimer,
                         TIMER_CHECK,
                         VDH_TIMER_GLOBAL_CONTEXT );            /*          */
        flVVD |= VVD_TIMERARMED;                                /*          */
      }
      ReleaseMutexSem(hmxWindowedEvent);
    }
    else
      flVVD |= VVD_PMBGND;
  }
  ReleaseMutexSem(hmxPhysPageEvent);
}

/***************************************************************************
 *
 * FUNCTION NAME = vvGetPhysPage()
 *
 * DESCRIPTION   = Allocate (or steal) physical page for VDM
 *
 * INPUT         = iPage == index of page for current VDM
 *
 * OUTPUT        = TRUE if page was allocated, FALSE if not
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 * CALLED BY
 *      VVFaultHook
 *
 ****************************************************************************/

BOOL PRIVENTRY vvGetPhysPage(INT iPage)
{
  register INT i;
  INT iVirt,tmp;
  HHOOK hhook;
  BOOL fSuccess;
  INT iNotMine,iMine;
  HVDM hvdmOther,hvdmOtherTmp;
  ULONG timeNotMine,timeMine;

  AssertTRUE(!(VDMData.flVDMVideo&VDM_FGND));

  if (flVVD&VVD_NOPHYSPAGES)
    return  FALSE;

  /*
  ** Hey, we already get one
  */

  if (VDMData.aiVirtToPhys[iPage])
    return  TRUE;

  RequestMutexSem(hmxPhysPageEvent);


retry:

  /*
  ** For VDM request, search from high end down for a whole free page
  */

  fSuccess = FALSE;
  iNotMine = iMine = -1;
  timeNotMine = timeMine = 0xffffffff;

  for (i = MAX_PAGESPERPLANE-1; i >= 0; i--)
  {

    if (ahPhysPage[i] == PHYSPAGE_AVAILABLE)
    {
      fSuccess++;
      break;
    }

/*** V2.0JWK01
 *
 * The search algorithm has been altered to ensure that the VideoState Sem
 * is not currently owned.  Since it is not owned, we can steal the page
 * without releasing and requesting semaphors, which may have returned an error
 * or caused a block while hmxPhysPageEvent is not owned, invalidating the
 * page info obtained during the search.  Also added some error checking.
 */

    hvdmOtherTmp = (HVDM)(ahPhysPage[i]);       //V2.0JWK01


    /*
    ** Just in case there aren't any free pages, record the least
    ** recently stolen page currently being used by other VDMs
    */

    if (THISVDMOWNSPHYSPAGE(CURRENT_VDM,
                            i))
    {

      if (atimePhysPageAlloc[i] < timeMine)
      {
        if (VDHRequestMutexSem(pVDMData(hvdmOtherTmp)->hmxVideoState, 0)) //V2.0JWK01
        {
            iMine = i;
            timeMine = atimePhysPageAlloc[i];
            VDHReleaseMutexSem(pVDMData(hvdmOtherTmp)->hmxVideoState);    //V2.0JWK01
        }
      }
    }

    else

      if (VDMOWNSPHYSPAGE(i))
      {

        if (atimePhysPageAlloc[i] < timeNotMine)
        {
          if (VDHRequestMutexSem(pVDMData(hvdmOtherTmp)->hmxVideoState, 0)) //V2.0JWK01
          {
            iNotMine = i;
            timeNotMine = atimePhysPageAlloc[i];
            VDHReleaseMutexSem(pVDMData(hvdmOtherTmp)->hmxVideoState);    //V2.0JWK01
          }
        }
      }
  }

  /*
  ** If we didn't find a free page, then we steal from whoever we can
  */

  if (!fSuccess)
  {
    i = -1;

    /*
    ** Steal first from other VDMs, then from self
    */

    if (iNotMine >= 0)
      i = iNotMine;

    else

      if (iMine >= 0)

        i = iMine;

    /*
    ** If there's anything to steal, then free and allocate for us
    */

    if (i >= 0)
    {
      iVirt = aiPhysToVirt[i];
      hvdmOther = (HVDM)(ahPhysPage[i]);

      /*
      ** If we're stealing from ourselves, then we don't need a hook
      */

      if (VDMData.hvdmVideo == hvdmOther)
      {

        vvUnvalidatePage(hvdmOther,
                         VDMData.hvdmVideo,
                         iVirt);                /*            */
        vvFreePhysPage(hvdmOther,
                       iVirt);
        fSuccess++;
      }

      else
      {
 //V2.0JWK41  if (ALLOCHOOK((PFNARM)VVUnmapPhysContext,
 //V2.0JWK41                1,
 //V2.0JWK41                hhook,
 //V2.0JWK41                tmp))
 //V2.0JWK41  {

          /*
          ** To avoid deadlocks, must release all semaphores
          */

          //V2.0JWK01 ReleaseMutexSem(hmxPhysPageEvent);
          //V2.0JWK01 ReleaseMutexSem(VDMData.hmxVideoState);

          /*
          ** Now reclaim semaphores in proper order for victim VDM
          */

          //V2.0JWK01 RequestMutexSem(pVDMData(hvdmOther)->hmxVideoState);
          //V2.0JWK01 RequestMutexSem(hmxPhysPageEvent);

          /*
          ** Next, make sure the victim page is still valid
          */

          if (VDHRequestMutexSem(pVDMData(hvdmOther)->hmxVideoState, 0))
          {
            if (iVirt == aiPhysToVirt[i] && hvdmOther == (HVDM)(ahPhysPage[i]))
            {
              hhook = pVDMData(hvdmOther)->hhookUnMapPhysContext;  //V2.0JWK41
              HOOKDATA(hhook, 0) |= 1 << iVirt;                    //V2.0JWK41
 //V2.0JWK41  HOOKDATA(hhook,0) = iVirt;
 //V2.0JWK41  ARMHOOK(hhook,hvdmOther);
              if (!(pVDMData(hvdmOther)->flVDMXVideo & VDMX_UNMAPHOOK)) {   //V2.0JWK41
                  ARMHOOK(hhook, hvdmOther);                                //V2.0JWK41
                  pVDMData(hvdmOther)->flVDMXVideo |= VDMX_UNMAPHOOK;       //V2.0JWK41
              }                                                             //V2.0JWK41

              vvUnvalidatePage(
                 hvdmOther,
                 VDMData.hvdmVideo,
                 iVirt);                /*            */
              vvFreePhysPage(hvdmOther,
                             iVirt);
              fSuccess++;
            }
            //V2.0JWK01 ReleaseMutexSem(hmxPhysPageEvent);
            ReleaseMutexSem(pVDMData(hvdmOther)->hmxVideoState);
            //V2.0JWK01 RequestMutexSem(VDMData.hmxVideoState);
            //V2.0JWK01 RequestMutexSem(hmxPhysPageEvent);

            if (!fSuccess || ahPhysPage[i] != PHYSPAGE_AVAILABLE)
            {
              goto retry;
            }
          }
 //V2.0JWK41  }
      }
    }
  }

  /*
  ** Allocate page, and record mapping
  */

  if (fSuccess)
  {
    ahPhysPage[i] = (ULONG)(VDMData.hvdmVideo);
    aiPhysToVirt[i] = iPage;
    atimePhysPageAlloc[i] = VDHQuerySysValue(NULL,
                                             VDHGSV_MSECSBOOT);
    VDMData.aiVirtToPhys[iPage] = i;

    /*
    ** If this is the first page allocated for this VDM,
    ** set its PHYSPAGE bit and request the video controller
    */

    if (!VDMData.nPhysPages++)
    {
      VDMData.flVDMVideo |= VDM_PHYSPAGE;

      /*
      ** To avoid deadlock with PM threads coming in through
      ** GetPhysBytes, we must release PhysPageEvent semaphore first
      */

      ReleaseMutexSem(hmxPhysPageEvent);
      vvRequestController(VDMData.hvdmVideo,
                          FALSE);
      return  fSuccess;
    }
  }

  ReleaseMutexSem(hmxPhysPageEvent);
  return  fSuccess;
}

/***************************************************************************
 *
 * FUNCTION NAME = vvFreePhysPage()
 *
 * DESCRIPTION   = Free physical page for VDM
 *
 *
 * INPUT         = hvdm  == VDM handle
 *                 iPage == index of page for current VDM
 *
 * OUTPUT        = None
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 ****************************************************************************/

VOID PRIVENTRY vvFreePhysPage(HVDM hvdm,INT iPage)
{
  register INT i;
  register PVDMDATA pvd = pVDMData(hvdm);

  RequestMutexSem(hmxPhysPageEvent);

  if (i = pvd->aiVirtToPhys[iPage])
  {
    AssertTRUE(aiPhysToVirt[i] == iPage);
    pvd->aiVirtToPhys[iPage] = 0;                /* zero the owner's
                                                    physical index          */
    ahPhysPage[i] = NULL;                        /* zero the owner          */
    aiPhysToVirt[i] = 0;                         /* zero the owner's
                                                    virtual page            */
    atimePhysPageAlloc[i] = 0;                   /* zero the time of last
                                                    allocation              */

    if (!--pvd->nPhysPages)
      pvd->flVDMVideo &= ~VDM_PHYSPAGE;
  }
  ReleaseMutexSem(hmxPhysPageEvent);
}

/***************************************************************************
 *
 * FUNCTION NAME = vvFreeAllPhysPages()
 *
 * DESCRIPTION   = Free all physical pages
 *                 for a dying VDM, or one changing video modes
 *
 * INPUT         = hvdm   == VDM handle
 *                 fDying == TRUE if VDM is dying, FALSE otherwise
 *
 * OUTPUT        = None
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 * CALLED BY
 *      vvUpdatePageStates
 *      VVDestroy
 *
 ****************************************************************************/

VOID PRIVENTRY vvFreeAllPhysPages(HVDM hvdm,BOOL fDying)
{
  register INT i;
  register PVDMDATA pvd = pVDMData(hvdm);

  /*
  ** First, a quick check before we start taking resources
  */

  if (pvd->flVDMVideo&VDM_PHYSPAGE)
  {
    //V2.0JWK42 switched order of next two semaphor calls to prevent deadlock

        /*** Don't let PHYSPAGE state change from underneath us ***/  //V2.0JWK42
        RequestMutexSem(hmxPhysPageEvent);                            //V2.0JWK42
                                                                      //V2.0JWK42
        /*** Request AND lock the controller ***/                     //V2.0JWK42
        vvRequestController(pvd->hvdmVideo, TRUE);                    //V2.0JWK42

//V2.0JWK42    /*
//V2.0JWK42    ** Request AND lock the controller
//V2.0JWK42    */
//V2.0JWK42
//V2.0JWK42    vvRequestController(pvd->hvdmVideo,
//V2.0JWK42                        TRUE);
//V2.0JWK42
//V2.0JWK42    /*
//V2.0JWK42    ** Don't let PHYSPAGE state change from underneath us
//V2.0JWK42    */
//V2.0JWK42
//V2.0JWK42    RequestMutexSem(hmxPhysPageEvent);

    if (pvd->flVDMVideo&VDM_PHYSPAGE)
    {

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

        if (THISVDMOWNSPHYSPAGE(hvdm,
                                i))
        {

          if (!fDying)

            vvUnvalidatePage(
              hvdm,
              NULL,
              aiPhysToVirt[i]);
          vvFreePhysPage(hvdm,
                         aiPhysToVirt[i]);
        }
      }
    }
    ReleaseMutexSem(hmxPhysPageEvent);

    /*
    ** Controller is up for grabs now
    */

    vvFreeController(pvd->hvdmVideo);
  }
}

/***************************************************************************
 *
 * FUNCTION NAME = vvGetPhysBytes()
 *
 * DESCRIPTION   = Allocate physical video memory for PM
 *
 *                 WARNING WARNING WARNING
 *
 *                 Although it is not clear by just looking at this code, it
 *                 is perfectly legitimate for the video controller to be
 *                 locked for use by PM when this request is made.  This
 *                 fact, in turn, dictates the semaphore hierarchy that must
 *                 be followed throughout this module:  Request controller
 *                 first and PhysPageEvent semaphore last.
 *
 * INPUT         = nBytes == # bytes required
 *
 * OUTPUT        = Offset of physical video memory, NULL if none
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 ****************************************************************************/

ULONG PRIVENTRY vvGetPhysBytes(ULONG nBytes)
{
  ULONG p = 0;
  register INT i,j,k;

  /*
  ** Fail request if there are any ACTIVE VDMs running around
  */

  if (!lseHeadActive.lse_plseNext)
  {
    RequestMutexSem(hmxPhysPageEvent);           /*                         */

    /*
    ** Since the VGA Controller ownership can change by the time this
    ** point is reached, lets make sure the Controller is locked when
    ** this thread claims it to avoid deadlock with vvGetPhysPage
    */


#ifdef   EGAVGA                                  /*                         */
    vvRequestController(DISPLAY_OWNER,
                    LOCKBUTDONOTBLOCK);          /*                         */
#endif                                           /*                         */

    /*
    ** For PM request, search from low end up (and no stealing!)
    */

    for (i = 0,
         j = -1,
         k = 0; i < MAX_PAGESPERPLANE; i++)
    {

      if (ahPhysPage[i] == PHYSPAGE_AVAILABLE)
      {

        if (j < 0)
        {
          j = i;                                 /* save index of first
                                                    page                    */
          k = PAGESIZE;                          /* initialize byte count   */
        }

        else
          k += PAGESIZE;                         /* otherwise, bump byte
                                                    count                   */
      }

      else
        j = -1;

      if (k >= nBytes)
      {
        p = j *PAGESIZE+ahPhysPage[j];

        for (i = j; k > 0; i++)
        {
          j = PAGESIZE-ahPhysPage[i];            /* get # bytes in page     */
          ahPhysPage[i] += min(j,
                               k);
          k -= j;                                /* note that this is more
                                                    general                 */
        }                                        /* than we need to be....  */
        break;
      }
    }

#ifdef   EGAVGA                                  /*                         */
    vvFreeController(DISPLAY_OWNER);             /*                         */
#endif                                           /*                         */
    ReleaseMutexSem(hmxPhysPageEvent);           /*                         */
  }
  return  p;
}

/***************************************************************************
 *
 * FUNCTION NAME = vvFreePhysBytes()
 *
 * DESCRIPTION   = Free physical video memory for PM
 *
 *                 WARNING WARNING WARNING Although it is not clear by just
 *                 looking at this code, it is perfectly legitimate for the
 *                 video controller to be locked for use by PM when this
 *                 request is made.  This fact, in turn, dictates the
 *                 semaphore hierarchy that must be followed throughout this
 *                 module:  Request controller first and PhysPageEvent
 *                 semaphore last.
 *
 * INPUT         = nBytes == # bytes required
 *                 p      == offset of physical video memory
 *
 * OUTPUT        = None
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 ****************************************************************************/

VOID PRIVENTRY vvFreePhysBytes(ULONG nBytes,ULONG p)
{
  register INT i,k;

  RequestMutexSem(hmxPhysPageEvent);

  /*
  ** Since the VGA Controller ownership can change by the time this
  ** point is reached, lets make sure the Controller is locked when
  ** this thread claims it to avoid deadlock with vvGetPhysPage
  */

#ifdef   EGAVGA                                  /*                         */
  vvRequestController(DISPLAY_OWNER,
                    LOCKBUTDONOTBLOCK);          /*                         */
#endif                                           /*                         */

  for (i = p/PAGESIZE,
       k = nBytes; k > 0; i++)
  {
    k -= ahPhysPage[i];
    ahPhysPage[i] = PHYSPAGE_AVAILABLE;
  }

#ifdef   EGAVGA                                  /*                         */
  vvFreeController(DISPLAY_OWNER);               /*                         */
#endif                                           /*                         */
  ReleaseMutexSem(hmxPhysPageEvent);
}

/***************************************************************************
 *
 * FUNCTION NAME = VVUnmapPhysContext()
 *
 * DESCRIPTION   = Unmap a physical page in current VDM
 *
 *                 This is called whenever we have stolen a physical page
 *                 for a VDM.  It unmaps the specified page so that if the
 *                 VDM tries to touch it again, we will catch a page fault
 *                 first.
 *
 * INPUT         = hhook == hook handle
 *               (1st reference item is bitmap of stolen pages to be unmapped) //V2.0JWK41
 *                 pcrf -> VDM register frame
 *
 * OUTPUT        = None
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 ****************************************************************************/

VOID HOOKENTRY VVUnmapPhysContext(HHOOK hhook,PCRF pcrf)
{
  PBVDM pvdm;
  register ULONG flags;                                              //V2.0JWK41
  register ULONG i = 0;                                              //V2.0JWK41



  VDMData.flVDMXVideo &= ~VDMX_UNMAPHOOK;                            //V2.0JWK41
  flags = HOOKDATA(hhook,0);                                         //V2.0JWK41
                                                                     //V2.0JWK41
  while (flags) {                                                    //V2.0JWK41
                                                                     //V2.0JWK41
        if (flags & 1) {                                             //V2.0JWK41
            pvdm = i*PAGESIZE + VDMData.pvdmPhysVRAMActive;          //V2.0JWK41
            vvMapPage(pvdm, NULL, ALL_PLANES, BANK0, MAX_VGA_BANK, VDHMT_INVALID); //V2.0JWK41
        }                                                            //V2.0JWK41
        flags >>= 1;                                                 //V2.0JWK41
        i++;                                                         //V2.0JWK41
  }                                                                  //V2.0JWK41
  HOOKDATA(hhook,0) = 0;                                             //V2.0JWK41


//V2.0JWK41  pvdm = HOOKDATA(hhook,
//V2.0JWK41                  0)*PAGESIZE+VDMData.pvdmPhysVRAMActive;
//V2.0JWK41  FREEHOOK(hhook);
//V2.0JWK41  vvMapPage(pvdm,
//V2.0JWK41            NULL,
//V2.0JWK41            ALL_PLANES,
//V2.0JWK41            BANK0,                                              /*            */
//V2.0JWK41            VDHMT_INVALID);


}

/***************************************************************************
 *
 * FUNCTION NAME = VVUpdateContext()
 *
 * DESCRIPTION   = Update VDM page mappings in context
 *
 *
 * INPUT         = p == undefined
 *                 pcrf -> VDM register frame (ignored)
 *
 * OUTPUT        = None
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 ****************************************************************************/

VOID HOOKENTRY VVUpdateContext(
  PVOID p,
  PCRF pcrf )
{
#ifdef SVGA
  if( vvAcceleratorChip()                                       /*          */
      && !(VDMData.flVDMVideo & VDM_UPDATEHOOK) )               /*          */
  {                                                             /*          */
      /* We are in the foreground */                            /*          */
      /* waiting for the accelerator drawing engine to stop */  /*          */
      /* Just trap what we need for that */                     /*          */
      vvAcceleratorSetIOHooks( TRUE );                          /*          */
  }
  else
#endif /* SVGA */
  {
    /*
    ** Set up all appropriate mappings
    */

    VDMData.flVDMVideo &= ~VDM_UPDATEHOOK;
    VVEnableBuffer(TRUE,
                   pcrf);
  }
}

/***************************************************************************
 *
 * FUNCTION NAME = vvSetIOState()
 *
 * DESCRIPTION   = Set hardware according to specified register list
 *
 *                 This worker routine is used by vvSetCopyState and
 *                 vvRestoreCopyState, and by the pointer-draw code, to
 *                 reprogram the hardware according to the given list.
 *
 * INPUT         = hvdm  -> VDM
 *                 pprle -> register list
 *                 iMode == index of mode-byte to output
 *
 * OUTPUT        = None
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 * CALLED BY
 *      vvSetCopyState
 *      vvRestoreCopyState
 *      vvUpdatePhysContext
 *
 ****************************************************************************/

VOID PRIVENTRY vvSetIOState(
  HVDM hvdm,
  register PPRLE pprle,                                         /*          */
  INT iMode )
{
  BYTE bIndex, bData;
  FLAGS flCond;
  PORT portData, portIndx, portLast = 0;                        /*          */
  register PBYTE pbShadowLast;
  register PVDMDATA pvd = pVDMData(hvdm);

  AssertTRUE( pvd->flVDMVideo & (VDM_FGND | VDM_PHYSPAGE) );

  /*
  ** While more register-list entries
  */

  for( ;                                                        /*          */
       (*pprle);                                                /*          */
       pprle++ )                                                /*          */
  {
#ifdef SVGA
    /*
    ** If port is valid only on specific SVGA, skip it.                    
    */
    if (((*pprle)->rle_pple->sTrapBRegInfo.fTrapType & PORTF_NOTVALIDSVGAPORT) ||
       ((*pprle)->rle_flags & PORTF_NOTVALIDSVGAPORT))
       continue;
#endif
    portData = ((PSCGAINFO) psCurAdapterInfo)->                 /*          */
                 pportfnCGAAdjustedPort(                        /*          */
                   &(*pprle)->rle_pple->sTrapBRegInfo );        /*          */
    /*
    ** Reprogram port only if we have to
    */
    if( (iMode == SETIO_LIMIT_SHADOWED)
        || ((*pprle)->rle_flags & PORTF_REQUIRED) )             /*          */
    {

      /*
      ** Determine data to output (assume SETIO_SHADOWED)
      */

      flCond = (*pprle)->rle_fbCond;                            /*          */
      bData = pVDM( hvdm,                                       /*          */
                    PBYTE,                                      /*          */
                    (*pprle)->rle_pple->                        /*          */
                      sTrapBRegInfo.pvTrapWriteShadow )         /*          */
                [(*pprle)->rle_pple->ppleBRegIndx               /*          */
                 ? (*pprle)->rle_indx                           /*          */
                 : 0];                                          /*          */
      if( iMode > SETIO_SHADOWED )
      {
        /*
        ** Clear bits in VDM's setting we need to change
        */
        bData &= ~(*pprle)->rle_fbMask;                         /*          */
        /*
        ** and OR in the appropriate bits from our mode table
        */
        bData |= ((*pprle)->rle_bMode [iMode]                   /*          */
                  & (*pprle)->rle_fbMask);                      /*          */
      }
      if( pvd->flVDMVideo & VDM_PHYSPAGE )
      {
        flCond = 0;     /* FLAG: -- Probably shouldn't do this! */
        bData = vvAdjustShadowOutput( (*pprle),                 /*          */
                                      bData );
      }
      if( !(*pprle)->rle_pple->ppleBRegIndx )                   /*          */
        /*
        ** Non-indexed register
        */
        vvShadowOutput( hvdm,
                        0,
                        portData,
                        -1,
                        bData,
                        NULL,
                        (*pprle)->rle_pbExtShadowData,          /*          */
                        flCond );
      else
      {
        /*
        ** Indexed register
        */
        portIndx =                                              /*          */
          ((PSCGAINFO) psCurAdapterInfo)->                      /*          */
            pportfnCGAAdjustedPort(                             /*          */
              &(*pprle)->rle_pple->                             /*          */
                ppleBRegIndx->sTrapBRegInfo );                  /*          */
        /*
        ** Save current index if new port
        */
        if( (portIndx != portLast)                              /*          */
            && (iMode <= SETIO_SHADOWED) )
        {
          if (portLast)
            vvShadowOutput( hvdm,
                            0,
                            portLast,
                            -1,
                            bIndex,
                            NULL,
                            pbShadowLast,
                            0 );
          portLast = portIndx;                                  /*          */
          bIndex = *pVDM( hvdm,
                          PBYTE,
                          (*pprle)->rle_pple->ppleBRegIndx->    /*          */
                            sTrapBRegInfo.pvTrapWriteShadow );  /*          */
          pbShadowLast = (*pprle)->rle_pbExtShadowIndx;         /*          */
        }
        vvShadowOutput( hvdm,
                        portIndx,                               /*          */
                        portData,                               /*          */
                        (*pprle)->rle_indx,                     /*          */
                        bData,
                        (*pprle)->rle_pbExtShadowIndx,          /*          */
                        (*pprle)->rle_pbExtShadowData,          /*          */
                        flCond );
      }
    }
  } /* end for pprle */                                         /*          */
  if (portLast)
    vvShadowOutput( hvdm,
                    0,
                    portLast,
                    -1,
                    bIndex,
                    NULL,
                    pbShadowLast,
                    0 );
}

/***************************************************************************
 *
 * FUNCTION NAME = VVUpdatePhysContext()
 *
 * DESCRIPTION   = Set up I/O state for current VDM
 *
 *                 This sets up the hardware for a VDM with PHYSPAGE memory.
 *                 Note that we first have to successfully request ownership
 *                 of the video controller, which may block us temporarily.
 *
 * INPUT         =  p    -> refdata (none)
 *                  pcrf -> VDM register frame
 *
 * OUTPUT        =  None
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 ****************************************************************************/

VOID HOOKENTRY VVUpdatePhysContext(
  PVOID p,
  PCRF pcrf )
{
  VDMData.flVDMVideo &= ~VDM_PHYSHOOK;

  /*
  ** First, a quick check before we start taking resources
  */
  if( VDMData.nPhysPages )
  {
    /*
    ** Don't let PHYSPAGE state change from underneath us
    */
    RequestMutexSem( VDMData.hmxVideoState );
    /*
    ** Request AND lock the controller
    */
    vvRequestController(VDMData.hvdmVideo,
                        TRUE);
    if( VDMData.nPhysPages )
    {
      /*
      ** Restore latches and I/O state, IN THAT ORDER
      */
      vvRestoreLatches( CURRENT_VDM,
                        FALSE );
      vvSetIOState( CURRENT_VDM,
                    aprleMemory,                                 /*          */
                    SETIO_LIMIT_SHADOWED );
    }
    /*
    ** Controller is still ours, but can be stolen now
    */
    vvUnlockController();
    ReleaseMutexSem( VDMData.hmxVideoState );
  }
}

/***************************************************************************
 *
 * FUNCTION NAME = vvSetCopyState()
 *
 * DESCRIPTION   = Set hardware for specified access
 *
 *                 Depending on the given state (MEMORY_TEXT, MEMORY_FONT, or
 *                 MEMORY_GRFX), the video hardware is programmed for copying
 *                 memory in that state.  This involves reprogramming all the
 *                 registers listed in aprleMemory.
 *
 * INPUT         = hvdm   -> VDM
 *                 ms     == either MEMORY_TEXT, MEMORY_FONT or MEMORY_GRFX
 *                 iPlane == desired plane
 *
 * OUTPUT        = Newly selected memory state
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 * CALLED BY
 *      vvTransferPage
 *      vvLoadCodePageFont
 *      vvTransferLinearBuffer
 *      vvSaveLatches
 *      vvRestoreLatches
 *
 ****************************************************************************/

VOID PRIVENTRY vvSetCopyState(
  HVDM hvdm,
  register MSTATE ms,
  INT iPlane )
{
  BYTE b;
  register PVDMDATA pvd = pVDMData(hvdm);

  /*
  ** If we're not already in desired state
  */
  if( (ms != MEMORY_NONE)
      && (ms != pvd->mstateCopy) )
  {
    vvSetIOState( hvdm,
                  aprleMemory,                                   /*          */
                  amstateMap [ms] );
    pvd->mstateCopy = ms;
  }
  /*
  ** When ALL_PLANES is specified, set "latch" read/write mode
  */
  if( iPlane == ALL_PLANES )
  {
    vvShadowOutput( hvdm,
                    pleSEQIndx.sTrapBRegInfo.portTrap,          /*          */
                    pleSEQData.sTrapBRegInfo.portTrap,          /*          */
                    REG_SEQMAPMASK,
                    SEQMAP_ALL,
                    pbSEQExtShadowIndx,
                    prleSEQExtShadowData[REG_SEQMAPMASK]->
                      rle_pbExtShadowData,
                    0 );
    vvShadowOutput( hvdm,
                    pleGDCIndx.sTrapBRegInfo.portTrap,          /*          */
                    pleGDCData.sTrapBRegInfo.portTrap,          /*          */
                    REG_GDCMODE,
                    GDCMODE_WRITE1|GDCMODE_READ0|!GDCMODE_ODDEVENDIFF,
                    pbGDCExtShadowIndx,
                    prleGDCExtShadowData[REG_GDCMODE]->
                      rle_pbExtShadowData,
                    0 );

    if (pvd->mstateCopy == MEMORY_NONE)
      pvd->mstateCopy = MEMORY_LATCH;
  }
  /*
  ** For graphics modes, set up specified plane for read/write
  */
  else
    if( ms == MEMORY_GRFX )
    {
      /*
      ** Set up plane for writing
      */
      vvShadowOutput( hvdm,
                      pleSEQIndx.sTrapBRegInfo.portTrap,        /*          */
                      pleSEQData.sTrapBRegInfo.portTrap,        /*          */
                      REG_SEQMAPMASK,
                      1 << iPlane,
                      pbSEQExtShadowIndx,
                      prleSEQExtShadowData[REG_SEQMAPMASK]->
                        rle_pbExtShadowData,
                      0 );
      /*
      ** Set up same plane for reading
      */
      vvShadowOutput( hvdm,
                      pleGDCIndx.sTrapBRegInfo.portTrap,        /*          */
                      pleGDCData.sTrapBRegInfo.portTrap,        /*          */
                      REG_GDCREADMAP,
                      iPlane,
                      pbGDCExtShadowIndx,
                      prleGDCExtShadowData[REG_GDCREADMAP]->
                        rle_pbExtShadowData,
                      0 );
    }
    else
      if( ms == MEMORY_GRFX256 )
      {
        vvShadowOutput( hvdm,
                        pleSEQIndx.sTrapBRegInfo.portTrap,      /*          */
                        pleSEQData.sTrapBRegInfo.portTrap,      /*          */
                        REG_SEQMAPMASK,
                        SEQMAP_ALL,
                        pbSEQExtShadowIndx,
                        prleSEQExtShadowData[REG_SEQMAPMASK]->
                          rle_pbExtShadowData,
                        0 );
        /*!!Might this not work? */                             /*          */
        /*!!if this does NOT set packed mode? */                /*          */
        vvShadowOutput( hvdm,
                        pleGDCIndx.sTrapBRegInfo.portTrap,      /*          */
                        pleGDCData.sTrapBRegInfo.portTrap,      /*          */
                        REG_GDCMODE,
                        GDCMODE_256COLOR,
                        pbGDCExtShadowIndx,
                        prleGDCExtShadowData[REG_GDCMODE]->
                          rle_pbExtShadowData,
                        0 );
      }
}

/***************************************************************************
 *
 * FUNCTION NAME = vvRestoreCopyState()
 *
 * DESCRIPTION   = Restore hardware state
 *
 *                 This basically undoes the work done by vvSetCopyState.
 *                 For every register in the aprleMemory list, the virtual
 *                 values for those registers is written back to the
 *                 hardware.  This is done for all the registers listed in
 *                 aprleMemory.
 *
 * INPUT         = hvdm -> VDM
 *                 iMode == Restore mode (SETIO_SHADOWED or SETIO_LIMIT_SHADOWED)
 * OUTPUT        =
 *                 None
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 ****************************************************************************/

VOID PRIVENTRY vvRestoreCopyState(
  register HVDM hvdm,
  INT iMode )
{
  register PVDMDATA pvd = pVDMData(hvdm);

  /*
  ** If we were in "latch" mode, then just fix the two registers
  */

  if (pvd->mstateCopy == MEMORY_LATCH)
  {
    vvShadowOutput( hvdm,
                    pleSEQIndx.sTrapBRegInfo.portTrap,          /*          */
                    pleSEQData.sTrapBRegInfo.portTrap,          /*          */
                    REG_SEQMAPMASK,
                    pvd->aregSEQData[REG_SEQMAPMASK],
                    pbSEQExtShadowIndx,
                    prleSEQExtShadowData[REG_SEQMAPMASK]->
                      rle_pbExtShadowData,
                    0 );
    vvShadowOutput( hvdm,
                    pleGDCIndx.sTrapBRegInfo.portTrap,          /*          */
                    pleGDCData.sTrapBRegInfo.portTrap,          /*          */
                    REG_GDCMODE,
                    pvd->aregGDCData[REG_GDCMODE],
                    pbGDCExtShadowIndx,
                    prleGDCExtShadowData[REG_GDCMODE]->
                      rle_pbExtShadowData,
                    0 );
  }

  else
    if (pvd->mstateCopy != MEMORY_NONE)
      vvSetIOState( hvdm,
                    aprleMemory,                                /*          */
                    iMode );
  pvd->mstateCopy = MEMORY_NONE;
}

/***************************************************************************
 *
 * FUNCTION NAME = vvSaveLatches()
 *
 * DESCRIPTION   = Save physical latch contents
 *
 *                 Save the physical latch state.  The latch values are
 *                 transferred to physical memory, and then copied from
 *                 there to virtual storage.
 *
 * INPUT         = hvdm -> VDM
 *                 fGlobal == TRUE to save in global storage,
 *                            FALSE in per-VDM storage
 *
 * OUTPUT        = None
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 ****************************************************************************/

VOID PRIVENTRY vvSaveLatches(HVDM hvdm,BOOL fGlobal)
{
  register INT i;
  register PBYTE pbPhysLatch;
  register PVDMDATA pvd = pVDMData(hvdm);

  if (pvd->mstateVideo == MEMORY_GRFX && (pbPhysLatch = vvQueryLatchAddress
     (hvdm)))
  {
    /*
    ** We have to take this semaphore because saving
    ** the latches requires using a global VRAM location
    */
    RequestMutexSem(hmxPhysLatch);
    vvSetCopyState( hvdm,
                    MEMORY_NONE,
                    ALL_PLANES );
    *pbPhysLatch = 0;            /* a dummy write, to save latches */
    if (!fGlobal)
    {
      for (i = 0; i < MAX_PLANES; i++)
      {
        vvSetCopyState( hvdm,
                        MEMORY_GRFX,
                        i );
        pvd->bLatches[i] = *pbPhysLatch;
      }
      /*
      ** Yes, we only release this semaphore on non-global saves
      */
      ReleaseMutexSem( hmxPhysLatch );
    }
  }
}

/***************************************************************************
 *
 * FUNCTION NAME = vvRestoreLatches()
 *
 * DESCRIPTION   = Restore physical latch contents
 *
 *                 Restore the physical latch state.  The latch values are
 *                 copied from virtual storage to the physical memory, and
 *                 then loaded into the physical latches.
 *
 * INPUT         = hvdm -> VDM
 *                 fGlobal == TRUE to restore from global storage, FALSE from per-VDM storage
 *
 * OUTPUT        = None
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 ****************************************************************************/

VOID PRIVENTRY vvRestoreLatches(
  HVDM hvdm,
  BOOL fGlobal )
{
  register INT i;
  register PBYTE pbPhysLatch;
  register PVDMDATA pvd = pVDMData(hvdm);

  if (pvd->mstateVideo == MEMORY_GRFX && (pbPhysLatch = vvQueryLatchAddress
     (hvdm)))
  {
    if (!fGlobal)
    {
      /*
      ** We have to take this semaphore because saving
      ** the latches requires using a global VRAM location
      */
      RequestMutexSem(hmxPhysLatch);
      for (i = 0; i < MAX_PLANES; i++)
      {
        vvSetCopyState( hvdm,
                        MEMORY_GRFX,
                        i );
        *pbPhysLatch = pvd->bLatches[i];
      }
    }
    i = *pbPhysLatch;                    /* a dummy read, to load latches */
    ReleaseMutexSem(hmxPhysLatch);
  }
}

/***************************************************************************
 *
 * FUNCTION NAME = vvQueryLatchAddress()
 *
 * DESCRIPTION   = Determine suitable latch addres
 *
 * INPUT         = hvdm -> VDM
 *
 * OUTPUT        = pbPhysLatch -> latch suitable for specified VDM, NULL if none
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 ****************************************************************************/

PBYTE PRIVENTRY vvQueryLatchAddress(
  HVDM hvdm )
{
  register PVDMDATA pvd = pVDMData(hvdm);

  if (pvd->flVDMVideo&VDM_PHYSPAGE)
    return  pPhysVRAM+vvdrqVideo.vvd_offLatchByte;
  else
    if (pvd->flVDMVideo&VDM_FGND)
      return  pPhysVRAM+(pvd->pvdmPhysVRAMActive-pvdmPhysVRAM)+
         (pvd->npgPhysVRAMActive *PAGESIZE)-1;
    else
      return  NULL;
}

#pragma  END_SWAP_CODE

