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:
◱ PixelyIon 2020-02-11 12:04:22 +05:30 committed by ◱ PixelyIon
parent 003e9c5a01
commit 66d20a9429
13 changed files with 186 additions and 180 deletions

2
.gitignore vendored
View File

@ -95,4 +95,4 @@ captures/
.vscode/ .vscode/
# Discord plugin for IntelliJ IDEA # Discord plugin for IntelliJ IDEA
.idea\discord.xml .idea/discord.xml

View File

@ -1,3 +1,3 @@
<component name="DependencyValidationManager"> <component name="DependencyValidationManager">
<scope name="SkylineCPP" pattern="file[app]:src/main/cpp//*&amp;&amp;!file[Lightswitch]:*&amp;&amp;!file[app]:*" /> <scope name="SkylineCPP" pattern="file[app]:src/main/cpp//*&amp;&amp;!file[Skyline]:*&amp;&amp;!file[app]:*" />
</component> </component>

View File

@ -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.

View File

@ -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" />

View File

@ -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()));

View File

@ -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;
} }

View File

@ -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
}; };
/** /**

View File

@ -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();

View File

@ -62,7 +62,7 @@ namespace skyline::gpu {
break; break;
} }
} }
sched_yield(); asm("yield");
} }
struct { struct {
u32 slot; u32 slot;

View File

@ -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);

View File

@ -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"

View File

@ -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));
} }
} }

View File

@ -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?) {