1bf215546Sopenharmony_ci/*
2bf215546Sopenharmony_ci * Copyright (c) 2017 Rob Clark <robdclark@gmail.com>
3bf215546Sopenharmony_ci *
4bf215546Sopenharmony_ci * Permission is hereby granted, free of charge, to any person obtaining a
5bf215546Sopenharmony_ci * copy of this software and associated documentation files (the "Software"),
6bf215546Sopenharmony_ci * to deal in the Software without restriction, including without limitation
7bf215546Sopenharmony_ci * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8bf215546Sopenharmony_ci * and/or sell copies of the Software, and to permit persons to whom the
9bf215546Sopenharmony_ci * Software is furnished to do so, subject to the following conditions:
10bf215546Sopenharmony_ci *
11bf215546Sopenharmony_ci * The above copyright notice and this permission notice (including the next
12bf215546Sopenharmony_ci * paragraph) shall be included in all copies or substantial portions of the
13bf215546Sopenharmony_ci * Software.
14bf215546Sopenharmony_ci *
15bf215546Sopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16bf215546Sopenharmony_ci * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17bf215546Sopenharmony_ci * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
18bf215546Sopenharmony_ci * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19bf215546Sopenharmony_ci * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20bf215546Sopenharmony_ci * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21bf215546Sopenharmony_ci * SOFTWARE.
22bf215546Sopenharmony_ci */
23bf215546Sopenharmony_ci
24bf215546Sopenharmony_ci#include <assert.h>
25bf215546Sopenharmony_ci#include <err.h>
26bf215546Sopenharmony_ci#include <fcntl.h>
27bf215546Sopenharmony_ci#include <getopt.h>
28bf215546Sopenharmony_ci#include <stdarg.h>
29bf215546Sopenharmony_ci#include <stdbool.h>
30bf215546Sopenharmony_ci#include <stdint.h>
31bf215546Sopenharmony_ci#include <stdio.h>
32bf215546Sopenharmony_ci#include <stdlib.h>
33bf215546Sopenharmony_ci#include <string.h>
34bf215546Sopenharmony_ci#include <unistd.h>
35bf215546Sopenharmony_ci
36bf215546Sopenharmony_ci#include "util/os_file.h"
37bf215546Sopenharmony_ci
38bf215546Sopenharmony_ci#include "freedreno_pm4.h"
39bf215546Sopenharmony_ci
40bf215546Sopenharmony_ci#include "afuc.h"
41bf215546Sopenharmony_ci#include "util.h"
42bf215546Sopenharmony_ci#include "emu.h"
43bf215546Sopenharmony_ci
44bf215546Sopenharmony_cistatic int gpuver;
45bf215546Sopenharmony_ci
46bf215546Sopenharmony_ci/* non-verbose mode should output something suitable to feed back into
47bf215546Sopenharmony_ci * assembler.. verbose mode has additional output useful for debugging
48bf215546Sopenharmony_ci * (like unexpected bits that are set)
49bf215546Sopenharmony_ci */
50bf215546Sopenharmony_cistatic bool verbose = false;
51bf215546Sopenharmony_ci
52bf215546Sopenharmony_ci/* emulator mode: */
53bf215546Sopenharmony_cistatic bool emulator = false;
54bf215546Sopenharmony_ci
55bf215546Sopenharmony_cistatic void
56bf215546Sopenharmony_ciprint_gpu_reg(uint32_t regbase)
57bf215546Sopenharmony_ci{
58bf215546Sopenharmony_ci   if (regbase < 0x100)
59bf215546Sopenharmony_ci      return;
60bf215546Sopenharmony_ci
61bf215546Sopenharmony_ci   char *name = afuc_gpu_reg_name(regbase);
62bf215546Sopenharmony_ci   if (name) {
63bf215546Sopenharmony_ci      printf("\t; %s", name);
64bf215546Sopenharmony_ci      free(name);
65bf215546Sopenharmony_ci   }
66bf215546Sopenharmony_ci}
67bf215546Sopenharmony_ci
68bf215546Sopenharmony_ci#define printerr(fmt, ...) afuc_printc(AFUC_ERR, fmt, ##__VA_ARGS__)
69bf215546Sopenharmony_ci#define printlbl(fmt, ...) afuc_printc(AFUC_LBL, fmt, ##__VA_ARGS__)
70bf215546Sopenharmony_ci
71bf215546Sopenharmony_civoid
72bf215546Sopenharmony_ciprint_src(unsigned reg)
73bf215546Sopenharmony_ci{
74bf215546Sopenharmony_ci   if (reg == REG_REM)
75bf215546Sopenharmony_ci      printf("$rem"); /* remainding dwords in packet */
76bf215546Sopenharmony_ci   else if (reg == REG_MEMDATA)
77bf215546Sopenharmony_ci      printf("$memdata");
78bf215546Sopenharmony_ci   else if (reg == REG_REGDATA)
79bf215546Sopenharmony_ci      printf("$regdata");
80bf215546Sopenharmony_ci   else if (reg == REG_DATA)
81bf215546Sopenharmony_ci      printf("$data");
82bf215546Sopenharmony_ci   else
83bf215546Sopenharmony_ci      printf("$%02x", reg);
84bf215546Sopenharmony_ci}
85bf215546Sopenharmony_ci
86bf215546Sopenharmony_civoid
87bf215546Sopenharmony_ciprint_dst(unsigned reg)
88bf215546Sopenharmony_ci{
89bf215546Sopenharmony_ci   if (reg == REG_REM)
90bf215546Sopenharmony_ci      printf("$rem"); /* remainding dwords in packet */
91bf215546Sopenharmony_ci   else if (reg == REG_ADDR)
92bf215546Sopenharmony_ci      printf("$addr");
93bf215546Sopenharmony_ci   else if (reg == REG_USRADDR)
94bf215546Sopenharmony_ci      printf("$usraddr");
95bf215546Sopenharmony_ci   else if (reg == REG_DATA)
96bf215546Sopenharmony_ci      printf("$data");
97bf215546Sopenharmony_ci   else
98bf215546Sopenharmony_ci      printf("$%02x", reg);
99bf215546Sopenharmony_ci}
100bf215546Sopenharmony_ci
101bf215546Sopenharmony_cistatic void
102bf215546Sopenharmony_ciprint_alu_name(afuc_opc opc, uint32_t instr)
103bf215546Sopenharmony_ci{
104bf215546Sopenharmony_ci   if (opc == OPC_ADD) {
105bf215546Sopenharmony_ci      printf("add ");
106bf215546Sopenharmony_ci   } else if (opc == OPC_ADDHI) {
107bf215546Sopenharmony_ci      printf("addhi ");
108bf215546Sopenharmony_ci   } else if (opc == OPC_SUB) {
109bf215546Sopenharmony_ci      printf("sub ");
110bf215546Sopenharmony_ci   } else if (opc == OPC_SUBHI) {
111bf215546Sopenharmony_ci      printf("subhi ");
112bf215546Sopenharmony_ci   } else if (opc == OPC_AND) {
113bf215546Sopenharmony_ci      printf("and ");
114bf215546Sopenharmony_ci   } else if (opc == OPC_OR) {
115bf215546Sopenharmony_ci      printf("or ");
116bf215546Sopenharmony_ci   } else if (opc == OPC_XOR) {
117bf215546Sopenharmony_ci      printf("xor ");
118bf215546Sopenharmony_ci   } else if (opc == OPC_NOT) {
119bf215546Sopenharmony_ci      printf("not ");
120bf215546Sopenharmony_ci   } else if (opc == OPC_SHL) {
121bf215546Sopenharmony_ci      printf("shl ");
122bf215546Sopenharmony_ci   } else if (opc == OPC_USHR) {
123bf215546Sopenharmony_ci      printf("ushr ");
124bf215546Sopenharmony_ci   } else if (opc == OPC_ISHR) {
125bf215546Sopenharmony_ci      printf("ishr ");
126bf215546Sopenharmony_ci   } else if (opc == OPC_ROT) {
127bf215546Sopenharmony_ci      printf("rot ");
128bf215546Sopenharmony_ci   } else if (opc == OPC_MUL8) {
129bf215546Sopenharmony_ci      printf("mul8 ");
130bf215546Sopenharmony_ci   } else if (opc == OPC_MIN) {
131bf215546Sopenharmony_ci      printf("min ");
132bf215546Sopenharmony_ci   } else if (opc == OPC_MAX) {
133bf215546Sopenharmony_ci      printf("max ");
134bf215546Sopenharmony_ci   } else if (opc == OPC_CMP) {
135bf215546Sopenharmony_ci      printf("cmp ");
136bf215546Sopenharmony_ci   } else if (opc == OPC_MSB) {
137bf215546Sopenharmony_ci      printf("msb ");
138bf215546Sopenharmony_ci   } else {
139bf215546Sopenharmony_ci      printerr("[%08x]", instr);
140bf215546Sopenharmony_ci      printf("  ; alu%02x ", opc);
141bf215546Sopenharmony_ci   }
142bf215546Sopenharmony_ci}
143bf215546Sopenharmony_ci
144bf215546Sopenharmony_cistatic const char *
145bf215546Sopenharmony_cigetpm4(uint32_t id)
146bf215546Sopenharmony_ci{
147bf215546Sopenharmony_ci   return afuc_pm_id_name(id);
148bf215546Sopenharmony_ci}
149bf215546Sopenharmony_ci
150bf215546Sopenharmony_cistatic struct {
151bf215546Sopenharmony_ci   uint32_t offset;
152bf215546Sopenharmony_ci   uint32_t num_jump_labels;
153bf215546Sopenharmony_ci   uint32_t jump_labels[256];
154bf215546Sopenharmony_ci} jump_labels[1024];
155bf215546Sopenharmony_ciint num_jump_labels;
156bf215546Sopenharmony_ci
157bf215546Sopenharmony_cistatic void
158bf215546Sopenharmony_ciadd_jump_table_entry(uint32_t n, uint32_t offset)
159bf215546Sopenharmony_ci{
160bf215546Sopenharmony_ci   int i;
161bf215546Sopenharmony_ci
162bf215546Sopenharmony_ci   if (n > 128) /* can't possibly be a PM4 PKT3.. */
163bf215546Sopenharmony_ci      return;
164bf215546Sopenharmony_ci
165bf215546Sopenharmony_ci   for (i = 0; i < num_jump_labels; i++)
166bf215546Sopenharmony_ci      if (jump_labels[i].offset == offset)
167bf215546Sopenharmony_ci         goto add_label;
168bf215546Sopenharmony_ci
169bf215546Sopenharmony_ci   num_jump_labels = i + 1;
170bf215546Sopenharmony_ci   jump_labels[i].offset = offset;
171bf215546Sopenharmony_ci   jump_labels[i].num_jump_labels = 0;
172bf215546Sopenharmony_ci
173bf215546Sopenharmony_ciadd_label:
174bf215546Sopenharmony_ci   jump_labels[i].jump_labels[jump_labels[i].num_jump_labels++] = n;
175bf215546Sopenharmony_ci   assert(jump_labels[i].num_jump_labels < 256);
176bf215546Sopenharmony_ci}
177bf215546Sopenharmony_ci
178bf215546Sopenharmony_cistatic int
179bf215546Sopenharmony_ciget_jump_table_entry(uint32_t offset)
180bf215546Sopenharmony_ci{
181bf215546Sopenharmony_ci   int i;
182bf215546Sopenharmony_ci
183bf215546Sopenharmony_ci   for (i = 0; i < num_jump_labels; i++)
184bf215546Sopenharmony_ci      if (jump_labels[i].offset == offset)
185bf215546Sopenharmony_ci         return i;
186bf215546Sopenharmony_ci
187bf215546Sopenharmony_ci   return -1;
188bf215546Sopenharmony_ci}
189bf215546Sopenharmony_ci
190bf215546Sopenharmony_cistatic uint32_t label_offsets[0x512];
191bf215546Sopenharmony_cistatic int num_label_offsets;
192bf215546Sopenharmony_ci
193bf215546Sopenharmony_cistatic int
194bf215546Sopenharmony_cilabel_idx(uint32_t offset, bool create)
195bf215546Sopenharmony_ci{
196bf215546Sopenharmony_ci   int i;
197bf215546Sopenharmony_ci   for (i = 0; i < num_label_offsets; i++)
198bf215546Sopenharmony_ci      if (offset == label_offsets[i])
199bf215546Sopenharmony_ci         return i;
200bf215546Sopenharmony_ci   if (!create)
201bf215546Sopenharmony_ci      return -1;
202bf215546Sopenharmony_ci   label_offsets[i] = offset;
203bf215546Sopenharmony_ci   num_label_offsets = i + 1;
204bf215546Sopenharmony_ci   return i;
205bf215546Sopenharmony_ci}
206bf215546Sopenharmony_ci
207bf215546Sopenharmony_cistatic const char *
208bf215546Sopenharmony_cilabel_name(uint32_t offset, bool allow_jt)
209bf215546Sopenharmony_ci{
210bf215546Sopenharmony_ci   static char name[12];
211bf215546Sopenharmony_ci   int lidx;
212bf215546Sopenharmony_ci
213bf215546Sopenharmony_ci   if (allow_jt) {
214bf215546Sopenharmony_ci      lidx = get_jump_table_entry(offset);
215bf215546Sopenharmony_ci      if (lidx >= 0) {
216bf215546Sopenharmony_ci         int j;
217bf215546Sopenharmony_ci         for (j = 0; j < jump_labels[lidx].num_jump_labels; j++) {
218bf215546Sopenharmony_ci            uint32_t jump_label = jump_labels[lidx].jump_labels[j];
219bf215546Sopenharmony_ci            const char *str = getpm4(jump_label);
220bf215546Sopenharmony_ci            if (str)
221bf215546Sopenharmony_ci               return str;
222bf215546Sopenharmony_ci         }
223bf215546Sopenharmony_ci         // if we don't find anything w/ known name, maybe we should
224bf215546Sopenharmony_ci         // return UNKN%d to at least make it clear that this is some
225bf215546Sopenharmony_ci         // sort of jump-table entry?
226bf215546Sopenharmony_ci      }
227bf215546Sopenharmony_ci   }
228bf215546Sopenharmony_ci
229bf215546Sopenharmony_ci   lidx = label_idx(offset, false);
230bf215546Sopenharmony_ci   if (lidx < 0)
231bf215546Sopenharmony_ci      return NULL;
232bf215546Sopenharmony_ci   sprintf(name, "l%03d", lidx);
233bf215546Sopenharmony_ci   return name;
234bf215546Sopenharmony_ci}
235bf215546Sopenharmony_ci
236bf215546Sopenharmony_cistatic uint32_t fxn_offsets[0x512];
237bf215546Sopenharmony_cistatic int num_fxn_offsets;
238bf215546Sopenharmony_ci
239bf215546Sopenharmony_cistatic int
240bf215546Sopenharmony_cifxn_idx(uint32_t offset, bool create)
241bf215546Sopenharmony_ci{
242bf215546Sopenharmony_ci   int i;
243bf215546Sopenharmony_ci   for (i = 0; i < num_fxn_offsets; i++)
244bf215546Sopenharmony_ci      if (offset == fxn_offsets[i])
245bf215546Sopenharmony_ci         return i;
246bf215546Sopenharmony_ci   if (!create)
247bf215546Sopenharmony_ci      return -1;
248bf215546Sopenharmony_ci   fxn_offsets[i] = offset;
249bf215546Sopenharmony_ci   num_fxn_offsets = i + 1;
250bf215546Sopenharmony_ci   return i;
251bf215546Sopenharmony_ci}
252bf215546Sopenharmony_ci
253bf215546Sopenharmony_cistatic const char *
254bf215546Sopenharmony_cifxn_name(uint32_t offset)
255bf215546Sopenharmony_ci{
256bf215546Sopenharmony_ci   static char name[14];
257bf215546Sopenharmony_ci   int fidx = fxn_idx(offset, false);
258bf215546Sopenharmony_ci   if (fidx < 0)
259bf215546Sopenharmony_ci      return NULL;
260bf215546Sopenharmony_ci   sprintf(name, "fxn%02d", fidx);
261bf215546Sopenharmony_ci   return name;
262bf215546Sopenharmony_ci}
263bf215546Sopenharmony_ci
264bf215546Sopenharmony_civoid
265bf215546Sopenharmony_ciprint_control_reg(uint32_t id)
266bf215546Sopenharmony_ci{
267bf215546Sopenharmony_ci   char *name = afuc_control_reg_name(id);
268bf215546Sopenharmony_ci   if (name) {
269bf215546Sopenharmony_ci      printf("@%s", name);
270bf215546Sopenharmony_ci      free(name);
271bf215546Sopenharmony_ci   } else {
272bf215546Sopenharmony_ci      printf("0x%03x", id);
273bf215546Sopenharmony_ci   }
274bf215546Sopenharmony_ci}
275bf215546Sopenharmony_ci
276bf215546Sopenharmony_civoid
277bf215546Sopenharmony_ciprint_pipe_reg(uint32_t id)
278bf215546Sopenharmony_ci{
279bf215546Sopenharmony_ci   char *name = afuc_pipe_reg_name(id);
280bf215546Sopenharmony_ci   if (name) {
281bf215546Sopenharmony_ci      printf("|%s", name);
282bf215546Sopenharmony_ci      free(name);
283bf215546Sopenharmony_ci   } else {
284bf215546Sopenharmony_ci      printf("0x%03x", id);
285bf215546Sopenharmony_ci   }
286bf215546Sopenharmony_ci}
287bf215546Sopenharmony_ci
288bf215546Sopenharmony_cistatic void
289bf215546Sopenharmony_cidisasm_instr(uint32_t *instrs, unsigned pc)
290bf215546Sopenharmony_ci{
291bf215546Sopenharmony_ci   int jump_label_idx;
292bf215546Sopenharmony_ci   afuc_instr *instr = (void *)&instrs[pc];
293bf215546Sopenharmony_ci   const char *fname, *lname;
294bf215546Sopenharmony_ci   afuc_opc opc;
295bf215546Sopenharmony_ci   bool rep;
296bf215546Sopenharmony_ci
297bf215546Sopenharmony_ci   afuc_get_opc(instr, &opc, &rep);
298bf215546Sopenharmony_ci
299bf215546Sopenharmony_ci   lname = label_name(pc, false);
300bf215546Sopenharmony_ci   fname = fxn_name(pc);
301bf215546Sopenharmony_ci   jump_label_idx = get_jump_table_entry(pc);
302bf215546Sopenharmony_ci
303bf215546Sopenharmony_ci   if (jump_label_idx >= 0) {
304bf215546Sopenharmony_ci      int j;
305bf215546Sopenharmony_ci      printf("\n");
306bf215546Sopenharmony_ci      for (j = 0; j < jump_labels[jump_label_idx].num_jump_labels; j++) {
307bf215546Sopenharmony_ci         uint32_t jump_label = jump_labels[jump_label_idx].jump_labels[j];
308bf215546Sopenharmony_ci         const char *name = getpm4(jump_label);
309bf215546Sopenharmony_ci         if (name) {
310bf215546Sopenharmony_ci            printlbl("%s", name);
311bf215546Sopenharmony_ci         } else {
312bf215546Sopenharmony_ci            printlbl("UNKN%d", jump_label);
313bf215546Sopenharmony_ci         }
314bf215546Sopenharmony_ci         printf(":\n");
315bf215546Sopenharmony_ci      }
316bf215546Sopenharmony_ci   }
317bf215546Sopenharmony_ci
318bf215546Sopenharmony_ci   if (fname) {
319bf215546Sopenharmony_ci      printlbl("%s", fname);
320bf215546Sopenharmony_ci      printf(":\n");
321bf215546Sopenharmony_ci   }
322bf215546Sopenharmony_ci
323bf215546Sopenharmony_ci   if (lname) {
324bf215546Sopenharmony_ci      printlbl(" %s", lname);
325bf215546Sopenharmony_ci      printf(":");
326bf215546Sopenharmony_ci   } else {
327bf215546Sopenharmony_ci      printf("      ");
328bf215546Sopenharmony_ci   }
329bf215546Sopenharmony_ci
330bf215546Sopenharmony_ci   if (verbose) {
331bf215546Sopenharmony_ci      printf("\t%04x: %08x  ", pc, instrs[pc]);
332bf215546Sopenharmony_ci   } else {
333bf215546Sopenharmony_ci      printf("  ");
334bf215546Sopenharmony_ci   }
335bf215546Sopenharmony_ci
336bf215546Sopenharmony_ci   switch (opc) {
337bf215546Sopenharmony_ci   case OPC_NOP: {
338bf215546Sopenharmony_ci      /* a6xx changed the default immediate, and apparently 0
339bf215546Sopenharmony_ci       * is illegal now.
340bf215546Sopenharmony_ci       */
341bf215546Sopenharmony_ci      const uint32_t nop = gpuver >= 6 ? 0x1000000 : 0x0;
342bf215546Sopenharmony_ci      if (instrs[pc] != nop) {
343bf215546Sopenharmony_ci         printerr("[%08x]", instrs[pc]);
344bf215546Sopenharmony_ci         printf("  ; ");
345bf215546Sopenharmony_ci      }
346bf215546Sopenharmony_ci      if (rep)
347bf215546Sopenharmony_ci         printf("(rep)");
348bf215546Sopenharmony_ci      printf("nop");
349bf215546Sopenharmony_ci      print_gpu_reg(instrs[pc]);
350bf215546Sopenharmony_ci
351bf215546Sopenharmony_ci      break;
352bf215546Sopenharmony_ci   }
353bf215546Sopenharmony_ci   case OPC_ADD:
354bf215546Sopenharmony_ci   case OPC_ADDHI:
355bf215546Sopenharmony_ci   case OPC_SUB:
356bf215546Sopenharmony_ci   case OPC_SUBHI:
357bf215546Sopenharmony_ci   case OPC_AND:
358bf215546Sopenharmony_ci   case OPC_OR:
359bf215546Sopenharmony_ci   case OPC_XOR:
360bf215546Sopenharmony_ci   case OPC_NOT:
361bf215546Sopenharmony_ci   case OPC_SHL:
362bf215546Sopenharmony_ci   case OPC_USHR:
363bf215546Sopenharmony_ci   case OPC_ISHR:
364bf215546Sopenharmony_ci   case OPC_ROT:
365bf215546Sopenharmony_ci   case OPC_MUL8:
366bf215546Sopenharmony_ci   case OPC_MIN:
367bf215546Sopenharmony_ci   case OPC_MAX:
368bf215546Sopenharmony_ci   case OPC_CMP: {
369bf215546Sopenharmony_ci      bool src1 = true;
370bf215546Sopenharmony_ci
371bf215546Sopenharmony_ci      if (opc == OPC_NOT)
372bf215546Sopenharmony_ci         src1 = false;
373bf215546Sopenharmony_ci
374bf215546Sopenharmony_ci      if (rep)
375bf215546Sopenharmony_ci         printf("(rep)");
376bf215546Sopenharmony_ci
377bf215546Sopenharmony_ci      print_alu_name(opc, instrs[pc]);
378bf215546Sopenharmony_ci      print_dst(instr->alui.dst);
379bf215546Sopenharmony_ci      printf(", ");
380bf215546Sopenharmony_ci      if (src1) {
381bf215546Sopenharmony_ci         print_src(instr->alui.src);
382bf215546Sopenharmony_ci         printf(", ");
383bf215546Sopenharmony_ci      }
384bf215546Sopenharmony_ci      printf("0x%04x", instr->alui.uimm);
385bf215546Sopenharmony_ci      print_gpu_reg(instr->alui.uimm);
386bf215546Sopenharmony_ci
387bf215546Sopenharmony_ci      /* print out unexpected bits: */
388bf215546Sopenharmony_ci      if (verbose) {
389bf215546Sopenharmony_ci         if (instr->alui.src && !src1)
390bf215546Sopenharmony_ci            printerr("  (src=%02x)", instr->alui.src);
391bf215546Sopenharmony_ci      }
392bf215546Sopenharmony_ci
393bf215546Sopenharmony_ci      break;
394bf215546Sopenharmony_ci   }
395bf215546Sopenharmony_ci   case OPC_MOVI: {
396bf215546Sopenharmony_ci      if (rep)
397bf215546Sopenharmony_ci         printf("(rep)");
398bf215546Sopenharmony_ci      printf("mov ");
399bf215546Sopenharmony_ci      print_dst(instr->movi.dst);
400bf215546Sopenharmony_ci      printf(", 0x%04x", instr->movi.uimm);
401bf215546Sopenharmony_ci      if (instr->movi.shift)
402bf215546Sopenharmony_ci         printf(" << %u", instr->movi.shift);
403bf215546Sopenharmony_ci
404bf215546Sopenharmony_ci      if ((instr->movi.dst == REG_ADDR) && (instr->movi.shift >= 16)) {
405bf215546Sopenharmony_ci         uint32_t val = (uint32_t)instr->movi.uimm << (uint32_t)instr->movi.shift;
406bf215546Sopenharmony_ci         val &= ~0x40000;  /* b18 seems to be a flag */
407bf215546Sopenharmony_ci
408bf215546Sopenharmony_ci         if ((val & 0x00ffffff) == 0) {
409bf215546Sopenharmony_ci            printf("\t; ");
410bf215546Sopenharmony_ci            print_pipe_reg(val >> 24);
411bf215546Sopenharmony_ci            break;
412bf215546Sopenharmony_ci         }
413bf215546Sopenharmony_ci      }
414bf215546Sopenharmony_ci      /* using mov w/ << 16 is popular way to construct a pkt7
415bf215546Sopenharmony_ci       * header to send (for ex, from PFP to ME), so check that
416bf215546Sopenharmony_ci       * case first
417bf215546Sopenharmony_ci       */
418bf215546Sopenharmony_ci      if ((instr->movi.shift == 16) &&
419bf215546Sopenharmony_ci          ((instr->movi.uimm & 0xff00) == 0x7000)) {
420bf215546Sopenharmony_ci         unsigned opc, p;
421bf215546Sopenharmony_ci
422bf215546Sopenharmony_ci         opc = instr->movi.uimm & 0x7f;
423bf215546Sopenharmony_ci         p = pm4_odd_parity_bit(opc);
424bf215546Sopenharmony_ci
425bf215546Sopenharmony_ci         /* So, you'd think that checking the parity bit would be
426bf215546Sopenharmony_ci          * a good way to rule out false positives, but seems like
427bf215546Sopenharmony_ci          * ME doesn't really care.. at least it would filter out
428bf215546Sopenharmony_ci          * things that look like actual legit packets between
429bf215546Sopenharmony_ci          * PFP and ME..
430bf215546Sopenharmony_ci          */
431bf215546Sopenharmony_ci         if (1 || p == ((instr->movi.uimm >> 7) & 0x1)) {
432bf215546Sopenharmony_ci            const char *name = getpm4(opc);
433bf215546Sopenharmony_ci            printf("\t; ");
434bf215546Sopenharmony_ci            if (name)
435bf215546Sopenharmony_ci               printlbl("%s", name);
436bf215546Sopenharmony_ci            else
437bf215546Sopenharmony_ci               printlbl("UNKN%u", opc);
438bf215546Sopenharmony_ci            break;
439bf215546Sopenharmony_ci         }
440bf215546Sopenharmony_ci      }
441bf215546Sopenharmony_ci
442bf215546Sopenharmony_ci      print_gpu_reg((uint32_t)instr->movi.uimm << (uint32_t)instr->movi.shift);
443bf215546Sopenharmony_ci
444bf215546Sopenharmony_ci      break;
445bf215546Sopenharmony_ci   }
446bf215546Sopenharmony_ci   case OPC_ALU: {
447bf215546Sopenharmony_ci      bool src1 = true;
448bf215546Sopenharmony_ci
449bf215546Sopenharmony_ci      if (instr->alu.alu == OPC_NOT || instr->alu.alu == OPC_MSB)
450bf215546Sopenharmony_ci         src1 = false;
451bf215546Sopenharmony_ci
452bf215546Sopenharmony_ci      if (instr->alu.pad)
453bf215546Sopenharmony_ci         printf("[%08x]  ; ", instrs[pc]);
454bf215546Sopenharmony_ci
455bf215546Sopenharmony_ci      if (rep)
456bf215546Sopenharmony_ci         printf("(rep)");
457bf215546Sopenharmony_ci      if (instr->alu.xmov)
458bf215546Sopenharmony_ci         printf("(xmov%d)", instr->alu.xmov);
459bf215546Sopenharmony_ci
460bf215546Sopenharmony_ci      /* special case mnemonics:
461bf215546Sopenharmony_ci       *   reading $00 seems to always yield zero, and so:
462bf215546Sopenharmony_ci       *      or $dst, $00, $src -> mov $dst, $src
463bf215546Sopenharmony_ci       *   Maybe add one for negate too, ie.
464bf215546Sopenharmony_ci       *      sub $dst, $00, $src ???
465bf215546Sopenharmony_ci       */
466bf215546Sopenharmony_ci      if ((instr->alu.alu == OPC_OR) && !instr->alu.src1) {
467bf215546Sopenharmony_ci         printf("mov ");
468bf215546Sopenharmony_ci         src1 = false;
469bf215546Sopenharmony_ci      } else {
470bf215546Sopenharmony_ci         print_alu_name(instr->alu.alu, instrs[pc]);
471bf215546Sopenharmony_ci      }
472bf215546Sopenharmony_ci
473bf215546Sopenharmony_ci      print_dst(instr->alu.dst);
474bf215546Sopenharmony_ci      if (src1) {
475bf215546Sopenharmony_ci         printf(", ");
476bf215546Sopenharmony_ci         print_src(instr->alu.src1);
477bf215546Sopenharmony_ci      }
478bf215546Sopenharmony_ci      printf(", ");
479bf215546Sopenharmony_ci      print_src(instr->alu.src2);
480bf215546Sopenharmony_ci
481bf215546Sopenharmony_ci      /* print out unexpected bits: */
482bf215546Sopenharmony_ci      if (verbose) {
483bf215546Sopenharmony_ci         if (instr->alu.pad)
484bf215546Sopenharmony_ci            printerr("  (pad=%01x)", instr->alu.pad);
485bf215546Sopenharmony_ci         if (instr->alu.src1 && !src1)
486bf215546Sopenharmony_ci            printerr("  (src1=%02x)", instr->alu.src1);
487bf215546Sopenharmony_ci      }
488bf215546Sopenharmony_ci
489bf215546Sopenharmony_ci      /* xmov is a modifier that makes the processor execute up to 3
490bf215546Sopenharmony_ci       * extra mov's after the current instruction. Given an ALU
491bf215546Sopenharmony_ci       * instruction:
492bf215546Sopenharmony_ci       *
493bf215546Sopenharmony_ci       * (xmovN) alu $dst, $src1, $src2
494bf215546Sopenharmony_ci       *
495bf215546Sopenharmony_ci       * In all of the uses in the firmware blob, $dst and $src2 are one
496bf215546Sopenharmony_ci       * of the "special" registers $data, $addr, $addr2. I've observed
497bf215546Sopenharmony_ci       * that if $dst isn't "special" then it's replaced with $00
498bf215546Sopenharmony_ci       * instead of $data, but I haven't checked what happens if $src2
499bf215546Sopenharmony_ci       * isn't "special".  Anyway, in the usual case, the HW produces a
500bf215546Sopenharmony_ci       * count M = min(N, $rem) and then does the following:
501bf215546Sopenharmony_ci       *
502bf215546Sopenharmony_ci       * M = 1:
503bf215546Sopenharmony_ci       * mov $data, $src2
504bf215546Sopenharmony_ci       *
505bf215546Sopenharmony_ci       * M = 2:
506bf215546Sopenharmony_ci       * mov $data, $src2
507bf215546Sopenharmony_ci       * mov $data, $src2
508bf215546Sopenharmony_ci       *
509bf215546Sopenharmony_ci       * M = 3:
510bf215546Sopenharmony_ci       * mov $data, $src2
511bf215546Sopenharmony_ci       * mov $dst, $src2 (special case for CP_CONTEXT_REG_BUNCH)
512bf215546Sopenharmony_ci       * mov $data, $src2
513bf215546Sopenharmony_ci       *
514bf215546Sopenharmony_ci       * It seems to be frequently used in combination with (rep) to
515bf215546Sopenharmony_ci       * provide a kind of hardware-based loop unrolling, and there's
516bf215546Sopenharmony_ci       * even a special case in the ISA to be able to do this with
517bf215546Sopenharmony_ci       * CP_CONTEXT_REG_BUNCH. However (rep) isn't required.
518bf215546Sopenharmony_ci       *
519bf215546Sopenharmony_ci       * This dumps the expected extra instructions, assuming that $rem
520bf215546Sopenharmony_ci       * isn't too small.
521bf215546Sopenharmony_ci       */
522bf215546Sopenharmony_ci      if (verbose && instr->alu.xmov) {
523bf215546Sopenharmony_ci         for (int i = 0; i < instr->alu.xmov; i++) {
524bf215546Sopenharmony_ci            printf("\n        ; mov ");
525bf215546Sopenharmony_ci            if (instr->alu.dst < 0x1d)
526bf215546Sopenharmony_ci               printf("$00");
527bf215546Sopenharmony_ci            else if (instr->alu.xmov == 3 && i == 1)
528bf215546Sopenharmony_ci               print_dst(instr->alu.dst);
529bf215546Sopenharmony_ci            else
530bf215546Sopenharmony_ci               printf("$data");
531bf215546Sopenharmony_ci            printf(", ");
532bf215546Sopenharmony_ci            print_src(instr->alu.src2);
533bf215546Sopenharmony_ci         }
534bf215546Sopenharmony_ci      }
535bf215546Sopenharmony_ci
536bf215546Sopenharmony_ci      break;
537bf215546Sopenharmony_ci   }
538bf215546Sopenharmony_ci   case OPC_CWRITE6:
539bf215546Sopenharmony_ci   case OPC_CREAD6:
540bf215546Sopenharmony_ci   case OPC_STORE6:
541bf215546Sopenharmony_ci   case OPC_LOAD6: {
542bf215546Sopenharmony_ci      if (rep)
543bf215546Sopenharmony_ci         printf("(rep)");
544bf215546Sopenharmony_ci
545bf215546Sopenharmony_ci      bool is_control_reg = true;
546bf215546Sopenharmony_ci      bool is_store = true;
547bf215546Sopenharmony_ci      if (gpuver >= 6) {
548bf215546Sopenharmony_ci         switch (opc) {
549bf215546Sopenharmony_ci         case OPC_CWRITE6:
550bf215546Sopenharmony_ci            printf("cwrite ");
551bf215546Sopenharmony_ci            break;
552bf215546Sopenharmony_ci         case OPC_CREAD6:
553bf215546Sopenharmony_ci            is_store = false;
554bf215546Sopenharmony_ci            printf("cread ");
555bf215546Sopenharmony_ci            break;
556bf215546Sopenharmony_ci         case OPC_STORE6:
557bf215546Sopenharmony_ci            is_control_reg = false;
558bf215546Sopenharmony_ci            printf("store ");
559bf215546Sopenharmony_ci            break;
560bf215546Sopenharmony_ci         case OPC_LOAD6:
561bf215546Sopenharmony_ci            is_control_reg = false;
562bf215546Sopenharmony_ci            is_store = false;
563bf215546Sopenharmony_ci            printf("load ");
564bf215546Sopenharmony_ci            break;
565bf215546Sopenharmony_ci         default:
566bf215546Sopenharmony_ci            assert(!"unreachable");
567bf215546Sopenharmony_ci         }
568bf215546Sopenharmony_ci      } else {
569bf215546Sopenharmony_ci         switch (opc) {
570bf215546Sopenharmony_ci         case OPC_CWRITE5:
571bf215546Sopenharmony_ci            printf("cwrite ");
572bf215546Sopenharmony_ci            break;
573bf215546Sopenharmony_ci         case OPC_CREAD5:
574bf215546Sopenharmony_ci            is_store = false;
575bf215546Sopenharmony_ci            printf("cread ");
576bf215546Sopenharmony_ci            break;
577bf215546Sopenharmony_ci         default:
578bf215546Sopenharmony_ci            fprintf(stderr, "A6xx control opcode on A5xx?\n");
579bf215546Sopenharmony_ci            exit(1);
580bf215546Sopenharmony_ci         }
581bf215546Sopenharmony_ci      }
582bf215546Sopenharmony_ci
583bf215546Sopenharmony_ci      if (is_store)
584bf215546Sopenharmony_ci         print_src(instr->control.src1);
585bf215546Sopenharmony_ci      else
586bf215546Sopenharmony_ci         print_dst(instr->control.src1);
587bf215546Sopenharmony_ci      printf(", [");
588bf215546Sopenharmony_ci      print_src(instr->control.src2);
589bf215546Sopenharmony_ci      printf(" + ");
590bf215546Sopenharmony_ci      if (is_control_reg && instr->control.flags != 0x4)
591bf215546Sopenharmony_ci         print_control_reg(instr->control.uimm);
592bf215546Sopenharmony_ci      else
593bf215546Sopenharmony_ci         printf("0x%03x", instr->control.uimm);
594bf215546Sopenharmony_ci      printf("], 0x%x", instr->control.flags);
595bf215546Sopenharmony_ci      break;
596bf215546Sopenharmony_ci   }
597bf215546Sopenharmony_ci   case OPC_BRNEI:
598bf215546Sopenharmony_ci   case OPC_BREQI:
599bf215546Sopenharmony_ci   case OPC_BRNEB:
600bf215546Sopenharmony_ci   case OPC_BREQB: {
601bf215546Sopenharmony_ci      unsigned off = pc + instr->br.ioff;
602bf215546Sopenharmony_ci
603bf215546Sopenharmony_ci      assert(!rep);
604bf215546Sopenharmony_ci
605bf215546Sopenharmony_ci      /* Since $00 reads back zero, it can be used as src for
606bf215546Sopenharmony_ci       * unconditional branches.  (This only really makes sense
607bf215546Sopenharmony_ci       * for the BREQB.. or possible BRNEI if imm==0.)
608bf215546Sopenharmony_ci       *
609bf215546Sopenharmony_ci       * If bit=0 then branch is taken if *all* bits are zero.
610bf215546Sopenharmony_ci       * Otherwise it is taken if bit (bit-1) is clear.
611bf215546Sopenharmony_ci       *
612bf215546Sopenharmony_ci       * Note the instruction after a jump/branch is executed
613bf215546Sopenharmony_ci       * regardless of whether branch is taken, so use nop or
614bf215546Sopenharmony_ci       * take that into account in code.
615bf215546Sopenharmony_ci       */
616bf215546Sopenharmony_ci      if (instr->br.src || (opc != OPC_BRNEB)) {
617bf215546Sopenharmony_ci         bool immed = false;
618bf215546Sopenharmony_ci
619bf215546Sopenharmony_ci         if (opc == OPC_BRNEI) {
620bf215546Sopenharmony_ci            printf("brne ");
621bf215546Sopenharmony_ci            immed = true;
622bf215546Sopenharmony_ci         } else if (opc == OPC_BREQI) {
623bf215546Sopenharmony_ci            printf("breq ");
624bf215546Sopenharmony_ci            immed = true;
625bf215546Sopenharmony_ci         } else if (opc == OPC_BRNEB) {
626bf215546Sopenharmony_ci            printf("brne ");
627bf215546Sopenharmony_ci         } else if (opc == OPC_BREQB) {
628bf215546Sopenharmony_ci            printf("breq ");
629bf215546Sopenharmony_ci         }
630bf215546Sopenharmony_ci         print_src(instr->br.src);
631bf215546Sopenharmony_ci         if (immed) {
632bf215546Sopenharmony_ci            printf(", 0x%x,", instr->br.bit_or_imm);
633bf215546Sopenharmony_ci         } else {
634bf215546Sopenharmony_ci            printf(", b%u,", instr->br.bit_or_imm);
635bf215546Sopenharmony_ci         }
636bf215546Sopenharmony_ci      } else {
637bf215546Sopenharmony_ci         printf("jump");
638bf215546Sopenharmony_ci         if (verbose && instr->br.bit_or_imm) {
639bf215546Sopenharmony_ci            printerr("  (src=%03x, bit=%03x) ", instr->br.src,
640bf215546Sopenharmony_ci                     instr->br.bit_or_imm);
641bf215546Sopenharmony_ci         }
642bf215546Sopenharmony_ci      }
643bf215546Sopenharmony_ci
644bf215546Sopenharmony_ci      printf(" #");
645bf215546Sopenharmony_ci      printlbl("%s", label_name(off, true));
646bf215546Sopenharmony_ci      if (verbose)
647bf215546Sopenharmony_ci         printf(" (#%d, %04x)", instr->br.ioff, off);
648bf215546Sopenharmony_ci      break;
649bf215546Sopenharmony_ci   }
650bf215546Sopenharmony_ci   case OPC_CALL:
651bf215546Sopenharmony_ci      assert(!rep);
652bf215546Sopenharmony_ci      printf("call #");
653bf215546Sopenharmony_ci      printlbl("%s", fxn_name(instr->call.uoff));
654bf215546Sopenharmony_ci      if (verbose) {
655bf215546Sopenharmony_ci         printf(" (%04x)", instr->call.uoff);
656bf215546Sopenharmony_ci         if (instr->br.bit_or_imm || instr->br.src) {
657bf215546Sopenharmony_ci            printerr("  (src=%03x, bit=%03x) ", instr->br.src,
658bf215546Sopenharmony_ci                     instr->br.bit_or_imm);
659bf215546Sopenharmony_ci         }
660bf215546Sopenharmony_ci      }
661bf215546Sopenharmony_ci      break;
662bf215546Sopenharmony_ci   case OPC_RET:
663bf215546Sopenharmony_ci      assert(!rep);
664bf215546Sopenharmony_ci      if (instr->ret.pad)
665bf215546Sopenharmony_ci         printf("[%08x]  ; ", instrs[pc]);
666bf215546Sopenharmony_ci      if (instr->ret.interrupt)
667bf215546Sopenharmony_ci         printf("iret");
668bf215546Sopenharmony_ci      else
669bf215546Sopenharmony_ci         printf("ret");
670bf215546Sopenharmony_ci      break;
671bf215546Sopenharmony_ci   case OPC_WIN:
672bf215546Sopenharmony_ci      assert(!rep);
673bf215546Sopenharmony_ci      if (instr->waitin.pad)
674bf215546Sopenharmony_ci         printf("[%08x]  ; ", instrs[pc]);
675bf215546Sopenharmony_ci      printf("waitin");
676bf215546Sopenharmony_ci      if (verbose && instr->waitin.pad)
677bf215546Sopenharmony_ci         printerr("  (pad=%x)", instr->waitin.pad);
678bf215546Sopenharmony_ci      break;
679bf215546Sopenharmony_ci   case OPC_PREEMPTLEAVE6:
680bf215546Sopenharmony_ci      if (gpuver < 6) {
681bf215546Sopenharmony_ci         printf("[%08x]  ; op38", instrs[pc]);
682bf215546Sopenharmony_ci      } else {
683bf215546Sopenharmony_ci         printf("preemptleave #");
684bf215546Sopenharmony_ci         printlbl("%s", label_name(instr->call.uoff, true));
685bf215546Sopenharmony_ci      }
686bf215546Sopenharmony_ci      break;
687bf215546Sopenharmony_ci   case OPC_SETSECURE:
688bf215546Sopenharmony_ci      /* Note: This seems to implicitly read the secure/not-secure state
689bf215546Sopenharmony_ci       * to set from the low bit of $02, and implicitly jumps to pc + 3
690bf215546Sopenharmony_ci       * (i.e. skipping the next two instructions) if it succeeds. We
691bf215546Sopenharmony_ci       * print these implicit parameters to make reading the disassembly
692bf215546Sopenharmony_ci       * easier.
693bf215546Sopenharmony_ci       */
694bf215546Sopenharmony_ci      if (instr->pad)
695bf215546Sopenharmony_ci         printf("[%08x]  ; ", instrs[pc]);
696bf215546Sopenharmony_ci      printf("setsecure $02, #");
697bf215546Sopenharmony_ci      printlbl("%s", label_name(pc + 3, true));
698bf215546Sopenharmony_ci      break;
699bf215546Sopenharmony_ci   default:
700bf215546Sopenharmony_ci      printerr("[%08x]", instrs[pc]);
701bf215546Sopenharmony_ci      printf("  ; op%02x ", opc);
702bf215546Sopenharmony_ci      print_dst(instr->alui.dst);
703bf215546Sopenharmony_ci      printf(", ");
704bf215546Sopenharmony_ci      print_src(instr->alui.src);
705bf215546Sopenharmony_ci      print_gpu_reg(instrs[pc] & 0xffff);
706bf215546Sopenharmony_ci      break;
707bf215546Sopenharmony_ci   }
708bf215546Sopenharmony_ci   printf("\n");
709bf215546Sopenharmony_ci}
710bf215546Sopenharmony_ci
711bf215546Sopenharmony_cistatic void
712bf215546Sopenharmony_cisetup_packet_table(uint32_t *jmptbl, uint32_t sizedwords)
713bf215546Sopenharmony_ci{
714bf215546Sopenharmony_ci   num_jump_labels = 0;
715bf215546Sopenharmony_ci
716bf215546Sopenharmony_ci   for (unsigned i = 0; i < sizedwords; i++) {
717bf215546Sopenharmony_ci      unsigned offset = jmptbl[i];
718bf215546Sopenharmony_ci      unsigned n = i; // + CP_NOP;
719bf215546Sopenharmony_ci      add_jump_table_entry(n, offset);
720bf215546Sopenharmony_ci   }
721bf215546Sopenharmony_ci}
722bf215546Sopenharmony_ci
723bf215546Sopenharmony_cistatic void
724bf215546Sopenharmony_cisetup_labels(uint32_t *instrs, uint32_t sizedwords)
725bf215546Sopenharmony_ci{
726bf215546Sopenharmony_ci   afuc_opc opc;
727bf215546Sopenharmony_ci   bool rep;
728bf215546Sopenharmony_ci
729bf215546Sopenharmony_ci   num_label_offsets = 0;
730bf215546Sopenharmony_ci
731bf215546Sopenharmony_ci   for (unsigned i = 0; i < sizedwords; i++) {
732bf215546Sopenharmony_ci      afuc_instr *instr = (void *)&instrs[i];
733bf215546Sopenharmony_ci
734bf215546Sopenharmony_ci      afuc_get_opc(instr, &opc, &rep);
735bf215546Sopenharmony_ci
736bf215546Sopenharmony_ci      switch (opc) {
737bf215546Sopenharmony_ci      case OPC_BRNEI:
738bf215546Sopenharmony_ci      case OPC_BREQI:
739bf215546Sopenharmony_ci      case OPC_BRNEB:
740bf215546Sopenharmony_ci      case OPC_BREQB:
741bf215546Sopenharmony_ci         label_idx(i + instr->br.ioff, true);
742bf215546Sopenharmony_ci         break;
743bf215546Sopenharmony_ci      case OPC_PREEMPTLEAVE6:
744bf215546Sopenharmony_ci         if (gpuver >= 6)
745bf215546Sopenharmony_ci            label_idx(instr->call.uoff, true);
746bf215546Sopenharmony_ci         break;
747bf215546Sopenharmony_ci      case OPC_CALL:
748bf215546Sopenharmony_ci         fxn_idx(instr->call.uoff, true);
749bf215546Sopenharmony_ci         break;
750bf215546Sopenharmony_ci      case OPC_SETSECURE:
751bf215546Sopenharmony_ci         /* this implicitly jumps to pc + 3 if successful */
752bf215546Sopenharmony_ci         label_idx(i + 3, true);
753bf215546Sopenharmony_ci         break;
754bf215546Sopenharmony_ci      default:
755bf215546Sopenharmony_ci         break;
756bf215546Sopenharmony_ci      }
757bf215546Sopenharmony_ci   }
758bf215546Sopenharmony_ci}
759bf215546Sopenharmony_ci
760bf215546Sopenharmony_cistatic void
761bf215546Sopenharmony_cidisasm(struct emu *emu)
762bf215546Sopenharmony_ci{
763bf215546Sopenharmony_ci   uint32_t sizedwords = emu->sizedwords;
764bf215546Sopenharmony_ci   uint32_t lpac_offset = 0;
765bf215546Sopenharmony_ci
766bf215546Sopenharmony_ci   EMU_GPU_REG(CP_SQE_INSTR_BASE);
767bf215546Sopenharmony_ci   EMU_GPU_REG(CP_LPAC_SQE_INSTR_BASE);
768bf215546Sopenharmony_ci
769bf215546Sopenharmony_ci   emu_init(emu);
770bf215546Sopenharmony_ci
771bf215546Sopenharmony_ci#ifdef BOOTSTRAP_DEBUG
772bf215546Sopenharmony_ci   while (true) {
773bf215546Sopenharmony_ci      disasm_instr(emu->instrs, emu->gpr_regs.pc);
774bf215546Sopenharmony_ci      emu_step(emu);
775bf215546Sopenharmony_ci   }
776bf215546Sopenharmony_ci#endif
777bf215546Sopenharmony_ci
778bf215546Sopenharmony_ci   emu_run_bootstrap(emu);
779bf215546Sopenharmony_ci
780bf215546Sopenharmony_ci   /* Figure out if we have LPAC SQE appended: */
781bf215546Sopenharmony_ci   if (emu_get_reg64(emu, &CP_LPAC_SQE_INSTR_BASE)) {
782bf215546Sopenharmony_ci      lpac_offset = emu_get_reg64(emu, &CP_LPAC_SQE_INSTR_BASE) -
783bf215546Sopenharmony_ci            emu_get_reg64(emu, &CP_SQE_INSTR_BASE);
784bf215546Sopenharmony_ci      lpac_offset /= 4;
785bf215546Sopenharmony_ci      sizedwords = lpac_offset;
786bf215546Sopenharmony_ci   }
787bf215546Sopenharmony_ci
788bf215546Sopenharmony_ci   setup_packet_table(emu->jmptbl, ARRAY_SIZE(emu->jmptbl));
789bf215546Sopenharmony_ci   setup_labels(emu->instrs, emu->sizedwords);
790bf215546Sopenharmony_ci
791bf215546Sopenharmony_ci   /* TODO add option to emulate LPAC SQE instead: */
792bf215546Sopenharmony_ci   if (emulator) {
793bf215546Sopenharmony_ci      /* Start from clean slate: */
794bf215546Sopenharmony_ci      emu_fini(emu);
795bf215546Sopenharmony_ci      emu_init(emu);
796bf215546Sopenharmony_ci
797bf215546Sopenharmony_ci      while (true) {
798bf215546Sopenharmony_ci         disasm_instr(emu->instrs, emu->gpr_regs.pc);
799bf215546Sopenharmony_ci         emu_step(emu);
800bf215546Sopenharmony_ci      }
801bf215546Sopenharmony_ci   }
802bf215546Sopenharmony_ci
803bf215546Sopenharmony_ci   /* print instructions: */
804bf215546Sopenharmony_ci   for (int i = 0; i < sizedwords; i++) {
805bf215546Sopenharmony_ci      disasm_instr(emu->instrs, i);
806bf215546Sopenharmony_ci   }
807bf215546Sopenharmony_ci
808bf215546Sopenharmony_ci   if (!lpac_offset)
809bf215546Sopenharmony_ci      return;
810bf215546Sopenharmony_ci
811bf215546Sopenharmony_ci   printf(";\n");
812bf215546Sopenharmony_ci   printf("; LPAC microcode:\n");
813bf215546Sopenharmony_ci   printf(";\n");
814bf215546Sopenharmony_ci
815bf215546Sopenharmony_ci   emu_fini(emu);
816bf215546Sopenharmony_ci
817bf215546Sopenharmony_ci   emu->lpac = true;
818bf215546Sopenharmony_ci   emu->instrs += lpac_offset;
819bf215546Sopenharmony_ci   emu->sizedwords -= lpac_offset;
820bf215546Sopenharmony_ci
821bf215546Sopenharmony_ci   emu_init(emu);
822bf215546Sopenharmony_ci   emu_run_bootstrap(emu);
823bf215546Sopenharmony_ci
824bf215546Sopenharmony_ci   setup_packet_table(emu->jmptbl, ARRAY_SIZE(emu->jmptbl));
825bf215546Sopenharmony_ci   setup_labels(emu->instrs, emu->sizedwords);
826bf215546Sopenharmony_ci
827bf215546Sopenharmony_ci   /* print instructions: */
828bf215546Sopenharmony_ci   for (int i = 0; i < emu->sizedwords; i++) {
829bf215546Sopenharmony_ci      disasm_instr(emu->instrs, i);
830bf215546Sopenharmony_ci   }
831bf215546Sopenharmony_ci}
832bf215546Sopenharmony_ci
833bf215546Sopenharmony_ci
834bf215546Sopenharmony_cistatic void
835bf215546Sopenharmony_cidisasm_legacy(uint32_t *buf, int sizedwords)
836bf215546Sopenharmony_ci{
837bf215546Sopenharmony_ci   uint32_t *instrs = buf;
838bf215546Sopenharmony_ci   const int jmptbl_start = instrs[1] & 0xffff;
839bf215546Sopenharmony_ci   uint32_t *jmptbl = &buf[jmptbl_start];
840bf215546Sopenharmony_ci   int i;
841bf215546Sopenharmony_ci
842bf215546Sopenharmony_ci   /* parse jumptable: */
843bf215546Sopenharmony_ci   setup_packet_table(jmptbl, 0x80);
844bf215546Sopenharmony_ci
845bf215546Sopenharmony_ci   /* do a pre-pass to find instructions that are potential branch targets,
846bf215546Sopenharmony_ci    * and add labels for them:
847bf215546Sopenharmony_ci    */
848bf215546Sopenharmony_ci   setup_labels(instrs, jmptbl_start);
849bf215546Sopenharmony_ci
850bf215546Sopenharmony_ci   /* print instructions: */
851bf215546Sopenharmony_ci   for (i = 0; i < jmptbl_start; i++) {
852bf215546Sopenharmony_ci      disasm_instr(instrs, i);
853bf215546Sopenharmony_ci   }
854bf215546Sopenharmony_ci
855bf215546Sopenharmony_ci   /* print jumptable: */
856bf215546Sopenharmony_ci   if (verbose) {
857bf215546Sopenharmony_ci      printf(";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;\n");
858bf215546Sopenharmony_ci      printf("; JUMP TABLE\n");
859bf215546Sopenharmony_ci      for (i = 0; i < 0x7f; i++) {
860bf215546Sopenharmony_ci         int n = i; // + CP_NOP;
861bf215546Sopenharmony_ci         uint32_t offset = jmptbl[i];
862bf215546Sopenharmony_ci         const char *name = getpm4(n);
863bf215546Sopenharmony_ci         printf("%3d %02x: ", n, n);
864bf215546Sopenharmony_ci         printf("%04x", offset);
865bf215546Sopenharmony_ci         if (name) {
866bf215546Sopenharmony_ci            printf("   ; %s", name);
867bf215546Sopenharmony_ci         } else {
868bf215546Sopenharmony_ci            printf("   ; UNKN%d", n);
869bf215546Sopenharmony_ci         }
870bf215546Sopenharmony_ci         printf("\n");
871bf215546Sopenharmony_ci      }
872bf215546Sopenharmony_ci   }
873bf215546Sopenharmony_ci}
874bf215546Sopenharmony_ci
875bf215546Sopenharmony_cistatic void
876bf215546Sopenharmony_ciusage(void)
877bf215546Sopenharmony_ci{
878bf215546Sopenharmony_ci   fprintf(stderr, "Usage:\n"
879bf215546Sopenharmony_ci                   "\tdisasm [-g GPUVER] [-v] [-c] filename.asm\n"
880bf215546Sopenharmony_ci                   "\t\t-g - specify GPU version (5, etc)\n"
881bf215546Sopenharmony_ci                   "\t\t-c - use colors\n"
882bf215546Sopenharmony_ci                   "\t\t-v - verbose output\n"
883bf215546Sopenharmony_ci                   "\t\t-e - emulator mode\n");
884bf215546Sopenharmony_ci   exit(2);
885bf215546Sopenharmony_ci}
886bf215546Sopenharmony_ci
887bf215546Sopenharmony_ciint
888bf215546Sopenharmony_cimain(int argc, char **argv)
889bf215546Sopenharmony_ci{
890bf215546Sopenharmony_ci   uint32_t *buf;
891bf215546Sopenharmony_ci   char *file;
892bf215546Sopenharmony_ci   bool colors = false;
893bf215546Sopenharmony_ci   uint32_t gpu_id = 0;
894bf215546Sopenharmony_ci   size_t sz;
895bf215546Sopenharmony_ci   int c, ret;
896bf215546Sopenharmony_ci   bool unit_test = false;
897bf215546Sopenharmony_ci
898bf215546Sopenharmony_ci   /* Argument parsing: */
899bf215546Sopenharmony_ci   while ((c = getopt(argc, argv, "g:vceu")) != -1) {
900bf215546Sopenharmony_ci      switch (c) {
901bf215546Sopenharmony_ci      case 'g':
902bf215546Sopenharmony_ci         gpu_id = atoi(optarg);
903bf215546Sopenharmony_ci         break;
904bf215546Sopenharmony_ci      case 'v':
905bf215546Sopenharmony_ci         verbose = true;
906bf215546Sopenharmony_ci         break;
907bf215546Sopenharmony_ci      case 'c':
908bf215546Sopenharmony_ci         colors = true;
909bf215546Sopenharmony_ci         break;
910bf215546Sopenharmony_ci      case 'e':
911bf215546Sopenharmony_ci         emulator = true;
912bf215546Sopenharmony_ci         verbose  = true;
913bf215546Sopenharmony_ci         break;
914bf215546Sopenharmony_ci      case 'u':
915bf215546Sopenharmony_ci         unit_test = true;
916bf215546Sopenharmony_ci         break;
917bf215546Sopenharmony_ci      default:
918bf215546Sopenharmony_ci         usage();
919bf215546Sopenharmony_ci      }
920bf215546Sopenharmony_ci   }
921bf215546Sopenharmony_ci
922bf215546Sopenharmony_ci   if (optind >= argc) {
923bf215546Sopenharmony_ci      fprintf(stderr, "no file specified!\n");
924bf215546Sopenharmony_ci      usage();
925bf215546Sopenharmony_ci   }
926bf215546Sopenharmony_ci
927bf215546Sopenharmony_ci   file = argv[optind];
928bf215546Sopenharmony_ci
929bf215546Sopenharmony_ci   /* if gpu version not specified, infer from filename: */
930bf215546Sopenharmony_ci   if (!gpu_id) {
931bf215546Sopenharmony_ci      char *str = strstr(file, "a5");
932bf215546Sopenharmony_ci      if (!str)
933bf215546Sopenharmony_ci         str = strstr(file, "a6");
934bf215546Sopenharmony_ci      if (str)
935bf215546Sopenharmony_ci         gpu_id = atoi(str + 1);
936bf215546Sopenharmony_ci   }
937bf215546Sopenharmony_ci
938bf215546Sopenharmony_ci   if (gpu_id < 500) {
939bf215546Sopenharmony_ci      printf("invalid gpu_id: %d\n", gpu_id);
940bf215546Sopenharmony_ci      return -1;
941bf215546Sopenharmony_ci   }
942bf215546Sopenharmony_ci
943bf215546Sopenharmony_ci   gpuver = gpu_id / 100;
944bf215546Sopenharmony_ci
945bf215546Sopenharmony_ci   /* a6xx is *mostly* a superset of a5xx, but some opcodes shuffle
946bf215546Sopenharmony_ci    * around, and behavior of special regs is a bit different.  Right
947bf215546Sopenharmony_ci    * now we only bother to support the a6xx variant.
948bf215546Sopenharmony_ci    */
949bf215546Sopenharmony_ci   if (emulator && (gpuver != 6)) {
950bf215546Sopenharmony_ci      fprintf(stderr, "Emulator only supported on a6xx!\n");
951bf215546Sopenharmony_ci      return 1;
952bf215546Sopenharmony_ci   }
953bf215546Sopenharmony_ci
954bf215546Sopenharmony_ci   ret = afuc_util_init(gpuver, colors);
955bf215546Sopenharmony_ci   if (ret < 0) {
956bf215546Sopenharmony_ci      usage();
957bf215546Sopenharmony_ci   }
958bf215546Sopenharmony_ci
959bf215546Sopenharmony_ci   printf("; a%dxx microcode\n", gpuver);
960bf215546Sopenharmony_ci
961bf215546Sopenharmony_ci   buf = (uint32_t *)os_read_file(file, &sz);
962bf215546Sopenharmony_ci
963bf215546Sopenharmony_ci   if (!unit_test)
964bf215546Sopenharmony_ci      printf("; Disassembling microcode: %s\n", file);
965bf215546Sopenharmony_ci   printf("; Version: %08x\n\n", buf[1]);
966bf215546Sopenharmony_ci
967bf215546Sopenharmony_ci   if (gpuver < 6) {
968bf215546Sopenharmony_ci      disasm_legacy(&buf[1], sz / 4 - 1);
969bf215546Sopenharmony_ci   } else {
970bf215546Sopenharmony_ci      struct emu emu = {
971bf215546Sopenharmony_ci            .instrs = &buf[1],
972bf215546Sopenharmony_ci            .sizedwords = sz / 4 - 1,
973bf215546Sopenharmony_ci            .gpu_id = gpu_id,
974bf215546Sopenharmony_ci      };
975bf215546Sopenharmony_ci
976bf215546Sopenharmony_ci      disasm(&emu);
977bf215546Sopenharmony_ci   }
978bf215546Sopenharmony_ci
979bf215546Sopenharmony_ci   return 0;
980bf215546Sopenharmony_ci}
981