/* * Copyright (c) 2016 Etnaviv Project * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sub license, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice (including the * next paragraph) shall be included in all copies or substantial portions * of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. * * Authors: * Christian Gmeiner <christian.gmeiner@gmail.com> */ #include "etnaviv_disasm.h" #include <assert.h> #include <stdbool.h> #include <stdio.h> #include <stdlib.h> #include "hw/isa.xml.h" struct instr { /* dword0: */ uint32_t opc : 6; uint32_t cond : 5; uint32_t sat : 1; uint32_t dst_use : 1; uint32_t dst_amode : 3; uint32_t dst_reg : 7; uint32_t dst_comps : 4; uint32_t tex_id : 5; /* dword1: */ uint32_t tex_amode : 3; uint32_t tex_swiz : 8; uint32_t src0_use : 1; uint32_t src0_reg : 9; uint32_t type_bit2 : 1; uint32_t src0_swiz : 8; uint32_t src0_neg : 1; uint32_t src0_abs : 1; /* dword2: */ uint32_t src0_amode : 3; uint32_t src0_rgroup : 3; uint32_t src1_use : 1; uint32_t src1_reg : 9; uint32_t opcode_bit6 : 1; uint32_t src1_swiz : 8; uint32_t src1_neg : 1; uint32_t src1_abs : 1; uint32_t src1_amode : 3; uint32_t type_bit01 : 2; /* dword3: */ union { struct { uint32_t src1_rgroup : 3; uint32_t src2_use : 1; uint32_t src2_reg : 9; uint32_t unk3_13 : 1; uint32_t src2_swiz : 8; uint32_t src2_neg : 1; uint32_t src2_abs : 1; uint32_t unk3_24 : 1; uint32_t src2_amode : 3; uint32_t src2_rgroup : 3; uint32_t unk3_31 : 1; }; uint32_t dword3; }; }; struct dst_operand { bool use; uint8_t amode; uint16_t reg; uint8_t comps; }; struct src_operand { bool use; bool neg; bool abs; uint8_t rgroup; uint16_t reg; uint8_t swiz; uint8_t amode; }; struct tex_operand { uint8_t id; uint8_t amode; uint8_t swiz; }; struct opc_operands { struct dst_operand *dst; struct tex_operand *tex; struct src_operand *src0; struct src_operand *src1; struct src_operand *src2; int imm; }; static void printf_type(uint8_t type) { switch(type) { case INST_TYPE_F32: /* as f32 is the default print nothing */ break; case INST_TYPE_S32: printf(".s32"); break; case INST_TYPE_S8: printf(".s8"); break; case INST_TYPE_U16: printf(".u16"); break; case INST_TYPE_F16: printf(".f16"); break; case INST_TYPE_S16: printf(".s16"); break; case INST_TYPE_U32: printf(".u32"); break; case INST_TYPE_U8: printf(".u8"); break; default: abort(); break; } } static void print_condition(uint8_t condition) { switch (condition) { case INST_CONDITION_TRUE: break; case INST_CONDITION_GT: printf(".GT"); break; case INST_CONDITION_LT: printf(".LT"); break; case INST_CONDITION_GE: printf(".GE"); break; case INST_CONDITION_LE: printf(".LE"); break; case INST_CONDITION_EQ: printf(".EQ"); break; case INST_CONDITION_NE: printf(".NE"); break; case INST_CONDITION_AND: printf(".AND"); break; case INST_CONDITION_OR: printf(".OR"); break; case INST_CONDITION_XOR: printf(".XOR"); break; case INST_CONDITION_NOT: printf(".NOT"); break; case INST_CONDITION_NZ: printf(".NZ"); break; case INST_CONDITION_GEZ: printf(".GEZ"); break; case INST_CONDITION_GZ: printf(".GZ"); break; case INST_CONDITION_LEZ: printf(".LEZ"); break; case INST_CONDITION_LZ: printf(".LZ"); break; default: abort(); break; } } static void print_rgroup(uint8_t rgoup) { switch (rgoup) { case INST_RGROUP_TEMP: printf("t"); break; case INST_RGROUP_INTERNAL: printf("i"); break; case INST_RGROUP_UNIFORM_0: case INST_RGROUP_UNIFORM_1: printf("u"); break; } } static void print_components(uint8_t components) { if (components == 15) return; printf("."); if (components & INST_COMPS_X) printf("x"); else printf("_"); if (components & INST_COMPS_Y) printf("y"); else printf("_"); if (components & INST_COMPS_Z) printf("z"); else printf("_"); if (components & INST_COMPS_W) printf("w"); else printf("_"); } static inline void print_swiz_comp(uint8_t swiz_comp) { switch (swiz_comp) { case INST_SWIZ_COMP_X: printf("x"); break; case INST_SWIZ_COMP_Y: printf("y"); break; case INST_SWIZ_COMP_Z: printf("z"); break; case INST_SWIZ_COMP_W: printf("w"); break; default: abort(); break; } } static void print_swiz(uint8_t swiz) { // if a null swizzle if (swiz == 0xe4) return; const unsigned x = swiz & 0x3; const unsigned y = (swiz & 0x0C) >> 2; const unsigned z = (swiz & 0x30) >> 4; const unsigned w = (swiz & 0xc0) >> 6; printf("."); print_swiz_comp(x); print_swiz_comp(y); print_swiz_comp(z); print_swiz_comp(w); } static void print_amode(uint8_t amode) { switch (amode) { case INST_AMODE_DIRECT: /* nothing to output */ break; case INST_AMODE_ADD_A_X: printf("[a.x]"); break; case INST_AMODE_ADD_A_Y: printf("[a.y]"); break; case INST_AMODE_ADD_A_Z: printf("[a.z]"); break; case INST_AMODE_ADD_A_W: printf("[a.w]"); break; default: abort(); break; } } static void print_dst(struct dst_operand *dst, bool sep) { if (dst->use) { printf("t%u", dst->reg); print_amode(dst->amode); print_components(dst->comps); } else { printf("void"); } if (sep) printf(", "); } static void print_tex(struct tex_operand *tex, bool sep) { printf("tex%u", tex->id); print_amode(tex->amode); print_swiz(tex->swiz); if (sep) printf(", "); } static void print_src(struct src_operand *src, bool sep) { if (src->use) { if (src->neg) printf("-"); if (src->abs) printf("|"); if (src->rgroup == INST_RGROUP_UNIFORM_1) src->reg += 128; print_rgroup(src->rgroup); printf("%u", src->reg); print_amode(src->amode); print_swiz(src->swiz); if (src->abs) printf("|"); } else { printf("void"); } if (sep) printf(", "); } static void print_opc_default(struct opc_operands *operands) { print_dst(operands->dst, true); print_src(operands->src0, true); print_src(operands->src1, true); print_src(operands->src2, false); } static void print_opc_mov(struct opc_operands *operands) { // dst (areg) printf("a%u", operands->dst->reg); print_components(operands->dst->comps); printf(", "); print_src(operands->src0, true); print_src(operands->src1, true); print_src(operands->src2, false); } static void print_opc_tex(struct opc_operands *operands) { print_dst(operands->dst, true); print_tex(operands->tex, true); print_src(operands->src0, true); print_src(operands->src1, true); print_src(operands->src2, false); } static void print_opc_imm(struct opc_operands *operands) { print_dst(operands->dst, true); print_src(operands->src0, true); print_src(operands->src1, true); printf("label_%04d", operands->imm); } #define OPC_BITS 7 static const struct opc_info { const char *name; void (*print)(struct opc_operands *operands); } opcs[1 << OPC_BITS] = { #define OPC(opc) [INST_OPCODE_##opc] = {#opc, print_opc_default} #define OPC_MOV(opc) [INST_OPCODE_##opc] = {#opc, print_opc_mov} #define OPC_TEX(opc) [INST_OPCODE_##opc] = {#opc, print_opc_tex} #define OPC_IMM(opc) [INST_OPCODE_##opc] = {#opc, print_opc_imm} OPC(NOP), OPC(ADD), OPC(MAD), OPC(MUL), OPC(DST), OPC(DP3), OPC(DP4), OPC(DSX), OPC(DSY), OPC(MOV), OPC_MOV(MOVAR), OPC_MOV(MOVAF), OPC(RCP), OPC(RSQ), OPC(LITP), OPC(SELECT), OPC(SET), OPC(EXP), OPC(LOG), OPC(FRC), OPC_IMM(CALL), OPC(RET), OPC_IMM(BRANCH), OPC_TEX(TEXKILL), OPC_TEX(TEXLD), OPC_TEX(TEXLDB), OPC_TEX(TEXLDD), OPC_TEX(TEXLDL), OPC_TEX(TEXLDPCF), OPC(REP), OPC(ENDREP), OPC(LOOP), OPC(ENDLOOP), OPC(SQRT), OPC(SIN), OPC(COS), OPC(FLOOR), OPC(CEIL), OPC(SIGN), OPC(I2F), OPC(CMP), OPC(LOAD), OPC(STORE), OPC(IMULLO0), OPC(IMULHI0), OPC(LEADZERO), OPC(LSHIFT), OPC(RSHIFT), OPC(ROTATE), OPC(OR), OPC(AND), OPC(XOR), OPC(NOT), OPC(DP2), }; static void print_instr(uint32_t *dwords, int n, enum debug_t debug) { struct instr *instr = (struct instr *)dwords; const unsigned opc = instr->opc | (instr->opcode_bit6 << 6); const char *name = opcs[opc].name; printf("%04d: ", n); if (debug & PRINT_RAW) printf("%08x %08x %08x %08x ", dwords[0], dwords[1], dwords[2], dwords[3]); if (name) { struct dst_operand dst = { .use = instr->dst_use, .amode = instr->dst_amode, .reg = instr->dst_reg, .comps = instr->dst_comps }; struct tex_operand tex = { .id = instr->tex_id, .amode = instr->tex_amode, .swiz = instr->tex_swiz, }; struct src_operand src0 = { .use = instr->src0_use, .neg = instr->src0_neg, .abs = instr->src0_abs, .rgroup = instr->src0_rgroup, .reg = instr->src0_reg, .swiz = instr->src0_swiz, .amode = instr->src0_amode, }; struct src_operand src1 = { .use = instr->src1_use, .neg = instr->src1_neg, .abs = instr->src1_abs, .rgroup = instr->src1_rgroup, .reg = instr->src1_reg, .swiz = instr->src1_swiz, .amode = instr->src1_amode, }; struct src_operand src2 = { .use = instr->src2_use, .neg = instr->src2_neg, .abs = instr->src2_abs, .rgroup = instr->src2_rgroup, .reg = instr->src2_reg, .swiz = instr->src2_swiz, .amode = instr->src2_amode, }; int imm = (instr->dword3 & VIV_ISA_WORD_3_SRC2_IMM__MASK) >> VIV_ISA_WORD_3_SRC2_IMM__SHIFT; struct opc_operands operands = { .dst = &dst, .tex = &tex, .src0 = &src0, .src1 = &src1, .src2 = &src2, .imm = imm, }; uint8_t type = instr->type_bit01 | (instr->type_bit2 << 2); printf("%s", name); printf_type(type); if (instr->sat) printf(".SAT"); print_condition(instr->cond); printf(" "); opcs[opc].print(&operands); } else { printf("unknown (%d)", instr->opc); } printf("\n"); } void etna_disasm(uint32_t *dwords, int sizedwords, enum debug_t debug) { unsigned i; assert((sizedwords % 2) == 0); for (i = 0; i < sizedwords; i += 4) print_instr(&dwords[i], i / 4, debug); }