Cached interpreter plays Mario 64. Start looking into RSP as well

This commit is contained in:
2026-05-22 16:46:29 +02:00
parent c9a0307878
commit 4f42a673a3
23 changed files with 1161 additions and 845 deletions
+2
View File
@@ -4,6 +4,7 @@ saves/
.cache/ .cache/
.vs/ .vs/
.vscode/ .vscode/
.zed/
out/ out/
*.toml *.toml
*.ini *.ini
@@ -27,3 +28,4 @@ compile_commands.json
tests/ tests/
.DS_Store .DS_Store
resources/version.hpp resources/version.hpp
__cmake_systeminformation/CMakeFiles/
+6 -8
View File
@@ -16,6 +16,8 @@ Core::Core() :
cpuType = Interpreted; cpuType = Interpreted;
} else if (selectedCpu == "jit") { } else if (selectedCpu == "jit") {
cpuType = DynamicRecompiler; cpuType = DynamicRecompiler;
} else if (selectedCpu == "cached_interpreter") {
cpuType = CachedInterpreter;
} else { } else {
panic("Unimplemented CPU type"); panic("Unimplemented CPU type");
} }
@@ -66,6 +68,9 @@ u32 Core::StepCPU() {
if (cpuType == Interpreted) if (cpuType == Interpreted)
return interpreter.Step() + regs.PopStalledCycles(); return interpreter.Step() + regs.PopStalledCycles();
if (cpuType == CachedInterpreter)
return interpreter.ExecuteCached() + regs.PopStalledCycles();
#ifdef KAIZEN_JIT_ENABLED #ifdef KAIZEN_JIT_ENABLED
if (cpuType == DynamicRecompiler) if (cpuType == DynamicRecompiler)
return jit.Step() + regs.PopStalledCycles(); return jit.Step() + regs.PopStalledCycles();
@@ -96,11 +101,6 @@ void Core::StepRSP(const u32 cpuCycles) {
} }
} }
void Core::MaybeIdleSkip() {
if (GetRegs().nextPC == GetRegs().pc)
Scheduler::GetInstance().SkipToNext();
}
void Core::Run(const float volumeL, const float volumeR) { void Core::Run(const float volumeL, const float volumeR) {
MMIO &mmio = mem->mmio; MMIO &mmio = mem->mmio;
@@ -117,12 +117,10 @@ void Core::Run(const float volumeL, const float volumeR) {
for (int cycles = 0; cycles < mem->mmio.vi.cyclesPerHalfline;) { for (int cycles = 0; cycles < mem->mmio.vi.cyclesPerHalfline;) {
Scheduler::GetInstance().HandleEvents(); Scheduler::GetInstance().HandleEvents();
const u32 taken = StepCPU(); const u32 taken = StepCPU();
cycles += taken; cycles += taken;
StepRSP(taken);
frameCycles += taken; frameCycles += taken;
StepRSP(taken);
Scheduler::GetInstance().Tick(taken); Scheduler::GetInstance().Tick(taken);
} }
} }
+6 -2
View File
@@ -10,7 +10,7 @@
namespace n64 { namespace n64 {
struct Core { struct Core {
enum CPUType { Interpreted, DynamicRecompiler, CachedInterpreter } cpuType = Interpreted; enum CPUType { Interpreted, DynamicRecompiler, CachedInterpreter } cpuType = CachedInterpreter;
explicit Core(); explicit Core();
@@ -19,7 +19,11 @@ struct Core {
return instance; return instance;
} }
static void MaybeIdleSkip(); static inline bool IsAddressError(u8 mask, u64 vaddr) {
auto regs = GetRegs();
return (!regs.cop0.is64BitAddressing && s32(vaddr) != vaddr) || (vaddr & mask) != 0;
}
static Registers &GetRegs() { return GetInstance().regs; } static Registers &GetRegs() { return GetInstance().regs; }
static Mem &GetMem() { return *GetInstance().mem; } static Mem &GetMem() { return *GetInstance().mem; }
+2 -6
View File
@@ -27,13 +27,9 @@ u64 Scheduler::Remove(const EventType eventType) const {
return ret; return ret;
} }
void Scheduler::SkipToNext() { void Scheduler::SkipToNext() { ticks = events.top().time; }
ticks = events.top().time;
}
void Scheduler::Tick(const u64 t) { void Scheduler::Tick(const u64 t) { ticks += t; }
ticks += t;
}
void Scheduler::HandleEvents() { void Scheduler::HandleEvents() {
n64::Mem &mem = n64::Core::GetMem(); n64::Mem &mem = n64::Core::GetMem();
+2
View File
@@ -52,6 +52,7 @@ void DataCache::WriteBack<false>(u64 vaddr, u32 paddr) {
u32 origPhysAddr = (line.ptag << 12) | (paddr & 0xfff); u32 origPhysAddr = (line.ptag << 12) | (paddr & 0xfff);
u32 lineStart = GetDCacheLineStart(origPhysAddr); u32 lineStart = GetDCacheLineStart(origPhysAddr);
Core::GetInstance().interpreter.EvictCachedBlock(vaddr);
for (int i = 0; i < 16; i++) { for (int i = 0; i < 16; i++) {
mmio.rdp.WriteRDRAM(lineStart + i, line.data[i]); mmio.rdp.WriteRDRAM(lineStart + i, line.data[i]);
} }
@@ -86,6 +87,7 @@ void InstructionCache::WriteBack(u64 vaddr, u32 paddr, u32 ptag) {
if (line.ptag == ptag && line.valid) { if (line.ptag == ptag && line.valid) {
u32 origPhysAddr = (line.ptag << 12) | (paddr & 0xfff); u32 origPhysAddr = (line.ptag << 12) | (paddr & 0xfff);
u32 lineStart = GetICacheLineStart(origPhysAddr); u32 lineStart = GetICacheLineStart(origPhysAddr);
Core::GetInstance().interpreter.EvictCachedBlock(vaddr);
for (int i = 0; i < 16; i++) { for (int i = 0; i < 16; i++) {
mmio.rdp.WriteRDRAM(lineStart + i, line.data[i]); mmio.rdp.WriteRDRAM(lineStart + i, line.data[i]);
} }
+139 -16
View File
@@ -1,4 +1,6 @@
#include <Core.hpp> #include <Core.hpp>
#include <Scheduler.hpp>
#include "jit/helpers.hpp"
namespace n64 { namespace n64 {
Interpreter::Interpreter(Mem &mem, Registers &regs) : regs(regs), mem(mem) {} Interpreter::Interpreter(Mem &mem, Registers &regs) : regs(regs), mem(mem) {}
@@ -12,7 +14,7 @@ bool Interpreter::ShouldServiceInterrupt() const {
return interrupts_pending && interrupts_enabled && !currently_handling_exception && !currently_handling_error; return interrupts_pending && interrupts_enabled && !currently_handling_exception && !currently_handling_error;
} }
void Interpreter::CheckCompareInterrupt() const { void Interpreter::UpdateCompareInterrupt() const {
regs.cop0.count++; regs.cop0.count++;
regs.cop0.count &= 0x1FFFFFFFF; regs.cop0.count &= 0x1FFFFFFFF;
if (regs.cop0.count == static_cast<u64>(regs.cop0.compare) << 1) { if (regs.cop0.count == static_cast<u64>(regs.cop0.compare) << 1) {
@@ -21,38 +23,159 @@ void Interpreter::CheckCompareInterrupt() const {
} }
} }
u32 Interpreter::Step() { bool Interpreter::Fetch(Instruction &instr, u64 vaddr) {
CheckCompareInterrupt(); u32 paddr = 0;
if (!regs.cop0.MapVAddr(Cop0::LOAD, vaddr, paddr)) {
regs.cop0.HandleTLBException(vaddr);
regs.cop0.FireException(Cop0::GetTLBExceptionCode(regs.cop0.tlbError, Cop0::LOAD), 0, vaddr);
return false;
}
instr = mem.Read<u32>(paddr);
return true;
}
bool Interpreter::MaybeAdvance() {
UpdateCompareInterrupt();
regs.prevDelaySlot = regs.delaySlot; regs.prevDelaySlot = regs.delaySlot;
regs.delaySlot = false; regs.delaySlot = false;
if (check_address_error(0b11, u64(regs.pc))) [[unlikely]] { if (Core::IsAddressError(0b11, u64(regs.pc))) [[unlikely]] {
regs.cop0.HandleTLBException(regs.pc); regs.cop0.HandleTLBException(regs.pc);
regs.cop0.FireException(Cop0::ExceptionCode::AddressErrorLoad, 0, regs.pc); regs.cop0.FireException(Cop0::ExceptionCode::AddressErrorLoad, 0, regs.pc);
return 1; return false;
} }
u32 paddr = 0;
if (!regs.cop0.MapVAddr(Cop0::LOAD, regs.pc, paddr)) {
regs.cop0.HandleTLBException(regs.pc);
regs.cop0.FireException(Cop0::GetTLBExceptionCode(regs.cop0.tlbError, Cop0::LOAD), 0, regs.pc);
return 1;
}
const u32 instruction = mem.Read<u32>(paddr);
if (ShouldServiceInterrupt()) { if (ShouldServiceInterrupt()) {
regs.cop0.FireException(Cop0::ExceptionCode::Interrupt, 0, regs.pc); regs.cop0.FireException(Cop0::ExceptionCode::Interrupt, 0, regs.pc);
return 1; return false;
} }
regs.oldPC = regs.pc; regs.oldPC = regs.pc;
regs.pc = regs.nextPC; regs.pc = regs.nextPC;
regs.nextPC += 4; regs.nextPC += 4;
Exec(instruction); return true;
}
bool Interpreter::FetchThenMaybeAdvance(Instruction &instr) {
UpdateCompareInterrupt();
regs.prevDelaySlot = regs.delaySlot;
regs.delaySlot = false;
if (Core::IsAddressError(0b11, u64(regs.pc))) [[unlikely]] {
regs.cop0.HandleTLBException(regs.pc);
regs.cop0.FireException(Cop0::ExceptionCode::AddressErrorLoad, 0, regs.pc);
return false;
}
if (!Fetch(instr, regs.pc))
return false;
if (ShouldServiceInterrupt()) {
regs.cop0.FireException(Cop0::ExceptionCode::Interrupt, 0, regs.pc);
return false;
}
regs.oldPC = regs.pc;
regs.pc = regs.nextPC;
regs.nextPC += 4;
return true;
}
u32 Interpreter::Step() {
Instruction instr;
if (!FetchThenMaybeAdvance(instr))
return 1;
DecodeExecute(instr);
return 1; return 1;
} }
u32 Interpreter::CacheBlock(u32 addr) {
u32 blockAddr = addr;
CachedLine line;
u32 i;
bool fetchDelaySlot = false;
for (i = 0; i < MAX_INSTR_PER_BLOCK; i++) {
Instruction instr;
if (!Fetch(instr, addr))
return i + 1;
addr += 4;
line.code[i] = instr;
if (fetchDelaySlot) {
i++;
break;
}
if (InstrEndsBlock(instr)) {
if (InstrHasDelaySlot(instr) && !fetchDelaySlot) {
fetchDelaySlot = true;
continue;
}
if (i == 0)
i = 1;
break;
}
}
line.cycles = i;
line.len = i;
cachedState.blocks[CACHE_GET_BLOCK(blockAddr)]->lines[CACHE_GET_LINE(blockAddr)] = new CachedLine(line);
return ExecuteCached();
}
u32 Interpreter::ExecuteCached() {
u32 addr = regs.pc;
auto &blocks = cachedState.blocks;
if (!blocks[CACHE_GET_BLOCK(addr)]) {
blocks[CACHE_GET_BLOCK(addr)] = new CachedBlock<cachedState.MAX_LINES / 4>();
return CacheBlock(addr);
}
const auto line = blocks[CACHE_GET_BLOCK(addr)]->lines[CACHE_GET_LINE(addr)];
if (line) {
cachedState.exception = false;
// i copy the block cycles here in case the block evicts itself when executing which would set the cycles to
// 0, making so the emulator halts cause the outer loop won't advance
const auto blockCycles = line->cycles;
for (u32 i = 0; i < line->len; i++) {
addr += 4;
if (!MaybeAdvance())
return i + 1;
Instruction instr = line->code[i];
DecodeExecute(instr);
if (cachedState.exception)
return i + 1;
// Branch likely with false condition, it wasn't taken so don't execute the delay slot
if (IsBranchLikely(instr) && !regs.delaySlot)
break;
}
if (blockCycles == 0) {
panic("Cycles are 0!");
Scheduler::GetInstance().SkipToNext();
}
return blockCycles;
}
return CacheBlock(addr);
}
} // namespace n64 } // namespace n64
+61 -8
View File
@@ -1,36 +1,89 @@
#pragma once #pragma once
#include <Cache.hpp> #include <Cache.hpp>
#include <Mem.hpp> #include <Mem.hpp>
#include <JITUtils.hpp>
namespace n64 { namespace n64 {
struct Core; struct Core;
/*
static constexpr u32 MAX_INSTR_PER_BLOCK = 128;
static constexpr u32 MAX_LINES = 1 << 12;
#define CACHE_GET_BLOCK(addr) (addr / MAX_LINES)
#define CACHE_GET_LINE(addr) ((addr & (MAX_LINES - 1)) >> 2)
struct CachedLine {
std::array<Instruction, MAX_INSTR_PER_BLOCK> code = {};
u32 len = 0;
u32 cycles = 0;
} __attribute__((__packed__));
struct CachedBlock {
CachedBlock() { lines.resize(MAX_LINES / 4); }
std::vector<CachedLine *> lines = {};
};
struct CachedState {
std::vector<CachedBlock *> blocks = {};
bool exception = false;
void Reset() {
for (auto block : blocks) {
if (block)
for (auto line : block->lines)
delete line;
delete block;
}
blocks = {};
blocks.resize(((u64)std::numeric_limits<u32>::max() + 1) / MAX_LINES);
}
};
*/
struct Interpreter final { struct Interpreter final {
explicit Interpreter(Mem &, Registers &); explicit Interpreter(Mem &, Registers &);
~Interpreter() = default; ~Interpreter() = default;
u32 Step(); u32 Step();
u32 ExecuteCached();
bool FetchThenMaybeAdvance(Instruction &);
bool MaybeAdvance();
u32 CacheBlock(u32 addr);
void Reset() { cop2Latch = {}; } void SignalException(u32 addr) { cachedState.exception = true; }
void EvictCachedBlock(u32 addr) { cachedState.blocks[CACHE_GET_BLOCK(addr)] = {}; }
void Reset() {
cop2Latch = {};
cachedState.Reset();
}
CachedState<12, std::numeric_limits<u32>::max()> cachedState;
private: private:
friend struct Cop1;
friend struct Mem;
void MaybeIdleSkip();
InstructionCache icache; InstructionCache icache;
DataCache dcache; DataCache dcache;
Registers &regs; Registers &regs;
Mem &mem; Mem &mem;
u64 cop2Latch{}; u64 cop2Latch{};
friend struct Cop1; u32 rspSyncCount = 0;
bool Fetch(Instruction &, u64);
void CacheTypeData(u8, u64, u32, u32);
void CacheTypeInstruction(u8, u64, u32, u32);
void cache_type_data(u8, u64, u32, u32);
void cache_type_instruction(u8, u64, u32, u32);
#define check_address_error(mask, vaddr) \
(((!regs.cop0.is64BitAddressing) && (s32)(vaddr) != (vaddr)) || (((vaddr) & (mask)) != 0))
[[nodiscard]] bool ShouldServiceInterrupt() const; [[nodiscard]] bool ShouldServiceInterrupt() const;
void CheckCompareInterrupt() const; void UpdateCompareInterrupt() const;
void cop2Decode(Instruction); void cop2Decode(Instruction);
void special(Instruction); void special(Instruction);
void regimm(Instruction); void regimm(Instruction);
void Exec(Instruction); void DecodeExecute(Instruction);
void add(Instruction); void add(Instruction);
void addu(Instruction); void addu(Instruction);
void addi(Instruction); void addi(Instruction);
+1 -1
View File
@@ -41,7 +41,7 @@ void JIT::InvalidateBlock(const u32 paddr) {
std::optional<u32> JIT::FetchInstruction(s64 vaddr) { std::optional<u32> JIT::FetchInstruction(s64 vaddr) {
u32 paddr = 0; u32 paddr = 0;
if (check_address_error(0b11, vaddr)) [[unlikely]] { if (Core::IsAddressError(0b11, vaddr)) [[unlikely]] {
/*regs.cop0.HandleTLBException(blockPC); /*regs.cop0.HandleTLBException(blockPC);
regs.cop0.FireException(Cop0::ExceptionCode::AddressErrorLoad, 0, blockPC); regs.cop0.FireException(Cop0::ExceptionCode::AddressErrorLoad, 0, blockPC);
return 1;*/ return 1;*/
-3
View File
@@ -116,9 +116,6 @@ struct JIT final {
void BranchAbsTaken(s64 addr); void BranchAbsTaken(s64 addr);
void BranchAbsTaken(const Xbyak::Reg64 &addr); void BranchAbsTaken(const Xbyak::Reg64 &addr);
#define check_address_error(mask, vaddr) \
(((!regs.cop0.is64BitAddressing) && (s32)(vaddr) != (vaddr)) || (((vaddr) & (mask)) != 0))
[[nodiscard]] bool ShouldServiceInterrupt() const; [[nodiscard]] bool ShouldServiceInterrupt() const;
void CheckCompareInterrupt() const; void CheckCompareInterrupt() const;
std::optional<u32> FetchInstruction(s64); std::optional<u32> FetchInstruction(s64);
+42
View File
@@ -0,0 +1,42 @@
#pragma once
#include <Instruction.hpp>
#include <vector>
#include <array>
namespace n64 {
static constexpr u32 MAX_INSTR_PER_BLOCK = 128;
#define CACHE_GET_BLOCK(addr) (addr / (cachedState.MAX_LINES))
#define CACHE_GET_LINE(addr) ((addr & ((cachedState.MAX_LINES) - 1)) >> 2)
struct CachedLine {
std::array<Instruction, MAX_INSTR_PER_BLOCK> code = {};
u32 len = 0;
u32 cycles = 0;
} __attribute__((__packed__));
template <u32 lineAmount>
struct CachedBlock {
CachedBlock() { lines.resize(lineAmount); }
std::vector<CachedLine *> lines = {};
};
template <u32 blockBits, u64 addressSpace>
struct CachedState {
static constexpr u32 MAX_LINES = 1 << blockBits;
std::vector<CachedBlock<MAX_LINES / 4> *> blocks = {};
bool exception = false;
void Reset() {
for (auto block : blocks) {
if (block)
for (auto line : block->lines)
delete line;
delete block;
}
blocks = {};
blocks.resize((addressSpace + 1) / MAX_LINES);
}
};
} // namespace n64
-1
View File
@@ -121,7 +121,6 @@ void RDP::Write(const u32 addr, const u32 val) {
} }
void RDP::WriteStatus(const u32 val) { void RDP::WriteStatus(const u32 val) {
DPCStatusWrite temp{}; DPCStatusWrite temp{};
temp.raw = val; temp.raw = val;
+6 -1
View File
@@ -83,7 +83,12 @@ auto RSP::Read(const u32 addr) -> u32 {
case 0x04080000: case 0x04080000:
return pc & 0xFFC; return pc & 0xFFC;
default: default:
panic("Unimplemented SP register read {:08X}", addr); {
auto &regs = Core::GetRegs();
panic("Unimplemented SP register read {:08X} (cpu pc: 0x{:016X}, rsp pc: 0x{:04X}, ra: 0x{:016X})", addr,
(u64)regs.oldPC, pc & 0xffc, (u64)regs.gpr[31]);
}
} }
} }
@@ -1310,6 +1310,7 @@ void Cop1::swc1(const Instruction instr) {
regs.cop0.HandleTLBException(addr); regs.cop0.HandleTLBException(addr);
regs.cop0.FireException(Cop0::GetTLBExceptionCode(regs.cop0.tlbError, Cop0::STORE), 0, regs.oldPC); regs.cop0.FireException(Cop0::GetTLBExceptionCode(regs.cop0.tlbError, Cop0::STORE), 0, regs.oldPC);
} else { } else {
Core::GetInstance().interpreter.EvictCachedBlock(addr);
mem.Write<u32>(physical, FGR_T<u32>(regs.cop0.status, instr.ft())); mem.Write<u32>(physical, FGR_T<u32>(regs.cop0.status, instr.ft()));
} }
} }
@@ -1337,6 +1338,7 @@ void Cop1::sdc1(const Instruction instr) {
regs.cop0.HandleTLBException(addr); regs.cop0.HandleTLBException(addr);
regs.cop0.FireException(Cop0::GetTLBExceptionCode(regs.cop0.tlbError, Cop0::STORE), 0, regs.oldPC); regs.cop0.FireException(Cop0::GetTLBExceptionCode(regs.cop0.tlbError, Cop0::STORE), 0, regs.oldPC);
} else { } else {
Core::GetInstance().interpreter.EvictCachedBlock(addr);
mem.Write(physical, FGR_T<u64>(regs.cop0.status, instr.ft())); mem.Write(physical, FGR_T<u64>(regs.cop0.status, instr.ft()));
} }
} }
+1 -1
View File
@@ -249,7 +249,7 @@ void Interpreter::cop2Decode(const Instruction instr) {
} }
} }
void Interpreter::Exec(const Instruction instr) { void Interpreter::DecodeExecute(const Instruction instr) {
// 00rr_rccc // 00rr_rccc
switch (instr.opcode()) { switch (instr.opcode()) {
case Instruction::SPECIAL: case Instruction::SPECIAL:
+25 -17
View File
@@ -140,7 +140,6 @@ void Interpreter::branch(const bool cond, const s64 address) {
regs.delaySlot = true; regs.delaySlot = true;
if (cond) { if (cond) {
regs.nextPC = address; regs.nextPC = address;
Core::MaybeIdleSkip();
} }
} }
@@ -148,7 +147,6 @@ void Interpreter::branch_likely(const bool cond, const s64 address) {
if (cond) { if (cond) {
regs.delaySlot = true; regs.delaySlot = true;
regs.nextPC = address; regs.nextPC = address;
Core::MaybeIdleSkip();
} else { } else {
regs.SetPC64(regs.nextPC); regs.SetPC64(regs.nextPC);
} }
@@ -202,7 +200,7 @@ void Interpreter::lb(const Instruction instr) {
void Interpreter::lh(const Instruction instr) { void Interpreter::lh(const Instruction instr) {
const u64 address = regs.Read<s64>(instr.rs()) + (s16)instr; const u64 address = regs.Read<s64>(instr.rs()) + (s16)instr;
if (check_address_error(0b1, address)) { if (Core::IsAddressError(0b1, address)) {
regs.cop0.HandleTLBException(address); regs.cop0.HandleTLBException(address);
regs.cop0.FireException(Cop0::ExceptionCode::AddressErrorLoad, 0, regs.oldPC); regs.cop0.FireException(Cop0::ExceptionCode::AddressErrorLoad, 0, regs.oldPC);
return; return;
@@ -220,7 +218,7 @@ void Interpreter::lh(const Instruction instr) {
void Interpreter::lw(const Instruction instr) { void Interpreter::lw(const Instruction instr) {
const s16 offset = instr; const s16 offset = instr;
const u64 address = regs.Read<s64>(instr.rs()) + offset; const u64 address = regs.Read<s64>(instr.rs()) + offset;
if (check_address_error(0b11, address)) { if (Core::IsAddressError(0b11, address)) {
regs.cop0.HandleTLBException(address); regs.cop0.HandleTLBException(address);
regs.cop0.FireException(Cop0::ExceptionCode::AddressErrorLoad, 0, regs.oldPC); regs.cop0.FireException(Cop0::ExceptionCode::AddressErrorLoad, 0, regs.oldPC);
return; return;
@@ -243,7 +241,7 @@ void Interpreter::ll(const Instruction instr) {
regs.cop0.FireException(Cop0::GetTLBExceptionCode(regs.cop0.tlbError, Cop0::LOAD), 0, regs.oldPC); regs.cop0.FireException(Cop0::GetTLBExceptionCode(regs.cop0.tlbError, Cop0::LOAD), 0, regs.oldPC);
} else { } else {
const s32 result = mem.Read<u32>(physical); const s32 result = mem.Read<u32>(physical);
if (check_address_error(0b11, address)) { if (Core::IsAddressError(0b11, address)) {
regs.cop0.FireException(Cop0::ExceptionCode::AddressErrorLoad, 0, regs.oldPC); regs.cop0.FireException(Cop0::ExceptionCode::AddressErrorLoad, 0, regs.oldPC);
return; return;
} }
@@ -287,7 +285,7 @@ void Interpreter::lwr(const Instruction instr) {
void Interpreter::ld(const Instruction instr) { void Interpreter::ld(const Instruction instr) {
const s64 address = regs.Read<s64>(instr.rs()) + (s16)instr; const s64 address = regs.Read<s64>(instr.rs()) + (s16)instr;
if (check_address_error(0b111, address)) { if (Core::IsAddressError(0b111, address)) {
regs.cop0.HandleTLBException(address); regs.cop0.HandleTLBException(address);
regs.cop0.FireException(Cop0::ExceptionCode::AddressErrorLoad, 0, regs.oldPC); regs.cop0.FireException(Cop0::ExceptionCode::AddressErrorLoad, 0, regs.oldPC);
return; return;
@@ -315,7 +313,7 @@ void Interpreter::lld(const Instruction instr) {
regs.cop0.HandleTLBException(address); regs.cop0.HandleTLBException(address);
regs.cop0.FireException(Cop0::GetTLBExceptionCode(regs.cop0.tlbError, Cop0::LOAD), 0, regs.oldPC); regs.cop0.FireException(Cop0::GetTLBExceptionCode(regs.cop0.tlbError, Cop0::LOAD), 0, regs.oldPC);
} else { } else {
if (check_address_error(0b111, address)) { if (Core::IsAddressError(0b111, address)) {
regs.cop0.FireException(Cop0::ExceptionCode::AddressErrorLoad, 0, regs.oldPC); regs.cop0.FireException(Cop0::ExceptionCode::AddressErrorLoad, 0, regs.oldPC);
} else { } else {
regs.Write(instr.rt(), mem.Read<u64>(paddr)); regs.Write(instr.rt(), mem.Read<u64>(paddr));
@@ -369,7 +367,7 @@ void Interpreter::lbu(const Instruction instr) {
void Interpreter::lhu(const Instruction instr) { void Interpreter::lhu(const Instruction instr) {
const s64 address = regs.Read<s64>(instr.rs()) + (s16)instr; const s64 address = regs.Read<s64>(instr.rs()) + (s16)instr;
if (check_address_error(0b1, address)) { if (Core::IsAddressError(0b1, address)) {
regs.cop0.HandleTLBException(address); regs.cop0.HandleTLBException(address);
regs.cop0.FireException(Cop0::ExceptionCode::AddressErrorLoad, 0, regs.oldPC); regs.cop0.FireException(Cop0::ExceptionCode::AddressErrorLoad, 0, regs.oldPC);
return; return;
@@ -386,7 +384,7 @@ void Interpreter::lhu(const Instruction instr) {
void Interpreter::lwu(const Instruction instr) { void Interpreter::lwu(const Instruction instr) {
const s64 address = regs.Read<s64>(instr.rs()) + (s16)instr; const s64 address = regs.Read<s64>(instr.rs()) + (s16)instr;
if (check_address_error(0b11, address)) { if (Core::IsAddressError(0b11, address)) {
regs.cop0.HandleTLBException(address); regs.cop0.HandleTLBException(address);
regs.cop0.FireException(Cop0::ExceptionCode::AddressErrorLoad, 0, regs.oldPC); regs.cop0.FireException(Cop0::ExceptionCode::AddressErrorLoad, 0, regs.oldPC);
return; return;
@@ -409,6 +407,7 @@ void Interpreter::sb(const Instruction instr) {
regs.cop0.HandleTLBException(address); regs.cop0.HandleTLBException(address);
regs.cop0.FireException(Cop0::GetTLBExceptionCode(regs.cop0.tlbError, Cop0::STORE), 0, regs.oldPC); regs.cop0.FireException(Cop0::GetTLBExceptionCode(regs.cop0.tlbError, Cop0::STORE), 0, regs.oldPC);
} else { } else {
EvictCachedBlock(address);
mem.Write<u8>(paddr, regs.Read<s64>(instr.rt())); mem.Write<u8>(paddr, regs.Read<s64>(instr.rt()));
} }
} }
@@ -419,7 +418,7 @@ void Interpreter::sc(const Instruction instr) {
if (regs.cop0.llbit) { if (regs.cop0.llbit) {
regs.cop0.llbit = false; regs.cop0.llbit = false;
if (check_address_error(0b11, address)) { if (Core::IsAddressError(0b11, address)) {
regs.Write(instr.rt(), 0); regs.Write(instr.rt(), 0);
regs.cop0.HandleTLBException(address); regs.cop0.HandleTLBException(address);
regs.cop0.FireException(Cop0::ExceptionCode::AddressErrorStore, 0, regs.oldPC); regs.cop0.FireException(Cop0::ExceptionCode::AddressErrorStore, 0, regs.oldPC);
@@ -432,6 +431,7 @@ void Interpreter::sc(const Instruction instr) {
regs.cop0.HandleTLBException(address); regs.cop0.HandleTLBException(address);
regs.cop0.FireException(Cop0::GetTLBExceptionCode(regs.cop0.tlbError, Cop0::STORE), 0, regs.oldPC); regs.cop0.FireException(Cop0::GetTLBExceptionCode(regs.cop0.tlbError, Cop0::STORE), 0, regs.oldPC);
} else { } else {
EvictCachedBlock(address);
mem.Write<u32>(paddr, regs.Read<s64>(instr.rt())); mem.Write<u32>(paddr, regs.Read<s64>(instr.rt()));
regs.Write(instr.rt(), 1); regs.Write(instr.rt(), 1);
} }
@@ -451,7 +451,7 @@ void Interpreter::scd(const Instruction instr) {
if (regs.cop0.llbit) { if (regs.cop0.llbit) {
regs.cop0.llbit = false; regs.cop0.llbit = false;
if (check_address_error(0b111, address)) { if (Core::IsAddressError(0b111, address)) {
regs.Write(instr.rt(), 0); regs.Write(instr.rt(), 0);
regs.cop0.HandleTLBException(address); regs.cop0.HandleTLBException(address);
regs.cop0.FireException(Cop0::ExceptionCode::AddressErrorStore, 0, regs.oldPC); regs.cop0.FireException(Cop0::ExceptionCode::AddressErrorStore, 0, regs.oldPC);
@@ -464,6 +464,7 @@ void Interpreter::scd(const Instruction instr) {
regs.cop0.HandleTLBException(address); regs.cop0.HandleTLBException(address);
regs.cop0.FireException(Cop0::GetTLBExceptionCode(regs.cop0.tlbError, Cop0::STORE), 0, regs.oldPC); regs.cop0.FireException(Cop0::GetTLBExceptionCode(regs.cop0.tlbError, Cop0::STORE), 0, regs.oldPC);
} else { } else {
EvictCachedBlock(address);
mem.Write<u32>(paddr, regs.Read<s64>(instr.rt())); mem.Write<u32>(paddr, regs.Read<s64>(instr.rt()));
regs.Write(instr.rt(), 1); regs.Write(instr.rt(), 1);
} }
@@ -480,6 +481,7 @@ void Interpreter::sh(const Instruction instr) {
regs.cop0.HandleTLBException(address); regs.cop0.HandleTLBException(address);
regs.cop0.FireException(Cop0::GetTLBExceptionCode(regs.cop0.tlbError, Cop0::STORE), 0, regs.oldPC); regs.cop0.FireException(Cop0::GetTLBExceptionCode(regs.cop0.tlbError, Cop0::STORE), 0, regs.oldPC);
} else { } else {
EvictCachedBlock(address);
mem.Write<u16>(physical, regs.Read<s64>(instr.rt())); mem.Write<u16>(physical, regs.Read<s64>(instr.rt()));
} }
} }
@@ -487,7 +489,7 @@ void Interpreter::sh(const Instruction instr) {
void Interpreter::sw(const Instruction instr) { void Interpreter::sw(const Instruction instr) {
const s16 offset = instr; const s16 offset = instr;
const u64 address = regs.Read<s64>(instr.rs()) + offset; const u64 address = regs.Read<s64>(instr.rs()) + offset;
if (check_address_error(0b11, address)) { if (Core::IsAddressError(0b11, address)) {
regs.cop0.HandleTLBException(address); regs.cop0.HandleTLBException(address);
regs.cop0.FireException(Cop0::ExceptionCode::AddressErrorStore, 0, regs.oldPC); regs.cop0.FireException(Cop0::ExceptionCode::AddressErrorStore, 0, regs.oldPC);
return; return;
@@ -498,13 +500,14 @@ void Interpreter::sw(const Instruction instr) {
regs.cop0.HandleTLBException(address); regs.cop0.HandleTLBException(address);
regs.cop0.FireException(Cop0::GetTLBExceptionCode(regs.cop0.tlbError, Cop0::STORE), 0, regs.oldPC); regs.cop0.FireException(Cop0::GetTLBExceptionCode(regs.cop0.tlbError, Cop0::STORE), 0, regs.oldPC);
} else { } else {
EvictCachedBlock(address);
mem.Write<u32>(physical, regs.Read<s64>(instr.rt())); mem.Write<u32>(physical, regs.Read<s64>(instr.rt()));
} }
} }
void Interpreter::sd(const Instruction instr) { void Interpreter::sd(const Instruction instr) {
const s64 address = regs.Read<s64>(instr.rs()) + (s16)instr; const s64 address = regs.Read<s64>(instr.rs()) + (s16)instr;
if (check_address_error(0b111, address)) { if (Core::IsAddressError(0b111, address)) {
regs.cop0.HandleTLBException(address); regs.cop0.HandleTLBException(address);
regs.cop0.FireException(Cop0::ExceptionCode::AddressErrorStore, 0, regs.oldPC); regs.cop0.FireException(Cop0::ExceptionCode::AddressErrorStore, 0, regs.oldPC);
return; return;
@@ -515,6 +518,7 @@ void Interpreter::sd(const Instruction instr) {
regs.cop0.HandleTLBException(address); regs.cop0.HandleTLBException(address);
regs.cop0.FireException(Cop0::GetTLBExceptionCode(regs.cop0.tlbError, Cop0::STORE), 0, regs.oldPC); regs.cop0.FireException(Cop0::GetTLBExceptionCode(regs.cop0.tlbError, Cop0::STORE), 0, regs.oldPC);
} else { } else {
EvictCachedBlock(address);
mem.Write(physical, regs.Read<s64>(instr.rt())); mem.Write(physical, regs.Read<s64>(instr.rt()));
} }
} }
@@ -530,6 +534,7 @@ void Interpreter::sdl(const Instruction instr) {
const u64 mask = 0xFFFFFFFFFFFFFFFF >> shift; const u64 mask = 0xFFFFFFFFFFFFFFFF >> shift;
const u64 data = mem.Read<u64>(paddr & ~7); const u64 data = mem.Read<u64>(paddr & ~7);
const u64 rt = regs.Read<s64>(instr.rt()); const u64 rt = regs.Read<s64>(instr.rt());
EvictCachedBlock(address);
mem.Write(paddr & ~7, (data & ~mask) | (rt >> shift)); mem.Write(paddr & ~7, (data & ~mask) | (rt >> shift));
} }
} }
@@ -545,6 +550,7 @@ void Interpreter::sdr(const Instruction instr) {
const u64 mask = 0xFFFFFFFFFFFFFFFF << shift; const u64 mask = 0xFFFFFFFFFFFFFFFF << shift;
const u64 data = mem.Read<u64>(paddr & ~7); const u64 data = mem.Read<u64>(paddr & ~7);
const u64 rt = regs.Read<s64>(instr.rt()); const u64 rt = regs.Read<s64>(instr.rt());
EvictCachedBlock(address);
mem.Write(paddr & ~7, (data & ~mask) | (rt << shift)); mem.Write(paddr & ~7, (data & ~mask) | (rt << shift));
} }
} }
@@ -560,6 +566,7 @@ void Interpreter::swl(const Instruction instr) {
const u32 mask = 0xFFFFFFFF >> shift; const u32 mask = 0xFFFFFFFF >> shift;
const u32 data = mem.Read<u32>(paddr & ~3); const u32 data = mem.Read<u32>(paddr & ~3);
const u32 rt = regs.Read<s64>(instr.rt()); const u32 rt = regs.Read<s64>(instr.rt());
EvictCachedBlock(address);
mem.Write<u32>(paddr & ~3, (data & ~mask) | (rt >> shift)); mem.Write<u32>(paddr & ~3, (data & ~mask) | (rt >> shift));
} }
} }
@@ -575,6 +582,7 @@ void Interpreter::swr(const Instruction instr) {
const u32 mask = 0xFFFFFFFF << shift; const u32 mask = 0xFFFFFFFF << shift;
const u32 data = mem.Read<u32>(paddr & ~3); const u32 data = mem.Read<u32>(paddr & ~3);
const u32 rt = regs.Read<s64>(instr.rt()); const u32 rt = regs.Read<s64>(instr.rt());
EvictCachedBlock(address);
mem.Write<u32>(paddr & ~3, (data & ~mask) | (rt << shift)); mem.Write<u32>(paddr & ~3, (data & ~mask) | (rt << shift));
} }
} }
@@ -869,12 +877,12 @@ void Interpreter::cache(const Instruction instr) {
panic("Unknown cache type {}", type); panic("Unknown cache type {}", type);
if (type == 0) if (type == 0)
return cache_type_instruction(op, vaddr, paddr, ptag); return CacheTypeInstruction(op, vaddr, paddr, ptag);
return cache_type_data(op, vaddr, paddr, ptag); return CacheTypeData(op, vaddr, paddr, ptag);
} }
void Interpreter::cache_type_instruction(const u8 op, const u64 vaddr, const u32 paddr, const u32 ptag) { void Interpreter::CacheTypeInstruction(const u8 op, const u64 vaddr, const u32 paddr, const u32 ptag) {
switch (op) { switch (op) {
case 0: case 0:
icache.InvalidateIndex(vaddr); icache.InvalidateIndex(vaddr);
@@ -899,7 +907,7 @@ void Interpreter::cache_type_instruction(const u8 op, const u64 vaddr, const u32
} }
} }
void Interpreter::cache_type_data(const u8 op, const u64 vaddr, const u32 paddr, const u32 ptag) { void Interpreter::CacheTypeData(const u8 op, const u64 vaddr, const u32 paddr, const u32 ptag) {
switch (op) { switch (op) {
case 0: case 0:
dcache.WriteBack<true>(vaddr, paddr); dcache.WriteBack<true>(vaddr, paddr);
+50 -7
View File
@@ -20,6 +20,34 @@ static bool SpecialEndsBlock(const Instruction instr) {
} }
} }
static bool InstrHasDelaySlot(const Instruction instr) {
switch (instr.opcode()) {
case Instruction::SPECIAL:
if (instr.special() == Instruction::JR || instr.special() == Instruction::JALR)
return true;
return false;
case Instruction::REGIMM:
case Instruction::J:
case Instruction::JAL:
case Instruction::BEQ:
case Instruction::BNE:
case Instruction::BLEZ:
case Instruction::BGTZ:
case Instruction::BEQL:
case Instruction::BNEL:
case Instruction::BLEZL:
case Instruction::BGTZL:
return true;
case Instruction::COP1:
if (instr.cop_rs() == 8)
return true;
return false;
default:
return false;
}
}
static bool InstrEndsBlock(const Instruction instr) { static bool InstrEndsBlock(const Instruction instr) {
switch (instr.opcode()) { switch (instr.opcode()) {
case Instruction::SPECIAL: case Instruction::SPECIAL:
@@ -31,7 +59,28 @@ static bool InstrEndsBlock(const Instruction instr) {
case Instruction::BNE: case Instruction::BNE:
case Instruction::BLEZ: case Instruction::BLEZ:
case Instruction::BGTZ: case Instruction::BGTZ:
case Instruction::BEQL:
case Instruction::BNEL:
case Instruction::BLEZL:
case Instruction::BGTZL:
return true; return true;
case Instruction::COP1:
if (instr.cop_rs() == 8)
return true;
return false;
case Instruction::COP0:
switch (instr.cop_rs()) {
case 0x10 ... 0x1F:
switch (instr.cop_funct()) {
case 0x18: // eret
return true;
default:
return false;
}
default:
return false;
}
default: default:
return false; return false;
} }
@@ -55,16 +104,10 @@ static bool IsBranchLikely(const Instruction instr) {
return false; return false;
} }
case Instruction::COP1: case Instruction::COP1:
{ if (instr.cop_rs() == 8 && (instr.cop_rt() == 2 || instr.cop_rt() == 3))
if (instr.cop_rs() == 0x08) {
if (instr.cop_rt() == 2 || instr.cop_rt() == 3)
return true; return true;
return false; return false;
}
return false;
}
default: default:
return false; return false;
} }
+6 -6
View File
@@ -930,7 +930,7 @@ void JIT::lb(const Instruction instr) {
void JIT::ld(const Instruction instr) { void JIT::ld(const Instruction instr) {
if (regs.IsRegConstant(instr.rs())) { if (regs.IsRegConstant(instr.rs())) {
const s64 address = regs.Read<s64>(instr.rs()) + (s16)instr; const s64 address = regs.Read<s64>(instr.rs()) + (s16)instr;
if (check_address_error(0b111, address)) { if (Core::IsAddressError(0b111, address)) {
// regs.cop0.HandleTLBException(address); // regs.cop0.HandleTLBException(address);
// regs.cop0.FireException(Cop0::ExceptionCode::AddressErrorLoad, 0, regs.oldPC); // regs.cop0.FireException(Cop0::ExceptionCode::AddressErrorLoad, 0, regs.oldPC);
// return; // return;
@@ -1014,7 +1014,7 @@ void JIT::ldr(const Instruction instr) {
void JIT::lh(const Instruction instr) { void JIT::lh(const Instruction instr) {
if (regs.IsRegConstant(instr.rs())) { if (regs.IsRegConstant(instr.rs())) {
const u64 address = regs.Read<s64>(instr.rs()) + (s16)instr; const u64 address = regs.Read<s64>(instr.rs()) + (s16)instr;
if (check_address_error(0b1, address)) { if (Core::IsAddressError(0b1, address)) {
// regs.cop0.HandleTLBException(address); // regs.cop0.HandleTLBException(address);
// regs.cop0.FireException(Cop0::ExceptionCode::AddressErrorLoad, 0, regs.oldPC); // regs.cop0.FireException(Cop0::ExceptionCode::AddressErrorLoad, 0, regs.oldPC);
// return; // return;
@@ -1043,7 +1043,7 @@ void JIT::lhu(const Instruction instr) {
u32 paddr; u32 paddr;
if (regs.IsRegConstant(instr.rs())) { if (regs.IsRegConstant(instr.rs())) {
const s64 address = regs.Read<s64>(instr.rs()) + (s16)instr; const s64 address = regs.Read<s64>(instr.rs()) + (s16)instr;
if (check_address_error(0b1, address)) { if (Core::IsAddressError(0b1, address)) {
regs.cop0.HandleTLBException(address); regs.cop0.HandleTLBException(address);
regs.cop0.FireException(Cop0::ExceptionCode::AddressErrorLoad, 0, regs.oldPC); regs.cop0.FireException(Cop0::ExceptionCode::AddressErrorLoad, 0, regs.oldPC);
return; return;
@@ -1080,7 +1080,7 @@ void JIT::lw(const Instruction instr) {
u32 paddr = 0; u32 paddr = 0;
if (regs.IsRegConstant(instr.rs())) { if (regs.IsRegConstant(instr.rs())) {
const u64 address = regs.Read<s64>(instr.rs()) + offset; const u64 address = regs.Read<s64>(instr.rs()) + offset;
if (check_address_error(0b11, address)) { if (Core::IsAddressError(0b11, address)) {
// regs.cop0.HandleTLBException(address); // regs.cop0.HandleTLBException(address);
// regs.cop0.FireException(Cop0::ExceptionCode::AddressErrorLoad, 0, regs.oldPC); // regs.cop0.FireException(Cop0::ExceptionCode::AddressErrorLoad, 0, regs.oldPC);
// return; // return;
@@ -1344,7 +1344,7 @@ void JIT::sw(const Instruction instr) {
if (regs.IsRegConstant(instr.rs(), instr.rt())) { if (regs.IsRegConstant(instr.rs(), instr.rt())) {
const s16 offset = instr; const s16 offset = instr;
const u64 address = regs.Read<s64>(instr.rs()) + offset; const u64 address = regs.Read<s64>(instr.rs()) + offset;
if (check_address_error(0b11, address)) { if (Core::IsAddressError(0b11, address)) {
// regs.cop0.HandleTLBException(address); // regs.cop0.HandleTLBException(address);
// regs.cop0.FireException(Cop0::ExceptionCode::AddressErrorStore, 0, regs.oldPC); // regs.cop0.FireException(Cop0::ExceptionCode::AddressErrorStore, 0, regs.oldPC);
panic("[JIT]: Unhandled ADES exception in SW!"); panic("[JIT]: Unhandled ADES exception in SW!");
@@ -1367,7 +1367,7 @@ void JIT::sw(const Instruction instr) {
if (regs.IsRegConstant(instr.rs())) { if (regs.IsRegConstant(instr.rs())) {
const s16 offset = instr; const s16 offset = instr;
const u64 address = regs.Read<s64>(instr.rs()) + offset; const u64 address = regs.Read<s64>(instr.rs()) + offset;
if (check_address_error(0b11, address)) { if (Core::IsAddressError(0b11, address)) {
// regs.cop0.HandleTLBException(address); // regs.cop0.HandleTLBException(address);
// regs.cop0.FireException(Cop0::ExceptionCode::AddressErrorStore, 0, regs.oldPC); // regs.cop0.FireException(Cop0::ExceptionCode::AddressErrorStore, 0, regs.oldPC);
panic("[JIT]: Unhandled ADES exception in SW!"); panic("[JIT]: Unhandled ADES exception in SW!");
+3 -2
View File
@@ -51,9 +51,10 @@ auto PI::BusRead<u8, true>(u32 addr) -> u8 {
n64::Mem &mem = n64::Core::GetMem(); n64::Mem &mem = n64::Core::GetMem();
switch (addr) { switch (addr) {
case REGION_PI_UNKNOWN: case REGION_PI_UNKNOWN:
mem.DumpRDRAM();
panic("Reading byte from address 0x{:08X} in unsupported region: REGION_PI_UNKNOWN - This is the N64DD, " panic("Reading byte from address 0x{:08X} in unsupported region: REGION_PI_UNKNOWN - This is the N64DD, "
"returning FF because it is not emulated", "returning FF because it is not emulated (pc: 0x{:016X})",
addr); addr, (u64)Core::GetRegs().oldPC);
case REGION_PI_64DD_REG: case REGION_PI_64DD_REG:
panic("Reading byte from address 0x{:08X} in unsupported region: REGION_PI_64DD_REG - This is the N64DD, " panic("Reading byte from address 0x{:08X} in unsupported region: REGION_PI_64DD_REG - This is the N64DD, "
"returning FF because it is not emulated", "returning FF because it is not emulated",
+3 -2
View File
@@ -363,13 +363,14 @@ bool Cop0::ProbeTLB(const TLBAccessType accessType, const u64 vaddr, u32 &paddr)
void Cop0::FireException(const ExceptionCode code, const int cop, s64 pc) { void Cop0::FireException(const ExceptionCode code, const int cop, s64 pc) {
Registers &regs = Core::GetRegs(); Registers &regs = Core::GetRegs();
Core::GetInstance().interpreter.SignalException(pc);
u16 vectorOffset = 0x0180; u16 vectorOffset = 0x0180;
if (tlbError == MISS && (code == ExceptionCode::TLBLoad || code == ExceptionCode::TLBStore)) { if (tlbError == MISS && (code == ExceptionCode::TLBLoad || code == ExceptionCode::TLBStore)) {
if (!status.exl) { if (!status.exl) {
vectorOffset = 0x0000;
if (is64BitAddressing) if (is64BitAddressing)
vectorOffset = 0x0080; vectorOffset = 0x0080;
else
vectorOffset = 0x0000;
} }
} }
+64 -36
View File
@@ -5,7 +5,9 @@
#include <ImGuiImpl/StatusBar.hpp> #include <ImGuiImpl/StatusBar.hpp>
#include <resources/gamecontrollerdb.h> #include <resources/gamecontrollerdb.h>
KaizenGui::KaizenGui() noexcept : window("Kaizen " KAIZEN_VERSION_STR, 1280, 720), settingsWindow(window), vulkanWidget(window.getHandle()), emuThread(fpsCounter, settingsWindow) { KaizenGui::KaizenGui() noexcept :
window("Kaizen " KAIZEN_VERSION_STR, 1280, 720), settingsWindow(window), vulkanWidget(window.getHandle()),
emuThread(fpsCounter, settingsWindow) {
gui::Initialize(n64::Core::GetInstance().parallel.wsi, window.getHandle()); gui::Initialize(n64::Core::GetInstance().parallel.wsi, window.getHandle());
SDL_InitSubSystem(SDL_INIT_GAMEPAD); SDL_InitSubSystem(SDL_INIT_GAMEPAD);
@@ -33,7 +35,8 @@ void KaizenGui::QueryDevices(const SDL_Event &event) {
if (gamepad) if (gamepad)
SDL_CloseGamepad(gamepad); SDL_CloseGamepad(gamepad);
break; break;
default: break; default:
break;
} }
} }
@@ -45,11 +48,16 @@ void KaizenGui::HandleInput(const SDL_Event &event) {
if (!gamepad) if (!gamepad)
break; break;
{ {
pif.UpdateButton(0, n64::Controller::Key::Z, SDL_GetGamepadAxis(gamepad, SDL_GAMEPAD_AXIS_LEFT_TRIGGER) == SDL_JOYSTICK_AXIS_MAX); pif.UpdateButton(0, n64::Controller::Key::Z,
pif.UpdateButton(0, n64::Controller::Key::CUp, SDL_GetGamepadAxis(gamepad, SDL_GAMEPAD_AXIS_RIGHTY) <= -127); SDL_GetGamepadAxis(gamepad, SDL_GAMEPAD_AXIS_LEFT_TRIGGER) == SDL_JOYSTICK_AXIS_MAX);
pif.UpdateButton(0, n64::Controller::Key::CDown, SDL_GetGamepadAxis(gamepad, SDL_GAMEPAD_AXIS_RIGHTY) >= 127); pif.UpdateButton(0, n64::Controller::Key::CUp,
pif.UpdateButton(0, n64::Controller::Key::CLeft, SDL_GetGamepadAxis(gamepad, SDL_GAMEPAD_AXIS_RIGHTX) <= -127); SDL_GetGamepadAxis(gamepad, SDL_GAMEPAD_AXIS_RIGHTY) <= -127);
pif.UpdateButton(0, n64::Controller::Key::CRight, SDL_GetGamepadAxis(gamepad, SDL_GAMEPAD_AXIS_RIGHTX) >= 127); pif.UpdateButton(0, n64::Controller::Key::CDown,
SDL_GetGamepadAxis(gamepad, SDL_GAMEPAD_AXIS_RIGHTY) >= 127);
pif.UpdateButton(0, n64::Controller::Key::CLeft,
SDL_GetGamepadAxis(gamepad, SDL_GAMEPAD_AXIS_RIGHTX) <= -127);
pif.UpdateButton(0, n64::Controller::Key::CRight,
SDL_GetGamepadAxis(gamepad, SDL_GAMEPAD_AXIS_RIGHTX) >= 127);
float xclamped = SDL_GetGamepadAxis(gamepad, SDL_GAMEPAD_AXIS_LEFTX); float xclamped = SDL_GetGamepadAxis(gamepad, SDL_GAMEPAD_AXIS_LEFTX);
if (xclamped < 0) { if (xclamped < 0) {
@@ -134,16 +142,21 @@ void KaizenGui::HandleInput(const SDL_Event &event) {
float x = 0, y = 0; float x = 0, y = 0;
if (keys[SDL_SCANCODE_UP]) y = 86; if (keys[SDL_SCANCODE_UP])
if (keys[SDL_SCANCODE_DOWN]) y = -86; y = 86;
if (keys[SDL_SCANCODE_LEFT]) x = -86; if (keys[SDL_SCANCODE_DOWN])
if (keys[SDL_SCANCODE_RIGHT]) x = 86; y = -86;
if (keys[SDL_SCANCODE_LEFT])
x = -86;
if (keys[SDL_SCANCODE_RIGHT])
x = 86;
pif.UpdateAxis(0, n64::Controller::Axis::X, x); pif.UpdateAxis(0, n64::Controller::Axis::X, x);
pif.UpdateAxis(0, n64::Controller::Axis::Y, y); pif.UpdateAxis(0, n64::Controller::Axis::Y, y);
} }
break; break;
default: break; default:
break;
} }
} }
@@ -156,9 +169,9 @@ std::pair<std::optional<s64>, std::optional<Util::Error::MemoryAccess>> RenderEr
auto memoryAccess = Util::Error::GetMemoryAccess(); auto memoryAccess = Util::Error::GetMemoryAccess();
if (memoryAccess.has_value()) { if (memoryAccess.has_value()) {
const auto [is_write, size, address, written_val] = memoryAccess.value(); const auto [is_write, size, address, written_val] = memoryAccess.value();
ImGui::Text("%s", std::format("{} {}-bit value @ {:08X}{}", is_write ? "Writing" : "Reading", ImGui::Text("%s",
static_cast<u8>(size), address, std::format("{} {}-bit value @ {:08X}{}", is_write ? "Writing" : "Reading", static_cast<u8>(size),
is_write ? std::format(" (value = 0x{:X})", written_val) : "") address, is_write ? std::format(" (value = 0x{:X})", written_val) : "")
.c_str()); .c_str());
} }
@@ -260,7 +273,8 @@ void KaizenGui::RenderUI() {
if (ImGui::BeginPopupModal(Util::Error::GetSeverity().as_c_str(), nullptr, ImGuiWindowFlags_AlwaysAutoResize)) { if (ImGui::BeginPopupModal(Util::Error::GetSeverity().as_c_str(), nullptr, ImGuiWindowFlags_AlwaysAutoResize)) {
emuThread.TogglePause(); emuThread.TogglePause();
switch (Util::Error::GetSeverity().as_enum) { switch (Util::Error::GetSeverity().as_enum) {
case Util::Error::Severity::WARN: { case Util::Error::Severity::WARN:
{
ImGui::PushStyleColor(ImGuiCol_TitleBg, 0x8054eae5); ImGui::PushStyleColor(ImGuiCol_TitleBg, 0x8054eae5);
ImGui::PushStyleColor(ImGuiCol_Text, 0xff7be4e1); ImGui::PushStyleColor(ImGuiCol_Text, 0xff7be4e1);
ImGui::Text("Warning of type: %s", Util::Error::GetType().as_c_str()); ImGui::Text("Warning of type: %s", Util::Error::GetType().as_c_str());
@@ -270,8 +284,10 @@ void KaizenGui::RenderUI() {
RenderErrorMessageDetails(); RenderErrorMessageDetails();
if (n64::Core::GetInstance().romLoaded && !n64::Core::GetInstance().pause) { if (n64::Core::GetInstance().romLoaded && !n64::Core::GetInstance().pause) {
const bool ignore = ImGui::Button("Try continuing"); ImGui::SameLine(); const bool ignore = ImGui::Button("Try continuing");
const bool stop = ImGui::Button("Stop emulation"); ImGui::SameLine(); ImGui::SameLine();
const bool stop = ImGui::Button("Stop emulation");
ImGui::SameLine();
const bool chooseAnother = ImGui::Button("Choose another ROM"); const bool chooseAnother = ImGui::Button("Choose another ROM");
if (ignore || stop || chooseAnother) { if (ignore || stop || chooseAnother) {
Util::Error::SetHandled(); Util::Error::SetHandled();
@@ -294,8 +310,10 @@ void KaizenGui::RenderUI() {
if (ImGui::Button("OK")) if (ImGui::Button("OK"))
ImGui::CloseCurrentPopup(); ImGui::CloseCurrentPopup();
} break; }
case Util::Error::Severity::UNRECOVERABLE: { break;
case Util::Error::Severity::UNRECOVERABLE:
{
emuThread.Stop(); emuThread.Stop();
ImGui::PushStyleColor(ImGuiCol_TitleBg, 0x800000ff); ImGui::PushStyleColor(ImGuiCol_TitleBg, 0x800000ff);
ImGui::PushStyleColor(ImGuiCol_Text, 0xff3b3bbf); ImGui::PushStyleColor(ImGuiCol_Text, 0xff3b3bbf);
@@ -307,8 +325,10 @@ void KaizenGui::RenderUI() {
RenderErrorMessageDetails(); RenderErrorMessageDetails();
if (ImGui::Button("OK")) if (ImGui::Button("OK"))
ImGui::CloseCurrentPopup(); ImGui::CloseCurrentPopup();
} break; }
case Util::Error::Severity::NON_FATAL: { break;
case Util::Error::Severity::NON_FATAL:
{
ImGui::PushStyleColor(ImGuiCol_TitleBg, 0x800000ff); ImGui::PushStyleColor(ImGuiCol_TitleBg, 0x800000ff);
ImGui::PushStyleColor(ImGuiCol_Text, 0xff3b3bbf); ImGui::PushStyleColor(ImGuiCol_Text, 0xff3b3bbf);
ImGui::Text("An error has occurred!"); ImGui::Text("An error has occurred!");
@@ -318,10 +338,13 @@ void KaizenGui::RenderUI() {
ImGui::Text(R"(Error message: "%s")", Util::Error::GetError().c_str()); ImGui::Text(R"(Error message: "%s")", Util::Error::GetError().c_str());
auto [lastPC, memoryAccess] = RenderErrorMessageDetails(); auto [lastPC, memoryAccess] = RenderErrorMessageDetails();
const bool ignore = ImGui::Button("Try continuing"); ImGui::SameLine(); const bool ignore = ImGui::Button("Try continuing");
const bool stop = ImGui::Button("Stop emulation"); ImGui::SameLine(); ImGui::SameLine();
const bool stop = ImGui::Button("Stop emulation");
ImGui::SameLine();
const bool chooseAnother = ImGui::Button("Choose another ROM"); const bool chooseAnother = ImGui::Button("Choose another ROM");
const bool openInDebugger = lastPC.has_value() ? ImGui::Button("Add breakpoint at this PC and open the debugger") : false; const bool openInDebugger =
lastPC.has_value() ? ImGui::Button("Add breakpoint at this PC and open the debugger") : false;
if (ignore || stop || chooseAnother || openInDebugger) { if (ignore || stop || chooseAnother || openInDebugger) {
Util::Error::SetHandled(); Util::Error::SetHandled();
ImGui::CloseCurrentPopup(); ImGui::CloseCurrentPopup();
@@ -346,8 +369,10 @@ void KaizenGui::RenderUI() {
debugger.Open(); debugger.Open();
emuThread.Reset(); emuThread.Reset();
} }
} break; }
default: break; break;
default:
break;
} }
ImGui::EndPopup(); ImGui::EndPopup();
@@ -359,7 +384,8 @@ void KaizenGui::RenderUI() {
} }
if (shouldDisplaySpinner) { if (shouldDisplaySpinner) {
ImGui::SetNextWindowPos({static_cast<float>(width) * 0.5f, static_cast<float>(height) * 0.5f}, 0, ImVec2(0.5f, 0.5f)); ImGui::SetNextWindowPos({static_cast<float>(width) * 0.5f, static_cast<float>(height) * 0.5f}, 0,
ImVec2(0.5f, 0.5f));
ImGui::PushStyleColor(ImGuiCol_WindowBg, IM_COL32_BLACK_TRANS); ImGui::PushStyleColor(ImGuiCol_WindowBg, IM_COL32_BLACK_TRANS);
ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 0.0f); ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 0.0f);
@@ -386,8 +412,11 @@ void KaizenGui::RenderUI() {
if (fileDialogOpen) { if (fileDialogOpen) {
fileDialogOpen = false; fileDialogOpen = false;
constexpr SDL_DialogFileFilter filters[] = {{"All files", "*"}, {"Nintendo 64 executable", "n64;z64;v64"}, {"Nintendo 64 executable archive", "rar;tar;zip;7z"}}; constexpr SDL_DialogFileFilter filters[] = {{"All files", "*"},
SDL_ShowOpenFileDialog([](void *userdata, const char * const *filelist, int) { {"Nintendo 64 executable", "n64;z64;v64"},
{"Nintendo 64 executable archive", "rar;tar;zip;7z"}};
SDL_ShowOpenFileDialog(
[](void *userdata, const char *const *filelist, int) {
auto kaizen = static_cast<KaizenGui *>(userdata); auto kaizen = static_cast<KaizenGui *>(userdata);
if (!filelist) { if (!filelist) {
@@ -402,10 +431,10 @@ void KaizenGui::RenderUI() {
kaizen->fileToLoad = *filelist; kaizen->fileToLoad = *filelist;
kaizen->shouldDisplaySpinner = true; kaizen->shouldDisplaySpinner = true;
std::thread fileWorker(&KaizenGui::FileWorker, kaizen); std::thread fileWorker(&KaizenGui::FileWorker, kaizen);
fileWorker.detach(); fileWorker.detach();
}, this, window.getHandle(), filters, 3, nullptr, false); },
this, window.getHandle(), filters, 3, nullptr, false);
} }
if (minimized) if (minimized)
@@ -442,7 +471,8 @@ void KaizenGui::run() {
case SDL_EVENT_WINDOW_RESTORED: case SDL_EVENT_WINDOW_RESTORED:
minimized = false; minimized = false;
break; break;
default: break; default:
break;
} }
QueryDevices(e); QueryDevices(e);
HandleInput(e); HandleInput(e);
@@ -455,6 +485,4 @@ void KaizenGui::run() {
} }
} }
void KaizenGui::LoadTAS(const std::string &path) noexcept { void KaizenGui::LoadTAS(const std::string &path) noexcept { n64::Core::GetInstance().LoadTAS(fs::path(path)); }
n64::Core::GetInstance().LoadTAS(fs::path(path));
}
+2 -1
View File
@@ -37,12 +37,13 @@ private:
void HandleInput(const SDL_Event &event); void HandleInput(const SDL_Event &event);
void QueryDevices(const SDL_Event &event); void QueryDevices(const SDL_Event &event);
[[noreturn]] void FileWorker() { void FileWorker() {
while (true) { while (true) {
if (!fileToLoad.empty()) { if (!fileToLoad.empty()) {
LoadROM(fileToLoad); LoadROM(fileToLoad);
shouldDisplaySpinner = false; shouldDisplaySpinner = false;
fileToLoad = ""; fileToLoad = "";
return;
} }
} }
} }
+11 -2
View File
@@ -2,9 +2,13 @@
#include <Options.hpp> #include <Options.hpp>
#include <log.hpp> #include <log.hpp>
#include <imgui.h> #include <imgui.h>
#include <Core.hpp>
CPUSettings::CPUSettings() { CPUSettings::CPUSettings() {
if (Options::GetInstance().GetValue<std::string>("cpu", "type") == "jit") { auto selectedCpuType = Options::GetInstance().GetValue<std::string>("cpu", "type");
if (selectedCpuType == "jit") {
selectedCpuTypeIndex = 2;
} else if (selectedCpuType == "cached_interpreter") {
selectedCpuTypeIndex = 1; selectedCpuTypeIndex = 1;
} else { } else {
selectedCpuTypeIndex = 0; selectedCpuTypeIndex = 0;
@@ -12,7 +16,7 @@ CPUSettings::CPUSettings() {
} }
void CPUSettings::render() { void CPUSettings::render() {
const char *items[] = {"Interpreter", const char *items[] = {"Interpreter", "Cached Interpreter",
#ifdef KAIZEN_JIT_ENABLED #ifdef KAIZEN_JIT_ENABLED
"Dynamic Recompiler" "Dynamic Recompiler"
#endif #endif
@@ -37,8 +41,13 @@ void CPUSettings::render() {
if (modified) { if (modified) {
if (selectedCpuTypeIndex == 0) { if (selectedCpuTypeIndex == 0) {
Options::GetInstance().SetValue<std::string>("cpu", "type", "interpreter"); Options::GetInstance().SetValue<std::string>("cpu", "type", "interpreter");
n64::Core::GetInstance().cpuType = n64::Core::Interpreted;
} else if (selectedCpuTypeIndex == 1) {
Options::GetInstance().SetValue<std::string>("cpu", "type", "cached_interpreter");
n64::Core::GetInstance().cpuType = n64::Core::CachedInterpreter;
} else { } else {
Options::GetInstance().SetValue<std::string>("cpu", "type", "jit"); Options::GetInstance().SetValue<std::string>("cpu", "type", "jit");
n64::Core::GetInstance().cpuType = n64::Core::DynamicRecompiler;
} }
} }
} }
+2
View File
@@ -3,7 +3,9 @@
#include <common.hpp> #include <common.hpp>
namespace n64 { namespace n64 {
struct Instruction { struct Instruction {
Instruction() = default;
Instruction(u32 v) { instr.raw = v; } Instruction(u32 v) { instr.raw = v; }
void operator=(u32 v) { instr.raw = v; } void operator=(u32 v) { instr.raw = v; }
operator u32() const { return instr.raw; } operator u32() const { return instr.raw; }