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

View File

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

View File

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

View File

@@ -11,8 +11,8 @@ Cop1::Cop1() {
}
void Cop1::Reset() {
fcr0 = 0;
fcr31.raw = 0x01000800;
fcr0 = 0xa00;
fcr31.write(0x01000800);
memset(fgr, 0, 32 * sizeof(FGR));
}
@@ -32,10 +32,6 @@ template void Cop1::decode<JIT>(JIT&, u32);
void Cop1::decodeInterp(Interpreter &cpu, u32 instr) {
Registers &regs = cpu.regs;
if(!regs.cop0.status.cu1) {
FireException(regs, ExceptionCode::CoprocessorUnusable, 1, true);
return;
}
u8 mask_sub = (instr >> 21) & 0x1F;
u8 mask_fun = instr & 0x3F;
@@ -45,11 +41,11 @@ void Cop1::decodeInterp(Interpreter &cpu, u32 instr) {
case 0x00: mfc1(regs, instr); break;
case 0x01: dmfc1(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 0x05: dmtc1(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:
switch(mask_branch) {
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 0x0E: ceilws(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 0x24: cvtws(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 0x3E: ccond<float>(regs, instr, LE); 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;
case 0x11: // d
@@ -121,9 +114,6 @@ void Cop1::decodeInterp(Interpreter &cpu, u32 instr) {
case 0x0E: ceilwd(regs, instr); break;
case 0x0F: floorwd(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 0x25: cvtld(regs, instr); 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 0x3E: ccond<double>(regs, instr, LE); 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;
case 0x14: // w
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 0x21: cvtdw(regs, instr); break;
case 0x24:
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);
default: unimplemented(regs);
}
break;
case 0x15: // l
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 0x21: cvtdl(regs, instr); break;
case 0x24: case 0x25:
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);
default: unimplemented(regs);
}
break;
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 0x01: dmfc1(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 0x05: dmtc1(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:
/*switch(mask_branch) {
case 0: {
@@ -239,7 +215,7 @@ void Cop1::decodeJIT(JIT &cpu, u32 instr) {
case 0x0E: ceilws(regs, instr); break;
case 0x0F: floorws(regs, instr); break;
case 0x20:
FireException(regs, ExceptionCode::ReservedInstruction, 1, true);
unimplemented(regs);
break;
case 0x21: cvtds(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 0x20: cvtsd(regs, instr); break;
case 0x21:
FireException(regs, ExceptionCode::ReservedInstruction, 1, true);
unimplemented(regs);
break;
case 0x24: cvtwd(regs, instr); break;
case 0x25: cvtld(regs, instr); break;
@@ -308,28 +284,20 @@ void Cop1::decodeJIT(JIT &cpu, u32 instr) {
break;
case 0x14: // w
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 0x21: cvtdw(regs, instr); break;
case 0x24:
FireException(regs, ExceptionCode::ReservedInstruction, 1, true);
unimplemented(regs);
break;
default: Util::panic("Unimplemented COP1 function W[{} {}] ({:08X}) ({:016X})", mask_fun >> 3, mask_fun & 7, instr, (u64)regs.oldPC);
}
break;
case 0x15: // l
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 0x21: cvtdl(regs, instr); break;
case 0x24: case 0x25:
FireException(regs, ExceptionCode::ReservedInstruction, 1, true);
unimplemented(regs);
break;
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;
} __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 {
@@ -70,92 +81,133 @@ struct Cop1 {
friend struct Interpreter;
friend struct JIT;
template <typename T>
FORCE_INLINE void SetReg(Cop0& cop0, u8 index, T value) {
if constexpr(sizeof(T) == 4) {
template<typename T>
FORCE_INLINE T GetFGR_FR(Cop0& cop0, u8 r) {
if constexpr (std::is_same_v<T, u32> || std::is_same_v<T, s32>) {
if (cop0.status.fr) {
fgr[index].lo = value;
return fgr[r].lo;
} else {
if (index & 1) {
fgr[index & ~1].hi = value;
if (r & 1) {
return fgr[r & ~1].hi;
} else {
fgr[index].lo = value;
return fgr[r].lo;
}
}
} else if constexpr(sizeof(T) == 8) {
if(!cop0.status.fr) {
index &= ~1;
} else if constexpr (std::is_same_v<T, u64> || std::is_same_v<T, s64>) {
if (!cop0.status.fr) {
// 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>
FORCE_INLINE T GetReg(Cop0& cop0, u8 index) {
if constexpr(sizeof(T) == 4) {
if(cop0.status.fr) {
return fgr[index].lo;
template<typename T>
FORCE_INLINE void SetFGR_FR(Cop0& cop0, u8 r, T value) {
if constexpr (std::is_same_v<T, u32> || std::is_same_v<T, s32>) {
if (cop0.status.fr) {
fgr[r].lo = value;
} else {
if (index & 1) {
return fgr[index & ~1].hi;
if (r & 1) {
fgr[r & ~1].hi = value;
} else {
return fgr[index].lo;
fgr[r].lo = value;
}
}
} else if constexpr(sizeof(T) == 8) {
if(!cop0.status.fr) {
index &= ~1;
} else if constexpr (std::is_same_v<T, u64> || std::is_same_v<T, s64>) {
if (!cop0.status.fr) {
// 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>
FORCE_INLINE void SetFGR(u8 r, T value) {
fgr[r].raw = value;
}
template<typename T>
FORCE_INLINE u64 GetFGR(u8 r) {
if constexpr (std::is_same_v<T, u32> || std::is_same_v<T, s32>) {
return fgr[r].lo;
} else if constexpr (std::is_same_v<T, u64> || std::is_same_v<T, s64>) {
return fgr[r].raw;
}
}
template <typename T>
FORCE_INLINE void SetCop1Reg(Cop0& cop0, u8 index, T value) {
if constexpr (sizeof(T) == 4) {
u32 raw;
memcpy(&raw, &value, sizeof(T));
SetReg<u32>(cop0, index, raw);
} else if constexpr (sizeof(T) == 8) {
u64 raw;
memcpy(&raw, &value, sizeof(T));
SetReg<u64>(cop0, index, raw);
FORCE_INLINE T GetFGR_FS(Cop0& cop0, u8 fs) {
if constexpr (std::is_same_v<T, u32> || std::is_same_v<T, s32>) {
if (!cop0.status.fr) {
fs &= ~1;
}
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 GetCop1Reg(Cop0& cop0, u8 index) {
T value;
if constexpr (sizeof(T) == 4) {
u32 raw = GetReg<u32>(cop0, index);
memcpy(&value, &raw, sizeof(T));
} else if constexpr (sizeof(T) == 8) {
u64 raw = GetReg<u64>(cop0, index);
memcpy(&value, &raw, sizeof(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;
}
return value;
}
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:
void decodeInterp(Interpreter&, u32);
void decodeJIT(JIT&, u32);
void absd(Registers&, u32 instr);
void abss(Registers&, u32 instr);
void absw(Registers&, u32 instr);
void absl(Registers&, u32 instr);
void adds(Registers&, u32 instr);
void addd(Registers&, u32 instr);
void subs(Registers&, u32 instr);
void subd(Registers&, u32 instr);
void subw(Registers&, u32 instr);
void subl(Registers&, u32 instr);
void ceills(Registers&, u32 instr);
void ceilws(Registers&, u32 instr);
void ceilld(Registers&, u32 instr);
void ceilwd(Registers&, u32 instr);
void cfc1(Registers&, u32 instr) const;
void ctc1(Registers&, u32 instr);
void unimplemented(Registers&);
void roundls(Registers&, u32 instr);
void roundld(Registers&, u32 instr);
void roundws(Registers&, u32 instr);
@@ -180,12 +232,8 @@ private:
void divd(Registers&, u32 instr);
void muls(Registers&, u32 instr);
void muld(Registers&, u32 instr);
void mulw(Registers&, u32 instr);
void mull(Registers&, u32 instr);
void movs(Registers&, u32 instr);
void movd(Registers&, u32 instr);
void movw(Registers&, u32 instr);
void movl(Registers&, u32 instr);
void negs(Registers&, u32 instr);
void negd(Registers&, u32 instr);
void sqrts(Registers&, u32 instr);

View File

@@ -7,6 +7,21 @@
#include <cfenv>
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) {
int og = fegetround();
switch(fcr31.rounding_mode) {
@@ -19,85 +34,265 @@ FORCE_INLINE int PushRoundingMode(const FCR31& fcr31) {
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 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 { \
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; \
} \
if(isnan(fs) || isnan(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)
#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)
void Cop1::absd(Registers& regs, u32 instr) {
double fs = GetCop1Reg<double>(regs.cop0, FS(instr));
SetCop1Reg<double>(regs.cop0, FD(instr), std::abs(fs));
OP(double, std::abs(fs));
}
void Cop1::abss(Registers& regs, u32 instr) {
float fs = GetCop1Reg<float>(regs.cop0, FS(instr));
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));
OP(float, std::abs(fs));
}
void Cop1::adds(Registers& regs, u32 instr) {
float fs = GetCop1Reg<float>(regs.cop0, FS(instr));
float ft = GetCop1Reg<float>(regs.cop0, FT(instr));
checknanregs(fs, ft);
float result = fs + ft;
SetCop1Reg<float>(regs.cop0, FD(instr), result);
OP(float, fs + ft);
}
void Cop1::addd(Registers& regs, u32 instr) {
double fs = GetCop1Reg<double>(regs.cop0, FS(instr));
double ft = GetCop1Reg<double>(regs.cop0, FT(instr));
checknanregs(fs, ft);
double result = fs + ft;
SetCop1Reg<double>(regs.cop0, FD(instr), result);
OP(double, fs + ft);
}
void Cop1::ceills(Registers& regs, u32 instr) {
float fs = GetCop1Reg<float>(regs.cop0, FS(instr));
s64 result = std::ceil(fs);
SetReg<u64>(regs.cop0, FD(instr), result);
CheckFPUUsable();
auto fs = GetFGR_FS<float>(regs.cop0, FS(instr));
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) {
float fs = GetCop1Reg<float>(regs.cop0, FS(instr));
s32 result = std::ceil(fs);
SetReg<u32>(regs.cop0, FD(instr), result);
CheckFPUUsable();
auto fs = GetFGR_FS<float>(regs.cop0, FS(instr));
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) {
double fs = GetCop1Reg<double>(regs.cop0, FS(instr));
s64 result = std::ceil(fs);
SetReg<u64>(regs.cop0, FD(instr), result);
CheckFPUUsable();
auto fs = GetFGR_FS<double>(regs.cop0, FS(instr));
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) {
double fs = GetCop1Reg<double>(regs.cop0, FS(instr));
s32 result = std::ceil(fs);
SetReg<u32>(regs.cop0, FD(instr), result);
CheckFPUUsable();
auto fs = GetFGR_FS<double>(regs.cop0, FS(instr));
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 {
CheckFPUUsable_PreserveCause();
u8 fd = RD(instr);
s32 val = 0;
switch(fd) {
case 0: val = fcr0; break;
case 31:
val = fcr31.raw;
val = fcr31.read();
break;
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) {
CheckFPUUsable_PreserveCause();
u8 fs = RD(instr);
u32 val = regs.gpr[RT(instr)];
switch(fs) {
case 0: break;
case 31: {
val &= 0x183ffff;
fcr31.raw = val;
fcr31.write(val);
FireFPUException(regs);
} break;
default: Util::panic("Undefined CTC1 with rd != 0 or 31");
}
}
void Cop1::cvtds(Registers& regs, u32 instr) {
SetCop1Reg<double>(
regs.cop0,
FD(instr),
GetCop1Reg<float>(
regs.cop0,
FS(instr)
)
);
CheckFPUUsable();
auto fs = GetFGR_FS<float>(regs.cop0, FS(instr));
CheckCVTArg(fs);
double result;
CVT_OP_CheckExcept({ result = double(fs); });
SetFGR_Raw(FD(instr), result);
}
void Cop1::cvtsd(Registers& regs, u32 instr) {
SetCop1Reg<float>(
regs.cop0,
FD(instr),
GetCop1Reg<double>(
regs.cop0,
FS(instr)
)
);
CheckFPUUsable();
auto fs = GetFGR_FS<double>(regs.cop0, FS(instr));
CheckCVTArg(fs);
float result;
CVT_OP_CheckExcept({ result = float(fs); });
SetFGR_Raw(FD(instr), result);
}
void Cop1::cvtwd(Registers& regs, u32 instr) {
SetReg<u32>(
regs.cop0,
FD(instr),
GetCop1Reg<double>(
regs.cop0,
FS(instr)
)
);
CheckFPUUsable();
auto fs = GetFGR_FS<double>(regs.cop0, FS(instr));
CheckCVTArg(fs);
s32 result;
CVT_OP_CheckExcept({ result = s32(fs); });
SetFGR(FD(instr), result);
}
void Cop1::cvtws(Registers& regs, u32 instr) {
SetReg<u32>(
regs.cop0,
FD(instr),
GetCop1Reg<float>(
regs.cop0,
FS(instr)
)
);
CheckFPUUsable();
auto fs = GetFGR_FS<float>(regs.cop0, FS(instr));
CheckCVTArg(fs);
s32 result;
CVT_OP_CheckExcept({ result = s32(fs); });
SetFGR(FD(instr), result);
}
void Cop1::cvtls(Registers& regs, u32 instr) {
SetReg<u64>(
regs.cop0,
FD(instr),
GetCop1Reg<float>(
regs.cop0,
FS(instr)
)
);
CheckFPUUsable();
auto fs = GetFGR_FS<float>(regs.cop0, FS(instr));
CheckCVTArg(fs);
s64 result;
CVT_OP_CheckExcept({ result = s64(fs); });
SetFGR(FD(instr), result);
}
void Cop1::cvtsl(Registers& regs, u32 instr) {
SetCop1Reg<float>(
regs.cop0,
FD(instr),
(s64)GetReg<u64>(
regs.cop0,
FS(instr)
)
);
CheckFPUUsable();
auto fs = GetFGR_FR<s64>(regs.cop0, FS(instr));
if (fs >= (s64)0x0080000000000000 || fs < s64(0xff80'0000'0000'0000)) {
fcr31.cause_unimplemented_operation = true;
CheckFPUException();
}
float result;
OP_CheckExcept({ result = float(fs); });
SetFGR_Raw(FD(instr), result);
}
void Cop1::cvtdw(Registers& regs, u32 instr) {
SetCop1Reg<double>(
regs.cop0,
FD(instr),
(s32)GetReg<u32>(
regs.cop0,
FS(instr)
)
);
CheckFPUUsable();
auto fs = GetFGR_FS<s32>(regs.cop0, FS(instr));
double result;
OP_CheckExcept({ result = double(fs); });
SetFGR_Raw(FD(instr), result);
}
void Cop1::cvtsw(Registers& regs, u32 instr) {
SetCop1Reg<float>(
regs.cop0,
FD(instr),
(s32)GetReg<u32>(
regs.cop0,
FS(instr)
)
);
CheckFPUUsable();
auto fs = GetFGR_FS<s32>(regs.cop0, FS(instr));
float result;
OP_CheckExcept({ result = float(fs); });
SetFGR_Raw(FD(instr), result);
}
void Cop1::cvtdl(Registers& regs, u32 instr) {
SetCop1Reg<double>(
regs.cop0,
FD(instr),
(s64)GetReg<u64>(
regs.cop0,
FS(instr)
)
);
CheckFPUUsable();
auto fs = GetFGR_FR<s64>(regs.cop0, FS(instr));
if (fs >= (s64)0x0080000000000000 || fs < s64(0xff80'0000'0000'0000)) {
fcr31.cause_unimplemented_operation = true;
CheckFPUException();
}
double result;
OP_CheckExcept({ result = double(fs); });
SetFGR_Raw(FD(instr), result);
}
void Cop1::cvtld(Registers& regs, u32 instr) {
SetReg<u64>(
regs.cop0,
FD(instr),
GetCop1Reg<double>(
regs.cop0,
FS(instr)
)
);
CheckFPUUsable();
auto fs = GetFGR_FS<double>(regs.cop0, FS(instr));
CheckCVTArg(fs);
s64 result;
PUSHROUNDING;
CVT_OP_CheckExcept({ result = std::rint(fs); });
POPROUNDING;
CheckRound(fs, result);
SetFGR(FD(instr), result);
}
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) {
case F: return false;
case UN: return std::isnan(fs) || std::isnan(ft);
case EQ: return fs == ft;
case UEQ: return (std::isnan(fs) || std::isnan(ft)) || (fs == ft);
case OLT: return (!std::isnan(fs) && !std::isnan(ft)) && (fs < ft);
case ULT: return (std::isnan(fs) || std::isnan(ft)) || (fs < ft);
case OLE: return (!std::isnan(fs) && !std::isnan(ft)) && (fs <= ft);
case ULE: return (std::isnan(fs) || std::isnan(ft)) || (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;
}
case F: case SF: return false;
case UN: case NGLE: return any_unordered(fs, ft);
case EQ: case SEQ: return fs == ft;
case UEQ: case NGL: return fs == ft || any_unordered(fs, ft);
case OLT: case LT: return fs < ft;
case ULT: case NGE: return fs < ft || any_unordered(fs, ft);
case OLE: case LE: return fs <= ft;
case ULE: case NGT: return fs <= ft || any_unordered(fs, ft);
}
}
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>
void Cop1::ccond(Registers& regs, u32 instr, CompConds cond) {
T fs = GetCop1Reg<T>(regs.cop0, FS(instr));
T ft = GetCop1Reg<T>(regs.cop0, FT(instr));
fcr31.compare = CalculateCondition(regs, fs, ft, cond);
CheckFPUUsable();
T fs = GetFGR_FS<T>(regs.cop0, FS(instr));
T ft = GetFGR_FT<T>(FT(instr));
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<double>(Registers& regs, u32 instr, CompConds cond);
void Cop1::divs(Registers &regs, u32 instr) {
float fs = GetCop1Reg<float>(regs.cop0, FS(instr));
float ft = GetCop1Reg<float>(regs.cop0, FT(instr));
SetCop1Reg<float>(regs.cop0, FD(instr), fs / ft);
OP(float, fs / ft);
}
void Cop1::divd(Registers &regs, u32 instr) {
double fs = GetCop1Reg<double>(regs.cop0, FS(instr));
double ft = GetCop1Reg<double>(regs.cop0, FT(instr));
SetCop1Reg<double>(regs.cop0, FD(instr), fs / ft);
OP(double, fs / ft);
}
void Cop1::muls(Registers &regs, u32 instr) {
float fs = GetCop1Reg<float>(regs.cop0, FS(instr));
float ft = GetCop1Reg<float>(regs.cop0, FT(instr));
SetCop1Reg<float>(regs.cop0, FD(instr), fs * ft);
OP(float, fs * ft);
}
void Cop1::muld(Registers& regs, u32 instr) {
double fs = GetCop1Reg<double>(regs.cop0, FS(instr));
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);
OP(double, fs * ft);
}
void Cop1::subs(Registers &regs, u32 instr) {
float fs = GetCop1Reg<float>(regs.cop0, FS(instr));
float ft = GetCop1Reg<float>(regs.cop0, FT(instr));
SetCop1Reg<float>(regs.cop0, FD(instr), fs - ft);
OP(float, fs - ft);
}
void Cop1::subd(Registers &regs, u32 instr) {
double fs = GetCop1Reg<double>(regs.cop0, FS(instr));
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);
OP(double, fs - ft);
}
void Cop1::movs(Registers& regs, u32 instr) {
SetCop1Reg<float>(
regs.cop0,
FD(instr),
GetCop1Reg<float>(
regs.cop0,
FS(instr)
)
);
CheckFPUUsable_PreserveCause();
auto val = GetFGR_FR<u64>(regs.cop0, FS(instr));
SetFGR(FD(instr), val);
}
void Cop1::movd(Registers& regs, u32 instr) {
SetCop1Reg<double>(
regs.cop0,
FD(instr),
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)
)
);
CheckFPUUsable_PreserveCause();
auto val = GetFGR_FS<double>(regs.cop0, FS(instr));
SetFGR_Raw(FD(instr), val);
}
void Cop1::negs(Registers &regs, u32 instr) {
SetCop1Reg<float>(
regs.cop0,
FD(instr),
-GetCop1Reg<float>(
regs.cop0,
FS(instr)
)
);
OP(float, -fs);
}
void Cop1::negd(Registers &regs, u32 instr) {
SetCop1Reg<double>(
regs.cop0,
FD(instr),
-GetCop1Reg<double>(
regs.cop0,
FS(instr)
)
);
OP(double, -ft);
}
void Cop1::sqrts(Registers &regs, u32 instr) {
float fs = GetCop1Reg<float>(regs.cop0, FS(instr));
SetCop1Reg<float>(regs.cop0, FD(instr), std::sqrt(fs));
OP(float, std::sqrt(fs));
}
void Cop1::sqrtd(Registers &regs, u32 instr) {
double fs = GetCop1Reg<double>(regs.cop0, FS(instr));
SetCop1Reg<double>(regs.cop0, FD(instr), std::sqrt(fs));
OP(double, std::sqrt(fs));
}
void Cop1::roundls(Registers& regs, u32 instr) {
float fs = GetCop1Reg<float>(regs.cop0, FS(instr));
PUSHROUNDINGMODE;
SetReg<u64>(regs.cop0, FD(instr), (s32)std::nearbyint(fs));
POPROUNDINGMODE;
CheckFPUUsable();
auto fs = GetFGR_FS<float>(regs.cop0, FS(instr));
CheckCVTArg(fs);
s64 result;
CVT_OP_CheckExcept({ result = std::nearbyint(fs); });
CheckRound(fs, result);
SetFGR(FD(instr), result);
}
void Cop1::roundld(Registers& regs, u32 instr) {
double fs = GetCop1Reg<double>(regs.cop0, FS(instr));
PUSHROUNDINGMODE;
SetReg<u64>(regs.cop0, FD(instr), (s64)std::nearbyint(fs));
POPROUNDINGMODE;
CheckFPUUsable();
auto fs = GetFGR_FS<double>(regs.cop0, FS(instr));
CheckCVTArg(fs);
s64 result;
CVT_OP_CheckExcept({ result = std::nearbyint(fs); });
CheckRound(fs, result);
SetFGR(FD(instr), result);
}
void Cop1::roundws(Registers& regs, u32 instr) {
float fs = GetCop1Reg<float>(regs.cop0, FS(instr));
PUSHROUNDINGMODE;
SetReg<u32>(regs.cop0, FD(instr), (s32)std::nearbyint(fs));
POPROUNDINGMODE;
CheckFPUUsable();
auto fs = GetFGR_FS<float>(regs.cop0, FS(instr));
CheckCVTArg(fs);
s32 result;
CVT_OP_CheckExcept({ result = std::nearbyint(fs); });
CheckRound(fs, result);
SetFGR(FD(instr), result);
}
void Cop1::roundwd(Registers& regs, u32 instr) {
double fs = GetCop1Reg<double>(regs.cop0, FS(instr));
PUSHROUNDINGMODE;
SetReg<u32>(regs.cop0, FD(instr), (s32)std::nearbyint(fs));
POPROUNDINGMODE;
CheckFPUUsable();
auto fs = GetFGR_FS<double>(regs.cop0, FS(instr));
CheckCVTArg(fs);
s32 result;
CVT_OP_CheckExcept({ result = std::nearbyint(fs); });
CheckRound(fs, result);
SetFGR(FD(instr), result);
}
void Cop1::floorls(Registers& regs, u32 instr) {
float fs = GetCop1Reg<float>(regs.cop0, FS(instr));
SetReg<u64>(regs.cop0, FD(instr), (s64)std::floor(fs));
CheckFPUUsable();
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) {
double fs = GetCop1Reg<double>(regs.cop0, FS(instr));
SetReg<u64>(regs.cop0, FD(instr), (s64)std::floor(fs));
CheckFPUUsable();
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) {
float fs = GetCop1Reg<float>(regs.cop0, FS(instr));
SetReg<u32>(regs.cop0, FD(instr), (s64)std::floor(fs));
CheckFPUUsable();
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) {
double fs = GetCop1Reg<double>(regs.cop0, FS(instr));
SetReg<u32>(regs.cop0, FD(instr), (s64)std::floor(fs));
CheckFPUUsable();
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>
void Cop1::lwc1(T &cpu, Mem &mem, u32 instr) {
if constexpr(std::is_same_v<decltype(cpu), Interpreter&>) {
Registers& regs = cpu.regs;
CheckFPUUsable_PreserveCause();
lwc1Interp(cpu.regs, mem, instr);
} else if constexpr (std::is_same_v<decltype(cpu), JIT&>) {
lwc1JIT(cpu, mem, instr);
@@ -462,6 +635,8 @@ template void Cop1::lwc1<JIT>(JIT&, Mem&, u32);
template<class T>
void Cop1::swc1(T &cpu, Mem &mem, u32 instr) {
if constexpr(std::is_same_v<decltype(cpu), Interpreter&>) {
Registers& regs = cpu.regs;
CheckFPUUsable_PreserveCause();
swc1Interp(cpu.regs, mem, instr);
} else if constexpr (std::is_same_v<decltype(cpu), JIT&>) {
swc1JIT(cpu, mem, instr);
@@ -476,6 +651,8 @@ template void Cop1::swc1<JIT>(JIT&, Mem&, u32);
template<class T>
void Cop1::ldc1(T &cpu, Mem &mem, u32 instr) {
if constexpr(std::is_same_v<decltype(cpu), Interpreter&>) {
Registers& regs = cpu.regs;
CheckFPUUsable_PreserveCause();
ldc1Interp(cpu.regs, mem, instr);
} else if constexpr (std::is_same_v<decltype(cpu), JIT&>) {
ldc1JIT(cpu, mem, instr);
@@ -490,6 +667,8 @@ template void Cop1::ldc1<JIT>(JIT&, Mem&, u32);
template<class T>
void Cop1::sdc1(T &cpu, Mem &mem, u32 instr) {
if constexpr(std::is_same_v<decltype(cpu), Interpreter&>) {
Registers& regs = cpu.regs;
CheckFPUUsable_PreserveCause();
sdc1Interp(cpu.regs, mem, instr);
} else if constexpr (std::is_same_v<decltype(cpu), JIT&>) {
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);
} else {
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);
FireException(regs, GetTLBExceptionCode(regs.cop0.tlbError, STORE), 0, true);
} 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) {
if(!regs.cop0.status.cu1) {
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);
} else {
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);
FireException(regs, GetTLBExceptionCode(regs.cop0.tlbError, STORE), 0, true);
} 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) {
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) {
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) {
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) {
SetReg<u64>(regs.cop0, FS(instr), regs.gpr[RT(instr)]);
CheckFPUUsable_PreserveCause();
SetFGR_FR<u64>(regs.cop0, FS(instr), regs.gpr[RT(instr)]);
}
}