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

View File

@@ -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);

View File

@@ -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)

View File

@@ -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);

View File

@@ -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;
}

View File

@@ -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)

View File

@@ -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<u32>(regs, regs.pc, regs.pc);
HandleInterrupt(regs);

View File

@@ -1,6 +1,7 @@
#pragma once
#include <Registers.hpp>
#include <Mem.hpp>
#include <util.hpp>
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);
}

View File

@@ -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<T>(isviewer, paddr & ISVIEWER_DSIZE, be32toh(val));
break;
case 0x1FC007C0 ... 0x1FC007FF:
if constexpr (sizeof(T) == 1) {
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 {
util::WriteAccess<T>(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);
}
}

View File

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

View File

@@ -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<u32>(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<u32>(imem, pc & IMEM_DSIZE);
//oldPC = pc & 0xFFF;
//pc = nextPC & 0xFFF;
//nextPC += 4;
//Exec(mi, regs, rdp, instr);
}
}

View File

@@ -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
};
}

View File

@@ -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);
}
}
}

View File

@@ -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);

View File

@@ -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<class T>
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<u32>(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) {
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<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)))
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;

View File

@@ -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<class T>
T GetReg(u8);
u32 GetReg32(u8);
u64 GetReg64(u8);
template<class T>
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);

View File

@@ -4,27 +4,27 @@
namespace n64 {
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) {
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<s32>(RD(instr));
regs.gpr[RT(instr)] = s32(GetReg32(RD(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) {
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;

View File

@@ -2,7 +2,7 @@
#include <n64/core/cpu/Registers.hpp>
#include <n64/core/Mem.hpp>
#include <util.hpp>
#include <math.h>
#include <cmath>
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;

View File

@@ -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) {

View File

@@ -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

View File

@@ -7,6 +7,7 @@
#include <vector>
#include <array>
#include <chrono>
#include <functional>
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<RomTypes>(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 <size_t size, typename T = u8>
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<T, size> raw;
size_t head;
static constexpr size_t mask = size - 1;
};
inline size_t NextPow2(size_t num) {