#include "stdafx.h" #include "CgBinaryProgram.h" #include "Emu/System.h" #include "RSXVertexProgram.h" void CgBinaryDisasm::AddScaCodeDisasm(const std::string& code) { ensure((m_sca_opcode < 21)); m_arb_shader += rsx_vp_sca_op_names[m_sca_opcode] + code + " "; } void CgBinaryDisasm::AddVecCodeDisasm(const std::string& code) { ensure((m_vec_opcode < 26)); m_arb_shader += rsx_vp_vec_op_names[m_vec_opcode] + code + " "; } std::string CgBinaryDisasm::GetMaskDisasm(bool is_sca) const { std::string ret; ret.reserve(5); ret += '.'; if (is_sca) { if (d3.sca_writemask_x) ret += "x"; if (d3.sca_writemask_y) ret += "y"; if (d3.sca_writemask_z) ret += "z"; if (d3.sca_writemask_w) ret += "w"; } else { if (d3.vec_writemask_x) ret += "x"; if (d3.vec_writemask_y) ret += "y"; if (d3.vec_writemask_z) ret += "z"; if (d3.vec_writemask_w) ret += "w"; } return ret == "."sv || ret == ".xyzw"sv ? "" : (ret); } std::string CgBinaryDisasm::GetVecMaskDisasm() { return GetMaskDisasm(false); } std::string CgBinaryDisasm::GetScaMaskDisasm() { return GetMaskDisasm(true); } std::string CgBinaryDisasm::GetDSTDisasm(bool is_sca) { std::string ret; std::string mask = GetMaskDisasm(is_sca); static constexpr std::array output_names = { "out_diffuse_color", "out_specular_color", "out_back_diffuse_color", "out_back_specular_color", "out_fog", "out_point_size", "out_clip_distance[0]", "out_clip_distance[1]", "out_clip_distance[2]", "out_clip_distance[3]", "out_clip_distance[4]", "out_clip_distance[5]", "out_tc8", "out_tc9", "out_tc0", "out_tc1", "out_tc2", "out_tc3", "out_tc4", "out_tc5", "out_tc6", "out_tc7" }; switch ((is_sca && d3.sca_dst_tmp != 0x3f) ? 0x1f : d3.dst) { case 0x1f: ret += (is_sca ? fmt::format("R%d", d3.sca_dst_tmp) : fmt::format("R%d", d0.dst_tmp)) + mask; break; default: if (d3.dst < output_names.size()) { fmt::append(ret, "%s%s", output_names[d3.dst], mask); } else { rsx_log.error("dst index out of range: %u", d3.dst); fmt::append(ret, "(bad out index) o[%d]", d3.dst); } // Vertex Program supports double destinations, notably in MOV if (d0.dst_tmp != 0x3f) fmt::append(ret, " R%d%s", d0.dst_tmp, mask); break; } return ret; } std::string CgBinaryDisasm::GetSRCDisasm(const u32 n) { std::string ret; static constexpr std::array reg_table = { "in_pos", "in_weight", "in_normal", "in_diff_color", "in_spec_color", "in_fog", "in_point_size", "in_7", "in_tc0", "in_tc1", "in_tc2", "in_tc3", "in_tc4", "in_tc5", "in_tc6", "in_tc7" }; switch (src[n].reg_type) { case 1: //temp ret += 'R'; ret += std::to_string(src[n].tmp_src); break; case 2: //input if (d1.input_src < reg_table.size()) { fmt::append(ret, "%s", reg_table[d1.input_src]); } else { rsx_log.error("Bad input src num: %d", u32{ d1.input_src }); fmt::append(ret, "v[%d] # bad src", d1.input_src); } break; case 3: //const ret += std::string("c[" + (d3.index_const ? AddAddrRegDisasm() + " + " : "") + std::to_string(d1.const_src) + "]"); break; default: rsx_log.fatal("Bad src%u reg type: %d", n, u32{ src[n].reg_type }); break; } static constexpr std::string_view f = "xyzw"; std::string swizzle; swizzle.reserve(5); swizzle += '.'; swizzle += f[src[n].swz_x]; swizzle += f[src[n].swz_y]; swizzle += f[src[n].swz_z]; swizzle += f[src[n].swz_w]; if (swizzle == ".xxxx") swizzle = ".x"; else if (swizzle == ".yyyy") swizzle = ".y"; else if (swizzle == ".zzzz") swizzle = ".z"; else if (swizzle == ".wwww") swizzle = ".w"; if (swizzle != ".xyzw"sv) { ret += swizzle; } bool abs = false; switch (n) { case 0: abs = d0.src0_abs; break; case 1: abs = d0.src1_abs; break; case 2: abs = d0.src2_abs; break; } if (abs) ret = "|" + ret + "|"; if (src[n].neg) ret = "-" + ret; return ret; } void CgBinaryDisasm::SetDSTDisasm(bool is_sca, const std::string& value) { is_sca ? AddScaCodeDisasm() : AddVecCodeDisasm(); if (d0.cond == 0) return; enum { lt = 0x1, eq = 0x2, gt = 0x4 }; if (d0.staturate) { m_arb_shader.pop_back(); m_arb_shader += "_sat "; } std::string dest; if (d0.cond_update_enable_0 && d0.cond_update_enable_1) { m_arb_shader.pop_back(); m_arb_shader += "C "; dest = fmt::format("RC%s", GetMaskDisasm(is_sca).c_str()); } else if (d3.dst != 0x1f || (is_sca ? d3.sca_dst_tmp != 0x3f : d0.dst_tmp != 0x3f)) { dest = GetDSTDisasm(is_sca); } AddCodeCondDisasm(FormatDisasm(dest), value); } std::string CgBinaryDisasm::GetTexDisasm() { return fmt::format("TEX%d", 0); } std::string CgBinaryDisasm::FormatDisasm(const std::string& code) { const std::pair> repl_list[] = { { "$$", []() -> std::string { return "$"; } }, { "$0", [this]{ return GetSRCDisasm(0); } }, { "$1", [this]{ return GetSRCDisasm(1); } }, { "$2", [this]{ return GetSRCDisasm(2); } }, { "$s", [this]{ return GetSRCDisasm(2); } }, { "$am", [this]{ return AddAddrMaskDisasm(); } }, { "$a", [this]{ return AddAddrRegDisasm(); } }, { "$t", [this]{ return GetTexDisasm(); } }, { "$fa", [this]{ return std::to_string(GetAddrDisasm()); } }, { "$ifcond ", [this] { std::string cond = GetCondDisasm(); if (cond == "true") cond.clear(); return cond; } }, { "$cond", [this]{ return GetCondDisasm(); } }, }; return fmt::replace_all(code, repl_list); } std::string CgBinaryDisasm::GetCondDisasm() const { enum { lt = 0x1, eq = 0x2, gt = 0x4 }; if (d0.cond == 0) return "false"; if (d0.cond == (lt | gt | eq)) return "true"; static const char* cond_string_table[(lt | gt | eq) + 1] = { "ERROR", "LT", "EQ", "LE", "GT", "NE", "GE", "ERROR" }; static constexpr std::string_view f = "xyzw"; std::string swizzle; swizzle.reserve(5); swizzle += '.'; swizzle += f[d0.mask_x]; swizzle += f[d0.mask_y]; swizzle += f[d0.mask_z]; swizzle += f[d0.mask_w]; if (swizzle == ".xxxx") swizzle = ".x"; else if (swizzle == ".yyyy") swizzle = ".y"; else if (swizzle == ".zzzz") swizzle = ".z"; else if (swizzle == ".wwww") swizzle = ".w"; if (swizzle == ".xyzw"sv) { swizzle.clear(); } return fmt::format("(%s%s)", cond_string_table[d0.cond], swizzle.c_str()); } void CgBinaryDisasm::AddCodeCondDisasm(const std::string& dst, const std::string& src) { enum { lt = 0x1, eq = 0x2, gt = 0x4 }; if (!d0.cond_test_enable || d0.cond == (lt | gt | eq)) { AddCodeDisasm(dst + ", " + src + ";"); return; } if (d0.cond == 0) { AddCodeDisasm("# " + dst + ", " + src + ";"); return; } static const char* cond_string_table[(lt | gt | eq) + 1] = { "ERROR", "LT", "EQ", "LE", "GT", "NE", "GE", "ERROR" }; static constexpr std::string_view f = "xyzw"; std::string swizzle; swizzle.reserve(5); swizzle += '.'; swizzle += f[d0.mask_x]; swizzle += f[d0.mask_y]; swizzle += f[d0.mask_z]; swizzle += f[d0.mask_w]; if (swizzle == ".xxxx") swizzle = ".x"; else if (swizzle == ".yyyy") swizzle = ".y"; else if (swizzle == ".zzzz") swizzle = ".z"; else if (swizzle == ".wwww") swizzle = ".w"; if (swizzle == ".xyzw"sv) { swizzle.clear(); } std::string cond = fmt::format("%s%s", cond_string_table[d0.cond], swizzle.c_str()); AddCodeDisasm(dst + "(" + cond + ") " + ", " + src + ";"); } std::string CgBinaryDisasm::AddAddrMaskDisasm() const { static constexpr std::string_view f = "xyzw"; return std::string(".") + f[d0.addr_swz]; } std::string CgBinaryDisasm::AddAddrRegDisasm() { //static constexpr std::string_view f = "xyzw"; return fmt::format("A%d", d0.addr_reg_sel_1) + AddAddrMaskDisasm(); } u32 CgBinaryDisasm::GetAddrDisasm() const { return (d2.iaddrh << 3) | d3.iaddrl; } void CgBinaryDisasm::AddCodeDisasm(const std::string& code) { m_arb_shader += FormatDisasm(code) + "\n"; } void CgBinaryDisasm::SetDSTVecDisasm(const std::string& code) { SetDSTDisasm(false, code); } void CgBinaryDisasm::SetDSTScaDisasm(const std::string& code) { SetDSTDisasm(true, code); } void CgBinaryDisasm::TaskVP() { m_instr_count = 0; bool is_has_BRA = false; for (u32 i = 1; m_instr_count < m_max_instr_count; m_instr_count++) { if (is_has_BRA) { d3.HEX = m_data[i]; i += 4; } else { d1.HEX = m_data[i++]; m_sca_opcode = d1.sca_opcode; switch (d1.sca_opcode) { case 0x08: //BRA is_has_BRA = true; d3.HEX = m_data[++i]; i += 4; AddScaCodeDisasm("# WARNING"); break; case 0x09: //BRI d2.HEX = m_data[i++]; d3.HEX = m_data[i]; i += 2; AddScaCodeDisasm("$ifcond # WARNING"); break; default: d3.HEX = m_data[++i]; i += 2; break; } } if (d3.end) { m_instr_count++; if (i < m_data.size()) { rsx_log.error("Program end before buffer end."); } break; } } for (u32 i = 0; i < m_instr_count; ++i) { d0.HEX = m_data[i * 4 + 0]; d1.HEX = m_data[i * 4 + 1]; d2.HEX = m_data[i * 4 + 2]; d3.HEX = m_data[i * 4 + 3]; src[0].src0l = d2.src0l; src[0].src0h = d1.src0h; src[1].src1 = d2.src1; src[2].src2l = d3.src2l; src[2].src2h = d2.src2h; m_sca_opcode = d1.sca_opcode; switch (d1.sca_opcode) { case RSX_SCA_OPCODE_NOP: break; case RSX_SCA_OPCODE_MOV: SetDSTScaDisasm("$s"); break; case RSX_SCA_OPCODE_RCP: SetDSTScaDisasm("$s"); break; case RSX_SCA_OPCODE_RCC: SetDSTScaDisasm("$s"); break; case RSX_SCA_OPCODE_RSQ: SetDSTScaDisasm("$s"); break; case RSX_SCA_OPCODE_EXP: SetDSTScaDisasm("$s"); break; case RSX_SCA_OPCODE_LOG: SetDSTScaDisasm("$s"); break; case RSX_SCA_OPCODE_LIT: SetDSTScaDisasm("$s"); break; case RSX_SCA_OPCODE_BRA: AddScaCodeDisasm("BRA # WARNING"); break; case RSX_SCA_OPCODE_BRI: AddCodeDisasm("$ifcond # WARNING"); break; case RSX_SCA_OPCODE_CAL: AddCodeDisasm("$ifcond $f# WARNING"); break; case RSX_SCA_OPCODE_CLI: AddCodeDisasm("$ifcond $f # WARNING"); break; case RSX_SCA_OPCODE_RET: AddCodeDisasm("$ifcond # WARNING"); break; case RSX_SCA_OPCODE_LG2: SetDSTScaDisasm("$s"); break; case RSX_SCA_OPCODE_EX2: SetDSTScaDisasm("$s"); break; case RSX_SCA_OPCODE_SIN: SetDSTScaDisasm("$s"); break; case RSX_SCA_OPCODE_COS: SetDSTScaDisasm("$s"); break; case RSX_SCA_OPCODE_BRB: SetDSTScaDisasm("# WARNING Boolean constant"); break; case RSX_SCA_OPCODE_CLB: SetDSTScaDisasm("# WARNING Boolean constant"); break; case RSX_SCA_OPCODE_PSH: SetDSTScaDisasm(""); break; case RSX_SCA_OPCODE_POP: SetDSTScaDisasm(""); break; default: rsx_log.error("Unknown vp sca_opcode 0x%x", u32{ d1.sca_opcode }); break; } m_vec_opcode = d1.vec_opcode; switch (d1.vec_opcode) { case RSX_VEC_OPCODE_NOP: break; case RSX_VEC_OPCODE_MOV: SetDSTVecDisasm("$0"); break; case RSX_VEC_OPCODE_MUL: SetDSTVecDisasm("$0, $1"); break; case RSX_VEC_OPCODE_ADD: SetDSTVecDisasm("$0, $2"); break; case RSX_VEC_OPCODE_MAD: SetDSTVecDisasm("$0, $1, $2"); break; case RSX_VEC_OPCODE_DP3: SetDSTVecDisasm("$0, $1"); break; case RSX_VEC_OPCODE_DPH: SetDSTVecDisasm("$0, $1"); break; case RSX_VEC_OPCODE_DP4: SetDSTVecDisasm("$0, $1"); break; case RSX_VEC_OPCODE_DST: SetDSTVecDisasm("$0, $1"); break; case RSX_VEC_OPCODE_MIN: SetDSTVecDisasm("$0, $1"); break; case RSX_VEC_OPCODE_MAX: SetDSTVecDisasm("$0, $1"); break; case RSX_VEC_OPCODE_SLT: SetDSTVecDisasm("$0, $1"); break; case RSX_VEC_OPCODE_SGE: SetDSTVecDisasm("$0, $1"); break; case RSX_VEC_OPCODE_ARL: AddCodeDisasm("ARL, $a, $0"); break; case RSX_VEC_OPCODE_FRC: SetDSTVecDisasm("$0"); break; case RSX_VEC_OPCODE_FLR: SetDSTVecDisasm("$0"); break; case RSX_VEC_OPCODE_SEQ: SetDSTVecDisasm("$0, $1"); break; case RSX_VEC_OPCODE_SFL: SetDSTVecDisasm("$0"); break; case RSX_VEC_OPCODE_SGT: SetDSTVecDisasm("$0, $1"); break; case RSX_VEC_OPCODE_SLE: SetDSTVecDisasm("$0, $1"); break; case RSX_VEC_OPCODE_SNE: SetDSTVecDisasm("$0, $1"); break; case RSX_VEC_OPCODE_STR: SetDSTVecDisasm("$0"); break; case RSX_VEC_OPCODE_SSG: SetDSTVecDisasm("$0"); break; case RSX_VEC_OPCODE_TXL: SetDSTVecDisasm("$t, $0"); break; default: rsx_log.error("Unknown vp opcode 0x%x", u32{ d1.vec_opcode }); break; } } m_arb_shader += "END\n"; }