Initial implementation of binary operation table

This commit is contained in:
Mr-Wiseguy 2024-05-22 21:24:11 -04:00
parent ba4aede49c
commit 347a792f80

View file

@ -1,6 +1,8 @@
#include <vector>
#include <set>
#include <unordered_set>
#include <unordered_map>
#include <cassert>
#include "rabbitizer.hpp"
#include "fmt/format.h"
@ -18,6 +20,344 @@ std::string_view ctx_gpr_prefix(int reg) {
return "";
}
enum class UnaryOpType {
None,
ToS32,
ToU32,
ToS64,
ToU64,
NegateS32,
NegateS64,
Lui,
Mask5, // Mask to 5 bits
Mask6, // Mask to 5 bits
};
enum class BinaryOpType {
// Addition/subtraction
Add32,
Sub32,
Add64,
Sub64,
// Bitwise
And64,
Or64,
Nor64,
Xor64,
Sll32,
Sll64,
Srl32,
Srl64,
Sra32,
Sra64,
Less,
// Loads
LD,
LW,
LWU,
LH,
LHU,
LB,
LBU,
LDL,
LDR,
LWL,
LWR,
COUNT,
};
enum class Operand {
Rd, // GPR
Rs, // GPR
Rt, // GPR
Fd, // FPR
Fs, // FPR
Ft, // FPR
ImmU16, // 16-bit immediate, unsigned
ImmS16, // 16-bit immediate, signed
Sa, // Shift amount
Sa32, // Shift amount plus 32
Base = Rs, // Alias for Rs for loads
};
struct UnaryOp {
UnaryOpType operation;
Operand output;
Operand input;
};
struct BinaryOp {
// The type of binary operation this represents.
BinaryOpType type;
// Operation to apply to each operand before applying the binary operation to them.
UnaryOpType operand_operations[2];
// The output operand.
Operand output;
// The source of the input operands.
Operand operands[2];
};
const std::unordered_map<InstrId, UnaryOp> unary_ops {
{ InstrId::cpu_lui, { UnaryOpType::Lui, Operand::Rt, Operand::ImmU16 } },
};
const std::unordered_map<InstrId, BinaryOp> binary_ops {
// Addition/subtraction
{ InstrId::cpu_addu, { BinaryOpType::Add32, { UnaryOpType::None, UnaryOpType::None }, Operand::Rd, { Operand::Rs, Operand::Rt }} },
{ InstrId::cpu_add, { BinaryOpType::Add32, { UnaryOpType::None, UnaryOpType::None }, Operand::Rd, { Operand::Rs, Operand::Rt }} },
{ InstrId::cpu_negu, { BinaryOpType::Sub32, { UnaryOpType::None, UnaryOpType::None }, Operand::Rd, { Operand::Rs, Operand::Rt }} }, // pseudo op for subu
{ InstrId::cpu_subu, { BinaryOpType::Sub32, { UnaryOpType::None, UnaryOpType::None }, Operand::Rd, { Operand::Rs, Operand::Rt }} },
{ InstrId::cpu_sub, { BinaryOpType::Sub32, { UnaryOpType::None, UnaryOpType::None }, Operand::Rd, { Operand::Rs, Operand::Rt }} },
{ InstrId::cpu_daddu, { BinaryOpType::Add64, { UnaryOpType::None, UnaryOpType::None }, Operand::Rd, { Operand::Rs, Operand::Rt }} },
{ InstrId::cpu_dadd, { BinaryOpType::Add64, { UnaryOpType::None, UnaryOpType::None }, Operand::Rd, { Operand::Rs, Operand::Rt }} },
{ InstrId::cpu_dsubu, { BinaryOpType::Sub64, { UnaryOpType::None, UnaryOpType::None }, Operand::Rd, { Operand::Rs, Operand::Rt }} },
{ InstrId::cpu_dsub, { BinaryOpType::Sub64, { UnaryOpType::None, UnaryOpType::None }, Operand::Rd, { Operand::Rs, Operand::Rt }} },
// Addition/subtraction (immediate)
{ InstrId::cpu_addi, { BinaryOpType::Add32, { UnaryOpType::None, UnaryOpType::None }, Operand::Rt, { Operand::Rs, Operand::ImmS16 }} },
{ InstrId::cpu_addiu, { BinaryOpType::Add32, { UnaryOpType::None, UnaryOpType::None }, Operand::Rt, { Operand::Rs, Operand::ImmS16 }} },
{ InstrId::cpu_daddi, { BinaryOpType::Add64, { UnaryOpType::None, UnaryOpType::None }, Operand::Rt, { Operand::Rs, Operand::ImmS16 }} },
{ InstrId::cpu_daddiu, { BinaryOpType::Add64, { UnaryOpType::None, UnaryOpType::None }, Operand::Rt, { Operand::Rs, Operand::ImmS16 }} },
// Bitwise
{ InstrId::cpu_and, { BinaryOpType::And64, { UnaryOpType::None, UnaryOpType::None }, Operand::Rd, { Operand::Rs, Operand::Rt }} },
{ InstrId::cpu_or, { BinaryOpType::Or64, { UnaryOpType::None, UnaryOpType::None }, Operand::Rd, { Operand::Rs, Operand::Rt }} },
{ InstrId::cpu_nor, { BinaryOpType::Nor64, { UnaryOpType::None, UnaryOpType::None }, Operand::Rd, { Operand::Rs, Operand::Rt }} },
{ InstrId::cpu_xor, { BinaryOpType::Xor64, { UnaryOpType::None, UnaryOpType::None }, Operand::Rd, { Operand::Rs, Operand::Rt }} },
// Bitwise (immediate)
{ InstrId::cpu_andi, { BinaryOpType::And64, { UnaryOpType::None, UnaryOpType::None }, Operand::Rt, { Operand::Rs, Operand::ImmU16 }} },
{ InstrId::cpu_ori, { BinaryOpType::Or64, { UnaryOpType::None, UnaryOpType::None }, Operand::Rt, { Operand::Rs, Operand::ImmU16 }} },
{ InstrId::cpu_xori, { BinaryOpType::Xor64, { UnaryOpType::None, UnaryOpType::None }, Operand::Rt, { Operand::Rs, Operand::ImmU16 }} },
// Shifts
/* BUG Should mask after (change op to Sll32 and input op to ToU32) */
{ InstrId::cpu_sllv, { BinaryOpType::Sll64, { UnaryOpType::ToS32, UnaryOpType::Mask5 }, Operand::Rd, { Operand::Rt, Operand::Rs }} },
{ InstrId::cpu_dsllv, { BinaryOpType::Sll64, { UnaryOpType::None, UnaryOpType::Mask6 }, Operand::Rd, { Operand::Rt, Operand::Rs }} },
{ InstrId::cpu_srlv, { BinaryOpType::Srl32, { UnaryOpType::ToU32, UnaryOpType::Mask5 }, Operand::Rd, { Operand::Rt, Operand::Rs }} },
{ InstrId::cpu_dsrlv, { BinaryOpType::Srl64, { UnaryOpType::ToU64, UnaryOpType::Mask6 }, Operand::Rd, { Operand::Rt, Operand::Rs }} },
/* BUG Should mask after (change op to Sra32 and input op to ToS64) */
{ InstrId::cpu_srav, { BinaryOpType::Sra64, { UnaryOpType::ToS32, UnaryOpType::Mask5 }, Operand::Rd, { Operand::Rt, Operand::Rs }} },
{ InstrId::cpu_dsrav, { BinaryOpType::Sra64, { UnaryOpType::ToS64, UnaryOpType::Mask6 }, Operand::Rd, { Operand::Rt, Operand::Rs }} },
// Shifts (immediate)
/* BUG Should mask after (change op to Sll32 and input op to ToU32) */
{ InstrId::cpu_sll, { BinaryOpType::Sll64, { UnaryOpType::ToS32, UnaryOpType::None }, Operand::Rd, { Operand::Rt, Operand::Sa }} },
{ InstrId::cpu_dsll, { BinaryOpType::Sll64, { UnaryOpType::None, UnaryOpType::None }, Operand::Rd, { Operand::Rt, Operand::Sa }} },
{ InstrId::cpu_dsll32, { BinaryOpType::Sll64, { UnaryOpType::None, UnaryOpType::None }, Operand::Rd, { Operand::Rt, Operand::Sa32 }} },
{ InstrId::cpu_srl, { BinaryOpType::Srl32, { UnaryOpType::ToU32, UnaryOpType::None }, Operand::Rd, { Operand::Rt, Operand::Sa }} },
{ InstrId::cpu_dsrl, { BinaryOpType::Srl64, { UnaryOpType::ToU64, UnaryOpType::None }, Operand::Rd, { Operand::Rt, Operand::Sa }} },
{ InstrId::cpu_dsrl32, { BinaryOpType::Srl64, { UnaryOpType::ToU64, UnaryOpType::None }, Operand::Rd, { Operand::Rt, Operand::Sa32 }} },
/* BUG should cast after (change op to Sra32 and input op to ToS64) */
{ InstrId::cpu_sra, { BinaryOpType::Sra64, { UnaryOpType::ToS32, UnaryOpType::None }, Operand::Rd, { Operand::Rt, Operand::Sa }} },
{ InstrId::cpu_dsra, { BinaryOpType::Sra64, { UnaryOpType::ToS64, UnaryOpType::None }, Operand::Rd, { Operand::Rt, Operand::Sa }} },
{ InstrId::cpu_dsra32, { BinaryOpType::Sra64, { UnaryOpType::ToS64, UnaryOpType::None }, Operand::Rd, { Operand::Rt, Operand::Sa32 }} },
// Comparisons
{ InstrId::cpu_slt, { BinaryOpType::Less, { UnaryOpType::ToS64, UnaryOpType::ToS64 }, Operand::Rd, { Operand::Rs, Operand::Rt }} },
{ InstrId::cpu_sltu, { BinaryOpType::Less, { UnaryOpType::ToU64, UnaryOpType::ToU64 }, Operand::Rd, { Operand::Rs, Operand::Rt }} },
// Comparisons (immediate)
{ InstrId::cpu_slti, { BinaryOpType::Less, { UnaryOpType::ToS64, UnaryOpType::None }, Operand::Rt, { Operand::Rs, Operand::ImmS16 }} },
{ InstrId::cpu_sltiu, { BinaryOpType::Less, { UnaryOpType::ToU64, UnaryOpType::None }, Operand::Rt, { Operand::Rs, Operand::ImmS16 }} },
// Loads
{ InstrId::cpu_ld, { BinaryOpType::LD, { UnaryOpType::None, UnaryOpType::None }, Operand::Rt, { Operand::ImmS16, Operand::Base }} },
{ InstrId::cpu_lw, { BinaryOpType::LW, { UnaryOpType::None, UnaryOpType::None }, Operand::Rt, { Operand::ImmS16, Operand::Base }} },
{ InstrId::cpu_lwu, { BinaryOpType::LWU, { UnaryOpType::None, UnaryOpType::None }, Operand::Rt, { Operand::ImmS16, Operand::Base }} },
{ InstrId::cpu_lh, { BinaryOpType::LH, { UnaryOpType::None, UnaryOpType::None }, Operand::Rt, { Operand::ImmS16, Operand::Base }} },
{ InstrId::cpu_lhu, { BinaryOpType::LHU, { UnaryOpType::None, UnaryOpType::None }, Operand::Rt, { Operand::ImmS16, Operand::Base }} },
{ InstrId::cpu_lb, { BinaryOpType::LB, { UnaryOpType::None, UnaryOpType::None }, Operand::Rt, { Operand::ImmS16, Operand::Base }} },
{ InstrId::cpu_lbu, { BinaryOpType::LBU, { UnaryOpType::None, UnaryOpType::None }, Operand::Rt, { Operand::ImmS16, Operand::Base }} },
{ InstrId::cpu_ldl, { BinaryOpType::LDL, { UnaryOpType::None, UnaryOpType::None }, Operand::Rt, { Operand::ImmS16, Operand::Base }} },
{ InstrId::cpu_ldr, { BinaryOpType::LDR, { UnaryOpType::None, UnaryOpType::None }, Operand::Rt, { Operand::ImmS16, Operand::Base }} },
{ InstrId::cpu_lwl, { BinaryOpType::LWL, { UnaryOpType::None, UnaryOpType::None }, Operand::Rt, { Operand::ImmS16, Operand::Base }} },
{ InstrId::cpu_lwr, { BinaryOpType::LWR, { UnaryOpType::None, UnaryOpType::None }, Operand::Rt, { Operand::ImmS16, Operand::Base }} },
};
struct InstructionContext {
int rd;
int rs;
int rt;
int sa;
int fd;
int fs;
int ft;
int cop1_cs;
uint16_t imm16;
};
class CGenerator {
public:
CGenerator() = default;
void process_binary_op(std::ostream& output_file, const BinaryOp& op, const InstructionContext& ctx);
private:
void get_operand_string(Operand operand, UnaryOpType operation, const InstructionContext& context, std::string& operand_string);
void get_notation(BinaryOpType op_type, std::string& func_string, std::string& infix_string);
};
struct BinaryOpFields { std::string func_string; std::string infix_string; };
std::vector<BinaryOpFields> c_op_fields = []() {
std::vector<BinaryOpFields> ret{};
ret.resize(static_cast<size_t>(BinaryOpType::COUNT));
std::vector<char> ops_setup{};
ops_setup.resize(static_cast<size_t>(BinaryOpType::COUNT));
auto setup_op = [&ret, &ops_setup](BinaryOpType op_type, const std::string& func_string, const std::string& infix_string) {
size_t index = static_cast<size_t>(op_type);
// Prevent setting up an operation twice.
assert(ops_setup[index] == false, "Operation already setup!");
ops_setup[index] = true;
ret[index] = { func_string, infix_string };
};
setup_op(BinaryOpType::Add32, "ADD32", "");
setup_op(BinaryOpType::Sub32, "SUB32", "");
setup_op(BinaryOpType::Add64, "", "+");
setup_op(BinaryOpType::Sub64, "", "-");
setup_op(BinaryOpType::And64, "", "&");
setup_op(BinaryOpType::Or64, "", "|");
setup_op(BinaryOpType::Nor64, "~", "|");
setup_op(BinaryOpType::Xor64, "", "^");
setup_op(BinaryOpType::Sll32, "S32", "<<");
setup_op(BinaryOpType::Sll64, "", "<<");
setup_op(BinaryOpType::Srl32, "S32", ">>");
setup_op(BinaryOpType::Srl64, "", ">>");
setup_op(BinaryOpType::Sra32, "S32", ">>"); // Arithmetic aspect will be taken care of by unary op for first operand.
setup_op(BinaryOpType::Sra64, "", ">>"); // Arithmetic aspect will be taken care of by unary op for first operand.
setup_op(BinaryOpType::Less, "", "<");
setup_op(BinaryOpType::LD, "LD", "");
setup_op(BinaryOpType::LW, "MEM_W", "");
setup_op(BinaryOpType::LWU, "MEM_WU", "");
setup_op(BinaryOpType::LH, "MEM_H", "");
setup_op(BinaryOpType::LHU, "MEM_HU", "");
setup_op(BinaryOpType::LB, "MEM_B", "");
setup_op(BinaryOpType::LBU, "MEM_BU", "");
setup_op(BinaryOpType::LDL, "do_ldl", "");
setup_op(BinaryOpType::LDR, "do_ldr", "");
setup_op(BinaryOpType::LWL, "do_lwl", "");
setup_op(BinaryOpType::LWR, "do_lwr", "");
// Ensure every operation has been setup.
for (char is_set : ops_setup) {
assert(is_set, "Operation has not been setup!");
}
return ret;
}();
std::string gpr_to_string(int gpr_index) {
if (gpr_index == 0) {
return "0";
}
return fmt::format("ctx->r{}", gpr_index);
}
void CGenerator::get_operand_string(Operand operand, UnaryOpType operation, const InstructionContext& context, std::string& operand_string) {
switch (operand) {
case Operand::Rd:
operand_string = gpr_to_string(context.rd);
break;
case Operand::Rs:
operand_string = gpr_to_string(context.rs);
break;
case Operand::Rt:
operand_string = gpr_to_string(context.rt);
break;
case Operand::Fd:
assert(false);
break;
case Operand::Fs:
assert(false);
break;
case Operand::Ft:
assert(false);
break;
case Operand::ImmU16:
operand_string = fmt::format("{:#X}", context.imm16);
break;
case Operand::ImmS16:
operand_string = fmt::format("{:#X}", (int16_t)context.imm16);
break;
case Operand::Sa:
operand_string = std::to_string(context.sa);
break;
case Operand::Sa32:
operand_string = fmt::format("({} + 32)", context.sa);
break;
}
switch (operation) {
case UnaryOpType::None:
break;
case UnaryOpType::ToS32:
operand_string = "S32(" + operand_string + ")";
break;
case UnaryOpType::ToU32:
operand_string = "U32(" + operand_string + ")";
break;
case UnaryOpType::ToS64:
operand_string = "SIGNED(" + operand_string + ")";
break;
case UnaryOpType::ToU64:
// Nothing to do here, they're already U64
break;
case UnaryOpType::NegateS32:
assert(false);
break;
case UnaryOpType::NegateS64:
assert(false);
break;
case UnaryOpType::Lui:
assert(false);
// operand_string = "S32(" + operand_string + ")";
break;
case UnaryOpType::Mask5:
operand_string = "(" + operand_string + " & 31)";
break;
case UnaryOpType::Mask6:
operand_string = "(" + operand_string + " & 63)";
break;
}
}
void CGenerator::get_notation(BinaryOpType op_type, std::string& func_string, std::string& infix_string) {
func_string = c_op_fields[static_cast<size_t>(op_type)].func_string;
infix_string = c_op_fields[static_cast<size_t>(op_type)].infix_string;
}
void CGenerator::process_binary_op(std::ostream& output_file, const BinaryOp& op, const InstructionContext& ctx) {
// Thread local variables to prevent allocations when possible.
// TODO these thread locals probably don't actually help right now, so figure out a better way to prevent allocations.
thread_local std::string output{};
thread_local std::string input_a{};
thread_local std::string input_b{};
thread_local std::string func_string{};
thread_local std::string infix_string{};
bool is_infix;
get_operand_string(op.output, UnaryOpType::None, ctx, output);
get_operand_string(op.operands[0], op.operand_operations[0], ctx, input_a);
get_operand_string(op.operands[1], op.operand_operations[1], ctx, input_b);
get_notation(op.type, func_string, infix_string);
// Not strictly necessary, just here to have parity with the old recompiler output.
if (op.type == BinaryOpType::Less) {
fmt::print(output_file, "{} = {} {} {} ? 1 : 0;\n", output, input_a, infix_string, input_b);
}
// TODO encode these ops to avoid needing special handling.
else if (op.type == BinaryOpType::LWL || op.type == BinaryOpType::LWR || op.type == BinaryOpType::LDL || op.type == BinaryOpType::LDR) {
fmt::print(output_file, "{} = {}(rdram, {}, {}, {});\n", output, func_string, output, input_a, input_b);
}
else if (!func_string.empty() && !infix_string.empty()) {
fmt::print(output_file, "{} = {}({} {} {});\n", output, func_string, input_a, infix_string, input_b);
}
else if (!func_string.empty()) {
fmt::print(output_file, "{} = {}({}, {});\n", output, func_string, input_a, input_b);
}
else if (!infix_string.empty()) {
fmt::print(output_file, "{} = {} {} {};\n", output, input_a, infix_string, input_b);
}
else {
assert(false, "Binary operation must have either a function or infix!");
}
}
// Major TODO, this function grew very organically and needs to be cleaned up. Ideally, it'll get split up into some sort of lookup table grouped by similar instruction types.
bool process_instruction(const RecompPort::Context& context, const RecompPort::Config& config, const RecompPort::Function& func, const RecompPort::FunctionStats& stats, const std::unordered_set<uint32_t>& skipped_insns, size_t instr_index, const std::vector<rabbitizer::InstructionCpu>& instructions, std::ofstream& output_file, bool indent, bool emit_link_branch, int link_branch_index, size_t reloc_index, bool& needs_link_branch, bool& is_branch_likely, std::span<std::vector<uint32_t>> static_funcs_out) {
const auto& section = context.sections[func.section_index];
@ -320,6 +660,8 @@ bool process_instruction(const RecompPort::Context& context, const RecompPort::C
}
}
bool handled = true;
switch (instr.getUniqueId()) {
case InstrId::cpu_nop:
fmt::print(output_file, "\n");
@ -369,101 +711,6 @@ bool process_instruction(const RecompPort::Context& context, const RecompPort::C
print_line("gpr jr_addend_{:08X} = {}{}", cur_jtbl.jr_vram, ctx_gpr_prefix(cur_jtbl.addend_reg), cur_jtbl.addend_reg);
}
}
print_line("{}{} = ADD32({}{}, {}{})", ctx_gpr_prefix(rd), rd, ctx_gpr_prefix(rs), rs, ctx_gpr_prefix(rt), rt);
break;
case InstrId::cpu_daddu:
print_line("{}{} = {}{} + {}{}", ctx_gpr_prefix(rd), rd, ctx_gpr_prefix(rs), rs, ctx_gpr_prefix(rt), rt);
break;
case InstrId::cpu_negu: // pseudo instruction for subu x, 0, y
case InstrId::cpu_sub:
case InstrId::cpu_subu:
print_line("{}{} = SUB32({}{}, {}{})", ctx_gpr_prefix(rd), rd, ctx_gpr_prefix(rs), rs, ctx_gpr_prefix(rt), rt);
break;
case InstrId::cpu_addi:
case InstrId::cpu_addiu:
print_line("{}{} = ADD32({}{}, {})", ctx_gpr_prefix(rt), rt, ctx_gpr_prefix(rs), rs, signed_imm_string);
break;
case InstrId::cpu_daddi:
case InstrId::cpu_daddiu:
print_line("{}{} = {}{} + {}", ctx_gpr_prefix(rt), rt, ctx_gpr_prefix(rs), rs, signed_imm_string);
break;
case InstrId::cpu_and:
print_line("{}{} = {}{} & {}{}", ctx_gpr_prefix(rd), rd, ctx_gpr_prefix(rs), rs, ctx_gpr_prefix(rt), rt);
break;
case InstrId::cpu_andi:
print_line("{}{} = {}{} & {}", ctx_gpr_prefix(rt), rt, ctx_gpr_prefix(rs), rs, unsigned_imm_string);
break;
case InstrId::cpu_or:
print_line("{}{} = {}{} | {}{}", ctx_gpr_prefix(rd), rd, ctx_gpr_prefix(rs), rs, ctx_gpr_prefix(rt), rt);
break;
case InstrId::cpu_ori:
print_line("{}{} = {}{} | {}", ctx_gpr_prefix(rt), rt, ctx_gpr_prefix(rs), rs, unsigned_imm_string);
break;
case InstrId::cpu_nor:
print_line("{}{} = ~({}{} | {}{})", ctx_gpr_prefix(rd), rd, ctx_gpr_prefix(rs), rs, ctx_gpr_prefix(rt), rt);
break;
case InstrId::cpu_xor:
print_line("{}{} = {}{} ^ {}{}", ctx_gpr_prefix(rd), rd, ctx_gpr_prefix(rs), rs, ctx_gpr_prefix(rt), rt);
break;
case InstrId::cpu_xori:
print_line("{}{} = {}{} ^ {}", ctx_gpr_prefix(rt), rt, ctx_gpr_prefix(rs), rs, unsigned_imm_string);
break;
case InstrId::cpu_sll:
print_line("{}{} = S32({}{}) << {}", ctx_gpr_prefix(rd), rd, ctx_gpr_prefix(rt), rt, sa);
break;
case InstrId::cpu_dsll:
print_line("{}{} = {}{} << {}", ctx_gpr_prefix(rd), rd, ctx_gpr_prefix(rt), rt, sa);
break;
case InstrId::cpu_dsll32:
print_line("{}{} = ((gpr)({}{})) << ({} + 32)", ctx_gpr_prefix(rd), rd, ctx_gpr_prefix(rt), rt, sa);
break;
case InstrId::cpu_sllv:
print_line("{}{} = S32({}{}) << ({}{} & 31)", ctx_gpr_prefix(rd), rd, ctx_gpr_prefix(rt), rt, ctx_gpr_prefix(rs), rs);
break;
case InstrId::cpu_dsllv:
print_line("{}{} = {}{} << ({}{} & 63)", ctx_gpr_prefix(rd), rd, ctx_gpr_prefix(rt), rt, ctx_gpr_prefix(rs), rs);
break;
case InstrId::cpu_sra:
print_line("{}{} = S32({}{}) >> {}", ctx_gpr_prefix(rd), rd, ctx_gpr_prefix(rt), rt, sa);
break;
case InstrId::cpu_dsra:
print_line("{}{} = SIGNED({}{}) >> {}", ctx_gpr_prefix(rd), rd, ctx_gpr_prefix(rt), rt, sa);
break;
case InstrId::cpu_dsra32:
print_line("{}{} = SIGNED({}{}) >> ({} + 32)", ctx_gpr_prefix(rd), rd, ctx_gpr_prefix(rt), rt, sa);
break;
case InstrId::cpu_srav:
print_line("{}{} = S32({}{}) >> ({}{} & 31)", ctx_gpr_prefix(rd), rd, ctx_gpr_prefix(rt), rt, ctx_gpr_prefix(rs), rs);
break;
case InstrId::cpu_dsrav:
print_line("{}{} = SIGNED({}{}) >> ({}{} & 63)", ctx_gpr_prefix(rd), rd, ctx_gpr_prefix(rt), rt, ctx_gpr_prefix(rs), rs);
break;
case InstrId::cpu_srl:
print_line("{}{} = S32(U32({}{}) >> {})", ctx_gpr_prefix(rd), rd, ctx_gpr_prefix(rt), rt, sa);
break;
case InstrId::cpu_dsrl:
print_line("{}{} = {}{} >> {}", ctx_gpr_prefix(rd), rd, ctx_gpr_prefix(rt), rt, sa);
break;
case InstrId::cpu_dsrl32:
print_line("{}{} = ((gpr)({}{})) >> ({} + 32)", ctx_gpr_prefix(rd), rd, ctx_gpr_prefix(rt), rt, sa);
break;
case InstrId::cpu_srlv:
print_line("{}{} = S32(U32({}{}) >> ({}{} & 31))", ctx_gpr_prefix(rd), rd, ctx_gpr_prefix(rt), rt, ctx_gpr_prefix(rs), rs);
break;
case InstrId::cpu_dsrlv:
print_line("{}{} = {}{} >> ({}{} & 63))", ctx_gpr_prefix(rd), rd, ctx_gpr_prefix(rt), rt, ctx_gpr_prefix(rs), rs);
break;
case InstrId::cpu_slt:
print_line("{}{} = SIGNED({}{}) < SIGNED({}{}) ? 1 : 0", ctx_gpr_prefix(rd), rd, ctx_gpr_prefix(rs), rs, ctx_gpr_prefix(rt), rt);
break;
case InstrId::cpu_slti:
print_line("{}{} = SIGNED({}{}) < {} ? 1 : 0", ctx_gpr_prefix(rt), rt, ctx_gpr_prefix(rs), rs, signed_imm_string);
break;
case InstrId::cpu_sltu:
print_line("{}{} = {}{} < {}{} ? 1 : 0", ctx_gpr_prefix(rd), rd, ctx_gpr_prefix(rs), rs, ctx_gpr_prefix(rt), rt);
break;
case InstrId::cpu_sltiu:
print_line("{}{} = {}{} < {} ? 1 : 0", ctx_gpr_prefix(rt), rt, ctx_gpr_prefix(rs), rs, signed_imm_string);
break;
case InstrId::cpu_mult:
print_line("result = S64(S32({}{})) * S64(S32({}{})); lo = S32(result >> 0); hi = S32(result >> 32)", ctx_gpr_prefix(rs), rs, ctx_gpr_prefix(rt), rt);
@ -502,25 +749,6 @@ bool process_instruction(const RecompPort::Context& context, const RecompPort::C
case InstrId::cpu_mthi:
print_line("hi = {}{}", ctx_gpr_prefix(rs), rs);
break;
// Loads
case InstrId::cpu_ld:
print_line("{}{} = LD({}, {}{})", ctx_gpr_prefix(rt), rt, signed_imm_string, ctx_gpr_prefix(base), base);
break;
case InstrId::cpu_lw:
print_line("{}{} = MEM_W({}, {}{})", ctx_gpr_prefix(rt), rt, signed_imm_string, ctx_gpr_prefix(base), base);
break;
case InstrId::cpu_lh:
print_line("{}{} = MEM_H({}, {}{})", ctx_gpr_prefix(rt), rt, signed_imm_string, ctx_gpr_prefix(base), base);
break;
case InstrId::cpu_lb:
print_line("{}{} = MEM_B({}, {}{})", ctx_gpr_prefix(rt), rt, signed_imm_string, ctx_gpr_prefix(base), base);
break;
case InstrId::cpu_lhu:
print_line("{}{} = MEM_HU({}, {}{})", ctx_gpr_prefix(rt), rt, signed_imm_string, ctx_gpr_prefix(base), base);
break;
case InstrId::cpu_lbu:
print_line("{}{} = MEM_BU({}, {}{})", ctx_gpr_prefix(rt), rt, signed_imm_string, ctx_gpr_prefix(base), base);
break;
// Stores
case InstrId::cpu_sd:
print_line("SD({}{}, {}, {}{})", ctx_gpr_prefix(rt), rt, signed_imm_string, ctx_gpr_prefix(base), base);
@ -548,12 +776,6 @@ bool process_instruction(const RecompPort::Context& context, const RecompPort::C
// LWR x + 1 -> 00000000 012389AB
// LWR x + 2 -> 00000000 0189ABCD
// LWR x + 3 -> FFFFFFFF 89ABCDEF
case InstrId::cpu_lwl:
print_line("{}{} = do_lwl(rdram, {}{}, {}, {}{})", ctx_gpr_prefix(rt), rt, ctx_gpr_prefix(rt), rt, signed_imm_string, ctx_gpr_prefix(base), base);
break;
case InstrId::cpu_lwr:
print_line("{}{} = do_lwr(rdram, {}{}, {}, {}{})", ctx_gpr_prefix(rt), rt, ctx_gpr_prefix(rt), rt, signed_imm_string, ctx_gpr_prefix(base), base);
break;
// Unaligned stores
// examples:
// reg = 11111111 01234567
@ -1141,6 +1363,29 @@ bool process_instruction(const RecompPort::Context& context, const RecompPort::C
print_line("ctx->f{}.u32l = S32(floor(ctx->f{}.d))", fd, fs);
break;
default:
handled = false;
break;
}
auto find_it = binary_ops.find(instr.getUniqueId());
if (find_it != binary_ops.end()) {
CGenerator generator{};
InstructionContext instruction_context{};
instruction_context.rd = rd;
instruction_context.rs = rs;
instruction_context.rt = rt;
instruction_context.sa = sa;
instruction_context.fd = fd;
instruction_context.fs = fs;
instruction_context.ft = ft;
instruction_context.cop1_cs = cop1_cs;
instruction_context.imm16 = imm;
print_indent();
generator.process_binary_op(output_file, find_it->second, instruction_context);
handled = true;
}
if (!handled) {
fmt::print(stderr, "Unhandled instruction: {}\n", instr.getOpcodeName());
return false;
}