#pragma once #include #include "dynarmic/interface/A32/a32.h" #include "dynarmic/interface/A32/config.h" #include "dynarmic/interface/exclusive_monitor.h" #include "dynarmic_cp15.hpp" #include "helpers.hpp" #include "kernel.hpp" #include "memory.hpp" class CPU; class MyEnvironment final : public Dynarmic::A32::UserCallbacks { public: u64 ticksLeft = 0; u64 totalTicks = 0; Memory& mem; Kernel& kernel; u64 getCyclesForInstruction(bool isThumb, u32 instruction); u8 MemoryRead8(u32 vaddr) override { return mem.read8(vaddr); } u16 MemoryRead16(u32 vaddr) override { return mem.read16(vaddr); } u32 MemoryRead32(u32 vaddr) override { return mem.read32(vaddr); } u64 MemoryRead64(u32 vaddr) override { return mem.read64(vaddr); } void MemoryWrite8(u32 vaddr, u8 value) override { mem.write8(vaddr, value); } void MemoryWrite16(u32 vaddr, u16 value) override { mem.write16(vaddr, value); } void MemoryWrite32(u32 vaddr, u32 value) override { mem.write32(vaddr, value); } void MemoryWrite64(u32 vaddr, u64 value) override { mem.write64(vaddr, value); } #define makeExclusiveWriteHandler(size) \ bool MemoryWriteExclusive##size(u32 vaddr, u##size value, u##size expected) override { \ u##size current = mem.read##size(vaddr); /* Get current value */ \ if (current == expected) { /* Perform the write if current == expected */ \ mem.write##size(vaddr, value); \ return true; /* Exclusive write succeeded */ \ } \ \ return false; /* Exclusive write failed */ \ } makeExclusiveWriteHandler(8) makeExclusiveWriteHandler(16) makeExclusiveWriteHandler(32) makeExclusiveWriteHandler(64) #undef makeExclusiveWriteHandler void InterpreterFallback(u32 pc, size_t num_instructions) override { // This is never called in practice. std::terminate(); } void CallSVC(u32 swi) override { kernel.serviceSVC(swi); } void ExceptionRaised(u32 pc, Dynarmic::A32::Exception exception) override { switch (exception) { case Dynarmic::A32::Exception::UnpredictableInstruction: Helpers::panic("Unpredictable instruction at pc = %08X", pc); break; default: Helpers::panic("Fired exception oops"); } } void AddTicks(u64 ticks) override { totalTicks += ticks; if (ticks > ticksLeft) { ticksLeft = 0; return; } ticksLeft -= ticks; } u64 GetTicksRemaining() override { return ticksLeft; } u64 GetTicksForCode(bool isThumb, u32 vaddr, u32 instruction) override { return getCyclesForInstruction(isThumb, instruction); } MyEnvironment(Memory& mem, Kernel& kernel) : mem(mem), kernel(kernel) {} }; class CPU { std::unique_ptr jit; std::shared_ptr cp15; // Make exclusive monitor with only 1 CPU core Dynarmic::ExclusiveMonitor exclusiveMonitor{1}; MyEnvironment env; Memory& mem; public: static constexpr u64 ticksPerSec = 268111856; CPU(Memory& mem, Kernel& kernel); void reset(); void setReg(int index, u32 value) { jit->Regs()[index] = value; } u32 getReg(int index) { return jit->Regs()[index]; } std::span regs() { return jit->Regs(); } // Get reference to array of FPRs. This array consists of the FPRs as single precision values // Hence why its base type is u32 std::span fprs() { return std::span(jit->ExtRegs()).first<32>(); } void setCPSR(u32 value) { jit->SetCpsr(value); } u32 getCPSR() { return jit->Cpsr(); } void setFPSCR(u32 value) { jit->SetFpscr(value); } u32 getFPSCR() { return jit->Fpscr(); } // Set the base pointer to thread-local storage, stored in a CP15 register on the 3DS void setTLSBase(u32 value) { cp15->setTLSBase(value); } u64 getTicks() { return env.totalTicks; } // Get reference to tick count. Memory needs access to this u64& getTicksRef() { return env.totalTicks; } void clearCache() { jit->ClearCache(); } void runFrame() { env.ticksLeft = ticksPerSec / 60; execute: const auto exitReason = jit->Run(); if (static_cast(exitReason) != 0) [[unlikely]] { // Cache invalidation needs to exit the JIT so it returns a CacheInvalidation HaltReason. In our case, we just go back to executing // The goto might be terrible but it does guarantee that this does not recursively call run and crash, instead getting optimized to a jump if (Dynarmic::Has(exitReason, Dynarmic::HaltReason::CacheInvalidation)) { goto execute; } else { Helpers::panic("Exit reason: %d\nPC: %08X", static_cast(exitReason), getReg(15)); } } } };