/*DDK*************************************************************************/
/*                                                                           */
/* COPYRIGHT    Copyright (C) 1995 IBM Corporation                           */
/*                                                                           */
/*    The following IBM OS/2 WARP source code is provided to you solely for  */
/*    the purpose of assisting you in your development of OS/2 WARP device   */
/*    drivers. You may use this code in accordance with the IBM License      */
/*    Agreement provided in the IBM Device Driver Source Kit for OS/2. This  */
/*    Copyright statement may not be removed.                                */
/*                                                                           */
/*****************************************************************************/
/*static char *SCCSID = "@(#)bvhxga2.c  13.6 91/05/23";*/

/**********************************************************************/
/*                                                                    */
/*   File            = BVHXGA2                                        */
/*                                                                    */
/*   Description     = 2.0 BVH Support for XGA                        */
/*                                                                    */
/*   Function        = Provides the extra BVS support that is not     */
/*                     provided by the VGA BVS suuport.               */
/*                     DevEnable - sets up call vector table          */
/*                     SetMode                                        */
/*                     GetMode                                        */
/*                     RetConfigInfo                                  */
/*                                                                    */
/*   Reference       = Winthorn Functional Specification              */
/*                                                                    */
/*   Modification History                                             */
/*                                                                    */
/*   11/24/92  DMH  53028  - Remove 132 column resolution validation, */
/*                           allowing it to the video mode table      */
/*                           look-up done by BVHVGA.                  */
/*   04/25/93             68212  - Mark 132 column mode, before       */
/*                           chaining into bvhvga                     */
/*                                                                    */
/*                                                                    */
/**********************************************************************/

#define INCL_BASE               /* ALL of OS/2 Base                */
#define INCL_DOSDEVICES         /* Device specific, ring 2 support */
#define INCL_GPIERRORS
#define INCL_GPIBITMAPS
#define INCL_VIO
#include <os2.h>
#include <xgaadapt.h>
#include <plasma.h>
#include "bvhxga2.h"

#define DEVINFO_SUBMODEL 4      /* Same as 2.0 definition, STJ */
#define MODE_132_XGA            0x4000          /*          */

/**********************************************************************/
/* Private interfaces                                                 */
/**********************************************************************/
SHORT         ExamineConfig(VOID);

/**********************************************************************/
/* globals                                                            */
/**********************************************************************/
/* MEB. I have removed the need for this variable - see the comment   */
/*      below around the code that used to use it.                    */
/* BOOL          DMQS_seg_access = FALSE;                             */
/**********************************************************************/
HFILE         dev_handle;
ADAPTERINFO   xga_adapter;
USHORT        huge_shift;
#ifndef SM00035
BOOL          need_table_init = TRUE;
#endif /* ndef SM00035 */

/* need upto 9 xga instances (8 slots + 1 planar) */
XGAINSTANCE InstanceData[9];
BYTE        bNumberOfInstances;

MODEDATAINFO ModesInfo;

PSZ DMQS_path = NULL;
USHORT DMQS_filename_offset;

UCHAR monitor_id_ascii[MAX_MON_ID];

/******************************************************************************/
/* Table of valid tokens for DMQS override file.                              */
/******************************************************************************/
UCHAR DMQS_token_table[] = DMQS_TOKEN_TABLE;

VOID FAR haltproc(VOID);

#define INDEXED_REG 0x01

/**********************************************************************/
/* This data is used when no DMQS monitor configuration files can be  */
/* found.                                                             */
/**********************************************************************/
BYTE   DMQSModeData_LoRes[] =
{
    /******************************************************************/
    /* Lo Res Mode Data                                               */
    /******************************************************************/

    /* HEADER */
    0x70, 0x00, /*  112   Length of data             */
    0x80, 0x02, /*  640   Pixel width                */
    0xE0, 0x01, /*  480   Pixel height               */
    0x00, 0x00, /*    0   Minimum XGA level          */
    0x00, 0x00, /*    0   Vendor ID                  */
    0x00, 0x00, /*        Reserved                   */
    0x00, 0x00, /*        Flags                      */
    0x10, 0x00, /*   16   Offset of start of data    */

    /* DATA */
    INDEXED_REG, 0x50, 0x01,  // display mode 1-prepare for reset, VFB
    INDEXED_REG, 0x50, 0x00,  //     "         ,reset
    INDEXED_REG, 0x10, 0x63,
    INDEXED_REG, 0x11, 0x00,
    INDEXED_REG, 0x12, 0x4f,
    INDEXED_REG, 0x13, 0x00,
    INDEXED_REG, 0x14, 0x4f,
    INDEXED_REG, 0x15, 0x00,
    INDEXED_REG, 0x16, 0x63,
    INDEXED_REG, 0x17, 0x00,
    INDEXED_REG, 0x18, 0x55,
    INDEXED_REG, 0x19, 0x00,
    INDEXED_REG, 0x1a, 0x61,
    INDEXED_REG, 0x1b, 0x00,
    INDEXED_REG, 0x1c, 0x00,
    INDEXED_REG, 0x1e, 0x00,
    INDEXED_REG, 0x20, 0x0c,
    INDEXED_REG, 0x21, 0x02,
    INDEXED_REG, 0x22, 0xdf,
    INDEXED_REG, 0x23, 0x01,
    INDEXED_REG, 0x24, 0xdf,
    INDEXED_REG, 0x25, 0x01,
    INDEXED_REG, 0x26, 0x0c,
    INDEXED_REG, 0x27, 0x02,
    INDEXED_REG, 0x28, 0xea,
    INDEXED_REG, 0x29, 0x01,
    INDEXED_REG, 0x2a, 0xec,
    INDEXED_REG, 0x2c, 0xff,
    INDEXED_REG, 0x2d, 0xff,
    INDEXED_REG, 0x54, 0x00,  // clock select
    INDEXED_REG, 0x70, 0x00,  // external clock select
    INDEXED_REG, 0x50, 0xd7   // display mode 1 - 480 line, VFB,
                              // non interlaced, normal operation
};

BYTE   DMQSModeData_HiRes[] =
{
    /******************************************************************/
    /* Hi Res Mode Data                                               */
    /******************************************************************/

    /* HEADER */
    0x70, 0x00, /*  112   Length of data             */
    0x00, 0x04, /* 1024   Pixel width                */
    0x00, 0x03, /*  768   Pixel height               */
    0x00, 0x00, /*    0   Minimum XGA level          */
    0x00, 0x00, /*    0   Vendor ID                  */
    0x00, 0x00, /*        Reserved                   */
    0x00, 0x00, /*        Flags                      */
    0x10, 0x00, /*   16   Offset of start of data    */

    /* DATA */
    INDEXED_REG, 0x50, 0x01,  // display mode 1-prepare for reset, VFB
    INDEXED_REG, 0x50, 0x00,  //     "         ,reset
    INDEXED_REG, 0x10, 0x9d,
    INDEXED_REG, 0x11, 0x00,
    INDEXED_REG, 0x12, 0x7f,
    INDEXED_REG, 0x13, 0x00,
    INDEXED_REG, 0x14, 0x7f,
    INDEXED_REG, 0x15, 0x00,
    INDEXED_REG, 0x16, 0x9d,
    INDEXED_REG, 0x17, 0x00,
    INDEXED_REG, 0x18, 0x87,
    INDEXED_REG, 0x19, 0x00,
    INDEXED_REG, 0x1a, 0x9c,
    INDEXED_REG, 0x1b, 0x00,
    INDEXED_REG, 0x1c, 0x40,
    INDEXED_REG, 0x1e, 0x04,
    INDEXED_REG, 0x20, 0x30,
    INDEXED_REG, 0x21, 0x03,
    INDEXED_REG, 0x22, 0xff,
    INDEXED_REG, 0x23, 0x02,
    INDEXED_REG, 0x24, 0xff,
    INDEXED_REG, 0x25, 0x02,
    INDEXED_REG, 0x26, 0x30,
    INDEXED_REG, 0x27, 0x03,
    INDEXED_REG, 0x28, 0x00,
    INDEXED_REG, 0x29, 0x03,
    INDEXED_REG, 0x2a, 0x08,
    INDEXED_REG, 0x2c, 0xff,
    INDEXED_REG, 0x2d, 0xff,
    INDEXED_REG, 0x54, 0x0d,  // clock select
    INDEXED_REG, 0x70, 0x00,  // external clock select
    INDEXED_REG, 0x50, 0x0f   // display mode 1 - 768 line,
                              // interlaced, normal operation
};

/**********************************************************************/
/* Table of VDH entry points                                          */
/*                                                                    */
/* Device chaining is not implemented in these video device handlers. */
/* If the VDH supports the VDH function, DevEnable will over-write    */
/* the current entry in the call vector table.  If the VDH does not   */
/* support the VDH function, the call vector table entry will not be  */
/* touched.                                                           */
/**********************************************************************/
static USHORT FAR * VDHEntryPoint[] = {
                             (PVOID)ChainRouter,         /* 256 */
                             (PVOID)InitEnv,             /* 257 */
                             (PVOID)SaveEnv,             /* 258 */
                             (PVOID)RestoreEnv,          /* 259 */
                             (PVOID)RetConfigInfo,       /* 260 */
                             (PVOID)ChainRouter,         /* 261 */
                             (PVOID)ChainRouter,         /* 262 */
                             (PVOID)ChainRouter,         /* 263 */
                             (PVOID)ChainRouter,         /* 264 */
                             (PVOID)ChainRouter,         /* 265 */
                             (PVOID)ChainRouter,         /* 266 */
                             (PVOID)ChainRouter,         /* 267 */
                             (PVOID)BVHGetMode,          /* 268 */
                             (PVOID)BVHSetMode,          /* 269 */
                             (PVOID)ChainRouter,         /* 270 */
                             (PVOID)ChainRouter,         /* 271 */
                             (PVOID)ChainRouter,         /* 272 */
                             (PVOID)ChainRouter,         /* 273 */
                             (PVOID)ChainRouter,         /* 274 */
                             (PVOID)ChainRouter,         /* 275 */
                             (PVOID)UNSUPPORTED_FUNCTION,/* 276 */
                             (PVOID)UNSUPPORTED_FUNCTION,/* 277 */
                             (PVOID)UNSUPPORTED_FUNCTION,/* 278 */
                             (PVOID)ChainRouter          /* 279 */
                                          };


#ifdef SM00035
USHORT vga_present;
#else /* ndef SM00035 */
USHORT vga_present = TRUE;
#endif /* ndef SM00035 */

VDHCONFIGINFO VGAConfigData;

FNVGACHAIN ChainedCallVectorTable[MaxFn];

BOOL first_init = TRUE; /* Flag to force only one INIT device initialization */
BOOL fFirstSetMode = TRUE;

ULONG PartialSaveSize;   /* Room required to save entire PVB in the popup mode */

USHORT DeviceDriver = 0;/* version number                                     */

/**********************************************************************/
/* _acrtused is required in a Dynamic Link Library to nullify         */
/* automatic loading of C runtime support. It must be initialised     */
/* to 1 or 0.                                                         */
/* See OS/2 programming Guide Chapter 6.                              */
/**********************************************************************/
int             __acrtused = 1;

/**********************************************************************/
/* Function prototypes for functions that are forward referenced      */
/* within this module.                                                */
/**********************************************************************/
VOID    SearchForXGAInstances(VOID);
VOID    WorkOutAvailableModes(VOID);



/**********************************************************************/
/* checks if a plasma display is being used and if so returns the     */
/* display type number; if not returns zero                           */
/**********************************************************************/
USHORT pascal near plasma_display_type(VOID)
#define REQUEST_BLK_LEN 0x20
{
    BYTE                 sub_model;
    USHORT               request_blk[REQUEST_BLK_LEN];
    HFILE                h_screen;
    USHORT               action_taken;

    #define DEVINFO_SUBMODEL 4
    DosDevConfig(&sub_model, DEVINFO_SUBMODEL, 0);

    if (sub_model == KAUAI_SUBMODEL)
    {
        request_blk[0] = REQUEST_BLK_LEN * 2; /* length in bytes */

        request_blk[1] =                      /* LID ?           */
        request_blk[2] =                      /* unit            */
        request_blk[4] =                      /* reserved        */
        request_blk[5] = 0;                   /* reserved        */

        request_blk[3] = 3;                   /* function        */

        request_blk[6] = 0xffff;              /* return code     */


        DosOpen("\\DEV\\SCREEN$",
                &h_screen,
                &action_taken,
                0L,        /* size */
                FILE_SYSTEM,
                OPEN_ACTION_OPEN_IF_EXISTS,
                OPEN_FLAGS_WRITE_THROUGH |
                OPEN_SHARE_DENYNONE      |
                OPEN_ACCESS_READWRITE,
                0L);


        DosDevIOCtl (NULL,
                     request_blk,
                     0x74,          /* ABIOS pass through */
                     0x03,          /* category 3         */
                     h_screen);

        DosClose(h_screen);


        if ( request_blk[0x10] & PLASMA_ENABLED )
        {
            /**********************************************************/
            /* the plasma is enabled: determine its type and return   */
            /* the appropriate display type number                    */
            /**********************************************************/
            return(DISPLAY_TYPE_PLASMA);
        }
    }

    /* not kauai or plasma disabled */
    return(DISPLAY_TYPE_NONE);

} /* plasma_display_type */


/************************************************************************/
/*                                                                      */
/*  SUBROUTINE NAME: DevEnable                                          */
/*                                                                      */
/*  DESCRIPTIVE NAME: Initialize call vector table                      */
/*                                                                      */
/*  FUNCTION: DevEnable is called via the Presentation Manager DDI      */
/*            interface.  The entry points of all VDH routines are      */
/*            appropriately entered into the call vector table.         */
/*            In addition, display adapter configuration is verified.   */
/*                                                                      */
/*  ENTRY POINT: DevEnable                                              */
/*    LINKAGE:   CALL FAR                                               */
/*                                                                      */
/*  INPUT: (Passed on stack)                                            */
/*             FAR * Parameter2  ( far pointer to parameter 2 packet )  */
/*                      FAR *Flags                                      */
/*                      FAR *CallVectorTable                            */
/*             FAR * Parameter1  ( far pointer to parameter 1 packet )  */
/*                      ULONG EngineVersion ( Graphics engine ver. )    */
/*                      ULONG TableSize ( Length of call table )        */
/*             ULONG Subfunction ( Enable subfunction )                 */
/*             FAR *CallVectorTable[] ( BVS's table of entry points )   */
/*         (Referenced)                                                 */
/*             VDHEntryPoint[] (global data - table of entry points )   */
/*                                                                      */
/*  EXIT-NORMAL: AX = 0                                                 */
/*               Entries in VDHEntryPoint table are copied to           */
/*                  CallVectorTable                                     */
/*                                                                      */
/*  EXIT-ERROR: AX = ERROR_VIO_BAD_ADAPTER or                           */
/*                   DosOpen error or                                   */
/*                   PMERR_DEV_FUNC_NOT_INSTALLED                       */
/*                                                                      */
/*  INTERNAL REFERENCES:                                                */
/*    ROUTINES: NONE                                                    */
/*                                                                      */
/*  EXTERNAL REFERENCES:                                                */
/*    ROUTINES: ExamineConfig                                           */
/*    DATA:                                                             */
/*              VDHEntryPoint[] (Global shared): table of entry points  */
/*                                                                      */
/************************************************************************/
USHORT EXPENTRY DEVENABLE( PDEV_PARM2 Parm2,
                           PDEV_PARM1 Parm1,
                           ULONG      Subfunction )
{
    /******************************************************************/
    /* Local variables                                                */
    /******************************************************************/
    VDH_CONFIG VGAParmBlock;
    USHORT ActionTaken;
    ULONG  i;
    register USHORT j;


    if ( !first_init )
    {
        /**************************************************************/
        /* We assume that we have been called again in case we have   */
        /* any process specific data to take care of.  As it 'appens  */
        /* we do, so we use this opportunity to do a DosGetSeg on it. */
        /*                                                            */
        /* There is a specific segment for each of the XGA instances  */
        /* which holds the DMQS file data.                            */
        /**************************************************************/
        for (j = 0; j < bNumberOfInstances; j++)
        {
            if (InstanceData[j].DMQS_Mon_File_Memory)
            {
                DosGetSeg(InstanceData[j].DMQS_Mon_File_Memory);
            }
        }

        /**************************************************************/
        /* Initialisation already done, just exit                     */
        /**************************************************************/
        return(NO_ERROR);
    }


    if ( ( Subfunction != FnFillInitDevBlock )  &&
         ( Subfunction != FnFillLogicalDevBlock) )
    {
        return(PMERR_DEV_FUNC_NOT_INSTALLED);
    }



#ifndef SM00035
    /******************************************************************/
    /* initialise the call table - we need to have InitEnv even if    */
    /* the xga driver is not yet installed.  Others pass through to   */
    /* vga                                                            */
    /******************************************************************/
    if ( need_table_init )
    {
        for ( i = FnTextBufferUpdate, j = FnChainedBufUpdate;
              (i <= FnGetLVBInfo) && (i < Parm1->TableSize);
              i++, j++ )
        {

            if ( VDHEntryPoint[j] != UNSUPPORTED_FUNCTION )
            {
                /******************************************************/
                /* Save a copy of the call vector table for chaining  */
                /******************************************************/
                ChainedCallVectorTable[j] =
                     (USHORT (APIENTRY *)())(Parm2->CallVectorTable[i]);

                /******************************************************/
                /* Change the current entry in the call vector table  */
                /******************************************************/
                Parm2->CallVectorTable[i] = (PVOID)ChainRouter;

            } /* supported function */
        } /* for all supported entry points */

        need_table_init = FALSE;

        Parm2->CallVectorTable[FnInitializeEnvironment] = (PVOID)InitEnv;
        Parm2->CallVectorTable[FnReturnConfigInfo] = (PVOID)RetConfigInfo;

        if ( DosGetHugeShift(&huge_shift) != NO_ERROR )
        {
            goto DEVENABLE_ERROR_EXIT;
        }
        huge_shift = 1 << huge_shift;

    } /* need table init */
#endif /* ndef SM00035 */

    /******************************************************************/
    /* The presence of the VGA VDH is determined by a successful      */
    /* call to RetConfigInfo in the call vector table                 */
    /******************************************************************/
    VGAParmBlock.Length        = sizeof( VDH_CONFIG );
    VGAParmBlock.Flags         = NONE;
    VGAParmBlock.ConfigDataPTR =(PVDHCONFIGINFO)&VGAConfigData;

    /******************************************************************/
    /* Only need display type and partial save size                   */
    /******************************************************************/
    VGAConfigData.cb = MinDLen_Config_PartSaveSz;

    vga_present = !ChainedVDHRetConfigInfo( (PENVIRONMENT)NULL,
                                   (PVDH_INITENV)&VGAParmBlock,
                                   FnReturnConfigInfo );

    /******************************************************************/
    /* Device driver detection: if DosOpen fails, it's not there and  */
    /* we need to wait until later                                    */
    /******************************************************************/
    if ( (NO_ERROR != DosOpen("\\DEV\\$$$XGA20",
                              &dev_handle,
                              &ActionTaken,
                              NO_SIZE,
                              FILE_SYSTEM,
                              OPEN_ACTION_OPEN_IF_EXISTS,
                              OPEN_FLAGS_WRITE_THROUGH |
                              OPEN_SHARE_DENYNONE |
                              OPEN_ACCESS_READWRITE,
                              RESERVED_LONG )) )
    {
        return(NO_ERROR);
    }

    first_init = FALSE;

#ifdef SM00035
    if ( DosGetHugeShift(&huge_shift) != NO_ERROR )
    {
        goto DEVENABLE_ERROR_EXIT;
    }
    huge_shift = 1 << huge_shift;


    /******************************************************************/
    /* Verify presence of adapter and record adapter configuration    */
    /* for later use by RetConfigInfo and RetROMFontInfo.             */
    /* We need the call vector to RetConfigInfo in the vga bvh (if    */
    /* it's there).  This vector must be accessible prior to the call */
    /* to ExamineConfig()                                             */
    /******************************************************************/
    ChainedCallVectorTable[FnChainedRetConfigInfo] =
       (USHORT (APIENTRY *)())(Parm2->CallVectorTable[FnReturnConfigInfo]);
#endif /* SM00035 */

    if ( ExamineConfig() != NO_ERROR )
    {
        goto DEVENABLE_ERROR_EXIT;
    }


#ifdef SM00035
    /******************************************************************/
    /* Copy all VDH entry points that are supported for this adapter  */
    /* to BVS's call table for this adapter                           */
    /******************************************************************/
    for ( i = FnTextBufferUpdate, j = FnChainedBufUpdate;
          (i <= FnGetLVBInfo) && (i < Parm1->TableSize);
          i++, j++ )
    {
        if (( VDHEntryPoint[j] != UNSUPPORTED_FUNCTION ) &&
            ( VDHEntryPoint[j] != InitEnv) &&
            ( VDHEntryPoint[j] != RetConfigInfo))
        {
            /**********************************************************/
            /* Save a copy of the call vector table for chaining      */
            /**********************************************************/
            ChainedCallVectorTable[j] =
                 (USHORT (APIENTRY *)())(Parm2->CallVectorTable[i]);

            /**********************************************************/
            /* Change the current entry in the call vector table      */
            /**********************************************************/
            Parm2->CallVectorTable[i] = VDHEntryPoint[j];
        } /* supported function */
    } /* for all supported entry points */
#else /* ndef SM00035 */
    /******************************************************************/
    /* Copy all VDH entry points that are supported for this adapter  */
    /* to BVS's call table for this adapter                           */
    /******************************************************************/
    for ( i = FnTextBufferUpdate, j = FnChainedBufUpdate;
          (i <= FnGetLVBInfo) && (i < Parm1->TableSize);
          i++, j++ )
    {
        if (( VDHEntryPoint[j] != UNSUPPORTED_FUNCTION ) &&
            ( VDHEntryPoint[j] != (PVOID)InitEnv) &&
            ( VDHEntryPoint[j] != (PVOID)RetConfigInfo))
        {
            /**********************************************************/
            /* Change the current entry in the call vector table      */
            /**********************************************************/
            Parm2->CallVectorTable[i] = VDHEntryPoint[j];
        } /* supported function */
    } /* for all supported entry points */
#endif /* ndef SM00035 */


    /******************************************************************/
    /* If we do not manage to find any XGA instances then we are      */
    /* stuffed!                                                       */
    /******************************************************************/
    SearchForXGAInstances();
    if (bNumberOfInstances == 0)
    {
        DosPutMessage( 1, 24, "No XGA adapters found!\n\r" );
        goto DEVENABLE_ERROR_EXIT;
    }
    WorkOutAvailableModes();
    if (ModesInfo.bNumberOfModes == 0)
    {
        DosPutMessage( 1, 32, "Did not recognise XGA monitor!\n\r" );
        goto DEVENABLE_ERROR_EXIT;
    }

    return(NO_ERROR);

DEVENABLE_ERROR_EXIT:
    return(PMERR_DEV_FUNC_NOT_INSTALLED);
} /* DEVENABLE */



/**********************************************************************/
/* This routine searches for XGA instances.                           */
/**********************************************************************/
VOID SearchForXGAInstances(VOID)
{
    USHORT            rc;
    USHORT            fn_id;
    BYTE              bSlot;
    BYTE              bInstance;


    bNumberOfInstances = 0;

    /******************************************************************/
    /* For each of the slots call the ring0 to see if it can find an  */
    /* XGA instance using the POS id and data.  (Note that slot 0 is  */
    /* the planar).                                                   */
    /******************************************************************/
    for (bSlot = 0; bSlot <= 8; bSlot++)
    {
        /**************************************************************/
        /* Call the ring0 with the blank instance data for it to fill */
        /* in.                                                        */
        /**************************************************************/
        InstanceData[bNumberOfInstances].slot_number = bSlot;
        InstanceData[bNumberOfInstances].XGA_level = 0;
        fn_id = IO_SETINSTANCEDATA;
        rc = DosDevIOCtl((PVOID)&InstanceData[bNumberOfInstances],
                         &fn_id,
                         GEN_FUNCTION,
                         XGA_CATEGORY,
                         dev_handle);

        /**************************************************************/
        /* Get the instance data back from the ring0.                 */
        /**************************************************************/
        fn_id = IO_GETINSTANCEDATA;
        rc = DosDevIOCtl((PVOID)&InstanceData[bNumberOfInstances],
                         &fn_id,
                         GEN_FUNCTION,
                         XGA_CATEGORY,
                         dev_handle);

        /**************************************************************/
        /* See if the ring0 found an XGA instance at this slot.       */
        /**************************************************************/
        if (InstanceData[bNumberOfInstances].XGA_level != 0)
        {
            /**********************************************************/
            /* The ring0 did find an instance.                        */
            /**********************************************************/
            bNumberOfInstances++;
        }
    }

    /******************************************************************/
    /* Before we return we want to clear all the DMQS_Mon_File_Memory */
    /* fields to indicate that we have not read in any DMQS monitor   */
    /* configuration file data.  All the other fields should be set   */
    /* up by now.                                                     */
    /******************************************************************/
    for (bInstance = 0; bInstance < bNumberOfInstances; bInstance++)
    {
        InstanceData[bInstance].DMQS_Mon_File_Memory = NULL;
    }
}


/**********************************************************************/
/* Adds a resolution mode to the table.                               */
/**********************************************************************/
VOID AddModeIfAvailable(PDMQS_MON_DATA_HEADER pModeData,
                        BYTE                  bInstance)
{
    /******************************************************************/
    /* At 1040x768 extraneous lines appear on the left of the screen. */
    /* Disable this mode to avoid the problem. Unknown if this is due */
    /* to hardware or software.                                       */
    /******************************************************************/
    if ( pModeData->usXPels == 1040 )
    {
      return;
    }

    /******************************************************************/
    /* For some reason the DMQS monitor data XGA level is in a        */
    /* different format to the DMQS primary data XGA level so we have */
    /* to do all this swapping around of the bytes...                 */
    /******************************************************************/
    if ( ( (pModeData->usMinXGALevel >>8)
                         <= (InstanceData[bInstance].XGA_level &0xFF) )
      && ( (pModeData->usMinXGALevel &0xFF)
                         <= (InstanceData[bInstance].XGA_level >>8) )
         && (ModesInfo.bNumberOfModes < MAX_NUM_DMQS_MODES) )
    {
        /**************************************************************/
        /* This XGA card supports this mode, so we should add it to   */
        /* the list of modes that we support.                         */
        /**************************************************************/
        ModesInfo.ModeData[ModesInfo.bNumberOfModes].
                            pDMQSModeData = pModeData;
        ModesInfo.ModeData[ModesInfo.bNumberOfModes].
                            usXPels = pModeData->usXPels;
        ModesInfo.ModeData[ModesInfo.bNumberOfModes].
                            usYPels = pModeData->usYPels;
        ModesInfo.ModeData[ModesInfo.bNumberOfModes].
                            bInstance = bInstance;
        ModesInfo.ModeData[ModesInfo.bNumberOfModes].
                            bSlot = InstanceData[bInstance].slot_number;

        /**************************************************************/
        /* Now work out the maximum bits per pel that the card can    */
        /* run at this resolution.                                    */
        /**************************************************************/
        if ( ((InstanceData[bInstance].XGA_level & 0xFF) >= 0x04) &&
             (pModeData->usModePixelRate <= MAX_16BPP_PIXEL_RATE) &&
             (InstanceData[bInstance].XGA_vram_size
                                    >= ((ULONG)pModeData->usXPels *
                                        (ULONG)pModeData->usYPels * 2)))
        {
            /**********************************************************/
            /* The XGA level is appropriate for 16bpp operation, this */
            /* is a low res mode, and there is enough VRAM on the     */
            /* card. Thus we can run at 16 bpp.                       */
            /**********************************************************/
            ModesInfo.ModeData[ModesInfo.bNumberOfModes].
                                                       bBitsPerPel = 16;
        }
        else if (InstanceData[bInstance].XGA_vram_size
                   >= ((ULONG)pModeData->usXPels *
                       (ULONG)pModeData->usYPels))
        {
            /**********************************************************/
            /* The XGA has enough memory for 8 bits per pel at this   */
            /* resolution.                                            */
            /**********************************************************/
            ModesInfo.ModeData[ModesInfo.bNumberOfModes].
                                                        bBitsPerPel = 8;
        }
        else if (InstanceData[bInstance].XGA_vram_size
                 >= ( (((ULONG)pModeData->usXPels + 1) / 2) *
                      (ULONG)pModeData->usYPels ))
        {
            /**********************************************************/
            /* The XGA has enough memory for 4 bits per pel at this   */
            /* resolution.                                            */
            /**********************************************************/
            ModesInfo.ModeData[ModesInfo.bNumberOfModes].
                                                        bBitsPerPel = 4;
        }
        else
        {
            /**********************************************************/
            /* The XGA does not have enough memory to run at this     */
            /* resolution!                                            */
            /**********************************************************/
            ModesInfo.bNumberOfModes--;
        }

        ModesInfo.bNumberOfModes++;
    }
}

/**********************************************************************/
/* Open the DMQS monitor file, and return its handle.                 */
/* Returns NULL if no file found.                                     */
/**********************************************************************/
HFILE GetDMQSMonitorFileHandle(PUCHAR puchMonitorID)
{
    HFILE   FileHandle;
    USHORT  i;
    USHORT  actionTaken;

    /******************************************************************/
    /* Add the DMQS momitor file name to the DMQS path.               */
    /******************************************************************/
    strcpy(&DMQS_path[DMQS_filename_offset], DMQS_DISPLAY_FILE);

    for ( i = 0; i < MAX_MON_ID; i++ )
    {
      if ( puchMonitorID[i] == '\0' )
      {
        break;
      }

      DMQS_path[DMQS_filename_offset + 3 + i] = puchMonitorID[i];
    }

    strcpy(&DMQS_path[DMQS_filename_offset + 3 + i], DMQS_DISPLAY_EXT);


    if ( DosOpen(DMQS_path,
                 (PHFILE)&FileHandle,
                 (PUSHORT)&actionTaken,
                 0L,
                 FILE_OPEN,
                 OPEN_ACTION_OPEN_IF_EXISTS,
                 OPEN_SHARE_DENYNONE | OPEN_ACCESS_READONLY,
                 0L ) == NO_ERROR )
    {
        /**********************************************************/
        /* Found the DMQS monitor file so return its handle.      */
        /**********************************************************/
        return(FileHandle);
    }

    /******************************************************************/
    /* Did not manage to find the DMQS monitor file.                  */
    /******************************************************************/
    return(NULL);
}

VOID NEAR get_DMQS_path(VOID)

/**********************************************************************/
/* Sets the DMQS_path global pointer to the path for DMQS files if    */
/* one exists, NULL otherwise.                                        */
/**********************************************************************/

{
  PSZ pszPath;

  /********************************************************************/
  /* Use the path specified by the DMQS environment variable, if      */
  /* defined.                                                         */
  /********************************************************************/
  if ( DosScanEnv(DMQS_ENVIRONMENT, &pszPath) == NO_ERROR )
  {
    /******************************************************************/
    /* Need to allocate memory for the path and filename string.      */
    /* We need the length of the path + 1 for the backslash + the     */
    /* maximum length of a filename + 1 for the NULL terminator.      */
    /******************************************************************/
    DMQS_filename_offset = strlen(pszPath) + 1;
    if ( DosAllocSeg(DMQS_filename_offset + MAX_FILENAME_LEN + 1,
                     &SELECTOROF(DMQS_path), 0) != NO_ERROR )
    {
      DMQS_path = NULL;
    }
    else
    {
      OFFSETOF(DMQS_path) = 0;
      strcpy(DMQS_path, pszPath);
      DMQS_path[DMQS_filename_offset - 1] = '\\';
    }
  }

  else /* environment variable not defined */
  {
    DMQS_path = DMQS_DIRECTORY;
    DMQS_filename_offset = DMQS_DIR_FILE;
  }

} /* get_DMQS_path */



HFILE NEAR open_override_file()

/**********************************************************************/
/* Opens the DMQS override file. Returns the file handle if           */
/* successfule, 0 if not.                                             */
/**********************************************************************/

{
  HFILE hfile;
  USHORT action_taken;

  /********************************************************************/
  /* Copy the override file name to the end of the DMQS path.         */
  /********************************************************************/
  strcpy(&DMQS_path[DMQS_filename_offset], DMQS_OVERRIDE_FILE);

  if ( DosOpen(DMQS_path,
               &hfile,
               &action_taken,
               0L,
               FILE_OPEN,
               OPEN_ACTION_OPEN_IF_EXISTS,
               OPEN_SHARE_DENYNONE | OPEN_ACCESS_READONLY,
               0L ) == NO_ERROR )
  {
    return(hfile);
  }
  else
  {
    return(0);
  }

} /* open_override_file */



VOID NEAR read_override_file(HFILE hfile, POVERRIDEBUFFER override)

/**********************************************************************/
/* Queries the size of the DMQS override file, allocates memory for   */
/* it and reads the entire file.                                      */
/**********************************************************************/

{
  ULONG  ulFileSize, ulBackPtr;
  PUCHAR file_data;
  USHORT bytes_read;

  /* Defect 61215 -  If we are in phase 1 of boot DosQFileInfo is  */
  /* not available.  Use the technique below to find the file size */
  if ( (DosChgFilePtr( hfile,
                       0L,                    /* Move 0 bytes from EOF */
                       2,                     /* Move to EOF */
                       &ulFileSize ) == NO_ERROR) &&
       (DosAllocSeg((USHORT)ulFileSize,
                    &SELECTOROF(file_data),
                    0) == NO_ERROR) )
  {
    OFFSETOF(file_data) = 0;

    /********************************************************/
    /* Reset the file pointer back to the start of the file */
    /********************************************************/
    DosChgFilePtr( hfile,
                   0L,            /* Move 0 bytes from top  */
                   0,             /* Move to file beginning */
                   &ulBackPtr );

    if ( DosRead(hfile,
                 file_data,
                 (USHORT)ulFileSize,
                 &bytes_read) == NO_ERROR )
    {
      override->length = (USHORT)ulFileSize;
      override->position = 0;
      override->data = file_data;
      return;
    }

  }

  /********************************************************************/
  /* error occurred                                                   */
  /********************************************************************/
  override->data = NULL;


} /* read_override_file */




VOID skip_white_space(POVERRIDEBUFFER override)

/**********************************************************************/
/* Advances override->position to point to the next override data     */
/* character which is not white space. The last character is not read */
/* so the pointer is still valid on exit from this function.          */
/**********************************************************************/

{
  PUCHAR or_data = override->data;
  register USHORT or_length = override->length - 1;
  register USHORT or_position = override->position;
  BOOL white_char = TRUE;

  while ( (or_position < or_length) && white_char )
  {
    switch ( or_data[or_position] )
    {
      case ' ':
      case '\t':
      case '\n':
      case '\r':
        or_position++;
        break;

      default:
        white_char = FALSE;
        break;
    }
  }

  override->position = or_position;

} /* skip_white_space */





BOOL fetch_number(POVERRIDEBUFFER override, PUSHORT pus)

/**********************************************************************/
/* Reads a single digit number from the override data, converting     */
/* from ASCII to the value.                                           */
/**********************************************************************/

{
  register USHORT value;

  skip_white_space(override);

  value = (USHORT)(override->data[override->position++] - '0');

  if ( value <= MAX_DMQS_SLOTS )
  {
    *pus = value;
    return(TRUE);
  }
  else
  {
    return(FALSE);
  }

} /* fetch_number */




BOOL fetch_id(POVERRIDEBUFFER override)

/**********************************************************************/
/* Reads a four digit monitor id from the override data, converting   */
/* from ASCII to a USHORT. Override data is base 16.                  */
/**********************************************************************/

{
  register USHORT i;

  skip_white_space(override);

  for ( i = 0; i < MAX_MON_ID; i++)
  {
    if ( override->position < override->length )
    {
      monitor_id_ascii[i] = override->data[override->position];
    }
    else
    {
      return(FALSE);
    }

    if ( monitor_id_ascii[i] == (UCHAR)END_TAG )
    {
      break;
    }

    override->position++;

  }

  if ( monitor_id_ascii[i] == (UCHAR)END_TAG )
  {
    monitor_id_ascii[i] = '\0';
  }
  else
  {
    monitor_id_ascii[i-1] = '\0';
  }

  return(TRUE);

} /* fetch_id */




USHORT next_token(POVERRIDEBUFFER override)

/**********************************************************************/
/* Searches for the next token in the override data and returns an    */
/* identifier for it.                                                 */
/**********************************************************************/

{
  USHORT token;
  USHORT i;
  register USHORT or_position;
  register USHORT or_length;
  PUCHAR or_data;
  BOOL match;

  or_length = override->length;
  or_data = override->data;

  token = 0;
  skip_white_space(override);

  for ( i = 0; DMQS_token_table[i] != '\0'; )
  {
    token++;
    or_position = override->position;

    /****************************************************************/
    /* Try to match the rest of the token.                          */
    /****************************************************************/
    while ( !(match = (DMQS_token_table[i] == '\0')) &&
            (or_position < or_length) &&
            (or_data[or_position++] == DMQS_token_table[i++]) );

    if ( match )
    {
      /**************************************************************/
      /* Got a match. Skip past the token in the override data.     */
      /**************************************************************/
      override->position = or_position;
      return(token);
    }

    else /* no match on this token table entry */
    {
      if ( or_position >= or_length )
      {
        override->position = or_length;
        return(DT_END_OF_DATA);
      }

      /**************************************************************/
      /* Skip to end of token                                       */
      /**************************************************************/
      while ( DMQS_token_table[i++] != '\0' );
    }

  } /* for each token table entry */

  /********************************************************************/
  /* No match in the token table for this character in the override   */
  /* data. Advance data pointer and return error.                     */
  /********************************************************************/
  override->position++;
  return(DT_ERROR);

} /* next_token */





VOID NEAR override_monitor_id(USHORT slot_number)

/**********************************************************************/
/* Finds the instance for slot_number and replaces its monitor id     */
/* field with monitor_id.                                             */
/**********************************************************************/

{
  register USHORT i;

  for ( i = 0; i < bNumberOfInstances ; i++)
  {
    if ( InstanceData[i].slot_number == (BYTE)slot_number )
    {
      strcpy(InstanceData[i].psz_monitor_id, monitor_id_ascii);
    }
  }

} /* override_monitor_id */





BOOL NEAR valid_startup_slot(USHORT slot_number)

/**********************************************************************/
/* Returns TRUE if slot_number has an XGA in it, FALSE otherwise.     */
/**********************************************************************/

{
  register USHORT i;

  for ( i = 0; i < bNumberOfInstances ; i++)
  {
    if ( InstanceData[i].slot_number == (BYTE)slot_number )
    {
      return(TRUE);
    }
  }

  return(FALSE);

} /* valid_startup_slot */





BOOL process_tag(POVERRIDEBUFFER override)

/**********************************************************************/
/* Interprets the tag currently pointed to in the override buffer.    */
/**********************************************************************/

{
  register USHORT override_state = STATE_BEGIN;
  register USHORT tag;
  USHORT slot_number;
  USHORT token;

  do
  {
    switch ( override_state )
    {
      case STATE_BEGIN:
        tag = next_token(override);
        switch ( tag )
        {
          case DT_SLOT:
          case DT_STARTUP:
            if ( next_token(override) == DT_PARAM_SEPARATOR )
            {
              override_state = STATE_NUMBER;
            }
            else
            {
              override_state = STATE_ERROR;
            }
            break;

          default:
            override_state = STATE_ERROR;
            break;
        }

        break;

      case STATE_END:
        if ( next_token(override) == DT_END_TAG )
        {
          override_state = STATE_COMPLETE;
        }
        else
        {
          override_state = STATE_ERROR;
        }

        break;

      case STATE_NUMBER:
        if ( (next_token(override) == DT_NUMBER) &&
             (next_token(override) == DT_PARAM_EQUATE) &&
             fetch_number(override, &slot_number) )
        {
          switch ( tag )
          {
            case DT_SLOT:
              if ( next_token(override) == DT_PARAM_SEPARATOR )
              {
                override_state = STATE_MONITOR_ID;
              }
              else
              {
                override_state = STATE_ERROR;
              }
              break;

            case DT_STARTUP:
              override_state = STATE_END;
              break;
          }
        }
        else
        {
          override_state = STATE_ERROR;
        }

        break;

      case STATE_MONITOR_ID:
        if ( (next_token(override) == DT_MONITOR_ID) &&
             (next_token(override) == DT_PARAM_EQUATE) &&
             fetch_id(override) )
        {
          override_state = STATE_END;
        }
        else
        {
          override_state = STATE_ERROR;
        }

        break;

      case STATE_ERROR:
        tag = DT_ERROR;

        do
        {
          token = next_token(override);
        } while ( (token != DT_END_OF_DATA) && (token != DT_END_TAG) );

        override_state = STATE_COMPLETE;
        break;

      default:
        override_state = STATE_ERROR;
        break;

    } /* switch ( override_state ) */

  } while ( override_state != STATE_COMPLETE );

  /******************************************************************/
  /* If the tag has been successfully processed, update our modes   */
  /* data to reflect the override.                                  */
  /******************************************************************/
  switch ( tag )
  {
    case DT_SLOT:
      override_monitor_id(slot_number);
      return(TRUE);

    case DT_STARTUP:
      if ( valid_startup_slot(slot_number) )
      {
        ModesInfo.primary_adapter_slot = slot_number;
      }
      return(TRUE);

    default:
      return(FALSE);
  }

} /* process_tag */




VOID NEAR find_begin_tag(POVERRIDEBUFFER override)

/**********************************************************************/
/* Finds the next occurrence of the begin tag in the DMQS override    */
/* file. Everything between the current position and the tag is       */
/* ignored.                                                           */
/**********************************************************************/

{
  while ( (override->position < override->length) &&
          (override->data[override->position++] != BEGIN_TAG) );


} /* find_begin_tag */



VOID NEAR process_override_file(HFILE hfile)

/**********************************************************************/
/* Reads the DMQS override file with handle hfile. Interprets the     */
/* data and records it.                                               */
/**********************************************************************/

{
  OVERRIDEBUFFER override;

  read_override_file(hfile, &override);

  if ( override.data != NULL )
  {
    find_begin_tag(&override);

    while ( override.position < override.length )
    {
      process_tag(&override);

      find_begin_tag(&override);
    }

    DosFreeSeg(SELECTOROF(override.data));
  }

} /* process_override_file */



VOID NEAR get_override_data(VOID)

/**********************************************************************/
/* Reads the DMQS override file, if it exists, records the primary    */
/* adapter slot in the ModeData and puts the forced monitor IDs       */
/* into the instance data.                                            */
/**********************************************************************/

{
  HFILE override_handle;

  override_handle = open_override_file();
  ModesInfo.primary_adapter_slot = NO_PRIMARY_ADAPTER_OVERRIDE;

  if ( override_handle != 0 )
  {
    process_override_file(override_handle);
    DosClose(override_handle);
  }

} /* get_override_data */





VOID NEAR get_iso_colour_data(PDMQS_MON_DATA_FILE   pModeDataFile,
                              ULONG                 ulInstance)

/**********************************************************************/
/* Takes the ISO colour information read in from the DMQS file and    */
/* inserts the compliant RGBs in the InstanceData for use by the PM   */
/* driver in constructing the palette.                                */
/**********************************************************************/

{
#define DMQS_EXTENSION_EXISTS (p_ext_data->Prefix.usLength != 0)
  register BOOL PM_colours_found = FALSE;
  PDMQSEXTENSIONDATA p_ext_data;

  /********************************************************************/
  /* Find the optional extension specifying the PM ISO compliant      */
  /* colours. If it does not exist, use a default selection (these    */
  /* are not defined officially and may not be compliant).            */
  /********************************************************************/
  p_ext_data = &pModeDataFile->Extension;

  for /* ever */ (; ; )
  {
    if ( DMQS_EXTENSION_EXISTS )
    {
      PM_colours_found = (p_ext_data->Prefix.usID == DMQS_EXT_ID_PM_ISO);
      if ( PM_colours_found )
      {
        break;
      }
      else
      {
        (PBYTE)p_ext_data += p_ext_data->Prefix.usLength +
                             sizeof(p_ext_data->Prefix.usLength);
      }
    }
    else
    {
      break;
    }

  } /* forever */


  if ( PM_colours_found )
  {
    register USHORT i;
    for ( i = DMQS_NUM_ISO_COLORS; i--; )
    {
      InstanceData[ulInstance].aISOColours[i].ul =
                                            DMQS_ISO_RGB(p_ext_data, i);
    }
  }
  else /* no colours extension - use defaults */
  {
    InstanceData[ulInstance].aISOColours[0].ul = XGA_BLACK;
    InstanceData[ulInstance].aISOColours[1].ul = XGA_WHITE;
    InstanceData[ulInstance].aISOColours[2].ul = XGA_RED;
    InstanceData[ulInstance].aISOColours[3].ul = XGA_BLUE;
    InstanceData[ulInstance].aISOColours[4].ul = XGA_GREEN;
    InstanceData[ulInstance].aISOColours[5].ul = XGA_CYAN;
    InstanceData[ulInstance].aISOColours[6].ul = XGA_MAGENTA;
    InstanceData[ulInstance].aISOColours[7].ul = XGA_YELLOW;
    InstanceData[ulInstance].aISOColours[8].ul = XGA_LIGHT_GRAY;
    InstanceData[ulInstance].aISOColours[9].ul = XGA_PURPLE;
    InstanceData[ulInstance].aISOColours[10].ul = XGA_MUSTARD;
  }

} /* get_iso_colour_data */







VOID NEAR setup_monitor_id(BYTE bInstance)
{

  USHORT i;
  USHORT j;
  USHORT usMonitorID;

  /******************************************************************/
  /* Convert MonID to 4 digit hex string.                           */
  /******************************************************************/
  usMonitorID = InstanceData[bInstance].XGA_monitor_id;
  for (i = 0; i < 4 ; i++ )
  {
    j = usMonitorID & 0x0F;
    usMonitorID >>= 4;

    if (j > 9)
    {
      InstanceData[bInstance].psz_monitor_id[3 - i] =
      DMQS_path[DMQS_filename_offset + 6 - i] = (UCHAR)('A' + j - 10);
    }

    else /* 0 - 9 */
    {
      InstanceData[bInstance].psz_monitor_id[3 - i] =
      DMQS_path[DMQS_filename_offset + 6 - i] = (UCHAR)('0' + j);
    }
  }

} /* setup_monitor_id */




/**********************************************************************/
/* Determine what modes the monitor can run in.                       */
/**********************************************************************/
VOID WorkOutAvailableModes(VOID)
{
    PDMQS_MON_DATA_HEADER pModeData;
    PDMQS_MON_DATA_HEADER pModeDataEnd;
    PDMQS_MON_DATA_FILE   pModeDataFile;
    ULONG   ulFileSize, ulBackPtr;
    BYTE    bInstance;
    HFILE   FileHandle;
    USHORT  usBytesRead;
    USHORT  rc;
    USHORT  fn_id;
    BOOL    no_mon_file_data;

    /******************************************************************/
    /* At the moment we do not have any DMQS monitor data files so we */
    /* must use our internal monitor information.                     */
    /******************************************************************/
    ModesInfo.bNumberOfModes = 0;

    pModeData = NULL;

    /******************************************************************/
    /* Clear the monitor ID fields.                                   */
    /******************************************************************/
    for (bInstance = 0; bInstance < bNumberOfInstances; bInstance++)
    {
      InstanceData[bInstance].psz_monitor_id[0] = '\0';
    }

    /******************************************************************/
    /* Get the path for DMQS files.                                   */
    /******************************************************************/
    get_DMQS_path();
    get_override_data();

    /******************************************************************/
    /* For each XGA instance we have found see which monitor we have  */
    /* attached and work out which modes we have available for that   */
    /* level of XGA card.                                             */
    /******************************************************************/
    for (bInstance = 0; bInstance < bNumberOfInstances; bInstance++)
    {
        /**************************************************************/
        /* We have found no monitor file yet.                         */
        /**************************************************************/
        no_mon_file_data = TRUE;

        /**************************************************************/
        /* If there is no override on the monitor ID then set up the  */
        /* ASCII version of the real ID.                              */
        /**************************************************************/
        if ( InstanceData[bInstance].psz_monitor_id[0] == '\0' )
        {
          setup_monitor_id(bInstance);
        }
        FileHandle = GetDMQSMonitorFileHandle(
                                InstanceData[bInstance].psz_monitor_id);

        if (FileHandle != NULL)
        {

            /**********************************************************/
            /* We found the DMQS monitor file ok. Query its size so   */
            /* we can allocate memory for us to read it into.         */
            /* Allocate a segment so we can read in the data.         */
            /*                                                        */
            /* We allocate the segment as shared, because we may have */
            /* to access the memory from any process.                 */
            /**********************************************************/

            /**********************************************************/
            /* Defect 61215 -  If we are in phase 1 of boot           */
            /* DosQFileInfo is not available.  Use the technique      */
            /* below to find the file size                            */
            /**********************************************************/
            if ( (DosChgFilePtr( FileHandle,
                                 0L,            /* Move 0 bytes from EOF */
                                 2,             /* Move to EOF */
                                 &ulFileSize ) == NO_ERROR) &&

                 (DosAllocSeg((USHORT)ulFileSize,
                              &SELECTOROF(pModeDataFile),
                              SEG_GETTABLE) == NO_ERROR) )
            {
              OFFSETOF(pModeDataFile) = 0;

              /********************************************************/
              /* Reset the file pointer back to the start of the file */
              /********************************************************/
              DosChgFilePtr( FileHandle,
                             0L,            /* Move 0 bytes from top  */
                             0,             /* Move to file beginning */
                             &ulBackPtr );

              /********************************************************/
              /* Read in the file.                                    */
              /********************************************************/
              if ( (DosRead(FileHandle,
                            pModeDataFile,
                            (USHORT)ulFileSize,
                            &usBytesRead) != NO_ERROR) ||
                   (usBytesRead != (USHORT)ulFileSize) )
              {
                DosFreeSeg(SELECTOROF(pModeDataFile));
              }

              else /* successful read */
              {
                no_mon_file_data = FALSE;
                InstanceData[bInstance].DMQS_Mon_File_Memory =
                                              SELECTOROF(pModeDataFile);
                /**********************************************************/
                /* We now have the monitor file data in memory. Loop      */
                /* through each of the monitor modes, adding the mode to  */
                /* our list of available modes if appropriate.            */
                /**********************************************************/
                pModeData = (PDMQS_MON_DATA_HEADER)
                                  ((PBYTE)pModeDataFile +
                                         pModeDataFile->MonitorDataOffset);
                pModeDataEnd = (PDMQS_MON_DATA_HEADER)
                  ( ((PBYTE)pModeDataFile) + pModeDataFile->usTotalLength );

                while(pModeData < pModeDataEnd)
                {
                    AddModeIfAvailable(pModeData, bInstance );

                    pModeData = (PDMQS_MON_DATA_HEADER)
                               ( ((PBYTE)pModeData) + pModeData->usLength );
                }

                /**********************************************************/
                /* Get the ISO colour information from the DMQS file data */
                /* and store the data in the InstanceData.                */
                /**********************************************************/
                get_iso_colour_data(pModeDataFile, (ULONG)bInstance);

                /**********************************************************/
                /* Set the adapter instance monitor related fields using  */
                /* the data from the monitor file.                        */
                /**********************************************************/
                InstanceData[bInstance].usMonitorTypeInstance =
                                               pModeDataFile->usMonitorType;
                InstanceData[bInstance].usScreenSizeX =
                                               pModeDataFile->usScreenWidth;
                InstanceData[bInstance].usScreenSizeY =
                                               pModeDataFile->usScreenHeight;

              } /* successful read */

            } /* got memory to read file */

        } /* FileHandle != NULL */

        if ( no_mon_file_data )
        {
            /**************************************************************/
            /* We only get here if we could not open the monitor file or  */
            /* failed to allocate memory to read it in (or something else */
            /* went wrong), so must use our internal default mode data.   */
            /**************************************************************/
            switch (InstanceData[bInstance].XGA_monitor_id)
            {
                case D_8517:
                case D_8515:
                case D_8514:
                case D_9518:
                    AddModeIfAvailable(
                            (PDMQS_MON_DATA_HEADER)&DMQSModeData_HiRes[0],
                            bInstance );

                    /******************************************************/
                    /* There is no break here, because all (current) high */
                    /* res monitors also support low res.                 */
                    /******************************************************/

                case D_8507_8604:
                case D_8512_8513:
                case D_8503:
                case D_9515:
                case D_9517:
                    AddModeIfAvailable(
                            (PDMQS_MON_DATA_HEADER)&DMQSModeData_LoRes[0],
                            bInstance );
                    break;

                default:
                    /******************************************************/
                    /* If we have not found any modes at all and we are   */
                    /* now looking at the first XGA adapter we found (ie  */
                    /* we have looked at all the other adapters already)  */
                    /* then set the monitor id to 8503 (mono lores).      */
                    /* This offers a worst case situation that will not   */
                    /* crash us, and is essential for P75 plasma          */
                    /* operation.                                         */
                    /******************************************************/
                    if (   (ModesInfo.bNumberOfModes == 0)
                        && (bInstance == 0) )
                    {
                        InstanceData[bInstance].XGA_monitor_id = D_8503;
                        AddModeIfAvailable(
                                (PDMQS_MON_DATA_HEADER)&DMQSModeData_LoRes[0],
                                bInstance );
                    }
                    break;
            }
            /**************************************************************/
            /* We must also try to set up the monitor related fields of   */
            /* the instance data.                                         */
            /**************************************************************/
            InstanceData[bInstance].usMonitorTypeInstance = MT_COLOR_CRT;
            switch (InstanceData[bInstance].XGA_monitor_id)
            {
                case D_8512_8513:
                case D_8503:
                    InstanceData[bInstance].usScreenSizeX = 240;
                    InstanceData[bInstance].usScreenSizeY = 180;
                    break;

                case D_8517:
                    InstanceData[bInstance].usScreenSizeX = 295;
                    InstanceData[bInstance].usScreenSizeY = 225;
                    break;

                case D_8515:
                case D_9515:
                case D_9517:
                    InstanceData[bInstance].usScreenSizeX = 250;
                    InstanceData[bInstance].usScreenSizeY = 187;
                    break;

                case D_8507_8604:
                    InstanceData[bInstance].usMonitorTypeInstance = MT_MONO_CRT;
                case D_8514:
                case D_9518:
                    InstanceData[bInstance].usScreenSizeX = 285;
                    InstanceData[bInstance].usScreenSizeY = 214;
                    break;
            }

        } /* no monitor file data */

        if (FileHandle != NULL)
        {
          DosClose(FileHandle);
        }

    } /* for all XGA instances */

    /******************************************************************/
    /* If we allocated memory for the DMQS path then free it here.    */
    /* (Offset is 0 is we alloced memory).                            */
    /******************************************************************/
    if ( (SELECTOROF(DMQS_path) != 0) &&
         (OFFSETOF(DMQS_path) == 0) )
    {
      DosFreeSeg(SELECTOROF(DMQS_path));
    }

    /******************************************************************/
    /* Transfer the mode information to the ring0.  (This will then   */
    /* be queried by the top level driver).                           */
    /******************************************************************/
    fn_id = IO_SETMODEINFO;
    rc = DosDevIOCtl((PVOID)&ModesInfo,
                     &fn_id,
                     GEN_FUNCTION,
                     XGA_CATEGORY,
                     dev_handle);
}


/**********************************************************************/
/* private setmode functions                                          */
/**********************************************************************/

BOOL pascal near its_native_mode ( USHORT    environment_present,
                                   PVDH_MODE parm_block,
                                   ULONG     function )
{  return(
       (function == FnSetMode) &&
       (parm_block->Flags  <= 1 ) &&
       (parm_block->ModeDataPTR->fbType & PARM_NATIVE_MODE ) &&
        environment_present );

} /* its_native_mode */





/**********************************************************************/
/* Search for a mode which matches the chosen one.                    */
/* If the same mode is avaliable on several cards, then we will chose */
/* the one with the latest XGA level, since this is likely to be      */
/* faster and better.                                                 */
/**********************************************************************/
BYTE pascal near SearchForGraphicsMode(PVIOMODEINFO pReqMode)
{
    USHORT fn_id;
    BYTE   bMode;
    BYTE   bFoundMode;
    BYTE   bOldNumberOfModes;
    BYTE   bInstance;

    bFoundMode = 0xFF;

    for (bMode = 0; bMode < ModesInfo.bNumberOfModes; bMode++)
    {
        if ( (ModesInfo.primary_adapter_slot !=
                                         NO_PRIMARY_ADAPTER_OVERRIDE) &&
             (ModesInfo.primary_adapter_slot != ModesInfo.ModeData[bMode].bSlot) )
        {
          continue;
        }

        if (   (pReqMode->hres == ModesInfo.ModeData[bMode].usXPels)
            && (pReqMode->vres == ModesInfo.ModeData[bMode].usYPels)
            && (pReqMode->color<= ModesInfo.ModeData[bMode].bBitsPerPel) )
        {
            /**********************************************************/
            /* Check to see if we already found a matching mode.      */
            /**********************************************************/
            if (bFoundMode != 0xFF)
            {
                /******************************************************/
                /* If the previously found mode was on the primary    */
                /* adapter (and the mode we just found is not on the  */
                /* primary adpater) then we must use the mode we have */
                /* just found.  (This will gives us dual screen       */
                /* support).  Otherwise we must choose between the    */
                /* modes by comparing the XGA level of the instances. */
                /* If the XGA levels are the same then we use them in */
                /* the order they are in the monitor file (ie the     */
                /* order in which we found them).                     */
                /******************************************************/
                if ( ( InstanceData[ModesInfo.ModeData[bFoundMode].
                                           bInstance].primary_adapter
                        && !InstanceData[ModesInfo.ModeData[bMode].
                                           bInstance].primary_adapter )
                  || ( InstanceData[ModesInfo.ModeData[bFoundMode].
                                           bInstance].XGA_level <
                         InstanceData[ModesInfo.ModeData[bMode].
                                           bInstance].XGA_level )  )
                {
                    bFoundMode = bMode;
                }
            }
            else
            {
                bFoundMode = bMode;
            }
        }
    }

    if (bFoundMode == 0xFF)
    {
        /**************************************************************/
        /* We did not find a mode that fitted the requested mode.     */
        /**************************************************************/
        return(bFoundMode);
    }

    /******************************************************************/
    /* If the number of instances is more than one then we want to    */
    /* discard all of the instances except for the one which will be  */
    /* used to do this mode. This also limits the number of modes     */
    /* that we can use in the future so we must remove modes from the */
    /* mode list.                                                     */
    /******************************************************************/
    if (bNumberOfInstances > 1)
    {
        for (bInstance = 0; bInstance < bNumberOfInstances; bInstance++)
        {
            if (   (bInstance != ModesInfo.ModeData[bFoundMode].bInstance)
                && (InstanceData[bInstance].DMQS_Mon_File_Memory != NULL) )
            {
                DosFreeSeg(InstanceData[bInstance].DMQS_Mon_File_Memory);
            }
        }
        InstanceData[0] = InstanceData[ModesInfo.ModeData[bFoundMode].bInstance];
        bNumberOfInstances = 1;

        bOldNumberOfModes = ModesInfo.bNumberOfModes;
        ModesInfo.bNumberOfModes = 0;
        for (bMode = 0; bMode < bOldNumberOfModes; bMode++)
        {
            if (ModesInfo.ModeData[bMode].bInstance
                            == ModesInfo.ModeData[bFoundMode].bInstance)
            {
                ModesInfo.ModeData[ModesInfo.bNumberOfModes]
                                            = ModesInfo.ModeData[bMode];
                if (bFoundMode == bMode)
                {
                    bFoundMode = ModesInfo.bNumberOfModes;
                }
                ModesInfo.bNumberOfModes++;
            }
        }
    }

    /******************************************************************/
    /* Make sure that the ring0 is using the instance we have chosen. */
    /******************************************************************/
    if (fFirstSetMode == TRUE)
    {
        fn_id = IO_SETINSTANCEDATA;
        DosDevIOCtl((PVOID)&InstanceData[0],
                         &fn_id,
                         GEN_FUNCTION,
                         XGA_CATEGORY,
                         dev_handle);
        fFirstSetMode = FALSE;
    }

    return(bFoundMode);

} /* SearchForGraphicsMode */





USHORT pascal near update_environment_buffer(PENVIRONMENT environment,
                                             PVIOMODEINFO pReqMode)
{
   /******************************************************/
   /* found a matching graphics mode -                   */
   /* update the enviroment buffer                       */
   /******************************************************/
   environment->ModeData.cb = MinDLen_Mode_Vres;
   environment->ModeData.fbType = pReqMode->fbType;
   environment->ModeData.color = pReqMode->color;
   environment->ModeData.col = pReqMode->col;
   environment->ModeData.row = pReqMode->row;
   environment->ModeData.hres = pReqMode->hres;
   environment->ModeData.vres = pReqMode->vres;
   environment->EnvFlags |= ENV_NATIVE_MODE;

   return(NO_ERROR);

} /* update_environment_buffer */





USHORT pascal near set_132_column_mode()
{
    USHORT fn_id;

    fn_id = IO_132_COL_MODE;

    /******************************************************************/
    /* put the coprocessor into 132 column mode                       */
    /******************************************************************/
    return( DosDevIOCtl(NULL,
                        &fn_id,
                        GEN_FUNCTION,
                        XGA_CATEGORY,
                        dev_handle) );

} /* set_132_column_mode */





/**********************************************************************/
/* Put the xga into vga mode providing                                */
/*   - it is not already in vga mode AND                              */
/*   - the xga is the primary adapter                                 */
/**********************************************************************/
USHORT pascal near set_xga_vga_mode()
{
    USHORT fn_id;

    if ( (xga_inp(xga_adapter.usIORegBase) > MODE_VGA_ADE) &&
         ((xga_adapter.usFlags & XGA_PRIMARY_ADAPTER) != 0) )
    {
        fn_id = IO_VGA_MODE;

        /**************************************************************/
        /* put the coprocessor into vga mode                          */
        /**************************************************************/
        return( DosDevIOCtl(NULL,
                            &fn_id,
                            GEN_FUNCTION,
                            XGA_CATEGORY,
                            dev_handle) );
    }
    else
    {
        /**************************************************************/
        /* already in vga mode                                        */
        /**************************************************************/
        return(NO_ERROR);
    }

} /* set_xga_vga_mode */





USHORT pascal near vga_set_mode(PENVIRONMENT Environment,
                                PVDH_MODE    ParmBlock)
{
    register USHORT rc;


    /******************************************************************/
    /* make sure the xga is in vga mode before calling the vga bvh    */
    /******************************************************************/
    if ( ParmBlock->Flags & UPDATE_HARDWARE )
    {
        PreSetMode();

        rc = set_xga_vga_mode();
        if ( rc == NO_ERROR )
        {
            rc = ChainedVDHSetMode(
                    (PENVIRONMENT)&Environment->VGAEnvironment,
                    ParmBlock,
                    FnSetMode);
        }

        PostSetMode();
    }
    else
    {
        rc = ChainedVDHSetMode(
                 (PENVIRONMENT)&Environment->VGAEnvironment,
                 ParmBlock,
                 FnSetMode);
    }


    return( rc );

} /* vga_set_mode */






USHORT pascal near set_text_vga_mode(PENVIRONMENT Environment, /*->SM00028,STJ*/
                                     PVDH_MODE    ParmBlock)

{
    register USHORT rc;
    PVIOMODEINFO      pReqMode;

    /******************************************************************/
    /* if 132 column mode and primary adapter then validate the       */
    /* number of lines                                                */
    /******************************************************************/
    pReqMode = ParmBlock->ModeDataPTR;



    if ( (pReqMode->col == 132) &&
         ((xga_adapter.usFlags & XGA_PRIMARY_ADAPTER) != 0) )
    {
/* 53028 Validation of video resolution here causes inconsistencies...
        if ( ((pReqMode->cb >= MinDLen_Mode_Row) &&
              (pReqMode->row > MAX_132_ROWS)) ||
             ((pReqMode->cb >= MinDLen_Mode_Vres) &&
              (pReqMode->vres > MAX_132_VRES)) )
        {
            return(ERROR_VIO_MODE);
        }
...and is best left to the video mode table look-up done by BVHVGA */
        /*            
        ** Chain the mode into bvhvga, but mark it first, as to allow bvhvga
        ** to differentiate between single XGA configuration and dual XGA/VGA
        ** for which the 132 mode is not supported. Note that the test for 
        ** XGA_PRIMARY means that in multiple XGA configurations, mode 132 on
        ** the secondary XGA's is not supported, for the reasons of not being
        ** able to target the specific instance in xga.sys. xga.sys will dedicate
        ** the instance running in vga mode to os/2 full screens and pass all the
        ** requests for xga modes to the one running in xga mode.
        ** The flag in parameters is checked and then reset by bvhvga.
        */
        ParmBlock->Flags |= MODE_132_XGA;         /*          */
    }
    else
    {
        if ( ParmBlock->Flags & UPDATE_HARDWARE )
        {
            rc = set_xga_vga_mode();
            if ( rc != NO_ERROR )
            {
                return( rc );
            }
        }
    }

    /******************************************************************/
    /* Pass the call onto the VGA BVH set mode                        */
    /******************************************************************/
    if ( (rc = vga_set_mode(Environment,
                            ParmBlock)) != NO_ERROR )
    {
        return( rc );
    }

    /******************************************************************/
    /* we've now set a vga mode so reset the native mode flag         */
    /******************************************************************/
    Environment->EnvFlags &= ~ENV_NATIVE_MODE;

    /******************************************************************/
    /* if 132 column mode then do 132 column xga specifics            */
    /******************************************************************/
    if ( (pReqMode->col == 132) &&
         ((xga_adapter.usFlags & XGA_PRIMARY_ADAPTER) != 0) )
    {
        if ( ParmBlock->Flags & UPDATE_HARDWARE )
        {
            rc = set_132_column_mode();
            /*           
            ** It is too late to return errors now, even if the last call failed.
            ** Hardware has already been affected by bvhvga and the first mode 
            ** set without the hardware flag returned successfuly if we got to this point.
            */
        }

        Environment->ModeData.col = 132;
    }
    else /* not 132 column mode */
    {
        Environment->ModeData.col = pReqMode->col;
    }
    return(NO_ERROR);

} /* set_text_vga_mode */               /*SM00028,STJ<-*/


/**********************************************************************/
/*                                                                    */
/*  SUBROUTINE NAME: BVHSetMode                                       */
/*                                                                    */
/*  DESCRIPTIVE NAME: Set video mode                                  */
/*                                                                    */
/*  FUNCTION: SetMode is called by BVS to set the video mode.         */
/*            If the request specifies hardware, the hardware and the */
/*            environment buffer, if passed, will be updated.         */
/*            Otherwise just the environment buffer, if passed, will  */
/*            be updated.                                             */
/*                                                                    */
/*  ENTRY POINT: BVHSetMode                                           */
/*    LINKAGE:   CALL FAR ( via BVS-DDI call vector table entry 272 ) */
/*                                                                    */
/*  INPUT: (Passed on stack)                                          */
/*             FAR *Environment ( Environment buffer for the session )*/
/*             FAR *ParmBlock                                         */
/*                     USHORT Length = length of this packet          */
/*                     USHORT Flags  = 0 - Environment buffer only    */
/*                                     1 - Hardware also              */
/*                     VIOMODEINFO FAR *ModeDataPTR                   */
/*             ULONG Function ( Call vector table entry = 272 )       */
/*         (Referenced)                                               */
/*                                                                    */
/*  EXIT-NORMAL: AX = 0                                               */
/*               Video mode is set                                    */
/*                                                                    */
/*  EXIT-ERROR: AX = ERROR_VIO_INVALID_PARMS                          */
/*                                                                    */
/*  INTERNAL REFERENCES:                                              */
/*    ROUTINES: NONE                                                  */
/*                                                                    */
/*  EXTERNAL REFERENCES:                                              */
/*    ROUTINES: SetHWMode                                             */
/*                                                                    */
/**********************************************************************/
USHORT EXPENTRY BVHSetMode( PENVIRONMENT Environment,
                            PVDH_MODE    ParmBlock,
                            ULONG        Function )
{
    /******************************************************************/
    /* Local variables                                                */
    /******************************************************************/
    USHORT            rc;
    PVIOMODEINFO      pReqMode;
    USHORT            fn_id;
    BYTE              bMode;
    MODEDATA          ThisMode;



    /******************************************************************/
    /* are we setting a native mode?                                  */
    /******************************************************************/
    if ( its_native_mode(SELECTOROF(Environment), ParmBlock, Function) )
    {
        /**************************************************************/
        /* Check for valid mode length                                */
        /**************************************************************/
        if ( ParmBlock->Length < sizeof(VDH_MODE) )
        {
           return(ERROR_VIO_INVALID_LENGTH);
        }

        /**************************************************************/
        /* Extract a pointer to the mode data from the parm block.    */
        /**************************************************************/
        pReqMode = ParmBlock->ModeDataPTR;

        /**************************************************************/
        /* Check the flags field for validity                         */
        /**************************************************************/
        if (pReqMode->fbType & (~VALID_TYPE_FLAGS))
        {
            return(ERROR_VIO_MODE);
        }

        /**************************************************************/
        /* Check for valid packet length - for a graphics mode we     */
        /* need at least the vertical resolution                      */
        /**************************************************************/
        if ( pReqMode->cb < MinDLen_Mode_Vres )
        {
            return(ERROR_VIO_INVALID_LENGTH);
        }

#ifdef OMIT
/**********************************************************************/
/* MEB. I have omitted this because although in practice it seemed to */
/*      work, the theory (that of doing the work once at enable time  */
/*      and then again later) is not very good.  More importantly     */
/*      defect 57110 was still found, even with this      in.         */
/*                                                                    */
/*      Thus I re-architected this to do the work just once, and to   */
/*      use shared memory, so that the memory is still around, even   */
/*      when the sysinit process goes away.                           */
/**********************************************************************/
        /**************************************************************/
        /* Get access to the segment holding the DMQS mode setting    */
        /* data.  This is necessary since at DevEnable time the BVH   */
        /* is called by the sysinit process - this has ended by this  */
        /* time and its memory has gone with it!                      */
        /**************************************************************/
        if ( !DMQS_seg_access )
        {
            WorkOutAvailableModes();
            DMQS_seg_access = TRUE;
        }
#endif /* OMIT */

        /**************************************************************/
        /* Can we find this graphics mode?                            */
        /**************************************************************/
        bMode = SearchForGraphicsMode(pReqMode);

        if ( bMode != 0xff )
        {
            /**********************************************************/
            /* found a matching graphics mode. Put into native mode   */
            /* and update the environment                             */
            /**********************************************************/
            if ( ParmBlock->Flags & UPDATE_HARDWARE )
            {
                /******************************************************/
                /* Get a copy of the mode data needed to switch the   */
                /* adapter into the desired mode.  We must adjust the */
                /* bits per pel because the ModeData maybe for a      */
                /* higher bits pel support.  (This means it must also */
                /* support our desired bits per pel).                 */
                /******************************************************/
                ThisMode = ModesInfo.ModeData[bMode];
                ThisMode.bBitsPerPel = pReqMode->color;

                fn_id = IO_NATIVE_MODE;
                rc = DosDevIOCtl(&ThisMode,
                                 &fn_id,
                                 GEN_FUNCTION,
                                 XGA_CATEGORY,
                                 dev_handle);

                if ( rc != NO_ERROR )
                {
                    return(rc);
                }

                /******************************************************/
                /* Now we have done a mode set we must query the      */
                /* adapter configuration for other parts of the BVH   */
                /* to use.  (The mode set will select which adapter   */
                /* we are using).                                     */
                /******************************************************/
                ExamineConfig();
            }

            return(update_environment_buffer(Environment, pReqMode));

        } /* graphics mode found */

        else /* no extended graphics mode found */
        {
            return(ERROR_VIO_MODE);
        }
    } /* set a native mode */


    /******************************************************************/
    /* a vga mode or native text mode requested - look for a vga to   */
    /* pass call to                                                   */
    /******************************************************************/
    if ( vga_present )
    {
        return( set_text_vga_mode(Environment,
                                  ParmBlock) );
    }
    else /* no vga */
    {
        return(ERROR_VIO_MODE);
    }

} /* BVHSetMode */






/**********************************************************************/
/*                                                                    */
/*  SUBROUTINE NAME: BVHGetMode                                       */
/*                                                                    */
/*  DESCRIPTIVE NAME: Get current video mode setting                  */
/*                                                                    */
/*  FUNCTION: GetMode is called by BVS to get the current mode setting*/
/*            If the request specifies hardware and the hardware is   */
/*            readable, the actual hardware setting will be read and  */
/*            returned.  Otherwise the returned information will be   */
/*            taken from the environment buffer, if it has been passed*/
/*                                                                    */
/*  ENTRY POINT: BVHGetMode                                           */
/*    LINKAGE:   CALL FAR ( via BVS-DDI call vector table entry 271 ) */
/*                                                                    */
/*  INPUT: (Passed on stack)                                          */
/*             FAR *Environment ( Environment buffer for the session )*/
/*             FAR *ParmBlock                                         */
/*                     USHORT Length = length of this packet          */
/*                     USHORT Flags  = 0 - Environment buffer only    */
/*                                     1 - Hardware also              */
/*                     VIOMODEINFO FAR *ModeDataPTR                   */
/*             ULONG Function ( Call vector table entry = 271 )       */
/*         (Referenced)                                               */
/*                                                                    */
/*  EXIT-NORMAL: AX = 0                                               */
/*               Current mode setting is returned                     */
/*                                                                    */
/*  EXIT-ERROR: AX = ERROR_VIO_INVALID_PARMS                          */
/*                                                                    */
/*  EFFECTS: If hardware specified and hardware is readable, the      */
/*           environment buffer is updated, if passed.                */
/*           If the length of ModeData does not exactly fit a         */
/*           parameter, the length is adjusted and returned.          */
/*                                                                    */
/*  INTERNAL REFERENCES:                                              */
/*    ROUTINES: NONE                                                  */
/*                                                                    */
/*  EXTERNAL REFERENCES:                                              */
/*    ROUTINES: SaveRestoreHW, SetEnvMode                             */
/*                                                                    */
/**********************************************************************/
USHORT EXPENTRY BVHGetMode( PENVIRONMENT Environment,
                            PVDH_MODE    ParmBlock,
                            ULONG        Function )
{
    /******************************************************************/
    /* Local variables                                                */
    /******************************************************************/
    PVIOMODEINFO      pReqMode;
    register USHORT   ReqModeLen;
    register USHORT   copy_size;


    /******************************************************************/
    /* If the current mode in the environment buffer is not XGA       */
    /* native mode, pass the call on to the vga                       */
    /******************************************************************/
    if ( (Environment->EnvFlags & ENV_NATIVE_MODE) == 0 )
    {
        if ( vga_present )
        {
            return( ChainedVDHGetMode(
                     (PENVIRONMENT)&Environment->VGAEnvironment,
                                   ParmBlock,
                                   Function) );
        }

        /**************************************************************/
        /* no vga present and not native mode so error exit           */
        /**************************************************************/
        return(ERROR_VIO_MODE);

    } /* not native mode */


    /******************************************************************/
    /* validate the parameters                                        */
    /******************************************************************/
    if ( ( Function != FnGetMode ) ||
         ( ParmBlock->Length < sizeof( VDH_MODE ) ) ||
         ( ParmBlock->Flags > 1 ) ||
         ( !SELECTOROF(Environment) ) )
    {
        return(ERROR_VIO_INVALID_PARMS);
    }

    /******************************************************************/
    /* validate the length of the mode data - must at least ask for   */
    /* the buffer length                                              */
    /******************************************************************/
    ReqModeLen = (pReqMode = ParmBlock->ModeDataPTR)->cb;
    if ( ReqModeLen < Mode_Return_Length )
    {
        return(ERROR_VIO_INVALID_LENGTH);
    }

    /******************************************************************/
    /* if only the buffer length is needed then we can fill that in   */
    /* and exit                                                       */
    /******************************************************************/
    if ( ReqModeLen == Mode_Return_Length )
    {
        pReqMode->cb = sizeof( VIOMODEINFO );
        return(NO_ERROR);
    }

    /******************************************************************/
    /* XGA native mode:                                               */
    /* The amount of data to be copied never exceeds that up to the   */
    /* vertical resolution                                            */
    /******************************************************************/
    copy_size = min(ReqModeLen, MinDLen_Mode_Vres);
    ReqModeLen = 0;

    /******************************************************************/
    /* copy as much data as was requested and set up the appropriate  */
    /* data buffer length                                             */
    /******************************************************************/
    switch ( copy_size )
    {
        case MinDLen_Mode_Vres:
            pReqMode->vres = Environment->ModeData.vres;
            ReqModeLen += MinDLen_Mode_Vres - MinDLen_Mode_Hres;

        case MinDLen_Mode_Hres:
        case MinDLen_Mode_Hres + 1:
            pReqMode->hres = Environment->ModeData.hres;
            ReqModeLen += MinDLen_Mode_Hres - MinDLen_Mode_Row;

        case MinDLen_Mode_Row:
        case MinDLen_Mode_Row + 1:
            pReqMode->row = Environment->ModeData.row;
            ReqModeLen += MinDLen_Mode_Row - MinDLen_Mode_Column;

        case MinDLen_Mode_Column:
        case MinDLen_Mode_Column + 1:
            pReqMode->col = Environment->ModeData.col;
            ReqModeLen += MinDLen_Mode_Column - MinDLen_Mode_Color;

        case MinDLen_Mode_Color:
        case MinDLen_Mode_Color + 1:
            pReqMode->color = Environment->ModeData.color;
            ReqModeLen += MinDLen_Mode_Color - MinDLen_Mode_Type;

        case MinDLen_Mode_Type:
            pReqMode->fbType = Environment->ModeData.fbType;
            ReqModeLen += MinDLen_Mode_Type;

    } /* switch copy_size */

    pReqMode->cb = ReqModeLen;
    return( NO_ERROR );

} /* BVHGetMode */





/************************************************************************/
/*                                                                      */
/*  SUBROUTINE NAME: ChainRouter                                        */
/*                                                                      */
/*  DESCRIPTIVE NAME: Call default entry in call vector table           */
/*                                                                      */
/*  FUNCTION: During VDH chaining, the environment buffer of the        */
/*            previous call table entry is tacked on to the end of the  */
/*            current VDH environment buffer.  If a particular VDH      */
/*            service is not supported, the VDH has to call the         */
/*            previous entry with its environment.                      */
/*                                                                      */
/*  ENTRY POINT: UnsupportedService                                     */
/*    LINKAGE:   CALL FAR                                               */
/*                                                                      */
/*  INPUT: (Passed on stack)                                            */
/*             FAR *Environment ( Environment buffer for the session )  */
/*             FAR *ParmBlock                                           */
/*             ULONG Function ( Call vector table entry )               */
/*                                                                      */
/*  EXIT:      AX = return code from call table vector routine          */
/*                                                                      */
/*  EFFECTS: NONE                                                       */
/*                                                                      */
/*  INTERNAL REFERENCES:                                                */
/*    ROUTINES: NONE                                                    */
/*                                                                      */
/*  EXTERNAL REFERENCES:                                                */
/*    ROUTINES: NONE                                                    */
/*                                                                      */
/************************************************************************/
USHORT EXPENTRY ChainRouter( PENVIRONMENT Environment,
                             PCHAR        ParmBlock,
                             ULONG        Function )
{
    return((*ChainedCallVectorTable[ Function - FnTextBufferUpdate ])(
                                 (char far *)&Environment->VGAEnvironment,
                                 ParmBlock,
                                 Function) );

} /* ChainRouter */




/************************************************************************/
/*                                                                      */
/*  SUBROUTINE NAME: ExamineConfig                                      */
/*                                                                      */
/*  DESCRIPTIVE NAME: Examine display adapter configuration             */
/*                                                                      */
/*  FUNCTION: ExamineConfig is called by DevInit in order to verify     */
/*            the presence of the appropriate display adapter.          */
/*            In addition, info is obtained for later use by            */
/*            RetConfigInfo.  Starting addresses for ROM fonts are      */
/*            obtained.                                                 */
/*                                                                      */
/*  ENTRY POINT: ExamineConfig                                          */
/*    LINKAGE:   CALL FAR                                               */
/*                                                                      */
/*  INPUT: (Passed on stack)                                            */
/*             NONE                                                     */
/*         (Referenced)                                                 */
/*             MonitorTable[] (global data - table of monitor types )   */
/*                                                                      */
/*  EXIT-NORMAL: AX = 0                                                 */
/*                                                                      */
/*  EXIT-ERROR: AX = ERROR_VIO_BAD_ADAPTER or                           */
/*                   DosDevConfig error                                 */
/*                                                                      */
/*  EFFECTS: NONE                                                       */
/*                                                                      */
/*  INTERNAL REFERENCES:                                                */
/*    ROUTINES: PhysToUVirt, FreePhysToUVirt                            */
/*                                                                      */
/*  EXTERNAL REFERENCES:                                                */
/*    ROUTINES: DosDevConfig, CharFontBegin, CharFontEnd2               */
/*                                                                      */
/************************************************************************/
SHORT ExamineConfig(VOID)
{
    VDH_CONFIG VGAParmBlock;
    register USHORT rc;
    USHORT          fn_id;

    /******************************************************************/
    /* Verify presence of the adapter and determine amount of display */
    /* memory.  Sets up xga_adapter global structure                  */
    /******************************************************************/
    fn_id = BVH_CONFIG;
    rc = DosDevIOCtl(&xga_adapter,
                     &fn_id,
                     GEN_FUNCTION,
                     XGA_CATEGORY,
                     dev_handle);
    if ( rc != NO_ERROR )
    {
        return(rc);
    }


    /******************************************************************/
    /* check if we have a plasma display attached                     */
    /******************************************************************/
    rc = plasma_display_type();
    if ( rc != DISPLAY_TYPE_NONE )
    {
        xga_adapter.usDisplayType = rc;
    }
    else /* no plasma display */
    {
        /**************************************************************/
        /* We must now change the format of the display type from the */
        /* value read from the hardware to the VDH format             */
        /**************************************************************/
        switch (xga_adapter.usDisplayType)
        {
            case D_9515:
                xga_adapter.usDisplayType = 16;
                break;

            case D_9517:
                xga_adapter.usDisplayType = 17;
                break;

            case D_9518:
                xga_adapter.usDisplayType = 18;
                break;

            case D_8507_8604:
                xga_adapter.usDisplayType = 11;
                break;

            case D_8517:
            case D_8515:
            case D_8514:
                xga_adapter.usDisplayType = 9;
                break;

            case D_8503:
                xga_adapter.usDisplayType = 3;
                break;

            case D_8512_8513:
            default:
                xga_adapter.usDisplayType = 4;
                break;
        }
    }

    /******************************************************************/
    /* The presence of the VGA VDH is determined by a successful call */
    /* to RetConfigInfo in the call vector table                      */
    /******************************************************************/
    VGAParmBlock.Length        = sizeof( VDH_CONFIG );
    VGAParmBlock.Flags         = NONE;
    VGAParmBlock.ConfigDataPTR =(PVDHCONFIGINFO)&VGAConfigData;

    /******************************************************************/
    /* Only need display type and partial save size                   */
    /******************************************************************/
    VGAConfigData.cb = MinDLen_Config_PartSaveSz;

    vga_present = !ChainedVDHRetConfigInfo( (PENVIRONMENT)NULL,
                                   (PVDH_INITENV)&VGAParmBlock,
                                   FnReturnConfigInfo );

    /******************************************************************/
    /* Initialize values used in RetConfigInfo and GetMode            */
    /*                                                                */
    /* If power-up display, partial save size =                       */
    /*       80x25x2 + maximum font length                            */
    /* If not power-up display, partial save size =                   */
    /*       ( never a popup )                                        */
    /******************************************************************/
    PartialSaveSize = vga_present ? VGAConfigData.PartSaveSize +
                                    VGADisplaySize
                                  : UNKNOWN;

    return( NO_ERROR );

} /* ExamineConfig */




/************************************************************************/
/*                                                                      */
/*  SUBROUTINE NAME: InitEnv                                            */
/*                                                                      */
/*  DESCRIPTIVE NAME: Initialize environment                            */
/*                                                                      */
/*  FUNCTION: InitEnv is called by BVS to initialize the video          */
/*            environment during the creation of a new session.  This   */
/*            includes initializing the adapter hardware and/or the     */
/*            environment buffer.                                       */
/*                                                                      */
/*  ENTRY POINT: InitEnv                                                */
/*    LINKAGE:   CALL FAR ( via BVS-DDI call vector table entry 257 )   */
/*                                                                      */
/*  INPUT: (Passed on stack)                                            */
/*             FAR *Environment ( Environment buffer for the session )  */
/*             FAR *ParmBlock                                           */
/*                     USHORT Length = length of this packet            */
/*                     USHORT Flags  = 0 - Environment buffer only      */
/*                                     1 - Hardware also                */
/*             ULONG Function ( Call vector table entry = 257 )         */
/*         (Referenced)                                                 */
/*                                                                      */
/*  EXIT-NORMAL: AX = 0                                                 */
/*               Environment buffer is initialized                      */
/*                                                                      */
/*  EXIT-ERROR: AX = ERROR_VIO_INVALID_PARMS                            */
/*                                                                      */
/*  EFFECTS: If requested, display adapter hardware is initialized to   */
/*           highest resolution mode supported.                         */
/*                                                                      */
/*  INTERNAL REFERENCES:                                                */
/*    ROUTINES: NONE                                                    */
/*                                                                      */
/************************************************************************/
USHORT EXPENTRY InitEnv( PENVIRONMENT Environment,
                         PVDH_INITENV ParmBlock,
                         ULONG        Function )
{
    register USHORT rc;

    /******************************************************************/
    /* validate parameters                                            */
    /******************************************************************/
    if ( ( Function != FnInitializeEnvironment )        ||
         ( ParmBlock->Length < sizeof( VDH_INITENV ) )  ||
         ( ParmBlock->Flags  > VALID_INITENV_FLAG )     ||
         !SELECTOROF( Environment ) )
    {
        return(ERROR_VIO_INVALID_PARMS);
    }


    /******************************************************************/
    /* we have valid parameters                                       */
    /* check if this is a DOS session and record in the environment   */
    /******************************************************************/
    Environment->EnvFlags = ParmBlock->Flags & INITENV_3xBOX;

    /******************************************************************/
    /* if it's a dos session we'll need the huge shift for saving     */
    /* vram                                                           */
    /******************************************************************/
    Environment->state_buffer.VRAM.huge_shift = huge_shift;

    /******************************************************************/
    /* If VDHVGA.DLL has been successfully installed, call InitEnv in */
    /* VDHVGA.DLL                                                     */
    /******************************************************************/
    if ( vga_present )
    {
        /**************************************************************/
        /* Turn off the display so we don't get any garbage.          */
        /**************************************************************/
        rc = NO_ERROR;
        if ( ParmBlock->Flags & UPDATE_HARDWARE )
        {
            PreSetMode();
            rc = set_xga_vga_mode();
        }

        if (rc == NO_ERROR)
        {
            rc = ChainedVDHInitEnv( (PENVIRONMENT)&Environment->
                                    VGAEnvironment,
                                    ParmBlock,
                                    Function );
        }

        /**************************************************************/
        /* Wait for 5 frames and then restore the PEL_MASK register   */
        /* (to avoid displaying garbage).                             */
        /**************************************************************/
        if ( ParmBlock->Flags & UPDATE_HARDWARE )
        {
            PostSetMode();
        }

        return( rc );

    } /* vga present */


    /******************************************************************/
    /* no vga present - set native mode                               */
    /******************************************************************/
    Environment->EnvFlags |= ENV_NATIVE_MODE;

    return(NO_ERROR);

} /* InitEnv */





/************************************************************************/
/*                                                                      */
/*  SUBROUTINE NAME: RetConfigInfo                                      */
/*                                                                      */
/*  DESCRIPTIVE NAME: Return video adapter configuration information    */
/*                                                                      */
/*  FUNCTION: RetConfigInfo is called by BVS to identify the current    */
/*            display adapter.                                          */
/*                                                                      */
/*  ENTRY POINT: RetConfigInfo                                          */
/*    LINKAGE:   CALL FAR ( via BVS-DDI call vector table entry 260 )   */
/*                                                                      */
/*  INPUT: (Passed on stack)                                            */
/*             FAR *Environment ( Environment buffer for the session )  */
/*             FAR *ParmBlock                                           */
/*                     USHORT Length = length of this packet            */
/*                     USHORT Flags  = 0 ( reserved )                   */
/*                     FAR *ConfigData = VioGetConfig structure         */
/*             ULONG Function ( Call vector table entry = 260 )         */
/*         (Referenced)                                                 */
/*             MemorySize (global data - amount of video memory)        */
/*                                                                      */
/*  EXIT-NORMAL: AX = 0                                                 */
/*               Configuration data is returned to caller               */
/*                                                                      */
/*  EXIT-ERROR: AX = ERROR_VIO_INVALID_PARMS                            */
/*                                                                      */
/*  EFFECTS: If the length of ConfigData does not exactly fit a         */
/*           parameter, the length is adjusted and returned.            */
/*                                                                      */
/*  INTERNAL REFERENCES:                                                */
/*    ROUTINES: NONE                                                    */
/*                                                                      */
/*  EXTERNAL REFERENCES:                                                */
/*    ROUTINES: NONE                                                    */
/*                                                                      */
/************************************************************************/
USHORT EXPENTRY RetConfigInfo( PENVIRONMENT Environment,
                               PVDH_CONFIG  ParmBlock,
                               ULONG        Function )
{
    PVDHCONFIGINFO  pReqConfig;
    register USHORT ReqConfigLen;
    register USHORT copy_size;


    if ( ( Function != FnReturnConfigInfo )            ||
         ( ParmBlock->Length < sizeof( VDH_CONFIG ) ) ||
         ( ParmBlock->Flags ) )
    {
        return(ERROR_VIO_INVALID_PARMS);
    }


    /******************************************************************/
    /* get the length of the config data asked for - if just the      */
    /* length of the buffer is asked for, set the field and return    */
    /* immediately                                                    */
    /******************************************************************/
    ReqConfigLen = (pReqConfig = ParmBlock->ConfigDataPTR)->cb;

    if ( ReqConfigLen == Config_Return_Length )
    {
        pReqConfig->cb = sizeof( VDHCONFIGINFO );
        return(NO_ERROR);
    } /* only buffer length asked for */


    /******************************************************************/
    /* if some length of data is asked for other than just the buffer */
    /* size then at least up to the adapter field must be requested   */
    /******************************************************************/
    if ( ReqConfigLen < MinDLen_Config_Adapter )
    {
        return(ERROR_VIO_INVALID_LENGTH);
    }


    /******************************************************************/
    /* get the amount of data to copy to the user buffer: this cannot */
    /* exceed the amount required to copy up to the EMDisplay info    */
    /******************************************************************/
    copy_size = min( ReqConfigLen, MinDLen_Config_EMDisplay);
    ReqConfigLen = 0;
    switch ( copy_size )
    {
        case MinDLen_Config_EMDisplay:
            pReqConfig->EMDisplaysOFF =
                  OFFSET( pReqConfig->LEMDisplaydata ) -
                  OFFSET( pReqConfig->cb );

            pReqConfig->LEMDisplaydata = 1;
            pReqConfig->EMDisplays = EmDisp_MPA |
                                     EmDisp_CGA |
                                     EmDisp_EGA |
                                     EmDisp_VGA |
                                     EmDisp_8514;

            ReqConfigLen += MinDLen_Config_EMDisplay -
                                                  MinDLen_Config_EMAdapter;

        case MinDLen_Config_EMAdapter + 3:
        case MinDLen_Config_EMAdapter + 2:
        case MinDLen_Config_EMAdapter + 1:
        case MinDLen_Config_EMAdapter:
            pReqConfig->EMAdaptersOFF =
                      OFFSET( pReqConfig->LEMAdapterdata ) -
                      OFFSET( pReqConfig->cb );

            pReqConfig->LEMAdapterdata = 1;
            pReqConfig->EMAdapters =
                         EMULATE_TYPE_8514 |
                         ( !vga_present ? 0
                                        : EMULATE_TYPE_MPA |
                                          EMULATE_TYPE_CGA |
                                          EMULATE_TYPE_EGA |
                                          EMULATE_TYPE_VGA );
            ReqConfigLen += MinDLen_Config_EMAdapter -
                                              MinDLen_Config_PartSaveSz;

        case MinDLen_Config_PartSaveSz + 7:
        case MinDLen_Config_PartSaveSz + 6:
        case MinDLen_Config_PartSaveSz + 5:
        case MinDLen_Config_PartSaveSz + 4:
        case MinDLen_Config_PartSaveSz + 3:
        case MinDLen_Config_PartSaveSz + 2:
        case MinDLen_Config_PartSaveSz + 1:
        case MinDLen_Config_PartSaveSz:
            pReqConfig->PartSaveSize = PartialSaveSize;
            ReqConfigLen += MinDLen_Config_PartSaveSz -
                                              MinDLen_Config_FullSaveSz;

        case MinDLen_Config_FullSaveSz + 3:
        case MinDLen_Config_FullSaveSz + 2:
        case MinDLen_Config_FullSaveSz + 1:
        case MinDLen_Config_FullSaveSz:
            pReqConfig->FullSaveSize = vga_present ?
                                       VGAConfigData.FullSaveSize :
                                       UNKNOWN;
            ReqConfigLen += MinDLen_Config_FullSaveSz -
                                                  MinDLen_Config_HWBufLen;

        case MinDLen_Config_HWBufLen + 3:
        case MinDLen_Config_HWBufLen + 2:
        case MinDLen_Config_HWBufLen + 1:
        case MinDLen_Config_HWBufLen:
            /**********************************************************/
            /* If the BVHVGA is there, allocate enough room to tack   */
            /* its environment buffer onto the end of the XGA VDH     */
            /* environment buffer                                     */
            /**********************************************************/
            if ( vga_present )
            {
                 pReqConfig->HWBufferSize = VGAConfigData.HWBufferSize +
                                                    sizeof( ENVIRONMENT );
            }
            else
            {
                pReqConfig->HWBufferSize = sizeof( ENVIRONMENT );
            }

            ReqConfigLen += MinDLen_Config_HWBufLen - MinDLen_Config_Flag;

        case MinDLen_Config_Flag + 3:
        case MinDLen_Config_Flag + 2:
        case MinDLen_Config_Flag + 1:
        case MinDLen_Config_Flag:
            if ( vga_present )
            {
               pReqConfig->Flags = (xga_adapter.usFlags & PRIMARY_ADAPTER) |
                                           VGAConfigData.Flags;
            }
            else
            {
               pReqConfig->Flags = xga_adapter.usFlags & PRIMARY_ADAPTER;
            }
            ReqConfigLen += MinDLen_Config_Flag - MinDLen_Config_Version;

        case MinDLen_Config_Version + 1:
        case MinDLen_Config_Version:
            pReqConfig->DeviceDriver = DeviceDriver;
            ReqConfigLen += MinDLen_Config_Version -
                                                 MinDLen_Config_ConfigNum;

        case MinDLen_Config_ConfigNum + 1:
        case MinDLen_Config_ConfigNum:
            pReqConfig->Configuration = UNKNOWN;
            ReqConfigLen += MinDLen_Config_ConfigNum -
                                                    MinDLen_Config_Memory;

        case MinDLen_Config_Memory + 1:
        case MinDLen_Config_Memory:
            pReqConfig->cbMemory = xga_adapter.lMemorySize;
            ReqConfigLen += MinDLen_Config_Memory - MinDLen_Config_Display;

        case MinDLen_Config_Display + 3:
        case MinDLen_Config_Display + 2:
        case MinDLen_Config_Display + 1:
        case MinDLen_Config_Display:
            pReqConfig->display = xga_adapter.usDisplayType;
            ReqConfigLen += MinDLen_Config_Display - MinDLen_Config_Adapter;

        case MinDLen_Config_Adapter + 1:
        case MinDLen_Config_Adapter:
            pReqConfig->adapter  = TYPE_XGA;
            ReqConfigLen += MinDLen_Config_Adapter;

    } /* switch copy_size */

    /******************************************************************/
    /* put the length of data copied into the user buffer length      */
    /* field                                                          */
    /******************************************************************/
    pReqConfig->cb = ReqConfigLen;

    return( NO_ERROR );

} /* RetConfigInfo */






USHORT PASCAL NEAR restore_coprocessor_state(PXGASTATE state_buffer)
{
    register USHORT rc;
    USHORT   fn_id;

    /******************************************************************/
    /* put coprocessor into native mode                               */
    /******************************************************************/
    fn_id = IO_NATIVE_MODE;
    rc = DosDevIOCtl(&ModesInfo.ModeData[0],
                     &fn_id,
                     GEN_FUNCTION,
                     XGA_CATEGORY,
                     dev_handle);
    if ( rc != NO_ERROR )
    {
        return(rc);
    }

    /******************************************************************/
    /* restore the palette                                            */
    /******************************************************************/
    fn_id = IO_RESTORE_PALETTE;
    rc = DosDevIOCtl(state_buffer->saved_palette,
                     &fn_id,
                     GEN_FUNCTION,
                     XGA_CATEGORY,
                     dev_handle);
    if ( rc != NO_ERROR )
    {
        return(rc);
    }

    /******************************************************************/
    /* restore the io registers                                       */
    /******************************************************************/
    fn_id = IO_RESTORE_REGS;
    rc = DosDevIOCtl(state_buffer->saved_io_regs,
                     &fn_id,
                     GEN_FUNCTION,
                     XGA_CATEGORY,
                     dev_handle);
    if ( rc != NO_ERROR )
    {
        return(rc);
    }

    /******************************************************************/
    /* restore the coprocessor state                                  */
    /******************************************************************/
    fn_id = IO_RESTORE_COPROC;
    rc = DosDevIOCtl(&state_buffer->coproc_state,
                     &fn_id,
                     GEN_FUNCTION,
                     XGA_CATEGORY,
                     dev_handle);
    if ( rc != NO_ERROR )
    {
        return(rc);
    }

    /******************************************************************/
    /* restore the VRAM                                               */
    /******************************************************************/
    fn_id = IO_RESTORE_VRAM;
    rc = DosDevIOCtl(&state_buffer->VRAM,
                     &fn_id,
                     GEN_FUNCTION,
                     XGA_CATEGORY,
                     dev_handle);
    if ( rc != NO_ERROR )
    {
        return(rc);
    }

    /******************************************************************/
    /* trash our buffers                                              */
    /******************************************************************/
    return( DosFreeSeg(SELECTOROF(state_buffer->VRAM.saved_buffer)) );

} /* restore_coprocessor_state */







USHORT EXPENTRY RestoreEnv( PENVIRONMENT  Environment,
                            PVDH_SAVEREST ParmBlock,
                            ULONG         Function )
{
    /******************************************************************/
    /* Local variable.                                                */
    /******************************************************************/
    register USHORT rc;

    if ( (Function != FnRestoreEnvironment)                                 ||
         (SELECTOROF(Environment) == 0)                                     ||
         ((ParmBlock->Flags &
          ~(SAVEREST_HARDWARE | SAVEREST_FULLPVB | SAVEREST_PARTPVB)) != 0) ||
         ((ParmBlock->Flags & (SAVEREST_FULLPVB | SAVEREST_PARTPVB)) ==
                                     (SAVEREST_FULLPVB | SAVEREST_PARTPVB)) )
    {
        return(ERROR_VIO_INVALID_PARMS);
    }

    if ( ParmBlock->Length < sizeof(VDH_SAVEREST) )
    {
        return(ERROR_VIO_INVALID_LENGTH);
    }

    /*********************************************************************/
    /* @d59982 - Check if restoring from a VGA PopUp to XGA Graphics Mode */
    /*********************************************************************/
    if (( vga_present )
    && (Environment->EnvFlags & (ENV_NATIVE_MODE | GRAPHICS_MODE))
    && (xga_inp(xga_adapter.usIORegBase) == MODE_EXT_GRAPHICS)
    && ((xga_adapter.usFlags & XGA_PRIMARY_ADAPTER) != 0) )
    {
        /* Restore the environment flags */

        Environment->EnvFlags &= ~(ENV_NATIVE_MODE | GRAPHICS_MODE);

        return( restore_coprocessor_state(&Environment->state_buffer) );

    }   /* @d59982 */

#ifndef R20
    /******************************************************************/
    /* check if restoring dos and native mode                         */
    /******************************************************************/
    if ( (Environment->EnvFlags & (INITENV_3xBOX | ENV_NATIVE_MODE)) !=
                                   (INITENV_3xBOX | ENV_NATIVE_MODE) )
#endif /* ndef R20 */
    {
        /**************************************************************/
        /* put the coprocessor into vga mode                          */
        /**************************************************************/
        if ( ParmBlock->Flags & RS_UPDATE_HARDWARE )
        {
            rc = set_xga_vga_mode();
            if ( rc != NO_ERROR )
            {
                return(rc);
            }
        }

        if ( vga_present )
        {
            /**********************************************************/
            /* Turn the display off to avoid displaying garbage.      */
            /**********************************************************/
            if ( ParmBlock->Flags & RS_UPDATE_HARDWARE )
            {
                PreSetMode();
            }

            /**********************************************************/
            /* Pass the call onto the VGABVH handler.                 */
            /**********************************************************/
            rc = ChainedVDHRestoreEnv(
                             (PENVIRONMENT)&Environment->VGAEnvironment,
                             ParmBlock,
                             Function );
            if (rc != NO_ERROR)
            {
                PostSetMode();
                return(rc);
            }

            /**********************************************************/
            /* if we are restoring to 132 column mode then do the xga */
            /* specific stuff                                         */
            /**********************************************************/
            if ( (Environment->ModeData.col == 132) &&
                 (ParmBlock->Flags & RS_UPDATE_HARDWARE) &&
                 ((xga_adapter.usFlags & XGA_PRIMARY_ADAPTER) != 0) )
            {
                rc = set_132_column_mode();
                if ( rc != NO_ERROR )
                {
                    return(rc);
                }
            }

            /**********************************************************/
            /* Turn the display back on again, after waiting for 5    */
            /* frames (to allow XGA time to copy its fonts into       */
            /* SRAM).                                                 */
            /**********************************************************/
            if ( ParmBlock->Flags & RS_UPDATE_HARDWARE )
            {
                PostSetMode();
            }

        } /* vga_present */

    } /* not native mode dos */


#ifndef R20
    /******************************************************************/
    /* if dos and native mode then restore the coprocessor            */
    /******************************************************************/
    if ( (ParmBlock->Flags & RS_UPDATE_HARDWARE) &&
         (Environment->EnvFlags & (INITENV_3xBOX | ENV_NATIVE_MODE)) ==
                                     (INITENV_3xBOX | ENV_NATIVE_MODE) &&
                        ((xga_adapter.usFlags & XGA_PRIMARY_ADAPTER) != 0) )
    {
        return( restore_coprocessor_state(&Environment->state_buffer) );
    }
    else
#endif /* ndef R20 */
    {
        return(NO_ERROR);
    }

} /* RestoreEnv */







USHORT PASCAL NEAR save_coprocessor_state(PXGASTATE state_buffer)
{

    register USHORT segments_needed;
    register USHORT rc;
    USHORT          fn_id;

    /******************************************************************/
    /* calculate the number of segments needed to hold the VRAM then  */
    /* add on one for the coprocessor state                           */
    /******************************************************************/
    segments_needed = (USHORT)(xga_adapter.lMemorySize >> 0x10);

    rc = DosAllocHuge(segments_needed,
                      SIZE_COPROC_STATE + SIZE_IO_REGISTERS + SIZE_PALETTE,
                      (PSEL)&SELECTOROF(state_buffer->VRAM.saved_buffer),
                      0,
                      2);
    if ( rc != NO_ERROR )
    {
        /**************************************************************/
        /* Allocation failed, tidy up and go home                     */
        /**************************************************************/
        return(rc);
    }
    OFFSETOF(state_buffer->VRAM.saved_buffer) = 0;


    /******************************************************************/
    /* put the address of the last segment in the coprocessor state   */
    /* data                                                           */
    /******************************************************************/
    SELECTOROF(state_buffer->coproc_state.saved_state) =
    SELECTOROF(state_buffer->saved_io_regs) =
    SELECTOROF(state_buffer->saved_palette) =
                           SELECTOROF(state_buffer->VRAM.saved_buffer) +
                                           huge_shift * segments_needed;

    OFFSETOF(state_buffer->coproc_state.saved_state) = 0;
    OFFSETOF(state_buffer->saved_io_regs) = SIZE_COPROC_STATE;
    OFFSETOF(state_buffer->saved_palette) =
                                  SIZE_COPROC_STATE + SIZE_IO_REGISTERS;

// segment validation
// weird - if this isn't done xgaring0 GPs when it accesses this segment
    *state_buffer->coproc_state.saved_state = 0x55;


    /******************************************************************/
    /* save the io registers                                          */
    /******************************************************************/
    fn_id = IO_SAVE_REGS;
    rc = DosDevIOCtl(state_buffer->saved_io_regs,
                     &fn_id,
                     GEN_FUNCTION,
                     XGA_CATEGORY,
                     dev_handle);
    if ( rc != NO_ERROR )
    {
        return(rc);
    }

    /******************************************************************/
    /* save the palette                                               */
    /******************************************************************/
    fn_id = IO_SAVE_PALETTE;
    rc = DosDevIOCtl(state_buffer->saved_palette,
                     &fn_id,
                     GEN_FUNCTION,
                     XGA_CATEGORY,
                     dev_handle);
    if ( rc != NO_ERROR )
    {
        return(rc);
    }

    /******************************************************************/
    /* save the VRAM contents                                         */
    /******************************************************************/
    fn_id = IO_SAVE_VRAM;
    rc = DosDevIOCtl(&state_buffer->VRAM,
                     &fn_id,
                     GEN_FUNCTION,
                     XGA_CATEGORY,
                     dev_handle);
    if ( rc != NO_ERROR )
    {
        return(rc);
    }

    /******************************************************************/
    /* save the coprocessor state                                     */
    /******************************************************************/
    fn_id = IO_SAVE_COPROC;
    return( DosDevIOCtl(&state_buffer->coproc_state,
                       &fn_id,
                       GEN_FUNCTION,
                       XGA_CATEGORY,
                       dev_handle) );


} /* save_coprocessor_state */







USHORT EXPENTRY SaveEnv( PENVIRONMENT  Environment,
                         PVDH_SAVEREST ParmBlock,
                         ULONG         Function )
{
    register USHORT rc;   /* d59982 */
#ifndef R20
    /******************************************************************/
    /* if we are in native mode leaving a dos session then we need to */
    /* save the coprocessor state and vram                            */
    /******************************************************************/
    if ( Environment->EnvFlags & INITENV_3xBOX )
    {
        if ( (xga_inp(xga_adapter.usIORegBase) == MODE_EXT_GRAPHICS) &&
             ((xga_adapter.usFlags & XGA_PRIMARY_ADAPTER) != 0) )
        {
            if ( (Function != FnSaveEnvironment)                                 ||
                 (SELECTOROF(Environment) == 0)                                     ||
                 ((ParmBlock->Flags &
                  ~(SAVEREST_HARDWARE | SAVEREST_FULLPVB | SAVEREST_PARTPVB)) != 0) ||
                 ((ParmBlock->Flags & (SAVEREST_FULLPVB | SAVEREST_PARTPVB)) ==
                                           (SAVEREST_FULLPVB | SAVEREST_PARTPVB)) )
            {
                return(ERROR_VIO_INVALID_PARMS);
            }

            if ( ParmBlock->Length < sizeof(VDH_SAVEREST) )
            {
                return(ERROR_VIO_INVALID_LENGTH);
            }


            rc = save_coprocessor_state(&Environment->state_buffer);
            if ( rc != NO_ERROR )
            {
                return(rc);
            }

            /**********************************************************/
            /* remember we were native mode                           */
            /**********************************************************/
            Environment->EnvFlags |= ENV_NATIVE_MODE;

            return(NO_ERROR);

        } /* extended graphics mode */

        else /* vga mode */
        {
            Environment->EnvFlags &= ~ENV_NATIVE_MODE;
        }

    } /* 3x box */
#endif /* ndef R20 */


    if ( vga_present )
    {
      /*********************************************************************/
      /* @d59982  -  If we are in XGA native mode and an incomming VGA     */
      /* text mode popup is imminant then we need to save the coprocessor  */
      /* state and set mode to VGA.                                        */
      /*********************************************************************/
        if (( Environment->EnvFlags == 0 )
        &&  (xga_inp(xga_adapter.usIORegBase) == MODE_EXT_GRAPHICS)
        &&  ((xga_adapter.usFlags & XGA_PRIMARY_ADAPTER) != 0)
        &&  (ParmBlock->Flags & (SAVEREST_HARDWARE | SAVEREST_PARTPVB)) )
        {
           rc = save_coprocessor_state(&Environment->state_buffer);
           if ( rc != NO_ERROR )
           {
              return(rc);
           }

           /***************************************************************/
           /* Remember we are in XGA native mode displaying High Res      */
           /* Graphics. These flags get cleared in RestoreEnv.            */
           /***************************************************************/
           Environment->EnvFlags |= (ENV_NATIVE_MODE | GRAPHICS_MODE);

           PreSetMode();
           rc = set_xga_vga_mode();
           PostSetMode();
           if ( rc != NO_ERROR )
           {
              return(rc);
           }

        }   /* end @d59982 */

        /**************************************************************/
        /* Pass the call onto the VGABVH handler.                     */
        /**************************************************************/
        return(ChainedVDHSaveEnv(
                          (PENVIRONMENT)&Environment->VGAEnvironment,
                          ParmBlock,
                          Function ));

    }

    /******************************************************************/
    /* If leaving an OS/2 session in 132 column mode then set back to */
    /* standard vga mode. The ParmBlock doesn't give mode info so     */
    /* need to set vga mode always.                                   */
    /******************************************************************/
    if ( !(Environment->EnvFlags & INITENV_3xBOX) )
    {
      if ( (Environment->ModeData.col == 132) &&
           (ParmBlock->Flags & RS_UPDATE_HARDWARE) &&
           ((xga_adapter.usFlags & XGA_PRIMARY_ADAPTER) != 0) )
      {
        PreSetMode();
        rc = set_xga_vga_mode();
        PostSetMode();
        if ( rc != NO_ERROR )
        {
           return(rc);
        }
      }
    }

    return( NO_ERROR );

} /* SaveEnv */
