6 Commits

Author SHA1 Message Date
Wiseguy
c1a6dc93bf Implement fixed address and globally loaded sections in mods, respect section index in mod self-section relocs (#150) 2025-07-19 03:39:41 -04:00
Tharo
6860826da3 Implement remaining float comparisons in the op table (ordered/unordered and signaling/non-signaling still unimplemented) (#144) 2025-07-17 22:50:15 -04:00
Wiseguy
7b8b3c1920 Implement remaining odd float operations in live recompiler (#149) 2025-07-17 22:44:49 -04:00
Rainchus
6e7a5bdb2f Add dmtc1 and dmfc1 functionality to the recompiler (#134)
* make osPiReadIo no longer ignored

* remove added dmtc1/dmfc1 functionality

* add dmtc1 and dmfc1 to recompiler
2025-07-07 01:55:15 -04:00
MelonSpeedruns
e76668356b Fixed paths with spaces not being able to Compress-Archive properly. (#141)
* Fixed paths with spaces not being able to compress properly.

Needs testing on Linux and Mac!

* Fixed path for additional files
2025-07-07 01:53:02 -04:00
Wiseguy
3531bc0317 Optional dependencies for mod tool and add dependency name vector in recompiler context (#147) 2025-07-07 01:52:18 -04:00
6 changed files with 179 additions and 54 deletions

View File

@@ -238,15 +238,27 @@ constexpr int get_fpr_double_context_offset(int fpr_index) {
return offsetof(recomp_context, f0.d) + sizeof(recomp_context::f0) * fpr_index;
}
constexpr int get_fpr_u32l_context_offset(int fpr_index) {
constexpr bool is_fpr_u32l(N64Recomp::Operand operand) {
return
operand == N64Recomp::Operand::FdU32L ||
operand == N64Recomp::Operand::FsU32L ||
operand == N64Recomp::Operand::FtU32L;
return false;
}
constexpr void get_fpr_u32l_context_offset(int fpr_index, sljit_compiler* compiler, int odd_float_address_register, sljit_sw& out, sljit_sw& outw) {
if (fpr_index & 1) {
// TODO implement odd floats.
assert(false);
return -1;
// return fmt::format("ctx->f_odd[({} - 1) * 2]", fpr_index);
assert(compiler != nullptr);
// Load ctx->f_odd into the address register.
sljit_emit_op1(compiler, SLJIT_MOV_P, odd_float_address_register, 0, SLJIT_MEM1(Registers::ctx), offsetof(recomp_context, f_odd));
// sljit_emit_op0(compiler, SLJIT_BREAKPOINT);
out = SLJIT_MEM1(odd_float_address_register);
// Set a memory offset of ((fpr_index - 1) * 2) * sizeof(*f_odd).
outw = ((fpr_index - 1) * 2) * sizeof(*recomp_context::f_odd);
}
else {
return offsetof(recomp_context, f0.u32l) + sizeof(recomp_context::f0) * fpr_index;
out = SLJIT_MEM1(Registers::ctx);
outw = offsetof(recomp_context, f0.u32l) + sizeof(recomp_context::f0) * fpr_index;
}
}
@@ -265,7 +277,10 @@ void get_gpr_values(int gpr, sljit_sw& out, sljit_sw& outw) {
}
}
bool get_operand_values(N64Recomp::Operand operand, const N64Recomp::InstructionContext& context, sljit_sw& out, sljit_sw& outw) {
bool get_operand_values(N64Recomp::Operand operand, const N64Recomp::InstructionContext& context, sljit_sw& out, sljit_sw& outw,
sljit_compiler* compiler, int odd_float_address_register
)
{
using namespace N64Recomp;
switch (operand) {
@@ -303,16 +318,13 @@ bool get_operand_values(N64Recomp::Operand operand, const N64Recomp::Instruction
outw = get_fpr_double_context_offset(context.ft);
break;
case Operand::FdU32L:
out = SLJIT_MEM1(Registers::ctx);
outw = get_fpr_u32l_context_offset(context.fd);
get_fpr_u32l_context_offset(context.fd, compiler, odd_float_address_register, out, outw);
break;
case Operand::FsU32L:
out = SLJIT_MEM1(Registers::ctx);
outw = get_fpr_u32l_context_offset(context.fs);
get_fpr_u32l_context_offset(context.fs, compiler, odd_float_address_register, out, outw);
break;
case Operand::FtU32L:
out = SLJIT_MEM1(Registers::ctx);
outw = get_fpr_u32l_context_offset(context.ft);
get_fpr_u32l_context_offset(context.ft, compiler, odd_float_address_register, out, outw);
break;
case Operand::FdU32H:
assert(false);
@@ -389,16 +401,30 @@ void N64Recomp::LiveGenerator::process_binary_op(const BinaryOp& op, const Instr
if (outputs_to_zero(op.output, ctx)) {
return;
}
// Float u32l input operands are not allowed in a binary operation.
if (is_fpr_u32l(op.operands.operands[0]) || is_fpr_u32l(op.operands.operands[1])) {
assert(false);
errored = true;
return;
}
// A float u32l output operand is only allowed for lwc1, which has an op type of LW.
if (is_fpr_u32l(op.output) && op.type != BinaryOpType::LW) {
assert(false);
errored = true;
return;
}
sljit_sw dst;
sljit_sw dstw;
sljit_sw src1;
sljit_sw src1w;
sljit_sw src2;
sljit_sw src2w;
bool output_good = get_operand_values(op.output, ctx, dst, dstw);
bool input0_good = get_operand_values(op.operands.operands[0], ctx, src1, src1w);
bool input1_good = get_operand_values(op.operands.operands[1], ctx, src2, src2w);
bool output_good = get_operand_values(op.output, ctx, dst, dstw, compiler, Registers::arithmetic_temp2);
bool input0_good = get_operand_values(op.operands.operands[0], ctx, src1, src1w, nullptr, 0);
bool input1_good = get_operand_values(op.operands.operands[1], ctx, src2, src2w, nullptr, 0);
if (!output_good || !input0_good || !input1_good) {
assert(false);
@@ -748,6 +774,10 @@ void N64Recomp::LiveGenerator::process_binary_op(const BinaryOp& op, const Instr
case BinaryOpType::LessEqDouble:
do_float_compare_op(SLJIT_F_LESS_EQUAL, SLJIT_SET_F_LESS_EQUAL, true);
break;
case BinaryOpType::False:
// Load 0 into condition destination
sljit_emit_op1(compiler, SLJIT_MOV, dst, dstw, SLJIT_IMM, 0);
break;
// Loads
case BinaryOpType::LD:
@@ -866,12 +896,19 @@ void N64Recomp::LiveGenerator::process_unary_op(const UnaryOp& op, const Instruc
return;
}
// A unary op may have a float u32l as the source or destination, but not both.
if (is_fpr_u32l(op.input) && is_fpr_u32l(op.output)) {
assert(false);
errored = true;
return;
}
sljit_sw dst;
sljit_sw dstw;
sljit_sw src;
sljit_sw srcw;
bool output_good = get_operand_values(op.output, ctx, dst, dstw);
bool input_good = get_operand_values(op.input, ctx, src, srcw);
bool output_good = get_operand_values(op.output, ctx, dst, dstw, compiler, Registers::arithmetic_temp3);
bool input_good = get_operand_values(op.input, ctx, src, srcw, compiler, Registers::arithmetic_temp3);
if (!output_good || !input_good) {
assert(false);
@@ -1089,7 +1126,13 @@ void N64Recomp::LiveGenerator::process_unary_op(const UnaryOp& op, const Instruc
emit_l_from_d_func(do_floor_l_d);
break;
case UnaryOpType::None:
jit_op = SLJIT_MOV;
// Only write 32 bits to the output is a fpr u32l operand.
if (is_fpr_u32l(op.output)) {
jit_op = SLJIT_MOV32;
}
else {
jit_op = SLJIT_MOV;
}
break;
case UnaryOpType::ToS32:
case UnaryOpType::ToInt32:
@@ -1128,7 +1171,7 @@ void N64Recomp::LiveGenerator::process_store_op(const StoreOp& op, const Instruc
sljit_sw srcw;
sljit_sw imm = (sljit_sw)(int16_t)ctx.imm16;
get_operand_values(op.value_input, ctx, src, srcw);
get_operand_values(op.value_input, ctx, src, srcw, compiler, Registers::arithmetic_temp2);
// Only LO16 relocs are valid on stores.
if (ctx.reloc_type != RelocType::R_MIPS_NONE && ctx.reloc_type != RelocType::R_MIPS_LO16) {
@@ -1456,6 +1499,13 @@ void N64Recomp::LiveGenerator::emit_branch_condition(const ConditionalBranchOp&
return;
}
// Branch conditions do not allow float u32l operands.
if (is_fpr_u32l(op.operands.operands[0]) || is_fpr_u32l(op.operands.operands[1])) {
assert(false);
errored = true;
return;
}
sljit_s32 condition_type;
bool cmp_signed = op.operands.operand_operations[0] == UnaryOpType::ToS64;
// Comparisons need to be inverted to account for the fact that the generator is expected to generate a code block that only runs if
@@ -1509,8 +1559,8 @@ void N64Recomp::LiveGenerator::emit_branch_condition(const ConditionalBranchOp&
sljit_sw src2;
sljit_sw src2w;
get_operand_values(op.operands.operands[0], ctx, src1, src1w);
get_operand_values(op.operands.operands[1], ctx, src2, src2w);
get_operand_values(op.operands.operands[0], ctx, src1, src1w, nullptr, 0);
get_operand_values(op.operands.operands[1], ctx, src2, src2w, nullptr, 0);
// Relocations aren't valid on conditional branches.
if(ctx.reloc_type != RelocType::R_MIPS_NONE) {

View File

@@ -35,6 +35,8 @@ struct ModManifest {
std::vector<toml::table> config_options;
std::vector<std::string> dependencies;
std::vector<std::string> full_dependency_strings;
std::vector<std::string> optional_dependencies;
std::vector<std::string> full_optional_dependency_strings;
};
struct ModInputs {
@@ -323,6 +325,31 @@ ModManifest parse_mod_config_manifest(const std::filesystem::path& basedir, cons
});
}
// Optional dependency list (optional)
const toml::array& optional_dependency_array = read_toml_array(manifest_table, "optional_dependencies", false);
if (!optional_dependency_array.empty()) {
// Reserve room for all the dependencies.
ret.dependencies.reserve(optional_dependency_array.size());
optional_dependency_array.for_each([&ret](const auto& el) {
if constexpr (toml::is_string<decltype(el)>) {
size_t dependency_id_length;
bool dependency_version_has_label;
if (!validate_dependency_string(el.template ref<std::string>(), dependency_id_length, dependency_version_has_label)) {
throw toml::parse_error("Invalid optional dependency entry", el.source());
}
if (dependency_version_has_label) {
throw toml::parse_error("Dependency versions may not have labels", el.source());
}
std::string dependency_id = el.template ref<std::string>().substr(0, dependency_id_length);
ret.optional_dependencies.emplace_back(dependency_id);
ret.full_optional_dependency_strings.emplace_back(el.template ref<std::string>());
}
else {
throw toml::parse_error("Invalid type for optional dependency entry", el.source());
}
});
}
// Config schema (optional)
const toml::array& config_options_array = read_toml_array(manifest_table, "config_options", false);
if (!config_options_array.empty()) {
@@ -507,6 +534,10 @@ void write_manifest(const std::filesystem::path& path, const ModManifest& manife
output_data.emplace("dependencies", string_vector_to_toml(manifest.full_dependency_strings));
}
if (!manifest.full_optional_dependency_strings.empty()) {
output_data.emplace("optional_dependencies", string_vector_to_toml(manifest.full_optional_dependency_strings));
}
if (!manifest.config_options.empty()) {
toml::array options_array{};
for (const auto& option : manifest.config_options) {
@@ -974,11 +1005,11 @@ bool create_mod_zip(const std::filesystem::path& output_dir, const ModConfig& co
#ifdef _WIN32
std::filesystem::path temp_zip_path = output_path;
temp_zip_path.replace_extension(".zip");
std::string command_string = fmt::format("powershell -command Compress-Archive -Force -CompressionLevel Optimal -DestinationPath \"{}\" -Path \"{}\",\"{}\",\"{}\"",
std::string command_string = fmt::format("powershell -command Compress-Archive -Force -CompressionLevel Optimal -DestinationPath '{}' -Path '{}','{}','{}'",
temp_zip_path.string(), (output_dir / symbol_filename).string(), (output_dir / binary_filename).string(), (output_dir / manifest_filename).string());
for (const auto& cur_file : config.inputs.additional_files) {
command_string += fmt::format(",\"{}\"", cur_file.string());
command_string += fmt::format(",'{}'", cur_file.string());
}
STARTUPINFOA si{};
@@ -1138,8 +1169,9 @@ int main(int argc, const char** argv) {
}
}
// Copy the dependencies from the config into the context.
// Copy the dependencies and optional dependencies from the config into the context.
context.add_dependencies(config.manifest.dependencies);
context.add_dependencies(config.manifest.optional_dependencies);
N64Recomp::ElfParsingConfig elf_config {
.bss_section_suffix = {},

View File

@@ -104,6 +104,8 @@ namespace N64Recomp {
bool executable = false;
bool relocatable = false; // TODO is this needed? relocs being non-empty should be an equivalent check.
bool has_mips32_relocs = false;
bool fixed_address = false; // Only used in mods, indicates that the section shouldn't be relocated or placed into mod memory.
bool globally_loaded = false; // Only used in mods, indicates that the section's functions should be globally loaded. Does not actually load the section's contents into ram.
std::optional<uint32_t> got_ram_addr = std::nullopt;
};
@@ -233,6 +235,8 @@ namespace N64Recomp {
//// Mod dependencies and their symbols
//// Imported values
// Dependency names.
std::vector<std::string> dependencies;
// Mapping of dependency name to dependency index.
std::unordered_map<std::string, size_t> dependencies_by_name;
// List of symbols imported from dependencies.
@@ -276,6 +280,7 @@ namespace N64Recomp {
size_t dependency_index = dependencies_by_name.size();
dependencies.emplace_back(id);
dependencies_by_name.emplace(id, dependency_index);
dependency_events_by_name.resize(dependencies_by_name.size());
dependency_imports_by_name.resize(dependencies_by_name.size());
@@ -295,6 +300,7 @@ namespace N64Recomp {
for (const std::string& dep : new_dependencies) {
size_t dependency_index = dependencies_by_name.size();
dependencies.emplace_back(dep);
dependencies_by_name.emplace(dep, dependency_index);
}

View File

@@ -20,6 +20,11 @@ struct FileSubHeaderV1 {
uint32_t string_data_size;
};
enum class SectionFlags : uint32_t {
FixedAddress = 1 << 0,
GloballyLoaded = 1 << 1,
};
struct SectionHeaderV1 {
uint32_t flags;
uint32_t file_offset;
@@ -172,10 +177,22 @@ bool parse_v1(std::span<const char> data, const std::unordered_map<uint32_t, uin
cur_section.bss_size = section_header->bss_size;
cur_section.name = "mod_section_" + std::to_string(section_index);
cur_section.relocatable = true;
cur_section.fixed_address = (section_header->flags & static_cast<uint32_t>(SectionFlags::FixedAddress)) != 0;
cur_section.globally_loaded = (section_header->flags & static_cast<uint32_t>(SectionFlags::GloballyLoaded)) != 0;
if (cur_section.fixed_address && !cur_section.globally_loaded) {
printf("Fixed address sections that aren't globally loaded aren't currently supported\n");
return false;
}
if (cur_section.globally_loaded && !cur_section.fixed_address) {
printf("A globally loaded section must have a fixed address\n");
return false;
}
uint32_t num_funcs = section_header->num_funcs;
uint32_t num_relocs = section_header->num_relocs;
const FuncV1* funcs = reinterpret_data<FuncV1>(data, offset, num_funcs);
if (funcs == nullptr) {
printf("Failed to read funcs (count: %d)\n", num_funcs);

View File

@@ -9,6 +9,8 @@ namespace N64Recomp {
{ InstrId::cpu_mflo, { UnaryOpType::None, Operand::Rd, Operand::Lo } },
{ InstrId::cpu_mtc1, { UnaryOpType::None, Operand::FsU32L, Operand::Rt } },
{ InstrId::cpu_mfc1, { UnaryOpType::ToInt32, Operand::Rt, Operand::FsU32L } },
{ InstrId::cpu_dmtc1, { UnaryOpType::None, Operand::FsU64, Operand::Rt } },
{ InstrId::cpu_dmfc1, { UnaryOpType::None, Operand::Rt, Operand::FsU64 } },
// Float operations
{ InstrId::cpu_mov_s, { UnaryOpType::None, Operand::Fd, Operand::Fs, true } },
{ InstrId::cpu_mov_d, { UnaryOpType::None, Operand::FdDouble, Operand::FsDouble, true } },
@@ -98,34 +100,46 @@ namespace N64Recomp {
{ InstrId::cpu_mul_d, { BinaryOpType::MulDouble, Operand::FdDouble, {{ UnaryOpType::None, UnaryOpType::None }, { Operand::FsDouble, Operand::FtDouble }}, true, true } },
{ InstrId::cpu_div_s, { BinaryOpType::DivFloat, Operand::Fd, {{ UnaryOpType::None, UnaryOpType::None }, { Operand::Fs, Operand::Ft }}, true, true } },
{ InstrId::cpu_div_d, { BinaryOpType::DivDouble, Operand::FdDouble, {{ UnaryOpType::None, UnaryOpType::None }, { Operand::FsDouble, Operand::FtDouble }}, true, true } },
// Float comparisons TODO remaining operations and investigate ordered/unordered and default values
{ InstrId::cpu_c_lt_s, { BinaryOpType::LessFloat, Operand::Cop1cs, {{ UnaryOpType::None, UnaryOpType::None }, { Operand::Fs, Operand::Ft }}, true } },
{ InstrId::cpu_c_nge_s, { BinaryOpType::LessFloat, Operand::Cop1cs, {{ UnaryOpType::None, UnaryOpType::None }, { Operand::Fs, Operand::Ft }}, true } },
{ InstrId::cpu_c_olt_s, { BinaryOpType::LessFloat, Operand::Cop1cs, {{ UnaryOpType::None, UnaryOpType::None }, { Operand::Fs, Operand::Ft }}, true } },
{ InstrId::cpu_c_ult_s, { BinaryOpType::LessFloat, Operand::Cop1cs, {{ UnaryOpType::None, UnaryOpType::None }, { Operand::Fs, Operand::Ft }}, true } },
{ InstrId::cpu_c_lt_d, { BinaryOpType::LessDouble, Operand::Cop1cs, {{ UnaryOpType::None, UnaryOpType::None }, { Operand::FsDouble, Operand::FtDouble }}, true } },
{ InstrId::cpu_c_nge_d, { BinaryOpType::LessDouble, Operand::Cop1cs, {{ UnaryOpType::None, UnaryOpType::None }, { Operand::FsDouble, Operand::FtDouble }}, true } },
{ InstrId::cpu_c_olt_d, { BinaryOpType::LessDouble, Operand::Cop1cs, {{ UnaryOpType::None, UnaryOpType::None }, { Operand::FsDouble, Operand::FtDouble }}, true } },
{ InstrId::cpu_c_ult_d, { BinaryOpType::LessDouble, Operand::Cop1cs, {{ UnaryOpType::None, UnaryOpType::None }, { Operand::FsDouble, Operand::FtDouble }}, true } },
{ InstrId::cpu_c_le_s, { BinaryOpType::LessEqFloat, Operand::Cop1cs, {{ UnaryOpType::None, UnaryOpType::None }, { Operand::Fs, Operand::Ft }}, true } },
{ InstrId::cpu_c_ngt_s, { BinaryOpType::LessEqFloat, Operand::Cop1cs, {{ UnaryOpType::None, UnaryOpType::None }, { Operand::Fs, Operand::Ft }}, true } },
{ InstrId::cpu_c_ole_s, { BinaryOpType::LessEqFloat, Operand::Cop1cs, {{ UnaryOpType::None, UnaryOpType::None }, { Operand::Fs, Operand::Ft }}, true } },
{ InstrId::cpu_c_ule_s, { BinaryOpType::LessEqFloat, Operand::Cop1cs, {{ UnaryOpType::None, UnaryOpType::None }, { Operand::Fs, Operand::Ft }}, true } },
{ InstrId::cpu_c_le_d, { BinaryOpType::LessEqDouble, Operand::Cop1cs, {{ UnaryOpType::None, UnaryOpType::None }, { Operand::FsDouble, Operand::FtDouble }}, true } },
{ InstrId::cpu_c_ngt_d, { BinaryOpType::LessEqDouble, Operand::Cop1cs, {{ UnaryOpType::None, UnaryOpType::None }, { Operand::FsDouble, Operand::FtDouble }}, true } },
{ InstrId::cpu_c_ole_d, { BinaryOpType::LessEqDouble, Operand::Cop1cs, {{ UnaryOpType::None, UnaryOpType::None }, { Operand::FsDouble, Operand::FtDouble }}, true } },
{ InstrId::cpu_c_ule_d, { BinaryOpType::LessEqDouble, Operand::Cop1cs, {{ UnaryOpType::None, UnaryOpType::None }, { Operand::FsDouble, Operand::FtDouble }}, true } },
{ InstrId::cpu_c_eq_s, { BinaryOpType::EqualFloat, Operand::Cop1cs, {{ UnaryOpType::None, UnaryOpType::None }, { Operand::Fs, Operand::Ft }}, true } },
{ InstrId::cpu_c_ueq_s, { BinaryOpType::EqualFloat, Operand::Cop1cs, {{ UnaryOpType::None, UnaryOpType::None }, { Operand::Fs, Operand::Ft }}, true } },
{ InstrId::cpu_c_ngl_s, { BinaryOpType::EqualFloat, Operand::Cop1cs, {{ UnaryOpType::None, UnaryOpType::None }, { Operand::Fs, Operand::Ft }}, true } },
{ InstrId::cpu_c_seq_s, { BinaryOpType::EqualFloat, Operand::Cop1cs, {{ UnaryOpType::None, UnaryOpType::None }, { Operand::Fs, Operand::Ft }}, true } },
// Float comparisons TODO investigate ordered/unordered and default values
// Single Ordered
{ InstrId::cpu_c_f_s, { BinaryOpType::False, Operand::Cop1cs, {{ UnaryOpType::None, UnaryOpType::None }, { Operand::Fs, Operand::Ft }}, true } },
{ InstrId::cpu_c_eq_s, { BinaryOpType::EqualFloat, Operand::Cop1cs, {{ UnaryOpType::None, UnaryOpType::None }, { Operand::Fs, Operand::Ft }}, true } },
{ InstrId::cpu_c_olt_s, { BinaryOpType::LessFloat, Operand::Cop1cs, {{ UnaryOpType::None, UnaryOpType::None }, { Operand::Fs, Operand::Ft }}, true } },
{ InstrId::cpu_c_ole_s, { BinaryOpType::LessEqFloat, Operand::Cop1cs, {{ UnaryOpType::None, UnaryOpType::None }, { Operand::Fs, Operand::Ft }}, true } },
{ InstrId::cpu_c_sf_s, { BinaryOpType::False, Operand::Cop1cs, {{ UnaryOpType::None, UnaryOpType::None }, { Operand::Fs, Operand::Ft }}, true } },
{ InstrId::cpu_c_seq_s, { BinaryOpType::EqualFloat, Operand::Cop1cs, {{ UnaryOpType::None, UnaryOpType::None }, { Operand::Fs, Operand::Ft }}, true } },
{ InstrId::cpu_c_lt_s, { BinaryOpType::LessFloat, Operand::Cop1cs, {{ UnaryOpType::None, UnaryOpType::None }, { Operand::Fs, Operand::Ft }}, true } },
{ InstrId::cpu_c_le_s, { BinaryOpType::LessEqFloat, Operand::Cop1cs, {{ UnaryOpType::None, UnaryOpType::None }, { Operand::Fs, Operand::Ft }}, true } },
// Single Unordered
{ InstrId::cpu_c_un_s, { BinaryOpType::False, Operand::Cop1cs, {{ UnaryOpType::None, UnaryOpType::None }, { Operand::Fs, Operand::Ft }}, true } },
{ InstrId::cpu_c_ueq_s, { BinaryOpType::EqualFloat, Operand::Cop1cs, {{ UnaryOpType::None, UnaryOpType::None }, { Operand::Fs, Operand::Ft }}, true } },
{ InstrId::cpu_c_ult_s, { BinaryOpType::LessFloat, Operand::Cop1cs, {{ UnaryOpType::None, UnaryOpType::None }, { Operand::Fs, Operand::Ft }}, true } },
{ InstrId::cpu_c_ule_s, { BinaryOpType::LessEqFloat, Operand::Cop1cs, {{ UnaryOpType::None, UnaryOpType::None }, { Operand::Fs, Operand::Ft }}, true } },
{ InstrId::cpu_c_ngle_s, { BinaryOpType::False, Operand::Cop1cs, {{ UnaryOpType::None, UnaryOpType::None }, { Operand::Fs, Operand::Ft }}, true } },
{ InstrId::cpu_c_ngl_s, { BinaryOpType::EqualFloat, Operand::Cop1cs, {{ UnaryOpType::None, UnaryOpType::None }, { Operand::Fs, Operand::Ft }}, true } },
{ InstrId::cpu_c_nge_s, { BinaryOpType::LessFloat, Operand::Cop1cs, {{ UnaryOpType::None, UnaryOpType::None }, { Operand::Fs, Operand::Ft }}, true } },
{ InstrId::cpu_c_ngt_s, { BinaryOpType::LessEqFloat, Operand::Cop1cs, {{ UnaryOpType::None, UnaryOpType::None }, { Operand::Fs, Operand::Ft }}, true } },
// Double Ordered
{ InstrId::cpu_c_f_d, { BinaryOpType::False, Operand::Cop1cs, {{ UnaryOpType::None, UnaryOpType::None }, { Operand::FsDouble, Operand::FtDouble }}, true } },
{ InstrId::cpu_c_eq_d, { BinaryOpType::EqualDouble, Operand::Cop1cs, {{ UnaryOpType::None, UnaryOpType::None }, { Operand::FsDouble, Operand::FtDouble }}, true } },
{ InstrId::cpu_c_ueq_d, { BinaryOpType::EqualDouble, Operand::Cop1cs, {{ UnaryOpType::None, UnaryOpType::None }, { Operand::FsDouble, Operand::FtDouble }}, true } },
{ InstrId::cpu_c_ngl_d, { BinaryOpType::EqualDouble, Operand::Cop1cs, {{ UnaryOpType::None, UnaryOpType::None }, { Operand::FsDouble, Operand::FtDouble }}, true } },
{ InstrId::cpu_c_olt_d, { BinaryOpType::LessDouble, Operand::Cop1cs, {{ UnaryOpType::None, UnaryOpType::None }, { Operand::FsDouble, Operand::FtDouble }}, true } },
{ InstrId::cpu_c_ole_d, { BinaryOpType::LessEqDouble, Operand::Cop1cs, {{ UnaryOpType::None, UnaryOpType::None }, { Operand::FsDouble, Operand::FtDouble }}, true } },
/* TODO rename to c_sf_d when fixed in rabbitizer */
{ InstrId::cpu_c_df_d, { BinaryOpType::False, Operand::Cop1cs, {{ UnaryOpType::None, UnaryOpType::None }, { Operand::FsDouble, Operand::FtDouble }}, true } },
/* TODO rename to c_seq_d when fixed in rabbitizer */
{ InstrId::cpu_c_deq_d, { BinaryOpType::EqualDouble, Operand::Cop1cs, {{ UnaryOpType::None, UnaryOpType::None }, { Operand::FsDouble, Operand::FtDouble }}, true } },
{ InstrId::cpu_c_lt_d, { BinaryOpType::LessDouble, Operand::Cop1cs, {{ UnaryOpType::None, UnaryOpType::None }, { Operand::FsDouble, Operand::FtDouble }}, true } },
{ InstrId::cpu_c_le_d, { BinaryOpType::LessEqDouble, Operand::Cop1cs, {{ UnaryOpType::None, UnaryOpType::None }, { Operand::FsDouble, Operand::FtDouble }}, true } },
// Double Unordered
{ InstrId::cpu_c_un_d, { BinaryOpType::False, Operand::Cop1cs, {{ UnaryOpType::None, UnaryOpType::None }, { Operand::FsDouble, Operand::FtDouble }}, true } },
{ InstrId::cpu_c_ueq_d, { BinaryOpType::EqualDouble, Operand::Cop1cs, {{ UnaryOpType::None, UnaryOpType::None }, { Operand::FsDouble, Operand::FtDouble }}, true } },
{ InstrId::cpu_c_ult_d, { BinaryOpType::LessDouble, Operand::Cop1cs, {{ UnaryOpType::None, UnaryOpType::None }, { Operand::FsDouble, Operand::FtDouble }}, true } },
{ InstrId::cpu_c_ule_d, { BinaryOpType::LessEqDouble, Operand::Cop1cs, {{ UnaryOpType::None, UnaryOpType::None }, { Operand::FsDouble, Operand::FtDouble }}, true } },
{ InstrId::cpu_c_ngle_d, { BinaryOpType::False, Operand::Cop1cs, {{ UnaryOpType::None, UnaryOpType::None }, { Operand::FsDouble, Operand::FtDouble }}, true } },
{ InstrId::cpu_c_ngl_d, { BinaryOpType::EqualDouble, Operand::Cop1cs, {{ UnaryOpType::None, UnaryOpType::None }, { Operand::FsDouble, Operand::FtDouble }}, true } },
{ InstrId::cpu_c_nge_d, { BinaryOpType::LessDouble, Operand::Cop1cs, {{ UnaryOpType::None, UnaryOpType::None }, { Operand::FsDouble, Operand::FtDouble }}, true } },
{ InstrId::cpu_c_ngt_d, { BinaryOpType::LessEqDouble, Operand::Cop1cs, {{ UnaryOpType::None, UnaryOpType::None }, { Operand::FsDouble, Operand::FtDouble }}, true } },
// Loads
{ InstrId::cpu_ld, { BinaryOpType::LD, Operand::Rt, {{ UnaryOpType::None, UnaryOpType::None }, { Operand::Base, Operand::ImmS16 }}} },
{ InstrId::cpu_lw, { BinaryOpType::LW, Operand::Rt, {{ UnaryOpType::None, UnaryOpType::None }, { Operand::Base, Operand::ImmS16 }}} },

View File

@@ -152,6 +152,7 @@ bool process_instruction(GeneratorType& generator, const N64Recomp::Context& con
}
N64Recomp::RelocType reloc_type = N64Recomp::RelocType::R_MIPS_NONE;
bool has_reloc = false;
uint32_t reloc_section = 0;
uint32_t reloc_target_section_offset = 0;
size_t reloc_reference_symbol = (size_t)-1;
@@ -162,6 +163,7 @@ bool process_instruction(GeneratorType& generator, const N64Recomp::Context& con
// Check if this instruction has a reloc.
if (section.relocs.size() > 0 && section.relocs[reloc_index].address == instr_vram) {
has_reloc = true;
// Get the reloc data for this instruction
const auto& reloc = section.relocs[reloc_index];
reloc_section = reloc.target_section;
@@ -257,7 +259,7 @@ bool process_instruction(GeneratorType& generator, const N64Recomp::Context& con
return true;
};
auto print_func_call_by_address = [&generator, reloc_target_section_offset, reloc_section, reloc_reference_symbol, reloc_type, &context, &func, &static_funcs_out, &needs_link_branch, &print_indent, &process_delay_slot, &print_link_branch]
auto print_func_call_by_address = [&generator, reloc_target_section_offset, has_reloc, reloc_section, reloc_reference_symbol, reloc_type, &context, &func, &static_funcs_out, &needs_link_branch, &print_indent, &process_delay_slot, &print_link_branch]
(uint32_t target_func_vram, bool tail_call = false, bool indent = false)
{
bool call_by_lookup = false;
@@ -294,7 +296,11 @@ bool process_instruction(GeneratorType& generator, const N64Recomp::Context& con
}
}
else {
JalResolutionResult jal_result = resolve_jal(context, func.section_index, target_func_vram, matched_func_index);
uint32_t target_section = func.section_index;
if (has_reloc) {
target_section = reloc_section;
}
JalResolutionResult jal_result = resolve_jal(context, target_section, target_func_vram, matched_func_index);
switch (jal_result) {
case JalResolutionResult::NoMatch: