#pragma once #include #include "log.hpp" namespace n64 { #define STATUS_MASK 0xFF57FFFF #define CONFIG_MASK 0x0F00800F #define INDEX_MASK 0x8000003F #define COP0_REG_INDEX 0 #define COP0_REG_RANDOM 1 #define COP0_REG_ENTRYLO0 2 #define COP0_REG_ENTRYLO1 3 #define COP0_REG_CONTEXT 4 #define COP0_REG_PAGEMASK 5 #define COP0_REG_WIRED 6 #define COP0_REG_BADVADDR 8 #define COP0_REG_COUNT 9 #define COP0_REG_ENTRYHI 10 #define COP0_REG_COMPARE 11 #define COP0_REG_STATUS 12 #define COP0_REG_CAUSE 13 #define COP0_REG_EPC 14 #define COP0_REG_PRID 15 #define COP0_REG_CONFIG 16 #define COP0_REG_LLADDR 17 #define COP0_REG_WATCHLO 18 #define COP0_REG_WATCHHI 19 #define COP0_REG_XCONTEXT 20 #define COP0_REG_PARITY_ERR 26 #define COP0_REG_CACHE_ERR 27 #define COP0_REG_TAGLO 28 #define COP0_REG_TAGHI 29 #define COP0_REG_ERROREPC 30 #define ENTRY_LO_MASK 0x3FFFFFFF #define ENTRY_HI_MASK 0xC00000FFFFFFE0FF #define PAGEMASK_MASK 0x1FFE000 struct JIT; struct Interpreter; struct Registers; struct Mem; 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; union { u32 raw; struct { unsigned:1; unsigned v:1; unsigned d:1; unsigned c:3; unsigned pfn:20; unsigned:6; }; } 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 }; void FireException(Registers& regs, ExceptionCode code, int cop, s64 pc); 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(); u32 GetReg32(u8); [[nodiscard]] u64 GetReg64(u8) const; void SetReg32(u8, u32); void SetReg64(u8, u64); void Reset(); bool kernel_mode{true}; bool supervisor_mode{false}; bool user_mode{false}; bool is_64bit_addressing{false}; bool llbit{}; PageMask pageMask{}; EntryHi entryHi{}; EntryLo entryLo0{}, entryLo1{}; Index index{}; Cop0Context context{}; u32 wired{}, r7{}; u64 badVaddr{}, count{}; u32 compare{}; Cop0Status status{}; Cop0Cause cause{}; s64 EPC{}; u32 PRId{}, Config{}, LLAddr{}, WatchLo{}, WatchHi{}; Cop0XContext xcontext{}; u32 r21{}, r22{}, r23{}, r24{}, r25{}, ParityError{}, CacheError{}, TagLo{}, TagHi{}; s64 ErrorEPC{}; u32 r31{}; TLBEntry tlb[32]{}; TLBError tlbError = NONE; s64 openbus{}; template void decode(T&, u32); FORCE_INLINE u32 GetRandom() { u32 val = rand(); 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; } FORCE_INLINE void Update() { bool exception = status.exl || status.erl; kernel_mode = exception || status.ksu == 0; supervisor_mode = !exception && status.ksu == 1; user_mode = !exception && status.ksu == 2; is_64bit_addressing = (kernel_mode && status.kx) || (supervisor_mode && status.sx) || (user_mode && status.ux); } private: [[nodiscard]] FORCE_INLINE u32 GetWired() const { return wired & 0x3F; } [[nodiscard]] FORCE_INLINE u32 GetCount() const { return u32(u64(count >> 1)); } void decodeInterp(Registers&, u32); void decodeJIT(JIT&, u32); void mtc0(Registers&, u32); void dmtc0(Registers&, u32); void mfc0(Registers&, u32); void dmfc0(Registers&, u32) const; void eret(Registers&); void tlbr(); void tlbw(int); void tlbp(n64::Registers&); }; struct Registers; enum class ExceptionCode : u8; enum TLBAccessType { LOAD, STORE }; bool ProbeTLB(Registers& regs, TLBAccessType access_type, u64 vaddr, u32& paddr, int* match); bool MapVAddr(Registers& regs, TLBAccessType accessType, u64 vaddr, u32& paddr); bool UserMapVAddr32(Registers& regs, TLBAccessType accessType, u64 vaddr, u32& paddr); bool MapVAddr32(Registers& regs, TLBAccessType accessType, u64 vaddr, u32& paddr); bool UserMapVAddr64(Registers& regs, TLBAccessType accessType, u64 vaddr, u32& paddr); bool MapVAddr64(Registers& regs, TLBAccessType accessType, u64 vaddr, u32& paddr); TLBEntry* TLBTryMatch(Registers& regs, u64 vaddr, int* match); void HandleTLBException(Registers& regs, u64 vaddr); ExceptionCode GetTLBExceptionCode(TLBError error, TLBAccessType access_type); }