diff options
-rw-r--r-- | src/core/arm/arm_interface.h | 53 | ||||
-rw-r--r-- | src/core/arm/dynarmic/arm_dynarmic.cpp | 35 | ||||
-rw-r--r-- | src/core/arm/dynarmic/arm_dynarmic.h | 10 | ||||
-rw-r--r-- | src/core/arm/unicorn/arm_unicorn.cpp | 27 | ||||
-rw-r--r-- | src/core/arm/unicorn/arm_unicorn.h | 10 | ||||
-rw-r--r-- | src/core/core_cpu.cpp | 3 | ||||
-rw-r--r-- | src/core/core_cpu.h | 5 | ||||
-rw-r--r-- | src/core/gdbstub/gdbstub.cpp | 50 | ||||
-rw-r--r-- | src/core/hle/kernel/mutex.cpp | 4 | ||||
-rw-r--r-- | src/core/hle/kernel/svc.cpp | 8 | ||||
-rw-r--r-- | src/core/hle/kernel/svc_wrap.h | 73 | ||||
-rw-r--r-- | src/core/hle/kernel/thread.cpp | 4 | ||||
-rw-r--r-- | src/core/hle/service/acc/profile_manager.cpp | 5 | ||||
-rw-r--r-- | src/core/hle/service/am/am.cpp | 18 | ||||
-rw-r--r-- | src/core/hle/service/am/am.h | 1 | ||||
-rw-r--r-- | src/core/hle/service/hid/hid.cpp | 32 | ||||
-rw-r--r-- | src/video_core/CMakeLists.txt | 1 | ||||
-rw-r--r-- | src/video_core/engines/shader_bytecode.h | 195 | ||||
-rw-r--r-- | src/video_core/engines/shader_header.h | 103 | ||||
-rw-r--r-- | src/video_core/renderer_opengl/gl_shader_decompiler.cpp | 193 | ||||
-rw-r--r-- | src/yuzu_cmd/config.cpp | 4 | ||||
-rw-r--r-- | src/yuzu_cmd/default_ini.h | 2 |
22 files changed, 634 insertions, 202 deletions
diff --git a/src/core/arm/arm_interface.h b/src/core/arm/arm_interface.h index 0b2af2a9b..867e34932 100644 --- a/src/core/arm/arm_interface.h +++ b/src/core/arm/arm_interface.h @@ -10,7 +10,7 @@ namespace Core { -/// Generic ARM11 CPU interface +/// Generic ARMv8 CPU interface class ARM_Interface : NonCopyable { public: virtual ~ARM_Interface() {} @@ -19,9 +19,9 @@ public: std::array<u64, 31> cpu_registers; u64 sp; u64 pc; - u64 cpsr; - std::array<u128, 32> fpu_registers; - u64 fpscr; + u64 pstate; + std::array<u128, 32> vector_registers; + u64 fpcr; }; /// Runs the CPU until an event happens @@ -69,42 +69,50 @@ public: */ virtual void SetReg(int index, u64 value) = 0; - virtual u128 GetExtReg(int index) const = 0; - - virtual void SetExtReg(int index, u128 value) = 0; - /** - * Gets the value of a VFP register - * @param index Register index (0-31) - * @return Returns the value in the register + * Gets the value of a specified vector register. + * + * @param index The index of the vector register. + * @return the value within the vector register. */ - virtual u32 GetVFPReg(int index) const = 0; + virtual u128 GetVectorReg(int index) const = 0; /** - * Sets a VFP register to the given value - * @param index Register index (0-31) - * @param value Value to set register to + * Sets a given value into a vector register. + * + * @param index The index of the vector register. + * @param value The new value to place in the register. */ - virtual void SetVFPReg(int index, u32 value) = 0; + virtual void SetVectorReg(int index, u128 value) = 0; /** - * Get the current CPSR register - * @return Returns the value of the CPSR register + * Get the current PSTATE register + * @return Returns the value of the PSTATE register */ - virtual u32 GetCPSR() const = 0; + virtual u32 GetPSTATE() const = 0; /** - * Set the current CPSR register - * @param cpsr Value to set CPSR to + * Set the current PSTATE register + * @param pstate Value to set PSTATE to */ - virtual void SetCPSR(u32 cpsr) = 0; + virtual void SetPSTATE(u32 pstate) = 0; virtual VAddr GetTlsAddress() const = 0; virtual void SetTlsAddress(VAddr address) = 0; + /** + * Gets the value within the TPIDR_EL0 (read/write software thread ID) register. + * + * @return the value within the register. + */ virtual u64 GetTPIDR_EL0() const = 0; + /** + * Sets a new value within the TPIDR_EL0 (read/write software thread ID) register. + * + * @param value The new value to place in the register. + */ virtual void SetTPIDR_EL0(u64 value) = 0; /** @@ -119,6 +127,7 @@ public: */ virtual void LoadContext(const ThreadContext& ctx) = 0; + /// Clears the exclusive monitor's state. virtual void ClearExclusiveState() = 0; /// Prepare core for thread reschedule (if needed to correctly handle state) diff --git a/src/core/arm/dynarmic/arm_dynarmic.cpp b/src/core/arm/dynarmic/arm_dynarmic.cpp index 0c175d872..3f072c51f 100644 --- a/src/core/arm/dynarmic/arm_dynarmic.cpp +++ b/src/core/arm/dynarmic/arm_dynarmic.cpp @@ -194,29 +194,20 @@ void ARM_Dynarmic::SetReg(int index, u64 value) { jit->SetRegister(index, value); } -u128 ARM_Dynarmic::GetExtReg(int index) const { +u128 ARM_Dynarmic::GetVectorReg(int index) const { return jit->GetVector(index); } -void ARM_Dynarmic::SetExtReg(int index, u128 value) { +void ARM_Dynarmic::SetVectorReg(int index, u128 value) { jit->SetVector(index, value); } -u32 ARM_Dynarmic::GetVFPReg(int /*index*/) const { - UNIMPLEMENTED(); - return {}; -} - -void ARM_Dynarmic::SetVFPReg(int /*index*/, u32 /*value*/) { - UNIMPLEMENTED(); -} - -u32 ARM_Dynarmic::GetCPSR() const { +u32 ARM_Dynarmic::GetPSTATE() const { return jit->GetPstate(); } -void ARM_Dynarmic::SetCPSR(u32 cpsr) { - jit->SetPstate(cpsr); +void ARM_Dynarmic::SetPSTATE(u32 pstate) { + jit->SetPstate(pstate); } u64 ARM_Dynarmic::GetTlsAddress() const { @@ -239,18 +230,18 @@ void ARM_Dynarmic::SaveContext(ThreadContext& ctx) { ctx.cpu_registers = jit->GetRegisters(); ctx.sp = jit->GetSP(); ctx.pc = jit->GetPC(); - ctx.cpsr = jit->GetPstate(); - ctx.fpu_registers = jit->GetVectors(); - ctx.fpscr = jit->GetFpcr(); + ctx.pstate = jit->GetPstate(); + ctx.vector_registers = jit->GetVectors(); + ctx.fpcr = jit->GetFpcr(); } void ARM_Dynarmic::LoadContext(const ThreadContext& ctx) { jit->SetRegisters(ctx.cpu_registers); jit->SetSP(ctx.sp); jit->SetPC(ctx.pc); - jit->SetPstate(static_cast<u32>(ctx.cpsr)); - jit->SetVectors(ctx.fpu_registers); - jit->SetFpcr(static_cast<u32>(ctx.fpscr)); + jit->SetPstate(static_cast<u32>(ctx.pstate)); + jit->SetVectors(ctx.vector_registers); + jit->SetFpcr(static_cast<u32>(ctx.fpcr)); } void ARM_Dynarmic::PrepareReschedule() { @@ -304,8 +295,8 @@ bool DynarmicExclusiveMonitor::ExclusiveWrite64(std::size_t core_index, VAddr va bool DynarmicExclusiveMonitor::ExclusiveWrite128(std::size_t core_index, VAddr vaddr, u128 value) { return monitor.DoExclusiveOperation(core_index, vaddr, 16, [&] { - Memory::Write64(vaddr, value[0]); - Memory::Write64(vaddr, value[1]); + Memory::Write64(vaddr + 0, value[0]); + Memory::Write64(vaddr + 8, value[1]); }); } diff --git a/src/core/arm/dynarmic/arm_dynarmic.h b/src/core/arm/dynarmic/arm_dynarmic.h index 56c60c853..e61382d3d 100644 --- a/src/core/arm/dynarmic/arm_dynarmic.h +++ b/src/core/arm/dynarmic/arm_dynarmic.h @@ -29,14 +29,12 @@ public: u64 GetPC() const override; u64 GetReg(int index) const override; void SetReg(int index, u64 value) override; - u128 GetExtReg(int index) const override; - void SetExtReg(int index, u128 value) override; - u32 GetVFPReg(int index) const override; - void SetVFPReg(int index, u32 value) override; - u32 GetCPSR() const override; + u128 GetVectorReg(int index) const override; + void SetVectorReg(int index, u128 value) override; + u32 GetPSTATE() const override; + void SetPSTATE(u32 pstate) override; void Run() override; void Step() override; - void SetCPSR(u32 cpsr) override; VAddr GetTlsAddress() const override; void SetTlsAddress(VAddr address) override; void SetTPIDR_EL0(u64 value) override; diff --git a/src/core/arm/unicorn/arm_unicorn.cpp b/src/core/arm/unicorn/arm_unicorn.cpp index 4e02b7cd4..e218a0b15 100644 --- a/src/core/arm/unicorn/arm_unicorn.cpp +++ b/src/core/arm/unicorn/arm_unicorn.cpp @@ -131,33 +131,24 @@ void ARM_Unicorn::SetReg(int regn, u64 val) { CHECKED(uc_reg_write(uc, treg, &val)); } -u128 ARM_Unicorn::GetExtReg(int /*index*/) const { +u128 ARM_Unicorn::GetVectorReg(int /*index*/) const { UNIMPLEMENTED(); static constexpr u128 res{}; return res; } -void ARM_Unicorn::SetExtReg(int /*index*/, u128 /*value*/) { +void ARM_Unicorn::SetVectorReg(int /*index*/, u128 /*value*/) { UNIMPLEMENTED(); } -u32 ARM_Unicorn::GetVFPReg(int /*index*/) const { - UNIMPLEMENTED(); - return {}; -} - -void ARM_Unicorn::SetVFPReg(int /*index*/, u32 /*value*/) { - UNIMPLEMENTED(); -} - -u32 ARM_Unicorn::GetCPSR() const { +u32 ARM_Unicorn::GetPSTATE() const { u64 nzcv{}; CHECKED(uc_reg_read(uc, UC_ARM64_REG_NZCV, &nzcv)); return static_cast<u32>(nzcv); } -void ARM_Unicorn::SetCPSR(u32 cpsr) { - u64 nzcv = cpsr; +void ARM_Unicorn::SetPSTATE(u32 pstate) { + u64 nzcv = pstate; CHECKED(uc_reg_write(uc, UC_ARM64_REG_NZCV, &nzcv)); } @@ -219,7 +210,7 @@ void ARM_Unicorn::SaveContext(ThreadContext& ctx) { CHECKED(uc_reg_read(uc, UC_ARM64_REG_SP, &ctx.sp)); CHECKED(uc_reg_read(uc, UC_ARM64_REG_PC, &ctx.pc)); - CHECKED(uc_reg_read(uc, UC_ARM64_REG_NZCV, &ctx.cpsr)); + CHECKED(uc_reg_read(uc, UC_ARM64_REG_NZCV, &ctx.pstate)); for (auto i = 0; i < 29; ++i) { uregs[i] = UC_ARM64_REG_X0 + i; @@ -234,7 +225,7 @@ void ARM_Unicorn::SaveContext(ThreadContext& ctx) { for (int i = 0; i < 32; ++i) { uregs[i] = UC_ARM64_REG_Q0 + i; - tregs[i] = &ctx.fpu_registers[i]; + tregs[i] = &ctx.vector_registers[i]; } CHECKED(uc_reg_read_batch(uc, uregs, tregs, 32)); @@ -246,7 +237,7 @@ void ARM_Unicorn::LoadContext(const ThreadContext& ctx) { CHECKED(uc_reg_write(uc, UC_ARM64_REG_SP, &ctx.sp)); CHECKED(uc_reg_write(uc, UC_ARM64_REG_PC, &ctx.pc)); - CHECKED(uc_reg_write(uc, UC_ARM64_REG_NZCV, &ctx.cpsr)); + CHECKED(uc_reg_write(uc, UC_ARM64_REG_NZCV, &ctx.pstate)); for (int i = 0; i < 29; ++i) { uregs[i] = UC_ARM64_REG_X0 + i; @@ -261,7 +252,7 @@ void ARM_Unicorn::LoadContext(const ThreadContext& ctx) { for (auto i = 0; i < 32; ++i) { uregs[i] = UC_ARM64_REG_Q0 + i; - tregs[i] = (void*)&ctx.fpu_registers[i]; + tregs[i] = (void*)&ctx.vector_registers[i]; } CHECKED(uc_reg_write_batch(uc, uregs, tregs, 32)); diff --git a/src/core/arm/unicorn/arm_unicorn.h b/src/core/arm/unicorn/arm_unicorn.h index d6f7cf4ab..75761950b 100644 --- a/src/core/arm/unicorn/arm_unicorn.h +++ b/src/core/arm/unicorn/arm_unicorn.h @@ -22,12 +22,10 @@ public: u64 GetPC() const override; u64 GetReg(int index) const override; void SetReg(int index, u64 value) override; - u128 GetExtReg(int index) const override; - void SetExtReg(int index, u128 value) override; - u32 GetVFPReg(int index) const override; - void SetVFPReg(int index, u32 value) override; - u32 GetCPSR() const override; - void SetCPSR(u32 cpsr) override; + u128 GetVectorReg(int index) const override; + void SetVectorReg(int index, u128 value) override; + u32 GetPSTATE() const override; + void SetPSTATE(u32 pstate) override; VAddr GetTlsAddress() const override; void SetTlsAddress(VAddr address) override; void SetTPIDR_EL0(u64 value) override; diff --git a/src/core/core_cpu.cpp b/src/core/core_cpu.cpp index 15d60cc8a..21568ad50 100644 --- a/src/core/core_cpu.cpp +++ b/src/core/core_cpu.cpp @@ -9,6 +9,7 @@ #ifdef ARCHITECTURE_x86_64 #include "core/arm/dynarmic/arm_dynarmic.h" #endif +#include "core/arm/exclusive_monitor.h" #include "core/arm/unicorn/arm_unicorn.h" #include "core/core_cpu.h" #include "core/core_timing.h" @@ -66,6 +67,8 @@ Cpu::Cpu(std::shared_ptr<ExclusiveMonitor> exclusive_monitor, scheduler = std::make_shared<Kernel::Scheduler>(arm_interface.get()); } +Cpu::~Cpu() = default; + std::shared_ptr<ExclusiveMonitor> Cpu::MakeExclusiveMonitor(std::size_t num_cores) { if (Settings::values.use_cpu_jit) { #ifdef ARCHITECTURE_x86_64 diff --git a/src/core/core_cpu.h b/src/core/core_cpu.h index 1d229b42f..685532965 100644 --- a/src/core/core_cpu.h +++ b/src/core/core_cpu.h @@ -6,11 +6,10 @@ #include <atomic> #include <condition_variable> +#include <cstddef> #include <memory> #include <mutex> -#include <string> #include "common/common_types.h" -#include "core/arm/exclusive_monitor.h" namespace Kernel { class Scheduler; @@ -19,6 +18,7 @@ class Scheduler; namespace Core { class ARM_Interface; +class ExclusiveMonitor; constexpr unsigned NUM_CPU_CORES{4}; @@ -43,6 +43,7 @@ class Cpu { public: Cpu(std::shared_ptr<ExclusiveMonitor> exclusive_monitor, std::shared_ptr<CpuBarrier> cpu_barrier, std::size_t core_index); + ~Cpu(); void RunLoop(bool tight_loop = true); diff --git a/src/core/gdbstub/gdbstub.cpp b/src/core/gdbstub/gdbstub.cpp index cfaf20a88..1b04f68bf 100644 --- a/src/core/gdbstub/gdbstub.cpp +++ b/src/core/gdbstub/gdbstub.cpp @@ -65,9 +65,9 @@ constexpr u32 MSG_WAITALL = 8; constexpr u32 LR_REGISTER = 30; constexpr u32 SP_REGISTER = 31; constexpr u32 PC_REGISTER = 32; -constexpr u32 CPSR_REGISTER = 33; +constexpr u32 PSTATE_REGISTER = 33; constexpr u32 UC_ARM64_REG_Q0 = 34; -constexpr u32 FPSCR_REGISTER = 66; +constexpr u32 FPCR_REGISTER = 66; // TODO/WiP - Used while working on support for FPU constexpr u32 TODO_DUMMY_REG_997 = 997; @@ -116,7 +116,7 @@ constexpr char target_xml[] = <reg name="pc" bitsize="64" type="code_ptr"/> - <flags id="cpsr_flags" size="4"> + <flags id="pstate_flags" size="4"> <field name="SP" start="0" end="0"/> <field name="" start="1" end="1"/> <field name="EL" start="2" end="3"/> @@ -135,7 +135,7 @@ constexpr char target_xml[] = <field name="Z" start="30" end="30"/> <field name="N" start="31" end="31"/> </flags> - <reg name="cpsr" bitsize="32" type="cpsr_flags"/> + <reg name="pstate" bitsize="32" type="pstate_flags"/> </feature> <feature name="org.gnu.gdb.aarch64.fpu"> </feature> @@ -227,10 +227,10 @@ static u64 RegRead(std::size_t id, Kernel::Thread* thread = nullptr) { return thread->context.sp; } else if (id == PC_REGISTER) { return thread->context.pc; - } else if (id == CPSR_REGISTER) { - return thread->context.cpsr; - } else if (id > CPSR_REGISTER && id < FPSCR_REGISTER) { - return thread->context.fpu_registers[id - UC_ARM64_REG_Q0][0]; + } else if (id == PSTATE_REGISTER) { + return thread->context.pstate; + } else if (id > PSTATE_REGISTER && id < FPCR_REGISTER) { + return thread->context.vector_registers[id - UC_ARM64_REG_Q0][0]; } else { return 0; } @@ -247,10 +247,10 @@ static void RegWrite(std::size_t id, u64 val, Kernel::Thread* thread = nullptr) thread->context.sp = val; } else if (id == PC_REGISTER) { thread->context.pc = val; - } else if (id == CPSR_REGISTER) { - thread->context.cpsr = val; - } else if (id > CPSR_REGISTER && id < FPSCR_REGISTER) { - thread->context.fpu_registers[id - (CPSR_REGISTER + 1)][0] = val; + } else if (id == PSTATE_REGISTER) { + thread->context.pstate = val; + } else if (id > PSTATE_REGISTER && id < FPCR_REGISTER) { + thread->context.vector_registers[id - (PSTATE_REGISTER + 1)][0] = val; } } @@ -781,11 +781,11 @@ static void ReadRegister() { LongToGdbHex(reply, RegRead(id, current_thread)); } else if (id == PC_REGISTER) { LongToGdbHex(reply, RegRead(id, current_thread)); - } else if (id == CPSR_REGISTER) { - IntToGdbHex(reply, (u32)RegRead(id, current_thread)); - } else if (id >= UC_ARM64_REG_Q0 && id < FPSCR_REGISTER) { + } else if (id == PSTATE_REGISTER) { + IntToGdbHex(reply, static_cast<u32>(RegRead(id, current_thread))); + } else if (id >= UC_ARM64_REG_Q0 && id < FPCR_REGISTER) { LongToGdbHex(reply, RegRead(id, current_thread)); - } else if (id == FPSCR_REGISTER) { + } else if (id == FPCR_REGISTER) { LongToGdbHex(reply, RegRead(TODO_DUMMY_REG_998, current_thread)); } else { LongToGdbHex(reply, RegRead(TODO_DUMMY_REG_997, current_thread)); @@ -811,7 +811,7 @@ static void ReadRegisters() { bufptr += 16; - IntToGdbHex(bufptr, (u32)RegRead(CPSR_REGISTER, current_thread)); + IntToGdbHex(bufptr, static_cast<u32>(RegRead(PSTATE_REGISTER, current_thread))); bufptr += 8; @@ -843,11 +843,11 @@ static void WriteRegister() { RegWrite(id, GdbHexToLong(buffer_ptr), current_thread); } else if (id == PC_REGISTER) { RegWrite(id, GdbHexToLong(buffer_ptr), current_thread); - } else if (id == CPSR_REGISTER) { + } else if (id == PSTATE_REGISTER) { RegWrite(id, GdbHexToInt(buffer_ptr), current_thread); - } else if (id >= UC_ARM64_REG_Q0 && id < FPSCR_REGISTER) { + } else if (id >= UC_ARM64_REG_Q0 && id < FPCR_REGISTER) { RegWrite(id, GdbHexToLong(buffer_ptr), current_thread); - } else if (id == FPSCR_REGISTER) { + } else if (id == FPCR_REGISTER) { RegWrite(TODO_DUMMY_REG_998, GdbHexToLong(buffer_ptr), current_thread); } else { RegWrite(TODO_DUMMY_REG_997, GdbHexToLong(buffer_ptr), current_thread); @@ -866,16 +866,16 @@ static void WriteRegisters() { if (command_buffer[0] != 'G') return SendReply("E01"); - for (u32 i = 0, reg = 0; reg <= FPSCR_REGISTER; i++, reg++) { + for (u32 i = 0, reg = 0; reg <= FPCR_REGISTER; i++, reg++) { if (reg <= SP_REGISTER) { RegWrite(reg, GdbHexToLong(buffer_ptr + i * 16), current_thread); } else if (reg == PC_REGISTER) { RegWrite(PC_REGISTER, GdbHexToLong(buffer_ptr + i * 16), current_thread); - } else if (reg == CPSR_REGISTER) { - RegWrite(CPSR_REGISTER, GdbHexToInt(buffer_ptr + i * 16), current_thread); - } else if (reg >= UC_ARM64_REG_Q0 && reg < FPSCR_REGISTER) { + } else if (reg == PSTATE_REGISTER) { + RegWrite(PSTATE_REGISTER, GdbHexToInt(buffer_ptr + i * 16), current_thread); + } else if (reg >= UC_ARM64_REG_Q0 && reg < FPCR_REGISTER) { RegWrite(reg, GdbHexToLong(buffer_ptr + i * 16), current_thread); - } else if (reg == FPSCR_REGISTER) { + } else if (reg == FPCR_REGISTER) { RegWrite(TODO_DUMMY_REG_998, GdbHexToLong(buffer_ptr + i * 16), current_thread); } else { UNIMPLEMENTED(); diff --git a/src/core/hle/kernel/mutex.cpp b/src/core/hle/kernel/mutex.cpp index 36bf0b677..51f4544be 100644 --- a/src/core/hle/kernel/mutex.cpp +++ b/src/core/hle/kernel/mutex.cpp @@ -62,7 +62,7 @@ ResultCode Mutex::TryAcquire(HandleTable& handle_table, VAddr address, Handle ho Handle requesting_thread_handle) { // The mutex address must be 4-byte aligned if ((address % sizeof(u32)) != 0) { - return ResultCode(ErrorModule::Kernel, ErrCodes::InvalidAddress); + return ERR_INVALID_ADDRESS; } SharedPtr<Thread> holding_thread = handle_table.Get<Thread>(holding_thread_handle); @@ -100,7 +100,7 @@ ResultCode Mutex::TryAcquire(HandleTable& handle_table, VAddr address, Handle ho ResultCode Mutex::Release(VAddr address) { // The mutex address must be 4-byte aligned if ((address % sizeof(u32)) != 0) { - return ResultCode(ErrorModule::Kernel, ErrCodes::InvalidAddress); + return ERR_INVALID_ADDRESS; } auto [thread, num_waiters] = GetHighestPriorityMutexWaitingThread(GetCurrentThread(), address); diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp index c5c1697ee..371fc439e 100644 --- a/src/core/hle/kernel/svc.cpp +++ b/src/core/hle/kernel/svc.cpp @@ -280,6 +280,10 @@ static ResultCode ArbitrateLock(Handle holding_thread_handle, VAddr mutex_addr, "requesting_current_thread_handle=0x{:08X}", holding_thread_handle, mutex_addr, requesting_thread_handle); + if (Memory::IsKernelVirtualAddress(mutex_addr)) { + return ERR_INVALID_ADDRESS_STATE; + } + auto& handle_table = Core::System::GetInstance().Kernel().HandleTable(); return Mutex::TryAcquire(handle_table, mutex_addr, holding_thread_handle, requesting_thread_handle); @@ -289,6 +293,10 @@ static ResultCode ArbitrateLock(Handle holding_thread_handle, VAddr mutex_addr, static ResultCode ArbitrateUnlock(VAddr mutex_addr) { LOG_TRACE(Kernel_SVC, "called mutex_addr=0x{:X}", mutex_addr); + if (Memory::IsKernelVirtualAddress(mutex_addr)) { + return ERR_INVALID_ADDRESS_STATE; + } + return Mutex::Release(mutex_addr); } diff --git a/src/core/hle/kernel/svc_wrap.h b/src/core/hle/kernel/svc_wrap.h index 1eda5f879..fea9ba5ea 100644 --- a/src/core/hle/kernel/svc_wrap.h +++ b/src/core/hle/kernel/svc_wrap.h @@ -13,7 +13,9 @@ namespace Kernel { -#define PARAM(n) Core::CurrentArmInterface().GetReg(n) +static inline u64 Param(int n) { + return Core::CurrentArmInterface().GetReg(n); +} /** * HLE a function return from the current ARM userland process @@ -28,23 +30,23 @@ static inline void FuncReturn(u64 res) { template <ResultCode func(u64)> void SvcWrap() { - FuncReturn(func(PARAM(0)).raw); + FuncReturn(func(Param(0)).raw); } template <ResultCode func(u32)> void SvcWrap() { - FuncReturn(func((u32)PARAM(0)).raw); + FuncReturn(func((u32)Param(0)).raw); } template <ResultCode func(u32, u32)> void SvcWrap() { - FuncReturn(func((u32)PARAM(0), (u32)PARAM(1)).raw); + FuncReturn(func((u32)Param(0), (u32)Param(1)).raw); } template <ResultCode func(u32*, u32)> void SvcWrap() { u32 param_1 = 0; - u32 retval = func(¶m_1, (u32)PARAM(1)).raw; + u32 retval = func(¶m_1, (u32)Param(1)).raw; Core::CurrentArmInterface().SetReg(1, param_1); FuncReturn(retval); } @@ -52,39 +54,39 @@ void SvcWrap() { template <ResultCode func(u32*, u64)> void SvcWrap() { u32 param_1 = 0; - u32 retval = func(¶m_1, PARAM(1)).raw; + u32 retval = func(¶m_1, Param(1)).raw; Core::CurrentArmInterface().SetReg(1, param_1); FuncReturn(retval); } template <ResultCode func(u64, s32)> void SvcWrap() { - FuncReturn(func(PARAM(0), (s32)PARAM(1)).raw); + FuncReturn(func(Param(0), (s32)Param(1)).raw); } template <ResultCode func(u64*, u64)> void SvcWrap() { u64 param_1 = 0; - u32 retval = func(¶m_1, PARAM(1)).raw; + u32 retval = func(¶m_1, Param(1)).raw; Core::CurrentArmInterface().SetReg(1, param_1); FuncReturn(retval); } template <ResultCode func(u32, u64)> void SvcWrap() { - FuncReturn(func((u32)(PARAM(0) & 0xFFFFFFFF), PARAM(1)).raw); + FuncReturn(func((u32)(Param(0) & 0xFFFFFFFF), Param(1)).raw); } template <ResultCode func(u32, u32, u64)> void SvcWrap() { - FuncReturn(func((u32)(PARAM(0) & 0xFFFFFFFF), (u32)(PARAM(1) & 0xFFFFFFFF), PARAM(2)).raw); + FuncReturn(func((u32)(Param(0) & 0xFFFFFFFF), (u32)(Param(1) & 0xFFFFFFFF), Param(2)).raw); } template <ResultCode func(u32, u32*, u64*)> void SvcWrap() { u32 param_1 = 0; u64 param_2 = 0; - ResultCode retval = func((u32)(PARAM(2) & 0xFFFFFFFF), ¶m_1, ¶m_2); + ResultCode retval = func((u32)(Param(2) & 0xFFFFFFFF), ¶m_1, ¶m_2); Core::CurrentArmInterface().SetReg(1, param_1); Core::CurrentArmInterface().SetReg(2, param_2); FuncReturn(retval.raw); @@ -93,46 +95,46 @@ void SvcWrap() { template <ResultCode func(u64, u64, u32, u32)> void SvcWrap() { FuncReturn( - func(PARAM(0), PARAM(1), (u32)(PARAM(3) & 0xFFFFFFFF), (u32)(PARAM(3) & 0xFFFFFFFF)).raw); + func(Param(0), Param(1), (u32)(Param(3) & 0xFFFFFFFF), (u32)(Param(3) & 0xFFFFFFFF)).raw); } template <ResultCode func(u32, u64, u32)> void SvcWrap() { - FuncReturn(func((u32)PARAM(0), PARAM(1), (u32)PARAM(2)).raw); + FuncReturn(func((u32)Param(0), Param(1), (u32)Param(2)).raw); } template <ResultCode func(u64, u64, u64)> void SvcWrap() { - FuncReturn(func(PARAM(0), PARAM(1), PARAM(2)).raw); + FuncReturn(func(Param(0), Param(1), Param(2)).raw); } template <ResultCode func(u32, u64, u64, u32)> void SvcWrap() { - FuncReturn(func((u32)PARAM(0), PARAM(1), PARAM(2), (u32)PARAM(3)).raw); + FuncReturn(func((u32)Param(0), Param(1), Param(2), (u32)Param(3)).raw); } template <ResultCode func(u32, u64, u64)> void SvcWrap() { - FuncReturn(func((u32)PARAM(0), PARAM(1), PARAM(2)).raw); + FuncReturn(func((u32)Param(0), Param(1), Param(2)).raw); } template <ResultCode func(u32*, u64, u64, s64)> void SvcWrap() { u32 param_1 = 0; - ResultCode retval = func(¶m_1, PARAM(1), (u32)(PARAM(2) & 0xFFFFFFFF), (s64)PARAM(3)); + ResultCode retval = func(¶m_1, Param(1), (u32)(Param(2) & 0xFFFFFFFF), (s64)Param(3)); Core::CurrentArmInterface().SetReg(1, param_1); FuncReturn(retval.raw); } template <ResultCode func(u64, u64, u32, s64)> void SvcWrap() { - FuncReturn(func(PARAM(0), PARAM(1), (u32)PARAM(2), (s64)PARAM(3)).raw); + FuncReturn(func(Param(0), Param(1), (u32)Param(2), (s64)Param(3)).raw); } template <ResultCode func(u64*, u64, u64, u64)> void SvcWrap() { u64 param_1 = 0; - u32 retval = func(¶m_1, PARAM(1), PARAM(2), PARAM(3)).raw; + u32 retval = func(¶m_1, Param(1), Param(2), Param(3)).raw; Core::CurrentArmInterface().SetReg(1, param_1); FuncReturn(retval); } @@ -141,7 +143,7 @@ template <ResultCode func(u32*, u64, u64, u64, u32, s32)> void SvcWrap() { u32 param_1 = 0; u32 retval = - func(¶m_1, PARAM(1), PARAM(2), PARAM(3), (u32)PARAM(4), (s32)(PARAM(5) & 0xFFFFFFFF)) + func(¶m_1, Param(1), Param(2), Param(3), (u32)Param(4), (s32)(Param(5) & 0xFFFFFFFF)) .raw; Core::CurrentArmInterface().SetReg(1, param_1); FuncReturn(retval); @@ -151,13 +153,13 @@ template <ResultCode func(MemoryInfo*, PageInfo*, u64)> void SvcWrap() { MemoryInfo memory_info = {}; PageInfo page_info = {}; - u32 retval = func(&memory_info, &page_info, PARAM(2)).raw; + u32 retval = func(&memory_info, &page_info, Param(2)).raw; - Memory::Write64(PARAM(0), memory_info.base_address); - Memory::Write64(PARAM(0) + 8, memory_info.size); - Memory::Write32(PARAM(0) + 16, memory_info.type); - Memory::Write32(PARAM(0) + 20, memory_info.attributes); - Memory::Write32(PARAM(0) + 24, memory_info.permission); + Memory::Write64(Param(0), memory_info.base_address); + Memory::Write64(Param(0) + 8, memory_info.size); + Memory::Write32(Param(0) + 16, memory_info.type); + Memory::Write32(Param(0) + 20, memory_info.attributes); + Memory::Write32(Param(0) + 24, memory_info.permission); FuncReturn(retval); } @@ -165,7 +167,7 @@ void SvcWrap() { template <ResultCode func(u32*, u64, u64, u32)> void SvcWrap() { u32 param_1 = 0; - u32 retval = func(¶m_1, PARAM(1), PARAM(2), (u32)(PARAM(3) & 0xFFFFFFFF)).raw; + u32 retval = func(¶m_1, Param(1), Param(2), (u32)(Param(3) & 0xFFFFFFFF)).raw; Core::CurrentArmInterface().SetReg(1, param_1); FuncReturn(retval); } @@ -174,7 +176,7 @@ template <ResultCode func(Handle*, u64, u32, u32)> void SvcWrap() { u32 param_1 = 0; u32 retval = - func(¶m_1, PARAM(1), (u32)(PARAM(2) & 0xFFFFFFFF), (u32)(PARAM(3) & 0xFFFFFFFF)).raw; + func(¶m_1, Param(1), (u32)(Param(2) & 0xFFFFFFFF), (u32)(Param(3) & 0xFFFFFFFF)).raw; Core::CurrentArmInterface().SetReg(1, param_1); FuncReturn(retval); } @@ -182,14 +184,14 @@ void SvcWrap() { template <ResultCode func(u64, u32, s32, s64)> void SvcWrap() { FuncReturn( - func(PARAM(0), (u32)(PARAM(1) & 0xFFFFFFFF), (s32)(PARAM(2) & 0xFFFFFFFF), (s64)PARAM(3)) + func(Param(0), (u32)(Param(1) & 0xFFFFFFFF), (s32)(Param(2) & 0xFFFFFFFF), (s64)Param(3)) .raw); } template <ResultCode func(u64, u32, s32, s32)> void SvcWrap() { - FuncReturn(func(PARAM(0), (u32)(PARAM(1) & 0xFFFFFFFF), (s32)(PARAM(2) & 0xFFFFFFFF), - (s32)(PARAM(3) & 0xFFFFFFFF)) + FuncReturn(func(Param(0), (u32)(Param(1) & 0xFFFFFFFF), (s32)(Param(2) & 0xFFFFFFFF), + (s32)(Param(3) & 0xFFFFFFFF)) .raw); } @@ -219,20 +221,17 @@ void SvcWrap() { template <void func(s64)> void SvcWrap() { - func((s64)PARAM(0)); + func((s64)Param(0)); } template <void func(u64, u64 len)> void SvcWrap() { - func(PARAM(0), PARAM(1)); + func(Param(0), Param(1)); } template <void func(u64, u64, u64)> void SvcWrap() { - func(PARAM(0), PARAM(1), PARAM(2)); + func(Param(0), Param(1), Param(2)); } -#undef PARAM -#undef FuncReturn - } // namespace Kernel diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp index 89cd5f401..d4183d6e3 100644 --- a/src/core/hle/kernel/thread.cpp +++ b/src/core/hle/kernel/thread.cpp @@ -217,8 +217,8 @@ static void ResetThreadContext(Core::ARM_Interface::ThreadContext& context, VAdd context.cpu_registers[0] = arg; context.pc = entry_point; context.sp = stack_top; - context.cpsr = 0; - context.fpscr = 0; + context.pstate = 0; + context.fpcr = 0; } ResultVal<SharedPtr<Thread>> Thread::Create(KernelCore& kernel, std::string name, VAddr entry_point, diff --git a/src/core/hle/service/acc/profile_manager.cpp b/src/core/hle/service/acc/profile_manager.cpp index 0071ca613..bcb3475db 100644 --- a/src/core/hle/service/acc/profile_manager.cpp +++ b/src/core/hle/service/acc/profile_manager.cpp @@ -25,7 +25,7 @@ const UUID& UUID::Generate() { ProfileManager::ProfileManager() { // TODO(ogniK): Create the default user we have for now until loading/saving users is added auto user_uuid = UUID{1, 0}; - CreateNewUser(user_uuid, Settings::values.username); + ASSERT(CreateNewUser(user_uuid, Settings::values.username).IsSuccess()); OpenUser(user_uuid); } @@ -91,7 +91,8 @@ ResultCode ProfileManager::CreateNewUser(UUID uuid, const ProfileUsername& usern /// specifically by allowing an std::string for the username. This is required specifically since /// we're loading a string straight from the config ResultCode ProfileManager::CreateNewUser(UUID uuid, const std::string& username) { - ProfileUsername username_output; + ProfileUsername username_output{}; + if (username.size() > username_output.size()) { std::copy_n(username.begin(), username_output.size(), username_output.begin()); } else { diff --git a/src/core/hle/service/am/am.cpp b/src/core/hle/service/am/am.cpp index d1f7007ec..9c975325a 100644 --- a/src/core/hle/service/am/am.cpp +++ b/src/core/hle/service/am/am.cpp @@ -20,6 +20,7 @@ #include "core/hle/service/nvflinger/nvflinger.h" #include "core/hle/service/pm/pm.h" #include "core/hle/service/set/set.h" +#include "core/hle/service/vi/vi.h" #include "core/settings.h" namespace Service::AM { @@ -334,7 +335,7 @@ ICommonStateGetter::ICommonStateGetter() : ServiceFramework("ICommonStateGetter" {51, nullptr, "SetVrModeEnabled"}, {52, nullptr, "SwitchLcdBacklight"}, {55, nullptr, "IsInControllerFirmwareUpdateSection"}, - {60, nullptr, "GetDefaultDisplayResolution"}, + {60, &ICommonStateGetter::GetDefaultDisplayResolution, "GetDefaultDisplayResolution"}, {61, &ICommonStateGetter::GetDefaultDisplayResolutionChangeEvent, "GetDefaultDisplayResolutionChangeEvent"}, {62, nullptr, "GetHdcpAuthenticationState"}, @@ -393,6 +394,21 @@ void ICommonStateGetter::GetDefaultDisplayResolutionChangeEvent(Kernel::HLEReque LOG_WARNING(Service_AM, "(STUBBED) called"); } +void ICommonStateGetter::GetDefaultDisplayResolution(Kernel::HLERequestContext& ctx) { + IPC::ResponseBuilder rb{ctx, 4}; + rb.Push(RESULT_SUCCESS); + + if (Settings::values.use_docked_mode) { + rb.Push(static_cast<u32>(Service::VI::DisplayResolution::DockedWidth)); + rb.Push(static_cast<u32>(Service::VI::DisplayResolution::DockedHeight)); + } else { + rb.Push(static_cast<u32>(Service::VI::DisplayResolution::UndockedWidth)); + rb.Push(static_cast<u32>(Service::VI::DisplayResolution::UndockedHeight)); + } + + LOG_DEBUG(Service_AM, "called"); +} + void ICommonStateGetter::GetOperationMode(Kernel::HLERequestContext& ctx) { const bool use_docked_mode{Settings::values.use_docked_mode}; IPC::ResponseBuilder rb{ctx, 3}; diff --git a/src/core/hle/service/am/am.h b/src/core/hle/service/am/am.h index fd9ae296b..b39b0d838 100644 --- a/src/core/hle/service/am/am.h +++ b/src/core/hle/service/am/am.h @@ -123,6 +123,7 @@ private: void GetOperationMode(Kernel::HLERequestContext& ctx); void GetPerformanceMode(Kernel::HLERequestContext& ctx); void GetBootMode(Kernel::HLERequestContext& ctx); + void GetDefaultDisplayResolution(Kernel::HLERequestContext& ctx); Kernel::SharedPtr<Kernel::Event> event; }; diff --git a/src/core/hle/service/hid/hid.cpp b/src/core/hle/service/hid/hid.cpp index ab2f17db9..256c49bfc 100644 --- a/src/core/hle/service/hid/hid.cpp +++ b/src/core/hle/service/hid/hid.cpp @@ -313,7 +313,7 @@ public: {64, nullptr, "DeactivateJoySixAxisSensor"}, {65, nullptr, "GetJoySixAxisSensorLifoHandle"}, {66, &Hid::StartSixAxisSensor, "StartSixAxisSensor"}, - {67, nullptr, "StopSixAxisSensor"}, + {67, &Hid::StopSixAxisSensor, "StopSixAxisSensor"}, {68, nullptr, "IsSixAxisSensorFusionEnabled"}, {69, nullptr, "EnableSixAxisSensorFusion"}, {70, nullptr, "SetSixAxisSensorFusionParameters"}, @@ -329,7 +329,7 @@ public: {80, nullptr, "GetGyroscopeZeroDriftMode"}, {81, nullptr, "ResetGyroscopeZeroDriftMode"}, {82, &Hid::IsSixAxisSensorAtRest, "IsSixAxisSensorAtRest"}, - {91, nullptr, "ActivateGesture"}, + {91, &Hid::ActivateGesture, "ActivateGesture"}, {100, &Hid::SetSupportedNpadStyleSet, "SetSupportedNpadStyleSet"}, {101, &Hid::GetSupportedNpadStyleSet, "GetSupportedNpadStyleSet"}, {102, &Hid::SetSupportedNpadIdType, "SetSupportedNpadIdType"}, @@ -364,8 +364,8 @@ public: {208, nullptr, "GetActualVibrationGcErmCommand"}, {209, nullptr, "BeginPermitVibrationSession"}, {210, nullptr, "EndPermitVibrationSession"}, - {300, nullptr, "ActivateConsoleSixAxisSensor"}, - {301, nullptr, "StartConsoleSixAxisSensor"}, + {300, &Hid::ActivateConsoleSixAxisSensor, "ActivateConsoleSixAxisSensor"}, + {301, &Hid::StartConsoleSixAxisSensor, "StartConsoleSixAxisSensor"}, {302, nullptr, "StopConsoleSixAxisSensor"}, {303, nullptr, "ActivateSevenSixAxisSensor"}, {304, nullptr, "StartSevenSixAxisSensor"}, @@ -579,6 +579,30 @@ private: rb.Push(RESULT_SUCCESS); LOG_WARNING(Service_HID, "(STUBBED) called"); } + + void ActivateConsoleSixAxisSensor(Kernel::HLERequestContext& ctx) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); + LOG_WARNING(Service_HID, "(STUBBED) called"); + } + + void StartConsoleSixAxisSensor(Kernel::HLERequestContext& ctx) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); + LOG_WARNING(Service_HID, "(STUBBED) called"); + } + + void StopSixAxisSensor(Kernel::HLERequestContext& ctx) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); + LOG_WARNING(Service_HID, "(STUBBED) called"); + } + + void ActivateGesture(Kernel::HLERequestContext& ctx) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); + LOG_WARNING(Service_HID, "(STUBBED) called"); + } }; class HidDbg final : public ServiceFramework<HidDbg> { diff --git a/src/video_core/CMakeLists.txt b/src/video_core/CMakeLists.txt index 4a79ce39c..f5ae57039 100644 --- a/src/video_core/CMakeLists.txt +++ b/src/video_core/CMakeLists.txt @@ -14,6 +14,7 @@ add_library(video_core STATIC engines/maxwell_dma.cpp engines/maxwell_dma.h engines/shader_bytecode.h + engines/shader_header.h gpu.cpp gpu.h macro_interpreter.cpp diff --git a/src/video_core/engines/shader_bytecode.h b/src/video_core/engines/shader_bytecode.h index 6e555ea03..7e1de0fa1 100644 --- a/src/video_core/engines/shader_bytecode.h +++ b/src/video_core/engines/shader_bytecode.h @@ -240,6 +240,41 @@ enum class FlowCondition : u64 { Fcsm_Tr = 0x1C, // TODO(bunnei): What is this used for? }; +enum class ControlCode : u64 { + F = 0, + LT = 1, + EQ = 2, + LE = 3, + GT = 4, + NE = 5, + GE = 6, + Num = 7, + Nan = 8, + LTU = 9, + EQU = 10, + LEU = 11, + GTU = 12, + NEU = 13, + GEU = 14, + // + OFF = 16, + LO = 17, + SFF = 18, + LS = 19, + HI = 20, + SFT = 21, + HS = 22, + OFT = 23, + CSM_TA = 24, + CSM_TR = 25, + CSM_MX = 26, + FCSM_TA = 27, + FCSM_TR = 28, + FCSM_MX = 29, + RLE = 30, + RGT = 31, +}; + enum class PredicateResultMode : u64 { None = 0x0, NotZero = 0x3, @@ -271,6 +306,15 @@ enum class TextureProcessMode : u64 { LLA = 7 // Load LOD. The A is unknown, does not appear to differ with LL }; +enum class TextureMiscMode : u64 { + DC, + AOFFI, // Uses Offset + NDV, + NODEP, + MZ, + PTP, +}; + enum class IpaInterpMode : u64 { Linear = 0, Perspective = 1, Flat = 2, Sc = 3 }; enum class IpaSampleMode : u64 { Default = 0, Centroid = 1, Offset = 2 }; @@ -546,6 +590,15 @@ union Instruction { } pset; union { + BitField<0, 3, u64> pred0; + BitField<3, 3, u64> pred3; + BitField<8, 5, ControlCode> cc; // flag in cc + BitField<39, 3, u64> pred39; + BitField<42, 1, u64> neg_pred39; + BitField<45, 4, PredOperation> op; // op with pred39 + } csetp; + + union { BitField<39, 3, u64> pred39; BitField<42, 1, u64> neg_pred; BitField<43, 1, u64> neg_a; @@ -590,42 +643,127 @@ union Instruction { BitField<28, 1, u64> array; BitField<29, 2, TextureType> texture_type; BitField<31, 4, u64> component_mask; + BitField<49, 1, u64> nodep_flag; + BitField<50, 1, u64> dc_flag; + BitField<54, 1, u64> aoffi_flag; BitField<55, 3, TextureProcessMode> process_mode; bool IsComponentEnabled(std::size_t component) const { return ((1ull << component) & component_mask) != 0; } + + TextureProcessMode GetTextureProcessMode() const { + return process_mode; + } + + bool UsesMiscMode(TextureMiscMode mode) const { + switch (mode) { + case TextureMiscMode::DC: + return dc_flag != 0; + case TextureMiscMode::NODEP: + return nodep_flag != 0; + case TextureMiscMode::AOFFI: + return aoffi_flag != 0; + default: + break; + } + return false; + } } tex; union { BitField<22, 6, TextureQueryType> query_type; BitField<31, 4, u64> component_mask; + BitField<49, 1, u64> nodep_flag; + + bool UsesMiscMode(TextureMiscMode mode) const { + switch (mode) { + case TextureMiscMode::NODEP: + return nodep_flag != 0; + default: + break; + } + return false; + } } txq; union { BitField<28, 1, u64> array; BitField<29, 2, TextureType> texture_type; BitField<31, 4, u64> component_mask; + BitField<35, 1, u64> ndv_flag; + BitField<49, 1, u64> nodep_flag; bool IsComponentEnabled(std::size_t component) const { return ((1ull << component) & component_mask) != 0; } + + bool UsesMiscMode(TextureMiscMode mode) const { + switch (mode) { + case TextureMiscMode::NDV: + return (ndv_flag != 0); + case TextureMiscMode::NODEP: + return (nodep_flag != 0); + default: + break; + } + return false; + } } tmml; union { BitField<28, 1, u64> array; BitField<29, 2, TextureType> texture_type; + BitField<35, 1, u64> ndv_flag; + BitField<49, 1, u64> nodep_flag; + BitField<50, 1, u64> dc_flag; + BitField<54, 2, u64> info; BitField<56, 2, u64> component; + + bool UsesMiscMode(TextureMiscMode mode) const { + switch (mode) { + case TextureMiscMode::NDV: + return ndv_flag != 0; + case TextureMiscMode::NODEP: + return nodep_flag != 0; + case TextureMiscMode::DC: + return dc_flag != 0; + case TextureMiscMode::AOFFI: + return info == 1; + case TextureMiscMode::PTP: + return info == 2; + default: + break; + } + return false; + } } tld4; union { + BitField<49, 1, u64> nodep_flag; + BitField<50, 1, u64> dc_flag; + BitField<51, 1, u64> aoffi_flag; BitField<52, 2, u64> component; + + bool UsesMiscMode(TextureMiscMode mode) const { + switch (mode) { + case TextureMiscMode::DC: + return dc_flag != 0; + case TextureMiscMode::NODEP: + return nodep_flag != 0; + case TextureMiscMode::AOFFI: + return aoffi_flag != 0; + default: + break; + } + return false; + } } tld4s; union { BitField<0, 8, Register> gpr0; BitField<28, 8, Register> gpr28; - BitField<49, 1, u64> nodep; + BitField<49, 1, u64> nodep_flag; BitField<50, 3, u64> component_mask_selector; BitField<53, 4, u64> texture_info; @@ -645,6 +783,37 @@ union Instruction { UNREACHABLE(); } + TextureProcessMode GetTextureProcessMode() const { + switch (texture_info) { + case 0: + case 2: + case 6: + case 8: + case 9: + case 11: + return TextureProcessMode::LZ; + case 3: + case 5: + case 13: + return TextureProcessMode::LL; + default: + break; + } + return TextureProcessMode::None; + } + + bool UsesMiscMode(TextureMiscMode mode) const { + switch (mode) { + case TextureMiscMode::DC: + return (texture_info >= 4 && texture_info <= 6) || texture_info == 9; + case TextureMiscMode::NODEP: + return nodep_flag != 0; + default: + break; + } + return false; + } + bool IsArrayTexture() const { // TEXS only supports Texture2D arrays. return texture_info >= 7 && texture_info <= 9; @@ -673,6 +842,7 @@ union Instruction { } texs; union { + BitField<49, 1, u64> nodep_flag; BitField<53, 4, u64> texture_info; TextureType GetTextureType() const { @@ -693,6 +863,26 @@ union Instruction { UNREACHABLE(); } + TextureProcessMode GetTextureProcessMode() const { + if (texture_info == 1 || texture_info == 5 || texture_info == 12) + return TextureProcessMode::LL; + return TextureProcessMode::LZ; + } + + bool UsesMiscMode(TextureMiscMode mode) const { + switch (mode) { + case TextureMiscMode::AOFFI: + return texture_info == 12 || texture_info == 4; + case TextureMiscMode::MZ: + return texture_info == 5; + case TextureMiscMode::NODEP: + return nodep_flag != 0; + default: + break; + } + return false; + } + bool IsArrayTexture() const { // TEXS only supports Texture2D arrays. return texture_info == 8; @@ -735,6 +925,7 @@ union Instruction { BitField<36, 5, u64> index; } cbuf36; + BitField<47, 1, u64> generates_cc; BitField<61, 1, u64> is_b_imm; BitField<60, 1, u64> is_b_gpr; BitField<59, 1, u64> is_c_gpr; @@ -859,6 +1050,7 @@ public: ISET_IMM, PSETP, PSET, + CSETP, XMAD_IMM, XMAD_CR, XMAD_RC, @@ -1095,6 +1287,7 @@ private: INST("0011011-0101----", Id::ISET_IMM, Type::IntegerSet, "ISET_IMM"), INST("0101000010001---", Id::PSET, Type::PredicateSetRegister, "PSET"), INST("0101000010010---", Id::PSETP, Type::PredicateSetPredicate, "PSETP"), + INST("010100001010----", Id::CSETP, Type::PredicateSetPredicate, "CSETP"), INST("0011011-00------", Id::XMAD_IMM, Type::Xmad, "XMAD_IMM"), INST("0100111---------", Id::XMAD_CR, Type::Xmad, "XMAD_CR"), INST("010100010-------", Id::XMAD_RC, Type::Xmad, "XMAD_RC"), diff --git a/src/video_core/engines/shader_header.h b/src/video_core/engines/shader_header.h new file mode 100644 index 000000000..a885ee3cf --- /dev/null +++ b/src/video_core/engines/shader_header.h @@ -0,0 +1,103 @@ +// Copyright 2018 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include "common/bit_field.h" +#include "common/common_funcs.h" +#include "common/common_types.h" + +namespace Tegra::Shader { + +enum class OutputTopology : u32 { + PointList = 1, + LineStrip = 6, + TriangleStrip = 7, +}; + +// Documentation in: +// http://download.nvidia.com/open-gpu-doc/Shader-Program-Header/1/Shader-Program-Header.html#ImapTexture +struct Header { + union { + BitField<0, 5, u32> sph_type; + BitField<5, 5, u32> version; + BitField<10, 4, u32> shader_type; + BitField<14, 1, u32> mrt_enable; + BitField<15, 1, u32> kills_pixels; + BitField<16, 1, u32> does_global_store; + BitField<17, 4, u32> sass_version; + BitField<21, 5, u32> reserved; + BitField<26, 1, u32> does_load_or_store; + BitField<27, 1, u32> does_fp64; + BitField<28, 4, u32> stream_out_mask; + } common0; + + union { + BitField<0, 24, u32> shader_local_memory_low_size; + BitField<24, 8, u32> per_patch_attribute_count; + } common1; + + union { + BitField<0, 24, u32> shader_local_memory_high_size; + BitField<24, 8, u32> threads_per_input_primitive; + } common2; + + union { + BitField<0, 24, u32> shader_local_memory_crs_size; + BitField<24, 4, OutputTopology> output_topology; + BitField<28, 4, u32> reserved; + } common3; + + union { + BitField<0, 12, u32> max_output_vertices; + BitField<12, 8, u32> store_req_start; // NOTE: not used by geometry shaders. + BitField<24, 4, u32> reserved; + BitField<12, 8, u32> store_req_end; // NOTE: not used by geometry shaders. + } common4; + + union { + struct { + INSERT_PADDING_BYTES(3); // ImapSystemValuesA + INSERT_PADDING_BYTES(1); // ImapSystemValuesB + INSERT_PADDING_BYTES(16); // ImapGenericVector[32] + INSERT_PADDING_BYTES(2); // ImapColor + INSERT_PADDING_BYTES(2); // ImapSystemValuesC + INSERT_PADDING_BYTES(5); // ImapFixedFncTexture[10] + INSERT_PADDING_BYTES(1); // ImapReserved + INSERT_PADDING_BYTES(3); // OmapSystemValuesA + INSERT_PADDING_BYTES(1); // OmapSystemValuesB + INSERT_PADDING_BYTES(16); // OmapGenericVector[32] + INSERT_PADDING_BYTES(2); // OmapColor + INSERT_PADDING_BYTES(2); // OmapSystemValuesC + INSERT_PADDING_BYTES(5); // OmapFixedFncTexture[10] + INSERT_PADDING_BYTES(1); // OmapReserved + } vtg; + + struct { + INSERT_PADDING_BYTES(3); // ImapSystemValuesA + INSERT_PADDING_BYTES(1); // ImapSystemValuesB + INSERT_PADDING_BYTES(32); // ImapGenericVector[32] + INSERT_PADDING_BYTES(2); // ImapColor + INSERT_PADDING_BYTES(2); // ImapSystemValuesC + INSERT_PADDING_BYTES(10); // ImapFixedFncTexture[10] + INSERT_PADDING_BYTES(2); // ImapReserved + struct { + u32 target; + union { + BitField<0, 1, u32> sample_mask; + BitField<1, 1, u32> depth; + BitField<2, 30, u32> reserved; + }; + } omap; + bool IsColorComponentOutputEnabled(u32 render_target, u32 component) const { + const u32 bit = render_target * 4 + component; + return omap.target & (1 << bit); + } + } ps; + }; +}; + +static_assert(sizeof(Header) == 0x50, "Incorrect structure size"); + +} // namespace Tegra::Shader diff --git a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp index 252ff18fc..b3e95187e 100644 --- a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp +++ b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp @@ -12,6 +12,7 @@ #include "common/assert.h" #include "common/common_types.h" #include "video_core/engines/shader_bytecode.h" +#include "video_core/engines/shader_header.h" #include "video_core/renderer_opengl/gl_rasterizer.h" #include "video_core/renderer_opengl/gl_shader_decompiler.h" @@ -26,7 +27,7 @@ using Tegra::Shader::Sampler; using Tegra::Shader::SubOp; constexpr u32 PROGRAM_END = MAX_PROGRAM_CODE_LENGTH; -constexpr u32 PROGRAM_HEADER_SIZE = 0x50; +constexpr u32 PROGRAM_HEADER_SIZE = sizeof(Tegra::Shader::Header); class DecompileFail : public std::runtime_error { public: @@ -235,6 +236,14 @@ private: const std::string& suffix; }; +enum class InternalFlag : u64 { + ZeroFlag = 0, + CarryFlag = 1, + OverflowFlag = 2, + NaNFlag = 3, + Amount +}; + /** * Used to manage shader registers that are emulated with GLSL. This class keeps track of the state * of all registers (e.g. whether they are currently being used as Floats or Integers), and @@ -328,13 +337,19 @@ public: void SetRegisterToInteger(const Register& reg, bool is_signed, u64 elem, const std::string& value, u64 dest_num_components, u64 value_num_components, bool is_saturated = false, - u64 dest_elem = 0, Register::Size size = Register::Size::Word) { + u64 dest_elem = 0, Register::Size size = Register::Size::Word, + bool sets_cc = false) { ASSERT_MSG(!is_saturated, "Unimplemented"); const std::string func{is_signed ? "intBitsToFloat" : "uintBitsToFloat"}; SetRegister(reg, elem, func + '(' + ConvertIntegerSize(value, size) + ')', dest_num_components, value_num_components, dest_elem); + + if (sets_cc) { + const std::string zero_condition = "( " + ConvertIntegerSize(value, size) + " == 0 )"; + SetInternalFlag(InternalFlag::ZeroFlag, zero_condition); + } } /** @@ -351,6 +366,26 @@ public: shader.AddLine(dest + " = " + src + ';'); } + std::string GetControlCode(const Tegra::Shader::ControlCode cc) const { + switch (cc) { + case Tegra::Shader::ControlCode::NEU: + return "!(" + GetInternalFlag(InternalFlag::ZeroFlag) + ')'; + default: + LOG_CRITICAL(HW_GPU, "Unimplemented Control Code {}", static_cast<u32>(cc)); + UNREACHABLE(); + return "false"; + } + } + + std::string GetInternalFlag(const InternalFlag ii) const { + const u32 code = static_cast<u32>(ii); + return "internalFlag_" + std::to_string(code) + suffix; + } + + void SetInternalFlag(const InternalFlag ii, const std::string& value) const { + shader.AddLine(GetInternalFlag(ii) + " = " + value + ';'); + } + /** * Writes code that does a output attribute assignment to register operation. Output attributes * are stored as floats, so this may require conversion. @@ -414,6 +449,12 @@ public: } declarations.AddNewLine(); + for (u32 ii = 0; ii < static_cast<u64>(InternalFlag::Amount); ii++) { + const InternalFlag code = static_cast<InternalFlag>(ii); + declarations.AddLine("bool " + GetInternalFlag(code) + " = false;"); + } + declarations.AddNewLine(); + for (const auto element : declr_input_attribute) { // TODO(bunnei): Use proper number of elements for these u32 idx = @@ -674,7 +715,7 @@ public: u32 main_offset, Maxwell3D::Regs::ShaderStage stage, const std::string& suffix) : subroutines(subroutines), program_code(program_code), main_offset(main_offset), stage(stage), suffix(suffix) { - + std::memcpy(&header, program_code.data(), sizeof(Tegra::Shader::Header)); Generate(suffix); } @@ -688,23 +729,6 @@ public: } private: - // Shader program header for a Fragment Shader. - struct FragmentHeader { - INSERT_PADDING_WORDS(5); - INSERT_PADDING_WORDS(13); - u32 enabled_color_outputs; - union { - BitField<0, 1, u32> writes_samplemask; - BitField<1, 1, u32> writes_depth; - }; - - bool IsColorComponentOutputEnabled(u32 render_target, u32 component) const { - const u32 bit = render_target * 4 + component; - return enabled_color_outputs & (1 << bit); - } - }; - static_assert(sizeof(FragmentHeader) == PROGRAM_HEADER_SIZE, "FragmentHeader size is wrong"); - /// Gets the Subroutine object corresponding to the specified address. const Subroutine& GetSubroutine(u32 begin, u32 end) const { const auto iter = subroutines.find(Subroutine{begin, end, suffix}); @@ -954,8 +978,6 @@ private: // TEXS has two destination registers and a swizzle. The first two elements in the swizzle // go into gpr0+0 and gpr0+1, and the rest goes into gpr28+0 and gpr28+1 - ASSERT_MSG(instr.texs.nodep == 0, "TEXS nodep not implemented"); - std::size_t written_components = 0; for (u32 component = 0; component < 4; ++component) { if (!instr.texs.IsComponentEnabled(component)) { @@ -1010,10 +1032,8 @@ private: /// Writes the output values from a fragment shader to the corresponding GLSL output variables. void EmitFragmentOutputsWrite() { ASSERT(stage == Maxwell3D::Regs::ShaderStage::Fragment); - FragmentHeader header; - std::memcpy(&header, program_code.data(), PROGRAM_HEADER_SIZE); - ASSERT_MSG(header.writes_samplemask == 0, "Samplemask write is unimplemented"); + ASSERT_MSG(header.ps.omap.sample_mask == 0, "Samplemask write is unimplemented"); // Write the color outputs using the data in the shader registers, disabled // rendertargets/components are skipped in the register assignment. @@ -1022,7 +1042,7 @@ private: ++render_target) { // TODO(Subv): Figure out how dual-source blending is configured in the Switch. for (u32 component = 0; component < 4; ++component) { - if (header.IsColorComponentOutputEnabled(render_target, component)) { + if (header.ps.IsColorComponentOutputEnabled(render_target, component)) { shader.AddLine(fmt::format("FragColor{}[{}] = {};", render_target, component, regs.GetRegisterAsFloat(current_reg))); ++current_reg; @@ -1030,7 +1050,7 @@ private: } } - if (header.writes_depth) { + if (header.ps.omap.depth) { // The depth output is always 2 registers after the last color output, and current_reg // already contains one past the last color register. @@ -1640,7 +1660,8 @@ private: } regs.SetRegisterToInteger(instr.gpr0, instr.conversion.is_output_signed, 0, op_a, 1, - 1, instr.alu.saturate_d, 0, instr.conversion.dest_size); + 1, instr.alu.saturate_d, 0, instr.conversion.dest_size, + instr.generates_cc.Value() != 0); break; } case OpCode::Id::I2F_R: @@ -1779,8 +1800,8 @@ private: Tegra::Shader::IpaMode input_mode{Tegra::Shader::IpaInterpMode::Perspective, Tegra::Shader::IpaSampleMode::Default}; - u32 next_element = instr.attribute.fmt20.element; - u32 next_index = static_cast<u32>(instr.attribute.fmt20.index.Value()); + u64 next_element = instr.attribute.fmt20.element; + u64 next_index = static_cast<u64>(instr.attribute.fmt20.index.Value()); const auto LoadNextElement = [&](u32 reg_offset) { regs.SetRegisterToInputAttibute(instr.gpr0.Value() + reg_offset, next_element, @@ -1844,8 +1865,8 @@ private: ASSERT_MSG((instr.attribute.fmt20.immediate.Value() % sizeof(u32)) == 0, "Unaligned attribute loads are not supported"); - u32 next_element = instr.attribute.fmt20.element; - u32 next_index = static_cast<u32>(instr.attribute.fmt20.index.Value()); + u64 next_element = instr.attribute.fmt20.element; + u64 next_index = static_cast<u64>(instr.attribute.fmt20.index.Value()); const auto StoreNextElement = [&](u32 reg_offset) { regs.SetOutputAttributeToRegister(static_cast<Attribute::Index>(next_index), @@ -1871,6 +1892,13 @@ private: Tegra::Shader::TextureType texture_type{instr.tex.texture_type}; std::string coord; + ASSERT_MSG(!instr.tex.UsesMiscMode(Tegra::Shader::TextureMiscMode::NODEP), + "NODEP is not implemented"); + ASSERT_MSG(!instr.tex.UsesMiscMode(Tegra::Shader::TextureMiscMode::AOFFI), + "AOFFI is not implemented"); + ASSERT_MSG(!instr.tex.UsesMiscMode(Tegra::Shader::TextureMiscMode::DC), + "DC is not implemented"); + switch (texture_type) { case Tegra::Shader::TextureType::Texture1D: { const std::string x = regs.GetRegisterAsFloat(instr.gpr8); @@ -1953,6 +1981,11 @@ private: Tegra::Shader::TextureType texture_type{instr.texs.GetTextureType()}; bool is_array{instr.texs.IsArrayTexture()}; + ASSERT_MSG(!instr.texs.UsesMiscMode(Tegra::Shader::TextureMiscMode::NODEP), + "NODEP is not implemented"); + ASSERT_MSG(!instr.texs.UsesMiscMode(Tegra::Shader::TextureMiscMode::DC), + "DC is not implemented"); + switch (texture_type) { case Tegra::Shader::TextureType::Texture2D: { if (is_array) { @@ -1989,6 +2022,13 @@ private: ASSERT(instr.tlds.IsArrayTexture() == false); std::string coord; + ASSERT_MSG(!instr.tlds.UsesMiscMode(Tegra::Shader::TextureMiscMode::NODEP), + "NODEP is not implemented"); + ASSERT_MSG(!instr.tlds.UsesMiscMode(Tegra::Shader::TextureMiscMode::AOFFI), + "AOFFI is not implemented"); + ASSERT_MSG(!instr.tlds.UsesMiscMode(Tegra::Shader::TextureMiscMode::MZ), + "MZ is not implemented"); + switch (instr.tlds.GetTextureType()) { case Tegra::Shader::TextureType::Texture2D: { if (instr.tlds.IsArrayTexture()) { @@ -2017,6 +2057,17 @@ private: ASSERT(instr.tld4.array == 0); std::string coord; + ASSERT_MSG(!instr.tld4.UsesMiscMode(Tegra::Shader::TextureMiscMode::NODEP), + "NODEP is not implemented"); + ASSERT_MSG(!instr.tld4.UsesMiscMode(Tegra::Shader::TextureMiscMode::AOFFI), + "AOFFI is not implemented"); + ASSERT_MSG(!instr.tld4.UsesMiscMode(Tegra::Shader::TextureMiscMode::DC), + "DC is not implemented"); + ASSERT_MSG(!instr.tld4.UsesMiscMode(Tegra::Shader::TextureMiscMode::NDV), + "NDV is not implemented"); + ASSERT_MSG(!instr.tld4.UsesMiscMode(Tegra::Shader::TextureMiscMode::PTP), + "PTP is not implemented"); + switch (instr.tld4.texture_type) { case Tegra::Shader::TextureType::Texture2D: { const std::string x = regs.GetRegisterAsFloat(instr.gpr8); @@ -2054,6 +2105,13 @@ private: break; } case OpCode::Id::TLD4S: { + ASSERT_MSG(!instr.tld4s.UsesMiscMode(Tegra::Shader::TextureMiscMode::NODEP), + "NODEP is not implemented"); + ASSERT_MSG(!instr.tld4s.UsesMiscMode(Tegra::Shader::TextureMiscMode::AOFFI), + "AOFFI is not implemented"); + ASSERT_MSG(!instr.tld4s.UsesMiscMode(Tegra::Shader::TextureMiscMode::DC), + "DC is not implemented"); + const std::string op_a = regs.GetRegisterAsFloat(instr.gpr8); const std::string op_b = regs.GetRegisterAsFloat(instr.gpr20); // TODO(Subv): Figure out how the sampler type is encoded in the TLD4S instruction. @@ -2066,6 +2124,9 @@ private: break; } case OpCode::Id::TXQ: { + ASSERT_MSG(!instr.txq.UsesMiscMode(Tegra::Shader::TextureMiscMode::NODEP), + "NODEP is not implemented"); + // TODO: the new commits on the texture refactor, change the way samplers work. // Sadly, not all texture instructions specify the type of texture their sampler // uses. This must be fixed at a later instance. @@ -2086,6 +2147,11 @@ private: break; } case OpCode::Id::TMML: { + ASSERT_MSG(!instr.tmml.UsesMiscMode(Tegra::Shader::TextureMiscMode::NODEP), + "NODEP is not implemented"); + ASSERT_MSG(!instr.tmml.UsesMiscMode(Tegra::Shader::TextureMiscMode::NDV), + "NDV is not implemented"); + const std::string op_a = regs.GetRegisterAsFloat(instr.gpr8); const std::string op_b = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1); const bool is_array = instr.tmml.array != 0; @@ -2252,31 +2318,55 @@ private: break; } case OpCode::Type::PredicateSetPredicate: { - const std::string op_a = - GetPredicateCondition(instr.psetp.pred12, instr.psetp.neg_pred12 != 0); - const std::string op_b = - GetPredicateCondition(instr.psetp.pred29, instr.psetp.neg_pred29 != 0); + switch (opcode->GetId()) { + case OpCode::Id::PSETP: { + const std::string op_a = + GetPredicateCondition(instr.psetp.pred12, instr.psetp.neg_pred12 != 0); + const std::string op_b = + GetPredicateCondition(instr.psetp.pred29, instr.psetp.neg_pred29 != 0); - // We can't use the constant predicate as destination. - ASSERT(instr.psetp.pred3 != static_cast<u64>(Pred::UnusedIndex)); + // We can't use the constant predicate as destination. + ASSERT(instr.psetp.pred3 != static_cast<u64>(Pred::UnusedIndex)); - const std::string second_pred = - GetPredicateCondition(instr.psetp.pred39, instr.psetp.neg_pred39 != 0); + const std::string second_pred = + GetPredicateCondition(instr.psetp.pred39, instr.psetp.neg_pred39 != 0); - const std::string combiner = GetPredicateCombiner(instr.psetp.op); + const std::string combiner = GetPredicateCombiner(instr.psetp.op); - const std::string predicate = - '(' + op_a + ") " + GetPredicateCombiner(instr.psetp.cond) + " (" + op_b + ')'; + const std::string predicate = + '(' + op_a + ") " + GetPredicateCombiner(instr.psetp.cond) + " (" + op_b + ')'; - // Set the primary predicate to the result of Predicate OP SecondPredicate - SetPredicate(instr.psetp.pred3, - '(' + predicate + ") " + combiner + " (" + second_pred + ')'); + // Set the primary predicate to the result of Predicate OP SecondPredicate + SetPredicate(instr.psetp.pred3, + '(' + predicate + ") " + combiner + " (" + second_pred + ')'); - if (instr.psetp.pred0 != static_cast<u64>(Pred::UnusedIndex)) { - // Set the secondary predicate to the result of !Predicate OP SecondPredicate, - // if enabled - SetPredicate(instr.psetp.pred0, - "!(" + predicate + ") " + combiner + " (" + second_pred + ')'); + if (instr.psetp.pred0 != static_cast<u64>(Pred::UnusedIndex)) { + // Set the secondary predicate to the result of !Predicate OP SecondPredicate, + // if enabled + SetPredicate(instr.psetp.pred0, + "!(" + predicate + ") " + combiner + " (" + second_pred + ')'); + } + break; + } + case OpCode::Id::CSETP: { + const std::string pred = + GetPredicateCondition(instr.csetp.pred39, instr.csetp.neg_pred39 != 0); + const std::string combiner = GetPredicateCombiner(instr.csetp.op); + const std::string controlCode = regs.GetControlCode(instr.csetp.cc); + if (instr.csetp.pred3 != static_cast<u64>(Pred::UnusedIndex)) { + SetPredicate(instr.csetp.pred3, + '(' + controlCode + ") " + combiner + " (" + pred + ')'); + } + if (instr.csetp.pred0 != static_cast<u64>(Pred::UnusedIndex)) { + SetPredicate(instr.csetp.pred0, + "!(" + controlCode + ") " + combiner + " (" + pred + ')'); + } + break; + } + default: { + LOG_CRITICAL(HW_GPU, "Unhandled predicate instruction: {}", opcode->GetName()); + UNREACHABLE(); + } } break; } @@ -2666,6 +2756,7 @@ private: private: const std::set<Subroutine>& subroutines; const ProgramCode& program_code; + Tegra::Shader::Header header; const u32 main_offset; Maxwell3D::Regs::ShaderStage stage; const std::string& suffix; diff --git a/src/yuzu_cmd/config.cpp b/src/yuzu_cmd/config.cpp index 991abda2e..7ec1f5110 100644 --- a/src/yuzu_cmd/config.cpp +++ b/src/yuzu_cmd/config.cpp @@ -125,6 +125,10 @@ void Config::ReadValues() { // System Settings::values.use_docked_mode = sdl2_config->GetBoolean("System", "use_docked_mode", false); + Settings::values.username = sdl2_config->Get("System", "username", "yuzu"); + if (Settings::values.username.empty()) { + Settings::values.username = "yuzu"; + } // Miscellaneous Settings::values.log_filter = sdl2_config->Get("Miscellaneous", "log_filter", "*:Trace"); diff --git a/src/yuzu_cmd/default_ini.h b/src/yuzu_cmd/default_ini.h index 002a4ec15..d35c441e9 100644 --- a/src/yuzu_cmd/default_ini.h +++ b/src/yuzu_cmd/default_ini.h @@ -176,7 +176,7 @@ use_docked_mode = # Sets the account username, max length is 32 characters # yuzu (default) -username = +username = yuzu # Sets the systems language index # 0: Japanese, 1: English (default), 2: French, 3: German, 4: Italian, 5: Spanish, 6: Chinese, |