dynemu/src/lib/sys_emulator.cpp
2026-03-29 22:54:00 +07:00

726 lines
No EOL
26 KiB
C++

/* (c) 2025 Wrapper Inc. - The New Emulation Library */
#include <fstream>
#include "fmt/format.h"
#include "shared/os_wrapper.hpp"
#include "dynemu/sys_emulator.hpp"
#include "dynarmic/interface/A32/a32.h"
#include "dynarmic/interface/A32/config.h"
#include "dynarmic/interface/A32/coprocessor.h"
#include "dynarmic/interface/exclusive_monitor.h"
#include "dynarmic/interface/optimization_flags.h"
#include "dynarmic/frontend/A32/a32_ir_emitter.h"
#include "mcl/assert.hpp"
#define DEBUG 0
#define BLOCK_ALLIGN 0x10000
#define ALIGN_BLOCK(x) ((x + (BLOCK_ALLIGN - 1)) & ~(BLOCK_ALLIGN - 1))
#define EMU_MIN(a,b) ((a) < (b) ? (a) : (b))
#define EMU_MAX(a,b) ((a) > (b) ? (a) : (b))
namespace Dynemu {
struct FlashPartition {
u32 start_vaddr;
u32 size;
std::unique_ptr<u8[]> data;
};
struct FlashLoader {
u16 magic;
u32 count;
std::unique_ptr<FlashPartition[]> data;
};
/* 01 - Private */
MemMap * MyEmulator::find_memory_map_by_vaddr(u32 vaddr) {
for (int i = 0; i < mem_map_index; i++) {
if (vaddr >= mem_map[i].start && vaddr < (mem_map[i].start + mem_map[i].size))
return &mem_map[i];
}
return nullptr;
}
MMIO * MyEmulator::find_mmio_by_vaddr(u32 vaddr) {
for (int i = 0; i < mmio_index; i++) {
if (vaddr >= mmio_map[i].start && vaddr < (mmio_map[i].start + mmio_map[i].size))
return &mmio_map[i];
}
return nullptr;
}
std::optional<u32> MyEmulator::handle_mmio_read(u32 vaddr, u8 access) {
if (auto mmio = find_mmio_by_vaddr(vaddr); mmio != nullptr) {
#if DEBUG
printf("Handling MMIO READ for 0x%08x\n", vaddr);
#endif
return mmio->read(vaddr - mmio->start, access);
}
return std::nullopt;
}
bool MyEmulator::handle_mmio_write(u32 vaddr, u32 value, u8 access) {
if (auto mmio = find_mmio_by_vaddr(vaddr); mmio != nullptr) {
#if DEBUG
printf("Handling MMIO WRITE for 0x%08x\n", vaddr);
#endif
mmio->write(vaddr - mmio->start, value, access);
return true;
}
return false;
}
bool MyEmulator::check_memory_access(u32 vaddr, u32 size) {
if (auto mmio = find_mmio_by_vaddr(vaddr); mmio != nullptr) {
return ((mmio->start + mmio->size) - vaddr) >= size;
}
if (auto mmap = find_memory_map_by_vaddr(vaddr); mmap != nullptr) {
return ((mmap->start + mmap->size) - vaddr) >= size;
}
return false;
}
/* 02 - Public */
void MyEmulator::CreateMemoryMap(u32 vaddr, u32 size) {
ASSERT_MSG(find_mmio_by_vaddr(vaddr) == nullptr, "Cannot create memory map on MMIO-mapped addresses");
if (find_memory_map_by_vaddr(vaddr) == nullptr)
mem_map[mem_map_index++] = {vaddr, size, std::vector<u8>(size)};
}
size_t MyEmulator::UploadToMemory(const std::string &filename, std::int64_t dest, u32 size) {
std::ifstream file(filename, std::ios::binary);
u8 buf[2048];
u32 offset = 0;
u32 read_n;
if (!file.is_open()) {
std::cerr << fmt::format("dynemu: unable to open code file {}: {}", filename, std::strerror(errno)) << std::endl;
return 0;
}
if (dest != -1) {
file.seekg(0, file.end);
size_t fileSize = file.tellg();
file.seekg(0, file.beg);
auto mmap = find_memory_map_by_vaddr((u32)dest);
bool fitsOnMemoryMap = mmap == nullptr ? (size >= fileSize) : (((mmap->start + mmap->size) - dest) >= fileSize);
if (!fitsOnMemoryMap) {
std::cerr << "dynemu: code file too large to fit into memory" << std::endl;
return 0;
}
CreateMemoryMap((u32)dest, ALIGN_BLOCK(size));
while ((size_t)file.tellg() < fileSize) {
read_n = EMU_MIN(fileSize - file.tellg(), sizeof(buf));
file.read((char *)buf, read_n);
MemoryWriteBytes((u32)(dest + offset), buf, read_n);
offset += read_n;
}
return fileSize;
} else {
FlashLoader fl{};
u32 codeSize = 0;
file.read((char *)&fl.magic, sizeof(fl.magic));
file.read((char *)&fl.count, sizeof(fl.count));
if (fl.magic != 0x4c46) {
std::cerr << "dynemu: invalid FL magic check" << std::endl;
return 0;
}
fl.data = std::make_unique<FlashPartition[]>(fl.count);
for (u32 i = 0; i < fl.count; i++) {
file.read((char *)&fl.data.get()[i].start_vaddr, sizeof(fl.data.get()[i].start_vaddr));
file.read((char *)&fl.data.get()[i].size, sizeof(fl.data.get()[i].size));
fl.data.get()[i].data = std::make_unique<u8[]>(fl.data.get()[i].size);
file.read((char *)fl.data.get()[i].data.get(), fl.data.get()[i].size);
codeSize += fl.data.get()[i].size;
CreateMemoryMap(fl.data.get()[i].start_vaddr, ALIGN_BLOCK(fl.data.get()[i].size));
MemoryWriteBytes(fl.data.get()[i].start_vaddr, fl.data.get()[i].data.get(), fl.data.get()[i].size);
}
return codeSize;
}
}
bool MyEmulator::UploadFLSFromBytes(const u8 *fls) {
FlashLoader fl{};
memcpy(&fl.magic, fls, sizeof(fl.magic));
memcpy(&fl.count, fls + sizeof(fl.magic), sizeof(fl.count));
if (fl.magic != 0x4c46) {
std::cerr << "dynemu: invalid FL magic check" << std::endl;
return false;
}
fl.data = std::make_unique<FlashPartition[]>(fl.count);
auto offset = sizeof(fl.magic) + sizeof(fl.count);
for (u32 i = 0; i < fl.count; i++) {
memcpy(&fl.data.get()[i].start_vaddr, fls + offset, sizeof(fl.data.get()[i].start_vaddr));
memcpy(&fl.data.get()[i].size, fls + offset + sizeof(fl.data.get()[i].start_vaddr), sizeof(fl.data.get()[i].size));
fl.data.get()[i].data = std::make_unique<u8[]>(fl.data.get()[i].size);
memcpy((char *)fl.data.get()[i].data.get(), fls + offset + sizeof(fl.data.get()[i].start_vaddr) + sizeof(fl.data.get()[i].size), fl.data.get()[i].size);
offset += sizeof(fl.data.get()[i].start_vaddr) + sizeof(fl.data.get()[i].size) + fl.data.get()[i].size;
CreateMemoryMap(fl.data.get()[i].start_vaddr, ALIGN_BLOCK(fl.data.get()[i].size));
MemoryWriteBytes(fl.data.get()[i].start_vaddr, fl.data.get()[i].data.get(), fl.data.get()[i].size);
}
return true;
}
void MyEmulator::UploadOSStub(u32 dest, u32 mem_start, u32 mem_size) {
if (big_endian) {
MemoryWriteBytes(dest, OS_LoopLoader_BE.data(), (u32)ALIGN_BLOCK(OS_LoopLoader_BE.size()));
} else {
MemoryWriteBytes(dest, OS_LoopLoader.data(), (u32)ALIGN_BLOCK(OS_LoopLoader.size()));
}
MemoryWrite32(dest + MEM_INIT_REG_OFFSET, mem_start);
MemoryWrite32(dest + MEM_INIT_REG_SIZE, mem_size);
}
void MyEmulator::CreateMMIO(u32 addr_start, u32 addr_size, const MMIO_Read &read, const MMIO_Write &write) {
ASSERT_MSG(find_memory_map_by_vaddr(addr_start) == nullptr, "Cannot create MMIO map on memory-mapped address");
if (find_mmio_by_vaddr(addr_start) == nullptr)
mmio_map[mmio_index++] = {addr_start, addr_size, read, write};
}
bool MyEmulator::PreCodeReadHook(bool is_thumb, u32 pc, Dynarmic::A32::IREmitter& ir) {
#if DEBUG
std::cout << fmt::format("executing at {:#010x}", pc) << std::endl;
std::cout << fmt::format("REG P0 {:#010x}", cpu->Regs()[0]) << std::endl;
std::cout << fmt::format("REG P1 {:#010x}", cpu->Regs()[1]) << std::endl;
std::cout << fmt::format("REG P2 {:#010x}", cpu->Regs()[2]) << std::endl;
std::cout << fmt::format("REG P12 {:#010x}", cpu->Regs()[12]) << std::endl;
std::cout << fmt::format("REG P15 {:#010x}", cpu->Regs()[15]) << std::endl;
#endif
thumb_mode = is_thumb;
if (return_mode && (pc >= 0xfffffffc || pc <= 0xffffffff)) {
cpu->HaltExecution(Dynarmic::HaltReason::Step);
ir.SetTerm(Dynarmic::IR::Term::CheckHalt{Dynarmic::IR::Term::ReturnToDispatch{}});
return false;
}
return true;
}
std::optional<u32> MyEmulator::MemoryReadCode(u32 vaddr, bool is_thumb) {
if (!check_memory_access(vaddr, 4)) {
std::cerr << fmt::format("dynemu: Invalid memory fetch at {:#010x}", vaddr) << std::endl;
return std::nullopt;
}
u32 temp;
if (auto ret = handle_mmio_read(vaddr, 4); ret.has_value()) {
temp = ret.value();
} else {
temp = MemoryRead32(vaddr);
}
if (big_endian && is_thumb) {
u32 temp_t_be;
temp_t_be = (temp >> 16) & 0xff;
temp_t_be |= ((temp >> 24) & 0xff) << 8;
temp_t_be |= (temp & 0xff) << 16;
temp_t_be |= ((temp >> 8) & 0xff) << 24;
return temp_t_be;
}
return temp;
}
u8 MyEmulator::MemoryRead8(u32 vaddr) {
if (!check_memory_access(vaddr, 1)) {
std::cerr << fmt::format("dynemu: Invalid memory read at {:#010x}", vaddr) << std::endl;
cpu->HaltExecution(Dynarmic::HaltReason::MemoryAbort);
return -1; // Undefined behavior can occur
}
#if DEBUG
if (cpu->IsExecuting()) printf("Reading at 0x%08x\n", vaddr);
#endif
if (auto ret = handle_mmio_read(vaddr, 1); ret.has_value())
return ret.value();
auto mmap = find_memory_map_by_vaddr(vaddr);
return mmap->data[vaddr - mmap->start];
}
u16 MyEmulator::MemoryRead16(u32 vaddr) {
if (!check_memory_access(vaddr, 2)) {
std::cerr << fmt::format("dynemu: Invalid memory read at {:#010x}", vaddr) << std::endl;
cpu->HaltExecution(Dynarmic::HaltReason::MemoryAbort);
return -1; // Undefined behavior can occur
}
if (auto ret = handle_mmio_read(vaddr, 2); ret.has_value())
return ret.value();
u16 temp;
if (big_endian) {
temp = MemoryRead8(vaddr + 1);
temp |= MemoryRead8(vaddr) << 8;
} else {
temp = MemoryRead8(vaddr);
temp |= MemoryRead8(vaddr + 1) << 8;
}
return temp;
}
u32 MyEmulator::MemoryRead32(u32 vaddr) {
if (!check_memory_access(vaddr, 4)) {
std::cerr << fmt::format("dynemu: Invalid memory read at {:#010x}", vaddr) << std::endl;
cpu->HaltExecution(Dynarmic::HaltReason::MemoryAbort);
return -1; // Undefined behavior can occur
}
if (auto ret = handle_mmio_read(vaddr, 4); ret.has_value())
return ret.value();
u32 temp;
if (big_endian) {
temp = MemoryRead8(vaddr + 3);
temp |= MemoryRead8(vaddr + 2) << 8;
temp |= MemoryRead8(vaddr + 1) << 16;
temp |= MemoryRead8(vaddr) << 24;
} else {
temp = MemoryRead8(vaddr);
temp |= MemoryRead8(vaddr + 1) << 8;
temp |= MemoryRead8(vaddr + 2) << 16;
temp |= MemoryRead8(vaddr + 3) << 24;
}
return temp;
}
u64 MyEmulator::MemoryRead64(u32 vaddr) {
ASSERT_FALSE("target is not a 64-bit device");
}
void MyEmulator::MemoryWrite8(u32 vaddr, u8 value) {
if (!check_memory_access(vaddr, 1)) {
std::cerr << fmt::format("dynemu: Invalid memory write at {:#010x}", vaddr) << std::endl;
cpu->HaltExecution(Dynarmic::HaltReason::MemoryAbort);
return; // Undefined behavior can occur
}
#if DEBUG
if (cpu->IsExecuting()) printf("Writing at 0x%08x 0x%02x\n", vaddr, value);
#endif
if (handle_mmio_write(vaddr, value, 1))
return;
auto mmap = find_memory_map_by_vaddr(vaddr);
mmap->data[vaddr - mmap->start] = value;
}
void MyEmulator::MemoryWrite16(u32 vaddr, u16 value) {
if (!check_memory_access(vaddr, 2)) {
std::cerr << fmt::format("dynemu: Invalid memory write at {:#010x}", vaddr) << std::endl;
cpu->HaltExecution(Dynarmic::HaltReason::MemoryAbort);
return; // Undefined behavior can occur
}
if (handle_mmio_write(vaddr, value, 2))
return;
if (big_endian) {
MemoryWrite8(vaddr, value >> 8);
MemoryWrite8(vaddr + 1, value);
} else {
MemoryWrite8(vaddr, value);
MemoryWrite8(vaddr + 1, value >> 8);
}
}
void MyEmulator::MemoryWrite32(u32 vaddr, u32 value) {
if (!check_memory_access(vaddr, 4)) {
std::cerr << fmt::format("dynemu: Invalid memory write at {:#010x}", vaddr) << std::endl;
cpu->HaltExecution(Dynarmic::HaltReason::MemoryAbort);
return; // Undefined behavior can occur
}
if (handle_mmio_write(vaddr, value, 4))
return;
if (big_endian) {
MemoryWrite8(vaddr, value >> 24);
MemoryWrite8(vaddr + 1, value >> 16);
MemoryWrite8(vaddr + 2, value >> 8);
MemoryWrite8(vaddr + 3, value);
} else {
MemoryWrite8(vaddr, value);
MemoryWrite8(vaddr + 1, value >> 8);
MemoryWrite8(vaddr + 2, value >> 16);
MemoryWrite8(vaddr + 3, value >> 24);
}
}
void MyEmulator::MemoryWrite64(u32 vaddr, u64 value) {
ASSERT_FALSE("target is not a 64-bit device");
}
void MyEmulator::MemoryReadBytes(u32 vaddr, u8 *output, u32 size) {
ASSERT_MSG(find_mmio_by_vaddr(vaddr) == nullptr, "You're trying to read a MMIO mapped address. Use coresponding MMIO routines instead.");
ASSERT_MSG(check_memory_access(vaddr, size), "Invalid memory read");
auto mmap = find_memory_map_by_vaddr(vaddr);
memcpy(output, &mmap->data[vaddr - mmap->start], size);
}
void MyEmulator::MemoryWriteBytes(u32 vaddr, const u8 *input, u32 size) {
ASSERT_MSG(find_mmio_by_vaddr(vaddr) == nullptr, "You're trying to write a MMIO mapped address. Use coresponding MMIO routines instead.");
ASSERT_MSG(check_memory_access(vaddr, size), "Invalid memory write");
auto mmap = find_memory_map_by_vaddr(vaddr);
memcpy(&mmap->data[vaddr - mmap->start], input, size);
}
u32 MyEmulator::Execute(u32 pc, bool return_mode) {
if (return_mode)
cpu->Regs()[14] = 0xfffffffc;
this->return_mode = return_mode;
cpu->Regs()[15] = pc & ~1;
cpu->SetCpsr((pc & 1) ? 0x30 : 0x1d0);
Dynarmic::HaltReason returnCode = cpu->Run();
cpu->ClearCache();
if (returnCode == Dynarmic::HaltReason::MemoryAbort)
throw std::runtime_error("attempted to access outside memory bounds");
return cpu->Regs()[0];
}
std::future<u32> MyEmulator::Spawn(u32 pc, bool return_mode) {
return std::async(std::launch::async, [this, pc, return_mode] {
return Execute(pc, return_mode);
});
}
std::optional<Callback> CP14::CompileInternalOperation(bool two, unsigned opc1, CoprocReg CRd, CoprocReg CRn, CoprocReg CRm, unsigned opc2) {
return std::nullopt;
}
// as MCR (Write Coprocessor), when output is u32 pointer, write to data
CallbackOrAccessOneWord CP14::CompileSendOneWord(bool two, unsigned opc1, CoprocReg CRn, CoprocReg CRm, unsigned opc2) {
// const std::lock_guard<std::mutex> lock(dcc_lock);
switch (state.dcc_type) {
case DCCType::ARM7_ARM9:
if (!two && opc1 == 0 && CRm == CoprocReg::C0 && opc2 == 0) {
switch (CRn) {
case CoprocReg::C0:
return &state.dcc_status;
case CoprocReg::C1:
return Callback{
[](void* cp14_state, std::uint32_t write_value, std::uint32_t word2_unused) -> std::uint64_t {
CP14State& state = *reinterpret_cast<CP14State*>(cp14_state);
#if DEBUG
printf("DEBUG WRITE: 0x%08x\n", write_value);
#endif
state.dcc_write_data = write_value;
state.dcc_status |= 0b10;
return 0;
},
reinterpret_cast<void*>(&state),
};
default:
break;
}
}
break;
case DCCType::ARM11_CORTEX:
if (!two && opc1 == 0 && CRn == CoprocReg::C0 && opc2 == 2) {
switch (CRm) {
case CoprocReg::C0:
return &dummy_value;
case CoprocReg::C1:
return &dummy_value;
case CoprocReg::C2:
return &state.dcc_status;
case CoprocReg::C3:
return Callback{
[](void* cp14_state, std::uint32_t write_value, std::uint32_t word2_unused) -> std::uint64_t {
CP14State& state = *reinterpret_cast<CP14State*>(cp14_state);
#if DEBUG
printf("DEBUG WRITE: 0x%08x\n", write_value);
#endif
state.dcc_write_data = write_value;
state.dcc_status |= (0b01 << 29);
return 0;
},
reinterpret_cast<void*>(&state),
};
default:
break;
}
}
break;
}
std::cerr << "dynemu: something is wrong with CP14 state" << std::endl;
return CallbackOrAccessOneWord{};
}
CallbackOrAccessTwoWords CP14::CompileSendTwoWords(bool two, unsigned opc, CoprocReg CRm) {
return CallbackOrAccessTwoWords{};
}
// as MRC (Read Coprocessor), when output is u32 pointer, read from data
CallbackOrAccessOneWord CP14::CompileGetOneWord(bool two, unsigned opc1, CoprocReg CRn, CoprocReg CRm, unsigned opc2) {
// const std::lock_guard<std::mutex> lock(dcc_lock);
switch (state.dcc_type) {
case DCCType::ARM7_ARM9:
if (!two && opc1 == 0 && CRm == CoprocReg::C0 && opc2 == 0) {
switch (CRn) {
case CoprocReg::C0:
return &state.dcc_status;
case CoprocReg::C1:
return Callback{
[](void* cp14_state, std::uint32_t word1_unused, std::uint32_t word2_unused) -> std::uint64_t {
CP14State& state = *reinterpret_cast<CP14State*>(cp14_state);
#if DEBUG
printf("DEBUG READ: 0x%08x\n", state.dcc_read_data);
#endif
u32 temp = state.dcc_read_data;
state.dcc_status &= ~0b01;
return temp;
},
reinterpret_cast<void*>(&state),
};
default:
break;
}
}
break;
case DCCType::ARM11_CORTEX:
if (!two && opc1 == 0 && CRn == CoprocReg::C0 && opc2 == 2) {
switch (CRm) {
case CoprocReg::C0:
return Callback{
[](void* cp14_state, std::uint32_t word1_unused, std::uint32_t word2_unused) -> std::uint64_t {
CP14State& state = *reinterpret_cast<CP14State*>(cp14_state);
#if DEBUG
printf("DEBUG READ: 0x%08x\n", state.dcc_read_data);
#endif
u32 temp = state.dcc_read_data;
state.dcc_status &= ~(0b10 << 29);
return temp;
},
reinterpret_cast<void*>(&state),
};
case CoprocReg::C1:
return &dummy_value;
case CoprocReg::C2:
return &state.dcc_status;
case CoprocReg::C3:
return &dummy_value;
default:
break;
}
}
break;
}
std::cerr << "dynemu: something is wrong with CP14 state" << std::endl;
return CallbackOrAccessOneWord{};
}
CallbackOrAccessTwoWords CP14::CompileGetTwoWords(bool two, unsigned opc, CoprocReg CRm) {
return CallbackOrAccessTwoWords{};
}
std::optional<Callback> CP14::CompileLoadWords(bool two, bool long_transfer, CoprocReg CRd, std::optional<std::uint8_t> option) {
return std::nullopt;
}
std::optional<Callback> CP14::CompileStoreWords(bool two, bool long_transfer, CoprocReg CRd, std::optional<std::uint8_t> option) {
return std::nullopt;
}
CP14::CP14(DCCType dcc_type, CP14State &state) : state(state) {
switch (dcc_type) {
case DCCType::ARM7_ARM9:
state.dcc_status = 0b01;
break;
case DCCType::ARM11_CORTEX:
state.dcc_status = 0b10 << 29;
break;
}
state.dcc_type = dcc_type;
state.dcc_write_data = 0;
state.dcc_read_data = 0;
}
void CP14::Send(u32 value) {
const std::lock_guard<std::mutex> lock(dcc_lock);
switch (state.dcc_type) {
case DCCType::ARM7_ARM9:
while (state.dcc_status & 0b01);
state.dcc_read_data = value;
state.dcc_status |= 0b01;
break;
case DCCType::ARM11_CORTEX:
while (state.dcc_status & (0b10 << 29));
state.dcc_read_data = value;
state.dcc_status |= (0b10 << 29);
break;
}
}
u32 CP14::Get() {
const std::lock_guard<std::mutex> lock(dcc_lock);
u32 temp;
switch (state.dcc_type) {
case DCCType::ARM7_ARM9:
while (!(state.dcc_status & 0b10));
temp = state.dcc_write_data;
state.dcc_status &= ~0b10;
break;
case DCCType::ARM11_CORTEX:
while (!(state.dcc_status & (0b01 << 29)));
temp = state.dcc_write_data;
state.dcc_status &= ~(0b01 << 29);
break;
}
return temp;
}
class CP15 final : public Dynarmic::A32::Coprocessor {
using Callback = Dynarmic::A32::Coprocessor::Callback;
using CoprocReg = Dynarmic::A32::CoprocReg;
using CallbackOrAccessOneWord = Dynarmic::A32::Coprocessor::CallbackOrAccessOneWord;
using CallbackOrAccessTwoWords = Dynarmic::A32::Coprocessor::CallbackOrAccessTwoWords;
u32 threadStoragePointer; // Pointer to thread-local storage
std::optional<Callback> CompileInternalOperation(bool two, unsigned opc1, CoprocReg CRd,
CoprocReg CRn, CoprocReg CRm,
unsigned opc2) override {
return std::nullopt;
}
CallbackOrAccessOneWord CompileSendOneWord(bool two, unsigned opc1, CoprocReg CRn,
CoprocReg CRm, unsigned opc2) override {
return CallbackOrAccessOneWord{};
}
CallbackOrAccessTwoWords CompileSendTwoWords(bool two, unsigned opc, CoprocReg CRm) override {
return CallbackOrAccessTwoWords{};
}
CallbackOrAccessOneWord CompileGetOneWord(bool two, unsigned opc1, CoprocReg CRn,
CoprocReg CRm, unsigned opc2) override {
// Stores a pointer to thread-local storage, accessed via mrc p15, 0, rd, c13, c0, 3
if (!two && CRn == CoprocReg::C13 && opc1 == 0 && CRm == CoprocReg::C0 && opc2 == 3) {
return &threadStoragePointer;
}
return CallbackOrAccessOneWord{};
}
CallbackOrAccessTwoWords CompileGetTwoWords(bool two, unsigned opc, CoprocReg CRm) override {
return CallbackOrAccessTwoWords{};
}
std::optional<Callback> CompileLoadWords(bool two, bool long_transfer, CoprocReg CRd,
std::optional<std::uint8_t> option) override {
return std::nullopt;
}
std::optional<Callback> CompileStoreWords(bool two, bool long_transfer, CoprocReg CRd,
std::optional<std::uint8_t> option) override {
return std::nullopt;
}
public:
void setTLSBase(u32 value) {
threadStoragePointer = value;
}
};
Emulator::Emulator(DCCType dcc_type, bool no_opts, bool big_endian) : env(big_endian) {
Dynarmic::A32::UserConfig user_config;
cp14 = std::make_shared<CP14>(dcc_type, cp14_state);
user_config.enable_cycle_counting = false;
if (no_opts)
user_config.optimizations = Dynarmic::no_optimizations;
user_config.callbacks = &env;
user_config.check_halt_on_memory_access = true;
user_config.coprocessors[14] = cp14;
user_config.coprocessors[15] = std::make_shared<CP15>();
user_config.arch_version = Dynarmic::A32::ArchVersion::v7;
mon = std::make_unique<Dynarmic::ExclusiveMonitor>(1);
user_config.global_monitor = mon.get();
cpu = std::make_unique<Dynarmic::A32::Jit>(user_config);
env.cpu = cpu.get();
};
}