mirror of
https://github.com/skyline-emu/skyline.git
synced 2024-11-22 10:09:24 +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/
|
||||
|
||||
# Discord plugin for IntelliJ IDEA
|
||||
.idea\discord.xml
|
||||
.idea/discord.xml
|
||||
|
@ -1,3 +1,3 @@
|
||||
<component name="DependencyValidationManager">
|
||||
<scope name="SkylineCPP" pattern="file[app]:src/main/cpp//*&&!file[Lightswitch]:*&&!file[app]:*" />
|
||||
</component>
|
||||
<scope name="SkylineCPP" pattern="file[app]:src/main/cpp//*&&!file[Skyline]:*&&!file[app]:*" />
|
||||
</component>
|
||||
|
@ -10,12 +10,18 @@
|
||||
</h1>
|
||||
|
||||
<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>
|
||||
|
||||
### 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.
|
||||
|
||||
### 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
|
||||
* Nintendo Switch is a trademark of Nintendo Co., Ltd.
|
||||
* Android is a trademark of Google LLC.
|
||||
|
@ -49,7 +49,8 @@
|
||||
<activity
|
||||
android:name="emu.skyline.GameActivity"
|
||||
android:configChanges="orientation|screenSize"
|
||||
android:screenOrientation="landscape">
|
||||
android:screenOrientation="landscape"
|
||||
android:launchMode="singleInstance">
|
||||
<meta-data
|
||||
android:name="android.support.PARENT_ACTIVITY"
|
||||
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);
|
||||
env->ReleaseStringUTFChars(romJstring, romString);
|
||||
os.Execute(romFd, static_cast<skyline::TitleFormat>(romType));
|
||||
logger->Info("Emulation has ended");
|
||||
} catch (std::exception &e) {
|
||||
logger->Error(e.what());
|
||||
} catch (...) {
|
||||
logger->Error("An unknown exception has occurred");
|
||||
}
|
||||
logger->Info("Emulation has ended");
|
||||
|
||||
auto end = std::chrono::steady_clock::now();
|
||||
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) {
|
||||
auto none = Group::None;
|
||||
constexpr u64 timeout = 1000; // The timeout in ns
|
||||
auto start = utils::GetTimeNs();
|
||||
while (next != group && !next.compare_exchange_weak(none, group)) {
|
||||
if (flag == group && ((utils::GetTimeNs() - start) > timeout)) {
|
||||
num++;
|
||||
return;
|
||||
}
|
||||
constexpr u64 timeout = 100; // The timeout in ns
|
||||
auto end = utils::GetTimeNs() + timeout;
|
||||
while (true) {
|
||||
if (next == group) {
|
||||
if (flag == group) {
|
||||
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");
|
||||
}
|
||||
while (flag != group && !flag.compare_exchange_weak(none, group))
|
||||
asm volatile("yield");
|
||||
num++;
|
||||
}
|
||||
|
||||
void GroupMutex::unlock() {
|
||||
std::lock_guard lock(mtx);
|
||||
if (!--num)
|
||||
flag.exchange(Group::None);
|
||||
flag.exchange(next);
|
||||
}
|
||||
|
||||
Settings::Settings(const int preferenceFd) {
|
||||
@ -118,6 +132,6 @@ namespace skyline {
|
||||
gpu = std::move(std::make_shared<gpu::GPU>(*this));
|
||||
}
|
||||
|
||||
thread_local std::shared_ptr<kernel::type::KThread> DeviceState::thread = 0;
|
||||
thread_local ThreadContext *DeviceState::ctx = 0;
|
||||
thread_local std::shared_ptr<kernel::type::KThread> DeviceState::thread = nullptr;
|
||||
thread_local ThreadContext *DeviceState::ctx = nullptr;
|
||||
}
|
||||
|
@ -36,7 +36,7 @@ namespace skyline {
|
||||
constexpr u8 NumRegs = 30; //!< The amount of registers that ARMv8 has
|
||||
constexpr u32 TpidrroEl0 = 0x5E83; //!< ID of TPIDRRO_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 CntvctEl0 = 0x5F02; //!< ID of CNTVCT_EL0 in MRS
|
||||
// Kernel
|
||||
@ -101,18 +101,17 @@ namespace skyline {
|
||||
* @return The current time in nanoseconds
|
||||
*/
|
||||
inline u64 GetTimeNs() {
|
||||
static u64 frequencyMs{};
|
||||
if (!frequencyMs) {
|
||||
asm("MRS %0, CNTFRQ_EL0" : "=r"(frequencyMs));
|
||||
frequencyMs *= 1000000000;
|
||||
}
|
||||
constexpr uint64_t NsInSecond = 1000000000;
|
||||
static u64 frequency{};
|
||||
if (!frequency)
|
||||
asm("MRS %0, CNTFRQ_EL0" : "=r"(frequency));
|
||||
u64 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
|
||||
* @param value The value to round up
|
||||
* @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> 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
|
||||
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.height = static_cast<u32>(ANativeWindow_getHeight(window));
|
||||
format = ANativeWindow_getFormat(window);
|
||||
surfaceUpdate = false;
|
||||
} else if (Surface == nullptr) {
|
||||
surfaceUpdate = true;
|
||||
} else
|
||||
surfaceUpdate = (Surface == nullptr);
|
||||
return;
|
||||
}
|
||||
if (!bufferQueue.displayQueue.empty()) {
|
||||
auto &buffer = bufferQueue.displayQueue.front();
|
||||
bufferQueue.displayQueue.pop();
|
||||
|
@ -62,7 +62,7 @@ namespace skyline::gpu {
|
||||
break;
|
||||
}
|
||||
}
|
||||
sched_yield();
|
||||
asm("yield");
|
||||
}
|
||||
struct {
|
||||
u32 slot;
|
||||
|
@ -8,6 +8,7 @@
|
||||
#include "nce.h"
|
||||
|
||||
extern bool Halt;
|
||||
extern jobject Surface;
|
||||
extern skyline::GroupMutex jniMtx;
|
||||
|
||||
namespace skyline {
|
||||
@ -16,10 +17,17 @@ namespace skyline {
|
||||
state.thread = state.process->threads.at(thread);
|
||||
state.ctx = reinterpret_cast<ThreadContext *>(state.thread->ctxMemory->kernel.address);
|
||||
while (true) {
|
||||
std::lock_guard jniGd(jniMtx);
|
||||
asm("yield");
|
||||
if (Halt)
|
||||
break;
|
||||
if (!Surface)
|
||||
continue;
|
||||
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);
|
||||
try {
|
||||
if (kernel::svc::SvcTable[svc]) {
|
||||
@ -43,11 +51,14 @@ namespace skyline {
|
||||
} catch (...) {
|
||||
state.logger->Error("An unknown exception has occurred");
|
||||
}
|
||||
if (thread == state.process->pid) {
|
||||
jniMtx.lock(GroupMutex::Group::Group2);
|
||||
state.os->KillThread(thread);
|
||||
Halt = true;
|
||||
jniMtx.unlock();
|
||||
if (!Halt) {
|
||||
if (thread == state.process->pid) {
|
||||
jniMtx.lock(GroupMutex::Group::Group2);
|
||||
state.os->KillThread(thread);
|
||||
Halt = true;
|
||||
jniMtx.unlock();
|
||||
} else
|
||||
state.os->KillThread(thread);
|
||||
}
|
||||
}
|
||||
|
||||
@ -60,15 +71,17 @@ namespace skyline {
|
||||
|
||||
void NCE::Execute() {
|
||||
while (true) {
|
||||
std::lock_guard jniGd(jniMtx);
|
||||
std::lock_guard guard(jniMtx);
|
||||
if (Halt)
|
||||
break;
|
||||
state.os->serviceManager.Loop();
|
||||
state.gpu->Loop();
|
||||
}
|
||||
jniMtx.lock(GroupMutex::Group::Group2);
|
||||
Halt = true;
|
||||
jniMtx.unlock();
|
||||
if (!Halt) {
|
||||
jniMtx.lock(GroupMutex::Group::Group2);
|
||||
Halt = true;
|
||||
jniMtx.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -93,7 +106,7 @@ namespace skyline {
|
||||
}
|
||||
|
||||
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");
|
||||
auto thread = state.thread ? state.thread : state.process->threads.at(state.process->pid);
|
||||
ExecuteFunctionCtx(call, funcRegs, reinterpret_cast<ThreadContext *>(thread->ctxMemory->kernel.address));
|
||||
@ -151,67 +164,23 @@ namespace skyline {
|
||||
state.logger->Debug("CPU Context:{}", regStr);
|
||||
}
|
||||
|
||||
const std::array<u32, 18> cntpctEl0X0 = {
|
||||
0xA9BF0BE1, // STP X1, X2, [SP, #-16]!
|
||||
0x3C9F0FE0, // STR Q0, [SP, #-16]!
|
||||
0x3C9F0FE1, // STR Q1, [SP, #-16]!
|
||||
0x3C9F0FE2, // STR Q2, [SP, #-16]!
|
||||
const std::array<u32, 16> CntpctEl0 = {
|
||||
0xD10083FF, // SUB SP, SP, #32
|
||||
0xA90107E0, // STP X0, X1, [SP, #16]
|
||||
0xD28F0860, // MOV X0, #30787
|
||||
0xF2AE3680, // MOVK X0, #29108, LSL #16
|
||||
0xD53BE001, // MRS X1, CNTFRQ_EL0
|
||||
0xD53BE042, // MRS X2, CNTVCT_EL0
|
||||
0x9E630020, // UCVTF D0, X0
|
||||
0xD2C9F001, // MOV X1, 87411174408192
|
||||
0xF2E82E41, // MOVK X1, 0x4172, LSL 48
|
||||
0x9E670022, // FMOV D2, X1
|
||||
0x9E630041, // UCVTF D1, X1
|
||||
0x1E621800, // FDIV D0, D0, D2
|
||||
0x1E610800, // FMUL D0, D0, D1
|
||||
0x9E790000, // FCVTZU X0, D0
|
||||
0x3CC107E2, // LDR Q2, [SP], #16
|
||||
0x3CC107E1, // LDR Q1, [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
|
||||
0xF2CB5880, // MOVK X0, #23236, LSL #32
|
||||
0xD345FC21, // LSR X1, X1, #5
|
||||
0xF2E14F80, // MOVK X0, #2684, LSL #48
|
||||
0x9BC07C21, // UMULH X1, X1, X0
|
||||
0xD347FC21, // LSR X1, X1, #7
|
||||
0xD53BE040, // MRS X0, CNTVCT_EL0
|
||||
0x9AC10801, // UDIV X1, X0, X1
|
||||
0x8B010421, // ADD X1, X1, X1, LSL #1
|
||||
0xD37AE420, // LSL X0, X1, #6
|
||||
0xF90003E0, // STR X0, [SP, #0]
|
||||
0xA94107E0, // LDP X0, X1, [SP, #16]
|
||||
};
|
||||
|
||||
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);
|
||||
offset += guest::svcHandlerSize;
|
||||
|
||||
static u64 frequency{};
|
||||
if (!frequency)
|
||||
asm("MRS %0, CNTFRQ_EL0" : "=r"(frequency));
|
||||
|
||||
for (u32 *address = start; address < end; address++) {
|
||||
auto instrSvc = reinterpret_cast<instr::Svc *>(address);
|
||||
auto instrMrs = reinterpret_cast<instr::Mrs *>(address);
|
||||
@ -275,9 +248,9 @@ namespace skyline {
|
||||
strX0 = 0xF81F0FE0; // STR X0, [SP, #-16]!
|
||||
offset += sizeof(strX0);
|
||||
}
|
||||
u32 mrsX0 = 0xD53BD040; // MRS X0, TPIDR_EL0
|
||||
const u32 mrsX0 = 0xD53BD040; // MRS X0, TPIDR_EL0
|
||||
offset += sizeof(mrsX0);
|
||||
u32 ldrTls = 0xF9408000; // LDR X0, [X0, #256]
|
||||
const u32 ldrTls = 0xF9408000; // LDR X0, [X0, #256]
|
||||
offset += sizeof(ldrTls);
|
||||
u32 movXn{};
|
||||
u32 ldrX0{};
|
||||
@ -300,41 +273,41 @@ namespace skyline {
|
||||
if (ldrX0)
|
||||
patch.push_back(ldrX0);
|
||||
patch.push_back(bret.raw);
|
||||
} else if (instrMrs->srcReg == constant::CntpctEl0) {
|
||||
instr::B bjunc(offset);
|
||||
if (instrMrs->destReg == 0)
|
||||
offset += cntpctEl0X0.size() * sizeof(u32);
|
||||
else if (instrMrs->destReg == 1)
|
||||
offset += cntpctEl0X1.size() * sizeof(u32);
|
||||
else
|
||||
offset += cntpctEl0Xn.size() * sizeof(u32);
|
||||
instr::B bret(-offset + sizeof(u32));
|
||||
offset += sizeof(bret);
|
||||
} else if (frequency != constant::TegraX1Freq) {
|
||||
if (instrMrs->srcReg == constant::CntpctEl0) {
|
||||
instr::B bjunc(offset);
|
||||
offset += CntpctEl0.size() * sizeof(u32);
|
||||
instr::Ldr ldr(0xF94003E0); // LDR XOUT, [SP]
|
||||
ldr.destReg = instrMrs->destReg;
|
||||
offset += sizeof(ldr);
|
||||
const u32 addSp = 0x910083FF; // ADD SP, SP, #32
|
||||
offset += sizeof(addSp);
|
||||
instr::B bret(-offset + sizeof(u32));
|
||||
offset += sizeof(bret);
|
||||
|
||||
*address = bjunc.raw;
|
||||
if (instrMrs->destReg == 0)
|
||||
for (auto &instr : cntpctEl0X0)
|
||||
*address = bjunc.raw;
|
||||
for (const auto &instr : CntpctEl0)
|
||||
patch.push_back(instr);
|
||||
else if (instrMrs->destReg == 1)
|
||||
for (auto &instr : cntpctEl0X1)
|
||||
patch.push_back(instr);
|
||||
else {
|
||||
cntpctEl0Xn[13] = instr::Fcvtzu(regs::X(instrMrs->destReg), 0).raw;
|
||||
for (auto &instr : cntpctEl0Xn)
|
||||
patch.push_back(ldr.raw);
|
||||
patch.push_back(addSp);
|
||||
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);
|
||||
}
|
||||
} 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);
|
||||
|
@ -278,10 +278,8 @@ namespace skyline::guest {
|
||||
.sa_sigaction = reinterpret_cast<void (*)(int, struct siginfo *, void *)>(reinterpret_cast<void *>(signalHandler)),
|
||||
.sa_flags = SA_SIGINFO,
|
||||
};
|
||||
/*
|
||||
for (int signal : {SIGILL, SIGTRAP, SIGBUS, SIGFPE, SIGSEGV})
|
||||
sigaction(signal, &sigact, nullptr);
|
||||
*/
|
||||
ctx->state = ThreadState::Running;
|
||||
asm("MOV LR, %0\n\t"
|
||||
"MOV X0, %1\n\t"
|
||||
|
@ -302,30 +302,33 @@ namespace skyline {
|
||||
};
|
||||
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 {
|
||||
u64 val;
|
||||
struct {
|
||||
u16 v0;
|
||||
u16 v16;
|
||||
u16 v32;
|
||||
u16 v64;
|
||||
u16 v48;
|
||||
};
|
||||
} val;
|
||||
val.val = value;
|
||||
std::array<u32, 4> instr;
|
||||
std::vector<u32> instr;
|
||||
instr::Movz mov0(destReg, val.v0, 0);
|
||||
instr[0] = mov0.raw;
|
||||
instr.push_back(mov0.raw);
|
||||
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[2] = mov32.raw;
|
||||
instr::Movk mov64(destReg, val.v64, 48);
|
||||
instr[3] = mov64.raw;
|
||||
if (val.v32)
|
||||
instr.push_back(mov32.raw);
|
||||
instr::Movk mov48(destReg, val.v48, 48);
|
||||
if (val.v48)
|
||||
instr.push_back(mov48.raw);
|
||||
return instr;
|
||||
}
|
||||
|
||||
const std::array<u32, 2> MoveU32Reg(regs::X destReg, u32 value) {
|
||||
const std::vector<u32> MoveU32Reg(regs::X destReg, u32 value) {
|
||||
union {
|
||||
u32 val;
|
||||
struct {
|
||||
@ -334,11 +337,12 @@ namespace skyline {
|
||||
};
|
||||
} val;
|
||||
val.val = value;
|
||||
std::array<u32, 2> instr;
|
||||
std::vector<u32> instr;
|
||||
instr::Movz mov0(destReg, val.v0, 0);
|
||||
instr[0] = mov0.raw;
|
||||
instr.push_back(mov0.raw);
|
||||
instr::Movk mov16(destReg, val.v16, 16);
|
||||
instr[1] = mov16.raw;
|
||||
if (val.v16)
|
||||
instr.push_back(mov16.raw);
|
||||
return instr;
|
||||
}
|
||||
|
||||
@ -398,44 +402,37 @@ namespace skyline {
|
||||
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:
|
||||
/**
|
||||
* @brief Creates a FCVTZU (Scalar, Integer) instruction
|
||||
* @param destReg The destination Xn register to store the value in
|
||||
* @param srcReg The source Dn register to retrieve the value from
|
||||
* @brief Creates a LDR (immediate) instruction
|
||||
* @param raw The raw value of the whole instruction
|
||||
*/
|
||||
Fcvtzu(regs::X destReg, u8 srcReg) {
|
||||
this->destReg = static_cast<u8>(destReg);
|
||||
this->srcReg = static_cast<u8>(srcReg);
|
||||
sig0 = 0xE40;
|
||||
ftype = 1;
|
||||
sig1 = 0x1E;
|
||||
sf = 1;
|
||||
}
|
||||
Ldr(u32 raw) : raw(raw) {}
|
||||
|
||||
/**
|
||||
* @brief Returns if the opcode is valid or not
|
||||
* @return If the opcode represents a valid FCVTZU instruction
|
||||
*/
|
||||
inline bool Verify() {
|
||||
return (sig0 == 0xE40 && sig1 == 0x1E);
|
||||
return (sig0 == 0x0 && sig1 == 0x1CA && sig2 == 0x1);
|
||||
}
|
||||
|
||||
union {
|
||||
struct __attribute__((packed)) {
|
||||
u8 destReg : 5;
|
||||
u8 srcReg : 5;
|
||||
u32 sig0 : 12;
|
||||
u8 ftype : 2;
|
||||
u8 sig1 : 7;
|
||||
u8 sf : 1;
|
||||
u8 sig0 : 2;
|
||||
u16 imm : 9;
|
||||
u16 sig1 : 9;
|
||||
u8 x : 1;
|
||||
u8 sig2 : 1;
|
||||
};
|
||||
u32 raw{};
|
||||
};
|
||||
};
|
||||
static_assert(sizeof(Fcvtzu) == sizeof(u32));
|
||||
static_assert(sizeof(Ldr) == sizeof(u32));
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,6 @@
|
||||
package emu.skyline
|
||||
|
||||
import android.content.Intent
|
||||
import android.net.Uri
|
||||
import android.os.Bundle
|
||||
import android.os.ParcelFileDescriptor
|
||||
@ -19,47 +20,61 @@ class GameActivity : AppCompatActivity(), SurfaceHolder.Callback, InputQueue.Cal
|
||||
System.loadLibrary("skyline") // libskyline.so
|
||||
}
|
||||
|
||||
private lateinit var rom: Uri
|
||||
private lateinit var romFd: ParcelFileDescriptor
|
||||
private lateinit var preferenceFd: ParcelFileDescriptor
|
||||
private lateinit var logFd: ParcelFileDescriptor
|
||||
private var surface: Surface? = null
|
||||
private var inputQueue: Long = 0L
|
||||
private var shouldFinish: Boolean = true
|
||||
private lateinit var gameThread: Thread
|
||||
|
||||
private external fun executeRom(romString: String, romType: Int, romFd: Int, preferenceFd: Int, logFd: Int)
|
||||
private external fun setHalt(halt: Boolean)
|
||||
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?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
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")
|
||||
preferenceFd = ParcelFileDescriptor.open(preference, ParcelFileDescriptor.MODE_READ_WRITE)
|
||||
val log = File("${applicationInfo.dataDir}/skyline.log")
|
||||
logFd = ParcelFileDescriptor.open(log, ParcelFileDescriptor.MODE_CREATE or ParcelFileDescriptor.MODE_READ_WRITE)
|
||||
window.setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN)
|
||||
game_view.holder.addCallback(this)
|
||||
//window.takeInputQueue(this)
|
||||
gameThread = Thread {
|
||||
while ((surface == null))
|
||||
Thread.yield()
|
||||
executeRom(Uri.decode(rom.toString()), romType, romFd.fd, preferenceFd.fd, logFd.fd)
|
||||
runOnUiThread { finish() }
|
||||
}
|
||||
gameThread.start()
|
||||
executeRom(intent.data!!)
|
||||
}
|
||||
|
||||
override fun onNewIntent(intent: Intent?) {
|
||||
shouldFinish = false
|
||||
setHalt(true)
|
||||
gameThread.join()
|
||||
shouldFinish = true
|
||||
romFd.close()
|
||||
executeRom(intent?.data!!)
|
||||
super.onNewIntent(intent)
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
super.onDestroy()
|
||||
shouldFinish = false
|
||||
setHalt(true)
|
||||
gameThread.join()
|
||||
romFd.close()
|
||||
preferenceFd.close()
|
||||
logFd.close()
|
||||
super.onDestroy()
|
||||
}
|
||||
|
||||
override fun surfaceCreated(holder: SurfaceHolder?) {
|
||||
|
Loading…
Reference in New Issue
Block a user