diff --git a/.gitmodules b/.gitmodules index ded5d14b..aa478637 100644 --- a/.gitmodules +++ b/.gitmodules @@ -4,3 +4,7 @@ [submodule "external/nativefiledialog-extended"] path = external/nativefiledialog-extended url = https://github.com/btzy/nativefiledialog-extended +[submodule "external/capstone"] + path = external/capstone + url = https://github.com/capstone-engine/capstone/ + branch = next diff --git a/external/capstone b/external/capstone new file mode 160000 index 00000000..0d0e6843 --- /dev/null +++ b/external/capstone @@ -0,0 +1 @@ +Subproject commit 0d0e684358afd0889b98deaf3941caa9c6855cae diff --git a/external/parallel-rdp/CMakeLists.txt b/external/parallel-rdp/CMakeLists.txt index d3ec29d4..a7232cb8 100644 --- a/external/parallel-rdp/CMakeLists.txt +++ b/external/parallel-rdp/CMakeLists.txt @@ -71,7 +71,7 @@ target_include_directories(parallel-rdp PUBLIC ../imgui/imgui ../imgui/imgui/backends . - ) +) target_link_libraries(parallel-rdp SDL2main SDL2) diff --git a/src/frontend/App.cpp b/src/frontend/App.cpp index 29fa7463..42a0b90a 100644 --- a/src/frontend/App.cpp +++ b/src/frontend/App.cpp @@ -28,7 +28,7 @@ void App::Run() { case SDLK_o: { nfdchar_t* outpath; const nfdu8filteritem_t filter {"Nintendo 64 roms", "n64,z64,v64,N64,Z64,V64"}; - nfdresult_t result = NFD_OpenDialog(&outpath, &filter, 1, nullptr); + nfdresult_t result = NFD_OpenDialog(&outpath, &filter, 1, "/run/media/simuuz/HDD/n64_roms/tests"); if(result == NFD_OKAY) { core.LoadROM(outpath); NFD_FreePath(outpath); diff --git a/src/frontend/imgui/CMakeLists.txt b/src/frontend/imgui/CMakeLists.txt index 1651270b..402eb0e6 100644 --- a/src/frontend/imgui/CMakeLists.txt +++ b/src/frontend/imgui/CMakeLists.txt @@ -3,6 +3,7 @@ project(frontend-imgui) add_subdirectory(../../../external/imgui temp) add_subdirectory(../../../external/nativefiledialog-extended temp1) +SET(NFD_PORTAL ON CACHE BOOL "Use dbus for native file dialog instead of gtk") find_package(SDL2 REQUIRED) find_package(fmt REQUIRED) diff --git a/src/frontend/imgui/Window.cpp b/src/frontend/imgui/Window.cpp index 7b97caae..ecf73d25 100644 --- a/src/frontend/imgui/Window.cpp +++ b/src/frontend/imgui/Window.cpp @@ -147,7 +147,7 @@ void Window::Render(n64::Core& core) { if (ImGui::MenuItem("Open", "O")) { nfdchar_t *outpath; const nfdu8filteritem_t filter{"Nintendo 64 roms", "n64,z64,v64,N64,Z64,V64"}; - nfdresult_t result = NFD_OpenDialog(&outpath, &filter, 1, nullptr); + nfdresult_t result = NFD_OpenDialog(&outpath, &filter, 1, "/run/media/simuuz/HDD/n64_roms/tests"); if (result == NFD_OKAY) { core.LoadROM(outpath); NFD_FreePath(outpath); diff --git a/src/n64/Core.cpp b/src/n64/Core.cpp index 6704aa7c..a3bf3d0a 100644 --- a/src/n64/Core.cpp +++ b/src/n64/Core.cpp @@ -29,8 +29,10 @@ void Core::Reset() { void Core::LoadROM(const std::string& rom_) { rom = rom_; + cpu.Reset(); + mem.Reset(); mem.LoadROM(rom); - pause = true; + pause = false; romLoaded = true; } diff --git a/src/n64/core/CMakeLists.txt b/src/n64/core/CMakeLists.txt index 12557287..6bf69058 100644 --- a/src/n64/core/CMakeLists.txt +++ b/src/n64/core/CMakeLists.txt @@ -4,6 +4,7 @@ project(core) add_subdirectory(cpu) add_subdirectory(../../../external/parallel-rdp temp) +add_subdirectory(../../../external/capstone temp1) find_package(fmt REQUIRED) find_package(SDL2 REQUIRED) @@ -46,9 +47,10 @@ target_include_directories(core PUBLIC ../.. ../../frontend/imgui/debugger ../../../external + ../../../external/capstone ../../../external/imgui/imgui ../../../external/imgui/imgui/debugger ../../../external/imgui/imgui/backends mmio) -target_link_libraries(core PUBLIC SDL2main SDL2 fmt cpu parallel-rdp) +target_link_libraries(core PUBLIC capstone SDL2main SDL2 fmt cpu parallel-rdp) diff --git a/src/n64/core/Cpu.cpp b/src/n64/core/Cpu.cpp index 483cefb7..7d128a2f 100644 --- a/src/n64/core/Cpu.cpp +++ b/src/n64/core/Cpu.cpp @@ -23,7 +23,7 @@ inline void CheckCompareInterrupt(MI& mi, Registers& regs) { regs.cop0.count &= 0x1FFFFFFFF; if(regs.cop0.count == (u64)regs.cop0.compare << 1) { regs.cop0.cause.ip7 = 1; - //UpdateInterrupt(mi, regs); + UpdateInterrupt(mi, regs); } } @@ -41,7 +41,7 @@ void FireException(Registers& regs, ExceptionCode code, int cop, s64 pc) { bool old_exl = regs.cop0.status.exl; if(!regs.cop0.status.exl) { - if(regs.delaySlot) { // TODO: cached value of delay_slot should be used, but Namco Museum breaks! + if(regs.prevDelaySlot) { // TODO: cached value of delay_slot should be used, but Namco Museum breaks! regs.cop0.cause.branchDelay = true; pc -= 4; } else { @@ -66,15 +66,15 @@ void FireException(Registers& regs, ExceptionCode code, int cop, s64 pc) { case ReservedInstruction: case CoprocessorUnusable: case Overflow: case Trap: case FloatingPointError: case Watch: - regs.SetPC((s64)((s32)0x80000180)); + regs.SetPC(s64(s32(0x80000180))); break; case TLBLoad: case TLBStore: if(old_exl || regs.cop0.tlbError == INVALID) { - regs.SetPC((s64)((s32)0x80000180)); + regs.SetPC(s64(s32(0x80000180))); } else if(Is64BitAddressing(regs.cop0, regs.cop0.badVaddr)) { - regs.SetPC((s64)((s32)0x80000080)); + regs.SetPC(s64(s32(0x80000080))); } else { - regs.SetPC((s64)((s32)0x80000000)); + regs.SetPC(s64(s32(0x80000000))); } break; default: util::panic("Unhandled exception! {}\n", code); @@ -91,11 +91,11 @@ inline void HandleInterrupt(Registers& regs) { void Cpu::Step(Mem& mem) { regs.gpr[0] = 0; - CheckCompareInterrupt(mem.mmio.mi, regs); - regs.prevDelaySlot = regs.delaySlot; regs.delaySlot = false; + CheckCompareInterrupt(mem.mmio.mi, regs); + u32 instruction = mem.Read(regs, regs.pc, regs.pc); HandleInterrupt(regs); diff --git a/src/n64/core/Cpu.hpp b/src/n64/core/Cpu.hpp index 9397f505..2def49e4 100644 --- a/src/n64/core/Cpu.hpp +++ b/src/n64/core/Cpu.hpp @@ -1,6 +1,7 @@ #pragma once #include #include +#include namespace n64 { struct Cpu { @@ -121,5 +122,5 @@ enum ExceptionCode : u8 { Watch = 23 }; -void FireException(Registers& regs, ExceptionCode code, int cop, s64 pc); +void FireException(Registers& regs_, ExceptionCode code, int cop, s64 pc); } diff --git a/src/n64/core/Mem.cpp b/src/n64/core/Mem.cpp index ba6c07e9..b4784e25 100644 --- a/src/n64/core/Mem.cpp +++ b/src/n64/core/Mem.cpp @@ -180,6 +180,18 @@ void Mem::Write(Registers& regs, u32 vaddr, T val, s64 pc) { break; case 0x04040000 ... 0x040FFFFF: case 0x04100000 ... 0x041FFFFF: case 0x04300000 ... 0x044FFFFF: case 0x04500000 ... 0x048FFFFF: mmio.Write(*this, regs, paddr, val); break; + case 0x10000000 ... 0x13FF0013: break; + case 0x13FF0014: { + if(val < ISVIEWER_SIZE) { + char* message = (char*)calloc(val + 1, 1); + memcpy(message, isviewer, val); + printf("%s", message); + free(message); + } + } break; + case 0x13FF0020 ... 0x13FFFFFF: + util::WriteAccess(isviewer, paddr & ISVIEWER_DSIZE, be32toh(val)); + break; case 0x1FC007C0 ... 0x1FC007FF: if constexpr (sizeof(T) == 1) { util::WriteAccess(pifRam, BYTE_ADDRESS(paddr) & PIF_RAM_DSIZE, val); @@ -188,11 +200,12 @@ void Mem::Write(Registers& regs, u32 vaddr, T val, s64 pc) { } else { util::WriteAccess(pifRam, paddr & PIF_RAM_DSIZE, val); } + ProcessPIFCommands(pifRam, mmio.si.controller, *this); break; case 0x00800000 ... 0x03FFFFFF: case 0x04002000 ... 0x0403FFFF: - case 0x04200000 ... 0x042FFFFF: - case 0x04900000 ... 0x07FFFFFF: case 0x08000000 ... 0x0FFFFFFF: - case 0x80000000 ... 0xFFFFFFFF: case 0x1FC00800 ... 0x7FFFFFFF: break; + case 0x04200000 ... 0x042FFFFF: case 0x04900000 ... 0x07FFFFFF: + case 0x08000000 ... 0x0FFFFFFF: + case 0x1FC00800 ... 0x7FFFFFFF: case 0x80000000 ... 0xFFFFFFFF: break; default: util::panic("Unimplemented {}-bit write at address {:08X} with value {:0X} (PC = {:016X})\n", sizeof(T) * 8, paddr, val, regs.pc); } } diff --git a/src/n64/core/Mem.hpp b/src/n64/core/Mem.hpp index 9f1a638d..be09099f 100644 --- a/src/n64/core/Mem.hpp +++ b/src/n64/core/Mem.hpp @@ -29,6 +29,7 @@ private: MMIO mmio; std::vector cart, sram; u8 pifBootrom[PIF_BOOTROM_SIZE]{}; + u8 isviewer[ISVIEWER_SIZE]{}; size_t romMask; }; } diff --git a/src/n64/core/RSP.cpp b/src/n64/core/RSP.cpp index dda193d6..8670753d 100644 --- a/src/n64/core/RSP.cpp +++ b/src/n64/core/RSP.cpp @@ -31,12 +31,13 @@ void RSP::Reset() { void RSP::Step(MI& mi, Registers& regs, RDP& rdp) { if(!spStatus.halt) { - gpr[0] = 0; - u32 instr = util::ReadAccess(imem, pc & IMEM_DSIZE); - oldPC = pc & 0xFFF; - pc = nextPC & 0xFFF; - nextPC += 4; - Exec(mi, regs, rdp, instr); + util::panic("RSP!\n"); + //gpr[0] = 0; + //u32 instr = util::ReadAccess(imem, pc & IMEM_DSIZE); + //oldPC = pc & 0xFFF; + //pc = nextPC & 0xFFF; + //nextPC += 4; + //Exec(mi, regs, rdp, instr); } } diff --git a/src/n64/core/cpu/Registers.hpp b/src/n64/core/cpu/Registers.hpp index d427181e..32bfc424 100644 --- a/src/n64/core/cpu/Registers.hpp +++ b/src/n64/core/cpu/Registers.hpp @@ -14,4 +14,40 @@ struct Registers { s64 hi, lo; bool prevDelaySlot, delaySlot; }; + +constexpr char* gprStr[32] = { + "zero", // 0 + "at", // 1 + "v0", // 2 + "v1", // 3 + "a0", // 4 + "a1", // 5 + "a2", // 6 + "a3", // 7 + "t0", // 8 + "t1", // 9 + "t2", // 10 + "t3", // 11 + "t4", // 12 + "t5", // 13 + "t6", // 14 + "t7", // 15 + "s0", // 16 + "s1", // 17 + "s2", // 18 + "s3", // 19 + "s4", // 20 + "s5", // 21 + "s6", // 22 + "s7", // 23 + "t8", // 24 + "t9", // 25 + "k0", // 26 + "k1", // 27 + "gp", // 28 + "sp", // 29 + "s8", // 30 + "ra" // 31 +}; + } diff --git a/src/n64/core/cpu/decode.cpp b/src/n64/core/cpu/decode.cpp index 0217e889..e829f174 100644 --- a/src/n64/core/cpu/decode.cpp +++ b/src/n64/core/cpu/decode.cpp @@ -50,7 +50,12 @@ void Cpu::special(Mem& mem, u32 instr) { case 0x2D: daddu(instr); break; case 0x2E: dsub(instr); break; case 0x2F: dsubu(instr); break; + case 0x30: trap(regs.gpr[RS(instr)] >= regs.gpr[RT(instr)]); break; + case 0x31: trap((u64)regs.gpr[RS(instr)] >= (u64)regs.gpr[RT(instr)]); break; + case 0x32: trap(regs.gpr[RS(instr)] < regs.gpr[RT(instr)]); break; + case 0x33: trap((u64)regs.gpr[RS(instr)] < (u64)regs.gpr[RT(instr)]); break; case 0x34: trap(regs.gpr[RS(instr)] == regs.gpr[RT(instr)]); break; + case 0x36: trap(regs.gpr[RS(instr)] != regs.gpr[RT(instr)]); break; case 0x38: dsll(instr); break; case 0x3A: dsrl(instr); break; case 0x3B: dsra(instr); break; @@ -58,7 +63,7 @@ void Cpu::special(Mem& mem, u32 instr) { case 0x3E: dsrl32(instr); break; case 0x3F: dsra32(instr); break; default: - util::panic("Unimplemented special {} {} (pc: {:+016X})", (mask >> 3) & 7, mask & 7, regs.oldPC); + util::panic("Unimplemented special {} {} ({:08X}) (pc: {:016X})\n", (mask >> 3) & 7, mask & 7, instr, (u64)regs.oldPC); } } @@ -70,12 +75,18 @@ void Cpu::regimm(u32 instr) { case 0x01: b(instr, regs.gpr[RS(instr)] >= 0); break; case 0x02: bl(instr, regs.gpr[RS(instr)] < 0); break; case 0x03: bl(instr, regs.gpr[RS(instr)] >= 0); break; + case 0x08: trap(regs.gpr[RS(instr)] >= s64(s16(instr))); break; + case 0x09: trap(u64(regs.gpr[RS(instr)]) >= u64(s64(s16(instr)))); break; + case 0x0A: trap(regs.gpr[RS(instr)] < s64(s16(instr))); break; + case 0x0B: trap(u64(regs.gpr[RS(instr)]) < u64(s64(s16(instr)))); break; + case 0x0C: trap(regs.gpr[RS(instr)] == s64(s16(instr))); break; + case 0x0E: trap(regs.gpr[RS(instr)] != s64(s16(instr))); break; case 0x10: blink(instr, regs.gpr[RS(instr)] < 0); break; case 0x11: blink(instr, regs.gpr[RS(instr)] >= 0); break; case 0x12: bllink(instr, regs.gpr[RS(instr)] < 0); break; case 0x13: bllink(instr, regs.gpr[RS(instr)] >= 0); break; default: - util::panic("Unimplemented regimm {} {}", (mask >> 3) & 3, mask & 7); + util::panic("Unimplemented regimm {} {} ({:08X}) (pc: {:016X})\n", (mask >> 3) & 3, mask & 7, instr, (u64)regs.oldPC); } } @@ -137,7 +148,7 @@ void Cpu::Exec(Mem& mem, u32 instr) { case 0x3D: regs.cop1.sdc1(regs, mem, instr); break; case 0x3F: sd(mem, instr); break; default: - util::panic("Unimplemented instruction {} {}", (mask >> 3) & 7, mask & 7); + util::panic("Unimplemented instruction {} {} ({:08X}) (pc: {:016X})\n", (mask >> 3) & 7, mask & 7, instr, (u64)regs.oldPC); } } } \ No newline at end of file diff --git a/src/n64/core/cpu/instructions.cpp b/src/n64/core/cpu/instructions.cpp index 912bf1de..44c5ed05 100644 --- a/src/n64/core/cpu/instructions.cpp +++ b/src/n64/core/cpu/instructions.cpp @@ -137,7 +137,7 @@ void Cpu::branch_likely(bool cond, s64 address) { if (cond) { regs.nextPC = address; } else { - regs.SetPC(regs.nextPC); + regs.SetPC(regs.pc + 4); } } @@ -420,8 +420,8 @@ void Cpu::nor(u32 instr) { } void Cpu::j(u32 instr) { - u32 target = (instr & 0x3ffffff) << 2; - u32 address = (regs.oldPC & ~0xfffffff) | target; + s32 target = (instr & 0x3ffffff) << 2; + s32 address = (regs.oldPC & ~0xfffffff) | target; if ((address & 3) != 0) { HandleTLBException(regs, (s64)((s32)address)); FireException(regs, ExceptionCode::DataBusError, 0, regs.oldPC); diff --git a/src/n64/core/cpu/registers/Cop0.cpp b/src/n64/core/cpu/registers/Cop0.cpp index ddf53f2c..0d9721d3 100644 --- a/src/n64/core/cpu/registers/Cop0.cpp +++ b/src/n64/core/cpu/registers/Cop0.cpp @@ -10,115 +10,152 @@ Cop0::Cop0() { void Cop0::Reset() { cause.raw = 0xB000007C; - random = 0x0000001F; status.raw = 0x241000E0; - wired = 64; - index = 64; PRId = 0x00000B22; Config = 0x7006E463; EPC = 0xFFFFFFFFFFFFFFFF; ErrorEPC = 0xFFFFFFFFFFFFFFFF; } -template -T Cop0::GetReg(u8 addr) { +u32 Cop0::GetReg32(u8 addr) { switch(addr) { - case 0: return index & 0x8000001F; - case 1: return random & 0x1F; - case 2: return entryLo0.raw & 0x3FFFFFFF; - case 3: return entryLo1.raw & 0x3FFFFFFF; - case 4: return context.raw; - case 5: return pageMask.raw; - case 6: return wired & 0x3F; - case 7: return r7; - case 8: return badVaddr; - case 9: return count >> 1; - case 10: return entryHi.raw & 0xFFFFFFFFFFFFE0FF; - case 11: return compare; - case 12: return status.raw & STATUS_MASK; - case 13: return cause.raw; - case 14: return EPC; - case 15: return PRId & 0xFFFF; - case 16: return Config; - case 17: return LLAddr; - case 18: return WatchLo; - case 19: return WatchHi; - case 20: return xcontext.raw & 0xFFFFFFF0; - case 21: return r21; - case 22: return r22; - case 23: return r23; - case 24: return r24; - case 25: return r25; - case 26: return ParityError; - case 27: return CacheError; - case 28: return TagLo & 0xFFFFFFF; - case 29: return TagHi; - case 30: return ErrorEPC; - case 31: return r31; + case COP0_REG_INDEX: return index & 0x8000003F; + 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 0; default: util::panic("Unsupported word read from COP0 register {}\n", index); } } -template u32 Cop0::GetReg(u8 addr); -template u64 Cop0::GetReg(u8 addr); -template s32 Cop0::GetReg(u8 addr); -template s64 Cop0::GetReg(u8 addr); - -template -void Cop0::SetReg(u8 addr, T value) { +u64 Cop0::GetReg64(u8 addr) { switch(addr) { - case 0: index = value & 0x8000001F; break; - case 1: random = value & 0x1F; break; - case 2: entryLo0.raw = value & 0x3FFFFFFF; break; - case 3: entryLo1.raw = value & 0x3FFFFFFF; break; - case 4: context.raw = value; break; - case 5: pageMask.raw = value; break; - case 6: wired = value & 0x3F; break; - case 7: r7 = value; break; - case 9: count = value << 1; break; - case 10: entryHi.raw = value & 0xFFFFFFFFFFFFE0FF; break; - case 11: { - cause.ip7 = 0; - compare = value; - } break; - case 12: - status.raw &= ~STATUS_MASK; - status.raw |= value & STATUS_MASK; + case 2: return entryLo0.raw; + case 3: return entryLo1.raw; + case 4: return context.raw; + case 8: return badVaddr; + case 10: return entryHi.raw; + case 12: return status.raw; + case 14: return EPC; + case 15: return PRId; + case 17: return LLAddr; + case 20: return xcontext.raw & 0xFFFFFFFFFFFFFFF0; + case 30: return ErrorEPC; + default: + util::panic("Unsupported word read from COP0 register {}\n", index); + } +} + +void Cop0::SetReg32(u8 addr, u32 value) { + switch(addr) { + case COP0_REG_INDEX: index = value; break; + case COP0_REG_RANDOM: break; + case COP0_REG_ENTRYLO0: + entryLo0.raw = value & ENTRY_LO_MASK; break; - case 13: { + 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); + break; + case COP0_REG_CAUSE: { + Cop0Cause temp{}; + temp.raw = value; + cause.ip0 = temp.ip0; + cause.ip1 = temp.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 = value; break; + case COP0_REG_TAGHI: TagHi = value; break; + case COP0_REG_ERROREPC: ErrorEPC = s64(s32(value)); break; + case 7: case 21: case 22: break; + case 23: case 24: case 25: break; + case 31: break; + default: + util::panic("Unsupported word read from COP0 register {}\n", index); + } +} + +void Cop0::SetReg64(u8 addr, u64 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 = (((s64)(s32)value) & 0xFFFFFFFFFF800000) | (context.raw & 0x7FFFFF); + break; + case COP0_REG_XCONTEXT: + context.raw = (((s64)(s32)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; } break; - case 14: EPC = value; break; - case 15: PRId = value & 0xFFFF; break; - case 16: Config = value; break; - case 17: LLAddr = value; break; - case 18: WatchLo = value; break; - case 19: WatchHi = value; break; - case 21: r21 = value; break; - case 22: r22 = value; break; - case 23: r23 = value; break; - case 24: r24 = value; break; - case 25: r25 = value; break; - case 26: ParityError = value; break; - case 27: CacheError = value; break; - case 28: TagLo = value & 0xFFFFFFF; break; - case 29: TagHi = value; break; - case 30: ErrorEPC = value; break; - case 31: r31 = value; 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: - util::panic("Unsupported word write to COP0 register {}\n", index); + util::panic("Unsupported word write to COP0 register {}\n", addr); } } -template void Cop0::SetReg(u8 addr, u32 value); -template void Cop0::SetReg(u8 addr, u64 value); -template void Cop0::SetReg(u8 addr, s32 value); -template void Cop0::SetReg(u8 addr, s64 value); - #define vpn(addr, PageMask) (((((addr) & 0xFFFFFFFFFF) | (((addr) >> 22) & 0x30000000000)) & ~((PageMask) | 0x1FFF))) TLBEntry* TLBTryMatch(Registers& regs, u32 vaddr, int* match) { @@ -223,9 +260,7 @@ void Cop0::decode(Registers& regs, Mem& mem, u32 instr) { case 0x01: tlbr(regs); break; case 0x02: tlbwi(regs); break; case 0x08: tlbp(regs); break; - case 0x18: - eret(regs); - break; + case 0x18: eret(regs); break; default: util::panic("Unimplemented COP0 function {} {} ({:08X}) ({:016lX})", mask_cop2 >> 3, mask_cop2 & 7, instr, regs.oldPC); } break; diff --git a/src/n64/core/cpu/registers/Cop0.hpp b/src/n64/core/cpu/registers/Cop0.hpp index 6122af9b..23e41eb7 100644 --- a/src/n64/core/cpu/registers/Cop0.hpp +++ b/src/n64/core/cpu/registers/Cop0.hpp @@ -3,6 +3,36 @@ namespace n64 { #define STATUS_MASK 0xFF57FFFF +#define CONFIG_MASK 0x0F00800F +#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 Cpu; struct Registers; @@ -153,11 +183,11 @@ union Cop0XContext { struct Cop0 { Cop0(); - template - T GetReg(u8); + u32 GetReg32(u8); + u64 GetReg64(u8); - template - void SetReg(u8, T); + void SetReg32(u8, u32); + void SetReg64(u8, u64); void Reset(); @@ -170,23 +200,41 @@ struct Cop0 { PageMask pageMask{}; EntryHi entryHi{}; EntryLo entryLo0{}, entryLo1{}; - u32 index, random; + u32 index; Cop0Context context{}; u32 wired, r7{}; u64 badVaddr{}, count{}; u32 compare{}; Cop0Status status{}; Cop0Cause cause{}; - u64 EPC; + s64 EPC; u32 PRId, Config, LLAddr{}, WatchLo{}, WatchHi{}; Cop0XContext xcontext{}; u32 r21{}, r22{}, r23{}, r24{}, r25{}, ParityError{}, CacheError{}, TagLo{}, TagHi{}; - u64 ErrorEPC; + s64 ErrorEPC; u32 r31{}; TLBEntry tlb[32]{}; TLBError tlbError = NONE; void decode(Registers&, Mem&, u32); private: + inline u32 GetWired() { return wired & 0x3F; } + inline u32 GetCount() { return u32(u64(count >> 1)); } + inline u32 GetRandom() { + int val = rand(); + int wired = GetWired(); + int lower, upper; + if(wired > 31) { + lower = 0; + upper = 64; + } else { + lower = wired; + upper = 32 - wired; + } + + val = (val % upper) + lower; + return val; + } + void mtc0(Registers&, u32); void dmtc0(Registers&, u32); void mfc0(Registers&, u32); diff --git a/src/n64/core/cpu/registers/cop0instructions.cpp b/src/n64/core/cpu/registers/cop0instructions.cpp index d4ca147b..49dd5a1f 100644 --- a/src/n64/core/cpu/registers/cop0instructions.cpp +++ b/src/n64/core/cpu/registers/cop0instructions.cpp @@ -4,27 +4,27 @@ namespace n64 { void Cop0::mtc0(Registers& regs, u32 instr) { - SetReg(RD(instr), regs.gpr[RT(instr)]); + SetReg32(RD(instr), regs.gpr[RT(instr)]); } void Cop0::dmtc0(Registers& regs, u32 instr) { - SetReg(RD(instr), regs.gpr[RT(instr)]); + SetReg64(RD(instr), regs.gpr[RT(instr)]); } void Cop0::mfc0(Registers& regs, u32 instr) { - regs.gpr[RT(instr)] = GetReg(RD(instr)); + regs.gpr[RT(instr)] = s32(GetReg32(RD(instr))); } void Cop0::dmfc0(Registers& regs, u32 instr) { - regs.gpr[RT(instr)] = GetReg(RD(instr)); + regs.gpr[RT(instr)] = s64(GetReg64(RD(instr))); } void Cop0::eret(Registers& regs) { if(status.erl) { - regs.SetPC((s64)ErrorEPC); + regs.SetPC(ErrorEPC); status.erl = false; } else { - regs.SetPC((s64)EPC); + regs.SetPC(EPC); status.exl = false; } llbit = false; diff --git a/src/n64/core/cpu/registers/cop1instructions.cpp b/src/n64/core/cpu/registers/cop1instructions.cpp index 4f619d60..c605a202 100644 --- a/src/n64/core/cpu/registers/cop1instructions.cpp +++ b/src/n64/core/cpu/registers/cop1instructions.cpp @@ -2,7 +2,7 @@ #include #include #include -#include +#include namespace n64 { void Cop1::absd(Registers& regs, u32 instr) { @@ -202,7 +202,7 @@ void Cop1::ccondd(Registers& regs, u32 instr, CompConds cond) { double ft = GetCop1RegDouble(regs.cop0, FT(instr)); bool less, equal, unordered; - if(isnan(fs) || isnan(ft)) { + if(std::isnan(fs) || std::isnan(ft)) { less = false; equal = false; unordered = true; @@ -222,7 +222,7 @@ void Cop1::cconds(Registers& regs, u32 instr, CompConds cond) { float ft = GetCop1RegFloat(regs.cop0, FT(instr)); bool less, equal, unordered; - if(isnan(fs) || isnan(ft)) { + if(std::isnan(fs) || std::isnan(ft)) { less = false; equal = false; unordered = true; diff --git a/src/n64/core/mmio/PI.cpp b/src/n64/core/mmio/PI.cpp index 667b3e57..633b3b2d 100644 --- a/src/n64/core/mmio/PI.cpp +++ b/src/n64/core/mmio/PI.cpp @@ -59,7 +59,7 @@ void PI::Write(Mem& mem, Registers& regs, u32 addr, u32 val) { cartAddr = cart_addr + len; InterruptRaise(mi, regs, Interrupt::PI); status = status & 0xFFFFFFFE; - util::info("PI DMA from rdram to cart (size: {:.2f} MiB)\n", (float)len / 1048576); + util::logdebug("PI DMA from rdram to cart (size: {:.2f} MiB)\n", (float)len / 1048576); } break; case 0x0460000C: { u32 len = (val & 0x00FFFFFF) + 1; @@ -76,7 +76,7 @@ void PI::Write(Mem& mem, Registers& regs, u32 addr, u32 val) { cartAddr = cart_addr + len; InterruptRaise(mi, regs, Interrupt::PI); status = status & 0xFFFFFFFE; - util::info("PI DMA from cart to rdram (size: {:.2f} MiB)\n", (float)len / 1048576); + util::logdebug("PI DMA from cart to rdram (size: {:.2f} MiB)\n", (float)len / 1048576); } break; case 0x04600010: if(val & 2) { diff --git a/src/n64/memory_regions.hpp b/src/n64/memory_regions.hpp index 960e0356..20340fcf 100644 --- a/src/n64/memory_regions.hpp +++ b/src/n64/memory_regions.hpp @@ -12,6 +12,8 @@ #define PIF_RAM_DSIZE (PIF_RAM_SIZE - 1) #define PIF_BOOTROM_SIZE 0x7C0 #define PIF_BOOTROM_DSIZE (PIF_BOOTROM_SIZE - 1) +#define ISVIEWER_SIZE 0xFFDF +#define ISVIEWER_DSIZE (ISVIEWER_SIZE - 1) #define RDRAM_REGION 0 ... RDRAM_DSIZE #define DMEM_REGION 0x04000000 ... DMEM_DSIZE diff --git a/src/util.hpp b/src/util.hpp index 6e2cd14e..c3a385d3 100644 --- a/src/util.hpp +++ b/src/util.hpp @@ -7,6 +7,7 @@ #include #include #include +#include namespace util { using SteadyClock = std::chrono::steady_clock; @@ -141,16 +142,12 @@ inline void SwapN64Rom(size_t size, u8* data) { memcpy(&endianness, data, 4); endianness = static_cast(be32toh(endianness)); switch(endianness) { - case V64: { - u8* tmp = (u8*)calloc(size, 1); - for(int i = 0; i < size; i++) { - data[i] = tmp[i ^ 2]; - } - free(tmp); - } break; + case V64: + SwapBuffer32(size, data); + SwapBuffer16(size, data); + break; case N64: break; - case Z64: - SwapBuffer32(size, data); break; + case Z64: SwapBuffer32(size, data); break; default: panic("Unrecognized rom format! Make sure this is a valid Nintendo 64 ROM dump!\n"); } @@ -159,18 +156,22 @@ inline void SwapN64Rom(size_t size, u8* data) { template struct CircularBuffer { CircularBuffer() : head(0) { - memset(raw.data(), 0, size * sizeof(T)); + raw.fill(T()); } // make it scroll void PushValue(T val) { raw[head] = val; head++; - head &= mask; + if(head == size) { + head = 0; + } } T PopValue() { head--; - head &= mask; + if(head == 0) { + head = size - 1; + } return raw[head]; } @@ -179,11 +180,11 @@ struct CircularBuffer { } auto begin() { return raw.begin(); } auto end() { return raw.end(); } + auto fill(const T& u) { raw.fill(u); } size_t GetHead() { return head; } private: std::array raw; size_t head; - static constexpr size_t mask = size - 1; }; inline size_t NextPow2(size_t num) {