#include #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 { using namespace Xbyak::util; void JIT::lui(const u32 instr) { u64 val = static_cast(static_cast(instr)); val <<= 16; regs.Write(RT(instr), val); } void JIT::add(const u32 instr) { if (regs.IsRegConstant(RS(instr), RT(instr))) { const u32 rs = regs.Read(RS(instr)); const u32 rt = regs.Read(RT(instr)); const 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!"); } regs.Write(RD(instr), result); return; } if (regs.IsRegConstant(RS(instr))) { const u32 rs = regs.Read(RS(instr)); regs.Read(RT(instr), code.eax); code.add(code.eax, rs); regs.Write(RD(instr), code.eax); return; } if (regs.IsRegConstant(RT(instr))) { const u32 rt = regs.Read(RT(instr)); regs.Read(RS(instr), code.eax); code.add(code.eax, rt); regs.Write(RD(instr), code.eax); return; } regs.Read(RT(instr), code.eax); regs.Read(RS(instr), code.edi); code.add(code.eax, code.edi); regs.Write(RD(instr), code.eax); } void JIT::addu(u32 instr) { if (regs.IsRegConstant(RS(instr), RT(instr))) { const s32 rs = regs.Read(RS(instr)); const s32 rt = regs.Read(RT(instr)); const s32 result = rs + rt; regs.Write(RD(instr), result); return; } if (regs.IsRegConstant(RS(instr))) { const s32 rs = regs.Read(RS(instr)); regs.Read(RT(instr), code.eax); code.add(code.eax, rs); regs.Write(RD(instr), code.eax); return; } if (regs.IsRegConstant(RT(instr))) { const s32 rs = regs.Read(RT(instr)); regs.Read(RS(instr), code.eax); code.add(code.eax, rs); regs.Write(RD(instr), code.eax); return; } regs.Read(RS(instr), code.eax); regs.Read(RT(instr), code.edi); code.add(code.eax, code.edi); regs.Write(RD(instr), code.eax); } void JIT::addi(u32 instr) { u32 imm = s32(s16(instr)); if (regs.IsRegConstant(RS(instr))) { auto rs = regs.Read(RS(instr)); u32 result = rs + imm; if (check_signed_overflow(rs, imm, result)) { Util::panic("[JIT]: Unhandled Overflow exception in ADDI!"); } regs.Write(RT(instr), static_cast(result)); return; } regs.Read(RS(instr), code.eax); code.add(code.eax, imm); regs.Write(RT(instr), code.eax); } void JIT::addiu(u32 instr) { u32 imm = s32(s16(instr)); if (regs.IsRegConstant(RS(instr))) { auto rs = regs.Read(RS(instr)); u32 result = rs + imm; regs.Write(RT(instr), s32(result)); return; } regs.Read(RS(instr), code.eax); code.add(code.eax, imm); regs.Write(RT(instr), code.eax); } void JIT::andi(u32 instr) { const s64 imm = static_cast(instr); if (regs.IsRegConstant(RS(instr))) { regs.Write(RT(instr), regs.Read(RS(instr)) & imm); return; } regs.Read(RS(instr), code.rax); code.and_(code.rax, imm); regs.Write(RT(instr), code.rax); } void JIT::and_(u32 instr) { if (regs.IsRegConstant(RS(instr), RT(instr))) { regs.Write(RD(instr), regs.Read(RS(instr)) & regs.Read(RT(instr))); return; } if (regs.IsRegConstant(RS(instr))) { const auto rs = regs.Read(RS(instr)); regs.Read(RT(instr), code.rax); code.and_(code.rax, rs); regs.Write(RD(instr), code.rax); return; } if (regs.IsRegConstant(RT(instr))) { const auto rt = regs.Read(RT(instr)); regs.Read(RS(instr), code.rax); code.and_(code.rax, rt); regs.Write(RD(instr), code.rax); return; } regs.Read(RS(instr), code.rax); regs.Read(RT(instr), code.rdi); code.and_(code.rdi, code.rax); regs.Write(RD(instr), code.rdi); } void JIT::SkipSlot() { code.jmp("not_taken"); } void JIT::SkipSlotConstant() { blockOldPC = blockPC; blockPC = blockNextPC; blockNextPC = blockPC + 4; } void JIT::BranchTaken(const s64 offs) { code.mov(code.rax, blockPC + offs); code.mov(REG(qword, pc), code.rax); } void JIT::BranchNotTaken() { code.mov(code.rax, blockPC + 4); code.mov(REG(qword, pc), code.rax); } void JIT::BranchTaken(const Xbyak::Reg64 &offs) { code.add(offs, blockPC); code.mov(REG(qword, pc), offs); } void JIT::BranchAbsTaken(const s64 addr) { code.mov(code.rax, addr); code.mov(REG(qword, pc), code.rax); } void JIT::BranchAbsTaken(const Xbyak::Reg64 &addr) { code.mov(REG(qword, pc), addr); } #define branch(offs, cond) \ do { \ Xbyak::Label taken, not_taken; \ code.j##cond(taken); \ BranchNotTaken(); \ code.jmp(not_taken); \ code.L(taken); \ BranchTaken(offs); \ code.L(not_taken); \ } \ while (0) #define branch_abs(addr, cond) \ do { \ Xbyak::Label taken, not_taken; \ code.j##cond(taken); \ BranchNotTaken(); \ code.jmp(not_taken); \ code.L(taken); \ BranchAbsTaken(addr); \ code.L(not_taken); \ } \ while (0) #define branch_likely(offs, cond) \ do { \ Xbyak::Label taken; \ code.j##cond(taken); \ SkipSlot(); \ code.jmp(branch_likely_not_taken); \ code.L(taken); \ BranchTaken(offs); \ } \ while (0) void JIT::branch_constant(const bool cond, const s64 offset) { if (cond) { BranchTaken(offset); return; } BranchNotTaken(); } void JIT::branch_likely_constant(const bool cond, const s64 offset) { if (cond) { BranchTaken(offset); } else { SkipSlotConstant(); } } void JIT::branch_abs_constant(const bool cond, const s64 address) { if (cond) { BranchAbsTaken(address); return; } BranchNotTaken(); } void JIT::bfc0(u32 instr) { const s16 imm = instr; const s64 offset = u64((s64)imm) << 2; const s64 address = blockPC + offset; // code.mov(code.al, REG(byte, cop1.fcr31.compare)); // code.test(code.al, code.al); // branch(address, z); } void JIT::blfc0(u32 instr) { const s16 imm = instr; const s64 offset = u64((s64)imm) << 2; const s64 address = blockPC + offset; // code.mov(code.al, REG(byte, cop1.fcr31.compare)); // code.test(code.al, code.al); // branch_likely(address, z); } void JIT::bfc1(u32 instr) { const s16 imm = instr; const s64 offset = u64((s64)imm) << 2; const s64 address = blockPC + offset; // code.mov(code.al, REG(byte, cop1.fcr31.compare)); // code.test(code.al, code.al); // branch(address, nz); } void JIT::blfc1(u32 instr) { const s16 imm = instr; const s64 offset = u64((s64)imm) << 2; const s64 address = blockPC + offset; // code.mov(code.al, REG(byte, cop1.fcr31.compare)); // code.test(code.al, code.al); // branch_likely(address, nz); } 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(RS(instr)) < 0, offset); return; } regs.Read(RS(instr), code.rax); code.cmp(code.rax, 0); branch(offset, l); } void JIT::bgez(const u32 instr) { const s16 imm = instr; const s64 offset = u64((s64)imm) << 2; if (regs.IsRegConstant(RS(instr))) { branch_constant(regs.Read(RS(instr)) >= 0, offset); return; } regs.Read(RS(instr), code.rax); code.cmp(code.rax, 0); branch(offset, ge); } void JIT::bltzl(const u32 instr) { Util::panic("Implement branch likely < 0"); const s16 imm = instr; const s64 offset = u64((s64)imm) << 2; if (regs.IsRegConstant(RS(instr))) { branch_likely_constant(regs.Read(RS(instr)) < 0, offset); return; } regs.Read(RS(instr), code.rax); code.cmp(code.rax, 0); branch_likely(offset, l); } void JIT::bgezl(const u32 instr) { Util::panic("Implement branch likely >= 0"); const s16 imm = instr; const s64 offset = u64((s64)imm) << 2; if (regs.IsRegConstant(RS(instr))) { branch_likely_constant(regs.Read(RS(instr)) >= 0, offset); return; } regs.Read(RS(instr), code.rax); code.cmp(code.rax, 0); branch_likely(offset, ge); } void JIT::bltzal(const u32 instr) { const s16 imm = instr; const s64 offset = u64((s64)imm) << 2; regs.Write(31, blockNextPC); if (regs.IsRegConstant(RS(instr))) { branch_constant(regs.Read(RS(instr)) < 0, offset); return; } regs.Read(RS(instr), code.rax); code.cmp(code.rax, 0); branch(offset, l); } void JIT::bgezal(const u32 instr) { const s16 imm = instr; const s64 offset = u64((s64)imm) << 2; regs.Write(31, blockNextPC); if (regs.IsRegConstant(RS(instr))) { branch_constant(regs.Read(RS(instr)) >= 0, offset); return; } regs.Read(RS(instr), code.rax); code.cmp(code.rax, 0); branch(offset, ge); } void JIT::bltzall(const u32 instr) { Util::panic("Implement branch likely and link < 0"); const s16 imm = instr; const s64 offset = u64((s64)imm) << 2; regs.Write(31, blockNextPC); if (regs.IsRegConstant(RS(instr))) { branch_likely_constant(regs.Read(RS(instr)) < 0, offset); return; } regs.Read(RS(instr), code.rax); code.cmp(code.rax, 0); branch_likely(offset, l); } void JIT::bgezall(const u32 instr) { Util::panic("Implement branch likely and link >= 0"); const s16 imm = instr; const s64 offset = u64((s64)imm) << 2; regs.Write(31, blockNextPC); if (regs.IsRegConstant(RS(instr))) { branch_likely_constant(regs.Read(RS(instr)) >= 0, offset); return; } regs.Read(RS(instr), code.rax); code.cmp(code.rax, 0); branch_likely(offset, ge); } void JIT::beq(const u32 instr) { const s16 imm = instr; const s64 offset = u64((s64)imm) << 2; if (regs.IsRegConstant(RS(instr)) && regs.IsRegConstant(RT(instr))) { branch_constant(regs.Read(RS(instr)) == regs.Read(RT(instr)), offset); return; } if (regs.IsRegConstant(RS(instr))) { regs.Read(RT(instr), code.rax); code.cmp(code.rax, regs.Read(RS(instr))); branch(offset, e); return; } if (regs.IsRegConstant(RT(instr))) { regs.Read(RS(instr), code.rax); code.cmp(code.rax, regs.Read(RT(instr))); branch(offset, e); return; } regs.Read(RS(instr), code.rax); regs.Read(RT(instr), code.rdi); code.cmp(code.rax, code.rdi); branch(offset, e); } void JIT::beql(const u32 instr) { Util::panic("Implement branch likely =="); const s16 imm = instr; const s64 offset = u64((s64)imm) << 2; if (regs.IsRegConstant(RS(instr)) && regs.IsRegConstant(RT(instr))) { branch_likely_constant(regs.Read(RS(instr)) == regs.Read(RT(instr)), offset); return; } if (regs.IsRegConstant(RS(instr))) { regs.Read(RT(instr), code.rax); code.cmp(code.rax, regs.Read(RS(instr))); branch_likely(offset, e); return; } if (regs.IsRegConstant(RT(instr))) { regs.Read(RS(instr), code.rax); code.cmp(code.rax, regs.Read(RT(instr))); branch_likely(offset, e); return; } regs.Read(RS(instr), code.rax); regs.Read(RT(instr), code.rdi); code.cmp(code.rax, code.rdi); branch_likely(offset, e); } void JIT::bne(const u32 instr) { const s16 imm = instr; const s64 offset = u64((s64)imm) << 2; if (regs.IsRegConstant(RS(instr)) && regs.IsRegConstant(RT(instr))) { branch_constant(regs.Read(RS(instr)) != regs.Read(RT(instr)), offset); return; } if (regs.IsRegConstant(RS(instr))) { regs.Read(RT(instr), code.rax); code.cmp(code.rax, regs.Read(RS(instr))); branch(offset, ne); return; } if (regs.IsRegConstant(RT(instr))) { regs.Read(RS(instr), code.rax); code.cmp(code.rax, regs.Read(RT(instr))); branch(offset, ne); return; } regs.Read(RS(instr), code.rax); regs.Read(RT(instr), code.rdi); code.cmp(code.rax, code.rdi); branch(offset, ne); } void JIT::bnel(const u32 instr) { Util::panic("Implement branch likely !="); const s16 imm = instr; const s64 offset = u64((s64)imm) << 2; if (regs.IsRegConstant(RS(instr)) && regs.IsRegConstant(RT(instr))) { branch_likely_constant(regs.Read(RS(instr)) != regs.Read(RT(instr)), offset); return; } if (regs.IsRegConstant(RS(instr))) { regs.Read(RT(instr), code.rax); code.cmp(code.rax, regs.Read(RS(instr))); branch_likely(offset, ne); return; } if (regs.IsRegConstant(RT(instr))) { regs.Read(RS(instr), code.rax); code.cmp(code.rax, regs.Read(RT(instr))); branch_likely(offset, ne); return; } regs.Read(RS(instr), code.rax); regs.Read(RT(instr), code.rdi); code.cmp(code.rax, code.rdi); branch_likely(offset, ne); } void JIT::blez(const u32 instr) { const s16 imm = instr; const s64 offset = u64((s64)imm) << 2; if (regs.IsRegConstant(RS(instr))) { branch_constant(regs.Read(RS(instr)) <= 0, offset); return; } regs.Read(RS(instr), code.rax); code.cmp(code.rax, 0); branch(offset, le); } void JIT::blezl(const u32 instr) { Util::panic("Implement branch likely <= 0"); const s16 imm = instr; const s64 offset = u64((s64)imm) << 2; if (regs.IsRegConstant(RS(instr))) { branch_likely_constant(regs.Read(RS(instr)) <= 0, offset); return; } regs.Read(RS(instr), code.rax); code.cmp(code.rax, 0); branch_likely(offset, le); } void JIT::bgtz(const u32 instr) { const s16 imm = instr; const s64 offset = u64((s64)imm) << 2; if (regs.IsRegConstant(RS(instr))) { branch_constant(regs.Read(RS(instr)) > 0, offset); return; } regs.Read(RS(instr), code.rax); code.cmp(code.rax, 0); branch(offset, g); } void JIT::bgtzl(const u32 instr) { Util::panic("Implement branch likely > 0"); const s16 imm = instr; const s64 offset = u64((s64)imm) << 2; if (regs.IsRegConstant(RS(instr))) { branch_likely_constant(regs.Read(RS(instr)) > 0, offset); return; } regs.Read(RS(instr), code.rax); code.cmp(code.rax, 0); branch_likely(offset, g); } void JIT::dadd(u32 instr) { if (regs.IsRegConstant(RS(instr), RT(instr))) { auto rs = regs.Read(RS(instr)); auto rt = regs.Read(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!"); } regs.Write(RD(instr), result); return; } if (regs.IsRegConstant(RS(instr))) { auto rs = regs.Read(RS(instr)); regs.Read(RT(instr), code.rax); code.add(code.rax, rs); regs.Write(RD(instr), code.rax); return; } if (regs.IsRegConstant(RT(instr))) { auto rt = regs.Read(RT(instr)); regs.Read(RS(instr), code.rax); code.add(code.rax, rt); regs.Write(RD(instr), code.rax); return; } regs.Read(RS(instr), code.rax); regs.Read(RT(instr), code.rdi); code.add(code.rax, code.rdi); regs.Write(RD(instr), code.rax); } void JIT::daddu(u32 instr) { // TODO: IMPLEMENT DADDU BY ITSELF ACTUALLY dadd(instr); } void JIT::daddi(u32 instr) { u64 imm = s64(s16(instr)); if (regs.IsRegConstant(RS(instr))) { auto rs = regs.Read(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!"); } regs.Write(RT(instr), result); return; } regs.Read(RS(instr), code.rax); code.add(code.rax, imm); regs.Write(RT(instr), code.rax); } void JIT::daddiu(u32 instr) { // TODO: IMPLEMENT DADDIU BY ITSELF ACTUALLY daddi(instr); } void JIT::ddiv(u32 instr) { if (regs.IsRegConstant(RS(instr), RT(instr))) { auto dividend = regs.Read(RS(instr)); auto divisor = regs.Read(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.SetLOConstant(); regs.SetHIConstant(); } else { Util::panic("[JIT]: Implement non constant DDIV!"); } } void JIT::ddivu(u32 instr) { if (regs.IsRegConstant(RS(instr), RT(instr))) { auto dividend = regs.Read(RS(instr)); auto divisor = regs.Read(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.SetLOConstant(); regs.SetHIConstant(); } else { Util::panic("[JIT]: Implement non constant DDIVU!"); } } void JIT::div(u32 instr) { if (regs.IsRegConstant(RS(instr), RT(instr))) { s64 dividend = regs.Read(RS(instr)); s64 divisor = regs.Read(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.SetLOConstant(); regs.SetHIConstant(); } else { Util::panic("[JIT]: Implement non constant DIV!"); } } void JIT::divu(u32 instr) { if (regs.IsRegConstant(RS(instr), RT(instr))) { auto dividend = regs.Read(RS(instr)); auto divisor = regs.Read(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.SetLOConstant(); regs.SetHIConstant(); } else { Util::panic("[JIT]: Implement non constant DIVU!"); } } void JIT::dmult(u32 instr) { if (regs.IsRegConstant(RT(instr), RS(instr))) { auto rt = regs.Read(RT(instr)); auto rs = regs.Read(RS(instr)); s128 result = (s128)rt * (s128)rs; regs.lo = result & 0xFFFFFFFFFFFFFFFF; regs.hi = result >> 64; regs.SetHIConstant(); regs.SetLOConstant(); } else { Util::panic("[JIT]: Implement non constant DMULT!"); } } void JIT::dmultu(u32 instr) { if (regs.IsRegConstant(RT(instr), RS(instr))) { auto rt = regs.Read(RT(instr)); auto rs = regs.Read(RS(instr)); u128 result = (u128)rt * (u128)rs; regs.lo = s64(result & 0xFFFFFFFFFFFFFFFF); regs.hi = s64(result >> 64); regs.SetHIConstant(); regs.SetLOConstant(); } else { Util::panic("[JIT]: Implement non constant DMULT!"); } } void JIT::dsll(u32 instr) { if (regs.IsRegConstant(RT(instr))) { u8 sa = ((instr >> 6) & 0x1f); auto result = regs.Read(RT(instr)) << sa; regs.Write(RD(instr), result); } else { Util::panic("[JIT]: Implement non constant DSLL!"); } } void JIT::dsllv(u32 instr) { if (regs.IsRegConstant(RT(instr), RS(instr))) { auto sa = regs.Read(RS(instr)) & 63; auto result = regs.Read(RT(instr)) << sa; regs.Write(RD(instr), result); } else { Util::panic("[JIT]: Implement non constant DSLLV!"); } } void JIT::dsll32(u32 instr) { if (regs.IsRegConstant(RT(instr))) { u8 sa = ((instr >> 6) & 0x1f); auto result = regs.Read(RT(instr)) << (sa + 32); regs.Write(RD(instr), result); } else { Util::panic("[JIT]: Implement non constant DSLL32!"); } } void JIT::dsra(u32 instr) { if (regs.IsRegConstant(RT(instr))) { auto rt = regs.Read(RT(instr)); u8 sa = ((instr >> 6) & 0x1f); s64 result = rt >> sa; regs.Write(RD(instr), result); } else { Util::panic("[JIT]: Implement non constant DSRA!"); } } void JIT::dsrav(u32 instr) { if (regs.IsRegConstant(RT(instr), RS(instr))) { auto rt = regs.Read(RT(instr)); auto rs = regs.Read(RS(instr)); s64 sa = rs & 63; s64 result = rt >> sa; regs.Write(RD(instr), result); } else { Util::panic("[JIT]: Implement non constant DSRAV!"); } } void JIT::dsra32(u32 instr) { if (regs.IsRegConstant(RT(instr))) { auto rt = regs.Read(RT(instr)); u8 sa = ((instr >> 6) & 0x1f); s64 result = rt >> (sa + 32); regs.Write(RD(instr), result); } else { Util::panic("[JIT]: Implement non constant DSRA32!"); } } void JIT::dsrl(u32 instr) { if (regs.IsRegConstant(RT(instr))) { auto rt = regs.Read(RT(instr)); u8 sa = ((instr >> 6) & 0x1f); u64 result = rt >> sa; regs.Write(RD(instr), s64(result)); } else { Util::panic("[JIT]: Implement non constant DSRL!"); } } void JIT::dsrlv(u32 instr) { if (regs.IsRegConstant(RT(instr), RS(instr))) { u8 amount = regs.Read(RS(instr)) & 63; auto rt = regs.Read(RT(instr)); u64 result = rt >> amount; regs.Write(RD(instr), s64(result)); } else { Util::panic("[JIT]: Implement non constant DSRLV!"); } } void JIT::dsrl32(u32 instr) { if (regs.IsRegConstant(RT(instr))) { auto rt = regs.Read(RT(instr)); u8 sa = ((instr >> 6) & 0x1f); u64 result = rt >> (sa + 32); regs.Write(RD(instr), s64(result)); } else { Util::panic("[JIT]: Implement non constant DSRL32!"); } } void JIT::dsub(u32 instr) { if (regs.IsRegConstant(RT(instr), RS(instr))) { auto rt = regs.Read(RT(instr)); auto rs = regs.Read(RS(instr)); s64 result = rs - rt; if (check_signed_underflow(rs, rt, result)) { // regs.cop0.FireException(ExceptionCode::Overflow, 0, regs.oldPC); Util::panic("[JIT]: Unhandled Overflow exception in DSUB!"); } else { regs.Write(RD(instr), result); } } else { Util::panic("[JIT]: Implement non constant DSUB!"); } } void JIT::dsubu(u32 instr) { if (regs.IsRegConstant(RT(instr), RS(instr))) { auto rt = regs.Read(RT(instr)); auto rs = regs.Read(RS(instr)); s64 result = rs - rt; regs.Write(RD(instr), result); } else { Util::panic("[JIT]: Implement non constant DSUBU!"); } } void JIT::j(const u32 instr) { const s32 target = (instr & 0x3ffffff) << 2; const s64 address = (blockOldPC & ~0xfffffff) | target; branch_abs_constant(true, address); } void JIT::jr(const u32 instr) { if (regs.IsRegConstant(RS(instr))) { const u64 address = regs.Read(RS(instr)); branch_abs_constant(true, address); return; } regs.Read(RS(instr), code.rax); branch_abs(code.rax, mp); } void JIT::jal(const u32 instr) { regs.Write(31, blockNextPC); j(instr); } void JIT::jalr(const u32 instr) { regs.Write(RD(instr), blockNextPC); jr(instr); } void JIT::lbu(u32 instr) { if (regs.IsRegConstant(RS(instr))) { const u64 address = regs.Read(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 { code.mov(code.ARG2, code.ptr[code.rbp + (reinterpret_cast(®s) - reinterpret_cast(this))]); code.mov(code.ARG3, paddr); emitMemberFunctionCall(&Mem::Read, &mem); regs.Write(RT(instr), code.rax); } } else { Util::panic("[JIT]: Implement non constant LBU!"); } } void JIT::lb(u32 instr) { if (regs.IsRegConstant(RS(instr))) { const u64 address = regs.Read(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 (pc: 0x{:016X})!", blockPC); } else { code.mov(code.ARG2, code.ptr[code.rbp + (reinterpret_cast(®s) - reinterpret_cast(this))]); code.mov(code.ARG3, paddr); emitMemberFunctionCall(&Mem::Read, &mem); regs.Write(RT(instr), code.rax); } } else { Util::panic("[JIT]: Implement non constant LB!"); } } void JIT::ld(u32 instr) { if (regs.IsRegConstant(RS(instr))) { const s64 address = regs.Read(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 { code.mov(code.ARG2, code.ptr[code.rbp + (reinterpret_cast(®s) - reinterpret_cast(this))]); code.mov(code.ARG3, paddr); emitMemberFunctionCall(&Mem::Read, &mem); regs.Write(RT(instr), code.rax); } } else { Util::panic("[JIT]: Implement non constant LD!"); } } void JIT::ldc1(u32 instr) { if (regs.IsRegConstant(BASE(instr))) { const u64 addr = static_cast(static_cast(instr)) + regs.Read(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(regs, physical); regs.cop1.FGR_T(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(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 { Util::panic("[JIT]: Implement constant LDL!"); const s32 shift = 8 * ((address ^ 0) & 7); const u64 mask = 0xFFFFFFFFFFFFFFFF << shift; const u64 data = mem.Read(regs, paddr & ~7); const s64 result = (s64)((regs.Read(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(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(regs, paddr & ~7); const s64 result = (s64)((regs.Read(RT(instr)) & ~mask) | (data >> shift)); regs.Write(RT(instr), result); } } else { Util::panic("[JIT]: Implement non constant LDR!"); } } void JIT::lh(u32 instr) { if (regs.IsRegConstant(RS(instr))) { const u64 address = regs.Read(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!"); return; } 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 LH!"); return; } code.mov(code.ARG2, code.ptr[code.rbp + (reinterpret_cast(®s) - reinterpret_cast(this))]); code.mov(code.ARG3, paddr); emitMemberFunctionCall(&Mem::Read, &mem); regs.Write(RT(instr), code.rax); return; } Util::panic("[JIT]: Implement non constant LH!"); } void JIT::lhu(u32 instr) { u32 paddr; if (regs.IsRegConstant(RS(instr))) { const s64 address = regs.Read(RS(instr)) + (s16)instr; if (check_address_error(0b1, address)) { regs.cop0.HandleTLBException(address); regs.cop0.FireException(ExceptionCode::AddressErrorLoad, 0, regs.oldPC); return; } 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); return; } code.mov(code.ARG2, code.ptr[code.rbp + (reinterpret_cast(®s) - reinterpret_cast(this))]); code.mov(code.ARG3, paddr); emitMemberFunctionCall(&Mem::Read, &mem); regs.Write(RT(instr), code.rax); } code.mov(code.ARG2, Cop0::LOAD); regs.Read(RS(instr), code.ARG3); code.add(code.ARG3, s16(instr)); code.mov(code.ARG4, reinterpret_cast(&paddr)); emitMemberFunctionCall(&Cop0::MapVAddr, ®s.cop0); code.mov(code.ARG2, code.ptr[code.rbp + (reinterpret_cast(®s) - reinterpret_cast(this))]); code.mov(code.ARG3, code.qword[code.ARG4]); emitMemberFunctionCall(&Mem::Read, &mem); regs.Write(RT(instr), code.rax); } void JIT::ll(u32) { Util::panic("[JIT]: Implement constant LL!"); } void JIT::lld(u32) { Util::panic("[JIT]: Implement constant LLD!"); } void JIT::lw(u32 instr) { const s16 offset = instr; u32 paddr = 0; if (regs.IsRegConstant(RS(instr))) { const u64 address = regs.Read(RS(instr)) + offset; if (check_address_error(0b11, address)) { // regs.cop0.HandleTLBException(address); // regs.cop0.FireException(ExceptionCode::AddressErrorLoad, 0, regs.oldPC); // return; Util::panic("[JIT]: Unhandled ADEL exception in LW!"); return; } 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 LW!"); return; } code.mov(code.ARG2, code.ptr[code.rbp + (reinterpret_cast(®s) - reinterpret_cast(this))]); code.mov(code.ARG3, paddr); emitMemberFunctionCall(&Mem::Read, &mem); regs.Write(RT(instr), code.rax); return; } code.mov(code.ARG2, Cop0::LOAD); regs.Read(RS(instr), code.ARG3); code.add(code.ARG3, offset); code.mov(code.ARG4, reinterpret_cast(&paddr)); emitMemberFunctionCall(&Cop0::MapVAddr, ®s.cop0); code.mov(code.ARG2, code.ptr[code.rbp + (reinterpret_cast(®s) - reinterpret_cast(this))]); code.mov(code.ARG3, code.qword[code.ARG4]); emitMemberFunctionCall(&Mem::Read, &mem); regs.Write(RT(instr), code.rax); } void JIT::lwc1(u32) { Util::panic("[JIT]: Implement constant LWC1!"); } void JIT::lwl(u32) { Util::panic("[JIT]: Implement constant LWL!"); } void JIT::lwu(u32) { Util::panic("[JIT]: Implement constant LWU!"); } void JIT::lwr(u32) { Util::panic("[JIT]: Implement constant LWR!"); } void JIT::mfhi(u32 instr) { if (regs.GetHIConstant()) { regs.Write(RD(instr), regs.hi); } else { code.mov(code.rax, REG(qword, hi)); regs.Write(RD(instr), code.rax); } } void JIT::mflo(u32 instr) { if (regs.GetLOConstant()) { regs.Write(RD(instr), regs.lo); } else { code.mov(code.rax, REG(qword, lo)); regs.Write(RD(instr), code.rax); } } void JIT::mult(u32 instr) { if (regs.IsRegConstant(RT(instr), RS(instr))) { auto rt = regs.Read(RT(instr)); auto rs = regs.Read(RS(instr)); s64 result = (s64)rt * (s64)rs; regs.lo = (s64)((s32)result); regs.SetLOConstant(); regs.hi = (s64)((s32)(result >> 32)); regs.SetHIConstant(); } else { Util::panic("[JIT]: Implement non constant MULT!"); } } void JIT::multu(u32 instr) { if (regs.IsRegConstant(RT(instr), RS(instr))) { auto rt = regs.Read(RT(instr)); auto rs = regs.Read(RS(instr)); u64 result = (u64)rt * (u64)rs; regs.lo = (s64)((s32)result); regs.SetLOConstant(); regs.hi = (s64)((s32)(result >> 32)); regs.SetHIConstant(); } else { Util::panic("[JIT]: Implement non constant MULTU!"); } } void JIT::mthi(u32 instr) { if (regs.IsRegConstant(RS(instr))) { regs.hi = regs.Read(RS(instr)); regs.SetHIConstant(); } else { regs.Read(RS(instr), code.rax); code.mov(REG(qword, hi), code.rax); regs.UnsetHIConstant(); } } void JIT::mtlo(u32 instr) { if (regs.IsRegConstant(RS(instr))) { regs.lo = regs.Read(RS(instr)); regs.SetLOConstant(); } else { regs.Read(RS(instr), code.rax); code.mov(REG(qword, lo), code.rax); regs.UnsetLOConstant(); } } void JIT::nor(u32 instr) { if (regs.IsRegConstant(RS(instr), RT(instr))) { regs.Write(RD(instr), ~(regs.Read(RS(instr)) | regs.Read(RT(instr)))); } else { Util::panic("[JIT]: Implement non constant NOR!"); } } void JIT::slti(u32 instr) { if (regs.IsRegConstant(RS(instr))) { s16 imm = instr; regs.Write(RT(instr), regs.Read(RS(instr)) < imm); return; } Util::panic("[JIT]: Implement non constant SLTI!"); } void JIT::sltiu(u32 instr) { if (regs.IsRegConstant(RS(instr))) { s16 imm = instr; regs.Write(RT(instr), regs.Read(RS(instr)) < imm); } else { Util::panic("[JIT]: Implement non constant SLTIU!"); } } void JIT::slt(u32 instr) { if (regs.IsRegConstant(RS(instr), RT(instr))) { regs.Write(RD(instr), regs.Read(RS(instr)) < regs.Read(RT(instr))); return; } if (regs.IsRegConstant(RT(instr))) { s64 rt = regs.Read(RT(instr)); regs.Read(RS(instr), code.rax); code.cmp(code.rax, rt); code.setl(code.al); regs.Write(RD(instr), code.al); return; } if (regs.IsRegConstant(RS(instr))) { s64 rs = regs.Read(RS(instr)); regs.Read(RT(instr), code.rax); code.cmp(code.rax, rs); code.setge(code.al); regs.Write(RD(instr), code.al); return; } regs.Read(RS(instr), code.rax); regs.Read(RT(instr), code.rdx); code.cmp(code.rax, code.rdx); code.setl(code.al); regs.Write(RD(instr), code.al); } void JIT::sltu(u32 instr) { if (regs.IsRegConstant(RS(instr), RT(instr))) { regs.Write(RD(instr), regs.Read(RS(instr)) < regs.Read(RT(instr))); } else { Util::panic("[JIT]: Implement non constant SLT!"); } } void JIT::sll(u32 instr) { const u8 sa = ((instr >> 6) & 0x1f); if (regs.IsRegConstant(RT(instr))) { const s32 result = regs.Read(RT(instr)) << sa; regs.Write(RD(instr), (s64)result); } else { regs.Read(RT(instr), code.rax); code.sal(code.rax, sa); regs.Write(RD(instr), code.eax); } } void JIT::sllv(u32 instr) { if (regs.IsRegConstant(RS(instr), RT(instr))) { u8 sa = (regs.Read(RS(instr))) & 0x1F; u32 rt = regs.Read(RT(instr)); s32 result = rt << sa; regs.Write(RD(instr), (s64)result); } else { Util::panic("[JIT]: Implement non constant SLLV!"); } } void JIT::sub(u32 instr) { if (regs.IsRegConstant(RS(instr), RT(instr))) { s32 rt = regs.Read(RT(instr)); s32 rs = regs.Read(RS(instr)); s32 result = rs - rt; if (check_signed_underflow(rs, rt, result)) { regs.cop0.FireException(ExceptionCode::Overflow, 0, regs.oldPC); } else { regs.Write(RD(instr), result); } } else { Util::panic("[JIT]: Implement non constant SUB!"); } } void JIT::subu(u32 instr) { if (regs.IsRegConstant(RS(instr), RT(instr))) { u32 rt = regs.Read(RT(instr)); u32 rs = regs.Read(RS(instr)); u32 result = rs - rt; regs.Write(RD(instr), (s64)((s32)result)); } else { Util::panic("[JIT]: Implement non constant SUBU!"); } } void JIT::sra(u32 instr) { if (regs.IsRegConstant(RT(instr))) { s64 rt = regs.Read(RT(instr)); u8 sa = ((instr >> 6) & 0x1f); s32 result = rt >> sa; regs.Write(RD(instr), result); } else { Util::panic("[JIT]: Implement non constant SRA!"); } } void JIT::srav(u32 instr) { if (regs.IsRegConstant(RS(instr), RT(instr))) { s64 rs = regs.Read(RS(instr)); s64 rt = regs.Read(RT(instr)); u8 sa = rs & 0x1f; s32 result = rt >> sa; regs.Write(RD(instr), result); } else { Util::panic("[JIT]: Implement non constant SRAV!"); } } void JIT::srl(u32 instr) { if (regs.IsRegConstant(RT(instr))) { u32 rt = regs.Read(RT(instr)); u8 sa = ((instr >> 6) & 0x1f); u32 result = rt >> sa; regs.Write(RD(instr), (s32)result); } else { Util::panic("[JIT]: Implement non constant SRL!"); } } void JIT::sw(const u32 instr) { u32 physical; if (regs.IsRegConstant(RS(instr), RT(instr))) { const s16 offset = instr; const u64 address = regs.Read(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; } 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.lea(code.ARG2, code.ptr[code.rbp + (reinterpret_cast(®s) - reinterpret_cast(this))]); code.mov(code.ARG3, physical); regs.Read(RT(instr), code.ARG4); emitMemberFunctionCall(&Mem::WriteJIT, &mem); } return; } if (regs.IsRegConstant(RS(instr))) { const s16 offset = instr; const u64 address = regs.Read(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; } 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.ARG2, code.ptr[code.rbp + (reinterpret_cast(®s) - reinterpret_cast(this))]); code.mov(code.ARG3, physical); regs.Read(RT(instr), code.ARG4); emitMemberFunctionCall(&Mem::WriteJIT, &mem); } return; } if (regs.IsRegConstant(RT(instr))) { const s16 offset = instr; code.mov(code.ARG2, Cop0::STORE); regs.Read(RS(instr), code.ARG3); code.add(code.ARG3, offset); code.mov(code.ARG4, reinterpret_cast(&physical)); emitMemberFunctionCall(&Cop0::MapVAddr, ®s.cop0); code.mov(code.ARG2, code.ptr[code.rbp + (reinterpret_cast(®s) - reinterpret_cast(this))]); code.mov(code.ARG3, code.qword[code.ARG4]); regs.Read(RT(instr), code.ARG4); emitMemberFunctionCall(&Mem::WriteJIT, &mem); return; } const s16 offset = instr; code.mov(code.ARG2, Cop0::STORE); regs.Read(RS(instr), code.rdx); code.add(code.ARG3, offset); code.mov(code.ARG4, reinterpret_cast(&physical)); emitMemberFunctionCall(&Cop0::MapVAddr, ®s.cop0); code.mov(code.ARG2, code.ptr[code.rbp + (reinterpret_cast(®s) - reinterpret_cast(this))]); code.mov(code.ARG3, code.qword[code.ARG4]); regs.Read(RT(instr), code.ARG4); emitMemberFunctionCall(&Mem::WriteJIT, &mem); } void JIT::srlv(u32 instr) { if (regs.IsRegConstant(RS(instr), RT(instr))) { u8 sa = (regs.Read(RS(instr)) & 0x1F); u32 rt = regs.Read(RT(instr)); s32 result = rt >> sa; regs.Write(RD(instr), (s64)result); } else { Util::panic("[JIT]: Implement non constant SRLV!"); } } void JIT::or_(u32 instr) { if (regs.IsRegConstant(RS(instr), RT(instr))) { regs.Write(RD(instr), regs.Read(RS(instr)) | regs.Read(RT(instr))); } else { Util::panic("[JIT]: Implement non constant OR!"); } } void JIT::ori(u32 instr) { s64 imm = (u16)instr; if (regs.IsRegConstant(RS(instr))) { s64 result = imm | regs.Read(RS(instr)); regs.Write(RT(instr), result); return; } regs.Read(RS(instr), code.rax); code.or_(code.rax, imm); regs.Write(RT(instr), code.rax); } void JIT::xori(u32 instr) { if (regs.IsRegConstant(RS(instr))) { s64 imm = (u16)instr; regs.Write(RT(instr), regs.Read(RS(instr)) ^ imm); } else { Util::panic("[JIT]: Implement non constant XORI!"); } } void JIT::xor_(u32 instr) { if (regs.IsRegConstant(RS(instr), RT(instr))) { regs.Write(RD(instr), regs.Read(RT(instr)) ^ regs.Read(RS(instr))); } else { Util::panic("[JIT]: Implement non constant XOR!"); } } } // namespace n64