This commit is contained in:
CocoSimone
2022-09-07 20:32:34 +02:00
parent 87ad20b519
commit c15d881adb
23 changed files with 307 additions and 148 deletions

4
.gitmodules vendored
View File

@@ -4,3 +4,7 @@
[submodule "external/nativefiledialog-extended"] [submodule "external/nativefiledialog-extended"]
path = external/nativefiledialog-extended path = external/nativefiledialog-extended
url = https://github.com/btzy/nativefiledialog-extended url = https://github.com/btzy/nativefiledialog-extended
[submodule "external/capstone"]
path = external/capstone
url = https://github.com/capstone-engine/capstone/
branch = next

1
external/capstone vendored Submodule

Submodule external/capstone added at 0d0e684358

View File

@@ -71,7 +71,7 @@ target_include_directories(parallel-rdp PUBLIC
../imgui/imgui ../imgui/imgui
../imgui/imgui/backends ../imgui/imgui/backends
. .
) )
target_link_libraries(parallel-rdp SDL2main SDL2) target_link_libraries(parallel-rdp SDL2main SDL2)

View File

@@ -28,7 +28,7 @@ void App::Run() {
case SDLK_o: { case SDLK_o: {
nfdchar_t* outpath; nfdchar_t* outpath;
const nfdu8filteritem_t filter {"Nintendo 64 roms", "n64,z64,v64,N64,Z64,V64"}; 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) { if(result == NFD_OKAY) {
core.LoadROM(outpath); core.LoadROM(outpath);
NFD_FreePath(outpath); NFD_FreePath(outpath);

View File

@@ -3,6 +3,7 @@ project(frontend-imgui)
add_subdirectory(../../../external/imgui temp) add_subdirectory(../../../external/imgui temp)
add_subdirectory(../../../external/nativefiledialog-extended temp1) 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(SDL2 REQUIRED)
find_package(fmt REQUIRED) find_package(fmt REQUIRED)

View File

@@ -147,7 +147,7 @@ void Window::Render(n64::Core& core) {
if (ImGui::MenuItem("Open", "O")) { if (ImGui::MenuItem("Open", "O")) {
nfdchar_t *outpath; nfdchar_t *outpath;
const nfdu8filteritem_t filter{"Nintendo 64 roms", "n64,z64,v64,N64,Z64,V64"}; 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) { if (result == NFD_OKAY) {
core.LoadROM(outpath); core.LoadROM(outpath);
NFD_FreePath(outpath); NFD_FreePath(outpath);

View File

@@ -29,8 +29,10 @@ void Core::Reset() {
void Core::LoadROM(const std::string& rom_) { void Core::LoadROM(const std::string& rom_) {
rom = rom_; rom = rom_;
cpu.Reset();
mem.Reset();
mem.LoadROM(rom); mem.LoadROM(rom);
pause = true; pause = false;
romLoaded = true; romLoaded = true;
} }

View File

@@ -4,6 +4,7 @@ project(core)
add_subdirectory(cpu) add_subdirectory(cpu)
add_subdirectory(../../../external/parallel-rdp temp) add_subdirectory(../../../external/parallel-rdp temp)
add_subdirectory(../../../external/capstone temp1)
find_package(fmt REQUIRED) find_package(fmt REQUIRED)
find_package(SDL2 REQUIRED) find_package(SDL2 REQUIRED)
@@ -46,9 +47,10 @@ target_include_directories(core PUBLIC
../.. ../..
../../frontend/imgui/debugger ../../frontend/imgui/debugger
../../../external ../../../external
../../../external/capstone
../../../external/imgui/imgui ../../../external/imgui/imgui
../../../external/imgui/imgui/debugger ../../../external/imgui/imgui/debugger
../../../external/imgui/imgui/backends ../../../external/imgui/imgui/backends
mmio) mmio)
target_link_libraries(core PUBLIC SDL2main SDL2 fmt cpu parallel-rdp) target_link_libraries(core PUBLIC capstone SDL2main SDL2 fmt cpu parallel-rdp)

View File

@@ -23,7 +23,7 @@ inline void CheckCompareInterrupt(MI& mi, Registers& regs) {
regs.cop0.count &= 0x1FFFFFFFF; regs.cop0.count &= 0x1FFFFFFFF;
if(regs.cop0.count == (u64)regs.cop0.compare << 1) { if(regs.cop0.count == (u64)regs.cop0.compare << 1) {
regs.cop0.cause.ip7 = 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; bool old_exl = regs.cop0.status.exl;
if(!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; regs.cop0.cause.branchDelay = true;
pc -= 4; pc -= 4;
} else { } else {
@@ -66,15 +66,15 @@ void FireException(Registers& regs, ExceptionCode code, int cop, s64 pc) {
case ReservedInstruction: case CoprocessorUnusable: case ReservedInstruction: case CoprocessorUnusable:
case Overflow: case Trap: case Overflow: case Trap:
case FloatingPointError: case Watch: case FloatingPointError: case Watch:
regs.SetPC((s64)((s32)0x80000180)); regs.SetPC(s64(s32(0x80000180)));
break; break;
case TLBLoad: case TLBStore: case TLBLoad: case TLBStore:
if(old_exl || regs.cop0.tlbError == INVALID) { 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)) { } else if(Is64BitAddressing(regs.cop0, regs.cop0.badVaddr)) {
regs.SetPC((s64)((s32)0x80000080)); regs.SetPC(s64(s32(0x80000080)));
} else { } else {
regs.SetPC((s64)((s32)0x80000000)); regs.SetPC(s64(s32(0x80000000)));
} }
break; break;
default: util::panic("Unhandled exception! {}\n", code); default: util::panic("Unhandled exception! {}\n", code);
@@ -91,11 +91,11 @@ inline void HandleInterrupt(Registers& regs) {
void Cpu::Step(Mem& mem) { void Cpu::Step(Mem& mem) {
regs.gpr[0] = 0; regs.gpr[0] = 0;
CheckCompareInterrupt(mem.mmio.mi, regs);
regs.prevDelaySlot = regs.delaySlot; regs.prevDelaySlot = regs.delaySlot;
regs.delaySlot = false; regs.delaySlot = false;
CheckCompareInterrupt(mem.mmio.mi, regs);
u32 instruction = mem.Read<u32>(regs, regs.pc, regs.pc); u32 instruction = mem.Read<u32>(regs, regs.pc, regs.pc);
HandleInterrupt(regs); HandleInterrupt(regs);

View File

@@ -1,6 +1,7 @@
#pragma once #pragma once
#include <Registers.hpp> #include <Registers.hpp>
#include <Mem.hpp> #include <Mem.hpp>
#include <util.hpp>
namespace n64 { namespace n64 {
struct Cpu { struct Cpu {
@@ -121,5 +122,5 @@ enum ExceptionCode : u8 {
Watch = 23 Watch = 23
}; };
void FireException(Registers& regs, ExceptionCode code, int cop, s64 pc); void FireException(Registers& regs_, ExceptionCode code, int cop, s64 pc);
} }

View File

@@ -180,6 +180,18 @@ void Mem::Write(Registers& regs, u32 vaddr, T val, s64 pc) {
break; break;
case 0x04040000 ... 0x040FFFFF: case 0x04100000 ... 0x041FFFFF: case 0x04040000 ... 0x040FFFFF: case 0x04100000 ... 0x041FFFFF:
case 0x04300000 ... 0x044FFFFF: case 0x04500000 ... 0x048FFFFF: mmio.Write(*this, regs, paddr, val); break; 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<T>(isviewer, paddr & ISVIEWER_DSIZE, be32toh(val));
break;
case 0x1FC007C0 ... 0x1FC007FF: case 0x1FC007C0 ... 0x1FC007FF:
if constexpr (sizeof(T) == 1) { if constexpr (sizeof(T) == 1) {
util::WriteAccess<T>(pifRam, BYTE_ADDRESS(paddr) & PIF_RAM_DSIZE, val); util::WriteAccess<T>(pifRam, BYTE_ADDRESS(paddr) & PIF_RAM_DSIZE, val);
@@ -188,11 +200,12 @@ void Mem::Write(Registers& regs, u32 vaddr, T val, s64 pc) {
} else { } else {
util::WriteAccess<T>(pifRam, paddr & PIF_RAM_DSIZE, val); util::WriteAccess<T>(pifRam, paddr & PIF_RAM_DSIZE, val);
} }
ProcessPIFCommands(pifRam, mmio.si.controller, *this);
break; break;
case 0x00800000 ... 0x03FFFFFF: case 0x04002000 ... 0x0403FFFF: case 0x00800000 ... 0x03FFFFFF: case 0x04002000 ... 0x0403FFFF:
case 0x04200000 ... 0x042FFFFF: case 0x04200000 ... 0x042FFFFF: case 0x04900000 ... 0x07FFFFFF:
case 0x04900000 ... 0x07FFFFFF: case 0x08000000 ... 0x0FFFFFFF: case 0x08000000 ... 0x0FFFFFFF:
case 0x80000000 ... 0xFFFFFFFF: case 0x1FC00800 ... 0x7FFFFFFF: break; 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); default: util::panic("Unimplemented {}-bit write at address {:08X} with value {:0X} (PC = {:016X})\n", sizeof(T) * 8, paddr, val, regs.pc);
} }
} }

View File

@@ -29,6 +29,7 @@ private:
MMIO mmio; MMIO mmio;
std::vector<u8> cart, sram; std::vector<u8> cart, sram;
u8 pifBootrom[PIF_BOOTROM_SIZE]{}; u8 pifBootrom[PIF_BOOTROM_SIZE]{};
u8 isviewer[ISVIEWER_SIZE]{};
size_t romMask; size_t romMask;
}; };
} }

View File

@@ -31,12 +31,13 @@ void RSP::Reset() {
void RSP::Step(MI& mi, Registers& regs, RDP& rdp) { void RSP::Step(MI& mi, Registers& regs, RDP& rdp) {
if(!spStatus.halt) { if(!spStatus.halt) {
gpr[0] = 0; util::panic("RSP!\n");
u32 instr = util::ReadAccess<u32>(imem, pc & IMEM_DSIZE); //gpr[0] = 0;
oldPC = pc & 0xFFF; //u32 instr = util::ReadAccess<u32>(imem, pc & IMEM_DSIZE);
pc = nextPC & 0xFFF; //oldPC = pc & 0xFFF;
nextPC += 4; //pc = nextPC & 0xFFF;
Exec(mi, regs, rdp, instr); //nextPC += 4;
//Exec(mi, regs, rdp, instr);
} }
} }

View File

@@ -14,4 +14,40 @@ struct Registers {
s64 hi, lo; s64 hi, lo;
bool prevDelaySlot, delaySlot; 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
};
} }

View File

@@ -50,7 +50,12 @@ void Cpu::special(Mem& mem, u32 instr) {
case 0x2D: daddu(instr); break; case 0x2D: daddu(instr); break;
case 0x2E: dsub(instr); break; case 0x2E: dsub(instr); break;
case 0x2F: dsubu(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 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 0x38: dsll(instr); break;
case 0x3A: dsrl(instr); break; case 0x3A: dsrl(instr); break;
case 0x3B: dsra(instr); break; case 0x3B: dsra(instr); break;
@@ -58,7 +63,7 @@ void Cpu::special(Mem& mem, u32 instr) {
case 0x3E: dsrl32(instr); break; case 0x3E: dsrl32(instr); break;
case 0x3F: dsra32(instr); break; case 0x3F: dsra32(instr); break;
default: 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 0x01: b(instr, regs.gpr[RS(instr)] >= 0); break;
case 0x02: bl(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 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 0x10: blink(instr, regs.gpr[RS(instr)] < 0); break;
case 0x11: 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 0x12: bllink(instr, regs.gpr[RS(instr)] < 0); break;
case 0x13: bllink(instr, regs.gpr[RS(instr)] >= 0); break; case 0x13: bllink(instr, regs.gpr[RS(instr)] >= 0); break;
default: 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 0x3D: regs.cop1.sdc1(regs, mem, instr); break;
case 0x3F: sd(mem, instr); break; case 0x3F: sd(mem, instr); break;
default: 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);
} }
} }
} }

View File

@@ -137,7 +137,7 @@ void Cpu::branch_likely(bool cond, s64 address) {
if (cond) { if (cond) {
regs.nextPC = address; regs.nextPC = address;
} else { } else {
regs.SetPC(regs.nextPC); regs.SetPC(regs.pc + 4);
} }
} }
@@ -420,8 +420,8 @@ void Cpu::nor(u32 instr) {
} }
void Cpu::j(u32 instr) { void Cpu::j(u32 instr) {
u32 target = (instr & 0x3ffffff) << 2; s32 target = (instr & 0x3ffffff) << 2;
u32 address = (regs.oldPC & ~0xfffffff) | target; s32 address = (regs.oldPC & ~0xfffffff) | target;
if ((address & 3) != 0) { if ((address & 3) != 0) {
HandleTLBException(regs, (s64)((s32)address)); HandleTLBException(regs, (s64)((s32)address));
FireException(regs, ExceptionCode::DataBusError, 0, regs.oldPC); FireException(regs, ExceptionCode::DataBusError, 0, regs.oldPC);

View File

@@ -10,115 +10,152 @@ Cop0::Cop0() {
void Cop0::Reset() { void Cop0::Reset() {
cause.raw = 0xB000007C; cause.raw = 0xB000007C;
random = 0x0000001F;
status.raw = 0x241000E0; status.raw = 0x241000E0;
wired = 64;
index = 64;
PRId = 0x00000B22; PRId = 0x00000B22;
Config = 0x7006E463; Config = 0x7006E463;
EPC = 0xFFFFFFFFFFFFFFFF; EPC = 0xFFFFFFFFFFFFFFFF;
ErrorEPC = 0xFFFFFFFFFFFFFFFF; ErrorEPC = 0xFFFFFFFFFFFFFFFF;
} }
template<class T> u32 Cop0::GetReg32(u8 addr) {
T Cop0::GetReg(u8 addr) {
switch(addr) { switch(addr) {
case 0: return index & 0x8000001F; case COP0_REG_INDEX: return index & 0x8000003F;
case 1: return random & 0x1F; case COP0_REG_RANDOM: return GetRandom();
case 2: return entryLo0.raw & 0x3FFFFFFF; case COP0_REG_ENTRYLO0: return entryLo0.raw;
case 3: return entryLo1.raw & 0x3FFFFFFF; case COP0_REG_ENTRYLO1: return entryLo1.raw;
case 4: return context.raw; case COP0_REG_CONTEXT: return context.raw;
case 5: return pageMask.raw; case COP0_REG_PAGEMASK: return pageMask.raw;
case 6: return wired & 0x3F; case COP0_REG_WIRED: return wired;
case 7: return r7; case COP0_REG_BADVADDR: return badVaddr;
case 8: return badVaddr; case COP0_REG_COUNT: return GetCount();
case 9: return count >> 1; case COP0_REG_ENTRYHI: return entryHi.raw;
case 10: return entryHi.raw & 0xFFFFFFFFFFFFE0FF; case COP0_REG_COMPARE: return compare;
case 11: return compare; case COP0_REG_STATUS: return status.raw;
case 12: return status.raw & STATUS_MASK; case COP0_REG_CAUSE: return cause.raw;
case 13: return cause.raw; case COP0_REG_EPC: return EPC;
case 14: return EPC; case COP0_REG_PRID: return PRId;
case 15: return PRId & 0xFFFF; case COP0_REG_CONFIG: return Config;
case 16: return Config; case COP0_REG_LLADDR: return LLAddr;
case 17: return LLAddr; case COP0_REG_WATCHLO: return WatchLo;
case 18: return WatchLo; case COP0_REG_WATCHHI: return WatchHi;
case 19: return WatchHi; case COP0_REG_XCONTEXT: return xcontext.raw;
case 20: return xcontext.raw & 0xFFFFFFF0; case COP0_REG_PARITY_ERR: return ParityError;
case 21: return r21; case COP0_REG_CACHE_ERR: return CacheError;
case 22: return r22; case COP0_REG_TAGLO: return TagLo;
case 23: return r23; case COP0_REG_TAGHI: return TagHi;
case 24: return r24; case COP0_REG_ERROREPC: return ErrorEPC;
case 25: return r25; case 7: case 21: case 22:
case 26: return ParityError; case 23: case 24: case 25:
case 27: return CacheError; case 31: return 0;
case 28: return TagLo & 0xFFFFFFF;
case 29: return TagHi;
case 30: return ErrorEPC;
case 31: return r31;
default: default:
util::panic("Unsupported word read from COP0 register {}\n", index); util::panic("Unsupported word read from COP0 register {}\n", index);
} }
} }
template u32 Cop0::GetReg<u32>(u8 addr); u64 Cop0::GetReg64(u8 addr) {
template u64 Cop0::GetReg<u64>(u8 addr);
template s32 Cop0::GetReg<s32>(u8 addr);
template s64 Cop0::GetReg<s64>(u8 addr);
template<class T>
void Cop0::SetReg(u8 addr, T value) {
switch(addr) { switch(addr) {
case 0: index = value & 0x8000001F; break; case 2: return entryLo0.raw;
case 1: random = value & 0x1F; break; case 3: return entryLo1.raw;
case 2: entryLo0.raw = value & 0x3FFFFFFF; break; case 4: return context.raw;
case 3: entryLo1.raw = value & 0x3FFFFFFF; break; case 8: return badVaddr;
case 4: context.raw = value; break; case 10: return entryHi.raw;
case 5: pageMask.raw = value; break; case 12: return status.raw;
case 6: wired = value & 0x3F; break; case 14: return EPC;
case 7: r7 = value; break; case 15: return PRId;
case 9: count = value << 1; break; case 17: return LLAddr;
case 10: entryHi.raw = value & 0xFFFFFFFFFFFFE0FF; break; case 20: return xcontext.raw & 0xFFFFFFFFFFFFFFF0;
case 11: { case 30: return ErrorEPC;
cause.ip7 = 0; default:
compare = value; util::panic("Unsupported word read from COP0 register {}\n", index);
} break; }
case 12: }
status.raw &= ~STATUS_MASK;
status.raw |= value & STATUS_MASK; 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; 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{}; Cop0Cause tmp{};
tmp.raw = value; tmp.raw = value;
cause.ip0 = tmp.ip0; cause.ip0 = tmp.ip0;
cause.ip1 = tmp.ip1; cause.ip1 = tmp.ip1;
} break; } break;
case 14: EPC = value; break; case COP0_REG_BADVADDR: break;
case 15: PRId = value & 0xFFFF; break; case COP0_REG_EPC: EPC = (s64)value; break;
case 16: Config = value; break; case COP0_REG_LLADDR: LLAddr = value; break;
case 17: LLAddr = value; break; case COP0_REG_ERROREPC: ErrorEPC = (s64)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;
default: 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<u32>(u8 addr, u32 value);
template void Cop0::SetReg<u64>(u8 addr, u64 value);
template void Cop0::SetReg<s32>(u8 addr, s32 value);
template void Cop0::SetReg<s64>(u8 addr, s64 value);
#define vpn(addr, PageMask) (((((addr) & 0xFFFFFFFFFF) | (((addr) >> 22) & 0x30000000000)) & ~((PageMask) | 0x1FFF))) #define vpn(addr, PageMask) (((((addr) & 0xFFFFFFFFFF) | (((addr) >> 22) & 0x30000000000)) & ~((PageMask) | 0x1FFF)))
TLBEntry* TLBTryMatch(Registers& regs, u32 vaddr, int* match) { 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 0x01: tlbr(regs); break;
case 0x02: tlbwi(regs); break; case 0x02: tlbwi(regs); break;
case 0x08: tlbp(regs); break; case 0x08: tlbp(regs); break;
case 0x18: case 0x18: eret(regs); break;
eret(regs);
break;
default: util::panic("Unimplemented COP0 function {} {} ({:08X}) ({:016lX})", mask_cop2 >> 3, mask_cop2 & 7, instr, regs.oldPC); default: util::panic("Unimplemented COP0 function {} {} ({:08X}) ({:016lX})", mask_cop2 >> 3, mask_cop2 & 7, instr, regs.oldPC);
} }
break; break;

View File

@@ -3,6 +3,36 @@
namespace n64 { namespace n64 {
#define STATUS_MASK 0xFF57FFFF #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 Cpu;
struct Registers; struct Registers;
@@ -153,11 +183,11 @@ union Cop0XContext {
struct Cop0 { struct Cop0 {
Cop0(); Cop0();
template<class T> u32 GetReg32(u8);
T GetReg(u8); u64 GetReg64(u8);
template<class T> void SetReg32(u8, u32);
void SetReg(u8, T); void SetReg64(u8, u64);
void Reset(); void Reset();
@@ -170,23 +200,41 @@ struct Cop0 {
PageMask pageMask{}; PageMask pageMask{};
EntryHi entryHi{}; EntryHi entryHi{};
EntryLo entryLo0{}, entryLo1{}; EntryLo entryLo0{}, entryLo1{};
u32 index, random; u32 index;
Cop0Context context{}; Cop0Context context{};
u32 wired, r7{}; u32 wired, r7{};
u64 badVaddr{}, count{}; u64 badVaddr{}, count{};
u32 compare{}; u32 compare{};
Cop0Status status{}; Cop0Status status{};
Cop0Cause cause{}; Cop0Cause cause{};
u64 EPC; s64 EPC;
u32 PRId, Config, LLAddr{}, WatchLo{}, WatchHi{}; u32 PRId, Config, LLAddr{}, WatchLo{}, WatchHi{};
Cop0XContext xcontext{}; Cop0XContext xcontext{};
u32 r21{}, r22{}, r23{}, r24{}, r25{}, ParityError{}, CacheError{}, TagLo{}, TagHi{}; u32 r21{}, r22{}, r23{}, r24{}, r25{}, ParityError{}, CacheError{}, TagLo{}, TagHi{};
u64 ErrorEPC; s64 ErrorEPC;
u32 r31{}; u32 r31{};
TLBEntry tlb[32]{}; TLBEntry tlb[32]{};
TLBError tlbError = NONE; TLBError tlbError = NONE;
void decode(Registers&, Mem&, u32); void decode(Registers&, Mem&, u32);
private: 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 mtc0(Registers&, u32);
void dmtc0(Registers&, u32); void dmtc0(Registers&, u32);
void mfc0(Registers&, u32); void mfc0(Registers&, u32);

View File

@@ -4,27 +4,27 @@
namespace n64 { namespace n64 {
void Cop0::mtc0(Registers& regs, u32 instr) { void Cop0::mtc0(Registers& regs, u32 instr) {
SetReg<u32>(RD(instr), regs.gpr[RT(instr)]); SetReg32(RD(instr), regs.gpr[RT(instr)]);
} }
void Cop0::dmtc0(Registers& regs, u32 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) { void Cop0::mfc0(Registers& regs, u32 instr) {
regs.gpr[RT(instr)] = GetReg<s32>(RD(instr)); regs.gpr[RT(instr)] = s32(GetReg32(RD(instr)));
} }
void Cop0::dmfc0(Registers& regs, u32 instr) { void Cop0::dmfc0(Registers& regs, u32 instr) {
regs.gpr[RT(instr)] = GetReg<s64>(RD(instr)); regs.gpr[RT(instr)] = s64(GetReg64(RD(instr)));
} }
void Cop0::eret(Registers& regs) { void Cop0::eret(Registers& regs) {
if(status.erl) { if(status.erl) {
regs.SetPC((s64)ErrorEPC); regs.SetPC(ErrorEPC);
status.erl = false; status.erl = false;
} else { } else {
regs.SetPC((s64)EPC); regs.SetPC(EPC);
status.exl = false; status.exl = false;
} }
llbit = false; llbit = false;

View File

@@ -2,7 +2,7 @@
#include <n64/core/cpu/Registers.hpp> #include <n64/core/cpu/Registers.hpp>
#include <n64/core/Mem.hpp> #include <n64/core/Mem.hpp>
#include <util.hpp> #include <util.hpp>
#include <math.h> #include <cmath>
namespace n64 { namespace n64 {
void Cop1::absd(Registers& regs, u32 instr) { 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)); double ft = GetCop1RegDouble(regs.cop0, FT(instr));
bool less, equal, unordered; bool less, equal, unordered;
if(isnan(fs) || isnan(ft)) { if(std::isnan(fs) || std::isnan(ft)) {
less = false; less = false;
equal = false; equal = false;
unordered = true; unordered = true;
@@ -222,7 +222,7 @@ void Cop1::cconds(Registers& regs, u32 instr, CompConds cond) {
float ft = GetCop1RegFloat(regs.cop0, FT(instr)); float ft = GetCop1RegFloat(regs.cop0, FT(instr));
bool less, equal, unordered; bool less, equal, unordered;
if(isnan(fs) || isnan(ft)) { if(std::isnan(fs) || std::isnan(ft)) {
less = false; less = false;
equal = false; equal = false;
unordered = true; unordered = true;

View File

@@ -59,7 +59,7 @@ void PI::Write(Mem& mem, Registers& regs, u32 addr, u32 val) {
cartAddr = cart_addr + len; cartAddr = cart_addr + len;
InterruptRaise(mi, regs, Interrupt::PI); InterruptRaise(mi, regs, Interrupt::PI);
status = status & 0xFFFFFFFE; 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; } break;
case 0x0460000C: { case 0x0460000C: {
u32 len = (val & 0x00FFFFFF) + 1; u32 len = (val & 0x00FFFFFF) + 1;
@@ -76,7 +76,7 @@ void PI::Write(Mem& mem, Registers& regs, u32 addr, u32 val) {
cartAddr = cart_addr + len; cartAddr = cart_addr + len;
InterruptRaise(mi, regs, Interrupt::PI); InterruptRaise(mi, regs, Interrupt::PI);
status = status & 0xFFFFFFFE; 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; } break;
case 0x04600010: case 0x04600010:
if(val & 2) { if(val & 2) {

View File

@@ -12,6 +12,8 @@
#define PIF_RAM_DSIZE (PIF_RAM_SIZE - 1) #define PIF_RAM_DSIZE (PIF_RAM_SIZE - 1)
#define PIF_BOOTROM_SIZE 0x7C0 #define PIF_BOOTROM_SIZE 0x7C0
#define PIF_BOOTROM_DSIZE (PIF_BOOTROM_SIZE - 1) #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 RDRAM_REGION 0 ... RDRAM_DSIZE
#define DMEM_REGION 0x04000000 ... DMEM_DSIZE #define DMEM_REGION 0x04000000 ... DMEM_DSIZE

View File

@@ -7,6 +7,7 @@
#include <vector> #include <vector>
#include <array> #include <array>
#include <chrono> #include <chrono>
#include <functional>
namespace util { namespace util {
using SteadyClock = std::chrono::steady_clock; using SteadyClock = std::chrono::steady_clock;
@@ -141,16 +142,12 @@ inline void SwapN64Rom(size_t size, u8* data) {
memcpy(&endianness, data, 4); memcpy(&endianness, data, 4);
endianness = static_cast<RomTypes>(be32toh(endianness)); endianness = static_cast<RomTypes>(be32toh(endianness));
switch(endianness) { switch(endianness) {
case V64: { case V64:
u8* tmp = (u8*)calloc(size, 1); SwapBuffer32(size, data);
for(int i = 0; i < size; i++) { SwapBuffer16(size, data);
data[i] = tmp[i ^ 2]; break;
}
free(tmp);
} break;
case N64: break; case N64: break;
case Z64: case Z64: SwapBuffer32(size, data); break;
SwapBuffer32(size, data); break;
default: default:
panic("Unrecognized rom format! Make sure this is a valid Nintendo 64 ROM dump!\n"); 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 <size_t size, typename T = u8> template <size_t size, typename T = u8>
struct CircularBuffer { struct CircularBuffer {
CircularBuffer() : head(0) { CircularBuffer() : head(0) {
memset(raw.data(), 0, size * sizeof(T)); raw.fill(T());
} }
// make it scroll // make it scroll
void PushValue(T val) { void PushValue(T val) {
raw[head] = val; raw[head] = val;
head++; head++;
head &= mask; if(head == size) {
head = 0;
}
} }
T PopValue() { T PopValue() {
head--; head--;
head &= mask; if(head == 0) {
head = size - 1;
}
return raw[head]; return raw[head];
} }
@@ -179,11 +180,11 @@ struct CircularBuffer {
} }
auto begin() { return raw.begin(); } auto begin() { return raw.begin(); }
auto end() { return raw.end(); } auto end() { return raw.end(); }
auto fill(const T& u) { raw.fill(u); }
size_t GetHead() { return head; } size_t GetHead() { return head; }
private: private:
std::array<T, size> raw; std::array<T, size> raw;
size_t head; size_t head;
static constexpr size_t mask = size - 1;
}; };
inline size_t NextPow2(size_t num) { inline size_t NextPow2(size_t num) {