diff --git a/external/parallel-rdp/parallel-rdp-standalone b/external/parallel-rdp/parallel-rdp-standalone index 1203303a..63f8935d 160000 --- a/external/parallel-rdp/parallel-rdp-standalone +++ b/external/parallel-rdp/parallel-rdp-standalone @@ -1 +1 @@ -Subproject commit 1203303ac149e9c8326bfd87d75adfc70671c7c3 +Subproject commit 63f8935dbb7bd3ed07ca8547f77498bef09eaa84 diff --git a/src/backend/core/JIT/instructions.cpp b/src/backend/core/JIT/instructions.cpp index 04b9d541..aa50d2c7 100644 --- a/src/backend/core/JIT/instructions.cpp +++ b/src/backend/core/JIT/instructions.cpp @@ -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; } } diff --git a/src/backend/core/interpreter/instructions.cpp b/src/backend/core/interpreter/instructions.cpp index ee3b6f8c..e3f8374d 100644 --- a/src/backend/core/interpreter/instructions.cpp +++ b/src/backend/core/interpreter/instructions.cpp @@ -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) { diff --git a/src/backend/core/registers/Cop0.cpp b/src/backend/core/registers/Cop0.cpp index dc32365f..f8320610 100644 --- a/src/backend/core/registers/Cop0.cpp +++ b/src/backend/core/registers/Cop0.cpp @@ -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; diff --git a/src/backend/core/registers/Cop0.hpp b/src/backend/core/registers/Cop0.hpp index d9b33201..117f68cc 100644 --- a/src/backend/core/registers/Cop0.hpp +++ b/src/backend/core/registers/Cop0.hpp @@ -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 }; diff --git a/src/backend/core/registers/Cop1.cpp b/src/backend/core/registers/Cop1.cpp index 35e3c330..742d9635 100644 --- a/src/backend/core/registers/Cop1.cpp +++ b/src/backend/core/registers/Cop1.cpp @@ -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&, u32); void Cop1::decodeInterp(Interpreter &cpu, u32 instr) { Registers ®s = 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(regs, instr, NGE); break; case 0x3E: ccond(regs, instr, LE); break; case 0x3F: ccond(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(regs, instr, F); break; @@ -142,35 +132,21 @@ void Cop1::decodeInterp(Interpreter &cpu, u32 instr) { case 0x3D: ccond(regs, instr, NGE); break; case 0x3E: ccond(regs, instr, LE); break; case 0x3F: ccond(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); } diff --git a/src/backend/core/registers/Cop1.hpp b/src/backend/core/registers/Cop1.hpp index 9d463289..4d1dba1c 100644 --- a/src/backend/core/registers/Cop1.hpp +++ b/src/backend/core/registers/Cop1.hpp @@ -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 - FORCE_INLINE void SetReg(Cop0& cop0, u8 index, T value) { - if constexpr(sizeof(T) == 4) { + template + FORCE_INLINE T GetFGR_FR(Cop0& cop0, u8 r) { + if constexpr (std::is_same_v || std::is_same_v) { 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 || std::is_same_v) { + 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 - FORCE_INLINE T GetReg(Cop0& cop0, u8 index) { - if constexpr(sizeof(T) == 4) { - if(cop0.status.fr) { - return fgr[index].lo; + template + FORCE_INLINE void SetFGR_FR(Cop0& cop0, u8 r, T value) { + if constexpr (std::is_same_v || std::is_same_v) { + 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 || std::is_same_v) { + 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 + FORCE_INLINE void SetFGR(u8 r, T value) { + fgr[r].raw = value; + } + + template + FORCE_INLINE u64 GetFGR(u8 r) { + if constexpr (std::is_same_v || std::is_same_v) { + return fgr[r].lo; + } else if constexpr (std::is_same_v || std::is_same_v) { + return fgr[r].raw; } } template - FORCE_INLINE void SetCop1Reg(Cop0& cop0, u8 index, T value) { - if constexpr (sizeof(T) == 4) { - u32 raw; - memcpy(&raw, &value, sizeof(T)); - SetReg(cop0, index, raw); - } else if constexpr (sizeof(T) == 8) { - u64 raw; - memcpy(&raw, &value, sizeof(T)); - SetReg(cop0, index, raw); + FORCE_INLINE T GetFGR_FS(Cop0& cop0, u8 fs) { + if constexpr (std::is_same_v || std::is_same_v) { + if (!cop0.status.fr) { + fs &= ~1; + } + return fgr[fs].lo; + } else if constexpr (std::is_same_v || std::is_same_v) { + if (!cop0.status.fr) { + fs &= ~1; + } + return GetFGR_Raw(fs); } } template - FORCE_INLINE T GetCop1Reg(Cop0& cop0, u8 index) { - T value; - if constexpr (sizeof(T) == 4) { - u32 raw = GetReg(cop0, index); - memcpy(&value, &raw, sizeof(T)); - } else if constexpr (sizeof(T) == 8) { - u64 raw = GetReg(cop0, index); - memcpy(&value, &raw, sizeof(T)); + FORCE_INLINE T GetFGR_Raw(u8 r) { + if constexpr (std::is_same_v) { + static_assert(sizeof(float) == sizeof(u32), "float and u32 need to both be 32 bits for this to work."); + auto rawvalue = GetFGR(r); + float floatvalue; + memcpy(&floatvalue, &rawvalue, sizeof(float)); + return floatvalue; + } else if constexpr (std::is_same_v) { + static_assert(sizeof(double) == sizeof(u64), "double and u64 need to both be 64 bits for this to work."); + double doublevalue; + auto rawvalue = GetFGR(r); + memcpy(&doublevalue, &rawvalue, sizeof(double)); + return doublevalue; } - return value; + } + + template + FORCE_INLINE void SetFGR_Raw(u8 r, T val) { + if constexpr (std::is_same_v) { + 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(r, rawvalue); + } else if constexpr (std::is_same_v) { + 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(r, rawvalue); + } + } + + template + FORCE_INLINE T GetFGR_FT(u8 ft) { + return GetFGR_Raw(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); diff --git a/src/backend/core/registers/cop/cop1instructions.cpp b/src/backend/core/registers/cop/cop1instructions.cpp index fd494ec0..40a88697 100644 --- a/src/backend/core/registers/cop/cop1instructions.cpp +++ b/src/backend/core/registers/cop/cop1instructions.cpp @@ -7,6 +7,21 @@ #include 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(regs.cop0, FS(instr)); \ + auto ft = GetFGR_FT(FT(instr)); \ + CheckArg(fs); \ + CheckArg(ft); \ + T result; \ + OP_CheckExcept({result = (op);}); \ + SetFGR_Raw(FD(instr), result); \ +} while(0) + +template +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 +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 +FORCE_INLINE bool isnan(T f) { + if constexpr(std::is_same::value) { + u32 v = F_TO_U32(f); + return ((v & 0x7F800000) == 0x7F800000) && ((v & 0x7FFFFF) != 0); + } else if constexpr(std::is_same::value) { + u64 v = D_TO_U64(f); + return ((v & 0x7FF0000000000000) == 0x7FF0000000000000) && ((v & 0xFFFFFFFFFFFFF) != 0); + } else { + Util::panic("Invalid float type in isnan"); + } +} + +template +FORCE_INLINE bool isqnan(T f) { + if constexpr(std::is_same::value) { + u32 v = F_TO_U32(f); + return (v & 0x7FC00000) == 0x7FC00000; + } else if constexpr(std::is_same::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(regs.cop0, FS(instr)); - SetCop1Reg(regs.cop0, FD(instr), std::abs(fs)); + OP(double, std::abs(fs)); } void Cop1::abss(Registers& regs, u32 instr) { - float fs = GetCop1Reg(regs.cop0, FS(instr)); - SetCop1Reg(regs.cop0, FD(instr), std::abs(fs)); -} - -void Cop1::absw(Registers& regs, u32 instr) { - s32 fs = GetReg(regs.cop0, FS(instr)); - SetReg(regs.cop0, FD(instr), std::abs(fs)); -} - -void Cop1::absl(Registers& regs, u32 instr) { - s64 fs = GetReg(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(regs.cop0, FS(instr)); - float ft = GetCop1Reg(regs.cop0, FT(instr)); - checknanregs(fs, ft); - float result = fs + ft; - SetCop1Reg(regs.cop0, FD(instr), result); + OP(float, fs + ft); } void Cop1::addd(Registers& regs, u32 instr) { - double fs = GetCop1Reg(regs.cop0, FS(instr)); - double ft = GetCop1Reg(regs.cop0, FT(instr)); - checknanregs(fs, ft); - double result = fs + ft; - SetCop1Reg(regs.cop0, FD(instr), result); + OP(double, fs + ft); } void Cop1::ceills(Registers& regs, u32 instr) { - float fs = GetCop1Reg(regs.cop0, FS(instr)); - s64 result = std::ceil(fs); - SetReg(regs.cop0, FD(instr), result); + CheckFPUUsable(); + auto fs = GetFGR_FS(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(regs.cop0, FS(instr)); - s32 result = std::ceil(fs); - SetReg(regs.cop0, FD(instr), result); + CheckFPUUsable(); + auto fs = GetFGR_FS(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(regs.cop0, FS(instr)); - s64 result = std::ceil(fs); - SetReg(regs.cop0, FD(instr), result); + CheckFPUUsable(); + auto fs = GetFGR_FS(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(regs.cop0, FS(instr)); - s32 result = std::ceil(fs); - SetReg(regs.cop0, FD(instr), result); + CheckFPUUsable(); + auto fs = GetFGR_FS(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( - regs.cop0, - FD(instr), - GetCop1Reg( - regs.cop0, - FS(instr) - ) - ); + CheckFPUUsable(); + auto fs = GetFGR_FS(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( - regs.cop0, - FD(instr), - GetCop1Reg( - regs.cop0, - FS(instr) - ) - ); + CheckFPUUsable(); + auto fs = GetFGR_FS(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( - regs.cop0, - FD(instr), - GetCop1Reg( - regs.cop0, - FS(instr) - ) - ); + CheckFPUUsable(); + auto fs = GetFGR_FS(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( - regs.cop0, - FD(instr), - GetCop1Reg( - regs.cop0, - FS(instr) - ) - ); + CheckFPUUsable(); + auto fs = GetFGR_FS(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( - regs.cop0, - FD(instr), - GetCop1Reg( - regs.cop0, - FS(instr) - ) - ); + CheckFPUUsable(); + auto fs = GetFGR_FS(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( - regs.cop0, - FD(instr), - (s64)GetReg( - regs.cop0, - FS(instr) - ) - ); + CheckFPUUsable(); + auto fs = GetFGR_FR(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( - regs.cop0, - FD(instr), - (s32)GetReg( - regs.cop0, - FS(instr) - ) - ); + CheckFPUUsable(); + auto fs = GetFGR_FS(regs.cop0, FS(instr)); + double result; + OP_CheckExcept({ result = double(fs); }); + SetFGR_Raw(FD(instr), result); } void Cop1::cvtsw(Registers& regs, u32 instr) { - SetCop1Reg( - regs.cop0, - FD(instr), - (s32)GetReg( - regs.cop0, - FS(instr) - ) - ); + CheckFPUUsable(); + auto fs = GetFGR_FS(regs.cop0, FS(instr)); + float result; + OP_CheckExcept({ result = float(fs); }); + SetFGR_Raw(FD(instr), result); } void Cop1::cvtdl(Registers& regs, u32 instr) { - SetCop1Reg( - regs.cop0, - FD(instr), - (s64)GetReg( - regs.cop0, - FS(instr) - ) - ); + CheckFPUUsable(); + auto fs = GetFGR_FR(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( - regs.cop0, - FD(instr), - GetCop1Reg( - regs.cop0, - FS(instr) - ) - ); + CheckFPUUsable(); + auto fs = GetFGR_FS(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 -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(cond - 8)); +template +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 void Cop1::ccond(Registers& regs, u32 instr, CompConds cond) { - T fs = GetCop1Reg(regs.cop0, FS(instr)); - T ft = GetCop1Reg(regs.cop0, FT(instr)); - - fcr31.compare = CalculateCondition(regs, fs, ft, cond); + CheckFPUUsable(); + T fs = GetFGR_FS(regs.cop0, FS(instr)); + T ft = GetFGR_FT(FT(instr)); + CheckInvalidRegs(regs, fs, ft, cond); + fcr31.compare = CalculateCondition(fs, ft, cond); } template void Cop1::ccond(Registers& regs, u32 instr, CompConds cond); template void Cop1::ccond(Registers& regs, u32 instr, CompConds cond); void Cop1::divs(Registers ®s, u32 instr) { - float fs = GetCop1Reg(regs.cop0, FS(instr)); - float ft = GetCop1Reg(regs.cop0, FT(instr)); - SetCop1Reg(regs.cop0, FD(instr), fs / ft); + OP(float, fs / ft); } void Cop1::divd(Registers ®s, u32 instr) { - double fs = GetCop1Reg(regs.cop0, FS(instr)); - double ft = GetCop1Reg(regs.cop0, FT(instr)); - SetCop1Reg(regs.cop0, FD(instr), fs / ft); + OP(double, fs / ft); } void Cop1::muls(Registers ®s, u32 instr) { - float fs = GetCop1Reg(regs.cop0, FS(instr)); - float ft = GetCop1Reg(regs.cop0, FT(instr)); - SetCop1Reg(regs.cop0, FD(instr), fs * ft); + OP(float, fs * ft); } void Cop1::muld(Registers& regs, u32 instr) { - double fs = GetCop1Reg(regs.cop0, FS(instr)); - double ft = GetCop1Reg(regs.cop0, FT(instr)); - SetCop1Reg(regs.cop0, FD(instr), fs * ft); -} - -void Cop1::mulw(Registers ®s, u32 instr) { - u32 fs = GetReg(regs.cop0, FS(instr)); - u32 ft = GetReg(regs.cop0, FT(instr)); - SetReg(regs.cop0, FD(instr), fs * ft); -} - -void Cop1::mull(Registers ®s, u32 instr) { - u64 fs = GetReg(regs.cop0, FS(instr)); - u64 ft = GetReg(regs.cop0, FT(instr)); - SetReg(regs.cop0, FD(instr), fs * ft); + OP(double, fs * ft); } void Cop1::subs(Registers ®s, u32 instr) { - float fs = GetCop1Reg(regs.cop0, FS(instr)); - float ft = GetCop1Reg(regs.cop0, FT(instr)); - SetCop1Reg(regs.cop0, FD(instr), fs - ft); + OP(float, fs - ft); } void Cop1::subd(Registers ®s, u32 instr) { - double fs = GetCop1Reg(regs.cop0, FS(instr)); - double ft = GetCop1Reg(regs.cop0, FT(instr)); - SetCop1Reg(regs.cop0, FD(instr), fs - ft); -} - -void Cop1::subw(Registers ®s, u32 instr) { - u32 fs = GetReg(regs.cop0, FS(instr)); - u32 ft = GetReg(regs.cop0, FT(instr)); - SetReg(regs.cop0, FD(instr), fs - ft); -} - -void Cop1::subl(Registers ®s, u32 instr) { - u64 fs = GetReg(regs.cop0, FS(instr)); - u64 ft = GetReg(regs.cop0, FT(instr)); - SetReg(regs.cop0, FD(instr), fs - ft); + OP(double, fs - ft); } void Cop1::movs(Registers& regs, u32 instr) { - SetCop1Reg( - regs.cop0, - FD(instr), - GetCop1Reg( - regs.cop0, - FS(instr) - ) - ); + CheckFPUUsable_PreserveCause(); + auto val = GetFGR_FR(regs.cop0, FS(instr)); + SetFGR(FD(instr), val); } void Cop1::movd(Registers& regs, u32 instr) { - SetCop1Reg( - regs.cop0, - FD(instr), - GetCop1Reg( - regs.cop0, - FS(instr) - ) - ); -} - -void Cop1::movw(Registers& regs, u32 instr) { - SetReg( - regs.cop0, - FD(instr), - GetReg( - regs.cop0, - FS(instr) - ) - ); -} - -void Cop1::movl(Registers& regs, u32 instr) { - SetReg( - regs.cop0, - FD(instr), - GetReg( - regs.cop0, - FS(instr) - ) - ); + CheckFPUUsable_PreserveCause(); + auto val = GetFGR_FS(regs.cop0, FS(instr)); + SetFGR_Raw(FD(instr), val); } void Cop1::negs(Registers ®s, u32 instr) { - SetCop1Reg( - regs.cop0, - FD(instr), - -GetCop1Reg( - regs.cop0, - FS(instr) - ) - ); + OP(float, -fs); } void Cop1::negd(Registers ®s, u32 instr) { - SetCop1Reg( - regs.cop0, - FD(instr), - -GetCop1Reg( - regs.cop0, - FS(instr) - ) - ); + OP(double, -ft); } void Cop1::sqrts(Registers ®s, u32 instr) { - float fs = GetCop1Reg(regs.cop0, FS(instr)); - SetCop1Reg(regs.cop0, FD(instr), std::sqrt(fs)); + OP(float, std::sqrt(fs)); } void Cop1::sqrtd(Registers ®s, u32 instr) { - double fs = GetCop1Reg(regs.cop0, FS(instr)); - SetCop1Reg(regs.cop0, FD(instr), std::sqrt(fs)); + OP(double, std::sqrt(fs)); } void Cop1::roundls(Registers& regs, u32 instr) { - float fs = GetCop1Reg(regs.cop0, FS(instr)); - PUSHROUNDINGMODE; - SetReg(regs.cop0, FD(instr), (s32)std::nearbyint(fs)); - POPROUNDINGMODE; + CheckFPUUsable(); + auto fs = GetFGR_FS(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(regs.cop0, FS(instr)); - PUSHROUNDINGMODE; - SetReg(regs.cop0, FD(instr), (s64)std::nearbyint(fs)); - POPROUNDINGMODE; + CheckFPUUsable(); + auto fs = GetFGR_FS(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(regs.cop0, FS(instr)); - PUSHROUNDINGMODE; - SetReg(regs.cop0, FD(instr), (s32)std::nearbyint(fs)); - POPROUNDINGMODE; + CheckFPUUsable(); + auto fs = GetFGR_FS(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(regs.cop0, FS(instr)); - PUSHROUNDINGMODE; - SetReg(regs.cop0, FD(instr), (s32)std::nearbyint(fs)); - POPROUNDINGMODE; + CheckFPUUsable(); + auto fs = GetFGR_FS(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(regs.cop0, FS(instr)); - SetReg(regs.cop0, FD(instr), (s64)std::floor(fs)); + CheckFPUUsable(); + auto fs = GetFGR_FS(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(regs.cop0, FS(instr)); - SetReg(regs.cop0, FD(instr), (s64)std::floor(fs)); + CheckFPUUsable(); + auto fs = GetFGR_FS(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(regs.cop0, FS(instr)); - SetReg(regs.cop0, FD(instr), (s64)std::floor(fs)); + CheckFPUUsable(); + auto fs = GetFGR_FS(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(regs.cop0, FS(instr)); - SetReg(regs.cop0, FD(instr), (s64)std::floor(fs)); + CheckFPUUsable(); + auto fs = GetFGR_FS(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(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(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(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(regs.cop0, FS(instr)); + CheckCVTArg(fs); + s64 result; + CVT_OP_CheckExcept({ result = std::trunc(fs); }); + CheckRound(fs, result); + SetFGR(FD(instr), result); } template void Cop1::lwc1(T &cpu, Mem &mem, u32 instr) { if constexpr(std::is_same_v) { + Registers& regs = cpu.regs; + CheckFPUUsable_PreserveCause(); lwc1Interp(cpu.regs, mem, instr); } else if constexpr (std::is_same_v) { lwc1JIT(cpu, mem, instr); @@ -462,6 +635,8 @@ template void Cop1::lwc1(JIT&, Mem&, u32); template void Cop1::swc1(T &cpu, Mem &mem, u32 instr) { if constexpr(std::is_same_v) { + Registers& regs = cpu.regs; + CheckFPUUsable_PreserveCause(); swc1Interp(cpu.regs, mem, instr); } else if constexpr (std::is_same_v) { swc1JIT(cpu, mem, instr); @@ -476,6 +651,8 @@ template void Cop1::swc1(JIT&, Mem&, u32); template void Cop1::ldc1(T &cpu, Mem &mem, u32 instr) { if constexpr(std::is_same_v) { + Registers& regs = cpu.regs; + CheckFPUUsable_PreserveCause(); ldc1Interp(cpu.regs, mem, instr); } else if constexpr (std::is_same_v) { ldc1JIT(cpu, mem, instr); @@ -490,6 +667,8 @@ template void Cop1::ldc1(JIT&, Mem&, u32); template void Cop1::sdc1(T &cpu, Mem &mem, u32 instr) { if constexpr(std::is_same_v) { + Registers& regs = cpu.regs; + CheckFPUUsable_PreserveCause(); sdc1Interp(cpu.regs, mem, instr); } else if constexpr (std::is_same_v) { 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(regs.cop0, FT(instr), data); + SetFGR_FR(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(regs.cop0, FT(instr))); + mem.Write32(regs, physical, GetFGR_FR(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(regs.cop0, FT(instr), data); + SetFGR_FR(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(regs.cop0, FT(instr))); + mem.Write64(regs, physical, GetFGR_FR(regs.cop0, FT(instr))); } } -void Cop1::truncws(Registers& regs, u32 instr) { - float fs = GetCop1Reg(regs.cop0, FS(instr)); - s32 result = (s32)std::trunc(fs); - SetReg(regs.cop0, FD(instr), result); -} - -void Cop1::truncwd(Registers& regs, u32 instr) { - double fs = GetCop1Reg(regs.cop0, FS(instr)); - s32 result = (s32)std::trunc(fs); - SetReg(regs.cop0, FD(instr), result); -} - -void Cop1::truncls(Registers& regs, u32 instr) { - float fs = GetCop1Reg(regs.cop0, FS(instr)); - s64 result = (s64)std::trunc(fs); - SetReg(regs.cop0, FD(instr), result); -} - -void Cop1::truncld(Registers& regs, u32 instr) { - double fs = GetCop1Reg(regs.cop0, FS(instr)); - s64 result = (s64)std::trunc(fs); - SetReg(regs.cop0, FD(instr), result); -} - void Cop1::mfc1(Registers& regs, u32 instr) { - regs.gpr[RT(instr)] = (s32)GetReg(regs.cop0, FS(instr)); + CheckFPUUsable_PreserveCause(); + regs.gpr[RT(instr)] = (s32)GetFGR_FR(regs.cop0, FS(instr)); } void Cop1::dmfc1(Registers& regs, u32 instr) { - regs.gpr[RT(instr)] = (s64)GetReg(regs.cop0, FS(instr)); + CheckFPUUsable_PreserveCause(); + regs.gpr[RT(instr)] = (s64)GetFGR_FR(regs.cop0, FS(instr)); } void Cop1::mtc1(Registers& regs, u32 instr) { - SetReg(regs.cop0, FS(instr), regs.gpr[RT(instr)]); + CheckFPUUsable_PreserveCause(); + SetFGR_FR(regs.cop0, FS(instr), regs.gpr[RT(instr)]); } void Cop1::dmtc1(Registers& regs, u32 instr) { - SetReg(regs.cop0, FS(instr), regs.gpr[RT(instr)]); + CheckFPUUsable_PreserveCause(); + SetFGR_FR(regs.cop0, FS(instr), regs.gpr[RT(instr)]); } } diff --git a/src/common.hpp b/src/common.hpp index 196698bb..879cbef7 100644 --- a/src/common.hpp +++ b/src/common.hpp @@ -56,9 +56,6 @@ static FORCE_INLINE constexpr u32 GetVideoFrequency(bool pal) { #define ELEMENT_INDEX(i) (7 - (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__) #define ABI_WINDOWS #else diff --git a/src/frontend/imgui/Settings.cpp b/src/frontend/imgui/Settings.cpp index 8cba5c97..f57117b7 100644 --- a/src/frontend/imgui/Settings.cpp +++ b/src/frontend/imgui/Settings.cpp @@ -29,10 +29,10 @@ Settings::Settings(n64::Core& core) { settings = json::parse(settingsFile); checkjsonentry(oldVolumeL, float, "audio", "volumeL", 0.5); - volumeL = oldVolumeL; checkjsonentry(oldVolumeR, float, "audio", "volumeR", 0.5); - volumeR = oldVolumeR; checkjsonentry(mute, bool, "audio", "mute", false); + volumeL = mute ? 0 : oldVolumeL; + volumeR = mute ? 0 : oldVolumeR; checkjsonentry(lockChannels, bool, "audio", "lockChannels", true); checkjsonentry(jit, bool, "cpu", "enableJIT", false); } else {