Figure out why the program counter never stops increasing after a certain point
This commit is contained in:
@@ -4,6 +4,7 @@ saves/
|
|||||||
.cache/
|
.cache/
|
||||||
.vs/
|
.vs/
|
||||||
.vscode/
|
.vscode/
|
||||||
|
.zed/
|
||||||
out/
|
out/
|
||||||
*.toml
|
*.toml
|
||||||
*.ini
|
*.ini
|
||||||
|
|||||||
@@ -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");
|
||||||
}
|
}
|
||||||
@@ -63,7 +65,13 @@ void Core::LoadROM(const std::string &rom_) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
u32 Core::StepCPU() {
|
u32 Core::StepCPU() {
|
||||||
if (cpuType == Interpreted)
|
if (cpuType == Interpreted) {
|
||||||
|
auto taken = interpreter.Step() + regs.PopStalledCycles();
|
||||||
|
StepRSP(taken);
|
||||||
|
return taken;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cpuType == CachedInterpreter)
|
||||||
return interpreter.ExecuteCached() + regs.PopStalledCycles();
|
return interpreter.ExecuteCached() + regs.PopStalledCycles();
|
||||||
|
|
||||||
#ifdef KAIZEN_JIT_ENABLED
|
#ifdef KAIZEN_JIT_ENABLED
|
||||||
@@ -115,8 +123,6 @@ void Core::Run(const float volumeL, const float volumeR) {
|
|||||||
|
|
||||||
const u32 taken = StepCPU();
|
const u32 taken = StepCPU();
|
||||||
cycles += taken;
|
cycles += taken;
|
||||||
|
|
||||||
StepRSP(taken);
|
|
||||||
frameCycles += taken;
|
frameCycles += taken;
|
||||||
Scheduler::GetInstance().Tick(taken);
|
Scheduler::GetInstance().Tick(taken);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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();
|
||||||
|
|
||||||
|
|||||||
@@ -52,6 +52,18 @@ bool Interpreter::MaybeAdvance() {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
regs.push_to_stack_trace(regs.pc);
|
||||||
|
if ((u32)regs.pc == 0x4) {
|
||||||
|
auto ®s = Core::GetRegs();
|
||||||
|
std::sort(regs.stack_trace.begin(), regs.stack_trace.end());
|
||||||
|
|
||||||
|
std::println("Stack trace:");
|
||||||
|
for (int i = 0; i < regs.stack_trace.size(); i++) {
|
||||||
|
std::println(" [{:016X}]", regs.stack_trace[i]);
|
||||||
|
}
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
regs.oldPC = regs.pc;
|
regs.oldPC = regs.pc;
|
||||||
regs.pc = regs.nextPC;
|
regs.pc = regs.nextPC;
|
||||||
regs.nextPC += 4;
|
regs.nextPC += 4;
|
||||||
@@ -101,23 +113,22 @@ u32 DivideAddr(u32 addr, u32 &offset) {
|
|||||||
return addr / MAX_LINES_PER_BLOCK;
|
return addr / MAX_LINES_PER_BLOCK;
|
||||||
}
|
}
|
||||||
|
|
||||||
CachedLine *CachedState::GetLine(u64 addr) {
|
std::shared_ptr<CachedLine> CachedState::GetLine(u64 addr) {
|
||||||
u32 offset;
|
u32 offset;
|
||||||
u32 page = DivideAddr(addr, offset);
|
u32 page = DivideAddr(addr, offset);
|
||||||
if (blocks[page] && blocks[page]->valid)
|
if (blocks[page])
|
||||||
return &blocks[page]->lines[offset];
|
return blocks[page]->lines[offset];
|
||||||
|
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
void CachedState::InsertLine(u64 addr, const CachedLine &line) {
|
void CachedState::InsertLine(u64 addr, std::shared_ptr<CachedLine> line) {
|
||||||
u32 offset;
|
u32 offset;
|
||||||
u32 page = DivideAddr(addr, offset);
|
u32 page = DivideAddr(addr, offset);
|
||||||
|
|
||||||
if (!blocks[page])
|
if (!blocks[page])
|
||||||
blocks[page] = std::make_unique<CachedBlock>();
|
blocks[page] = std::make_unique<CachedBlock>();
|
||||||
|
|
||||||
blocks[page]->valid = true;
|
|
||||||
blocks[page]->lines[offset] = line;
|
blocks[page]->lines[offset] = line;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -125,8 +136,8 @@ void CachedState::EvictLine(u64 addr) {
|
|||||||
u32 offset;
|
u32 offset;
|
||||||
u32 page = DivideAddr(addr, offset);
|
u32 page = DivideAddr(addr, offset);
|
||||||
|
|
||||||
if (blocks[page] && blocks[page]->valid)
|
if (blocks[page])
|
||||||
blocks[page]->valid = false;
|
blocks[page] = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
u32 Interpreter::ExecuteCached() {
|
u32 Interpreter::ExecuteCached() {
|
||||||
@@ -141,14 +152,13 @@ u32 Interpreter::ExecuteCached() {
|
|||||||
|
|
||||||
Instruction instr = line->code[i];
|
Instruction instr = line->code[i];
|
||||||
DecodeExecute(instr);
|
DecodeExecute(instr);
|
||||||
if (IsBranchLikely(instr) && !regs.delaySlot) {
|
|
||||||
line->len -= 1; // Branch likely with false condition, it wasn't taken so don't execute the delay slot
|
Core::GetInstance().StepRSP(1);
|
||||||
// and remove it from the cache
|
|
||||||
if (line->len <= 0)
|
// Branch likely with false condition, it wasn't taken so don't execute the delay slot
|
||||||
line->len = 1;
|
if (IsBranchLikely(instr) && !regs.delaySlot)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if (line->cycles == 0)
|
if (line->cycles == 0)
|
||||||
Scheduler::GetInstance().SkipToNext();
|
Scheduler::GetInstance().SkipToNext();
|
||||||
@@ -182,7 +192,7 @@ u32 Interpreter::ExecuteCached() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
cachedState.InsertLine(block_addr, {code, i, i});
|
cachedState.InsertLine(block_addr, std::make_shared<CachedLine>(code, i, i));
|
||||||
|
|
||||||
return ExecuteCached();
|
return ExecuteCached();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,8 +15,7 @@ struct CachedLine {
|
|||||||
};
|
};
|
||||||
|
|
||||||
struct CachedBlock {
|
struct CachedBlock {
|
||||||
std::array<CachedLine, MAX_LINES_PER_BLOCK / 4> lines = {};
|
std::array<std::shared_ptr<CachedLine>, MAX_LINES_PER_BLOCK / 4> lines = {};
|
||||||
bool valid = false;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
using CachedBlocks = std::vector<std::unique_ptr<CachedBlock>>;
|
using CachedBlocks = std::vector<std::unique_ptr<CachedBlock>>;
|
||||||
@@ -34,8 +33,8 @@ struct CachedState {
|
|||||||
exception = false;
|
exception = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
CachedLine *GetLine(u64);
|
std::shared_ptr<CachedLine> GetLine(u64);
|
||||||
void InsertLine(u64, const CachedLine &);
|
void InsertLine(u64, std::shared_ptr<CachedLine>);
|
||||||
void EvictLine(u64);
|
void EvictLine(u64);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -83,13 +83,21 @@ 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 ®s = Core::GetRegs();
|
||||||
|
std::println("Stack trace:");
|
||||||
|
for (int i = 0; i < regs.stack_trace.size(); i++) {
|
||||||
|
std::println(" [{:016X}]", regs.stack_trace[i]);
|
||||||
|
}
|
||||||
|
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]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void RSP::WriteStatus(const u32 value) {
|
void RSP::WriteStatus(const u32 value) {
|
||||||
Mem& mem = Core::GetMem();
|
Mem &mem = Core::GetMem();
|
||||||
Registers& regs = Core::GetRegs();
|
Registers ®s = Core::GetRegs();
|
||||||
MI &mi = mem.mmio.mi;
|
MI &mi = mem.mmio.mi;
|
||||||
const auto write = SPStatusWrite{.raw = value};
|
const auto write = SPStatusWrite{.raw = value};
|
||||||
if (write.clearHalt && !write.setHalt) {
|
if (write.clearHalt && !write.setHalt) {
|
||||||
@@ -130,7 +138,7 @@ void RSP::WriteStatus(const u32 value) {
|
|||||||
|
|
||||||
template <>
|
template <>
|
||||||
void RSP::DMA<true>() {
|
void RSP::DMA<true>() {
|
||||||
Mem& mem = Core::GetMem();
|
Mem &mem = Core::GetMem();
|
||||||
u32 length = spDMALen.len + 1;
|
u32 length = spDMALen.len + 1;
|
||||||
|
|
||||||
length = (length + 0x7) & ~0x7;
|
length = (length + 0x7) & ~0x7;
|
||||||
@@ -163,7 +171,7 @@ void RSP::DMA<true>() {
|
|||||||
|
|
||||||
template <>
|
template <>
|
||||||
void RSP::DMA<false>() {
|
void RSP::DMA<false>() {
|
||||||
Mem& mem = Core::GetMem();
|
Mem &mem = Core::GetMem();
|
||||||
u32 length = spDMALen.len + 1;
|
u32 length = spDMALen.len + 1;
|
||||||
|
|
||||||
length = (length + 0x7) & ~0x7;
|
length = (length + 0x7) & ~0x7;
|
||||||
|
|||||||
@@ -6,7 +6,10 @@ namespace n64 {
|
|||||||
#ifdef KAIZEN_JIT_ENABLED
|
#ifdef KAIZEN_JIT_ENABLED
|
||||||
Registers::Registers(JIT &jit) : jit(jit) { Reset(); }
|
Registers::Registers(JIT &jit) : jit(jit) { Reset(); }
|
||||||
#else
|
#else
|
||||||
Registers::Registers() { Reset(); }
|
Registers::Registers() {
|
||||||
|
stack_trace.resize(0x10000000);
|
||||||
|
Reset();
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
void Registers::Reset() {
|
void Registers::Reset() {
|
||||||
@@ -14,6 +17,7 @@ void Registers::Reset() {
|
|||||||
lo = 0;
|
lo = 0;
|
||||||
delaySlot = false;
|
delaySlot = false;
|
||||||
prevDelaySlot = false;
|
prevDelaySlot = false;
|
||||||
|
std::fill(stack_trace.begin(), stack_trace.end(), 0);
|
||||||
gpr.fill(0);
|
gpr.fill(0);
|
||||||
regIsConstant = 1; // first bit is true indicating $zero is constant which yes it is always
|
regIsConstant = 1; // first bit is true indicating $zero is constant which yes it is always
|
||||||
|
|
||||||
|
|||||||
@@ -48,6 +48,14 @@ struct Registers {
|
|||||||
Cop0 cop0;
|
Cop0 cop0;
|
||||||
Cop1 cop1;
|
Cop1 cop1;
|
||||||
|
|
||||||
|
std::vector<u64> stack_trace;
|
||||||
|
int stack_count = 0;
|
||||||
|
|
||||||
|
void push_to_stack_trace(s64 val) {
|
||||||
|
stack_trace[stack_count++] = val;
|
||||||
|
stack_count %= stack_trace.size();
|
||||||
|
}
|
||||||
|
|
||||||
void CpuStall(u32 cycles) { extraCycles += cycles; }
|
void CpuStall(u32 cycles) { extraCycles += cycles; }
|
||||||
|
|
||||||
u32 PopStalledCycles() {
|
u32 PopStalledCycles() {
|
||||||
|
|||||||
@@ -4,7 +4,10 @@
|
|||||||
#include <imgui.h>
|
#include <imgui.h>
|
||||||
|
|
||||||
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 +15,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,6 +40,8 @@ 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");
|
||||||
|
} else if (selectedCpuTypeIndex == 1) {
|
||||||
|
Options::GetInstance().SetValue<std::string>("cpu", "type", "cached_interpreter");
|
||||||
} else {
|
} else {
|
||||||
Options::GetInstance().SetValue<std::string>("cpu", "type", "jit");
|
Options::GetInstance().SetValue<std::string>("cpu", "type", "jit");
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user