svc: Rework 32 bit codepath
Fixing once and for all argument ordering issues.
This commit is contained in:
parent
afe6f87c08
commit
30b067e9f0
7 changed files with 481 additions and 28 deletions
14
Ryujinx.HLE/HOS/Kernel/SupervisorCall/RAttribute.cs
Normal file
14
Ryujinx.HLE/HOS/Kernel/SupervisorCall/RAttribute.cs
Normal file
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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<KAutoObject>(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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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<int, string> _svcFuncs64;
|
||||
|
@ -84,10 +85,30 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
|
|||
|
||||
_svcFuncs32 = new Dictionary<int, string>
|
||||
{
|
||||
{ 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<SvcHandler, ExecutionContext>[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<SvcHandler, ExecutionContext> 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<SvcHandler, ExecutionContext> GenerateMethod(string svcName, bool aarch32)
|
||||
private static Action<SvcHandler, ExecutionContext> 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<SvcHandler, ExecutionContext>)method.CreateDelegate(typeof(Action<SvcHandler, ExecutionContext>));
|
||||
}
|
||||
|
||||
// TODO: merge 64 bits variant
|
||||
private static Action<SvcHandler, ExecutionContext> 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<SvcHandler, ExecutionContext>)method.CreateDelegate(typeof(Action<SvcHandler, ExecutionContext>));
|
||||
}
|
||||
|
||||
private static void CheckIfTypeIsSupported(Type type, string svcName)
|
||||
{
|
||||
switch (Type.GetTypeCode(type))
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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();
|
||||
|
|
Loading…
Add table
Reference in a new issue