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