diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml
index 1acfbe56..d51d0e81 100644
--- a/.idea/inspectionProfiles/Project_Default.xml
+++ b/.idea/inspectionProfiles/Project_Default.xml
@@ -947,7 +947,10 @@
-
+
+
+
+
@@ -958,13 +961,19 @@
-
+
+
+
+
+
-
+
+
+
@@ -975,12 +984,22 @@
-
+
+
+
-
-
+
+
+
+
+
+
+
+
+
+
@@ -992,10 +1011,14 @@
-
+
+
+
-
+
+
+
@@ -1005,12 +1028,26 @@
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -1021,8 +1058,16 @@
-
-
+
+
+
+
+
+
+
+
+
+
@@ -1048,10 +1093,25 @@
-
+
+
+
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -1146,7 +1206,7 @@
-
+
@@ -1584,7 +1644,7 @@
-
+
@@ -1783,7 +1843,16 @@
-
+
+
+
@@ -1874,7 +1943,7 @@
-
+
@@ -2081,8 +2150,8 @@
-
-
+
+
@@ -2112,7 +2181,15 @@
-
+
+
+
+
+
+
+
+
+
@@ -2465,5 +2542,17 @@
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/cpp/main.cpp b/app/src/main/cpp/main.cpp
index c7db4637..716d7eda 100644
--- a/app/src/main/cpp/main.cpp
+++ b/app/src/main/cpp/main.cpp
@@ -7,7 +7,7 @@
bool Halt;
jobject Surface;
uint FaultCount;
-skyline::GroupMutex jniMtx;
+skyline::GroupMutex JniMtx;
void signalHandler(int signal) {
syslog(LOG_ERR, "Halting program due to signal: %s", strsignal(signal));
@@ -56,18 +56,18 @@ extern "C" JNIEXPORT void Java_emu_skyline_GameActivity_executeRom(JNIEnv *env,
}
extern "C" JNIEXPORT void Java_emu_skyline_GameActivity_setHalt(JNIEnv *env, jobject instance, jboolean halt) {
- jniMtx.lock(skyline::GroupMutex::Group::Group2);
+ JniMtx.lock(skyline::GroupMutex::Group::Group2);
Halt = halt;
- jniMtx.unlock();
+ JniMtx.unlock();
}
extern "C" JNIEXPORT void Java_emu_skyline_GameActivity_setSurface(JNIEnv *env, jobject instance, jobject surface) {
- jniMtx.lock(skyline::GroupMutex::Group::Group2);
+ JniMtx.lock(skyline::GroupMutex::Group::Group2);
if (!env->IsSameObject(Surface, nullptr))
env->DeleteGlobalRef(Surface);
if (!env->IsSameObject(surface, nullptr))
Surface = env->NewGlobalRef(surface);
else
Surface = surface;
- jniMtx.unlock();
+ JniMtx.unlock();
}
diff --git a/app/src/main/cpp/skyline/nce.cpp b/app/src/main/cpp/skyline/nce.cpp
index b191d995..72745346 100644
--- a/app/src/main/cpp/skyline/nce.cpp
+++ b/app/src/main/cpp/skyline/nce.cpp
@@ -3,32 +3,38 @@
#include "os.h"
#include "jvm.h"
#include "nce/guest.h"
-#include "nce/instr.h"
+#include "nce/instructions.h"
#include "kernel/svc.h"
#include "nce.h"
extern bool Halt;
extern jobject Surface;
-extern skyline::GroupMutex jniMtx;
+extern skyline::GroupMutex JniMtx;
namespace skyline {
void NCE::KernelThread(pid_t thread) {
try {
state.thread = state.process->threads.at(thread);
state.ctx = reinterpret_cast(state.thread->ctxMemory->kernel.address);
+
while (true) {
asm("yield");
+
if (__predict_false(Halt))
break;
if (__predict_false(!Surface))
continue;
+
if (state.ctx->state == ThreadState::WaitKernel) {
- std::lock_guard jniGd(jniMtx);
+ std::lock_guard jniGd(JniMtx);
+
if (__predict_false(Halt))
break;
if (__predict_false(!Surface))
continue;
+
const u16 svc = static_cast(state.ctx->commandId);
+
try {
if (kernel::svc::SvcTable[svc]) {
state.logger->Debug("SVC called 0x{:X}", svc);
@@ -39,10 +45,12 @@ namespace skyline {
} catch (const std::exception &e) {
throw exception("{} (SVC: 0x{:X})", e.what(), svc);
}
+
state.ctx->state = ThreadState::WaitRun;
} else if (__predict_false(state.ctx->state == ThreadState::GuestCrash)) {
state.logger->Warn("Thread with PID {} has crashed due to signal: {}", thread, strsignal(state.ctx->commandId));
ThreadTrace();
+
state.ctx->state = ThreadState::WaitRun;
break;
}
@@ -52,12 +60,15 @@ namespace skyline {
} catch (...) {
state.logger->Error("An unknown exception has occurred");
}
+
if (!Halt) {
if (thread == state.process->pid) {
- jniMtx.lock(GroupMutex::Group::Group2);
+ JniMtx.lock(GroupMutex::Group::Group2);
+
state.os->KillThread(thread);
Halt = true;
- jniMtx.unlock();
+
+ JniMtx.unlock();
} else {
state.os->KillThread(thread);
}
@@ -74,9 +85,11 @@ namespace skyline {
void NCE::Execute() {
try {
while (true) {
- std::lock_guard guard(jniMtx);
+ std::lock_guard guard(JniMtx);
+
if (Halt)
break;
+
state.gpu->Loop();
}
} catch (const std::exception &e) {
@@ -84,10 +97,11 @@ namespace skyline {
} catch (...) {
state.logger->Error("An unknown exception has occurred");
}
+
if (!Halt) {
- jniMtx.lock(GroupMutex::Group::Group2);
+ JniMtx.lock(GroupMutex::Group::Group2);
Halt = true;
- jniMtx.unlock();
+ JniMtx.unlock();
}
}
@@ -100,10 +114,14 @@ namespace skyline {
void ExecuteFunctionCtx(ThreadCall call, Registers &funcRegs, ThreadContext *ctx) __attribute__ ((optnone)) {
ctx->commandId = static_cast(call);
Registers registers = ctx->registers;
+
while (ctx->state != ThreadState::WaitInit && ctx->state != ThreadState::WaitKernel);
+
ctx->registers = funcRegs;
ctx->state = ThreadState::WaitFunc;
+
while (ctx->state != ThreadState::WaitInit && ctx->state != ThreadState::WaitKernel);
+
funcRegs = ctx->registers;
ctx->registers = registers;
}
@@ -115,6 +133,7 @@ namespace skyline {
void NCE::ExecuteFunction(ThreadCall call, Registers &funcRegs) {
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(thread->ctxMemory->kernel.address));
}
@@ -127,10 +146,12 @@ namespace skyline {
void NCE::StartThread(u64 entryArg, u32 handle, std::shared_ptr &thread) {
auto ctx = reinterpret_cast(thread->ctxMemory->kernel.address);
while (ctx->state != ThreadState::WaitInit);
+
ctx->tpidrroEl0 = thread->tls;
ctx->registers.x0 = entryArg;
ctx->registers.x1 = handle;
ctx->state = ThreadState::WaitRun;
+
state.logger->Debug("Starting kernel thread for guest thread: {}", thread->pid);
threadMap[thread->pid] = std::make_shared(&NCE::KernelThread, this, thread->pid);
}
@@ -139,30 +160,40 @@ namespace skyline {
std::string raw;
std::string trace;
std::string regStr;
+
ctx = ctx ? ctx : state.ctx;
+
if (numHist) {
std::vector instrs(numHist);
u64 size = sizeof(u32) * numHist;
u64 offset = ctx->pc - size + (2 * sizeof(u32));
+
state.process->ReadMemory(instrs.data(), offset, size);
+
for (auto &instr : instrs) {
instr = __builtin_bswap32(instr);
+
if (offset == ctx->pc)
trace += fmt::format("\n-> 0x{:X} : 0x{:08X}", offset, instr);
else
trace += fmt::format("\n 0x{:X} : 0x{:08X}", offset, instr);
+
raw += fmt::format("{:08X}", instr);
offset += sizeof(u32);
}
}
+
if (ctx->faultAddress)
regStr += fmt::format("\nFault Address: 0x{:X}", ctx->faultAddress);
+
if (ctx->sp)
regStr += fmt::format("\nStack Pointer: 0x{:X}", ctx->sp);
+
for (u16 index = 0; index < constant::NumRegs - 1; index += 2) {
auto xStr = index < 10 ? " X" : "X";
regStr += fmt::format("\n{}{}: 0x{:<16X} {}{}: 0x{:X}", xStr, index, ctx->registers.regs[index], xStr, index + 1, ctx->registers.regs[index + 1]);
}
+
if (numHist) {
state.logger->Debug("Process Trace:{}", trace);
state.logger->Debug("Raw Instructions: 0x{}", raw);
@@ -197,7 +228,7 @@ namespace skyline {
auto instrMrs = reinterpret_cast(address);
if (instrSvc->Verify()) {
- instr::B bjunc(offset);
+ instr::B bJunc(offset);
constexpr u32 strLr = 0xF81F0FFE; // STR LR, [SP, #-16]!
offset += sizeof(strLr);
instr::BL bSvCtx(patchOffset - offset);
@@ -217,7 +248,7 @@ namespace skyline {
instr::B bret(-offset + sizeof(u32));
offset += sizeof(bret);
- *address = bjunc.raw;
+ *address = bJunc.raw;
patch.push_back(strLr);
patch.push_back(bSvCtx.raw);
for (auto &instr : movPc)
@@ -229,7 +260,7 @@ namespace skyline {
patch.push_back(bret.raw);
} else if (instrMrs->Verify()) {
if (instrMrs->srcReg == constant::TpidrroEl0) {
- instr::B bjunc(offset);
+ instr::B bJunc(offset);
u32 strX0{};
if (instrMrs->destReg != regs::X0) {
strX0 = 0xF81F0FE0; // STR X0, [SP, #-16]!
@@ -250,7 +281,7 @@ namespace skyline {
instr::B bret(-offset + sizeof(u32));
offset += sizeof(bret);
- *address = bjunc.raw;
+ *address = bJunc.raw;
if (strX0)
patch.push_back(strX0);
patch.push_back(mrsX0);
@@ -262,7 +293,7 @@ namespace skyline {
patch.push_back(bret.raw);
} else if (frequency != constant::TegraX1Freq) {
if (instrMrs->srcReg == constant::CntpctEl0) {
- instr::B bjunc(offset);
+ instr::B bJunc(offset);
offset += guest::rescaleClockSize;
instr::Ldr ldr(0xF94003E0); // LDR XOUT, [SP]
ldr.destReg = instrMrs->destReg;
@@ -272,7 +303,7 @@ namespace skyline {
instr::B bret(-offset + sizeof(u32));
offset += sizeof(bret);
- *address = bjunc.raw;
+ *address = bJunc.raw;
auto size = patch.size();
patch.resize(size + (guest::rescaleClockSize / sizeof(u32)));
std::memcpy(patch.data() + size, reinterpret_cast(&guest::RescaleClock), guest::rescaleClockSize);
@@ -280,13 +311,13 @@ namespace skyline {
patch.push_back(addSp);
patch.push_back(bret.raw);
} else if (instrMrs->srcReg == constant::CntfrqEl0) {
- instr::B bjunc(offset);
+ instr::B bJunc(offset);
auto movFreq = instr::MoveU32Reg(static_cast(instrMrs->destReg), constant::TegraX1Freq);
offset += sizeof(u32) * movFreq.size();
instr::B bret(-offset + sizeof(u32));
offset += sizeof(bret);
- *address = bjunc.raw;
+ *address = bJunc.raw;
for (auto &instr : movFreq)
patch.push_back(instr);
patch.push_back(bret.raw);
@@ -298,6 +329,7 @@ namespace skyline {
}
}
}
+
offset -= sizeof(u32);
patchOffset -= sizeof(u32);
}
diff --git a/app/src/main/cpp/skyline/nce/guest.cpp b/app/src/main/cpp/skyline/nce/guest.cpp
index 282c6d1b..8724f3b1 100644
--- a/app/src/main/cpp/skyline/nce/guest.cpp
+++ b/app/src/main/cpp/skyline/nce/guest.cpp
@@ -1,7 +1,7 @@
-#include
#include
#include
#include // This is used implicitly
+#include
#include "guest_common.h"
#define FORCE_INLINE __attribute__((always_inline)) inline // NOLINT(cppcoreguidelines-macro-usage)
@@ -98,8 +98,10 @@ namespace skyline::guest {
void SvcHandler(u64 pc, u32 svc) {
volatile ThreadContext *ctx;
asm("MRS %0, TPIDR_EL0":"=r"(ctx));
+
ctx->pc = pc;
ctx->commandId = svc;
+
if (svc == 0xB) { // svcSleepThread
switch (ctx->registers.x0) {
case 0:
@@ -119,6 +121,7 @@ namespace skyline::guest {
"LDR LR, [SP], #16":: : "x0", "x1", "x2", "x3", "x4", "x5", "x8");
break;
}
+
default: {
struct timespec spec = {
.tv_sec = static_cast(ctx->registers.x0 / 1000000000),
@@ -160,20 +163,25 @@ namespace skyline::guest {
"LDP X1, X2, [SP], #16"::"r"(ctx->registers.x0));
return;
}
+
while (true) {
ctx->state = ThreadState::WaitKernel;
+
while (ctx->state == ThreadState::WaitKernel);
+
if (ctx->state == ThreadState::WaitRun) {
break;
} else if (ctx->state == ThreadState::WaitFunc) {
if (ctx->commandId == static_cast(ThreadCall::Syscall)) {
SaveCtxStack();
LoadCtxTls();
+
asm("STR LR, [SP, #-16]!\n\t"
"MOV LR, SP\n\t"
"SVC #0\n\t"
"MOV SP, LR\n\t"
"LDR LR, [SP], #16");
+
SaveCtxTls();
LoadCtxStack();
} else if (ctx->commandId == static_cast(ThreadCall::Memcopy)) {
@@ -181,11 +189,13 @@ namespace skyline::guest {
auto dest = reinterpret_cast(ctx->registers.x1);
auto size = ctx->registers.x2;
auto end = src + size;
+
while (src < end)
*(src++) = *(dest++);
} else if (ctx->commandId == static_cast(ThreadCall::Clone)) {
SaveCtxStack();
LoadCtxTls();
+
asm("STR LR, [SP, #-16]!\n\t"
"MOV LR, SP\n\t"
"SVC #0\n\t"
@@ -225,25 +235,31 @@ namespace skyline::guest {
".parent:\n\t"
"MOV SP, LR\n\t"
"LDR LR, [SP], #16");
+
SaveCtxTls();
LoadCtxStack();
}
}
}
+
ctx->state = ThreadState::Running;
}
void SignalHandler(int signal, siginfo_t *info, ucontext_t *ucontext) {
volatile ThreadContext *ctx;
asm("MRS %0, TPIDR_EL0":"=r"(ctx));
+
for (u8 index = 0; index < 30; index++)
ctx->registers.regs[index] = ucontext->uc_mcontext.regs[index];
+
ctx->pc = ucontext->uc_mcontext.pc;
ctx->commandId = static_cast(signal);
ctx->faultAddress = ucontext->uc_mcontext.fault_address;
ctx->sp = ucontext->uc_mcontext.sp;
+
while (true) {
ctx->state = ThreadState::GuestCrash;
+
if (ctx->state == ThreadState::WaitRun)
exit(0);
}
@@ -252,20 +268,24 @@ namespace skyline::guest {
void GuestEntry(u64 address) {
volatile ThreadContext *ctx;
asm("MRS %0, TPIDR_EL0":"=r"(ctx));
+
while (true) {
ctx->state = ThreadState::WaitInit;
while (ctx->state == ThreadState::WaitInit);
+
if (ctx->state == ThreadState::WaitRun) {
break;
} else if (ctx->state == ThreadState::WaitFunc) {
if (ctx->commandId == static_cast(ThreadCall::Syscall)) {
SaveCtxStack();
LoadCtxTls();
+
asm("STR LR, [SP, #-16]!\n\t"
"MOV LR, SP\n\t"
"SVC #0\n\t"
"MOV SP, LR\n\t"
"LDR LR, [SP], #16");
+
SaveCtxTls();
LoadCtxStack();
}
@@ -274,17 +294,22 @@ namespace skyline::guest {
auto dest = reinterpret_cast(ctx->registers.x1);
auto size = ctx->registers.x2;
auto end = src + size;
+
while (src < end)
*(src++) = *(dest++);
}
}
+
struct sigaction sigact{
.sa_sigaction = reinterpret_cast(reinterpret_cast(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"
"MOV X1, %2\n\t"
@@ -317,6 +342,7 @@ namespace skyline::guest {
"MOV X28, XZR\n\t"
"MOV X29, XZR\n\t"
"RET"::"r"(address), "r"(ctx->registers.x0), "r"(ctx->registers.x1) : "x0", "x1", "lr");
+
__builtin_unreachable();
}
}
diff --git a/app/src/main/cpp/skyline/nce/guest.h b/app/src/main/cpp/skyline/nce/guest.h
index 9165b0a3..9cfce6e7 100644
--- a/app/src/main/cpp/skyline/nce/guest.h
+++ b/app/src/main/cpp/skyline/nce/guest.h
@@ -2,21 +2,41 @@
namespace skyline {
namespace guest {
- constexpr size_t saveCtxSize = 20 * sizeof(u32);
- constexpr size_t loadCtxSize = 20 * sizeof(u32);
- constexpr size_t rescaleClockSize = 16 * sizeof(u32);
+ constexpr size_t saveCtxSize = 20 * sizeof(u32); //!< The size of the SaveCtx function in 32-bit ARMv8 instructions
+ constexpr size_t loadCtxSize = 20 * sizeof(u32); //!< The size of the LoadCtx function in 32-bit ARMv8 instructions
+ constexpr size_t rescaleClockSize = 16 * sizeof(u32); //!< The size of the RescaleClock function in 32-bit ARMv8 instructions
#ifdef NDEBUG
- constexpr size_t svcHandlerSize = 225 * sizeof(u32);
+ constexpr size_t svcHandlerSize = 225 * sizeof(u32); //!< The size of the SvcHandler (Release) function in 32-bit ARMv8 instructions
#else
- constexpr size_t svcHandlerSize = 400 * sizeof(u32);
+ constexpr size_t svcHandlerSize = 400 * sizeof(u32); //!< The size of the SvcHandler (Debug) function in 32-bit ARMv8 instructions
#endif
+ /**
+ * @brief This is the entry point for all guest threads
+ * @param address The address of the actual thread entry point
+ */
void GuestEntry(u64 address);
+ /**
+ * @brief This saves the context from CPU registers into TLS
+ */
extern "C" void SaveCtx(void);
+
+ /**
+ * @brief This loads the context from TLS into CPU registers
+ */
extern "C" void LoadCtx(void);
+
+ /**
+ * @brief This rescales the clock to Tegra X1 levels and puts the output on stack
+ */
extern "C" __noreturn void RescaleClock(void);
+ /**
+ * @brief This is used to handle all SVC calls
+ * @param pc The address of PC when the call was being done
+ * @param svc The SVC ID of the SVC being called
+ */
void SvcHandler(u64 pc, u32 svc);
}
}
diff --git a/app/src/main/cpp/skyline/nce/instr.h b/app/src/main/cpp/skyline/nce/instructions.h
similarity index 70%
rename from app/src/main/cpp/skyline/nce/instr.h
rename to app/src/main/cpp/skyline/nce/instructions.h
index baf170ff..63b90069 100644
--- a/app/src/main/cpp/skyline/nce/instr.h
+++ b/app/src/main/cpp/skyline/nce/instructions.h
@@ -16,27 +16,27 @@ namespace skyline {
* @brief Creates a BRK instruction with a specific immediate value, used for generating BRK opcodes
* @param value The immediate value of the instruction
*/
- explicit Brk(u16 value) {
- sig0 = 0x0; // First 5 bits of a BRK instruction are 0
+ inline constexpr Brk(u16 value) {
+ sig0 = 0x0;
this->value = value;
- sig1 = 0x6A1; // Last 11 bits of a BRK instruction stored as u16
+ sig1 = 0x6A1;
}
/**
* @brief Returns if the opcode is valid or not
* @return If the opcode represents a valid BRK instruction
*/
- inline bool Verify() {
+ inline constexpr bool Verify() {
return (sig0 == 0x0 && sig1 == 0x6A1);
}
union {
struct {
- u8 sig0 : 5;
- u32 value : 16;
- u16 sig1 : 11;
+ u8 sig0 : 5; //!< 5-bit signature (0x0)
+ u32 value : 16; //!< 16-bit immediate value
+ u16 sig1 : 11; //!< 11-bit signature (0x6A1)
};
- u32 raw{};
+ u32 raw{}; //!< The raw value of the instruction
};
};
static_assert(sizeof(Brk) == sizeof(u32));
@@ -49,17 +49,17 @@ namespace skyline {
* @brief Returns if the opcode is valid or not
* @return If the opcode represents a valid SVC instruction
*/
- inline bool Verify() {
+ inline constexpr bool Verify() {
return (sig0 == 0x1 && sig1 == 0x6A0);
}
union {
struct {
- u8 sig0 : 5;
- u32 value : 16;
- u16 sig1 : 11;
+ u8 sig0 : 5; //!< 5-bit signature (0x0)
+ u32 value : 16; //!< 16-bit immediate value
+ u16 sig1 : 11; //!< 11-bit signature (0x6A1)
};
- u32 raw{};
+ u32 raw{}; //!< The raw value of the instruction
};
};
static_assert(sizeof(Svc) == sizeof(u32));
@@ -73,27 +73,27 @@ namespace skyline {
* @param srcReg The source system register
* @param dstReg The destination Xn register
*/
- Mrs(u32 srcReg, regs::X dstReg) {
+ inline constexpr Mrs(u32 srcReg, regs::X dstReg) {
this->srcReg = srcReg;
this->destReg = dstReg;
- sig = 0xD53; // Last 12 bits of a MRS instruction stored as u16
+ sig = 0xD53;
}
/**
* @brief Returns if the opcode is valid or not
* @return If the opcode represents a valid MRS instruction
*/
- inline bool Verify() {
+ inline constexpr bool Verify() {
return (sig == 0xD53);
}
union {
struct {
- u8 destReg : 5;
- u32 srcReg : 15;
- u16 sig : 12;
+ u8 destReg : 5; //!< 5-bit destination register
+ u32 srcReg : 15; //!< 15-bit source register
+ u16 sig : 12; //!< 16-bit signature (0xD53)
};
- u32 raw{};
+ u32 raw{}; //!< The raw value of the instruction
};
};
static_assert(sizeof(Mrs) == sizeof(u32));
@@ -107,7 +107,7 @@ namespace skyline {
* @brief Creates a B instruction with a specific offset
* @param offset The offset to encode in the instruction (Should be 32-bit aligned)
*/
- explicit B(i64 offset) {
+ inline constexpr B(i64 offset) {
this->offset = static_cast(offset / 4);
sig = 0x5;
}
@@ -116,7 +116,7 @@ namespace skyline {
* @brief Returns the offset of the instruction
* @return The offset encoded within the instruction
*/
- inline i32 Offset() {
+ inline constexpr i32 Offset() {
return offset * 4;
}
@@ -124,16 +124,16 @@ namespace skyline {
* @brief Returns if the opcode is valid or not
* @return If the opcode represents a valid Branch instruction
*/
- inline bool Verify() {
+ inline constexpr bool Verify() {
return (sig == 0x5);
}
union {
struct {
- i32 offset : 26;
- u8 sig : 6;
+ i32 offset : 26; //!< 26-bit branch offset
+ u8 sig : 6; //!< 6-bit signature (0x5)
};
- u32 raw{};
+ u32 raw{}; //!< The raw value of the instruction
};
};
static_assert(sizeof(B) == sizeof(u32));
@@ -147,7 +147,7 @@ namespace skyline {
* @brief Creates a BL instruction with a specific offset
* @param offset The offset to encode in the instruction (Should be 32-bit aligned)
*/
- explicit BL(i64 offset) {
+ inline constexpr BL(i64 offset) {
this->offset = static_cast(offset / 4);
sig = 0x25;
}
@@ -156,7 +156,7 @@ namespace skyline {
* @brief Returns the offset of the instruction
* @return The offset encoded within the instruction
*/
- inline i32 Offset() {
+ inline constexpr i32 Offset() {
return offset * 4;
}
@@ -164,16 +164,16 @@ namespace skyline {
* @brief Returns if the opcode is valid or not
* @return If the opcode represents a valid Branch Linked instruction
*/
- inline bool Verify() {
- return (sig == 0x85);
+ inline constexpr bool Verify() {
+ return (sig == 0x25);
}
union {
struct {
- i32 offset : 26;
- u8 sig : 6;
+ i32 offset : 26; //!< 26-bit branch offset
+ u8 sig : 6; //!< 6-bit signature (0x25)
};
- u32 raw{};
+ u32 raw{}; //!< The raw value of the instruction
};
};
static_assert(sizeof(BL) == sizeof(u32));
@@ -189,7 +189,7 @@ namespace skyline {
* @param imm16 The 16-bit value to store
* @param shift The offset (in bits and 16-bit aligned) in the register to store the value at
*/
- Movz(regs::X destReg, u16 imm16, u8 shift = 0) {
+ inline constexpr Movz(regs::X destReg, u16 imm16, u8 shift = 0) {
this->destReg = static_cast(destReg);
this->imm16 = imm16;
hw = static_cast(shift / 16);
@@ -203,7 +203,7 @@ namespace skyline {
* @param imm16 The 16-bit value to store
* @param shift The offset (in bits and 16-bit aligned) in the register to store the value at
*/
- Movz(regs::W destReg, u16 imm16, u8 shift = 0) {
+ inline constexpr Movz(regs::W destReg, u16 imm16, u8 shift = 0) {
this->destReg = static_cast(destReg);
this->imm16 = imm16;
hw = static_cast(shift / 16);
@@ -215,7 +215,7 @@ namespace skyline {
* @brief Returns the offset of the instruction
* @return The offset encoded within the instruction
*/
- inline u8 Shift() {
+ inline constexpr u8 Shift() {
return static_cast(hw * 16);
}
@@ -223,19 +223,19 @@ namespace skyline {
* @brief Returns if the opcode is valid or not
* @return If the opcode represents a valid MOVZ instruction
*/
- inline bool Verify() {
+ inline constexpr bool Verify() {
return (sig == 0xA5);
}
union {
struct __attribute__((packed)) {
- u8 destReg : 5;
- u16 imm16 : 16;
- u8 hw : 2;
- u8 sig : 8;
- u8 sf : 1;
+ u8 destReg : 5; //!< 5-bit destination register
+ u16 imm16 : 16; //!< 16-bit immediate value
+ u8 hw : 2; //!< 2-bit offset
+ u8 sig : 8; //!< 8-bit signature (0xA5)
+ u8 sf : 1; //!< 1-bit register type
};
- u32 raw{};
+ u32 raw{}; //!< The raw value of the instruction
};
};
static_assert(sizeof(Movz) == sizeof(u32));
@@ -251,7 +251,7 @@ namespace skyline {
* @param imm16 The 16-bit value to store
* @param shift The offset (in bits and 16-bit aligned) in the register to store the value at
*/
- Movk(regs::X destReg, u16 imm16, u8 shift = 0) {
+ inline constexpr Movk(regs::X destReg, u16 imm16, u8 shift = 0) {
this->destReg = static_cast(destReg);
this->imm16 = imm16;
hw = static_cast(shift / 16);
@@ -265,7 +265,7 @@ namespace skyline {
* @param imm16 The 16-bit value to store
* @param shift The offset (in bits and 16-bit aligned) in the register to store the value at
*/
- Movk(regs::W destReg, u16 imm16, u8 shift = 0) {
+ inline constexpr Movk(regs::W destReg, u16 imm16, u8 shift = 0) {
this->destReg = static_cast(destReg);
this->imm16 = imm16;
hw = static_cast(shift / 16);
@@ -277,7 +277,7 @@ namespace skyline {
* @brief Returns the offset of the instruction
* @return The offset encoded within the instruction
*/
- inline u8 Shift() {
+ inline constexpr u8 Shift() {
return static_cast(hw * 16);
}
@@ -285,24 +285,29 @@ namespace skyline {
* @brief Returns if the opcode is valid or not
* @return If the opcode represents a valid MOVK instruction
*/
- inline bool Verify() {
+ inline constexpr bool Verify() {
return (sig == 0xE5);
}
union {
struct __attribute__((packed)) {
- u8 destReg : 5;
- u16 imm16 : 16;
- u8 hw : 2;
- u8 sig : 8;
- u8 sf : 1;
+ u8 destReg : 5; //!< 5-bit destination register
+ u16 imm16 : 16; //!< 16-bit immediate value
+ u8 hw : 2; //!< 2-bit offset
+ u8 sig : 8; //!< 8-bit signature (0xA5)
+ u8 sf : 1; //!< 1-bit register type
};
- u32 raw{};
+ u32 raw{}; //!< The raw value of the instruction
};
};
static_assert(sizeof(Movk) == sizeof(u32));
- const std::vector MoveU64Reg(regs::X destReg, u64 value) {
+ /**
+ * @param destReg The destination register of the operation
+ * @param value The 64-bit value to insert into the register
+ * @return A vector with the instructions to insert the value
+ */
+ inline const std::vector MoveU64Reg(regs::X destReg, u64 value) {
union {
u64 val;
struct {
@@ -328,7 +333,12 @@ namespace skyline {
return instr;
}
- const std::vector MoveU32Reg(regs::X destReg, u32 value) {
+ /**
+ * @param destReg The destination register of the operation
+ * @param value The 32-bit value to insert into the register
+ * @return A vector with the instructions to insert the value
+ */
+ inline const std::vector MoveU32Reg(regs::X destReg, u32 value) {
union {
u32 val;
struct {
@@ -356,12 +366,12 @@ namespace skyline {
* @param destReg The destination Xn register to store the value in
* @param srcReg The source Xn register to retrieve the value from
*/
- Mov(regs::X destReg, regs::X srcReg) {
+ inline constexpr Mov(regs::X destReg, regs::X srcReg) {
this->destReg = static_cast(destReg);
- zeroReg = 0x1F;
+ sig0 = 0x1F;
imm6 = 0;
this->srcReg = static_cast(srcReg);
- sig = 0x150;
+ sig1 = 0x150;
sf = 1;
}
@@ -370,12 +380,12 @@ namespace skyline {
* @param destReg The destination Wn register to store the value in
* @param srcReg The source Wn register to retrieve the value from
*/
- Mov(regs::W destReg, regs::W srcReg) {
+ inline constexpr Mov(regs::W destReg, regs::W srcReg) {
this->destReg = static_cast(destReg);
- zeroReg = 0x1F;
+ sig0 = 0x1F;
imm6 = 0;
this->srcReg = static_cast(srcReg);
- sig = 0x150;
+ sig1 = 0x150;
sf = 0;
}
@@ -383,20 +393,20 @@ namespace skyline {
* @brief Returns if the opcode is valid or not
* @return If the opcode represents a valid MOVZ instruction
*/
- inline bool Verify() {
- return (sig == 0x150);
+ inline constexpr bool Verify() {
+ return (sig0 == 0x1F) && (sig1 == 0x150);
}
union {
struct __attribute__((packed)) {
- u8 destReg : 5;
- u8 zeroReg : 5;
- u8 imm6 : 6;
- u8 srcReg : 5;
- u16 sig : 10;
- u8 sf : 1;
+ u8 destReg : 5; //!< 5-bit destination register
+ u8 sig0 : 5; //!< 5-bit signature (0x1F)
+ u8 imm6 : 6; //!< 6-bit immediate value
+ u8 srcReg : 5; //!< 5-bit source register
+ u16 sig1 : 10; //!< 10-bit signature (0x150)
+ u8 sf : 1; //!< 1-bit register type
};
- u32 raw{};
+ u32 raw{}; //!< The raw value of the instruction
};
};
static_assert(sizeof(Mov) == sizeof(u32));
@@ -410,27 +420,27 @@ namespace skyline {
* @brief Creates a LDR (immediate) instruction
* @param raw The raw value of the whole instruction
*/
- Ldr(u32 raw) : raw(raw) {}
+ inline constexpr 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() {
+ inline constexpr bool Verify() {
return (sig0 == 0x0 && sig1 == 0x1CA && sig2 == 0x1);
}
union {
struct __attribute__((packed)) {
- u8 destReg : 5;
- u8 srcReg : 5;
- u8 sig0 : 2;
- u16 imm : 9;
- u16 sig1 : 9;
- u8 x : 1;
- u8 sig2 : 1;
+ u8 destReg : 5; //!< 5-bit destination register
+ u8 srcReg : 5; //!< 5-bit source register
+ u8 sig0 : 2; //!< 2-bit signature (0x0)
+ u16 imm : 9; //!< 6-bit immediate value
+ u16 sig1 : 9; //!< 9-bit signature (0x1CA)
+ u8 sf : 1; //!< 1-bit register type
+ u8 sig2 : 1; //!< 1-bit signature (0x1)
};
- u32 raw{};
+ u32 raw{}; //!< The raw value of the instruction
};
};
static_assert(sizeof(Ldr) == sizeof(u32));