diff --git a/CMakeLists.txt b/CMakeLists.txt index 9ed6c81..2733666 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -159,14 +159,8 @@ project(OfflineModRecomp) add_executable(OfflineModRecomp) target_sources(OfflineModRecomp PRIVATE - ${CMAKE_CURRENT_SOURCE_DIR}/../sljit/sljit_src/sljitLir.c ${CMAKE_CURRENT_SOURCE_DIR}/src/config.cpp ${CMAKE_CURRENT_SOURCE_DIR}/OfflineModRecomp/main.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/OfflineModRecomp/sljit_generator.cpp -) - -target_include_directories(OfflineModRecomp PRIVATE - ${CMAKE_CURRENT_SOURCE_DIR}/../sljit/sljit_src ) target_link_libraries(OfflineModRecomp fmt rabbitizer tomlplusplus::tomlplusplus N64Recomp) diff --git a/OfflineModRecomp/main.cpp b/OfflineModRecomp/main.cpp index aa25dc8..02ef166 100644 --- a/OfflineModRecomp/main.cpp +++ b/OfflineModRecomp/main.cpp @@ -3,7 +3,7 @@ #include #include -#include "n64recomp.h" +#include "recompiler/context.h" #include "rabbitizer.hpp" static std::vector read_file(const std::filesystem::path& path, bool& found) { diff --git a/RecompModTool/main.cpp b/RecompModTool/main.cpp index 78649ef..9fbb7d1 100644 --- a/RecompModTool/main.cpp +++ b/RecompModTool/main.cpp @@ -7,7 +7,7 @@ #include #include "fmt/format.h" #include "fmt/ostream.h" -#include "n64recomp.h" +#include "recompiler/context.h" #include #ifdef _WIN32 diff --git a/include/recomp.h b/include/recomp.h new file mode 100644 index 0000000..39b0d8f --- /dev/null +++ b/include/recomp.h @@ -0,0 +1,293 @@ +#ifndef __RECOMP_H__ +#define __RECOMP_H__ + +#include +#include +#include +#include + +// Compiler definition to disable inter-procedural optimization, allowing multiple functions to be in a single file without breaking interposition. +#if defined(_MSC_VER) && !defined(__clang__) + // MSVC's __declspec(noinline) seems to disable inter-procedural optimization entirely, so it's all that's needed. + #define RECOMP_FUNC __declspec(noinline) +#elif defined(__clang__) + // Clang has no dedicated IPO attribute, so we use a combination of other attributes to give the desired behavior. + // The inline keyword allows multiple definitions during linking, and extern forces clang to emit an externally visible definition. + // Weak forces Clang to not perform any IPO as the symbol can be interposed, which prevents actual inlining due to the inline keyword. + // Add noinline on for good measure, which doesn't conflict with the inline keyword as they have different meanings. + #define RECOMP_FUNC extern inline __attribute__((weak,noinline)) +#elif defined(__GNUC__) + // Use GCC's attribute for disabling inter-procedural optimizations. + #define RECOMP_FUNC __attribute__((noipa)) +#else + #error "No RECOMP_FUNC definition for this compiler" +#endif + +typedef uint64_t gpr; + +#define SIGNED(val) \ + ((int64_t)(val)) + +#define ADD32(a, b) \ + ((gpr)(int32_t)((a) + (b))) + +#define SUB32(a, b) \ + ((gpr)(int32_t)((a) - (b))) + +#define MEM_W(offset, reg) \ + (*(int32_t*)(rdram + ((((reg) + (offset))) - 0xFFFFFFFF80000000))) + +#define MEM_H(offset, reg) \ + (*(int16_t*)(rdram + ((((reg) + (offset)) ^ 2) - 0xFFFFFFFF80000000))) + +#define MEM_B(offset, reg) \ + (*(int8_t*)(rdram + ((((reg) + (offset)) ^ 3) - 0xFFFFFFFF80000000))) + +#define MEM_HU(offset, reg) \ + (*(uint16_t*)(rdram + ((((reg) + (offset)) ^ 2) - 0xFFFFFFFF80000000))) + +#define MEM_BU(offset, reg) \ + (*(uint8_t*)(rdram + ((((reg) + (offset)) ^ 3) - 0xFFFFFFFF80000000))) + +#define SD(val, offset, reg) { \ + *(uint32_t*)(rdram + ((((reg) + (offset) + 4)) - 0xFFFFFFFF80000000)) = (uint32_t)((gpr)(val) >> 0); \ + *(uint32_t*)(rdram + ((((reg) + (offset) + 0)) - 0xFFFFFFFF80000000)) = (uint32_t)((gpr)(val) >> 32); \ +} + +static inline uint64_t load_doubleword(uint8_t* rdram, gpr reg, gpr offset) { + uint64_t ret = 0; + uint64_t lo = (uint64_t)(uint32_t)MEM_W(reg, offset + 4); + uint64_t hi = (uint64_t)(uint32_t)MEM_W(reg, offset + 0); + ret = (lo << 0) | (hi << 32); + return ret; +} + +#define LD(offset, reg) \ + load_doubleword(rdram, offset, reg) + +static inline gpr do_lwl(uint8_t* rdram, gpr initial_value, gpr offset, gpr reg) { + // Calculate the overall address + gpr address = (offset + reg); + + // Load the aligned word + gpr word_address = address & ~0x3; + uint32_t loaded_value = MEM_W(0, word_address); + + // Mask the existing value and shift the loaded value appropriately + gpr misalignment = address & 0x3; + gpr masked_value = initial_value & ~(0xFFFFFFFFu << (misalignment * 8)); + loaded_value <<= (misalignment * 8); + + // Cast to int32_t to sign extend first + return (gpr)(int32_t)(masked_value | loaded_value); +} + +static inline gpr do_lwr(uint8_t* rdram, gpr initial_value, gpr offset, gpr reg) { + // Calculate the overall address + gpr address = (offset + reg); + + // Load the aligned word + gpr word_address = address & ~0x3; + uint32_t loaded_value = MEM_W(0, word_address); + + // Mask the existing value and shift the loaded value appropriately + gpr misalignment = address & 0x3; + gpr masked_value = initial_value & ~(0xFFFFFFFFu >> (24 - misalignment * 8)); + loaded_value >>= (24 - misalignment * 8); + + // Cast to int32_t to sign extend first + return (gpr)(int32_t)(masked_value | loaded_value); +} + +static inline void do_swl(uint8_t* rdram, gpr offset, gpr reg, gpr val) { + // Calculate the overall address + gpr address = (offset + reg); + + // Get the initial value of the aligned word + gpr word_address = address & ~0x3; + uint32_t initial_value = MEM_W(0, word_address); + + // Mask the initial value and shift the input value appropriately + gpr misalignment = address & 0x3; + uint32_t masked_initial_value = initial_value & ~(0xFFFFFFFFu >> (misalignment * 8)); + uint32_t shifted_input_value = ((uint32_t)val) >> (misalignment * 8); + MEM_W(0, word_address) = masked_initial_value | shifted_input_value; +} + +static inline void do_swr(uint8_t* rdram, gpr offset, gpr reg, gpr val) { + // Calculate the overall address + gpr address = (offset + reg); + + // Get the initial value of the aligned word + gpr word_address = address & ~0x3; + uint32_t initial_value = MEM_W(0, word_address); + + // Mask the initial value and shift the input value appropriately + gpr misalignment = address & 0x3; + uint32_t masked_initial_value = initial_value & ~(0xFFFFFFFFu << (24 - misalignment * 8)); + uint32_t shifted_input_value = ((uint32_t)val) << (24 - misalignment * 8); + MEM_W(0, word_address) = masked_initial_value | shifted_input_value; +} + +#define S32(val) \ + ((int32_t)(val)) + +#define U32(val) \ + ((uint32_t)(val)) + +#define S64(val) \ + ((int64_t)(val)) + +#define U64(val) \ + ((uint64_t)(val)) + +#define MUL_S(val1, val2) \ + ((val1) * (val2)) + +#define MUL_D(val1, val2) \ + ((val1) * (val2)) + +#define DIV_S(val1, val2) \ + ((val1) / (val2)) + +#define DIV_D(val1, val2) \ + ((val1) / (val2)) + +#define CVT_S_W(val) \ + ((float)((int32_t)(val))) + +#define CVT_D_W(val) \ + ((double)((int32_t)(val))) + +#define CVT_D_S(val) \ + ((double)(val)) + +#define CVT_S_D(val) \ + ((float)(val)) + +#define TRUNC_W_S(val) \ + ((int32_t)(val)) + +#define TRUNC_W_D(val) \ + ((int32_t)(val)) + +#define TRUNC_L_S(val) \ + ((int64_t)(val)) + +#define TRUNC_L_D(val) \ + ((int64_t)(val)) + +#define DEFAULT_ROUNDING_MODE 0 + +static inline int32_t do_cvt_w_s(float val, unsigned int rounding_mode) { + switch (rounding_mode) { + case 0: // round to nearest value + return (int32_t)lroundf(val); + case 1: // round to zero (truncate) + return (int32_t)val; + case 2: // round to positive infinity (ceil) + return (int32_t)ceilf(val); + case 3: // round to negative infinity (floor) + return (int32_t)floorf(val); + } + assert(0); + return 0; +} + +#define CVT_W_S(val) \ + do_cvt_w_s(val, rounding_mode) + +static inline int32_t do_cvt_w_d(double val, unsigned int rounding_mode) { + switch (rounding_mode) { + case 0: // round to nearest value + return (int32_t)lround(val); + case 1: // round to zero (truncate) + return (int32_t)val; + case 2: // round to positive infinity (ceil) + return (int32_t)ceil(val); + case 3: // round to negative infinity (floor) + return (int32_t)floor(val); + } + assert(0); + return 0; +} + +#define CVT_W_D(val) \ + do_cvt_w_d(val, rounding_mode) + +#define NAN_CHECK(val) \ + assert(val == val) + +//#define NAN_CHECK(val) + +typedef union { + double d; + struct { + float fl; + float fh; + }; + struct { + uint32_t u32l; + uint32_t u32h; + }; + uint64_t u64; +} fpr; + +typedef struct { + gpr r0, r1, r2, r3, r4, r5, r6, r7, + r8, r9, r10, r11, r12, r13, r14, r15, + r16, r17, r18, r19, r20, r21, r22, r23, + r24, r25, r26, r27, r28, r29, r30, r31; + fpr f0, f1, f2, f3, f4, f5, f6, f7, + f8, f9, f10, f11, f12, f13, f14, f15, + f16, f17, f18, f19, f20, f21, f22, f23, + f24, f25, f26, f27, f28, f29, f30, f31; + uint64_t hi, lo; + uint32_t* f_odd; + uint32_t status_reg; + uint8_t mips3_float_mode; +} recomp_context; + +// Checks if the target is an even float register or that mips3 float mode is enabled +#define CHECK_FR(ctx, idx) \ + assert(((idx) & 1) == 0 || (ctx)->mips3_float_mode) + +#ifdef __cplusplus +extern "C" { +#endif + +void cop0_status_write(recomp_context* ctx, gpr value); +gpr cop0_status_read(recomp_context* ctx); +void switch_error(const char* func, uint32_t vram, uint32_t jtbl); +void do_break(uint32_t vram); + +typedef void (recomp_func_t)(uint8_t* rdram, recomp_context* ctx); + +recomp_func_t* get_function(int32_t vram); + +#define LOOKUP_FUNC(val) \ + get_function((int32_t)(val)) + +extern int32_t* section_addresses; + +#define LO16(x) \ + ((x) & 0xFFFF) + +#define HI16(x) \ + (((x) >> 16) + (((x) >> 15) & 1)) + +#define RELOC_HI16(section_index, offset) \ + HI16(section_addresses[section_index] + (offset)) + +#define RELOC_LO16(section_index, offset) \ + LO16(section_addresses[section_index] + (offset)) + +void recomp_syscall_handler(uint8_t* rdram, recomp_context* ctx, int32_t instruction_vram); + +void pause_self(uint8_t *rdram); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/include/n64recomp.h b/include/recompiler/context.h similarity index 100% rename from include/n64recomp.h rename to include/recompiler/context.h diff --git a/include/generator.h b/include/recompiler/generator.h similarity index 99% rename from include/generator.h rename to include/recompiler/generator.h index b34d2a9..b084e16 100644 --- a/include/generator.h +++ b/include/recompiler/generator.h @@ -1,7 +1,7 @@ #ifndef __GENERATOR_H__ #define __GENERATOR_H__ -#include "n64recomp.h" +#include "recompiler/context.h" #include "operations.h" namespace N64Recomp { diff --git a/include/operations.h b/include/recompiler/operations.h similarity index 100% rename from include/operations.h rename to include/recompiler/operations.h diff --git a/src/analysis.cpp b/src/analysis.cpp index db8c7a2..c790142 100644 --- a/src/analysis.cpp +++ b/src/analysis.cpp @@ -4,7 +4,7 @@ #include "rabbitizer.hpp" #include "fmt/format.h" -#include "n64recomp.h" +#include "recompiler/context.h" #include "analysis.h" extern "C" const char* RabbitizerRegister_getNameGpr(uint8_t regValue); diff --git a/src/analysis.h b/src/analysis.h index 19eb55a..44a8f4c 100644 --- a/src/analysis.h +++ b/src/analysis.h @@ -4,7 +4,7 @@ #include #include -#include "n64recomp.h" +#include "recompiler/context.h" namespace N64Recomp { struct JumpTable { diff --git a/src/cgenerator.cpp b/src/cgenerator.cpp index e1c2f15..de7d56a 100644 --- a/src/cgenerator.cpp +++ b/src/cgenerator.cpp @@ -4,7 +4,7 @@ #include "fmt/format.h" #include "fmt/ostream.h" -#include "generator.h" +#include "recompiler/generator.h" struct BinaryOpFields { std::string func_string; std::string infix_string; }; diff --git a/src/config.cpp b/src/config.cpp index d3b236f..d4c00f1 100644 --- a/src/config.cpp +++ b/src/config.cpp @@ -3,7 +3,7 @@ #include #include "fmt/format.h" #include "config.h" -#include "n64recomp.h" +#include "recompiler/context.h" std::filesystem::path concat_if_not_empty(const std::filesystem::path& parent, const std::filesystem::path& child) { if (!child.empty()) { diff --git a/src/elf.cpp b/src/elf.cpp index a18fdbd..d83908c 100644 --- a/src/elf.cpp +++ b/src/elf.cpp @@ -3,7 +3,7 @@ #include "fmt/format.h" // #include "fmt/ostream.h" -#include "n64recomp.h" +#include "recompiler/context.h" #include "elfio/elfio.hpp" bool read_symbols(N64Recomp::Context& context, const ELFIO::elfio& elf_file, ELFIO::section* symtab_section, const N64Recomp::ElfParsingConfig& elf_config, bool dumping_context, std::unordered_map>& data_syms) { diff --git a/src/main.cpp b/src/main.cpp index a2ccdc1..4e5b27b 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -9,7 +9,7 @@ #include "fmt/format.h" #include "fmt/ostream.h" -#include "n64recomp.h" +#include "recompiler/context.h" #include "config.h" #include diff --git a/src/mod_symbols.cpp b/src/mod_symbols.cpp index 24675fe..fcfdead 100644 --- a/src/mod_symbols.cpp +++ b/src/mod_symbols.cpp @@ -1,6 +1,6 @@ #include -#include "n64recomp.h" +#include "recompiler/context.h" struct FileHeader { char magic[8]; // N64RSYMS diff --git a/src/operations.cpp b/src/operations.cpp index 40526c2..02a2bc4 100644 --- a/src/operations.cpp +++ b/src/operations.cpp @@ -1,4 +1,4 @@ -#include "operations.h" +#include "recompiler/operations.h" namespace N64Recomp { const std::unordered_map unary_ops { diff --git a/src/recompilation.cpp b/src/recompilation.cpp index 7413627..114d380 100644 --- a/src/recompilation.cpp +++ b/src/recompilation.cpp @@ -8,10 +8,10 @@ #include "fmt/format.h" #include "fmt/ostream.h" -#include "n64recomp.h" +#include "recompiler/context.h" #include "analysis.h" -#include "operations.h" -#include "generator.h" +#include "recompiler/operations.h" +#include "recompiler/generator.h" enum class JalResolutionResult { NoMatch, diff --git a/src/symbol_lists.cpp b/src/symbol_lists.cpp index 4b4eff9..cbe5ff5 100644 --- a/src/symbol_lists.cpp +++ b/src/symbol_lists.cpp @@ -1,4 +1,4 @@ -#include "n64recomp.h" +#include "recompiler/context.h" const std::unordered_set N64Recomp::reimplemented_funcs { // OS initialize functions