Moved from src/mesa/main
authorMichal Krol <mjkrol@gmail.org>
Mon, 29 Mar 2004 11:09:34 +0000 (11:09 +0000)
committerMichal Krol <mjkrol@gmail.org>
Mon, 29 Mar 2004 11:09:34 +0000 (11:09 +0000)
12 files changed:
src/mesa/shader/nvfragparse.c [new file with mode: 0644]
src/mesa/shader/nvfragparse.h [new file with mode: 0644]
src/mesa/shader/nvfragprog.h [new file with mode: 0644]
src/mesa/shader/nvprogram.c [new file with mode: 0644]
src/mesa/shader/nvprogram.h [new file with mode: 0644]
src/mesa/shader/nvvertexec.c [new file with mode: 0644]
src/mesa/shader/nvvertexec.h [new file with mode: 0644]
src/mesa/shader/nvvertparse.c [new file with mode: 0644]
src/mesa/shader/nvvertparse.h [new file with mode: 0644]
src/mesa/shader/nvvertprog.h [new file with mode: 0644]
src/mesa/shader/program.c [new file with mode: 0644]
src/mesa/shader/program.h [new file with mode: 0644]

diff --git a/src/mesa/shader/nvfragparse.c b/src/mesa/shader/nvfragparse.c
new file mode 100644 (file)
index 0000000..b742ff0
--- /dev/null
@@ -0,0 +1,1767 @@
+/*
+ * Mesa 3-D graphics library
+ * Version:  6.0
+ *
+ * Copyright (C) 1999-2004  Brian Paul   All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * BRIAN PAUL BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+/**
+ * \file nvfragparse.c
+ * NVIDIA fragment program parser.
+ * \author Brian Paul
+ */
+
+/*
+ * Regarding GL_NV_fragment_program:
+ *
+ * Portions of this software may use or implement intellectual
+ * property owned and licensed by NVIDIA Corporation. NVIDIA disclaims
+ * any and all warranties with respect to such intellectual property,
+ * including any use thereof or modifications thereto.
+ */
+
+#include "glheader.h"
+#include "context.h"
+#include "hash.h"
+#include "imports.h"
+#include "macros.h"
+#include "mtypes.h"
+#include "nvfragprog.h"
+#include "nvfragparse.h"
+#include "nvprogram.h"
+#include "program.h"
+
+
+#define INPUT_1V     1
+#define INPUT_2V     2
+#define INPUT_3V     3
+#define INPUT_1S     4
+#define INPUT_2S     5
+#define INPUT_CC     6
+#define INPUT_1V_T   7  /* one source vector, plus textureId */
+#define INPUT_3V_T   8  /* one source vector, plus textureId */
+#define INPUT_NONE   9
+#define OUTPUT_V    20
+#define OUTPUT_S    21
+#define OUTPUT_NONE 22
+
+/* IRIX defines some of these */
+#undef _R
+#undef _H
+#undef _X
+#undef _C
+#undef _S
+
+/* Optional suffixes */
+#define _R  FLOAT32  /* float */
+#define _H  FLOAT16  /* half-float */
+#define _X  FIXED12  /* fixed */
+#define _C  0x08     /* set cond codes */
+#define _S  0x10     /* saturate, clamp result to [0,1] */
+
+struct instruction_pattern {
+   const char *name;
+   enum fp_opcode opcode;
+   GLuint inputs;
+   GLuint outputs;
+   GLuint suffixes;
+};
+
+static const struct instruction_pattern Instructions[] = {
+   { "ADD", FP_OPCODE_ADD, INPUT_2V, OUTPUT_V, _R | _H | _X | _C | _S },
+   { "COS", FP_OPCODE_COS, INPUT_1S, OUTPUT_S, _R | _H |      _C | _S },
+   { "DDX", FP_OPCODE_DDX, INPUT_1V, OUTPUT_V, _R | _H |      _C | _S },
+   { "DDY", FP_OPCODE_DDY, INPUT_1V, OUTPUT_V, _R | _H |      _C | _S },
+   { "DP3", FP_OPCODE_DP3, INPUT_2V, OUTPUT_S, _R | _H | _X | _C | _S },
+   { "DP4", FP_OPCODE_DP4, INPUT_2V, OUTPUT_S, _R | _H | _X | _C | _S },
+   { "DST", FP_OPCODE_DP4, INPUT_2V, OUTPUT_V, _R | _H |      _C | _S },
+   { "EX2", FP_OPCODE_DP4, INPUT_1S, OUTPUT_S, _R | _H |      _C | _S },
+   { "FLR", FP_OPCODE_FLR, INPUT_1V, OUTPUT_V, _R | _H | _X | _C | _S },
+   { "FRC", FP_OPCODE_FRC, INPUT_1V, OUTPUT_V, _R | _H | _X | _C | _S },
+   { "KIL", FP_OPCODE_KIL, INPUT_CC, OUTPUT_NONE, 0                   },
+   { "LG2", FP_OPCODE_LG2, INPUT_1S, OUTPUT_S, _R | _H |      _C | _S },
+   { "LIT", FP_OPCODE_LIT, INPUT_1V, OUTPUT_V, _R | _H |      _C | _S },
+   { "LRP", FP_OPCODE_LRP, INPUT_3V, OUTPUT_V, _R | _H | _X | _C | _S },
+   { "MAD", FP_OPCODE_MAD, INPUT_3V, OUTPUT_V, _R | _H | _X | _C | _S },
+   { "MAX", FP_OPCODE_MAX, INPUT_2V, OUTPUT_V, _R | _H | _X | _C | _S },
+   { "MIN", FP_OPCODE_MIN, INPUT_2V, OUTPUT_V, _R | _H | _X | _C | _S },
+   { "MOV", FP_OPCODE_MOV, INPUT_1V, OUTPUT_V, _R | _H | _X | _C | _S },
+   { "MUL", FP_OPCODE_MUL, INPUT_2V, OUTPUT_V, _R | _H | _X | _C | _S },
+   { "PK2H",  FP_OPCODE_PK2H,  INPUT_1V, OUTPUT_S, 0                  },
+   { "PK2US", FP_OPCODE_PK2US, INPUT_1V, OUTPUT_S, 0                  },
+   { "PK4B",  FP_OPCODE_PK4B,  INPUT_1V, OUTPUT_S, 0                  },
+   { "PK4UB", FP_OPCODE_PK4UB, INPUT_1V, OUTPUT_S, 0                  },
+   { "POW", FP_OPCODE_POW, INPUT_2S, OUTPUT_S, _R | _H |      _C | _S },
+   { "RCP", FP_OPCODE_RCP, INPUT_1S, OUTPUT_S, _R | _H |      _C | _S },
+   { "RFL", FP_OPCODE_RFL, INPUT_2V, OUTPUT_V, _R | _H |      _C | _S },
+   { "RSQ", FP_OPCODE_RSQ, INPUT_1S, OUTPUT_S, _R | _H |      _C | _S },
+   { "SEQ", FP_OPCODE_SEQ, INPUT_2V, OUTPUT_V, _R | _H | _X | _C | _S },
+   { "SFL", FP_OPCODE_SFL, INPUT_2V, OUTPUT_V, _R | _H | _X | _C | _S },
+   { "SGE", FP_OPCODE_SGE, INPUT_2V, OUTPUT_V, _R | _H | _X | _C | _S },
+   { "SGT", FP_OPCODE_SGT, INPUT_2V, OUTPUT_V, _R | _H | _X | _C | _S },
+   { "SIN", FP_OPCODE_SIN, INPUT_1S, OUTPUT_S, _R | _H |      _C | _S },
+   { "SLE", FP_OPCODE_SLE, INPUT_2V, OUTPUT_V, _R | _H | _X | _C | _S },
+   { "SLT", FP_OPCODE_SLT, INPUT_2V, OUTPUT_V, _R | _H | _X | _C | _S },
+   { "SNE", FP_OPCODE_SNE, INPUT_2V, OUTPUT_V, _R | _H | _X | _C | _S },
+   { "STR", FP_OPCODE_STR, INPUT_2V, OUTPUT_V, _R | _H | _X | _C | _S },
+   { "SUB", FP_OPCODE_SUB, INPUT_2V, OUTPUT_V, _R | _H | _X | _C | _S },
+   { "TEX", FP_OPCODE_TEX, INPUT_1V_T, OUTPUT_V,              _C | _S },
+   { "TXD", FP_OPCODE_TXD, INPUT_3V_T, OUTPUT_V,              _C | _S },
+   { "TXP", FP_OPCODE_TXP, INPUT_1V_T, OUTPUT_V,              _C | _S },
+   { "UP2H",  FP_OPCODE_UP2H,  INPUT_1S, OUTPUT_V,            _C | _S },
+   { "UP2US", FP_OPCODE_UP2US, INPUT_1S, OUTPUT_V,            _C | _S },
+   { "UP4B",  FP_OPCODE_UP4B,  INPUT_1S, OUTPUT_V,            _C | _S },
+   { "UP4UB", FP_OPCODE_UP4UB, INPUT_1S, OUTPUT_V,            _C | _S },
+   { "X2D", FP_OPCODE_X2D, INPUT_3V, OUTPUT_V, _R | _H |      _C | _S },
+   { NULL, (enum fp_opcode) -1, 0, 0, 0 }
+};
+
+
+/*
+ * Information needed or computed during parsing.
+ * Remember, we can't modify the target program object until we've
+ * _successfully_ parsed the program text.
+ */
+struct parse_state {
+   GLcontext *ctx;
+   const GLubyte *start;              /* start of program string */
+   const GLubyte *pos;                /* current position */
+   const GLubyte *curLine;
+   struct fragment_program *program;  /* current program */
+
+   struct program_parameter_list *parameters;
+
+   GLuint numInst;                    /* number of instructions parsed */
+   GLuint inputsRead;                 /* bitmask of input registers used */
+   GLuint outputsWritten;             /* bitmask of 1 << FRAG_OUTPUT_* bits */
+   GLuint texturesUsed[MAX_TEXTURE_IMAGE_UNITS];
+};
+
+
+
+/*
+ * Called whenever we find an error during parsing.
+ */
+static void
+record_error(struct parse_state *parseState, const char *msg, int lineNo)
+{
+#ifdef DEBUG
+   GLint line, column;
+   const GLubyte *lineStr;
+   lineStr = _mesa_find_line_column(parseState->start,
+                                    parseState->pos, &line, &column);
+   _mesa_debug(parseState->ctx,
+               "nvfragparse.c(%d): line %d, column %d:%s (%s)\n",
+               lineNo, line, column, (char *) lineStr, msg);
+   _mesa_free((void *) lineStr);
+#else
+   (void) lineNo;
+#endif
+
+   /* Check that no error was already recorded.  Only record the first one. */
+   if (parseState->ctx->Program.ErrorString[0] == 0) {
+      _mesa_set_program_error(parseState->ctx,
+                              parseState->pos - parseState->start,
+                              msg);
+   }
+}
+
+
+#define RETURN_ERROR                                                   \
+do {                                                                   \
+   record_error(parseState, "Unexpected end of input.", __LINE__);     \
+   return GL_FALSE;                                                    \
+} while(0)
+
+#define RETURN_ERROR1(msg)                                             \
+do {                                                                   \
+   record_error(parseState, msg, __LINE__);                            \
+   return GL_FALSE;                                                    \
+} while(0)
+
+#define RETURN_ERROR2(msg1, msg2)                                      \
+do {                                                                   \
+   char err[1000];                                                     \
+   _mesa_sprintf(err, "%s %s", msg1, msg2);                            \
+   record_error(parseState, err, __LINE__);                            \
+   return GL_FALSE;                                                    \
+} while(0)
+
+
+
+
+/*
+ * Search a list of instruction structures for a match.
+ */
+static struct instruction_pattern
+MatchInstruction(const GLubyte *token)
+{
+   const struct instruction_pattern *inst;
+   struct instruction_pattern result;
+
+   for (inst = Instructions; inst->name; inst++) {
+      if (_mesa_strncmp((const char *) token, inst->name, 3) == 0) {
+         /* matched! */
+         int i = 3;
+         result = *inst;
+         result.suffixes = 0;
+         /* look at suffix */
+         if (token[i] == 'R') {
+            result.suffixes |= _R;
+            i++;
+         }
+         else if (token[i] == 'H') {
+            result.suffixes |= _H;
+            i++;
+         }
+         else if (token[i] == 'X') {
+            result.suffixes |= _X;
+            i++;
+         }
+         if (token[i] == 'C') {
+            result.suffixes |= _C;
+            i++;
+         }
+         if (token[i] == '_' && token[i+1] == 'S' &&
+             token[i+2] == 'A' && token[i+3] == 'T') {
+            result.suffixes |= _S;
+         }
+         return result;
+      }
+   }
+   result.opcode = (enum fp_opcode) -1;
+   return result;
+}
+
+
+
+
+/**********************************************************************/
+
+
+static GLboolean IsLetter(GLubyte b)
+{
+   return (b >= 'a' && b <= 'z') ||
+          (b >= 'A' && b <= 'Z') ||
+          (b == '_') ||
+          (b == '$');
+}
+
+
+static GLboolean IsDigit(GLubyte b)
+{
+   return b >= '0' && b <= '9';
+}
+
+
+static GLboolean IsWhitespace(GLubyte b)
+{
+   return b == ' ' || b == '\t' || b == '\n' || b == '\r';
+}
+
+
+/**
+ * Starting at 'str' find the next token.  A token can be an integer,
+ * an identifier or punctuation symbol.
+ * \return <= 0 we found an error, else, return number of characters parsed.
+ */
+static GLint
+GetToken(struct parse_state *parseState, GLubyte *token)
+{
+   const GLubyte *str = parseState->pos;
+   GLint i = 0, j = 0;
+
+   token[0] = 0;
+
+   /* skip whitespace and comments */
+   while (str[i] && (IsWhitespace(str[i]) || str[i] == '#')) {
+      if (str[i] == '#') {
+         /* skip comment */
+         while (str[i] && (str[i] != '\n' && str[i] != '\r')) {
+            i++;
+         }
+         if (str[i] == '\n' || str[i] == '\r')
+            parseState->curLine = str + i + 1;
+      }
+      else {
+         /* skip whitespace */
+         if (str[i] == '\n' || str[i] == '\r')
+            parseState->curLine = str + i + 1;
+         i++;
+      }
+   }
+
+   if (str[i] == 0)
+      return -i;
+
+   /* try matching an integer */
+   while (str[i] && IsDigit(str[i])) {
+      token[j++] = str[i++];
+   }
+   if (j > 0 || !str[i]) {
+      token[j] = 0;
+      return i;
+   }
+
+   /* try matching an identifier */
+   if (IsLetter(str[i])) {
+      while (str[i] && (IsLetter(str[i]) || IsDigit(str[i]))) {
+         token[j++] = str[i++];
+      }
+      token[j] = 0;
+      return i;
+   }
+
+   /* punctuation character */
+   if (str[i]) {
+      token[0] = str[i++];
+      token[1] = 0;
+      return i;
+   }
+
+   /* end of input */
+   token[0] = 0;
+   return i;
+}
+
+
+/**
+ * Get next token from input stream and increment stream pointer past token.
+ */
+static GLboolean
+Parse_Token(struct parse_state *parseState, GLubyte *token)
+{
+   GLint i;
+   i = GetToken(parseState, token);
+   if (i <= 0) {
+      parseState->pos += (-i);
+      return GL_FALSE;
+   }
+   parseState->pos += i;
+   return GL_TRUE;
+}
+
+
+/**
+ * Get next token from input stream but don't increment stream pointer.
+ */
+static GLboolean
+Peek_Token(struct parse_state *parseState, GLubyte *token)
+{
+   GLint i, len;
+   i = GetToken(parseState, token);
+   if (i <= 0) {
+      parseState->pos += (-i);
+      return GL_FALSE;
+   }
+   len = _mesa_strlen((const char *) token);
+   parseState->pos += (i - len);
+   return GL_TRUE;
+}
+
+
+/**********************************************************************/
+
+static const char *InputRegisters[MAX_NV_FRAGMENT_PROGRAM_INPUTS + 1] = {
+   "WPOS", "COL0", "COL1", "FOGC",
+   "TEX0", "TEX1", "TEX2", "TEX3", "TEX4", "TEX5", "TEX6", "TEX7", NULL
+};
+
+static const char *OutputRegisters[MAX_NV_FRAGMENT_PROGRAM_OUTPUTS + 1] = {
+   "COLR", "COLH",
+   /* These are only allows for register combiners */
+   /*
+   "TEX0", "TEX1", "TEX2", "TEX3",
+   */
+   "DEPR", NULL
+};
+
+
+
+
+/**********************************************************************/
+
+/**
+ * Try to match 'pattern' as the next token after any whitespace/comments.
+ */
+static GLboolean
+Parse_String(struct parse_state *parseState, const char *pattern)
+{
+   const GLubyte *m;
+   GLint i;
+
+   /* skip whitespace and comments */
+   while (IsWhitespace(*parseState->pos) || *parseState->pos == '#') {
+      if (*parseState->pos == '#') {
+         while (*parseState->pos && (*parseState->pos != '\n' && *parseState->pos != '\r')) {
+            parseState->pos += 1;
+         }
+         if (*parseState->pos == '\n' || *parseState->pos == '\r')
+            parseState->curLine = parseState->pos + 1;
+      }
+      else {
+         /* skip whitespace */
+         if (*parseState->pos == '\n' || *parseState->pos == '\r')
+            parseState->curLine = parseState->pos + 1;
+         parseState->pos += 1;
+      }
+   }
+
+   /* Try to match the pattern */
+   m = parseState->pos;
+   for (i = 0; pattern[i]; i++) {
+      if (*m != (GLubyte) pattern[i])
+         return GL_FALSE;
+      m += 1;
+   }
+   parseState->pos = m;
+
+   return GL_TRUE; /* success */
+}
+
+
+static GLboolean
+Parse_Identifier(struct parse_state *parseState, GLubyte *ident)
+{
+   if (!Parse_Token(parseState, ident))
+      RETURN_ERROR;
+   if (IsLetter(ident[0]))
+      return GL_TRUE;
+   else
+      RETURN_ERROR1("Expected an identfier");
+}
+
+
+/**
+ * Parse a floating point constant, or a defined symbol name.
+ * [+/-]N[.N[eN]]
+ * Output:  number[0 .. 3] will get the value.
+ */
+static GLboolean
+Parse_ScalarConstant(struct parse_state *parseState, GLfloat *number)
+{
+   char *end = NULL;
+
+   *number = (GLfloat) _mesa_strtod((const char *) parseState->pos, &end);
+
+   if (end && end > (char *) parseState->pos) {
+      /* got a number */
+      parseState->pos = (GLubyte *) end;
+      number[1] = *number;
+      number[2] = *number;
+      number[3] = *number;
+      return GL_TRUE;
+   }
+   else {
+      /* should be an identifier */
+      GLubyte ident[100];
+      const GLfloat *constant;
+      if (!Parse_Identifier(parseState, ident))
+         RETURN_ERROR1("Expected an identifier");
+      constant = _mesa_lookup_parameter_value(parseState->parameters,
+                                              -1, (const char *) ident);
+      /* XXX Check that it's a constant and not a parameter */
+      if (!constant) {
+         RETURN_ERROR1("Undefined symbol");
+      }
+      else {
+         COPY_4V(number, constant);
+         return GL_TRUE;
+      }
+   }
+}
+
+
+
+/**
+ * Parse a vector constant, one of:
+ *   { float }
+ *   { float, float }
+ *   { float, float, float }
+ *   { float, float, float, float }
+ */
+static GLboolean
+Parse_VectorConstant(struct parse_state *parseState, GLfloat *vec)
+{
+   /* "{" was already consumed */
+
+   ASSIGN_4V(vec, 0.0, 0.0, 0.0, 1.0);
+
+   if (!Parse_ScalarConstant(parseState, vec+0))  /* X */
+      return GL_FALSE;
+
+   if (Parse_String(parseState, "}")) {
+      return GL_TRUE;
+   }
+
+   if (!Parse_String(parseState, ","))
+      RETURN_ERROR1("Expected comma in vector constant");
+
+   if (!Parse_ScalarConstant(parseState, vec+1))  /* Y */
+      return GL_FALSE;
+
+   if (Parse_String(parseState, "}")) {
+      return GL_TRUE;
+   }
+
+   if (!Parse_String(parseState, ","))
+      RETURN_ERROR1("Expected comma in vector constant");
+
+   if (!Parse_ScalarConstant(parseState, vec+2))  /* Z */
+      return GL_FALSE;
+
+   if (Parse_String(parseState, "}")) {
+      return GL_TRUE;
+   }
+
+   if (!Parse_String(parseState, ","))
+      RETURN_ERROR1("Expected comma in vector constant");
+
+   if (!Parse_ScalarConstant(parseState, vec+3))  /* W */
+      return GL_FALSE;
+
+   if (!Parse_String(parseState, "}"))
+      RETURN_ERROR1("Expected closing brace in vector constant");
+
+   return GL_TRUE;
+}
+
+
+/**
+ * Parse <number>, <varname> or {a, b, c, d}.
+ * Return number of values in the vector or scalar, or zero if parse error.
+ */
+static GLuint
+Parse_VectorOrScalarConstant(struct parse_state *parseState, GLfloat *vec)
+{
+   if (Parse_String(parseState, "{")) {
+      return Parse_VectorConstant(parseState, vec);
+   }
+   else {
+      GLboolean b = Parse_ScalarConstant(parseState, vec);
+      if (b) {
+         vec[1] = vec[2] = vec[3] = vec[0];
+      }
+      return b;
+   }
+}
+
+
+/**
+ * Parse a texture image source:
+ *    [TEX0 | TEX1 | .. | TEX15] , [1D | 2D | 3D | CUBE | RECT]
+ */
+static GLboolean
+Parse_TextureImageId(struct parse_state *parseState,
+                     GLubyte *texUnit, GLubyte *texTargetBit)
+{
+   GLubyte imageSrc[100];
+   GLint unit;
+
+   if (!Parse_Token(parseState, imageSrc))
+      RETURN_ERROR;
+   
+   if (imageSrc[0] != 'T' ||
+       imageSrc[1] != 'E' ||
+       imageSrc[2] != 'X') {
+      RETURN_ERROR1("Expected TEX# source");
+   }
+   unit = _mesa_atoi((const char *) imageSrc + 3);
+   if ((unit < 0 || unit > MAX_TEXTURE_IMAGE_UNITS) ||
+       (unit == 0 && (imageSrc[3] != '0' || imageSrc[4] != 0))) {
+      RETURN_ERROR1("Invalied TEX# source index");
+   }
+   *texUnit = unit;
+
+   if (!Parse_String(parseState, ","))
+      RETURN_ERROR1("Expected ,");
+
+   if (Parse_String(parseState, "1D")) {
+      *texTargetBit = TEXTURE_1D_BIT;
+   }
+   else if (Parse_String(parseState, "2D")) {
+      *texTargetBit = TEXTURE_2D_BIT;
+   }
+   else if (Parse_String(parseState, "3D")) {
+      *texTargetBit = TEXTURE_3D_BIT;
+   }
+   else if (Parse_String(parseState, "CUBE")) {
+      *texTargetBit = TEXTURE_CUBE_BIT;
+   }
+   else if (Parse_String(parseState, "RECT")) {
+      *texTargetBit = TEXTURE_RECT_BIT;
+   }
+   else {
+      RETURN_ERROR1("Invalid texture target token");
+   }
+
+   /* update record of referenced texture units */
+   parseState->texturesUsed[*texUnit] |= *texTargetBit;
+   if (_mesa_bitcount(parseState->texturesUsed[*texUnit]) > 1) {
+      RETURN_ERROR1("Only one texture target can be used per texture unit.");
+   }
+
+   return GL_TRUE;
+}
+
+
+/**
+ * Parse a scalar suffix like .x, .y, .z or .w or parse a swizzle suffix
+ * like .wxyz, .xxyy, etc and return the swizzle indexes.
+ */
+static GLboolean
+Parse_SwizzleSuffix(const GLubyte *token, GLuint swizzle[4])
+{
+   if (token[1] == 0) {
+      /* single letter swizzle (scalar) */
+      if (token[0] == 'x')
+         ASSIGN_4V(swizzle, 0, 0, 0, 0);
+      else if (token[0] == 'y')
+         ASSIGN_4V(swizzle, 1, 1, 1, 1);
+      else if (token[0] == 'z')
+         ASSIGN_4V(swizzle, 2, 2, 2, 2);
+      else if (token[0] == 'w')
+         ASSIGN_4V(swizzle, 3, 3, 3, 3);
+      else
+         return GL_FALSE;
+   }
+   else {
+      /* 4-component swizzle (vector) */
+      GLint k;
+      for (k = 0; token[k] && k < 4; k++) {
+         if (token[k] == 'x')
+            swizzle[k] = 0;
+         else if (token[k] == 'y')
+            swizzle[k] = 1;
+         else if (token[k] == 'z')
+            swizzle[k] = 2;
+         else if (token[k] == 'w')
+            swizzle[k] = 3;
+         else
+            return GL_FALSE;
+      }
+      if (k != 4)
+         return GL_FALSE;
+   }
+   return GL_TRUE;
+}
+
+
+static GLboolean
+Parse_CondCodeMask(struct parse_state *parseState,
+                   struct fp_dst_register *dstReg)
+{
+   if (Parse_String(parseState, "EQ"))
+      dstReg->CondMask = COND_EQ;
+   else if (Parse_String(parseState, "GE"))
+      dstReg->CondMask = COND_GE;
+   else if (Parse_String(parseState, "GT"))
+      dstReg->CondMask = COND_GT;
+   else if (Parse_String(parseState, "LE"))
+      dstReg->CondMask = COND_LE;
+   else if (Parse_String(parseState, "LT"))
+      dstReg->CondMask = COND_LT;
+   else if (Parse_String(parseState, "NE"))
+      dstReg->CondMask = COND_NE;
+   else if (Parse_String(parseState, "TR"))
+      dstReg->CondMask = COND_TR;
+   else if (Parse_String(parseState, "FL"))
+      dstReg->CondMask = COND_FL;
+   else
+      RETURN_ERROR1("Invalid condition code mask");
+
+   /* look for optional .xyzw swizzle */
+   if (Parse_String(parseState, ".")) {
+      GLubyte token[100];
+      if (!Parse_Token(parseState, token))  /* get xyzw suffix */
+         RETURN_ERROR;
+
+      if (!Parse_SwizzleSuffix(token, dstReg->CondSwizzle))
+         RETURN_ERROR1("Invalid swizzle suffix");
+   }
+
+   return GL_TRUE;
+}
+
+
+/**
+ * Parse a temporary register: Rnn or Hnn
+ */
+static GLboolean
+Parse_TempReg(struct parse_state *parseState, GLint *tempRegNum)
+{
+   GLubyte token[100];
+
+   /* Should be 'R##' or 'H##' */
+   if (!Parse_Token(parseState, token))
+      RETURN_ERROR;
+   if (token[0] != 'R' && token[0] != 'H')
+      RETURN_ERROR1("Expected R## or H##");
+
+   if (IsDigit(token[1])) {
+      GLint reg = _mesa_atoi((const char *) (token + 1));
+      if (token[0] == 'H')
+         reg += 32;
+      if (reg >= MAX_NV_FRAGMENT_PROGRAM_TEMPS)
+         RETURN_ERROR1("Invalid temporary register name");
+      *tempRegNum = reg;
+   }
+   else {
+      RETURN_ERROR1("Invalid temporary register name");
+   }
+
+   return GL_TRUE;
+}
+
+
+/**
+ * Parse a write-only dummy register: RC or HC.
+ */
+static GLboolean
+Parse_DummyReg(struct parse_state *parseState, GLint *regNum)
+{
+   if (Parse_String(parseState, "RC")) {
+       *regNum = 0;
+   }
+   else if (Parse_String(parseState, "HC")) {
+       *regNum = 1;
+   }
+   else {
+      RETURN_ERROR1("Invalid write-only register name");
+   }
+
+   return GL_TRUE;
+}
+
+
+/**
+ * Parse a program local parameter register "p[##]"
+ */
+static GLboolean
+Parse_ProgramParamReg(struct parse_state *parseState, GLint *regNum)
+{
+   GLubyte token[100];
+
+   if (!Parse_String(parseState, "p["))
+      RETURN_ERROR1("Expected p[");
+
+   if (!Parse_Token(parseState, token))
+      RETURN_ERROR;
+
+   if (IsDigit(token[0])) {
+      /* a numbered program parameter register */
+      GLint reg = _mesa_atoi((const char *) token);
+      if (reg >= MAX_NV_FRAGMENT_PROGRAM_PARAMS)
+         RETURN_ERROR1("Invalid constant program number");
+      *regNum = reg;
+   }
+   else {
+      RETURN_ERROR;
+   }
+
+   if (!Parse_String(parseState, "]"))
+      RETURN_ERROR1("Expected ]");
+
+   return GL_TRUE;
+}
+
+
+/**
+ * Parse f[name]  - fragment input register
+ */
+static GLboolean
+Parse_FragReg(struct parse_state *parseState, GLint *tempRegNum)
+{
+   GLubyte token[100];
+   GLint j;
+
+   /* Match 'f[' */
+   if (!Parse_String(parseState, "f["))
+      RETURN_ERROR1("Expected f[");
+
+   /* get <name> and look for match */
+   if (!Parse_Token(parseState, token)) {
+      RETURN_ERROR;
+   }
+   for (j = 0; InputRegisters[j]; j++) {
+      if (_mesa_strcmp((const char *) token, InputRegisters[j]) == 0) {
+         *tempRegNum = j;
+         parseState->inputsRead |= (1 << j);
+         break;
+      }
+   }
+   if (!InputRegisters[j]) {
+      /* unknown input register label */
+      RETURN_ERROR2("Invalid register name", token);
+   }
+
+   /* Match '[' */
+   if (!Parse_String(parseState, "]"))
+      RETURN_ERROR1("Expected ]");
+
+   return GL_TRUE;
+}
+
+
+static GLboolean
+Parse_OutputReg(struct parse_state *parseState, GLint *outputRegNum)
+{
+   GLubyte token[100];
+   GLint j;
+
+   /* Match "o[" */
+   if (!Parse_String(parseState, "o["))
+      RETURN_ERROR1("Expected o[");
+
+   /* Get output reg name */
+   if (!Parse_Token(parseState, token))
+      RETURN_ERROR;
+
+   /* try to match an output register name */
+   for (j = 0; OutputRegisters[j]; j++) {
+      if (_mesa_strcmp((const char *) token, OutputRegisters[j]) == 0) {
+         static GLuint bothColors = (1 << FRAG_OUTPUT_COLR) | (1 << FRAG_OUTPUT_COLH);
+         *outputRegNum = j;
+         parseState->outputsWritten |= (1 << j);
+         if ((parseState->outputsWritten & bothColors) == bothColors) {
+            RETURN_ERROR1("Illegal to write to both o[COLR] and o[COLH]");
+         }
+         break;
+      }
+   }
+   if (!OutputRegisters[j])
+      RETURN_ERROR1("Invalid output register name");
+
+   /* Match ']' */
+   if (!Parse_String(parseState, "]"))
+      RETURN_ERROR1("Expected ]");
+
+   return GL_TRUE;
+}
+
+
+static GLboolean
+Parse_MaskedDstReg(struct parse_state *parseState,
+                   struct fp_dst_register *dstReg)
+{
+   GLubyte token[100];
+
+   /* Dst reg can be R<n>, H<n>, o[n], RC or HC */
+   if (!Peek_Token(parseState, token))
+      RETURN_ERROR;
+
+   if (_mesa_strcmp((const char *) token, "RC") == 0 ||
+       _mesa_strcmp((const char *) token, "HC") == 0) {
+      /* a write-only register */
+      dstReg->File = PROGRAM_WRITE_ONLY;
+      if (!Parse_DummyReg(parseState, &dstReg->Index))
+         RETURN_ERROR;
+   }
+   else if (token[0] == 'R' || token[0] == 'H') {
+      /* a temporary register */
+      dstReg->File = PROGRAM_TEMPORARY;
+      if (!Parse_TempReg(parseState, &dstReg->Index))
+         RETURN_ERROR;
+   }
+   else if (token[0] == 'o') {
+      /* an output register */
+      dstReg->File = PROGRAM_OUTPUT;
+      if (!Parse_OutputReg(parseState, &dstReg->Index))
+         RETURN_ERROR;
+   }
+   else {
+      RETURN_ERROR1("Invalid destination register name");
+   }
+
+   /* Parse optional write mask */
+   if (Parse_String(parseState, ".")) {
+      /* got a mask */
+      GLint k = 0;
+
+      if (!Parse_Token(parseState, token))  /* get xyzw writemask */
+         RETURN_ERROR;
+
+      dstReg->WriteMask[0] = GL_FALSE;
+      dstReg->WriteMask[1] = GL_FALSE;
+      dstReg->WriteMask[2] = GL_FALSE;
+      dstReg->WriteMask[3] = GL_FALSE;
+
+      if (token[k] == 'x') {
+         dstReg->WriteMask[0] = GL_TRUE;
+         k++;
+      }
+      if (token[k] == 'y') {
+         dstReg->WriteMask[1] = GL_TRUE;
+         k++;
+      }
+      if (token[k] == 'z') {
+         dstReg->WriteMask[2] = GL_TRUE;
+         k++;
+      }
+      if (token[k] == 'w') {
+         dstReg->WriteMask[3] = GL_TRUE;
+         k++;
+      }
+      if (k == 0) {
+         RETURN_ERROR1("Invalid writemask character");
+      }
+
+   }
+   else {
+      dstReg->WriteMask[0] = GL_TRUE;
+      dstReg->WriteMask[1] = GL_TRUE;
+      dstReg->WriteMask[2] = GL_TRUE;
+      dstReg->WriteMask[3] = GL_TRUE;
+   }
+
+   /* optional condition code mask */
+   if (Parse_String(parseState, "(")) {
+      /* ("EQ" | "GE" | "GT" | "LE" | "LT" | "NE" | "TR" | "FL".x|y|z|w) */
+      /* ("EQ" | "GE" | "GT" | "LE" | "LT" | "NE" | "TR" | "FL".[xyzw]) */
+      if (!Parse_CondCodeMask(parseState, dstReg))
+         RETURN_ERROR;
+
+      if (!Parse_String(parseState, ")"))  /* consume ")" */
+         RETURN_ERROR1("Expected )");
+
+      return GL_TRUE;
+   }
+   else {
+      /* no cond code mask */
+      dstReg->CondMask = COND_TR;
+      dstReg->CondSwizzle[0] = 0;
+      dstReg->CondSwizzle[1] = 1;
+      dstReg->CondSwizzle[2] = 2;
+      dstReg->CondSwizzle[3] = 3;
+      return GL_TRUE;
+   }
+}
+
+
+/**
+ * Parse a vector source (register, constant, etc):
+ *   <vectorSrc>    ::= <absVectorSrc>
+ *                    | <baseVectorSrc>
+ *   <absVectorSrc> ::= <negate> "|" <baseVectorSrc> "|"
+ */
+static GLboolean
+Parse_VectorSrc(struct parse_state *parseState,
+                struct fp_src_register *srcReg)
+{
+   GLfloat sign = 1.0F;
+   GLubyte token[100];
+
+   /*
+    * First, take care of +/- and absolute value stuff.
+    */
+   if (Parse_String(parseState, "-"))
+      sign = -1.0F;
+   else if (Parse_String(parseState, "+"))
+      sign = +1.0F;
+
+   if (Parse_String(parseState, "|")) {
+      srcReg->Abs = GL_TRUE;
+      srcReg->NegateAbs = (sign < 0.0F) ? GL_TRUE : GL_FALSE;
+
+      if (Parse_String(parseState, "-"))
+         srcReg->NegateBase = GL_TRUE;
+      else if (Parse_String(parseState, "+"))
+         srcReg->NegateBase = GL_FALSE;
+      else
+         srcReg->NegateBase = GL_FALSE;
+   }
+   else {
+      srcReg->Abs = GL_FALSE;
+      srcReg->NegateAbs = GL_FALSE;
+      srcReg->NegateBase = (sign < 0.0F) ? GL_TRUE : GL_FALSE;
+   }
+
+   /* This should be the real src vector/register name */
+   if (!Peek_Token(parseState, token))
+      RETURN_ERROR;
+
+   /* Src reg can be Rn, Hn, f[n], p[n], a named parameter, a scalar
+    * literal or vector literal.
+    */
+   if (token[0] == 'R' || token[0] == 'H') {
+      srcReg->File = PROGRAM_TEMPORARY;
+      if (!Parse_TempReg(parseState, &srcReg->Index))
+         RETURN_ERROR;
+   }
+   else if (token[0] == 'f') {
+      /* XXX this might be an identier! */
+      srcReg->File = PROGRAM_INPUT;
+      if (!Parse_FragReg(parseState, &srcReg->Index))
+         RETURN_ERROR;
+   }
+   else if (token[0] == 'p') {
+      /* XXX this might be an identier! */
+      srcReg->File = PROGRAM_LOCAL_PARAM;
+      if (!Parse_ProgramParamReg(parseState, &srcReg->Index))
+         RETURN_ERROR;
+   }
+   else if (IsLetter(token[0])){
+      GLubyte ident[100];
+      GLint paramIndex;
+      if (!Parse_Identifier(parseState, ident))
+         RETURN_ERROR;
+      paramIndex = _mesa_lookup_parameter_index(parseState->parameters,
+                                                -1, (const char *) ident);
+      if (paramIndex < 0) {
+         RETURN_ERROR2("Undefined constant or parameter: ", ident);
+      }
+      srcReg->File = PROGRAM_NAMED_PARAM;
+      srcReg->Index = paramIndex;      
+   }
+   else if (IsDigit(token[0]) || token[0] == '-' || token[0] == '+' || token[0] == '.'){
+      /* literal scalar constant */
+      GLfloat values[4];
+      GLuint paramIndex;
+      if (!Parse_ScalarConstant(parseState, values))
+         RETURN_ERROR;
+      paramIndex = _mesa_add_unnamed_constant(parseState->parameters, values);
+      srcReg->File = PROGRAM_NAMED_PARAM;
+      srcReg->Index = paramIndex;
+   }
+   else if (token[0] == '{'){
+      /* literal vector constant */
+      GLfloat values[4];
+      GLuint paramIndex;
+      (void) Parse_String(parseState, "{");
+      if (!Parse_VectorConstant(parseState, values))
+         RETURN_ERROR;
+      paramIndex = _mesa_add_unnamed_constant(parseState->parameters, values);
+      srcReg->File = PROGRAM_NAMED_PARAM;
+      srcReg->Index = paramIndex;      
+   }
+   else {
+      RETURN_ERROR2("Invalid source register name", token);
+   }
+
+   /* init swizzle fields */
+   srcReg->Swizzle[0] = 0;
+   srcReg->Swizzle[1] = 1;
+   srcReg->Swizzle[2] = 2;
+   srcReg->Swizzle[3] = 3;
+
+   /* Look for optional swizzle suffix */
+   if (Parse_String(parseState, ".")) {
+      if (!Parse_Token(parseState, token))
+         RETURN_ERROR;
+
+      if (!Parse_SwizzleSuffix(token, srcReg->Swizzle))
+         RETURN_ERROR1("Invalid swizzle suffix");
+   }
+
+   /* Finish absolute value */
+   if (srcReg->Abs && !Parse_String(parseState, "|")) {
+      RETURN_ERROR1("Expected |");
+   }
+
+   return GL_TRUE;
+}
+
+
+static GLboolean
+Parse_ScalarSrcReg(struct parse_state *parseState,
+                   struct fp_src_register *srcReg)
+{
+   GLubyte token[100];
+   GLfloat sign = 1.0F;
+   GLboolean needSuffix = GL_TRUE;
+
+   /*
+    * First, take care of +/- and absolute value stuff.
+    */
+   if (Parse_String(parseState, "-"))
+      sign = -1.0F;
+   else if (Parse_String(parseState, "+"))
+      sign = +1.0F;
+
+   if (Parse_String(parseState, "|")) {
+      srcReg->Abs = GL_TRUE;
+      srcReg->NegateAbs = (sign < 0.0F) ? GL_TRUE : GL_FALSE;
+
+      if (Parse_String(parseState, "-"))
+         srcReg->NegateBase = GL_TRUE;
+      else if (Parse_String(parseState, "+"))
+         srcReg->NegateBase = GL_FALSE;
+      else
+         srcReg->NegateBase = GL_FALSE;
+   }
+   else {
+      srcReg->Abs = GL_FALSE;
+      srcReg->NegateAbs = GL_FALSE;
+      srcReg->NegateBase = (sign < 0.0F) ? GL_TRUE : GL_FALSE;
+   }
+
+   if (!Peek_Token(parseState, token))
+      RETURN_ERROR;
+
+   /* Src reg can be R<n>, H<n> or a named fragment attrib */
+   if (token[0] == 'R' || token[0] == 'H') {
+      srcReg->File = PROGRAM_TEMPORARY;
+      if (!Parse_TempReg(parseState, &srcReg->Index))
+         RETURN_ERROR;
+   }
+   else if (token[0] == 'f') {
+      srcReg->File = PROGRAM_INPUT;
+      if (!Parse_FragReg(parseState, &srcReg->Index))
+         RETURN_ERROR;
+   }
+   else if (token[0] == '{') {
+      /* vector literal */
+      GLfloat values[4];
+      GLuint paramIndex;
+      (void) Parse_String(parseState, "{");
+      if (!Parse_VectorConstant(parseState, values))
+         RETURN_ERROR;
+      paramIndex = _mesa_add_unnamed_constant(parseState->parameters, values);
+      srcReg->File = PROGRAM_NAMED_PARAM;
+      srcReg->Index = paramIndex;      
+   }
+   else if (IsDigit(token[0])) {
+      /* scalar literal */
+      GLfloat values[4];
+      GLuint paramIndex;
+      if (!Parse_ScalarConstant(parseState, values))
+         RETURN_ERROR;
+      paramIndex = _mesa_add_unnamed_constant(parseState->parameters, values);
+      srcReg->Index = paramIndex;      
+      srcReg->File = PROGRAM_NAMED_PARAM;
+      needSuffix = GL_FALSE;
+   }
+   else {
+      RETURN_ERROR2("Invalid scalar source argument", token);
+   }
+
+   if (needSuffix) {
+      /* parse .[xyzw] suffix */
+      if (!Parse_String(parseState, "."))
+         RETURN_ERROR1("Expected .");
+
+      if (!Parse_Token(parseState, token))
+         RETURN_ERROR;
+
+      if (token[0] == 'x' && token[1] == 0) {
+         srcReg->Swizzle[0] = 0;
+      }
+      else if (token[0] == 'y' && token[1] == 0) {
+         srcReg->Swizzle[0] = 1;
+      }
+      else if (token[0] == 'z' && token[1] == 0) {
+         srcReg->Swizzle[0] = 2;
+      }
+      else if (token[0] == 'w' && token[1] == 0) {
+         srcReg->Swizzle[0] = 3;
+      }
+      else {
+         RETURN_ERROR1("Invalid scalar source suffix");
+      }
+   }
+   else {
+      srcReg->Swizzle[0] = 0;
+   }
+   srcReg->Swizzle[1] = srcReg->Swizzle[2] = srcReg->Swizzle[3] = 0;
+
+   /* Finish absolute value */
+   if (srcReg->Abs && !Parse_String(parseState, "|")) {
+      RETURN_ERROR1("Expected |");
+   }
+
+   return GL_TRUE;
+}
+
+
+
+static GLboolean
+Parse_InstructionSequence(struct parse_state *parseState,
+                          struct fp_instruction program[])
+{
+   while (1) {
+      struct fp_instruction *inst = program + parseState->numInst;
+      struct instruction_pattern instMatch;
+      GLubyte token[100];
+
+      /* Initialize the instruction */
+      inst->SrcReg[0].File = (enum register_file) -1;
+      inst->SrcReg[1].File = (enum register_file) -1;
+      inst->SrcReg[2].File = (enum register_file) -1;
+      inst->DstReg.File = (enum register_file) -1;
+      inst->DstReg.CondSwizzle[0] = 0;
+      inst->DstReg.CondSwizzle[1] = 1;
+      inst->DstReg.CondSwizzle[2] = 2;
+      inst->DstReg.CondSwizzle[3] = 3;
+
+      /* special instructions */
+      if (Parse_String(parseState, "DEFINE")) {
+         GLubyte id[100];
+         GLfloat value[7];  /* yes, 7 to be safe */
+         if (!Parse_Identifier(parseState, id))
+            RETURN_ERROR;
+         /* XXX make sure id is not a reserved identifer, like R9 */
+         if (!Parse_String(parseState, "="))
+            RETURN_ERROR1("Expected =");
+         if (!Parse_VectorOrScalarConstant(parseState, value))
+            RETURN_ERROR;
+         if (!Parse_String(parseState, ";"))
+            RETURN_ERROR1("Expected ;");
+         if (_mesa_lookup_parameter_index(parseState->parameters,
+                                          -1, (const char *) id) >= 0) {
+            RETURN_ERROR2(id, "already defined");
+         }
+         _mesa_add_named_parameter(parseState->parameters,
+                                   (const char *) id, value);
+      }
+      else if (Parse_String(parseState, "DECLARE")) {
+         GLubyte id[100];
+         GLfloat value[7] = {0, 0, 0, 0, 0, 0, 0};  /* yes, to be safe */
+         if (!Parse_Identifier(parseState, id))
+            RETURN_ERROR;
+         /* XXX make sure id is not a reserved identifer, like R9 */
+         if (Parse_String(parseState, "=")) {
+            if (!Parse_VectorOrScalarConstant(parseState, value))
+               RETURN_ERROR;
+         }
+         if (!Parse_String(parseState, ";"))
+            RETURN_ERROR1("Expected ;");
+         if (_mesa_lookup_parameter_index(parseState->parameters,
+                                          -1, (const char *) id) >= 0) {
+            RETURN_ERROR2(id, "already declared");
+         }
+         _mesa_add_named_parameter(parseState->parameters,
+                                   (const char *) id, value);
+      }
+      else if (Parse_String(parseState, "END")) {
+         inst->Opcode = FP_OPCODE_END;
+         inst->StringPos = parseState->curLine - parseState->start;
+         assert(inst->StringPos >= 0);
+         parseState->numInst++;
+         if (Parse_Token(parseState, token)) {
+            RETURN_ERROR1("Code after END opcode.");
+         }
+         break;
+      }
+      else {
+         /* general/arithmetic instruction */
+
+         /* get token */
+         if (!Parse_Token(parseState, token)) {
+            RETURN_ERROR1("Missing END instruction.");
+         }
+
+         /* try to find matching instuction */
+         instMatch = MatchInstruction(token);
+         if (instMatch.opcode < 0) {
+            /* bad instruction name */
+            RETURN_ERROR2("Unexpected token: ", token);
+         }
+
+         inst->Opcode = instMatch.opcode;
+         inst->Precision = instMatch.suffixes & (_R | _H | _X);
+         inst->Saturate = (instMatch.suffixes & (_S)) ? GL_TRUE : GL_FALSE;
+         inst->UpdateCondRegister = (instMatch.suffixes & (_C)) ? GL_TRUE : GL_FALSE;
+         inst->StringPos = parseState->curLine - parseState->start;
+         assert(inst->StringPos >= 0);
+
+         /*
+          * parse the input and output operands
+          */
+         if (instMatch.outputs == OUTPUT_S || instMatch.outputs == OUTPUT_V) {
+            if (!Parse_MaskedDstReg(parseState, &inst->DstReg))
+               RETURN_ERROR;
+            if (!Parse_String(parseState, ","))
+               RETURN_ERROR1("Expected ,");
+         }
+         else if (instMatch.outputs == OUTPUT_NONE) {
+            ASSERT(instMatch.opcode == FP_OPCODE_KIL);
+            /* This is a little weird, the cond code info is in the dest register */
+            if (!Parse_CondCodeMask(parseState, &inst->DstReg))
+               RETURN_ERROR;
+         }
+
+         if (instMatch.inputs == INPUT_1V) {
+            if (!Parse_VectorSrc(parseState, &inst->SrcReg[0]))
+               RETURN_ERROR;
+         }
+         else if (instMatch.inputs == INPUT_2V) {
+            if (!Parse_VectorSrc(parseState, &inst->SrcReg[0]))
+               RETURN_ERROR;
+            if (!Parse_String(parseState, ","))
+               RETURN_ERROR1("Expected ,");
+            if (!Parse_VectorSrc(parseState, &inst->SrcReg[1]))
+               RETURN_ERROR;
+         }
+         else if (instMatch.inputs == INPUT_3V) {
+            if (!Parse_VectorSrc(parseState, &inst->SrcReg[0]))
+               RETURN_ERROR;
+            if (!Parse_String(parseState, ","))
+               RETURN_ERROR1("Expected ,");
+            if (!Parse_VectorSrc(parseState, &inst->SrcReg[1]))
+               RETURN_ERROR;
+            if (!Parse_String(parseState, ","))
+               RETURN_ERROR1("Expected ,");
+            if (!Parse_VectorSrc(parseState, &inst->SrcReg[2]))
+               RETURN_ERROR;
+         }
+         else if (instMatch.inputs == INPUT_1S) {
+            if (!Parse_ScalarSrcReg(parseState, &inst->SrcReg[0]))
+               RETURN_ERROR;
+         }
+         else if (instMatch.inputs == INPUT_2S) {
+            if (!Parse_ScalarSrcReg(parseState, &inst->SrcReg[0]))
+               RETURN_ERROR;
+            if (!Parse_String(parseState, ","))
+               RETURN_ERROR1("Expected ,");
+            if (!Parse_ScalarSrcReg(parseState, &inst->SrcReg[1]))
+               RETURN_ERROR;
+         }
+         else if (instMatch.inputs == INPUT_CC) {
+            /* XXX to-do */
+         }
+         else if (instMatch.inputs == INPUT_1V_T) {
+            if (!Parse_VectorSrc(parseState, &inst->SrcReg[0]))
+               RETURN_ERROR;
+            if (!Parse_String(parseState, ","))
+               RETURN_ERROR1("Expected ,");
+            if (!Parse_TextureImageId(parseState, &inst->TexSrcUnit,
+                                      &inst->TexSrcBit))
+               RETURN_ERROR;
+         }
+         else if (instMatch.inputs == INPUT_3V_T) {
+            if (!Parse_VectorSrc(parseState, &inst->SrcReg[0]))
+               RETURN_ERROR;
+            if (!Parse_String(parseState, ","))
+               RETURN_ERROR1("Expected ,");
+            if (!Parse_VectorSrc(parseState, &inst->SrcReg[1]))
+               RETURN_ERROR;
+            if (!Parse_String(parseState, ","))
+               RETURN_ERROR1("Expected ,");
+            if (!Parse_VectorSrc(parseState, &inst->SrcReg[2]))
+               RETURN_ERROR;
+            if (!Parse_String(parseState, ","))
+               RETURN_ERROR1("Expected ,");
+            if (!Parse_TextureImageId(parseState, &inst->TexSrcUnit,
+                                      &inst->TexSrcBit))
+               RETURN_ERROR;
+         }
+
+         /* end of statement semicolon */
+         if (!Parse_String(parseState, ";"))
+            RETURN_ERROR1("Expected ;");
+
+         parseState->numInst++;
+
+         if (parseState->numInst >= MAX_NV_FRAGMENT_PROGRAM_INSTRUCTIONS)
+            RETURN_ERROR1("Program too long");
+      }
+   }
+   return GL_TRUE;
+}
+
+
+
+/**
+ * Parse/compile the 'str' returning the compiled 'program'.
+ * ctx->Program.ErrorPos will be -1 if successful.  Otherwise, ErrorPos
+ * indicates the position of the error in 'str'.
+ */
+void
+_mesa_parse_nv_fragment_program(GLcontext *ctx, GLenum dstTarget,
+                                const GLubyte *str, GLsizei len,
+                                struct fragment_program *program)
+{
+   struct parse_state parseState;
+   struct fp_instruction instBuffer[MAX_NV_FRAGMENT_PROGRAM_INSTRUCTIONS];
+   struct fp_instruction *newInst;
+   GLenum target;
+   GLubyte *programString;
+
+   /* Make a null-terminated copy of the program string */
+   programString = (GLubyte *) MALLOC(len + 1);
+   if (!programString) {
+      _mesa_error(ctx, GL_OUT_OF_MEMORY, "glLoadProgramNV");
+      return;
+   }
+   MEMCPY(programString, str, len);
+   programString[len] = 0;
+
+   /* Get ready to parse */
+   _mesa_bzero(&parseState, sizeof(struct parse_state));
+   parseState.ctx = ctx;
+   parseState.start = programString;
+   parseState.program = program;
+   parseState.numInst = 0;
+   parseState.curLine = programString;
+   parseState.parameters = _mesa_new_parameter_list();
+
+   /* Reset error state */
+   _mesa_set_program_error(ctx, -1, NULL);
+
+   /* check the program header */
+   if (_mesa_strncmp((const char *) programString, "!!FP1.0", 7) == 0) {
+      target = GL_FRAGMENT_PROGRAM_NV;
+      parseState.pos = programString + 7;
+   }
+   else if (_mesa_strncmp((const char *) programString, "!!FCP1.0", 8) == 0) {
+      /* fragment / register combiner program - not supported */
+      _mesa_set_program_error(ctx, 0, "Invalid fragment program header");
+      _mesa_error(ctx, GL_INVALID_OPERATION, "glLoadProgramNV(bad header)");
+      return;
+   }
+   else {
+      /* invalid header */
+      _mesa_set_program_error(ctx, 0, "Invalid fragment program header");
+      _mesa_error(ctx, GL_INVALID_OPERATION, "glLoadProgramNV(bad header)");
+      return;
+   }
+
+   /* make sure target and header match */
+   if (target != dstTarget) {
+      _mesa_error(ctx, GL_INVALID_OPERATION,
+                  "glLoadProgramNV(target mismatch 0x%x != 0x%x)",
+                  target, dstTarget);
+      return;
+   }
+
+   if (Parse_InstructionSequence(&parseState, instBuffer)) {
+      GLuint u;
+      /* successful parse! */
+
+      if (parseState.outputsWritten == 0) {
+         /* must write at least one output! */
+         _mesa_error(ctx, GL_INVALID_OPERATION,
+                     "Invalid fragment program - no outputs written.");
+         return;
+      }
+
+      /* copy the compiled instructions */
+      assert(parseState.numInst <= MAX_NV_FRAGMENT_PROGRAM_INSTRUCTIONS);
+      newInst = (struct fp_instruction *)
+         MALLOC(parseState.numInst * sizeof(struct fp_instruction));
+      if (!newInst) {
+         _mesa_error(ctx, GL_OUT_OF_MEMORY, "glLoadProgramNV");
+         return;  /* out of memory */
+      }
+      MEMCPY(newInst, instBuffer,
+             parseState.numInst * sizeof(struct fp_instruction));
+
+      /* install the program */
+      program->Base.Target = target;
+      if (program->Base.String) {
+         FREE(program->Base.String);
+      }
+      program->Base.String = programString;
+      program->Base.Format = GL_PROGRAM_FORMAT_ASCII_ARB;
+      if (program->Instructions) {
+         FREE(program->Instructions);
+      }
+      program->Instructions = newInst;
+      program->InputsRead = parseState.inputsRead;
+      program->OutputsWritten = parseState.outputsWritten;
+      for (u = 0; u < ctx->Const.MaxTextureImageUnits; u++)
+         program->TexturesUsed[u] = parseState.texturesUsed[u];
+
+      /* save program parameters */
+      program->Parameters = parseState.parameters;
+
+      /* allocate registers for declared program parameters */
+#if 00
+      _mesa_assign_program_registers(&(program->SymbolTable));
+#endif
+
+#ifdef DEBUG
+      _mesa_printf("--- glLoadProgramNV(%d) result ---\n", program->Base.Id);
+      _mesa_print_nv_fragment_program(program);
+      _mesa_printf("----------------------------------\n");
+#endif
+   }
+   else {
+      /* Error! */
+      _mesa_error(ctx, GL_INVALID_OPERATION, "glLoadProgramNV");
+      /* NOTE: _mesa_set_program_error would have been called already */
+   }
+}
+
+
+static void
+PrintSrcReg(const struct fragment_program *program,
+            const struct fp_src_register *src)
+{
+   static const char comps[5] = "xyzw";
+
+   if (src->NegateAbs) {
+      _mesa_printf("-");
+   }
+   if (src->Abs) {
+      _mesa_printf("|");
+   }
+   if (src->NegateBase) {
+      _mesa_printf("-");
+   }
+   if (src->File == PROGRAM_NAMED_PARAM) {
+      if (program->Parameters->Parameters[src->Index].Type == CONSTANT) {
+         printf("{%g, %g, %g, %g}",
+                program->Parameters->Parameters[src->Index].Values[0],
+                program->Parameters->Parameters[src->Index].Values[1],
+                program->Parameters->Parameters[src->Index].Values[2],
+                program->Parameters->Parameters[src->Index].Values[3]);
+      }
+      else {
+         ASSERT(program->Parameters->Parameters[src->Index].Type
+                == NAMED_PARAMETER);
+         printf("%s", program->Parameters->Parameters[src->Index].Name);
+      }
+   }
+   else if (src->File == PROGRAM_OUTPUT) {
+      _mesa_printf("o[%s]", OutputRegisters[src->Index]);
+   }
+   else if (src->File == PROGRAM_INPUT) {
+      _mesa_printf("f[%s]", InputRegisters[src->Index]);
+   }
+   else if (src->File == PROGRAM_LOCAL_PARAM) {
+      _mesa_printf("p[%d]", src->Index);
+   }
+   else if (src->File == PROGRAM_TEMPORARY) {
+      if (src->Index >= 32)
+         _mesa_printf("H%d", src->Index);
+      else
+         _mesa_printf("R%d", src->Index);
+   }
+   else if (src->File == PROGRAM_WRITE_ONLY) {
+      _mesa_printf("%cC", "HR"[src->Index]);
+   }
+   else {
+      _mesa_problem(NULL, "Invalid fragment register %d", src->Index);
+      return;
+   }
+   if (src->Swizzle[0] == src->Swizzle[1] &&
+       src->Swizzle[0] == src->Swizzle[2] &&
+       src->Swizzle[0] == src->Swizzle[3]) {
+      _mesa_printf(".%c", comps[src->Swizzle[0]]);
+   }
+   else if (src->Swizzle[0] != 0 ||
+            src->Swizzle[1] != 1 ||
+            src->Swizzle[2] != 2 ||
+            src->Swizzle[3] != 3) {
+      _mesa_printf(".%c%c%c%c",
+                   comps[src->Swizzle[0]],
+                   comps[src->Swizzle[1]],
+                   comps[src->Swizzle[2]],
+                   comps[src->Swizzle[3]]);
+   }
+   if (src->Abs) {
+      _mesa_printf("|");
+   }
+}
+
+static void
+PrintTextureSrc(const struct fp_instruction *inst)
+{
+   _mesa_printf("TEX%d, ", inst->TexSrcUnit);
+   switch (inst->TexSrcBit) {
+   case TEXTURE_1D_BIT:
+      _mesa_printf("1D");
+      break;
+   case TEXTURE_2D_BIT:
+      _mesa_printf("2D");
+      break;
+   case TEXTURE_3D_BIT:
+      _mesa_printf("3D");
+      break;
+   case TEXTURE_RECT_BIT:
+      _mesa_printf("RECT");
+      break;
+   case TEXTURE_CUBE_BIT:
+      _mesa_printf("CUBE");
+      break;
+   default:
+      _mesa_problem(NULL, "Invalid textue target in PrintTextureSrc");
+   }
+}
+
+static void
+PrintCondCode(const struct fp_dst_register *dst)
+{
+   static const char *comps = "xyzw";
+   static const char *ccString[] = {
+      "??", "GT", "EQ", "LT", "UN", "GE", "LE", "NE", "TR", "FL", "??"
+   };
+
+   _mesa_printf("%s", ccString[dst->CondMask]);
+   if (dst->CondSwizzle[0] == dst->CondSwizzle[1] &&
+       dst->CondSwizzle[0] == dst->CondSwizzle[2] &&
+       dst->CondSwizzle[0] == dst->CondSwizzle[3]) {
+      _mesa_printf(".%c", comps[dst->CondSwizzle[0]]);
+   }
+   else if (dst->CondSwizzle[0] != 0 ||
+            dst->CondSwizzle[1] != 1 ||
+            dst->CondSwizzle[2] != 2 ||
+            dst->CondSwizzle[3] != 3) {
+      _mesa_printf(".%c%c%c%c",
+                   comps[dst->CondSwizzle[0]],
+                   comps[dst->CondSwizzle[1]],
+                   comps[dst->CondSwizzle[2]],
+                   comps[dst->CondSwizzle[3]]);
+   }
+}
+
+
+static void
+PrintDstReg(const struct fp_dst_register *dst)
+{
+   GLint w = dst->WriteMask[0] + dst->WriteMask[1]
+           + dst->WriteMask[2] + dst->WriteMask[3];
+
+   if (dst->File == PROGRAM_OUTPUT) {
+      _mesa_printf("o[%s]", OutputRegisters[dst->Index]);
+   }
+   else if (dst->File == PROGRAM_TEMPORARY) {
+      if (dst->Index >= 32)
+         _mesa_printf("H%d", dst->Index);
+      else
+         _mesa_printf("R%d", dst->Index);
+   }
+   else if (dst->File == PROGRAM_LOCAL_PARAM) {
+      _mesa_printf("p[%d]", dst->Index);
+   }
+   else if (dst->File == PROGRAM_WRITE_ONLY) {
+      _mesa_printf("%cC", "HR"[dst->Index]);
+   }
+   else {
+      _mesa_printf("???");
+   }
+
+   if (w != 0 && w != 4) {
+      _mesa_printf(".");
+      if (dst->WriteMask[0])
+         _mesa_printf("x");
+      if (dst->WriteMask[1])
+         _mesa_printf("y");
+      if (dst->WriteMask[2])
+         _mesa_printf("z");
+      if (dst->WriteMask[3])
+         _mesa_printf("w");
+   }
+
+   if (dst->CondMask != COND_TR ||
+       dst->CondSwizzle[0] != 0 ||
+       dst->CondSwizzle[1] != 1 ||
+       dst->CondSwizzle[2] != 2 ||
+       dst->CondSwizzle[3] != 3) {
+      _mesa_printf(" (");
+      PrintCondCode(dst);
+      _mesa_printf(")");
+   }
+}
+
+
+/**
+ * Print (unparse) the given vertex program.  Just for debugging.
+ */
+void
+_mesa_print_nv_fragment_program(const struct fragment_program *program)
+{
+   const struct fp_instruction *inst;
+
+   for (inst = program->Instructions; inst->Opcode != FP_OPCODE_END; inst++) {
+      int i;
+      for (i = 0; Instructions[i].name; i++) {
+         if (inst->Opcode == Instructions[i].opcode) {
+            /* print instruction name */
+            _mesa_printf("%s", Instructions[i].name);
+            if (inst->Precision == FLOAT16)
+               _mesa_printf("H");
+            else if (inst->Precision == FIXED12)
+               _mesa_printf("X");
+            if (inst->UpdateCondRegister)
+               _mesa_printf("C");
+            if (inst->Saturate)
+               _mesa_printf("_SAT");
+            _mesa_printf(" ");
+
+            if (Instructions[i].inputs == INPUT_CC) {
+               PrintCondCode(&inst->DstReg);
+            }
+            else if (Instructions[i].outputs == OUTPUT_V ||
+                     Instructions[i].outputs == OUTPUT_S) {
+               /* print dest register */
+               PrintDstReg(&inst->DstReg);
+               _mesa_printf(", ");
+            }
+
+            /* print source register(s) */
+            if (Instructions[i].inputs == INPUT_1V ||
+                Instructions[i].inputs == INPUT_1S) {
+               PrintSrcReg(program, &inst->SrcReg[0]);
+            }
+            else if (Instructions[i].inputs == INPUT_2V ||
+                     Instructions[i].inputs == INPUT_2S) {
+               PrintSrcReg(program, &inst->SrcReg[0]);
+               _mesa_printf(", ");
+               PrintSrcReg(program, &inst->SrcReg[1]);
+            }
+            else if (Instructions[i].inputs == INPUT_3V) {
+               PrintSrcReg(program, &inst->SrcReg[0]);
+               _mesa_printf(", ");
+               PrintSrcReg(program, &inst->SrcReg[1]);
+               _mesa_printf(", ");
+               PrintSrcReg(program, &inst->SrcReg[2]);
+            }
+            else if (Instructions[i].inputs == INPUT_1V_T) {
+               PrintSrcReg(program, &inst->SrcReg[0]);
+               _mesa_printf(", ");
+               PrintTextureSrc(inst);
+            }
+            else if (Instructions[i].inputs == INPUT_3V_T) {
+               PrintSrcReg(program, &inst->SrcReg[0]);
+               _mesa_printf(", ");
+               PrintSrcReg(program, &inst->SrcReg[1]);
+               _mesa_printf(", ");
+               PrintSrcReg(program, &inst->SrcReg[2]);
+               _mesa_printf(", ");
+               PrintTextureSrc(inst);
+            }
+            _mesa_printf(";\n");
+            break;
+         }
+      }
+      if (!Instructions[i].name) {
+         _mesa_printf("Invalid opcode %d\n", inst->Opcode);
+      }
+   }
+   _mesa_printf("END\n");
+}
+
+
+const char *
+_mesa_nv_fragment_input_register_name(GLuint i)
+{
+   ASSERT(i < MAX_NV_FRAGMENT_PROGRAM_INPUTS);
+   return InputRegisters[i];
+}
+
+
+const char *
+_mesa_nv_fragment_output_register_name(GLuint i)
+{
+   ASSERT(i < MAX_NV_FRAGMENT_PROGRAM_OUTPUTS);
+   return OutputRegisters[i];
+}
diff --git a/src/mesa/shader/nvfragparse.h b/src/mesa/shader/nvfragparse.h
new file mode 100644 (file)
index 0000000..849a7fb
--- /dev/null
@@ -0,0 +1,52 @@
+
+/*
+ * Mesa 3-D graphics library
+ * Version:  5.1
+ *
+ * Copyright (C) 1999-2002  Brian Paul   All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * BRIAN PAUL BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors:
+ *    Brian Paul
+ */
+
+
+#ifndef NVFRAGPARSE_H
+#define NVFRAGPARSE_H
+
+
+extern void
+_mesa_parse_nv_fragment_program(GLcontext *ctx, GLenum target,
+                                const GLubyte *str, GLsizei len,
+                                struct fragment_program *program);
+
+
+extern void
+_mesa_print_nv_fragment_program(const struct fragment_program *program);
+
+
+extern const char *
+_mesa_nv_fragment_input_register_name(GLuint i);
+
+
+extern const char *
+_mesa_nv_fragment_output_register_name(GLuint i);
+
+
+#endif
diff --git a/src/mesa/shader/nvfragprog.h b/src/mesa/shader/nvfragprog.h
new file mode 100644 (file)
index 0000000..fa817d4
--- /dev/null
@@ -0,0 +1,160 @@
+/*
+ * Mesa 3-D graphics library
+ * Version:  5.1
+ *
+ * Copyright (C) 1999-2003  Brian Paul   All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * BRIAN PAUL BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+
+/* Private fragment program types and constants only used by files
+ * related to fragment programs.
+ *
+ * XXX TO-DO: Rename this file "fragprog.h" since it's not NV-specific.
+ */
+
+
+#ifndef NVFRAGPROG_H
+#define NVFRAGPROG_H
+
+#include "config.h"
+
+
+/* output registers */
+#define FRAG_OUTPUT_COLR  0
+#define FRAG_OUTPUT_COLH  1
+#define FRAG_OUTPUT_DEPR  2
+
+
+/* condition codes */
+#define COND_GT  1  /* greater than zero */
+#define COND_EQ  2  /* equal to zero */
+#define COND_LT  3  /* less than zero */
+#define COND_UN  4  /* unordered (NaN) */
+#define COND_GE  5  /* greater then or equal to zero */
+#define COND_LE  6  /* less then or equal to zero */
+#define COND_NE  7  /* not equal to zero */
+#define COND_TR  8  /* always true */
+#define COND_FL  9  /* always false */
+
+
+/* instruction precision */
+#define FLOAT32  0x1
+#define FLOAT16  0x2
+#define FIXED12  0x4
+
+
+/* Fragment program instruction opcodes */
+enum fp_opcode {
+   FP_OPCODE_ABS = 1000,     /* ARB_f_p only */
+   FP_OPCODE_ADD,
+   FP_OPCODE_CMP,            /* ARB_f_p only */
+   FP_OPCODE_COS,
+   FP_OPCODE_DDX,            /* NV_f_p only */
+   FP_OPCODE_DDY,            /* NV_f_p only */
+   FP_OPCODE_DP3,
+   FP_OPCODE_DP4,
+   FP_OPCODE_DPH,            /* ARB_f_p only */
+   FP_OPCODE_DST,
+   FP_OPCODE_EX2,
+   FP_OPCODE_FLR,
+   FP_OPCODE_FRC,
+   FP_OPCODE_KIL,
+   FP_OPCODE_LG2,
+   FP_OPCODE_LIT,
+   FP_OPCODE_LRP,
+   FP_OPCODE_MAD,
+   FP_OPCODE_MAX,
+   FP_OPCODE_MIN,
+   FP_OPCODE_MOV,
+   FP_OPCODE_MUL,
+   FP_OPCODE_PK2H,           /* NV_f_p only */
+   FP_OPCODE_PK2US,          /* NV_f_p only */
+   FP_OPCODE_PK4B,           /* NV_f_p only */
+   FP_OPCODE_PK4UB,          /* NV_f_p only */
+   FP_OPCODE_POW,
+   FP_OPCODE_RCP,
+   FP_OPCODE_RFL,            /* NV_f_p only */
+   FP_OPCODE_RSQ,
+   FP_OPCODE_SCS,            /* ARB_f_p only */
+   FP_OPCODE_SEQ,            /* NV_f_p only */
+   FP_OPCODE_SFL,            /* NV_f_p only */
+   FP_OPCODE_SGE,            /* NV_f_p only */
+   FP_OPCODE_SGT,            /* NV_f_p only */
+   FP_OPCODE_SIN,
+   FP_OPCODE_SLE,            /* NV_f_p only */
+   FP_OPCODE_SLT,
+   FP_OPCODE_SNE,            /* NV_f_p only */
+   FP_OPCODE_STR,            /* NV_f_p only */
+   FP_OPCODE_SUB,
+   FP_OPCODE_SWZ,            /* ARB_f_p only */
+   FP_OPCODE_TEX,
+   FP_OPCODE_TXB,            /* ARB_f_p only */
+   FP_OPCODE_TXD,            /* NV_f_p only */
+   FP_OPCODE_TXP,
+   FP_OPCODE_UP2H,           /* NV_f_p only */
+   FP_OPCODE_UP2US,          /* NV_f_p only */
+   FP_OPCODE_UP4B,           /* NV_f_p only */
+   FP_OPCODE_UP4UB,          /* NV_f_p only */
+   FP_OPCODE_X2D,            /* XPD in ARB_f_p */
+   FP_OPCODE_END /* private opcode */
+};
+
+
+/* Instruction source register */
+struct fp_src_register
+{
+   enum register_file File;
+   GLint Index;
+   GLuint Swizzle[4];
+   GLboolean NegateBase; /* negate before absolute value? */
+   GLboolean Abs;        /* take absolute value? */
+   GLboolean NegateAbs;  /* negate after absolute value? */
+};
+
+
+/* Instruction destination register */
+struct fp_dst_register
+{
+   enum register_file File;
+   GLint Index;
+   GLboolean WriteMask[4];
+   GLuint CondMask;
+   GLuint CondSwizzle[4];
+};
+
+
+/* Fragment program instruction */
+struct fp_instruction
+{
+   enum fp_opcode Opcode;
+   struct fp_src_register SrcReg[3];
+   struct fp_dst_register DstReg;
+   GLboolean Saturate;
+   GLboolean UpdateCondRegister;
+   GLubyte Precision;    /* FLOAT32, FLOAT16 or FIXED12 */
+   GLubyte TexSrcUnit;   /* texture unit for TEX, TXD, TXP instructions */
+   GLubyte TexSrcBit;    /* TEXTURE_1D,2D,3D,CUBE,RECT_BIT source target */
+#if FEATURE_MESA_program_debug
+   GLint StringPos;
+#endif
+};
+
+
+#endif
diff --git a/src/mesa/shader/nvprogram.c b/src/mesa/shader/nvprogram.c
new file mode 100644 (file)
index 0000000..d32cb7f
--- /dev/null
@@ -0,0 +1,871 @@
+/*
+ * Mesa 3-D graphics library
+ * Version:  6.0
+ *
+ * Copyright (C) 1999-2004  Brian Paul   All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * BRIAN PAUL BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+/**
+ * \file nvprogram.c
+ * NVIDIA vertex/fragment program state management functions.
+ * \author Brian Paul
+ */
+
+/*
+ * Regarding GL_NV_fragment/vertex_program, GL_NV_vertex_program1_1, etc:
+ *
+ * Portions of this software may use or implement intellectual
+ * property owned and licensed by NVIDIA Corporation. NVIDIA disclaims
+ * any and all warranties with respect to such intellectual property,
+ * including any use thereof or modifications thereto.
+ */
+
+#include "glheader.h"
+#include "context.h"
+#include "hash.h"
+#include "imports.h"
+#include "macros.h"
+#include "mtypes.h"
+#include "nvfragparse.h"
+#include "nvfragprog.h"
+#include "nvvertexec.h"
+#include "nvvertparse.h"
+#include "nvvertprog.h"
+#include "nvprogram.h"
+#include "program.h"
+
+
+
+/**
+ * Execute a vertex state program.
+ * \note Called from the GL API dispatcher.
+ */
+void GLAPIENTRY
+_mesa_ExecuteProgramNV(GLenum target, GLuint id, const GLfloat *params)
+{
+   struct vertex_program *vprog;
+   GET_CURRENT_CONTEXT(ctx);
+   ASSERT_OUTSIDE_BEGIN_END(ctx);
+
+   if (target != GL_VERTEX_STATE_PROGRAM_NV) {
+      _mesa_error(ctx, GL_INVALID_ENUM, "glExecuteProgramNV");
+      return;
+   }
+
+   FLUSH_VERTICES(ctx, _NEW_PROGRAM);
+
+   vprog = (struct vertex_program *)
+      _mesa_HashLookup(ctx->Shared->Programs, id);
+
+   if (!vprog || vprog->Base.Target != GL_VERTEX_STATE_PROGRAM_NV) {
+      _mesa_error(ctx, GL_INVALID_OPERATION, "glExecuteProgramNV");
+      return;
+   }
+   
+   _mesa_init_vp_registers(ctx);
+   _mesa_init_tracked_matrices(ctx);
+   COPY_4V(ctx->VertexProgram.Inputs[VERT_ATTRIB_POS], params);
+   _mesa_exec_vertex_program(ctx, vprog);
+}
+
+
+/**
+ * Determine if a set of programs is resident in hardware.
+ * \note Not compiled into display lists.
+ * \note Called from the GL API dispatcher.
+ */
+GLboolean GLAPIENTRY _mesa_AreProgramsResidentNV(GLsizei n, const GLuint *ids,
+                                      GLboolean *residences)
+{
+   GLint i, j;
+   GLboolean allResident = GL_TRUE;
+   GET_CURRENT_CONTEXT(ctx);
+   ASSERT_OUTSIDE_BEGIN_END_WITH_RETVAL(ctx, GL_FALSE);
+
+   if (n < 0) {
+      _mesa_error(ctx, GL_INVALID_VALUE, "glAreProgramsResidentNV(n)");
+      return GL_FALSE;
+   }
+
+   for (i = 0; i < n; i++) {
+      const struct program *prog;
+      if (ids[i] == 0) {
+         _mesa_error(ctx, GL_INVALID_VALUE, "glAreProgramsResidentNV");
+         return GL_FALSE;
+      }
+      prog = (const struct program *)
+         _mesa_HashLookup(ctx->Shared->Programs, ids[i]);
+      if (!prog) {
+         _mesa_error(ctx, GL_INVALID_VALUE, "glAreProgramsResidentNV");
+         return GL_FALSE;
+      }
+      if (prog->Resident) {
+        if (!allResident)
+           residences[i] = GL_TRUE;
+      }
+      else {
+         if (allResident) {
+           allResident = GL_FALSE;
+           for (j = 0; j < i; j++)
+              residences[j] = GL_TRUE;
+        }
+        residences[i] = GL_FALSE;
+      }
+   }
+
+   return allResident;
+}
+
+
+/**
+ * Request that a set of programs be resident in hardware.
+ * \note Called from the GL API dispatcher.
+ */
+void GLAPIENTRY
+_mesa_RequestResidentProgramsNV(GLsizei n, const GLuint *ids)
+{
+   GLint i;
+   GET_CURRENT_CONTEXT(ctx);
+   ASSERT_OUTSIDE_BEGIN_END(ctx);
+
+   if (n < 0) {
+      _mesa_error(ctx, GL_INVALID_VALUE, "glRequestResidentProgramsNV(n)");
+      return;
+   }
+
+   /* just error checking for now */
+   for (i = 0; i < n; i++) {
+      struct program *prog;
+
+      if (ids[i] == 0) {
+         _mesa_error(ctx, GL_INVALID_VALUE, "glRequestResidentProgramsNV(id)");
+         return;
+      }
+
+      prog = (struct program *) _mesa_HashLookup(ctx->Shared->Programs, ids[i]);
+
+      if (!prog) {
+         _mesa_error(ctx, GL_INVALID_VALUE, "glRequestResidentProgramsNV(id)");
+         return;
+      }
+
+      prog->Resident = GL_TRUE;
+   }
+}
+
+
+/**
+ * Get a program parameter register.
+ * \note Not compiled into display lists.
+ * \note Called from the GL API dispatcher.
+ */
+void GLAPIENTRY
+_mesa_GetProgramParameterfvNV(GLenum target, GLuint index,
+                              GLenum pname, GLfloat *params)
+{
+   GET_CURRENT_CONTEXT(ctx);
+   ASSERT_OUTSIDE_BEGIN_END(ctx);
+
+   if (target == GL_VERTEX_PROGRAM_NV) {
+      if (pname == GL_PROGRAM_PARAMETER_NV) {
+         if (index < MAX_NV_VERTEX_PROGRAM_PARAMS) {
+            COPY_4V(params, ctx->VertexProgram.Parameters[index]);
+         }
+         else {
+            _mesa_error(ctx, GL_INVALID_VALUE,
+                        "glGetProgramParameterfvNV(index)");
+            return;
+         }
+      }
+      else {
+         _mesa_error(ctx, GL_INVALID_ENUM, "glGetProgramParameterfvNV(pname)");
+         return;
+      }
+   }
+   else {
+      _mesa_error(ctx, GL_INVALID_ENUM, "glGetProgramParameterfvNV(target)");
+      return;
+   }
+}
+
+
+/**
+ * Get a program parameter register.
+ * \note Not compiled into display lists.
+ * \note Called from the GL API dispatcher.
+ */
+void GLAPIENTRY
+_mesa_GetProgramParameterdvNV(GLenum target, GLuint index,
+                              GLenum pname, GLdouble *params)
+{
+   GET_CURRENT_CONTEXT(ctx);
+   ASSERT_OUTSIDE_BEGIN_END(ctx);
+
+   if (target == GL_VERTEX_PROGRAM_NV) {
+      if (pname == GL_PROGRAM_PARAMETER_NV) {
+         if (index < MAX_NV_VERTEX_PROGRAM_PARAMS) {
+            COPY_4V(params, ctx->VertexProgram.Parameters[index]);
+         }
+         else {
+            _mesa_error(ctx, GL_INVALID_VALUE,
+                        "glGetProgramParameterdvNV(index)");
+            return;
+         }
+      }
+      else {
+         _mesa_error(ctx, GL_INVALID_ENUM, "glGetProgramParameterdvNV(pname)");
+         return;
+      }
+   }
+   else {
+      _mesa_error(ctx, GL_INVALID_ENUM, "glGetProgramParameterdvNV(target)");
+      return;
+   }
+}
+
+
+/**
+ * Get a program attribute.
+ * \note Not compiled into display lists.
+ * \note Called from the GL API dispatcher.
+ */
+void GLAPIENTRY
+_mesa_GetProgramivNV(GLuint id, GLenum pname, GLint *params)
+{
+   struct program *prog;
+   GET_CURRENT_CONTEXT(ctx);
+
+   if (!ctx->_CurrentProgram)
+      ASSERT_OUTSIDE_BEGIN_END(ctx);
+
+   prog = (struct program *) _mesa_HashLookup(ctx->Shared->Programs, id);
+   if (!prog) {
+      _mesa_error(ctx, GL_INVALID_OPERATION, "glGetProgramivNV");
+      return;
+   }
+
+   switch (pname) {
+      case GL_PROGRAM_TARGET_NV:
+         *params = prog->Target;
+         return;
+      case GL_PROGRAM_LENGTH_NV:
+         *params = prog->String ? _mesa_strlen((char *) prog->String) : 0;
+         return;
+      case GL_PROGRAM_RESIDENT_NV:
+         *params = prog->Resident;
+         return;
+      default:
+         _mesa_error(ctx, GL_INVALID_ENUM, "glGetProgramivNV(pname)");
+         return;
+   }
+}
+
+
+/**
+ * Get the program source code.
+ * \note Not compiled into display lists.
+ * \note Called from the GL API dispatcher.
+ */
+void GLAPIENTRY
+_mesa_GetProgramStringNV(GLuint id, GLenum pname, GLubyte *program)
+{
+   struct program *prog;
+   GET_CURRENT_CONTEXT(ctx);
+
+   if (!ctx->_CurrentProgram)
+      ASSERT_OUTSIDE_BEGIN_END(ctx);
+
+   if (pname != GL_PROGRAM_STRING_NV) {
+      _mesa_error(ctx, GL_INVALID_ENUM, "glGetProgramStringNV(pname)");
+      return;
+   }
+
+   prog = (struct program *) _mesa_HashLookup(ctx->Shared->Programs, id);
+   if (!prog) {
+      _mesa_error(ctx, GL_INVALID_OPERATION, "glGetProgramStringNV");
+      return;
+   }
+
+   if (prog->String) {
+      MEMCPY(program, prog->String, _mesa_strlen((char *) prog->String));
+   }
+   else {
+      program[0] = 0;
+   }
+}
+
+
+/**
+ * Get matrix tracking information.
+ * \note Not compiled into display lists.
+ * \note Called from the GL API dispatcher.
+ */
+void GLAPIENTRY
+_mesa_GetTrackMatrixivNV(GLenum target, GLuint address,
+                         GLenum pname, GLint *params)
+{
+   GET_CURRENT_CONTEXT(ctx);
+   ASSERT_OUTSIDE_BEGIN_END(ctx);
+
+   if (target == GL_VERTEX_PROGRAM_NV
+       && ctx->Extensions.NV_vertex_program) {
+      GLuint i;
+
+      if ((address & 0x3) || address >= MAX_NV_VERTEX_PROGRAM_PARAMS) {
+         _mesa_error(ctx, GL_INVALID_VALUE, "glGetTrackMatrixivNV(address)");
+         return;
+      }
+
+      i = address / 4;
+
+      switch (pname) {
+         case GL_TRACK_MATRIX_NV:
+            params[0] = (GLint) ctx->VertexProgram.TrackMatrix[i];
+            return;
+         case GL_TRACK_MATRIX_TRANSFORM_NV:
+            params[0] = (GLint) ctx->VertexProgram.TrackMatrixTransform[i];
+            return;
+         default:
+            _mesa_error(ctx, GL_INVALID_ENUM, "glGetTrackMatrixivNV");
+            return;
+      }
+   }
+   else {
+      _mesa_error(ctx, GL_INVALID_ENUM, "glGetTrackMatrixivNV");
+      return;
+   }
+}
+
+
+/**
+ * Get a vertex (or vertex array) attribute.
+ * \note Not compiled into display lists.
+ * \note Called from the GL API dispatcher.
+ */
+void GLAPIENTRY
+_mesa_GetVertexAttribdvNV(GLuint index, GLenum pname, GLdouble *params)
+{
+   GET_CURRENT_CONTEXT(ctx);
+   ASSERT_OUTSIDE_BEGIN_END(ctx);
+
+   if (index == 0 || index >= MAX_NV_VERTEX_PROGRAM_INPUTS) {
+      _mesa_error(ctx, GL_INVALID_VALUE, "glGetVertexAttribdvNV(index)");
+      return;
+   }
+
+   switch (pname) {
+      case GL_ATTRIB_ARRAY_SIZE_NV:
+         params[0] = ctx->Array.VertexAttrib[index].Size;
+         break;
+      case GL_ATTRIB_ARRAY_STRIDE_NV:
+         params[0] = ctx->Array.VertexAttrib[index].Stride;
+         break;
+      case GL_ATTRIB_ARRAY_TYPE_NV:
+         params[0] = ctx->Array.VertexAttrib[index].Type;
+         break;
+      case GL_CURRENT_ATTRIB_NV:
+        FLUSH_CURRENT(ctx, 0);
+         COPY_4V(params, ctx->Current.Attrib[index]);
+         break;
+      default:
+         _mesa_error(ctx, GL_INVALID_ENUM, "glGetVertexAttribdvNV");
+         return;
+   }
+}
+
+/**
+ * Get a vertex (or vertex array) attribute.
+ * \note Not compiled into display lists.
+ * \note Called from the GL API dispatcher.
+ */
+void GLAPIENTRY
+_mesa_GetVertexAttribfvNV(GLuint index, GLenum pname, GLfloat *params)
+{
+   GET_CURRENT_CONTEXT(ctx);
+   ASSERT_OUTSIDE_BEGIN_END(ctx);
+
+   if (index == 0 || index >= MAX_NV_VERTEX_PROGRAM_INPUTS) {
+      _mesa_error(ctx, GL_INVALID_VALUE, "glGetVertexAttribdvNV(index)");
+      return;
+   }
+
+   switch (pname) {
+      case GL_ATTRIB_ARRAY_SIZE_NV:
+         params[0] = (GLfloat) ctx->Array.VertexAttrib[index].Size;
+         break;
+      case GL_ATTRIB_ARRAY_STRIDE_NV:
+         params[0] = (GLfloat) ctx->Array.VertexAttrib[index].Stride;
+         break;
+      case GL_ATTRIB_ARRAY_TYPE_NV:
+         params[0] = (GLfloat) ctx->Array.VertexAttrib[index].Type;
+         break;
+      case GL_CURRENT_ATTRIB_NV:
+        FLUSH_CURRENT(ctx, 0);
+         COPY_4V(params, ctx->Current.Attrib[index]);
+         break;
+      default:
+         _mesa_error(ctx, GL_INVALID_ENUM, "glGetVertexAttribdvNV");
+         return;
+   }
+}
+
+/**
+ * Get a vertex (or vertex array) attribute.
+ * \note Not compiled into display lists.
+ * \note Called from the GL API dispatcher.
+ */
+void GLAPIENTRY
+_mesa_GetVertexAttribivNV(GLuint index, GLenum pname, GLint *params)
+{
+   GET_CURRENT_CONTEXT(ctx);
+   ASSERT_OUTSIDE_BEGIN_END(ctx);
+
+   if (index == 0 || index >= MAX_NV_VERTEX_PROGRAM_INPUTS) {
+      _mesa_error(ctx, GL_INVALID_VALUE, "glGetVertexAttribdvNV(index)");
+      return;
+   }
+
+   switch (pname) {
+      case GL_ATTRIB_ARRAY_SIZE_NV:
+         params[0] = ctx->Array.VertexAttrib[index].Size;
+         break;
+      case GL_ATTRIB_ARRAY_STRIDE_NV:
+         params[0] = ctx->Array.VertexAttrib[index].Stride;
+         break;
+      case GL_ATTRIB_ARRAY_TYPE_NV:
+         params[0] = ctx->Array.VertexAttrib[index].Type;
+         break;
+      case GL_CURRENT_ATTRIB_NV:
+        FLUSH_CURRENT(ctx, 0);
+         params[0] = (GLint) ctx->Current.Attrib[index][0];
+         params[1] = (GLint) ctx->Current.Attrib[index][1];
+         params[2] = (GLint) ctx->Current.Attrib[index][2];
+         params[3] = (GLint) ctx->Current.Attrib[index][3];
+         break;
+      case GL_VERTEX_ATTRIB_ARRAY_BUFFER_BINDING_ARB:
+         if (!ctx->Extensions.ARB_vertex_buffer_object) {
+            _mesa_error(ctx, GL_INVALID_ENUM, "glGetVertexAttribdvNV");
+            return;
+         }
+         params[0] = ctx->Array.VertexAttrib[index].BufferObj->Name;
+         break;
+      default:
+         _mesa_error(ctx, GL_INVALID_ENUM, "glGetVertexAttribdvNV");
+         return;
+   }
+}
+
+
+/**
+ * Get a vertex array attribute pointer.
+ * \note Not compiled into display lists.
+ * \note Called from the GL API dispatcher.
+ */
+void GLAPIENTRY
+_mesa_GetVertexAttribPointervNV(GLuint index, GLenum pname, GLvoid **pointer)
+{
+   GET_CURRENT_CONTEXT(ctx);
+   ASSERT_OUTSIDE_BEGIN_END(ctx);
+
+   if (index >= MAX_NV_VERTEX_PROGRAM_INPUTS) {
+      _mesa_error(ctx, GL_INVALID_VALUE, "glGetVertexAttribPointerNV(index)");
+      return;
+   }
+
+   if (pname != GL_ATTRIB_ARRAY_POINTER_NV) {
+      _mesa_error(ctx, GL_INVALID_ENUM, "glGetVertexAttribPointerNV(pname)");
+      return;
+   }
+
+   *pointer = (GLvoid *) ctx->Array.VertexAttrib[index].Ptr;;
+}
+
+
+
+/**
+ * Load/parse/compile a program.
+ * \note Called from the GL API dispatcher.
+ */
+void GLAPIENTRY
+_mesa_LoadProgramNV(GLenum target, GLuint id, GLsizei len,
+                    const GLubyte *program)
+{
+   struct program *prog;
+   GET_CURRENT_CONTEXT(ctx);
+   ASSERT_OUTSIDE_BEGIN_END(ctx);
+
+   if (id == 0) {
+      _mesa_error(ctx, GL_INVALID_VALUE, "glLoadProgramNV(id)");
+      return;
+   }
+
+   FLUSH_VERTICES(ctx, _NEW_PROGRAM);
+
+   prog = (struct program *) _mesa_HashLookup(ctx->Shared->Programs, id);
+
+   if (prog && prog->Target != 0 && prog->Target != target) {
+      _mesa_error(ctx, GL_INVALID_OPERATION, "glLoadProgramNV(target)");
+      return;
+   }
+
+   if ((target == GL_VERTEX_PROGRAM_NV ||
+        target == GL_VERTEX_STATE_PROGRAM_NV)
+       && ctx->Extensions.NV_vertex_program) {
+      struct vertex_program *vprog = (struct vertex_program *) prog;
+      if (!vprog) {
+         vprog = CALLOC_STRUCT(vertex_program);
+         if (!vprog) {
+            _mesa_error(ctx, GL_OUT_OF_MEMORY, "glLoadProgramNV");
+            return;
+         }
+         vprog->Base.RefCount = 1;
+         vprog->Base.Resident = GL_TRUE;
+         _mesa_HashInsert(ctx->Shared->Programs, id, vprog);
+      }
+      _mesa_parse_nv_vertex_program(ctx, target, program, len, vprog);
+   }
+   else if (target == GL_FRAGMENT_PROGRAM_NV
+            && ctx->Extensions.NV_fragment_program) {
+      struct fragment_program *fprog = (struct fragment_program *) prog;
+      if (!fprog) {
+         fprog = CALLOC_STRUCT(fragment_program);
+         if (!fprog) {
+            _mesa_error(ctx, GL_OUT_OF_MEMORY, "glLoadProgramNV");
+            return;
+         }
+         fprog->Base.RefCount = 1;
+         fprog->Base.Resident = GL_TRUE;
+         _mesa_HashInsert(ctx->Shared->Programs, id, fprog);
+      }
+      _mesa_parse_nv_fragment_program(ctx, target, program, len, fprog);
+   }
+   else {
+      _mesa_error(ctx, GL_INVALID_ENUM, "glLoadProgramNV(target)");
+   }
+}
+
+
+
+/**
+ * Set a program parameter register.
+ * \note Called from the GL API dispatcher.
+ */
+void GLAPIENTRY
+_mesa_ProgramParameter4dNV(GLenum target, GLuint index,
+                           GLdouble x, GLdouble y, GLdouble z, GLdouble w)
+{
+   _mesa_ProgramParameter4fNV(target, index, 
+                             (GLfloat)x, (GLfloat)y, (GLfloat)z, (GLfloat)w);
+}
+
+
+/**
+ * Set a program parameter register.
+ * \note Called from the GL API dispatcher.
+ */
+void GLAPIENTRY
+_mesa_ProgramParameter4dvNV(GLenum target, GLuint index,
+                            const GLdouble *params)
+{
+   _mesa_ProgramParameter4fNV(target, index,
+                              (GLfloat)params[0], (GLfloat)params[1], 
+                             (GLfloat)params[2], (GLfloat)params[3]);
+}
+
+
+/**
+ * Set a program parameter register.
+ * \note Called from the GL API dispatcher.
+ */
+void GLAPIENTRY
+_mesa_ProgramParameter4fNV(GLenum target, GLuint index,
+                           GLfloat x, GLfloat y, GLfloat z, GLfloat w)
+{
+   GET_CURRENT_CONTEXT(ctx);
+   ASSERT_OUTSIDE_BEGIN_END(ctx);
+
+   if (target == GL_VERTEX_PROGRAM_NV && ctx->Extensions.NV_vertex_program) {
+      if (index < MAX_NV_VERTEX_PROGRAM_PARAMS) {
+         FLUSH_VERTICES(ctx, _NEW_PROGRAM);
+         ASSIGN_4V(ctx->VertexProgram.Parameters[index], x, y, z, w);
+      }
+      else {
+         _mesa_error(ctx, GL_INVALID_VALUE, "glProgramParameterNV(index)");
+         return;
+      }
+   }
+   else {
+      _mesa_error(ctx, GL_INVALID_ENUM, "glProgramParameterNV");
+      return;
+   }
+}
+
+
+/**
+ * Set a program parameter register.
+ * \note Called from the GL API dispatcher.
+ */
+void GLAPIENTRY
+_mesa_ProgramParameter4fvNV(GLenum target, GLuint index,
+                            const GLfloat *params)
+{
+   _mesa_ProgramParameter4fNV(target, index,
+                              params[0], params[1], params[2], params[3]);
+}
+
+
+
+/**
+ * Set a sequence of program parameter registers.
+ * \note Called from the GL API dispatcher.
+ */
+void GLAPIENTRY
+_mesa_ProgramParameters4dvNV(GLenum target, GLuint index,
+                             GLuint num, const GLdouble *params)
+{
+   GET_CURRENT_CONTEXT(ctx);
+   ASSERT_OUTSIDE_BEGIN_END(ctx);
+
+   if (target == GL_VERTEX_PROGRAM_NV && ctx->Extensions.NV_vertex_program) {
+      GLuint i;
+      if (index + num > MAX_NV_VERTEX_PROGRAM_PARAMS) {
+         _mesa_error(ctx, GL_INVALID_VALUE, "glProgramParameters4dvNV");
+         return;
+      }
+      for (i = 0; i < num; i++) {
+         ctx->VertexProgram.Parameters[index + i][0] = (GLfloat) params[0];
+         ctx->VertexProgram.Parameters[index + i][1] = (GLfloat) params[1];
+         ctx->VertexProgram.Parameters[index + i][2] = (GLfloat) params[2];
+         ctx->VertexProgram.Parameters[index + i][3] = (GLfloat) params[3];
+         params += 4;
+      };
+   }
+   else {
+      _mesa_error(ctx, GL_INVALID_ENUM, "glProgramParameters4dvNV");
+      return;
+   }
+}
+
+
+/**
+ * Set a sequence of program parameter registers.
+ * \note Called from the GL API dispatcher.
+ */
+void GLAPIENTRY
+_mesa_ProgramParameters4fvNV(GLenum target, GLuint index,
+                             GLuint num, const GLfloat *params)
+{
+   GET_CURRENT_CONTEXT(ctx);
+   ASSERT_OUTSIDE_BEGIN_END(ctx);
+
+   if (target == GL_VERTEX_PROGRAM_NV && ctx->Extensions.NV_vertex_program) {
+      GLuint i;
+      if (index + num > MAX_NV_VERTEX_PROGRAM_PARAMS) {
+         _mesa_error(ctx, GL_INVALID_VALUE, "glProgramParameters4fvNV");
+         return;
+      }
+      for (i = 0; i < num; i++) {
+         COPY_4V(ctx->VertexProgram.Parameters[index + i], params);
+         params += 4;
+      };
+   }
+   else {
+      _mesa_error(ctx, GL_INVALID_ENUM, "glProgramParameters4fvNV");
+      return;
+   }
+}
+
+
+
+/**
+ * Setup tracking of matrices into program parameter registers.
+ * \note Called from the GL API dispatcher.
+ */
+void GLAPIENTRY
+_mesa_TrackMatrixNV(GLenum target, GLuint address,
+                    GLenum matrix, GLenum transform)
+{
+   GET_CURRENT_CONTEXT(ctx);
+   ASSERT_OUTSIDE_BEGIN_END(ctx);
+
+   FLUSH_VERTICES(ctx, _NEW_PROGRAM);
+
+   if (target == GL_VERTEX_PROGRAM_NV && ctx->Extensions.NV_vertex_program) {
+      if (address & 0x3) {
+         /* addr must be multiple of four */
+         _mesa_error(ctx, GL_INVALID_VALUE, "glTrackMatrixNV(address)");
+         return;
+      }
+
+      switch (matrix) {
+         case GL_NONE:
+         case GL_MODELVIEW:
+         case GL_PROJECTION:
+         case GL_TEXTURE:
+         case GL_COLOR:
+         case GL_MODELVIEW_PROJECTION_NV:
+         case GL_MATRIX0_NV:
+         case GL_MATRIX1_NV:
+         case GL_MATRIX2_NV:
+         case GL_MATRIX3_NV:
+         case GL_MATRIX4_NV:
+         case GL_MATRIX5_NV:
+         case GL_MATRIX6_NV:
+         case GL_MATRIX7_NV:
+            /* OK, fallthrough */
+            break;
+         default:
+            _mesa_error(ctx, GL_INVALID_ENUM, "glTrackMatrixNV(matrix)");
+            return;
+      }
+
+      switch (transform) {
+         case GL_IDENTITY_NV:
+         case GL_INVERSE_NV:
+         case GL_TRANSPOSE_NV:
+         case GL_INVERSE_TRANSPOSE_NV:
+            /* OK, fallthrough */
+            break;
+         default:
+            _mesa_error(ctx, GL_INVALID_ENUM, "glTrackMatrixNV(transform)");
+            return;
+      }
+
+      ctx->VertexProgram.TrackMatrix[address / 4] = matrix;
+      ctx->VertexProgram.TrackMatrixTransform[address / 4] = transform;
+   }
+   else {
+      _mesa_error(ctx, GL_INVALID_ENUM, "glTrackMatrixNV(target)");
+      return;
+   }
+}
+
+
+void GLAPIENTRY
+_mesa_ProgramNamedParameter4fNV(GLuint id, GLsizei len, const GLubyte *name,
+                                GLfloat x, GLfloat y, GLfloat z, GLfloat w)
+{
+   struct program *prog;
+   struct fragment_program *fragProg;
+   GLfloat *v;
+
+   GET_CURRENT_CONTEXT(ctx);
+   ASSERT_OUTSIDE_BEGIN_END(ctx);
+
+   FLUSH_VERTICES(ctx, _NEW_PROGRAM);
+
+   prog = (struct program *) _mesa_HashLookup(ctx->Shared->Programs, id);
+   if (!prog || prog->Target != GL_FRAGMENT_PROGRAM_NV) {
+      _mesa_error(ctx, GL_INVALID_OPERATION, "glProgramNamedParameterNV");
+      return;
+   }
+
+   if (len <= 0) {
+      _mesa_error(ctx, GL_INVALID_VALUE, "glProgramNamedParameterNV");
+      return;
+   }
+
+   fragProg = (struct fragment_program *) prog;
+   v = _mesa_lookup_parameter_value(fragProg->Parameters, len, (char *) name);
+   if (v) {
+      v[0] = x;
+      v[1] = y;
+      v[2] = z;
+      v[3] = w;
+      return;
+   }
+
+   _mesa_error(ctx, GL_INVALID_VALUE, "glProgramNamedParameterNV");
+}
+
+
+void GLAPIENTRY
+_mesa_ProgramNamedParameter4fvNV(GLuint id, GLsizei len, const GLubyte *name,
+                                 const float v[])
+{
+   _mesa_ProgramNamedParameter4fNV(id, len, name, v[0], v[1], v[2], v[3]);
+}
+
+
+void GLAPIENTRY
+_mesa_ProgramNamedParameter4dNV(GLuint id, GLsizei len, const GLubyte *name,
+                                GLdouble x, GLdouble y, GLdouble z, GLdouble w)
+{
+   _mesa_ProgramNamedParameter4fNV(id, len, name, (GLfloat)x, (GLfloat)y, 
+                                   (GLfloat)z, (GLfloat)w);
+}
+
+
+void GLAPIENTRY
+_mesa_ProgramNamedParameter4dvNV(GLuint id, GLsizei len, const GLubyte *name,
+                                 const double v[])
+{
+   _mesa_ProgramNamedParameter4fNV(id, len, name,
+                                   (GLfloat)v[0], (GLfloat)v[1],
+                                   (GLfloat)v[2], (GLfloat)v[3]);
+}
+
+
+void GLAPIENTRY
+_mesa_GetProgramNamedParameterfvNV(GLuint id, GLsizei len, const GLubyte *name,
+                                   GLfloat *params)
+{
+   struct program *prog;
+   struct fragment_program *fragProg;
+   const GLfloat *v;
+
+   GET_CURRENT_CONTEXT(ctx);
+
+   if (!ctx->_CurrentProgram)
+      ASSERT_OUTSIDE_BEGIN_END(ctx);
+
+   prog = (struct program *) _mesa_HashLookup(ctx->Shared->Programs, id);
+   if (!prog || prog->Target != GL_FRAGMENT_PROGRAM_NV) {
+      _mesa_error(ctx, GL_INVALID_OPERATION, "glGetProgramNamedParameterNV");
+      return;
+   }
+
+   if (len <= 0) {
+      _mesa_error(ctx, GL_INVALID_VALUE, "glGetProgramNamedParameterNV");
+      return;
+   }
+
+   fragProg = (struct fragment_program *) prog;
+   v = _mesa_lookup_parameter_value(fragProg->Parameters, len, (char *) name);
+   if (v) {
+      params[0] = v[0];
+      params[1] = v[1];
+      params[2] = v[2];
+      params[3] = v[3];
+      return;
+   }
+
+   _mesa_error(ctx, GL_INVALID_VALUE, "glGetProgramNamedParameterNV");
+}
+
+
+void GLAPIENTRY
+_mesa_GetProgramNamedParameterdvNV(GLuint id, GLsizei len, const GLubyte *name,
+                                   GLdouble *params)
+{
+   GLfloat floatParams[4];
+   _mesa_GetProgramNamedParameterfvNV(id, len, name, floatParams);
+   COPY_4V(params, floatParams);
+}
diff --git a/src/mesa/shader/nvprogram.h b/src/mesa/shader/nvprogram.h
new file mode 100644 (file)
index 0000000..dcea772
--- /dev/null
@@ -0,0 +1,119 @@
+/*
+ * Mesa 3-D graphics library
+ * Version:  5.1
+ *
+ * Copyright (C) 1999-2003  Brian Paul   All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * BRIAN PAUL BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors:
+ *    Brian Paul
+ */
+
+
+#ifndef NVPROGRAM_H
+#define NVPROGRAM_H
+
+
+extern void GLAPIENTRY
+_mesa_ExecuteProgramNV(GLenum target, GLuint id, const GLfloat *params);
+
+extern GLboolean GLAPIENTRY 
+_mesa_AreProgramsResidentNV(GLsizei n, const GLuint *ids, GLboolean *residences);
+
+extern void GLAPIENTRY
+_mesa_RequestResidentProgramsNV(GLsizei n, const GLuint *ids);
+
+extern void GLAPIENTRY
+_mesa_GetProgramParameterfvNV(GLenum target, GLuint index, GLenum pname, GLfloat *params);
+
+extern void GLAPIENTRY
+_mesa_GetProgramParameterdvNV(GLenum target, GLuint index, GLenum pname, GLdouble *params);
+
+extern void GLAPIENTRY
+_mesa_GetProgramivNV(GLuint id, GLenum pname, GLint *params);
+
+extern void GLAPIENTRY
+_mesa_GetProgramStringNV(GLuint id, GLenum pname, GLubyte *program);
+
+extern void GLAPIENTRY
+_mesa_GetTrackMatrixivNV(GLenum target, GLuint address, GLenum pname, GLint *params);
+
+extern void GLAPIENTRY
+_mesa_GetVertexAttribdvNV(GLuint index, GLenum pname, GLdouble *params);
+
+extern void GLAPIENTRY
+_mesa_GetVertexAttribfvNV(GLuint index, GLenum pname, GLfloat *params);
+
+extern void GLAPIENTRY
+_mesa_GetVertexAttribivNV(GLuint index, GLenum pname, GLint *params);
+
+extern void GLAPIENTRY
+_mesa_GetVertexAttribPointervNV(GLuint index, GLenum pname, GLvoid **pointer);
+
+extern void GLAPIENTRY
+_mesa_LoadProgramNV(GLenum target, GLuint id, GLsizei len, const GLubyte *program);
+
+extern void GLAPIENTRY
+_mesa_ProgramParameter4dNV(GLenum target, GLuint index, GLdouble x, GLdouble y, GLdouble z, GLdouble w);
+
+extern void GLAPIENTRY
+_mesa_ProgramParameter4dvNV(GLenum target, GLuint index, const GLdouble *params);
+
+extern void GLAPIENTRY
+_mesa_ProgramParameter4fNV(GLenum target, GLuint index, GLfloat x, GLfloat y, GLfloat z, GLfloat w);
+
+extern void GLAPIENTRY
+_mesa_ProgramParameter4fvNV(GLenum target, GLuint index, const GLfloat *params);
+
+extern void GLAPIENTRY
+_mesa_ProgramParameters4dvNV(GLenum target, GLuint index, GLuint num, const GLdouble *params);
+
+extern void GLAPIENTRY
+_mesa_ProgramParameters4fvNV(GLenum target, GLuint index, GLuint num, const GLfloat *params);
+
+extern void GLAPIENTRY
+_mesa_TrackMatrixNV(GLenum target, GLuint address, GLenum matrix, GLenum transform);
+
+
+extern void GLAPIENTRY
+_mesa_ProgramNamedParameter4fNV(GLuint id, GLsizei len, const GLubyte *name,
+                                GLfloat x, GLfloat y, GLfloat z, GLfloat w);
+
+extern void GLAPIENTRY
+_mesa_ProgramNamedParameter4fvNV(GLuint id, GLsizei len, const GLubyte *name,
+                                 const float v[]);
+
+extern void GLAPIENTRY
+_mesa_ProgramNamedParameter4dNV(GLuint id, GLsizei len, const GLubyte *name,
+                                GLdouble x, GLdouble y, GLdouble z, GLdouble w);
+
+extern void GLAPIENTRY
+_mesa_ProgramNamedParameter4dvNV(GLuint id, GLsizei len, const GLubyte *name,
+                                 const double v[]);
+
+extern void GLAPIENTRY
+_mesa_GetProgramNamedParameterfvNV(GLuint id, GLsizei len, const GLubyte *name,
+                                   GLfloat *params);
+
+extern void GLAPIENTRY
+_mesa_GetProgramNamedParameterdvNV(GLuint id, GLsizei len, const GLubyte *name,
+                                   GLdouble *params);
+
+
+#endif
diff --git a/src/mesa/shader/nvvertexec.c b/src/mesa/shader/nvvertexec.c
new file mode 100644 (file)
index 0000000..9663b38
--- /dev/null
@@ -0,0 +1,839 @@
+/*
+ * Mesa 3-D graphics library
+ * Version:  6.0.1
+ *
+ * Copyright (C) 1999-2004  Brian Paul   All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * BRIAN PAUL BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+/**
+ * \file nvvertexec.c
+ * Code to execute vertex programs.
+ * \author Brian Paul
+ */
+
+#include "glheader.h"
+#include "context.h"
+#include "imports.h"
+#include "macros.h"
+#include "mtypes.h"
+#include "nvvertexec.h"
+#include "nvvertprog.h"
+#include "program.h"
+#include "math/m_matrix.h"
+
+
+static const GLfloat zeroVec[4] = { 0, 0, 0, 0 };
+
+
+/**
+ * Load/initialize the vertex program registers.
+ * This needs to be done per vertex.
+ */
+void
+_mesa_init_vp_registers(GLcontext *ctx)
+{
+   GLuint i;
+
+   /* Input registers get initialized from the current vertex attribs */
+   MEMCPY(ctx->VertexProgram.Inputs, ctx->Current.Attrib,
+          VERT_ATTRIB_MAX * 4 * sizeof(GLfloat));
+
+   /* Output and temp regs are initialized to [0,0,0,1] */
+   for (i = 0; i < MAX_NV_VERTEX_PROGRAM_OUTPUTS; i++) {
+      ASSIGN_4V(ctx->VertexProgram.Outputs[i], 0.0F, 0.0F, 0.0F, 1.0F);
+   }
+   for (i = 0; i < MAX_NV_VERTEX_PROGRAM_TEMPS; i++) {
+      ASSIGN_4V(ctx->VertexProgram.Temporaries[i], 0.0F, 0.0F, 0.0F, 1.0F);
+   }
+
+   /* The program parameters aren't touched */
+   /* XXX: This should be moved to glBegin() time, but its safe (and slow!) 
+    * here - Karl
+    */
+   if (ctx->VertexProgram.Current->Parameters) {
+      /* Grab the state */                       
+      _mesa_load_state_parameters(ctx, ctx->VertexProgram.Current->Parameters);
+
+      /* And copy it into the program state */
+      for (i=0; i<ctx->VertexProgram.Current->Parameters->NumParameters; i++) {
+         MEMCPY(ctx->VertexProgram.Parameters[i], 
+                &ctx->VertexProgram.Current->Parameters->Parameters[i].Values,
+                4*sizeof(GLfloat));                            
+      }                                  
+   }
+}
+
+
+
+/**
+ * Copy the 16 elements of a matrix into four consecutive program
+ * registers starting at 'pos'.
+ */
+static void
+load_matrix(GLfloat registers[][4], GLuint pos, const GLfloat mat[16])
+{
+   GLuint i;
+   for (i = 0; i < 4; i++) {
+      registers[pos + i][0] = mat[0 + i];
+      registers[pos + i][1] = mat[4 + i];
+      registers[pos + i][2] = mat[8 + i];
+      registers[pos + i][3] = mat[12 + i];
+   }
+}
+
+
+/**
+ * As above, but transpose the matrix.
+ */
+static void
+load_transpose_matrix(GLfloat registers[][4], GLuint pos,
+                      const GLfloat mat[16])
+{
+   MEMCPY(registers[pos], mat, 16 * sizeof(GLfloat));
+}
+
+
+/**
+ * Load all currently tracked matrices into the program registers.
+ * This needs to be done per glBegin/glEnd.
+ */
+void
+_mesa_init_tracked_matrices(GLcontext *ctx)
+{
+   GLuint i;
+
+   for (i = 0; i < MAX_NV_VERTEX_PROGRAM_PARAMS / 4; i++) {
+      /* point 'mat' at source matrix */
+      GLmatrix *mat;
+      if (ctx->VertexProgram.TrackMatrix[i] == GL_MODELVIEW) {
+         mat = ctx->ModelviewMatrixStack.Top;
+      }
+      else if (ctx->VertexProgram.TrackMatrix[i] == GL_PROJECTION) {
+         mat = ctx->ProjectionMatrixStack.Top;
+      }
+      else if (ctx->VertexProgram.TrackMatrix[i] == GL_TEXTURE) {
+         mat = ctx->TextureMatrixStack[ctx->Texture.CurrentUnit].Top;
+      }
+      else if (ctx->VertexProgram.TrackMatrix[i] == GL_COLOR) {
+         mat = ctx->ColorMatrixStack.Top;
+      }
+      else if (ctx->VertexProgram.TrackMatrix[i]==GL_MODELVIEW_PROJECTION_NV) {
+         /* XXX verify the combined matrix is up to date */
+         mat = &ctx->_ModelProjectMatrix;
+      }
+      else if (ctx->VertexProgram.TrackMatrix[i] >= GL_MATRIX0_NV &&
+               ctx->VertexProgram.TrackMatrix[i] <= GL_MATRIX7_NV) {
+         GLuint n = ctx->VertexProgram.TrackMatrix[i] - GL_MATRIX0_NV;
+         ASSERT(n < MAX_PROGRAM_MATRICES);
+         mat = ctx->ProgramMatrixStack[n].Top;
+      }
+      else {
+         /* no matrix is tracked, but we leave the register values as-is */
+         assert(ctx->VertexProgram.TrackMatrix[i] == GL_NONE);
+         continue;
+      }
+
+      /* load the matrix */
+      if (ctx->VertexProgram.TrackMatrixTransform[i] == GL_IDENTITY_NV) {
+         load_matrix(ctx->VertexProgram.Parameters, i*4, mat->m);
+      }
+      else if (ctx->VertexProgram.TrackMatrixTransform[i] == GL_INVERSE_NV) {
+         _math_matrix_analyse(mat); /* update the inverse */
+         assert((mat->flags & MAT_DIRTY_INVERSE) == 0);
+         load_matrix(ctx->VertexProgram.Parameters, i*4, mat->inv);
+      }
+      else if (ctx->VertexProgram.TrackMatrixTransform[i] == GL_TRANSPOSE_NV) {
+         load_transpose_matrix(ctx->VertexProgram.Parameters, i*4, mat->m);
+      }
+      else {
+         assert(ctx->VertexProgram.TrackMatrixTransform[i]
+                == GL_INVERSE_TRANSPOSE_NV);
+         _math_matrix_analyse(mat); /* update the inverse */
+         assert((mat->flags & MAT_DIRTY_INVERSE) == 0);
+         load_transpose_matrix(ctx->VertexProgram.Parameters, i*4, mat->inv);
+      }
+   }
+}
+
+
+
+/**
+ * For debugging.  Dump the current vertex program machine registers.
+ */
+void
+_mesa_dump_vp_state( const struct vertex_program_state *state )
+{
+   int i;
+   _mesa_printf("VertexIn:\n");
+   for (i = 0; i < MAX_NV_VERTEX_PROGRAM_INPUTS; i++) {
+      _mesa_printf("%d: %f %f %f %f   ", i,
+                   state->Inputs[i][0],
+                   state->Inputs[i][1],
+                   state->Inputs[i][2],
+                   state->Inputs[i][3]);
+   }
+   _mesa_printf("\n");
+
+   _mesa_printf("VertexOut:\n");
+   for (i = 0; i < MAX_NV_VERTEX_PROGRAM_OUTPUTS; i++) {
+      _mesa_printf("%d: %f %f %f %f   ", i,
+                  state->Outputs[i][0],
+                  state->Outputs[i][1],
+                  state->Outputs[i][2],
+                  state->Outputs[i][3]);
+   }
+   _mesa_printf("\n");
+
+   _mesa_printf("Registers:\n");
+   for (i = 0; i < MAX_NV_VERTEX_PROGRAM_TEMPS; i++) {
+      _mesa_printf("%d: %f %f %f %f   ", i,
+                  state->Temporaries[i][0],
+                  state->Temporaries[i][1],
+                  state->Temporaries[i][2],
+                  state->Temporaries[i][3]);
+   }
+   _mesa_printf("\n");
+
+   _mesa_printf("Parameters:\n");
+   for (i = 0; i < MAX_NV_VERTEX_PROGRAM_PARAMS; i++) {
+      _mesa_printf("%d: %f %f %f %f   ", i,
+                  state->Parameters[i][0],
+                  state->Parameters[i][1],
+                  state->Parameters[i][2],
+                  state->Parameters[i][3]);
+   }
+   _mesa_printf("\n");
+}
+
+
+
+/**
+ * Return a pointer to the 4-element float vector specified by the given
+ * source register.
+ */
+static INLINE const GLfloat *
+get_register_pointer( const struct vp_src_register *source,
+                      const struct vertex_program_state *state )
+{
+   if (source->RelAddr) {
+      const GLint reg = source->Index + state->AddressReg[0];
+      ASSERT( (source->File == PROGRAM_ENV_PARAM) || 
+        (source->File == PROGRAM_STATE_VAR) );
+      if (reg < 0 || reg > MAX_NV_VERTEX_PROGRAM_PARAMS)
+         return zeroVec;
+      else
+         return state->Parameters[reg];
+   }
+   else {
+      switch (source->File) {
+         case PROGRAM_TEMPORARY:
+            return state->Temporaries[source->Index];
+         case PROGRAM_INPUT:
+            return state->Inputs[source->Index];
+         case PROGRAM_LOCAL_PARAM:
+            /* XXX fix */
+            return state->Temporaries[source->Index]; 
+         case PROGRAM_ENV_PARAM:
+            return state->Parameters[source->Index];
+         case PROGRAM_STATE_VAR:
+            return state->Parameters[source->Index];
+         default:
+            _mesa_problem(NULL,
+                          "Bad source register file in fetch_vector4(vp)");
+            return NULL;
+      }
+   }
+   return NULL;
+}
+
+
+/**
+ * Fetch a 4-element float vector from the given source register.
+ * Apply swizzling and negating as needed.
+ */
+static INLINE void
+fetch_vector4( const struct vp_src_register *source,
+               const struct vertex_program_state *state,
+               GLfloat result[4] )
+{
+   const GLfloat *src = get_register_pointer(source, state);
+
+   if (source->Negate) {
+      result[0] = -src[source->Swizzle[0]];
+      result[1] = -src[source->Swizzle[1]];
+      result[2] = -src[source->Swizzle[2]];
+      result[3] = -src[source->Swizzle[3]];
+   }
+   else {
+      result[0] = src[source->Swizzle[0]];
+      result[1] = src[source->Swizzle[1]];
+      result[2] = src[source->Swizzle[2]];
+      result[3] = src[source->Swizzle[3]];
+   }
+}
+
+
+
+/**
+ * As above, but only return result[0] element.
+ */
+static INLINE void
+fetch_vector1( const struct vp_src_register *source,
+               const struct vertex_program_state *state,
+               GLfloat result[4] )
+{
+   const GLfloat *src = get_register_pointer(source, state);
+
+   if (source->Negate) {
+      result[0] = -src[source->Swizzle[0]];
+   }
+   else {
+      result[0] = src[source->Swizzle[0]];
+   }
+}
+
+
+/**
+ * Store 4 floats into a register.
+ */
+static void
+store_vector4( const struct vp_dst_register *dest,
+               struct vertex_program_state *state,
+               const GLfloat value[4] )
+{
+   GLfloat *dst;
+   switch (dest->File) {
+      case PROGRAM_TEMPORARY:
+         dst = state->Temporaries[dest->Index];
+         break;
+      case PROGRAM_OUTPUT:
+         dst = state->Outputs[dest->Index];
+         break;
+      case PROGRAM_ENV_PARAM:
+         {
+            /* a slight hack */
+            GET_CURRENT_CONTEXT(ctx);
+            dst = ctx->VertexProgram.Parameters[dest->Index];
+         }
+         break;
+      default:
+         _mesa_problem(NULL, "Invalid register file in store_vector4(file=%d)",
+                       dest->File);
+         return;
+   }
+
+   if (dest->WriteMask[0])
+      dst[0] = value[0];
+   if (dest->WriteMask[1])
+      dst[1] = value[1];
+   if (dest->WriteMask[2])
+      dst[2] = value[2];
+   if (dest->WriteMask[3])
+      dst[3] = value[3];
+}
+
+
+/**
+ * Set x to positive or negative infinity.
+ */
+#if defined(USE_IEEE) || defined(_WIN32)
+#define SET_POS_INFINITY(x)  ( *((GLuint *) &x) = 0x7F800000 )
+#define SET_NEG_INFINITY(x)  ( *((GLuint *) &x) = 0xFF800000 )
+#elif defined(VMS)
+#define SET_POS_INFINITY(x)  x = __MAXFLOAT
+#define SET_NEG_INFINITY(x)  x = -__MAXFLOAT
+#else
+#define SET_POS_INFINITY(x)  x = (GLfloat) HUGE_VAL
+#define SET_NEG_INFINITY(x)  x = (GLfloat) -HUGE_VAL
+#endif
+
+#define SET_FLOAT_BITS(x, bits) ((fi_type *) &(x))->i = bits
+
+
+/**
+ * Execute the given vertex program
+ */
+void
+_mesa_exec_vertex_program(GLcontext *ctx, const struct vertex_program *program)
+{
+   struct vertex_program_state *state = &ctx->VertexProgram;
+   const struct vp_instruction *inst;
+
+   ctx->_CurrentProgram = GL_VERTEX_PROGRAM_ARB; /* or NV, doesn't matter */
+
+   /* If the program is position invariant, multiply the input
+    * position and the MVP matrix and stick it into the output pos slot
+    */
+   if (ctx->VertexProgram.Current->IsPositionInvariant) {
+      TRANSFORM_POINT( ctx->VertexProgram.Outputs[0], 
+                       ctx->_ModelProjectMatrix.m, 
+                       ctx->VertexProgram.Inputs[0]);
+
+      /* XXX: This could go elsewhere */
+      ctx->VertexProgram.Current->OutputsWritten |= 0x1;
+   }
+
+   for (inst = program->Instructions; /*inst->Opcode != VP_OPCODE_END*/; inst++) {
+
+      if (ctx->VertexProgram.CallbackEnabled &&
+          ctx->VertexProgram.Callback) {
+         ctx->VertexProgram.CurrentPosition = inst->StringPos;
+         ctx->VertexProgram.Callback(program->Base.Target,
+                                     ctx->VertexProgram.CallbackData);
+      }
+
+      switch (inst->Opcode) {
+         case VP_OPCODE_MOV:
+            {
+               GLfloat t[4];
+               fetch_vector4( &inst->SrcReg[0], state, t );
+               store_vector4( &inst->DstReg, state, t );
+            }
+            break;
+         case VP_OPCODE_LIT:
+            {
+               const GLfloat epsilon = 1.0e-5F; /* XXX fix? */
+               GLfloat t[4], lit[4];
+               fetch_vector4( &inst->SrcReg[0], state, t );
+               if (t[3] < -(128.0F - epsilon))
+                   t[3] = - (128.0F - epsilon);
+               else if (t[3] > 128.0F - epsilon)
+                  t[3] = 128.0F - epsilon;
+               if (t[0] < 0.0)
+                  t[0] = 0.0;
+               if (t[1] < 0.0)
+                  t[1] = 0.0;
+               lit[0] = 1.0;
+               lit[1] = t[0];
+               lit[2] = (t[0] > 0.0) ? (GLfloat) exp(t[3] * log(t[1])) : 0.0F;
+               lit[3] = 1.0;
+               store_vector4( &inst->DstReg, state, lit );
+            }
+            break;
+         case VP_OPCODE_RCP:
+            {
+               GLfloat t[4];
+               fetch_vector1( &inst->SrcReg[0], state, t );
+               if (t[0] != 1.0F)
+                  t[0] = 1.0F / t[0];  /* div by zero is infinity! */
+               t[1] = t[2] = t[3] = t[0];
+               store_vector4( &inst->DstReg, state, t );
+            }
+            break;
+         case VP_OPCODE_RSQ:
+            {
+               GLfloat t[4];
+               fetch_vector1( &inst->SrcReg[0], state, t );
+               t[0] = INV_SQRTF(FABSF(t[0]));
+               t[1] = t[2] = t[3] = t[0];
+               store_vector4( &inst->DstReg, state, t );
+            }
+            break;
+         case VP_OPCODE_EXP:
+            {
+               GLfloat t[4], q[4], floor_t0;
+               fetch_vector1( &inst->SrcReg[0], state, t );
+               floor_t0 = (float) floor(t[0]);
+               if (floor_t0 > FLT_MAX_EXP) {
+                  SET_POS_INFINITY(q[0]);
+                  SET_POS_INFINITY(q[2]);
+               }
+               else if (floor_t0 < FLT_MIN_EXP) {
+                  q[0] = 0.0F;
+                  q[2] = 0.0F;
+               }
+               else {
+#ifdef USE_IEEE
+                  GLint ii = (GLint) floor_t0;
+                  ii = (ii < 23) + 0x3f800000;
+                  SET_FLOAT_BITS(q[0], ii);
+                  q[0] = *((GLfloat *) &ii);
+#else
+                  q[0] = (GLfloat) pow(2.0, floor_t0);
+#endif
+                  q[2] = (GLfloat) (q[0] * LOG2(q[1]));
+               }
+               q[1] = t[0] - floor_t0;
+               q[3] = 1.0F;
+               store_vector4( &inst->DstReg, state, q );
+            }
+            break;
+         case VP_OPCODE_LOG:
+            {
+               GLfloat t[4], q[4], abs_t0;
+               fetch_vector1( &inst->SrcReg[0], state, t );
+               abs_t0 = (GLfloat) fabs(t[0]);
+               if (abs_t0 != 0.0F) {
+                  /* Since we really can't handle infinite values on VMS
+                   * like other OSes we'll use __MAXFLOAT to represent
+                   * infinity.  This may need some tweaking.
+                   */
+#ifdef VMS
+                  if (abs_t0 == __MAXFLOAT)
+#else
+                  if (IS_INF_OR_NAN(abs_t0))
+#endif
+                  {
+                     SET_POS_INFINITY(q[0]);
+                     q[1] = 1.0F;
+                     SET_POS_INFINITY(q[2]);
+                  }
+                  else {
+                     int exponent;
+                     double mantissa = frexp(t[0], &exponent);
+                     q[0] = (GLfloat) (exponent - 1);
+                     q[1] = (GLfloat) (2.0 * mantissa); /* map [.5, 1) -> [1, 2) */
+                     q[2] = (GLfloat) (q[0] + LOG2(q[1]));
+                  }
+                  }
+               else {
+                  SET_NEG_INFINITY(q[0]);
+                  q[1] = 1.0F;
+                  SET_NEG_INFINITY(q[2]);
+               }
+               q[3] = 1.0;
+               store_vector4( &inst->DstReg, state, q );
+            }
+            break;
+         case VP_OPCODE_MUL:
+            {
+               GLfloat t[4], u[4], prod[4];
+               fetch_vector4( &inst->SrcReg[0], state, t );
+               fetch_vector4( &inst->SrcReg[1], state, u );
+               prod[0] = t[0] * u[0];
+               prod[1] = t[1] * u[1];
+               prod[2] = t[2] * u[2];
+               prod[3] = t[3] * u[3];
+               store_vector4( &inst->DstReg, state, prod );
+            }
+            break;
+         case VP_OPCODE_ADD:
+            {
+               GLfloat t[4], u[4], sum[4];
+               fetch_vector4( &inst->SrcReg[0], state, t );
+               fetch_vector4( &inst->SrcReg[1], state, u );
+               sum[0] = t[0] + u[0];
+               sum[1] = t[1] + u[1];
+               sum[2] = t[2] + u[2];
+               sum[3] = t[3] + u[3];
+               store_vector4( &inst->DstReg, state, sum );
+            }
+            break;
+         case VP_OPCODE_DP3:
+            {
+               GLfloat t[4], u[4], dot[4];
+               fetch_vector4( &inst->SrcReg[0], state, t );
+               fetch_vector4( &inst->SrcReg[1], state, u );
+               dot[0] = t[0] * u[0] + t[1] * u[1] + t[2] * u[2];
+               dot[1] = dot[2] = dot[3] = dot[0];
+               store_vector4( &inst->DstReg, state, dot );
+            }
+            break;
+         case VP_OPCODE_DP4:
+            {
+               GLfloat t[4], u[4], dot[4];
+               fetch_vector4( &inst->SrcReg[0], state, t );
+               fetch_vector4( &inst->SrcReg[1], state, u );
+               dot[0] = t[0] * u[0] + t[1] * u[1] + t[2] * u[2] + t[3] * u[3];
+               dot[1] = dot[2] = dot[3] = dot[0];
+               store_vector4( &inst->DstReg, state, dot );
+            }
+            break;
+         case VP_OPCODE_DST:
+            {
+               GLfloat t[4], u[4], dst[4];
+               fetch_vector4( &inst->SrcReg[0], state, t );
+               fetch_vector4( &inst->SrcReg[1], state, u );
+               dst[0] = 1.0F;
+               dst[1] = t[1] * u[1];
+               dst[2] = t[2];
+               dst[3] = u[3];
+               store_vector4( &inst->DstReg, state, dst );
+            }
+            break;
+         case VP_OPCODE_MIN:
+            {
+               GLfloat t[4], u[4], min[4];
+               fetch_vector4( &inst->SrcReg[0], state, t );
+               fetch_vector4( &inst->SrcReg[1], state, u );
+               min[0] = (t[0] < u[0]) ? t[0] : u[0];
+               min[1] = (t[1] < u[1]) ? t[1] : u[1];
+               min[2] = (t[2] < u[2]) ? t[2] : u[2];
+               min[3] = (t[3] < u[3]) ? t[3] : u[3];
+               store_vector4( &inst->DstReg, state, min );
+            }
+            break;
+         case VP_OPCODE_MAX:
+            {
+               GLfloat t[4], u[4], max[4];
+               fetch_vector4( &inst->SrcReg[0], state, t );
+               fetch_vector4( &inst->SrcReg[1], state, u );
+               max[0] = (t[0] > u[0]) ? t[0] : u[0];
+               max[1] = (t[1] > u[1]) ? t[1] : u[1];
+               max[2] = (t[2] > u[2]) ? t[2] : u[2];
+               max[3] = (t[3] > u[3]) ? t[3] : u[3];
+               store_vector4( &inst->DstReg, state, max );
+            }
+            break;
+         case VP_OPCODE_SLT:
+            {
+               GLfloat t[4], u[4], slt[4];
+               fetch_vector4( &inst->SrcReg[0], state, t );
+               fetch_vector4( &inst->SrcReg[1], state, u );
+               slt[0] = (t[0] < u[0]) ? 1.0F : 0.0F;
+               slt[1] = (t[1] < u[1]) ? 1.0F : 0.0F;
+               slt[2] = (t[2] < u[2]) ? 1.0F : 0.0F;
+               slt[3] = (t[3] < u[3]) ? 1.0F : 0.0F;
+               store_vector4( &inst->DstReg, state, slt );
+            }
+            break;
+         case VP_OPCODE_SGE:
+            {
+               GLfloat t[4], u[4], sge[4];
+               fetch_vector4( &inst->SrcReg[0], state, t );
+               fetch_vector4( &inst->SrcReg[1], state, u );
+               sge[0] = (t[0] >= u[0]) ? 1.0F : 0.0F;
+               sge[1] = (t[1] >= u[1]) ? 1.0F : 0.0F;
+               sge[2] = (t[2] >= u[2]) ? 1.0F : 0.0F;
+               sge[3] = (t[3] >= u[3]) ? 1.0F : 0.0F;
+               store_vector4( &inst->DstReg, state, sge );
+            }
+            break;
+         case VP_OPCODE_MAD:
+            {
+               GLfloat t[4], u[4], v[4], sum[4];
+               fetch_vector4( &inst->SrcReg[0], state, t );
+               fetch_vector4( &inst->SrcReg[1], state, u );
+               fetch_vector4( &inst->SrcReg[2], state, v );
+               sum[0] = t[0] * u[0] + v[0];
+               sum[1] = t[1] * u[1] + v[1];
+               sum[2] = t[2] * u[2] + v[2];
+               sum[3] = t[3] * u[3] + v[3];
+               store_vector4( &inst->DstReg, state, sum );
+            }
+            break;
+         case VP_OPCODE_ARL:
+            {
+               GLfloat t[4];
+               fetch_vector4( &inst->SrcReg[0], state, t );
+               state->AddressReg[0] = (GLint) floor(t[0]);
+            }
+            break;
+         case VP_OPCODE_DPH:
+            {
+               GLfloat t[4], u[4], dot[4];
+               fetch_vector4( &inst->SrcReg[0], state, t );
+               fetch_vector4( &inst->SrcReg[1], state, u );
+               dot[0] = t[0] * u[0] + t[1] * u[1] + t[2] * u[2] + u[3];
+               dot[1] = dot[2] = dot[3] = dot[0];
+               store_vector4( &inst->DstReg, state, dot );
+            }
+            break;
+         case VP_OPCODE_RCC:
+            {
+               GLfloat t[4], u;
+               fetch_vector1( &inst->SrcReg[0], state, t );
+               if (t[0] == 1.0F)
+                  u = 1.0F;
+               else
+                  u = 1.0F / t[0];
+               if (u > 0.0F) {
+                  if (u > 1.884467e+019F) {
+                     u = 1.884467e+019F;  /* IEEE 32-bit binary value 0x5F800000 */
+                  }
+                  else if (u < 5.42101e-020F) {
+                     u = 5.42101e-020F;   /* IEEE 32-bit binary value 0x1F800000 */
+                  }
+               }
+               else {
+                  if (u < -1.884467e+019F) {
+                     u = -1.884467e+019F; /* IEEE 32-bit binary value 0xDF800000 */
+                  }
+                  else if (u > -5.42101e-020F) {
+                     u = -5.42101e-020F;  /* IEEE 32-bit binary value 0x9F800000 */
+                  }
+               }
+               t[0] = t[1] = t[2] = t[3] = u;
+               store_vector4( &inst->DstReg, state, t );
+            }
+            break;
+         case VP_OPCODE_SUB: /* GL_NV_vertex_program1_1 */
+            {
+               GLfloat t[4], u[4], sum[4];
+               fetch_vector4( &inst->SrcReg[0], state, t );
+               fetch_vector4( &inst->SrcReg[1], state, u );
+               sum[0] = t[0] - u[0];
+               sum[1] = t[1] - u[1];
+               sum[2] = t[2] - u[2];
+               sum[3] = t[3] - u[3];
+               store_vector4( &inst->DstReg, state, sum );
+            }
+            break;
+         case VP_OPCODE_ABS: /* GL_NV_vertex_program1_1 */
+            {
+               GLfloat t[4];
+               fetch_vector4( &inst->SrcReg[0], state, t );
+               if (t[0] < 0.0)  t[0] = -t[0];
+               if (t[1] < 0.0)  t[1] = -t[1];
+               if (t[2] < 0.0)  t[2] = -t[2];
+               if (t[3] < 0.0)  t[3] = -t[3];
+               store_vector4( &inst->DstReg, state, t );
+            }
+            break;
+         case VP_OPCODE_FLR: /* GL_ARB_vertex_program */
+            {
+               GLfloat t[4];
+               fetch_vector4( &inst->SrcReg[0], state, t );
+               t[0] = FLOORF(t[0]);
+               t[1] = FLOORF(t[1]);
+               t[2] = FLOORF(t[2]);
+               t[3] = FLOORF(t[3]);
+               store_vector4( &inst->DstReg, state, t );
+            }
+            break;
+         case VP_OPCODE_FRC: /* GL_ARB_vertex_program */
+            {
+               GLfloat t[4];
+               fetch_vector4( &inst->SrcReg[0], state, t );
+               t[0] = t[0] - FLOORF(t[0]);
+               t[1] = t[1] - FLOORF(t[1]);
+               t[2] = t[2] - FLOORF(t[2]);
+               t[3] = t[3] - FLOORF(t[3]);
+               store_vector4( &inst->DstReg, state, t );
+            }
+            break;
+         case VP_OPCODE_EX2: /* GL_ARB_vertex_program */
+            {
+               GLfloat t[4];
+               fetch_vector1( &inst->SrcReg[0], state, t );
+               t[0] = t[1] = t[2] = t[3] = (GLfloat)_mesa_pow(2.0, t[0]);
+               store_vector4( &inst->DstReg, state, t );
+            }
+            break;
+         case VP_OPCODE_LG2: /* GL_ARB_vertex_program */
+            {
+               GLfloat t[4];
+               fetch_vector1( &inst->SrcReg[0], state, t );
+               t[0] = t[1] = t[2] = t[3] = LOG2(t[0]);
+               store_vector4( &inst->DstReg, state, t );
+            }
+            break;
+         case VP_OPCODE_POW: /* GL_ARB_vertex_program */
+            {
+               GLfloat t[4], u[4];
+               fetch_vector1( &inst->SrcReg[0], state, t );
+               fetch_vector1( &inst->SrcReg[1], state, u );
+               t[0] = t[1] = t[2] = t[3] = (GLfloat)_mesa_pow(t[0], u[0]);
+               store_vector4( &inst->DstReg, state, t );
+            }
+            break;
+         case VP_OPCODE_XPD: /* GL_ARB_vertex_program */
+            {
+               GLfloat t[4], u[4], cross[4];
+               fetch_vector4( &inst->SrcReg[0], state, t );
+               fetch_vector4( &inst->SrcReg[1], state, u );
+               cross[0] = t[1] * u[2] - t[2] * u[1];
+               cross[1] = t[2] * u[0] - t[0] * u[2];
+               cross[2] = t[0] * u[1] - t[1] * u[0];
+               store_vector4( &inst->DstReg, state, cross );
+            }
+            break;
+         case VP_OPCODE_SWZ: /* GL_ARB_vertex_program */
+            {
+               const struct vp_src_register *source = &inst->SrcReg[0];
+               const GLfloat *src = get_register_pointer(source, state);
+               GLfloat result[4];
+               GLuint i;
+
+               /* do extended swizzling here */
+               for (i = 0; i < 3; i++) {
+                  if (source->Swizzle[i] == SWIZZLE_ZERO)
+                     result[i] = 0.0;
+                  else if (source->Swizzle[i] == SWIZZLE_ONE)
+                     result[i] = -1.0;
+                  else
+                     result[i] = -src[source->Swizzle[i]];
+                  if (source->Negate)
+                     result[i] = -result[i];
+               }
+               store_vector4( &inst->DstReg, state, result );
+            }
+            break;
+
+         case VP_OPCODE_END:
+            ctx->_CurrentProgram = 0;
+            return;
+         default:
+            /* bad instruction opcode */
+            _mesa_problem(ctx, "Bad VP Opcode in _mesa_exec_vertex_program");
+            ctx->_CurrentProgram = 0;
+            return;
+      } /* switch */
+   } /* for */
+
+   ctx->_CurrentProgram = 0;
+}
+
+
+
+/**
+Thoughts on vertex program optimization:
+
+The obvious thing to do is to compile the vertex program into X86/SSE/3DNow!
+assembly code.  That will probably be a lot of work.
+
+Another approach might be to replace the vp_instruction->Opcode field with
+a pointer to a specialized C function which executes the instruction.
+In particular we can write functions which skip swizzling, negating,
+masking, relative addressing, etc. when they're not needed.
+
+For example:
+
+void simple_add( struct vp_instruction *inst )
+{
+   GLfloat *sum = machine->Registers[inst->DstReg.Register];
+   GLfloat *a = machine->Registers[inst->SrcReg[0].Register];
+   GLfloat *b = machine->Registers[inst->SrcReg[1].Register];
+   sum[0] = a[0] + b[0];
+   sum[1] = a[1] + b[1];
+   sum[2] = a[2] + b[2];
+   sum[3] = a[3] + b[3];
+}
+
+*/
+
+/*
+
+KW:
+
+A first step would be to 'vectorize' the programs in the same way as
+the normal transformation code in the tnl module.  Thus each opcode
+takes zero or more input vectors (registers) and produces one or more
+output vectors.
+
+These operations would intially be coded in C, with machine-specific
+assembly following, as is currently the case for matrix
+transformations in the math/ directory.  The preprocessing scheme for
+selecting simpler operations Brian describes above would also work
+here.
+
+This should give reasonable performance without excessive effort.
+
+*/
diff --git a/src/mesa/shader/nvvertexec.h b/src/mesa/shader/nvvertexec.h
new file mode 100644 (file)
index 0000000..0e4b60e
--- /dev/null
@@ -0,0 +1,43 @@
+/*
+ * Mesa 3-D graphics library
+ * Version:  5.1
+ *
+ * Copyright (C) 1999-2003  Brian Paul   All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * BRIAN PAUL BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors:
+ *    Brian Paul
+ */
+
+#ifndef NVVERTEXEC_H
+#define NVVERTEXEC_H
+
+extern void
+_mesa_init_vp_registers(GLcontext *ctx);
+
+extern void
+_mesa_init_tracked_matrices(GLcontext *ctx);
+
+extern void
+_mesa_exec_vertex_program(GLcontext *ctx, const struct vertex_program *program);
+
+extern void
+_mesa_dump_vp_state( const struct vertex_program_state *state );
+
+#endif
diff --git a/src/mesa/shader/nvvertparse.c b/src/mesa/shader/nvvertparse.c
new file mode 100644 (file)
index 0000000..bacf5a5
--- /dev/null
@@ -0,0 +1,1496 @@
+/*
+ * Mesa 3-D graphics library
+ * Version:  6.0.1
+ *
+ * Copyright (C) 1999-2004  Brian Paul   All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * BRIAN PAUL BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+/**
+ * \file nvvertparse.c
+ * NVIDIA vertex program parser.
+ * \author Brian Paul
+ */
+
+/*
+ * Regarding GL_NV_vertex_program, GL_NV_vertex_program1_1:
+ *
+ * Portions of this software may use or implement intellectual
+ * property owned and licensed by NVIDIA Corporation. NVIDIA disclaims
+ * any and all warranties with respect to such intellectual property,
+ * including any use thereof or modifications thereto.
+ */
+
+#include "glheader.h"
+#include "context.h"
+#include "hash.h"
+#include "imports.h"
+#include "macros.h"
+#include "mtypes.h"
+#include "nvprogram.h"
+#include "nvvertparse.h"
+#include "nvvertprog.h"
+#include "program.h"
+
+
+/**
+ * Current parsing state.  This structure is passed among the parsing
+ * functions and keeps track of the current parser position and various
+ * program attributes.
+ */
+struct parse_state {
+   GLcontext *ctx;
+   const GLubyte *start;
+   const GLubyte *pos;
+   const GLubyte *curLine;
+   GLboolean isStateProgram;
+   GLboolean isPositionInvariant;
+   GLboolean isVersion1_1;
+   GLuint inputsRead;
+   GLuint outputsWritten;
+   GLboolean anyProgRegsWritten;
+   GLuint numInst;                 /* number of instructions parsed */
+};
+
+
+/*
+ * Called whenever we find an error during parsing.
+ */
+static void
+record_error(struct parse_state *parseState, const char *msg, int lineNo)
+{
+#ifdef DEBUG
+   GLint line, column;
+   const GLubyte *lineStr;
+   lineStr = _mesa_find_line_column(parseState->start,
+                                    parseState->pos, &line, &column);
+   _mesa_debug(parseState->ctx,
+               "nvfragparse.c(%d): line %d, column %d:%s (%s)\n",
+               lineNo, line, column, (char *) lineStr, msg);
+   _mesa_free((void *) lineStr);
+#else
+   (void) lineNo;
+#endif
+
+   /* Check that no error was already recorded.  Only record the first one. */
+   if (parseState->ctx->Program.ErrorString[0] == 0) {
+      _mesa_set_program_error(parseState->ctx,
+                              parseState->pos - parseState->start,
+                              msg);
+   }
+}
+
+
+#define RETURN_ERROR                                                   \
+do {                                                                   \
+   record_error(parseState, "Unexpected end of input.", __LINE__);     \
+   return GL_FALSE;                                                    \
+} while(0)
+
+#define RETURN_ERROR1(msg)                                             \
+do {                                                                   \
+   record_error(parseState, msg, __LINE__);                            \
+   return GL_FALSE;                                                    \
+} while(0)
+
+#define RETURN_ERROR2(msg1, msg2)                                      \
+do {                                                                   \
+   char err[1000];                                                     \
+   _mesa_sprintf(err, "%s %s", msg1, msg2);                            \
+   record_error(parseState, err, __LINE__);                            \
+   return GL_FALSE;                                                    \
+} while(0)
+
+
+
+
+
+static GLboolean IsLetter(GLubyte b)
+{
+   return (b >= 'a' && b <= 'z') || (b >= 'A' && b <= 'Z');
+}
+
+
+static GLboolean IsDigit(GLubyte b)
+{
+   return b >= '0' && b <= '9';
+}
+
+
+static GLboolean IsWhitespace(GLubyte b)
+{
+   return b == ' ' || b == '\t' || b == '\n' || b == '\r';
+}
+
+
+/**
+ * Starting at 'str' find the next token.  A token can be an integer,
+ * an identifier or punctuation symbol.
+ * \return <= 0 we found an error, else, return number of characters parsed.
+ */
+static GLint
+GetToken(struct parse_state *parseState, GLubyte *token)
+{
+   const GLubyte *str = parseState->pos;
+   GLint i = 0, j = 0;
+
+   token[0] = 0;
+
+   /* skip whitespace and comments */
+   while (str[i] && (IsWhitespace(str[i]) || str[i] == '#')) {
+      if (str[i] == '#') {
+         /* skip comment */
+         while (str[i] && (str[i] != '\n' && str[i] != '\r')) {
+            i++;
+         }
+         if (str[i] == '\n' || str[i] == '\r')
+            parseState->curLine = str + i + 1;
+      }
+      else {
+         /* skip whitespace */
+         if (str[i] == '\n' || str[i] == '\r')
+            parseState->curLine = str + i + 1;
+         i++;
+      }
+   }
+
+   if (str[i] == 0)
+      return -i;
+
+   /* try matching an integer */
+   while (str[i] && IsDigit(str[i])) {
+      token[j++] = str[i++];
+   }
+   if (j > 0 || !str[i]) {
+      token[j] = 0;
+      return i;
+   }
+
+   /* try matching an identifier */
+   if (IsLetter(str[i])) {
+      while (str[i] && (IsLetter(str[i]) || IsDigit(str[i]))) {
+         token[j++] = str[i++];
+      }
+      token[j] = 0;
+      return i;
+   }
+
+   /* punctuation character */
+   if (str[i]) {
+      token[0] = str[i++];
+      token[1] = 0;
+      return i;
+   }
+
+   /* end of input */
+   token[0] = 0;
+   return i;
+}
+
+
+/**
+ * Get next token from input stream and increment stream pointer past token.
+ */
+static GLboolean
+Parse_Token(struct parse_state *parseState, GLubyte *token)
+{
+   GLint i;
+   i = GetToken(parseState, token);
+   if (i <= 0) {
+      parseState->pos += (-i);
+      return GL_FALSE;
+   }
+   parseState->pos += i;
+   return GL_TRUE;
+}
+
+
+/**
+ * Get next token from input stream but don't increment stream pointer.
+ */
+static GLboolean
+Peek_Token(struct parse_state *parseState, GLubyte *token)
+{
+   GLint i, len;
+   i = GetToken(parseState, token);
+   if (i <= 0) {
+      parseState->pos += (-i);
+      return GL_FALSE;
+   }
+   len = _mesa_strlen((const char *) token);
+   parseState->pos += (i - len);
+   return GL_TRUE;
+}
+
+
+/**
+ * Try to match 'pattern' as the next token after any whitespace/comments.
+ * Advance the current parsing position only if we match the pattern.
+ * \return GL_TRUE if pattern is matched, GL_FALSE otherwise.
+ */
+static GLboolean
+Parse_String(struct parse_state *parseState, const char *pattern)
+{
+   const GLubyte *m;
+   GLint i;
+
+   /* skip whitespace and comments */
+   while (IsWhitespace(*parseState->pos) || *parseState->pos == '#') {
+      if (*parseState->pos == '#') {
+         while (*parseState->pos && (*parseState->pos != '\n' && *parseState->pos != '\r')) {
+            parseState->pos += 1;
+         }
+         if (*parseState->pos == '\n' || *parseState->pos == '\r')
+            parseState->curLine = parseState->pos + 1;
+      }
+      else {
+         /* skip whitespace */
+         if (*parseState->pos == '\n' || *parseState->pos == '\r')
+            parseState->curLine = parseState->pos + 1;
+         parseState->pos += 1;
+      }
+   }
+
+   /* Try to match the pattern */
+   m = parseState->pos;
+   for (i = 0; pattern[i]; i++) {
+      if (*m != (GLubyte) pattern[i])
+         return GL_FALSE;
+      m += 1;
+   }
+   parseState->pos = m;
+
+   return GL_TRUE; /* success */
+}
+
+
+/**********************************************************************/
+
+static const char *InputRegisters[MAX_NV_VERTEX_PROGRAM_INPUTS + 1] = {
+   "OPOS", "WGHT", "NRML", "COL0", "COL1", "FOGC", "6", "7",
+   "TEX0", "TEX1", "TEX2", "TEX3", "TEX4", "TEX5", "TEX6", "TEX7", NULL
+};
+
+static const char *OutputRegisters[MAX_NV_VERTEX_PROGRAM_OUTPUTS + 1] = {
+   "HPOS", "COL0", "COL1", "BFC0", "BFC1", "FOGC", "PSIZ",
+   "TEX0", "TEX1", "TEX2", "TEX3", "TEX4", "TEX5", "TEX6", "TEX7", NULL
+};
+
+/* NOTE: the order here must match opcodes in nvvertprog.h */
+static const char *Opcodes[] = {
+   "MOV", "LIT", "RCP", "RSQ", "EXP", "LOG", "MUL", "ADD", "DP3", "DP4",
+   "DST", "MIN", "MAX", "SLT", "SGE", "MAD", "ARL", "DPH", "RCC", "SUB",
+   "ABS", "END",
+   /* GL_ARB_vertex_program */
+   "FLR", "FRC", "EX2", "LG2", "POW", "XPD", "SWZ",
+   NULL
+};
+
+
+
+/**
+ * Parse a temporary register: Rnn
+ */
+static GLboolean
+Parse_TempReg(struct parse_state *parseState, GLint *tempRegNum)
+{
+   GLubyte token[100];
+
+   /* Should be 'R##' */
+   if (!Parse_Token(parseState, token))
+      RETURN_ERROR;
+   if (token[0] != 'R')
+      RETURN_ERROR1("Expected R##");
+
+   if (IsDigit(token[1])) {
+      GLint reg = _mesa_atoi((char *) (token + 1));
+      if (reg >= MAX_NV_VERTEX_PROGRAM_TEMPS)
+         RETURN_ERROR1("Bad temporary register name");
+      *tempRegNum = reg;
+   }
+   else {
+      RETURN_ERROR1("Bad temporary register name");
+   }
+
+   return GL_TRUE;
+}
+
+
+/**
+ * Parse address register "A0.x"
+ */
+static GLboolean
+Parse_AddrReg(struct parse_state *parseState)
+{
+   /* match 'A0' */
+   if (!Parse_String(parseState, "A0"))
+      RETURN_ERROR;
+
+   /* match '.' */
+   if (!Parse_String(parseState, "."))
+      RETURN_ERROR;
+
+   /* match 'x' */
+   if (!Parse_String(parseState, "x"))
+      RETURN_ERROR;
+
+   return GL_TRUE;
+}
+
+
+/**
+ * Parse absolute program parameter register "c[##]"
+ */
+static GLboolean
+Parse_AbsParamReg(struct parse_state *parseState, GLint *regNum)
+{
+   GLubyte token[100];
+
+   if (!Parse_String(parseState, "c"))
+      RETURN_ERROR;
+
+   if (!Parse_String(parseState, "["))
+      RETURN_ERROR;
+
+   if (!Parse_Token(parseState, token))
+      RETURN_ERROR;
+
+   if (IsDigit(token[0])) {
+      /* a numbered program parameter register */
+      GLint reg = _mesa_atoi((char *) token);
+      if (reg >= MAX_NV_VERTEX_PROGRAM_PARAMS)
+         RETURN_ERROR1("Bad program parameter number");
+      *regNum = reg;
+   }
+   else {
+      RETURN_ERROR;
+   }
+
+   if (!Parse_String(parseState, "]"))
+      RETURN_ERROR;
+
+   return GL_TRUE;
+}
+
+
+static GLboolean
+Parse_ParamReg(struct parse_state *parseState, struct vp_src_register *srcReg)
+{
+   GLubyte token[100];
+
+   if (!Parse_String(parseState, "c"))
+      RETURN_ERROR;
+
+   if (!Parse_String(parseState, "["))
+      RETURN_ERROR;
+
+   if (!Peek_Token(parseState, token))
+      RETURN_ERROR;
+
+   if (IsDigit(token[0])) {
+      /* a numbered program parameter register */
+      GLint reg;
+      (void) Parse_Token(parseState, token);
+      reg = _mesa_atoi((char *) token);
+      if (reg >= MAX_NV_VERTEX_PROGRAM_PARAMS)
+         RETURN_ERROR1("Bad program parameter number");
+      srcReg->File = PROGRAM_ENV_PARAM;
+      srcReg->Index = reg;
+   }
+   else if (_mesa_strcmp((const char *) token, "A0") == 0) {
+      /* address register "A0.x" */
+      if (!Parse_AddrReg(parseState))
+         RETURN_ERROR;
+
+      srcReg->RelAddr = GL_TRUE;
+      srcReg->File = PROGRAM_ENV_PARAM;
+      /* Look for +/-N offset */
+      if (!Peek_Token(parseState, token))
+         RETURN_ERROR;
+
+      if (token[0] == '-' || token[0] == '+') {
+         const GLubyte sign = token[0];
+         (void) Parse_Token(parseState, token); /* consume +/- */
+
+         /* an integer should be next */
+         if (!Parse_Token(parseState, token))
+            RETURN_ERROR;
+
+         if (IsDigit(token[0])) {
+            const GLint k = _mesa_atoi((char *) token);
+            if (sign == '-') {
+               if (k > 64)
+                  RETURN_ERROR1("Bad address offset");
+               srcReg->Index = -k;
+            }
+            else {
+               if (k > 63)
+                  RETURN_ERROR1("Bad address offset");
+               srcReg->Index = k;
+            }
+         }
+         else {
+            RETURN_ERROR;
+         }
+      }
+      else {
+         /* probably got a ']', catch it below */
+      }
+   }
+   else {
+      RETURN_ERROR;
+   }
+
+   /* Match closing ']' */
+   if (!Parse_String(parseState, "]"))
+      RETURN_ERROR;
+
+   return GL_TRUE;
+}
+
+
+/**
+ * Parse v[#] or v[<name>]
+ */
+static GLboolean
+Parse_AttribReg(struct parse_state *parseState, GLint *tempRegNum)
+{
+   GLubyte token[100];
+   GLint j;
+
+   /* Match 'v' */
+   if (!Parse_String(parseState, "v"))
+      RETURN_ERROR;
+
+   /* Match '[' */
+   if (!Parse_String(parseState, "["))
+      RETURN_ERROR;
+
+   /* match number or named register */
+   if (!Parse_Token(parseState, token))
+      RETURN_ERROR;
+
+   if (parseState->isStateProgram && token[0] != '0')
+      RETURN_ERROR1("Only v[0] accessible in vertex state programs");
+
+   if (IsDigit(token[0])) {
+      GLint reg = _mesa_atoi((char *) token);
+      if (reg >= MAX_NV_VERTEX_PROGRAM_INPUTS)
+         RETURN_ERROR1("Bad vertex attribute register name");
+      *tempRegNum = reg;
+   }
+   else {
+      for (j = 0; InputRegisters[j]; j++) {
+         if (_mesa_strcmp((const char *) token, InputRegisters[j]) == 0) {
+            *tempRegNum = j;
+            break;
+         }
+      }
+      if (!InputRegisters[j]) {
+         /* unknown input register label */
+         RETURN_ERROR2("Bad register name", token);
+      }
+   }
+
+   /* Match '[' */
+   if (!Parse_String(parseState, "]"))
+      RETURN_ERROR;
+
+   return GL_TRUE;
+}
+
+
+static GLboolean
+Parse_OutputReg(struct parse_state *parseState, GLint *outputRegNum)
+{
+   GLubyte token[100];
+   GLint start, j;
+
+   /* Match 'o' */
+   if (!Parse_String(parseState, "o"))
+      RETURN_ERROR;
+
+   /* Match '[' */
+   if (!Parse_String(parseState, "["))
+      RETURN_ERROR;
+
+   /* Get output reg name */
+   if (!Parse_Token(parseState, token))
+      RETURN_ERROR;
+
+   if (parseState->isPositionInvariant)
+      start = 1; /* skip HPOS register name */
+   else
+      start = 0;
+
+   /* try to match an output register name */
+   for (j = start; OutputRegisters[j]; j++) {
+      if (_mesa_strcmp((const char *) token, OutputRegisters[j]) == 0) {
+         *outputRegNum = j;
+         break;
+      }
+   }
+   if (!OutputRegisters[j])
+      RETURN_ERROR1("Unrecognized output register name");
+
+   /* Match ']' */
+   if (!Parse_String(parseState, "]"))
+      RETURN_ERROR1("Expected ]");
+
+   return GL_TRUE;
+}
+
+
+static GLboolean
+Parse_MaskedDstReg(struct parse_state *parseState, struct vp_dst_register *dstReg)
+{
+   GLubyte token[100];
+
+   /* Dst reg can be R<n> or o[n] */
+   if (!Peek_Token(parseState, token))
+      RETURN_ERROR;
+
+   if (token[0] == 'R') {
+      /* a temporary register */
+      dstReg->File = PROGRAM_TEMPORARY;
+      if (!Parse_TempReg(parseState, &dstReg->Index))
+         RETURN_ERROR;
+   }
+   else if (!parseState->isStateProgram && token[0] == 'o') {
+      /* an output register */
+      dstReg->File = PROGRAM_OUTPUT;
+      if (!Parse_OutputReg(parseState, &dstReg->Index))
+         RETURN_ERROR;
+   }
+   else if (parseState->isStateProgram && token[0] == 'c' &&
+            parseState->isStateProgram) {
+      /* absolute program parameter register */
+      /* Only valid for vertex state programs */
+      dstReg->File = PROGRAM_ENV_PARAM;
+      if (!Parse_AbsParamReg(parseState, &dstReg->Index))
+         RETURN_ERROR;
+   }
+   else {
+      RETURN_ERROR1("Bad destination register name");
+   }
+
+   /* Parse optional write mask */
+   if (!Peek_Token(parseState, token))
+      RETURN_ERROR;
+
+   if (token[0] == '.') {
+      /* got a mask */
+      GLint k = 0;
+
+      if (!Parse_String(parseState, "."))
+         RETURN_ERROR;
+
+      if (!Parse_Token(parseState, token))
+         RETURN_ERROR;
+
+      dstReg->WriteMask[0] = GL_FALSE;
+      dstReg->WriteMask[1] = GL_FALSE;
+      dstReg->WriteMask[2] = GL_FALSE;
+      dstReg->WriteMask[3] = GL_FALSE;
+
+      if (token[k] == 'x') {
+         dstReg->WriteMask[0] = GL_TRUE;
+         k++;
+      }
+      if (token[k] == 'y') {
+         dstReg->WriteMask[1] = GL_TRUE;
+         k++;
+      }
+      if (token[k] == 'z') {
+         dstReg->WriteMask[2] = GL_TRUE;
+         k++;
+      }
+      if (token[k] == 'w') {
+         dstReg->WriteMask[3] = GL_TRUE;
+         k++;
+      }
+      if (k == 0) {
+         RETURN_ERROR1("Bad writemask character");
+      }
+      return GL_TRUE;
+   }
+   else {
+      dstReg->WriteMask[0] = GL_TRUE;
+      dstReg->WriteMask[1] = GL_TRUE;
+      dstReg->WriteMask[2] = GL_TRUE;
+      dstReg->WriteMask[3] = GL_TRUE;
+      return GL_TRUE;
+   }
+}
+
+
+static GLboolean
+Parse_SwizzleSrcReg(struct parse_state *parseState, struct vp_src_register *srcReg)
+{
+   GLubyte token[100];
+
+   srcReg->RelAddr = GL_FALSE;
+
+   /* check for '-' */
+   if (!Peek_Token(parseState, token))
+      RETURN_ERROR;
+   if (token[0] == '-') {
+      (void) Parse_String(parseState, "-");
+      srcReg->Negate = GL_TRUE;
+      if (!Peek_Token(parseState, token))
+         RETURN_ERROR;
+   }
+   else {
+      srcReg->Negate = GL_FALSE;
+   }
+
+   /* Src reg can be R<n>, c[n], c[n +/- offset], or a named vertex attrib */
+   if (token[0] == 'R') {
+      srcReg->File = PROGRAM_TEMPORARY;
+      if (!Parse_TempReg(parseState, &srcReg->Index))
+         RETURN_ERROR;
+   }
+   else if (token[0] == 'c') {
+      if (!Parse_ParamReg(parseState, srcReg))
+         RETURN_ERROR;
+   }
+   else if (token[0] == 'v') {
+      srcReg->File = PROGRAM_INPUT;
+      if (!Parse_AttribReg(parseState, &srcReg->Index))
+         RETURN_ERROR;
+   }
+   else {
+      RETURN_ERROR2("Bad source register name", token);
+   }
+
+   /* init swizzle fields */
+   srcReg->Swizzle[0] = 0;
+   srcReg->Swizzle[1] = 1;
+   srcReg->Swizzle[2] = 2;
+   srcReg->Swizzle[3] = 3;
+
+   /* Look for optional swizzle suffix */
+   if (!Peek_Token(parseState, token))
+      RETURN_ERROR;
+   if (token[0] == '.') {
+      (void) Parse_String(parseState, ".");  /* consume . */
+
+      if (!Parse_Token(parseState, token))
+         RETURN_ERROR;
+
+      if (token[1] == 0) {
+         /* single letter swizzle */
+         if (token[0] == 'x')
+            ASSIGN_4V(srcReg->Swizzle, 0, 0, 0, 0);
+         else if (token[0] == 'y')
+            ASSIGN_4V(srcReg->Swizzle, 1, 1, 1, 1);
+         else if (token[0] == 'z')
+            ASSIGN_4V(srcReg->Swizzle, 2, 2, 2, 2);
+         else if (token[0] == 'w')
+            ASSIGN_4V(srcReg->Swizzle, 3, 3, 3, 3);
+         else
+            RETURN_ERROR1("Expected x, y, z, or w");
+      }
+      else {
+         /* 2, 3 or 4-component swizzle */
+         GLint k;
+         for (k = 0; token[k] && k < 5; k++) {
+            if (token[k] == 'x')
+               srcReg->Swizzle[k] = 0;
+            else if (token[k] == 'y')
+               srcReg->Swizzle[k] = 1;
+            else if (token[k] == 'z')
+               srcReg->Swizzle[k] = 2;
+            else if (token[k] == 'w')
+               srcReg->Swizzle[k] = 3;
+            else
+               RETURN_ERROR;
+         }
+         if (k >= 5)
+            RETURN_ERROR;
+      }
+   }
+
+   return GL_TRUE;
+}
+
+
+static GLboolean
+Parse_ScalarSrcReg(struct parse_state *parseState, struct vp_src_register *srcReg)
+{
+   GLubyte token[100];
+
+   srcReg->RelAddr = GL_FALSE;
+
+   /* check for '-' */
+   if (!Peek_Token(parseState, token))
+      RETURN_ERROR;
+   if (token[0] == '-') {
+      srcReg->Negate = GL_TRUE;
+      (void) Parse_String(parseState, "-"); /* consume '-' */
+      if (!Peek_Token(parseState, token))
+         RETURN_ERROR;
+   }
+   else {
+      srcReg->Negate = GL_FALSE;
+   }
+
+   /* Src reg can be R<n>, c[n], c[n +/- offset], or a named vertex attrib */
+   if (token[0] == 'R') {
+      srcReg->File = PROGRAM_TEMPORARY;
+      if (!Parse_TempReg(parseState, &srcReg->Index))
+         RETURN_ERROR;
+   }
+   else if (token[0] == 'c') {
+      if (!Parse_ParamReg(parseState, srcReg))
+         RETURN_ERROR;
+   }
+   else if (token[0] == 'v') {
+      srcReg->File = PROGRAM_INPUT;
+      if (!Parse_AttribReg(parseState, &srcReg->Index))
+         RETURN_ERROR;
+   }
+   else {
+      RETURN_ERROR2("Bad source register name", token);
+   }
+
+   /* Look for .[xyzw] suffix */
+   if (!Parse_String(parseState, "."))
+      RETURN_ERROR;
+
+   if (!Parse_Token(parseState, token))
+      RETURN_ERROR;
+
+   if (token[0] == 'x' && token[1] == 0) {
+      srcReg->Swizzle[0] = 0;
+   }
+   else if (token[0] == 'y' && token[1] == 0) {
+      srcReg->Swizzle[0] = 1;
+   }
+   else if (token[0] == 'z' && token[1] == 0) {
+      srcReg->Swizzle[0] = 2;
+   }
+   else if (token[0] == 'w' && token[1] == 0) {
+      srcReg->Swizzle[0] = 3;
+   }
+   else {
+      RETURN_ERROR1("Bad scalar source suffix");
+   }
+   srcReg->Swizzle[1] = srcReg->Swizzle[2] = srcReg->Swizzle[3] = 0;
+
+   return GL_TRUE;
+}
+
+
+static GLint
+Parse_UnaryOpInstruction(struct parse_state *parseState,
+                         struct vp_instruction *inst, enum vp_opcode opcode)
+{
+   if (opcode == VP_OPCODE_ABS && !parseState->isVersion1_1)
+      RETURN_ERROR1("ABS illegal for vertex program 1.0");
+
+   inst->Opcode = opcode;
+   inst->StringPos = parseState->curLine - parseState->start;
+
+   /* dest reg */
+   if (!Parse_MaskedDstReg(parseState, &inst->DstReg))
+      RETURN_ERROR;
+
+   /* comma */
+   if (!Parse_String(parseState, ","))
+      RETURN_ERROR;
+
+   /* src arg */
+   if (!Parse_SwizzleSrcReg(parseState, &inst->SrcReg[0]))
+      RETURN_ERROR;
+
+   /* semicolon */
+   if (!Parse_String(parseState, ";"))
+      RETURN_ERROR;
+
+   return GL_TRUE;
+}
+
+
+static GLboolean
+Parse_BiOpInstruction(struct parse_state *parseState,
+                      struct vp_instruction *inst, enum vp_opcode opcode)
+{
+   if (opcode == VP_OPCODE_DPH && !parseState->isVersion1_1)
+      RETURN_ERROR1("DPH illegal for vertex program 1.0");
+   if (opcode == VP_OPCODE_SUB && !parseState->isVersion1_1)
+      RETURN_ERROR1("SUB illegal for vertex program 1.0");
+
+   inst->Opcode = opcode;
+   inst->StringPos = parseState->curLine - parseState->start;
+
+   /* dest reg */
+   if (!Parse_MaskedDstReg(parseState, &inst->DstReg))
+      RETURN_ERROR;
+
+   /* comma */
+   if (!Parse_String(parseState, ","))
+      RETURN_ERROR;
+
+   /* first src arg */
+   if (!Parse_SwizzleSrcReg(parseState, &inst->SrcReg[0]))
+      RETURN_ERROR;
+
+   /* comma */
+   if (!Parse_String(parseState, ","))
+      RETURN_ERROR;
+
+   /* second src arg */
+   if (!Parse_SwizzleSrcReg(parseState, &inst->SrcReg[1]))
+      RETURN_ERROR;
+
+   /* semicolon */
+   if (!Parse_String(parseState, ";"))
+      RETURN_ERROR;
+
+   /* make sure we don't reference more than one program parameter register */
+   if (inst->SrcReg[0].File == PROGRAM_ENV_PARAM &&
+       inst->SrcReg[1].File == PROGRAM_ENV_PARAM &&
+       inst->SrcReg[0].Index != inst->SrcReg[1].Index)
+      RETURN_ERROR1("Can't reference two program parameter registers");
+
+   /* make sure we don't reference more than one vertex attribute register */
+   if (inst->SrcReg[0].File == PROGRAM_INPUT &&
+       inst->SrcReg[1].File == PROGRAM_INPUT &&
+       inst->SrcReg[0].Index != inst->SrcReg[1].Index)
+      RETURN_ERROR1("Can't reference two vertex attribute registers");
+
+   return GL_TRUE;
+}
+
+
+static GLboolean
+Parse_TriOpInstruction(struct parse_state *parseState,
+                       struct vp_instruction *inst, enum vp_opcode opcode)
+{
+   inst->Opcode = opcode;
+   inst->StringPos = parseState->curLine - parseState->start;
+
+   /* dest reg */
+   if (!Parse_MaskedDstReg(parseState, &inst->DstReg))
+      RETURN_ERROR;
+
+   /* comma */
+   if (!Parse_String(parseState, ","))
+      RETURN_ERROR;
+
+   /* first src arg */
+   if (!Parse_SwizzleSrcReg(parseState, &inst->SrcReg[0]))
+      RETURN_ERROR;
+
+   /* comma */
+   if (!Parse_String(parseState, ","))
+      RETURN_ERROR;
+
+   /* second src arg */
+   if (!Parse_SwizzleSrcReg(parseState, &inst->SrcReg[1]))
+      RETURN_ERROR;
+
+   /* comma */
+   if (!Parse_String(parseState, ","))
+      RETURN_ERROR;
+
+   /* third src arg */
+   if (!Parse_SwizzleSrcReg(parseState, &inst->SrcReg[2]))
+      RETURN_ERROR;
+
+   /* semicolon */
+   if (!Parse_String(parseState, ";"))
+      RETURN_ERROR;
+
+   /* make sure we don't reference more than one program parameter register */
+   if ((inst->SrcReg[0].File == PROGRAM_ENV_PARAM &&
+        inst->SrcReg[1].File == PROGRAM_ENV_PARAM &&
+        inst->SrcReg[0].Index != inst->SrcReg[1].Index) ||
+       (inst->SrcReg[0].File == PROGRAM_ENV_PARAM &&
+        inst->SrcReg[2].File == PROGRAM_ENV_PARAM &&
+        inst->SrcReg[0].Index != inst->SrcReg[2].Index) ||
+       (inst->SrcReg[1].File == PROGRAM_ENV_PARAM &&
+        inst->SrcReg[2].File == PROGRAM_ENV_PARAM &&
+        inst->SrcReg[1].Index != inst->SrcReg[2].Index))
+      RETURN_ERROR1("Can only reference one program register");
+
+   /* make sure we don't reference more than one vertex attribute register */
+   if ((inst->SrcReg[0].File == PROGRAM_INPUT &&
+        inst->SrcReg[1].File == PROGRAM_INPUT &&
+        inst->SrcReg[0].Index != inst->SrcReg[1].Index) ||
+       (inst->SrcReg[0].File == PROGRAM_INPUT &&
+        inst->SrcReg[2].File == PROGRAM_INPUT &&
+        inst->SrcReg[0].Index != inst->SrcReg[2].Index) ||
+       (inst->SrcReg[1].File == PROGRAM_INPUT &&
+        inst->SrcReg[2].File == PROGRAM_INPUT &&
+        inst->SrcReg[1].Index != inst->SrcReg[2].Index))
+      RETURN_ERROR1("Can only reference one input register");
+
+   return GL_TRUE;
+}
+
+
+static GLboolean
+Parse_ScalarInstruction(struct parse_state *parseState,
+                        struct vp_instruction *inst, enum vp_opcode opcode)
+{
+   if (opcode == VP_OPCODE_RCC && !parseState->isVersion1_1)
+      RETURN_ERROR1("RCC illegal for vertex program 1.0");
+
+   inst->Opcode = opcode;
+   inst->StringPos = parseState->curLine - parseState->start;
+
+   /* dest reg */
+   if (!Parse_MaskedDstReg(parseState, &inst->DstReg))
+      RETURN_ERROR;
+
+   /* comma */
+   if (!Parse_String(parseState, ","))
+      RETURN_ERROR;
+
+   /* first src arg */
+   if (!Parse_ScalarSrcReg(parseState, &inst->SrcReg[0]))
+      RETURN_ERROR;
+
+   /* semicolon */
+   if (!Parse_String(parseState, ";"))
+      RETURN_ERROR;
+
+   return GL_TRUE;
+}
+
+
+static GLboolean
+Parse_AddressInstruction(struct parse_state *parseState, struct vp_instruction *inst)
+{
+   inst->Opcode = VP_OPCODE_ARL;
+   inst->StringPos = parseState->curLine - parseState->start;
+
+   /* dest A0 reg */
+   if (!Parse_AddrReg(parseState))
+      RETURN_ERROR;
+
+   /* comma */
+   if (!Parse_String(parseState, ","))
+      RETURN_ERROR;
+
+   /* parse src reg */
+   if (!Parse_ScalarSrcReg(parseState, &inst->SrcReg[0]))
+      RETURN_ERROR;
+
+   /* semicolon */
+   if (!Parse_String(parseState, ";"))
+      RETURN_ERROR;
+
+   return GL_TRUE;
+}
+
+
+static GLboolean
+Parse_EndInstruction(struct parse_state *parseState, struct vp_instruction *inst)
+{
+   GLubyte token[100];
+
+   inst->Opcode = VP_OPCODE_END;
+   inst->StringPos = parseState->curLine - parseState->start;
+
+   /* this should fail! */
+   if (Parse_Token(parseState, token))
+      RETURN_ERROR2("Unexpected token after END:", token);
+   else
+      return GL_TRUE;
+}
+
+
+static GLboolean
+Parse_OptionSequence(struct parse_state *parseState,
+                     struct vp_instruction program[])
+{
+   while (1) {
+      if (!Parse_String(parseState, "OPTION"))
+         return GL_TRUE;  /* ok, not an OPTION statement */
+      if (Parse_String(parseState, "NV_position_invariant")) {
+         parseState->isPositionInvariant = GL_TRUE;
+      }
+      else {
+         RETURN_ERROR1("unexpected OPTION statement");
+      }
+      if (!Parse_String(parseState, ";"))
+         return GL_FALSE;
+   }
+}
+
+
+static GLboolean
+Parse_InstructionSequence(struct parse_state *parseState,
+                          struct vp_instruction program[])
+{
+   while (1) {
+      struct vp_instruction *inst = program + parseState->numInst;
+
+      /* Initialize the instruction */
+      inst->SrcReg[0].File = (enum register_file) -1;
+      inst->SrcReg[1].File = (enum register_file) -1;
+      inst->SrcReg[2].File = (enum register_file) -1;
+      inst->DstReg.File = (enum register_file) -1;
+
+      if (Parse_String(parseState, "MOV")) {
+         if (!Parse_UnaryOpInstruction(parseState, inst, VP_OPCODE_MOV))
+            RETURN_ERROR;
+      }
+      else if (Parse_String(parseState, "LIT")) {
+         if (!Parse_UnaryOpInstruction(parseState, inst, VP_OPCODE_LIT))
+            RETURN_ERROR;
+      }
+      else if (Parse_String(parseState, "ABS")) {
+         if (!Parse_UnaryOpInstruction(parseState, inst, VP_OPCODE_ABS))
+            RETURN_ERROR;
+      }
+      else if (Parse_String(parseState, "MUL")) {
+         if (!Parse_BiOpInstruction(parseState, inst, VP_OPCODE_MUL))
+            RETURN_ERROR;
+      }
+      else if (Parse_String(parseState, "ADD")) {
+         if (!Parse_BiOpInstruction(parseState, inst, VP_OPCODE_ADD))
+            RETURN_ERROR;
+      }
+      else if (Parse_String(parseState, "DP3")) {
+         if (!Parse_BiOpInstruction(parseState, inst, VP_OPCODE_DP3))
+            RETURN_ERROR;
+      }
+      else if (Parse_String(parseState, "DP4")) {
+         if (!Parse_BiOpInstruction(parseState, inst, VP_OPCODE_DP4))
+            RETURN_ERROR;
+      }
+      else if (Parse_String(parseState, "DST")) {
+         if (!Parse_BiOpInstruction(parseState, inst, VP_OPCODE_DST))
+            RETURN_ERROR;
+      }
+      else if (Parse_String(parseState, "MIN")) {
+         if (!Parse_BiOpInstruction(parseState, inst, VP_OPCODE_MIN))
+            RETURN_ERROR;
+      }
+      else if (Parse_String(parseState, "MAX")) {
+         if (!Parse_BiOpInstruction(parseState, inst, VP_OPCODE_MAX))
+            RETURN_ERROR;
+      }
+      else if (Parse_String(parseState, "SLT")) {
+         if (!Parse_BiOpInstruction(parseState, inst, VP_OPCODE_SLT))
+            RETURN_ERROR;
+      }
+      else if (Parse_String(parseState, "SGE")) {
+         if (!Parse_BiOpInstruction(parseState, inst, VP_OPCODE_SGE))
+            RETURN_ERROR;
+      }
+      else if (Parse_String(parseState, "DPH")) {
+         if (!Parse_BiOpInstruction(parseState, inst, VP_OPCODE_DPH))
+            RETURN_ERROR;
+      }
+      else if (Parse_String(parseState, "SUB")) {
+         if (!Parse_BiOpInstruction(parseState, inst, VP_OPCODE_SUB))
+            RETURN_ERROR;
+      }
+      else if (Parse_String(parseState, "MAD")) {
+         if (!Parse_TriOpInstruction(parseState, inst, VP_OPCODE_MAD))
+            RETURN_ERROR;
+      }
+      else if (Parse_String(parseState, "RCP")) {
+         if (!Parse_ScalarInstruction(parseState, inst, VP_OPCODE_RCP))
+            RETURN_ERROR;
+      }
+      else if (Parse_String(parseState, "RSQ")) {
+         if (!Parse_ScalarInstruction(parseState, inst, VP_OPCODE_RSQ))
+            RETURN_ERROR;
+      }
+      else if (Parse_String(parseState, "EXP")) {
+         if (!Parse_ScalarInstruction(parseState, inst, VP_OPCODE_EXP))
+            RETURN_ERROR;
+      }
+      else if (Parse_String(parseState, "LOG")) {
+         if (!Parse_ScalarInstruction(parseState, inst, VP_OPCODE_LOG))
+            RETURN_ERROR;
+      }
+      else if (Parse_String(parseState, "RCC")) {
+         if (!Parse_ScalarInstruction(parseState, inst, VP_OPCODE_RCC))
+            RETURN_ERROR;
+      }
+      else if (Parse_String(parseState, "ARL")) {
+         if (!Parse_AddressInstruction(parseState, inst))
+            RETURN_ERROR;
+      }
+      else if (Parse_String(parseState, "END")) {
+         if (!Parse_EndInstruction(parseState, inst))
+            RETURN_ERROR;
+         else {
+            parseState->numInst++;
+            return GL_TRUE;  /* all done */
+         }
+      }
+      else {
+         /* bad instruction name */
+         RETURN_ERROR1("Unexpected token");
+      }
+
+      /* examine input/output registers */
+      if (inst->DstReg.File == PROGRAM_OUTPUT)
+         parseState->outputsWritten |= (1 << inst->DstReg.Index);
+      else if (inst->DstReg.File == PROGRAM_ENV_PARAM)
+         parseState->anyProgRegsWritten = GL_TRUE;
+
+      if (inst->SrcReg[0].File == PROGRAM_INPUT)
+         parseState->inputsRead |= (1 << inst->SrcReg[0].Index);
+      if (inst->SrcReg[1].File == PROGRAM_INPUT)
+         parseState->inputsRead |= (1 << inst->SrcReg[1].Index);
+      if (inst->SrcReg[2].File == PROGRAM_INPUT)
+         parseState->inputsRead |= (1 << inst->SrcReg[2].Index);
+
+      parseState->numInst++;
+
+      if (parseState->numInst >= MAX_NV_VERTEX_PROGRAM_INSTRUCTIONS)
+         RETURN_ERROR1("Program too long");
+   }
+
+   RETURN_ERROR;
+}
+
+
+static GLboolean
+Parse_Program(struct parse_state *parseState,
+              struct vp_instruction instBuffer[])
+{
+   if (parseState->isVersion1_1) {
+      if (!Parse_OptionSequence(parseState, instBuffer)) {
+         return GL_FALSE;
+      }
+   }
+   return Parse_InstructionSequence(parseState, instBuffer);
+}
+
+
+/**
+ * Parse/compile the 'str' returning the compiled 'program'.
+ * ctx->Program.ErrorPos will be -1 if successful.  Otherwise, ErrorPos
+ * indicates the position of the error in 'str'.
+ */
+void
+_mesa_parse_nv_vertex_program(GLcontext *ctx, GLenum dstTarget,
+                              const GLubyte *str, GLsizei len,
+                              struct vertex_program *program)
+{
+   struct parse_state parseState;
+   struct vp_instruction instBuffer[MAX_NV_VERTEX_PROGRAM_INSTRUCTIONS];
+   struct vp_instruction *newInst;
+   GLenum target;
+   GLubyte *programString;
+
+   /* Make a null-terminated copy of the program string */
+   programString = (GLubyte *) MALLOC(len + 1);
+   if (!programString) {
+      _mesa_error(ctx, GL_OUT_OF_MEMORY, "glLoadProgramNV");
+      return;
+   }
+   MEMCPY(programString, str, len);
+   programString[len] = 0;
+
+   /* Get ready to parse */
+   parseState.ctx = ctx;
+   parseState.start = programString;
+   parseState.isPositionInvariant = GL_FALSE;
+   parseState.isVersion1_1 = GL_FALSE;
+   parseState.numInst = 0;
+   parseState.inputsRead = 0;
+   parseState.outputsWritten = 0;
+   parseState.anyProgRegsWritten = GL_FALSE;
+
+   /* Reset error state */
+   _mesa_set_program_error(ctx, -1, NULL);
+
+   /* check the program header */
+   if (_mesa_strncmp((const char *) programString, "!!VP1.0", 7) == 0) {
+      target = GL_VERTEX_PROGRAM_NV;
+      parseState.pos = programString + 7;
+      parseState.isStateProgram = GL_FALSE;
+   }
+   else if (_mesa_strncmp((const char *) programString, "!!VP1.1", 7) == 0) {
+      target = GL_VERTEX_PROGRAM_NV;
+      parseState.pos = programString + 7;
+      parseState.isStateProgram = GL_FALSE;
+      parseState.isVersion1_1 = GL_TRUE;
+   }
+   else if (_mesa_strncmp((const char *) programString, "!!VSP1.0", 8) == 0) {
+      target = GL_VERTEX_STATE_PROGRAM_NV;
+      parseState.pos = programString + 8;
+      parseState.isStateProgram = GL_TRUE;
+   }
+   else {
+      /* invalid header */
+      ctx->Program.ErrorPos = 0;
+      _mesa_error(ctx, GL_INVALID_OPERATION, "glLoadProgramNV(bad header)");
+      return;
+   }
+
+   /* make sure target and header match */
+   if (target != dstTarget) {
+      _mesa_error(ctx, GL_INVALID_OPERATION,
+                  "glLoadProgramNV(target mismatch)");
+      return;
+   }
+
+
+   if (Parse_Program(&parseState, instBuffer)) {
+      /* successful parse! */
+
+      if (parseState.isStateProgram) {
+         if (!parseState.anyProgRegsWritten) {
+            _mesa_error(ctx, GL_INVALID_OPERATION,
+                        "glLoadProgramNV(c[#] not written)");
+            return;
+         }
+      }
+      else {
+         if (!parseState.isPositionInvariant &&
+             !(parseState.outputsWritten & 1)) {
+            /* bit 1 = HPOS register */
+            _mesa_error(ctx, GL_INVALID_OPERATION,
+                        "glLoadProgramNV(HPOS not written)");
+            return;
+         }
+      }
+
+      /* copy the compiled instructions */
+      assert(parseState.numInst <= MAX_NV_VERTEX_PROGRAM_INSTRUCTIONS);
+      newInst = (struct vp_instruction *)
+         MALLOC(parseState.numInst * sizeof(struct vp_instruction));
+      if (!newInst) {
+         _mesa_error(ctx, GL_OUT_OF_MEMORY, "glLoadProgramNV");
+         FREE(programString);
+         return;  /* out of memory */
+      }
+      MEMCPY(newInst, instBuffer,
+             parseState.numInst * sizeof(struct vp_instruction));
+
+      /* install the program */
+      program->Base.Target = target;
+      if (program->Base.String) {
+         FREE(program->Base.String);
+      }
+      program->Base.String = programString;
+      program->Base.Format = GL_PROGRAM_FORMAT_ASCII_ARB;
+      if (program->Instructions) {
+         FREE(program->Instructions);
+      }
+      program->Instructions = newInst;
+      program->InputsRead = parseState.inputsRead;
+      program->OutputsWritten = parseState.outputsWritten;
+      program->IsPositionInvariant = parseState.isPositionInvariant;
+
+#ifdef DEBUG
+      _mesa_printf("--- glLoadProgramNV result ---\n");
+      _mesa_print_nv_vertex_program(program);
+      _mesa_printf("------------------------------\n");
+#endif
+   }
+   else {
+      /* Error! */
+      _mesa_error(ctx, GL_INVALID_OPERATION, "glLoadProgramNV");
+      /* NOTE: _mesa_set_program_error would have been called already */
+      /* GL_NV_vertex_program isn't supposed to set the error string
+       * so we reset it here.
+       */
+      _mesa_set_program_error(ctx, ctx->Program.ErrorPos, NULL);
+   }
+}
+
+
+static void
+PrintSrcReg(const struct vp_src_register *src)
+{
+   static const char comps[5] = "xyzw";
+   if (src->Negate)
+      _mesa_printf("-");
+   if (src->RelAddr) {
+      if (src->Index > 0)
+         _mesa_printf("c[A0.x + %d]", src->Index);
+      else if (src->Index < 0)
+         _mesa_printf("c[A0.x - %d]", -src->Index);
+      else
+         _mesa_printf("c[A0.x]");
+   }
+   else if (src->File == PROGRAM_OUTPUT) {
+      _mesa_printf("o[%s]", OutputRegisters[src->Index]);
+   }
+   else if (src->File == PROGRAM_INPUT) {
+      _mesa_printf("v[%s]", InputRegisters[src->Index]);
+   }
+   else if (src->File == PROGRAM_ENV_PARAM) {
+      _mesa_printf("c[%d]", src->Index);
+   }
+   else {
+      ASSERT(src->File == PROGRAM_TEMPORARY);
+      _mesa_printf("R%d", src->Index);
+   }
+
+   if (src->Swizzle[0] == src->Swizzle[1] &&
+       src->Swizzle[0] == src->Swizzle[2] &&
+       src->Swizzle[0] == src->Swizzle[3]) {
+      _mesa_printf(".%c", comps[src->Swizzle[0]]);
+   }
+   else if (src->Swizzle[0] != 0 ||
+            src->Swizzle[1] != 1 ||
+            src->Swizzle[2] != 2 ||
+            src->Swizzle[3] != 3) {
+      _mesa_printf(".%c%c%c%c",
+             comps[src->Swizzle[0]],
+             comps[src->Swizzle[1]],
+             comps[src->Swizzle[2]],
+             comps[src->Swizzle[3]]);
+   }
+}
+
+
+static void
+PrintDstReg(const struct vp_dst_register *dst)
+{
+   GLint w = dst->WriteMask[0] + dst->WriteMask[1]
+           + dst->WriteMask[2] + dst->WriteMask[3];
+
+   if (dst->File == PROGRAM_OUTPUT) {
+      _mesa_printf("o[%s]", OutputRegisters[dst->Index]);
+   }
+   else if (dst->File == PROGRAM_INPUT) {
+      _mesa_printf("v[%s]", InputRegisters[dst->Index]);
+   }
+   else if (dst->File == PROGRAM_ENV_PARAM) {
+      _mesa_printf("c[%d]", dst->Index);
+   }
+   else {
+      ASSERT(dst->File == PROGRAM_TEMPORARY);
+      _mesa_printf("R%d", dst->Index);
+   }
+
+   if (w != 0 && w != 4) {
+      _mesa_printf(".");
+      if (dst->WriteMask[0])
+         _mesa_printf("x");
+      if (dst->WriteMask[1])
+         _mesa_printf("y");
+      if (dst->WriteMask[2])
+         _mesa_printf("z");
+      if (dst->WriteMask[3])
+         _mesa_printf("w");
+   }
+}
+
+
+/**
+ * Print a single NVIDIA vertex program instruction.
+ */
+void
+_mesa_print_nv_vertex_instruction(const struct vp_instruction *inst)
+{
+   switch (inst->Opcode) {
+      case VP_OPCODE_MOV:
+      case VP_OPCODE_LIT:
+      case VP_OPCODE_RCP:
+      case VP_OPCODE_RSQ:
+      case VP_OPCODE_EXP:
+      case VP_OPCODE_LOG:
+      case VP_OPCODE_RCC:
+      case VP_OPCODE_ABS:
+         _mesa_printf("%s ", Opcodes[(int) inst->Opcode]);
+         PrintDstReg(&inst->DstReg);
+         _mesa_printf(", ");
+         PrintSrcReg(&inst->SrcReg[0]);
+         _mesa_printf(";\n");
+         break;
+      case VP_OPCODE_MUL:
+      case VP_OPCODE_ADD:
+      case VP_OPCODE_DP3:
+      case VP_OPCODE_DP4:
+      case VP_OPCODE_DST:
+      case VP_OPCODE_MIN:
+      case VP_OPCODE_MAX:
+      case VP_OPCODE_SLT:
+      case VP_OPCODE_SGE:
+      case VP_OPCODE_DPH:
+      case VP_OPCODE_SUB:
+         _mesa_printf("%s ", Opcodes[(int) inst->Opcode]);
+         PrintDstReg(&inst->DstReg);
+         _mesa_printf(", ");
+         PrintSrcReg(&inst->SrcReg[0]);
+         _mesa_printf(", ");
+         PrintSrcReg(&inst->SrcReg[1]);
+         _mesa_printf(";\n");
+         break;
+      case VP_OPCODE_MAD:
+         _mesa_printf("MAD ");
+         PrintDstReg(&inst->DstReg);
+         _mesa_printf(", ");
+         PrintSrcReg(&inst->SrcReg[0]);
+         _mesa_printf(", ");
+         PrintSrcReg(&inst->SrcReg[1]);
+         _mesa_printf(", ");
+         PrintSrcReg(&inst->SrcReg[2]);
+         _mesa_printf(";\n");
+         break;
+      case VP_OPCODE_ARL:
+         _mesa_printf("ARL A0.x, ");
+         PrintSrcReg(&inst->SrcReg[0]);
+         _mesa_printf(";\n");
+         break;
+      case VP_OPCODE_END:
+         _mesa_printf("END\n");
+         break;
+      default:
+         _mesa_printf("BAD INSTRUCTION\n");
+   }
+}
+
+
+/**
+ * Print (unparse) the given vertex program.  Just for debugging.
+ */
+void
+_mesa_print_nv_vertex_program(const struct vertex_program *program)
+{
+   const struct vp_instruction *inst;
+
+   for (inst = program->Instructions; ; inst++) {
+      _mesa_print_nv_vertex_instruction(inst);
+      if (inst->Opcode == VP_OPCODE_END)
+         return;
+   }
+}
+
+
+const char *
+_mesa_nv_vertex_input_register_name(GLuint i)
+{
+   ASSERT(i < MAX_NV_VERTEX_PROGRAM_INPUTS);
+   return InputRegisters[i];
+}
+
+
+const char *
+_mesa_nv_vertex_output_register_name(GLuint i)
+{
+   ASSERT(i < MAX_NV_VERTEX_PROGRAM_OUTPUTS);
+   return OutputRegisters[i];
+}
diff --git a/src/mesa/shader/nvvertparse.h b/src/mesa/shader/nvvertparse.h
new file mode 100644 (file)
index 0000000..205885f
--- /dev/null
@@ -0,0 +1,50 @@
+/*
+ * Mesa 3-D graphics library
+ * Version:  5.1
+ *
+ * Copyright (C) 1999-2003  Brian Paul   All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * BRIAN PAUL BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors:
+ *    Brian Paul
+ */
+
+
+#ifndef NVVERTPARSE_H
+#define NVVERTPARSE_H
+
+
+extern void
+_mesa_parse_nv_vertex_program(GLcontext *ctx, GLenum target,
+                              const GLubyte *str, GLsizei len,
+                              struct vertex_program *program);
+
+extern void
+_mesa_print_nv_vertex_instruction(const struct vp_instruction *inst);
+
+extern void
+_mesa_print_nv_vertex_program(const struct vertex_program *program);
+
+extern const char *
+_mesa_nv_vertex_input_register_name(GLuint i);
+
+extern const char *
+_mesa_nv_vertex_output_register_name(GLuint i);
+
+#endif
diff --git a/src/mesa/shader/nvvertprog.h b/src/mesa/shader/nvvertprog.h
new file mode 100644 (file)
index 0000000..820b3e5
--- /dev/null
@@ -0,0 +1,107 @@
+/*
+ * Mesa 3-D graphics library
+ * Version:  5.1
+ *
+ * Copyright (C) 1999-2002  Brian Paul   All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * BRIAN PAUL BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+
+/* Private vertex program types and constants only used by files
+ * related to vertex programs.
+ *
+ * XXX TO-DO: Rename this file "vertprog.h" since it's not NV-specific.
+ */
+
+
+#ifndef NVVERTPROG_H
+#define NVVERTPROG_H
+
+
+/* Vertex program opcodes */
+enum vp_opcode
+{
+   VP_OPCODE_MOV,
+   VP_OPCODE_LIT,
+   VP_OPCODE_RCP,
+   VP_OPCODE_RSQ,
+   VP_OPCODE_EXP,
+   VP_OPCODE_LOG,
+   VP_OPCODE_MUL,
+   VP_OPCODE_ADD,
+   VP_OPCODE_DP3,
+   VP_OPCODE_DP4,
+   VP_OPCODE_DST,
+   VP_OPCODE_MIN,
+   VP_OPCODE_MAX,
+   VP_OPCODE_SLT,
+   VP_OPCODE_SGE,
+   VP_OPCODE_MAD,
+   VP_OPCODE_ARL,
+   VP_OPCODE_DPH,
+   VP_OPCODE_RCC,
+   VP_OPCODE_SUB,
+   VP_OPCODE_ABS,
+   VP_OPCODE_END,
+   /* Additional opcodes for GL_ARB_vertex_program */ 
+   VP_OPCODE_FLR,
+   VP_OPCODE_FRC,
+   VP_OPCODE_EX2,
+   VP_OPCODE_LG2,
+   VP_OPCODE_POW,
+   VP_OPCODE_XPD,
+   VP_OPCODE_SWZ
+};
+
+
+
+/* Instruction source register */
+struct vp_src_register
+{
+   enum register_file File;  /* which register file */
+   GLint Index;              /* index into register file */
+   GLubyte Swizzle[4]; /* Each value is 0,1,2,3 for x,y,z,w or */
+                       /* SWIZZLE_ZERO or SWIZZLE_ONE for VP_OPCODE_SWZ. */
+   GLboolean Negate;
+   GLboolean RelAddr;
+};
+
+
+/* Instruction destination register */
+struct vp_dst_register
+{
+   enum register_file File;  /* which register file */
+   GLint Index;              /* index into register file */
+   GLboolean WriteMask[4];
+};
+
+
+/* Vertex program instruction */
+struct vp_instruction
+{
+   enum vp_opcode Opcode;
+   struct vp_src_register SrcReg[3];
+   struct vp_dst_register DstReg;
+#if FEATURE_MESA_program_debug
+   GLint StringPos;
+#endif
+};
+
+
+#endif /* VERTPROG_H */
diff --git a/src/mesa/shader/program.c b/src/mesa/shader/program.c
new file mode 100644 (file)
index 0000000..6c1e362
--- /dev/null
@@ -0,0 +1,1271 @@
+/*
+ * Mesa 3-D graphics library
+ * Version:  6.0
+ *
+ * Copyright (C) 1999-2004  Brian Paul   All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * BRIAN PAUL BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+/**
+ * \file program.c
+ * Vertex and fragment program support functions.
+ * \author Brian Paul
+ */
+
+
+#include "glheader.h"
+#include "context.h"
+#include "hash.h"
+#include "imports.h"
+#include "macros.h"
+#include "mtypes.h"
+#include "program.h"
+#include "nvfragparse.h"
+#include "nvfragprog.h"
+#include "nvvertparse.h"
+
+
+/**********************************************************************/
+/* Utility functions                                                  */
+/**********************************************************************/
+
+
+/**
+ * Init context's program state
+ */
+void
+_mesa_init_program(GLcontext *ctx)
+{
+   GLuint i;
+
+   ctx->Program.ErrorPos = -1;
+   ctx->Program.ErrorString = _mesa_strdup("");
+
+#if FEATURE_NV_vertex_program || FEATURE_ARB_vertex_program
+   ctx->VertexProgram.Enabled = GL_FALSE;
+   ctx->VertexProgram.PointSizeEnabled = GL_FALSE;
+   ctx->VertexProgram.TwoSideEnabled = GL_FALSE;
+   ctx->VertexProgram.Current = NULL;
+   ctx->VertexProgram.Current = (struct vertex_program *) ctx->Shared->DefaultVertexProgram;
+   assert(ctx->VertexProgram.Current);
+   ctx->VertexProgram.Current->Base.RefCount++;
+   for (i = 0; i < MAX_NV_VERTEX_PROGRAM_PARAMS / 4; i++) {
+      ctx->VertexProgram.TrackMatrix[i] = GL_NONE;
+      ctx->VertexProgram.TrackMatrixTransform[i] = GL_IDENTITY_NV;
+   }
+#endif
+
+#if FEATURE_NV_fragment_program || FEATURE_ARB_fragment_program
+   ctx->FragmentProgram.Enabled = GL_FALSE;
+   ctx->FragmentProgram.Current = (struct fragment_program *) ctx->Shared->DefaultFragmentProgram;
+   assert(ctx->FragmentProgram.Current);
+   ctx->FragmentProgram.Current->Base.RefCount++;
+#endif
+}
+
+
+/**
+ * Set the vertex/fragment program error state (position and error string).
+ * This is generally called from within the parsers.
+ */
+void
+_mesa_set_program_error(GLcontext *ctx, GLint pos, const char *string)
+{
+   ctx->Program.ErrorPos = pos;
+   _mesa_free((void *) ctx->Program.ErrorString);
+   if (!string)
+      string = "";
+   ctx->Program.ErrorString = _mesa_strdup(string);
+}
+
+
+/**
+ * Find the line number and column for 'pos' within 'string'.
+ * Return a copy of the line which contains 'pos'.  Free the line with
+ * _mesa_free().
+ * \param string  the program string
+ * \param pos     the position within the string
+ * \param line    returns the line number corresponding to 'pos'.
+ * \param col     returns the column number corresponding to 'pos'.
+ * \return copy of the line containing 'pos'.
+ */
+const GLubyte *
+_mesa_find_line_column(const GLubyte *string, const GLubyte *pos,
+                       GLint *line, GLint *col)
+{
+   const GLubyte *lineStart = string;
+   const GLubyte *p = string;
+   GLubyte *s;
+   int len;
+
+   *line = 1;
+
+   while (p != pos) {
+      if (*p == (GLubyte) '\n') {
+         (*line)++;
+         lineStart = p + 1;
+      }
+      p++;
+   }
+
+   *col = (pos - lineStart) + 1;
+
+   /* return copy of this line */
+   while (*p != 0 && *p != '\n')
+      p++;
+   len = p - lineStart;
+   s = (GLubyte *) _mesa_malloc(len + 1);
+   _mesa_memcpy(s, lineStart, len);
+   s[len] = 0;
+
+   return s;
+}
+
+
+static struct program * _mesa_init_program_struct( GLcontext *ctx, 
+                                                  struct program *prog,
+                                                  GLenum target, GLuint id)
+{
+   if (prog) {
+      prog->Id = id;
+      prog->Target = target;
+      prog->Resident = GL_TRUE;
+      prog->RefCount = 1;
+   }
+
+   return prog;
+}
+
+struct program * _mesa_init_fragment_program( GLcontext *ctx, 
+                                             struct fragment_program *prog,
+                                             GLenum target, GLuint id)
+{
+   if (prog) 
+      return _mesa_init_program_struct( ctx, &prog->Base, target, id );
+   else
+      return NULL;
+}
+
+struct program * _mesa_init_vertex_program( GLcontext *ctx, 
+                                           struct vertex_program *prog,
+                                           GLenum target, GLuint id)
+{
+   if (prog) 
+      return _mesa_init_program_struct( ctx, &prog->Base, target, id );
+   else
+      return NULL;
+}
+
+
+/**
+ * Allocate and initialize a new fragment/vertex program object but
+ * don't put it into the program hash table.  Called via
+ * ctx->Driver.NewProgram.  May be overridden (ie. replaced) by a
+ * device driver function to implement OO deriviation with additional
+ * types not understood by this function.
+ * 
+ * \param ctx  context
+ * \param id   program id/number
+ * \param target  program target/type
+ * \return  pointer to new program object
+ */
+struct program *
+_mesa_new_program(GLcontext *ctx, GLenum target, GLuint id)
+{
+   switch (target) {
+   case GL_VERTEX_PROGRAM_ARB: /* == GL_VERTEX_PROGRAM_NV */
+      return _mesa_init_vertex_program( ctx, CALLOC_STRUCT(vertex_program),
+                                       target, id );
+
+   case GL_FRAGMENT_PROGRAM_NV:
+   case GL_FRAGMENT_PROGRAM_ARB:
+      return _mesa_init_fragment_program( ctx, CALLOC_STRUCT(fragment_program),
+                                         target, id );
+
+   default:
+      _mesa_problem(ctx, "bad target in _mesa_new_program");
+      return NULL;
+   }
+}
+
+
+/**
+ * Delete a program and remove it from the hash table, ignoring the
+ * reference count.
+ * Called via ctx->Driver.DeleteProgram.  May be wrapped (OO deriviation)
+ * by a device driver function.
+ */
+void
+_mesa_delete_program(GLcontext *ctx, struct program *prog)
+{
+   ASSERT(prog);
+
+   if (prog->String)
+      _mesa_free(prog->String);
+   if (prog->Target == GL_VERTEX_PROGRAM_NV ||
+       prog->Target == GL_VERTEX_STATE_PROGRAM_NV) {
+      struct vertex_program *vprog = (struct vertex_program *) prog;
+      if (vprog->Instructions)
+         _mesa_free(vprog->Instructions);
+   }
+   else if (prog->Target == GL_FRAGMENT_PROGRAM_NV ||
+            prog->Target == GL_FRAGMENT_PROGRAM_ARB) {
+      struct fragment_program *fprog = (struct fragment_program *) prog;
+      if (fprog->Instructions)
+         _mesa_free(fprog->Instructions);
+      if (fprog->Parameters) {
+         _mesa_free_parameter_list(fprog->Parameters);
+      }
+   }
+   _mesa_free(prog);
+}
+
+
+
+/**********************************************************************/
+/* Program parameter functions                                        */
+/**********************************************************************/
+
+struct program_parameter_list *
+_mesa_new_parameter_list(void)
+{
+   return (struct program_parameter_list *)
+      _mesa_calloc(sizeof(struct program_parameter_list));
+}
+
+
+/**
+ * Free a parameter list and all its parameters
+ */
+void
+_mesa_free_parameter_list(struct program_parameter_list *paramList)
+{
+   _mesa_free_parameters(paramList);
+   _mesa_free(paramList);
+}
+
+
+/**
+ * Free all the parameters in the given list, but don't free the
+ * paramList structure itself.
+ */
+void
+_mesa_free_parameters(struct program_parameter_list *paramList)
+{
+   GLuint i;
+   for (i = 0; i < paramList->NumParameters; i++) {
+      _mesa_free((void *) paramList->Parameters[i].Name);
+   }
+   _mesa_free(paramList->Parameters);
+   paramList->NumParameters = 0;
+   paramList->Parameters = NULL;
+}
+
+
+/**
+ * Helper function used by the functions below.
+ */
+static GLint
+add_parameter(struct program_parameter_list *paramList,
+              const char *name, const GLfloat values[4],
+              enum parameter_type type)
+{
+   const GLuint n = paramList->NumParameters;
+
+   paramList->Parameters = (struct program_parameter *)
+      _mesa_realloc(paramList->Parameters,
+                    n * sizeof(struct program_parameter),
+                    (n + 1) * sizeof(struct program_parameter));
+   if (!paramList->Parameters) {
+      /* out of memory */
+      paramList->NumParameters = 0;
+      return -1;
+   }
+   else {
+      paramList->NumParameters = n + 1;
+      paramList->Parameters[n].Name = _mesa_strdup(name);
+      paramList->Parameters[n].Type = type;
+      if (values)
+         COPY_4V(paramList->Parameters[n].Values, values);
+      return (GLint) n;
+   }
+}
+
+
+/**
+ * Add a new named program parameter (Ex: NV_fragment_program DEFINE statement)
+ * \return index of the new entry in the parameter list
+ */
+GLint
+_mesa_add_named_parameter(struct program_parameter_list *paramList,
+                          const char *name, const GLfloat values[4])
+{
+   return add_parameter(paramList, name, values, NAMED_PARAMETER);
+}
+
+
+/**
+ * Add a new unnamed constant to the parameter list.
+ * \param paramList - the parameter list
+ * \param values - four float values
+ * \return index of the new parameter.
+ */
+GLint
+_mesa_add_named_constant(struct program_parameter_list *paramList,
+                         const char *name, const GLfloat values[4])
+{
+   return add_parameter(paramList, name, values, CONSTANT);
+}
+
+
+/**
+ * Add a new unnamed constant to the parameter list.
+ * \param paramList - the parameter list
+ * \param values - four float values
+ * \return index of the new parameter.
+ */
+GLint
+_mesa_add_unnamed_constant(struct program_parameter_list *paramList,
+                           const GLfloat values[4])
+{
+   /* generate a new dummy name */
+   static GLuint n = 0;
+   char name[20];
+   _mesa_sprintf(name, "constant%d", n);
+   n++;
+   /* store it */
+   return add_parameter(paramList, name, values, CONSTANT);
+}
+
+
+/**
+ * Add a new state reference to the parameter list.
+ * \param paramList - the parameter list
+ * \param state     - an array of 6 state tokens
+ *
+ * \return index of the new parameter.
+ */
+GLint
+_mesa_add_state_reference(struct program_parameter_list *paramList,
+                          GLint *stateTokens)
+{
+   /* XXX Should we parse <stateString> here and produce the parameter's
+    * list of STATE_* tokens here, or in the parser?
+    */
+   GLint a, idx;
+
+   idx = add_parameter(paramList, "Some State", NULL, STATE);
+       
+   for (a=0; a<6; a++)
+      paramList->Parameters[idx].StateIndexes[a] = (enum state_index) stateTokens[a];
+
+   return idx;
+}
+
+
+/**
+ * Lookup a parameter value by name in the given parameter list.
+ * \return pointer to the float[4] values.
+ */
+GLfloat *
+_mesa_lookup_parameter_value(struct program_parameter_list *paramList,
+                             GLsizei nameLen, const char *name)
+{
+   GLuint i;
+
+   if (!paramList)
+      return NULL;
+
+   if (nameLen == -1) {
+      /* name is null-terminated */
+      for (i = 0; i < paramList->NumParameters; i++) {
+         if (_mesa_strcmp(paramList->Parameters[i].Name, name) == 0)
+            return paramList->Parameters[i].Values;
+      }
+   }
+   else {
+      /* name is not null-terminated, use nameLen */
+      for (i = 0; i < paramList->NumParameters; i++) {
+         if (_mesa_strncmp(paramList->Parameters[i].Name, name, nameLen) == 0
+             && _mesa_strlen(paramList->Parameters[i].Name) == (size_t)nameLen)
+            return paramList->Parameters[i].Values;
+      }
+   }
+   return NULL;
+}
+
+
+/**
+ * Lookup a parameter index by name in the given parameter list.
+ * \return index of parameter in the list.
+ */
+GLint
+_mesa_lookup_parameter_index(struct program_parameter_list *paramList,
+                             GLsizei nameLen, const char *name)
+{
+   GLint i;
+
+   if (!paramList)
+      return -1;
+
+   if (nameLen == -1) {
+      /* name is null-terminated */
+      for (i = 0; i < (GLint) paramList->NumParameters; i++) {
+         if (_mesa_strcmp(paramList->Parameters[i].Name, name) == 0)
+            return i;
+      }
+   }
+   else {
+      /* name is not null-terminated, use nameLen */
+      for (i = 0; i < (GLint) paramList->NumParameters; i++) {
+         if (_mesa_strncmp(paramList->Parameters[i].Name, name, nameLen) == 0
+             && _mesa_strlen(paramList->Parameters[i].Name) == (size_t)nameLen)
+            return i;
+      }
+   }
+   return -1;
+}
+
+
+/**
+ * Use the list of tokens in the state[] array to find global GL state
+ * and return it in <value>.  Usually, four values are returned in <value>
+ * but matrix queries may return as many as 16 values.
+ * This function is used for ARB vertex/fragment programs.
+ * The program parser will produce the state[] values.
+ */
+static void
+_mesa_fetch_state(GLcontext *ctx, const enum state_index state[],
+                  GLfloat *value)
+{
+   switch (state[0]) {
+   case STATE_MATERIAL:
+      {
+         /* state[1] is either 0=front or 1=back side */
+         const GLuint face = (GLuint) state[1];
+         /* state[2] is the material attribute */
+         switch (state[2]) {
+         case STATE_AMBIENT:
+            if (face == 0)
+               COPY_4V(value, ctx->Light.Material.Attrib[MAT_ATTRIB_FRONT_AMBIENT]);
+            else
+               COPY_4V(value, ctx->Light.Material.Attrib[MAT_ATTRIB_BACK_AMBIENT]);
+            return;
+         case STATE_DIFFUSE:
+            if (face == 0)
+               COPY_4V(value, ctx->Light.Material.Attrib[MAT_ATTRIB_FRONT_DIFFUSE]);
+            else
+               COPY_4V(value, ctx->Light.Material.Attrib[MAT_ATTRIB_BACK_DIFFUSE]);
+            return;
+         case STATE_SPECULAR:
+            if (face == 0)
+               COPY_4V(value, ctx->Light.Material.Attrib[MAT_ATTRIB_FRONT_SPECULAR]);
+            else
+               COPY_4V(value, ctx->Light.Material.Attrib[MAT_ATTRIB_BACK_SPECULAR]);
+            return;
+         case STATE_EMISSION:
+            if (face == 0)
+               COPY_4V(value, ctx->Light.Material.Attrib[MAT_ATTRIB_FRONT_EMISSION]);
+            else
+               COPY_4V(value, ctx->Light.Material.Attrib[MAT_ATTRIB_BACK_EMISSION]);
+            return;
+         case STATE_SHININESS:
+            if (face == 0)
+               value[0] = ctx->Light.Material.Attrib[MAT_ATTRIB_FRONT_SHININESS][0];
+            else
+               value[0] = ctx->Light.Material.Attrib[MAT_ATTRIB_BACK_SHININESS][0];
+            value[1] = 0.0F;
+            value[2] = 0.0F;
+            value[3] = 1.0F;
+            return;
+         default:
+            _mesa_problem(ctx, "Invalid material state in fetch_state");
+            return;
+         }
+      };
+      return;
+   case STATE_LIGHT:
+      {
+         /* state[1] is the light number */
+         const GLuint ln = (GLuint) state[1];
+         /* state[2] is the light attribute */
+         switch (state[2]) {
+         case STATE_AMBIENT:
+            COPY_4V(value, ctx->Light.Light[ln].Ambient);
+            return;
+         case STATE_DIFFUSE:
+            COPY_4V(value, ctx->Light.Light[ln].Diffuse);
+            return;
+         case STATE_SPECULAR:
+            COPY_4V(value, ctx->Light.Light[ln].Specular);
+            return;
+         case STATE_POSITION:
+            COPY_4V(value, ctx->Light.Light[ln].EyePosition);
+            return;
+         case STATE_ATTENUATION:
+            value[0] = ctx->Light.Light[ln].ConstantAttenuation;
+            value[1] = ctx->Light.Light[ln].LinearAttenuation;
+            value[2] = ctx->Light.Light[ln].QuadraticAttenuation;
+            value[3] = ctx->Light.Light[ln].SpotExponent;
+            return;
+         case STATE_SPOT_DIRECTION:
+            COPY_4V(value, ctx->Light.Light[ln].EyeDirection);
+            return;
+         case STATE_HALF:
+            {
+               GLfloat eye_z[] = {0, 0, 1};
+                                       
+               /* Compute infinite half angle vector:
+                *   half-vector = light_position + (0, 0, 1) 
+                * and then normalize.  w = 0
+                                        *
+                                        * light.EyePosition.w should be 0 for infinite lights.
+                */
+                                       ADD_3V(value, eye_z, ctx->Light.Light[ln].EyePosition);
+                                       NORMALIZE_3FV(value);
+                                       value[3] = 0;
+            }                                            
+            return;
+         default:
+            _mesa_problem(ctx, "Invalid light state in fetch_state");
+            return;
+         }
+      }
+      return;
+   case STATE_LIGHTMODEL_AMBIENT:
+      COPY_4V(value, ctx->Light.Model.Ambient);
+      return;
+   case STATE_LIGHTMODEL_SCENECOLOR:
+      if (state[1] == 0) {
+         /* front */
+         GLint i;
+         for (i = 0; i < 4; i++) {
+            value[i] = ctx->Light.Model.Ambient[i]
+               * ctx->Light.Material.Attrib[MAT_ATTRIB_FRONT_AMBIENT][i]
+               + ctx->Light.Material.Attrib[MAT_ATTRIB_FRONT_EMISSION][i];
+         }
+      }
+      else {
+         /* back */
+         GLint i;
+         for (i = 0; i < 4; i++) {
+            value[i] = ctx->Light.Model.Ambient[i]
+               * ctx->Light.Material.Attrib[MAT_ATTRIB_BACK_AMBIENT][i]
+               + ctx->Light.Material.Attrib[MAT_ATTRIB_BACK_EMISSION][i];
+         }
+      }
+      return;
+   case STATE_LIGHTPROD:
+      {
+         const GLuint ln = (GLuint) state[1];
+         const GLuint face = (GLuint) state[2];
+         GLint i;
+         ASSERT(face == 0 || face == 1);
+         switch (state[3]) {
+            case STATE_AMBIENT:
+               for (i = 0; i < 3; i++) {
+                  value[i] = ctx->Light.Light[ln].Ambient[i] *
+                     ctx->Light.Material.Attrib[MAT_ATTRIB_FRONT_AMBIENT+face][i];
+               }
+               /* [3] = material alpha */
+               value[3] = ctx->Light.Material.Attrib[MAT_ATTRIB_FRONT_DIFFUSE+face][3];
+               return;
+            case STATE_DIFFUSE:
+               for (i = 0; i < 3; i++) {
+                  value[i] = ctx->Light.Light[ln].Diffuse[i] *
+                     ctx->Light.Material.Attrib[MAT_ATTRIB_FRONT_DIFFUSE+face][i];
+               }
+               /* [3] = material alpha */
+               value[3] = ctx->Light.Material.Attrib[MAT_ATTRIB_FRONT_DIFFUSE+face][3];
+               return;
+            case STATE_SPECULAR:
+               for (i = 0; i < 3; i++) {
+                  value[i] = ctx->Light.Light[ln].Specular[i] *
+                     ctx->Light.Material.Attrib[MAT_ATTRIB_FRONT_SPECULAR+face][i];
+               }
+               /* [3] = material alpha */
+               value[3] = ctx->Light.Material.Attrib[MAT_ATTRIB_FRONT_DIFFUSE+face][3];
+               return;
+            default:
+               _mesa_problem(ctx, "Invalid lightprod state in fetch_state");
+               return;
+         }
+      }
+      return;
+   case STATE_TEXGEN:
+      {
+         /* state[1] is the texture unit */
+         const GLuint unit = (GLuint) state[1];
+         /* state[2] is the texgen attribute */
+         switch (state[2]) {
+         case STATE_TEXGEN_EYE_S:
+            COPY_4V(value, ctx->Texture.Unit[unit].EyePlaneS);
+            return;
+         case STATE_TEXGEN_EYE_T:
+            COPY_4V(value, ctx->Texture.Unit[unit].EyePlaneT);
+            return;
+         case STATE_TEXGEN_EYE_R:
+            COPY_4V(value, ctx->Texture.Unit[unit].EyePlaneR);
+            return;
+         case STATE_TEXGEN_EYE_Q:
+            COPY_4V(value, ctx->Texture.Unit[unit].EyePlaneQ);
+            return;
+         case STATE_TEXGEN_OBJECT_S:
+            COPY_4V(value, ctx->Texture.Unit[unit].ObjectPlaneS);
+            return;
+         case STATE_TEXGEN_OBJECT_T:
+            COPY_4V(value, ctx->Texture.Unit[unit].ObjectPlaneT);
+            return;
+         case STATE_TEXGEN_OBJECT_R:
+            COPY_4V(value, ctx->Texture.Unit[unit].ObjectPlaneR);
+            return;
+         case STATE_TEXGEN_OBJECT_Q:
+            COPY_4V(value, ctx->Texture.Unit[unit].ObjectPlaneQ);
+            return;
+         default:
+            _mesa_problem(ctx, "Invalid texgen state in fetch_state");
+            return;
+         }
+      }
+      return;
+   case STATE_TEXENV_COLOR:
+      {                
+         /* state[1] is the texture unit */
+         const GLuint unit = (GLuint) state[1];
+         COPY_4V(value, ctx->Texture.Unit[unit].EnvColor);
+      }                        
+      return;
+   case STATE_FOG_COLOR:
+      COPY_4V(value, ctx->Fog.Color);
+      return;
+   case STATE_FOG_PARAMS:
+      value[0] = ctx->Fog.Density;
+      value[1] = ctx->Fog.Start;
+      value[2] = ctx->Fog.End;
+      value[3] = 1.0F / (ctx->Fog.End - ctx->Fog.Start);
+      return;
+   case STATE_CLIPPLANE:
+      {
+         const GLuint plane = (GLuint) state[1];
+         COPY_4V(value, ctx->Transform.EyeUserPlane[plane]);
+      }
+      return;
+   case STATE_POINT_SIZE:
+      value[0] = ctx->Point.Size;
+      value[1] = ctx->Point.MinSize;
+      value[2] = ctx->Point.MaxSize;
+      value[3] = ctx->Point.Threshold;
+      return;
+   case STATE_POINT_ATTENUATION:
+      value[0] = ctx->Point.Params[0];
+      value[1] = ctx->Point.Params[1];
+      value[2] = ctx->Point.Params[2];
+      value[3] = 1.0F;
+      return;
+   case STATE_MATRIX:
+      {
+         /* state[1] = modelview, projection, texture, etc. */
+         /* state[2] = which texture matrix or program matrix */
+         /* state[3] = first column to fetch */
+         /* state[4] = last column to fetch */
+         /* state[5] = transpose, inverse or invtrans */
+
+         const GLmatrix *matrix;
+         const enum state_index mat = state[1];
+         const GLuint index = (GLuint) state[2];
+         const GLuint first = (GLuint) state[3];
+         const GLuint last = (GLuint) state[4];
+         const enum state_index modifier = state[5];
+         const GLfloat *m;
+         GLuint row, i;
+         if (mat == STATE_MODELVIEW) {
+            matrix = ctx->ModelviewMatrixStack.Top;
+         }
+         else if (mat == STATE_PROJECTION) {
+            matrix = ctx->ProjectionMatrixStack.Top;
+         }
+         else if (mat == STATE_MVP) {
+            matrix = &ctx->_ModelProjectMatrix;
+         }
+         else if (mat == STATE_TEXTURE) {
+            matrix = ctx->TextureMatrixStack[index].Top;
+         }
+         else if (mat == STATE_PROGRAM) {
+            matrix = ctx->ProgramMatrixStack[index].Top;
+         }
+         else {
+            _mesa_problem(ctx, "Bad matrix name in _mesa_fetch_state()");
+            return;
+         }
+         if (modifier == STATE_MATRIX_INVERSE ||
+             modifier == STATE_MATRIX_INVTRANS) {
+            /* XXX be sure inverse is up to date */
+            m = matrix->inv;
+         }
+         else {
+            m = matrix->m;
+         }
+         if (modifier == STATE_MATRIX_TRANSPOSE ||
+             modifier == STATE_MATRIX_INVTRANS) {
+            for (i = 0, row = first; row <= last; row++) {
+               value[i++] = m[row * 4 + 0];
+               value[i++] = m[row * 4 + 1];
+               value[i++] = m[row * 4 + 2];
+               value[i++] = m[row * 4 + 3];
+            }
+         }
+         else {
+            for (i = 0, row = first; row <= last; row++) {
+               value[i++] = m[row + 0];
+               value[i++] = m[row + 4];
+               value[i++] = m[row + 8];
+               value[i++] = m[row + 12];
+            }
+         }
+      }
+      return;
+   case STATE_DEPTH_RANGE:
+      value[0] = ctx->Viewport.Near;                     /* near       */              
+      value[1] = ctx->Viewport.Far;                      /* far        */              
+      value[2] = ctx->Viewport.Far - ctx->Viewport.Near; /* far - near */              
+      value[3] = 0;            
+      return;
+   case STATE_FRAGMENT_PROGRAM:
+      {
+         /* state[1] = {STATE_ENV, STATE_LOCAL} */
+         /* state[2] = parameter index          */
+         int idx = state[2];                             
+
+         switch (state[1]) {
+            case STATE_ENV:
+               COPY_4V(value, ctx->FragmentProgram.Parameters[idx]);                                             
+               break;
+
+            case STATE_LOCAL:
+               COPY_4V(value, ctx->FragmentProgram.Current->Base.LocalParams[idx]);
+               break;                          
+            default:
+               _mesa_problem(ctx, "Bad state switch in _mesa_fetch_state()");
+               return;
+         }                               
+      }                        
+      return;
+               
+   case STATE_VERTEX_PROGRAM:
+      {                
+         /* state[1] = {STATE_ENV, STATE_LOCAL} */
+         /* state[2] = parameter index          */
+         int idx = state[2];                             
+                       
+         switch (state[1]) {
+            case STATE_ENV:
+               COPY_4V(value, ctx->VertexProgram.Parameters[idx]);                                               
+               break;
+
+            case STATE_LOCAL:
+               COPY_4V(value, ctx->VertexProgram.Current->Base.LocalParams[idx]);
+               break;                          
+            default:
+               _mesa_problem(ctx, "Bad state switch in _mesa_fetch_state()");
+               return;
+         }                               
+      }                        
+      return;
+   default:
+      _mesa_problem(ctx, "Invalid state in fetch_state");
+      return;
+   }
+}
+
+
+/**
+ * Loop over all the parameters in a parameter list.  If the parameter
+ * is a GL state reference, look up the current value of that state
+ * variable and put it into the parameter's Value[4] array.
+ * This would be called at glBegin time when using a fragment program.
+ */
+void
+_mesa_load_state_parameters(GLcontext *ctx,
+                            struct program_parameter_list *paramList)
+{
+   GLuint i;
+
+   if (!paramList)
+      return;
+
+   for (i = 0; i < paramList->NumParameters; i++) {
+      if (paramList->Parameters[i].Type == STATE) {
+         _mesa_fetch_state(ctx, paramList->Parameters[i].StateIndexes,
+                           paramList->Parameters[i].Values);
+      }
+   }
+}
+
+
+
+/**********************************************************************/
+/* API functions                                                      */
+/**********************************************************************/
+
+
+/**
+ * Bind a program (make it current)
+ * \note Called from the GL API dispatcher by both glBindProgramNV
+ * and glBindProgramARB.
+ */
+void GLAPIENTRY
+_mesa_BindProgram(GLenum target, GLuint id)
+{
+   struct program *prog;
+   GET_CURRENT_CONTEXT(ctx);
+   ASSERT_OUTSIDE_BEGIN_END(ctx);
+
+   FLUSH_VERTICES(ctx, _NEW_PROGRAM);
+
+   if ((target == GL_VERTEX_PROGRAM_NV
+        && ctx->Extensions.NV_vertex_program) ||
+       (target == GL_VERTEX_PROGRAM_ARB
+        && ctx->Extensions.ARB_vertex_program)) {
+      if (ctx->VertexProgram.Current &&
+          ctx->VertexProgram.Current->Base.Id == id)
+         return;
+      /* decrement refcount on previously bound vertex program */
+      if (ctx->VertexProgram.Current) {
+         ctx->VertexProgram.Current->Base.RefCount--;
+         /* and delete if refcount goes below one */
+         if (ctx->VertexProgram.Current->Base.RefCount <= 0) {
+            ctx->Driver.DeleteProgram(ctx, &(ctx->VertexProgram.Current->Base));
+            _mesa_HashRemove(ctx->Shared->Programs, id);
+         }
+      }
+   }
+   else if ((target == GL_FRAGMENT_PROGRAM_NV
+             && ctx->Extensions.NV_fragment_program) ||
+            (target == GL_FRAGMENT_PROGRAM_ARB
+             && ctx->Extensions.ARB_fragment_program)) {
+      if (ctx->FragmentProgram.Current &&
+          ctx->FragmentProgram.Current->Base.Id == id)
+         return;
+      /* decrement refcount on previously bound fragment program */
+      if (ctx->FragmentProgram.Current) {
+         ctx->FragmentProgram.Current->Base.RefCount--;
+         /* and delete if refcount goes below one */
+         if (ctx->FragmentProgram.Current->Base.RefCount <= 0) {
+            ctx->Driver.DeleteProgram(ctx, &(ctx->FragmentProgram.Current->Base));
+            _mesa_HashRemove(ctx->Shared->Programs, id);
+         }
+      }
+   }
+   else {
+      _mesa_error(ctx, GL_INVALID_ENUM, "glBindProgramNV/ARB(target)");
+      return;
+   }
+
+   /* NOTE: binding to a non-existant program is not an error.
+    * That's supposed to be caught in glBegin.
+    */
+   if (id == 0) {
+      /* default program */
+      prog = NULL;
+      if (target == GL_VERTEX_PROGRAM_NV || target == GL_VERTEX_PROGRAM_ARB)
+         prog = ctx->Shared->DefaultVertexProgram;
+      else
+         prog = ctx->Shared->DefaultFragmentProgram;
+   }
+   else {
+      prog = (struct program *) _mesa_HashLookup(ctx->Shared->Programs, id);
+      if (prog) {
+         if (prog->Target == 0) {
+            /* prog was allocated with glGenProgramsNV */
+            prog->Target = target;
+         }
+         else if (prog->Target != target) {
+            _mesa_error(ctx, GL_INVALID_OPERATION,
+                        "glBindProgramNV/ARB(target mismatch)");
+            return;
+         }
+      }
+      else {
+         /* allocate a new program now */
+         prog = ctx->Driver.NewProgram(ctx, target, id);
+         if (!prog) {
+            _mesa_error(ctx, GL_OUT_OF_MEMORY, "glBindProgramNV/ARB");
+            return;
+         }
+         prog->Id = id;
+         prog->Target = target;
+         prog->Resident = GL_TRUE;
+         prog->RefCount = 1;
+         _mesa_HashInsert(ctx->Shared->Programs, id, prog);
+      }
+   }
+
+   /* bind now */
+   if (target == GL_VERTEX_PROGRAM_NV || target == GL_VERTEX_PROGRAM_ARB) {
+      ctx->VertexProgram.Current = (struct vertex_program *) prog;
+   }
+   else if (target == GL_FRAGMENT_PROGRAM_NV || target == GL_FRAGMENT_PROGRAM_ARB) {
+      ctx->FragmentProgram.Current = (struct fragment_program *) prog;
+   }
+
+   if (prog)
+      prog->RefCount++;
+
+   if (ctx->Driver.BindProgram)
+      ctx->Driver.BindProgram(ctx, target, prog);
+}
+
+
+/**
+ * Delete a list of programs.
+ * \note Not compiled into display lists.
+ * \note Called by both glDeleteProgramsNV and glDeleteProgramsARB.
+ */
+void GLAPIENTRY 
+_mesa_DeletePrograms(GLsizei n, const GLuint *ids)
+{
+   GLint i;
+   GET_CURRENT_CONTEXT(ctx);
+   ASSERT_OUTSIDE_BEGIN_END_AND_FLUSH(ctx);
+
+   if (n < 0) {
+      _mesa_error( ctx, GL_INVALID_VALUE, "glDeleteProgramsNV" );
+      return;
+   }
+
+   for (i = 0; i < n; i++) {
+      if (ids[i] != 0) {
+         struct program *prog = (struct program *)
+            _mesa_HashLookup(ctx->Shared->Programs, ids[i]);
+         if (prog) {
+            if (prog->Target == GL_VERTEX_PROGRAM_NV ||
+                prog->Target == GL_VERTEX_STATE_PROGRAM_NV) {
+               if (ctx->VertexProgram.Current &&
+                   ctx->VertexProgram.Current->Base.Id == ids[i]) {
+                  /* unbind this currently bound program */
+                  _mesa_BindProgram(prog->Target, 0);
+               }
+            }
+            else if (prog->Target == GL_FRAGMENT_PROGRAM_NV ||
+                     prog->Target == GL_FRAGMENT_PROGRAM_ARB) {
+               if (ctx->FragmentProgram.Current &&
+                   ctx->FragmentProgram.Current->Base.Id == ids[i]) {
+                  /* unbind this currently bound program */
+                  _mesa_BindProgram(prog->Target, 0);
+               }
+            }
+            else {
+               _mesa_problem(ctx, "bad target in glDeleteProgramsNV");
+               return;
+            }
+            prog->RefCount--;
+            if (prog->RefCount <= 0) {
+               ctx->Driver.DeleteProgram(ctx, prog);
+            }
+         }
+        else {
+           /* This is necessary as we can't tell from HashLookup
+            * whether the entry exists with data == 0, or if it
+            * doesn't exist at all.  As GenPrograms creates the first
+            * case below, need to call Remove() to avoid memory leak:
+            */
+            _mesa_HashRemove(ctx->Shared->Programs, ids[i]);
+        }
+      }
+   }
+}
+
+
+/**
+ * Generate a list of new program identifiers.
+ * \note Not compiled into display lists.
+ * \note Called by both glGenProgramsNV and glGenProgramsARB.
+ */
+void GLAPIENTRY
+_mesa_GenPrograms(GLsizei n, GLuint *ids)
+{
+   GLuint first;
+   GLuint i;
+   GET_CURRENT_CONTEXT(ctx);
+   ASSERT_OUTSIDE_BEGIN_END(ctx);
+
+   if (n < 0) {
+      _mesa_error(ctx, GL_INVALID_VALUE, "glGenPrograms");
+      return;
+   }
+
+   if (!ids)
+      return;
+
+   first = _mesa_HashFindFreeKeyBlock(ctx->Shared->Programs, n);
+
+   for (i = 0; i < (GLuint) n; i++) {
+      _mesa_HashInsert(ctx->Shared->Programs, first + i, 0);
+   }
+
+   /* Return the program names */
+   for (i = 0; i < (GLuint) n; i++) {
+      ids[i] = first + i;
+   }
+}
+
+
+/**
+ * Determine if id names a program.
+ * \note Not compiled into display lists.
+ * \note Called from both glIsProgramNV and glIsProgramARB.
+ * \param id is the program identifier
+ * \return GL_TRUE if id is a program, else GL_FALSE.
+ */
+GLboolean GLAPIENTRY
+_mesa_IsProgram(GLuint id)
+{
+   GET_CURRENT_CONTEXT(ctx);
+   ASSERT_OUTSIDE_BEGIN_END_WITH_RETVAL(ctx, GL_FALSE);
+
+   if (id == 0)
+      return GL_FALSE;
+
+   if (_mesa_HashLookup(ctx->Shared->Programs, id))
+      return GL_TRUE;
+   else
+      return GL_FALSE;
+}
+
+
+
+/**********************************************************************/
+/* GL_MESA_program_debug extension                                    */
+/**********************************************************************/
+
+
+/* XXX temporary */
+void
+glProgramCallbackMESA(GLenum target, GLprogramcallbackMESA callback,
+                      GLvoid *data)
+{
+   _mesa_ProgramCallbackMESA(target, callback, data);
+}
+
+
+void
+_mesa_ProgramCallbackMESA(GLenum target, GLprogramcallbackMESA callback,
+                          GLvoid *data)
+{
+   GET_CURRENT_CONTEXT(ctx);
+
+   switch (target) {
+      case GL_FRAGMENT_PROGRAM_ARB:
+         if (!ctx->Extensions.ARB_fragment_program) {
+            _mesa_error(ctx, GL_INVALID_ENUM, "glProgramCallbackMESA(target)");
+            return;
+         }
+         ctx->FragmentProgram.Callback = callback;
+         ctx->FragmentProgram.CallbackData = data;
+         break;
+      case GL_FRAGMENT_PROGRAM_NV:
+         if (!ctx->Extensions.NV_fragment_program) {
+            _mesa_error(ctx, GL_INVALID_ENUM, "glProgramCallbackMESA(target)");
+            return;
+         }
+         ctx->FragmentProgram.Callback = callback;
+         ctx->FragmentProgram.CallbackData = data;
+         break;
+      case GL_VERTEX_PROGRAM_ARB: /* == GL_VERTEX_PROGRAM_NV */
+         if (!ctx->Extensions.ARB_vertex_program &&
+             !ctx->Extensions.NV_vertex_program) {
+            _mesa_error(ctx, GL_INVALID_ENUM, "glProgramCallbackMESA(target)");
+            return;
+         }
+         ctx->VertexProgram.Callback = callback;
+         ctx->VertexProgram.CallbackData = data;
+         break;
+      default:
+         _mesa_error(ctx, GL_INVALID_ENUM, "glProgramCallbackMESA(target)");
+         return;
+   }
+}
+
+
+/* XXX temporary */
+void
+glGetProgramRegisterfvMESA(GLenum target,
+                           GLsizei len, const GLubyte *registerName,
+                           GLfloat *v)
+{
+   _mesa_GetProgramRegisterfvMESA(target, len, registerName, v);
+}
+
+
+void
+_mesa_GetProgramRegisterfvMESA(GLenum target,
+                               GLsizei len, const GLubyte *registerName,
+                               GLfloat *v)
+{
+   char reg[1000];
+   GET_CURRENT_CONTEXT(ctx);
+
+   /* We _should_ be inside glBegin/glEnd */
+#if 0
+   if (ctx->Driver.CurrentExecPrimitive == PRIM_OUTSIDE_BEGIN_END) {
+      _mesa_error(ctx, GL_INVALID_OPERATION, "glGetProgramRegisterfvMESA");
+      return;
+   }
+#endif
+
+   /* make null-terminated copy of registerName */
+   len = MIN2((unsigned int) len, sizeof(reg) - 1);
+   _mesa_memcpy(reg, registerName, len);
+   reg[len] = 0;
+
+   switch (target) {
+      case GL_VERTEX_PROGRAM_NV:
+         if (!ctx->Extensions.ARB_vertex_program &&
+             !ctx->Extensions.NV_vertex_program) {
+            _mesa_error(ctx, GL_INVALID_ENUM,
+                        "glGetProgramRegisterfvMESA(target)");
+            return;
+         }
+         if (!ctx->VertexProgram.Enabled) {
+            _mesa_error(ctx, GL_INVALID_OPERATION,
+                        "glGetProgramRegisterfvMESA");
+            return;
+         }
+         /* GL_NV_vertex_program */
+         if (reg[0] == 'R') {
+            /* Temp register */
+            GLint i = _mesa_atoi(reg + 1);
+            if (i >= (GLint)ctx->Const.MaxVertexProgramTemps) {
+               _mesa_error(ctx, GL_INVALID_VALUE,
+                           "glGetProgramRegisterfvMESA(registerName)");
+               return;
+            }
+            COPY_4V(v, ctx->VertexProgram.Temporaries[i]);
+         }
+         else if (reg[0] == 'v' && reg[1] == '[') {
+            /* Vertex Input attribute */
+            GLuint i;
+            for (i = 0; i < ctx->Const.MaxVertexProgramAttribs; i++) {
+               const char *name = _mesa_nv_vertex_input_register_name(i);
+               char number[10];
+               sprintf(number, "%d", i);
+               if (_mesa_strncmp(reg + 2, name, 4) == 0 ||
+                   _mesa_strncmp(reg + 2, number, _mesa_strlen(number)) == 0) {
+                  COPY_4V(v, ctx->VertexProgram.Inputs[i]);
+                  return;
+               }
+            }
+            _mesa_error(ctx, GL_INVALID_VALUE,
+                        "glGetProgramRegisterfvMESA(registerName)");
+            return;
+         }
+         else if (reg[0] == 'o' && reg[1] == '[') {
+            /* Vertex output attribute */
+         }
+         /* GL_ARB_vertex_program */
+         else if (_mesa_strncmp(reg, "vertex.", 7) == 0) {
+
+         }
+         else {
+            _mesa_error(ctx, GL_INVALID_VALUE,
+                        "glGetProgramRegisterfvMESA(registerName)");
+            return;
+         }
+         break;
+      case GL_FRAGMENT_PROGRAM_ARB:
+         if (!ctx->Extensions.ARB_fragment_program) {
+            _mesa_error(ctx, GL_INVALID_ENUM,
+                        "glGetProgramRegisterfvMESA(target)");
+            return;
+         }
+         if (!ctx->FragmentProgram.Enabled) {
+            _mesa_error(ctx, GL_INVALID_OPERATION,
+                        "glGetProgramRegisterfvMESA");
+            return;
+         }
+         /* XXX to do */
+         break;
+      case GL_FRAGMENT_PROGRAM_NV:
+         if (!ctx->Extensions.NV_fragment_program) {
+            _mesa_error(ctx, GL_INVALID_ENUM,
+                        "glGetProgramRegisterfvMESA(target)");
+            return;
+         }
+         if (!ctx->FragmentProgram.Enabled) {
+            _mesa_error(ctx, GL_INVALID_OPERATION,
+                        "glGetProgramRegisterfvMESA");
+            return;
+         }
+         if (reg[0] == 'R') {
+            /* Temp register */
+            GLint i = _mesa_atoi(reg + 1);
+            if (i >= (GLint)ctx->Const.MaxFragmentProgramTemps) {
+               _mesa_error(ctx, GL_INVALID_VALUE,
+                           "glGetProgramRegisterfvMESA(registerName)");
+               return;
+            }
+            COPY_4V(v, ctx->FragmentProgram.Machine.Temporaries[i]);
+         }
+         else if (reg[0] == 'f' && reg[1] == '[') {
+            /* Fragment input attribute */
+            GLuint i;
+            for (i = 0; i < ctx->Const.MaxFragmentProgramAttribs; i++) {
+               const char *name = _mesa_nv_fragment_input_register_name(i);
+               if (_mesa_strncmp(reg + 2, name, 4) == 0) {
+                  COPY_4V(v, ctx->FragmentProgram.Machine.Inputs[i]);
+                  return;
+               }
+            }
+            _mesa_error(ctx, GL_INVALID_VALUE,
+                        "glGetProgramRegisterfvMESA(registerName)");
+            return;
+         }
+         else if (_mesa_strcmp(reg, "o[COLR]") == 0) {
+            /* Fragment output color */
+            COPY_4V(v, ctx->FragmentProgram.Machine.Outputs[FRAG_OUTPUT_COLR]);
+         }
+         else if (_mesa_strcmp(reg, "o[COLH]") == 0) {
+            /* Fragment output color */
+            COPY_4V(v, ctx->FragmentProgram.Machine.Outputs[FRAG_OUTPUT_COLH]);
+         }
+         else if (_mesa_strcmp(reg, "o[DEPR]") == 0) {
+            /* Fragment output depth */
+            COPY_4V(v, ctx->FragmentProgram.Machine.Outputs[FRAG_OUTPUT_DEPR]);
+         }
+         else {
+            /* try user-defined identifiers */
+            const GLfloat *value = _mesa_lookup_parameter_value(
+                       ctx->FragmentProgram.Current->Parameters, -1, reg);
+            if (value) {
+               COPY_4V(v, value);
+            }
+            else {
+               _mesa_error(ctx, GL_INVALID_VALUE,
+                           "glGetProgramRegisterfvMESA(registerName)");
+               return;
+            }
+         }
+         break;
+      default:
+         _mesa_error(ctx, GL_INVALID_ENUM,
+                     "glGetProgramRegisterfvMESA(target)");
+         return;
+   }
+
+}
diff --git a/src/mesa/shader/program.h b/src/mesa/shader/program.h
new file mode 100644 (file)
index 0000000..f71b46f
--- /dev/null
@@ -0,0 +1,253 @@
+/*
+ * Mesa 3-D graphics library
+ * Version:  5.1
+ *
+ * Copyright (C) 1999-2003  Brian Paul   All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * BRIAN PAUL BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+/**
+ * \file program.c
+ * Vertex and fragment program support functions.
+ * \author Brian Paul
+ */
+
+
+#ifndef PROGRAM_H
+#define PROGRAM_H
+
+#include "mtypes.h"
+
+
+/* for GL_ARB_v_p and GL_ARB_f_p SWZ instruction */
+#define SWIZZLE_X    0
+#define SWIZZLE_Y    1
+#define SWIZZLE_Z    2
+#define SWIZZLE_W    3
+#define SWIZZLE_ZERO 4         /* keep these values together: KW */
+#define SWIZZLE_ONE  5         /* keep these values together: KW */
+
+
+/*
+ * Internal functions
+ */
+
+extern void
+_mesa_init_program(GLcontext *ctx);
+
+extern void
+_mesa_set_program_error(GLcontext *ctx, GLint pos, const char *string);
+
+extern const GLubyte *
+_mesa_find_line_column(const GLubyte *string, const GLubyte *pos,
+                       GLint *line, GLint *col);
+
+
+extern struct program * 
+_mesa_init_vertex_program( GLcontext *ctx, 
+                          struct vertex_program *prog, 
+                          GLenum target,
+                          GLuint id );
+
+extern struct program * 
+_mesa_init_fragment_program( GLcontext *ctx, 
+                            struct fragment_program *prog,
+                            GLenum target, 
+                            GLuint id );
+
+extern struct program *
+_mesa_new_program(GLcontext *ctx, GLenum target, GLuint id);
+
+extern void
+_mesa_delete_program(GLcontext *ctx, struct program *prog);
+
+
+
+/*
+ * Used for describing GL state referenced from inside ARB vertex and
+ * fragment programs.
+ * A string such as "state.light[0].ambient" gets translated into a
+ * sequence of tokens such as [ STATE_LIGHT, 0, STATE_AMBIENT ].
+ */
+enum state_index {
+   STATE_MATERIAL,
+
+   STATE_LIGHT,
+   STATE_LIGHTMODEL_AMBIENT,
+   STATE_LIGHTMODEL_SCENECOLOR,
+   STATE_LIGHTPROD,
+
+   STATE_TEXGEN,
+
+   STATE_FOG_COLOR,
+   STATE_FOG_PARAMS,
+
+   STATE_CLIPPLANE,
+
+   STATE_POINT_SIZE,
+   STATE_POINT_ATTENUATION,
+
+   STATE_MATRIX,
+   STATE_MODELVIEW,
+   STATE_PROJECTION,
+   STATE_MVP,
+   STATE_TEXTURE,
+   STATE_PROGRAM,
+   STATE_MATRIX_INVERSE,
+   STATE_MATRIX_TRANSPOSE,
+   STATE_MATRIX_INVTRANS,
+
+   STATE_AMBIENT,
+   STATE_DIFFUSE,
+   STATE_SPECULAR,
+   STATE_EMISSION,
+   STATE_SHININESS,
+   STATE_HALF, 
+
+   STATE_POSITION,
+   STATE_ATTENUATION,
+   STATE_SPOT_DIRECTION,
+
+   STATE_TEXGEN_EYE_S,
+   STATE_TEXGEN_EYE_T,
+   STATE_TEXGEN_EYE_R,
+   STATE_TEXGEN_EYE_Q,
+   STATE_TEXGEN_OBJECT_S,
+   STATE_TEXGEN_OBJECT_T,
+   STATE_TEXGEN_OBJECT_R,
+   STATE_TEXGEN_OBJECT_Q,
+
+   STATE_TEXENV_COLOR,
+       
+   STATE_DEPTH_RANGE,
+
+   STATE_VERTEX_PROGRAM,
+   STATE_FRAGMENT_PROGRAM,
+
+   STATE_ENV,
+   STATE_LOCAL
+};
+
+
+
+/*
+ * Named program parameters 
+ * Used for NV_fragment_program "DEFINE"d constants and "DECLARE"d parameters,
+ * and ARB_fragment_program global state references.  For the later, Name
+ * might be "state.light[0].diffuse", for example.
+ */
+
+enum parameter_type
+{
+   NAMED_PARAMETER,
+   CONSTANT,
+   STATE
+};
+
+
+struct program_parameter
+{
+   const char *Name;                   /* Null-terminated */
+   enum parameter_type Type;
+   enum state_index StateIndexes[6];   /* Global state reference */
+   GLfloat Values[4];
+};
+
+
+struct program_parameter_list
+{
+   GLuint NumParameters;
+   struct program_parameter *Parameters;
+};
+
+
+/*
+ * Program parameter functions
+ */
+
+extern struct program_parameter_list *
+_mesa_new_parameter_list(void);
+
+extern void
+_mesa_free_parameter_list(struct program_parameter_list *paramList);
+
+extern void
+_mesa_free_parameters(struct program_parameter_list *paramList);
+
+extern GLint
+_mesa_add_named_parameter(struct program_parameter_list *paramList,
+                          const char *name, const GLfloat values[4]);
+
+extern GLint
+_mesa_add_named_constant(struct program_parameter_list *paramList,
+                         const char *name, const GLfloat values[4]);
+
+extern GLint
+_mesa_add_unnamed_constant(struct program_parameter_list *paramList,
+                           const GLfloat values[4]);
+
+extern GLint
+_mesa_add_state_reference(struct program_parameter_list *paramList,
+                          GLint *stateTokens);
+
+extern GLfloat *
+_mesa_lookup_parameter_value(struct program_parameter_list *paramList,
+                             GLsizei nameLen, const char *name);
+
+extern GLint
+_mesa_lookup_parameter_index(struct program_parameter_list *paramList,
+                             GLsizei nameLen, const char *name);
+
+extern void
+_mesa_load_state_parameters(GLcontext *ctx,
+                            struct program_parameter_list *paramList);
+
+
+/*
+ * API functions
+ */
+
+extern void GLAPIENTRY
+_mesa_BindProgram(GLenum target, GLuint id);
+
+extern void GLAPIENTRY
+_mesa_DeletePrograms(GLsizei n, const GLuint *ids);
+
+extern void GLAPIENTRY
+_mesa_GenPrograms(GLsizei n, GLuint *ids);
+
+extern GLboolean GLAPIENTRY
+_mesa_IsProgram(GLuint id);
+
+
+
+/*
+ * GL_MESA_program_debug
+ */
+
+extern void
+_mesa_ProgramCallbackMESA(GLenum target, GLprogramcallbackMESA callback,
+                          GLvoid *data);
+
+extern void
+_mesa_GetProgramRegisterfvMESA(GLenum target, GLsizei len,
+                               const GLubyte *registerName, GLfloat *v);
+
+
+#endif /* PROGRAM_H */