Some restructuring

This commit is contained in:
SimoneN64
2025-01-12 23:45:18 +01:00
parent 8210f37335
commit 536fbddf95
7 changed files with 173 additions and 172 deletions

View File

@@ -17,8 +17,6 @@ struct CodeGenerator : Xbyak::CodeGenerator {
CodeGenerator() : Xbyak::CodeGenerator{kCodeCacheSize} {} CodeGenerator() : Xbyak::CodeGenerator{kCodeCacheSize} {}
}; };
enum BranchCondition { EQ, NE, GT, GE, LT, LE, GTU, GEU, LTU, LEU };
struct JIT : BaseCPU { struct JIT : BaseCPU {
explicit JIT(ParallelRDP &); explicit JIT(ParallelRDP &);
~JIT() override = default; ~JIT() override = default;
@@ -42,6 +40,47 @@ private:
u64 cop2Latch{}; u64 cop2Latch{};
friend struct Cop1; friend struct Cop1;
// Credits to PCSX-Redux: https://github.com/grumpycoders/pcsx-redux
// Sets dest to "pointer"
void loadAddress(const Xbyak::Reg64 dest, void *pointer) { code.mov(dest, reinterpret_cast<uintptr_t>(pointer)); }
// Load a pointer to the JIT object in "reg"
void loadThisPointer(const Xbyak::Reg64 reg) { code.mov(reg, code.rbp); }
// Emit a call to a class member function, passing "thisObject" (+ an adjustment if necessary)
// As the function's "this" pointer. Only works with classes with single, non-virtual inheritance
// Hence the static asserts. Those are all we need though, thankfully.
template <typename T>
void emitMemberFunctionCall(T func, void *thisObject) {
void *functionPtr;
auto thisPtr = reinterpret_cast<uintptr_t>(thisObject);
#if defined(WIN32) || defined(_WIN32) || defined(__WIN32) && !defined(__CYGWIN__)
static_assert(sizeof(T) == 8, "[x64 JIT] Invalid size for member function pointer");
std::memcpy(&functionPtr, &func, sizeof(T));
#else
static_assert(sizeof(T) == 16, "[x64 JIT] Invalid size for member function pointer");
uintptr_t arr[2];
std::memcpy(arr, &func, sizeof(T));
// First 8 bytes correspond to the actual pointer to the function
functionPtr = reinterpret_cast<void *>(arr[0]);
// Next 8 bytes correspond to the "this" pointer adjustment
thisPtr += arr[1];
#endif
// Load this pointer to arg1
if (thisPtr == reinterpret_cast<uintptr_t>(this)) {
loadThisPointer(code.rdi);
} else {
loadAddress(code.rdi, reinterpret_cast<void *>(thisPtr));
}
code.call(functionPtr);
}
void SkipSlot();
void BranchTaken(s64 offs);
void BranchTaken(const Xbyak::Reg &offs);
#define check_address_error(mask, vaddr) \ #define check_address_error(mask, vaddr) \
(((!regs.cop0.is64BitAddressing) && (s32)(vaddr) != (vaddr)) || (((vaddr) & (mask)) != 0)) (((!regs.cop0.is64BitAddressing) && (s32)(vaddr) != (vaddr)) || (((vaddr) & (mask)) != 0))
@@ -59,14 +98,30 @@ private:
void addiu(u32); void addiu(u32);
void andi(u32); void andi(u32);
void and_(u32); void and_(u32);
void b(u32 instr, BranchCondition, u32 reg1, u32 reg2); void branch(const Xbyak::Reg &address);
void b(u32 instr, BranchCondition, u32 reg); void branch_likely(const Xbyak::Reg &address);
void blink(u32 instr, BranchCondition, u32 reg1, u32 reg2); void branch_constant(const bool cond, const s64 address);
void blink(u32 instr, BranchCondition, u32 reg); void branch_likely_constant(const bool cond, const s64 address);
void bl(u32 instr, BranchCondition, u32 reg1, u32 reg2); void bltz(u32);
void bl(u32 instr, BranchCondition, u32 reg); void bgez(u32);
void bllink(u32 instr, BranchCondition, u32 reg1, u32 reg2); void bltzl(u32);
void bllink(u32 instr, BranchCondition, u32 reg); void bgezl(u32);
void bltzal(u32);
void bgezal(u32);
void bltzall(u32);
void bgezall(u32);
void beq(u32);
void beql(u32);
void bne(u32);
void bnel(u32);
void blez(u32);
void blezl(u32);
void bgtz(u32);
void bgtzl(u32);
void bfc1(u32 instr);
void blfc1(u32 instr);
void bfc0(u32 instr);
void blfc0(u32 instr);
void dadd(u32); void dadd(u32);
void daddu(u32); void daddu(u32);
void daddi(u32); void daddi(u32);

View File

@@ -1,5 +1,5 @@
#include <core/registers/Cop0.hpp> #include <registers/Cop0.hpp>
#include <core/registers/Registers.hpp> #include <registers/Registers.hpp>
#include <log.hpp> #include <log.hpp>
#include <ranges> #include <ranges>

View File

@@ -1,8 +1,8 @@
#include <cfenv> #include <cfenv>
#include <cmath> #include <cmath>
#include <core/Interpreter.hpp> #include <Interpreter.hpp>
#include <core/registers/Cop1.hpp> #include <registers/Cop1.hpp>
#include <core/registers/Registers.hpp> #include <registers/Registers.hpp>
#include <utils/FloatingPoint.hpp> #include <utils/FloatingPoint.hpp>
namespace n64 { namespace n64 {
@@ -1220,71 +1220,7 @@ void Cop1::truncld(const u32 instr) {
FGR_D<s64>(regs.cop0.status, FD(instr)) = fd; FGR_D<s64>(regs.cop0.status, FD(instr)) = fd;
} }
template <class T> void Cop1::lwc1(Mem &mem, u32 instr) {
void Cop1::lwc1(T &cpu, Mem &mem, u32 instr) {
if constexpr (std::is_same_v<decltype(cpu), Interpreter &>) {
if (!CheckFPUUsable<true>())
return;
lwc1Interp(mem, instr);
} else if constexpr (std::is_same_v<decltype(cpu), JIT &>) {
lwc1JIT(cpu, mem, instr);
} else {
Util::panic("What the fuck did you just give me?!!");
}
}
template void Cop1::lwc1<Interpreter>(Interpreter &, Mem &, u32);
template void Cop1::lwc1<JIT>(JIT &, Mem &, u32);
template <class T>
void Cop1::swc1(T &cpu, Mem &mem, u32 instr) {
if constexpr (std::is_same_v<decltype(cpu), Interpreter &>) {
if (!CheckFPUUsable<true>())
return;
swc1Interp(mem, instr);
} else if constexpr (std::is_same_v<decltype(cpu), JIT &>) {
swc1JIT(cpu, mem, instr);
} else {
Util::panic("What the fuck did you just give me?!!");
}
}
template void Cop1::swc1<Interpreter>(Interpreter &, Mem &, u32);
template void Cop1::swc1<JIT>(JIT &, Mem &, u32);
template <class T>
void Cop1::ldc1(T &cpu, Mem &mem, u32 instr) {
if constexpr (std::is_same_v<decltype(cpu), Interpreter &>) {
if (!CheckFPUUsable<true>())
return;
ldc1Interp(mem, instr);
} else if constexpr (std::is_same_v<decltype(cpu), JIT &>) {
ldc1JIT(cpu, mem, instr);
} else {
Util::panic("What the fuck did you just give me?!!");
}
}
template void Cop1::ldc1<Interpreter>(Interpreter &, Mem &, u32);
template void Cop1::ldc1<JIT>(JIT &, Mem &, u32);
template <class T>
void Cop1::sdc1(T &cpu, Mem &mem, u32 instr) {
if constexpr (std::is_same_v<decltype(cpu), Interpreter &>) {
if (!CheckFPUUsable<true>())
return;
sdc1Interp(mem, instr);
} else if constexpr (std::is_same_v<decltype(cpu), JIT &>) {
sdc1JIT(cpu, mem, instr);
} else {
Util::panic("What the fuck did you just give me?!!");
}
}
template void Cop1::sdc1<Interpreter>(Interpreter &, Mem &, u32);
template void Cop1::sdc1<JIT>(JIT &, Mem &, u32);
void Cop1::lwc1Interp(Mem &mem, const u32 instr) {
const u64 addr = static_cast<s64>(static_cast<s16>(instr)) + regs.Read<s64>(BASE(instr)); const u64 addr = static_cast<s64>(static_cast<s16>(instr)) + regs.Read<s64>(BASE(instr));
if (u32 physical; !regs.cop0.MapVAddr(Cop0::LOAD, addr, physical)) { if (u32 physical; !regs.cop0.MapVAddr(Cop0::LOAD, addr, physical)) {
@@ -1296,7 +1232,7 @@ void Cop1::lwc1Interp(Mem &mem, const u32 instr) {
} }
} }
void Cop1::swc1Interp(Mem &mem, const u32 instr) { void Cop1::swc1(Mem &mem, u32 instr) {
const u64 addr = static_cast<s64>(static_cast<s16>(instr)) + regs.Read<s64>(BASE(instr)); const u64 addr = static_cast<s64>(static_cast<s16>(instr)) + regs.Read<s64>(BASE(instr));
if (u32 physical; !regs.cop0.MapVAddr(Cop0::STORE, addr, physical)) { if (u32 physical; !regs.cop0.MapVAddr(Cop0::STORE, addr, physical)) {
@@ -1307,14 +1243,7 @@ void Cop1::swc1Interp(Mem &mem, const u32 instr) {
} }
} }
void Cop1::unimplemented() { void Cop1::ldc1(Mem &mem, u32 instr) {
if (!CheckFPUUsable())
return;
SetCauseUnimplemented();
regs.cop0.FireException(ExceptionCode::FloatingPointError, 0, regs.oldPC);
}
void Cop1::ldc1Interp(Mem &mem, const u32 instr) {
const u64 addr = static_cast<s64>(static_cast<s16>(instr)) + regs.Read<s64>(BASE(instr)); const u64 addr = static_cast<s64>(static_cast<s16>(instr)) + regs.Read<s64>(BASE(instr));
if (u32 physical; !regs.cop0.MapVAddr(Cop0::LOAD, addr, physical)) { if (u32 physical; !regs.cop0.MapVAddr(Cop0::LOAD, addr, physical)) {
@@ -1326,7 +1255,7 @@ void Cop1::ldc1Interp(Mem &mem, const u32 instr) {
} }
} }
void Cop1::sdc1Interp(Mem &mem, const u32 instr) { void Cop1::sdc1(Mem &mem, u32 instr) {
const u64 addr = static_cast<s64>(static_cast<s16>(instr)) + regs.Read<s64>(BASE(instr)); const u64 addr = static_cast<s64>(static_cast<s16>(instr)) + regs.Read<s64>(BASE(instr));
if (u32 physical; !regs.cop0.MapVAddr(Cop0::STORE, addr, physical)) { if (u32 physical; !regs.cop0.MapVAddr(Cop0::STORE, addr, physical)) {
@@ -1337,6 +1266,13 @@ void Cop1::sdc1Interp(Mem &mem, const u32 instr) {
} }
} }
void Cop1::unimplemented() {
if (!CheckFPUUsable())
return;
SetCauseUnimplemented();
regs.cop0.FireException(ExceptionCode::FloatingPointError, 0, regs.oldPC);
}
void Cop1::mfc1(const u32 instr) { void Cop1::mfc1(const u32 instr) {
if (!CheckFPUUsable<true>()) if (!CheckFPUUsable<true>())
return; return;

View File

@@ -304,7 +304,38 @@ void Interpreter::Exec(const u32 instr) {
regs.cop0.decode(*this, instr); regs.cop0.decode(*this, instr);
break; break;
case COP1: case COP1:
regs.cop1.decode(*this, instr); {
const u8 mask_sub = (instr >> 21) & 0x1F;
const u8 mask_branch = (instr >> 16) & 0x1F;
if (mask_sub == 0x08) {
switch (mask_branch) {
case 0:
if (!regs.cop1.CheckFPUUsable())
return;
b(instr, !regs.cop1.fcr31.compare);
break;
case 1:
if (!regs.cop1.CheckFPUUsable())
return;
b(instr, regs.cop1.fcr31.compare);
break;
case 2:
if (!regs.cop1.CheckFPUUsable())
return;
bl(instr, !regs.cop1.fcr31.compare);
break;
case 3:
if (!regs.cop1.CheckFPUUsable())
return;
bl(instr, regs.cop1.fcr31.compare);
break;
default:
Util::panic("Undefined BC COP1 {:02X}", mask_branch);
}
break;
}
regs.cop1.decode(instr);
}
break; break;
case COP2: case COP2:
cop2Decode(instr); cop2Decode(instr);
@@ -387,13 +418,13 @@ void Interpreter::Exec(const u32 instr) {
ll(instr); ll(instr);
break; break;
case LWC1: case LWC1:
regs.cop1.lwc1(*this, mem, instr); regs.cop1.lwc1(mem, instr);
break; break;
case LLD: case LLD:
lld(instr); lld(instr);
break; break;
case LDC1: case LDC1:
regs.cop1.ldc1(*this, mem, instr); regs.cop1.ldc1(mem, instr);
break; break;
case LD: case LD:
ld(instr); ld(instr);
@@ -402,13 +433,13 @@ void Interpreter::Exec(const u32 instr) {
sc(instr); sc(instr);
break; break;
case SWC1: case SWC1:
regs.cop1.swc1(*this, mem, instr); regs.cop1.swc1(mem, instr);
break; break;
case SCD: case SCD:
scd(instr); scd(instr);
break; break;
case SDC1: case SDC1:
regs.cop1.sdc1(*this, mem, instr); regs.cop1.sdc1(mem, instr);
break; break;
case SD: case SD:
sd(instr); sd(instr);

View File

@@ -172,16 +172,16 @@ void JIT::regimm(const u32 instr) {
// 000r_rccc // 000r_rccc
switch (const u8 mask = instr >> 16 & 0x1F) { switch (const u8 mask = instr >> 16 & 0x1F) {
case BLTZ: case BLTZ:
b(instr, LT, RS(instr)); bltz(instr);
break; break;
case BGEZ: case BGEZ:
b(instr, GE, RS(instr)); bgez(instr);
break; break;
case BLTZL: case BLTZL:
bl(instr, LT, RS(instr)); bltzl(instr);
break; break;
case BGEZL: case BGEZL:
bl(instr, GE, RS(instr)); bgezl(instr);
break; break;
case TGEI: case TGEI:
trap(regs.Read<s64>(RS(instr)) >= static_cast<s64>(static_cast<s16>(instr))); trap(regs.Read<s64>(RS(instr)) >= static_cast<s64>(static_cast<s16>(instr)));
@@ -202,16 +202,16 @@ void JIT::regimm(const u32 instr) {
trap(regs.Read<s64>(RS(instr)) != static_cast<s64>(static_cast<s16>(instr))); trap(regs.Read<s64>(RS(instr)) != static_cast<s64>(static_cast<s16>(instr)));
break; break;
case BLTZAL: case BLTZAL:
blink(instr, LT, RS(instr)); bltzal(instr);
break; break;
case BGEZAL: case BGEZAL:
blink(instr, GE, RS(instr)); bgezal(instr);
break; break;
case BLTZALL: case BLTZALL:
bllink(instr, LT, RS(instr)); bltzall(instr);
break; break;
case BGEZALL: case BGEZALL:
bllink(instr, GE, RS(instr)); bgezall(instr);
break; break;
default: default:
Util::panic("Unimplemented regimm {} {} ({:08X}) (pc: {:016X})", (mask >> 3) & 3, mask & 7, instr, Util::panic("Unimplemented regimm {} {} ({:08X}) (pc: {:016X})", (mask >> 3) & 3, mask & 7, instr,
@@ -234,16 +234,16 @@ void JIT::Emit(const u32 instr) {
jal(instr); jal(instr);
break; break;
case BEQ: case BEQ:
b(instr, EQ, RS(instr), RT(instr)); beq(instr);
break; break;
case BNE: case BNE:
b(instr, NE, RS(instr), RT(instr)); bne(instr);
break; break;
case BLEZ: case BLEZ:
b(instr, LE, RS(instr)); blez(instr);
break; break;
case BGTZ: case BGTZ:
b(instr, GT, RS(instr)); bgtz(instr);
break; break;
case ADDI: case ADDI:
addi(instr); addi(instr);
@@ -273,21 +273,52 @@ void JIT::Emit(const u32 instr) {
regs.cop0.decode(*this, instr); regs.cop0.decode(*this, instr);
break; break;
case COP1: case COP1:
regs.cop1.decode(*this, instr); {
const u8 mask_sub = (instr >> 21) & 0x1F;
const u8 mask_branch = (instr >> 16) & 0x1F;
if (mask_sub == 0x08) {
switch (mask_branch) {
case 0:
// if (!regs.cop1.CheckFPUUsable())
// return;
bfc0(instr);
break;
case 1:
// if (!regs.cop1.CheckFPUUsable())
// return;
bfc1(instr);
break;
case 2:
// if (!regs.cop1.CheckFPUUsable())
// return;
blfc0(instr);
break;
case 3:
// if (!regs.cop1.CheckFPUUsable())
// return;
blfc1(instr);
break;
default:
Util::panic("Undefined BC COP1 {:02X}", mask_branch);
}
break;
}
regs.cop1.decode(instr);
}
break; break;
case COP2: case COP2:
break; break;
case BEQL: case BEQL:
bl(instr, EQ, RS(instr), RT(instr)); beql(instr);
break; break;
case BNEL: case BNEL:
bl(instr, NE, RS(instr), RT(instr)); bnel(instr);
break; break;
case BLEZL: case BLEZL:
bl(instr, LE, RS(instr)); blezl(instr);
break; break;
case BGTZL: case BGTZL:
bl(instr, GT, RS(instr)); bgtzl(instr);
break; break;
case DADDI: case DADDI:
daddi(instr); daddi(instr);

View File

@@ -12,20 +12,7 @@ void Cop1::Reset() {
memset(fgr, 0, 32 * sizeof(FloatingPointReg)); memset(fgr, 0, 32 * sizeof(FloatingPointReg));
} }
template <class T> void Cop1::decode(const u32 instr) {
void Cop1::decode(T &cpu, u32 instr) {
if constexpr (std::is_same_v<decltype(cpu), Interpreter &>) {
decodeInterp(cpu, instr);
} else {
Util::panic("What the fuck did you just give me?!");
}
}
template void Cop1::decode<Interpreter>(Interpreter &, u32);
template void Cop1::decode<JIT>(JIT &, u32);
void Cop1::decodeInterp(Interpreter &cpu, u32 instr) {
const u8 mask_sub = (instr >> 21) & 0x1F; const u8 mask_sub = (instr >> 21) & 0x1F;
const u8 mask_fun = instr & 0x3F; const u8 mask_fun = instr & 0x3F;
const u8 mask_branch = (instr >> 16) & 0x1F; const u8 mask_branch = (instr >> 16) & 0x1F;
@@ -55,32 +42,6 @@ void Cop1::decodeInterp(Interpreter &cpu, u32 instr) {
case 0x07: case 0x07:
unimplemented(); unimplemented();
break; break;
case 0x08:
switch (mask_branch) {
case 0:
if (!CheckFPUUsable())
return;
cpu.b(instr, !fcr31.compare);
break;
case 1:
if (!CheckFPUUsable())
return;
cpu.b(instr, fcr31.compare);
break;
case 2:
if (!CheckFPUUsable())
return;
cpu.bl(instr, !fcr31.compare);
break;
case 3:
if (!CheckFPUUsable())
return;
cpu.bl(instr, fcr31.compare);
break;
default:
Util::panic("Undefined BC COP1 {:02X}", mask_branch);
}
break;
case 0x10: // s case 0x10: // s
switch (mask_fun) { switch (mask_fun) {
case 0x00: case 0x00:

View File

@@ -118,11 +118,12 @@ struct Cop1 {
u32 fcr0{}; u32 fcr0{};
FCR31 fcr31{}; FCR31 fcr31{};
FloatingPointReg fgr[32]{}; FloatingPointReg fgr[32]{};
bool fgrIsConstant[32]{};
void Reset(); void Reset();
template <class T> // either JIT or Interpreter void decode(u32);
void decode(T &, u32);
friend struct Interpreter; friend struct Interpreter;
friend struct JIT;
template <bool preserveCause = false> template <bool preserveCause = false>
bool CheckFPUUsable(); bool CheckFPUUsable();
@@ -159,8 +160,6 @@ private:
auto FGR_S(const Cop0Status &, u32) -> T &; auto FGR_S(const Cop0Status &, u32) -> T &;
template <typename T> template <typename T>
auto FGR_D(const Cop0Status &, u32) -> T &; auto FGR_D(const Cop0Status &, u32) -> T &;
void decodeInterp(Interpreter &, u32);
void decodeJIT(JIT &, u32);
void absd(u32 instr); void absd(u32 instr);
void abss(u32 instr); void abss(u32 instr);
void adds(u32 instr); void adds(u32 instr);
@@ -234,23 +233,11 @@ private:
void negd(u32 instr); void negd(u32 instr);
void sqrts(u32 instr); void sqrts(u32 instr);
void sqrtd(u32 instr); void sqrtd(u32 instr);
template <class T> void lwc1(Mem &, u32);
void lwc1(T &, Mem &, u32); void swc1(Mem &, u32);
template <class T> void ldc1(Mem &, u32);
void swc1(T &, Mem &, u32); void sdc1(Mem &, u32);
template <class T>
void ldc1(T &, Mem &, u32);
template <class T>
void sdc1(T &, Mem &, u32);
void lwc1Interp(Mem &, u32);
void swc1Interp(Mem &, u32);
void ldc1Interp(Mem &, u32);
void sdc1Interp(Mem &, u32);
void lwc1JIT(JIT &, Mem &, u32) { Util::panic("[JIT]: lwc1 not implemented!"); }
void swc1JIT(JIT &, Mem &, u32) { Util::panic("[JIT]: swc1 not implemented!"); }
void ldc1JIT(JIT &, Mem &, u32) { Util::panic("[JIT]: ldc1 not implemented!"); }
void sdc1JIT(JIT &, Mem &, u32) { Util::panic("[JIT]: sdc1 not implemented!"); }
void mfc1(u32 instr); void mfc1(u32 instr);
void dmfc1(u32 instr); void dmfc1(u32 instr);
void mtc1(u32 instr); void mtc1(u32 instr);