[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:
water111
2022-02-03 22:45:41 -05:00
committed by GitHub
parent 3d7228ec77
commit 2342b6fb0f
41 changed files with 8811 additions and 111 deletions
+369
View File
@@ -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