Buncha instructions for JIT
This commit is contained in:
@@ -40,4 +40,8 @@ static bool InstrEndsBlock(const u32 instr) {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#define REG(acc, x) code.acc[offsetof(JIT, regs) + offsetof(Registers, x)]
|
||||||
|
#define GPR(x) code.qword[offsetof(JIT, regs) + offsetof(Registers, gpr) + 8 * x]
|
||||||
|
#define GPR_constant_marker(x) code.byte[offsetof(JIT, regs) + offsetof(Registers, gprIsConstant) + x]
|
||||||
} // namespace n64
|
} // namespace n64
|
||||||
|
|||||||
@@ -1,42 +1,90 @@
|
|||||||
#include <JIT.hpp>
|
#include <JIT.hpp>
|
||||||
|
#include <jit/helpers.hpp>
|
||||||
|
|
||||||
#define check_signed_overflow(op1, op2, res) (((~((op1) ^ (op2)) & ((op1) ^ (res))) >> ((sizeof(res) * 8) - 1)) & 1)
|
#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)
|
#define check_signed_underflow(op1, op2, res) (((((op1) ^ (op2)) & ((op1) ^ (res))) >> ((sizeof(res) * 8) - 1)) & 1)
|
||||||
|
|
||||||
namespace n64 {
|
namespace n64 {
|
||||||
void JIT::lui(u32 instr) {
|
using namespace Xbyak::util;
|
||||||
u64 val = s64((s16)instr);
|
|
||||||
|
void JIT::lui(const u32 instr) {
|
||||||
|
if (RT(instr) == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
u64 val = static_cast<s64>(static_cast<s16>(instr));
|
||||||
val <<= 16;
|
val <<= 16;
|
||||||
regs.Write(RT(instr), val);
|
code.mov(GPR(RT(instr)), val);
|
||||||
|
code.mov(GPR_constant_marker(RT(instr)), 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
void JIT::add(u32 instr) {
|
void JIT::add(const u32 instr) {
|
||||||
|
if (RD(instr) == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
if (regs.IsRegConstant(RS(instr), RT(instr))) {
|
if (regs.IsRegConstant(RS(instr), RT(instr))) {
|
||||||
u32 rs = regs.Read<s32>(RS(instr));
|
const u32 rs = regs.Read<s32>(RS(instr));
|
||||||
u32 rt = regs.Read<s32>(RT(instr));
|
const u32 rt = regs.Read<s32>(RT(instr));
|
||||||
u32 result = rs + rt;
|
const u32 result = rs + rt;
|
||||||
if (check_signed_overflow(rs, rt, result)) {
|
if (check_signed_overflow(rs, rt, result)) {
|
||||||
// regs.cop0.FireException(ExceptionCode::Overflow, 0, regs.oldPC);
|
// regs.cop0.FireException(ExceptionCode::Overflow, 0, regs.oldPC);
|
||||||
Util::panic("[JIT]: Unhandled Overflow exception in ADD!");
|
Util::panic("[JIT]: Unhandled Overflow exception in ADD!");
|
||||||
}
|
}
|
||||||
regs.Write(RD(instr), s32(result));
|
|
||||||
} else {
|
code.mov(code.eax, static_cast<s32>(result));
|
||||||
Util::panic("[JIT]: Implement non constant ADD");
|
code.movsxd(code.rax, code.eax);
|
||||||
|
code.mov(GPR(RD(instr)), code.rax);
|
||||||
|
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (regs.IsRegConstant(RS(instr))) {
|
||||||
|
const u32 rs = regs.Read<s32>(RS(instr));
|
||||||
|
code.mov(code.eax, GPR(RT(instr)));
|
||||||
|
code.add(code.eax, rs);
|
||||||
|
code.movsxd(code.rax, code.eax);
|
||||||
|
code.mov(GPR(RD(instr)), code.rax);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (regs.IsRegConstant(RT(instr))) {
|
||||||
|
const u32 rt = regs.Read<s32>(RT(instr));
|
||||||
|
code.mov(code.eax, GPR(RS(instr)));
|
||||||
|
code.add(code.eax, rt);
|
||||||
|
code.movsxd(code.rax, code.eax);
|
||||||
|
code.mov(GPR(RD(instr)), code.rax);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
code.mov(code.edi, GPR(RT(instr)));
|
||||||
|
code.mov(code.eax, GPR(RS(instr)));
|
||||||
|
code.add(code.eax, code.edi);
|
||||||
|
code.movsxd(code.rax, code.eax);
|
||||||
|
code.mov(GPR(RD(instr)), code.rax);
|
||||||
}
|
}
|
||||||
|
|
||||||
void JIT::addu(u32 instr) {
|
void JIT::addu(u32 instr) {
|
||||||
|
if (RD(instr) == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
if (regs.IsRegConstant(RS(instr), RT(instr))) {
|
if (regs.IsRegConstant(RS(instr), RT(instr))) {
|
||||||
s32 rs = regs.Read<s32>(RS(instr));
|
const s32 rs = regs.Read<s32>(RS(instr));
|
||||||
s32 rt = regs.Read<s32>(RT(instr));
|
const s32 rt = regs.Read<s32>(RT(instr));
|
||||||
s32 result = rs + rt;
|
const s32 result = rs + rt;
|
||||||
regs.Write(RD(instr), result);
|
|
||||||
|
code.mov(code.eax, result);
|
||||||
|
code.movsxd(code.rax, code.eax);
|
||||||
|
code.mov(GPR(RD(instr)), code.rax);
|
||||||
} else {
|
} else {
|
||||||
Util::panic("[JIT]: Implement non constant ADDI");
|
Util::panic("[JIT]: Implement non constant ADDI");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void JIT::addi(u32 instr) {
|
void JIT::addi(u32 instr) {
|
||||||
|
if (RT(instr) == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
if (regs.IsRegConstant(RS(instr))) {
|
if (regs.IsRegConstant(RS(instr))) {
|
||||||
auto rs = regs.Read<u32>(RS(instr));
|
auto rs = regs.Read<u32>(RS(instr));
|
||||||
u32 imm = s32(s16(instr));
|
u32 imm = s32(s16(instr));
|
||||||
@@ -44,7 +92,9 @@ void JIT::addi(u32 instr) {
|
|||||||
if (check_signed_overflow(rs, imm, result)) {
|
if (check_signed_overflow(rs, imm, result)) {
|
||||||
Util::panic("[JIT]: Unhandled Overflow exception in ADDI!");
|
Util::panic("[JIT]: Unhandled Overflow exception in ADDI!");
|
||||||
} else {
|
} else {
|
||||||
regs.Write(RT(instr), s32(result));
|
code.mov(code.eax, static_cast<s32>(result));
|
||||||
|
code.movsxd(code.rax, code.eax);
|
||||||
|
code.mov(GPR(RT(instr)), code.rax);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Util::panic("[JIT]: Implement non constant ADDI!");
|
Util::panic("[JIT]: Implement non constant ADDI!");
|
||||||
@@ -52,6 +102,9 @@ void JIT::addi(u32 instr) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void JIT::addiu(u32 instr) {
|
void JIT::addiu(u32 instr) {
|
||||||
|
if (RT(instr) == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
if (regs.IsRegConstant(RS(instr))) {
|
if (regs.IsRegConstant(RS(instr))) {
|
||||||
auto rs = regs.Read<u32>(RS(instr));
|
auto rs = regs.Read<u32>(RS(instr));
|
||||||
u32 imm = s32(s16(instr));
|
u32 imm = s32(s16(instr));
|
||||||
@@ -63,6 +116,9 @@ void JIT::addiu(u32 instr) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void JIT::andi(u32 instr) {
|
void JIT::andi(u32 instr) {
|
||||||
|
if (RT(instr) == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
s64 imm = (u16)instr;
|
s64 imm = (u16)instr;
|
||||||
if (regs.IsRegConstant(RS(instr))) {
|
if (regs.IsRegConstant(RS(instr))) {
|
||||||
regs.Write(RT(instr), regs.Read<s64>(RS(instr)) & imm);
|
regs.Write(RT(instr), regs.Read<s64>(RS(instr)) & imm);
|
||||||
@@ -72,6 +128,9 @@ void JIT::andi(u32 instr) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void JIT::and_(u32 instr) {
|
void JIT::and_(u32 instr) {
|
||||||
|
if (RD(instr) == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
if (regs.IsRegConstant(RS(instr), RT(instr))) {
|
if (regs.IsRegConstant(RS(instr), RT(instr))) {
|
||||||
regs.Write(RD(instr), regs.Read<s64>(RS(instr)) & regs.Read<s64>(RT(instr)));
|
regs.Write(RD(instr), regs.Read<s64>(RS(instr)) & regs.Read<s64>(RT(instr)));
|
||||||
} else {
|
} else {
|
||||||
@@ -79,76 +138,158 @@ void JIT::and_(u32 instr) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void branch(Registers ®s, const bool cond, const s64 address) {
|
void JIT::SkipSlot() {
|
||||||
regs.delaySlot = true;
|
code.mov(code.rax, REG(qword, pc));
|
||||||
|
code.mov(REG(qword, oldPC), code.rax);
|
||||||
|
|
||||||
|
code.mov(code.rax, REG(qword, nextPC));
|
||||||
|
code.mov(REG(qword, pc), code.rax);
|
||||||
|
|
||||||
|
code.add(code.rax, 4);
|
||||||
|
code.mov(REG(qword, nextPC), code.rax);
|
||||||
|
}
|
||||||
|
|
||||||
|
void JIT::BranchTaken(const s64 offs) {
|
||||||
|
code.mov(code.rax, REG(qword, pc));
|
||||||
|
code.add(code.rax, offs);
|
||||||
|
code.mov(REG(qword, nextPC), code.rax);
|
||||||
|
}
|
||||||
|
|
||||||
|
void JIT::BranchTaken(const Xbyak::Reg &offs) {
|
||||||
|
code.mov(code.rax, REG(qword, pc));
|
||||||
|
code.add(code.rax, offs);
|
||||||
|
code.mov(REG(qword, nextPC), code.rax);
|
||||||
|
}
|
||||||
|
|
||||||
|
#define branch(offs, cond) \
|
||||||
|
do { \
|
||||||
|
code.mov(code.al, 1); \
|
||||||
|
code.mov(REG(byte, delaySlot), code.al); \
|
||||||
|
code.j##cond("taken"); \
|
||||||
|
code.jmp("not taken"); \
|
||||||
|
code.L("taken"); \
|
||||||
|
BranchTaken(offs); \
|
||||||
|
code.L("not_taken"); \
|
||||||
|
} \
|
||||||
|
while (0)
|
||||||
|
|
||||||
|
#define branch_likely(offs, cond) \
|
||||||
|
do { \
|
||||||
|
code.j##cond("taken"); \
|
||||||
|
SkipSlot(); \
|
||||||
|
code.jmp("not_taken"); \
|
||||||
|
code.L("taken"); \
|
||||||
|
code.mov(code.al, 1); \
|
||||||
|
code.mov(REG(byte, delaySlot), code.al); \
|
||||||
|
BranchTaken(offs); \
|
||||||
|
code.L("not_taken"); \
|
||||||
|
} \
|
||||||
|
while (0)
|
||||||
|
|
||||||
|
void JIT::branch_constant(const bool cond, const s64 offset) {
|
||||||
|
code.mov(code.al, 1);
|
||||||
|
code.mov(REG(byte, delaySlot), code.al);
|
||||||
if (cond) {
|
if (cond) {
|
||||||
regs.nextPC = address;
|
BranchTaken(offset);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void branch_likely(Registers ®s, const bool cond, const s64 address) {
|
void JIT::branch_likely_constant(const bool cond, const s64 offset) {
|
||||||
if (cond) {
|
if (cond) {
|
||||||
regs.delaySlot = true;
|
code.mov(code.al, 1);
|
||||||
regs.nextPC = address;
|
code.mov(REG(byte, delaySlot), code.al);
|
||||||
|
BranchTaken(offset);
|
||||||
} else {
|
} else {
|
||||||
regs.SetPC64(regs.nextPC);
|
SkipSlot();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool EvaluateCondition(Registers ®s, BranchCondition, u32, u32) {
|
void JIT::bfc0(u32 instr) {
|
||||||
Util::panic("[JIT]: non-constant EvaluateCondition!");
|
const s16 imm = instr;
|
||||||
|
const s64 offset = u64((s64)imm) << 2;
|
||||||
|
const s64 address = regs.pc + offset;
|
||||||
|
// branch(regs, EvaluateCondition(regs, cond, regs.cop1.fcr31.compare, 1), address);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool EvaluateConditionConstant(Registers ®s, const BranchCondition cond, const u32 reg1, const u32 reg2) {
|
void JIT::blfc0(u32 instr) {
|
||||||
switch (cond) {
|
const s16 imm = instr;
|
||||||
case EQ:
|
const s64 offset = u64((s64)imm) << 2;
|
||||||
return regs.Read<s64>(reg1) == regs.Read<s64>(reg2);
|
const s64 address = regs.pc + offset;
|
||||||
case NE:
|
// branch_likely(regs, EvaluateCondition(regs, cond, regs.cop1.fcr31.compare, 1), address);
|
||||||
return regs.Read<s64>(reg1) != regs.Read<s64>(reg2);
|
}
|
||||||
case LT:
|
|
||||||
return regs.Read<s64>(reg1) < regs.Read<s64>(reg2);
|
void JIT::bfc1(u32 instr) {
|
||||||
case LE:
|
const s16 imm = instr;
|
||||||
return regs.Read<s64>(reg1) <= regs.Read<s64>(reg2);
|
const s64 offset = u64((s64)imm) << 2;
|
||||||
case GT:
|
const s64 address = regs.pc + offset;
|
||||||
return regs.Read<s64>(reg1) > regs.Read<s64>(reg2);
|
// branch(regs, EvaluateCondition(regs, cond, regs.cop1.fcr31.compare, 1), address);
|
||||||
case GE:
|
}
|
||||||
return regs.Read<s64>(reg1) >= regs.Read<s64>(reg2);
|
|
||||||
case LTU:
|
void JIT::blfc1(u32 instr) {
|
||||||
return regs.Read<u64>(reg1) < regs.Read<u64>(reg2);
|
const s16 imm = instr;
|
||||||
case LEU:
|
const s64 offset = u64((s64)imm) << 2;
|
||||||
return regs.Read<u64>(reg1) <= regs.Read<u64>(reg2);
|
const s64 address = regs.pc + offset;
|
||||||
case GTU:
|
// branch_likely(regs, EvaluateCondition(regs, cond, regs.cop1.fcr31.compare, 1), address);
|
||||||
return regs.Read<u64>(reg1) > regs.Read<u64>(reg2);
|
}
|
||||||
case GEU:
|
|
||||||
return regs.Read<u64>(reg1) >= regs.Read<u64>(reg2);
|
void JIT::bltz(const u32 instr) {
|
||||||
|
const s16 imm = instr;
|
||||||
|
const s64 offset = u64((s64)imm) << 2;
|
||||||
|
if (regs.IsRegConstant(RS(instr))) {
|
||||||
|
branch_constant(regs.Read<s64>(RS(instr)) < 0, offset);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
code.mov(code.rax, GPR(RS(instr)));
|
||||||
|
code.cmp(code.rax, 0);
|
||||||
|
branch(offset, l);
|
||||||
}
|
}
|
||||||
|
|
||||||
void JIT::b(u32 instr, BranchCondition cond, u32 reg1, u32 reg2) {
|
void JIT::bgez(const u32 instr) {
|
||||||
bool isConstant = regs.IsRegConstant(reg1, reg2);
|
const s16 imm = instr;
|
||||||
if (isConstant) {
|
const s64 offset = u64((s64)imm) << 2;
|
||||||
const s16 imm = instr;
|
if (regs.IsRegConstant(RS(instr))) {
|
||||||
const s64 offset = u64((s64)imm) << 2;
|
branch_constant(regs.Read<s64>(RS(instr)) >= 0, offset);
|
||||||
const s64 address = regs.pc + offset;
|
return;
|
||||||
branch(regs, EvaluateConditionConstant(regs, cond, reg1, reg2), address);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
code.mov(code.rax, GPR(RS(instr)));
|
||||||
|
code.cmp(code.rax, 0);
|
||||||
|
branch(offset, ge);
|
||||||
}
|
}
|
||||||
|
|
||||||
void JIT::b(u32 instr, BranchCondition cond, u32 reg) {}
|
void JIT::bltzl(const u32 instr) {}
|
||||||
|
|
||||||
void JIT::blink(u32 instr, BranchCondition cond, u32 reg1, u32 reg2) {}
|
void JIT::bgezl(const u32 instr) {}
|
||||||
|
|
||||||
void JIT::blink(u32 instr, BranchCondition cond, u32 reg) {}
|
void JIT::bltzal(const u32 instr) {}
|
||||||
|
|
||||||
void JIT::bl(u32 instr, BranchCondition cond, u32 reg1, u32 reg2) {}
|
void JIT::bgezal(const u32 instr) {}
|
||||||
|
|
||||||
void JIT::bl(u32 instr, BranchCondition cond, u32 reg) {}
|
void JIT::bltzall(const u32 instr) {}
|
||||||
|
|
||||||
void JIT::bllink(u32 instr, BranchCondition cond, u32 reg1, u32 reg2) {}
|
void JIT::bgezall(const u32 instr) {}
|
||||||
|
|
||||||
void JIT::bllink(u32 instr, BranchCondition cond, u32 reg) {}
|
void JIT::beq(const u32 instr) {}
|
||||||
|
|
||||||
|
void JIT::beql(const u32 instr) {}
|
||||||
|
|
||||||
|
void JIT::bne(const u32 instr) {}
|
||||||
|
|
||||||
|
void JIT::bnel(const u32 instr) {}
|
||||||
|
|
||||||
|
void JIT::blez(const u32 instr) {}
|
||||||
|
|
||||||
|
void JIT::blezl(const u32 instr) {}
|
||||||
|
|
||||||
|
void JIT::bgtz(const u32 instr) {}
|
||||||
|
|
||||||
|
void JIT::bgtzl(const u32 instr) {}
|
||||||
|
|
||||||
void JIT::dadd(u32 instr) {
|
void JIT::dadd(u32 instr) {
|
||||||
|
if (RD(instr) == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
if (regs.IsRegConstant(RS(instr), RT(instr))) {
|
if (regs.IsRegConstant(RS(instr), RT(instr))) {
|
||||||
auto rs = regs.Read<u64>(RS(instr));
|
auto rs = regs.Read<u64>(RS(instr));
|
||||||
auto rt = regs.Read<u64>(RT(instr));
|
auto rt = regs.Read<u64>(RT(instr));
|
||||||
@@ -446,6 +587,194 @@ void JIT::dsubu(u32 instr) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void JIT::j(const u32 instr) {
|
||||||
|
const s32 target = (instr & 0x3ffffff) << 2;
|
||||||
|
code.mov(code.rax, REG(qword, oldPC));
|
||||||
|
code.and_(code.rax, ~0xfffffff);
|
||||||
|
code.or_(code.rax, target);
|
||||||
|
code.mov(code.dl, 1);
|
||||||
|
code.mov(REG(byte, delaySlot), code.dl);
|
||||||
|
code.mov(code.rdi, target);
|
||||||
|
code.mov(REG(qword, nextPC), code.rdi);
|
||||||
|
}
|
||||||
|
|
||||||
|
void JIT::jr(const u32 instr) {
|
||||||
|
if (regs.IsRegConstant(RS(instr))) {
|
||||||
|
const u64 address = regs.Read<s64>(RS(instr));
|
||||||
|
code.mov(code.dl, 1);
|
||||||
|
code.mov(REG(byte, delaySlot), code.dl);
|
||||||
|
code.mov(code.rdi, address);
|
||||||
|
code.mov(REG(qword, nextPC), code.rdi);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
code.mov(code.dl, 1);
|
||||||
|
code.mov(REG(byte, delaySlot), code.dl);
|
||||||
|
code.mov(code.rdi, GPR(RS(instr)));
|
||||||
|
code.mov(REG(qword, nextPC), code.rdi);
|
||||||
|
}
|
||||||
|
|
||||||
|
void JIT::jal(const u32 instr) {
|
||||||
|
code.mov(code.rax, REG(qword, nextPC));
|
||||||
|
code.mov(GPR(31), code.rax);
|
||||||
|
j(instr);
|
||||||
|
}
|
||||||
|
|
||||||
|
void JIT::jalr(const u32 instr) {
|
||||||
|
if (regs.IsRegConstant(RS(instr))) {
|
||||||
|
const u64 addr = regs.Read<s64>(RS(instr));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void JIT::lbu(u32 instr) {
|
||||||
|
if (regs.IsRegConstant(RS(instr))) {
|
||||||
|
const u64 address = regs.Read<s64>(RS(instr)) + (s16)instr;
|
||||||
|
u32 paddr;
|
||||||
|
if (!regs.cop0.MapVAddr(Cop0::LOAD, address, paddr)) {
|
||||||
|
// regs.cop0.HandleTLBException(address);
|
||||||
|
// regs.cop0.FireException(Cop0::GetTLBExceptionCode(regs.cop0.tlbError, Cop0::LOAD), 0, regs.oldPC);
|
||||||
|
Util::panic("[JIT]: Unhandled TLBL exception in LBU!");
|
||||||
|
} else {
|
||||||
|
const u8 value = mem.Read<u8>(regs, paddr);
|
||||||
|
regs.Write(RT(instr), value);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Util::panic("[JIT]: Implement non constant LBU!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void JIT::lb(u32 instr) {
|
||||||
|
if (regs.IsRegConstant(RS(instr))) {
|
||||||
|
const u64 address = regs.Read<s64>(RS(instr)) + (s16)instr;
|
||||||
|
if (u32 paddr = 0; !regs.cop0.MapVAddr(Cop0::LOAD, address, paddr)) {
|
||||||
|
// regs.cop0.HandleTLBException(address);
|
||||||
|
// regs.cop0.FireException(Cop0::GetTLBExceptionCode(regs.cop0.tlbError, Cop0::LOAD), 0, regs.oldPC);
|
||||||
|
Util::panic("[JIT]: Unhandled TLBL exception in LB!");
|
||||||
|
} else {
|
||||||
|
regs.Write(RT(instr), (s8)mem.Read<u8>(regs, paddr));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Util::panic("[JIT]: Implement non constant LB!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void JIT::ld(u32 instr) {
|
||||||
|
if (regs.IsRegConstant(RS(instr))) {
|
||||||
|
const s64 address = regs.Read<s64>(RS(instr)) + (s16)instr;
|
||||||
|
if (check_address_error(0b111, address)) {
|
||||||
|
// regs.cop0.HandleTLBException(address);
|
||||||
|
// regs.cop0.FireException(ExceptionCode::AddressErrorLoad, 0, regs.oldPC);
|
||||||
|
// return;
|
||||||
|
Util::panic("[JIT]: Unhandled ADEL exception in LD!");
|
||||||
|
}
|
||||||
|
|
||||||
|
u32 paddr = 0;
|
||||||
|
if (!regs.cop0.MapVAddr(Cop0::LOAD, address, paddr)) {
|
||||||
|
// regs.cop0.HandleTLBException(address);
|
||||||
|
// regs.cop0.FireException(Cop0::GetTLBExceptionCode(regs.cop0.tlbError, Cop0::LOAD), 0, regs.oldPC);
|
||||||
|
Util::panic("[JIT]: Unhandled TLBL exception in LD!");
|
||||||
|
} else {
|
||||||
|
const s64 value = mem.Read<u64>(regs, paddr);
|
||||||
|
regs.Write(RT(instr), value);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Util::panic("[JIT]: Implement non constant LD!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void JIT::ldc1(u32 instr) {
|
||||||
|
if (regs.IsRegConstant(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)) {
|
||||||
|
// regs.cop0.HandleTLBException(addr);
|
||||||
|
// regs.cop0.FireException(Cop0::GetTLBExceptionCode(regs.cop0.tlbError, Cop0::LOAD), 0, regs.oldPC);
|
||||||
|
Util::panic("[JIT]: Unhandled TLBL exception in LD1!");
|
||||||
|
} else {
|
||||||
|
const u64 data = mem.Read<u64>(regs, physical);
|
||||||
|
regs.cop1.FGR_T<u64>(regs.cop0.status, FT(instr)) = data;
|
||||||
|
regs.cop1.fgrIsConstant[FT(instr)] = true;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Util::panic("[JIT]: Implement non constant LD1!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void JIT::ldl(u32 instr) {
|
||||||
|
if (regs.IsRegConstant(RS(instr), RT(instr))) {
|
||||||
|
const u64 address = regs.Read<s64>(RS(instr)) + (s16)instr;
|
||||||
|
u32 paddr = 0;
|
||||||
|
if (!regs.cop0.MapVAddr(Cop0::LOAD, address, paddr)) {
|
||||||
|
// regs.cop0.HandleTLBException(address);
|
||||||
|
// regs.cop0.FireException(Cop0::GetTLBExceptionCode(regs.cop0.tlbError, Cop0::LOAD), 0, regs.oldPC);
|
||||||
|
Util::panic("[JIT]: Unhandled TLBL exception in LDL!");
|
||||||
|
} else {
|
||||||
|
const s32 shift = 8 * ((address ^ 0) & 7);
|
||||||
|
const u64 mask = 0xFFFFFFFFFFFFFFFF << shift;
|
||||||
|
const u64 data = mem.Read<u64>(regs, paddr & ~7);
|
||||||
|
const s64 result = (s64)((regs.Read<s64>(RT(instr)) & ~mask) | (data << shift));
|
||||||
|
regs.Write(RT(instr), result);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Util::panic("[JIT]: Implement non constant LDL!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void JIT::ldr(u32 instr) {
|
||||||
|
if (regs.IsRegConstant(RS(instr), RT(instr))) {
|
||||||
|
const u64 address = regs.Read<s64>(RS(instr)) + (s16)instr;
|
||||||
|
u32 paddr;
|
||||||
|
if (!regs.cop0.MapVAddr(Cop0::LOAD, address, paddr)) {
|
||||||
|
// regs.cop0.HandleTLBException(address);
|
||||||
|
// regs.cop0.FireException(Cop0::GetTLBExceptionCode(regs.cop0.tlbError, Cop0::LOAD), 0, regs.oldPC);
|
||||||
|
Util::panic("[JIT]: Unhandled TLBL exception in LDR!");
|
||||||
|
} else {
|
||||||
|
const s32 shift = 8 * ((address ^ 7) & 7);
|
||||||
|
const u64 mask = 0xFFFFFFFFFFFFFFFF >> shift;
|
||||||
|
const u64 data = mem.Read<u64>(regs, paddr & ~7);
|
||||||
|
const s64 result = (s64)((regs.Read<s64>(RT(instr)) & ~mask) | (data >> shift));
|
||||||
|
regs.Write(RT(instr), result);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Util::panic("[JIT]: Implement non constant LDR!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void JIT::lh(u32 instr) {
|
||||||
|
const u64 address = regs.Read<s64>(RS(instr)) + (s16)instr;
|
||||||
|
if (check_address_error(0b1, address)) {
|
||||||
|
// regs.cop0.HandleTLBException(address);
|
||||||
|
// regs.cop0.FireException(ExceptionCode::AddressErrorLoad, 0, regs.oldPC);
|
||||||
|
// return;
|
||||||
|
Util::panic("[JIT]: Unhandled ADEL exception in LH!");
|
||||||
|
}
|
||||||
|
|
||||||
|
u32 paddr = 0;
|
||||||
|
if (!regs.cop0.MapVAddr(Cop0::LOAD, address, paddr)) {
|
||||||
|
// regs.cop0.HandleTLBException(address);
|
||||||
|
// regs.cop0.FireException(Cop0::GetTLBExceptionCode(regs.cop0.tlbError, Cop0::LOAD), 0, regs.oldPC);
|
||||||
|
} else {
|
||||||
|
regs.Write(RT(instr), (s16)mem.Read<u16>(regs, paddr));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void JIT::lhu(u32) {}
|
||||||
|
|
||||||
|
void JIT::ll(u32) {}
|
||||||
|
|
||||||
|
void JIT::lld(u32) {}
|
||||||
|
|
||||||
|
void JIT::lw(u32) {}
|
||||||
|
|
||||||
|
void JIT::lwc1(u32) {}
|
||||||
|
|
||||||
|
void JIT::lwl(u32) {}
|
||||||
|
|
||||||
|
void JIT::lwu(u32) {}
|
||||||
|
|
||||||
|
void JIT::lwr(u32) {}
|
||||||
|
|
||||||
void JIT::mfhi(u32 instr) {
|
void JIT::mfhi(u32 instr) {
|
||||||
if (regs.hiIsConstant) {
|
if (regs.hiIsConstant) {
|
||||||
regs.Write(RD(instr), regs.hi);
|
regs.Write(RD(instr), regs.hi);
|
||||||
@@ -631,6 +960,92 @@ void JIT::srl(u32 instr) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void JIT::sw(const u32 instr) {
|
||||||
|
if (regs.IsRegConstant(RS(instr)) && regs.IsRegConstant(RT(instr))) {
|
||||||
|
const s16 offset = instr;
|
||||||
|
const u64 address = regs.Read<s64>(RS(instr)) + offset;
|
||||||
|
if (check_address_error(0b11, address)) {
|
||||||
|
// regs.cop0.HandleTLBException(address);
|
||||||
|
// regs.cop0.FireException(ExceptionCode::AddressErrorStore, 0, regs.oldPC);
|
||||||
|
Util::panic("[JIT]: Unhandled ADES exception in SW!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
u32 physical;
|
||||||
|
if (!regs.cop0.MapVAddr(Cop0::STORE, address, physical)) {
|
||||||
|
// regs.cop0.HandleTLBException(address);
|
||||||
|
// regs.cop0.FireException(Cop0::GetTLBExceptionCode(regs.cop0.tlbError, Cop0::STORE), 0, regs.oldPC);
|
||||||
|
Util::panic("[JIT]: Unhandled TLBS exception in SW!");
|
||||||
|
} else {
|
||||||
|
code.mov(code.rsi, reinterpret_cast<uintptr_t>(®s));
|
||||||
|
code.mov(code.edx, physical);
|
||||||
|
code.mov(code.rcx, regs.Read<s64>(RT(instr)));
|
||||||
|
emitMemberFunctionCall(&Mem::Write<u32>, &mem);
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (regs.IsRegConstant(RS(instr))) {
|
||||||
|
const s16 offset = instr;
|
||||||
|
const u64 address = regs.Read<s64>(RS(instr)) + offset;
|
||||||
|
if (check_address_error(0b11, address)) {
|
||||||
|
// regs.cop0.HandleTLBException(address);
|
||||||
|
// regs.cop0.FireException(ExceptionCode::AddressErrorStore, 0, regs.oldPC);
|
||||||
|
Util::panic("[JIT]: Unhandled ADES exception in SW!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
u32 physical;
|
||||||
|
if (!regs.cop0.MapVAddr(Cop0::STORE, address, physical)) {
|
||||||
|
// regs.cop0.HandleTLBException(address);
|
||||||
|
// regs.cop0.FireException(Cop0::GetTLBExceptionCode(regs.cop0.tlbError, Cop0::STORE), 0, regs.oldPC);
|
||||||
|
Util::panic("[JIT]: Unhandled TLBS exception in SW!");
|
||||||
|
} else {
|
||||||
|
code.mov(code.rsi, reinterpret_cast<uintptr_t>(®s));
|
||||||
|
code.mov(code.edx, physical);
|
||||||
|
code.mov(code.rcx, GPR(RT(instr)));
|
||||||
|
emitMemberFunctionCall(&Mem::Write<u32>, &mem);
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (regs.IsRegConstant(RT(instr))) {
|
||||||
|
const s16 offset = instr;
|
||||||
|
code.mov(code.rdx, GPR(RS(instr)));
|
||||||
|
code.add(code.rdx, offset);
|
||||||
|
|
||||||
|
code.mov(code.esi, Cop0::STORE);
|
||||||
|
|
||||||
|
u32 physical;
|
||||||
|
code.mov(code.rcx, reinterpret_cast<uintptr_t>(&physical));
|
||||||
|
emitMemberFunctionCall(&Cop0::MapVAddr, ®s.cop0);
|
||||||
|
|
||||||
|
code.mov(code.rsi, reinterpret_cast<uintptr_t>(®s));
|
||||||
|
code.mov(code.edx, physical);
|
||||||
|
code.mov(code.rcx, regs.Read<s64>(RT(instr)));
|
||||||
|
emitMemberFunctionCall(&Mem::Write<u32>, &mem);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const s16 offset = instr;
|
||||||
|
code.mov(code.rdx, GPR(RS(instr)));
|
||||||
|
code.add(code.rdx, offset);
|
||||||
|
|
||||||
|
code.mov(code.esi, Cop0::STORE);
|
||||||
|
|
||||||
|
u32 physical;
|
||||||
|
code.mov(code.rcx, reinterpret_cast<uintptr_t>(&physical));
|
||||||
|
emitMemberFunctionCall(&Cop0::MapVAddr, ®s.cop0);
|
||||||
|
|
||||||
|
code.mov(code.rsi, reinterpret_cast<uintptr_t>(®s));
|
||||||
|
code.mov(code.edx, physical);
|
||||||
|
code.mov(code.rcx, GPR(RT(instr)));
|
||||||
|
emitMemberFunctionCall(&Mem::Write<u32>, &mem);
|
||||||
|
}
|
||||||
|
|
||||||
void JIT::srlv(u32 instr) {
|
void JIT::srlv(u32 instr) {
|
||||||
if (regs.IsRegConstant(RS(instr), RT(instr))) {
|
if (regs.IsRegConstant(RS(instr), RT(instr))) {
|
||||||
u8 sa = (regs.Read<s64>(RS(instr)) & 0x1F);
|
u8 sa = (regs.Read<s64>(RS(instr)) & 0x1F);
|
||||||
@@ -651,10 +1066,13 @@ void JIT::or_(u32 instr) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void JIT::ori(u32 instr) {
|
void JIT::ori(u32 instr) {
|
||||||
|
if (RT(instr) == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
if (regs.IsRegConstant(RS(instr))) {
|
if (regs.IsRegConstant(RS(instr))) {
|
||||||
s64 imm = (u16)instr;
|
s64 imm = (u16)instr;
|
||||||
s64 result = imm | regs.Read<s64>(RS(instr));
|
s64 result = imm | regs.Read<s64>(RS(instr));
|
||||||
regs.Write(RT(instr), result);
|
code.mov(GPR(RT(instr)), result);
|
||||||
} else {
|
} else {
|
||||||
Util::panic("[JIT]: Implement non constant ORI!");
|
Util::panic("[JIT]: Implement non constant ORI!");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,13 +9,15 @@ struct Registers {
|
|||||||
void SetPC64(s64);
|
void SetPC64(s64);
|
||||||
void SetPC32(s32);
|
void SetPC32(s32);
|
||||||
|
|
||||||
bool IsRegConstant(u32 index) {
|
[[nodiscard]] bool IsRegConstant(const u32 index) const {
|
||||||
if (index == 0)
|
if (index == 0)
|
||||||
return true;
|
return true;
|
||||||
return gprIsConstant[index];
|
return gprIsConstant[index];
|
||||||
}
|
}
|
||||||
|
|
||||||
bool IsRegConstant(u32 first, u32 second) { return IsRegConstant(first) && IsRegConstant(second); }
|
[[nodiscard]] bool IsRegConstant(const u32 first, const u32 second) const {
|
||||||
|
return IsRegConstant(first) && IsRegConstant(second);
|
||||||
|
}
|
||||||
|
|
||||||
std::array<bool, 32> gprIsConstant{};
|
std::array<bool, 32> gprIsConstant{};
|
||||||
bool loIsConstant = false, hiIsConstant = false;
|
bool loIsConstant = false, hiIsConstant = false;
|
||||||
@@ -39,8 +41,6 @@ struct Registers {
|
|||||||
T Read(size_t);
|
T Read(size_t);
|
||||||
template <typename T>
|
template <typename T>
|
||||||
void Write(size_t, T);
|
void Write(size_t, T);
|
||||||
|
|
||||||
private:
|
|
||||||
std::array<s64, 32> gpr{};
|
std::array<s64, 32> gpr{};
|
||||||
};
|
};
|
||||||
} // namespace n64
|
} // namespace n64
|
||||||
|
|||||||
Reference in New Issue
Block a user