void
svga_shader_dump(
   const unsigned *assem,
   unsigned dwords,
   unsigned do_binary )
{
   boolean finished = FALSE;
   struct dump_info di;

   di.version = *assem++;
   di.is_ps = (di.version & 0xFFFF0000) == 0xFFFF0000;
   di.indent = 0;

   _debug_printf(
      "%s_%u_%u\n",
      di.is_ps ? "ps" : "vs",
      (di.version >> 8) & 0xff,
      di.version & 0xff );

   while (!finished) {
      struct sh_op op = *(struct sh_op *) assem;

      switch (op.opcode) {
      case SVGA3DOP_DCL:
         {
            struct sh_dcl dcl = *(struct sh_dcl *) assem;

            _debug_printf( "dcl" );
            switch (sh_dstreg_type(dcl.reg)) {
            case SVGA3DREG_INPUT:
               if ((di.is_ps && di.version >= SVGA3D_PS_30) ||
                   (!di.is_ps && di.version >= SVGA3D_VS_30)) {
                  dump_semantic(dcl.u.semantic.usage,
                                dcl.u.semantic.usage_index);
               }
               break;
            case SVGA3DREG_TEXCRDOUT:
               if (!di.is_ps && di.version >= SVGA3D_VS_30) {
                  dump_semantic(dcl.u.semantic.usage,
                                dcl.u.semantic.usage_index);
               }
               break;
            case SVGA3DREG_SAMPLER:
               dump_sampleinfo( dcl.u.sampleinfo );
               break;
            }
            dump_dstreg(dcl.reg, NULL, &di);
            _debug_printf( "\n" );
            assem += sizeof( struct sh_dcl ) / sizeof( unsigned );
         }
         break;

      case SVGA3DOP_DEFB:
         {
            struct sh_defb defb = *(struct sh_defb *) assem;

            _debug_printf( "defb " );
            dump_reg( defb.reg, NULL, &di );
            _debug_printf( ", " );
            dump_bdata( defb.data );
            _debug_printf( "\n" );
            assem += sizeof( struct sh_defb ) / sizeof( unsigned );
         }
         break;

      case SVGA3DOP_DEFI:
         {
            struct sh_defi defi = *(struct sh_defi *) assem;

            _debug_printf( "defi " );
            dump_reg( defi.reg, NULL, &di );
            _debug_printf( ", " );
            dump_idata( defi.idata );
            _debug_printf( "\n" );
            assem += sizeof( struct sh_defi ) / sizeof( unsigned );
         }
         break;

      case SVGA3DOP_TEXCOORD:
         {
            struct sh_opcode_info info = *svga_opcode_info(op.opcode);

            assert(di.is_ps);
            if (di.version > SVGA3D_PS_13) {
               assert(info.num_src == 0);

               info.num_src = 1;
            }

            dump_inst(&di, &assem, op, &info);
         }
         break;

      case SVGA3DOP_TEX:
         {
            struct sh_opcode_info info = *svga_opcode_info(op.opcode);

            assert(di.is_ps);
            if (di.version > SVGA3D_PS_13) {
               assert(info.num_src == 0);

               if (di.version > SVGA3D_PS_14) {
                  info.num_src = 2;
                  info.mnemonic = "texld";
               } else {
                  info.num_src = 1;
               }
            }

            dump_inst(&di, &assem, op, &info);
         }
         break;

      case SVGA3DOP_DEF:
         {
            struct sh_def def = *(struct sh_def *) assem;

            _debug_printf( "def " );
            dump_reg( def.reg, NULL, &di );
            _debug_printf( ", " );
            dump_cdata( def.cdata );
            _debug_printf( "\n" );
            assem += sizeof( struct sh_def ) / sizeof( unsigned );
         }
         break;

      case SVGA3DOP_SINCOS:
         {
            struct sh_opcode_info info = *svga_opcode_info(op.opcode);

            if ((di.is_ps && di.version >= SVGA3D_PS_30) ||
                (!di.is_ps && di.version >= SVGA3D_VS_30)) {
               assert(info.num_src == 3);

               info.num_src = 1;
            }

            dump_inst(&di, &assem, op, &info);
         }
         break;

      case SVGA3DOP_PHASE:
         _debug_printf( "phase\n" );
         assem += sizeof( struct sh_op ) / sizeof( unsigned );
         break;

      case SVGA3DOP_COMMENT:
         {
            struct sh_comment comment = *(struct sh_comment *)assem;

            /* Ignore comment contents. */
            assem += sizeof(struct sh_comment) / sizeof(unsigned) + comment.size;
         }
         break;

      case SVGA3DOP_END:
         finished = TRUE;
         break;

      default:
         {
            const struct sh_opcode_info *info = svga_opcode_info(op.opcode);

            dump_inst(&di, &assem, op, info);
         }
      }
   }
}
void
svga_shader_dump(
   const unsigned *assem,
   unsigned dwords,
   unsigned do_binary )
{
   const unsigned *start = assem;
   boolean finished = FALSE;
   struct dump_info di;
   unsigned i;

   if (do_binary) {
      for (i = 0; i < dwords; i++) 
         debug_printf("  0x%08x,\n", assem[i]);
      
      debug_printf("\n\n");
   }

   di.version.value = *assem++;
   di.is_ps = (di.version.type == SVGA3D_PS_TYPE);

   debug_printf(
      "%s_%u_%u\n",
      di.is_ps ? "ps" : "vs",
      di.version.major,
      di.version.minor );

   while (!finished) {
      struct sh_op op = *(struct sh_op *) assem;

      if (assem - start >= dwords) {
         debug_printf("... ran off end of buffer\n");
         assert(0);
         return;
      }

      switch (op.opcode) {
      case SVGA3DOP_DCL:
         {
            struct sh_dcl dcl = *(struct sh_dcl *) assem;

            debug_printf( "dcl" );
            if (sh_dstreg_type( dcl.reg ) == SVGA3DREG_SAMPLER)
               dump_sampleinfo( dcl.u.ps.sampleinfo );
            else if (di.is_ps) {
               if (di.version.major == 3 && 
                   sh_dstreg_type( dcl.reg ) != SVGA3DREG_MISCTYPE)
                  dump_usageinfo( dcl.u.vs.semantic );
            }
            else
               dump_usageinfo( dcl.u.vs.semantic );
            dump_dstreg( dcl.reg, &di );
            debug_printf( "\n" );
            assem += sizeof( struct sh_dcl ) / sizeof( unsigned );
         }
         break;

      case SVGA3DOP_DEFB:
         {
            struct sh_defb defb = *(struct sh_defb *) assem;

            debug_printf( "defb " );
            dump_reg( defb.reg, NULL, &di );
            debug_printf( ", " );
            dump_bdata( defb.data );
            debug_printf( "\n" );
            assem += sizeof( struct sh_defb ) / sizeof( unsigned );
         }
         break;

      case SVGA3DOP_DEFI:
         {
            struct sh_defi defi = *(struct sh_defi *) assem;

            debug_printf( "defi " );
            dump_reg( defi.reg, NULL, &di );
            debug_printf( ", " );
            dump_idata( defi.idata );
            debug_printf( "\n" );
            assem += sizeof( struct sh_defi ) / sizeof( unsigned );
         }
         break;

      case SVGA3DOP_TEXCOORD:
         assert( di.is_ps );
         dump_op( op, "texcoord" );
         if (0) {
            struct sh_dstop dstop = *(struct sh_dstop *) assem;
            dump_dstreg( dstop.dst, &di );
            assem += sizeof( struct sh_dstop ) / sizeof( unsigned );
         }
         else {
            struct sh_unaryop unaryop = *(struct sh_unaryop *) assem;
            dump_dstreg( unaryop.dst, &di );
            debug_printf( ", " );
            dump_srcreg( unaryop.src, NULL, &di );
            assem += sizeof( struct sh_unaryop ) / sizeof( unsigned );
         }
         debug_printf( "\n" );
         break;

      case SVGA3DOP_TEX:
         assert( di.is_ps );
         if (0) {
            dump_op( op, "tex" );
            if (0) {
               struct sh_dstop dstop = *(struct sh_dstop *) assem;

               dump_dstreg( dstop.dst, &di );
               assem += sizeof( struct sh_dstop ) / sizeof( unsigned );
            }
            else {
               struct sh_unaryop unaryop = *(struct sh_unaryop *) assem;

               dump_dstreg( unaryop.dst, &di );
               debug_printf( ", " );
               dump_srcreg( unaryop.src, NULL, &di );
               assem += sizeof( struct sh_unaryop ) / sizeof( unsigned );
            }
         }
         else {
            struct sh_binaryop binaryop = *(struct sh_binaryop *) assem;

            dump_op( op, "texld" );
            dump_dstreg( binaryop.dst, &di );
            debug_printf( ", " );
            dump_srcreg( binaryop.src0, NULL, &di );
            debug_printf( ", " );
            dump_srcreg( binaryop.src1, NULL, &di );
            assem += sizeof( struct sh_binaryop ) / sizeof( unsigned );
         }
         debug_printf( "\n" );
         break;

      case SVGA3DOP_DEF:
         {
            struct sh_def def = *(struct sh_def *) assem;

            debug_printf( "def " );
            dump_reg( def.reg, NULL, &di );
            debug_printf( ", " );
            dump_cdata( def.cdata );
            debug_printf( "\n" );
            assem += sizeof( struct sh_def ) / sizeof( unsigned );
         }
         break;

      case SVGA3DOP_PHASE:
         debug_printf( "phase\n" );
         assem += sizeof( struct sh_op ) / sizeof( unsigned );
         break;

      case SVGA3DOP_COMMENT:
         {
            struct sh_comment comment = *(struct sh_comment *)assem;

            /* Ignore comment contents. */
            assem += sizeof(struct sh_comment) / sizeof(unsigned) + comment.size;
         }
         break;

      case SVGA3DOP_RET:
         debug_printf( "ret\n" );
         assem += sizeof( struct sh_op ) / sizeof( unsigned );
         break;

      case SVGA3DOP_END:
         debug_printf( "end\n" );
         finished = TRUE;
         break;

      default:
         {
            const struct sh_opcode_info *info = svga_opcode_info( op.opcode );
            uint i;
            uint num_src = info->num_src + op.predicated;
            boolean not_first_arg = FALSE;

            assert( info->num_dst <= 1 );

            if (op.opcode == SVGA3DOP_SINCOS && di.version.major < 3)
               num_src += 2;

            dump_comp_op( op, info->mnemonic );
            assem += sizeof( struct sh_op ) / sizeof( unsigned );

            if (info->num_dst > 0) {
               struct sh_dstreg dstreg = *(struct sh_dstreg *) assem;

               dump_dstreg( dstreg, &di );
               assem += sizeof( struct sh_dstreg ) / sizeof( unsigned );
               not_first_arg = TRUE;
            }

            for (i = 0; i < num_src; i++) {
               struct sh_srcreg srcreg;
               struct sh_srcreg indreg;

               srcreg = *(struct sh_srcreg *) assem;
               assem += sizeof( struct sh_srcreg ) / sizeof( unsigned );
               if (srcreg.relative && !di.is_ps && di.version.major >= 2) {
                  indreg = *(struct sh_srcreg *) assem;
                  assem += sizeof( struct sh_srcreg ) / sizeof( unsigned );
               }

               if (not_first_arg)
                  debug_printf( ", " );
               else
                  debug_printf( " " );
               dump_srcreg( srcreg, &indreg, &di );
               not_first_arg = TRUE;
            }

            debug_printf( "\n" );
         }
      }
   }
}