Misc upgrades including mips3 float mode support, skip overwriting existing files if they're identical to the current recompiled output
This commit is contained in:
parent
9321a60f28
commit
d249363fe5
|
@ -4,6 +4,7 @@
|
||||||
#include <unordered_set>
|
#include <unordered_set>
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
|
#include <filesystem>
|
||||||
#include "rabbitizer.hpp"
|
#include "rabbitizer.hpp"
|
||||||
#include "fmt/format.h"
|
#include "fmt/format.h"
|
||||||
#include "fmt/ostream.h"
|
#include "fmt/ostream.h"
|
||||||
|
@ -187,7 +188,7 @@ BranchTargets get_branch_targets(const std::vector<rabbitizer::InstructionRsp>&
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool process_instruction(size_t instr_index, const std::vector<rabbitizer::InstructionRsp>& instructions, std::ofstream& output_file, const BranchTargets& branch_targets, bool indent, bool in_delay_slot) {
|
bool process_instruction(size_t instr_index, const std::vector<rabbitizer::InstructionRsp>& instructions, std::ofstream& output_file, const BranchTargets& branch_targets, const std::unordered_set<uint32_t>& unsupported_instructions, bool indent, bool in_delay_slot) {
|
||||||
const auto& instr = instructions[instr_index];
|
const auto& instr = instructions[instr_index];
|
||||||
|
|
||||||
uint32_t instr_vram = instr.getVram();
|
uint32_t instr_vram = instr.getVram();
|
||||||
|
@ -230,7 +231,7 @@ bool process_instruction(size_t instr_index, const std::vector<rabbitizer::Instr
|
||||||
auto print_unconditional_branch = [&]<typename... Ts>(fmt::format_string<Ts...> fmt_str, Ts ...args) {
|
auto print_unconditional_branch = [&]<typename... Ts>(fmt::format_string<Ts...> fmt_str, Ts ...args) {
|
||||||
if (instr_index < instructions.size() - 1) {
|
if (instr_index < instructions.size() - 1) {
|
||||||
uint32_t next_vram = instr_vram + 4;
|
uint32_t next_vram = instr_vram + 4;
|
||||||
process_instruction(instr_index + 1, instructions, output_file, branch_targets, false, true);
|
process_instruction(instr_index + 1, instructions, output_file, branch_targets, unsupported_instructions, false, true);
|
||||||
}
|
}
|
||||||
print_indent();
|
print_indent();
|
||||||
fmt::print(output_file, fmt_str, args...);
|
fmt::print(output_file, fmt_str, args...);
|
||||||
|
@ -241,7 +242,7 @@ bool process_instruction(size_t instr_index, const std::vector<rabbitizer::Instr
|
||||||
fmt::print(output_file, "{{\n ");
|
fmt::print(output_file, "{{\n ");
|
||||||
if (instr_index < instructions.size() - 1) {
|
if (instr_index < instructions.size() - 1) {
|
||||||
uint32_t next_vram = instr_vram + 4;
|
uint32_t next_vram = instr_vram + 4;
|
||||||
process_instruction(instr_index + 1, instructions, output_file, branch_targets, true, true);
|
process_instruction(instr_index + 1, instructions, output_file, branch_targets, unsupported_instructions, true, true);
|
||||||
}
|
}
|
||||||
fmt::print(output_file, " ");
|
fmt::print(output_file, " ");
|
||||||
fmt::print(output_file, fmt_str, args...);
|
fmt::print(output_file, fmt_str, args...);
|
||||||
|
@ -252,6 +253,14 @@ bool process_instruction(size_t instr_index, const std::vector<rabbitizer::Instr
|
||||||
print_indent();
|
print_indent();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Replace unsupported instructions with early returns
|
||||||
|
if (unsupported_instructions.contains(instr_vram)) {
|
||||||
|
print_line("return RspExitReason::Unsupported", instr_vram);
|
||||||
|
if (indent) {
|
||||||
|
print_indent();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
int rd = (int)instr.GetO32_rd();
|
int rd = (int)instr.GetO32_rd();
|
||||||
int rs = (int)instr.GetO32_rs();
|
int rs = (int)instr.GetO32_rs();
|
||||||
int base = rs;
|
int base = rs;
|
||||||
|
@ -337,6 +346,10 @@ bool process_instruction(size_t instr_index, const std::vector<rabbitizer::Instr
|
||||||
break;
|
break;
|
||||||
case InstrId::rsp_add:
|
case InstrId::rsp_add:
|
||||||
case InstrId::rsp_addu:
|
case InstrId::rsp_addu:
|
||||||
|
if (rd == 0) {
|
||||||
|
fmt::print(output_file, "\n");
|
||||||
|
break;
|
||||||
|
}
|
||||||
print_line("{}{} = RSP_ADD32({}{}, {}{})", ctx_gpr_prefix(rd), rd, ctx_gpr_prefix(rs), rs, ctx_gpr_prefix(rt), rt);
|
print_line("{}{} = RSP_ADD32({}{}, {}{})", ctx_gpr_prefix(rd), rd, ctx_gpr_prefix(rs), rs, ctx_gpr_prefix(rt), rt);
|
||||||
break;
|
break;
|
||||||
case InstrId::rsp_negu: // pseudo instruction for subu x, 0, y
|
case InstrId::rsp_negu: // pseudo instruction for subu x, 0, y
|
||||||
|
@ -349,6 +362,10 @@ bool process_instruction(size_t instr_index, const std::vector<rabbitizer::Instr
|
||||||
print_line("{}{} = RSP_ADD32({}{}, {})", ctx_gpr_prefix(rt), rt, ctx_gpr_prefix(rs), rs, signed_imm_string);
|
print_line("{}{} = RSP_ADD32({}{}, {})", ctx_gpr_prefix(rt), rt, ctx_gpr_prefix(rs), rs, signed_imm_string);
|
||||||
break;
|
break;
|
||||||
case InstrId::rsp_and:
|
case InstrId::rsp_and:
|
||||||
|
if (rd == 0) {
|
||||||
|
fmt::print(output_file, "\n");
|
||||||
|
break;
|
||||||
|
}
|
||||||
print_line("{}{} = {}{} & {}{}", ctx_gpr_prefix(rd), rd, ctx_gpr_prefix(rs), rs, ctx_gpr_prefix(rt), rt);
|
print_line("{}{} = {}{} & {}{}", ctx_gpr_prefix(rd), rd, ctx_gpr_prefix(rs), rs, ctx_gpr_prefix(rt), rt);
|
||||||
break;
|
break;
|
||||||
case InstrId::rsp_andi:
|
case InstrId::rsp_andi:
|
||||||
|
@ -518,6 +535,7 @@ void write_indirect_jumps(std::ofstream& output_file, const BranchTargets& branc
|
||||||
//std::string output_file_path = "../test/rsp/njpgdspMain.cpp";
|
//std::string output_file_path = "../test/rsp/njpgdspMain.cpp";
|
||||||
//std::string output_function_name = "njpgdspMain";
|
//std::string output_function_name = "njpgdspMain";
|
||||||
//const std::vector<uint32_t> extra_indirect_branch_targets{};
|
//const std::vector<uint32_t> extra_indirect_branch_targets{};
|
||||||
|
//const std::unordered_set<uint32_t> unsupported_instructions{};
|
||||||
|
|
||||||
// OoT aspMain
|
// OoT aspMain
|
||||||
//constexpr size_t rsp_text_offset = 0xB89260;
|
//constexpr size_t rsp_text_offset = 0xB89260;
|
||||||
|
@ -527,17 +545,35 @@ void write_indirect_jumps(std::ofstream& output_file, const BranchTargets& branc
|
||||||
//std::string output_file_path = "../test/rsp/aspMain.cpp";
|
//std::string output_file_path = "../test/rsp/aspMain.cpp";
|
||||||
//std::string output_function_name = "aspMain";
|
//std::string output_function_name = "aspMain";
|
||||||
//const std::vector<uint32_t> extra_indirect_branch_targets{ 0x1F68, 0x1230, 0x114C, 0x1F18, 0x1E2C, 0x14F4, 0x1E9C, 0x1CB0, 0x117C, 0x17CC, 0x11E8, 0x1AA4, 0x1B34, 0x1190, 0x1C5C, 0x1220, 0x1784, 0x1830, 0x1A20, 0x1884, 0x1A84, 0x1A94, 0x1A48, 0x1BA0 };
|
//const std::vector<uint32_t> extra_indirect_branch_targets{ 0x1F68, 0x1230, 0x114C, 0x1F18, 0x1E2C, 0x14F4, 0x1E9C, 0x1CB0, 0x117C, 0x17CC, 0x11E8, 0x1AA4, 0x1B34, 0x1190, 0x1C5C, 0x1220, 0x1784, 0x1830, 0x1A20, 0x1884, 0x1A84, 0x1A94, 0x1A48, 0x1BA0 };
|
||||||
|
//const std::unordered_set<uint32_t> unsupported_instructions{};
|
||||||
|
|
||||||
// MM's njpgdspMain is identical to OoT's
|
// MM's njpgdspMain is identical to OoT's
|
||||||
|
|
||||||
// MM aspMain
|
//// MM aspMain
|
||||||
constexpr size_t rsp_text_offset = 0xC40FF0;
|
//constexpr size_t rsp_text_offset = 0xC40FF0;
|
||||||
constexpr size_t rsp_text_size = 0x1000;
|
//constexpr size_t rsp_text_size = 0x1000;
|
||||||
constexpr size_t rsp_text_address = 0x04001000;
|
//constexpr size_t rsp_text_address = 0x04001000;
|
||||||
std::string rom_file_path = "../../MMRecomp/mm.us.rev1.z64"; // uncompressed rom!
|
//std::string rom_file_path = "../../MMRecomp/mm.us.rev1.z64"; // uncompressed rom!
|
||||||
std::string output_file_path = "../../MMRecomp/rsp/aspMain.cpp";
|
//std::string output_file_path = "../../MMRecomp/rsp/aspMain.cpp";
|
||||||
std::string output_function_name = "aspMain";
|
//std::string output_function_name = "aspMain";
|
||||||
const std::vector<uint32_t> extra_indirect_branch_targets{ 0x1F80, 0x1250, 0x1154, 0x1094, 0x1E0C, 0x1514, 0x1E7C, 0x1C90, 0x1180, 0x1808, 0x11E8, 0x1ADC, 0x1B6C, 0x1194, 0x1EF8, 0x1240, 0x17C0, 0x186C, 0x1A58, 0x18BC, 0x1ABC, 0x1ACC, 0x1A80, 0x1BD4 };
|
//const std::vector<uint32_t> extra_indirect_branch_targets{ 0x1F80, 0x1250, 0x1154, 0x1094, 0x1E0C, 0x1514, 0x1E7C, 0x1C90, 0x1180, 0x1808, 0x11E8, 0x1ADC, 0x1B6C, 0x1194, 0x1EF8, 0x1240, 0x17C0, 0x186C, 0x1A58, 0x18BC, 0x1ABC, 0x1ACC, 0x1A80, 0x1BD4 };
|
||||||
|
//const std::unordered_set<uint32_t> unsupported_instructions{};
|
||||||
|
|
||||||
|
// BT n_aspMain
|
||||||
|
constexpr size_t rsp_text_offset = 0x1E4F3B0;
|
||||||
|
constexpr size_t rsp_text_size = 0xF80;
|
||||||
|
constexpr size_t rsp_text_address = 0x04001080;
|
||||||
|
std::string rom_file_path = "../../BTRecomp/banjotooie.decompressed.us.z64"; // uncompressed rom!
|
||||||
|
std::string output_file_path = "../../BTRecomp/rsp/n_aspMain.cpp";
|
||||||
|
std::string output_function_name = "n_aspMain";
|
||||||
|
const std::vector<uint32_t> extra_indirect_branch_targets{
|
||||||
|
// dispatch table
|
||||||
|
0x1AE8, 0x143C, 0x1240, 0x1D84, 0x126C, 0x1B20, 0x12A8, 0x1214, 0x141C, 0x1310, 0x13CC, 0x12E4, 0x1FB0, 0x1358, 0x16EC, 0x1408
|
||||||
|
};
|
||||||
|
const std::unordered_set<uint32_t> unsupported_instructions{
|
||||||
|
// cmd_MP3
|
||||||
|
0x00001214
|
||||||
|
};
|
||||||
|
|
||||||
#ifdef _MSC_VER
|
#ifdef _MSC_VER
|
||||||
inline uint32_t byteswap(uint32_t val) {
|
inline uint32_t byteswap(uint32_t val) {
|
||||||
|
@ -589,6 +625,7 @@ int main() {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Open output file and write beginning
|
// Open output file and write beginning
|
||||||
|
std::filesystem::create_directories(std::filesystem::path{ output_file_path }.parent_path());
|
||||||
std::ofstream output_file(output_file_path);
|
std::ofstream output_file(output_file_path);
|
||||||
fmt::print(output_file,
|
fmt::print(output_file,
|
||||||
"#include \"rsp.h\"\n"
|
"#include \"rsp.h\"\n"
|
||||||
|
@ -603,7 +640,7 @@ int main() {
|
||||||
" r1 = 0xFC0;\n", output_function_name);
|
" r1 = 0xFC0;\n", output_function_name);
|
||||||
// Write each instruction
|
// Write each instruction
|
||||||
for (size_t instr_index = 0; instr_index < instrs.size(); instr_index++) {
|
for (size_t instr_index = 0; instr_index < instrs.size(); instr_index++) {
|
||||||
process_instruction(instr_index, instrs, output_file, branch_targets, false, false);
|
process_instruction(instr_index, instrs, output_file, branch_targets, unsupported_instructions, false, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Terminate instruction code with a return to indicate that the microcode has run past its end
|
// Terminate instruction code with a return to indicate that the microcode has run past its end
|
||||||
|
|
|
@ -39,14 +39,23 @@ namespace RecompPort {
|
||||||
uint32_t value;
|
uint32_t value;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct FunctionSize {
|
||||||
|
std::string func_name;
|
||||||
|
uint32_t size_bytes;
|
||||||
|
};
|
||||||
|
|
||||||
struct Config {
|
struct Config {
|
||||||
int32_t entrypoint;
|
int32_t entrypoint;
|
||||||
|
bool uses_mips3_float_mode;
|
||||||
std::filesystem::path elf_path;
|
std::filesystem::path elf_path;
|
||||||
std::filesystem::path output_func_path;
|
std::filesystem::path output_func_path;
|
||||||
std::filesystem::path relocatable_sections_path;
|
std::filesystem::path relocatable_sections_path;
|
||||||
std::vector<std::string> stubbed_funcs;
|
std::vector<std::string> stubbed_funcs;
|
||||||
|
std::vector<std::string> ignored_funcs;
|
||||||
DeclaredFunctionMap declared_funcs;
|
DeclaredFunctionMap declared_funcs;
|
||||||
std::vector<InstructionPatch> instruction_patches;
|
std::vector<InstructionPatch> instruction_patches;
|
||||||
|
std::vector<FunctionSize> manual_func_sizes;
|
||||||
|
std::string bss_section_suffix;
|
||||||
|
|
||||||
Config(const char* path);
|
Config(const char* path);
|
||||||
bool good() { return !bad; }
|
bool good() { return !bad; }
|
||||||
|
@ -107,6 +116,7 @@ namespace RecompPort {
|
||||||
std::vector<uint32_t> function_addrs;
|
std::vector<uint32_t> function_addrs;
|
||||||
std::vector<Reloc> relocs;
|
std::vector<Reloc> relocs;
|
||||||
std::string name;
|
std::string name;
|
||||||
|
ELFIO::Elf_Half bss_section_index = (ELFIO::Elf_Half)-1;
|
||||||
bool executable = false;
|
bool executable = false;
|
||||||
bool relocatable = false;
|
bool relocatable = false;
|
||||||
};
|
};
|
||||||
|
@ -128,6 +138,8 @@ namespace RecompPort {
|
||||||
std::vector<std::vector<size_t>> section_functions;
|
std::vector<std::vector<size_t>> section_functions;
|
||||||
// The section names that were specified as relocatable
|
// The section names that were specified as relocatable
|
||||||
std::unordered_set<std::string> relocatable_sections;
|
std::unordered_set<std::string> relocatable_sections;
|
||||||
|
// Functions with manual size overrides
|
||||||
|
std::unordered_map<std::string, size_t> manually_sized_funcs;
|
||||||
int executable_section_count;
|
int executable_section_count;
|
||||||
|
|
||||||
Context(const ELFIO::elfio& elf_file) {
|
Context(const ELFIO::elfio& elf_file) {
|
||||||
|
@ -142,7 +154,7 @@ namespace RecompPort {
|
||||||
};
|
};
|
||||||
|
|
||||||
bool analyze_function(const Context& context, const Function& function, const std::vector<rabbitizer::InstructionCpu>& instructions, FunctionStats& stats);
|
bool analyze_function(const Context& context, const Function& function, const std::vector<rabbitizer::InstructionCpu>& instructions, FunctionStats& stats);
|
||||||
bool recompile_function(const Context& context, const Function& func, const std::filesystem::path& output_path, std::span<std::vector<uint32_t>> static_funcs);
|
bool recompile_function(const Context& context, const Config& config, const Function& func, const std::filesystem::path& output_path, std::span<std::vector<uint32_t>> static_funcs);
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -199,7 +199,9 @@ bool analyze_instruction(const rabbitizer::InstructionCpu& instr, const RecompPo
|
||||||
address,
|
address,
|
||||||
instr.getVram()
|
instr.getVram()
|
||||||
);
|
);
|
||||||
} else {
|
}
|
||||||
|
// Allow tail calls (TODO account for trailing nops due to bad function splits)
|
||||||
|
else if (instr.getVram() != func.vram + (func.words.size() - 2) * sizeof(func.words[0])) {
|
||||||
// Inconclusive analysis
|
// Inconclusive analysis
|
||||||
fmt::print(stderr, "Failed to to find jump table for `jr {}` at 0x{:08X} in {}\n", RabbitizerRegister_getNameGpr(rs), instr.getVram(), func.name);
|
fmt::print(stderr, "Failed to to find jump table for `jr {}` at 0x{:08X} in {}\n", RabbitizerRegister_getNameGpr(rs), instr.getVram(), func.name);
|
||||||
return false;
|
return false;
|
||||||
|
|
|
@ -43,6 +43,32 @@ std::vector<std::string> get_stubbed_funcs(const toml::value& patches_data) {
|
||||||
return stubbed_funcs;
|
return stubbed_funcs;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::vector<std::string> get_ignored_funcs(const toml::value& patches_data) {
|
||||||
|
std::vector<std::string> ignored_funcs{};
|
||||||
|
|
||||||
|
// Check if the ignored funcs array exists.
|
||||||
|
const auto& ignored_funcs_data = toml::find_or<toml::value>(patches_data, "ignored", toml::value{});
|
||||||
|
|
||||||
|
if (ignored_funcs_data.type() == toml::value_t::empty) {
|
||||||
|
// No stubs, nothing to do here.
|
||||||
|
return ignored_funcs;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the ignored funcs array as an array type.
|
||||||
|
const toml::array& ignored_funcs_array = ignored_funcs_data.as_array();
|
||||||
|
|
||||||
|
// Make room for all the ignored funcs in the array.
|
||||||
|
ignored_funcs.resize(ignored_funcs_array.size());
|
||||||
|
|
||||||
|
// Gather the stubs and place them into the array.
|
||||||
|
for (size_t stub_idx = 0; stub_idx < ignored_funcs_array.size(); stub_idx++) {
|
||||||
|
// Copy the entry into the ignored function list.
|
||||||
|
ignored_funcs[stub_idx] = ignored_funcs_array[stub_idx].as_string();
|
||||||
|
}
|
||||||
|
|
||||||
|
return ignored_funcs;
|
||||||
|
}
|
||||||
|
|
||||||
std::unordered_map<std::string, RecompPort::FunctionArgType> arg_type_map{
|
std::unordered_map<std::string, RecompPort::FunctionArgType> arg_type_map{
|
||||||
{"u32", RecompPort::FunctionArgType::u32},
|
{"u32", RecompPort::FunctionArgType::u32},
|
||||||
{"s32", RecompPort::FunctionArgType::s32},
|
{"s32", RecompPort::FunctionArgType::s32},
|
||||||
|
@ -84,7 +110,7 @@ RecompPort::DeclaredFunctionMap get_declared_funcs(const toml::value& patches_da
|
||||||
const toml::array& funcs_array = funcs_data.as_array();
|
const toml::array& funcs_array = funcs_data.as_array();
|
||||||
|
|
||||||
// Reserve room for all the funcs in the map.
|
// Reserve room for all the funcs in the map.
|
||||||
declared_funcs.reserve(funcs_data.size());
|
declared_funcs.reserve(funcs_array.size());
|
||||||
for (const toml::value& cur_func_val : funcs_array) {
|
for (const toml::value& cur_func_val : funcs_array) {
|
||||||
const std::string& func_name = toml::find<std::string>(cur_func_val, "name");
|
const std::string& func_name = toml::find<std::string>(cur_func_val, "name");
|
||||||
const toml::array& args_in = toml::find<toml::array>(cur_func_val, "args");
|
const toml::array& args_in = toml::find<toml::array>(cur_func_val, "args");
|
||||||
|
@ -95,6 +121,40 @@ RecompPort::DeclaredFunctionMap get_declared_funcs(const toml::value& patches_da
|
||||||
return declared_funcs;
|
return declared_funcs;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::vector<RecompPort::FunctionSize> get_func_sizes(const toml::value& patches_data) {
|
||||||
|
std::vector<RecompPort::FunctionSize> func_sizes{};
|
||||||
|
|
||||||
|
// Check if the func size array exists.
|
||||||
|
const toml::value& sizes_data = toml::find_or<toml::value>(patches_data, "function_sizes", toml::value{});
|
||||||
|
if (sizes_data.type() == toml::value_t::empty) {
|
||||||
|
// No func size array, nothing to do here
|
||||||
|
return func_sizes;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the funcs array as an array type.
|
||||||
|
const toml::array& sizes_array = sizes_data.as_array();
|
||||||
|
|
||||||
|
// Reserve room for all the funcs in the map.
|
||||||
|
func_sizes.reserve(sizes_array.size());
|
||||||
|
for (const toml::value& cur_func_size : sizes_array) {
|
||||||
|
const std::string& func_name = toml::find<std::string>(cur_func_size, "name");
|
||||||
|
uint32_t func_size = toml::find<uint32_t>(cur_func_size, "size");
|
||||||
|
|
||||||
|
// Make sure the size is divisible by 4
|
||||||
|
if (func_size & (4 - 1)) {
|
||||||
|
// It's not, so throw an error (and make it look like a normal toml one).
|
||||||
|
throw toml::type_error(toml::detail::format_underline(
|
||||||
|
std::string{ std::source_location::current().function_name() } + ": function size not divisible by 4", {
|
||||||
|
{cur_func_size.location(), ""}
|
||||||
|
}), cur_func_size.location());
|
||||||
|
}
|
||||||
|
|
||||||
|
func_sizes.emplace_back(func_name, func_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
return func_sizes;
|
||||||
|
}
|
||||||
|
|
||||||
std::vector<RecompPort::InstructionPatch> get_instruction_patches(const toml::value& patches_data) {
|
std::vector<RecompPort::InstructionPatch> get_instruction_patches(const toml::value& patches_data) {
|
||||||
std::vector<RecompPort::InstructionPatch> ret;
|
std::vector<RecompPort::InstructionPatch> ret;
|
||||||
|
|
||||||
|
@ -155,6 +215,8 @@ RecompPort::Config::Config(const char* path) {
|
||||||
elf_path = concat_if_not_empty(basedir, toml::find<std::string>(input_data, "elf_path"));
|
elf_path = concat_if_not_empty(basedir, toml::find<std::string>(input_data, "elf_path"));
|
||||||
output_func_path = concat_if_not_empty(basedir, toml::find<std::string>(input_data, "output_func_path"));
|
output_func_path = concat_if_not_empty(basedir, toml::find<std::string>(input_data, "output_func_path"));
|
||||||
relocatable_sections_path = concat_if_not_empty(basedir, toml::find_or<std::string>(input_data, "relocatable_sections_path", ""));
|
relocatable_sections_path = concat_if_not_empty(basedir, toml::find_or<std::string>(input_data, "relocatable_sections_path", ""));
|
||||||
|
uses_mips3_float_mode = toml::find_or<bool>(input_data, "uses_mips3_float_mode", false);
|
||||||
|
bss_section_suffix = toml::find_or<std::string>(input_data, "bss_section_suffix", ".bss");
|
||||||
|
|
||||||
// Patches section (optional)
|
// Patches section (optional)
|
||||||
const toml::value& patches_data = toml::find_or<toml::value>(config_data, "patches", toml::value{});
|
const toml::value& patches_data = toml::find_or<toml::value>(config_data, "patches", toml::value{});
|
||||||
|
@ -162,11 +224,17 @@ RecompPort::Config::Config(const char* path) {
|
||||||
// Stubs array (optional)
|
// Stubs array (optional)
|
||||||
stubbed_funcs = get_stubbed_funcs(patches_data);
|
stubbed_funcs = get_stubbed_funcs(patches_data);
|
||||||
|
|
||||||
|
// Ignored funcs array (optional)
|
||||||
|
ignored_funcs = get_ignored_funcs(patches_data);
|
||||||
|
|
||||||
// Functions (optional)
|
// Functions (optional)
|
||||||
declared_funcs = get_declared_funcs(patches_data);
|
declared_funcs = get_declared_funcs(patches_data);
|
||||||
|
|
||||||
// Single-instruction patches (optional)
|
// Single-instruction patches (optional)
|
||||||
instruction_patches = get_instruction_patches(patches_data);
|
instruction_patches = get_instruction_patches(patches_data);
|
||||||
|
|
||||||
|
// Manual function sizes (optional)
|
||||||
|
manual_func_sizes = get_func_sizes(patches_data);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (const toml::syntax_error& err) {
|
catch (const toml::syntax_error& err) {
|
||||||
|
|
134
src/main.cpp
134
src/main.cpp
|
@ -540,6 +540,8 @@ std::unordered_set<std::string> renamed_funcs{
|
||||||
"sincosf",
|
"sincosf",
|
||||||
"sinf",
|
"sinf",
|
||||||
"cosf",
|
"cosf",
|
||||||
|
"__sinf",
|
||||||
|
"__cosf",
|
||||||
"sqrt",
|
"sqrt",
|
||||||
"sqrtf",
|
"sqrtf",
|
||||||
"memcpy",
|
"memcpy",
|
||||||
|
@ -567,17 +569,8 @@ std::unordered_set<std::string> renamed_funcs{
|
||||||
"roundf",
|
"roundf",
|
||||||
"trunc",
|
"trunc",
|
||||||
"truncf",
|
"truncf",
|
||||||
"vsprintf"
|
"vsprintf",
|
||||||
};
|
"__assert",
|
||||||
|
|
||||||
// Functions that weren't declared properly and thus have no size in the elf
|
|
||||||
std::unordered_map<std::string, size_t> unsized_funcs{
|
|
||||||
{ "guMtxF2L", 0x64 },
|
|
||||||
{ "guScaleF", 0x48 },
|
|
||||||
{ "guTranslateF", 0x48 },
|
|
||||||
{ "guMtxIdentF", 0x48 },
|
|
||||||
{ "sqrtf", 0x8 },
|
|
||||||
{ "guMtxIdent", 0x4C },
|
|
||||||
};
|
};
|
||||||
|
|
||||||
bool read_symbols(RecompPort::Context& context, const ELFIO::elfio& elf_file, ELFIO::section* symtab_section, uint32_t entrypoint) {
|
bool read_symbols(RecompPort::Context& context, const ELFIO::elfio& elf_file, ELFIO::section* symtab_section, uint32_t entrypoint) {
|
||||||
|
@ -605,24 +598,23 @@ bool read_symbols(RecompPort::Context& context, const ELFIO::elfio& elf_file, EL
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if this symbol is the entrypoint
|
// Check if this symbol is the entrypoint
|
||||||
if (value == entrypoint /*&& type == ELFIO::STT_FUNC*/) {
|
if (value == entrypoint && type == ELFIO::STT_FUNC) {
|
||||||
if (found_entrypoint_func) {
|
if (found_entrypoint_func) {
|
||||||
fmt::print(stderr, "Ambiguous entrypoint\n");
|
fmt::print(stderr, "Ambiguous entrypoint: {}\n", name);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
found_entrypoint_func = true;
|
found_entrypoint_func = true;
|
||||||
|
fmt::print("Found entrypoint, original name: {}\n", name);
|
||||||
size = 0x50; // dummy size for entrypoints, should cover them all
|
size = 0x50; // dummy size for entrypoints, should cover them all
|
||||||
name = "recomp_entrypoint";
|
name = "recomp_entrypoint";
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if this symbol is unsized and if so populate its size from the unsized_funcs map
|
// Check if this symbol has a size override
|
||||||
if (size == 0) {
|
auto size_find = context.manually_sized_funcs.find(name);
|
||||||
auto size_find = unsized_funcs.find(name);
|
if (size_find != context.manually_sized_funcs.end()) {
|
||||||
if (size_find != unsized_funcs.end()) {
|
|
||||||
size = size_find->second;
|
size = size_find->second;
|
||||||
type = ELFIO::STT_FUNC;
|
type = ELFIO::STT_FUNC;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if (reimplemented_funcs.contains(name)) {
|
if (reimplemented_funcs.contains(name)) {
|
||||||
reimplemented = true;
|
reimplemented = true;
|
||||||
|
@ -653,7 +645,7 @@ bool read_symbols(RecompPort::Context& context, const ELFIO::elfio& elf_file, EL
|
||||||
context.functions_by_vram[vram].push_back(context.functions.size());
|
context.functions_by_vram[vram].push_back(context.functions.size());
|
||||||
|
|
||||||
// Find the entrypoint by rom address in case it doesn't have vram as its value
|
// Find the entrypoint by rom address in case it doesn't have vram as its value
|
||||||
if (rom_address == 0x1000) {
|
if (rom_address == 0x1000 && type == ELFIO::STT_FUNC) {
|
||||||
vram = entrypoint;
|
vram = entrypoint;
|
||||||
found_entrypoint_func = true;
|
found_entrypoint_func = true;
|
||||||
name = "recomp_entrypoint";
|
name = "recomp_entrypoint";
|
||||||
|
@ -719,7 +711,7 @@ std::optional<size_t> get_segment(const std::vector<SegmentEntry>& segments, ELF
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
|
|
||||||
ELFIO::section* read_sections(RecompPort::Context& context, const ELFIO::elfio& elf_file) {
|
ELFIO::section* read_sections(RecompPort::Context& context, const RecompPort::Config& config, const ELFIO::elfio& elf_file) {
|
||||||
ELFIO::section* symtab_section = nullptr;
|
ELFIO::section* symtab_section = nullptr;
|
||||||
std::vector<SegmentEntry> segments{};
|
std::vector<SegmentEntry> segments{};
|
||||||
segments.resize(elf_file.segments.size());
|
segments.resize(elf_file.segments.size());
|
||||||
|
@ -740,6 +732,7 @@ ELFIO::section* read_sections(RecompPort::Context& context, const ELFIO::elfio&
|
||||||
//);
|
//);
|
||||||
|
|
||||||
std::unordered_map<std::string, ELFIO::section*> reloc_sections_by_name;
|
std::unordered_map<std::string, ELFIO::section*> reloc_sections_by_name;
|
||||||
|
std::unordered_map<std::string, ELFIO::section*> bss_sections_by_name;
|
||||||
|
|
||||||
// Iterate over every section to record rom addresses and find the symbol table
|
// Iterate over every section to record rom addresses and find the symbol table
|
||||||
fmt::print("Sections\n");
|
fmt::print("Sections\n");
|
||||||
|
@ -757,6 +750,10 @@ ELFIO::section* read_sections(RecompPort::Context& context, const ELFIO::elfio&
|
||||||
symtab_section = section.get();
|
symtab_section = section.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (context.relocatable_sections.contains(section_name)) {
|
||||||
|
section_out.relocatable = true;
|
||||||
|
}
|
||||||
|
|
||||||
// Check if this section is a reloc section
|
// Check if this section is a reloc section
|
||||||
if (type == ELFIO::SHT_REL) {
|
if (type == ELFIO::SHT_REL) {
|
||||||
// If it is, determine the name of the section it relocates
|
// If it is, determine the name of the section it relocates
|
||||||
|
@ -773,6 +770,16 @@ ELFIO::section* read_sections(RecompPort::Context& context, const ELFIO::elfio&
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If the section is bss (SHT_NOBITS) and ends with the bss suffix, add it to the bss section map
|
||||||
|
if (type == ELFIO::SHT_NOBITS && section_name.ends_with(config.bss_section_suffix)) {
|
||||||
|
std::string bss_target_section = section_name.substr(0, section_name.size() - config.bss_section_suffix.size());
|
||||||
|
|
||||||
|
// If this bss section is for a section that has been marked as relocatable, record it in the reloc section lookup
|
||||||
|
if (context.relocatable_sections.contains(bss_target_section)) {
|
||||||
|
bss_sections_by_name[bss_target_section] = section.get();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// If this section isn't bss (SHT_NOBITS) and ends up in the rom (SHF_ALLOC),
|
// If this section isn't bss (SHT_NOBITS) and ends up in the rom (SHF_ALLOC),
|
||||||
// find this section's rom address and copy it into the rom
|
// find this section's rom address and copy it into the rom
|
||||||
if (type != ELFIO::SHT_NOBITS && section->get_flags() & ELFIO::SHF_ALLOC && section->get_size() != 0) {
|
if (type != ELFIO::SHT_NOBITS && section->get_flags() & ELFIO::SHF_ALLOC && section->get_size() != 0) {
|
||||||
|
@ -831,13 +838,18 @@ ELFIO::section* read_sections(RecompPort::Context& context, const ELFIO::elfio&
|
||||||
|
|
||||||
// TODO make sure that a reloc section was found for every section marked as relocatable
|
// TODO make sure that a reloc section was found for every section marked as relocatable
|
||||||
|
|
||||||
// Process reloc sections
|
// Process bss and reloc sections
|
||||||
for (RecompPort::Section §ion_out : context.sections) {
|
for (RecompPort::Section §ion_out : context.sections) {
|
||||||
|
// Check if a bss section was found that corresponds with this section
|
||||||
|
auto bss_find = bss_sections_by_name.find(section_out.name);
|
||||||
|
if (bss_find != bss_sections_by_name.end()) {
|
||||||
|
section_out.bss_section_index = bss_find->second->get_index();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (section_out.relocatable) {
|
||||||
// Check if a reloc section was found that corresponds with this section
|
// Check if a reloc section was found that corresponds with this section
|
||||||
auto reloc_find = reloc_sections_by_name.find(section_out.name);
|
auto reloc_find = reloc_sections_by_name.find(section_out.name);
|
||||||
if (reloc_find != reloc_sections_by_name.end()) {
|
if (reloc_find != reloc_sections_by_name.end()) {
|
||||||
// Mark the section as relocatable
|
|
||||||
section_out.relocatable = true;
|
|
||||||
// Create an accessor for the reloc section
|
// Create an accessor for the reloc section
|
||||||
ELFIO::relocation_section_accessor rel_accessor{ elf_file, reloc_find->second };
|
ELFIO::relocation_section_accessor rel_accessor{ elf_file, reloc_find->second };
|
||||||
// Allocate space for the relocs in this section
|
// Allocate space for the relocs in this section
|
||||||
|
@ -923,6 +935,7 @@ ELFIO::section* read_sections(RecompPort::Context& context, const ELFIO::elfio&
|
||||||
// Nothing to do here
|
// Nothing to do here
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Sort this section's relocs by address, which allows for binary searching and more efficient iteration during recompilation.
|
// Sort this section's relocs by address, which allows for binary searching and more efficient iteration during recompilation.
|
||||||
// This is safe to do as the entire full_immediate in present in relocs due to the pairing that was done earlier, so the HI16 does not
|
// This is safe to do as the entire full_immediate in present in relocs due to the pairing that was done earlier, so the HI16 does not
|
||||||
|
@ -968,7 +981,7 @@ void analyze_sections(RecompPort::Context& context, const ELFIO::elfio& elf_file
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool read_list_file(const std::filesystem::path& filename, std::unordered_set<std::string>& entries_out) {
|
bool read_list_file(const std::filesystem::path& filename, std::vector<std::string>& entries_out) {
|
||||||
std::ifstream input_file{ filename };
|
std::ifstream input_file{ filename };
|
||||||
if (!input_file.good()) {
|
if (!input_file.good()) {
|
||||||
return false;
|
return false;
|
||||||
|
@ -977,7 +990,7 @@ bool read_list_file(const std::filesystem::path& filename, std::unordered_set<st
|
||||||
std::string entry;
|
std::string entry;
|
||||||
|
|
||||||
while (input_file >> entry) {
|
while (input_file >> entry) {
|
||||||
entries_out.emplace(std::move(entry));
|
entries_out.emplace_back(std::move(entry));
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
@ -1007,14 +1020,17 @@ int main(int argc, char** argv) {
|
||||||
RabbitizerConfig_Cfg.pseudos.pseudoBnez = false;
|
RabbitizerConfig_Cfg.pseudos.pseudoBnez = false;
|
||||||
RabbitizerConfig_Cfg.pseudos.pseudoNot = false;
|
RabbitizerConfig_Cfg.pseudos.pseudoNot = false;
|
||||||
|
|
||||||
std::unordered_set<std::string> relocatable_sections{};
|
std::vector<std::string> relocatable_sections_ordered{};
|
||||||
|
|
||||||
if (!config.relocatable_sections_path.empty()) {
|
if (!config.relocatable_sections_path.empty()) {
|
||||||
if (!read_list_file(config.relocatable_sections_path, relocatable_sections)) {
|
if (!read_list_file(config.relocatable_sections_path, relocatable_sections_ordered)) {
|
||||||
exit_failure("Failed to load the relocatable section list file: " + std::string(argv[4]) + "\n");
|
exit_failure("Failed to load the relocatable section list file: " + std::string(argv[4]) + "\n");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::unordered_set<std::string> relocatable_sections{};
|
||||||
|
relocatable_sections.insert(relocatable_sections_ordered.begin(), relocatable_sections_ordered.end());
|
||||||
|
|
||||||
if (!elf_file.load(config.elf_path.string())) {
|
if (!elf_file.load(config.elf_path.string())) {
|
||||||
exit_failure("Failed to load provided elf file\n");
|
exit_failure("Failed to load provided elf file\n");
|
||||||
}
|
}
|
||||||
|
@ -1031,7 +1047,7 @@ int main(int argc, char** argv) {
|
||||||
context.relocatable_sections = std::move(relocatable_sections);
|
context.relocatable_sections = std::move(relocatable_sections);
|
||||||
|
|
||||||
// Read all of the sections in the elf and look for the symbol table section
|
// Read all of the sections in the elf and look for the symbol table section
|
||||||
ELFIO::section* symtab_section = read_sections(context, elf_file);
|
ELFIO::section* symtab_section = read_sections(context, config, elf_file);
|
||||||
|
|
||||||
// Search the sections to see if any are overlays or TLB-mapped
|
// Search the sections to see if any are overlays or TLB-mapped
|
||||||
analyze_sections(context, elf_file);
|
analyze_sections(context, elf_file);
|
||||||
|
@ -1041,6 +1057,17 @@ int main(int argc, char** argv) {
|
||||||
exit_failure("No symbol table section found\n");
|
exit_failure("No symbol table section found\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Functions that weren't declared properly and thus have no size in the elf
|
||||||
|
//context.manually_sized_funcs.emplace("guMtxF2L", 0x64);
|
||||||
|
//context.manually_sized_funcs.emplace("guScaleF", 0x48);
|
||||||
|
//context.manually_sized_funcs.emplace("guTranslateF", 0x48);
|
||||||
|
//context.manually_sized_funcs.emplace("guMtxIdentF", 0x48);
|
||||||
|
//context.manually_sized_funcs.emplace("sqrtf", 0x8);
|
||||||
|
//context.manually_sized_funcs.emplace("guMtxIdent", 0x4C);
|
||||||
|
for (const auto& func_size : config.manual_func_sizes) {
|
||||||
|
context.manually_sized_funcs.emplace(func_size.func_name, func_size.size_bytes);
|
||||||
|
}
|
||||||
|
|
||||||
// Read all of the symbols in the elf and look for the entrypoint function
|
// Read all of the symbols in the elf and look for the entrypoint function
|
||||||
bool found_entrypoint_func = read_symbols(context, elf_file, symtab_section, config.entrypoint);
|
bool found_entrypoint_func = read_symbols(context, elf_file, symtab_section, config.entrypoint);
|
||||||
|
|
||||||
|
@ -1050,6 +1077,8 @@ int main(int argc, char** argv) {
|
||||||
|
|
||||||
fmt::print("Function count: {}\n", context.functions.size());
|
fmt::print("Function count: {}\n", context.functions.size());
|
||||||
|
|
||||||
|
std::filesystem::create_directories(config.output_func_path);
|
||||||
|
|
||||||
std::ofstream lookup_file{ config.output_func_path / "lookup.cpp" };
|
std::ofstream lookup_file{ config.output_func_path / "lookup.cpp" };
|
||||||
std::ofstream func_header_file{ config.output_func_path / "funcs.h" };
|
std::ofstream func_header_file{ config.output_func_path / "funcs.h" };
|
||||||
|
|
||||||
|
@ -1087,6 +1116,19 @@ int main(int argc, char** argv) {
|
||||||
context.functions[func_find->second].stubbed = true;
|
context.functions[func_find->second].stubbed = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Ignore any functions specified in the config file.
|
||||||
|
for (const std::string& ignored_func : config.ignored_funcs) {
|
||||||
|
// Check if the specified function exists.
|
||||||
|
auto func_find = context.functions_by_name.find(ignored_func);
|
||||||
|
if (func_find == context.functions_by_name.end()) {
|
||||||
|
// Function doesn't exist, present an error to the user instead of silently failing to mark it as ignored.
|
||||||
|
// This helps prevent typos in the config file or functions renamed between versions from causing issues.
|
||||||
|
exit_failure(fmt::format("Function {} is set as ignored in the config file but does not exist!", ignored_func));
|
||||||
|
}
|
||||||
|
// Mark the function as .
|
||||||
|
context.functions[func_find->second].ignored = true;
|
||||||
|
}
|
||||||
|
|
||||||
// Apply any single-instruction patches.
|
// Apply any single-instruction patches.
|
||||||
for (const RecompPort::InstructionPatch& patch : config.instruction_patches) {
|
for (const RecompPort::InstructionPatch& patch : config.instruction_patches) {
|
||||||
// Check if the specified function exists.
|
// Check if the specified function exists.
|
||||||
|
@ -1102,7 +1144,7 @@ int main(int argc, char** argv) {
|
||||||
|
|
||||||
// Check that the function actually contains this vram address.
|
// Check that the function actually contains this vram address.
|
||||||
if (patch.vram < func_vram || patch.vram >= func_vram + func.words.size() * sizeof(func.words[0])) {
|
if (patch.vram < func_vram || patch.vram >= func_vram + func.words.size() * sizeof(func.words[0])) {
|
||||||
exit_failure(fmt::vformat("Function {} has an instruction patch for vram 0x{:08X} but doesn't contain that vram address!", fmt::make_format_args(patch.vram)));
|
exit_failure(fmt::format("Function {} has an instruction patch for vram 0x{:08X} but doesn't contain that vram address!", patch.func_name, (uint32_t)patch.vram));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Calculate the instruction index and modify the instruction.
|
// Calculate the instruction index and modify the instruction.
|
||||||
|
@ -1119,7 +1161,7 @@ int main(int argc, char** argv) {
|
||||||
"void {}(uint8_t* rdram, recomp_context* ctx);\n", func.name);
|
"void {}(uint8_t* rdram, recomp_context* ctx);\n", func.name);
|
||||||
//fmt::print(lookup_file,
|
//fmt::print(lookup_file,
|
||||||
// " {{ 0x{:08X}u, {} }},\n", func.vram, func.name);
|
// " {{ 0x{:08X}u, {} }},\n", func.vram, func.name);
|
||||||
if (RecompPort::recompile_function(context, func, config.output_func_path / (func.name + ".c"), static_funcs_by_section) == false) {
|
if (RecompPort::recompile_function(context, config, func, config.output_func_path / (func.name + ".c"), static_funcs_by_section) == false) {
|
||||||
//lookup_file.clear();
|
//lookup_file.clear();
|
||||||
fmt::print(stderr, "Error recompiling {}\n", func.name);
|
fmt::print(stderr, "Error recompiling {}\n", func.name);
|
||||||
std::exit(EXIT_FAILURE);
|
std::exit(EXIT_FAILURE);
|
||||||
|
@ -1188,7 +1230,7 @@ int main(int argc, char** argv) {
|
||||||
"void {}(uint8_t* rdram, recomp_context* ctx);\n", func.name);
|
"void {}(uint8_t* rdram, recomp_context* ctx);\n", func.name);
|
||||||
//fmt::print(lookup_file,
|
//fmt::print(lookup_file,
|
||||||
// " {{ 0x{:08X}u, {} }},\n", func.vram, func.name);
|
// " {{ 0x{:08X}u, {} }},\n", func.vram, func.name);
|
||||||
if (RecompPort::recompile_function(context, func, config.output_func_path / (func.name + ".c"), static_funcs_by_section) == false) {
|
if (RecompPort::recompile_function(context, config, func, config.output_func_path / (func.name + ".c"), static_funcs_by_section) == false) {
|
||||||
//lookup_file.clear();
|
//lookup_file.clear();
|
||||||
fmt::print(stderr, "Error recompiling {}\n", func.name);
|
fmt::print(stderr, "Error recompiling {}\n", func.name);
|
||||||
std::exit(EXIT_FAILURE);
|
std::exit(EXIT_FAILURE);
|
||||||
|
@ -1226,13 +1268,24 @@ int main(int argc, char** argv) {
|
||||||
"\n"
|
"\n"
|
||||||
);
|
);
|
||||||
|
|
||||||
|
std::unordered_map<std::string, size_t> relocatable_section_indices{};
|
||||||
|
size_t written_sections = 0;
|
||||||
|
|
||||||
for (size_t section_index = 0; section_index < context.sections.size(); section_index++) {
|
for (size_t section_index = 0; section_index < context.sections.size(); section_index++) {
|
||||||
const auto& section = context.sections[section_index];
|
const auto& section = context.sections[section_index];
|
||||||
const auto& section_funcs = context.section_functions[section_index];
|
const auto& section_funcs = context.section_functions[section_index];
|
||||||
|
|
||||||
|
if (section.name == ".cosection") {
|
||||||
|
fmt::print("");
|
||||||
|
}
|
||||||
|
|
||||||
if (!section_funcs.empty()) {
|
if (!section_funcs.empty()) {
|
||||||
std::string_view section_name_trimmed{ section.name };
|
std::string_view section_name_trimmed{ section.name };
|
||||||
|
|
||||||
|
if (section.relocatable) {
|
||||||
|
relocatable_section_indices.emplace(section.name, written_sections);
|
||||||
|
}
|
||||||
|
|
||||||
while (section_name_trimmed[0] == '.') {
|
while (section_name_trimmed[0] == '.') {
|
||||||
section_name_trimmed.remove_prefix(1);
|
section_name_trimmed.remove_prefix(1);
|
||||||
}
|
}
|
||||||
|
@ -1253,6 +1306,7 @@ int main(int argc, char** argv) {
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt::print(overlay_file, "}};\n");
|
fmt::print(overlay_file, "}};\n");
|
||||||
|
written_sections++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
section_load_table += "};\n";
|
section_load_table += "};\n";
|
||||||
|
@ -1260,6 +1314,24 @@ int main(int argc, char** argv) {
|
||||||
fmt::print(overlay_file, "{}", section_load_table);
|
fmt::print(overlay_file, "{}", section_load_table);
|
||||||
|
|
||||||
fmt::print(overlay_file, "const size_t num_sections = {};\n", context.sections.size());
|
fmt::print(overlay_file, "const size_t num_sections = {};\n", context.sections.size());
|
||||||
|
|
||||||
|
|
||||||
|
fmt::print(overlay_file, "static int overlay_sections_by_index[] = {{\n");
|
||||||
|
for (const std::string& section : relocatable_sections_ordered) {
|
||||||
|
// Check if this is an empty overlay
|
||||||
|
if (section == "*") {
|
||||||
|
fmt::print(overlay_file, " -1,\n");
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
auto find_it = relocatable_section_indices.find(section);
|
||||||
|
if (find_it == relocatable_section_indices.end()) {
|
||||||
|
fmt::print(stderr, "Failed to find written section index of relocatable section: {}\n", section);
|
||||||
|
std::exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
fmt::print(overlay_file, " {},\n", relocatable_section_indices[section]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fmt::print(overlay_file, "}};\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
|
@ -9,6 +9,7 @@
|
||||||
#include "recomp_port.h"
|
#include "recomp_port.h"
|
||||||
|
|
||||||
using InstrId = rabbitizer::InstrId::UniqueId;
|
using InstrId = rabbitizer::InstrId::UniqueId;
|
||||||
|
using Cop0Reg = rabbitizer::Registers::Cpu::Cop0;
|
||||||
|
|
||||||
std::string_view ctx_gpr_prefix(int reg) {
|
std::string_view ctx_gpr_prefix(int reg) {
|
||||||
if (reg != 0) {
|
if (reg != 0) {
|
||||||
|
@ -17,7 +18,7 @@ std::string_view ctx_gpr_prefix(int reg) {
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
bool process_instruction(const RecompPort::Context& context, 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) {
|
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];
|
const auto& section = context.sections[func.section_index];
|
||||||
const auto& instr = instructions[instr_index];
|
const auto& instr = instructions[instr_index];
|
||||||
needs_link_branch = false;
|
needs_link_branch = false;
|
||||||
|
@ -46,23 +47,19 @@ bool process_instruction(const RecompPort::Context& context, const RecompPort::F
|
||||||
uint32_t reloc_target_section_offset = 0;
|
uint32_t reloc_target_section_offset = 0;
|
||||||
|
|
||||||
// Check if this instruction has a reloc.
|
// Check if this instruction has a reloc.
|
||||||
if (section.relocatable && section.relocs[reloc_index].address == instr_vram) {
|
if (section.relocatable && section.relocs.size() > 0 && section.relocs[reloc_index].address == instr_vram) {
|
||||||
// Get the reloc data for this instruction
|
// Get the reloc data for this instruction
|
||||||
const auto& reloc = section.relocs[reloc_index];
|
const auto& reloc = section.relocs[reloc_index];
|
||||||
reloc_section = reloc.target_section;
|
reloc_section = reloc.target_section;
|
||||||
// Some symbols are in a nonexistent section (e.g. absolute symbols), so check that the section is valid before doing anything else.
|
// Some symbols are in a nonexistent section (e.g. absolute symbols), so check that the section is valid before doing anything else.
|
||||||
// Absolute symbols will never need to be relocated so it's safe to skip this.
|
// Absolute symbols will never need to be relocated so it's safe to skip this.
|
||||||
if (reloc_section < context.sections.size()) {
|
if (reloc_section < context.sections.size()) {
|
||||||
// Check if the target section is also relocatable. References to symbols that are in a non-relocatable section don't need to
|
// Ignore this reloc if it points to a different section.
|
||||||
// be relocated, so we can skip those.
|
// Also check if the reloc points to the bss section since that will also be relocated with the section.
|
||||||
if (context.sections[reloc_section].relocatable) {
|
if (reloc_section == func.section_index || reloc_section == section.bss_section_index) {
|
||||||
// For games where references between sections aren't relocated, ignore this reloc if it points to a different section
|
|
||||||
// TODO expose this as a config option
|
|
||||||
// TODO!!!!! also relocate references to the corresponding bss section!!!!!
|
|
||||||
if (reloc_section == func.section_index) {
|
|
||||||
// Record the reloc's data.
|
// Record the reloc's data.
|
||||||
reloc_type = reloc.type;
|
reloc_type = reloc.type;
|
||||||
reloc_target_section_offset = reloc.target_address - context.sections[reloc_section].ram_addr;
|
reloc_target_section_offset = reloc.target_address - section.ram_addr;
|
||||||
// Ignore all relocs that aren't HI16 or LO16.
|
// Ignore all relocs that aren't HI16 or LO16.
|
||||||
if (reloc_type == RecompPort::RelocType::R_MIPS_HI16 || reloc_type == RecompPort::RelocType::R_MIPS_LO16) {
|
if (reloc_type == RecompPort::RelocType::R_MIPS_HI16 || reloc_type == RecompPort::RelocType::R_MIPS_LO16) {
|
||||||
at_reloc = true;
|
at_reloc = true;
|
||||||
|
@ -70,7 +67,6 @@ bool process_instruction(const RecompPort::Context& context, const RecompPort::F
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
auto print_indent = [&]() {
|
auto print_indent = [&]() {
|
||||||
fmt::print(output_file, " ");
|
fmt::print(output_file, " ");
|
||||||
|
@ -96,7 +92,7 @@ bool process_instruction(const RecompPort::Context& context, const RecompPort::F
|
||||||
if (reloc_index + 1 < section.relocs.size() && next_vram > section.relocs[reloc_index].address) {
|
if (reloc_index + 1 < section.relocs.size() && next_vram > section.relocs[reloc_index].address) {
|
||||||
next_reloc_index++;
|
next_reloc_index++;
|
||||||
}
|
}
|
||||||
process_instruction(context, func, stats, skipped_insns, instr_index + 1, instructions, output_file, false, false, link_branch_index, next_reloc_index, dummy_needs_link_branch, dummy_is_branch_likely, static_funcs_out);
|
process_instruction(context, config, func, stats, skipped_insns, instr_index + 1, instructions, output_file, false, false, link_branch_index, next_reloc_index, dummy_needs_link_branch, dummy_is_branch_likely, static_funcs_out);
|
||||||
}
|
}
|
||||||
print_indent();
|
print_indent();
|
||||||
fmt::vprint(output_file, fmt_str, fmt::make_format_args(args...));
|
fmt::vprint(output_file, fmt_str, fmt::make_format_args(args...));
|
||||||
|
@ -117,7 +113,7 @@ bool process_instruction(const RecompPort::Context& context, const RecompPort::F
|
||||||
if (reloc_index + 1 < section.relocs.size() && next_vram > section.relocs[reloc_index].address) {
|
if (reloc_index + 1 < section.relocs.size() && next_vram > section.relocs[reloc_index].address) {
|
||||||
next_reloc_index++;
|
next_reloc_index++;
|
||||||
}
|
}
|
||||||
process_instruction(context, func, stats, skipped_insns, instr_index + 1, instructions, output_file, true, false, link_branch_index, next_reloc_index, dummy_needs_link_branch, dummy_is_branch_likely, static_funcs_out);
|
process_instruction(context, config, func, stats, skipped_insns, instr_index + 1, instructions, output_file, true, false, link_branch_index, next_reloc_index, dummy_needs_link_branch, dummy_is_branch_likely, static_funcs_out);
|
||||||
}
|
}
|
||||||
fmt::print(output_file, " ");
|
fmt::print(output_file, " ");
|
||||||
fmt::vprint(output_file, fmt_str, fmt::make_format_args(args...));
|
fmt::vprint(output_file, fmt_str, fmt::make_format_args(args...));
|
||||||
|
@ -127,6 +123,76 @@ bool process_instruction(const RecompPort::Context& context, const RecompPort::F
|
||||||
fmt::print(output_file, ";\n }}\n");
|
fmt::print(output_file, ";\n }}\n");
|
||||||
};
|
};
|
||||||
|
|
||||||
|
auto print_func_call = [&](uint32_t target_func_vram) {
|
||||||
|
const auto matching_funcs_find = context.functions_by_vram.find(target_func_vram);
|
||||||
|
std::string jal_target_name;
|
||||||
|
uint32_t section_vram_start = section.ram_addr;
|
||||||
|
uint32_t section_vram_end = section.ram_addr + section.size;
|
||||||
|
// TODO the current section should be prioritized if the target jal is in its vram even if a function isn't known (i.e. static)
|
||||||
|
if (matching_funcs_find != context.functions_by_vram.end()) {
|
||||||
|
// If we found matches for the target function by vram,
|
||||||
|
const auto& matching_funcs_vec = matching_funcs_find->second;
|
||||||
|
size_t real_func_index;
|
||||||
|
bool ambiguous;
|
||||||
|
// If there is more than one corresponding function, look for any that have a nonzero size.
|
||||||
|
if (matching_funcs_vec.size() > 1) {
|
||||||
|
size_t nonzero_func_index = (size_t)-1;
|
||||||
|
bool found_nonzero_func = false;
|
||||||
|
for (size_t cur_func_index : matching_funcs_vec) {
|
||||||
|
const auto& cur_func = context.functions[cur_func_index];
|
||||||
|
if (cur_func.words.size() != 0) {
|
||||||
|
if (found_nonzero_func) {
|
||||||
|
ambiguous = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
// If this section is relocatable and the target vram is in the section, don't call functions
|
||||||
|
// in any section other than this one.
|
||||||
|
if (cur_func.section_index == func.section_index ||
|
||||||
|
!(section.relocatable && target_func_vram >= section_vram_start && target_func_vram < section_vram_end)) {
|
||||||
|
found_nonzero_func = true;
|
||||||
|
nonzero_func_index = cur_func_index;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (nonzero_func_index == (size_t)-1) {
|
||||||
|
fmt::print(stderr, "[Warn] Potential jal resolution ambiguity\n");
|
||||||
|
for (size_t cur_func_index : matching_funcs_vec) {
|
||||||
|
fmt::print(stderr, " {}\n", context.functions[cur_func_index].name);
|
||||||
|
}
|
||||||
|
nonzero_func_index = 0;
|
||||||
|
}
|
||||||
|
real_func_index = nonzero_func_index;
|
||||||
|
ambiguous = false;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
real_func_index = matching_funcs_vec.front();
|
||||||
|
ambiguous = false;
|
||||||
|
}
|
||||||
|
if (ambiguous) {
|
||||||
|
fmt::print(stderr, "Ambiguous jal target: 0x{:08X}\n", target_func_vram);
|
||||||
|
for (size_t cur_func_index : matching_funcs_vec) {
|
||||||
|
const auto& cur_func = context.functions[cur_func_index];
|
||||||
|
fmt::print(stderr, " {}\n", cur_func.name);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
jal_target_name = context.functions[real_func_index].name;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
const auto& section = context.sections[func.section_index];
|
||||||
|
if (target_func_vram >= section.ram_addr && target_func_vram < section.ram_addr + section.size) {
|
||||||
|
jal_target_name = fmt::format("static_{}_{:08X}", func.section_index, target_func_vram);
|
||||||
|
static_funcs_out[func.section_index].push_back(target_func_vram);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
fmt::print(stderr, "No function found for jal target: 0x{:08X}\n", target_func_vram);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
needs_link_branch = true;
|
||||||
|
print_unconditional_branch("{}(rdram, ctx)", jal_target_name);
|
||||||
|
};
|
||||||
|
|
||||||
if (indent) {
|
if (indent) {
|
||||||
print_indent();
|
print_indent();
|
||||||
}
|
}
|
||||||
|
@ -148,18 +214,20 @@ bool process_instruction(const RecompPort::Context& context, const RecompPort::F
|
||||||
std::string unsigned_imm_string;
|
std::string unsigned_imm_string;
|
||||||
std::string signed_imm_string;
|
std::string signed_imm_string;
|
||||||
|
|
||||||
|
uint32_t func_vram_end = func.vram + func.words.size() * sizeof(func.words[0]);
|
||||||
|
|
||||||
if (!at_reloc) {
|
if (!at_reloc) {
|
||||||
unsigned_imm_string = fmt::format("{:#X}", imm);
|
unsigned_imm_string = fmt::format("{:#X}", imm);
|
||||||
signed_imm_string = fmt::format("{:#X}", (int16_t)imm);
|
signed_imm_string = fmt::format("{:#X}", (int16_t)imm);
|
||||||
} else {
|
} else {
|
||||||
switch (reloc_type) {
|
switch (reloc_type) {
|
||||||
case RecompPort::RelocType::R_MIPS_HI16:
|
case RecompPort::RelocType::R_MIPS_HI16:
|
||||||
unsigned_imm_string = fmt::format("RELOC_HI16({}, {:#X})", reloc_section, reloc_target_section_offset);
|
unsigned_imm_string = fmt::format("RELOC_HI16({}, {:#X})", (uint32_t)func.section_index, reloc_target_section_offset);
|
||||||
signed_imm_string = "(int16_t)" + unsigned_imm_string;
|
signed_imm_string = "(int16_t)" + unsigned_imm_string;
|
||||||
reloc_handled = true;
|
reloc_handled = true;
|
||||||
break;
|
break;
|
||||||
case RecompPort::RelocType::R_MIPS_LO16:
|
case RecompPort::RelocType::R_MIPS_LO16:
|
||||||
unsigned_imm_string = fmt::format("RELOC_LO16({}, {:#X})", reloc_section, reloc_target_section_offset);
|
unsigned_imm_string = fmt::format("RELOC_LO16({}, {:#X})", (uint32_t)func.section_index, reloc_target_section_offset);
|
||||||
signed_imm_string = "(int16_t)" + unsigned_imm_string;
|
signed_imm_string = "(int16_t)" + unsigned_imm_string;
|
||||||
reloc_handled = true;
|
reloc_handled = true;
|
||||||
break;
|
break;
|
||||||
|
@ -170,6 +238,33 @@ bool process_instruction(const RecompPort::Context& context, const RecompPort::F
|
||||||
case InstrId::cpu_nop:
|
case InstrId::cpu_nop:
|
||||||
fmt::print(output_file, "\n");
|
fmt::print(output_file, "\n");
|
||||||
break;
|
break;
|
||||||
|
// Cop0 (Limited functionality)
|
||||||
|
case InstrId::cpu_mfc0:
|
||||||
|
{
|
||||||
|
Cop0Reg reg = instr.Get_cop0d();
|
||||||
|
switch (reg) {
|
||||||
|
case Cop0Reg::COP0_Status:
|
||||||
|
print_line("{}{} = cop0_status_read(ctx)", ctx_gpr_prefix(rt), rt);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
fmt::print(stderr, "Unhandled cop0 register in mfc0: {}\n", (int)reg);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case InstrId::cpu_mtc0:
|
||||||
|
{
|
||||||
|
Cop0Reg reg = instr.Get_cop0d();
|
||||||
|
switch (reg) {
|
||||||
|
case Cop0Reg::COP0_Status:
|
||||||
|
print_line("cop0_status_write(ctx, {}{})", ctx_gpr_prefix(rt), rt);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
fmt::print(stderr, "Unhandled cop0 register in mtc0: {}\n", (int)reg);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
// Arithmetic
|
// Arithmetic
|
||||||
case InstrId::cpu_lui:
|
case InstrId::cpu_lui:
|
||||||
print_line("{}{} = S32({} << 16)", ctx_gpr_prefix(rt), rt, unsigned_imm_string);
|
print_line("{}{} = S32({} << 16)", ctx_gpr_prefix(rt), rt, unsigned_imm_string);
|
||||||
|
@ -202,6 +297,10 @@ bool process_instruction(const RecompPort::Context& context, const RecompPort::F
|
||||||
case InstrId::cpu_addiu:
|
case InstrId::cpu_addiu:
|
||||||
print_line("{}{} = ADD32({}{}, {})", ctx_gpr_prefix(rt), rt, ctx_gpr_prefix(rs), rs, signed_imm_string);
|
print_line("{}{} = ADD32({}{}, {})", ctx_gpr_prefix(rt), rt, ctx_gpr_prefix(rs), rs, signed_imm_string);
|
||||||
break;
|
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:
|
case InstrId::cpu_and:
|
||||||
print_line("{}{} = {}{} & {}{}", ctx_gpr_prefix(rd), rd, ctx_gpr_prefix(rs), rs, ctx_gpr_prefix(rt), rt);
|
print_line("{}{} = {}{} & {}{}", ctx_gpr_prefix(rd), rd, ctx_gpr_prefix(rs), rs, ctx_gpr_prefix(rt), rt);
|
||||||
break;
|
break;
|
||||||
|
@ -226,21 +325,48 @@ bool process_instruction(const RecompPort::Context& context, const RecompPort::F
|
||||||
case InstrId::cpu_sll:
|
case InstrId::cpu_sll:
|
||||||
print_line("{}{} = S32({}{}) << {}", ctx_gpr_prefix(rd), rd, ctx_gpr_prefix(rt), rt, sa);
|
print_line("{}{} = S32({}{}) << {}", ctx_gpr_prefix(rd), rd, ctx_gpr_prefix(rt), rt, sa);
|
||||||
break;
|
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:
|
case InstrId::cpu_sllv:
|
||||||
print_line("{}{} = S32({}{}) << ({}{} & 31)", ctx_gpr_prefix(rd), rd, ctx_gpr_prefix(rt), rt, ctx_gpr_prefix(rs), rs);
|
print_line("{}{} = S32({}{}) << ({}{} & 31)", ctx_gpr_prefix(rd), rd, ctx_gpr_prefix(rt), rt, ctx_gpr_prefix(rs), rs);
|
||||||
break;
|
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:
|
case InstrId::cpu_sra:
|
||||||
print_line("{}{} = S32(SIGNED({}{}) >> {})", ctx_gpr_prefix(rd), rd, ctx_gpr_prefix(rt), rt, sa);
|
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;
|
break;
|
||||||
case InstrId::cpu_srav:
|
case InstrId::cpu_srav:
|
||||||
print_line("{}{} = S32(SIGNED({}{}) >> ({}{} & 31))", ctx_gpr_prefix(rd), rd, ctx_gpr_prefix(rt), rt, ctx_gpr_prefix(rs), rs);
|
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;
|
break;
|
||||||
case InstrId::cpu_srl:
|
case InstrId::cpu_srl:
|
||||||
print_line("{}{} = S32(U32({}{}) >> {})", ctx_gpr_prefix(rd), rd, ctx_gpr_prefix(rt), rt, sa);
|
print_line("{}{} = S32(U32({}{}) >> {})", ctx_gpr_prefix(rd), rd, ctx_gpr_prefix(rt), rt, sa);
|
||||||
break;
|
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:
|
case InstrId::cpu_srlv:
|
||||||
print_line("{}{} = S32(U32({}{}) >> ({}{} & 31))", ctx_gpr_prefix(rd), rd, ctx_gpr_prefix(rt), rt, ctx_gpr_prefix(rs), rs);
|
print_line("{}{} = S32(U32({}{}) >> ({}{} & 31))", ctx_gpr_prefix(rd), rd, ctx_gpr_prefix(rt), rt, ctx_gpr_prefix(rs), rs);
|
||||||
break;
|
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:
|
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);
|
print_line("{}{} = SIGNED({}{}) < SIGNED({}{}) ? 1 : 0", ctx_gpr_prefix(rd), rd, ctx_gpr_prefix(rs), rs, ctx_gpr_prefix(rt), rt);
|
||||||
break;
|
break;
|
||||||
|
@ -273,7 +399,9 @@ bool process_instruction(const RecompPort::Context& context, const RecompPort::F
|
||||||
print_line("{}{} = hi", ctx_gpr_prefix(rd), rd);
|
print_line("{}{} = hi", ctx_gpr_prefix(rd), rd);
|
||||||
break;
|
break;
|
||||||
// Loads
|
// Loads
|
||||||
// TODO ld
|
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:
|
case InstrId::cpu_lw:
|
||||||
print_line("{}{} = MEM_W({}, {}{})", ctx_gpr_prefix(rt), rt, signed_imm_string, ctx_gpr_prefix(base), base);
|
print_line("{}{} = MEM_W({}, {}{})", ctx_gpr_prefix(rt), rt, signed_imm_string, ctx_gpr_prefix(base), base);
|
||||||
break;
|
break;
|
||||||
|
@ -290,6 +418,9 @@ bool process_instruction(const RecompPort::Context& context, const RecompPort::F
|
||||||
print_line("{}{} = MEM_BU({}, {}{})", ctx_gpr_prefix(rt), rt, signed_imm_string, ctx_gpr_prefix(base), base);
|
print_line("{}{} = MEM_BU({}, {}{})", ctx_gpr_prefix(rt), rt, signed_imm_string, ctx_gpr_prefix(base), base);
|
||||||
break;
|
break;
|
||||||
// Stores
|
// Stores
|
||||||
|
case InstrId::cpu_sd:
|
||||||
|
print_line("SD({}{}, {}, {}{})", ctx_gpr_prefix(rt), rt, signed_imm_string, ctx_gpr_prefix(base), base);
|
||||||
|
break;
|
||||||
case InstrId::cpu_sw:
|
case InstrId::cpu_sw:
|
||||||
print_line("MEM_W({}, {}{}) = {}{}", signed_imm_string, ctx_gpr_prefix(base), base, ctx_gpr_prefix(rt), rt);
|
print_line("MEM_W({}, {}{}) = {}{}", signed_imm_string, ctx_gpr_prefix(base), base, ctx_gpr_prefix(rt), rt);
|
||||||
break;
|
break;
|
||||||
|
@ -342,67 +473,8 @@ bool process_instruction(const RecompPort::Context& context, const RecompPort::F
|
||||||
|
|
||||||
// Branches
|
// Branches
|
||||||
case InstrId::cpu_jal:
|
case InstrId::cpu_jal:
|
||||||
{
|
print_func_call(instr.getBranchVramGeneric());
|
||||||
uint32_t target_func_vram = instr.getBranchVramGeneric();
|
|
||||||
const auto matching_funcs_find = context.functions_by_vram.find(target_func_vram);
|
|
||||||
std::string jal_target_name;
|
|
||||||
// TODO the current section should be prioritized if the target jal is in its vram even if a function isn't known (i.e. static)
|
|
||||||
if (matching_funcs_find != context.functions_by_vram.end()) {
|
|
||||||
// If we found matches for the target function by vram,
|
|
||||||
const auto& matching_funcs_vec = matching_funcs_find->second;
|
|
||||||
size_t real_func_index;
|
|
||||||
bool ambiguous;
|
|
||||||
// If there is more than one corresponding function, look for any that have a nonzero size
|
|
||||||
if (matching_funcs_vec.size() > 1) {
|
|
||||||
size_t nonzero_func_index = (size_t)-1;
|
|
||||||
bool found_nonzero_func = false;
|
|
||||||
for (size_t cur_func_index : matching_funcs_vec) {
|
|
||||||
const auto& cur_func = context.functions[cur_func_index];
|
|
||||||
if (cur_func.words.size() != 0) {
|
|
||||||
if (found_nonzero_func) {
|
|
||||||
ambiguous = true;
|
|
||||||
break;
|
break;
|
||||||
}
|
|
||||||
found_nonzero_func = true;
|
|
||||||
nonzero_func_index = cur_func_index;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (nonzero_func_index == (size_t)-1) {
|
|
||||||
fmt::print(stderr, "[Warn] Potential jal resolution ambiguity\n");
|
|
||||||
for (size_t cur_func_index : matching_funcs_vec) {
|
|
||||||
fmt::print(stderr, " {}\n", context.functions[cur_func_index].name);
|
|
||||||
}
|
|
||||||
nonzero_func_index = 0;
|
|
||||||
}
|
|
||||||
real_func_index = nonzero_func_index;
|
|
||||||
ambiguous = false;
|
|
||||||
} else {
|
|
||||||
real_func_index = matching_funcs_vec.front();
|
|
||||||
ambiguous = false;
|
|
||||||
}
|
|
||||||
if (ambiguous) {
|
|
||||||
fmt::print(stderr, "Ambiguous jal target: 0x{:08X}\n", target_func_vram);
|
|
||||||
for (size_t cur_func_index : matching_funcs_vec) {
|
|
||||||
const auto& cur_func = context.functions[cur_func_index];
|
|
||||||
fmt::print(stderr, " {}\n", cur_func.name);
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
jal_target_name = context.functions[real_func_index].name;
|
|
||||||
} else {
|
|
||||||
const auto& section = context.sections[func.section_index];
|
|
||||||
if (target_func_vram >= section.ram_addr && target_func_vram < section.ram_addr + section.size) {
|
|
||||||
jal_target_name = fmt::format("static_{}_{:08X}", func.section_index, target_func_vram);
|
|
||||||
static_funcs_out[func.section_index].push_back(target_func_vram);
|
|
||||||
} else {
|
|
||||||
fmt::print(stderr, "No function found for jal target: 0x{:08X}\n", target_func_vram);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
needs_link_branch = true;
|
|
||||||
print_unconditional_branch("{}(rdram, ctx)", jal_target_name);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case InstrId::cpu_jalr:
|
case InstrId::cpu_jalr:
|
||||||
// jalr can only be handled with $ra as the return address register
|
// jalr can only be handled with $ra as the return address register
|
||||||
if (rd != (int)rabbitizer::Registers::Cpu::GprO32::GPR_O32_ra) {
|
if (rd != (int)rabbitizer::Registers::Cpu::GprO32::GPR_O32_ra) {
|
||||||
|
@ -419,8 +491,20 @@ bool process_instruction(const RecompPort::Context& context, const RecompPort::F
|
||||||
if (branch_target == instr_vram) {
|
if (branch_target == instr_vram) {
|
||||||
print_line("void pause_self(uint8_t *rdram); pause_self(rdram)");
|
print_line("void pause_self(uint8_t *rdram); pause_self(rdram)");
|
||||||
}
|
}
|
||||||
|
// Check if the branch is within this function
|
||||||
|
else if (branch_target >= func.vram && branch_target < func_vram_end) {
|
||||||
print_unconditional_branch("goto L_{:08X}", branch_target);
|
print_unconditional_branch("goto L_{:08X}", branch_target);
|
||||||
}
|
}
|
||||||
|
// Otherwise, check if it's a tail call
|
||||||
|
else if (instr_vram == func_vram_end - 2 * sizeof(func.words[0])) {
|
||||||
|
fmt::print("Tail call in {}\n", func.name);
|
||||||
|
print_func_call(branch_target);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
fmt::print(stderr, "Unhandled branch in {} at 0x{:08X} to 0x{:08X}\n", func.name, instr_vram, branch_target);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case InstrId::cpu_jr:
|
case InstrId::cpu_jr:
|
||||||
if (rs == (int)rabbitizer::Registers::Cpu::GprO32::GPR_O32_ra) {
|
if (rs == (int)rabbitizer::Registers::Cpu::GprO32::GPR_O32_ra) {
|
||||||
|
@ -439,7 +523,7 @@ bool process_instruction(const RecompPort::Context& context, const RecompPort::F
|
||||||
if (reloc_index + 1 < section.relocs.size() && next_vram > section.relocs[reloc_index].address) {
|
if (reloc_index + 1 < section.relocs.size() && next_vram > section.relocs[reloc_index].address) {
|
||||||
next_reloc_index++;
|
next_reloc_index++;
|
||||||
}
|
}
|
||||||
process_instruction(context, func, stats, skipped_insns, instr_index + 1, instructions, output_file, false, false, link_branch_index, next_reloc_index, dummy_needs_link_branch, dummy_is_branch_likely, static_funcs_out);
|
process_instruction(context, config, func, stats, skipped_insns, instr_index + 1, instructions, output_file, false, false, link_branch_index, next_reloc_index, dummy_needs_link_branch, dummy_is_branch_likely, static_funcs_out);
|
||||||
print_indent();
|
print_indent();
|
||||||
fmt::print(output_file, "switch (jr_addend_{:08X} >> 2) {{\n", cur_jtbl.jr_vram);
|
fmt::print(output_file, "switch (jr_addend_{:08X} >> 2) {{\n", cur_jtbl.jr_vram);
|
||||||
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++) {
|
||||||
|
@ -459,16 +543,27 @@ bool process_instruction(const RecompPort::Context& context, const RecompPort::F
|
||||||
});
|
});
|
||||||
|
|
||||||
if (jump_find_result != stats.absolute_jumps.end()) {
|
if (jump_find_result != stats.absolute_jumps.end()) {
|
||||||
needs_link_branch = true;
|
|
||||||
print_unconditional_branch("LOOKUP_FUNC({})(rdram, ctx)", (uint64_t)(int32_t)jump_find_result->jump_target);
|
print_unconditional_branch("LOOKUP_FUNC({})(rdram, ctx)", (uint64_t)(int32_t)jump_find_result->jump_target);
|
||||||
// jr doesn't link so it acts like a tail call, meaning we should return directly after the jump returns
|
// jr doesn't link so it acts like a tail call, meaning we should return directly after the jump returns
|
||||||
print_line("return");
|
print_line("return");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool is_tail_call = instr_vram == func_vram_end - 2 * sizeof(func.words[0]);
|
||||||
fmt::print(stderr, "No jump table found for jr at 0x{:08X}\n", instr_vram);
|
if (is_tail_call) {
|
||||||
|
fmt::print("Indirect tail call in {}\n", func.name);
|
||||||
|
print_unconditional_branch("LOOKUP_FUNC({}{})(rdram, ctx)", ctx_gpr_prefix(rs), rs);
|
||||||
|
print_line("return");
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fmt::print(stderr, "No jump table found for jr at 0x{:08X} and not tail call\n", instr_vram);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case InstrId::cpu_syscall:
|
||||||
|
print_line("recomp_syscall_handler(rdram, ctx, 0x{:08X})", instr_vram);
|
||||||
|
// syscalls don't link, so treat it like a tail call
|
||||||
|
print_line("return");
|
||||||
break;
|
break;
|
||||||
case InstrId::cpu_bnel:
|
case InstrId::cpu_bnel:
|
||||||
is_branch_likely = true;
|
is_branch_likely = true;
|
||||||
|
@ -530,7 +625,7 @@ bool process_instruction(const RecompPort::Context& context, const RecompPort::F
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// odd fpr
|
// odd fpr
|
||||||
print_line("ctx->f{}.u32h = {}{}", fs - 1, ctx_gpr_prefix(rt), rt);
|
print_line("ctx->f_odd[({} - 1) * 2] = {}{}", fs, ctx_gpr_prefix(rt), rt);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case InstrId::cpu_mfc1:
|
case InstrId::cpu_mfc1:
|
||||||
|
@ -539,7 +634,7 @@ bool process_instruction(const RecompPort::Context& context, const RecompPort::F
|
||||||
print_line("{}{} = (int32_t)ctx->f{}.u32l", ctx_gpr_prefix(rt), rt, fs);
|
print_line("{}{} = (int32_t)ctx->f{}.u32l", ctx_gpr_prefix(rt), rt, fs);
|
||||||
} else {
|
} else {
|
||||||
// odd fpr
|
// odd fpr
|
||||||
print_line("{}{} = (int32_t)ctx->f{}.u32h", ctx_gpr_prefix(rt), rt, fs - 1);
|
print_line("{}{} = (int32_t)ctx->f_odd[({} - 1) * 2]", ctx_gpr_prefix(rt), rt, fs);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
//case InstrId::cpu_dmfc1:
|
//case InstrId::cpu_dmfc1:
|
||||||
|
@ -557,16 +652,12 @@ bool process_instruction(const RecompPort::Context& context, const RecompPort::F
|
||||||
print_line("ctx->f{}.u32l = MEM_W({}, {}{})", ft, signed_imm_string, ctx_gpr_prefix(base), base);
|
print_line("ctx->f{}.u32l = MEM_W({}, {}{})", ft, signed_imm_string, ctx_gpr_prefix(base), base);
|
||||||
} else {
|
} else {
|
||||||
// odd fpr
|
// odd fpr
|
||||||
print_line("ctx->f{}.u32h = MEM_W({}, {}{})", ft - 1, signed_imm_string, ctx_gpr_prefix(base), base);
|
print_line("ctx->f_odd[({} - 1) * 2] = MEM_W({}, {}{})", ft, signed_imm_string, ctx_gpr_prefix(base), base);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case InstrId::cpu_ldc1:
|
case InstrId::cpu_ldc1:
|
||||||
if ((ft & 1) == 0) {
|
print_line("CHECK_FR(ctx, {})", ft);
|
||||||
print_line("ctx->f{}.u64 = LD({}, {}{})", ft, signed_imm_string, ctx_gpr_prefix(base), base);
|
print_line("ctx->f{}.u64 = LD({}, {}{})", ft, signed_imm_string, ctx_gpr_prefix(base), base);
|
||||||
} else {
|
|
||||||
fmt::print(stderr, "Invalid operand for ldc1: f{}\n", ft);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
case InstrId::cpu_swc1:
|
case InstrId::cpu_swc1:
|
||||||
if ((ft & 1) == 0) {
|
if ((ft & 1) == 0) {
|
||||||
|
@ -574,66 +665,56 @@ bool process_instruction(const RecompPort::Context& context, const RecompPort::F
|
||||||
print_line("MEM_W({}, {}{}) = ctx->f{}.u32l", signed_imm_string, ctx_gpr_prefix(base), base, ft);
|
print_line("MEM_W({}, {}{}) = ctx->f{}.u32l", signed_imm_string, ctx_gpr_prefix(base), base, ft);
|
||||||
} else {
|
} else {
|
||||||
// odd fpr
|
// odd fpr
|
||||||
print_line("MEM_W({}, {}{}) = ctx->f{}.u32h", signed_imm_string, ctx_gpr_prefix(base), base, ft - 1);
|
print_line("MEM_W({}, {}{}) = ctx->f_odd[({} - 1) * 2]", signed_imm_string, ctx_gpr_prefix(base), base, ft);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case InstrId::cpu_sdc1:
|
case InstrId::cpu_sdc1:
|
||||||
if ((ft & 1) == 0) {
|
print_line("CHECK_FR(ctx, {})", ft);
|
||||||
print_line("SD(ctx->f{}.u64, {}, {}{})", ft, signed_imm_string, ctx_gpr_prefix(base), base);
|
print_line("SD(ctx->f{}.u64, {}, {}{})", ft, signed_imm_string, ctx_gpr_prefix(base), base);
|
||||||
} else {
|
|
||||||
fmt::print(stderr, "Invalid operand for sdc1: f{}\n", ft);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
// Cop1 compares
|
// Cop1 compares
|
||||||
case InstrId::cpu_c_lt_s:
|
case InstrId::cpu_c_lt_s:
|
||||||
if ((fs & 1) == 0 && (ft & 1) == 0) {
|
print_line("CHECK_FR(ctx, {})", fs);
|
||||||
|
print_line("CHECK_FR(ctx, {})", ft);
|
||||||
|
print_line("c1cs = ctx->f{}.fl < ctx->f{}.fl", fs, ft);
|
||||||
|
break;
|
||||||
|
case InstrId::cpu_c_olt_s:
|
||||||
|
print_line("CHECK_FR(ctx, {})", fs);
|
||||||
|
print_line("CHECK_FR(ctx, {})", ft);
|
||||||
|
//print_line("*(volatile int*)0 = 0;");
|
||||||
print_line("c1cs = ctx->f{}.fl < ctx->f{}.fl", fs, ft);
|
print_line("c1cs = ctx->f{}.fl < ctx->f{}.fl", fs, ft);
|
||||||
} else {
|
|
||||||
fmt::print(stderr, "Invalid operand for c.lt.s: f{} f{}\n", fs, ft);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
case InstrId::cpu_c_lt_d:
|
case InstrId::cpu_c_lt_d:
|
||||||
if ((fs & 1) == 0 && (ft & 1) == 0) {
|
print_line("CHECK_FR(ctx, {})", fs);
|
||||||
|
print_line("CHECK_FR(ctx, {})", ft);
|
||||||
print_line("c1cs = ctx->f{}.d < ctx->f{}.d", fs, ft);
|
print_line("c1cs = ctx->f{}.d < ctx->f{}.d", fs, ft);
|
||||||
} else {
|
|
||||||
fmt::print(stderr, "Invalid operand for c.lt.d: f{} f{}\n", fs, ft);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
case InstrId::cpu_c_le_s:
|
case InstrId::cpu_c_le_s:
|
||||||
if ((fs & 1) == 0 && (ft & 1) == 0) {
|
print_line("CHECK_FR(ctx, {})", fs);
|
||||||
|
print_line("CHECK_FR(ctx, {})", ft);
|
||||||
|
print_line("c1cs = ctx->f{}.fl <= ctx->f{}.fl", fs, ft);
|
||||||
|
break;
|
||||||
|
case InstrId::cpu_c_ole_s:
|
||||||
|
print_line("CHECK_FR(ctx, {})", fs);
|
||||||
|
print_line("CHECK_FR(ctx, {})", ft);
|
||||||
|
//print_line("*(volatile int*)0 = 0;");
|
||||||
print_line("c1cs = ctx->f{}.fl <= ctx->f{}.fl", fs, ft);
|
print_line("c1cs = ctx->f{}.fl <= ctx->f{}.fl", fs, ft);
|
||||||
} else {
|
|
||||||
fmt::print(stderr, "Invalid operand for c.le.s: f{} f{}\n", fs, ft);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
case InstrId::cpu_c_le_d:
|
case InstrId::cpu_c_le_d:
|
||||||
if ((fs & 1) == 0 && (ft & 1) == 0) {
|
print_line("CHECK_FR(ctx, {})", fs);
|
||||||
|
print_line("CHECK_FR(ctx, {})", ft);
|
||||||
print_line("c1cs = ctx->f{}.d <= ctx->f{}.d", fs, ft);
|
print_line("c1cs = ctx->f{}.d <= ctx->f{}.d", fs, ft);
|
||||||
} else {
|
|
||||||
fmt::print(stderr, "Invalid operand for c.le.d: f{} f{}\n", fs, ft);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
case InstrId::cpu_c_eq_s:
|
case InstrId::cpu_c_eq_s:
|
||||||
if ((fs & 1) == 0 && (ft & 1) == 0) {
|
print_line("CHECK_FR(ctx, {})", fs);
|
||||||
|
print_line("CHECK_FR(ctx, {})", ft);
|
||||||
print_line("c1cs = ctx->f{}.fl == ctx->f{}.fl", fs, ft);
|
print_line("c1cs = ctx->f{}.fl == ctx->f{}.fl", fs, ft);
|
||||||
} else {
|
|
||||||
fmt::print(stderr, "Invalid operand for c.eq.s: f{} f{}\n", fs, ft);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
case InstrId::cpu_c_eq_d:
|
case InstrId::cpu_c_eq_d:
|
||||||
if ((fs & 1) == 0 && (ft & 1) == 0) {
|
print_line("CHECK_FR(ctx, {})", fs);
|
||||||
|
print_line("CHECK_FR(ctx, {})", ft);
|
||||||
print_line("c1cs = ctx->f{}.d == ctx->f{}.d", fs, ft);
|
print_line("c1cs = ctx->f{}.d == ctx->f{}.d", fs, ft);
|
||||||
} else {
|
|
||||||
fmt::print(stderr, "Invalid operand for c.eq.d: f{} f{}\n", fs, ft);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
// Cop1 branches
|
// Cop1 branches
|
||||||
|
@ -656,236 +737,148 @@ bool process_instruction(const RecompPort::Context& context, const RecompPort::F
|
||||||
|
|
||||||
// Cop1 arithmetic
|
// Cop1 arithmetic
|
||||||
case InstrId::cpu_mov_s:
|
case InstrId::cpu_mov_s:
|
||||||
if ((fd & 1) == 0 && (fs & 1) == 0) {
|
print_line("CHECK_FR(ctx, {})", fd);
|
||||||
// even fpr
|
print_line("CHECK_FR(ctx, {})", fs);
|
||||||
print_line("ctx->f{}.fl = ctx->f{}.fl", fd, fs);
|
print_line("ctx->f{}.fl = ctx->f{}.fl", fd, fs);
|
||||||
} else {
|
|
||||||
fmt::print(stderr, "Invalid operand(s) for mov.s: f{} f{}\n", fd, fs);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
case InstrId::cpu_mov_d:
|
case InstrId::cpu_mov_d:
|
||||||
if ((fd & 1) == 0 && (fs & 1) == 0) {
|
print_line("CHECK_FR(ctx, {})", fd);
|
||||||
// even fpr
|
print_line("CHECK_FR(ctx, {})", fs);
|
||||||
print_line("ctx->f{}.d = ctx->f{}.d", fd, fs);
|
print_line("ctx->f{}.d = ctx->f{}.d", fd, fs);
|
||||||
} else {
|
|
||||||
fmt::print(stderr, "Invalid operand(s) for mov.d: f{} f{}\n", fd, fs);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
case InstrId::cpu_neg_s:
|
case InstrId::cpu_neg_s:
|
||||||
if ((fd & 1) == 0 && (fs & 1) == 0) {
|
print_line("CHECK_FR(ctx, {})", fd);
|
||||||
// even fpr
|
print_line("CHECK_FR(ctx, {})", fs);
|
||||||
print_line("NAN_CHECK(ctx->f{}.fl)", fs);
|
print_line("NAN_CHECK(ctx->f{}.fl)", fs);
|
||||||
print_line("ctx->f{}.fl = -ctx->f{}.fl", fd, fs);
|
print_line("ctx->f{}.fl = -ctx->f{}.fl", fd, fs);
|
||||||
} else {
|
|
||||||
fmt::print(stderr, "Invalid operand(s) for neg.s: f{} f{}\n", fd, fs);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
case InstrId::cpu_neg_d:
|
case InstrId::cpu_neg_d:
|
||||||
if ((fd & 1) == 0 && (fs & 1) == 0) {
|
print_line("CHECK_FR(ctx, {})", fd);
|
||||||
// even fpr
|
print_line("CHECK_FR(ctx, {})", fs);
|
||||||
print_line("NAN_CHECK(ctx->f{}.d)", fs);
|
print_line("NAN_CHECK(ctx->f{}.d)", fs);
|
||||||
print_line("ctx->f{}.d = -ctx->f{}.d", fd, fs);
|
print_line("ctx->f{}.d = -ctx->f{}.d", fd, fs);
|
||||||
} else {
|
|
||||||
fmt::print(stderr, "Invalid operand(s) for neg.d: f{} f{}\n", fd, fs);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
case InstrId::cpu_abs_s:
|
case InstrId::cpu_abs_s:
|
||||||
if ((fd & 1) == 0 && (fs & 1) == 0) {
|
print_line("CHECK_FR(ctx, {})", fd);
|
||||||
// even fpr
|
print_line("CHECK_FR(ctx, {})", fs);
|
||||||
print_line("NAN_CHECK(ctx->f{}.fl)", fs);
|
print_line("NAN_CHECK(ctx->f{}.fl)", fs);
|
||||||
print_line("ctx->f{}.fl = fabsf(ctx->f{}.fl)", fd, fs);
|
print_line("ctx->f{}.fl = fabsf(ctx->f{}.fl)", fd, fs);
|
||||||
} else {
|
|
||||||
fmt::print(stderr, "Invalid operand(s) for abs.s: f{} f{}\n", fd, fs);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
case InstrId::cpu_abs_d:
|
case InstrId::cpu_abs_d:
|
||||||
if ((fd & 1) == 0 && (fs & 1) == 0) {
|
print_line("CHECK_FR(ctx, {})", fd);
|
||||||
// even fpr
|
print_line("CHECK_FR(ctx, {})", fs);
|
||||||
print_line("NAN_CHECK(ctx->f{}.d)", fs);
|
print_line("NAN_CHECK(ctx->f{}.d)", fs);
|
||||||
print_line("ctx->f{}.d = fabs(ctx->f{}.d)", fd, fs);
|
print_line("ctx->f{}.d = fabs(ctx->f{}.d)", fd, fs);
|
||||||
} else {
|
|
||||||
fmt::print(stderr, "Invalid operand(s) for abs.d: f{} f{}\n", fd, fs);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
case InstrId::cpu_sqrt_s:
|
case InstrId::cpu_sqrt_s:
|
||||||
if ((fd & 1) == 0 && (fs & 1) == 0) {
|
print_line("CHECK_FR(ctx, {})", fd);
|
||||||
// even fpr
|
print_line("CHECK_FR(ctx, {})", fs);
|
||||||
print_line("NAN_CHECK(ctx->f{}.fl)", fs);
|
print_line("NAN_CHECK(ctx->f{}.fl)", fs);
|
||||||
print_line("ctx->f{}.fl = sqrtf(ctx->f{}.fl)", fd, fs);
|
print_line("ctx->f{}.fl = sqrtf(ctx->f{}.fl)", fd, fs);
|
||||||
} else {
|
|
||||||
fmt::print(stderr, "Invalid operand(s) for sqrt.s: f{} f{}\n", fd, fs);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
case InstrId::cpu_sqrt_d:
|
case InstrId::cpu_sqrt_d:
|
||||||
if ((fd & 1) == 0 && (fs & 1) == 0) {
|
print_line("CHECK_FR(ctx, {})", fd);
|
||||||
// even fpr
|
print_line("CHECK_FR(ctx, {})", fs);
|
||||||
print_line("NAN_CHECK(ctx->f{}.d)", fs);
|
print_line("NAN_CHECK(ctx->f{}.d)", fs);
|
||||||
print_line("ctx->f{}.d = sqrt(ctx->f{}.d)", fd, fs);
|
print_line("ctx->f{}.d = sqrt(ctx->f{}.d)", fd, fs);
|
||||||
} else {
|
|
||||||
fmt::print(stderr, "Invalid operand(s) for sqrt.d: f{} f{}\n", fd, fs);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
case InstrId::cpu_add_s:
|
case InstrId::cpu_add_s:
|
||||||
if ((fd & 1) == 0 && (fs & 1) == 0 && (ft & 1) == 0) {
|
print_line("CHECK_FR(ctx, {})", fd);
|
||||||
// even fpr
|
print_line("CHECK_FR(ctx, {})", fs);
|
||||||
|
print_line("CHECK_FR(ctx, {})", ft);
|
||||||
print_line("NAN_CHECK(ctx->f{}.fl); NAN_CHECK(ctx->f{}.fl)", fs, ft);
|
print_line("NAN_CHECK(ctx->f{}.fl); NAN_CHECK(ctx->f{}.fl)", fs, ft);
|
||||||
print_line("ctx->f{}.fl = ctx->f{}.fl + ctx->f{}.fl", fd, fs, ft);
|
print_line("ctx->f{}.fl = ctx->f{}.fl + ctx->f{}.fl", fd, fs, ft);
|
||||||
} else {
|
|
||||||
fmt::print(stderr, "Invalid operand(s) for add.s: f{} f{} f{}\n", fd, fs, ft);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
case InstrId::cpu_add_d:
|
case InstrId::cpu_add_d:
|
||||||
if ((fd & 1) == 0 && (fs & 1) == 0 && (ft & 1) == 0) {
|
print_line("CHECK_FR(ctx, {})", fd);
|
||||||
// even fpr
|
print_line("CHECK_FR(ctx, {})", fs);
|
||||||
|
print_line("CHECK_FR(ctx, {})", ft);
|
||||||
print_line("NAN_CHECK(ctx->f{}.d); NAN_CHECK(ctx->f{}.d)", fs, ft);
|
print_line("NAN_CHECK(ctx->f{}.d); NAN_CHECK(ctx->f{}.d)", fs, ft);
|
||||||
print_line("ctx->f{}.d = ctx->f{}.d + ctx->f{}.d", fd, fs, ft);
|
print_line("ctx->f{}.d = ctx->f{}.d + ctx->f{}.d", fd, fs, ft);
|
||||||
} else {
|
|
||||||
fmt::print(stderr, "Invalid operand(s) for add.d: f{} f{} f{}\n", fd, fs, ft);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
case InstrId::cpu_sub_s:
|
case InstrId::cpu_sub_s:
|
||||||
if ((fd & 1) == 0 && (fs & 1) == 0 && (ft & 1) == 0) {
|
print_line("CHECK_FR(ctx, {})", fd);
|
||||||
// even fpr
|
print_line("CHECK_FR(ctx, {})", fs);
|
||||||
|
print_line("CHECK_FR(ctx, {})", ft);
|
||||||
print_line("NAN_CHECK(ctx->f{}.fl); NAN_CHECK(ctx->f{}.fl)", fs, ft);
|
print_line("NAN_CHECK(ctx->f{}.fl); NAN_CHECK(ctx->f{}.fl)", fs, ft);
|
||||||
print_line("ctx->f{}.fl = ctx->f{}.fl - ctx->f{}.fl", fd, fs, ft);
|
print_line("ctx->f{}.fl = ctx->f{}.fl - ctx->f{}.fl", fd, fs, ft);
|
||||||
} else {
|
|
||||||
fmt::print(stderr, "Invalid operand(s) for sub.s: f{} f{} f{}\n", fd, fs, ft);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
case InstrId::cpu_sub_d:
|
case InstrId::cpu_sub_d:
|
||||||
if ((fd & 1) == 0 && (fs & 1) == 0 && (ft & 1) == 0) {
|
print_line("CHECK_FR(ctx, {})", fd);
|
||||||
// even fpr
|
print_line("CHECK_FR(ctx, {})", fs);
|
||||||
|
print_line("CHECK_FR(ctx, {})", ft);
|
||||||
print_line("NAN_CHECK(ctx->f{}.d); NAN_CHECK(ctx->f{}.d)", fs, ft);
|
print_line("NAN_CHECK(ctx->f{}.d); NAN_CHECK(ctx->f{}.d)", fs, ft);
|
||||||
print_line("ctx->f{}.d = ctx->f{}.d - ctx->f{}.d", fd, fs, ft);
|
print_line("ctx->f{}.d = ctx->f{}.d - ctx->f{}.d", fd, fs, ft);
|
||||||
} else {
|
|
||||||
fmt::print(stderr, "Invalid operand(s) for sub.d: f{} f{} f{}\n", fd, fs, ft);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
case InstrId::cpu_mul_s:
|
case InstrId::cpu_mul_s:
|
||||||
if ((fd & 1) == 0 && (fs & 1) == 0 && (ft & 1) == 0) {
|
print_line("CHECK_FR(ctx, {})", fd);
|
||||||
// even fpr
|
print_line("CHECK_FR(ctx, {})", fs);
|
||||||
|
print_line("CHECK_FR(ctx, {})", ft);
|
||||||
print_line("NAN_CHECK(ctx->f{}.fl); NAN_CHECK(ctx->f{}.fl)", fs, ft);
|
print_line("NAN_CHECK(ctx->f{}.fl); NAN_CHECK(ctx->f{}.fl)", fs, ft);
|
||||||
print_line("ctx->f{}.fl = MUL_S(ctx->f{}.fl, ctx->f{}.fl)", fd, fs, ft);
|
print_line("ctx->f{}.fl = MUL_S(ctx->f{}.fl, ctx->f{}.fl)", fd, fs, ft);
|
||||||
} else {
|
|
||||||
fmt::print(stderr, "Invalid operand(s) for mul.s: f{} f{} f{}\n", fd, fs, ft);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
case InstrId::cpu_mul_d:
|
case InstrId::cpu_mul_d:
|
||||||
if ((fd & 1) == 0 && (fs & 1) == 0 && (ft & 1) == 0) {
|
print_line("CHECK_FR(ctx, {})", fd);
|
||||||
// even fpr
|
print_line("CHECK_FR(ctx, {})", fs);
|
||||||
|
print_line("CHECK_FR(ctx, {})", ft);
|
||||||
print_line("NAN_CHECK(ctx->f{}.d); NAN_CHECK(ctx->f{}.d)", fs, ft);
|
print_line("NAN_CHECK(ctx->f{}.d); NAN_CHECK(ctx->f{}.d)", fs, ft);
|
||||||
print_line("ctx->f{}.d = MUL_D(ctx->f{}.d, ctx->f{}.d)", fd, fs, ft);
|
print_line("ctx->f{}.d = MUL_D(ctx->f{}.d, ctx->f{}.d)", fd, fs, ft);
|
||||||
} else {
|
|
||||||
fmt::print(stderr, "Invalid operand(s) for mul.d: f{} f{} f{}\n", fd, fs, ft);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
case InstrId::cpu_div_s:
|
case InstrId::cpu_div_s:
|
||||||
if ((fd & 1) == 0 && (fs & 1) == 0 && (ft & 1) == 0) {
|
print_line("CHECK_FR(ctx, {})", fd);
|
||||||
// even fpr
|
print_line("CHECK_FR(ctx, {})", fs);
|
||||||
|
print_line("CHECK_FR(ctx, {})", ft);
|
||||||
print_line("NAN_CHECK(ctx->f{}.fl); NAN_CHECK(ctx->f{}.fl)", fs, ft);
|
print_line("NAN_CHECK(ctx->f{}.fl); NAN_CHECK(ctx->f{}.fl)", fs, ft);
|
||||||
print_line("ctx->f{}.fl = DIV_S(ctx->f{}.fl, ctx->f{}.fl)", fd, fs, ft);
|
print_line("ctx->f{}.fl = DIV_S(ctx->f{}.fl, ctx->f{}.fl)", fd, fs, ft);
|
||||||
} else {
|
|
||||||
fmt::print(stderr, "Invalid operand(s) for div.s: f{} f{} f{}\n", fd, fs, ft);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
case InstrId::cpu_div_d:
|
case InstrId::cpu_div_d:
|
||||||
if ((fd & 1) == 0 && (fs & 1) == 0 && (ft & 1) == 0) {
|
print_line("CHECK_FR(ctx, {})", fd);
|
||||||
// even fpr
|
print_line("CHECK_FR(ctx, {})", fs);
|
||||||
|
print_line("CHECK_FR(ctx, {})", ft);
|
||||||
print_line("NAN_CHECK(ctx->f{}.d); NAN_CHECK(ctx->f{}.d)", fs, ft);
|
print_line("NAN_CHECK(ctx->f{}.d); NAN_CHECK(ctx->f{}.d)", fs, ft);
|
||||||
print_line("ctx->f{}.d = DIV_D(ctx->f{}.d, ctx->f{}.d)", fd, fs, ft);
|
print_line("ctx->f{}.d = DIV_D(ctx->f{}.d, ctx->f{}.d)", fd, fs, ft);
|
||||||
} else {
|
|
||||||
fmt::print(stderr, "Invalid operand(s) for div.d: f{} f{} f{}\n", fd, fs, ft);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
case InstrId::cpu_cvt_s_w:
|
case InstrId::cpu_cvt_s_w:
|
||||||
if ((fd & 1) == 0 && (fs & 1) == 0) {
|
print_line("CHECK_FR(ctx, {})", fd);
|
||||||
// even fpr
|
print_line("CHECK_FR(ctx, {})", fs);
|
||||||
print_line("ctx->f{}.fl = CVT_S_W(ctx->f{}.u32l)", fd, fs);
|
print_line("ctx->f{}.fl = CVT_S_W(ctx->f{}.u32l)", fd, fs);
|
||||||
} else {
|
|
||||||
fmt::print(stderr, "Invalid operand(s) for cvt.s.w: f{} f{}\n", fd, fs);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
case InstrId::cpu_cvt_d_w:
|
case InstrId::cpu_cvt_d_w:
|
||||||
if ((fd & 1) == 0 && (fs & 1) == 0) {
|
print_line("CHECK_FR(ctx, {})", fd);
|
||||||
// even fpr
|
print_line("CHECK_FR(ctx, {})", fs);
|
||||||
print_line("ctx->f{}.d = CVT_D_W(ctx->f{}.u32l)", fd, fs);
|
print_line("ctx->f{}.d = CVT_D_W(ctx->f{}.u32l)", fd, fs);
|
||||||
} else {
|
|
||||||
fmt::print(stderr, "Invalid operand(s) for cvt.d.w: f{} f{}\n", fd, fs);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
case InstrId::cpu_cvt_d_s:
|
case InstrId::cpu_cvt_d_s:
|
||||||
if ((fd & 1) == 0 && (fs & 1) == 0) {
|
print_line("CHECK_FR(ctx, {})", fd);
|
||||||
// even fpr
|
print_line("CHECK_FR(ctx, {})", fs);
|
||||||
print_line("NAN_CHECK(ctx->f{}.fl)", fs);
|
print_line("NAN_CHECK(ctx->f{}.fl)", fs);
|
||||||
print_line("ctx->f{}.d = CVT_D_S(ctx->f{}.fl)", fd, fs);
|
print_line("ctx->f{}.d = CVT_D_S(ctx->f{}.fl)", fd, fs);
|
||||||
} else {
|
|
||||||
fmt::print(stderr, "Invalid operand(s) for cvt.d.s: f{} f{}\n", fd, fs);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
case InstrId::cpu_cvt_s_d:
|
case InstrId::cpu_cvt_s_d:
|
||||||
if ((fd & 1) == 0 && (fs & 1) == 0) {
|
print_line("CHECK_FR(ctx, {})", fd);
|
||||||
// even fpr
|
print_line("CHECK_FR(ctx, {})", fs);
|
||||||
print_line("NAN_CHECK(ctx->f{}.d)", fs);
|
print_line("NAN_CHECK(ctx->f{}.d)", fs);
|
||||||
print_line("ctx->f{}.fl = CVT_S_D(ctx->f{}.d)", fd, fs);
|
print_line("ctx->f{}.fl = CVT_S_D(ctx->f{}.d)", fd, fs);
|
||||||
} else {
|
|
||||||
fmt::print(stderr, "Invalid operand(s) for cvt.s.d: f{} f{}\n", fd, fs);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
case InstrId::cpu_trunc_w_s:
|
case InstrId::cpu_trunc_w_s:
|
||||||
if ((fd & 1) == 0 && (fs & 1) == 0) {
|
print_line("CHECK_FR(ctx, {})", fd);
|
||||||
// even fpr
|
print_line("CHECK_FR(ctx, {})", fs);
|
||||||
print_line("ctx->f{}.u32l = TRUNC_W_S(ctx->f{}.fl)", fd, fs);
|
print_line("ctx->f{}.u32l = TRUNC_W_S(ctx->f{}.fl)", fd, fs);
|
||||||
} else {
|
|
||||||
fmt::print(stderr, "Invalid operand(s) for trunc.w.s: f{} f{}\n", fd, fs);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
case InstrId::cpu_trunc_w_d:
|
case InstrId::cpu_trunc_w_d:
|
||||||
if ((fd & 1) == 0 && (fs & 1) == 0) {
|
print_line("CHECK_FR(ctx, {})", fd);
|
||||||
// even fpr
|
print_line("CHECK_FR(ctx, {})", fs);
|
||||||
print_line("ctx->f{}.u32l = TRUNC_W_D(ctx->f{}.d)", fd, fs);
|
print_line("ctx->f{}.u32l = TRUNC_W_D(ctx->f{}.d)", fd, fs);
|
||||||
} else {
|
|
||||||
fmt::print(stderr, "Invalid operand(s) for trunc.w.d: f{} f{}\n", fd, fs);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
//case InstrId::cpu_trunc_l_s:
|
//case InstrId::cpu_trunc_l_s:
|
||||||
// if ((fd & 1) == 0 && (fs & 1) == 0) {
|
// print_line("CHECK_FR(ctx, {})", fd);
|
||||||
// // even fpr
|
// print_line("CHECK_FR(ctx, {})", fs);
|
||||||
// print_line("ctx->f{}.u64 = TRUNC_L_S(ctx->f{}.fl)", fd, fs);
|
// print_line("ctx->f{}.u64 = TRUNC_L_S(ctx->f{}.fl)", fd, fs);
|
||||||
// } else {
|
|
||||||
// fmt::print(stderr, "Invalid operand(s) for trunc.l.s: f{} f{}\n", fd, fs);
|
|
||||||
// return false;
|
|
||||||
// }
|
|
||||||
// break;
|
// break;
|
||||||
//case InstrId::cpu_trunc_l_d:
|
//case InstrId::cpu_trunc_l_d:
|
||||||
// if ((fd & 1) == 0 && (fs & 1) == 0) {
|
// print_line("CHECK_FR(ctx, {})", fd);
|
||||||
// // even fpr
|
// print_line("CHECK_FR(ctx, {})", fs);
|
||||||
// print_line("ctx->f{}.u64 = TRUNC_L_D(ctx->f{}.d)", fd, fs);
|
// print_line("ctx->f{}.u64 = TRUNC_L_D(ctx->f{}.d)", fd, fs);
|
||||||
// } else {
|
|
||||||
// fmt::print(stderr, "Invalid operand(s) for trunc.l.d: f{} f{}\n", fd, fs);
|
|
||||||
// return false;
|
|
||||||
// }
|
|
||||||
// break;
|
// break;
|
||||||
case InstrId::cpu_ctc1:
|
case InstrId::cpu_ctc1:
|
||||||
if (cop1_cs != 31) {
|
if (cop1_cs != 31) {
|
||||||
|
@ -902,76 +895,44 @@ bool process_instruction(const RecompPort::Context& context, const RecompPort::F
|
||||||
print_line("{}{} = rounding_mode", ctx_gpr_prefix(rt), rt);
|
print_line("{}{} = rounding_mode", ctx_gpr_prefix(rt), rt);
|
||||||
break;
|
break;
|
||||||
case InstrId::cpu_cvt_w_s:
|
case InstrId::cpu_cvt_w_s:
|
||||||
if ((fd & 1) == 0 && (fs & 1) == 0) {
|
print_line("CHECK_FR(ctx, {})", fd);
|
||||||
// even fpr
|
print_line("CHECK_FR(ctx, {})", fs);
|
||||||
print_line("ctx->f{}.u32l = CVT_W_S(ctx->f{}.fl)", fd, fs);
|
print_line("ctx->f{}.u32l = CVT_W_S(ctx->f{}.fl)", fd, fs);
|
||||||
} else {
|
|
||||||
fmt::print(stderr, "Invalid operand(s) for cvt.w.s: f{} f{}\n", fd, fs);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
case InstrId::cpu_cvt_w_d:
|
case InstrId::cpu_cvt_w_d:
|
||||||
if ((fd & 1) == 0 && (fs & 1) == 0) {
|
print_line("CHECK_FR(ctx, {})", fd);
|
||||||
// even fpr
|
print_line("CHECK_FR(ctx, {})", fs);
|
||||||
print_line("ctx->f{}.u32l = CVT_W_D(ctx->f{}.d)", fd, fs);
|
print_line("ctx->f{}.u32l = CVT_W_D(ctx->f{}.d)", fd, fs);
|
||||||
} else {
|
|
||||||
fmt::print(stderr, "Invalid operand(s) for cvt.w.d: f{} f{}\n", fd, fs);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
case InstrId::cpu_round_w_s:
|
case InstrId::cpu_round_w_s:
|
||||||
if ((fd & 1) == 0 && (fs & 1) == 0) {
|
print_line("CHECK_FR(ctx, {})", fd);
|
||||||
// even fpr
|
print_line("CHECK_FR(ctx, {})", fs);
|
||||||
print_line("ctx->f{}.u32l = lroundf(ctx->f{}.fl)", fd, fs);
|
print_line("ctx->f{}.u32l = lroundf(ctx->f{}.fl)", fd, fs);
|
||||||
} else {
|
|
||||||
fmt::print(stderr, "Invalid operand(s) for round.w.s: f{} f{}\n", fd, fs);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
case InstrId::cpu_round_w_d:
|
case InstrId::cpu_round_w_d:
|
||||||
if ((fd & 1) == 0 && (fs & 1) == 0) {
|
print_line("CHECK_FR(ctx, {})", fd);
|
||||||
// even fpr
|
print_line("CHECK_FR(ctx, {})", fs);
|
||||||
print_line("ctx->f{}.u32l = lround(ctx->f{}.d)", fd, fs);
|
print_line("ctx->f{}.u32l = lround(ctx->f{}.d)", fd, fs);
|
||||||
} else {
|
|
||||||
fmt::print(stderr, "Invalid operand(s) for round.w.d: f{} f{}\n", fd, fs);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
case InstrId::cpu_ceil_w_s:
|
case InstrId::cpu_ceil_w_s:
|
||||||
if ((fd & 1) == 0 && (fs & 1) == 0) {
|
print_line("CHECK_FR(ctx, {})", fd);
|
||||||
// even fpr
|
print_line("CHECK_FR(ctx, {})", fs);
|
||||||
print_line("ctx->f{}.u32l = S32(ceilf(ctx->f{}.fl))", fd, fs);
|
print_line("ctx->f{}.u32l = S32(ceilf(ctx->f{}.fl))", fd, fs);
|
||||||
} else {
|
|
||||||
fmt::print(stderr, "Invalid operand(s) for ceil.w.s: f{} f{}\n", fd, fs);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
case InstrId::cpu_ceil_w_d:
|
case InstrId::cpu_ceil_w_d:
|
||||||
if ((fd & 1) == 0 && (fs & 1) == 0) {
|
print_line("CHECK_FR(ctx, {})", fd);
|
||||||
// even fpr
|
print_line("CHECK_FR(ctx, {})", fs);
|
||||||
print_line("ctx->f{}.u32l = S32(ceil(ctx->f{}.d))", fd, fs);
|
print_line("ctx->f{}.u32l = S32(ceil(ctx->f{}.d))", fd, fs);
|
||||||
} else {
|
|
||||||
fmt::print(stderr, "Invalid operand(s) for ceil.w.d: f{} f{}\n", fd, fs);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
case InstrId::cpu_floor_w_s:
|
case InstrId::cpu_floor_w_s:
|
||||||
if ((fd & 1) == 0 && (fs & 1) == 0) {
|
print_line("CHECK_FR(ctx, {})", fd);
|
||||||
// even fpr
|
print_line("CHECK_FR(ctx, {})", fs);
|
||||||
print_line("ctx->f{}.u32l = S32(floorf(ctx->f{}.fl))", fd, fs);
|
print_line("ctx->f{}.u32l = S32(floorf(ctx->f{}.fl))", fd, fs);
|
||||||
} else {
|
|
||||||
fmt::print(stderr, "Invalid operand(s) for floor.w.s: f{} f{}\n", fd, fs);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
case InstrId::cpu_floor_w_d:
|
case InstrId::cpu_floor_w_d:
|
||||||
if ((fd & 1) == 0 && (fs & 1) == 0) {
|
print_line("CHECK_FR(ctx, {})", fd);
|
||||||
// even fpr
|
print_line("CHECK_FR(ctx, {})", fs);
|
||||||
print_line("ctx->f{}.u32l = S32(floor(ctx->f{}.d))", fd, fs);
|
print_line("ctx->f{}.u32l = S32(floor(ctx->f{}.d))", fd, fs);
|
||||||
} else {
|
|
||||||
fmt::print(stderr, "Invalid operand(s) for floor.w.d: f{} f{}\n", fd, fs);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
fmt::print(stderr, "Unhandled instruction: {}\n", instr.getOpcodeName());
|
fmt::print(stderr, "Unhandled instruction: {}\n", instr.getOpcodeName());
|
||||||
|
@ -986,16 +947,43 @@ bool process_instruction(const RecompPort::Context& context, const RecompPort::F
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool RecompPort::recompile_function(const RecompPort::Context& context, const RecompPort::Function& func, const std::filesystem::path& output_path, std::span<std::vector<uint32_t>> static_funcs_out) {
|
bool compare_files(const std::filesystem::path& file1_path, const std::filesystem::path& file2_path) {
|
||||||
|
static std::vector<char> file1_buf(65536);
|
||||||
|
static std::vector<char> file2_buf(65536);
|
||||||
|
|
||||||
|
std::ifstream file1(file1_path, std::ifstream::ate | std::ifstream::binary); //open file at the end
|
||||||
|
std::ifstream file2(file2_path, std::ifstream::ate | std::ifstream::binary); //open file at the end
|
||||||
|
const std::ifstream::pos_type fileSize = file1.tellg();
|
||||||
|
|
||||||
|
file1.rdbuf()->pubsetbuf(file1_buf.data(), file1_buf.size());
|
||||||
|
file2.rdbuf()->pubsetbuf(file2_buf.data(), file2_buf.size());
|
||||||
|
|
||||||
|
if (fileSize != file2.tellg()) {
|
||||||
|
return false; //different file size
|
||||||
|
}
|
||||||
|
|
||||||
|
file1.seekg(0); //rewind
|
||||||
|
file2.seekg(0); //rewind
|
||||||
|
|
||||||
|
std::istreambuf_iterator<char> begin1(file1);
|
||||||
|
std::istreambuf_iterator<char> begin2(file2);
|
||||||
|
|
||||||
|
return std::equal(begin1, std::istreambuf_iterator<char>(), begin2); //Second argument is end-of-range iterator
|
||||||
|
}
|
||||||
|
|
||||||
|
bool RecompPort::recompile_function(const RecompPort::Context& context, const RecompPort::Config& config, const RecompPort::Function& func, const std::filesystem::path& output_path, std::span<std::vector<uint32_t>> static_funcs_out) {
|
||||||
//fmt::print("Recompiling {}\n", func.name);
|
//fmt::print("Recompiling {}\n", func.name);
|
||||||
std::vector<rabbitizer::InstructionCpu> instructions;
|
std::vector<rabbitizer::InstructionCpu> instructions;
|
||||||
|
|
||||||
// Open the output file and write the file header
|
// Open the output file and write the file header
|
||||||
std::ofstream output_file{ output_path };
|
std::filesystem::path temp_path = output_path;
|
||||||
|
temp_path.replace_extension(".tmp");
|
||||||
|
std::ofstream output_file{ temp_path };
|
||||||
if (!output_file.good()) {
|
if (!output_file.good()) {
|
||||||
fmt::print(stderr, "Failed to open file for writing: {}\n", output_path.string() );
|
fmt::print(stderr, "Failed to open file for writing: {}\n", temp_path.string() );
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt::print(output_file,
|
fmt::print(output_file,
|
||||||
"#include \"recomp.h\"\n"
|
"#include \"recomp.h\"\n"
|
||||||
"#include \"disable_warnings.h\"\n"
|
"#include \"disable_warnings.h\"\n"
|
||||||
|
@ -1053,7 +1041,7 @@ bool RecompPort::recompile_function(const RecompPort::Context& context, const Re
|
||||||
bool needs_link_branch = false;
|
bool needs_link_branch = false;
|
||||||
bool in_likely_delay_slot = false;
|
bool in_likely_delay_slot = false;
|
||||||
const auto& section = context.sections[func.section_index];
|
const auto& section = context.sections[func.section_index];
|
||||||
bool needs_reloc = section.relocatable;
|
bool needs_reloc = section.relocatable && section.relocs.size() > 0;
|
||||||
size_t reloc_index = 0;
|
size_t reloc_index = 0;
|
||||||
for (size_t instr_index = 0; instr_index < instructions.size(); ++instr_index) {
|
for (size_t instr_index = 0; instr_index < instructions.size(); ++instr_index) {
|
||||||
bool had_link_branch = needs_link_branch;
|
bool had_link_branch = needs_link_branch;
|
||||||
|
@ -1075,8 +1063,13 @@ bool RecompPort::recompile_function(const RecompPort::Context& context, const Re
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (section.name == ".anseq") {
|
||||||
|
std::this_thread::yield();
|
||||||
|
}
|
||||||
|
|
||||||
// Process the current instruction and check for errors
|
// Process the current instruction and check for errors
|
||||||
if (process_instruction(context, func, stats, skipped_insns, instr_index, instructions, output_file, false, needs_link_branch, num_link_branches, reloc_index, needs_link_branch, is_branch_likely, static_funcs_out) == false) {
|
if (process_instruction(context, config, func, stats, skipped_insns, instr_index, instructions, output_file, false, needs_link_branch, num_link_branches, reloc_index, needs_link_branch, is_branch_likely, static_funcs_out) == false) {
|
||||||
fmt::print(stderr, "Error in recompilation, clearing {}\n", output_path.string());
|
fmt::print(stderr, "Error in recompilation, clearing {}\n", output_path.string());
|
||||||
output_file.clear();
|
output_file.clear();
|
||||||
return false;
|
return false;
|
||||||
|
@ -1100,5 +1093,17 @@ bool RecompPort::recompile_function(const RecompPort::Context& context, const Re
|
||||||
// Terminate the function
|
// Terminate the function
|
||||||
fmt::print(output_file, ";}}\n");
|
fmt::print(output_file, ";}}\n");
|
||||||
|
|
||||||
|
output_file.close();
|
||||||
|
|
||||||
|
// If a file of the target name exists and it's identical to the output file, delete the output file.
|
||||||
|
// This prevents updating the existing file so that it doesn't need to be rebuilt.
|
||||||
|
if (std::filesystem::exists(output_path) && compare_files(output_path, temp_path)) {
|
||||||
|
std::filesystem::remove(temp_path);
|
||||||
|
}
|
||||||
|
// Otherwise, rename the new file to the target path.
|
||||||
|
else {
|
||||||
|
std::filesystem::rename(temp_path, output_path);
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue