2021-05-23 16:59:09 -04:00
|
|
|
#pragma once
|
|
|
|
|
|
|
|
#include <unordered_map>
|
|
|
|
|
2022-02-08 19:02:47 -05:00
|
|
|
#include "common/util/Assert.h"
|
2021-05-23 16:59:09 -04:00
|
|
|
|
2022-06-22 23:37:46 -04:00
|
|
|
#include "decompiler/VuDisasm/VuProgram.h"
|
|
|
|
|
2021-05-23 16:59:09 -04:00
|
|
|
namespace decompiler {
|
|
|
|
|
|
|
|
struct VuDecodeStep {
|
|
|
|
enum class FieldK {
|
|
|
|
IEMDT,
|
|
|
|
DST_MASK,
|
|
|
|
FT,
|
|
|
|
FS,
|
|
|
|
FD,
|
|
|
|
BC,
|
|
|
|
IMM11_BRANCH,
|
|
|
|
IMM11_SIGNED,
|
|
|
|
IMM15_UNSIGNED,
|
|
|
|
IMM5_SIGNED,
|
|
|
|
IMM24_UNSIGNED,
|
|
|
|
FTF,
|
|
|
|
FSF,
|
|
|
|
NONE,
|
|
|
|
} field;
|
|
|
|
|
|
|
|
enum class AtomK {
|
|
|
|
IEMDT,
|
|
|
|
DST_MASK,
|
|
|
|
DST_VF,
|
|
|
|
DST_VI,
|
|
|
|
DST_ACC,
|
|
|
|
DST_Q,
|
|
|
|
DST_P,
|
|
|
|
SRC_P,
|
|
|
|
SRC_Q,
|
|
|
|
SRC_I,
|
|
|
|
SRC_VF,
|
|
|
|
SRC_VI,
|
|
|
|
SRC_IMM,
|
|
|
|
BC,
|
|
|
|
ASSERT_ZERO,
|
|
|
|
LOAD_STORE_OFFSET,
|
|
|
|
SECOND_SOURCE_FIELD,
|
|
|
|
FIRST_SOURCE_FIELD,
|
|
|
|
BRANCH_TARGET
|
|
|
|
} atom;
|
|
|
|
};
|
|
|
|
|
|
|
|
class VuDisassembler {
|
|
|
|
public:
|
2021-11-13 20:44:17 -05:00
|
|
|
enum VuKind {
|
|
|
|
VU0,
|
|
|
|
VU1,
|
|
|
|
};
|
|
|
|
VuDisassembler(VuKind kind);
|
2021-05-23 16:59:09 -04:00
|
|
|
VuProgram disassemble(void* data, int size_bytes, bool debug_print = false);
|
|
|
|
std::string to_string(const VuInstruction& instr) const;
|
2022-02-24 22:33:10 -05:00
|
|
|
std::string to_cpp(const VuInstruction& instr, bool mips2c_format) const;
|
2021-05-23 16:59:09 -04:00
|
|
|
std::string to_string(const VuInstructionPair& pair) const;
|
2022-02-24 22:33:10 -05:00
|
|
|
std::string to_string_with_cpp(const VuInstructionPair& pair, bool mips2c_format, int idx) const;
|
2021-05-23 16:59:09 -04:00
|
|
|
std::string to_string(const VuProgram& prog) const;
|
2022-02-24 22:33:10 -05:00
|
|
|
std::string to_string_with_cpp(const VuProgram& prog, bool mips2c_format) const;
|
2022-02-03 22:45:41 -05:00
|
|
|
int add_label(int instr);
|
|
|
|
void add_label_with_name(int instr, const std::string& name);
|
2021-05-23 16:59:09 -04:00
|
|
|
|
|
|
|
private:
|
2021-11-13 20:44:17 -05:00
|
|
|
VuKind m_kind;
|
2021-05-23 16:59:09 -04:00
|
|
|
VuInstrK upper_kind(u32 in);
|
|
|
|
VuInstrK lower_kind(u32 in);
|
|
|
|
VuInstruction decode(VuInstrK kind, u32 data, int instr_idx);
|
2021-11-13 20:44:17 -05:00
|
|
|
s32 get_instruction_index_mask();
|
2022-02-03 22:45:41 -05:00
|
|
|
std::unordered_map<int, std::string> m_user_named_instructions;
|
2021-05-23 16:59:09 -04:00
|
|
|
|
|
|
|
struct VuUpperOp6 {
|
|
|
|
bool goto_11 = false;
|
|
|
|
VuInstrK kind = VuInstrK::INVALID;
|
|
|
|
bool known = false;
|
|
|
|
void set_11() {
|
|
|
|
known = true;
|
|
|
|
goto_11 = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
void set(VuInstrK _kind) {
|
|
|
|
known = true;
|
|
|
|
kind = _kind;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
struct VuLowerOp6 {
|
|
|
|
bool goto_other = false;
|
|
|
|
VuInstrK kind = VuInstrK::INVALID;
|
|
|
|
bool known = false;
|
|
|
|
void set_other() {
|
|
|
|
known = true;
|
|
|
|
goto_other = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
void set(VuInstrK _kind) {
|
|
|
|
known = true;
|
|
|
|
kind = _kind;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
struct OpInfo {
|
|
|
|
OpInfo() = default;
|
|
|
|
OpInfo(const std::string& _name) : name(_name) {}
|
|
|
|
|
|
|
|
OpInfo& iemdt() { return step({VuDecodeStep::FieldK::IEMDT, VuDecodeStep::AtomK::IEMDT}); }
|
|
|
|
OpInfo& bc() { return step({VuDecodeStep::FieldK::BC, VuDecodeStep::AtomK::BC}); }
|
|
|
|
OpInfo& dst_vf_ft() { return step({VuDecodeStep::FieldK::FT, VuDecodeStep::AtomK::DST_VF}); }
|
|
|
|
OpInfo& src_vf_fs() { return step({VuDecodeStep::FieldK::FS, VuDecodeStep::AtomK::SRC_VF}); }
|
|
|
|
OpInfo& dss_fd_fs_ft() {
|
|
|
|
return step({VuDecodeStep::FieldK::FD, VuDecodeStep::AtomK::DST_VF})
|
|
|
|
.step({VuDecodeStep::FieldK::FS, VuDecodeStep::AtomK::SRC_VF})
|
|
|
|
.step({VuDecodeStep::FieldK::FT, VuDecodeStep::AtomK::SRC_VF});
|
|
|
|
}
|
|
|
|
|
|
|
|
OpInfo& dst_mask() {
|
|
|
|
return step({VuDecodeStep::FieldK::DST_MASK, VuDecodeStep::AtomK::DST_MASK});
|
|
|
|
}
|
|
|
|
|
|
|
|
OpInfo& dst_acc() { return step({VuDecodeStep::FieldK::NONE, VuDecodeStep::AtomK::DST_ACC}); }
|
|
|
|
OpInfo& dst_vfd() { return step({VuDecodeStep::FieldK::FD, VuDecodeStep::AtomK::DST_VF}); }
|
|
|
|
OpInfo& dst_q() { return step({VuDecodeStep::FieldK::NONE, VuDecodeStep::AtomK::DST_Q}); }
|
|
|
|
OpInfo& dst_p() { return step({VuDecodeStep::FieldK::NONE, VuDecodeStep::AtomK::DST_P}); }
|
|
|
|
OpInfo& src_q() { return step({VuDecodeStep::FieldK::NONE, VuDecodeStep::AtomK::SRC_Q}); }
|
|
|
|
OpInfo& src_i() { return step({VuDecodeStep::FieldK::NONE, VuDecodeStep::AtomK::SRC_I}); }
|
|
|
|
OpInfo& src_p() { return step({VuDecodeStep::FieldK::NONE, VuDecodeStep::AtomK::SRC_P}); }
|
|
|
|
|
|
|
|
OpInfo& step(VuDecodeStep x) {
|
|
|
|
decode.push_back(x);
|
|
|
|
return *this;
|
|
|
|
}
|
|
|
|
|
|
|
|
OpInfo& dst_mask_zero() {
|
|
|
|
return step({VuDecodeStep::FieldK::DST_MASK, VuDecodeStep::AtomK::ASSERT_ZERO});
|
|
|
|
}
|
|
|
|
|
|
|
|
OpInfo& vft_zero() {
|
|
|
|
return step({VuDecodeStep::FieldK::FT, VuDecodeStep::AtomK::ASSERT_ZERO});
|
|
|
|
}
|
|
|
|
|
|
|
|
OpInfo& vit_zero() { return vft_zero(); }
|
|
|
|
|
|
|
|
OpInfo& vis_zero() {
|
|
|
|
return step({VuDecodeStep::FieldK::FS, VuDecodeStep::AtomK::ASSERT_ZERO});
|
|
|
|
}
|
|
|
|
|
2022-03-12 12:58:04 -05:00
|
|
|
OpInfo& vfs_zero() {
|
|
|
|
return step({VuDecodeStep::FieldK::FS, VuDecodeStep::AtomK::ASSERT_ZERO});
|
|
|
|
}
|
|
|
|
|
2021-05-23 16:59:09 -04:00
|
|
|
OpInfo& ftf_zero() {
|
|
|
|
return step({VuDecodeStep::FieldK::FTF, VuDecodeStep::AtomK::ASSERT_ZERO});
|
|
|
|
}
|
|
|
|
|
|
|
|
OpInfo& fsf_zero() {
|
|
|
|
return step({VuDecodeStep::FieldK::FSF, VuDecodeStep::AtomK::ASSERT_ZERO});
|
|
|
|
}
|
|
|
|
|
|
|
|
OpInfo& src_vit() { return step({VuDecodeStep::FieldK::FT, VuDecodeStep::AtomK::SRC_VI}); }
|
|
|
|
|
|
|
|
OpInfo& src_vis() { return step({VuDecodeStep::FieldK::FS, VuDecodeStep::AtomK::SRC_VI}); }
|
|
|
|
|
|
|
|
OpInfo& src_vft() { return step({VuDecodeStep::FieldK::FT, VuDecodeStep::AtomK::SRC_VF}); }
|
|
|
|
|
|
|
|
OpInfo& src_vfs() { return step({VuDecodeStep::FieldK::FS, VuDecodeStep::AtomK::SRC_VF}); }
|
|
|
|
|
|
|
|
OpInfo& rel_branch11() {
|
|
|
|
return step({VuDecodeStep::FieldK::IMM11_BRANCH, VuDecodeStep::AtomK::BRANCH_TARGET});
|
|
|
|
}
|
|
|
|
|
|
|
|
OpInfo& imm15_unsigned() {
|
|
|
|
return step({VuDecodeStep::FieldK::IMM15_UNSIGNED, VuDecodeStep::AtomK::SRC_IMM});
|
|
|
|
}
|
|
|
|
|
|
|
|
OpInfo& dst_vft() { return step({VuDecodeStep::FieldK::FT, VuDecodeStep::AtomK::DST_VF}); }
|
|
|
|
|
|
|
|
OpInfo& dst_vfs() { return step({VuDecodeStep::FieldK::FS, VuDecodeStep::AtomK::DST_VF}); }
|
|
|
|
|
|
|
|
OpInfo& dst_vit() { return step({VuDecodeStep::FieldK::FT, VuDecodeStep::AtomK::DST_VI}); }
|
|
|
|
|
|
|
|
OpInfo& dst_vid() { return step({VuDecodeStep::FieldK::FD, VuDecodeStep::AtomK::DST_VI}); }
|
|
|
|
|
|
|
|
OpInfo& ftf_1() {
|
|
|
|
return step({VuDecodeStep::FieldK::FTF, VuDecodeStep::AtomK::SECOND_SOURCE_FIELD});
|
|
|
|
}
|
|
|
|
OpInfo& ftf_0() {
|
|
|
|
return step({VuDecodeStep::FieldK::FTF, VuDecodeStep::AtomK::FIRST_SOURCE_FIELD});
|
|
|
|
}
|
|
|
|
OpInfo& fsf_0() {
|
|
|
|
return step({VuDecodeStep::FieldK::FSF, VuDecodeStep::AtomK::FIRST_SOURCE_FIELD});
|
|
|
|
}
|
|
|
|
|
|
|
|
OpInfo& src_imm11_load_store() {
|
|
|
|
return step({VuDecodeStep::FieldK::IMM11_SIGNED, VuDecodeStep::AtomK::LOAD_STORE_OFFSET});
|
|
|
|
}
|
|
|
|
|
|
|
|
OpInfo& imm11_zero() {
|
|
|
|
return step({VuDecodeStep::FieldK::IMM11_SIGNED, VuDecodeStep::AtomK::ASSERT_ZERO});
|
|
|
|
}
|
|
|
|
|
|
|
|
OpInfo& src_imm15_unsigned() {
|
|
|
|
return step({VuDecodeStep::FieldK::IMM15_UNSIGNED, VuDecodeStep::AtomK::SRC_IMM});
|
|
|
|
}
|
|
|
|
|
|
|
|
OpInfo& src_imm24_unsigned() {
|
|
|
|
return step({VuDecodeStep::FieldK::IMM24_UNSIGNED, VuDecodeStep::AtomK::SRC_IMM});
|
|
|
|
}
|
|
|
|
|
|
|
|
OpInfo& src_imm5_signed() {
|
|
|
|
return step({VuDecodeStep::FieldK::IMM5_SIGNED, VuDecodeStep::AtomK::SRC_IMM});
|
|
|
|
}
|
|
|
|
|
|
|
|
OpInfo& imm15_zero() {
|
|
|
|
return step({VuDecodeStep::FieldK::IMM15_UNSIGNED, VuDecodeStep::AtomK::ASSERT_ZERO});
|
|
|
|
}
|
|
|
|
|
|
|
|
std::string name;
|
|
|
|
std::vector<VuDecodeStep> decode;
|
|
|
|
bool known = false;
|
|
|
|
};
|
|
|
|
|
|
|
|
const OpInfo& info(VuInstrK op) const {
|
2022-02-08 19:02:47 -05:00
|
|
|
ASSERT((int)op < (int)VuInstrK::INVALID);
|
2021-05-23 16:59:09 -04:00
|
|
|
return m_op_info[(int)op];
|
|
|
|
}
|
|
|
|
|
|
|
|
VuUpperOp6 m_upper_op6_table[64];
|
|
|
|
VuLowerOp6 m_lower_op6_table[64];
|
|
|
|
OpInfo m_op_info[(int)VuInstrK::INVALID];
|
|
|
|
|
|
|
|
std::unordered_map<int, int> m_labels;
|
|
|
|
std::vector<std::string> m_label_names;
|
|
|
|
|
|
|
|
void name_labels();
|
|
|
|
|
|
|
|
OpInfo& add_op(VuInstrK kind, const std::string& name);
|
|
|
|
};
|
|
|
|
|
|
|
|
} // namespace decompiler
|