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