diff --git a/src/backend/core/Cache.cpp b/src/backend/core/Cache.cpp new file mode 100644 index 0000000..94bad01 --- /dev/null +++ b/src/backend/core/Cache.cpp @@ -0,0 +1,94 @@ +#include + +namespace n64 { +void InstructionCache::FillLine(u64 vaddr, u32 paddr) { + int index = GetICacheLineIndex(vaddr); + auto &line = lines[index]; + line.valid = true; + line.ptag = paddr & ~0x00000fff; + auto &mmio = Core::GetMem().mmio; + + for (int i = 0; i < 8; i++) { + line.data[i] = mmio.rdp.ReadRDRAM(line.ptag | index); + } +} + +void InstructionCache::StoreTag(u64 vaddr, u32 ptag, Cop0 &cop0) { + auto &line = lines[GetICacheLineIndex(vaddr)]; + + line.valid = (cop0.tagLo.pstate >> 1) & 1; + line.ptag = ptag; +} + +void DataCache::StoreTag(u64 vaddr, u32 ptag, Cop0 &cop0) { + auto &line = lines[GetDCacheLineIndex(vaddr)]; + + line.valid = (cop0.tagLo.pstate >> 1) & 1; + line.dirty = (cop0.tagLo.pstate >> 0) & 1; + line.ptag = ptag; +} + +void InstructionCache::LoadTag(u64 vaddr) { + auto &cop0 = Core::GetRegs().cop0; + auto &line = lines[GetICacheLineIndex(vaddr)]; + cop0.tagLo.pstate = line.valid << 1; + cop0.tagLo.ptaglo = line.ptag; +} + +void DataCache::LoadTag(u64 vaddr) { + auto &cop0 = Core::GetRegs().cop0; + auto &line = lines[GetDCacheLineIndex(vaddr)]; + cop0.tagLo.pstate = (line.valid << 1) | line.dirty; + cop0.tagLo.ptaglo = line.ptag; +} + +template <> +void DataCache::WriteBack(u64 vaddr, u32 paddr) { + auto &mmio = Core::GetMem().mmio; + + DCacheLine &line = lines[GetDCacheLineIndex(vaddr)]; + if (!line.valid) + return; + + u32 origPhysAddr = (line.ptag << 12) | (paddr & 0xfff); + u32 lineStart = GetDCacheLineStart(origPhysAddr); + for (int i = 0; i < 16; i++) { + mmio.rdp.WriteRDRAM(lineStart + i, line.data[i]); + } + + line.dirty = false; +} + +template <> +void DataCache::WriteBack(u64 vaddr, u32 paddr) { + WriteBack(vaddr, paddr); + lines[GetDCacheLineIndex(vaddr)].valid = false; +} + +template <> +void DataCache::WriteBack(u64 vaddr, u32 paddr, u32 ptag) { + DCacheLine &line = lines[GetDCacheLineIndex(vaddr)]; + if (line.ptag == ptag) + WriteBack(vaddr, paddr); +} + +template <> +void DataCache::WriteBack(u64 vaddr, u32 paddr, u32 ptag) { + DCacheLine &line = lines[GetDCacheLineIndex(vaddr)]; + if (line.ptag == ptag) + WriteBack(vaddr, paddr); +} + +void InstructionCache::WriteBack(u64 vaddr, u32 paddr, u32 ptag) { + auto &mmio = Core::GetMem().mmio; + ICacheLine &line = lines[GetICacheLineIndex(vaddr)]; + + if (line.ptag == ptag && line.valid) { + u32 origPhysAddr = (line.ptag << 12) | (paddr & 0xfff); + u32 lineStart = GetICacheLineStart(origPhysAddr); + for (int i = 0; i < 16; i++) { + mmio.rdp.WriteRDRAM(lineStart + i, line.data[i]); + } + } +} +} // namespace n64 diff --git a/src/backend/core/Cache.hpp b/src/backend/core/Cache.hpp index 31e7b5a..49da308 100644 --- a/src/backend/core/Cache.hpp +++ b/src/backend/core/Cache.hpp @@ -1,27 +1,61 @@ #pragma once #include +#include namespace n64 { -struct alignas(32) InstructionCache { +struct alignas(32) ICacheLine { bool valid; u32 data[8]; u32 ptag; - - private: - friend struct Interpreter; - int GetLineIndex(u64 vaddr) { return (vaddr >> 5) & 0x1FF; } - u32 GetLineStart(u64 paddr) { return paddr & ~0x1F; } }; -struct alignas(32) DataCache { +struct alignas(32) DCacheLine { bool valid, dirty; u8 data[16]; u32 ptag; int index; +}; + +struct Cop0; + +inline const u32 GetPhysicalAddressPTag(const u32 paddr) { return paddr >> 12; } +inline const int GetICacheLineIndex(u64 vaddr) { return (vaddr >> 5) & 0x1FF; } +inline const u32 GetICacheLineStart(u64 paddr) { return paddr & ~0x1F; } +inline const int GetDCacheLineIndex(u64 vaddr) { return (vaddr >> 4) & 0x1FF; } +inline const u32 GetDCacheLineStart(u64 paddr) { return paddr & ~0xF; } + +struct InstructionCache { + std::array lines; + + void FillLine(u64, u32); + void InvalidateIndex(u64 vaddr) { lines[GetICacheLineIndex(vaddr)].valid = false; } + void StoreTag(u64, u32, Cop0 &); + void LoadTag(u64 vaddr); + + void WriteBack(u64 vaddr, u32 paddr, u32 ptag); + void InvalidateIndex(u64 vaddr, u32 ptag) { + int lineIndex = GetICacheLineIndex(vaddr); + if (lines[lineIndex].valid && lines[lineIndex].ptag == ptag) + lines[lineIndex].valid = false; + } private: - friend struct Interpreter; - int GetLineIndex(u64 vaddr) { return (vaddr >> 4) & 0x1FF; } - u32 GetLineStart(u64 paddr) { return paddr & ~0xF; } +}; + +struct DataCache { + std::array lines; + + void StoreTag(u64, u32, Cop0 &); + void LoadTag(u64 vaddr); + template + void WriteBack(u64 vaddr, u32 paddr); + template + void WriteBack(u64 vaddr, u32 paddr, u32 ptag); + void InvalidateIndex(u64 vaddr) { lines[GetDCacheLineIndex(vaddr)].valid = false; } + void InvalidateIndex(u64 vaddr, u32 ptag) { + int lineIndex = GetDCacheLineIndex(vaddr); + if (lines[lineIndex].valid && lines[lineIndex].ptag == ptag) + lines[lineIndex].valid = false; + } }; } // namespace n64 diff --git a/src/backend/core/Interpreter.cpp b/src/backend/core/Interpreter.cpp index a389dd1..4bdcceb 100644 --- a/src/backend/core/Interpreter.cpp +++ b/src/backend/core/Interpreter.cpp @@ -1,58 +1,58 @@ #include namespace n64 { -Interpreter::Interpreter(Mem& mem, Registers& regs) : regs(regs), mem(mem) {} +Interpreter::Interpreter(Mem &mem, Registers ®s) : regs(regs), mem(mem) {} bool Interpreter::ShouldServiceInterrupt() const { - const bool interrupts_pending = (regs.cop0.status.im & regs.cop0.cause.interruptPending) != 0; - const bool interrupts_enabled = regs.cop0.status.ie == 1; - const bool currently_handling_exception = regs.cop0.status.exl == 1; - const bool currently_handling_error = regs.cop0.status.erl == 1; + const bool interrupts_pending = (regs.cop0.status.im & regs.cop0.cause.interruptPending) != 0; + const bool interrupts_enabled = regs.cop0.status.ie == 1; + const bool currently_handling_exception = regs.cop0.status.exl == 1; + const bool currently_handling_error = regs.cop0.status.erl == 1; - return interrupts_pending && interrupts_enabled && !currently_handling_exception && !currently_handling_error; + return interrupts_pending && interrupts_enabled && !currently_handling_exception && !currently_handling_error; } void Interpreter::CheckCompareInterrupt() const { - regs.cop0.count++; - regs.cop0.count &= 0x1FFFFFFFF; - if (regs.cop0.count == static_cast(regs.cop0.compare) << 1) { - regs.cop0.cause.ip7 = 1; - mem.mmio.mi.UpdateInterrupt(); - } + regs.cop0.count++; + regs.cop0.count &= 0x1FFFFFFFF; + if (regs.cop0.count == static_cast(regs.cop0.compare) << 1) { + regs.cop0.cause.ip7 = 1; + mem.mmio.mi.UpdateInterrupt(); + } } u32 Interpreter::Step() { - CheckCompareInterrupt(); + CheckCompareInterrupt(); - regs.prevDelaySlot = regs.delaySlot; - regs.delaySlot = false; + regs.prevDelaySlot = regs.delaySlot; + regs.delaySlot = false; + + if (check_address_error(0b11, u64(regs.pc))) [[unlikely]] { + regs.cop0.HandleTLBException(regs.pc); + regs.cop0.FireException(Cop0::ExceptionCode::AddressErrorLoad, 0, regs.pc); + return 1; + } + + u32 paddr = 0; + if (!regs.cop0.MapVAddr(Cop0::LOAD, regs.pc, paddr)) { + regs.cop0.HandleTLBException(regs.pc); + regs.cop0.FireException(Cop0::GetTLBExceptionCode(regs.cop0.tlbError, Cop0::LOAD), 0, regs.pc); + return 1; + } + + const u32 instruction = mem.Read(paddr); + + if (ShouldServiceInterrupt()) { + regs.cop0.FireException(Cop0::ExceptionCode::Interrupt, 0, regs.pc); + return 1; + } + + regs.oldPC = regs.pc; + regs.pc = regs.nextPC; + regs.nextPC += 4; + + Exec(instruction); - if (check_address_error(0b11, u64(regs.pc))) [[unlikely]] { - regs.cop0.HandleTLBException(regs.pc); - regs.cop0.FireException(ExceptionCode::AddressErrorLoad, 0, regs.pc); return 1; - } - - u32 paddr = 0; - if (!regs.cop0.MapVAddr(Cop0::LOAD, regs.pc, paddr)) { - regs.cop0.HandleTLBException(regs.pc); - regs.cop0.FireException(Cop0::GetTLBExceptionCode(regs.cop0.tlbError, Cop0::LOAD), 0, regs.pc); - return 1; - } - - const u32 instruction = mem.Read(paddr); - - if (ShouldServiceInterrupt()) { - regs.cop0.FireException(ExceptionCode::Interrupt, 0, regs.pc); - return 1; - } - - regs.oldPC = regs.pc; - regs.pc = regs.nextPC; - regs.nextPC += 4; - - Exec(instruction); - - return 1; } } // namespace n64 diff --git a/src/backend/core/Interpreter.hpp b/src/backend/core/Interpreter.hpp index 03e4b65..a90dd10 100644 --- a/src/backend/core/Interpreter.hpp +++ b/src/backend/core/Interpreter.hpp @@ -20,8 +20,8 @@ struct Interpreter final { u64 cop2Latch{}; friend struct Cop1; - void cache_type_data(u8); - void cache_type_instruction(u8); + void cache_type_data(u8, u64, u32, u32); + void cache_type_instruction(u8, u64, u32, u32); #define check_address_error(mask, vaddr) \ (((!regs.cop0.is64BitAddressing) && (s32)(vaddr) != (vaddr)) || (((vaddr) & (mask)) != 0)) [[nodiscard]] bool ShouldServiceInterrupt() const; diff --git a/src/backend/core/JIT.cpp b/src/backend/core/JIT.cpp index 8122ecc..f1d0b57 100644 --- a/src/backend/core/JIT.cpp +++ b/src/backend/core/JIT.cpp @@ -43,7 +43,7 @@ std::optional JIT::FetchInstruction(s64 vaddr) { if (check_address_error(0b11, vaddr)) [[unlikely]] { /*regs.cop0.HandleTLBException(blockPC); - regs.cop0.FireException(ExceptionCode::AddressErrorLoad, 0, blockPC); + regs.cop0.FireException(Cop0::ExceptionCode::AddressErrorLoad, 0, blockPC); return 1;*/ Util::Error::GetInstance().Throw({Util::Error::Severity::NON_FATAL}, {Util::Error::Type::UNHANDLED_EXCEPTION}, blockPC, {}, diff --git a/src/backend/core/interpreter/cop0instructions.cpp b/src/backend/core/interpreter/cop0instructions.cpp index 484316f..5c2f5ce 100644 --- a/src/backend/core/interpreter/cop0instructions.cpp +++ b/src/backend/core/interpreter/cop0instructions.cpp @@ -4,90 +4,90 @@ namespace n64 { void Cop0::mtc0(const Instruction instr) { - Registers& regs = Core::GetRegs(); - SetReg32(instr.rd(), regs.Read(instr.rt())); + Registers ®s = Core::GetRegs(); + SetReg32(instr.rd(), regs.Read(instr.rt())); } -void Cop0::dmtc0(const Instruction instr) { - Registers& regs = Core::GetRegs(); - SetReg64(instr.rd(), regs.Read(instr.rt())); +void Cop0::dmtc0(const Instruction instr) { + Registers ®s = Core::GetRegs(); + SetReg64(instr.rd(), regs.Read(instr.rt())); } -void Cop0::mfc0(const Instruction instr) { - Registers& regs = Core::GetRegs(); - regs.Write(instr.rt(), s32(GetReg32(instr.rd()))); +void Cop0::mfc0(const Instruction instr) { + Registers ®s = Core::GetRegs(); + regs.Write(instr.rt(), s32(GetReg32(instr.rd()))); } -void Cop0::dmfc0(const Instruction instr) const { - Registers& regs = Core::GetRegs(); - regs.Write(instr.rt(), s64(GetReg64(instr.rd()))); +void Cop0::dmfc0(const Instruction instr) const { + Registers ®s = Core::GetRegs(); + regs.Write(instr.rt(), s64(GetReg64(instr.rd()))); } void Cop0::eret() { - Registers& regs = Core::GetRegs(); - if (!regs.cop0.kernelMode) { - FireException(ExceptionCode::CoprocessorUnusable, 0, regs.oldPC); - return; - } - if (status.erl) { - regs.SetPC64(ErrorEPC); - status.erl = false; - } else { - regs.SetPC64(EPC); - status.exl = false; - } - regs.cop0.Update(); - llbit = false; + Registers ®s = Core::GetRegs(); + if (!regs.cop0.kernelMode) { + FireException(Cop0::ExceptionCode::CoprocessorUnusable, 0, regs.oldPC); + return; + } + if (status.erl) { + regs.SetPC64(ErrorEPC); + status.erl = false; + } else { + regs.SetPC64(EPC); + status.exl = false; + } + regs.cop0.Update(); + llbit = false; } void Cop0::tlbr() { - if (index.i >= 32) { - panic("TLBR with TLB index {}", index.i); - } + if (index.i >= 32) { + panic("TLBR with TLB index {}", index.i); + } - const TLBEntry entry = tlb[index.i]; + const TLBEntry entry = tlb[index.i]; - entryHi.raw = entry.entryHi.raw; - entryLo0.raw = entry.entryLo0.raw & 0x3FFFFFFF; - entryLo1.raw = entry.entryLo1.raw & 0x3FFFFFFF; + entryHi.raw = entry.entryHi.raw; + entryLo0.raw = entry.entryLo0.raw & 0x3FFFFFFF; + entryLo1.raw = entry.entryLo1.raw & 0x3FFFFFFF; - entryLo0.g = entry.global; - entryLo1.g = entry.global; - pageMask.raw = entry.pageMask.raw; + entryLo0.g = entry.global; + entryLo1.g = entry.global; + pageMask.raw = entry.pageMask.raw; } void Cop0::tlbw(const int index_) { - PageMask page_mask{}; - page_mask = pageMask; - const u32 top = page_mask.mask & 0xAAA; - page_mask.mask = top | (top >> 1); + PageMask page_mask{}; + page_mask = pageMask; + const u32 top = page_mask.mask & 0xAAA; + page_mask.mask = top | (top >> 1); - if (index_ >= 32) { - panic("TLBWI with TLB index {}", index_); - } + if (index_ >= 32) { + panic("TLBWI with TLB index {}", index_); + } - tlb[index_].entryHi.raw = entryHi.raw; - tlb[index_].entryHi.vpn2 &= ~page_mask.mask; + tlb[index_].entryHi.raw = entryHi.raw; + tlb[index_].entryHi.vpn2 &= ~page_mask.mask; - tlb[index_].entryLo0.raw = entryLo0.raw & 0x03FFFFFE; - tlb[index_].entryLo1.raw = entryLo1.raw & 0x03FFFFFE; - tlb[index_].pageMask.raw = page_mask.raw; + tlb[index_].entryLo0.raw = entryLo0.raw & 0x03FFFFFE; + tlb[index_].entryLo1.raw = entryLo1.raw & 0x03FFFFFE; + tlb[index_].pageMask.raw = page_mask.raw; - tlb[index_].global = entryLo0.g && entryLo1.g; - tlb[index_].initialized = true; + tlb[index_].global = entryLo0.g && entryLo1.g; + tlb[index_].initialized = true; } void Cop0::tlbp() { - int match = -1; - const TLBEntry *entry = TLBTryMatch(entryHi.raw, match); - if (match >= 0) { - index.raw = match; - return; - } + int match = -1; + const TLBEntry *entry = TLBTryMatch(entryHi.raw, match); + if (match >= 0) { + index.raw = match; + return; + } - index.raw = 0; - index.p = 1; + index.raw = 0; + index.p = 1; } } // namespace n64 diff --git a/src/backend/core/interpreter/cop1instructions.cpp b/src/backend/core/interpreter/cop1instructions.cpp index 530f2ed..3f702ac 100644 --- a/src/backend/core/interpreter/cop1instructions.cpp +++ b/src/backend/core/interpreter/cop1instructions.cpp @@ -5,377 +5,378 @@ namespace n64 { template <> -auto Cop1::FGR_T(const Cop0Status &status, const u32 index) -> s32 & { - if (status.fr) { - return fgr[index].int32; - } +auto Cop1::FGR_T(const Cop0::Status &status, const u32 index) -> s32 & { + if (status.fr) { + return fgr[index].int32; + } - if (index & 1) { - return fgr[index & ~1].int32h; - } + if (index & 1) { + return fgr[index & ~1].int32h; + } - return fgr[index & ~1].int32; + return fgr[index & ~1].int32; } template <> -auto Cop1::FGR_T(const Cop0Status &status, const u32 index) -> u32 & { - return reinterpret_cast(FGR_T(status, index)); +auto Cop1::FGR_T(const Cop0::Status &status, const u32 index) -> u32 & { + return reinterpret_cast(FGR_T(status, index)); } template <> -auto Cop1::FGR_T(const Cop0Status &, const u32 index) -> float & { - return fgr[index].float32; -} - -template <> -auto Cop1::FGR_T(const Cop0Status &status, const u32 index) -> s64 & { - if (status.fr) { - return fgr[index].int64; - } - - return fgr[index & ~1].int64; -} - -template <> -auto Cop1::FGR_T(const Cop0Status &status, const u32 index) -> u64 & { - return reinterpret_cast(FGR_T(status, index)); -} - -template <> -auto Cop1::FGR_T(const Cop0Status &, const u32 index) -> double & { - return fgr[index].float64; -} - -template <> -auto Cop1::FGR_S(const Cop0Status &status, const u32 index) -> s32 & { - if (status.fr) { - return fgr[index].int32; - } - - return fgr[index & ~1].int32; -} - -template <> -auto Cop1::FGR_S(const Cop0Status &status, const u32 index) -> u32 & { - return reinterpret_cast(FGR_S(status, index)); -} - -template <> -auto Cop1::FGR_S(const Cop0Status &status, const u32 index) -> float & { - if (status.fr) { +auto Cop1::FGR_T(const Cop0::Status &, const u32 index) -> float & { return fgr[index].float32; - } - - return fgr[index & ~1].float32; } template <> -auto Cop1::FGR_S(const Cop0Status &status, const u32 index) -> s64 & { - return FGR_T(status, index); +auto Cop1::FGR_T(const Cop0::Status &status, const u32 index) -> s64 & { + if (status.fr) { + return fgr[index].int64; + } + + return fgr[index & ~1].int64; } template <> -auto Cop1::FGR_S(const Cop0Status &status, const u32 index) -> u64 & { - return reinterpret_cast(FGR_S(status, index)); +auto Cop1::FGR_T(const Cop0::Status &status, const u32 index) -> u64 & { + return reinterpret_cast(FGR_T(status, index)); } template <> -auto Cop1::FGR_S(const Cop0Status &status, const u32 index) -> double & { - if (status.fr) { +auto Cop1::FGR_T(const Cop0::Status &, const u32 index) -> double & { return fgr[index].float64; - } - - return fgr[index & ~1].float64; } template <> -auto Cop1::FGR_D(const Cop0Status &, const u32 index) -> s32 & { - fgr[index].int32h = 0; - return fgr[index].int32; +auto Cop1::FGR_S(const Cop0::Status &status, const u32 index) -> s32 & { + if (status.fr) { + return fgr[index].int32; + } + + return fgr[index & ~1].int32; } template <> -auto Cop1::FGR_D(const Cop0Status &status, const u32 index) -> u32 & { - return reinterpret_cast(FGR_D(status, index)); +auto Cop1::FGR_S(const Cop0::Status &status, const u32 index) -> u32 & { + return reinterpret_cast(FGR_S(status, index)); } template <> -auto Cop1::FGR_D(const Cop0Status &, const u32 index) -> float & { - fgr[index].float32h = 0; - return fgr[index].float32; +auto Cop1::FGR_S(const Cop0::Status &status, const u32 index) -> float & { + if (status.fr) { + return fgr[index].float32; + } + + return fgr[index & ~1].float32; } template <> -auto Cop1::FGR_D(const Cop0Status &, const u32 index) -> s64 & { - return fgr[index].int64; +auto Cop1::FGR_S(const Cop0::Status &status, const u32 index) -> s64 & { + return FGR_T(status, index); } template <> -auto Cop1::FGR_D(const Cop0Status &status, const u32 index) -> u64 & { - return reinterpret_cast(FGR_D(status, index)); +auto Cop1::FGR_S(const Cop0::Status &status, const u32 index) -> u64 & { + return reinterpret_cast(FGR_S(status, index)); } template <> -auto Cop1::FGR_D(const Cop0Status &status, const u32 index) -> double & { - return FGR_T(status, index); +auto Cop1::FGR_S(const Cop0::Status &status, const u32 index) -> double & { + if (status.fr) { + return fgr[index].float64; + } + + return fgr[index & ~1].float64; +} + +template <> +auto Cop1::FGR_D(const Cop0::Status &, const u32 index) -> s32 & { + fgr[index].int32h = 0; + return fgr[index].int32; +} + +template <> +auto Cop1::FGR_D(const Cop0::Status &status, const u32 index) -> u32 & { + return reinterpret_cast(FGR_D(status, index)); +} + +template <> +auto Cop1::FGR_D(const Cop0::Status &, const u32 index) -> float & { + fgr[index].float32h = 0; + return fgr[index].float32; +} + +template <> +auto Cop1::FGR_D(const Cop0::Status &, const u32 index) -> s64 & { + return fgr[index].int64; +} + +template <> +auto Cop1::FGR_D(const Cop0::Status &status, const u32 index) -> u64 & { + return reinterpret_cast(FGR_D(status, index)); +} + +template <> +auto Cop1::FGR_D(const Cop0::Status &status, const u32 index) -> double & { + return FGR_T(status, index); } template <> bool Cop1::isqnan(const float f) { - return std::bit_cast(f) >> 22 & 1; + return std::bit_cast(f) >> 22 & 1; } template <> bool Cop1::isqnan(const double f) { - return std::bit_cast(f) >> 51 & 1; + return std::bit_cast(f) >> 51 & 1; } template <> bool Cop1::CheckCVTArg(const float f) { - Registers& regs = Core::GetRegs(); - switch (std::fpclassify(f)) { - case FP_SUBNORMAL: - case FP_INFINITE: - case FP_NAN: - SetCauseUnimplemented(); - regs.cop0.FireException(ExceptionCode::FloatingPointError, 0, regs.oldPC); - return false; - } + Registers ®s = Core::GetRegs(); + switch (std::fpclassify(f)) { + case FP_SUBNORMAL: + case FP_INFINITE: + case FP_NAN: + SetCauseUnimplemented(); + regs.cop0.FireException(Cop0::ExceptionCode::FloatingPointError, 0, regs.oldPC); + return false; + } - if (f >= 0x1p+31f || f < -0x1p+31f) { - SetCauseUnimplemented(); - regs.cop0.FireException(ExceptionCode::FloatingPointError, 0, regs.oldPC); - return false; - } + if (f >= 0x1p+31f || f < -0x1p+31f) { + SetCauseUnimplemented(); + regs.cop0.FireException(Cop0::ExceptionCode::FloatingPointError, 0, regs.oldPC); + return false; + } - return true; + return true; } template <> bool Cop1::CheckCVTArg(const double f) { - Registers& regs = Core::GetRegs(); - switch (std::fpclassify(f)) { - case FP_SUBNORMAL: - case FP_INFINITE: - case FP_NAN: - SetCauseUnimplemented(); - regs.cop0.FireException(ExceptionCode::FloatingPointError, 0, regs.oldPC); - return false; - } + Registers ®s = Core::GetRegs(); + switch (std::fpclassify(f)) { + case FP_SUBNORMAL: + case FP_INFINITE: + case FP_NAN: + SetCauseUnimplemented(); + regs.cop0.FireException(Cop0::ExceptionCode::FloatingPointError, 0, regs.oldPC); + return false; + } - if ((f >= 0x1p+31 || f < -0x1p+31)) { - SetCauseUnimplemented(); - regs.cop0.FireException(ExceptionCode::FloatingPointError, 0, regs.oldPC); - return false; - } + if ((f >= 0x1p+31 || f < -0x1p+31)) { + SetCauseUnimplemented(); + regs.cop0.FireException(Cop0::ExceptionCode::FloatingPointError, 0, regs.oldPC); + return false; + } - return true; + return true; } template <> bool Cop1::CheckCVTArg(const float f) { - Registers& regs = Core::GetRegs(); - switch (std::fpclassify(f)) { - case FP_SUBNORMAL: - case FP_INFINITE: - case FP_NAN: - SetCauseUnimplemented(); - regs.cop0.FireException(ExceptionCode::FloatingPointError, 0, regs.oldPC); - return false; - } + Registers ®s = Core::GetRegs(); + switch (std::fpclassify(f)) { + case FP_SUBNORMAL: + case FP_INFINITE: + case FP_NAN: + SetCauseUnimplemented(); + regs.cop0.FireException(Cop0::ExceptionCode::FloatingPointError, 0, regs.oldPC); + return false; + } - if ((f >= 0x1p+53f || f <= -0x1p+53f)) { - SetCauseUnimplemented(); - regs.cop0.FireException(ExceptionCode::FloatingPointError, 0, regs.oldPC); - return false; - } + if ((f >= 0x1p+53f || f <= -0x1p+53f)) { + SetCauseUnimplemented(); + regs.cop0.FireException(Cop0::ExceptionCode::FloatingPointError, 0, regs.oldPC); + return false; + } - return true; + return true; } template <> bool Cop1::CheckCVTArg(const double f) { - Registers& regs = Core::GetRegs(); - switch (std::fpclassify(f)) { - case FP_SUBNORMAL: - case FP_INFINITE: - case FP_NAN: - SetCauseUnimplemented(); - regs.cop0.FireException(ExceptionCode::FloatingPointError, 0, regs.oldPC); - return false; - } + Registers ®s = Core::GetRegs(); + switch (std::fpclassify(f)) { + case FP_SUBNORMAL: + case FP_INFINITE: + case FP_NAN: + SetCauseUnimplemented(); + regs.cop0.FireException(Cop0::ExceptionCode::FloatingPointError, 0, regs.oldPC); + return false; + } - if ((f >= 0x1p+53 || f <= -0x1p+53)) { - SetCauseUnimplemented(); - regs.cop0.FireException(ExceptionCode::FloatingPointError, 0, regs.oldPC); - return false; - } + if ((f >= 0x1p+53 || f <= -0x1p+53)) { + SetCauseUnimplemented(); + regs.cop0.FireException(Cop0::ExceptionCode::FloatingPointError, 0, regs.oldPC); + return false; + } - return true; + return true; } template bool Cop1::CheckArg(const T f) { - Registers& regs = Core::GetRegs(); - switch (std::fpclassify(f)) { - case FP_SUBNORMAL: - SetCauseUnimplemented(); - regs.cop0.FireException(ExceptionCode::FloatingPointError, 0, regs.oldPC); - return false; - case FP_NAN: - if (isqnan(f) ? SetCauseInvalid() : (SetCauseUnimplemented(), true)) { - regs.cop0.FireException(ExceptionCode::FloatingPointError, 0, regs.oldPC); - return false; + Registers ®s = Core::GetRegs(); + switch (std::fpclassify(f)) { + case FP_SUBNORMAL: + SetCauseUnimplemented(); + regs.cop0.FireException(Cop0::ExceptionCode::FloatingPointError, 0, regs.oldPC); + return false; + case FP_NAN: + if (isqnan(f) ? SetCauseInvalid() : (SetCauseUnimplemented(), true)) { + regs.cop0.FireException(Cop0::ExceptionCode::FloatingPointError, 0, regs.oldPC); + return false; + } + return true; } return true; - } - return true; } template bool Cop1::CheckArgs(const T f1, const T f2) { - Registers& regs = Core::GetRegs(); - auto class1 = std::fpclassify(f1), class2 = std::fpclassify(f2); - if ((class1 == FP_NAN && !isqnan(f1)) || (class2 == FP_NAN && !isqnan(f2))) { - SetCauseUnimplemented(); - regs.cop0.FireException(ExceptionCode::FloatingPointError, 0, regs.oldPC); - return false; - } - - if (class1 == FP_SUBNORMAL || class2 == FP_SUBNORMAL) { - SetCauseUnimplemented(); - regs.cop0.FireException(ExceptionCode::FloatingPointError, 0, regs.oldPC); - return false; - } - - if ((class1 == FP_NAN && isqnan(f1)) || (class2 == FP_NAN && isqnan(f2))) { - if (SetCauseInvalid()) { - regs.cop0.FireException(ExceptionCode::FloatingPointError, 0, regs.oldPC); - return false; + Registers ®s = Core::GetRegs(); + auto class1 = std::fpclassify(f1), class2 = std::fpclassify(f2); + if ((class1 == FP_NAN && !isqnan(f1)) || (class2 == FP_NAN && !isqnan(f2))) { + SetCauseUnimplemented(); + regs.cop0.FireException(Cop0::ExceptionCode::FloatingPointError, 0, regs.oldPC); + return false; } - } - return true; + if (class1 == FP_SUBNORMAL || class2 == FP_SUBNORMAL) { + SetCauseUnimplemented(); + regs.cop0.FireException(Cop0::ExceptionCode::FloatingPointError, 0, regs.oldPC); + return false; + } + + if ((class1 == FP_NAN && isqnan(f1)) || (class2 == FP_NAN && isqnan(f2))) { + if (SetCauseInvalid()) { + regs.cop0.FireException(Cop0::ExceptionCode::FloatingPointError, 0, regs.oldPC); + return false; + } + } + + return true; } -template<> +template <> bool Cop1::CheckFPUUsable() { - Registers& regs = Core::GetRegs(); - if (!regs.cop0.status.cu1) { - regs.cop0.FireException(ExceptionCode::CoprocessorUnusable, 1, regs.oldPC); - return false; - } + Registers ®s = Core::GetRegs(); + if (!regs.cop0.status.cu1) { + regs.cop0.FireException(Cop0::ExceptionCode::CoprocessorUnusable, 1, regs.oldPC); + return false; + } - return true; + return true; } -template<> +template <> bool Cop1::CheckFPUUsable() { - if (!CheckFPUUsable()) return false; - fcr31.cause = {}; - return true; + if (!CheckFPUUsable()) + return false; + fcr31.cause = {}; + return true; } template FORCE_INLINE T FlushResult(T f, const u32 round) { - switch (round) { - case FE_TONEAREST: - case FE_TOWARDZERO: - return std::copysign(T(), f); - case FE_UPWARD: - return std::signbit(f) ? -T() : std::numeric_limits::min(); - case FE_DOWNWARD: - return std::signbit(f) ? -std::numeric_limits::min() : T(); - default: - __builtin_unreachable(); - } + switch (round) { + case FE_TONEAREST: + case FE_TOWARDZERO: + return std::copysign(T(), f); + case FE_UPWARD: + return std::signbit(f) ? -T() : std::numeric_limits::min(); + case FE_DOWNWARD: + return std::signbit(f) ? -std::numeric_limits::min() : T(); + default: + __builtin_unreachable(); + } } template <> bool Cop1::CheckResult(float &f) { - Registers& regs = Core::GetRegs(); - switch (std::fpclassify(f)) { - case FP_SUBNORMAL: - if (!fcr31.fs || fcr31.enable.underflow || fcr31.enable.inexact_operation) { - SetCauseUnimplemented(); - regs.cop0.FireException(ExceptionCode::FloatingPointError, 0, regs.oldPC); - return false; + Registers ®s = Core::GetRegs(); + switch (std::fpclassify(f)) { + case FP_SUBNORMAL: + if (!fcr31.fs || fcr31.enable.underflow || fcr31.enable.inexact_operation) { + SetCauseUnimplemented(); + regs.cop0.FireException(Cop0::ExceptionCode::FloatingPointError, 0, regs.oldPC); + return false; + } + SetCauseUnderflow(); + SetCauseInexact(); + f = FlushResult(f, std::fegetround()); + return true; + case FP_NAN: + { + f = std::bit_cast(0x7fbf'ffff); + return true; + } } - SetCauseUnderflow(); - SetCauseInexact(); - f = FlushResult(f, std::fegetround()); return true; - case FP_NAN: - { - f = std::bit_cast(0x7fbf'ffff); - return true; - } - } - return true; } template <> bool Cop1::CheckResult(double &f) { - Registers& regs = Core::GetRegs(); - switch (std::fpclassify(f)) { - case FP_SUBNORMAL: - if (!fcr31.fs || fcr31.enable.underflow || fcr31.enable.inexact_operation) { - SetCauseUnimplemented(); - regs.cop0.FireException(ExceptionCode::FloatingPointError, 0, regs.oldPC); - return false; + Registers ®s = Core::GetRegs(); + switch (std::fpclassify(f)) { + case FP_SUBNORMAL: + if (!fcr31.fs || fcr31.enable.underflow || fcr31.enable.inexact_operation) { + SetCauseUnimplemented(); + regs.cop0.FireException(Cop0::ExceptionCode::FloatingPointError, 0, regs.oldPC); + return false; + } + SetCauseUnderflow(); + SetCauseInexact(); + f = FlushResult(f, fegetround()); + return true; + case FP_NAN: + { + f = std::bit_cast(0x7ff7'ffff'ffff'ffff); + return true; + } } - SetCauseUnderflow(); - SetCauseInexact(); - f = FlushResult(f, fegetround()); return true; - case FP_NAN: - { - f = std::bit_cast(0x7ff7'ffff'ffff'ffff); - return true; - } - } - return true; } template bool Cop1::TestExceptions() { - Registers& regs = Core::GetRegs(); - const u32 exc = std::fetestexcept(FE_ALL_EXCEPT); + Registers ®s = Core::GetRegs(); + const u32 exc = std::fetestexcept(FE_ALL_EXCEPT); - if (!exc) - return false; + if (!exc) + return false; - if constexpr (cvt) { - if (exc & FE_INVALID) { - SetCauseUnimplemented(); - regs.cop0.FireException(ExceptionCode::FloatingPointError, 0, regs.oldPC); - return true; + if constexpr (cvt) { + if (exc & FE_INVALID) { + SetCauseUnimplemented(); + regs.cop0.FireException(Cop0::ExceptionCode::FloatingPointError, 0, regs.oldPC); + return true; + } } - } - if (exc & FE_UNDERFLOW) { - if (!fcr31.fs || fcr31.enable.underflow || fcr31.enable.inexact_operation) { - SetCauseUnimplemented(); - regs.cop0.FireException(ExceptionCode::FloatingPointError, 0, regs.oldPC); - return true; + if (exc & FE_UNDERFLOW) { + if (!fcr31.fs || fcr31.enable.underflow || fcr31.enable.inexact_operation) { + SetCauseUnimplemented(); + regs.cop0.FireException(Cop0::ExceptionCode::FloatingPointError, 0, regs.oldPC); + return true; + } } - } - bool raise = false; - if (exc & FE_DIVBYZERO) - raise |= SetCauseDivisionByZero(); - if (exc & FE_INEXACT) { - raise |= SetCauseInexact(); - } - if (exc & FE_UNDERFLOW) - raise |= SetCauseUnderflow(); - if (exc & FE_OVERFLOW) - raise |= SetCauseOverflow(); - if (exc & FE_INVALID) - raise |= SetCauseInvalid(); - if (raise) - regs.cop0.FireException(ExceptionCode::FloatingPointError, 0, regs.oldPC); - return raise; + bool raise = false; + if (exc & FE_DIVBYZERO) + raise |= SetCauseDivisionByZero(); + if (exc & FE_INEXACT) { + raise |= SetCauseInexact(); + } + if (exc & FE_UNDERFLOW) + raise |= SetCauseUnderflow(); + if (exc & FE_OVERFLOW) + raise |= SetCauseOverflow(); + if (exc & FE_INVALID) + raise |= SetCauseInvalid(); + if (raise) + regs.cop0.FireException(Cop0::ExceptionCode::FloatingPointError, 0, regs.oldPC); + return raise; } template bool Cop1::TestExceptions(); @@ -384,58 +385,58 @@ template bool Cop1::TestExceptions(); void Cop1::SetCauseUnimplemented() { fcr31.cause.unimplemented_operation = true; } bool Cop1::SetCauseUnderflow() { - fcr31.cause.underflow = true; - if (fcr31.enable.underflow) - return true; - fcr31.flag.underflow = true; - return false; + fcr31.cause.underflow = true; + if (fcr31.enable.underflow) + return true; + fcr31.flag.underflow = true; + return false; } bool Cop1::SetCauseInexact() { - fcr31.cause.inexact_operation = true; - if (fcr31.enable.inexact_operation) - return true; - fcr31.flag.inexact_operation = true; - return false; + fcr31.cause.inexact_operation = true; + if (fcr31.enable.inexact_operation) + return true; + fcr31.flag.inexact_operation = true; + return false; } bool Cop1::SetCauseDivisionByZero() { - fcr31.cause.division_by_zero = true; - if (fcr31.enable.division_by_zero) - return true; - fcr31.flag.division_by_zero = true; - return false; + fcr31.cause.division_by_zero = true; + if (fcr31.enable.division_by_zero) + return true; + fcr31.flag.division_by_zero = true; + return false; } bool Cop1::SetCauseOverflow() { - fcr31.cause.overflow = true; - if (fcr31.enable.overflow) - return true; - fcr31.flag.overflow = true; - return false; + fcr31.cause.overflow = true; + if (fcr31.enable.overflow) + return true; + fcr31.flag.overflow = true; + return false; } bool Cop1::SetCauseInvalid() { - fcr31.cause.invalid_operation = true; - if (fcr31.enable.invalid_operation) - return true; - fcr31.flag.invalid_operation = true; - return false; + fcr31.cause.invalid_operation = true; + if (fcr31.enable.invalid_operation) + return true; + fcr31.flag.invalid_operation = true; + return false; } #define CHECK_FPE_IMPL(type, res, operation, convert) \ - feclearexcept(FE_ALL_EXCEPT); \ - volatile type v##res = [&]() __attribute__((noinline)) -> type { return operation; }(); \ - if (TestExceptions()) \ - return; \ - type res = v##res; + feclearexcept(FE_ALL_EXCEPT); \ + volatile type v##res = [&]() __attribute__((noinline)) -> type { return operation; }(); \ + if (TestExceptions()) \ + return; \ + type res = v##res; #define CHECK_FPE_IMPL_CONST(type, res, operation, convert) \ - feclearexcept(FE_ALL_EXCEPT); \ - volatile type v##res = [&]() __attribute__((noinline)) -> type { return operation; }(); \ - if (TestExceptions()) \ - return; \ - const type res = v##res; + feclearexcept(FE_ALL_EXCEPT); \ + volatile type v##res = [&]() __attribute__((noinline)) -> type { return operation; }(); \ + if (TestExceptions()) \ + return; \ + const type res = v##res; #define CHECK_FPE(type, res, operation) CHECK_FPE_IMPL(type, res, operation, false) #define CHECK_FPE_CONST(type, res, operation) CHECK_FPE_IMPL_CONST(type, res, operation, false) @@ -443,312 +444,313 @@ bool Cop1::SetCauseInvalid() { #define CHECK_FPE_CONV_CONST(type, res, operation) CHECK_FPE_IMPL_CONST(type, res, operation, true) void Cop1::absd(const Instruction instr) { - Registers& regs = Core::GetRegs(); - if (!CheckFPUUsable()) - return; - const auto fs = FGR_S(regs.cop0.status, instr.fs()); - if (!CheckArg(fs)) - return; - auto fd = std::abs(fs); - if (!CheckResult(fd)) - return; - FGR_D(regs.cop0.status, instr.fd()) = fd; + Registers ®s = Core::GetRegs(); + if (!CheckFPUUsable()) + return; + const auto fs = FGR_S(regs.cop0.status, instr.fs()); + if (!CheckArg(fs)) + return; + auto fd = std::abs(fs); + if (!CheckResult(fd)) + return; + FGR_D(regs.cop0.status, instr.fd()) = fd; } void Cop1::abss(const Instruction instr) { - Registers& regs = Core::GetRegs(); - if (!CheckFPUUsable()) - return; - const auto fs = FGR_S(regs.cop0.status, instr.fs()); - if (!CheckArg(fs)) - return; - auto fd = std::abs(fs); - if (!CheckResult(fd)) - return; - FGR_D(regs.cop0.status, instr.fd()) = fd; + Registers ®s = Core::GetRegs(); + if (!CheckFPUUsable()) + return; + const auto fs = FGR_S(regs.cop0.status, instr.fs()); + if (!CheckArg(fs)) + return; + auto fd = std::abs(fs); + if (!CheckResult(fd)) + return; + FGR_D(regs.cop0.status, instr.fd()) = fd; } void Cop1::adds(const Instruction instr) { - Registers& regs = Core::GetRegs(); - if (!CheckFPUUsable()) - return; - const auto fs = FGR_S(regs.cop0.status, instr.fs()); - const auto ft = FGR_T(regs.cop0.status, instr.ft()); - if (!CheckArgs(fs, ft)) - return; - CHECK_FPE(float, fd, fs + ft) - if (!CheckResult(fd)) - return; - FGR_D(regs.cop0.status, instr.fd()) = fd; + Registers ®s = Core::GetRegs(); + if (!CheckFPUUsable()) + return; + const auto fs = FGR_S(regs.cop0.status, instr.fs()); + const auto ft = FGR_T(regs.cop0.status, instr.ft()); + if (!CheckArgs(fs, ft)) + return; + CHECK_FPE(float, fd, fs + ft) + if (!CheckResult(fd)) + return; + FGR_D(regs.cop0.status, instr.fd()) = fd; } void Cop1::addd(const Instruction instr) { - Registers& regs = Core::GetRegs(); - if (!CheckFPUUsable()) - return; - const auto fs = FGR_S(regs.cop0.status, instr.fs()); - const auto ft = FGR_T(regs.cop0.status, instr.ft()); - if (!CheckArgs(fs, ft)) - return; - CHECK_FPE(double, fd, fs + ft) - if (!CheckResult(fd)) - return; - FGR_D(regs.cop0.status, instr.fd()) = fd; + Registers ®s = Core::GetRegs(); + if (!CheckFPUUsable()) + return; + const auto fs = FGR_S(regs.cop0.status, instr.fs()); + const auto ft = FGR_T(regs.cop0.status, instr.ft()); + if (!CheckArgs(fs, ft)) + return; + CHECK_FPE(double, fd, fs + ft) + if (!CheckResult(fd)) + return; + FGR_D(regs.cop0.status, instr.fd()) = fd; } void Cop1::ceills(const Instruction instr) { - Registers& regs = Core::GetRegs(); - if (!CheckFPUUsable()) - return; - const auto fs = FGR_S(regs.cop0.status, instr.fs()); - if (!CheckCVTArg(fs)) - return; - CHECK_FPE_CONST(s64, fd, ircolib::roundCeil(fs)); - FGR_D(regs.cop0.status, instr.fd()) = fd; + Registers ®s = Core::GetRegs(); + if (!CheckFPUUsable()) + return; + const auto fs = FGR_S(regs.cop0.status, instr.fs()); + if (!CheckCVTArg(fs)) + return; + CHECK_FPE_CONST(s64, fd, ircolib::roundCeil(fs)); + FGR_D(regs.cop0.status, instr.fd()) = fd; } void Cop1::ceilld(const Instruction instr) { - Registers& regs = Core::GetRegs(); - if (!CheckFPUUsable()) - return; - const auto fs = FGR_S(regs.cop0.status, instr.fs()); - if (!CheckCVTArg(fs)) - return; - CHECK_FPE_CONST(s64, fd, ircolib::roundCeil(fs)) - FGR_D(regs.cop0.status, instr.fd()) = fd; + Registers ®s = Core::GetRegs(); + if (!CheckFPUUsable()) + return; + const auto fs = FGR_S(regs.cop0.status, instr.fs()); + if (!CheckCVTArg(fs)) + return; + CHECK_FPE_CONST(s64, fd, ircolib::roundCeil(fs)) + FGR_D(regs.cop0.status, instr.fd()) = fd; } void Cop1::ceilws(const Instruction instr) { - Registers& regs = Core::GetRegs(); - if (!CheckFPUUsable()) - return; - const auto fs = FGR_S(regs.cop0.status, instr.fs()); - if (!CheckCVTArg(fs)) - return; - CHECK_FPE_CONV_CONST(s32, fd, ircolib::roundCeil(fs)) - FGR_D(regs.cop0.status, instr.fd()) = fd; + Registers ®s = Core::GetRegs(); + if (!CheckFPUUsable()) + return; + const auto fs = FGR_S(regs.cop0.status, instr.fs()); + if (!CheckCVTArg(fs)) + return; + CHECK_FPE_CONV_CONST(s32, fd, ircolib::roundCeil(fs)) + FGR_D(regs.cop0.status, instr.fd()) = fd; } void Cop1::ceilwd(const Instruction instr) { - Registers& regs = Core::GetRegs(); - if (!CheckFPUUsable()) - return; - const auto fs = FGR_S(regs.cop0.status, instr.fs()); - if (!CheckCVTArg(fs)) - return; - CHECK_FPE_CONV_CONST(s32, fd, ircolib::roundCeil(fs)) - FGR_D(regs.cop0.status, instr.fd()) = fd; + Registers ®s = Core::GetRegs(); + if (!CheckFPUUsable()) + return; + const auto fs = FGR_S(regs.cop0.status, instr.fs()); + if (!CheckCVTArg(fs)) + return; + CHECK_FPE_CONV_CONST(s32, fd, ircolib::roundCeil(fs)) + FGR_D(regs.cop0.status, instr.fd()) = fd; } void Cop1::cfc1(const Instruction instr) { - Registers& regs = Core::GetRegs(); - if (!CheckFPUUsable()) - return; - const u8 fd = instr.rd(); - s32 val = 0; - switch (fd) { - case 0: - val = fcr0; - break; - case 31: - val = fcr31.read(); - break; - default: - panic("Undefined CFC1 with rd != 0 or 31"); - } - regs.Write(instr.rt(), val); + Registers ®s = Core::GetRegs(); + if (!CheckFPUUsable()) + return; + const u8 fd = instr.rd(); + s32 val = 0; + switch (fd) { + case 0: + val = fcr0; + break; + case 31: + val = fcr31.read(); + break; + default: + panic("Undefined CFC1 with rd != 0 or 31"); + } + regs.Write(instr.rt(), val); } void Cop1::ctc1(const Instruction instr) { - Registers& regs = Core::GetRegs(); - if (!CheckFPUUsable()) - return; - const u8 fs = instr.rd(); - const u32 val = regs.Read(instr.rt()); - switch (fs) { - case 0: - break; - case 31: - { - const u32 prevRound = fcr31.rounding_mode; - fcr31.write(val); - if (prevRound != fcr31.rounding_mode) { - switch (fcr31.rounding_mode) { - case 0: - fesetround(FE_TONEAREST); - break; - case 1: - fesetround(FE_TOWARDZERO); - break; - case 2: - fesetround(FE_UPWARD); - break; - case 3: - fesetround(FE_DOWNWARD); - break; + Registers ®s = Core::GetRegs(); + if (!CheckFPUUsable()) + return; + const u8 fs = instr.rd(); + const u32 val = regs.Read(instr.rt()); + switch (fs) { + case 0: + break; + case 31: + { + const u32 prevRound = fcr31.rounding_mode; + fcr31.write(val); + if (prevRound != fcr31.rounding_mode) { + switch (fcr31.rounding_mode) { + case 0: + fesetround(FE_TONEAREST); + break; + case 1: + fesetround(FE_TOWARDZERO); + break; + case 2: + fesetround(FE_UPWARD); + break; + case 3: + fesetround(FE_DOWNWARD); + break; + } + } + if (fcr31.cause.inexact_operation && fcr31.enable.inexact_operation) + regs.cop0.FireException(Cop0::ExceptionCode::FloatingPointError, 0, regs.oldPC); + if (fcr31.cause.underflow && fcr31.enable.underflow) + regs.cop0.FireException(Cop0::ExceptionCode::FloatingPointError, 0, regs.oldPC); + if (fcr31.cause.overflow && fcr31.enable.overflow) + regs.cop0.FireException(Cop0::ExceptionCode::FloatingPointError, 0, regs.oldPC); + if (fcr31.cause.division_by_zero && fcr31.enable.division_by_zero) + regs.cop0.FireException(Cop0::ExceptionCode::FloatingPointError, 0, regs.oldPC); + if (fcr31.cause.invalid_operation && fcr31.enable.invalid_operation) + regs.cop0.FireException(Cop0::ExceptionCode::FloatingPointError, 0, regs.oldPC); + if (fcr31.cause.unimplemented_operation) + regs.cop0.FireException(Cop0::ExceptionCode::FloatingPointError, 0, regs.oldPC); } - } - if (fcr31.cause.inexact_operation && fcr31.enable.inexact_operation) - regs.cop0.FireException(ExceptionCode::FloatingPointError, 0, regs.oldPC); - if (fcr31.cause.underflow && fcr31.enable.underflow) - regs.cop0.FireException(ExceptionCode::FloatingPointError, 0, regs.oldPC); - if (fcr31.cause.overflow && fcr31.enable.overflow) - regs.cop0.FireException(ExceptionCode::FloatingPointError, 0, regs.oldPC); - if (fcr31.cause.division_by_zero && fcr31.enable.division_by_zero) - regs.cop0.FireException(ExceptionCode::FloatingPointError, 0, regs.oldPC); - if (fcr31.cause.invalid_operation && fcr31.enable.invalid_operation) - regs.cop0.FireException(ExceptionCode::FloatingPointError, 0, regs.oldPC); - if (fcr31.cause.unimplemented_operation) - regs.cop0.FireException(ExceptionCode::FloatingPointError, 0, regs.oldPC); + break; + default: + panic("Undefined CTC1 with rd != 0 or 31"); } - break; - default: - panic("Undefined CTC1 with rd != 0 or 31"); - } } void Cop1::cvtds(const Instruction instr) { - Registers& regs = Core::GetRegs(); - if (!CheckFPUUsable()) - return; - const auto fs = FGR_S(regs.cop0.status, instr.fs()); - if (!CheckArg(fs)) - return; - CHECK_FPE(double, fd, fs) - if (!CheckResult(fd)) - return; - FGR_D(regs.cop0.status, instr.fd()) = fd; + Registers ®s = Core::GetRegs(); + if (!CheckFPUUsable()) + return; + const auto fs = FGR_S(regs.cop0.status, instr.fs()); + if (!CheckArg(fs)) + return; + CHECK_FPE(double, fd, fs) + if (!CheckResult(fd)) + return; + FGR_D(regs.cop0.status, instr.fd()) = fd; } void Cop1::cvtsd(const Instruction instr) { - Registers& regs = Core::GetRegs(); - if (!CheckFPUUsable()) - return; - const auto fs = FGR_S(regs.cop0.status, instr.fs()); - if (!CheckArg(fs)) - return; - CHECK_FPE(float, fd, static_cast(fs)) - if (!CheckResult(fd)) - return; - FGR_D(regs.cop0.status, instr.fd()) = fd; + Registers ®s = Core::GetRegs(); + if (!CheckFPUUsable()) + return; + const auto fs = FGR_S(regs.cop0.status, instr.fs()); + if (!CheckArg(fs)) + return; + CHECK_FPE(float, fd, static_cast(fs)) + if (!CheckResult(fd)) + return; + FGR_D(regs.cop0.status, instr.fd()) = fd; } void Cop1::cvtsw(const Instruction instr) { - Registers& regs = Core::GetRegs(); - if (!CheckFPUUsable()) - return; - const auto fs = FGR_S(regs.cop0.status, instr.fs()); - CHECK_FPE(float, fd, fs) - if (!CheckResult(fd)) return; - FGR_D(regs.cop0.status, instr.fd()) = fd; + Registers ®s = Core::GetRegs(); + if (!CheckFPUUsable()) + return; + const auto fs = FGR_S(regs.cop0.status, instr.fs()); + CHECK_FPE(float, fd, fs) + if (!CheckResult(fd)) + return; + FGR_D(regs.cop0.status, instr.fd()) = fd; } void Cop1::cvtsl(const Instruction instr) { - Registers& regs = Core::GetRegs(); - if (!CheckFPUUsable()) - return; - const auto fs = FGR_S(regs.cop0.status, instr.fs()); - if (fs >= 0x0080000000000000 || fs < static_cast(0xff80000000000000)) { - SetCauseUnimplemented(); - regs.cop0.FireException(ExceptionCode::FloatingPointError, 0, regs.oldPC); - return; - } - CHECK_FPE(float, fd, fs) - if (!CheckResult(fd)) - return; - FGR_D(regs.cop0.status, instr.fd()) = fd; + Registers ®s = Core::GetRegs(); + if (!CheckFPUUsable()) + return; + const auto fs = FGR_S(regs.cop0.status, instr.fs()); + if (fs >= 0x0080000000000000 || fs < static_cast(0xff80000000000000)) { + SetCauseUnimplemented(); + regs.cop0.FireException(Cop0::ExceptionCode::FloatingPointError, 0, regs.oldPC); + return; + } + CHECK_FPE(float, fd, fs) + if (!CheckResult(fd)) + return; + FGR_D(regs.cop0.status, instr.fd()) = fd; } void Cop1::cvtwd(const Instruction instr) { - Registers& regs = Core::GetRegs(); - if (!CheckFPUUsable()) - return; - const auto fs = FGR_S(regs.cop0.status, instr.fs()); - if (!CheckCVTArg(fs)) - return; - CHECK_FPE_CONV_CONST(s32, fd, ircolib::roundCurrent(fs)) - FGR_D(regs.cop0.status, instr.fd()) = fd; + Registers ®s = Core::GetRegs(); + if (!CheckFPUUsable()) + return; + const auto fs = FGR_S(regs.cop0.status, instr.fs()); + if (!CheckCVTArg(fs)) + return; + CHECK_FPE_CONV_CONST(s32, fd, ircolib::roundCurrent(fs)) + FGR_D(regs.cop0.status, instr.fd()) = fd; } void Cop1::cvtws(const Instruction instr) { - Registers& regs = Core::GetRegs(); - if (!CheckFPUUsable()) - return; - const auto fs = FGR_S(regs.cop0.status, instr.fs()); - if (!CheckCVTArg(fs)) - return; - CHECK_FPE_CONV_CONST(s32, fd, ircolib::roundCurrent(fs)) - FGR_D(regs.cop0.status, instr.fd()) = fd; + Registers ®s = Core::GetRegs(); + if (!CheckFPUUsable()) + return; + const auto fs = FGR_S(regs.cop0.status, instr.fs()); + if (!CheckCVTArg(fs)) + return; + CHECK_FPE_CONV_CONST(s32, fd, ircolib::roundCurrent(fs)) + FGR_D(regs.cop0.status, instr.fd()) = fd; } void Cop1::cvtls(const Instruction instr) { - Registers& regs = Core::GetRegs(); - if (!CheckFPUUsable()) - return; - const auto fs = FGR_S(regs.cop0.status, instr.fs()); - if (!CheckCVTArg(fs)) - return; - CHECK_FPE_CONST(s64, fd, ircolib::roundCurrent(fs)) - FGR_D(regs.cop0.status, instr.fd()) = fd; + Registers ®s = Core::GetRegs(); + if (!CheckFPUUsable()) + return; + const auto fs = FGR_S(regs.cop0.status, instr.fs()); + if (!CheckCVTArg(fs)) + return; + CHECK_FPE_CONST(s64, fd, ircolib::roundCurrent(fs)) + FGR_D(regs.cop0.status, instr.fd()) = fd; } void Cop1::cvtdw(const Instruction instr) { - Registers& regs = Core::GetRegs(); - if (!CheckFPUUsable()) - return; - const auto fs = FGR_S(regs.cop0.status, instr.fs()); - CHECK_FPE(double, fd, fs) - if (!CheckResult(fd)) - return; - FGR_D(regs.cop0.status, instr.fd()) = fd; + Registers ®s = Core::GetRegs(); + if (!CheckFPUUsable()) + return; + const auto fs = FGR_S(regs.cop0.status, instr.fs()); + CHECK_FPE(double, fd, fs) + if (!CheckResult(fd)) + return; + FGR_D(regs.cop0.status, instr.fd()) = fd; } void Cop1::cvtdl(const Instruction instr) { - Registers& regs = Core::GetRegs(); - if (!CheckFPUUsable()) - return; + Registers ®s = Core::GetRegs(); + if (!CheckFPUUsable()) + return; - const auto fs = FGR_S(regs.cop0.status, instr.fs()); + const auto fs = FGR_S(regs.cop0.status, instr.fs()); - if (fs >= 0x0080000000000000 || fs < static_cast(0xff80000000000000)) { - SetCauseUnimplemented(); - regs.cop0.FireException(ExceptionCode::FloatingPointError, 0, regs.oldPC); - return; - } - CHECK_FPE(double, fd, fs) - if (!CheckResult(fd)) - return; - FGR_D(regs.cop0.status, instr.fd()) = fd; + if (fs >= 0x0080000000000000 || fs < static_cast(0xff80000000000000)) { + SetCauseUnimplemented(); + regs.cop0.FireException(Cop0::ExceptionCode::FloatingPointError, 0, regs.oldPC); + return; + } + CHECK_FPE(double, fd, fs) + if (!CheckResult(fd)) + return; + FGR_D(regs.cop0.status, instr.fd()) = fd; } void Cop1::cvtld(const Instruction instr) { - Registers& regs = Core::GetRegs(); - if (!CheckFPUUsable()) - return; - const auto fs = FGR_S(regs.cop0.status, instr.fs()); - if (!CheckCVTArg(fs)) - return; - CHECK_FPE_CONST(s64, fd, ircolib::roundCurrent(fs)) - FGR_D(regs.cop0.status, instr.fd()) = fd; + Registers ®s = Core::GetRegs(); + if (!CheckFPUUsable()) + return; + const auto fs = FGR_S(regs.cop0.status, instr.fs()); + if (!CheckCVTArg(fs)) + return; + CHECK_FPE_CONST(s64, fd, ircolib::roundCurrent(fs)) + FGR_D(regs.cop0.status, instr.fd()) = fd; } template bool Cop1::XORDERED(const T fs, const T ft) { - Registers& regs = Core::GetRegs(); - if (std::isnan(fs) || std::isnan(ft)) { - if (std::isnan(fs) && (!quiet || isqnan(fs)) && SetCauseInvalid()) { - regs.cop0.FireException(ExceptionCode::FloatingPointError, 0, regs.oldPC); - return false; + Registers ®s = Core::GetRegs(); + if (std::isnan(fs) || std::isnan(ft)) { + if (std::isnan(fs) && (!quiet || isqnan(fs)) && SetCauseInvalid()) { + regs.cop0.FireException(Cop0::ExceptionCode::FloatingPointError, 0, regs.oldPC); + return false; + } + if (std::isnan(ft) && (!quiet || isqnan(ft)) && SetCauseInvalid()) { + regs.cop0.FireException(Cop0::ExceptionCode::FloatingPointError, 0, regs.oldPC); + return false; + } + fcr31.compare = cf; + return false; } - if (std::isnan(ft) && (!quiet || isqnan(ft)) && SetCauseInvalid()) { - regs.cop0.FireException(ExceptionCode::FloatingPointError, 0, regs.oldPC); - return false; - } - fcr31.compare = cf; - return false; - } - return true; + return true; } #define ORDERED(type, cf) XORDERED @@ -756,191 +758,191 @@ bool Cop1::XORDERED(const T fs, const T ft) { template void Cop1::cf(const Instruction instr) { - Registers& regs = Core::GetRegs(); - if (!CheckFPUUsable()) - return; + Registers ®s = Core::GetRegs(); + if (!CheckFPUUsable()) + return; - const T fs = FGR_S(regs.cop0.status, instr.fs()); - if (const T ft = FGR_T(regs.cop0.status, instr.ft()); !UNORDERED(T, 0)(fs, ft)) - return; - fcr31.compare = 0; + const T fs = FGR_S(regs.cop0.status, instr.fs()); + if (const T ft = FGR_T(regs.cop0.status, instr.ft()); !UNORDERED(T, 0)(fs, ft)) + return; + fcr31.compare = 0; } template void Cop1::cun(const Instruction instr) { - Registers& regs = Core::GetRegs(); - if (!CheckFPUUsable()) - return; - const T fs = FGR_S(regs.cop0.status, instr.fs()); - if (const T ft = FGR_T(regs.cop0.status, instr.ft()); !UNORDERED(T, 1)(fs, ft)) - return; - fcr31.compare = 0; + Registers ®s = Core::GetRegs(); + if (!CheckFPUUsable()) + return; + const T fs = FGR_S(regs.cop0.status, instr.fs()); + if (const T ft = FGR_T(regs.cop0.status, instr.ft()); !UNORDERED(T, 1)(fs, ft)) + return; + fcr31.compare = 0; } template void Cop1::ceq(const Instruction instr) { - Registers& regs = Core::GetRegs(); - if (!CheckFPUUsable()) - return; - const T fs = FGR_S(regs.cop0.status, instr.fs()); - const T ft = FGR_T(regs.cop0.status, instr.ft()); - if (!UNORDERED(T, 0)(fs, ft)) - return; - fcr31.compare = fs == ft; + Registers ®s = Core::GetRegs(); + if (!CheckFPUUsable()) + return; + const T fs = FGR_S(regs.cop0.status, instr.fs()); + const T ft = FGR_T(regs.cop0.status, instr.ft()); + if (!UNORDERED(T, 0)(fs, ft)) + return; + fcr31.compare = fs == ft; } template void Cop1::cueq(const Instruction instr) { - Registers& regs = Core::GetRegs(); - if (!CheckFPUUsable()) - return; - const T fs = FGR_S(regs.cop0.status, instr.fs()); - const T ft = FGR_T(regs.cop0.status, instr.ft()); - if (!UNORDERED(T, 1)(fs, ft)) - return; - fcr31.compare = fs == ft; + Registers ®s = Core::GetRegs(); + if (!CheckFPUUsable()) + return; + const T fs = FGR_S(regs.cop0.status, instr.fs()); + const T ft = FGR_T(regs.cop0.status, instr.ft()); + if (!UNORDERED(T, 1)(fs, ft)) + return; + fcr31.compare = fs == ft; } template void Cop1::colt(const Instruction instr) { - Registers& regs = Core::GetRegs(); - if (!CheckFPUUsable()) - return; - const T fs = FGR_S(regs.cop0.status, instr.fs()); - const T ft = FGR_T(regs.cop0.status, instr.ft()); - if (!UNORDERED(T, 0)(fs, ft)) - return; - fcr31.compare = fs < ft; + Registers ®s = Core::GetRegs(); + if (!CheckFPUUsable()) + return; + const T fs = FGR_S(regs.cop0.status, instr.fs()); + const T ft = FGR_T(regs.cop0.status, instr.ft()); + if (!UNORDERED(T, 0)(fs, ft)) + return; + fcr31.compare = fs < ft; } template void Cop1::cult(const Instruction instr) { - Registers& regs = Core::GetRegs(); - if (!CheckFPUUsable()) - return; - const T fs = FGR_S(regs.cop0.status, instr.fs()); - const T ft = FGR_T(regs.cop0.status, instr.ft()); - if (!UNORDERED(T, 1)(fs, ft)) - return; - fcr31.compare = fs < ft; + Registers ®s = Core::GetRegs(); + if (!CheckFPUUsable()) + return; + const T fs = FGR_S(regs.cop0.status, instr.fs()); + const T ft = FGR_T(regs.cop0.status, instr.ft()); + if (!UNORDERED(T, 1)(fs, ft)) + return; + fcr31.compare = fs < ft; } template void Cop1::cole(const Instruction instr) { - Registers& regs = Core::GetRegs(); - if (!CheckFPUUsable()) - return; - const T fs = FGR_S(regs.cop0.status, instr.fs()); - const T ft = FGR_T(regs.cop0.status, instr.ft()); - if (!UNORDERED(T, 0)(fs, ft)) - return; - fcr31.compare = fs <= ft; + Registers ®s = Core::GetRegs(); + if (!CheckFPUUsable()) + return; + const T fs = FGR_S(regs.cop0.status, instr.fs()); + const T ft = FGR_T(regs.cop0.status, instr.ft()); + if (!UNORDERED(T, 0)(fs, ft)) + return; + fcr31.compare = fs <= ft; } template void Cop1::cule(const Instruction instr) { - Registers& regs = Core::GetRegs(); - if (!CheckFPUUsable()) - return; - const T fs = FGR_S(regs.cop0.status, instr.fs()); - const T ft = FGR_T(regs.cop0.status, instr.ft()); - if (!UNORDERED(T, 1)(fs, ft)) - return; - fcr31.compare = fs <= ft; + Registers ®s = Core::GetRegs(); + if (!CheckFPUUsable()) + return; + const T fs = FGR_S(regs.cop0.status, instr.fs()); + const T ft = FGR_T(regs.cop0.status, instr.ft()); + if (!UNORDERED(T, 1)(fs, ft)) + return; + fcr31.compare = fs <= ft; } template void Cop1::csf(const Instruction instr) { - Registers& regs = Core::GetRegs(); - if (!CheckFPUUsable()) - return; - const T fs = FGR_S(regs.cop0.status, instr.fs()); - if (const T ft = FGR_T(regs.cop0.status, instr.ft()); !ORDERED(T, 0)(fs, ft)) - return; - fcr31.compare = 0; + Registers ®s = Core::GetRegs(); + if (!CheckFPUUsable()) + return; + const T fs = FGR_S(regs.cop0.status, instr.fs()); + if (const T ft = FGR_T(regs.cop0.status, instr.ft()); !ORDERED(T, 0)(fs, ft)) + return; + fcr31.compare = 0; } template void Cop1::cngle(const Instruction instr) { - Registers& regs = Core::GetRegs(); - if (!CheckFPUUsable()) - return; - const T fs = FGR_S(regs.cop0.status, instr.fs()); - if (const T ft = FGR_T(regs.cop0.status, instr.ft()); !ORDERED(T, 1)(fs, ft)) - return; - fcr31.compare = 0; + Registers ®s = Core::GetRegs(); + if (!CheckFPUUsable()) + return; + const T fs = FGR_S(regs.cop0.status, instr.fs()); + if (const T ft = FGR_T(regs.cop0.status, instr.ft()); !ORDERED(T, 1)(fs, ft)) + return; + fcr31.compare = 0; } template void Cop1::cseq(const Instruction instr) { - Registers& regs = Core::GetRegs(); - if (!CheckFPUUsable()) - return; - const T fs = FGR_S(regs.cop0.status, instr.fs()); - const T ft = FGR_T(regs.cop0.status, instr.ft()); - if (!ORDERED(T, 0)(fs, ft)) - return; - fcr31.compare = fs == ft; + Registers ®s = Core::GetRegs(); + if (!CheckFPUUsable()) + return; + const T fs = FGR_S(regs.cop0.status, instr.fs()); + const T ft = FGR_T(regs.cop0.status, instr.ft()); + if (!ORDERED(T, 0)(fs, ft)) + return; + fcr31.compare = fs == ft; } template void Cop1::cngl(const Instruction instr) { - Registers& regs = Core::GetRegs(); - if (!CheckFPUUsable()) - return; - const T fs = FGR_S(regs.cop0.status, instr.fs()); - const T ft = FGR_T(regs.cop0.status, instr.ft()); - if (!ORDERED(T, 1)(fs, ft)) - return; - fcr31.compare = fs == ft; + Registers ®s = Core::GetRegs(); + if (!CheckFPUUsable()) + return; + const T fs = FGR_S(regs.cop0.status, instr.fs()); + const T ft = FGR_T(regs.cop0.status, instr.ft()); + if (!ORDERED(T, 1)(fs, ft)) + return; + fcr31.compare = fs == ft; } template void Cop1::clt(const Instruction instr) { - Registers& regs = Core::GetRegs(); - if (!CheckFPUUsable()) - return; - const T fs = FGR_S(regs.cop0.status, instr.fs()); - const T ft = FGR_T(regs.cop0.status, instr.ft()); - if (!ORDERED(T, 0)(fs, ft)) - return; - fcr31.compare = fs < ft; + Registers ®s = Core::GetRegs(); + if (!CheckFPUUsable()) + return; + const T fs = FGR_S(regs.cop0.status, instr.fs()); + const T ft = FGR_T(regs.cop0.status, instr.ft()); + if (!ORDERED(T, 0)(fs, ft)) + return; + fcr31.compare = fs < ft; } template void Cop1::cnge(const Instruction instr) { - Registers& regs = Core::GetRegs(); - if (!CheckFPUUsable()) - return; - const T fs = FGR_S(regs.cop0.status, instr.fs()); - const T ft = FGR_T(regs.cop0.status, instr.ft()); - if (!ORDERED(T, 1)(fs, ft)) - return; - fcr31.compare = fs < ft; + Registers ®s = Core::GetRegs(); + if (!CheckFPUUsable()) + return; + const T fs = FGR_S(regs.cop0.status, instr.fs()); + const T ft = FGR_T(regs.cop0.status, instr.ft()); + if (!ORDERED(T, 1)(fs, ft)) + return; + fcr31.compare = fs < ft; } template void Cop1::cle(const Instruction instr) { - Registers& regs = Core::GetRegs(); - if (!CheckFPUUsable()) - return; - const T fs = FGR_S(regs.cop0.status, instr.fs()); - const T ft = FGR_T(regs.cop0.status, instr.ft()); - if (!ORDERED(T, 0)(fs, ft)) - return; - fcr31.compare = fs <= ft; + Registers ®s = Core::GetRegs(); + if (!CheckFPUUsable()) + return; + const T fs = FGR_S(regs.cop0.status, instr.fs()); + const T ft = FGR_T(regs.cop0.status, instr.ft()); + if (!ORDERED(T, 0)(fs, ft)) + return; + fcr31.compare = fs <= ft; } template void Cop1::cngt(const Instruction instr) { - Registers& regs = Core::GetRegs(); - if (!CheckFPUUsable()) - return; - const T fs = FGR_S(regs.cop0.status, instr.fs()); - const T ft = FGR_T(regs.cop0.status, instr.ft()); - if (!ORDERED(T, 1)(fs, ft)) - return; - fcr31.compare = fs <= ft; + Registers ®s = Core::GetRegs(); + if (!CheckFPUUsable()) + return; + const T fs = FGR_S(regs.cop0.status, instr.fs()); + const T ft = FGR_T(regs.cop0.status, instr.ft()); + if (!ORDERED(T, 1)(fs, ft)) + return; + fcr31.compare = fs <= ft; } template void Cop1::cf(Instruction instr); @@ -977,401 +979,401 @@ template void Cop1::cle(Instruction instr); template void Cop1::cngt(Instruction instr); void Cop1::divs(const Instruction instr) { - Registers& regs = Core::GetRegs(); - if (!CheckFPUUsable()) - return; - const auto fs = FGR_S(regs.cop0.status, instr.fs()); - const auto ft = FGR_T(regs.cop0.status, instr.ft()); - if (!CheckArgs(fs, ft)) - return; - CHECK_FPE(float, fd, fs / ft) - if (!CheckResult(fd)) - return; - FGR_D(regs.cop0.status, instr.fd()) = fd; + Registers ®s = Core::GetRegs(); + if (!CheckFPUUsable()) + return; + const auto fs = FGR_S(regs.cop0.status, instr.fs()); + const auto ft = FGR_T(regs.cop0.status, instr.ft()); + if (!CheckArgs(fs, ft)) + return; + CHECK_FPE(float, fd, fs / ft) + if (!CheckResult(fd)) + return; + FGR_D(regs.cop0.status, instr.fd()) = fd; } void Cop1::divd(const Instruction instr) { - Registers& regs = Core::GetRegs(); - if (!CheckFPUUsable()) - return; - const auto fs = FGR_S(regs.cop0.status, instr.fs()); - const auto ft = FGR_T(regs.cop0.status, instr.ft()); - if (!CheckArgs(fs, ft)) - return; - CHECK_FPE(double, fd, fs / ft) - if (!CheckResult(fd)) - return; - FGR_D(regs.cop0.status, instr.fd()) = fd; + Registers ®s = Core::GetRegs(); + if (!CheckFPUUsable()) + return; + const auto fs = FGR_S(regs.cop0.status, instr.fs()); + const auto ft = FGR_T(regs.cop0.status, instr.ft()); + if (!CheckArgs(fs, ft)) + return; + CHECK_FPE(double, fd, fs / ft) + if (!CheckResult(fd)) + return; + FGR_D(regs.cop0.status, instr.fd()) = fd; } void Cop1::muls(const Instruction instr) { - Registers& regs = Core::GetRegs(); - if (!CheckFPUUsable()) - return; - const auto fs = FGR_S(regs.cop0.status, instr.fs()); - const auto ft = FGR_T(regs.cop0.status, instr.ft()); - if (!CheckArgs(fs, ft)) - return; - CHECK_FPE(float, fd, fs *ft) - if (!CheckResult(fd)) - return; - FGR_D(regs.cop0.status, instr.fd()) = fd; + Registers ®s = Core::GetRegs(); + if (!CheckFPUUsable()) + return; + const auto fs = FGR_S(regs.cop0.status, instr.fs()); + const auto ft = FGR_T(regs.cop0.status, instr.ft()); + if (!CheckArgs(fs, ft)) + return; + CHECK_FPE(float, fd, fs *ft) + if (!CheckResult(fd)) + return; + FGR_D(regs.cop0.status, instr.fd()) = fd; } void Cop1::muld(const Instruction instr) { - Registers& regs = Core::GetRegs(); - if (!CheckFPUUsable()) - return; - const auto fs = FGR_S(regs.cop0.status, instr.fs()); - const auto ft = FGR_T(regs.cop0.status, instr.ft()); - if (!CheckArgs(fs, ft)) - return; - CHECK_FPE(double, fd, fs *ft) - if (!CheckResult(fd)) - return; - FGR_D(regs.cop0.status, instr.fd()) = fd; + Registers ®s = Core::GetRegs(); + if (!CheckFPUUsable()) + return; + const auto fs = FGR_S(regs.cop0.status, instr.fs()); + const auto ft = FGR_T(regs.cop0.status, instr.ft()); + if (!CheckArgs(fs, ft)) + return; + CHECK_FPE(double, fd, fs *ft) + if (!CheckResult(fd)) + return; + FGR_D(regs.cop0.status, instr.fd()) = fd; } void Cop1::subs(const Instruction instr) { - Registers& regs = Core::GetRegs(); - if (!CheckFPUUsable()) - return; - const auto fs = FGR_S(regs.cop0.status, instr.fs()); - const auto ft = FGR_T(regs.cop0.status, instr.ft()); - if (!CheckArgs(fs, ft)) - return; - CHECK_FPE(float, fd, fs - ft) - if (!CheckResult(fd)) - return; - FGR_D(regs.cop0.status, instr.fd()) = fd; + Registers ®s = Core::GetRegs(); + if (!CheckFPUUsable()) + return; + const auto fs = FGR_S(regs.cop0.status, instr.fs()); + const auto ft = FGR_T(regs.cop0.status, instr.ft()); + if (!CheckArgs(fs, ft)) + return; + CHECK_FPE(float, fd, fs - ft) + if (!CheckResult(fd)) + return; + FGR_D(regs.cop0.status, instr.fd()) = fd; } void Cop1::subd(const Instruction instr) { - Registers& regs = Core::GetRegs(); - if (!CheckFPUUsable()) - return; - const auto fs = FGR_S(regs.cop0.status, instr.fs()); - const auto ft = FGR_T(regs.cop0.status, instr.ft()); - if (!CheckArgs(fs, ft)) - return; - CHECK_FPE(double, fd, fs - ft) - if (!CheckResult(fd)) - return; - FGR_D(regs.cop0.status, instr.fd()) = fd; + Registers ®s = Core::GetRegs(); + if (!CheckFPUUsable()) + return; + const auto fs = FGR_S(regs.cop0.status, instr.fs()); + const auto ft = FGR_T(regs.cop0.status, instr.ft()); + if (!CheckArgs(fs, ft)) + return; + CHECK_FPE(double, fd, fs - ft) + if (!CheckResult(fd)) + return; + FGR_D(regs.cop0.status, instr.fd()) = fd; } void Cop1::movs(const Instruction instr) { movd(instr); } void Cop1::movd(const Instruction instr) { - Registers& regs = Core::GetRegs(); - if (!CheckFPUUsable()) - return; - FGR_D(regs.cop0.status, instr.fd()) = FGR_S(regs.cop0.status, instr.fs()); + Registers ®s = Core::GetRegs(); + if (!CheckFPUUsable()) + return; + FGR_D(regs.cop0.status, instr.fd()) = FGR_S(regs.cop0.status, instr.fs()); } void Cop1::negs(const Instruction instr) { - Registers& regs = Core::GetRegs(); - if (!CheckFPUUsable()) - return; - const auto fs = FGR_S(regs.cop0.status, instr.fs()); - if (!CheckArg(fs)) - return; - CHECK_FPE(float, fd, -fs) - if (!CheckResult(fd)) - return; - FGR_D(regs.cop0.status, instr.fd()) = fd; + Registers ®s = Core::GetRegs(); + if (!CheckFPUUsable()) + return; + const auto fs = FGR_S(regs.cop0.status, instr.fs()); + if (!CheckArg(fs)) + return; + CHECK_FPE(float, fd, -fs) + if (!CheckResult(fd)) + return; + FGR_D(regs.cop0.status, instr.fd()) = fd; } void Cop1::negd(const Instruction instr) { - Registers& regs = Core::GetRegs(); - if (!CheckFPUUsable()) - return; - const auto fs = FGR_S(regs.cop0.status, instr.fs()); - if (!CheckArg(fs)) - return; - CHECK_FPE(double, fd, -fs) - if (!CheckResult(fd)) - return; - FGR_D(regs.cop0.status, instr.fd()) = fd; + Registers ®s = Core::GetRegs(); + if (!CheckFPUUsable()) + return; + const auto fs = FGR_S(regs.cop0.status, instr.fs()); + if (!CheckArg(fs)) + return; + CHECK_FPE(double, fd, -fs) + if (!CheckResult(fd)) + return; + FGR_D(regs.cop0.status, instr.fd()) = fd; } void Cop1::sqrts(const Instruction instr) { - Registers& regs = Core::GetRegs(); - if (!CheckFPUUsable()) - return; - const auto fs = FGR_S(regs.cop0.status, instr.fs()); - if (!CheckArg(fs)) - return; - CHECK_FPE(float, fd, sqrtf(fs)) - if (!CheckResult(fd)) - return; - FGR_D(regs.cop0.status, instr.fd()) = fd; + Registers ®s = Core::GetRegs(); + if (!CheckFPUUsable()) + return; + const auto fs = FGR_S(regs.cop0.status, instr.fs()); + if (!CheckArg(fs)) + return; + CHECK_FPE(float, fd, sqrtf(fs)) + if (!CheckResult(fd)) + return; + FGR_D(regs.cop0.status, instr.fd()) = fd; } void Cop1::sqrtd(const Instruction instr) { - Registers& regs = Core::GetRegs(); - if (!CheckFPUUsable()) - return; - const auto fs = FGR_S(regs.cop0.status, instr.fs()); - if (!CheckArg(fs)) - return; - CHECK_FPE(double, fd, sqrt(fs)) - if (!CheckResult(fd)) - return; - FGR_D(regs.cop0.status, instr.fd()) = fd; + Registers ®s = Core::GetRegs(); + if (!CheckFPUUsable()) + return; + const auto fs = FGR_S(regs.cop0.status, instr.fs()); + if (!CheckArg(fs)) + return; + CHECK_FPE(double, fd, sqrt(fs)) + if (!CheckResult(fd)) + return; + FGR_D(regs.cop0.status, instr.fd()) = fd; } void Cop1::roundls(const Instruction instr) { - Registers& regs = Core::GetRegs(); - if (!CheckFPUUsable()) - return; - const auto fs = FGR_S(regs.cop0.status, instr.fs()); - if (!CheckCVTArg(fs)) - return; - CHECK_FPE_CONST(s64, fd, ircolib::roundNearest(fs)) - if (fd != fs && SetCauseInexact()) { - regs.cop0.FireException(ExceptionCode::FloatingPointError, 0, regs.oldPC); - return; - } - FGR_D(regs.cop0.status, instr.fd()) = fd; + Registers ®s = Core::GetRegs(); + if (!CheckFPUUsable()) + return; + const auto fs = FGR_S(regs.cop0.status, instr.fs()); + if (!CheckCVTArg(fs)) + return; + CHECK_FPE_CONST(s64, fd, ircolib::roundNearest(fs)) + if (fd != fs && SetCauseInexact()) { + regs.cop0.FireException(Cop0::ExceptionCode::FloatingPointError, 0, regs.oldPC); + return; + } + FGR_D(regs.cop0.status, instr.fd()) = fd; } void Cop1::roundld(const Instruction instr) { - Registers& regs = Core::GetRegs(); - if (!CheckFPUUsable()) - return; - const auto fs = FGR_S(regs.cop0.status, instr.fs()); - if (!CheckCVTArg(fs)) - return; - CHECK_FPE_CONST(s64, fd, ircolib::roundNearest(fs)) - if (fd != fs && SetCauseInexact()) { - regs.cop0.FireException(ExceptionCode::FloatingPointError, 0, regs.oldPC); - return; - } - FGR_D(regs.cop0.status, instr.fd()) = fd; + Registers ®s = Core::GetRegs(); + if (!CheckFPUUsable()) + return; + const auto fs = FGR_S(regs.cop0.status, instr.fs()); + if (!CheckCVTArg(fs)) + return; + CHECK_FPE_CONST(s64, fd, ircolib::roundNearest(fs)) + if (fd != fs && SetCauseInexact()) { + regs.cop0.FireException(Cop0::ExceptionCode::FloatingPointError, 0, regs.oldPC); + return; + } + FGR_D(regs.cop0.status, instr.fd()) = fd; } void Cop1::roundws(const Instruction instr) { - Registers& regs = Core::GetRegs(); - if (!CheckFPUUsable()) - return; - const auto fs = FGR_S(regs.cop0.status, instr.fs()); - if (!CheckCVTArg(fs)) - return; - CHECK_FPE_CONV_CONST(s32, fd, ircolib::roundNearest(fs)) - if (fd != fs && SetCauseInexact()) { - regs.cop0.FireException(ExceptionCode::FloatingPointError, 0, regs.oldPC); - return; - } - FGR_D(regs.cop0.status, instr.fd()) = fd; + Registers ®s = Core::GetRegs(); + if (!CheckFPUUsable()) + return; + const auto fs = FGR_S(regs.cop0.status, instr.fs()); + if (!CheckCVTArg(fs)) + return; + CHECK_FPE_CONV_CONST(s32, fd, ircolib::roundNearest(fs)) + if (fd != fs && SetCauseInexact()) { + regs.cop0.FireException(Cop0::ExceptionCode::FloatingPointError, 0, regs.oldPC); + return; + } + FGR_D(regs.cop0.status, instr.fd()) = fd; } void Cop1::roundwd(const Instruction instr) { - Registers& regs = Core::GetRegs(); - if (!CheckFPUUsable()) - return; - const auto fs = FGR_S(regs.cop0.status, instr.fs()); - if (!CheckCVTArg(fs)) - return; - CHECK_FPE_CONV_CONST(s32, fd, ircolib::roundNearest(fs)) - if (fd != fs && SetCauseInexact()) { - regs.cop0.FireException(ExceptionCode::FloatingPointError, 0, regs.oldPC); - return; - } - FGR_D(regs.cop0.status, instr.fd()) = fd; + Registers ®s = Core::GetRegs(); + if (!CheckFPUUsable()) + return; + const auto fs = FGR_S(regs.cop0.status, instr.fs()); + if (!CheckCVTArg(fs)) + return; + CHECK_FPE_CONV_CONST(s32, fd, ircolib::roundNearest(fs)) + if (fd != fs && SetCauseInexact()) { + regs.cop0.FireException(Cop0::ExceptionCode::FloatingPointError, 0, regs.oldPC); + return; + } + FGR_D(regs.cop0.status, instr.fd()) = fd; } void Cop1::floorls(const Instruction instr) { - Registers& regs = Core::GetRegs(); - if (!CheckFPUUsable()) - return; - const auto fs = FGR_S(regs.cop0.status, instr.fs()); - if (!CheckCVTArg(fs)) - return; - CHECK_FPE_CONST(s64, fd, ircolib::roundFloor(fs)) - FGR_D(regs.cop0.status, instr.fd()) = fd; + Registers ®s = Core::GetRegs(); + if (!CheckFPUUsable()) + return; + const auto fs = FGR_S(regs.cop0.status, instr.fs()); + if (!CheckCVTArg(fs)) + return; + CHECK_FPE_CONST(s64, fd, ircolib::roundFloor(fs)) + FGR_D(regs.cop0.status, instr.fd()) = fd; } void Cop1::floorld(const Instruction instr) { - Registers& regs = Core::GetRegs(); - if (!CheckFPUUsable()) - return; - const auto fs = FGR_S(regs.cop0.status, instr.fs()); - if (!CheckCVTArg(fs)) - return; - CHECK_FPE_CONST(s64, fd, ircolib::roundFloor(fs)) - FGR_D(regs.cop0.status, instr.fd()) = fd; + Registers ®s = Core::GetRegs(); + if (!CheckFPUUsable()) + return; + const auto fs = FGR_S(regs.cop0.status, instr.fs()); + if (!CheckCVTArg(fs)) + return; + CHECK_FPE_CONST(s64, fd, ircolib::roundFloor(fs)) + FGR_D(regs.cop0.status, instr.fd()) = fd; } void Cop1::floorws(const Instruction instr) { - Registers& regs = Core::GetRegs(); - if (!CheckFPUUsable()) - return; - const auto fs = FGR_S(regs.cop0.status, instr.fs()); - if (!CheckCVTArg(fs)) - return; - CHECK_FPE_CONV_CONST(s32, fd, ircolib::roundFloor(fs)) - FGR_D(regs.cop0.status, instr.fd()) = fd; + Registers ®s = Core::GetRegs(); + if (!CheckFPUUsable()) + return; + const auto fs = FGR_S(regs.cop0.status, instr.fs()); + if (!CheckCVTArg(fs)) + return; + CHECK_FPE_CONV_CONST(s32, fd, ircolib::roundFloor(fs)) + FGR_D(regs.cop0.status, instr.fd()) = fd; } void Cop1::floorwd(const Instruction instr) { - Registers& regs = Core::GetRegs(); - if (!CheckFPUUsable()) - return; - const auto fs = FGR_S(regs.cop0.status, instr.fs()); - if (!CheckCVTArg(fs)) - return; - CHECK_FPE_CONV_CONST(s32, fd, ircolib::roundFloor(fs)) - FGR_D(regs.cop0.status, instr.fd()) = fd; + Registers ®s = Core::GetRegs(); + if (!CheckFPUUsable()) + return; + const auto fs = FGR_S(regs.cop0.status, instr.fs()); + if (!CheckCVTArg(fs)) + return; + CHECK_FPE_CONV_CONST(s32, fd, ircolib::roundFloor(fs)) + FGR_D(regs.cop0.status, instr.fd()) = fd; } void Cop1::truncws(const Instruction instr) { - Registers& regs = Core::GetRegs(); - if (!CheckFPUUsable()) - return; - const auto fs = FGR_S(regs.cop0.status, instr.fs()); - if (!CheckCVTArg(fs)) - return; - CHECK_FPE_CONV_CONST(s32, fd, ircolib::roundTrunc(fs)) - if (static_cast(fd) != fs && SetCauseInexact()) { - regs.cop0.FireException(ExceptionCode::FloatingPointError, 0, regs.oldPC); - return; - } - FGR_D(regs.cop0.status, instr.fd()) = fd; + Registers ®s = Core::GetRegs(); + if (!CheckFPUUsable()) + return; + const auto fs = FGR_S(regs.cop0.status, instr.fs()); + if (!CheckCVTArg(fs)) + return; + CHECK_FPE_CONV_CONST(s32, fd, ircolib::roundTrunc(fs)) + if (static_cast(fd) != fs && SetCauseInexact()) { + regs.cop0.FireException(Cop0::ExceptionCode::FloatingPointError, 0, regs.oldPC); + return; + } + FGR_D(regs.cop0.status, instr.fd()) = fd; } void Cop1::truncwd(const Instruction instr) { - Registers& regs = Core::GetRegs(); - if (!CheckFPUUsable()) - return; - const auto fs = FGR_S(regs.cop0.status, instr.fs()); - if (!CheckCVTArg(fs)) - return; - CHECK_FPE_CONV_CONST(s32, fd, ircolib::roundTrunc(fs)) - if (static_cast(fd) != fs && SetCauseInexact()) { - regs.cop0.FireException(ExceptionCode::FloatingPointError, 0, regs.oldPC); - return; - } - FGR_D(regs.cop0.status, instr.fd()) = fd; + Registers ®s = Core::GetRegs(); + if (!CheckFPUUsable()) + return; + const auto fs = FGR_S(regs.cop0.status, instr.fs()); + if (!CheckCVTArg(fs)) + return; + CHECK_FPE_CONV_CONST(s32, fd, ircolib::roundTrunc(fs)) + if (static_cast(fd) != fs && SetCauseInexact()) { + regs.cop0.FireException(Cop0::ExceptionCode::FloatingPointError, 0, regs.oldPC); + return; + } + FGR_D(regs.cop0.status, instr.fd()) = fd; } void Cop1::truncls(const Instruction instr) { - Registers& regs = Core::GetRegs(); - if (!CheckFPUUsable()) - return; - const auto fs = FGR_S(regs.cop0.status, instr.fs()); - if (!CheckCVTArg(fs)) - return; - CHECK_FPE_CONST(s64, fd, ircolib::roundTrunc(fs)) - if (static_cast(fd) != fs && SetCauseInexact()) { - regs.cop0.FireException(ExceptionCode::FloatingPointError, 0, regs.oldPC); - return; - } - FGR_D(regs.cop0.status, instr.fd()) = fd; + Registers ®s = Core::GetRegs(); + if (!CheckFPUUsable()) + return; + const auto fs = FGR_S(regs.cop0.status, instr.fs()); + if (!CheckCVTArg(fs)) + return; + CHECK_FPE_CONST(s64, fd, ircolib::roundTrunc(fs)) + if (static_cast(fd) != fs && SetCauseInexact()) { + regs.cop0.FireException(Cop0::ExceptionCode::FloatingPointError, 0, regs.oldPC); + return; + } + FGR_D(regs.cop0.status, instr.fd()) = fd; } void Cop1::truncld(const Instruction instr) { - Registers& regs = Core::GetRegs(); - if (!CheckFPUUsable()) - return; - const auto fs = FGR_S(regs.cop0.status, instr.fs()); - if (!CheckCVTArg(fs)) - return; - CHECK_FPE_CONST(s64, fd, ircolib::roundTrunc(fs)) - if (static_cast(fd) != fs && SetCauseInexact()) { - regs.cop0.FireException(ExceptionCode::FloatingPointError, 0, regs.oldPC); - return; - } - FGR_D(regs.cop0.status, instr.fd()) = fd; + Registers ®s = Core::GetRegs(); + if (!CheckFPUUsable()) + return; + const auto fs = FGR_S(regs.cop0.status, instr.fs()); + if (!CheckCVTArg(fs)) + return; + CHECK_FPE_CONST(s64, fd, ircolib::roundTrunc(fs)) + if (static_cast(fd) != fs && SetCauseInexact()) { + regs.cop0.FireException(Cop0::ExceptionCode::FloatingPointError, 0, regs.oldPC); + return; + } + FGR_D(regs.cop0.status, instr.fd()) = fd; } void Cop1::lwc1(const Instruction instr) { - Mem& mem = Core::GetMem(); - Registers& regs = Core::GetRegs(); - const u64 addr = static_cast(static_cast(instr)) + regs.Read(instr.base()); + Mem &mem = Core::GetMem(); + Registers ®s = Core::GetRegs(); + const u64 addr = static_cast(static_cast(instr)) + regs.Read(instr.base()); - if (u32 physical; !regs.cop0.MapVAddr(Cop0::LOAD, addr, physical)) { - regs.cop0.HandleTLBException(addr); - regs.cop0.FireException(Cop0::GetTLBExceptionCode(regs.cop0.tlbError, Cop0::LOAD), 0, regs.oldPC); - } else { - const u32 data = mem.Read(physical); - FGR_T(regs.cop0.status, instr.ft()) = data; - } + if (u32 physical; !regs.cop0.MapVAddr(Cop0::LOAD, addr, physical)) { + regs.cop0.HandleTLBException(addr); + regs.cop0.FireException(Cop0::GetTLBExceptionCode(regs.cop0.tlbError, Cop0::LOAD), 0, regs.oldPC); + } else { + const u32 data = mem.Read(physical); + FGR_T(regs.cop0.status, instr.ft()) = data; + } } void Cop1::swc1(const Instruction instr) { - Mem& mem = Core::GetMem(); - Registers& regs = Core::GetRegs(); - const u64 addr = static_cast(static_cast(instr)) + regs.Read(instr.base()); + Mem &mem = Core::GetMem(); + Registers ®s = Core::GetRegs(); + const u64 addr = static_cast(static_cast(instr)) + regs.Read(instr.base()); - if (u32 physical; !regs.cop0.MapVAddr(Cop0::STORE, addr, physical)) { - regs.cop0.HandleTLBException(addr); - regs.cop0.FireException(Cop0::GetTLBExceptionCode(regs.cop0.tlbError, Cop0::STORE), 0, regs.oldPC); - } else { - mem.Write(physical, FGR_T(regs.cop0.status, instr.ft())); - } + if (u32 physical; !regs.cop0.MapVAddr(Cop0::STORE, addr, physical)) { + regs.cop0.HandleTLBException(addr); + regs.cop0.FireException(Cop0::GetTLBExceptionCode(regs.cop0.tlbError, Cop0::STORE), 0, regs.oldPC); + } else { + mem.Write(physical, FGR_T(regs.cop0.status, instr.ft())); + } } void Cop1::ldc1(const Instruction instr) { - Mem& mem = Core::GetMem(); - Registers& regs = Core::GetRegs(); - const u64 addr = static_cast(static_cast(instr)) + regs.Read(instr.base()); + Mem &mem = Core::GetMem(); + Registers ®s = Core::GetRegs(); + const u64 addr = static_cast(static_cast(instr)) + regs.Read(instr.base()); - if (u32 physical; !regs.cop0.MapVAddr(Cop0::LOAD, addr, physical)) { - regs.cop0.HandleTLBException(addr); - regs.cop0.FireException(Cop0::GetTLBExceptionCode(regs.cop0.tlbError, Cop0::LOAD), 0, regs.oldPC); - } else { - const u64 data = mem.Read(physical); - FGR_T(regs.cop0.status, instr.ft()) = data; - } + if (u32 physical; !regs.cop0.MapVAddr(Cop0::LOAD, addr, physical)) { + regs.cop0.HandleTLBException(addr); + regs.cop0.FireException(Cop0::GetTLBExceptionCode(regs.cop0.tlbError, Cop0::LOAD), 0, regs.oldPC); + } else { + const u64 data = mem.Read(physical); + FGR_T(regs.cop0.status, instr.ft()) = data; + } } void Cop1::sdc1(const Instruction instr) { - Mem& mem = Core::GetMem(); - Registers& regs = Core::GetRegs(); - const u64 addr = static_cast(static_cast(instr)) + regs.Read(instr.base()); + Mem &mem = Core::GetMem(); + Registers ®s = Core::GetRegs(); + const u64 addr = static_cast(static_cast(instr)) + regs.Read(instr.base()); - if (u32 physical; !regs.cop0.MapVAddr(Cop0::STORE, addr, physical)) { - regs.cop0.HandleTLBException(addr); - regs.cop0.FireException(Cop0::GetTLBExceptionCode(regs.cop0.tlbError, Cop0::STORE), 0, regs.oldPC); - } else { - mem.Write(physical, FGR_T(regs.cop0.status, instr.ft())); - } + if (u32 physical; !regs.cop0.MapVAddr(Cop0::STORE, addr, physical)) { + regs.cop0.HandleTLBException(addr); + regs.cop0.FireException(Cop0::GetTLBExceptionCode(regs.cop0.tlbError, Cop0::STORE), 0, regs.oldPC); + } else { + mem.Write(physical, FGR_T(regs.cop0.status, instr.ft())); + } } void Cop1::unimplemented() { - Registers& regs = Core::GetRegs(); - if (!CheckFPUUsable()) - return; - SetCauseUnimplemented(); - regs.cop0.FireException(ExceptionCode::FloatingPointError, 0, regs.oldPC); + Registers ®s = Core::GetRegs(); + if (!CheckFPUUsable()) + return; + SetCauseUnimplemented(); + regs.cop0.FireException(Cop0::ExceptionCode::FloatingPointError, 0, regs.oldPC); } void Cop1::mfc1(const Instruction instr) { - Registers& regs = Core::GetRegs(); - if (!CheckFPUUsable()) - return; - regs.Write(instr.rt(), FGR_T(regs.cop0.status, instr.fs())); + Registers ®s = Core::GetRegs(); + if (!CheckFPUUsable()) + return; + regs.Write(instr.rt(), FGR_T(regs.cop0.status, instr.fs())); } void Cop1::dmfc1(const Instruction instr) { - Registers& regs = Core::GetRegs(); - if (!CheckFPUUsable()) - return; - regs.Write(instr.rt(), FGR_S(regs.cop0.status, instr.fs())); + Registers ®s = Core::GetRegs(); + if (!CheckFPUUsable()) + return; + regs.Write(instr.rt(), FGR_S(regs.cop0.status, instr.fs())); } void Cop1::mtc1(const Instruction instr) { - Registers& regs = Core::GetRegs(); - if (!CheckFPUUsable()) - return; - FGR_T(regs.cop0.status, instr.fs()) = regs.Read(instr.rt()); + Registers ®s = Core::GetRegs(); + if (!CheckFPUUsable()) + return; + FGR_T(regs.cop0.status, instr.fs()) = regs.Read(instr.rt()); } void Cop1::dmtc1(const Instruction instr) { - Registers& regs = Core::GetRegs(); - if (!CheckFPUUsable()) - return; - FGR_S(regs.cop0.status, instr.fs()) = regs.Read(instr.rt()); + Registers ®s = Core::GetRegs(); + if (!CheckFPUUsable()) + return; + FGR_S(regs.cop0.status, instr.fs()) = regs.Read(instr.rt()); } } // namespace n64 diff --git a/src/backend/core/interpreter/decode.cpp b/src/backend/core/interpreter/decode.cpp index 35ca5d0..abf8c52 100644 --- a/src/backend/core/interpreter/decode.cpp +++ b/src/backend/core/interpreter/decode.cpp @@ -33,10 +33,10 @@ void Interpreter::special(const Instruction instr) { jalr(instr); break; case Instruction::SYSCALL: - regs.cop0.FireException(ExceptionCode::Syscall, 0, regs.oldPC); + regs.cop0.FireException(Cop0::ExceptionCode::Syscall, 0, regs.oldPC); break; case Instruction::BREAK: - regs.cop0.FireException(ExceptionCode::Breakpoint, 0, regs.oldPC); + regs.cop0.FireException(Cop0::ExceptionCode::Breakpoint, 0, regs.oldPC); break; case Instruction::SYNC: break; // SYNC @@ -222,7 +222,7 @@ void Interpreter::regimm(const Instruction instr) { void Interpreter::cop2Decode(const Instruction instr) { if (!regs.cop0.status.cu2) { - regs.cop0.FireException(ExceptionCode::CoprocessorUnusable, 2, regs.oldPC); + regs.cop0.FireException(Cop0::ExceptionCode::CoprocessorUnusable, 2, regs.oldPC); return; } switch (instr.rs()) { @@ -245,7 +245,7 @@ void Interpreter::cop2Decode(const Instruction instr) { ctc2(instr); break; default: - regs.cop0.FireException(ExceptionCode::ReservedInstruction, 2, regs.oldPC); + regs.cop0.FireException(Cop0::ExceptionCode::ReservedInstruction, 2, regs.oldPC); } } @@ -361,7 +361,7 @@ void Interpreter::Exec(const Instruction instr) { ldr(instr); break; case 0x1F: - regs.cop0.FireException(ExceptionCode::ReservedInstruction, 0, regs.oldPC); + regs.cop0.FireException(Cop0::ExceptionCode::ReservedInstruction, 0, regs.oldPC); break; case Instruction::LB: lb(instr); @@ -409,10 +409,7 @@ void Interpreter::Exec(const Instruction instr) { swr(instr); break; case Instruction::CACHE: - { - panic("CACHE 0b{:05b}, 0x{:04X}({}/r{} = 0x{:08X})", instr.op(), instr.offset(), - Registers::regNames[instr.base()], instr.base(), regs.Read(instr.base())); - } + // cache(instr); break; case Instruction::LL: ll(instr); diff --git a/src/backend/core/interpreter/instructions.cpp b/src/backend/core/interpreter/instructions.cpp index d2cf017..9c79db0 100644 --- a/src/backend/core/interpreter/instructions.cpp +++ b/src/backend/core/interpreter/instructions.cpp @@ -8,7 +8,7 @@ void Interpreter::add(const Instruction instr) { const u32 rs = regs.Read(instr.rs()); const u32 rt = regs.Read(instr.rt()); if (const u32 result = rs + rt; check_signed_overflow(rs, rt, result)) { - regs.cop0.FireException(ExceptionCode::Overflow, 0, regs.oldPC); + regs.cop0.FireException(Cop0::ExceptionCode::Overflow, 0, regs.oldPC); } else { regs.Write(instr.rd(), static_cast(result)); } @@ -25,7 +25,7 @@ void Interpreter::addi(const Instruction instr) { const u32 rs = regs.Read(instr.rs()); const u32 imm = static_cast(static_cast(instr)); if (const u32 result = rs + imm; check_signed_overflow(rs, imm, result)) { - regs.cop0.FireException(ExceptionCode::Overflow, 0, regs.oldPC); + regs.cop0.FireException(Cop0::ExceptionCode::Overflow, 0, regs.oldPC); } else { regs.Write(instr.rt(), static_cast(result)); } @@ -42,7 +42,7 @@ void Interpreter::dadd(const Instruction instr) { const u64 rs = regs.Read(instr.rs()); const u64 rt = regs.Read(instr.rt()); if (const u64 result = rt + rs; check_signed_overflow(rs, rt, result)) { - regs.cop0.FireException(ExceptionCode::Overflow, 0, regs.oldPC); + regs.cop0.FireException(Cop0::ExceptionCode::Overflow, 0, regs.oldPC); } else { regs.Write(instr.rd(), result); } @@ -58,7 +58,7 @@ void Interpreter::daddi(const Instruction instr) { const u64 imm = s64(s16(instr)); const u64 rs = regs.Read(instr.rs()); if (const u64 result = imm + rs; check_signed_overflow(rs, imm, result)) { - regs.cop0.FireException(ExceptionCode::Overflow, 0, regs.oldPC); + regs.cop0.FireException(Cop0::ExceptionCode::Overflow, 0, regs.oldPC); } else { regs.Write(instr.rt(), result); } @@ -202,7 +202,7 @@ void Interpreter::lh(const Instruction instr) { const u64 address = regs.Read(instr.rs()) + (s16)instr; if (check_address_error(0b1, address)) { regs.cop0.HandleTLBException(address); - regs.cop0.FireException(ExceptionCode::AddressErrorLoad, 0, regs.oldPC); + regs.cop0.FireException(Cop0::ExceptionCode::AddressErrorLoad, 0, regs.oldPC); return; } @@ -220,7 +220,7 @@ void Interpreter::lw(const Instruction instr) { const u64 address = regs.Read(instr.rs()) + offset; if (check_address_error(0b11, address)) { regs.cop0.HandleTLBException(address); - regs.cop0.FireException(ExceptionCode::AddressErrorLoad, 0, regs.oldPC); + regs.cop0.FireException(Cop0::ExceptionCode::AddressErrorLoad, 0, regs.oldPC); return; } @@ -242,7 +242,7 @@ void Interpreter::ll(const Instruction instr) { } else { const s32 result = mem.Read(physical); if (check_address_error(0b11, address)) { - regs.cop0.FireException(ExceptionCode::AddressErrorLoad, 0, regs.oldPC); + regs.cop0.FireException(Cop0::ExceptionCode::AddressErrorLoad, 0, regs.oldPC); return; } @@ -287,7 +287,7 @@ void Interpreter::ld(const Instruction instr) { const s64 address = regs.Read(instr.rs()) + (s16)instr; if (check_address_error(0b111, address)) { regs.cop0.HandleTLBException(address); - regs.cop0.FireException(ExceptionCode::AddressErrorLoad, 0, regs.oldPC); + regs.cop0.FireException(Cop0::ExceptionCode::AddressErrorLoad, 0, regs.oldPC); return; } @@ -303,7 +303,7 @@ void Interpreter::ld(const Instruction instr) { void Interpreter::lld(const Instruction instr) { if (!regs.cop0.is64BitAddressing && !regs.cop0.kernelMode) { - regs.cop0.FireException(ExceptionCode::ReservedInstruction, 0, regs.oldPC); + regs.cop0.FireException(Cop0::ExceptionCode::ReservedInstruction, 0, regs.oldPC); return; } @@ -314,7 +314,7 @@ void Interpreter::lld(const Instruction instr) { regs.cop0.FireException(Cop0::GetTLBExceptionCode(regs.cop0.tlbError, Cop0::LOAD), 0, regs.oldPC); } else { if (check_address_error(0b111, address)) { - regs.cop0.FireException(ExceptionCode::AddressErrorLoad, 0, regs.oldPC); + regs.cop0.FireException(Cop0::ExceptionCode::AddressErrorLoad, 0, regs.oldPC); } else { regs.Write(instr.rt(), mem.Read(paddr)); regs.cop0.llbit = true; @@ -369,7 +369,7 @@ void Interpreter::lhu(const Instruction instr) { const s64 address = regs.Read(instr.rs()) + (s16)instr; if (check_address_error(0b1, address)) { regs.cop0.HandleTLBException(address); - regs.cop0.FireException(ExceptionCode::AddressErrorLoad, 0, regs.oldPC); + regs.cop0.FireException(Cop0::ExceptionCode::AddressErrorLoad, 0, regs.oldPC); return; } u32 paddr; @@ -386,7 +386,7 @@ void Interpreter::lwu(const Instruction instr) { const s64 address = regs.Read(instr.rs()) + (s16)instr; if (check_address_error(0b11, address)) { regs.cop0.HandleTLBException(address); - regs.cop0.FireException(ExceptionCode::AddressErrorLoad, 0, regs.oldPC); + regs.cop0.FireException(Cop0::ExceptionCode::AddressErrorLoad, 0, regs.oldPC); return; } @@ -420,7 +420,7 @@ void Interpreter::sc(const Instruction instr) { if (check_address_error(0b11, address)) { regs.Write(instr.rt(), 0); regs.cop0.HandleTLBException(address); - regs.cop0.FireException(ExceptionCode::AddressErrorStore, 0, regs.oldPC); + regs.cop0.FireException(Cop0::ExceptionCode::AddressErrorStore, 0, regs.oldPC); return; } @@ -440,7 +440,7 @@ void Interpreter::sc(const Instruction instr) { void Interpreter::scd(const Instruction instr) { if (!regs.cop0.is64BitAddressing && !regs.cop0.kernelMode) { - regs.cop0.FireException(ExceptionCode::ReservedInstruction, 0, regs.oldPC); + regs.cop0.FireException(Cop0::ExceptionCode::ReservedInstruction, 0, regs.oldPC); return; } @@ -452,7 +452,7 @@ void Interpreter::scd(const Instruction instr) { if (check_address_error(0b111, address)) { regs.Write(instr.rt(), 0); regs.cop0.HandleTLBException(address); - regs.cop0.FireException(ExceptionCode::AddressErrorStore, 0, regs.oldPC); + regs.cop0.FireException(Cop0::ExceptionCode::AddressErrorStore, 0, regs.oldPC); return; } @@ -487,7 +487,7 @@ void Interpreter::sw(const Instruction instr) { const u64 address = regs.Read(instr.rs()) + offset; if (check_address_error(0b11, address)) { regs.cop0.HandleTLBException(address); - regs.cop0.FireException(ExceptionCode::AddressErrorStore, 0, regs.oldPC); + regs.cop0.FireException(Cop0::ExceptionCode::AddressErrorStore, 0, regs.oldPC); return; } @@ -504,7 +504,7 @@ void Interpreter::sd(const Instruction instr) { const s64 address = regs.Read(instr.rs()) + (s16)instr; if (check_address_error(0b111, address)) { regs.cop0.HandleTLBException(address); - regs.cop0.FireException(ExceptionCode::AddressErrorStore, 0, regs.oldPC); + regs.cop0.FireException(Cop0::ExceptionCode::AddressErrorStore, 0, regs.oldPC); return; } @@ -757,7 +757,7 @@ void Interpreter::dsub(const Instruction instr) { const s64 rt = regs.Read(instr.rt()); const s64 rs = regs.Read(instr.rs()); if (const s64 result = rs - rt; check_signed_underflow(rs, rt, result)) { - regs.cop0.FireException(ExceptionCode::Overflow, 0, regs.oldPC); + regs.cop0.FireException(Cop0::ExceptionCode::Overflow, 0, regs.oldPC); } else { regs.Write(instr.rd(), result); } @@ -775,7 +775,7 @@ void Interpreter::sub(const Instruction instr) { const s32 rs = regs.Read(instr.rs()); const s32 result = rs - rt; if (check_signed_underflow(rs, rt, result)) { - regs.cop0.FireException(ExceptionCode::Overflow, 0, regs.oldPC); + regs.cop0.FireException(Cop0::ExceptionCode::Overflow, 0, regs.oldPC); } else { regs.Write(instr.rd(), result); } @@ -831,7 +831,7 @@ void Interpreter::mthi(const Instruction instr) { regs.hi = regs.Read(instr void Interpreter::trap(const bool cond) const { Cop0 &cop0 = Core::GetRegs().cop0; if (cond) { - cop0.FireException(ExceptionCode::Trap, 0, regs.oldPC); + cop0.FireException(Cop0::ExceptionCode::Trap, 0, regs.oldPC); } } @@ -852,21 +852,84 @@ void Interpreter::cfc2(const Instruction) {} void Interpreter::cache(const Instruction instr) { u8 type = instr.op() & 3; - u8 op = (instr.op() >> 2) & 7; + const u8 op = (instr.op() >> 2) & 7; + u64 vaddr = regs.Read(instr.rs()) + (s16)instr.offset(); + u32 paddr; + if (!regs.cop0.MapVAddr(Cop0::LOAD, vaddr, paddr)) { + regs.cop0.HandleTLBException(vaddr); + regs.cop0.FireException(Cop0::GetTLBExceptionCode(regs.cop0.tlbError, Cop0::LOAD), 0, regs.oldPC); + return; + } + + u32 ptag = GetPhysicalAddressPTag(paddr); + if (type > 1) panic("Unknown cache type {}", type); if (type == 0) - return cache_type_instruction(op); + return cache_type_instruction(op, vaddr, paddr, ptag); - if (type == 1) - return cache_type_data(op); + return cache_type_data(op, vaddr, paddr, ptag); } -void Interpreter::cache_type_instruction(const u8 op) { +void Interpreter::cache_type_instruction(const u8 op, const u64 vaddr, const u32 paddr, const u32 ptag) { switch (op) { case 0: - icache. + icache.InvalidateIndex(vaddr); + break; + case 1: + icache.LoadTag(vaddr); + break; + case 2: + icache.StoreTag(vaddr, regs.cop0.tagLo.ptaglo, regs.cop0); + break; + case 4: + icache.InvalidateIndex(vaddr, ptag); + break; + case 5: + icache.FillLine(vaddr, paddr); + break; + case 6: + icache.WriteBack(vaddr, paddr, ptag); + break; + default: + panic("Unimplemented icache op 0b{:03b}", op); + } +} + +void Interpreter::cache_type_data(const u8 op, const u64 vaddr, const u32 paddr, const u32 ptag) { + switch (op) { + case 0: + dcache.WriteBack(vaddr, paddr); + break; + case 1: + dcache.LoadTag(vaddr); + break; + case 2: + dcache.StoreTag(vaddr, regs.cop0.tagLo.ptaglo, regs.cop0); + break; + case 3: + { + auto &line = dcache.lines[GetDCacheLineIndex(vaddr)]; + if ((line.ptag != ptag || !line.valid) && line.dirty) + dcache.WriteBack(vaddr, paddr); + + line.ptag = ptag; + line.valid = true; + line.dirty = true; + } + break; + case 4: + dcache.InvalidateIndex(vaddr, ptag); + break; + case 5: + dcache.WriteBack(vaddr, paddr, ptag); + break; + case 6: + dcache.WriteBack(vaddr, paddr, ptag); + break; + default: + panic("Unimplemented dcache op 0b{:03b}", op); } } } // namespace n64 diff --git a/src/backend/core/jit/decode.cpp b/src/backend/core/jit/decode.cpp index 2b14735..27c025f 100644 --- a/src/backend/core/jit/decode.cpp +++ b/src/backend/core/jit/decode.cpp @@ -3,463 +3,463 @@ namespace n64 { void JIT::special(const Instruction instr) { - // 00rr_rccc - switch (instr.special()) { - case Instruction::SLL: - if (instr != 0) { - sll(instr); + // 00rr_rccc + switch (instr.special()) { + case Instruction::SLL: + if (instr != 0) { + sll(instr); + } + break; + case Instruction::SRL: + srl(instr); + break; + case Instruction::SRA: + sra(instr); + break; + case Instruction::SLLV: + sllv(instr); + break; + case Instruction::SRLV: + srlv(instr); + break; + case Instruction::SRAV: + srav(instr); + break; + case Instruction::JR: + jr(instr); + break; + case Instruction::JALR: + jalr(instr); + break; + case Instruction::SYSCALL: + regs.cop0.FireException(Cop0::ExceptionCode::Syscall, 0, regs.oldPC); + break; + case Instruction::BREAK: + regs.cop0.FireException(Cop0::ExceptionCode::Breakpoint, 0, regs.oldPC); + break; + case Instruction::SYNC: + break; // SYNC + case Instruction::MFHI: + mfhi(instr); + break; + case Instruction::MTHI: + mthi(instr); + break; + case Instruction::MFLO: + mflo(instr); + break; + case Instruction::MTLO: + mtlo(instr); + break; + case Instruction::DSLLV: + dsllv(instr); + break; + case Instruction::DSRLV: + dsrlv(instr); + break; + case Instruction::DSRAV: + dsrav(instr); + break; + case Instruction::MULT: + mult(instr); + break; + case Instruction::MULTU: + multu(instr); + break; + case Instruction::DIV: + div(instr); + break; + case Instruction::DIVU: + divu(instr); + break; + case Instruction::DMULT: + dmult(instr); + break; + case Instruction::DMULTU: + dmultu(instr); + break; + case Instruction::DDIV: + ddiv(instr); + break; + case Instruction::DDIVU: + ddivu(instr); + break; + case Instruction::ADD: + add(instr); + break; + case Instruction::ADDU: + addu(instr); + break; + case Instruction::SUB: + sub(instr); + break; + case Instruction::SUBU: + subu(instr); + break; + case Instruction::AND: + and_(instr); + break; + case Instruction::OR: + or_(instr); + break; + case Instruction::XOR: + xor_(instr); + break; + case Instruction::NOR: + nor(instr); + break; + case Instruction::SLT: + slt(instr); + break; + case Instruction::SLTU: + sltu(instr); + break; + case Instruction::DADD: + dadd(instr); + break; + case Instruction::DADDU: + daddu(instr); + break; + case Instruction::DSUB: + dsub(instr); + break; + case Instruction::DSUBU: + dsubu(instr); + break; + case Instruction::TGE: + trap(regs.Read(instr.rs()) >= regs.Read(instr.rt())); + break; + case Instruction::TGEU: + trap(regs.Read(instr.rs()) >= regs.Read(instr.rt())); + break; + case Instruction::TLT: + trap(regs.Read(instr.rs()) < regs.Read(instr.rt())); + break; + case Instruction::TLTU: + trap(regs.Read(instr.rs()) < regs.Read(instr.rt())); + break; + case Instruction::TEQ: + trap(regs.Read(instr.rs()) == regs.Read(instr.rt())); + break; + case Instruction::TNE: + trap(regs.Read(instr.rs()) != regs.Read(instr.rt())); + break; + case Instruction::DSLL: + dsll(instr); + break; + case Instruction::DSRL: + dsrl(instr); + break; + case Instruction::DSRA: + dsra(instr); + break; + case Instruction::DSLL32: + dsll32(instr); + break; + case Instruction::DSRL32: + dsrl32(instr); + break; + case Instruction::DSRA32: + dsra32(instr); + break; + default: + panic("Unimplemented special {} ({:08X}) (pc: {:016X})", instr.special(), u32(instr), + static_cast(regs.oldPC)); } - break; - case Instruction::SRL: - srl(instr); - break; - case Instruction::SRA: - sra(instr); - break; - case Instruction::SLLV: - sllv(instr); - break; - case Instruction::SRLV: - srlv(instr); - break; - case Instruction::SRAV: - srav(instr); - break; - case Instruction::JR: - jr(instr); - break; - case Instruction::JALR: - jalr(instr); - break; - case Instruction::SYSCALL: - regs.cop0.FireException(ExceptionCode::Syscall, 0, regs.oldPC); - break; - case Instruction::BREAK: - regs.cop0.FireException(ExceptionCode::Breakpoint, 0, regs.oldPC); - break; - case Instruction::SYNC: - break; // SYNC - case Instruction::MFHI: - mfhi(instr); - break; - case Instruction::MTHI: - mthi(instr); - break; - case Instruction::MFLO: - mflo(instr); - break; - case Instruction::MTLO: - mtlo(instr); - break; - case Instruction::DSLLV: - dsllv(instr); - break; - case Instruction::DSRLV: - dsrlv(instr); - break; - case Instruction::DSRAV: - dsrav(instr); - break; - case Instruction::MULT: - mult(instr); - break; - case Instruction::MULTU: - multu(instr); - break; - case Instruction::DIV: - div(instr); - break; - case Instruction::DIVU: - divu(instr); - break; - case Instruction::DMULT: - dmult(instr); - break; - case Instruction::DMULTU: - dmultu(instr); - break; - case Instruction::DDIV: - ddiv(instr); - break; - case Instruction::DDIVU: - ddivu(instr); - break; - case Instruction::ADD: - add(instr); - break; - case Instruction::ADDU: - addu(instr); - break; - case Instruction::SUB: - sub(instr); - break; - case Instruction::SUBU: - subu(instr); - break; - case Instruction::AND: - and_(instr); - break; - case Instruction::OR: - or_(instr); - break; - case Instruction::XOR: - xor_(instr); - break; - case Instruction::NOR: - nor(instr); - break; - case Instruction::SLT: - slt(instr); - break; - case Instruction::SLTU: - sltu(instr); - break; - case Instruction::DADD: - dadd(instr); - break; - case Instruction::DADDU: - daddu(instr); - break; - case Instruction::DSUB: - dsub(instr); - break; - case Instruction::DSUBU: - dsubu(instr); - break; - case Instruction::TGE: - trap(regs.Read(instr.rs()) >= regs.Read(instr.rt())); - break; - case Instruction::TGEU: - trap(regs.Read(instr.rs()) >= regs.Read(instr.rt())); - break; - case Instruction::TLT: - trap(regs.Read(instr.rs()) < regs.Read(instr.rt())); - break; - case Instruction::TLTU: - trap(regs.Read(instr.rs()) < regs.Read(instr.rt())); - break; - case Instruction::TEQ: - trap(regs.Read(instr.rs()) == regs.Read(instr.rt())); - break; - case Instruction::TNE: - trap(regs.Read(instr.rs()) != regs.Read(instr.rt())); - break; - case Instruction::DSLL: - dsll(instr); - break; - case Instruction::DSRL: - dsrl(instr); - break; - case Instruction::DSRA: - dsra(instr); - break; - case Instruction::DSLL32: - dsll32(instr); - break; - case Instruction::DSRL32: - dsrl32(instr); - break; - case Instruction::DSRA32: - dsra32(instr); - break; - default: - panic("Unimplemented special {} ({:08X}) (pc: {:016X})", instr.special(), u32(instr), - static_cast(regs.oldPC)); - } } void JIT::regimm(const Instruction instr) { - // 000r_rccc - switch (instr.regimm()) { - case Instruction::BLTZ: - bltz(instr); - break; - case Instruction::BGEZ: - bgez(instr); - break; - case Instruction::BLTZL: - bltzl(instr); - break; - case Instruction::BGEZL: - bgezl(instr); - break; - case Instruction::TGEI: - trap(regs.Read(instr.rs()) >= static_cast(static_cast(instr))); - break; - case Instruction::TGEIU: - trap(regs.Read(instr.rs()) >= static_cast(static_cast(static_cast(instr)))); - break; - case Instruction::TLTI: - trap(regs.Read(instr.rs()) < static_cast(static_cast(instr))); - break; - case Instruction::TLTIU: - trap(regs.Read(instr.rs()) < static_cast(static_cast(static_cast(instr)))); - break; - case Instruction::TEQI: - trap(regs.Read(instr.rs()) == static_cast(static_cast(instr))); - break; - case Instruction::TNEI: - trap(regs.Read(instr.rs()) != static_cast(static_cast(instr))); - break; - case Instruction::BLTZAL: - bltzal(instr); - break; - case Instruction::BGEZAL: - bgezal(instr); - break; - case Instruction::BLTZALL: - bltzall(instr); - break; - case Instruction::BGEZALL: - bgezall(instr); - break; - default: - panic("Unimplemented regimm {} ({:08X}) (pc: {:016X})", instr.regimm(), u32(instr), - static_cast(regs.oldPC)); - } + // 000r_rccc + switch (instr.regimm()) { + case Instruction::BLTZ: + bltz(instr); + break; + case Instruction::BGEZ: + bgez(instr); + break; + case Instruction::BLTZL: + bltzl(instr); + break; + case Instruction::BGEZL: + bgezl(instr); + break; + case Instruction::TGEI: + trap(regs.Read(instr.rs()) >= static_cast(static_cast(instr))); + break; + case Instruction::TGEIU: + trap(regs.Read(instr.rs()) >= static_cast(static_cast(static_cast(instr)))); + break; + case Instruction::TLTI: + trap(regs.Read(instr.rs()) < static_cast(static_cast(instr))); + break; + case Instruction::TLTIU: + trap(regs.Read(instr.rs()) < static_cast(static_cast(static_cast(instr)))); + break; + case Instruction::TEQI: + trap(regs.Read(instr.rs()) == static_cast(static_cast(instr))); + break; + case Instruction::TNEI: + trap(regs.Read(instr.rs()) != static_cast(static_cast(instr))); + break; + case Instruction::BLTZAL: + bltzal(instr); + break; + case Instruction::BGEZAL: + bgezal(instr); + break; + case Instruction::BLTZALL: + bltzall(instr); + break; + case Instruction::BGEZALL: + bgezall(instr); + break; + default: + panic("Unimplemented regimm {} ({:08X}) (pc: {:016X})", instr.regimm(), u32(instr), + static_cast(regs.oldPC)); + } } void JIT::Emit(const Instruction instr) { - switch (instr.opcode()) { - case Instruction::SPECIAL: - special(instr); - break; - case Instruction::REGIMM: - regimm(instr); - break; - case Instruction::J: - j(instr); - break; - case Instruction::JAL: - jal(instr); - break; - case Instruction::BEQ: - beq(instr); - break; - case Instruction::BNE: - bne(instr); - break; - case Instruction::BLEZ: - blez(instr); - break; - case Instruction::BGTZ: - bgtz(instr); - break; - case Instruction::ADDI: - addi(instr); - break; - case Instruction::ADDIU: - addiu(instr); - break; - case Instruction::SLTI: - slti(instr); - break; - case Instruction::SLTIU: - sltiu(instr); - break; - case Instruction::ANDI: - andi(instr); - break; - case Instruction::ORI: - ori(instr); - break; - case Instruction::XORI: - xori(instr); - break; - case Instruction::LUI: - lui(instr); - break; - case Instruction::COP0: - switch (instr.cop_rs()) { - case 0x00: - code.mov(code.ARG2, instr); - emitMemberFunctionCall(&Cop0::mfc0, ®s.cop0); + switch (instr.opcode()) { + case Instruction::SPECIAL: + special(instr); break; - case 0x01: - code.mov(code.ARG2, instr); - emitMemberFunctionCall(&Cop0::dmfc0, ®s.cop0); + case Instruction::REGIMM: + regimm(instr); break; - case 0x04: - code.mov(code.ARG2, instr); - emitMemberFunctionCall(&Cop0::mtc0, ®s.cop0); + case Instruction::J: + j(instr); break; - case 0x05: - code.mov(code.ARG2, instr); - emitMemberFunctionCall(&Cop0::dmtc0, ®s.cop0); + case Instruction::JAL: + jal(instr); break; - case 0x10 ... 0x1F: - switch (instr.cop_funct()) { - case 0x01: - emitMemberFunctionCall(&Cop0::tlbr, ®s.cop0); - break; - case 0x02: - code.mov(code.ARG2, COP0_REG_INDEX); - emitMemberFunctionCall(&Cop0::GetReg32, ®s.cop0); - code.mov(code.ARG2, code.rax); - code.and_(code.ARG2, 0x3F); - emitMemberFunctionCall(&Cop0::tlbw, ®s.cop0); - break; - case 0x06: - emitMemberFunctionCall(&Cop0::GetRandom, ®s.cop0); - code.mov(code.ARG2, code.rax); - emitMemberFunctionCall(&Cop0::tlbw, ®s.cop0); - break; - case 0x08: - emitMemberFunctionCall(&Cop0::tlbp, ®s.cop0); - break; - case 0x18: - emitMemberFunctionCall(&Cop0::eret, ®s.cop0); - break; - default: - panic("Unimplemented COP0 function {} ({:08X}) ({:016X})", instr.cop_funct(), u32(instr), - regs.oldPC); - } + case Instruction::BEQ: + beq(instr); break; - default: - panic("Unimplemented COP0 instruction {}", instr.cop_rs()); - } - break; - case Instruction::COP1: - { - if (instr.cop_rs() == 0x08) { - switch (instr.cop_rt()) { - case 0: - // if (!regs.cop1.CheckFPUUsable()) - // return; - bfc0(instr); - break; - case 1: - // if (!regs.cop1.CheckFPUUsable()) - // return; - bfc1(instr); - break; - case 2: - // if (!regs.cop1.CheckFPUUsable()) - // return; - blfc0(instr); - break; - case 3: - // if (!regs.cop1.CheckFPUUsable()) - // return; - blfc1(instr); - break; + case Instruction::BNE: + bne(instr); + break; + case Instruction::BLEZ: + blez(instr); + break; + case Instruction::BGTZ: + bgtz(instr); + break; + case Instruction::ADDI: + addi(instr); + break; + case Instruction::ADDIU: + addiu(instr); + break; + case Instruction::SLTI: + slti(instr); + break; + case Instruction::SLTIU: + sltiu(instr); + break; + case Instruction::ANDI: + andi(instr); + break; + case Instruction::ORI: + ori(instr); + break; + case Instruction::XORI: + xori(instr); + break; + case Instruction::LUI: + lui(instr); + break; + case Instruction::COP0: + switch (instr.cop_rs()) { + case 0x00: + code.mov(code.ARG2, instr); + emitMemberFunctionCall(&Cop0::mfc0, ®s.cop0); + break; + case 0x01: + code.mov(code.ARG2, instr); + emitMemberFunctionCall(&Cop0::dmfc0, ®s.cop0); + break; + case 0x04: + code.mov(code.ARG2, instr); + emitMemberFunctionCall(&Cop0::mtc0, ®s.cop0); + break; + case 0x05: + code.mov(code.ARG2, instr); + emitMemberFunctionCall(&Cop0::dmtc0, ®s.cop0); + break; + case 0x10 ... 0x1F: + switch (instr.cop_funct()) { + case 0x01: + emitMemberFunctionCall(&Cop0::tlbr, ®s.cop0); + break; + case 0x02: + code.mov(code.ARG2, COP0_REG_INDEX); + emitMemberFunctionCall(&Cop0::GetReg32, ®s.cop0); + code.mov(code.ARG2, code.rax); + code.and_(code.ARG2, 0x3F); + emitMemberFunctionCall(&Cop0::tlbw, ®s.cop0); + break; + case 0x06: + emitMemberFunctionCall(&Cop0::GetRandom, ®s.cop0); + code.mov(code.ARG2, code.rax); + emitMemberFunctionCall(&Cop0::tlbw, ®s.cop0); + break; + case 0x08: + emitMemberFunctionCall(&Cop0::tlbp, ®s.cop0); + break; + case 0x18: + emitMemberFunctionCall(&Cop0::eret, ®s.cop0); + break; + default: + panic("Unimplemented COP0 function {} ({:08X}) ({:016X})", instr.cop_funct(), u32(instr), regs.oldPC); + } + break; default: - panic("Undefined BC COP1 {:02X}", instr.cop_rt()); + panic("Unimplemented COP0 instruction {}", instr.cop_rs()); } break; - } - regs.cop1.decode(instr); + case Instruction::COP1: + { + if (instr.cop_rs() == 0x08) { + switch (instr.cop_rt()) { + case 0: + // if (!regs.cop1.CheckFPUUsable()) + // return; + bfc0(instr); + break; + case 1: + // if (!regs.cop1.CheckFPUUsable()) + // return; + bfc1(instr); + break; + case 2: + // if (!regs.cop1.CheckFPUUsable()) + // return; + blfc0(instr); + break; + case 3: + // if (!regs.cop1.CheckFPUUsable()) + // return; + blfc1(instr); + break; + default: + panic("Undefined BC COP1 {:02X}", instr.cop_rt()); + } + break; + } + regs.cop1.decode(instr); + } + break; + case Instruction::COP2: + break; + case Instruction::BEQL: + beql(instr); + break; + case Instruction::BNEL: + bnel(instr); + break; + case Instruction::BLEZL: + blezl(instr); + break; + case Instruction::BGTZL: + bgtzl(instr); + break; + case Instruction::DADDI: + daddi(instr); + break; + case Instruction::DADDIU: + daddiu(instr); + break; + case Instruction::LDL: + ldl(instr); + break; + case Instruction::LDR: + ldr(instr); + break; + case 0x1F: + regs.cop0.FireException(Cop0::ExceptionCode::ReservedInstruction, 0, regs.oldPC); + break; + case Instruction::LB: + lb(instr); + break; + case Instruction::LH: + lh(instr); + break; + case Instruction::LWL: + lwl(instr); + break; + case Instruction::LW: + lw(instr); + break; + case Instruction::LBU: + lbu(instr); + break; + case Instruction::LHU: + lhu(instr); + break; + case Instruction::LWR: + lwr(instr); + break; + case Instruction::LWU: + lwu(instr); + break; + case Instruction::SB: + sb(instr); + break; + case Instruction::SH: + sh(instr); + break; + case Instruction::SWL: + swl(instr); + break; + case Instruction::SW: + sw(instr); + break; + case Instruction::SDL: + sdl(instr); + break; + case Instruction::SDR: + sdr(instr); + break; + case Instruction::SWR: + swr(instr); + break; + case Instruction::CACHE: + break; // CACHE + case Instruction::LL: + ll(instr); + break; + case Instruction::LWC1: + lwc1(instr); + break; + case Instruction::LLD: + lld(instr); + break; + case Instruction::LDC1: + ldc1(instr); + break; + case Instruction::LD: + ld(instr); + break; + case Instruction::SC: + sc(instr); + break; + case Instruction::SWC1: + swc1(instr); + break; + case Instruction::SCD: + scd(instr); + break; + case Instruction::SDC1: + sdc1(instr); + break; + case Instruction::SD: + sd(instr); + break; + default: + DumpBlockCacheToDisk(); + panic("Unimplemented instruction {:02X} ({:08X}) (pc: {:016X})", instr.opcode(), u32(instr), + static_cast(regs.oldPC)); } - break; - case Instruction::COP2: - break; - case Instruction::BEQL: - beql(instr); - break; - case Instruction::BNEL: - bnel(instr); - break; - case Instruction::BLEZL: - blezl(instr); - break; - case Instruction::BGTZL: - bgtzl(instr); - break; - case Instruction::DADDI: - daddi(instr); - break; - case Instruction::DADDIU: - daddiu(instr); - break; - case Instruction::LDL: - ldl(instr); - break; - case Instruction::LDR: - ldr(instr); - break; - case 0x1F: - regs.cop0.FireException(ExceptionCode::ReservedInstruction, 0, regs.oldPC); - break; - case Instruction::LB: - lb(instr); - break; - case Instruction::LH: - lh(instr); - break; - case Instruction::LWL: - lwl(instr); - break; - case Instruction::LW: - lw(instr); - break; - case Instruction::LBU: - lbu(instr); - break; - case Instruction::LHU: - lhu(instr); - break; - case Instruction::LWR: - lwr(instr); - break; - case Instruction::LWU: - lwu(instr); - break; - case Instruction::SB: - sb(instr); - break; - case Instruction::SH: - sh(instr); - break; - case Instruction::SWL: - swl(instr); - break; - case Instruction::SW: - sw(instr); - break; - case Instruction::SDL: - sdl(instr); - break; - case Instruction::SDR: - sdr(instr); - break; - case Instruction::SWR: - swr(instr); - break; - case Instruction::CACHE: - break; // CACHE - case Instruction::LL: - ll(instr); - break; - case Instruction::LWC1: - lwc1(instr); - break; - case Instruction::LLD: - lld(instr); - break; - case Instruction::LDC1: - ldc1(instr); - break; - case Instruction::LD: - ld(instr); - break; - case Instruction::SC: - sc(instr); - break; - case Instruction::SWC1: - swc1(instr); - break; - case Instruction::SCD: - scd(instr); - break; - case Instruction::SDC1: - sdc1(instr); - break; - case Instruction::SD: - sd(instr); - break; - default: - DumpBlockCacheToDisk(); - panic("Unimplemented instruction {:02X} ({:08X}) (pc: {:016X})", instr.opcode(), u32(instr), static_cast(regs.oldPC)); - } } } // namespace n64 diff --git a/src/backend/core/jit/instructions.cpp b/src/backend/core/jit/instructions.cpp index 1d2b04f..77aa0da 100644 --- a/src/backend/core/jit/instructions.cpp +++ b/src/backend/core/jit/instructions.cpp @@ -7,1066 +7,1068 @@ namespace n64 { using namespace Xbyak::util; void JIT::lui(const Instruction instr) { - u64 val = static_cast(static_cast(instr)); - val <<= 16; - regs.Write(instr.rt(), val); + u64 val = static_cast(static_cast(instr)); + val <<= 16; + regs.Write(instr.rt(), val); } void JIT::add(const Instruction instr) { - if (regs.IsRegConstant(instr.rs(), instr.rt())) { - const u32 rs = regs.Read(instr.rs()); - const u32 rt = regs.Read(instr.rt()); - const u32 result = rs + rt; - if (check_signed_overflow(rs, rt, result)) { - // regs.cop0.FireException(ExceptionCode::Overflow, 0, regs.oldPC); - panic("[JIT]: Unhandled Overflow exception in ADD!"); + if (regs.IsRegConstant(instr.rs(), instr.rt())) { + const u32 rs = regs.Read(instr.rs()); + const u32 rt = regs.Read(instr.rt()); + const u32 result = rs + rt; + if (check_signed_overflow(rs, rt, result)) { + // regs.cop0.FireException(Cop0::ExceptionCode::Overflow, 0, regs.oldPC); + panic("[JIT]: Unhandled Overflow exception in ADD!"); + } + + regs.Write(instr.rd(), result); + return; } - regs.Write(instr.rd(), result); - return; - } + if (regs.IsRegConstant(instr.rs())) { + const u32 rs = regs.Read(instr.rs()); + regs.Read(instr.rt(), code.SCR1.cvt32()); + code.add(code.SCR1.cvt32(), rs); + regs.Write(instr.rd(), code.SCR1.cvt32()); + + return; + } + + if (regs.IsRegConstant(instr.rt())) { + const u32 rt = regs.Read(instr.rt()); + regs.Read(instr.rs(), code.SCR1.cvt32()); + code.add(code.SCR1.cvt32(), rt); + regs.Write(instr.rd(), code.SCR1.cvt32()); + + return; + } - if (regs.IsRegConstant(instr.rs())) { - const u32 rs = regs.Read(instr.rs()); regs.Read(instr.rt(), code.SCR1.cvt32()); - code.add(code.SCR1.cvt32(), rs); + regs.Read(instr.rs(), code.SCR2.cvt32()); + code.add(code.SCR1.cvt32(), code.SCR2.cvt32()); regs.Write(instr.rd(), code.SCR1.cvt32()); - - return; - } - - if (regs.IsRegConstant(instr.rt())) { - const u32 rt = regs.Read(instr.rt()); - regs.Read(instr.rs(), code.SCR1.cvt32()); - code.add(code.SCR1.cvt32(), rt); - regs.Write(instr.rd(), code.SCR1.cvt32()); - - return; - } - - regs.Read(instr.rt(), code.SCR1.cvt32()); - regs.Read(instr.rs(), code.SCR2.cvt32()); - code.add(code.SCR1.cvt32(), code.SCR2.cvt32()); - regs.Write(instr.rd(), code.SCR1.cvt32()); } void JIT::addu(const Instruction instr) { - if (regs.IsRegConstant(instr.rs(), instr.rt())) { - const s32 rs = regs.Read(instr.rs()); - const s32 rt = regs.Read(instr.rt()); - const s32 result = rs + rt; + if (regs.IsRegConstant(instr.rs(), instr.rt())) { + const s32 rs = regs.Read(instr.rs()); + const s32 rt = regs.Read(instr.rt()); + const s32 result = rs + rt; - regs.Write(instr.rd(), result); - return; - } + regs.Write(instr.rd(), result); + return; + } - if (regs.IsRegConstant(instr.rs())) { - const s32 rs = regs.Read(instr.rs()); - regs.Read(instr.rt(), code.SCR1.cvt32()); - code.add(code.SCR1.cvt32(), rs); - regs.Write(instr.rd(), code.SCR1.cvt32()); - return; - } + if (regs.IsRegConstant(instr.rs())) { + const s32 rs = regs.Read(instr.rs()); + regs.Read(instr.rt(), code.SCR1.cvt32()); + code.add(code.SCR1.cvt32(), rs); + regs.Write(instr.rd(), code.SCR1.cvt32()); + return; + } + + if (regs.IsRegConstant(instr.rt())) { + const s32 rs = regs.Read(instr.rt()); + regs.Read(instr.rs(), code.SCR1.cvt32()); + code.add(code.SCR1.cvt32(), rs); + regs.Write(instr.rd(), code.SCR1.cvt32()); + return; + } - if (regs.IsRegConstant(instr.rt())) { - const s32 rs = regs.Read(instr.rt()); regs.Read(instr.rs(), code.SCR1.cvt32()); - code.add(code.SCR1.cvt32(), rs); + regs.Read(instr.rt(), code.SCR2.cvt32()); + code.add(code.SCR1.cvt32(), code.SCR2.cvt32()); regs.Write(instr.rd(), code.SCR1.cvt32()); - return; - } - - regs.Read(instr.rs(), code.SCR1.cvt32()); - regs.Read(instr.rt(), code.SCR2.cvt32()); - code.add(code.SCR1.cvt32(), code.SCR2.cvt32()); - regs.Write(instr.rd(), code.SCR1.cvt32()); } void JIT::addi(const Instruction instr) { - u32 imm = s32(s16(instr)); - if (regs.IsRegConstant(instr.rs())) { - auto rs = regs.Read(instr.rs()); - u32 result = rs + imm; - if (check_signed_overflow(rs, imm, result)) { - panic("[JIT]: Unhandled Overflow exception in ADDI!"); + u32 imm = s32(s16(instr)); + if (regs.IsRegConstant(instr.rs())) { + auto rs = regs.Read(instr.rs()); + u32 result = rs + imm; + if (check_signed_overflow(rs, imm, result)) { + panic("[JIT]: Unhandled Overflow exception in ADDI!"); + } + + regs.Write(instr.rt(), static_cast(result)); + return; } - regs.Write(instr.rt(), static_cast(result)); - return; - } - - regs.Read(instr.rs(), code.SCR1.cvt32()); - code.add(code.eax, SCR1.cvt32()); - regs.Write(instr.rt(), code.SCR1.cvt32()); + regs.Read(instr.rs(), code.SCR1.cvt32()); + code.add(code.eax, SCR1.cvt32()); + regs.Write(instr.rt(), code.SCR1.cvt32()); } void JIT::addiu(const Instruction instr) { - u32 imm = s32(s16(instr)); - if (regs.IsRegConstant(instr.rs())) { - auto rs = regs.Read(instr.rs()); - u32 result = rs + imm; - regs.Write(instr.rt(), s32(result)); + u32 imm = s32(s16(instr)); + if (regs.IsRegConstant(instr.rs())) { + auto rs = regs.Read(instr.rs()); + u32 result = rs + imm; + regs.Write(instr.rt(), s32(result)); - return; - } + return; + } - regs.Read(instr.rs(), code.SCR1.cvt32()); - code.add(code.SCR1.cvt32(), imm); - regs.Write(instr.rt(), code.SCR1.cvt32()); + regs.Read(instr.rs(), code.SCR1.cvt32()); + code.add(code.SCR1.cvt32(), imm); + regs.Write(instr.rt(), code.SCR1.cvt32()); } void JIT::andi(const Instruction instr) { - const s64 imm = static_cast(instr); - if (regs.IsRegConstant(instr.rs())) { - regs.Write(instr.rt(), regs.Read(instr.rs()) & imm); - return; - } + const s64 imm = static_cast(instr); + if (regs.IsRegConstant(instr.rs())) { + regs.Write(instr.rt(), regs.Read(instr.rs()) & imm); + return; + } - regs.Read(instr.rs(), code.SCR1); - code.and_(code.SCR1, imm); - regs.Write(instr.rt(), code.SCR1); + regs.Read(instr.rs(), code.SCR1); + code.and_(code.SCR1, imm); + regs.Write(instr.rt(), code.SCR1); } void JIT::and_(const Instruction instr) { - if (regs.IsRegConstant(instr.rs(), instr.rt())) { - regs.Write(instr.rd(), regs.Read(instr.rs()) & regs.Read(instr.rt())); - return; - } + if (regs.IsRegConstant(instr.rs(), instr.rt())) { + regs.Write(instr.rd(), regs.Read(instr.rs()) & regs.Read(instr.rt())); + return; + } - if (regs.IsRegConstant(instr.rs())) { - const auto rs = regs.Read(instr.rs()); - regs.Read(instr.rt(), code.SCR1); - code.and_(code.SCR1, rs); - regs.Write(instr.rd(), code.SCR1); - return; - } + if (regs.IsRegConstant(instr.rs())) { + const auto rs = regs.Read(instr.rs()); + regs.Read(instr.rt(), code.SCR1); + code.and_(code.SCR1, rs); + regs.Write(instr.rd(), code.SCR1); + return; + } + + if (regs.IsRegConstant(instr.rt())) { + const auto rt = regs.Read(instr.rt()); + regs.Read(instr.rs(), code.SCR1); + code.and_(code.SCR1, rt); + regs.Write(instr.rd(), code.SCR1); + return; + } - if (regs.IsRegConstant(instr.rt())) { - const auto rt = regs.Read(instr.rt()); regs.Read(instr.rs(), code.SCR1); - code.and_(code.SCR1, rt); - regs.Write(instr.rd(), code.SCR1); - return; - } - - regs.Read(instr.rs(), code.SCR1); - regs.Read(instr.rt(), code.SCR2); - code.and_(code.SCR2, code.SCR1); - regs.Write(instr.rd(), code.SCR2); + regs.Read(instr.rt(), code.SCR2); + code.and_(code.SCR2, code.SCR1); + regs.Write(instr.rd(), code.SCR2); } void JIT::bfc0(const Instruction instr) { - const s16 imm = instr; - const s64 offset = u64((s64)imm) << 2; - const s64 address = blockPC + offset; - // code.mov(code.al, code.byte[code.rbp + REG_OFFSET(cop1.fcr31.compare)); - // code.test(code.al, code.al); - // branch(address, z); + const s16 imm = instr; + const s64 offset = u64((s64)imm) << 2; + const s64 address = blockPC + offset; + // code.mov(code.al, code.byte[code.rbp + REG_OFFSET(cop1.fcr31.compare)); + // code.test(code.al, code.al); + // branch(address, z); } void JIT::blfc0(const Instruction instr) { - const s16 imm = instr; - const s64 offset = u64((s64)imm) << 2; - const s64 address = blockPC + offset; - // code.mov(code.al, code.byte[code.rbp + REG_OFFSET(cop1.fcr31.compare)); - // code.test(code.al, code.al); - // branch_likely(address, z); + const s16 imm = instr; + const s64 offset = u64((s64)imm) << 2; + const s64 address = blockPC + offset; + // code.mov(code.al, code.byte[code.rbp + REG_OFFSET(cop1.fcr31.compare)); + // code.test(code.al, code.al); + // branch_likely(address, z); } void JIT::bfc1(const Instruction instr) { - const s16 imm = instr; - const s64 offset = u64((s64)imm) << 2; - const s64 address = blockPC + offset; - // code.mov(code.al, code.byte[code.rbp + REG_OFFSET(cop1.fcr31.compare)); - // code.test(code.al, code.al); - // branch(address, nz); + const s16 imm = instr; + const s64 offset = u64((s64)imm) << 2; + const s64 address = blockPC + offset; + // code.mov(code.al, code.byte[code.rbp + REG_OFFSET(cop1.fcr31.compare)); + // code.test(code.al, code.al); + // branch(address, nz); } void JIT::blfc1(const Instruction instr) { - const s16 imm = instr; - const s64 offset = u64((s64)imm) << 2; - const s64 address = blockPC + offset; - // code.mov(code.al, code.byte[code.rbp + REG_OFFSET(cop1.fcr31.compare)); - // code.test(code.al, code.al); - // branch_likely(address, nz); + const s16 imm = instr; + const s64 offset = u64((s64)imm) << 2; + const s64 address = blockPC + offset; + // code.mov(code.al, code.byte[code.rbp + REG_OFFSET(cop1.fcr31.compare)); + // code.test(code.al, code.al); + // branch_likely(address, nz); } void JIT::BranchNotTaken() {} void JIT::BranchTaken(const s64 offs) { - code.mov(code.SCR2, code.qword[code.rbp + PC_OFFSET]); - code.add(code.SCR2, offs); - SetPC64(code.SCR2); + code.mov(code.SCR2, code.qword[code.rbp + PC_OFFSET]); + code.add(code.SCR2, offs); + SetPC64(code.SCR2); } void JIT::BranchTaken(const Xbyak::Reg64 &offs) { - code.mov(code.SCR2, code.qword[code.rbp + PC_OFFSET]); - code.add(code.SCR2, offs); - SetPC64(code.SCR2); + code.mov(code.SCR2, code.qword[code.rbp + PC_OFFSET]); + code.add(code.SCR2, offs); + SetPC64(code.SCR2); } -void JIT::BranchAbsTaken(const s64 addr) { - SetPC64(addr); -} +void JIT::BranchAbsTaken(const s64 addr) { SetPC64(addr); } -void JIT::BranchAbsTaken(const Xbyak::Reg64 &addr) { - SetPC64(addr); -} +void JIT::BranchAbsTaken(const Xbyak::Reg64 &addr) { SetPC64(addr); } void JIT::branch_constant(const bool cond, const s64 offset) { - if(cond) { - regs.delaySlot = true; - BranchTaken(offset); - branch_taken = true; - } + if (cond) { + regs.delaySlot = true; + BranchTaken(offset); + branch_taken = true; + } } void JIT::branch_abs_constant(const bool cond, const s64 address) { - if(cond) { - regs.delaySlot = true; - BranchAbsTaken(address); - branch_taken = true; - } + if (cond) { + regs.delaySlot = true; + BranchAbsTaken(address); + branch_taken = true; + } } void JIT::branch_likely_constant(const bool cond, const s64 offset) { - if(cond) { - regs.delaySlot = true; - BranchTaken(offset); - branch_taken = true; - } else { - SetPC64(blockNextPC); - } + if (cond) { + regs.delaySlot = true; + BranchTaken(offset); + branch_taken = true; + } else { + SetPC64(blockNextPC); + } } -#define branch(offs, cond) do { \ - Xbyak::Label taken, not_taken; \ - code.j## cond(taken); \ - code.jmp(not_taken); \ - code.L(taken); \ - BranchTaken(offs); \ - code.mov(code.byte[code.rbp + BRANCH_TAKEN_OFFSET], 1); \ - code.L(not_taken); \ -} while(0) +#define branch(offs, cond) \ + do { \ + Xbyak::Label taken, not_taken; \ + code.j##cond(taken); \ + code.jmp(not_taken); \ + code.L(taken); \ + BranchTaken(offs); \ + code.mov(code.byte[code.rbp + BRANCH_TAKEN_OFFSET], 1); \ + code.L(not_taken); \ + } \ + while (0) -#define branch_abs(addr, cond) do { \ - Xbyak::Label taken, not_taken; \ - code.j## cond(taken); \ - code.jmp(not_taken); \ - code.L(taken); \ - BranchAbsTaken(addr); \ - code.mov(code.byte[code.rbp + BRANCH_TAKEN_OFFSET], 1); \ - code.L(not_taken); \ -} while(0) +#define branch_abs(addr, cond) \ + do { \ + Xbyak::Label taken, not_taken; \ + code.j##cond(taken); \ + code.jmp(not_taken); \ + code.L(taken); \ + BranchAbsTaken(addr); \ + code.mov(code.byte[code.rbp + BRANCH_TAKEN_OFFSET], 1); \ + code.L(not_taken); \ + } \ + while (0) -#define branch_likely(offs, cond) do { \ - Xbyak::Label taken, not_taken, end; \ - code.j## cond(taken); \ - code.jmp(not_taken); \ - code.L(taken); \ - BranchTaken(offs); \ - code.mov(code.byte[code.rbp + BRANCH_TAKEN_OFFSET], 1); \ - code.jmp(end); \ - code.L(not_taken); \ - SetPC64(blockNextPC); \ - code.L(end); \ -} while(0) +#define branch_likely(offs, cond) \ + do { \ + Xbyak::Label taken, not_taken, end; \ + code.j##cond(taken); \ + code.jmp(not_taken); \ + code.L(taken); \ + BranchTaken(offs); \ + code.mov(code.byte[code.rbp + BRANCH_TAKEN_OFFSET], 1); \ + code.jmp(end); \ + code.L(not_taken); \ + SetPC64(blockNextPC); \ + code.L(end); \ + } \ + while (0) void JIT::bltz(const Instruction instr) { - const s16 imm = instr; - const s64 offset = static_cast(imm) << 2; - if (regs.IsRegConstant(instr.rs())) { - branch_constant(regs.Read(instr.rs()) < 0, offset); - return; - } + const s16 imm = instr; + const s64 offset = static_cast(imm) << 2; + if (regs.IsRegConstant(instr.rs())) { + branch_constant(regs.Read(instr.rs()) < 0, offset); + return; + } - regs.Read(instr.rs(), code.rax); - code.cmp(code.rax, 0); - branch(offset, l); + regs.Read(instr.rs(), code.rax); + code.cmp(code.rax, 0); + branch(offset, l); } void JIT::bgez(const Instruction instr) { - const s16 imm = instr; - const s64 offset = u64((s64)imm) << 2; - if (regs.IsRegConstant(instr.rs())) { - branch_constant(regs.Read(instr.rs()) >= 0, offset); - return; - } + const s16 imm = instr; + const s64 offset = u64((s64)imm) << 2; + if (regs.IsRegConstant(instr.rs())) { + branch_constant(regs.Read(instr.rs()) >= 0, offset); + return; + } - regs.Read(instr.rs(), code.rax); - code.cmp(code.rax, 0); - branch(offset, ge); + regs.Read(instr.rs(), code.rax); + code.cmp(code.rax, 0); + branch(offset, ge); } void JIT::bltzl(const Instruction instr) { - panic("Implement branch likely < 0"); - const s16 imm = instr; - const s64 offset = u64((s64)imm) << 2; - if (regs.IsRegConstant(instr.rs())) { - branch_likely_constant(regs.Read(instr.rs()) < 0, offset); - return; - } + panic("Implement branch likely < 0"); + const s16 imm = instr; + const s64 offset = u64((s64)imm) << 2; + if (regs.IsRegConstant(instr.rs())) { + branch_likely_constant(regs.Read(instr.rs()) < 0, offset); + return; + } - regs.Read(instr.rs(), code.rax); - code.cmp(code.rax, 0); - branch_likely(offset, l); + regs.Read(instr.rs(), code.rax); + code.cmp(code.rax, 0); + branch_likely(offset, l); } void JIT::bgezl(const Instruction instr) { - panic("Implement branch likely >= 0"); - const s16 imm = instr; - const s64 offset = u64((s64)imm) << 2; - if (regs.IsRegConstant(instr.rs())) { - branch_likely_constant(regs.Read(instr.rs()) >= 0, offset); - return; - } + panic("Implement branch likely >= 0"); + const s16 imm = instr; + const s64 offset = u64((s64)imm) << 2; + if (regs.IsRegConstant(instr.rs())) { + branch_likely_constant(regs.Read(instr.rs()) >= 0, offset); + return; + } - regs.Read(instr.rs(), code.rax); - code.cmp(code.rax, 0); - branch_likely(offset, ge); + regs.Read(instr.rs(), code.rax); + code.cmp(code.rax, 0); + branch_likely(offset, ge); } void JIT::bltzal(const Instruction instr) { - const s16 imm = instr; - const s64 offset = u64((s64)imm) << 2; - regs.Write(31, blockNextPC); - if (regs.IsRegConstant(instr.rs())) { - branch_constant(regs.Read(instr.rs()) < 0, offset); - return; - } + const s16 imm = instr; + const s64 offset = u64((s64)imm) << 2; + regs.Write(31, blockNextPC); + if (regs.IsRegConstant(instr.rs())) { + branch_constant(regs.Read(instr.rs()) < 0, offset); + return; + } - regs.Read(instr.rs(), code.rax); - code.cmp(code.rax, 0); - branch(offset, l); + regs.Read(instr.rs(), code.rax); + code.cmp(code.rax, 0); + branch(offset, l); } void JIT::bgezal(const Instruction instr) { - const s16 imm = instr; - const s64 offset = u64((s64)imm) << 2; - regs.Write(31, blockNextPC); - if (regs.IsRegConstant(instr.rs())) { - branch_constant(regs.Read(instr.rs()) >= 0, offset); - return; - } + const s16 imm = instr; + const s64 offset = u64((s64)imm) << 2; + regs.Write(31, blockNextPC); + if (regs.IsRegConstant(instr.rs())) { + branch_constant(regs.Read(instr.rs()) >= 0, offset); + return; + } - regs.Read(instr.rs(), code.rax); - code.cmp(code.rax, 0); - branch(offset, ge); + regs.Read(instr.rs(), code.rax); + code.cmp(code.rax, 0); + branch(offset, ge); } void JIT::bltzall(const Instruction instr) { - panic("Implement branch likely and link < 0"); - const s16 imm = instr; - const s64 offset = u64((s64)imm) << 2; - regs.Write(31, blockNextPC); - if (regs.IsRegConstant(instr.rs())) { - branch_likely_constant(regs.Read(instr.rs()) < 0, offset); - return; - } + panic("Implement branch likely and link < 0"); + const s16 imm = instr; + const s64 offset = u64((s64)imm) << 2; + regs.Write(31, blockNextPC); + if (regs.IsRegConstant(instr.rs())) { + branch_likely_constant(regs.Read(instr.rs()) < 0, offset); + return; + } - regs.Read(instr.rs(), code.rax); - code.cmp(code.rax, 0); - branch_likely(offset, l); + regs.Read(instr.rs(), code.rax); + code.cmp(code.rax, 0); + branch_likely(offset, l); } void JIT::bgezall(const Instruction instr) { - panic("Implement branch likely and link >= 0"); - const s16 imm = instr; - const s64 offset = u64((s64)imm) << 2; - regs.Write(31, blockNextPC); - if (regs.IsRegConstant(instr.rs())) { - branch_likely_constant(regs.Read(instr.rs()) >= 0, offset); - return; - } + panic("Implement branch likely and link >= 0"); + const s16 imm = instr; + const s64 offset = u64((s64)imm) << 2; + regs.Write(31, blockNextPC); + if (regs.IsRegConstant(instr.rs())) { + branch_likely_constant(regs.Read(instr.rs()) >= 0, offset); + return; + } - regs.Read(instr.rs(), code.rax); - code.cmp(code.rax, 0); - branch_likely(offset, ge); + regs.Read(instr.rs(), code.rax); + code.cmp(code.rax, 0); + branch_likely(offset, ge); } void JIT::beq(const Instruction instr) { - const s16 imm = instr; - const s64 offset = u64((s64)imm) << 2; - if (regs.IsRegConstant(instr.rs()) && regs.IsRegConstant(instr.rt())) { - branch_constant(regs.Read(instr.rs()) == regs.Read(instr.rt()), offset); - return; - } + const s16 imm = instr; + const s64 offset = u64((s64)imm) << 2; + if (regs.IsRegConstant(instr.rs()) && regs.IsRegConstant(instr.rt())) { + branch_constant(regs.Read(instr.rs()) == regs.Read(instr.rt()), offset); + return; + } - if (regs.IsRegConstant(instr.rs())) { - regs.Read(instr.rt(), code.rax); - code.cmp(code.rax, regs.Read(instr.rs())); - branch(offset, e); - return; - } + if (regs.IsRegConstant(instr.rs())) { + regs.Read(instr.rt(), code.rax); + code.cmp(code.rax, regs.Read(instr.rs())); + branch(offset, e); + return; + } + + if (regs.IsRegConstant(instr.rt())) { + regs.Read(instr.rs(), code.rax); + code.cmp(code.rax, regs.Read(instr.rt())); + branch(offset, e); + return; + } - if (regs.IsRegConstant(instr.rt())) { regs.Read(instr.rs(), code.rax); - code.cmp(code.rax, regs.Read(instr.rt())); + regs.Read(instr.rt(), code.rdi); + code.cmp(code.rax, code.rdi); branch(offset, e); - return; - } - - regs.Read(instr.rs(), code.rax); - regs.Read(instr.rt(), code.rdi); - code.cmp(code.rax, code.rdi); - branch(offset, e); } void JIT::beql(const Instruction instr) { - panic("Implement branch likely =="); - const s16 imm = instr; - const s64 offset = u64((s64)imm) << 2; - if (regs.IsRegConstant(instr.rs()) && regs.IsRegConstant(instr.rt())) { - branch_likely_constant(regs.Read(instr.rs()) == regs.Read(instr.rt()), offset); - return; - } + panic("Implement branch likely =="); + const s16 imm = instr; + const s64 offset = u64((s64)imm) << 2; + if (regs.IsRegConstant(instr.rs()) && regs.IsRegConstant(instr.rt())) { + branch_likely_constant(regs.Read(instr.rs()) == regs.Read(instr.rt()), offset); + return; + } - if (regs.IsRegConstant(instr.rs())) { - regs.Read(instr.rt(), code.rax); - code.cmp(code.rax, regs.Read(instr.rs())); - branch_likely(offset, e); - return; - } + if (regs.IsRegConstant(instr.rs())) { + regs.Read(instr.rt(), code.rax); + code.cmp(code.rax, regs.Read(instr.rs())); + branch_likely(offset, e); + return; + } + + if (regs.IsRegConstant(instr.rt())) { + regs.Read(instr.rs(), code.rax); + code.cmp(code.rax, regs.Read(instr.rt())); + branch_likely(offset, e); + return; + } - if (regs.IsRegConstant(instr.rt())) { regs.Read(instr.rs(), code.rax); - code.cmp(code.rax, regs.Read(instr.rt())); + regs.Read(instr.rt(), code.rdi); + code.cmp(code.rax, code.rdi); branch_likely(offset, e); - return; - } - - regs.Read(instr.rs(), code.rax); - regs.Read(instr.rt(), code.rdi); - code.cmp(code.rax, code.rdi); - branch_likely(offset, e); } void JIT::bne(const Instruction instr) { - const s16 imm = instr; - const s64 offset = static_cast(imm) << 2; - if (regs.IsRegConstant(instr.rs()) && regs.IsRegConstant(instr.rt())) { - branch_constant(regs.Read(instr.rs()) != regs.Read(instr.rt()), offset); - return; - } + const s16 imm = instr; + const s64 offset = static_cast(imm) << 2; + if (regs.IsRegConstant(instr.rs()) && regs.IsRegConstant(instr.rt())) { + branch_constant(regs.Read(instr.rs()) != regs.Read(instr.rt()), offset); + return; + } - if (regs.IsRegConstant(instr.rs())) { - regs.Read(instr.rt(), code.rax); - code.cmp(code.rax, regs.Read(instr.rs())); - branch(offset, ne); - return; - } + if (regs.IsRegConstant(instr.rs())) { + regs.Read(instr.rt(), code.rax); + code.cmp(code.rax, regs.Read(instr.rs())); + branch(offset, ne); + return; + } + + if (regs.IsRegConstant(instr.rt())) { + regs.Read(instr.rs(), code.rax); + code.cmp(code.rax, regs.Read(instr.rt())); + branch(offset, ne); + return; + } - if (regs.IsRegConstant(instr.rt())) { regs.Read(instr.rs(), code.rax); - code.cmp(code.rax, regs.Read(instr.rt())); + regs.Read(instr.rt(), code.rdi); + code.cmp(code.rax, code.rdi); branch(offset, ne); - return; - } - - regs.Read(instr.rs(), code.rax); - regs.Read(instr.rt(), code.rdi); - code.cmp(code.rax, code.rdi); - branch(offset, ne); } void JIT::bnel(const Instruction instr) { - panic("Implement branch likely !="); - const s16 imm = instr; - const s64 offset = u64((s64)imm) << 2; - if (regs.IsRegConstant(instr.rs()) && regs.IsRegConstant(instr.rt())) { - branch_likely_constant(regs.Read(instr.rs()) != regs.Read(instr.rt()), offset); - return; - } + panic("Implement branch likely !="); + const s16 imm = instr; + const s64 offset = u64((s64)imm) << 2; + if (regs.IsRegConstant(instr.rs()) && regs.IsRegConstant(instr.rt())) { + branch_likely_constant(regs.Read(instr.rs()) != regs.Read(instr.rt()), offset); + return; + } - if (regs.IsRegConstant(instr.rs())) { - regs.Read(instr.rt(), code.rax); - code.cmp(code.rax, regs.Read(instr.rs())); - branch_likely(offset, ne); - return; - } + if (regs.IsRegConstant(instr.rs())) { + regs.Read(instr.rt(), code.rax); + code.cmp(code.rax, regs.Read(instr.rs())); + branch_likely(offset, ne); + return; + } + + if (regs.IsRegConstant(instr.rt())) { + regs.Read(instr.rs(), code.rax); + code.cmp(code.rax, regs.Read(instr.rt())); + branch_likely(offset, ne); + return; + } - if (regs.IsRegConstant(instr.rt())) { regs.Read(instr.rs(), code.rax); - code.cmp(code.rax, regs.Read(instr.rt())); + regs.Read(instr.rt(), code.rdi); + code.cmp(code.rax, code.rdi); branch_likely(offset, ne); - return; - } - - regs.Read(instr.rs(), code.rax); - regs.Read(instr.rt(), code.rdi); - code.cmp(code.rax, code.rdi); - branch_likely(offset, ne); } void JIT::blez(const Instruction instr) { - const s16 imm = instr; - const s64 offset = u64((s64)imm) << 2; - if (regs.IsRegConstant(instr.rs())) { - branch_constant(regs.Read(instr.rs()) <= 0, offset); - return; - } + const s16 imm = instr; + const s64 offset = u64((s64)imm) << 2; + if (regs.IsRegConstant(instr.rs())) { + branch_constant(regs.Read(instr.rs()) <= 0, offset); + return; + } - regs.Read(instr.rs(), code.rax); - code.cmp(code.rax, 0); - branch(offset, le); + regs.Read(instr.rs(), code.rax); + code.cmp(code.rax, 0); + branch(offset, le); } void JIT::blezl(const Instruction instr) { - panic("Implement branch likely <= 0"); - const s16 imm = instr; - const s64 offset = u64((s64)imm) << 2; - if (regs.IsRegConstant(instr.rs())) { - branch_likely_constant(regs.Read(instr.rs()) <= 0, offset); - return; - } + panic("Implement branch likely <= 0"); + const s16 imm = instr; + const s64 offset = u64((s64)imm) << 2; + if (regs.IsRegConstant(instr.rs())) { + branch_likely_constant(regs.Read(instr.rs()) <= 0, offset); + return; + } - regs.Read(instr.rs(), code.rax); - code.cmp(code.rax, 0); - branch_likely(offset, le); + regs.Read(instr.rs(), code.rax); + code.cmp(code.rax, 0); + branch_likely(offset, le); } void JIT::bgtz(const Instruction instr) { - const s16 imm = instr; - const s64 offset = u64((s64)imm) << 2; - if (regs.IsRegConstant(instr.rs())) { - branch_constant(regs.Read(instr.rs()) > 0, offset); - return; - } + const s16 imm = instr; + const s64 offset = u64((s64)imm) << 2; + if (regs.IsRegConstant(instr.rs())) { + branch_constant(regs.Read(instr.rs()) > 0, offset); + return; + } - regs.Read(instr.rs(), code.rax); - code.cmp(code.rax, 0); - branch(offset, g); + regs.Read(instr.rs(), code.rax); + code.cmp(code.rax, 0); + branch(offset, g); } void JIT::bgtzl(const Instruction instr) { - panic("Implement branch likely > 0"); - const s16 imm = instr; - const s64 offset = u64((s64)imm) << 2; - if (regs.IsRegConstant(instr.rs())) { - branch_likely_constant(regs.Read(instr.rs()) > 0, offset); - return; - } + panic("Implement branch likely > 0"); + const s16 imm = instr; + const s64 offset = u64((s64)imm) << 2; + if (regs.IsRegConstant(instr.rs())) { + branch_likely_constant(regs.Read(instr.rs()) > 0, offset); + return; + } - regs.Read(instr.rs(), code.rax); - code.cmp(code.rax, 0); - branch_likely(offset, g); + regs.Read(instr.rs(), code.rax); + code.cmp(code.rax, 0); + branch_likely(offset, g); } void JIT::dadd(const Instruction instr) { - if (regs.IsRegConstant(instr.rs(), instr.rt())) { - auto rs = regs.Read(instr.rs()); - auto rt = regs.Read(instr.rt()); - u64 result = rt + rs; - if (check_signed_overflow(rs, rt, result)) { - // regs.cop0.FireException(ExceptionCode::Overflow, 0, regs.oldPC); - panic("[JIT]: Unhandled Overflow exception in DADD!"); + if (regs.IsRegConstant(instr.rs(), instr.rt())) { + auto rs = regs.Read(instr.rs()); + auto rt = regs.Read(instr.rt()); + u64 result = rt + rs; + if (check_signed_overflow(rs, rt, result)) { + // regs.cop0.FireException(Cop0::ExceptionCode::Overflow, 0, regs.oldPC); + panic("[JIT]: Unhandled Overflow exception in DADD!"); + } + regs.Write(instr.rd(), result); + return; } - regs.Write(instr.rd(), result); - return; - } - if (regs.IsRegConstant(instr.rs())) { - auto rs = regs.Read(instr.rs()); - regs.Read(instr.rt(), code.SCR1); - code.add(code.SCR1, rs); - regs.Write(instr.rd(), code.SCR1); - return; - } + if (regs.IsRegConstant(instr.rs())) { + auto rs = regs.Read(instr.rs()); + regs.Read(instr.rt(), code.SCR1); + code.add(code.SCR1, rs); + regs.Write(instr.rd(), code.SCR1); + return; + } + + if (regs.IsRegConstant(instr.rt())) { + auto rt = regs.Read(instr.rt()); + regs.Read(instr.rs(), code.SCR1); + code.add(code.SCR1, rt); + regs.Write(instr.rd(), code.SCR1); + return; + } - if (regs.IsRegConstant(instr.rt())) { - auto rt = regs.Read(instr.rt()); regs.Read(instr.rs(), code.SCR1); - code.add(code.SCR1, rt); + regs.Read(instr.rt(), code.SCR2); + code.add(code.SCR1, code.SCR2); regs.Write(instr.rd(), code.SCR1); - return; - } - - regs.Read(instr.rs(), code.SCR1); - regs.Read(instr.rt(), code.SCR2); - code.add(code.SCR1, code.SCR2); - regs.Write(instr.rd(), code.SCR1); } void JIT::daddu(const Instruction instr) { - // TODO: IMPLEMENT DADDU BY ITSELF ACTUALLY - dadd(instr); + // TODO: IMPLEMENT DADDU BY ITSELF ACTUALLY + dadd(instr); } void JIT::daddi(const Instruction instr) { - u64 imm = s64(s16(instr)); - if (regs.IsRegConstant(instr.rs())) { - auto rs = regs.Read(instr.rs()); - u64 result = imm + rs; - if (check_signed_overflow(rs, imm, result)) { - // regs.cop0.FireException(ExceptionCode::Overflow, 0, regs.oldPC); - panic("[JIT]: Unhandled Overflow exception in DADDI!"); + u64 imm = s64(s16(instr)); + if (regs.IsRegConstant(instr.rs())) { + auto rs = regs.Read(instr.rs()); + u64 result = imm + rs; + if (check_signed_overflow(rs, imm, result)) { + // regs.cop0.FireException(Cop0::ExceptionCode::Overflow, 0, regs.oldPC); + panic("[JIT]: Unhandled Overflow exception in DADDI!"); + } + regs.Write(instr.rt(), result); + return; } - regs.Write(instr.rt(), result); - return; - } - regs.Read(instr.rs(), code.SCR1); - code.add(code.SCR1, imm); - regs.Write(instr.rt(), code.SCR1); + regs.Read(instr.rs(), code.SCR1); + code.add(code.SCR1, imm); + regs.Write(instr.rt(), code.SCR1); } void JIT::daddiu(const Instruction instr) { - // TODO: IMPLEMENT DADDIU BY ITSELF ACTUALLY - daddi(instr); + // TODO: IMPLEMENT DADDIU BY ITSELF ACTUALLY + daddi(instr); } void JIT::ddiv(const Instruction instr) { - if (regs.IsRegConstant(instr.rs(), instr.rt())) { - auto dividend = regs.Read(instr.rs()); - auto divisor = regs.Read(instr.rt()); - if (dividend == 0x8000000000000000 && divisor == 0xFFFFFFFFFFFFFFFF) { - regs.lo = dividend; - regs.hi = 0; - } else if (divisor == 0) { - regs.hi = dividend; - if (dividend >= 0) { - regs.lo = -1; - } else { - regs.lo = 1; - } - } else { - s64 quotient = dividend / divisor; - s64 remainder = dividend % divisor; - regs.lo = quotient; - regs.hi = remainder; - } + if (regs.IsRegConstant(instr.rs(), instr.rt())) { + auto dividend = regs.Read(instr.rs()); + auto divisor = regs.Read(instr.rt()); + if (dividend == 0x8000000000000000 && divisor == 0xFFFFFFFFFFFFFFFF) { + regs.lo = dividend; + regs.hi = 0; + } else if (divisor == 0) { + regs.hi = dividend; + if (dividend >= 0) { + regs.lo = -1; + } else { + regs.lo = 1; + } + } else { + s64 quotient = dividend / divisor; + s64 remainder = dividend % divisor; + regs.lo = quotient; + regs.hi = remainder; + } - regs.SetLOConstant(); - regs.SetHIConstant(); - } else { - panic("[JIT]: Implement non constant DDIV!"); - } + regs.SetLOConstant(); + regs.SetHIConstant(); + } else { + panic("[JIT]: Implement non constant DDIV!"); + } } void JIT::ddivu(const Instruction instr) { - if (regs.IsRegConstant(instr.rs(), instr.rt())) { - auto dividend = regs.Read(instr.rs()); - auto divisor = regs.Read(instr.rt()); - if (divisor == 0) { - regs.lo = -1; - regs.hi = (s64)dividend; - } else { - u64 quotient = dividend / divisor; - u64 remainder = dividend % divisor; - regs.lo = (s64)quotient; - regs.hi = (s64)remainder; - } + if (regs.IsRegConstant(instr.rs(), instr.rt())) { + auto dividend = regs.Read(instr.rs()); + auto divisor = regs.Read(instr.rt()); + if (divisor == 0) { + regs.lo = -1; + regs.hi = (s64)dividend; + } else { + u64 quotient = dividend / divisor; + u64 remainder = dividend % divisor; + regs.lo = (s64)quotient; + regs.hi = (s64)remainder; + } - regs.SetLOConstant(); - regs.SetHIConstant(); - } else { - panic("[JIT]: Implement non constant DDIVU!"); - } + regs.SetLOConstant(); + regs.SetHIConstant(); + } else { + panic("[JIT]: Implement non constant DDIVU!"); + } } void JIT::div(const Instruction instr) { - if (regs.IsRegConstant(instr.rs(), instr.rt())) { - s64 dividend = regs.Read(instr.rs()); - s64 divisor = regs.Read(instr.rt()); + if (regs.IsRegConstant(instr.rs(), instr.rt())) { + s64 dividend = regs.Read(instr.rs()); + s64 divisor = regs.Read(instr.rt()); - if (divisor == 0) { - regs.hi = dividend; - if (dividend >= 0) { - regs.lo = s64(-1); - } else { - regs.lo = s64(1); - } + if (divisor == 0) { + regs.hi = dividend; + if (dividend >= 0) { + regs.lo = s64(-1); + } else { + regs.lo = s64(1); + } + } else { + s32 quotient = dividend / divisor; + s32 remainder = dividend % divisor; + regs.lo = quotient; + regs.hi = remainder; + } + + regs.SetLOConstant(); + regs.SetHIConstant(); } else { - s32 quotient = dividend / divisor; - s32 remainder = dividend % divisor; - regs.lo = quotient; - regs.hi = remainder; + panic("[JIT]: Implement non constant DIV!"); } - - regs.SetLOConstant(); - regs.SetHIConstant(); - } else { - panic("[JIT]: Implement non constant DIV!"); - } } void JIT::divu(const Instruction instr) { - if (regs.IsRegConstant(instr.rs(), instr.rt())) { - auto dividend = regs.Read(instr.rs()); - auto divisor = regs.Read(instr.rt()); - if (divisor == 0) { - regs.lo = -1; - regs.hi = (s32)dividend; - } else { - s32 quotient = (s32)(dividend / divisor); - s32 remainder = (s32)(dividend % divisor); - regs.lo = quotient; - regs.hi = remainder; - } + if (regs.IsRegConstant(instr.rs(), instr.rt())) { + auto dividend = regs.Read(instr.rs()); + auto divisor = regs.Read(instr.rt()); + if (divisor == 0) { + regs.lo = -1; + regs.hi = (s32)dividend; + } else { + s32 quotient = (s32)(dividend / divisor); + s32 remainder = (s32)(dividend % divisor); + regs.lo = quotient; + regs.hi = remainder; + } - regs.SetLOConstant(); - regs.SetHIConstant(); - } else { - panic("[JIT]: Implement non constant DIVU!"); - } + regs.SetLOConstant(); + regs.SetHIConstant(); + } else { + panic("[JIT]: Implement non constant DIVU!"); + } } void JIT::dmult(const Instruction instr) { - if (regs.IsRegConstant(instr.rt(), instr.rs())) { - auto rt = regs.Read(instr.rt()); - auto rs = regs.Read(instr.rs()); - s128 result = (s128)rt * (s128)rs; - regs.lo = result & 0xFFFFFFFFFFFFFFFF; - regs.hi = result >> 64; - regs.SetHIConstant(); - regs.SetLOConstant(); - } else { - panic("[JIT]: Implement non constant DMULT!"); - } + if (regs.IsRegConstant(instr.rt(), instr.rs())) { + auto rt = regs.Read(instr.rt()); + auto rs = regs.Read(instr.rs()); + s128 result = (s128)rt * (s128)rs; + regs.lo = result & 0xFFFFFFFFFFFFFFFF; + regs.hi = result >> 64; + regs.SetHIConstant(); + regs.SetLOConstant(); + } else { + panic("[JIT]: Implement non constant DMULT!"); + } } void JIT::dmultu(const Instruction instr) { - if (regs.IsRegConstant(instr.rt(), instr.rs())) { - auto rt = regs.Read(instr.rt()); - auto rs = regs.Read(instr.rs()); - u128 result = (u128)rt * (u128)rs; - regs.lo = s64(result & 0xFFFFFFFFFFFFFFFF); - regs.hi = s64(result >> 64); - regs.SetHIConstant(); - regs.SetLOConstant(); - } else { - panic("[JIT]: Implement non constant DMULT!"); - } + if (regs.IsRegConstant(instr.rt(), instr.rs())) { + auto rt = regs.Read(instr.rt()); + auto rs = regs.Read(instr.rs()); + u128 result = (u128)rt * (u128)rs; + regs.lo = s64(result & 0xFFFFFFFFFFFFFFFF); + regs.hi = s64(result >> 64); + regs.SetHIConstant(); + regs.SetLOConstant(); + } else { + panic("[JIT]: Implement non constant DMULT!"); + } } void JIT::dsll(const Instruction instr) { - if (regs.IsRegConstant(instr.rt())) { - u8 sa = ((instr >> 6) & 0x1f); - auto result = regs.Read(instr.rt()) << sa; - regs.Write(instr.rd(), result); - } else { - panic("[JIT]: Implement non constant DSLL!"); - } + if (regs.IsRegConstant(instr.rt())) { + u8 sa = ((instr >> 6) & 0x1f); + auto result = regs.Read(instr.rt()) << sa; + regs.Write(instr.rd(), result); + } else { + panic("[JIT]: Implement non constant DSLL!"); + } } void JIT::dsllv(const Instruction instr) { - if (regs.IsRegConstant(instr.rt(), instr.rs())) { - auto sa = regs.Read(instr.rs()) & 63; - auto result = regs.Read(instr.rt()) << sa; - regs.Write(instr.rd(), result); - } else { - panic("[JIT]: Implement non constant DSLLV!"); - } + if (regs.IsRegConstant(instr.rt(), instr.rs())) { + auto sa = regs.Read(instr.rs()) & 63; + auto result = regs.Read(instr.rt()) << sa; + regs.Write(instr.rd(), result); + } else { + panic("[JIT]: Implement non constant DSLLV!"); + } } void JIT::dsll32(const Instruction instr) { - if (regs.IsRegConstant(instr.rt())) { - u8 sa = ((instr >> 6) & 0x1f); - auto result = regs.Read(instr.rt()) << (sa + 32); - regs.Write(instr.rd(), result); - } else { - panic("[JIT]: Implement non constant DSLL32!"); - } + if (regs.IsRegConstant(instr.rt())) { + u8 sa = ((instr >> 6) & 0x1f); + auto result = regs.Read(instr.rt()) << (sa + 32); + regs.Write(instr.rd(), result); + } else { + panic("[JIT]: Implement non constant DSLL32!"); + } } void JIT::dsra(const Instruction instr) { - if (regs.IsRegConstant(instr.rt())) { - auto rt = regs.Read(instr.rt()); - u8 sa = ((instr >> 6) & 0x1f); - s64 result = rt >> sa; - regs.Write(instr.rd(), result); - } else { - panic("[JIT]: Implement non constant DSRA!"); - } + if (regs.IsRegConstant(instr.rt())) { + auto rt = regs.Read(instr.rt()); + u8 sa = ((instr >> 6) & 0x1f); + s64 result = rt >> sa; + regs.Write(instr.rd(), result); + } else { + panic("[JIT]: Implement non constant DSRA!"); + } } void JIT::dsrav(const Instruction instr) { - if (regs.IsRegConstant(instr.rt(), instr.rs())) { - auto rt = regs.Read(instr.rt()); - auto rs = regs.Read(instr.rs()); - s64 sa = rs & 63; - s64 result = rt >> sa; - regs.Write(instr.rd(), result); - } else { - panic("[JIT]: Implement non constant DSRAV!"); - } + if (regs.IsRegConstant(instr.rt(), instr.rs())) { + auto rt = regs.Read(instr.rt()); + auto rs = regs.Read(instr.rs()); + s64 sa = rs & 63; + s64 result = rt >> sa; + regs.Write(instr.rd(), result); + } else { + panic("[JIT]: Implement non constant DSRAV!"); + } } void JIT::dsra32(const Instruction instr) { - if (regs.IsRegConstant(instr.rt())) { - auto rt = regs.Read(instr.rt()); - u8 sa = ((instr >> 6) & 0x1f); - s64 result = rt >> (sa + 32); - regs.Write(instr.rd(), result); - } else { - panic("[JIT]: Implement non constant DSRA32!"); - } + if (regs.IsRegConstant(instr.rt())) { + auto rt = regs.Read(instr.rt()); + u8 sa = ((instr >> 6) & 0x1f); + s64 result = rt >> (sa + 32); + regs.Write(instr.rd(), result); + } else { + panic("[JIT]: Implement non constant DSRA32!"); + } } void JIT::dsrl(const Instruction instr) { - if (regs.IsRegConstant(instr.rt())) { - auto rt = regs.Read(instr.rt()); - u8 sa = ((instr >> 6) & 0x1f); - u64 result = rt >> sa; - regs.Write(instr.rd(), s64(result)); - } else { - panic("[JIT]: Implement non constant DSRL!"); - } + if (regs.IsRegConstant(instr.rt())) { + auto rt = regs.Read(instr.rt()); + u8 sa = ((instr >> 6) & 0x1f); + u64 result = rt >> sa; + regs.Write(instr.rd(), s64(result)); + } else { + panic("[JIT]: Implement non constant DSRL!"); + } } void JIT::dsrlv(const Instruction instr) { - if (regs.IsRegConstant(instr.rt(), instr.rs())) { - u8 amount = regs.Read(instr.rs()) & 63; - auto rt = regs.Read(instr.rt()); - u64 result = rt >> amount; - regs.Write(instr.rd(), s64(result)); - } else { - panic("[JIT]: Implement non constant DSRLV!"); - } + if (regs.IsRegConstant(instr.rt(), instr.rs())) { + u8 amount = regs.Read(instr.rs()) & 63; + auto rt = regs.Read(instr.rt()); + u64 result = rt >> amount; + regs.Write(instr.rd(), s64(result)); + } else { + panic("[JIT]: Implement non constant DSRLV!"); + } } void JIT::dsrl32(const Instruction instr) { - if (regs.IsRegConstant(instr.rt())) { - auto rt = regs.Read(instr.rt()); - u8 sa = ((instr >> 6) & 0x1f); - u64 result = rt >> (sa + 32); - regs.Write(instr.rd(), s64(result)); - } else { - panic("[JIT]: Implement non constant DSRL32!"); - } + if (regs.IsRegConstant(instr.rt())) { + auto rt = regs.Read(instr.rt()); + u8 sa = ((instr >> 6) & 0x1f); + u64 result = rt >> (sa + 32); + regs.Write(instr.rd(), s64(result)); + } else { + panic("[JIT]: Implement non constant DSRL32!"); + } } void JIT::dsub(const Instruction instr) { - if (regs.IsRegConstant(instr.rt(), instr.rs())) { - auto rt = regs.Read(instr.rt()); - auto rs = regs.Read(instr.rs()); - s64 result = rs - rt; - if (check_signed_underflow(rs, rt, result)) { - // regs.cop0.FireException(ExceptionCode::Overflow, 0, regs.oldPC); - panic("[JIT]: Unhandled Overflow exception in DSUB!"); + if (regs.IsRegConstant(instr.rt(), instr.rs())) { + auto rt = regs.Read(instr.rt()); + auto rs = regs.Read(instr.rs()); + s64 result = rs - rt; + if (check_signed_underflow(rs, rt, result)) { + // regs.cop0.FireException(Cop0::ExceptionCode::Overflow, 0, regs.oldPC); + panic("[JIT]: Unhandled Overflow exception in DSUB!"); + } else { + regs.Write(instr.rd(), result); + } } else { - regs.Write(instr.rd(), result); + panic("[JIT]: Implement non constant DSUB!"); } - } else { - panic("[JIT]: Implement non constant DSUB!"); - } } void JIT::dsubu(const Instruction instr) { - if (regs.IsRegConstant(instr.rt(), instr.rs())) { - auto rt = regs.Read(instr.rt()); - auto rs = regs.Read(instr.rs()); - s64 result = rs - rt; - regs.Write(instr.rd(), result); - } else { - panic("[JIT]: Implement non constant DSUBU!"); - } + if (regs.IsRegConstant(instr.rt(), instr.rs())) { + auto rt = regs.Read(instr.rt()); + auto rs = regs.Read(instr.rs()); + s64 result = rs - rt; + regs.Write(instr.rd(), result); + } else { + panic("[JIT]: Implement non constant DSUBU!"); + } } void JIT::j(const Instruction instr) { - const s32 target = (instr & 0x3ffffff) << 2; - const s64 address = blockOldPC & ~0xfffffff | target; - branch_abs_constant(true, address); + const s32 target = (instr & 0x3ffffff) << 2; + const s64 address = blockOldPC & ~0xfffffff | target; + branch_abs_constant(true, address); } void JIT::jr(const Instruction instr) { - if (regs.IsRegConstant(instr.rs())) { - const auto address = regs.Read(instr.rs()); - branch_abs_constant(true, address); - return; - } + if (regs.IsRegConstant(instr.rs())) { + const auto address = regs.Read(instr.rs()); + branch_abs_constant(true, address); + return; + } - regs.Read(instr.rs(), code.rax); - branch_abs(code.rax, mp); + regs.Read(instr.rs(), code.rax); + branch_abs(code.rax, mp); } - + void JIT::jal(const Instruction instr) { - regs.Write(31, blockNextPC); - j(instr); + regs.Write(31, blockNextPC); + j(instr); } void JIT::jalr(const Instruction instr) { - regs.Write(instr.rd(), blockNextPC); - jr(instr); + regs.Write(instr.rd(), blockNextPC); + jr(instr); } void JIT::lbu(const Instruction instr) { - if (regs.IsRegConstant(instr.rs())) { - const u64 address = regs.Read(instr.rs()) + (s16)instr; - u32 paddr; - if (!regs.cop0.MapVAddr(Cop0::LOAD, address, paddr)) { - // regs.cop0.HandleTLBException(address); - // regs.cop0.FireException(Cop0::GetTLBExceptionCode(regs.cop0.tlbError, Cop0::LOAD), 0, regs.oldPC); - panic("[JIT]: Unhandled TLBL exception in LBU!"); + if (regs.IsRegConstant(instr.rs())) { + const u64 address = regs.Read(instr.rs()) + (s16)instr; + u32 paddr; + if (!regs.cop0.MapVAddr(Cop0::LOAD, address, paddr)) { + // regs.cop0.HandleTLBException(address); + // regs.cop0.FireException(Cop0::GetTLBExceptionCode(regs.cop0.tlbError, Cop0::LOAD), 0, regs.oldPC); + panic("[JIT]: Unhandled TLBL exception in LBU!"); + } else { + code.mov(code.ARG2, paddr); + emitMemberFunctionCall(&Mem::Read, &mem); + regs.Write(instr.rt(), code.rax); + } } else { - code.mov(code.ARG2, paddr); - emitMemberFunctionCall(&Mem::Read, &mem); - regs.Write(instr.rt(), code.rax); + panic("[JIT]: Implement non constant LBU!"); } - } else { - panic("[JIT]: Implement non constant LBU!"); - } } void JIT::lb(const Instruction instr) { - if (regs.IsRegConstant(instr.rs())) { - const u64 address = regs.Read(instr.rs()) + (s16)instr; - if (u32 paddr = 0; !regs.cop0.MapVAddr(Cop0::LOAD, address, paddr)) { - // regs.cop0.HandleTLBException(address); - // regs.cop0.FireException(Cop0::GetTLBExceptionCode(regs.cop0.tlbError, Cop0::LOAD), 0, regs.oldPC); - panic("[JIT]: Unhandled TLBL exception in LB (pc: 0x{:016X})!", static_cast(blockPC)); + if (regs.IsRegConstant(instr.rs())) { + const u64 address = regs.Read(instr.rs()) + (s16)instr; + if (u32 paddr = 0; !regs.cop0.MapVAddr(Cop0::LOAD, address, paddr)) { + // regs.cop0.HandleTLBException(address); + // regs.cop0.FireException(Cop0::GetTLBExceptionCode(regs.cop0.tlbError, Cop0::LOAD), 0, regs.oldPC); + panic("[JIT]: Unhandled TLBL exception in LB (pc: 0x{:016X})!", static_cast(blockPC)); + } else { + code.mov(code.ARG2, paddr); + emitMemberFunctionCall(&Mem::Read, &mem); + regs.Write(instr.rt(), code.rax); + } } else { - code.mov(code.ARG2, paddr); - emitMemberFunctionCall(&Mem::Read, &mem); - regs.Write(instr.rt(), code.rax); + panic("[JIT]: Implement non constant LB!"); } - } else { - panic("[JIT]: Implement non constant LB!"); - } } void JIT::ld(const Instruction instr) { - if (regs.IsRegConstant(instr.rs())) { - const s64 address = regs.Read(instr.rs()) + (s16)instr; - if (check_address_error(0b111, address)) { - // regs.cop0.HandleTLBException(address); - // regs.cop0.FireException(ExceptionCode::AddressErrorLoad, 0, regs.oldPC); - // return; - panic("[JIT]: Unhandled ADEL exception in LD!"); - } + if (regs.IsRegConstant(instr.rs())) { + const s64 address = regs.Read(instr.rs()) + (s16)instr; + if (check_address_error(0b111, address)) { + // regs.cop0.HandleTLBException(address); + // regs.cop0.FireException(Cop0::ExceptionCode::AddressErrorLoad, 0, regs.oldPC); + // return; + panic("[JIT]: Unhandled ADEL exception in LD!"); + } - u32 paddr = 0; - if (!regs.cop0.MapVAddr(Cop0::LOAD, address, paddr)) { - // regs.cop0.HandleTLBException(address); - // regs.cop0.FireException(Cop0::GetTLBExceptionCode(regs.cop0.tlbError, Cop0::LOAD), 0, regs.oldPC); - panic("[JIT]: Unhandled TLBL exception in LD!"); + u32 paddr = 0; + if (!regs.cop0.MapVAddr(Cop0::LOAD, address, paddr)) { + // regs.cop0.HandleTLBException(address); + // regs.cop0.FireException(Cop0::GetTLBExceptionCode(regs.cop0.tlbError, Cop0::LOAD), 0, regs.oldPC); + panic("[JIT]: Unhandled TLBL exception in LD!"); + } else { + code.mov(code.ARG2, paddr); + emitMemberFunctionCall(&Mem::Read, &mem); + regs.Write(instr.rt(), code.rax); + } } else { - code.mov(code.ARG2, paddr); - emitMemberFunctionCall(&Mem::Read, &mem); - regs.Write(instr.rt(), code.rax); + panic("[JIT]: Implement non constant LD!"); } - } else { - panic("[JIT]: Implement non constant LD!"); - } } void JIT::ldc1(const Instruction instr) { - if (regs.IsRegConstant(instr.base())) { - const u64 addr = static_cast(static_cast(instr)) + regs.Read(instr.base()); + if (regs.IsRegConstant(instr.base())) { + const u64 addr = static_cast(static_cast(instr)) + regs.Read(instr.base()); - if (u32 physical; !regs.cop0.MapVAddr(Cop0::LOAD, addr, physical)) { - // regs.cop0.HandleTLBException(addr); - // regs.cop0.FireException(Cop0::GetTLBExceptionCode(regs.cop0.tlbError, Cop0::LOAD), 0, regs.oldPC); - panic("[JIT]: Unhandled TLBL exception in LD1!"); + if (u32 physical; !regs.cop0.MapVAddr(Cop0::LOAD, addr, physical)) { + // regs.cop0.HandleTLBException(addr); + // regs.cop0.FireException(Cop0::GetTLBExceptionCode(regs.cop0.tlbError, Cop0::LOAD), 0, regs.oldPC); + panic("[JIT]: Unhandled TLBL exception in LD1!"); + } else { + const u64 data = mem.Read(physical); + regs.cop1.FGR_T(regs.cop0.status, instr.ft()) = data; + regs.cop1.fgrIsConstant[instr.ft()] = true; + } } else { - const u64 data = mem.Read(physical); - regs.cop1.FGR_T(regs.cop0.status, instr.ft()) = data; - regs.cop1.fgrIsConstant[instr.ft()] = true; + panic("[JIT]: Implement non constant LD1!"); } - } else { - panic("[JIT]: Implement non constant LD1!"); - } } void JIT::ldl(const Instruction instr) { - if (regs.IsRegConstant(instr.rs(), instr.rt())) { - const u64 address = regs.Read(instr.rs()) + (s16)instr; - u32 paddr = 0; - if (!regs.cop0.MapVAddr(Cop0::LOAD, address, paddr)) { - // regs.cop0.HandleTLBException(address); - // regs.cop0.FireException(Cop0::GetTLBExceptionCode(regs.cop0.tlbError, Cop0::LOAD), 0, regs.oldPC); - panic("[JIT]: Unhandled TLBL exception in LDL!"); + if (regs.IsRegConstant(instr.rs(), instr.rt())) { + const u64 address = regs.Read(instr.rs()) + (s16)instr; + u32 paddr = 0; + if (!regs.cop0.MapVAddr(Cop0::LOAD, address, paddr)) { + // regs.cop0.HandleTLBException(address); + // regs.cop0.FireException(Cop0::GetTLBExceptionCode(regs.cop0.tlbError, Cop0::LOAD), 0, regs.oldPC); + panic("[JIT]: Unhandled TLBL exception in LDL!"); + } else { + panic("[JIT]: Implement constant LDL!"); + const s32 shift = 8 * ((address ^ 0) & 7); + const u64 mask = 0xFFFFFFFFFFFFFFFF << shift; + const u64 data = mem.Read(paddr & ~7); + const s64 result = (s64)((regs.Read(instr.rt()) & ~mask) | (data << shift)); + regs.Write(instr.rt(), result); + } } else { - panic("[JIT]: Implement constant LDL!"); - const s32 shift = 8 * ((address ^ 0) & 7); - const u64 mask = 0xFFFFFFFFFFFFFFFF << shift; - const u64 data = mem.Read(paddr & ~7); - const s64 result = (s64)((regs.Read(instr.rt()) & ~mask) | (data << shift)); - regs.Write(instr.rt(), result); + panic("[JIT]: Implement non constant LDL!"); } - } else { - panic("[JIT]: Implement non constant LDL!"); - } } void JIT::ldr(const Instruction instr) { - if (regs.IsRegConstant(instr.rs(), instr.rt())) { - const u64 address = regs.Read(instr.rs()) + (s16)instr; - u32 paddr; - if (!regs.cop0.MapVAddr(Cop0::LOAD, address, paddr)) { - // regs.cop0.HandleTLBException(address); - // regs.cop0.FireException(Cop0::GetTLBExceptionCode(regs.cop0.tlbError, Cop0::LOAD), 0, regs.oldPC); - panic("[JIT]: Unhandled TLBL exception in LDR!"); + if (regs.IsRegConstant(instr.rs(), instr.rt())) { + const u64 address = regs.Read(instr.rs()) + (s16)instr; + u32 paddr; + if (!regs.cop0.MapVAddr(Cop0::LOAD, address, paddr)) { + // regs.cop0.HandleTLBException(address); + // regs.cop0.FireException(Cop0::GetTLBExceptionCode(regs.cop0.tlbError, Cop0::LOAD), 0, regs.oldPC); + panic("[JIT]: Unhandled TLBL exception in LDR!"); + } else { + const s32 shift = 8 * ((address ^ 7) & 7); + const u64 mask = 0xFFFFFFFFFFFFFFFF >> shift; + const u64 data = mem.Read(paddr & ~7); + const s64 result = (s64)((regs.Read(instr.rt()) & ~mask) | (data >> shift)); + regs.Write(instr.rt(), result); + } } else { - const s32 shift = 8 * ((address ^ 7) & 7); - const u64 mask = 0xFFFFFFFFFFFFFFFF >> shift; - const u64 data = mem.Read(paddr & ~7); - const s64 result = (s64)((regs.Read(instr.rt()) & ~mask) | (data >> shift)); - regs.Write(instr.rt(), result); + panic("[JIT]: Implement non constant LDR!"); } - } else { - panic("[JIT]: Implement non constant LDR!"); - } } void JIT::lh(const Instruction instr) { - if (regs.IsRegConstant(instr.rs())) { - const u64 address = regs.Read(instr.rs()) + (s16)instr; - if (check_address_error(0b1, address)) { - // regs.cop0.HandleTLBException(address); - // regs.cop0.FireException(ExceptionCode::AddressErrorLoad, 0, regs.oldPC); - // return; - panic("[JIT]: Unhandled ADEL exception in LH!"); - return; + if (regs.IsRegConstant(instr.rs())) { + const u64 address = regs.Read(instr.rs()) + (s16)instr; + if (check_address_error(0b1, address)) { + // regs.cop0.HandleTLBException(address); + // regs.cop0.FireException(Cop0::ExceptionCode::AddressErrorLoad, 0, regs.oldPC); + // return; + panic("[JIT]: Unhandled ADEL exception in LH!"); + return; + } + + u32 paddr = 0; + if (!regs.cop0.MapVAddr(Cop0::LOAD, address, paddr)) { + // regs.cop0.HandleTLBException(address); + // regs.cop0.FireException(Cop0::GetTLBExceptionCode(regs.cop0.tlbError, Cop0::LOAD), 0, regs.oldPC); + panic("[JIT]: Unhandled TLBL exception in LH!"); + return; + } + + code.mov(code.ARG2, paddr); + emitMemberFunctionCall(&Mem::Read, &mem); + regs.Write(instr.rt(), code.rax); + return; } - u32 paddr = 0; - if (!regs.cop0.MapVAddr(Cop0::LOAD, address, paddr)) { - // regs.cop0.HandleTLBException(address); - // regs.cop0.FireException(Cop0::GetTLBExceptionCode(regs.cop0.tlbError, Cop0::LOAD), 0, regs.oldPC); - panic("[JIT]: Unhandled TLBL exception in LH!"); - return; - } - - code.mov(code.ARG2, paddr); - emitMemberFunctionCall(&Mem::Read, &mem); - regs.Write(instr.rt(), code.rax); - return; - } - - panic("[JIT]: Implement non constant LH!"); + panic("[JIT]: Implement non constant LH!"); } void JIT::lhu(const Instruction instr) { - u32 paddr; - if (regs.IsRegConstant(instr.rs())) { - const s64 address = regs.Read(instr.rs()) + (s16)instr; - if (check_address_error(0b1, address)) { - regs.cop0.HandleTLBException(address); - regs.cop0.FireException(ExceptionCode::AddressErrorLoad, 0, regs.oldPC); - return; + u32 paddr; + if (regs.IsRegConstant(instr.rs())) { + const s64 address = regs.Read(instr.rs()) + (s16)instr; + if (check_address_error(0b1, address)) { + regs.cop0.HandleTLBException(address); + regs.cop0.FireException(Cop0::ExceptionCode::AddressErrorLoad, 0, regs.oldPC); + return; + } + + if (!regs.cop0.MapVAddr(Cop0::LOAD, address, paddr)) { + regs.cop0.HandleTLBException(address); + regs.cop0.FireException(Cop0::GetTLBExceptionCode(regs.cop0.tlbError, Cop0::LOAD), 0, regs.oldPC); + return; + } + + code.mov(code.ARG2, paddr); + emitMemberFunctionCall(&Mem::Read, &mem); + regs.Write(instr.rt(), code.rax); } - if (!regs.cop0.MapVAddr(Cop0::LOAD, address, paddr)) { - regs.cop0.HandleTLBException(address); - regs.cop0.FireException(Cop0::GetTLBExceptionCode(regs.cop0.tlbError, Cop0::LOAD), 0, regs.oldPC); - return; - } + code.mov(code.ARG2, Cop0::LOAD); + regs.Read(instr.rs(), code.ARG3); + code.add(code.ARG3, s16(instr)); + code.mov(code.ARG4, reinterpret_cast(&paddr)); + emitMemberFunctionCall(&Cop0::MapVAddr, ®s.cop0); - code.mov(code.ARG2, paddr); + code.mov(code.ARG2, code.qword[code.ARG4]); emitMemberFunctionCall(&Mem::Read, &mem); regs.Write(instr.rt(), code.rax); - } - - code.mov(code.ARG2, Cop0::LOAD); - regs.Read(instr.rs(), code.ARG3); - code.add(code.ARG3, s16(instr)); - code.mov(code.ARG4, reinterpret_cast(&paddr)); - emitMemberFunctionCall(&Cop0::MapVAddr, ®s.cop0); - - code.mov(code.ARG2, code.qword[code.ARG4]); - emitMemberFunctionCall(&Mem::Read, &mem); - regs.Write(instr.rt(), code.rax); } void JIT::ll(const Instruction) { panic("[JIT]: Implement constant LL!"); } @@ -1074,42 +1076,42 @@ void JIT::ll(const Instruction) { panic("[JIT]: Implement constant LL!"); } void JIT::lld(const Instruction) { panic("[JIT]: Implement constant LLD!"); } void JIT::lw(const Instruction instr) { - const s16 offset = instr; - u32 paddr = 0; - if (regs.IsRegConstant(instr.rs())) { - const u64 address = regs.Read(instr.rs()) + offset; - if (check_address_error(0b11, address)) { - // regs.cop0.HandleTLBException(address); - // regs.cop0.FireException(ExceptionCode::AddressErrorLoad, 0, regs.oldPC); - // return; - panic("[JIT]: Unhandled ADEL exception in LW!"); - return; + const s16 offset = instr; + u32 paddr = 0; + if (regs.IsRegConstant(instr.rs())) { + const u64 address = regs.Read(instr.rs()) + offset; + if (check_address_error(0b11, address)) { + // regs.cop0.HandleTLBException(address); + // regs.cop0.FireException(Cop0::ExceptionCode::AddressErrorLoad, 0, regs.oldPC); + // return; + panic("[JIT]: Unhandled ADEL exception in LW!"); + return; + } + + if (!regs.cop0.MapVAddr(Cop0::LOAD, address, paddr)) { + // regs.cop0.HandleTLBException(address); + // regs.cop0.FireException(Cop0::GetTLBExceptionCode(regs.cop0.tlbError, Cop0::LOAD), 0, regs.oldPC); + panic("[JIT]: Unhandled TLBL exception in LW!"); + return; + } + + code.mov(code.ARG2, paddr); + emitMemberFunctionCall(&Mem::Read, &mem); + regs.Write(instr.rt(), code.rax); + return; } - if (!regs.cop0.MapVAddr(Cop0::LOAD, address, paddr)) { - // regs.cop0.HandleTLBException(address); - // regs.cop0.FireException(Cop0::GetTLBExceptionCode(regs.cop0.tlbError, Cop0::LOAD), 0, regs.oldPC); - panic("[JIT]: Unhandled TLBL exception in LW!"); - return; - } + code.mov(code.ARG2, Cop0::LOAD); - code.mov(code.ARG2, paddr); + regs.Read(instr.rs(), code.ARG3); + code.add(code.ARG3, offset); + + code.mov(code.ARG4, reinterpret_cast(&paddr)); + emitMemberFunctionCall(&Cop0::MapVAddr, ®s.cop0); + + code.mov(code.ARG2, code.qword[code.ARG4]); emitMemberFunctionCall(&Mem::Read, &mem); regs.Write(instr.rt(), code.rax); - return; - } - - code.mov(code.ARG2, Cop0::LOAD); - - regs.Read(instr.rs(), code.ARG3); - code.add(code.ARG3, offset); - - code.mov(code.ARG4, reinterpret_cast(&paddr)); - emitMemberFunctionCall(&Cop0::MapVAddr, ®s.cop0); - - code.mov(code.ARG2, code.qword[code.ARG4]); - emitMemberFunctionCall(&Mem::Read, &mem); - regs.Write(instr.rt(), code.rax); } void JIT::lwc1(const Instruction) { panic("[JIT]: Implement constant LWC1!"); } @@ -1121,271 +1123,287 @@ void JIT::lwu(const Instruction) { panic("[JIT]: Implement constant LWU!"); } void JIT::lwr(const Instruction) { panic("[JIT]: Implement constant LWR!"); } void JIT::mfhi(const Instruction instr) { - if (regs.GetHIConstant()) { - regs.Write(instr.rd(), regs.hi); - } else { - code.mov(code.SCR1, code.qword[HI_OFFSET]); - regs.Write(instr.rd(), code.SCR1); - } + if (regs.GetHIConstant()) { + regs.Write(instr.rd(), regs.hi); + } else { + code.mov(code.SCR1, code.qword[HI_OFFSET]); + regs.Write(instr.rd(), code.SCR1); + } } void JIT::mflo(const Instruction instr) { - if (regs.GetLOConstant()) { - regs.Write(instr.rd(), regs.lo); - } else { - code.mov(code.SCR1, code.qword[LO_OFFSET]); - regs.Write(instr.rd(), code.SCR1); - } + if (regs.GetLOConstant()) { + regs.Write(instr.rd(), regs.lo); + } else { + code.mov(code.SCR1, code.qword[LO_OFFSET]); + regs.Write(instr.rd(), code.SCR1); + } } void JIT::mult(const Instruction instr) { - if (regs.IsRegConstant(instr.rt(), instr.rs())) { - auto rt = regs.Read(instr.rt()); - auto rs = regs.Read(instr.rs()); - s64 result = (s64)rt * (s64)rs; - regs.lo = (s64)((s32)result); - regs.SetLOConstant(); - regs.hi = (s64)((s32)(result >> 32)); - regs.SetHIConstant(); - } else { - panic("[JIT]: Implement non constant MULT!"); - } + if (regs.IsRegConstant(instr.rt(), instr.rs())) { + auto rt = regs.Read(instr.rt()); + auto rs = regs.Read(instr.rs()); + s64 result = (s64)rt * (s64)rs; + regs.lo = (s64)((s32)result); + regs.SetLOConstant(); + regs.hi = (s64)((s32)(result >> 32)); + regs.SetHIConstant(); + } else { + panic("[JIT]: Implement non constant MULT!"); + } } void JIT::multu(const Instruction instr) { - if (regs.IsRegConstant(instr.rt(), instr.rs())) { - auto rt = regs.Read(instr.rt()); - auto rs = regs.Read(instr.rs()); - u64 result = (u64)rt * (u64)rs; - regs.lo = (s64)((s32)result); - regs.SetLOConstant(); - regs.hi = (s64)((s32)(result >> 32)); - regs.SetHIConstant(); - } else { - panic("[JIT]: Implement non constant MULTU!"); - } + if (regs.IsRegConstant(instr.rt(), instr.rs())) { + auto rt = regs.Read(instr.rt()); + auto rs = regs.Read(instr.rs()); + u64 result = (u64)rt * (u64)rs; + regs.lo = (s64)((s32)result); + regs.SetLOConstant(); + regs.hi = (s64)((s32)(result >> 32)); + regs.SetHIConstant(); + } else { + panic("[JIT]: Implement non constant MULTU!"); + } } void JIT::mthi(const Instruction instr) { - if (regs.IsRegConstant(instr.rs())) { - regs.hi = regs.Read(instr.rs()); - regs.SetHIConstant(); - } else { - regs.Read(instr.rs(), code.SCR1); - code.mov(code.qword[HI_OFFSET], code.SCR1); - regs.UnsetHIConstant(); - } + if (regs.IsRegConstant(instr.rs())) { + regs.hi = regs.Read(instr.rs()); + regs.SetHIConstant(); + } else { + regs.Read(instr.rs(), code.SCR1); + code.mov(code.qword[HI_OFFSET], code.SCR1); + regs.UnsetHIConstant(); + } } void JIT::mtlo(const Instruction instr) { - if (regs.IsRegConstant(instr.rs())) { - regs.lo = regs.Read(instr.rs()); - regs.SetLOConstant(); - } else { - regs.Read(instr.rs(), code.SCR1); - code.mov(code.qword[LO_OFFSET], code.SCR1); - regs.UnsetLOConstant(); - } + if (regs.IsRegConstant(instr.rs())) { + regs.lo = regs.Read(instr.rs()); + regs.SetLOConstant(); + } else { + regs.Read(instr.rs(), code.SCR1); + code.mov(code.qword[LO_OFFSET], code.SCR1); + regs.UnsetLOConstant(); + } } void JIT::nor(const Instruction instr) { - if (regs.IsRegConstant(instr.rs(), instr.rt())) { - regs.Write(instr.rd(), ~(regs.Read(instr.rs()) | regs.Read(instr.rt()))); - } else { - panic("[JIT]: Implement non constant NOR!"); - } + if (regs.IsRegConstant(instr.rs(), instr.rt())) { + regs.Write(instr.rd(), ~(regs.Read(instr.rs()) | regs.Read(instr.rt()))); + } else { + panic("[JIT]: Implement non constant NOR!"); + } } void JIT::slti(const Instruction instr) { - if (regs.IsRegConstant(instr.rs())) { - s16 imm = instr; - regs.Write(instr.rt(), regs.Read(instr.rs()) < imm); - return; - } + if (regs.IsRegConstant(instr.rs())) { + s16 imm = instr; + regs.Write(instr.rt(), regs.Read(instr.rs()) < imm); + return; + } - panic("[JIT]: Implement non constant SLTI!"); + panic("[JIT]: Implement non constant SLTI!"); } void JIT::sltiu(const Instruction instr) { - if (regs.IsRegConstant(instr.rs())) { - s16 imm = instr; - regs.Write(instr.rt(), regs.Read(instr.rs()) < imm); - } else { - panic("[JIT]: Implement non constant SLTIU!"); - } + if (regs.IsRegConstant(instr.rs())) { + s16 imm = instr; + regs.Write(instr.rt(), regs.Read(instr.rs()) < imm); + } else { + panic("[JIT]: Implement non constant SLTIU!"); + } } void JIT::slt(const Instruction instr) { - if (regs.IsRegConstant(instr.rs(), instr.rt())) { - regs.Write(instr.rd(), regs.Read(instr.rs()) < regs.Read(instr.rt())); - return; - } + if (regs.IsRegConstant(instr.rs(), instr.rt())) { + regs.Write(instr.rd(), regs.Read(instr.rs()) < regs.Read(instr.rt())); + return; + } + + if (regs.IsRegConstant(instr.rt())) { + s64 rt = regs.Read(instr.rt()); + regs.Read(instr.rs(), code.SCR1); + code.cmp(code.SCR1, rt); + code.setl(code.SCR1.cvt8()); + regs.Write(instr.rd(), code.SCR1.cvt8()); + return; + } + + if (regs.IsRegConstant(instr.rs())) { + s64 rs = regs.Read(instr.rs()); + regs.Read(instr.rt(), code.SCR1); + code.cmp(code.SCR1, rs); + code.setge(code.SCR1.cvt8()); + regs.Write(instr.rd(), code.SCR1.cvt8()); + return; + } - if (regs.IsRegConstant(instr.rt())) { - s64 rt = regs.Read(instr.rt()); regs.Read(instr.rs(), code.SCR1); - code.cmp(code.SCR1, rt); + regs.Read(instr.rt(), code.SCR2); + code.cmp(code.SCR1, code.SCR2); code.setl(code.SCR1.cvt8()); regs.Write(instr.rd(), code.SCR1.cvt8()); - return; - } - - if (regs.IsRegConstant(instr.rs())) { - s64 rs = regs.Read(instr.rs()); - regs.Read(instr.rt(), code.SCR1); - code.cmp(code.SCR1, rs); - code.setge(code.SCR1.cvt8()); - regs.Write(instr.rd(), code.SCR1.cvt8()); - return; - } - - regs.Read(instr.rs(), code.SCR1); - regs.Read(instr.rt(), code.SCR2); - code.cmp(code.SCR1, code.SCR2); - code.setl(code.SCR1.cvt8()); - regs.Write(instr.rd(), code.SCR1.cvt8()); } void JIT::sltu(const Instruction instr) { - if (regs.IsRegConstant(instr.rs(), instr.rt())) { - regs.Write(instr.rd(), regs.Read(instr.rs()) < regs.Read(instr.rt())); - } else { - panic("[JIT]: Implement non constant SLT!"); - } + if (regs.IsRegConstant(instr.rs(), instr.rt())) { + regs.Write(instr.rd(), regs.Read(instr.rs()) < regs.Read(instr.rt())); + } else { + panic("[JIT]: Implement non constant SLT!"); + } } void JIT::sll(const Instruction instr) { - const u8 sa = ((instr >> 6) & 0x1f); - if (regs.IsRegConstant(instr.rt())) { - const s32 result = regs.Read(instr.rt()) << sa; - regs.Write(instr.rd(), (s64)result); - } else { - regs.Read(instr.rt(), code.SCR1); - code.sal(code.SCR1, sa); - regs.Write(instr.rd(), code.SCR1.cvt32()); - } + const u8 sa = ((instr >> 6) & 0x1f); + if (regs.IsRegConstant(instr.rt())) { + const s32 result = regs.Read(instr.rt()) << sa; + regs.Write(instr.rd(), (s64)result); + } else { + regs.Read(instr.rt(), code.SCR1); + code.sal(code.SCR1, sa); + regs.Write(instr.rd(), code.SCR1.cvt32()); + } } void JIT::sllv(const Instruction instr) { - if (regs.IsRegConstant(instr.rs(), instr.rt())) { - u8 sa = (regs.Read(instr.rs())) & 0x1F; - u32 rt = regs.Read(instr.rt()); - s32 result = rt << sa; - regs.Write(instr.rd(), (s64)result); - } else { - panic("[JIT]: Implement non constant SLLV!"); - } + if (regs.IsRegConstant(instr.rs(), instr.rt())) { + u8 sa = (regs.Read(instr.rs())) & 0x1F; + u32 rt = regs.Read(instr.rt()); + s32 result = rt << sa; + regs.Write(instr.rd(), (s64)result); + } else { + panic("[JIT]: Implement non constant SLLV!"); + } } void JIT::sub(const Instruction instr) { - if (regs.IsRegConstant(instr.rs(), instr.rt())) { - s32 rt = regs.Read(instr.rt()); - s32 rs = regs.Read(instr.rs()); - s32 result = rs - rt; - if (check_signed_underflow(rs, rt, result)) { - regs.cop0.FireException(ExceptionCode::Overflow, 0, regs.oldPC); + if (regs.IsRegConstant(instr.rs(), instr.rt())) { + s32 rt = regs.Read(instr.rt()); + s32 rs = regs.Read(instr.rs()); + s32 result = rs - rt; + if (check_signed_underflow(rs, rt, result)) { + regs.cop0.FireException(Cop0::ExceptionCode::Overflow, 0, regs.oldPC); + } else { + regs.Write(instr.rd(), result); + } } else { - regs.Write(instr.rd(), result); + panic("[JIT]: Implement non constant SUB!"); } - } else { - panic("[JIT]: Implement non constant SUB!"); - } } void JIT::subu(const Instruction instr) { - if (regs.IsRegConstant(instr.rs(), instr.rt())) { - u32 rt = regs.Read(instr.rt()); - u32 rs = regs.Read(instr.rs()); - u32 result = rs - rt; - regs.Write(instr.rd(), (s64)((s32)result)); - } else { - panic("[JIT]: Implement non constant SUBU!"); - } + if (regs.IsRegConstant(instr.rs(), instr.rt())) { + u32 rt = regs.Read(instr.rt()); + u32 rs = regs.Read(instr.rs()); + u32 result = rs - rt; + regs.Write(instr.rd(), (s64)((s32)result)); + } else { + panic("[JIT]: Implement non constant SUBU!"); + } } void JIT::sra(const Instruction instr) { - if (regs.IsRegConstant(instr.rt())) { - s64 rt = regs.Read(instr.rt()); - u8 sa = ((instr >> 6) & 0x1f); - s32 result = rt >> sa; - regs.Write(instr.rd(), result); - } else { - panic("[JIT]: Implement non constant SRA!"); - } + if (regs.IsRegConstant(instr.rt())) { + s64 rt = regs.Read(instr.rt()); + u8 sa = ((instr >> 6) & 0x1f); + s32 result = rt >> sa; + regs.Write(instr.rd(), result); + } else { + panic("[JIT]: Implement non constant SRA!"); + } } void JIT::srav(const Instruction instr) { - if (regs.IsRegConstant(instr.rs(), instr.rt())) { - s64 rs = regs.Read(instr.rs()); - s64 rt = regs.Read(instr.rt()); - u8 sa = rs & 0x1f; - s32 result = rt >> sa; - regs.Write(instr.rd(), result); - } else { - panic("[JIT]: Implement non constant SRAV!"); - } + if (regs.IsRegConstant(instr.rs(), instr.rt())) { + s64 rs = regs.Read(instr.rs()); + s64 rt = regs.Read(instr.rt()); + u8 sa = rs & 0x1f; + s32 result = rt >> sa; + regs.Write(instr.rd(), result); + } else { + panic("[JIT]: Implement non constant SRAV!"); + } } void JIT::srl(const Instruction instr) { - if (regs.IsRegConstant(instr.rt())) { - u32 rt = regs.Read(instr.rt()); - u8 sa = ((instr >> 6) & 0x1f); - u32 result = rt >> sa; - regs.Write(instr.rd(), (s32)result); - } else { - panic("[JIT]: Implement non constant SRL!"); - } + if (regs.IsRegConstant(instr.rt())) { + u32 rt = regs.Read(instr.rt()); + u8 sa = ((instr >> 6) & 0x1f); + u32 result = rt >> sa; + regs.Write(instr.rd(), (s32)result); + } else { + panic("[JIT]: Implement non constant SRL!"); + } } void JIT::sw(const Instruction instr) { - u32 physical; - if (regs.IsRegConstant(instr.rs(), instr.rt())) { - const s16 offset = instr; - const u64 address = regs.Read(instr.rs()) + offset; - if (check_address_error(0b11, address)) { - // regs.cop0.HandleTLBException(address); - // regs.cop0.FireException(ExceptionCode::AddressErrorStore, 0, regs.oldPC); - panic("[JIT]: Unhandled ADES exception in SW!"); - return; + u32 physical; + if (regs.IsRegConstant(instr.rs(), instr.rt())) { + const s16 offset = instr; + const u64 address = regs.Read(instr.rs()) + offset; + if (check_address_error(0b11, address)) { + // regs.cop0.HandleTLBException(address); + // regs.cop0.FireException(Cop0::ExceptionCode::AddressErrorStore, 0, regs.oldPC); + panic("[JIT]: Unhandled ADES exception in SW!"); + return; + } + + if (!regs.cop0.MapVAddr(Cop0::STORE, address, physical)) { + // regs.cop0.HandleTLBException(address); + // regs.cop0.FireException(Cop0::GetTLBExceptionCode(regs.cop0.tlbError, Cop0::STORE), 0, regs.oldPC); + panic("[JIT]: Unhandled TLBS exception in SW!"); + } else { + code.mov(code.ARG2, physical); + regs.Read(instr.rt(), code.ARG3); + emitMemberFunctionCall(&Mem::WriteJIT, &mem); + } + + return; } - if (!regs.cop0.MapVAddr(Cop0::STORE, address, physical)) { - // regs.cop0.HandleTLBException(address); - // regs.cop0.FireException(Cop0::GetTLBExceptionCode(regs.cop0.tlbError, Cop0::STORE), 0, regs.oldPC); - panic("[JIT]: Unhandled TLBS exception in SW!"); - } else { - code.mov(code.ARG2, physical); - regs.Read(instr.rt(), code.ARG3); - emitMemberFunctionCall(&Mem::WriteJIT, &mem); + if (regs.IsRegConstant(instr.rs())) { + const s16 offset = instr; + const u64 address = regs.Read(instr.rs()) + offset; + if (check_address_error(0b11, address)) { + // regs.cop0.HandleTLBException(address); + // regs.cop0.FireException(Cop0::ExceptionCode::AddressErrorStore, 0, regs.oldPC); + panic("[JIT]: Unhandled ADES exception in SW!"); + return; + } + + if (!regs.cop0.MapVAddr(Cop0::STORE, address, physical)) { + // regs.cop0.HandleTLBException(address); + // regs.cop0.FireException(Cop0::GetTLBExceptionCode(regs.cop0.tlbError, Cop0::STORE), 0, regs.oldPC); + panic("[JIT]: Unhandled TLBS exception in SW!"); + } else { + code.mov(code.ARG2, physical); + regs.Read(instr.rt(), code.ARG3); + emitMemberFunctionCall(&Mem::WriteJIT, &mem); + } + + return; } - return; - } + if (regs.IsRegConstant(instr.rt())) { + const s16 offset = instr; + code.mov(code.ARG2, Cop0::STORE); - if (regs.IsRegConstant(instr.rs())) { - const s16 offset = instr; - const u64 address = regs.Read(instr.rs()) + offset; - if (check_address_error(0b11, address)) { - // regs.cop0.HandleTLBException(address); - // regs.cop0.FireException(ExceptionCode::AddressErrorStore, 0, regs.oldPC); - panic("[JIT]: Unhandled ADES exception in SW!"); - return; + regs.Read(instr.rs(), code.ARG3); + code.add(code.ARG3, offset); + + code.mov(code.ARG4, reinterpret_cast(&physical)); + emitMemberFunctionCall(&Cop0::MapVAddr, ®s.cop0); + + code.mov(code.ARG2, code.qword[code.ARG4]); + regs.Read(instr.rt(), code.ARG3); + emitMemberFunctionCall(&Mem::WriteJIT, &mem); + + return; } - if (!regs.cop0.MapVAddr(Cop0::STORE, address, physical)) { - // regs.cop0.HandleTLBException(address); - // regs.cop0.FireException(Cop0::GetTLBExceptionCode(regs.cop0.tlbError, Cop0::STORE), 0, regs.oldPC); - panic("[JIT]: Unhandled TLBS exception in SW!"); - } else { - code.mov(code.ARG2, physical); - regs.Read(instr.rt(), code.ARG3); - emitMemberFunctionCall(&Mem::WriteJIT, &mem); - } - - return; - } - - if (regs.IsRegConstant(instr.rt())) { const s16 offset = instr; code.mov(code.ARG2, Cop0::STORE); @@ -1398,70 +1416,54 @@ void JIT::sw(const Instruction instr) { code.mov(code.ARG2, code.qword[code.ARG4]); regs.Read(instr.rt(), code.ARG3); emitMemberFunctionCall(&Mem::WriteJIT, &mem); - - return; - } - - const s16 offset = instr; - code.mov(code.ARG2, Cop0::STORE); - - regs.Read(instr.rs(), code.ARG3); - code.add(code.ARG3, offset); - - code.mov(code.ARG4, reinterpret_cast(&physical)); - emitMemberFunctionCall(&Cop0::MapVAddr, ®s.cop0); - - code.mov(code.ARG2, code.qword[code.ARG4]); - regs.Read(instr.rt(), code.ARG3); - emitMemberFunctionCall(&Mem::WriteJIT, &mem); } void JIT::srlv(const Instruction instr) { - if (regs.IsRegConstant(instr.rs(), instr.rt())) { - u8 sa = (regs.Read(instr.rs()) & 0x1F); - u32 rt = regs.Read(instr.rt()); - s32 result = rt >> sa; - regs.Write(instr.rd(), (s64)result); - } else { - panic("[JIT]: Implement non constant SRLV!"); - } + if (regs.IsRegConstant(instr.rs(), instr.rt())) { + u8 sa = (regs.Read(instr.rs()) & 0x1F); + u32 rt = regs.Read(instr.rt()); + s32 result = rt >> sa; + regs.Write(instr.rd(), (s64)result); + } else { + panic("[JIT]: Implement non constant SRLV!"); + } } void JIT::or_(const Instruction instr) { - if (regs.IsRegConstant(instr.rs(), instr.rt())) { - regs.Write(instr.rd(), regs.Read(instr.rs()) | regs.Read(instr.rt())); - } else { - panic("[JIT]: Implement non constant OR!"); - } + if (regs.IsRegConstant(instr.rs(), instr.rt())) { + regs.Write(instr.rd(), regs.Read(instr.rs()) | regs.Read(instr.rt())); + } else { + panic("[JIT]: Implement non constant OR!"); + } } void JIT::ori(const Instruction instr) { - s64 imm = (u16)instr; - if (regs.IsRegConstant(instr.rs())) { - s64 result = imm | regs.Read(instr.rs()); - regs.Write(instr.rt(), result); - return; - } + s64 imm = (u16)instr; + if (regs.IsRegConstant(instr.rs())) { + s64 result = imm | regs.Read(instr.rs()); + regs.Write(instr.rt(), result); + return; + } - regs.Read(instr.rs(), code.SCR1); - code.or_(code.SCR1, imm); - regs.Write(instr.rt(), code.SCR1); + regs.Read(instr.rs(), code.SCR1); + code.or_(code.SCR1, imm); + regs.Write(instr.rt(), code.SCR1); } void JIT::xori(const Instruction instr) { - if (regs.IsRegConstant(instr.rs())) { - s64 imm = (u16)instr; - regs.Write(instr.rt(), regs.Read(instr.rs()) ^ imm); - } else { - panic("[JIT]: Implement non constant XORI!"); - } + if (regs.IsRegConstant(instr.rs())) { + s64 imm = (u16)instr; + regs.Write(instr.rt(), regs.Read(instr.rs()) ^ imm); + } else { + panic("[JIT]: Implement non constant XORI!"); + } } void JIT::xor_(const Instruction instr) { - if (regs.IsRegConstant(instr.rs(), instr.rt())) { - regs.Write(instr.rd(), regs.Read(instr.rt()) ^ regs.Read(instr.rs())); - } else { - panic("[JIT]: Implement non constant XOR!"); - } + if (regs.IsRegConstant(instr.rs(), instr.rt())) { + regs.Write(instr.rd(), regs.Read(instr.rt()) ^ regs.Read(instr.rs())); + } else { + panic("[JIT]: Implement non constant XOR!"); + } } } // namespace n64 diff --git a/src/backend/core/registers/Cop0.cpp b/src/backend/core/registers/Cop0.cpp index e1dba7e..6661aed 100644 --- a/src/backend/core/registers/Cop0.cpp +++ b/src/backend/core/registers/Cop0.cpp @@ -5,547 +5,570 @@ namespace n64 { Cop0::Cop0() { Reset(); } void Cop0::Reset() { - cause.raw = 0xB000007C; - status.raw = 0; - status.cu0 = 1; - status.cu1 = 1; - status.fr = 1; - PRId = 0x00000B22; - Config = 0x7006E463; - EPC = 0xFFFFFFFFFFFFFFFFll; - ErrorEPC = 0xFFFFFFFFFFFFFFFFll; - wired = 0; - index.raw = 63; - badVaddr = 0xFFFFFFFFFFFFFFFF; + cause.raw = 0xB000007C; + status.raw = 0; + status.cu0 = 1; + status.cu1 = 1; + status.fr = 1; + PRId = 0x00000B22; + Config = 0x7006E463; + EPC = 0xFFFFFFFFFFFFFFFFll; + ErrorEPC = 0xFFFFFFFFFFFFFFFFll; + wired = 0; + index.raw = 63; + badVaddr = 0xFFFFFFFFFFFFFFFF; - kernelMode = {true}; - supervisorMode = {false}; - userMode = {false}; - is64BitAddressing = {false}; - llbit = {}; + kernelMode = {true}; + supervisorMode = {false}; + userMode = {false}; + is64BitAddressing = {false}; + llbit = {}; - pageMask = {}; - entryHi = {}; - entryLo0 = {}, entryLo1 = {}; - context = {}; - wired = {}, r7 = {}; - count = {}; - compare = {}; - LLAddr = {}, WatchLo = {}, WatchHi = {}; - xcontext = {}; - r21 = {}, r22 = {}, r23 = {}, r24 = {}, r25 = {}; - ParityError = {}, CacheError = {}, TagLo = {}, TagHi = {}; - ErrorEPC = {}; - r31 = {}; - memset(tlb, 0, sizeof(TLBEntry) * 32); - tlbError = NONE; - openbus = {}; + pageMask = {}; + entryHi = {}; + entryLo0 = {}, entryLo1 = {}; + context = {}; + wired = {}, r7 = {}; + count = {}; + compare = {}; + LLAddr = {}, WatchLo = {}, WatchHi = {}; + xcontext = {}; + r21 = {}, r22 = {}, r23 = {}, r24 = {}, r25 = {}; + ParityError = {}, CacheError = {}, tagLo = {}, TagHi = {}; + ErrorEPC = {}; + r31 = {}; + memset(tlb, 0, sizeof(TLBEntry) * 32); + tlbError = NONE; + openbus = {}; } u32 Cop0::GetReg32(const u8 addr) { - switch (addr) { - case COP0_REG_INDEX: - return index.raw & INDEX_MASK; - case COP0_REG_RANDOM: - return GetRandom(); - case COP0_REG_ENTRYLO0: - return entryLo0.raw; - case COP0_REG_ENTRYLO1: - return entryLo1.raw; - case COP0_REG_CONTEXT: - return context.raw; - case COP0_REG_PAGEMASK: - return pageMask.raw; - case COP0_REG_WIRED: - return wired; - case COP0_REG_BADVADDR: - return badVaddr; - case COP0_REG_COUNT: - return GetCount(); - case COP0_REG_ENTRYHI: - return entryHi.raw; - case COP0_REG_COMPARE: - return compare; - case COP0_REG_STATUS: - return status.raw; - case COP0_REG_CAUSE: - return cause.raw; - case COP0_REG_EPC: - return EPC; - case COP0_REG_PRID: - return PRId; - case COP0_REG_CONFIG: - return Config; - case COP0_REG_LLADDR: - return LLAddr; - case COP0_REG_WATCHLO: - return WatchLo; - case COP0_REG_WATCHHI: - return WatchHi; - case COP0_REG_XCONTEXT: - return xcontext.raw; - case COP0_REG_PARITY_ERR: - return ParityError; - case COP0_REG_CACHE_ERR: - return CacheError; - case COP0_REG_TAGLO: - return TagLo; - case COP0_REG_TAGHI: - return TagHi; - case COP0_REG_ERROREPC: - return ErrorEPC; - case 7: - case 21: - case 22: - case 23: - case 24: - case 25: - case 31: - return openbus; - default: - panic("Unsupported word read from COP0 register {}", addr); - } + switch (addr) { + case COP0_REG_INDEX: + return index.raw & INDEX_MASK; + case COP0_REG_RANDOM: + return GetRandom(); + case COP0_REG_ENTRYLO0: + return entryLo0.raw; + case COP0_REG_ENTRYLO1: + return entryLo1.raw; + case COP0_REG_CONTEXT: + return context.raw; + case COP0_REG_PAGEMASK: + return pageMask.raw; + case COP0_REG_WIRED: + return wired; + case COP0_REG_BADVADDR: + return badVaddr; + case COP0_REG_COUNT: + return GetCount(); + case COP0_REG_ENTRYHI: + return entryHi.raw; + case COP0_REG_COMPARE: + return compare; + case COP0_REG_STATUS: + return status.raw; + case COP0_REG_CAUSE: + return cause.raw; + case COP0_REG_EPC: + return EPC; + case COP0_REG_PRID: + return PRId; + case COP0_REG_CONFIG: + return Config; + case COP0_REG_LLADDR: + return LLAddr; + case COP0_REG_WATCHLO: + return WatchLo; + case COP0_REG_WATCHHI: + return WatchHi; + case COP0_REG_XCONTEXT: + return xcontext.raw; + case COP0_REG_PARITY_ERR: + return ParityError; + case COP0_REG_CACHE_ERR: + return CacheError; + case COP0_REG_TAGLO: + return tagLo.raw; + case COP0_REG_TAGHI: + return TagHi; + case COP0_REG_ERROREPC: + return ErrorEPC; + case 7: + case 21: + case 22: + case 23: + case 24: + case 25: + case 31: + return openbus; + default: + panic("Unsupported word read from COP0 register {}", addr); + } } u64 Cop0::GetReg64(const u8 addr) const { - switch (addr) { - case COP0_REG_ENTRYLO0: - return entryLo0.raw; - case COP0_REG_ENTRYLO1: - return entryLo1.raw; - case COP0_REG_CONTEXT: - return context.raw; - case COP0_REG_BADVADDR: - return badVaddr; - case COP0_REG_ENTRYHI: - return entryHi.raw; - case COP0_REG_STATUS: - return status.raw; - case COP0_REG_EPC: - return EPC; - case COP0_REG_PRID: - return PRId; - case COP0_REG_LLADDR: - return LLAddr; - case COP0_REG_XCONTEXT: - return xcontext.raw & 0xFFFFFFFFFFFFFFF0; - case COP0_REG_ERROREPC: - return ErrorEPC; - case 7: - case 21: - case 22: - case 23: - case 24: - case 25: - case 31: - return openbus; - default: - panic("Unsupported dword read from COP0 register {}", addr); - } + switch (addr) { + case COP0_REG_ENTRYLO0: + return entryLo0.raw; + case COP0_REG_ENTRYLO1: + return entryLo1.raw; + case COP0_REG_CONTEXT: + return context.raw; + case COP0_REG_BADVADDR: + return badVaddr; + case COP0_REG_ENTRYHI: + return entryHi.raw; + case COP0_REG_STATUS: + return status.raw; + case COP0_REG_EPC: + return EPC; + case COP0_REG_PRID: + return PRId; + case COP0_REG_LLADDR: + return LLAddr; + case COP0_REG_XCONTEXT: + return xcontext.raw & 0xFFFFFFFFFFFFFFF0; + case COP0_REG_ERROREPC: + return ErrorEPC; + case 7: + case 21: + case 22: + case 23: + case 24: + case 25: + case 31: + return openbus; + default: + panic("Unsupported dword read from COP0 register {}", addr); + } } void Cop0::SetReg32(const u8 addr, const u32 value) { - openbus = value & 0xFFFFFFFF; - switch (addr) { - case COP0_REG_INDEX: - index.raw = value & INDEX_MASK; - break; - case COP0_REG_RANDOM: - break; - case COP0_REG_ENTRYLO0: - entryLo0.raw = value & ENTRY_LO_MASK; - break; - case COP0_REG_ENTRYLO1: - entryLo1.raw = value & ENTRY_LO_MASK; - break; - case COP0_REG_CONTEXT: - context.raw = (s64(s32(value)) & 0xFFFFFFFFFF800000) | (context.raw & 0x7FFFFF); - break; - case COP0_REG_PAGEMASK: - pageMask.raw = value & PAGEMASK_MASK; - break; - case COP0_REG_WIRED: - wired = value & 63; - break; - case COP0_REG_BADVADDR: - break; - case COP0_REG_COUNT: - count = (u64)value << 1; - break; - case COP0_REG_ENTRYHI: - entryHi.raw = s64(s32(value)) & ENTRY_HI_MASK; - break; - case COP0_REG_COMPARE: - compare = value; - cause.ip7 = false; - break; - case COP0_REG_STATUS: - status.raw &= ~STATUS_MASK; - status.raw |= (value & STATUS_MASK); - Update(); - break; - case COP0_REG_CAUSE: - { - Cop0Cause tmp{}; - tmp.raw = value; - cause.ip0 = tmp.ip0; - cause.ip1 = tmp.ip1; + openbus = value & 0xFFFFFFFF; + switch (addr) { + case COP0_REG_INDEX: + index.raw = value & INDEX_MASK; + break; + case COP0_REG_RANDOM: + break; + case COP0_REG_ENTRYLO0: + entryLo0.raw = value & ENTRY_LO_MASK; + break; + case COP0_REG_ENTRYLO1: + entryLo1.raw = value & ENTRY_LO_MASK; + break; + case COP0_REG_CONTEXT: + context.raw = (s64(s32(value)) & 0xFFFFFFFFFF800000) | (context.raw & 0x7FFFFF); + break; + case COP0_REG_PAGEMASK: + pageMask.raw = value & PAGEMASK_MASK; + break; + case COP0_REG_WIRED: + wired = value & 63; + break; + case COP0_REG_BADVADDR: + break; + case COP0_REG_COUNT: + count = (u64)value << 1; + break; + case COP0_REG_ENTRYHI: + entryHi.raw = s64(s32(value)) & ENTRY_HI_MASK; + break; + case COP0_REG_COMPARE: + compare = value; + cause.ip7 = false; + break; + case COP0_REG_STATUS: + status.raw &= ~STATUS_MASK; + status.raw |= (value & STATUS_MASK); + Update(); + break; + case COP0_REG_CAUSE: + { + Cause tmp{}; + tmp.raw = value; + cause.ip0 = tmp.ip0; + cause.ip1 = tmp.ip1; + } + break; + case COP0_REG_EPC: + EPC = s64(s32(value)); + break; + case COP0_REG_PRID: + break; + case COP0_REG_CONFIG: + Config &= ~CONFIG_MASK; + Config |= (value & CONFIG_MASK); + break; + case COP0_REG_LLADDR: + LLAddr = value; + break; + case COP0_REG_WATCHLO: + WatchLo = value; + break; + case COP0_REG_WATCHHI: + WatchHi = value; + break; + case COP0_REG_XCONTEXT: + xcontext.raw = (s64(s32(value)) & 0xFFFFFFFE00000000) | (xcontext.raw & 0x1FFFFFFFF); + break; + case COP0_REG_PARITY_ERR: + ParityError = value & 0xff; + break; + case COP0_REG_CACHE_ERR: + break; + case COP0_REG_TAGLO: + tagLo.raw = value; + break; + case COP0_REG_TAGHI: + TagHi = value; + break; + case COP0_REG_ERROREPC: + ErrorEPC = s64(s32(value)); + break; + case 7: + case 21: + case 22: + case 23: + case 24: + case 25: + case 31: + break; + default: + panic("Unsupported word write from COP0 register {}", addr); } - break; - case COP0_REG_EPC: - EPC = s64(s32(value)); - break; - case COP0_REG_PRID: - break; - case COP0_REG_CONFIG: - Config &= ~CONFIG_MASK; - Config |= (value & CONFIG_MASK); - break; - case COP0_REG_LLADDR: - LLAddr = value; - break; - case COP0_REG_WATCHLO: - WatchLo = value; - break; - case COP0_REG_WATCHHI: - WatchHi = value; - break; - case COP0_REG_XCONTEXT: - xcontext.raw = (s64(s32(value)) & 0xFFFFFFFE00000000) | (xcontext.raw & 0x1FFFFFFFF); - break; - case COP0_REG_PARITY_ERR: - ParityError = value & 0xff; - break; - case COP0_REG_CACHE_ERR: - break; - case COP0_REG_TAGLO: - TagLo = value; - break; - case COP0_REG_TAGHI: - TagHi = value; - break; - case COP0_REG_ERROREPC: - ErrorEPC = s64(s32(value)); - break; - case 7: - case 21: - case 22: - case 23: - case 24: - case 25: - case 31: - break; - default: - panic("Unsupported word write from COP0 register {}", addr); - } } void Cop0::SetReg64(const u8 addr, const u64 value) { - openbus = value; - switch (addr) { - case COP0_REG_ENTRYLO0: - entryLo0.raw = value & ENTRY_LO_MASK; - break; - case COP0_REG_ENTRYLO1: - entryLo1.raw = value & ENTRY_LO_MASK; - break; - case COP0_REG_CONTEXT: - context.raw = (value & 0xFFFFFFFFFF800000) | (context.raw & 0x7FFFFF); - break; - case COP0_REG_XCONTEXT: - xcontext.raw = (value & 0xFFFFFFFE00000000) | (xcontext.raw & 0x1FFFFFFFF); - break; - case COP0_REG_ENTRYHI: - entryHi.raw = value & ENTRY_HI_MASK; - break; - case COP0_REG_STATUS: - status.raw = value; - break; - case COP0_REG_CAUSE: - { - Cop0Cause tmp{}; - tmp.raw = value; - cause.ip0 = tmp.ip0; - cause.ip1 = tmp.ip1; + openbus = value; + switch (addr) { + case COP0_REG_ENTRYLO0: + entryLo0.raw = value & ENTRY_LO_MASK; + break; + case COP0_REG_ENTRYLO1: + entryLo1.raw = value & ENTRY_LO_MASK; + break; + case COP0_REG_CONTEXT: + context.raw = (value & 0xFFFFFFFFFF800000) | (context.raw & 0x7FFFFF); + break; + case COP0_REG_XCONTEXT: + xcontext.raw = (value & 0xFFFFFFFE00000000) | (xcontext.raw & 0x1FFFFFFFF); + break; + case COP0_REG_ENTRYHI: + entryHi.raw = value & ENTRY_HI_MASK; + break; + case COP0_REG_STATUS: + status.raw = value; + break; + case COP0_REG_CAUSE: + { + Cause tmp{}; + tmp.raw = value; + cause.ip0 = tmp.ip0; + cause.ip1 = tmp.ip1; + } + break; + case COP0_REG_BADVADDR: + break; + case COP0_REG_EPC: + EPC = (s64)value; + break; + case COP0_REG_LLADDR: + LLAddr = value; + break; + case COP0_REG_ERROREPC: + ErrorEPC = (s64)value; + break; + default: + panic("Unsupported dword write to COP0 register {}", addr); } - break; - case COP0_REG_BADVADDR: - break; - case COP0_REG_EPC: - EPC = (s64)value; - break; - case COP0_REG_LLADDR: - LLAddr = value; - break; - case COP0_REG_ERROREPC: - ErrorEPC = (s64)value; - break; - default: - panic("Unsupported dword write to COP0 register {}", addr); - } } static FORCE_INLINE u64 getVPN(const u64 addr, const u64 pageMask) { - const u64 mask = pageMask | 0x1fff; - const u64 vpn = addr & 0xFFFFFFFFFF | addr >> 22 & 0x30000000000; + const u64 mask = pageMask | 0x1fff; + const u64 vpn = addr & 0xFFFFFFFFFF | addr >> 22 & 0x30000000000; - return vpn & ~mask; + return vpn & ~mask; } -TLBEntry *Cop0::TLBTryMatch(const u64 vaddr, int &index) { - for (int i = 0; i < 32; i++) { - TLBEntry *entry = &tlb[i]; - if (!entry->initialized) - continue; - - const u64 entry_vpn = getVPN(entry->entryHi.raw, entry->pageMask.raw); - const u64 vaddr_vpn = getVPN(vaddr, entry->pageMask.raw); +Cop0::TLBEntry *Cop0::TLBTryMatch(const u64 vaddr, int &index) { + for (int i = 0; i < 32; i++) { + TLBEntry *entry = &tlb[i]; + if (!entry->initialized) + continue; - const bool vpn_match = entry_vpn == vaddr_vpn; - const bool asid_match = entry->global || entryHi.asid == entry->entryHi.asid; + const u64 entry_vpn = getVPN(entry->entryHi.raw, entry->pageMask.raw); + const u64 vaddr_vpn = getVPN(vaddr, entry->pageMask.raw); - if(!vpn_match || !asid_match) - continue; + const bool vpn_match = entry_vpn == vaddr_vpn; + const bool asid_match = entry->global || entryHi.asid == entry->entryHi.asid; - index = i; - return entry; - } + if (!vpn_match || !asid_match) + continue; - return nullptr; + index = i; + return entry; + } + + return nullptr; } -TLBEntry *Cop0::TLBTryMatch(const u64 vaddr) { - for (auto &t : tlb) { - TLBEntry *entry = &t; - if (!entry->initialized) - continue; - - const u64 entry_vpn = getVPN(entry->entryHi.raw, entry->pageMask.raw); - const u64 vaddr_vpn = getVPN(vaddr, entry->pageMask.raw); +Cop0::TLBEntry *Cop0::TLBTryMatch(const u64 vaddr) { + for (auto &t : tlb) { + TLBEntry *entry = &t; + if (!entry->initialized) + continue; - const bool vpn_match = entry_vpn == vaddr_vpn; - const bool asid_match = entry->global || entryHi.asid == entry->entryHi.asid; - - if (vpn_match && asid_match) - return entry; - } + const u64 entry_vpn = getVPN(entry->entryHi.raw, entry->pageMask.raw); + const u64 vaddr_vpn = getVPN(vaddr, entry->pageMask.raw); - return nullptr; + const bool vpn_match = entry_vpn == vaddr_vpn; + const bool asid_match = entry->global || entryHi.asid == entry->entryHi.asid; + + if (vpn_match && asid_match) + return entry; + } + + return nullptr; } bool Cop0::ProbeTLB(const TLBAccessType accessType, const u64 vaddr, u32 &paddr) { - const TLBEntry *entry = TLBTryMatch(vaddr); - if (!entry) { - tlbError = MISS; - return false; - } + const TLBEntry *entry = TLBTryMatch(vaddr); + if (!entry) { + tlbError = MISS; + return false; + } - const u32 mask = entry->pageMask.mask << 12 | 0xFFF; - const u32 odd = vaddr & mask + 1; + const u32 mask = entry->pageMask.mask << 12 | 0xFFF; + const u32 odd = vaddr & mask + 1; - const EntryLo entryLo = odd ? entry->entryLo1 : entry->entryLo0; + const EntryLo entryLo = odd ? entry->entryLo1 : entry->entryLo0; - if (!entryLo.v) { - tlbError = INVALID; - return false; - } + if (!entryLo.v) { + tlbError = INVALID; + return false; + } - if (accessType == STORE && !entryLo.d) { - tlbError = MODIFICATION; - return false; - } + if (accessType == STORE && !entryLo.d) { + tlbError = MODIFICATION; + return false; + } - paddr = entryLo.pfn << 12 | vaddr & mask; + paddr = entryLo.pfn << 12 | vaddr & mask; - return true; + return true; } void Cop0::FireException(const ExceptionCode code, const int cop, s64 pc) { - Registers& regs = Core::GetRegs(); + Registers ®s = Core::GetRegs(); - u16 vectorOffset = 0x0180; - if(tlbError == MISS && (code == ExceptionCode::TLBLoad || code == ExceptionCode::TLBStore)) { - if(!status.exl) { - if(is64BitAddressing) vectorOffset = 0x0080; - else vectorOffset = 0x0000; - } - } - - cause.copError = cop; - cause.exceptionCode = static_cast(code); - - if (!status.exl) { - if ((cause.branchDelay = regs.prevDelaySlot)) { - pc -= 4; + u16 vectorOffset = 0x0180; + if (tlbError == MISS && (code == ExceptionCode::TLBLoad || code == ExceptionCode::TLBStore)) { + if (!status.exl) { + if (is64BitAddressing) + vectorOffset = 0x0080; + else + vectorOffset = 0x0000; + } } - status.exl = true; - EPC = pc; - } + cause.copError = cop; + cause.exceptionCode = static_cast(code); - if (status.bev) { - panic("BEV bit set!"); - } + if (!status.exl) { + if ((cause.branchDelay = regs.prevDelaySlot)) { + pc -= 4; + } - regs.SetPC32(s32(0x80000000 + vectorOffset)); - Update(); + status.exl = true; + EPC = pc; + } + + if (status.bev) { + panic("BEV bit set!"); + } + + regs.SetPC32(s32(0x80000000 + vectorOffset)); + Update(); } void Cop0::HandleTLBException(const u64 vaddr) { - const u64 vpn2 = vaddr >> 13 & 0x7FFFF; - const u64 xvpn2 = vaddr >> 13 & 0x7FFFFFF; - badVaddr = vaddr; - context.badvpn2 = vpn2; - xcontext.badvpn2 = xvpn2; - xcontext.r = vaddr >> 62 & 3; - entryHi.vpn2 = xvpn2; - entryHi.r = vaddr >> 62 & 3; + const u64 vpn2 = vaddr >> 13 & 0x7FFFF; + const u64 xvpn2 = vaddr >> 13 & 0x7FFFFFF; + badVaddr = vaddr; + context.badvpn2 = vpn2; + xcontext.badvpn2 = xvpn2; + xcontext.r = vaddr >> 62 & 3; + entryHi.vpn2 = xvpn2; + entryHi.r = vaddr >> 62 & 3; } -ExceptionCode Cop0::GetTLBExceptionCode(const TLBError error, const TLBAccessType accessType) { - switch (error) { - case NONE: - panic("Getting TLB exception with error NONE"); - case INVALID: - case MISS: - return accessType == LOAD ? ExceptionCode::TLBLoad : ExceptionCode::TLBStore; - case MODIFICATION: - return ExceptionCode::TLBModification; - case DISALLOWED_ADDRESS: - return accessType == LOAD ? ExceptionCode::AddressErrorLoad : ExceptionCode::AddressErrorStore; - default: - panic("Getting TLB exception for unknown error code! ({})", static_cast(error)); - return {}; - } +Cop0::ExceptionCode Cop0::GetTLBExceptionCode(const TLBError error, const TLBAccessType accessType) { + switch (error) { + case NONE: + panic("Getting TLB exception with error NONE"); + case INVALID: + case MISS: + return accessType == LOAD ? ExceptionCode::TLBLoad : ExceptionCode::TLBStore; + case MODIFICATION: + return ExceptionCode::TLBModification; + case DISALLOWED_ADDRESS: + return accessType == LOAD ? ExceptionCode::AddressErrorLoad : ExceptionCode::AddressErrorStore; + default: + panic("Getting TLB exception for unknown error code! ({})", static_cast(error)); + return {}; + } } void Cop0::decode(const Instruction instr) { - Registers& regs = Core::GetRegs(); - switch (instr.cop_rs()) { - case 0x00: mfc0(instr); break; - case 0x01: dmfc0(instr); break; - case 0x04: mtc0(instr); break; - case 0x05: dmtc0(instr); break; + Registers ®s = Core::GetRegs(); + switch (instr.cop_rs()) { + case 0x00: + mfc0(instr); + break; + case 0x01: + dmfc0(instr); + break; + case 0x04: + mtc0(instr); + break; + case 0x05: + dmtc0(instr); + break; case 0x10 ... 0x1F: - switch (instr.cop_funct()) { - case 0x01: tlbr(); break; - case 0x02: tlbw(index.i); break; - case 0x06: tlbw(GetRandom()); break; - case 0x08: tlbp(); break; - case 0x18: eret(); break; + switch (instr.cop_funct()) { + case 0x01: + tlbr(); + break; + case 0x02: + tlbw(index.i); + break; + case 0x06: + tlbw(GetRandom()); + break; + case 0x08: + tlbp(); + break; + case 0x18: + eret(); + break; default: - panic("Unimplemented COP0 function {} ({:08X}) ({:016X})", instr.cop_funct(), u32(instr), - regs.oldPC); - } - break; + panic("Unimplemented COP0 function {} ({:08X}) ({:016X})", instr.cop_funct(), u32(instr), regs.oldPC); + } + break; default: - panic("Unimplemented COP0 instruction {}", instr.cop_rs()); - } + panic("Unimplemented COP0 instruction {}", instr.cop_rs()); + } } template <> bool Cop0::MapVirtualAddress(const TLBAccessType accessType, const u64 vaddr, u32 &paddr) { - if(ircolib::IsInsideRange(vaddr, START_VREGION_KUSEG, END_VREGION_KUSEG)) - return ProbeTLB(accessType, s64(s32(vaddr)), paddr); - - tlbError = DISALLOWED_ADDRESS; - return false; + if (ircolib::IsInsideRange(vaddr, START_VREGION_KUSEG, END_VREGION_KUSEG)) + return ProbeTLB(accessType, s64(s32(vaddr)), paddr); + + tlbError = DISALLOWED_ADDRESS; + return false; } template <> bool Cop0::MapVirtualAddress(const TLBAccessType accessType, const u64 vaddr, u32 &paddr) { - u8 segment = static_cast(vaddr) >> 29 & 7; - if(ircolib::IsInsideRange(segment, 0, 3) || segment == 7) - return ProbeTLB(accessType, static_cast(vaddr), paddr); - - if(ircolib::IsInsideRange(segment, 4, 5)) { - paddr = vaddr & 0x1FFFFFFF; - return true; - } + u8 segment = static_cast(vaddr) >> 29 & 7; + if (ircolib::IsInsideRange(segment, 0, 3) || segment == 7) + return ProbeTLB(accessType, static_cast(vaddr), paddr); - if(segment == 6) - panic("Unimplemented virtual mapping in KSSEG! ({:08X})", vaddr); + if (ircolib::IsInsideRange(segment, 4, 5)) { + paddr = vaddr & 0x1FFFFFFF; + return true; + } - panic("Should never end up in base case in MapVirtualAddress! ({:08X})", vaddr); + if (segment == 6) + panic("Unimplemented virtual mapping in KSSEG! ({:08X})", vaddr); - return false; + panic("Should never end up in base case in MapVirtualAddress! ({:08X})", vaddr); + + return false; } template <> bool Cop0::MapVirtualAddress(const TLBAccessType accessType, const u64 vaddr, u32 &paddr) { - if(ircolib::IsInsideRange(vaddr, 0x0000000000000000, 0x000000FFFFFFFFFF)) - return ProbeTLB(accessType, vaddr, paddr); - - tlbError = DISALLOWED_ADDRESS; - return false; + if (ircolib::IsInsideRange(vaddr, 0x0000000000000000, 0x000000FFFFFFFFFF)) + return ProbeTLB(accessType, vaddr, paddr); + + tlbError = DISALLOWED_ADDRESS; + return false; } template <> bool Cop0::MapVirtualAddress(const TLBAccessType accessType, const u64 vaddr, u32 &paddr) { - if(ircolib::IsInsideRange(vaddr, 0x0000000000000000, 0x000000FFFFFFFFFF) || // VREGION_XKUSEG - ircolib::IsInsideRange(vaddr, 0x4000000000000000, 0x400000FFFFFFFFFF) || // VREGION_XKSSEG - ircolib::IsInsideRange(vaddr, 0xC000000000000000, 0xC00000FF7FFFFFFF) || // VREGION_XKSEG - ircolib::IsInsideRange(vaddr, 0xFFFFFFFFE0000000, 0xFFFFFFFFFFFFFFFF)) // VREGION_CKSEG3 - return ProbeTLB(accessType, vaddr, paddr); - - if(ircolib::IsInsideRange(vaddr, 0x8000000000000000, 0xBFFFFFFFFFFFFFFF)) { // VREGION_XKPHYS - if (!kernelMode) - panic("Access to XKPHYS address 0x{:016X} when outside kernel mode!", vaddr); - - const u8 high_two_bits = (vaddr >> 62) & 0b11; - if (high_two_bits != 0b10) - panic("Access to XKPHYS address 0x{:016X} with high two bits != 0b10!", vaddr); + if (ircolib::IsInsideRange(vaddr, 0x0000000000000000, 0x000000FFFFFFFFFF) || // VREGION_XKUSEG + ircolib::IsInsideRange(vaddr, 0x4000000000000000, 0x400000FFFFFFFFFF) || // VREGION_XKSSEG + ircolib::IsInsideRange(vaddr, 0xC000000000000000, 0xC00000FF7FFFFFFF) || // VREGION_XKSEG + ircolib::IsInsideRange(vaddr, 0xFFFFFFFFE0000000, 0xFFFFFFFFFFFFFFFF)) // VREGION_CKSEG3 + return ProbeTLB(accessType, vaddr, paddr); - const u8 subsegment = (vaddr >> 59) & 0b11; - bool cached = subsegment != 2; // do something with this eventually - // If any bits in the range of 58:32 are set, the address is invalid. - const bool valid = (vaddr & 0x07FFFFFF00000000) == 0; - if (!valid) { - tlbError = DISALLOWED_ADDRESS; - return false; + if (ircolib::IsInsideRange(vaddr, 0x8000000000000000, 0xBFFFFFFFFFFFFFFF)) { // VREGION_XKPHYS + if (!kernelMode) + panic("Access to XKPHYS address 0x{:016X} when outside kernel mode!", vaddr); + + const u8 high_two_bits = (vaddr >> 62) & 0b11; + if (high_two_bits != 0b10) + panic("Access to XKPHYS address 0x{:016X} with high two bits != 0b10!", vaddr); + + const u8 subsegment = (vaddr >> 59) & 0b11; + bool cached = subsegment != 2; // do something with this eventually + // If any bits in the range of 58:32 are set, the address is invalid. + const bool valid = (vaddr & 0x07FFFFFF00000000) == 0; + if (!valid) { + tlbError = DISALLOWED_ADDRESS; + return false; + } + + paddr = vaddr & 0xFFFFFFFF; + return true; } - paddr = vaddr & 0xFFFFFFFF; - return true; - } + if (ircolib::IsInsideRange(vaddr, 0xFFFFFFFF80000000, 0xFFFFFFFF9FFFFFFF) || // VREGION_CKSEG0 + ircolib::IsInsideRange(vaddr, 0xFFFFFFFFA0000000, 0xFFFFFFFFBFFFFFFF)) { // VREGION_CKSEG1 + u32 cut = u32(vaddr) >> 28; + u32 num = cut == 0xA; + // Identical to ksegX in 32 bit mode. + // Unmapped translation. Subtract the base address of the space to get the physical address. + paddr = vaddr - (cut << 28); // Implies cutting off the high 32 bits + trace("CKSEG{}: Translated 0x{:016X} to 0x{:08X}", num, vaddr, paddr); + return true; + } - if(ircolib::IsInsideRange(vaddr, 0xFFFFFFFF80000000, 0xFFFFFFFF9FFFFFFF) || // VREGION_CKSEG0 - ircolib::IsInsideRange(vaddr, 0xFFFFFFFFA0000000, 0xFFFFFFFFBFFFFFFF)) { // VREGION_CKSEG1 - u32 cut = u32(vaddr) >> 28; - u32 num = cut == 0xA; - // Identical to ksegX in 32 bit mode. - // Unmapped translation. Subtract the base address of the space to get the physical address. - paddr = vaddr - (cut << 28); // Implies cutting off the high 32 bits - trace("CKSEG{}: Translated 0x{:016X} to 0x{:08X}", num, vaddr, paddr); - return true; - } + if (ircolib::IsInsideRange(vaddr, 0x0000010000000000, 0x3FFFFFFFFFFFFFFF) || // VREGION_XBAD1 + ircolib::IsInsideRange(vaddr, 0x4000010000000000, 0x7FFFFFFFFFFFFFFF) || // VREGION_XBAD2 + ircolib::IsInsideRange(vaddr, 0xC00000FF80000000, 0xFFFFFFFF7FFFFFFF)) { // VREGION_XBAD3 + tlbError = DISALLOWED_ADDRESS; + return false; + } - if(ircolib::IsInsideRange(vaddr, 0x0000010000000000, 0x3FFFFFFFFFFFFFFF) || // VREGION_XBAD1 - ircolib::IsInsideRange(vaddr, 0x4000010000000000, 0x7FFFFFFFFFFFFFFF) || // VREGION_XBAD2 - ircolib::IsInsideRange(vaddr, 0xC00000FF80000000, 0xFFFFFFFF7FFFFFFF)) { // VREGION_XBAD3 - tlbError = DISALLOWED_ADDRESS; - return false; - } - - panic("Resolving virtual address 0x{:016X} in 64 bit mode", vaddr); - return false; // just to silence warning + panic("Resolving virtual address 0x{:016X} in 64 bit mode", vaddr); + return false; // just to silence warning } -bool Cop0::MapVAddr(const TLBAccessType accessType, const u64 vaddr, u32 &paddr) { - if(supervisorMode) - panic("Supervisor mode memory access"); +bool Cop0::MapVAddr(const TLBAccessType accessType, const u64 vaddr, u32 &paddr) { + if (supervisorMode) + panic("Supervisor mode memory access"); - if (is64BitAddressing) [[unlikely]] { - if (kernelMode) [[likely]] return MapVirtualAddress(accessType, vaddr, paddr); - if (userMode) return MapVirtualAddress(accessType, vaddr, paddr); + if (is64BitAddressing) [[unlikely]] { + if (kernelMode) [[likely]] + return MapVirtualAddress(accessType, vaddr, paddr); + if (userMode) + return MapVirtualAddress(accessType, vaddr, paddr); + + panic("Unknown mode! This should never happen!"); + } + + if (kernelMode) [[likely]] + return MapVirtualAddress(accessType, vaddr, paddr); + if (userMode) + return MapVirtualAddress(accessType, vaddr, paddr); panic("Unknown mode! This should never happen!"); - } - - if (kernelMode) [[likely]] return MapVirtualAddress(accessType, vaddr, paddr); - if (userMode) return MapVirtualAddress(accessType, vaddr, paddr); - - panic("Unknown mode! This should never happen!"); } } // namespace n64 diff --git a/src/backend/core/registers/Cop0.hpp b/src/backend/core/registers/Cop0.hpp index 915c0c5..02b9db3 100644 --- a/src/backend/core/registers/Cop0.hpp +++ b/src/backend/core/registers/Cop0.hpp @@ -38,248 +38,259 @@ namespace n64 { #define ENTRY_HI_MASK 0xC00000FFFFFFE0FF #define PAGEMASK_MASK 0x1FFE000 -union Cop0Cause { - u32 raw; - struct { - unsigned : 8; - unsigned interruptPending : 8; - unsigned : 16; - } __attribute__((__packed__)); - struct { - unsigned : 2; - unsigned exceptionCode : 5; - unsigned : 1; - unsigned ip0 : 1; - unsigned ip1 : 1; - unsigned ip2 : 1; - unsigned ip3 : 1; - unsigned ip4 : 1; - unsigned ip5 : 1; - unsigned ip6 : 1; - unsigned ip7 : 1; - unsigned : 12; - unsigned copError : 2; - unsigned : 1; - unsigned branchDelay : 1; - } __attribute__((__packed__)); -}; - -union Cop0Status { - struct { - unsigned ie : 1; - unsigned exl : 1; - unsigned erl : 1; - unsigned ksu : 2; - unsigned ux : 1; - unsigned sx : 1; - unsigned kx : 1; - unsigned im : 8; - unsigned ds : 9; - unsigned re : 1; - unsigned fr : 1; - unsigned rp : 1; - unsigned cu0 : 1; - unsigned cu1 : 1; - unsigned cu2 : 1; - unsigned cu3 : 1; - } __attribute__((__packed__)); - struct { - unsigned : 16; - unsigned de : 1; - unsigned ce : 1; - unsigned ch : 1; - unsigned : 1; - unsigned sr : 1; - unsigned ts : 1; - unsigned bev : 1; - unsigned : 1; - unsigned its : 1; - unsigned : 7; - } __attribute__((__packed__)); - u32 raw; -} __attribute__((__packed__)); - -union EntryLo { - u32 raw; - struct { - unsigned g : 1; - unsigned v : 1; - unsigned d : 1; - unsigned c : 3; - unsigned pfn : 20; - unsigned : 6; - }; -}; - -union EntryHi { - u64 raw; - struct { - u64 asid : 8; - u64 : 5; - u64 vpn2 : 27; - u64 fill : 22; - u64 r : 2; - } __attribute__((__packed__)); -}; - -union PageMask { - u32 raw; - struct { - unsigned : 13; - unsigned mask : 12; - unsigned : 7; - }; -}; - -union Index { - u32 raw; - struct { - unsigned i : 6; - unsigned : 25; - unsigned p : 1; - }; -}; - -struct TLBEntry { - bool initialized; - EntryLo entryLo0, entryLo1; - EntryHi entryHi; - PageMask pageMask; - - bool global; -}; - -enum TLBError : u8 { NONE, MISS, INVALID, MODIFICATION, DISALLOWED_ADDRESS }; - -enum class ExceptionCode : u8 { - 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 -}; - -union Cop0Context { - u64 raw; - struct { - u64 : 4; - u64 badvpn2 : 19; - u64 ptebase : 41; - }; -}; - -union Cop0XContext { - u64 raw; - struct { - u64 : 4; - u64 badvpn2 : 27; - u64 r : 2; - u64 ptebase : 31; - } __attribute__((__packed__)); -}; - struct Cop0 { - Cop0(); + union Cause { + u32 raw; + struct { + unsigned : 8; + unsigned interruptPending : 8; + unsigned : 16; + } __attribute__((__packed__)); + struct { + unsigned : 2; + unsigned exceptionCode : 5; + unsigned : 1; + unsigned ip0 : 1; + unsigned ip1 : 1; + unsigned ip2 : 1; + unsigned ip3 : 1; + unsigned ip4 : 1; + unsigned ip5 : 1; + unsigned ip6 : 1; + unsigned ip7 : 1; + unsigned : 12; + unsigned copError : 2; + unsigned : 1; + unsigned branchDelay : 1; + } __attribute__((__packed__)); + }; - bool kernelMode{true}; - bool supervisorMode{false}; - bool userMode{false}; - bool is64BitAddressing{false}; - bool llbit{}; - TLBError tlbError = NONE; + union Status { + struct { + unsigned ie : 1; + unsigned exl : 1; + unsigned erl : 1; + unsigned ksu : 2; + unsigned ux : 1; + unsigned sx : 1; + unsigned kx : 1; + unsigned im : 8; + unsigned ds : 9; + unsigned re : 1; + unsigned fr : 1; + unsigned rp : 1; + unsigned cu0 : 1; + unsigned cu1 : 1; + unsigned cu2 : 1; + unsigned cu3 : 1; + } __attribute__((__packed__)); + struct { + unsigned : 16; + unsigned de : 1; + unsigned ce : 1; + unsigned ch : 1; + unsigned : 1; + unsigned sr : 1; + unsigned ts : 1; + unsigned bev : 1; + unsigned : 1; + unsigned its : 1; + unsigned : 7; + } __attribute__((__packed__)); + u32 raw; + } __attribute__((__packed__)); - PageMask pageMask{}; - EntryHi entryHi{}; - EntryLo entryLo0{}, entryLo1{}; - Index index{}; - Cop0Context context{}; - u32 wired{}, r7{}; - u32 compare{}; - Cop0Status status{}; - Cop0Cause cause{}; - u32 PRId{}, Config{}, LLAddr{}, WatchLo{}, WatchHi{}; - u32 r21{}, r22{}, r23{}, r24{}, r25{}, ParityError{}, CacheError{}, TagLo{}, TagHi{}; - u32 r31{}; - Cop0XContext xcontext{}; - u64 badVaddr{}, count{}; - s64 EPC{}; - s64 ErrorEPC{}; - s64 openbus{}; - TLBEntry tlb[32]{}; - - enum TLBAccessType { LOAD, STORE }; + union EntryLo { + u32 raw; + struct { + unsigned g : 1; + unsigned v : 1; + unsigned d : 1; + unsigned c : 3; + unsigned pfn : 20; + unsigned : 6; + }; + }; - u32 GetReg32(u8); - [[nodiscard]] u64 GetReg64(u8) const; + union EntryHi { + u64 raw; + struct { + u64 asid : 8; + u64 : 5; + u64 vpn2 : 27; + u64 fill : 22; + u64 r : 2; + } __attribute__((__packed__)); + }; - void SetReg32(u8, u32); - void SetReg64(u8, u64); + union PageMask { + u32 raw; + struct { + unsigned : 13; + unsigned mask : 12; + unsigned : 7; + }; + }; - void Reset(); + union Index { + u32 raw; + struct { + unsigned i : 6; + unsigned : 25; + unsigned p : 1; + }; + }; - bool ProbeTLB(TLBAccessType accessType, u64 vaddr, u32 &paddr); - void FireException(ExceptionCode code, int cop, s64 pc); - bool MapVAddr(TLBAccessType accessType, u64 vaddr, u32 &paddr); + struct TLBEntry { + bool initialized; + EntryLo entryLo0, entryLo1; + EntryHi entryHi; + PageMask pageMask; - TLBEntry *TLBTryMatch(u64 vaddr, int &index); - TLBEntry *TLBTryMatch(u64 vaddr); - void HandleTLBException(u64 vaddr); - static ExceptionCode GetTLBExceptionCode(TLBError error, TLBAccessType accessType); - void decode(const Instruction); + bool global; + }; - [[nodiscard]] FORCE_INLINE u32 GetRandom() const { - u32 val = rand(); - const auto wired_ = GetWired(); - u32 lower, upper; - if (wired_ > 31) { - lower = 0; - upper = 64; - } else { - lower = wired_; - upper = 32 - wired_; + enum TLBError : u8 { NONE, MISS, INVALID, MODIFICATION, DISALLOWED_ADDRESS }; + + enum class ExceptionCode : u8 { + 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 + }; + + union Context { + u64 raw; + struct { + u64 : 4; + u64 badvpn2 : 19; + u64 ptebase : 41; + }; + }; + + union XContext { + u64 raw; + struct { + u64 : 4; + u64 badvpn2 : 27; + u64 r : 2; + u64 ptebase : 31; + } __attribute__((__packed__)); + }; + + union TagLo { + u32 raw; + struct { + u64 : 4; + u64 ptaglo : 20; + u64 pstate : 2; + u64 : 6; + } __attribute__((__packed__)); + }; + + Cop0(); + + bool kernelMode{true}; + bool supervisorMode{false}; + bool userMode{false}; + bool is64BitAddressing{false}; + bool llbit{}; + TLBError tlbError = NONE; + + TagLo tagLo; + PageMask pageMask{}; + EntryHi entryHi{}; + EntryLo entryLo0{}, entryLo1{}; + Index index{}; + Context context{}; + u32 wired{}, r7{}; + u32 compare{}; + Status status{}; + Cause cause{}; + u32 PRId{}, Config{}, LLAddr{}, WatchLo{}, WatchHi{}; + u32 r21{}, r22{}, r23{}, r24{}, r25{}, ParityError{}, CacheError{}, TagHi{}; + u32 r31{}; + XContext xcontext{}; + u64 badVaddr{}, count{}; + s64 EPC{}; + s64 ErrorEPC{}; + s64 openbus{}; + TLBEntry tlb[32]{}; + + enum TLBAccessType { LOAD, STORE }; + + u32 GetReg32(u8); + [[nodiscard]] u64 GetReg64(u8) const; + + void SetReg32(u8, u32); + void SetReg64(u8, u64); + + void Reset(); + + bool ProbeTLB(TLBAccessType accessType, u64 vaddr, u32 &paddr); + void FireException(ExceptionCode code, int cop, s64 pc); + bool MapVAddr(TLBAccessType accessType, u64 vaddr, u32 &paddr); + + TLBEntry *TLBTryMatch(u64 vaddr, int &index); + TLBEntry *TLBTryMatch(u64 vaddr); + void HandleTLBException(u64 vaddr); + static ExceptionCode GetTLBExceptionCode(TLBError error, TLBAccessType accessType); + void decode(const Instruction); + + [[nodiscard]] FORCE_INLINE u32 GetRandom() const { + u32 val = rand(); + const auto wired_ = GetWired(); + u32 lower, upper; + if (wired_ > 31) { + lower = 0; + upper = 64; + } else { + lower = wired_; + upper = 32 - wired_; + } + + val = (val % upper) + lower; + return val; } - val = (val % upper) + lower; - return val; - } + FORCE_INLINE void Update() { + const bool exception = status.exl || status.erl; - FORCE_INLINE void Update() { - const bool exception = status.exl || status.erl; + kernelMode = exception || status.ksu == 0; + supervisorMode = !exception && status.ksu == 1; + userMode = !exception && status.ksu == 2; + is64BitAddressing = (kernelMode && status.kx) || (supervisorMode && status.sx) || (userMode && status.ux); + } - kernelMode = exception || status.ksu == 0; - supervisorMode = !exception && status.ksu == 1; - userMode = !exception && status.ksu == 2; - is64BitAddressing = (kernelMode && status.kx) || (supervisorMode && status.sx) || (userMode && status.ux); - } + private: + friend struct JIT; -private: - friend struct JIT; + [[nodiscard]] FORCE_INLINE u32 GetWired() const { return wired & 0x3F; } + [[nodiscard]] FORCE_INLINE u32 GetCount() const { return u32(u64(count >> 1)); } - [[nodiscard]] FORCE_INLINE u32 GetWired() const { return wired & 0x3F; } - [[nodiscard]] FORCE_INLINE u32 GetCount() const { return u32(u64(count >> 1)); } + void mtc0(const Instruction); + void dmtc0(const Instruction); + void mfc0(const Instruction); + void dmfc0(const Instruction) const; + void eret(); - void mtc0(const Instruction); - void dmtc0(const Instruction); - void mfc0(const Instruction); - void dmfc0(const Instruction) const; - void eret(); + void tlbr(); + void tlbw(int); + void tlbp(); - void tlbr(); - void tlbw(int); - void tlbp(); - - template - bool MapVirtualAddress(TLBAccessType accessType, u64 vaddr, u32 &paddr); + template + bool MapVirtualAddress(TLBAccessType accessType, u64 vaddr, u32 &paddr); }; } // namespace n64 diff --git a/src/backend/core/registers/Cop1.hpp b/src/backend/core/registers/Cop1.hpp index e125099..a79d4a7 100644 --- a/src/backend/core/registers/Cop1.hpp +++ b/src/backend/core/registers/Cop1.hpp @@ -7,241 +7,241 @@ namespace n64 { struct Cop1; union FCR31 { - FCR31() = default; - struct { - unsigned rounding_mode : 2; + FCR31() = default; struct { - unsigned inexact_operation : 1; - unsigned underflow : 1; - unsigned overflow : 1; - unsigned division_by_zero : 1; - unsigned invalid_operation : 1; - } flag; - struct { - unsigned inexact_operation : 1; - unsigned underflow : 1; - unsigned overflow : 1; - unsigned division_by_zero : 1; - unsigned invalid_operation : 1; - } enable; - struct { - unsigned inexact_operation : 1; - unsigned underflow : 1; - unsigned overflow : 1; - unsigned division_by_zero : 1; - unsigned invalid_operation : 1; - unsigned unimplemented_operation : 1; - } cause; - unsigned : 5; - unsigned compare : 1; - unsigned fs : 1; - unsigned : 7; - } __attribute__((__packed__)); + unsigned rounding_mode : 2; + struct { + unsigned inexact_operation : 1; + unsigned underflow : 1; + unsigned overflow : 1; + unsigned division_by_zero : 1; + unsigned invalid_operation : 1; + } flag; + struct { + unsigned inexact_operation : 1; + unsigned underflow : 1; + unsigned overflow : 1; + unsigned division_by_zero : 1; + unsigned invalid_operation : 1; + } enable; + struct { + unsigned inexact_operation : 1; + unsigned underflow : 1; + unsigned overflow : 1; + unsigned division_by_zero : 1; + unsigned invalid_operation : 1; + unsigned unimplemented_operation : 1; + } cause; + unsigned : 5; + unsigned compare : 1; + unsigned fs : 1; + unsigned : 7; + } __attribute__((__packed__)); - [[nodiscard]] u32 read() const { - u32 ret = 0; - ret |= (u32(fs) << 24); - ret |= (u32(compare) << 23); - ret |= (u32(cause.unimplemented_operation) << 17); - ret |= (u32(cause.invalid_operation) << 16); - ret |= (u32(cause.division_by_zero) << 15); - ret |= (u32(cause.overflow) << 14); - ret |= (u32(cause.underflow) << 13); - ret |= (u32(cause.inexact_operation) << 12); - ret |= (u32(enable.invalid_operation) << 11); - ret |= (u32(enable.division_by_zero) << 10); - ret |= (u32(enable.overflow) << 9); - ret |= (u32(enable.underflow) << 8); - ret |= (u32(enable.inexact_operation) << 7); - ret |= (u32(flag.invalid_operation) << 6); - ret |= (u32(flag.division_by_zero) << 5); - ret |= (u32(flag.overflow) << 4); - ret |= (u32(flag.underflow) << 3); - ret |= (u32(flag.inexact_operation) << 2); - ret |= (u32(rounding_mode) & 3); + [[nodiscard]] u32 read() const { + u32 ret = 0; + ret |= (u32(fs) << 24); + ret |= (u32(compare) << 23); + ret |= (u32(cause.unimplemented_operation) << 17); + ret |= (u32(cause.invalid_operation) << 16); + ret |= (u32(cause.division_by_zero) << 15); + ret |= (u32(cause.overflow) << 14); + ret |= (u32(cause.underflow) << 13); + ret |= (u32(cause.inexact_operation) << 12); + ret |= (u32(enable.invalid_operation) << 11); + ret |= (u32(enable.division_by_zero) << 10); + ret |= (u32(enable.overflow) << 9); + ret |= (u32(enable.underflow) << 8); + ret |= (u32(enable.inexact_operation) << 7); + ret |= (u32(flag.invalid_operation) << 6); + ret |= (u32(flag.division_by_zero) << 5); + ret |= (u32(flag.overflow) << 4); + ret |= (u32(flag.underflow) << 3); + ret |= (u32(flag.inexact_operation) << 2); + ret |= (u32(rounding_mode) & 3); - return ret; - } + return ret; + } - void write(u32 val) { - fs = val >> 24; - compare = val >> 23; - cause.unimplemented_operation = val >> 17; - cause.invalid_operation = val >> 16; - cause.division_by_zero = val >> 15; - cause.overflow = val >> 14; - cause.underflow = val >> 13; - cause.inexact_operation = val >> 12; - enable.invalid_operation = val >> 11; - enable.division_by_zero = val >> 10; - enable.overflow = val >> 9; - enable.underflow = val >> 8; - enable.inexact_operation = val >> 7; - flag.invalid_operation = val >> 6; - flag.division_by_zero = val >> 5; - flag.overflow = val >> 4; - flag.underflow = val >> 3; - flag.inexact_operation = val >> 2; - rounding_mode = val & 3; - } + void write(u32 val) { + fs = val >> 24; + compare = val >> 23; + cause.unimplemented_operation = val >> 17; + cause.invalid_operation = val >> 16; + cause.division_by_zero = val >> 15; + cause.overflow = val >> 14; + cause.underflow = val >> 13; + cause.inexact_operation = val >> 12; + enable.invalid_operation = val >> 11; + enable.division_by_zero = val >> 10; + enable.overflow = val >> 9; + enable.underflow = val >> 8; + enable.inexact_operation = val >> 7; + flag.invalid_operation = val >> 6; + flag.division_by_zero = val >> 5; + flag.overflow = val >> 4; + flag.underflow = val >> 3; + flag.inexact_operation = val >> 2; + rounding_mode = val & 3; + } }; union FloatingPointReg { - struct { - s32 int32; - s32 int32h; - }; - struct { - u32 uint32; - u32 uint32h; - }; - struct { - s64 int64; - }; - struct { - u64 uint64; - }; - struct { - float float32; - float float32h; - }; - struct { - double float64; - }; + struct { + s32 int32; + s32 int32h; + }; + struct { + u32 uint32; + u32 uint32h; + }; + struct { + s64 int64; + }; + struct { + u64 uint64; + }; + struct { + float float32; + float float32h; + }; + struct { + double float64; + }; }; struct Cop1 { - explicit Cop1(); - bool fgrIsConstant[32]{}; - u32 fcr0{}; - FCR31 fcr31{}; - FloatingPointReg fgr[32]{}; + explicit Cop1(); + bool fgrIsConstant[32]{}; + u32 fcr0{}; + FCR31 fcr31{}; + FloatingPointReg fgr[32]{}; - void Reset(); - void decode(const Instruction); - friend struct Interpreter; - friend struct JIT; + void Reset(); + void decode(const Instruction); + friend struct Interpreter; + friend struct JIT; - template - bool CheckFPUUsable(); - template - bool CheckResult(T &); - template - bool CheckArg(T); - template - bool CheckArgs(T, T); - template - bool isqnan(T); + template + bool CheckFPUUsable(); + template + bool CheckResult(T &); + template + bool CheckArg(T); + template + bool CheckArgs(T, T); + template + bool isqnan(T); - template - bool XORDERED(T fs, T ft); + template + bool XORDERED(T fs, T ft); - template - bool CheckCVTArg(float f); - template - bool CheckCVTArg(double f); + template + bool CheckCVTArg(float f); + template + bool CheckCVTArg(double f); - template - bool TestExceptions(); - void SetCauseUnimplemented(); - bool SetCauseUnderflow(); - bool SetCauseInexact(); - bool SetCauseDivisionByZero(); - bool SetCauseOverflow(); - bool SetCauseInvalid(); + template + bool TestExceptions(); + void SetCauseUnimplemented(); + bool SetCauseUnderflow(); + bool SetCauseInexact(); + bool SetCauseDivisionByZero(); + bool SetCauseOverflow(); + bool SetCauseInvalid(); -private: - template - auto FGR_T(const Cop0Status &, u32) -> T &; - template - auto FGR_S(const Cop0Status &, u32) -> T &; - template - auto FGR_D(const Cop0Status &, u32) -> T &; - void absd(const Instruction instr); - void abss(const Instruction instr); - void adds(const Instruction instr); - void addd(const Instruction instr); - void subs(const Instruction instr); - void subd(const Instruction instr); - void ceills(const Instruction instr); - void ceilws(const Instruction instr); - void ceilld(const Instruction instr); - void ceilwd(const Instruction instr); - void cfc1(const Instruction instr); - void ctc1(const Instruction instr); - void unimplemented(); - void roundls(const Instruction instr); - void roundld(const Instruction instr); - void roundws(const Instruction instr); - void roundwd(const Instruction instr); - void floorls(const Instruction instr); - void floorld(const Instruction instr); - void floorws(const Instruction instr); - void floorwd(const Instruction instr); - void cvtls(const Instruction instr); - void cvtws(const Instruction instr); - void cvtds(const Instruction instr); - void cvtsw(const Instruction instr); - void cvtdw(const Instruction instr); - void cvtsd(const Instruction instr); - void cvtwd(const Instruction instr); - void cvtld(const Instruction instr); - void cvtdl(const Instruction instr); - void cvtsl(const Instruction instr); - template - void cf(const Instruction instr); - template - void cun(const Instruction instr); - template - void ceq(const Instruction instr); - template - void cueq(const Instruction instr); - template - void colt(const Instruction instr); - template - void cult(const Instruction instr); - template - void cole(const Instruction instr); - template - void cule(const Instruction instr); - template - void csf(const Instruction instr); - template - void cngle(const Instruction instr); - template - void cseq(const Instruction instr); - template - void cngl(const Instruction instr); - template - void clt(const Instruction instr); - template - void cnge(const Instruction instr); - template - void cle(const Instruction instr); - template - void cngt(const Instruction instr); - void divs(const Instruction instr); - void divd(const Instruction instr); - void muls(const Instruction instr); - void muld(const Instruction instr); - void movs(const Instruction instr); - void movd(const Instruction instr); - void negs(const Instruction instr); - void negd(const Instruction instr); - void sqrts(const Instruction instr); - void sqrtd(const Instruction instr); - void lwc1(const Instruction instr); - void swc1(const Instruction instr); - void ldc1(const Instruction instr); - void sdc1(const Instruction instr); + private: + template + auto FGR_T(const Cop0::Status &, u32) -> T &; + template + auto FGR_S(const Cop0::Status &, u32) -> T &; + template + auto FGR_D(const Cop0::Status &, u32) -> T &; + void absd(const Instruction instr); + void abss(const Instruction instr); + void adds(const Instruction instr); + void addd(const Instruction instr); + void subs(const Instruction instr); + void subd(const Instruction instr); + void ceills(const Instruction instr); + void ceilws(const Instruction instr); + void ceilld(const Instruction instr); + void ceilwd(const Instruction instr); + void cfc1(const Instruction instr); + void ctc1(const Instruction instr); + void unimplemented(); + void roundls(const Instruction instr); + void roundld(const Instruction instr); + void roundws(const Instruction instr); + void roundwd(const Instruction instr); + void floorls(const Instruction instr); + void floorld(const Instruction instr); + void floorws(const Instruction instr); + void floorwd(const Instruction instr); + void cvtls(const Instruction instr); + void cvtws(const Instruction instr); + void cvtds(const Instruction instr); + void cvtsw(const Instruction instr); + void cvtdw(const Instruction instr); + void cvtsd(const Instruction instr); + void cvtwd(const Instruction instr); + void cvtld(const Instruction instr); + void cvtdl(const Instruction instr); + void cvtsl(const Instruction instr); + template + void cf(const Instruction instr); + template + void cun(const Instruction instr); + template + void ceq(const Instruction instr); + template + void cueq(const Instruction instr); + template + void colt(const Instruction instr); + template + void cult(const Instruction instr); + template + void cole(const Instruction instr); + template + void cule(const Instruction instr); + template + void csf(const Instruction instr); + template + void cngle(const Instruction instr); + template + void cseq(const Instruction instr); + template + void cngl(const Instruction instr); + template + void clt(const Instruction instr); + template + void cnge(const Instruction instr); + template + void cle(const Instruction instr); + template + void cngt(const Instruction instr); + void divs(const Instruction instr); + void divd(const Instruction instr); + void muls(const Instruction instr); + void muld(const Instruction instr); + void movs(const Instruction instr); + void movd(const Instruction instr); + void negs(const Instruction instr); + void negd(const Instruction instr); + void sqrts(const Instruction instr); + void sqrtd(const Instruction instr); + void lwc1(const Instruction instr); + void swc1(const Instruction instr); + void ldc1(const Instruction instr); + void sdc1(const Instruction instr); - void mfc1(const Instruction instr); - void dmfc1(const Instruction instr); - void mtc1(const Instruction instr); - void dmtc1(const Instruction instr); - void truncws(const Instruction instr); - void truncwd(const Instruction instr); - void truncls(const Instruction instr); - void truncld(const Instruction instr); + void mfc1(const Instruction instr); + void dmfc1(const Instruction instr); + void mtc1(const Instruction instr); + void dmtc1(const Instruction instr); + void truncws(const Instruction instr); + void truncwd(const Instruction instr); + void truncls(const Instruction instr); + void truncld(const Instruction instr); }; } // namespace n64