freedreno/afuc: Add iret
[mesa.git] / src / freedreno / afuc / disasm.c
1 /*
2 * Copyright (c) 2017 Rob Clark <robdclark@gmail.com>
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a
5 * copy of this software and associated documentation files (the "Software"),
6 * to deal in the Software without restriction, including without limitation
7 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8 * and/or sell copies of the Software, and to permit persons to whom the
9 * Software is furnished to do so, subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice (including the next
12 * paragraph) shall be included in all copies or substantial portions of the
13 * Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
18 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 * SOFTWARE.
22 */
23
24 #include <err.h>
25 #include <stdint.h>
26 #include <stdbool.h>
27 #include <unistd.h>
28 #include <fcntl.h>
29 #include <stdarg.h>
30 #include <stdlib.h>
31 #include <stdio.h>
32 #include <string.h>
33 #include <assert.h>
34 #include <getopt.h>
35
36 #include "afuc.h"
37 #include "rnn.h"
38 #include "rnndec.h"
39
40 static int gpuver;
41
42
43 static struct rnndeccontext *ctx;
44 static struct rnndb *db;
45 static struct rnndomain *control_regs;
46 struct rnndomain *dom[2];
47 const char *variant;
48
49 /* non-verbose mode should output something suitable to feed back into
50 * assembler.. verbose mode has additional output useful for debugging
51 * (like unexpected bits that are set)
52 */
53 static bool verbose = false;
54
55 static void print_gpu_reg(uint32_t regbase)
56 {
57 struct rnndomain *d = NULL;
58
59 if (regbase < 0x100)
60 return;
61
62 if (rnndec_checkaddr(ctx, dom[0], regbase, 0))
63 d = dom[0];
64 else if (rnndec_checkaddr(ctx, dom[1], regbase, 0))
65 d = dom[1];
66
67 if (d) {
68 struct rnndecaddrinfo *info = rnndec_decodeaddr(ctx, d, regbase, 0);
69 if (info) {
70 printf("\t; %s", info->name);
71 free(info->name);
72 free(info);
73 return;
74 }
75 }
76 }
77
78 static void printc(const char *c, const char *fmt, ...)
79 {
80 va_list args;
81 printf("%s", c);
82 va_start(args, fmt);
83 vprintf(fmt, args);
84 va_end(args);
85 printf("%s", ctx->colors->reset);
86 }
87
88 #define printerr(fmt, ...) printc(ctx->colors->err, fmt, ##__VA_ARGS__)
89 #define printlbl(fmt, ...) printc(ctx->colors->btarg, fmt, ##__VA_ARGS__)
90
91 static void print_reg(unsigned reg)
92 {
93 // XXX seems like *reading* $00 --> literal zero??
94 // seems like read from $1c gives packet remaining len??
95 // $01 current packet header, writing to $01 triggers
96 // parsing header and jumping to appropriate handler.
97 if (reg == 0x1c)
98 printf("$rem"); /* remainding dwords in packet */
99 else if (reg == 0x1d)
100 printf("$addr");
101 else if (reg == 0x1e)
102 printf("$addr2"); // XXX
103 else if (reg == 0x1f)
104 printf("$data");
105 else
106 printf("$%02x", reg);
107 }
108
109 static void print_src(unsigned reg)
110 {
111 print_reg(reg);
112 }
113
114 static void print_dst(unsigned reg)
115 {
116 print_reg(reg);
117 }
118
119 static void print_alu_name(afuc_opc opc, uint32_t instr)
120 {
121 if (opc == OPC_ADD) {
122 printf("add ");
123 } else if (opc == OPC_ADDHI) {
124 printf("addhi ");
125 } else if (opc == OPC_SUB) {
126 printf("sub ");
127 } else if (opc == OPC_SUBHI) {
128 printf("subhi ");
129 } else if (opc == OPC_AND) {
130 printf("and ");
131 } else if (opc == OPC_OR) {
132 printf("or ");
133 } else if (opc == OPC_XOR) {
134 printf("xor ");
135 } else if (opc == OPC_NOT) {
136 printf("not ");
137 } else if (opc == OPC_SHL) {
138 printf("shl ");
139 } else if (opc == OPC_USHR) {
140 printf("ushr ");
141 } else if (opc == OPC_ISHR) {
142 printf("ishr ");
143 } else if (opc == OPC_ROT) {
144 printf("rot ");
145 } else if (opc == OPC_MUL8) {
146 printf("mul8 ");
147 } else if (opc == OPC_MIN) {
148 printf("min ");
149 } else if (opc == OPC_MAX) {
150 printf("max ");
151 } else if (opc == OPC_CMP) {
152 printf("cmp ");
153 } else if (opc == OPC_MSB) {
154 printf("msb ");
155 } else {
156 printerr("[%08x]", instr);
157 printf(" ; alu%02x ", opc);
158 }
159 }
160
161 static const char *getpm4(uint32_t id)
162 {
163 return rnndec_decode_enum(ctx, "adreno_pm4_type3_packets", id);
164 }
165
166 static inline unsigned
167 _odd_parity_bit(unsigned val)
168 {
169 /* See: http://graphics.stanford.edu/~seander/bithacks.html#ParityParallel
170 * note that we want odd parity so 0x6996 is inverted.
171 */
172 val ^= val >> 16;
173 val ^= val >> 8;
174 val ^= val >> 4;
175 val &= 0xf;
176 return (~0x6996 >> val) & 1;
177 }
178
179 static struct {
180 uint32_t offset;
181 uint32_t num_jump_labels;
182 uint32_t jump_labels[256];
183 } jump_labels[1024];
184 int num_jump_labels;
185
186 static void add_jump_table_entry(uint32_t n, uint32_t offset)
187 {
188 int i;
189
190 if (n > 128) /* can't possibly be a PM4 PKT3.. */
191 return;
192
193 for (i = 0; i < num_jump_labels; i++)
194 if (jump_labels[i].offset == offset)
195 goto add_label;
196
197 num_jump_labels = i + 1;
198 jump_labels[i].offset = offset;
199 jump_labels[i].num_jump_labels = 0;
200
201 add_label:
202 jump_labels[i].jump_labels[jump_labels[i].num_jump_labels++] = n;
203 assert(jump_labels[i].num_jump_labels < 256);
204 }
205
206 static int get_jump_table_entry(uint32_t offset)
207 {
208 int i;
209
210 for (i = 0; i < num_jump_labels; i++)
211 if (jump_labels[i].offset == offset)
212 return i;
213
214 return -1;
215 }
216
217 static uint32_t label_offsets[0x512];
218 static int num_label_offsets;
219
220 static int label_idx(uint32_t offset, bool create)
221 {
222 int i;
223 for (i = 0; i < num_label_offsets; i++)
224 if (offset == label_offsets[i])
225 return i;
226 if (!create)
227 return -1;
228 label_offsets[i] = offset;
229 num_label_offsets = i+1;
230 return i;
231 }
232
233 static const char *
234 label_name(uint32_t offset, bool allow_jt)
235 {
236 static char name[8];
237 int lidx;
238
239 if (allow_jt) {
240 lidx = get_jump_table_entry(offset);
241 if (lidx >= 0) {
242 int j;
243 for (j = 0; j < jump_labels[lidx].num_jump_labels; j++) {
244 uint32_t jump_label = jump_labels[lidx].jump_labels[j];
245 const char *str = getpm4(jump_label);
246 if (str)
247 return str;
248 }
249 // if we don't find anything w/ known name, maybe we should
250 // return UNKN%d to at least make it clear that this is some
251 // sort of jump-table entry?
252 }
253 }
254
255 lidx = label_idx(offset, false);
256 if (lidx < 0)
257 return NULL;
258 sprintf(name, "l%03d", lidx);
259 return name;
260 }
261
262
263 static uint32_t fxn_offsets[0x512];
264 static int num_fxn_offsets;
265
266 static int fxn_idx(uint32_t offset, bool create)
267 {
268 int i;
269 for (i = 0; i < num_fxn_offsets; i++)
270 if (offset == fxn_offsets[i])
271 return i;
272 if (!create)
273 return -1;
274 fxn_offsets[i] = offset;
275 num_fxn_offsets = i+1;
276 return i;
277 }
278
279 static const char *
280 fxn_name(uint32_t offset)
281 {
282 static char name[8];
283 int fidx = fxn_idx(offset, false);
284 if (fidx < 0)
285 return NULL;
286 sprintf(name, "fxn%02d", fidx);
287 return name;
288 }
289
290 static void print_control_reg(uint32_t id)
291 {
292 if (rnndec_checkaddr(ctx, control_regs, id, 0)) {
293 struct rnndecaddrinfo *info = rnndec_decodeaddr(ctx, control_regs, id, 0);
294 printf("@%s", info->name);
295 free(info->name);
296 free(info);
297 } else {
298 printf("0x%03x", id);
299 }
300 }
301
302 static void disasm(uint32_t *buf, int sizedwords)
303 {
304 uint32_t *instrs = buf;
305 const int jmptbl_start = instrs[1] & 0xffff;
306 uint32_t *jmptbl = &buf[jmptbl_start];
307 afuc_opc opc;
308 bool rep;
309 int i;
310
311
312 /* parse jumptable: */
313 for (i = 0; i < 0x80; i++) {
314 unsigned offset = jmptbl[i];
315 unsigned n = i;// + CP_NOP;
316 add_jump_table_entry(n, offset);
317 }
318
319 /* do a pre-pass to find instructions that are potential branch targets,
320 * and add labels for them:
321 */
322 for (i = 0; i < jmptbl_start; i++) {
323 afuc_instr *instr = (void *)&instrs[i];
324
325 afuc_get_opc(instr, &opc, &rep);
326
327 switch (opc) {
328 case OPC_BRNEI:
329 case OPC_BREQI:
330 case OPC_BRNEB:
331 case OPC_BREQB:
332 label_idx(i + instr->br.ioff, true);
333 break;
334 case OPC_PREEMPTLEAVE6:
335 if (gpuver >= 6)
336 label_idx(instr->call.uoff, true);
337 break;
338 case OPC_CALL:
339 fxn_idx(instr->call.uoff, true);
340 break;
341 case OPC_SETSECURE:
342 /* this implicitly jumps to pc + 3 if successful */
343 label_idx(i + 3, true);
344 break;
345 default:
346 break;
347 }
348 }
349
350 /* print instructions: */
351 for (i = 0; i < jmptbl_start; i++) {
352 int jump_label_idx;
353 afuc_instr *instr = (void *)&instrs[i];
354 const char *fname, *lname;
355 afuc_opc opc;
356 bool rep;
357
358 afuc_get_opc(instr, &opc, &rep);
359
360 lname = label_name(i, false);
361 fname = fxn_name(i);
362 jump_label_idx = get_jump_table_entry(i);
363
364 if (jump_label_idx >= 0) {
365 int j;
366 printf("\n");
367 for (j = 0; j < jump_labels[jump_label_idx].num_jump_labels; j++) {
368 uint32_t jump_label = jump_labels[jump_label_idx].jump_labels[j];
369 const char *name = getpm4(jump_label);
370 if (name) {
371 printlbl("%s", name);
372 } else {
373 printlbl("UNKN%d", jump_label);
374 }
375 printf(":\n");
376 }
377 }
378
379 if (fname) {
380 printlbl("%s", fname);
381 printf(":\n");
382 }
383
384 if (lname) {
385 printlbl(" %s", lname);
386 printf(":");
387 } else {
388 printf(" ");
389 }
390
391
392 if (verbose) {
393 printf("\t%04x: %08x ", i, instrs[i]);
394 } else {
395 printf(" ");
396 }
397
398 switch (opc) {
399 case OPC_NOP: {
400 /* a6xx changed the default immediate, and apparently 0
401 * is illegal now.
402 */
403 const uint32_t nop = gpuver >= 6 ? 0x1000000 : 0x0;
404 if (instrs[i] != nop) {
405 printerr("[%08x]", instrs[i]);
406 printf(" ; ");
407 }
408 if (rep)
409 printf("(rep)");
410 printf("nop");
411 print_gpu_reg(instrs[i]);
412
413 break;
414 }
415 case OPC_ADD:
416 case OPC_ADDHI:
417 case OPC_SUB:
418 case OPC_SUBHI:
419 case OPC_AND:
420 case OPC_OR:
421 case OPC_XOR:
422 case OPC_NOT:
423 case OPC_SHL:
424 case OPC_USHR:
425 case OPC_ISHR:
426 case OPC_ROT:
427 case OPC_MUL8:
428 case OPC_MIN:
429 case OPC_MAX:
430 case OPC_CMP: {
431 bool src1 = true;
432
433 if (opc == OPC_NOT)
434 src1 = false;
435
436 if (rep)
437 printf("(rep)");
438
439 print_alu_name(opc, instrs[i]);
440 print_dst(instr->alui.dst);
441 printf(", ");
442 if (src1) {
443 print_src(instr->alui.src);
444 printf(", ");
445 }
446 printf("0x%04x", instr->alui.uimm);
447 print_gpu_reg(instr->alui.uimm);
448
449 /* print out unexpected bits: */
450 if (verbose) {
451 if (instr->alui.src && !src1)
452 printerr(" (src=%02x)", instr->alui.src);
453 }
454
455 break;
456 }
457 case OPC_MOVI: {
458 if (rep)
459 printf("(rep)");
460 printf("mov ");
461 print_dst(instr->movi.dst);
462 printf(", 0x%04x", instr->movi.uimm);
463 if (instr->movi.shift)
464 printf(" << %u", instr->movi.shift);
465
466 /* using mov w/ << 16 is popular way to construct a pkt7
467 * header to send (for ex, from PFP to ME), so check that
468 * case first
469 */
470 if ((instr->movi.shift == 16) &&
471 ((instr->movi.uimm & 0xff00) == 0x7000)) {
472 unsigned opc, p;
473
474 opc = instr->movi.uimm & 0x7f;
475 p = _odd_parity_bit(opc);
476
477 /* So, you'd think that checking the parity bit would be
478 * a good way to rule out false positives, but seems like
479 * ME doesn't really care.. at least it would filter out
480 * things that look like actual legit packets between
481 * PFP and ME..
482 */
483 if (1 || p == ((instr->movi.uimm >> 7) & 0x1)) {
484 const char *name = getpm4(opc);
485 printf("\t; ");
486 if (name)
487 printlbl("%s", name);
488 else
489 printlbl("UNKN%u", opc);
490 break;
491 }
492 }
493
494 print_gpu_reg(instr->movi.uimm << instr->movi.shift);
495
496 break;
497 }
498 case OPC_ALU: {
499 bool src1 = true;
500
501 if (instr->alu.alu == OPC_NOT || instr->alu.alu == OPC_MSB)
502 src1 = false;
503
504 if (instr->alu.pad)
505 printf("[%08x] ; ", instrs[i]);
506
507 if (rep)
508 printf("(rep)");
509
510 /* special case mnemonics:
511 * reading $00 seems to always yield zero, and so:
512 * or $dst, $00, $src -> mov $dst, $src
513 * Maybe add one for negate too, ie.
514 * sub $dst, $00, $src ???
515 */
516 if ((instr->alu.alu == OPC_OR) && !instr->alu.src1) {
517 printf("mov ");
518 src1 = false;
519 } else {
520 print_alu_name(instr->alu.alu, instrs[i]);
521 }
522
523 print_dst(instr->alu.dst);
524 if (src1) {
525 printf(", ");
526 print_src(instr->alu.src1);
527 }
528 printf(", ");
529 print_src(instr->alu.src2);
530
531 /* print out unexpected bits: */
532 if (verbose) {
533 if (instr->alu.pad)
534 printerr(" (pad=%03x)", instr->alu.pad);
535 if (instr->alu.src1 && !src1)
536 printerr(" (src1=%02x)", instr->alu.src1);
537 }
538 break;
539 }
540 case OPC_CWRITE6:
541 case OPC_CREAD6:
542 case OPC_STORE6:
543 case OPC_LOAD6: {
544 if (rep)
545 printf("(rep)");
546
547 bool is_control_reg = true;
548 if (gpuver >= 6) {
549 switch (opc) {
550 case OPC_CWRITE6:
551 printf("cwrite ");
552 break;
553 case OPC_CREAD6:
554 printf("cread ");
555 break;
556 case OPC_STORE6:
557 is_control_reg = false;
558 printf("store ");
559 break;
560 case OPC_LOAD6:
561 is_control_reg = false;
562 printf("load ");
563 break;
564 default:
565 assert(!"unreachable");
566 }
567 } else {
568 switch (opc) {
569 case OPC_CWRITE5:
570 printf("cwrite ");
571 break;
572 case OPC_CREAD5:
573 printf("cread ");
574 break;
575 default:
576 fprintf(stderr, "A6xx control opcode on A5xx?\n");
577 exit(1);
578 }
579 }
580
581 print_src(instr->control.src1);
582 printf(", [");
583 print_src(instr->control.src2);
584 printf(" + ");
585 if (is_control_reg && instr->control.flags != 0x4)
586 print_control_reg(instr->control.uimm);
587 else
588 printf("0x%03x", instr->control.uimm);
589 printf("], 0x%x", instr->control.flags);
590 break;
591 }
592 case OPC_BRNEI:
593 case OPC_BREQI:
594 case OPC_BRNEB:
595 case OPC_BREQB: {
596 unsigned off = i + instr->br.ioff;
597
598 assert(!rep);
599
600 /* Since $00 reads back zero, it can be used as src for
601 * unconditional branches. (This only really makes sense
602 * for the BREQB.. or possible BRNEI if imm==0.)
603 *
604 * If bit=0 then branch is taken if *all* bits are zero.
605 * Otherwise it is taken if bit (bit-1) is clear.
606 *
607 * Note the instruction after a jump/branch is executed
608 * regardless of whether branch is taken, so use nop or
609 * take that into account in code.
610 */
611 if (instr->br.src || (opc != OPC_BRNEB)) {
612 bool immed = false;
613
614 if (opc == OPC_BRNEI) {
615 printf("brne ");
616 immed = true;
617 } else if (opc == OPC_BREQI) {
618 printf("breq ");
619 immed = true;
620 } else if (opc == OPC_BRNEB) {
621 printf("brne ");
622 } else if (opc == OPC_BREQB) {
623 printf("breq ");
624 }
625 print_src(instr->br.src);
626 if (immed) {
627 printf(", 0x%x,", instr->br.bit_or_imm);
628 } else {
629 printf(", b%u,", instr->br.bit_or_imm);
630 }
631 } else {
632 printf("jump");
633 if (verbose && instr->br.bit_or_imm) {
634 printerr(" (src=%03x, bit=%03x) ",
635 instr->br.src, instr->br.bit_or_imm);
636 }
637 }
638
639 printf(" #");
640 printlbl("%s", label_name(off, true));
641 if (verbose)
642 printf(" (#%d, %04x)", instr->br.ioff, off);
643 break;
644 }
645 case OPC_CALL:
646 assert(!rep);
647 printf("call #");
648 printlbl("%s", fxn_name(instr->call.uoff));
649 if (verbose) {
650 printf(" (%04x)", instr->call.uoff);
651 if (instr->br.bit_or_imm || instr->br.src) {
652 printerr(" (src=%03x, bit=%03x) ",
653 instr->br.src, instr->br.bit_or_imm);
654 }
655 }
656 break;
657 case OPC_RET:
658 assert(!rep);
659 if (instr->ret.pad)
660 printf("[%08x] ; ", instrs[i]);
661 if (instr->ret.interrupt)
662 printf("iret");
663 else
664 printf("ret");
665 break;
666 case OPC_WIN:
667 assert(!rep);
668 if (instr->waitin.pad)
669 printf("[%08x] ; ", instrs[i]);
670 printf("waitin");
671 if (verbose && instr->waitin.pad)
672 printerr(" (pad=%x)", instr->waitin.pad);
673 break;
674 case OPC_PREEMPTLEAVE6:
675 if (gpuver < 6) {
676 printf("[%08x] ; op38", instrs[i]);
677 } else {
678 printf("preemptleave #");
679 printlbl("%s", label_name(instr->call.uoff, true));
680 }
681 break;
682 case OPC_SETSECURE:
683 /* Note: This seems to implicitly read the secure/not-secure state
684 * to set from the low bit of $02, and implicitly jumps to pc + 3
685 * (i.e. skipping the next two instructions) if it succeeds. We
686 * print these implicit parameters to make reading the disassembly
687 * easier.
688 */
689 if (instr->pad)
690 printf("[%08x] ; ", instrs[i]);
691 printf("setsecure $02, #");
692 printlbl("%s", label_name(i + 3, true));
693 break;
694 default:
695 printerr("[%08x]", instrs[i]);
696 printf(" ; op%02x ", opc);
697 print_dst(instr->alui.dst);
698 printf(", ");
699 print_src(instr->alui.src);
700 print_gpu_reg(instrs[i] & 0xffff);
701 break;
702 }
703 printf("\n");
704 }
705
706 /* print jumptable: */
707 if (verbose) {
708 printf(";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;\n");
709 printf("; JUMP TABLE\n");
710 for (i = 0; i < 0x7f; i++) {
711 int n = i;// + CP_NOP;
712 uint32_t offset = jmptbl[i];
713 const char *name = getpm4(n);
714 printf("%3d %02x: ", n, n);
715 printf("%04x", offset);
716 if (name) {
717 printf(" ; %s", name);
718 } else {
719 printf(" ; UNKN%d", n);
720 }
721 printf("\n");
722 }
723 }
724 }
725
726 #define CHUNKSIZE 4096
727
728 static char * readfile(const char *path, int *sz)
729 {
730 char *buf = NULL;
731 int fd, ret, n = 0;
732
733 fd = open(path, O_RDONLY);
734 if (fd < 0)
735 return NULL;
736
737 while (1) {
738 buf = realloc(buf, n + CHUNKSIZE);
739 ret = read(fd, buf + n, CHUNKSIZE);
740 if (ret < 0) {
741 free(buf);
742 *sz = 0;
743 return NULL;
744 } else if (ret < CHUNKSIZE) {
745 n += ret;
746 *sz = n;
747 return buf;
748 } else {
749 n += CHUNKSIZE;
750 }
751 }
752 }
753
754 static void usage(void)
755 {
756 fprintf(stderr, "Usage:\n"
757 "\tdisasm [-g GPUVER] [-v] [-c] filename.asm\n"
758 "\t\t-g - specify GPU version (5, etc)\n"
759 "\t\t-c - use colors\n"
760 "\t\t-v - verbose output\n"
761 );
762 exit(2);
763 }
764
765 int main(int argc, char **argv)
766 {
767 uint32_t *buf;
768 char *file, *control_reg_name;
769 bool colors = false;
770 int sz, c;
771
772 /* Argument parsing: */
773 while ((c = getopt (argc, argv, "g:vc")) != -1) {
774 switch (c) {
775 case 'g':
776 gpuver = atoi(optarg);
777 break;
778 case 'v':
779 verbose = true;
780 break;
781 case 'c':
782 colors = true;
783 break;
784 default:
785 usage();
786 }
787 }
788
789 if (optind >= argc) {
790 fprintf(stderr, "no file specified!\n");
791 usage();
792 }
793
794 file = argv[optind];
795
796 /* if gpu version not specified, infer from filename: */
797 if (!gpuver) {
798 if (strstr(file, "a5")) {
799 gpuver = 5;
800 } else if (strstr(file, "a6")) {
801 gpuver = 6;
802 }
803 }
804
805 switch (gpuver) {
806 case 6:
807 printf("; a6xx microcode\n");
808 variant = "A6XX";
809 control_reg_name = "A6XX_CONTROL_REG";
810 break;
811 case 5:
812 printf("; a5xx microcode\n");
813 variant = "A5XX";
814 control_reg_name = "A5XX_CONTROL_REG";
815 break;
816 default:
817 fprintf(stderr, "unknown GPU version!\n");
818 usage();
819 }
820
821 rnn_init();
822 db = rnn_newdb();
823
824 ctx = rnndec_newcontext(db);
825 ctx->colors = colors ? &envy_def_colors : &envy_null_colors;
826
827 rnn_parsefile(db, "adreno.xml");
828 rnn_prepdb(db);
829 if (db->estatus)
830 errx(db->estatus, "failed to parse register database");
831 dom[0] = rnn_finddomain(db, variant);
832 dom[1] = rnn_finddomain(db, "AXXX");
833 control_regs = rnn_finddomain(db, control_reg_name);
834
835 rnndec_varadd(ctx, "chip", variant);
836
837 buf = (uint32_t *)readfile(file, &sz);
838
839 printf("; Disassembling microcode: %s\n", file);
840 printf("; Version: %08x\n\n", buf[1]);
841 disasm(&buf[1], sz/4 - 1);
842
843 return 0;
844 }