fix unitialized values in radeonClear
[mesa.git] / src / mesa / tnl / t_vb_arbprogram_sse.c
index 83be1d2d9f98a9a225c3fde48a63e8107a8b011c..b9126d6d8868265c307cae30e1d1cbb17f2d6dea 100644 (file)
 /**
  * \file t_vb_arb_program_sse.c
  *
- * Translate simplified vertex_program representation to x86/SSE/SSE2
- * machine code using mesa's rtasm runtime assembler.
+ * Translate simplified vertex_program representation to
+ * x86/x87/SSE/SSE2 machine code using mesa's rtasm runtime assembler.
+ *
+ * This is very much a first attempt - build something that works.
+ * There are probably better approaches for applying SSE to vertex
+ * programs, and the whole thing is crying out for static analysis of
+ * the programs to avoid redundant operations.
  *
  * \author Keith Whitwell
  */
@@ -38,6 +43,7 @@
 #include "mtypes.h"
 #include "arbprogparse.h"
 #include "program.h"
+#include "program_instruction.h"
 #include "math/m_matrix.h"
 #include "math/m_translate.h"
 #include "t_context.h"
@@ -48,7 +54,6 @@
 #include "x86/rtasm/x86sse.h"
 #include "x86/common_x86_asm.h"
 
-
 #define X    0
 #define Y    1
 #define Z    2
 
 /* Reg usage:
  *
- * EAX - point to 'm->File[0]'
+ * EAX - temp
+ * EBX - point to 'm->File[0]'
  * ECX - point to 'm->File[3]'
- * EDX,
- * EBX,
- * ESP,
+ * EDX - holds 'm'
  * EBP,
  * ESI,
  * EDI
@@ -76,8 +80,7 @@ do {                                                                  \
 
 struct compilation {
    struct x86_function func;
-   struct arb_vp_machine *m;
-
+   struct tnl_compiled_program *p;   
    GLuint insn_counter;
 
    struct {
@@ -92,6 +95,7 @@ struct compilation {
    } file[4];
 
    GLboolean have_sse2;
+   GLshort fpucntl;
 };
 
 static INLINE GLboolean eq( struct x86_reg a,
@@ -103,6 +107,10 @@ static INLINE GLboolean eq( struct x86_reg a,
           a.disp == b.disp);
 }
       
+static GLint get_offset( const void *a, const void *b )
+{
+   return (const char *)b - (const char *)a;
+}
 
 
 static struct x86_reg get_reg_ptr(GLuint file,
@@ -112,7 +120,7 @@ static struct x86_reg get_reg_ptr(GLuint file,
 
    switch (file) {
    case FILE_REG:
-      reg = x86_make_reg(file_REG32, reg_AX);
+      reg = x86_make_reg(file_REG32, reg_BX);
       assert(idx != REG_UNDEF);
       break;
    case FILE_STATE_PARAM:
@@ -158,17 +166,12 @@ static struct x86_reg get_xmm_reg( struct compilation *cp )
    return x86_make_reg(file_XMM, oldest);
 }
 
-      
-
-
-static struct x86_reg get_dst_reg( struct compilation *cp, 
-                                  GLuint file, GLuint idx )
+static void invalidate_xmm( struct compilation *cp, 
+                           GLuint file, GLuint idx )
 {
-   struct x86_reg reg;
    GLuint i;
 
-   /* Invalidate any old copy of this register in XMM0-7.  Don't reuse
-    * as this may be one of the arguments.
+   /* Invalidate any old copy of this register in XMM0-7.  
     */
    for (i = 0; i < 8; i++) {
       if (cp->xmm[i].file == file && cp->xmm[i].idx == idx) {
@@ -178,6 +181,20 @@ static struct x86_reg get_dst_reg( struct compilation *cp,
         break;
       }
    }
+}
+      
+
+/* Return an XMM reg to receive the results of an operation.
+ */
+static struct x86_reg get_dst_xmm_reg( struct compilation *cp, 
+                                      GLuint file, GLuint idx )
+{
+   struct x86_reg reg;
+
+   /* Invalidate any old copy of this register in XMM0-7.  Don't reuse
+    * as this may be one of the arguments.
+    */
+   invalidate_xmm( cp, file, idx );
 
    reg = get_xmm_reg( cp );
    cp->xmm[reg.idx].file = file;
@@ -186,6 +203,21 @@ static struct x86_reg get_dst_reg( struct compilation *cp,
    return reg;   
 }
 
+/* As above, but return a pointer.  Note - this pointer may alias
+ * those returned by get_arg_ptr().
+ */
+static struct x86_reg get_dst_ptr( struct compilation *cp, 
+                                  GLuint file, GLuint idx )
+{
+   /* Invalidate any old copy of this register in XMM0-7.  Don't reuse
+    * as this may be one of the arguments.
+    */
+   invalidate_xmm( cp, file, idx );
+
+   return get_reg_ptr(file, idx);
+}
+
+
 
 /* Return an XMM reg if the argument is resident, otherwise return a
  * base+offset pointer to the saved value.
@@ -205,6 +237,27 @@ static struct x86_reg get_arg( struct compilation *cp, GLuint file, GLuint idx )
    return get_reg_ptr(file, idx);
 }
 
+/* As above, but always return a pointer:
+ */
+static struct x86_reg get_arg_ptr( struct compilation *cp, GLuint file, GLuint idx )
+{
+   GLuint i;
+
+   /* If there is a modified version of this register in one of the
+    * XMM regs, write it out to memory.
+    */
+   for (i = 0; i < 8; i++) {
+      if (cp->xmm[i].file == file && 
+         cp->xmm[i].idx == idx &&
+         cp->xmm[i].dirty) 
+        spill(cp, i);
+   }
+
+   return get_reg_ptr(file, idx);
+}
+
+/* Emulate pshufd insn in regular SSE, if necessary:
+ */
 static void emit_pshufd( struct compilation *cp,
                         struct x86_reg dst,
                         struct x86_reg arg0,
@@ -221,7 +274,18 @@ static void emit_pshufd( struct compilation *cp,
       sse_shufps(&cp->func, dst, dst, shuf);
    }
 }
-                        
+
+static void set_fpu_round_neg_inf( struct compilation *cp )
+{
+   if (cp->fpucntl != RND_NEG_FPU) {
+      struct x86_reg regEDX = x86_make_reg(file_REG32, reg_DX);
+      struct arb_vp_machine *m = NULL;
+
+      cp->fpucntl = RND_NEG_FPU;
+      x87_fnclex(&cp->func);
+      x87_fldcw(&cp->func, x86_make_disp(regEDX, get_offset(m, &m->fpucntl_rnd_neg)));
+   }
+}
 
 
 /* Perform a reduced swizzle.  
@@ -229,12 +293,13 @@ static void emit_pshufd( struct compilation *cp,
 static GLboolean emit_RSW( struct compilation *cp, union instruction op ) 
 {
    struct x86_reg arg0 = get_arg(cp, op.rsw.file0, op.rsw.idx0);
-   struct x86_reg dst = get_dst_reg(cp, FILE_REG, op.rsw.dst);
-   GLuint swz = op.rsw.swz;
+   struct x86_reg dst = get_dst_xmm_reg(cp, FILE_REG, op.rsw.dst);
+   GLuint swz = GET_SWZ(op.rsw.swz, 0) | (GET_SWZ(op.rsw.swz, 1) << 2) |
+               (GET_SWZ(op.rsw.swz, 2) << 4| (GET_SWZ(op.rsw.swz, 3) << 6));
    GLuint neg = op.rsw.neg;
 
    emit_pshufd(cp, dst, arg0, swz);
-   
+
    if (neg) {
       struct x86_reg negs = get_arg(cp, FILE_REG, REG_SWZ);
       struct x86_reg tmp = get_xmm_reg(cp);
@@ -242,6 +307,7 @@ static GLboolean emit_RSW( struct compilation *cp, union instruction op )
        * Use neg as arg to pshufd
        * Multiply
        */
+      /* is the emit_pshufd necessary? only SWZ can negate individual components */
       emit_pshufd(cp, tmp, negs, 
                  SHUF((neg & 1) ? 1 : 0,
                       (neg & 2) ? 1 : 0,
@@ -253,114 +319,254 @@ static GLboolean emit_RSW( struct compilation *cp, union instruction op )
    return GL_TRUE;
 }
 
+/* Perform a full swizzle
+ */
+static GLboolean emit_SWZ( struct compilation *cp, union instruction op ) 
+{
+   struct x86_reg arg0 = get_arg(cp, op.rsw.file0, op.rsw.idx0);
+   struct x86_reg dst = get_dst_xmm_reg(cp, FILE_REG, op.rsw.dst);
+   struct x86_reg negs = get_arg(cp, FILE_REG, REG_SWZ);
+   struct x86_reg tmp = get_xmm_reg(cp);
+   GLubyte neg = op.rsw.neg;
+   GLubyte shuf2, swz, savepos, savemask, swizzle[4];
+
+   swizzle[0] = GET_SWZ(op.rsw.swz, 0);
+   swizzle[1] = GET_SWZ(op.rsw.swz, 1);
+   swizzle[2] = GET_SWZ(op.rsw.swz, 2);
+   swizzle[3] = GET_SWZ(op.rsw.swz, 3);
+
+   swz = SHUF((swizzle[0] & 3), (swizzle[1] & 3),
+             (swizzle[2] & 3), (swizzle[3] & 3));
+
+   emit_pshufd(cp, dst, arg0, swz);
+
+   /* can handle negation and replace with zero with the same shuffle/mul */
+   shuf2 = SHUF(swizzle[0] == 4 ? 2 : (neg & 1),
+               swizzle[1] == 4 ? 2 : ((neg & 2) >> 1),
+               swizzle[2] == 4 ? 2 : ((neg & 4) >> 2),
+               swizzle[3] == 4 ? 2 : ((neg & 8) >> 3));
+
+   /* now the hard part is getting those 1's in there... */
+   savepos = 0;
+   savemask = 0;
+   if (swizzle[0] == 5) savepos = 1;
+   if (swizzle[1] == 5) savepos = 2;
+   else savemask |= 1 << 2;
+   if (swizzle[2] == 5) savepos = 3;
+   else savemask |= 2 << 4;
+   if (swizzle[3] == 5) savepos = 4;
+   else savemask |= 3 << 6;
+   if (savepos) {
+      /* need a mov first as movss from memory will overwrite high bits of xmm reg */
+      sse_movups(&cp->func, tmp, negs);
+      /* can only replace lowest 32bits, thus move away that part first */
+      emit_pshufd(cp, dst, dst, savemask);
+      sse_movss(&cp->func, dst, tmp);
+      emit_pshufd(cp, dst, dst, (savepos - 1) | (savemask & 0xfc));
+   }
+
+   if (shuf2) {
+      /* Load 1,-1,0,0
+       * Use neg as arg to pshufd
+       * Multiply
+       */
+      emit_pshufd(cp, tmp, negs, shuf2);
+      sse_mulps(&cp->func, dst, tmp);
+   }
+
+   return GL_TRUE;
+}
+
+/* Helper for writemask:
+ */
+static GLboolean emit_shuf_copy1( struct compilation *cp,
+                                 struct x86_reg dst,
+                                 struct x86_reg arg0,
+                                 struct x86_reg arg1,
+                                 GLubyte shuf )
+{
+   struct x86_reg tmp = get_xmm_reg(cp);
+   sse_movups(&cp->func, dst, arg1);
+   emit_pshufd(cp, dst, dst, shuf);
+   emit_pshufd(cp, tmp, arg0, shuf);
+
+   sse_movss(&cp->func, dst, tmp);
+
+   emit_pshufd(cp, dst, dst, shuf);
+   return GL_TRUE;
+}
+
+
+/* Helper for writemask:
+ */
+static GLboolean emit_shuf_copy2( struct compilation *cp,
+                                 struct x86_reg dst,
+                                 struct x86_reg arg0,
+                                 struct x86_reg arg1,
+                                 GLubyte shuf )
+{
+   struct x86_reg tmp = get_xmm_reg(cp);
+   emit_pshufd(cp, dst, arg1, shuf);
+   emit_pshufd(cp, tmp, arg0, shuf);
+
+   sse_shufps(&cp->func, dst, tmp, SHUF(X, Y, Z, W));
+
+   emit_pshufd(cp, dst, dst, shuf);
+   return GL_TRUE;
+}
+
+
+static void emit_x87_ex2( struct compilation *cp )
+{
+   struct x86_reg st0 = x86_make_reg(file_x87, 0);
+   struct x86_reg st1 = x86_make_reg(file_x87, 1);
+   struct x86_reg st3 = x86_make_reg(file_x87, 3);
+
+   set_fpu_round_neg_inf( cp );
+
+   x87_fld(&cp->func, st0); /* a a */
+   x87_fprndint( &cp->func );  /* int(a) a */
+   x87_fld(&cp->func, st0); /* int(a) int(a) a */
+   x87_fstp(&cp->func, st3); /* int(a) a int(a)*/
+   x87_fsubp(&cp->func, st1); /* frac(a) int(a) */
+   x87_f2xm1(&cp->func);    /* (2^frac(a))-1 int(a)*/
+   x87_fld1(&cp->func);    /* 1 (2^frac(a))-1 int(a)*/
+   x87_faddp(&cp->func, st1);  /* 2^frac(a) int(a) */
+   x87_fscale(&cp->func);      /* 2^a */
+}
+
+#if 0
+static GLboolean emit_MSK2( struct compilation *cp, union instruction op )
+{
+   struct x86_reg arg0 = get_arg(cp, op.msk.file, op.msk.arg);
+   struct x86_reg arg1 = get_arg(cp, FILE_REG, op.msk.dst); /* NOTE! */
+   struct x86_reg dst = get_dst_xmm_reg(cp, FILE_REG, op.msk.dst);
+   
+   /* make full width bitmask in tmp 
+    * dst = ~tmp
+    * tmp &= arg0
+    * dst &= arg1
+    * dst |= tmp
+    */
+   emit_pshufd(cp, tmp, get_arg(cp, FILE_REG, REG_NEGS), 
+              SHUF((op.msk.mask & 1) ? 2 : 0,
+                   (op.msk.mask & 2) ? 2 : 0,
+                   (op.msk.mask & 4) ? 2 : 0,
+                   (op.msk.mask & 8) ? 2 : 0));
+   sse2_pnot(&cp->func, dst, tmp);
+   sse2_pand(&cp->func, arg0, tmp);
+   sse2_pand(&cp->func, arg1, dst);
+   sse2_por(&cp->func, tmp, dst);
+   return GL_TRUE;
+}
+#endif
+
+
 /* Used to implement write masking.  This and most of the other instructions
  * here would be easier to implement if there had been a translation
  * to a 2 argument format (dst/arg0, arg1) at the shader level before
  * attempting to translate to x86/sse code.
  */
-/* Hmm.  I went back to MSK from SEL to make things easier -- was that just BS?
- */
 static GLboolean emit_MSK( struct compilation *cp, union instruction op )
 {
    struct x86_reg arg = get_arg(cp, op.msk.file, op.msk.idx);
-   struct x86_reg dst0 = get_arg(cp, FILE_REG, op.msk.dst);
-   struct x86_reg dst = get_dst_reg(cp, FILE_REG, op.msk.dst);
+   struct x86_reg dst0 = get_arg(cp, FILE_REG, op.msk.dst); /* NOTE! */
+   struct x86_reg dst = get_dst_xmm_reg(cp, FILE_REG, op.msk.dst);
    
-   sse_movups(&cp->func, dst, dst0);
+   /* Note that dst and dst0 refer to the same program variable, but
+    * will definitely be different XMM registers.  We're effectively
+    * treating this as a 2 argument SEL now, just one of which happens
+    * always to be the same register as the destination.
+    */
 
    switch (op.msk.mask) {
    case 0:
+      sse_movups(&cp->func, dst, dst0);
       return GL_TRUE;
 
    case WRITEMASK_X:
       if (arg.file == file_XMM) {
+        sse_movups(&cp->func, dst, dst0);
         sse_movss(&cp->func, dst, arg);
       }
       else {
         struct x86_reg tmp = get_xmm_reg(cp);
+        sse_movups(&cp->func, dst, dst0);
         sse_movss(&cp->func, tmp, arg);
         sse_movss(&cp->func, dst, tmp);
       }
       return GL_TRUE;
 
-   case WRITEMASK_Y: {
-      struct x86_reg tmp = get_xmm_reg(cp);
-      emit_pshufd(cp, dst, dst, SHUF(Y, X, Z, W));
-      emit_pshufd(cp, tmp, arg, SHUF(Y, X, Z, W));
-      sse_movss(&cp->func, dst, tmp);
-      emit_pshufd(cp, dst, dst, SHUF(Y, X, Z, W));
+   case WRITEMASK_XY:
+      sse_movups(&cp->func, dst, dst0);
+      sse_shufps(&cp->func, dst, arg, SHUF(X, Y, Z, W));
       return GL_TRUE;
-   }
 
-   case WRITEMASK_Z: {
-      struct x86_reg tmp = get_xmm_reg(cp);
-      emit_pshufd(cp, dst, dst, SHUF(Z, Y, X, W));
-      emit_pshufd(cp, tmp, arg, SHUF(Z, Y, X, W));
-      sse_movss(&cp->func, dst, tmp);
-      emit_pshufd(cp, dst, dst, SHUF(Z, Y, X, W));
+   case WRITEMASK_ZW: 
+      sse_movups(&cp->func, dst, arg);
+      sse_shufps(&cp->func, dst, dst0, SHUF(X, Y, Z, W));
       return GL_TRUE;
-   }
 
-   case WRITEMASK_W: {
-      struct x86_reg tmp = get_xmm_reg(cp);
-      emit_pshufd(cp, dst, dst, SHUF(W, Y, Z, X));
-      emit_pshufd(cp, tmp, arg, SHUF(W, Y, Z, X));
-      sse_movss(&cp->func, dst, tmp);
-      emit_pshufd(cp, dst, dst, SHUF(W, Y, Z, X));
+   case WRITEMASK_YZW: 
+      if (dst0.file == file_XMM) {
+        sse_movups(&cp->func, dst, arg);
+        sse_movss(&cp->func, dst, dst0);
+      }
+      else {
+        struct x86_reg tmp = get_xmm_reg(cp);      
+        sse_movups(&cp->func, dst, arg);
+        sse_movss(&cp->func, tmp, dst0);
+        sse_movss(&cp->func, dst, tmp);
+      }
       return GL_TRUE;
-   }
 
-   case WRITEMASK_XY:
-      sse_shufps(&cp->func, dst, arg, SHUF(X, Y, Z, W));
+   case WRITEMASK_Y:
+      emit_shuf_copy1(cp, dst, arg, dst0, SHUF(Y,X,Z,W));
       return GL_TRUE;
 
-   case WRITEMASK_ZW: {
-      struct x86_reg tmp = get_xmm_reg(cp);      
-      sse_movups(&cp->func, tmp, dst);
-      sse_movups(&cp->func, dst, arg);
-      sse_shufps(&cp->func, dst, tmp, SHUF(X, Y, Z, W));
+   case WRITEMASK_Z: 
+      emit_shuf_copy1(cp, dst, arg, dst0, SHUF(Z,Y,X,W));
       return GL_TRUE;
-   }
 
-   case WRITEMASK_YZW: {
-      struct x86_reg tmp = get_xmm_reg(cp);      
-      sse_movss(&cp->func, tmp, dst);
-      sse_movups(&cp->func, dst, arg);
-      sse_movss(&cp->func, dst, tmp);
+   case WRITEMASK_W: 
+      emit_shuf_copy1(cp, dst, arg, dst0, SHUF(W,Y,Z,X));
+      return GL_TRUE;
+
+   case WRITEMASK_XZ:
+      emit_shuf_copy2(cp, dst, arg, dst0, SHUF(X,Z,Y,W));
+      return GL_TRUE;
+
+   case WRITEMASK_XW: 
+      emit_shuf_copy2(cp, dst, arg, dst0, SHUF(X,W,Z,Y));
+
+   case WRITEMASK_YZ:      
+      emit_shuf_copy2(cp, dst, arg, dst0, SHUF(Z,Y,X,W));
+      return GL_TRUE;
+
+   case WRITEMASK_YW:
+      emit_shuf_copy2(cp, dst, arg, dst0, SHUF(W,Y,Z,X));
+      return GL_TRUE;
+
+   case WRITEMASK_XZW:
+      emit_shuf_copy1(cp, dst, dst0, arg, SHUF(Y,X,Z,W));
+      return GL_TRUE;
+
+   case WRITEMASK_XYW: 
+      emit_shuf_copy1(cp, dst, dst0, arg, SHUF(Z,Y,X,W));
+      return GL_TRUE;
+
+   case WRITEMASK_XYZ: 
+      emit_shuf_copy1(cp, dst, dst0, arg, SHUF(W,Y,Z,X));
       return GL_TRUE;
-   }
 
    case WRITEMASK_XYZW:
       sse_movups(&cp->func, dst, arg);
       return GL_TRUE;      
 
    default:
-      FAIL;
-   }
-
-#if 0
-   /* The catchall implementation:
-    */
-
-   /* make full width bitmask in tmp 
-    * dst = ~tmp
-    * tmp &= arg0
-    * dst &= arg1
-    * dst |= tmp
-    */
-   {
-      struct x86_reg negs = get_arg(cp, FILE_REG, REG_NEGS);
-      emit_pshufd(cp, tmp, negs, 
-                 SHUF((op.msk.mask & 1) ? 2 : 0,
-                      (op.msk.mask & 2) ? 2 : 0,
-                      (op.msk.mask & 4) ? 2 : 0,
-                      (op.msk.mask & 8) ? 2 : 0));
-      sse_mulps(&cp->func, dst, tmp);
+      assert(0);
+      break;
    }
-
-   return GL_TRUE;
-#endif
-   FAIL;
 }
 
 
@@ -379,7 +585,7 @@ static GLboolean emit_PRT( struct compilation *cp, union instruction op )
 static GLboolean emit_ABS( struct compilation *cp, union instruction op ) 
 {
    struct x86_reg arg0 = get_arg(cp, op.alu.file0, op.alu.idx0);
-   struct x86_reg dst = get_dst_reg(cp, FILE_REG, op.alu.dst);
+   struct x86_reg dst = get_dst_xmm_reg(cp, FILE_REG, op.alu.dst);
    struct x86_reg neg = get_reg_ptr(FILE_REG, REG_NEG);
 
    sse_movups(&cp->func, dst, arg0);
@@ -392,7 +598,7 @@ static GLboolean emit_ADD( struct compilation *cp, union instruction op )
 {
    struct x86_reg arg0 = get_arg(cp, op.alu.file0, op.alu.idx0);
    struct x86_reg arg1 = get_arg(cp, op.alu.file1, op.alu.idx1);
-   struct x86_reg dst = get_dst_reg(cp, FILE_REG, op.alu.dst);
+   struct x86_reg dst = get_dst_xmm_reg(cp, FILE_REG, op.alu.dst);
 
    sse_movups(&cp->func, dst, arg0);
    sse_addps(&cp->func, dst, arg1);
@@ -406,7 +612,7 @@ static GLboolean emit_DP3( struct compilation *cp, union instruction op )
 {
    struct x86_reg arg0 = get_arg(cp, op.alu.file0, op.alu.idx0);
    struct x86_reg arg1 = get_arg(cp, op.alu.file1, op.alu.idx1);
-   struct x86_reg dst = get_dst_reg(cp, FILE_REG, op.alu.dst);
+   struct x86_reg dst = get_dst_xmm_reg(cp, FILE_REG, op.alu.dst);
    struct x86_reg tmp = get_xmm_reg(cp); 
 
    sse_movups(&cp->func, dst, arg0);
@@ -428,7 +634,7 @@ static GLboolean emit_DP4( struct compilation *cp, union instruction op )
 {
    struct x86_reg arg0 = get_arg(cp, op.alu.file0, op.alu.idx0);
    struct x86_reg arg1 = get_arg(cp, op.alu.file1, op.alu.idx1);
-   struct x86_reg dst = get_dst_reg(cp, FILE_REG, op.alu.dst);
+   struct x86_reg dst = get_dst_xmm_reg(cp, FILE_REG, op.alu.dst);
    struct x86_reg tmp = get_xmm_reg(cp);      
 
    sse_movups(&cp->func, dst, arg0);
@@ -446,135 +652,302 @@ static GLboolean emit_DP4( struct compilation *cp, union instruction op )
 
 static GLboolean emit_DPH( struct compilation *cp, union instruction op )
 {
-/*    struct x86_reg arg0 = get_arg(cp, op.alu.file0, op.alu.idx0); */
-/*    struct x86_reg arg1 = get_arg(cp, op.alu.file1, op.alu.idx1); */
-   struct x86_reg dst = get_dst_reg(cp, FILE_REG, op.alu.dst);
+   struct x86_reg arg0 = get_arg(cp, op.alu.file0, op.alu.idx0); 
+   struct x86_reg arg1 = get_arg(cp, op.alu.file1, op.alu.idx1); 
+   struct x86_reg dst = get_dst_xmm_reg(cp, FILE_REG, op.alu.dst);
+   struct x86_reg tmp = get_xmm_reg(cp);
 
-/*    dst[0] = (arg0[0] * arg1[0] +  */
-/*          arg0[1] * arg1[1] +  */
-/*          arg0[2] * arg1[2] +  */
-/*          1.0     * arg1[3]); */
-   
+   sse_movups(&cp->func, dst, arg0);
+   sse_mulps(&cp->func, dst, arg1);
+
+   /* Now the hard bit: sum the values (from DP3):
+    */ 
+   sse_movhlps(&cp->func, tmp, dst);
+   sse_addss(&cp->func, dst, tmp); /* a*x+c*z, b*y, ?, ? */
+   emit_pshufd(cp, tmp, dst, SHUF(Y,X,W,Z));
+   sse_addss(&cp->func, dst, tmp);
+   emit_pshufd(cp, tmp, arg1, SHUF(W,W,W,W));
+   sse_addss(&cp->func, dst, tmp);
    sse_shufps(&cp->func, dst, dst, SHUF(X, X, X, X));
-   FAIL;
+   return GL_TRUE;
 }
 
+#if 0
 static GLboolean emit_DST( struct compilation *cp, union instruction op )
 {
-/*    struct x86_reg arg0 = get_arg(cp, op.alu.file0, op.alu.idx0); */
-/*    struct x86_reg arg1 = get_arg(cp, op.alu.file1, op.alu.idx1); */
-/*    struct x86_reg dst = get_dst_reg(cp, FILE_REG, op.alu.dst); */
+    struct x86_reg arg0 = get_arg_ptr(cp, op.alu.file0, op.alu.idx0); 
+    struct x86_reg arg1 = get_arg_ptr(cp, op.alu.file1, op.alu.idx1); 
+    struct x86_reg dst = get_dst_ptr(cp, FILE_REG, op.alu.dst); 
 
 /*    dst[0] = 1.0     * 1.0F; */
 /*    dst[1] = arg0[1] * arg1[1]; */
 /*    dst[2] = arg0[2] * 1.0; */
 /*    dst[3] = 1.0     * arg1[3]; */
 
-   FAIL;
+    /* Would rather do some of this with integer regs, but:
+     *  1) No proper support for immediate values yet
+     *  2) I'd need to push/pop somewhere to get a free reg.
+     */ 
+    x87_fld1(&cp->func);
+    x87_fstp(&cp->func, dst); /* would rather do an immediate store... */
+    x87_fld(&cp->func, x86_make_disp(arg0, 4));
+    x87_fmul(&cp->func, x86_make_disp(arg1, 4));
+    x87_fstp(&cp->func, x86_make_disp(dst, 4));
+    
+    if (!eq(arg0, dst)) {
+       x86_fld(&cp->func, x86_make_disp(arg0, 8));
+       x86_stp(&cp->func, x86_make_disp(dst, 8));
+    }
+
+    if (!eq(arg1, dst)) {
+       x86_fld(&cp->func, x86_make_disp(arg0, 12));
+       x86_stp(&cp->func, x86_make_disp(dst, 12));
+    } 
+
+    return GL_TRUE;
+}
+#else
+static GLboolean emit_DST( struct compilation *cp, union instruction op )
+{
+    struct x86_reg arg0 = get_arg(cp, op.alu.file0, op.alu.idx0); 
+    struct x86_reg arg1 = get_arg(cp, op.alu.file1, op.alu.idx1); 
+    struct x86_reg dst = get_dst_xmm_reg(cp, FILE_REG, op.alu.dst); 
+    struct x86_reg tmp = get_xmm_reg(cp);
+    struct x86_reg ones = get_reg_ptr(FILE_REG, REG_ONES);
+
+    emit_shuf_copy2(cp, dst, arg0, ones, SHUF(X,W,Z,Y));
+    emit_shuf_copy2(cp, tmp, arg1, ones, SHUF(X,Z,Y,W));
+    sse_mulps(&cp->func, dst, tmp);
+
+/*    dst[0] = 1.0     * 1.0F; */
+/*    dst[1] = arg0[1] * arg1[1]; */
+/*    dst[2] = arg0[2] * 1.0; */
+/*    dst[3] = 1.0     * arg1[3]; */
+
+    return GL_TRUE;
+}
+#endif
+
+static GLboolean emit_LG2( struct compilation *cp, union instruction op ) 
+{
+   struct x86_reg arg0 = get_arg_ptr(cp, op.alu.file0, op.alu.idx0); 
+   struct x86_reg dst = get_dst_ptr(cp, FILE_REG, op.alu.dst); 
+
+   x87_fld1(&cp->func);                /* 1 */
+   x87_fld(&cp->func, arg0);   /* a0 1 */
+   x87_fyl2x(&cp->func);       /* log2(a0) */
+   x87_fst(&cp->func, x86_make_disp(dst, 0));
+   x87_fst(&cp->func, x86_make_disp(dst, 4));
+   x87_fst(&cp->func, x86_make_disp(dst, 8));
+   x87_fstp(&cp->func, x86_make_disp(dst, 12));
+   
+   return GL_TRUE;
 }
 
 
 static GLboolean emit_EX2( struct compilation *cp, union instruction op ) 
 {
-/*    struct x86_reg arg0 = get_arg(cp, op.alu.file0, op.alu.idx0); */
-   struct x86_reg dst = get_dst_reg(cp, FILE_REG, op.alu.dst);
+   struct x86_reg arg0 = get_arg_ptr(cp, op.alu.file0, op.alu.idx0); 
+   struct x86_reg dst = get_dst_ptr(cp, FILE_REG, op.alu.dst);
 
-/*    dst[0] = (GLfloat)RoughApproxPow2(arg0[0]); */
-   sse_shufps(&cp->func, dst, dst, SHUF(X, X, X, X));
-   FAIL;
+   /* CAUTION: dst may alias arg0!
+    */
+   x87_fld(&cp->func, arg0);   
+
+   emit_x87_ex2(cp);
+
+   x87_fst(&cp->func, x86_make_disp(dst, 0));    
+   x87_fst(&cp->func, x86_make_disp(dst, 4));    
+   x87_fst(&cp->func, x86_make_disp(dst, 8));    
+   x87_fst(&cp->func, x86_make_disp(dst, 12));    
+   return GL_TRUE;
 }
 
 static GLboolean emit_EXP( struct compilation *cp, union instruction op )
 {
-/*    struct x86_reg arg0 = get_arg(cp, op.alu.file0, op.alu.idx0); */
-/*    struct x86_reg dst = get_dst_reg(cp, FILE_REG, op.alu.dst); */
-
-/*    GLfloat tmp = arg0[0]; */
-/*    GLfloat flr_tmp = FLOORF(tmp); */
-/*    dst[0] = (GLfloat) (1 << (int)flr_tmp); */
-/*    dst[1] = tmp - flr_tmp; */
-/*    dst[2] = RoughApproxPow2(tmp); */
+    struct x86_reg arg0 = get_arg_ptr(cp, op.alu.file0, op.alu.idx0); 
+    struct x86_reg dst = get_dst_ptr(cp, FILE_REG, op.alu.dst); 
+    struct x86_reg st0 = x86_make_reg(file_x87, 0);
+    struct x86_reg st1 = x86_make_reg(file_x87, 1);
+    struct x86_reg st3 = x86_make_reg(file_x87, 3);
+
+    /* CAUTION: dst may alias arg0!
+     */
+    x87_fld(&cp->func, arg0);  /* arg0.x */
+    x87_fld(&cp->func, st0); /* arg arg */
+
+    /* by default, fpu is setup to round-to-nearest.  We want to
+     * change this now, and track the state through to the end of the
+     * generated function so that it isn't repeated unnecessarily.
+     * Alternately, could subtract .5 to get round to -inf behaviour.
+     */
+    set_fpu_round_neg_inf( cp );
+    x87_fprndint( &cp->func ); /* flr(a) a */
+    x87_fld(&cp->func, st0); /* flr(a) flr(a) a */
+    x87_fld1(&cp->func);    /* 1 floor(a) floor(a) a */
+    x87_fst(&cp->func, x86_make_disp(dst, 12));  /* stack unchanged */
+    x87_fscale(&cp->func);  /* 2^floor(a) floor(a) a */
+    x87_fst(&cp->func, st3); /* 2^floor(a) floor(a) a 2^floor(a)*/
+    x87_fstp(&cp->func, x86_make_disp(dst, 0)); /* flr(a) a 2^flr(a) */
+    x87_fsubrp(&cp->func, st1); /* frac(a) 2^flr(a) */
+    x87_fst(&cp->func, x86_make_disp(dst, 4));    /* frac(a) 2^flr(a) */
+    x87_f2xm1(&cp->func);    /* (2^frac(a))-1 2^flr(a)*/
+    x87_fld1(&cp->func);    /* 1 (2^frac(a))-1 2^flr(a)*/
+    x87_faddp(&cp->func, st1); /* 2^frac(a) 2^flr(a) */
+    x87_fmulp(&cp->func, st1); /* 2^a */
+    x87_fst(&cp->func, x86_make_disp(dst, 8));    
+    
+
+
+/*    dst[0] = 2^floor(tmp); */
+/*    dst[1] = frac(tmp); */
+/*    dst[2] = 2^floor(tmp) * 2^frac(tmp); */
 /*    dst[3] = 1.0F; */
-   FAIL;
+    return GL_TRUE;
 }
 
-static GLboolean emit_FLR( struct compilation *cp, union instruction op ) 
+static GLboolean emit_LOG( struct compilation *cp, union instruction op )
 {
-/*    struct x86_reg arg0 = get_arg(cp, op.alu.file0, op.alu.idx0); */
-/*    struct x86_reg dst = get_dst_reg(cp, FILE_REG, op.alu.dst); */
-
-/*    dst[0] = FLOORF(arg0[0]); */
-/*    dst[1] = FLOORF(arg0[1]); */
-/*    dst[2] = FLOORF(arg0[2]); */
-/*    dst[3] = FLOORF(arg0[3]); */
-   FAIL;
+    struct x86_reg arg0 = get_arg_ptr(cp, op.alu.file0, op.alu.idx0); 
+    struct x86_reg dst = get_dst_ptr(cp, FILE_REG, op.alu.dst); 
+    struct x86_reg st0 = x86_make_reg(file_x87, 0);
+    struct x86_reg st1 = x86_make_reg(file_x87, 1);
+    struct x86_reg st2 = x86_make_reg(file_x87, 2);
+    /* CAUTION: dst may alias arg0!
+     */
+    x87_fld(&cp->func, arg0);  /* arg0.x */
+    x87_fabs(&cp->func);       /* |arg0.x| */
+    x87_fxtract(&cp->func);    /* mantissa(arg0.x), exponent(arg0.x) */
+    x87_fst(&cp->func, st2);   /* mantissa, exponent, mantissa */
+    x87_fld1(&cp->func);       /* 1, mantissa, exponent, mantissa */
+    x87_fyl2x(&cp->func);      /* log2(mantissa), exponent, mantissa */
+    x87_fadd(&cp->func, st0, st1);     /* e+l2(m), e, m  */
+    x87_fstp(&cp->func, x86_make_disp(dst, 8)); /* e, m */
+
+    x87_fld1(&cp->func);       /* 1, e, m */
+    x87_fsub(&cp->func, st1, st0);     /* 1, e-1, m */
+    x87_fstp(&cp->func, x86_make_disp(dst, 12)); /* e-1,m */
+    x87_fstp(&cp->func, dst);  /* m */
+
+    x87_fadd(&cp->func, st0, st0);     /* 2m */
+    x87_fstp(&cp->func, x86_make_disp(dst, 4));        
+
+    return GL_TRUE;
 }
 
-static GLboolean emit_FRC( struct compilation *cp, union instruction op ) 
+static GLboolean emit_FLR( struct compilation *cp, union instruction op ) 
 {
-/*    struct x86_reg arg0 = get_arg(cp, op.alu.file0, op.alu.idx0); */
-/*    struct x86_reg dst = get_dst_reg(cp, FILE_REG, op.alu.dst); */
+   struct x86_reg arg0 = get_arg_ptr(cp, op.alu.file0, op.alu.idx0); 
+   struct x86_reg dst = get_dst_ptr(cp, FILE_REG, op.alu.dst); 
+   int i;
 
-/*    dst[0] = arg0[0] - FLOORF(arg0[0]); */
-/*    dst[1] = arg0[1] - FLOORF(arg0[1]); */
-/*    dst[2] = arg0[2] - FLOORF(arg0[2]); */
-/*    dst[3] = arg0[3] - FLOORF(arg0[3]); */
-   FAIL;
+   set_fpu_round_neg_inf( cp );
+
+   for (i = 0; i < 4; i++) {
+      x87_fld(&cp->func, x86_make_disp(arg0, i*4));   
+      x87_fprndint( &cp->func );   
+      x87_fstp(&cp->func, x86_make_disp(dst, i*4));
+   }
+
+
+   return GL_TRUE;
 }
 
-static GLboolean emit_LG2( struct compilation *cp, union instruction op ) 
+static GLboolean emit_FRC( struct compilation *cp, union instruction op ) 
 {
-/*    struct x86_reg arg0 = get_arg(cp, op.alu.file0, op.alu.idx0); */
-/*    struct x86_reg dst = get_dst_reg(cp, FILE_REG, op.alu.dst); */
+   struct x86_reg arg0 = get_arg_ptr(cp, op.alu.file0, op.alu.idx0); 
+   struct x86_reg dst = get_dst_ptr(cp, FILE_REG, op.alu.dst); 
+   struct x86_reg st0 = x86_make_reg(file_x87, 0);
+   struct x86_reg st1 = x86_make_reg(file_x87, 1);
+   int i;
 
-/*    dst[0] = RoughApproxLog2(arg0[0]); */
+   set_fpu_round_neg_inf( cp );
 
-/*    sse_shufps(&cp->func, dst, dst, SHUF(X, X, X, X)); */
-   FAIL;
+   /* Knowing liveness info or even just writemask would be useful
+    * here:
+    */
+   for (i = 0; i < 4; i++) {
+      x87_fld(&cp->func, x86_make_disp(arg0, i*4));   
+      x87_fld(&cp->func, st0); /* a a */
+      x87_fprndint( &cp->func );   /* flr(a) a */
+      x87_fsubrp(&cp->func, st1); /* frc(a) */
+      x87_fstp(&cp->func, x86_make_disp(dst, i*4));
+   }
+
+   return GL_TRUE;
 }
 
 
 
 static GLboolean emit_LIT( struct compilation *cp, union instruction op )
 {
-/*    struct x86_reg arg0 = get_arg(cp, op.alu.file0, op.alu.idx0); */
-/*    struct x86_reg dst = get_dst_reg(cp, FILE_REG, op.alu.dst); */
+#if 1
+   struct x86_reg arg0 = get_arg_ptr(cp, op.alu.file0, op.alu.idx0); 
+   struct x86_reg dst = get_dst_ptr(cp, FILE_REG, op.alu.dst); 
+   struct x86_reg lit = get_arg(cp, FILE_REG, REG_LIT);
+   struct x86_reg tmp = get_xmm_reg(cp);
+   struct x86_reg st1 = x86_make_reg(file_x87, 1);
+   struct x86_reg regEAX = x86_make_reg(file_REG32, reg_AX);
+   GLubyte *fixup1, *fixup2;
 
-/*    const GLfloat epsilon = 1.0F / 256.0F; */
-/*    GLfloat tmp[4]; */
 
-/*    tmp[0] = MAX2(arg0[0], 0.0F); */
-/*    tmp[1] = MAX2(arg0[1], 0.0F); */
-/*    tmp[3] = CLAMP(arg0[3], -(128.0F - epsilon), (128.0F - epsilon)); */
+   /* Load the interesting parts of arg0:
+    */
+   x87_fld(&cp->func, x86_make_disp(arg0, 12));        /* a3 */
+   x87_fld(&cp->func, x86_make_disp(arg0, 4)); /* a1 a3 */
+   x87_fld(&cp->func, x86_make_disp(arg0, 0)); /* a0 a1 a3 */
+   
+   /* Intialize dst:
+    */
+   sse_movaps(&cp->func, tmp, lit);
+   sse_movaps(&cp->func, dst, tmp);
+   
+   /* Check arg0[0]:
+    */
+   x87_fldz(&cp->func);                /* 0 a0 a1 a3 */
+   x87_fucomp(&cp->func, st1); /* a0 a1 a3 */
+   x87_fnstsw(&cp->func, regEAX);
+   x86_sahf(&cp->func);
+   fixup1 = x86_jcc_forward(&cp->func, cc_AE); 
+   
+   x87_fstp(&cp->func, x86_make_disp(dst, 4)); /* a1 a3 */
 
-/*    dst[0] = 1.0; */
-/*    dst[1] = tmp[0]; */
-/*    dst[2] = (tmp[0] > 0.0) ? RoughApproxPower(tmp[1], tmp[3]) : 0.0F; */
-/*    dst[3] = 1.0; */
-   FAIL;
-}
+   /* Check arg0[1]:
+    */ 
+   x87_fldz(&cp->func);                /* 0 a1 a3 */
+   x87_fucomp(&cp->func, st1); /* a1 a3 */
+   x87_fnstsw(&cp->func, regEAX);
+   x86_sahf(&cp->func);
+   fixup2 = x86_jcc_forward(&cp->func, cc_AE); 
 
+   /* Compute pow(a1, a3)
+    */
+   x87_fyl2x(&cp->func);       /* a3*log2(a1) */
 
-static GLboolean emit_LOG( struct compilation *cp, union instruction op )
-{
-/*    struct x86_reg arg0 = get_arg(cp, op.alu.file0, op.alu.idx0); */
-/*    struct x86_reg dst = get_dst_reg(cp, FILE_REG, op.alu.dst); */
+   emit_x87_ex2( cp );         /* 2^(a3*log2(a1)) */
 
-/*    GLfloat tmp = FABSF(arg0[0]); */
-/*    int exponent; */
-/*    GLfloat mantissa = FREXPF(tmp, &exponent); */
-/*    dst[0] = (GLfloat) (exponent - 1); */
-/*    dst[1] = 2.0 * mantissa; // map [.5, 1) -> [1, 2)  */
-/*    dst[2] = dst[0] + LOG2(dst[1]); */
-/*    dst[3] = 1.0; */
-   FAIL;
+   x87_fstp(&cp->func, x86_make_disp(dst, 8));
+   
+   /* Land jumps:
+    */
+   x86_fixup_fwd_jump(&cp->func, fixup1);
+   x86_fixup_fwd_jump(&cp->func, fixup2);
+#else
+   struct x86_reg dst = get_dst_xmm_reg(cp, FILE_REG, op.alu.dst); 
+   struct x86_reg ones = get_reg_ptr(FILE_REG, REG_LIT);
+   sse_movups(&cp->func, dst, ones);
+#endif   
+   return GL_TRUE;
 }
 
+
+
 static GLboolean emit_MAX( struct compilation *cp, union instruction op )
 {
    struct x86_reg arg0 = get_arg(cp, op.alu.file0, op.alu.idx0);
    struct x86_reg arg1 = get_arg(cp, op.alu.file1, op.alu.idx1);
-   struct x86_reg dst = get_dst_reg(cp, FILE_REG, op.alu.dst);
+   struct x86_reg dst = get_dst_xmm_reg(cp, FILE_REG, op.alu.dst);
 
    sse_movups(&cp->func, dst, arg0);
    sse_maxps(&cp->func, dst, arg1);
@@ -586,7 +959,7 @@ static GLboolean emit_MIN( struct compilation *cp, union instruction op )
 {
    struct x86_reg arg0 = get_arg(cp, op.alu.file0, op.alu.idx0);
    struct x86_reg arg1 = get_arg(cp, op.alu.file1, op.alu.idx1);
-   struct x86_reg dst = get_dst_reg(cp, FILE_REG, op.alu.dst);
+   struct x86_reg dst = get_dst_xmm_reg(cp, FILE_REG, op.alu.dst);
 
    sse_movups(&cp->func, dst, arg0);
    sse_minps(&cp->func, dst, arg1);
@@ -596,7 +969,7 @@ static GLboolean emit_MIN( struct compilation *cp, union instruction op )
 static GLboolean emit_MOV( struct compilation *cp, union instruction op )
 {
    struct x86_reg arg0 = get_arg(cp, op.alu.file0, op.alu.idx0);
-   struct x86_reg dst = get_dst_reg(cp, FILE_REG, op.alu.dst);
+   struct x86_reg dst = get_dst_xmm_reg(cp, FILE_REG, op.alu.dst);
 
    sse_movups(&cp->func, dst, arg0);
    return GL_TRUE;
@@ -606,7 +979,7 @@ static GLboolean emit_MUL( struct compilation *cp, union instruction op )
 {
    struct x86_reg arg0 = get_arg(cp, op.alu.file0, op.alu.idx0);
    struct x86_reg arg1 = get_arg(cp, op.alu.file1, op.alu.idx1);
-   struct x86_reg dst = get_dst_reg(cp, FILE_REG, op.alu.dst);
+   struct x86_reg dst = get_dst_xmm_reg(cp, FILE_REG, op.alu.dst);
 
    sse_movups(&cp->func, dst, arg0);
    sse_mulps(&cp->func, dst, arg1);
@@ -616,14 +989,22 @@ static GLboolean emit_MUL( struct compilation *cp, union instruction op )
 
 static GLboolean emit_POW( struct compilation *cp, union instruction op ) 
 {
-/*    struct x86_reg arg0 = get_arg(cp, op.alu.file0, op.alu.idx0); */
-/*    struct x86_reg arg1 = get_arg(cp, op.alu.file1, op.alu.idx1); */
-   struct x86_reg dst = get_dst_reg(cp, FILE_REG, op.alu.dst);
+   struct x86_reg arg0 = get_arg_ptr(cp, op.alu.file0, op.alu.idx0); 
+   struct x86_reg arg1 = get_arg_ptr(cp, op.alu.file1, op.alu.idx1); 
+   struct x86_reg dst = get_dst_ptr(cp, FILE_REG, op.alu.dst);
 
-/*    dst[0] = (GLfloat)RoughApproxPower(arg0[0], arg1[0]); */
+   x87_fld(&cp->func, arg1);           /* a1 */
+   x87_fld(&cp->func, arg0);   /* a0 a1 */
+   x87_fyl2x(&cp->func);       /* a1*log2(a0) */
 
-   sse_shufps(&cp->func, dst, dst, SHUF(X, X, X, X));
-   FAIL;
+   emit_x87_ex2( cp );         /* 2^(a1*log2(a0)) */
+
+   x87_fst(&cp->func, x86_make_disp(dst, 0));    
+   x87_fst(&cp->func, x86_make_disp(dst, 4));    
+   x87_fst(&cp->func, x86_make_disp(dst, 8));    
+   x87_fstp(&cp->func, x86_make_disp(dst, 12));    
+    
+   return GL_TRUE;
 }
 
 static GLboolean emit_REL( struct compilation *cp, union instruction op )
@@ -631,7 +1012,7 @@ static GLboolean emit_REL( struct compilation *cp, union instruction op )
 /*    GLuint idx = (op.alu.idx0 + (GLint)cp->File[0][REG_ADDR][0]) & (MAX_NV_VERTEX_PROGRAM_PARAMS-1); */
 /*    GLuint idx = 0; */
 /*    struct x86_reg arg0 = get_arg(cp, op.alu.file0, idx); */
-/*    struct x86_reg dst = get_dst_reg(cp, FILE_REG, op.alu.dst); */
+/*    struct x86_reg dst = get_dst_xmm_reg(cp, FILE_REG, op.alu.dst); */
 
 /*    dst[0] = arg0[0]; */
 /*    dst[1] = arg0[1]; */
@@ -644,7 +1025,7 @@ static GLboolean emit_REL( struct compilation *cp, union instruction op )
 static GLboolean emit_RCP( struct compilation *cp, union instruction op )
 {
    struct x86_reg arg0 = get_arg(cp, op.alu.file0, op.alu.idx0);
-   struct x86_reg dst = get_dst_reg(cp, FILE_REG, op.alu.dst);
+   struct x86_reg dst = get_dst_xmm_reg(cp, FILE_REG, op.alu.dst);
 
    if (cp->have_sse2) {
       sse2_rcpss(&cp->func, dst, arg0);
@@ -662,8 +1043,19 @@ static GLboolean emit_RCP( struct compilation *cp, union instruction op )
 static GLboolean emit_RSQ( struct compilation *cp, union instruction op )
 {
    struct x86_reg arg0 = get_arg(cp, op.alu.file0, op.alu.idx0);
-   struct x86_reg dst = get_dst_reg(cp, FILE_REG, op.alu.dst);
+   struct x86_reg dst = get_dst_xmm_reg(cp, FILE_REG, op.alu.dst);
+#if 0
+   struct x86_reg neg = get_reg_ptr(FILE_REG, REG_NEG);
 
+/* get abs value first. This STILL doesn't work.
+   Looks like we get bogus neg values ?
+*/
+   sse_movss(&cp->func, dst, arg0);
+   sse_mulss(&cp->func, dst, neg);
+   sse_maxss(&cp->func, dst, arg0);
+
+   sse_rsqrtss(&cp->func, dst, dst);
+#endif
    sse_rsqrtss(&cp->func, dst, arg0);
    sse_shufps(&cp->func, dst, dst, SHUF(X, X, X, X));
    return GL_TRUE;
@@ -674,7 +1066,7 @@ static GLboolean emit_SGE( struct compilation *cp, union instruction op )
 {
    struct x86_reg arg0 = get_arg(cp, op.alu.file0, op.alu.idx0);
    struct x86_reg arg1 = get_arg(cp, op.alu.file1, op.alu.idx1);
-   struct x86_reg dst = get_dst_reg(cp, FILE_REG, op.alu.dst);
+   struct x86_reg dst = get_dst_xmm_reg(cp, FILE_REG, op.alu.dst);
    struct x86_reg ones = get_reg_ptr(FILE_REG, REG_ONES);
 
    sse_movups(&cp->func, dst, arg0);
@@ -688,7 +1080,7 @@ static GLboolean emit_SLT( struct compilation *cp, union instruction op )
 {
    struct x86_reg arg0 = get_arg(cp, op.alu.file0, op.alu.idx0);
    struct x86_reg arg1 = get_arg(cp, op.alu.file1, op.alu.idx1);
-   struct x86_reg dst = get_dst_reg(cp, FILE_REG, op.alu.dst);
+   struct x86_reg dst = get_dst_xmm_reg(cp, FILE_REG, op.alu.dst);
    struct x86_reg ones = get_reg_ptr(FILE_REG, REG_ONES);
    
    sse_movups(&cp->func, dst, arg0);
@@ -701,7 +1093,7 @@ static GLboolean emit_SUB( struct compilation *cp, union instruction op )
 {
    struct x86_reg arg0 = get_arg(cp, op.alu.file0, op.alu.idx0);
    struct x86_reg arg1 = get_arg(cp, op.alu.file1, op.alu.idx1);
-   struct x86_reg dst = get_dst_reg(cp, FILE_REG, op.alu.dst);
+   struct x86_reg dst = get_dst_xmm_reg(cp, FILE_REG, op.alu.dst);
 
    sse_movups(&cp->func, dst, arg0);
    sse_subps(&cp->func, dst, arg1);
@@ -713,7 +1105,7 @@ static GLboolean emit_XPD( struct compilation *cp, union instruction op )
 {
    struct x86_reg arg0 = get_arg(cp, op.alu.file0, op.alu.idx0);
    struct x86_reg arg1 = get_arg(cp, op.alu.file1, op.alu.idx1);
-   struct x86_reg dst = get_dst_reg(cp, FILE_REG, op.alu.dst);
+   struct x86_reg dst = get_dst_xmm_reg(cp, FILE_REG, op.alu.dst);
    struct x86_reg tmp0 = get_xmm_reg(cp);
    struct x86_reg tmp1 = get_xmm_reg(cp);
 
@@ -747,60 +1139,98 @@ static GLboolean (* const emit_func[])(struct compilation *, union instruction)
 {
    emit_ABS,
    emit_ADD,
-   emit_NOP,
+   emit_NOP, /* ARA */
+   emit_NOP, /* ARL */
+   emit_NOP, /* ARL_NV */
+   emit_NOP, /* ARR */
+   emit_NOP, /* BRA */
+   emit_NOP, /* CAL */
+   emit_NOP, /* CMP */
+   emit_NOP, /* COS */
+   emit_NOP, /* DDX */
+   emit_NOP, /* DDY */
    emit_DP3,
    emit_DP4,
    emit_DPH,
    emit_DST,
-   emit_NOP,
+   emit_NOP, /* END */
    emit_EX2,
    emit_EXP,
    emit_FLR,
    emit_FRC,
+   emit_NOP, /* KIL */
+   emit_NOP, /* KIL_NV */
    emit_LG2,
    emit_LIT,
    emit_LOG,
-   emit_NOP,
+   emit_NOP, /* LRP */
+   emit_NOP, /* MAD */
    emit_MAX,
    emit_MIN,
    emit_MOV,
    emit_MUL,
+   emit_NOP, /* PK2H */
+   emit_NOP, /* PK2US */
+   emit_NOP, /* PK4B */
+   emit_NOP, /* PK4UB */
    emit_POW,
+   emit_NOP, /* POPA */
    emit_PRT,
-   emit_NOP,
+   emit_NOP, /* PUSHA */
+   emit_NOP, /* RCC */
    emit_RCP,
+   emit_NOP, /* RET */
+   emit_NOP, /* RFL */
    emit_RSQ,
+   emit_NOP, /* SCS */
+   emit_NOP, /* SEQ */
+   emit_NOP, /* SFL */
    emit_SGE,
+   emit_NOP, /* SGT */
+   emit_NOP, /* SIN */
+   emit_NOP, /* SLE */
    emit_SLT,
+   emit_NOP, /* SNE */
+   emit_NOP, /* SSG */
+   emit_NOP, /* STR */
    emit_SUB,
-   emit_RSW,
+   emit_SWZ, /* SWZ */
+   emit_NOP, /* TEX */
+   emit_NOP, /* TXB */
+   emit_NOP, /* TXD */
+   emit_NOP, /* TXL */
+   emit_NOP, /* TXP */
+   emit_NOP, /* TXP_NV */
+   emit_NOP, /* UP2H */
+   emit_NOP, /* UP2US */
+   emit_NOP, /* UP4B */
+   emit_NOP, /* UP4UB */
+   emit_NOP, /* X2D */
    emit_XPD,
    emit_RSW,
    emit_MSK,
    emit_REL,
 };
 
-static GLint get_offset( const void *a, const void *b )
-{
-   return (const char *)b - (const char *)a;
-}
 
 
 static GLboolean build_vertex_program( struct compilation *cp )
 {
+   struct arb_vp_machine *m = NULL;
    GLuint j;
 
-   struct x86_reg regEAX = x86_make_reg(file_REG32, reg_AX);
-   struct x86_reg parmECX = x86_make_reg(file_REG32, reg_CX);
+   struct x86_reg regEBX = x86_make_reg(file_REG32, reg_BX);
+   struct x86_reg regECX = x86_make_reg(file_REG32, reg_CX);
+   struct x86_reg regEDX = x86_make_reg(file_REG32, reg_DX);
 
-   x86_mov(&cp->func, regEAX, x86_fn_arg(&cp->func, 1));
-   x86_mov(&cp->func, parmECX, regEAX);
-   
-   x86_mov(&cp->func, regEAX, x86_make_disp(regEAX, get_offset(cp->m, cp->m->File + FILE_REG)));
-   x86_mov(&cp->func, parmECX, x86_make_disp(parmECX, get_offset(cp->m, cp->m->File + FILE_STATE_PARAM)));
+   x86_push(&cp->func, regEBX);
+
+   x86_mov(&cp->func, regEDX, x86_fn_arg(&cp->func, 1));   
+   x86_mov(&cp->func, regEBX, x86_make_disp(regEDX, get_offset(m, m->File + FILE_REG)));
+   x86_mov(&cp->func, regECX, x86_make_disp(regEDX, get_offset(m, m->File + FILE_STATE_PARAM)));
 
-   for (j = 0; j < cp->m->nr_instructions; j++) {
-      union instruction inst = cp->m->instructions[j];  
+   for (j = 0; j < cp->p->nr_instructions; j++) {
+      union instruction inst = cp->p->instructions[j];  
       cp->insn_counter = j+1;  /* avoid zero */
       
       if (DISASSEM) {
@@ -827,6 +1257,14 @@ static GLboolean build_vertex_program( struct compilation *cp )
    if (cp->func.need_emms)
       mmx_emms(&cp->func);
 
+   /* Restore FPU control word?
+    */
+   if (cp->fpucntl != RESTORE_FPU) {
+      x87_fnclex(&cp->func);
+      x87_fldcw(&cp->func, x86_make_disp(regEDX, get_offset(m, &m->fpucntl_restore)));
+   }
+
+   x86_pop(&cp->func, regEBX);
    x86_ret(&cp->func);
 
    return GL_TRUE;
@@ -842,27 +1280,39 @@ static GLboolean build_vertex_program( struct compilation *cp )
  * struct arb_vertex_machine.
  */
 GLboolean
-_tnl_sse_codegen_vertex_program(struct arb_vp_machine *m)
+_tnl_sse_codegen_vertex_program(struct tnl_compiled_program *p)
 {
    struct compilation cp;
    
-   memset(&cp, 0, sizeof(cp));
-   cp.m = m;
+   /* sanity checks */
+   assert(emit_func[OPCODE_ABS] == emit_ABS);
+   assert(emit_func[OPCODE_MUL] == emit_MUL);
+   assert(emit_func[OPCODE_XPD] == emit_XPD);
+
+   _mesa_memset(&cp, 0, sizeof(cp));
+   cp.p = p;
    cp.have_sse2 = 1;
 
-   if (m->func) {
-      free((void *)m->func);
-      m->func = NULL;
+   if (p->compiled_func) {
+      _mesa_free((void *)p->compiled_func);
+      p->compiled_func = NULL;
    }
 
    x86_init_func(&cp.func);
 
+   cp.fpucntl = RESTORE_FPU;
+
+
+   /* Note ctx state is not referenced in building the function, so it
+    * depends only on the list of instructions:
+    */
    if (!build_vertex_program(&cp)) {
       x86_release_func( &cp.func );
       return GL_FALSE;
    }
 
-   m->func = (void (*)(struct arb_vp_machine *))x86_get_func( &cp.func );
+
+   p->compiled_func = (void (*)(struct arb_vp_machine *))x86_get_func( &cp.func );
    return GL_TRUE;
 }
 
@@ -871,7 +1321,7 @@ _tnl_sse_codegen_vertex_program(struct arb_vp_machine *m)
 #else
 
 GLboolean
-_tnl_sse_codegen_vertex_program( GLcontext *ctx )
+_tnl_sse_codegen_vertex_program(struct tnl_compiled_program *p)
 {
    /* Dummy version for when USE_SSE_ASM not defined */
    return GL_FALSE;