diff --git a/Ryujinx.HLE/HOS/Kernel/SupervisorCall/RAttribute.cs b/Ryujinx.HLE/HOS/Kernel/SupervisorCall/RAttribute.cs new file mode 100644 index 0000000000..028ba7713d --- /dev/null +++ b/Ryujinx.HLE/HOS/Kernel/SupervisorCall/RAttribute.cs @@ -0,0 +1,14 @@ +using System; + +namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall +{ + [AttributeUsage(AttributeTargets.Parameter, AllowMultiple = false)] + public class RAttribute : Attribute + { + public readonly int Index; + public RAttribute(int index) + { + Index = index; + } + } +} diff --git a/Ryujinx.HLE/HOS/Kernel/SupervisorCall/SvcIpc.cs b/Ryujinx.HLE/HOS/Kernel/SupervisorCall/SvcIpc.cs index 7c1c981bf0..4ba52465c5 100644 --- a/Ryujinx.HLE/HOS/Kernel/SupervisorCall/SvcIpc.cs +++ b/Ryujinx.HLE/HOS/Kernel/SupervisorCall/SvcIpc.cs @@ -35,6 +35,11 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall return ConnectToNamedPort(namePtr, out handle); } + public KernelResult ConnectToNamedPort32([R(1)] uint namePtr, [R(1)] out int handle) + { + return ConnectToNamedPort(namePtr, out handle); + } + private KernelResult ConnectToNamedPort(ulong namePtr, out int handle) { handle = 0; @@ -86,6 +91,11 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall return SendSyncRequest((ulong)_system.Scheduler.GetCurrentThread().Context.Tpidr, 0x100, handle); } + public KernelResult SendSyncRequest32([R(0)] int handle) + { + return SendSyncRequest((ulong)_system.Scheduler.GetCurrentThread().Context.Tpidr, 0x100, handle); + } + public KernelResult SendSyncRequestWithUserBuffer64(ulong messagePtr, ulong size, int handle) { return SendSyncRequest(messagePtr, size, handle); diff --git a/Ryujinx.HLE/HOS/Kernel/SupervisorCall/SvcMemory.cs b/Ryujinx.HLE/HOS/Kernel/SupervisorCall/SvcMemory.cs index 9943aca6d4..51b82c3d61 100644 --- a/Ryujinx.HLE/HOS/Kernel/SupervisorCall/SvcMemory.cs +++ b/Ryujinx.HLE/HOS/Kernel/SupervisorCall/SvcMemory.cs @@ -11,6 +11,17 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall return SetHeapSize(size, out position); } + public KernelResult SetHeapSize32([R(1)] uint size, [R(1)] out uint position) + { + ulong temporaryPosition; + + KernelResult result = SetHeapSize(size, out temporaryPosition); + + position = (uint)temporaryPosition; + + return result; + } + private KernelResult SetHeapSize(ulong size, out ulong position) { if ((size & 0xfffffffe001fffff) != 0) @@ -32,6 +43,15 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall return SetMemoryAttribute(position, size, attributeMask, attributeValue); } + public KernelResult SetMemoryAttribute32( + [R(0)] uint position, + [R(1)] uint size, + [R(2)] MemoryAttribute attributeMask, + [R(3)] MemoryAttribute attributeValue) + { + return SetMemoryAttribute(position, size, attributeMask, attributeValue); + } + private KernelResult SetMemoryAttribute( ulong position, ulong size, @@ -70,6 +90,11 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall return MapMemory(dst, src, size); } + public KernelResult MapMemory32([R(0)] uint dst, [R(1)] uint src, [R(2)] uint size) + { + return MapMemory(dst, src, size); + } + private KernelResult MapMemory(ulong dst, ulong src, ulong size) { if (!PageAligned(src | dst)) @@ -109,6 +134,11 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall return UnmapMemory(dst, src, size); } + public KernelResult UnmapMemory32([R(0)] uint dst, [R(1)] uint src, [R(2)] uint size) + { + return UnmapMemory(dst, src, size); + } + private KernelResult UnmapMemory(ulong dst, ulong src, ulong size) { if (!PageAligned(src | dst)) @@ -148,8 +178,9 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall return QueryMemory(infoPtr, position); } - public KernelResult QueryMemory32(uint infoPtr, uint x1, uint position) + public KernelResult QueryMemory32([R(0)] uint infoPtr, [R(1)] uint r1, [R(2)] uint position) { + // FIXME: Nintendo here bzero the pointer info structure and then copy every element one by one if QueryMemory succeed. return QueryMemory(infoPtr, position); } @@ -274,6 +305,15 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall return CreateTransferMemory(address, size, permission, out handle); } + public KernelResult CreateTransferMemory32( + [R(1)] uint address, + [R(2)] uint size, + [R(3)] MemoryPermission permission, + [R(1)] out int handle) + { + return CreateTransferMemory(address, size, permission, out handle); + } + private KernelResult CreateTransferMemory(ulong address, ulong size, MemoryPermission permission, out int handle) { handle = 0; diff --git a/Ryujinx.HLE/HOS/Kernel/SupervisorCall/SvcSystem.cs b/Ryujinx.HLE/HOS/Kernel/SupervisorCall/SvcSystem.cs index 236947f12c..c2a4c0327f 100644 --- a/Ryujinx.HLE/HOS/Kernel/SupervisorCall/SvcSystem.cs +++ b/Ryujinx.HLE/HOS/Kernel/SupervisorCall/SvcSystem.cs @@ -17,6 +17,11 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall ExitProcess(); } + public void ExitProcess32() + { + ExitProcess(); + } + public KernelResult TerminateProcess64(int handle) { return TerminateProcess(handle); @@ -109,6 +114,11 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall return CloseHandle(handle); } + public KernelResult CloseHandle32([R(0)] int handle) + { + return CloseHandle(handle); + } + private KernelResult CloseHandle(int handle) { KAutoObject obj = _process.HandleTable.GetObject(handle); @@ -208,6 +218,11 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall Break(reason); } + public void Break32([R(0)] uint reason, [R(1)] uint r1, [R(2)] uint info) + { + Break(reason); + } + private void Break(ulong reason) { KThread currentThread = _system.Scheduler.GetCurrentThread(); @@ -240,7 +255,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall OutputDebugString(strPtr, size); } - public void OutputDebugString32(uint strPtr, uint size) + public void OutputDebugString32([R(0)] uint strPtr, [R(1)] uint size) { OutputDebugString(strPtr, size); } @@ -257,12 +272,22 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall return GetInfo(id, handle, subId, out value); } - public KernelResult GetInfo32(uint subIdLo, uint id, int handle, uint subIdHi, out uint valueLo, out uint valueHi) + public KernelResult GetInfo32( + [R(0)] uint subIdLow, + [R(1)] uint id, + [R(2)] int handle, + [R(3)] uint subIdHigh, + [R(1)] out uint valueLow, + [R(2)] out uint valueHigh) { long value; - KernelResult result = GetInfo(id, handle, subIdHi | ((long)subIdLo << 32), out value); - valueLo = (uint)(value >> 32); - valueHi = (uint)value; + + long subId = (long)(subIdLow | ((ulong)subIdHigh << 32)); + + KernelResult result = GetInfo(id, handle, subId, out value); + valueHigh = (uint)(value >> 32); + valueLow = (uint)(value & 0xFFFFFFFF); + return result; } diff --git a/Ryujinx.HLE/HOS/Kernel/SupervisorCall/SvcTable.cs b/Ryujinx.HLE/HOS/Kernel/SupervisorCall/SvcTable.cs index 9a94394de6..bf689d4226 100644 --- a/Ryujinx.HLE/HOS/Kernel/SupervisorCall/SvcTable.cs +++ b/Ryujinx.HLE/HOS/Kernel/SupervisorCall/SvcTable.cs @@ -3,6 +3,7 @@ using Ryujinx.Common.Logging; using Ryujinx.HLE.HOS.Kernel.Common; using System; using System.Collections.Generic; +using System.Diagnostics; using System.Linq; using System.Reflection; using System.Reflection.Emit; @@ -11,7 +12,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall { static class SvcTable { - private const int SvcFuncMaxArguments = 8; + private const int SvcFuncMaxArguments64 = 8; private const int SvcFuncMaxArguments32 = 4; private static Dictionary _svcFuncs64; @@ -84,10 +85,30 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall _svcFuncs32 = new Dictionary { + { 0x01, nameof(SvcHandler.SetHeapSize32) }, + { 0x03, nameof(SvcHandler.SetMemoryAttribute32) }, + { 0x04, nameof(SvcHandler.MapMemory32) }, + { 0x05, nameof(SvcHandler.UnmapMemory32) }, { 0x06, nameof(SvcHandler.QueryMemory32) }, + { 0x07, nameof(SvcHandler.ExitProcess32) }, { 0x08, nameof(SvcHandler.CreateThread32) }, + { 0x09, nameof(SvcHandler.StartThread32) }, + { 0x0a, nameof(SvcHandler.ExitThread32) }, + { 0x0b, nameof(SvcHandler.SleepThread32) }, + { 0x0c, nameof(SvcHandler.GetThreadPriority32) }, + { 0x0d, nameof(SvcHandler.SetThreadPriority32) }, + { 0x0f, nameof(SvcHandler.SetThreadCoreMask32) }, + { 0x10, nameof(SvcHandler.GetCurrentProcessorNumber32) }, + { 0x15, nameof(SvcHandler.CreateTransferMemory32) }, + { 0x16, nameof(SvcHandler.CloseHandle32) }, + { 0x18, nameof(SvcHandler.WaitSynchronization32) }, + { 0x1d, nameof(SvcHandler.SignalProcessWideKey32) }, + { 0x1f, nameof(SvcHandler.ConnectToNamedPort32) }, + { 0x21, nameof(SvcHandler.SendSyncRequest32) }, + { 0x25, nameof(SvcHandler.GetThreadId32) }, + { 0x26, nameof(SvcHandler.Break32) }, { 0x27, nameof(SvcHandler.OutputDebugString32) }, - { 0x29, nameof(SvcHandler.GetInfo64) } + { 0x29, nameof(SvcHandler.GetInfo32) } }; _svcTable64 = new Action[0x80]; @@ -106,19 +127,32 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall if (funcTable.TryGetValue(svcId, out string svcName)) { if (svcId == 0x08) { } - return table[svcId] = GenerateMethod(svcName, aarch32); + + Action svcFunc; + + if (aarch32) + { + svcFunc = GenerateMethod32(svcName); + } + else + { + svcFunc = GenerateMethod64(svcName); + } + table[svcId] = svcFunc; + + return table[svcId] = svcFunc; } if (aarch32 && _svcFuncs64.TryGetValue(svcId, out string svcName64)) { Logger.PrintWarning(LogClass.KernelSvc, $"Svc \"{svcName64}\" ({svcId}) does not have a 32-bit call signature defined - fell back to 64-bit."); - return table[svcId] = GenerateMethod(svcName64, aarch32); + throw new NotImplementedException($"Svc \"{svcName64}\" ({svcId}) does not have a 32-bit call signature defined."); } return null; } - private static Action GenerateMethod(string svcName, bool aarch32) + private static Action GenerateMethod64(string svcName) { Type[] argTypes = new Type[] { typeof(SvcHandler), typeof(ExecutionContext) }; @@ -128,12 +162,9 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall ParameterInfo[] methodArgs = methodInfo.GetParameters(); - int maxArgs = aarch32 ? SvcFuncMaxArguments32 : SvcFuncMaxArguments; - int numArgs = methodArgs.Count(x => !x.IsOut); - - if (numArgs > maxArgs) + if (methodArgs.Length > SvcFuncMaxArguments64) { - //throw new InvalidOperationException($"Method \"{svcName}\" has too many arguments, max is {maxArgs}."); + throw new InvalidOperationException($"Method \"{svcName}\" has too many arguments, max is {SvcFuncMaxArguments64}."); } ILGenerator generator = method.GetILGenerator(); @@ -212,8 +243,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall generator.Emit(OpCodes.Ldc_I4, index); generator.Emit(OpCodes.Ldarg_1); - int argIndex = (aarch32 && index >= maxArgs) ? index - maxArgs : byRefArgsCount + index; - generator.Emit(OpCodes.Ldc_I4, argIndex); + generator.Emit(OpCodes.Ldc_I4, byRefArgsCount + index); MethodInfo info = typeof(ExecutionContext).GetMethod(nameof(ExecutionContext.GetX)); @@ -275,8 +305,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall else { generator.Emit(OpCodes.Ldarg_1); - int argIndex = (aarch32 && index >= maxArgs) ? index - maxArgs : byRefArgsCount + index; - generator.Emit(OpCodes.Ldc_I4, argIndex); + generator.Emit(OpCodes.Ldc_I4, byRefArgsCount + index); MethodInfo info = typeof(ExecutionContext).GetMethod(nameof(ExecutionContext.GetX)); @@ -335,7 +364,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall } // Zero out the remaining unused registers. - while (outRegIndex < maxArgs) + while (outRegIndex < SvcFuncMaxArguments64) { generator.Emit(OpCodes.Ldarg_1); generator.Emit(OpCodes.Ldc_I4, outRegIndex++); @@ -351,6 +380,273 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall return (Action)method.CreateDelegate(typeof(Action)); } + // TODO: merge 64 bits variant + private static Action GenerateMethod32(string svcName) + { + Type[] argTypes = new Type[] { typeof(SvcHandler), typeof(ExecutionContext) }; + + DynamicMethod method = new DynamicMethod(svcName, null, argTypes); + + MethodInfo methodInfo = typeof(SvcHandler).GetMethod(svcName); + + ParameterInfo[] methodArgs = methodInfo.GetParameters(); + int numArgs = methodArgs.Count(x => !x.IsOut); + + ILGenerator generator = method.GetILGenerator(); + + void ConvertToArgType(Type sourceType) + { + CheckIfTypeIsSupported(sourceType, svcName); + + switch (Type.GetTypeCode(sourceType)) + { + case TypeCode.UInt32: generator.Emit(OpCodes.Conv_U4); break; + case TypeCode.Int32: generator.Emit(OpCodes.Conv_I4); break; + case TypeCode.UInt16: generator.Emit(OpCodes.Conv_U2); break; + case TypeCode.Int16: generator.Emit(OpCodes.Conv_I2); break; + case TypeCode.Byte: generator.Emit(OpCodes.Conv_U1); break; + case TypeCode.SByte: generator.Emit(OpCodes.Conv_I1); break; + + case TypeCode.Boolean: + generator.Emit(OpCodes.Conv_I4); + generator.Emit(OpCodes.Ldc_I4_1); + generator.Emit(OpCodes.And); + break; + } + } + + void ConvertToFieldType(Type sourceType) + { + CheckIfTypeIsSupported(sourceType, svcName); + + switch (Type.GetTypeCode(sourceType)) + { + case TypeCode.UInt32: + case TypeCode.Int32: + case TypeCode.UInt16: + case TypeCode.Int16: + case TypeCode.Byte: + case TypeCode.SByte: + case TypeCode.Boolean: + generator.Emit(OpCodes.Conv_U8); + break; + } + } + + RAttribute GetRegisterAttribute(ParameterInfo parameterInfo) + { + RAttribute argumentAttribute = (RAttribute)parameterInfo.GetCustomAttribute(typeof(RAttribute)); + + if (argumentAttribute == null) + { + throw new InvalidOperationException($"Method \"{svcName}\" is missing a {typeof(RAttribute).Name} attribute on parameter \"{parameterInfo.Name}\""); + } + + return argumentAttribute; + } + + // For functions returning output values, the first registers + // are used to hold pointers where the value will be stored, + // so they can't be used to pass argument and we must + // skip them. + int byRefArgsCount = 0; + + for (int index = 0; index < methodArgs.Length; index++) + { + if (methodArgs[index].ParameterType.IsByRef) + { + byRefArgsCount++; + } + } + + BindingFlags staticNonPublic = BindingFlags.NonPublic | BindingFlags.Static; + + // Print all the arguments for debugging purposes. + int inputArgsCount = methodArgs.Length - byRefArgsCount; + + if (inputArgsCount != 0) + { + generator.Emit(OpCodes.Ldc_I4, inputArgsCount); + + generator.Emit(OpCodes.Newarr, typeof(object)); + + string argsFormat = svcName; + + for (int index = 0; index < methodArgs.Length; index++) + { + Type argType = methodArgs[index].ParameterType; + + // Ignore out argument for printing + if (argType.IsByRef) + { + continue; + } + + RAttribute registerAttribute = GetRegisterAttribute(methodArgs[index]); + + argsFormat += $" {methodArgs[index].Name}: 0x{{{index}:X8}},"; + + generator.Emit(OpCodes.Dup); + generator.Emit(OpCodes.Ldc_I4, index); + + generator.Emit(OpCodes.Ldarg_1); + generator.Emit(OpCodes.Ldc_I4, registerAttribute.Index); + + MethodInfo info = typeof(ExecutionContext).GetMethod(nameof(ExecutionContext.GetX)); + + generator.Emit(OpCodes.Call, info); + + generator.Emit(OpCodes.Box, typeof(ulong)); + + generator.Emit(OpCodes.Stelem_Ref); + } + + argsFormat = argsFormat.Substring(0, argsFormat.Length - 1); + + generator.Emit(OpCodes.Ldstr, argsFormat); + } + else + { + generator.Emit(OpCodes.Ldnull); + + generator.Emit(OpCodes.Ldstr, svcName); + } + + MethodInfo printArgsMethod = typeof(SvcTable).GetMethod(nameof(PrintArguments), staticNonPublic); + + generator.Emit(OpCodes.Call, printArgsMethod); + + // Call the SVC function handler. + generator.Emit(OpCodes.Ldarg_0); + + List<(LocalBuilder, RAttribute)> locals = new List<(LocalBuilder, RAttribute)>(); + + for (int index = 0; index < methodArgs.Length; index++) + { + Type argType = methodArgs[index].ParameterType; + RAttribute registerAttribute = GetRegisterAttribute(methodArgs[index]); + + if (argType.IsByRef) + { + argType = argType.GetElementType(); + + LocalBuilder local = generator.DeclareLocal(argType); + + locals.Add((local, registerAttribute)); + + if (!methodArgs[index].IsOut) + { + generator.Emit(OpCodes.Ldarg_1); + generator.Emit(OpCodes.Ldc_I4, registerAttribute.Index); + + MethodInfo info = typeof(ExecutionContext).GetMethod(nameof(ExecutionContext.GetX)); + + generator.Emit(OpCodes.Call, info); + + ConvertToArgType(argType); + + generator.Emit(OpCodes.Stloc, local); + } + + generator.Emit(OpCodes.Ldloca, local); + } + else + { + generator.Emit(OpCodes.Ldarg_1); + generator.Emit(OpCodes.Ldc_I4, registerAttribute.Index); + + MethodInfo info = typeof(ExecutionContext).GetMethod(nameof(ExecutionContext.GetX)); + + generator.Emit(OpCodes.Call, info); + + ConvertToArgType(argType); + } + } + + generator.Emit(OpCodes.Call, methodInfo); + + int outRegIndex = 0; + + Type retType = methodInfo.ReturnType; + + // Print result code. + if (retType == typeof(KernelResult)) + { + MethodInfo printResultMethod = typeof(SvcTable).GetMethod(nameof(PrintResult), staticNonPublic); + + generator.Emit(OpCodes.Dup); + generator.Emit(OpCodes.Ldstr, svcName); + generator.Emit(OpCodes.Call, printResultMethod); + } + + // Save return value into register X0 (when the method has a return value). + if (retType != typeof(void)) + { + CheckIfTypeIsSupported(retType, svcName); + + LocalBuilder tempLocal = generator.DeclareLocal(retType); + + generator.Emit(OpCodes.Stloc, tempLocal); + generator.Emit(OpCodes.Ldarg_1); + generator.Emit(OpCodes.Ldc_I4, outRegIndex++); + generator.Emit(OpCodes.Ldloc, tempLocal); + + ConvertToFieldType(retType); + + MethodInfo info = typeof(ExecutionContext).GetMethod(nameof(ExecutionContext.SetX)); + + generator.Emit(OpCodes.Call, info); + } + + for (int index = 0; index < locals.Count; index++) + { + (LocalBuilder local, RAttribute attribute) = locals[index]; + generator.Emit(OpCodes.Ldarg_1); + generator.Emit(OpCodes.Ldc_I4, attribute.Index); + generator.Emit(OpCodes.Ldloc, local); + + ConvertToFieldType(local.LocalType); + + MethodInfo info = typeof(ExecutionContext).GetMethod(nameof(ExecutionContext.SetX)); + + generator.Emit(OpCodes.Call, info); + } + + bool IsRegisterInUse(int registerIndex) + { + for (int index = 0; index < locals.Count; index++) + { + if (registerIndex == locals[index].Item2.Index) + { + return true; + } + } + + return false; + } + + // Zero out the remaining unused registers. + for (int i = 0; i < SvcFuncMaxArguments32; i++) + { + if (IsRegisterInUse(i)) + { + continue; + } + + generator.Emit(OpCodes.Ldarg_1); + generator.Emit(OpCodes.Ldc_I4, i); + generator.Emit(OpCodes.Ldc_I8, 0L); + + MethodInfo info = typeof(ExecutionContext).GetMethod(nameof(ExecutionContext.SetX)); + + generator.Emit(OpCodes.Call, info); + } + + generator.Emit(OpCodes.Ret); + + return (Action)method.CreateDelegate(typeof(Action)); + } + private static void CheckIfTypeIsSupported(Type type, string svcName) { switch (Type.GetTypeCode(type)) diff --git a/Ryujinx.HLE/HOS/Kernel/SupervisorCall/SvcThread.cs b/Ryujinx.HLE/HOS/Kernel/SupervisorCall/SvcThread.cs index 6c8f6ba739..1ef5fa2521 100644 --- a/Ryujinx.HLE/HOS/Kernel/SupervisorCall/SvcThread.cs +++ b/Ryujinx.HLE/HOS/Kernel/SupervisorCall/SvcThread.cs @@ -20,13 +20,13 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall } public KernelResult CreateThread32( - ulong entrypoint, - ulong argsPtr, - ulong stackTop, - int cpuCore, - int priority, - - out int handle) + [R(1)] uint entrypoint, + [R(2)] uint argsPtr, + [R(3)] uint stackTop, + [R(0)] int priority, + [R(4)] int cpuCore, + + [R(1)] out int handle) { return CreateThread(entrypoint, argsPtr, stackTop, priority, cpuCore, out handle); } @@ -95,6 +95,11 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall return StartThread(handle); } + public KernelResult StartThread32([R(0)] int handle) + { + return StartThread(handle); + } + private KernelResult StartThread(int handle) { KThread thread = _process.HandleTable.GetKThread(handle); @@ -125,6 +130,11 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall ExitThread(); } + public void ExitThread32() + { + ExitThread(); + } + private void ExitThread() { KThread currentThread = _system.Scheduler.GetCurrentThread(); @@ -139,6 +149,13 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall SleepThread(timeout); } + public void SleepThread32([R(0)] uint timeoutLow, [R(1)] uint timeoutHigh) + { + long timeout = (long)(timeoutLow | ((ulong)timeoutHigh << 32)); + + SleepThread(timeout); + } + private void SleepThread(long timeout) { KThread currentThread = _system.Scheduler.GetCurrentThread(); @@ -163,6 +180,11 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall return GetThreadPriority(handle, out priority); } + public KernelResult GetThreadPriority32([R(1)] int handle, [R(1)] out int priority) + { + return GetThreadPriority(handle, out priority); + } + private KernelResult GetThreadPriority(int handle, out int priority) { KThread thread = _process.HandleTable.GetKThread(handle); @@ -186,6 +208,11 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall return SetThreadPriority(handle, priority); } + public KernelResult SetThreadPriority32([R(0)] int handle, [R(1)] int priority) + { + return SetThreadPriority(handle, priority); + } + public KernelResult SetThreadPriority(int handle, int priority) { // TODO: NPDM check. @@ -232,6 +259,13 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall return SetThreadCoreMask(handle, preferredCore, affinityMask); } + public KernelResult SetThreadCoreMask32([R(0)] int handle, [R(1)] int preferredCore, [R(2)] uint affinityMaskLow, [R(3)] uint affinityMaskHigh) + { + long affinityMask = (long)(affinityMaskLow | ((ulong)affinityMaskHigh << 32)); + + return SetThreadCoreMask(handle, preferredCore, affinityMask); + } + private KernelResult SetThreadCoreMask(int handle, int preferredCore, long affinityMask) { KProcess currentProcess = _system.Scheduler.GetCurrentProcess(); @@ -283,11 +317,28 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall return _system.Scheduler.GetCurrentThread().CurrentCore; } + public int GetCurrentProcessorNumber32() + { + return _system.Scheduler.GetCurrentThread().CurrentCore; + } + public KernelResult GetThreadId64(int handle, out long threadUid) { return GetThreadId(handle, out threadUid); } + public KernelResult GetThreadId32([R(1)] int handle, [R(1)] out uint threadUidLow, [R(2)] out uint threadUidHigh) + { + long threadUid; + + KernelResult result = GetThreadId(handle, out threadUid); + + threadUidLow = (uint)(threadUid >> 32); + threadUidHigh = (uint)(threadUid & 0xFFFFFFFF); + + return result; + } + private KernelResult GetThreadId(int handle, out long threadUid) { KThread thread = _process.HandleTable.GetKThread(handle); diff --git a/Ryujinx.HLE/HOS/Kernel/SupervisorCall/SvcThreadSync.cs b/Ryujinx.HLE/HOS/Kernel/SupervisorCall/SvcThreadSync.cs index 6e5b478251..02d6d24772 100644 --- a/Ryujinx.HLE/HOS/Kernel/SupervisorCall/SvcThreadSync.cs +++ b/Ryujinx.HLE/HOS/Kernel/SupervisorCall/SvcThreadSync.cs @@ -12,6 +12,18 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall return WaitSynchronization(handlesPtr, handlesCount, timeout, out handleIndex); } + public KernelResult WaitSynchronization32( + [R(0)] uint timeoutLow, + [R(1)] uint handlesPtr, + [R(2)] int handlesCount, + [R(3)] uint timeoutHigh, + [R(1)] out int handleIndex) + { + long timeout = (long)(timeoutLow | ((ulong)timeoutHigh << 32)); + + return WaitSynchronization(handlesPtr, handlesCount, timeout, out handleIndex); + } + private KernelResult WaitSynchronization(ulong handlesPtr, int handlesCount, long timeout, out int handleIndex) { handleIndex = 0; @@ -142,6 +154,11 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall return SignalProcessWideKey(address, count); } + public KernelResult SignalProcessWideKey32([R(0)] uint address, [R(1)] int count) + { + return SignalProcessWideKey(address, count); + } + private KernelResult SignalProcessWideKey(ulong address, int count) { KProcess currentProcess = _system.Scheduler.GetCurrentProcess();