/*DDK*************************************************************************/
/*                                                                           */
/* COPYRIGHT (C) Microsoft Corporation, 1989                                 */
/* 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 = SVGAMODE.C
 *
 * DESCRIPTIVE NAME = BASE VIDEO DEVICE HANDLER  - PRIVATES
 *
 *
 * VERSION      V2.0
 *
 * DATE
 *
 * DESCRIPTION  Super VGA specific routines to set/get video modes.
 *
 *              BASE VIDEO DEVICE HANDLER  - PRIVATES
 *
 *              THIS SOURCE FILE CONTAINS ROUTINES WHICH ARE NOT PUBLIC.
 *              THEY ARE CALLED BY VARIOUS VDH ROUTINES TO PERFORM I/O
 *              FUNCTIONS.
 *
 *              THESE ROUTINES EXECUTE AS RING 2 CONFORMING
 *
 * FUNCTIONS
 *
 * NOTES        NONE
 *
 * STRUCTURES   NONE
 *
 * EXTERNAL REFERENCES
 *
 *              NONE
 *
 * EXTERNAL FUNCTIONS
 *
 *              NONE
 *
*/

#define  INCL_BASE                     /* ALL of OS/2 Base                  */
#define  INCL_DOSDEVICES               /* Device specific, ring 2 support   */
#define  INCL_OS2STD                   /* Needed for NULL definition in     */
                                       /* OS2STD.H                          */
#include <os2.h>
#include <memory.h>
#include "svgatype.h"
#pragma  intrinsic(memcpy,memcmp,memset)

#define NO_MATCH  0xFFFF                /*          */
/*
**      Externals
*/

extern PVIDEOMODE pModes;              /*            */
extern PMEMORYMAPS pMemoryMaps;        /*            */
extern USHORT usMaxVideoModes;         /*            */
extern FPHWCOMMAND pHWLockData;        /*            */
extern FPHWCOMMAND pHWUnLockData;      /*            */
extern FPHWCOMMAND pHWCleanData;       /*            */
extern FPHWCOMMAND pHWCurrSetBank;     /*            */
extern OEMSVGAINFO SVGAHardware;
extern OEMINFO OEMHardware;            /*            */
extern ULONG PartialSaveSize;
extern ENTRYPOINT FAR *ChainedEntryPoint[];
extern CLUTDATA ColourCLUT16[];
extern CLUTDATA ColourCLUT256[];
extern CLUTDATA MonoCLUT16[];
extern CLUTDATA MonoCLUT256[];
extern BOOL PMILoaded;

extern USHORT FAR WDPanelOn( VOID );                            /*          */
extern VOID FAR WDSetPanelClk( VOID );                          /*          */

USHORT FAR HardwareColor(VOID);
USHORT NEAR AdjustModeLen(USHORT *ReqModeLen);                  /*          */
VOID EXPENTRY AccessRegister(PREGADDRESS,USHORT,PBYTE);
VOID PASCAL FAR AccessHardware(PREGADDRESS,USHORT,USHORT,USHORT,PREGDATA);
VOID EXPENTRY AccessVideoEnable(USHORT,USHORT,USHORT FAR *);
VOID PASCAL FAR AccessCLUT(USHORT,PREGDATA);
VOID PASCAL NEAR SaveRestoreHW(PENVIRONMENT,USHORT);
VOID PASCAL FAR AccessFont(PBYTE,PBYTE,USHORT,USHORT);
VOID PASCAL FAR SetDiamondClk(USHORT,USHORT,USHORT);    /*            */  /*            */
VOID PASCAL FAR SetNumber9Clk(USHORT);                  /*            */
extern USHORT NEAR PhysToUVirt(FARADDRESS,FARADDRESS FAR *,USHORT);
extern VOID NEAR FreePhysToUVirt(USHORT);

/***************************************************************************
 *
 * FUNCTION NAME = SVGASetBank()
 *
 * DESCRIPTION   =
 *
 *
 * INPUT         = NONE
 *
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL =
 *
 * RETURN-ERROR  = NONE
 *
 * METHOD
 *      Search for 1st regop assign
 *      set list data to Bank
 *
 **************************************************************************/

VOID  APIENTRY FAR SVGASetBank(USHORT usBank)
{
  FPHWCOMMAND pHWCmd = pHWCurrSetBank;

  for (;OFFSETOF(pHWCmd) &&
       (pHWCmd->usCommand != PMICMD_REGOP) &&
       (pHWCmd->usIndexPort != PMI_REGOP_ASSIGN);
       pHWCmd = MAKEP(SELECTOROF(pHWCmd),pHWCmd->pNextCmd));

  if (!pHWCmd)
    return;

  pHWCmd->usData = usBank;

  ExecCommands(pHWCurrSetBank);
}


/***************************************************************************
 *
 * FUNCTION NAME = SetupTridentRegisters(VOID)
 *
 * DESCRIPTION   =
 *
 * INPUT         = NONE
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL = NONE
 * RETURN-ERROR  = NONE
 *
 **************************************************************************/

VOID NEAR SetupTridentRegisters(VOID)
{
  USHORT TempData,ChipVersion;
  REGDATA RegData;
  REGADDRESS RegAddress;

  RegAddress.AddressPort = SEQADDRESSPORT;
  RegAddress.DataPort = SEQDATAPORT;
  RegAddress.Flags = NONE;
  RegData.DataArea = (PBYTE)&TempData;
  RegData.NumEntries = 1;
  RegData.FirstEntry = 0x0b;           /* set 'new' definition */
  AccessHardware(&RegAddress, BYTES, NONE, GET, &RegData);
  ChipVersion = TempData&0xff;
  RegData.FirstEntry = 0x0e;           /* get bank select contents */
  AccessHardware(&RegAddress, BYTES, NONE, GET, &RegData);
  TempData &= 0xf0;
  TempData |= 0x02;                    /* set bank 0 */
  AccessHardware(&RegAddress, BYTES, NONE, SET, &RegData);

  if (ChipVersion >= 3)
  {
    RegData.FirstEntry = 0x0e;
    AccessHardware(&RegAddress, BYTES, NONE, GET, &RegData);
    TempData = (TempData|0x80)^0x02;
                                       /* unprotect register 0x0c */
    AccessHardware(&RegAddress, BYTES, NONE, SET, &RegData);
    RegData.FirstEntry = 0x0c;
    AccessHardware(&RegAddress, BYTES, NONE, GET, &RegData);
    TempData &= ~0x60;

    if (SVGAHardware.Memory > 256L*1024L)
      TempData |= 0x40;

    else
      TempData |= 0x20;
    AccessHardware(&RegAddress, BYTES, NONE, SET, &RegData);
  }
}

/***************************************************************************
 *
 * FUNCTION NAME = FixTridentRegisters()
 *
 * DESCRIPTION   =
 *
 *      Trident Mode Control Registers at 0x3c4, index 0x0e changes
 *      definition when the Chip Version register at index 0x0b is
 *      read (new) and written (old). When in new mode, Mode Control
 *      Register 1 is the used for bank selection. Here, we ensure
 *      that the bank is cleared out to zero.
 *
 *      Bit 1 is interpreted as if it were inverted, therefore to
 *      clear it, it must be written out SET.
 *
 * INPUT         = NONE
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL = NONE
 * RETURN-ERROR  = NONE
 *
 **************************************************************************/

VOID NEAR FixTridentRegisters(VOID)
{
  INT Color;
  USHORT TempData;
  REGDATA RegData;
  REGADDRESS RegAddress;

  RegAddress.AddressPort = SEQADDRESSPORT;
  RegAddress.DataPort = SEQDATAPORT;
  RegAddress.Flags = NONE;
  RegData.DataArea = (PBYTE)&TempData;
  RegData.NumEntries = 1;
  RegData.FirstEntry = 0x0b;           /* set 'old' definition */
  AccessHardware(&RegAddress, BYTES, NONE, SET, &RegData);

  TempData = 0;
  RegData.FirstEntry = 0x0d;           /* clear Mode Control reg 2 */
  AccessHardware(&RegAddress, BYTES, NONE, SET, &RegData);

  RegData.FirstEntry = 0x0b;           /* set 'new' definition */
  AccessHardware(&RegAddress, BYTES, NONE, GET, &RegData);

  RegData.FirstEntry = 0x0d;           /* get Mode Control reg 2 */
  AccessHardware(&RegAddress, BYTES, NONE, GET, &RegData);

  TempData &= ~1;                      /* clear ext CLK select */
  AccessHardware(&RegAddress, BYTES, NONE, SET, &RegData);

  RegData.FirstEntry = 0x0e;           /* get bank select contents */
  AccessHardware(&RegAddress, BYTES, NONE, GET, &RegData);

  TempData &= 0xf0;
  TempData |= 0x02;                    /* set bank 0 */
  AccessHardware(&RegAddress, BYTES, NONE, SET, &RegData);

  Color = HardwareColor();
  RegAddress.AddressPort = CRTADDRESSPORT;
  RegAddress.DataPort = CRTDATAPORT;
  RegAddress.ColorAdjust = COLORADJUSTMENT;
  RegAddress.Flags = NONE;
  RegData.DataArea = (PBYTE)&TempData;
  RegData.FirstEntry = 0x1e;
  RegData.NumEntries = 1;
  AccessHardware(&RegAddress, BYTES, Color, GET, &RegData);

  TempData &= ~0x04;
  AccessHardware(&RegAddress, BYTES, Color, SET, &RegData);

}

/***************************************************************************
 *
 * FUNCTION NAME = CleanupSVGARegisters()
 *
 * DESCRIPTION   =
 *
 * INPUT         = NONE
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL = NONE
 * RETURN-ERROR  = NONE
 *
 **************************************************************************/

VOID NEAR CleanupSVGARegisters(PENVIRONMENT Environment)
{

  if (!PMILoaded)
    return ;

  ExecCommands(pHWUnLockData);                                 /*            */
  ExecCommands(pHWCleanData);                                  /*            */
}

/***************************************************************************
 *
 * FUNCTION NAME = FixSVGARegisters()
 *
 * DESCRIPTION   = Restore registers that get trashed by a regular VGA SetMode
 *                 which writes to 'undocumented' IBM VGA registers, etc......
 *
 * INPUT         = NONE
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL = NONE
 * RETURN-ERROR  = NONE
 *
 **************************************************************************/

VOID NEAR FixSVGARegisters(PENVIRONMENT Environment)
{
  USHORT ColorMode,VideoEnabled,VideoOn;

  if (!PMILoaded)
    return ;
  ColorMode = HardwareColor();
  AccessVideoEnable(ColorMode, GET, &VideoEnabled);
  VideoOn = 0;                        /* disable video during these fixups */
  AccessVideoEnable(ColorMode, SET, &VideoOn);

  if (SVGAHardware.AdapterType == TRIDENT_ADAPTER)
    FixTridentRegisters();
  else
    CleanupSVGARegisters(Environment);

  if (VideoEnabled)
  {
    VideoOn = 1;                       /* re-enable video if necessary */
    AccessVideoEnable(ColorMode, SET, &VideoOn);
  }

}

/***************************************************************************
 *
 *  SUBROUTINE NAME: SetEnvMode
 *
 *  DESCRIPTIVE NAME: Set the environment mode
 *
 *  FUNCTION: SetEnvMode is called to update the environment buffer
 *            with the current video mode.
 *
 *  ENTRY POINT: SetEnvMode
 *    LINKAGE:   CALL FAR
 *
 *  INPUT: (Passed on stack)
 *             UCHAR Mode ( Index in table of supported modes )
 *             FAR * Environment ( far pointer to environment buffer )
 *             FAR * HWEnvironment (pointer to hardware state buffer)
 *         (Referenced)
 *             Modes[] (global data - table of supported video modes )
 *
 *  EXIT-NORMAL: Environment buffer is updated to reflect HW state
 *
 *  EFFECTS: NONE
 *
 *  INTERNAL REFERENCES:
 *    ROUTINES: NONE
 *
 *  EXTERNAL REFERENCES:
 *    ROUTINES: NONE
 *
 ****************************************************************************/

VOID PASCAL NEAR SetEnvMode(USHORT Mode,PENVIRONMENT Environment,PENVIRONMENT
                             HWEnvironment,USHORT GetMode)
{
  PVIDEOMODE pCurMode;

  if (GetMode)
  {
    pCurMode = &pModes[Environment->ModeIndex = Mode]; /*            */
    Environment->ModeData.cb = pCurMode->cb;
    Environment->ModeData.fbType = pCurMode->fbType;
    Environment->ModeData.color = pCurMode->color;
    Environment->ModeData.col = pCurMode->col;
    Environment->ModeData.row = pCurMode->row;
    Environment->ModeData.hres = pCurMode->hres;
    Environment->ModeData.vres = pCurMode->vres;
    Environment->ModeData.fmt_ID = pCurMode->fmt_ID;
    Environment->ModeData.attrib = pCurMode->attrib;
    Environment->ModeData.BufferAddress = pMemoryMaps[pCurMode->MemMap].
       Start.FullAddress;
    Environment->ModeData.BufferLength = pMemoryMaps[pCurMode->MemMap].
       TotalSize;
    Environment->ModeData.FullBufferSize = Environment->ModeData.BufferLength;
    Environment->ModeData.PartialBufferSize = (ULONG)4000;
    Environment->ModeData.ExtDataArea = NULL;
  }
}

/*****************************************************************************
 *
 * FUNCTION NAME = SetModeFont()
 *
 * DESCRIPTION   = Set an appropriate font for the given column mode.
 *                 Search for best fit by making a chained call to
 *                 BVHVGA. If best fit leaves empty scan lines between the rows
 *                 greater than 1/2 the pelrows size of the font,
 *                 try double-scanning. If that doesn't produce a font
 *                 load the previously found font.
 *                 Get the font data from from BVHVGA.
 *                 This data is then loaded into the video buffer's
 *                 font-plane.
 *
 * INPUT         = ModeIndex, Environment, SetHardware flag, pDoubleScan
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL = 0
 * RETURN-ERROR  = ERROR_VIO_FONT
 *
 ***************************************************************************/
/*           new version of the font function */
USHORT PASCAL NEAR SetModeFont(USHORT ModeIndex,PENVIRONMENT Environment,
                               BOOL SetHardware,
                               PUSHORT pDoubleScan)
{
  USHORT rc = 0;
  SEL FontSelector;
  VDH_FONT ParmBlock =
  {
    14,2,NULL,0,0,0
  };
  FARADDRESS Source,Destination;
  USHORT ColorMode;
  USHORT FontFound;
  USHORT TempData, RegsLocked, VertDispEnd, ScanRow;
  REGADDRESS RegAddress;
  REGDATA RegData;
  USHORT DoubleScan = 0;
  USHORT VerticalRes, DoubleScanRows, DoubleScanColumns;                /*            */

  VerticalRes = pModes[ModeIndex].vres;

  /*
  ** All mode/font vertical parameters are USHORT, therefore sign
  ** checking must be done on all comparisons.
  */
  if (VerticalRes <= Environment->ModeData.row)
     return ERROR_VIO_FONT;
  ParmBlock.PelRows    = VerticalRes / Environment->ModeData.row;
  ParmBlock.PelColumns = pModes[ModeIndex].hres / Environment->ModeData.col;
  ParmBlock.FontBuffer = NULL;
  ParmBlock.FontLength = 0;


  /*
  ** first call to get size of Font Buffer
  */
  rc = ChainedGetCurrentFont((PENVIRONMENT)(&Environment->VGAEnvironment),
     &ParmBlock, FnGetFont);

  if (!rc)
  {

    if (!(rc = DosAllocSeg(ParmBlock.FontLength, &FontSelector, 0)))
    {
      SELECTOROF(ParmBlock.FontBuffer) = FontSelector;

      FontFound = FALSE;

      for (;ParmBlock.PelColumns && !FontFound;ParmBlock.PelColumns--)
      {

        ParmBlock.PelRows    = VerticalRes / Environment->ModeData.row;

        for(;ParmBlock.PelRows && !FontFound;ParmBlock.PelRows--)
        {

           /*
           ** Query if this font is available
           */
           if (!(rc = ChainedGetCurrentFont((PENVIRONMENT)
                      (&Environment->VGAEnvironment), &ParmBlock, FnGetFont)))
           {
              /*
              ** If empty scan lines between the rows are greater than
              ** 1/2 of the font size, try double scanning. If double
              ** scan doesn't produce a font, go for the spaced one.
              */
              if (((VerticalRes / Environment->ModeData.row) - ParmBlock.PelRows) >
                   (ParmBlock.PelRows >> 1))
              {
                 DoubleScan = 1;
                 DoubleScanRows = ParmBlock.PelRows;
                 DoubleScanColumns = ParmBlock.PelColumns;              /*            */
                 VerticalRes >>= 1;
                 /*
                 ** Start the search again. Add 1 since we
                 ** are entering the same loop and PelRows
                 ** will be decremented by 1.
                 */
                 ParmBlock.PelRows = VerticalRes / Environment->ModeData.row + 1;
              }
              else
              {
                 /*                                                         
                 ** We have the font, but breaking out of this loop would still
                 ** decrement the PelColumns in the outer loop, so in order to preserve
                 ** the correct dimensions, increment the PelColumns.
                 */
                 ParmBlock.PelColumns++;
                 FontFound = TRUE;
                 break;
              }
           }
        }
      }
      if (!FontFound)
      {
        if (DoubleScan)
        {
          /*
          ** Double scanning didn't find a better fit. A font is already
          ** in the buffer from the previous fit. Reset the error code
          ** from the last font search failure, double scan flag and vertical res.
          */
          FontFound = TRUE;
          DoubleScan = 0;
          ParmBlock.PelRows = DoubleScanRows;
          ParmBlock.PelColumns = DoubleScanColumns;              /*            */
          VerticalRes <<= 1;
          rc = 0;
        }
        else
        {
          DosFreeSeg(FontSelector);
          return ERROR_VIO_FONT;
        }
      }
      if (SetHardware)
      {
        Source.FullAddress = PVB_FONT_ADDRESS;
        /*
        ** copy the font to the video buffer
        */
        if (!(rc = PhysToUVirt(Source, &Destination, PVB_FONT_SIZE)))
        {
           ParmBlock.Flags |= ENVFLAG_NO_VALIDATION;            /*            */
           ChainedSetCurrentFont((PENVIRONMENT) (&Environment->VGAEnvironment),
                      &ParmBlock, FnSetFont);
           AccessFont(ParmBlock.FontBuffer, Destination.FullAddress,
                      ParmBlock.PelRows, ParmBlock.PelColumns);

           ColorMode = Environment->ModeData.fbType & MODE_FLAG_NOT_MONO;
           RegAddress.AddressPort = CRTADDRESSPORT;
           RegAddress.DataPort    = CRTDATAPORT;
           RegAddress.ColorAdjust = COLORADJUSTMENT;
           RegAddress.Flags       = NONE;
           RegData.NumEntries   = 1;

           /*
           ** Set number of scan lines per character row in CRT 9
           */
           RegData.FirstEntry   = 0x09;
           RegData.DataArea     = (PBYTE)&TempData;
           AccessHardware( &RegAddress, BYTES, ColorMode, GET, &RegData );
           TempData &= 0xe0;
           ScanRow = VerticalRes / Environment->ModeData.row;
           TempData |= ScanRow - 1;
           if (DoubleScan)
             TempData |= 0x80;
           AccessHardware( &RegAddress, BYTES, ColorMode, SET, &RegData );

           /*
           ** Ensure registers 0-7 are unprotected.
           */
           RegData.FirstEntry = 0x11;
           AccessHardware( &RegAddress, BYTES, ColorMode, GET, &RegData );
           if ((RegsLocked = TempData) & 0x80)
           {
              TempData ^= 0x80;
              AccessHardware( &RegAddress, BYTES, ColorMode, SET, &RegData );
           }

           /*
           ** Set Vertical Display End
           */
           VertDispEnd  = (Environment->ModeData.row * ScanRow);
           if (DoubleScan) VertDispEnd <<= 1;
           TempData = VertDispEnd - 1;
           RegData.FirstEntry = 0x12;
           AccessHardware( &RegAddress, BYTES, ColorMode, SET, &RegData );

           /*
           ** Set Vertical Display End overflow (bit 8,9 of display end are
           ** in bits 1 and 6.
           */
           RegData.FirstEntry = 0x07;
           AccessHardware( &RegAddress, BYTES, ColorMode, GET, &RegData );
           TempData &= 0xBD;                    //reset bits 1 and 6
           if (VertDispEnd & 0x100)
              TempData |= 0x2;                  //bit 8 is set. Set bit 1
           if (VertDispEnd & 0x200)
              TempData |= 0x40;                 //bit 9 is set. Set bit 6
           AccessHardware( &RegAddress, BYTES, ColorMode, SET, &RegData );

           /*
           ** Relock CRTs if necessary
           */
           if (RegsLocked)
           {
             TempData = RegsLocked;
             RegData.FirstEntry = 0x11;
             AccessHardware( &RegAddress, BYTES, ColorMode, SET, &RegData );
           }

           FreePhysToUVirt(Destination.Part.Selector);
           DosFreeSeg(FontSelector);
        }
      }
    }

    if (!rc)
    {
      if (pDoubleScan)
        *pDoubleScan = DoubleScan;
      /*
      **  Font found. Set the Environment PelRows
      */
      Environment->PelRows = ParmBlock.PelRows;        /*          */
      return 0;
    }

  }
  return rc;
}
/*****************************************************************************
 *           
 * FUNCTION NAME = SearchFont()
 *
 * DESCRIPTION   = Search for an available font to match the given mode.
 *
 * INPUT         = NONE
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL = 0
 * RETURN-ERROR  = ERROR
 *
 ***************************************************************************/

USHORT PASCAL NEAR SearchFont(USHORT ModeIndex, PENVIRONMENT Environment,
                              PVDHMODEINFO pReqMode,
                              PUSHORT pDoubleScan)
{
  USHORT tempModeDataRow, tempModeDataCol;
  USHORT rc;
  /*
  ** Restore these values if Search failed.
  */
  tempModeDataRow = Environment->ModeData.row;
  tempModeDataCol = Environment->ModeData.col;
  Environment->ModeData.row = pReqMode->row;
  Environment->ModeData.col = pReqMode->col;
  if (rc = SetModeFont(ModeIndex, Environment, FALSE, pDoubleScan))
  {
     Environment->ModeData.row = tempModeDataRow;
     Environment->ModeData.col = tempModeDataCol;
  }
  return rc;
}

/*****************************************************************************
 *
 *  SUBROUTINE NAME: SetHWMode
 *
 *  DESCRIPTIVE NAME: Set the hardware mode
 *
 *  FUNCTION: SetHWMode is called to set the hardware to the supported
 *            video mode. If Flags = UPDATE_HARDWARE, the registers
 *            are set.  If environment buffer is passed, the registers
 *            are shadowed.
 *
 *  ENTRY POINT: SetHWMode
 *
 *  INPUT: (Passed on stack)
 *             USHORT Mode ( Index in table of supported modes )
 *
 *         (Referenced)
 *             Modes[] (global data - table of supported video modes )
 *
 *  EXIT-NORMAL: Video mode is set on hardware
 *
 *  EFFECTS: NONE
 *
 *  INTERNAL REFERENCES:
 *    ROUTINES:
 *
 *  EXTERNAL REFERENCES:
 *    ROUTINES: AccessHardware, AccessRegister
 *            
 *  If not Graphics, search thru mode table to find CRTCtlRegs_CMD with cursor
 *  start/end data and change them to be the 2nd to last and last pel in the
 *  row          index 0xB -> end   0xA -> start
 *
 *  Turn the video signal back on
 *
 ****************************************************************************/

USHORT PASCAL NEAR SetHWMode(USHORT ModeIndex,USHORT Flags,PENVIRONMENT
                              Environment)
{
  USHORT rc;
  USHORT EnvBufferPassed;
  USHORT UpdateHWare;
  USHORT ColorMode;
  USHORT VideoEnable;
  USHORT VideoOn;
  REGDATA RegData;
  REGADDRESS RegAddress;
  PCLUTDATA CurrentCLUT;
  USHORT CLUTLength;
/*BOOL fLocked;       -gone-                         */
  INT Color = 1;                       /*            */
  USHORT TempCursor;                   /*            */

  rc = NO_ERROR;
  UpdateHWare = Flags&ENVFLAG_HARDWARE;

  if (EnvBufferPassed = SEG(Environment))
    ColorMode = Environment->ModeData.fbType;
  else
    ColorMode = pModes[ModeIndex].fbType; /*            */
  ColorMode = !(ColorMode&MODE_FLAG_NO_CLR_BRST);

  if (!(pModes[ModeIndex].fbType&MODE_FLAG_GRAPHICS) || (pModes[ModeIndex].
     color == 4))
  {
    CurrentCLUT = (ColorMode)?&ColourCLUT16[0]:&MonoCLUT16[0];
    CLUTLength = (ColorMode)?COLOURCLUT16_LENGTH:MONOCLUT16_LENGTH;
  }
  else
  {
    CurrentCLUT = (ColorMode)?&ColourCLUT256[0]:&MonoCLUT256[0];
    CLUTLength = (ColorMode)?COLOURCLUT256_LENGTH:MONOCLUT256_LENGTH;
  }

  if (UpdateHWare)
  {

    if (EnvBufferPassed)
      VideoOn = Environment->VideoEnable;

    else
      AccessVideoEnable(TRUE, GET, &VideoOn);
    VideoEnable = 0;                   /* Turn video off                    */
    AccessVideoEnable(TRUE, SET, &VideoEnable);

    ExecCommands(pHWUnLockData);                                /*          */

    if (OEMHardware.Manufacturer == DIAMOND_MANUFACTURER)       /*          */  /*            */
       SetDiamondClk(Environment->ModeData.hres,
                     Environment->ModeData.fbType,
                     Environment->ModeData.color);              /*          */  /*            */

    ExecCommands(pModes[ModeIndex].pHWSetModeData);

    if( (SVGAHardware.AdapterType == WESTERNDIG_ADAPTER)        /*          */
        && ((SVGAHardware.ChipType == WESTERNDIG_WD9024_CHIP)   /*          */
            || (SVGAHardware.ChipType == WESTERNDIG_WD9026_CHIP)/*          */
            || (SVGAHardware.ChipType == WESTERNDIG_WD9026_CHIP)/*          */
            )                                                   /*          */
        /* It's a laptop! Are external only modes disallowed?*/ /*          */
        && WDPanelOn() )                                        /*          */
      WDSetPanelClk();                                          /*          */

/*  ExecCommands(pHWLockData);                                               */

    pHWCurrSetBank = pModes[ModeIndex].pHWSetBank;
    Environment->pSetBankData = pModes[ModeIndex].pHWSetBank;

  /*  note for #9, you have to set adapter to enhanced mode                  */   /*            */
  /*  before setting the external clock                                      */
    if (OEMHardware.Manufacturer == NUMBER9_MANUFACTURER)      /*            */
      if ((Environment->ModeData.fbType & 0x02) ||     /* graphics mode      */
          (Environment->ModeData.hres == 1056) ||      /* 132 cols text      */
          (Environment->ModeData.hres == 1188))
         SetNumber9Clk(Environment->ModeData.hres);

  }

  if (UpdateHWare)
  {

    if (!(pModes[ModeIndex].fbType&MODE_FLAG_GRAPHICS))
    {
      rc = SetModeFont(ModeIndex, Environment, TRUE, NULL);             /* drw */

      /*
      **            Set Cursor size CRT registers.
      */
      RegAddress.DataPort = CRTDATAPORT;
      RegAddress.ColorAdjust = COLORADJUSTMENT;
      RegAddress.Flags = NONE;
      RegAddress.AddressPort = CRTADDRESSPORT;
      TempCursor = (Environment->PelRows-2) | (Environment->PelRows-1)<<8;
      RegData.FirstEntry = 0xA;
      RegData.DataArea = (PBYTE)&TempCursor;
      RegData.NumEntries = 2;
      AccessHardware(&RegAddress, BYTES, Color, SET, &RegData);
    }                                           /*            end */
    if(!(pModes[ModeIndex].fbType&MODE_FLAG_GRAPHICS) ||
        pModes[ModeIndex].color <= 8)            /*            */  /*            */
    {
                                              /* set up CLUT */
      RegData.FirstEntry = 0;
      RegData.DataArea = (PBYTE)CurrentCLUT;
      RegData.NumEntries = CLUTLength;
      AccessCLUT(SET, &RegData);
    }
  }

  if (EnvBufferPassed)
  {                                    /* clear out */
    memset(&Environment->LookupTable[0], 0, 256*sizeof(CLUTDATA));
                                       /* put copy of CLUT into environment */
    memcpy(&Environment->LookupTable[0], CurrentCLUT, CLUTLength *sizeof
       (CLUTDATA));
  }

  if (UpdateHWare && VideoOn)
  {
    VideoEnable = 1;                   /* Turn video on */
    AccessVideoEnable(TRUE, SET, &VideoEnable);
  }
  return (rc);
}

/*****************************************************************************
 *
 * FUNCTION NAME = GetModeIndex
 *
 * DESCRIPTION   = Within each chipset and chip type, an examination of
 *                 just 5 registers is all that is needed to determine
 *                 which of the supported mode is currently set.
 *
 *                 The registers examined are:
 *                                 - Miscellaneous Output Register
 *                                 - Attribute Mode Control Register
 *                                 - Attribute Color Plane Enable Register
 *                                 - Sequencer Clocking Mode Register
 *                                 - Sequencer Memory Mode Register
 *
 *                 If no mode is found use modeindex from environment
 *
 * INPUT         = Environment
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL = NONE
 * RETURN-ERROR  = NONE
 *
 ***************************************************************************/

USHORT NEAR GetModeIndex(PENVIRONMENT Environment)
{
  if (Environment->ModeIndex < usMaxVideoModes)
    return(Environment->ModeIndex);
  else
    return(-1);
}

/*****************************************************************************
 *
 * FUNCTION NAME = SearchModeTable
 *
 * DESCRIPTION   =
 *
 *  Find the mode based upon matching specified parameters with entry in the
 *  table of supported modes.  If a partial parameter list is provided, use
 *  the highest resolution mode that matches the provided parameters.
 *
 *  Verify that the mode is valid by finding it in the mode table
 *
 *  Type matches - If color specified, check for match. Otherwise
 *                 default color, col, row, hres, vres to this mode
 *
 *  Color matches - If col specified, check for match.  Otherwise
 *                  default col, row, hres, vres to this mode
 *
 *  hres matches - If vres specified, check for match.
 *                 Otherwise default vres to this mode
 *
 * INPUT         = int *Mode,PVDHMODEINFO pReqMode
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL = NONE
 * RETURN-ERROR  = NONE
 *
 ***************************************************************************/

USHORT NEAR SearchModeTable(int *Mode,PVDHMODEINFO pReqMode)
{
  int i;
  PVIDEOMODE pCurMode;
  USHORT ReqModeLen;
  USHORT fModeFound = FALSE;
  USHORT fNativeTextMode;              /*            */
  USHORT fPartialModeFound = NO_MATCH;   /*            */
  USHORT fPanelLimited = FALSE;

  if( (SVGAHardware.AdapterType == WESTERNDIG_ADAPTER)          /*          */
      && ((SVGAHardware.ChipType == WESTERNDIG_WD9024_CHIP)     /*          */
          || (SVGAHardware.ChipType == WESTERNDIG_WD9026_CHIP)  /*          */
          || (SVGAHardware.ChipType == WESTERNDIG_WD9027_CHIP)))/*          */
    /* It's a laptop! Are external only modes disallowed?  */   /*          */
    fPanelLimited = WDPanelOn();                                /*          */
  ReqModeLen = pReqMode->cb;
  fNativeTextMode = (!(pReqMode->fbType&MODE_FLAG_GRAPHICS) &&  
     ((pReqMode->col == 40) ||
      (pReqMode->col == 80) ||
      (pReqMode->col == 132)));

  /*
  ** bail out early if not enough info for us, ie. not native or 132 column
  */

  if (ReqModeLen < FIELDOFFSET(VDHMODEINFO, row))
    return (FALSE);

  if (fNativeTextMode && (!(pReqMode->row) || !(pReqMode->col)))        /*          */
    return (FALSE);

  /*
  ** loop down so we find highest resolution first
  */

  for (i = usMaxVideoModes-1; (i >= 0) && !fModeFound; i--)
  {
    pCurMode = &pModes[i];
    if( fPanelLimited && (pCurMode->hres > 720) )
      continue;

    /*            start */
    if (fNativeTextMode)
    {

      if (pCurMode->fbType & MODE_FLAG_GRAPHICS)
        continue;

      if ((ReqModeLen >= FIELDOFFSET(VDHMODEINFO, fmt_ID)) &&
         (pReqMode->vres != pCurMode->vres))
        continue;

      else

        if ((ReqModeLen >= FIELDOFFSET(VDHMODEINFO, vres)) &&
           (pReqMode->hres != pCurMode->hres))
          continue;

        else
          /*           
          ** If columns are the same, but rows not, we have a
          ** partial match if the colors match.
          ** If more than one partial match found, choose the one
          ** rows closer to the ones requested.
          ** If both columns and rows the same, full match
          ** found if colors match.
          ** If ReqModeLen < rows, match just color and columns.
          */
          if ((ReqModeLen >= FIELDOFFSET(VDHMODEINFO, row)) &&
             (pReqMode->col == pCurMode->col))
          {
            if ((ReqModeLen >= FIELDOFFSET(VDHMODEINFO, hres)) &&
               (pReqMode->row == pCurMode->row))
            {
               if(pReqMode->color == pCurMode->color)
                  fModeFound = TRUE;
               else
                  continue;
            }
            else
            {
               if(pReqMode->color == pCurMode->color)
               {
                  /*
                  ** If this is the first partial match, remember the mode
                  ** If one partial match already exists, set the mode to
                  ** current if reqeusted rows are less than current rows. If
                  ** greater, keep previous partially matched mode.
                  ** This logic depends on mode table being in ascending order
                  ** in respect to the rows.
                  */
                  if ((fPartialModeFound == NO_MATCH) ||
                     ((fPartialModeFound != NO_MATCH) && (pReqMode->row < pCurMode->row)))
                  {
                     fPartialModeFound = i;
                  }
                  continue;
               }
               else
                  continue;
            }
          }
          else if (ReqModeLen < FIELDOFFSET(VDHMODEINFO, row))
          {
            if((ReqModeLen >= FIELDOFFSET(VDHMODEINFO, col)) &&
               (pReqMode->color == pCurMode->color))
               fModeFound = TRUE;
            else
               continue;
          }
          else
            continue;
    }
    /*            end */
    else
    {

      if ((ReqModeLen >= FIELDOFFSET(VDHMODEINFO, fmt_ID)) &&
         (pReqMode->vres != pCurMode->vres))
        continue;

      else

        if ((ReqModeLen >= FIELDOFFSET(VDHMODEINFO, vres)) &&
           (pReqMode->hres != pCurMode->hres))
          continue;

        else

          if ((ReqModeLen >= FIELDOFFSET(VDHMODEINFO, hres)) &&
             (pReqMode->row != pCurMode->row))
            continue;

          else

            if ((ReqModeLen >= FIELDOFFSET(VDHMODEINFO, row)) &&
               (pReqMode->col != pCurMode->col))
              continue;

            else

              if ((ReqModeLen >= FIELDOFFSET(VDHMODEINFO, col)) &&
                 (pReqMode->color != pCurMode->color))
                continue;

              else

                if ((ReqModeLen >= FIELDOFFSET(VDHMODEINFO, color)) &&
                   ((BYTE)(pReqMode->fbType&~MODE_FLAG_NO_CLR_BRST) ==
                   pCurMode->fbType))

                  fModeFound = TRUE;
    }

    if (fModeFound)
      *Mode = i;
  }
  if (!(fModeFound) && (fPartialModeFound != NO_MATCH))       /*            */
  {
      *Mode = fPartialModeFound;
      fModeFound = TRUE;
  }                                                            /*            */
  return (fModeFound);
}

/*****************************************************************************
 *
 * FUNCTION NAME = SetSVGAMode()
 *
 * DESCRIPTION   =
 *
 *      Parameters are valid - Update environment buffer if it was passed
 *                           - Update hardware if in foreground
 *
 *      Put all of the caller's parameters into the environment buffer.
 *      Fill in missing parameters with defaults in the mode table.
 *
 *      If Flags = UPDATE_HARDWARE, SetHWMode will set the hardware registers.
 *      If environment buffer is passed, SetHWMode will shadow the registers.
 *
 *      For 132 column modes, chain to BVHVGA to let it do some internal
 *      updates which will allow us not to have to process scroll and cursor
 *      position functionality etc.
 *
 *      Note the new flag 'ENVFLAG_NO_VALIDATION' which means BVHVGA should
 *      blindly accept the mode data passed without validating it. In other
 *      words, this data comes from a chained BVH which 'knows what its doing'.
 *
 * INPUT         = PENVIRONMENT Environment,PVDH_MODE ParmBlock
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL = NONE
 * RETURN-ERROR  = NONE
 *
 ***************************************************************************/

USHORT NEAR SetSVGAMode(PENVIRONMENT Environment,PVDH_MODE ParmBlock)
{
  USHORT rc;
  USHORT ModeIndex;
  USHORT tempflags;
  USHORT EnvBufferSeg;
  PVIDEOMODE pCurMode;
  PVDHMODEINFO pReqMode;
  USHORT fModeFound;
  VDH_MODE TmpParmBlock;
  VDHMODEINFO ReqModeInfo;
  USHORT ReqModeLen;
  USHORT fNativeTextMode,DoubleScan;              /*                        */

  ReqModeLen = (pReqMode = ParmBlock->pVDHModeInfo)->cb;
  fNativeTextMode = (!(pReqMode->fbType & MODE_FLAG_GRAPHICS) &&
                     ((pReqMode->col == 40) ||          /*            */
                      (pReqMode->col == 80) ||
                      (pReqMode->col == 132)));
  rc = ERROR_VIO_INVALID_LENGTH;
  EnvBufferSeg = SEG(Environment);

  if ((ParmBlock->Length >= sizeof(VDH_MODE)) && (ReqModeLen >= FIELDOFFSET
     (VDHMODEINFO, color)))
  {
    rc = ERROR_VIO_MODE;               /* Initialize to error               */
    tempflags = ParmBlock->Flags;

    if (pReqMode->fbType&MODE_FLAG_NO_CLR_BRST)
      tempflags |= MODE_FLAG_NO_CLR_BRST;/* Indicate B/W Mode               */

    if (pModes && (fModeFound = SearchModeTable(&ModeIndex, pReqMode)) &&
       (pMemoryMaps[pModes[ModeIndex].MemMap].TotalSize <= SVGAHardware.Memory ))
    {
      if (fNativeTextMode)                                      /*            */
      {
        if (rc = SearchFont(ModeIndex, Environment, pReqMode, &DoubleScan))
           return (rc);
      }
      pCurMode = &pModes[ModeIndex];

      if (EnvBufferSeg)
      {
        Environment->ModeIndex = ModeIndex;
        Environment->ModeData.cb = ReqModeLen;
        Environment->ModeData.fbType = pReqMode->fbType;
        AdjustModeLen(&ReqModeLen);                             /*            */
        if (ReqModeLen > FIELDOFFSET(VDHMODEINFO, color))
          Environment->ModeData.color = pReqMode->color;
        else
          Environment->ModeData.color = pCurMode->color;

        if (ReqModeLen > FIELDOFFSET(VDHMODEINFO, col))
          Environment->ModeData.col = pReqMode->col;
        else
          Environment->ModeData.col = pCurMode->col;

        if (ReqModeLen > FIELDOFFSET(VDHMODEINFO, row))
          Environment->ModeData.row = pReqMode->row;
        else
          Environment->ModeData.row = pCurMode->row;

        if (ReqModeLen > FIELDOFFSET(VDHMODEINFO, hres))
          Environment->ModeData.hres = pReqMode->hres;
        else
          Environment->ModeData.hres = pCurMode->hres;

        if (ReqModeLen > FIELDOFFSET(VDHMODEINFO, vres))
          Environment->ModeData.vres = pReqMode->vres;
        else
          Environment->ModeData.vres = pCurMode->vres;

        if (ReqModeLen > FIELDOFFSET(VDHMODEINFO, fmt_ID))
          Environment->ModeData.fmt_ID = pReqMode->fmt_ID;
        else
          Environment->ModeData.fmt_ID = pCurMode->fmt_ID;

        if (ReqModeLen > FIELDOFFSET(VDHMODEINFO, attrib))
        {

          if (Environment->ModeData.fmt_ID == ATTRIB_WORLDFORMAT)
            Environment->ModeData.attrib = ATTRIB_WORLDCOUNT;
          else
            Environment->ModeData.attrib = pCurMode->attrib;
        }

        if (ReqModeLen > FIELDOFFSET(VDHMODEINFO, BufferAddress))
          Environment->ModeData.BufferAddress = pMemoryMaps[pCurMode->MemMap].
             Start.FullAddress;

        if (ReqModeLen > FIELDOFFSET(VDHMODEINFO, BufferLength))
          Environment->ModeData.BufferLength = (Environment->ModeData.fbType
             &MODE_FLAG_GRAPHICS)?(pMemoryMaps[pCurMode->MemMap].TotalSize):(2
                *Environment->ModeData.col *Environment->ModeData.row);

        if (ReqModeLen > FIELDOFFSET(VDHMODEINFO, ExtDataArea))
          Environment->ModeData.ExtDataArea = NULL;

      }
      rc = SetHWMode(ModeIndex, tempflags, Environment);

      if (fNativeTextMode)
      {                                /*            */
        if (EnvBufferSeg)
          memcpy(&ReqModeInfo, &Environment->ModeData, FIELDOFFSET(VDHMODEINFO, attrib));
        else
          memcpy(&ReqModeInfo, pCurMode, FIELDOFFSET(VDHMODEINFO, attrib));
        ReqModeInfo.cb = FIELDOFFSET(VDHMODEINFO, attrib);
        TmpParmBlock.Length = ParmBlock->Length;
        TmpParmBlock.Flags = ENVFLAG_NO_VALIDATION;
        TmpParmBlock.pVDHModeInfo = &ReqModeInfo;
        rc = ChainedSetMode((PENVIRONMENT)(&Environment->VGAEnvironment),
           &TmpParmBlock, FnSetMode);

        if ((DoubleScan) && (tempflags & ENVFLAG_HARDWARE)) /*            */
          Environment->ModeData.vres >>= 1;

      }
    }
  }
  /*
  **           
  ** Deallocate pCmdData and build a new one in order to handle SetMode
  ** calls in the background without the HARDWARE flag. Do this only if
  ** HARDWARE flag not set, to avoid creating the list for environments
  ** which may never be saved/or terminated (such as Popup).              
  */
  if ((!rc) && !(tempflags & ENVFLAG_HARDWARE))         /*            */
  {
    if (Environment->pCmdData)
      SVGAFreeMem(&Environment->pCmdData);              /* remove old table, if any */
    SVGACopyCmdList(pModes[Environment->ModeIndex].pHWSetModeData,
                    &Environment->pCmdData);
  }
  return (rc);
}

/*****************************************************************************
 *
 * FUNCTION NAME = AdjustModeLen()
 *
 * DESCRIPTION   =
 *
 *      Adjust the Requested Mode length to an exact offset into
 *      the VDHMODEINFO structure. This makes compares and use of
 *      SetReqModeData() simpler to implement.
 *
 * INPUT         = USHORT *ReqModeLen
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL = NONE
 * RETURN-ERROR  = NONE
 *
 ***************************************************************************/

USHORT NEAR AdjustModeLen(USHORT *ReqModeLen)
{
  USHORT rc = NO_ERROR;
  USHORT ModeLen = sizeof(VDHMODEINFO);


  if (*ReqModeLen < FIELDOFFSET(VDHMODEINFO, ExtDataArea))
    ModeLen = FIELDOFFSET(VDHMODEINFO, PartialBufferSize);

  if (*ReqModeLen < FIELDOFFSET(VDHMODEINFO, PartialBufferSize))
    ModeLen = FIELDOFFSET(VDHMODEINFO, FullBufferSize);

  if (*ReqModeLen < FIELDOFFSET(VDHMODEINFO, FullBufferSize))
    ModeLen = FIELDOFFSET(VDHMODEINFO, BufferLength);

  if (*ReqModeLen < FIELDOFFSET(VDHMODEINFO, BufferLength))
    ModeLen = FIELDOFFSET(VDHMODEINFO, BufferAddress);

  if (*ReqModeLen < FIELDOFFSET(VDHMODEINFO, BufferAddress))
    ModeLen = FIELDOFFSET(VDHMODEINFO, attrib);

  if (*ReqModeLen < FIELDOFFSET(VDHMODEINFO, attrib))
    ModeLen = FIELDOFFSET(VDHMODEINFO, fmt_ID);

  if (*ReqModeLen < FIELDOFFSET(VDHMODEINFO, fmt_ID))
    ModeLen = FIELDOFFSET(VDHMODEINFO, vres);

  if (*ReqModeLen < FIELDOFFSET(VDHMODEINFO, vres))
    ModeLen = FIELDOFFSET(VDHMODEINFO, hres);

  if (*ReqModeLen < FIELDOFFSET(VDHMODEINFO, hres))
    ModeLen = FIELDOFFSET(VDHMODEINFO, row);

  if (*ReqModeLen < FIELDOFFSET(VDHMODEINFO, row))
    ModeLen = FIELDOFFSET(VDHMODEINFO, col);

  if (*ReqModeLen < FIELDOFFSET(VDHMODEINFO, col))
    ModeLen = FIELDOFFSET(VDHMODEINFO, color);

  if (*ReqModeLen < FIELDOFFSET(VDHMODEINFO, color))
    ModeLen = FIELDOFFSET(VDHMODEINFO, fbType);

  if (*ReqModeLen < FIELDOFFSET(VDHMODEINFO, fbType))
    rc = ERROR_VIO_INVALID_LENGTH;

  if (*ReqModeLen == FIELDOFFSET(VDHMODEINFO, cb))
    ModeLen = sizeof(VDHMODEINFO);

  if (*ReqModeLen > sizeof(VDHMODEINFO))
    ModeLen = FIELDOFFSET(VDHMODEINFO, vres);
  *ReqModeLen = ModeLen;
  return (rc);
}

/*****************************************************************************
 *
 * FUNCTION NAME = SetReqModeData()
 *
 * DESCRIPTION   =
 *
 *      Data in structure pointed to by pReqMode will be
 *      updated according to the structure length.
 *
 *      Switching on the length allows the update to begin with
 *      the last item in the structure and then fall through
 *      onto the rest.
 *
 *      Note no 'break' statements between case values.
 *
 * INPUT         = USHORT ReqModeLen,USHORT UpdateHardware,PVIDEOMODE
 *                 pCurMode,PVDHMODEINFO pReqMode,PENVIRONMENT Environment
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL = NONE
 * RETURN-ERROR  = NONE
 *
 ***************************************************************************/

VOID NEAR SetReqModeData(USHORT ReqModeLen,USHORT UpdateHardware,PVIDEOMODE
                          pCurMode,PVDHMODEINFO pReqMode,PENVIRONMENT
                          Environment)
{
  USHORT EnvBufferPassed;


  if (EnvBufferPassed = SEG(Environment))

    switch (ReqModeLen)
    {                            /* Update from Environment where possible */

      default  :
        pReqMode->ExtDataArea = NULL;

      case  FIELDOFFSET(VDHMODEINFO, ExtDataArea) :
        pReqMode->PartialBufferSize = PartialSaveSize;

        if (pReqMode->attrib == ATTRIB_WORLDCOUNT)
          pReqMode->PartialBufferSize *= 2;

      case  FIELDOFFSET(VDHMODEINFO, PartialBufferSize) :
        pReqMode->FullBufferSize = (pReqMode->fbType&MODE_FLAG_GRAPHICS)?
           (pMemoryMaps[pCurMode->MemMap].TotalSize):(2*pCurMode->col
           *pCurMode->row+PVB_FONT_SIZE);

      case  FIELDOFFSET(VDHMODEINFO, FullBufferSize) :
        pReqMode->row = (UpdateHardware)?pCurMode->row:
           Environment->ModeData.row;
        pReqMode->BufferLength = (pReqMode->fbType&MODE_FLAG_GRAPHICS)?
           pMemoryMaps[pCurMode->MemMap].TotalSize:2*pCurMode->col
           *pReqMode->row;

      case  FIELDOFFSET(VDHMODEINFO, BufferLength) :
        pReqMode->BufferAddress = pMemoryMaps[pCurMode->MemMap].
           Start.FullAddress;

      /*
      ** Physical buffer start address
      */

      case  FIELDOFFSET(VDHMODEINFO, BufferAddress) :
        pReqMode->attrib = Environment->ModeData.attrib;

        if ((pReqMode->attrib == ATTRIB_WORLDCOUNT) && (ReqModeLen >=
           FIELDOFFSET(VDHMODEINFO, FullBufferSize)))
          pReqMode->FullBufferSize *= 2;

      case  FIELDOFFSET(VDHMODEINFO, attrib) : /* Attribute */
        pReqMode->fmt_ID = Environment->ModeData.fmt_ID;

      case  FIELDOFFSET(VDHMODEINFO, fmt_ID) : /* Attribute format ID */
        /*             Set it to whatever our Environment has */
        /*            pReqMode->vres = pCurMode->vres;  */
        pReqMode->vres = Environment->ModeData.vres;

      case  FIELDOFFSET(VDHMODEINFO, vres) :   /* Vertical resolution */
        pReqMode->hres = pCurMode->hres;

      case  FIELDOFFSET(VDHMODEINFO, hres) :   /* Horizontal resolution */
        pReqMode->row = (UpdateHardware)?pCurMode->row:
           Environment->ModeData.row;

      case  FIELDOFFSET(VDHMODEINFO, row) :    /* Number of text rows */
        pReqMode->col = pCurMode->col;

      case  FIELDOFFSET(VDHMODEINFO, col) :    /* # of text cols: 40 or 80 */
        pReqMode->color = pCurMode->color;

      case  FIELDOFFSET(VDHMODEINFO, color) :
        pReqMode->fbType = pCurMode->fbType;
        if (Environment->ModeData.fbType & MODE_FLAG_NO_CLR_BRST) /*            */
          pReqMode->fbType |= MODE_FLAG_NO_CLR_BRST;
        break;

    }

  else

    switch (ReqModeLen)
    {                                  /* Update from Current Mode Data */

      default  :
        pReqMode->ExtDataArea = NULL;

      case  FIELDOFFSET(VDHMODEINFO, ExtDataArea) :
        pReqMode->PartialBufferSize = PartialSaveSize;

        if (pReqMode->attrib == ATTRIB_WORLDCOUNT)
          pReqMode->PartialBufferSize *= 2;

      case  FIELDOFFSET(VDHMODEINFO, PartialBufferSize) :
        pReqMode->FullBufferSize = (pReqMode->fbType&MODE_FLAG_GRAPHICS)?
           (pMemoryMaps[pCurMode->MemMap].TotalSize):(2*pCurMode->col
           *pCurMode->row+PVB_FONT_SIZE);

      case  FIELDOFFSET(VDHMODEINFO, FullBufferSize) :
        pReqMode->BufferLength = (pReqMode->fbType&MODE_FLAG_GRAPHICS)?
           pMemoryMaps[pCurMode->MemMap].TotalSize:2*pCurMode->col
           *pCurMode->row;

      case  FIELDOFFSET(VDHMODEINFO, BufferLength) :
        pReqMode->BufferAddress = pMemoryMaps[pCurMode->MemMap].
           Start.FullAddress;

      case  FIELDOFFSET(VDHMODEINFO, BufferAddress) : /* Phys buffer   */
        pReqMode->attrib = pCurMode->attrib;          /* start address */

        if ((pReqMode->attrib == ATTRIB_WORLDCOUNT) && (ReqModeLen >=
           FIELDOFFSET(VDHMODEINFO, FullBufferSize)))
          pReqMode->FullBufferSize *= 2;

      case  FIELDOFFSET(VDHMODEINFO, attrib) : /* Attribute */
        pReqMode->fmt_ID = pCurMode->fmt_ID;

      case  FIELDOFFSET(VDHMODEINFO, fmt_ID) : /* Attribute format ID */
        pReqMode->vres = pCurMode->vres;

      case  FIELDOFFSET(VDHMODEINFO, vres) :   /* Vertical resolution */
        pReqMode->hres = pCurMode->hres;

      case  FIELDOFFSET(VDHMODEINFO, hres) :   /* Horizontal resolution */
        pReqMode->row = pCurMode->row;

      case  FIELDOFFSET(VDHMODEINFO, row) :    /* Number of text rows */
        pReqMode->col = pCurMode->col;

      case  FIELDOFFSET(VDHMODEINFO, col) :    /* # of text cols: 40 or 80 */
        pReqMode->color = pCurMode->color;

      case  FIELDOFFSET(VDHMODEINFO, color) :
        pReqMode->fbType = pCurMode->fbType;
        break;

    }
}

/*****************************************************************************
 *
 * FUNCTION NAME = GetSVGAMode()
 *
 * DESCRIPTION   =
 *
 *      If requesting application is running in foreground,
 *         - Return hardware value if hardware can be read
 *         - Otherwise return environment buffer value
 *          ( report error if write-only hardware and no env buffer passed )
 *      If requesting application is running in background,
 *         - Return environment buffer value
 *          ( report error if environment buffer was not passed )
 *
 *      Allocate temporary storage to temporary environment buffer
 *      Read all registers directly from the hardware
 *      Update environment buffer with current mode setting
 *      Deallocate temporary storage to temporary environment buffer
 *
 * INPUT         = PENVIRONMENT Environment,PVDH_MODE ParmBlock
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL = NONE
 * RETURN-ERROR  = NONE
 *
 ***************************************************************************/

USHORT NEAR GetSVGAMode(PENVIRONMENT Environment,PVDH_MODE ParmBlock)
{
  SEL Selector;
  PVIDEOMODE pCurMode;
  PVDHMODEINFO pReqMode;
  PENVIRONMENT TempEnv;
  USHORT rc,ModeIndex,UpdateHardware;
  USHORT EnvBufferPassed,ReqModeLen;

  rc = ERROR_VIO_INVALID_PARMS;        /* Initialize to error               */
  EnvBufferPassed = SEG(Environment);  /* Non-zero = TRUE                   */

  if ((ParmBlock->Length >= sizeof(VDH_MODE)) && /* Valid packet length     */
     (ParmBlock->Flags <= 1) &&        /* Valid flags                       */
     (EnvBufferPassed || (ParmBlock->Flags&ENVFLAG_HARDWARE)))
  {

    rc = NO_ERROR;                     /* Initialize no error               */
    UpdateHardware = ParmBlock->Flags&ENVFLAG_HARDWARE;
    ReqModeLen = (pReqMode = ParmBlock->pVDHModeInfo)->cb;

    if (!(rc = AdjustModeLen(&ReqModeLen)))
    {

      if (UpdateHardware)
      {

        if (!(rc = DosAllocSeg(sizeof(ENVIRONMENT), (PSEL)&Selector, 0)))
        {
          TempEnv = (PENVIRONMENT)MAKEFARPTR(Selector, 0);
          TempEnv->VideoEnable = 1;
          SaveRestoreHW(TempEnv, GET);
          ModeIndex = GetModeIndex(TempEnv);

          if (ModeIndex == -1)
            rc = ERROR_VIO_MODE;       /* Somebody corrupted the registers  */

          if (!rc && EnvBufferPassed)
            SetEnvMode((UCHAR)ModeIndex, Environment, TempEnv, 0);
          DosFreeSeg(Selector);
        }
      }

      else
      {

        if (EnvBufferPassed)
          ModeIndex = Environment->ModeIndex;
        else
          rc = ERROR_VIO_INVALID_PARMS;/* Not FG OR write-only, no env
                                          buffer                            */
      }

      if (!rc)
      {
        pCurMode = &pModes[ModeIndex];
        pReqMode->fbType = pCurMode->fbType;

        if (EnvBufferPassed && !UpdateHardware)
        {

          if ((Environment->ModeData.fbType&MODE_FLAG_NOT_MONO) &&
             !(Environment->ModeData.fbType&MODE_FLAG_GRAPHICS))
          {
            register irow = 0;


            while ((irow < 256) && (Environment->LookupTable[irow].Red ==
               Environment->LookupTable[irow].Green) &&
               (Environment->LookupTable[irow].Red == Environment->LookupTable
               [irow].Blue))
              irow++;

            /*
            ** If loop completed, assume black & white
            */
            if (irow == 256)
              Environment->ModeData.fbType |= MODE_FLAG_NO_CLR_BRST;
          }
          pReqMode->fbType = Environment->ModeData.fbType;
        }
        SetReqModeData(ReqModeLen, UpdateHardware, pCurMode, pReqMode,
           Environment);
      }                                /* !rc */
    }                                  /* valid mode len */
  }                                    /* if good parameters */

  if (!rc)
    pReqMode->cb = ReqModeLen;
  return (rc);
}
