mirror of
https://github.com/N64Recomp/N64Recomp.git
synced 2025-05-14 08:12:19 +00:00
Removed output file from generator signatures, implement some of the luajit generator
This commit is contained in:
parent
f3f4a5cd73
commit
c54855b05d
4 changed files with 656 additions and 234 deletions
|
@ -27,111 +27,116 @@ namespace N64Recomp {
|
||||||
|
|
||||||
class Generator {
|
class Generator {
|
||||||
public:
|
public:
|
||||||
virtual void process_binary_op(std::ostream& output_file, const BinaryOp& op, const InstructionContext& ctx) const = 0;
|
virtual void process_binary_op(const BinaryOp& op, const InstructionContext& ctx) const = 0;
|
||||||
virtual void process_unary_op(std::ostream& output_file, const UnaryOp& op, const InstructionContext& ctx) const = 0;
|
virtual void process_unary_op(const UnaryOp& op, const InstructionContext& ctx) const = 0;
|
||||||
virtual void process_store_op(std::ostream& output_file, const StoreOp& op, const InstructionContext& ctx) const = 0;
|
virtual void process_store_op(const StoreOp& op, const InstructionContext& ctx) const = 0;
|
||||||
virtual void emit_function_start(std::ostream& output_file, const std::string& function_name) const = 0;
|
virtual void emit_function_start(const std::string& function_name) const = 0;
|
||||||
virtual void emit_function_end(std::ostream& output_file) const = 0;
|
virtual void emit_function_end() const = 0;
|
||||||
virtual void emit_function_call_lookup(std::ostream& output_file, uint32_t addr) const = 0;
|
virtual void emit_function_call_lookup(uint32_t addr) const = 0;
|
||||||
virtual void emit_function_call_by_register(std::ostream& output_file, int reg) const = 0;
|
virtual void emit_function_call_by_register(int reg) const = 0;
|
||||||
virtual void emit_function_call_by_name(std::ostream& output_file, const std::string& func_name) const = 0;
|
virtual void emit_function_call_by_name(const std::string& func_name) const = 0;
|
||||||
virtual void emit_goto(std::ostream& output_file, const std::string& target) const = 0;
|
virtual void emit_goto(const std::string& target) const = 0;
|
||||||
virtual void emit_label(std::ostream& output_file, const std::string& label_name) const = 0;
|
virtual void emit_label(const std::string& label_name) const = 0;
|
||||||
virtual void emit_variable_declaration(std::ostream& output_file, const std::string& var_name, int reg) const = 0;
|
virtual void emit_variable_declaration(const std::string& var_name, int reg) const = 0;
|
||||||
virtual void emit_branch_condition(std::ostream& output_file, const ConditionalBranchOp& op, const InstructionContext& ctx) const = 0;
|
virtual void emit_branch_condition(const ConditionalBranchOp& op, const InstructionContext& ctx) const = 0;
|
||||||
virtual void emit_branch_close(std::ostream& output_file) const = 0;
|
virtual void emit_branch_close() const = 0;
|
||||||
virtual void emit_switch(std::ostream& output_file, const std::string& jump_variable, int shift_amount) const = 0;
|
virtual void emit_switch(const std::string& jump_variable, int shift_amount) const = 0;
|
||||||
virtual void emit_case(std::ostream& output_file, int case_index, const std::string& target_label) const = 0;
|
virtual void emit_case(int case_index, const std::string& target_label) const = 0;
|
||||||
virtual void emit_switch_error(std::ostream& output_file, uint32_t instr_vram, uint32_t jtbl_vram) const = 0;
|
virtual void emit_switch_error(uint32_t instr_vram, uint32_t jtbl_vram) const = 0;
|
||||||
virtual void emit_switch_close(std::ostream& output_file) const = 0;
|
virtual void emit_switch_close() const = 0;
|
||||||
virtual void emit_return(std::ostream& output_file) const = 0;
|
virtual void emit_return() const = 0;
|
||||||
virtual void emit_check_fr(std::ostream& output_file, int fpr) const = 0;
|
virtual void emit_check_fr(int fpr) const = 0;
|
||||||
virtual void emit_check_nan(std::ostream& output_file, int fpr, bool is_double) const = 0;
|
virtual void emit_check_nan(int fpr, bool is_double) const = 0;
|
||||||
virtual void emit_cop0_status_read(std::ostream& output_file, int reg) const = 0;
|
virtual void emit_cop0_status_read(int reg) const = 0;
|
||||||
virtual void emit_cop0_status_write(std::ostream& output_file, int reg) const = 0;
|
virtual void emit_cop0_status_write(int reg) const = 0;
|
||||||
virtual void emit_cop1_cs_read(std::ostream& output_file, int reg) const = 0;
|
virtual void emit_cop1_cs_read(int reg) const = 0;
|
||||||
virtual void emit_cop1_cs_write(std::ostream& output_file, int reg) const = 0;
|
virtual void emit_cop1_cs_write(int reg) const = 0;
|
||||||
virtual void emit_muldiv(std::ostream& output_file, InstrId instr_id, int reg1, int reg2) const = 0;
|
virtual void emit_muldiv(InstrId instr_id, int reg1, int reg2) const = 0;
|
||||||
virtual void emit_syscall(std::ostream& output_file, uint32_t instr_vram) const = 0;
|
virtual void emit_syscall(uint32_t instr_vram) const = 0;
|
||||||
virtual void emit_do_break(std::ostream& output_file, uint32_t instr_vram) const = 0;
|
virtual void emit_do_break(uint32_t instr_vram) const = 0;
|
||||||
virtual void emit_pause_self(std::ostream& output_file) const = 0;
|
virtual void emit_pause_self() const = 0;
|
||||||
virtual void emit_trigger_event(std::ostream& output_file, size_t event_index) const = 0;
|
virtual void emit_trigger_event(size_t event_index) const = 0;
|
||||||
virtual void emit_comment(std::ostream& output_file, const std::string& comment) const = 0;
|
virtual void emit_comment(const std::string& comment) const = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
class CGenerator final : Generator {
|
class CGenerator final : Generator {
|
||||||
public:
|
public:
|
||||||
CGenerator() = default;
|
CGenerator(std::ostream& output_file) : output_file(output_file) {};
|
||||||
void process_binary_op(std::ostream& output_file, const BinaryOp& op, const InstructionContext& ctx) const final;
|
void process_binary_op(const BinaryOp& op, const InstructionContext& ctx) const final;
|
||||||
void process_unary_op(std::ostream& output_file, const UnaryOp& op, const InstructionContext& ctx) const final;
|
void process_unary_op(const UnaryOp& op, const InstructionContext& ctx) const final;
|
||||||
void process_store_op(std::ostream& output_file, const StoreOp& op, const InstructionContext& ctx) const final;
|
void process_store_op(const StoreOp& op, const InstructionContext& ctx) const final;
|
||||||
void emit_function_start(std::ostream& output_file, const std::string& function_name) const final;
|
void emit_function_start(const std::string& function_name) const final;
|
||||||
void emit_function_end(std::ostream& output_file) const final;
|
void emit_function_end() const final;
|
||||||
void emit_function_call_lookup(std::ostream& output_file, uint32_t addr) const final;
|
void emit_function_call_lookup(uint32_t addr) const final;
|
||||||
void emit_function_call_by_register(std::ostream& output_file, int reg) const final;
|
void emit_function_call_by_register(int reg) const final;
|
||||||
void emit_function_call_by_name(std::ostream& output_file, const std::string& func_name) const final;
|
void emit_function_call_by_name(const std::string& func_name) const final;
|
||||||
void emit_goto(std::ostream& output_file, const std::string& target) const final;
|
void emit_goto(const std::string& target) const final;
|
||||||
void emit_label(std::ostream& output_file, const std::string& label_name) const final;
|
void emit_label(const std::string& label_name) const final;
|
||||||
void emit_variable_declaration(std::ostream& output_file, const std::string& var_name, int reg) const final;
|
void emit_variable_declaration(const std::string& var_name, int reg) const final;
|
||||||
void emit_branch_condition(std::ostream& output_file, const ConditionalBranchOp& op, const InstructionContext& ctx) const final;
|
void emit_branch_condition(const ConditionalBranchOp& op, const InstructionContext& ctx) const final;
|
||||||
void emit_branch_close(std::ostream& output_file) const final;
|
void emit_branch_close() const final;
|
||||||
void emit_switch(std::ostream& output_file, const std::string& jump_variable, int shift_amount) const final;
|
void emit_switch(const std::string& jump_variable, int shift_amount) const final;
|
||||||
void emit_case(std::ostream& output_file, int case_index, const std::string& target_label) const final;
|
void emit_case(int case_index, const std::string& target_label) const final;
|
||||||
void emit_switch_error(std::ostream& output_file, uint32_t instr_vram, uint32_t jtbl_vram) const final;
|
void emit_switch_error(uint32_t instr_vram, uint32_t jtbl_vram) const final;
|
||||||
void emit_switch_close(std::ostream& output_file) const final;
|
void emit_switch_close() const final;
|
||||||
void emit_return(std::ostream& output_file) const final;
|
void emit_return() const final;
|
||||||
void emit_check_fr(std::ostream& output_file, int fpr) const final;
|
void emit_check_fr(int fpr) const final;
|
||||||
void emit_check_nan(std::ostream& output_file, int fpr, bool is_double) const final;
|
void emit_check_nan(int fpr, bool is_double) const final;
|
||||||
void emit_cop0_status_read(std::ostream& output_file, int reg) const final;
|
void emit_cop0_status_read(int reg) const final;
|
||||||
void emit_cop0_status_write(std::ostream& output_file, int reg) const final;
|
void emit_cop0_status_write(int reg) const final;
|
||||||
void emit_cop1_cs_read(std::ostream& output_file, int reg) const final;
|
void emit_cop1_cs_read(int reg) const final;
|
||||||
void emit_cop1_cs_write(std::ostream& output_file, int reg) const final;
|
void emit_cop1_cs_write(int reg) const final;
|
||||||
void emit_muldiv(std::ostream& output_file, InstrId instr_id, int reg1, int reg2) const final;
|
void emit_muldiv(InstrId instr_id, int reg1, int reg2) const final;
|
||||||
void emit_syscall(std::ostream& output_file, uint32_t instr_vram) const final;
|
void emit_syscall(uint32_t instr_vram) const final;
|
||||||
void emit_do_break(std::ostream& output_file, uint32_t instr_vram) const final;
|
void emit_do_break(uint32_t instr_vram) const final;
|
||||||
void emit_pause_self(std::ostream& output_file) const final;
|
void emit_pause_self() const final;
|
||||||
void emit_trigger_event(std::ostream& output_file, size_t event_index) const final;
|
void emit_trigger_event(size_t event_index) const final;
|
||||||
void emit_comment(std::ostream& output_file, const std::string& comment) const final;
|
void emit_comment(const std::string& comment) const final;
|
||||||
private:
|
private:
|
||||||
void get_operand_string(Operand operand, UnaryOpType operation, const InstructionContext& context, std::string& operand_string) const;
|
void get_operand_string(Operand operand, UnaryOpType operation, const InstructionContext& context, std::string& operand_string) const;
|
||||||
void get_binary_expr_string(BinaryOpType type, const BinaryOperands& operands, const InstructionContext& ctx, const std::string& output, std::string& expr_string) const;
|
void get_binary_expr_string(BinaryOpType type, const BinaryOperands& operands, const InstructionContext& ctx, const std::string& output, std::string& expr_string) const;
|
||||||
void get_notation(BinaryOpType op_type, std::string& func_string, std::string& infix_string) const;
|
void get_notation(BinaryOpType op_type, std::string& func_string, std::string& infix_string) const;
|
||||||
|
std::ostream& output_file;
|
||||||
};
|
};
|
||||||
|
|
||||||
class LuajitGenerator final : Generator {
|
class LuajitGenerator final : Generator {
|
||||||
public:
|
public:
|
||||||
LuajitGenerator() = default;
|
LuajitGenerator(std::ostream& output_file) : output_file(output_file) {};
|
||||||
void process_binary_op(std::ostream& output_file, const BinaryOp& op, const InstructionContext& ctx) const final;
|
void process_binary_op(const BinaryOp& op, const InstructionContext& ctx) const final;
|
||||||
void process_unary_op(std::ostream& output_file, const UnaryOp& op, const InstructionContext& ctx) const final;
|
void process_unary_op(const UnaryOp& op, const InstructionContext& ctx) const final;
|
||||||
void process_store_op(std::ostream& output_file, const StoreOp& op, const InstructionContext& ctx) const final;
|
void process_store_op(const StoreOp& op, const InstructionContext& ctx) const final;
|
||||||
void emit_function_start(std::ostream& output_file, const std::string& function_name) const final;
|
void emit_function_start(const std::string& function_name) const final;
|
||||||
void emit_function_end(std::ostream& output_file) const final;
|
void emit_function_end() const final;
|
||||||
void emit_function_call_lookup(std::ostream& output_file, uint32_t addr) const final;
|
void emit_function_call_lookup(uint32_t addr) const final;
|
||||||
void emit_function_call_by_register(std::ostream& output_file, int reg) const final;
|
void emit_function_call_by_register(int reg) const final;
|
||||||
void emit_function_call_by_name(std::ostream& output_file, const std::string& func_name) const final;
|
void emit_function_call_by_name(const std::string& func_name) const final;
|
||||||
void emit_goto(std::ostream& output_file, const std::string& target) const final;
|
void emit_goto(const std::string& target) const final;
|
||||||
void emit_label(std::ostream& output_file, const std::string& label_name) const final;
|
void emit_label(const std::string& label_name) const final;
|
||||||
void emit_variable_declaration(std::ostream& output_file, const std::string& var_name, int reg) const final;
|
void emit_variable_declaration(const std::string& var_name, int reg) const final;
|
||||||
void emit_branch_condition(std::ostream& output_file, const ConditionalBranchOp& op, const InstructionContext& ctx) const final;
|
void emit_branch_condition(const ConditionalBranchOp& op, const InstructionContext& ctx) const final;
|
||||||
void emit_branch_close(std::ostream& output_file) const final;
|
void emit_branch_close() const final;
|
||||||
void emit_switch(std::ostream& output_file, const std::string& jump_variable, int shift_amount) const final;
|
void emit_switch(const std::string& jump_variable, int shift_amount) const final;
|
||||||
void emit_case(std::ostream& output_file, int case_index, const std::string& target_label) const final;
|
void emit_case(int case_index, const std::string& target_label) const final;
|
||||||
void emit_switch_error(std::ostream& output_file, uint32_t instr_vram, uint32_t jtbl_vram) const final;
|
void emit_switch_error(uint32_t instr_vram, uint32_t jtbl_vram) const final;
|
||||||
void emit_switch_close(std::ostream& output_file) const final;
|
void emit_switch_close() const final;
|
||||||
void emit_return(std::ostream& output_file) const final;
|
void emit_return() const final;
|
||||||
void emit_check_fr(std::ostream& output_file, int fpr) const final;
|
void emit_check_fr(int fpr) const final;
|
||||||
void emit_check_nan(std::ostream& output_file, int fpr, bool is_double) const final;
|
void emit_check_nan(int fpr, bool is_double) const final;
|
||||||
void emit_cop0_status_read(std::ostream& output_file, int reg) const final;
|
void emit_cop0_status_read(int reg) const final;
|
||||||
void emit_cop0_status_write(std::ostream& output_file, int reg) const final;
|
void emit_cop0_status_write(int reg) const final;
|
||||||
void emit_cop1_cs_read(std::ostream& output_file, int reg) const final;
|
void emit_cop1_cs_read(int reg) const final;
|
||||||
void emit_cop1_cs_write(std::ostream& output_file, int reg) const final;
|
void emit_cop1_cs_write(int reg) const final;
|
||||||
void emit_muldiv(std::ostream& output_file, InstrId instr_id, int reg1, int reg2) const final;
|
void emit_muldiv(InstrId instr_id, int reg1, int reg2) const final;
|
||||||
void emit_syscall(std::ostream& output_file, uint32_t instr_vram) const final;
|
void emit_syscall(uint32_t instr_vram) const final;
|
||||||
void emit_do_break(std::ostream& output_file, uint32_t instr_vram) const final;
|
void emit_do_break(uint32_t instr_vram) const final;
|
||||||
void emit_pause_self(std::ostream& output_file) const final;
|
void emit_pause_self() const final;
|
||||||
void emit_trigger_event(std::ostream& output_file, size_t event_index) const final;
|
void emit_trigger_event(size_t event_index) const final;
|
||||||
void emit_comment(std::ostream& output_file, const std::string& comment) const final;
|
void emit_comment(const std::string& comment) const final;
|
||||||
private:
|
private:
|
||||||
|
void get_operand_string(Operand operand, UnaryOpType operation, const InstructionContext& context, std::string& operand_string) const;
|
||||||
|
void get_binary_expr_string(BinaryOpType type, const BinaryOperands& operands, const InstructionContext& ctx, const std::string& output, std::string& expr_string) const;
|
||||||
|
void get_notation(BinaryOpType op_type, std::string& func_string, std::string& infix_string) const;
|
||||||
|
std::ostream& output_file;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
|
|
||||||
struct BinaryOpFields { std::string func_string; std::string infix_string; };
|
struct BinaryOpFields { std::string func_string; std::string infix_string; };
|
||||||
|
|
||||||
std::vector<BinaryOpFields> c_op_fields = []() {
|
static std::vector<BinaryOpFields> c_op_fields = []() {
|
||||||
std::vector<BinaryOpFields> ret{};
|
std::vector<BinaryOpFields> ret{};
|
||||||
ret.resize(static_cast<size_t>(N64Recomp::BinaryOpType::COUNT));
|
ret.resize(static_cast<size_t>(N64Recomp::BinaryOpType::COUNT));
|
||||||
std::vector<char> ops_setup{};
|
std::vector<char> ops_setup{};
|
||||||
|
@ -72,22 +72,22 @@ std::vector<BinaryOpFields> c_op_fields = []() {
|
||||||
return ret;
|
return ret;
|
||||||
}();
|
}();
|
||||||
|
|
||||||
std::string gpr_to_string(int gpr_index) {
|
static std::string gpr_to_string(int gpr_index) {
|
||||||
if (gpr_index == 0) {
|
if (gpr_index == 0) {
|
||||||
return "0";
|
return "0";
|
||||||
}
|
}
|
||||||
return fmt::format("ctx->r{}", gpr_index);
|
return fmt::format("ctx->r{}", gpr_index);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string fpr_to_string(int fpr_index) {
|
static std::string fpr_to_string(int fpr_index) {
|
||||||
return fmt::format("ctx->f{}.fl", fpr_index);
|
return fmt::format("ctx->f{}.fl", fpr_index);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string fpr_double_to_string(int fpr_index) {
|
static std::string fpr_double_to_string(int fpr_index) {
|
||||||
return fmt::format("ctx->f{}.d", fpr_index);
|
return fmt::format("ctx->f{}.d", fpr_index);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string fpr_u32l_to_string(int fpr_index) {
|
static std::string fpr_u32l_to_string(int fpr_index) {
|
||||||
if (fpr_index & 1) {
|
if (fpr_index & 1) {
|
||||||
return fmt::format("ctx->f_odd[({} - 1) * 2]", fpr_index);
|
return fmt::format("ctx->f_odd[({} - 1) * 2]", fpr_index);
|
||||||
}
|
}
|
||||||
|
@ -96,11 +96,11 @@ std::string fpr_u32l_to_string(int fpr_index) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string fpr_u64_to_string(int fpr_index) {
|
static std::string fpr_u64_to_string(int fpr_index) {
|
||||||
return fmt::format("ctx->f{}.u64", fpr_index);
|
return fmt::format("ctx->f{}.u64", fpr_index);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string unsigned_reloc(const N64Recomp::InstructionContext& context) {
|
static std::string unsigned_reloc(const N64Recomp::InstructionContext& context) {
|
||||||
switch (context.reloc_type) {
|
switch (context.reloc_type) {
|
||||||
case N64Recomp::RelocType::R_MIPS_HI16:
|
case N64Recomp::RelocType::R_MIPS_HI16:
|
||||||
return fmt::format("{}RELOC_HI16({}, {:#X})",
|
return fmt::format("{}RELOC_HI16({}, {:#X})",
|
||||||
|
@ -113,7 +113,7 @@ std::string unsigned_reloc(const N64Recomp::InstructionContext& context) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string signed_reloc(const N64Recomp::InstructionContext& context) {
|
static std::string signed_reloc(const N64Recomp::InstructionContext& context) {
|
||||||
return "(int16_t)" + unsigned_reloc(context);
|
return "(int16_t)" + unsigned_reloc(context);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -365,7 +365,7 @@ void N64Recomp::CGenerator::get_binary_expr_string(BinaryOpType type, const Bina
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void N64Recomp::CGenerator::emit_function_start(std::ostream& output_file, const std::string& function_name) const {
|
void N64Recomp::CGenerator::emit_function_start(const std::string& function_name) const {
|
||||||
fmt::print(output_file,
|
fmt::print(output_file,
|
||||||
"RECOMP_FUNC void {}(uint8_t* rdram, recomp_context* ctx) {{\n"
|
"RECOMP_FUNC void {}(uint8_t* rdram, recomp_context* ctx) {{\n"
|
||||||
// these variables shouldn't need to be preserved across function boundaries, so make them local for more efficient output
|
// these variables shouldn't need to be preserved across function boundaries, so make them local for more efficient output
|
||||||
|
@ -375,37 +375,37 @@ void N64Recomp::CGenerator::emit_function_start(std::ostream& output_file, const
|
||||||
function_name);
|
function_name);
|
||||||
}
|
}
|
||||||
|
|
||||||
void N64Recomp::CGenerator::emit_function_end(std::ostream& output_file) const {
|
void N64Recomp::CGenerator::emit_function_end() const {
|
||||||
fmt::print(output_file, ";}}\n");
|
fmt::print(output_file, ";}}\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
void N64Recomp::CGenerator::emit_function_call_lookup(std::ostream& output_file, uint32_t addr) const {
|
void N64Recomp::CGenerator::emit_function_call_lookup(uint32_t addr) const {
|
||||||
fmt::print(output_file, "LOOKUP_FUNC(0x{:08X})(rdram, ctx);\n", addr);
|
fmt::print(output_file, "LOOKUP_FUNC(0x{:08X})(rdram, ctx);\n", addr);
|
||||||
}
|
}
|
||||||
|
|
||||||
void N64Recomp::CGenerator::emit_function_call_by_register(std::ostream& output_file, int reg) const {
|
void N64Recomp::CGenerator::emit_function_call_by_register(int reg) const {
|
||||||
fmt::print(output_file, "LOOKUP_FUNC({})(rdram, ctx);\n", gpr_to_string(reg));
|
fmt::print(output_file, "LOOKUP_FUNC({})(rdram, ctx);\n", gpr_to_string(reg));
|
||||||
}
|
}
|
||||||
|
|
||||||
void N64Recomp::CGenerator::emit_function_call_by_name(std::ostream& output_file, const std::string& func_name) const {
|
void N64Recomp::CGenerator::emit_function_call_by_name(const std::string& func_name) const {
|
||||||
fmt::print(output_file, "{}(rdram, ctx);\n", func_name);
|
fmt::print(output_file, "{}(rdram, ctx);\n", func_name);
|
||||||
}
|
}
|
||||||
|
|
||||||
void N64Recomp::CGenerator::emit_goto(std::ostream& output_file, const std::string& target) const {
|
void N64Recomp::CGenerator::emit_goto(const std::string& target) const {
|
||||||
fmt::print(output_file,
|
fmt::print(output_file,
|
||||||
" goto {};\n", target);
|
" goto {};\n", target);
|
||||||
}
|
}
|
||||||
|
|
||||||
void N64Recomp::CGenerator::emit_label(std::ostream& output_file, const std::string& label_name) const {
|
void N64Recomp::CGenerator::emit_label(const std::string& label_name) const {
|
||||||
fmt::print(output_file,
|
fmt::print(output_file,
|
||||||
"{}:\n", label_name);
|
"{}:\n", label_name);
|
||||||
}
|
}
|
||||||
|
|
||||||
void N64Recomp::CGenerator::emit_variable_declaration(std::ostream& output_file, const std::string& var_name, int reg) const {
|
void N64Recomp::CGenerator::emit_variable_declaration(const std::string& var_name, int reg) const {
|
||||||
fmt::print(output_file, "gpr {} = {};\n", var_name, gpr_to_string(reg));
|
fmt::print(output_file, "gpr {} = {};\n", var_name, gpr_to_string(reg));
|
||||||
}
|
}
|
||||||
|
|
||||||
void N64Recomp::CGenerator::emit_branch_condition(std::ostream& output_file, const ConditionalBranchOp& op, const InstructionContext& ctx) const {
|
void N64Recomp::CGenerator::emit_branch_condition(const ConditionalBranchOp& op, const InstructionContext& ctx) const {
|
||||||
// Thread local variables to prevent allocations when possible.
|
// 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.
|
// TODO these thread locals probably don't actually help right now, so figure out a better way to prevent allocations.
|
||||||
thread_local std::string expr_string{};
|
thread_local std::string expr_string{};
|
||||||
|
@ -413,55 +413,55 @@ void N64Recomp::CGenerator::emit_branch_condition(std::ostream& output_file, con
|
||||||
fmt::print(output_file, "if ({}) {{\n", expr_string);
|
fmt::print(output_file, "if ({}) {{\n", expr_string);
|
||||||
}
|
}
|
||||||
|
|
||||||
void N64Recomp::CGenerator::emit_branch_close(std::ostream& output_file) const {
|
void N64Recomp::CGenerator::emit_branch_close() const {
|
||||||
fmt::print(output_file, "}}\n");
|
fmt::print(output_file, "}}\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
void N64Recomp::CGenerator::emit_switch_close(std::ostream& output_file) const {
|
void N64Recomp::CGenerator::emit_switch_close() const {
|
||||||
fmt::print(output_file, "}}\n");
|
fmt::print(output_file, "}}\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
void N64Recomp::CGenerator::emit_switch(std::ostream& output_file, const std::string& jump_variable, int shift_amount) const {
|
void N64Recomp::CGenerator::emit_switch(const std::string& jump_variable, int shift_amount) const {
|
||||||
fmt::print(output_file, "switch ({} >> {}) {{\n", jump_variable, shift_amount);
|
fmt::print(output_file, "switch ({} >> {}) {{\n", jump_variable, shift_amount);
|
||||||
}
|
}
|
||||||
|
|
||||||
void N64Recomp::CGenerator::emit_case(std::ostream& output_file, int case_index, const std::string& target_label) const {
|
void N64Recomp::CGenerator::emit_case(int case_index, const std::string& target_label) const {
|
||||||
fmt::print(output_file, "case {}: goto {}; break;\n", case_index, target_label);
|
fmt::print(output_file, "case {}: goto {}; break;\n", case_index, target_label);
|
||||||
}
|
}
|
||||||
|
|
||||||
void N64Recomp::CGenerator::emit_switch_error(std::ostream& output_file, uint32_t instr_vram, uint32_t jtbl_vram) const {
|
void N64Recomp::CGenerator::emit_switch_error(uint32_t instr_vram, uint32_t jtbl_vram) const {
|
||||||
fmt::print(output_file, "default: switch_error(__func__, 0x{:08X}, 0x{:08X});\n", instr_vram, jtbl_vram);
|
fmt::print(output_file, "default: switch_error(__func__, 0x{:08X}, 0x{:08X});\n", instr_vram, jtbl_vram);
|
||||||
}
|
}
|
||||||
|
|
||||||
void N64Recomp::CGenerator::emit_return(std::ostream& output_file) const {
|
void N64Recomp::CGenerator::emit_return() const {
|
||||||
fmt::print(output_file, "return;\n");
|
fmt::print(output_file, "return;\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
void N64Recomp::CGenerator::emit_check_fr(std::ostream& output_file, int fpr) const {
|
void N64Recomp::CGenerator::emit_check_fr(int fpr) const {
|
||||||
fmt::print(output_file, "CHECK_FR(ctx, {});\n ", fpr);
|
fmt::print(output_file, "CHECK_FR(ctx, {});\n ", fpr);
|
||||||
}
|
}
|
||||||
|
|
||||||
void N64Recomp::CGenerator::emit_check_nan(std::ostream& output_file, int fpr, bool is_double) const {
|
void N64Recomp::CGenerator::emit_check_nan(int fpr, bool is_double) const {
|
||||||
fmt::print(output_file, "NAN_CHECK(ctx->f{}.{}); ", fpr, is_double ? "d" : "fl");
|
fmt::print(output_file, "NAN_CHECK(ctx->f{}.{}); ", fpr, is_double ? "d" : "fl");
|
||||||
}
|
}
|
||||||
|
|
||||||
void N64Recomp::CGenerator::emit_cop0_status_read(std::ostream& output_file, int reg) const {
|
void N64Recomp::CGenerator::emit_cop0_status_read(int reg) const {
|
||||||
fmt::print(output_file, "{} = cop0_status_read(ctx);\n", gpr_to_string(reg));
|
fmt::print(output_file, "{} = cop0_status_read(ctx);\n", gpr_to_string(reg));
|
||||||
}
|
}
|
||||||
|
|
||||||
void N64Recomp::CGenerator::emit_cop0_status_write(std::ostream& output_file, int reg) const {
|
void N64Recomp::CGenerator::emit_cop0_status_write(int reg) const {
|
||||||
fmt::print(output_file, "cop0_status_write(ctx, {})", gpr_to_string(reg));
|
fmt::print(output_file, "cop0_status_write(ctx, {})", gpr_to_string(reg));
|
||||||
}
|
}
|
||||||
|
|
||||||
void N64Recomp::CGenerator::emit_cop1_cs_read(std::ostream& output_file, int reg) const {
|
void N64Recomp::CGenerator::emit_cop1_cs_read(int reg) const {
|
||||||
fmt::print(output_file, "{} = rounding_mode;\n", gpr_to_string(reg));
|
fmt::print(output_file, "{} = rounding_mode;\n", gpr_to_string(reg));
|
||||||
}
|
}
|
||||||
|
|
||||||
void N64Recomp::CGenerator::emit_cop1_cs_write(std::ostream& output_file, int reg) const {
|
void N64Recomp::CGenerator::emit_cop1_cs_write(int reg) const {
|
||||||
fmt::print(output_file, "rounding_mode = ({}) & 0x3;\n", gpr_to_string(reg));
|
fmt::print(output_file, "rounding_mode = ({}) & 0x3;\n", gpr_to_string(reg));
|
||||||
}
|
}
|
||||||
|
|
||||||
void N64Recomp::CGenerator::emit_muldiv(std::ostream& output_file, InstrId instr_id, int reg1, int reg2) const {
|
void N64Recomp::CGenerator::emit_muldiv(InstrId instr_id, int reg1, int reg2) const {
|
||||||
switch (instr_id) {
|
switch (instr_id) {
|
||||||
case InstrId::cpu_mult:
|
case InstrId::cpu_mult:
|
||||||
fmt::print(output_file, "result = S64(S32({})) * S64(S32({})); lo = S32(result >> 0); hi = S32(result >> 32);\n", gpr_to_string(reg1), gpr_to_string(reg2));
|
fmt::print(output_file, "result = S64(S32({})) * S64(S32({})); lo = S32(result >> 0); hi = S32(result >> 32);\n", gpr_to_string(reg1), gpr_to_string(reg2));
|
||||||
|
@ -491,27 +491,27 @@ void N64Recomp::CGenerator::emit_muldiv(std::ostream& output_file, InstrId instr
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void N64Recomp::CGenerator::emit_syscall(std::ostream& output_file, uint32_t instr_vram) const {
|
void N64Recomp::CGenerator::emit_syscall(uint32_t instr_vram) const {
|
||||||
fmt::print(output_file, "recomp_syscall_handler(rdram, ctx, 0x{:08X});\n", instr_vram);
|
fmt::print(output_file, "recomp_syscall_handler(rdram, ctx, 0x{:08X});\n", instr_vram);
|
||||||
}
|
}
|
||||||
|
|
||||||
void N64Recomp::CGenerator::emit_do_break(std::ostream& output_file, uint32_t instr_vram) const {
|
void N64Recomp::CGenerator::emit_do_break(uint32_t instr_vram) const {
|
||||||
fmt::print(output_file, "do_break({});\n", instr_vram);
|
fmt::print(output_file, "do_break({});\n", instr_vram);
|
||||||
}
|
}
|
||||||
|
|
||||||
void N64Recomp::CGenerator::emit_pause_self(std::ostream& output_file) const {
|
void N64Recomp::CGenerator::emit_pause_self() const {
|
||||||
fmt::print(output_file, "pause_self(rdram);\n");
|
fmt::print(output_file, "pause_self(rdram);\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
void N64Recomp::CGenerator::emit_trigger_event(std::ostream& output_file, size_t event_index) const {
|
void N64Recomp::CGenerator::emit_trigger_event(size_t event_index) const {
|
||||||
fmt::print(output_file, "recomp_trigger_event(rdram, ctx, base_event_index + {});\n", event_index);
|
fmt::print(output_file, "recomp_trigger_event(rdram, ctx, base_event_index + {});\n", event_index);
|
||||||
}
|
}
|
||||||
|
|
||||||
void N64Recomp::CGenerator::emit_comment(std::ostream& output_file, const std::string& comment) const {
|
void N64Recomp::CGenerator::emit_comment(const std::string& comment) const {
|
||||||
fmt::print(output_file, "// {}\n", comment);
|
fmt::print(output_file, "// {}\n", comment);
|
||||||
}
|
}
|
||||||
|
|
||||||
void N64Recomp::CGenerator::process_binary_op(std::ostream& output_file, const BinaryOp& op, const InstructionContext& ctx) const {
|
void N64Recomp::CGenerator::process_binary_op(const BinaryOp& op, const InstructionContext& ctx) const {
|
||||||
// Thread local variables to prevent allocations when possible.
|
// 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.
|
// 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 output{};
|
||||||
|
@ -521,7 +521,7 @@ void N64Recomp::CGenerator::process_binary_op(std::ostream& output_file, const B
|
||||||
fmt::print(output_file, "{} = {};\n", output, expression);
|
fmt::print(output_file, "{} = {};\n", output, expression);
|
||||||
}
|
}
|
||||||
|
|
||||||
void N64Recomp::CGenerator::process_unary_op(std::ostream& output_file, const UnaryOp& op, const InstructionContext& ctx) const {
|
void N64Recomp::CGenerator::process_unary_op(const UnaryOp& op, const InstructionContext& ctx) const {
|
||||||
// Thread local variables to prevent allocations when possible.
|
// 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.
|
// 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 output{};
|
||||||
|
@ -532,7 +532,7 @@ void N64Recomp::CGenerator::process_unary_op(std::ostream& output_file, const Un
|
||||||
fmt::print(output_file, "{} = {};\n", output, input);
|
fmt::print(output_file, "{} = {};\n", output, input);
|
||||||
}
|
}
|
||||||
|
|
||||||
void N64Recomp::CGenerator::process_store_op(std::ostream& output_file, const StoreOp& op, const InstructionContext& ctx) const {
|
void N64Recomp::CGenerator::process_store_op(const StoreOp& op, const InstructionContext& ctx) const {
|
||||||
// Thread local variables to prevent allocations when possible.
|
// 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.
|
// TODO these thread locals probably don't actually help right now, so figure out a better way to prevent allocations.
|
||||||
thread_local std::string base_str{};
|
thread_local std::string base_str{};
|
||||||
|
|
|
@ -6,139 +6,556 @@
|
||||||
|
|
||||||
#include "generator.h"
|
#include "generator.h"
|
||||||
|
|
||||||
void N64Recomp::LuajitGenerator::process_binary_op(std::ostream& output_file, const BinaryOp& op, const InstructionContext& ctx) const {
|
struct BinaryOpFields { std::string func_string; std::string infix_string; };
|
||||||
// TODO
|
|
||||||
fmt::print(output_file, "\n");
|
static std::vector<BinaryOpFields> luajit_op_fields = []() {
|
||||||
|
std::vector<BinaryOpFields> ret{};
|
||||||
|
ret.resize(static_cast<size_t>(N64Recomp::BinaryOpType::COUNT));
|
||||||
|
std::vector<char> ops_setup{};
|
||||||
|
ops_setup.resize(static_cast<size_t>(N64Recomp::BinaryOpType::COUNT));
|
||||||
|
|
||||||
|
auto setup_op = [&ret, &ops_setup](N64Recomp::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(N64Recomp::BinaryOpType::Add32, "ADD32", "");
|
||||||
|
setup_op(N64Recomp::BinaryOpType::Sub32, "SUB32", "");
|
||||||
|
setup_op(N64Recomp::BinaryOpType::Add64, "", "+");
|
||||||
|
setup_op(N64Recomp::BinaryOpType::Sub64, "", "-");
|
||||||
|
setup_op(N64Recomp::BinaryOpType::And64, "bit.band", "");
|
||||||
|
setup_op(N64Recomp::BinaryOpType::AddFloat, "", "+");
|
||||||
|
setup_op(N64Recomp::BinaryOpType::AddDouble, "", "+");
|
||||||
|
setup_op(N64Recomp::BinaryOpType::SubFloat, "", "-");
|
||||||
|
setup_op(N64Recomp::BinaryOpType::SubDouble, "", "-");
|
||||||
|
setup_op(N64Recomp::BinaryOpType::MulFloat, "MUL_S", "");
|
||||||
|
setup_op(N64Recomp::BinaryOpType::MulDouble, "MUL_D", "");
|
||||||
|
setup_op(N64Recomp::BinaryOpType::DivFloat, "DIV_S", "");
|
||||||
|
setup_op(N64Recomp::BinaryOpType::DivDouble, "DIV_D", "");
|
||||||
|
setup_op(N64Recomp::BinaryOpType::Or64, "bit.bor", "");
|
||||||
|
setup_op(N64Recomp::BinaryOpType::Nor64, "NOR", "");
|
||||||
|
setup_op(N64Recomp::BinaryOpType::Xor64, "bit.bxor", "");
|
||||||
|
setup_op(N64Recomp::BinaryOpType::Sll32, "SLL32", "");
|
||||||
|
setup_op(N64Recomp::BinaryOpType::Sll64, "bit.lshift", "");
|
||||||
|
setup_op(N64Recomp::BinaryOpType::Srl32, "SRL32", "");
|
||||||
|
setup_op(N64Recomp::BinaryOpType::Srl64, "bit.rshift", "");
|
||||||
|
setup_op(N64Recomp::BinaryOpType::Sra32, "SRA32", "");
|
||||||
|
setup_op(N64Recomp::BinaryOpType::Sra64, "bit.arshift", "");
|
||||||
|
setup_op(N64Recomp::BinaryOpType::Equal, "", "==");
|
||||||
|
setup_op(N64Recomp::BinaryOpType::NotEqual, "", "~=");
|
||||||
|
setup_op(N64Recomp::BinaryOpType::Less, "", "<");
|
||||||
|
setup_op(N64Recomp::BinaryOpType::LessEq, "", "<=");
|
||||||
|
setup_op(N64Recomp::BinaryOpType::Greater, "", ">");
|
||||||
|
setup_op(N64Recomp::BinaryOpType::GreaterEq, "", ">=");
|
||||||
|
setup_op(N64Recomp::BinaryOpType::LD, "MEM_LD", "");
|
||||||
|
setup_op(N64Recomp::BinaryOpType::LW, "MEM_LW", "");
|
||||||
|
setup_op(N64Recomp::BinaryOpType::LWU, "MEM_LWU", "");
|
||||||
|
setup_op(N64Recomp::BinaryOpType::LH, "MEM_LH", "");
|
||||||
|
setup_op(N64Recomp::BinaryOpType::LHU, "MEM_LHU", "");
|
||||||
|
setup_op(N64Recomp::BinaryOpType::LB, "MEM_LB", "");
|
||||||
|
setup_op(N64Recomp::BinaryOpType::LBU, "MEM_LBU", "");
|
||||||
|
setup_op(N64Recomp::BinaryOpType::LDL, "MEM_LDL", "");
|
||||||
|
setup_op(N64Recomp::BinaryOpType::LDR, "MEM_LDR", "");
|
||||||
|
setup_op(N64Recomp::BinaryOpType::LWL, "MEM_LWL", "");
|
||||||
|
setup_op(N64Recomp::BinaryOpType::LWR, "MEM_LWR", "");
|
||||||
|
setup_op(N64Recomp::BinaryOpType::True, "", "");
|
||||||
|
setup_op(N64Recomp::BinaryOpType::False, "", "");
|
||||||
|
|
||||||
|
// Ensure every operation has been setup.
|
||||||
|
for (char is_set : ops_setup) {
|
||||||
|
assert(is_set && "Operation has not been setup!");
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}();
|
||||||
|
|
||||||
|
static std::string gpr_to_string(int gpr_index) {
|
||||||
|
if (gpr_index == 0) {
|
||||||
|
return "0ULL";
|
||||||
|
}
|
||||||
|
return fmt::format("ctx.r{}", gpr_index);
|
||||||
}
|
}
|
||||||
|
|
||||||
void N64Recomp::LuajitGenerator::process_unary_op(std::ostream& output_file, const UnaryOp& op, const InstructionContext& ctx) const {
|
static std::string fpr_to_string(int fpr_index) {
|
||||||
// TODO
|
return fmt::format("ctx.f{}.fl", fpr_index);
|
||||||
fmt::print(output_file, "\n");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void N64Recomp::LuajitGenerator::process_store_op(std::ostream& output_file, const StoreOp& op, const InstructionContext& ctx) const {
|
static std::string fpr_double_to_string(int fpr_index) {
|
||||||
// TODO
|
return fmt::format("ctx.f{}.d", fpr_index);
|
||||||
fmt::print(output_file, "\n");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void N64Recomp::LuajitGenerator::emit_function_start(std::ostream& output_file, const std::string& function_name) const {
|
static std::string fpr_u32l_to_string(int fpr_index) {
|
||||||
fmt::print(output_file, "function {}(rdram, ctx)\n", function_name);
|
if (fpr_index & 1) {
|
||||||
|
return fmt::format("ctx.f_odd[({} - 1) * 2]", fpr_index);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return fmt::format("ctx.f{}.u32l", fpr_index);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void N64Recomp::LuajitGenerator::emit_function_end(std::ostream& output_file) const {
|
static std::string fpr_u64_to_string(int fpr_index) {
|
||||||
|
return fmt::format("ctx.f{}.u64", fpr_index);
|
||||||
|
}
|
||||||
|
|
||||||
|
static std::string unsigned_reloc(const N64Recomp::InstructionContext& context) {
|
||||||
|
switch (context.reloc_type) {
|
||||||
|
case N64Recomp::RelocType::R_MIPS_HI16:
|
||||||
|
return fmt::format("{}RELOC_HI16({}, {:#X}ULL)",
|
||||||
|
context.reloc_tag_as_reference ? "REF_" : "", context.reloc_section_index, context.reloc_target_section_offset);
|
||||||
|
case N64Recomp::RelocType::R_MIPS_LO16:
|
||||||
|
return fmt::format("{}RELOC_LO16({}, {:#X}ULL)",
|
||||||
|
context.reloc_tag_as_reference ? "REF_" : "", context.reloc_section_index, context.reloc_target_section_offset);
|
||||||
|
default:
|
||||||
|
throw std::runtime_error(fmt::format("Unexpected reloc type {}\n", static_cast<int>(context.reloc_type)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static std::string signed_reloc(const N64Recomp::InstructionContext& context) {
|
||||||
|
return "(int16_t)" + unsigned_reloc(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
void N64Recomp::LuajitGenerator::get_operand_string(Operand operand, UnaryOpType operation, const InstructionContext& context, std::string& operand_string) const {
|
||||||
|
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:
|
||||||
|
operand_string = fpr_to_string(context.fd);
|
||||||
|
break;
|
||||||
|
case Operand::Fs:
|
||||||
|
operand_string = fpr_to_string(context.fs);
|
||||||
|
break;
|
||||||
|
case Operand::Ft:
|
||||||
|
operand_string = fpr_to_string(context.ft);
|
||||||
|
break;
|
||||||
|
case Operand::FdDouble:
|
||||||
|
operand_string = fpr_double_to_string(context.fd);
|
||||||
|
break;
|
||||||
|
case Operand::FsDouble:
|
||||||
|
operand_string = fpr_double_to_string(context.fs);
|
||||||
|
break;
|
||||||
|
case Operand::FtDouble:
|
||||||
|
operand_string = fpr_double_to_string(context.ft);
|
||||||
|
break;
|
||||||
|
case Operand::FdU32L:
|
||||||
|
operand_string = fpr_u32l_to_string(context.fd);
|
||||||
|
break;
|
||||||
|
case Operand::FsU32L:
|
||||||
|
operand_string = fpr_u32l_to_string(context.fs);
|
||||||
|
break;
|
||||||
|
case Operand::FtU32L:
|
||||||
|
operand_string = fpr_u32l_to_string(context.ft);
|
||||||
|
break;
|
||||||
|
case Operand::FdU32H:
|
||||||
|
assert(false);
|
||||||
|
break;
|
||||||
|
case Operand::FsU32H:
|
||||||
|
assert(false);
|
||||||
|
break;
|
||||||
|
case Operand::FtU32H:
|
||||||
|
assert(false);
|
||||||
|
break;
|
||||||
|
case Operand::FdU64:
|
||||||
|
operand_string = fpr_u64_to_string(context.fd);
|
||||||
|
break;
|
||||||
|
case Operand::FsU64:
|
||||||
|
operand_string = fpr_u64_to_string(context.fs);
|
||||||
|
break;
|
||||||
|
case Operand::FtU64:
|
||||||
|
operand_string = fpr_u64_to_string(context.ft);
|
||||||
|
break;
|
||||||
|
case Operand::ImmU16:
|
||||||
|
if (context.reloc_type != N64Recomp::RelocType::R_MIPS_NONE) {
|
||||||
|
operand_string = unsigned_reloc(context);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
operand_string = fmt::format("{:#X}ULL", context.imm16);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case Operand::ImmS16:
|
||||||
|
if (context.reloc_type != N64Recomp::RelocType::R_MIPS_NONE) {
|
||||||
|
operand_string = signed_reloc(context);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
operand_string = fmt::format("{:#X}ULL", (int16_t)context.imm16);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case Operand::Sa:
|
||||||
|
operand_string = std::to_string(context.sa);
|
||||||
|
break;
|
||||||
|
case Operand::Sa32:
|
||||||
|
operand_string = fmt::format("({} + 32ULL)", context.sa);
|
||||||
|
break;
|
||||||
|
case Operand::Cop1cs:
|
||||||
|
operand_string = fmt::format("c1cs");
|
||||||
|
break;
|
||||||
|
case Operand::Hi:
|
||||||
|
operand_string = "hi";
|
||||||
|
break;
|
||||||
|
case Operand::Lo:
|
||||||
|
operand_string = "lo";
|
||||||
|
break;
|
||||||
|
case Operand::Zero:
|
||||||
|
operand_string = "0ULL";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
switch (operation) {
|
||||||
|
case UnaryOpType::None:
|
||||||
|
break;
|
||||||
|
case UnaryOpType::ToS32:
|
||||||
|
case UnaryOpType::ToInt32:
|
||||||
|
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:
|
||||||
|
operand_string = "S32(bit.lshift(" + operand_string + "), 16ULL)";
|
||||||
|
break;
|
||||||
|
case UnaryOpType::Mask5:
|
||||||
|
operand_string = "(bit.band(" + operand_string + "), 31ULL)";
|
||||||
|
break;
|
||||||
|
case UnaryOpType::Mask6:
|
||||||
|
operand_string = "(bit.band(" + operand_string + "), 63ULL)";
|
||||||
|
break;
|
||||||
|
case UnaryOpType::Negate:
|
||||||
|
operand_string = "-" + operand_string;
|
||||||
|
break;
|
||||||
|
case UnaryOpType::AbsFloat:
|
||||||
|
operand_string = "ABSF(" + operand_string + ")";
|
||||||
|
break;
|
||||||
|
case UnaryOpType::AbsDouble:
|
||||||
|
operand_string = "ABS(" + operand_string + ")";
|
||||||
|
break;
|
||||||
|
case UnaryOpType::SqrtFloat:
|
||||||
|
operand_string = "SQRTF(" + operand_string + ")";
|
||||||
|
break;
|
||||||
|
case UnaryOpType::SqrtDouble:
|
||||||
|
operand_string = "SQRT(" + operand_string + ")";
|
||||||
|
break;
|
||||||
|
case UnaryOpType::ConvertSFromW:
|
||||||
|
operand_string = "CVT_S_W(" + operand_string + ")";
|
||||||
|
break;
|
||||||
|
case UnaryOpType::ConvertWFromS:
|
||||||
|
operand_string = "CVT_W_S(" + operand_string + ", rounding_mode)";
|
||||||
|
break;
|
||||||
|
case UnaryOpType::ConvertDFromW:
|
||||||
|
operand_string = "CVT_D_W(" + operand_string + ")";
|
||||||
|
break;
|
||||||
|
case UnaryOpType::ConvertWFromD:
|
||||||
|
operand_string = "CVT_W_D(" + operand_string + ", rounding_mode)";
|
||||||
|
break;
|
||||||
|
case UnaryOpType::ConvertDFromS:
|
||||||
|
operand_string = "CVT_D_S(" + operand_string + ")";
|
||||||
|
break;
|
||||||
|
case UnaryOpType::ConvertSFromD:
|
||||||
|
operand_string = "CVT_S_D(" + operand_string + ")";
|
||||||
|
break;
|
||||||
|
case UnaryOpType::ConvertDFromL:
|
||||||
|
operand_string = "CVT_D_L(" + operand_string + ")";
|
||||||
|
break;
|
||||||
|
case UnaryOpType::ConvertLFromD:
|
||||||
|
operand_string = "CVT_L_D(" + operand_string + ")";
|
||||||
|
break;
|
||||||
|
case UnaryOpType::ConvertSFromL:
|
||||||
|
operand_string = "CVT_S_L(" + operand_string + ")";
|
||||||
|
break;
|
||||||
|
case UnaryOpType::ConvertLFromS:
|
||||||
|
operand_string = "CVT_L_S(" + operand_string + ")";
|
||||||
|
break;
|
||||||
|
case UnaryOpType::TruncateWFromS:
|
||||||
|
operand_string = "TRUNC_W_S(" + operand_string + ")";
|
||||||
|
break;
|
||||||
|
case UnaryOpType::TruncateWFromD:
|
||||||
|
operand_string = "TRUNC_W_D(" + operand_string + ")";
|
||||||
|
break;
|
||||||
|
case UnaryOpType::RoundWFromS:
|
||||||
|
operand_string = "ROUND_W_S(" + operand_string + ")";
|
||||||
|
break;
|
||||||
|
case UnaryOpType::RoundWFromD:
|
||||||
|
operand_string = "ROUND_W_D(" + operand_string + ")";
|
||||||
|
break;
|
||||||
|
case UnaryOpType::CeilWFromS:
|
||||||
|
operand_string = "CEILF(" + operand_string + ")";
|
||||||
|
break;
|
||||||
|
case UnaryOpType::CeilWFromD:
|
||||||
|
operand_string = "CEIL(" + operand_string + ")";
|
||||||
|
break;
|
||||||
|
case UnaryOpType::FloorWFromS:
|
||||||
|
operand_string = "FLOORF(" + operand_string + ")";
|
||||||
|
break;
|
||||||
|
case UnaryOpType::FloorWFromD:
|
||||||
|
operand_string = "FLOOR(" + operand_string + ")";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void N64Recomp::LuajitGenerator::get_notation(BinaryOpType op_type, std::string& func_string, std::string& infix_string) const {
|
||||||
|
func_string = luajit_op_fields[static_cast<size_t>(op_type)].func_string;
|
||||||
|
infix_string = luajit_op_fields[static_cast<size_t>(op_type)].infix_string;
|
||||||
|
}
|
||||||
|
|
||||||
|
void N64Recomp::LuajitGenerator::get_binary_expr_string(BinaryOpType type, const BinaryOperands& operands, const InstructionContext& ctx, const std::string& output, std::string& expr_string) const {
|
||||||
|
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(operands.operands[0], operands.operand_operations[0], ctx, input_a);
|
||||||
|
get_operand_string(operands.operands[1], operands.operand_operations[1], ctx, input_b);
|
||||||
|
get_notation(type, func_string, infix_string);
|
||||||
|
|
||||||
|
if (!func_string.empty() && !infix_string.empty()) {
|
||||||
|
expr_string = fmt::format("{}({} {} {})", func_string, input_a, infix_string, input_b);
|
||||||
|
}
|
||||||
|
else if (!func_string.empty()) {
|
||||||
|
expr_string = fmt::format("{}({}, {})", func_string, input_a, input_b);
|
||||||
|
}
|
||||||
|
else if (!infix_string.empty()) {
|
||||||
|
expr_string = fmt::format("{} {} {}", input_a, infix_string, input_b);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// Handle special cases
|
||||||
|
if (type == BinaryOpType::True) {
|
||||||
|
expr_string = "true";
|
||||||
|
}
|
||||||
|
else if (type == BinaryOpType::False) {
|
||||||
|
expr_string = "false";
|
||||||
|
}
|
||||||
|
assert(false && "Binary operation must have either a function or infix!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void N64Recomp::LuajitGenerator::process_binary_op(const BinaryOp& op, const InstructionContext& ctx) const {
|
||||||
|
// 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 expression{};
|
||||||
|
get_operand_string(op.output, UnaryOpType::None, ctx, output);
|
||||||
|
get_binary_expr_string(op.type, op.operands, ctx, output, expression);
|
||||||
|
|
||||||
|
// Explicitly convert coprocessor 1 compare results into a number to ensure comparisons work correctly with it.
|
||||||
|
if (op.output == Operand::Cop1cs) {
|
||||||
|
expression = "BOOL_TO_NUM(" + expression + ")";
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt::print(output_file, "{} = {}\n", output, expression);
|
||||||
|
}
|
||||||
|
|
||||||
|
void N64Recomp::LuajitGenerator::process_unary_op(const UnaryOp& op, const InstructionContext& ctx) const {
|
||||||
|
// 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{};
|
||||||
|
bool is_infix;
|
||||||
|
get_operand_string(op.output, UnaryOpType::None, ctx, output);
|
||||||
|
get_operand_string(op.input, op.operation, ctx, input);
|
||||||
|
fmt::print(output_file, "{} = {}\n", output, input);
|
||||||
|
}
|
||||||
|
|
||||||
|
void N64Recomp::LuajitGenerator::process_store_op(const StoreOp& op, const InstructionContext& ctx) const {
|
||||||
|
// 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 base_str{};
|
||||||
|
thread_local std::string imm_str{};
|
||||||
|
thread_local std::string value_input{};
|
||||||
|
bool is_infix;
|
||||||
|
get_operand_string(Operand::Base, UnaryOpType::None, ctx, base_str);
|
||||||
|
get_operand_string(Operand::ImmS16, UnaryOpType::None, ctx, imm_str);
|
||||||
|
get_operand_string(op.value_input, UnaryOpType::None, ctx, value_input);
|
||||||
|
|
||||||
|
std::string func_text;
|
||||||
|
|
||||||
|
switch (op.type) {
|
||||||
|
case StoreOpType::SD:
|
||||||
|
func_text = "MEM_SD";
|
||||||
|
break;
|
||||||
|
case StoreOpType::SDL:
|
||||||
|
func_text = "MEM_SDL";
|
||||||
|
break;
|
||||||
|
case StoreOpType::SDR:
|
||||||
|
func_text = "MEM_SDR";
|
||||||
|
break;
|
||||||
|
case StoreOpType::SW:
|
||||||
|
func_text = "MEM_SW";
|
||||||
|
break;
|
||||||
|
case StoreOpType::SWL:
|
||||||
|
func_text = "MEM_SWL";
|
||||||
|
break;
|
||||||
|
case StoreOpType::SWR:
|
||||||
|
func_text = "MEM_SWR";
|
||||||
|
break;
|
||||||
|
case StoreOpType::SH:
|
||||||
|
func_text = "MEM_SH";
|
||||||
|
break;
|
||||||
|
case StoreOpType::SB:
|
||||||
|
func_text = "MEM_SB";
|
||||||
|
break;
|
||||||
|
case StoreOpType::SDC1:
|
||||||
|
func_text = "MEM_SD";
|
||||||
|
break;
|
||||||
|
case StoreOpType::SWC1:
|
||||||
|
func_text = "MEM_SW";
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw std::runtime_error("Unhandled store op");
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt::print(output_file, "{}(rdram, {}, {}, {});\n", func_text, imm_str, base_str, value_input);
|
||||||
|
}
|
||||||
|
|
||||||
|
void N64Recomp::LuajitGenerator::emit_function_start(const std::string& function_name) const {
|
||||||
|
fmt::print(output_file,
|
||||||
|
"function {}(rdram, ctx)\n"
|
||||||
|
// these variables shouldn't need to be preserved across function boundaries, so make them local for more efficient output
|
||||||
|
" local hi = 0ULL\n"
|
||||||
|
" local lo = 0ULL\n"
|
||||||
|
" local result = 0ULL\n"
|
||||||
|
" local rounding_mode = DEFAULT_ROUNDING_MODE\n"
|
||||||
|
" local c1cs = 0ULL\n", // cop1 conditional signal
|
||||||
|
function_name);
|
||||||
|
}
|
||||||
|
|
||||||
|
void N64Recomp::LuajitGenerator::emit_function_end() const {
|
||||||
fmt::print(output_file, "end\n");
|
fmt::print(output_file, "end\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
void N64Recomp::LuajitGenerator::emit_function_call_lookup(std::ostream& output_file, uint32_t addr) const {
|
void N64Recomp::LuajitGenerator::emit_function_call_lookup(uint32_t addr) const {
|
||||||
// TODO
|
// TODO
|
||||||
fmt::print(output_file, "\n");
|
fmt::print(output_file, "\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
void N64Recomp::LuajitGenerator::emit_function_call_by_register(std::ostream& output_file, int reg) const {
|
void N64Recomp::LuajitGenerator::emit_function_call_by_register(int reg) const {
|
||||||
// TODO
|
// TODO
|
||||||
fmt::print(output_file, "\n");
|
fmt::print(output_file, "\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
void N64Recomp::LuajitGenerator::emit_function_call_by_name(std::ostream& output_file, const std::string& func_name) const {
|
void N64Recomp::LuajitGenerator::emit_function_call_by_name(const std::string& func_name) const {
|
||||||
// TODO
|
// TODO
|
||||||
fmt::print(output_file, "\n");
|
fmt::print(output_file, "\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
void N64Recomp::LuajitGenerator::emit_goto(std::ostream& output_file, const std::string& target) const {
|
void N64Recomp::LuajitGenerator::emit_goto(const std::string& target) const {
|
||||||
fmt::print(output_file, "goto {}\n", target);
|
fmt::print(output_file, "goto {}\n", target);
|
||||||
}
|
}
|
||||||
|
|
||||||
void N64Recomp::LuajitGenerator::emit_label(std::ostream& output_file, const std::string& label_name) const {
|
void N64Recomp::LuajitGenerator::emit_label(const std::string& label_name) const {
|
||||||
fmt::print(output_file, "::{}::\n", label_name);
|
fmt::print(output_file, "::{}::\n", label_name);
|
||||||
}
|
}
|
||||||
|
|
||||||
void N64Recomp::LuajitGenerator::emit_variable_declaration(std::ostream& output_file, const std::string& var_name, int reg) const {
|
void N64Recomp::LuajitGenerator::emit_variable_declaration(const std::string& var_name, int reg) const {
|
||||||
// TODO
|
// TODO
|
||||||
fmt::print(output_file, "{} = 0\n", var_name);
|
fmt::print(output_file, "{} = 0\n", var_name);
|
||||||
}
|
}
|
||||||
|
|
||||||
void N64Recomp::LuajitGenerator::emit_branch_condition(std::ostream& output_file, const ConditionalBranchOp& op, const InstructionContext& ctx) const {
|
void N64Recomp::LuajitGenerator::emit_branch_condition(const ConditionalBranchOp& op, const InstructionContext& ctx) const {
|
||||||
// TODO
|
// Thread local variables to prevent allocations when possible.
|
||||||
fmt::print(output_file, "if (true) then\n");
|
// TODO these thread locals probably don't actually help right now, so figure out a better way to prevent allocations.
|
||||||
|
thread_local std::string expr_string{};
|
||||||
|
get_binary_expr_string(op.comparison, op.operands, ctx, "", expr_string);
|
||||||
|
fmt::print(output_file, "if {} then\n", expr_string);
|
||||||
}
|
}
|
||||||
|
|
||||||
void N64Recomp::LuajitGenerator::emit_branch_close(std::ostream& output_file) const {
|
void N64Recomp::LuajitGenerator::emit_branch_close() const {
|
||||||
fmt::print(output_file, "end\n");
|
fmt::print(output_file, "end\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
void N64Recomp::LuajitGenerator::emit_switch(std::ostream& output_file, const std::string& jump_variable, int shift_amount) const {
|
void N64Recomp::LuajitGenerator::emit_switch(const std::string& jump_variable, int shift_amount) const {
|
||||||
fmt::print(output_file, "do local case_index = bit.rshift({}, {}ULL)\n", jump_variable, shift_amount);
|
fmt::print(output_file, "do local case_index = bit.rshift({}, {}ULL)\n", jump_variable, shift_amount);
|
||||||
}
|
}
|
||||||
|
|
||||||
void N64Recomp::LuajitGenerator::emit_case(std::ostream& output_file, int case_index, const std::string& target_label) const {
|
void N64Recomp::LuajitGenerator::emit_case(int case_index, const std::string& target_label) const {
|
||||||
fmt::print(output_file, "if case_index == {} then goto {} end\n", case_index, target_label);
|
fmt::print(output_file, "if case_index == {} then goto {} end\n", case_index, target_label);
|
||||||
}
|
}
|
||||||
|
|
||||||
void N64Recomp::LuajitGenerator::emit_switch_error(std::ostream& output_file, uint32_t instr_vram, uint32_t jtbl_vram) const {
|
void N64Recomp::LuajitGenerator::emit_switch_error(uint32_t instr_vram, uint32_t jtbl_vram) const {
|
||||||
fmt::print(output_file, "switch_error(\'lua\', {:08X}, {:08X})\n", instr_vram, jtbl_vram);
|
fmt::print(output_file, "switch_error(\'lua\', {:08X}, {:08X})\n", instr_vram, jtbl_vram);
|
||||||
}
|
}
|
||||||
|
|
||||||
void N64Recomp::LuajitGenerator::emit_switch_close(std::ostream& output_file) const {
|
void N64Recomp::LuajitGenerator::emit_switch_close() const {
|
||||||
fmt::print(output_file, "end\n");
|
fmt::print(output_file, "end\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
void N64Recomp::LuajitGenerator::emit_return(std::ostream& output_file) const {
|
void N64Recomp::LuajitGenerator::emit_return() const {
|
||||||
fmt::print(output_file, "return\n");
|
// Wrap the retur in a do/end construct to prevent errors from statements after the return.
|
||||||
|
fmt::print(output_file, "do return end\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
void N64Recomp::LuajitGenerator::emit_check_fr(std::ostream& output_file, int fpr) const {
|
void N64Recomp::LuajitGenerator::emit_check_fr(int fpr) const {
|
||||||
// Not used
|
// Not used
|
||||||
}
|
}
|
||||||
|
|
||||||
void N64Recomp::LuajitGenerator::emit_check_nan(std::ostream& output_file, int fpr, bool is_double) const {
|
void N64Recomp::LuajitGenerator::emit_check_nan(int fpr, bool is_double) const {
|
||||||
// Not used
|
// Not used
|
||||||
}
|
}
|
||||||
|
|
||||||
void N64Recomp::LuajitGenerator::emit_cop0_status_read(std::ostream& output_file, int reg) const {
|
void N64Recomp::LuajitGenerator::emit_cop0_status_read(int reg) const {
|
||||||
// TODO
|
// TODO
|
||||||
fmt::print(output_file, "\n");
|
fmt::print(output_file, "\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
void N64Recomp::LuajitGenerator::emit_cop0_status_write(std::ostream& output_file, int reg) const {
|
void N64Recomp::LuajitGenerator::emit_cop0_status_write(int reg) const {
|
||||||
// TODO
|
// TODO
|
||||||
fmt::print(output_file, "\n");
|
fmt::print(output_file, "\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
void N64Recomp::LuajitGenerator::emit_cop1_cs_read(std::ostream& output_file, int reg) const {
|
void N64Recomp::LuajitGenerator::emit_cop1_cs_read(int reg) const {
|
||||||
// TODO
|
// TODO
|
||||||
fmt::print(output_file, "\n");
|
fmt::print(output_file, "\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
void N64Recomp::LuajitGenerator::emit_cop1_cs_write(std::ostream& output_file, int reg) const {
|
void N64Recomp::LuajitGenerator::emit_cop1_cs_write(int reg) const {
|
||||||
// TODO
|
// TODO
|
||||||
fmt::print(output_file, "\n");
|
fmt::print(output_file, "\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
void N64Recomp::LuajitGenerator::emit_muldiv(std::ostream& output_file, InstrId instr_id, int reg1, int reg2) const {
|
void N64Recomp::LuajitGenerator::emit_muldiv(InstrId instr_id, int reg1, int reg2) const {
|
||||||
// TODO
|
// TODO
|
||||||
fmt::print(output_file, "\n");
|
fmt::print(output_file, "\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
void N64Recomp::LuajitGenerator::emit_syscall(std::ostream& output_file, uint32_t instr_vram) const {
|
void N64Recomp::LuajitGenerator::emit_syscall(uint32_t instr_vram) const {
|
||||||
// TODO
|
// TODO
|
||||||
fmt::print(output_file, "\n");
|
fmt::print(output_file, "\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
void N64Recomp::LuajitGenerator::emit_do_break(std::ostream& output_file, uint32_t instr_vram) const {
|
void N64Recomp::LuajitGenerator::emit_do_break(uint32_t instr_vram) const {
|
||||||
// TODO
|
// TODO
|
||||||
fmt::print(output_file, "\n");
|
fmt::print(output_file, "\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
void N64Recomp::LuajitGenerator::emit_pause_self(std::ostream& output_file) const {
|
void N64Recomp::LuajitGenerator::emit_pause_self() const {
|
||||||
// TODO
|
// TODO
|
||||||
fmt::print(output_file, "\n");
|
fmt::print(output_file, "\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
void N64Recomp::LuajitGenerator::emit_trigger_event(std::ostream& output_file, size_t event_index) const {
|
void N64Recomp::LuajitGenerator::emit_trigger_event(size_t event_index) const {
|
||||||
// TODO
|
// TODO
|
||||||
fmt::print(output_file, "\n");
|
fmt::print(output_file, "\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
void N64Recomp::LuajitGenerator::emit_comment(std::ostream& output_file, const std::string& comment) const {
|
void N64Recomp::LuajitGenerator::emit_comment(const std::string& comment) const {
|
||||||
fmt::print(output_file, "-- {}\n", comment);
|
fmt::print(output_file, "-- {}\n", comment);
|
||||||
}
|
}
|
||||||
|
|
|
@ -134,11 +134,11 @@ bool process_instruction(GeneratorType& generator, const N64Recomp::Context& con
|
||||||
// Output a comment with the original instruction
|
// Output a comment with the original instruction
|
||||||
print_indent();
|
print_indent();
|
||||||
if (instr.isBranch() || instr.getUniqueId() == InstrId::cpu_j) {
|
if (instr.isBranch() || instr.getUniqueId() == InstrId::cpu_j) {
|
||||||
generator.emit_comment(output_file, fmt::format("0x{:08X}: {}", instr_vram, instr.disassemble(0, fmt::format("L_{:08X}", (uint32_t)instr.getBranchVramGeneric()))));
|
generator.emit_comment(fmt::format("0x{:08X}: {}", instr_vram, instr.disassemble(0, fmt::format("L_{:08X}", (uint32_t)instr.getBranchVramGeneric()))));
|
||||||
} else if (instr.getUniqueId() == InstrId::cpu_jal) {
|
} else if (instr.getUniqueId() == InstrId::cpu_jal) {
|
||||||
generator.emit_comment(output_file, fmt::format("0x{:08X}: {}", instr_vram, instr.disassemble(0, fmt::format("0x{:08X}", (uint32_t)instr.getBranchVramGeneric()))));
|
generator.emit_comment(fmt::format("0x{:08X}: {}", instr_vram, instr.disassemble(0, fmt::format("0x{:08X}", (uint32_t)instr.getBranchVramGeneric()))));
|
||||||
} else {
|
} else {
|
||||||
generator.emit_comment(output_file, fmt::format("0x{:08X}: {}", instr_vram, instr.disassemble(0)));
|
generator.emit_comment(fmt::format("0x{:08X}: {}", instr_vram, instr.disassemble(0)));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (skipped_insns.contains(instr_vram)) {
|
if (skipped_insns.contains(instr_vram)) {
|
||||||
|
@ -226,7 +226,7 @@ bool process_instruction(GeneratorType& generator, const N64Recomp::Context& con
|
||||||
auto print_link_branch = [&]() {
|
auto print_link_branch = [&]() {
|
||||||
if (needs_link_branch) {
|
if (needs_link_branch) {
|
||||||
print_indent();
|
print_indent();
|
||||||
generator.emit_goto(output_file, fmt::format("after_{}", link_branch_index));
|
generator.emit_goto(fmt::format("after_{}", link_branch_index));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -235,7 +235,7 @@ bool process_instruction(GeneratorType& generator, const N64Recomp::Context& con
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
print_indent();
|
print_indent();
|
||||||
generator.emit_return(output_file);
|
generator.emit_return();
|
||||||
print_link_branch();
|
print_link_branch();
|
||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
|
@ -245,7 +245,7 @@ bool process_instruction(GeneratorType& generator, const N64Recomp::Context& con
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
print_indent();
|
print_indent();
|
||||||
generator.emit_goto(output_file, target);
|
generator.emit_goto(target);
|
||||||
print_link_branch();
|
print_link_branch();
|
||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
|
@ -255,7 +255,7 @@ bool process_instruction(GeneratorType& generator, const N64Recomp::Context& con
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
print_indent();
|
print_indent();
|
||||||
generator.emit_function_call_by_register(output_file, reg);
|
generator.emit_function_call_by_register(reg);
|
||||||
print_link_branch();
|
print_link_branch();
|
||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
|
@ -265,7 +265,7 @@ bool process_instruction(GeneratorType& generator, const N64Recomp::Context& con
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
print_indent();
|
print_indent();
|
||||||
generator.emit_function_call_lookup(output_file, target_vram);
|
generator.emit_function_call_lookup(target_vram);
|
||||||
print_link_branch();
|
print_link_branch();
|
||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
|
@ -284,7 +284,7 @@ bool process_instruction(GeneratorType& generator, const N64Recomp::Context& con
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
print_indent();
|
print_indent();
|
||||||
generator.emit_trigger_event(output_file, reloc_reference_symbol);
|
generator.emit_trigger_event(reloc_reference_symbol);
|
||||||
print_link_branch();
|
print_link_branch();
|
||||||
}
|
}
|
||||||
// Normal symbol or reference symbol,
|
// Normal symbol or reference symbol,
|
||||||
|
@ -342,10 +342,10 @@ bool process_instruction(GeneratorType& generator, const N64Recomp::Context& con
|
||||||
}
|
}
|
||||||
print_indent();
|
print_indent();
|
||||||
if (call_by_lookup) {
|
if (call_by_lookup) {
|
||||||
generator.emit_function_call_lookup(output_file, target_func_vram);
|
generator.emit_function_call_lookup(target_func_vram);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
generator.emit_function_call_by_name(output_file, jal_target_name);
|
generator.emit_function_call_by_name(jal_target_name);
|
||||||
}
|
}
|
||||||
print_link_branch();
|
print_link_branch();
|
||||||
}
|
}
|
||||||
|
@ -363,9 +363,9 @@ bool process_instruction(GeneratorType& generator, const N64Recomp::Context& con
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
print_indent();
|
print_indent();
|
||||||
generator.emit_return(output_file);
|
generator.emit_return();
|
||||||
print_indent();
|
print_indent();
|
||||||
generator.emit_branch_close(output_file);
|
generator.emit_branch_close();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -378,11 +378,11 @@ bool process_instruction(GeneratorType& generator, const N64Recomp::Context& con
|
||||||
|
|
||||||
print_indent();
|
print_indent();
|
||||||
print_indent();
|
print_indent();
|
||||||
generator.emit_goto(output_file, fmt::format("L_{:08X}", branch_target));
|
generator.emit_goto(fmt::format("L_{:08X}", branch_target));
|
||||||
if (needs_link_branch) {
|
if (needs_link_branch) {
|
||||||
print_indent();
|
print_indent();
|
||||||
print_indent();
|
print_indent();
|
||||||
generator.emit_goto(output_file, fmt::format("after_{}", link_branch_index));
|
generator.emit_goto(fmt::format("after_{}", link_branch_index));
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
|
@ -416,7 +416,7 @@ bool process_instruction(GeneratorType& generator, const N64Recomp::Context& con
|
||||||
switch (reg) {
|
switch (reg) {
|
||||||
case Cop0Reg::COP0_Status:
|
case Cop0Reg::COP0_Status:
|
||||||
print_indent();
|
print_indent();
|
||||||
generator.emit_cop0_status_read(output_file, rt);
|
generator.emit_cop0_status_read(rt);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
fmt::print(stderr, "Unhandled cop0 register in mfc0: {}\n", (int)reg);
|
fmt::print(stderr, "Unhandled cop0 register in mfc0: {}\n", (int)reg);
|
||||||
|
@ -430,7 +430,7 @@ bool process_instruction(GeneratorType& generator, const N64Recomp::Context& con
|
||||||
switch (reg) {
|
switch (reg) {
|
||||||
case Cop0Reg::COP0_Status:
|
case Cop0Reg::COP0_Status:
|
||||||
print_indent();
|
print_indent();
|
||||||
generator.emit_cop0_status_write(output_file, rt);
|
generator.emit_cop0_status_write(rt);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
fmt::print(stderr, "Unhandled cop0 register in mtc0: {}\n", (int)reg);
|
fmt::print(stderr, "Unhandled cop0 register in mtc0: {}\n", (int)reg);
|
||||||
|
@ -451,7 +451,7 @@ bool process_instruction(GeneratorType& generator, const N64Recomp::Context& con
|
||||||
if (find_result != stats.jump_tables.end()) {
|
if (find_result != stats.jump_tables.end()) {
|
||||||
const N64Recomp::JumpTable& cur_jtbl = *find_result;
|
const N64Recomp::JumpTable& cur_jtbl = *find_result;
|
||||||
print_indent();
|
print_indent();
|
||||||
generator.emit_variable_declaration(output_file, fmt::format("jr_addend_{:08X}", cur_jtbl.jr_vram), cur_jtbl.addend_reg);
|
generator.emit_variable_declaration(fmt::format("jr_addend_{:08X}", cur_jtbl.jr_vram), cur_jtbl.addend_reg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
@ -464,7 +464,7 @@ bool process_instruction(GeneratorType& generator, const N64Recomp::Context& con
|
||||||
case InstrId::cpu_divu:
|
case InstrId::cpu_divu:
|
||||||
case InstrId::cpu_ddivu:
|
case InstrId::cpu_ddivu:
|
||||||
print_indent();
|
print_indent();
|
||||||
generator.emit_muldiv(output_file, instr.getUniqueId(), rs, rt);
|
generator.emit_muldiv(instr.getUniqueId(), rs, rt);
|
||||||
break;
|
break;
|
||||||
// Branches
|
// Branches
|
||||||
case InstrId::cpu_jal:
|
case InstrId::cpu_jal:
|
||||||
|
@ -487,7 +487,7 @@ bool process_instruction(GeneratorType& generator, const N64Recomp::Context& con
|
||||||
uint32_t branch_target = instr.getBranchVramGeneric();
|
uint32_t branch_target = instr.getBranchVramGeneric();
|
||||||
if (branch_target == instr_vram) {
|
if (branch_target == instr_vram) {
|
||||||
print_indent();
|
print_indent();
|
||||||
generator.emit_pause_self(output_file);
|
generator.emit_pause_self();
|
||||||
}
|
}
|
||||||
// Check if the branch is within this function
|
// Check if the branch is within this function
|
||||||
else if (branch_target >= func.vram && branch_target < func_vram_end) {
|
else if (branch_target >= func.vram && branch_target < func_vram_end) {
|
||||||
|
@ -511,7 +511,7 @@ bool process_instruction(GeneratorType& generator, const N64Recomp::Context& con
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
print_indent();
|
print_indent();
|
||||||
generator.emit_return(output_file);
|
generator.emit_return();
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
fmt::print(stderr, "Unhandled branch in {} at 0x{:08X} to 0x{:08X}\n", func.name, instr_vram, branch_target);
|
fmt::print(stderr, "Unhandled branch in {} at 0x{:08X} to 0x{:08X}\n", func.name, instr_vram, branch_target);
|
||||||
|
@ -534,37 +534,37 @@ bool process_instruction(GeneratorType& generator, const N64Recomp::Context& con
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
print_indent();
|
print_indent();
|
||||||
generator.emit_switch(output_file, fmt::format("jr_addend_{:08X}", cur_jtbl.jr_vram), 2);
|
generator.emit_switch(fmt::format("jr_addend_{:08X}", cur_jtbl.jr_vram), 2);
|
||||||
for (size_t entry_index = 0; entry_index < cur_jtbl.entries.size(); entry_index++) {
|
for (size_t entry_index = 0; entry_index < cur_jtbl.entries.size(); entry_index++) {
|
||||||
print_indent();
|
print_indent();
|
||||||
print_indent();
|
print_indent();
|
||||||
generator.emit_case(output_file, entry_index, fmt::format("L_{:08X}", cur_jtbl.entries[entry_index]));
|
generator.emit_case(entry_index, fmt::format("L_{:08X}", cur_jtbl.entries[entry_index]));
|
||||||
}
|
}
|
||||||
print_indent();
|
print_indent();
|
||||||
print_indent();
|
print_indent();
|
||||||
generator.emit_switch_error(output_file, instr_vram, cur_jtbl.vram);
|
generator.emit_switch_error(instr_vram, cur_jtbl.vram);
|
||||||
print_indent();
|
print_indent();
|
||||||
generator.emit_switch_close(output_file);
|
generator.emit_switch_close();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt::print("[Info] Indirect tail call in {}\n", func.name);
|
fmt::print("[Info] Indirect tail call in {}\n", func.name);
|
||||||
print_func_call_by_register(rs);
|
print_func_call_by_register(rs);
|
||||||
print_indent();
|
print_indent();
|
||||||
generator.emit_return(output_file);
|
generator.emit_return();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case InstrId::cpu_syscall:
|
case InstrId::cpu_syscall:
|
||||||
print_indent();
|
print_indent();
|
||||||
generator.emit_syscall(output_file, instr_vram);
|
generator.emit_syscall(instr_vram);
|
||||||
// syscalls don't link, so treat it like a tail call
|
// syscalls don't link, so treat it like a tail call
|
||||||
print_indent();
|
print_indent();
|
||||||
generator.emit_return(output_file);
|
generator.emit_return();
|
||||||
break;
|
break;
|
||||||
case InstrId::cpu_break:
|
case InstrId::cpu_break:
|
||||||
print_indent();
|
print_indent();
|
||||||
generator.emit_do_break(output_file, instr_vram);
|
generator.emit_do_break(instr_vram);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
// Cop1 rounding mode
|
// Cop1 rounding mode
|
||||||
|
@ -574,7 +574,7 @@ bool process_instruction(GeneratorType& generator, const N64Recomp::Context& con
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
print_indent();
|
print_indent();
|
||||||
generator.emit_cop1_cs_write(output_file, rt);
|
generator.emit_cop1_cs_write(rt);
|
||||||
break;
|
break;
|
||||||
case InstrId::cpu_cfc1:
|
case InstrId::cpu_cfc1:
|
||||||
if (cop1_cs != 31) {
|
if (cop1_cs != 31) {
|
||||||
|
@ -582,7 +582,7 @@ bool process_instruction(GeneratorType& generator, const N64Recomp::Context& con
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
print_indent();
|
print_indent();
|
||||||
generator.emit_cop1_cs_read(output_file, rt);
|
generator.emit_cop1_cs_read(rt);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
handled = false;
|
handled = false;
|
||||||
|
@ -604,28 +604,28 @@ bool process_instruction(GeneratorType& generator, const N64Recomp::Context& con
|
||||||
instruction_context.reloc_section_index = reloc_section;
|
instruction_context.reloc_section_index = reloc_section;
|
||||||
instruction_context.reloc_target_section_offset = reloc_target_section_offset;
|
instruction_context.reloc_target_section_offset = reloc_target_section_offset;
|
||||||
|
|
||||||
auto do_check_fr = [](std::ostream& output_file, const GeneratorType& generator, const InstructionContext& ctx, Operand operand) {
|
auto do_check_fr = [](const GeneratorType& generator, const InstructionContext& ctx, Operand operand) {
|
||||||
switch (operand) {
|
switch (operand) {
|
||||||
case Operand::Fd:
|
case Operand::Fd:
|
||||||
case Operand::FdDouble:
|
case Operand::FdDouble:
|
||||||
case Operand::FdU32L:
|
case Operand::FdU32L:
|
||||||
case Operand::FdU32H:
|
case Operand::FdU32H:
|
||||||
case Operand::FdU64:
|
case Operand::FdU64:
|
||||||
generator.emit_check_fr(output_file, ctx.fd);
|
generator.emit_check_fr(ctx.fd);
|
||||||
break;
|
break;
|
||||||
case Operand::Fs:
|
case Operand::Fs:
|
||||||
case Operand::FsDouble:
|
case Operand::FsDouble:
|
||||||
case Operand::FsU32L:
|
case Operand::FsU32L:
|
||||||
case Operand::FsU32H:
|
case Operand::FsU32H:
|
||||||
case Operand::FsU64:
|
case Operand::FsU64:
|
||||||
generator.emit_check_fr(output_file, ctx.fs);
|
generator.emit_check_fr(ctx.fs);
|
||||||
break;
|
break;
|
||||||
case Operand::Ft:
|
case Operand::Ft:
|
||||||
case Operand::FtDouble:
|
case Operand::FtDouble:
|
||||||
case Operand::FtU32L:
|
case Operand::FtU32L:
|
||||||
case Operand::FtU32H:
|
case Operand::FtU32H:
|
||||||
case Operand::FtU64:
|
case Operand::FtU64:
|
||||||
generator.emit_check_fr(output_file, ctx.ft);
|
generator.emit_check_fr(ctx.ft);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
// No MIPS3 float check needed for non-float operands.
|
// No MIPS3 float check needed for non-float operands.
|
||||||
|
@ -633,25 +633,25 @@ bool process_instruction(GeneratorType& generator, const N64Recomp::Context& con
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
auto do_check_nan = [](std::ostream& output_file, const GeneratorType& generator, const InstructionContext& ctx, Operand operand) {
|
auto do_check_nan = [](const GeneratorType& generator, const InstructionContext& ctx, Operand operand) {
|
||||||
switch (operand) {
|
switch (operand) {
|
||||||
case Operand::Fd:
|
case Operand::Fd:
|
||||||
generator.emit_check_nan(output_file, ctx.fd, false);
|
generator.emit_check_nan(ctx.fd, false);
|
||||||
break;
|
break;
|
||||||
case Operand::Fs:
|
case Operand::Fs:
|
||||||
generator.emit_check_nan(output_file, ctx.fs, false);
|
generator.emit_check_nan(ctx.fs, false);
|
||||||
break;
|
break;
|
||||||
case Operand::Ft:
|
case Operand::Ft:
|
||||||
generator.emit_check_nan(output_file, ctx.ft, false);
|
generator.emit_check_nan(ctx.ft, false);
|
||||||
break;
|
break;
|
||||||
case Operand::FdDouble:
|
case Operand::FdDouble:
|
||||||
generator.emit_check_nan(output_file, ctx.fd, true);
|
generator.emit_check_nan(ctx.fd, true);
|
||||||
break;
|
break;
|
||||||
case Operand::FsDouble:
|
case Operand::FsDouble:
|
||||||
generator.emit_check_nan(output_file, ctx.fs, true);
|
generator.emit_check_nan(ctx.fs, true);
|
||||||
break;
|
break;
|
||||||
case Operand::FtDouble:
|
case Operand::FtDouble:
|
||||||
generator.emit_check_nan(output_file, ctx.ft, true);
|
generator.emit_check_nan(ctx.ft, true);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
// No NaN checks needed for non-float operands.
|
// No NaN checks needed for non-float operands.
|
||||||
|
@ -665,19 +665,19 @@ bool process_instruction(GeneratorType& generator, const N64Recomp::Context& con
|
||||||
const BinaryOp& op = find_binary_it->second;
|
const BinaryOp& op = find_binary_it->second;
|
||||||
|
|
||||||
if (op.check_fr) {
|
if (op.check_fr) {
|
||||||
do_check_fr(output_file, generator, instruction_context, op.output);
|
do_check_fr(generator, instruction_context, op.output);
|
||||||
do_check_fr(output_file, generator, instruction_context, op.operands.operands[0]);
|
do_check_fr(generator, instruction_context, op.operands.operands[0]);
|
||||||
do_check_fr(output_file, generator, instruction_context, op.operands.operands[1]);
|
do_check_fr(generator, instruction_context, op.operands.operands[1]);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (op.check_nan) {
|
if (op.check_nan) {
|
||||||
do_check_nan(output_file, generator, instruction_context, op.operands.operands[0]);
|
do_check_nan(generator, instruction_context, op.operands.operands[0]);
|
||||||
do_check_nan(output_file, generator, instruction_context, op.operands.operands[1]);
|
do_check_nan(generator, instruction_context, op.operands.operands[1]);
|
||||||
fmt::print(output_file, "\n");
|
fmt::print(output_file, "\n");
|
||||||
print_indent();
|
print_indent();
|
||||||
}
|
}
|
||||||
|
|
||||||
generator.process_binary_op(output_file, op, instruction_context);
|
generator.process_binary_op(op, instruction_context);
|
||||||
handled = true;
|
handled = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -687,24 +687,24 @@ bool process_instruction(GeneratorType& generator, const N64Recomp::Context& con
|
||||||
const UnaryOp& op = find_unary_it->second;
|
const UnaryOp& op = find_unary_it->second;
|
||||||
|
|
||||||
if (op.check_fr) {
|
if (op.check_fr) {
|
||||||
do_check_fr(output_file, generator, instruction_context, op.output);
|
do_check_fr(generator, instruction_context, op.output);
|
||||||
do_check_fr(output_file, generator, instruction_context, op.input);
|
do_check_fr(generator, instruction_context, op.input);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (op.check_nan) {
|
if (op.check_nan) {
|
||||||
do_check_nan(output_file, generator, instruction_context, op.input);
|
do_check_nan(generator, instruction_context, op.input);
|
||||||
fmt::print(output_file, "\n");
|
fmt::print(output_file, "\n");
|
||||||
print_indent();
|
print_indent();
|
||||||
}
|
}
|
||||||
|
|
||||||
generator.process_unary_op(output_file, op, instruction_context);
|
generator.process_unary_op(op, instruction_context);
|
||||||
handled = true;
|
handled = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto find_conditional_branch_it = conditional_branch_ops.find(instr.getUniqueId());
|
auto find_conditional_branch_it = conditional_branch_ops.find(instr.getUniqueId());
|
||||||
if (find_conditional_branch_it != conditional_branch_ops.end()) {
|
if (find_conditional_branch_it != conditional_branch_ops.end()) {
|
||||||
print_indent();
|
print_indent();
|
||||||
generator.emit_branch_condition(output_file, find_conditional_branch_it->second, instruction_context);
|
generator.emit_branch_condition(find_conditional_branch_it->second, instruction_context);
|
||||||
|
|
||||||
print_indent();
|
print_indent();
|
||||||
if (find_conditional_branch_it->second.link) {
|
if (find_conditional_branch_it->second.link) {
|
||||||
|
@ -719,7 +719,7 @@ bool process_instruction(GeneratorType& generator, const N64Recomp::Context& con
|
||||||
}
|
}
|
||||||
|
|
||||||
print_indent();
|
print_indent();
|
||||||
generator.emit_branch_close(output_file);
|
generator.emit_branch_close();
|
||||||
|
|
||||||
is_branch_likely = find_conditional_branch_it->second.likely;
|
is_branch_likely = find_conditional_branch_it->second.likely;
|
||||||
handled = true;
|
handled = true;
|
||||||
|
@ -731,10 +731,10 @@ bool process_instruction(GeneratorType& generator, const N64Recomp::Context& con
|
||||||
const StoreOp& op = find_store_it->second;
|
const StoreOp& op = find_store_it->second;
|
||||||
|
|
||||||
if (op.type == StoreOpType::SDC1) {
|
if (op.type == StoreOpType::SDC1) {
|
||||||
do_check_fr(output_file, generator, instruction_context, op.value_input);
|
do_check_fr(generator, instruction_context, op.value_input);
|
||||||
}
|
}
|
||||||
|
|
||||||
generator.process_store_op(output_file, op, instruction_context);
|
generator.process_store_op(op, instruction_context);
|
||||||
handled = true;
|
handled = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -746,7 +746,7 @@ bool process_instruction(GeneratorType& generator, const N64Recomp::Context& con
|
||||||
// TODO is this used?
|
// TODO is this used?
|
||||||
if (emit_link_branch) {
|
if (emit_link_branch) {
|
||||||
print_indent();
|
print_indent();
|
||||||
generator.emit_label(output_file, fmt::format("after_{}", link_branch_index));
|
generator.emit_label(fmt::format("after_{}", link_branch_index));
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
@ -757,7 +757,7 @@ bool recompile_function_impl(GeneratorType& generator, const N64Recomp::Context&
|
||||||
//fmt::print("Recompiling {}\n", func.name);
|
//fmt::print("Recompiling {}\n", func.name);
|
||||||
std::vector<rabbitizer::InstructionCpu> instructions;
|
std::vector<rabbitizer::InstructionCpu> instructions;
|
||||||
|
|
||||||
generator.emit_function_start(output_file, func.name);
|
generator.emit_function_start(func.name);
|
||||||
|
|
||||||
// Skip analysis and recompilation of this function is stubbed.
|
// Skip analysis and recompilation of this function is stubbed.
|
||||||
if (!func.stubbed) {
|
if (!func.stubbed) {
|
||||||
|
@ -816,11 +816,11 @@ bool recompile_function_impl(GeneratorType& generator, const N64Recomp::Context&
|
||||||
bool is_branch_likely = false;
|
bool is_branch_likely = false;
|
||||||
// If we're in the delay slot of a likely instruction, emit a goto to skip the instruction before any labels
|
// If we're in the delay slot of a likely instruction, emit a goto to skip the instruction before any labels
|
||||||
if (in_likely_delay_slot) {
|
if (in_likely_delay_slot) {
|
||||||
generator.emit_goto(output_file, fmt::format("skip_{}", num_likely_branches));
|
generator.emit_goto(fmt::format("skip_{}", num_likely_branches));
|
||||||
}
|
}
|
||||||
// If there are any other branch labels to insert and we're at the next one, insert it
|
// If there are any other branch labels to insert and we're at the next one, insert it
|
||||||
if (cur_label != branch_labels.end() && vram >= *cur_label) {
|
if (cur_label != branch_labels.end() && vram >= *cur_label) {
|
||||||
generator.emit_label(output_file, fmt::format("L_{:08X}", *cur_label));
|
generator.emit_label(fmt::format("L_{:08X}", *cur_label));
|
||||||
++cur_label;
|
++cur_label;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -842,7 +842,7 @@ bool recompile_function_impl(GeneratorType& generator, const N64Recomp::Context&
|
||||||
// Now that the instruction has been processed, emit a skip label for the likely branch if needed
|
// Now that the instruction has been processed, emit a skip label for the likely branch if needed
|
||||||
if (in_likely_delay_slot) {
|
if (in_likely_delay_slot) {
|
||||||
fmt::print(output_file, " ");
|
fmt::print(output_file, " ");
|
||||||
generator.emit_label(output_file, fmt::format("skip_{}", num_likely_branches));
|
generator.emit_label(fmt::format("skip_{}", num_likely_branches));
|
||||||
num_likely_branches++;
|
num_likely_branches++;
|
||||||
}
|
}
|
||||||
// Mark the next instruction as being in a likely delay slot if the
|
// Mark the next instruction as being in a likely delay slot if the
|
||||||
|
@ -853,18 +853,18 @@ bool recompile_function_impl(GeneratorType& generator, const N64Recomp::Context&
|
||||||
}
|
}
|
||||||
|
|
||||||
// Terminate the function
|
// Terminate the function
|
||||||
generator.emit_function_end(output_file);
|
generator.emit_function_end();
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Wrap the templated function with CGenerator as the template parameter.
|
// Wrap the templated function with CGenerator as the template parameter.
|
||||||
bool N64Recomp::recompile_function(const N64Recomp::Context& context, const N64Recomp::Function& func, std::ofstream& output_file, std::span<std::vector<uint32_t>> static_funcs_out, bool tag_reference_relocs) {
|
bool N64Recomp::recompile_function(const N64Recomp::Context& context, const N64Recomp::Function& func, std::ofstream& output_file, std::span<std::vector<uint32_t>> static_funcs_out, bool tag_reference_relocs) {
|
||||||
CGenerator generator{};
|
CGenerator generator{output_file};
|
||||||
return recompile_function_impl(generator, context, func, output_file, static_funcs_out, tag_reference_relocs);
|
return recompile_function_impl(generator, context, func, output_file, static_funcs_out, tag_reference_relocs);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool N64Recomp::recompile_function_luajit(const N64Recomp::Context& context, const N64Recomp::Function& func, std::ofstream& output_file, std::span<std::vector<uint32_t>> static_funcs_out, bool tag_reference_relocs) {
|
bool N64Recomp::recompile_function_luajit(const N64Recomp::Context& context, const N64Recomp::Function& func, std::ofstream& output_file, std::span<std::vector<uint32_t>> static_funcs_out, bool tag_reference_relocs) {
|
||||||
LuajitGenerator generator{};
|
LuajitGenerator generator{output_file};
|
||||||
return recompile_function_impl(generator, context, func, output_file, static_funcs_out, tag_reference_relocs);
|
return recompile_function_impl(generator, context, func, output_file, static_funcs_out, tag_reference_relocs);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue