/*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    pmiGetc()           Return the next character from the PMI file.
 *              NextChar()          Calls pmiGetc() to return the next character.
 *              GetLongValue()      Convert ASCII string to unsigned long.
 *              NextToken()         Returns the next token and updates appropriate globals.
 *              SkipSection()       This reads and discards tokens.
 *              ParseOutB()         Parse and return values from an OUTB() command.
 *              ProcessUnlock()     Unlock Adapters extended registers.
 *              ProcessLock()       Lock Adapters extended registers.
 *              ProcessTrapRegs()   Process trap registers.
 *              ProcessCleanup()    Load process clean up values.
 *              itoa                Convert given integer value to ASCII.
 *              ProcessPMIFile()    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  PLE     apleAll[];
extern  PLE     apleNoTrap[];
extern  ULONG   ulNumLockCmds;
extern  ULONG   ulNumUnLockCmds;
extern  USHORT  ausLockReg[];
extern  USHORT  ausLockCmd[];
extern  USHORT  ausUnLockReg[];
extern  USHORT  ausUnLockCmd[];
extern  CLEANDATA CleanData[];
extern  BYTE    abCRTMask[];
extern  BYTE    abGDCMask[];
extern  BYTE    abSEQMask[];
extern  BYTE    abATCMask[];
extern  BYTE    abATIMask[];
extern ULONG ulSVGAChipType;
extern ULONG ulSVGAAdapterType;


#define LOGERROR                1

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

#define MAX_PATH                260
#define MAX_VIDEO_MODES         8

#define ATTADDRESSPORT          0x03C0
#define SEQADDRESSPORT          0x03C4
#define GRAPHADDRESSPORT        0x03CE
#define CRTCADDRESSPORT         0x03D4

#define LINELEN                 256
#define BUFFSIZE                1024

#define PMI_MODE_COLOUR         0x08    /* Bit 3 - 0 = mono, 1 = colour   */
#define PMI_MODE_GRAPHICS       0x10    /* Bit 4 - 0 = text, 1 = graphics */

#define EOF                     (-1)

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

typedef enum _PMITOKEN

{
        TOK_SECT_BEGIN,
        TOK_ADAPTERTYPE,                /* Section Heading tokens */
        TOK_MONITORTYPE,
        TOK_SETBANK,                                            /*            */
        TOK_CHIPSET,
        TOK_BLOCKCOMMENT,
        TOK_MODEINFO,
        TOK_SETMODE,
        TOK_TRAPREGS,
        TOK_LOCK,
        TOK_UNLOCK,
        TOK_CLEANUP,
        TOK_SECT_END,

        TOK_MI_BEGIN,
        TOK_MODEATTRIBUTES,             /* ModeInfo Section tokens */
        TOK_BYTESPERSCANLINE,
        TOK_TEXTROWS,
        TOK_XRESOLUTION,
        TOK_YRESOLUTION,
        TOK_XCHARSIZE,
        TOK_YCHARSIZE,
        TOK_BITSPERPIXEL,
        TOK_NUMBEROFPLANES,
        TOK_PAGELENGTH,
        TOK_SAVESIZE,
        TOK_INTERLACEMODE,
        TOK_BUFFERADDRESS,
        TOK_MI_END,

        TOK_AT_BEGIN,
        TOK_ADAPTER_VIDEO7,             /* AdapterType Section Tokens */
        TOK_ADAPTER_TRIDENT,
        TOK_ADAPTER_TSENG,
        TOK_ADAPTER_WESTERNDIGITAL,
        TOK_ADAPTER_ATI,
        TOK_ADAPTER_IBM,                                                /*            */
        TOK_ADAPTER_CIRRUS,                                             /*            */
        TOK_ADAPTER_S3,                                                 /*            */
        TOK_AT_END,

        TOK_CT_BEGIN,
        TOK_CHIP_HT205,                 /* ChipSet Section Tokens */
        TOK_CHIP_HT208,
        TOK_CHIP_HT209,
        TOK_CHIP_8800,
        TOK_CHIP_8900,
        TOK_CHIP_ET3000,
        TOK_CHIP_ET4000,
        TOK_CHIP_PVGA1A,
        TOK_CHIP_WD9000,
        TOK_CHIP_WD9011,
        TOK_CHIP_WD9030,
        TOK_CHIP_ATI1,
        TOK_CHIP_ATI2,
        TOK_CHIP_IBM,                                                   /*            */
        TOK_CHIP_CIRRUS22,                                              /*            */
        TOK_CHIP_CIRRUS24,                                              /*            */
        TOK_CHIP_CIRRUS26,                                              /*            */
        TOK_CHIP_CIRRUS28,                                              /*            */
        TOK_CHIP_S386C80X,                                              /*            */
        TOK_CHIP_S386C928,                                              /*            */
        TOK_CT_END,

        TOK_SM_BEGIN,
        TOK_INBYTE,                     /* SetMode Section tokens */
        TOK_OUTBYTE,
        TOK_OUTWORD,
        TOK_OUTBLOCK,
        TOK_RMWBYTE,
        TOK_RMWWORD,                    /*            */
        TOK_VALUE,
        TOK_VARIABLE,
        TOK_LPAREN,
        TOK_RPAREN,
        TOK_EQUALS,
        TOK_SEMICOLON,
        TOK_HYPHEN,
        TOK_SM_END,

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

TOK     Tokens[] =

{
        "[ADAPTERTYPE]",        TOK_ADAPTERTYPE,
        "[CHIPSET]",            TOK_CHIPSET,
        "[COMMENT]",            TOK_BLOCKCOMMENT,
        "[MODEINFO]",           TOK_MODEINFO,
        "[SETMODE]",            TOK_SETMODE,
        "[TRAPREGS]",           TOK_TRAPREGS,
        "[LOCK]",               TOK_LOCK,
        "[UNLOCK]",             TOK_UNLOCK,
        "[CLEANUP]",            TOK_CLEANUP,
        "[MONITORTYPE]",        TOK_MONITORTYPE,
        "[SETBANK]",            TOK_SETBANK,                    /*            */

        "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,

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

        "HT205",                TOK_CHIP_HT205,
        "HT208",                TOK_CHIP_HT208,
        "HT209",                TOK_CHIP_HT209,
        "TR8800",               TOK_CHIP_8800,
        "TR8900",               TOK_CHIP_8900,
        "ET3000",               TOK_CHIP_ET3000,
        "ET4000",               TOK_CHIP_ET4000,
        "PVGA1A",               TOK_CHIP_PVGA1A,
        "PVGA1B",               TOK_CHIP_WD9000,
        "PVGA1C",               TOK_CHIP_WD9011,
        "PVGA1D",               TOK_CHIP_WD9030,
        "ATI18800",             TOK_CHIP_ATI1,
        "ATI28800",             TOK_CHIP_ATI2,
        "IBMSVGA",              TOK_CHIP_IBM,                        /*            */
        "GD5422",               TOK_CHIP_CIRRUS22,                   /*            */
        "GD5424",               TOK_CHIP_CIRRUS24,                   /*            */
        "GD5426",               TOK_CHIP_CIRRUS26,                   /*            */
        "GD5428",               TOK_CHIP_CIRRUS28,                   /*            */
        "S386C80X",             TOK_CHIP_S386C80X,                   /*            */
        "S386C928",             TOK_CHIP_S386C928,                   /*            */

        "INB",                  TOK_INBYTE,
        "OUTB",                 TOK_OUTBYTE,
        "OUTW",                 TOK_OUTWORD,
        "BOUTB",                TOK_OUTBLOCK,
        "RMWB",                 TOK_RMWBYTE,
        "RMWW",                 TOK_RMWWORD,                         /*            */
};

USHORT ChipTable[] =

{
        1,      /* TOK_CHIP_HT205 */
        2,      /* TOK_CHIP_HT208 */
        3,      /* TOK_CHIP_HT209 */
        1,      /* TOK_CHIP_8800 */
        2,      /* TOK_CHIP_8900 */
        1,      /* TOK_CHIP_ET3000 */
        2,      /* TOK_CHIP_ET4000 */
        1,      /* TOK_CHIP_PVGA1A */
        2,      /* TOK_CHIP_WD9000 */
        3,      /* TOK_CHIP_WD9011 */
        4,      /* TOK_CHIP_WD9030 */
        1,      /* TOK_CHIP_ATI1 */
        2,      /* TOK_CHIP_ATI2 */
        1,      /* TOK_CHIP_IBM                                                    */
        1,      /* TOK_CHIP_CIRRUS22                                               */
        2,      /* TOK_CHIP_CIRRUS24                                               */
        3,      /* TOK_CHIP_CIRRUS26                                               */
        3,      /* TOK_CHIP_CIRRUS28                                               */
        1,      /* TOK_CHIP_S386C80X                                               */
        2,      /* TOK_CHIP_S386C928                                              */
};

USHORT          ColNo  = 0;
USHORT          LineNo = 1;

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

USHORT          AdapterType   = 0;
USHORT          ChipType      = 0;
USHORT          VARRegs[256]  = {0};

VALIDMODES      SupportedModes[MAX_VIDEO_MODES] =

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

#pragma END_SWAP_DATA

#pragma BEGIN_SWAP_CODE


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

CHAR    PRIVENTRY pmiGetc(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 = NextChar()
 *
 * DESCRIPTION   = Calls pmiGetc() to return the next character and updates
 *                 the global variables 'ch' and 'upch' with the character
 *                 and its uppercase equivalent, respectively.
 *
 * INPUT         = HFILE
 *
 * OUTPUT        = None
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 **************************************************************************/

VOID    PRIVENTRY NextChar(HFILE f)
{
        ch = pmiGetc(f);
        if ((ch >= 'a') && (ch <= 'z'))
            upch = ch - (CHAR)('a' - 'A');
        else
            upch = ch;

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


/***************************************************************************
 *
 * FUNCTION NAME = GetLongValue()
 *
 * DESCRIPTION   = This is a bit convoluted because we can't use library
 *                 functions.  First check to see if string is a hex one and
 *                 adjust pointer and radix as necessary.  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 GetLongValue(CHAR * s)
{
        ULONG   v = 0, radix = 10;

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

        while (*s)
        {
            _asm
            {                           ; 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 = NextToken()
 *
 * DESCRIPTION   = Returns the next token and updates appropriate global
 *                 variables such as: the current variable name and the
 *                 current numeric value just read.
 *
 *                 Comments ('C' style) will be skipped if encountered.
 *
 * INPUT         = HFILE
 *
 * OUTPUT        = PMITOKEN - Next token
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 **************************************************************************/

PMITOKEN PRIVENTRY NextToken(HFILE f)
{
        PMITOKEN        tok = TOK_EOF;
        CHAR            szStrIn[LINELEN];
        int             i;

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

                i = 0;

                do
                {
                    szStrIn[i++] = upch;
                    NextChar(f);
                } while ( ((upch >= 'A') && (upch <= 'Z')) ||
                          ((upch >= '0') && (upch <= '9')) ||
                          ((upch == ']')));

                    szStrIn[i] = '\0';

                for (i=0; i<sizeof(Tokens)/sizeof(TOK); i++)

                    if (!strcmp(SSToDS(szStrIn), Tokens[i].tok_txt))
                        return(Tokens[i].tok_val);

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

                if ( (szStrIn[0] == 'R') &&
                    ((szStrIn[1] >= '0') && (szStrIn[1] <= '9')) )
                {
                    strcpy(CurrVariable, SSToDS(szStrIn));
                    return(TOK_VARIABLE);
                }
                  else
                    return(TOK_UNKNOWN);

            }

            else

              if ((upch >= '0') && (upch <= '9'))

              {

                i = 0;
                do
                {
                    szStrIn[i++] = upch;
                    NextChar(f);
                } while ( (upch == 'X') ||
                         ((upch >= '0') && (upch <= '9')) ||
                         ((upch >= 'A') && (upch <= 'F')) );

                szStrIn[i] = '\0';
                CurrValue = GetLongValue(SSToDS(szStrIn));
                return(TOK_VALUE);
              }
                else switch (upch)
                {
                  case ' ': case '\t': case '\n': case ',':
                    NextChar(f);
                    break;
                  case '(':
                    NextChar(f);
                    return(TOK_LPAREN);
                  case ')':
                    NextChar(f);
                    return(TOK_RPAREN);
                  case '=':
                    NextChar(f);
                    return(TOK_EQUALS);
                  case ';':
                    NextChar(f);
                    return(TOK_SEMICOLON);
                  case '-':
                    NextChar(f);
                    return(TOK_HYPHEN);
                  case '/':                                       /* comment? */
                    NextChar(f);
                    if ((ch == '/') || (ch == '*'))
                    {
                      if (ch == '/')
                      {

                        /*
                        ** skip to end-of-line
                        */

                        while ((ch != '\n') && (ch != EOF))
                                NextChar(f);
                        if (ch == '\n')
                            NextChar(f);
                      }

                      else

                      {

                            /*
                            ** skip to comment end
                            */

                            NextChar(f);
                            do
                            {
                                while ((ch != '*') && (ch != EOF))
                                    NextChar(f);
                                NextChar(f);
                            } while ((ch != '/') && (ch != EOF));

                            NextChar(f);
                      }
                    }

                    break;

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


/***************************************************************************
 *
 * FUNCTION NAME = SkipSection()
 *
 * 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 SkipSection(PMITOKEN tok, HFILE pmifile)
{
        do tok = NextToken(pmifile);
        while ((tok > TOK_SECT_END) && (tok != TOK_EOF));
        return (tok);
}


/***************************************************************************
 *
 * FUNCTION NAME = ParseOutB()
 *
 * DESCRIPTION   = Parse and return values from an OUTB() command.
 *
 * INPUT         = PMITOKEN - tok
 *                 HFILE - pmifile
 *                 PUSHORT - DataPort
 *                 PUSHORT - Data
 *
 * OUTPUT        = PMITOKEN
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 **************************************************************************/

PMITOKEN PRIVENTRY ParseOutB(PMITOKEN tok, HFILE pmifile, PUSHORT DataPort, PUSHORT Data)
{
        tok = NextToken(pmifile);       /* get LPAREN */
        if (tok != TOK_LPAREN)
            return(TOK_UNKNOWN);        /* force an error */

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

        tok = NextToken(pmifile);       /* get data byte value */
        if (tok != TOK_VALUE)
            return(TOK_UNKNOWN);        /* force an error */
        *Data = (USHORT)CurrValue;

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

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

        return(tok);
}


/***************************************************************************
 *
 * FUNCTION NAME = ProcessUnlock()
 *
 * DESCRIPTION   = This section contains a sequence of outb() commands
 *                 which UNLOCK the adapter's extended registers.
 *
 * INPUT         = PMITOKEN - tok
 *                 HFILE - pmifile
 *
 * OUTPUT        = PMITOKEN
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 **************************************************************************/

PMITOKEN PRIVENTRY ProcessUnlock(PMITOKEN tok, HFILE pmifile)
{
        ulNumUnLockCmds = 0;
        tok = NextToken(pmifile);
        do
        {
          if ((tok <= TOK_SM_BEGIN) || (tok >= TOK_SM_END))
              return(TOK_UNKNOWN);    /* force an error */

            switch (tok)
            {
              case TOK_OUTBYTE:
                  tok = ParseOutB(tok,
                                  pmifile,
                                  &ausUnLockReg[ulNumUnLockCmds],
                                  &ausUnLockCmd[ulNumUnLockCmds]);
                  ulNumUnLockCmds++;
                  break;
            }

            if (tok != TOK_EOF)
                tok = NextToken(pmifile);

        }
        while ((tok > TOK_SM_BEGIN) && (tok < TOK_SM_END)
           && (ulNumUnLockCmds < MAX_LOCKUNLOCKCMD));

        if (ulNumUnLockCmds > MAX_LOCKUNLOCKCMD)
            tok = TOK_UNKNOWN;

        return (tok);
}


/***************************************************************************
 *
 * FUNCTION NAME = ProcessLock()
 *
 * DESCRIPTION   = This section contains a sequence of outb() or outw()
 *                 commands which LOCK the adapter's extended registers.
 *
 * INPUT         = PMITOKEN - tok
 *                 HFILE - pmifile
 *
 * OUTPUT        = PMITOKEN
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 **************************************************************************/

PMITOKEN PRIVENTRY ProcessLock(PMITOKEN tok, HFILE pmifile)
{
        ulNumLockCmds = 0;
        tok = NextToken(pmifile);
        do
        {
          if ((tok <= TOK_SM_BEGIN) || (tok >= TOK_SM_END))
              return(TOK_UNKNOWN);    /* force an error */

          switch (tok)
          {
            case TOK_OUTBYTE:
                tok = ParseOutB(tok,
                                pmifile,
                                &ausLockReg[ulNumLockCmds],
                                &ausLockCmd[ulNumLockCmds]);
                ulNumLockCmds++;
                break;
          }

          if (tok != TOK_EOF)
              tok = NextToken(pmifile);

        }
        while ((tok > TOK_SM_BEGIN) && (tok < TOK_SM_END)
          && (ulNumLockCmds < MAX_LOCKUNLOCKCMD));

        if (ulNumLockCmds > MAX_LOCKUNLOCKCMD)
            tok = TOK_UNKNOWN;

        return (tok);
}


/***************************************************************************
 *
 * FUNCTION NAME = ProcessTrapRegs()
 *
 * 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
 *
 * OUTPUT        = PMITOKEN
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 **************************************************************************/

PMITOKEN PRIVENTRY ProcessTrapRegs(PMITOKEN tok, HFILE pmifile)
{
        USHORT          Port, RangeStart, RangeEnd;
        PBYTE           pbData;
        PPLE            pple;

        tok = NextToken(pmifile);
        do
        {

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

          Port = (USHORT)CurrValue;
          tok = NextToken(pmifile);

          while ((tok != TOK_SEMICOLON) && (tok != TOK_EOF))
          {
            if (tok != TOK_VALUE)           /* get range start */
                return(TOK_UNKNOWN);        /* force an error */

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

            if (tok != TOK_HYPHEN)
                return(TOK_UNKNOWN);        /* force an error */

            tok = NextToken(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,            */

            switch (Port)
            {
              case CRTCADDRESSPORT:
                  if (RangeEnd > MAX_CRTREGS)
                    return(TOK_UNKNOWN);        /* force an error,            */
                  pbData = &abCRTMask[0];
                  break;

              case GRAPHADDRESSPORT:
                  if (RangeEnd > MAX_GDCREGS)
                    return(TOK_UNKNOWN);        /* force an error,            */
                  pbData = &abGDCMask[0];
                  break;

              case SEQADDRESSPORT:
                  if (RangeEnd > MAX_SEQREGS)
                    return(TOK_UNKNOWN);        /* force an error,            */
                  pbData = &abSEQMask[0];
                  break;

              case ATTADDRESSPORT:
                  pbData = &abATCMask[0];
                  break;

              case PORT_ATIVGA_INDX:
                  pbData = &abATIMask[0];
                  break;

              default:
                  pbData = NULL;
                  break;
            }

            if (pbData)
            {

              /*
              ** update max no of indices used by this port
              */

              for (pple = apleAll;
                   pple->ple_port && (Port != (pple->ple_port & PORT_MASK));
                   pple++);

              if ((pple->ple_port) && (RangeEnd+1 > pple->ple_nRegs))     /*            */
                  pple->ple_nRegs = RangeEnd + 1;

              for (pple = apleNoTrap;
                   pple->ple_port && (Port != (pple->ple_port & PORT_MASK));
                   pple++);

              if ((pple->ple_port) && (RangeEnd+1 > pple->ple_nRegs))     /*            */
                  pple->ple_nRegs = RangeEnd + 1;

              while (RangeStart <= RangeEnd)
              {
                  pbData[RangeStart / 8] |= 1 << (RangeStart % 8);
                  RangeStart++;
              }
            }

            tok = NextToken(pmifile);

          }

          if (tok == TOK_SEMICOLON)
              tok = NextToken(pmifile);

        } while ((tok > TOK_SECT_END) && (tok != TOK_EOF));

        return (tok);
}


/***************************************************************************
 *
 * FUNCTION NAME = ProcessCleanup()
 *
 * DESCRIPTION   = Load process clean up values.
 *
 *                 This section consists of Read-Modify-Write commands.
 *
 *                 RMWB(<Index Port>, <Data Port>, <index>, <AND mask>, <OR mask>);
 *
 * INPUT         = PMITOKEN - tok
 *                 HFILE - pmifile
 *
 * OUTPUT        = PMITOKEN
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 **************************************************************************/

PMITOKEN PRIVENTRY ProcessCleanup(PMITOKEN tok, HFILE pmifile)
{
        USHORT  Command, IndexPort, DataPort, Index, ANDMask, ORMask;
        ULONG   ulCleanup = 0;

        tok = NextToken(pmifile);
        do
        {
          if ((tok <= TOK_SM_BEGIN) || (tok >= TOK_SM_END))
              return(TOK_UNKNOWN);            /* force an error */

          switch (tok)
          {
            case TOK_RMWBYTE:
            case TOK_RMWWORD:               /*            */

                if (tok == TOK_RMWBYTE)
                  CleanData[ulCleanup].Command = PMICMD_RMWB;   /*            */
                else
                  CleanData[ulCleanup].Command = PMICMD_RMWW;   /*            */

                tok = NextToken(pmifile);   /* get LPAREN     */
                if (tok != TOK_LPAREN)
                    return(TOK_UNKNOWN);    /* force an error */

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

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

                if (CleanData[ulCleanup].Command == PMICMD_RMWB)        /*            */
                {                            /*            */
                    tok = NextToken(pmifile);/* get index value */
                    if (tok != TOK_VALUE)
                        return(TOK_UNKNOWN); /* force an error  */
                    Index = (USHORT)CurrValue;
                }

                tok = NextToken(pmifile);   /* get AND mask   */
                if (tok != TOK_VALUE)
                    return(TOK_UNKNOWN);    /* force an error */
                ANDMask = (USHORT)CurrValue;

                tok = NextToken(pmifile);   /* get OR mask    */
                if (tok != TOK_VALUE)
                    return(TOK_UNKNOWN);    /* force an error */
                ORMask = (USHORT)CurrValue;

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

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

                CleanData[ulCleanup].IndexPort = IndexPort;
                CleanData[ulCleanup].DataPort  = DataPort;
                CleanData[ulCleanup].Index     = Index;
                CleanData[ulCleanup].ANDMask   = ANDMask;
                CleanData[ulCleanup].ORMask    = ORMask;
                ulCleanup++;
                break;
          }

          if (tok != TOK_EOF)
              tok = NextToken(pmifile);

        } while ((tok > TOK_SM_BEGIN) && (tok < TOK_SM_END) && (ulCleanup < MAX_CLEANDATA));

        return (tok);
}

#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 itoa(int v, CHAR * s)
{
        int     r;

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

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

VOID PRIVENTRY LogError(char * fname)
{
        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:     ");
            itoa(LineNo, buff+strlen(buff)-1);
            strcat(buff, ", column:     ");
            itoa(ColNo, buff+strlen(buff)-1);
            strcat(buff, "\r\n");
            ulBytesWritten = VDHWrite(errfile, buff, strlen(buff));
            VDHClose(errfile);
        }
}

#endif


/***************************************************************************
 *
 * FUNCTION NAME = ProcessPMIFile()
 *
 * 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.
 *
 *                 Return: TRUE if processing successful, FALSE otherwise.
 *
 * INPUT         = PCHAR - fname
 *                 ULONG - AdapterType
 *                 ULONG - Chiptype
 *
 * OUTPUT        = FALSE - failed
 *                 TRUE  - success
 *
 * 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.                                   
 *
 **************************************************************************/

BOOL    PRIVENTRY vvProcessPMIFile(CHAR * fname)
{
        HFILE           pmifile;
        BOOL            rc = FALSE;
        ULONG           ulAction, ulPMIAdapterType, ulPMIChipType;
        PMITOKEN        tok;
        ULONG           Boot_Drive;                             /*            */

        /*
        ** replaced with            version
        */

        Boot_Drive = (VDHQuerySysValue(CURRENT_VDM, VDHGSV_BOOTDRV)-3); /*            */
        fname[0] += Boot_Drive;                                         /*            */

        if (VDHOpen(fname, SSToDS(&pmifile), SSToDS(&ulAction), 0L,     /*            */
                    VDHOPEN_FILE_NORMAL,
                    VDHOPEN_FILE_OPEN,
                    VDHOPEN_ACCESS_READONLY | VDHOPEN_SHARE_DENYWRITE | VDHOPEN_FLAGS_SEQUENTIAL,
                    NULL))
        {
            NextChar(pmifile);                          /* look ahead the 1st char */
            tok = NextToken(pmifile);
            do
            {
              switch (tok)
              {
                  case TOK_BLOCKCOMMENT:
                  case TOK_MODEINFO:
                  case TOK_SETMODE:
                  case TOK_MONITORTYPE:
                  case TOK_SETBANK:                             /*            */
                      tok = SkipSection(tok, pmifile);
                      break;

                  case TOK_ADAPTERTYPE:
                      tok = NextToken(pmifile);

                      if ((tok < TOK_AT_BEGIN) || (tok > TOK_AT_END))
                      {
                          tok = TOK_UNKNOWN;  /* force an error */
                          break;
                      }

                      ulPMIAdapterType = tok - TOK_AT_BEGIN;
                      tok = NextToken(pmifile);

                      /*
                      **  if this isn't the same adapter, reset                   
                      **  ulSVGAAdapterType to default and log the error.
                      */

                      if (ulPMIAdapterType != ulSVGAAdapterType)
                      {
                          tok = TOK_UNKNOWN;  /* force an error */
                          ulSVGAAdapterType = 0;
                          break;
                      }
                      break;

                  case TOK_CHIPSET:
                      tok = NextToken(pmifile);
                      if ((tok < TOK_CT_BEGIN) || (tok > TOK_CT_END))
                      {
                          tok = TOK_UNKNOWN;  /* force an error */
                          break;
                      }

                      ulPMIChipType = ChipTable[tok - TOK_CT_BEGIN - 1];
                      tok = NextToken(pmifile);

                      /*
                      **  if this isn't the same chip, reset                      
                      **  ulSVGAAdapterType to default and log the error.
                      */

                      if (ulPMIChipType != ulSVGAChipType)
                      {
                          tok = TOK_UNKNOWN;  /* force an error */
                          ulSVGAChipType = 0;
                          break;
                      }
                      break;

                  case TOK_UNLOCK:
                      tok = ProcessUnlock(tok, pmifile);
                      break;

                  case TOK_LOCK:
                      tok = ProcessLock(tok, pmifile);
                      break;

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

                  case TOK_CLEANUP:
                      tok = ProcessCleanup(tok, pmifile);
                      break;

                  default:
                      break;

              }

              if (tok == TOK_UNKNOWN)
              {
                  LogError(fname);
                  tok = SkipSection(tok, pmifile);
              }


            }
            while(tok != TOK_EOF);

            rc = TRUE;
            VDHClose(pmifile);

        }

        return(rc);
}

#pragma END_SWAP_CODE

