1/*
2 * Mesa 3-D graphics library
3 * Version:  6.5.2
4 *
5 * Copyright (C) 1999-2006  Brian Paul   All Rights Reserved.
6 *
7 * Permission is hereby granted, free of charge, to any person obtaining a
8 * copy of this software and associated documentation files (the "Software"),
9 * to deal in the Software without restriction, including without limitation
10 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
11 * and/or sell copies of the Software, and to permit persons to whom the
12 * Software is furnished to do so, subject to the following conditions:
13 *
14 * The above copyright notice and this permission notice shall be included
15 * in all copies or substantial portions of the Software.
16 *
17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
18 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
20 * BRIAN PAUL BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
21 * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
22 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23 */
24
25/**
26 * \file nvvertparse.c
27 * NVIDIA vertex program parser.
28 * \author Brian Paul
29 */
30
31/*
32 * Regarding GL_NV_vertex_program, GL_NV_vertex_program1_1:
33 *
34 * Portions of this software may use or implement intellectual
35 * property owned and licensed by NVIDIA Corporation. NVIDIA disclaims
36 * any and all warranties with respect to such intellectual property,
37 * including any use thereof or modifications thereto.
38 */
39
40#include "main/glheader.h"
41#include "main/context.h"
42#include "main/imports.h"
43#include "main/nvprogram.h"
44#include "nvvertparse.h"
45#include "prog_instruction.h"
46#include "prog_parameter.h"
47#include "prog_print.h"
48#include "program.h"
49
50
51/**
52 * Current parsing state.  This structure is passed among the parsing
53 * functions and keeps track of the current parser position and various
54 * program attributes.
55 */
56struct parse_state {
57   struct gl_context *ctx;
58   const GLubyte *start;
59   const GLubyte *pos;
60   const GLubyte *curLine;
61   GLboolean isStateProgram;
62   GLboolean isPositionInvariant;
63   GLboolean isVersion1_1;
64   GLbitfield inputsRead;
65   GLbitfield outputsWritten;
66   GLboolean anyProgRegsWritten;
67   GLboolean indirectRegisterFiles;
68   GLuint numInst;                 /* number of instructions parsed */
69};
70
71
72/*
73 * Called whenever we find an error during parsing.
74 */
75static void
76record_error(struct parse_state *parseState, const char *msg, int lineNo)
77{
78#ifdef DEBUG
79   GLint line, column;
80   const GLubyte *lineStr;
81   lineStr = _mesa_find_line_column(parseState->start,
82                                    parseState->pos, &line, &column);
83   _mesa_debug(parseState->ctx,
84               "nvfragparse.c(%d): line %d, column %d:%s (%s)\n",
85               lineNo, line, column, (char *) lineStr, msg);
86   free((void *) lineStr);
87#else
88   (void) lineNo;
89#endif
90
91   /* Check that no error was already recorded.  Only record the first one. */
92   if (parseState->ctx->Program.ErrorString[0] == 0) {
93      _mesa_set_program_error(parseState->ctx,
94                              parseState->pos - parseState->start,
95                              msg);
96   }
97}
98
99
100#define RETURN_ERROR							\
101do {									\
102   record_error(parseState, "Unexpected end of input.", __LINE__);	\
103   return GL_FALSE;							\
104} while(0)
105
106#define RETURN_ERROR1(msg)						\
107do {									\
108   record_error(parseState, msg, __LINE__);				\
109   return GL_FALSE;							\
110} while(0)
111
112#define RETURN_ERROR2(msg1, msg2)					\
113do {									\
114   char err[1000];							\
115   sprintf(err, "%s %s", msg1, msg2);				\
116   record_error(parseState, err, __LINE__);				\
117   return GL_FALSE;							\
118} while(0)
119
120
121
122
123
124static GLboolean IsLetter(GLubyte b)
125{
126   return (b >= 'a' && b <= 'z') || (b >= 'A' && b <= 'Z');
127}
128
129
130static GLboolean IsDigit(GLubyte b)
131{
132   return b >= '0' && b <= '9';
133}
134
135
136static GLboolean IsWhitespace(GLubyte b)
137{
138   return b == ' ' || b == '\t' || b == '\n' || b == '\r';
139}
140
141
142/**
143 * Starting at 'str' find the next token.  A token can be an integer,
144 * an identifier or punctuation symbol.
145 * \return <= 0 we found an error, else, return number of characters parsed.
146 */
147static GLint
148GetToken(struct parse_state *parseState, GLubyte *token)
149{
150   const GLubyte *str = parseState->pos;
151   GLint i = 0, j = 0;
152
153   token[0] = 0;
154
155   /* skip whitespace and comments */
156   while (str[i] && (IsWhitespace(str[i]) || str[i] == '#')) {
157      if (str[i] == '#') {
158         /* skip comment */
159         while (str[i] && (str[i] != '\n' && str[i] != '\r')) {
160            i++;
161         }
162         if (str[i] == '\n' || str[i] == '\r')
163            parseState->curLine = str + i + 1;
164      }
165      else {
166         /* skip whitespace */
167         if (str[i] == '\n' || str[i] == '\r')
168            parseState->curLine = str + i + 1;
169         i++;
170      }
171   }
172
173   if (str[i] == 0)
174      return -i;
175
176   /* try matching an integer */
177   while (str[i] && IsDigit(str[i])) {
178      token[j++] = str[i++];
179   }
180   if (j > 0 || !str[i]) {
181      token[j] = 0;
182      return i;
183   }
184
185   /* try matching an identifier */
186   if (IsLetter(str[i])) {
187      while (str[i] && (IsLetter(str[i]) || IsDigit(str[i]))) {
188         token[j++] = str[i++];
189      }
190      token[j] = 0;
191      return i;
192   }
193
194   /* punctuation character */
195   if (str[i]) {
196      token[0] = str[i++];
197      token[1] = 0;
198      return i;
199   }
200
201   /* end of input */
202   token[0] = 0;
203   return i;
204}
205
206
207/**
208 * Get next token from input stream and increment stream pointer past token.
209 */
210static GLboolean
211Parse_Token(struct parse_state *parseState, GLubyte *token)
212{
213   GLint i;
214   i = GetToken(parseState, token);
215   if (i <= 0) {
216      parseState->pos += (-i);
217      return GL_FALSE;
218   }
219   parseState->pos += i;
220   return GL_TRUE;
221}
222
223
224/**
225 * Get next token from input stream but don't increment stream pointer.
226 */
227static GLboolean
228Peek_Token(struct parse_state *parseState, GLubyte *token)
229{
230   GLint i, len;
231   i = GetToken(parseState, token);
232   if (i <= 0) {
233      parseState->pos += (-i);
234      return GL_FALSE;
235   }
236   len = (GLint) strlen((const char *) token);
237   parseState->pos += (i - len);
238   return GL_TRUE;
239}
240
241
242/**
243 * Try to match 'pattern' as the next token after any whitespace/comments.
244 * Advance the current parsing position only if we match the pattern.
245 * \return GL_TRUE if pattern is matched, GL_FALSE otherwise.
246 */
247static GLboolean
248Parse_String(struct parse_state *parseState, const char *pattern)
249{
250   const GLubyte *m;
251   GLint i;
252
253   /* skip whitespace and comments */
254   while (IsWhitespace(*parseState->pos) || *parseState->pos == '#') {
255      if (*parseState->pos == '#') {
256         while (*parseState->pos && (*parseState->pos != '\n' && *parseState->pos != '\r')) {
257            parseState->pos += 1;
258         }
259         if (*parseState->pos == '\n' || *parseState->pos == '\r')
260            parseState->curLine = parseState->pos + 1;
261      }
262      else {
263         /* skip whitespace */
264         if (*parseState->pos == '\n' || *parseState->pos == '\r')
265            parseState->curLine = parseState->pos + 1;
266         parseState->pos += 1;
267      }
268   }
269
270   /* Try to match the pattern */
271   m = parseState->pos;
272   for (i = 0; pattern[i]; i++) {
273      if (*m != (GLubyte) pattern[i])
274         return GL_FALSE;
275      m += 1;
276   }
277   parseState->pos = m;
278
279   return GL_TRUE; /* success */
280}
281
282
283/**********************************************************************/
284
285static const char *InputRegisters[MAX_NV_VERTEX_PROGRAM_INPUTS + 1] = {
286   "OPOS", "WGHT", "NRML", "COL0", "COL1", "FOGC", "6", "7",
287   "TEX0", "TEX1", "TEX2", "TEX3", "TEX4", "TEX5", "TEX6", "TEX7", NULL
288};
289
290static const char *OutputRegisters[MAX_NV_VERTEX_PROGRAM_OUTPUTS + 1] = {
291   "HPOS", "COL0", "COL1", "FOGC",
292   "TEX0", "TEX1", "TEX2", "TEX3", "TEX4", "TEX5", "TEX6", "TEX7",
293   "PSIZ", "BFC0", "BFC1", NULL
294};
295
296
297
298/**
299 * Parse a temporary register: Rnn
300 */
301static GLboolean
302Parse_TempReg(struct parse_state *parseState, GLint *tempRegNum)
303{
304   GLubyte token[100];
305
306   /* Should be 'R##' */
307   if (!Parse_Token(parseState, token))
308      RETURN_ERROR;
309   if (token[0] != 'R')
310      RETURN_ERROR1("Expected R##");
311
312   if (IsDigit(token[1])) {
313      GLint reg = atoi((char *) (token + 1));
314      if (reg >= MAX_NV_VERTEX_PROGRAM_TEMPS)
315         RETURN_ERROR1("Bad temporary register name");
316      *tempRegNum = reg;
317   }
318   else {
319      RETURN_ERROR1("Bad temporary register name");
320   }
321
322   return GL_TRUE;
323}
324
325
326/**
327 * Parse address register "A0.x"
328 */
329static GLboolean
330Parse_AddrReg(struct parse_state *parseState)
331{
332   /* match 'A0' */
333   if (!Parse_String(parseState, "A0"))
334      RETURN_ERROR;
335
336   /* match '.' */
337   if (!Parse_String(parseState, "."))
338      RETURN_ERROR;
339
340   /* match 'x' */
341   if (!Parse_String(parseState, "x"))
342      RETURN_ERROR;
343
344   return GL_TRUE;
345}
346
347
348/**
349 * Parse absolute program parameter register "c[##]"
350 */
351static GLboolean
352Parse_AbsParamReg(struct parse_state *parseState, GLint *regNum)
353{
354   GLubyte token[100];
355
356   if (!Parse_String(parseState, "c"))
357      RETURN_ERROR;
358
359   if (!Parse_String(parseState, "["))
360      RETURN_ERROR;
361
362   if (!Parse_Token(parseState, token))
363      RETURN_ERROR;
364
365   if (IsDigit(token[0])) {
366      /* a numbered program parameter register */
367      GLint reg = atoi((char *) token);
368      if (reg >= MAX_NV_VERTEX_PROGRAM_PARAMS)
369         RETURN_ERROR1("Bad program parameter number");
370      *regNum = reg;
371   }
372   else {
373      RETURN_ERROR;
374   }
375
376   if (!Parse_String(parseState, "]"))
377      RETURN_ERROR;
378
379   return GL_TRUE;
380}
381
382
383static GLboolean
384Parse_ParamReg(struct parse_state *parseState, struct prog_src_register *srcReg)
385{
386   GLubyte token[100];
387
388   if (!Parse_String(parseState, "c"))
389      RETURN_ERROR;
390
391   if (!Parse_String(parseState, "["))
392      RETURN_ERROR;
393
394   if (!Peek_Token(parseState, token))
395      RETURN_ERROR;
396
397   if (IsDigit(token[0])) {
398      /* a numbered program parameter register */
399      GLint reg;
400      (void) Parse_Token(parseState, token);
401      reg = atoi((char *) token);
402      if (reg >= MAX_NV_VERTEX_PROGRAM_PARAMS)
403         RETURN_ERROR1("Bad program parameter number");
404      srcReg->File = PROGRAM_ENV_PARAM;
405      srcReg->Index = reg;
406   }
407   else if (strcmp((const char *) token, "A0") == 0) {
408      /* address register "A0.x" */
409      if (!Parse_AddrReg(parseState))
410         RETURN_ERROR;
411
412      srcReg->RelAddr = GL_TRUE;
413      srcReg->File = PROGRAM_ENV_PARAM;
414      parseState->indirectRegisterFiles |= (1 << srcReg->File);
415      /* Look for +/-N offset */
416      if (!Peek_Token(parseState, token))
417         RETURN_ERROR;
418
419      if (token[0] == '-' || token[0] == '+') {
420         const GLubyte sign = token[0];
421         (void) Parse_Token(parseState, token); /* consume +/- */
422
423         /* an integer should be next */
424         if (!Parse_Token(parseState, token))
425            RETURN_ERROR;
426
427         if (IsDigit(token[0])) {
428            const GLint k = atoi((char *) token);
429            if (sign == '-') {
430               if (k > 64)
431                  RETURN_ERROR1("Bad address offset");
432               srcReg->Index = -k;
433            }
434            else {
435               if (k > 63)
436                  RETURN_ERROR1("Bad address offset");
437               srcReg->Index = k;
438            }
439         }
440         else {
441            RETURN_ERROR;
442         }
443      }
444      else {
445         /* probably got a ']', catch it below */
446      }
447   }
448   else {
449      RETURN_ERROR;
450   }
451
452   /* Match closing ']' */
453   if (!Parse_String(parseState, "]"))
454      RETURN_ERROR;
455
456   return GL_TRUE;
457}
458
459
460/**
461 * Parse v[#] or v[<name>]
462 */
463static GLboolean
464Parse_AttribReg(struct parse_state *parseState, GLint *tempRegNum)
465{
466   GLubyte token[100];
467   GLint j;
468
469   /* Match 'v' */
470   if (!Parse_String(parseState, "v"))
471      RETURN_ERROR;
472
473   /* Match '[' */
474   if (!Parse_String(parseState, "["))
475      RETURN_ERROR;
476
477   /* match number or named register */
478   if (!Parse_Token(parseState, token))
479      RETURN_ERROR;
480
481   if (parseState->isStateProgram && token[0] != '0')
482      RETURN_ERROR1("Only v[0] accessible in vertex state programs");
483
484   if (IsDigit(token[0])) {
485      GLint reg = atoi((char *) token);
486      if (reg >= MAX_NV_VERTEX_PROGRAM_INPUTS)
487         RETURN_ERROR1("Bad vertex attribute register name");
488      *tempRegNum = reg;
489   }
490   else {
491      for (j = 0; InputRegisters[j]; j++) {
492         if (strcmp((const char *) token, InputRegisters[j]) == 0) {
493            *tempRegNum = j;
494            break;
495         }
496      }
497      if (!InputRegisters[j]) {
498         /* unknown input register label */
499         RETURN_ERROR2("Bad register name", token);
500      }
501   }
502
503   /* Match '[' */
504   if (!Parse_String(parseState, "]"))
505      RETURN_ERROR;
506
507   return GL_TRUE;
508}
509
510
511static GLboolean
512Parse_OutputReg(struct parse_state *parseState, GLint *outputRegNum)
513{
514   GLubyte token[100];
515   GLint start, j;
516
517   /* Match 'o' */
518   if (!Parse_String(parseState, "o"))
519      RETURN_ERROR;
520
521   /* Match '[' */
522   if (!Parse_String(parseState, "["))
523      RETURN_ERROR;
524
525   /* Get output reg name */
526   if (!Parse_Token(parseState, token))
527      RETURN_ERROR;
528
529   if (parseState->isPositionInvariant)
530      start = 1; /* skip HPOS register name */
531   else
532      start = 0;
533
534   /* try to match an output register name */
535   for (j = start; OutputRegisters[j]; j++) {
536      if (strcmp((const char *) token, OutputRegisters[j]) == 0) {
537         *outputRegNum = j;
538         break;
539      }
540   }
541   if (!OutputRegisters[j])
542      RETURN_ERROR1("Unrecognized output register name");
543
544   /* Match ']' */
545   if (!Parse_String(parseState, "]"))
546      RETURN_ERROR1("Expected ]");
547
548   return GL_TRUE;
549}
550
551
552static GLboolean
553Parse_MaskedDstReg(struct parse_state *parseState, struct prog_dst_register *dstReg)
554{
555   GLubyte token[100];
556   GLint idx;
557
558   /* Dst reg can be R<n> or o[n] */
559   if (!Peek_Token(parseState, token))
560      RETURN_ERROR;
561
562   if (token[0] == 'R') {
563      /* a temporary register */
564      dstReg->File = PROGRAM_TEMPORARY;
565      if (!Parse_TempReg(parseState, &idx))
566         RETURN_ERROR;
567      dstReg->Index = idx;
568   }
569   else if (!parseState->isStateProgram && token[0] == 'o') {
570      /* an output register */
571      dstReg->File = PROGRAM_OUTPUT;
572      if (!Parse_OutputReg(parseState, &idx))
573         RETURN_ERROR;
574      dstReg->Index = idx;
575   }
576   else if (parseState->isStateProgram && token[0] == 'c' &&
577            parseState->isStateProgram) {
578      /* absolute program parameter register */
579      /* Only valid for vertex state programs */
580      dstReg->File = PROGRAM_ENV_PARAM;
581      if (!Parse_AbsParamReg(parseState, &idx))
582         RETURN_ERROR;
583      dstReg->Index = idx;
584   }
585   else {
586      RETURN_ERROR1("Bad destination register name");
587   }
588
589   /* Parse optional write mask */
590   if (!Peek_Token(parseState, token))
591      RETURN_ERROR;
592
593   if (token[0] == '.') {
594      /* got a mask */
595      GLint k = 0;
596
597      if (!Parse_String(parseState, "."))
598         RETURN_ERROR;
599
600      if (!Parse_Token(parseState, token))
601         RETURN_ERROR;
602
603      dstReg->WriteMask = 0;
604
605      if (token[k] == 'x') {
606         dstReg->WriteMask |= WRITEMASK_X;
607         k++;
608      }
609      if (token[k] == 'y') {
610         dstReg->WriteMask |= WRITEMASK_Y;
611         k++;
612      }
613      if (token[k] == 'z') {
614         dstReg->WriteMask |= WRITEMASK_Z;
615         k++;
616      }
617      if (token[k] == 'w') {
618         dstReg->WriteMask |= WRITEMASK_W;
619         k++;
620      }
621      if (k == 0) {
622         RETURN_ERROR1("Bad writemask character");
623      }
624      return GL_TRUE;
625   }
626   else {
627      dstReg->WriteMask = WRITEMASK_XYZW;
628      return GL_TRUE;
629   }
630}
631
632
633static GLboolean
634Parse_SwizzleSrcReg(struct parse_state *parseState, struct prog_src_register *srcReg)
635{
636   GLubyte token[100];
637   GLint idx;
638
639   srcReg->RelAddr = GL_FALSE;
640
641   /* check for '-' */
642   if (!Peek_Token(parseState, token))
643      RETURN_ERROR;
644   if (token[0] == '-') {
645      (void) Parse_String(parseState, "-");
646      srcReg->Negate = NEGATE_XYZW;
647      if (!Peek_Token(parseState, token))
648         RETURN_ERROR;
649   }
650   else {
651      srcReg->Negate = NEGATE_NONE;
652   }
653
654   /* Src reg can be R<n>, c[n], c[n +/- offset], or a named vertex attrib */
655   if (token[0] == 'R') {
656      srcReg->File = PROGRAM_TEMPORARY;
657      if (!Parse_TempReg(parseState, &idx))
658         RETURN_ERROR;
659      srcReg->Index = idx;
660   }
661   else if (token[0] == 'c') {
662      if (!Parse_ParamReg(parseState, srcReg))
663         RETURN_ERROR;
664   }
665   else if (token[0] == 'v') {
666      srcReg->File = PROGRAM_INPUT;
667      if (!Parse_AttribReg(parseState, &idx))
668         RETURN_ERROR;
669      srcReg->Index = idx;
670   }
671   else {
672      RETURN_ERROR2("Bad source register name", token);
673   }
674
675   /* init swizzle fields */
676   srcReg->Swizzle = SWIZZLE_NOOP;
677
678   /* Look for optional swizzle suffix */
679   if (!Peek_Token(parseState, token))
680      RETURN_ERROR;
681   if (token[0] == '.') {
682      (void) Parse_String(parseState, ".");  /* consume . */
683
684      if (!Parse_Token(parseState, token))
685         RETURN_ERROR;
686
687      if (token[1] == 0) {
688         /* single letter swizzle */
689         if (token[0] == 'x')
690            srcReg->Swizzle = SWIZZLE_XXXX;
691         else if (token[0] == 'y')
692            srcReg->Swizzle = SWIZZLE_YYYY;
693         else if (token[0] == 'z')
694            srcReg->Swizzle = SWIZZLE_ZZZZ;
695         else if (token[0] == 'w')
696            srcReg->Swizzle = SWIZZLE_WWWW;
697         else
698            RETURN_ERROR1("Expected x, y, z, or w");
699      }
700      else {
701         /* 2, 3 or 4-component swizzle */
702         GLint k;
703
704         srcReg->Swizzle = 0;
705
706         for (k = 0; token[k] && k < 5; k++) {
707            if (token[k] == 'x')
708               srcReg->Swizzle |= 0 << (k*3);
709            else if (token[k] == 'y')
710               srcReg->Swizzle |= 1 << (k*3);
711            else if (token[k] == 'z')
712               srcReg->Swizzle |= 2 << (k*3);
713            else if (token[k] == 'w')
714               srcReg->Swizzle |= 3 << (k*3);
715            else
716               RETURN_ERROR;
717         }
718         if (k >= 5)
719            RETURN_ERROR;
720      }
721   }
722
723   return GL_TRUE;
724}
725
726
727static GLboolean
728Parse_ScalarSrcReg(struct parse_state *parseState, struct prog_src_register *srcReg)
729{
730   GLubyte token[100];
731   GLint idx;
732
733   srcReg->RelAddr = GL_FALSE;
734
735   /* check for '-' */
736   if (!Peek_Token(parseState, token))
737      RETURN_ERROR;
738   if (token[0] == '-') {
739      srcReg->Negate = NEGATE_XYZW;
740      (void) Parse_String(parseState, "-"); /* consume '-' */
741      if (!Peek_Token(parseState, token))
742         RETURN_ERROR;
743   }
744   else {
745      srcReg->Negate = NEGATE_NONE;
746   }
747
748   /* Src reg can be R<n>, c[n], c[n +/- offset], or a named vertex attrib */
749   if (token[0] == 'R') {
750      srcReg->File = PROGRAM_TEMPORARY;
751      if (!Parse_TempReg(parseState, &idx))
752         RETURN_ERROR;
753      srcReg->Index = idx;
754   }
755   else if (token[0] == 'c') {
756      if (!Parse_ParamReg(parseState, srcReg))
757         RETURN_ERROR;
758   }
759   else if (token[0] == 'v') {
760      srcReg->File = PROGRAM_INPUT;
761      if (!Parse_AttribReg(parseState, &idx))
762         RETURN_ERROR;
763      srcReg->Index = idx;
764   }
765   else {
766      RETURN_ERROR2("Bad source register name", token);
767   }
768
769   /* Look for .[xyzw] suffix */
770   if (!Parse_String(parseState, "."))
771      RETURN_ERROR;
772
773   if (!Parse_Token(parseState, token))
774      RETURN_ERROR;
775
776   if (token[0] == 'x' && token[1] == 0) {
777      srcReg->Swizzle = 0;
778   }
779   else if (token[0] == 'y' && token[1] == 0) {
780      srcReg->Swizzle = 1;
781   }
782   else if (token[0] == 'z' && token[1] == 0) {
783      srcReg->Swizzle = 2;
784   }
785   else if (token[0] == 'w' && token[1] == 0) {
786      srcReg->Swizzle = 3;
787   }
788   else {
789      RETURN_ERROR1("Bad scalar source suffix");
790   }
791
792   return GL_TRUE;
793}
794
795
796static GLint
797Parse_UnaryOpInstruction(struct parse_state *parseState,
798                         struct prog_instruction *inst,
799                         enum prog_opcode opcode)
800{
801   if (opcode == OPCODE_ABS && !parseState->isVersion1_1)
802      RETURN_ERROR1("ABS illegal for vertex program 1.0");
803
804   inst->Opcode = opcode;
805
806   /* dest reg */
807   if (!Parse_MaskedDstReg(parseState, &inst->DstReg))
808      RETURN_ERROR;
809
810   /* comma */
811   if (!Parse_String(parseState, ","))
812      RETURN_ERROR;
813
814   /* src arg */
815   if (!Parse_SwizzleSrcReg(parseState, &inst->SrcReg[0]))
816      RETURN_ERROR;
817
818   /* semicolon */
819   if (!Parse_String(parseState, ";"))
820      RETURN_ERROR;
821
822   return GL_TRUE;
823}
824
825
826static GLboolean
827Parse_BiOpInstruction(struct parse_state *parseState,
828                      struct prog_instruction *inst,
829                      enum prog_opcode opcode)
830{
831   if (opcode == OPCODE_DPH && !parseState->isVersion1_1)
832      RETURN_ERROR1("DPH illegal for vertex program 1.0");
833   if (opcode == OPCODE_SUB && !parseState->isVersion1_1)
834      RETURN_ERROR1("SUB illegal for vertex program 1.0");
835
836   inst->Opcode = opcode;
837
838   /* dest reg */
839   if (!Parse_MaskedDstReg(parseState, &inst->DstReg))
840      RETURN_ERROR;
841
842   /* comma */
843   if (!Parse_String(parseState, ","))
844      RETURN_ERROR;
845
846   /* first src arg */
847   if (!Parse_SwizzleSrcReg(parseState, &inst->SrcReg[0]))
848      RETURN_ERROR;
849
850   /* comma */
851   if (!Parse_String(parseState, ","))
852      RETURN_ERROR;
853
854   /* second src arg */
855   if (!Parse_SwizzleSrcReg(parseState, &inst->SrcReg[1]))
856      RETURN_ERROR;
857
858   /* semicolon */
859   if (!Parse_String(parseState, ";"))
860      RETURN_ERROR;
861
862   /* make sure we don't reference more than one program parameter register */
863   if (inst->SrcReg[0].File == PROGRAM_ENV_PARAM &&
864       inst->SrcReg[1].File == PROGRAM_ENV_PARAM &&
865       inst->SrcReg[0].Index != inst->SrcReg[1].Index)
866      RETURN_ERROR1("Can't reference two program parameter registers");
867
868   /* make sure we don't reference more than one vertex attribute register */
869   if (inst->SrcReg[0].File == PROGRAM_INPUT &&
870       inst->SrcReg[1].File == PROGRAM_INPUT &&
871       inst->SrcReg[0].Index != inst->SrcReg[1].Index)
872      RETURN_ERROR1("Can't reference two vertex attribute registers");
873
874   return GL_TRUE;
875}
876
877
878static GLboolean
879Parse_TriOpInstruction(struct parse_state *parseState,
880                       struct prog_instruction *inst,
881                       enum prog_opcode opcode)
882{
883   inst->Opcode = opcode;
884
885   /* dest reg */
886   if (!Parse_MaskedDstReg(parseState, &inst->DstReg))
887      RETURN_ERROR;
888
889   /* comma */
890   if (!Parse_String(parseState, ","))
891      RETURN_ERROR;
892
893   /* first src arg */
894   if (!Parse_SwizzleSrcReg(parseState, &inst->SrcReg[0]))
895      RETURN_ERROR;
896
897   /* comma */
898   if (!Parse_String(parseState, ","))
899      RETURN_ERROR;
900
901   /* second src arg */
902   if (!Parse_SwizzleSrcReg(parseState, &inst->SrcReg[1]))
903      RETURN_ERROR;
904
905   /* comma */
906   if (!Parse_String(parseState, ","))
907      RETURN_ERROR;
908
909   /* third src arg */
910   if (!Parse_SwizzleSrcReg(parseState, &inst->SrcReg[2]))
911      RETURN_ERROR;
912
913   /* semicolon */
914   if (!Parse_String(parseState, ";"))
915      RETURN_ERROR;
916
917   /* make sure we don't reference more than one program parameter register */
918   if ((inst->SrcReg[0].File == PROGRAM_ENV_PARAM &&
919        inst->SrcReg[1].File == PROGRAM_ENV_PARAM &&
920        inst->SrcReg[0].Index != inst->SrcReg[1].Index) ||
921       (inst->SrcReg[0].File == PROGRAM_ENV_PARAM &&
922        inst->SrcReg[2].File == PROGRAM_ENV_PARAM &&
923        inst->SrcReg[0].Index != inst->SrcReg[2].Index) ||
924       (inst->SrcReg[1].File == PROGRAM_ENV_PARAM &&
925        inst->SrcReg[2].File == PROGRAM_ENV_PARAM &&
926        inst->SrcReg[1].Index != inst->SrcReg[2].Index))
927      RETURN_ERROR1("Can only reference one program register");
928
929   /* make sure we don't reference more than one vertex attribute register */
930   if ((inst->SrcReg[0].File == PROGRAM_INPUT &&
931        inst->SrcReg[1].File == PROGRAM_INPUT &&
932        inst->SrcReg[0].Index != inst->SrcReg[1].Index) ||
933       (inst->SrcReg[0].File == PROGRAM_INPUT &&
934        inst->SrcReg[2].File == PROGRAM_INPUT &&
935        inst->SrcReg[0].Index != inst->SrcReg[2].Index) ||
936       (inst->SrcReg[1].File == PROGRAM_INPUT &&
937        inst->SrcReg[2].File == PROGRAM_INPUT &&
938        inst->SrcReg[1].Index != inst->SrcReg[2].Index))
939      RETURN_ERROR1("Can only reference one input register");
940
941   return GL_TRUE;
942}
943
944
945static GLboolean
946Parse_ScalarInstruction(struct parse_state *parseState,
947                        struct prog_instruction *inst,
948                        enum prog_opcode opcode)
949{
950   if (opcode == OPCODE_RCC && !parseState->isVersion1_1)
951      RETURN_ERROR1("RCC illegal for vertex program 1.0");
952
953   inst->Opcode = opcode;
954
955   /* dest reg */
956   if (!Parse_MaskedDstReg(parseState, &inst->DstReg))
957      RETURN_ERROR;
958
959   /* comma */
960   if (!Parse_String(parseState, ","))
961      RETURN_ERROR;
962
963   /* first src arg */
964   if (!Parse_ScalarSrcReg(parseState, &inst->SrcReg[0]))
965      RETURN_ERROR;
966
967   /* semicolon */
968   if (!Parse_String(parseState, ";"))
969      RETURN_ERROR;
970
971   return GL_TRUE;
972}
973
974
975static GLboolean
976Parse_AddressInstruction(struct parse_state *parseState, struct prog_instruction *inst)
977{
978   inst->Opcode = OPCODE_ARL;
979
980   /* Make ARB_vp backends happy */
981   inst->DstReg.File = PROGRAM_ADDRESS;
982   inst->DstReg.WriteMask = WRITEMASK_X;
983   inst->DstReg.Index = 0;
984
985   /* dest A0 reg */
986   if (!Parse_AddrReg(parseState))
987      RETURN_ERROR;
988
989   /* comma */
990   if (!Parse_String(parseState, ","))
991      RETURN_ERROR;
992
993   /* parse src reg */
994   if (!Parse_ScalarSrcReg(parseState, &inst->SrcReg[0]))
995      RETURN_ERROR;
996
997   /* semicolon */
998   if (!Parse_String(parseState, ";"))
999      RETURN_ERROR;
1000
1001   return GL_TRUE;
1002}
1003
1004
1005static GLboolean
1006Parse_EndInstruction(struct parse_state *parseState, struct prog_instruction *inst)
1007{
1008   GLubyte token[100];
1009
1010   inst->Opcode = OPCODE_END;
1011
1012   /* this should fail! */
1013   if (Parse_Token(parseState, token))
1014      RETURN_ERROR2("Unexpected token after END:", token);
1015   else
1016      return GL_TRUE;
1017}
1018
1019
1020/**
1021 * The PRINT instruction is Mesa-specific and is meant as a debugging aid for
1022 * the vertex program developer.
1023 * The NV_vertex_program extension grammar is modified as follows:
1024 *
1025 *  <instruction>        ::= <ARL-instruction>
1026 *                         | ...
1027 *                         | <PRINT-instruction>
1028 *
1029 *  <PRINT-instruction>  ::= "PRINT" <string literal>
1030 *                         | "PRINT" <string literal> "," <srcReg>
1031 *                         | "PRINT" <string literal> "," <dstReg>
1032 */
1033static GLboolean
1034Parse_PrintInstruction(struct parse_state *parseState, struct prog_instruction *inst)
1035{
1036   const GLubyte *str;
1037   GLubyte *msg;
1038   GLuint len;
1039   GLubyte token[100];
1040   struct prog_src_register *srcReg = &inst->SrcReg[0];
1041   GLint idx;
1042
1043   inst->Opcode = OPCODE_PRINT;
1044
1045   /* The first argument is a literal string 'just like this' */
1046   if (!Parse_String(parseState, "'"))
1047      RETURN_ERROR;
1048
1049   str = parseState->pos;
1050   for (len = 0; str[len] != '\''; len++) /* find closing quote */
1051      ;
1052   parseState->pos += len + 1;
1053   msg = (GLubyte*) malloc(len + 1);
1054
1055   memcpy(msg, str, len);
1056   msg[len] = 0;
1057   inst->Data = msg;
1058
1059   /* comma */
1060   if (Parse_String(parseState, ",")) {
1061
1062      /* The second argument is a register name */
1063      if (!Peek_Token(parseState, token))
1064         RETURN_ERROR;
1065
1066      srcReg->RelAddr = GL_FALSE;
1067      srcReg->Negate = NEGATE_NONE;
1068      srcReg->Swizzle = SWIZZLE_NOOP;
1069
1070      /* Register can be R<n>, c[n], c[n +/- offset], a named vertex attrib,
1071       * or an o[n] output register.
1072       */
1073      if (token[0] == 'R') {
1074         srcReg->File = PROGRAM_TEMPORARY;
1075         if (!Parse_TempReg(parseState, &idx))
1076            RETURN_ERROR;
1077	 srcReg->Index = idx;
1078      }
1079      else if (token[0] == 'c') {
1080         srcReg->File = PROGRAM_ENV_PARAM;
1081         if (!Parse_ParamReg(parseState, srcReg))
1082            RETURN_ERROR;
1083      }
1084      else if (token[0] == 'v') {
1085         srcReg->File = PROGRAM_INPUT;
1086         if (!Parse_AttribReg(parseState, &idx))
1087            RETURN_ERROR;
1088	 srcReg->Index = idx;
1089      }
1090      else if (token[0] == 'o') {
1091         srcReg->File = PROGRAM_OUTPUT;
1092         if (!Parse_OutputReg(parseState, &idx))
1093            RETURN_ERROR;
1094	 srcReg->Index = idx;
1095      }
1096      else {
1097         RETURN_ERROR2("Bad source register name", token);
1098      }
1099   }
1100   else {
1101      srcReg->File = PROGRAM_UNDEFINED;
1102   }
1103
1104   /* semicolon */
1105   if (!Parse_String(parseState, ";"))
1106      RETURN_ERROR;
1107
1108   return GL_TRUE;
1109}
1110
1111
1112static GLboolean
1113Parse_OptionSequence(struct parse_state *parseState,
1114                     struct prog_instruction program[])
1115{
1116   (void) program;
1117   while (1) {
1118      if (!Parse_String(parseState, "OPTION"))
1119         return GL_TRUE;  /* ok, not an OPTION statement */
1120      if (Parse_String(parseState, "NV_position_invariant")) {
1121         parseState->isPositionInvariant = GL_TRUE;
1122      }
1123      else {
1124         RETURN_ERROR1("unexpected OPTION statement");
1125      }
1126      if (!Parse_String(parseState, ";"))
1127         return GL_FALSE;
1128   }
1129}
1130
1131
1132static GLboolean
1133Parse_InstructionSequence(struct parse_state *parseState,
1134                          struct prog_instruction program[])
1135{
1136   while (1) {
1137      struct prog_instruction *inst = program + parseState->numInst;
1138
1139      /* Initialize the instruction */
1140      _mesa_init_instructions(inst, 1);
1141
1142      if (Parse_String(parseState, "MOV")) {
1143         if (!Parse_UnaryOpInstruction(parseState, inst, OPCODE_MOV))
1144            RETURN_ERROR;
1145      }
1146      else if (Parse_String(parseState, "LIT")) {
1147         if (!Parse_UnaryOpInstruction(parseState, inst, OPCODE_LIT))
1148            RETURN_ERROR;
1149      }
1150      else if (Parse_String(parseState, "ABS")) {
1151         if (!Parse_UnaryOpInstruction(parseState, inst, OPCODE_ABS))
1152            RETURN_ERROR;
1153      }
1154      else if (Parse_String(parseState, "MUL")) {
1155         if (!Parse_BiOpInstruction(parseState, inst, OPCODE_MUL))
1156            RETURN_ERROR;
1157      }
1158      else if (Parse_String(parseState, "ADD")) {
1159         if (!Parse_BiOpInstruction(parseState, inst, OPCODE_ADD))
1160            RETURN_ERROR;
1161      }
1162      else if (Parse_String(parseState, "DP3")) {
1163         if (!Parse_BiOpInstruction(parseState, inst, OPCODE_DP3))
1164            RETURN_ERROR;
1165      }
1166      else if (Parse_String(parseState, "DP4")) {
1167         if (!Parse_BiOpInstruction(parseState, inst, OPCODE_DP4))
1168            RETURN_ERROR;
1169      }
1170      else if (Parse_String(parseState, "DST")) {
1171         if (!Parse_BiOpInstruction(parseState, inst, OPCODE_DST))
1172            RETURN_ERROR;
1173      }
1174      else if (Parse_String(parseState, "MIN")) {
1175         if (!Parse_BiOpInstruction(parseState, inst, OPCODE_MIN))
1176            RETURN_ERROR;
1177      }
1178      else if (Parse_String(parseState, "MAX")) {
1179         if (!Parse_BiOpInstruction(parseState, inst, OPCODE_MAX))
1180            RETURN_ERROR;
1181      }
1182      else if (Parse_String(parseState, "SLT")) {
1183         if (!Parse_BiOpInstruction(parseState, inst, OPCODE_SLT))
1184            RETURN_ERROR;
1185      }
1186      else if (Parse_String(parseState, "SGE")) {
1187         if (!Parse_BiOpInstruction(parseState, inst, OPCODE_SGE))
1188            RETURN_ERROR;
1189      }
1190      else if (Parse_String(parseState, "DPH")) {
1191         if (!Parse_BiOpInstruction(parseState, inst, OPCODE_DPH))
1192            RETURN_ERROR;
1193      }
1194      else if (Parse_String(parseState, "SUB")) {
1195         if (!Parse_BiOpInstruction(parseState, inst, OPCODE_SUB))
1196            RETURN_ERROR;
1197      }
1198      else if (Parse_String(parseState, "MAD")) {
1199         if (!Parse_TriOpInstruction(parseState, inst, OPCODE_MAD))
1200            RETURN_ERROR;
1201      }
1202      else if (Parse_String(parseState, "RCP")) {
1203         if (!Parse_ScalarInstruction(parseState, inst, OPCODE_RCP))
1204            RETURN_ERROR;
1205      }
1206      else if (Parse_String(parseState, "RSQ")) {
1207         if (!Parse_ScalarInstruction(parseState, inst, OPCODE_RSQ))
1208            RETURN_ERROR;
1209      }
1210      else if (Parse_String(parseState, "EXP")) {
1211         if (!Parse_ScalarInstruction(parseState, inst, OPCODE_EXP))
1212            RETURN_ERROR;
1213      }
1214      else if (Parse_String(parseState, "LOG")) {
1215         if (!Parse_ScalarInstruction(parseState, inst, OPCODE_LOG))
1216            RETURN_ERROR;
1217      }
1218      else if (Parse_String(parseState, "RCC")) {
1219         if (!Parse_ScalarInstruction(parseState, inst, OPCODE_RCC))
1220            RETURN_ERROR;
1221      }
1222      else if (Parse_String(parseState, "ARL")) {
1223         if (!Parse_AddressInstruction(parseState, inst))
1224            RETURN_ERROR;
1225      }
1226      else if (Parse_String(parseState, "PRINT")) {
1227         if (!Parse_PrintInstruction(parseState, inst))
1228            RETURN_ERROR;
1229      }
1230      else if (Parse_String(parseState, "END")) {
1231         if (!Parse_EndInstruction(parseState, inst))
1232            RETURN_ERROR;
1233         else {
1234            parseState->numInst++;
1235            return GL_TRUE;  /* all done */
1236         }
1237      }
1238      else {
1239         /* bad instruction name */
1240         RETURN_ERROR1("Unexpected token");
1241      }
1242
1243      /* examine input/output registers */
1244      if (inst->DstReg.File == PROGRAM_OUTPUT)
1245         parseState->outputsWritten |= (1 << inst->DstReg.Index);
1246      else if (inst->DstReg.File == PROGRAM_ENV_PARAM)
1247         parseState->anyProgRegsWritten = GL_TRUE;
1248
1249      if (inst->SrcReg[0].File == PROGRAM_INPUT)
1250         parseState->inputsRead |= (1 << inst->SrcReg[0].Index);
1251      if (inst->SrcReg[1].File == PROGRAM_INPUT)
1252         parseState->inputsRead |= (1 << inst->SrcReg[1].Index);
1253      if (inst->SrcReg[2].File == PROGRAM_INPUT)
1254         parseState->inputsRead |= (1 << inst->SrcReg[2].Index);
1255
1256      parseState->numInst++;
1257
1258      if (parseState->numInst >= MAX_NV_VERTEX_PROGRAM_INSTRUCTIONS)
1259         RETURN_ERROR1("Program too long");
1260   }
1261
1262   RETURN_ERROR;
1263}
1264
1265
1266static GLboolean
1267Parse_Program(struct parse_state *parseState,
1268              struct prog_instruction instBuffer[])
1269{
1270   if (parseState->isVersion1_1) {
1271      if (!Parse_OptionSequence(parseState, instBuffer)) {
1272         return GL_FALSE;
1273      }
1274   }
1275   return Parse_InstructionSequence(parseState, instBuffer);
1276}
1277
1278
1279/**
1280 * Parse/compile the 'str' returning the compiled 'program'.
1281 * ctx->Program.ErrorPos will be -1 if successful.  Otherwise, ErrorPos
1282 * indicates the position of the error in 'str'.
1283 */
1284void
1285_mesa_parse_nv_vertex_program(struct gl_context *ctx, GLenum dstTarget,
1286                              const GLubyte *str, GLsizei len,
1287                              struct gl_vertex_program *program)
1288{
1289   struct parse_state parseState;
1290   struct prog_instruction instBuffer[MAX_NV_VERTEX_PROGRAM_INSTRUCTIONS];
1291   struct prog_instruction *newInst;
1292   GLenum target;
1293   GLubyte *programString;
1294
1295   /* Make a null-terminated copy of the program string */
1296   programString = (GLubyte *) MALLOC(len + 1);
1297   if (!programString) {
1298      _mesa_error(ctx, GL_OUT_OF_MEMORY, "glLoadProgramNV");
1299      return;
1300   }
1301   memcpy(programString, str, len);
1302   programString[len] = 0;
1303
1304   /* Get ready to parse */
1305   parseState.ctx = ctx;
1306   parseState.start = programString;
1307   parseState.isPositionInvariant = GL_FALSE;
1308   parseState.isVersion1_1 = GL_FALSE;
1309   parseState.numInst = 0;
1310   parseState.inputsRead = 0;
1311   parseState.outputsWritten = 0;
1312   parseState.anyProgRegsWritten = GL_FALSE;
1313   parseState.indirectRegisterFiles = 0x0;
1314
1315   /* Reset error state */
1316   _mesa_set_program_error(ctx, -1, NULL);
1317
1318   /* check the program header */
1319   if (strncmp((const char *) programString, "!!VP1.0", 7) == 0) {
1320      target = GL_VERTEX_PROGRAM_NV;
1321      parseState.pos = programString + 7;
1322      parseState.isStateProgram = GL_FALSE;
1323   }
1324   else if (strncmp((const char *) programString, "!!VP1.1", 7) == 0) {
1325      target = GL_VERTEX_PROGRAM_NV;
1326      parseState.pos = programString + 7;
1327      parseState.isStateProgram = GL_FALSE;
1328      parseState.isVersion1_1 = GL_TRUE;
1329   }
1330   else if (strncmp((const char *) programString, "!!VSP1.0", 8) == 0) {
1331      target = GL_VERTEX_STATE_PROGRAM_NV;
1332      parseState.pos = programString + 8;
1333      parseState.isStateProgram = GL_TRUE;
1334   }
1335   else {
1336      /* invalid header */
1337      ctx->Program.ErrorPos = 0;
1338      _mesa_error(ctx, GL_INVALID_OPERATION, "glLoadProgramNV(bad header)");
1339      return;
1340   }
1341
1342   /* make sure target and header match */
1343   if (target != dstTarget) {
1344      _mesa_error(ctx, GL_INVALID_OPERATION,
1345                  "glLoadProgramNV(target mismatch)");
1346      return;
1347   }
1348
1349
1350   if (Parse_Program(&parseState, instBuffer)) {
1351      gl_state_index state_tokens[STATE_LENGTH] = {0, 0, 0, 0, 0};
1352      int i;
1353
1354      /* successful parse! */
1355
1356      if (parseState.isStateProgram) {
1357         if (!parseState.anyProgRegsWritten) {
1358            _mesa_error(ctx, GL_INVALID_OPERATION,
1359                        "glLoadProgramNV(c[#] not written)");
1360            return;
1361         }
1362      }
1363      else {
1364         if (!parseState.isPositionInvariant &&
1365             !(parseState.outputsWritten & (1 << VERT_RESULT_HPOS))) {
1366            /* bit 1 = HPOS register */
1367            _mesa_error(ctx, GL_INVALID_OPERATION,
1368                        "glLoadProgramNV(HPOS not written)");
1369            return;
1370         }
1371      }
1372
1373      /* copy the compiled instructions */
1374      assert(parseState.numInst <= MAX_NV_VERTEX_PROGRAM_INSTRUCTIONS);
1375      newInst = _mesa_alloc_instructions(parseState.numInst);
1376      if (!newInst) {
1377         _mesa_error(ctx, GL_OUT_OF_MEMORY, "glLoadProgramNV");
1378         free(programString);
1379         return;  /* out of memory */
1380      }
1381      _mesa_copy_instructions(newInst, instBuffer, parseState.numInst);
1382
1383      /* install the program */
1384      program->Base.Target = target;
1385      if (program->Base.String) {
1386         free(program->Base.String);
1387      }
1388      program->Base.String = programString;
1389      program->Base.Format = GL_PROGRAM_FORMAT_ASCII_ARB;
1390      if (program->Base.Instructions) {
1391         free(program->Base.Instructions);
1392      }
1393      program->Base.Instructions = newInst;
1394      program->Base.InputsRead = parseState.inputsRead;
1395      if (parseState.isPositionInvariant)
1396         program->Base.InputsRead |= VERT_BIT_POS;
1397      program->Base.NumInstructions = parseState.numInst;
1398      program->Base.OutputsWritten = parseState.outputsWritten;
1399      program->IsPositionInvariant = parseState.isPositionInvariant;
1400      program->IsNVProgram = GL_TRUE;
1401
1402#ifdef DEBUG_foo
1403      printf("--- glLoadProgramNV result ---\n");
1404      _mesa_fprint_program_opt(stdout, &program->Base, PROG_PRINT_NV, 0);
1405      printf("------------------------------\n");
1406#endif
1407
1408      if (program->Base.Parameters)
1409	 _mesa_free_parameter_list(program->Base.Parameters);
1410
1411      program->Base.Parameters = _mesa_new_parameter_list ();
1412      program->Base.NumParameters = 0;
1413
1414      program->Base.IndirectRegisterFiles = parseState.indirectRegisterFiles;
1415
1416      state_tokens[0] = STATE_VERTEX_PROGRAM;
1417      state_tokens[1] = STATE_ENV;
1418      /* Add refs to all of the potential params, in order.  If we want to not
1419       * upload everything, _mesa_layout_parameters is the answer.
1420       */
1421      for (i = 0; i < MAX_NV_VERTEX_PROGRAM_PARAMS; i++) {
1422	 GLint index;
1423	 state_tokens[2] = i;
1424	 index = _mesa_add_state_reference(program->Base.Parameters,
1425					   state_tokens);
1426	 assert(index == i);
1427	 (void)index;
1428      }
1429      program->Base.NumParameters = program->Base.Parameters->NumParameters;
1430
1431      _mesa_setup_nv_temporary_count(&program->Base);
1432      _mesa_emit_nv_temp_initialization(ctx, &program->Base);
1433   }
1434   else {
1435      /* Error! */
1436      _mesa_error(ctx, GL_INVALID_OPERATION, "glLoadProgramNV");
1437      /* NOTE: _mesa_set_program_error would have been called already */
1438      /* GL_NV_vertex_program isn't supposed to set the error string
1439       * so we reset it here.
1440       */
1441      _mesa_set_program_error(ctx, ctx->Program.ErrorPos, NULL);
1442   }
1443}
1444
1445
1446const char *
1447_mesa_nv_vertex_input_register_name(GLuint i)
1448{
1449   ASSERT(i < MAX_NV_VERTEX_PROGRAM_INPUTS);
1450   return InputRegisters[i];
1451}
1452
1453
1454const char *
1455_mesa_nv_vertex_output_register_name(GLuint i)
1456{
1457   ASSERT(i < MAX_NV_VERTEX_PROGRAM_OUTPUTS);
1458   return OutputRegisters[i];
1459}
1460
1461