diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 00000000..3108455f --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,83 @@ +cmake_minimum_required(VERSION 3.20) +project(kaizen) + +set(BUILD_SHARED_LIBS OFF) +set(CMAKE_CXX_STANDARD 17) + +find_package(SDL2 REQUIRED) +find_package(fmt REQUIRED) +find_package(mio REQUIRED) +find_package(nlohmann_json REQUIRED) + +option(RAPIDJSON_BUILD_DOC "Build rapidjson documentation." OFF) +option(RAPIDJSON_BUILD_EXAMPLES "Build rapidjson examples." OFF) +option(RAPIDJSON_BUILD_TESTS "Build rapidjson perftests and unittests." OFF) + +include_directories( + src + src/utils + src/frontend + src/frontend/imgui + src/backend + src/backend/core + src/backend/core/mmio + src/backend/core/registers + src/backend/core/rsp + external + external/xbyak + external/parallel-rdp + external/parallel-rdp/parallel-rdp-standalone/parallel-rdp + external/parallel-rdp/parallel-rdp-standalone/volk + external/parallel-rdp/parallel-rdp-standalone/spirv-cross + external/parallel-rdp/parallel-rdp-standalone/vulkan + external/parallel-rdp/parallel-rdp-standalone/vulkan-headers/include + external/parallel-rdp/parallel-rdp-standalone/util + external/nativefiledialog-extended/src/include + external/imgui/imgui + external/imgui/imgui/backends + external/discord-rpc/include + external/unarr + ${SDL2_INCLUDE_DIRS} +) + +add_compile_definitions(SIMD_SUPPORT) +if(WIN32) + add_definitions(-DNOMINMAX) + add_definitions(-D_CRT_SECURE_NO_WARNINGS) +endif () +add_compile_options(-mssse3 -msse4.1) +if(${CMAKE_BUILD_TYPE} MATCHES Debug) + #add_compile_options(-fsanitize=address -fsanitize=undefined) + #add_link_options(-fsanitize=address -fsanitize=undefined) +endif() + +add_subdirectory(src/frontend) +add_subdirectory(src/frontend/imgui) +add_subdirectory(src/backend) +add_subdirectory(src/backend/netplay) +add_subdirectory(src/backend/core) +add_subdirectory(src/backend/core/interpreter) +add_subdirectory(src/backend/core/mem) +add_subdirectory(src/backend/core/mmio) +add_subdirectory(src/backend/core/registers) +add_subdirectory(src/backend/core/rsp) +add_subdirectory(external/discord-rpc) +add_subdirectory(external/imgui) +add_subdirectory(external/nativefiledialog-extended) +add_subdirectory(external/parallel-rdp) +add_subdirectory(external/unarr) + +add_executable(kaizen main.cpp) + +if(MSVC) + target_compile_options(parallel-rdp PUBLIC /EHa) +endif() + +file(COPY ${PROJECT_SOURCE_DIR}/resources/ DESTINATION ${PROJECT_BINARY_DIR}/resources/) +file(REMOVE + ${PROJECT_BINARY_DIR}/resources/mario.png + ${PROJECT_BINARY_DIR}/resources/shader.frag + ${PROJECT_BINARY_DIR}/resources/shader.vert) + +target_link_libraries(kaizen PUBLIC frontend frontend-imgui + discord-rpc imgui nfd parallel-rdp backend fmt::fmt mio::mio nlohmann_json::nlohmann_json core registers interpreter mem unarr mmio rsp SDL2::SDL2main SDL2::SDL2) diff --git a/src/main.cpp b/main.cpp similarity index 100% rename from src/main.cpp rename to main.cpp diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt deleted file mode 100644 index 5c692fbb..00000000 --- a/src/CMakeLists.txt +++ /dev/null @@ -1,83 +0,0 @@ -cmake_minimum_required(VERSION 3.20) -project(kaizen) - -set(BUILD_SHARED_LIBS OFF) -set(CMAKE_CXX_STANDARD 17) - -find_package(SDL2 REQUIRED) -find_package(fmt REQUIRED) -find_package(mio REQUIRED) -find_package(nlohmann_json REQUIRED) - -option(RAPIDJSON_BUILD_DOC "Build rapidjson documentation." OFF) -option(RAPIDJSON_BUILD_EXAMPLES "Build rapidjson examples." OFF) -option(RAPIDJSON_BUILD_TESTS "Build rapidjson perftests and unittests." OFF) - -include_directories( - . - utils - frontend - frontend/imgui - backend - backend/core - backend/core/mmio - backend/core/registers - backend/core/rsp - ../external - ../external/xbyak - ../external/parallel-rdp - ../external/parallel-rdp/parallel-rdp-standalone/parallel-rdp - ../external/parallel-rdp/parallel-rdp-standalone/volk - ../external/parallel-rdp/parallel-rdp-standalone/spirv-cross - ../external/parallel-rdp/parallel-rdp-standalone/vulkan - ../external/parallel-rdp/parallel-rdp-standalone/vulkan-headers/include - ../external/parallel-rdp/parallel-rdp-standalone/util - ../external/nativefiledialog-extended/src/include - ../external/imgui/imgui - ../external/imgui/imgui/backends - ../external/discord-rpc/include - ../external/unarr - ${SDL2_INCLUDE_DIRS} -) - -add_compile_definitions(SIMD_SUPPORT) -if(WIN32) - add_definitions(-DNOMINMAX) - add_definitions(-D_CRT_SECURE_NO_WARNINGS) -endif () -add_compile_options(-mssse3 -msse4.1) -if(${CMAKE_BUILD_TYPE} MATCHES Debug) - #add_compile_options(-fsanitize=address -fsanitize=undefined) - #add_link_options(-fsanitize=address -fsanitize=undefined) -endif() - -add_subdirectory(frontend) -add_subdirectory(frontend/imgui) -add_subdirectory(backend) -add_subdirectory(backend/netplay) -add_subdirectory(backend/core) -add_subdirectory(backend/core/interpreter) -add_subdirectory(backend/core/mem) -add_subdirectory(backend/core/mmio) -add_subdirectory(backend/core/registers) -add_subdirectory(backend/core/rsp) -add_subdirectory(../external/discord-rpc discord-rpc) -add_subdirectory(../external/imgui imgui) -add_subdirectory(../external/nativefiledialog-extended nfd) -add_subdirectory(../external/parallel-rdp parallel-rdp) -add_subdirectory(../external/unarr unarr) - -add_executable(kaizen main.cpp) - -if(MSVC) - target_compile_options(parallel-rdp PUBLIC /EHa) -endif() - -file(COPY ${PROJECT_SOURCE_DIR}/../resources/ DESTINATION ${PROJECT_BINARY_DIR}/resources/) -file(REMOVE - ${PROJECT_BINARY_DIR}/resources/mario.png - ${PROJECT_BINARY_DIR}/resources/shader.frag - ${PROJECT_BINARY_DIR}/resources/shader.vert) - -target_link_libraries(kaizen PUBLIC frontend frontend-imgui - discord-rpc imgui nfd parallel-rdp backend fmt::fmt mio::mio nlohmann_json::nlohmann_json core registers interpreter mem unarr mmio rsp SDL2::SDL2main SDL2::SDL2) diff --git a/src/backend/core/JIT.hpp b/src/backend/core/JIT.hpp index aa3bb935..8a4a289d 100644 --- a/src/backend/core/JIT.hpp +++ b/src/backend/core/JIT.hpp @@ -21,6 +21,7 @@ private: void cop2Decode(u32); void special(u32); void regimm(u32); + void Emit(u32); void add(u32); void addu(u32); void addi(u32); @@ -114,4 +115,4 @@ private: void ctc2(u32); void cfc2(u32); }; -} \ No newline at end of file +} diff --git a/src/backend/core/JIT/decode.cpp b/src/backend/core/JIT/decode.cpp new file mode 100644 index 00000000..c6a67ee3 --- /dev/null +++ b/src/backend/core/JIT/decode.cpp @@ -0,0 +1,175 @@ +#include +#include + +namespace n64 { +void JIT::special(u32 instr) { + u8 mask = (instr & 0x3F); + // 00rr_rccc + switch (mask) { // TODO: named constants for clearer code + case 0: + if (instr != 0) { + sll(instr); + } + break; + case 0x02: srl(instr); break; + case 0x03: sra(instr); break; + case 0x04: sllv(instr); break; + case 0x06: srlv(instr); break; + case 0x07: srav(instr); break; + case 0x08: jr(instr); break; + case 0x09: jalr(instr); break; + case 0x0C: FireException(regs, ExceptionCode::Syscall, 0, true); break; + case 0x0D: FireException(regs, ExceptionCode::Breakpoint, 0, true); break; + case 0x0F: break; // SYNC + case 0x10: mfhi(instr); break; + case 0x11: mthi(instr); break; + case 0x12: mflo(instr); break; + case 0x13: mtlo(instr); break; + case 0x14: dsllv(instr); break; + case 0x16: dsrlv(instr); break; + case 0x17: dsrav(instr); break; + case 0x18: mult(instr); break; + case 0x19: multu(instr); break; + case 0x1A: div(instr); break; + case 0x1B: divu(instr); break; + case 0x1C: dmult(instr); break; + case 0x1D: dmultu(instr); break; + case 0x1E: ddiv(instr); break; + case 0x1F: ddivu(instr); break; + case 0x20: add(instr); break; + case 0x21: addu(instr); break; + case 0x22: sub(instr); break; + case 0x23: subu(instr); break; + case 0x24: and_(instr); break; + case 0x25: or_(instr); break; + case 0x26: xor_(instr); break; + case 0x27: nor(instr); break; + case 0x2A: slt(instr); break; + case 0x2B: sltu(instr); break; + case 0x2C: dadd(instr); break; + case 0x2D: daddu(instr); break; + case 0x2E: dsub(instr); break; + case 0x2F: dsubu(instr); break; + case 0x30: trap(regs.gpr[RS(instr)] >= regs.gpr[RT(instr)]); break; + case 0x31: trap((u64)regs.gpr[RS(instr)] >= (u64)regs.gpr[RT(instr)]); break; + case 0x32: trap(regs.gpr[RS(instr)] < regs.gpr[RT(instr)]); break; + case 0x33: trap((u64)regs.gpr[RS(instr)] < (u64)regs.gpr[RT(instr)]); break; + case 0x34: trap(regs.gpr[RS(instr)] == regs.gpr[RT(instr)]); break; + case 0x36: trap(regs.gpr[RS(instr)] != regs.gpr[RT(instr)]); break; + case 0x38: dsll(instr); break; + case 0x3A: dsrl(instr); break; + case 0x3B: dsra(instr); break; + case 0x3C: dsll32(instr); break; + case 0x3E: dsrl32(instr); break; + case 0x3F: dsra32(instr); break; + default: + Util::panic("Unimplemented special {} {} ({:08X}) (pc: {:016X})", (mask >> 3) & 7, mask & 7, instr, (u64)regs.oldPC); + } +} + +void JIT::regimm(u32 instr) { + u8 mask = ((instr >> 16) & 0x1F); + // 000r_rccc + switch (mask) { // TODO: named constants for clearer code + case 0x00: b(instr, regs.gpr[RS(instr)] < 0); break; + case 0x01: b(instr, regs.gpr[RS(instr)] >= 0); break; + case 0x02: bl(instr, regs.gpr[RS(instr)] < 0); break; + case 0x03: bl(instr, regs.gpr[RS(instr)] >= 0); break; + case 0x08: trap(regs.gpr[RS(instr)] >= s64(s16(instr))); break; + case 0x09: trap(u64(regs.gpr[RS(instr)]) >= u64(s64(s16(instr)))); break; + case 0x0A: trap(regs.gpr[RS(instr)] < s64(s16(instr))); break; + case 0x0B: trap(u64(regs.gpr[RS(instr)]) < u64(s64(s16(instr)))); break; + case 0x0C: trap(regs.gpr[RS(instr)] == s64(s16(instr))); break; + case 0x0E: trap(regs.gpr[RS(instr)] != s64(s16(instr))); break; + case 0x10: blink(instr, regs.gpr[RS(instr)] < 0); break; + case 0x11: blink(instr, regs.gpr[RS(instr)] >= 0); break; + case 0x12: bllink(instr, regs.gpr[RS(instr)] < 0); break; + case 0x13: bllink(instr, regs.gpr[RS(instr)] >= 0); break; + default: + Util::panic("Unimplemented regimm {} {} ({:08X}) (pc: {:016X})", (mask >> 3) & 3, mask & 7, instr, (u64)regs.oldPC); + } +} + +void JIT::cop2Decode(u32 instr) { + if(!regs.cop0.status.cu2) { + FireException(regs, ExceptionCode::CoprocessorUnusable, 2, true); + return; + } + switch(RS(instr)) { + case 0x00: mfc2(instr); break; + case 0x01: dmfc2(instr); break; + case 0x02: cfc2(instr); break; + case 0x04: mtc2(instr); break; + case 0x05: dmtc2(instr); break; + case 0x06: ctc2(instr); break; + default: + FireException(regs, ExceptionCode::ReservedInstruction, 2, true); + } +} + +void JIT::Emit(u32 instr) { + u8 mask = (instr >> 26) & 0x3f; + // 00rr_rccc + switch(mask) { // TODO: named constants for clearer code + case 0x00: special(instr); break; + case 0x01: regimm(instr); break; + case 0x02: j(instr); break; + case 0x03: jal(instr); break; + case 0x04: b(instr, regs.gpr[RS(instr)] == regs.gpr[RT(instr)]); break; + case 0x05: { + //fmt::print("RS: {:016X}, RT: {:016X}", (u64)regs.gpr[RS(instr)], (u64)regs.gpr[RT(instr)]); + b(instr, regs.gpr[RS(instr)] != regs.gpr[RT(instr)]); + } break; + case 0x06: b(instr, regs.gpr[RS(instr)] <= 0); break; + case 0x07: b(instr, regs.gpr[RS(instr)] > 0); break; + case 0x08: addi(instr); break; + case 0x09: addiu(instr); break; + case 0x0A: slti(instr); break; + case 0x0B: sltiu(instr); break; + case 0x0C: andi(instr); break; + case 0x0D: ori(instr); break; + case 0x0E: xori(instr); break; + case 0x0F: lui(instr); break; + case 0x10: regs.cop0.decode(regs, instr); break; + case 0x11: regs.cop1.decode(regs, *this, instr); break; + case 0x12: cop2Decode(instr); break; + case 0x14: bl(instr, regs.gpr[RS(instr)] == regs.gpr[RT(instr)]); break; + case 0x15: bl(instr, regs.gpr[RS(instr)] != regs.gpr[RT(instr)]); break; + case 0x16: bl(instr, regs.gpr[RS(instr)] <= 0); break; + case 0x17: bl(instr, regs.gpr[RS(instr)] > 0); break; + case 0x18: daddi(instr); break; + case 0x19: daddiu(instr); break; + case 0x1A: ldl(instr); break; + case 0x1B: ldr(instr); break; + case 0x1F: FireException(regs, ExceptionCode::ReservedInstruction, 0, true); break; + case 0x20: lb(instr); break; + case 0x21: lh(instr); break; + case 0x22: lwl(instr); break; + case 0x23: lw(instr); break; + case 0x24: lbu(instr); break; + case 0x25: lhu(instr); break; + case 0x26: lwr(instr); break; + case 0x27: lwu(instr); break; + case 0x28: sb(instr); break; + case 0x29: sh(instr); break; + case 0x2A: swl(instr); break; + case 0x2B: sw(instr); break; + case 0x2C: sdl(instr); break; + case 0x2D: sdr(instr); break; + case 0x2E: swr(instr); break; + case 0x2F: break; // CACHE + case 0x30: ll(instr); break; + case 0x31: regs.cop1.lwc1(regs, mem, instr); break; + case 0x34: lld(instr); break; + case 0x35: regs.cop1.ldc1(regs, mem, instr); break; + case 0x37: ld(instr); break; + case 0x38: sc(instr); break; + case 0x39: regs.cop1.swc1(regs, mem, instr); break; + case 0x3C: scd(instr); break; + case 0x3D: regs.cop1.sdc1(regs, mem, instr); break; + case 0x3F: sd(instr); break; + default: + Util::panic("Unimplemented instruction {:02X} ({:08X}) (pc: {:016X})", mask, instr, (u64)regs.oldPC); + } +} +} diff --git a/src/backend/core/JIT/instructions.cpp b/src/backend/core/JIT/instructions.cpp new file mode 100644 index 00000000..872da53a --- /dev/null +++ b/src/backend/core/JIT/instructions.cpp @@ -0,0 +1,933 @@ +#include + +#define check_address_error(mask, vaddr) (((!regs.cop0.is_64bit_addressing) && (s32)(vaddr) != (vaddr)) || (((vaddr) & (mask)) != 0)) +#define check_signed_overflow(op1, op2, res) (((~((op1) ^ (op2)) & ((op1) ^ (res))) >> ((sizeof(res) * 8) - 1)) & 1) +#define check_signed_underflow(op1, op2, res) (((((op1) ^ (op2)) & ((op1) ^ (res))) >> ((sizeof(res) * 8) - 1)) & 1) + +namespace n64 { +void Interpreter::add(u32 instr) { + u32 rs = (s32)regs.gpr[RS(instr)]; + u32 rt = (s32)regs.gpr[RT(instr)]; + u32 result = rs + rt; + if(check_signed_overflow(rs, rt, result)) { + FireException(regs, ExceptionCode::Overflow, 0, true); + } else { + if(likely(RD(instr) != 0)) { + regs.gpr[RD(instr)] = s32(result); + } + } +} + +void Interpreter::addu(u32 instr) { + if(likely(RD(instr) != 0)) { + s32 rs = (s32)regs.gpr[RS(instr)]; + s32 rt = (s32)regs.gpr[RT(instr)]; + s32 result = rs + rt; + regs.gpr[RD(instr)] = result; + } +} + +void Interpreter::addi(u32 instr) { + u32 rs = regs.gpr[RS(instr)]; + u32 imm = s32(s16(instr)); + u32 result = rs + imm; + if(check_signed_overflow(rs, imm, result)) { + FireException(regs, ExceptionCode::Overflow, 0, true); + } else { + regs.gpr[RT(instr)] = s32(result); + } +} + +void Interpreter::addiu(u32 instr) { + s32 rs = (s32)regs.gpr[RS(instr)]; + s16 imm = (s16)(instr); + s32 result = rs + imm; + regs.gpr[RT(instr)] = result; +} + +void Interpreter::dadd(u32 instr) { + u64 rs = regs.gpr[RS(instr)]; + u64 rt = regs.gpr[RT(instr)]; + u64 result = rt + rs; + if(check_signed_overflow(rs, rt, result)) { + FireException(regs, ExceptionCode::Overflow, 0, true); + } else { + if(likely(RD(instr) != 0)) { + regs.gpr[RD(instr)] = result; + } + } +} + +void Interpreter::daddu(u32 instr) { + if(likely(RD(instr) != 0)) { + s64 rs = regs.gpr[RS(instr)]; + s64 rt = regs.gpr[RT(instr)]; + regs.gpr[RD(instr)] = rs + rt; + } +} + +void Interpreter::daddi(u32 instr) { + u64 imm = s64(s16(instr)); + u64 rs = regs.gpr[RS(instr)]; + u64 result = imm + rs; + if(check_signed_overflow(rs, imm, result)) { + FireException(regs, ExceptionCode::Overflow, 0, true); + } else { + regs.gpr[RT(instr)] = result; + } +} + +void Interpreter::daddiu(u32 instr) { + s16 imm = (s16)(instr); + s64 rs = regs.gpr[RS(instr)]; + regs.gpr[RT(instr)] = rs + imm; +} + +void Interpreter::div(u32 instr) { + s64 dividend = (s32)regs.gpr[RS(instr)]; + s64 divisor = (s32)regs.gpr[RT(instr)]; + + if(divisor == 0) { + regs.hi = dividend; + if(dividend >= 0) { + regs.lo = s64(-1); + } else { + regs.lo = s64(1); + } + } else { + s32 quotient = dividend / divisor; + s32 remainder = dividend % divisor; + regs.lo = quotient; + regs.hi = remainder; + } +} + +void Interpreter::divu(u32 instr) { + u32 dividend = regs.gpr[RS(instr)]; + u32 divisor = regs.gpr[RT(instr)]; + if(divisor == 0) { + regs.lo = -1; + regs.hi = (s32)dividend; + } else { + s32 quotient = (s32)(dividend / divisor); + s32 remainder = (s32)(dividend % divisor); + regs.lo = quotient; + regs.hi = remainder; + } +} + +void Interpreter::ddiv(u32 instr) { + s64 dividend = regs.gpr[RS(instr)]; + s64 divisor = regs.gpr[RT(instr)]; + if (dividend == 0x8000000000000000 && divisor == 0xFFFFFFFFFFFFFFFF) { + regs.lo = dividend; + regs.hi = 0; + } else if(divisor == 0) { + regs.hi = dividend; + if(dividend >= 0) { + regs.lo = -1; + } else { + regs.lo = 1; + } + } else { + s64 quotient = dividend / divisor; + s64 remainder = dividend % divisor; + regs.lo = quotient; + regs.hi = remainder; + } +} + +void Interpreter::ddivu(u32 instr) { + u64 dividend = regs.gpr[RS(instr)]; + u64 divisor = regs.gpr[RT(instr)]; + if(divisor == 0) { + regs.lo = -1; + regs.hi = (s64)dividend; + } else { + u64 quotient = dividend / divisor; + u64 remainder = dividend % divisor; + regs.lo = (s64)quotient; + regs.hi = (s64)remainder; + } +} + +void Interpreter::branch(bool cond, s64 address) { + regs.delaySlot = true; + if (cond) { + regs.nextPC = address; + } +} + +void Interpreter::branch_likely(bool cond, s64 address) { + if (cond) { + regs.delaySlot = true; + regs.nextPC = address; + } else { + regs.SetPC64(regs.nextPC); + } +} + +void Interpreter::b(u32 instr, bool cond) { + s16 imm = instr; + s64 offset = u64((s64)imm) << 2; + s64 address = regs.pc + offset; + branch(cond, address); +} + +void Interpreter::blink(u32 instr, bool cond) { + regs.gpr[31] = regs.nextPC; + s16 imm = instr; + s64 offset = u64((s64)imm) << 2; + s64 address = regs.pc + offset; + branch(cond, address); +} + +void Interpreter::bl(u32 instr, bool cond) { + s16 imm = instr; + s64 offset = u64((s64)imm) << 2; + s64 address = regs.pc + offset; + branch_likely(cond, address); +} + +void Interpreter::bllink(u32 instr, bool cond) { + regs.gpr[31] = regs.nextPC; + s16 imm = instr; + s64 offset = u64((s64)imm) << 2; + s64 address = regs.pc + offset; + branch_likely(cond, address); +} + +void Interpreter::lui(u32 instr) { + u64 val = s64((s16)instr); + val <<= 16; + regs.gpr[RT(instr)] = val; +} + +void Interpreter::lb(u32 instr) { + u64 address = regs.gpr[RS(instr)] + (s16)instr; + u32 paddr = 0; + if(!MapVAddr(regs, LOAD, address, paddr)) { + HandleTLBException(regs, address); + FireException(regs, GetTLBExceptionCode(regs.cop0.tlbError, LOAD), 0, true); + } else { + regs.gpr[RT(instr)] = (s8)mem.Read8(regs, paddr); + } +} + +void Interpreter::lh(u32 instr) { + u64 address = regs.gpr[RS(instr)] + (s16)instr; + if ((address & 0b1) > 0) { + HandleTLBException(regs, address); + FireException(regs, ExceptionCode::AddressErrorLoad, 0, true); + return; + } + + u32 paddr = 0; + if(!MapVAddr(regs, LOAD, address, paddr)) { + HandleTLBException(regs, address); + FireException(regs, GetTLBExceptionCode(regs.cop0.tlbError, LOAD), 0, true); + } else { + regs.gpr[RT(instr)] = (s16)mem.Read16(regs, paddr); + } +} + +void Interpreter::lw(u32 instr) { + s16 offset = instr; + u64 address = regs.gpr[RS(instr)] + offset; + if (check_address_error(0b11, address)) { + HandleTLBException(regs, address); + FireException(regs, ExceptionCode::AddressErrorLoad, 0, true); + return; + } + + u32 physical = 0; + if (!MapVAddr(regs, LOAD, address, physical)) { + HandleTLBException(regs, address); + FireException(regs, GetTLBExceptionCode(regs.cop0.tlbError, LOAD), 0, true); + } else { + regs.gpr[RT(instr)] = (s32)mem.Read32(regs, physical); + } +} + +void Interpreter::ll(u32 instr) { + u64 address = regs.gpr[RS(instr)] + (s16)instr; + u32 physical; + if (!MapVAddr(regs, LOAD, address, physical)) { + HandleTLBException(regs, address); + FireException(regs, GetTLBExceptionCode(regs.cop0.tlbError, LOAD), 0, true); + } else { + if ((address & 0b11) > 0) { + FireException(regs, ExceptionCode::AddressErrorLoad, 0, true); + return; + } else { + regs.gpr[RT(instr)] = (s32)mem.Read32(regs, physical); + } + } + + regs.cop0.llbit = true; + regs.cop0.LLAddr = physical >> 4; +} + +void Interpreter::lwl(u32 instr) { + u64 address = regs.gpr[RS(instr)] + (s16)instr; + u32 paddr = 0; + if(!MapVAddr(regs, LOAD, address, paddr)) { + HandleTLBException(regs, address); + FireException(regs, GetTLBExceptionCode(regs.cop0.tlbError, LOAD), 0, true); + } else { + u32 shift = 8 * ((address ^ 0) & 3); + u32 mask = 0xFFFFFFFF << shift; + u32 data = mem.Read32(regs, paddr & ~3); + s32 result = s32((regs.gpr[RT(instr)] & ~mask) | (data << shift)); + regs.gpr[RT(instr)] = result; + } +} + +void Interpreter::lwr(u32 instr) { + u64 address = regs.gpr[RS(instr)] + (s16)instr; + u32 paddr = 0; + if(!MapVAddr(regs, LOAD, address, paddr)) { + HandleTLBException(regs, address); + FireException(regs, GetTLBExceptionCode(regs.cop0.tlbError, LOAD), 0, true); + } else { + u32 shift = 8 * ((address ^ 3) & 3); + u32 mask = 0xFFFFFFFF >> shift; + u32 data = mem.Read32(regs, paddr & ~3); + s32 result = s32((regs.gpr[RT(instr)] & ~mask) | (data >> shift)); + regs.gpr[RT(instr)] = result; + } +} + +void Interpreter::ld(u32 instr) { + s64 address = regs.gpr[RS(instr)] + (s16)instr; + if (check_address_error(0b111, address)) { + HandleTLBException(regs, address); + FireException(regs, ExceptionCode::AddressErrorLoad, 0, true); + return; + } + + u32 paddr = 0; + if(!MapVAddr(regs, LOAD, address, paddr)) { + HandleTLBException(regs, address); + FireException(regs, GetTLBExceptionCode(regs.cop0.tlbError, LOAD), 0, true); + } else { + s64 value = mem.Read64(regs, paddr); + regs.gpr[RT(instr)] = value; + } +} + +void Interpreter::lld(u32 instr) { + if (!regs.cop0.is_64bit_addressing && !regs.cop0.kernel_mode) { + FireException(regs, ExceptionCode::ReservedInstruction, 0, true); + return; + } + + u64 address = regs.gpr[RS(instr)] + (s16)instr; + u32 paddr; + if (!MapVAddr(regs, LOAD, address, paddr)) { + HandleTLBException(regs, address); + FireException(regs, GetTLBExceptionCode(regs.cop0.tlbError, LOAD), 0, true); + } else { + if ((address & 0b111) > 0) { + FireException(regs, ExceptionCode::AddressErrorLoad, 0, true); + } else { + regs.gpr[RT(instr)] = mem.Read64(regs, paddr); + regs.cop0.llbit = true; + regs.cop0.LLAddr = paddr >> 4; + } + } +} + +void Interpreter::ldl(u32 instr) { + u64 address = regs.gpr[RS(instr)] + (s16)instr; + u32 paddr = 0; + if (!MapVAddr(regs, LOAD, address, paddr)) { + HandleTLBException(regs, address); + FireException(regs, GetTLBExceptionCode(regs.cop0.tlbError, LOAD), 0, true); + } else { + s32 shift = 8 * ((address ^ 0) & 7); + u64 mask = 0xFFFFFFFFFFFFFFFF << shift; + u64 data = mem.Read64(regs, paddr & ~7); + s64 result = (s64) ((regs.gpr[RT(instr)] & ~mask) | (data << shift)); + regs.gpr[RT(instr)] = result; + } +} + +void Interpreter::ldr(u32 instr) { + u64 address = regs.gpr[RS(instr)] + (s16)instr; + u32 paddr; + if (!MapVAddr(regs, LOAD, address, paddr)) { + HandleTLBException(regs, address); + FireException(regs, GetTLBExceptionCode(regs.cop0.tlbError, LOAD), 0, true); + } else { + s32 shift = 8 * ((address ^ 7) & 7); + u64 mask = 0xFFFFFFFFFFFFFFFF >> shift; + u64 data = mem.Read64(regs, paddr & ~7); + s64 result = (s64) ((regs.gpr[RT(instr)] & ~mask) | (data >> shift)); + regs.gpr[RT(instr)] = result; + } +} + +void Interpreter::lbu(u32 instr) { + u64 address = regs.gpr[RS(instr)] + (s16)instr; + u32 paddr; + if (!MapVAddr(regs, LOAD, address, paddr)) { + HandleTLBException(regs, address); + FireException(regs, GetTLBExceptionCode(regs.cop0.tlbError, LOAD), 0, true); + } else { + u8 value = mem.Read8(regs, paddr); + regs.gpr[RT(instr)] = value; + } +} + +void Interpreter::lhu(u32 instr) { + s64 address = regs.gpr[RS(instr)] + (s16)instr; + if ((address & 0b1) > 0) { + HandleTLBException(regs, address); + FireException(regs, ExceptionCode::AddressErrorLoad, 0, true); + return; + } + u32 paddr; + if (!MapVAddr(regs, LOAD, address, paddr)) { + HandleTLBException(regs, address); + FireException(regs, GetTLBExceptionCode(regs.cop0.tlbError, LOAD), 0, true); + } else { + u16 value = mem.Read16(regs, paddr); + regs.gpr[RT(instr)] = value; + } +} + +void Interpreter::lwu(u32 instr) { + s64 address = regs.gpr[RS(instr)] + (s16)instr; + if ((address & 0b11) > 0) { + HandleTLBException(regs, address); + FireException(regs, ExceptionCode::AddressErrorLoad, 0, true); + return; + } + + u32 paddr; + if (!MapVAddr(regs, LOAD, address, paddr)) { + HandleTLBException(regs, address); + FireException(regs, GetTLBExceptionCode(regs.cop0.tlbError, LOAD), 0, true); + } else { + u32 value = mem.Read32(regs, paddr); + regs.gpr[RT(instr)] = value; + } +} + +void Interpreter::sb(u32 instr) { + u64 address = regs.gpr[RS(instr)] + (s16)instr; + u32 paddr; + if (!MapVAddr(regs, STORE, address, paddr)) { + HandleTLBException(regs, address); + FireException(regs, GetTLBExceptionCode(regs.cop0.tlbError, STORE), 0, true); + } else { + mem.Write8(regs, paddr, regs.gpr[RT(instr)]); + } +} + +void Interpreter::sc(u32 instr) { + u64 address = regs.gpr[RS(instr)] + (s16)instr; + + if ((address & 0b11) > 0) { + FireException(regs, ExceptionCode::AddressErrorStore, 0, true); + return; + } + + if(regs.cop0.llbit) { + regs.cop0.llbit = false; + u32 paddr = 0; + if(!MapVAddr(regs, STORE, address, paddr)) { + HandleTLBException(regs, address); + FireException(regs, GetTLBExceptionCode(regs.cop0.tlbError, STORE), 0, true); + } else { + mem.Write32(regs, paddr, regs.gpr[RT(instr)]); + regs.gpr[RT(instr)] = 1; + } + } else { + regs.gpr[RT(instr)] = 0; + } +} + +void Interpreter::scd(u32 instr) { + if (!regs.cop0.is_64bit_addressing && !regs.cop0.kernel_mode) { + FireException(regs, ExceptionCode::ReservedInstruction, 0, true); + return; + } + + s64 address = regs.gpr[RS(instr)] + (s16)instr; + if ((address & 0b111) > 0) { + HandleTLBException(regs, address); + FireException(regs, ExceptionCode::AddressErrorStore, 0, true); + return; + } + + if(regs.cop0.llbit) { + regs.cop0.llbit = false; + u32 paddr = 0; + if(!MapVAddr(regs, STORE, address, paddr)) { + HandleTLBException(regs, address); + FireException(regs, GetTLBExceptionCode(regs.cop0.tlbError, STORE), 0, true); + } else { + mem.Write32(regs, paddr, regs.gpr[RT(instr)]); + regs.gpr[RT(instr)] = 1; + } + } else { + regs.gpr[RT(instr)] = 0; + } +} + +void Interpreter::sh(u32 instr) { + s64 address = regs.gpr[RS(instr)] + (s16)instr; + + u32 physical; + if(!MapVAddr(regs, STORE, address, physical)) { + HandleTLBException(regs, address); + FireException(regs, GetTLBExceptionCode(regs.cop0.tlbError, STORE), 0, true); + } else { + mem.Write16(regs, physical, regs.gpr[RT(instr)]); + } +} + +void Interpreter::sw(u32 instr) { + s16 offset = instr; + u64 address = regs.gpr[RS(instr)] + offset; + if (check_address_error(0b11, address)) { + HandleTLBException(regs, address); + FireException(regs, ExceptionCode::AddressErrorStore, 0, true); + return; + } + + u32 physical; + if(!MapVAddr(regs, STORE, address, physical)) { + HandleTLBException(regs, address); + FireException(regs, GetTLBExceptionCode(regs.cop0.tlbError, STORE), 0, true); + } else { + mem.Write32(regs, physical, regs.gpr[RT(instr)]); + } +} + +void Interpreter::sd(u32 instr) { + s64 address = regs.gpr[RS(instr)] + (s16)instr; + if (check_address_error(0b111, address)) { + HandleTLBException(regs, address); + FireException(regs, ExceptionCode::AddressErrorStore, 0, true); + return; + } + + u32 physical; + if(!MapVAddr(regs, STORE, address, physical)) { + HandleTLBException(regs, address); + FireException(regs, GetTLBExceptionCode(regs.cop0.tlbError, STORE), 0, true); + } else { + mem.Write64(regs, physical, regs.gpr[RT(instr)]); + } +} + +void Interpreter::sdl(u32 instr) { + u64 address = regs.gpr[RS(instr)] + (s16)instr; + u32 paddr; + if (!MapVAddr(regs, STORE, address, paddr)) { + HandleTLBException(regs, address); + FireException(regs, GetTLBExceptionCode(regs.cop0.tlbError, STORE), 0, true); + } else { + s32 shift = 8 * ((address ^ 0) & 7); + u64 mask = 0xFFFFFFFFFFFFFFFF >> shift; + u64 data = mem.Read64(regs, paddr & ~7); + u64 rt = regs.gpr[RT(instr)]; + mem.Write64(regs, paddr & ~7, (data & ~mask) | (rt >> shift)); + } +} + +void Interpreter::sdr(u32 instr) { + u64 address = regs.gpr[RS(instr)] + (s16)instr; + u32 paddr; + if (!MapVAddr(regs, STORE, address, paddr)) { + HandleTLBException(regs, address); + FireException(regs, GetTLBExceptionCode(regs.cop0.tlbError, STORE), 0, true); + } else { + s32 shift = 8 * ((address ^ 7) & 7); + u64 mask = 0xFFFFFFFFFFFFFFFF << shift; + u64 data = mem.Read64(regs, paddr & ~7); + u64 rt = regs.gpr[RT(instr)]; + mem.Write64(regs, paddr & ~7, (data & ~mask) | (rt << shift)); + } +} + +void Interpreter::swl(u32 instr) { + u64 address = regs.gpr[RS(instr)] + (s16)instr; + u32 paddr; + if (!MapVAddr(regs, STORE, address, paddr)) { + HandleTLBException(regs, address); + FireException(regs, GetTLBExceptionCode(regs.cop0.tlbError, STORE), 0, true); + } else { + u32 shift = 8 * ((address ^ 0) & 3); + u32 mask = 0xFFFFFFFF >> shift; + u32 data = mem.Read32(regs, paddr & ~3); + u32 rt = regs.gpr[RT(instr)]; + mem.Write32(regs, paddr & ~3, (data & ~mask) | (rt >> shift)); + } +} + +void Interpreter::swr(u32 instr) { + u64 address = regs.gpr[RS(instr)] + (s16)instr; + u32 paddr; + if (!MapVAddr(regs, STORE, address, paddr)) { + HandleTLBException(regs, address); + FireException(regs, GetTLBExceptionCode(regs.cop0.tlbError, STORE), 0, true); + } else { + u32 shift = 8 * ((address ^ 3) & 3); + u32 mask = 0xFFFFFFFF << shift; + u32 data = mem.Read32(regs, paddr & ~3); + u32 rt = regs.gpr[RT(instr)]; + mem.Write32(regs, paddr & ~3, (data & ~mask) | (rt << shift)); + } +} + +void Interpreter::ori(u32 instr) { + s64 imm = (u16)instr; + s64 result = imm | regs.gpr[RS(instr)]; + regs.gpr[RT(instr)] = result; +} + +void Interpreter::or_(u32 instr) { + if(likely(RD(instr) != 0)) { + regs.gpr[RD(instr)] = regs.gpr[RS(instr)] | regs.gpr[RT(instr)]; + } +} + +void Interpreter::nor(u32 instr) { + if(likely(RD(instr) != 0)) { + regs.gpr[RD(instr)] = ~(regs.gpr[RS(instr)] | regs.gpr[RT(instr)]); + } +} + +void Interpreter::j(u32 instr) { + s32 target = (instr & 0x3ffffff) << 2; + s64 address = (regs.oldPC & ~0xfffffff) | target; + + branch(true, address); +} + +void Interpreter::jal(u32 instr) { + regs.gpr[31] = regs.nextPC; + j(instr); +} + +void Interpreter::jalr(u32 instr) { + branch(true, regs.gpr[RS(instr)]); + if(likely(RD(instr) != 0)) { + regs.gpr[RD(instr)] = regs.pc + 4; + } +} + +void Interpreter::slti(u32 instr) { + s16 imm = instr; + regs.gpr[RT(instr)] = regs.gpr[RS(instr)] < imm; +} + +void Interpreter::sltiu(u32 instr) { + s16 imm = instr; + regs.gpr[RT(instr)] = (u64)regs.gpr[RS(instr)] < imm; +} + +void Interpreter::slt(u32 instr) { + if(likely(RD(instr) != 0)) { + regs.gpr[RD(instr)] = regs.gpr[RS(instr)] < regs.gpr[RT(instr)]; + } +} + +void Interpreter::sltu(u32 instr) { + if(likely(RD(instr) != 0)) { + regs.gpr[RD(instr)] = (u64) regs.gpr[RS(instr)] < (u64) regs.gpr[RT(instr)]; + } +} + +void Interpreter::xori(u32 instr) { + s64 imm = (u16)instr; + regs.gpr[RT(instr)] = regs.gpr[RS(instr)] ^ imm; +} + +void Interpreter::xor_(u32 instr) { + if(likely(RD(instr) != 0)) { + regs.gpr[RD(instr)] = regs.gpr[RT(instr)] ^ regs.gpr[RS(instr)]; + } +} + +void Interpreter::andi(u32 instr) { + s64 imm = (u16)instr; + regs.gpr[RT(instr)] = regs.gpr[RS(instr)] & imm; +} + +void Interpreter::and_(u32 instr) { + if(likely(RD(instr) != 0)) { + regs.gpr[RD(instr)] = regs.gpr[RS(instr)] & regs.gpr[RT(instr)]; + } +} + +void Interpreter::sll(u32 instr) { + if(likely(RD(instr) != 0)) { + u8 sa = ((instr >> 6) & 0x1f); + s32 result = regs.gpr[RT(instr)] << sa; + regs.gpr[RD(instr)] = (s64) result; + } +} + +void Interpreter::sllv(u32 instr) { + if(likely(RD(instr) != 0)) { + u8 sa = (regs.gpr[RS(instr)]) & 0x1F; + u32 rt = regs.gpr[RT(instr)]; + s32 result = rt << sa; + regs.gpr[RD(instr)] = (s64) result; + } +} + +void Interpreter::dsll32(u32 instr) { + if(likely(RD(instr) != 0)) { + u8 sa = ((instr >> 6) & 0x1f); + s64 result = regs.gpr[RT(instr)] << (sa + 32); + regs.gpr[RD(instr)] = result; + } +} + +void Interpreter::dsll(u32 instr) { + if(likely(RD(instr) != 0)) { + u8 sa = ((instr >> 6) & 0x1f); + s64 result = regs.gpr[RT(instr)] << sa; + regs.gpr[RD(instr)] = result; + } +} + +void Interpreter::dsllv(u32 instr) { + if(likely(RD(instr) != 0)) { + s64 sa = regs.gpr[RS(instr)] & 63; + s64 result = regs.gpr[RT(instr)] << sa; + regs.gpr[RD(instr)] = result; + } +} + +void Interpreter::srl(u32 instr) { + if(likely(RD(instr) != 0)) { + u32 rt = regs.gpr[RT(instr)]; + u8 sa = ((instr >> 6) & 0x1f); + u32 result = rt >> sa; + regs.gpr[RD(instr)] = (s32) result; + } +} + +void Interpreter::srlv(u32 instr) { + if(likely(RD(instr) != 0)) { + u8 sa = (regs.gpr[RS(instr)] & 0x1F); + u32 rt = regs.gpr[RT(instr)]; + s32 result = rt >> sa; + regs.gpr[RD(instr)] = (s64) result; + } +} + +void Interpreter::dsrl(u32 instr) { + if(likely(RD(instr) != 0)) { + u64 rt = regs.gpr[RT(instr)]; + u8 sa = ((instr >> 6) & 0x1f); + u64 result = rt >> sa; + regs.gpr[RD(instr)] = s64(result); + } +} + +void Interpreter::dsrlv(u32 instr) { + if(likely(RD(instr) != 0)) { + u8 amount = (regs.gpr[RS(instr)] & 63); + u64 rt = regs.gpr[RT(instr)]; + u64 result = rt >> amount; + regs.gpr[RD(instr)] = s64(result); + } +} + +void Interpreter::dsrl32(u32 instr) { + if(likely(RD(instr) != 0)) { + u64 rt = regs.gpr[RT(instr)]; + u8 sa = ((instr >> 6) & 0x1f); + u64 result = rt >> (sa + 32); + regs.gpr[RD(instr)] = s64(result); + } +} + +void Interpreter::sra(u32 instr) { + if(likely(RD(instr) != 0)) { + s64 rt = regs.gpr[RT(instr)]; + u8 sa = ((instr >> 6) & 0x1f); + s32 result = rt >> sa; + regs.gpr[RD(instr)] = result; + } +} + +void Interpreter::srav(u32 instr) { + s64 rt = regs.gpr[RT(instr)]; + if(likely(RD(instr) != 0)) { + s64 rs = regs.gpr[RS(instr)]; + u8 sa = rs & 0x1f; + s32 result = rt >> sa; + regs.gpr[RD(instr)] = result; + } +} + +void Interpreter::dsra(u32 instr) { + if(likely(RD(instr) != 0)) { + s64 rt = regs.gpr[RT(instr)]; + u8 sa = ((instr >> 6) & 0x1f); + s64 result = rt >> sa; + regs.gpr[RD(instr)] = result; + } +} + +void Interpreter::dsrav(u32 instr) { + if(likely(RD(instr) != 0)) { + s64 rt = regs.gpr[RT(instr)]; + s64 rs = regs.gpr[RS(instr)]; + s64 sa = rs & 63; + s64 result = rt >> sa; + regs.gpr[RD(instr)] = result; + } +} + +void Interpreter::dsra32(u32 instr) { + if(likely(RD(instr) != 0)) { + s64 rt = regs.gpr[RT(instr)]; + u8 sa = ((instr >> 6) & 0x1f); + s64 result = rt >> (sa + 32); + regs.gpr[RD(instr)] = result; + } +} + +void Interpreter::jr(u32 instr) { + s64 address = regs.gpr[RS(instr)]; + branch(true, address); +} + +void Interpreter::dsub(u32 instr) { + s64 rt = regs.gpr[RT(instr)]; + s64 rs = regs.gpr[RS(instr)]; + s64 result = rs - rt; + if(check_signed_underflow(rs, rt, result)) { + FireException(regs, ExceptionCode::Overflow, 0, true); + } else { + if(likely(RD(instr) != 0)) { + regs.gpr[RD(instr)] = result; + } + } +} + +void Interpreter::dsubu(u32 instr) { + if(likely(RD(instr) != 0)) { + u64 rt = regs.gpr[RT(instr)]; + u64 rs = regs.gpr[RS(instr)]; + u64 result = rs - rt; + regs.gpr[RD(instr)] = s64(result); + } +} + +void Interpreter::sub(u32 instr) { + s32 rt = regs.gpr[RT(instr)]; + s32 rs = regs.gpr[RS(instr)]; + s32 result = rs - rt; + if(check_signed_underflow(rs, rt, result)) { + FireException(regs, ExceptionCode::Overflow, 0, true); + } else { + if(likely(RD(instr) != 0)) { + regs.gpr[RD(instr)] = result; + } + } +} + +void Interpreter::subu(u32 instr) { + if(likely(RD(instr) != 0)) { + u32 rt = regs.gpr[RT(instr)]; + u32 rs = regs.gpr[RS(instr)]; + u32 result = rs - rt; + regs.gpr[RD(instr)] = (s64) ((s32) result); + } +} + +void Interpreter::dmultu(u32 instr) { + u64 rt = regs.gpr[RT(instr)]; + u64 rs = regs.gpr[RS(instr)]; + u128 result = (u128)rt * (u128)rs; + regs.lo = (s64)(result & 0xFFFFFFFFFFFFFFFF); + regs.hi = (s64)(result >> 64); +} + +void Interpreter::dmult(u32 instr) { + s64 rt = regs.gpr[RT(instr)]; + s64 rs = regs.gpr[RS(instr)]; + s128 result = (s128)rt * (s128)rs; + regs.lo = result & 0xFFFFFFFFFFFFFFFF; + regs.hi = result >> 64; +} + +void Interpreter::multu(u32 instr) { + u32 rt = regs.gpr[RT(instr)]; + u32 rs = regs.gpr[RS(instr)]; + u64 result = (u64)rt * (u64)rs; + regs.lo = (s64)((s32)result); + regs.hi = (s64)((s32)(result >> 32)); +} + +void Interpreter::mult(u32 instr) { + s32 rt = regs.gpr[RT(instr)]; + s32 rs = regs.gpr[RS(instr)]; + s64 result = (s64)rt * (s64)rs; + regs.lo = (s64)((s32)result); + regs.hi = (s64)((s32)(result >> 32)); +} + +void Interpreter::mflo(u32 instr) { + if(likely(RD(instr) != 0)) { + regs.gpr[RD(instr)] = regs.lo; + } +} + +void Interpreter::mfhi(u32 instr) { + if(likely(RD(instr) != 0)) { + regs.gpr[RD(instr)] = regs.hi; + } +} + +void Interpreter::mtlo(u32 instr) { + regs.lo = regs.gpr[RS(instr)]; +} + +void Interpreter::mthi(u32 instr) { + regs.hi = regs.gpr[RS(instr)]; +} + +void Interpreter::trap(bool cond) { + if(cond) { + FireException(regs, ExceptionCode::Trap, 0, true); + } +} + +void Interpreter::mtc2(u32 instr) { + cop2Latch = regs.gpr[RT(instr)]; +} + +void Interpreter::mfc2(u32 instr) { + s32 value = cop2Latch; + regs.gpr[RT(instr)] = value; +} + +void Interpreter::dmtc2(u32 instr) { + cop2Latch = regs.gpr[RT(instr)]; +} + +void Interpreter::dmfc2(u32 instr) { + regs.gpr[RT(instr)] = cop2Latch; +} + +void Interpreter::ctc2(u32) { + +} + +void Interpreter::cfc2(u32) { + +} + +} \ No newline at end of file diff --git a/src/backend/core/registers/CMakeLists.txt b/src/backend/core/registers/CMakeLists.txt index f2a7ac0a..8efec528 100644 --- a/src/backend/core/registers/CMakeLists.txt +++ b/src/backend/core/registers/CMakeLists.txt @@ -1,4 +1,4 @@ -file(GLOB SOURCES *.cpp) -file(GLOB HEADERS *.cpp) +file(GLOB_RECURSE SOURCES *.cpp) +file(GLOB_RECURSE HEADERS *.cpp) -add_library(registers ${SOURCES} ${HEADERS}) \ No newline at end of file +add_library(registers ${SOURCES} ${HEADERS}) diff --git a/src/backend/core/registers/Cop1.hpp b/src/backend/core/registers/Cop1.hpp index 5b9d8938..b3143cda 100644 --- a/src/backend/core/registers/Cop1.hpp +++ b/src/backend/core/registers/Cop1.hpp @@ -64,7 +64,8 @@ struct Cop1 { FCR31 fcr31{}; FGR fgr[32]{}; void Reset(); - void decode(Registers&, Interpreter&, u32); + template // either JIT or Interpreter + void decode(Registers&, T&, u32); friend struct Interpreter; template @@ -135,6 +136,8 @@ struct Cop1 { return value; } private: + void decodeInterp(Registers&, Interpreter&, u32); + void decodeJIT(Registers&, JIT&, u32); void absd(Registers&, u32 instr); void abss(Registers&, u32 instr); void absw(Registers&, u32 instr); diff --git a/src/backend/core/interpreter/cop/cop0instructions.cpp b/src/backend/core/registers/cop/cop0instructions.cpp similarity index 100% rename from src/backend/core/interpreter/cop/cop0instructions.cpp rename to src/backend/core/registers/cop/cop0instructions.cpp diff --git a/src/backend/core/interpreter/cop/cop1instructions.cpp b/src/backend/core/registers/cop/cop1instructions.cpp similarity index 100% rename from src/backend/core/interpreter/cop/cop1instructions.cpp rename to src/backend/core/registers/cop/cop1instructions.cpp