mirror of
https://github.com/open-goal/jak-project
synced 2026-08-13 20:25:20 -04:00
[graphics] MERC (#1124)
* merc dma generation * wip very early version of merc * small fixes * fix * fix more merc bugs * derp fixed min * oops we did need something weird on max * merc for everything * program optimization 1 * more optimization * windows * windows 2 * clean up * fix test * BLERC
This commit is contained in:
@@ -575,8 +575,286 @@ char bc_to_part(int x) {
|
||||
return '?';
|
||||
}
|
||||
}
|
||||
|
||||
std::string mask_to_string(u8 val) {
|
||||
std::string result;
|
||||
if (val & 8) {
|
||||
result += 'x';
|
||||
}
|
||||
if (val & 4) {
|
||||
result += 'y';
|
||||
}
|
||||
if (val & 2) {
|
||||
result += 'z';
|
||||
}
|
||||
if (val & 1) {
|
||||
result += 'w';
|
||||
}
|
||||
return result;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
int unk = 0;
|
||||
std::string VuDisassembler::to_cpp(const VuInstruction& instr) const {
|
||||
switch (instr.kind) {
|
||||
case VuInstrK::NOP:
|
||||
case VuInstrK::LOWER_NOP:
|
||||
return "/* nop */";
|
||||
case VuInstrK::LQ:
|
||||
if (instr.src.at(1).is_int_reg(0)) {
|
||||
return fmt::format("lq_buffer(Mask::{}, vu.{}, {});", mask_to_string(*instr.mask),
|
||||
instr.dst->to_string(m_label_names),
|
||||
instr.src.at(0).to_string(m_label_names));
|
||||
} else if (instr.src.at(0).value() == 0) {
|
||||
return fmt::format("lq_buffer(Mask::{}, vu.{}, vu.{});", mask_to_string(*instr.mask),
|
||||
instr.dst->to_string(m_label_names),
|
||||
instr.src.at(1).to_string(m_label_names));
|
||||
} else {
|
||||
return fmt::format("lq_buffer(Mask::{}, vu.{}, vu.{} + {});", mask_to_string(*instr.mask),
|
||||
instr.dst->to_string(m_label_names),
|
||||
instr.src.at(1).to_string(m_label_names),
|
||||
instr.src.at(0).to_string(m_label_names));
|
||||
}
|
||||
goto unknown;
|
||||
case VuInstrK::LQI:
|
||||
return fmt::format("lq_buffer(Mask::{}, vu.{}, vu.{}++);", mask_to_string(*instr.mask),
|
||||
instr.dst->to_string(m_label_names),
|
||||
instr.src.at(0).to_string(m_label_names));
|
||||
case VuInstrK::SQI:
|
||||
return fmt::format("sq_buffer(Mask::{}, vu.{}, vu.{}++);", mask_to_string(*instr.mask),
|
||||
instr.dst->to_string(m_label_names),
|
||||
instr.src.at(0).to_string(m_label_names));
|
||||
case VuInstrK::SQ:
|
||||
if (instr.src.at(1).is_int_reg(0)) {
|
||||
return fmt::format("sq_buffer(Mask::{}, vu.{}, {});", mask_to_string(*instr.mask),
|
||||
instr.dst->to_string(m_label_names),
|
||||
instr.src.at(0).to_string(m_label_names));
|
||||
} else if (instr.src.at(0).value() == 0) {
|
||||
return fmt::format("sq_buffer(Mask::{}, vu.{}, vu.{});", mask_to_string(*instr.mask),
|
||||
instr.dst->to_string(m_label_names),
|
||||
instr.src.at(1).to_string(m_label_names));
|
||||
} else {
|
||||
return fmt::format("sq_buffer(Mask::{}, vu.{}, vu.{} + {});", mask_to_string(*instr.mask),
|
||||
instr.dst->to_string(m_label_names),
|
||||
instr.src.at(1).to_string(m_label_names),
|
||||
instr.src.at(0).to_string(m_label_names));
|
||||
}
|
||||
goto unknown;
|
||||
case VuInstrK::IADDI:
|
||||
if (instr.src.at(0).is_int_reg(0)) {
|
||||
return fmt::format("vu.{} = {};", instr.dst->to_string(m_label_names),
|
||||
(s16)instr.src.at(1).value());
|
||||
} else {
|
||||
return fmt::format("vu.{} = vu.{} + {};", instr.dst->to_string(m_label_names),
|
||||
instr.src.at(0).to_string(m_label_names), (s16)instr.src.at(1).value());
|
||||
}
|
||||
|
||||
case VuInstrK::IADDIU:
|
||||
if (instr.src.at(0).is_int_reg(0)) {
|
||||
return fmt::format("vu.{} = 0x{:x}; /* {} */\n", instr.dst->to_string(m_label_names),
|
||||
(u16)instr.src.at(1).value(), instr.src.at(1).value());
|
||||
} else {
|
||||
return fmt::format("vu.{} = vu.{} + 0x{:x}; /* {} */", instr.dst->to_string(m_label_names),
|
||||
instr.src.at(0).to_string(m_label_names), (u16)instr.src.at(1).value(),
|
||||
instr.src.at(1).value());
|
||||
}
|
||||
|
||||
case VuInstrK::ISW:
|
||||
if (instr.src.at(2).is_int_reg(0)) {
|
||||
return fmt::format("isw_buffer(Mask::{}, vu.{}, {});", mask_to_string(*instr.mask),
|
||||
instr.src.at(0).to_string(m_label_names),
|
||||
instr.src.at(1).to_string(m_label_names));
|
||||
} else {
|
||||
return fmt::format("isw_buffer(Mask::{}, vu.{}, vu.{} + {});", mask_to_string(*instr.mask),
|
||||
instr.src.at(0).to_string(m_label_names),
|
||||
instr.src.at(2).to_string(m_label_names),
|
||||
instr.src.at(1).to_string(m_label_names));
|
||||
}
|
||||
goto unknown;
|
||||
|
||||
case VuInstrK::ISWR:
|
||||
return fmt::format("isw_buffer(Mask::{}, vu.{}, vu.{});", mask_to_string(*instr.mask),
|
||||
instr.src.at(0).to_string(m_label_names),
|
||||
instr.src.at(1).to_string(m_label_names));
|
||||
|
||||
case VuInstrK::ILWR:
|
||||
return fmt::format("ilw_buffer(Mask::{}, vu.{}, vu.{});", mask_to_string(*instr.mask),
|
||||
instr.dst->to_string(m_label_names),
|
||||
instr.src.at(0).to_string(m_label_names));
|
||||
|
||||
case VuInstrK::ILW:
|
||||
if (instr.src.at(1).is_int_reg(0)) {
|
||||
return fmt::format("ilw_buffer(Mask::{}, vu.{}, {});", mask_to_string(*instr.mask),
|
||||
instr.dst->to_string(m_label_names),
|
||||
instr.src.at(0).to_string(m_label_names));
|
||||
} else {
|
||||
return fmt::format("ilw_buffer(Mask::{}, vu.{}, vu.{} + {});", mask_to_string(*instr.mask),
|
||||
instr.dst->to_string(m_label_names),
|
||||
instr.src.at(1).to_string(m_label_names),
|
||||
instr.src.at(0).to_string(m_label_names));
|
||||
}
|
||||
|
||||
case VuInstrK::ISUBIU:
|
||||
if (instr.src.at(0).is_int_reg(0)) {
|
||||
return fmt::format("vu.{} = -{};\n", instr.dst->to_string(m_label_names),
|
||||
instr.src.at(1).value());
|
||||
} else {
|
||||
return fmt::format("vu.{} = vu.{} - 0x{:x}; /* {} */", instr.dst->to_string(m_label_names),
|
||||
instr.src.at(0).to_string(m_label_names), (u16)instr.src.at(1).value(),
|
||||
instr.src.at(1).value());
|
||||
}
|
||||
|
||||
case VuInstrK::MTIR:
|
||||
return fmt::format("vu.{} = vu.{}.{}_as_u16();", instr.dst->to_string(m_label_names),
|
||||
instr.src.at(0).to_string(m_label_names),
|
||||
bc_to_part(*instr.first_src_field));
|
||||
|
||||
case VuInstrK::MFIR:
|
||||
return fmt::format("vu.{}.mfir(Mask::{}, vu.{});", instr.dst->to_string(m_label_names),
|
||||
mask_to_string(*instr.mask), instr.src.at(0).to_string(m_label_names));
|
||||
|
||||
case VuInstrK::B:
|
||||
return fmt::format("bc = true;", instr.src.at(0).to_string(m_label_names));
|
||||
|
||||
case VuInstrK::IBGTZ:
|
||||
return fmt::format("bc = ((s16)vu.{}) > 0;", instr.src.at(0).to_string(m_label_names));
|
||||
|
||||
case VuInstrK::IBGEZ:
|
||||
return fmt::format("bc = ((s16)vu.{}) >= 0;", instr.src.at(0).to_string(m_label_names));
|
||||
|
||||
case VuInstrK::IBEQ:
|
||||
if (instr.src.at(0).is_int_reg(0)) {
|
||||
return fmt::format("bc = (vu.{} == 0);", instr.src.at(1).to_string(m_label_names));
|
||||
} else {
|
||||
return fmt::format("bc = (vu.{} == vu.{});", instr.src.at(0).to_string(m_label_names),
|
||||
instr.src.at(1).to_string(m_label_names));
|
||||
}
|
||||
|
||||
case VuInstrK::IBNE:
|
||||
if (instr.src.at(0).is_int_reg(0)) {
|
||||
return fmt::format("bc = (vu.{} != 0);", instr.src.at(1).to_string(m_label_names));
|
||||
} else {
|
||||
return fmt::format("bc = (vu.{} != vu.{});", instr.src.at(0).to_string(m_label_names),
|
||||
instr.src.at(1).to_string(m_label_names));
|
||||
}
|
||||
|
||||
case VuInstrK::IADD:
|
||||
return fmt::format("vu.{} = vu.{} + vu.{};", instr.dst->to_string(m_label_names),
|
||||
instr.src.at(0).to_string(m_label_names),
|
||||
instr.src.at(1).to_string(m_label_names));
|
||||
case VuInstrK::ISUB:
|
||||
return fmt::format("vu.{} = vu.{} - vu.{};", instr.dst->to_string(m_label_names),
|
||||
instr.src.at(0).to_string(m_label_names),
|
||||
instr.src.at(1).to_string(m_label_names));
|
||||
case VuInstrK::IAND:
|
||||
return fmt::format("vu.{} = vu.{} & vu.{};", instr.dst->to_string(m_label_names),
|
||||
instr.src.at(0).to_string(m_label_names),
|
||||
instr.src.at(1).to_string(m_label_names));
|
||||
case VuInstrK::IOR:
|
||||
if (instr.src.at(1).is_int_reg(0)) {
|
||||
return fmt::format("vu.{} = vu.{};", instr.dst->to_string(m_label_names),
|
||||
instr.src.at(0).to_string(m_label_names));
|
||||
} else {
|
||||
goto unknown;
|
||||
}
|
||||
|
||||
case VuInstrK::MFP:
|
||||
return fmt::format("vu.{}.mfp(Mask::{}, vu.P);", instr.dst->to_string(m_label_names),
|
||||
mask_to_string(*instr.mask));
|
||||
|
||||
case VuInstrK::MULq:
|
||||
return fmt::format("vu.{}.mul(Mask::{}, vu.{}, vu.Q); /* TODO mulq */",
|
||||
instr.dst->to_string(m_label_names), mask_to_string(*instr.mask),
|
||||
instr.src.at(0).to_string(m_label_names));
|
||||
case VuInstrK::DIV:
|
||||
return fmt::format(
|
||||
"vu.Q = vu.{}.{}() / vu.{}.{}();\n", instr.src.at(0).to_string(m_label_names),
|
||||
bc_to_part(*instr.first_src_field), instr.src.at(1).to_string(m_label_names),
|
||||
bc_to_part(*instr.second_src_field));
|
||||
|
||||
case VuInstrK::ERLENG:
|
||||
return fmt::format("vu.P = erleng(Mask::{}, vu.{}); /* TODO erleng */",
|
||||
mask_to_string(*instr.mask), instr.src.at(0).to_string(m_label_names));
|
||||
|
||||
case VuInstrK::MR32:
|
||||
case VuInstrK::MOVE:
|
||||
case VuInstrK::ITOF0:
|
||||
case VuInstrK::ITOF15:
|
||||
case VuInstrK::FTOI0:
|
||||
case VuInstrK::FTOI4:
|
||||
return fmt::format("vu.{}.{}(Mask::{}, vu.{});", instr.dst->to_string(m_label_names),
|
||||
info(instr.kind).name, mask_to_string(*instr.mask),
|
||||
instr.src.at(0).to_string(m_label_names));
|
||||
|
||||
case VuInstrK::ADDbc:
|
||||
case VuInstrK::MULbc:
|
||||
case VuInstrK::MINIbc:
|
||||
case VuInstrK::MAXbc:
|
||||
return fmt::format("vu.{}.{}(Mask::{}, vu.{}, vu.{}.{}());",
|
||||
instr.dst->to_string(m_label_names), info(instr.kind).name,
|
||||
mask_to_string(*instr.mask), instr.src.at(0).to_string(m_label_names),
|
||||
instr.src.at(1).to_string(m_label_names), bc_to_part(*instr.bc));
|
||||
|
||||
case VuInstrK::FP_CONSTANT:
|
||||
return fmt::format("vu.I = {};", float_to_string(instr.fp));
|
||||
|
||||
case VuInstrK::MINIi:
|
||||
case VuInstrK::MAXi:
|
||||
return fmt::format("vu.{}.{}(Mask::{}, vu.{}, vu.I);", instr.dst->to_string(m_label_names),
|
||||
info(instr.kind).name, mask_to_string(*instr.mask),
|
||||
instr.src.at(0).to_string(m_label_names));
|
||||
|
||||
case VuInstrK::MUL:
|
||||
case VuInstrK::ADD:
|
||||
case VuInstrK::MAX:
|
||||
case VuInstrK::MINI:
|
||||
return fmt::format("vu.{}.{}(Mask::{}, vu.{}, vu.{});", instr.dst->to_string(m_label_names),
|
||||
info(instr.kind).name, mask_to_string(*instr.mask),
|
||||
instr.src.at(0).to_string(m_label_names),
|
||||
instr.src.at(1).to_string(m_label_names));
|
||||
|
||||
case VuInstrK::ADDAbc:
|
||||
return fmt::format("vu.acc.adda(Mask::{}, vu.{}, vu.{}.{}());", mask_to_string(*instr.mask),
|
||||
instr.dst->to_string(m_label_names),
|
||||
instr.src.at(0).to_string(m_label_names), bc_to_part(*instr.bc));
|
||||
case VuInstrK::MADDA:
|
||||
return fmt::format("vu.acc.madda(Mask::{}, vu.{}, vu.{});", mask_to_string(*instr.mask),
|
||||
instr.src.at(0).to_string(m_label_names),
|
||||
instr.src.at(1).to_string(m_label_names));
|
||||
case VuInstrK::MADDAbc:
|
||||
return fmt::format("vu.acc.madda(Mask::{}, vu.{}, vu.{}.{}());", mask_to_string(*instr.mask),
|
||||
instr.src.at(0).to_string(m_label_names),
|
||||
instr.src.at(1).to_string(m_label_names), bc_to_part(*instr.bc));
|
||||
case VuInstrK::MADDbc:
|
||||
return fmt::format("vu.acc.madd(Mask::{}, vu.{}, vu.{}, vu.{}.{}());",
|
||||
mask_to_string(*instr.mask), instr.dst->to_string(m_label_names),
|
||||
instr.src.at(0).to_string(m_label_names),
|
||||
instr.src.at(1).to_string(m_label_names), bc_to_part(*instr.bc));
|
||||
case VuInstrK::MULA:
|
||||
return fmt::format("vu.acc.mula(Mask::{}, vu.{}, vu.{});", mask_to_string(*instr.mask),
|
||||
instr.src.at(0).to_string(m_label_names),
|
||||
instr.src.at(1).to_string(m_label_names));
|
||||
case VuInstrK::MULAbc:
|
||||
return fmt::format("vu.acc.mula(Mask::{}, vu.{}, vu.{}.{}());", mask_to_string(*instr.mask),
|
||||
instr.src.at(0).to_string(m_label_names),
|
||||
instr.src.at(1).to_string(m_label_names), bc_to_part(*instr.bc));
|
||||
|
||||
case VuInstrK::XGKICK:
|
||||
return fmt::format("xgkick(vu.{});", instr.src.at(0).to_string(m_label_names));
|
||||
|
||||
case VuInstrK::XTOP:
|
||||
return fmt::format("vu.{} = xtop();", instr.src.at(0).to_string(m_label_names));
|
||||
default:
|
||||
unk++;
|
||||
return "???";
|
||||
}
|
||||
|
||||
unknown:
|
||||
|
||||
unk++;
|
||||
return "???";
|
||||
}
|
||||
|
||||
std::string VuDisassembler::to_string(const VuInstruction& instr) const {
|
||||
if (instr.kind == VuInstrK::FP_CONSTANT) {
|
||||
return float_to_string(instr.fp);
|
||||
@@ -719,6 +997,11 @@ int VuDisassembler::add_label(int instr) {
|
||||
}
|
||||
}
|
||||
|
||||
void VuDisassembler::add_label_with_name(int instr, const std::string& name) {
|
||||
add_label(instr);
|
||||
m_user_named_instructions[instr] = name;
|
||||
}
|
||||
|
||||
void VuDisassembler::name_labels() {
|
||||
std::vector<int> instrs_with_labels;
|
||||
for (auto [instr, label_idx] : m_labels) {
|
||||
@@ -732,5 +1015,91 @@ void VuDisassembler::name_labels() {
|
||||
auto label_idx = m_labels.at(instr);
|
||||
m_label_names.at(label_idx) = fmt::format("L{}", idx++);
|
||||
}
|
||||
|
||||
for (auto& kv : m_user_named_instructions) {
|
||||
m_label_names.at(m_labels.at(kv.first)) = kv.second;
|
||||
}
|
||||
}
|
||||
|
||||
bool has_branch_delay(const VuInstructionPair& pair) {
|
||||
switch (pair.lower.kind) {
|
||||
case VuInstrK::JALR:
|
||||
// case VuInstrK::JR: it does, but it's not supported
|
||||
case VuInstrK::IBGTZ:
|
||||
case VuInstrK::IBNE:
|
||||
case VuInstrK::IBEQ:
|
||||
case VuInstrK::B:
|
||||
case VuInstrK::BAL:
|
||||
case VuInstrK::IBLTZ:
|
||||
case VuInstrK::IBLEZ:
|
||||
case VuInstrK::IBGEZ:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
std::string get_label_name(const VuInstructionPair& pair,
|
||||
const std::vector<std::string>& label_names) {
|
||||
for (auto& arg : pair.lower.src) {
|
||||
if (arg.kind() == VuInstructionAtom::Kind::LABEL) {
|
||||
return arg.to_string(label_names);
|
||||
}
|
||||
}
|
||||
assert(false);
|
||||
}
|
||||
|
||||
std::string VuDisassembler::to_string_with_cpp(const VuProgram& prog) const {
|
||||
std::string result;
|
||||
for (int i = 0; i < (int)prog.instructions().size(); i++) {
|
||||
auto lab = m_labels.find(i);
|
||||
if (lab != m_labels.end()) {
|
||||
result += m_label_names.at(lab->second);
|
||||
result += ':';
|
||||
result += '\n';
|
||||
}
|
||||
auto& pair = prog.instructions().at(i);
|
||||
if (has_branch_delay(pair)) {
|
||||
result += "// BRANCH!\n";
|
||||
// set bc
|
||||
result += to_string_with_cpp(prog.instructions().at(i));
|
||||
result += "\n";
|
||||
result += to_string_with_cpp(prog.instructions().at(i + 1));
|
||||
result += "\n";
|
||||
result += fmt::format("if (bc) {{ goto {}; }}\n", get_label_name(pair, m_label_names));
|
||||
result += "\n\n";
|
||||
i++;
|
||||
} else {
|
||||
result += to_string_with_cpp(prog.instructions().at(i));
|
||||
result += '\n';
|
||||
}
|
||||
}
|
||||
fmt::print("TOTAL unk: {}\n", unk);
|
||||
return result;
|
||||
}
|
||||
|
||||
namespace {
|
||||
bool is_nop(const VuInstruction& i) {
|
||||
return i.kind == VuInstrK::NOP || i.kind == VuInstrK::LOWER_NOP;
|
||||
}
|
||||
} // namespace
|
||||
std::string VuDisassembler::to_string_with_cpp(const VuInstructionPair& pair) const {
|
||||
std::string result;
|
||||
result += fmt::format(" // {:25s} | {:30s}\n", to_string(pair.lower), to_string(pair.upper));
|
||||
|
||||
if (!is_nop(pair.lower) && !is_nop(pair.upper) && pair.lower.kind == VuInstrK::FP_CONSTANT) {
|
||||
result += fmt::format(" {:25s}", to_cpp(pair.upper));
|
||||
result += fmt::format(" {:25s}", to_cpp(pair.lower));
|
||||
} else {
|
||||
if (!is_nop(pair.upper)) {
|
||||
result += fmt::format(" {:25s}", to_cpp(pair.upper));
|
||||
}
|
||||
|
||||
if (!is_nop(pair.lower)) {
|
||||
result += fmt::format(" {:25s}", to_cpp(pair.lower));
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
} // namespace decompiler
|
||||
|
||||
Reference in New Issue
Block a user