/*DDK*************************************************************************/
/*                                                                           */
/* COPYRIGHT (C) Microsoft Corporation, 1989                                 */
/* COPYRIGHT    Copyright (C) 1995 IBM Corporation                           */
/*                                                                           */
/*    The following IBM OS/2 WARP source code is provided to you solely for  */
/*    the purpose of assisting you in your development of OS/2 WARP device   */
/*    drivers. You may use this code in accordance with the IBM License      */
/*    Agreement provided in the IBM Device Driver Source Kit for OS/2. This  */
/*    Copyright statement may not be removed.                                */
/*                                                                           */
/*****************************************************************************/
/*****************************************************************************
 *
 * SOURCE FILE NAME = SVGAPMI.C
 *
 * DESCRIPTIVE NAME = BASE VIDEO DEVICE HANDLER  - PRIVATES
 *
 *
 * VERSION      V2.0
 *
 * DATE
 *
 * DESCRIPTION
 *
 *      Code to process a pseudo-PMI file which contains
 *      information to allow video modes to be set on a
 *      particular chipset based adapter-card.
 *      Data is read from a text file and used to create/fill
 *      data structures then used to set the video modes
 *      supported.
 *
 *      The file is parsed and local data structures updated
 *      with the appropriate information.
 *      On completion of a section, or when enough information
 *      has been accumulated to fill a VIDEOMODE data structure,
 *      the aim of these routines, the main video mode data is
 *      updated accordingly.
 *
 *      This allows a text file to be processed which contains
 *      data for any number of adapter types and chipsets. This
 *      set of routines can then selectively fill in only the
 *      modes required for the adapter currently installed in
 *      the system.
 *
 * FUNCTIONS
 *
 * NOTES        NONE
 *
 * STRUCTURES   NONE
 *
 * EXTERNAL REFERENCES
 *
 *              NONE
 *
 * EXTERNAL FUNCTIONS
 *
 *              NONE
 *
*/

#define  INCL_BASE              /* ALL of OS/2 Base */
#define  INCL_OS2STD            /* Needed for NULL definition in OS2STD.H */
#define  INCL_DOSMEMMGR         /* pull in memory management stuff */
#define  INCL_DOSINFOSEG        /*            change DOSSearchPath */
#include <os2.h>
#include <string.h>
#include "svgatype.h"


extern OEMSVGAINFO SVGAHardware;
extern PVIDEOMODE pModes;                                       /*          */
extern PMEMORYMAPS pMemoryMaps;                                 /*          */
extern USHORT usMaxVideoModes;                                  /*          */
extern USHORT SVGAMonitorType;                                  /*          */
extern SEL GlobalInfoSeg;                                       /*          */

extern FPHWCOMMAND pHWLockData;                                 /*          */
extern FPHWCOMMAND pHWUnLockData;                               /*          */
extern FPHWCOMMAND pHWCleanData;                                /*          */
extern FPHWCOMMAND pHWCurrSetBank;                              /*          */

#define  LOGERROR      1
#define  STATIC                        /* static */
#pragma  intrinsic(strcpy, strcmp, strlen, strcat, memset, memcpy)
#define  MAX_PATH      260
#define  LINELEN       256
#define  BUFFSIZE      1024
#define  PMI_MODE_COLOUR 0x08          /* Bit 3 - 0 = mono, 1 = colour */
#define  PMI_MODE_GRAPHICS 0x10        /* Bit 4 - 0 = text, 1 = graphics */
#define  EOF           (-1)

/*
**      Type Definitions
**
**      Tokens are grouped, and must be kept that way.
**      There are dummy tokens, place holders, TOK_??_BEGIN and TOK_??_END
**      to make tests and expansion easier. Just be sure to keep token
**      info for a given section, within these bounds.
*/

typedef enum _PMITOKEN
{
     TOK_SECT_BEGIN,
     TOK_ADAPTERTYPE,           /* Section Heading tokens */
     TOK_CHIPSET,
     TOK_MONITORTYPE,
     TOK_BLOCKCOMMENT,
     TOK_MODEINFO,
     TOK_SETMODE,
     TOK_TRAPREGS,
     TOK_LOCK,
     TOK_UNLOCK,
     TOK_CLEANUP,
     TOK_SETBANK,
     TOK_GETBANK,                                               /*          */
     TOK_SETBANKLINEAR,                                         /*          */
     TOK_GETBANKLINEAR,                                         /*          */
     TOK_SECT_END,

     TOK_MI_BEGIN,
     TOK_MODEATTRIBUTES,        /* ModeInfo Section tokens */
     TOK_BYTESPERSCANLINE,
     TOK_TEXTROWS,
     TOK_XRESOLUTION,
     TOK_YRESOLUTION,
     TOK_XCHARSIZE,
     TOK_YCHARSIZE,
     TOK_BITSPERPIXEL,
     TOK_NUMBEROFPLANES,
     TOK_PAGELENGTH,
     TOK_SAVESIZE,
     TOK_TOTALMEMORY,
     TOK_INTERLACEMODE,
     TOK_BUFFERADDRESS,
     TOK_MMIOADDRESS,                                           /*          */
     TOK_APERTURESIZE,                                          /*          */
     TOK_INT10MODESET,                                          /*          */
     TOK_MI_END,
     TOK_AT_BEGIN,
     TOK_ADAPTER_VIDEO7,        /* AdapterType Section Tokens */
     TOK_ADAPTER_TRIDENT,
     TOK_ADAPTER_TSENG,
     TOK_ADAPTER_WESTERNDIGITAL,
     TOK_ADAPTER_ATI,
     TOK_ADAPTER_IBM,
     TOK_ADAPTER_CIRRUS,      /*            */
     TOK_ADAPTER_S3,            /*            */  /*            */
     TOK_ADAPTER_CHIPS,                                         /*          */
     TOK_ADAPTER_WEITEK,                                        /*          */
     TOK_AT_END,

     TOK_CT_BEGIN,
     TOK_CHIP_HT205,            /* ChipSet Section Tokens */
     TOK_CHIP_HT208,
     TOK_CHIP_HT209,
     TOK_CHIP_8800,
     TOK_CHIP_8900,
     TOK_CHIP_ET3000,
     TOK_CHIP_ET4000,
     TOK_CHIP_ET4000W32,                                        /*          */
     TOK_CHIP_ET4000W32I,                                       /*          */
     TOK_CHIP_ET4000W32P,                                       /*          */
     TOK_CHIP_PVGA1A,
     TOK_CHIP_WD9000,
     TOK_CHIP_WD9011,
     TOK_CHIP_WD9030,
     TOK_CHIP_WD9026,                                           /*          */
     TOK_CHIP_WD9027,                                           /*          */
     TOK_CHIP_WD9031,                                           /*          */
     TOK_CHIP_WD9024,                                           /*          */
     TOK_CHIP_WD9033,                                           /*          */
     TOK_CHIP_ATI1,
     TOK_CHIP_ATI2,
     TOK_CHIP_ATI3,                                             /*          */
     TOK_CHIP_ATI6,                                             /*          */
     TOK_CHIP_ATI6AX,                                           /*          */
     TOK_CHIP_ATI6GX,                                           /*          */
     TOK_CHIP_IBM,
     TOK_CHIP_GD5422,                           /*            */
     TOK_CHIP_GD5424,                           /*            */
     TOK_CHIP_GD5426,                           /*            */
     TOK_CHIP_GD5428,                           /*            */  /*            */
     TOK_CHIP_S386C80X,                         /*            */
     TOK_CHIP_S386C928,                         /*            */
     TOK_CHIP_S386C911,                                         /*          */
     TOK_CHIP_VISION864,                                        /*          */
     TOK_CHIP_VISION964,                                        /*          */
     TOK_CHIP_WEITEKP9000,                                      /*          */
     TOK_CHIP_WEITEKW5186,                                      /*          */
     TOK_CHIP_WEITEKW5286,                                      /*          */
     TOK_CT_END,

     TOK_MT_BEGIN,              /* MonitorType Section Tokens            */
     TOK_MONITOR_NEC2A,
     TOK_MONITOR_IBM8514A,
     TOK_MONITOR_NEC3FGX,
     TOK_MONITOR_NEC3FG,
     TOK_MONITOR_NEC4FG,
     TOK_MONITOR_NEC5FG,
     TOK_MONITOR_CS1572FS,
     TOK_MONITOR_CS1024NI,
     TOK_MT_END,

     TOK_SM_BEGIN,
     TOK_INBYTE,                /* Command Section tokens */
     TOK_OUTBYTE,
     TOK_INWORD,
     TOK_OUTWORD,
     TOK_INBLOCK,
     TOK_OUTBLOCK,
     TOK_RMWBYTE,
     TOK_RMWELSE,                                               /*          */
     TOK_RMWWORD,                                               /*          */
     TOK_WAIT,                                                  /*          */
     TOK_SETBANKCALL,                                           /*          */
     TOK_VALUE,
     TOK_VARIABLE,
     TOK_LPAREN,
     TOK_RPAREN,
     TOK_EQUALS,
     TOK_SEMICOLON,
     TOK_REGOP_AND,
     TOK_REGOP_OR,
     TOK_REGOP_XOR,
     TOK_REGOP_SHL,
     TOK_REGOP_SHR,
     TOK_SM_END,

     TOK_EOF,
     TOK_ERROR,                 /* put only error tokens beyond this point */
     TOK_ERROR_SYNTAX,          /* must correspond to errors[] */
     TOK_ERROR_MEMORY,
     TOK_ERROR_VARIABLE,
     TOK_ERROR_ADAPTER,
     TOK_ERROR_MONITOR,
     TOK_ERROR_CHIPSET,

} PMITOKEN;

typedef struct _TOK
{
  char *tok_txt;
  PMITOKEN tok_val;
} TOK;

typedef struct _VALIDMODES
{
  USHORT hres;
  USHORT vres;
  BYTE color;
} VALIDMODES;

/*
**      Local data
*/

STATIC TOK Tokens[] =
{
  "[ADAPTERTYPE]",      TOK_ADAPTERTYPE,
  "[MONITORTYPE]",      TOK_MONITORTYPE,                        /*          */
  "[CHIPSET]",          TOK_CHIPSET,
  "[COMMENT]",          TOK_BLOCKCOMMENT,
  "[MODEINFO]",         TOK_MODEINFO,
  "[SETMODE]",          TOK_SETMODE,
  "[TRAPREGS]",         TOK_TRAPREGS,
  "[LOCK]",             TOK_LOCK,
  "[UNLOCK]",           TOK_UNLOCK,
  "[CLEANUP]",          TOK_CLEANUP,
  "[SETBANK]",          TOK_SETBANK,                            /*          */
  "[GETBANK]",          TOK_GETBANK,                            /*          */
  "[SETBANKLINEAR]",    TOK_SETBANKLINEAR,                      /*          */
  "[GETBANKLINEAR]",    TOK_GETBANKLINEAR,                      /*          */
  "MODEATTRIBUTES",     TOK_MODEATTRIBUTES,
  "BYTESPERSCANLINE",   TOK_BYTESPERSCANLINE,
  "TEXTROWS",           TOK_TEXTROWS,
  "XRESOLUTION",        TOK_XRESOLUTION,
  "YRESOLUTION",        TOK_YRESOLUTION,
  "XCHARSIZE",          TOK_XCHARSIZE,
  "YCHARSIZE",          TOK_YCHARSIZE,
  "BITSPERPIXEL",       TOK_BITSPERPIXEL,
  "NUMBEROFPLANES",     TOK_NUMBEROFPLANES,
  "PAGELENGTH",         TOK_PAGELENGTH,
  "SAVESIZE",           TOK_SAVESIZE,
  "TOTALMEMORY",        TOK_TOTALMEMORY,                        /*          */
  "INTERLACEMODE",      TOK_INTERLACEMODE,
  "BUFFERADDRESS",      TOK_BUFFERADDRESS,
  "MEMORYIOADDRESS",    TOK_MMIOADDRESS,                        /*          */
  "APERTURESIZE",       TOK_APERTURESIZE,                       /*          */
  "INT10MODESET",       TOK_INT10MODESET,                       /*          */

  "VIDEO7",             TOK_ADAPTER_VIDEO7,
  "TRIDENT",            TOK_ADAPTER_TRIDENT,
  "TSENG",              TOK_ADAPTER_TSENG,
  "WESTERNDIGITAL",     TOK_ADAPTER_WESTERNDIGITAL,
  "ATI",                TOK_ADAPTER_ATI,
  "IBM",                TOK_ADAPTER_IBM,
  "CIRRUS",             TOK_ADAPTER_CIRRUS,                     /*          */
  "S3",                 TOK_ADAPTER_S3,                         /*          */  /*            */
  "CHIPS",              TOK_ADAPTER_CHIPS,                      /*          */
  "WEITEK",             TOK_ADAPTER_WEITEK,                     /*          */

  "HT205",              TOK_CHIP_HT205,
  "HT208",              TOK_CHIP_HT208,
  "HT209",              TOK_CHIP_HT209,
  "TR8800",             TOK_CHIP_8800,
  "TR8900",             TOK_CHIP_8900,
  "ET3000",             TOK_CHIP_ET3000,
  "ET4000",             TOK_CHIP_ET4000,
  "ET4000W32",          TOK_CHIP_ET4000W32,                     /*          */
  "ET4000W32I",         TOK_CHIP_ET4000W32I,                    /*          */
  "ET4000W32P",         TOK_CHIP_ET4000W32P,                    /*          */
  "PVGA1A",             TOK_CHIP_PVGA1A,
  "PVGA1B",             TOK_CHIP_WD9000,
  "PVGA1C",             TOK_CHIP_WD9011,
  "PVGA1D",             TOK_CHIP_WD9030,
  "WD90C26",            TOK_CHIP_WD9026,                        /*          */
  "WD90C27",            TOK_CHIP_WD9027,                        /*          */
  "WD90C31",            TOK_CHIP_WD9031,                        /*          */
  "WD90C24",            TOK_CHIP_WD9024,                        /*          */
  "WD90C33",            TOK_CHIP_WD9033,                        /*          */
  "ATI18800",           TOK_CHIP_ATI1,
  "ATI28800",           TOK_CHIP_ATI2,
  "ATI38800",           TOK_CHIP_ATI3,                          /*          */
  "ATI68800",           TOK_CHIP_ATI6,                          /*          */
  "ATI68800AX",         TOK_CHIP_ATI6AX,                        /*          */
  "ATI68800GX",         TOK_CHIP_ATI6GX,                        /*          */
  "IBMSVGA",            TOK_CHIP_IBM,
  "GD5422",             TOK_CHIP_GD5422,                        /*          */
  "GD5424",             TOK_CHIP_GD5424,                        /*          */
  "GD5426",             TOK_CHIP_GD5426,                        /*          */
  "GD5428",             TOK_CHIP_GD5428,                        /*          */  /*            */
  "S386C80X",           TOK_CHIP_S386C80X,                      /*          */
  "S386C928",           TOK_CHIP_S386C928,                      /*          */
  "S386C911",           TOK_CHIP_S386C911,                      /*          */
  "VISION864",          TOK_CHIP_VISION864,                     /*          */
  "VISION964",          TOK_CHIP_VISION964,                     /*          */
  "P9000",              TOK_CHIP_WEITEKP9000,                   /*          */
  "W5186",              TOK_CHIP_WEITEKW5186,                   /*          */
  "W5286",              TOK_CHIP_WEITEKW5286,                   /*          */

  "INB",                TOK_INBYTE,
  "OUTB",               TOK_OUTBYTE,
  "INW",                TOK_INWORD,                             /*          */
  "OUTW",               TOK_OUTWORD,
  "BINB",               TOK_INBLOCK,                            /*          */
  "BOUTB",              TOK_OUTBLOCK,
  "RMWB",               TOK_RMWBYTE,
  "RMWE",               TOK_RMWELSE,                            /*          */
  "RMWW",               TOK_RMWWORD,                            /*          */
  "WAIT",               TOK_WAIT,                               /*          */
  "SETBANKLINEAR",      TOK_SETBANKCALL,                        /*          */
  "NEC2A",              TOK_MONITOR_NEC2A,                      /*          */
  "IBM8514",            TOK_MONITOR_IBM8514A,                   /*          */
  "NEC3FGX",            TOK_MONITOR_NEC3FGX,                    /*          */
  "NEC3FG",             TOK_MONITOR_NEC3FG,                     /*          */
  "NEC4FG",             TOK_MONITOR_NEC4FG,                     /*          */
  "NEC5FG",             TOK_MONITOR_NEC5FG,                     /*          */
  "CS1572FS",           TOK_MONITOR_CS1572FS,                   /*          */
  "CS1024NI",           TOK_MONITOR_CS1024NI,                   /*          */
} ;

STATIC USHORT ChipTable[] =
{
  1,                                   /* TOK_CHIP_HT205  */
  2,                                   /* TOK_CHIP_HT208  */
  3,                                   /* TOK_CHIP_HT209  */
  1,                                   /* TOK_CHIP_8800   */
  2,                                   /* TOK_CHIP_8900   */
  1,                                   /* TOK_CHIP_ET3000 */
  2,                                   /* TOK_CHIP_ET4000 */
  3,                    /* TOK_CHIP_ET4000W32 */                /*          */
  4,                    /* TOK_CHIP_ET4000W32I */               /*          */
  5,                    /* TOK_CHIP_ET4000W32P */               /*          */
  1,                                   /* TOK_CHIP_PVGA1A */
  2,                                   /* TOK_CHIP_WD9000 */
  3,                                   /* TOK_CHIP_WD9011 */
  4,                                   /* TOK_CHIP_WD9030 */
  5,                    /* TOK_CHIP_WD9026 */                   /*          */
  6,                    /* TOK_CHIP_WD9027 */                   /*          */
  7,                    /* TOK_CHIP_WD9031 */                   /*          */
  8,                    /* TOK_CHIP_WD9024 */                   /*          */
  9,                    /* TOK_CHIP_WD9033 */                   /*          */
  1,                                   /* TOK_CHIP_ATI1   */
  2,                                   /* TOK_CHIP_ATI2   */
  3,                    /* TOK_CHIP_ATI3 */                     /*          */
  4,                    /* TOK_CHIP_ATI6 */                     /*          */
  5,                    /* TOK_CHIP_ATI6AX */                   /*          */
  6,                    /* TOK_CHIP_ATI6CX */                   /*          */
  1,                                   /* TOK_CHIP_IBM    */
  1,                                   /* TOK_CHIP_GD5422 */    /*           */
  2,                                   /* TOK_CHIP_GD5424 */    /*           */
  3,                                   /* TOK_CHIP_GD5425 */    /*           */
  1,                                   /* TOK_CHIP_S386C80X */  /*           */  /*            */
  2,                                   /* TOK_CHIP_S386C928 */  /*           */
  3,                    /* TOK_CHIP_S386C911 */                 /*          */
  4,                    /* TOK_CHIP_VISION864 */                /*          */
  5,                    /* TOK_CHIP_VISION964 */                /*          */
  1,                    /* TOK_CHIP_WEITEKP9000 */              /*          */
  2,                    /* TOK_CHIP_WEITEKW5186 */              /*          */
  3,                    /* TOK_CHIP_WEITEKW5286 */              /*          */
} ;

STATIC char * errors[] =
{
  "syntax",
  "memory",
  "variable",
  "adapter",
  "monitor",
  "chipset",
};

STATIC USHORT ColNo = 0;
STATIC USHORT LineNo = 1;

STATIC char ch = 0,upch = 0;
STATIC ULONG CurrValue = 0;
STATIC char CurrVariable[LINELEN] = { 0 } ;

STATIC USHORT AdapterType = 0;
STATIC USHORT ChipType = 0;

STATIC VIDEOMODE CurrVideoMode = { 0 } ;
STATIC MEMORYMAPS CurrMemoryMap = { 0 } ;

/***************************************************************************
 *
 * FUNCTION NAME = pmiGetc()
 *
 * DESCRIPTION   =
 *
 *       Return the next character from the file.
 *       Data is read in blocks into an internal buffer and returned
 *       from there. This function will take care of filling the
 *       buffer when it empties or hasn't been read yet.
 *
 * INPUT         = NONE
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL = HFILE f
 *                 NULL  - if no memory available
 * RETURN-ERROR  = NONE
 *
 **************************************************************************/

STATIC char NEAR pmiGetc(HFILE f)
{
  static char buffer[BUFFSIZE];         /* read buffer */
  static int buffpos = 0,buffcount = 0; /* buff ptrs */
  int bytesread;


  if (buffpos == buffcount)
  {

    if (DosRead(f, &buffer, BUFFSIZE, &bytesread))
    {
      buffpos = buffcount = 0;
      return (EOF);
    }

    else
    {

      if (!bytesread)
      {
        buffpos = buffcount = 0;
        return (EOF);
      }
      buffpos = 0;
      buffcount = bytesread;
    }
  }
  return (buffer[buffpos++]);
}

/***************************************************************************
 *
 * FUNCTION NAME = NextChar()
 *
 * DESCRIPTION   =
 *
 *      Calls pmiGetc() to return the next character and updates the
 *      global variables 'ch' and 'upch' with the character and its
 *      uppercase equivalent, respectively.
 *
 * INPUT         = HFILE f
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL = NONE
 * RETURN-ERROR  = NONE
 *
 **************************************************************************/

STATIC void NEAR NextChar(HFILE f)
{
  ch = pmiGetc(f);

  if ((ch >= 'a') && (ch <= 'z'))
    upch = ch-(char)('a'-'A');

  else
    upch = ch;

  ColNo++;

  if (ch == '\n')
  {
    LineNo++;
    ColNo = 0;
  }
}

/***************************************************************************
 *
 * FUNCTION NAME = GetLongValue()
 *
 * DESCRIPTION   =
 *
 *      This is a bit convoluted because we can't use library functions.
 *      First check to see if string is a hex one and adjust pointer
 *        and radix as necessary.
 *      Note: the 'emit' statements make the operands 32-bit.
 *      Then process the string, adding numbers/hex digits.
 *
 * INPUT         = NONE
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL = NONE
 * RETURN-ERROR  = NONE
 *
 **************************************************************************/

STATIC ULONG NEAR GetLongValue(char *s)
{
  ULONG v = 0,radix = 10;

  if ((strlen(s) > 1) && (s[1] == 'X'))
  {
    radix = 16;                        /* adjust radix to hex */
    s += 2;                            /* skip the '0X' part */
  }

  while (*s)
  {
    _asm {                      ;
                _emit 0x66              ;
                xor dx, dx              ; Do the multiply this way
                _emit 0x66              ;
                mov ax, word ptr v      ; to prevent library routines
                _emit 0x66              ;
                mul word ptr radix      ; being called in which aren't
                _emit 0x66              ;
                mov word ptr v, ax      ; desirable.
            }


    if (*s <= '9')
      v += (ULONG)(*s++-'0');          /* handle number between 0-9 */

    else
      v += (ULONG)(*s++-'A'+10);       /* handle hex letters A-F */
  }
  return (v);
}

/***************************************************************************
 *
 * FUNCTION NAME = NextToken()
 *
 * DESCRIPTION   =
 *
 *      Returns the next token and updates appropriate global
 *      variables such as: the current variable name and the
 *      current numeric value just read.
 *
 *      Comments ('C' style) will be skipped if encountered.
 *
 * INPUT         = HFILE f
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL = NONE
 * RETURN-ERROR  = NONE
 *
 **************************************************************************/

STATIC PMITOKEN NEAR NextToken(HFILE f)
{
  PMITOKEN tok = TOK_EOF;
  char sin[LINELEN];
  int i;

  while (ch != EOF)
  {
    if (((upch >= 'A') && (upch <= 'Z')) || (upch == '['))
    {                                  /* look for keyword */
      i = 0;
      do
      {
        sin[i++] = upch;
        NextChar(f);
      }
      while (((upch >= 'A') && (upch <= 'Z')) || ((upch >= '0') && (upch <=
         '9')) || ((upch == ']')));
      sin[i] = '\0';

      for (i = 0; i < sizeof(Tokens)/sizeof(TOK); i++)
        if (!strcmp(sin, Tokens[i].tok_txt))
          return (Tokens[i].tok_val);
                                /* test for variable in range R0 - R255 */

      if ((sin[0] == 'R') && ((sin[1] >= '0') && (sin[1] <= '9')))
      {
        strcpy(CurrVariable, sin);
        return (TOK_VARIABLE);
      }
      else
        return (TOK_ERROR_SYNTAX);
    }
    else
      if ((upch >= '0') && (upch <= '9'))
      {
        i = 0;
        do
        {
          sin[i++] = upch;
          NextChar(f);
        }
        while ((upch == 'X') || ((upch >= '0') && (upch <= '9')) || ((upch >=
           'A') && (upch <= 'F')));
        sin[i] = '\0';
        CurrValue = GetLongValue(sin);
        return (TOK_VALUE);
      }
      else
        switch (upch)
        {
          case ' ' :
          case '\t' :
          case '\n' :
          case ',' :
            NextChar(f);
            break;

          case '(' :
            NextChar(f);
            return (TOK_LPAREN);

          case ')' :
            NextChar(f);
            return (TOK_RPAREN);

          case '=' :
            NextChar(f);
            return (TOK_EQUALS);

          case ';' :
            NextChar(f);
            return (TOK_SEMICOLON);

          case '|' :                    /* bitwise OR operation             */
            NextChar(f);
            return (TOK_REGOP_OR);

          case '&' :                    /* bitwise AND operation            */
            NextChar(f);
            return (TOK_REGOP_AND);

          case '^' :                    /* bitwise XOR operation            */
            NextChar(f);
            return (TOK_REGOP_XOR);

          case '<' :                    /* bitwise SHL operation            */
            NextChar(f);
            return (TOK_REGOP_SHL);

          case '>' :                    /* bitwise SHR operation            */
            NextChar(f);
            return (TOK_REGOP_SHR);

          case '/' :                   /* comment? */
            NextChar(f);
            if ((ch == '/') || (ch == '*'))
            {
              if (ch == '/')
              {                        /* skip to end-of-line */
                while ((ch != '\n') && (ch != EOF))
                  NextChar(f);
                if (ch == '\n')
                  NextChar(f);
              }
              else
              {                        /* skip to comment end */
                NextChar(f);
                do
                {
                  while ((ch != '*') && (ch != EOF))
                    NextChar(f);
                  NextChar(f);
                }
                while ((ch != '/') && (ch != EOF));
                NextChar(f);
              }
            }
            break;

          default  :
            NextChar(f);
            break;
        }
  }
  return (tok);
}
/* Moved ahead of usage */                                      /*          */
#if      LOGERROR

/*****************************************************************************
 *
 * FUNCTION NAME = itoa(int
 *
 * DESCRIPTION   =
 *
 *      Convert given integer value to ASCII.
 *      ASCII conversion is added to the END of the
 *      given string and works backwards.
 *
 * INPUT         = NONE
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL = NONE
 * RETURN-ERROR  = NONE
 *
 ****************************************************************************/

void NEAR itoa(int v,char *s)
{
  int r;


  while (v)
  {
    r = v%10;
    v /= 10;
    *s = (char)(r+'0');
    s--;
  }
}


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

void NEAR LogError(PMITOKEN tok, char * errfname)
{
  int byteswritten;
  HFILE errfile;
  char buff[128];
  ULONG position;
  USHORT action;

  if (!(DosOpen(errfname,
                &errfile,
                &action,
                0L,
                FILE_NORMAL,
                FILE_CREATE|FILE_OPEN,
                OPEN_ACCESS_WRITEONLY|OPEN_SHARE_DENYWRITE|OPEN_FLAGS_SEQUENTIAL,
                NULL)))
  {
    DosChgFilePtr(errfile,
                  0L,
                  FILE_END,
                  &position);
    strcpy(buff, "BVHSVGA: Error (");
    strcat(buff, errors[tok-TOK_ERROR-1]);
    strcat(buff, ") at line:     ");
    itoa(LineNo, buff+strlen(buff)-1);
    strcat(buff, ", column:     ");
    itoa(ColNo, buff+strlen(buff)-1);
    strcat(buff, "\r\n");
    DosWrite(errfile,
             buff,
             strlen(buff),
             &byteswritten);
    DosClose(errfile);
  }
}

#endif /* LOGERROR */

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

STATIC PMITOKEN NEAR ProcessModeInfo(                           /*          */
  PMITOKEN tok,                                                 /*          */
  HFILE pmifile,                                                /*          */
  char * errfname )                                             /*          */
{
  PMITOKEN modetok;

  CurrVideoMode.cb = 12;
  CurrVideoMode.fmt_ID = 0;
  CurrVideoMode.attrib = 1;
  tok = NextToken(pmifile);

  if( ((tok <= TOK_MI_BEGIN) || (tok >= TOK_MI_END))            /*          */
      /* Allow uplevel PMI files */                             /*          */
      && (tok != TOK_ERROR_SYNTAX) )                            /*          */
    return (TOK_ERROR_SYNTAX);       /* force an error */       /*          */
  do
  {


    modetok = tok;
    tok = NextToken(pmifile);          /* get equals */

    if (tok != TOK_EQUALS)
      return (TOK_ERROR_SYNTAX);       /* force an error */

    tok = NextToken(pmifile);          /* get value */

    if (tok != TOK_VALUE)
      return (TOK_ERROR_SYNTAX);       /* force an error */

    switch (modetok)
    {

      case  TOK_MODEATTRIBUTES :
        CurrVideoMode.fbType = 0;

        if (CurrValue&PMI_MODE_COLOUR)
          CurrVideoMode.fbType |= MODE_FLAG_NOT_MONO;

        if (CurrValue&PMI_MODE_GRAPHICS)
        {
          CurrVideoMode.fbType |= MODE_FLAG_NATIVE;
          CurrVideoMode.fbType |= MODE_FLAG_GRAPHICS;
        }
        break;

      case  TOK_BYTESPERSCANLINE :
        CurrVideoMode.col = (USHORT)CurrValue;
        break;
      case  TOK_TEXTROWS :
        CurrVideoMode.row = (USHORT)CurrValue;
        break;

      case  TOK_XRESOLUTION :
        CurrVideoMode.hres = (USHORT)CurrValue;
        break;

      case  TOK_YRESOLUTION :
        CurrVideoMode.vres = (USHORT)CurrValue;
        break;

      case  TOK_XCHARSIZE :
        CurrVideoMode.XCharSize = (BYTE)CurrValue;
        break;

      case  TOK_YCHARSIZE :
        CurrVideoMode.YCharSize = (BYTE)CurrValue;
        break;

      case  TOK_BITSPERPIXEL :
        CurrVideoMode.color = (BYTE)CurrValue;
        break;

      case  TOK_NUMBEROFPLANES :

        if (CurrValue <= 1)
          CurrMemoryMap.BitPlanes = 0;
        else
        {
          CurrMemoryMap.BitPlanes = 1;

          while ((USHORT)--CurrValue)
          {
            CurrMemoryMap.BitPlanes <<= 1;
            CurrMemoryMap.BitPlanes |= 1;
          }
        }
        break;

      case  TOK_PAGELENGTH :
        CurrMemoryMap.PageLength = CurrValue;
        break;

      case  TOK_SAVESIZE :
        CurrMemoryMap.TotalSize = CurrValue;
        break;

      case TOK_TOTALMEMORY :
        SVGAHardware.Memory = CurrValue;                        /*          */
        break;

      case TOK_BUFFERADDRESS :
        CurrMemoryMap.Start.FullAddress = (VOID FAR *)CurrValue;
        break;

      case TOK_MMIOADDRESS :                                    /*          */
      case TOK_APERTURESIZE :                                   /*          */
      case TOK_INT10MODESET :                                   /*          */
        break;                                                  /*          */

      case TOK_INTERLACEMODE :
        CurrVideoMode.SVGAFlags = CurrValue;
        break;

      default  :
        /* Allow uplevel PMI files */                           /*          */
        /* return (TOK_ERROR_SYNTAX);*/     /* force an error *//*          */
#if LOGERROR                                                    /*          */
        LogError(TOK_ERROR_SYNTAX, errfname);                   /*          */
#endif                                                          /*          */
        break;                                                  /*          */
    }

    tok = NextToken(pmifile);          /* get next possible keyword */

  } while( ((tok > TOK_MI_BEGIN) && (tok < TOK_MI_END))         /*          */
           /* Allow uplevel PMI files */                        /*          */
           || (tok == TOK_ERROR_SYNTAX) );                      /*          */
  return (tok);

}

/***************************************************************************
 *
 * FUNCTION NAME = VARIndex()
 *
 * DESCRIPTION   =
 *
 *      Use current variable name to get its number for use as
 *      an index into the table of registers.
 *
 * INPUT         = NONE
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL = NONE
 * RETURN-ERROR  = NONE
 *
 **************************************************************************/

STATIC int NEAR VARIndex(VOID)
{
  int v = 0;
  char *p;

  p = &CurrVariable[1];
  v = (int)GetLongValue(p);
  return (v);
}

/***************************************************************************
 *
 * FUNCTION NAME = pbCmdMemory()
 *
 * DESCRIPTION   = Allocate requested amount of memory
 *
 * INPUT         = NONE
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL = NONE
 * RETURN-ERROR  = NONE
 *
 **************************************************************************/

STATIC VOID NEAR * NEAR pbCmdMemory(USHORT cb)
{
static  BOOL  fInitMem = FALSE;
static  VOID FAR *p = &fInitMem;
static  ULONG   ulOffset;

        if (!fInitMem)
        {
            if (DosSizeSeg(SELECTOROF(p), &ulOffset))   /* get size of data seg */
                return NULL;

            if (DosReallocSeg(0, SELECTOROF(p)))        /* realloc to 64k       */
                return NULL;

            fInitMem = TRUE;
        }
        ulOffset += cb;                                 /* update whats used    */
        return (ulOffset > 0x10000) ? NULL : (VOID NEAR *)(ulOffset-cb);
}

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

STATIC VOID NEAR AddHWCommand(FPHWCOMMAND * fpCmd, FPHWCOMMAND fpData)
{
        FPHWCOMMAND     pTmp;

        fpData->pNextCmd = NULL;
        fpData->pPrevCmd = NULL;
        if (!*fpCmd)
        {
            *fpCmd = pbCmdMemory(sizeof(HWCOMMAND));
            **fpCmd = *fpData;
        }
        else
        {
            for (pTmp = *fpCmd; pTmp->pNextCmd; pTmp=pTmp->pNextCmd);
            pTmp->pNextCmd = pbCmdMemory(sizeof(HWCOMMAND));
            *pTmp->pNextCmd = *fpData;
        }
}

/***************************************************************************
 *
 * FUNCTION NAME = ParseWait()
 *
 * DESCRIPTION   = Parse and return values from a WAIT() command.
 *
 *      WAIT(<status port>, <test mask>, <count>, <timeout>, <0|1>);
 *
 *   Examples:
 *      Wait(0x3da, 0x08, 1, 0, 1); wait until vert retrace starts
 *      Wait(0x3da, 0x08, 1, 0, 0); wait until vert retrace ends
 *
 * INPUT         = NONE
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL = NONE
 * RETURN-ERROR  = NONE
 *
 **************************************************************************/

STATIC PMITOKEN NEAR ParseWait(PMITOKEN tok,HFILE pmifile,
                               PUSHORT Port,
                               PUSHORT Mask,
                               PUSHORT Count,
                               PUSHORT Timeout,
                               PUSHORT SetClear)
{
  tok = NextToken(pmifile);     /* get LPAREN */
  if (tok != TOK_LPAREN)
    return (TOK_ERROR_SYNTAX);  /* force an error */

  tok = NextToken(pmifile);     /* get port value */
  if (tok != TOK_VALUE)
    return (TOK_ERROR_SYNTAX);  /* force an error */
  *Port = (USHORT)CurrValue;

  tok = NextToken(pmifile);     /* get test mask value */
  if (tok != TOK_VALUE)
    return (TOK_ERROR_SYNTAX);  /* force an error */
  *Mask = (USHORT)CurrValue;

  tok = NextToken(pmifile);     /* get count value */
  if (tok != TOK_VALUE)
    return (TOK_ERROR_SYNTAX);  /* force an error */
  *Count = (USHORT)CurrValue;

  tok = NextToken(pmifile);     /* get timeout value */
  if (tok != TOK_VALUE)
    return (TOK_ERROR_SYNTAX);  /* force an error */
  *Timeout = (USHORT)CurrValue;

  tok = NextToken(pmifile);     /* get SET/CLEAR value */
  if (tok != TOK_VALUE)
    return (TOK_ERROR_SYNTAX);  /* force an error */
  if ((*SetClear = (USHORT)CurrValue) > 1)
    return (TOK_ERROR_SYNTAX);  /* force an error */

  tok = NextToken(pmifile);     /* get RPAREN */
  if (tok != TOK_RPAREN)
    return (TOK_ERROR_SYNTAX);  /* force an error */

  tok = NextToken(pmifile);     /* get SEMICOLON */
  if (tok != TOK_SEMICOLON)
    tok = TOK_ERROR_SYNTAX;             /* force an error */
  return (tok);
}

/***************************************************************************
 *
 * FUNCTION NAME = ParseRMW()
 *
 * DESCRIPTION   = Parse and return values from an RMWB() command.
 *
 *      RMWB(<Index Port>, <Data Port>, <index>, <AND mask>, <OR mask>);
 *      RMWE(<Read Port>, <Write Port>, <AND mask>, <OR mask>);
 *      RMWW(<Read Port>, <Write Port>, <AND mask>, <OR mask>);
 *
 * INPUT         = NONE
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL = NONE
 * RETURN-ERROR  = NONE
 *
 **************************************************************************/

STATIC PMITOKEN NEAR ParseRMW(
  PMITOKEN tok,
  HFILE pmifile,
  FPHWCOMMAND pHWCmd )
{
  PMITOKEN      tokBW;

  tokBW = tok;                          /* TOK_RMWBYTE/TOK_RMWELSE/TOK_RMWWORD */
  tok = NextToken(pmifile);             /* get LPAREN */
  if (tok != TOK_LPAREN)
    return (TOK_ERROR_SYNTAX);          /* force an error */
  tok = NextToken(pmifile);             /* get index port value */
  if (tok != TOK_VALUE)
    return (TOK_ERROR_SYNTAX);          /* force an error */
  pHWCmd->usIndexPort = (USHORT)CurrValue;
  tok = NextToken(pmifile);             /* get data port value */
  if (tok != TOK_VALUE)
    return (TOK_ERROR_SYNTAX);          /* force an error */
  pHWCmd->usDataPort = (USHORT)CurrValue;
  if (tokBW == TOK_RMWBYTE)
  {
    tok = NextToken(pmifile);           /* get index value */
    if (tok != TOK_VALUE)
      return (TOK_ERROR_SYNTAX);        /* force an error */
    pHWCmd->usStartReg = (USHORT)CurrValue;
  }
  tok = NextToken(pmifile);             /* get AND Mask value */
  if (tok != TOK_VALUE)
    return (TOK_ERROR_SYNTAX);          /* force an error */
  pHWCmd->usANDMask = (USHORT)CurrValue;
  tok = NextToken(pmifile);             /* get OR Mask value */
  if (tok != TOK_VALUE)
    return (TOK_ERROR_SYNTAX);          /* force an error */
  pHWCmd->usORMask = (USHORT)CurrValue;
  tok = NextToken(pmifile);             /* get RPAREN */
  if (tok != TOK_RPAREN)
    return (TOK_ERROR_SYNTAX);          /* force an error */
  tok = NextToken(pmifile);             /* get SEMICOLON */
  if (tok != TOK_SEMICOLON)
    tok = TOK_ERROR_SYNTAX;             /* force an error */
  return (tok);
}

/***************************************************************************
 *
 * FUNCTION NAME = ParseOutCmd()
 *
 * DESCRIPTION   = Parse and return values from an OUTB() or OUTW() command.
 *
 *          OUTB(<IO Port>, <data byte>|<Variable>);
 *          OUTW(<IO Port>, <data word>|<Variable>);
 *
 * INPUT         = NONE
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL = NONE
 * RETURN-ERROR  = NONE
 *
 **************************************************************************/

STATIC PMITOKEN NEAR ParseOutCmd(
  PMITOKEN tok,
  HFILE pmifile,
  FPHWCOMMAND pHWCmd )
{
  tok = NextToken(pmifile);            /* get LPAREN */
  if (tok != TOK_LPAREN)
    return (TOK_ERROR_SYNTAX);         /* force an error */
  tok = NextToken(pmifile);            /* get port value */
  if (tok != TOK_VALUE)
    return (TOK_ERROR_SYNTAX);         /* force an error */
  pHWCmd->usIndexPort = (USHORT)CurrValue;
  tok = NextToken(pmifile);            /* get data/variable value */
  if( tok == TOK_VALUE )
  {
    pHWCmd->usFlags = PMIFLAG_NONE;
    pHWCmd->usData = (USHORT)CurrValue;
  }
  else if( tok == TOK_VARIABLE )
  {
    pHWCmd->usFlags = PMIFLAG_REGISTER;
    pHWCmd->usStartReg = VARIndex();   /* get variable index */
  }
  else
    return (TOK_ERROR_SYNTAX);         /* force an error */
  tok = NextToken(pmifile);            /* get RPAREN */
  if (tok != TOK_RPAREN)
    return (TOK_ERROR_SYNTAX);         /* force an error */
  tok = NextToken(pmifile);            /* get SEMICOLON */
  if (tok != TOK_SEMICOLON)
    tok = TOK_ERROR_SYNTAX;            /* force an error */
  return (tok);
}

/***************************************************************************
 *
 * FUNCTION NAME = ParseInCmd()
 *
 * DESCRIPTION   = Parse and return values from an INB() or INW() command.
 *
 *          INB(<Variable>, <IO Port>);
 *          INW(<Variable>, <IO Port>);
 *
 * INPUT         = NONE
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL = NONE
 * RETURN-ERROR  = NONE
 *
 **************************************************************************/

STATIC PMITOKEN NEAR ParseInCmd(
  PMITOKEN tok,
  HFILE pmifile,
  FPHWCOMMAND pHWCmd )
{
  tok = NextToken(pmifile);             /* get LPAREN */
  if (tok != TOK_LPAREN)
    return (TOK_ERROR_SYNTAX);          /* force an error */
  tok = NextToken(pmifile);             /* get VARiable */
  if (tok != TOK_VARIABLE)
    return (TOK_ERROR_SYNTAX);          /* force an error */
  pHWCmd->usFlags = PMIFLAG_REGISTER;
  pHWCmd->usStartReg = VARIndex();      /* get variable index */
  tok = NextToken(pmifile);             /* get PORT value */
  if (tok != TOK_VALUE)
    return (TOK_ERROR_SYNTAX);          /* force an error */
  pHWCmd->usIndexPort = (USHORT)CurrValue;
  tok = NextToken(pmifile);             /* get RPAREN */
  if (tok != TOK_RPAREN)
    return (TOK_ERROR_SYNTAX);          /* force an error */
  tok = NextToken(pmifile);             /* get SEMICOLON */
  if (tok != TOK_SEMICOLON)
    return (TOK_ERROR_SYNTAX);          /* force an error */
  return (tok);
}

/***************************************************************************
 *
 * FUNCTION NAME = ParseBlockCmd()
 *
 * DESCRIPTION   = Parse and return values from an BOUTB() or BINB() command.
 *
 *          BINB(<no of values>, <start index>, <Index Port>, <Data Port>);
 *          BOUTB(<no of values>, <start index>, <Index Port>, <Data Port>);
 *
 * INPUT         = NONE
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL = NONE
 * RETURN-ERROR  = NONE
 *
 **************************************************************************/

STATIC PMITOKEN NEAR ParseBlockCmd(PMITOKEN tok,
                                   HFILE pmifile,
                                   PUSHORT Count,
                                   PUSHORT StartIndex,
                                   PUSHORT IndexPort,
                                   PUSHORT DataPort)
{

  tok = NextToken(pmifile);      /* get LPAREN */

  if (tok != TOK_LPAREN)
    return (TOK_ERROR_SYNTAX);   /* force an error */

  tok = NextToken(pmifile);      /* get count */

  if (tok != TOK_VALUE)
    return (TOK_ERROR_SYNTAX);   /* force an error */

  *Count = (USHORT)CurrValue;
  tok = NextToken(pmifile);      /* get start index */

  if (tok != TOK_VALUE)
    return (TOK_ERROR_SYNTAX);   /* force an error */

  *StartIndex = (USHORT)CurrValue;
  tok = NextToken(pmifile);      /* get Index Port */

  if (tok != TOK_VALUE)
    return (TOK_ERROR_SYNTAX);   /* force an error */

  *IndexPort = (USHORT)CurrValue;
  tok = NextToken(pmifile);      /* get Data Port */

  if (tok != TOK_VALUE)
    return (TOK_ERROR_SYNTAX);   /* force an error */

  *DataPort = (USHORT)CurrValue;
  tok = NextToken(pmifile);      /* get RPAREN */

  if (tok != TOK_RPAREN)
    return (TOK_ERROR_SYNTAX);   /* force an error */

  tok = NextToken(pmifile);      /* get SEMICOLON */

  if (tok != TOK_SEMICOLON)
    tok = TOK_ERROR_SYNTAX;             /* force an error */
  return (tok);

}

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

STATIC PMITOKEN NEAR ParseVariable(
  PMITOKEN tok,
  HFILE pmifile,
  FPHWCOMMAND pHWCmd )
{
  pHWCmd->usStartReg = VARIndex();      /* get variable index */
  tok = NextToken(pmifile);             /* get equals or bitwise operator */
  switch (tok)
  {
    case TOK_EQUALS:
      pHWCmd->usIndexPort = 0;
      break;
    case TOK_REGOP_AND:
      pHWCmd->usIndexPort = PMI_REGOP_AND;
      tok = NextToken(pmifile);         /* get equals */
      break;
    case TOK_REGOP_OR:
      pHWCmd->usIndexPort = PMI_REGOP_OR;
      tok = NextToken(pmifile);         /* get equals */
      break;
    case TOK_REGOP_XOR:
      pHWCmd->usIndexPort = PMI_REGOP_XOR;
      tok = NextToken(pmifile);         /* get equals */
      break;
    case TOK_REGOP_SHL:
      pHWCmd->usIndexPort = PMI_REGOP_SHL;
      tok = NextToken(pmifile);         /* get equals */
      if( tok == TOK_REGOP_SHL )                                /*          */
        tok = NextToken(pmifile);       /* get equals */        /*          */
      break;
    case TOK_REGOP_SHR:
      pHWCmd->usIndexPort = PMI_REGOP_SHR;
      tok = NextToken(pmifile);         /* get equals */
      if( tok == TOK_REGOP_SHR )                                /*          */
        tok = NextToken(pmifile);       /* get equals */        /*          */
      break;
    default:
      return (TOK_ERROR_SYNTAX);        /* force an error */
  }
  if (tok != TOK_EQUALS)
    return (TOK_ERROR_SYNTAX);          /* force an error */
  tok = NextToken(pmifile);             /* get value */
  if( tok == TOK_VARIABLE )                                     /*          */
  {                                                             /*          */
    pHWCmd->usFlags = PMIFLAG_REGISTER;                         /*          */
    pHWCmd->usData = VARIndex();                                /*          */
  }                                                             /*          */
  else if (tok == TOK_VALUE)                                    /*          */
  {
    pHWCmd->usFlags = PMIFLAG_NONE;
    pHWCmd->usData = (USHORT)CurrValue;
  }
  else
    tok = TOK_ERROR_SYNTAX;             /* force an error */
  return ( tok );
}

/***************************************************************************
 *
 * FUNCTION NAME = ProcessCmdSection()
 *
 * DESCRIPTION   = Commands in the [SETMODE] section are processed here.
 *
 * INPUT         = NONE
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL = NONE
 * RETURN-ERROR  = NONE
 *
 **************************************************************************/

STATIC PMITOKEN NEAR ProcessCmdSection(PMITOKEN tok,
                                       HFILE pmifile,
                                       FPHWCOMMAND * pHWCmd)
{
  USHORT DataPort,IndexPort,StartIndex,Count;
  HWCOMMAND HWCmd = {0};

  *pHWCmd = NULL;
  tok = NextToken(pmifile);

  if( tok == TOK_SETBANKCALL )                                  /*          */
  {                                                             /*          */
    tok = NextToken( pmifile );                                 /*          */
    if( tok != TOK_SEMICOLON )                                  /*          */
      return( TOK_ERROR_SYNTAX );   /* force an error */        /*          */
    tok = NextToken( pmifile );                                 /*          */
    *pHWCmd = pHWCurrSetBank;                                   /*          */
  }                                                             /*          */
  else                                                          /*          */
  {
    do
    {

      if ((tok <= TOK_SM_BEGIN) || (tok >= TOK_SM_END))
        return (TOK_ERROR_SYNTAX);       /* force an error */

      switch (tok)
      {

        case  TOK_INBYTE :
        case  TOK_INWORD :
          if (tok == TOK_INBYTE)
            HWCmd.usCommand = PMICMD_INB;
          else
            HWCmd.usCommand = PMICMD_INW;
          tok = ParseInCmd( tok,
                            pmifile,
                            &HWCmd );
          if (tok == TOK_ERROR_SYNTAX)
            return TOK_ERROR_SYNTAX;
          AddHWCommand(pHWCmd, &HWCmd);
          break;

        case  TOK_OUTBYTE :
        case  TOK_OUTWORD :
          if (tok == TOK_OUTBYTE)
            HWCmd.usCommand = PMICMD_OUTB;
          else
            HWCmd.usCommand = PMICMD_OUTW;
          tok = ParseOutCmd( tok,
                             pmifile,
                             &HWCmd );
          if( tok == TOK_ERROR_SYNTAX )
            return TOK_ERROR_SYNTAX;
          AddHWCommand(pHWCmd, &HWCmd);
          break;

        case  TOK_OUTBLOCK :
          tok = ParseBlockCmd(tok, pmifile, &Count, &StartIndex, &IndexPort, &DataPort);
          if (tok == TOK_ERROR_SYNTAX)
            return TOK_ERROR_SYNTAX;
          HWCmd.usCommand   = PMICMD_BOUTB;
          HWCmd.usFlags = (IndexPort == ATTADDRESSPORT) ? PMIFLAG_ATC : PMIFLAG_NONE;
          HWCmd.usIndexPort = IndexPort;
          HWCmd.usDataPort  = DataPort;
          HWCmd.usStartReg  = StartIndex;
          HWCmd.usNumRegs   = Count;
          AddHWCommand(pHWCmd, &HWCmd);
          break;

        case  TOK_INBLOCK :
          tok = ParseBlockCmd(tok, pmifile, &Count, &StartIndex, &IndexPort, &DataPort);
          if (tok == TOK_ERROR_SYNTAX)
            return TOK_ERROR_SYNTAX;
          HWCmd.usCommand   = PMICMD_BINB;
          HWCmd.usFlags = (IndexPort == ATTADDRESSPORT) ? PMIFLAG_ATC : PMIFLAG_NONE;
          HWCmd.usIndexPort = IndexPort;
          HWCmd.usDataPort  = DataPort;
          HWCmd.usStartReg  = StartIndex;
          HWCmd.usNumRegs   = Count;
          AddHWCommand(pHWCmd, &HWCmd);
          break;

        case  TOK_RMWBYTE :
        case  TOK_RMWELSE :
        case  TOK_RMWWORD :
          if (tok == TOK_RMWBYTE)
            HWCmd.usCommand = PMICMD_RMWB;
          else if (tok == TOK_RMWELSE)                          /*          */
            HWCmd.usCommand = PMICMD_RMWE;                      /*          */
          else
            HWCmd.usCommand = PMICMD_RMWW;
          HWCmd.usFlags = PMIFLAG_NONE;
          tok = ParseRMW( tok,
                          pmifile,
                          &HWCmd );
          if (tok == TOK_ERROR_SYNTAX)
            return TOK_ERROR_SYNTAX;
          AddHWCommand(pHWCmd, &HWCmd);
          break;

        case  TOK_WAIT :
          HWCmd.usFlags   = PMIFLAG_NONE;
          HWCmd.usCommand = PMICMD_WAIT;
          tok = ParseWait(tok, pmifile,
                         &HWCmd.usIndexPort, /* port      */
                         &HWCmd.usANDMask,   /* mask      */
                         &HWCmd.usStartReg,  /* count     */
                         &HWCmd.usData,      /* timeout   */
                         &HWCmd.usDataPort); /* set/clear */
          if (tok == TOK_ERROR_SYNTAX)
            return TOK_ERROR_SYNTAX;
          AddHWCommand(pHWCmd, &HWCmd);
          break;

        case  TOK_VARIABLE :
          HWCmd.usCommand = PMICMD_REGOP;
          tok = ParseVariable( tok,
                               pmifile,
                               &HWCmd );
          if( HWCmd.usStartReg > 255 )  /* in range?      */
            return TOK_ERROR_VARIABLE;  /* force an error */
          if( tok == TOK_ERROR_SYNTAX )
            return TOK_ERROR_SYNTAX;
          AddHWCommand(pHWCmd, &HWCmd);
          break;
      }
      tok = NextToken(pmifile);
    }
    while ((tok > TOK_SM_BEGIN) && (tok < TOK_SM_END));
  }
  return (tok);
}
                                                                /*          */
/***************************************************************************
 *
 * FUNCTION NAME = SkipSection()
 *
 * DESCRIPTION   =
 *
 * INPUT         = NONE
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL = NONE
 * RETURN-ERROR  = NONE
 *
 **************************************************************************/

STATIC PMITOKEN NEAR SkipSection(
  PMITOKEN tok,
  HFILE pmifile )
{
  do
    tok = NextToken( pmifile );
  while( (tok > TOK_SECT_END) && (tok != TOK_EOF) );
  return( tok );
}

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

STATIC PMITOKEN NEAR ProcessTrapRegs(PMITOKEN tok,HFILE pmifile)
{
  return( SkipSection( tok, pmifile ) );
}

/*****************************************************************************
 *
 * FUNCTION NAME = GrowSeg()
 *
 * DESCRIPTION   =
 *
 *      Grow Mode and MemoryMap tables as required.
 *      Call AllocSeg if nothing allocated yet, otherwise Realloc.
 *
 * INPUT         = NONE
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL = NONE
 * RETURN-ERROR  = NONE
 *
 ****************************************************************************/

STATIC USHORT NEAR GrowSeg(VOID FAR **p,USHORT size)
{
  SEL sel;
  USHORT rc;

  sel = SELECTOROF(*p);

  if (sel)
    rc = DosReallocSeg(size, sel);
  else
    rc = DosAllocSeg(size, &sel, 0);

  SELECTOROF(*p) = sel;
  return  rc;
}

/*****************************************************************************
 *
 * FUNCTION NAME = GrowTables
 *
 * DESCRIPTION   = Grow each of the memory and mode tables to a new size.
 *
 * INPUT         = NONE
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL = NONE
 * RETURN-ERROR  = NONE
 *
 ****************************************************************************/

STATIC USHORT NEAR GrowTables(USHORT i)
{
  USHORT rc;
  ++i;

  if (!(rc = GrowSeg(&pModes, i *sizeof(VIDEOMODE))))
    rc = GrowSeg(&pMemoryMaps, i *sizeof(MEMORYMAPS));

  return  rc;
}

/*****************************************************************************
 *
 * FUNCTION NAME = MoveTables
 *
 * DESCRIPTION   =
 *
 *      The two tables containing mode and memory map data, are currently
 *      stored in data allocated with a DosAlloc, somewhere above. This
 *      routine grows the existing static global data area, where all of
 *      the BVHSVGA global variables are located, to accomodate that data.
 *      The tables are then copied to the new locations, at the end of the
 *      existing data area. The previous tables are freed and their old
 *      pointers updated to point to the data.
 *
 * INPUT         = NONE
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL = NONE
 * RETURN-ERROR  = NONE
 *
 ****************************************************************************/

STATIC USHORT NEAR MoveTables(VOID)
{
  USHORT rc;
  ULONG ulModeSize,ulMapSize,ulOldSize;
  VOID FAR *p = &pModes;

  if (rc = DosSizeSeg(SELECTOROF(pModes), &ulModeSize))
    return  rc;

  if (rc = DosSizeSeg(SELECTOROF(pMemoryMaps), &ulMapSize))
    return  rc;
                        /* dummy call to get end of used memory */
                        /* we've already expanded DGROUP to 64k */
  ulOldSize = (USHORT)pbCmdMemory(0);

  OFFSETOF(p) = (USHORT)ulOldSize;
  memcpy((PVIDEOMODE)p, pModes, (USHORT)ulModeSize);
  DosFreeSeg(SELECTOROF(pModes));

  pModes = p;
  OFFSETOF(p) += (USHORT)ulModeSize;
  memcpy((PVIDEOMODE)p, pMemoryMaps, (USHORT)ulMapSize);
  DosFreeSeg(SELECTOROF(pMemoryMaps));

  pMemoryMaps = p;
  return  FALSE;

}

/*****************************************************************************
 *
 * FUNCTION NAME = UpdateModeTable()
 *
 * DESCRIPTION   =
 *
 *      If the data just read in is for the adapter currently
 *      installed, then
 *          Using CurrVideoMode update the appropriate Mode table entry.
 *          Using CurrMemoryMap update the appropriate Memory Map entry.
 *
 *                     REWRITTEN FOR DYNAMIC MODE TABLE ALLOCATION.
 *
 * INPUT         = NONE
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL = NONE
 * RETURN-ERROR  = NONE
 *
 ****************************************************************************/

STATIC int NEAR UpdateModeTable(VOID)
{
  int rc = FALSE;
  static USHORT ModeIndex = 0;

  CurrVideoMode.Flags = IGNORE_CLR_BRST;

  if (!(CurrVideoMode.fbType&MODE_FLAG_GRAPHICS))
    CurrMemoryMap.BitPlanes = 0;
  else

    if ((CurrVideoMode.hres <= 640) && (CurrVideoMode.vres <= 480) &&
       (CurrVideoMode.color <= 4))

      CurrVideoMode.fbType &= ~MODE_FLAG_NATIVE;
  CurrVideoMode.MemMap = (BYTE)ModeIndex;

  if (!GrowTables(ModeIndex))
  {
    pModes[ModeIndex] = CurrVideoMode;
    pMemoryMaps[ModeIndex] = CurrMemoryMap;
    usMaxVideoModes++;
  }
  ModeIndex++;
  rc = TRUE;
                                /*            clear out for new mode */
  memset(&CurrVideoMode, 0, sizeof(VIDEOMODE));
  return(rc);
}

/*****************************************************************************
 *
 * FUNCTION NAME = ProcessPMIFile()
 *
 * DESCRIPTION   =
 *
 *      The main loop of this module which processes a stream of
 *      tokens to parse the input text file. Video Mode Data structures
 *      are updated as this proceeds.
 *
 * INPUT         = NONE
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL = NONE
 * RETURN-ERROR  = NONE
 *
 ****************************************************************************/

int NEAR ProcessPMIFile(char *pmifname)
{
  char fname[MAX_PATH];
  char errfname[MAX_PATH];
  HFILE pmifile;
  USHORT rc,pmiaction,ModeDataUpdated = 0;
  PMITOKEN tok;
  char *p;

#if 0                   /*            */
  if (!DosSearchPath(2, "DPATH", pmifname, fname, sizeof(fname)))
  {
    strcpy(errfname, fname);
    p = errfname;
    while (*p && (*p != '.'))
      p++;
    strcpy(p, ".err");
  }

#else                   /*            */
{
  PGINFOSEG pGlobalInfoSeg;

  SELECTOROF(pGlobalInfoSeg) = GlobalInfoSeg;
  OFFSETOF(pGlobalInfoSeg) = 0;

  strcpy(fname, SVGA_DATAFNAME);
  fname[0] = (char)(pGlobalInfoSeg->bootdrive + 'a' - 1); /* 1=A, 2=B etc. */

  strcpy(errfname, fname);
  p = errfname;
  while (*p && (*p != '.'))
    p++;
  strcpy(p, ".err");

}
#endif

  if (!(rc = DosOpen(fname, &pmifile, &pmiaction, 0L, FILE_NORMAL, FILE_OPEN,
     OPEN_ACCESS_READONLY|OPEN_SHARE_DENYWRITE|OPEN_FLAGS_SEQUENTIAL, NULL)))
  {
    NextChar(pmifile);                 /* look ahead the 1st one */
    tok = NextToken(pmifile);

    do
    {

      switch (tok)
      {
        case  TOK_BLOCKCOMMENT :
          tok = SkipSection( tok, pmifile );                    /*          */
          break;

        case  TOK_ADAPTERTYPE :
          tok = NextToken(pmifile);

          if ((tok < TOK_AT_BEGIN) || (tok > TOK_AT_END))
          {
            tok = TOK_ERROR_ADAPTER;   /* force an error */
            break;
          }
          CurrVideoMode.AdapterType = tok-TOK_AT_BEGIN;
          tok = NextToken(pmifile);
          break;

        case  TOK_MONITORTYPE :        /*            */
          tok = NextToken(pmifile);

          if ((tok < TOK_MT_BEGIN) || (tok > TOK_MT_END))
          {
            tok = TOK_ERROR_MONITOR;   /* force an error */
            break;
          }
          SVGAMonitorType = (USHORT)(tok-TOK_MT_BEGIN-1);
          tok = NextToken(pmifile);
          break;

        case  TOK_CHIPSET :
          tok = NextToken(pmifile);

          if ((tok < TOK_CT_BEGIN) || (tok > TOK_CT_END))
          {
            tok = TOK_ERROR_CHIPSET;   /* force an error */
            break;
          }
          CurrVideoMode.ChipVersion = ChipTable[tok-TOK_CT_BEGIN-1];
          tok = NextToken(pmifile);
          break;

        case  TOK_MODEINFO :
          tok = ProcessModeInfo(tok, pmifile, errfname);
          break;

        case  TOK_SETMODE :
          tok = ProcessCmdSection(tok, pmifile, &CurrVideoMode.pHWSetModeData);

          if (tok < TOK_ERROR_SYNTAX)
            ModeDataUpdated |= UpdateModeTable();
          break;

        case  TOK_TRAPREGS :
          tok = ProcessTrapRegs(tok, pmifile);
          break;

        case  TOK_UNLOCK :
          if (pHWUnLockData)
            tok = SkipSection( tok, pmifile );                  /*          */
          else
            tok = ProcessCmdSection(tok, pmifile, &pHWUnLockData);
          break;

        case  TOK_LOCK :
          if (pHWUnLockData)
            tok = SkipSection( tok, pmifile );                  /*          */
          else
            tok = ProcessCmdSection(tok, pmifile, &pHWLockData);
          break;

        case  TOK_CLEANUP :
          if (pHWCleanData)                                     /*          */
            tok = SkipSection( tok, pmifile );                  /*          */
          else                                                  /*          */
            tok = ProcessCmdSection( tok,                       /*          */
                                     pmifile,                   /*          */
                                     &pHWCleanData);            /*          */
          break;

        case TOK_SETBANK :
          tok = ProcessCmdSection(tok, pmifile, &CurrVideoMode.pHWSetBank);
          break;

        case TOK_SETBANKLINEAR :                                /*          */
          if( pHWCurrSetBank )                                  /*          */
            tok = SkipSection( tok, pmifile );                  /*          */
          else                                                  /*          */
            tok = ProcessCmdSection( tok,                       /*          */
                                     pmifile,                   /*          */
                                     &pHWCurrSetBank );         /*          */
          break;                                                /*          */

        case TOK_GETBANK :                                      /*          */
        case TOK_GETBANKLINEAR :                                /*          */
          tok = SkipSection( tok, pmifile );                    /*          */
          break;                                                /*          */

        default  :
          break;

      }
      if (tok >= TOK_ERROR)
      {
#if LOGERROR                                                    /*          */
        LogError(tok, errfname);
#endif                                                          /*          */
        tok = SkipSection( tok, pmifile );                      /*          */
      }

    }
    while (tok != TOK_EOF);

    DosClose(pmifile);

    if (!ModeDataUpdated || MoveTables())
      rc = -1;                                      /*                        */

  }
  return (rc);
}
