fpu improvements

This commit is contained in:
SimoneN64
2023-08-28 21:10:16 +02:00
parent 28d49812fa
commit 9de27272e9
10 changed files with 767 additions and 522 deletions

View File

@@ -6,7 +6,7 @@
namespace n64 { namespace n64 {
void JIT::add(u32 instr) { void JIT::add(u32 instr) {
if(likely(RD(instr) != 0)) { if (RD(instr) != 0) [[likely]] {
movsx(eax, dword[rdi + offsetof(Registers, gpr[RS(instr)])]); // rs movsx(eax, dword[rdi + offsetof(Registers, gpr[RS(instr)])]); // rs
movsx(ecx, dword[rdi + offsetof(Registers, gpr[RT(instr)])]); // rt movsx(ecx, dword[rdi + offsetof(Registers, gpr[RT(instr)])]); // rt
CodeGenerator::add(eax, ecx); CodeGenerator::add(eax, ecx);
@@ -19,7 +19,7 @@ void JIT::addu(u32 instr) {
} }
void JIT::addi(u32 instr) { void JIT::addi(u32 instr) {
if(likely(RT(instr) != 0)) { if (RT(instr) != 0) [[likely]] {
movsx(eax, dword[rdi + offsetof(Registers, gpr[RS(instr)])]); movsx(eax, dword[rdi + offsetof(Registers, gpr[RS(instr)])]);
mov(ecx, s32(s16(instr))); mov(ecx, s32(s16(instr)));
CodeGenerator::add(eax, ecx); CodeGenerator::add(eax, ecx);
@@ -32,7 +32,7 @@ void JIT::addiu(u32 instr) {
} }
void JIT::dadd(u32 instr) { void JIT::dadd(u32 instr) {
if(likely(RD(instr) != 0)) { if (RD(instr) != 0) [[likely]] {
mov(rax, GPR(RS(instr))); // rs mov(rax, GPR(RS(instr))); // rs
mov(rcx, GPR(RT(instr))); // rt mov(rcx, GPR(RT(instr))); // rt
CodeGenerator::add(rax, rcx); CodeGenerator::add(rax, rcx);
@@ -45,7 +45,7 @@ void JIT::daddu(u32 instr) {
} }
void JIT::daddi(u32 instr) { void JIT::daddi(u32 instr) {
if(likely(RT(instr) != 0)) { if (RT(instr) != 0) [[likely]] {
mov(rax, GPR(RS(instr))); mov(rax, GPR(RS(instr)));
mov(rcx, s64(s16(instr))); mov(rcx, s64(s16(instr)));
CodeGenerator::add(rax, rcx); CodeGenerator::add(rax, rcx);
@@ -665,7 +665,7 @@ void JIT::ori(u32 instr) {
} }
void JIT::or_(u32 instr) { void JIT::or_(u32 instr) {
if(likely(RD(instr) != 0)) { if (RD(instr) != 0) [[likely]] {
mov(rax, GPR(RS(instr))); mov(rax, GPR(RS(instr)));
mov(rcx, GPR(RT(instr))); mov(rcx, GPR(RT(instr)));
CodeGenerator::or_(rax, rcx); CodeGenerator::or_(rax, rcx);
@@ -674,7 +674,7 @@ void JIT::or_(u32 instr) {
} }
void JIT::nor(u32 instr) { void JIT::nor(u32 instr) {
if(likely(RD(instr) != 0)) { if (RD(instr) != 0) [[likely]] {
mov(rax, GPR(RS(instr))); mov(rax, GPR(RS(instr)));
mov(rcx, GPR(RT(instr))); mov(rcx, GPR(RT(instr)));
CodeGenerator::or_(rax, rcx); CodeGenerator::or_(rax, rcx);
@@ -702,7 +702,7 @@ void JIT::jalr(u32 instr) {
mov(rax, GPR(RS(instr))); mov(rax, GPR(RS(instr)));
mov(qword[rdi + offsetof(Registers, nextPC)], rax); mov(qword[rdi + offsetof(Registers, nextPC)], rax);
if(likely(RD(instr) != 0)) { if (RD(instr) != 0) [[likely]] {
mov(rax, qword[rdi + offsetof(Registers, pc)]); mov(rax, qword[rdi + offsetof(Registers, pc)]);
CodeGenerator::add(rax, 4); CodeGenerator::add(rax, 4);
mov(GPR(RD(instr)), rax); mov(GPR(RD(instr)), rax);
@@ -724,7 +724,7 @@ void JIT::sltiu(u32 instr) {
} }
void JIT::slt(u32 instr) { void JIT::slt(u32 instr) {
if(likely(RD(instr) != 0)) { if (RD(instr) != 0) [[likely]] {
mov(rax, GPR(RS(instr))); mov(rax, GPR(RS(instr)));
mov(rcx, GPR(RT(instr))); mov(rcx, GPR(RT(instr)));
cmp(rax, rcx); cmp(rax, rcx);
@@ -733,7 +733,7 @@ void JIT::slt(u32 instr) {
} }
void JIT::sltu(u32 instr) { void JIT::sltu(u32 instr) {
if(likely(RD(instr) != 0)) { if (RD(instr) != 0) [[likely]] {
mov(rax, GPR(RS(instr))); mov(rax, GPR(RS(instr)));
mov(rcx, GPR(RT(instr))); mov(rcx, GPR(RT(instr)));
cmp(rax, rcx); cmp(rax, rcx);
@@ -750,7 +750,7 @@ void JIT::xori(u32 instr) {
} }
void JIT::xor_(u32 instr) { void JIT::xor_(u32 instr) {
if(likely(RD(instr) != 0)) { if (RD(instr) != 0) [[likely]] {
mov(rax, GPR(RT(instr))); mov(rax, GPR(RT(instr)));
mov(rcx, GPR(RS(instr))); mov(rcx, GPR(RS(instr)));
CodeGenerator::xor_(rax, rcx); CodeGenerator::xor_(rax, rcx);
@@ -767,7 +767,7 @@ void JIT::andi(u32 instr) {
} }
void JIT::and_(u32 instr) { void JIT::and_(u32 instr) {
if(likely(RD(instr) != 0)) { if (RD(instr) != 0) [[likely]] {
mov(rax, GPR(RT(instr))); mov(rax, GPR(RT(instr)));
mov(rcx, GPR(RS(instr))); mov(rcx, GPR(RS(instr)));
CodeGenerator::and_(rax, rcx); CodeGenerator::and_(rax, rcx);
@@ -776,7 +776,7 @@ void JIT::and_(u32 instr) {
} }
void JIT::sll(u32 instr) { void JIT::sll(u32 instr) {
if(likely(RD(instr) != 0)) { if (RD(instr) != 0) [[likely]] {
u8 sa = ((instr >> 6) & 0x1f); u8 sa = ((instr >> 6) & 0x1f);
mov(rax, GPR(RT(instr))); mov(rax, GPR(RT(instr)));
sal(rax, sa); sal(rax, sa);
@@ -786,7 +786,7 @@ void JIT::sll(u32 instr) {
} }
void JIT::sllv(u32 instr) { void JIT::sllv(u32 instr) {
if(likely(RD(instr) != 0)) { if (RD(instr) != 0) [[likely]] {
mov(rcx, GPR(RS(instr))); mov(rcx, GPR(RS(instr)));
CodeGenerator::and_(cl, 0x1F); CodeGenerator::and_(cl, 0x1F);
mov(rax, GPR(RT(instr))); mov(rax, GPR(RT(instr)));
@@ -797,7 +797,7 @@ void JIT::sllv(u32 instr) {
} }
void JIT::dsll32(u32 instr) { void JIT::dsll32(u32 instr) {
if(likely(RD(instr) != 0)) { if (RD(instr) != 0) [[likely]] {
u8 sa = ((instr >> 6) & 0x1f) + 32; u8 sa = ((instr >> 6) & 0x1f) + 32;
mov(rax, GPR(RT(instr))); mov(rax, GPR(RT(instr)));
sal(rax, sa); sal(rax, sa);
@@ -806,7 +806,7 @@ void JIT::dsll32(u32 instr) {
} }
void JIT::dsll(u32 instr) { void JIT::dsll(u32 instr) {
if(likely(RD(instr) != 0)) { if (RD(instr) != 0) [[likely]] {
u8 sa = ((instr >> 6) & 0x1f); u8 sa = ((instr >> 6) & 0x1f);
mov(rax, GPR(RT(instr))); mov(rax, GPR(RT(instr)));
sal(rax, sa); sal(rax, sa);
@@ -815,7 +815,7 @@ void JIT::dsll(u32 instr) {
} }
void JIT::dsllv(u32 instr) { void JIT::dsllv(u32 instr) {
if(likely(RD(instr) != 0)) { if (RD(instr) != 0) [[likely]] {
mov(rcx, GPR(RS(instr))); mov(rcx, GPR(RS(instr)));
CodeGenerator::and_(cl, 63); CodeGenerator::and_(cl, 63);
mov(rax, GPR(RT(instr))); mov(rax, GPR(RT(instr)));
@@ -825,7 +825,7 @@ void JIT::dsllv(u32 instr) {
} }
void JIT::srl(u32 instr) { void JIT::srl(u32 instr) {
if(likely(RD(instr) != 0)) { if (RD(instr) != 0) [[likely]] {
u8 sa = ((instr >> 6) & 0x1f); u8 sa = ((instr >> 6) & 0x1f);
mov(rax, GPR(RT(instr))); mov(rax, GPR(RT(instr)));
CodeGenerator::shr(rax, sa); CodeGenerator::shr(rax, sa);
@@ -835,7 +835,7 @@ void JIT::srl(u32 instr) {
} }
void JIT::srlv(u32 instr) { void JIT::srlv(u32 instr) {
if(likely(RD(instr) != 0)) { if (RD(instr) != 0) [[likely]] {
mov(rcx, GPR(RS(instr))); mov(rcx, GPR(RS(instr)));
CodeGenerator::and_(cl, 0x1F); CodeGenerator::and_(cl, 0x1F);
mov(rax, GPR(RT(instr))); mov(rax, GPR(RT(instr)));
@@ -846,7 +846,7 @@ void JIT::srlv(u32 instr) {
} }
void JIT::dsrl(u32 instr) { void JIT::dsrl(u32 instr) {
if(likely(RD(instr) != 0)) { if (RD(instr) != 0) [[likely]] {
u8 sa = ((instr >> 6) & 0x1f); u8 sa = ((instr >> 6) & 0x1f);
mov(rax, GPR(RT(instr))); mov(rax, GPR(RT(instr)));
CodeGenerator::shr(rax, sa); CodeGenerator::shr(rax, sa);
@@ -855,7 +855,7 @@ void JIT::dsrl(u32 instr) {
} }
void JIT::dsrlv(u32 instr) { void JIT::dsrlv(u32 instr) {
if(likely(RD(instr) != 0)) { if (RD(instr) != 0) [[likely]] {
mov(rcx, GPR(RS(instr))); mov(rcx, GPR(RS(instr)));
CodeGenerator::and_(cl, 63); CodeGenerator::and_(cl, 63);
mov(rax, GPR(RT(instr))); mov(rax, GPR(RT(instr)));
@@ -865,7 +865,7 @@ void JIT::dsrlv(u32 instr) {
} }
void JIT::dsrl32(u32 instr) { void JIT::dsrl32(u32 instr) {
if(likely(RD(instr) != 0)) { if (RD(instr) != 0) [[likely]] {
u8 sa = ((instr >> 6) & 0x1f) + 32; u8 sa = ((instr >> 6) & 0x1f) + 32;
mov(rax, GPR(RT(instr))); mov(rax, GPR(RT(instr)));
CodeGenerator::shr(rax, sa); CodeGenerator::shr(rax, sa);
@@ -874,7 +874,7 @@ void JIT::dsrl32(u32 instr) {
} }
void JIT::sra(u32 instr) { void JIT::sra(u32 instr) {
if(likely(RD(instr) != 0)) { if (RD(instr) != 0) [[likely]] {
u8 sa = ((instr >> 6) & 0x1f); u8 sa = ((instr >> 6) & 0x1f);
mov(rax, GPR(RT(instr))); mov(rax, GPR(RT(instr)));
sar(rax, sa); sar(rax, sa);
@@ -884,7 +884,7 @@ void JIT::sra(u32 instr) {
} }
void JIT::srav(u32 instr) { void JIT::srav(u32 instr) {
if(likely(RD(instr) != 0)) { if (RD(instr) != 0) [[likely]] {
mov(rcx, GPR(RS(instr))); mov(rcx, GPR(RS(instr)));
CodeGenerator::and_(cl, 0x1F); CodeGenerator::and_(cl, 0x1F);
mov(rax, GPR(RT(instr))); mov(rax, GPR(RT(instr)));
@@ -895,7 +895,7 @@ void JIT::srav(u32 instr) {
} }
void JIT::dsra(u32 instr) { void JIT::dsra(u32 instr) {
if(likely(RD(instr) != 0)) { if (RD(instr) != 0) [[likely]] {
u8 sa = ((instr >> 6) & 0x1f); u8 sa = ((instr >> 6) & 0x1f);
mov(rax, GPR(RT(instr))); mov(rax, GPR(RT(instr)));
sar(rax, sa); sar(rax, sa);
@@ -904,7 +904,7 @@ void JIT::dsra(u32 instr) {
} }
void JIT::dsrav(u32 instr) { void JIT::dsrav(u32 instr) {
if(likely(RD(instr) != 0)) { if (RD(instr) != 0) [[likely]] {
mov(rcx, GPR(RS(instr))); mov(rcx, GPR(RS(instr)));
CodeGenerator::and_(cl, 63); CodeGenerator::and_(cl, 63);
mov(rax, GPR(RT(instr))); mov(rax, GPR(RT(instr)));
@@ -914,7 +914,7 @@ void JIT::dsrav(u32 instr) {
} }
void JIT::dsra32(u32 instr) { void JIT::dsra32(u32 instr) {
if(likely(RD(instr) != 0)) { if (RD(instr) != 0) [[likely]] {
u8 sa = ((instr >> 6) & 0x1f) + 32; u8 sa = ((instr >> 6) & 0x1f) + 32;
mov(rax, GPR(RT(instr))); mov(rax, GPR(RT(instr)));
sar(rax, sa); sar(rax, sa);
@@ -929,7 +929,7 @@ void JIT::jr(u32 instr) {
} }
void JIT::dsub(u32 instr) { void JIT::dsub(u32 instr) {
if(likely(RD(instr) != 0)) { if (RD(instr) != 0) [[likely]] {
mov(rax, GPR(RT(instr))); mov(rax, GPR(RT(instr)));
mov(rcx, GPR(RS(instr))); mov(rcx, GPR(RS(instr)));
CodeGenerator::sub(rcx, rax); CodeGenerator::sub(rcx, rax);
@@ -942,7 +942,7 @@ void JIT::dsubu(u32 instr) {
} }
void JIT::sub(u32 instr) { void JIT::sub(u32 instr) {
if(likely(RD(instr) != 0)) { if (RD(instr) != 0) [[likely]] {
mov(eax, GPR(RT(instr))); mov(eax, GPR(RT(instr)));
mov(ecx, GPR(RS(instr))); mov(ecx, GPR(RS(instr)));
CodeGenerator::sub(ecx, eax); CodeGenerator::sub(ecx, eax);
@@ -988,13 +988,13 @@ void JIT::mult(u32 instr) {
} }
void JIT::mflo(u32 instr) { void JIT::mflo(u32 instr) {
if(likely(RD(instr) != 0)) { if (RD(instr) != 0) [[likely]] {
regs.gpr[RD(instr)] = regs.lo; regs.gpr[RD(instr)] = regs.lo;
} }
} }
void JIT::mfhi(u32 instr) { void JIT::mfhi(u32 instr) {
if(likely(RD(instr) != 0)) { if (RD(instr) != 0) [[likely]] {
regs.gpr[RD(instr)] = regs.hi; regs.gpr[RD(instr)] = regs.hi;
} }
} }

View File

@@ -12,14 +12,14 @@ void Interpreter::add(u32 instr) {
if(check_signed_overflow(rs, rt, result)) { if(check_signed_overflow(rs, rt, result)) {
FireException(regs, ExceptionCode::Overflow, 0, true); FireException(regs, ExceptionCode::Overflow, 0, true);
} else { } else {
if(likely(RD(instr) != 0)) { if (RD(instr) != 0) [[likely]] {
regs.gpr[RD(instr)] = s32(result); regs.gpr[RD(instr)] = s32(result);
} }
} }
} }
void Interpreter::addu(u32 instr) { void Interpreter::addu(u32 instr) {
if(likely(RD(instr) != 0)) { if (RD(instr) != 0) [[likely]] {
s32 rs = (s32)regs.gpr[RS(instr)]; s32 rs = (s32)regs.gpr[RS(instr)];
s32 rt = (s32)regs.gpr[RT(instr)]; s32 rt = (s32)regs.gpr[RT(instr)];
s32 result = rs + rt; s32 result = rs + rt;
@@ -34,16 +34,20 @@ void Interpreter::addi(u32 instr) {
if(check_signed_overflow(rs, imm, result)) { if(check_signed_overflow(rs, imm, result)) {
FireException(regs, ExceptionCode::Overflow, 0, true); FireException(regs, ExceptionCode::Overflow, 0, true);
} else { } else {
if (RT(instr) != 0) [[likely]] {
regs.gpr[RT(instr)] = s32(result); regs.gpr[RT(instr)] = s32(result);
} }
} }
}
void Interpreter::addiu(u32 instr) { void Interpreter::addiu(u32 instr) {
s32 rs = (s32)regs.gpr[RS(instr)]; s32 rs = (s32)regs.gpr[RS(instr)];
s16 imm = (s16)(instr); s16 imm = (s16)(instr);
s32 result = rs + imm; s32 result = rs + imm;
if (RT(instr) != 0) [[likely]] {
regs.gpr[RT(instr)] = result; regs.gpr[RT(instr)] = result;
} }
}
void Interpreter::dadd(u32 instr) { void Interpreter::dadd(u32 instr) {
u64 rs = regs.gpr[RS(instr)]; u64 rs = regs.gpr[RS(instr)];
@@ -52,14 +56,14 @@ void Interpreter::dadd(u32 instr) {
if(check_signed_overflow(rs, rt, result)) { if(check_signed_overflow(rs, rt, result)) {
FireException(regs, ExceptionCode::Overflow, 0, true); FireException(regs, ExceptionCode::Overflow, 0, true);
} else { } else {
if(likely(RD(instr) != 0)) { if (RD(instr) != 0) [[likely]] {
regs.gpr[RD(instr)] = result; regs.gpr[RD(instr)] = result;
} }
} }
} }
void Interpreter::daddu(u32 instr) { void Interpreter::daddu(u32 instr) {
if(likely(RD(instr) != 0)) { if (RD(instr) != 0) [[likely]] {
s64 rs = regs.gpr[RS(instr)]; s64 rs = regs.gpr[RS(instr)];
s64 rt = regs.gpr[RT(instr)]; s64 rt = regs.gpr[RT(instr)];
regs.gpr[RD(instr)] = rs + rt; regs.gpr[RD(instr)] = rs + rt;
@@ -73,15 +77,19 @@ void Interpreter::daddi(u32 instr) {
if(check_signed_overflow(rs, imm, result)) { if(check_signed_overflow(rs, imm, result)) {
FireException(regs, ExceptionCode::Overflow, 0, true); FireException(regs, ExceptionCode::Overflow, 0, true);
} else { } else {
if (RT(instr) != 0) [[likely]] {
regs.gpr[RT(instr)] = result; regs.gpr[RT(instr)] = result;
} }
} }
}
void Interpreter::daddiu(u32 instr) { void Interpreter::daddiu(u32 instr) {
s16 imm = (s16)(instr); s16 imm = (s16)(instr);
s64 rs = regs.gpr[RS(instr)]; s64 rs = regs.gpr[RS(instr)];
if (RT(instr) != 0) [[likely]] {
regs.gpr[RT(instr)] = rs + imm; regs.gpr[RT(instr)] = rs + imm;
} }
}
void Interpreter::div(u32 instr) { void Interpreter::div(u32 instr) {
s64 dividend = (s32)regs.gpr[RS(instr)]; s64 dividend = (s32)regs.gpr[RS(instr)];
@@ -200,8 +208,10 @@ void Interpreter::bllink(u32 instr, bool cond) {
void Interpreter::lui(u32 instr) { void Interpreter::lui(u32 instr) {
u64 val = s64((s16)instr); u64 val = s64((s16)instr);
val <<= 16; val <<= 16;
if (RT(instr) != 0) [[likely]] {
regs.gpr[RT(instr)] = val; regs.gpr[RT(instr)] = val;
} }
}
void Interpreter::lb(u32 instr) { void Interpreter::lb(u32 instr) {
u64 address = regs.gpr[RS(instr)] + (s16)instr; u64 address = regs.gpr[RS(instr)] + (s16)instr;
@@ -210,13 +220,15 @@ void Interpreter::lb(u32 instr) {
HandleTLBException(regs, address); HandleTLBException(regs, address);
FireException(regs, GetTLBExceptionCode(regs.cop0.tlbError, LOAD), 0, true); FireException(regs, GetTLBExceptionCode(regs.cop0.tlbError, LOAD), 0, true);
} else { } else {
if (RT(instr) != 0) [[likely]] {
regs.gpr[RT(instr)] = (s8) mem.Read8(regs, paddr); regs.gpr[RT(instr)] = (s8) mem.Read8(regs, paddr);
} }
} }
}
void Interpreter::lh(u32 instr) { void Interpreter::lh(u32 instr) {
u64 address = regs.gpr[RS(instr)] + (s16)instr; u64 address = regs.gpr[RS(instr)] + (s16)instr;
if ((address & 0b1) > 0) { if (check_address_error(0b1, address)) {
HandleTLBException(regs, address); HandleTLBException(regs, address);
FireException(regs, ExceptionCode::AddressErrorLoad, 0, true); FireException(regs, ExceptionCode::AddressErrorLoad, 0, true);
return; return;
@@ -227,9 +239,11 @@ void Interpreter::lh(u32 instr) {
HandleTLBException(regs, address); HandleTLBException(regs, address);
FireException(regs, GetTLBExceptionCode(regs.cop0.tlbError, LOAD), 0, true); FireException(regs, GetTLBExceptionCode(regs.cop0.tlbError, LOAD), 0, true);
} else { } else {
if (RT(instr) != 0) [[likely]] {
regs.gpr[RT(instr)] = (s16) mem.Read16(regs, paddr); regs.gpr[RT(instr)] = (s16) mem.Read16(regs, paddr);
} }
} }
}
void Interpreter::lw(u32 instr) { void Interpreter::lw(u32 instr) {
s16 offset = instr; s16 offset = instr;
@@ -245,9 +259,11 @@ void Interpreter::lw(u32 instr) {
HandleTLBException(regs, address); HandleTLBException(regs, address);
FireException(regs, GetTLBExceptionCode(regs.cop0.tlbError, LOAD), 0, true); FireException(regs, GetTLBExceptionCode(regs.cop0.tlbError, LOAD), 0, true);
} else { } else {
if(RT(instr) != 0) [[likely]] {
regs.gpr[RT(instr)] = (s32) mem.Read32(regs, physical); regs.gpr[RT(instr)] = (s32) mem.Read32(regs, physical);
} }
} }
}
void Interpreter::ll(u32 instr) { void Interpreter::ll(u32 instr) {
u64 address = regs.gpr[RS(instr)] + (s16)instr; u64 address = regs.gpr[RS(instr)] + (s16)instr;
@@ -257,12 +273,14 @@ void Interpreter::ll(u32 instr) {
FireException(regs, GetTLBExceptionCode(regs.cop0.tlbError, LOAD), 0, true); FireException(regs, GetTLBExceptionCode(regs.cop0.tlbError, LOAD), 0, true);
} else { } else {
s32 result = mem.Read32(regs, physical); s32 result = mem.Read32(regs, physical);
if ((address & 0b11) > 0) { if (check_address_error(0b11, address)) {
FireException(regs, ExceptionCode::AddressErrorLoad, 0, true); FireException(regs, ExceptionCode::AddressErrorLoad, 0, true);
return; return;
} }
if (RT(instr) != 0) [[likely]] {
regs.gpr[RT(instr)] = result; regs.gpr[RT(instr)] = result;
}
regs.cop0.llbit = true; regs.cop0.llbit = true;
regs.cop0.LLAddr = physical >> 4; regs.cop0.LLAddr = physical >> 4;
@@ -280,9 +298,11 @@ void Interpreter::lwl(u32 instr) {
u32 mask = 0xFFFFFFFF << shift; u32 mask = 0xFFFFFFFF << shift;
u32 data = mem.Read32(regs, paddr & ~3); u32 data = mem.Read32(regs, paddr & ~3);
s32 result = s32((regs.gpr[RT(instr)] & ~mask) | (data << shift)); s32 result = s32((regs.gpr[RT(instr)] & ~mask) | (data << shift));
if (RT(instr) != 0) [[likely]] {
regs.gpr[RT(instr)] = result; regs.gpr[RT(instr)] = result;
} }
} }
}
void Interpreter::lwr(u32 instr) { void Interpreter::lwr(u32 instr) {
u64 address = regs.gpr[RS(instr)] + (s16)instr; u64 address = regs.gpr[RS(instr)] + (s16)instr;
@@ -295,9 +315,11 @@ void Interpreter::lwr(u32 instr) {
u32 mask = 0xFFFFFFFF >> shift; u32 mask = 0xFFFFFFFF >> shift;
u32 data = mem.Read32(regs, paddr & ~3); u32 data = mem.Read32(regs, paddr & ~3);
s32 result = s32((regs.gpr[RT(instr)] & ~mask) | (data >> shift)); s32 result = s32((regs.gpr[RT(instr)] & ~mask) | (data >> shift));
if (RT(instr) != 0) [[likely]] {
regs.gpr[RT(instr)] = result; regs.gpr[RT(instr)] = result;
} }
} }
}
void Interpreter::ld(u32 instr) { void Interpreter::ld(u32 instr) {
s64 address = regs.gpr[RS(instr)] + (s16)instr; s64 address = regs.gpr[RS(instr)] + (s16)instr;
@@ -313,9 +335,11 @@ void Interpreter::ld(u32 instr) {
FireException(regs, GetTLBExceptionCode(regs.cop0.tlbError, LOAD), 0, true); FireException(regs, GetTLBExceptionCode(regs.cop0.tlbError, LOAD), 0, true);
} else { } else {
s64 value = mem.Read64(regs, paddr); s64 value = mem.Read64(regs, paddr);
if (RT(instr) != 0) [[likely]] {
regs.gpr[RT(instr)] = value; regs.gpr[RT(instr)] = value;
} }
} }
}
void Interpreter::lld(u32 instr) { void Interpreter::lld(u32 instr) {
if (!regs.cop0.is_64bit_addressing && !regs.cop0.kernel_mode) { if (!regs.cop0.is_64bit_addressing && !regs.cop0.kernel_mode) {
@@ -329,10 +353,12 @@ void Interpreter::lld(u32 instr) {
HandleTLBException(regs, address); HandleTLBException(regs, address);
FireException(regs, GetTLBExceptionCode(regs.cop0.tlbError, LOAD), 0, true); FireException(regs, GetTLBExceptionCode(regs.cop0.tlbError, LOAD), 0, true);
} else { } else {
if ((address & 0b111) > 0) { if (check_address_error(0b111, address)) {
FireException(regs, ExceptionCode::AddressErrorLoad, 0, true); FireException(regs, ExceptionCode::AddressErrorLoad, 0, true);
} else { } else {
if (RT(instr) != 0) [[likely]] {
regs.gpr[RT(instr)] = mem.Read64(regs, paddr); regs.gpr[RT(instr)] = mem.Read64(regs, paddr);
}
regs.cop0.llbit = true; regs.cop0.llbit = true;
regs.cop0.LLAddr = paddr >> 4; regs.cop0.LLAddr = paddr >> 4;
} }
@@ -350,9 +376,11 @@ void Interpreter::ldl(u32 instr) {
u64 mask = 0xFFFFFFFFFFFFFFFF << shift; u64 mask = 0xFFFFFFFFFFFFFFFF << shift;
u64 data = mem.Read64(regs, paddr & ~7); u64 data = mem.Read64(regs, paddr & ~7);
s64 result = (s64) ((regs.gpr[RT(instr)] & ~mask) | (data << shift)); s64 result = (s64) ((regs.gpr[RT(instr)] & ~mask) | (data << shift));
if (RT(instr) != 0) [[likely]] {
regs.gpr[RT(instr)] = result; regs.gpr[RT(instr)] = result;
} }
} }
}
void Interpreter::ldr(u32 instr) { void Interpreter::ldr(u32 instr) {
u64 address = regs.gpr[RS(instr)] + (s16)instr; u64 address = regs.gpr[RS(instr)] + (s16)instr;
@@ -365,9 +393,11 @@ void Interpreter::ldr(u32 instr) {
u64 mask = 0xFFFFFFFFFFFFFFFF >> shift; u64 mask = 0xFFFFFFFFFFFFFFFF >> shift;
u64 data = mem.Read64(regs, paddr & ~7); u64 data = mem.Read64(regs, paddr & ~7);
s64 result = (s64) ((regs.gpr[RT(instr)] & ~mask) | (data >> shift)); s64 result = (s64) ((regs.gpr[RT(instr)] & ~mask) | (data >> shift));
if (RT(instr) != 0) [[likely]] {
regs.gpr[RT(instr)] = result; regs.gpr[RT(instr)] = result;
} }
} }
}
void Interpreter::lbu(u32 instr) { void Interpreter::lbu(u32 instr) {
u64 address = regs.gpr[RS(instr)] + (s16)instr; u64 address = regs.gpr[RS(instr)] + (s16)instr;
@@ -377,13 +407,15 @@ void Interpreter::lbu(u32 instr) {
FireException(regs, GetTLBExceptionCode(regs.cop0.tlbError, LOAD), 0, true); FireException(regs, GetTLBExceptionCode(regs.cop0.tlbError, LOAD), 0, true);
} else { } else {
u8 value = mem.Read8(regs, paddr); u8 value = mem.Read8(regs, paddr);
if (RT(instr) != 0) [[likely]] {
regs.gpr[RT(instr)] = value; regs.gpr[RT(instr)] = value;
} }
} }
}
void Interpreter::lhu(u32 instr) { void Interpreter::lhu(u32 instr) {
s64 address = regs.gpr[RS(instr)] + (s16)instr; s64 address = regs.gpr[RS(instr)] + (s16)instr;
if ((address & 0b1) > 0) { if (check_address_error(0b1, address)) {
HandleTLBException(regs, address); HandleTLBException(regs, address);
FireException(regs, ExceptionCode::AddressErrorLoad, 0, true); FireException(regs, ExceptionCode::AddressErrorLoad, 0, true);
return; return;
@@ -394,13 +426,15 @@ void Interpreter::lhu(u32 instr) {
FireException(regs, GetTLBExceptionCode(regs.cop0.tlbError, LOAD), 0, true); FireException(regs, GetTLBExceptionCode(regs.cop0.tlbError, LOAD), 0, true);
} else { } else {
u16 value = mem.Read16(regs, paddr); u16 value = mem.Read16(regs, paddr);
if (RT(instr) != 0) [[likely]] {
regs.gpr[RT(instr)] = value; regs.gpr[RT(instr)] = value;
} }
} }
}
void Interpreter::lwu(u32 instr) { void Interpreter::lwu(u32 instr) {
s64 address = regs.gpr[RS(instr)] + (s16)instr; s64 address = regs.gpr[RS(instr)] + (s16)instr;
if ((address & 0b11) > 0) { if (check_address_error(0b11, address)) {
HandleTLBException(regs, address); HandleTLBException(regs, address);
FireException(regs, ExceptionCode::AddressErrorLoad, 0, true); FireException(regs, ExceptionCode::AddressErrorLoad, 0, true);
return; return;
@@ -412,9 +446,11 @@ void Interpreter::lwu(u32 instr) {
FireException(regs, GetTLBExceptionCode(regs.cop0.tlbError, LOAD), 0, true); FireException(regs, GetTLBExceptionCode(regs.cop0.tlbError, LOAD), 0, true);
} else { } else {
u32 value = mem.Read32(regs, paddr); u32 value = mem.Read32(regs, paddr);
if (RT(instr) != 0) [[likely]] {
regs.gpr[RT(instr)] = value; regs.gpr[RT(instr)] = value;
} }
} }
}
void Interpreter::sb(u32 instr) { void Interpreter::sb(u32 instr) {
u64 address = regs.gpr[RS(instr)] + (s16)instr; u64 address = regs.gpr[RS(instr)] + (s16)instr;
@@ -430,7 +466,7 @@ void Interpreter::sb(u32 instr) {
void Interpreter::sc(u32 instr) { void Interpreter::sc(u32 instr) {
u64 address = regs.gpr[RS(instr)] + (s16)instr; u64 address = regs.gpr[RS(instr)] + (s16)instr;
if ((address & 0b11) > 0) { if (check_address_error(0b11, address)) {
FireException(regs, ExceptionCode::AddressErrorStore, 0, true); FireException(regs, ExceptionCode::AddressErrorStore, 0, true);
return; return;
} }
@@ -443,12 +479,16 @@ void Interpreter::sc(u32 instr) {
FireException(regs, GetTLBExceptionCode(regs.cop0.tlbError, STORE), 0, true); FireException(regs, GetTLBExceptionCode(regs.cop0.tlbError, STORE), 0, true);
} else { } else {
mem.Write32(regs, paddr, regs.gpr[RT(instr)]); mem.Write32(regs, paddr, regs.gpr[RT(instr)]);
if (RT(instr) != 0) [[likely]] {
regs.gpr[RT(instr)] = 1; regs.gpr[RT(instr)] = 1;
} }
}
} else { } else {
if (RT(instr) != 0) [[likely]] {
regs.gpr[RT(instr)] = 0; regs.gpr[RT(instr)] = 0;
} }
} }
}
void Interpreter::scd(u32 instr) { void Interpreter::scd(u32 instr) {
if (!regs.cop0.is_64bit_addressing && !regs.cop0.kernel_mode) { if (!regs.cop0.is_64bit_addressing && !regs.cop0.kernel_mode) {
@@ -457,7 +497,7 @@ void Interpreter::scd(u32 instr) {
} }
s64 address = regs.gpr[RS(instr)] + (s16)instr; s64 address = regs.gpr[RS(instr)] + (s16)instr;
if ((address & 0b111) > 0) { if (check_address_error(0b111, address)) {
HandleTLBException(regs, address); HandleTLBException(regs, address);
FireException(regs, ExceptionCode::AddressErrorStore, 0, true); FireException(regs, ExceptionCode::AddressErrorStore, 0, true);
return; return;
@@ -471,12 +511,16 @@ void Interpreter::scd(u32 instr) {
FireException(regs, GetTLBExceptionCode(regs.cop0.tlbError, STORE), 0, true); FireException(regs, GetTLBExceptionCode(regs.cop0.tlbError, STORE), 0, true);
} else { } else {
mem.Write32(regs, paddr, regs.gpr[RT(instr)]); mem.Write32(regs, paddr, regs.gpr[RT(instr)]);
if (RT(instr) != 0) [[likely]] {
regs.gpr[RT(instr)] = 1; regs.gpr[RT(instr)] = 1;
} }
}
} else { } else {
if (RT(instr) != 0) [[likely]] {
regs.gpr[RT(instr)] = 0; regs.gpr[RT(instr)] = 0;
} }
} }
}
void Interpreter::sh(u32 instr) { void Interpreter::sh(u32 instr) {
s64 address = regs.gpr[RS(instr)] + (s16)instr; s64 address = regs.gpr[RS(instr)] + (s16)instr;
@@ -588,17 +632,19 @@ void Interpreter::swr(u32 instr) {
void Interpreter::ori(u32 instr) { void Interpreter::ori(u32 instr) {
s64 imm = (u16)instr; s64 imm = (u16)instr;
s64 result = imm | regs.gpr[RS(instr)]; s64 result = imm | regs.gpr[RS(instr)];
if (RT(instr) != 0) [[likely]] {
regs.gpr[RT(instr)] = result; regs.gpr[RT(instr)] = result;
} }
}
void Interpreter::or_(u32 instr) { void Interpreter::or_(u32 instr) {
if(likely(RD(instr) != 0)) { if (RD(instr) != 0) [[likely]] {
regs.gpr[RD(instr)] = regs.gpr[RS(instr)] | regs.gpr[RT(instr)]; regs.gpr[RD(instr)] = regs.gpr[RS(instr)] | regs.gpr[RT(instr)];
} }
} }
void Interpreter::nor(u32 instr) { void Interpreter::nor(u32 instr) {
if(likely(RD(instr) != 0)) { if (RD(instr) != 0) [[likely]] {
regs.gpr[RD(instr)] = ~(regs.gpr[RS(instr)] | regs.gpr[RT(instr)]); regs.gpr[RD(instr)] = ~(regs.gpr[RS(instr)] | regs.gpr[RT(instr)]);
} }
} }
@@ -616,58 +662,71 @@ void Interpreter::jal(u32 instr) {
} }
void Interpreter::jalr(u32 instr) { void Interpreter::jalr(u32 instr) {
branch(true, regs.gpr[RS(instr)]); u64 addr = regs.gpr[RS(instr)];
if(likely(RD(instr) != 0)) { if(check_address_error(0b11, addr)) {
FireException(regs, ExceptionCode::AddressErrorLoad, 0, true);
} else {
branch(true, addr);
if (RD(instr) != 0) [[likely]] {
regs.gpr[RD(instr)] = regs.pc + 4; regs.gpr[RD(instr)] = regs.pc + 4;
} }
} }
}
void Interpreter::slti(u32 instr) { void Interpreter::slti(u32 instr) {
s16 imm = instr; s16 imm = instr;
if (RT(instr) != 0) [[likely]] {
regs.gpr[RT(instr)] = regs.gpr[RS(instr)] < imm; regs.gpr[RT(instr)] = regs.gpr[RS(instr)] < imm;
} }
}
void Interpreter::sltiu(u32 instr) { void Interpreter::sltiu(u32 instr) {
s16 imm = instr; s16 imm = instr;
if (RT(instr) != 0) [[likely]] {
regs.gpr[RT(instr)] = (u64) regs.gpr[RS(instr)] < imm; regs.gpr[RT(instr)] = (u64) regs.gpr[RS(instr)] < imm;
} }
}
void Interpreter::slt(u32 instr) { void Interpreter::slt(u32 instr) {
if(likely(RD(instr) != 0)) { if (RD(instr) != 0) [[likely]] {
regs.gpr[RD(instr)] = regs.gpr[RS(instr)] < regs.gpr[RT(instr)]; regs.gpr[RD(instr)] = regs.gpr[RS(instr)] < regs.gpr[RT(instr)];
} }
} }
void Interpreter::sltu(u32 instr) { void Interpreter::sltu(u32 instr) {
if(likely(RD(instr) != 0)) { if (RD(instr) != 0) [[likely]] {
regs.gpr[RD(instr)] = (u64) regs.gpr[RS(instr)] < (u64) regs.gpr[RT(instr)]; regs.gpr[RD(instr)] = (u64) regs.gpr[RS(instr)] < (u64) regs.gpr[RT(instr)];
} }
} }
void Interpreter::xori(u32 instr) { void Interpreter::xori(u32 instr) {
s64 imm = (u16)instr; s64 imm = (u16)instr;
if (RT(instr) != 0) [[likely]] {
regs.gpr[RT(instr)] = regs.gpr[RS(instr)] ^ imm; regs.gpr[RT(instr)] = regs.gpr[RS(instr)] ^ imm;
} }
}
void Interpreter::xor_(u32 instr) { void Interpreter::xor_(u32 instr) {
if(likely(RD(instr) != 0)) { if (RD(instr) != 0) [[likely]] {
regs.gpr[RD(instr)] = regs.gpr[RT(instr)] ^ regs.gpr[RS(instr)]; regs.gpr[RD(instr)] = regs.gpr[RT(instr)] ^ regs.gpr[RS(instr)];
} }
} }
void Interpreter::andi(u32 instr) { void Interpreter::andi(u32 instr) {
s64 imm = (u16)instr; s64 imm = (u16)instr;
if (RT(instr) != 0) [[likely]] {
regs.gpr[RT(instr)] = regs.gpr[RS(instr)] & imm; regs.gpr[RT(instr)] = regs.gpr[RS(instr)] & imm;
} }
}
void Interpreter::and_(u32 instr) { void Interpreter::and_(u32 instr) {
if(likely(RD(instr) != 0)) { if (RD(instr) != 0) [[likely]] {
regs.gpr[RD(instr)] = regs.gpr[RS(instr)] & regs.gpr[RT(instr)]; regs.gpr[RD(instr)] = regs.gpr[RS(instr)] & regs.gpr[RT(instr)];
} }
} }
void Interpreter::sll(u32 instr) { void Interpreter::sll(u32 instr) {
if(likely(RD(instr) != 0)) { if (RD(instr) != 0) [[likely]] {
u8 sa = ((instr >> 6) & 0x1f); u8 sa = ((instr >> 6) & 0x1f);
s32 result = regs.gpr[RT(instr)] << sa; s32 result = regs.gpr[RT(instr)] << sa;
regs.gpr[RD(instr)] = (s64) result; regs.gpr[RD(instr)] = (s64) result;
@@ -675,7 +734,7 @@ void Interpreter::sll(u32 instr) {
} }
void Interpreter::sllv(u32 instr) { void Interpreter::sllv(u32 instr) {
if(likely(RD(instr) != 0)) { if (RD(instr) != 0) [[likely]] {
u8 sa = (regs.gpr[RS(instr)]) & 0x1F; u8 sa = (regs.gpr[RS(instr)]) & 0x1F;
u32 rt = regs.gpr[RT(instr)]; u32 rt = regs.gpr[RT(instr)];
s32 result = rt << sa; s32 result = rt << sa;
@@ -684,7 +743,7 @@ void Interpreter::sllv(u32 instr) {
} }
void Interpreter::dsll32(u32 instr) { void Interpreter::dsll32(u32 instr) {
if(likely(RD(instr) != 0)) { if (RD(instr) != 0) [[likely]] {
u8 sa = ((instr >> 6) & 0x1f); u8 sa = ((instr >> 6) & 0x1f);
s64 result = regs.gpr[RT(instr)] << (sa + 32); s64 result = regs.gpr[RT(instr)] << (sa + 32);
regs.gpr[RD(instr)] = result; regs.gpr[RD(instr)] = result;
@@ -692,7 +751,7 @@ void Interpreter::dsll32(u32 instr) {
} }
void Interpreter::dsll(u32 instr) { void Interpreter::dsll(u32 instr) {
if(likely(RD(instr) != 0)) { if (RD(instr) != 0) [[likely]] {
u8 sa = ((instr >> 6) & 0x1f); u8 sa = ((instr >> 6) & 0x1f);
s64 result = regs.gpr[RT(instr)] << sa; s64 result = regs.gpr[RT(instr)] << sa;
regs.gpr[RD(instr)] = result; regs.gpr[RD(instr)] = result;
@@ -700,7 +759,7 @@ void Interpreter::dsll(u32 instr) {
} }
void Interpreter::dsllv(u32 instr) { void Interpreter::dsllv(u32 instr) {
if(likely(RD(instr) != 0)) { if (RD(instr) != 0) [[likely]] {
s64 sa = regs.gpr[RS(instr)] & 63; s64 sa = regs.gpr[RS(instr)] & 63;
s64 result = regs.gpr[RT(instr)] << sa; s64 result = regs.gpr[RT(instr)] << sa;
regs.gpr[RD(instr)] = result; regs.gpr[RD(instr)] = result;
@@ -708,7 +767,7 @@ void Interpreter::dsllv(u32 instr) {
} }
void Interpreter::srl(u32 instr) { void Interpreter::srl(u32 instr) {
if(likely(RD(instr) != 0)) { if (RD(instr) != 0) [[likely]] {
u32 rt = regs.gpr[RT(instr)]; u32 rt = regs.gpr[RT(instr)];
u8 sa = ((instr >> 6) & 0x1f); u8 sa = ((instr >> 6) & 0x1f);
u32 result = rt >> sa; u32 result = rt >> sa;
@@ -717,7 +776,7 @@ void Interpreter::srl(u32 instr) {
} }
void Interpreter::srlv(u32 instr) { void Interpreter::srlv(u32 instr) {
if(likely(RD(instr) != 0)) { if (RD(instr) != 0) [[likely]] {
u8 sa = (regs.gpr[RS(instr)] & 0x1F); u8 sa = (regs.gpr[RS(instr)] & 0x1F);
u32 rt = regs.gpr[RT(instr)]; u32 rt = regs.gpr[RT(instr)];
s32 result = rt >> sa; s32 result = rt >> sa;
@@ -726,7 +785,7 @@ void Interpreter::srlv(u32 instr) {
} }
void Interpreter::dsrl(u32 instr) { void Interpreter::dsrl(u32 instr) {
if(likely(RD(instr) != 0)) { if (RD(instr) != 0) [[likely]] {
u64 rt = regs.gpr[RT(instr)]; u64 rt = regs.gpr[RT(instr)];
u8 sa = ((instr >> 6) & 0x1f); u8 sa = ((instr >> 6) & 0x1f);
u64 result = rt >> sa; u64 result = rt >> sa;
@@ -735,7 +794,7 @@ void Interpreter::dsrl(u32 instr) {
} }
void Interpreter::dsrlv(u32 instr) { void Interpreter::dsrlv(u32 instr) {
if(likely(RD(instr) != 0)) { if (RD(instr) != 0) [[likely]] {
u8 amount = (regs.gpr[RS(instr)] & 63); u8 amount = (regs.gpr[RS(instr)] & 63);
u64 rt = regs.gpr[RT(instr)]; u64 rt = regs.gpr[RT(instr)];
u64 result = rt >> amount; u64 result = rt >> amount;
@@ -744,7 +803,7 @@ void Interpreter::dsrlv(u32 instr) {
} }
void Interpreter::dsrl32(u32 instr) { void Interpreter::dsrl32(u32 instr) {
if(likely(RD(instr) != 0)) { if (RD(instr) != 0) [[likely]] {
u64 rt = regs.gpr[RT(instr)]; u64 rt = regs.gpr[RT(instr)];
u8 sa = ((instr >> 6) & 0x1f); u8 sa = ((instr >> 6) & 0x1f);
u64 result = rt >> (sa + 32); u64 result = rt >> (sa + 32);
@@ -753,7 +812,7 @@ void Interpreter::dsrl32(u32 instr) {
} }
void Interpreter::sra(u32 instr) { void Interpreter::sra(u32 instr) {
if(likely(RD(instr) != 0)) { if (RD(instr) != 0) [[likely]] {
s64 rt = regs.gpr[RT(instr)]; s64 rt = regs.gpr[RT(instr)];
u8 sa = ((instr >> 6) & 0x1f); u8 sa = ((instr >> 6) & 0x1f);
s32 result = rt >> sa; s32 result = rt >> sa;
@@ -762,9 +821,9 @@ void Interpreter::sra(u32 instr) {
} }
void Interpreter::srav(u32 instr) { void Interpreter::srav(u32 instr) {
s64 rt = regs.gpr[RT(instr)]; if (RD(instr) != 0) [[likely]] {
if(likely(RD(instr) != 0)) {
s64 rs = regs.gpr[RS(instr)]; s64 rs = regs.gpr[RS(instr)];
s64 rt = regs.gpr[RT(instr)];
u8 sa = rs & 0x1f; u8 sa = rs & 0x1f;
s32 result = rt >> sa; s32 result = rt >> sa;
regs.gpr[RD(instr)] = result; regs.gpr[RD(instr)] = result;
@@ -772,7 +831,7 @@ void Interpreter::srav(u32 instr) {
} }
void Interpreter::dsra(u32 instr) { void Interpreter::dsra(u32 instr) {
if(likely(RD(instr) != 0)) { if (RD(instr) != 0) [[likely]] {
s64 rt = regs.gpr[RT(instr)]; s64 rt = regs.gpr[RT(instr)];
u8 sa = ((instr >> 6) & 0x1f); u8 sa = ((instr >> 6) & 0x1f);
s64 result = rt >> sa; s64 result = rt >> sa;
@@ -781,7 +840,7 @@ void Interpreter::dsra(u32 instr) {
} }
void Interpreter::dsrav(u32 instr) { void Interpreter::dsrav(u32 instr) {
if(likely(RD(instr) != 0)) { if (RD(instr) != 0) [[likely]] {
s64 rt = regs.gpr[RT(instr)]; s64 rt = regs.gpr[RT(instr)];
s64 rs = regs.gpr[RS(instr)]; s64 rs = regs.gpr[RS(instr)];
s64 sa = rs & 63; s64 sa = rs & 63;
@@ -791,7 +850,7 @@ void Interpreter::dsrav(u32 instr) {
} }
void Interpreter::dsra32(u32 instr) { void Interpreter::dsra32(u32 instr) {
if(likely(RD(instr) != 0)) { if (RD(instr) != 0) [[likely]] {
s64 rt = regs.gpr[RT(instr)]; s64 rt = regs.gpr[RT(instr)];
u8 sa = ((instr >> 6) & 0x1f); u8 sa = ((instr >> 6) & 0x1f);
s64 result = rt >> (sa + 32); s64 result = rt >> (sa + 32);
@@ -801,8 +860,12 @@ void Interpreter::dsra32(u32 instr) {
void Interpreter::jr(u32 instr) { void Interpreter::jr(u32 instr) {
s64 address = regs.gpr[RS(instr)]; s64 address = regs.gpr[RS(instr)];
if(check_address_error(0b11, address)) {
FireException(regs, ExceptionCode::AddressErrorLoad, 0, true);
} else {
branch(true, address); branch(true, address);
} }
}
void Interpreter::dsub(u32 instr) { void Interpreter::dsub(u32 instr) {
s64 rt = regs.gpr[RT(instr)]; s64 rt = regs.gpr[RT(instr)];
@@ -811,14 +874,14 @@ void Interpreter::dsub(u32 instr) {
if(check_signed_underflow(rs, rt, result)) { if(check_signed_underflow(rs, rt, result)) {
FireException(regs, ExceptionCode::Overflow, 0, true); FireException(regs, ExceptionCode::Overflow, 0, true);
} else { } else {
if(likely(RD(instr) != 0)) { if (RD(instr) != 0) [[likely]] {
regs.gpr[RD(instr)] = result; regs.gpr[RD(instr)] = result;
} }
} }
} }
void Interpreter::dsubu(u32 instr) { void Interpreter::dsubu(u32 instr) {
if(likely(RD(instr) != 0)) { if (RD(instr) != 0) [[likely]] {
u64 rt = regs.gpr[RT(instr)]; u64 rt = regs.gpr[RT(instr)];
u64 rs = regs.gpr[RS(instr)]; u64 rs = regs.gpr[RS(instr)];
u64 result = rs - rt; u64 result = rs - rt;
@@ -833,14 +896,14 @@ void Interpreter::sub(u32 instr) {
if(check_signed_underflow(rs, rt, result)) { if(check_signed_underflow(rs, rt, result)) {
FireException(regs, ExceptionCode::Overflow, 0, true); FireException(regs, ExceptionCode::Overflow, 0, true);
} else { } else {
if(likely(RD(instr) != 0)) { if (RD(instr) != 0) [[likely]] {
regs.gpr[RD(instr)] = result; regs.gpr[RD(instr)] = result;
} }
} }
} }
void Interpreter::subu(u32 instr) { void Interpreter::subu(u32 instr) {
if(likely(RD(instr) != 0)) { if (RD(instr) != 0) [[likely]] {
u32 rt = regs.gpr[RT(instr)]; u32 rt = regs.gpr[RT(instr)];
u32 rs = regs.gpr[RS(instr)]; u32 rs = regs.gpr[RS(instr)];
u32 result = rs - rt; u32 result = rs - rt;
@@ -881,13 +944,13 @@ void Interpreter::mult(u32 instr) {
} }
void Interpreter::mflo(u32 instr) { void Interpreter::mflo(u32 instr) {
if(likely(RD(instr) != 0)) { if (RD(instr) != 0) [[likely]] {
regs.gpr[RD(instr)] = regs.lo; regs.gpr[RD(instr)] = regs.lo;
} }
} }
void Interpreter::mfhi(u32 instr) { void Interpreter::mfhi(u32 instr) {
if(likely(RD(instr) != 0)) { if (RD(instr) != 0) [[likely]] {
regs.gpr[RD(instr)] = regs.hi; regs.gpr[RD(instr)] = regs.hi;
} }
} }
@@ -912,16 +975,20 @@ void Interpreter::mtc2(u32 instr) {
void Interpreter::mfc2(u32 instr) { void Interpreter::mfc2(u32 instr) {
s32 value = cop2Latch; s32 value = cop2Latch;
if (RT(instr) != 0) [[likely]] {
regs.gpr[RT(instr)] = value; regs.gpr[RT(instr)] = value;
} }
}
void Interpreter::dmtc2(u32 instr) { void Interpreter::dmtc2(u32 instr) {
cop2Latch = regs.gpr[RT(instr)]; cop2Latch = regs.gpr[RT(instr)];
} }
void Interpreter::dmfc2(u32 instr) { void Interpreter::dmfc2(u32 instr) {
if (RT(instr) != 0) [[likely]] {
regs.gpr[RT(instr)] = cop2Latch; regs.gpr[RT(instr)] = cop2Latch;
} }
}
void Interpreter::ctc2(u32) { void Interpreter::ctc2(u32) {

View File

@@ -254,7 +254,7 @@ void FireException(Registers& regs, ExceptionCode code, int cop, bool useOldPC)
bool old_exl = regs.cop0.status.exl; bool old_exl = regs.cop0.status.exl;
s64 pc = useOldPC ? regs.oldPC : regs.pc; s64 pc = useOldPC ? regs.oldPC : regs.pc;
if(!regs.cop0.status.exl) { if(!old_exl) {
if(regs.prevDelaySlot) { if(regs.prevDelaySlot) {
regs.cop0.cause.branchDelay = true; regs.cop0.cause.branchDelay = true;
pc -= 4; pc -= 4;

View File

@@ -171,20 +171,20 @@ enum TLBError : u8 {
}; };
enum class ExceptionCode : u8 { enum class ExceptionCode : u8 {
Interrupt, Interrupt = 0,
TLBModification, TLBModification = 1,
TLBLoad, TLBLoad = 2,
TLBStore, TLBStore = 3,
AddressErrorLoad, AddressErrorLoad = 4,
AddressErrorStore, AddressErrorStore = 5,
InstructionBusError, InstructionBusError = 6,
DataBusError, DataBusError = 7,
Syscall, Syscall = 8,
Breakpoint, Breakpoint = 9,
ReservedInstruction, ReservedInstruction = 10,
CoprocessorUnusable, CoprocessorUnusable = 11,
Overflow, Overflow = 12,
Trap, Trap = 13,
FloatingPointError = 15, FloatingPointError = 15,
Watch = 23 Watch = 23
}; };

View File

@@ -11,8 +11,8 @@ Cop1::Cop1() {
} }
void Cop1::Reset() { void Cop1::Reset() {
fcr0 = 0; fcr0 = 0xa00;
fcr31.raw = 0x01000800; fcr31.write(0x01000800);
memset(fgr, 0, 32 * sizeof(FGR)); memset(fgr, 0, 32 * sizeof(FGR));
} }
@@ -32,10 +32,6 @@ template void Cop1::decode<JIT>(JIT&, u32);
void Cop1::decodeInterp(Interpreter &cpu, u32 instr) { void Cop1::decodeInterp(Interpreter &cpu, u32 instr) {
Registers &regs = cpu.regs; Registers &regs = cpu.regs;
if(!regs.cop0.status.cu1) {
FireException(regs, ExceptionCode::CoprocessorUnusable, 1, true);
return;
}
u8 mask_sub = (instr >> 21) & 0x1F; u8 mask_sub = (instr >> 21) & 0x1F;
u8 mask_fun = instr & 0x3F; u8 mask_fun = instr & 0x3F;
@@ -45,11 +41,11 @@ void Cop1::decodeInterp(Interpreter &cpu, u32 instr) {
case 0x00: mfc1(regs, instr); break; case 0x00: mfc1(regs, instr); break;
case 0x01: dmfc1(regs, instr); break; case 0x01: dmfc1(regs, instr); break;
case 0x02: cfc1(regs, instr); break; case 0x02: cfc1(regs, instr); break;
case 0x03: FireException(regs, ExceptionCode::ReservedInstruction, 1, true); break; case 0x03: unimplemented(regs); break;
case 0x04: mtc1(regs, instr); break; case 0x04: mtc1(regs, instr); break;
case 0x05: dmtc1(regs, instr); break; case 0x05: dmtc1(regs, instr); break;
case 0x06: ctc1(regs, instr); break; case 0x06: ctc1(regs, instr); break;
case 0x07: FireException(regs, ExceptionCode::ReservedInstruction, 1, true); break; case 0x07: unimplemented(regs); break;
case 0x08: case 0x08:
switch(mask_branch) { switch(mask_branch) {
case 0: cpu.b(instr, !regs.cop1.fcr31.compare); break; case 0: cpu.b(instr, !regs.cop1.fcr31.compare); break;
@@ -77,9 +73,6 @@ void Cop1::decodeInterp(Interpreter &cpu, u32 instr) {
case 0x0D: truncws(regs, instr); break; case 0x0D: truncws(regs, instr); break;
case 0x0E: ceilws(regs, instr); break; case 0x0E: ceilws(regs, instr); break;
case 0x0F: floorws(regs, instr); break; case 0x0F: floorws(regs, instr); break;
case 0x20:
FireException(regs, ExceptionCode::ReservedInstruction, 1, true);
break;
case 0x21: cvtds(regs, instr); break; case 0x21: cvtds(regs, instr); break;
case 0x24: cvtws(regs, instr); break; case 0x24: cvtws(regs, instr); break;
case 0x25: cvtls(regs, instr); break; case 0x25: cvtls(regs, instr); break;
@@ -99,7 +92,7 @@ void Cop1::decodeInterp(Interpreter &cpu, u32 instr) {
case 0x3D: ccond<float>(regs, instr, NGE); break; case 0x3D: ccond<float>(regs, instr, NGE); break;
case 0x3E: ccond<float>(regs, instr, LE); break; case 0x3E: ccond<float>(regs, instr, LE); break;
case 0x3F: ccond<float>(regs, instr, NGT); break; case 0x3F: ccond<float>(regs, instr, NGT); break;
default: Util::panic("Unimplemented COP1 function S[{} {}] ({:08X}) ({:016X})", mask_fun >> 3, mask_fun & 7, instr, (u64)regs.oldPC); default: unimplemented(regs);
} }
break; break;
case 0x11: // d case 0x11: // d
@@ -121,9 +114,6 @@ void Cop1::decodeInterp(Interpreter &cpu, u32 instr) {
case 0x0E: ceilwd(regs, instr); break; case 0x0E: ceilwd(regs, instr); break;
case 0x0F: floorwd(regs, instr); break; case 0x0F: floorwd(regs, instr); break;
case 0x20: cvtsd(regs, instr); break; case 0x20: cvtsd(regs, instr); break;
case 0x21:
FireException(regs, ExceptionCode::ReservedInstruction, 1, true);
break;
case 0x24: cvtwd(regs, instr); break; case 0x24: cvtwd(regs, instr); break;
case 0x25: cvtld(regs, instr); break; case 0x25: cvtld(regs, instr); break;
case 0x30: ccond<double>(regs, instr, F); break; case 0x30: ccond<double>(regs, instr, F); break;
@@ -142,35 +132,21 @@ void Cop1::decodeInterp(Interpreter &cpu, u32 instr) {
case 0x3D: ccond<double>(regs, instr, NGE); break; case 0x3D: ccond<double>(regs, instr, NGE); break;
case 0x3E: ccond<double>(regs, instr, LE); break; case 0x3E: ccond<double>(regs, instr, LE); break;
case 0x3F: ccond<double>(regs, instr, NGT); break; case 0x3F: ccond<double>(regs, instr, NGT); break;
default: Util::panic("Unimplemented COP1 function D[{} {}] ({:08X}) ({:016X})", mask_fun >> 3, mask_fun & 7, instr, (u64)regs.oldPC); default: unimplemented(regs);
} }
break; break;
case 0x14: // w case 0x14: // w
switch(mask_fun) { switch(mask_fun) {
case 0x01: subw(regs, instr); break;
case 0x05: absw(regs, instr); break;
case 0x02: mulw(regs, instr); break;
case 0x06: movw(regs, instr); break;
case 0x20: cvtsw(regs, instr); break; case 0x20: cvtsw(regs, instr); break;
case 0x21: cvtdw(regs, instr); break; case 0x21: cvtdw(regs, instr); break;
case 0x24: default: unimplemented(regs);
FireException(regs, ExceptionCode::ReservedInstruction, 1, true);
break;
default: Util::panic("Unimplemented COP1 function W[{} {}] ({:08X}) ({:016X})", mask_fun >> 3, mask_fun & 7, instr, (u64)regs.oldPC);
} }
break; break;
case 0x15: // l case 0x15: // l
switch(mask_fun) { switch(mask_fun) {
case 0x01: subl(regs, instr); break;
case 0x05: absl(regs, instr); break;
case 0x02: mull(regs, instr); break;
case 0x06: movl(regs, instr); break;
case 0x20: cvtsl(regs, instr); break; case 0x20: cvtsl(regs, instr); break;
case 0x21: cvtdl(regs, instr); break; case 0x21: cvtdl(regs, instr); break;
case 0x24: case 0x25: default: unimplemented(regs);
FireException(regs, ExceptionCode::ReservedInstruction, 1, true);
break;
default: Util::panic("Unimplemented COP1 function L[{} {}] ({:08X}) ({:016X})", mask_fun >> 3, mask_fun & 7, instr, (u64)regs.oldPC);
} }
break; break;
default: Util::panic("Unimplemented COP1 instruction {} {}", mask_sub >> 3, mask_sub & 7); default: Util::panic("Unimplemented COP1 instruction {} {}", mask_sub >> 3, mask_sub & 7);
@@ -197,11 +173,11 @@ void Cop1::decodeJIT(JIT &cpu, u32 instr) {
case 0x00: mfc1(regs, instr); break; case 0x00: mfc1(regs, instr); break;
case 0x01: dmfc1(regs, instr); break; case 0x01: dmfc1(regs, instr); break;
case 0x02: cfc1(regs, instr); break; case 0x02: cfc1(regs, instr); break;
case 0x03: FireException(regs, ExceptionCode::ReservedInstruction, 1, true); break; case 0x03: unimplemented(regs); break;
case 0x04: mtc1(regs, instr); break; case 0x04: mtc1(regs, instr); break;
case 0x05: dmtc1(regs, instr); break; case 0x05: dmtc1(regs, instr); break;
case 0x06: ctc1(regs, instr); break; case 0x06: ctc1(regs, instr); break;
case 0x07: FireException(regs, ExceptionCode::ReservedInstruction, 1, true); break; case 0x07: unimplemented(regs); break;
case 0x08: case 0x08:
/*switch(mask_branch) { /*switch(mask_branch) {
case 0: { case 0: {
@@ -239,7 +215,7 @@ void Cop1::decodeJIT(JIT &cpu, u32 instr) {
case 0x0E: ceilws(regs, instr); break; case 0x0E: ceilws(regs, instr); break;
case 0x0F: floorws(regs, instr); break; case 0x0F: floorws(regs, instr); break;
case 0x20: case 0x20:
FireException(regs, ExceptionCode::ReservedInstruction, 1, true); unimplemented(regs);
break; break;
case 0x21: cvtds(regs, instr); break; case 0x21: cvtds(regs, instr); break;
case 0x24: cvtws(regs, instr); break; case 0x24: cvtws(regs, instr); break;
@@ -283,7 +259,7 @@ void Cop1::decodeJIT(JIT &cpu, u32 instr) {
case 0x0F: floorwd(regs, instr); break; case 0x0F: floorwd(regs, instr); break;
case 0x20: cvtsd(regs, instr); break; case 0x20: cvtsd(regs, instr); break;
case 0x21: case 0x21:
FireException(regs, ExceptionCode::ReservedInstruction, 1, true); unimplemented(regs);
break; break;
case 0x24: cvtwd(regs, instr); break; case 0x24: cvtwd(regs, instr); break;
case 0x25: cvtld(regs, instr); break; case 0x25: cvtld(regs, instr); break;
@@ -308,28 +284,20 @@ void Cop1::decodeJIT(JIT &cpu, u32 instr) {
break; break;
case 0x14: // w case 0x14: // w
switch(mask_fun) { switch(mask_fun) {
case 0x01: subw(regs, instr); break;
case 0x05: absw(regs, instr); break;
case 0x02: mulw(regs, instr); break;
case 0x06: movw(regs, instr); break;
case 0x20: cvtsw(regs, instr); break; case 0x20: cvtsw(regs, instr); break;
case 0x21: cvtdw(regs, instr); break; case 0x21: cvtdw(regs, instr); break;
case 0x24: case 0x24:
FireException(regs, ExceptionCode::ReservedInstruction, 1, true); unimplemented(regs);
break; break;
default: Util::panic("Unimplemented COP1 function W[{} {}] ({:08X}) ({:016X})", mask_fun >> 3, mask_fun & 7, instr, (u64)regs.oldPC); default: Util::panic("Unimplemented COP1 function W[{} {}] ({:08X}) ({:016X})", mask_fun >> 3, mask_fun & 7, instr, (u64)regs.oldPC);
} }
break; break;
case 0x15: // l case 0x15: // l
switch(mask_fun) { switch(mask_fun) {
case 0x01: subl(regs, instr); break;
case 0x05: absl(regs, instr); break;
case 0x02: mull(regs, instr); break;
case 0x06: movl(regs, instr); break;
case 0x20: cvtsl(regs, instr); break; case 0x20: cvtsl(regs, instr); break;
case 0x21: cvtdl(regs, instr); break; case 0x21: cvtdl(regs, instr); break;
case 0x24: case 0x25: case 0x24: case 0x25:
FireException(regs, ExceptionCode::ReservedInstruction, 1, true); unimplemented(regs);
break; break;
default: Util::panic("Unimplemented COP1 function L[{} {}] ({:08X}) ({:016X})", mask_fun >> 3, mask_fun & 7, instr, (u64)regs.oldPC); default: Util::panic("Unimplemented COP1 function L[{} {}] ({:08X}) ({:016X})", mask_fun >> 3, mask_fun & 7, instr, (u64)regs.oldPC);
} }

View File

@@ -36,7 +36,18 @@ union FCR31 {
unsigned:14; unsigned:14;
} __attribute__((__packed__)); } __attribute__((__packed__));
u32 raw; u32 read() const {
return (fs << 24) | (compare << 23) | (cause << 12) | (enable << 7) | (flag << 2) | rounding_mode;
}
void write(u32 val) {
fs = (val & 0x01000000) >> 24;
compare = (val & 0x00800000) >> 23;
cause = (val & 0x0003f000) >> 12;
enable = (val & 0x00000f80) >> 7;
flag = (val & 0x0000007c) >> 2;
rounding_mode = val & 3;
}
}; };
enum CompConds { enum CompConds {
@@ -71,91 +82,132 @@ struct Cop1 {
friend struct JIT; friend struct JIT;
template<typename T> template<typename T>
FORCE_INLINE void SetReg(Cop0& cop0, u8 index, T value) { FORCE_INLINE T GetFGR_FR(Cop0& cop0, u8 r) {
if constexpr(sizeof(T) == 4) { if constexpr (std::is_same_v<T, u32> || std::is_same_v<T, s32>) {
if (cop0.status.fr) { if (cop0.status.fr) {
fgr[index].lo = value; return fgr[r].lo;
} else { } else {
if (index & 1) { if (r & 1) {
fgr[index & ~1].hi = value; return fgr[r & ~1].hi;
} else { } else {
fgr[index].lo = value; return fgr[r].lo;
} }
} }
} else if constexpr(sizeof(T) == 8) { } else if constexpr (std::is_same_v<T, u64> || std::is_same_v<T, s64>) {
if (!cop0.status.fr) { if (!cop0.status.fr) {
index &= ~1; // When this bit is not set, accessing odd registers is not allowed.
r &= ~1;
} }
fgr[index].raw = value; return fgr[r].raw;
} }
} }
template<typename T> template<typename T>
FORCE_INLINE T GetReg(Cop0& cop0, u8 index) { FORCE_INLINE void SetFGR_FR(Cop0& cop0, u8 r, T value) {
if constexpr(sizeof(T) == 4) { if constexpr (std::is_same_v<T, u32> || std::is_same_v<T, s32>) {
if (cop0.status.fr) { if (cop0.status.fr) {
return fgr[index].lo; fgr[r].lo = value;
} else { } else {
if (index & 1) { if (r & 1) {
return fgr[index & ~1].hi; fgr[r & ~1].hi = value;
} else { } else {
return fgr[index].lo; fgr[r].lo = value;
} }
} }
} else if constexpr(sizeof(T) == 8) { } else if constexpr (std::is_same_v<T, u64> || std::is_same_v<T, s64>) {
if (!cop0.status.fr) { if (!cop0.status.fr) {
index &= ~1; // When this bit is not set, accessing odd registers is not allowed.
r &= ~1;
} }
return fgr[index].raw; fgr[r].raw = value;
} }
} }
template<typename T> template<typename T>
FORCE_INLINE void SetCop1Reg(Cop0& cop0, u8 index, T value) { FORCE_INLINE void SetFGR(u8 r, T value) {
if constexpr (sizeof(T) == 4) { fgr[r].raw = value;
u32 raw; }
memcpy(&raw, &value, sizeof(T));
SetReg<u32>(cop0, index, raw); template<typename T>
} else if constexpr (sizeof(T) == 8) { FORCE_INLINE u64 GetFGR(u8 r) {
u64 raw; if constexpr (std::is_same_v<T, u32> || std::is_same_v<T, s32>) {
memcpy(&raw, &value, sizeof(T)); return fgr[r].lo;
SetReg<u64>(cop0, index, raw); } else if constexpr (std::is_same_v<T, u64> || std::is_same_v<T, s64>) {
return fgr[r].raw;
} }
} }
template <typename T> template <typename T>
FORCE_INLINE T GetCop1Reg(Cop0& cop0, u8 index) { FORCE_INLINE T GetFGR_FS(Cop0& cop0, u8 fs) {
T value; if constexpr (std::is_same_v<T, u32> || std::is_same_v<T, s32>) {
if constexpr (sizeof(T) == 4) { if (!cop0.status.fr) {
u32 raw = GetReg<u32>(cop0, index); fs &= ~1;
memcpy(&value, &raw, sizeof(T));
} else if constexpr (sizeof(T) == 8) {
u64 raw = GetReg<u64>(cop0, index);
memcpy(&value, &raw, sizeof(T));
} }
return value; return fgr[fs].lo;
} else if constexpr (std::is_same_v<T, float> || std::is_same_v<T, double>) {
if (!cop0.status.fr) {
fs &= ~1;
}
return GetFGR_Raw<T>(fs);
}
}
template <typename T>
FORCE_INLINE T GetFGR_Raw(u8 r) {
if constexpr (std::is_same_v<T, float>) {
static_assert(sizeof(float) == sizeof(u32), "float and u32 need to both be 32 bits for this to work.");
auto rawvalue = GetFGR<u32>(r);
float floatvalue;
memcpy(&floatvalue, &rawvalue, sizeof(float));
return floatvalue;
} else if constexpr (std::is_same_v<T, double>) {
static_assert(sizeof(double) == sizeof(u64), "double and u64 need to both be 64 bits for this to work.");
double doublevalue;
auto rawvalue = GetFGR<u64>(r);
memcpy(&doublevalue, &rawvalue, sizeof(double));
return doublevalue;
}
}
template <typename T>
FORCE_INLINE void SetFGR_Raw(u8 r, T val) {
if constexpr (std::is_same_v<T, float>) {
static_assert(sizeof(float) == sizeof(u32), "float and u32 need to both be 32 bits for this to work.");
u32 rawvalue;
memcpy(&rawvalue, &val, sizeof(float));
SetFGR<u32>(r, rawvalue);
} else if constexpr (std::is_same_v<T, double>) {
static_assert(sizeof(double) == sizeof(u64), "double and u64 need to both be 64 bits for this to work.");
u64 rawvalue;
memcpy(&rawvalue, &val, sizeof(double));
SetFGR<u64>(r, rawvalue);
}
}
template <typename T>
FORCE_INLINE T GetFGR_FT(u8 ft) {
return GetFGR_Raw<T>(ft);
} }
private: private:
void decodeInterp(Interpreter&, u32); void decodeInterp(Interpreter&, u32);
void decodeJIT(JIT&, u32); void decodeJIT(JIT&, u32);
void absd(Registers&, u32 instr); void absd(Registers&, u32 instr);
void abss(Registers&, u32 instr); void abss(Registers&, u32 instr);
void absw(Registers&, u32 instr);
void absl(Registers&, u32 instr);
void adds(Registers&, u32 instr); void adds(Registers&, u32 instr);
void addd(Registers&, u32 instr); void addd(Registers&, u32 instr);
void subs(Registers&, u32 instr); void subs(Registers&, u32 instr);
void subd(Registers&, u32 instr); void subd(Registers&, u32 instr);
void subw(Registers&, u32 instr);
void subl(Registers&, u32 instr);
void ceills(Registers&, u32 instr); void ceills(Registers&, u32 instr);
void ceilws(Registers&, u32 instr); void ceilws(Registers&, u32 instr);
void ceilld(Registers&, u32 instr); void ceilld(Registers&, u32 instr);
void ceilwd(Registers&, u32 instr); void ceilwd(Registers&, u32 instr);
void cfc1(Registers&, u32 instr) const; void cfc1(Registers&, u32 instr) const;
void ctc1(Registers&, u32 instr); void ctc1(Registers&, u32 instr);
void unimplemented(Registers&);
void roundls(Registers&, u32 instr); void roundls(Registers&, u32 instr);
void roundld(Registers&, u32 instr); void roundld(Registers&, u32 instr);
void roundws(Registers&, u32 instr); void roundws(Registers&, u32 instr);
@@ -180,12 +232,8 @@ private:
void divd(Registers&, u32 instr); void divd(Registers&, u32 instr);
void muls(Registers&, u32 instr); void muls(Registers&, u32 instr);
void muld(Registers&, u32 instr); void muld(Registers&, u32 instr);
void mulw(Registers&, u32 instr);
void mull(Registers&, u32 instr);
void movs(Registers&, u32 instr); void movs(Registers&, u32 instr);
void movd(Registers&, u32 instr); void movd(Registers&, u32 instr);
void movw(Registers&, u32 instr);
void movl(Registers&, u32 instr);
void negs(Registers&, u32 instr); void negs(Registers&, u32 instr);
void negd(Registers&, u32 instr); void negd(Registers&, u32 instr);
void sqrts(Registers&, u32 instr); void sqrts(Registers&, u32 instr);

View File

@@ -7,6 +7,21 @@
#include <cfenv> #include <cfenv>
namespace n64 { namespace n64 {
FORCE_INLINE bool FireFPUException(Registers& regs) {
FCR31& fcr31 = regs.cop1.fcr31;
if(fcr31.cause & ((1 << 5) | fcr31.enable)) {
FireException(regs, ExceptionCode::FloatingPointError, 0, true);
return true;
}
return false;
}
#define CheckFPUException() do { if(FireFPUException(regs)) { return; } } while(0)
#define CheckFPUUsable_PreserveCause() do { if(!regs.cop0.status.cu1) { FireException(regs, ExceptionCode::CoprocessorUnusable, 1, true); return; } } while(0)
#define CheckFPUUsable() do { CheckFPUUsable_PreserveCause(); regs.cop1.fcr31.cause = 0; } while(0)
#define CheckRound(a, b) do { if ((a) != (b)) { fcr31.cause_inexact_operation = true; if(!fcr31.enable_inexact_operation) { fcr31.flag_inexact_operation = true; } } CheckFPUException(); } while(0);
FORCE_INLINE int PushRoundingMode(const FCR31& fcr31) { FORCE_INLINE int PushRoundingMode(const FCR31& fcr31) {
int og = fegetround(); int og = fegetround();
switch(fcr31.rounding_mode) { switch(fcr31.rounding_mode) {
@@ -19,85 +34,265 @@ FORCE_INLINE int PushRoundingMode(const FCR31& fcr31) {
return og; return og;
} }
#define CheckCVTArg(f) do { SetCauseByArgCVT(regs, f); CheckFPUException(); } while(0)
#define CheckArg(f) do { SetCauseByArg(regs, f); CheckFPUException(); } while(0)
#define PUSHROUNDING int orig_round = PushRoundingMode(regs.cop1.fcr31)
#define POPROUNDING fesetround(orig_round)
#define OP_CheckExcept(op) do { PUSHROUNDING; feclearexcept(FE_ALL_EXCEPT); op; SetFPUCauseRaised(regs, fetestexcept(FE_ALL_EXCEPT)); POPROUNDING; } while(0)
#define CVT_OP_CheckExcept(op) do { feclearexcept(FE_ALL_EXCEPT); op; SetFPUCauseCVTRaised(regs, fetestexcept(FE_ALL_EXCEPT)); CheckFPUException(); } while(0)
#define OP(T, op) do { \
CheckFPUUsable(); \
auto fs = GetFGR_FS<T>(regs.cop0, FS(instr)); \
auto ft = GetFGR_FT<T>(FT(instr)); \
CheckArg(fs); \
CheckArg(ft); \
T result; \
OP_CheckExcept({result = (op);}); \
SetFGR_Raw<T>(FD(instr), result); \
} while(0)
template <typename T>
FORCE_INLINE void SetCauseByArgCVT(Registers& regs, T f) {
if constexpr(sizeof(T) == 4) {
switch (std::fpclassify(f)) {
case FP_NAN:
case FP_INFINITE:
case FP_SUBNORMAL:
regs.cop1.fcr31.cause_unimplemented_operation = true;
break;
case FP_NORMAL:
// Check overflow
if (f >= 2147483648.0 || f < -2147483648.0) {
regs.cop1.fcr31.cause_unimplemented_operation = true;
}
break;
case FP_ZERO:
break; // Fine
}
} else if constexpr(sizeof(T) == 8) {
switch (std::fpclassify(f)) {
case FP_NAN:
case FP_INFINITE:
case FP_SUBNORMAL:
regs.cop1.fcr31.cause_unimplemented_operation = true;
break;
case FP_NORMAL:
// Check overflow
if (f >= 9007199254740992.000000 || f <= -9007199254740992.000000) {
regs.cop1.fcr31.cause_unimplemented_operation = true;
}
break;
case FP_ZERO:
break; // Fine
}
}
}
FORCE_INLINE void SetFPUCauseRaised(Registers& regs, int raised) {
if (raised == 0) {
return;
}
if (raised & FE_UNDERFLOW) {
if (!regs.cop1.fcr31.fs || regs.cop1.fcr31.enable_underflow || regs.cop1.fcr31.enable_inexact_operation) {
regs.cop1.fcr31.cause_unimplemented_operation = true;
return;
} else {
regs.cop1.fcr31.cause_underflow = true;
if(!regs.cop1.fcr31.enable_underflow) {
regs.cop1.fcr31.flag_underflow = true;
}
}
}
if (raised & FE_INEXACT) {
regs.cop1.fcr31.cause_inexact_operation = true;
if(!regs.cop1.fcr31.enable_inexact_operation) {
regs.cop1.fcr31.flag_inexact_operation = true;
}
}
if (raised & FE_DIVBYZERO) {
regs.cop1.fcr31.cause_division_by_zero = true;
if(!regs.cop1.fcr31.enable_division_by_zero) {
regs.cop1.fcr31.flag_division_by_zero = true;
}
}
if (raised & FE_OVERFLOW) {
regs.cop1.fcr31.cause_overflow = true;
if(!regs.cop1.fcr31.enable_overflow) {
regs.cop1.fcr31.flag_overflow = true;
}
}
if (raised & FE_INVALID) {
regs.cop1.fcr31.cause_invalid_operation = true;
if(!regs.cop1.fcr31.enable_invalid_operation) {
regs.cop1.fcr31.flag_invalid_operation = true;
}
}
}
FORCE_INLINE void SetFPUCauseCVTRaised(Registers& regs, int raised) {
if(raised & FE_INVALID) {
regs.cop1.fcr31.cause_unimplemented_operation = true;
return;
}
SetFPUCauseRaised(regs, raised);
}
template <typename T>
FORCE_INLINE void SetCauseByArg(Registers& regs, T f) {
int c = std::fpclassify(f);
switch(c) {
case FP_NAN:
if(isqnan(f)) {
regs.cop1.fcr31.cause_invalid_operation = true;
if(!regs.cop1.fcr31.enable_invalid_operation) {
regs.cop1.fcr31.flag_invalid_operation = true;
}
} else {
regs.cop1.fcr31.cause_unimplemented_operation = true;
}
break;
case FP_SUBNORMAL:
regs.cop1.fcr31.cause_unimplemented_operation = true;
break;
case FP_INFINITE:
case FP_ZERO:
case FP_NORMAL:
break; // No-op, these are fine.
default:
Util::panic("Unknown floating point classification: {}", c);
}
}
#define PUSHROUNDINGMODE int og = PushRoundingMode(fcr31) #define PUSHROUNDINGMODE int og = PushRoundingMode(fcr31)
#define POPROUNDINGMODE fesetround(og) #define POPROUNDINGMODE fesetround(og)
#define any_unordered(fs, ft) (std::isnan(fs) || std::isnan(ft))
#define F_TO_U32(f) (*((u32*)(&(f))))
#define D_TO_U64(d) (*((u64*)(&(d))))
#define U64_TO_D(d) (*((double*)(&(d))))
template <typename T>
FORCE_INLINE bool isnan(T f) {
if constexpr(std::is_same<T, float>::value) {
u32 v = F_TO_U32(f);
return ((v & 0x7F800000) == 0x7F800000) && ((v & 0x7FFFFF) != 0);
} else if constexpr(std::is_same<T, double>::value) {
u64 v = D_TO_U64(f);
return ((v & 0x7FF0000000000000) == 0x7FF0000000000000) && ((v & 0xFFFFFFFFFFFFF) != 0);
} else {
Util::panic("Invalid float type in isnan");
}
}
template <typename T>
FORCE_INLINE bool isqnan(T f) {
if constexpr(std::is_same<T, float>::value) {
u32 v = F_TO_U32(f);
return (v & 0x7FC00000) == 0x7FC00000;
} else if constexpr(std::is_same<T, double>::value) {
u64 v = D_TO_U64(f);
return (v & 0x7FF8000000000000) == 0x7FF8000000000000;
} else {
Util::panic("Invalid float type in isqnan");
}
}
#define checknanregs(fs, ft) do { \ #define checknanregs(fs, ft) do { \
if(std::isnan(fs) || std::isnan(ft)) { \ if(isnan(fs) || isnan(ft)) { \
regs.cop1.fcr31.flag_invalid_operation = true; \
regs.cop1.fcr31.cause_invalid_operation = true; \ regs.cop1.fcr31.cause_invalid_operation = true; \
FireException(regs, ExceptionCode::FloatingPointError, 1, true); \ if(!regs.cop1.fcr31.enable_invalid_operation) { \
return; \ regs.cop1.fcr31.flag_invalid_operation = true; \
} \
CheckFPUException(); \
} \
} while(0)
#define checkqnanregs(fs, ft) do { \
if(isqnan(fs) || isqnan(ft)) { \
regs.cop1.fcr31.cause_invalid_operation = true; \
if(!regs.cop1.fcr31.enable_invalid_operation) { \
regs.cop1.fcr31.flag_invalid_operation = true; \
} \
CheckFPUException(); \
} \ } \
} while(0) } while(0)
void Cop1::absd(Registers& regs, u32 instr) { void Cop1::absd(Registers& regs, u32 instr) {
double fs = GetCop1Reg<double>(regs.cop0, FS(instr)); OP(double, std::abs(fs));
SetCop1Reg<double>(regs.cop0, FD(instr), std::abs(fs));
} }
void Cop1::abss(Registers& regs, u32 instr) { void Cop1::abss(Registers& regs, u32 instr) {
float fs = GetCop1Reg<float>(regs.cop0, FS(instr)); OP(float, std::abs(fs));
SetCop1Reg<float>(regs.cop0, FD(instr), std::abs(fs));
}
void Cop1::absw(Registers& regs, u32 instr) {
s32 fs = GetReg<s32>(regs.cop0, FS(instr));
SetReg<u32>(regs.cop0, FD(instr), std::abs(fs));
}
void Cop1::absl(Registers& regs, u32 instr) {
s64 fs = GetReg<s64>(regs.cop0, FS(instr));
SetReg(regs.cop0, FD(instr), std::abs(fs));
} }
void Cop1::adds(Registers& regs, u32 instr) { void Cop1::adds(Registers& regs, u32 instr) {
float fs = GetCop1Reg<float>(regs.cop0, FS(instr)); OP(float, fs + ft);
float ft = GetCop1Reg<float>(regs.cop0, FT(instr));
checknanregs(fs, ft);
float result = fs + ft;
SetCop1Reg<float>(regs.cop0, FD(instr), result);
} }
void Cop1::addd(Registers& regs, u32 instr) { void Cop1::addd(Registers& regs, u32 instr) {
double fs = GetCop1Reg<double>(regs.cop0, FS(instr)); OP(double, fs + ft);
double ft = GetCop1Reg<double>(regs.cop0, FT(instr));
checknanregs(fs, ft);
double result = fs + ft;
SetCop1Reg<double>(regs.cop0, FD(instr), result);
} }
void Cop1::ceills(Registers& regs, u32 instr) { void Cop1::ceills(Registers& regs, u32 instr) {
float fs = GetCop1Reg<float>(regs.cop0, FS(instr)); CheckFPUUsable();
s64 result = std::ceil(fs); auto fs = GetFGR_FS<float>(regs.cop0, FS(instr));
SetReg<u64>(regs.cop0, FD(instr), result); CheckCVTArg(fs);
s64 result;
CVT_OP_CheckExcept({ result = std::ceil(fs); });
CheckRound(result, fs);
SetFGR(FD(instr), result);
} }
void Cop1::ceilws(Registers& regs, u32 instr) { void Cop1::ceilws(Registers& regs, u32 instr) {
float fs = GetCop1Reg<float>(regs.cop0, FS(instr)); CheckFPUUsable();
s32 result = std::ceil(fs); auto fs = GetFGR_FS<float>(regs.cop0, FS(instr));
SetReg<u32>(regs.cop0, FD(instr), result); CheckCVTArg(fs);
s32 result;
CVT_OP_CheckExcept({ result = std::ceil(fs); });
CheckRound(result, fs);
SetFGR(FD(instr), result);
} }
void Cop1::ceilld(Registers& regs, u32 instr) { void Cop1::ceilld(Registers& regs, u32 instr) {
double fs = GetCop1Reg<double>(regs.cop0, FS(instr)); CheckFPUUsable();
s64 result = std::ceil(fs); auto fs = GetFGR_FS<double>(regs.cop0, FS(instr));
SetReg<u64>(regs.cop0, FD(instr), result); CheckCVTArg(fs);
s64 result;
CVT_OP_CheckExcept({ result = std::ceil(fs); });
CheckRound(result, fs);
SetFGR(FD(instr), result);
} }
void Cop1::ceilwd(Registers& regs, u32 instr) { void Cop1::ceilwd(Registers& regs, u32 instr) {
double fs = GetCop1Reg<double>(regs.cop0, FS(instr)); CheckFPUUsable();
s32 result = std::ceil(fs); auto fs = GetFGR_FS<double>(regs.cop0, FS(instr));
SetReg<u32>(regs.cop0, FD(instr), result); CheckCVTArg(fs);
s32 result;
CVT_OP_CheckExcept({ result = std::ceil(fs); });
CheckRound(result, fs);
SetFGR(FD(instr), result);
} }
void Cop1::cfc1(Registers& regs, u32 instr) const { void Cop1::cfc1(Registers& regs, u32 instr) const {
CheckFPUUsable_PreserveCause();
u8 fd = RD(instr); u8 fd = RD(instr);
s32 val = 0; s32 val = 0;
switch(fd) { switch(fd) {
case 0: val = fcr0; break; case 0: val = fcr0; break;
case 31: case 31:
val = fcr31.raw; val = fcr31.read();
break; break;
default: Util::panic("Undefined CFC1 with rd != 0 or 31"); default: Util::panic("Undefined CFC1 with rd != 0 or 31");
} }
@@ -105,349 +300,327 @@ void Cop1::cfc1(Registers& regs, u32 instr) const {
} }
void Cop1::ctc1(Registers& regs, u32 instr) { void Cop1::ctc1(Registers& regs, u32 instr) {
CheckFPUUsable_PreserveCause();
u8 fs = RD(instr); u8 fs = RD(instr);
u32 val = regs.gpr[RT(instr)]; u32 val = regs.gpr[RT(instr)];
switch(fs) { switch(fs) {
case 0: break; case 0: break;
case 31: { case 31: {
val &= 0x183ffff; fcr31.write(val);
fcr31.raw = val; FireFPUException(regs);
} break; } break;
default: Util::panic("Undefined CTC1 with rd != 0 or 31"); default: Util::panic("Undefined CTC1 with rd != 0 or 31");
} }
} }
void Cop1::cvtds(Registers& regs, u32 instr) { void Cop1::cvtds(Registers& regs, u32 instr) {
SetCop1Reg<double>( CheckFPUUsable();
regs.cop0, auto fs = GetFGR_FS<float>(regs.cop0, FS(instr));
FD(instr), CheckCVTArg(fs);
GetCop1Reg<float>( double result;
regs.cop0, CVT_OP_CheckExcept({ result = double(fs); });
FS(instr) SetFGR_Raw(FD(instr), result);
)
);
} }
void Cop1::cvtsd(Registers& regs, u32 instr) { void Cop1::cvtsd(Registers& regs, u32 instr) {
SetCop1Reg<float>( CheckFPUUsable();
regs.cop0, auto fs = GetFGR_FS<double>(regs.cop0, FS(instr));
FD(instr), CheckCVTArg(fs);
GetCop1Reg<double>( float result;
regs.cop0, CVT_OP_CheckExcept({ result = float(fs); });
FS(instr) SetFGR_Raw(FD(instr), result);
)
);
} }
void Cop1::cvtwd(Registers& regs, u32 instr) { void Cop1::cvtwd(Registers& regs, u32 instr) {
SetReg<u32>( CheckFPUUsable();
regs.cop0, auto fs = GetFGR_FS<double>(regs.cop0, FS(instr));
FD(instr), CheckCVTArg(fs);
GetCop1Reg<double>( s32 result;
regs.cop0, CVT_OP_CheckExcept({ result = s32(fs); });
FS(instr) SetFGR(FD(instr), result);
)
);
} }
void Cop1::cvtws(Registers& regs, u32 instr) { void Cop1::cvtws(Registers& regs, u32 instr) {
SetReg<u32>( CheckFPUUsable();
regs.cop0, auto fs = GetFGR_FS<float>(regs.cop0, FS(instr));
FD(instr), CheckCVTArg(fs);
GetCop1Reg<float>( s32 result;
regs.cop0, CVT_OP_CheckExcept({ result = s32(fs); });
FS(instr) SetFGR(FD(instr), result);
)
);
} }
void Cop1::cvtls(Registers& regs, u32 instr) { void Cop1::cvtls(Registers& regs, u32 instr) {
SetReg<u64>( CheckFPUUsable();
regs.cop0, auto fs = GetFGR_FS<float>(regs.cop0, FS(instr));
FD(instr), CheckCVTArg(fs);
GetCop1Reg<float>( s64 result;
regs.cop0, CVT_OP_CheckExcept({ result = s64(fs); });
FS(instr) SetFGR(FD(instr), result);
)
);
} }
void Cop1::cvtsl(Registers& regs, u32 instr) { void Cop1::cvtsl(Registers& regs, u32 instr) {
SetCop1Reg<float>( CheckFPUUsable();
regs.cop0, auto fs = GetFGR_FR<s64>(regs.cop0, FS(instr));
FD(instr), if (fs >= (s64)0x0080000000000000 || fs < s64(0xff80'0000'0000'0000)) {
(s64)GetReg<u64>( fcr31.cause_unimplemented_operation = true;
regs.cop0, CheckFPUException();
FS(instr) }
) float result;
); OP_CheckExcept({ result = float(fs); });
SetFGR_Raw(FD(instr), result);
} }
void Cop1::cvtdw(Registers& regs, u32 instr) { void Cop1::cvtdw(Registers& regs, u32 instr) {
SetCop1Reg<double>( CheckFPUUsable();
regs.cop0, auto fs = GetFGR_FS<s32>(regs.cop0, FS(instr));
FD(instr), double result;
(s32)GetReg<u32>( OP_CheckExcept({ result = double(fs); });
regs.cop0, SetFGR_Raw(FD(instr), result);
FS(instr)
)
);
} }
void Cop1::cvtsw(Registers& regs, u32 instr) { void Cop1::cvtsw(Registers& regs, u32 instr) {
SetCop1Reg<float>( CheckFPUUsable();
regs.cop0, auto fs = GetFGR_FS<s32>(regs.cop0, FS(instr));
FD(instr), float result;
(s32)GetReg<u32>( OP_CheckExcept({ result = float(fs); });
regs.cop0, SetFGR_Raw(FD(instr), result);
FS(instr)
)
);
} }
void Cop1::cvtdl(Registers& regs, u32 instr) { void Cop1::cvtdl(Registers& regs, u32 instr) {
SetCop1Reg<double>( CheckFPUUsable();
regs.cop0, auto fs = GetFGR_FR<s64>(regs.cop0, FS(instr));
FD(instr), if (fs >= (s64)0x0080000000000000 || fs < s64(0xff80'0000'0000'0000)) {
(s64)GetReg<u64>( fcr31.cause_unimplemented_operation = true;
regs.cop0, CheckFPUException();
FS(instr) }
) double result;
); OP_CheckExcept({ result = double(fs); });
SetFGR_Raw(FD(instr), result);
} }
void Cop1::cvtld(Registers& regs, u32 instr) { void Cop1::cvtld(Registers& regs, u32 instr) {
SetReg<u64>( CheckFPUUsable();
regs.cop0, auto fs = GetFGR_FS<double>(regs.cop0, FS(instr));
FD(instr), CheckCVTArg(fs);
GetCop1Reg<double>( s64 result;
regs.cop0, PUSHROUNDING;
FS(instr) CVT_OP_CheckExcept({ result = std::rint(fs); });
) POPROUNDING;
); CheckRound(fs, result);
SetFGR(FD(instr), result);
} }
template <typename T> template <typename T>
inline bool CalculateCondition(Registers& regs, T fs, T ft, CompConds cond) { inline bool CalculateCondition(T fs, T ft, CompConds cond) {
switch(cond) { switch(cond) {
case F: return false; case F: case SF: return false;
case UN: return std::isnan(fs) || std::isnan(ft); case UN: case NGLE: return any_unordered(fs, ft);
case EQ: return fs == ft; case EQ: case SEQ: return fs == ft;
case UEQ: return (std::isnan(fs) || std::isnan(ft)) || (fs == ft); case UEQ: case NGL: return fs == ft || any_unordered(fs, ft);
case OLT: return (!std::isnan(fs) && !std::isnan(ft)) && (fs < ft); case OLT: case LT: return fs < ft;
case ULT: return (std::isnan(fs) || std::isnan(ft)) || (fs < ft); case ULT: case NGE: return fs < ft || any_unordered(fs, ft);
case OLE: return (!std::isnan(fs) && !std::isnan(ft)) && (fs <= ft); case OLE: case LE: return fs <= ft;
case ULE: return (std::isnan(fs) || std::isnan(ft)) || (fs <= ft); case ULE: case NGT: return fs <= ft || any_unordered(fs, ft);
default: }
if(std::isnan(fs) || std::isnan(ft)) {
regs.cop1.fcr31.flag_invalid_operation = true;
regs.cop1.fcr31.cause_invalid_operation = true;
FireException(regs, ExceptionCode::FloatingPointError, 1, true);
return false;
} }
return CalculateCondition(regs, fs, ft, static_cast<CompConds>(cond - 8)); template <typename T>
inline void CheckInvalidRegs(Registers& regs, T fs, T ft, CompConds cond) {
switch(cond) {
case F ... ULE: checkqnanregs(fs, ft); break;
case SF ... NGT: checknanregs(fs, ft); break;
} }
} }
template <typename T> template <typename T>
void Cop1::ccond(Registers& regs, u32 instr, CompConds cond) { void Cop1::ccond(Registers& regs, u32 instr, CompConds cond) {
T fs = GetCop1Reg<T>(regs.cop0, FS(instr)); CheckFPUUsable();
T ft = GetCop1Reg<T>(regs.cop0, FT(instr)); T fs = GetFGR_FS<T>(regs.cop0, FS(instr));
T ft = GetFGR_FT<T>(FT(instr));
fcr31.compare = CalculateCondition(regs, fs, ft, cond); CheckInvalidRegs(regs, fs, ft, cond);
fcr31.compare = CalculateCondition(fs, ft, cond);
} }
template void Cop1::ccond<float>(Registers& regs, u32 instr, CompConds cond); template void Cop1::ccond<float>(Registers& regs, u32 instr, CompConds cond);
template void Cop1::ccond<double>(Registers& regs, u32 instr, CompConds cond); template void Cop1::ccond<double>(Registers& regs, u32 instr, CompConds cond);
void Cop1::divs(Registers &regs, u32 instr) { void Cop1::divs(Registers &regs, u32 instr) {
float fs = GetCop1Reg<float>(regs.cop0, FS(instr)); OP(float, fs / ft);
float ft = GetCop1Reg<float>(regs.cop0, FT(instr));
SetCop1Reg<float>(regs.cop0, FD(instr), fs / ft);
} }
void Cop1::divd(Registers &regs, u32 instr) { void Cop1::divd(Registers &regs, u32 instr) {
double fs = GetCop1Reg<double>(regs.cop0, FS(instr)); OP(double, fs / ft);
double ft = GetCop1Reg<double>(regs.cop0, FT(instr));
SetCop1Reg<double>(regs.cop0, FD(instr), fs / ft);
} }
void Cop1::muls(Registers &regs, u32 instr) { void Cop1::muls(Registers &regs, u32 instr) {
float fs = GetCop1Reg<float>(regs.cop0, FS(instr)); OP(float, fs * ft);
float ft = GetCop1Reg<float>(regs.cop0, FT(instr));
SetCop1Reg<float>(regs.cop0, FD(instr), fs * ft);
} }
void Cop1::muld(Registers& regs, u32 instr) { void Cop1::muld(Registers& regs, u32 instr) {
double fs = GetCop1Reg<double>(regs.cop0, FS(instr)); OP(double, fs * ft);
double ft = GetCop1Reg<double>(regs.cop0, FT(instr));
SetCop1Reg<double>(regs.cop0, FD(instr), fs * ft);
}
void Cop1::mulw(Registers &regs, u32 instr) {
u32 fs = GetReg<u32>(regs.cop0, FS(instr));
u32 ft = GetReg<u32>(regs.cop0, FT(instr));
SetReg<u32>(regs.cop0, FD(instr), fs * ft);
}
void Cop1::mull(Registers &regs, u32 instr) {
u64 fs = GetReg<u64>(regs.cop0, FS(instr));
u64 ft = GetReg<u64>(regs.cop0, FT(instr));
SetReg<u64>(regs.cop0, FD(instr), fs * ft);
} }
void Cop1::subs(Registers &regs, u32 instr) { void Cop1::subs(Registers &regs, u32 instr) {
float fs = GetCop1Reg<float>(regs.cop0, FS(instr)); OP(float, fs - ft);
float ft = GetCop1Reg<float>(regs.cop0, FT(instr));
SetCop1Reg<float>(regs.cop0, FD(instr), fs - ft);
} }
void Cop1::subd(Registers &regs, u32 instr) { void Cop1::subd(Registers &regs, u32 instr) {
double fs = GetCop1Reg<double>(regs.cop0, FS(instr)); OP(double, fs - ft);
double ft = GetCop1Reg<double>(regs.cop0, FT(instr));
SetCop1Reg<double>(regs.cop0, FD(instr), fs - ft);
}
void Cop1::subw(Registers &regs, u32 instr) {
u32 fs = GetReg<u32>(regs.cop0, FS(instr));
u32 ft = GetReg<u32>(regs.cop0, FT(instr));
SetReg<u32>(regs.cop0, FD(instr), fs - ft);
}
void Cop1::subl(Registers &regs, u32 instr) {
u64 fs = GetReg<u64>(regs.cop0, FS(instr));
u64 ft = GetReg<u64>(regs.cop0, FT(instr));
SetReg<u64>(regs.cop0, FD(instr), fs - ft);
} }
void Cop1::movs(Registers& regs, u32 instr) { void Cop1::movs(Registers& regs, u32 instr) {
SetCop1Reg<float>( CheckFPUUsable_PreserveCause();
regs.cop0, auto val = GetFGR_FR<u64>(regs.cop0, FS(instr));
FD(instr), SetFGR(FD(instr), val);
GetCop1Reg<float>(
regs.cop0,
FS(instr)
)
);
} }
void Cop1::movd(Registers& regs, u32 instr) { void Cop1::movd(Registers& regs, u32 instr) {
SetCop1Reg<double>( CheckFPUUsable_PreserveCause();
regs.cop0, auto val = GetFGR_FS<double>(regs.cop0, FS(instr));
FD(instr), SetFGR_Raw(FD(instr), val);
GetCop1Reg<double>(
regs.cop0,
FS(instr)
)
);
}
void Cop1::movw(Registers& regs, u32 instr) {
SetReg<u32>(
regs.cop0,
FD(instr),
GetReg<u32>(
regs.cop0,
FS(instr)
)
);
}
void Cop1::movl(Registers& regs, u32 instr) {
SetReg<u64>(
regs.cop0,
FD(instr),
GetReg<u64>(
regs.cop0,
FS(instr)
)
);
} }
void Cop1::negs(Registers &regs, u32 instr) { void Cop1::negs(Registers &regs, u32 instr) {
SetCop1Reg<float>( OP(float, -fs);
regs.cop0,
FD(instr),
-GetCop1Reg<float>(
regs.cop0,
FS(instr)
)
);
} }
void Cop1::negd(Registers &regs, u32 instr) { void Cop1::negd(Registers &regs, u32 instr) {
SetCop1Reg<double>( OP(double, -ft);
regs.cop0,
FD(instr),
-GetCop1Reg<double>(
regs.cop0,
FS(instr)
)
);
} }
void Cop1::sqrts(Registers &regs, u32 instr) { void Cop1::sqrts(Registers &regs, u32 instr) {
float fs = GetCop1Reg<float>(regs.cop0, FS(instr)); OP(float, std::sqrt(fs));
SetCop1Reg<float>(regs.cop0, FD(instr), std::sqrt(fs));
} }
void Cop1::sqrtd(Registers &regs, u32 instr) { void Cop1::sqrtd(Registers &regs, u32 instr) {
double fs = GetCop1Reg<double>(regs.cop0, FS(instr)); OP(double, std::sqrt(fs));
SetCop1Reg<double>(regs.cop0, FD(instr), std::sqrt(fs));
} }
void Cop1::roundls(Registers& regs, u32 instr) { void Cop1::roundls(Registers& regs, u32 instr) {
float fs = GetCop1Reg<float>(regs.cop0, FS(instr)); CheckFPUUsable();
PUSHROUNDINGMODE; auto fs = GetFGR_FS<float>(regs.cop0, FS(instr));
SetReg<u64>(regs.cop0, FD(instr), (s32)std::nearbyint(fs)); CheckCVTArg(fs);
POPROUNDINGMODE; s64 result;
CVT_OP_CheckExcept({ result = std::nearbyint(fs); });
CheckRound(fs, result);
SetFGR(FD(instr), result);
} }
void Cop1::roundld(Registers& regs, u32 instr) { void Cop1::roundld(Registers& regs, u32 instr) {
double fs = GetCop1Reg<double>(regs.cop0, FS(instr)); CheckFPUUsable();
PUSHROUNDINGMODE; auto fs = GetFGR_FS<double>(regs.cop0, FS(instr));
SetReg<u64>(regs.cop0, FD(instr), (s64)std::nearbyint(fs)); CheckCVTArg(fs);
POPROUNDINGMODE; s64 result;
CVT_OP_CheckExcept({ result = std::nearbyint(fs); });
CheckRound(fs, result);
SetFGR(FD(instr), result);
} }
void Cop1::roundws(Registers& regs, u32 instr) { void Cop1::roundws(Registers& regs, u32 instr) {
float fs = GetCop1Reg<float>(regs.cop0, FS(instr)); CheckFPUUsable();
PUSHROUNDINGMODE; auto fs = GetFGR_FS<float>(regs.cop0, FS(instr));
SetReg<u32>(regs.cop0, FD(instr), (s32)std::nearbyint(fs)); CheckCVTArg(fs);
POPROUNDINGMODE; s32 result;
CVT_OP_CheckExcept({ result = std::nearbyint(fs); });
CheckRound(fs, result);
SetFGR(FD(instr), result);
} }
void Cop1::roundwd(Registers& regs, u32 instr) { void Cop1::roundwd(Registers& regs, u32 instr) {
double fs = GetCop1Reg<double>(regs.cop0, FS(instr)); CheckFPUUsable();
PUSHROUNDINGMODE; auto fs = GetFGR_FS<double>(regs.cop0, FS(instr));
SetReg<u32>(regs.cop0, FD(instr), (s32)std::nearbyint(fs)); CheckCVTArg(fs);
POPROUNDINGMODE; s32 result;
CVT_OP_CheckExcept({ result = std::nearbyint(fs); });
CheckRound(fs, result);
SetFGR(FD(instr), result);
} }
void Cop1::floorls(Registers& regs, u32 instr) { void Cop1::floorls(Registers& regs, u32 instr) {
float fs = GetCop1Reg<float>(regs.cop0, FS(instr)); CheckFPUUsable();
SetReg<u64>(regs.cop0, FD(instr), (s64)std::floor(fs)); auto fs = GetFGR_FS<float>(regs.cop0, FS(instr));
CheckCVTArg(fs);
s64 result;
CVT_OP_CheckExcept({ result = std::floor(fs); });
CheckRound(fs, result);
SetFGR(FD(instr), result);
} }
void Cop1::floorld(Registers& regs, u32 instr) { void Cop1::floorld(Registers& regs, u32 instr) {
double fs = GetCop1Reg<double>(regs.cop0, FS(instr)); CheckFPUUsable();
SetReg<u64>(regs.cop0, FD(instr), (s64)std::floor(fs)); auto fs = GetFGR_FS<double>(regs.cop0, FS(instr));
CheckCVTArg(fs);
s64 result;
CVT_OP_CheckExcept({ result = std::floor(fs); });
CheckRound(fs, result);
SetFGR(FD(instr), result);
} }
void Cop1::floorws(Registers& regs, u32 instr) { void Cop1::floorws(Registers& regs, u32 instr) {
float fs = GetCop1Reg<float>(regs.cop0, FS(instr)); CheckFPUUsable();
SetReg<u32>(regs.cop0, FD(instr), (s64)std::floor(fs)); auto fs = GetFGR_FS<float>(regs.cop0, FS(instr));
CheckCVTArg(fs);
s32 result;
CVT_OP_CheckExcept({ result = std::floor(fs); });
CheckRound(fs, result);
SetFGR(FD(instr), result);
} }
void Cop1::floorwd(Registers& regs, u32 instr) { void Cop1::floorwd(Registers& regs, u32 instr) {
double fs = GetCop1Reg<double>(regs.cop0, FS(instr)); CheckFPUUsable();
SetReg<u32>(regs.cop0, FD(instr), (s64)std::floor(fs)); auto fs = GetFGR_FS<double>(regs.cop0, FS(instr));
CheckCVTArg(fs);
s32 result;
CVT_OP_CheckExcept({ result = std::floor(fs); });
CheckRound(fs, result);
SetFGR(FD(instr), result);
}
void Cop1::truncws(Registers& regs, u32 instr) {
CheckFPUUsable();
auto fs = GetFGR_FS<float>(regs.cop0, FS(instr));
CheckCVTArg(fs);
s32 result;
CVT_OP_CheckExcept({ result = std::trunc(fs); });
CheckRound(fs, result);
SetFGR(FD(instr), result);
}
void Cop1::truncwd(Registers& regs, u32 instr) {
CheckFPUUsable();
auto fs = GetFGR_FS<double>(regs.cop0, FS(instr));
CheckCVTArg(fs);
s32 result;
CVT_OP_CheckExcept({ result = std::trunc(fs); });
CheckRound(fs, result);
SetFGR(FD(instr), result);
}
void Cop1::truncls(Registers& regs, u32 instr) {
CheckFPUUsable();
auto fs = GetFGR_FS<float>(regs.cop0, FS(instr));
CheckCVTArg(fs);
s64 result;
CVT_OP_CheckExcept({ result = std::trunc(fs); });
CheckRound(fs, result);
SetFGR(FD(instr), result);
}
void Cop1::truncld(Registers& regs, u32 instr) {
CheckFPUUsable();
auto fs = GetFGR_FS<double>(regs.cop0, FS(instr));
CheckCVTArg(fs);
s64 result;
CVT_OP_CheckExcept({ result = std::trunc(fs); });
CheckRound(fs, result);
SetFGR(FD(instr), result);
} }
template<class T> template<class T>
void Cop1::lwc1(T &cpu, Mem &mem, u32 instr) { void Cop1::lwc1(T &cpu, Mem &mem, u32 instr) {
if constexpr(std::is_same_v<decltype(cpu), Interpreter&>) { if constexpr(std::is_same_v<decltype(cpu), Interpreter&>) {
Registers& regs = cpu.regs;
CheckFPUUsable_PreserveCause();
lwc1Interp(cpu.regs, mem, instr); lwc1Interp(cpu.regs, mem, instr);
} else if constexpr (std::is_same_v<decltype(cpu), JIT&>) { } else if constexpr (std::is_same_v<decltype(cpu), JIT&>) {
lwc1JIT(cpu, mem, instr); lwc1JIT(cpu, mem, instr);
@@ -462,6 +635,8 @@ template void Cop1::lwc1<JIT>(JIT&, Mem&, u32);
template<class T> template<class T>
void Cop1::swc1(T &cpu, Mem &mem, u32 instr) { void Cop1::swc1(T &cpu, Mem &mem, u32 instr) {
if constexpr(std::is_same_v<decltype(cpu), Interpreter&>) { if constexpr(std::is_same_v<decltype(cpu), Interpreter&>) {
Registers& regs = cpu.regs;
CheckFPUUsable_PreserveCause();
swc1Interp(cpu.regs, mem, instr); swc1Interp(cpu.regs, mem, instr);
} else if constexpr (std::is_same_v<decltype(cpu), JIT&>) { } else if constexpr (std::is_same_v<decltype(cpu), JIT&>) {
swc1JIT(cpu, mem, instr); swc1JIT(cpu, mem, instr);
@@ -476,6 +651,8 @@ template void Cop1::swc1<JIT>(JIT&, Mem&, u32);
template<class T> template<class T>
void Cop1::ldc1(T &cpu, Mem &mem, u32 instr) { void Cop1::ldc1(T &cpu, Mem &mem, u32 instr) {
if constexpr(std::is_same_v<decltype(cpu), Interpreter&>) { if constexpr(std::is_same_v<decltype(cpu), Interpreter&>) {
Registers& regs = cpu.regs;
CheckFPUUsable_PreserveCause();
ldc1Interp(cpu.regs, mem, instr); ldc1Interp(cpu.regs, mem, instr);
} else if constexpr (std::is_same_v<decltype(cpu), JIT&>) { } else if constexpr (std::is_same_v<decltype(cpu), JIT&>) {
ldc1JIT(cpu, mem, instr); ldc1JIT(cpu, mem, instr);
@@ -490,6 +667,8 @@ template void Cop1::ldc1<JIT>(JIT&, Mem&, u32);
template<class T> template<class T>
void Cop1::sdc1(T &cpu, Mem &mem, u32 instr) { void Cop1::sdc1(T &cpu, Mem &mem, u32 instr) {
if constexpr(std::is_same_v<decltype(cpu), Interpreter&>) { if constexpr(std::is_same_v<decltype(cpu), Interpreter&>) {
Registers& regs = cpu.regs;
CheckFPUUsable_PreserveCause();
sdc1Interp(cpu.regs, mem, instr); sdc1Interp(cpu.regs, mem, instr);
} else if constexpr (std::is_same_v<decltype(cpu), JIT&>) { } else if constexpr (std::is_same_v<decltype(cpu), JIT&>) {
sdc1JIT(cpu, mem, instr); sdc1JIT(cpu, mem, instr);
@@ -515,7 +694,7 @@ void Cop1::lwc1Interp(Registers& regs, Mem& mem, u32 instr) {
FireException(regs, GetTLBExceptionCode(regs.cop0.tlbError, LOAD), 0, true); FireException(regs, GetTLBExceptionCode(regs.cop0.tlbError, LOAD), 0, true);
} else { } else {
u32 data = mem.Read32(regs, physical); u32 data = mem.Read32(regs, physical);
SetReg<u32>(regs.cop0, FT(instr), data); SetFGR_FR<u32>(regs.cop0, FT(instr), data);
} }
} }
@@ -532,10 +711,16 @@ void Cop1::swc1Interp(Registers& regs, Mem& mem, u32 instr) {
HandleTLBException(regs, addr); HandleTLBException(regs, addr);
FireException(regs, GetTLBExceptionCode(regs.cop0.tlbError, STORE), 0, true); FireException(regs, GetTLBExceptionCode(regs.cop0.tlbError, STORE), 0, true);
} else { } else {
mem.Write32(regs, physical, GetReg<u32>(regs.cop0, FT(instr))); mem.Write32(regs, physical, GetFGR_FR<u32>(regs.cop0, FT(instr)));
} }
} }
void Cop1::unimplemented(Registers& regs) {
CheckFPUUsable();
fcr31.cause_unimplemented_operation = true;
FireFPUException(regs);
}
void Cop1::ldc1Interp(Registers& regs, Mem& mem, u32 instr) { void Cop1::ldc1Interp(Registers& regs, Mem& mem, u32 instr) {
if(!regs.cop0.status.cu1) { if(!regs.cop0.status.cu1) {
FireException(regs, ExceptionCode::CoprocessorUnusable, 1, true); FireException(regs, ExceptionCode::CoprocessorUnusable, 1, true);
@@ -550,7 +735,7 @@ void Cop1::ldc1Interp(Registers& regs, Mem& mem, u32 instr) {
FireException(regs, GetTLBExceptionCode(regs.cop0.tlbError, LOAD), 0, true); FireException(regs, GetTLBExceptionCode(regs.cop0.tlbError, LOAD), 0, true);
} else { } else {
u64 data = mem.Read64(regs, physical); u64 data = mem.Read64(regs, physical);
SetReg<u64>(regs.cop0, FT(instr), data); SetFGR_FR<u64>(regs.cop0, FT(instr), data);
} }
} }
@@ -567,48 +752,28 @@ void Cop1::sdc1Interp(Registers& regs, Mem& mem, u32 instr) {
HandleTLBException(regs, addr); HandleTLBException(regs, addr);
FireException(regs, GetTLBExceptionCode(regs.cop0.tlbError, STORE), 0, true); FireException(regs, GetTLBExceptionCode(regs.cop0.tlbError, STORE), 0, true);
} else { } else {
mem.Write64(regs, physical, GetReg<u64>(regs.cop0, FT(instr))); mem.Write64(regs, physical, GetFGR_FR<u64>(regs.cop0, FT(instr)));
} }
} }
void Cop1::truncws(Registers& regs, u32 instr) {
float fs = GetCop1Reg<float>(regs.cop0, FS(instr));
s32 result = (s32)std::trunc(fs);
SetReg<u32>(regs.cop0, FD(instr), result);
}
void Cop1::truncwd(Registers& regs, u32 instr) {
double fs = GetCop1Reg<double>(regs.cop0, FS(instr));
s32 result = (s32)std::trunc(fs);
SetReg<u32>(regs.cop0, FD(instr), result);
}
void Cop1::truncls(Registers& regs, u32 instr) {
float fs = GetCop1Reg<float>(regs.cop0, FS(instr));
s64 result = (s64)std::trunc(fs);
SetReg<u64>(regs.cop0, FD(instr), result);
}
void Cop1::truncld(Registers& regs, u32 instr) {
double fs = GetCop1Reg<double>(regs.cop0, FS(instr));
s64 result = (s64)std::trunc(fs);
SetReg<u64>(regs.cop0, FD(instr), result);
}
void Cop1::mfc1(Registers& regs, u32 instr) { void Cop1::mfc1(Registers& regs, u32 instr) {
regs.gpr[RT(instr)] = (s32)GetReg<u32>(regs.cop0, FS(instr)); CheckFPUUsable_PreserveCause();
regs.gpr[RT(instr)] = (s32)GetFGR_FR<u32>(regs.cop0, FS(instr));
} }
void Cop1::dmfc1(Registers& regs, u32 instr) { void Cop1::dmfc1(Registers& regs, u32 instr) {
regs.gpr[RT(instr)] = (s64)GetReg<u64>(regs.cop0, FS(instr)); CheckFPUUsable_PreserveCause();
regs.gpr[RT(instr)] = (s64)GetFGR_FR<u64>(regs.cop0, FS(instr));
} }
void Cop1::mtc1(Registers& regs, u32 instr) { void Cop1::mtc1(Registers& regs, u32 instr) {
SetReg<u32>(regs.cop0, FS(instr), regs.gpr[RT(instr)]); CheckFPUUsable_PreserveCause();
SetFGR_FR<u32>(regs.cop0, FS(instr), regs.gpr[RT(instr)]);
} }
void Cop1::dmtc1(Registers& regs, u32 instr) { void Cop1::dmtc1(Registers& regs, u32 instr) {
SetReg<u64>(regs.cop0, FS(instr), regs.gpr[RT(instr)]); CheckFPUUsable_PreserveCause();
SetFGR_FR<u64>(regs.cop0, FS(instr), regs.gpr[RT(instr)]);
} }
} }

View File

@@ -56,9 +56,6 @@ static FORCE_INLINE constexpr u32 GetVideoFrequency(bool pal) {
#define ELEMENT_INDEX(i) (7 - (i)) #define ELEMENT_INDEX(i) (7 - (i))
#define BYTE_INDEX(i) (15 - (i)) #define BYTE_INDEX(i) (15 - (i))
#define unlikely(exp) __builtin_expect(exp, 0)
#define likely(exp) __builtin_expect(exp, 1)
#if defined(WIN32) || defined(_WIN32) || defined(__WIN32) && !defined(__CYGWIN__) #if defined(WIN32) || defined(_WIN32) || defined(__WIN32) && !defined(__CYGWIN__)
#define ABI_WINDOWS #define ABI_WINDOWS
#else #else

View File

@@ -29,10 +29,10 @@ Settings::Settings(n64::Core& core) {
settings = json::parse(settingsFile); settings = json::parse(settingsFile);
checkjsonentry(oldVolumeL, float, "audio", "volumeL", 0.5); checkjsonentry(oldVolumeL, float, "audio", "volumeL", 0.5);
volumeL = oldVolumeL;
checkjsonentry(oldVolumeR, float, "audio", "volumeR", 0.5); checkjsonentry(oldVolumeR, float, "audio", "volumeR", 0.5);
volumeR = oldVolumeR;
checkjsonentry(mute, bool, "audio", "mute", false); checkjsonentry(mute, bool, "audio", "mute", false);
volumeL = mute ? 0 : oldVolumeL;
volumeR = mute ? 0 : oldVolumeR;
checkjsonentry(lockChannels, bool, "audio", "lockChannels", true); checkjsonentry(lockChannels, bool, "audio", "lockChannels", true);
checkjsonentry(jit, bool, "cpu", "enableJIT", false); checkjsonentry(jit, bool, "cpu", "enableJIT", false);
} else { } else {