/*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 = bvhsubs.c
 *
 * DESCRIPTIVE NAME = Super VGA memory and list-processing routines.
 *
 *
 * VERSION = V2.0
 *
 * DATE
 *
 * DESCRIPTION Base video device handler - memory management routines.
 *
 * FUNCTIONS
 *
 * DEPENDENCIES:
 *
 * NOTES
 *
 * STRUCTURES
 *
 * EXTERNAL REFERENCES
 *
 * EXTERNAL FUNCTIONS
 *
*/

/*
**      Include files
*/

#define  INCL_DOSMODULEMGR
#define  INCL_DOSDEVICES               /* Device specific, ring 2 support   */
#include <memory.h>
#include <string.h>                     //          
#include <ctype.h>                     //          
#include "bvhtype.h"                  /* Type definitions                  */

#pragma  intrinsic(memcpy)
#pragma  intrinsic(strcmp)              //          

extern USHORT APIENTRY FAR DosSMFreeMem(VOID FAR *);
extern USHORT APIENTRY FAR DosSMGetMem(VOID FAR *, USHORT);
/**************************************************************************
 *
 * FUNCTION NAME = BVH_INIT()
 *
 * DESCRIPTION   = Initialize BVH for each new process.
 *
 *           videopmi.dll will not be loaded until a leter stage when
 * the 32 bit DOS calls in videopmi.dll will not experience any problems.
 * The time is determined by the loading of a specific DLL, like pmwin.dll
 * or pmwp.dll.
 *
 * callvectortable will bot be updated until videopmi.dll is loaded.
 * Before then, the table still contains the entry points from bvhvga.dll.
 *
 * INPUT         = NONE
 *
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL =
 *
 * RETURN-ERROR  = NONE
 *
 **************************************************************************/

USHORT APIENTRY FAR BVH_INIT(VOID)
{
   USHORT rc = TRUE, rc1, rc2;
//   static fPMINotTried = TRUE;
   HMODULE hModule;
   char szModule[] = "pmwp";
   if (!f32bModLoaded && !DosGetModHandle (szModule, &hModule))
      f32bModLoaded = TRUE;

   if (f32bModLoaded)
   {
//      if (fPMINotTried)                        //          
//      if (fPMINotTried || GetAccessToFonts ()) //                      
      if (fPMINotTried ||                        //          
          GetAccessToFonts () ||                 //          
          DosGetSeg (selBVHTable))               //          
      {
         /*           
          * if Both code page and rom fonts cannot be found, fail to load
          */
         rc1 = GetCodePage();
         rc2 = GetROMFonts();
         if (rc1 && rc2)
            return FALSE;

         fPMINotTried = FALSE;
         if (!(rc = LoadBVHHandler (&pfnPMIRequest)))
         {
            if (rc = !InitializeBVH (&ulTotalVideoModes,
                                     &ulMaxListSize,
                                     &selBVHTable))
               fNoVideopmi = FALSE;                 /* successful */
         }
         else
         /*
          * cannot access videopmi, either no PMI file or videopmi load failed.
          */
         {
            ulTotalVideoModes = 0;
            ulMaxListSize = 0;
         }

#ifdef  CHAIN_VGA

         /*
          * size of the environment variable not including
          * VGA environment.
          */
         ulEnvLength = sizeof(ENVIRONMENT) + ulMaxListSize;
#endif
         GetAdapterConfig();
      }
      else
      {
//         GetAccessToFonts();           

         /*           
          * If videopmi is not used, don't get access to videopmi by executing
          * SetAccessToPMI.
          */
         if (fNoVideopmi)
            return TRUE;
         else
//            return(!SetAccessToPMI());               
            return (!LoadBVHHandler (&pfnPMIRequest));   //          
      }
   }

   return rc;
}
/***************************************************************************
 *
 * FUNCTION NAME = my_strcmpi()
 *
 * DESCRIPTION   = Private case insensitive string compare.
 *                 The purpose of this silly function is to linking
 *                 to the MSC strcmpi which can't be made intrinsic.
 *                 The case of input strings is preserved.
 * INPUT         = CHAR *p1, CHAR *p2
 *
 * OUTPUT        = 0 if equal, < 0 if p1 < p2. > 0 if p1 > p2
 *
 * RETURN-NORMAL =
 *
 * RETURN-ERROR  = NONE
 *
 *
 **************************************************************************/
INT my_strcmpi(CHAR *p1, CHAR *p2)                      //          
{
  INT i;
  CHAR p3[128],p4[128];
  for(i=0;(p1[i] !=0) && i < 127;i++)
    p3[i] = (CHAR) _toupper((INT) p1[i]);
  p3[i] = 0;
  for(i=0;(p2[i] !=0) && i < 127;i++)
    p4[i] = (CHAR) _toupper((INT) p2[i]);
  p4[i] = 0;
  i = strcmp(p3,p4);
  return (i);
}
/***************************************************************************
 *
 * FUNCTION NAME = BVHSaveRestoreState()
 *
 * DESCRIPTION   =
 *
 *
 * INPUT         = NONE
 *
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL =
 *
 * RETURN-ERROR  = NONE
 *
 *
 **************************************************************************/
VOID  APIENTRY FAR BVHSaveRestoreState(USHORT Direction,
                                        PENVIRONMENT Environment,
                                        PVDHSAVEREST VDHSaveRest)
{
  USHORT    Sel;
  USHORT    Offset;
  VIDEOSTATE sVideoState;

  sVideoState.miState = pModes[Environment->ModeIndex].miModeId;
//             Sel = (USHORT) ((ULONG) Environment >> 16);
  Sel = SELECTOROF(Environment);            //          
  sVideoState.fStateFlags = 0;     //          
  if (VDHSaveRest->Flags&SAVEREST_HARDWARE)
  {
    sVideoState.fStateFlags = STATEFLAG_REGISTERS;
    Offset = FIELDOFFSET(ENVIRONMENT, bHardwareStateData);
    Offset += OFFSETOF(Environment);       //          
    MAKE32P(Sel,Offset,(ULONG FAR*)&sVideoState.pModeData);
    if(Environment->ModeData.color <= 8)
    {
      sVideoState.fStateFlags |= STATEFLAG_CLUT;
      Offset = (USHORT) ((ULONG)(&Environment->LookupTable)-(ULONG)Environment);
      Offset += OFFSETOF(Environment);     //          
      MAKE32P(Sel,Offset,(ULONG FAR*)&sVideoState.pCLUT);
    }
  }
  if (VDHSaveRest->Flags&(SAVEREST_FULLPVB|SAVEREST_PARTPVB))
  {
    sVideoState.fStateFlags |= STATEFLAG_VRAM;
    sVideoState.ulVRAMSaveSize = Environment->ModeData.FullBufferSize;
    MAKE32P(VDHSaveRest->PVBHugeSEL, 0,(ULONG FAR*)&sVideoState.pVRAM);
  }
  if (Direction == RESTORE)
  {
    /*           
     * unlock the registers before restore.
     */
    pfnPMIRequest (&Adapter,
                   PMIREQUEST_UNLOCKREGISTERS,
                   NULL,
                   NULL);

    /*
    ** Set Diamond SpeedStar specific external clock bits
    */
    /*           
    ** 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[sVideoState.miState].color;
    Adapter.ModeInfo.usBytesPerScanLine = pModes[sVideoState.miState].col;
    Adapter.ModeInfo.usTextRows = pModes[sVideoState.miState].row;
    Adapter.ModeInfo.usXResolution = pModes[sVideoState.miState].hres;
    Adapter.ModeInfo.usYResolution = pModes[sVideoState.miState].vres;
    Adapter.ModeInfo.usType = pModes[sVideoState.miState].fbType;
    Adapter.ModeInfo.ulPageLength= pModes[sVideoState.miState].ulPageLength;
    Adapter.ModeInfo.ulSaveSize  = pModes[sVideoState.miState].ulTotalSize;
    Adapter.ModeInfo.bXCharSize  = pModes[sVideoState.miState].XCharSize;
    Adapter.ModeInfo.bYCharSize  = pModes[sVideoState.miState].YCharSize;
    Adapter.ModeInfo.ulBufferAddress = (ULONG) pModes[sVideoState.miState].BufferAddress;
    Adapter.ModeInfo.ulApertureSize  = pModes[sVideoState.miState].BufferLength;
    if (!(Environment->CurrentCFGIndex & CFG_INDEX_SET))
    {
      Adapter.ModeInfo.bVrtRefresh = pModes[sVideoState.miState].bVrtRefresh;
      Adapter.ModeInfo.bHrtRefresh = pModes[sVideoState.miState].bHrtRefresh;
      Adapter.ModeInfo.bVrtPolPos = pModes[sVideoState.miState].bVrtPolPos;
      Adapter.ModeInfo.bHrtPolPos = pModes[sVideoState.miState].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;
    }
    Adapter.ModeInfo.usScrnTop = pModes[sVideoState.miState].usScrnTop;
    Adapter.ModeInfo.usScrnBottom = pModes[sVideoState.miState].usScrnBottom;
    Adapter.ModeInfo.usScrnLeft = pModes[sVideoState.miState].usScrnLeft;
    Adapter.ModeInfo.usScrnRight = pModes[sVideoState.miState].usScrnRight;

    pfnPMIRequest (&Adapter,
                   PMIREQUEST_RESTORESTATE,
                   &sVideoState,
                   NULL);

#ifdef TSU07
    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 /* TSU07 */

    /*  note for #9, you have to set adapter to enhanced mode               */   /*            */
    /*  before setting the external clock                                   */
//           ALl clock code moved to IBMGPMI.DLL
//    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);
  }
  else
  {
    pfnPMIRequest (&Adapter,                           /*             */
                   PMIREQUEST_UNLOCKREGISTERS,         /*             */
                   NULL,                               /*             */
                   NULL);                              /*             */

    pfnPMIRequest(&Adapter,
                  PMIREQUEST_SAVESTATE,
                  NULL,
                  &sVideoState);
  }
  if((Direction == RESTORE) &&
      !(Environment->ModeData.fbType & MODE_FLAG_GRAPHICS))
    SetHWFont(Environment);
}
/***************************************************************************
 *
 * FUNCTION NAME = BVHAccessCLUT()
 *
 * DESCRIPTION   =
 *
 *
 * INPUT         = NONE
 *
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL =
 *
 * RETURN-ERROR  = NONE
 *
 *
 **************************************************************************/
VOID  APIENTRY FAR BVHAccessCLUT(USHORT Direction, PCLUT pClut)
{
  if (Direction == SET)
     pfnPMIRequest(&Adapter,
                     PMIREQUEST_SETCLUT,
                     (PCLUTDATA) pClut,
                     NULL);
  else
     pfnPMIRequest(&Adapter,
                     PMIREQUEST_GETCLUT,
                     NULL,
                     (PCLUTDATA) pClut);
}
/***************************************************************************
 *
 * FUNCTION NAME = BVHAccessFont()
 *
 * DESCRIPTION   =
 *
 *
 * INPUT         = NONE
 *
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL =
 *
 * RETURN-ERROR  = NONE
 *
 *
 **************************************************************************/
USHORT  APIENTRY FAR BVHAccessFont(USHORT Direction, PFONTDATA pFontData)
{
  if (Direction == SET)
     return(pfnPMIRequest(&Adapter, PMIREQUEST_SETFONT, pFontData, NULL));
  else
     return(pfnPMIRequest(&Adapter, PMIREQUEST_GETFONT, NULL, pFontData));
}
/***************************************************************************
 *
 * FUNCTION NAME = BVHAccessPalette()
 *
 * DESCRIPTION   =
 *
 *
 * INPUT         = NONE
 *
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL =
 *
 * RETURN-ERROR  = NONE
 *
 *
 **************************************************************************/
VOID  APIENTRY FAR BVHAccessPalette(USHORT Direction, PPALETTEDATA pPalette)
{
  if (Direction == SET)
     pfnPMIRequest(&Adapter, PMIREQUEST_SETPALETTE, pPalette, NULL);
  else
     pfnPMIRequest(&Adapter, PMIREQUEST_GETPALETTE, NULL, pPalette);
}
/***************************************************************************
 *
 * FUNCTION NAME = BVHSetBank()
 *
 * DESCRIPTION   =
 *
 *
 * INPUT         = NONE
 *
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL =
 *
 * RETURN-ERROR  = NONE
 *
 * METHOD
 *      Search for 1st regop assign
 *      set list data to Bank
 *
 **************************************************************************/

VOID  APIENTRY FAR BVHSetBank(USHORT usBank, ULONG ulModeID)
{
  BANKDATA sBankData;

  sBankData.ulBank = usBank;
  sBankData.miBank = ulModeID;

  pfnPMIRequest(&Adapter, PMIREQUEST_SETBANK, &sBankData, NULL);
}

/*****************************************************************************
 *
 *  FUNCTION NAME:    BVHGetMem()
 *
 *
 *  DESCRIPTIVE NAME: Get memory
 *
 *  FUNCTION:
 *
 *  ENTRY POINT:
 *
 *  INPUT: (Passed on stack)
 *
 *  EXIT-NORMAL: AX = 0
 *
 *  EXIT-ERROR: AX = return code from DosSMGetMem()
 *
 *  EFFECTS:
 *
 *  NOTES:
 *      DosSMGetMem requires the first 4 bytes to be reserved for its own use.
 *      The pointer returned is therefore adjusted here to point to the actual
 *      memory requested.
 *
 *  INTERNAL REFERENCES:
 *    ROUTINES:
 *
 *  EXTERNAL REFERENCES:
 *    ROUTINES:
 *
 ****************************************************************************/

USHORT PASCAL NEAR BVHGetMem(VOID FAR * p, USHORT size)
{
    USHORT      rc;

    if ( !(rc = DosSMGetMem(p, size)) )
      *(USHORT FAR *)p += 4;
    return(rc);
}

/*****************************************************************************
 *
 *  FUNCTION NAME:    BVHFreeMem()
 *
 *
 *  DESCRIPTIVE NAME: Free memory previously allocated by BVHGetMem()
 *
 *  FUNCTION:
 *
 *  ENTRY POINT:
 *
 *  INPUT: (Passed on stack)
 *
 *  EXIT-NORMAL: AX = 0
 *
 *  EXIT-ERROR: AX = return code from DosSMFreeMem()
 *
 *  EFFECTS:
 *
 *  NOTES:
 *      Note the adjustment of 4 bytes to compensate for reserved space.
 *
 *  INTERNAL REFERENCES:
 *    ROUTINES:
 *
 *  EXTERNAL REFERENCES:
 *    ROUTINES:
 *
 ****************************************************************************/

USHORT PASCAL NEAR BVHFreeMem(VOID FAR * * p)
{
    USHORT rc;

    if ( !(rc = DosSMFreeMem((USHORT FAR *)*p-2)) )
      *p = NULL;
    return(rc);
}

/*****************************************************************************
 *
 *  FUNCTION NAME:    LoadBVHHandler()
 *
 *
 *  DESCRIPTIVE NAME: Load VIDEOPMI.DLL handler
 *
 *  FUNCTION: BVHPMI is a device independent handler. It depends on the
 *            VIDEOPMI.DLL for all hardware specific requests. It can't
 *            function unless this handler is loaded and initialized properly.
 *            This function
 *
 *  ENTRY POINT: LoadBVHHandler
 *    LINKAGE:   CALL NEAR
 *
 *  INPUT: (Passed on stack)
 *    PFNVIDEOPMIREQUEST *pfnPMIRequest(pointer to the address of the PMI entry point)
 *
 *  EXIT:      AX = 0 if success. DOS error otherwise.
 *
 *  EFFECTS: NONE
 *
 *  INTERNAL REFERENCES:
 *    ROUTINES: NONE
 *
 *  EXTERNAL REFERENCES:
 *    ROUTINES: NONE
 *
 ****************************************************************************/

ADAPTERINFO sCFGAdapter;             //          
char sFail[256];
APIRET LoadBVHHandler(PFNVIDEOPMIREQUEST *pfnPMIRequest)
{
   APIRET rc;
   HMODULE hmodVIDEOPMI;
   HMODULE hmodVIDEOCFG;                //          
   PFNCFG16REQUEST pfnCFGRequest;       //          
   //
   //           
   //
   //ADAPTERINFO sCFGAdapter;             //          
   //char sFail[256] = {0};

   if (!(rc = DosLoadModule(sFail, 256, "VIDEOPMI", &hmodVIDEOPMI)))
   {
      if (!(rc = DosGetProcAddr (hmodVIDEOPMI,
                                 "VIDEOPMI16Request",
                                 (PFN FAR *)pfnPMIRequest)))

        rc = (*pfnPMIRequest) (&Adapter,
                               PMIREQUEST_LOADPMIFILE,
                             //  "svgadata.pmi",
                               "\\os2\\svgadata.pmi",  //           
                               NULL);
      if (rc)
         DosFreeModule (hmodVIDEOPMI);
      /*
      ** Load the video configuration manager and get the current configuration
      ** If current adapter instance from svgadata.pmi does not match
      ** the one in video.cfg, discard the configuration table. This is done so
      ** until the video configuration manager becomes a true master of the
      ** install at which time, the name of the pmi file and the actual hardware
      ** configuration of the adapter instance will be given to bvhsvga by the
      ** manager. Later,...
      **
      ** No need to load the configuration manager twice.
      */
      if (rc || fCurrentCfg)
         return(rc);
      if (!(rc = DosLoadModule(sFail, 256, "VIDEOCFG", &hmodVIDEOCFG)))
      {
        if (!(rc = DosGetProcAddr (hmodVIDEOCFG,
                                 "VCFG16Request",
                                 (PFN FAR *)&pfnCFGRequest)))
           if (!(rc = pfnCFGRequest (NULL,
                                     VCFG_GET_CURRENT_CONFIG,
                                     &sCFGAdapter,
                                     &CurrentMonitor)))
              // compare the OEMstring between the PMI adapter and CFG adapter
              if (!my_strcmpi(sCFGAdapter.szOEMString,Adapter.Adapter.szOEMString))        //          
                  fCurrentCfg = TRUE;
        /*
        ** Don't free the module, as it improves the performance. VIDEOCFG
        ** obtains its data at its first loading and if freed, has to process
        ** the monitor info again.
        */
      }
      /*
      ** If we failed to get the configuration or to load the manager, don't
      ** fail. We can handle the situation on our own.
      */
      rc = NO_ERROR;
   }
   return(rc);
}

#ifdef TSU03  /*            */
/*****************************************************************************
 *
 *  FUNCTION NAME:    SwapEntries()
 *
 *
 *  DESCRIPTIVE NAME: Swap the entries in the table
 *
 *  FUNCTION: This function swaps the passed entris.
 *
 *  ENTRY POINT: SwapEntries
 *    LINKAGE:   CALL NEAR
 *
 *  INPUT: (Passed on stack)
 *            PVIDEOMODEINFO pSrc (address of the first)
 *            PVIDEOMODEINFO pDst (address of the second)
 *            USHORT usSize        (size of entries)
 *            PVIDEOMODEINFO pTemp (Temporary storage for the swap)
 *
 *  EXIT:     None.
 *
 *  EFFECTS: NONE
 *
 *  INTERNAL REFERENCES:
 *    ROUTINES: NONE
 *
 *  EXTERNAL REFERENCES:
 *    ROUTINES: NONE
 *
 ****************************************************************************/
VOID SwapEntries(PVIDEOMODEINFO pSrc,PVIDEOMODEINFO pDst,USHORT usSize,PVIDEOMODEINFO pTmp)
{
   memcpy(pTmp,pSrc,usSize);
   memcpy(pSrc,pDst,usSize);
   memcpy(pDst,pTmp,usSize);
}
/*****************************************************************************
 *
 *  FUNCTION NAME:    CellSort()
 *
 *
 *  DESCRIPTIVE NAME: Sort the mode header list obtained from VIDEOPMI.DLL
 *
 *  FUNCTION: The list will be sorted in the following manner. Graphics modes,
 *            first, ascending horizontal/vertical, followed by text modes in
 *            ascending columns/rows.
 *
 *  ENTRY POINT: SortModeTable
 *    LINKAGE:   CALL NEAR
 *
 *  INPUT: (Passed on stack)
 *            ULONG ulTotalModes
 *            PVIDEOMODEINFO pModes (address of the mode table)
 *
 *  EXIT:     None.
 *
 *  EFFECTS: NONE
 *
 *  INTERNAL REFERENCES:
 *    ROUTINES: NONE
 *
 *  EXTERNAL REFERENCES:
 *    ROUTINES: NONE
 *
 *  REMARK: The sorting algorithm employed here is a simple linear sort with
 *          swapping entries. The table is expected to be more of less already
 *          in the proper order, being dictated by the PMI file layout.
 *          The cell-sort works from left to right, comparing usCurrent
 *          entry and the one next to it, until the usEnd entry is hit
 *          (compare function dependant on the passed compare flag).
 *          If entries are in order it continues to increment the index.
 *          If out of order, it swaps and invokes itself with usEnd being decremented.
 *          Exit condition: usEnd hits the bottom (nothing to sort) or current
 *          hits the top (end).
 ****************************************************************************/
VOID   CellSort(PVIDEOMODEINFO pTable,
                USHORT usCurrent,
                USHORT usEnd,
                USHORT CompareFlag,
                USHORT usBottom,
                PVIDEOMODEINFO Swap)
{

   if(usEnd == usBottom) return;
   while (usCurrent < usEnd)
   {
      if(CompareFlag & MODE_FLAG_GRAPHICS)
      {
        if ((pTable[usCurrent].usXResolution <
             pTable[usCurrent+1].usXResolution) ||
           ((pTable[usCurrent].usXResolution ==
             pTable[usCurrent+1].usXResolution) &&
            (pTable[usCurrent].usYResolution <
             pTable[usCurrent+1].usYResolution)) ||
           ((pTable[usCurrent].usXResolution ==
             pTable[usCurrent+1].usXResolution) &&
            (pTable[usCurrent].usYResolution ==
             pTable[usCurrent+1].usYResolution) &&
            (pTable[usCurrent].bBitsPerPixel <
             pTable[usCurrent+1].bBitsPerPixel)))
        {
          usCurrent++;
          continue;
        }
        else
        {
          SwapEntries(&pTable[usCurrent],
                      &pTable[usCurrent+1],
                      sizeof(VIDEOMODEINFO),
                      Swap);
          CellSort(pTable,
                   usBottom,                    /* start */
                   usCurrent,                   /* end   */
                   MODE_FLAG_GRAPHICS,
                   usBottom,                    /* bottom*/
                   Swap);
          usCurrent++;
          continue;
        }
      }
      else
      {
        if ((pTable[usCurrent].usBytesPerScanLine <
             pTable[usCurrent+1].usBytesPerScanLine) ||
           ((pTable[usCurrent].usBytesPerScanLine ==
             pTable[usCurrent+1].usBytesPerScanLine) &&
            (pTable[usCurrent].usTextRows <
             pTable[usCurrent+1].usTextRows)) ||
           ((pTable[usCurrent].usBytesPerScanLine ==
             pTable[usCurrent+1].usBytesPerScanLine) &&
            (pTable[usCurrent].usTextRows ==
             pTable[usCurrent+1].usTextRows) &&
            (pTable[usCurrent].bBitsPerPixel <
             pTable[usCurrent+1].bBitsPerPixel)))
        {
          usCurrent++;
          continue;
        }
        else
        {
          SwapEntries(&pTable[usCurrent],
                      &pTable[usCurrent+1],
                      sizeof(VIDEOMODEINFO),
                      Swap);
          CellSort(pTable,
                   usBottom,                    /* start */
                   usCurrent,                   /* end   */
                   !MODE_FLAG_GRAPHICS,
                   usBottom,                    /* bottom */
                   Swap);
          usCurrent++;
          continue;
        }
      }
   }
   return;
}
/*****************************************************************************
 *
 *  FUNCTION NAME:    SortModeTable()
 *
 *
 *  DESCRIPTIVE NAME: Sort the mode header list obtained from VIDEOPMI.DLL
 *
 *  FUNCTION: The list will be sorted in the following manner. Graphics modes,
 *            first, ascending horizontal/vertical, followed by text modes in
 *            ascending columns/rows.
 *
 *  ENTRY POINT: SortModeTable
 *    LINKAGE:   CALL NEAR
 *
 *  INPUT: (Passed on stack)
 *            USHORT usTotalModes
 *            PVIDEOMODEINFO pModes (address of the mode table)
 *
 *  EXIT:     None.
 *
 *  EFFECTS: NONE
 *
 *  INTERNAL REFERENCES:
 *    ROUTINES: NONE
 *
 *  EXTERNAL REFERENCES:
 *    ROUTINES: NONE
 *
 *  REMARK: The sorting algorithm employed here is a simple linear sort with
 *          swapping entries. The table is expected to be more of less already
 *          in the proper order, being dictated by the PMI file layout.
 *          There are two passes. First which makes sure that graphics modes
 *          preceed the text modes and second which cell-sorts graphics and
 *          text sections separatelly. The cell-sort works from left to right,
 *          comparing entry i with i+1 and if out of order, swaps the entries
 *          and invokes the same basic cell sort for all the entries 0 thru i,
 *          as i+1 cell may belong to a cell other than i. So, each swap may cause
 *          a cascade of swaps to the left resulting in i*i-1 swaps at worst.
 ****************************************************************************/
VOID  SortModeTable(USHORT usTotalModes,PVIDEOMODEINFO pModes)
{
  USHORT usTextStartIndex = 0, usCurrentFrontCell = 0;
  USHORT usCurrentBackCell = usTotalModes - 1;
  PVIDEOMODEINFO TempSwapEntry;
  BOOL NotDone = TRUE;

  BVHGetMem(&TempSwapEntry, sizeof (VIDEOMODEINFO));
  /*
  ** Make sure all graphics entries come before the text entries.
  ** The grouping algorithm attacks the list from both ends, graphics
  ** have to be grouped at front and text modes at the back of the
  ** list.
  */
  while(usCurrentFrontCell < usCurrentBackCell)
  {
     /*
     ** If front entry already graphics, nothing to do, go check the back
     */
     if (pModes[usCurrentFrontCell].usType & MODE_FLAG_GRAPHICS)
        usCurrentFrontCell++;
     else
     {
       /*
       ** Front entry is text, have to swap. Find a candidate.
       */
       if (pModes[usCurrentBackCell].usType & MODE_FLAG_GRAPHICS)
       {
          SwapEntries(&pModes[usCurrentFrontCell],
                      &pModes[usCurrentBackCell],
                      sizeof(VIDEOMODEINFO),
                      TempSwapEntry);
          usCurrentFrontCell++;
          usCurrentBackCell--;
       }
       else
       {
          /*
          ** Back entry is text. Try next one.
          */
          usCurrentBackCell--;
          continue;
       }
     }
     if (!(pModes[usCurrentBackCell].usType & MODE_FLAG_GRAPHICS))
        usCurrentBackCell--;
     else
     {
       /*
       ** Back entry is graphics, have to swap. Find a candidate.
       */
       if (!(pModes[usCurrentFrontCell].usType & MODE_FLAG_GRAPHICS))
       {
          SwapEntries(&pModes[usCurrentFrontCell],
                      &pModes[usCurrentBackCell],
                      sizeof(VIDEOMODEINFO),
                      TempSwapEntry);
          usCurrentFrontCell++;
          usCurrentBackCell--;
       }
       else
       {
          /*
          ** Back entry is text. Try next one.
          */
          usCurrentFrontCell++;
          continue;
       }
     }
  }
  /*
  ** Now, we have two sections in the mode table. Text starts at usTextStartIndex
  */
  usTextStartIndex = usCurrentBackCell+1;
  /*
  ** Sort the graphics section by calling the cell sort recursivly
  */
  CellSort(pModes,
           0,                           /* start */
           usTextStartIndex-1,          /* end   */
           MODE_FLAG_GRAPHICS,
           0,                           /* bottom*/
           TempSwapEntry);
  /*
  ** Sort the text section by calling the cell sort recursivly
  */
  CellSort(pModes,
           usTextStartIndex,            /* start */
           usTotalModes - 1,            /* end   */
           !MODE_FLAG_GRAPHICS,
           usTextStartIndex,            /* bottom*/
           TempSwapEntry);
  BVHFreeMem(&TempSwapEntry);
}

#endif /*            */

/****************************************************************           
 *
 *  FUNCTION NAME:    InRightOrder ()
 *
 *
 *  DESCRIPTIVE NAME: In Order
 *
 *  FUNCTION: Determine if the two entries are in the order expected.
 *
 *  ENTRY POINT: InRightOrder ()
 *    LINKAGE:   CALL NEAR
 *
 *  INPUT: (Passed on stack)
 *            USHORT i : First entry
 *            USHORT j : Second entry
 *            PVIDEOMODEINFO pModes (mode table)
 *
 *  EXIT:     None.
 *
 *  RETURN: TRUE - first entry is "less than" the second entry.
 *           FALSE - otherwise.
 *
 ****************************************************************************/

BOOL InRightOrder (USHORT         i,
                   USHORT         j,
                   PVIDEOMODEINFO pModes)
{
   /*
    * Entry i is graphics mode and entry j is text mode.
    */
   if ((pModes[i].usType & MODE_FLAG_GRAPHICS) &&
       !(pModes[j].usType & MODE_FLAG_GRAPHICS))
      return TRUE;

   /*
    * Both are graphics mode
    */
   if((pModes[i].usType & MODE_FLAG_GRAPHICS) &&
      (pModes[j].usType & MODE_FLAG_GRAPHICS))
   {
      /*
       * x resolutions are in order
       */
      if (pModes[i].usXResolution < pModes[j].usXResolution)
         return TRUE;
      /*
       * x resolutions are the same. y resolutions are in order.
       */
      else if ((pModes[i].usXResolution == pModes[j].usXResolution) &&
               (pModes[i].usYResolution < pModes[j].usYResolution))
            return TRUE;
      /*
       *  resolutions are the same. Colors are in order.
       */
      else if ((pModes[i].usXResolution == pModes[j].usXResolution) &&
               (pModes[i].usYResolution == pModes[j].usYResolution) &&
               (pModes[i].bBitsPerPixel < pModes[j].bBitsPerPixel))
            return TRUE;
      /*            
       *  resolutions and colors are the same.
       *  refresh rates are in order. High refresh rates will be on the bottom,
       *  which will be search first.
       */
      else if ((pModes[i].usXResolution == pModes[j].usXResolution) &&
               (pModes[i].usYResolution == pModes[j].usYResolution) &&
               (pModes[i].bBitsPerPixel == pModes[j].bBitsPerPixel) &&
               (pModes[i].bVrtRefresh < pModes[j].bVrtRefresh))
            return TRUE;
      /*
       * For the rest cases, return FALSE;
       */
      else return FALSE;
   }

   /*
    * Both are text modes.
    */
   if(!(pModes[i].usType & MODE_FLAG_GRAPHICS) &&
      !(pModes[j].usType & MODE_FLAG_GRAPHICS))
   {
      /*
       * Columns are in order
       */
      if (pModes[i].usBytesPerScanLine < pModes[j].usBytesPerScanLine)
         return TRUE;
      /*
       * Columns are the same. Rows are in order.
       */
      else if ((pModes[i].usBytesPerScanLine == pModes[j].usBytesPerScanLine) &&
               (pModes[i].usTextRows < pModes[j].usTextRows))
            return TRUE;
      /*
       *  resolutions are the same. Colors are in order.
       */
      else if ((pModes[i].usBytesPerScanLine == pModes[j].usBytesPerScanLine) &&
               (pModes[i].usTextRows == pModes[j].usTextRows) &&
               (pModes[i].bBitsPerPixel < pModes[j].bBitsPerPixel))
            return TRUE;
      /*
       * For the rest cases, return FALSE;
       */
      else return FALSE;
   }

   /*
    * The last case: i is text mode and j is graphics mode.
    */
   return FALSE;
}

/* -------------------------            -----------------------------------
 * Rewrite SortModeTable(). The original routine causes an infinite loop in
 * CellSort(). Rewrite the routine using QuickSort. Quick Sort can be found
 * in all Algorithm textbook, e.g., Algorithm by Robert Sedgewick,
 * Addison-Wesley, 1983, pp. 106.
 *------------------------------------------------------------------------*/

VOID QuickSortAscending (INT            iLeftInd,
                         INT            iRightInd,
                         PVIDEOMODEINFO pModes,
                         PVIDEOMODEINFO pTempModeInfo)
{
   INT i, j;

   if (iRightInd <= iLeftInd)
      return;

   i = iLeftInd - 1;
   j = iRightInd;

   do
   {
      do
      {
         i++;
      } while (InRightOrder (i, iRightInd, pModes));

      do
      {
         j--;
      } while ((j > iLeftInd) && InRightOrder (iRightInd, j, pModes));

      /*
       * swap i and j entries.
       */
      memcpy (pTempModeInfo, &pModes[i], sizeof (VIDEOMODEINFO));
      memcpy (&pModes[i], &pModes[j], sizeof (VIDEOMODEINFO));
      memcpy (&pModes[j], pTempModeInfo, sizeof (VIDEOMODEINFO));
   } while (j > i);

   memcpy(&pModes[j], &pModes[i], sizeof (VIDEOMODEINFO));
   memcpy(&pModes[i], &pModes[iRightInd], sizeof (VIDEOMODEINFO));
   memcpy(&pModes[iRightInd], pTempModeInfo, sizeof (VIDEOMODEINFO));

   QuickSortAscending (iLeftInd, i - 1, pModes, pTempModeInfo);
   QuickSortAscending (i + 1, iRightInd, pModes, pTempModeInfo);
}

/********************************************************           
 *
 *  FUNCTION NAME:    SortModeTable()
 *
 *
 *  DESCRIPTIVE NAME: Sort the mode header list obtained from VIDEOPMI.DLL
 *
 *  FUNCTION: The list will be sorted in the following manner. Graphics modes,
 *            first, ascending horizontal/vertical, followed by text modes in
 *            ascending columns/rows.
 *
 *  ENTRY POINT: SortModeTable
 *    LINKAGE:   CALL NEAR
 *
 *  INPUT: (Passed on stack)
 *            USHORT usTotalModes
 *            PVIDEOMODEINFO pModes (address of the mode table)
 *
 *  EXIT:     None.
 *
 *  EFFECTS: NONE
 *
 *  INTERNAL REFERENCES:
 *    ROUTINES: NONE
 *
 *  EXTERNAL REFERENCES:
 *    ROUTINES: NONE
 *
 ****************************************************************************/

VOID  SortModeTable(USHORT usTotalModes,PVIDEOMODEINFO pModes)
{
   VIDEOMODEINFO TempModeInfo;

   QuickSortAscending (0, (INT)(usTotalModes - 1), pModes, &TempModeInfo);
}

/*****************************************************************************
 *
 *  FUNCTION NAME:    InitializeBVH()
 *
 *
 *  DESCRIPTIVE NAME: Initialize all structures related to BVHDATA.DLL handler
 *
 *  FUNCTION: BVHPMI will obtain the list of supported modes from VIDEOPMI.DLL.
 *            The list will be sorted in the following manner. Graphics modes first,
 *            ascending horizontal/vertical, followed by text modes in ascending
 *            columns/rows.
 *
 *  ENTRY POINT: InitializeBVH
 *    LINKAGE:   CALL NEAR
 *
 *  INPUT: (Passed on stack)
 *             PULONG ulTotalModes (pointer to the total supported modes )
 *             PVIDEOMODEINFO *pModes (pointer to the address of the mode table)
 *
 *  EXIT:      AX = 0 if success. DOS error otherwise.
 *
 *  EFFECTS: NONE
 *
 *  INTERNAL REFERENCES:
 *    ROUTINES: NONE
 *
 *  EXTERNAL REFERENCES:
 *    ROUTINES: NONE
 *
 ****************************************************************************/
APIRET InitializeBVH (PULONG  pulTotalModes,
                      PULONG  pulMaxListSize,
                      SEL    *selTable)
{
  APIRET rc;
  PVIDEOMODEINFO pTmpModes;
  SEL selBVHTable;
  USHORT i;

  /*
  ** Query number of mode entries
  */
  if (!(rc = pfnPMIRequest(&Adapter,
                           PMIREQUEST_QUERYMAXMODEENTRIES,
                           NULL,
                           pulTotalModes)))
  {
     if (!(rc=DosAllocSeg((USHORT) (*pulTotalModes)*sizeof(VIDEOMODEINFO),
                                    &selBVHTable,
                                    SEG_GETTABLE)))
     {
        pTmpModes = MAKEP(selBVHTable, 0);
        /*
        ** Obtain all ModeInfoData
        */
        if (!(rc=pfnPMIRequest(&Adapter,
                               PMIREQUEST_QUERYMODEINFODATA,
                               NULL,
                               pTmpModes)))
        {
           /*
           ** Allocate for the internal format mode table.
           ** Query maximum OEM mode section required and add
           ** the OEM section at the end of each mode.
           */
           if (!(rc=DosAllocSeg((USHORT) (*pulTotalModes)*sizeof(VIDEOMODE),
                                          selTable,
                                          SEG_GETTABLE)))
           {
              SortModeTable((USHORT) *pulTotalModes,pTmpModes);
              pModes = MAKEP(*selTable, 0);         /* global pointer to the table */
              for(i=0;i< (USHORT) *pulTotalModes;i++)
              {
                 pModes[i].cb = 14;
                 pModes[i].fmt_ID = 0;
                 pModes[i].attrib = 1;
                 pModes[i].miModeId = pTmpModes[i].miModeId;
                 pModes[i].color    = pTmpModes[i].bBitsPerPixel;
                 pModes[i].col      = pTmpModes[i].usBytesPerScanLine;
                 pModes[i].row      = pTmpModes[i].usTextRows;
                 pModes[i].hres     = pTmpModes[i].usXResolution;
                 pModes[i].vres     = pTmpModes[i].usYResolution;
                 pModes[i].fbType   = (BYTE) pTmpModes[i].usType;
                 pModes[i].ulPageLength= pTmpModes[i].ulPageLength;
                 pModes[i].ulTotalSize= pTmpModes[i].ulSaveSize;
                 pModes[i].XCharSize= pTmpModes[i].bXCharSize;
                 pModes[i].YCharSize= pTmpModes[i].bYCharSize;
                 pModes[i].BufferAddress=
                                 (UCHAR FAR * ) pTmpModes[i].ulBufferAddress;
                 pModes[i].BufferLength = pTmpModes[i].ulApertureSize;
                 /*
                 **            Adding display entries.
                 */
                 pModes[i].bVrtRefresh = pTmpModes[i].bVrtRefresh;
                 pModes[i].bHrtRefresh = pTmpModes[i].bHrtRefresh;
                 pModes[i].bVrtPolPos  = pTmpModes[i].bVrtPolPos;
                 pModes[i].bHrtPolPos  = pTmpModes[i].bHrtPolPos;
                 pModes[i].usScrnTop   = pTmpModes[i].usScrnTop;
                 pModes[i].usScrnBottom= pTmpModes[i].usScrnBottom;
                 pModes[i].usScrnLeft  = pTmpModes[i].usScrnLeft;
                 pModes[i].usScrnRight = pTmpModes[i].usScrnRight;
              }
              /*
              ** Free the VIDEOPMI table selector
              */
              DosFreeSeg(selBVHTable);
              /*
              ** Query maximum size for the hardware command list required
              */
              rc=pfnPMIRequest(&Adapter,
                               PMIREQUEST_QUERYMAXMODELISTSIZE,
                               NULL,
                               pulMaxListSize);
           }
        }
     }
  }
  return rc;
}

#ifdef TSU12   //          

/*****************************************************************************
 *
 *  FUNCTION NAME:    SetAccessToPMI()
 *
 *
 *  DESCRIPTIVE NAME: Set access to PMI for each new process.
 *
 *  FUNCTION:
 *
 *  ENTRY POINT: SetAccessToPMI
 *    LINKAGE:   CALL NEAR
 *
 *  INPUT: (Passed on stack)
 *
 *  EXIT:    0 if success. DOS error otherwise.
 *
 *  EFFECTS: NONE
 *
 *  INTERNAL REFERENCES:
 *    ROUTINES: NONE
 *
 *  EXTERNAL REFERENCES:
 *    ROUTINES: NONE
 *
 ****************************************************************************/
APIRET   SetAccessToPMI(VOID)
{
  APIRET rc;
  /*
   * Both handle and PMIrequest entry point are unique, not per
   * process. So by loading again, we ensure that VIDEOPMI.DLL is loaded
   * at all times, regardless of the status of all the past processes.
   * Mode table and other PMI related data was obtained at the first load.
   * DosGetSeg() provides addressability to the global table selector for
   * each new  process.
   */
  if(!(rc = LoadBVHHandler(&pfnPMIRequest)))
    rc = DosGetSeg(selBVHTable);
  return(rc);
}

#endif  //          


/****************************************************************************
 *
 *  SUBROUTINE NAME: PhysToUVirt
 *
 *  DESCRIPTIVE NAME: Allocate an LDT sel:off to physical memory
 *
 *  FUNCTION: PhysToUVirt is called by routines who need access to
 *            either the physical display buffer, or the ROM font.
 *            This routine calls the screen device driver (SCREEN$)
 *            via DosDevIOCtl who, in turn, issues a
 *            DevHlp_PhysToUVirt call.
 *
 *            A check is made on the handle to SCREEN$, obtained by
 *            DosOpen, to determine it's validity. The handle is
 *            allocated on a per-process basis, which means it might
 *            not be valid the next time it is used. An alternative
 *            is to use instance data and store the handle there,
 *            but currently there is no other need for instance data
 *            and the overhead too high for 2 bytes out of a 4k page.
 *
 *  ENTRY POINT: PhysToUVirt
 *    LINKAGE:   CALL FAR
 *
 *  INPUT: (Passed on stack)
 *             FAR *PhysicalAddress ( Physical address )
 *             FAR *LDTSelector     ( Place to return LDT sel:off )
 *             USHORT Length        ( Size of physical memory area )
 *
 *  EXIT-NORMAL: AX = 0
 *               LDT sel:off is returned to caller
 *
 *  EXIT-ERROR: AX = DosDevIOCtl error
 *
 *  EFFECTS: NONE
 *
 *  INTERNAL REFERENCES:
 *    ROUTINES: NONE
 *
 *  EXTERNAL REFERENCES:
 *    ROUTINES: DosDevIOCtl
 *
 ****************************************************************************/

USHORT NEAR PhysToUVirt (FARADDRESS      PhysicalAddress,
                         FARADDRESS far *LDTAddress,
                         USHORT          Length)
{
   HFILE hScreenDD;                   /* handle of SCREEN$ device driver   */
   USHORT OpenAction;                 /* action taken to open device       */
   USHORT rc;                         /* function return code              */
   FARADDRESS ParmBlock;
   struct
   {
     FARADDRESS PhysicalAddress;
     short Length;
   }
   ParmList;

   ParmList.Length = Length;
   ParmList.PhysicalAddress.FullAddress = PhysicalAddress.FullAddress;

   if (!(rc = DosOpen (SCREENDD_NAME,
                       &hScreenDD,
                       &OpenAction,
                       NO_SIZE,
                       NO_ATTRIBUTES,
                       OPEN_IF_EXISTS,
                       NO_INHERIT + DENY_NONE + READ_AND_WRITE,
                       RESERVED_LONG)))

   {
      if (!(rc = DosDevIOCtl ((PVOID)&ParmBlock,
                              (PVOID)&ParmList,
                              0x75,
                              0x3,
                              hScreenDD)))
         *LDTAddress = ParmBlock;

      DosClose (hScreenDD);
   }

   return  rc;
}

/****************************************************************************
 *
 *  SUBROUTINE NAME: FreePhysToUVirt
 *
 *  DESCRIPTIVE NAME: Deallocate an LDT selector to physical memory
 *
 *  FUNCTION: FreePhysToUVirt is called by routines who are done
 *            accessing the memory allocated by PhysToUVirt.  This is
 *            done by calling the screen device driver (SCREEN$) via
 *            DosDevIOCtl who, in turn, issues a DevHlp_PhysToUVirt.
 *
 *  ENTRY POINT: FreePhysToUVirt
 *    LINKAGE:   CALL FAR
 *
 *  INPUT: (Passed on stack)
 *             USHORT LDTSelector  ( LDT selector to be deallocated )
 *
 *  EXIT-NORMAL: LDT selector is deallocated
 *
 *  EFFECTS: NONE
 *
 *  INTERNAL REFERENCES:
 *    ROUTINES: NONE
 *
 *  EXTERNAL REFERENCES:
 *    ROUTINES: DosDevIOCtl
 *
 ****************************************************************************/

VOID NEAR FreePhysToUVirt(USHORT LDTSelector)
{
   HFILE hScreenDD;                   /* handle of SCREEN$ device driver   */
   USHORT OpenAction;                 /* action taken to open device       */
   USHORT rc;                         /* function return code              */

   if (!(rc = DosOpen (SCREENDD_NAME,
                       &hScreenDD,
                       &OpenAction,
                       NO_SIZE,
                       NO_ATTRIBUTES,
                       OPEN_IF_EXISTS,
                       NO_INHERIT + DENY_NONE + READ_AND_WRITE,
                       RESERVED_LONG)))

   {
      DosDevIOCtl ((PVOID)NULL,
                   (PVOID)&LDTSelector,
                   0x71,
                   0x3,
                   hScreenDD);

      DosClose (hScreenDD);
   }
}

/****************************************************************************
 *
 * SUBROUTINE NAME: SCREENDDIoctl
 *
 * DESCRIPTIVE NAME: perform requested IOCTL request to screendd
 *
 * FUNCTION:  Issue generic IOCTL, use strong error checking
 *
 *  ENTRY POINT: ScreenDDIoctl
 *    LINKAGE:   CALL NEAR
 *
 *  INPUT: data - data from IOCTL request
 *         parm - parameter to IOCTL request
 *         function - function requested
 *
 *  EXIT:  error code from DosOpen or DosDevIoctl
 *
 *  EFFECTS: determined by request
 *
 *  INTERNAL REFERENCES:
 *
 *    ROUTINES: DosOpen, DosDevIoctl
 *
 *  EXTERNAL REFERENCES:
 ****************************************************************************/

int PASCAL near SCREENDDIoctl(VOID *data,VOID *parm,USHORT function)
{
   HFILE hScreenDD;                   /* handle of SCREEN$ device driver   */
   USHORT OpenAction;                 /* action taken to open device       */
   USHORT rc;                         /* function return code              */

   if (!(rc = DosOpen (SCREENDD_NAME,
                       &hScreenDD,
                       &OpenAction,
                       NO_SIZE,
                       NO_ATTRIBUTES,
                       OPEN_IF_EXISTS,
                       NO_INHERIT + DENY_NONE + READ_AND_WRITE,
                       RESERVED_LONG)))
   {
      rc = DosDevIOCtl (data,
                        parm,
                        function,
                        SCREENDD_CATEGORY,
                        hScreenDD);

      DosClose (hScreenDD);
   }

   return  rc;
}
/*****************************************************************************
 *
 * SUBROUTINE NAME: OEMHLPIoctl
 *
 * DESCRIPTIVE NAME: perform requested IOCTL request
 *
 * FUNCTION:  Issue generic IOCTL, use strong error checking
 *
 *  ENTRY POINT: OEMHLPIoctl
 *    LINKAGE:   CALL NEAR
 *
 *  INPUT: data - data from IOCTL request
 *         parm - parameter to IOCTL request
 *         function - function requested
 *
 *  EXIT:  error code from DosOpen or DosDevIoctl
 *
 *  EFFECTS: determined by request
 *
 *  INTERNAL REFERENCES:
 *
 *    ROUTINES: DosOpen, DosDevIoctl
 *
 *  EXTERNAL REFERENCES:
 *
 ****************************************************************************/

INT PASCAL NEAR OEMHLPIoctl(UCHAR * data,
                            UCHAR * parm,
                            USHORT  function)
{

   HFILE hOemhlp;                     /* handle of OEMHLP$ device driver   */
   USHORT OpenAction;                 /* action taken to open device       */
   USHORT rc;                         /* function return code              */


   if (!(rc = DosOpen (OEMHLPDD_NAME,
                       &hOemhlp,
                       &OpenAction,
                       NO_SIZE,
                       NO_ATTRIBUTES,
                       OPEN_IF_EXISTS,
                       NO_INHERIT+ DENY_NONE + READ_AND_WRITE,
                       RESERVED_LONG)))

   {
      rc = DosDevIOCtl (data,
                        parm,
                        function,
                        OEMHLP_CATEGORY,
                        hOemhlp);
      DosClose (hOemhlp);
   }

   return  rc;

}

/****************************************************************************
 *
 * FUNCTION NAME = GetAdapterConfig
 *
 * DESCRIPTION   = Determine the adapter type from SCREEN$
 *
 * INPUT         = NONE
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL = NONE
 * RETURN-ERROR  = NONE
 *
 ****************************************************************************/

USHORT PASCAL NEAR GetAdapterConfig(VOID)
{
  USHORT rc = 0;                       /* default no error                  */
  struct
  { /* hardware configuration information */
        USHORT fVideoType;      /* video adapter(s) and monitor(s) */
        USHORT display;         /* display type */
  } VideoHardware;
  UCHAR videoType;


  /*
   * get adapter type from screenDD to be used in SetDiamondClk @TSU
   */
  SCREENDDIoctl(&BVHHardware, NULL, SCREENDD_SVGA_ID);

  if (!fNoVideopmi)
  {
     ulTotalMemory = Adapter.Adapter.ulTotalMemory;
     rc = SCREENDDIoctl(&OEMHardware, NULL, SCREENDD_SVGA_OEM);
     CurrentDisplay = MONITOR_OEM;
  }

  if (rc || fNoVideopmi)
  {
    /*
    ** Use the adapter information obtained from OEMHLP$ to determine
    ** the default video memory and display configurations
    */
    VideoHardware.fVideoType = VDHERROR_NO_ADAPTER;/* assume no adapter       */
    if (!(rc = OEMHLPIoctl(&videoType, NULL, OEMHLP_FUNCTION_ADAPTER)))
    {
      if (videoType&MPA_DEVICE)
      {
        VideoHardware.fVideoType |= MPA_BIT;/* mark MPA is present            */
      }
      if (videoType&CGA_DEVICE)
      {                                  /* B8000 accessible?                 */
        VideoHardware.fVideoType |= CGA_BIT;/* mark CGA is present            */
      }

      if (videoType&EGA_DEVICE)
      {                                  /* EGA present?                      */
        VideoHardware.fVideoType ^= CGA_BIT|EGA_BIT;
      }

      if (videoType&VGA_DEVICE)
      {                                  /* VGA present?                      */

        if (!(OEMHLPIoctl(&videoType, NULL, /* Get monitor type              */
           OEMHLP_FUNCTION_VIDEO)))
        {
          ulTotalMemory = 256L*1024L;/* Default to 256K              */

          switch (videoType)
          {
            case  VGA_NODISPLAY :

              VideoHardware.display = NoDisplay;
              VideoHardware.fVideoType = NODISPLAY_BIT;
              break;

            case  VGA_COLOR :

              VideoHardware.display = Color8512_8513;
              VideoHardware.fVideoType ^= CGA_BIT|VGC_BIT;
              break;

            case  VGA_MONO :

              if (VideoHardware.fVideoType&MPA_BIT)
              {
                VideoHardware.display = Mono8503;
                VideoHardware.fVideoType ^= MPA_BIT|VGM_BIT;
              }

              else
              {
                VideoHardware.display = Color8512_8513;
                VideoHardware.fVideoType ^= CGA_BIT|VGC_BIT;
              }
              break;

          }                            /* end of switch ( videoType )       */
        }                              /* end of VGA monitor query through  */
      }                                /* end of EGA/VGA present check      */
    }
    CurrentDisplay = VideoHardware.display;
  }                                    /* end of video adapter query        */
                                       /* through OEMHLP$                   */
  /*
   *           
   * see if a plasma display is present
  */

  if (!(rc = OEMHLPIoctl(&videoType, NULL, OEMHLP_FUNCTION_MISC)) &&
      (videoType & 0x40))
      CurrentDisplay = PlasmaDisplay;

  return (rc);
}

/****************************************************************           
 *
 * FUNCTION NAME = VGA_ENV ()
 *
 * DESCRIPTION   = Determine the position of VGA environment
 *                 It depends on if the environment is created when videopmi
 *                 is loaded. Check InitEnv(). GetCurrentConfig ()
 *
 * INPUT         = NONE
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL = NONE
 * RETURN-ERROR  = NONE
 *
 ****************************************************************************/

PENVIRONMENT VGA_ENV (PENVIRONMENT pEnvironment)
{
   if (pEnvironment->EnvFlags & ENVFLAG_BEFOREPMI)
      return (PENVIRONMENT)((PBYTE)pEnvironment + sizeof(ENVIRONMENT));
   else
      return (PENVIRONMENT)((PBYTE)pEnvironment + ulEnvLength);
}
