Minor tweaks to help out at a driver level.
[mesa.git] / src / mesa / main / nvvertparse.c
index 83215cb621a938c7344addc0f990fac7e84c30c3..15451adc71f149b267ad1875847ce8a0aed62698 100644 (file)
@@ -1,5 +1,3 @@
-/* $Id: nvvertparse.c,v 1.3 2003/02/25 19:27:54 brianp Exp $ */
-
 /*
  * Mesa 3-D graphics library
  * Version:  5.1
 
 /**
  * \file nvvertparse.c
- * \brief NVIDIA vertex program parser.
+ * 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"
@@ -40,6 +46,8 @@
 #include "nvprogram.h"
 #include "nvvertparse.h"
 #include "nvvertprog.h"
+#include "program.h"
+
 
 /**
  * Current parsing state.  This structure is passed among the parsing
  * 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;
-   GLuint progRegsWritten;
+   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');
@@ -82,8 +145,9 @@ static GLboolean IsWhitespace(GLubyte b)
  * \return <= 0 we found an error, else, return number of characters parsed.
  */
 static GLint
-GetToken(const GLubyte *str, GLubyte *token)
+GetToken(struct parse_state *parseState, GLubyte *token)
 {
+   const GLubyte *str = parseState->pos;
    GLint i = 0, j = 0;
 
    token[0] = 0;
@@ -95,9 +159,13 @@ GetToken(const GLubyte *str, GLubyte *token)
          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++;
       }
    }
@@ -123,7 +191,7 @@ GetToken(const GLubyte *str, GLubyte *token)
       return i;
    }
 
-   /* punctuation */
+   /* punctuation character */
    if (str[i]) {
       token[0] = str[i++];
       token[1] = 0;
@@ -143,7 +211,7 @@ static GLboolean
 Parse_Token(struct parse_state *parseState, GLubyte *token)
 {
    GLint i;
-   i = GetToken(parseState->pos, token);
+   i = GetToken(parseState, token);
    if (i <= 0) {
       parseState->pos += (-i);
       return GL_FALSE;
@@ -160,7 +228,7 @@ static GLboolean
 Peek_Token(struct parse_state *parseState, GLubyte *token)
 {
    GLint i, len;
-   i = GetToken(parseState->pos, token);
+   i = GetToken(parseState, token);
    if (i <= 0) {
       parseState->pos += (-i);
       return GL_FALSE;
@@ -188,9 +256,13 @@ Parse_String(struct parse_state *parseState, const char *pattern)
          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;
       }
    }
@@ -210,70 +282,27 @@ Parse_String(struct parse_state *parseState, const char *pattern)
 
 /**********************************************************************/
 
-static const char *InputRegisters[] = {
+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[] = {
+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", NULL
+   "ABS", "END",
+   /* GL_ARB_vertex_program */
+   "FLR", "FRC", "EX2", "LG2", "POW", "XPD", "SWZ",
+   NULL
 };
 
 
-#ifdef DEBUG
-
-#define RETURN_ERROR                                           \
-do {                                                           \
-   _mesa_printf("vert prog error at %d\n", __LINE__);          \
-   return GL_FALSE;                                            \
-} while(0)
-
-#define RETURN_ERROR1(msg)                                     \
-do {                                                           \
-   _mesa_printf("vert prog error at %d: %s\n", __LINE__, msg); \
-   return GL_FALSE;                                            \
-} while(0)
-
-#define RETURN_ERROR2(msg1, msg2)                                      \
-do {                                                                   \
-   _mesa_printf("vert prog error at %d: %s %s\n", __LINE__, msg1, msg2);\
-   return GL_FALSE;                                                    \
-} while(0)
-
-#else
-
-#define RETURN_ERROR                return GL_FALSE
-#define RETURN_ERROR1(msg1)         return GL_FALSE
-#define RETURN_ERROR2(msg1, msg2)   return GL_FALSE
-
-#endif
-
-
-static GLuint
-IsProgRegister(GLuint r)
-{
-   return (GLuint) (r >= VP_PROG_REG_START && r <= VP_PROG_REG_END);
-}
-
-static GLuint
-IsInputRegister(GLuint r)
-{
-   return (GLuint) (r >= VP_INPUT_REG_START && r <= VP_INPUT_REG_END);
-}
-
-static GLuint
-IsOutputRegister(GLuint r)
-{
-   return (GLuint) (r >= VP_OUTPUT_REG_START && r <= VP_OUTPUT_REG_END);
-}
-
 
 /**
  * Parse a temporary register: Rnn
@@ -291,9 +320,9 @@ Parse_TempReg(struct parse_state *parseState, GLint *tempRegNum)
 
    if (IsDigit(token[1])) {
       GLint reg = _mesa_atoi((char *) (token + 1));
-      if (reg >= VP_NUM_TEMP_REGS)
+      if (reg >= MAX_NV_VERTEX_PROGRAM_TEMPS)
          RETURN_ERROR1("Bad temporary register name");
-      *tempRegNum = VP_TEMP_REG_START + reg;
+      *tempRegNum = reg;
    }
    else {
       RETURN_ERROR1("Bad temporary register name");
@@ -345,9 +374,9 @@ Parse_AbsParamReg(struct parse_state *parseState, GLint *regNum)
    if (IsDigit(token[0])) {
       /* a numbered program parameter register */
       GLint reg = _mesa_atoi((char *) token);
-      if (reg >= VP_NUM_PROG_REGS)
-         RETURN_ERROR1("Bad constant program number");
-      *regNum = VP_PROG_REG_START + reg;
+      if (reg >= MAX_NV_VERTEX_PROGRAM_PARAMS)
+         RETURN_ERROR1("Bad program parameter number");
+      *regNum = reg;
    }
    else {
       RETURN_ERROR;
@@ -379,9 +408,10 @@ Parse_ParamReg(struct parse_state *parseState, struct vp_src_register *srcReg)
       GLint reg;
       (void) Parse_Token(parseState, token);
       reg = _mesa_atoi((char *) token);
-      if (reg >= VP_NUM_PROG_REGS)
-         RETURN_ERROR1("Bad constant program number");
-      srcReg->Register = VP_PROG_REG_START + reg;
+      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" */
@@ -389,8 +419,7 @@ Parse_ParamReg(struct parse_state *parseState, struct vp_src_register *srcReg)
          RETURN_ERROR;
 
       srcReg->RelAddr = GL_TRUE;
-      srcReg->Register = 0;
-
+      srcReg->File = PROGRAM_ENV_PARAM;
       /* Look for +/-N offset */
       if (!Peek_Token(parseState, token))
          RETURN_ERROR;
@@ -408,12 +437,12 @@ Parse_ParamReg(struct parse_state *parseState, struct vp_src_register *srcReg)
             if (sign == '-') {
                if (k > 64)
                   RETURN_ERROR1("Bad address offset");
-               srcReg->Register = -k;
+               srcReg->Index = -k;
             }
             else {
                if (k > 63)
                   RETURN_ERROR1("Bad address offset");
-               srcReg->Register = k;
+               srcReg->Index = k;
             }
          }
          else {
@@ -462,14 +491,14 @@ Parse_AttribReg(struct parse_state *parseState, GLint *tempRegNum)
 
    if (IsDigit(token[0])) {
       GLint reg = _mesa_atoi((char *) token);
-      if (reg >= VP_NUM_INPUT_REGS)
+      if (reg >= MAX_NV_VERTEX_PROGRAM_INPUTS)
          RETURN_ERROR1("Bad vertex attribute register name");
-      *tempRegNum = VP_INPUT_REG_START + reg;
+      *tempRegNum = reg;
    }
    else {
       for (j = 0; InputRegisters[j]; j++) {
          if (_mesa_strcmp((const char *) token, InputRegisters[j]) == 0) {
-            *tempRegNum = VP_INPUT_REG_START + j;
+            *tempRegNum = j;
             break;
          }
       }
@@ -513,7 +542,7 @@ Parse_OutputReg(struct parse_state *parseState, GLint *outputRegNum)
    /* try to match an output register name */
    for (j = start; OutputRegisters[j]; j++) {
       if (_mesa_strcmp((const char *) token, OutputRegisters[j]) == 0) {
-         *outputRegNum = VP_OUTPUT_REG_START + j;
+         *outputRegNum = j;
          break;
       }
    }
@@ -539,17 +568,20 @@ Parse_MaskedDstReg(struct parse_state *parseState, struct vp_dst_register *dstRe
 
    if (token[0] == 'R') {
       /* a temporary register */
-      if (!Parse_TempReg(parseState, &dstReg->Register))
+      dstReg->File = PROGRAM_TEMPORARY;
+      if (!Parse_TempReg(parseState, &dstReg->Index))
          RETURN_ERROR;
    }
    else if (!parseState->isStateProgram && token[0] == 'o') {
       /* an output register */
-      if (!Parse_OutputReg(parseState, &dstReg->Register))
+      dstReg->File = PROGRAM_OUTPUT;
+      if (!Parse_OutputReg(parseState, &dstReg->Index))
          RETURN_ERROR;
    }
    else if (parseState->isStateProgram && token[0] == 'c') {
       /* absolute program parameter register */
-      if (!Parse_AbsParamReg(parseState, &dstReg->Register))
+      dstReg->File = PROGRAM_ENV_PARAM;
+      if (!Parse_AbsParamReg(parseState, &dstReg->Index))
          RETURN_ERROR;
    }
    else {
@@ -628,7 +660,8 @@ Parse_SwizzleSrcReg(struct parse_state *parseState, struct vp_src_register *srcR
 
    /* Src reg can be R<n>, c[n], c[n +/- offset], or a named vertex attrib */
    if (token[0] == 'R') {
-      if (!Parse_TempReg(parseState, &srcReg->Register))
+      srcReg->File = PROGRAM_TEMPORARY;
+      if (!Parse_TempReg(parseState, &srcReg->Index))
          RETURN_ERROR;
    }
    else if (token[0] == 'c') {
@@ -636,7 +669,8 @@ Parse_SwizzleSrcReg(struct parse_state *parseState, struct vp_src_register *srcR
          RETURN_ERROR;
    }
    else if (token[0] == 'v') {
-      if (!Parse_AttribReg(parseState, &srcReg->Register))
+      srcReg->File = PROGRAM_INPUT;
+      if (!Parse_AttribReg(parseState, &srcReg->Index))
          RETURN_ERROR;
    }
    else {
@@ -717,7 +751,8 @@ Parse_ScalarSrcReg(struct parse_state *parseState, struct vp_src_register *srcRe
 
    /* Src reg can be R<n>, c[n], c[n +/- offset], or a named vertex attrib */
    if (token[0] == 'R') {
-      if (!Parse_TempReg(parseState, &srcReg->Register))
+      srcReg->File = PROGRAM_TEMPORARY;
+      if (!Parse_TempReg(parseState, &srcReg->Index))
          RETURN_ERROR;
    }
    else if (token[0] == 'c') {
@@ -725,7 +760,8 @@ Parse_ScalarSrcReg(struct parse_state *parseState, struct vp_src_register *srcRe
          RETURN_ERROR;
    }
    else if (token[0] == 'v') {
-      if (!Parse_AttribReg(parseState, &srcReg->Register))
+      srcReg->File = PROGRAM_INPUT;
+      if (!Parse_AttribReg(parseState, &srcReg->Index))
          RETURN_ERROR;
    }
    else {
@@ -768,6 +804,7 @@ Parse_UnaryOpInstruction(struct parse_state *parseState,
       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))
@@ -799,6 +836,7 @@ Parse_BiOpInstruction(struct parse_state *parseState,
       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))
@@ -825,15 +863,15 @@ Parse_BiOpInstruction(struct parse_state *parseState,
       RETURN_ERROR;
 
    /* make sure we don't reference more than one program parameter register */
-   if (IsProgRegister(inst->SrcReg[0].Register) &&
-       IsProgRegister(inst->SrcReg[1].Register) &&
-       inst->SrcReg[0].Register != inst->SrcReg[1].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 (IsInputRegister(inst->SrcReg[0].Register) &&
-       IsInputRegister(inst->SrcReg[1].Register) &&
-       inst->SrcReg[0].Register != inst->SrcReg[1].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;
@@ -845,6 +883,7 @@ 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))
@@ -879,27 +918,27 @@ Parse_TriOpInstruction(struct parse_state *parseState,
       RETURN_ERROR;
 
    /* make sure we don't reference more than one program parameter register */
-   if ((IsProgRegister(inst->SrcReg[0].Register) &&
-        IsProgRegister(inst->SrcReg[1].Register) &&
-        inst->SrcReg[0].Register != inst->SrcReg[1].Register) ||
-       (IsProgRegister(inst->SrcReg[0].Register) &&
-        IsProgRegister(inst->SrcReg[2].Register) &&
-        inst->SrcReg[0].Register != inst->SrcReg[2].Register) ||
-       (IsProgRegister(inst->SrcReg[1].Register) &&
-        IsProgRegister(inst->SrcReg[2].Register) &&
-        inst->SrcReg[1].Register != inst->SrcReg[2].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 ((IsInputRegister(inst->SrcReg[0].Register) &&
-        IsInputRegister(inst->SrcReg[1].Register) &&
-        inst->SrcReg[0].Register != inst->SrcReg[1].Register) ||
-       (IsInputRegister(inst->SrcReg[0].Register) &&
-        IsInputRegister(inst->SrcReg[2].Register) &&
-        inst->SrcReg[0].Register != inst->SrcReg[2].Register) ||
-       (IsInputRegister(inst->SrcReg[1].Register) &&
-        IsInputRegister(inst->SrcReg[2].Register) &&
-        inst->SrcReg[1].Register != inst->SrcReg[2].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;
@@ -914,6 +953,7 @@ Parse_ScalarInstruction(struct parse_state *parseState,
       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))
@@ -939,6 +979,7 @@ 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))
@@ -966,6 +1007,7 @@ 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))
@@ -1002,10 +1044,10 @@ Parse_InstructionSequence(struct parse_state *parseState,
       struct vp_instruction *inst = program + parseState->numInst;
 
       /* Initialize the instruction */
-      inst->SrcReg[0].Register = -1;
-      inst->SrcReg[1].Register = -1;
-      inst->SrcReg[2].Register = -1;
-      inst->DstReg.Register = -1;
+      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))
@@ -1105,26 +1147,17 @@ Parse_InstructionSequence(struct parse_state *parseState,
       }
 
       /* examine input/output registers */
-      {
-         const GLint srcReg0 = inst->SrcReg[0].Register;
-         const GLint srcReg1 = inst->SrcReg[1].Register;
-         const GLint srcReg2 = inst->SrcReg[2].Register;
-         const GLint dstReg  = inst->DstReg.Register;
-
-         if (IsOutputRegister(dstReg))
-            parseState->outputsWritten |= (1 << (dstReg - VP_OUTPUT_REG_START));
-         else if (IsProgRegister(dstReg))
-            parseState->progRegsWritten |= (1 << (dstReg - VP_PROG_REG_START));
-
-         if (IsInputRegister(srcReg0) && !inst->SrcReg[0].RelAddr)
-            parseState->inputsRead |= (1 << (srcReg0 - VP_INPUT_REG_START));
-
-         if (IsInputRegister(srcReg1) && !inst->SrcReg[1].RelAddr)
-            parseState->inputsRead |= (1 << (srcReg1 - VP_INPUT_REG_START));
-
-         if (IsInputRegister(srcReg2) && !inst->SrcReg[2].RelAddr)
-            parseState->inputsRead |= (1 << (srcReg2 - VP_INPUT_REG_START));
-      }
+      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++;
 
@@ -1174,13 +1207,18 @@ _mesa_parse_nv_vertex_program(GLcontext *ctx, GLenum dstTarget,
    MEMCPY(programString, str, len);
    programString[len] = 0;
 
-   /* get ready to parse */
+   /* 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.progRegsWritten = 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) {
@@ -1218,7 +1256,7 @@ _mesa_parse_nv_vertex_program(GLcontext *ctx, GLenum dstTarget,
       /* successful parse! */
 
       if (parseState.isStateProgram) {
-         if (parseState.progRegsWritten == 0) {
+         if (!parseState.anyProgRegsWritten) {
             _mesa_error(ctx, GL_INVALID_OPERATION,
                         "glLoadProgramNV(c[#] not written)");
             return;
@@ -1252,6 +1290,7 @@ _mesa_parse_nv_vertex_program(GLcontext *ctx, GLenum dstTarget,
          FREE(program->Base.String);
       }
       program->Base.String = programString;
+      program->Base.Format = GL_PROGRAM_FORMAT_ASCII_ARB;
       if (program->Instructions) {
          FREE(program->Instructions);
       }
@@ -1260,7 +1299,7 @@ _mesa_parse_nv_vertex_program(GLcontext *ctx, GLenum dstTarget,
       program->OutputsWritten = parseState.outputsWritten;
       program->IsPositionInvariant = parseState.isPositionInvariant;
 
-#ifdef DEBUG_foo
+#ifdef DEBUG
       _mesa_printf("--- glLoadProgramNV result ---\n");
       _mesa_print_nv_vertex_program(program);
       _mesa_printf("------------------------------\n");
@@ -1268,19 +1307,12 @@ _mesa_parse_nv_vertex_program(GLcontext *ctx, GLenum dstTarget,
    }
    else {
       /* Error! */
-      ctx->Program.ErrorPos = parseState.pos - str;
       _mesa_error(ctx, GL_INVALID_OPERATION, "glLoadProgramNV");
-#ifdef DEBUG
-      {
-         GLint line, column;
-         const GLubyte *lineStr;
-         lineStr = _mesa_find_line_column(programString,
-                                          parseState.pos, &line, &column);
-         _mesa_debug(ctx, "Parse error on line %d, column %d:%s\n",
-                     line, column, (char *) lineStr);
-         _mesa_free((void *) lineStr);
-      }
-#endif
+      /* 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);
    }
 }
 
@@ -1292,27 +1324,25 @@ PrintSrcReg(const struct vp_src_register *src)
    if (src->Negate)
       _mesa_printf("-");
    if (src->RelAddr) {
-      if (src->Register > 0)
-         _mesa_printf("c[A0.x + %d]", src->Register);
-      else if (src->Register < 0)
-         _mesa_printf("c[A0.x - %d]", -src->Register);
+      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->Register >= VP_OUTPUT_REG_START
-       && src->Register <= VP_OUTPUT_REG_END) {
-      _mesa_printf("o[%s]", OutputRegisters[src->Register - VP_OUTPUT_REG_START]);
+   else if (src->File == PROGRAM_OUTPUT) {
+      _mesa_printf("o[%s]", OutputRegisters[src->Index]);
    }
-   else if (src->Register >= VP_INPUT_REG_START
-            && src->Register <= VP_INPUT_REG_END) {
-      _mesa_printf("v[%s]", InputRegisters[src->Register - VP_INPUT_REG_START]);
+   else if (src->File == PROGRAM_INPUT) {
+      _mesa_printf("v[%s]", InputRegisters[src->Index]);
    }
-   else if (src->Register >= VP_PROG_REG_START
-            && src->Register <= VP_PROG_REG_END) {
-      _mesa_printf("c[%d]", src->Register - VP_PROG_REG_START);
+   else if (src->File == PROGRAM_ENV_PARAM) {
+      _mesa_printf("c[%d]", src->Index);
    }
    else {
-      _mesa_printf("R%d", src->Register - VP_TEMP_REG_START);
+      ASSERT(src->File == PROGRAM_TEMPORARY);
+      _mesa_printf("R%d", src->Index);
    }
 
    if (src->Swizzle[0] == src->Swizzle[1] &&
@@ -1339,20 +1369,18 @@ PrintDstReg(const struct vp_dst_register *dst)
    GLint w = dst->WriteMask[0] + dst->WriteMask[1]
            + dst->WriteMask[2] + dst->WriteMask[3];
 
-   if (dst->Register >= VP_OUTPUT_REG_START
-       && dst->Register <= VP_OUTPUT_REG_END) {
-      _mesa_printf("o[%s]", OutputRegisters[dst->Register - VP_OUTPUT_REG_START]);
+   if (dst->File == PROGRAM_OUTPUT) {
+      _mesa_printf("o[%s]", OutputRegisters[dst->Index]);
    }
-   else if (dst->Register >= VP_INPUT_REG_START
-            && dst->Register <= VP_INPUT_REG_END) {
-      _mesa_printf("v[%s]", InputRegisters[dst->Register - VP_INPUT_REG_START]);
+   else if (dst->File == PROGRAM_INPUT) {
+      _mesa_printf("v[%s]", InputRegisters[dst->Index]);
    }
-   else if (dst->Register >= VP_PROG_REG_START
-            && dst->Register <= VP_PROG_REG_END) {
-      _mesa_printf("c[%d]", dst->Register - VP_PROG_REG_START);
+   else if (dst->File == PROGRAM_ENV_PARAM) {
+      _mesa_printf("c[%d]", dst->Index);
    }
    else {
-      _mesa_printf("R%d", dst->Register - VP_TEMP_REG_START);
+      ASSERT(dst->File == PROGRAM_TEMPORARY);
+      _mesa_printf("R%d", dst->Index);
    }
 
    if (w != 0 && w != 4) {
@@ -1370,15 +1398,12 @@ PrintDstReg(const struct vp_dst_register *dst)
 
 
 /**
- * Print (unparse) the given vertex program.  Just for debugging.
+ * Print a single NVIDIA vertex program instruction.
  */
 void
-_mesa_print_nv_vertex_program(const struct vertex_program *program)
+_mesa_print_nv_vertex_instruction(const struct vp_instruction *inst)
 {
-   const struct vp_instruction *inst;
-
-   for (inst = program->Instructions; ; inst++) {
-      switch (inst->Opcode) {
+   switch (inst->Opcode) {
       case VP_OPCODE_MOV:
       case VP_OPCODE_LIT:
       case VP_OPCODE_RCP:
@@ -1430,10 +1455,40 @@ _mesa_print_nv_vertex_program(const struct vertex_program *program)
          break;
       case VP_OPCODE_END:
          _mesa_printf("END\n");
-         return;
+         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];
+}