/*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 = BVHMODE.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_DOSDEVICES               /* Device specific, ring 2 support   */
#define  INCL_OS2STD                   /* Needed for NULL definition in     */
                                       /* OS2STD.H                          */
#include <memory.h>
#include "bvhtype.h"
#pragma  intrinsic(memcpy,memcmp,memset)
/***************************************************************************
 *
 * FUNCTION NAME = FixBVHRegisters()
 *
 * 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 FixBVHRegisters(PENVIRONMENT Environment)
{
  USHORT ColorMode,VideoEnabled,VideoOn;
  ColorMode = HardwareColor();
  AccessVideoEnable(ColorMode, GET, &VideoEnabled);
  VideoOn = 0;                        /* disable video during these fixups */
  AccessVideoEnable(ColorMode, SET, &VideoOn);

  pfnPMIRequest(&Adapter, PMIREQUEST_UNLOCKREGISTERS, NULL, NULL);
  pfnPMIRequest(&Adapter, PMIREQUEST_CLEANUP, NULL, NULL);
  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 video mode.
 *
 *  ENTRY POINT: SetEnvMode
 *    LINKAGE:   CALL FAR
 *
 *  INPUT: (Passed on stack)
 *             FAR * Environment ( far pointer to environment buffer )
 *             USHORT   RequiredModeLength
 *             PVDHMODEINFO RequiredMode
 *             USHORT   ModeIndex
 *
 *  EXIT-NORMAL: Environment buffer is updated to reflect mode set in the
 *               hardware. All the fields of the environment mode data
 *               are set.
 *               Update the environment structure w/ the data in requested
 *               mode as much as possible. Otherwise, use the data in
 *               the mode found.
 *
 *  EFFECTS: NONE
 *
 *  INTERNAL REFERENCES:
 *    ROUTINES: NONE
 *
 *  EXTERNAL REFERENCES:
 *    ROUTINES: NONE
 *
 ****************************************************************************/

VOID PASCAL NEAR SetEnvMode(PENVIRONMENT Environment,
                            USHORT        ReqModeLen,
                            PVDHMODEINFO    pReqMode,
                            USHORT         ModeIndex)
{
  PVIDEOMODE pCurMode = &pModes[ModeIndex];

  Environment->ModeIndex = ModeIndex;
  Environment->ModeData.cb = ReqModeLen;
  Environment->ModeData.fbType = pReqMode->fbType;
  AdjustModeLen(&ReqModeLen);
  /*
   * !!!!! ReqModeLen is the adjusted length?
   */
  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;
  Environment->ModeData.FullBufferSize =
       (Environment->ModeData.fbType & MODE_FLAG_GRAPHICS) ?
               pCurMode->ulTotalSize : Environment->ModeData.col*
                                       Environment->ModeData.row*2;

  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 == WorldFormat)
      Environment->ModeData.attrib = WorldAttrCount;
    else
      Environment->ModeData.attrib = pReqMode->attrib;
  }
  else
  {
    if (Environment->ModeData.fmt_ID == WorldFormat)
      Environment->ModeData.attrib = WorldAttrCount;
    else
      Environment->ModeData.attrib = DefaultAttrCount;
  }

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

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

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

  Environment->ScrollRect.Right = Environment->ModeData.col-1;
  Environment->ScrollRect.Bottom = Environment->ModeData.row-1;

  if (Environment->ModeData.fmt_ID == WorldFormat)
  {
    Environment->AttrBufSize = WorldAttrCount;
    Environment->ModeData.FullBufferSize *= 2;
  }
  else
    Environment->AttrBufSize = DefaultAttrCount;

  Environment->CharAttrib.XCharSize = pCurMode->XCharSize;
  Environment->CharAttrib.YCharSize = pCurMode->YCharSize;
  if (!(Environment->ModeData.fbType & MODE_FLAG_GRAPHICS) && Environment->pFontData)
  {
    Environment->CharAttrib.XCharSize = (BYTE) Environment->pFontData->ulFontWidth;
    Environment->CharAttrib.YCharSize = (BYTE) Environment->pFontData->ulFontHeight;
  }
}

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

USHORT PASCAL NEAR SetHWMode (PENVIRONMENT Environment,
                              USHORT       Flags,
                              USHORT       ModeIndex)
{
  USHORT rc;
  USHORT UpdateHWare;
  USHORT ColorMode;
  USHORT VideoEnable;
  USHORT VideoOn;
  REGDATA RegData;
  REGADDRESS RegAddress;
  PCLUT CurrentCLUT;
  USHORT TempCursor;
//  USHORT miMode = (USHORT) pModes[ModeIndex].miModeId;  //          

  rc = NO_ERROR;
  UpdateHWare = Flags & UPDATE_HARDWARE;
  ColorMode = !(Environment->ModeData.fbType & MODE_FLAG_NO_CLR_BRST);

  if (!(Environment->ModeData.fbType & MODE_FLAG_GRAPHICS) ||  /* text mode */
       (pModes[ModeIndex].color == 4))
     CurrentCLUT = ColorMode ? &ColourCLUT16 : &MonoCLUT16;
  else
     CurrentCLUT = ColorMode ? &ColourCLUT256 : &MonoCLUT256;

  if (UpdateHWare)
  {

    VideoOn = Environment->VideoEnable;
    VideoEnable = 0;                     /* Turn video off */
    AccessVideoEnable(ColorMode, SET, &VideoEnable);
    pfnPMIRequest(&Adapter, PMIREQUEST_UNLOCKREGISTERS, NULL, NULL);

    /*           
    ** Clock is still set in bvhsvga.dll for Diamond S3 928 and S3 80X.
    */
    if ((OEMHardware.Manufacturer == DIAMOND_MANUFACTURER) &&
        (BVHHardware.AdapterType == S3_ADAPTER) &&
        (BVHHardware.ChipType <= S3_86C928_CHIP))       //928 & 80X
       SetDiamondClk(Environment->ModeData.hres,
                     Environment->ModeData.fbType,
                     Environment->ModeData.color);

    /*
    **            Set adapter.modeinfo PMI keyvariables by copying the
    ** mode values into it. Set display related values from the CurrentMonitor
    ** if the configuration was set and searched for the right mode entry.
    */
    Adapter.ModeInfo.bBitsPerPixel = pModes[ModeIndex].color;
    Adapter.ModeInfo.usBytesPerScanLine = pModes[ModeIndex].col;
    Adapter.ModeInfo.usTextRows = pModes[ModeIndex].row;
    Adapter.ModeInfo.usXResolution = pModes[ModeIndex].hres;
    Adapter.ModeInfo.usYResolution = pModes[ModeIndex].vres;
    Adapter.ModeInfo.usType = pModes[ModeIndex].fbType;
    Adapter.ModeInfo.ulPageLength= pModes[ModeIndex].ulPageLength;
    Adapter.ModeInfo.ulSaveSize  = pModes[ModeIndex].ulTotalSize;
    Adapter.ModeInfo.bXCharSize  = pModes[ModeIndex].XCharSize;
    Adapter.ModeInfo.bYCharSize  = pModes[ModeIndex].YCharSize;
    Adapter.ModeInfo.ulBufferAddress = (ULONG) pModes[ModeIndex].BufferAddress;
    Adapter.ModeInfo.ulApertureSize  = pModes[ModeIndex].BufferLength;


    /*           
     * If there is a corresponding entry in video.cfg,
     *   1) if the PMI mode has a refresh rate, use its refresh rate info,
     *   2) else use the refresh rate in video.cfg.
     * If there is no entry in video.cfg, always set the refresh rates to
     * unknown (0xff).
     */
    if (Environment->CurrentCFGIndex & CFG_INDEX_SET)
    {
       if (pModes[ModeIndex].bVrtRefresh != 0xFF)
       {
          Adapter.ModeInfo.bVrtRefresh = pModes[ModeIndex].bVrtRefresh;
          Adapter.ModeInfo.bHrtRefresh = pModes[ModeIndex].bHrtRefresh;
          Adapter.ModeInfo.bVrtPolPos = pModes[ModeIndex].bVrtPolPos;
          Adapter.ModeInfo.bHrtPolPos = pModes[ModeIndex].bHrtPolPos;
       }
       else
       {
          USHORT iIndex = Environment->CurrentCFGIndex & CFG_INDEX_MASK;
          Adapter.ModeInfo.bVrtRefresh= CurrentMonitor.ModeInfo[iIndex].bVertRefresh;
          Adapter.ModeInfo.bHrtRefresh= CurrentMonitor.ModeInfo[iIndex].bHorizRefresh;
          Adapter.ModeInfo.bVrtPolPos = CurrentMonitor.ModeInfo[iIndex].bVPolarityPos;
          Adapter.ModeInfo.bHrtPolPos = CurrentMonitor.ModeInfo[iIndex].bHPolarityPos;
       }
    }
    else
    {
       Adapter.ModeInfo.bVrtRefresh = Adapter.ModeInfo.bHrtRefresh =
       Adapter.ModeInfo.bVrtPolPos  = Adapter.ModeInfo.bHrtPolPos  = 0xff;
    }

    Adapter.ModeInfo.usScrnTop = pModes[ModeIndex].usScrnTop;
    Adapter.ModeInfo.usScrnBottom = pModes[ModeIndex].usScrnBottom;
    Adapter.ModeInfo.usScrnLeft = pModes[ModeIndex].usScrnLeft;
    Adapter.ModeInfo.usScrnRight = pModes[ModeIndex].usScrnRight;


    /*
     * the setmode command list is copied to hHardwareStateData
     * It will be used in restore.
     */
    if (rc = pfnPMIRequest(&Adapter,
                           PMIREQUEST_SETMODE,
                           &pModes[ModeIndex].miModeId,
                           &Environment->bHardwareStateData[0]))
      return rc;

#ifdef TSU01
/*
 * This is not needed. Put it here in case we may need in the future.
 */

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

#endif    /* TSU01 */

//             Move the clock setting to ibmgpmi.dll.
//
//    /*  note for #9, you have to set adapter to enhanced mode               */   /*            */
//    /*  before setting the external clock                                   */
//    //           ALl clk code moved to PMI
//    if (OEMHardware.Manufacturer == NUMBER9_MANUFACTURER)      /*            */
//       if ((Environment->ModeData.fbType & MODE_FLAG_GRAPHICS) || /* graphics */
//           (Environment->ModeData.hres == 1056) ||      /* 132 cols text      */
//           (Environment->ModeData.hres == 1188))
//          SetNumber9Clk(Environment->ModeData.hres);

    /*
     * Text Mode
     */
    if (!(Environment->ModeData.fbType & MODE_FLAG_GRAPHICS))
    {
      /*
      ** Set Cursor size CRT registers.
      */
      RegAddress.DataPort = CRTMONODATAPORT;
      RegAddress.ColorAdjust = COLORADJUSTMENT;
      RegAddress.Flags = NONE;
      RegAddress.AddressPort = CRTMONOADDRPORT;
      TempCursor = (USHORT) ((Environment->pFontData)->ulFontWidth-2) |
                   (USHORT) ((Environment->pFontData)->ulFontHeight-1)<<8;
      RegData.FirstEntry = 0xA;
      RegData.DataArea = (PBYTE)&TempCursor;
      RegData.NumEntries = 2;
      AccessHardware(&RegAddress, BYTES, ColorMode, SET, &RegData);
    }

    if(!(Environment->ModeData.fbType & MODE_FLAG_GRAPHICS) ||
        pModes[ModeIndex].color <= 8)
    {
      BVHAccessCLUT(SET,CurrentCLUT);
    }
  }
  else
    /*
    ** mode set in the background. Obtain the hardware command list.
    */
    pfnPMIRequest(&Adapter,
                  PMIREQUEST_QUERYMODEHRDWRLIST,
                  &pModes[ModeIndex].miModeId,
                  &Environment->bHardwareStateData[0]);

  memset(&Environment->LookupTable.aRGB[0], 0, 256 * sizeof(SVGARGB));
                                     /* put copy of CLUT into environment */
  memcpy(&Environment->LookupTable.aRGB[0],
         &CurrentCLUT->aRGB[0],
         (USHORT) CurrentCLUT->ulRGBCount*sizeof(SVGARGB));
  Environment->LookupTable.ulRGBStart = 0;
  Environment->LookupTable.ulRGBCount = CurrentCLUT->ulRGBCount;

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

/*****************************************************************************
 *
 * 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.
 *
 *  WARNING: If length specified is for the type only, return highest VGA mode!
 *  This is due to popups (code in cmd.exe _Init) which do VioGetMode and
 *  then modify the length to 3 and issue the VioSetMode and expect to get
 *  80x25 (historically was the highest resolution mode).
 *
 *  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
 *
 * typedef struct _VIDEOMODE
 *        {                              *  VioGetMode structure starts     *
 *            USHORT      cb;
 *            BYTE        fbType;
 *            BYTE        color;
 *            USHORT      col;
 *            USHORT      row;
 *            USHORT      hres;
 *            USHORT      vres;
 *            BYTE        fmt_ID;        * DBCS support *
 *            BYTE        attrib;
 *            UCHAR       FAR * BufferAddress;
 *            ULONG       BufferLength;
 *            ULONG       ulTotalSize;   * total bytes of VRAM to save      *
 *                                       * VioGetMode structure ends        *
 *            ULONG       ulPageLength;  * no of bytes to save a plane      *
 *            MODEID      miModeId;      * used to make setmode request     *
 *            BYTE        XCharSize;     * char cell width                  *
 *            BYTE        YCharSize;     * char cell height                 *
 *        } VIDEOMODE, FAR * PVIDEOMODE;
 *
 *                 TRUE: if a mode is found, FALSE otherwise.
 * RETURN-NORMAL = NONE
 * RETURN-ERROR  = NONE
 *
 ***************************************************************************/

#define SPECIALCASE (IGNORE_CLR_BRST | NOT_PLASMA | MODE_FLAG_VGA_ENTRY)
USHORT NEAR SearchModeTable (int          *Mode,
                             PVDHMODEINFO  pReqMode,
                             USHORT     *CfgIndex)
{
  int i,j;
  PVIDEOMODE pCurMode;
  USHORT ReqModeLen;
  USHORT fModeFound = FALSE;
  USHORT fTextMode;
  USHORT fPartialModeFound = NO_MATCH, fVGAModeFound = NO_MATCH;
  BOOL   fVGAEntry;
//  BOOL   fPanelLimited = FALSE; /*            */           
  USHORT  usHighestRes = 0;         //          
  BYTE   bTempRefresh; //          

  ReqModeLen = pReqMode->cb;

  /*
  ** Reset configuration index from previous mode sets.
  */
  *CfgIndex &= ~CFG_INDEX_SET;

  fTextMode = !(pReqMode->fbType & MODE_FLAG_GRAPHICS);
  /*
  ** If call made to obtain the highest columns text mode, make sure
  ** the request gets 80x25.      due to VioPopup's ill-defined behaviour.
  */
  if (fTextMode &&
     (ReqModeLen >= FIELDOFFSET(VDHMODEINFO, color)) &&
     (ReqModeLen < FIELDOFFSET(VDHMODEINFO, col)))
  {
    pReqMode->col = 80;
    pReqMode->color = 4;
    ReqModeLen = FIELDOFFSET(VDHMODEINFO, row);
  }

  if (fTextMode && (!(pReqMode->row) || !(pReqMode->col)))
    return (FALSE);

  /*
  ** If current monitor configuration is valid, search thru the table and
  ** try to find the refresh. Use XResolution and YResolution only. If the
  ** mode is found in the table. Use VertRefresh as the additional searching
  ** parameter. If found, set the CfgIndex to the index in the table.
  */
  if (!fTextMode && (fCurrentCfg))
  {
    for(j=0;(j<MAX_MONITOR_MODES) &&
            (CurrentMonitor.ModeInfo[j].usXResolution) &&
            (!((*CfgIndex) & CFG_INDEX_SET));j++)
        if ((CurrentMonitor.ModeInfo[j].usXResolution == pReqMode->hres) &&
//            (CurrentMonitor.ModeInfo[j].usYResolution == pReqMode->vres))
            (CurrentMonitor.ModeInfo[j].usYResolution == pReqMode->vres) &&
            (CurrentMonitor.ModeInfo[j].bVertRefresh != 0xFF))
        {
            *CfgIndex = CFG_INDEX_SET | (j & CFG_INDEX_MASK);
        }
  }

  /*            */
  if (*CfgIndex & CFG_INDEX_SET)
    bTempRefresh = 0;      /* search for highest possible refresh rate */
  else
    bTempRefresh = 0xFF;   /* search for the lowest refresh rate */

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

     /*           
      * Check if it is IBM ThinkPad.
      */
     if((BVHHardware.AdapterType == WESTERNDIG_ADAPTER)
         && (BVHHardware.ChipType == WESTERNDIG_WD9024_CHIP))
     {
        USHORT usTemp;                             //          
                                                   //          
        if (usTemp = ThinkPadHighestRes())         //          
           usHighestRes = usTemp;                  //          
//        HWOutpByte (0x070, 0x7C);                //          
//        if (HWInpByte (0x071) == 'I')
//        {
//           HWOutpByte (0x070, 0x7D);
//           if ((HWInpByte (0x071) == 'S') ||     /* TBird Enhanced or later */
//               (HWInpByte (0x071) == 0x06))      /* TBird or Flamingo       */
//           {
//              /*
//               * Check the highest resolution
//               */
//              HWOutpByte (0x0d00, 0x12);
//              usHighestRes = ((USHORT)HWInpByte (0x0d01) +1) * 8;
//           }
//        }
     }
  }


  /*
  ** loop down so we find highest resolution first
  **
  **           
  ** VGA entries will be loaded into modelist in VIDEOPMI
  ** first. Since the mode search is from the bottom to the top,
  ** the modes in PMI file will be searched first. Then, the modes in
  ** VGA entries.
  ** The modes in PMI file are already preferrred to the VGA modes.
  ** VGA modeswill only be used when an exactly matched or partially
  ** matched mode cannot be found in PMI file.
  */
  for (i = (INT) ulTotalVideoModes-1; (i >= 0) && !fModeFound; i--)
  {
    pCurMode = &pModes[i];

    /*
     *           
     */
//    if( fPanelLimited && (pCurMode->hres > 720) )
    //          
    if(usHighestRes && (pCurMode->hres > usHighestRes) && !fTextMode)
      continue;

    if (pCurMode->fbType & MODE_FLAG_VGA_ENTRY)
       fVGAEntry = TRUE;
    else
       fVGAEntry = FALSE;

    /*
    ** Partial mode match applies to both graphics and text modes.           
    */
    if (fTextMode && (pCurMode->fbType & MODE_FLAG_GRAPHICS))
        continue;

    /*           
     * Mask out the flags for VGA entry, B/W mode, and planar display
     * to compare graphics mode and color
     */
    else if ((BYTE)(pCurMode->fbType & ~SPECIALCASE)
              !=  (BYTE)(pReqMode->fbType&~MODE_FLAG_NO_CLR_BRST))
        continue;


    /*         
     * If the requested mode is graphics mode and it includes
     * the information about vertical and horizontal resolutions,
     * check only resolution and color.
     * Text row and column will not be checked.
     * Some applications (very few) will set the graphics mode by
     * specifying the text row and column without the full information
     * about vertical and horizontal resolution. They will go through
     * the code after this "if" statement just like text mode does.
     */
    if (!fTextMode                                       &&
        (ReqModeLen >= FIELDOFFSET(VDHMODEINFO, fmt_ID)) &&
        (pReqMode->vres == pCurMode->vres)               &&
        (pReqMode->hres == pCurMode->hres)               &&
        (pReqMode->color == pCurMode->color))
    {
       /*
       ** If display index is set, check if current mode matches the
       ** vertical refresh. If so, set mode found flag to true. If not,
       ** mark this mode as partial mode only the first time. If the index
       ** is not set, refresh info is not checked.
       */
       /*           
        * Display index is not set.
        * Search for the mode with the lowest refresh rate or without
        * refresh rate, too.
        */
       if (!(*CfgIndex & CFG_INDEX_SET))
       {
          /* the mode has refresh entry */
          if ((pCurMode->bVrtRefresh != 0xFF) &&
              (pCurMode->bVrtRefresh < bTempRefresh))
          {
             fPartialModeFound = i;   //set this entry as the partial mode
             bTempRefresh = pCurMode->bVrtRefresh;
          }
          else  /* the mode doesn't have refresh rate entry */
          {
             if (bTempRefresh == 0xFF) /* no mode is found */
             {
                if (!fVGAEntry)
                   fPartialModeFound = i;   //set this entry as the partial mode
                else if (fVGAModeFound == NO_MATCH)
                   fVGAModeFound = i;
             }
          }
       }
       else
       /*           
        * Display index is set.
        * 1) If the current mode has the exactly the same vertical refresh rate,
        *    mode is found.
        * 2) If the current mode doesn't have a refresh rate (0xFF), partial
        *    match is found.
        * 3) If the current mode has a refresh rate and it is lower than
        *    the monitor can handled. A temp mode is found.
        *    If the refresh rate of the current mode is greater than
        *    the refresh rate of the existing temp mode, the existing temp mode
        *    is replaced with the current mode.
        */
       {
          if (pCurMode->bVrtRefresh != 0xFF)  /* the mode has refresh entry */
          {
            /* exact match */
            if (CurrentMonitor.ModeInfo[(*CfgIndex) & CFG_INDEX_MASK].bVertRefresh
                == pCurMode->bVrtRefresh)
            {
               if (!fVGAEntry)
               {
                  fModeFound = TRUE;
                  *Mode = i;
               }
               else if (fVGAModeFound == NO_MATCH)
                  fVGAModeFound = i;
            }
            /* partial match */
            else
            if ((CurrentMonitor.ModeInfo[(*CfgIndex) & CFG_INDEX_MASK].bVertRefresh
                 > pCurMode->bVrtRefresh) &&
                (pCurMode->bVrtRefresh > bTempRefresh))
            {
               fPartialModeFound = i;   //set this entry as the partial mode
               bTempRefresh = pCurMode->bVrtRefresh;
            }
          }
          else /* current mode doesn't have a refresh rate */
          {
             if (bTempRefresh == 0)  /* no mode is found */
             {
                if (!fVGAEntry)
                   fPartialModeFound = i;   //set this entry as the partial mode
                else if (fVGAModeFound == NO_MATCH)
                   fVGAModeFound = i;
             }
          }
       }
       continue;
    }

    /*
     * For DBCS support, vertical resolution must match.
     */
    if ((ReqModeLen >= FIELDOFFSET(VDHMODEINFO, fmt_ID)) &&
       (pReqMode->vres != pCurMode->vres))
      continue;

    else

      /*
       * at least hres is specified.
       */
      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
        ** with the closer rows 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 < color, type already matched, so mode found.
        */
        if ((ReqModeLen >= FIELDOFFSET(VDHMODEINFO, row)) &&
           (pReqMode->col == pCurMode->col))
        {
          if ((ReqModeLen >= FIELDOFFSET(VDHMODEINFO, hres)) &&
             (pReqMode->row == pCurMode->row))
          {
             if(pReqMode->color == pCurMode->color)
             {
                if (!fVGAEntry)
                {
                   fModeFound = TRUE;
                   *Mode = i;
                }
                else if (fVGAModeFound == NO_MATCH)
                   fVGAModeFound = i;
             }
             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 requested 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)))
                {
                   if (!fVGAEntry)
                      fPartialModeFound = i;
                   else if (fVGAModeFound == NO_MATCH)
                      fVGAModeFound = i;
                }
                continue;
             }
             else
                continue;
          }
        }    /* matches col */

        /*
         * at most color is specified.
         */
        else if (ReqModeLen < FIELDOFFSET(VDHMODEINFO, row))
        {
          if((ReqModeLen >= FIELDOFFSET(VDHMODEINFO, col)) &&
             (pReqMode->color == pCurMode->color))
          {
             if (!fVGAEntry)
             {
                fModeFound = TRUE;
                *Mode = i;
             }
             else if (fVGAModeFound == NO_MATCH)
                fVGAModeFound = i;
          }
          else if((ReqModeLen >= FIELDOFFSET(VDHMODEINFO, color)) &&
                  (ReqModeLen < FIELDOFFSET(VDHMODEINFO, col)))
          {
             if (!fVGAEntry)
             {
                fModeFound = TRUE;
                *Mode = i;
             }
             else if (fVGAModeFound == NO_MATCH)
                fVGAModeFound = i;
          }
          else
             continue;
        }
        else
          continue;

    /*           
     * If current display is planar (plasma) display and current mode
     * cannot be set on planar display (NOT_PLASMA), or
     * the reuqested mode is B/W (NO_CLR_BRST) mode and current mode cannot
     * be set in B/W mode (IGNORE_CLR_BURST),
     * continue mode search.
     */
    if (((CurrentDisplay == PlasmaDisplay) &&
         (pCurMode->fbType & NOT_PLASMA)) ||
        ((pReqMode->fbType & MODE_FLAG_NO_CLR_BRST) &&
         (pCurMode->fbType & IGNORE_CLR_BRST)))
       {
          fModeFound = FALSE;
          fPartialModeFound = NO_MATCH;
          fVGAModeFound = NO_MATCH;
       }

//              }
//              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)
  {
     if (fPartialModeFound != NO_MATCH)
     {
        *Mode = fPartialModeFound;
        fModeFound = TRUE;
     }
     else if (fVGAModeFound != NO_MATCH)
     {
        *Mode = fVGAModeFound;
        fModeFound = TRUE;
     }
  }
  return (fModeFound);
}

/*****************************************************************************
 *
 * FUNCTION NAME = SetBVHMode()
 *
 * 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.
 *
 *      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 SetBVHMode(PENVIRONMENT Environment,
                        PVDHMODEINFO pReqMode,
                        USHORT Flags)
{
  USHORT rc = ERROR_VIO_INVALID_LENGTH;
  USHORT ModeIndex;
  USHORT ReqModeLen;
  USHORT fNativeTextMode;

  if ((ReqModeLen = pReqMode->cb) >= FIELDOFFSET(VDHMODEINFO, color))
  {                                                  /* 3rd byte */
    rc = ERROR_VIO_MODE;                       /* Initialize to error */

    fNativeTextMode = !(pReqMode->fbType & MODE_FLAG_GRAPHICS) &&
                       ((pReqMode->col == 40) ||
                        (pReqMode->col == 80) ||
                        (pReqMode->col == 132));

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

    /*
    ** Set the index into the configuration as well as mode index           
    */
    Environment->CurrentCFGIndex = 0;
    if(SearchModeTable (&ModeIndex, pReqMode,&(Environment->CurrentCFGIndex)) &&
       (pModes[ModeIndex].ulTotalSize <= ulTotalMemory ))
    {
      if (fNativeTextMode && !SearchFont (Environment,
                                          &pModes[ModeIndex],
                                          pReqMode))
           return(ERROR_VIO_FONT);

      SetEnvMode(Environment,ReqModeLen,pReqMode,ModeIndex);

      if (rc = SetHWMode(Environment,Flags,ModeIndex))
        return rc;

      if (fNativeTextMode &&
          (Flags & UPDATE_HARDWARE) &&
          (rc = SetHWFont(Environment)))
         return rc;
    }
  }
  return (rc);
}

/*****************************************************************************
 *
 * FUNCTION NAME = AdjustModeLen()
 *
 * DESCRIPTION   =
 *
 *      Adjust the Requested Mode length to an exact offset into
 *      the VDHMODEINFO structure. This makes compares 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 < sizeof (VDHMODEINFO))
    ModeLen = FIELDOFFSET(VDHMODEINFO, ExtDataArea);

  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 = GetBVHMode()
 *
 * DESCRIPTION   =
 *
 *      Return environment buffer value
 *
 * INPUT         = PENVIRONMENT Environment,PVDH_MODE ParmBlock
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL = NONE
 * RETURN-ERROR  = NONE
 *
 ***************************************************************************/

USHORT NEAR GetBVHMode(PENVIRONMENT Environment,PVDHMODEINFO pReqMode, USHORT Flags)
{
  PVDHMODEINFO pCurMode;
  USHORT rc,UpdateHardware,ReqModeLen;
  rc = NO_ERROR;                     /* Initialize no error               */
  UpdateHardware = Flags&UPDATE_HARDWARE;
  ReqModeLen = pReqMode->cb;

  if (!(rc = AdjustModeLen(&ReqModeLen)))
  {
    pCurMode = &Environment->ModeData;
    pReqMode->fbType = pCurMode->fbType;

    if (!UpdateHardware)
    {

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

        while ((irow < 256) && (Environment->LookupTable.aRGB[irow].bR ==
           Environment->LookupTable.aRGB[irow].bG) &&
           (Environment->LookupTable.aRGB[irow].bR ==
            Environment->LookupTable.aRGB[irow].bB))
          irow++;
        /*
        ** If loop completed, assume black & white
        */
        if (irow == 256)
          Environment->ModeData.fbType |= MODE_FLAG_NO_CLR_BRST;
      }
    }
    switch (ReqModeLen)
    {                            /* Update from Environment where possible */

      default  :
        pReqMode->ExtDataArea = NULL;

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

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

      case  FIELDOFFSET(VDHMODEINFO, PartialBufferSize) :
        pReqMode->FullBufferSize = pCurMode->FullBufferSize;

        /*           
         * For text mode,
         * add font size to FullBufferSize to be consistent w/ bvhvga.dll.
         */
        if (!(Environment->ModeData.fbType & MODE_FLAG_GRAPHICS))
           pReqMode->FullBufferSize +=
                  (ULONG) (256 * (ULONG)(pCurMode->vres / pCurMode->row));

      case  FIELDOFFSET(VDHMODEINFO, FullBufferSize) :
        pReqMode->BufferLength = pCurMode->BufferLength;

      case  FIELDOFFSET(VDHMODEINFO, BufferLength) :
        pReqMode->BufferAddress = pCurMode->BufferAddress;

      /*
      ** Physical buffer start address
      */

      case  FIELDOFFSET(VDHMODEINFO, BufferAddress) :
        pReqMode->attrib = pCurMode->attrib;

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

      case  FIELDOFFSET(VDHMODEINFO, fmt_ID) : /* Attribute format ID */
        /*  Set it to whatever our Environment has */
        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;
    }
  }                                  /* valid mode len */
  return (rc);
}
