void disassembleCfALUInstruction(fmt::MemoryWriter &out, const ControlFlowInst &inst) { auto name = getInstructionName(inst.alu.word1.CF_INST()); auto addr = inst.alu.word0.ADDR(); auto count = inst.alu.word1.COUNT() + 1; out << name << " ADDR(" << addr << ")" << " CNT(" << count << ")"; if (!inst.word1.BARRIER()) { out << " NO_BARRIER"; } if (inst.word1.WHOLE_QUAD_MODE()) { out << " WHOLE_QUAD"; } disassembleKcache(out, 0, inst.alu.word0.KCACHE_MODE0(), inst.alu.word0.KCACHE_BANK0(), inst.alu.word1.KCACHE_ADDR0()); disassembleKcache(out, 1, inst.alu.word1.KCACHE_MODE1(), inst.alu.word0.KCACHE_BANK1(), inst.alu.word1.KCACHE_ADDR1()); }
static void insertPush(Shader &shader, uint32_t cfPC) { auto push = new shadir::CfInstruction {}; push->cfPC = cfPC; push->id = SQ_CF_INST_PUSH; push->name = getInstructionName(push->id); shader.linear.push_back(push); shader.custom.emplace_back(push); }
static void insertElse(Shader &shader, uint32_t cfPC) { auto elseIns = new shadir::CfInstruction {}; elseIns->cfPC = cfPC; elseIns->id = SQ_CF_INST_ELSE; elseIns->name = getInstructionName(elseIns->id); elseIns->popCount = 1; shader.linear.push_back(elseIns); shader.custom.emplace_back(elseIns); }
static void insertContinue(Shader &shader, uint32_t cfPC) { auto loopContinue = new shadir::CfInstruction {}; loopContinue->cfPC = cfPC; loopContinue->id = SQ_CF_INST_LOOP_CONTINUE; loopContinue->name = getInstructionName(loopContinue->id); loopContinue->popCount = 1; shader.linear.push_back(loopContinue); shader.custom.emplace_back(loopContinue); }
static void insertBreak(Shader &shader, uint32_t cfPC) { auto loopBreak = new shadir::CfInstruction {}; loopBreak->cfPC = cfPC; loopBreak->id = SQ_CF_INST_LOOP_BREAK; loopBreak->name = getInstructionName(loopBreak->id); loopBreak->popCount = 1; shader.linear.push_back(loopBreak); shader.custom.emplace_back(loopBreak); }
static void insertPop(Shader &shader, uint32_t cfPC) { auto pop = new shadir::CfInstruction {}; pop->cfPC = cfPC; pop->id = SQ_CF_INST_PUSH; pop->name = getInstructionName(pop->id); pop->popCount = 1; shader.linear.push_back(pop); shader.custom.emplace_back(pop); }
static void disassembleNormal(State &state, const ControlFlowInst &inst) { auto id = inst.word1.CF_INST(); auto name = getInstructionName(id); switch (id) { case SQ_CF_INST_WAIT_ACK: case SQ_CF_INST_TEX_ACK: case SQ_CF_INST_VTX_ACK: case SQ_CF_INST_VTX_TC_ACK: decaf_abort(fmt::format("Unable to decode instruction {} {}", id, name)); } // Decode instruction clause state.out.write("{}{:02} ", state.indent, state.cfPC); disassembleCF(state.out, inst); state.out << "\n"; switch (id) { case SQ_CF_INST_LOOP_START: case SQ_CF_INST_LOOP_START_DX10: case SQ_CF_INST_LOOP_START_NO_AL: increaseIndent(state); break; case SQ_CF_INST_LOOP_END: decreaseIndent(state); break; case SQ_CF_INST_TEX: disassembleTEXClause(state, inst); break; case SQ_CF_INST_VTX: case SQ_CF_INST_VTX_TC: disassembleVtxClause(state, inst); break; } }
void disassembleCF(fmt::MemoryWriter &out, const ControlFlowInst &inst) { auto id = inst.word1.CF_INST(); auto name = getInstructionName(id); out << name; switch (id) { case SQ_CF_INST_TEX: disassembleCfTEX(out, inst); break; case SQ_CF_INST_VTX: case SQ_CF_INST_VTX_TC: disassembleCfVTX(out, inst); break; case SQ_CF_INST_LOOP_START: case SQ_CF_INST_LOOP_START_DX10: case SQ_CF_INST_LOOP_START_NO_AL: case SQ_CF_INST_LOOP_END: case SQ_CF_INST_LOOP_CONTINUE: case SQ_CF_INST_LOOP_BREAK: disassembleLoop(out, inst); break; case SQ_CF_INST_JUMP: case SQ_CF_INST_ELSE: case SQ_CF_INST_CALL: case SQ_CF_INST_CALL_FS: case SQ_CF_INST_RETURN: case SQ_CF_INST_POP_JUMP: disassembleJump(out, inst); break; case SQ_CF_INST_EMIT_VERTEX: case SQ_CF_INST_EMIT_CUT_VERTEX: case SQ_CF_INST_CUT_VERTEX: if (!inst.word1.BARRIER()) { out << " NO_BARRIER"; } break; case SQ_CF_INST_PUSH: case SQ_CF_INST_PUSH_ELSE: case SQ_CF_INST_KILL: disassembleCondition(out, inst); // switch case pass through case SQ_CF_INST_POP: case SQ_CF_INST_POP_PUSH: case SQ_CF_INST_POP_PUSH_ELSE: if (inst.word1.POP_COUNT()) { out << " POP_COUNT(" << inst.word1.POP_COUNT() << ")"; } if (inst.word1.VALID_PIXEL_MODE()) { out << " VALID_PIX"; } break; case SQ_CF_INST_END_PROGRAM: case SQ_CF_INST_NOP: break; case SQ_CF_INST_WAIT_ACK: case SQ_CF_INST_TEX_ACK: case SQ_CF_INST_VTX_ACK: case SQ_CF_INST_VTX_TC_ACK: default: out << " UNK_FORMAT"; break; } }
void disassembleAluInstruction(fmt::MemoryWriter &out, const ControlFlowInst &parent, const AluInst &inst, size_t groupPC, SQ_CHAN unit, const gsl::span<const uint32_t> &literals, int namePad) { const char *name = nullptr; SQ_ALU_FLAGS flags; auto srcCount = 0u; if (inst.word1.ENCODING() == SQ_ALU_ENCODING::OP2) { name = getInstructionName(inst.op2.ALU_INST()); flags = getInstructionFlags(inst.op2.ALU_INST()); srcCount = getInstructionNumSrcs(inst.op2.ALU_INST()); } else { name = getInstructionName(inst.op3.ALU_INST()); flags = getInstructionFlags(inst.op3.ALU_INST()); srcCount = getInstructionNumSrcs(inst.op3.ALU_INST()); } out << fmt::pad(name, namePad, ' ') << ' '; auto writeMask = true; if (inst.word1.ENCODING() == SQ_ALU_ENCODING::OP2) { writeMask = inst.op2.WRITE_MASK(); } if (!writeMask) { out << "____"; } else { disassembleAluSource(out, parent, groupPC, inst.word0.INDEX_MODE(), inst.word1.DST_GPR(), inst.word1.DST_REL(), inst.word1.DST_CHAN(), 0, false, false); } if (srcCount > 0) { auto literal = 0u; auto abs = false; if (inst.word0.SRC0_SEL() == SQ_ALU_SRC::LITERAL) { literal = literals[inst.word0.SRC0_CHAN()]; } if (inst.word1.ENCODING() == SQ_ALU_ENCODING::OP2) { abs = !!inst.op2.SRC0_ABS(); } out << ", "; disassembleAluSource(out, parent, groupPC, inst.word0.INDEX_MODE(), inst.word0.SRC0_SEL(), inst.word0.SRC0_REL(), inst.word0.SRC0_CHAN(), literal, inst.word0.SRC0_NEG(), abs); } if (srcCount > 1) { auto literal = 0u; auto abs = false; if (inst.word0.SRC1_SEL() == SQ_ALU_SRC::LITERAL) { literal = literals[inst.word0.SRC1_CHAN()]; } if (inst.word1.ENCODING() == SQ_ALU_ENCODING::OP2) { abs = !!inst.op2.SRC1_ABS(); } out << ", "; disassembleAluSource(out, parent, groupPC, inst.word0.INDEX_MODE(), inst.word0.SRC1_SEL(), inst.word0.SRC1_REL(), inst.word0.SRC1_CHAN(), literal, inst.word0.SRC1_NEG(), abs); } if (srcCount > 2) { auto literal = 0u; if (inst.op3.SRC2_SEL() == SQ_ALU_SRC::LITERAL) { literal = literals[inst.op3.SRC2_CHAN()]; } out << ", "; disassembleAluSource(out, parent, groupPC, inst.word0.INDEX_MODE(), inst.op3.SRC2_SEL(), inst.op3.SRC2_REL(), inst.op3.SRC2_CHAN(), literal, inst.op3.SRC2_NEG(), false); } if (inst.word1.CLAMP()) { out << " CLAMP"; } if (isTranscendentalOnly(flags)) { switch (static_cast<SQ_ALU_SCL_BANK_SWIZZLE>(inst.word1.BANK_SWIZZLE())) { case SQ_ALU_SCL_BANK_SWIZZLE::SCL_210: out << " SCL_210"; break; case SQ_ALU_SCL_BANK_SWIZZLE::SCL_122: out << " SCL_122"; break; case SQ_ALU_SCL_BANK_SWIZZLE::SCL_212: out << " SCL_212"; break; case SQ_ALU_SCL_BANK_SWIZZLE::SCL_221: out << " SCL_221"; break; default: decaf_abort(fmt::format("Unexpected BANK_SWIZZLE {}", inst.word1.BANK_SWIZZLE())); } } else { switch (inst.word1.BANK_SWIZZLE()) { case SQ_ALU_VEC_BANK_SWIZZLE::VEC_012: // This is default, no need to print break; case SQ_ALU_VEC_BANK_SWIZZLE::VEC_021: out << " VEC_021"; break; case SQ_ALU_VEC_BANK_SWIZZLE::VEC_120: out << " VEC_120"; break; case SQ_ALU_VEC_BANK_SWIZZLE::VEC_102: out << " VEC_102"; break; case SQ_ALU_VEC_BANK_SWIZZLE::VEC_201: out << " VEC_201"; break; case SQ_ALU_VEC_BANK_SWIZZLE::VEC_210: out << " VEC_210"; break; default: decaf_abort(fmt::format("Unexpected BANK_SWIZZLE {}", inst.word1.BANK_SWIZZLE())); } } switch (inst.word0.PRED_SEL()) { case SQ_PRED_SEL::OFF: break; case SQ_PRED_SEL::ZERO: out << " PRED_SEL_ZERO"; break; case SQ_PRED_SEL::ONE: out << " PRED_SEL_ONE"; break; default: decaf_abort(fmt::format("Unexpected PRED_SEL {}", inst.word0.PRED_SEL())); } if (inst.word1.ENCODING() == SQ_ALU_ENCODING::OP2) { if (inst.op2.UPDATE_EXECUTE_MASK()) { out << " UPDATE_EXECUTE_MASK"; switch (inst.op2.EXECUTE_MASK_OP()) { case SQ_ALU_EXECUTE_MASK_OP::DEACTIVATE: out << " DEACTIVATE"; break; case SQ_ALU_EXECUTE_MASK_OP::BREAK: out << " BREAK"; break; case SQ_ALU_EXECUTE_MASK_OP::CONTINUE: out << " CONTINUE"; break; case SQ_ALU_EXECUTE_MASK_OP::KILL: out << " KILL"; break; default: decaf_abort(fmt::format("Unexpected EXECUTE_MASK_OP {}", inst.op2.EXECUTE_MASK_OP())); } } else { switch (inst.op2.OMOD()) { case SQ_ALU_OMOD::OFF: break; case SQ_ALU_OMOD::D2: out << " OMOD_D2"; break; case SQ_ALU_OMOD::M2: out << " OMOD_M2"; break; case SQ_ALU_OMOD::M4: out << " OMOD_M4"; break; default: decaf_abort(fmt::format("Unexpected OMOD {}", inst.op2.OMOD())); } } if (inst.op2.UPDATE_PRED()) { out << " UPDATE_PRED"; } } }
void pcode(Tinstruction inst[], int stack[], FILE *output){ int base = 1; // Points to the base address in the stack for the current invocation of a given procedure int top = 0; // Points to the current top of the stack int counter = 0; // Points to an instruction in the program area int aux; int stop = 0; do{ Tinstruction instruction = inst[counter]; counter++; switch (instruction.operation){ case LIT: top++; stack[top] = instruction.argument; break; case OPR: switch(instruction.argument){ case RTN: top = base - 1; counter = stack[top+3]; base = stack[top+2]; break; case NEG: stack[top]= -1*stack[top]; break; case ADD: top--; stack[top] += stack[top+1]; break; case SUB: top--; stack[top] -= stack[top+1]; break; case MUL: top--; stack[top] *= stack[top+1]; break; case DIV: top--; stack[top] /= stack[top+1]; break; case MOD: top--; stack[top] %= stack[top+1]; break; case ODD: stack[top] = (stack[top]%2 == 1); break; case EQL: top--; stack[top] = (stack[top] == stack[top+1]); break; case NEQ: top--; stack[top] = (stack[top] != stack[top+1]); break; case LSS: top--; stack[top] = (stack[top] < stack[top+1]); break; case LEQ: top--; stack[top] = (stack[top] <= stack[top+1]); break; case GTR: top--; stack[top] = (stack[top] > stack[top+1]); break; case GEQ: top--; stack[top] = (stack[top] >= stack[top+1]); break; default: printf("Unknown OPR subfunction"); break; } break; case LOD: top++; stack[top] = stack[ getBase(instruction.level, base, stack) + instruction.argument ]; stack[ getBase(instruction.level, base, stack) + instruction.argument ] = -10; break; case STO: stack[ getBase(instruction.level, base, stack) + instruction.argument ] = stack[top]; top--; break; case CAL: stack[top+1] = getBase(instruction.level, base, stack); stack[top+2] = base; stack[top+3] = counter; base = top+1; counter = instruction.argument; break; case INT: top += instruction.argument; break; case JMP: counter = instruction.argument-1; break; case JPC: if (stack[top] == 0){ counter = instruction.argument-1; } top--; break; default: printf("Unknown function"); } // PrintStackTrace fprintf(output,"%-10s %-7d %-7d %-7d %-7d %-15d", getInstructionName(instruction.operation),instruction.level, instruction.argument, top, counter, base); for (aux = base; aux<top+1 && top > 0;aux++){ if(stack[aux] == -10){ fprintf(output,"_ "); }else{ fprintf(output,"%d ", stack[aux]); } } fprintf(output,"\n"); // If RTN, stop stop = (instruction.operation == 1 && instruction.argument == 0); }while(stop == 0); }
void disassembleVtxInstruction(fmt::MemoryWriter &out, const latte::ControlFlowInst &parent, const VertexFetchInst &vtx, int namePad) { auto id = vtx.word0.VTX_INST(); auto name = getInstructionName(id); out << fmt::pad(name, namePad, ' ') << ' '; // dst auto dstSelX = vtx.word1.DST_SEL_X(); auto dstSelY = vtx.word1.DST_SEL_Y(); auto dstSelZ = vtx.word1.DST_SEL_Z(); auto dstSelW = vtx.word1.DST_SEL_W(); if (dstSelX != latte::SQ_SEL::SEL_MASK || dstSelY != latte::SQ_SEL::SEL_MASK || dstSelZ != latte::SQ_SEL::SEL_MASK || dstSelW != latte::SQ_SEL::SEL_MASK) { if (id == SQ_VTX_INST_SEMANTIC) { out << "SEM[" << vtx.sem.SEMANTIC_ID() << "]"; } else { out << "R" << vtx.gpr.DST_GPR(); if (vtx.gpr.DST_REL() == SQ_REL::REL) { out << "[AL]"; } } out << '.' << disassembleDestMask(dstSelX) << disassembleDestMask(dstSelY) << disassembleDestMask(dstSelZ) << disassembleDestMask(dstSelW); } else { out << "____"; } // src out << ", R" << vtx.word0.SRC_GPR(); if (vtx.word0.SRC_REL() == SQ_REL::REL) { out << "[AL]"; } out << '.' << disassembleDestMask(vtx.word0.SRC_SEL_X()); out << ", b" << vtx.word0.BUFFER_ID(); // fetch_type if (vtx.word0.FETCH_TYPE() == SQ_VTX_FETCH_TYPE::VERTEX_DATA) { out << " VERTEX_DATA"; } else if (vtx.word0.FETCH_TYPE() == SQ_VTX_FETCH_TYPE::INSTANCE_DATA) { out << " INSTANCE_DATA"; } else if(vtx.word0.FETCH_TYPE() == SQ_VTX_FETCH_TYPE::NO_INDEX_OFFSET) { out << " NO_INDEX_OFFSET"; } else { out << " FETCH_TYPE(" << vtx.word0.FETCH_TYPE() << ")"; } // format if (!vtx.word1.USE_CONST_FIELDS()) { out << " FORMAT("; out << " " << vtx.word1.DATA_FORMAT(); if (vtx.word1.NUM_FORMAT_ALL() == 0) { out << " NORM"; } else if (vtx.word1.NUM_FORMAT_ALL() == 1) { out << " INT"; } else if (vtx.word1.NUM_FORMAT_ALL() == 2) { out << " SCALED"; } else { out << vtx.word1.NUM_FORMAT_ALL(); } if (vtx.word1.FORMAT_COMP_ALL()) { out << " SIGNED"; } else { out << " UNSIGNED"; } out << " " << vtx.word1.SRF_MODE_ALL(); out << ")"; } else { out << " FMT_FROM_FETCH_CONSTANT"; } if (vtx.word2.MEGA_FETCH()) { out << " MEGA(" << (vtx.word0.MEGA_FETCH_COUNT() + 1) << ")"; } else { out << " MINI(" << (vtx.word0.MEGA_FETCH_COUNT() + 1) << ")"; } out << " OFFSET(" << vtx.word2.OFFSET() << ")"; if (vtx.word0.FETCH_WHOLE_QUAD()) { out << " WHOLE_QUAD"; } if (vtx.word2.ENDIAN_SWAP() == SQ_ENDIAN::SWAP_8IN16) { out << " ENDIAN_SWAP(8IN16)"; } else if (vtx.word2.ENDIAN_SWAP() == SQ_ENDIAN::SWAP_8IN32) { out << " ENDIAN_SWAP(8IN32)"; } else if (vtx.word2.ENDIAN_SWAP() != SQ_ENDIAN::NONE) { out << " ENDIAN_SWAP(" << vtx.word2.ENDIAN_SWAP() << ")"; } if (vtx.word2.CONST_BUF_NO_STRIDE()) { out << " CONST_BUF_NO_STRIDE"; } if (vtx.word2.ALT_CONST()) { out << " ALT_CONST"; } }