/*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 = VVSVGA.C
 *
 * DESCRIPTIVE NAME = Virtual Video SVGA Specific Processing
 *
 *
 * VERSION = V2.0
 *
 * DATE      02/12/92
 *
 * DESCRIPTION  Virtual Video SVGA Specific Processing
 *
 * FUNCTIONS    vvTsengLockState()     Query Tseng extended registers lock state.
 *              vvVideo7LockState()    Query Video7 extended registers lock state.
 *              vvWDLockState()        Query Western Digital extended registers lock state.
 *              vvSVGAGetLockState()   Query Western Digital extended registers lock state.
 *              vvNOPSetBank()         NOP set bank  ( do nothing )
 *              vvNOPGetBank()         NOP get bank  ( do nothing )
 *              vvVideo7SetBank()      Set video7 bank register
 *              vvTridentSetBank()     Set Trident bank register
 *              vvTsengSetBank()       Set Tseng bank register
 *              vvWDSetBank()          Set Western Digital bank register
 *              vvATISetBank()         Set ATI bank register
 *              vvIBMSetBank()         Set IBM bank register
 *              vvVideo7GetBankFgnd()  Get Video7 bank register ( Foreground )
 *              vvVideo7GetBankBgnd()  Get Video7 bank register ( Background )
 *              vvVideo7GetBank()      Get Video7 bank register
 *              vvTsengGetBank()       Get Tseng bank register
 *              vvTridentGetBank()     Get Trident bank register
 *              vvWDGetBank()          Get Western Digital bank register
 *              vvATIGetBank()         Get ATI bank register
 *              vvIBMGetBank()         Get IBM bank register
 *              vvMaxBanks()           Calculate maximum number of banks
 *              vvSVGALinearMode       Get SVGA linear mode.
 *              vvIdentifySVGA         Get SVGA VRAM size.
 *
 * NOTES
 *
 *
 * STRUCTURES
 *
 * EXTERNAL REFERENCES
 *
 * EXTERNAL FUNCTIONS
 *
*/


#define  IO8BIT           /* CL386 Version 6.00.054 FLAG:              */

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

#ifdef   VDDSTRICT
MODNAME = __FILE__;
#endif

/*
**     Externals
*/

extern ULONG ulSVGAAdapterType;
extern ULONG ulSVGAChipType;
extern ULONG ulSVGAVRAMSize;                     /*                         */
extern ULONG ulXGAInstance;                      /*                         */
extern ULONG ulWD_MASK_GDCLOCK;                  /*                         */
extern ULONG ulWD_MASK_CRTLOCK;                  /*                         */
extern ULONG ulWD_MASK_SEQLOCK;                  /*                         */
extern OEMINFO sOEMData;                         /*                          */
#pragma  BEGIN_SWAP_CODE                          /*                         */

/***************************************************************************
 *
 * FUNCTION NAME = vvTsengLockState()
 *
 * DESCRIPTION   = Query Tseng extended registers lock state.
 *
 * INPUT         = None
 *
 * OUTPUT        = MASK_ALL_LOCKED - if extended regs are locked
 *                 FALSE- if unlocked
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 ****************************************************************************/

BOOL PRIVENTRY vvTsengLockState(VOID)
{
  return(((INB(0x3d8) & 0xA0) == 0xA0) ? 0 : MASK_ALL_LOCKED); /*               */
}

/***************************************************************************
 *
 * FUNCTION NAME = vvS3LockState()
 *
 * DESCRIPTION   = Query S3 extended registers lock state.
 *
 * INPUT         = None
 *
 * OUTPUT        = MASK_ALL_LOCKED - if extended regs are locked
 *                 FALSE- if unlocked
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 ****************************************************************************/

BOOL PRIVENTRY vvS3LockState(VOID)
{
  BYTE bCRTIndex,bLockReg;

  bCRTIndex = INB(PORT_COLRCRTINDX);    /* save CRT index */    /*          */
  OUTB(PORT_COLRCRTINDX, 0x38);         /* Lock/Unlock reg */   /*          */
  bLockReg = INB(PORT_COLRCRTDATA) & 0x48;                      /*          */
                                        /* get 1 if unlocked */ /*          */
  if (bLockReg)
  {
    OUTB(PORT_COLRCRTINDX, 0x39);       /* Lock/Unlock reg */   /*          */
    bLockReg = INB(PORT_COLRCRTDATA) & 0xA0;  /* get 1 if unlocked */
  }
  OUTB(PORT_COLRCRTINDX,bCRTIndex);     /* restore CRT index */ /*          */
  if (bLockReg) return 0;               /*                         */
  return(MASK_ALL_LOCKED);
}
/***************************************************************************
 *
 * FUNCTION NAME = vvVideo7LockState()
 *
 * DESCRIPTION   = Query Video7 extended registers lock state.
 *
 * INPUT         = None
 *
 * OUTPUT        = MASK_ALL_LOCKED - if extended regs are locked
 *                 FALSE- if unlocked
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 ****************************************************************************/

BOOL PRIVENTRY vvVideo7LockState(VOID)
{
  BYTE bSeqIndex,bLockReg;

  bSeqIndex = INB(PORT_SEQINDX);        /* save SEQ index */    /*          */
  OUTB(PORT_SEQINDX, 6);                /* Lock/Unlock reg */   /*          */
  bLockReg = INB(PORT_SEQDATA);         /* get 1 if unlocked */ /*          */
  OUTB(PORT_SEQINDX, bSeqIndex);        /* restore SEQ index */ /*          */
  if (bLockReg) return 0;               /*                         */
  return(MASK_ALL_LOCKED);
}

/***************************************************************************
 *
 * FUNCTION NAME = vvWDLockState()
 *
 * DESCRIPTION   = Query Western Digital extended registers lock state.
 *
 *                 Three sets of registers are locked, extended CRT, GDC and SEQ.
 *                 This routine returns a bit mask indicating which registers are
 *                 masked.
 *
 *                 GDCs are unlocked if PR05 contains 0x05.
 *                 CRTs are unlocked if PR10 contains 0x85.
 *                 SEQs are unlocked if PR20 reads as 8 bits wide.
 *
 * INPUT         = None
 *
 * OUTPUT        = BitMask - if extended regs are locked
 *                 FALSE- if unlocked
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 ****************************************************************************/
BOOL    PRIVENTRY vvWDLockState(VOID)
{
  BYTE    bGDCIndex, bCRTIndex, bLockReg, bSEQIndex;
  BOOL    fLocked = 0;


  bGDCIndex = INB(PORT_GDCINDX);          /* save GDC index                 */
  bCRTIndex = INB(PORT_COLRCRTINDX);      /* save CRT index                 */
  bSEQIndex = INB(PORT_SEQINDX);          /* save SEQ index                 */

  OUTB(PORT_GDCINDX, 0x0f);               /* Lock/Unlock GDC                */
  bLockReg = INB(PORT_GDCDATA) & 0x05;    /* get state                      */
  if ((bLockReg & 0x05) != 5)
    fLocked |= ulWD_MASK_GDCLOCK;         /*                                */

  OUTB(PORT_COLRCRTINDX, 0x29);           /* check CRT too                  */
  bLockReg = INB(PORT_COLRCRTDATA);       /* get state                      */
  if ((bLockReg & 0x85) != 0x85)
    fLocked |= ulWD_MASK_CRTLOCK;         /*                                */

  OUTB(PORT_SEQINDX, 0x012);              /* when locked, SEQ index reads   */
  bLockReg = INB(PORT_SEQINDX);           /* as 3 bit only!                 */
  if ((bLockReg & 0xFF) != 0x12)
    fLocked |= ulWD_MASK_SEQLOCK;         /*                                */


  OUTB(PORT_GDCINDX, bGDCIndex);          /* restore GDC index              */
  OUTB(PORT_COLRCRTINDX, bCRTIndex);      /* restore CRT index              */
  OUTB(PORT_SEQINDX, bSEQIndex);          /* restore SEQ index              */
  return(fLocked);
}


/***************************************************************************
 *
 * FUNCTION NAME = vvSVGAGetLockState()
 *
 * DESCRIPTION   = Query Western Digital extended registers lock state.
 *
 * INPUT         = None
 *
 * OUTPUT        = TRUE - if registers are locked
 *                 FALSE- if unlocked
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 ****************************************************************************/

BOOL PRIVENTRY vvSVGAGetLockState(HVDM hvdm)            /*            */
{
  if (!(pVDMData(hvdm)->flVDMVideo & VDM_FGND))         /*            */
      return (FALSE);                                   /*            */

  switch (ulSVGAAdapterType)
  {
    case  VIDEO7_ADAPTER :
      return (vvVideo7LockState());
    case  WESTERNDIG_ADAPTER :
      return (vvWDLockState());
    case  TSENG_ADAPTER :
      return (vvTsengLockState());
    case  S3_ADAPTER :                                  /*            */
      return (vvS3LockState());
    case  TRIDENT_ADAPTER :
    case  ATI_ADAPTER :
    case  CIRRUS_ADAPTER :                              /*            */
    default  :
      return (FALSE);
  }
}

/***************************************************************************
 *
 * FUNCTION NAME = vvNOPSetBank()
 *
 * DESCRIPTION   = NOP set bank  ( do nothing )
 *
 * INPUT         = None
 *
 * OUTPUT        = None
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 ****************************************************************************/

VOID PRIVENTRY vvNOPSetBank(HVDM hvdm,ULONG ulBank,BOOL fSetWriteBank)
{
  hvdm;
  ulBank;
  fSetWriteBank;
}

/***************************************************************************
 *
 * FUNCTION NAME = vvNOPGetBank()
 *
 * DESCRIPTION   = NOP get bank  ( do nothing )
 *
 * INPUT         = None
 *
 * OUTPUT        = None
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 ****************************************************************************/

ULONG PRIVENTRY vvNOPGetBank(HVDM hvdm,BOOL fGetWriteBank)
{
  hvdm;
  fGetWriteBank;
  return (0);
}

/***************************************************************************
 *
 * FUNCTION NAME = vvVideo7SetBank()
 *
 * DESCRIPTION   = Set video7 bank register
 *
 *                 We have two scenarios here, one for the HT205 chip
 *                 (Version 3) and another for HT208/HT209 (Version 4/5)
 *                 chips.
 *
 *                 Version 4/5 are easy, a single register is dedicated to
 *                 selecting the read bank and another to the write bank.
 *                 The chip should first be set into single bank mode before
 *                 setting these registers.
 *
 *                 Version 3 chips are a nightmare.  There are different
 *                 registers to be set according to whether the mode is
 *                 16/256 colour and also for read/write bank selection.
 *
 * INPUT         = hvdm
 *                 ulBank
 *                 fSetWriteBank
 *
 * OUTPUT        = None
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 ****************************************************************************/

VOID PRIVENTRY vvVideo7SetBank(HVDM hvdm,ULONG ulBank,BOOL fSetWriteBank)
{
  register BYTE bBank,bTmp;
  BOOL fColours16;
  PVDMDATA pvd = pVDMData(hvdm);
  BYTE bSeqIndex;


  if (!(pvd->flVDMVideo&VDM_FGND))      /*                         */
    return ;                            /*                         */
                                        /*            save before lock */
  bSeqIndex = INB(PORT_SEQINDX);        /* save seq index */    /*          */
  fColours16 = !((pvd->aregSEQData[REG_SEQMEMMODE]&SEQMEM_CHAIN4) &&
     (pvd->aregSEQData[0xfc]&0x04));

  if (fColours16)
  {

    /*
    ** 16-colour bank select
    */

    OUTB(PORT_SEQINDX,                                          /*          */
         0xf6);                         /* select Bank Select Reg  */
    bTmp = INB(PORT_SEQDATA);           /* get contents            */
    bTmp &= 0xf0;                       /*            reset bits 0,1*/
                                        /* for write and 2,3 for read*/
    bBank = (BYTE)(ulBank&1);           /* only 1 legal bit        */

//    if (!fSetWriteBank)
//      bBank <<= 2;                    /* shift for read bank     */
                                        /*            */
    bBank = (bBank << 2) | bBank;       /* set  read=write bank     */
    bBank |= bTmp;
    OUTB(PORT_SEQDATA,                                          /*          */
         bBank);                        /* set the bank            */
  }

  else

    switch (ulSVGAChipType)
    {

      /*
      ** 256 colour bank select
      */

      case  VIDEO7_HT205_CHIP :         /* Version 3 chip          */
        OUTB(PORT_SEQINDX,                                      /*          */
             0xf9);                     /* Extended Page Select Reg */
        bBank = (BYTE)(ulBank&1);       /* get 1st bit             */
        OUTB(PORT_SEQDATA,                                      /*          */
             bBank);                    /* set this one            */
        bTmp = INB(0x3cc)&0xdf;         /* Misc Output Reg, clear bit 5 */
        bBank = (BYTE)(ulBank&2);       /* get 2nd bit             */
        bBank <<= 4;                    /* move to bit 5           */
        OUTB(0x3c2,
             bBank|bTmp);               /* write new Misc Output reg value */
        OUTB(PORT_SEQINDX,                                      /*          */
             0xf6);                     /* Bank Select Reg         */
        bTmp = INB(PORT_SEQDATA);       /* get current value */ /*          */
        bBank = (BYTE)(ulBank);         /* get 3rd bit             */

        if (fSetWriteBank)
        {
          bTmp &= 0xfc;                 /* clear bits 0-1          */
          bBank = (bBank >> 2)&3;       /* isolate bits 0-1        */
        }

        else
        {
          bTmp &= 0xf3;                 /* clear bits 3-2          */
          bBank &= 0x0c;                /* isolate bits 3-2        */
        }
        OUTB(PORT_SEQDATA,                                      /*          */
             bBank|bTmp);               /* set the bank            */
        break;
      case  VIDEO7_HT208_CHIP :         /* these are Version 4/5 chips */
      case  VIDEO7_HT209_CHIP :

                                        /* first set single bank mode */

        OUTB(PORT_SEQINDX,                                      /*          */
             0xe0);                     /* Miscellaneous Control Reg */
        bTmp = INB(PORT_SEQDATA)&0x7f;  /* clear bit 7 */       /*          */
        OUTB(PORT_SEQDATA,                                      /*          */
             bTmp);                     /* set single bank mode    */
        bBank = (BYTE)ulBank << 4;      /* adjust for 64k          */

        if (pvd->mstateVideo < MEMORY_GRFX)
          bBank |= 0x20;
        OUTB(PORT_SEQINDX,                                      /*          */
             0xe8);                     /* Write Bank Select register */
        OUTB(PORT_SEQDATA,                                      /*          */
             bBank);                    /* set the bank            */
        OUTB(PORT_SEQINDX,                                      /*          */
             0xe9);                     /* Read Bank Select register */
        OUTB(PORT_SEQDATA,                                      /*          */
             bBank);                    /* set the bank            */
        break;
    }

  OUTB(PORT_SEQINDX,                    /*            */        /*          */
       bSeqIndex);                      /* restore SEQ index */
}
/***************************************************************************
 *
 * FUNCTION NAME = vvTridentSetBank()
 *
 * DESCRIPTION   = Set Trident bank register
 *
 *                 Register 0bh at port 3c4h (Hardware Version Register) is
 *                 read-only.
 *
 *                 BUT, By writing to the register the Mode Control
 *                      registers will assume their 'old' definitions.
 *                      Reading the register causes the Mode Control
 *                      registers to assume 'new' definitions.  The
 *                      register's previous state therefore can't be
 *                      restored.  All we need in a multi-tasking
 *                      environment......  Note also that Page bit (bit 1)
 *                      is inverted when setting the write bank and doesn't
 *                      read back as written.
 *
 *                 Mode Control registers 1 & 2 are at 3c4/5 index
 *                 0Dh and 0Eh.
 *
 *                 Write operations require bit 1 (Page Select) to be
 *                 inverted.
 *
 * INPUT         = hvdm
 *                 ulBank
 *                 fSetWriteBank
 *
 * OUTPUT        = None
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 ****************************************************************************/

VOID PRIVENTRY vvTridentSetBank(HVDM hvdm,ULONG ulBank,BOOL fSetWriteBank)
{
  register BYTE bBank,bSeqIndx,bGDCIndx;
  BOOL fNewState;
  PVDMDATA pvd = pVDMData(hvdm);


  if (!(pvd->flVDMVideo&VDM_FGND))      /*                                      */
    return ;                            /*                                      */

  if (pVDMData(hvdm)->mstateVideo >= MEMORY_GRFX)
  {
    bGDCIndx = INB(PORT_GDCINDX);                               /*          */
    OUTB(PORT_GDCINDX,                  /*            */        /*          */
         0x06);                         /*            */
    if ( !(INB(PORT_GDCDATA) & 0x08) )  /*            only if A000 referenced   */
    {                                                           /*          */
        bSeqIndx = INB(PORT_SEQINDX);   /* save SEQ index */    /*          */
        fNewState = VVGetTridentRegDef();
        OUTW(PORT_GDCINDX,                                      /*          */
             0x0506);                   /* memory addressing mode to 64k        */
        OUTB(PORT_SEQINDX,                                      /*          */
             0x0b);                     /* This changes the Mode Control        */
        INB(PORT_SEQDATA);              /* reg's def to 'new' *//*          */
        OUTB(PORT_SEQINDX,                                      /*          */
             0x0e);                     /* select Mode Control reg #1           */
        bBank = (BYTE)ulBank;
        bBank ^= 0x02;                  /* invert the Page bit                  */
        OUTB(PORT_SEQDATA,                                      /*          */
             bBank);                    /* set the bank                         */

        if (!fNewState)
          OUTW(PORT_SEQINDX,                                    /*          */
               0x010b);                 /* set back to 'old' mode               */
        OUTB(PORT_SEQINDX,                                      /*          */
             bSeqIndx);                 /* restore index                        */
    }
    OUTB(PORT_GDCINDX,                                          /*          */
         bGDCIndx);                     /* as opposed to 128k.                  */
  }
}

/***************************************************************************
 *
 * FUNCTION NAME = vvTsengSetBank()
 *
 * DESCRIPTION   = Set Tseng bank register
 *
 *                 Tseng Bank Select is done via port 0x3cd which contains
 *                 bits to select read and write banks.
 *                 The read and write banks are set up here.
 *
 *                 On the ET3000 the layout is:
 *                         bits 0-2 - write bank (64k bank)
 *                         bits 3-5 - read bank (64k bank)
 *
 *                 On the ET4000 the layout is:
 *                         bits 0-3 - write bank (64k bank)
 *                         bits 4-7 - read bank (64k bank)
 *
 * INPUT         = hvdm
 *                 ulBank
 *                 fSetWriteBank
 *
 * OUTPUT        = None
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 ****************************************************************************/

VOID PRIVENTRY vvTsengSetBank(HVDM hvdm,ULONG ulBank,BOOL fSetWriteBank)
{
  BYTE bReadBank,bWriteBank;
  PVDMDATA pvd = pVDMData(hvdm);


  if (!(pvd->flVDMVideo&VDM_FGND))               /*                         */
    return ;                                     /*                         */
  fSetWriteBank;                                 /* shut the compiler up    */
  ulBank &= 0x0f;                                /* prevent any overrun     */
  bWriteBank = (BYTE)ulBank;                     /* set up write bank       */
  bReadBank = bWriteBank << 3;                   /* set up read bank        */

  if (ulSVGAChipType == TSENG_ET4000_CHIP)       /* adjust for ET4000       */
    bReadBank <<= 1;
  OUTB(0x3cd,
       bReadBank|bWriteBank);                    /* write it out            */
}

/***************************************************************************
 *
 * FUNCTION NAME = vvWDSetBank()
 *
 * DESCRIPTION   = Set Western Digital bank register
 *
 *                 Only use Bank Register PR0A because we set up Single Bank
 *                 mode first.  The register has 4k granularity, so adjust
 *                 to 64k.  Registers must be unlocked before we get here.
 *
 * INPUT         = hvdm
 *                 ulBank
 *                 fSetWriteBank
 *
 * OUTPUT        = None
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 ****************************************************************************/

VOID PRIVENTRY vvWDSetBank(HVDM hvdm,ULONG ulBank,BOOL fSetWriteBank)
{
  register BYTE bBank,bTmp;
  PVDMDATA pvd = pVDMData(hvdm);
  register BYTE bGDCIndex, bSEQIndex;                           /*          */


  if (!(pvd->flVDMVideo&VDM_FGND))      /*                         */
    return ;                            /*                         */

                                        /* First set up for Single Bank Mode*/

  fSetWriteBank;                        /* shut the compiler up    */
  bSEQIndex = INB(PORT_SEQINDX);        /* save SEQ index */    /*          */
  OUTB(PORT_SEQINDX,                                            /*          */
       0x11);                           /* Fix System Interface reg */
  bTmp = INB(PORT_SEQDATA);             /* get current value * //*          */
  bTmp &= 0x7f;                         /* clear bit 7             */
  OUTB(PORT_SEQDATA,                                            /*          */
       bTmp);                           /* restore register        */
  OUTB(PORT_SEQINDX, bSEQIndex);        /* restore SEQ index */ /*          */
  bGDCIndex = INB(PORT_GDCINDX);          /* save GDC index */  /*          */
  OUTB(PORT_GDCINDX,                                            /*          */
       0x0b);                                    /* Fix Memory Size
                                                    register                */
  bTmp = INB(PORT_GDCDATA);                  /* disable PR0B */ /*          */
  bTmp &= 0xf7;                                  /* clear bit 3             */
  OUTB(PORT_GDCDATA,                                            /*          */
       bTmp);                                    /* restore register        */

                                                 /* Now set the Bank        */

  bBank = (BYTE)ulBank << 4;                     /* adjust for 64k          */
  OUTB(PORT_GDCINDX,                                            /*          */
       9);                                       /* index PR0A              */
  OUTB(PORT_GDCDATA,                                            /*          */
       bBank);                                   /* set the bank            */
  OUTB(PORT_GDCINDX, bGDCIndex);         /* restore GDC index *//*          */

}

/***************************************************************************
 *
 * FUNCTION NAME = vvATISetBank()
 *
 * DESCRIPTION   = Set ATI bank register
 *
 *                 Set single bank mode for access to video RAM.  Bank
 *                 selection assumes single-bank mode and bank size 64k.
 *
 *                 ATI extended registers are on port 0x1CE.
 *
 *                 We should probably restore the state of the Miscellaneous
 *                 and Memory Page select registers, but hopefully that's
 *                 done as part of Save/Restore IO state.
 *
 * INPUT         = hvdm
 *                 ulBank
 *                 fSetWriteBank
 *
 * OUTPUT        = None
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 ****************************************************************************/

#pragma optimize("", off)                               /*            */

VOID PRIVENTRY vvATISetBank(HVDM hvdm,ULONG ulBank,BOOL fSetWriteBank)
{
  register BYTE bBank,bTmp;
  PVDMDATA pvd = pVDMData(hvdm);


  if (!(pvd->flVDMVideo&VDM_FGND))               /*                         */
    return ;                                     /*                         */
  fSetWriteBank;                                 /* shut the compiler up    */

                                         /* First set up for Single Bank Mode*/

  OUTB(PORT_ATIVGA_INDX,
       0xbe);                                    /* Miscellaneous register  */
  bTmp = INB(PORT_ATIVGA_DATA);                  /* get current contents    */
  OUTB(PORT_ATIVGA_INDX,
       0xbe);                                    /* clear bit 3 (dual page
                                                    mode)                   */
  OUTB(PORT_ATIVGA_DATA,
       bTmp&~0x08);                              /* set back single page
                                                    mode                    */
  OUTB(PORT_ATIVGA_INDX,
       0xb2);                                    /* Memory Page Select      */
  bTmp = INB(PORT_ATIVGA_DATA)&0xe1;             /* get current contents    */

                                            /* isolate bank select bits (4-1)*/

  bBank = (BYTE)ulBank << 1;                     /* shift into place (bits
                                                    4-1)                    */
  bBank |= bTmp;                                 /* combine other previous
                                                    bits                    */
  OUTB(PORT_ATIVGA_INDX,
       0xb2);                                    /* Memory Page Select      */
  OUTB(PORT_ATIVGA_DATA,
       bBank);                                   /* Finally set the bank    */
}

#pragma optimize("", on)                                /*            */

/***************************************************************************
 *
 * FUNCTION NAME = vvIBMSetBank()
 *
 * DESCRIPTION   = Set IBM bank register
 *
 * INPUT         = hvdm
 *                 ulBank
 *                 fSetWriteBank
 *
 * OUTPUT        = None
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 ****************************************************************************/

VOID PRIVENTRY vvIBMSetBank(HVDM hvdm,ULONG ulBank,BOOL fSetWriteBank)
{

  if (!(pVDMData(hvdm)->flVDMVideo&VDM_FGND))
    return ;
  fSetWriteBank;                                 /* shut the compiler up    */
  OUTB(ulXGAInstance+AXGA_APERTURE_INDEX,
       (BYTE)ulBank);
}

/***************************************************************************
 *
 * FUNCTION NAME = vvCirrusSetBank()
 *
 * DESCRIPTION   = Set Cirrus bank register
 *
 * INPUT         = hvdm
 *                 ulBank
 *                 fSetWriteBank
 *
 * OUTPUT        = None
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 * NOTE: To address 2 MB of RAM, set bit 5 of 3ce,b to 1. Shift bank only twice.
 ****************************************************************************/
/*            */
VOID    PRIVENTRY vvCirrusSetBank(HVDM hvdm, ULONG ulBank, BOOL fSetWriteBank)
{
        register BYTE   bBank, bTmp, bIndx;
        PVDMDATA        pvd = pVDMData(hvdm);

        if ( !(pVDMData(hvdm)->flVDMVideo & VDM_FGND) )
            return;

        fSetWriteBank;                  // shut the compiler up

        bIndx = INB(PORT_GDCINDX);      // get current contents /*          */

        OUTB(PORT_GDCINDX, 0x09);       // Offset register      /*          */
        bTmp = INB(PORT_GDCDATA) & 0x0f;// get current contents /*          */

        bBank = (BYTE)ulBank << 4;      // shift into place (bits 7-4)
        bBank |= bTmp;                  // combine other previous bits

        OUTB(PORT_GDCDATA, bBank);      // Set Bank             /*          */
        OUTB(PORT_GDCINDX, bIndx);      // Restore Index        /*          */
}

/***************************************************************************
 *
 * FUNCTION NAME = vvS3SetBank()
 *
 * DESCRIPTION   = Set S3 bank register(s)
 *
 * INPUT         = hvdm
 *                 ulBank
 *                 fSetWriteBank
 *
 * OUTPUT        = None
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 * NOTE: To address 2/4 MB of RAM, set bit 2/3 of 3d4,51 to 1.
 ****************************************************************************/
/*            */
VOID    PRIVENTRY vvS3SetBank(HVDM hvdm, ULONG ulBank, BOOL fSetWriteBank)
{
  register BYTE   bBanklo, bBankhi, bTmp, bIndx;
  PVDMDATA        pvd = pVDMData(hvdm);

  if ( !(pvd->flVDMVideo & VDM_FGND) )
      return;
  if (!v8514Accelerated(hvdm) && (pvd->mstateVideo == MEMORY_GRFX))
      ulBank <<= 2;                     // bank granularity is still 64K
  fSetWriteBank;                        // shut the compiler up

  bIndx = INB(PORT_COLRCRTINDX);        // get current contents /*          */

  OUTB(PORT_COLRCRTINDX, 0x35);         // Offset register      /*          */
  bTmp = INB(PORT_COLRCRTDATA) & 0xf0;  // get current hi-nib   /*          */

  bBanklo = (BYTE)ulBank & 0x0f;        // mask into place (bits 3-0)
  bBanklo |= bTmp;                      // combine other previous bits

  OUTB(PORT_COLRCRTDATA, bBanklo);      // Set Bank             /*          */

  OUTB(PORT_COLRCRTINDX, 0x51);         // Offset register      /*          */
  bTmp = INB(PORT_COLRCRTDATA);         // get current contents /*          */
  bTmp &= 0xF3;                         // clear bits 2,3
  bBankhi = ((BYTE)ulBank & 0x30) >> 2; // get extended bit 2 in reg 51
  bBankhi |= bTmp;                      // combine other previous bits

  OUTB(PORT_COLRCRTDATA, bBankhi);      // Set Bank (2/4 MB addressing)/*          */

  OUTB(PORT_COLRCRTINDX, bIndx);        // Restore Index        /*          */
}
/***************************************************************************
 *
 * FUNCTION NAME = vvVideo7GetBankFgnd()
 *
 * DESCRIPTION   = Get Video7 bank register ( Foreground )
 *
 * INPUT         = hvdm
 *                 fGetWriteBank
 *
 * OUTPUT        = ulBank
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 ****************************************************************************/

ULONG PRIVENTRY vvVideo7GetBankFgnd(HVDM hvdm,BOOL fGetWriteBank)
{
  ULONG ulBank;
  BYTE bSeqIndex,bTmp;
  BOOL fColours16;
  PVDMDATA pvd = pVDMData(hvdm);

  vvUnLockSVGARegisters();

  bSeqIndex = INB(PORT_SEQINDX);        /* save SEQ index    */ /*          */
  switch (ulSVGAChipType)
  {
    case  VIDEO7_HT205_CHIP :                    /* Version 3 chip          */
    case  VIDEO7_HT208_CHIP :                    /* these are Version 4/5
                                                    chips                   */
      fColours16 = !((pvd->aregSEQData[REG_SEQMEMMODE]&SEQMEM_CHAIN4) &&
         (pvd->aregGDCData[REG_GDCMODE]&GDCMODE_256COLOR));

      if (fColours16)
      {

        /*
        ** 16-colour bank select
        */

        OUTB(PORT_SEQINDX,                                      /*          */
             0xf6);                     /* select Bank Select Reg  */
        ulBank = (BYTE)INB(PORT_SEQDATA);/* get contents */     /*          */

        if (!fGetWriteBank)
          ulBank >>= 2;                 /* read bank bits are 2-3  */
        ulBank &= 0x03;
      }

      else
      {

        /*
        ** 256 colour bank select
        */

        OUTB(PORT_SEQINDX,                                      /*          */
             0xf9);                             /* Extended Page Select reg */
        ulBank = (BYTE)INB(PORT_SEQDATA)&0x01;  /* get bit 0 */ /*          */

                                        /* bit 5 in Misc. Output Reg is bit 1*/

        ulBank |= (INB(0x3cc)&0x20) >> 4;
        OUTB(PORT_SEQINDX,                                      /*          */
             0xf6);                     /* Bank Select Reg         */
        bTmp = INB(PORT_SEQDATA);       /* get Bank Select */   /*          */

        if (fGetWriteBank)
          bTmp <<= 2;
        bTmp &= 0x0c;                   /* isolate bits 2-3        */
        ulBank |= (ULONG)bTmp;          /* combine                 */
      }
      break;
    case  VIDEO7_HT209_CHIP :
      if (fGetWriteBank)
        OUTB(PORT_SEQINDX,                                      /*          */
             0xe8);                     /* Write Bank Select */

      else
        OUTB(PORT_SEQINDX,                                      /*          */
             0xe9);                     /* Read Bank Select */
      ulBank = INB(PORT_SEQDATA) >> 4;  /* get bank */          /*          */

      if (pvd->mstateVideo < MEMORY_GRFX)
        ulBank &= ~0x2;
      break;
  }
  OUTB(PORT_SEQINDX, bSeqIndex);        /* restore SEQ index */ /*          */
  return (ulBank&0x0f);
}

/***************************************************************************
 *
 * FUNCTION NAME = vvVideo7GetBankBgnd()
 *
 * DESCRIPTION   = Get Video7 bank register ( Background )
 *
 * INPUT         = hvdm
 *                 fGetWriteBank
 *
 * OUTPUT        = ulBank
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 ****************************************************************************/

ULONG PRIVENTRY vvVideo7GetBankBgnd(HVDM hvdm,BOOL fGetWriteBank)
{
  ULONG ulBank;
  BYTE bTmp;
  BOOL fColours16;
  PVDMDATA pvd = pVDMData(hvdm);


  switch (ulSVGAChipType)
  {
    case  VIDEO7_HT205_CHIP :                    /* Version 3 chip          */
    case  VIDEO7_HT208_CHIP :                    /* these are Version 4/5
                                                    chips                   */
      fColours16 = !((pvd->aregSEQData[REG_SEQMEMMODE]&SEQMEM_CHAIN4) &&
         (pvd->aregGDCData[REG_GDCMODE]&GDCMODE_256COLOR));

      if (fColours16)
      {

        /*
        ** 16-colour bank select
        */

        ulBank = pvd->aregSEQData[0xf6];

        if (!fGetWriteBank)
          ulBank >>= 2;
        ulBank &= 0x03;
      }

      else
      {

        /*
        ** 256 colour bank select
        */

        ulBank = pvd->aregSEQData[0xf9]&1;
        ulBank |= (pvd->regMiscOut&0x20) >> 4;
        bTmp = pvd->aregSEQData[0xf6];

        if (fGetWriteBank)
          bTmp <<= 2;
        bTmp &= 0x0c;                            /* isolate bits 2-3        */
        ulBank |= (ULONG)bTmp;                   /* combine                 */
      }
      break;
    case  VIDEO7_HT209_CHIP :

      if (fGetWriteBank)
        ulBank = pvd->aregSEQData[0xe8];

      else
        ulBank = pvd->aregSEQData[0xe9];
      ulBank >>= 4;

      if (pvd->mstateVideo < MEMORY_GRFX)
        ulBank = 0;
      break;
  }
  return (ulBank&0x0f);
}

/***************************************************************************
 *
 * FUNCTION NAME = vvVideo7GetBank()
 *
 * DESCRIPTION   = Get Video7 bank register
 *
 * INPUT         = hvdm
 *                 fGetWriteBank
 *
 * OUTPUT        = ulBank
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 ****************************************************************************/

ULONG PRIVENTRY vvVideo7GetBank(HVDM hvdm,BOOL fGetWriteBank)
{
  PVDMDATA pvd = pVDMData(hvdm);


  if (pvd->flVDMVideo&VDM_FGND)
    return (vvVideo7GetBankFgnd(hvdm,
                                fGetWriteBank));

  else
    return (vvVideo7GetBankBgnd(hvdm,
                                fGetWriteBank));
}

/***************************************************************************
 *
 * FUNCTION NAME = vvTsengGetBank()
 *
 * DESCRIPTION   = Get Tseng bank register
 *
 * INPUT         = hvdm
 *                 fGetWriteBank
 *
 * OUTPUT        = ulBank
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 ****************************************************************************/

ULONG PRIVENTRY vvTsengGetBank(HVDM hvdm,BOOL fGetWriteBank)
{
  ULONG ulBank;

  fGetWriteBank;                                 /* shut the compiler up    */
  ulBank = (ULONG)pVDMData(hvdm)->regTsengSegSelect;
  ulBank &= (ulSVGAChipType == TSENG_ET4000_CHIP)?0x0f:0x07;
  return (ulBank);
}

/***************************************************************************
 *
 * FUNCTION NAME = vvTridentGetBank()
 *
 * DESCRIPTION   = Get Trident bank register
 *
 *                 Tseng Bank Select is done via port 0x3cd which contains
 *                 bits to select read and write banks.
 *                 The read and write banks are set up here.
 *
 *                 On the ET3000 the layout is:
 *                         bits 0-2 - write bank (64k bank)
 *                         bits 3-5 - read bank (64k bank)
 *
 *                 On the ET4000 the layout is:
 *                         bits 0-3 - write bank (64k bank)
 *                         bits 4-7 - read bank (64k bank)
 *
 * INPUT         = hvdm
 *                 fGetWriteBank
 *
 * OUTPUT        = ulBank
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 ****************************************************************************/

ULONG PRIVENTRY vvTridentGetBank(HVDM hvdm,BOOL fGetWriteBank)
{
  ULONG ulBank = 0;
  register BYTE bSeqIndx;
  BOOL fNewState;
  PVDMDATA pvd = pVDMData(hvdm);


  if (pvd->mstateVideo >= MEMORY_GRFX)
  {

    if (pvd->flVDMVideo&VDM_FGND)
    {
      bSeqIndx = INB(PORT_SEQINDX);     /* save SEQ index */    /*          */
      fNewState = VVGetTridentRegDef();
      OUTB(PORT_SEQINDX,                                        /*          */
           0x0b);                       /* This changes Mode
                                           Control regs            */
      INB(PORT_SEQDATA);                /* def 'old' to 'new' *//*          */
      OUTB(PORT_SEQINDX,                                        /*          */
           0x0e);                       /* select Mode Control reg
                                           #1                      */
      ulBank = (BYTE)INB(PORT_SEQDATA);                         /*          */

      if (!fNewState)
        OUTW(PORT_SEQINDX,                                      /*          */
             0x010b);                   /* set back to 'old' mode  */
      OUTB(PORT_SEQINDX,                                        /*          */
           bSeqIndx);
    }

    else
    {
      ulBank = pvd->aregSEQData[0x0e]^0x02;
    }
  }
  return (ulBank&0x0f);
}

/***************************************************************************
 *
 * FUNCTION NAME = vvWDGetBank()
 *
 * DESCRIPTION   = Get Western Digital bank register
 *
 *
 * INPUT         = hvdm
 *                 fGetWriteBank
 *
 * OUTPUT        = ulBank
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 ****************************************************************************/

ULONG PRIVENTRY vvWDGetBank(HVDM hvdm,BOOL fGetWriteBank)
{
  ULONG ulBank;
  PVDMDATA pvd = pVDMData(hvdm);
  register BYTE bGDCIndex;                                      /*          */

  fGetWriteBank;                                 /* shut the compiler up    */

  if (pvd->flVDMVideo&VDM_FGND)
  {

    vvUnLockSVGARegisters();
    bGDCIndex = INB(PORT_GDCINDX);        /* save GDC index */  /*          */
    OUTB(PORT_GDCINDX,                                          /*          */
         9);                               /* index PR0A   */
    ulBank = (BYTE)INB(PORT_GDCDATA) >> 4; /* get the bank */   /*          */
    OUTB(PORT_GDCINDX, bGDCIndex);       /* restore GDC index *//*          */

  }

  else
  {
    ulBank = pvd->aregGDCData[9] >> 4;
  }
  return (ulBank&0x0f);
}

/***************************************************************************
 *
 * FUNCTION NAME = vvATIGetBank()
 *
 * DESCRIPTION   = Get ATI bank register
 *
 *                 The ATI Bank Register can be used in one of two modes.
 *
 *                     In Dual Bank Mode: bits 7-5 are read bank
 *                                             3-1 are write bank
 *
 *                     In Single Bank Mode: bits 4-1 are read/write bank
 *
 * INPUT         = hvdm
 *                 fGetWriteBank
 *
 * OUTPUT        = ulBank
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 ****************************************************************************/

ULONG PRIVENTRY vvATIGetBank(HVDM hvdm,BOOL fGetWriteBank)
{
  ULONG ulBank,bTmp;
  PVDMDATA pvd = pVDMData(hvdm);


  if (pvd->flVDMVideo&VDM_FGND)
  {
    OUTB(0x1ce,
         0xbe);                                  /* Miscellaneous register  */
    bTmp = INB(0x1cf);                           /* get current contents    */
    OUTB(0x1ce,
         0xb2);                                  /* Memory Page Select      */
    ulBank = INB(0x1cf);                         /* get contents            */
  }

  else
  {
    bTmp = pvd->aregATIData[0xbe];
    ulBank = pvd->aregATIData[0xb2];
  }

  if (bTmp&0x08)                                 /* bit set => dual page
                                                    mode                    */
    ulBank >>= ((fGetWriteBank)?1:5);

  else                                           /* otherwise single bank
                                                    mode                    */
    ulBank >>= 1;
  return (ulBank&0x0f);
}

/***************************************************************************
 *
 * FUNCTION NAME = vvIBMGetBank()
 *
 * DESCRIPTION   = Get IBM bank register
 *
 *
 * INPUT         = hvdm
 *                 fGetWriteBank
 *
 * OUTPUT        = ulBank
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 ****************************************************************************/

ULONG PRIVENTRY vvIBMGetBank(HVDM hvdm,BOOL fGetWriteBank)
{
  ULONG ulBank;
  PVDMDATA pvd = pVDMData(hvdm);


  if (pvd->flVDMVideo&VDM_FGND)
    ulBank = INB(ulXGAInstance+AXGA_APERTURE_INDEX);

  else
    ulBank = pvd->aregXGAData[AXGA_APERTURE_INDEX];
  return (ulBank&0x0f);
}

/***************************************************************************
 *
 * FUNCTION NAME = vvCirrusGetBank()
 *
 * DESCRIPTION   = Get Cirrus bank register
 *
 * INPUT         = hvdm
 *                 fGetWriteBank
 *
 * OUTPUT        = ulBank
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 * NOTE: In dual-page mapping scheme, both offset registers can be used.
 * Address A(15) bit selects Offset 1. Currently not taken into account.
 *
 ****************************************************************************/
/*            */
ULONG   PRIVENTRY vvCirrusGetBank(HVDM hvdm, BOOL fGetWriteBank)
{
  ULONG           ulBank, bTmp;
  BYTE            b2MBAddressing;
  PVDMDATA        pvd = pVDMData(hvdm);

  if ( pvd->flVDMVideo & VDM_FGND ) {

      bTmp = INB(PORT_GDCINDX);         // get current contents /*          */

      OUTB(PORT_GDCINDX, 0x0B);         // Mode extn register   /*          */
      b2MBAddressing = (INB(PORT_GDCDATA) & 0x20)               /*          */
          && ((ulSVGAChipType == CIRRUS_5426_CHIP) ||
              (ulSVGAChipType == CIRRUS_5428_CHIP));
      OUTB(PORT_GDCINDX, 0x09);         // Offset register      /*          */
      ulBank = INB(PORT_GDCDATA);       // get current contents /*          */

      OUTB(PORT_GDCINDX, bTmp);         // Restore Index        /*          */

  } else {

      b2MBAddressing = ((ulSVGAChipType == CIRRUS_5426_CHIP) ||
                        (ulSVGAChipType == CIRRUS_5428_CHIP)) &&
          (pvd->aregGDCData[0x0B] & 0x20);
      ulBank = pvd->aregGDCData[0x09];
  }
  if (b2MBAddressing)
  {
      ulBank = (ulBank & 0x7F);
      ulBank >>= 2;                                             //          
  }
  else
      ulBank >>= 4;

  return (ulBank);
}

/***************************************************************************
 *
 * FUNCTION NAME = vvS3GetBank()
 *
 * DESCRIPTION   = Get S3 bank register(s)
 *
 * INPUT         = hvdm
 *                 fGetWriteBank
 *
 * OUTPUT        = ulBank
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 *
 ****************************************************************************/
ULONG   PRIVENTRY vvS3GetBank(HVDM hvdm, BOOL fGetWriteBank)            /*            */
{
  ULONG           ulBank, bTmp;
  PVDMDATA        pvd = pVDMData(hvdm);

  if ( pvd->flVDMVideo & VDM_FGND ) {

      bTmp = INB(PORT_COLRCRTINDX);     // get current contents /*          */

      OUTB(PORT_COLRCRTINDX, 0x35);     // Offset register      /*          */
      ulBank = INB(PORT_COLRCRTDATA) & 0x0f;
                                        // get current contents /*          */

      OUTB(PORT_COLRCRTINDX, 0x51);     // Mode extn register   /*          */
      ulBank |= (INB(PORT_COLRCRTDATA) & 0x0C) << 2;
                                        // bits 2,3 are bits 4,5/*          */

      OUTB(PORT_COLRCRTINDX, bTmp);     // Restore Index        /*          */

  } else {
      ulBank = pvd->aregCRTData[0x35] & 0x0f;
      ulBank |= pvd->aregCRTData[0x51] & 0x0C << 2;
  }

  if ((pvd->mstateVideo == MEMORY_GRFX) && !v8514Accelerated(hvdm))
      ulBank >>= 2;
  return (ulBank);
}

/***************************************************************************
 *
 * FUNCTION NAME = vvMaxBanks()
 *
 * DESCRIPTION   = Calculate maximum number of banks
 *
 *                 Calculate the maximum number of banks used by this mode.
 *                 Note this doesn't take into account that the mode might
 *                 be a planar one.
 *
 *                 Note: the fixups for Western Digital and Trident are due
 *                 to CRT values not being 'correct' in the standard
 *                 registers which we use for the calculation.
 *                 Also the test for nBytes == 160 occurs only with certain
 *                 DRAM configurations which result in the CRT horizontal
 *                 Display End register reflecting this value. We therefore
 *                 cannot tell the difference between a normal mode set
 *                 and deliberately altered values which would allow for
 *                 horizontal panning.
 *
 * INPUT         = hvdm
 *
 * OUTPUT        = ulMaxBanks
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 ****************************************************************************/
ULONG PRIVENTRY vvMaxBanks(HVDM hvdm)
{
  register PVDMDATA pvd = pVDMData(hvdm);
  ULONG nBytes,nRows,nBitsPixel,nCols;

  if ((!ulSVGAAdapterType) ||                    /* no PMI loaded           */
     ((pvd->ulBIOSMode&~BIOSVINFO_DONTCLEAR) == BIOSVMODE_CO320X200X256)   /*            */
      && !(pvd->flVDMXVideo & VDMX_ENHANCEDMODE))/*            */
    return (1);

  /*
  ** If IO-trapping is OFF then save all of VRAM, we make no assumptions...
  */
  if (pvd->flVDMXVideo & VDMX_NOIOTRAPPING)                     /*            */
  {
    if ((pvd->ulBIOSMode&~BIOSVINFO_DONTCLEAR) <= BIOSVMODE_CO320X200X256)
      return (1);
    if (pvd->mstateVideo == MEMORY_GRFX256)
      return (ulSVGAVRAMSize >> 16);
    else if (pvd->mstateVideo == MEMORY_GRFX)
    {                                                           /*            */
      if((ulSVGAAdapterType == S3_ADAPTER) && v8514Accelerated(hvdm))
        /* If accelerated, pixel organization is packed */
        return(ulSVGAVRAMSize >> 16);
      else
        return (ulSVGAVRAMSize >> 18);
    }
  }                                                             /*            */

  nRows = pvd->vvMode.vvm_nRows;                 /*            */
  nCols = pvd->vvMode.vvm_nCols;                 /*            */
  nBitsPixel = (pvd->mstateVideo == MEMORY_GRFX256)?8:1;
  nBytes = (nCols / 8) * nBitsPixel;             /*            */

  /*
  ** Check for logical line length greater than the physical line.
  ** If so, use logical line length in determining maximum number
  ** of banks.
  */
  if (pvd->ulLogicalBytes > nBytes)              /*            */
      nBytes = pvd->ulLogicalBytes;              /*            */

  nBytes *= pvd->nPlanes;                        /*            */

  /*
  ** For 256-colour modes, where on-demand shadow buffer allocation
  ** is a myth....
  **
  ** This is an attempt to ensure that as much VRAM is saved as possible
  ** without wasting resources. We will encounter apps that use off-screen
  ** VRAM, the size of which we do not know. The only way to catch these
  ** types of access is to invalidate the A000 region on every bank switch,
  ** and thereby allow the shadow buffer to be grown as required.
  ** This requires registers to be trapped in the foreground which would
  ** give poor performance. The next best alternative is to calculate
  ** number of banks for given mode and round the number of
  ** banks saved to the nearest 256k boundary.
  */

  if (pvd->flVDMXVideo & VDMX_ENHANCEDMODE)   /* V2.0DRW06 */
  {

    register ULONG      ulMaxBanks;

    if (ulSVGAAdapterType == S3_ADAPTER && v8514Accelerated(hvdm))
       ulMaxBanks = ulSVGAVRAMSize >> 16;     /* save entire VRAM, as offscreen may be used*/
    else
    {
       ulMaxBanks = ((nBytes * nRows ) >> 16)+1;

       /*            round it to 256K boundary */
       ulMaxBanks += 3;
       ulMaxBanks >>= 2;
       ulMaxBanks <<= 2;
       if (ulMaxBanks > (ulSVGAVRAMSize >> 16))    /* Do sanity check to ensure */
           ulMaxBanks = ulSVGAVRAMSize >> 16;     /* we don't try to save more */
    }
    return (ulMaxBanks);                        /* VRAM than there really is */
  }
  else if ((pvd->mstateVideo == MEMORY_GRFX) || /* V2.0DRW06 */
           (pvd->mstateVideo == MEMORY_GRFX256))
    {
       register ULONG      ulMaxBanks;

       ulMaxBanks = ((nBytes * nRows) >> 18) + 1;
       /*
       ** If planar mode greater than 640x480 and less than 2
       ** banks calculated as used, increase to 2, so that we
       ** cover cases in which extra memory is used, but not
       ** known as used.                                           
       */
       if (pvd->mstateVideo == MEMORY_GRFX && nRows > 480 &&
           nCols  > 640 && ulMaxBanks < 2)
          ulMaxBanks = 2;
       return ulMaxBanks;
    }
  else
    return (((nBytes * nRows ) >> 16)+1);

}

/***************************************************************************
 *
 * FUNCTION NAME = vvSVGALinearMode
 *
 * DESCRIPTION   = Get SVGA linear mode.
 *
 *                 This req'd because the GDC Mode reg doesn't reflect
 *                 256-colour modes.
 *
 *                 ATI has bits in extended 'ATI registers'.
 *                 Video7 have bits in extended sequencer 0xfc.
 *
 * INPUT         = hvdm
 *
 * OUTPUT        = True = 256 colour mode
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 * REMARK: More on the fault hook processing...
 *         Page mapping depends on the state evaluated from the shadow registers,
 *         which has to represent the true state of the hardware. If the register
 *         is not added to the arleMemory structure, its value represents the
 *         true state only after the mode set has returned, when vvUpdateAll will
 *         refresh them all. If the mappings are believed to be       at that
 *         time, they will be invalidated and fault hooks enabled again.
 *         However, if evaluating 256 mode is essential before the mode set
 *         returns, ensure that the register which is being evaluated is added
 *         to the arleMemory struc.
 *
 ****************************************************************************/

BOOL PRIVENTRY vvSVGALinearMode(HVDM hvdm)
{
  BOOL fLinearMode = FALSE;
  register ulData;
  register PVDMDATA pvd = pVDMData(hvdm);


  switch (ulSVGAAdapterType)
  {
    case  ATI_ADAPTER :
      ulData = pvd->aregATIData[0xb0];

      switch (ulSVGAChipType)
      {
        case  ATI_18800_CHIP :
          ulData &= 0x3e;                        /* isolate bits 1, 2, 4, 5 */
          break;
        case  ATI_28800_CHIP :
          ulData &= 0x20;                        /* isolate bit 5           */
          break;
      }
      fLinearMode = !!ulData;                    /* if any bits set, we're
                                                    256-colour              */
      break;
    case  VIDEO7_ADAPTER :                       /* isolate bit 2           */
      ulData = pvd->aregSEQData[0xfc]&0x04;
      fLinearMode = !!ulData;                    /* if any bits set, we're
                                                    256-colour              */
      break;
    case S3_ADAPTER:                             /*            */
      ulData = pvd->aregCRTData[0x3a]&0x10;      /* bit 4 set for linear modes */
      fLinearMode = !!ulData;                    /* if any bits set, we're
                                                    256-colour              */
      break;
  }
  return  fLinearMode;
}

/***************************************************************************
 *
 * FUNCTION NAME = vvIdentifySVGA
 *
 * DESCRIPTION   = Get SVGA chip id and VRAM size.
 *
 * INPUT         = none
 *
 * OUTPUT        = sSVGAData structure
 *
 * RETURN-NORMAL = TRUE
 * RETURN-ERROR  = FALSE
 *
 ****************************************************************************/

BOOL PRIVENTRY vvIdentifySVGA(OEMSVGAINFO *sSVGAData)          /*            */
{
  HFILE hScreenDD;        // handle of SCREENDD$ device driver
  ULONG ulOpenAction;   // action taken to open device
  ULONG rc;             // function return code
  ULONG ulSize = sizeof (OEMSVGAINFO );

  if ( rc = VDHOpen(SCREENDD_NAME,
                       SSToDS(&hScreenDD),
                       SSToDS(&ulOpenAction),
                       0L, 0, VDHOPEN_FILE_OPEN,
                       VDHOPEN_ACCESS_READONLY | VDHOPEN_SHARE_DENYNONE,
                       0L)) {
      rc = VDHDevIOCtl( hScreenDD,
                        SCREENDD_CATEGORY,
                        SVGA_ID,
                        NULL, 0L, NULL,
                        (VOID *) sSVGAData,
                        ulSize,
                        NULL);

      if (rc)                                         /*            */
      {
         ulSize = sizeof (OEMINFO );
         sOEMData.Length = ulSize;
         rc = VDHDevIOCtl( hScreenDD,
                           SCREENDD_CATEGORY,
                           SVGA_OEM,
                           NULL, 0L, NULL,
                           &sOEMData,
                           ulSize,
                           NULL);
      }

      VDHClose(hScreenDD);
  }
  return rc;
} /*            end */
#pragma  END_SWAP_CODE

