mirror of
https://github.com/yuzu-emu/yuzu.git
synced 2024-07-04 23:31:19 +01:00
Merge pull request #2042 from bunnei/dynarmic
Interface ARM CPU JIT (Dynarmic)
This commit is contained in:
commit
f196924ddd
19 changed files with 319 additions and 48 deletions
23
.gitmodules
vendored
23
.gitmodules
vendored
|
@ -1,15 +1,18 @@
|
||||||
[submodule "inih"]
|
[submodule "inih"]
|
||||||
path = externals/inih/inih
|
path = externals/inih/inih
|
||||||
url = https://github.com/svn2github/inih
|
url = https://github.com/svn2github/inih
|
||||||
[submodule "boost"]
|
[submodule "boost"]
|
||||||
path = externals/boost
|
path = externals/boost
|
||||||
url = https://github.com/citra-emu/ext-boost.git
|
url = https://github.com/citra-emu/ext-boost.git
|
||||||
[submodule "nihstro"]
|
[submodule "nihstro"]
|
||||||
path = externals/nihstro
|
path = externals/nihstro
|
||||||
url = https://github.com/neobrain/nihstro.git
|
url = https://github.com/neobrain/nihstro.git
|
||||||
[submodule "soundtouch"]
|
[submodule "soundtouch"]
|
||||||
path = externals/soundtouch
|
path = externals/soundtouch
|
||||||
url = https://github.com/citra-emu/ext-soundtouch.git
|
url = https://github.com/citra-emu/ext-soundtouch.git
|
||||||
[submodule "catch"]
|
[submodule "catch"]
|
||||||
path = externals/catch
|
path = externals/catch
|
||||||
url = https://github.com/philsquared/Catch.git
|
url = https://github.com/philsquared/Catch.git
|
||||||
|
[submodule "dynarmic"]
|
||||||
|
path = externals/dynarmic
|
||||||
|
url = https://github.com/MerryMage/dynarmic.git
|
||||||
|
|
|
@ -118,12 +118,11 @@ else()
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
find_package(Boost 1.57.0 QUIET)
|
find_package(Boost 1.57.0 QUIET)
|
||||||
if (Boost_FOUND)
|
if (NOT Boost_FOUND)
|
||||||
include_directories(${Boost_INCLUDE_DIRS})
|
|
||||||
else()
|
|
||||||
message(STATUS "Boost 1.57.0 or newer not found, falling back to externals")
|
message(STATUS "Boost 1.57.0 or newer not found, falling back to externals")
|
||||||
include_directories(externals/boost)
|
set(Boost_INCLUDE_DIR "${CMAKE_SOURCE_DIR}/externals/boost")
|
||||||
endif()
|
endif()
|
||||||
|
include_directories(${Boost_INCLUDE_DIR})
|
||||||
|
|
||||||
# Include bundled CMake modules
|
# Include bundled CMake modules
|
||||||
list(APPEND CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/externals/cmake-modules")
|
list(APPEND CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/externals/cmake-modules")
|
||||||
|
@ -227,6 +226,9 @@ set(INI_PREFIX "${CMAKE_CURRENT_SOURCE_DIR}/externals/inih")
|
||||||
include_directories(${INI_PREFIX})
|
include_directories(${INI_PREFIX})
|
||||||
add_subdirectory(${INI_PREFIX})
|
add_subdirectory(${INI_PREFIX})
|
||||||
|
|
||||||
|
option(DYNARMIC_TESTS OFF)
|
||||||
|
add_subdirectory(externals/dynarmic)
|
||||||
|
|
||||||
add_subdirectory(externals/glad)
|
add_subdirectory(externals/glad)
|
||||||
include_directories(externals/microprofile)
|
include_directories(externals/microprofile)
|
||||||
include_directories(externals/nihstro/include)
|
include_directories(externals/nihstro/include)
|
||||||
|
|
2
externals/boost
vendored
2
externals/boost
vendored
|
@ -1 +1 @@
|
||||||
Subproject commit 2dcb9d979665b6aabb1635c617973e02914e60ec
|
Subproject commit f005c955f8147a29667aa0b65257abc3dd520b0c
|
1
externals/dynarmic
vendored
Submodule
1
externals/dynarmic
vendored
Submodule
|
@ -0,0 +1 @@
|
||||||
|
Subproject commit 943487eceed82fbae016c333dc9fe33981e69aa8
|
|
@ -65,6 +65,7 @@ void Config::ReadValues() {
|
||||||
Settings::values.pad_circle_modifier_scale = (float)sdl2_config->GetReal("Controls", "pad_circle_modifier_scale", 0.5);
|
Settings::values.pad_circle_modifier_scale = (float)sdl2_config->GetReal("Controls", "pad_circle_modifier_scale", 0.5);
|
||||||
|
|
||||||
// Core
|
// Core
|
||||||
|
Settings::values.use_cpu_jit = sdl2_config->GetBoolean("Core", "use_cpu_jit", true);
|
||||||
Settings::values.frame_skip = sdl2_config->GetInteger("Core", "frame_skip", 0);
|
Settings::values.frame_skip = sdl2_config->GetInteger("Core", "frame_skip", 0);
|
||||||
|
|
||||||
// Renderer
|
// Renderer
|
||||||
|
|
|
@ -38,6 +38,10 @@ pad_circle_modifier =
|
||||||
pad_circle_modifier_scale =
|
pad_circle_modifier_scale =
|
||||||
|
|
||||||
[Core]
|
[Core]
|
||||||
|
# Whether to use the Just-In-Time (JIT) compiler for CPU emulation
|
||||||
|
# 0: Interpreter (slow), 1 (default): JIT (fast)
|
||||||
|
use_cpu_jit =
|
||||||
|
|
||||||
# The applied frameskip amount. Must be a power of two.
|
# The applied frameskip amount. Must be a power of two.
|
||||||
# 0 (default): No frameskip, 1: x2 frameskip, 2: x4 frameskip, 3: x8 frameskip, etc.
|
# 0 (default): No frameskip, 1: x2 frameskip, 2: x4 frameskip, 3: x8 frameskip, etc.
|
||||||
frame_skip =
|
frame_skip =
|
||||||
|
|
|
@ -41,6 +41,7 @@ void Config::ReadValues() {
|
||||||
qt_config->endGroup();
|
qt_config->endGroup();
|
||||||
|
|
||||||
qt_config->beginGroup("Core");
|
qt_config->beginGroup("Core");
|
||||||
|
Settings::values.use_cpu_jit = qt_config->value("use_cpu_jit", true).toBool();
|
||||||
Settings::values.frame_skip = qt_config->value("frame_skip", 0).toInt();
|
Settings::values.frame_skip = qt_config->value("frame_skip", 0).toInt();
|
||||||
qt_config->endGroup();
|
qt_config->endGroup();
|
||||||
|
|
||||||
|
@ -134,6 +135,7 @@ void Config::SaveValues() {
|
||||||
qt_config->endGroup();
|
qt_config->endGroup();
|
||||||
|
|
||||||
qt_config->beginGroup("Core");
|
qt_config->beginGroup("Core");
|
||||||
|
qt_config->setValue("use_cpu_jit", Settings::values.use_cpu_jit);
|
||||||
qt_config->setValue("frame_skip", Settings::values.frame_skip);
|
qt_config->setValue("frame_skip", Settings::values.frame_skip);
|
||||||
qt_config->endGroup();
|
qt_config->endGroup();
|
||||||
|
|
||||||
|
|
|
@ -7,6 +7,7 @@
|
||||||
#include "ui_configure_general.h"
|
#include "ui_configure_general.h"
|
||||||
|
|
||||||
#include "core/settings.h"
|
#include "core/settings.h"
|
||||||
|
#include "core/system.h"
|
||||||
|
|
||||||
ConfigureGeneral::ConfigureGeneral(QWidget *parent) :
|
ConfigureGeneral::ConfigureGeneral(QWidget *parent) :
|
||||||
QWidget(parent),
|
QWidget(parent),
|
||||||
|
@ -14,6 +15,8 @@ ConfigureGeneral::ConfigureGeneral(QWidget *parent) :
|
||||||
{
|
{
|
||||||
ui->setupUi(this);
|
ui->setupUi(this);
|
||||||
this->setConfiguration();
|
this->setConfiguration();
|
||||||
|
|
||||||
|
ui->toggle_cpu_jit->setEnabled(!System::IsPoweredOn());
|
||||||
}
|
}
|
||||||
|
|
||||||
ConfigureGeneral::~ConfigureGeneral() {
|
ConfigureGeneral::~ConfigureGeneral() {
|
||||||
|
@ -22,6 +25,7 @@ ConfigureGeneral::~ConfigureGeneral() {
|
||||||
void ConfigureGeneral::setConfiguration() {
|
void ConfigureGeneral::setConfiguration() {
|
||||||
ui->toggle_deepscan->setChecked(UISettings::values.gamedir_deepscan);
|
ui->toggle_deepscan->setChecked(UISettings::values.gamedir_deepscan);
|
||||||
ui->toggle_check_exit->setChecked(UISettings::values.confirm_before_closing);
|
ui->toggle_check_exit->setChecked(UISettings::values.confirm_before_closing);
|
||||||
|
ui->toggle_cpu_jit->setChecked(Settings::values.use_cpu_jit);
|
||||||
ui->region_combobox->setCurrentIndex(Settings::values.region_value);
|
ui->region_combobox->setCurrentIndex(Settings::values.region_value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -29,5 +33,6 @@ void ConfigureGeneral::applyConfiguration() {
|
||||||
UISettings::values.gamedir_deepscan = ui->toggle_deepscan->isChecked();
|
UISettings::values.gamedir_deepscan = ui->toggle_deepscan->isChecked();
|
||||||
UISettings::values.confirm_before_closing = ui->toggle_check_exit->isChecked();
|
UISettings::values.confirm_before_closing = ui->toggle_check_exit->isChecked();
|
||||||
Settings::values.region_value = ui->region_combobox->currentIndex();
|
Settings::values.region_value = ui->region_combobox->currentIndex();
|
||||||
|
Settings::values.use_cpu_jit = ui->toggle_cpu_jit->isChecked();
|
||||||
Settings::Apply();
|
Settings::Apply();
|
||||||
}
|
}
|
||||||
|
|
|
@ -43,6 +43,26 @@
|
||||||
</layout>
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QGroupBox" name="groupBox_2">
|
||||||
|
<property name="title">
|
||||||
|
<string>Performance</string>
|
||||||
|
</property>
|
||||||
|
<layout class="QHBoxLayout" name="horizontalLayout_7">
|
||||||
|
<item>
|
||||||
|
<layout class="QVBoxLayout" name="verticalLayout_5">
|
||||||
|
<item>
|
||||||
|
<widget class="QCheckBox" name="toggle_cpu_jit">
|
||||||
|
<property name="text">
|
||||||
|
<string>Enable CPU JIT</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<widget class="QGroupBox" name="groupBox_4">
|
<widget class="QGroupBox" name="groupBox_4">
|
||||||
<property name="title">
|
<property name="title">
|
||||||
|
|
|
@ -13,7 +13,7 @@
|
||||||
#define MICROPROFILE_WEBSERVER 0
|
#define MICROPROFILE_WEBSERVER 0
|
||||||
#define MICROPROFILE_GPU_TIMERS 0 // TODO: Implement timer queries when we upgrade to OpenGL 3.3
|
#define MICROPROFILE_GPU_TIMERS 0 // TODO: Implement timer queries when we upgrade to OpenGL 3.3
|
||||||
#define MICROPROFILE_CONTEXT_SWITCH_TRACE 0
|
#define MICROPROFILE_CONTEXT_SWITCH_TRACE 0
|
||||||
#define MICROPROFILE_PER_THREAD_BUFFER_SIZE (2048<<12) // 8 MB
|
#define MICROPROFILE_PER_THREAD_BUFFER_SIZE (2048<<13) // 16 MB
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
// This isn't defined by the standard library in MSVC2015
|
// This isn't defined by the standard library in MSVC2015
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
set(SRCS
|
set(SRCS
|
||||||
arm/disassembler/arm_disasm.cpp
|
arm/disassembler/arm_disasm.cpp
|
||||||
arm/disassembler/load_symbol_map.cpp
|
arm/disassembler/load_symbol_map.cpp
|
||||||
|
arm/dynarmic/arm_dynarmic.cpp
|
||||||
arm/dyncom/arm_dyncom.cpp
|
arm/dyncom/arm_dyncom.cpp
|
||||||
arm/dyncom/arm_dyncom_dec.cpp
|
arm/dyncom/arm_dyncom_dec.cpp
|
||||||
arm/dyncom/arm_dyncom_interpreter.cpp
|
arm/dyncom/arm_dyncom_interpreter.cpp
|
||||||
|
@ -141,6 +142,7 @@ set(HEADERS
|
||||||
arm/arm_interface.h
|
arm/arm_interface.h
|
||||||
arm/disassembler/arm_disasm.h
|
arm/disassembler/arm_disasm.h
|
||||||
arm/disassembler/load_symbol_map.h
|
arm/disassembler/load_symbol_map.h
|
||||||
|
arm/dynarmic/arm_dynarmic.h
|
||||||
arm/dyncom/arm_dyncom.h
|
arm/dyncom/arm_dyncom.h
|
||||||
arm/dyncom/arm_dyncom_dec.h
|
arm/dyncom/arm_dyncom_dec.h
|
||||||
arm/dyncom/arm_dyncom_interpreter.h
|
arm/dyncom/arm_dyncom_interpreter.h
|
||||||
|
@ -285,6 +287,10 @@ set(HEADERS
|
||||||
system.h
|
system.h
|
||||||
)
|
)
|
||||||
|
|
||||||
|
include_directories(../../externals/dynarmic/include)
|
||||||
|
|
||||||
create_directory_groups(${SRCS} ${HEADERS})
|
create_directory_groups(${SRCS} ${HEADERS})
|
||||||
|
|
||||||
add_library(core STATIC ${SRCS} ${HEADERS})
|
add_library(core STATIC ${SRCS} ${HEADERS})
|
||||||
|
|
||||||
|
target_link_libraries(core dynarmic)
|
||||||
|
|
|
@ -121,15 +121,6 @@ public:
|
||||||
*/
|
*/
|
||||||
virtual void AddTicks(u64 ticks) = 0;
|
virtual void AddTicks(u64 ticks) = 0;
|
||||||
|
|
||||||
/**
|
|
||||||
* Initializes a CPU context for use on this CPU
|
|
||||||
* @param context Thread context to reset
|
|
||||||
* @param stack_top Pointer to the top of the stack
|
|
||||||
* @param entry_point Entry point for execution
|
|
||||||
* @param arg User argument for thread
|
|
||||||
*/
|
|
||||||
virtual void ResetContext(Core::ThreadContext& context, u32 stack_top, u32 entry_point, u32 arg) = 0;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Saves the current CPU context
|
* Saves the current CPU context
|
||||||
* @param ctx Thread context to save
|
* @param ctx Thread context to save
|
||||||
|
|
174
src/core/arm/dynarmic/arm_dynarmic.cpp
Normal file
174
src/core/arm/dynarmic/arm_dynarmic.cpp
Normal file
|
@ -0,0 +1,174 @@
|
||||||
|
// Copyright 2016 Citra Emulator Project
|
||||||
|
// Licensed under GPLv2 or any later version
|
||||||
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
|
#include "common/assert.h"
|
||||||
|
#include "common/microprofile.h"
|
||||||
|
|
||||||
|
#include <dynarmic/dynarmic.h>
|
||||||
|
|
||||||
|
#include "core/arm/dynarmic/arm_dynarmic.h"
|
||||||
|
#include "core/arm/dyncom/arm_dyncom_interpreter.h"
|
||||||
|
#include "core/core.h"
|
||||||
|
#include "core/core_timing.h"
|
||||||
|
#include "core/hle/svc.h"
|
||||||
|
#include "core/memory.h"
|
||||||
|
|
||||||
|
static void InterpreterFallback(u32 pc, Dynarmic::Jit* jit, void* user_arg) {
|
||||||
|
ARMul_State* state = static_cast<ARMul_State*>(user_arg);
|
||||||
|
|
||||||
|
state->Reg = jit->Regs();
|
||||||
|
state->Cpsr = jit->Cpsr();
|
||||||
|
state->Reg[15] = pc;
|
||||||
|
state->ExtReg = jit->ExtRegs();
|
||||||
|
state->VFP[VFP_FPSCR] = jit->Fpscr();
|
||||||
|
state->NumInstrsToExecute = 1;
|
||||||
|
|
||||||
|
InterpreterMainLoop(state);
|
||||||
|
|
||||||
|
bool is_thumb = (state->Cpsr & (1 << 5)) != 0;
|
||||||
|
state->Reg[15] &= (is_thumb ? 0xFFFFFFFE : 0xFFFFFFFC);
|
||||||
|
|
||||||
|
jit->Regs() = state->Reg;
|
||||||
|
jit->Cpsr() = state->Cpsr;
|
||||||
|
jit->ExtRegs() = state->ExtReg;
|
||||||
|
jit->SetFpscr(state->VFP[VFP_FPSCR]);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool IsReadOnlyMemory(u32 vaddr) {
|
||||||
|
// TODO(bunnei): ImplementMe
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static Dynarmic::UserCallbacks GetUserCallbacks(ARMul_State* interpeter_state) {
|
||||||
|
Dynarmic::UserCallbacks user_callbacks{};
|
||||||
|
user_callbacks.InterpreterFallback = &InterpreterFallback;
|
||||||
|
user_callbacks.user_arg = static_cast<void*>(interpeter_state);
|
||||||
|
user_callbacks.CallSVC = &SVC::CallSVC;
|
||||||
|
user_callbacks.IsReadOnlyMemory = &IsReadOnlyMemory;
|
||||||
|
user_callbacks.MemoryRead8 = &Memory::Read8;
|
||||||
|
user_callbacks.MemoryRead16 = &Memory::Read16;
|
||||||
|
user_callbacks.MemoryRead32 = &Memory::Read32;
|
||||||
|
user_callbacks.MemoryRead64 = &Memory::Read64;
|
||||||
|
user_callbacks.MemoryWrite8 = &Memory::Write8;
|
||||||
|
user_callbacks.MemoryWrite16 = &Memory::Write16;
|
||||||
|
user_callbacks.MemoryWrite32 = &Memory::Write32;
|
||||||
|
user_callbacks.MemoryWrite64 = &Memory::Write64;
|
||||||
|
return user_callbacks;
|
||||||
|
}
|
||||||
|
|
||||||
|
ARM_Dynarmic::ARM_Dynarmic(PrivilegeMode initial_mode) {
|
||||||
|
interpreter_state = std::make_unique<ARMul_State>(initial_mode);
|
||||||
|
jit = std::make_unique<Dynarmic::Jit>(GetUserCallbacks(interpreter_state.get()));
|
||||||
|
}
|
||||||
|
|
||||||
|
void ARM_Dynarmic::SetPC(u32 pc) {
|
||||||
|
jit->Regs()[15] = pc;
|
||||||
|
}
|
||||||
|
|
||||||
|
u32 ARM_Dynarmic::GetPC() const {
|
||||||
|
return jit->Regs()[15];
|
||||||
|
}
|
||||||
|
|
||||||
|
u32 ARM_Dynarmic::GetReg(int index) const {
|
||||||
|
return jit->Regs()[index];
|
||||||
|
}
|
||||||
|
|
||||||
|
void ARM_Dynarmic::SetReg(int index, u32 value) {
|
||||||
|
jit->Regs()[index] = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
u32 ARM_Dynarmic::GetVFPReg(int index) const {
|
||||||
|
return jit->ExtRegs()[index];
|
||||||
|
}
|
||||||
|
|
||||||
|
void ARM_Dynarmic::SetVFPReg(int index, u32 value) {
|
||||||
|
jit->ExtRegs()[index] = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
u32 ARM_Dynarmic::GetVFPSystemReg(VFPSystemRegister reg) const {
|
||||||
|
if (reg == VFP_FPSCR) {
|
||||||
|
return jit->Fpscr();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Dynarmic does not implement and/or expose other VFP registers, fallback to interpreter state
|
||||||
|
return interpreter_state->VFP[reg];
|
||||||
|
}
|
||||||
|
|
||||||
|
void ARM_Dynarmic::SetVFPSystemReg(VFPSystemRegister reg, u32 value) {
|
||||||
|
if (reg == VFP_FPSCR) {
|
||||||
|
jit->SetFpscr(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Dynarmic does not implement and/or expose other VFP registers, fallback to interpreter state
|
||||||
|
interpreter_state->VFP[reg] = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
u32 ARM_Dynarmic::GetCPSR() const {
|
||||||
|
return jit->Cpsr();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ARM_Dynarmic::SetCPSR(u32 cpsr) {
|
||||||
|
jit->Cpsr() = cpsr;
|
||||||
|
}
|
||||||
|
|
||||||
|
u32 ARM_Dynarmic::GetCP15Register(CP15Register reg) {
|
||||||
|
return interpreter_state->CP15[reg];
|
||||||
|
}
|
||||||
|
|
||||||
|
void ARM_Dynarmic::SetCP15Register(CP15Register reg, u32 value) {
|
||||||
|
interpreter_state->CP15[reg] = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ARM_Dynarmic::AddTicks(u64 ticks) {
|
||||||
|
down_count -= ticks;
|
||||||
|
if (down_count < 0) {
|
||||||
|
CoreTiming::Advance();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
MICROPROFILE_DEFINE(ARM_Jit, "ARM JIT", "ARM JIT", MP_RGB(255, 64, 64));
|
||||||
|
|
||||||
|
void ARM_Dynarmic::ExecuteInstructions(int num_instructions) {
|
||||||
|
MICROPROFILE_SCOPE(ARM_Jit);
|
||||||
|
|
||||||
|
jit->Run(static_cast<unsigned>(num_instructions));
|
||||||
|
|
||||||
|
AddTicks(num_instructions);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ARM_Dynarmic::SaveContext(Core::ThreadContext& ctx) {
|
||||||
|
memcpy(ctx.cpu_registers, jit->Regs().data(), sizeof(ctx.cpu_registers));
|
||||||
|
memcpy(ctx.fpu_registers, jit->ExtRegs().data(), sizeof(ctx.fpu_registers));
|
||||||
|
|
||||||
|
ctx.sp = jit->Regs()[13];
|
||||||
|
ctx.lr = jit->Regs()[14];
|
||||||
|
ctx.pc = jit->Regs()[15];
|
||||||
|
ctx.cpsr = jit->Cpsr();
|
||||||
|
|
||||||
|
ctx.fpscr = jit->Fpscr();
|
||||||
|
ctx.fpexc = interpreter_state->VFP[VFP_FPEXC];
|
||||||
|
}
|
||||||
|
|
||||||
|
void ARM_Dynarmic::LoadContext(const Core::ThreadContext& ctx) {
|
||||||
|
memcpy(jit->Regs().data(), ctx.cpu_registers, sizeof(ctx.cpu_registers));
|
||||||
|
memcpy(jit->ExtRegs().data(), ctx.fpu_registers, sizeof(ctx.fpu_registers));
|
||||||
|
|
||||||
|
jit->Regs()[13] = ctx.sp;
|
||||||
|
jit->Regs()[14] = ctx.lr;
|
||||||
|
jit->Regs()[15] = ctx.pc;
|
||||||
|
jit->Cpsr() = ctx.cpsr;
|
||||||
|
|
||||||
|
jit->SetFpscr(ctx.fpscr);
|
||||||
|
interpreter_state->VFP[VFP_FPEXC] = ctx.fpexc;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ARM_Dynarmic::PrepareReschedule() {
|
||||||
|
if (jit->IsExecuting()) {
|
||||||
|
jit->HaltExecution();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ARM_Dynarmic::ClearInstructionCache() {
|
||||||
|
jit->ClearCache();
|
||||||
|
}
|
50
src/core/arm/dynarmic/arm_dynarmic.h
Normal file
50
src/core/arm/dynarmic/arm_dynarmic.h
Normal file
|
@ -0,0 +1,50 @@
|
||||||
|
// Copyright 2016 Citra Emulator Project
|
||||||
|
// Licensed under GPLv2 or any later version
|
||||||
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
#include <dynarmic/dynarmic.h>
|
||||||
|
|
||||||
|
#include "common/common_types.h"
|
||||||
|
|
||||||
|
#include "core/arm/arm_interface.h"
|
||||||
|
#include "core/arm/skyeye_common/armstate.h"
|
||||||
|
|
||||||
|
namespace Core {
|
||||||
|
struct ThreadContext;
|
||||||
|
}
|
||||||
|
|
||||||
|
class ARM_Dynarmic final : public ARM_Interface {
|
||||||
|
public:
|
||||||
|
ARM_Dynarmic(PrivilegeMode initial_mode);
|
||||||
|
|
||||||
|
void SetPC(u32 pc) override;
|
||||||
|
u32 GetPC() const override;
|
||||||
|
u32 GetReg(int index) const override;
|
||||||
|
void SetReg(int index, u32 value) override;
|
||||||
|
u32 GetVFPReg(int index) const override;
|
||||||
|
void SetVFPReg(int index, u32 value) override;
|
||||||
|
u32 GetVFPSystemReg(VFPSystemRegister reg) const override;
|
||||||
|
void SetVFPSystemReg(VFPSystemRegister reg, u32 value) override;
|
||||||
|
u32 GetCPSR() const override;
|
||||||
|
void SetCPSR(u32 cpsr) override;
|
||||||
|
u32 GetCP15Register(CP15Register reg) override;
|
||||||
|
void SetCP15Register(CP15Register reg, u32 value) override;
|
||||||
|
|
||||||
|
void AddTicks(u64 ticks) override;
|
||||||
|
|
||||||
|
void SaveContext(Core::ThreadContext& ctx) override;
|
||||||
|
void LoadContext(const Core::ThreadContext& ctx) override;
|
||||||
|
|
||||||
|
void PrepareReschedule() override;
|
||||||
|
void ExecuteInstructions(int num_instructions) override;
|
||||||
|
|
||||||
|
void ClearInstructionCache() override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::unique_ptr<Dynarmic::Jit> jit;
|
||||||
|
std::unique_ptr<ARMul_State> interpreter_state;
|
||||||
|
};
|
|
@ -93,15 +93,6 @@ void ARM_DynCom::ExecuteInstructions(int num_instructions) {
|
||||||
AddTicks(ticks_executed);
|
AddTicks(ticks_executed);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ARM_DynCom::ResetContext(Core::ThreadContext& context, u32 stack_top, u32 entry_point, u32 arg) {
|
|
||||||
memset(&context, 0, sizeof(Core::ThreadContext));
|
|
||||||
|
|
||||||
context.cpu_registers[0] = arg;
|
|
||||||
context.pc = entry_point;
|
|
||||||
context.sp = stack_top;
|
|
||||||
context.cpsr = USER32MODE | ((entry_point & 1) << 5); // Usermode and THUMB mode
|
|
||||||
}
|
|
||||||
|
|
||||||
void ARM_DynCom::SaveContext(Core::ThreadContext& ctx) {
|
void ARM_DynCom::SaveContext(Core::ThreadContext& ctx) {
|
||||||
memcpy(ctx.cpu_registers, state->Reg.data(), sizeof(ctx.cpu_registers));
|
memcpy(ctx.cpu_registers, state->Reg.data(), sizeof(ctx.cpu_registers));
|
||||||
memcpy(ctx.fpu_registers, state->ExtReg.data(), sizeof(ctx.fpu_registers));
|
memcpy(ctx.fpu_registers, state->ExtReg.data(), sizeof(ctx.fpu_registers));
|
||||||
|
@ -111,8 +102,8 @@ void ARM_DynCom::SaveContext(Core::ThreadContext& ctx) {
|
||||||
ctx.pc = state->Reg[15];
|
ctx.pc = state->Reg[15];
|
||||||
ctx.cpsr = state->Cpsr;
|
ctx.cpsr = state->Cpsr;
|
||||||
|
|
||||||
ctx.fpscr = state->VFP[1];
|
ctx.fpscr = state->VFP[VFP_FPSCR];
|
||||||
ctx.fpexc = state->VFP[2];
|
ctx.fpexc = state->VFP[VFP_FPEXC];
|
||||||
}
|
}
|
||||||
|
|
||||||
void ARM_DynCom::LoadContext(const Core::ThreadContext& ctx) {
|
void ARM_DynCom::LoadContext(const Core::ThreadContext& ctx) {
|
||||||
|
@ -124,8 +115,8 @@ void ARM_DynCom::LoadContext(const Core::ThreadContext& ctx) {
|
||||||
state->Reg[15] = ctx.pc;
|
state->Reg[15] = ctx.pc;
|
||||||
state->Cpsr = ctx.cpsr;
|
state->Cpsr = ctx.cpsr;
|
||||||
|
|
||||||
state->VFP[1] = ctx.fpscr;
|
state->VFP[VFP_FPSCR] = ctx.fpscr;
|
||||||
state->VFP[2] = ctx.fpexc;
|
state->VFP[VFP_FPEXC] = ctx.fpexc;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ARM_DynCom::PrepareReschedule() {
|
void ARM_DynCom::PrepareReschedule() {
|
||||||
|
|
|
@ -16,7 +16,7 @@ namespace Core {
|
||||||
struct ThreadContext;
|
struct ThreadContext;
|
||||||
}
|
}
|
||||||
|
|
||||||
class ARM_DynCom final : virtual public ARM_Interface {
|
class ARM_DynCom final : public ARM_Interface {
|
||||||
public:
|
public:
|
||||||
ARM_DynCom(PrivilegeMode initial_mode);
|
ARM_DynCom(PrivilegeMode initial_mode);
|
||||||
~ARM_DynCom();
|
~ARM_DynCom();
|
||||||
|
@ -38,7 +38,6 @@ public:
|
||||||
|
|
||||||
void AddTicks(u64 ticks) override;
|
void AddTicks(u64 ticks) override;
|
||||||
|
|
||||||
void ResetContext(Core::ThreadContext& context, u32 stack_top, u32 entry_point, u32 arg) override;
|
|
||||||
void SaveContext(Core::ThreadContext& ctx) override;
|
void SaveContext(Core::ThreadContext& ctx) override;
|
||||||
void LoadContext(const Core::ThreadContext& ctx) override;
|
void LoadContext(const Core::ThreadContext& ctx) override;
|
||||||
|
|
||||||
|
|
|
@ -6,16 +6,16 @@
|
||||||
|
|
||||||
#include "common/logging/log.h"
|
#include "common/logging/log.h"
|
||||||
|
|
||||||
|
#include "core/arm/arm_interface.h"
|
||||||
|
#include "core/arm/dynarmic/arm_dynarmic.h"
|
||||||
|
#include "core/arm/dyncom/arm_dyncom.h"
|
||||||
#include "core/core.h"
|
#include "core/core.h"
|
||||||
#include "core/core_timing.h"
|
#include "core/core_timing.h"
|
||||||
|
#include "core/gdbstub/gdbstub.h"
|
||||||
#include "core/arm/arm_interface.h"
|
|
||||||
#include "core/arm/dyncom/arm_dyncom.h"
|
|
||||||
#include "core/hle/hle.h"
|
#include "core/hle/hle.h"
|
||||||
#include "core/hle/kernel/thread.h"
|
#include "core/hle/kernel/thread.h"
|
||||||
#include "core/hw/hw.h"
|
#include "core/hw/hw.h"
|
||||||
|
#include "core/settings.h"
|
||||||
#include "core/gdbstub/gdbstub.h"
|
|
||||||
|
|
||||||
namespace Core {
|
namespace Core {
|
||||||
|
|
||||||
|
@ -73,8 +73,13 @@ void Stop() {
|
||||||
|
|
||||||
/// Initialize the core
|
/// Initialize the core
|
||||||
void Init() {
|
void Init() {
|
||||||
g_sys_core = std::make_unique<ARM_DynCom>(USER32MODE);
|
if (Settings::values.use_cpu_jit) {
|
||||||
g_app_core = std::make_unique<ARM_DynCom>(USER32MODE);
|
g_sys_core = std::make_unique<ARM_Dynarmic>(USER32MODE);
|
||||||
|
g_app_core = std::make_unique<ARM_Dynarmic>(USER32MODE);
|
||||||
|
} else {
|
||||||
|
g_sys_core = std::make_unique<ARM_DynCom>(USER32MODE);
|
||||||
|
g_app_core = std::make_unique<ARM_DynCom>(USER32MODE);
|
||||||
|
}
|
||||||
|
|
||||||
LOG_DEBUG(Core, "Initialized OK");
|
LOG_DEBUG(Core, "Initialized OK");
|
||||||
}
|
}
|
||||||
|
|
|
@ -441,6 +441,22 @@ std::tuple<u32, u32, bool> GetFreeThreadLocalSlot(std::vector<std::bitset<8>>& t
|
||||||
return std::make_tuple(0, 0, true);
|
return std::make_tuple(0, 0, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resets a thread context, making it ready to be scheduled and run by the CPU
|
||||||
|
* @param context Thread context to reset
|
||||||
|
* @param stack_top Address of the top of the stack
|
||||||
|
* @param entry_point Address of entry point for execution
|
||||||
|
* @param arg User argument for thread
|
||||||
|
*/
|
||||||
|
static void ResetThreadContext(Core::ThreadContext& context, u32 stack_top, u32 entry_point, u32 arg) {
|
||||||
|
memset(&context, 0, sizeof(Core::ThreadContext));
|
||||||
|
|
||||||
|
context.cpu_registers[0] = arg;
|
||||||
|
context.pc = entry_point;
|
||||||
|
context.sp = stack_top;
|
||||||
|
context.cpsr = USER32MODE | ((entry_point & 1) << 5); // Usermode and THUMB mode
|
||||||
|
}
|
||||||
|
|
||||||
ResultVal<SharedPtr<Thread>> Thread::Create(std::string name, VAddr entry_point, s32 priority,
|
ResultVal<SharedPtr<Thread>> Thread::Create(std::string name, VAddr entry_point, s32 priority,
|
||||||
u32 arg, s32 processor_id, VAddr stack_top) {
|
u32 arg, s32 processor_id, VAddr stack_top) {
|
||||||
if (priority < THREADPRIO_HIGHEST || priority > THREADPRIO_LOWEST) {
|
if (priority < THREADPRIO_HIGHEST || priority > THREADPRIO_LOWEST) {
|
||||||
|
@ -525,7 +541,7 @@ ResultVal<SharedPtr<Thread>> Thread::Create(std::string name, VAddr entry_point,
|
||||||
|
|
||||||
// TODO(peachum): move to ScheduleThread() when scheduler is added so selected core is used
|
// TODO(peachum): move to ScheduleThread() when scheduler is added so selected core is used
|
||||||
// to initialize the context
|
// to initialize the context
|
||||||
Core::g_app_core->ResetContext(thread->context, stack_top, entry_point, arg);
|
ResetThreadContext(thread->context, stack_top, entry_point, arg);
|
||||||
|
|
||||||
ready_queue.push_back(thread->current_priority, thread.get());
|
ready_queue.push_back(thread->current_priority, thread.get());
|
||||||
thread->status = THREADSTATUS_READY;
|
thread->status = THREADSTATUS_READY;
|
||||||
|
|
|
@ -60,6 +60,7 @@ struct Values {
|
||||||
float pad_circle_modifier_scale;
|
float pad_circle_modifier_scale;
|
||||||
|
|
||||||
// Core
|
// Core
|
||||||
|
bool use_cpu_jit;
|
||||||
int frame_skip;
|
int frame_skip;
|
||||||
|
|
||||||
// Data Storage
|
// Data Storage
|
||||||
|
|
Loading…
Reference in a new issue