fixed J
This commit is contained in:
4
.gitmodules
vendored
4
.gitmodules
vendored
@@ -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
|
||||
|
||||
1
external/capstone
vendored
Submodule
1
external/capstone
vendored
Submodule
Submodule external/capstone added at 0d0e684358
@@ -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);
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,6 +29,7 @@ private:
|
||||
MMIO mmio;
|
||||
std::vector<u8> cart, sram;
|
||||
u8 pifBootrom[PIF_BOOTROM_SIZE]{};
|
||||
u8 isviewer[ISVIEWER_SIZE]{};
|
||||
size_t romMask;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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
|
||||
|
||||
27
src/util.hpp
27
src/util.hpp
@@ -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) {
|
||||
|
||||
Reference in New Issue
Block a user