/*
 * Copyright 2005 Ben Skeggs.
 * SPDX-License-Identifier: MIT
 */

#include "r300_fragprog.h"

#include <stdio.h>

#include "r300_reg.h"

static void
presub_string(char out[10], unsigned int inst)
{
   switch (inst & 0x600000) {
   case R300_ALU_SRCP_1_MINUS_2_SRC0:
      sprintf(out, "bias");
      break;
   case R300_ALU_SRCP_SRC1_MINUS_SRC0:
      sprintf(out, "sub");
      break;
   case R300_ALU_SRCP_SRC1_PLUS_SRC0:
      sprintf(out, "add");
      break;
   case R300_ALU_SRCP_1_MINUS_SRC0:
      sprintf(out, "inv ");
      break;
   }
}

static int
get_msb(unsigned int bit, unsigned int r400_ext_addr)
{
   return (r400_ext_addr & bit) ? 1 << 5 : 0;
}

/* just some random things... */
void
r300FragmentProgramDump(struct radeon_compiler *c, void *user)
{
   struct r300_fragment_program_compiler *compiler = (struct r300_fragment_program_compiler *)c;
   struct r300_fragment_program_code *code = &compiler->code->code.r300;
   int n, i, j;
   static int pc = 0;

   fprintf(stderr, "pc=%d*************************************\n", pc++);

   fprintf(stderr, "Hardware program\n");
   fprintf(stderr, "----------------\n");
   if (c->is_r400) {
      fprintf(stderr, "code_offset_ext: %08x\n", code->r400_code_offset_ext);
   }

   for (n = 0; n <= (code->config & 3); n++) {
      uint32_t code_addr = code->code_addr[3 - (code->config & 3) + n];
      unsigned int alu_offset = ((code_addr & R300_ALU_START_MASK) >> R300_ALU_START_SHIFT) +
                                (((code->r400_code_offset_ext >> (24 - (n * 6))) & 0x7) << 6);
      unsigned int alu_end = ((code_addr & R300_ALU_SIZE_MASK) >> R300_ALU_SIZE_SHIFT) +
                             (((code->r400_code_offset_ext >> (27 - (n * 6))) & 0x7) << 6);
      int tex_offset = (code_addr & R300_TEX_START_MASK) >> R300_TEX_START_SHIFT;
      int tex_end = (code_addr & R300_TEX_SIZE_MASK) >> R300_TEX_SIZE_SHIFT;

      fprintf(stderr,
              "NODE %d: alu_offset: %u, tex_offset: %d, "
              "alu_end: %u, tex_end: %d  (code_addr: %08x)\n",
              n, alu_offset, tex_offset, alu_end, tex_end, code_addr);

      if (n > 0 || (code->config & R300_PFS_CNTL_FIRST_NODE_HAS_TEX)) {
         fprintf(stderr, "  TEX:\n");
         for (i = tex_offset; i <= tex_offset + tex_end; ++i) {
            const char *instr;

            switch ((code->tex.inst[i] >> R300_TEX_INST_SHIFT) & 15) {
            case R300_TEX_OP_LD:
               instr = "TEX";
               break;
            case R300_TEX_OP_KIL:
               instr = "KIL";
               break;
            case R300_TEX_OP_TXP:
               instr = "TXP";
               break;
            case R300_TEX_OP_TXB:
               instr = "TXB";
               break;
            default:
               instr = "UNKNOWN";
            }

            fprintf(stderr, "    %s t%i, %c%i, texture[%i]   (%08x)\n", instr,
                    (code->tex.inst[i] >> R300_DST_ADDR_SHIFT) & 31, 't',
                    (code->tex.inst[i] >> R300_SRC_ADDR_SHIFT) & 31,
                    (code->tex.inst[i] & R300_TEX_ID_MASK) >> R300_TEX_ID_SHIFT, code->tex.inst[i]);
         }
      }

      for (i = alu_offset; i <= alu_offset + alu_end; ++i) {
         char srcc[4][10], dstc[20];
         char srca[4][10], dsta[20];
         char argc[3][20];
         char arga[3][20];
         char flags[5], tmp[10];

         for (j = 0; j < 3; ++j) {
            int regc = code->alu.inst[i].rgb_addr >> (j * 6);
            int rega = code->alu.inst[i].alpha_addr >> (j * 6);
            int msbc = get_msb(R400_ADDR_EXT_RGB_MSB_BIT(j), code->alu.inst[i].r400_ext_addr);
            int msba = get_msb(R400_ADDR_EXT_A_MSB_BIT(j), code->alu.inst[i].r400_ext_addr);

            sprintf(srcc[j], "%c%i", (regc & 32) ? 'c' : 't', (regc & 31) | msbc);
            sprintf(srca[j], "%c%i", (rega & 32) ? 'c' : 't', (rega & 31) | msba);
         }

         dstc[0] = 0;
         sprintf(flags, "%s%s%s", (code->alu.inst[i].rgb_addr & R300_ALU_DSTC_REG_X) ? "x" : "",
                 (code->alu.inst[i].rgb_addr & R300_ALU_DSTC_REG_Y) ? "y" : "",
                 (code->alu.inst[i].rgb_addr & R300_ALU_DSTC_REG_Z) ? "z" : "");
         if (flags[0] != 0) {
            unsigned int msb = get_msb(R400_ADDRD_EXT_RGB_MSB_BIT, code->alu.inst[i].r400_ext_addr);

            sprintf(dstc, "t%i.%s ",
                    ((code->alu.inst[i].rgb_addr >> R300_ALU_DSTC_SHIFT) & 31) | msb, flags);
         }
         sprintf(flags, "%s%s%s", (code->alu.inst[i].rgb_addr & R300_ALU_DSTC_OUTPUT_X) ? "x" : "",
                 (code->alu.inst[i].rgb_addr & R300_ALU_DSTC_OUTPUT_Y) ? "y" : "",
                 (code->alu.inst[i].rgb_addr & R300_ALU_DSTC_OUTPUT_Z) ? "z" : "");
         if (flags[0] != 0) {
            sprintf(tmp, "o%i.%s", (code->alu.inst[i].rgb_addr >> 29) & 3, flags);
            strcat(dstc, tmp);
         }
         /* Presub */
         presub_string(srcc[3], code->alu.inst[i].rgb_inst);
         presub_string(srca[3], code->alu.inst[i].alpha_inst);

         dsta[0] = 0;
         if (code->alu.inst[i].alpha_addr & R300_ALU_DSTA_REG) {
            unsigned int msb = get_msb(R400_ADDRD_EXT_A_MSB_BIT, code->alu.inst[i].r400_ext_addr);
            sprintf(dsta, "t%i.w ",
                    ((code->alu.inst[i].alpha_addr >> R300_ALU_DSTA_SHIFT) & 31) | msb);
         }
         if (code->alu.inst[i].alpha_addr & R300_ALU_DSTA_OUTPUT) {
            sprintf(tmp, "o%i.w ", (code->alu.inst[i].alpha_addr >> 25) & 3);
            strcat(dsta, tmp);
         }
         if (code->alu.inst[i].alpha_addr & R300_ALU_DSTA_DEPTH) {
            strcat(dsta, "Z");
         }

         fprintf(stderr,
                 "%3i: xyz: %3s %3s %3s %5s-> %-20s (%08x)\n"
                 "       w: %3s %3s %3s %5s-> %-20s (%08x)\n",
                 i, srcc[0], srcc[1], srcc[2], srcc[3], dstc, code->alu.inst[i].rgb_addr, srca[0],
                 srca[1], srca[2], srca[3], dsta, code->alu.inst[i].alpha_addr);

         for (j = 0; j < 3; ++j) {
            int regc = code->alu.inst[i].rgb_inst >> (j * 7);
            int rega = code->alu.inst[i].alpha_inst >> (j * 7);
            int d;
            char buf[20];

            d = regc & 31;
            if (d < 12) {
               switch (d % 4) {
               case R300_ALU_ARGC_SRC0C_XYZ:
                  sprintf(buf, "%s.xyz", srcc[d / 4]);
                  break;
               case R300_ALU_ARGC_SRC0C_XXX:
                  sprintf(buf, "%s.xxx", srcc[d / 4]);
                  break;
               case R300_ALU_ARGC_SRC0C_YYY:
                  sprintf(buf, "%s.yyy", srcc[d / 4]);
                  break;
               case R300_ALU_ARGC_SRC0C_ZZZ:
                  sprintf(buf, "%s.zzz", srcc[d / 4]);
                  break;
               }
            } else if (d < 15) {
               sprintf(buf, "%s.www", srca[d - 12]);
            } else if (d < 20) {
               switch (d) {
               case R300_ALU_ARGC_SRCP_XYZ:
                  sprintf(buf, "srcp.xyz");
                  break;
               case R300_ALU_ARGC_SRCP_XXX:
                  sprintf(buf, "srcp.xxx");
                  break;
               case R300_ALU_ARGC_SRCP_YYY:
                  sprintf(buf, "srcp.yyy");
                  break;
               case R300_ALU_ARGC_SRCP_ZZZ:
                  sprintf(buf, "srcp.zzz");
                  break;
               case R300_ALU_ARGC_SRCP_WWW:
                  sprintf(buf, "srcp.www");
                  break;
               }
            } else if (d == 20) {
               sprintf(buf, "0.0");
            } else if (d == 21) {
               sprintf(buf, "1.0");
            } else if (d == 22) {
               sprintf(buf, "0.5");
            } else if (d >= 23 && d < 32) {
               d -= 23;
               switch (d / 3) {
               case 0:
                  sprintf(buf, "%s.yzx", srcc[d % 3]);
                  break;
               case 1:
                  sprintf(buf, "%s.zxy", srcc[d % 3]);
                  break;
               case 2:
                  sprintf(buf, "%s.Wzy", srcc[d % 3]);
                  break;
               }
            } else {
               sprintf(buf, "%i", d);
            }

            sprintf(argc[j], "%s%s%s%s", (regc & 32) ? "-" : "", (regc & 64) ? "|" : "", buf,
                    (regc & 64) ? "|" : "");

            d = rega & 31;
            if (d < 9) {
               sprintf(buf, "%s.%c", srcc[d / 3], 'x' + (char)(d % 3));
            } else if (d < 12) {
               sprintf(buf, "%s.w", srca[d - 9]);
            } else if (d < 16) {
               switch (d) {
               case R300_ALU_ARGA_SRCP_X:
                  sprintf(buf, "srcp.x");
                  break;
               case R300_ALU_ARGA_SRCP_Y:
                  sprintf(buf, "srcp.y");
                  break;
               case R300_ALU_ARGA_SRCP_Z:
                  sprintf(buf, "srcp.z");
                  break;
               case R300_ALU_ARGA_SRCP_W:
                  sprintf(buf, "srcp.w");
                  break;
               }
            } else if (d == 16) {
               sprintf(buf, "0.0");
            } else if (d == 17) {
               sprintf(buf, "1.0");
            } else if (d == 18) {
               sprintf(buf, "0.5");
            } else {
               sprintf(buf, "%i", d);
            }

            sprintf(arga[j], "%s%s%s%s", (rega & 32) ? "-" : "", (rega & 64) ? "|" : "", buf,
                    (rega & 64) ? "|" : "");
         }

         fprintf(stderr,
                 "     xyz: %8s %8s %8s    op: %08x %s\n"
                 "       w: %8s %8s %8s    op: %08x\n",
                 argc[0], argc[1], argc[2], code->alu.inst[i].rgb_inst,
                 code->alu.inst[i].rgb_inst & R300_ALU_INSERT_NOP ? "NOP" : "", arga[0], arga[1],
                 arga[2], code->alu.inst[i].alpha_inst);
      }
   }
}
