/*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.                                */
/*                                                                           */
/*****************************************************************************/
/**************************************************************************
 *
 * SOURCE FILE NAME = VVPMI.C
 *
 * DESCRIPTIVE NAME = Virtual Video PMI File Processing
 *
 *
 * VERSION = V2.0
 *
 * DATE      02/07/92
 *
 * 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/save/restore the video
 *              modes supported.
 *
 *              The file is parsed and local data structures updated
 *              with the appropriate information.
 *
 *              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
 *              vvPMIGetc()             Return the next character from the PMI file.
 *              vvPMINextChar()         Calls pmiGetc() to return the next character.
 *              vvPMIGetLongValue()     Convert ASCII string to unsigned long.
 *              vvPMINextToken()        Returns the next token and updates appropriate globals.
 *              vvPMISkipSection()      This reads and discards tokens.
 *              vvPMIParseOutB()        Parse and return values from an OUTB() command.
 *              vvPMIProcessTrapRegs()  Process trap registers.
 *              vvPMIProcessCmdSection()Load PMI command section
 *              vvPMIProcessFile()      The main loop of this module.
 *
 *
 * NOTES
 *
 *
 * STRUCTURES
 *
 * EXTERNAL REFERENCES
 *
 * EXTERNAL FUNCTIONS
 *
*/


#include <mvdm.h>
#include <vvd.h>
#include "vvdp.h"
#ifdef VDDSTRICT
MODNAME = __FILE__;
#endif
 /*
 **      Externals
 */

extern PPLE appleAll[];
extern PLE pleATCx;                                             /*          */
extern PLE pleATC0;                                             /*          */

extern BOOL     flInt10Disabled;                                /*            */
extern SSVGAINFO sATIAdapterInfo;                               /*          */
extern SSVGAINFO sCirrusAdapterInfo;                            /*          */
extern SSVGAINFO sIBMAdapterInfo;                               /*          */
extern SSVGAINFO sS3AdapterInfo;                                /*          */
extern SSVGAINFO sTridentAdapterInfo;                           /*          */
extern SSVGAINFO sTsengAdapterInfo;                             /*          */
extern SSVGAINFO sVGAAdapterInfo;                               /*          */
extern SSVGAINFO sVideo7AdapterInfo;                            /*          */
extern SSVGAINFO sWDAdapterInfo;                                /*          */

extern PSSVGAINFO psCurAdapterInfo;                             /*          */
extern PSACCELERATORINFO psCurAcceleratorInfo;                  /*          */
extern PSDACINFO psCurDacInfo;                                  /*          */
extern PSDACINFO apsAllDacInfo [MAX_DAC];                       /*          */

extern HWCMD aHWCmdSVGA [AHWCMDSVGASIZE];                       /*          */
extern USHORT usaHWCmdSVGANext;                                 /*          */
extern PHWCMD pHWCmdSVGALockData;                               /*          */
extern PHWCMD pHWCmdSVGAUnLockData;                             /*          */
extern PHWCMD pHWCmdSVGACleanData;                /*          *//*          */
extern PHWCMD pHWCmdSVGASetBankData;                            /*          */
extern PHWCMD pHWCmdSVGAGetBankData;                            /*          */

#define LOGERROR                1

#pragma intrinsic(strcpy, strcmp, strlen, strcat, memset)

#define MAX_PATH                260
#define MAX_VIDEO_MODES         9

#ifdef NOT_V21MNH00                                             /*          */
#define ATTADDRESSPORT          0x03C0
#define SEQADDRESSPORT          0x03C4
#define GRAPHADDRESSPORT        0x03CE
#define CRTCADDRESSPORT         0x03D4
#endif /* NOT_V21MNH00 */                                       /*          */

#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,                       /* Section Heading tokens */
  TOK_PMIVERSION,                                               /*          */
  TOK_HARDWARE,                                                 /*          */
  TOK_IDENTIFYADAPTER,                                          /*          */
  TOK_ADAPTERTYPE,
  TOK_MONITORTYPE,
  TOK_CHIPSET,
  TOK_BLOCKCOMMENT,
  TOK_INCLUDECODE,                                              /*          */
  TOK_SETBANKLINEAR,                                            /*          */
  TOK_GETBANKLINEAR,                                            /*          */
  TOK_PROGRAMDAC,                                               /*          */
  TOK_MODEINFO,
  TOK_SETMODE,
  TOK_TRAPREGS,
  TOK_LOCK,
  TOK_UNLOCK,
  TOK_CLEANUP,
  TOK_SETBANK,                                                  /*          */
  TOK_GETBANK,                                                  /*          */
  TOK_SECT_END,

  TOK_HARDWARE_BEGIN,                                           /*          */
  TOK_BUSTYPE,                                                  /*          */
  TOK_OEMSTRING,                                                /*          */
  TOK_VERSION,                                                  /*          */
  TOK_DACSTRING,                                                /*          */
  TOK_TOTALMEMORY,                                              /*          */
  TOK_MMIOADDRESS,                                              /*          */
  TOK_ENDIAN,                                                   /*          */
  TOK_HARDWARE_END,                                             /*          */

  TOK_BYTE_IOPORT,                                              /*          */
  TOK_WORD_IOPORT,                                              /*          */

  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_INTERLACEMODE,
  TOK_BUFFERADDRESS,
  TOK_APERTURESIZE,                                             /*          */
  TOK_INT10MODESET,                                             /*          */
  TOK_MI_END,

  TOK_SM_BEGIN,
  TOK_INBYTE,                           /* SetMode Section tokens */
  TOK_OUTBYTE,
  TOK_INWORD,                                                   /*          */
  TOK_OUTWORD,
  TOK_INBLOCK,                                                  /*          */
  TOK_OUTBLOCK,
  TOK_RMWBI,                                                    /*          */
  TOK_RMWBN,                                                    /*          */
  TOK_RMWWN,                                                    /*          */
  TOK_WAIT,                                                     /*          */
  TOK_WAITW,                                                    /*          */
  TOK_SETBANKCALL,                                              /*          */
  TOK_GETBANKCALL,                                              /*          */
  TOK_VALUE,
  TOK_VARIABLE,
  TOK_STRING,                                                   /*          */
  TOK_LPAREN,
  TOK_RPAREN,
  TOK_EQUALS,
  TOK_SEMICOLON,
  TOK_HYPHEN,
  TOK_REGOP_AND,                                                /*          */
  TOK_REGOP_OR,                                                 /*          */
  TOK_REGOP_XOR,                                                /*          */
  TOK_REGOP_SHL,                                                /*          */
  TOK_REGOP_SHR,                                                /*          */
  TOK_SM_END,

  TOK_NULL,                                                     /*          */
  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,                                            /*          */
  TOK_UNKNOWN,                                                  /*          */

}       PMITOKEN;

typedef struct _TOK

{
        CHAR            * tok_txt;
        PMITOKEN        tok_val;

}       TOK;

typedef struct _VALIDMODES

{
        USHORT          hres;
        USHORT          vres;
        BYTE            color;

}       VALIDMODES;

#pragma BEGIN_SWAP_DATA

 /*
 **      Local data
 */

OEMSVGAINFO sSVGA;                                              /*          */
OEMINFO sOEMData;                                               /*          */
ULONG   ulSVGAAdapterType = 0;                                  /*          */
ULONG   ulSVGAChipType = 0;                                     /*          */
ULONG   ulSVGAVRAMSize = 0;                                     /*          */

BOOL    fPMIProcessed = FALSE;                                  /*          */
BOOL    fPMILoaded = FALSE;                                     /*          */
CHAR    pszPMIStrIn [LINELEN];

TOK     Tokens[] =

{
  "[PMIVERSION]",       TOK_PMIVERSION,                         /*          */
  "[HARDWARE]",         TOK_HARDWARE,                           /*          */
  "[IDENTIFYADAPTER]",  TOK_IDENTIFYADAPTER,                    /*          */
  "[ADAPTERTYPE]",      TOK_ADAPTERTYPE,
  "[CHIPSET]",          TOK_CHIPSET,
  "[COMMENT]",          TOK_BLOCKCOMMENT,
  "#INCLUDECODE",       TOK_INCLUDECODE,                        /*          */
  "[SETBANKLINEAR]",    TOK_SETBANKLINEAR,                      /*          */
  "[GETBANKLINEAR]",    TOK_GETBANKLINEAR,                      /*          */
  "[PROGRAMDAC]",       TOK_PROGRAMDAC,                         /*          */
  "[MODEINFO]",         TOK_MODEINFO,
  "[SETMODE]",          TOK_SETMODE,
  "[TRAPREGS]",         TOK_TRAPREGS,
  "[LOCK]",             TOK_LOCK,
  "[UNLOCK]",           TOK_UNLOCK,
  "[CLEANUP]",          TOK_CLEANUP,
  "[MONITORTYPE]",      TOK_MONITORTYPE,
  "[SETBANK]",          TOK_SETBANK,                            /*          */
  "[GETBANK]",          TOK_GETBANK,                            /*          */

  "BUSTYPE",            TOK_BUSTYPE,                            /*          */
  "OEMSTRING",          TOK_OEMSTRING,                          /*          */
  "VERSION",            TOK_VERSION,                            /*          */
  "DACSTRING",          TOK_DACSTRING,                          /*          */
  "TOTALMEMORY",        TOK_TOTALMEMORY,                        /*          */
  "MEMORYIOADDRESS",    TOK_MMIOADDRESS,                        /*          */
  "ENDIAN",             TOK_ENDIAN,                             /*          */

  "BYTE_IOPORT",        TOK_BYTE_IOPORT,                        /*          */
  "WORD_IOPORT",        TOK_WORD_IOPORT,                        /*          */

  "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,
  "INTERLACEMODE",      TOK_INTERLACEMODE,
  "BUFFERADDRESS",      TOK_BUFFERADDRESS,
  "APERTURESIZE",       TOK_APERTURESIZE,                       /*          */
  "INT10MODESET",       TOK_INT10MODESET,                       /*          */

  "NULL",               TOK_NULL,                               /*          */

  "INB",                TOK_INBYTE,
  "OUTB",               TOK_OUTBYTE,
  "INW",                TOK_INWORD,                             /*          */
  "OUTW",               TOK_OUTWORD,
  "BINB",               TOK_INBLOCK,                            /*          */
  "BOUTB",              TOK_OUTBLOCK,
  "RMWBI",              TOK_RMWBI,                              /*          */
  "RMWB",               TOK_RMWBI,                              /*          */
  "RMWBN",              TOK_RMWBN,                              /*          */
  "RMWE",               TOK_RMWBN,                              /*          */
  "RMWW",               TOK_RMWBN,                              /*          */
  "RMWWN",              TOK_RMWWN,                              /*          */
  "WAIT",               TOK_WAIT,                               /*          */
  "WAITW",              TOK_WAITW,                              /*          */
  "SETBANKLINEAR",      TOK_SETBANKCALL,                        /*          */
  "GETBANKLINEAR",      TOK_GETBANKCALL,                        /*          */
};

USHORT          ColNo  = 0;
USHORT          LineNo = 1;

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

VALIDMODES      SupportedModes[MAX_VIDEO_MODES] =

{
        {640,  480, 8},
        {800,  600, 4},
        {800,  600, 8},
        {1024, 768, 4},
        {1024, 768, 8},
        {1280,1024, 8},
        {132,  25,  4},
        {132,  43,  4},
        {132,  44,  4},
};


struct _SVGATABLE                               /*            */
{
 PSSVGAINFO     apsInfo;
 ULONG          flAdapterFlag;           //           INT10restore, bgnd exec and future need
}apsAllAdapterInfo[SVGA_LASTADAPTER + 0x01] =
{
  {&sVGAAdapterInfo, 0},                        /* 00 = UNKNOWN_ADAPTER    */
  {&sVideo7AdapterInfo, 0},                     /* 01 = VIDEO7_ADAPTER     */
  {&sTridentAdapterInfo,0},                     /* 02 = TRIDENT_ADAPTER    */
  {&sTsengAdapterInfo,VDMX_INT10RESTORE},       /* 03 = TSENG_ADAPTER      */
  {&sWDAdapterInfo,0},                          /* 04 = WESTERNDIG_ADAPTER */
  {&sATIAdapterInfo,0},                         /* 05 = ATI_ADAPTER        */
  {&sIBMAdapterInfo,0},                         /* 06 = IBM_ADAPTER        */
  {&sCirrusAdapterInfo,0},                      /* 07 = CIRRUS_ADAPTER     */
  {&sS3AdapterInfo,VDMX_INT10RESTORE},          /* 08 = S3_ADAPTER         */
  {&sVGAAdapterInfo,VDMX_INT10RESTORE},         /* 09 = CHIPS_ADAPTER      */
  {&sVGAAdapterInfo,0}                          /* 10 = WEITEK_ADAPTER     */
};

#pragma END_SWAP_DATA

#pragma BEGIN_SWAP_CODE


/***************************************************************************
 *
 * FUNCTION NAME = vvPMIGetc()
 *
 * 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        = CHAR - Next character from PMI file.
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 **************************************************************************/

CHAR    PRIVENTRY vvPMIGetc(
  HFILE f )
{
static  CHAR    buffer[BUFFSIZE];                       /* read buffer */
static  ULONG   buffpos = 0, buffcount = 0;             /* buffer ptrs */
        ULONG   bytesread;

        if (buffpos == buffcount)
        {
            bytesread = VDHRead(f, &buffer, BUFFSIZE);
            if (!bytesread || (bytesread == -1))
            {
                buffpos = buffcount = 0;
                return(EOF);
            } else
            {
                buffpos = 0;
                buffcount = bytesread;
            }
        }
        return(buffer[buffpos++]);
}


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

VOID    PRIVENTRY vvPMINextChar(HFILE f)
{
  ch = vvPMIGetc(f);
  if ((ch >= 'a') && (ch <= 'z'))
    upch = ch - (CHAR)('a' - 'A');
  else
    upch = ch;
  ColNo++;
  if (ch == '\n')
  {
    LineNo++;
    ColNo = 0;
  }
}


/***************************************************************************
 *
 * FUNCTION NAME = vvPMIGetLongValue()
 *
 * 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.  Then process the
 *                 string, adding numbers/hex digits.
 *
 * INPUT         = PCHAR - Character representation of long.
 *
 * OUTPUT        = ULONG - Value converted to unsigned long.
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 **************************************************************************/

ULONG   PRIVENTRY vvPMIGetLongValue(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
            {                           ; Do the multiply this way
                mov eax, v              ; to prevent library routines
                mul radix               ; being called in which aren't
                mov v, eax              ; 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 = vvPMINextToken()
 *
 * 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
 *
 * OUTPUT        = PMITOKEN - Next token
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 **************************************************************************/

PMITOKEN PRIVENTRY vvPMINextToken(HFILE f)
{
  PMITOKEN        tok = TOK_EOF;
  int             i;

  while (ch != EOF)
  {
    if( ((upch >= 'A') && (upch <= 'Z'))
        || (upch == '[')
        || (upch == '#') )                                      /*          */
    {
      /*
      ** look for keyword
      */

      i = 0;

      do
      {
        if( i < (LINELEN - 1) )                                 /*          */
          pszPMIStrIn [i++] = upch;
        vvPMINextChar(f);
      } while ( ((upch >= 'A') && (upch <= 'Z'))                /*          */
                || ((upch >= '0') && (upch <= '9'))             /*          */
                || (upch == ']')                                /*          */
                || (upch == '_') );                             /*          */

          pszPMIStrIn [i] = '\0';

      for (i=0; i<sizeof(Tokens)/sizeof(TOK); i++)
        if( !strcmp( pszPMIStrIn, Tokens[i].tok_txt ) )
          return( Tokens[i].tok_val );

      /*
      ** test for variable in range R0 - R255
      */

      if ( (pszPMIStrIn[0] == 'R')
           && (pszPMIStrIn[1] >= '0')                               /*          */
           && (pszPMIStrIn[1] <= '9')                               /*          */
           && (!pszPMIStrIn[2]                                      /*          */
               || ((pszPMIStrIn[2] >= '0')                          /*          */
                   && (pszPMIStrIn[2] <= '9')                       /*          */
                   && (!pszPMIStrIn[3]                              /*          */
                       || ((pszPMIStrIn[3] >= '0')                  /*          */
                           && (pszPMIStrIn[3] <= '9')               /*          */
                           && !pszPMIStrIn[4])))) )                 /*          */
      {
          strcpy( CurrVariable, pszPMIStrIn );
          return(TOK_VARIABLE);
      }
        else
          return(TOK_UNKNOWN);
    }
    else
      if ((upch >= '0') && (upch <= '9'))
      {
        i = 0;
        do
        {
          if( i < (LINELEN - 1) )                               /*          */
            pszPMIStrIn [i++] = upch;
          vvPMINextChar(f);
        } while ( (upch == 'X') ||
                 ((upch >= '0') && (upch <= '9')) ||
                 ((upch >= 'A') && (upch <= 'F')) );
        pszPMIStrIn [i] = '\0';
        CurrValue = vvPMIGetLongValue( pszPMIStrIn );
        return(TOK_VALUE);
      }
      else switch (upch)
      {
        case ' ':
        case '\t':
        case '\n':
        case ',':
          vvPMINextChar(f);
          break;
        case '(':
          vvPMINextChar(f);
          return(TOK_LPAREN);
        case ')':
          vvPMINextChar(f);
          return(TOK_RPAREN);
        case '=':
          vvPMINextChar(f);
          return(TOK_EQUALS);
        case ';':
          vvPMINextChar(f);
          return(TOK_SEMICOLON);
        case '|' :                    /* bitwise OR op */       /*          */
          vvPMINextChar(f);
          return (TOK_REGOP_OR);
        case '&' :                    /* bitwise AND op */      /*          */
          vvPMINextChar(f);
          return (TOK_REGOP_AND);
        case '^' :                    /* bitwise XOR op */      /*          */
          vvPMINextChar(f);
          return (TOK_REGOP_XOR);
        case '<' :                    /* bitwise SHL op */      /*          */
          vvPMINextChar(f);
          return (TOK_REGOP_SHL);
        case '>' :                    /* bitwise SHR op */      /*          */
          vvPMINextChar(f);
          return (TOK_REGOP_SHR);
        case '-':
          vvPMINextChar(f);
          return(TOK_HYPHEN);
        case '"':                                               /*          */
          vvPMINextChar(f);                                     /*          */
          i = 0;                                                /*          */
          while( (ch != '"')                                    /*          */
                 && (ch != EOF) )                               /*          */
          {                                                     /*          */
            if( i < (LINELEN - 1) )                             /*          */
              /* Leave in lower case! */                        /*          */
              pszPMIStrIn [i++] = ch;                           /*          */
            vvPMINextChar(f);                                   /*          */
          }                                                     /*          */
          vvPMINextChar(f);                                     /*          */
          pszPMIStrIn [i] = '\0';                               /*          */
          return( TOK_STRING );                                 /*          */
        case '/':                     /* comment? */
          vvPMINextChar(f);
          if ((ch == '/') || (ch == '*'))
          {
            if (ch == '/')
            {
              /*
              ** skip to end-of-line
              */
              while ((ch != '\n') && (ch != EOF))
                      vvPMINextChar(f);
              if (ch == '\n')
                  vvPMINextChar(f);
            }
            else
            {
              /*
              ** skip to comment end
              */

              vvPMINextChar(f);
              do
              {
                while ((ch != '*') &&
                       (upch != 'S') &&                         /*          */
                       (ch != EOF))
                  vvPMINextChar(f);
                /*
                 * check if the string is "SPECIAL"
                 */
                if (upch == 'S')                                /*          */
                {                                               /* Start */
                  vvPMINextChar(f);
                  pszPMIStrIn [0] = upch;
                  i = 1;
                  while ((upch != '\n') &&
                         (upch != ' ')  &&
                         (upch != '\t'))
                  {
                    if( i < (LINELEN - 1) )                     /*          */
                      pszPMIStrIn [i++] = upch;
                    vvPMINextChar(f);
                  }
                  pszPMIStrIn [i] = '\0';
                  if( !strcmp( pszPMIStrIn, "SPECIAL" ) )
                    sOEMData.Manufacturer = LACUNA_MANUFACTURER;
                }                                               /* end */
                else                                            /*          */
                  /*
                   * ch is '*' or EOF
                   */
                  vvPMINextChar(f);
              } while ((ch != '/') && (ch != EOF));
              vvPMINextChar(f);
            }
          }
          break;

        default:
          vvPMINextChar(f);
          break;
      }
  }
  return(tok);
}

/***************************************************************************
 *
 * FUNCTION NAME = vvPMIVARIndex()
 *
 * 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
 *
 **************************************************************************/

UINT PRIVENTRY vvPMIVARIndex(VOID)
{
  return( (UINT) vvPMIGetLongValue( &CurrVariable[1]) );
}

/***************************************************************************
 *
 * FUNCTION NAME = vvPMISkipSection()
 *
 * DESCRIPTION   = This reads and discards tokens skipping to the start of
 *                 a new section or end of file, whichever comes first.
 *
 * INPUT         = PMITOKEN - Token
 *                 HFILE - pmifile
 *
 * OUTPUT        = PMITOKEN
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 **************************************************************************/

PMITOKEN PRIVENTRY vvPMISkipSection(PMITOKEN tok, HFILE pmifile)
{
  do
    tok = vvPMINextToken( pmifile );
  while( ((tok < TOK_SECT_BEGIN)                                /*          */
          || (tok > TOK_SECT_END))
         && (tok != TOK_EOF) );
  return( tok );
}

/* Moved ahead of new calls */                                  /*          */
#if LOGERROR

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

VOID    PRIVENTRY vvPMIParseItoa(int v, CHAR * s)
{
        int     r;

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

/***************************************************************************
 *
 * FUNCTION NAME = vvPMILogError()              (            this function )
 *
 * DESCRIPTION   = Log a PMI-file parsing error to a file.
 *
 * INPUT         =
 *
 * OUTPUT        =
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 **************************************************************************/

VOID PRIVENTRY vvPMILogError(
  char * fname,
  char *pszErrorMsg )
{
  CHAR        * p, buff[80];
  ULONG       ulAction, ulBytesWritten;
  HFILE       errfile;

  strcpy(buff, fname);
  p = buff;
  while (*p && (*p != '.')) p++;
  strcpy(p, ".err");
  if ( VDHOpen(buff, SSToDS(&errfile), SSToDS(&ulAction), 0L,
               VDHOPEN_FILE_NORMAL,
               VDHOPEN_FILE_OPEN | VDHOPEN_FILE_CREATE,
               VDHOPEN_ACCESS_WRITEONLY | VDHOPEN_SHARE_DENYWRITE | VDHOPEN_FLAGS_SEQUENTIAL,
               NULL) )
  {
    VDHSeek(errfile, 0L, VDHSK_END_OF_FILE);
    strcpy(buff, "VSVGA: Error at line:     ");
    vvPMIParseItoa(LineNo, buff+strlen(buff)-1);
    strcat(buff, ", column:     ");
    vvPMIParseItoa(ColNo, buff+strlen(buff)-1);
    strcat(buff, ", ");                                           /*          */
    strcat(buff, pszErrorMsg);                                    /*          */
    strcat(buff, "\r\n");                                         /*          */
    ulBytesWritten = VDHWrite(errfile, buff, strlen(buff));
    VDHClose(errfile);
  }
}

#endif /* LOGERROR */
                                                                /*          */
/***************************************************************************
 *
 * FUNCTION NAME = vvPMIMatchChipType
 *
 * DESCRIPTION   =
 *
 * INPUT         =
 *
 * OUTPUT        = ChipType or UNKNOWN_CHIP
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 **************************************************************************/

ULONG PRIVENTRY vvPMIMatchChipType(                             /*          */
  PSSVGAINFO psCurAdapterInfo )                                 /*          */
{
  ULONG ulPMIChipType;

  for ( ulPMIChipType = 0;                                      /*          */
        (ulPMIChipType
          < psCurAdapterInfo->ulSVGAAdapterMaxChip)             /*          */
        && strcmp( psCurAdapterInfo->                           /*          */
                     ppszSVGAAdapterChipNames                   /*          */
                       [ulPMIChipType],                         /*          */
                   pszPMIStrIn );                               /*          */
        ulPMIChipType++)                                        /*          */
  {                                                             /*          */
    /* Ends with ulPMIChipType ==  */                           /*          */
    /* psCurAdapterInfo->ulSVGAAdapterMaxChip */                /*          */
    /* when no match! */                                        /*          */
  }                                                             /*          */
  return( (ulPMIChipType                                        /*          */
           == psCurAdapterInfo->ulSVGAAdapterMaxChip)           /*          */
          ? UNKNOWN_CHIP                                        /*          */
          : ++ulPMIChipType );                                  /*          */
}
                                                                /*          */
/***************************************************************************
 *
 * FUNCTION NAME = vvPMISetAdapterType
 *
 * DESCRIPTION   =
 *
 * INPUT         =
 *
 * OUTPUT        = ulSVGAAdapterType set
 *                 ulSVGAChipType set
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 **************************************************************************/

VOID    PRIVENTRY vvPMISetAdapterType(
  ULONG ulNewAdapterType,
  ULONG ulNewChipType,
  ULONG ulNewVRAMSize )                                         /*          */
{
  ulSVGAAdapterType = ulNewAdapterType;
  ulSVGAChipType = ulNewChipType;
  ulSVGAVRAMSize = ulNewVRAMSize;                               /*          */
  psCurAdapterInfo =                                            /*          */
    apsAllAdapterInfo                                           /*          */
      /* Allow uplevel SCREENDD & PMI files */                  /*          */
      /* Unknown adapter type is OK! */                         /*          */
      [((ulSVGAAdapterType <= SVGA_LASTADAPTER)                 /*          */
       ? ulSVGAAdapterType                                      /*          */
       : UNKNOWN_ADAPTER)].apsInfo;                             /*          */
  psCurAdapterInfo->pfnSVGAEditTables();          /*          *//*          */
  /* Must be after pfnSVGAEditTables: */                        /*          */
  psCurAcceleratorInfo = psCurAdapterInfo->psAcceleratorInfo;   /*          */
}
                                                                /*          */
/***************************************************************************
 *
 * FUNCTION NAME = vvPMICompareChipType
 *
 * DESCRIPTION   =
 *
 * INPUT         =
 *
 * OUTPUT        = modified ChipType
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 **************************************************************************/

VOID PRIVENTRY vvPMICompareChipType(                            /*          */
  ULONG ulPMIAdapterType,                                       /*          */
  ULONG ulPMIChipType,
  ULONG ulPMIVRAMSize,                                          /*          */
  char * fname )
{
  /* Allow uplevel SCREENDD & PMI files */                      /*          */
  /* Unknown chip type is OK! */                                /*          */
  if( ulPMIChipType == UNKNOWN_CHIP )                           /*          */
  {                                                             /*          */
    vvPMILogError( fname,                                       /*          */
                   "Unrecognized Chip Type" );                  /*          */
    if( (sSVGA.AdapterType <= SVGA_LASTADAPTER)                 /*          */
        && (sSVGA.ChipType                                      /*          */
            <= psCurAdapterInfo->ulSVGAAdapterMaxChip) )        /*          */
      /* If just PMI is uplevel:             */                 /*          */
      /* Assume SCREENDD doesn't know type   */                 /*          */
      sSVGA.ChipType = UNKNOWN_CHIP;                            /*          */
    else
      /* Otherwise:                          */                 /*          */
      /* If both SCREENDD & PMI are uplevel: */                 /*          */
      /* Or PMI is downlevel:                */                 /*          */
      /* Assume SCREENDD knows the type      */                 /*          */
      /* SCREENDD is uplevel compared to VVID */                /*          */
      ulPMIChipType = sSVGA.ChipType;                           /*          */
  }                                                             /*          */
  /*
  **  if this isn't the same chip, reset                                    
  **  ulPMIAdapterType to default & log the error.
  */
  if( ulPMIChipType != sSVGA.ChipType )                         /*          */
    /* Allow uplevel SCREENDD & PMI files */                    /*          */
    if( sSVGA.ChipType == UNKNOWN_CHIP )                        /*          */
    {
      /* PMI is uplevel & SCREENDD NOT */                       /*          */
      sSVGA.ChipType = ulPMIChipType;                           /*          */
    }
    else
    {
      vvPMILogError( fname,                                     /*          */
                     "Data not for installed chipset" );        /*          */
      /*!! Do we really want to do this?*/                      /*          */
      /*!! Or do we still want to take  */                      /*          */
      /*!! advantage of SCREENDD info?  */                      /*          */
      ulPMIChipType = UNKNOWN_CHIP;                             /*          */
    }
  /* Now we know both adaptertype and chiptype */
  vvPMISetAdapterType( ulPMIAdapterType,                        /*          */
                       ulPMIChipType,                           /*          */
                       ulPMIVRAMSize );                         /*          */
}
                                                                /*          */
/***************************************************************************
 *
 * FUNCTION NAME = vvPMICompareAdapterType
 *
 * DESCRIPTION   =
 *
 * INPUT         =
 *
 * OUTPUT        = modified AdapterType & ChipType
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 **************************************************************************/

VOID PRIVENTRY vvPMICompareAdapterType(                         /*          */
  ULONG ulPMIAdapterType,
  ULONG ulPMIChipType,
  char * fname )
{
  ULONG ulPMIVRAMSize;                                          /*          */

  ulPMIVRAMSize = ulSVGAVRAMSize;                               /*          */
  /* Allow uplevel SCREENDD & PMI files */                      /*          */
  /* Unknown adapter type is OK! */                             /*          */
  if( ulPMIAdapterType == UNKNOWN_ADAPTER )                     /*          */
  {                                                             /*          */
    vvPMILogError( fname,                                       /*          */
                   "Unrecognized Adapter Type" );               /*          */
    if( sSVGA.AdapterType <= SVGA_LASTADAPTER )                 /*          */
    {
      /* If just PMI is uplevel:             */                 /*          */
      /* Assume SCREENDD doesn't know type   */                 /*          */
      sSVGA.AdapterType = UNKNOWN_ADAPTER;                      /*          */
      sSVGA.ChipType = UNKNOWN_CHIP;                            /*          */
      sSVGA.Memory = 0x00040000L;       /* 256K=4*64K */        /*          */
    }
    else
    {
      /* Otherwise:                          */
      /* If both SCREENDD & PMI are uplevel: */                 /*          */
      /* Or PMI is downlevel:                */                 /*          */
      /* Assume SCREENDD knows the type      */                 /*          */
      ulPMIAdapterType = sSVGA.AdapterType;                     /*          */
      ulPMIChipType = sSVGA.ChipType;                           /*          */
      ulPMIVRAMSize = sSVGA.Memory;                             /*          */
    }
  }                                                             /*          */
  /*
  **  if this isn't the same adapter, reset                                 
  **  ulPMIAdapterType to default and log the error.
  */
  if( ulPMIAdapterType != sSVGA.AdapterType )                   /*          */
    /* Allow uplevel SCREENDD & PMI files */                    /*          */
    if( sSVGA.AdapterType == UNKNOWN_ADAPTER )                  /*          */
    {                                                           /*          */
      /* PMI is uplevel & SCREENDD NOT */                       /*          */
      sSVGA.AdapterType = ulPMIAdapterType;
      sSVGA.ChipType = ulPMIChipType;
      sSVGA.Memory = ulPMIVRAMSize;                             /*          */
    }                                                           /*          */
    else                                                        /*          */
    {                                                           /*          */
      vvPMILogError( fname,                                     /*          */
                     "Data not for installed adapter" );
      /*!! Do we really want to do this?*/                      /*          */
      /*!! Or do we still want to take  */                      /*          */
      /*!! advantage of SCREENDD info?  */                      /*          */
      ulPMIAdapterType = UNKNOWN_ADAPTER;                       /*          */
      ulPMIChipType = UNKNOWN_CHIP;                             /*          */
      ulPMIVRAMSize = 0x00040000L;      /* 256K=4*64K */        /*          */
    }                                                           /*          */
  /* We know adaptertype, now determine chiptype */
  vvPMICompareChipType( ulPMIAdapterType,                       /*          */
                        ulPMIChipType,                          /*          */
                        ulPMIVRAMSize,                          /*          */
                        fname );
}
                                                                /*          */
/***************************************************************************
 *
 * FUNCTION NAME = vvPMIParseHardware()
 *
 * DESCRIPTION   = This parses the hardware section.
 *
 * INPUT         = PMITOKEN - Token
 *                 HFILE - pmifile
 *                 FNAME - error file
 *
 * OUTPUT        = PMITOKEN
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 **************************************************************************/

PMITOKEN PRIVENTRY vvPMIParseHardware(                          /*          */
  PMITOKEN tok,
  HFILE pmifile,
  char * fname )
{
  PMITOKEN tok_hardware;
  ULONG ulPMIAdapterType;
  ULONG ulPMIChipType;
  ULONG ulPMIDacType;                                           /*          */
  USHORT usNameIndex;

  tok = vvPMINextToken( pmifile );      /* eat [HARDWARE] */
  while( (tok >= TOK_HARDWARE_BEGIN)
         && (tok <= TOK_HARDWARE_END) )
  {
    tok_hardware = tok;
    tok = vvPMINextToken( pmifile );
    if (tok != TOK_EQUALS)
      return (TOK_ERROR_SYNTAX);        /* force an error */
    tok = vvPMINextToken( pmifile );
    switch( tok_hardware )
    {
      case TOK_OEMSTRING:
        if( tok == TOK_STRING)
        {
          /* Shorten to just ChipType name: */
          for( usNameIndex = 0;
               (pszPMIStrIn [usNameIndex] != '\0')
               && (pszPMIStrIn [usNameIndex] != ' ');
               usNameIndex++ )
          {}
          pszPMIStrIn [usNameIndex] = '\0';
          /* Find ChipType name in adapter tables: */
          for( ulPMIAdapterType = 1;
               (ulPMIAdapterType <= SVGA_LASTADAPTER)
               && !(apsAllAdapterInfo [ulPMIAdapterType].apsInfo/*          */
                    && (ulPMIChipType =                         /*          */
                          vvPMIMatchChipType(                   /*          */
                            apsAllAdapterInfo                   /*          */
                              [ulPMIAdapterType].apsInfo )));   /*          */
               ulPMIAdapterType++ )
          {}
          if( ulPMIAdapterType > SVGA_LASTADAPTER )
            ulPMIAdapterType = UNKNOWN_ADAPTER;
          vvPMICompareAdapterType( ulPMIAdapterType,
                                   ulPMIChipType,
                                   fname );
        }
        else
          tok = TOK_ERROR_SYNTAX;
        break;
      case TOK_DACSTRING:                                       /*          */
        if( tok == TOK_STRING)                                  /*          */
        {                                                       /*          */
          /* Shorten to just ChipType name: */                  /*          */
          for( usNameIndex = 0;                                 /*          */
               (pszPMIStrIn [usNameIndex] != '\0')              /*          */
               && (pszPMIStrIn [usNameIndex] != ' ');
               usNameIndex++ )                                  /*          */
          {}                                                    /*          */
          pszPMIStrIn [usNameIndex] = '\0';                     /*          */
          /* Find DACType name in DAC tables: */                /*          */
          /* Skip entries with NULL pointers: */                /*          */
          for( ulPMIDacType = 0;                                /*          */
               (ulPMIDacType < MAX_DAC)                         /*          */
               && !(apsAllDacInfo [ulPMIDacType]                /*          */
                    && !strcmp( apsAllDacInfo [ulPMIDacType]->  /*          */
                                 pszDacName,                    /*          */
                                pszPMIStrIn ));                 /*          */
               ulPMIDacType++ )                                 /*          */
          {}                                                    /*          */
          if( ulPMIDacType >= MAX_DAC )                         /*          */
            ulPMIDacType = HICOLOR_DAC;                         /*          */
          psCurDacInfo = apsAllDacInfo [ulPMIDacType];          /*          */
        }                                                       /*          */
        else                                                    /*          */
          tok = TOK_ERROR_SYNTAX;                               /*          */
        break;                                                  /*          */
      case TOK_TOTALMEMORY:
        if( tok == TOK_VALUE )
          ulSVGAVRAMSize = CurrValue;
        else
          tok = TOK_ERROR_SYNTAX;
        break;
      case TOK_VERSION:
        /* Expect a string: */
        break;
      case TOK_BUSTYPE:
      case TOK_ENDIAN:
        /* Expect another token: */
        break;
      case TOK_MMIOADDRESS:
        /* Expect a number: */
        break;
    }
    if( tok == TOK_ERROR_SYNTAX)
      vvPMILogError( fname,
                     "Unrecognized/Unexpected Token" );
    tok = vvPMINextToken( pmifile );
  }
  return (tok);
}
                                                                /*          */
/***************************************************************************
 *
 * FUNCTION NAME = vvPMIPortToPPLE()
 *
 * DESCRIPTION   = Return PPLE for port address
 *
 * INPUT         = CurrValue has port address
 *
 * OUTPUT        = PPLE containing port information
 *                 for that address
 *
 * RETURN-NORMAL = NONE
 * RETURN-ERROR  = NONE
 *
 **************************************************************************/

PPLE PRIVENTRY vvPMIPortToPPLE(
  PORT port,
  char * fname )
{
  PPLE *ppple;
  STRAPREGINFO sTrapRegInfo;

  sTrapRegInfo.portTrap = port;
  for( ppple = appleAll;
       *ppple
          /* Non-empty table index: */
       && ((sTrapRegInfo.fTrapType =                            /*          */
             (*ppple)->sTrapBRegInfo.fTrapType)                 /*          */
           & (WRITE_ALL | READ_ALL))                            /*          */
          /* Ports map identically: */
       && (((PSCGAINFO) psCurAdapterInfo)->                     /*          */
             pportfnCGAAdjustedPort(                            /*          */
               &(*ppple)->sTrapBRegInfo )                       /*          */
           != ((PSCGAINFO) psCurAdapterInfo)->                  /*          */
                pportfnCGAAdjustedPort(                         /*          */
                  (PSTRAPREGINFO) SSToDS( &sTrapRegInfo ) ));   /*          */
       ppple++)
  {}
  /* If PSTRAPREGINFO found: */                                 /*          */
  if( *ppple )                                                  /*          */
    (*ppple)->sTrapBRegInfo.fTrapType &= ~ PORTF_NOTVALIDSVGAPORT;        /*                      */
  else
    vvPMILogError( fname,                                       /*          */
                   "No port list entry for port" );             /*          */
  return( *ppple );
}
                                                                /*          */
/***************************************************************************
 *
 * FUNCTION NAME = vvPMIPortToTrapReg()
 *
 * DESCRIPTION   = Return PSTRAPREGINFO for port address
 *
 * INPUT         = CurrValue has port address
 *
 * OUTPUT        = PSTRAPREGINFO containing port information
 *                 for that address
 *
 * RETURN-NORMAL = NONE
 * RETURN-ERROR  = NONE
 *
 **************************************************************************/

PSTRAPREGINFO PRIVENTRY vvPMIPortToTrapReg(
  PORT port,                                                    /*          */
  char * fname )
{
  PPLE pple;
  PSTRAPREGINFO psTrapRegInfo;
  if (!psCurAcceleratorInfo)                                    /*          */
    return(((PSTRAPREGINFO) vvPMIPortToPPLE( port,              /*          */
                                             fname )));         /*          */

  /* Look in current adapter accelerator register table first */
  psTrapRegInfo =
    &psCurAcceleratorInfo->
      psAcceleratorTrapRegInfo
        [psCurAcceleratorInfo->
          puifnAcceleratorPortIndex( port )];                   /*          */
  return( ((psTrapRegInfo->portTrap == CurrValue)
           ? psTrapRegInfo
             /* If not found yet then look in standard port list */
           : ((PSTRAPREGINFO)
                vvPMIPortToPPLE( port,
                                 fname ))) );
}
                                                                /*          */
/***************************************************************************
 *
 * FUNCTION NAME = vvPMIIndexPortToTrapReg()
 *
 * DESCRIPTION   = Return PSTRAPREGINFO for port address
 *
 * INPUT         = CurrValue has port address
 *
 * OUTPUT        = PSTRAPREGINFO containing port information
 *                 for that address
 *
 * RETURN-NORMAL = NONE
 * RETURN-ERROR  = NONE
 *
 **************************************************************************/

PSTRAPREGINFO PRIVENTRY vvPMIIndexPortToTrapReg(                /*          */
  char * fname )
{
  PSTRAPREGINFO psTrapRegInfo;

  psTrapRegInfo = vvPMIPortToTrapReg( CurrValue,                /*          */
                                      fname );                  /*          */
  /* pleATCx is not an index! use pleATC0 instead */            /*          */
  return( (psTrapRegInfo == (PSTRAPREGINFO) &pleATCx )          /*          */
          ? (PSTRAPREGINFO) &pleATC0                            /*          */
          : psTrapRegInfo );
}

/***************************************************************************
 *
 * FUNCTION NAME = vvPMIProcessTrapRegs()
 *
 * DESCRIPTION   = Process trap registers
 *
 *                 Trap registers section layout is as follows:
 *
 *                     <port>[,<index range>];
 *
 *                     eg: 0x3c4, 0x00-0x04, 0x06-0x07;
 *
 *                 If this section exists, it should contain at least one entry.
 *
 * INPUT         = PMITOKEN - tok
 *                 HFILE - pmifile
 *                 FNAME - error log file name
 *
 * OUTPUT        = PMITOKEN
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 **************************************************************************/

PMITOKEN PRIVENTRY vvPMIProcessTrapRegs(
  PMITOKEN tok,
  HFILE pmifile,                                                /*          */
  char * fname)                                                 /*          */
{
  USHORT RangeStart, RangeEnd;
  PBYTE pbData;
  PPLE pple;
  PORT port;

  tok = vvPMINextToken(pmifile);
  do
  {
    /* Allow uplevel SCREENDD & PMI files */                    /*          */
    /* Tolerate new style PMI files with error comment! */      /*          */
    while ( tok == TOK_UNKNOWN )                                /*          */
    {                                                           /*          */
      vvPMILogError( fname,                                     /*          */
                     "Unrecognized/Unexpected Token" );         /*          */
      tok = vvPMINextToken(pmifile);                            /*          */
    }                                                           /*          */
    if( (tok == TOK_BYTE_IOPORT)                                /*          */
        || (tok == TOK_WORD_IOPORT) )                           /*          */
      tok = vvPMINextToken(pmifile);                            /*          */
    if (tok != TOK_VALUE)
      return(TOK_UNKNOWN);            /* force an error */
    port = CurrValue;
    vvPMIPortToTrapReg( CurrValue,                              /*          */
                        fname );      /* Verify in tables! */   /*          */
    tok = vvPMINextToken(pmifile);
    if((tok != TOK_SEMICOLON) && (tok != TOK_EOF))
    {
      /* Verify in BYTE tables! */
      pple = vvPMIPortToPPLE( port,                             /*          */
                              fname );                          /*          */
      while ((tok != TOK_SEMICOLON) && (tok != TOK_EOF))
      {
        /* Allow uplevel SCREENDD & PMI files */                /*          */
        /* Tolerate new style PMI files w/error comment! */     /*          */
        while ( tok == TOK_UNKNOWN )                            /*          */
        {                                                       /*          */
          vvPMILogError( fname,                                 /*          */
                         "Unrecognized/Unexpected Token" );     /*          */
          tok = vvPMINextToken(pmifile);                        /*          */
        }                                                       /*          */
        if (tok != TOK_VALUE)           /* get range start */
          return(TOK_UNKNOWN);          /* force an error */

        RangeStart = (USHORT)CurrValue;
        tok = vvPMINextToken(pmifile);  /* get the '-' */

        /* Allow uplevel SCREENDD & PMI files */                /*          */
        /* Tolerate new style PMI files w/error comment! */     /*          */
        while ( tok == TOK_UNKNOWN )                            /*          */
        {                                                       /*          */
          vvPMILogError( fname,                                 /*          */
                         "Unrecognized/Unexpected Token" );     /*          */
          tok = vvPMINextToken(pmifile);                        /*          */
        }                                                       /*          */
        if (tok != TOK_HYPHEN)
          return(TOK_UNKNOWN);          /* force an error */

        tok = vvPMINextToken(pmifile);

        /* Allow uplevel SCREENDD & PMI files */                /*          */
        /* Tolerate new style PMI files w/error comment! */     /*          */
        while ( tok == TOK_UNKNOWN )                            /*          */
        {                                                       /*          */
          vvPMILogError( fname,                                 /*          */
                         "Unrecognized/Unexpected Token" );     /*          */
          tok = vvPMINextToken(pmifile);                        /*          */
        }                                                       /*          */
        if (tok != TOK_VALUE)           /* get range end */
          return(TOK_UNKNOWN);          /* force an error */

        RangeEnd = (USHORT)CurrValue;

        if (RangeStart > RangeEnd)
          return(TOK_UNKNOWN);          /* force an error */    /*          */

        if( pple )                      /* If in tables */      /*          */
        {
          if( !pple->ulBRegCount        /* Not an indexed port*//*          */
              && (pple [1].ppleBRegIndx == pple) )
                                        /* but next is! */      /*          */
            pple++;                     /* Old style PMI! */    /*          */
          if( pple->ulBRegCount )       /* Indexed port */      /*          */
          {
            /*
            ** update max no of indices used by this port
            */
            if( RangeEnd+1 > pple->ulBRegCount)                 /*          */
              pple->ulBRegCount = RangeEnd + 1;
            while( RangeStart <= RangeEnd )
            {
//@V3.0YEE01  pple->pbBRegMask [RangeStart / 8] |=              /*          */
//@V3.0YEE01    1 << (RangeStart % 8 );
              /* initialize all byte-size mask to ff              @V3.0YEE01*/
              pple->pbBRegMask [RangeStart] = 0xff;             /*@V3.0YEE01*/
              RangeStart++;
            }
          }
        } /* endif port in tables */
        tok = vvPMINextToken(pmifile);
      } /* end while trap reg index */
    } /* endif STRAPREGINFO found */
    if (tok == TOK_SEMICOLON)
      tok = vvPMINextToken(pmifile);
  } while ((tok > TOK_SECT_END) && (tok != TOK_EOF));
  return( tok );
}

/***************************************************************************
 *
 * FUNCTION NAME = vvPMIParseInCmd()
 *
 * 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
 *
 **************************************************************************/

PMITOKEN PRIVENTRY vvPMIParseInCmd(
  PMITOKEN tok,
  HFILE pmifile,
  char * fname,
  PHWCMD pHWCmd )
{
  pHWCmd->usFlags = PMIFLAG_REGISTER;
  tok = vvPMINextToken(pmifile);        /* get LPAREN */
  if (tok != TOK_LPAREN)
    return (TOK_ERROR_SYNTAX);          /* force an error */

  tok = vvPMINextToken(pmifile);        /* get VARiable */
  if (tok != TOK_VARIABLE)
    return (TOK_ERROR_SYNTAX);          /* force an error */
  pHWCmd->hwTarget.ulOperand =
    vvPMIVARIndex();                     /* get variable index */

  tok = vvPMINextToken(pmifile);        /* get PORT value */
  if (tok != TOK_VALUE)
    return (TOK_ERROR_SYNTAX);          /* force an error */
  pHWCmd->hwSource.psTrapRegOperand =                           /*          */
    /* We do not know if it is an index or data! */             /*          */
    /* Probably safest to assumes it is an index */             /*          */
    vvPMIIndexPortToTrapReg( fname );                           /*          */
  tok = vvPMINextToken(pmifile);        /* get RPAREN */
  if (tok != TOK_RPAREN)
    return (TOK_ERROR_SYNTAX);          /* force an error */

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

  return (tok);
}

/***************************************************************************
 *
 * FUNCTION NAME = vvPMIParseOutCmd()
 *
 * 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
 *
 **************************************************************************/

PMITOKEN PRIVENTRY vvPMIParseOutCmd(
  PMITOKEN tok,
  HFILE pmifile,
  char * fname,
  PHWCMD pHWCmd )
{
  tok = vvPMINextToken(pmifile);       /* get LPAREN */
  if (tok != TOK_LPAREN)
    return (TOK_ERROR_SYNTAX);         /* force an error */

  tok = vvPMINextToken(pmifile);       /* get port value */
  if (tok != TOK_VALUE)
    return (TOK_ERROR_SYNTAX);         /* force an error */
  pHWCmd->hwTarget.psTrapRegOperand =
    /* We do not know if it is an index or data! */             /*          */
    /* Probably safest to assumes it is an index */             /*          */
    vvPMIIndexPortToTrapReg( fname );                           /*          */

  tok = vvPMINextToken(pmifile);       /* get data/variable value */
  if( tok == TOK_VALUE )
  {
    pHWCmd->usFlags = PMIFLAG_NONE;
    pHWCmd->hwSource.ulOperand = (USHORT)CurrValue;
  }
  else if( tok == TOK_VARIABLE )
  {
    pHWCmd->usFlags = PMIFLAG_REGISTER;
    pHWCmd->hwSource.ulOperand =
      vvPMIVARIndex();                 /* get variable index */
  }
  else
    return (TOK_ERROR_SYNTAX);         /* force an error */

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

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

  return (tok);
}

/***************************************************************************
 *
 * FUNCTION NAME = vvPMIParseBlockCmd()
 *
 * 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
 *
 **************************************************************************/

PMITOKEN PRIVENTRY vvPMIParseBlockCmd(
  PMITOKEN tok,
  HFILE pmifile,
  char * fname,
  PHWCMD pHWCmd,
  PHWCMDOPERAND pHWCmdData,
  PHWCMDOPERAND pHWCmdStart )
{

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

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

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

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

  pHWCmd->hwCount.ulOperand = (USHORT)CurrValue;
  tok = vvPMINextToken(pmifile); /* get start index */

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

  pHWCmdStart->ulOperand = (USHORT)CurrValue;
  tok = vvPMINextToken(pmifile); /* get Index Port */

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

  pHWCmd->hwIndex.psTrapRegOperand =
    vvPMIIndexPortToTrapReg( fname );                           /*          */
  tok = vvPMINextToken(pmifile); /* get Data Port */

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

  pHWCmdData->psTrapRegOperand =
    vvPMIPortToTrapReg( CurrValue,                              /*          */
                        fname );                                /*          */
  tok = vvPMINextToken(pmifile); /* get RPAREN */

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

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

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

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

PMITOKEN PRIVENTRY vvPMIParseRMW(
  PMITOKEN tok,
  HFILE pmifile,
  char * fname,
  PHWCMD pHWCmd )
{
  PMITOKEN      tokBW;

  tokBW = tok;                          /* TOK_RMWBI/TOK_RMWBN/TOK_RMWWN */
  tok = vvPMINextToken(pmifile);        /* get LPAREN */
  if (tok != TOK_LPAREN)
    return (TOK_ERROR_SYNTAX);          /* force an error */

  tok = vvPMINextToken(pmifile);        /* get index port value */
  if (tok != TOK_VALUE)
    return (TOK_ERROR_SYNTAX);          /* force an error */
  pHWCmd->hwSource.psTrapRegOperand =                           /*          */
    vvPMIIndexPortToTrapReg( fname );                           /*          */

  tok = vvPMINextToken(pmifile);        /* get data port value */
  if (tok != TOK_VALUE)
    return (TOK_ERROR_SYNTAX);          /* force an error */
  pHWCmd->hwTarget.psTrapRegOperand =                           /*          */
    vvPMIPortToTrapReg( CurrValue,                              /*          */
                        fname );                                /*          */

  if (tokBW == TOK_RMWBI)                                       /*          */
  {
    tok = vvPMINextToken(pmifile);      /* get index value */
    if (tok != TOK_VALUE)
      return (TOK_ERROR_SYNTAX);        /* force an error */
    pHWCmd->hwIndex = pHWCmd->hwSource;
    pHWCmd->hwSource.ulOperand = (USHORT)CurrValue;
  }

  tok = vvPMINextToken(pmifile);        /* get AND Mask value */
  if (tok != TOK_VALUE)
    return (TOK_ERROR_SYNTAX);          /* force an error */
  pHWCmd->hwModify.ulOperand = (USHORT)CurrValue;

  tok = vvPMINextToken(pmifile);        /* get OR Mask value */
  if (tok != TOK_VALUE)
    return (TOK_ERROR_SYNTAX);          /* force an error */
  pHWCmd->hwCount.ulOperand = (USHORT)CurrValue;

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

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

  return (tok);
}

/***************************************************************************
 *
 * FUNCTION NAME = vvPMIParseWait()
 *
 * 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
 *
 **************************************************************************/

PMITOKEN PRIVENTRY vvPMIParseWait(
  PMITOKEN tok,
  HFILE pmifile,
  char * fname,
  PHWCMD pHWCmd )
{
  tok = vvPMINextToken(pmifile);        /* get LPAREN */
  if (tok != TOK_LPAREN)
    return (TOK_ERROR_SYNTAX);          /* force an error */

  tok = vvPMINextToken(pmifile);        /* get port value */
  if (tok != TOK_VALUE)
    return (TOK_ERROR_SYNTAX);          /* force an error */
  pHWCmd->hwSource.psTrapRegOperand =                           /*          */
    (PSTRAPREGINFO)                                             /*          */
      vvPMIPortToPPLE( CurrValue,                               /*          */
                       fname );                                 /*          */

  tok = vvPMINextToken(pmifile);        /* get test mask value */
  if (tok != TOK_VALUE)
    return (TOK_ERROR_SYNTAX);          /* force an error */
  pHWCmd->hwModify.ulOperand = CurrValue;

  tok = vvPMINextToken(pmifile);        /* get count value */
  if (tok != TOK_VALUE)
    return (TOK_ERROR_SYNTAX);          /* force an error */
  pHWCmd->hwCount.ulOperand = CurrValue;

  tok = vvPMINextToken(pmifile);        /* get timeout value */
  if (tok != TOK_VALUE)
    return (TOK_ERROR_SYNTAX);          /* force an error */
  pHWCmd->hwIndex.ulOperand = CurrValue;

  tok = vvPMINextToken(pmifile);        /* get SET/CLEAR value */
  if (tok != TOK_VALUE)
    return (TOK_ERROR_SYNTAX);          /* force an error */
  pHWCmd->hwTarget.ulOperand = CurrValue;

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

  tok = vvPMINextToken(pmifile);        /* get SEMICOLON */
  if (tok != TOK_SEMICOLON)
    tok = TOK_ERROR_SYNTAX;             /* force an error */
  return (tok);
}
                                                                /*          */
/***************************************************************************
 *
 * FUNCTION NAME = vvPMIParseWaitW()
 *
 * DESCRIPTION   = Parse and return values from a WAIT() command.
 *
 *      WAITW(<status port>, <test mask>, <count>, <0|1>);
 *
 *   Examples:
 *      WaitW(0x9ae8, 0x02ff, 0x0064, 0x0000); wait until fifo empty
 *
 * INPUT         = NONE
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL = NONE
 * RETURN-ERROR  = NONE
 *
 **************************************************************************/

PMITOKEN PRIVENTRY vvPMIParseWaitW(                             /*          */
  PMITOKEN tok,
  HFILE pmifile,
  char * fname,
  PHWCMD pHWCmd )
{
  tok = vvPMINextToken(pmifile);        /* get LPAREN */
  if (tok != TOK_LPAREN)
    return (TOK_ERROR_SYNTAX);          /* force an error */

  tok = vvPMINextToken(pmifile);        /* get port value */
  if (tok != TOK_VALUE)
    return (TOK_ERROR_SYNTAX);          /* force an error */
  pHWCmd->hwSource.psTrapRegOperand =                           /*          */
    vvPMIPortToTrapReg( CurrValue,                              /*          */
                        fname );                                /*          */

  tok = vvPMINextToken(pmifile);        /* get test mask value */
  if (tok != TOK_VALUE)
    return (TOK_ERROR_SYNTAX);          /* force an error */
  pHWCmd->hwModify.ulOperand = CurrValue;

  tok = vvPMINextToken(pmifile);        /* get timeout value */
  if (tok != TOK_VALUE)
    return (TOK_ERROR_SYNTAX);          /* force an error */
  pHWCmd->hwIndex.ulOperand = CurrValue;

  tok = vvPMINextToken(pmifile);        /* get SET/CLEAR value */
  if (tok != TOK_VALUE)
    return (TOK_ERROR_SYNTAX);          /* force an error */
  pHWCmd->hwTarget.ulOperand = CurrValue;

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

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

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

PMITOKEN PRIVENTRY vvPMIParseVariable(
  PMITOKEN tok,
  HFILE pmifile,
  char * fname,
  PHWCMD pHWCmd )
{
  pHWCmd->hwTarget.ulOperand =
    vvPMIVARIndex();                    /* get variable index */
  tok = vvPMINextToken(pmifile);        /* get equals or bitwise operator */
  switch (tok)
  {
    case TOK_EQUALS:
      pHWCmd->usCommand = PMICMD_REGOP_ASSIGN;
      break;
    case TOK_REGOP_AND:
      pHWCmd->usCommand = PMICMD_REGOP_AND;
      tok = vvPMINextToken(pmifile);    /* get equals */
      break;
    case TOK_REGOP_OR:
      pHWCmd->usCommand = PMICMD_REGOP_OR;
      tok = vvPMINextToken(pmifile);    /* get equals */
      break;
    case TOK_REGOP_XOR:
      pHWCmd->usCommand = PMICMD_REGOP_XOR;
      tok = vvPMINextToken(pmifile);    /* get equals */
      break;
    case TOK_REGOP_SHL:
      pHWCmd->usCommand = PMICMD_REGOP_SHL;
      tok = vvPMINextToken(pmifile);    /* get equals */
      if( tok == TOK_REGOP_SHL )                                /*          */
        tok = vvPMINextToken(pmifile);  /* get equals */        /*          */
      break;
    case TOK_REGOP_SHR:
      pHWCmd->usCommand = PMICMD_REGOP_SHR;
      tok = vvPMINextToken(pmifile);    /* get equals */
      if( tok == TOK_REGOP_SHR )                                /*          */
        tok = vvPMINextToken(pmifile);  /* get equals */        /*          */
      break;
    default:
      return (TOK_ERROR_SYNTAX);        /* force an error */
  }
  if (tok != TOK_EQUALS)
    return (TOK_ERROR_SYNTAX);          /* force an error */
  tok = vvPMINextToken(pmifile);        /* get operand */
  pHWCmd->usFlags = PMIFLAG_NONE;
  if( pHWCmd->usCommand != PMICMD_REGOP_ASSIGN ) /* Rx OP= ... */
  {
    pHWCmd->hwSource = pHWCmd->hwTarget;/* => Rx = Rx OP ... */
    if( tok == TOK_VARIABLE )           /* Rx = Ry OP Rz */     /*          */
    {
      pHWCmd->usFlags = PMIFLAG_REGISTER;
      pHWCmd->hwModify.ulOperand = vvPMIVARIndex();             /*          */
      tok = vvPMINextToken(pmifile);    /* get semicolon */
    }
    else if( tok == TOK_VALUE )         /* Rx = Ry OP imm */
    {
      pHWCmd->hwModify.ulOperand = CurrValue;
      tok = vvPMINextToken(pmifile);    /* get semicolon */
    }
    else
      tok = TOK_ERROR_SYNTAX;           /* force an error */
  }
  else                                  /* Rx = ... */
  {
    if( tok == TOK_VARIABLE )           /* Rx = Ry ... */       /*          */
    {
      pHWCmd->hwSource.ulOperand = vvPMIVARIndex();             /*          */
      tok = vvPMINextToken(pmifile);    /* get semi or bitwise operator */
      switch (tok)                      /* Rx = Ry OP? */
      {
        case TOK_REGOP_AND:
          pHWCmd->usCommand = PMICMD_REGOP_AND;
          tok = vvPMINextToken(pmifile);     /* get operand */
          break;
        case TOK_REGOP_OR:
          pHWCmd->usCommand = PMICMD_REGOP_OR;
          tok = vvPMINextToken(pmifile);     /* get operand */
          break;
        case TOK_REGOP_XOR:
          pHWCmd->usCommand = PMICMD_REGOP_XOR;
          tok = vvPMINextToken(pmifile);     /* get operand */
          break;
        case TOK_REGOP_SHL:
          pHWCmd->usCommand = PMICMD_REGOP_SHL;
          tok = vvPMINextToken(pmifile);     /* get operand */
          if( tok == TOK_REGOP_SHL )                            /*          */
            tok = vvPMINextToken(pmifile);   /* get operand */  /*          */
          break;
        case TOK_REGOP_SHR:
          pHWCmd->usCommand = PMICMD_REGOP_SHR;
          tok = vvPMINextToken(pmifile);     /* get operand */
          if( tok == TOK_REGOP_SHR )                            /*          */
            tok = vvPMINextToken(pmifile);   /* get operand */  /*          */
          break;
        default:
          break;
      }
      if( pHWCmd->usCommand == PMICMD_REGOP_ASSIGN )  /* Rx = Ry OP imm */
        pHWCmd->usFlags = PMIFLAG_REGISTER;
      else if( tok == TOK_VARIABLE )    /* Rx = Ry OP Rz */     /*          */
      {
        pHWCmd->usFlags = PMIFLAG_REGISTER;
        pHWCmd->hwModify.ulOperand = vvPMIVARIndex();           /*          */
        tok = vvPMINextToken(pmifile);  /* get semicolon */
      }
      else if( tok == TOK_VALUE )       /* Rx = Ry OP imm */
      {
        pHWCmd->hwModify.ulOperand = CurrValue;
        tok = vvPMINextToken(pmifile);  /* get semicolon */
      }
      else
        tok = TOK_ERROR_SYNTAX;         /* force an error */
    }
    else if( tok == TOK_VALUE )         /* Rx = imm */
    {
      pHWCmd->hwSource.ulOperand = CurrValue;
      tok = vvPMINextToken(pmifile);    /* get semicolon */
    }
    else
      tok = TOK_ERROR_SYNTAX;           /* force an error */
  }
  if (tok != TOK_SEMICOLON)
    tok = TOK_ERROR_SYNTAX;             /* force an error */
  return ( tok );
}

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

PMITOKEN PRIVENTRY vvPMIProcessCmdSection(
  PMITOKEN tok,
  HFILE pmifile,
  char * fname,                                                 /*          */
  PHWCMD *ppHWCmd,
  USHORT usCmdLimit )                                           /*          */
{
  USHORT DataPort,IndexPort,StartIndex,Count;
  USHORT usCmdCount;                                            /*          */
  PHWCMD pHWCmd;

  usCmdCount = 0;                                               /*          */
  tok = vvPMINextToken(pmifile);

  if( (tok == TOK_SETBANKCALL)                                  /*          */
      || (tok == TOK_GETBANKCALL) )                             /*          */
  {                                                             /*          */
    tok = vvPMINextToken( pmifile );                            /*          */
    if( tok != TOK_SEMICOLON )                                  /*          */
      return( TOK_ERROR_SYNTAX );   /* force an error */        /*          */
    tok = vvPMINextToken( pmifile );                            /*          */
  }                                                             /*          */
  else                                                          /*          */
  {
    if ((tok <= TOK_SM_BEGIN) || (tok >= TOK_SM_END))
      return (TOK_ERROR_SYNTAX);       /* force an error */
    /* Start list at next place in array */
    *ppHWCmd = &aHWCmdSVGA [usaHWCmdSVGANext];
    pHWCmd = *ppHWCmd;
    do
    {
      pHWCmd->usCommand = PMICMD_NONE;
      switch (tok)
      {
        case  TOK_INBYTE :
        case  TOK_INWORD :
          if (tok == TOK_INBYTE)
            pHWCmd->usCommand = PMICMD_INB;
          else
            pHWCmd->usCommand = PMICMD_INW;
          tok = vvPMIParseInCmd( tok,
                                 pmifile,
                                 fname,                         /*          */
                                 pHWCmd );
          break;
        case  TOK_OUTBYTE :
        case  TOK_OUTWORD :
          if (tok == TOK_OUTBYTE)
            pHWCmd->usCommand = PMICMD_OUTB;
          else
            pHWCmd->usCommand = PMICMD_OUTW;
          tok = vvPMIParseOutCmd( tok,
                                  pmifile,
                                  fname,                        /*          */
                                  pHWCmd );
          break;

        case  TOK_OUTBLOCK :
          pHWCmd->usCommand   = PMICMD_BOUTB;
          tok = vvPMIParseBlockCmd( tok,
                                    pmifile,
                                    fname,                      /*          */
                                    pHWCmd,
                                    &pHWCmd->hwTarget,
                                    &pHWCmd->hwSource );
          break;

        case  TOK_INBLOCK :
          pHWCmd->usCommand   = PMICMD_BINB;
          tok = vvPMIParseBlockCmd( tok,
                                    pmifile,
                                    fname,                      /*          */
                                    pHWCmd,
                                    &pHWCmd->hwSource,
                                    &pHWCmd->hwTarget );
          break;

        case  TOK_RMWBI :                                       /*          */
        case  TOK_RMWBN :                                       /*          */
        case  TOK_RMWWN :                                       /*          */
          if (tok == TOK_RMWBI)                                 /*          */
            pHWCmd->usCommand = PMICMD_RMWBI;                   /*          */
          else if (tok == TOK_RMWBN)                            /*          */
            pHWCmd->usCommand = PMICMD_RMWBN;                   /*          */
          else
            pHWCmd->usCommand = PMICMD_RMWWN;                   /*          */
          pHWCmd->usFlags = PMIFLAG_NONE;
          tok = vvPMIParseRMW( tok,
                               pmifile,
                               fname,                           /*          */
                               pHWCmd );
          break;

        case  TOK_WAIT :
          pHWCmd->usFlags   = PMIFLAG_NONE;
          pHWCmd->usCommand = PMICMD_WAIT;
          tok = vvPMIParseWait( tok,
                                pmifile,
                                fname,                          /*          */
                                pHWCmd );
          break;
        case TOK_WAITW:
          pHWCmd->usFlags   = PMIFLAG_NONE;                     /*          */
          pHWCmd->usCommand = PMICMD_WAITW;                     /*          */
          tok = vvPMIParseWaitW( tok,                           /*          */
                                 pmifile,                       /*          */
                                 fname,                         /*          */
                                 pHWCmd );                      /*          */
          break;                                                /*          */
        case  TOK_VARIABLE :
          tok = vvPMIParseVariable( tok,
                                    pmifile,
                                    fname,                      /*          */
                                    pHWCmd );
          break;
      }
      if( tok < TOK_ERROR )
        tok = vvPMINextToken(pmifile);
      ++pHWCmd;                                                 /*          */
      if( ++usCmdCount >= usCmdLimit )
      {
        /* This message one is really just a WARNING now! */    /*          */
        /* As long as some other section is smaller than  */    /*          */
        /* the claimed max for it, all sections may fit!  */    /*          */
        vvPMILogError( fname,                                   /*          */
                       "Too Many Commands For Table" );         /*          */
        --usCmdCount;                                           /*          */
      }
      if( ++usaHWCmdSVGANext > AHWCMDSVGASIZE )                 /*          */
      {                                                         /*          */
        /* Unlike above, this message really means it! */       /*          */
        vvPMILogError( fname,                                   /*          */
                       "Too Many Commands For Table" );         /*          */
        --usaHWCmdSVGANext;                                     /*          */
        --pHWCmd;                                               /*          */
      }                                                         /*          */
    }
    while ((tok > TOK_SM_BEGIN) && (tok < TOK_SM_END));
    pHWCmd->usCommand = PMICMD_NONE;                            /*          */
    if( ++usaHWCmdSVGANext > AHWCMDSVGASIZE )                   /*          */
    {                                                           /*          */
      /* Unlike above, this message really means it! */         /*          */
      vvPMILogError( fname,                                     /*          */
                     "Too Many Commands For Table" );           /*          */
      --usaHWCmdSVGANext;                                       /*          */
      --pHWCmd;                                                 /*          */
    }                                                           /*          */
  }
  return (tok);
}

/***************************************************************************
 *
 * FUNCTION NAME = vvPMIIdentifySVGA
 *
 * DESCRIPTION   = Get SVGA chip id and VRAM size.
 *
 * INPUT         = none
 *
 * OUTPUT        = sSVGAData structure
 *
 * RETURN-NORMAL = TRUE
 * RETURN-ERROR  = FALSE
 *
 ****************************************************************************/

BOOL PRIVENTRY vvPMIIdentifySVGA(OEMSVGAINFO *sSVGAData)        /*          */
{
  HFILE hScreenDD;        // handle of SCREENDD$ device driver
  ULONG ulOpenAction;   // action taken to open device
  ULONG rc;             // function return code
  ULONG ulSize = sizeof (OEMSVGAINFO );

  if ( rc = VDHOpen(SCREENDD_NAME,
                       SSToDS(&hScreenDD),
                       SSToDS(&ulOpenAction),
                       0L, 0, VDHOPEN_FILE_OPEN,
                       VDHOPEN_ACCESS_READONLY | VDHOPEN_SHARE_DENYNONE,
                       0L))
  {
    rc = VDHDevIOCtl( hScreenDD,
                      SCREENDD_CATEGORY,
                      SCREENDD_SVGA_ID,
                      NULL, 0L, NULL,
                      (VOID *) sSVGAData,
                      ulSize,
                      NULL);
    if (rc)                                                     /*          */
    {
      ulSize = sizeof (OEMINFO );
      sOEMData.OEMLength = ulSize;
      rc = VDHDevIOCtl( hScreenDD,
                        SCREENDD_CATEGORY,
                        SCREENDD_SVGA_OEM,
                        NULL, 0L, NULL,
                        &sOEMData,
                        ulSize,
                        NULL);
    }
    VDHClose(hScreenDD);
  }
  return rc;
} /*            end */
                                                                /*          */
/***************************************************************************
 *
 * FUNCTION NAME = vvPMIParseAdapterType
 *
 * DESCRIPTION   =
 *
 * INPUT         =
 *
 * OUTPUT        = usAdapterType set
 *                 usChipType set
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 **************************************************************************/

PMITOKEN PRIVENTRY vvPMIParseAdapterType(
  PMITOKEN tok,
  HFILE pmifile,
  char * fname )
{
  ULONG ulPMIAdapterType;
  ULONG ulPMIChipType;

  tok = vvPMINextToken(pmifile);
  if( tok == TOK_UNKNOWN )                                      /*          */
    for ( ulPMIAdapterType = SVGA_LASTADAPTER;                  /*          */
          ulPMIAdapterType                                      /*          */
          && !(apsAllAdapterInfo [ulPMIAdapterType].apsInfo     /*          */
               && !strcmp( apsAllAdapterInfo                    /*          */
                             [ulPMIAdapterType].apsInfo->       /*          */
                               pszSVGAAdapterName,              /*          */
                           pszPMIStrIn ));                      /*          */
          --ulPMIAdapterType )                                  /*          */
    {                                                           /*          */
      /* Ends with ulPMIAdapterType == 0 == UNKNOWN_ADAPTER */  /*          */
      /* when no match! */                                      /*          */
    }                                                           /*          */
  /* Allow downlevel SVGA.EXE/PMI files */                      /*          */
  if (tok == TOK_LPAREN)                                        /*          */
  {                                                             /*          */
    tok = vvPMINextToken(pmifile);                              /*          */
    if (tok == TOK_NULL)                                        /*          */
    {                                                           /*          */
      tok = vvPMINextToken(pmifile);                            /*          */
      if (tok == TOK_RPAREN)                                    /*          */
      {                                                         /*          */
        /* PMI is downlevel: */                                 /*          */
        vvPMILogError( fname,                                   /*          */
                       "Downlevel SVGA.EXE" );                  /*          */
        tok = TOK_UNKNOWN;                                      /*          */
        ulPMIAdapterType = UNKNOWN_ADAPTER;                     /*          */
      }                                                         /*          */
    }                                                           /*          */
  }                                                             /*          */
  /* Allow uplevel SCREENDD & PMI files */                      /*          */
  /* Or downlevel PMI:                  */                      /*          */
  /* Unknown adapter type is OK! */                             /*          */
  if( tok != TOK_UNKNOWN )                                      /*          */
    tok = TOK_UNKNOWN;                  /* force an error */
  else
  {
    tok = vvPMINextToken( pmifile );
    vvPMICompareAdapterType(                                    /*          */
      ulPMIAdapterType,                                         /*          */
      ((ulPMIAdapterType == sSVGA.AdapterType)                  /*          */
       ? sSVGA.ChipType                                         /*          */
       : UNKNOWN_CHIP),                                         /*          */
      fname );                                                  /*          */
  }
  return( tok );
}
                                                                /*          */
/***************************************************************************
 *
 * FUNCTION NAME = vvPMIParseChipType
 *
 * DESCRIPTION   =
 *
 * INPUT         =
 *
 * OUTPUT        = usAdapterType set
 *                 usChipType set
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 **************************************************************************/

PMITOKEN PRIVENTRY vvPMIParseChipType(
  PMITOKEN tok,
  HFILE pmifile,
  char * fname )
{
  ULONG ulPMIChipType;

  tok = vvPMINextToken(pmifile);
  if( tok == TOK_UNKNOWN )                                      /*          */
    ulPMIChipType = vvPMIMatchChipType( psCurAdapterInfo );     /*          */
  /* Allow downlevel SVGA.EXE/PMI files */                      /*          */
  if (tok == TOK_LPAREN)                                        /*          */
  {                                                             /*          */
    tok = vvPMINextToken(pmifile);                              /*          */
    if (tok == TOK_NULL)                                        /*          */
    {                                                           /*          */
      tok = vvPMINextToken(pmifile);                            /*          */
      if (tok == TOK_RPAREN)                                    /*          */
      {                                                         /*          */
        /* PMI is downlevel: */                                 /*          */
        vvPMILogError( fname,                                   /*          */
                       "Downlevel SVGA.EXE" );                  /*          */
        tok = TOK_UNKNOWN;                                      /*          */
        ulPMIChipType = UNKNOWN_CHIP;                           /*          */
      }                                                         /*          */
    }                                                           /*          */
  }                                                             /*          */
  /* Allow uplevel SCREENDD & PMI files */                      /*          */
  /* Or downlevel PMI:                  */                      /*          */
  /* Unknown chip type is OK! */                                /*          */
  if( tok != TOK_UNKNOWN )                                      /*          */
    tok = TOK_UNKNOWN;                  /* force an error */
  else
  {
    tok = vvPMINextToken( pmifile );
    vvPMICompareChipType( ulSVGAAdapterType,                    /*          */
                          ulPMIChipType,                        /*          */
                          ulSVGAVRAMSize,                       /*          */
                          fname );                              /*          */
  }
  return( tok );
}

/***************************************************************************
 *
 * FUNCTION NAME = vvPMIProcessFile()
 *
 * DESCRIPTION   = The main loop of this module which processes a stream of
 *                 tokens to parse the input text file. Data structures
 *                 are updated as this proceeds.
 *
 * INPUT         = PCHAR - fname
 *                 ULONG - AdapterType
 *                 ULONG - Chiptype
 *
 * OUTPUT        =
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 * REMARK:         If chip and adapter from screendd don't match the PMI
 *                 file, function returns success, but chip and adapter
 *                 type are reset to 0.                                   
 *
 **************************************************************************/

VOID    PRIVENTRY vvPMIProcessFile(CHAR * fname)
{
  HFILE           pmifile;
  ULONG           ulAction;
  PMITOKEN        tok;
  ULONG           Boot_Drive;                                   /*          */

  if (!fPMIProcessed)                                           /*          */
  {
    /*
    ** Determine the SVGA chipset and adapter
    */
    fPMILoaded = FALSE;                                         /*          */
    /* Process PMI file regardless of downlevel SCREENDD! */    /*          */
    /*!! Shouldn't vvPMIIdentifySVGA be called as early as  */  /*          */
    /*!! possible? In VDDInit!                              */  /*          */
    vvPMIIdentifySVGA( &sSVGA );                  /*          *//*          */
    vvPMISetAdapterType( (ULONG) sSVGA.AdapterType,             /*          */
                         (ULONG) sSVGA.ChipType,                /*          */
                         sSVGA.Memory );                        /*          */

    /* Yield correct drive letter nth time thru during debug */ /*          */
    Boot_Drive = VDHQuerySysValue( CURRENT_VDM,                 /*          */
                                   VDHGSV_BOOTDRV );            /*          */
    fname [0] = (char) ('a' + (Boot_Drive - 1));                /*          */

    if( VDHOpen( fname,                                         /*          */
                 SSToDS( &pmifile ),                            /*          */
                 SSToDS( &ulAction ),                           /*          */
                 0L,                                            /*          */
                 VDHOPEN_FILE_NORMAL,
                 VDHOPEN_FILE_OPEN,
                 VDHOPEN_ACCESS_READONLY
                 | VDHOPEN_SHARE_DENYWRITE
                 | VDHOPEN_FLAGS_SEQUENTIAL,
                 NULL ) )
    {
      vvPMINextChar(pmifile);           /* look ahead the 1st char */
      tok = vvPMINextToken(pmifile);
      do
      {
        switch (tok)
        {
          case TOK_PMIVERSION:                                  /*          */
          case TOK_IDENTIFYADAPTER:                             /*          */
          case TOK_PROGRAMDAC:                                  /*          */
          case TOK_BLOCKCOMMENT:
          case TOK_INCLUDECODE:                                 /*          */
          case TOK_MODEINFO:
          case TOK_SETMODE:
          case TOK_MONITORTYPE:
            tok = vvPMISkipSection(tok, pmifile);
            break;                                              /*          */
          case TOK_HARDWARE:                                    /*          */
            tok = vvPMIParseHardware( tok,                      /*          */
                                      pmifile,                  /*          */
                                      fname );                  /*          */
            break;                                              /*          */
          case TOK_ADAPTERTYPE:
            tok = vvPMIParseAdapterType( tok,                   /*          */
                                         pmifile,               /*          */
                                         fname );               /*          */
            break;
          case TOK_CHIPSET:
            tok = vvPMIParseChipType( tok,                      /*          */
                                      pmifile,                  /*          */
                                      fname );                  /*          */
            break;
          case TOK_TRAPREGS:
            tok = vvPMIProcessTrapRegs(tok, pmifile, fname);    /*          */
            break;
          case TOK_UNLOCK:
            tok = vvPMIProcessCmdSection(
                    tok,                                        /*          */
                    pmifile,                                    /*          */
                    fname,                                      /*          */
                    &pHWCmdSVGAUnLockData,                      /*          */
                    MAX_LOCKUNLOCKCMD );                        /*          */
            break;
          case TOK_LOCK:
            tok = vvPMIProcessCmdSection(
                    tok,                                        /*          */
                    pmifile,                                    /*          */
                    fname,                                      /*          */
                    &pHWCmdSVGALockData,                        /*          */
                    MAX_LOCKUNLOCKCMD );                        /*          */
            break;
          case TOK_CLEANUP:
            tok = vvPMIProcessCmdSection(
                    tok,                                        /*          */
                    pmifile,                                    /*          */
                    fname,                                      /*          */
                    &pHWCmdSVGACleanData,                       /*          */
                    MAX_CLEANDATA );                            /*          */
            break;
          case TOK_SETBANK:                                     /*          */
          case TOK_SETBANKLINEAR:                               /*          */
            tok = vvPMIProcessCmdSection(
                    tok,                                        /*          */
                    pmifile,                                    /*          */
                    fname,                                      /*          */
                    &pHWCmdSVGASetBankData,                     /*          */
                    MAX_SETBANK_CMD );                          /*          */
//          Disable PMI Bank Routines Until Further Tests       /*          */
//          psCurAdapterInfo->pfnSVGASetBank = &vvSVGASetBank;  /*          */
            break;                                              /*          */
          case TOK_GETBANK:                                     /*          */
          case TOK_GETBANKLINEAR:                               /*          */
            tok = vvPMIProcessCmdSection(
                    tok,                                        /*          */
                    pmifile,                                    /*          */
                    fname,                                      /*          */
                    &pHWCmdSVGAGetBankData,                     /*          */
                    MAX_GETBANK_CMD );                          /*          */
//          Disable PMI Bank Routines Until Further Tests       /*          */
//          psCurAdapterInfo->pulfnSVGAGetBank = &vvSVGAGetBank;/*          */
            break;                                              /*          */
          default:
            /* Avoid looping forever! */                        /*          */
            tok = TOK_UNKNOWN;                                  /*          */
            break;
        }
        if (tok == TOK_UNKNOWN)
        {
          vvPMILogError( fname,                                 /*          */
                         "Unrecognized/Unexpected Token" );     /*          */
          tok = vvPMISkipSection(tok, pmifile);
        }
      }
      while(tok != TOK_EOF);
      fPMILoaded = TRUE;
      VDHClose(pmifile);
    }
    else                                                        /*          */
    {                                                           /*          */
      /*!! Do we really want to do this?  Or do we want */      /*          */
      /*!! to take advantage of the SCREENDD info? */           /*          */
      /* NOP any SVGA functionality */                          /*          */
      vvPMISetAdapterType( UNKNOWN_ADAPTER,                     /*          */
                           UNKNOWN_CHIP,                        /*          */
                           0x00040000L );       /* 256K=4*64K *//*          */
    }                                                           /*          */
    fPMIProcessed = TRUE;
    flInt10Disabled  |= !(apsAllAdapterInfo[ulSVGAAdapterType].flAdapterFlag & VDMX_INT10RESTORE);              /*            */
  }
}

#pragma END_SWAP_CODE

