Compare commits

...

12 Commits

Author SHA1 Message Date
iris 58e9cc1bc3 Merge branch 'dev' into meson 2026-05-29 20:06:56 +02:00
iris 24fb2898e9 attempting more serious idle skipping 2026-05-29 14:32:07 +02:00
iris 2147195774 Place rsp.Step inside cached interpreter. Gains about 3 more fps 2026-05-29 10:24:57 +02:00
iris bb97dcc23f mmmmm 2026-05-28 18:06:37 +02:00
iris 920b77d381 wjkhasdfjhkasdf 2026-05-28 17:57:46 +02:00
iris 430ccdab40 it's a start... 2026-05-28 17:53:52 +02:00
iris 4f42a673a3 Cached interpreter plays Mario 64. Start looking into RSP as well 2026-05-28 17:33:48 +02:00
iris c9a0307878 idle skipping works! 2026-05-22 00:20:17 +02:00
iris 5fbda03ceb new idea 2026-05-21 17:55:11 +02:00
iris 366637aba3 Idle skipping... maybe? 2026-05-21 17:16:43 +02:00
iris 609fa2fb08 Cache instructions implemented but broken lmao. Commented out for now 2026-04-29 12:05:45 +02:00
iris e140a6d124 - Stop using inheritance for CPU, instead use composition.
- Introduce KAIZEN_JIT_ENABLED optional define instead of relying on __aarch64__ and the like.
- More cache work
2026-04-28 18:01:43 +02:00
43 changed files with 8657 additions and 8098 deletions
+3
View File
@@ -4,6 +4,7 @@ saves/
.cache/
.vs/
.vscode/
.zed/
out/
*.toml
*.ini
@@ -20,9 +21,11 @@ vgcore.*
*.data
disasm.txt
*log*.txt
*.log
CMakeSettings.json
compile_commands.json
*.diagsession
tests/
.DS_Store
resources/version.hpp
__cmake_systeminformation/CMakeFiles/
+7
View File
@@ -13,6 +13,7 @@ if(APPLE)
enable_language(OBJC)
endif()
set(USE_JIT FALSE)
set(VULKAN_VALIDATION FALSE)
set(SANITIZERS FALSE)
set(CMAKE_CXX_STANDARD 23)
@@ -115,6 +116,9 @@ endif()
set(SIMD_FLAG NULL)
if(ARM64)
if(USE_JIT)
message(FATAL_ERROR "JIT unsupported in aarch64 at the moment")
endif()
message("Defining USE_NEON...")
add_compile_definitions(USE_NEON)
add_compile_definitions(SIMD_SUPPORT)
@@ -182,6 +186,9 @@ endif()
target_link_libraries(kaizen PUBLIC imgui SDL3::SDL3 SDL3::SDL3-static cflags::cflags ${MIO_LIB} parallel-rdp capstone backend)
target_compile_definitions(kaizen PUBLIC SDL_MAIN_HANDLED)
if(USE_JIT)
target_compile_definitions(kaizen PUBLIC KAIZEN_JIT_ENABLED)
endif()
if (SANITIZERS)
message("UBSAN AND ASAN: ON")
+6 -4
View File
@@ -34,7 +34,8 @@ Util::IntrusivePtr<Context> InitVulkanContext(WSIPlatform *platform, const unsig
new_context->set_num_thread_indices(num_thread_indices);
new_context->set_system_handles(system_handles);
if (!new_context->init_instance(instance_ext.data(), instance_ext.size(), CONTEXT_CREATION_ENABLE_ADVANCED_WSI_BIT)) {
if (!new_context->init_instance(instance_ext.data(), instance_ext.size(),
CONTEXT_CREATION_ENABLE_ADVANCED_WSI_BIT)) {
panic("Failed to create Vulkan instance.\n");
}
@@ -99,15 +100,16 @@ void ParallelRDP::Init(const std::shared_ptr<WSIPlatform> &wsiPlatform,
constexpr auto sizeVert = sizeof(vertex_shader);
constexpr auto sizeFrag = sizeof(fragment_shader);
fullscreen_quad_program = wsi->get_device().request_program(
reinterpret_cast<const u32 *>(vertex_shader), sizeVert, reinterpret_cast<const u32 *>(fragment_shader),
fullscreen_quad_program = wsi->get_device().request_program(reinterpret_cast<const u32 *>(vertex_shader), sizeVert,
reinterpret_cast<const u32 *>(fragment_shader),
sizeFrag, &vertLayout, &fragLayout);
auto aligned_rdram = reinterpret_cast<uintptr_t>(rdram);
uintptr_t offset = 0;
if (wsi->get_device().get_device_features().supports_external_memory_host) {
const size_t align = wsi->get_device().get_device_features().host_memory_properties.minImportedHostPointerAlignment;
const size_t align =
wsi->get_device().get_device_features().host_memory_properties.minImportedHostPointerAlignment;
offset = aligned_rdram & align - 1;
aligned_rdram -= offset;
}
+30 -28
View File
@@ -4,18 +4,20 @@
#include <Options.hpp>
namespace n64 {
Core::Core() {
Core::Core() :
interpreter(*mem, regs)
#ifdef KAIZEN_JIT_ENABLED
,
jit(*mem, regs)
#endif
{
const auto selectedCpu = Options::GetInstance().GetValue<std::string>("cpu", "type");
if (selectedCpu == "interpreter") {
cpuType = Interpreted;
cpu = std::make_unique<Interpreter>(*mem, regs);
} else if (selectedCpu == "jit") {
#ifndef __aarch64__
cpuType = DynamicRecompiler;
cpu = std::make_unique<JIT>(*mem, regs);
#else
panic("JIT currently unsupported on aarch64");
#endif
} else if (selectedCpu == "cached_interpreter") {
cpuType = CachedInterpreter;
} else {
panic("Unimplemented CPU type");
}
@@ -30,7 +32,7 @@ void Core::Stop() {
void Core::Reset() {
regs.Reset();
mem->Reset();
cpu->Reset();
interpreter.Reset();
if (romLoaded)
mem->mmio.si.pif.Execute();
}
@@ -62,7 +64,20 @@ void Core::LoadROM(const std::string &rom_) {
romLoaded = true;
}
u32 Core::StepCPU() { return cpu->Step() + regs.PopStalledCycles(); }
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();
#endif
panic("Invalid CPU type?");
}
void Core::StepRSP(const u32 cpuCycles) {
MMIO &mmio = mem->mmio;
@@ -91,35 +106,25 @@ void Core::Run(const float volumeL, const float volumeR) {
bool broken = false;
for (int field = 0; field < mmio.vi.numFields; field++) {
Scheduler::GetInstance().HandleEvents();
u32 frameCycles = 0;
for (int i = 0; i < mmio.vi.numHalflines; i++) {
mmio.vi.current = (i << 1) + field;
for (int halfline = 0; halfline < mmio.vi.numHalflines; halfline++) {
mmio.vi.current = (halfline << 1) + field;
if ((mmio.vi.current & 0x3FE) == mmio.vi.intr) {
mmio.mi.InterruptRaise(MI::Interrupt::VI);
}
while (cycles < mem->mmio.vi.cyclesPerHalfline) {
for (int cycles = 0; cycles < mem->mmio.vi.cyclesPerHalfline;) {
Scheduler::GetInstance().HandleEvents();
const u32 taken = StepCPU();
cycles += taken;
if ((broken = breakpoints.contains(regs.nextPC)))
break;
StepRSP(taken);
frameCycles += taken;
StepRSP(taken);
Scheduler::GetInstance().Tick(taken);
}
if (broken)
break;
cycles -= mmio.vi.cyclesPerHalfline;
}
if (broken)
break;
if ((mmio.vi.current & 0x3FE) == mmio.vi.intr) {
mmio.mi.InterruptRaise(MI::Interrupt::VI);
}
@@ -127,8 +132,5 @@ void Core::Run(const float volumeL, const float volumeR) {
mmio.ai.Step(frameCycles, volumeL, volumeR);
Scheduler::GetInstance().Tick(frameCycles);
}
if (broken)
pause = true;
}
} // namespace n64
+17 -14
View File
@@ -1,18 +1,16 @@
#pragma once
#include <ParallelRDPWrapper.hpp>
#include <backend/core/Interpreter.hpp>
#ifdef KAIZEN_JIT_ENABLED
#include <backend/core/JIT.hpp>
#endif
#include <string>
#include <set>
#include <variant>
#include <Registers.hpp>
namespace n64 {
struct Core {
enum CPUType {
Interpreted,
DynamicRecompiler,
CachedInterpreter
} cpuType = Interpreted;
enum CPUType { Interpreted, DynamicRecompiler, CachedInterpreter } cpuType = CachedInterpreter;
explicit Core();
@@ -21,13 +19,14 @@ struct Core {
return instance;
}
static Registers& GetRegs() {
return GetInstance().regs;
static inline bool IsAddressError(u8 mask, u64 vaddr) {
auto regs = GetRegs();
return (!regs.cop0.is64BitAddressing && s32(vaddr) != vaddr) || (vaddr & mask) != 0;
}
static Mem& GetMem() {
return *GetInstance().mem;
}
static Registers &GetRegs() { return GetInstance().regs; }
static Mem &GetMem() { return *GetInstance().mem; }
u32 StepCPU();
void StepRSP(u32 cpuCycles);
@@ -49,14 +48,18 @@ struct Core {
bool pause = true;
bool romLoaded = false;
int slot = 0;
u32 cycles = 0;
size_t memSize{}, cpuSize{}, verSize{};
std::string rom;
std::set<s64> breakpoints{};
#ifdef KAIZEN_JIT_ENABLED
JIT jit;
std::unique_ptr<Mem> mem = std::make_unique<Mem>(jit);
Registers regs(jit);
#else
std::unique_ptr<Mem> mem = std::make_unique<Mem>();
std::unique_ptr<BaseCPU> cpu;
Registers regs;
#endif
Interpreter interpreter;
ParallelRDP parallel;
};
} // namespace n64
+28 -12
View File
@@ -5,22 +5,34 @@ void Scheduler::EnqueueRelative(const u64 t, const EventType type) { EnqueueAbso
void Scheduler::EnqueueAbsolute(const u64 t, const EventType type) { events.push({t, type}); }
Event *Scheduler::Find(const EventType eventType) const {
for (auto &event : events) {
if (event.type == eventType) {
const u64 ret = event.time - ticks;
return &event;
}
}
return nullptr;
}
u64 Scheduler::Remove(const EventType eventType) const {
for (auto &[time, type] : events) {
if (type == eventType) {
const u64 ret = time - ticks;
type = NONE;
time = ticks;
auto event = Find(eventType);
if (!event)
return 0;
const u64 ret = event->time - ticks;
event->type = NONE;
event->time = ticks;
return ret;
}
}
return 0;
}
void Scheduler::SkipToNext() { ticks = events.top().time; }
void Scheduler::Tick(const u64 t) {
void Scheduler::Tick(const u64 t) { ticks += t; }
void Scheduler::HandleEvents() {
n64::Mem &mem = n64::Core::GetMem();
ticks += t;
n64::MI &mi = mem.mmio.mi;
n64::SI &si = mem.mmio.si;
n64::PI &pi = mem.mmio.pi;
@@ -40,10 +52,14 @@ void Scheduler::Tick(const u64 t) {
case NONE:
break;
case IMPOSSIBLE:
Util::Error::GetInstance().Throw({Util::Error::Severity::UNRECOVERABLE}, {Util::Error::Type::ROM_LOAD_ERROR}, {}, {}, "Unrecognized rom endianness");
Util::Error::GetInstance().Throw({Util::Error::Severity::UNRECOVERABLE},
{Util::Error::Type::ROM_LOAD_ERROR}, {}, {},
"Unrecognized rom endianness");
return;
default:
Util::Error::GetInstance().Throw({Util::Error::Severity::UNRECOVERABLE}, {Util::Error::Type::ROM_LOAD_ERROR}, {}, {}, "Unknown scheduler event type {}", static_cast<int>(type));
Util::Error::GetInstance().Throw({Util::Error::Severity::UNRECOVERABLE},
{Util::Error::Type::ROM_LOAD_ERROR}, {}, {},
"Unknown scheduler event type {}", static_cast<int>(type));
return;
}
events.pop();
+3
View File
@@ -35,10 +35,13 @@ struct Scheduler {
return instance;
}
void HandleEvents();
void EnqueueRelative(u64, EventType);
void EnqueueAbsolute(u64, EventType);
[[nodiscard]] u64 Remove(EventType) const;
[[nodiscard]] Event *Find(EventType) const;
void Tick(u64 t);
void SkipToNext();
u8 index = 0;
u64 ticks = 0;
-33
View File
@@ -1,33 +0,0 @@
#pragma once
#include <Mem.hpp>
#include <Registers.hpp>
#include <Disassembler.hpp>
namespace n64 {
struct alignas(32) InstructionCache {
bool valid;
u32 data[8];
u32 ptag;
private:
int GetLineIndex(u64 vaddr) { return (vaddr >> 5) & 0x1FF; }
u32 GetLineStart(u64 paddr) { return paddr & ~0x1F; }
};
struct alignas(32) DataCache {
bool valid, dirty;
u8 data[16];
u32 ptag;
int index;
private:
int GetLineIndex(u64 vaddr) { return (vaddr >> 4) & 0x1FF; }
u32 GetLineStart(u64 paddr) { return paddr & ~0xF; }
};
struct BaseCPU {
virtual ~BaseCPU() = default;
virtual u32 Step() = 0;
virtual void Reset() = 0;
};
} // namespace n64
+2 -2
View File
@@ -2,7 +2,7 @@ file(GLOB SOURCES *.cpp)
file(GLOB HEADERS *.hpp)
add_subdirectory(interpreter)
if(NOT ARM64)
if(USE_JIT)
add_subdirectory(jit)
endif()
add_subdirectory(mem)
@@ -12,6 +12,6 @@ add_subdirectory(rsp)
add_library(core ${SOURCES} ${HEADERS})
target_link_libraries(core PRIVATE interpreter mem mmio unarr registers rsp)
if(NOT ARM64)
if(USE_JIT)
target_link_libraries(core PRIVATE jit)
endif()
+96
View File
@@ -0,0 +1,96 @@
#include <Core.hpp>
namespace n64 {
void InstructionCache::FillLine(u64 vaddr, u32 paddr) {
int index = GetICacheLineIndex(vaddr);
auto &line = lines[index];
line.valid = true;
line.ptag = paddr & ~0x00000fff;
auto &mmio = Core::GetMem().mmio;
for (int i = 0; i < 8; i++) {
line.data[i] = mmio.rdp.ReadRDRAM<u32>(line.ptag | index);
}
}
void InstructionCache::StoreTag(u64 vaddr, u32 ptag, Cop0 &cop0) {
auto &line = lines[GetICacheLineIndex(vaddr)];
line.valid = (cop0.tagLo.pstate >> 1) & 1;
line.ptag = ptag;
}
void DataCache::StoreTag(u64 vaddr, u32 ptag, Cop0 &cop0) {
auto &line = lines[GetDCacheLineIndex(vaddr)];
line.valid = (cop0.tagLo.pstate >> 1) & 1;
line.dirty = (cop0.tagLo.pstate >> 0) & 1;
line.ptag = ptag;
}
void InstructionCache::LoadTag(u64 vaddr) {
auto &cop0 = Core::GetRegs().cop0;
auto &line = lines[GetICacheLineIndex(vaddr)];
cop0.tagLo.pstate = line.valid << 1;
cop0.tagLo.ptaglo = line.ptag;
}
void DataCache::LoadTag(u64 vaddr) {
auto &cop0 = Core::GetRegs().cop0;
auto &line = lines[GetDCacheLineIndex(vaddr)];
cop0.tagLo.pstate = (line.valid << 1) | line.dirty;
cop0.tagLo.ptaglo = line.ptag;
}
template <>
void DataCache::WriteBack<false>(u64 vaddr, u32 paddr) {
auto &mmio = Core::GetMem().mmio;
DCacheLine &line = lines[GetDCacheLineIndex(vaddr)];
if (!line.valid)
return;
u32 origPhysAddr = (line.ptag << 12) | (paddr & 0xfff);
u32 lineStart = GetDCacheLineStart(origPhysAddr);
Core::GetInstance().interpreter.cachedState.EvictCachedBlock(vaddr);
for (int i = 0; i < 16; i++) {
mmio.rdp.WriteRDRAM(lineStart + i, line.data[i]);
}
line.dirty = false;
}
template <>
void DataCache::WriteBack<true>(u64 vaddr, u32 paddr) {
WriteBack<false>(vaddr, paddr);
lines[GetDCacheLineIndex(vaddr)].valid = false;
}
template <>
void DataCache::WriteBack<false>(u64 vaddr, u32 paddr, u32 ptag) {
DCacheLine &line = lines[GetDCacheLineIndex(vaddr)];
if (line.ptag == ptag)
WriteBack(vaddr, paddr);
}
template <>
void DataCache::WriteBack<true>(u64 vaddr, u32 paddr, u32 ptag) {
DCacheLine &line = lines[GetDCacheLineIndex(vaddr)];
if (line.ptag == ptag)
WriteBack<true>(vaddr, paddr);
}
void InstructionCache::WriteBack(u64 vaddr, u32 paddr, u32 ptag) {
auto &mmio = Core::GetMem().mmio;
ICacheLine &line = lines[GetICacheLineIndex(vaddr)];
if (line.ptag == ptag && line.valid) {
u32 origPhysAddr = (line.ptag << 12) | (paddr & 0xfff);
u32 lineStart = GetICacheLineStart(origPhysAddr);
Core::GetInstance().interpreter.cachedState.EvictCachedBlock(vaddr);
for (int i = 0; i < 16; i++) {
mmio.rdp.WriteRDRAM(lineStart + i, line.data[i]);
}
}
}
} // namespace n64
+61
View File
@@ -0,0 +1,61 @@
#pragma once
#include <common.hpp>
#include <array>
namespace n64 {
struct alignas(32) ICacheLine {
bool valid;
u32 data[8];
u32 ptag;
};
struct alignas(32) DCacheLine {
bool valid, dirty;
u8 data[16];
u32 ptag;
int index;
};
struct Cop0;
inline const u32 GetPhysicalAddressPTag(const u32 paddr) { return paddr >> 12; }
inline const int GetICacheLineIndex(u64 vaddr) { return (vaddr >> 5) & 0x1FF; }
inline const u32 GetICacheLineStart(u64 paddr) { return paddr & ~0x1F; }
inline const int GetDCacheLineIndex(u64 vaddr) { return (vaddr >> 4) & 0x1FF; }
inline const u32 GetDCacheLineStart(u64 paddr) { return paddr & ~0xF; }
struct InstructionCache {
std::array<ICacheLine, 512> lines;
void FillLine(u64, u32);
void InvalidateIndex(u64 vaddr) { lines[GetICacheLineIndex(vaddr)].valid = false; }
void StoreTag(u64, u32, Cop0 &);
void LoadTag(u64 vaddr);
void WriteBack(u64 vaddr, u32 paddr, u32 ptag);
void InvalidateIndex(u64 vaddr, u32 ptag) {
int lineIndex = GetICacheLineIndex(vaddr);
if (lines[lineIndex].valid && lines[lineIndex].ptag == ptag)
lines[lineIndex].valid = false;
}
private:
};
struct DataCache {
std::array<DCacheLine, 512> lines;
void StoreTag(u64, u32, Cop0 &);
void LoadTag(u64 vaddr);
template <bool invalidate = false>
void WriteBack(u64 vaddr, u32 paddr);
template <bool invalidate = false>
void WriteBack(u64 vaddr, u32 paddr, u32 ptag);
void InvalidateIndex(u64 vaddr) { lines[GetDCacheLineIndex(vaddr)].valid = false; }
void InvalidateIndex(u64 vaddr, u32 ptag) {
int lineIndex = GetDCacheLineIndex(vaddr);
if (lines[lineIndex].valid && lines[lineIndex].ptag == ptag)
lines[lineIndex].valid = false;
}
};
} // namespace n64
+141 -18
View File
@@ -1,4 +1,6 @@
#include <Core.hpp>
#include <Scheduler.hpp>
#include "jit/helpers.hpp"
namespace n64 {
Interpreter::Interpreter(Mem &mem, Registers &regs) : regs(regs), mem(mem) {}
@@ -12,7 +14,7 @@ bool Interpreter::ShouldServiceInterrupt() const {
return interrupts_pending && interrupts_enabled && !currently_handling_exception && !currently_handling_error;
}
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(ExceptionCode::AddressErrorLoad, 0, regs.pc);
return 1;
regs.cop0.FireException(Cop0::ExceptionCode::AddressErrorLoad, 0, regs.pc);
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(ExceptionCode::Interrupt, 0, regs.pc);
return 1;
regs.cop0.FireException(Cop0::ExceptionCode::Interrupt, 0, regs.pc);
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++) {
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 (line->idleSkip) {
Scheduler::GetInstance().SkipToNext();
}
if (blockCycles == 0)
panic("Cycles are 0");
return blockCycles;
}
return CacheBlock(addr);
}
} // namespace n64
+31 -11
View File
@@ -1,34 +1,53 @@
#pragma once
#include <BaseCPU.hpp>
#include <Cache.hpp>
#include <Mem.hpp>
#include <vector>
#include <JITUtils.hpp>
namespace n64 {
struct Core;
struct Interpreter final : BaseCPU {
struct Interpreter final {
explicit Interpreter(Mem &, Registers &);
~Interpreter() override = default;
u32 Step() override;
~Interpreter() = default;
u32 Step();
u32 ExecuteCached();
bool FetchThenMaybeAdvance(Instruction &);
bool MaybeAdvance();
u32 CacheBlock(u32 addr);
void Reset() override {
void SignalException(u32 addr) { cachedState.exception = true; }
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 &regs;
Mem &mem;
u64 cop2Latch{};
friend struct Cop1;
#define check_address_error(mask, vaddr) \
(((!regs.cop0.is64BitAddressing) && (s32)(vaddr) != (vaddr)) || (((vaddr) & (mask)) != 0))
u32 rspSyncCount = 0;
bool Fetch(Instruction &, u64);
void CacheTypeData(u8, u64, u32, u32);
void CacheTypeInstruction(u8, u64, u32, u32);
[[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,6 +60,7 @@ private:
void blink(Instruction, bool);
void bl(Instruction, bool);
void bllink(Instruction, bool);
void cache(Instruction);
void dadd(Instruction);
void daddu(Instruction);
void daddi(Instruction);
+16 -19
View File
@@ -1,11 +1,9 @@
#include <Core.hpp>
#include <jit/helpers.hpp>
#include <JIT.hpp>
#include <Disassembler.hpp>
namespace n64 {
#ifndef __aarch64__
JIT::JIT(Mem &mem, Registers &regs) : regs(regs), mem(mem) {
regs.SetJIT(this);
mem.SetJIT(this);
blockCache.resize(kUpperSize);
if (cs_open(CS_ARCH_MIPS, static_cast<cs_mode>(CS_MODE_MIPS64 | CS_MODE_BIG_ENDIAN), &disassemblerMips) !=
CS_ERR_OK) {
@@ -43,12 +41,12 @@ 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(ExceptionCode::AddressErrorLoad, 0, blockPC);
regs.cop0.FireException(Cop0::ExceptionCode::AddressErrorLoad, 0, blockPC);
return 1;*/
Util::Error::GetInstance().Throw(
{Util::Error::Severity::NON_FATAL}, {Util::Error::Type::UNHANDLED_EXCEPTION}, blockPC, {},
Util::Error::GetInstance().Throw({Util::Error::Severity::NON_FATAL}, {Util::Error::Type::UNHANDLED_EXCEPTION},
blockPC, {},
"[JIT]: Unhandled exception ADL due to unaligned PC virtual value!");
return std::nullopt;
}
@@ -118,8 +116,8 @@ u32 JIT::Step() {
/*regs.cop0.HandleTLBException(blockPC);
regs.cop0.FireException(Cop0::GetTLBExceptionCode(regs.cop0.tlbError, Cop0::LOAD), 0, blockPC);
return 1;*/
Util::Error::GetInstance().Throw({Util::Error::Severity::NON_FATAL}, {Util::Error::Type::UNHANDLED_EXCEPTION},
blockPC, {},
Util::Error::GetInstance().Throw(
{Util::Error::Severity::NON_FATAL}, {Util::Error::Type::UNHANDLED_EXCEPTION}, blockPC, {},
"[JIT]: Unhandled exception TLB exception {} when retrieving PC physical address!",
static_cast<int>(Cop0::GetTLBExceptionCode(regs.cop0.tlbError, Cop0::LOAD)));
return 0;
@@ -177,9 +175,9 @@ u32 JIT::Step() {
return 0;
if (InstrEndsBlock(delay_instruction.value())) {
Util::Error::GetInstance().Throw(
{Util::Error::Severity::NON_FATAL}, {Util::Error::Type::JIT_BRANCH_INSIDE_DELAY_SLOT},
blockPC, {}, "[JIT]: Unhandled case of branch from delay slot!");
Util::Error::GetInstance().Throw({Util::Error::Severity::NON_FATAL},
{Util::Error::Type::JIT_BRANCH_INSIDE_DELAY_SLOT}, blockPC, {},
"[JIT]: Unhandled case of branch from delay slot!");
return 0;
}
@@ -206,7 +204,8 @@ u32 JIT::Step() {
code.L(runtime_branch_taken);
}
if(branch_taken) branch_taken = false;
if (branch_taken)
branch_taken = false;
emitMemberFunctionCall(&JIT::AdvanceDelaySlot, this);
@@ -227,7 +226,8 @@ u32 JIT::Step() {
blockInfoSize = code.getSize() - blockInfoSize;
info("\tX86 code (block address = 0x{:016X}):", reinterpret_cast<uintptr_t>(block));
const auto count = cs_disasm(disassemblerX86, blockInfo, blockInfoSize, reinterpret_cast<uintptr_t>(block), 0, &insn);
const auto count =
cs_disasm(disassemblerX86, blockInfo, blockInfoSize, reinterpret_cast<uintptr_t>(block), 0, &insn);
if (count > 0) {
for (size_t j = 0; j < count; j++) {
info("\t\t0x{:016X}:\t{}\t\t{}", insn[j].address, insn[j].mnemonic, insn[j].op_str);
@@ -239,8 +239,5 @@ u32 JIT::Step() {
return block();
}
void JIT::DumpBlockCacheToDisk() const {
ircolib::WriteFileBinary(code.getCode<u8*>(), code.getSize(), "jit.dump");
}
#endif
void JIT::DumpBlockCacheToDisk() const { ircolib::WriteFileBinary(code.getCode<u8 *>(), code.getSize(), "jit.dump"); }
} // namespace n64
+26 -41
View File
@@ -1,5 +1,6 @@
#pragma once
#include <BaseCPU.hpp>
#include <Cache.hpp>
#include <Registers.hpp>
#include <Mem.hpp>
#include <vector>
#include <xbyak.h>
@@ -25,15 +26,12 @@ static constexpr u32 kCodeCacheAllocSize = kCodeCacheSize + 4_kb;
#define HI_OFFSET (reinterpret_cast<uintptr_t>(&regs.hi) - reinterpret_cast<uintptr_t>(this))
#define LO_OFFSET (reinterpret_cast<uintptr_t>(&regs.lo) - reinterpret_cast<uintptr_t>(this))
#ifdef __aarch64__
struct JIT : BaseCPU {};
#else
struct JIT final : BaseCPU {
struct JIT final {
explicit JIT(Mem &, Registers &);
~JIT() override = default;
u32 Step() override;
~JIT() = default;
u32 Step();
void Reset() override {
void Reset() {
code.reset();
blockCache = {};
blockCache.resize(kUpperSize);
@@ -47,6 +45,7 @@ struct JIT final : BaseCPU {
}
void InvalidateBlock(u32);
private:
friend struct Cop1;
friend struct Registers;
@@ -73,9 +72,9 @@ private:
return code.qword[code.rbp + GPR_OFFSET(index)];
}
Util::Error::GetInstance().Throw(
{Util::Error::Severity::UNRECOVERABLE}, {Util::Error::Type::JIT_INVALID_X86_REG_ADDRESSING},
blockPC, {}, "[JIT]: Invalid register addressing mode {}!", sizeof(T));
Util::Error::GetInstance().Throw({Util::Error::Severity::UNRECOVERABLE},
{Util::Error::Type::JIT_INVALID_X86_REG_ADDRESSING}, blockPC, {},
"[JIT]: Invalid register addressing mode {}!", sizeof(T));
return Xbyak::Address{0};
}
@@ -117,9 +116,6 @@ private:
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);
@@ -205,54 +201,44 @@ private:
void mtlo(Instruction);
void nor(Instruction);
void sb(const Instruction) {
Util::Error::GetInstance().Throw(
{Util::Error::Severity::NON_FATAL}, {Util::Error::Type::UNHANDLED_INSTRUCTION},
Util::Error::GetInstance().Throw({Util::Error::Severity::NON_FATAL}, {Util::Error::Type::UNHANDLED_INSTRUCTION},
blockPC, {}, "[JIT]: Unhandled 'sb'!");
}
void sc(const Instruction) {
Util::Error::GetInstance().Throw(
{Util::Error::Severity::NON_FATAL}, {Util::Error::Type::UNHANDLED_INSTRUCTION},
Util::Error::GetInstance().Throw({Util::Error::Severity::NON_FATAL}, {Util::Error::Type::UNHANDLED_INSTRUCTION},
blockPC, {}, "[JIT]: Unhandled 'sc'!");
}
void scd(const Instruction) {
Util::Error::GetInstance().Throw(
{Util::Error::Severity::NON_FATAL}, {Util::Error::Type::UNHANDLED_INSTRUCTION},
Util::Error::GetInstance().Throw({Util::Error::Severity::NON_FATAL}, {Util::Error::Type::UNHANDLED_INSTRUCTION},
blockPC, {}, "[JIT]: Unhandled 'scd'!");
}
void sd(const Instruction) {
Util::Error::GetInstance().Throw(
{Util::Error::Severity::NON_FATAL}, {Util::Error::Type::UNHANDLED_INSTRUCTION},
Util::Error::GetInstance().Throw({Util::Error::Severity::NON_FATAL}, {Util::Error::Type::UNHANDLED_INSTRUCTION},
blockPC, {}, "[JIT]: Unhandled 'sd'!");
}
void sdc1(const Instruction) {
Util::Error::GetInstance().Throw(
{Util::Error::Severity::NON_FATAL}, {Util::Error::Type::UNHANDLED_INSTRUCTION},
Util::Error::GetInstance().Throw({Util::Error::Severity::NON_FATAL}, {Util::Error::Type::UNHANDLED_INSTRUCTION},
blockPC, {}, "[JIT]: Unhandled 'sdc1'!");
}
void sdl(const Instruction) {
Util::Error::GetInstance().Throw(
{Util::Error::Severity::NON_FATAL}, {Util::Error::Type::UNHANDLED_INSTRUCTION},
Util::Error::GetInstance().Throw({Util::Error::Severity::NON_FATAL}, {Util::Error::Type::UNHANDLED_INSTRUCTION},
blockPC, {}, "[JIT]: Unhandled 'sdl'!");
}
void sdr(const Instruction) {
Util::Error::GetInstance().Throw(
{Util::Error::Severity::NON_FATAL}, {Util::Error::Type::UNHANDLED_INSTRUCTION},
Util::Error::GetInstance().Throw({Util::Error::Severity::NON_FATAL}, {Util::Error::Type::UNHANDLED_INSTRUCTION},
blockPC, {}, "[JIT]: Unhandled 'sdr'!");
}
void sh(const Instruction) {
Util::Error::GetInstance().Throw(
{Util::Error::Severity::NON_FATAL}, {Util::Error::Type::UNHANDLED_INSTRUCTION},
Util::Error::GetInstance().Throw({Util::Error::Severity::NON_FATAL}, {Util::Error::Type::UNHANDLED_INSTRUCTION},
blockPC, {}, "[JIT]: Unhandled 'sh'!");
}
void sw(Instruction);
void swl(const Instruction) {
Util::Error::GetInstance().Throw(
{Util::Error::Severity::NON_FATAL}, {Util::Error::Type::UNHANDLED_INSTRUCTION},
Util::Error::GetInstance().Throw({Util::Error::Severity::NON_FATAL}, {Util::Error::Type::UNHANDLED_INSTRUCTION},
blockPC, {}, "[JIT]: Unhandled 'swl'!");
}
void swr(const Instruction) {
Util::Error::GetInstance().Throw(
{Util::Error::Severity::NON_FATAL}, {Util::Error::Type::UNHANDLED_INSTRUCTION},
Util::Error::GetInstance().Throw({Util::Error::Severity::NON_FATAL}, {Util::Error::Type::UNHANDLED_INSTRUCTION},
blockPC, {}, "[JIT]: Unhandled 'swr'!");
}
void slti(Instruction);
@@ -264,23 +250,22 @@ private:
void sub(Instruction);
void subu(Instruction);
void swc1(const Instruction) {
Util::Error::GetInstance().Throw(
{Util::Error::Severity::NON_FATAL}, {Util::Error::Type::JIT_BRANCH_INSIDE_DELAY_SLOT},
blockPC, {}, "[JIT]: Unhandled case of branch from delay slot!");
Util::Error::GetInstance().Throw({Util::Error::Severity::NON_FATAL},
{Util::Error::Type::JIT_BRANCH_INSIDE_DELAY_SLOT}, blockPC, {},
"[JIT]: Unhandled case of branch from delay slot!");
}
void sra(Instruction);
void srav(Instruction);
void srl(Instruction);
void srlv(Instruction);
void trap(bool) {
Util::Error::GetInstance().Throw(
{Util::Error::Severity::NON_FATAL}, {Util::Error::Type::JIT_BRANCH_INSIDE_DELAY_SLOT},
blockPC, {}, "[JIT]: Unhandled case of branch from delay slot!");
Util::Error::GetInstance().Throw({Util::Error::Severity::NON_FATAL},
{Util::Error::Type::JIT_BRANCH_INSIDE_DELAY_SLOT}, blockPC, {},
"[JIT]: Unhandled case of branch from delay slot!");
}
void or_(Instruction);
void ori(Instruction);
void xor_(Instruction);
void xori(Instruction);
};
#endif
} // namespace n64
+45
View File
@@ -0,0 +1,45 @@
#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 {
bool idleSkip = false;
std::array<Instruction, MAX_INSTR_PER_BLOCK> code = {};
u32 len = 0;
u32 cycles = 0;
};
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 EvictCachedBlock(u32 addr) { blocks[addr / MAX_LINES] = {}; }
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
+140 -118
View File
@@ -4,9 +4,14 @@
#include <cassert>
#include <Core.hpp>
#include <Options.hpp>
#include <Registers.hpp>
namespace n64 {
#ifdef KAIZEN_JIT_ENABLED
Mem::Mem(JIT &jit) : flash(saveData), jit(jit) {
#else
Mem::Mem() : flash(saveData) {
#endif
rom.cart.resize(CART_SIZE);
std::ranges::fill(rom.cart, 0);
isviewer_sink = std::ofstream("isviewer.log");
@@ -19,9 +24,9 @@ void Mem::Reset() {
std::error_code error;
saveData.sync(error);
if (error) {
Util::Error::GetInstance().Throw(
{Util::Error::Severity::NON_FATAL}, {Util::Error::Type::COULD_NOT_SYNC_SAVE_DATA},
{}, {}, "[Mem]: Could not sync save data!");
Util::Error::GetInstance().Throw({Util::Error::Severity::NON_FATAL},
{Util::Error::Type::COULD_NOT_SYNC_SAVE_DATA}, {}, {},
"[Mem]: Could not sync save data!");
return;
}
saveData.unmap();
@@ -40,9 +45,9 @@ void Mem::LoadSRAM(SaveType save_type, fs::path path) {
if (saveData.is_mapped()) {
saveData.sync(error);
if (error) {
Util::Error::GetInstance().Throw(
{Util::Error::Severity::NON_FATAL}, {Util::Error::Type::COULD_NOT_SYNC_SAVE_DATA},
{}, {}, R"([Mem]: Could not sync save data stored @ "{}")", sramPath);
Util::Error::GetInstance().Throw({Util::Error::Severity::NON_FATAL},
{Util::Error::Type::COULD_NOT_SYNC_SAVE_DATA}, {}, {},
R"([Mem]: Could not sync save data stored @ "{}")", sramPath);
return;
}
saveData.unmap();
@@ -56,16 +61,16 @@ void Mem::LoadSRAM(SaveType save_type, fs::path path) {
if (sramVec.size() != SRAM_SIZE) {
Util::Error::GetInstance().Throw(
{Util::Error::Severity::NON_FATAL}, {Util::Error::Type::SAVE_DATA_IS_CORRUPT_OR_INVALID_SIZE},
{}, {}, "[Mem]: Save data is corrupt or has unexpected size! (it's {} KiB)", sramVec.size() / 1024);
{Util::Error::Severity::NON_FATAL}, {Util::Error::Type::SAVE_DATA_IS_CORRUPT_OR_INVALID_SIZE}, {}, {},
"[Mem]: Save data is corrupt or has unexpected size! (it's {} KiB)", sramVec.size() / 1024);
return;
}
saveData = mio::make_mmap_sink(sramPath, error);
if (error) {
Util::Error::GetInstance().Throw(
{Util::Error::Severity::NON_FATAL}, {Util::Error::Type::MMAP_MAKE_SINK_ERROR},
{}, {}, R"([Mem]: Could not create file sink for save data @ "{}")", sramPath);
Util::Error::GetInstance().Throw({Util::Error::Severity::NON_FATAL},
{Util::Error::Type::MMAP_MAKE_SINK_ERROR}, {}, {},
R"([Mem]: Could not create file sink for save data @ "{}")", sramPath);
}
}
}
@@ -148,13 +153,15 @@ u8 Mem::Read(const u32 paddr) {
n64::Registers &regs = n64::Core::GetRegs();
const SI &si = mmio.si;
if(ircolib::IsInsideRange(paddr, RDRAM_REGION_START, RDRAM_REGION_END)) return mmio.rdp.ReadRDRAM<u8>(paddr);
if (ircolib::IsInsideRange(paddr, RDRAM_REGION_START, RDRAM_REGION_END))
return mmio.rdp.ReadRDRAM<u8>(paddr);
if (ircolib::IsInsideRange(paddr, DMEM_REGION_START, RSP_MEM_REGION_END)) {
const auto &src = paddr & 0x1000 ? mmio.rsp.imem : mmio.rsp.dmem;
return src[BYTE_ADDRESS(paddr & 0xfff)];
}
if(ircolib::IsInsideRange(paddr, CART_REGION_START_2_1, CART_REGION_END_1_2)) return mmio.pi.BusRead<u8, false>(paddr);
if (ircolib::IsInsideRange(paddr, CART_REGION_START_2_1, CART_REGION_END_1_2))
return mmio.pi.BusRead<u8, false>(paddr);
if (ircolib::IsInsideRange(paddr, AI_REGION_START, AI_REGION_END)) {
const u32 w = mmio.ai.Read(paddr & ~3);
const int offs = 3 - (paddr & 3);
@@ -169,16 +176,20 @@ u8 Mem::Read(const u32 paddr) {
return 0;
}
if(ircolib::IsInsideRange(paddr, PIF_ROM_REGION_START, PIF_ROM_REGION_END)) return si.pif.bootrom[BYTE_ADDRESS(paddr) - PIF_ROM_REGION_START];
if(ircolib::IsInsideRange(paddr, PIF_RAM_REGION_START, PIF_RAM_REGION_END)) return si.pif.ram[paddr - PIF_RAM_REGION_START];
if (ircolib::IsInsideRange(paddr, PIF_ROM_REGION_START, PIF_ROM_REGION_END))
return si.pif.bootrom[BYTE_ADDRESS(paddr) - PIF_ROM_REGION_START];
if (ircolib::IsInsideRange(paddr, PIF_RAM_REGION_START, PIF_RAM_REGION_END))
return si.pif.ram[paddr - PIF_RAM_REGION_START];
if (ircolib::IsInsideRange(paddr, UNUSED_START_1, UNUSED_END_1) || // unused
ircolib::IsInsideRange(paddr, UNUSED_START_2, UNUSED_END_2) ||
ircolib::IsInsideRange(paddr, UNUSED_START_3, UNUSED_END_3) ||
ircolib::IsInsideRange(paddr, UNUSED_START_4, UNUSED_END_4)) return 0;
ircolib::IsInsideRange(paddr, UNUSED_START_4, UNUSED_END_4))
return 0;
Util::Error::GetInstance().Throw(
{Util::Error::Severity::NON_FATAL}, {Util::Error::Type::MEM_UNHANDLED_ACCESS}, regs.pc,
Util::Error::MemoryAccess{false, Util::Error::MemoryAccess::BYTE, paddr, 0}, "8-bit read access in unhandled region");
Util::Error::GetInstance().Throw({Util::Error::Severity::NON_FATAL}, {Util::Error::Type::MEM_UNHANDLED_ACCESS},
regs.pc,
Util::Error::MemoryAccess{false, Util::Error::MemoryAccess::BYTE, paddr, 0},
"8-bit read access in unhandled region");
return 0;
}
@@ -187,25 +198,32 @@ u16 Mem::Read(const u32 paddr) {
n64::Registers &regs = n64::Core::GetRegs();
const SI &si = mmio.si;
if(ircolib::IsInsideRange(paddr, RDRAM_REGION_START, RDRAM_REGION_END)) return mmio.rdp.ReadRDRAM<u16>(paddr);
if (ircolib::IsInsideRange(paddr, RDRAM_REGION_START, RDRAM_REGION_END))
return mmio.rdp.ReadRDRAM<u16>(paddr);
if (ircolib::IsInsideRange(paddr, DMEM_REGION_START, RSP_MEM_REGION_END)) {
const auto &src = paddr & 0x1000 ? mmio.rsp.imem : mmio.rsp.dmem;
return ircolib::ReadAccess<u16>(src, HALF_ADDRESS(paddr & 0xfff));
}
if(ircolib::IsInsideRange(paddr, CART_REGION_START_2_1, CART_REGION_END_1_2)) return mmio.pi.BusRead<u16, false>(paddr);
if (ircolib::IsInsideRange(paddr, CART_REGION_START_2_1, CART_REGION_END_1_2))
return mmio.pi.BusRead<u16, false>(paddr);
if (ircolib::IsInsideRange(paddr, MMIO_REGION_START_1, MMIO_REGION_END_1) ||
ircolib::IsInsideRange(paddr, MMIO_REGION_START_2, MMIO_REGION_END_2)) return mmio.Read(paddr);
if(ircolib::IsInsideRange(paddr, PIF_ROM_REGION_START, PIF_ROM_REGION_END)) return ircolib::ReadAccess<u16>(si.pif.bootrom, HALF_ADDRESS(paddr) - PIF_ROM_REGION_START);
if(ircolib::IsInsideRange(paddr, PIF_RAM_REGION_START, PIF_RAM_REGION_END)) return std::byteswap(ircolib::ReadAccess<u16>(si.pif.ram, paddr - PIF_RAM_REGION_START));
ircolib::IsInsideRange(paddr, MMIO_REGION_START_2, MMIO_REGION_END_2))
return mmio.Read(paddr);
if (ircolib::IsInsideRange(paddr, PIF_ROM_REGION_START, PIF_ROM_REGION_END))
return ircolib::ReadAccess<u16>(si.pif.bootrom, HALF_ADDRESS(paddr) - PIF_ROM_REGION_START);
if (ircolib::IsInsideRange(paddr, PIF_RAM_REGION_START, PIF_RAM_REGION_END))
return std::byteswap(ircolib::ReadAccess<u16>(si.pif.ram, paddr - PIF_RAM_REGION_START));
if (ircolib::IsInsideRange(paddr, UNUSED_START_1, UNUSED_END_1) || // unused
ircolib::IsInsideRange(paddr, UNUSED_START_2, UNUSED_END_2) ||
ircolib::IsInsideRange(paddr, UNUSED_START_3, UNUSED_END_3) ||
ircolib::IsInsideRange(paddr, UNUSED_START_4, UNUSED_END_4)) return 0;
ircolib::IsInsideRange(paddr, UNUSED_START_4, UNUSED_END_4))
return 0;
Util::Error::GetInstance().Throw(
{Util::Error::Severity::NON_FATAL}, {Util::Error::Type::MEM_UNHANDLED_ACCESS}, regs.pc,
Util::Error::MemoryAccess{false, Util::Error::MemoryAccess::SHORT, paddr, 0}, "16-bit read access in unhandled region");
Util::Error::GetInstance().Throw({Util::Error::Severity::NON_FATAL}, {Util::Error::Type::MEM_UNHANDLED_ACCESS},
regs.pc,
Util::Error::MemoryAccess{false, Util::Error::MemoryAccess::SHORT, paddr, 0},
"16-bit read access in unhandled region");
return 0;
}
@@ -214,26 +232,33 @@ u32 Mem::Read(const u32 paddr) {
n64::Registers &regs = n64::Core::GetRegs();
const SI &si = mmio.si;
if(ircolib::IsInsideRange(paddr, RDRAM_REGION_START, RDRAM_REGION_END)) return mmio.rdp.ReadRDRAM<u32>(paddr);
if (ircolib::IsInsideRange(paddr, RDRAM_REGION_START, RDRAM_REGION_END))
return mmio.rdp.ReadRDRAM<u32>(paddr);
if (ircolib::IsInsideRange(paddr, DMEM_REGION_START, RSP_MEM_REGION_END)) {
const auto &src = paddr & 0x1000 ? mmio.rsp.imem : mmio.rsp.dmem;
return ircolib::ReadAccess<u32>(src, paddr & 0xfff);
}
if(ircolib::IsInsideRange(paddr, CART_REGION_START_2_1, CART_REGION_END_1_2)) return mmio.pi.BusRead<u32, false>(paddr);
if (ircolib::IsInsideRange(paddr, CART_REGION_START_2_1, CART_REGION_END_1_2))
return mmio.pi.BusRead<u32, false>(paddr);
if (ircolib::IsInsideRange(paddr, MMIO_REGION_START_1, MMIO_REGION_END_1) ||
ircolib::IsInsideRange(paddr, MMIO_REGION_START_2, MMIO_REGION_END_2)) return mmio.Read(paddr);
ircolib::IsInsideRange(paddr, MMIO_REGION_START_2, MMIO_REGION_END_2))
return mmio.Read(paddr);
if(ircolib::IsInsideRange(paddr, PIF_ROM_REGION_START, PIF_ROM_REGION_END)) return ircolib::ReadAccess<u32>(si.pif.bootrom, paddr - PIF_ROM_REGION_START);
if(ircolib::IsInsideRange(paddr, PIF_RAM_REGION_START, PIF_RAM_REGION_END)) return std::byteswap(ircolib::ReadAccess<u32>(si.pif.ram, paddr - PIF_RAM_REGION_START));
if (ircolib::IsInsideRange(paddr, PIF_ROM_REGION_START, PIF_ROM_REGION_END))
return ircolib::ReadAccess<u32>(si.pif.bootrom, paddr - PIF_ROM_REGION_START);
if (ircolib::IsInsideRange(paddr, PIF_RAM_REGION_START, PIF_RAM_REGION_END))
return std::byteswap(ircolib::ReadAccess<u32>(si.pif.ram, paddr - PIF_RAM_REGION_START));
if (ircolib::IsInsideRange(paddr, UNUSED_START_1, UNUSED_END_1) || // unused
ircolib::IsInsideRange(paddr, UNUSED_START_2, UNUSED_END_2) ||
ircolib::IsInsideRange(paddr, UNUSED_START_3, UNUSED_END_3) ||
ircolib::IsInsideRange(paddr, UNUSED_START_4, UNUSED_END_4)) return 0;
ircolib::IsInsideRange(paddr, UNUSED_START_4, UNUSED_END_4))
return 0;
Util::Error::GetInstance().Throw(
{Util::Error::Severity::NON_FATAL}, {Util::Error::Type::MEM_UNHANDLED_ACCESS}, regs.pc,
Util::Error::MemoryAccess{false, Util::Error::MemoryAccess::WORD, paddr, 0}, "32-bit read access in unhandled region");
Util::Error::GetInstance().Throw({Util::Error::Severity::NON_FATAL}, {Util::Error::Type::MEM_UNHANDLED_ACCESS},
regs.pc,
Util::Error::MemoryAccess{false, Util::Error::MemoryAccess::WORD, paddr, 0},
"32-bit read access in unhandled region");
return 0;
}
@@ -242,38 +267,53 @@ u64 Mem::Read(const u32 paddr) {
n64::Registers &regs = n64::Core::GetRegs();
const SI &si = mmio.si;
if(ircolib::IsInsideRange(paddr, RDRAM_REGION_START, RDRAM_REGION_END)) return mmio.rdp.ReadRDRAM<u64>(paddr);
if (ircolib::IsInsideRange(paddr, RDRAM_REGION_START, RDRAM_REGION_END))
return mmio.rdp.ReadRDRAM<u64>(paddr);
if (ircolib::IsInsideRange(paddr, DMEM_REGION_START, RSP_MEM_REGION_END)) {
const auto &src = paddr & 0x1000 ? mmio.rsp.imem : mmio.rsp.dmem;
return ircolib::ReadAccess<u64>(src, paddr & 0xfff);
}
if(ircolib::IsInsideRange(paddr, CART_REGION_START_2_1, CART_REGION_END_1_2)) return mmio.pi.BusRead<u64, false>(paddr);
if (ircolib::IsInsideRange(paddr, CART_REGION_START_2_1, CART_REGION_END_1_2))
return mmio.pi.BusRead<u64, false>(paddr);
if (ircolib::IsInsideRange(paddr, MMIO_REGION_START_1, MMIO_REGION_END_1) ||
ircolib::IsInsideRange(paddr, MMIO_REGION_START_2, MMIO_REGION_END_2)) return mmio.Read(paddr);
ircolib::IsInsideRange(paddr, MMIO_REGION_START_2, MMIO_REGION_END_2))
return mmio.Read(paddr);
if(ircolib::IsInsideRange(paddr, PIF_ROM_REGION_START, PIF_ROM_REGION_END)) return ircolib::ReadAccess<u64>(si.pif.bootrom, paddr - PIF_ROM_REGION_START);
if(ircolib::IsInsideRange(paddr, PIF_RAM_REGION_START, PIF_RAM_REGION_END)) return std::byteswap(ircolib::ReadAccess<u64>(si.pif.ram, paddr - PIF_RAM_REGION_START));
if (ircolib::IsInsideRange(paddr, PIF_ROM_REGION_START, PIF_ROM_REGION_END))
return ircolib::ReadAccess<u64>(si.pif.bootrom, paddr - PIF_ROM_REGION_START);
if (ircolib::IsInsideRange(paddr, PIF_RAM_REGION_START, PIF_RAM_REGION_END))
return std::byteswap(ircolib::ReadAccess<u64>(si.pif.ram, paddr - PIF_RAM_REGION_START));
if (ircolib::IsInsideRange(paddr, UNUSED_START_1, UNUSED_END_1) || // unused
ircolib::IsInsideRange(paddr, UNUSED_START_2, UNUSED_END_2) ||
ircolib::IsInsideRange(paddr, UNUSED_START_3, UNUSED_END_3) ||
ircolib::IsInsideRange(paddr, UNUSED_START_4, UNUSED_END_4)) return 0;
ircolib::IsInsideRange(paddr, UNUSED_START_4, UNUSED_END_4))
return 0;
Util::Error::GetInstance().Throw(
{Util::Error::Severity::NON_FATAL}, {Util::Error::Type::MEM_UNHANDLED_ACCESS}, regs.pc,
Util::Error::MemoryAccess{false, Util::Error::MemoryAccess::DWORD, paddr, 0}, "64-bit read access in unhandled region");
Util::Error::GetInstance().Throw({Util::Error::Severity::NON_FATAL}, {Util::Error::Type::MEM_UNHANDLED_ACCESS},
regs.pc,
Util::Error::MemoryAccess{false, Util::Error::MemoryAccess::DWORD, paddr, 0},
"64-bit read access in unhandled region");
return 0;
}
template <>
void Mem::WriteInterpreter<u8>(u32 paddr, u32 val) {
void Mem::Write<u8>(u32 paddr, u32 val) {
n64::Registers &regs = n64::Core::GetRegs();
SI &si = mmio.si;
if(ircolib::IsInsideRange(paddr, RDRAM_REGION_START, RDRAM_REGION_END)) { mmio.rdp.WriteRDRAM<u8>(paddr, val); return; }
#ifdef KAIZEN_JIT_ENABLED
jit.InvalidateBlock(paddr);
#endif
if (ircolib::IsInsideRange(paddr, RDRAM_REGION_START, RDRAM_REGION_END)) {
mmio.rdp.WriteRDRAM<u8>(paddr, val);
return;
}
if (ircolib::IsInsideRange(paddr, DMEM_REGION_START, RSP_MEM_REGION_END)) {
val = val << (8 * (3 - (paddr & 3)));
auto &dest = paddr & 0x1000 ? mmio.rsp.imem : mmio.rsp.dmem;
bool is_imem = paddr & 0x1000;
auto &dest = is_imem ? mmio.rsp.imem : mmio.rsp.dmem;
paddr = (paddr & 0xFFF) & ~3;
ircolib::WriteAccess<u32>(dest, paddr, val);
return;
@@ -286,7 +326,8 @@ void Mem::WriteInterpreter<u8>(u32 paddr, u32 val) {
}
if (ircolib::IsInsideRange(paddr, MMIO_REGION_START_1, MMIO_REGION_END_1) ||
ircolib::IsInsideRange(paddr, MMIO_REGION_START_2, MMIO_REGION_END_2)) panic("MMIO Write<u8>!");
ircolib::IsInsideRange(paddr, MMIO_REGION_START_2, MMIO_REGION_END_2))
panic("MMIO Write<u8>!");
if (ircolib::IsInsideRange(paddr, PIF_RAM_REGION_START, PIF_RAM_REGION_END)) {
val = val << (8 * (3 - (paddr & 3)));
@@ -300,34 +341,29 @@ void Mem::WriteInterpreter<u8>(u32 paddr, u32 val) {
ircolib::IsInsideRange(paddr, UNUSED_START_2, UNUSED_END_2) ||
ircolib::IsInsideRange(paddr, UNUSED_START_3, UNUSED_END_3) ||
ircolib::IsInsideRange(paddr, PIF_ROM_REGION_START, PIF_ROM_REGION_END) ||
ircolib::IsInsideRange(paddr, UNUSED_START_4, UNUSED_END_4)) return;
ircolib::IsInsideRange(paddr, UNUSED_START_4, UNUSED_END_4))
return;
panic("Unimplemented 8-bit write at address {:08X} with value {:02X} (PC = {:016X})", paddr, val, (u64)regs.pc);
}
#ifndef __aarch64__
template <>
void Mem::WriteJIT<u8>(const u32 paddr, const u32 val) {
WriteInterpreter<u8>(paddr, val);
if (jit)
jit->InvalidateBlock(paddr);
}
#endif
template <>
void Mem::Write<u8>(const u32 paddr, const u32 val) {
WriteInterpreter<u8>(paddr, val);
}
template <>
void Mem::WriteInterpreter<u16>(u32 paddr, u32 val) {
void Mem::Write<u16>(u32 paddr, u32 val) {
n64::Registers &regs = n64::Core::GetRegs();
SI &si = mmio.si;
if(ircolib::IsInsideRange(paddr, RDRAM_REGION_START, RDRAM_REGION_END)) { mmio.rdp.WriteRDRAM<u16>(paddr, val); return; }
#ifdef KAIZEN_JIT_ENABLED
jit.InvalidateBlock(paddr);
#endif
if (ircolib::IsInsideRange(paddr, RDRAM_REGION_START, RDRAM_REGION_END)) {
mmio.rdp.WriteRDRAM<u16>(paddr, val);
return;
}
if (ircolib::IsInsideRange(paddr, DMEM_REGION_START, RSP_MEM_REGION_END)) {
val = val << (16 * !(paddr & 2));
auto &dest = paddr & 0x1000 ? mmio.rsp.imem : mmio.rsp.dmem;
bool is_imem = paddr & 0x1000;
auto &dest = is_imem ? mmio.rsp.imem : mmio.rsp.dmem;
paddr = (paddr & 0xFFF) & ~3;
ircolib::WriteAccess<u32>(dest, paddr, val);
return;
@@ -340,7 +376,8 @@ void Mem::WriteInterpreter<u16>(u32 paddr, u32 val) {
}
if (ircolib::IsInsideRange(paddr, MMIO_REGION_START_1, MMIO_REGION_END_1) ||
ircolib::IsInsideRange(paddr, MMIO_REGION_START_2, MMIO_REGION_END_2)) panic("MMIO Write<u16>!");
ircolib::IsInsideRange(paddr, MMIO_REGION_START_2, MMIO_REGION_END_2))
panic("MMIO Write<u16>!");
if (ircolib::IsInsideRange(paddr, PIF_RAM_REGION_START, PIF_RAM_REGION_END)) {
val = val << (16 * !(paddr & 2));
@@ -354,33 +391,28 @@ void Mem::WriteInterpreter<u16>(u32 paddr, u32 val) {
ircolib::IsInsideRange(paddr, UNUSED_START_2, UNUSED_END_2) ||
ircolib::IsInsideRange(paddr, UNUSED_START_3, UNUSED_END_3) ||
ircolib::IsInsideRange(paddr, PIF_ROM_REGION_START, PIF_ROM_REGION_END) ||
ircolib::IsInsideRange(paddr, UNUSED_START_4, UNUSED_END_4)) return;
ircolib::IsInsideRange(paddr, UNUSED_START_4, UNUSED_END_4))
return;
panic("Unimplemented 16-bit write at address {:08X} with value {:04X} (PC = {:016X})", paddr, val, (u64)regs.pc);
}
#ifndef __aarch64__
template <>
void Mem::WriteJIT<u16>(const u32 paddr, const u32 val) {
WriteInterpreter<u16>(paddr, val);
if (jit)
jit->InvalidateBlock(paddr);
}
#endif
template <>
void Mem::Write<u16>(const u32 paddr, const u32 val) {
WriteInterpreter<u16>(paddr, val);
}
template <>
void Mem::WriteInterpreter<u32>(const u32 paddr, const u32 val) {
void Mem::Write<u32>(const u32 paddr, const u32 val) {
n64::Registers &regs = n64::Core::GetRegs();
SI &si = mmio.si;
if(ircolib::IsInsideRange(paddr, RDRAM_REGION_START, RDRAM_REGION_END)) { mmio.rdp.WriteRDRAM<u32>(paddr, val); return; }
#ifdef KAIZEN_JIT_ENABLED
jit.InvalidateBlock(paddr);
#endif
if (ircolib::IsInsideRange(paddr, RDRAM_REGION_START, RDRAM_REGION_END)) {
mmio.rdp.WriteRDRAM<u32>(paddr, val);
return;
}
if (ircolib::IsInsideRange(paddr, DMEM_REGION_START, RSP_MEM_REGION_END)) {
auto &dest = paddr & 0x1000 ? mmio.rsp.imem : mmio.rsp.dmem;
bool is_imem = paddr & 0x1000;
auto &dest = is_imem ? mmio.rsp.imem : mmio.rsp.dmem;
ircolib::WriteAccess<u32>(dest, paddr & 0xfff, val);
return;
}
@@ -392,7 +424,10 @@ void Mem::WriteInterpreter<u32>(const u32 paddr, const u32 val) {
}
if (ircolib::IsInsideRange(paddr, MMIO_REGION_START_1, MMIO_REGION_END_1) ||
ircolib::IsInsideRange(paddr, MMIO_REGION_START_2, MMIO_REGION_END_2)) { mmio.Write(paddr, val); return; }
ircolib::IsInsideRange(paddr, MMIO_REGION_START_2, MMIO_REGION_END_2)) {
mmio.Write(paddr, val);
return;
}
if (ircolib::IsInsideRange(paddr, PIF_RAM_REGION_START, PIF_RAM_REGION_END)) {
ircolib::WriteAccess<u32>(si.pif.ram, paddr - PIF_RAM_REGION_START, std::byteswap(val));
@@ -404,42 +439,27 @@ void Mem::WriteInterpreter<u32>(const u32 paddr, const u32 val) {
ircolib::IsInsideRange(paddr, UNUSED_START_2, UNUSED_END_2) ||
ircolib::IsInsideRange(paddr, UNUSED_START_3, UNUSED_END_3) ||
ircolib::IsInsideRange(paddr, PIF_ROM_REGION_START, PIF_ROM_REGION_END) ||
ircolib::IsInsideRange(paddr, UNUSED_START_4, UNUSED_END_4)) return;
ircolib::IsInsideRange(paddr, UNUSED_START_4, UNUSED_END_4))
return;
panic("Unimplemented 32-bit write at address {:08X} with value {:08X} (PC = {:016X})", paddr, val, (u64)regs.pc);
}
#ifndef __aarch64__
template <>
void Mem::WriteJIT<u32>(const u32 paddr, const u32 val) {
WriteInterpreter<u32>(paddr, val);
if (jit)
jit->InvalidateBlock(paddr);
}
#endif
template <>
void Mem::Write<u32>(const u32 paddr, const u32 val) {
WriteInterpreter<u32>(paddr, val);
}
#ifndef __aarch64__
void Mem::WriteJIT(const u32 paddr, const u64 val) {
WriteInterpreter(paddr, val);
if (jit)
jit->InvalidateBlock(paddr);
}
#endif
void Mem::Write(const u32 paddr, const u64 val) { WriteInterpreter(paddr, val); }
void Mem::WriteInterpreter(const u32 paddr, u64 val) {
void Mem::Write(const u32 paddr, u64 val) {
n64::Registers &regs = n64::Core::GetRegs();
SI &si = mmio.si;
if(ircolib::IsInsideRange(paddr, RDRAM_REGION_START, RDRAM_REGION_END)) { mmio.rdp.WriteRDRAM<u64>(paddr, val); return; }
#ifdef KAIZEN_JIT_ENABLED
jit.InvalidateBlock(paddr);
#endif
if (ircolib::IsInsideRange(paddr, RDRAM_REGION_START, RDRAM_REGION_END)) {
mmio.rdp.WriteRDRAM<u64>(paddr, val);
return;
}
if (ircolib::IsInsideRange(paddr, DMEM_REGION_START, RSP_MEM_REGION_END)) {
auto &dest = paddr & 0x1000 ? mmio.rsp.imem : mmio.rsp.dmem;
bool is_imem = paddr & 0x1000;
auto &dest = is_imem ? mmio.rsp.imem : mmio.rsp.dmem;
val >>= 32;
ircolib::WriteAccess<u32>(dest, paddr & 0xfff, val);
return;
@@ -452,7 +472,8 @@ void Mem::WriteInterpreter(const u32 paddr, u64 val) {
}
if (ircolib::IsInsideRange(paddr, MMIO_REGION_START_1, MMIO_REGION_END_1) ||
ircolib::IsInsideRange(paddr, MMIO_REGION_START_2, MMIO_REGION_END_2)) panic("MMIO Write<u64>!");
ircolib::IsInsideRange(paddr, MMIO_REGION_START_2, MMIO_REGION_END_2))
panic("MMIO Write<u64>!");
if (ircolib::IsInsideRange(paddr, PIF_RAM_REGION_START, PIF_RAM_REGION_END)) {
ircolib::WriteAccess<u64>(si.pif.ram, paddr - PIF_RAM_REGION_START, std::byteswap(val));
@@ -464,7 +485,8 @@ void Mem::WriteInterpreter(const u32 paddr, u64 val) {
ircolib::IsInsideRange(paddr, UNUSED_START_2, UNUSED_END_2) ||
ircolib::IsInsideRange(paddr, UNUSED_START_3, UNUSED_END_3) ||
ircolib::IsInsideRange(paddr, PIF_ROM_REGION_START, PIF_ROM_REGION_END) ||
ircolib::IsInsideRange(paddr, UNUSED_START_4, UNUSED_END_4)) return;
ircolib::IsInsideRange(paddr, UNUSED_START_4, UNUSED_END_4))
return;
panic("Unimplemented 64-bit write at address {:08X} with value {:016X} (PC = {:016X})", paddr, val, (u64)regs.pc);
}
+9 -11
View File
@@ -74,15 +74,19 @@ struct Flash {
T Read(u32 index) const;
};
#ifdef KAIZEN_JIT_ENABLED
struct JIT;
struct Mem {
~Mem() = default;
Mem(JIT &jit);
JIT &jit;
#else
struct Mem {
Mem();
~Mem() = default;
void Reset();
void LoadSRAM(SaveType, fs::path);
void LoadROM(bool, const std::string &);
void SetJIT(JIT* jit) { this->jit = jit; }
[[nodiscard]] auto GetRDRAMPtr() -> u8 * { return mmio.rdp.rdram.data(); }
[[nodiscard]] auto GetRDRAM() -> std::vector<u8> & { return mmio.rdp.rdram; }
@@ -124,6 +128,7 @@ struct Mem {
ROM rom;
SaveType saveType = SAVE_NONE;
Flash flash;
private:
friend struct SI;
friend struct PI;
@@ -132,17 +137,9 @@ private:
friend struct JIT;
friend struct Core;
template <typename T>
void WriteInterpreter(u32, u32);
void WriteInterpreter(u32, u64);
template <typename T>
void WriteJIT(u32, u32);
void WriteJIT(u32, u64);
std::array<u8, ISVIEWER_SIZE> isviewer{};
std::ofstream isviewer_sink{};
int mmioSize{}, flashSize{};
JIT *jit = nullptr;
std::string sramPath{};
mio::mmap_sink saveData{};
@@ -151,4 +148,5 @@ private:
return std::ranges::any_of(pal_codes, [this](char a) { return rom.cart[0x3d] == a; });
}
};
#endif
} // namespace n64
-1
View File
@@ -121,7 +121,6 @@ void RDP::Write(const u32 addr, const u32 val) {
}
void RDP::WriteStatus(const u32 val) {
DPCStatusWrite temp{};
temp.raw = val;
+8 -34
View File
@@ -1,5 +1,6 @@
#include <Core.hpp>
#include <log.hpp>
#include <jit/helpers.hpp>
namespace n64 {
RSP::RSP() { Reset(); }
@@ -30,39 +31,6 @@ void RSP::Reset() {
steps = 0;
}
/*
FORCE_INLINE void logRSP(const RSP& rsp, const u32 instr) {
debug("{:04X} {:08X} ", rsp.oldPC, instr);
for (auto gpr : rsp.gpr) {
debug("{:08X} ", gpr);
}
for (auto vpr : rsp.vpr) {
for (int i = 0; i < 8; i++) {
debug("{:04X}", vpr.element[i]);
}
debug(" ");
}
for (int i = 0; i < 8; i++) {
debug("{:04X}", rsp.acc.h.element[i]);
}
debug(" ");
for (int i = 0; i < 8; i++) {
debug("{:04X}", rsp.acc.m.element[i]);
}
debug(" ");
for (int i = 0; i < 8; i++) {
debug("{:04X}", rsp.acc.l.element[i]);
}
debug(" {:04X} {:04X} {:02X}", rsp.GetVCC(), rsp.GetVCO(), rsp.GetVCE());
debug("DMEM: {:02X}{:02X}", rsp.dmem[0x3c4], rsp.dmem[0x3c5]);
}
*/
auto RSP::Read(const u32 addr) -> u32 {
switch (addr) {
case 0x04040000:
@@ -83,7 +51,12 @@ auto RSP::Read(const u32 addr) -> u32 {
case 0x04080000:
return pc & 0xFFC;
default:
panic("Unimplemented SP register read {:08X}", addr);
{
auto &regs = Core::GetRegs();
panic("Unimplemented SP register read {:08X} (cpu pc: 0x{:016X}, rsp pc: 0x{:04X}, ra: 0x{:016X})", addr,
(u64)regs.oldPC, pc & 0xffc, (u64)regs.gpr[31]);
}
}
}
@@ -171,6 +144,7 @@ void RSP::DMA<false>() {
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);
+1 -9
View File
@@ -5,6 +5,7 @@
#include <core/RDP.hpp>
#include <core/mmio/MI.hpp>
#include <Instruction.hpp>
#include <JITUtils.hpp>
#define RSP_BYTE(addr) (dmem[BYTE_ADDRESS(addr) & 0xFFF])
#define GET_RSP_HALF(addr) ((RSP_BYTE(addr) << 8) | RSP_BYTE((addr) + 1))
@@ -382,14 +383,5 @@ private:
nextPC = address & 0xFFC;
}
}
FORCE_INLINE void branch_likely(const u16 address, const bool cond) {
if (cond) {
nextPC = address & 0xFFC;
} else {
pc = nextPC & 0xFFC;
nextPC = pc + 4;
}
}
};
} // namespace n64
@@ -26,7 +26,7 @@ void Cop0::dmfc0(const Instruction instr) const {
void Cop0::eret() {
Registers &regs = Core::GetRegs();
if (!regs.cop0.kernelMode) {
FireException(ExceptionCode::CoprocessorUnusable, 0, regs.oldPC);
FireException(Cop0::ExceptionCode::CoprocessorUnusable, 0, regs.oldPC);
return;
}
if (status.erl) {
@@ -5,7 +5,7 @@
namespace n64 {
template <>
auto Cop1::FGR_T<s32>(const Cop0Status &status, const u32 index) -> s32 & {
auto Cop1::FGR_T<s32>(const Cop0::Status &status, const u32 index) -> s32 & {
if (status.fr) {
return fgr[index].int32;
}
@@ -18,17 +18,17 @@ auto Cop1::FGR_T<s32>(const Cop0Status &status, const u32 index) -> s32 & {
}
template <>
auto Cop1::FGR_T<u32>(const Cop0Status &status, const u32 index) -> u32 & {
auto Cop1::FGR_T<u32>(const Cop0::Status &status, const u32 index) -> u32 & {
return reinterpret_cast<u32 &>(FGR_T<s32>(status, index));
}
template <>
auto Cop1::FGR_T<float>(const Cop0Status &, const u32 index) -> float & {
auto Cop1::FGR_T<float>(const Cop0::Status &, const u32 index) -> float & {
return fgr[index].float32;
}
template <>
auto Cop1::FGR_T<s64>(const Cop0Status &status, const u32 index) -> s64 & {
auto Cop1::FGR_T<s64>(const Cop0::Status &status, const u32 index) -> s64 & {
if (status.fr) {
return fgr[index].int64;
}
@@ -37,17 +37,17 @@ auto Cop1::FGR_T<s64>(const Cop0Status &status, const u32 index) -> s64 & {
}
template <>
auto Cop1::FGR_T<u64>(const Cop0Status &status, const u32 index) -> u64 & {
auto Cop1::FGR_T<u64>(const Cop0::Status &status, const u32 index) -> u64 & {
return reinterpret_cast<u64 &>(FGR_T<s64>(status, index));
}
template <>
auto Cop1::FGR_T<double>(const Cop0Status &, const u32 index) -> double & {
auto Cop1::FGR_T<double>(const Cop0::Status &, const u32 index) -> double & {
return fgr[index].float64;
}
template <>
auto Cop1::FGR_S<s32>(const Cop0Status &status, const u32 index) -> s32 & {
auto Cop1::FGR_S<s32>(const Cop0::Status &status, const u32 index) -> s32 & {
if (status.fr) {
return fgr[index].int32;
}
@@ -56,12 +56,12 @@ auto Cop1::FGR_S<s32>(const Cop0Status &status, const u32 index) -> s32 & {
}
template <>
auto Cop1::FGR_S<u32>(const Cop0Status &status, const u32 index) -> u32 & {
auto Cop1::FGR_S<u32>(const Cop0::Status &status, const u32 index) -> u32 & {
return reinterpret_cast<u32 &>(FGR_S<s32>(status, index));
}
template <>
auto Cop1::FGR_S<float>(const Cop0Status &status, const u32 index) -> float & {
auto Cop1::FGR_S<float>(const Cop0::Status &status, const u32 index) -> float & {
if (status.fr) {
return fgr[index].float32;
}
@@ -70,17 +70,17 @@ auto Cop1::FGR_S<float>(const Cop0Status &status, const u32 index) -> float & {
}
template <>
auto Cop1::FGR_S<s64>(const Cop0Status &status, const u32 index) -> s64 & {
auto Cop1::FGR_S<s64>(const Cop0::Status &status, const u32 index) -> s64 & {
return FGR_T<s64>(status, index);
}
template <>
auto Cop1::FGR_S<u64>(const Cop0Status &status, const u32 index) -> u64 & {
auto Cop1::FGR_S<u64>(const Cop0::Status &status, const u32 index) -> u64 & {
return reinterpret_cast<u64 &>(FGR_S<s64>(status, index));
}
template <>
auto Cop1::FGR_S<double>(const Cop0Status &status, const u32 index) -> double & {
auto Cop1::FGR_S<double>(const Cop0::Status &status, const u32 index) -> double & {
if (status.fr) {
return fgr[index].float64;
}
@@ -89,34 +89,34 @@ auto Cop1::FGR_S<double>(const Cop0Status &status, const u32 index) -> double &
}
template <>
auto Cop1::FGR_D<s32>(const Cop0Status &, const u32 index) -> s32 & {
auto Cop1::FGR_D<s32>(const Cop0::Status &, const u32 index) -> s32 & {
fgr[index].int32h = 0;
return fgr[index].int32;
}
template <>
auto Cop1::FGR_D<u32>(const Cop0Status &status, const u32 index) -> u32 & {
auto Cop1::FGR_D<u32>(const Cop0::Status &status, const u32 index) -> u32 & {
return reinterpret_cast<u32 &>(FGR_D<s32>(status, index));
}
template <>
auto Cop1::FGR_D<float>(const Cop0Status &, const u32 index) -> float & {
auto Cop1::FGR_D<float>(const Cop0::Status &, const u32 index) -> float & {
fgr[index].float32h = 0;
return fgr[index].float32;
}
template <>
auto Cop1::FGR_D<s64>(const Cop0Status &, const u32 index) -> s64 & {
auto Cop1::FGR_D<s64>(const Cop0::Status &, const u32 index) -> s64 & {
return fgr[index].int64;
}
template <>
auto Cop1::FGR_D<u64>(const Cop0Status &status, const u32 index) -> u64 & {
auto Cop1::FGR_D<u64>(const Cop0::Status &status, const u32 index) -> u64 & {
return reinterpret_cast<u64 &>(FGR_D<s64>(status, index));
}
template <>
auto Cop1::FGR_D<double>(const Cop0Status &status, const u32 index) -> double & {
auto Cop1::FGR_D<double>(const Cop0::Status &status, const u32 index) -> double & {
return FGR_T<double>(status, index);
}
@@ -138,13 +138,13 @@ bool Cop1::CheckCVTArg<s32>(const float f) {
case FP_INFINITE:
case FP_NAN:
SetCauseUnimplemented();
regs.cop0.FireException(ExceptionCode::FloatingPointError, 0, regs.oldPC);
regs.cop0.FireException(Cop0::ExceptionCode::FloatingPointError, 0, regs.oldPC);
return false;
}
if (f >= 0x1p+31f || f < -0x1p+31f) {
SetCauseUnimplemented();
regs.cop0.FireException(ExceptionCode::FloatingPointError, 0, regs.oldPC);
regs.cop0.FireException(Cop0::ExceptionCode::FloatingPointError, 0, regs.oldPC);
return false;
}
@@ -159,13 +159,13 @@ bool Cop1::CheckCVTArg<s32>(const double f) {
case FP_INFINITE:
case FP_NAN:
SetCauseUnimplemented();
regs.cop0.FireException(ExceptionCode::FloatingPointError, 0, regs.oldPC);
regs.cop0.FireException(Cop0::ExceptionCode::FloatingPointError, 0, regs.oldPC);
return false;
}
if ((f >= 0x1p+31 || f < -0x1p+31)) {
SetCauseUnimplemented();
regs.cop0.FireException(ExceptionCode::FloatingPointError, 0, regs.oldPC);
regs.cop0.FireException(Cop0::ExceptionCode::FloatingPointError, 0, regs.oldPC);
return false;
}
@@ -180,13 +180,13 @@ bool Cop1::CheckCVTArg<s64>(const float f) {
case FP_INFINITE:
case FP_NAN:
SetCauseUnimplemented();
regs.cop0.FireException(ExceptionCode::FloatingPointError, 0, regs.oldPC);
regs.cop0.FireException(Cop0::ExceptionCode::FloatingPointError, 0, regs.oldPC);
return false;
}
if ((f >= 0x1p+53f || f <= -0x1p+53f)) {
SetCauseUnimplemented();
regs.cop0.FireException(ExceptionCode::FloatingPointError, 0, regs.oldPC);
regs.cop0.FireException(Cop0::ExceptionCode::FloatingPointError, 0, regs.oldPC);
return false;
}
@@ -201,13 +201,13 @@ bool Cop1::CheckCVTArg<s64>(const double f) {
case FP_INFINITE:
case FP_NAN:
SetCauseUnimplemented();
regs.cop0.FireException(ExceptionCode::FloatingPointError, 0, regs.oldPC);
regs.cop0.FireException(Cop0::ExceptionCode::FloatingPointError, 0, regs.oldPC);
return false;
}
if ((f >= 0x1p+53 || f <= -0x1p+53)) {
SetCauseUnimplemented();
regs.cop0.FireException(ExceptionCode::FloatingPointError, 0, regs.oldPC);
regs.cop0.FireException(Cop0::ExceptionCode::FloatingPointError, 0, regs.oldPC);
return false;
}
@@ -220,11 +220,11 @@ bool Cop1::CheckArg(const T f) {
switch (std::fpclassify(f)) {
case FP_SUBNORMAL:
SetCauseUnimplemented();
regs.cop0.FireException(ExceptionCode::FloatingPointError, 0, regs.oldPC);
regs.cop0.FireException(Cop0::ExceptionCode::FloatingPointError, 0, regs.oldPC);
return false;
case FP_NAN:
if (isqnan(f) ? SetCauseInvalid() : (SetCauseUnimplemented(), true)) {
regs.cop0.FireException(ExceptionCode::FloatingPointError, 0, regs.oldPC);
regs.cop0.FireException(Cop0::ExceptionCode::FloatingPointError, 0, regs.oldPC);
return false;
}
return true;
@@ -238,19 +238,19 @@ bool Cop1::CheckArgs(const T f1, const T f2) {
auto class1 = std::fpclassify(f1), class2 = std::fpclassify(f2);
if ((class1 == FP_NAN && !isqnan(f1)) || (class2 == FP_NAN && !isqnan(f2))) {
SetCauseUnimplemented();
regs.cop0.FireException(ExceptionCode::FloatingPointError, 0, regs.oldPC);
regs.cop0.FireException(Cop0::ExceptionCode::FloatingPointError, 0, regs.oldPC);
return false;
}
if (class1 == FP_SUBNORMAL || class2 == FP_SUBNORMAL) {
SetCauseUnimplemented();
regs.cop0.FireException(ExceptionCode::FloatingPointError, 0, regs.oldPC);
regs.cop0.FireException(Cop0::ExceptionCode::FloatingPointError, 0, regs.oldPC);
return false;
}
if ((class1 == FP_NAN && isqnan(f1)) || (class2 == FP_NAN && isqnan(f2))) {
if (SetCauseInvalid()) {
regs.cop0.FireException(ExceptionCode::FloatingPointError, 0, regs.oldPC);
regs.cop0.FireException(Cop0::ExceptionCode::FloatingPointError, 0, regs.oldPC);
return false;
}
}
@@ -262,7 +262,7 @@ template<>
bool Cop1::CheckFPUUsable<true>() {
Registers &regs = Core::GetRegs();
if (!regs.cop0.status.cu1) {
regs.cop0.FireException(ExceptionCode::CoprocessorUnusable, 1, regs.oldPC);
regs.cop0.FireException(Cop0::ExceptionCode::CoprocessorUnusable, 1, regs.oldPC);
return false;
}
@@ -271,7 +271,8 @@ bool Cop1::CheckFPUUsable<true>() {
template <>
bool Cop1::CheckFPUUsable<false>() {
if (!CheckFPUUsable<true>()) return false;
if (!CheckFPUUsable<true>())
return false;
fcr31.cause = {};
return true;
}
@@ -298,7 +299,7 @@ bool Cop1::CheckResult<float>(float &f) {
case FP_SUBNORMAL:
if (!fcr31.fs || fcr31.enable.underflow || fcr31.enable.inexact_operation) {
SetCauseUnimplemented();
regs.cop0.FireException(ExceptionCode::FloatingPointError, 0, regs.oldPC);
regs.cop0.FireException(Cop0::ExceptionCode::FloatingPointError, 0, regs.oldPC);
return false;
}
SetCauseUnderflow();
@@ -321,7 +322,7 @@ bool Cop1::CheckResult<double>(double &f) {
case FP_SUBNORMAL:
if (!fcr31.fs || fcr31.enable.underflow || fcr31.enable.inexact_operation) {
SetCauseUnimplemented();
regs.cop0.FireException(ExceptionCode::FloatingPointError, 0, regs.oldPC);
regs.cop0.FireException(Cop0::ExceptionCode::FloatingPointError, 0, regs.oldPC);
return false;
}
SetCauseUnderflow();
@@ -348,7 +349,7 @@ bool Cop1::TestExceptions() {
if constexpr (cvt) {
if (exc & FE_INVALID) {
SetCauseUnimplemented();
regs.cop0.FireException(ExceptionCode::FloatingPointError, 0, regs.oldPC);
regs.cop0.FireException(Cop0::ExceptionCode::FloatingPointError, 0, regs.oldPC);
return true;
}
}
@@ -356,7 +357,7 @@ bool Cop1::TestExceptions() {
if (exc & FE_UNDERFLOW) {
if (!fcr31.fs || fcr31.enable.underflow || fcr31.enable.inexact_operation) {
SetCauseUnimplemented();
regs.cop0.FireException(ExceptionCode::FloatingPointError, 0, regs.oldPC);
regs.cop0.FireException(Cop0::ExceptionCode::FloatingPointError, 0, regs.oldPC);
return true;
}
}
@@ -374,7 +375,7 @@ bool Cop1::TestExceptions() {
if (exc & FE_INVALID)
raise |= SetCauseInvalid();
if (raise)
regs.cop0.FireException(ExceptionCode::FloatingPointError, 0, regs.oldPC);
regs.cop0.FireException(Cop0::ExceptionCode::FloatingPointError, 0, regs.oldPC);
return raise;
}
@@ -589,17 +590,17 @@ void Cop1::ctc1(const Instruction instr) {
}
}
if (fcr31.cause.inexact_operation && fcr31.enable.inexact_operation)
regs.cop0.FireException(ExceptionCode::FloatingPointError, 0, regs.oldPC);
regs.cop0.FireException(Cop0::ExceptionCode::FloatingPointError, 0, regs.oldPC);
if (fcr31.cause.underflow && fcr31.enable.underflow)
regs.cop0.FireException(ExceptionCode::FloatingPointError, 0, regs.oldPC);
regs.cop0.FireException(Cop0::ExceptionCode::FloatingPointError, 0, regs.oldPC);
if (fcr31.cause.overflow && fcr31.enable.overflow)
regs.cop0.FireException(ExceptionCode::FloatingPointError, 0, regs.oldPC);
regs.cop0.FireException(Cop0::ExceptionCode::FloatingPointError, 0, regs.oldPC);
if (fcr31.cause.division_by_zero && fcr31.enable.division_by_zero)
regs.cop0.FireException(ExceptionCode::FloatingPointError, 0, regs.oldPC);
regs.cop0.FireException(Cop0::ExceptionCode::FloatingPointError, 0, regs.oldPC);
if (fcr31.cause.invalid_operation && fcr31.enable.invalid_operation)
regs.cop0.FireException(ExceptionCode::FloatingPointError, 0, regs.oldPC);
regs.cop0.FireException(Cop0::ExceptionCode::FloatingPointError, 0, regs.oldPC);
if (fcr31.cause.unimplemented_operation)
regs.cop0.FireException(ExceptionCode::FloatingPointError, 0, regs.oldPC);
regs.cop0.FireException(Cop0::ExceptionCode::FloatingPointError, 0, regs.oldPC);
}
break;
default:
@@ -639,7 +640,8 @@ void Cop1::cvtsw(const Instruction instr) {
return;
const auto fs = FGR_S<s32>(regs.cop0.status, instr.fs());
CHECK_FPE(float, fd, fs)
if (!CheckResult(fd)) return;
if (!CheckResult(fd))
return;
FGR_D<float>(regs.cop0.status, instr.fd()) = fd;
}
@@ -650,7 +652,7 @@ void Cop1::cvtsl(const Instruction instr) {
const auto fs = FGR_S<s64>(regs.cop0.status, instr.fs());
if (fs >= 0x0080000000000000 || fs < static_cast<s64>(0xff80000000000000)) {
SetCauseUnimplemented();
regs.cop0.FireException(ExceptionCode::FloatingPointError, 0, regs.oldPC);
regs.cop0.FireException(Cop0::ExceptionCode::FloatingPointError, 0, regs.oldPC);
return;
}
CHECK_FPE(float, fd, fs)
@@ -712,7 +714,7 @@ void Cop1::cvtdl(const Instruction instr) {
if (fs >= 0x0080000000000000 || fs < static_cast<s64>(0xff80000000000000)) {
SetCauseUnimplemented();
regs.cop0.FireException(ExceptionCode::FloatingPointError, 0, regs.oldPC);
regs.cop0.FireException(Cop0::ExceptionCode::FloatingPointError, 0, regs.oldPC);
return;
}
CHECK_FPE(double, fd, fs)
@@ -737,11 +739,11 @@ bool Cop1::XORDERED(const T fs, const T ft) {
Registers &regs = Core::GetRegs();
if (std::isnan(fs) || std::isnan(ft)) {
if (std::isnan(fs) && (!quiet || isqnan(fs)) && SetCauseInvalid()) {
regs.cop0.FireException(ExceptionCode::FloatingPointError, 0, regs.oldPC);
regs.cop0.FireException(Cop0::ExceptionCode::FloatingPointError, 0, regs.oldPC);
return false;
}
if (std::isnan(ft) && (!quiet || isqnan(ft)) && SetCauseInvalid()) {
regs.cop0.FireException(ExceptionCode::FloatingPointError, 0, regs.oldPC);
regs.cop0.FireException(Cop0::ExceptionCode::FloatingPointError, 0, regs.oldPC);
return false;
}
fcr31.compare = cf;
@@ -1130,7 +1132,7 @@ void Cop1::roundls(const Instruction instr) {
return;
CHECK_FPE_CONST(s64, fd, ircolib::roundNearest(fs))
if (fd != fs && SetCauseInexact()) {
regs.cop0.FireException(ExceptionCode::FloatingPointError, 0, regs.oldPC);
regs.cop0.FireException(Cop0::ExceptionCode::FloatingPointError, 0, regs.oldPC);
return;
}
FGR_D<s64>(regs.cop0.status, instr.fd()) = fd;
@@ -1145,7 +1147,7 @@ void Cop1::roundld(const Instruction instr) {
return;
CHECK_FPE_CONST(s64, fd, ircolib::roundNearest(fs))
if (fd != fs && SetCauseInexact()) {
regs.cop0.FireException(ExceptionCode::FloatingPointError, 0, regs.oldPC);
regs.cop0.FireException(Cop0::ExceptionCode::FloatingPointError, 0, regs.oldPC);
return;
}
FGR_D<s64>(regs.cop0.status, instr.fd()) = fd;
@@ -1160,7 +1162,7 @@ void Cop1::roundws(const Instruction instr) {
return;
CHECK_FPE_CONV_CONST(s32, fd, ircolib::roundNearest(fs))
if (fd != fs && SetCauseInexact()) {
regs.cop0.FireException(ExceptionCode::FloatingPointError, 0, regs.oldPC);
regs.cop0.FireException(Cop0::ExceptionCode::FloatingPointError, 0, regs.oldPC);
return;
}
FGR_D<s32>(regs.cop0.status, instr.fd()) = fd;
@@ -1175,7 +1177,7 @@ void Cop1::roundwd(const Instruction instr) {
return;
CHECK_FPE_CONV_CONST(s32, fd, ircolib::roundNearest(fs))
if (fd != fs && SetCauseInexact()) {
regs.cop0.FireException(ExceptionCode::FloatingPointError, 0, regs.oldPC);
regs.cop0.FireException(Cop0::ExceptionCode::FloatingPointError, 0, regs.oldPC);
return;
}
FGR_D<s32>(regs.cop0.status, instr.fd()) = fd;
@@ -1234,7 +1236,7 @@ void Cop1::truncws(const Instruction instr) {
return;
CHECK_FPE_CONV_CONST(s32, fd, ircolib::roundTrunc(fs))
if (static_cast<float>(fd) != fs && SetCauseInexact()) {
regs.cop0.FireException(ExceptionCode::FloatingPointError, 0, regs.oldPC);
regs.cop0.FireException(Cop0::ExceptionCode::FloatingPointError, 0, regs.oldPC);
return;
}
FGR_D<s32>(regs.cop0.status, instr.fd()) = fd;
@@ -1249,7 +1251,7 @@ void Cop1::truncwd(const Instruction instr) {
return;
CHECK_FPE_CONV_CONST(s32, fd, ircolib::roundTrunc(fs))
if (static_cast<double>(fd) != fs && SetCauseInexact()) {
regs.cop0.FireException(ExceptionCode::FloatingPointError, 0, regs.oldPC);
regs.cop0.FireException(Cop0::ExceptionCode::FloatingPointError, 0, regs.oldPC);
return;
}
FGR_D<s32>(regs.cop0.status, instr.fd()) = fd;
@@ -1264,7 +1266,7 @@ void Cop1::truncls(const Instruction instr) {
return;
CHECK_FPE_CONST(s64, fd, ircolib::roundTrunc(fs))
if (static_cast<float>(fd) != fs && SetCauseInexact()) {
regs.cop0.FireException(ExceptionCode::FloatingPointError, 0, regs.oldPC);
regs.cop0.FireException(Cop0::ExceptionCode::FloatingPointError, 0, regs.oldPC);
return;
}
FGR_D<s64>(regs.cop0.status, instr.fd()) = fd;
@@ -1279,7 +1281,7 @@ void Cop1::truncld(const Instruction instr) {
return;
CHECK_FPE_CONST(s64, fd, ircolib::roundTrunc(fs))
if (static_cast<double>(fd) != fs && SetCauseInexact()) {
regs.cop0.FireException(ExceptionCode::FloatingPointError, 0, regs.oldPC);
regs.cop0.FireException(Cop0::ExceptionCode::FloatingPointError, 0, regs.oldPC);
return;
}
FGR_D<s64>(regs.cop0.status, instr.fd()) = fd;
@@ -1308,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.cachedState.EvictCachedBlock(addr);
mem.Write<u32>(physical, FGR_T<u32>(regs.cop0.status, instr.ft()));
}
}
@@ -1335,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.cachedState.EvictCachedBlock(addr);
mem.Write(physical, FGR_T<u64>(regs.cop0.status, instr.ft()));
}
}
@@ -1344,7 +1348,7 @@ void Cop1::unimplemented() {
if (!CheckFPUUsable())
return;
SetCauseUnimplemented();
regs.cop0.FireException(ExceptionCode::FloatingPointError, 0, regs.oldPC);
regs.cop0.FireException(Cop0::ExceptionCode::FloatingPointError, 0, regs.oldPC);
}
void Cop1::mfc1(const Instruction instr) {
+12 -10
View File
@@ -33,10 +33,10 @@ void Interpreter::special(const Instruction instr) {
jalr(instr);
break;
case Instruction::SYSCALL:
regs.cop0.FireException(ExceptionCode::Syscall, 0, regs.oldPC);
regs.cop0.FireException(Cop0::ExceptionCode::Syscall, 0, regs.oldPC);
break;
case Instruction::BREAK:
regs.cop0.FireException(ExceptionCode::Breakpoint, 0, regs.oldPC);
regs.cop0.FireException(Cop0::ExceptionCode::Breakpoint, 0, regs.oldPC);
break;
case Instruction::SYNC:
break; // SYNC
@@ -164,8 +164,8 @@ void Interpreter::special(const Instruction instr) {
dsra32(instr);
break;
default:
panic("Unimplemented special {} {} ({:08X}) (pc: {:016X})", instr.instr.opcode.special_hi, instr.instr.opcode.special_lo, instr.instr.raw,
static_cast<u64>(regs.oldPC));
panic("Unimplemented special {} {} ({:08X}) (pc: {:016X})", instr.instr.opcode.special_hi,
instr.instr.opcode.special_lo, instr.instr.raw, static_cast<u64>(regs.oldPC));
}
}
@@ -222,7 +222,7 @@ void Interpreter::regimm(const Instruction instr) {
void Interpreter::cop2Decode(const Instruction instr) {
if (!regs.cop0.status.cu2) {
regs.cop0.FireException(ExceptionCode::CoprocessorUnusable, 2, regs.oldPC);
regs.cop0.FireException(Cop0::ExceptionCode::CoprocessorUnusable, 2, regs.oldPC);
return;
}
switch (instr.rs()) {
@@ -245,11 +245,11 @@ void Interpreter::cop2Decode(const Instruction instr) {
ctc2(instr);
break;
default:
regs.cop0.FireException(ExceptionCode::ReservedInstruction, 2, regs.oldPC);
regs.cop0.FireException(Cop0::ExceptionCode::ReservedInstruction, 2, regs.oldPC);
}
}
void Interpreter::Exec(const Instruction instr) {
void Interpreter::DecodeExecute(const Instruction instr) {
// 00rr_rccc
switch (instr.opcode()) {
case Instruction::SPECIAL:
@@ -361,7 +361,7 @@ void Interpreter::Exec(const Instruction instr) {
ldr(instr);
break;
case 0x1F:
regs.cop0.FireException(ExceptionCode::ReservedInstruction, 0, regs.oldPC);
regs.cop0.FireException(Cop0::ExceptionCode::ReservedInstruction, 0, regs.oldPC);
break;
case Instruction::LB:
lb(instr);
@@ -409,7 +409,8 @@ void Interpreter::Exec(const Instruction instr) {
swr(instr);
break;
case Instruction::CACHE:
break; // CACHE
// cache(instr);
break;
case Instruction::LL:
ll(instr);
break;
@@ -449,7 +450,8 @@ void Interpreter::Exec(const Instruction instr) {
sd(instr);
break;
default:
panic("Unimplemented instruction {:02X} ({:08X}) (pc: {:016X})", instr.instr.opcode.op, u32(instr), static_cast<u64>(regs.oldPC));
panic("Unimplemented instruction {:02X} ({:08X}) (pc: {:016X})", instr.instr.opcode.op, u32(instr),
static_cast<u64>(regs.oldPC));
}
}
} // namespace n64
+136 -37
View File
@@ -1,4 +1,5 @@
#include <Core.hpp>
#include <Scheduler.hpp>
#define check_signed_overflow(op1, op2, res) (((~((op1) ^ (op2)) & ((op1) ^ (res))) >> ((sizeof(res) * 8) - 1)) & 1)
#define check_signed_underflow(op1, op2, res) (((((op1) ^ (op2)) & ((op1) ^ (res))) >> ((sizeof(res) * 8) - 1)) & 1)
@@ -8,7 +9,7 @@ void Interpreter::add(const Instruction instr) {
const u32 rs = regs.Read<s32>(instr.rs());
const u32 rt = regs.Read<s32>(instr.rt());
if (const u32 result = rs + rt; check_signed_overflow(rs, rt, result)) {
regs.cop0.FireException(ExceptionCode::Overflow, 0, regs.oldPC);
regs.cop0.FireException(Cop0::ExceptionCode::Overflow, 0, regs.oldPC);
} else {
regs.Write(instr.rd(), static_cast<s32>(result));
}
@@ -25,7 +26,7 @@ void Interpreter::addi(const Instruction instr) {
const u32 rs = regs.Read<s64>(instr.rs());
const u32 imm = static_cast<s32>(static_cast<s16>(instr));
if (const u32 result = rs + imm; check_signed_overflow(rs, imm, result)) {
regs.cop0.FireException(ExceptionCode::Overflow, 0, regs.oldPC);
regs.cop0.FireException(Cop0::ExceptionCode::Overflow, 0, regs.oldPC);
} else {
regs.Write(instr.rt(), static_cast<s32>(result));
}
@@ -42,7 +43,7 @@ void Interpreter::dadd(const Instruction instr) {
const u64 rs = regs.Read<s64>(instr.rs());
const u64 rt = regs.Read<s64>(instr.rt());
if (const u64 result = rt + rs; check_signed_overflow(rs, rt, result)) {
regs.cop0.FireException(ExceptionCode::Overflow, 0, regs.oldPC);
regs.cop0.FireException(Cop0::ExceptionCode::Overflow, 0, regs.oldPC);
} else {
regs.Write(instr.rd(), result);
}
@@ -58,7 +59,7 @@ void Interpreter::daddi(const Instruction instr) {
const u64 imm = s64(s16(instr));
const u64 rs = regs.Read<s64>(instr.rs());
if (const u64 result = imm + rs; check_signed_overflow(rs, imm, result)) {
regs.cop0.FireException(ExceptionCode::Overflow, 0, regs.oldPC);
regs.cop0.FireException(Cop0::ExceptionCode::Overflow, 0, regs.oldPC);
} else {
regs.Write(instr.rt(), result);
}
@@ -144,12 +145,13 @@ void Interpreter::branch(const bool cond, const s64 address) {
}
void Interpreter::branch_likely(const bool cond, const s64 address) {
if (cond) {
if (!cond) {
regs.SetPC64(regs.nextPC);
return;
}
regs.delaySlot = true;
regs.nextPC = address;
} else {
regs.SetPC64(regs.nextPC);
}
}
void Interpreter::b(const Instruction instr, const bool cond) {
@@ -200,9 +202,9 @@ 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(ExceptionCode::AddressErrorLoad, 0, regs.oldPC);
regs.cop0.FireException(Cop0::ExceptionCode::AddressErrorLoad, 0, regs.oldPC);
return;
}
@@ -218,9 +220,9 @@ 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(ExceptionCode::AddressErrorLoad, 0, regs.oldPC);
regs.cop0.FireException(Cop0::ExceptionCode::AddressErrorLoad, 0, regs.oldPC);
return;
}
@@ -241,8 +243,8 @@ 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)) {
regs.cop0.FireException(ExceptionCode::AddressErrorLoad, 0, regs.oldPC);
if (Core::IsAddressError(0b11, address)) {
regs.cop0.FireException(Cop0::ExceptionCode::AddressErrorLoad, 0, regs.oldPC);
return;
}
@@ -285,9 +287,9 @@ 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(ExceptionCode::AddressErrorLoad, 0, regs.oldPC);
regs.cop0.FireException(Cop0::ExceptionCode::AddressErrorLoad, 0, regs.oldPC);
return;
}
@@ -303,7 +305,7 @@ void Interpreter::ld(const Instruction instr) {
void Interpreter::lld(const Instruction instr) {
if (!regs.cop0.is64BitAddressing && !regs.cop0.kernelMode) {
regs.cop0.FireException(ExceptionCode::ReservedInstruction, 0, regs.oldPC);
regs.cop0.FireException(Cop0::ExceptionCode::ReservedInstruction, 0, regs.oldPC);
return;
}
@@ -313,8 +315,8 @@ 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)) {
regs.cop0.FireException(ExceptionCode::AddressErrorLoad, 0, regs.oldPC);
if (Core::IsAddressError(0b111, address)) {
regs.cop0.FireException(Cop0::ExceptionCode::AddressErrorLoad, 0, regs.oldPC);
} else {
regs.Write(instr.rt(), mem.Read<u64>(paddr));
regs.cop0.llbit = true;
@@ -367,9 +369,9 @@ 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(ExceptionCode::AddressErrorLoad, 0, regs.oldPC);
regs.cop0.FireException(Cop0::ExceptionCode::AddressErrorLoad, 0, regs.oldPC);
return;
}
u32 paddr;
@@ -384,9 +386,9 @@ 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(ExceptionCode::AddressErrorLoad, 0, regs.oldPC);
regs.cop0.FireException(Cop0::ExceptionCode::AddressErrorLoad, 0, regs.oldPC);
return;
}
@@ -407,6 +409,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 {
cachedState.EvictCachedBlock(address);
mem.Write<u8>(paddr, regs.Read<s64>(instr.rt()));
}
}
@@ -417,10 +420,10 @@ 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(ExceptionCode::AddressErrorStore, 0, regs.oldPC);
regs.cop0.FireException(Cop0::ExceptionCode::AddressErrorStore, 0, regs.oldPC);
return;
}
@@ -430,6 +433,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 {
cachedState.EvictCachedBlock(address);
mem.Write<u32>(paddr, regs.Read<s64>(instr.rt()));
regs.Write(instr.rt(), 1);
}
@@ -440,7 +444,7 @@ void Interpreter::sc(const Instruction instr) {
void Interpreter::scd(const Instruction instr) {
if (!regs.cop0.is64BitAddressing && !regs.cop0.kernelMode) {
regs.cop0.FireException(ExceptionCode::ReservedInstruction, 0, regs.oldPC);
regs.cop0.FireException(Cop0::ExceptionCode::ReservedInstruction, 0, regs.oldPC);
return;
}
@@ -449,10 +453,10 @@ 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(ExceptionCode::AddressErrorStore, 0, regs.oldPC);
regs.cop0.FireException(Cop0::ExceptionCode::AddressErrorStore, 0, regs.oldPC);
return;
}
@@ -462,6 +466,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 {
cachedState.EvictCachedBlock(address);
mem.Write<u32>(paddr, regs.Read<s64>(instr.rt()));
regs.Write(instr.rt(), 1);
}
@@ -478,6 +483,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 {
cachedState.EvictCachedBlock(address);
mem.Write<u16>(physical, regs.Read<s64>(instr.rt()));
}
}
@@ -485,9 +491,9 @@ 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(ExceptionCode::AddressErrorStore, 0, regs.oldPC);
regs.cop0.FireException(Cop0::ExceptionCode::AddressErrorStore, 0, regs.oldPC);
return;
}
@@ -496,15 +502,16 @@ void Interpreter::sw(const Instruction instr) {
regs.cop0.HandleTLBException(address);
regs.cop0.FireException(Cop0::GetTLBExceptionCode(regs.cop0.tlbError, Cop0::STORE), 0, regs.oldPC);
} else {
cachedState.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(ExceptionCode::AddressErrorStore, 0, regs.oldPC);
regs.cop0.FireException(Cop0::ExceptionCode::AddressErrorStore, 0, regs.oldPC);
return;
}
@@ -513,6 +520,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 {
cachedState.EvictCachedBlock(address);
mem.Write(physical, regs.Read<s64>(instr.rt()));
}
}
@@ -528,6 +536,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());
cachedState.EvictCachedBlock(address);
mem.Write(paddr & ~7, (data & ~mask) | (rt >> shift));
}
}
@@ -543,6 +552,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());
cachedState.EvictCachedBlock(address);
mem.Write(paddr & ~7, (data & ~mask) | (rt << shift));
}
}
@@ -558,6 +568,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());
cachedState.EvictCachedBlock(address);
mem.Write<u32>(paddr & ~3, (data & ~mask) | (rt >> shift));
}
}
@@ -573,6 +584,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());
cachedState.EvictCachedBlock(address);
mem.Write<u32>(paddr & ~3, (data & ~mask) | (rt << shift));
}
}
@@ -583,7 +595,9 @@ void Interpreter::ori(const Instruction instr) {
regs.Write(instr.rt(), result);
}
void Interpreter::or_(const Instruction instr) { regs.Write(instr.rd(), regs.Read<s64>(instr.rs()) | regs.Read<s64>(instr.rt())); }
void Interpreter::or_(const Instruction instr) {
regs.Write(instr.rd(), regs.Read<s64>(instr.rs()) | regs.Read<s64>(instr.rt()));
}
void Interpreter::nor(const Instruction instr) {
regs.Write(instr.rd(), ~(regs.Read<s64>(instr.rs()) | regs.Read<s64>(instr.rt())));
@@ -622,7 +636,9 @@ void Interpreter::sltiu(const Instruction instr) {
regs.Write(instr.rt(), regs.Read<u64>(instr.rs()) < imm);
}
void Interpreter::slt(const Instruction instr) { regs.Write(instr.rd(), regs.Read<s64>(instr.rs()) < regs.Read<s64>(instr.rt())); }
void Interpreter::slt(const Instruction instr) {
regs.Write(instr.rd(), regs.Read<s64>(instr.rs()) < regs.Read<s64>(instr.rt()));
}
void Interpreter::sltu(const Instruction instr) {
regs.Write(instr.rd(), regs.Read<u64>(instr.rs()) < regs.Read<u64>(instr.rt()));
@@ -753,7 +769,7 @@ void Interpreter::dsub(const Instruction instr) {
const s64 rt = regs.Read<s64>(instr.rt());
const s64 rs = regs.Read<s64>(instr.rs());
if (const s64 result = rs - rt; check_signed_underflow(rs, rt, result)) {
regs.cop0.FireException(ExceptionCode::Overflow, 0, regs.oldPC);
regs.cop0.FireException(Cop0::ExceptionCode::Overflow, 0, regs.oldPC);
} else {
regs.Write(instr.rd(), result);
}
@@ -771,7 +787,7 @@ void Interpreter::sub(const Instruction instr) {
const s32 rs = regs.Read<s64>(instr.rs());
const s32 result = rs - rt;
if (check_signed_underflow(rs, rt, result)) {
regs.cop0.FireException(ExceptionCode::Overflow, 0, regs.oldPC);
regs.cop0.FireException(Cop0::ExceptionCode::Overflow, 0, regs.oldPC);
} else {
regs.Write(instr.rd(), result);
}
@@ -827,7 +843,7 @@ void Interpreter::mthi(const Instruction instr) { regs.hi = regs.Read<s64>(instr
void Interpreter::trap(const bool cond) const {
Cop0 &cop0 = Core::GetRegs().cop0;
if (cond) {
cop0.FireException(ExceptionCode::Trap, 0, regs.oldPC);
cop0.FireException(Cop0::ExceptionCode::Trap, 0, regs.oldPC);
}
}
@@ -845,4 +861,87 @@ void Interpreter::dmfc2(const Instruction instr) { regs.Write(instr.rt(), cop2La
void Interpreter::ctc2(const Instruction) {}
void Interpreter::cfc2(const Instruction) {}
void Interpreter::cache(const Instruction instr) {
u8 type = instr.op() & 3;
const u8 op = (instr.op() >> 2) & 7;
u64 vaddr = regs.Read<u64>(instr.rs()) + (s16)instr.offset();
u32 paddr;
if (!regs.cop0.MapVAddr(Cop0::LOAD, vaddr, paddr)) {
regs.cop0.HandleTLBException(vaddr);
regs.cop0.FireException(Cop0::GetTLBExceptionCode(regs.cop0.tlbError, Cop0::LOAD), 0, regs.oldPC);
return;
}
u32 ptag = GetPhysicalAddressPTag(paddr);
if (type > 1)
panic("Unknown cache type {}", type);
if (type == 0)
return CacheTypeInstruction(op, vaddr, paddr, ptag);
return CacheTypeData(op, vaddr, paddr, ptag);
}
void Interpreter::CacheTypeInstruction(const u8 op, const u64 vaddr, const u32 paddr, const u32 ptag) {
switch (op) {
case 0:
icache.InvalidateIndex(vaddr);
break;
case 1:
icache.LoadTag(vaddr);
break;
case 2:
icache.StoreTag(vaddr, regs.cop0.tagLo.ptaglo, regs.cop0);
break;
case 4:
icache.InvalidateIndex(vaddr, ptag);
break;
case 5:
icache.FillLine(vaddr, paddr);
break;
case 6:
icache.WriteBack(vaddr, paddr, ptag);
break;
default:
panic("Unimplemented icache op 0b{:03b}", op);
}
}
void Interpreter::CacheTypeData(const u8 op, const u64 vaddr, const u32 paddr, const u32 ptag) {
switch (op) {
case 0:
dcache.WriteBack<true>(vaddr, paddr);
break;
case 1:
dcache.LoadTag(vaddr);
break;
case 2:
dcache.StoreTag(vaddr, regs.cop0.tagLo.ptaglo, regs.cop0);
break;
case 3:
{
auto &line = dcache.lines[GetDCacheLineIndex(vaddr)];
if ((line.ptag != ptag || !line.valid) && line.dirty)
dcache.WriteBack(vaddr, paddr);
line.ptag = ptag;
line.valid = true;
line.dirty = true;
}
break;
case 4:
dcache.InvalidateIndex(vaddr, ptag);
break;
case 5:
dcache.WriteBack<true>(vaddr, paddr, ptag);
break;
case 6:
dcache.WriteBack(vaddr, paddr, ptag);
break;
default:
panic("Unimplemented dcache op 0b{:03b}", op);
}
}
} // namespace n64
+6 -6
View File
@@ -32,10 +32,10 @@ void JIT::special(const Instruction instr) {
jalr(instr);
break;
case Instruction::SYSCALL:
regs.cop0.FireException(ExceptionCode::Syscall, 0, regs.oldPC);
regs.cop0.FireException(Cop0::ExceptionCode::Syscall, 0, regs.oldPC);
break;
case Instruction::BREAK:
regs.cop0.FireException(ExceptionCode::Breakpoint, 0, regs.oldPC);
regs.cop0.FireException(Cop0::ExceptionCode::Breakpoint, 0, regs.oldPC);
break;
case Instruction::SYNC:
break; // SYNC
@@ -311,8 +311,7 @@ void JIT::Emit(const Instruction instr) {
emitMemberFunctionCall(&Cop0::eret, &regs.cop0);
break;
default:
panic("Unimplemented COP0 function {} ({:08X}) ({:016X})", instr.cop_funct(), u32(instr),
regs.oldPC);
panic("Unimplemented COP0 function {} ({:08X}) ({:016X})", instr.cop_funct(), u32(instr), regs.oldPC);
}
break;
default:
@@ -378,7 +377,7 @@ void JIT::Emit(const Instruction instr) {
ldr(instr);
break;
case 0x1F:
regs.cop0.FireException(ExceptionCode::ReservedInstruction, 0, regs.oldPC);
regs.cop0.FireException(Cop0::ExceptionCode::ReservedInstruction, 0, regs.oldPC);
break;
case Instruction::LB:
lb(instr);
@@ -459,7 +458,8 @@ void JIT::Emit(const Instruction instr) {
break;
default:
DumpBlockCacheToDisk();
panic("Unimplemented instruction {:02X} ({:08X}) (pc: {:016X})", instr.opcode(), u32(instr), static_cast<u64>(regs.oldPC));
panic("Unimplemented instruction {:02X} ({:08X}) (pc: {:016X})", instr.opcode(), u32(instr),
static_cast<u64>(regs.oldPC));
}
}
} // namespace n64
+75 -5
View File
@@ -20,6 +20,34 @@ static bool SpecialEndsBlock(const Instruction instr) {
}
}
static bool InstrHasDelaySlot(const Instruction instr) {
switch (instr.opcode()) {
case Instruction::SPECIAL:
if (instr.special() == Instruction::JR || instr.special() == Instruction::JALR)
return true;
return false;
case Instruction::REGIMM:
case Instruction::J:
case Instruction::JAL:
case Instruction::BEQ:
case Instruction::BNE:
case Instruction::BLEZ:
case Instruction::BGTZ:
case Instruction::BEQL:
case Instruction::BNEL:
case Instruction::BLEZL:
case Instruction::BGTZL:
return true;
case Instruction::COP1:
if (instr.cop_rs() == 8)
return true;
return false;
default:
return false;
}
}
static bool InstrEndsBlock(const Instruction instr) {
switch (instr.opcode()) {
case Instruction::SPECIAL:
@@ -31,7 +59,28 @@ static bool InstrEndsBlock(const Instruction instr) {
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;
}
@@ -55,16 +104,37 @@ static bool IsBranchLikely(const Instruction instr) {
return false;
}
case Instruction::COP1:
{
if (instr.cop_rs() == 0x08) {
if (instr.cop_rt() == 2 || instr.cop_rt() == 3)
if (instr.cop_rs() == 8 && (instr.cop_rt() == 2 || instr.cop_rt() == 3))
return true;
return false;
}
default:
return false;
}
}
static bool IsBranch(const Instruction instr) {
switch (instr.opcode()) {
case Instruction::BEQ:
case Instruction::BNE:
case Instruction::BLEZ:
case Instruction::BGTZ:
return true;
case Instruction::REGIMM:
switch (instr.regimm()) {
case Instruction::BLTZ:
case Instruction::BGEZ:
case Instruction::BLTZAL:
case Instruction::BGEZAL:
return true;
default:
return false;
}
case Instruction::COP1:
if (instr.cop_rs() == 8 && (instr.cop_rt() == 0 || instr.cop_rt() == 1))
return true;
return false;
default:
return false;
}
+31 -29
View File
@@ -18,7 +18,7 @@ void JIT::add(const Instruction instr) {
const u32 rt = regs.Read<s32>(instr.rt());
const u32 result = rs + rt;
if (check_signed_overflow(rs, rt, result)) {
// regs.cop0.FireException(ExceptionCode::Overflow, 0, regs.oldPC);
// regs.cop0.FireException(Cop0::ExceptionCode::Overflow, 0, regs.oldPC);
panic("[JIT]: Unhandled Overflow exception in ADD!");
}
@@ -205,13 +205,9 @@ void JIT::BranchTaken(const Xbyak::Reg64 &offs) {
SetPC64(code.SCR2);
}
void JIT::BranchAbsTaken(const s64 addr) {
SetPC64(addr);
}
void JIT::BranchAbsTaken(const s64 addr) { SetPC64(addr); }
void JIT::BranchAbsTaken(const Xbyak::Reg64 &addr) {
SetPC64(addr);
}
void JIT::BranchAbsTaken(const Xbyak::Reg64 &addr) { SetPC64(addr); }
void JIT::branch_constant(const bool cond, const s64 offset) {
if (cond) {
@@ -239,7 +235,8 @@ void JIT::branch_likely_constant(const bool cond, const s64 offset) {
}
}
#define branch(offs, cond) do { \
#define branch(offs, cond) \
do { \
Xbyak::Label taken, not_taken; \
code.j##cond(taken); \
code.jmp(not_taken); \
@@ -247,9 +244,11 @@ void JIT::branch_likely_constant(const bool cond, const s64 offset) {
BranchTaken(offs); \
code.mov(code.byte[code.rbp + BRANCH_TAKEN_OFFSET], 1); \
code.L(not_taken); \
} while(0)
} \
while (0)
#define branch_abs(addr, cond) do { \
#define branch_abs(addr, cond) \
do { \
Xbyak::Label taken, not_taken; \
code.j##cond(taken); \
code.jmp(not_taken); \
@@ -257,9 +256,11 @@ void JIT::branch_likely_constant(const bool cond, const s64 offset) {
BranchAbsTaken(addr); \
code.mov(code.byte[code.rbp + BRANCH_TAKEN_OFFSET], 1); \
code.L(not_taken); \
} while(0)
} \
while (0)
#define branch_likely(offs, cond) do { \
#define branch_likely(offs, cond) \
do { \
Xbyak::Label taken, not_taken, end; \
code.j##cond(taken); \
code.jmp(not_taken); \
@@ -270,7 +271,8 @@ void JIT::branch_likely_constant(const bool cond, const s64 offset) {
code.L(not_taken); \
SetPC64(blockNextPC); \
code.L(end); \
} while(0)
} \
while (0)
void JIT::bltz(const Instruction instr) {
const s16 imm = instr;
@@ -558,7 +560,7 @@ void JIT::dadd(const Instruction instr) {
auto rt = regs.Read<u64>(instr.rt());
u64 result = rt + rs;
if (check_signed_overflow(rs, rt, result)) {
// regs.cop0.FireException(ExceptionCode::Overflow, 0, regs.oldPC);
// regs.cop0.FireException(Cop0::ExceptionCode::Overflow, 0, regs.oldPC);
panic("[JIT]: Unhandled Overflow exception in DADD!");
}
regs.Write(instr.rd(), result);
@@ -598,7 +600,7 @@ void JIT::daddi(const Instruction instr) {
auto rs = regs.Read<u64>(instr.rs());
u64 result = imm + rs;
if (check_signed_overflow(rs, imm, result)) {
// regs.cop0.FireException(ExceptionCode::Overflow, 0, regs.oldPC);
// regs.cop0.FireException(Cop0::ExceptionCode::Overflow, 0, regs.oldPC);
panic("[JIT]: Unhandled Overflow exception in DADDI!");
}
regs.Write(instr.rt(), result);
@@ -842,7 +844,7 @@ void JIT::dsub(const Instruction instr) {
auto rs = regs.Read<s64>(instr.rs());
s64 result = rs - rt;
if (check_signed_underflow(rs, rt, result)) {
// regs.cop0.FireException(ExceptionCode::Overflow, 0, regs.oldPC);
// regs.cop0.FireException(Cop0::ExceptionCode::Overflow, 0, regs.oldPC);
panic("[JIT]: Unhandled Overflow exception in DSUB!");
} else {
regs.Write(instr.rd(), result);
@@ -928,9 +930,9 @@ 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(ExceptionCode::AddressErrorLoad, 0, regs.oldPC);
// regs.cop0.FireException(Cop0::ExceptionCode::AddressErrorLoad, 0, regs.oldPC);
// return;
panic("[JIT]: Unhandled ADEL exception in LD!");
}
@@ -1012,9 +1014,9 @@ 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(ExceptionCode::AddressErrorLoad, 0, regs.oldPC);
// regs.cop0.FireException(Cop0::ExceptionCode::AddressErrorLoad, 0, regs.oldPC);
// return;
panic("[JIT]: Unhandled ADEL exception in LH!");
return;
@@ -1041,9 +1043,9 @@ 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(ExceptionCode::AddressErrorLoad, 0, regs.oldPC);
regs.cop0.FireException(Cop0::ExceptionCode::AddressErrorLoad, 0, regs.oldPC);
return;
}
@@ -1078,9 +1080,9 @@ 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(ExceptionCode::AddressErrorLoad, 0, regs.oldPC);
// regs.cop0.FireException(Cop0::ExceptionCode::AddressErrorLoad, 0, regs.oldPC);
// return;
panic("[JIT]: Unhandled ADEL exception in LW!");
return;
@@ -1283,7 +1285,7 @@ void JIT::sub(const Instruction instr) {
s32 rs = regs.Read<s64>(instr.rs());
s32 result = rs - rt;
if (check_signed_underflow(rs, rt, result)) {
regs.cop0.FireException(ExceptionCode::Overflow, 0, regs.oldPC);
regs.cop0.FireException(Cop0::ExceptionCode::Overflow, 0, regs.oldPC);
} else {
regs.Write(instr.rd(), result);
}
@@ -1342,9 +1344,9 @@ 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(ExceptionCode::AddressErrorStore, 0, regs.oldPC);
// regs.cop0.FireException(Cop0::ExceptionCode::AddressErrorStore, 0, regs.oldPC);
panic("[JIT]: Unhandled ADES exception in SW!");
return;
}
@@ -1365,9 +1367,9 @@ 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(ExceptionCode::AddressErrorStore, 0, regs.oldPC);
// regs.cop0.FireException(Cop0::ExceptionCode::AddressErrorStore, 0, regs.oldPC);
panic("[JIT]: Unhandled ADES exception in SW!");
return;
}
+10 -5
View File
@@ -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",
@@ -186,7 +187,8 @@ auto PI::BusRead<u16, false>(u32 addr) -> u16 {
addr = (addr + 2) & ~3;
const u32 index = HALF_ADDRESS(addr) - SREGION_PI_ROM;
if (index > mem.rom.cart.size() - 1) {
panic("Address 0x{:08X} accessed an index {}/0x{:X} outside the bounds of the ROM!", addr, index, index);
panic("Address 0x{:08X} accessed an index {}/0x{:X} outside the bounds of the ROM!", addr, index,
index);
}
return ircolib::ReadAccess<u16>(mem.rom.cart, index);
}
@@ -260,7 +262,8 @@ auto PI::BusRead<u32, false>(u32 addr) -> u32 {
if (index > mem.rom.cart.size() - 3) { // -3 because we're reading an entire word
switch (addr) {
case REGION_CART_ISVIEWER_BUFFER:
return std::byteswap<u32>(ircolib::ReadAccess<u32>(mem.isviewer, addr - SREGION_CART_ISVIEWER_BUFFER));
return std::byteswap<u32>(
ircolib::ReadAccess<u32>(mem.isviewer, addr - SREGION_CART_ISVIEWER_BUFFER));
case CART_ISVIEWER_FLUSH:
panic("Read from ISViewer flush!");
default:
@@ -367,7 +370,8 @@ auto PI::BusRead<u64, false>(u32 addr) -> u64 {
{
const u32 index = addr - SREGION_PI_ROM;
if (index > mem.rom.cart.size() - 7) { // -7 because we're reading an entire dword
panic("Address 0x{:08X} accessed an index {}/0x{:X} outside the bounds of the ROM!", addr, index, index);
panic("Address 0x{:08X} accessed an index {}/0x{:X} outside the bounds of the ROM!", addr, index,
index);
}
return ircolib::ReadAccess<u64>(mem.rom.cart, index);
}
@@ -429,6 +433,7 @@ auto PI::Read(u32 addr) const -> u32 {
value |= (mem.mmio.mi.intr.pi << 3); // PI interrupt?
return value;
}
case 0x04600034:
case 0x04600014:
return piBsdDom1Lat;
case 0x04600018:
+10 -11
View File
@@ -4,8 +4,16 @@
#include <core/mmio/PIF.hpp>
namespace n64 {
struct SI {
SI();
void Reset();
[[nodiscard]] auto Read(u32) const -> u32;
void Write(u32, u32);
template <bool toDram>
void DMA();
void DMA();
union SIStatus {
union Status {
u32 raw{};
struct {
unsigned dmaBusy : 1;
@@ -17,17 +25,8 @@ union SIStatus {
};
};
struct SI {
SI();
void Reset();
[[nodiscard]] auto Read(u32) const -> u32;
void Write(u32, u32);
template <bool toDram>
void DMA();
void DMA();
bool toDram = false;
SIStatus status{};
Status status{};
u32 dramAddr{};
u32 pifAddr{};
PIF pif;
+49 -25
View File
@@ -34,7 +34,7 @@ void Cop0::Reset() {
LLAddr = {}, WatchLo = {}, WatchHi = {};
xcontext = {};
r21 = {}, r22 = {}, r23 = {}, r24 = {}, r25 = {};
ParityError = {}, CacheError = {}, TagLo = {}, TagHi = {};
ParityError = {}, CacheError = {}, tagLo = {}, TagHi = {};
ErrorEPC = {};
r31 = {};
memset(tlb, 0, sizeof(TLBEntry) * 32);
@@ -89,7 +89,7 @@ u32 Cop0::GetReg32(const u8 addr) {
case COP0_REG_CACHE_ERR:
return CacheError;
case COP0_REG_TAGLO:
return TagLo;
return tagLo.raw;
case COP0_REG_TAGHI:
return TagHi;
case COP0_REG_ERROREPC:
@@ -186,7 +186,7 @@ void Cop0::SetReg32(const u8 addr, const u32 value) {
break;
case COP0_REG_CAUSE:
{
Cop0Cause tmp{};
Cause tmp{};
tmp.raw = value;
cause.ip0 = tmp.ip0;
cause.ip1 = tmp.ip1;
@@ -219,7 +219,7 @@ void Cop0::SetReg32(const u8 addr, const u32 value) {
case COP0_REG_CACHE_ERR:
break;
case COP0_REG_TAGLO:
TagLo = value;
tagLo.raw = value;
break;
case COP0_REG_TAGHI:
TagHi = value;
@@ -263,7 +263,7 @@ void Cop0::SetReg64(const u8 addr, const u64 value) {
break;
case COP0_REG_CAUSE:
{
Cop0Cause tmp{};
Cause tmp{};
tmp.raw = value;
cause.ip0 = tmp.ip0;
cause.ip1 = tmp.ip1;
@@ -292,7 +292,7 @@ static FORCE_INLINE u64 getVPN(const u64 addr, const u64 pageMask) {
return vpn & ~mask;
}
TLBEntry *Cop0::TLBTryMatch(const u64 vaddr, int &index) {
Cop0::TLBEntry *Cop0::TLBTryMatch(const u64 vaddr, int &index) {
for (int i = 0; i < 32; i++) {
TLBEntry *entry = &tlb[i];
if (!entry->initialized)
@@ -314,7 +314,7 @@ TLBEntry *Cop0::TLBTryMatch(const u64 vaddr, int &index) {
return nullptr;
}
TLBEntry *Cop0::TLBTryMatch(const u64 vaddr) {
Cop0::TLBEntry *Cop0::TLBTryMatch(const u64 vaddr) {
for (auto &t : tlb) {
TLBEntry *entry = &t;
if (!entry->initialized)
@@ -363,11 +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 &regs = Core::GetRegs();
Core::GetInstance().interpreter.SignalException(pc);
u16 vectorOffset = 0x0180;
if (tlbError == MISS && (code == ExceptionCode::TLBLoad || code == ExceptionCode::TLBStore)) {
if (!status.exl) {
if(is64BitAddressing) vectorOffset = 0x0080;
else vectorOffset = 0x0000;
vectorOffset = 0x0000;
if (is64BitAddressing)
vectorOffset = 0x0080;
}
}
@@ -402,7 +405,7 @@ void Cop0::HandleTLBException(const u64 vaddr) {
entryHi.r = vaddr >> 62 & 3;
}
ExceptionCode Cop0::GetTLBExceptionCode(const TLBError error, const TLBAccessType accessType) {
Cop0::ExceptionCode Cop0::GetTLBExceptionCode(const TLBError error, const TLBAccessType accessType) {
switch (error) {
case NONE:
panic("Getting TLB exception with error NONE");
@@ -422,20 +425,37 @@ ExceptionCode Cop0::GetTLBExceptionCode(const TLBError error, const TLBAccessTyp
void Cop0::decode(const Instruction instr) {
Registers &regs = Core::GetRegs();
switch (instr.cop_rs()) {
case 0x00: mfc0(instr); break;
case 0x01: dmfc0(instr); break;
case 0x04: mtc0(instr); break;
case 0x05: dmtc0(instr); break;
case 0x00:
mfc0(instr);
break;
case 0x01:
dmfc0(instr);
break;
case 0x04:
mtc0(instr);
break;
case 0x05:
dmtc0(instr);
break;
case 0x10 ... 0x1F:
switch (instr.cop_funct()) {
case 0x01: tlbr(); break;
case 0x02: tlbw(index.i); break;
case 0x06: tlbw(GetRandom()); break;
case 0x08: tlbp(); break;
case 0x18: eret(); break;
case 0x01:
tlbr();
break;
case 0x02:
tlbw(index.i);
break;
case 0x06:
tlbw(GetRandom());
break;
case 0x08:
tlbp();
break;
case 0x18:
eret();
break;
default:
panic("Unimplemented COP0 function {} ({:08X}) ({:016X})", instr.cop_funct(), u32(instr),
regs.oldPC);
panic("Unimplemented COP0 function {} ({:08X}) ({:016X})", instr.cop_funct(), u32(instr), regs.oldPC);
}
break;
default:
@@ -537,14 +557,18 @@ bool Cop0::MapVAddr(const TLBAccessType accessType, const u64 vaddr, u32 &paddr)
panic("Supervisor mode memory access");
if (is64BitAddressing) [[unlikely]] {
if (kernelMode) [[likely]] return MapVirtualAddress<u64, false>(accessType, vaddr, paddr);
if (userMode) return MapVirtualAddress<u64, true>(accessType, vaddr, paddr);
if (kernelMode) [[likely]]
return MapVirtualAddress<u64, false>(accessType, vaddr, paddr);
if (userMode)
return MapVirtualAddress<u64, true>(accessType, vaddr, paddr);
panic("Unknown mode! This should never happen!");
}
if (kernelMode) [[likely]] return MapVirtualAddress<u32, false>(accessType, vaddr, paddr);
if (userMode) return MapVirtualAddress<u32, true>(accessType, vaddr, paddr);
if (kernelMode) [[likely]]
return MapVirtualAddress<u32, false>(accessType, vaddr, paddr);
if (userMode)
return MapVirtualAddress<u32, true>(accessType, vaddr, paddr);
panic("Unknown mode! This should never happen!");
}
+21 -10
View File
@@ -38,7 +38,8 @@ namespace n64 {
#define ENTRY_HI_MASK 0xC00000FFFFFFE0FF
#define PAGEMASK_MASK 0x1FFE000
union Cop0Cause {
struct Cop0 {
union Cause {
u32 raw;
struct {
unsigned : 8;
@@ -64,7 +65,7 @@ union Cop0Cause {
} __attribute__((__packed__));
};
union Cop0Status {
union Status {
struct {
unsigned ie : 1;
unsigned exl : 1;
@@ -170,7 +171,7 @@ enum class ExceptionCode : u8 {
Watch = 23
};
union Cop0Context {
union Context {
u64 raw;
struct {
u64 : 4;
@@ -179,7 +180,7 @@ union Cop0Context {
};
};
union Cop0XContext {
union XContext {
u64 raw;
struct {
u64 : 4;
@@ -189,7 +190,16 @@ union Cop0XContext {
} __attribute__((__packed__));
};
struct Cop0 {
union TagLo {
u32 raw;
struct {
u64 : 4;
u64 ptaglo : 20;
u64 pstate : 2;
u64 : 6;
} __attribute__((__packed__));
};
Cop0();
bool kernelMode{true};
@@ -199,19 +209,20 @@ struct Cop0 {
bool llbit{};
TLBError tlbError = NONE;
TagLo tagLo;
PageMask pageMask{};
EntryHi entryHi{};
EntryLo entryLo0{}, entryLo1{};
Index index{};
Cop0Context context{};
Context context{};
u32 wired{}, r7{};
u32 compare{};
Cop0Status status{};
Cop0Cause cause{};
Status status{};
Cause cause{};
u32 PRId{}, Config{}, LLAddr{}, WatchLo{}, WatchHi{};
u32 r21{}, r22{}, r23{}, r24{}, r25{}, ParityError{}, CacheError{}, TagLo{}, TagHi{};
u32 r21{}, r22{}, r23{}, r24{}, r25{}, ParityError{}, CacheError{}, TagHi{};
u32 r31{};
Cop0XContext xcontext{};
XContext xcontext{};
u64 badVaddr{}, count{};
s64 EPC{};
s64 ErrorEPC{};
+3 -3
View File
@@ -152,11 +152,11 @@ struct Cop1 {
private:
template <typename T>
auto FGR_T(const Cop0Status &, u32) -> T &;
auto FGR_T(const Cop0::Status &, u32) -> T &;
template <typename T>
auto FGR_S(const Cop0Status &, u32) -> T &;
auto FGR_S(const Cop0::Status &, u32) -> T &;
template <typename T>
auto FGR_D(const Cop0Status &, u32) -> T &;
auto FGR_D(const Cop0::Status &, u32) -> T &;
void absd(const Instruction instr);
void abss(const Instruction instr);
void adds(const Instruction instr);
+44 -53
View File
@@ -3,7 +3,11 @@
#include <core/JIT.hpp>
namespace n64 {
#ifdef KAIZEN_JIT_ENABLED
Registers::Registers(JIT &jit) : jit(jit) { Reset(); }
#else
Registers::Registers() { Reset(); }
#endif
void Registers::Reset() {
hi = 0;
@@ -72,85 +76,85 @@ s8 Registers::Read<s8>(size_t idx) {
return static_cast<s8>(Read<u8>(idx));
}
#ifndef __aarch64__
#ifdef KAIZEN_JIT_ENABLED
template <>
void Registers::Read<u64>(size_t idx, Xbyak::Reg reg) {
if (IsRegConstant(idx)) {
jit->code.mov(reg.cvt64(), Read<u64>(idx));
jit.code.mov(reg.cvt64(), Read<u64>(idx));
return;
}
jit->code.mov(reg.cvt64(), jit->GPR<u64>(idx));
jit.code.mov(reg.cvt64(), jit.GPR<u64>(idx));
}
template <>
void Registers::Read<s64>(size_t idx, Xbyak::Reg reg) {
if (IsRegConstant(idx)) {
jit->code.mov(reg.cvt64(), Read<s64>(idx));
jit.code.mov(reg.cvt64(), Read<s64>(idx));
return;
}
jit->code.mov(reg.cvt64(), jit->GPR<u64>(idx));
jit.code.mov(reg.cvt64(), jit.GPR<u64>(idx));
}
template <>
void Registers::Read<u32>(size_t idx, Xbyak::Reg reg) {
if (IsRegConstant(idx)) {
jit->code.mov(reg.cvt32(), Read<u32>(idx));
jit.code.mov(reg.cvt32(), Read<u32>(idx));
return;
}
jit->code.mov(reg.cvt32(), jit->GPR<u32>(idx));
jit.code.mov(reg.cvt32(), jit.GPR<u32>(idx));
}
template <>
void Registers::Read<s32>(size_t idx, Xbyak::Reg reg) {
if (IsRegConstant(idx)) {
jit->code.mov(reg.cvt32(), Read<s32>(idx));
jit.code.mov(reg.cvt32(), Read<s32>(idx));
return;
}
jit->code.mov(reg.cvt32(), jit->GPR<s32>(idx));
jit.code.mov(reg.cvt32(), jit.GPR<s32>(idx));
}
template <>
void Registers::Read<u16>(size_t idx, Xbyak::Reg reg) {
if (IsRegConstant(idx)) {
jit->code.mov(reg.cvt16(), Read<u16>(idx));
jit.code.mov(reg.cvt16(), Read<u16>(idx));
return;
}
jit->code.mov(reg.cvt16(), jit->GPR<u16>(idx));
jit.code.mov(reg.cvt16(), jit.GPR<u16>(idx));
}
template <>
void Registers::Read<s16>(size_t idx, Xbyak::Reg reg) {
if (IsRegConstant(idx)) {
jit->code.mov(reg.cvt16(), Read<s16>(idx));
jit.code.mov(reg.cvt16(), Read<s16>(idx));
return;
}
jit->code.mov(reg.cvt16(), jit->GPR<u16>(idx));
jit.code.mov(reg.cvt16(), jit.GPR<u16>(idx));
}
template <>
void Registers::Read<u8>(size_t idx, Xbyak::Reg reg) {
if (IsRegConstant(idx)) {
jit->code.mov(reg.cvt8(), Read<u8>(idx));
jit.code.mov(reg.cvt8(), Read<u8>(idx));
return;
}
jit->code.mov(reg.cvt8(), jit->GPR<u8>(idx));
jit.code.mov(reg.cvt8(), jit.GPR<u8>(idx));
}
template <>
void Registers::Read<s8>(size_t idx, Xbyak::Reg reg) {
if (IsRegConstant(idx)) {
jit->code.mov(reg.cvt8(), Read<s8>(idx));
jit.code.mov(reg.cvt8(), Read<s8>(idx));
return;
}
jit->code.mov(reg.cvt8(), jit->GPR<s8>(idx));
jit.code.mov(reg.cvt8(), jit.GPR<s8>(idx));
}
#endif
@@ -159,7 +163,6 @@ void Registers::Write<bool>(size_t idx, bool v) {
if (idx == 0)
return;
if (jit) [[unlikely]]
regIsConstant |= (1 << idx);
gpr[idx] = v;
@@ -170,8 +173,8 @@ void Registers::Write<u64>(size_t idx, u64 v) {
if (idx == 0)
return;
if (jit) [[unlikely]]
regIsConstant |= (1 << idx);
gpr[idx] = v;
}
@@ -185,8 +188,8 @@ void Registers::Write<u32>(size_t idx, u32 v) {
if (idx == 0)
return;
if (jit) [[unlikely]]
regIsConstant |= (1 << idx);
gpr[idx] = v;
}
@@ -196,8 +199,8 @@ void Registers::Write<s32>(size_t idx, s32 v) {
if (idx == 0)
return;
if (jit) [[unlikely]]
regIsConstant |= (1 << idx);
gpr[idx] = v;
}
@@ -206,8 +209,8 @@ void Registers::Write<u16>(size_t idx, u16 v) {
if (idx == 0)
return;
if (jit) [[unlikely]]
regIsConstant |= (1 << idx);
gpr[idx] = v;
}
@@ -217,8 +220,8 @@ void Registers::Write<s16>(size_t idx, s16 v) {
if (idx == 0)
return;
if (jit) [[unlikely]]
regIsConstant |= (1 << idx);
gpr[idx] = v;
}
@@ -227,8 +230,8 @@ void Registers::Write<u8>(size_t idx, u8 v) {
if (idx == 0)
return;
if (jit) [[unlikely]]
regIsConstant |= (1 << idx);
gpr[idx] = v;
}
@@ -238,24 +241,21 @@ void Registers::Write<s8>(size_t idx, s8 v) {
if (idx == 0)
return;
if (jit) [[unlikely]]
regIsConstant |= (1 << idx);
gpr[idx] = v;
}
#ifndef __aarch64__
#ifdef KAIZEN_JIT_ENABLED
template <>
void Registers::Write<bool>(size_t idx, Xbyak::Reg v) {
if (idx == 0)
return;
if (!jit)
panic("Did you try to call Registers::Write(size_t, *Xbyak::Reg*) from the interpreter?");
regIsConstant &= ~(1 << idx);
jit->code.movsx(v.cvt64(), v.cvt8());
jit->code.mov(jit->GPR<u64>(idx), v);
jit.code.movsx(v.cvt64(), v.cvt8());
jit.code.mov(jit.GPR<u64>(idx), v);
}
template <>
@@ -263,13 +263,10 @@ void Registers::Write<s8>(size_t idx, Xbyak::Reg v) {
if (idx == 0)
return;
if (!jit)
panic("Did you try to call Registers::Write(size_t, *Xbyak::Reg*) from the interpreter?");
regIsConstant &= ~(1 << idx);
jit->code.movsx(v.cvt64(), v.cvt8());
jit->code.mov(jit->GPR<u64>(idx), v);
jit.code.movsx(v.cvt64(), v.cvt8());
jit.code.mov(jit.GPR<u64>(idx), v);
}
template <>
@@ -277,13 +274,10 @@ void Registers::Write<u8>(size_t idx, Xbyak::Reg v) {
if (idx == 0)
return;
if (!jit)
panic("Did you try to call Registers::Write(size_t, *Xbyak::Reg*) from the interpreter?");
regIsConstant &= ~(1 << idx);
jit->code.movzx(v.cvt64(), v.cvt8());
jit->code.mov(jit->GPR<u64>(idx), v.cvt64());
jit.code.movzx(v.cvt64(), v.cvt8());
jit.code.mov(jit.GPR<u64>(idx), v.cvt64());
}
template <>
@@ -291,13 +285,10 @@ void Registers::Write<s16>(size_t idx, Xbyak::Reg v) {
if (idx == 0)
return;
if (!jit)
panic("Did you try to call Registers::Write(size_t, *Xbyak::Reg*) from the interpreter?");
regIsConstant &= ~(1 << idx);
jit->code.movsx(v.cvt64(), v.cvt16());
jit->code.mov(jit->GPR<u64>(idx), v.cvt64());
jit.code.movsx(v.cvt64(), v.cvt16());
jit.code.mov(jit.GPR<u64>(idx), v.cvt64());
}
template <>
@@ -310,8 +301,8 @@ void Registers::Write<u16>(size_t idx, Xbyak::Reg v) {
regIsConstant &= ~(1 << idx);
jit->code.movzx(v.cvt64(), v.cvt16());
jit->code.mov(jit->GPR<u64>(idx), v.cvt64());
jit.code.movzx(v.cvt64(), v.cvt16());
jit.code.mov(jit.GPR<u64>(idx), v.cvt64());
}
template <>
@@ -324,8 +315,8 @@ void Registers::Write<s32>(size_t idx, Xbyak::Reg v) {
regIsConstant &= ~(1 << idx);
jit->code.movsxd(v.cvt64(), v.cvt32());
jit->code.mov(jit->GPR<u64>(idx), v.cvt64());
jit.code.movsxd(v.cvt64(), v.cvt32());
jit.code.mov(jit.GPR<u64>(idx), v.cvt64());
}
template <>
@@ -338,8 +329,8 @@ void Registers::Write<u32>(size_t idx, Xbyak::Reg v) {
regIsConstant &= ~(1 << idx);
jit->code.movzx(v.cvt64(), v.cvt32());
jit->code.mov(jit->GPR<u64>(idx), v.cvt64());
jit.code.movzx(v.cvt64(), v.cvt32());
jit.code.mov(jit.GPR<u64>(idx), v.cvt64());
}
template <>
@@ -352,7 +343,7 @@ void Registers::Write<u64>(size_t idx, Xbyak::Reg v) {
regIsConstant &= ~(1 << idx);
jit->code.mov(jit->GPR<u64>(idx), v.cvt64());
jit.code.mov(jit.GPR<u64>(idx), v.cvt64());
}
template <>
+16 -21
View File
@@ -4,13 +4,17 @@
#include <backend/core/registers/Cop1.hpp>
namespace n64 {
#ifdef KAIZEN_JIT_ENABLED
struct JIT;
struct Registers {
JIT &jit;
Registers(JIT &jit);
#else
struct Registers {
Registers();
void Reset();
void SetPC64(s64);
void SetPC32(s32);
void SetJIT(JIT* jit) { this->jit = jit; }
[[nodiscard]] bool IsRegConstant(const u32 index) const {
if (index == 0)
@@ -22,31 +26,17 @@ struct Registers {
return IsRegConstant(index1) && IsRegConstant(index2);
}
bool GetLOConstant() {
return regIsConstant & (1ull << 32);
}
bool GetLOConstant() { return regIsConstant & (1ull << 32); }
bool GetHIConstant() {
return regIsConstant & (1ull << 33);
}
bool GetHIConstant() { return regIsConstant & (1ull << 33); }
void SetLOConstant() {
regIsConstant |= (1ull << 32);
}
void SetLOConstant() { regIsConstant |= (1ull << 32); }
void SetHIConstant() {
regIsConstant |= (1ull << 33);
}
void SetHIConstant() { regIsConstant |= (1ull << 33); }
void UnsetLOConstant() {
regIsConstant &= ~(1ull << 32);
}
void UnsetLOConstant() { regIsConstant &= ~(1ull << 32); }
void UnsetHIConstant() {
regIsConstant &= ~(1ull << 33);
}
JIT *jit = nullptr;
void UnsetHIConstant() { regIsConstant &= ~(1ull << 33); }
uint64_t regIsConstant = 0;
@@ -76,5 +66,10 @@ struct Registers {
void Write(size_t, Xbyak::Reg);
std::array<s64, 32> gpr{};
static inline const char *regNames[] = {"zero", "at", "v0", "v1", "a0", "a1", "a2", "a3", "t0", "t1", "t2",
"t3", "t4", "t5", "t6", "t7", "s0", "s1", "s2", "s3", "s4", "s5",
"s6", "s7", "t8", "t9", "k0", "k1", "gp", "sp", "s8", "ra"};
};
#endif
} // namespace n64
+11 -17
View File
@@ -1,16 +1,6 @@
#include <Debugger.hpp>
#include <imgui.h>
char const* regNames[] = {
"zero", "at", "v0", "v1",
"a0", "a1", "a2", "a3",
"t0", "t1", "t2", "t3",
"t4", "t5", "t6", "t7",
"s0", "s1", "s2", "s3",
"s4", "s5", "s6", "s7",
"t8", "t9", "k0", "k1",
"gp", "sp", "s8", "ra",
};
#include <Registers.hpp>
void BreakpointFunc(s64 addr, Disassembler::DisassemblyResult &) {
n64::Core &core = n64::Core::GetInstance();
@@ -70,7 +60,10 @@ void Debugger::RegisterView() {
if (!ImGui::BeginTabItem("Registers"))
return;
if(!ImGui::BeginTable("##regs", 4, ImGuiTableFlags_SizingStretchSame | ImGuiTableFlags_Resizable | ImGuiTableFlags_BordersOuter | ImGuiTableFlags_BordersV | ImGuiTableFlags_ContextMenuInBody))
if (!ImGui::BeginTable("##regs", 4,
ImGuiTableFlags_SizingStretchSame | ImGuiTableFlags_Resizable |
ImGuiTableFlags_BordersOuter | ImGuiTableFlags_BordersV |
ImGuiTableFlags_ContextMenuInBody))
return;
ImGui::TableSetupColumn("Name");
@@ -126,7 +119,7 @@ void Debugger::RegisterView() {
ImGui::TableNextRow();
ImGui::TableSetColumnIndex(0);
ImGui::Text("%s", regNames[i]);
ImGui::Text("%s", n64::Registers::regNames[i]);
ImGui::TableSetColumnIndex(1);
auto value = regs.Read<u64>(i);
@@ -134,7 +127,7 @@ void Debugger::RegisterView() {
renderMemoryTable(value);
ImGui::TableSetColumnIndex(2);
ImGui::Text("%s", regNames[i+1]);
ImGui::Text("%s", n64::Registers::regNames[i + 1]);
ImGui::TableSetColumnIndex(3);
value = regs.Read<u64>(i + 1);
@@ -163,7 +156,8 @@ bool Debugger::render() {
}
ImGui::BeginDisabled(followPC);
ImGui::InputScalar("Address", ImGuiDataType_S64, &startAddr, &step, &stepFast, "%016lX", ImGuiInputTextFlags_CharsHexadecimal);
ImGui::InputScalar("Address", ImGuiDataType_S64, &startAddr, &step, &stepFast, "%016lX",
ImGuiInputTextFlags_CharsHexadecimal);
ImGui::EndDisabled();
ImGui::Text("Follow program counter:");
@@ -196,8 +190,8 @@ bool Debugger::render() {
return false;
}
constexpr auto disasmTableFlags = ImGuiTableFlags_SizingFixedSame | ImGuiTableFlags_Resizable | ImGuiTableFlags_BordersOuter |
ImGuiTableFlags_BordersV | ImGuiTableFlags_ContextMenuInBody;
constexpr auto disasmTableFlags = ImGuiTableFlags_SizingFixedSame | ImGuiTableFlags_Resizable |
ImGuiTableFlags_BordersOuter | ImGuiTableFlags_BordersV | ImGuiTableFlags_ContextMenuInBody;
if (!ImGui::BeginTable("Disassembly", columns.size(), disasmTableFlags)) {
ImGui::EndTabBar();
+6 -1
View File
@@ -1,5 +1,6 @@
#pragma once
#include <backend/Core.hpp>
#include <Disassembler.hpp>
void BreakpointFunc(s64, Disassembler::DisassemblyResult &);
void AddressFunc(s64, Disassembler::DisassemblyResult &);
@@ -19,10 +20,14 @@ class Debugger final {
Column{"Address", &AddressFunc},
Column{"Instruction", &InstructionFunc},
};
public:
static void RegisterView();
bool followPC = true;
void Open(bool wantFollowPC = true) { enabled = true; followPC = wantFollowPC; }
void Open(bool wantFollowPC = true) {
enabled = true;
followPC = wantFollowPC;
}
void Close() { enabled = false; }
bool render();
};
+63 -35
View File
@@ -5,7 +5,9 @@
#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) {
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);
@@ -33,7 +35,8 @@ void KaizenGui::QueryDevices(const SDL_Event &event) {
if (gamepad)
SDL_CloseGamepad(gamepad);
break;
default: break;
default:
break;
}
}
@@ -45,11 +48,16 @@ void KaizenGui::HandleInput(const SDL_Event &event) {
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);
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) {
@@ -134,16 +142,21 @@ void KaizenGui::HandleInput(const SDL_Event &event) {
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;
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;
default:
break;
}
}
@@ -156,9 +169,9 @@ std::pair<std::optional<s64>, std::optional<Util::Error::MemoryAccess>> RenderEr
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) : "")
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());
}
@@ -260,7 +273,8 @@ void KaizenGui::RenderUI() {
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: {
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());
@@ -270,8 +284,10 @@ void KaizenGui::RenderUI() {
RenderErrorMessageDetails();
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 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();
@@ -294,8 +310,10 @@ void KaizenGui::RenderUI() {
if (ImGui::Button("OK"))
ImGui::CloseCurrentPopup();
} break;
case Util::Error::Severity::UNRECOVERABLE: {
}
break;
case Util::Error::Severity::UNRECOVERABLE:
{
emuThread.Stop();
ImGui::PushStyleColor(ImGuiCol_TitleBg, 0x800000ff);
ImGui::PushStyleColor(ImGuiCol_Text, 0xff3b3bbf);
@@ -307,8 +325,10 @@ void KaizenGui::RenderUI() {
RenderErrorMessageDetails();
if (ImGui::Button("OK"))
ImGui::CloseCurrentPopup();
} break;
case Util::Error::Severity::NON_FATAL: {
}
break;
case Util::Error::Severity::NON_FATAL:
{
ImGui::PushStyleColor(ImGuiCol_TitleBg, 0x800000ff);
ImGui::PushStyleColor(ImGuiCol_Text, 0xff3b3bbf);
ImGui::Text("An error has occurred!");
@@ -318,10 +338,13 @@ void KaizenGui::RenderUI() {
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 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;
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();
@@ -346,8 +369,10 @@ void KaizenGui::RenderUI() {
debugger.Open();
emuThread.Reset();
}
} break;
default: break;
}
break;
default:
break;
}
ImGui::EndPopup();
@@ -359,7 +384,8 @@ void KaizenGui::RenderUI() {
}
if (shouldDisplaySpinner) {
ImGui::SetNextWindowPos({static_cast<float>(width) * 0.5f, static_cast<float>(height) * 0.5f}, 0, ImVec2(0.5f, 0.5f));
ImGui::SetNextWindowPos({static_cast<float>(width) * 0.5f, static_cast<float>(height) * 0.5f}, 0,
ImVec2(0.5f, 0.5f));
ImGui::PushStyleColor(ImGuiCol_WindowBg, IM_COL32_BLACK_TRANS);
ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 0.0f);
@@ -386,8 +412,11 @@ void KaizenGui::RenderUI() {
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) {
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) {
@@ -402,10 +431,10 @@ void KaizenGui::RenderUI() {
kaizen->fileToLoad = *filelist;
kaizen->shouldDisplaySpinner = true;
std::thread fileWorker(&KaizenGui::FileWorker, kaizen);
fileWorker.detach();
}, this, window.getHandle(), filters, 3, nullptr, false);
},
this, window.getHandle(), filters, 3, nullptr, false);
}
if (minimized)
@@ -443,6 +472,7 @@ void KaizenGui::run() {
minimized = false;
break;
default:
break;
}
QueryDevices(e);
HandleInput(e);
@@ -455,6 +485,4 @@ void KaizenGui::run() {
}
}
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)); }
+2 -1
View File
@@ -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;
}
}
}
+13 -3
View File
@@ -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,9 +16,10 @@ CPUSettings::CPUSettings() {
}
void CPUSettings::render() {
const char* items[] = {
"Interpreter",
const char *items[] = {"Interpreter", "Cached Interpreter",
#ifdef KAIZEN_JIT_ENABLED
"Dynamic Recompiler"
#endif
};
const char *combo_preview_value = items[selectedCpuTypeIndex];
@@ -36,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;
}
}
}
+5 -1
View File
@@ -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; }
@@ -21,7 +23,9 @@ struct Instruction {
inline u8 vd() const { return fd(); }
inline u8 e1() const { return (instr.raw >> 7) & 0x0f; }
inline u8 e2() const { return rs() & 0x0f; }
inline u8 op() const { return rt(); }
inline u16 imm() const { return instr.itype.imm; }
inline u16 offset() const { return imm(); }
inline u32 target() const { return instr.jtype.target; }
inline u8 opcode() const { return instr.opcode.op; }
inline u8 special() const { return instr.opcode.special; }
@@ -221,4 +225,4 @@ struct Instruction {
static constexpr u8 BLTZALL = 0b10010;
static constexpr u8 BGEZALL = 0b10011;
};
}
} // namespace n64