diff --git a/src/backend/Core.cpp b/src/backend/Core.cpp index 0b257145..5fc70602 100644 --- a/src/backend/Core.cpp +++ b/src/backend/Core.cpp @@ -3,7 +3,7 @@ #include namespace n64 { -Core::Core(ParallelRDP& parallel) : cpu(std::make_unique(parallel)) {} +Core::Core(ParallelRDP& parallel) : cpu(std::make_unique(parallel)) {} void Core::Stop() { render = false; diff --git a/src/backend/Core.hpp b/src/backend/Core.hpp index d80a2e00..9fab9386 100644 --- a/src/backend/Core.hpp +++ b/src/backend/Core.hpp @@ -1,5 +1,6 @@ #pragma once #include +#include #include struct Window; diff --git a/src/backend/core/CMakeLists.txt b/src/backend/core/CMakeLists.txt index 7723a5a7..3cc08fbc 100644 --- a/src/backend/core/CMakeLists.txt +++ b/src/backend/core/CMakeLists.txt @@ -2,10 +2,11 @@ file(GLOB SOURCES *.cpp) file(GLOB HEADERS *.hpp) add_subdirectory(interpreter) +add_subdirectory(jit) add_subdirectory(mem) add_subdirectory(mmio) add_subdirectory(registers) add_subdirectory(rsp) add_library(core ${SOURCES} ${HEADERS}) -target_link_libraries(core PRIVATE interpreter mem mmio unarr registers rsp) \ No newline at end of file +target_link_libraries(core PRIVATE jit interpreter mem mmio unarr registers rsp) \ No newline at end of file diff --git a/src/backend/core/JIT.cpp b/src/backend/core/JIT.cpp index a488316b..b0d30069 100644 --- a/src/backend/core/JIT.cpp +++ b/src/backend/core/JIT.cpp @@ -2,7 +2,10 @@ #include namespace n64 { -JIT::JIT(ParallelRDP& parallel) : mem(regs, parallel) { } +JIT::JIT(ParallelRDP& parallel) : mem(regs, parallel) { + regs.gpr[0] = 0; + regs.gprIsConstant[0] = true; +} bool JIT::ShouldServiceInterrupt() { bool interrupts_pending = (regs.cop0.status.im & regs.cop0.cause.interruptPending) != 0; @@ -24,46 +27,40 @@ void JIT::CheckCompareInterrupt() { } int JIT::Step() { - CheckCompareInterrupt(); + u32 instruction; + s64 pc = regs.pc; - regs.prevDelaySlot = regs.delaySlot; - regs.delaySlot = false; + do { + //CheckCompareInterrupt(); - if (check_address_error(0b11, u64(regs.pc))) [[unlikely]] { - regs.cop0.HandleTLBException(regs.pc); - regs.cop0.FireException(ExceptionCode::AddressErrorLoad, 0, regs.pc); - return 1; - } + //regs.prevDelaySlot = regs.delaySlot; + //regs.delaySlot = false; - u32 paddr = 0; - if (!regs.cop0.MapVAddr(Cop0::LOAD, regs.pc, paddr)) { - regs.cop0.HandleTLBException(regs.pc); - regs.cop0.FireException(regs.cop0.GetTLBExceptionCode(regs.cop0.tlbError, Cop0::LOAD), 0, regs.pc); - return 1; - } - - u32 instruction = mem.Read(regs, paddr); - while (!InstrEndsBlock(instr)) { - - if (!regs.cop0.MapVAddr(Cop0::LOAD, regs.pc, paddr)) { - regs.cop0.HandleTLBException(regs.pc); - regs.cop0.FireException(regs.cop0.GetTLBExceptionCode(regs.cop0.tlbError, Cop0::LOAD), 0, regs.pc); + /*if (check_address_error(0b11, u64(pc))) [[unlikely]] { + regs.cop0.HandleTLBException(pc); + regs.cop0.FireException(ExceptionCode::AddressErrorLoad, 0, pc); return 1; + }*/ + + u32 paddr = 0; + if (!regs.cop0.MapVAddr(Cop0::LOAD, pc, paddr)) { + /*regs.cop0.HandleTLBException(pc); + regs.cop0.FireException(regs.cop0.GetTLBExceptionCode(regs.cop0.tlbError, Cop0::LOAD), 0, pc); + return 1;*/ + Util::panic("[JIT]: Unhandled exception TLB exception {} when retrieving PC physical address!", regs.cop0.GetTLBExceptionCode(regs.cop0.tlbError, Cop0::LOAD)); } instruction = mem.Read(regs, paddr); - } - if(ShouldServiceInterrupt()) { - regs.cop0.FireException(ExceptionCode::Interrupt, 0, regs.pc); - return 1; - } + /*if(ShouldServiceInterrupt()) { + regs.cop0.FireException(ExceptionCode::Interrupt, 0, regs.pc); + return 1; + }*/ - regs.oldPC = regs.pc; - regs.pc = regs.nextPC; - regs.nextPC += 4; + pc += 4; - Exec(instruction); + //Exec(instruction); + } while (!InstrEndsBlock(instruction)); return 1; } diff --git a/src/backend/core/JIT.hpp b/src/backend/core/JIT.hpp index e7a0e26f..2cabc48a 100644 --- a/src/backend/core/JIT.hpp +++ b/src/backend/core/JIT.hpp @@ -32,10 +32,10 @@ private: std::vector Serialize() override; void Deserialize(const std::vector&) override; + void Emit(u32); void cop2Decode(u32); void special(u32); void regimm(u32); - void Exec(u32); void add(u32); void addu(u32); void addi(u32); diff --git a/src/backend/core/jit/CMakeLists.txt b/src/backend/core/jit/CMakeLists.txt new file mode 100644 index 00000000..9042a394 --- /dev/null +++ b/src/backend/core/jit/CMakeLists.txt @@ -0,0 +1,4 @@ +file(GLOB_RECURSE SOURCES *.cpp) +file(GLOB_RECURSE HEADERS *.hpp) + +add_library(jit ${SOURCES} ${HEADERS}) \ No newline at end of file diff --git a/src/backend/core/jit/helpers.hpp b/src/backend/core/jit/helpers.hpp index 31b4300a..c7ed09f3 100644 --- a/src/backend/core/jit/helpers.hpp +++ b/src/backend/core/jit/helpers.hpp @@ -17,7 +17,8 @@ static inline bool InstrEndsBlock(u32 instr) { case SPECIAL: return SpecialEndsBlock(instr); case REGIMM: case J: case JAL: case BEQ: case BNE: case BLEZ: case BGTZ: case BEQL: - case BNEL: case BLEZL: case BGTZL: return true: + case BNEL: case BLEZL: case BGTZL: return true; + default: return false; } } } \ No newline at end of file diff --git a/src/backend/core/jit/instructions.cpp b/src/backend/core/jit/instructions.cpp new file mode 100644 index 00000000..5d286240 --- /dev/null +++ b/src/backend/core/jit/instructions.cpp @@ -0,0 +1,317 @@ +#include + +#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 JIT::lui(u32 instr) { + u64 val = s64((s16)instr); + val <<= 16; + if (RT(instr) != 0) [[likely]] { + regs.gpr[RT(instr)] = val; + regs.gprIsConstant[RT(instr)] = true; + } +} + +void JIT::add(u32 instr) { + if(regs.IsRegConstant(RS(instr), RT(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)) { + //regs.cop0.FireException(ExceptionCode::Overflow, 0, regs.oldPC); + Util::panic("[JIT]: Unhandled Overflow exception in ADD!"); + } + + if (RD(instr) != 0) [[likely]] { + regs.gpr[RD(instr)] = s32(result); + regs.gprIsConstant[RD(instr)] = true; + } + } else { + Util::panic("[JIT]: Implement non constant ADD"); + } +} + +void JIT::addu(u32 instr) { + if(regs.IsRegConstant(RS(instr), RT(instr))) { + s32 rs = (s32) regs.gpr[RS(instr)]; + s32 rt = (s32) regs.gpr[RT(instr)]; + s32 result = rs + rt; + + if (RD(instr) != 0) [[likely]] { + regs.gpr[RD(instr)] = result; + regs.gprIsConstant[RD(instr)] = true; + } + } else { + Util::panic("[JIT]: Implement non constant ADDI"); + } +} + +void JIT::addi(u32 instr) { + if(regs.IsRegConstant(RS(instr))) { + u32 rs = regs.gpr[RS(instr)]; + u32 imm = s32(s16(instr)); + u32 result = rs + imm; + if(check_signed_overflow(rs, imm, result)) { + Util::panic("[JIT]: Unhandled Overflow exception in ADDI!"); + } else { + if (RT(instr) != 0) [[likely]] { + regs.gpr[RT(instr)] = s32(result); + regs.gprIsConstant[RT(instr)] = true; + } + } + } else { + Util::panic("[JIT]: Implement non constant ADDI!"); + } +} + +void JIT::addiu(u32 instr) { + if(regs.IsRegConstant(RS(instr))) { + u32 rs = regs.gpr[RS(instr)]; + u32 imm = s32(s16(instr)); + u32 result = rs + imm; + + if (RT(instr) != 0) [[likely]] { + regs.gpr[RT(instr)] = s32(result); + regs.gprIsConstant[RT(instr)] = true; + } + } else { + Util::panic("[JIT]: Implement non constant ADDIU!"); + } +} + +void JIT::andi(u32 instr) { + s64 imm = (u16)instr; + if(regs.IsRegConstant(RS(instr))) { + if (RT(instr) != 0) [[likely]] { + regs.gpr[RT(instr)] = regs.gpr[RS(instr)] & imm; + regs.gprIsConstant[RT(instr)] = true; + } + } else { + Util::panic("[JIT]: Implement non constant ANDI!"); + } +} + +void JIT::and_(u32 instr) { + if(regs.IsRegConstant(RS(instr), RT(instr))) { + if (RD(instr) != 0) [[likely]] { + regs.gpr[RD(instr)] = regs.gpr[RS(instr)] & regs.gpr[RT(instr)]; + regs.gprIsConstant[RD(instr)] = true; + } + } else { + Util::panic("[JIT]: Implement non constant AND!"); + } +} + +void JIT::dadd(u32 instr) { + if(regs.IsRegConstant(RS(instr), RT(instr))) { + u64 rs = regs.gpr[RS(instr)]; + u64 rt = regs.gpr[RT(instr)]; + u64 result = rt + rs; + if (check_signed_overflow(rs, rt, result)) { + //regs.cop0.FireException(ExceptionCode::Overflow, 0, regs.oldPC); + Util::panic("[JIT]: Unhandled Overflow exception in DADD!"); + } + + if (RD(instr) != 0) [[likely]] { + regs.gpr[RD(instr)] = result; + regs.gprIsConstant[RD(instr)] = true; + } + } else { + Util::panic("[JIT]: Implement non constant DADD!"); + } +} + +void JIT::daddu(u32 instr) { + if(regs.IsRegConstant(RS(instr), RT(instr))) { + if (RD(instr) != 0) [[likely]] { + s64 rs = regs.gpr[RS(instr)]; + s64 rt = regs.gpr[RT(instr)]; + regs.gpr[RD(instr)] = rt + rs; + regs.gprIsConstant[RD(instr)] = true; + } + } else { + Util::panic("[JIT]: Implement non constant DADD!"); + } +} + +void JIT::daddi(u32 instr) { + if(regs.IsRegConstant(RS(instr))) { + u64 imm = s64(s16(instr)); + u64 rs = regs.gpr[RS(instr)]; + u64 result = imm + rs; + if (check_signed_overflow(rs, imm, result)) { + //regs.cop0.FireException(ExceptionCode::Overflow, 0, regs.oldPC); + Util::panic("[JIT]: Unhandled Overflow exception in DADDI!"); + } + + if (RT(instr) != 0) [[likely]] { + regs.gpr[RT(instr)] = result; + regs.gprIsConstant[RT(instr)] = true; + } + } else { + Util::panic("[JIT]: Implement non constant DADDI!"); + } +} + +void JIT::daddiu(u32 instr) { + if(regs.IsRegConstant(RS(instr))) { + if (RT(instr) != 0) [[likely]] { + s16 imm = s16(instr); + s64 rs = regs.gpr[RS(instr)]; + regs.gpr[RT(instr)] = imm + rs; + regs.gprIsConstant[RT(instr)] = true; + } + } else { + Util::panic("[JIT]: Implement non constant DADDI!"); + } +} + +void JIT::ddiv(u32 instr) { + if(regs.IsRegConstant(RS(instr), RT(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; + } + + regs.loIsConstant = true; + regs.hiIsConstant = true; + } else { + Util::panic("[JIT]: Implement non constant DDIV!"); + } +} + +void JIT::ddivu(u32 instr) { + if(regs.IsRegConstant(RS(instr), RT(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; + } + + regs.loIsConstant = true; + regs.hiIsConstant = true; + } else { + Util::panic("[JIT]: Implement non constant DDIVU!"); + } +} + +void JIT::div(u32 instr) { + if(regs.IsRegConstant(RS(instr), RT(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; + } + + regs.loIsConstant = true; + regs.hiIsConstant = true; + } else { + Util::panic("[JIT]: Implement non constant DIV!"); + } +} + +void JIT::divu(u32 instr) { + if(regs.IsRegConstant(RS(instr), RT(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; + } + + regs.loIsConstant = true; + regs.hiIsConstant = true; + } else { + Util::panic("[JIT]: Implement non constant DIVU!"); + } +} + +void JIT::dmult(u32 instr) { + +} + +void JIT::dmultu(u32 instr) { + +} + +void JIT::dsll(u32 instr) { + +} + +void JIT::dsllv(u32 instr) { + +} + +void JIT::dsll32(u32 instr) { + +} + +void JIT::dsra(u32 instr) { + +} + +void JIT::dsrav(u32 instr) { + +} + +void JIT::dsra32(u32 instr) { + +} + +void JIT::dsrl(u32 instr) { + +} + +void JIT::dsrlv(u32 instr) { + +} + +void JIT::dsrl32(u32 instr) { + +} + +void JIT::dsub(u32 instr) { + +} + +void JIT::dsubu(u32 instr) { + +} +} \ No newline at end of file diff --git a/src/backend/core/registers/Registers.hpp b/src/backend/core/registers/Registers.hpp index aab24af9..ebfb9b7b 100644 --- a/src/backend/core/registers/Registers.hpp +++ b/src/backend/core/registers/Registers.hpp @@ -7,9 +7,20 @@ struct Registers { void Reset(); void SetPC64(s64); void SetPC32(s32); - + + bool IsRegConstant(u32 index) { + if(index == 0) return true; + return gprIsConstant[index]; + } + + template + bool IsRegConstant(Args... indices) { + return IsRegConstant(indices...); + } + s64 gpr[32]{}; bool gprIsConstant[32]{}; + bool loIsConstant = false, hiIsConstant = false; Cop0 cop0; Cop1 cop1; s64 oldPC{}, pc{}, nextPC{};