mmio dcc and pybind
This commit is contained in:
parent
b53587f80d
commit
d76b978242
10 changed files with 965 additions and 258 deletions
|
|
@ -1,5 +1,5 @@
|
|||
cmake_minimum_required(VERSION 3.10.0)
|
||||
project(Dynemu VERSION 0.1.0 LANGUAGES C CXX)
|
||||
project(Dynemu VERSION 0.2.0 LANGUAGES C CXX)
|
||||
|
||||
set(CMAKE_CXX_STANDARD 20)
|
||||
|
||||
|
|
@ -18,6 +18,8 @@ if (DEFINED Dynemu_SHARED_LIBS)
|
|||
set(BUILD_SHARED_LIBS "${Dynemu_SHARED_LIBS}")
|
||||
endif ()
|
||||
|
||||
option(DYNEMU_PYTHON "Python support" ON)
|
||||
|
||||
# 01 - Boost first
|
||||
set(BOOST_INCLUDEDIR "${PROJECT_SOURCE_DIR}/externals/dynarmic/externals/ext-boost")
|
||||
find_package(Boost 1.57 REQUIRED)
|
||||
|
|
|
|||
81
bindings/pybind/dynemup.pyi
Normal file
81
bindings/pybind/dynemup.pyi
Normal file
|
|
@ -0,0 +1,81 @@
|
|||
"""
|
||||
Dynemu
|
||||
"""
|
||||
from __future__ import annotations
|
||||
import collections.abc
|
||||
import enum
|
||||
import typing
|
||||
__all__: list[str] = ['Dynemu']
|
||||
class Dynemu:
|
||||
class DCCType(enum.Enum):
|
||||
ARM11_CORTEX: typing.ClassVar[Dynemu.DCCType] # value = <DCCType.ARM11_CORTEX: 1>
|
||||
ARM7_ARM9: typing.ClassVar[Dynemu.DCCType] # value = <DCCType.ARM7_ARM9: 0>
|
||||
ARM11_CORTEX: typing.ClassVar[Dynemu.DCCType] # value = <DCCType.ARM11_CORTEX: 1>
|
||||
ARM7_ARM9: typing.ClassVar[Dynemu.DCCType] # value = <DCCType.ARM7_ARM9: 0>
|
||||
def __init__(self, dcc_type: Dynemu.DCCType = DCCType.ARM7_ARM9, no_opts: bool = False, big_endian: bool = False) -> None:
|
||||
...
|
||||
def dcc_read(self) -> int:
|
||||
...
|
||||
def dcc_write(self, value: typing.SupportsInt | typing.SupportsIndex) -> None:
|
||||
...
|
||||
def execute(self, pc: typing.SupportsInt | typing.SupportsIndex, stub_mode: bool = True) -> int:
|
||||
...
|
||||
def get_reg(self, reg_num: typing.SupportsInt | typing.SupportsIndex) -> int:
|
||||
...
|
||||
def memory_map(self, vaddr: typing.SupportsInt | typing.SupportsIndex, size: typing.SupportsInt | typing.SupportsIndex) -> None:
|
||||
...
|
||||
@typing.overload
|
||||
def mmio(self, vaddr: typing.SupportsInt | typing.SupportsIndex, size: typing.SupportsInt | typing.SupportsIndex) -> None:
|
||||
...
|
||||
@typing.overload
|
||||
def mmio(self, vaddr: typing.SupportsInt | typing.SupportsIndex, size: typing.SupportsInt | typing.SupportsIndex, read: collections.abc.Callable[[typing.SupportsInt | typing.SupportsIndex, typing.SupportsInt | typing.SupportsIndex], int]) -> None:
|
||||
...
|
||||
@typing.overload
|
||||
def mmio(self, vaddr: typing.SupportsInt | typing.SupportsIndex, size: typing.SupportsInt | typing.SupportsIndex, write: collections.abc.Callable[[typing.SupportsInt | typing.SupportsIndex, typing.SupportsInt | typing.SupportsIndex, typing.SupportsInt | typing.SupportsIndex], None]) -> None:
|
||||
...
|
||||
@typing.overload
|
||||
def mmio(self, vaddr: typing.SupportsInt | typing.SupportsIndex, size: typing.SupportsInt | typing.SupportsIndex, read: collections.abc.Callable[[typing.SupportsInt | typing.SupportsIndex, typing.SupportsInt | typing.SupportsIndex], int], write: collections.abc.Callable[[typing.SupportsInt | typing.SupportsIndex, typing.SupportsInt | typing.SupportsIndex, typing.SupportsInt | typing.SupportsIndex], None]) -> None:
|
||||
...
|
||||
@typing.overload
|
||||
def mmio(self, vaddr: typing.SupportsInt | typing.SupportsIndex, size: typing.SupportsInt | typing.SupportsIndex, write: collections.abc.Callable[[typing.SupportsInt | typing.SupportsIndex, typing.SupportsInt | typing.SupportsIndex, typing.SupportsInt | typing.SupportsIndex], None], read: collections.abc.Callable[[typing.SupportsInt | typing.SupportsIndex, typing.SupportsInt | typing.SupportsIndex], int]) -> None:
|
||||
...
|
||||
def read_bytes(self, vaddr: typing.SupportsInt | typing.SupportsIndex, size: typing.SupportsInt | typing.SupportsIndex) -> bytes:
|
||||
...
|
||||
def read_u16(self, vaddr: typing.SupportsInt | typing.SupportsIndex) -> int:
|
||||
...
|
||||
def read_u32(self, vaddr: typing.SupportsInt | typing.SupportsIndex) -> int:
|
||||
...
|
||||
def read_u64(self, vaddr: typing.SupportsInt | typing.SupportsIndex) -> int:
|
||||
...
|
||||
def read_u8(self, vaddr: typing.SupportsInt | typing.SupportsIndex) -> int:
|
||||
...
|
||||
def set_reg(self, reg_num: typing.SupportsInt | typing.SupportsIndex, value: typing.SupportsInt | typing.SupportsIndex) -> None:
|
||||
...
|
||||
def upload_file(self, filename: str, vaddr: typing.SupportsInt | typing.SupportsIndex, size: typing.SupportsInt | typing.SupportsIndex) -> None:
|
||||
...
|
||||
@typing.overload
|
||||
def upload_fls_memory(self, data: bytes) -> None:
|
||||
...
|
||||
@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:
|
||||
...
|
||||
@typing.overload
|
||||
def write_bytes(self, vaddr: typing.SupportsInt | typing.SupportsIndex, data: bytes) -> None:
|
||||
...
|
||||
@typing.overload
|
||||
def write_bytes(self, vaddr: typing.SupportsInt | typing.SupportsIndex, data: bytearray) -> None:
|
||||
...
|
||||
def write_u16(self, vaddr: typing.SupportsInt | typing.SupportsIndex, value: typing.SupportsInt | typing.SupportsIndex) -> None:
|
||||
...
|
||||
def write_u32(self, vaddr: typing.SupportsInt | typing.SupportsIndex, value: typing.SupportsInt | typing.SupportsIndex) -> None:
|
||||
...
|
||||
def write_u64(self, vaddr: typing.SupportsInt | typing.SupportsIndex, value: typing.SupportsInt | typing.SupportsIndex) -> None:
|
||||
...
|
||||
def write_u8(self, vaddr: typing.SupportsInt | typing.SupportsIndex, value: typing.SupportsInt | typing.SupportsIndex) -> None:
|
||||
...
|
||||
20
externals/CMakeLists.txt
vendored
20
externals/CMakeLists.txt
vendored
|
|
@ -15,9 +15,17 @@ set(BUILD_TESTING OFF)
|
|||
# simply add the directory to that file as a subdirectory
|
||||
# to have CMake automatically recognize them.
|
||||
|
||||
set(DYNARMIC_TESTS OFF CACHE BOOL "")
|
||||
set(DYNARMIC_FRONTENDS "A32;A64" CACHE STRING "")
|
||||
set(DYNARMIC_USE_PRECOMPILED_HEADERS ON CACHE BOOL "")
|
||||
set(DYNARMIC_USE_BUNDLED_EXTERNALS ON CACHE BOOL "")
|
||||
set(DYNARMIC_WARNINGS_AS_ERRORS OFF CACHE BOOL "")
|
||||
add_subdirectory(dynarmic)
|
||||
# Dynarmic
|
||||
|
||||
if (NOT TARGET dynarmic::dynarmic)
|
||||
set(DYNARMIC_TESTS OFF CACHE BOOL "")
|
||||
set(DYNARMIC_FRONTENDS "A32;A64" CACHE STRING "")
|
||||
set(DYNARMIC_USE_PRECOMPILED_HEADERS ON CACHE BOOL "")
|
||||
set(DYNARMIC_USE_BUNDLED_EXTERNALS ON CACHE BOOL "")
|
||||
set(DYNARMIC_WARNINGS_AS_ERRORS OFF CACHE BOOL "")
|
||||
add_subdirectory(dynarmic)
|
||||
endif()
|
||||
|
||||
if (DYNEMU_PYTHON)
|
||||
add_subdirectory(pybind11)
|
||||
endif()
|
||||
|
|
@ -14,6 +14,11 @@ extern "C" {
|
|||
typedef uint32_t u32;
|
||||
typedef uint64_t u64;
|
||||
|
||||
enum DY_DCCType {
|
||||
ARM7_ARM9,
|
||||
ARM11_CORTEX
|
||||
};
|
||||
|
||||
#ifndef __cplusplus
|
||||
typedef uint8_t bool;
|
||||
#endif
|
||||
|
|
@ -21,25 +26,29 @@ extern "C" {
|
|||
#if !defined(dynemu_EXPORTS) && !defined(DYNEMU_STATIC_DEFINE)
|
||||
typedef struct Emulator Emulator;
|
||||
#endif
|
||||
|
||||
typedef u64 (*DY_MMIO_Read)(u64 address, u8 access);
|
||||
typedef void (*DY_MMIO_Write)(u64 address, u64 value, u8 access);
|
||||
|
||||
DYNEMU_EXPORT Emulator *dynemu_open();
|
||||
DYNEMU_EXPORT Emulator *dynemu_open(enum DY_DCCType dcc_type, bool no_opts, bool big_endian);
|
||||
DYNEMU_EXPORT void dynemu_delete(Emulator *e);
|
||||
DYNEMU_EXPORT void dynemu_mmap(Emulator *e, u32 vaddr, u32 size);
|
||||
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, bool is_big);
|
||||
DYNEMU_EXPORT void dynemu_upload_memstub_nooffset(Emulator *e, u32 mem_start, u32 mem_size, bool is_big);
|
||||
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, bool is_big);
|
||||
DYNEMU_EXPORT u32 dynemu_read_u32(Emulator *e, u32 vaddr, bool is_big);
|
||||
DYNEMU_EXPORT u64 dynemu_read_u64(Emulator *e, u32 vaddr, bool is_big);
|
||||
DYNEMU_EXPORT u16 dynemu_read_u16(Emulator *e, u32 vaddr);
|
||||
DYNEMU_EXPORT u32 dynemu_read_u32(Emulator *e, u32 vaddr);
|
||||
DYNEMU_EXPORT u64 dynemu_read_u64(Emulator *e, u32 vaddr);
|
||||
DYNEMU_EXPORT void dynemu_write_u8(Emulator *e, u32 vaddr, u8 value);
|
||||
DYNEMU_EXPORT void dynemu_write_u16(Emulator *e, u32 vaddr, u16 value, bool is_big);
|
||||
DYNEMU_EXPORT void dynemu_write_u32(Emulator *e, u32 vaddr, u32 value, bool is_big);
|
||||
DYNEMU_EXPORT void dynemu_write_u64(Emulator *e, u32 vaddr, u64 value, bool is_big);
|
||||
DYNEMU_EXPORT void dynemu_write_u16(Emulator *e, u32 vaddr, u16 value);
|
||||
DYNEMU_EXPORT void dynemu_write_u32(Emulator *e, u32 vaddr, u32 value);
|
||||
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 is_big);
|
||||
DYNEMU_EXPORT u32 dynemu_execute(Emulator *e, u32 pc, bool stub_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
|
||||
|
|
|
|||
|
|
@ -1,66 +1,104 @@
|
|||
/* (c) 2025 Wrapper Inc. - The New Emulation Library */
|
||||
|
||||
#pragma once
|
||||
#include "dynarmic/interface/A32/config.h"
|
||||
#include "dynarmic/interface/halt_reason.h"
|
||||
#include "fmt/format.h"
|
||||
#include "dynarmic/interface/A32/a32.h"
|
||||
#include "dynarmic/interface/A32/coprocessor.h"
|
||||
#include "dynarmic/interface/exclusive_monitor.h"
|
||||
#include "dynemu/export.h"
|
||||
#include <functional>
|
||||
#include <future>
|
||||
#include <iostream>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
|
||||
using u8 = std::uint8_t;
|
||||
using u16 = std::uint16_t;
|
||||
using u32 = std::uint32_t;
|
||||
using u64 = std::uint64_t;
|
||||
|
||||
// typedef u64 (*MMIO_Read)(u64 address, u8 access);
|
||||
// typedef void (*MMIO_Write)(u64 address, u64 value, u8 access);
|
||||
|
||||
using MMIO_Read = std::function<u64(u64, u8)>;
|
||||
using MMIO_Write = std::function<void(u64, u64, u8)>;
|
||||
|
||||
enum class DCCType {
|
||||
ARM7_ARM9,
|
||||
ARM11_CORTEX
|
||||
};
|
||||
|
||||
namespace Dynemu {
|
||||
using Callback = Dynarmic::A32::Coprocessor::Callback;
|
||||
using CoprocReg = Dynarmic::A32::CoprocReg;
|
||||
using CallbackOrAccessOneWord = Dynarmic::A32::Coprocessor::CallbackOrAccessOneWord;
|
||||
using CallbackOrAccessTwoWords = Dynarmic::A32::Coprocessor::CallbackOrAccessTwoWords;
|
||||
|
||||
struct MemMap final {
|
||||
u64 start;
|
||||
u64 size;
|
||||
u32 start;
|
||||
u32 size;
|
||||
std::unique_ptr<u8[]> data;
|
||||
};
|
||||
|
||||
struct MMIO final {
|
||||
u32 start;
|
||||
u32 size;
|
||||
MMIO_Read read;
|
||||
MMIO_Write write;
|
||||
};
|
||||
|
||||
class DYNEMU_EXPORT MyEmulator final : public Dynarmic::A32::UserCallbacks {
|
||||
private:
|
||||
u8 find_memory_map_id(u32 vaddr);
|
||||
bool is_mmap_exists(u32 vaddr);
|
||||
template <std::size_t N>
|
||||
bool CheckExecOverridePC(const std::array<u32, N> &list, u32 pc);
|
||||
/* Memory map */
|
||||
std::optional<u8> find_memory_map_id(u32 vaddr);
|
||||
|
||||
/* MMIO */
|
||||
std::optional<u8> find_mmio_id(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 big_endian;
|
||||
bool thumb_mode;
|
||||
bool stub_mode;
|
||||
|
||||
public:
|
||||
/* Memory map */
|
||||
std::array<MemMap, 64> mem_map;
|
||||
u32 map_index;
|
||||
u32 os_offset;
|
||||
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() : map_index(0), os_offset(0xa8000000), mem_map() {};
|
||||
MyEmulator(bool big_endian = false) : mem_map_index(0), mmio_index(0), os_offset(0xa8000000), mem_map(), mmio_map(), big_endian(big_endian) {};
|
||||
|
||||
/*
|
||||
void CreateMemoryMap(u32 vaddr, u32 size)
|
||||
void CreateMemoryMap(u32 vaddr, u32 size)
|
||||
This function creates a memory map for this specific vaddr (virtual address) with specific size
|
||||
*/
|
||||
void CreateMemoryMap(u32 vaddr, u32 size);
|
||||
|
||||
/*
|
||||
void RoutineReplaceWithBXLR(const std::array<u32, N> &list, bool is_thumb, bool is_big)
|
||||
This function replaces each instruction on the list with "bx lr" instruction
|
||||
Currently, this has no use outside for hooking functions
|
||||
*/
|
||||
template <std::size_t N>
|
||||
void RoutineReplaceWithBXLR(const std::array<u32, N> &list, bool is_thumb, bool is_big);
|
||||
|
||||
/*
|
||||
size_t UploadToMemory(std::string filename, std::int64_t dest, u32 size)
|
||||
size_t UploadToMemory(const std::string &filename, std::int64_t dest, u32 size)
|
||||
This function uploads a file into memory, automatically creating a memory map if not exists.
|
||||
Setting dest to -1 will use a multi-map parser
|
||||
Returns:
|
||||
0: NOK
|
||||
1 or FileSize: OK
|
||||
*/
|
||||
size_t UploadToMemory(std::string filename, std::int64_t dest, u32 size);
|
||||
size_t UploadToMemory(const std::string &filename, std::int64_t dest, u32 size);
|
||||
|
||||
/*
|
||||
bool UploadFLSFromBytes(const u8 *fls)
|
||||
|
|
@ -84,27 +122,51 @@ namespace Dynemu {
|
|||
}
|
||||
|
||||
/*
|
||||
void UploadOSStub(u32 dest, u32 mem_start, u32 mem_size, bool is_big)
|
||||
This function uploads a stub libc to a specific dest virtual address, along with setting up the regions assosiated with the dynamic memory functions.
|
||||
Endianness were determined by is_big parameter.
|
||||
bool UploadFLSFromBytes(const std::vector<u8> &fls)
|
||||
This function uploads a multi-part code file from memory.
|
||||
Returns:
|
||||
0: NOK
|
||||
1: OK
|
||||
*/
|
||||
void UploadOSStub(u32 dest, u32 mem_start, u32 mem_size, bool is_big);
|
||||
template <std::size_t N>
|
||||
bool UploadFLSFromBytes(const std::vector<u8> &fls) {
|
||||
return UploadFLSFromBytes(fls.data());
|
||||
}
|
||||
|
||||
/*
|
||||
void UploadOSStub(u32 mem_start, u32 mem_size, bool is_big)
|
||||
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 is_big parameter.
|
||||
void UploadOSStub(u32 dest, u32 mem_start, u32 mem_size)
|
||||
This function uploads a stub libc to a specific dest 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, bool is_big) {
|
||||
UploadOSStub(os_offset, mem_start, mem_size, is_big);
|
||||
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.
|
||||
*/
|
||||
void CreateMMIO(u32 addr_start, u32 addr_size, const MMIO_Read &read = [](u64, u8) -> u64 {return -1;}, const MMIO_Write &write = [](u64, u32, u8) {});
|
||||
|
||||
/*
|
||||
bool PreCodeReadHook(bool is_thumb, u32 pc, Dynarmic::A32::IREmitter& ir)
|
||||
Custom function used for end of code detection.
|
||||
*/
|
||||
bool PreCodeReadHook(bool is_thumb, u32 pc, Dynarmic::A32::IREmitter& ir) override;
|
||||
|
||||
/*
|
||||
std::optional<u32> MemoryReadCode(u32 vaddr)
|
||||
Custom function used in code read routines.
|
||||
*/
|
||||
std::optional<u32> MemoryReadCode(u32 vaddr) override;
|
||||
|
||||
/*
|
||||
u8 MemoryRead8(u32 vaddr)
|
||||
Reads an 8-bit value from vaddr
|
||||
|
|
@ -118,6 +180,23 @@ namespace Dynemu {
|
|||
*/
|
||||
void MemoryReadBytes(u32 vaddr, u8 *output, u32 size);
|
||||
|
||||
/*
|
||||
void MemoryReadBytes(u32 vaddr, const std::array<u8, N> &data)
|
||||
Copy data from memory map to a single u8 pointer.
|
||||
*/
|
||||
template <std::size_t N>
|
||||
void MemoryReadBytes(u32 vaddr, const std::array<u8, N> &data) {
|
||||
MemoryReadBytes(vaddr, data.data(), N);
|
||||
};
|
||||
|
||||
/*
|
||||
void MemoryReadBytes(u32 vaddr, std::vector<u8> data)
|
||||
Copy data from memory map to a single u8 pointer.
|
||||
*/
|
||||
void MemoryReadBytes(u32 vaddr, std::vector<u8> data) {
|
||||
MemoryReadBytes(vaddr, data.data(), data.size());
|
||||
};
|
||||
|
||||
/*
|
||||
u16 MemoryRead16(u32 vaddr)
|
||||
Reads a 16-bit value from vaddr
|
||||
|
|
@ -139,26 +218,6 @@ namespace Dynemu {
|
|||
*/
|
||||
u64 MemoryRead64(u32 vaddr) override;
|
||||
|
||||
/*
|
||||
u16 MemoryRead16_Big(u32 vaddr)
|
||||
Reads a 16-bit value from vaddr
|
||||
Returns: short[vaddr]
|
||||
*/
|
||||
u16 MemoryRead16_Big(u32 vaddr);
|
||||
|
||||
/*
|
||||
u32 MemoryRead32_Big(u32 vaddr)
|
||||
Reads a 32-bit value from vaddr
|
||||
Returns: word[vaddr]
|
||||
*/
|
||||
u32 MemoryRead32_Big(u32 vaddr);
|
||||
|
||||
/*
|
||||
u64 MemoryRead64_Big(u32 vaddr)
|
||||
Reads a 64-bit value from vaddr
|
||||
Returns: dword[vaddr]
|
||||
*/
|
||||
u64 MemoryRead64_Big(u32 vaddr);
|
||||
|
||||
/*
|
||||
void MemoryWrite8(u32 vaddr, u8 value)
|
||||
|
|
@ -181,6 +240,14 @@ namespace Dynemu {
|
|||
MemoryWriteBytes(vaddr, data.data(), N);
|
||||
};
|
||||
|
||||
/*
|
||||
void MemoryWriteBytes(u32 vaddr, const std::vector<u8> &data)
|
||||
Copy data from a single u8 pointer into memory map.
|
||||
*/
|
||||
void MemoryWriteBytes(u32 vaddr, const std::vector<u8> &data) {
|
||||
MemoryWriteBytes(vaddr, data.data(), data.size());
|
||||
};
|
||||
|
||||
/*
|
||||
void MemoryWrite16(u32 vaddr, u16 value)
|
||||
Writes an 16-bit value to vaddr
|
||||
|
|
@ -198,24 +265,6 @@ namespace Dynemu {
|
|||
Writes an 64-bit value to vaddr
|
||||
*/
|
||||
void MemoryWrite64(u32 vaddr, u64 value) override;
|
||||
|
||||
/*
|
||||
void MemoryWrite16_Big(u32 vaddr, u16 value)
|
||||
Writes an 16-bit value to vaddr
|
||||
*/
|
||||
void MemoryWrite16_Big(u32 vaddr, u16 value);
|
||||
|
||||
/*
|
||||
void MemoryWrite32_Big(u32 vaddr, u32 value)
|
||||
Writes an 32-bit value to vaddr
|
||||
*/
|
||||
void MemoryWrite32_Big(u32 vaddr, u32 value);
|
||||
|
||||
/*
|
||||
void MemoryWrite64_Big(u32 vaddr, u64 value)
|
||||
Writes an 64-bit value to vaddr
|
||||
*/
|
||||
void MemoryWrite64_Big(u32 vaddr, u64 value);
|
||||
|
||||
/*
|
||||
void InterpreterFallback(u32 pc, size_t num_instructions)
|
||||
|
|
@ -233,29 +282,28 @@ namespace Dynemu {
|
|||
}
|
||||
|
||||
/*
|
||||
u32 Execute(u32 pc, bool is_big)
|
||||
u32 Execute(u32 pc, bool stub_mode)
|
||||
Executes from specific PC
|
||||
Returns: R0 register
|
||||
*/
|
||||
u32 Execute(u32 pc, bool is_big);
|
||||
|
||||
u32 Execute(u32 pc, bool stub_mode=true);
|
||||
|
||||
/*
|
||||
u32 Execute(u32 pc)
|
||||
Executes from specific PC
|
||||
Returns: R0 register
|
||||
std::future<u32> Spawn(u32 pc, bool stub_mode)
|
||||
Async version of Execute
|
||||
Returns: Future(R0 register)
|
||||
*/
|
||||
u32 Execute(u32 pc) {
|
||||
return Execute(pc, false);
|
||||
}
|
||||
|
||||
std::future<u32> Spawn(u32 pc, bool stub_mode=true);
|
||||
|
||||
/*
|
||||
void ExceptionRaised(u32 pc, Dynarmic::A32::Exception exception)
|
||||
*/
|
||||
void ExceptionRaised(u32 pc, Dynarmic::A32::Exception exception) override {
|
||||
std::cerr << fmt::format("decoder: exception at {:#08x}: ", pc);
|
||||
std::cerr << fmt::format("dynemu: exception at {:#08x}: ", pc);
|
||||
std::cerr << fmt::format("{}", (int)exception) << std::endl;
|
||||
std::terminate();
|
||||
// Do something.
|
||||
|
||||
if (exception == Dynarmic::A32::Exception::NoExecuteFault)
|
||||
cpu->HaltExecution(Dynarmic::HaltReason::MemoryAbort);
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
@ -269,14 +317,45 @@ namespace Dynemu {
|
|||
u64 GetTicksRemaining() override {return 8000;}
|
||||
};
|
||||
|
||||
struct CP14State {
|
||||
u32 dcc_status;
|
||||
u32 dcc_read_data;
|
||||
u32 dcc_write_data;
|
||||
DCCType dcc_type;
|
||||
};
|
||||
|
||||
class DYNEMU_EXPORT CP14 final : public Dynarmic::A32::Coprocessor {
|
||||
std::mutex dcc_lock;
|
||||
u32 dummy_value;
|
||||
|
||||
std::optional<Callback> CompileInternalOperation(bool two, unsigned opc1, CoprocReg CRd, CoprocReg CRn, CoprocReg CRm, unsigned opc2) override;
|
||||
CallbackOrAccessOneWord CompileSendOneWord(bool two, unsigned opc1, CoprocReg CRn, CoprocReg CRm, unsigned opc2) override;
|
||||
CallbackOrAccessTwoWords CompileSendTwoWords(bool two, unsigned opc, CoprocReg CRm) override;
|
||||
CallbackOrAccessOneWord CompileGetOneWord(bool two, unsigned opc1, CoprocReg CRn, CoprocReg CRm, unsigned opc2) override;
|
||||
CallbackOrAccessTwoWords CompileGetTwoWords(bool two, unsigned opc, CoprocReg CRm) override;
|
||||
std::optional<Callback> CompileLoadWords(bool two, bool long_transfer, CoprocReg CRd, std::optional<std::uint8_t> option) override;
|
||||
std::optional<Callback> CompileStoreWords(bool two, bool long_transfer, CoprocReg CRd, std::optional<std::uint8_t> option) override;
|
||||
|
||||
private:
|
||||
CP14State &state;
|
||||
|
||||
public:
|
||||
CP14(DCCType dcc_type, CP14State &state);
|
||||
void Send(u32 value);
|
||||
u32 Get();
|
||||
};
|
||||
|
||||
class DYNEMU_EXPORT Emulator final {
|
||||
private:
|
||||
std::unique_ptr<Dynarmic::A32::Jit> cpu;
|
||||
std::unique_ptr<Dynarmic::ExclusiveMonitor> mon;
|
||||
DCCType dcc_type;
|
||||
CP14State cp14_state;
|
||||
|
||||
public:
|
||||
MyEmulator env;
|
||||
std::shared_ptr<CP14> cp14;
|
||||
|
||||
Emulator();
|
||||
Emulator(DCCType dcc_type=DCCType::ARM7_ARM9, bool no_opts=false, bool big_endian=false);
|
||||
};
|
||||
}
|
||||
|
|
@ -2,4 +2,9 @@ include_directories(.)
|
|||
set_property(DIRECTORY APPEND PROPERTY
|
||||
COMPILE_DEFINITIONS $<$<CONFIG:Debug>:_DEBUG> $<$<NOT:$<CONFIG:Debug>>:NDEBUG>)
|
||||
|
||||
add_subdirectory(lib)
|
||||
add_subdirectory(lib)
|
||||
if (DYNEMU_PYTHON)
|
||||
set(PYBIND11_FINDPYTHON ON)
|
||||
add_subdirectory(pybind)
|
||||
endif()
|
||||
add_subdirectory(test)
|
||||
|
|
@ -4,8 +4,8 @@ using namespace Dynemu;
|
|||
extern "C" {
|
||||
#include "dynemu/libdynemu.h"
|
||||
|
||||
Emulator *dynemu_open() {
|
||||
auto temp = new Emulator();
|
||||
Emulator *dynemu_open(enum DY_DCCType dcc_type, bool no_opts, bool big_endian) {
|
||||
auto temp = new Emulator((DCCType)dcc_type, no_opts, big_endian);
|
||||
return temp;
|
||||
}
|
||||
|
||||
|
|
@ -17,6 +17,12 @@ extern "C" {
|
|||
return e->env.CreateMemoryMap(vaddr, size);
|
||||
}
|
||||
|
||||
void dynemu_mmio(Emulator *e, u32 vaddr, u32 size, DY_MMIO_Read read, DY_MMIO_Write write) {
|
||||
auto read_cb = std::bind(read, std::placeholders::_1, std::placeholders::_2);
|
||||
auto write_cb = std::bind(write, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3);
|
||||
return e->env.CreateMMIO(vaddr, size, read_cb, write_cb);
|
||||
}
|
||||
|
||||
size_t dynemu_upload_file(Emulator *e, const char *filename, std::int64_t dest, u32 size) {
|
||||
return e->env.UploadToMemory(filename, dest, size);
|
||||
}
|
||||
|
|
@ -25,68 +31,44 @@ extern "C" {
|
|||
return e->env.UploadFLSFromBytes(fls);
|
||||
}
|
||||
|
||||
void dynemu_upload_memstub(Emulator *e, u32 dest, u32 mem_start, u32 mem_size, bool is_big) {
|
||||
return e->env.UploadOSStub(dest, mem_start, mem_size, is_big);
|
||||
void dynemu_upload_memstub(Emulator *e, u32 dest, u32 mem_start, u32 mem_size) {
|
||||
return e->env.UploadOSStub(dest, mem_start, mem_size);
|
||||
}
|
||||
|
||||
void dynemu_upload_memstub_nooffset(Emulator *e, u32 mem_start, u32 mem_size, bool is_big) {
|
||||
return e->env.UploadOSStub(mem_start, mem_size, is_big);
|
||||
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);
|
||||
}
|
||||
|
||||
u16 dynemu_read_u16(Emulator *e, u32 vaddr, bool is_big) {
|
||||
if (is_big) {
|
||||
return e->env.MemoryRead16_Big(vaddr);
|
||||
} else {
|
||||
return e->env.MemoryRead16(vaddr);
|
||||
}
|
||||
u16 dynemu_read_u16(Emulator *e, u32 vaddr) {
|
||||
return e->env.MemoryRead16(vaddr);
|
||||
}
|
||||
|
||||
u32 dynemu_read_u32(Emulator *e, u32 vaddr, bool is_big) {
|
||||
if (is_big) {
|
||||
return e->env.MemoryRead32_Big(vaddr);
|
||||
} else {
|
||||
return e->env.MemoryRead32(vaddr);
|
||||
}
|
||||
u32 dynemu_read_u32(Emulator *e, u32 vaddr) {
|
||||
return e->env.MemoryRead32(vaddr);
|
||||
}
|
||||
|
||||
u64 dynemu_read_u64(Emulator *e, u32 vaddr, bool is_big) {
|
||||
if (is_big) {
|
||||
return e->env.MemoryRead64_Big(vaddr);
|
||||
} else {
|
||||
return e->env.MemoryRead64(vaddr);
|
||||
}
|
||||
u64 dynemu_read_u64(Emulator *e, u32 vaddr) {
|
||||
return e->env.MemoryRead64(vaddr);
|
||||
}
|
||||
|
||||
void dynemu_write_u8(Emulator *e, u32 vaddr, u8 value) {
|
||||
e->env.MemoryWrite8(vaddr, value);
|
||||
}
|
||||
|
||||
void dynemu_write_u16(Emulator *e, u32 vaddr, u16 value, bool is_big) {
|
||||
if (is_big) {
|
||||
e->env.MemoryWrite16_Big(vaddr, value);
|
||||
} else {
|
||||
e->env.MemoryWrite16(vaddr, value);
|
||||
}
|
||||
void dynemu_write_u16(Emulator *e, u32 vaddr, u16 value) {
|
||||
e->env.MemoryWrite16(vaddr, value);
|
||||
}
|
||||
|
||||
void dynemu_write_u32(Emulator *e, u32 vaddr, u32 value, bool is_big) {
|
||||
if (is_big) {
|
||||
e->env.MemoryWrite32_Big(vaddr, value);
|
||||
} else {
|
||||
e->env.MemoryWrite32(vaddr, value);
|
||||
}
|
||||
void dynemu_write_u32(Emulator *e, u32 vaddr, u32 value) {
|
||||
e->env.MemoryWrite32(vaddr, value);
|
||||
}
|
||||
|
||||
void dynemu_write_u64(Emulator *e, u32 vaddr, u64 value, bool is_big) {
|
||||
if (is_big) {
|
||||
e->env.MemoryWrite64_Big(vaddr, value);
|
||||
} else {
|
||||
e->env.MemoryWrite64(vaddr, value);
|
||||
}
|
||||
void dynemu_write_u64(Emulator *e, u32 vaddr, u64 value) {
|
||||
e->env.MemoryWrite64(vaddr, value);
|
||||
}
|
||||
|
||||
void dynemu_read_bytes(Emulator *e, u32 vaddr, u8 *output, u32 size) {
|
||||
|
|
@ -97,8 +79,8 @@ extern "C" {
|
|||
e->env.MemoryWriteBytes(vaddr, input, size);
|
||||
}
|
||||
|
||||
u32 dynemu_execute(Emulator *e, u32 pc, bool is_big) {
|
||||
return e->env.Execute(pc, is_big);
|
||||
u32 dynemu_execute(Emulator *e, u32 pc, bool stub_mode) {
|
||||
return e->env.Execute(pc, stub_mode);
|
||||
}
|
||||
|
||||
u32 dynemu_read_reg(Emulator *e, u8 reg) {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
/* (c) 2025 Wrapper Inc. - The New Emulation Library */
|
||||
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
#include "fmt/format.h"
|
||||
|
||||
|
|
@ -11,11 +10,15 @@
|
|||
#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 "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 {
|
||||
|
|
@ -31,25 +34,42 @@ namespace Dynemu {
|
|||
};
|
||||
|
||||
/* 01 - Private */
|
||||
u8 MyEmulator::find_memory_map_id(u32 vaddr) {
|
||||
for (int i = 0; i < mem_map.size(); i++) {
|
||||
if (vaddr >= mem_map[i].start && vaddr < (mem_map[i].start + mem_map[i].size)) return i;
|
||||
std::optional<u8> MyEmulator::find_memory_map_id(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;
|
||||
}
|
||||
std::cerr << fmt::format("Memory map {:#08x} OOB, Emulation cannot continue", vaddr) << std::endl;
|
||||
throw std::out_of_range("Data abort");
|
||||
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
bool MyEmulator::is_mmap_exists(u32 vaddr) {
|
||||
for (int i = 0; i < mem_map.size(); i++) {
|
||||
if (vaddr >= mem_map[i].start && vaddr < (mem_map[i].start + mem_map[i].size)) return true;
|
||||
std::optional<u8> MyEmulator::find_mmio_id(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 false;
|
||||
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
template <std::size_t N>
|
||||
bool MyEmulator::CheckExecOverridePC(const std::array<u32, N> &list, u32 pc) {
|
||||
for (int i = 0; i < N; i++) {
|
||||
if (pc == list[i]) return true;
|
||||
std::optional<u32> MyEmulator::handle_mmio_read(u32 vaddr, u8 access) {
|
||||
if (auto id = find_mmio_id(vaddr); id.has_value()) {
|
||||
#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 std::nullopt;
|
||||
}
|
||||
|
||||
bool MyEmulator::handle_mmio_write(u32 vaddr, u32 value, u8 access) {
|
||||
if (auto id = find_mmio_id(vaddr); id.has_value()) {
|
||||
#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);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
|
|
@ -57,28 +77,19 @@ namespace Dynemu {
|
|||
|
||||
/* 02 - Public */
|
||||
void MyEmulator::CreateMemoryMap(u32 vaddr, u32 size) {
|
||||
if (!is_mmap_exists(vaddr)) mem_map[map_index++] = {vaddr, size, std::make_unique<u8[]>(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)};
|
||||
}
|
||||
|
||||
template <std::size_t N>
|
||||
void MyEmulator::RoutineReplaceWithBXLR(const std::array<u32, N> &list, bool is_thumb, bool is_big) {
|
||||
for (int i = 0; i < N; i++) {
|
||||
if (is_thumb) {
|
||||
MemoryWrite16(list[i], is_big ? 0x7047 : 0x4770);
|
||||
} else {
|
||||
MemoryWrite32(list[i], is_big ? 0x1eff2fe1 : 0xe12fff1e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
size_t MyEmulator::UploadToMemory(std::string filename, std::int64_t dest, u32 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;
|
||||
int to_read = 0;
|
||||
u32 read_n;
|
||||
|
||||
if (!file.is_open()) {
|
||||
std::cerr << fmt::format("Unable to open {}: {}", filename, std::strerror(errno)) << std::endl;
|
||||
std::cerr << fmt::format("dynemu: unable to open code file {}: {}", filename, std::strerror(errno)) << std::endl;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -87,28 +98,38 @@ namespace Dynemu {
|
|||
size_t fileSize = file.tellg();
|
||||
file.seekg(0, file.beg);
|
||||
|
||||
if (size < fileSize && !is_mmap_exists((u32)dest)) {
|
||||
std::cerr << "Code file too large to fit into memory" << std::endl;
|
||||
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;
|
||||
}
|
||||
|
||||
if (!fitsOnMemoryMap) {
|
||||
std::cerr << "dynemu: code file too large to fit into memory" << std::endl;
|
||||
return 0;
|
||||
}
|
||||
|
||||
CreateMemoryMap((u32)dest, size);
|
||||
CreateMemoryMap((u32)dest, ALIGN_BLOCK(size));
|
||||
|
||||
while ((size_t)file.tellg() < fileSize) {
|
||||
to_read = (int)((fileSize - file.tellg()) >= 2048 ? 2048 : (fileSize - file.tellg()));
|
||||
file.read((char *)buf, to_read);
|
||||
MemoryWriteBytes((u32)dest + offset, buf, to_read);
|
||||
offset += to_read;
|
||||
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 << "Invalid FL magic check" << std::endl;
|
||||
std::cerr << "dynemu: nvalid FL magic check" << std::endl;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -120,11 +141,13 @@ namespace Dynemu {
|
|||
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);
|
||||
|
||||
CreateMemoryMap(fl.data.get()[i].start_vaddr, fl.data.get()[i].size + ((fl.data.get()[i].size % BLOCK_ALLIGN) ? (BLOCK_ALLIGN - (fl.data.get()[i].size % BLOCK_ALLIGN)) : 0));
|
||||
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 1;
|
||||
return codeSize;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -151,136 +174,247 @@ namespace Dynemu {
|
|||
|
||||
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, fl.data.get()[i].size + ((fl.data.get()[i].size % BLOCK_ALLIGN) ? (BLOCK_ALLIGN - (fl.data.get()[i].size % BLOCK_ALLIGN)) : 0));
|
||||
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, bool is_big) {
|
||||
if (is_big) {
|
||||
void MyEmulator::UploadOSStub(u32 dest, u32 mem_start, u32 mem_size) {
|
||||
if (big_endian) {
|
||||
MemoryWriteBytes(dest, OS_LoopLoader_BE.data(), (u32)OS_LoopLoader_BE.size());
|
||||
MemoryWrite32_Big(dest + MEM_INIT_REG_OFFSET, mem_start);
|
||||
MemoryWrite32_Big(dest + MEM_INIT_REG_SIZE, mem_size);
|
||||
} else {
|
||||
MemoryWriteBytes(dest, OS_LoopLoader.data(), (u32)OS_LoopLoader.size());
|
||||
MemoryWrite32(dest + MEM_INIT_REG_OFFSET, mem_start);
|
||||
MemoryWrite32(dest + MEM_INIT_REG_SIZE, mem_size);
|
||||
}
|
||||
|
||||
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())
|
||||
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 {:#08x}", pc) << std::endl;
|
||||
std::cout << fmt::format("REG P0 {:#08x}", cpu->Regs()[0]) << std::endl;
|
||||
std::cout << fmt::format("REG P1 {:#08x}", cpu->Regs()[1]) << std::endl;
|
||||
std::cout << fmt::format("REG P2 {:#08x}", cpu->Regs()[2]) << std::endl;
|
||||
std::cout << fmt::format("REG P12 {:#08x}", cpu->Regs()[12]) << std::endl;
|
||||
std::cout << fmt::format("REG P15 {:#08x}", cpu->Regs()[15]) << std::endl;
|
||||
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
|
||||
|
||||
if (pc == (os_offset + ROUTE_EXIT)) cpu->HaltExecution();
|
||||
thumb_mode = is_thumb;
|
||||
|
||||
if (stub_mode && pc == (os_offset + ROUTE_EXIT)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
return true;
|
||||
}
|
||||
|
||||
std::optional<u32> MyEmulator::MemoryReadCode(u32 vaddr) {
|
||||
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 && thumb_mode) {
|
||||
temp = MemoryRead8(vaddr + 1);
|
||||
temp |= MemoryRead8(vaddr) << 8;
|
||||
temp |= MemoryRead8(vaddr + 3) << 16;
|
||||
temp |= MemoryRead8(vaddr + 2) << 24;
|
||||
|
||||
return temp;
|
||||
}
|
||||
}
|
||||
|
||||
return MemoryRead32(vaddr);
|
||||
}
|
||||
|
||||
u8 MyEmulator::MemoryRead8(u32 vaddr) {
|
||||
if (auto ret = handle_mmio_read(vaddr, 1); ret.has_value())
|
||||
return ret.value();
|
||||
|
||||
#if DEBUG
|
||||
if (cpu->IsExecuting()) printf("Reading at 0x%08x\n", vaddr);
|
||||
#endif
|
||||
try {
|
||||
u8 mem_now = find_memory_map_id(vaddr);
|
||||
return mem_map[mem_now].data.get()[vaddr - mem_map[mem_now].start];
|
||||
} catch (std::out_of_range) {
|
||||
cpu->HaltExecution(Dynarmic::HaltReason::MemoryAbort);
|
||||
return 0xff; // Undefined behavior can occur
|
||||
}
|
||||
}
|
||||
|
||||
void MyEmulator::MemoryReadBytes(u32 vaddr, u8 *output, u32 size) {
|
||||
u8 mem_now = find_memory_map_id(vaddr);
|
||||
ASSERT(((vaddr - mem_map[mem_now].start) + size) <= mem_map[mem_now].size);
|
||||
memcpy(output, mem_map[mem_now].data.get() + (vaddr - mem_map[mem_now].start), size);
|
||||
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
|
||||
}
|
||||
|
||||
return mem_map[mmap.value()].data.get()[vaddr - mem_map[mmap.value()].start];
|
||||
}
|
||||
|
||||
u16 MyEmulator::MemoryRead16(u32 vaddr) {
|
||||
return u16(MemoryRead8(vaddr)) | u16(MemoryRead8(vaddr + 1)) << 8;
|
||||
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()) {
|
||||
std::cerr << fmt::format("dynemu: Invalid memory read at {:#010x}", vaddr) << std::endl;
|
||||
cpu->HaltExecution(Dynarmic::HaltReason::MemoryAbort);
|
||||
return -1; // Undefined behavior can occur
|
||||
}
|
||||
|
||||
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) {
|
||||
return u32(MemoryRead16(vaddr)) | u32(MemoryRead16(vaddr + 2)) << 16;
|
||||
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()) {
|
||||
std::cerr << fmt::format("dynemu: Invalid memory read at {:#010x}", vaddr) << std::endl;
|
||||
cpu->HaltExecution(Dynarmic::HaltReason::MemoryAbort);
|
||||
return -1; // Undefined behavior can occur
|
||||
}
|
||||
|
||||
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) {
|
||||
return u64(MemoryRead32(vaddr)) | u64(MemoryRead32(vaddr + 4)) << 32;
|
||||
}
|
||||
|
||||
u16 MyEmulator::MemoryRead16_Big(u32 vaddr) {
|
||||
return u16(MemoryRead8(vaddr + 1)) | u16(MemoryRead8(vaddr)) << 8;
|
||||
}
|
||||
|
||||
u32 MyEmulator::MemoryRead32_Big(u32 vaddr) {
|
||||
return u32(MemoryRead16_Big(vaddr + 2)) | u32(MemoryRead16_Big(vaddr)) << 16;
|
||||
}
|
||||
|
||||
u64 MyEmulator::MemoryRead64_Big(u32 vaddr) {
|
||||
return u64(MemoryRead32_Big(vaddr + 4)) | u64(MemoryRead32_Big(vaddr)) << 32;
|
||||
ASSERT_FALSE("target is not a 64-bit device");
|
||||
}
|
||||
|
||||
void MyEmulator::MemoryWrite8(u32 vaddr, u8 value) {
|
||||
if (handle_mmio_write(vaddr, value, 1))
|
||||
return;
|
||||
|
||||
#if DEBUG
|
||||
if (cpu->IsExecuting()) printf("Writing at 0x%08x 0x%02x\n", vaddr, value);
|
||||
#endif
|
||||
try {
|
||||
u8 mem_now = find_memory_map_id(vaddr);
|
||||
mem_map[mem_now].data.get()[vaddr - mem_map[mem_now].start] = value;
|
||||
} catch (std::out_of_range) {
|
||||
cpu->HaltExecution(Dynarmic::HaltReason::MemoryAbort);
|
||||
}
|
||||
}
|
||||
|
||||
void MyEmulator::MemoryWriteBytes(u32 vaddr, const u8 *input, u32 size) {
|
||||
u8 mem_now = find_memory_map_id(vaddr);
|
||||
ASSERT(((vaddr - mem_map[mem_now].start) + size) <= mem_map[mem_now].size);
|
||||
memcpy(mem_map[mem_now].data.get() + (vaddr - mem_map[mem_now].start), input, size);
|
||||
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
|
||||
}
|
||||
|
||||
mem_map[mmap.value()].data.get()[vaddr - mem_map[mmap.value()].start] = value;
|
||||
}
|
||||
|
||||
void MyEmulator::MemoryWrite16(u32 vaddr, u16 value) {
|
||||
MemoryWrite8(vaddr, u8(value));
|
||||
MemoryWrite8(vaddr + 1, u8(value >> 8));
|
||||
if (handle_mmio_write(vaddr, value, 2))
|
||||
return;
|
||||
|
||||
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 (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) {
|
||||
MemoryWrite16(vaddr, u16(value));
|
||||
MemoryWrite16(vaddr + 2, u16(value >> 16));
|
||||
if (handle_mmio_write(vaddr, value, 4))
|
||||
return;
|
||||
|
||||
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 (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) {
|
||||
MemoryWrite32(vaddr, u32(value));
|
||||
MemoryWrite32(vaddr + 4, u32(value >> 32));
|
||||
ASSERT_FALSE("target is not a 64-bit device");
|
||||
}
|
||||
|
||||
void MyEmulator::MemoryWrite16_Big(u32 vaddr, u16 value) {
|
||||
MemoryWrite8(vaddr + 1, u8(value));
|
||||
MemoryWrite8(vaddr, u8(value >> 8));
|
||||
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.");
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
void MyEmulator::MemoryWrite32_Big(u32 vaddr, u32 value) {
|
||||
MemoryWrite16_Big(vaddr + 2, u16(value));
|
||||
MemoryWrite16_Big(vaddr, u16(value >> 16));
|
||||
|
||||
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.");
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
void MyEmulator::MemoryWrite64_Big(u32 vaddr, u64 value) {
|
||||
MemoryWrite32_Big(vaddr + 4, u32(value));
|
||||
MemoryWrite32_Big(vaddr, u32(value >> 32));
|
||||
}
|
||||
|
||||
u32 MyEmulator::Execute(u32 pc, bool is_big) {
|
||||
cpu->Regs()[14] = os_offset + ROUTE_EXIT;
|
||||
u32 MyEmulator::Execute(u32 pc, bool stub_mode) {
|
||||
if (stub_mode)
|
||||
cpu->Regs()[14] = os_offset + ROUTE_EXIT;
|
||||
|
||||
this->stub_mode = stub_mode;
|
||||
cpu->Regs()[15] = pc & ~1;
|
||||
cpu->SetCpsr(((pc & 1) ? 0x30 : 0x1d0) | (is_big ? 0x200 : 0x0));
|
||||
cpu->SetCpsr((pc & 1) ? 0x30 : 0x1d0);
|
||||
|
||||
Dynarmic::HaltReason returnCode = cpu->Run();
|
||||
cpu->ClearCache();
|
||||
|
|
@ -288,6 +422,230 @@ 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::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;
|
||||
|
|
@ -341,12 +699,19 @@ namespace Dynemu {
|
|||
}
|
||||
};
|
||||
|
||||
Emulator::Emulator() {
|
||||
Emulator::Emulator(DCCType dcc_type, bool no_opts, bool big_endian) : env(big_endian) {
|
||||
Dynarmic::A32::UserConfig user_config;
|
||||
|
||||
user_config.callbacks = &env;
|
||||
user_config.coprocessors[15] = std::make_shared<CP15>();
|
||||
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);
|
||||
|
|
|
|||
6
src/pybind/CMakeLists.txt
Normal file
6
src/pybind/CMakeLists.txt
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
pybind11_add_module(dynemup dynemu.cpp)
|
||||
|
||||
add_dependencies(dynemup dynemu)
|
||||
target_link_libraries(dynemup PUBLIC dynemu)
|
||||
|
||||
install(TARGETS dynemup LIBRARY DESTINATION ./dynemup)
|
||||
170
src/pybind/dynemu.cpp
Normal file
170
src/pybind/dynemu.cpp
Normal file
|
|
@ -0,0 +1,170 @@
|
|||
#include <pybind11/pybind11.h>
|
||||
#include <pybind11/functional.h>
|
||||
#include <pybind11/pytypes.h>
|
||||
#include <pybind11/stl.h>
|
||||
#include <pybind11/native_enum.h>
|
||||
#include "dynemu/sys_emulator.hpp"
|
||||
|
||||
namespace py = pybind11;
|
||||
|
||||
struct Emu {
|
||||
Emu(DCCType dcc_type = DCCType::ARM7_ARM9, bool no_opts = false, bool big_endian = false) : emu(dcc_type, no_opts, big_endian) { }
|
||||
|
||||
void memory_map(u32 vaddr, u32 size) {
|
||||
emu.env.CreateMemoryMap(vaddr, size);
|
||||
}
|
||||
|
||||
void upload_file(const std::string &filename, u32 vaddr, u32 size) {
|
||||
emu.env.UploadToMemory(filename, vaddr, size);
|
||||
}
|
||||
|
||||
void upload_fls_memory(py::bytes data) {
|
||||
py::buffer_info info(py::buffer(data).request());
|
||||
emu.env.UploadFLSFromBytes((const u8 *)info.ptr);
|
||||
}
|
||||
|
||||
void upload_fls_memory(py::bytearray data) {
|
||||
py::buffer_info info(py::buffer(data).request());
|
||||
emu.env.UploadFLSFromBytes((const u8 *)info.ptr);
|
||||
}
|
||||
|
||||
void upload_memstub(u32 vaddr, u32 mem_start = 0xa0000000, u32 mem_size = 0x2000000) {
|
||||
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);
|
||||
}
|
||||
|
||||
void mmio(u32 vaddr, u32 size, const MMIO_Read &read) {
|
||||
emu.env.CreateMMIO(vaddr, size, read);
|
||||
}
|
||||
|
||||
void mmio(u32 vaddr, u32 size, const MMIO_Write &write) {
|
||||
emu.env.CreateMMIO(vaddr, size, [](u64, u8) -> u64 {return -1;}, write);
|
||||
}
|
||||
|
||||
void mmio(u32 vaddr, u32 size, const MMIO_Write &write, const MMIO_Read &read) {
|
||||
emu.env.CreateMMIO(vaddr, size, read, write);
|
||||
}
|
||||
|
||||
void mmio(u32 vaddr, u32 size) {
|
||||
emu.env.CreateMMIO(vaddr, size);
|
||||
}
|
||||
|
||||
u8 read_u8(u32 vaddr) {
|
||||
return emu.env.MemoryRead8(vaddr);
|
||||
}
|
||||
|
||||
u16 read_u16(u32 vaddr) {
|
||||
return emu.env.MemoryRead16(vaddr);
|
||||
}
|
||||
|
||||
u32 read_u32(u32 vaddr) {
|
||||
return emu.env.MemoryRead32(vaddr);
|
||||
}
|
||||
|
||||
u64 read_u64(u32 vaddr) {
|
||||
return emu.env.MemoryRead64(vaddr);
|
||||
}
|
||||
|
||||
void write_u8(u32 vaddr, u8 value) {
|
||||
emu.env.MemoryWrite8(vaddr, value);
|
||||
}
|
||||
|
||||
void write_u16(u32 vaddr, u16 value) {
|
||||
emu.env.MemoryWrite16(vaddr, value);
|
||||
}
|
||||
|
||||
void write_u32(u32 vaddr, u32 value) {
|
||||
emu.env.MemoryWrite32(vaddr, value);
|
||||
}
|
||||
|
||||
void write_u64(u32 vaddr, u64 value) {
|
||||
emu.env.MemoryWrite64(vaddr, value);
|
||||
}
|
||||
|
||||
py::bytes read_bytes(u32 vaddr, u32 size) {
|
||||
auto buf = std::vector<u8>(size);
|
||||
emu.env.MemoryReadBytes(vaddr, buf);
|
||||
|
||||
return py::bytes((const char *)buf.data(), buf.size());
|
||||
}
|
||||
|
||||
void write_bytes(u32 vaddr, py::bytes data) {
|
||||
py::buffer_info info(py::buffer(data).request());
|
||||
emu.env.MemoryWriteBytes(vaddr, (const u8 *)info.ptr, info.size);
|
||||
}
|
||||
|
||||
void write_bytes(u32 vaddr, py::bytearray data) {
|
||||
py::buffer_info info(py::buffer(data).request());
|
||||
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 get_reg(u8 reg_num) {
|
||||
return emu.env.cpu->Regs()[reg_num];
|
||||
}
|
||||
|
||||
void set_reg(u8 reg_num, u32 value) {
|
||||
emu.env.cpu->Regs()[reg_num] = value;
|
||||
}
|
||||
|
||||
u32 dcc_read() {
|
||||
return emu.cp14->Get();
|
||||
}
|
||||
|
||||
void dcc_write(u32 value) {
|
||||
emu.cp14->Send(value);
|
||||
}
|
||||
|
||||
Dynemu::Emulator emu;
|
||||
};
|
||||
|
||||
PYBIND11_MODULE(dynemup, module, py::mod_gil_not_used()) {
|
||||
module.doc() = "Dynemu";
|
||||
|
||||
py::class_<Emu> emu(module, "Dynemu");
|
||||
|
||||
py::native_enum<DCCType>(emu, "DCCType", "enum.Enum")
|
||||
.value("ARM7_ARM9", DCCType::ARM7_ARM9)
|
||||
.value("ARM11_CORTEX", DCCType::ARM11_CORTEX)
|
||||
.export_values()
|
||||
.finalize();
|
||||
|
||||
emu.def(py::init<DCCType, bool, bool>(), py::arg("dcc_type") = DCCType::ARM7_ARM9, py::arg("no_opts") = false, py::arg("big_endian") = false)
|
||||
.def("memory_map", &Emu::memory_map, py::arg("vaddr"), py::arg("size"))
|
||||
.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("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"))
|
||||
.def("mmio", static_cast<void (Emu::*)(u32, u32, const MMIO_Read &, const MMIO_Write &)>(&Emu::mmio), py::arg("vaddr"), py::arg("size"), py::arg("read"), py::arg("write"))
|
||||
.def("mmio", static_cast<void (Emu::*)(u32, u32, const MMIO_Write &, const MMIO_Read &)>(&Emu::mmio), py::arg("vaddr"), py::arg("size"), py::arg("write"), py::arg("read"))
|
||||
.def("read_u8", &Emu::read_u8, py::arg("vaddr"))
|
||||
.def("read_u16", &Emu::read_u16, py::arg("vaddr"))
|
||||
.def("read_u32", &Emu::read_u32, py::arg("vaddr"))
|
||||
.def("read_u64", &Emu::read_u64, py::arg("vaddr"))
|
||||
.def("write_u8", &Emu::write_u8, py::arg("vaddr"), py::arg("value"))
|
||||
.def("write_u16", &Emu::write_u16, py::arg("vaddr"), py::arg("value"))
|
||||
.def("write_u32", &Emu::write_u32, py::arg("vaddr"), py::arg("value"))
|
||||
.def("write_u64", &Emu::write_u64, py::arg("vaddr"), py::arg("value"))
|
||||
.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("get_reg", &Emu::get_reg, py::arg("reg_num"))
|
||||
.def("set_reg", &Emu::set_reg, py::arg("reg_num"), py::arg("value"))
|
||||
.def("dcc_read", &Emu::dcc_read)
|
||||
.def("dcc_write", &Emu::dcc_write, py::arg("value"));
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue