mirror of
https://github.com/skyline-emu/skyline.git
synced 2024-11-26 00:04:17 +01:00
Fix GroupMutex and Clock Rescaling
This commit mainly fixes GroupMutex and clock rescaling. In addition, clock rescaling is no longer performed if the CNTFRQ_EL0 of the host device is same as that of the Switch (19.2MHz) which is fairly common on higher end devices.
This commit is contained in:
parent
003e9c5a01
commit
66d20a9429
2
.gitignore
vendored
2
.gitignore
vendored
@ -95,4 +95,4 @@ captures/
|
|||||||
.vscode/
|
.vscode/
|
||||||
|
|
||||||
# Discord plugin for IntelliJ IDEA
|
# Discord plugin for IntelliJ IDEA
|
||||||
.idea\discord.xml
|
.idea/discord.xml
|
||||||
|
2
.idea/scopes/SkylineCPP.xml
generated
2
.idea/scopes/SkylineCPP.xml
generated
@ -1,3 +1,3 @@
|
|||||||
<component name="DependencyValidationManager">
|
<component name="DependencyValidationManager">
|
||||||
<scope name="SkylineCPP" pattern="file[app]:src/main/cpp//*&&!file[Lightswitch]:*&&!file[app]:*" />
|
<scope name="SkylineCPP" pattern="file[app]:src/main/cpp//*&&!file[Skyline]:*&&!file[app]:*" />
|
||||||
</component>
|
</component>
|
@ -10,12 +10,18 @@
|
|||||||
</h1>
|
</h1>
|
||||||
|
|
||||||
<p align="center">
|
<p align="center">
|
||||||
<i>Skyline is an experimental emulator that runs on ARMv8 Android™ devices and emulates the functionality of a Nintendo Switch™ system. Skyline currently does not run any games, nor Homebrew. It's licensed under GPLv3, refer to the <a href="https://github.com/skyline-emu/skyline/blob/master/LICENSE">license file</a> for more information.</i><br/><br>
|
<i>Skyline is an experimental emulator that runs on ARMv8 Android™ devices and emulates the functionality of a Nintendo Switch™ system. It's licensed under GPLv3, refer to the <a href="https://github.com/skyline-emu/skyline/blob/master/LICENSE">license file</a> for more information.</i><br/><br>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
### Contact
|
### Contact
|
||||||
> You can contact the core developers of Skyline at our [Discord](https://discord.gg/XnbXNQM). If you have any questions, feel free to ask.
|
> You can contact the core developers of Skyline at our [Discord](https://discord.gg/XnbXNQM). If you have any questions, feel free to ask.
|
||||||
|
|
||||||
|
### Credit
|
||||||
|
[<img align="left" height="10%" width="10%" src="https://i.imgur.com/aOADoDM.png"/>](https://ryujinx.org/)
|
||||||
|
[**Ryujinx**](https://ryujinx.org/)<br>
|
||||||
|
We've used Ryujinx throughout the project for reference, the amount of accuracy of their HLE kernel implementation is what makes them such an amazing reference.
|
||||||
|
<br>
|
||||||
|
<br>
|
||||||
### Disclaimer
|
### Disclaimer
|
||||||
* Nintendo Switch is a trademark of Nintendo Co., Ltd.
|
* Nintendo Switch is a trademark of Nintendo Co., Ltd.
|
||||||
* Android is a trademark of Google LLC.
|
* Android is a trademark of Google LLC.
|
||||||
|
@ -49,7 +49,8 @@
|
|||||||
<activity
|
<activity
|
||||||
android:name="emu.skyline.GameActivity"
|
android:name="emu.skyline.GameActivity"
|
||||||
android:configChanges="orientation|screenSize"
|
android:configChanges="orientation|screenSize"
|
||||||
android:screenOrientation="landscape">
|
android:screenOrientation="landscape"
|
||||||
|
android:launchMode="singleInstance">
|
||||||
<meta-data
|
<meta-data
|
||||||
android:name="android.support.PARENT_ACTIVITY"
|
android:name="android.support.PARENT_ACTIVITY"
|
||||||
android:value="emu.skyline.MainActivity" />
|
android:value="emu.skyline.MainActivity" />
|
||||||
|
@ -44,12 +44,12 @@ extern "C" JNIEXPORT void Java_emu_skyline_GameActivity_executeRom(JNIEnv *env,
|
|||||||
logger->Info("Launching ROM {}", romString);
|
logger->Info("Launching ROM {}", romString);
|
||||||
env->ReleaseStringUTFChars(romJstring, romString);
|
env->ReleaseStringUTFChars(romJstring, romString);
|
||||||
os.Execute(romFd, static_cast<skyline::TitleFormat>(romType));
|
os.Execute(romFd, static_cast<skyline::TitleFormat>(romType));
|
||||||
logger->Info("Emulation has ended");
|
|
||||||
} catch (std::exception &e) {
|
} catch (std::exception &e) {
|
||||||
logger->Error(e.what());
|
logger->Error(e.what());
|
||||||
} catch (...) {
|
} catch (...) {
|
||||||
logger->Error("An unknown exception has occurred");
|
logger->Error("An unknown exception has occurred");
|
||||||
}
|
}
|
||||||
|
logger->Info("Emulation has ended");
|
||||||
|
|
||||||
auto end = std::chrono::steady_clock::now();
|
auto end = std::chrono::steady_clock::now();
|
||||||
logger->Info("Done in: {} ms", (std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count()));
|
logger->Info("Done in: {} ms", (std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count()));
|
||||||
|
@ -18,23 +18,37 @@ namespace skyline {
|
|||||||
|
|
||||||
void GroupMutex::lock(Group group) {
|
void GroupMutex::lock(Group group) {
|
||||||
auto none = Group::None;
|
auto none = Group::None;
|
||||||
constexpr u64 timeout = 1000; // The timeout in ns
|
constexpr u64 timeout = 100; // The timeout in ns
|
||||||
auto start = utils::GetTimeNs();
|
auto end = utils::GetTimeNs() + timeout;
|
||||||
while (next != group && !next.compare_exchange_weak(none, group)) {
|
while (true) {
|
||||||
if (flag == group && ((utils::GetTimeNs() - start) > timeout)) {
|
if (next == group) {
|
||||||
num++;
|
if (flag == group) {
|
||||||
return;
|
std::lock_guard lock(mtx);
|
||||||
}
|
if (flag == group) {
|
||||||
|
auto groupT = group;
|
||||||
|
next.compare_exchange_strong(groupT, Group::None);
|
||||||
|
num++;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} else
|
||||||
|
flag.compare_exchange_weak(none, group);
|
||||||
|
} else if (flag == group && (next == Group::None || utils::GetTimeNs() >= end)) {
|
||||||
|
std::lock_guard lock(mtx);
|
||||||
|
if (flag == group) {
|
||||||
|
num++;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} else
|
||||||
|
next.compare_exchange_weak(none, group);
|
||||||
|
none = Group::None;
|
||||||
asm volatile("yield");
|
asm volatile("yield");
|
||||||
}
|
}
|
||||||
while (flag != group && !flag.compare_exchange_weak(none, group))
|
|
||||||
asm volatile("yield");
|
|
||||||
num++;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void GroupMutex::unlock() {
|
void GroupMutex::unlock() {
|
||||||
|
std::lock_guard lock(mtx);
|
||||||
if (!--num)
|
if (!--num)
|
||||||
flag.exchange(Group::None);
|
flag.exchange(next);
|
||||||
}
|
}
|
||||||
|
|
||||||
Settings::Settings(const int preferenceFd) {
|
Settings::Settings(const int preferenceFd) {
|
||||||
@ -118,6 +132,6 @@ namespace skyline {
|
|||||||
gpu = std::move(std::make_shared<gpu::GPU>(*this));
|
gpu = std::move(std::make_shared<gpu::GPU>(*this));
|
||||||
}
|
}
|
||||||
|
|
||||||
thread_local std::shared_ptr<kernel::type::KThread> DeviceState::thread = 0;
|
thread_local std::shared_ptr<kernel::type::KThread> DeviceState::thread = nullptr;
|
||||||
thread_local ThreadContext *DeviceState::ctx = 0;
|
thread_local ThreadContext *DeviceState::ctx = nullptr;
|
||||||
}
|
}
|
||||||
|
@ -36,7 +36,7 @@ namespace skyline {
|
|||||||
constexpr u8 NumRegs = 30; //!< The amount of registers that ARMv8 has
|
constexpr u8 NumRegs = 30; //!< The amount of registers that ARMv8 has
|
||||||
constexpr u32 TpidrroEl0 = 0x5E83; //!< ID of TPIDRRO_EL0 in MRS
|
constexpr u32 TpidrroEl0 = 0x5E83; //!< ID of TPIDRRO_EL0 in MRS
|
||||||
constexpr u32 CntfrqEl0 = 0x5F00; //!< ID of CNTFRQ_EL0 in MRS
|
constexpr u32 CntfrqEl0 = 0x5F00; //!< ID of CNTFRQ_EL0 in MRS
|
||||||
constexpr u32 TegraX1Freq = 0x124F800; //!< The clock frequency of the Tegra X1 (19.2 MHz)
|
constexpr u32 TegraX1Freq = 19200000; //!< The clock frequency of the Tegra X1 (19.2 MHz)
|
||||||
constexpr u32 CntpctEl0 = 0x5F01; //!< ID of CNTPCT_EL0 in MRS
|
constexpr u32 CntpctEl0 = 0x5F01; //!< ID of CNTPCT_EL0 in MRS
|
||||||
constexpr u32 CntvctEl0 = 0x5F02; //!< ID of CNTVCT_EL0 in MRS
|
constexpr u32 CntvctEl0 = 0x5F02; //!< ID of CNTVCT_EL0 in MRS
|
||||||
// Kernel
|
// Kernel
|
||||||
@ -101,18 +101,17 @@ namespace skyline {
|
|||||||
* @return The current time in nanoseconds
|
* @return The current time in nanoseconds
|
||||||
*/
|
*/
|
||||||
inline u64 GetTimeNs() {
|
inline u64 GetTimeNs() {
|
||||||
static u64 frequencyMs{};
|
constexpr uint64_t NsInSecond = 1000000000;
|
||||||
if (!frequencyMs) {
|
static u64 frequency{};
|
||||||
asm("MRS %0, CNTFRQ_EL0" : "=r"(frequencyMs));
|
if (!frequency)
|
||||||
frequencyMs *= 1000000000;
|
asm("MRS %0, CNTFRQ_EL0" : "=r"(frequency));
|
||||||
}
|
|
||||||
u64 ticks;
|
u64 ticks;
|
||||||
asm("MRS %0, CNTVCT_EL0" : "=r"(ticks));
|
asm("MRS %0, CNTVCT_EL0" : "=r"(ticks));
|
||||||
return ticks / frequencyMs;
|
return ((ticks / frequency) * NsInSecond) + (((ticks % frequency) * NsInSecond + (frequency / 2)) / frequency);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Aligns up a value to a multiple of twoB
|
* @brief Aligns up a value to a multiple of two
|
||||||
* @tparam Type The type of the values
|
* @tparam Type The type of the values
|
||||||
* @param value The value to round up
|
* @param value The value to round up
|
||||||
* @param multiple The multiple to round up to (Should be a multiple of 2)
|
* @param multiple The multiple to round up to (Should be a multiple of 2)
|
||||||
@ -216,6 +215,7 @@ namespace skyline {
|
|||||||
std::atomic<Group> flag = Group::None; //!< An atomic flag to hold which group holds the mutex
|
std::atomic<Group> flag = Group::None; //!< An atomic flag to hold which group holds the mutex
|
||||||
std::atomic<Group> next = Group::None; //!< An atomic flag to hold which group will hold the mutex next
|
std::atomic<Group> next = Group::None; //!< An atomic flag to hold which group will hold the mutex next
|
||||||
std::atomic<u8> num = 0; //!< An atomic u8 keeping track of how many users are holding the mutex
|
std::atomic<u8> num = 0; //!< An atomic u8 keeping track of how many users are holding the mutex
|
||||||
|
Mutex mtx; //!< A mutex to lock before changing of num and flag
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -31,9 +31,11 @@ namespace skyline::gpu {
|
|||||||
resolution.width = static_cast<u32>(ANativeWindow_getWidth(window));
|
resolution.width = static_cast<u32>(ANativeWindow_getWidth(window));
|
||||||
resolution.height = static_cast<u32>(ANativeWindow_getHeight(window));
|
resolution.height = static_cast<u32>(ANativeWindow_getHeight(window));
|
||||||
format = ANativeWindow_getFormat(window);
|
format = ANativeWindow_getFormat(window);
|
||||||
|
surfaceUpdate = false;
|
||||||
|
} else if (Surface == nullptr) {
|
||||||
surfaceUpdate = true;
|
surfaceUpdate = true;
|
||||||
} else
|
return;
|
||||||
surfaceUpdate = (Surface == nullptr);
|
}
|
||||||
if (!bufferQueue.displayQueue.empty()) {
|
if (!bufferQueue.displayQueue.empty()) {
|
||||||
auto &buffer = bufferQueue.displayQueue.front();
|
auto &buffer = bufferQueue.displayQueue.front();
|
||||||
bufferQueue.displayQueue.pop();
|
bufferQueue.displayQueue.pop();
|
||||||
|
@ -62,7 +62,7 @@ namespace skyline::gpu {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
sched_yield();
|
asm("yield");
|
||||||
}
|
}
|
||||||
struct {
|
struct {
|
||||||
u32 slot;
|
u32 slot;
|
||||||
|
@ -8,6 +8,7 @@
|
|||||||
#include "nce.h"
|
#include "nce.h"
|
||||||
|
|
||||||
extern bool Halt;
|
extern bool Halt;
|
||||||
|
extern jobject Surface;
|
||||||
extern skyline::GroupMutex jniMtx;
|
extern skyline::GroupMutex jniMtx;
|
||||||
|
|
||||||
namespace skyline {
|
namespace skyline {
|
||||||
@ -16,10 +17,17 @@ namespace skyline {
|
|||||||
state.thread = state.process->threads.at(thread);
|
state.thread = state.process->threads.at(thread);
|
||||||
state.ctx = reinterpret_cast<ThreadContext *>(state.thread->ctxMemory->kernel.address);
|
state.ctx = reinterpret_cast<ThreadContext *>(state.thread->ctxMemory->kernel.address);
|
||||||
while (true) {
|
while (true) {
|
||||||
std::lock_guard jniGd(jniMtx);
|
asm("yield");
|
||||||
if (Halt)
|
if (Halt)
|
||||||
break;
|
break;
|
||||||
|
if (!Surface)
|
||||||
|
continue;
|
||||||
if (state.ctx->state == ThreadState::WaitKernel) {
|
if (state.ctx->state == ThreadState::WaitKernel) {
|
||||||
|
std::lock_guard jniGd(jniMtx);
|
||||||
|
if (Halt)
|
||||||
|
break;
|
||||||
|
if (!Surface)
|
||||||
|
continue;
|
||||||
const u16 svc = static_cast<const u16>(state.ctx->commandId);
|
const u16 svc = static_cast<const u16>(state.ctx->commandId);
|
||||||
try {
|
try {
|
||||||
if (kernel::svc::SvcTable[svc]) {
|
if (kernel::svc::SvcTable[svc]) {
|
||||||
@ -43,11 +51,14 @@ namespace skyline {
|
|||||||
} catch (...) {
|
} catch (...) {
|
||||||
state.logger->Error("An unknown exception has occurred");
|
state.logger->Error("An unknown exception has occurred");
|
||||||
}
|
}
|
||||||
if (thread == state.process->pid) {
|
if (!Halt) {
|
||||||
jniMtx.lock(GroupMutex::Group::Group2);
|
if (thread == state.process->pid) {
|
||||||
state.os->KillThread(thread);
|
jniMtx.lock(GroupMutex::Group::Group2);
|
||||||
Halt = true;
|
state.os->KillThread(thread);
|
||||||
jniMtx.unlock();
|
Halt = true;
|
||||||
|
jniMtx.unlock();
|
||||||
|
} else
|
||||||
|
state.os->KillThread(thread);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -60,15 +71,17 @@ namespace skyline {
|
|||||||
|
|
||||||
void NCE::Execute() {
|
void NCE::Execute() {
|
||||||
while (true) {
|
while (true) {
|
||||||
std::lock_guard jniGd(jniMtx);
|
std::lock_guard guard(jniMtx);
|
||||||
if (Halt)
|
if (Halt)
|
||||||
break;
|
break;
|
||||||
state.os->serviceManager.Loop();
|
state.os->serviceManager.Loop();
|
||||||
state.gpu->Loop();
|
state.gpu->Loop();
|
||||||
}
|
}
|
||||||
jniMtx.lock(GroupMutex::Group::Group2);
|
if (!Halt) {
|
||||||
Halt = true;
|
jniMtx.lock(GroupMutex::Group::Group2);
|
||||||
jniMtx.unlock();
|
Halt = true;
|
||||||
|
jniMtx.unlock();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -93,7 +106,7 @@ namespace skyline {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void NCE::ExecuteFunction(ThreadCall call, Registers &funcRegs) {
|
void NCE::ExecuteFunction(ThreadCall call, Registers &funcRegs) {
|
||||||
if(state.process->status == kernel::type::KProcess::Status::Exiting)
|
if (state.process->status == kernel::type::KProcess::Status::Exiting)
|
||||||
throw exception("Executing function on Exiting process");
|
throw exception("Executing function on Exiting process");
|
||||||
auto thread = state.thread ? state.thread : state.process->threads.at(state.process->pid);
|
auto thread = state.thread ? state.thread : state.process->threads.at(state.process->pid);
|
||||||
ExecuteFunctionCtx(call, funcRegs, reinterpret_cast<ThreadContext *>(thread->ctxMemory->kernel.address));
|
ExecuteFunctionCtx(call, funcRegs, reinterpret_cast<ThreadContext *>(thread->ctxMemory->kernel.address));
|
||||||
@ -151,67 +164,23 @@ namespace skyline {
|
|||||||
state.logger->Debug("CPU Context:{}", regStr);
|
state.logger->Debug("CPU Context:{}", regStr);
|
||||||
}
|
}
|
||||||
|
|
||||||
const std::array<u32, 18> cntpctEl0X0 = {
|
const std::array<u32, 16> CntpctEl0 = {
|
||||||
0xA9BF0BE1, // STP X1, X2, [SP, #-16]!
|
0xD10083FF, // SUB SP, SP, #32
|
||||||
0x3C9F0FE0, // STR Q0, [SP, #-16]!
|
0xA90107E0, // STP X0, X1, [SP, #16]
|
||||||
0x3C9F0FE1, // STR Q1, [SP, #-16]!
|
0xD28F0860, // MOV X0, #30787
|
||||||
0x3C9F0FE2, // STR Q2, [SP, #-16]!
|
0xF2AE3680, // MOVK X0, #29108, LSL #16
|
||||||
0xD53BE001, // MRS X1, CNTFRQ_EL0
|
0xD53BE001, // MRS X1, CNTFRQ_EL0
|
||||||
0xD53BE042, // MRS X2, CNTVCT_EL0
|
0xF2CB5880, // MOVK X0, #23236, LSL #32
|
||||||
0x9E630020, // UCVTF D0, X0
|
0xD345FC21, // LSR X1, X1, #5
|
||||||
0xD2C9F001, // MOV X1, 87411174408192
|
0xF2E14F80, // MOVK X0, #2684, LSL #48
|
||||||
0xF2E82E41, // MOVK X1, 0x4172, LSL 48
|
0x9BC07C21, // UMULH X1, X1, X0
|
||||||
0x9E670022, // FMOV D2, X1
|
0xD347FC21, // LSR X1, X1, #7
|
||||||
0x9E630041, // UCVTF D1, X1
|
0xD53BE040, // MRS X0, CNTVCT_EL0
|
||||||
0x1E621800, // FDIV D0, D0, D2
|
0x9AC10801, // UDIV X1, X0, X1
|
||||||
0x1E610800, // FMUL D0, D0, D1
|
0x8B010421, // ADD X1, X1, X1, LSL #1
|
||||||
0x9E790000, // FCVTZU X0, D0
|
0xD37AE420, // LSL X0, X1, #6
|
||||||
0x3CC107E2, // LDR Q2, [SP], #16
|
0xF90003E0, // STR X0, [SP, #0]
|
||||||
0x3CC107E1, // LDR Q1, [SP], #16
|
0xA94107E0, // LDP X0, X1, [SP, #16]
|
||||||
0x3CC107E0, // LDR Q0, [SP], #16
|
|
||||||
0xA8C10BE1, // LDP X1, X2, [SP], #16
|
|
||||||
};
|
|
||||||
|
|
||||||
const std::array<u32, 18> cntpctEl0X1 = {
|
|
||||||
0xA9BF0BE0, // STP X0, X2, [SP, #-16]!
|
|
||||||
0x3C9F0FE0, // STR Q0, [SP, #-16]!
|
|
||||||
0x3C9F0FE1, // STR Q1, [SP, #-16]!
|
|
||||||
0x3C9F0FE2, // STR Q2, [SP, #-16]!
|
|
||||||
0xD53BE000, // MRS X0, CNTFRQ_EL0
|
|
||||||
0xD53BE042, // MRS X2, CNTVCT_EL0
|
|
||||||
0x9E630020, // UCVTF D0, X0
|
|
||||||
0xD2C9F000, // MOV X0, 87411174408192
|
|
||||||
0xF2E82E40, // MOVK X0, 0x4172, LSL 48
|
|
||||||
0x9E670002, // FMOV D2, X0
|
|
||||||
0x9E630041, // UCVTF D1, X2
|
|
||||||
0x1E621800, // FDIV D0, D0, D2
|
|
||||||
0x1E610800, // FMUL D0, D0, D1
|
|
||||||
0x9E790001, // FCVTZU X0, D0
|
|
||||||
0x3CC107E2, // LDR Q2, [SP], #16
|
|
||||||
0x3CC107E1, // LDR Q1, [SP], #16
|
|
||||||
0x3CC107E0, // LDR Q0, [SP], #16
|
|
||||||
0xA8C10BE0, // LDP X0, X2, [SP], #16
|
|
||||||
};
|
|
||||||
|
|
||||||
std::array<u32, 18> cntpctEl0Xn = {
|
|
||||||
0xA9BF07E0, // STP X0, X1, [SP, #-16]!
|
|
||||||
0x3C9F0FE0, // STR Q0, [SP, #-16]!
|
|
||||||
0x3C9F0FE1, // STR Q1, [SP, #-16]!
|
|
||||||
0x3C9F0FE2, // STR Q2, [SP, #-16]!
|
|
||||||
0xD53BE000, // MRS X0, CNTFRQ_EL0
|
|
||||||
0xD53BE041, // MRS X1, CNTVCT_EL0
|
|
||||||
0x9E630000, // UCVTF D0, X0
|
|
||||||
0xD2C9F000, // MOV X0, 87411174408192
|
|
||||||
0xF2E82E40, // MOVK X0, 0x4172, LSL 48
|
|
||||||
0x9E670002, // FMOV D2, X0
|
|
||||||
0x9E630021, // UCVTF D1, X1
|
|
||||||
0x1E621800, // FDIV D0, D0, D2
|
|
||||||
0x1E610800, // FMUL D0, D0, D1
|
|
||||||
0x00000000, // FCVTZU Xn, D0 (Set at runtime)
|
|
||||||
0x3CC107E2, // LDR Q2, [SP], #16
|
|
||||||
0x3CC107E1, // LDR Q1, [SP], #16
|
|
||||||
0x3CC107E0, // LDR Q0, [SP], #16
|
|
||||||
0xA8C107E0, // LDP X0, X1, [SP], #16
|
|
||||||
};
|
};
|
||||||
|
|
||||||
std::vector<u32> NCE::PatchCode(std::vector<u8> &code, u64 baseAddress, i64 offset) {
|
std::vector<u32> NCE::PatchCode(std::vector<u8> &code, u64 baseAddress, i64 offset) {
|
||||||
@ -232,6 +201,10 @@ namespace skyline {
|
|||||||
reinterpret_cast<void *>(&guest::svcHandler), guest::svcHandlerSize);
|
reinterpret_cast<void *>(&guest::svcHandler), guest::svcHandlerSize);
|
||||||
offset += guest::svcHandlerSize;
|
offset += guest::svcHandlerSize;
|
||||||
|
|
||||||
|
static u64 frequency{};
|
||||||
|
if (!frequency)
|
||||||
|
asm("MRS %0, CNTFRQ_EL0" : "=r"(frequency));
|
||||||
|
|
||||||
for (u32 *address = start; address < end; address++) {
|
for (u32 *address = start; address < end; address++) {
|
||||||
auto instrSvc = reinterpret_cast<instr::Svc *>(address);
|
auto instrSvc = reinterpret_cast<instr::Svc *>(address);
|
||||||
auto instrMrs = reinterpret_cast<instr::Mrs *>(address);
|
auto instrMrs = reinterpret_cast<instr::Mrs *>(address);
|
||||||
@ -275,9 +248,9 @@ namespace skyline {
|
|||||||
strX0 = 0xF81F0FE0; // STR X0, [SP, #-16]!
|
strX0 = 0xF81F0FE0; // STR X0, [SP, #-16]!
|
||||||
offset += sizeof(strX0);
|
offset += sizeof(strX0);
|
||||||
}
|
}
|
||||||
u32 mrsX0 = 0xD53BD040; // MRS X0, TPIDR_EL0
|
const u32 mrsX0 = 0xD53BD040; // MRS X0, TPIDR_EL0
|
||||||
offset += sizeof(mrsX0);
|
offset += sizeof(mrsX0);
|
||||||
u32 ldrTls = 0xF9408000; // LDR X0, [X0, #256]
|
const u32 ldrTls = 0xF9408000; // LDR X0, [X0, #256]
|
||||||
offset += sizeof(ldrTls);
|
offset += sizeof(ldrTls);
|
||||||
u32 movXn{};
|
u32 movXn{};
|
||||||
u32 ldrX0{};
|
u32 ldrX0{};
|
||||||
@ -300,41 +273,41 @@ namespace skyline {
|
|||||||
if (ldrX0)
|
if (ldrX0)
|
||||||
patch.push_back(ldrX0);
|
patch.push_back(ldrX0);
|
||||||
patch.push_back(bret.raw);
|
patch.push_back(bret.raw);
|
||||||
} else if (instrMrs->srcReg == constant::CntpctEl0) {
|
} else if (frequency != constant::TegraX1Freq) {
|
||||||
instr::B bjunc(offset);
|
if (instrMrs->srcReg == constant::CntpctEl0) {
|
||||||
if (instrMrs->destReg == 0)
|
instr::B bjunc(offset);
|
||||||
offset += cntpctEl0X0.size() * sizeof(u32);
|
offset += CntpctEl0.size() * sizeof(u32);
|
||||||
else if (instrMrs->destReg == 1)
|
instr::Ldr ldr(0xF94003E0); // LDR XOUT, [SP]
|
||||||
offset += cntpctEl0X1.size() * sizeof(u32);
|
ldr.destReg = instrMrs->destReg;
|
||||||
else
|
offset += sizeof(ldr);
|
||||||
offset += cntpctEl0Xn.size() * sizeof(u32);
|
const u32 addSp = 0x910083FF; // ADD SP, SP, #32
|
||||||
instr::B bret(-offset + sizeof(u32));
|
offset += sizeof(addSp);
|
||||||
offset += sizeof(bret);
|
instr::B bret(-offset + sizeof(u32));
|
||||||
|
offset += sizeof(bret);
|
||||||
|
|
||||||
*address = bjunc.raw;
|
*address = bjunc.raw;
|
||||||
if (instrMrs->destReg == 0)
|
for (const auto &instr : CntpctEl0)
|
||||||
for (auto &instr : cntpctEl0X0)
|
|
||||||
patch.push_back(instr);
|
patch.push_back(instr);
|
||||||
else if (instrMrs->destReg == 1)
|
patch.push_back(ldr.raw);
|
||||||
for (auto &instr : cntpctEl0X1)
|
patch.push_back(addSp);
|
||||||
patch.push_back(instr);
|
patch.push_back(bret.raw);
|
||||||
else {
|
} else if (instrMrs->srcReg == constant::CntfrqEl0) {
|
||||||
cntpctEl0Xn[13] = instr::Fcvtzu(regs::X(instrMrs->destReg), 0).raw;
|
instr::B bjunc(offset);
|
||||||
for (auto &instr : cntpctEl0Xn)
|
auto movFreq = instr::MoveU32Reg(static_cast<regs::X>(instrMrs->destReg), constant::TegraX1Freq);
|
||||||
|
offset += sizeof(u32) * movFreq.size();
|
||||||
|
instr::B bret(-offset + sizeof(u32));
|
||||||
|
offset += sizeof(bret);
|
||||||
|
|
||||||
|
*address = bjunc.raw;
|
||||||
|
for (auto &instr : movFreq)
|
||||||
patch.push_back(instr);
|
patch.push_back(instr);
|
||||||
|
patch.push_back(bret.raw);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (instrMrs->srcReg == constant::CntpctEl0) {
|
||||||
|
instr::Mrs mrs(constant::CntvctEl0, regs::X(instrMrs->destReg));
|
||||||
|
*address = mrs.raw;
|
||||||
}
|
}
|
||||||
patch.push_back(bret.raw);
|
|
||||||
} else if (instrMrs->srcReg == constant::CntfrqEl0) {
|
|
||||||
instr::B bjunc(offset);
|
|
||||||
auto movFreq = instr::MoveU32Reg(static_cast<regs::X>(instrMrs->destReg), constant::TegraX1Freq);
|
|
||||||
offset += sizeof(u32) * movFreq.size();
|
|
||||||
instr::B bret(-offset + sizeof(u32));
|
|
||||||
offset += sizeof(bret);
|
|
||||||
|
|
||||||
*address = bjunc.raw;
|
|
||||||
for (auto &instr : movFreq)
|
|
||||||
patch.push_back(instr);
|
|
||||||
patch.push_back(bret.raw);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
offset -= sizeof(u32);
|
offset -= sizeof(u32);
|
||||||
|
@ -278,10 +278,8 @@ namespace skyline::guest {
|
|||||||
.sa_sigaction = reinterpret_cast<void (*)(int, struct siginfo *, void *)>(reinterpret_cast<void *>(signalHandler)),
|
.sa_sigaction = reinterpret_cast<void (*)(int, struct siginfo *, void *)>(reinterpret_cast<void *>(signalHandler)),
|
||||||
.sa_flags = SA_SIGINFO,
|
.sa_flags = SA_SIGINFO,
|
||||||
};
|
};
|
||||||
/*
|
|
||||||
for (int signal : {SIGILL, SIGTRAP, SIGBUS, SIGFPE, SIGSEGV})
|
for (int signal : {SIGILL, SIGTRAP, SIGBUS, SIGFPE, SIGSEGV})
|
||||||
sigaction(signal, &sigact, nullptr);
|
sigaction(signal, &sigact, nullptr);
|
||||||
*/
|
|
||||||
ctx->state = ThreadState::Running;
|
ctx->state = ThreadState::Running;
|
||||||
asm("MOV LR, %0\n\t"
|
asm("MOV LR, %0\n\t"
|
||||||
"MOV X0, %1\n\t"
|
"MOV X0, %1\n\t"
|
||||||
|
@ -302,30 +302,33 @@ namespace skyline {
|
|||||||
};
|
};
|
||||||
static_assert(sizeof(Movk) == sizeof(u32));
|
static_assert(sizeof(Movk) == sizeof(u32));
|
||||||
|
|
||||||
const std::array<u32, 4> MoveU64Reg(regs::X destReg, u64 value) {
|
const std::vector<u32> MoveU64Reg(regs::X destReg, u64 value) {
|
||||||
union {
|
union {
|
||||||
u64 val;
|
u64 val;
|
||||||
struct {
|
struct {
|
||||||
u16 v0;
|
u16 v0;
|
||||||
u16 v16;
|
u16 v16;
|
||||||
u16 v32;
|
u16 v32;
|
||||||
u16 v64;
|
u16 v48;
|
||||||
};
|
};
|
||||||
} val;
|
} val;
|
||||||
val.val = value;
|
val.val = value;
|
||||||
std::array<u32, 4> instr;
|
std::vector<u32> instr;
|
||||||
instr::Movz mov0(destReg, val.v0, 0);
|
instr::Movz mov0(destReg, val.v0, 0);
|
||||||
instr[0] = mov0.raw;
|
instr.push_back(mov0.raw);
|
||||||
instr::Movk mov16(destReg, val.v16, 16);
|
instr::Movk mov16(destReg, val.v16, 16);
|
||||||
instr[1] = mov16.raw;
|
if (val.v16)
|
||||||
|
instr.push_back(mov16.raw);
|
||||||
instr::Movk mov32(destReg, val.v32, 32);
|
instr::Movk mov32(destReg, val.v32, 32);
|
||||||
instr[2] = mov32.raw;
|
if (val.v32)
|
||||||
instr::Movk mov64(destReg, val.v64, 48);
|
instr.push_back(mov32.raw);
|
||||||
instr[3] = mov64.raw;
|
instr::Movk mov48(destReg, val.v48, 48);
|
||||||
|
if (val.v48)
|
||||||
|
instr.push_back(mov48.raw);
|
||||||
return instr;
|
return instr;
|
||||||
}
|
}
|
||||||
|
|
||||||
const std::array<u32, 2> MoveU32Reg(regs::X destReg, u32 value) {
|
const std::vector<u32> MoveU32Reg(regs::X destReg, u32 value) {
|
||||||
union {
|
union {
|
||||||
u32 val;
|
u32 val;
|
||||||
struct {
|
struct {
|
||||||
@ -334,11 +337,12 @@ namespace skyline {
|
|||||||
};
|
};
|
||||||
} val;
|
} val;
|
||||||
val.val = value;
|
val.val = value;
|
||||||
std::array<u32, 2> instr;
|
std::vector<u32> instr;
|
||||||
instr::Movz mov0(destReg, val.v0, 0);
|
instr::Movz mov0(destReg, val.v0, 0);
|
||||||
instr[0] = mov0.raw;
|
instr.push_back(mov0.raw);
|
||||||
instr::Movk mov16(destReg, val.v16, 16);
|
instr::Movk mov16(destReg, val.v16, 16);
|
||||||
instr[1] = mov16.raw;
|
if (val.v16)
|
||||||
|
instr.push_back(mov16.raw);
|
||||||
return instr;
|
return instr;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -398,44 +402,37 @@ namespace skyline {
|
|||||||
static_assert(sizeof(Mov) == sizeof(u32));
|
static_assert(sizeof(Mov) == sizeof(u32));
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief A bit-field struct that encapsulates a FCVTZU (Scalar, Integer) instruction. See https://developer.arm.com/docs/ddi0602/d/simd-and-floating-point-instructions-alphabetic-order/fcvtzu-scalar-integer-floating-point-convert-to-unsigned-integer-rounding-toward-zero-scalar.
|
* @brief A bit-field struct that encapsulates a LDR (immediate) instruction. See https://developer.arm.com/docs/ddi0596/e/base-instructions-alphabetic-order/ldr-immediate-load-register-immediate.
|
||||||
*/
|
*/
|
||||||
struct Fcvtzu {
|
struct Ldr {
|
||||||
public:
|
public:
|
||||||
/**
|
/**
|
||||||
* @brief Creates a FCVTZU (Scalar, Integer) instruction
|
* @brief Creates a LDR (immediate) instruction
|
||||||
* @param destReg The destination Xn register to store the value in
|
* @param raw The raw value of the whole instruction
|
||||||
* @param srcReg The source Dn register to retrieve the value from
|
|
||||||
*/
|
*/
|
||||||
Fcvtzu(regs::X destReg, u8 srcReg) {
|
Ldr(u32 raw) : raw(raw) {}
|
||||||
this->destReg = static_cast<u8>(destReg);
|
|
||||||
this->srcReg = static_cast<u8>(srcReg);
|
|
||||||
sig0 = 0xE40;
|
|
||||||
ftype = 1;
|
|
||||||
sig1 = 0x1E;
|
|
||||||
sf = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Returns if the opcode is valid or not
|
* @brief Returns if the opcode is valid or not
|
||||||
* @return If the opcode represents a valid FCVTZU instruction
|
* @return If the opcode represents a valid FCVTZU instruction
|
||||||
*/
|
*/
|
||||||
inline bool Verify() {
|
inline bool Verify() {
|
||||||
return (sig0 == 0xE40 && sig1 == 0x1E);
|
return (sig0 == 0x0 && sig1 == 0x1CA && sig2 == 0x1);
|
||||||
}
|
}
|
||||||
|
|
||||||
union {
|
union {
|
||||||
struct __attribute__((packed)) {
|
struct __attribute__((packed)) {
|
||||||
u8 destReg : 5;
|
u8 destReg : 5;
|
||||||
u8 srcReg : 5;
|
u8 srcReg : 5;
|
||||||
u32 sig0 : 12;
|
u8 sig0 : 2;
|
||||||
u8 ftype : 2;
|
u16 imm : 9;
|
||||||
u8 sig1 : 7;
|
u16 sig1 : 9;
|
||||||
u8 sf : 1;
|
u8 x : 1;
|
||||||
|
u8 sig2 : 1;
|
||||||
};
|
};
|
||||||
u32 raw{};
|
u32 raw{};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
static_assert(sizeof(Fcvtzu) == sizeof(u32));
|
static_assert(sizeof(Ldr) == sizeof(u32));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
package emu.skyline
|
package emu.skyline
|
||||||
|
|
||||||
|
import android.content.Intent
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.os.ParcelFileDescriptor
|
import android.os.ParcelFileDescriptor
|
||||||
@ -19,47 +20,61 @@ class GameActivity : AppCompatActivity(), SurfaceHolder.Callback, InputQueue.Cal
|
|||||||
System.loadLibrary("skyline") // libskyline.so
|
System.loadLibrary("skyline") // libskyline.so
|
||||||
}
|
}
|
||||||
|
|
||||||
private lateinit var rom: Uri
|
|
||||||
private lateinit var romFd: ParcelFileDescriptor
|
private lateinit var romFd: ParcelFileDescriptor
|
||||||
private lateinit var preferenceFd: ParcelFileDescriptor
|
private lateinit var preferenceFd: ParcelFileDescriptor
|
||||||
private lateinit var logFd: ParcelFileDescriptor
|
private lateinit var logFd: ParcelFileDescriptor
|
||||||
private var surface: Surface? = null
|
private var surface: Surface? = null
|
||||||
private var inputQueue: Long = 0L
|
private var inputQueue: Long = 0L
|
||||||
|
private var shouldFinish: Boolean = true
|
||||||
private lateinit var gameThread: Thread
|
private lateinit var gameThread: Thread
|
||||||
|
|
||||||
private external fun executeRom(romString: String, romType: Int, romFd: Int, preferenceFd: Int, logFd: Int)
|
private external fun executeRom(romString: String, romType: Int, romFd: Int, preferenceFd: Int, logFd: Int)
|
||||||
private external fun setHalt(halt: Boolean)
|
private external fun setHalt(halt: Boolean)
|
||||||
private external fun setSurface(surface: Surface?)
|
private external fun setSurface(surface: Surface?)
|
||||||
|
|
||||||
|
fun executeRom(rom : Uri) {
|
||||||
|
val romType = getTitleFormat(rom, contentResolver).ordinal
|
||||||
|
romFd = contentResolver.openFileDescriptor(rom, "r")!!
|
||||||
|
gameThread = Thread {
|
||||||
|
while ((surface == null))
|
||||||
|
Thread.yield()
|
||||||
|
executeRom(Uri.decode(rom.toString()), romType, romFd.fd, preferenceFd.fd, logFd.fd)
|
||||||
|
if (shouldFinish)
|
||||||
|
runOnUiThread { finish() }
|
||||||
|
}
|
||||||
|
gameThread.start()
|
||||||
|
}
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
setContentView(R.layout.game_activity)
|
setContentView(R.layout.game_activity)
|
||||||
rom = intent.data!!
|
|
||||||
val romType = getTitleFormat(rom, contentResolver).ordinal
|
|
||||||
romFd = contentResolver.openFileDescriptor(rom, "r")!!
|
|
||||||
val preference = File("${applicationInfo.dataDir}/shared_prefs/${applicationInfo.packageName}_preferences.xml")
|
val preference = File("${applicationInfo.dataDir}/shared_prefs/${applicationInfo.packageName}_preferences.xml")
|
||||||
preferenceFd = ParcelFileDescriptor.open(preference, ParcelFileDescriptor.MODE_READ_WRITE)
|
preferenceFd = ParcelFileDescriptor.open(preference, ParcelFileDescriptor.MODE_READ_WRITE)
|
||||||
val log = File("${applicationInfo.dataDir}/skyline.log")
|
val log = File("${applicationInfo.dataDir}/skyline.log")
|
||||||
logFd = ParcelFileDescriptor.open(log, ParcelFileDescriptor.MODE_CREATE or ParcelFileDescriptor.MODE_READ_WRITE)
|
logFd = ParcelFileDescriptor.open(log, ParcelFileDescriptor.MODE_CREATE or ParcelFileDescriptor.MODE_READ_WRITE)
|
||||||
window.setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN)
|
window.setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN)
|
||||||
game_view.holder.addCallback(this)
|
game_view.holder.addCallback(this)
|
||||||
//window.takeInputQueue(this)
|
executeRom(intent.data!!)
|
||||||
gameThread = Thread {
|
}
|
||||||
while ((surface == null))
|
|
||||||
Thread.yield()
|
override fun onNewIntent(intent: Intent?) {
|
||||||
executeRom(Uri.decode(rom.toString()), romType, romFd.fd, preferenceFd.fd, logFd.fd)
|
shouldFinish = false
|
||||||
runOnUiThread { finish() }
|
setHalt(true)
|
||||||
}
|
gameThread.join()
|
||||||
gameThread.start()
|
shouldFinish = true
|
||||||
|
romFd.close()
|
||||||
|
executeRom(intent?.data!!)
|
||||||
|
super.onNewIntent(intent)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onDestroy() {
|
override fun onDestroy() {
|
||||||
super.onDestroy()
|
shouldFinish = false
|
||||||
setHalt(true)
|
setHalt(true)
|
||||||
gameThread.join()
|
gameThread.join()
|
||||||
romFd.close()
|
romFd.close()
|
||||||
preferenceFd.close()
|
preferenceFd.close()
|
||||||
logFd.close()
|
logFd.close()
|
||||||
|
super.onDestroy()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun surfaceCreated(holder: SurfaceHolder?) {
|
override fun surfaceCreated(holder: SurfaceHolder?) {
|
||||||
|
Loading…
Reference in New Issue
Block a user