no need for os_address, map find returns pointer

This commit is contained in:
wrapper 2026-03-29 21:05:41 +07:00
parent fea76d7347
commit ae9d34b3d8
6 changed files with 108 additions and 139 deletions

View file

@ -24,7 +24,7 @@ class Dynemu:
...
def dcc_write(self, value: typing.SupportsInt | typing.SupportsIndex) -> None:
...
def execute(self, pc: typing.SupportsInt | typing.SupportsIndex, stub_mode: bool = True) -> int:
def execute(self, pc: typing.SupportsInt | typing.SupportsIndex, return_mode: bool = True) -> int:
...
def get_reg(self, reg_num: typing.SupportsInt | typing.SupportsIndex) -> int:
...
@ -67,11 +67,7 @@ class Dynemu:
@typing.overload
def upload_fls_memory(self, data: bytearray) -> None:
...
@typing.overload
def upload_memstub(self, vaddr: typing.SupportsInt | typing.SupportsIndex, mem_start: typing.SupportsInt | typing.SupportsIndex = 2684354560, mem_size: typing.SupportsInt | typing.SupportsIndex = 33554432) -> None:
...
@typing.overload
def upload_memstub(self, mem_start: typing.SupportsInt | typing.SupportsIndex = 2684354560, mem_size: typing.SupportsInt | typing.SupportsIndex = 33554432) -> None:
def upload_memstub(self, vaddr: typing.SupportsInt | typing.SupportsIndex, mem_start: typing.SupportsInt | typing.SupportsIndex, mem_size: typing.SupportsInt | typing.SupportsIndex) -> None:
...
@typing.overload
def write_bytes(self, vaddr: typing.SupportsInt | typing.SupportsIndex, data: bytes) -> None:

View file

@ -36,7 +36,6 @@ extern "C" {
DYNEMU_EXPORT size_t dynemu_upload_file(Emulator *e, const char *filename, int64_t dest, u32 size);
DYNEMU_EXPORT bool dynemu_upload_fls_memory(Emulator *e, const u8 *fls);
DYNEMU_EXPORT void dynemu_upload_memstub(Emulator *e, u32 dest, u32 mem_start, u32 mem_size);
DYNEMU_EXPORT void dynemu_upload_memstub_nooffset(Emulator *e, u32 mem_start, u32 mem_size);
DYNEMU_EXPORT void dynemu_mmio(Emulator *e, u32 vaddr, u32 size, DY_MMIO_Read read, DY_MMIO_Write write);
DYNEMU_EXPORT u8 dynemu_read_u8(Emulator *e, u32 vaddr);
DYNEMU_EXPORT u16 dynemu_read_u16(Emulator *e, u32 vaddr);
@ -48,7 +47,7 @@ extern "C" {
DYNEMU_EXPORT void dynemu_write_u64(Emulator *e, u32 vaddr, u64 value);
DYNEMU_EXPORT void dynemu_read_bytes(Emulator *e, u32 vaddr, u8 *output, u32 size);
DYNEMU_EXPORT void dynemu_write_bytes(Emulator *e, u32 vaddr, const u8 *input, u32 size);
DYNEMU_EXPORT u32 dynemu_execute(Emulator *e, u32 pc, bool stub_mode);
DYNEMU_EXPORT u32 dynemu_execute(Emulator *e, u32 pc, bool return_mode);
DYNEMU_EXPORT u32 dynemu_read_reg(Emulator *e, u8 reg);
DYNEMU_EXPORT void dynemu_write_reg(Emulator *e, u8 reg, u32 value);
#ifdef __cplusplus

View file

@ -39,7 +39,7 @@ namespace Dynemu {
struct MemMap final {
u32 start;
u32 size;
std::unique_ptr<u8[]> data;
std::vector<u8> data;
};
struct MMIO final {
@ -52,37 +52,36 @@ namespace Dynemu {
class DYNEMU_EXPORT MyEmulator final : public Dynarmic::A32::UserCallbacks {
private:
/* Memory map */
std::optional<u8> find_memory_map_id(u32 vaddr);
MemMap * find_memory_map_by_vaddr(u32 vaddr);
/* MMIO */
std::optional<u8> find_mmio_id(u32 vaddr);
MMIO * find_mmio_by_vaddr(u32 vaddr);
std::optional<u32> handle_mmio_read(u32 vaddr, u8 access);
bool handle_mmio_write(u32 vaddr, u32 value, u8 access);
/* Misc */
bool check_memory_access(u32 vaddr);
bool check_memory_access(u32 vaddr, u32 size);
bool big_endian;
bool thumb_mode;
bool stub_mode;
bool return_mode;
u8 mem_map_index;
u8 mmio_index;
public:
/* Memory map */
std::array<MemMap, 64> mem_map;
u8 mem_map_index;
/* MMIO */
std::array<MMIO, 64> mmio_map;
u8 mmio_index;
/* state */
Dynarmic::A32::Jit *cpu;
u32 os_offset;
/*
MyEmulator()
Constructor
*/
MyEmulator(bool big_endian = false) : mem_map_index(0), mmio_index(0), os_offset(0xa8000000), mem_map(), mmio_map(), big_endian(big_endian) {};
MyEmulator(bool big_endian = false) : mem_map_index(0), mmio_index(0), mem_map(), mmio_map(), big_endian(big_endian) {};
/*
void CreateMemoryMap(u32 vaddr, u32 size)
@ -140,15 +139,6 @@ namespace Dynemu {
*/
void UploadOSStub(u32 dest, u32 mem_start, u32 mem_size);
/*
void UploadOSStub(u32 mem_start, u32 mem_size)
This function uploads a stub libc to the configured os_offset parameter virtual address, along with setting up the regions assosiated with the dynamic memory functions.
Endianness were determined by big_endian parameter.
*/
void UploadOSStub(u32 mem_start, u32 mem_size) {
UploadOSStub(os_offset, mem_start, mem_size);
}
/*
void CreateMMIO(u32 addr_start, u32 addr_size, const MMIO_Read &read, const MMIO_Write &write)
This function maps the virtual address to specific functions.
@ -290,18 +280,18 @@ namespace Dynemu {
}
/*
u32 Execute(u32 pc, bool stub_mode)
u32 Execute(u32 pc, bool return_mode)
Executes from specific PC
Returns: R0 register
*/
u32 Execute(u32 pc, bool stub_mode=true);
u32 Execute(u32 pc, bool return_mode=true);
/*
std::future<u32> Spawn(u32 pc, bool stub_mode)
std::future<u32> Spawn(u32 pc, bool return_mode)
Async version of Execute
Returns: Future(R0 register)
*/
std::future<u32> Spawn(u32 pc, bool stub_mode=true);
std::future<u32> Spawn(u32 pc, bool return_mode=true);
/*
void ExceptionRaised(u32 pc, Dynarmic::A32::Exception exception)

View file

@ -35,10 +35,6 @@ extern "C" {
return e->env.UploadOSStub(dest, mem_start, mem_size);
}
void dynemu_upload_memstub_nooffset(Emulator *e, u32 mem_start, u32 mem_size) {
return e->env.UploadOSStub(mem_start, mem_size);
}
u8 dynemu_read_u8(Emulator *e, u32 vaddr) {
return e->env.MemoryRead8(vaddr);
}
@ -79,8 +75,8 @@ extern "C" {
e->env.MemoryWriteBytes(vaddr, input, size);
}
u32 dynemu_execute(Emulator *e, u32 pc, bool stub_mode) {
return e->env.Execute(pc, stub_mode);
u32 dynemu_execute(Emulator *e, u32 pc, bool return_mode) {
return e->env.Execute(pc, return_mode);
}
u32 dynemu_read_reg(Emulator *e, u8 reg) {

View file

@ -35,52 +35,64 @@ namespace Dynemu {
};
/* 01 - Private */
std::optional<u8> MyEmulator::find_memory_map_id(u32 vaddr) {
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 i;
return &mem_map[i];
}
return std::nullopt;
return nullptr;
}
std::optional<u8> MyEmulator::find_mmio_id(u32 vaddr) {
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 i;
return &mmio_map[i];
}
return std::nullopt;
return nullptr;
}
std::optional<u32> MyEmulator::handle_mmio_read(u32 vaddr, u8 access) {
if (auto id = find_mmio_id(vaddr); id.has_value()) {
if (auto mmio = find_mmio_by_vaddr(vaddr); mmio != nullptr) {
#if DEBUG
printf("Handling MMIO READ for 0x%08x\n", vaddr);
#endif
return mmio_map[id.value()].read(vaddr - mmio_map[id.value()].start, access);
return mmio->read(vaddr - mmio->start, access);
}
return std::nullopt;
}
bool MyEmulator::handle_mmio_write(u32 vaddr, u32 value, u8 access) {
if (auto id = find_mmio_id(vaddr); id.has_value()) {
if (auto mmio = find_mmio_by_vaddr(vaddr); mmio != nullptr) {
#if DEBUG
printf("Handling MMIO WRITE for 0x%08x\n", vaddr);
#endif
mmio_map[id.value()].write(vaddr - mmio_map[id.value()].start, value, access);
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_id(vaddr).has_value(), "Cannot create memory map on MMIO-mapped addresses");
if (!find_memory_map_id(vaddr).has_value())
mem_map[mem_map_index++] = {vaddr, size, std::make_unique<u8[]>(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) {
@ -99,13 +111,8 @@ namespace Dynemu {
size_t fileSize = file.tellg();
file.seekg(0, file.beg);
auto mmap_id = find_memory_map_id((u32)dest);
bool fitsOnMemoryMap = !mmap_id.has_value() ? (size >= fileSize) : false;
if (!fitsOnMemoryMap && mmap_id.has_value()) {
u32 max_range = mem_map[mmap_id.value()].start + mem_map[mmap_id.value()].size;
fitsOnMemoryMap = (max_range - dest) >= fileSize;
}
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;
@ -130,7 +137,7 @@ namespace Dynemu {
file.read((char *)&fl.count, sizeof(fl.count));
if (fl.magic != 0x4c46) {
std::cerr << "dynemu: nvalid FL magic check" << std::endl;
std::cerr << "dynemu: invalid FL magic check" << std::endl;
return 0;
}
@ -159,7 +166,7 @@ namespace Dynemu {
memcpy(&fl.count, fls + sizeof(fl.magic), sizeof(fl.count));
if (fl.magic != 0x4c46) {
std::cerr << "Invalid FL magic check" << std::endl;
std::cerr << "dynemu: invalid FL magic check" << std::endl;
return false;
}
@ -191,13 +198,11 @@ namespace Dynemu {
MemoryWrite32(dest + MEM_INIT_REG_OFFSET, mem_start);
MemoryWrite32(dest + MEM_INIT_REG_SIZE, mem_size);
os_offset = dest;
}
void MyEmulator::CreateMMIO(u32 addr_start, u32 addr_size, const MMIO_Read &read, const MMIO_Write &write) {
ASSERT_MSG(!find_memory_map_id(addr_start).has_value(), "Cannot create MMIO map on memory-mapped address");
if (!find_mmio_id(addr_start).has_value())
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};
}
@ -213,7 +218,7 @@ namespace Dynemu {
thumb_mode = is_thumb;
if (stub_mode && pc == (os_offset + ROUTE_EXIT)) {
if (return_mode && pc == 0xfffffffc) {
cpu->HaltExecution(Dynarmic::HaltReason::Step);
ir.SetTerm(Dynarmic::IR::Term::CheckHalt{Dynarmic::IR::Term::ReturnToDispatch{}});
return false;
@ -223,16 +228,16 @@ namespace Dynemu {
}
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()) {
return ret.value();
} else {
if (!find_memory_map_id(vaddr).has_value()) {
std::cerr << fmt::format("dynemu: Invalid memory fetch at {:#010x}", vaddr) << std::endl;
return std::nullopt;
}
if (big_endian && is_thumb) {
temp = MemoryRead8(vaddr + 1);
temp |= MemoryRead8(vaddr) << 8;
@ -247,34 +252,33 @@ namespace Dynemu {
}
u8 MyEmulator::MemoryRead8(u32 vaddr) {
if (auto ret = handle_mmio_read(vaddr, 1); ret.has_value())
return ret.value();
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
auto mmap = find_memory_map_id(vaddr);
if (!mmap.has_value()) {
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, 1); ret.has_value())
return ret.value();
return mem_map[mmap.value()].data.get()[vaddr - mem_map[mmap.value()].start];
auto mmap = find_memory_map_by_vaddr(vaddr);
return mmap->data[vaddr - mmap->start];
}
u16 MyEmulator::MemoryRead16(u32 vaddr) {
if (auto ret = handle_mmio_read(vaddr, 2); ret.has_value())
return ret.value();
auto mmap = find_memory_map_id(vaddr);
if (!mmap.has_value()) {
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) {
@ -289,16 +293,15 @@ namespace Dynemu {
}
u32 MyEmulator::MemoryRead32(u32 vaddr) {
if (auto ret = handle_mmio_read(vaddr, 4); ret.has_value())
return ret.value();
auto mmap = find_memory_map_id(vaddr);
if (!mmap.has_value()) {
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) {
@ -321,34 +324,33 @@ namespace Dynemu {
}
void MyEmulator::MemoryWrite8(u32 vaddr, u8 value) {
if (handle_mmio_write(vaddr, value, 1))
return;
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
auto mmap = find_memory_map_id(vaddr);
if (!mmap.has_value()) {
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, 1))
return;
mem_map[mmap.value()].data.get()[vaddr - mem_map[mmap.value()].start] = value;
auto mmap = find_memory_map_by_vaddr(vaddr);
mmap->data[vaddr - mmap->start] = value;
}
void MyEmulator::MemoryWrite16(u32 vaddr, u16 value) {
if (handle_mmio_write(vaddr, value, 2))
return;
auto mmap = find_memory_map_id(vaddr);
if (!mmap.has_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);
@ -359,16 +361,15 @@ namespace Dynemu {
}
void MyEmulator::MemoryWrite32(u32 vaddr, u32 value) {
if (handle_mmio_write(vaddr, value, 4))
return;
auto mmap = find_memory_map_id(vaddr);
if (!mmap.has_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);
@ -387,35 +388,27 @@ namespace Dynemu {
}
void MyEmulator::MemoryReadBytes(u32 vaddr, u8 *output, u32 size) {
ASSERT_MSG(!find_mmio_id(vaddr).has_value(), "You're trying to read a MMIO mapped address. Use coresponding MMIO routines instead.");
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_id(vaddr);
ASSERT_MSG(mmap.has_value(), "Invalid memory read");
u32 maddr = vaddr - mem_map[mmap.value()].start;
ASSERT_MSG((maddr + size) <= mem_map[mmap.value()].size, "Invalid memory read");
memcpy(output, mem_map[mmap.value()].data.get() + maddr, size);
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_id(vaddr).has_value(), "You're trying to write a MMIO mapped address. Use coresponding MMIO routines instead.");
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_id(vaddr);
ASSERT_MSG(mmap.has_value(), "Invalid memory write");
u32 maddr = vaddr - mem_map[mmap.value()].start;
ASSERT_MSG((maddr + size) <= mem_map[mmap.value()].size, "Invalid memory write");
memcpy(mem_map[mmap.value()].data.get() + maddr, input, size);
auto mmap = find_memory_map_by_vaddr(vaddr);
memcpy(&mmap->data[vaddr - mmap->start], input, size);
}
u32 MyEmulator::Execute(u32 pc, bool stub_mode) {
if (stub_mode)
cpu->Regs()[14] = os_offset + ROUTE_EXIT;
u32 MyEmulator::Execute(u32 pc, bool return_mode) {
if (return_mode)
cpu->Regs()[14] = 0xfffffffc;
this->stub_mode = stub_mode;
this->return_mode = return_mode;
cpu->Regs()[15] = pc & ~1;
cpu->SetCpsr((pc & 1) ? 0x30 : 0x1d0);
@ -428,9 +421,9 @@ namespace Dynemu {
return cpu->Regs()[0];
}
std::future<u32> MyEmulator::Spawn(u32 pc, bool stub_mode) {
return std::async(std::launch::async, [this, pc, stub_mode] {
return Execute(pc, stub_mode);
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);
});
}

View file

@ -28,14 +28,10 @@ struct Emu {
emu.env.UploadFLSFromBytes((const u8 *)info.ptr);
}
void upload_memstub(u32 vaddr, u32 mem_start = 0xa0000000, u32 mem_size = 0x2000000) {
void upload_memstub(u32 vaddr, u32 mem_start, u32 mem_size) {
emu.env.UploadOSStub(vaddr, mem_start, mem_size);
}
void upload_memstub(u32 mem_start = 0xa0000000, u32 mem_size = 0x2000000) {
emu.env.UploadOSStub(mem_start, mem_size);
}
void mmio(u32 vaddr, u32 size, const MMIO_Read &read, const MMIO_Write &write) {
emu.env.CreateMMIO(vaddr, size, read, write);
}
@ -109,8 +105,8 @@ struct Emu {
emu.env.MemoryWriteBytes(vaddr, (const u8 *)info.ptr, info.size);
}
u32 execute(u32 pc, bool stub_mode=true) {
return emu.env.Execute(pc, stub_mode);
u32 execute(u32 pc, bool return_mode=true) {
return emu.env.Execute(pc, return_mode);
}
void halt() {
@ -152,8 +148,7 @@ PYBIND11_MODULE(_dynemu, module, py::mod_gil_not_used()) {
.def("upload_file", &Emu::upload_file, py::arg("filename"), py::arg("vaddr"), py::arg("size"))
.def("upload_fls_memory", static_cast<void (Emu::*)(py::bytes)>(&Emu::upload_fls_memory), py::arg("data"))
.def("upload_fls_memory", static_cast<void (Emu::*)(py::bytearray)>(&Emu::upload_fls_memory), py::arg("data"))
.def("upload_memstub", static_cast<void (Emu::*)(u32, u32, u32)>(&Emu::upload_memstub), py::arg("vaddr"), py::arg("mem_start") = 0xa0000000, py::arg("mem_size") = 0x2000000)
.def("upload_memstub", static_cast<void (Emu::*)(u32, u32)>(&Emu::upload_memstub), py::arg("mem_start") = 0xa0000000, py::arg("mem_size") = 0x2000000)
.def("upload_memstub", &Emu::upload_memstub, py::arg("vaddr"), py::arg("mem_start"), py::arg("mem_size"))
.def("mmio", static_cast<void (Emu::*)(u32, u32)>(&Emu::mmio), py::arg("vaddr"), py::arg("size"))
.def("mmio", static_cast<void (Emu::*)(u32, u32, const MMIO_Read &)>(&Emu::mmio), py::arg("vaddr"), py::arg("size"), py::arg("read"))
.def("mmio", static_cast<void (Emu::*)(u32, u32, const MMIO_Write &)>(&Emu::mmio), py::arg("vaddr"), py::arg("size"), py::arg("write"))
@ -171,7 +166,7 @@ PYBIND11_MODULE(_dynemu, module, py::mod_gil_not_used()) {
.def("read_bytes", &Emu::read_bytes, py::arg("vaddr"), py::arg("size"))
.def("write_bytes", static_cast<void (Emu::*)(u32, py::bytes)>(&Emu::write_bytes), py::arg("vaddr"), py::arg("data"))
.def("write_bytes", static_cast<void (Emu::*)(u32, py::bytearray)>(&Emu::write_bytes), py::arg("vaddr"), py::arg("data"))
.def("execute", &Emu::execute, py::arg("pc"), py::arg("stub_mode") = true)
.def("execute", &Emu::execute, py::arg("pc"), py::arg("return_mode") = true)
.def("halt", &Emu::halt)
.def("get_reg", &Emu::get_reg, py::arg("reg_num"))
.def("set_reg", &Emu::set_reg, py::arg("reg_num"), py::arg("value"))