Cached interpreter plays Mario 64. Start looking into RSP as well
This commit is contained in:
@@ -4,6 +4,7 @@ saves/
|
||||
.cache/
|
||||
.vs/
|
||||
.vscode/
|
||||
.zed/
|
||||
out/
|
||||
*.toml
|
||||
*.ini
|
||||
@@ -27,3 +28,4 @@ compile_commands.json
|
||||
tests/
|
||||
.DS_Store
|
||||
resources/version.hpp
|
||||
__cmake_systeminformation/CMakeFiles/
|
||||
|
||||
@@ -16,6 +16,8 @@ Core::Core() :
|
||||
cpuType = Interpreted;
|
||||
} else if (selectedCpu == "jit") {
|
||||
cpuType = DynamicRecompiler;
|
||||
} else if (selectedCpu == "cached_interpreter") {
|
||||
cpuType = CachedInterpreter;
|
||||
} else {
|
||||
panic("Unimplemented CPU type");
|
||||
}
|
||||
@@ -66,6 +68,9 @@ u32 Core::StepCPU() {
|
||||
if (cpuType == Interpreted)
|
||||
return interpreter.Step() + regs.PopStalledCycles();
|
||||
|
||||
if (cpuType == CachedInterpreter)
|
||||
return interpreter.ExecuteCached() + regs.PopStalledCycles();
|
||||
|
||||
#ifdef KAIZEN_JIT_ENABLED
|
||||
if (cpuType == DynamicRecompiler)
|
||||
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) {
|
||||
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;) {
|
||||
Scheduler::GetInstance().HandleEvents();
|
||||
|
||||
const u32 taken = StepCPU();
|
||||
cycles += taken;
|
||||
|
||||
StepRSP(taken);
|
||||
frameCycles += taken;
|
||||
StepRSP(taken);
|
||||
Scheduler::GetInstance().Tick(taken);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
|
||||
namespace n64 {
|
||||
struct Core {
|
||||
enum CPUType { Interpreted, DynamicRecompiler, CachedInterpreter } cpuType = Interpreted;
|
||||
enum CPUType { Interpreted, DynamicRecompiler, CachedInterpreter } cpuType = CachedInterpreter;
|
||||
|
||||
explicit Core();
|
||||
|
||||
@@ -19,7 +19,11 @@ struct Core {
|
||||
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 Mem &GetMem() { return *GetInstance().mem; }
|
||||
|
||||
@@ -27,13 +27,9 @@ u64 Scheduler::Remove(const EventType eventType) const {
|
||||
return ret;
|
||||
}
|
||||
|
||||
void Scheduler::SkipToNext() {
|
||||
ticks = events.top().time;
|
||||
}
|
||||
void Scheduler::SkipToNext() { ticks = events.top().time; }
|
||||
|
||||
void Scheduler::Tick(const u64 t) {
|
||||
ticks += t;
|
||||
}
|
||||
void Scheduler::Tick(const u64 t) { ticks += t; }
|
||||
|
||||
void Scheduler::HandleEvents() {
|
||||
n64::Mem &mem = n64::Core::GetMem();
|
||||
|
||||
@@ -52,6 +52,7 @@ void DataCache::WriteBack<false>(u64 vaddr, u32 paddr) {
|
||||
|
||||
u32 origPhysAddr = (line.ptag << 12) | (paddr & 0xfff);
|
||||
u32 lineStart = GetDCacheLineStart(origPhysAddr);
|
||||
Core::GetInstance().interpreter.EvictCachedBlock(vaddr);
|
||||
for (int i = 0; i < 16; 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) {
|
||||
u32 origPhysAddr = (line.ptag << 12) | (paddr & 0xfff);
|
||||
u32 lineStart = GetICacheLineStart(origPhysAddr);
|
||||
Core::GetInstance().interpreter.EvictCachedBlock(vaddr);
|
||||
for (int i = 0; i < 16; i++) {
|
||||
mmio.rdp.WriteRDRAM(lineStart + i, line.data[i]);
|
||||
}
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
#include <Core.hpp>
|
||||
#include <Scheduler.hpp>
|
||||
#include "jit/helpers.hpp"
|
||||
|
||||
namespace n64 {
|
||||
Interpreter::Interpreter(Mem &mem, Registers ®s) : regs(regs), mem(mem) {}
|
||||
@@ -12,7 +14,7 @@ bool Interpreter::ShouldServiceInterrupt() const {
|
||||
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 &= 0x1FFFFFFFF;
|
||||
if (regs.cop0.count == static_cast<u64>(regs.cop0.compare) << 1) {
|
||||
@@ -21,38 +23,159 @@ void Interpreter::CheckCompareInterrupt() const {
|
||||
}
|
||||
}
|
||||
|
||||
u32 Interpreter::Step() {
|
||||
CheckCompareInterrupt();
|
||||
bool Interpreter::Fetch(Instruction &instr, u64 vaddr) {
|
||||
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.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.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()) {
|
||||
regs.cop0.FireException(Cop0::ExceptionCode::Interrupt, 0, regs.pc);
|
||||
return 1;
|
||||
return false;
|
||||
}
|
||||
|
||||
regs.oldPC = regs.pc;
|
||||
regs.pc = regs.nextPC;
|
||||
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;
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
@@ -1,36 +1,89 @@
|
||||
#pragma once
|
||||
#include <Cache.hpp>
|
||||
#include <Mem.hpp>
|
||||
#include <JITUtils.hpp>
|
||||
|
||||
namespace n64 {
|
||||
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 {
|
||||
explicit Interpreter(Mem &, Registers &);
|
||||
~Interpreter() = default;
|
||||
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:
|
||||
friend struct Cop1;
|
||||
friend struct Mem;
|
||||
|
||||
void MaybeIdleSkip();
|
||||
|
||||
InstructionCache icache;
|
||||
DataCache dcache;
|
||||
Registers ®s;
|
||||
Mem &mem;
|
||||
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;
|
||||
void CheckCompareInterrupt() const;
|
||||
void UpdateCompareInterrupt() const;
|
||||
|
||||
void cop2Decode(Instruction);
|
||||
void special(Instruction);
|
||||
void regimm(Instruction);
|
||||
void Exec(Instruction);
|
||||
void DecodeExecute(Instruction);
|
||||
void add(Instruction);
|
||||
void addu(Instruction);
|
||||
void addi(Instruction);
|
||||
|
||||
@@ -41,7 +41,7 @@ void JIT::InvalidateBlock(const u32 paddr) {
|
||||
std::optional<u32> JIT::FetchInstruction(s64 vaddr) {
|
||||
u32 paddr = 0;
|
||||
|
||||
if (check_address_error(0b11, vaddr)) [[unlikely]] {
|
||||
if (Core::IsAddressError(0b11, vaddr)) [[unlikely]] {
|
||||
/*regs.cop0.HandleTLBException(blockPC);
|
||||
regs.cop0.FireException(Cop0::ExceptionCode::AddressErrorLoad, 0, blockPC);
|
||||
return 1;*/
|
||||
|
||||
@@ -116,9 +116,6 @@ struct JIT final {
|
||||
void BranchAbsTaken(s64 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;
|
||||
void CheckCompareInterrupt() const;
|
||||
std::optional<u32> FetchInstruction(s64);
|
||||
|
||||
@@ -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
|
||||
+168
-169
@@ -4,75 +4,75 @@
|
||||
|
||||
namespace n64 {
|
||||
RDP::RDP() {
|
||||
rdram.resize(RDRAM_SIZE);
|
||||
Reset();
|
||||
rdram.resize(RDRAM_SIZE);
|
||||
Reset();
|
||||
}
|
||||
|
||||
void RDP::Reset() {
|
||||
dpc = {};
|
||||
dpc.status.raw = 0x80;
|
||||
std::ranges::fill(rdram, 0);
|
||||
std::ranges::fill(cmd_buf, 0);
|
||||
dpc = {};
|
||||
dpc.status.raw = 0x80;
|
||||
std::ranges::fill(rdram, 0);
|
||||
std::ranges::fill(cmd_buf, 0);
|
||||
}
|
||||
|
||||
template <>
|
||||
void RDP::WriteRDRAM<u8>(const size_t idx, const u8 v) {
|
||||
if (const size_t real = BYTE_ADDRESS(idx); real < RDRAM_SIZE) [[likely]] {
|
||||
rdram[real] = v;
|
||||
}
|
||||
if (const size_t real = BYTE_ADDRESS(idx); real < RDRAM_SIZE) [[likely]] {
|
||||
rdram[real] = v;
|
||||
}
|
||||
}
|
||||
|
||||
template <>
|
||||
void RDP::WriteRDRAM<u16>(const size_t idx, const u16 v) {
|
||||
if (const size_t real = HALF_ADDRESS(idx); real < RDRAM_SIZE) [[likely]] {
|
||||
ircolib::WriteAccess<u16>(rdram, real, v);
|
||||
}
|
||||
if (const size_t real = HALF_ADDRESS(idx); real < RDRAM_SIZE) [[likely]] {
|
||||
ircolib::WriteAccess<u16>(rdram, real, v);
|
||||
}
|
||||
}
|
||||
|
||||
template <>
|
||||
void RDP::WriteRDRAM<u32>(const size_t idx, const u32 v) {
|
||||
if (idx < RDRAM_SIZE) [[likely]] {
|
||||
ircolib::WriteAccess<u32>(rdram, idx, v);
|
||||
}
|
||||
if (idx < RDRAM_SIZE) [[likely]] {
|
||||
ircolib::WriteAccess<u32>(rdram, idx, v);
|
||||
}
|
||||
}
|
||||
|
||||
template <>
|
||||
void RDP::WriteRDRAM<u64>(const size_t idx, const u64 v) {
|
||||
if (idx < RDRAM_SIZE) [[likely]] {
|
||||
ircolib::WriteAccess<u64>(rdram, idx, v);
|
||||
}
|
||||
if (idx < RDRAM_SIZE) [[likely]] {
|
||||
ircolib::WriteAccess<u64>(rdram, idx, v);
|
||||
}
|
||||
}
|
||||
|
||||
template <>
|
||||
u8 RDP::ReadRDRAM<u8>(const size_t idx) {
|
||||
if (const size_t real = BYTE_ADDRESS(idx); real < RDRAM_SIZE) [[likely]]
|
||||
return rdram[real];
|
||||
if (const size_t real = BYTE_ADDRESS(idx); real < RDRAM_SIZE) [[likely]]
|
||||
return rdram[real];
|
||||
|
||||
return 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
template <>
|
||||
u16 RDP::ReadRDRAM<u16>(const size_t idx) {
|
||||
if (const size_t real = HALF_ADDRESS(idx); real < RDRAM_SIZE) [[likely]]
|
||||
return ircolib::ReadAccess<u16>(rdram, real);
|
||||
if (const size_t real = HALF_ADDRESS(idx); real < RDRAM_SIZE) [[likely]]
|
||||
return ircolib::ReadAccess<u16>(rdram, real);
|
||||
|
||||
return 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
template <>
|
||||
u32 RDP::ReadRDRAM<u32>(const size_t idx) {
|
||||
if (idx < RDRAM_SIZE) [[likely]]
|
||||
return ircolib::ReadAccess<u32>(rdram, idx);
|
||||
if (idx < RDRAM_SIZE) [[likely]]
|
||||
return ircolib::ReadAccess<u32>(rdram, idx);
|
||||
|
||||
return 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
template <>
|
||||
u64 RDP::ReadRDRAM<u64>(const size_t idx) {
|
||||
if (idx < RDRAM_SIZE) [[likely]]
|
||||
return ircolib::ReadAccess<u64>(rdram, idx);
|
||||
if (idx < RDRAM_SIZE) [[likely]]
|
||||
return ircolib::ReadAccess<u64>(rdram, idx);
|
||||
|
||||
return 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const int cmd_lens[64] = {2, 2, 2, 2, 2, 2, 2, 2, 8, 12, 24, 28, 24, 28, 40, 44, 2, 2, 2, 2, 2, 2,
|
||||
@@ -80,79 +80,78 @@ static const int cmd_lens[64] = {2, 2, 2, 2, 2, 2, 2, 2, 8, 12, 24, 28, 24, 28,
|
||||
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2};
|
||||
|
||||
auto RDP::Read(const u32 addr) const -> u32 {
|
||||
switch (addr) {
|
||||
case 0x04100000:
|
||||
return dpc.start;
|
||||
case 0x04100004:
|
||||
return dpc.end;
|
||||
case 0x04100008:
|
||||
return dpc.current;
|
||||
case 0x0410000C:
|
||||
return dpc.status.raw;
|
||||
case 0x04100010:
|
||||
return dpc.clock;
|
||||
case 0x04100014:
|
||||
return dpc.status.cmdBusy;
|
||||
case 0x04100018:
|
||||
return dpc.status.pipeBusy;
|
||||
case 0x0410001C:
|
||||
return dpc.tmem;
|
||||
default:
|
||||
panic("Unhandled DP Command Registers read (addr: {:08X})", addr);
|
||||
}
|
||||
switch (addr) {
|
||||
case 0x04100000:
|
||||
return dpc.start;
|
||||
case 0x04100004:
|
||||
return dpc.end;
|
||||
case 0x04100008:
|
||||
return dpc.current;
|
||||
case 0x0410000C:
|
||||
return dpc.status.raw;
|
||||
case 0x04100010:
|
||||
return dpc.clock;
|
||||
case 0x04100014:
|
||||
return dpc.status.cmdBusy;
|
||||
case 0x04100018:
|
||||
return dpc.status.pipeBusy;
|
||||
case 0x0410001C:
|
||||
return dpc.tmem;
|
||||
default:
|
||||
panic("Unhandled DP Command Registers read (addr: {:08X})", addr);
|
||||
}
|
||||
|
||||
return 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void RDP::Write(const u32 addr, const u32 val) {
|
||||
switch (addr) {
|
||||
case 0x04100000:
|
||||
WriteStart(val);
|
||||
break;
|
||||
case 0x04100004:
|
||||
WriteEnd(val);
|
||||
break;
|
||||
case 0x0410000C:
|
||||
WriteStatus(val);
|
||||
break;
|
||||
default:
|
||||
panic("Unhandled DP Command Registers write (addr: {:08X}, val: {:08X})", addr, val);
|
||||
}
|
||||
switch (addr) {
|
||||
case 0x04100000:
|
||||
WriteStart(val);
|
||||
break;
|
||||
case 0x04100004:
|
||||
WriteEnd(val);
|
||||
break;
|
||||
case 0x0410000C:
|
||||
WriteStatus(val);
|
||||
break;
|
||||
default:
|
||||
panic("Unhandled DP Command Registers write (addr: {:08X}, val: {:08X})", addr, val);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
void RDP::WriteStatus(const u32 val) {
|
||||
DPCStatusWrite temp{};
|
||||
temp.raw = val;
|
||||
bool unfrozen = false;
|
||||
DPCStatusWrite temp{};
|
||||
temp.raw = val;
|
||||
bool unfrozen = false;
|
||||
#define CLEAR_SET(val, clear, set) \
|
||||
do { \
|
||||
if ((clear)) \
|
||||
(val) = 0; \
|
||||
if ((set)) \
|
||||
(val) = 1; \
|
||||
} \
|
||||
while (0)
|
||||
do { \
|
||||
if ((clear)) \
|
||||
(val) = 0; \
|
||||
if ((set)) \
|
||||
(val) = 1; \
|
||||
} \
|
||||
while (0)
|
||||
|
||||
CLEAR_SET(dpc.status.xbusDmemDma, temp.clearXbusDmemDma, temp.setXbusDmemDma);
|
||||
if (temp.clearFreeze) {
|
||||
dpc.status.freeze = false;
|
||||
unfrozen = true;
|
||||
}
|
||||
CLEAR_SET(dpc.status.xbusDmemDma, temp.clearXbusDmemDma, temp.setXbusDmemDma);
|
||||
if (temp.clearFreeze) {
|
||||
dpc.status.freeze = false;
|
||||
unfrozen = true;
|
||||
}
|
||||
|
||||
if (temp.setFreeze) {
|
||||
dpc.status.freeze = true;
|
||||
}
|
||||
CLEAR_SET(dpc.status.flush, temp.clearFlush, temp.setFlush);
|
||||
CLEAR_SET(dpc.status.tmemBusy, temp.clearTmem, false);
|
||||
CLEAR_SET(dpc.status.pipeBusy, temp.clearPipe, false);
|
||||
CLEAR_SET(dpc.status.cmdBusy, temp.clearCmd, false);
|
||||
CLEAR_SET(dpc.clock, temp.clearClock, false);
|
||||
if (temp.setFreeze) {
|
||||
dpc.status.freeze = true;
|
||||
}
|
||||
CLEAR_SET(dpc.status.flush, temp.clearFlush, temp.setFlush);
|
||||
CLEAR_SET(dpc.status.tmemBusy, temp.clearTmem, false);
|
||||
CLEAR_SET(dpc.status.pipeBusy, temp.clearPipe, false);
|
||||
CLEAR_SET(dpc.status.cmdBusy, temp.clearCmd, false);
|
||||
CLEAR_SET(dpc.clock, temp.clearClock, false);
|
||||
|
||||
if (!unfrozen) {
|
||||
RunCommand();
|
||||
}
|
||||
if (!unfrozen) {
|
||||
RunCommand();
|
||||
}
|
||||
}
|
||||
/*
|
||||
FORCE_INLINE void logCommand(u8 cmd) {
|
||||
@@ -197,102 +196,102 @@ FORCE_INLINE void logCommand(u8 cmd) {
|
||||
*/
|
||||
|
||||
void RDP::RunCommand() {
|
||||
n64::Mem& mem = n64::Core::GetMem();
|
||||
ParallelRDP& parallel = n64::Core::GetInstance().parallel;
|
||||
if (dpc.status.freeze) {
|
||||
return;
|
||||
}
|
||||
dpc.status.pipeBusy = true;
|
||||
dpc.status.startGclk = true;
|
||||
if (dpc.end > dpc.current) {
|
||||
dpc.status.freeze = true;
|
||||
|
||||
static int remaining_cmds = 0;
|
||||
|
||||
const u32 current = dpc.current & 0xFFFFF8;
|
||||
const u32 end = dpc.end & 0xFFFFF8;
|
||||
|
||||
const auto len = static_cast<s32>(end) - static_cast<s32>(current);
|
||||
if (len <= 0)
|
||||
return;
|
||||
|
||||
if (len + remaining_cmds * 4 > COMMAND_BUFFER_SIZE) {
|
||||
panic("Too many RDP commands");
|
||||
return;
|
||||
}
|
||||
|
||||
if (dpc.status.xbusDmemDma) {
|
||||
for (int i = 0; i < len; i += 4) {
|
||||
const u32 cmd = ircolib::ReadAccess<u32>(mem.mmio.rsp.dmem, current + i & 0xFFF);
|
||||
cmd_buf[remaining_cmds + (i >> 2)] = cmd;
|
||||
}
|
||||
} else {
|
||||
if (end > 0x7FFFFFF || current > 0x7FFFFFF) { // if (end > RDRAM_DSIZE || current > RDRAM_DSIZE)
|
||||
n64::Mem &mem = n64::Core::GetMem();
|
||||
ParallelRDP ¶llel = n64::Core::GetInstance().parallel;
|
||||
if (dpc.status.freeze) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (int i = 0; i < len; i += 4) {
|
||||
const u32 cmd = ircolib::ReadAccess<u32>(rdram, current + i);
|
||||
cmd_buf[remaining_cmds + (i >> 2)] = cmd;
|
||||
}
|
||||
}
|
||||
dpc.status.pipeBusy = true;
|
||||
dpc.status.startGclk = true;
|
||||
if (dpc.end > dpc.current) {
|
||||
dpc.status.freeze = true;
|
||||
|
||||
const int word_len = (len >> 2) + remaining_cmds;
|
||||
int buf_index = 0;
|
||||
static int remaining_cmds = 0;
|
||||
|
||||
bool processed_all = true;
|
||||
const u32 current = dpc.current & 0xFFFFF8;
|
||||
const u32 end = dpc.end & 0xFFFFF8;
|
||||
|
||||
while (buf_index < word_len) {
|
||||
const u8 cmd = cmd_buf[buf_index] >> 24 & 0x3F;
|
||||
const auto len = static_cast<s32>(end) - static_cast<s32>(current);
|
||||
if (len <= 0)
|
||||
return;
|
||||
|
||||
const int cmd_len = cmd_lens[cmd];
|
||||
if ((buf_index + cmd_len) * 4 > len + remaining_cmds * 4) {
|
||||
remaining_cmds = word_len - buf_index;
|
||||
|
||||
u32 tmp[remaining_cmds];
|
||||
for (int i = 0; i < remaining_cmds; i++) {
|
||||
tmp[i] = cmd_buf[buf_index + i];
|
||||
if (len + remaining_cmds * 4 > COMMAND_BUFFER_SIZE) {
|
||||
panic("Too many RDP commands");
|
||||
return;
|
||||
}
|
||||
|
||||
for (int i = 0; i < remaining_cmds; i++) {
|
||||
cmd_buf[i] = tmp[i];
|
||||
if (dpc.status.xbusDmemDma) {
|
||||
for (int i = 0; i < len; i += 4) {
|
||||
const u32 cmd = ircolib::ReadAccess<u32>(mem.mmio.rsp.dmem, current + i & 0xFFF);
|
||||
cmd_buf[remaining_cmds + (i >> 2)] = cmd;
|
||||
}
|
||||
} else {
|
||||
if (end > 0x7FFFFFF || current > 0x7FFFFFF) { // if (end > RDRAM_DSIZE || current > RDRAM_DSIZE)
|
||||
return;
|
||||
}
|
||||
|
||||
for (int i = 0; i < len; i += 4) {
|
||||
const u32 cmd = ircolib::ReadAccess<u32>(rdram, current + i);
|
||||
cmd_buf[remaining_cmds + (i >> 2)] = cmd;
|
||||
}
|
||||
}
|
||||
|
||||
processed_all = false;
|
||||
break;
|
||||
}
|
||||
const int word_len = (len >> 2) + remaining_cmds;
|
||||
int buf_index = 0;
|
||||
|
||||
if (cmd >= 8) {
|
||||
parallel.EnqueueCommand(cmd_len, &cmd_buf[buf_index]);
|
||||
}
|
||||
bool processed_all = true;
|
||||
|
||||
if (cmd == 0x29) {
|
||||
OnFullSync();
|
||||
}
|
||||
while (buf_index < word_len) {
|
||||
const u8 cmd = cmd_buf[buf_index] >> 24 & 0x3F;
|
||||
|
||||
buf_index += cmd_len;
|
||||
const int cmd_len = cmd_lens[cmd];
|
||||
if ((buf_index + cmd_len) * 4 > len + remaining_cmds * 4) {
|
||||
remaining_cmds = word_len - buf_index;
|
||||
|
||||
u32 tmp[remaining_cmds];
|
||||
for (int i = 0; i < remaining_cmds; i++) {
|
||||
tmp[i] = cmd_buf[buf_index + i];
|
||||
}
|
||||
|
||||
for (int i = 0; i < remaining_cmds; i++) {
|
||||
cmd_buf[i] = tmp[i];
|
||||
}
|
||||
|
||||
processed_all = false;
|
||||
break;
|
||||
}
|
||||
|
||||
if (cmd >= 8) {
|
||||
parallel.EnqueueCommand(cmd_len, &cmd_buf[buf_index]);
|
||||
}
|
||||
|
||||
if (cmd == 0x29) {
|
||||
OnFullSync();
|
||||
}
|
||||
|
||||
buf_index += cmd_len;
|
||||
}
|
||||
|
||||
if (processed_all) {
|
||||
remaining_cmds = 0;
|
||||
}
|
||||
|
||||
dpc.current = end;
|
||||
dpc.end = end;
|
||||
dpc.status.freeze = false;
|
||||
}
|
||||
|
||||
if (processed_all) {
|
||||
remaining_cmds = 0;
|
||||
}
|
||||
|
||||
dpc.current = end;
|
||||
dpc.end = end;
|
||||
dpc.status.freeze = false;
|
||||
}
|
||||
dpc.status.cbufReady = true;
|
||||
dpc.status.cbufReady = true;
|
||||
}
|
||||
|
||||
void RDP::OnFullSync() {
|
||||
n64::Mem& mem = n64::Core::GetMem();
|
||||
ParallelRDP& parallel = n64::Core::GetInstance().parallel;
|
||||
n64::Mem &mem = n64::Core::GetMem();
|
||||
ParallelRDP ¶llel = n64::Core::GetInstance().parallel;
|
||||
|
||||
parallel.OnFullSync();
|
||||
parallel.OnFullSync();
|
||||
|
||||
dpc.status.pipeBusy = false;
|
||||
dpc.status.startGclk = false;
|
||||
dpc.status.cbufReady = false;
|
||||
mem.mmio.mi.InterruptRaise(MI::Interrupt::DP);
|
||||
dpc.status.pipeBusy = false;
|
||||
dpc.status.startGclk = false;
|
||||
dpc.status.cbufReady = false;
|
||||
mem.mmio.mi.InterruptRaise(MI::Interrupt::DP);
|
||||
}
|
||||
} // namespace n64
|
||||
|
||||
+157
-152
@@ -5,29 +5,29 @@ namespace n64 {
|
||||
RSP::RSP() { Reset(); }
|
||||
|
||||
void RSP::Reset() {
|
||||
lastSuccessfulSPAddr.raw = 0;
|
||||
lastSuccessfulDRAMAddr.raw = 0;
|
||||
spStatus.raw = 0;
|
||||
spStatus.halt = true;
|
||||
oldPC = 0;
|
||||
pc = 0;
|
||||
nextPC = 4;
|
||||
spDMASPAddr.raw = 0;
|
||||
spDMADRAMAddr.raw = 0;
|
||||
spDMALen.raw = 0;
|
||||
dmem = {};
|
||||
imem = {};
|
||||
memset(vpr, 0, 32 * sizeof(VPR));
|
||||
memset(gpr, 0, 32 * sizeof(u32));
|
||||
memset(&vce, 0, sizeof(VPR));
|
||||
memset(&acc, 0, 3 * sizeof(VPR));
|
||||
memset(&vcc, 0, 2 * sizeof(VPR));
|
||||
memset(&vco, 0, 2 * sizeof(VPR));
|
||||
semaphore = false;
|
||||
divIn = 0;
|
||||
divOut = 0;
|
||||
divInLoaded = false;
|
||||
steps = 0;
|
||||
lastSuccessfulSPAddr.raw = 0;
|
||||
lastSuccessfulDRAMAddr.raw = 0;
|
||||
spStatus.raw = 0;
|
||||
spStatus.halt = true;
|
||||
oldPC = 0;
|
||||
pc = 0;
|
||||
nextPC = 4;
|
||||
spDMASPAddr.raw = 0;
|
||||
spDMADRAMAddr.raw = 0;
|
||||
spDMALen.raw = 0;
|
||||
dmem = {};
|
||||
imem = {};
|
||||
memset(vpr, 0, 32 * sizeof(VPR));
|
||||
memset(gpr, 0, 32 * sizeof(u32));
|
||||
memset(&vce, 0, sizeof(VPR));
|
||||
memset(&acc, 0, 3 * sizeof(VPR));
|
||||
memset(&vcc, 0, 2 * sizeof(VPR));
|
||||
memset(&vco, 0, 2 * sizeof(VPR));
|
||||
semaphore = false;
|
||||
divIn = 0;
|
||||
divOut = 0;
|
||||
divInLoaded = false;
|
||||
steps = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -64,165 +64,170 @@ FORCE_INLINE void logRSP(const RSP& rsp, const u32 instr) {
|
||||
*/
|
||||
|
||||
auto RSP::Read(const u32 addr) -> u32 {
|
||||
switch (addr) {
|
||||
case 0x04040000:
|
||||
return lastSuccessfulSPAddr.raw & 0x1FF8;
|
||||
case 0x04040004:
|
||||
return lastSuccessfulDRAMAddr.raw & 0xFFFFF8;
|
||||
case 0x04040008:
|
||||
case 0x0404000C:
|
||||
return spDMALen.raw;
|
||||
case 0x04040010:
|
||||
return spStatus.raw;
|
||||
case 0x04040014:
|
||||
return spStatus.dmaFull;
|
||||
case 0x04040018:
|
||||
return 0;
|
||||
case 0x0404001C:
|
||||
return AcquireSemaphore();
|
||||
case 0x04080000:
|
||||
return pc & 0xFFC;
|
||||
default:
|
||||
panic("Unimplemented SP register read {:08X}", addr);
|
||||
}
|
||||
switch (addr) {
|
||||
case 0x04040000:
|
||||
return lastSuccessfulSPAddr.raw & 0x1FF8;
|
||||
case 0x04040004:
|
||||
return lastSuccessfulDRAMAddr.raw & 0xFFFFF8;
|
||||
case 0x04040008:
|
||||
case 0x0404000C:
|
||||
return spDMALen.raw;
|
||||
case 0x04040010:
|
||||
return spStatus.raw;
|
||||
case 0x04040014:
|
||||
return spStatus.dmaFull;
|
||||
case 0x04040018:
|
||||
return 0;
|
||||
case 0x0404001C:
|
||||
return AcquireSemaphore();
|
||||
case 0x04080000:
|
||||
return pc & 0xFFC;
|
||||
default:
|
||||
{
|
||||
auto ®s = 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]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void RSP::WriteStatus(const u32 value) {
|
||||
Mem& mem = Core::GetMem();
|
||||
Registers& regs = Core::GetRegs();
|
||||
MI &mi = mem.mmio.mi;
|
||||
const auto write = SPStatusWrite{.raw = value};
|
||||
if (write.clearHalt && !write.setHalt) {
|
||||
spStatus.halt = false;
|
||||
}
|
||||
if (write.setHalt && !write.clearHalt) {
|
||||
regs.steps = 0;
|
||||
spStatus.halt = true;
|
||||
}
|
||||
if (write.clearBroke)
|
||||
spStatus.broke = false;
|
||||
if (write.clearIntr && !write.setIntr)
|
||||
mi.InterruptLower(MI::Interrupt::SP);
|
||||
if (write.setIntr && !write.clearIntr)
|
||||
mi.InterruptRaise(MI::Interrupt::SP);
|
||||
Mem &mem = Core::GetMem();
|
||||
Registers ®s = Core::GetRegs();
|
||||
MI &mi = mem.mmio.mi;
|
||||
const auto write = SPStatusWrite{.raw = value};
|
||||
if (write.clearHalt && !write.setHalt) {
|
||||
spStatus.halt = false;
|
||||
}
|
||||
if (write.setHalt && !write.clearHalt) {
|
||||
regs.steps = 0;
|
||||
spStatus.halt = true;
|
||||
}
|
||||
if (write.clearBroke)
|
||||
spStatus.broke = false;
|
||||
if (write.clearIntr && !write.setIntr)
|
||||
mi.InterruptLower(MI::Interrupt::SP);
|
||||
if (write.setIntr && !write.clearIntr)
|
||||
mi.InterruptRaise(MI::Interrupt::SP);
|
||||
|
||||
#define CLEAR_SET(val, clear, set) \
|
||||
do { \
|
||||
if ((clear) && !(set)) \
|
||||
(val) = 0; \
|
||||
if ((set) && !(clear)) \
|
||||
(val) = 1; \
|
||||
} \
|
||||
while (0)
|
||||
do { \
|
||||
if ((clear) && !(set)) \
|
||||
(val) = 0; \
|
||||
if ((set) && !(clear)) \
|
||||
(val) = 1; \
|
||||
} \
|
||||
while (0)
|
||||
|
||||
CLEAR_SET(spStatus.singleStep, write.clearSstep, write.setSstep);
|
||||
CLEAR_SET(spStatus.interruptOnBreak, write.clearIntrOnBreak, write.setIntrOnBreak);
|
||||
CLEAR_SET(spStatus.signal0, write.clearSignal0, write.setSignal0);
|
||||
CLEAR_SET(spStatus.signal1, write.clearSignal1, write.setSignal1);
|
||||
CLEAR_SET(spStatus.signal2, write.clearSignal2, write.setSignal2);
|
||||
CLEAR_SET(spStatus.signal3, write.clearSignal3, write.setSignal3);
|
||||
CLEAR_SET(spStatus.signal4, write.clearSignal4, write.setSignal4);
|
||||
CLEAR_SET(spStatus.signal5, write.clearSignal5, write.setSignal5);
|
||||
CLEAR_SET(spStatus.signal6, write.clearSignal6, write.setSignal6);
|
||||
CLEAR_SET(spStatus.signal7, write.clearSignal7, write.setSignal7);
|
||||
CLEAR_SET(spStatus.singleStep, write.clearSstep, write.setSstep);
|
||||
CLEAR_SET(spStatus.interruptOnBreak, write.clearIntrOnBreak, write.setIntrOnBreak);
|
||||
CLEAR_SET(spStatus.signal0, write.clearSignal0, write.setSignal0);
|
||||
CLEAR_SET(spStatus.signal1, write.clearSignal1, write.setSignal1);
|
||||
CLEAR_SET(spStatus.signal2, write.clearSignal2, write.setSignal2);
|
||||
CLEAR_SET(spStatus.signal3, write.clearSignal3, write.setSignal3);
|
||||
CLEAR_SET(spStatus.signal4, write.clearSignal4, write.setSignal4);
|
||||
CLEAR_SET(spStatus.signal5, write.clearSignal5, write.setSignal5);
|
||||
CLEAR_SET(spStatus.signal6, write.clearSignal6, write.setSignal6);
|
||||
CLEAR_SET(spStatus.signal7, write.clearSignal7, write.setSignal7);
|
||||
#undef CLEAR_SET
|
||||
}
|
||||
|
||||
template <>
|
||||
void RSP::DMA<true>() {
|
||||
Mem& mem = Core::GetMem();
|
||||
u32 length = spDMALen.len + 1;
|
||||
Mem &mem = Core::GetMem();
|
||||
u32 length = spDMALen.len + 1;
|
||||
|
||||
length = (length + 0x7) & ~0x7;
|
||||
length = (length + 0x7) & ~0x7;
|
||||
|
||||
const auto &src = spDMASPAddr.bank ? imem : dmem;
|
||||
const auto &src = spDMASPAddr.bank ? imem : dmem;
|
||||
|
||||
u32 mem_address = spDMASPAddr.address & 0xFF8;
|
||||
u32 dram_address = spDMADRAMAddr.address & 0xFFFFF8;
|
||||
trace("SP DMA from RSP to RDRAM (size: {} B, {:08X} to {:08X})", length, mem_address, dram_address);
|
||||
u32 mem_address = spDMASPAddr.address & 0xFF8;
|
||||
u32 dram_address = spDMADRAMAddr.address & 0xFFFFF8;
|
||||
trace("SP DMA from RSP to RDRAM (size: {} B, {:08X} to {:08X})", length, mem_address, dram_address);
|
||||
|
||||
for (u32 i = 0; i < spDMALen.count + 1; i++) {
|
||||
for (u32 j = 0; j < length; j++) {
|
||||
mem.mmio.rdp.WriteRDRAM<u8>(BYTE_ADDRESS(dram_address + j), src[(mem_address + j) & DMEM_DSIZE]);
|
||||
for (u32 i = 0; i < spDMALen.count + 1; i++) {
|
||||
for (u32 j = 0; j < length; j++) {
|
||||
mem.mmio.rdp.WriteRDRAM<u8>(BYTE_ADDRESS(dram_address + j), src[(mem_address + j) & DMEM_DSIZE]);
|
||||
}
|
||||
|
||||
const int skip = i == spDMALen.count ? 0 : spDMALen.skip;
|
||||
|
||||
dram_address += (length + skip);
|
||||
dram_address &= 0xFFFFF8;
|
||||
mem_address += length;
|
||||
mem_address &= 0xFF8;
|
||||
}
|
||||
trace("Addresses after: RSP: 0x{:08X}, Dram: 0x{:08X}", mem_address, dram_address);
|
||||
|
||||
const int skip = i == spDMALen.count ? 0 : spDMALen.skip;
|
||||
|
||||
dram_address += (length + skip);
|
||||
dram_address &= 0xFFFFF8;
|
||||
mem_address += length;
|
||||
mem_address &= 0xFF8;
|
||||
}
|
||||
trace("Addresses after: RSP: 0x{:08X}, Dram: 0x{:08X}", mem_address, dram_address);
|
||||
|
||||
lastSuccessfulSPAddr.address = mem_address;
|
||||
lastSuccessfulSPAddr.bank = spDMASPAddr.bank;
|
||||
lastSuccessfulDRAMAddr.address = dram_address;
|
||||
spDMALen.raw = 0xFF8 | (spDMALen.skip << 20);
|
||||
lastSuccessfulSPAddr.address = mem_address;
|
||||
lastSuccessfulSPAddr.bank = spDMASPAddr.bank;
|
||||
lastSuccessfulDRAMAddr.address = dram_address;
|
||||
spDMALen.raw = 0xFF8 | (spDMALen.skip << 20);
|
||||
}
|
||||
|
||||
template <>
|
||||
void RSP::DMA<false>() {
|
||||
Mem& mem = Core::GetMem();
|
||||
u32 length = spDMALen.len + 1;
|
||||
Mem &mem = Core::GetMem();
|
||||
u32 length = spDMALen.len + 1;
|
||||
|
||||
length = (length + 0x7) & ~0x7;
|
||||
length = (length + 0x7) & ~0x7;
|
||||
|
||||
auto &dst = spDMASPAddr.bank ? imem : dmem;
|
||||
auto &dst = spDMASPAddr.bank ? imem : dmem;
|
||||
|
||||
u32 mem_address = spDMASPAddr.address & 0xFF8;
|
||||
u32 dram_address = spDMADRAMAddr.address & 0xFFFFF8;
|
||||
trace("SP DMA from RDRAM to RSP (size: {} B, {:08X} to {:08X})", length, dram_address, mem_address);
|
||||
u32 mem_address = spDMASPAddr.address & 0xFF8;
|
||||
u32 dram_address = spDMADRAMAddr.address & 0xFFFFF8;
|
||||
trace("SP DMA from RDRAM to RSP (size: {} B, {:08X} to {:08X})", length, dram_address, mem_address);
|
||||
|
||||
for (u32 i = 0; i < spDMALen.count + 1; i++) {
|
||||
for (u32 j = 0; j < length; j++) {
|
||||
dst[(mem_address + j) & DMEM_DSIZE] = mem.mmio.rdp.ReadRDRAM<u8>(BYTE_ADDRESS(dram_address + j));
|
||||
for (u32 i = 0; i < spDMALen.count + 1; i++) {
|
||||
for (u32 j = 0; j < length; j++) {
|
||||
dst[(mem_address + j) & DMEM_DSIZE] = mem.mmio.rdp.ReadRDRAM<u8>(BYTE_ADDRESS(dram_address + j));
|
||||
}
|
||||
|
||||
const int skip = i == spDMALen.count ? 0 : spDMALen.skip;
|
||||
|
||||
dram_address += (length + skip);
|
||||
dram_address &= 0xFFFFF8;
|
||||
mem_address += length;
|
||||
mem_address &= 0xFF8;
|
||||
}
|
||||
trace("Addresses after: RSP: 0x{:08X}, Dram: 0x{:08X}", mem_address, dram_address);
|
||||
|
||||
const int skip = i == spDMALen.count ? 0 : spDMALen.skip;
|
||||
|
||||
dram_address += (length + skip);
|
||||
dram_address &= 0xFFFFF8;
|
||||
mem_address += length;
|
||||
mem_address &= 0xFF8;
|
||||
}
|
||||
trace("Addresses after: RSP: 0x{:08X}, Dram: 0x{:08X}", mem_address, dram_address);
|
||||
|
||||
lastSuccessfulSPAddr.address = mem_address;
|
||||
lastSuccessfulSPAddr.bank = spDMASPAddr.bank;
|
||||
lastSuccessfulDRAMAddr.address = dram_address;
|
||||
spDMALen.raw = 0xFF8 | (spDMALen.skip << 20);
|
||||
lastSuccessfulSPAddr.address = mem_address;
|
||||
lastSuccessfulSPAddr.bank = spDMASPAddr.bank;
|
||||
lastSuccessfulDRAMAddr.address = dram_address;
|
||||
spDMALen.raw = 0xFF8 | (spDMALen.skip << 20);
|
||||
}
|
||||
|
||||
void RSP::Write(const u32 addr, const u32 val) {
|
||||
switch (addr) {
|
||||
case 0x04040000:
|
||||
spDMASPAddr.raw = val & 0x1FF8;
|
||||
break;
|
||||
case 0x04040004:
|
||||
spDMADRAMAddr.raw = val & 0xFFFFF8;
|
||||
break;
|
||||
case 0x04040008:
|
||||
spDMALen.raw = val;
|
||||
DMA<false>();
|
||||
break;
|
||||
case 0x0404000C:
|
||||
spDMALen.raw = val;
|
||||
DMA<true>();
|
||||
break;
|
||||
case 0x04040010:
|
||||
WriteStatus(val);
|
||||
break;
|
||||
case 0x0404001C:
|
||||
ReleaseSemaphore();
|
||||
break;
|
||||
case 0x04080000:
|
||||
if (spStatus.halt) {
|
||||
SetPC(val);
|
||||
switch (addr) {
|
||||
case 0x04040000:
|
||||
spDMASPAddr.raw = val & 0x1FF8;
|
||||
break;
|
||||
case 0x04040004:
|
||||
spDMADRAMAddr.raw = val & 0xFFFFF8;
|
||||
break;
|
||||
case 0x04040008:
|
||||
spDMALen.raw = val;
|
||||
DMA<false>();
|
||||
break;
|
||||
case 0x0404000C:
|
||||
spDMALen.raw = val;
|
||||
DMA<true>();
|
||||
break;
|
||||
case 0x04040010:
|
||||
WriteStatus(val);
|
||||
break;
|
||||
case 0x0404001C:
|
||||
ReleaseSemaphore();
|
||||
break;
|
||||
case 0x04080000:
|
||||
if (spStatus.halt) {
|
||||
SetPC(val);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
panic("Unimplemented SP register write {:08X}, val: {:08X}", addr, val);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
panic("Unimplemented SP register write {:08X}, val: {:08X}", addr, val);
|
||||
}
|
||||
}
|
||||
} // namespace n64
|
||||
|
||||
@@ -1310,6 +1310,7 @@ void Cop1::swc1(const Instruction instr) {
|
||||
regs.cop0.HandleTLBException(addr);
|
||||
regs.cop0.FireException(Cop0::GetTLBExceptionCode(regs.cop0.tlbError, Cop0::STORE), 0, regs.oldPC);
|
||||
} else {
|
||||
Core::GetInstance().interpreter.EvictCachedBlock(addr);
|
||||
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.FireException(Cop0::GetTLBExceptionCode(regs.cop0.tlbError, Cop0::STORE), 0, regs.oldPC);
|
||||
} else {
|
||||
Core::GetInstance().interpreter.EvictCachedBlock(addr);
|
||||
mem.Write(physical, FGR_T<u64>(regs.cop0.status, instr.ft()));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -249,7 +249,7 @@ void Interpreter::cop2Decode(const Instruction instr) {
|
||||
}
|
||||
}
|
||||
|
||||
void Interpreter::Exec(const Instruction instr) {
|
||||
void Interpreter::DecodeExecute(const Instruction instr) {
|
||||
// 00rr_rccc
|
||||
switch (instr.opcode()) {
|
||||
case Instruction::SPECIAL:
|
||||
|
||||
@@ -140,7 +140,6 @@ void Interpreter::branch(const bool cond, const s64 address) {
|
||||
regs.delaySlot = true;
|
||||
if (cond) {
|
||||
regs.nextPC = address;
|
||||
Core::MaybeIdleSkip();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -148,7 +147,6 @@ void Interpreter::branch_likely(const bool cond, const s64 address) {
|
||||
if (cond) {
|
||||
regs.delaySlot = true;
|
||||
regs.nextPC = address;
|
||||
Core::MaybeIdleSkip();
|
||||
} else {
|
||||
regs.SetPC64(regs.nextPC);
|
||||
}
|
||||
@@ -202,7 +200,7 @@ void Interpreter::lb(const Instruction instr) {
|
||||
|
||||
void Interpreter::lh(const Instruction 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.FireException(Cop0::ExceptionCode::AddressErrorLoad, 0, regs.oldPC);
|
||||
return;
|
||||
@@ -220,7 +218,7 @@ void Interpreter::lh(const Instruction instr) {
|
||||
void Interpreter::lw(const Instruction instr) {
|
||||
const s16 offset = instr;
|
||||
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.FireException(Cop0::ExceptionCode::AddressErrorLoad, 0, regs.oldPC);
|
||||
return;
|
||||
@@ -243,7 +241,7 @@ void Interpreter::ll(const Instruction instr) {
|
||||
regs.cop0.FireException(Cop0::GetTLBExceptionCode(regs.cop0.tlbError, Cop0::LOAD), 0, regs.oldPC);
|
||||
} else {
|
||||
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);
|
||||
return;
|
||||
}
|
||||
@@ -287,7 +285,7 @@ void Interpreter::lwr(const Instruction instr) {
|
||||
|
||||
void Interpreter::ld(const Instruction 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.FireException(Cop0::ExceptionCode::AddressErrorLoad, 0, regs.oldPC);
|
||||
return;
|
||||
@@ -315,7 +313,7 @@ void Interpreter::lld(const Instruction instr) {
|
||||
regs.cop0.HandleTLBException(address);
|
||||
regs.cop0.FireException(Cop0::GetTLBExceptionCode(regs.cop0.tlbError, Cop0::LOAD), 0, regs.oldPC);
|
||||
} else {
|
||||
if (check_address_error(0b111, address)) {
|
||||
if (Core::IsAddressError(0b111, address)) {
|
||||
regs.cop0.FireException(Cop0::ExceptionCode::AddressErrorLoad, 0, regs.oldPC);
|
||||
} else {
|
||||
regs.Write(instr.rt(), mem.Read<u64>(paddr));
|
||||
@@ -369,7 +367,7 @@ void Interpreter::lbu(const Instruction instr) {
|
||||
|
||||
void Interpreter::lhu(const Instruction 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.FireException(Cop0::ExceptionCode::AddressErrorLoad, 0, regs.oldPC);
|
||||
return;
|
||||
@@ -386,7 +384,7 @@ void Interpreter::lhu(const Instruction instr) {
|
||||
|
||||
void Interpreter::lwu(const Instruction 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.FireException(Cop0::ExceptionCode::AddressErrorLoad, 0, regs.oldPC);
|
||||
return;
|
||||
@@ -409,6 +407,7 @@ void Interpreter::sb(const Instruction instr) {
|
||||
regs.cop0.HandleTLBException(address);
|
||||
regs.cop0.FireException(Cop0::GetTLBExceptionCode(regs.cop0.tlbError, Cop0::STORE), 0, regs.oldPC);
|
||||
} else {
|
||||
EvictCachedBlock(address);
|
||||
mem.Write<u8>(paddr, regs.Read<s64>(instr.rt()));
|
||||
}
|
||||
}
|
||||
@@ -419,7 +418,7 @@ void Interpreter::sc(const Instruction instr) {
|
||||
if (regs.cop0.llbit) {
|
||||
regs.cop0.llbit = false;
|
||||
|
||||
if (check_address_error(0b11, address)) {
|
||||
if (Core::IsAddressError(0b11, address)) {
|
||||
regs.Write(instr.rt(), 0);
|
||||
regs.cop0.HandleTLBException(address);
|
||||
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.FireException(Cop0::GetTLBExceptionCode(regs.cop0.tlbError, Cop0::STORE), 0, regs.oldPC);
|
||||
} else {
|
||||
EvictCachedBlock(address);
|
||||
mem.Write<u32>(paddr, regs.Read<s64>(instr.rt()));
|
||||
regs.Write(instr.rt(), 1);
|
||||
}
|
||||
@@ -451,7 +451,7 @@ void Interpreter::scd(const Instruction instr) {
|
||||
if (regs.cop0.llbit) {
|
||||
regs.cop0.llbit = false;
|
||||
|
||||
if (check_address_error(0b111, address)) {
|
||||
if (Core::IsAddressError(0b111, address)) {
|
||||
regs.Write(instr.rt(), 0);
|
||||
regs.cop0.HandleTLBException(address);
|
||||
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.FireException(Cop0::GetTLBExceptionCode(regs.cop0.tlbError, Cop0::STORE), 0, regs.oldPC);
|
||||
} else {
|
||||
EvictCachedBlock(address);
|
||||
mem.Write<u32>(paddr, regs.Read<s64>(instr.rt()));
|
||||
regs.Write(instr.rt(), 1);
|
||||
}
|
||||
@@ -480,6 +481,7 @@ void Interpreter::sh(const Instruction instr) {
|
||||
regs.cop0.HandleTLBException(address);
|
||||
regs.cop0.FireException(Cop0::GetTLBExceptionCode(regs.cop0.tlbError, Cop0::STORE), 0, regs.oldPC);
|
||||
} else {
|
||||
EvictCachedBlock(address);
|
||||
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) {
|
||||
const s16 offset = instr;
|
||||
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.FireException(Cop0::ExceptionCode::AddressErrorStore, 0, regs.oldPC);
|
||||
return;
|
||||
@@ -498,13 +500,14 @@ void Interpreter::sw(const Instruction instr) {
|
||||
regs.cop0.HandleTLBException(address);
|
||||
regs.cop0.FireException(Cop0::GetTLBExceptionCode(regs.cop0.tlbError, Cop0::STORE), 0, regs.oldPC);
|
||||
} else {
|
||||
EvictCachedBlock(address);
|
||||
mem.Write<u32>(physical, regs.Read<s64>(instr.rt()));
|
||||
}
|
||||
}
|
||||
|
||||
void Interpreter::sd(const Instruction 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.FireException(Cop0::ExceptionCode::AddressErrorStore, 0, regs.oldPC);
|
||||
return;
|
||||
@@ -515,6 +518,7 @@ void Interpreter::sd(const Instruction instr) {
|
||||
regs.cop0.HandleTLBException(address);
|
||||
regs.cop0.FireException(Cop0::GetTLBExceptionCode(regs.cop0.tlbError, Cop0::STORE), 0, regs.oldPC);
|
||||
} else {
|
||||
EvictCachedBlock(address);
|
||||
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 data = mem.Read<u64>(paddr & ~7);
|
||||
const u64 rt = regs.Read<s64>(instr.rt());
|
||||
EvictCachedBlock(address);
|
||||
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 data = mem.Read<u64>(paddr & ~7);
|
||||
const u64 rt = regs.Read<s64>(instr.rt());
|
||||
EvictCachedBlock(address);
|
||||
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 data = mem.Read<u32>(paddr & ~3);
|
||||
const u32 rt = regs.Read<s64>(instr.rt());
|
||||
EvictCachedBlock(address);
|
||||
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 data = mem.Read<u32>(paddr & ~3);
|
||||
const u32 rt = regs.Read<s64>(instr.rt());
|
||||
EvictCachedBlock(address);
|
||||
mem.Write<u32>(paddr & ~3, (data & ~mask) | (rt << shift));
|
||||
}
|
||||
}
|
||||
@@ -869,12 +877,12 @@ void Interpreter::cache(const Instruction instr) {
|
||||
panic("Unknown cache type {}", type);
|
||||
|
||||
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) {
|
||||
case 0:
|
||||
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) {
|
||||
case 0:
|
||||
dcache.WriteBack<true>(vaddr, paddr);
|
||||
|
||||
@@ -3,71 +3,114 @@
|
||||
|
||||
namespace n64 {
|
||||
static bool SpecialEndsBlock(const Instruction instr) {
|
||||
switch (instr.special()) {
|
||||
case Instruction::JR:
|
||||
case Instruction::JALR:
|
||||
case Instruction::SYSCALL:
|
||||
case Instruction::BREAK:
|
||||
case Instruction::TGE:
|
||||
case Instruction::TGEU:
|
||||
case Instruction::TLT:
|
||||
case Instruction::TLTU:
|
||||
case Instruction::TEQ:
|
||||
case Instruction::TNE:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
switch (instr.special()) {
|
||||
case Instruction::JR:
|
||||
case Instruction::JALR:
|
||||
case Instruction::SYSCALL:
|
||||
case Instruction::BREAK:
|
||||
case Instruction::TGE:
|
||||
case Instruction::TGEU:
|
||||
case Instruction::TLT:
|
||||
case Instruction::TLTU:
|
||||
case Instruction::TEQ:
|
||||
case Instruction::TNE:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
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) {
|
||||
switch (instr.opcode()) {
|
||||
case Instruction::SPECIAL:
|
||||
return SpecialEndsBlock(instr);
|
||||
case Instruction::REGIMM:
|
||||
case Instruction::J:
|
||||
case Instruction::JAL:
|
||||
case Instruction::BEQ:
|
||||
case Instruction::BNE:
|
||||
case Instruction::BLEZ:
|
||||
case Instruction::BGTZ:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
switch (instr.opcode()) {
|
||||
case Instruction::SPECIAL:
|
||||
return SpecialEndsBlock(instr);
|
||||
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;
|
||||
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:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static bool IsBranchLikely(const Instruction instr) {
|
||||
switch (instr.opcode()) {
|
||||
case Instruction::BEQL:
|
||||
case Instruction::BNEL:
|
||||
case Instruction::BLEZL:
|
||||
case Instruction::BGTZL:
|
||||
return true;
|
||||
case Instruction::REGIMM:
|
||||
switch (instr.regimm()) {
|
||||
case Instruction::BLTZL:
|
||||
case Instruction::BGEZL:
|
||||
case Instruction::BLTZALL:
|
||||
case Instruction::BGEZALL:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
case Instruction::COP1:
|
||||
{
|
||||
if (instr.cop_rs() == 0x08) {
|
||||
if (instr.cop_rt() == 2 || instr.cop_rt() == 3)
|
||||
return true;
|
||||
switch (instr.opcode()) {
|
||||
case Instruction::BEQL:
|
||||
case Instruction::BNEL:
|
||||
case Instruction::BLEZL:
|
||||
case Instruction::BGTZL:
|
||||
return true;
|
||||
case Instruction::REGIMM:
|
||||
switch (instr.regimm()) {
|
||||
case Instruction::BLTZL:
|
||||
case Instruction::BGEZL:
|
||||
case Instruction::BLTZALL:
|
||||
case Instruction::BGEZALL:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
case Instruction::COP1:
|
||||
if (instr.cop_rs() == 8 && (instr.cop_rt() == 2 || instr.cop_rt() == 3))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return false;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
|
||||
@@ -930,7 +930,7 @@ void JIT::lb(const Instruction instr) {
|
||||
void JIT::ld(const Instruction instr) {
|
||||
if (regs.IsRegConstant(instr.rs())) {
|
||||
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.FireException(Cop0::ExceptionCode::AddressErrorLoad, 0, regs.oldPC);
|
||||
// return;
|
||||
@@ -1014,7 +1014,7 @@ void JIT::ldr(const Instruction instr) {
|
||||
void JIT::lh(const Instruction instr) {
|
||||
if (regs.IsRegConstant(instr.rs())) {
|
||||
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.FireException(Cop0::ExceptionCode::AddressErrorLoad, 0, regs.oldPC);
|
||||
// return;
|
||||
@@ -1043,7 +1043,7 @@ void JIT::lhu(const Instruction instr) {
|
||||
u32 paddr;
|
||||
if (regs.IsRegConstant(instr.rs())) {
|
||||
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.FireException(Cop0::ExceptionCode::AddressErrorLoad, 0, regs.oldPC);
|
||||
return;
|
||||
@@ -1080,7 +1080,7 @@ void JIT::lw(const Instruction instr) {
|
||||
u32 paddr = 0;
|
||||
if (regs.IsRegConstant(instr.rs())) {
|
||||
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.FireException(Cop0::ExceptionCode::AddressErrorLoad, 0, regs.oldPC);
|
||||
// return;
|
||||
@@ -1344,7 +1344,7 @@ void JIT::sw(const Instruction instr) {
|
||||
if (regs.IsRegConstant(instr.rs(), instr.rt())) {
|
||||
const s16 offset = instr;
|
||||
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.FireException(Cop0::ExceptionCode::AddressErrorStore, 0, regs.oldPC);
|
||||
panic("[JIT]: Unhandled ADES exception in SW!");
|
||||
@@ -1367,7 +1367,7 @@ void JIT::sw(const Instruction instr) {
|
||||
if (regs.IsRegConstant(instr.rs())) {
|
||||
const s16 offset = instr;
|
||||
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.FireException(Cop0::ExceptionCode::AddressErrorStore, 0, regs.oldPC);
|
||||
panic("[JIT]: Unhandled ADES exception in SW!");
|
||||
|
||||
@@ -51,9 +51,10 @@ auto PI::BusRead<u8, true>(u32 addr) -> u8 {
|
||||
n64::Mem &mem = n64::Core::GetMem();
|
||||
switch (addr) {
|
||||
case REGION_PI_UNKNOWN:
|
||||
mem.DumpRDRAM();
|
||||
panic("Reading byte from address 0x{:08X} in unsupported region: REGION_PI_UNKNOWN - This is the N64DD, "
|
||||
"returning FF because it is not emulated",
|
||||
addr);
|
||||
"returning FF because it is not emulated (pc: 0x{:016X})",
|
||||
addr, (u64)Core::GetRegs().oldPC);
|
||||
case REGION_PI_64DD_REG:
|
||||
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",
|
||||
|
||||
@@ -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) {
|
||||
Registers ®s = Core::GetRegs();
|
||||
|
||||
Core::GetInstance().interpreter.SignalException(pc);
|
||||
|
||||
u16 vectorOffset = 0x0180;
|
||||
if (tlbError == MISS && (code == ExceptionCode::TLBLoad || code == ExceptionCode::TLBStore)) {
|
||||
if (!status.exl) {
|
||||
vectorOffset = 0x0000;
|
||||
if (is64BitAddressing)
|
||||
vectorOffset = 0x0080;
|
||||
else
|
||||
vectorOffset = 0x0000;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+411
-383
@@ -5,456 +5,484 @@
|
||||
#include <ImGuiImpl/StatusBar.hpp>
|
||||
#include <resources/gamecontrollerdb.h>
|
||||
|
||||
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());
|
||||
SDL_InitSubSystem(SDL_INIT_GAMEPAD);
|
||||
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());
|
||||
SDL_InitSubSystem(SDL_INIT_GAMEPAD);
|
||||
|
||||
SDL_AddGamepadMapping(gamecontrollerdb_str);
|
||||
SDL_AddGamepadMapping(gamecontrollerdb_str);
|
||||
}
|
||||
|
||||
KaizenGui::~KaizenGui() {
|
||||
gui::Cleanup();
|
||||
SDL_Quit();
|
||||
gui::Cleanup();
|
||||
SDL_Quit();
|
||||
}
|
||||
|
||||
void KaizenGui::QueryDevices(const SDL_Event &event) {
|
||||
switch (event.type) {
|
||||
case SDL_EVENT_GAMEPAD_ADDED:
|
||||
if (!gamepad) {
|
||||
const auto index = event.gdevice.which;
|
||||
switch (event.type) {
|
||||
case SDL_EVENT_GAMEPAD_ADDED:
|
||||
if (!gamepad) {
|
||||
const auto index = event.gdevice.which;
|
||||
|
||||
gamepad = SDL_OpenGamepad(index);
|
||||
info("Found controller!");
|
||||
info("Name: {}", SDL_GetGamepadName(gamepad));
|
||||
info("Vendor: {}", SDL_GetGamepadVendor(gamepad));
|
||||
gamepad = SDL_OpenGamepad(index);
|
||||
info("Found controller!");
|
||||
info("Name: {}", SDL_GetGamepadName(gamepad));
|
||||
info("Vendor: {}", SDL_GetGamepadVendor(gamepad));
|
||||
}
|
||||
break;
|
||||
case SDL_EVENT_GAMEPAD_REMOVED:
|
||||
if (gamepad)
|
||||
SDL_CloseGamepad(gamepad);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case SDL_EVENT_GAMEPAD_REMOVED:
|
||||
if (gamepad)
|
||||
SDL_CloseGamepad(gamepad);
|
||||
break;
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
|
||||
void KaizenGui::HandleInput(const SDL_Event &event) {
|
||||
const n64::Core& core = n64::Core::GetInstance();
|
||||
n64::PIF &pif = n64::Core::GetMem().mmio.si.pif;
|
||||
switch(event.type) {
|
||||
const n64::Core &core = n64::Core::GetInstance();
|
||||
n64::PIF &pif = n64::Core::GetMem().mmio.si.pif;
|
||||
switch (event.type) {
|
||||
case SDL_EVENT_GAMEPAD_AXIS_MOTION:
|
||||
if(!gamepad)
|
||||
if (!gamepad)
|
||||
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::CUp,
|
||||
SDL_GetGamepadAxis(gamepad, SDL_GAMEPAD_AXIS_RIGHTY) <= -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);
|
||||
if (xclamped < 0) {
|
||||
xclamped /= static_cast<float>(std::abs(SDL_JOYSTICK_AXIS_MAX));
|
||||
} else {
|
||||
xclamped /= SDL_JOYSTICK_AXIS_MAX;
|
||||
}
|
||||
|
||||
xclamped *= 86;
|
||||
|
||||
float yclamped = SDL_GetGamepadAxis(gamepad, SDL_GAMEPAD_AXIS_LEFTY);
|
||||
if (yclamped < 0) {
|
||||
yclamped /= static_cast<float>(std::abs(SDL_JOYSTICK_AXIS_MIN));
|
||||
} else {
|
||||
yclamped /= SDL_JOYSTICK_AXIS_MAX;
|
||||
}
|
||||
|
||||
yclamped *= 86;
|
||||
|
||||
pif.UpdateAxis(0, n64::Controller::Axis::Y, static_cast<s8>(-yclamped));
|
||||
pif.UpdateAxis(0, n64::Controller::Axis::X, static_cast<s8>(xclamped));
|
||||
}
|
||||
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::CUp, SDL_GetGamepadAxis(gamepad, SDL_GAMEPAD_AXIS_RIGHTY) <= -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);
|
||||
if (xclamped < 0) {
|
||||
xclamped /= static_cast<float>(std::abs(SDL_JOYSTICK_AXIS_MAX));
|
||||
} else {
|
||||
xclamped /= SDL_JOYSTICK_AXIS_MAX;
|
||||
}
|
||||
|
||||
xclamped *= 86;
|
||||
|
||||
float yclamped = SDL_GetGamepadAxis(gamepad, SDL_GAMEPAD_AXIS_LEFTY);
|
||||
if (yclamped < 0) {
|
||||
yclamped /= static_cast<float>(std::abs(SDL_JOYSTICK_AXIS_MIN));
|
||||
} else {
|
||||
yclamped /= SDL_JOYSTICK_AXIS_MAX;
|
||||
}
|
||||
|
||||
yclamped *= 86;
|
||||
|
||||
pif.UpdateAxis(0, n64::Controller::Axis::Y, static_cast<s8>(-yclamped));
|
||||
pif.UpdateAxis(0, n64::Controller::Axis::X, static_cast<s8>( xclamped));
|
||||
}
|
||||
break;
|
||||
case SDL_EVENT_GAMEPAD_BUTTON_DOWN:
|
||||
case SDL_EVENT_GAMEPAD_BUTTON_UP:
|
||||
if(!gamepad)
|
||||
break;
|
||||
if (!gamepad)
|
||||
break;
|
||||
|
||||
pif.UpdateButton(0, n64::Controller::Key::A, SDL_GetGamepadButton(gamepad, SDL_GAMEPAD_BUTTON_SOUTH));
|
||||
pif.UpdateButton(0, n64::Controller::Key::B, SDL_GetGamepadButton(gamepad, SDL_GAMEPAD_BUTTON_WEST));
|
||||
pif.UpdateButton(0, n64::Controller::Key::Start, SDL_GetGamepadButton(gamepad, SDL_GAMEPAD_BUTTON_START));
|
||||
pif.UpdateButton(0, n64::Controller::Key::DUp, SDL_GetGamepadButton(gamepad, SDL_GAMEPAD_BUTTON_DPAD_UP));
|
||||
pif.UpdateButton(0, n64::Controller::Key::DDown, SDL_GetGamepadButton(gamepad, SDL_GAMEPAD_BUTTON_DPAD_DOWN));
|
||||
pif.UpdateButton(0, n64::Controller::Key::DLeft, SDL_GetGamepadButton(gamepad, SDL_GAMEPAD_BUTTON_DPAD_LEFT));
|
||||
pif.UpdateButton(0, n64::Controller::Key::DRight, SDL_GetGamepadButton(gamepad, SDL_GAMEPAD_BUTTON_DPAD_RIGHT));
|
||||
pif.UpdateButton(0, n64::Controller::Key::LT, SDL_GetGamepadButton(gamepad, SDL_GAMEPAD_BUTTON_LEFT_SHOULDER));
|
||||
pif.UpdateButton(0, n64::Controller::Key::RT, SDL_GetGamepadButton(gamepad, SDL_GAMEPAD_BUTTON_RIGHT_SHOULDER));
|
||||
break;
|
||||
pif.UpdateButton(0, n64::Controller::Key::A, SDL_GetGamepadButton(gamepad, SDL_GAMEPAD_BUTTON_SOUTH));
|
||||
pif.UpdateButton(0, n64::Controller::Key::B, SDL_GetGamepadButton(gamepad, SDL_GAMEPAD_BUTTON_WEST));
|
||||
pif.UpdateButton(0, n64::Controller::Key::Start, SDL_GetGamepadButton(gamepad, SDL_GAMEPAD_BUTTON_START));
|
||||
pif.UpdateButton(0, n64::Controller::Key::DUp, SDL_GetGamepadButton(gamepad, SDL_GAMEPAD_BUTTON_DPAD_UP));
|
||||
pif.UpdateButton(0, n64::Controller::Key::DDown, SDL_GetGamepadButton(gamepad, SDL_GAMEPAD_BUTTON_DPAD_DOWN));
|
||||
pif.UpdateButton(0, n64::Controller::Key::DLeft, SDL_GetGamepadButton(gamepad, SDL_GAMEPAD_BUTTON_DPAD_LEFT));
|
||||
pif.UpdateButton(0, n64::Controller::Key::DRight, SDL_GetGamepadButton(gamepad, SDL_GAMEPAD_BUTTON_DPAD_RIGHT));
|
||||
pif.UpdateButton(0, n64::Controller::Key::LT, SDL_GetGamepadButton(gamepad, SDL_GAMEPAD_BUTTON_LEFT_SHOULDER));
|
||||
pif.UpdateButton(0, n64::Controller::Key::RT, SDL_GetGamepadButton(gamepad, SDL_GAMEPAD_BUTTON_RIGHT_SHOULDER));
|
||||
break;
|
||||
case SDL_EVENT_KEY_DOWN:
|
||||
case SDL_EVENT_KEY_UP:
|
||||
{
|
||||
const auto keys = SDL_GetKeyboardState(nullptr);
|
||||
if((keys[SDL_SCANCODE_LCTRL] || keys[SDL_SCANCODE_RCTRL]) && keys[SDL_SCANCODE_O]) {
|
||||
fileDialogOpen = true;
|
||||
{
|
||||
const auto keys = SDL_GetKeyboardState(nullptr);
|
||||
if ((keys[SDL_SCANCODE_LCTRL] || keys[SDL_SCANCODE_RCTRL]) && keys[SDL_SCANCODE_O]) {
|
||||
fileDialogOpen = true;
|
||||
}
|
||||
|
||||
fastForward = keys[SDL_SCANCODE_SPACE];
|
||||
if (!unlockFramerate)
|
||||
core.parallel.SetFramerateUnlocked(fastForward);
|
||||
|
||||
if (core.romLoaded) {
|
||||
if (keys[SDL_SCANCODE_P]) {
|
||||
emuThread.TogglePause();
|
||||
}
|
||||
|
||||
if (keys[SDL_SCANCODE_R]) {
|
||||
emuThread.Reset();
|
||||
}
|
||||
|
||||
if (keys[SDL_SCANCODE_Q]) {
|
||||
emuThread.Stop();
|
||||
}
|
||||
}
|
||||
|
||||
if (gamepad)
|
||||
break;
|
||||
|
||||
pif.UpdateButton(0, n64::Controller::Key::Z, keys[SDL_SCANCODE_Z]);
|
||||
pif.UpdateButton(0, n64::Controller::Key::CUp, keys[SDL_SCANCODE_HOME]);
|
||||
pif.UpdateButton(0, n64::Controller::Key::CDown, keys[SDL_SCANCODE_END]);
|
||||
pif.UpdateButton(0, n64::Controller::Key::CLeft, keys[SDL_SCANCODE_DELETE]);
|
||||
pif.UpdateButton(0, n64::Controller::Key::CRight, keys[SDL_SCANCODE_PAGEDOWN]);
|
||||
pif.UpdateButton(0, n64::Controller::Key::A, keys[SDL_SCANCODE_X]);
|
||||
pif.UpdateButton(0, n64::Controller::Key::B, keys[SDL_SCANCODE_C]);
|
||||
pif.UpdateButton(0, n64::Controller::Key::Start, keys[SDL_SCANCODE_RETURN]);
|
||||
pif.UpdateButton(0, n64::Controller::Key::DUp, keys[SDL_SCANCODE_I]);
|
||||
pif.UpdateButton(0, n64::Controller::Key::DDown, keys[SDL_SCANCODE_K]);
|
||||
pif.UpdateButton(0, n64::Controller::Key::DLeft, keys[SDL_SCANCODE_J]);
|
||||
pif.UpdateButton(0, n64::Controller::Key::DRight, keys[SDL_SCANCODE_L]);
|
||||
pif.UpdateButton(0, n64::Controller::Key::LT, keys[SDL_SCANCODE_A]);
|
||||
pif.UpdateButton(0, n64::Controller::Key::RT, keys[SDL_SCANCODE_S]);
|
||||
|
||||
float x = 0, y = 0;
|
||||
|
||||
if (keys[SDL_SCANCODE_UP])
|
||||
y = 86;
|
||||
if (keys[SDL_SCANCODE_DOWN])
|
||||
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::Y, y);
|
||||
}
|
||||
|
||||
fastForward = keys[SDL_SCANCODE_SPACE];
|
||||
if(!unlockFramerate)
|
||||
core.parallel.SetFramerateUnlocked(fastForward);
|
||||
|
||||
if(core.romLoaded) {
|
||||
if(keys[SDL_SCANCODE_P]) {
|
||||
emuThread.TogglePause();
|
||||
}
|
||||
|
||||
if(keys[SDL_SCANCODE_R]) {
|
||||
emuThread.Reset();
|
||||
}
|
||||
|
||||
if(keys[SDL_SCANCODE_Q]) {
|
||||
emuThread.Stop();
|
||||
}
|
||||
}
|
||||
|
||||
if(gamepad)
|
||||
break;
|
||||
|
||||
pif.UpdateButton(0, n64::Controller::Key::Z, keys[SDL_SCANCODE_Z]);
|
||||
pif.UpdateButton(0, n64::Controller::Key::CUp, keys[SDL_SCANCODE_HOME]);
|
||||
pif.UpdateButton(0, n64::Controller::Key::CDown, keys[SDL_SCANCODE_END]);
|
||||
pif.UpdateButton(0, n64::Controller::Key::CLeft, keys[SDL_SCANCODE_DELETE]);
|
||||
pif.UpdateButton(0, n64::Controller::Key::CRight, keys[SDL_SCANCODE_PAGEDOWN]);
|
||||
pif.UpdateButton(0, n64::Controller::Key::A, keys[SDL_SCANCODE_X]);
|
||||
pif.UpdateButton(0, n64::Controller::Key::B, keys[SDL_SCANCODE_C]);
|
||||
pif.UpdateButton(0, n64::Controller::Key::Start, keys[SDL_SCANCODE_RETURN]);
|
||||
pif.UpdateButton(0, n64::Controller::Key::DUp, keys[SDL_SCANCODE_I]);
|
||||
pif.UpdateButton(0, n64::Controller::Key::DDown, keys[SDL_SCANCODE_K]);
|
||||
pif.UpdateButton(0, n64::Controller::Key::DLeft, keys[SDL_SCANCODE_J]);
|
||||
pif.UpdateButton(0, n64::Controller::Key::DRight, keys[SDL_SCANCODE_L]);
|
||||
pif.UpdateButton(0, n64::Controller::Key::LT, keys[SDL_SCANCODE_A]);
|
||||
pif.UpdateButton(0, n64::Controller::Key::RT, keys[SDL_SCANCODE_S]);
|
||||
|
||||
float x = 0, y = 0;
|
||||
|
||||
if (keys[SDL_SCANCODE_UP]) y = 86;
|
||||
if (keys[SDL_SCANCODE_DOWN]) 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::Y, y);
|
||||
}
|
||||
break;
|
||||
default: break;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
std::pair<std::optional<s64>, std::optional<Util::Error::MemoryAccess>> RenderErrorMessageDetails() {
|
||||
auto lastPC = Util::Error::GetLastPC();
|
||||
if(lastPC.has_value()) {
|
||||
ImGui::Text("%s", std::format("Occurred @ PC = {:016X}", Util::Error::GetLastPC().value()).c_str());
|
||||
}
|
||||
auto lastPC = Util::Error::GetLastPC();
|
||||
if (lastPC.has_value()) {
|
||||
ImGui::Text("%s", std::format("Occurred @ PC = {:016X}", Util::Error::GetLastPC().value()).c_str());
|
||||
}
|
||||
|
||||
auto memoryAccess = Util::Error::GetMemoryAccess();
|
||||
if(memoryAccess.has_value()) {
|
||||
const auto [is_write, size, address, written_val] = memoryAccess.value();
|
||||
ImGui::Text("%s", std::format("{} {}-bit value @ {:08X}{}", is_write ? "Writing" : "Reading",
|
||||
static_cast<u8>(size), address,
|
||||
is_write ? std::format(" (value = 0x{:X})", written_val) : "")
|
||||
.c_str());
|
||||
}
|
||||
auto memoryAccess = Util::Error::GetMemoryAccess();
|
||||
if (memoryAccess.has_value()) {
|
||||
const auto [is_write, size, address, written_val] = memoryAccess.value();
|
||||
ImGui::Text("%s",
|
||||
std::format("{} {}-bit value @ {:08X}{}", is_write ? "Writing" : "Reading", static_cast<u8>(size),
|
||||
address, is_write ? std::format(" (value = 0x{:X})", written_val) : "")
|
||||
.c_str());
|
||||
}
|
||||
|
||||
return {lastPC, memoryAccess};
|
||||
return {lastPC, memoryAccess};
|
||||
}
|
||||
|
||||
void KaizenGui::RenderUI() {
|
||||
n64::Core& core = n64::Core::GetInstance();
|
||||
gui::StartFrame();
|
||||
n64::Core &core = n64::Core::GetInstance();
|
||||
gui::StartFrame();
|
||||
|
||||
if(ImGui::BeginMainMenuBar()) {
|
||||
if(ImGui::BeginMenu("File")) {
|
||||
if(ImGui::MenuItem("Open", "Ctrl-O")) {
|
||||
fileDialogOpen = true;
|
||||
}
|
||||
if(ImGui::MenuItem("Exit")) {
|
||||
quit = true;
|
||||
emuThread.Stop();
|
||||
}
|
||||
ImGui::EndMenu();
|
||||
}
|
||||
if(ImGui::BeginMenu("Emulation")) {
|
||||
ImGui::BeginDisabled(!core.romLoaded);
|
||||
if (ImGui::BeginMainMenuBar()) {
|
||||
if (ImGui::BeginMenu("File")) {
|
||||
if (ImGui::MenuItem("Open", "Ctrl-O")) {
|
||||
fileDialogOpen = true;
|
||||
}
|
||||
if (ImGui::MenuItem("Exit")) {
|
||||
quit = true;
|
||||
emuThread.Stop();
|
||||
}
|
||||
ImGui::EndMenu();
|
||||
}
|
||||
if (ImGui::BeginMenu("Emulation")) {
|
||||
ImGui::BeginDisabled(!core.romLoaded);
|
||||
|
||||
if(ImGui::MenuItem(core.pause ? "Resume" : "Pause", "P")) {
|
||||
emuThread.TogglePause();
|
||||
}
|
||||
if (ImGui::MenuItem(core.pause ? "Resume" : "Pause", "P")) {
|
||||
emuThread.TogglePause();
|
||||
}
|
||||
|
||||
if(ImGui::MenuItem("Reset", "R")) {
|
||||
emuThread.Reset();
|
||||
}
|
||||
if (ImGui::MenuItem("Reset", "R")) {
|
||||
emuThread.Reset();
|
||||
}
|
||||
|
||||
if(ImGui::MenuItem("Stop", "Q")) {
|
||||
emuThread.Stop();
|
||||
core.romLoaded = false;
|
||||
}
|
||||
if (ImGui::MenuItem("Stop", "Q")) {
|
||||
emuThread.Stop();
|
||||
core.romLoaded = false;
|
||||
}
|
||||
|
||||
if(ImGui::Checkbox("Unlock framerate", &unlockFramerate)) {
|
||||
core.parallel.SetFramerateUnlocked(unlockFramerate);
|
||||
}
|
||||
if (ImGui::Checkbox("Unlock framerate", &unlockFramerate)) {
|
||||
core.parallel.SetFramerateUnlocked(unlockFramerate);
|
||||
}
|
||||
|
||||
if(ImGui::MenuItem("Open Debugger")) {
|
||||
debugger.Open();
|
||||
}
|
||||
if (ImGui::MenuItem("Open Debugger")) {
|
||||
debugger.Open();
|
||||
}
|
||||
|
||||
ImGui::EndDisabled();
|
||||
ImGui::EndDisabled();
|
||||
|
||||
if(ImGui::MenuItem("Options")) {
|
||||
settingsWindow.isOpen = true;
|
||||
}
|
||||
ImGui::EndMenu();
|
||||
}
|
||||
if(ImGui::BeginMenu("Help")) {
|
||||
if(ImGui::MenuItem("About")) {
|
||||
aboutOpen = true;
|
||||
}
|
||||
if (ImGui::MenuItem("Options")) {
|
||||
settingsWindow.isOpen = true;
|
||||
}
|
||||
ImGui::EndMenu();
|
||||
}
|
||||
if (ImGui::BeginMenu("Help")) {
|
||||
if (ImGui::MenuItem("About")) {
|
||||
aboutOpen = true;
|
||||
}
|
||||
|
||||
ImGui::EndMenu();
|
||||
}
|
||||
ImGui::EndMainMenuBar();
|
||||
}
|
||||
|
||||
if(!Util::Error::IsHandled()) {
|
||||
ImGui::OpenPopup(Util::Error::GetSeverity().as_c_str());
|
||||
}
|
||||
|
||||
if(settingsWindow.isOpen) {
|
||||
ImGui::OpenPopup("Settings", ImGuiPopupFlags_None);
|
||||
}
|
||||
|
||||
if(aboutOpen) {
|
||||
ImGui::OpenPopup("About Kaizen");
|
||||
}
|
||||
|
||||
settingsWindow.render();
|
||||
debugger.render();
|
||||
|
||||
const ImVec2 center = ImGui::GetMainViewport()->GetCenter();
|
||||
ImGui::SetNextWindowPos(center, ImGuiCond_Appearing, ImVec2(0.5f, 0.5f));
|
||||
|
||||
if (ImGui::BeginPopupModal("About Kaizen", &aboutOpen, ImGuiWindowFlags_AlwaysAutoResize)) {
|
||||
ImGui::Text("Kaizen is a Nintendo 64 emulator that strives");
|
||||
ImGui::Text("to offer a friendly user experience and compatibility.");
|
||||
ImGui::Text("Kaizen is licensed under the BSD 3-clause license.");
|
||||
ImGui::Text("Nintendo 64 is a registered trademark of Nintendo Co., Ltd.");
|
||||
ImGui::Separator();
|
||||
ImGui::Text("Kaizen %s%s", KAIZEN_USE_HASH ? "dev build " : "", KAIZEN_VERSION_STR);
|
||||
ImGui::Separator();
|
||||
if(ImGui::Button("OK")) {
|
||||
aboutOpen = false;
|
||||
ImGui::CloseCurrentPopup();
|
||||
ImGui::EndMenu();
|
||||
}
|
||||
ImGui::EndMainMenuBar();
|
||||
}
|
||||
|
||||
ImGui::EndPopup();
|
||||
}
|
||||
if (!Util::Error::IsHandled()) {
|
||||
ImGui::OpenPopup(Util::Error::GetSeverity().as_c_str());
|
||||
}
|
||||
|
||||
ImGui::SetNextWindowPos(center, ImGuiCond_Appearing, ImVec2(0.5f, 0.5f));
|
||||
if (settingsWindow.isOpen) {
|
||||
ImGui::OpenPopup("Settings", ImGuiPopupFlags_None);
|
||||
}
|
||||
|
||||
if (ImGui::BeginPopupModal(Util::Error::GetSeverity().as_c_str(), nullptr, ImGuiWindowFlags_AlwaysAutoResize)) {
|
||||
emuThread.TogglePause();
|
||||
switch(Util::Error::GetSeverity().as_enum) {
|
||||
case Util::Error::Severity::WARN: {
|
||||
ImGui::PushStyleColor(ImGuiCol_TitleBg, 0x8054eae5);
|
||||
ImGui::PushStyleColor(ImGuiCol_Text, 0xff7be4e1);
|
||||
ImGui::Text("Warning of type: %s", Util::Error::GetType().as_c_str());
|
||||
ImGui::PopStyleColor();
|
||||
ImGui::PopStyleColor();
|
||||
ImGui::Text(R"(Warning message: "%s")", Util::Error::GetError().c_str());
|
||||
RenderErrorMessageDetails();
|
||||
if (aboutOpen) {
|
||||
ImGui::OpenPopup("About Kaizen");
|
||||
}
|
||||
|
||||
if(n64::Core::GetInstance().romLoaded && !n64::Core::GetInstance().pause) {
|
||||
const bool ignore = ImGui::Button("Try continuing"); ImGui::SameLine();
|
||||
const bool stop = ImGui::Button("Stop emulation"); ImGui::SameLine();
|
||||
const bool chooseAnother = ImGui::Button("Choose another ROM");
|
||||
if(ignore || stop || chooseAnother) {
|
||||
Util::Error::SetHandled();
|
||||
settingsWindow.render();
|
||||
debugger.render();
|
||||
|
||||
const ImVec2 center = ImGui::GetMainViewport()->GetCenter();
|
||||
ImGui::SetNextWindowPos(center, ImGuiCond_Appearing, ImVec2(0.5f, 0.5f));
|
||||
|
||||
if (ImGui::BeginPopupModal("About Kaizen", &aboutOpen, ImGuiWindowFlags_AlwaysAutoResize)) {
|
||||
ImGui::Text("Kaizen is a Nintendo 64 emulator that strives");
|
||||
ImGui::Text("to offer a friendly user experience and compatibility.");
|
||||
ImGui::Text("Kaizen is licensed under the BSD 3-clause license.");
|
||||
ImGui::Text("Nintendo 64 is a registered trademark of Nintendo Co., Ltd.");
|
||||
ImGui::Separator();
|
||||
ImGui::Text("Kaizen %s%s", KAIZEN_USE_HASH ? "dev build " : "", KAIZEN_VERSION_STR);
|
||||
ImGui::Separator();
|
||||
if (ImGui::Button("OK")) {
|
||||
aboutOpen = false;
|
||||
ImGui::CloseCurrentPopup();
|
||||
}
|
||||
|
||||
if(ignore) {
|
||||
emuThread.TogglePause();
|
||||
}
|
||||
|
||||
if(stop || chooseAnother) {
|
||||
emuThread.Stop();
|
||||
}
|
||||
|
||||
if(chooseAnother) {
|
||||
fileDialogOpen = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if(ImGui::Button("OK"))
|
||||
ImGui::CloseCurrentPopup();
|
||||
} break;
|
||||
case Util::Error::Severity::UNRECOVERABLE: {
|
||||
emuThread.Stop();
|
||||
ImGui::PushStyleColor(ImGuiCol_TitleBg, 0x800000ff);
|
||||
ImGui::PushStyleColor(ImGuiCol_Text, 0xff3b3bbf);
|
||||
ImGui::Text("An unrecoverable error has occurred! Emulation has been stopped...");
|
||||
ImGui::Text("Error of type: %s", Util::Error::GetType().as_c_str());
|
||||
ImGui::PopStyleColor();
|
||||
ImGui::PopStyleColor();
|
||||
ImGui::Text(R"(Error message: "%s")", Util::Error::GetError().c_str());
|
||||
RenderErrorMessageDetails();
|
||||
if(ImGui::Button("OK"))
|
||||
ImGui::CloseCurrentPopup();
|
||||
} break;
|
||||
case Util::Error::Severity::NON_FATAL: {
|
||||
ImGui::PushStyleColor(ImGuiCol_TitleBg, 0x800000ff);
|
||||
ImGui::PushStyleColor(ImGuiCol_Text, 0xff3b3bbf);
|
||||
ImGui::Text("An error has occurred!");
|
||||
ImGui::Text("Error of type: %s", Util::Error::GetType().as_c_str());
|
||||
ImGui::PopStyleColor();
|
||||
ImGui::PopStyleColor();
|
||||
ImGui::Text(R"(Error message: "%s")", Util::Error::GetError().c_str());
|
||||
auto [lastPC, memoryAccess] = RenderErrorMessageDetails();
|
||||
|
||||
const bool ignore = ImGui::Button("Try continuing"); ImGui::SameLine();
|
||||
const bool stop = ImGui::Button("Stop emulation"); ImGui::SameLine();
|
||||
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;
|
||||
if(ignore || stop || chooseAnother || openInDebugger) {
|
||||
Util::Error::SetHandled();
|
||||
ImGui::CloseCurrentPopup();
|
||||
}
|
||||
|
||||
if(ignore) {
|
||||
emuThread.TogglePause();
|
||||
}
|
||||
|
||||
if(stop || chooseAnother) {
|
||||
emuThread.Stop();
|
||||
}
|
||||
|
||||
if(chooseAnother) {
|
||||
fileDialogOpen = true;
|
||||
}
|
||||
|
||||
if(openInDebugger) {
|
||||
if(!n64::Core::GetInstance().breakpoints.contains(lastPC.value()))
|
||||
n64::Core::GetInstance().ToggleBreakpoint(lastPC.value());
|
||||
|
||||
debugger.Open();
|
||||
emuThread.Reset();
|
||||
}
|
||||
} break;
|
||||
default: break;
|
||||
ImGui::EndPopup();
|
||||
}
|
||||
|
||||
ImGui::EndPopup();
|
||||
}
|
||||
ImGui::SetNextWindowPos(center, ImGuiCond_Appearing, ImVec2(0.5f, 0.5f));
|
||||
|
||||
if(ImGui::BeginMainStatusBar()) {
|
||||
ImGui::Text("FPS: %.2f", ImGui::GetIO().Framerate);
|
||||
ImGui::EndMainStatusBar();
|
||||
}
|
||||
if (ImGui::BeginPopupModal(Util::Error::GetSeverity().as_c_str(), nullptr, ImGuiWindowFlags_AlwaysAutoResize)) {
|
||||
emuThread.TogglePause();
|
||||
switch (Util::Error::GetSeverity().as_enum) {
|
||||
case Util::Error::Severity::WARN:
|
||||
{
|
||||
ImGui::PushStyleColor(ImGuiCol_TitleBg, 0x8054eae5);
|
||||
ImGui::PushStyleColor(ImGuiCol_Text, 0xff7be4e1);
|
||||
ImGui::Text("Warning of type: %s", Util::Error::GetType().as_c_str());
|
||||
ImGui::PopStyleColor();
|
||||
ImGui::PopStyleColor();
|
||||
ImGui::Text(R"(Warning message: "%s")", Util::Error::GetError().c_str());
|
||||
RenderErrorMessageDetails();
|
||||
|
||||
if (shouldDisplaySpinner) {
|
||||
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::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 0.0f);
|
||||
if (n64::Core::GetInstance().romLoaded && !n64::Core::GetInstance().pause) {
|
||||
const bool ignore = ImGui::Button("Try continuing");
|
||||
ImGui::SameLine();
|
||||
const bool stop = ImGui::Button("Stop emulation");
|
||||
ImGui::SameLine();
|
||||
const bool chooseAnother = ImGui::Button("Choose another ROM");
|
||||
if (ignore || stop || chooseAnother) {
|
||||
Util::Error::SetHandled();
|
||||
ImGui::CloseCurrentPopup();
|
||||
}
|
||||
|
||||
ImGui::Begin("##spinnerContainer", nullptr, ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoDecoration);
|
||||
if (ignore) {
|
||||
emuThread.TogglePause();
|
||||
}
|
||||
|
||||
ImGui::Spinner("##spinner", 10.f, 4.f, ImGui::GetColorU32(ImGui::GetStyle().Colors[ImGuiCol_TitleBgActive]));
|
||||
ImGui::SameLine();
|
||||
if (stop || chooseAnother) {
|
||||
emuThread.Stop();
|
||||
}
|
||||
|
||||
ImGui::PushFont(nullptr, ImGui::GetStyle().FontSizeBase * 2.f);
|
||||
ImGui::Text("Loading \"%s\"...", fs::path(fileToLoad).filename().string().c_str());
|
||||
ImGui::PopFont();
|
||||
if (chooseAnother) {
|
||||
fileDialogOpen = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
ImGui::End();
|
||||
if (ImGui::Button("OK"))
|
||||
ImGui::CloseCurrentPopup();
|
||||
}
|
||||
break;
|
||||
case Util::Error::Severity::UNRECOVERABLE:
|
||||
{
|
||||
emuThread.Stop();
|
||||
ImGui::PushStyleColor(ImGuiCol_TitleBg, 0x800000ff);
|
||||
ImGui::PushStyleColor(ImGuiCol_Text, 0xff3b3bbf);
|
||||
ImGui::Text("An unrecoverable error has occurred! Emulation has been stopped...");
|
||||
ImGui::Text("Error of type: %s", Util::Error::GetType().as_c_str());
|
||||
ImGui::PopStyleColor();
|
||||
ImGui::PopStyleColor();
|
||||
ImGui::Text(R"(Error message: "%s")", Util::Error::GetError().c_str());
|
||||
RenderErrorMessageDetails();
|
||||
if (ImGui::Button("OK"))
|
||||
ImGui::CloseCurrentPopup();
|
||||
}
|
||||
break;
|
||||
case Util::Error::Severity::NON_FATAL:
|
||||
{
|
||||
ImGui::PushStyleColor(ImGuiCol_TitleBg, 0x800000ff);
|
||||
ImGui::PushStyleColor(ImGuiCol_Text, 0xff3b3bbf);
|
||||
ImGui::Text("An error has occurred!");
|
||||
ImGui::Text("Error of type: %s", Util::Error::GetType().as_c_str());
|
||||
ImGui::PopStyleColor();
|
||||
ImGui::PopStyleColor();
|
||||
ImGui::Text(R"(Error message: "%s")", Util::Error::GetError().c_str());
|
||||
auto [lastPC, memoryAccess] = RenderErrorMessageDetails();
|
||||
|
||||
ImGui::PopStyleVar();
|
||||
ImGui::PopStyleColor();
|
||||
}
|
||||
const bool ignore = ImGui::Button("Try continuing");
|
||||
ImGui::SameLine();
|
||||
const bool stop = ImGui::Button("Stop emulation");
|
||||
ImGui::SameLine();
|
||||
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;
|
||||
if (ignore || stop || chooseAnother || openInDebugger) {
|
||||
Util::Error::SetHandled();
|
||||
ImGui::CloseCurrentPopup();
|
||||
}
|
||||
|
||||
ImGui::Render();
|
||||
if (ImGui::GetIO().ConfigFlags & ImGuiConfigFlags_ViewportsEnable) {
|
||||
ImGui::UpdatePlatformWindows();
|
||||
ImGui::RenderPlatformWindowsDefault();
|
||||
}
|
||||
if (ignore) {
|
||||
emuThread.TogglePause();
|
||||
}
|
||||
|
||||
if(fileDialogOpen) {
|
||||
fileDialogOpen = false;
|
||||
constexpr SDL_DialogFileFilter filters[] = {{"All files", "*"}, {"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);
|
||||
if (stop || chooseAnother) {
|
||||
emuThread.Stop();
|
||||
}
|
||||
|
||||
if (!filelist) {
|
||||
panic("An error occured: {}", SDL_GetError());
|
||||
}
|
||||
if (chooseAnother) {
|
||||
fileDialogOpen = true;
|
||||
}
|
||||
|
||||
if (!*filelist) {
|
||||
warn("The user did not select any file.");
|
||||
warn("Most likely, the dialog was canceled.");
|
||||
if (openInDebugger) {
|
||||
if (!n64::Core::GetInstance().breakpoints.contains(lastPC.value()))
|
||||
n64::Core::GetInstance().ToggleBreakpoint(lastPC.value());
|
||||
|
||||
debugger.Open();
|
||||
emuThread.Reset();
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
ImGui::EndPopup();
|
||||
}
|
||||
|
||||
if (ImGui::BeginMainStatusBar()) {
|
||||
ImGui::Text("FPS: %.2f", ImGui::GetIO().Framerate);
|
||||
ImGui::EndMainStatusBar();
|
||||
}
|
||||
|
||||
if (shouldDisplaySpinner) {
|
||||
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::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 0.0f);
|
||||
|
||||
ImGui::Begin("##spinnerContainer", nullptr, ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoDecoration);
|
||||
|
||||
ImGui::Spinner("##spinner", 10.f, 4.f, ImGui::GetColorU32(ImGui::GetStyle().Colors[ImGuiCol_TitleBgActive]));
|
||||
ImGui::SameLine();
|
||||
|
||||
ImGui::PushFont(nullptr, ImGui::GetStyle().FontSizeBase * 2.f);
|
||||
ImGui::Text("Loading \"%s\"...", fs::path(fileToLoad).filename().string().c_str());
|
||||
ImGui::PopFont();
|
||||
|
||||
ImGui::End();
|
||||
|
||||
ImGui::PopStyleVar();
|
||||
ImGui::PopStyleColor();
|
||||
}
|
||||
|
||||
ImGui::Render();
|
||||
if (ImGui::GetIO().ConfigFlags & ImGuiConfigFlags_ViewportsEnable) {
|
||||
ImGui::UpdatePlatformWindows();
|
||||
ImGui::RenderPlatformWindowsDefault();
|
||||
}
|
||||
|
||||
if (fileDialogOpen) {
|
||||
fileDialogOpen = false;
|
||||
constexpr SDL_DialogFileFilter filters[] = {{"All files", "*"},
|
||||
{"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);
|
||||
|
||||
if (!filelist) {
|
||||
panic("An error occured: {}", SDL_GetError());
|
||||
}
|
||||
|
||||
if (!*filelist) {
|
||||
warn("The user did not select any file.");
|
||||
warn("Most likely, the dialog was canceled.");
|
||||
return;
|
||||
}
|
||||
|
||||
kaizen->fileToLoad = *filelist;
|
||||
kaizen->shouldDisplaySpinner = true;
|
||||
std::thread fileWorker(&KaizenGui::FileWorker, kaizen);
|
||||
fileWorker.detach();
|
||||
},
|
||||
this, window.getHandle(), filters, 3, nullptr, false);
|
||||
}
|
||||
|
||||
if (minimized)
|
||||
return;
|
||||
}
|
||||
|
||||
kaizen->fileToLoad = *filelist;
|
||||
kaizen->shouldDisplaySpinner = true;
|
||||
if (core.romLoaded) {
|
||||
core.parallel.UpdateScreen<true>();
|
||||
return;
|
||||
}
|
||||
|
||||
std::thread fileWorker(&KaizenGui::FileWorker, kaizen);
|
||||
fileWorker.detach();
|
||||
}, this, window.getHandle(), filters, 3, nullptr, false);
|
||||
}
|
||||
|
||||
if(minimized)
|
||||
return;
|
||||
|
||||
if(core.romLoaded) {
|
||||
core.parallel.UpdateScreen<true>();
|
||||
return;
|
||||
}
|
||||
|
||||
core.parallel.UpdateScreen<false>();
|
||||
core.parallel.UpdateScreen<false>();
|
||||
}
|
||||
|
||||
void KaizenGui::LoadROM(const std::string &path) noexcept {
|
||||
n64::Core& core = n64::Core::GetInstance();
|
||||
core.LoadROM(path);
|
||||
const auto gameNameDB = n64::Core::GetMem().rom.gameNameDB;
|
||||
SDL_SetWindowTitle(window.getHandle(), ("Kaizen " KAIZEN_VERSION_STR " - " + gameNameDB).c_str());
|
||||
n64::Core &core = n64::Core::GetInstance();
|
||||
core.LoadROM(path);
|
||||
const auto gameNameDB = n64::Core::GetMem().rom.gameNameDB;
|
||||
SDL_SetWindowTitle(window.getHandle(), ("Kaizen " KAIZEN_VERSION_STR " - " + gameNameDB).c_str());
|
||||
}
|
||||
|
||||
void KaizenGui::run() {
|
||||
while(!quit) {
|
||||
SDL_Event e;
|
||||
while (SDL_PollEvent(&e)) {
|
||||
ImGui_ImplSDL3_ProcessEvent(&e);
|
||||
switch(e.type) {
|
||||
case SDL_EVENT_QUIT:
|
||||
quit = true;
|
||||
emuThread.Stop();
|
||||
break;
|
||||
case SDL_EVENT_WINDOW_MINIMIZED:
|
||||
minimized = true;
|
||||
break;
|
||||
case SDL_EVENT_WINDOW_RESTORED:
|
||||
minimized = false;
|
||||
break;
|
||||
default: break;
|
||||
}
|
||||
QueryDevices(e);
|
||||
HandleInput(e);
|
||||
while (!quit) {
|
||||
SDL_Event e;
|
||||
while (SDL_PollEvent(&e)) {
|
||||
ImGui_ImplSDL3_ProcessEvent(&e);
|
||||
switch (e.type) {
|
||||
case SDL_EVENT_QUIT:
|
||||
quit = true;
|
||||
emuThread.Stop();
|
||||
break;
|
||||
case SDL_EVENT_WINDOW_MINIMIZED:
|
||||
minimized = true;
|
||||
break;
|
||||
case SDL_EVENT_WINDOW_RESTORED:
|
||||
minimized = false;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
QueryDevices(e);
|
||||
HandleInput(e);
|
||||
}
|
||||
|
||||
SDL_GetWindowSize(window.getHandle(), &width, &height);
|
||||
|
||||
emuThread.run();
|
||||
RenderUI();
|
||||
}
|
||||
|
||||
SDL_GetWindowSize(window.getHandle(), &width, &height);
|
||||
|
||||
emuThread.run();
|
||||
RenderUI();
|
||||
}
|
||||
}
|
||||
|
||||
void KaizenGui::LoadTAS(const std::string &path) noexcept {
|
||||
n64::Core::GetInstance().LoadTAS(fs::path(path));
|
||||
}
|
||||
void KaizenGui::LoadTAS(const std::string &path) noexcept { n64::Core::GetInstance().LoadTAS(fs::path(path)); }
|
||||
|
||||
@@ -37,12 +37,13 @@ private:
|
||||
void HandleInput(const SDL_Event &event);
|
||||
void QueryDevices(const SDL_Event &event);
|
||||
|
||||
[[noreturn]] void FileWorker() {
|
||||
void FileWorker() {
|
||||
while (true) {
|
||||
if (!fileToLoad.empty()) {
|
||||
LoadROM(fileToLoad);
|
||||
shouldDisplaySpinner = false;
|
||||
fileToLoad = "";
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,9 +2,13 @@
|
||||
#include <Options.hpp>
|
||||
#include <log.hpp>
|
||||
#include <imgui.h>
|
||||
#include <Core.hpp>
|
||||
|
||||
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;
|
||||
} else {
|
||||
selectedCpuTypeIndex = 0;
|
||||
@@ -12,7 +16,7 @@ CPUSettings::CPUSettings() {
|
||||
}
|
||||
|
||||
void CPUSettings::render() {
|
||||
const char *items[] = {"Interpreter",
|
||||
const char *items[] = {"Interpreter", "Cached Interpreter",
|
||||
#ifdef KAIZEN_JIT_ENABLED
|
||||
"Dynamic Recompiler"
|
||||
#endif
|
||||
@@ -37,8 +41,13 @@ void CPUSettings::render() {
|
||||
if (modified) {
|
||||
if (selectedCpuTypeIndex == 0) {
|
||||
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 {
|
||||
Options::GetInstance().SetValue<std::string>("cpu", "type", "jit");
|
||||
n64::Core::GetInstance().cpuType = n64::Core::DynamicRecompiler;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,7 +3,9 @@
|
||||
#include <common.hpp>
|
||||
|
||||
namespace n64 {
|
||||
|
||||
struct Instruction {
|
||||
Instruction() = default;
|
||||
Instruction(u32 v) { instr.raw = v; }
|
||||
void operator=(u32 v) { instr.raw = v; }
|
||||
operator u32() const { return instr.raw; }
|
||||
|
||||
Reference in New Issue
Block a user