Major cmake overhaul
This commit is contained in:
parent
09e03f9fb6
commit
b3d9a2cf3e
16 changed files with 854 additions and 579 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -34,6 +34,7 @@
|
|||
|
||||
# Output folders
|
||||
build/
|
||||
install_test/
|
||||
|
||||
# My test files
|
||||
uni_libs/
|
||||
|
|
|
|||
24
.vscode/cmake-variants.json
vendored
Normal file
24
.vscode/cmake-variants.json
vendored
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
{
|
||||
"buildType": {
|
||||
"default": "Debug",
|
||||
"description": "Build Type Selection",
|
||||
"choices": {
|
||||
"Debug": {
|
||||
"short": "Debug",
|
||||
"long": "Build with no optimizations and debugging information",
|
||||
"buildType": "Debug",
|
||||
"settings": {
|
||||
"BUILD_SHARED_LIBS": "ON"
|
||||
}
|
||||
},
|
||||
"Release": {
|
||||
"short": "Release",
|
||||
"long": "Build with optimizations and some debuging information",
|
||||
"buildType": "Release",
|
||||
"settings": {
|
||||
"BUILD_SHARED_LIBS": "ON"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
3
.vscode/settings.json
vendored
3
.vscode/settings.json
vendored
|
|
@ -103,7 +103,8 @@
|
|||
"shared_mutex": "cpp",
|
||||
"source_location": "cpp",
|
||||
"stack": "cpp",
|
||||
"stdfloat": "cpp"
|
||||
"stdfloat": "cpp",
|
||||
"resumable": "cpp"
|
||||
},
|
||||
"C_Cpp.default.compilerPath": "d:\\Daffin\\vs_com_2022\\VC\\Tools\\MSVC\\14.42.34433\\bin\\Hostx64\\x64\\cl.exe",
|
||||
"C_Cpp.default.configurationProvider": "ms-vscode.cmake-tools",
|
||||
|
|
|
|||
|
|
@ -1,33 +1,62 @@
|
|||
cmake_minimum_required(VERSION 3.10.0)
|
||||
project(qmgdec VERSION 0.1.0 LANGUAGES C CXX)
|
||||
project(Dynemu VERSION 0.1.0 LANGUAGES C CXX)
|
||||
|
||||
set(CMAKE_CXX_STANDARD 20)
|
||||
|
||||
if (NOT DEFINED CMAKE_CXX_VISIBILITY_PRESET AND
|
||||
NOT DEFINED CMAKE_VISIBILITY_INLINES_HIDDEN)
|
||||
set(CMAKE_CXX_VISIBILITY_PRESET hidden)
|
||||
set(CMAKE_VISIBILITY_INLINES_HIDDEN YES)
|
||||
endif ()
|
||||
|
||||
if(POLICY CMP0167)
|
||||
cmake_policy(SET CMP0167 OLD)
|
||||
endif()
|
||||
|
||||
# Variables
|
||||
if (DEFINED Dynemu_SHARED_LIBS)
|
||||
set(BUILD_SHARED_LIBS "${Dynemu_SHARED_LIBS}")
|
||||
endif ()
|
||||
|
||||
# 01 - Boost first
|
||||
set(BOOST_INCLUDEDIR "${PROJECT_SOURCE_DIR}/externals/dynarmic/externals/ext-boost")
|
||||
find_package(Boost 1.57 REQUIRED)
|
||||
|
||||
# 02 - Compile flags
|
||||
add_compile_options("$<$<C_COMPILER_ID:MSVC>:/utf-8>")
|
||||
add_compile_options("$<$<CXX_COMPILER_ID:MSVC>:/utf-8>")
|
||||
add_compile_options("$<$<C_COMPILER_ID:MSVC>:/utf-8>" "$<$<C_COMPILER_ID:MSVC>:/std:c++20>")
|
||||
add_compile_options("$<$<CXX_COMPILER_ID:MSVC>:/utf-8>" "$<$<CXX_COMPILER_ID:MSVC>:/std:c++20>")
|
||||
|
||||
add_compile_options("$<$<C_COMPILER_ID:Clang>:-DNOMINMAX>")
|
||||
add_compile_options("$<$<CXX_COMPILER_ID:Clang>:-DNOMINMAX>")
|
||||
if ( MSVC )
|
||||
set_target_properties( dynarmic PROPERTIES ARCHIVE_OUTPUT_DIRECTORY_DEBUG ${CMAKE_INSTALL_PREFIX} )
|
||||
set_target_properties( dynarmic PROPERTIES ARCHIVE_OUTPUT_DIRECTORY_RELEASE ${CMAKE_INSTALL_PREFIX} )
|
||||
|
||||
set_target_properties( mcl PROPERTIES ARCHIVE_OUTPUT_DIRECTORY_DEBUG ${CMAKE_INSTALL_PREFIX} )
|
||||
set_target_properties( mcl PROPERTIES ARCHIVE_OUTPUT_DIRECTORY_RELEASE ${CMAKE_INSTALL_PREFIX} )
|
||||
endif ()
|
||||
|
||||
# 03 - Dynarmic
|
||||
set(DYNARMIC_TESTS OFF CACHE BOOL "")
|
||||
set(DYNARMIC_FRONTENDS "A32;A64" CACHE STRING "")
|
||||
set(DYNARMIC_USE_PRECOMPILED_HEADERS ON CACHE BOOL "")
|
||||
add_subdirectory("${PROJECT_SOURCE_DIR}/externals/dynarmic")
|
||||
add_definitions(-D_CRT_SECURE_NO_WARNINGS -DNOMINMAX)
|
||||
|
||||
# 03 - Externals
|
||||
add_subdirectory(externals)
|
||||
|
||||
# 04 - My code
|
||||
add_subdirectory(src)
|
||||
|
||||
# 05 - Generate Include
|
||||
include(GenerateExportHeader)
|
||||
generate_export_header(dynemu EXPORT_FILE_NAME include/dynemu/export.h)
|
||||
target_compile_definitions(
|
||||
dynemu PUBLIC "$<$<NOT:$<BOOL:${BUILD_SHARED_LIBS}>>:DYNEMU_STATIC_DEFINE>")
|
||||
target_include_directories(
|
||||
dynemu PUBLIC "$<BUILD_INTERFACE:${CMAKE_CURRENT_BINARY_DIR}/include>")
|
||||
|
||||
# 06 - Package
|
||||
string(COMPARE EQUAL "${CMAKE_SOURCE_DIR}" "${CMAKE_CURRENT_SOURCE_DIR}" is_top_level)
|
||||
option(Dynemu_INCLUDE_PACKAGING "Include packaging rules for Dynemu" "${is_top_level}")
|
||||
if (Dynemu_INCLUDE_PACKAGING)
|
||||
add_subdirectory(pack)
|
||||
endif ()
|
||||
|
||||
#add_library(dynarmic STATIC IMPORTED)
|
||||
|
||||
# set_target_properties(dynarmic PROPERTIES
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
r"""Wrapper for bindings.h
|
||||
|
||||
Generated with:
|
||||
C:\Users\Daffin Backup\AppData\Roaming\Python\Python312\Scripts\ctypesgen -ldynemu_shared bindings.h -o ..\dynemu.py --insert-file=bindings.py
|
||||
C:\Users\Daffin Backup\AppData\Roaming\python\python312\scripts\ctypesgen --cpp=clang -E -ldynemu_shared bindings.h -o ..\dynemu.py --insert-file=bindings.py
|
||||
|
||||
Do not modify this file.
|
||||
"""
|
||||
|
|
@ -867,13 +867,13 @@ _libs["dynemu_shared"] = load_library("dynemu_shared")
|
|||
|
||||
# No modules
|
||||
|
||||
uint8_t = c_ubyte# C:/Strawberry/c/x86_64-w64-mingw32/include/stdint.h: 36
|
||||
uint8_t = c_ubyte# D:\\Daffin\\vs_com_2022\\VC\\Tools\\MSVC\\14.42.34433\\include\\stdint.h: 22
|
||||
|
||||
uint16_t = c_ushort# C:/Strawberry/c/x86_64-w64-mingw32/include/stdint.h: 38
|
||||
uint16_t = c_ushort# D:\\Daffin\\vs_com_2022\\VC\\Tools\\MSVC\\14.42.34433\\include\\stdint.h: 23
|
||||
|
||||
uint32_t = c_uint# C:/Strawberry/c/x86_64-w64-mingw32/include/stdint.h: 40
|
||||
uint32_t = c_uint# D:\\Daffin\\vs_com_2022\\VC\\Tools\\MSVC\\14.42.34433\\include\\stdint.h: 24
|
||||
|
||||
uint64_t = c_ulonglong# C:/Strawberry/c/x86_64-w64-mingw32/include/stdint.h: 42
|
||||
uint64_t = c_ulonglong# D:\\Daffin\\vs_com_2022\\VC\\Tools\\MSVC\\14.42.34433\\include\\stdint.h: 25
|
||||
|
||||
u8 = uint8_t# D:\\Daffin\\TheNewMobileTools\\subtask\\c_subproj\\dynemu\\bindings\\python\\src\\bindings.h: 3
|
||||
|
||||
|
|
|
|||
|
|
@ -1,2 +1,2 @@
|
|||
@echo off
|
||||
ctypesgen.exe -ldynemu_shared bindings.h -o ..\dynemu.py --insert-file=bindings.py
|
||||
ctypesgen -cpp="clang -E" -ldynemu_shared bindings.h -o ..\dynemu.py --insert-file=bindings.py
|
||||
21
externals/CMakeLists.txt
vendored
Normal file
21
externals/CMakeLists.txt
vendored
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
# Always build externals as static libraries, even when dynarmic is built as shared
|
||||
if (BUILD_SHARED_LIBS)
|
||||
set(BUILD_SHARED_LIBS OFF)
|
||||
set(CMAKE_POSITION_INDEPENDENT_CODE ON)
|
||||
set_property(DIRECTORY PROPERTY EXCLUDE_FROM_ALL ON)
|
||||
endif()
|
||||
|
||||
# Allow options shadowing with normal variables when subproject use old cmake policy
|
||||
set(CMAKE_POLICY_DEFAULT_CMP0077 NEW)
|
||||
|
||||
# Disable tests in all externals supporting the standard option name
|
||||
set(BUILD_TESTING OFF)
|
||||
|
||||
# For libraries that already come with a CMakeLists file,
|
||||
# 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 "")
|
||||
add_subdirectory(dynarmic)
|
||||
42
include/dynemu/libdynemu.h
Normal file
42
include/dynemu/libdynemu.h
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
#pragma once
|
||||
#include <stdint.h>
|
||||
#include "dynemu/export.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
using namespace Dynemu;
|
||||
|
||||
extern "C" {
|
||||
#endif
|
||||
typedef uint8_t u8;
|
||||
typedef uint16_t u16;
|
||||
typedef uint32_t u32;
|
||||
typedef uint64_t u64;
|
||||
|
||||
#ifndef __cplusplus
|
||||
typedef uint8_t bool;
|
||||
typedef struct Emulator Emulator;
|
||||
#endif
|
||||
|
||||
DYNEMU_EXPORT Emulator *dynemu_open();
|
||||
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 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 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_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_read_reg(Emulator *e, u8 reg);
|
||||
DYNEMU_EXPORT void dynemu_write_reg(Emulator *e, u8 reg, u32 value);
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
282
include/dynemu/sys_emulator.hpp
Normal file
282
include/dynemu/sys_emulator.hpp
Normal file
|
|
@ -0,0 +1,282 @@
|
|||
/* (c) 2025 Wrapper Inc. - The New Emulation Library */
|
||||
|
||||
#pragma once
|
||||
#include <format>
|
||||
#include "dynarmic/interface/A32/a32.h"
|
||||
#include "dynarmic/interface/exclusive_monitor.h"
|
||||
#include "dynemu/export.h"
|
||||
#include <iostream>
|
||||
|
||||
using u8 = std::uint8_t;
|
||||
using u16 = std::uint16_t;
|
||||
using u32 = std::uint32_t;
|
||||
using u64 = std::uint64_t;
|
||||
|
||||
namespace Dynemu {
|
||||
struct MemMap final {
|
||||
u64 start;
|
||||
u64 size;
|
||||
std::unique_ptr<u8[]> data;
|
||||
};
|
||||
|
||||
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);
|
||||
|
||||
public:
|
||||
std::array<MemMap, 64> mem_map;
|
||||
u32 map_index;
|
||||
u32 os_offset;
|
||||
Dynarmic::A32::Jit *cpu;
|
||||
|
||||
/*
|
||||
MyEmulator()
|
||||
Constructor
|
||||
*/
|
||||
MyEmulator() : map_index(0), os_offset(0xa8000000), mem_map() {};
|
||||
|
||||
/*
|
||||
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)
|
||||
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);
|
||||
|
||||
/*
|
||||
bool UploadFLSFromBytes(const u8 *fls)
|
||||
This function uploads a multi-part code file from memory.
|
||||
Returns:
|
||||
0: NOK
|
||||
1: OK
|
||||
*/
|
||||
bool UploadFLSFromBytes(const u8 *fls);
|
||||
|
||||
/*
|
||||
bool UploadFLSFromBytes(const std::array<u8, N> &fls)
|
||||
This function uploads a multi-part code file from memory.
|
||||
Returns:
|
||||
0: NOK
|
||||
1: OK
|
||||
*/
|
||||
template <std::size_t N>
|
||||
bool UploadFLSFromBytes(const std::array<u8, N> &fls) {
|
||||
return UploadFLSFromBytes(fls.data());
|
||||
}
|
||||
|
||||
/*
|
||||
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.
|
||||
*/
|
||||
void UploadOSStub(u32 dest, u32 mem_start, u32 mem_size, bool is_big);
|
||||
|
||||
/*
|
||||
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 mem_start, u32 mem_size, bool is_big) {
|
||||
UploadOSStub(os_offset, mem_start, mem_size, is_big);
|
||||
}
|
||||
|
||||
/*
|
||||
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;
|
||||
|
||||
/*
|
||||
u8 MemoryRead8(u32 vaddr)
|
||||
Reads an 8-bit value from vaddr
|
||||
Returns: byte[vaddr]
|
||||
*/
|
||||
u8 MemoryRead8(u32 vaddr) override;
|
||||
|
||||
/*
|
||||
void MemoryReadBytes(u32 vaddr, u8 *output, u32 size)
|
||||
Copy data from memory map to a single u8 pointer.
|
||||
*/
|
||||
void MemoryReadBytes(u32 vaddr, u8 *output, u32 size);
|
||||
|
||||
/*
|
||||
u16 MemoryRead16(u32 vaddr)
|
||||
Reads a 16-bit value from vaddr
|
||||
Returns: short[vaddr]
|
||||
*/
|
||||
u16 MemoryRead16(u32 vaddr) override;
|
||||
|
||||
/*
|
||||
u32 MemoryRead32(u32 vaddr)
|
||||
Reads a 32-bit value from vaddr
|
||||
Returns: word[vaddr]
|
||||
*/
|
||||
u32 MemoryRead32(u32 vaddr) override;
|
||||
|
||||
/*
|
||||
u64 MemoryRead64(u32 vaddr)
|
||||
Reads a 64-bit value from vaddr
|
||||
Returns: dword[vaddr]
|
||||
*/
|
||||
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)
|
||||
Writes an 8-bit value to vaddr
|
||||
*/
|
||||
void MemoryWrite8(u32 vaddr, u8 value) override;
|
||||
|
||||
/*
|
||||
void MemoryWriteBytes(u32 vaddr, const u8 *input, u32 size)
|
||||
Copy data from a single u8 pointer into memory map.
|
||||
*/
|
||||
void MemoryWriteBytes(u32 vaddr, const u8 *input, u32 size);
|
||||
|
||||
/*
|
||||
void MemoryWriteBytes(u32 vaddr, const std::array<u8, N> &data)
|
||||
Copy data from a single u8 pointer into memory map.
|
||||
*/
|
||||
template <std::size_t N>
|
||||
void MemoryWriteBytes(u32 vaddr, const std::array<u8, N> &data) {
|
||||
MemoryWriteBytes(vaddr, data.data(), N);
|
||||
};
|
||||
|
||||
/*
|
||||
void MemoryWrite16(u32 vaddr, u16 value)
|
||||
Writes an 16-bit value to vaddr
|
||||
*/
|
||||
void MemoryWrite16(u32 vaddr, u16 value) override;
|
||||
|
||||
/*
|
||||
void MemoryWrite32(u32 vaddr, u32 value)
|
||||
Writes an 32-bit value to vaddr
|
||||
*/
|
||||
void MemoryWrite32(u32 vaddr, u32 value) override;
|
||||
|
||||
/*
|
||||
void MemoryWrite64(u32 vaddr, u64 value)
|
||||
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)
|
||||
*/
|
||||
void InterpreterFallback(u32 pc, size_t num_instructions) override {
|
||||
// This is never called in practice.
|
||||
std::terminate();
|
||||
}
|
||||
|
||||
/*
|
||||
void CallSVC(u32 swi)
|
||||
*/
|
||||
void CallSVC(u32 swi) override {
|
||||
// Do something.
|
||||
}
|
||||
|
||||
/*
|
||||
u32 Execute(u32 pc, bool is_big)
|
||||
Executes from specific PC
|
||||
Returns: R0 register
|
||||
*/
|
||||
u32 Execute(u32 pc, bool is_big);
|
||||
|
||||
/*
|
||||
u32 Execute(u32 pc)
|
||||
Executes from specific PC
|
||||
Returns: R0 register
|
||||
*/
|
||||
u32 Execute(u32 pc) {
|
||||
return Execute(pc, false);
|
||||
}
|
||||
|
||||
/*
|
||||
void ExceptionRaised(u32 pc, Dynarmic::A32::Exception exception)
|
||||
*/
|
||||
void ExceptionRaised(u32 pc, Dynarmic::A32::Exception exception) override {
|
||||
std::cerr << std::format("decoder: exception at {:#08x}: ", pc);
|
||||
std::cerr << std::format("{}", (int)exception) << std::endl;
|
||||
std::terminate();
|
||||
// Do something.
|
||||
}
|
||||
|
||||
/*
|
||||
void AddTicks(u64 ticks)
|
||||
*/
|
||||
void AddTicks(u64 ticks) override {}
|
||||
|
||||
/*
|
||||
GetTicksRemaining()
|
||||
*/
|
||||
u64 GetTicksRemaining() override {return 8000;}
|
||||
};
|
||||
|
||||
class DYNEMU_EXPORT Emulator final {
|
||||
private:
|
||||
std::unique_ptr<Dynarmic::A32::Jit> cpu;
|
||||
std::unique_ptr<Dynarmic::ExclusiveMonitor> mon;
|
||||
|
||||
public:
|
||||
MyEmulator env;
|
||||
|
||||
Emulator();
|
||||
};
|
||||
}
|
||||
43
pack/CMakeLists.txt
Normal file
43
pack/CMakeLists.txt
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
include(GNUInstallDirs)
|
||||
include(CMakePackageConfigHelpers)
|
||||
|
||||
if (NOT DEFINED Dynemu_INSTALL_CMAKEDIR)
|
||||
set(Dynemu_INSTALL_CMAKEDIR "${CMAKE_INSTALL_LIBDIR}/cmake/dynemu"
|
||||
CACHE STRING "Path to Dynemu CMake files")
|
||||
endif ()
|
||||
|
||||
install(TARGETS dynemu EXPORT Dynemu_Targets
|
||||
RUNTIME COMPONENT Dynemu_Runtime
|
||||
LIBRARY COMPONENT Dynemu_Runtime
|
||||
NAMELINK_COMPONENT Dynemu_Development
|
||||
ARCHIVE COMPONENT Dynemu_Development
|
||||
INCLUDES DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}")
|
||||
|
||||
install(DIRECTORY "${Dynemu_SOURCE_DIR}/include/" "${Dynemu_BINARY_DIR}/include/"
|
||||
TYPE INCLUDE
|
||||
COMPONENT Dynemu_Development)
|
||||
|
||||
if (BUILD_SHARED_LIBS)
|
||||
set(type shared)
|
||||
else ()
|
||||
set(type static)
|
||||
endif ()
|
||||
|
||||
install(EXPORT Dynemu_Targets
|
||||
DESTINATION "${Dynemu_INSTALL_CMAKEDIR}"
|
||||
NAMESPACE Dynemu::
|
||||
FILE Dynemu-${type}-targets.cmake
|
||||
COMPONENT Dynemu_Development)
|
||||
|
||||
write_basic_package_version_file(
|
||||
DynemuConfigVersion.cmake
|
||||
COMPATIBILITY SameMajorVersion)
|
||||
|
||||
install(FILES
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/DynemuConfig.cmake"
|
||||
"${CMAKE_CURRENT_BINARY_DIR}/DynemuConfigVersion.cmake"
|
||||
DESTINATION "${Dynemu_INSTALL_CMAKEDIR}"
|
||||
COMPONENT Dynemu_Development)
|
||||
|
||||
# TODO: add additional CPack variables here
|
||||
include(CPack)
|
||||
57
pack/DynemuConfig.cmake
Normal file
57
pack/DynemuConfig.cmake
Normal file
|
|
@ -0,0 +1,57 @@
|
|||
cmake_minimum_required(VERSION 3.19)
|
||||
|
||||
set(Dynemu_known_comps static shared)
|
||||
set(Dynemu_comp_static NO)
|
||||
set(Dynemu_comp_shared NO)
|
||||
foreach (Dynemu_comp IN LISTS ${CMAKE_FIND_PACKAGE_NAME}_FIND_COMPONENTS)
|
||||
if (Dynemu_comp IN_LIST Dynemu_known_comps)
|
||||
set(Dynemu_comp_${Dynemu_comp} YES)
|
||||
else ()
|
||||
set(${CMAKE_FIND_PACKAGE_NAME}_NOT_FOUND_MESSAGE
|
||||
"Dynemu does not recognize component `${Dynemu_comp}`.")
|
||||
set(${CMAKE_FIND_PACKAGE_NAME}_FOUND FALSE)
|
||||
return()
|
||||
endif ()
|
||||
endforeach ()
|
||||
|
||||
if (Dynemu_comp_static AND Dynemu_comp_shared)
|
||||
set(${CMAKE_FIND_PACKAGE_NAME}_NOT_FOUND_MESSAGE
|
||||
"Dynemu `static` and `shared` components are mutually exclusive.")
|
||||
set(${CMAKE_FIND_PACKAGE_NAME}_FOUND FALSE)
|
||||
return()
|
||||
endif ()
|
||||
|
||||
set(Dynemu_static_targets "${CMAKE_CURRENT_LIST_DIR}/Dynemu-static-targets.cmake")
|
||||
set(Dynemu_shared_targets "${CMAKE_CURRENT_LIST_DIR}/Dynemu-shared-targets.cmake")
|
||||
|
||||
macro(Dynemu_load_targets type)
|
||||
if (NOT EXISTS "${Dynemu_${type}_targets}")
|
||||
set(${CMAKE_FIND_PACKAGE_NAME}_NOT_FOUND_MESSAGE
|
||||
"Dynemu `${type}` libraries were requested but not found.")
|
||||
set(${CMAKE_FIND_PACKAGE_NAME}_FOUND FALSE)
|
||||
return()
|
||||
endif ()
|
||||
include("${Dynemu_${type}_targets}")
|
||||
endmacro()
|
||||
|
||||
if (Dynemu_comp_static)
|
||||
Dynemu_load_targets(static)
|
||||
elseif (Dynemu_comp_shared)
|
||||
Dynemu_load_targets(shared)
|
||||
elseif (DEFINED Dynemu_SHARED_LIBS AND Dynemu_SHARED_LIBS)
|
||||
Dynemu_load_targets(shared)
|
||||
elseif (DEFINED Dynemu_SHARED_LIBS AND NOT Dynemu_SHARED_LIBS)
|
||||
Dynemu_load_targets(static)
|
||||
elseif (BUILD_SHARED_LIBS)
|
||||
if (EXISTS "${Dynemu_shared_targets}")
|
||||
Dynemu_load_targets(shared)
|
||||
else ()
|
||||
Dynemu_load_targets(static)
|
||||
endif ()
|
||||
else ()
|
||||
if (EXISTS "${Dynemu_static_targets}")
|
||||
Dynemu_load_targets(static)
|
||||
else ()
|
||||
Dynemu_load_targets(shared)
|
||||
endif ()
|
||||
endif ()
|
||||
|
|
@ -1,15 +1,36 @@
|
|||
add_library(dynemu OBJECT libdynemu.cpp)
|
||||
add_definitions(-DEXPORT_DLL)
|
||||
|
||||
add_library(dynemu libdynemu.cpp sys_emulator.cpp)
|
||||
add_library(dynemu::Dynemu ALIAS dynemu)
|
||||
set_target_properties(dynemu PROPERTIES
|
||||
VERSION ${Dynemu_VERSION}
|
||||
SOVERSION ${Dynemu_VERSION_MAJOR})
|
||||
add_dependencies(dynemu dynarmic mcl)
|
||||
target_link_libraries(dynemu PUBLIC dynarmic mcl)
|
||||
|
||||
set_property(TARGET dynemu PROPERTY POSITION_INDEPENDENT_CODE 1)
|
||||
target_include_directories(dynemu PUBLIC "$<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/include>")
|
||||
target_compile_features(dynemu PUBLIC cxx_std_20)
|
||||
|
||||
# shared and static libraries built from the same object files
|
||||
add_library(dynemu_shared SHARED $<TARGET_OBJECTS:dynemu>)
|
||||
add_library(dynemu_static STATIC $<TARGET_OBJECTS:dynemu>)
|
||||
# set(EXPORT_HEADERS
|
||||
# "libdynemu.h"
|
||||
# "sys_emulator.hpp"
|
||||
# )
|
||||
|
||||
add_dependencies(dynemu_shared dynarmic mcl)
|
||||
target_link_libraries(dynemu_shared PUBLIC dynarmic mcl)
|
||||
# set_property(TARGET dynemu PROPERTY POSITION_INDEPENDENT_CODE 1)
|
||||
# set_target_properties(dynemu PROPERTIES PUBLIC_HEADER "${EXPORT_HEADERS}")
|
||||
|
||||
add_dependencies(dynemu_static dynarmic mcl)
|
||||
target_link_libraries(dynemu_static PUBLIC dynarmic mcl)
|
||||
# # shared and static libraries built from the same object files
|
||||
# add_library(dynemu_shared SHARED $<TARGET_OBJECTS:dynemu>)
|
||||
# add_library(dynemu_static STATIC $<TARGET_OBJECTS:dynemu>)
|
||||
|
||||
# set_property(TARGET dynemu_shared PROPERTY OUTPUT_NAME dynemu)
|
||||
# set_property(TARGET dynemu_static PROPERTY OUTPUT_NAME dynemu)
|
||||
|
||||
# add_dependencies(dynemu_shared dynarmic mcl)
|
||||
# target_link_libraries(dynemu_shared PUBLIC dynarmic mcl)
|
||||
|
||||
# add_dependencies(dynemu_static dynarmic mcl)
|
||||
# target_link_libraries(dynemu_static PUBLIC dynarmic mcl)
|
||||
|
||||
# install(TARGETS dynemu_shared LIBRARY DESTINATION .)
|
||||
# install(TARGETS dynemu_static ARCHIVE DESTINATION .)
|
||||
|
|
@ -1,7 +1,9 @@
|
|||
#include "shared/sys_emulator.hpp"
|
||||
#include "libdynemu.hpp"
|
||||
#include "dynemu/sys_emulator.hpp"
|
||||
using namespace Dynemu;
|
||||
|
||||
extern "C" {
|
||||
#include "dynemu/libdynemu.h"
|
||||
|
||||
Emulator *dynemu_open() {
|
||||
auto temp = new Emulator();
|
||||
return temp;
|
||||
|
|
|
|||
|
|
@ -1,40 +0,0 @@
|
|||
#include <cstdint>
|
||||
#pragma once
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
// Microsoft
|
||||
#define EXPORT __declspec(dllexport)
|
||||
#define IMPORT __declspec(dllimport)
|
||||
#elif defined(__GNUC__)
|
||||
// GCC
|
||||
#define EXPORT __attribute__((visibility("default")))
|
||||
#define IMPORT
|
||||
#else
|
||||
// do nothing and hope for the best?
|
||||
#define EXPORT
|
||||
#define IMPORT
|
||||
#pragma warning Unknown dynamic link import/export semantics.
|
||||
#endif
|
||||
|
||||
extern "C" {
|
||||
EXPORT Emulator *dynemu_open();
|
||||
EXPORT void dynemu_delete(Emulator *e);
|
||||
EXPORT void dynemu_mmap(Emulator *e, u32 vaddr, u32 size);
|
||||
EXPORT size_t dynemu_upload_file(Emulator *e, const char *filename, int64_t dest, u32 size);
|
||||
EXPORT bool dynemu_upload_fls_memory(Emulator *e, const u8 *fls);
|
||||
EXPORT void dynemu_upload_memstub(Emulator *e, u32 dest, u32 mem_start, u32 mem_size, bool is_big);
|
||||
EXPORT void dynemu_upload_memstub_nooffset(Emulator *e, u32 mem_start, u32 mem_size, bool is_big);
|
||||
EXPORT u8 dynemu_read_u8(Emulator *e, u32 vaddr);
|
||||
EXPORT u16 dynemu_read_u16(Emulator *e, u32 vaddr, bool is_big);
|
||||
EXPORT u32 dynemu_read_u32(Emulator *e, u32 vaddr, bool is_big);
|
||||
EXPORT u64 dynemu_read_u64(Emulator *e, u32 vaddr, bool is_big);
|
||||
EXPORT void dynemu_write_u8(Emulator *e, u32 vaddr, u8 value);
|
||||
EXPORT void dynemu_write_u16(Emulator *e, u32 vaddr, u16 value, bool is_big);
|
||||
EXPORT void dynemu_write_u32(Emulator *e, u32 vaddr, u32 value, bool is_big);
|
||||
EXPORT void dynemu_write_u64(Emulator *e, u32 vaddr, u64 value, bool is_big);
|
||||
EXPORT void dynemu_read_bytes(Emulator *e, u32 vaddr, u8 *output, u32 size);
|
||||
EXPORT void dynemu_write_bytes(Emulator *e, u32 vaddr, const u8 *input, u32 size);
|
||||
EXPORT u32 dynemu_execute(Emulator *e, u32 pc, bool is_big);
|
||||
EXPORT u32 dynemu_read_reg(Emulator *e, u8 reg);
|
||||
EXPORT void dynemu_write_reg(Emulator *e, u8 reg, u32 value);
|
||||
}
|
||||
303
src/lib/sys_emulator.cpp
Normal file
303
src/lib/sys_emulator.cpp
Normal file
|
|
@ -0,0 +1,303 @@
|
|||
/* (c) 2025 Wrapper Inc. - The New Emulation Library */
|
||||
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
#include <format>
|
||||
|
||||
#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/exclusive_monitor.h"
|
||||
|
||||
#include "mcl/assert.hpp"
|
||||
|
||||
#define DEBUG 0
|
||||
#define BLOCK_ALLIGN 0x10000
|
||||
|
||||
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 */
|
||||
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::cerr << std::format("Memory map {:#08x} OOB, Emulation cannot continue", vaddr) << std::endl;
|
||||
throw std::out_of_range("Data abort");
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/* 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)};
|
||||
}
|
||||
|
||||
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) {
|
||||
std::ifstream file(filename, std::ios::binary);
|
||||
u8 buf[2048];
|
||||
u32 offset = 0;
|
||||
int to_read = 0;
|
||||
|
||||
if (!file.is_open()) {
|
||||
std::cerr << std::format("Unable to open {}: {}", 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);
|
||||
|
||||
if (size < fileSize && !is_mmap_exists((u32)dest)) {
|
||||
std::cerr << "Code file too large to fit into memory" << std::endl;
|
||||
return 0;
|
||||
}
|
||||
|
||||
CreateMemoryMap((u32)dest, 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;
|
||||
}
|
||||
|
||||
return fileSize;
|
||||
} else {
|
||||
FlashLoader fl{};
|
||||
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;
|
||||
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);
|
||||
|
||||
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));
|
||||
MemoryWriteBytes(fl.data.get()[i].start_vaddr, fl.data.get()[i].data.get(), fl.data.get()[i].size);
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
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 << "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, fl.data.get()[i].size + ((fl.data.get()[i].size % BLOCK_ALLIGN) ? (BLOCK_ALLIGN - (fl.data.get()[i].size % BLOCK_ALLIGN)) : 0));
|
||||
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) {
|
||||
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);
|
||||
}
|
||||
os_offset = dest;
|
||||
}
|
||||
|
||||
bool MyEmulator::PreCodeReadHook(bool is_thumb, u32 pc, Dynarmic::A32::IREmitter& ir) {
|
||||
#if DEBUG
|
||||
std::cout << std::format("executing at {:#08x}", pc) << std::endl;
|
||||
std::cout << std::format("REG P0 {:#08x}", cpu->Regs()[0]) << std::endl;
|
||||
std::cout << std::format("REG P1 {:#08x}", cpu->Regs()[1]) << std::endl;
|
||||
std::cout << std::format("REG P2 {:#08x}", cpu->Regs()[2]) << std::endl;
|
||||
std::cout << std::format("REG P12 {:#08x}", cpu->Regs()[12]) << std::endl;
|
||||
std::cout << std::format("REG P15 {:#08x}", cpu->Regs()[15]) << std::endl;
|
||||
#endif
|
||||
|
||||
if (pc == (os_offset + ROUTE_EXIT)) cpu->HaltExecution();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
u8 MyEmulator::MemoryRead8(u32 vaddr) {
|
||||
#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);
|
||||
}
|
||||
|
||||
u16 MyEmulator::MemoryRead16(u32 vaddr) {
|
||||
return u16(MemoryRead8(vaddr)) | u16(MemoryRead8(vaddr + 1)) << 8;
|
||||
}
|
||||
|
||||
u32 MyEmulator::MemoryRead32(u32 vaddr) {
|
||||
return u32(MemoryRead16(vaddr)) | u32(MemoryRead16(vaddr + 2)) << 16;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
void MyEmulator::MemoryWrite8(u32 vaddr, u8 value) {
|
||||
#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);
|
||||
}
|
||||
|
||||
void MyEmulator::MemoryWrite16(u32 vaddr, u16 value) {
|
||||
MemoryWrite8(vaddr, u8(value));
|
||||
MemoryWrite8(vaddr + 1, u8(value >> 8));
|
||||
}
|
||||
|
||||
void MyEmulator::MemoryWrite32(u32 vaddr, u32 value) {
|
||||
MemoryWrite16(vaddr, u16(value));
|
||||
MemoryWrite16(vaddr + 2, u16(value >> 16));
|
||||
}
|
||||
|
||||
void MyEmulator::MemoryWrite64(u32 vaddr, u64 value) {
|
||||
MemoryWrite32(vaddr, u32(value));
|
||||
MemoryWrite32(vaddr + 4, u32(value >> 32));
|
||||
}
|
||||
|
||||
void MyEmulator::MemoryWrite16_Big(u32 vaddr, u16 value) {
|
||||
MemoryWrite8(vaddr + 1, u8(value));
|
||||
MemoryWrite8(vaddr, u8(value >> 8));
|
||||
}
|
||||
|
||||
void MyEmulator::MemoryWrite32_Big(u32 vaddr, u32 value) {
|
||||
MemoryWrite16_Big(vaddr + 2, u16(value));
|
||||
MemoryWrite16_Big(vaddr, u16(value >> 16));
|
||||
}
|
||||
|
||||
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;
|
||||
cpu->Regs()[15] = pc & ~1;
|
||||
cpu->SetCpsr(((pc & 1) ? 0x30 : 0x1d0) | (is_big ? 0x200 : 0x0));
|
||||
|
||||
Dynarmic::HaltReason returnCode = cpu->Run();
|
||||
cpu->ClearCache();
|
||||
|
||||
return cpu->Regs()[0];
|
||||
}
|
||||
|
||||
Emulator::Emulator() {
|
||||
Dynarmic::A32::UserConfig user_config;
|
||||
|
||||
user_config.callbacks = &env;
|
||||
user_config.enable_cycle_counting = false;
|
||||
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();
|
||||
};
|
||||
}
|
||||
|
|
@ -1,511 +0,0 @@
|
|||
/* (c) 2025 Wrapper Inc. - The New Emulation Library */
|
||||
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
#include <format>
|
||||
|
||||
#include "os_wrapper.hpp"
|
||||
|
||||
#include "dynarmic/interface/A32/a32.h"
|
||||
#include "dynarmic/interface/A32/config.h"
|
||||
#include "dynarmic/interface/exclusive_monitor.h"
|
||||
|
||||
#include "mcl/assert.hpp"
|
||||
|
||||
using u8 = std::uint8_t;
|
||||
using u16 = std::uint16_t;
|
||||
using u32 = std::uint32_t;
|
||||
using u64 = std::uint64_t;
|
||||
|
||||
#define DEBUG 0
|
||||
#define BLOCK_ALLIGN 0x10000
|
||||
|
||||
class MemMap final {
|
||||
public:
|
||||
u64 start;
|
||||
u64 size;
|
||||
std::unique_ptr<u8[]> data;
|
||||
};
|
||||
|
||||
struct FlashPartition {
|
||||
u32 start_vaddr;
|
||||
u32 size;
|
||||
std::unique_ptr<u8[]> data;
|
||||
};
|
||||
|
||||
struct FlashLoader {
|
||||
u16 magic;
|
||||
u32 count;
|
||||
std::unique_ptr<FlashPartition[]> data;
|
||||
};
|
||||
|
||||
class MyEmulator final : public Dynarmic::A32::UserCallbacks {
|
||||
private:
|
||||
u8 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::cerr << std::format("Memory map {:#08x} OOB, Emulation cannot continue", vaddr) << std::endl;
|
||||
throw std::out_of_range("Data abort");
|
||||
}
|
||||
|
||||
bool 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;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
template <std::size_t N>
|
||||
bool CheckExecOverridePC(const std::array<u32, N> &list, u32 pc) {
|
||||
for (int i = 0; i < N; i++) {
|
||||
if (pc == list[i]) return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public:
|
||||
std::array<MemMap, 64> mem_map{};
|
||||
u32 map_index = 0;
|
||||
u32 os_offset = 0xa8000000;
|
||||
Dynarmic::A32::Jit *cpu;
|
||||
|
||||
/*
|
||||
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) {
|
||||
if (!is_mmap_exists(vaddr)) mem_map[map_index++] = {vaddr, size, std::make_unique<u8[]>(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) {
|
||||
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 UploadToMemory(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) {
|
||||
std::ifstream file(filename, std::ios::binary);
|
||||
u8 buf[2048];
|
||||
u32 offset = 0;
|
||||
int to_read = 0;
|
||||
|
||||
if (!file.is_open()) {
|
||||
std::cerr << std::format("Unable to open {}: {}", 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);
|
||||
|
||||
if (size < fileSize && !is_mmap_exists((u32)dest)) {
|
||||
std::cerr << "Code file too large to fit into memory" << std::endl;
|
||||
return 0;
|
||||
}
|
||||
|
||||
CreateMemoryMap((u32)dest, 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;
|
||||
}
|
||||
|
||||
return fileSize;
|
||||
} else {
|
||||
FlashLoader fl{};
|
||||
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;
|
||||
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);
|
||||
|
||||
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));
|
||||
MemoryWriteBytes(fl.data.get()[i].start_vaddr, fl.data.get()[i].data.get(), fl.data.get()[i].size);
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
bool UploadFLSFromBytes(const u8 *fls)
|
||||
This function uploads a multi-part code file from memory.
|
||||
Returns:
|
||||
0: NOK
|
||||
1: OK
|
||||
*/
|
||||
bool 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 << "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, fl.data.get()[i].size + ((fl.data.get()[i].size % BLOCK_ALLIGN) ? (BLOCK_ALLIGN - (fl.data.get()[i].size % BLOCK_ALLIGN)) : 0));
|
||||
MemoryWriteBytes(fl.data.get()[i].start_vaddr, fl.data.get()[i].data.get(), fl.data.get()[i].size);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
bool UploadFLSFromBytes(const std::array<u8, N> &fls)
|
||||
This function uploads a multi-part code file from memory.
|
||||
Returns:
|
||||
0: NOK
|
||||
1: OK
|
||||
*/
|
||||
template <std::size_t N>
|
||||
bool UploadFLSFromBytes(const std::array<u8, N> &fls) {
|
||||
return UploadFLSFromBytes(fls.data());
|
||||
}
|
||||
|
||||
/*
|
||||
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.
|
||||
*/
|
||||
void UploadOSStub(u32 dest, u32 mem_start, u32 mem_size, bool is_big) {
|
||||
if (is_big) {
|
||||
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);
|
||||
}
|
||||
os_offset = dest;
|
||||
}
|
||||
|
||||
/*
|
||||
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 mem_start, u32 mem_size, bool is_big) {
|
||||
UploadOSStub(os_offset, mem_start, mem_size, is_big);
|
||||
}
|
||||
|
||||
/*
|
||||
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 {
|
||||
#if DEBUG
|
||||
std::cout << std::format("executing at {:#08x}", pc) << std::endl;
|
||||
std::cout << std::format("REG P0 {:#08x}", cpu->Regs()[0]) << std::endl;
|
||||
std::cout << std::format("REG P1 {:#08x}", cpu->Regs()[1]) << std::endl;
|
||||
std::cout << std::format("REG P2 {:#08x}", cpu->Regs()[2]) << std::endl;
|
||||
std::cout << std::format("REG P12 {:#08x}", cpu->Regs()[12]) << std::endl;
|
||||
std::cout << std::format("REG P15 {:#08x}", cpu->Regs()[15]) << std::endl;
|
||||
#endif
|
||||
|
||||
if (pc == (os_offset + ROUTE_EXIT)) cpu->HaltExecution();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
u8 MemoryRead8(u32 vaddr)
|
||||
Reads an 8-bit value from vaddr
|
||||
Returns: byte[vaddr]
|
||||
*/
|
||||
u8 MemoryRead8(u32 vaddr) override {
|
||||
#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 MemoryReadBytes(u32 vaddr, u8 *output, u32 size)
|
||||
Copy data from memory map to a single u8 pointer.
|
||||
*/
|
||||
void 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);
|
||||
}
|
||||
|
||||
/*
|
||||
u16 MemoryRead16(u32 vaddr)
|
||||
Reads a 16-bit value from vaddr
|
||||
Returns: short[vaddr]
|
||||
*/
|
||||
u16 MemoryRead16(u32 vaddr) override {
|
||||
return u16(MemoryRead8(vaddr)) | u16(MemoryRead8(vaddr + 1)) << 8;
|
||||
}
|
||||
|
||||
/*
|
||||
u32 MemoryRead32(u32 vaddr)
|
||||
Reads a 32-bit value from vaddr
|
||||
Returns: word[vaddr]
|
||||
*/
|
||||
u32 MemoryRead32(u32 vaddr) override {
|
||||
return u32(MemoryRead16(vaddr)) | u32(MemoryRead16(vaddr + 2)) << 16;
|
||||
}
|
||||
|
||||
/*
|
||||
u64 MemoryRead64(u32 vaddr)
|
||||
Reads a 64-bit value from vaddr
|
||||
Returns: dword[vaddr]
|
||||
*/
|
||||
u64 MemoryRead64(u32 vaddr) override {
|
||||
return u64(MemoryRead32(vaddr)) | u64(MemoryRead32(vaddr + 4)) << 32;
|
||||
}
|
||||
|
||||
/*
|
||||
u16 MemoryRead16_Big(u32 vaddr)
|
||||
Reads a 16-bit value from vaddr
|
||||
Returns: short[vaddr]
|
||||
*/
|
||||
u16 MemoryRead16_Big(u32 vaddr) {
|
||||
return u16(MemoryRead8(vaddr + 1)) | u16(MemoryRead8(vaddr)) << 8;
|
||||
}
|
||||
|
||||
/*
|
||||
u32 MemoryRead32_Big(u32 vaddr)
|
||||
Reads a 32-bit value from vaddr
|
||||
Returns: word[vaddr]
|
||||
*/
|
||||
u32 MemoryRead32_Big(u32 vaddr) {
|
||||
return u32(MemoryRead16_Big(vaddr + 2)) | u32(MemoryRead16_Big(vaddr)) << 16;
|
||||
}
|
||||
|
||||
/*
|
||||
u64 MemoryRead64_Big(u32 vaddr)
|
||||
Reads a 64-bit value from vaddr
|
||||
Returns: dword[vaddr]
|
||||
*/
|
||||
u64 MemoryRead64_Big(u32 vaddr) {
|
||||
return u64(MemoryRead32_Big(vaddr + 4)) | u64(MemoryRead32_Big(vaddr)) << 32;
|
||||
}
|
||||
|
||||
/*
|
||||
void MemoryWrite8(u32 vaddr, u8 value)
|
||||
Writes an 8-bit value to vaddr
|
||||
*/
|
||||
void MemoryWrite8(u32 vaddr, u8 value) override {
|
||||
#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 MemoryWriteBytes(u32 vaddr, const u8 *input, u32 size)
|
||||
Copy data from a single u8 pointer into memory map.
|
||||
*/
|
||||
void 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);
|
||||
}
|
||||
|
||||
/*
|
||||
void MemoryWriteBytes(u32 vaddr, const std::array<u8, N> &data)
|
||||
Copy data from a single u8 pointer into memory map.
|
||||
*/
|
||||
template <std::size_t N>
|
||||
void MemoryWriteBytes(u32 vaddr, const std::array<u8, N> &data) {
|
||||
MemoryWriteBytes(vaddr, data.data(), N);
|
||||
}
|
||||
|
||||
/*
|
||||
void MemoryWrite16(u32 vaddr, u16 value)
|
||||
Writes an 16-bit value to vaddr
|
||||
*/
|
||||
void MemoryWrite16(u32 vaddr, u16 value) override {
|
||||
MemoryWrite8(vaddr, u8(value));
|
||||
MemoryWrite8(vaddr + 1, u8(value >> 8));
|
||||
}
|
||||
|
||||
/*
|
||||
void MemoryWrite32(u32 vaddr, u32 value)
|
||||
Writes an 32-bit value to vaddr
|
||||
*/
|
||||
void MemoryWrite32(u32 vaddr, u32 value) override {
|
||||
MemoryWrite16(vaddr, u16(value));
|
||||
MemoryWrite16(vaddr + 2, u16(value >> 16));
|
||||
}
|
||||
|
||||
/*
|
||||
void MemoryWrite64(u32 vaddr, u64 value)
|
||||
Writes an 64-bit value to vaddr
|
||||
*/
|
||||
void MemoryWrite64(u32 vaddr, u64 value) override {
|
||||
MemoryWrite32(vaddr, u32(value));
|
||||
MemoryWrite32(vaddr + 4, u32(value >> 32));
|
||||
}
|
||||
|
||||
/*
|
||||
void MemoryWrite16_Big(u32 vaddr, u16 value)
|
||||
Writes an 16-bit value to vaddr
|
||||
*/
|
||||
void MemoryWrite16_Big(u32 vaddr, u16 value) {
|
||||
MemoryWrite8(vaddr + 1, u8(value));
|
||||
MemoryWrite8(vaddr, u8(value >> 8));
|
||||
}
|
||||
|
||||
/*
|
||||
void MemoryWrite32_Big(u32 vaddr, u32 value)
|
||||
Writes an 32-bit value to vaddr
|
||||
*/
|
||||
void MemoryWrite32_Big(u32 vaddr, u32 value) {
|
||||
MemoryWrite16_Big(vaddr + 2, u16(value));
|
||||
MemoryWrite16_Big(vaddr, u16(value >> 16));
|
||||
}
|
||||
|
||||
/*
|
||||
void MemoryWrite64_Big(u32 vaddr, u64 value)
|
||||
Writes an 64-bit value to vaddr
|
||||
*/
|
||||
void MemoryWrite64_Big(u32 vaddr, u64 value) {
|
||||
MemoryWrite32_Big(vaddr + 4, u32(value));
|
||||
MemoryWrite32_Big(vaddr, u32(value >> 32));
|
||||
}
|
||||
|
||||
/*
|
||||
void InterpreterFallback(u32 pc, size_t num_instructions)
|
||||
*/
|
||||
void InterpreterFallback(u32 pc, size_t num_instructions) override {
|
||||
// This is never called in practice.
|
||||
std::terminate();
|
||||
}
|
||||
|
||||
/*
|
||||
void CallSVC(u32 swi)
|
||||
*/
|
||||
void CallSVC(u32 swi) override {
|
||||
// Do something.
|
||||
}
|
||||
|
||||
/*
|
||||
u32 Execute(u32 pc, bool is_big)
|
||||
Executes from specific PC
|
||||
Returns: R0 register
|
||||
*/
|
||||
u32 Execute(u32 pc, bool is_big) {
|
||||
cpu->Regs()[14] = os_offset + ROUTE_EXIT;
|
||||
cpu->Regs()[15] = pc & ~1;
|
||||
cpu->SetCpsr(((pc & 1) ? 0x30 : 0x1d0) | (is_big ? 0x200 : 0x0));
|
||||
|
||||
Dynarmic::HaltReason returnCode = cpu->Run();
|
||||
cpu->ClearCache();
|
||||
|
||||
return cpu->Regs()[0];
|
||||
}
|
||||
|
||||
/*
|
||||
u32 Execute(u32 pc)
|
||||
Executes from specific PC
|
||||
Returns: R0 register
|
||||
*/
|
||||
u32 Execute(u32 pc) {
|
||||
return Execute(pc, false);
|
||||
}
|
||||
|
||||
/*
|
||||
void ExceptionRaised(u32 pc, Dynarmic::A32::Exception exception)
|
||||
*/
|
||||
void ExceptionRaised(u32 pc, Dynarmic::A32::Exception exception) override {
|
||||
std::cerr << std::format("decoder: exception at {:#08x}: ", pc);
|
||||
std::cerr << std::format("{}", (int)exception) << std::endl;
|
||||
std::terminate();
|
||||
// Do something.
|
||||
}
|
||||
|
||||
/*
|
||||
void AddTicks(u64 ticks)
|
||||
*/
|
||||
void AddTicks(u64 ticks) override {}
|
||||
|
||||
/*
|
||||
GetTicksRemaining()
|
||||
*/
|
||||
u64 GetTicksRemaining() override {return 8000;}
|
||||
};
|
||||
|
||||
class Emulator {
|
||||
private:
|
||||
std::unique_ptr<Dynarmic::A32::Jit> cpu;
|
||||
std::unique_ptr<Dynarmic::ExclusiveMonitor> mon;
|
||||
|
||||
public:
|
||||
MyEmulator env;
|
||||
|
||||
Emulator() {
|
||||
Dynarmic::A32::UserConfig user_config;
|
||||
|
||||
user_config.callbacks = &env;
|
||||
user_config.enable_cycle_counting = false;
|
||||
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();
|
||||
};
|
||||
};
|
||||
Loading…
Add table
Add a link
Reference in a new issue