17ec681f3Smrg/*
27ec681f3Smrg * Copyright (c) 2017 Rob Clark <robdclark@gmail.com>
37ec681f3Smrg *
47ec681f3Smrg * Permission is hereby granted, free of charge, to any person obtaining a
57ec681f3Smrg * copy of this software and associated documentation files (the "Software"),
67ec681f3Smrg * to deal in the Software without restriction, including without limitation
77ec681f3Smrg * the rights to use, copy, modify, merge, publish, distribute, sublicense,
87ec681f3Smrg * and/or sell copies of the Software, and to permit persons to whom the
97ec681f3Smrg * Software is furnished to do so, subject to the following conditions:
107ec681f3Smrg *
117ec681f3Smrg * The above copyright notice and this permission notice (including the next
127ec681f3Smrg * paragraph) shall be included in all copies or substantial portions of the
137ec681f3Smrg * Software.
147ec681f3Smrg *
157ec681f3Smrg * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
167ec681f3Smrg * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
177ec681f3Smrg * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
187ec681f3Smrg * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
197ec681f3Smrg * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
207ec681f3Smrg * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
217ec681f3Smrg * SOFTWARE.
227ec681f3Smrg */
237ec681f3Smrg
247ec681f3Smrg#include <assert.h>
257ec681f3Smrg#include <err.h>
267ec681f3Smrg#include <fcntl.h>
277ec681f3Smrg#include <getopt.h>
287ec681f3Smrg#include <stdarg.h>
297ec681f3Smrg#include <stdbool.h>
307ec681f3Smrg#include <stdint.h>
317ec681f3Smrg#include <stdio.h>
327ec681f3Smrg#include <stdlib.h>
337ec681f3Smrg#include <string.h>
347ec681f3Smrg#include <unistd.h>
357ec681f3Smrg
367ec681f3Smrg#include "util/os_file.h"
377ec681f3Smrg
387ec681f3Smrg#include "freedreno_pm4.h"
397ec681f3Smrg
407ec681f3Smrg#include "afuc.h"
417ec681f3Smrg#include "util.h"
427ec681f3Smrg#include "emu.h"
437ec681f3Smrg
447ec681f3Smrgstatic int gpuver;
457ec681f3Smrg
467ec681f3Smrg/* non-verbose mode should output something suitable to feed back into
477ec681f3Smrg * assembler.. verbose mode has additional output useful for debugging
487ec681f3Smrg * (like unexpected bits that are set)
497ec681f3Smrg */
507ec681f3Smrgstatic bool verbose = false;
517ec681f3Smrg
527ec681f3Smrg/* emulator mode: */
537ec681f3Smrgstatic bool emulator = false;
547ec681f3Smrg
557ec681f3Smrgstatic void
567ec681f3Smrgprint_gpu_reg(uint32_t regbase)
577ec681f3Smrg{
587ec681f3Smrg   if (regbase < 0x100)
597ec681f3Smrg      return;
607ec681f3Smrg
617ec681f3Smrg   char *name = afuc_gpu_reg_name(regbase);
627ec681f3Smrg   if (name) {
637ec681f3Smrg      printf("\t; %s", name);
647ec681f3Smrg      free(name);
657ec681f3Smrg   }
667ec681f3Smrg}
677ec681f3Smrg
687ec681f3Smrg#define printerr(fmt, ...) afuc_printc(AFUC_ERR, fmt, ##__VA_ARGS__)
697ec681f3Smrg#define printlbl(fmt, ...) afuc_printc(AFUC_LBL, fmt, ##__VA_ARGS__)
707ec681f3Smrg
717ec681f3Smrgvoid
727ec681f3Smrgprint_src(unsigned reg)
737ec681f3Smrg{
747ec681f3Smrg   if (reg == REG_REM)
757ec681f3Smrg      printf("$rem"); /* remainding dwords in packet */
767ec681f3Smrg   else if (reg == REG_MEMDATA)
777ec681f3Smrg      printf("$memdata");
787ec681f3Smrg   else if (reg == REG_REGDATA)
797ec681f3Smrg      printf("$regdata");
807ec681f3Smrg   else if (reg == REG_DATA)
817ec681f3Smrg      printf("$data");
827ec681f3Smrg   else
837ec681f3Smrg      printf("$%02x", reg);
847ec681f3Smrg}
857ec681f3Smrg
867ec681f3Smrgvoid
877ec681f3Smrgprint_dst(unsigned reg)
887ec681f3Smrg{
897ec681f3Smrg   if (reg == REG_REM)
907ec681f3Smrg      printf("$rem"); /* remainding dwords in packet */
917ec681f3Smrg   else if (reg == REG_ADDR)
927ec681f3Smrg      printf("$addr");
937ec681f3Smrg   else if (reg == REG_USRADDR)
947ec681f3Smrg      printf("$usraddr");
957ec681f3Smrg   else if (reg == REG_DATA)
967ec681f3Smrg      printf("$data");
977ec681f3Smrg   else
987ec681f3Smrg      printf("$%02x", reg);
997ec681f3Smrg}
1007ec681f3Smrg
1017ec681f3Smrgstatic void
1027ec681f3Smrgprint_alu_name(afuc_opc opc, uint32_t instr)
1037ec681f3Smrg{
1047ec681f3Smrg   if (opc == OPC_ADD) {
1057ec681f3Smrg      printf("add ");
1067ec681f3Smrg   } else if (opc == OPC_ADDHI) {
1077ec681f3Smrg      printf("addhi ");
1087ec681f3Smrg   } else if (opc == OPC_SUB) {
1097ec681f3Smrg      printf("sub ");
1107ec681f3Smrg   } else if (opc == OPC_SUBHI) {
1117ec681f3Smrg      printf("subhi ");
1127ec681f3Smrg   } else if (opc == OPC_AND) {
1137ec681f3Smrg      printf("and ");
1147ec681f3Smrg   } else if (opc == OPC_OR) {
1157ec681f3Smrg      printf("or ");
1167ec681f3Smrg   } else if (opc == OPC_XOR) {
1177ec681f3Smrg      printf("xor ");
1187ec681f3Smrg   } else if (opc == OPC_NOT) {
1197ec681f3Smrg      printf("not ");
1207ec681f3Smrg   } else if (opc == OPC_SHL) {
1217ec681f3Smrg      printf("shl ");
1227ec681f3Smrg   } else if (opc == OPC_USHR) {
1237ec681f3Smrg      printf("ushr ");
1247ec681f3Smrg   } else if (opc == OPC_ISHR) {
1257ec681f3Smrg      printf("ishr ");
1267ec681f3Smrg   } else if (opc == OPC_ROT) {
1277ec681f3Smrg      printf("rot ");
1287ec681f3Smrg   } else if (opc == OPC_MUL8) {
1297ec681f3Smrg      printf("mul8 ");
1307ec681f3Smrg   } else if (opc == OPC_MIN) {
1317ec681f3Smrg      printf("min ");
1327ec681f3Smrg   } else if (opc == OPC_MAX) {
1337ec681f3Smrg      printf("max ");
1347ec681f3Smrg   } else if (opc == OPC_CMP) {
1357ec681f3Smrg      printf("cmp ");
1367ec681f3Smrg   } else if (opc == OPC_MSB) {
1377ec681f3Smrg      printf("msb ");
1387ec681f3Smrg   } else {
1397ec681f3Smrg      printerr("[%08x]", instr);
1407ec681f3Smrg      printf("  ; alu%02x ", opc);
1417ec681f3Smrg   }
1427ec681f3Smrg}
1437ec681f3Smrg
1447ec681f3Smrgstatic const char *
1457ec681f3Smrggetpm4(uint32_t id)
1467ec681f3Smrg{
1477ec681f3Smrg   return afuc_pm_id_name(id);
1487ec681f3Smrg}
1497ec681f3Smrg
1507ec681f3Smrgstatic struct {
1517ec681f3Smrg   uint32_t offset;
1527ec681f3Smrg   uint32_t num_jump_labels;
1537ec681f3Smrg   uint32_t jump_labels[256];
1547ec681f3Smrg} jump_labels[1024];
1557ec681f3Smrgint num_jump_labels;
1567ec681f3Smrg
1577ec681f3Smrgstatic void
1587ec681f3Smrgadd_jump_table_entry(uint32_t n, uint32_t offset)
1597ec681f3Smrg{
1607ec681f3Smrg   int i;
1617ec681f3Smrg
1627ec681f3Smrg   if (n > 128) /* can't possibly be a PM4 PKT3.. */
1637ec681f3Smrg      return;
1647ec681f3Smrg
1657ec681f3Smrg   for (i = 0; i < num_jump_labels; i++)
1667ec681f3Smrg      if (jump_labels[i].offset == offset)
1677ec681f3Smrg         goto add_label;
1687ec681f3Smrg
1697ec681f3Smrg   num_jump_labels = i + 1;
1707ec681f3Smrg   jump_labels[i].offset = offset;
1717ec681f3Smrg   jump_labels[i].num_jump_labels = 0;
1727ec681f3Smrg
1737ec681f3Smrgadd_label:
1747ec681f3Smrg   jump_labels[i].jump_labels[jump_labels[i].num_jump_labels++] = n;
1757ec681f3Smrg   assert(jump_labels[i].num_jump_labels < 256);
1767ec681f3Smrg}
1777ec681f3Smrg
1787ec681f3Smrgstatic int
1797ec681f3Smrgget_jump_table_entry(uint32_t offset)
1807ec681f3Smrg{
1817ec681f3Smrg   int i;
1827ec681f3Smrg
1837ec681f3Smrg   for (i = 0; i < num_jump_labels; i++)
1847ec681f3Smrg      if (jump_labels[i].offset == offset)
1857ec681f3Smrg         return i;
1867ec681f3Smrg
1877ec681f3Smrg   return -1;
1887ec681f3Smrg}
1897ec681f3Smrg
1907ec681f3Smrgstatic uint32_t label_offsets[0x512];
1917ec681f3Smrgstatic int num_label_offsets;
1927ec681f3Smrg
1937ec681f3Smrgstatic int
1947ec681f3Smrglabel_idx(uint32_t offset, bool create)
1957ec681f3Smrg{
1967ec681f3Smrg   int i;
1977ec681f3Smrg   for (i = 0; i < num_label_offsets; i++)
1987ec681f3Smrg      if (offset == label_offsets[i])
1997ec681f3Smrg         return i;
2007ec681f3Smrg   if (!create)
2017ec681f3Smrg      return -1;
2027ec681f3Smrg   label_offsets[i] = offset;
2037ec681f3Smrg   num_label_offsets = i + 1;
2047ec681f3Smrg   return i;
2057ec681f3Smrg}
2067ec681f3Smrg
2077ec681f3Smrgstatic const char *
2087ec681f3Smrglabel_name(uint32_t offset, bool allow_jt)
2097ec681f3Smrg{
2107ec681f3Smrg   static char name[12];
2117ec681f3Smrg   int lidx;
2127ec681f3Smrg
2137ec681f3Smrg   if (allow_jt) {
2147ec681f3Smrg      lidx = get_jump_table_entry(offset);
2157ec681f3Smrg      if (lidx >= 0) {
2167ec681f3Smrg         int j;
2177ec681f3Smrg         for (j = 0; j < jump_labels[lidx].num_jump_labels; j++) {
2187ec681f3Smrg            uint32_t jump_label = jump_labels[lidx].jump_labels[j];
2197ec681f3Smrg            const char *str = getpm4(jump_label);
2207ec681f3Smrg            if (str)
2217ec681f3Smrg               return str;
2227ec681f3Smrg         }
2237ec681f3Smrg         // if we don't find anything w/ known name, maybe we should
2247ec681f3Smrg         // return UNKN%d to at least make it clear that this is some
2257ec681f3Smrg         // sort of jump-table entry?
2267ec681f3Smrg      }
2277ec681f3Smrg   }
2287ec681f3Smrg
2297ec681f3Smrg   lidx = label_idx(offset, false);
2307ec681f3Smrg   if (lidx < 0)
2317ec681f3Smrg      return NULL;
2327ec681f3Smrg   sprintf(name, "l%03d", lidx);
2337ec681f3Smrg   return name;
2347ec681f3Smrg}
2357ec681f3Smrg
2367ec681f3Smrgstatic uint32_t fxn_offsets[0x512];
2377ec681f3Smrgstatic int num_fxn_offsets;
2387ec681f3Smrg
2397ec681f3Smrgstatic int
2407ec681f3Smrgfxn_idx(uint32_t offset, bool create)
2417ec681f3Smrg{
2427ec681f3Smrg   int i;
2437ec681f3Smrg   for (i = 0; i < num_fxn_offsets; i++)
2447ec681f3Smrg      if (offset == fxn_offsets[i])
2457ec681f3Smrg         return i;
2467ec681f3Smrg   if (!create)
2477ec681f3Smrg      return -1;
2487ec681f3Smrg   fxn_offsets[i] = offset;
2497ec681f3Smrg   num_fxn_offsets = i + 1;
2507ec681f3Smrg   return i;
2517ec681f3Smrg}
2527ec681f3Smrg
2537ec681f3Smrgstatic const char *
2547ec681f3Smrgfxn_name(uint32_t offset)
2557ec681f3Smrg{
2567ec681f3Smrg   static char name[14];
2577ec681f3Smrg   int fidx = fxn_idx(offset, false);
2587ec681f3Smrg   if (fidx < 0)
2597ec681f3Smrg      return NULL;
2607ec681f3Smrg   sprintf(name, "fxn%02d", fidx);
2617ec681f3Smrg   return name;
2627ec681f3Smrg}
2637ec681f3Smrg
2647ec681f3Smrgvoid
2657ec681f3Smrgprint_control_reg(uint32_t id)
2667ec681f3Smrg{
2677ec681f3Smrg   char *name = afuc_control_reg_name(id);
2687ec681f3Smrg   if (name) {
2697ec681f3Smrg      printf("@%s", name);
2707ec681f3Smrg      free(name);
2717ec681f3Smrg   } else {
2727ec681f3Smrg      printf("0x%03x", id);
2737ec681f3Smrg   }
2747ec681f3Smrg}
2757ec681f3Smrg
2767ec681f3Smrgvoid
2777ec681f3Smrgprint_pipe_reg(uint32_t id)
2787ec681f3Smrg{
2797ec681f3Smrg   char *name = afuc_pipe_reg_name(id);
2807ec681f3Smrg   if (name) {
2817ec681f3Smrg      printf("|%s", name);
2827ec681f3Smrg      free(name);
2837ec681f3Smrg   } else {
2847ec681f3Smrg      printf("0x%03x", id);
2857ec681f3Smrg   }
2867ec681f3Smrg}
2877ec681f3Smrg
2887ec681f3Smrgstatic void
2897ec681f3Smrgdisasm_instr(uint32_t *instrs, unsigned pc)
2907ec681f3Smrg{
2917ec681f3Smrg   int jump_label_idx;
2927ec681f3Smrg   afuc_instr *instr = (void *)&instrs[pc];
2937ec681f3Smrg   const char *fname, *lname;
2947ec681f3Smrg   afuc_opc opc;
2957ec681f3Smrg   bool rep;
2967ec681f3Smrg
2977ec681f3Smrg   afuc_get_opc(instr, &opc, &rep);
2987ec681f3Smrg
2997ec681f3Smrg   lname = label_name(pc, false);
3007ec681f3Smrg   fname = fxn_name(pc);
3017ec681f3Smrg   jump_label_idx = get_jump_table_entry(pc);
3027ec681f3Smrg
3037ec681f3Smrg   if (jump_label_idx >= 0) {
3047ec681f3Smrg      int j;
3057ec681f3Smrg      printf("\n");
3067ec681f3Smrg      for (j = 0; j < jump_labels[jump_label_idx].num_jump_labels; j++) {
3077ec681f3Smrg         uint32_t jump_label = jump_labels[jump_label_idx].jump_labels[j];
3087ec681f3Smrg         const char *name = getpm4(jump_label);
3097ec681f3Smrg         if (name) {
3107ec681f3Smrg            printlbl("%s", name);
3117ec681f3Smrg         } else {
3127ec681f3Smrg            printlbl("UNKN%d", jump_label);
3137ec681f3Smrg         }
3147ec681f3Smrg         printf(":\n");
3157ec681f3Smrg      }
3167ec681f3Smrg   }
3177ec681f3Smrg
3187ec681f3Smrg   if (fname) {
3197ec681f3Smrg      printlbl("%s", fname);
3207ec681f3Smrg      printf(":\n");
3217ec681f3Smrg   }
3227ec681f3Smrg
3237ec681f3Smrg   if (lname) {
3247ec681f3Smrg      printlbl(" %s", lname);
3257ec681f3Smrg      printf(":");
3267ec681f3Smrg   } else {
3277ec681f3Smrg      printf("      ");
3287ec681f3Smrg   }
3297ec681f3Smrg
3307ec681f3Smrg   if (verbose) {
3317ec681f3Smrg      printf("\t%04x: %08x  ", pc, instrs[pc]);
3327ec681f3Smrg   } else {
3337ec681f3Smrg      printf("  ");
3347ec681f3Smrg   }
3357ec681f3Smrg
3367ec681f3Smrg   switch (opc) {
3377ec681f3Smrg   case OPC_NOP: {
3387ec681f3Smrg      /* a6xx changed the default immediate, and apparently 0
3397ec681f3Smrg       * is illegal now.
3407ec681f3Smrg       */
3417ec681f3Smrg      const uint32_t nop = gpuver >= 6 ? 0x1000000 : 0x0;
3427ec681f3Smrg      if (instrs[pc] != nop) {
3437ec681f3Smrg         printerr("[%08x]", instrs[pc]);
3447ec681f3Smrg         printf("  ; ");
3457ec681f3Smrg      }
3467ec681f3Smrg      if (rep)
3477ec681f3Smrg         printf("(rep)");
3487ec681f3Smrg      printf("nop");
3497ec681f3Smrg      print_gpu_reg(instrs[pc]);
3507ec681f3Smrg
3517ec681f3Smrg      break;
3527ec681f3Smrg   }
3537ec681f3Smrg   case OPC_ADD:
3547ec681f3Smrg   case OPC_ADDHI:
3557ec681f3Smrg   case OPC_SUB:
3567ec681f3Smrg   case OPC_SUBHI:
3577ec681f3Smrg   case OPC_AND:
3587ec681f3Smrg   case OPC_OR:
3597ec681f3Smrg   case OPC_XOR:
3607ec681f3Smrg   case OPC_NOT:
3617ec681f3Smrg   case OPC_SHL:
3627ec681f3Smrg   case OPC_USHR:
3637ec681f3Smrg   case OPC_ISHR:
3647ec681f3Smrg   case OPC_ROT:
3657ec681f3Smrg   case OPC_MUL8:
3667ec681f3Smrg   case OPC_MIN:
3677ec681f3Smrg   case OPC_MAX:
3687ec681f3Smrg   case OPC_CMP: {
3697ec681f3Smrg      bool src1 = true;
3707ec681f3Smrg
3717ec681f3Smrg      if (opc == OPC_NOT)
3727ec681f3Smrg         src1 = false;
3737ec681f3Smrg
3747ec681f3Smrg      if (rep)
3757ec681f3Smrg         printf("(rep)");
3767ec681f3Smrg
3777ec681f3Smrg      print_alu_name(opc, instrs[pc]);
3787ec681f3Smrg      print_dst(instr->alui.dst);
3797ec681f3Smrg      printf(", ");
3807ec681f3Smrg      if (src1) {
3817ec681f3Smrg         print_src(instr->alui.src);
3827ec681f3Smrg         printf(", ");
3837ec681f3Smrg      }
3847ec681f3Smrg      printf("0x%04x", instr->alui.uimm);
3857ec681f3Smrg      print_gpu_reg(instr->alui.uimm);
3867ec681f3Smrg
3877ec681f3Smrg      /* print out unexpected bits: */
3887ec681f3Smrg      if (verbose) {
3897ec681f3Smrg         if (instr->alui.src && !src1)
3907ec681f3Smrg            printerr("  (src=%02x)", instr->alui.src);
3917ec681f3Smrg      }
3927ec681f3Smrg
3937ec681f3Smrg      break;
3947ec681f3Smrg   }
3957ec681f3Smrg   case OPC_MOVI: {
3967ec681f3Smrg      if (rep)
3977ec681f3Smrg         printf("(rep)");
3987ec681f3Smrg      printf("mov ");
3997ec681f3Smrg      print_dst(instr->movi.dst);
4007ec681f3Smrg      printf(", 0x%04x", instr->movi.uimm);
4017ec681f3Smrg      if (instr->movi.shift)
4027ec681f3Smrg         printf(" << %u", instr->movi.shift);
4037ec681f3Smrg
4047ec681f3Smrg      if ((instr->movi.dst == REG_ADDR) && (instr->movi.shift >= 16)) {
4057ec681f3Smrg         uint32_t val = (uint32_t)instr->movi.uimm << (uint32_t)instr->movi.shift;
4067ec681f3Smrg         val &= ~0x40000;  /* b18 seems to be a flag */
4077ec681f3Smrg
4087ec681f3Smrg         if ((val & 0x00ffffff) == 0) {
4097ec681f3Smrg            printf("\t; ");
4107ec681f3Smrg            print_pipe_reg(val >> 24);
4117ec681f3Smrg            break;
4127ec681f3Smrg         }
4137ec681f3Smrg      }
4147ec681f3Smrg      /* using mov w/ << 16 is popular way to construct a pkt7
4157ec681f3Smrg       * header to send (for ex, from PFP to ME), so check that
4167ec681f3Smrg       * case first
4177ec681f3Smrg       */
4187ec681f3Smrg      if ((instr->movi.shift == 16) &&
4197ec681f3Smrg          ((instr->movi.uimm & 0xff00) == 0x7000)) {
4207ec681f3Smrg         unsigned opc, p;
4217ec681f3Smrg
4227ec681f3Smrg         opc = instr->movi.uimm & 0x7f;
4237ec681f3Smrg         p = pm4_odd_parity_bit(opc);
4247ec681f3Smrg
4257ec681f3Smrg         /* So, you'd think that checking the parity bit would be
4267ec681f3Smrg          * a good way to rule out false positives, but seems like
4277ec681f3Smrg          * ME doesn't really care.. at least it would filter out
4287ec681f3Smrg          * things that look like actual legit packets between
4297ec681f3Smrg          * PFP and ME..
4307ec681f3Smrg          */
4317ec681f3Smrg         if (1 || p == ((instr->movi.uimm >> 7) & 0x1)) {
4327ec681f3Smrg            const char *name = getpm4(opc);
4337ec681f3Smrg            printf("\t; ");
4347ec681f3Smrg            if (name)
4357ec681f3Smrg               printlbl("%s", name);
4367ec681f3Smrg            else
4377ec681f3Smrg               printlbl("UNKN%u", opc);
4387ec681f3Smrg            break;
4397ec681f3Smrg         }
4407ec681f3Smrg      }
4417ec681f3Smrg
4427ec681f3Smrg      print_gpu_reg((uint32_t)instr->movi.uimm << (uint32_t)instr->movi.shift);
4437ec681f3Smrg
4447ec681f3Smrg      break;
4457ec681f3Smrg   }
4467ec681f3Smrg   case OPC_ALU: {
4477ec681f3Smrg      bool src1 = true;
4487ec681f3Smrg
4497ec681f3Smrg      if (instr->alu.alu == OPC_NOT || instr->alu.alu == OPC_MSB)
4507ec681f3Smrg         src1 = false;
4517ec681f3Smrg
4527ec681f3Smrg      if (instr->alu.pad)
4537ec681f3Smrg         printf("[%08x]  ; ", instrs[pc]);
4547ec681f3Smrg
4557ec681f3Smrg      if (rep)
4567ec681f3Smrg         printf("(rep)");
4577ec681f3Smrg      if (instr->alu.xmov)
4587ec681f3Smrg         printf("(xmov%d)", instr->alu.xmov);
4597ec681f3Smrg
4607ec681f3Smrg      /* special case mnemonics:
4617ec681f3Smrg       *   reading $00 seems to always yield zero, and so:
4627ec681f3Smrg       *      or $dst, $00, $src -> mov $dst, $src
4637ec681f3Smrg       *   Maybe add one for negate too, ie.
4647ec681f3Smrg       *      sub $dst, $00, $src ???
4657ec681f3Smrg       */
4667ec681f3Smrg      if ((instr->alu.alu == OPC_OR) && !instr->alu.src1) {
4677ec681f3Smrg         printf("mov ");
4687ec681f3Smrg         src1 = false;
4697ec681f3Smrg      } else {
4707ec681f3Smrg         print_alu_name(instr->alu.alu, instrs[pc]);
4717ec681f3Smrg      }
4727ec681f3Smrg
4737ec681f3Smrg      print_dst(instr->alu.dst);
4747ec681f3Smrg      if (src1) {
4757ec681f3Smrg         printf(", ");
4767ec681f3Smrg         print_src(instr->alu.src1);
4777ec681f3Smrg      }
4787ec681f3Smrg      printf(", ");
4797ec681f3Smrg      print_src(instr->alu.src2);
4807ec681f3Smrg
4817ec681f3Smrg      /* print out unexpected bits: */
4827ec681f3Smrg      if (verbose) {
4837ec681f3Smrg         if (instr->alu.pad)
4847ec681f3Smrg            printerr("  (pad=%01x)", instr->alu.pad);
4857ec681f3Smrg         if (instr->alu.src1 && !src1)
4867ec681f3Smrg            printerr("  (src1=%02x)", instr->alu.src1);
4877ec681f3Smrg      }
4887ec681f3Smrg
4897ec681f3Smrg      /* xmov is a modifier that makes the processor execute up to 3
4907ec681f3Smrg       * extra mov's after the current instruction. Given an ALU
4917ec681f3Smrg       * instruction:
4927ec681f3Smrg       *
4937ec681f3Smrg       * (xmovN) alu $dst, $src1, $src2
4947ec681f3Smrg       *
4957ec681f3Smrg       * In all of the uses in the firmware blob, $dst and $src2 are one
4967ec681f3Smrg       * of the "special" registers $data, $addr, $addr2. I've observed
4977ec681f3Smrg       * that if $dst isn't "special" then it's replaced with $00
4987ec681f3Smrg       * instead of $data, but I haven't checked what happens if $src2
4997ec681f3Smrg       * isn't "special".  Anyway, in the usual case, the HW produces a
5007ec681f3Smrg       * count M = min(N, $rem) and then does the following:
5017ec681f3Smrg       *
5027ec681f3Smrg       * M = 1:
5037ec681f3Smrg       * mov $data, $src2
5047ec681f3Smrg       *
5057ec681f3Smrg       * M = 2:
5067ec681f3Smrg       * mov $data, $src2
5077ec681f3Smrg       * mov $data, $src2
5087ec681f3Smrg       *
5097ec681f3Smrg       * M = 3:
5107ec681f3Smrg       * mov $data, $src2
5117ec681f3Smrg       * mov $dst, $src2 (special case for CP_CONTEXT_REG_BUNCH)
5127ec681f3Smrg       * mov $data, $src2
5137ec681f3Smrg       *
5147ec681f3Smrg       * It seems to be frequently used in combination with (rep) to
5157ec681f3Smrg       * provide a kind of hardware-based loop unrolling, and there's
5167ec681f3Smrg       * even a special case in the ISA to be able to do this with
5177ec681f3Smrg       * CP_CONTEXT_REG_BUNCH. However (rep) isn't required.
5187ec681f3Smrg       *
5197ec681f3Smrg       * This dumps the expected extra instructions, assuming that $rem
5207ec681f3Smrg       * isn't too small.
5217ec681f3Smrg       */
5227ec681f3Smrg      if (verbose && instr->alu.xmov) {
5237ec681f3Smrg         for (int i = 0; i < instr->alu.xmov; i++) {
5247ec681f3Smrg            printf("\n        ; mov ");
5257ec681f3Smrg            if (instr->alu.dst < 0x1d)
5267ec681f3Smrg               printf("$00");
5277ec681f3Smrg            else if (instr->alu.xmov == 3 && i == 1)
5287ec681f3Smrg               print_dst(instr->alu.dst);
5297ec681f3Smrg            else
5307ec681f3Smrg               printf("$data");
5317ec681f3Smrg            printf(", ");
5327ec681f3Smrg            print_src(instr->alu.src2);
5337ec681f3Smrg         }
5347ec681f3Smrg      }
5357ec681f3Smrg
5367ec681f3Smrg      break;
5377ec681f3Smrg   }
5387ec681f3Smrg   case OPC_CWRITE6:
5397ec681f3Smrg   case OPC_CREAD6:
5407ec681f3Smrg   case OPC_STORE6:
5417ec681f3Smrg   case OPC_LOAD6: {
5427ec681f3Smrg      if (rep)
5437ec681f3Smrg         printf("(rep)");
5447ec681f3Smrg
5457ec681f3Smrg      bool is_control_reg = true;
5467ec681f3Smrg      bool is_store = true;
5477ec681f3Smrg      if (gpuver >= 6) {
5487ec681f3Smrg         switch (opc) {
5497ec681f3Smrg         case OPC_CWRITE6:
5507ec681f3Smrg            printf("cwrite ");
5517ec681f3Smrg            break;
5527ec681f3Smrg         case OPC_CREAD6:
5537ec681f3Smrg            is_store = false;
5547ec681f3Smrg            printf("cread ");
5557ec681f3Smrg            break;
5567ec681f3Smrg         case OPC_STORE6:
5577ec681f3Smrg            is_control_reg = false;
5587ec681f3Smrg            printf("store ");
5597ec681f3Smrg            break;
5607ec681f3Smrg         case OPC_LOAD6:
5617ec681f3Smrg            is_control_reg = false;
5627ec681f3Smrg            is_store = false;
5637ec681f3Smrg            printf("load ");
5647ec681f3Smrg            break;
5657ec681f3Smrg         default:
5667ec681f3Smrg            assert(!"unreachable");
5677ec681f3Smrg         }
5687ec681f3Smrg      } else {
5697ec681f3Smrg         switch (opc) {
5707ec681f3Smrg         case OPC_CWRITE5:
5717ec681f3Smrg            printf("cwrite ");
5727ec681f3Smrg            break;
5737ec681f3Smrg         case OPC_CREAD5:
5747ec681f3Smrg            is_store = false;
5757ec681f3Smrg            printf("cread ");
5767ec681f3Smrg            break;
5777ec681f3Smrg         default:
5787ec681f3Smrg            fprintf(stderr, "A6xx control opcode on A5xx?\n");
5797ec681f3Smrg            exit(1);
5807ec681f3Smrg         }
5817ec681f3Smrg      }
5827ec681f3Smrg
5837ec681f3Smrg      if (is_store)
5847ec681f3Smrg         print_src(instr->control.src1);
5857ec681f3Smrg      else
5867ec681f3Smrg         print_dst(instr->control.src1);
5877ec681f3Smrg      printf(", [");
5887ec681f3Smrg      print_src(instr->control.src2);
5897ec681f3Smrg      printf(" + ");
5907ec681f3Smrg      if (is_control_reg && instr->control.flags != 0x4)
5917ec681f3Smrg         print_control_reg(instr->control.uimm);
5927ec681f3Smrg      else
5937ec681f3Smrg         printf("0x%03x", instr->control.uimm);
5947ec681f3Smrg      printf("], 0x%x", instr->control.flags);
5957ec681f3Smrg      break;
5967ec681f3Smrg   }
5977ec681f3Smrg   case OPC_BRNEI:
5987ec681f3Smrg   case OPC_BREQI:
5997ec681f3Smrg   case OPC_BRNEB:
6007ec681f3Smrg   case OPC_BREQB: {
6017ec681f3Smrg      unsigned off = pc + instr->br.ioff;
6027ec681f3Smrg
6037ec681f3Smrg      assert(!rep);
6047ec681f3Smrg
6057ec681f3Smrg      /* Since $00 reads back zero, it can be used as src for
6067ec681f3Smrg       * unconditional branches.  (This only really makes sense
6077ec681f3Smrg       * for the BREQB.. or possible BRNEI if imm==0.)
6087ec681f3Smrg       *
6097ec681f3Smrg       * If bit=0 then branch is taken if *all* bits are zero.
6107ec681f3Smrg       * Otherwise it is taken if bit (bit-1) is clear.
6117ec681f3Smrg       *
6127ec681f3Smrg       * Note the instruction after a jump/branch is executed
6137ec681f3Smrg       * regardless of whether branch is taken, so use nop or
6147ec681f3Smrg       * take that into account in code.
6157ec681f3Smrg       */
6167ec681f3Smrg      if (instr->br.src || (opc != OPC_BRNEB)) {
6177ec681f3Smrg         bool immed = false;
6187ec681f3Smrg
6197ec681f3Smrg         if (opc == OPC_BRNEI) {
6207ec681f3Smrg            printf("brne ");
6217ec681f3Smrg            immed = true;
6227ec681f3Smrg         } else if (opc == OPC_BREQI) {
6237ec681f3Smrg            printf("breq ");
6247ec681f3Smrg            immed = true;
6257ec681f3Smrg         } else if (opc == OPC_BRNEB) {
6267ec681f3Smrg            printf("brne ");
6277ec681f3Smrg         } else if (opc == OPC_BREQB) {
6287ec681f3Smrg            printf("breq ");
6297ec681f3Smrg         }
6307ec681f3Smrg         print_src(instr->br.src);
6317ec681f3Smrg         if (immed) {
6327ec681f3Smrg            printf(", 0x%x,", instr->br.bit_or_imm);
6337ec681f3Smrg         } else {
6347ec681f3Smrg            printf(", b%u,", instr->br.bit_or_imm);
6357ec681f3Smrg         }
6367ec681f3Smrg      } else {
6377ec681f3Smrg         printf("jump");
6387ec681f3Smrg         if (verbose && instr->br.bit_or_imm) {
6397ec681f3Smrg            printerr("  (src=%03x, bit=%03x) ", instr->br.src,
6407ec681f3Smrg                     instr->br.bit_or_imm);
6417ec681f3Smrg         }
6427ec681f3Smrg      }
6437ec681f3Smrg
6447ec681f3Smrg      printf(" #");
6457ec681f3Smrg      printlbl("%s", label_name(off, true));
6467ec681f3Smrg      if (verbose)
6477ec681f3Smrg         printf(" (#%d, %04x)", instr->br.ioff, off);
6487ec681f3Smrg      break;
6497ec681f3Smrg   }
6507ec681f3Smrg   case OPC_CALL:
6517ec681f3Smrg      assert(!rep);
6527ec681f3Smrg      printf("call #");
6537ec681f3Smrg      printlbl("%s", fxn_name(instr->call.uoff));
6547ec681f3Smrg      if (verbose) {
6557ec681f3Smrg         printf(" (%04x)", instr->call.uoff);
6567ec681f3Smrg         if (instr->br.bit_or_imm || instr->br.src) {
6577ec681f3Smrg            printerr("  (src=%03x, bit=%03x) ", instr->br.src,
6587ec681f3Smrg                     instr->br.bit_or_imm);
6597ec681f3Smrg         }
6607ec681f3Smrg      }
6617ec681f3Smrg      break;
6627ec681f3Smrg   case OPC_RET:
6637ec681f3Smrg      assert(!rep);
6647ec681f3Smrg      if (instr->ret.pad)
6657ec681f3Smrg         printf("[%08x]  ; ", instrs[pc]);
6667ec681f3Smrg      if (instr->ret.interrupt)
6677ec681f3Smrg         printf("iret");
6687ec681f3Smrg      else
6697ec681f3Smrg         printf("ret");
6707ec681f3Smrg      break;
6717ec681f3Smrg   case OPC_WIN:
6727ec681f3Smrg      assert(!rep);
6737ec681f3Smrg      if (instr->waitin.pad)
6747ec681f3Smrg         printf("[%08x]  ; ", instrs[pc]);
6757ec681f3Smrg      printf("waitin");
6767ec681f3Smrg      if (verbose && instr->waitin.pad)
6777ec681f3Smrg         printerr("  (pad=%x)", instr->waitin.pad);
6787ec681f3Smrg      break;
6797ec681f3Smrg   case OPC_PREEMPTLEAVE6:
6807ec681f3Smrg      if (gpuver < 6) {
6817ec681f3Smrg         printf("[%08x]  ; op38", instrs[pc]);
6827ec681f3Smrg      } else {
6837ec681f3Smrg         printf("preemptleave #");
6847ec681f3Smrg         printlbl("%s", label_name(instr->call.uoff, true));
6857ec681f3Smrg      }
6867ec681f3Smrg      break;
6877ec681f3Smrg   case OPC_SETSECURE:
6887ec681f3Smrg      /* Note: This seems to implicitly read the secure/not-secure state
6897ec681f3Smrg       * to set from the low bit of $02, and implicitly jumps to pc + 3
6907ec681f3Smrg       * (i.e. skipping the next two instructions) if it succeeds. We
6917ec681f3Smrg       * print these implicit parameters to make reading the disassembly
6927ec681f3Smrg       * easier.
6937ec681f3Smrg       */
6947ec681f3Smrg      if (instr->pad)
6957ec681f3Smrg         printf("[%08x]  ; ", instrs[pc]);
6967ec681f3Smrg      printf("setsecure $02, #");
6977ec681f3Smrg      printlbl("%s", label_name(pc + 3, true));
6987ec681f3Smrg      break;
6997ec681f3Smrg   default:
7007ec681f3Smrg      printerr("[%08x]", instrs[pc]);
7017ec681f3Smrg      printf("  ; op%02x ", opc);
7027ec681f3Smrg      print_dst(instr->alui.dst);
7037ec681f3Smrg      printf(", ");
7047ec681f3Smrg      print_src(instr->alui.src);
7057ec681f3Smrg      print_gpu_reg(instrs[pc] & 0xffff);
7067ec681f3Smrg      break;
7077ec681f3Smrg   }
7087ec681f3Smrg   printf("\n");
7097ec681f3Smrg}
7107ec681f3Smrg
7117ec681f3Smrgstatic void
7127ec681f3Smrgsetup_packet_table(uint32_t *jmptbl, uint32_t sizedwords)
7137ec681f3Smrg{
7147ec681f3Smrg   num_jump_labels = 0;
7157ec681f3Smrg
7167ec681f3Smrg   for (unsigned i = 0; i < sizedwords; i++) {
7177ec681f3Smrg      unsigned offset = jmptbl[i];
7187ec681f3Smrg      unsigned n = i; // + CP_NOP;
7197ec681f3Smrg      add_jump_table_entry(n, offset);
7207ec681f3Smrg   }
7217ec681f3Smrg}
7227ec681f3Smrg
7237ec681f3Smrgstatic void
7247ec681f3Smrgsetup_labels(uint32_t *instrs, uint32_t sizedwords)
7257ec681f3Smrg{
7267ec681f3Smrg   afuc_opc opc;
7277ec681f3Smrg   bool rep;
7287ec681f3Smrg
7297ec681f3Smrg   num_label_offsets = 0;
7307ec681f3Smrg
7317ec681f3Smrg   for (unsigned i = 0; i < sizedwords; i++) {
7327ec681f3Smrg      afuc_instr *instr = (void *)&instrs[i];
7337ec681f3Smrg
7347ec681f3Smrg      afuc_get_opc(instr, &opc, &rep);
7357ec681f3Smrg
7367ec681f3Smrg      switch (opc) {
7377ec681f3Smrg      case OPC_BRNEI:
7387ec681f3Smrg      case OPC_BREQI:
7397ec681f3Smrg      case OPC_BRNEB:
7407ec681f3Smrg      case OPC_BREQB:
7417ec681f3Smrg         label_idx(i + instr->br.ioff, true);
7427ec681f3Smrg         break;
7437ec681f3Smrg      case OPC_PREEMPTLEAVE6:
7447ec681f3Smrg         if (gpuver >= 6)
7457ec681f3Smrg            label_idx(instr->call.uoff, true);
7467ec681f3Smrg         break;
7477ec681f3Smrg      case OPC_CALL:
7487ec681f3Smrg         fxn_idx(instr->call.uoff, true);
7497ec681f3Smrg         break;
7507ec681f3Smrg      case OPC_SETSECURE:
7517ec681f3Smrg         /* this implicitly jumps to pc + 3 if successful */
7527ec681f3Smrg         label_idx(i + 3, true);
7537ec681f3Smrg         break;
7547ec681f3Smrg      default:
7557ec681f3Smrg         break;
7567ec681f3Smrg      }
7577ec681f3Smrg   }
7587ec681f3Smrg}
7597ec681f3Smrg
7607ec681f3Smrgstatic void
7617ec681f3Smrgdisasm(struct emu *emu)
7627ec681f3Smrg{
7637ec681f3Smrg   uint32_t sizedwords = emu->sizedwords;
7647ec681f3Smrg   uint32_t lpac_offset = 0;
7657ec681f3Smrg
7667ec681f3Smrg   EMU_GPU_REG(CP_SQE_INSTR_BASE);
7677ec681f3Smrg   EMU_GPU_REG(CP_LPAC_SQE_INSTR_BASE);
7687ec681f3Smrg
7697ec681f3Smrg   emu_init(emu);
7707ec681f3Smrg
7717ec681f3Smrg#ifdef BOOTSTRAP_DEBUG
7727ec681f3Smrg   while (true) {
7737ec681f3Smrg      disasm_instr(emu->instrs, emu->gpr_regs.pc);
7747ec681f3Smrg      emu_step(emu);
7757ec681f3Smrg   }
7767ec681f3Smrg#endif
7777ec681f3Smrg
7787ec681f3Smrg   emu_run_bootstrap(emu);
7797ec681f3Smrg
7807ec681f3Smrg   /* Figure out if we have LPAC SQE appended: */
7817ec681f3Smrg   if (emu_get_reg64(emu, &CP_LPAC_SQE_INSTR_BASE)) {
7827ec681f3Smrg      lpac_offset = emu_get_reg64(emu, &CP_LPAC_SQE_INSTR_BASE) -
7837ec681f3Smrg            emu_get_reg64(emu, &CP_SQE_INSTR_BASE);
7847ec681f3Smrg      lpac_offset /= 4;
7857ec681f3Smrg      sizedwords = lpac_offset;
7867ec681f3Smrg   }
7877ec681f3Smrg
7887ec681f3Smrg   setup_packet_table(emu->jmptbl, ARRAY_SIZE(emu->jmptbl));
7897ec681f3Smrg   setup_labels(emu->instrs, emu->sizedwords);
7907ec681f3Smrg
7917ec681f3Smrg   /* TODO add option to emulate LPAC SQE instead: */
7927ec681f3Smrg   if (emulator) {
7937ec681f3Smrg      /* Start from clean slate: */
7947ec681f3Smrg      emu_fini(emu);
7957ec681f3Smrg      emu_init(emu);
7967ec681f3Smrg
7977ec681f3Smrg      while (true) {
7987ec681f3Smrg         disasm_instr(emu->instrs, emu->gpr_regs.pc);
7997ec681f3Smrg         emu_step(emu);
8007ec681f3Smrg      }
8017ec681f3Smrg   }
8027ec681f3Smrg
8037ec681f3Smrg   /* print instructions: */
8047ec681f3Smrg   for (int i = 0; i < sizedwords; i++) {
8057ec681f3Smrg      disasm_instr(emu->instrs, i);
8067ec681f3Smrg   }
8077ec681f3Smrg
8087ec681f3Smrg   if (!lpac_offset)
8097ec681f3Smrg      return;
8107ec681f3Smrg
8117ec681f3Smrg   printf(";\n");
8127ec681f3Smrg   printf("; LPAC microcode:\n");
8137ec681f3Smrg   printf(";\n");
8147ec681f3Smrg
8157ec681f3Smrg   emu_fini(emu);
8167ec681f3Smrg
8177ec681f3Smrg   emu->lpac = true;
8187ec681f3Smrg   emu->instrs += lpac_offset;
8197ec681f3Smrg   emu->sizedwords -= lpac_offset;
8207ec681f3Smrg
8217ec681f3Smrg   emu_init(emu);
8227ec681f3Smrg   emu_run_bootstrap(emu);
8237ec681f3Smrg
8247ec681f3Smrg   setup_packet_table(emu->jmptbl, ARRAY_SIZE(emu->jmptbl));
8257ec681f3Smrg   setup_labels(emu->instrs, emu->sizedwords);
8267ec681f3Smrg
8277ec681f3Smrg   /* print instructions: */
8287ec681f3Smrg   for (int i = 0; i < emu->sizedwords; i++) {
8297ec681f3Smrg      disasm_instr(emu->instrs, i);
8307ec681f3Smrg   }
8317ec681f3Smrg}
8327ec681f3Smrg
8337ec681f3Smrg
8347ec681f3Smrgstatic void
8357ec681f3Smrgdisasm_legacy(uint32_t *buf, int sizedwords)
8367ec681f3Smrg{
8377ec681f3Smrg   uint32_t *instrs = buf;
8387ec681f3Smrg   const int jmptbl_start = instrs[1] & 0xffff;
8397ec681f3Smrg   uint32_t *jmptbl = &buf[jmptbl_start];
8407ec681f3Smrg   int i;
8417ec681f3Smrg
8427ec681f3Smrg   /* parse jumptable: */
8437ec681f3Smrg   setup_packet_table(jmptbl, 0x80);
8447ec681f3Smrg
8457ec681f3Smrg   /* do a pre-pass to find instructions that are potential branch targets,
8467ec681f3Smrg    * and add labels for them:
8477ec681f3Smrg    */
8487ec681f3Smrg   setup_labels(instrs, jmptbl_start);
8497ec681f3Smrg
8507ec681f3Smrg   /* print instructions: */
8517ec681f3Smrg   for (i = 0; i < jmptbl_start; i++) {
8527ec681f3Smrg      disasm_instr(instrs, i);
8537ec681f3Smrg   }
8547ec681f3Smrg
8557ec681f3Smrg   /* print jumptable: */
8567ec681f3Smrg   if (verbose) {
8577ec681f3Smrg      printf(";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;\n");
8587ec681f3Smrg      printf("; JUMP TABLE\n");
8597ec681f3Smrg      for (i = 0; i < 0x7f; i++) {
8607ec681f3Smrg         int n = i; // + CP_NOP;
8617ec681f3Smrg         uint32_t offset = jmptbl[i];
8627ec681f3Smrg         const char *name = getpm4(n);
8637ec681f3Smrg         printf("%3d %02x: ", n, n);
8647ec681f3Smrg         printf("%04x", offset);
8657ec681f3Smrg         if (name) {
8667ec681f3Smrg            printf("   ; %s", name);
8677ec681f3Smrg         } else {
8687ec681f3Smrg            printf("   ; UNKN%d", n);
8697ec681f3Smrg         }
8707ec681f3Smrg         printf("\n");
8717ec681f3Smrg      }
8727ec681f3Smrg   }
8737ec681f3Smrg}
8747ec681f3Smrg
8757ec681f3Smrgstatic void
8767ec681f3Smrgusage(void)
8777ec681f3Smrg{
8787ec681f3Smrg   fprintf(stderr, "Usage:\n"
8797ec681f3Smrg                   "\tdisasm [-g GPUVER] [-v] [-c] filename.asm\n"
8807ec681f3Smrg                   "\t\t-g - specify GPU version (5, etc)\n"
8817ec681f3Smrg                   "\t\t-c - use colors\n"
8827ec681f3Smrg                   "\t\t-v - verbose output\n"
8837ec681f3Smrg                   "\t\t-e - emulator mode\n");
8847ec681f3Smrg   exit(2);
8857ec681f3Smrg}
8867ec681f3Smrg
8877ec681f3Smrgint
8887ec681f3Smrgmain(int argc, char **argv)
8897ec681f3Smrg{
8907ec681f3Smrg   uint32_t *buf;
8917ec681f3Smrg   char *file;
8927ec681f3Smrg   bool colors = false;
8937ec681f3Smrg   uint32_t gpu_id = 0;
8947ec681f3Smrg   size_t sz;
8957ec681f3Smrg   int c, ret;
8967ec681f3Smrg   bool unit_test = false;
8977ec681f3Smrg
8987ec681f3Smrg   /* Argument parsing: */
8997ec681f3Smrg   while ((c = getopt(argc, argv, "g:vceu")) != -1) {
9007ec681f3Smrg      switch (c) {
9017ec681f3Smrg      case 'g':
9027ec681f3Smrg         gpu_id = atoi(optarg);
9037ec681f3Smrg         break;
9047ec681f3Smrg      case 'v':
9057ec681f3Smrg         verbose = true;
9067ec681f3Smrg         break;
9077ec681f3Smrg      case 'c':
9087ec681f3Smrg         colors = true;
9097ec681f3Smrg         break;
9107ec681f3Smrg      case 'e':
9117ec681f3Smrg         emulator = true;
9127ec681f3Smrg         verbose  = true;
9137ec681f3Smrg         break;
9147ec681f3Smrg      case 'u':
9157ec681f3Smrg         unit_test = true;
9167ec681f3Smrg         break;
9177ec681f3Smrg      default:
9187ec681f3Smrg         usage();
9197ec681f3Smrg      }
9207ec681f3Smrg   }
9217ec681f3Smrg
9227ec681f3Smrg   if (optind >= argc) {
9237ec681f3Smrg      fprintf(stderr, "no file specified!\n");
9247ec681f3Smrg      usage();
9257ec681f3Smrg   }
9267ec681f3Smrg
9277ec681f3Smrg   file = argv[optind];
9287ec681f3Smrg
9297ec681f3Smrg   /* if gpu version not specified, infer from filename: */
9307ec681f3Smrg   if (!gpu_id) {
9317ec681f3Smrg      char *str = strstr(file, "a5");
9327ec681f3Smrg      if (!str)
9337ec681f3Smrg         str = strstr(file, "a6");
9347ec681f3Smrg      if (str)
9357ec681f3Smrg         gpu_id = atoi(str + 1);
9367ec681f3Smrg   }
9377ec681f3Smrg
9387ec681f3Smrg   if (gpu_id < 500) {
9397ec681f3Smrg      printf("invalid gpu_id: %d\n", gpu_id);
9407ec681f3Smrg      return -1;
9417ec681f3Smrg   }
9427ec681f3Smrg
9437ec681f3Smrg   gpuver = gpu_id / 100;
9447ec681f3Smrg
9457ec681f3Smrg   /* a6xx is *mostly* a superset of a5xx, but some opcodes shuffle
9467ec681f3Smrg    * around, and behavior of special regs is a bit different.  Right
9477ec681f3Smrg    * now we only bother to support the a6xx variant.
9487ec681f3Smrg    */
9497ec681f3Smrg   if (emulator && (gpuver != 6)) {
9507ec681f3Smrg      fprintf(stderr, "Emulator only supported on a6xx!\n");
9517ec681f3Smrg      return 1;
9527ec681f3Smrg   }
9537ec681f3Smrg
9547ec681f3Smrg   ret = afuc_util_init(gpuver, colors);
9557ec681f3Smrg   if (ret < 0) {
9567ec681f3Smrg      usage();
9577ec681f3Smrg   }
9587ec681f3Smrg
9597ec681f3Smrg   printf("; a%dxx microcode\n", gpuver);
9607ec681f3Smrg
9617ec681f3Smrg   buf = (uint32_t *)os_read_file(file, &sz);
9627ec681f3Smrg
9637ec681f3Smrg   if (!unit_test)
9647ec681f3Smrg      printf("; Disassembling microcode: %s\n", file);
9657ec681f3Smrg   printf("; Version: %08x\n\n", buf[1]);
9667ec681f3Smrg
9677ec681f3Smrg   if (gpuver < 6) {
9687ec681f3Smrg      disasm_legacy(&buf[1], sz / 4 - 1);
9697ec681f3Smrg   } else {
9707ec681f3Smrg      struct emu emu = {
9717ec681f3Smrg            .instrs = &buf[1],
9727ec681f3Smrg            .sizedwords = sz / 4 - 1,
9737ec681f3Smrg            .gpu_id = gpu_id,
9747ec681f3Smrg      };
9757ec681f3Smrg
9767ec681f3Smrg      disasm(&emu);
9777ec681f3Smrg   }
9787ec681f3Smrg
9797ec681f3Smrg   return 0;
9807ec681f3Smrg}
981