forked from Mirror/Ryujinx
Stub a few services, add support for generating call stacks on the CPU
This commit is contained in:
parent
4906acdde9
commit
bd9b1e2c6b
20 changed files with 267 additions and 41 deletions
|
@ -1,4 +1,6 @@
|
||||||
public static class AOptimizations
|
public static class AOptimizations
|
||||||
{
|
{
|
||||||
public static bool DisableMemoryChecks = false;
|
public static bool DisableMemoryChecks = false;
|
||||||
|
|
||||||
|
public static bool GenerateCallStack = true;
|
||||||
}
|
}
|
|
@ -8,8 +8,6 @@ namespace ChocolArm64.Instruction
|
||||||
{
|
{
|
||||||
static partial class AInstEmit
|
static partial class AInstEmit
|
||||||
{
|
{
|
||||||
private const BindingFlags Binding = BindingFlags.NonPublic | BindingFlags.Instance;
|
|
||||||
|
|
||||||
public static void Brk(AILEmitterCtx Context)
|
public static void Brk(AILEmitterCtx Context)
|
||||||
{
|
{
|
||||||
EmitExceptionCall(Context, nameof(AThreadState.OnBreak));
|
EmitExceptionCall(Context, nameof(AThreadState.OnBreak));
|
||||||
|
@ -30,9 +28,7 @@ namespace ChocolArm64.Instruction
|
||||||
|
|
||||||
Context.EmitLdc_I4(Op.Id);
|
Context.EmitLdc_I4(Op.Id);
|
||||||
|
|
||||||
MethodInfo MthdInfo = typeof(AThreadState).GetMethod(MthdName, Binding);
|
Context.EmitPrivateCall(typeof(AThreadState), MthdName);
|
||||||
|
|
||||||
Context.EmitCall(MthdInfo);
|
|
||||||
|
|
||||||
//Check if the thread should still be running, if it isn't then we return 0
|
//Check if the thread should still be running, if it isn't then we return 0
|
||||||
//to force a return to the dispatcher and then exit the thread.
|
//to force a return to the dispatcher and then exit the thread.
|
||||||
|
@ -73,11 +69,7 @@ namespace ChocolArm64.Instruction
|
||||||
Context.EmitLdc_I8(Op.Position);
|
Context.EmitLdc_I8(Op.Position);
|
||||||
Context.EmitLdc_I4(Op.RawOpCode);
|
Context.EmitLdc_I4(Op.RawOpCode);
|
||||||
|
|
||||||
string MthdName = nameof(AThreadState.OnUndefined);
|
Context.EmitPrivateCall(typeof(AThreadState), nameof(AThreadState.OnUndefined));
|
||||||
|
|
||||||
MethodInfo MthdInfo = typeof(AThreadState).GetMethod(MthdName, Binding);
|
|
||||||
|
|
||||||
Context.EmitCall(MthdInfo);
|
|
||||||
|
|
||||||
if (Context.CurrBlock.Next != null)
|
if (Context.CurrBlock.Next != null)
|
||||||
{
|
{
|
||||||
|
|
|
@ -35,6 +35,14 @@ namespace ChocolArm64.Instruction
|
||||||
{
|
{
|
||||||
AOpCodeBImmAl Op = (AOpCodeBImmAl)Context.CurrOp;
|
AOpCodeBImmAl Op = (AOpCodeBImmAl)Context.CurrOp;
|
||||||
|
|
||||||
|
if (AOptimizations.GenerateCallStack)
|
||||||
|
{
|
||||||
|
Context.EmitLdarg(ATranslatedSub.StateArgIdx);
|
||||||
|
Context.EmitLdc_I8(Op.Imm);
|
||||||
|
|
||||||
|
Context.EmitPrivateCall(typeof(AThreadState), nameof(AThreadState.EnterMethod));
|
||||||
|
}
|
||||||
|
|
||||||
Context.EmitLdc_I(Op.Position + 4);
|
Context.EmitLdc_I(Op.Position + 4);
|
||||||
Context.EmitStint(AThreadState.LRIndex);
|
Context.EmitStint(AThreadState.LRIndex);
|
||||||
Context.EmitStoreState();
|
Context.EmitStoreState();
|
||||||
|
@ -72,6 +80,14 @@ namespace ChocolArm64.Instruction
|
||||||
{
|
{
|
||||||
AOpCodeBReg Op = (AOpCodeBReg)Context.CurrOp;
|
AOpCodeBReg Op = (AOpCodeBReg)Context.CurrOp;
|
||||||
|
|
||||||
|
if (AOptimizations.GenerateCallStack)
|
||||||
|
{
|
||||||
|
Context.EmitLdarg(ATranslatedSub.StateArgIdx);
|
||||||
|
Context.EmitLdintzr(Op.Rn);
|
||||||
|
|
||||||
|
Context.EmitPrivateCall(typeof(AThreadState), nameof(AThreadState.EnterMethod));
|
||||||
|
}
|
||||||
|
|
||||||
Context.EmitLdc_I(Op.Position + 4);
|
Context.EmitLdc_I(Op.Position + 4);
|
||||||
Context.EmitStint(AThreadState.LRIndex);
|
Context.EmitStint(AThreadState.LRIndex);
|
||||||
Context.EmitStoreState();
|
Context.EmitStoreState();
|
||||||
|
@ -84,6 +100,14 @@ namespace ChocolArm64.Instruction
|
||||||
{
|
{
|
||||||
AOpCodeBReg Op = (AOpCodeBReg)Context.CurrOp;
|
AOpCodeBReg Op = (AOpCodeBReg)Context.CurrOp;
|
||||||
|
|
||||||
|
if (AOptimizations.GenerateCallStack)
|
||||||
|
{
|
||||||
|
Context.EmitLdarg(ATranslatedSub.StateArgIdx);
|
||||||
|
Context.EmitLdintzr(Op.Rn);
|
||||||
|
|
||||||
|
Context.EmitPrivateCall(typeof(AThreadState), nameof(AThreadState.JumpMethod));
|
||||||
|
}
|
||||||
|
|
||||||
Context.EmitStoreState();
|
Context.EmitStoreState();
|
||||||
Context.EmitLdintzr(Op.Rn);
|
Context.EmitLdintzr(Op.Rn);
|
||||||
|
|
||||||
|
@ -105,6 +129,13 @@ namespace ChocolArm64.Instruction
|
||||||
|
|
||||||
public static void Ret(AILEmitterCtx Context)
|
public static void Ret(AILEmitterCtx Context)
|
||||||
{
|
{
|
||||||
|
if (AOptimizations.GenerateCallStack)
|
||||||
|
{
|
||||||
|
Context.EmitLdarg(ATranslatedSub.StateArgIdx);
|
||||||
|
|
||||||
|
Context.EmitPrivateCall(typeof(AThreadState), nameof(AThreadState.ExitMethod));
|
||||||
|
}
|
||||||
|
|
||||||
Context.EmitStoreState();
|
Context.EmitStoreState();
|
||||||
Context.EmitLdint(AThreadState.LRIndex);
|
Context.EmitLdint(AThreadState.LRIndex);
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
using ChocolArm64.Events;
|
using ChocolArm64.Events;
|
||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
|
|
||||||
namespace ChocolArm64.State
|
namespace ChocolArm64.State
|
||||||
|
@ -56,10 +57,17 @@ namespace ChocolArm64.State
|
||||||
public event EventHandler<AInstExceptionEventArgs> SvcCall;
|
public event EventHandler<AInstExceptionEventArgs> SvcCall;
|
||||||
public event EventHandler<AInstUndefinedEventArgs> Undefined;
|
public event EventHandler<AInstUndefinedEventArgs> Undefined;
|
||||||
|
|
||||||
|
private Stack<long> CallStack;
|
||||||
|
|
||||||
private static Stopwatch TickCounter;
|
private static Stopwatch TickCounter;
|
||||||
|
|
||||||
private static double HostTickFreq;
|
private static double HostTickFreq;
|
||||||
|
|
||||||
|
public AThreadState()
|
||||||
|
{
|
||||||
|
CallStack = new Stack<long>();
|
||||||
|
}
|
||||||
|
|
||||||
static AThreadState()
|
static AThreadState()
|
||||||
{
|
{
|
||||||
HostTickFreq = 1.0 / Stopwatch.Frequency;
|
HostTickFreq = 1.0 / Stopwatch.Frequency;
|
||||||
|
@ -83,5 +91,27 @@ namespace ChocolArm64.State
|
||||||
{
|
{
|
||||||
Undefined?.Invoke(this, new AInstUndefinedEventArgs(Position, RawOpCode));
|
Undefined?.Invoke(this, new AInstUndefinedEventArgs(Position, RawOpCode));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal void EnterMethod(long Position)
|
||||||
|
{
|
||||||
|
CallStack.Push(Position);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void ExitMethod()
|
||||||
|
{
|
||||||
|
CallStack.TryPop(out _);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void JumpMethod(long Position)
|
||||||
|
{
|
||||||
|
CallStack.TryPop(out _);
|
||||||
|
|
||||||
|
CallStack.Push(Position);
|
||||||
|
}
|
||||||
|
|
||||||
|
public long[] GetCallStack()
|
||||||
|
{
|
||||||
|
return CallStack.ToArray();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -461,6 +461,21 @@ namespace ChocolArm64.Translation
|
||||||
EmitCall(ObjType.GetMethod(MthdName));
|
EmitCall(ObjType.GetMethod(MthdName));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void EmitPrivateCall(Type ObjType, string MthdName)
|
||||||
|
{
|
||||||
|
if (ObjType == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(ObjType));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (MthdName == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(MthdName));
|
||||||
|
}
|
||||||
|
|
||||||
|
EmitCall(ObjType.GetMethod(MthdName, BindingFlags.Instance | BindingFlags.NonPublic));
|
||||||
|
}
|
||||||
|
|
||||||
public void EmitCall(MethodInfo MthdInfo)
|
public void EmitCall(MethodInfo MthdInfo)
|
||||||
{
|
{
|
||||||
if (MthdInfo == null)
|
if (MthdInfo == null)
|
||||||
|
|
|
@ -7,14 +7,16 @@ namespace Ryujinx.Core.Loaders
|
||||||
{
|
{
|
||||||
class Executable
|
class Executable
|
||||||
{
|
{
|
||||||
private AMemory Memory;
|
|
||||||
|
|
||||||
private List<ElfDyn> Dynamic;
|
private List<ElfDyn> Dynamic;
|
||||||
|
|
||||||
private Dictionary<long, string> m_SymbolTable;
|
private Dictionary<long, string> m_SymbolTable;
|
||||||
|
|
||||||
public IReadOnlyDictionary<long, string> SymbolTable => m_SymbolTable;
|
public IReadOnlyDictionary<long, string> SymbolTable => m_SymbolTable;
|
||||||
|
|
||||||
|
public string Name { get; private set; }
|
||||||
|
|
||||||
|
private AMemory Memory;
|
||||||
|
|
||||||
public long ImageBase { get; private set; }
|
public long ImageBase { get; private set; }
|
||||||
public long ImageEnd { get; private set; }
|
public long ImageEnd { get; private set; }
|
||||||
|
|
||||||
|
@ -24,6 +26,8 @@ namespace Ryujinx.Core.Loaders
|
||||||
|
|
||||||
m_SymbolTable = new Dictionary<long, string>();
|
m_SymbolTable = new Dictionary<long, string>();
|
||||||
|
|
||||||
|
Name = Exe.Name;
|
||||||
|
|
||||||
this.Memory = Memory;
|
this.Memory = Memory;
|
||||||
this.ImageBase = ImageBase;
|
this.ImageBase = ImageBase;
|
||||||
this.ImageEnd = ImageBase;
|
this.ImageEnd = ImageBase;
|
||||||
|
|
|
@ -2,6 +2,8 @@ namespace Ryujinx.Core.Loaders.Executables
|
||||||
{
|
{
|
||||||
public interface IExecutable
|
public interface IExecutable
|
||||||
{
|
{
|
||||||
|
string Name { get; }
|
||||||
|
|
||||||
byte[] Text { get; }
|
byte[] Text { get; }
|
||||||
byte[] RO { get; }
|
byte[] RO { get; }
|
||||||
byte[] Data { get; }
|
byte[] Data { get; }
|
||||||
|
|
|
@ -4,6 +4,8 @@ namespace Ryujinx.Core.Loaders.Executables
|
||||||
{
|
{
|
||||||
class Nro : IExecutable
|
class Nro : IExecutable
|
||||||
{
|
{
|
||||||
|
public string Name { get; private set; }
|
||||||
|
|
||||||
public byte[] Text { get; private set; }
|
public byte[] Text { get; private set; }
|
||||||
public byte[] RO { get; private set; }
|
public byte[] RO { get; private set; }
|
||||||
public byte[] Data { get; private set; }
|
public byte[] Data { get; private set; }
|
||||||
|
@ -14,8 +16,10 @@ namespace Ryujinx.Core.Loaders.Executables
|
||||||
public int DataOffset { get; private set; }
|
public int DataOffset { get; private set; }
|
||||||
public int BssSize { get; private set; }
|
public int BssSize { get; private set; }
|
||||||
|
|
||||||
public Nro(Stream Input)
|
public Nro(Stream Input, string Name)
|
||||||
{
|
{
|
||||||
|
this.Name = Name;
|
||||||
|
|
||||||
BinaryReader Reader = new BinaryReader(Input);
|
BinaryReader Reader = new BinaryReader(Input);
|
||||||
|
|
||||||
Input.Seek(4, SeekOrigin.Begin);
|
Input.Seek(4, SeekOrigin.Begin);
|
||||||
|
|
|
@ -6,6 +6,8 @@ namespace Ryujinx.Core.Loaders.Executables
|
||||||
{
|
{
|
||||||
class Nso : IExecutable
|
class Nso : IExecutable
|
||||||
{
|
{
|
||||||
|
public string Name { get; private set; }
|
||||||
|
|
||||||
public byte[] Text { get; private set; }
|
public byte[] Text { get; private set; }
|
||||||
public byte[] RO { get; private set; }
|
public byte[] RO { get; private set; }
|
||||||
public byte[] Data { get; private set; }
|
public byte[] Data { get; private set; }
|
||||||
|
@ -27,8 +29,10 @@ namespace Ryujinx.Core.Loaders.Executables
|
||||||
HasDataHash = 1 << 5
|
HasDataHash = 1 << 5
|
||||||
}
|
}
|
||||||
|
|
||||||
public Nso(Stream Input)
|
public Nso(Stream Input, string Name)
|
||||||
{
|
{
|
||||||
|
this.Name = Name;
|
||||||
|
|
||||||
BinaryReader Reader = new BinaryReader(Input);
|
BinaryReader Reader = new BinaryReader(Input);
|
||||||
|
|
||||||
Input.Seek(0, SeekOrigin.Begin);
|
Input.Seek(0, SeekOrigin.Begin);
|
||||||
|
|
|
@ -16,6 +16,7 @@
|
||||||
ServiceApm,
|
ServiceApm,
|
||||||
ServiceAudio,
|
ServiceAudio,
|
||||||
ServiceBsd,
|
ServiceBsd,
|
||||||
|
ServiceCaps,
|
||||||
ServiceFriend,
|
ServiceFriend,
|
||||||
ServiceFs,
|
ServiceFs,
|
||||||
ServiceHid,
|
ServiceHid,
|
||||||
|
|
|
@ -58,7 +58,9 @@ namespace Ryujinx.Core.OsHle
|
||||||
|
|
||||||
using (FileStream Input = new FileStream(File, FileMode.Open))
|
using (FileStream Input = new FileStream(File, FileMode.Open))
|
||||||
{
|
{
|
||||||
Nso Program = new Nso(Input);
|
string Name = Path.GetFileNameWithoutExtension(File);
|
||||||
|
|
||||||
|
Nso Program = new Nso(Input, Name);
|
||||||
|
|
||||||
MainProcess.LoadProgram(Program);
|
MainProcess.LoadProgram(Program);
|
||||||
}
|
}
|
||||||
|
@ -80,13 +82,15 @@ namespace Ryujinx.Core.OsHle
|
||||||
{
|
{
|
||||||
bool IsNro = Path.GetExtension(FileName).ToLower() == ".nro";
|
bool IsNro = Path.GetExtension(FileName).ToLower() == ".nro";
|
||||||
|
|
||||||
|
string Name = Path.GetFileNameWithoutExtension(FileName);
|
||||||
|
|
||||||
Process MainProcess = MakeProcess();
|
Process MainProcess = MakeProcess();
|
||||||
|
|
||||||
using (FileStream Input = new FileStream(FileName, FileMode.Open))
|
using (FileStream Input = new FileStream(FileName, FileMode.Open))
|
||||||
{
|
{
|
||||||
MainProcess.LoadProgram(IsNro
|
MainProcess.LoadProgram(IsNro
|
||||||
? (IExecutable)new Nro(Input)
|
? (IExecutable)new Nro(Input, Name)
|
||||||
: (IExecutable)new Nso(Input));
|
: (IExecutable)new Nso(Input, Name));
|
||||||
}
|
}
|
||||||
|
|
||||||
MainProcess.SetEmptyArgs();
|
MainProcess.SetEmptyArgs();
|
||||||
|
|
|
@ -156,13 +156,13 @@ namespace Ryujinx.Core.OsHle.Kernel
|
||||||
private void SvcSetThreadActivity(AThreadState ThreadState)
|
private void SvcSetThreadActivity(AThreadState ThreadState)
|
||||||
{
|
{
|
||||||
int Handle = (int)ThreadState.X0;
|
int Handle = (int)ThreadState.X0;
|
||||||
bool Active = (int)ThreadState.X1 != 0;
|
bool Active = (int)ThreadState.X1 == 0;
|
||||||
|
|
||||||
KThread CurrThread = Process.HandleTable.GetData<KThread>(Handle);
|
KThread Thread = Process.HandleTable.GetData<KThread>(Handle);
|
||||||
|
|
||||||
if (CurrThread != null)
|
if (Thread != null)
|
||||||
{
|
{
|
||||||
Process.Scheduler.SetThreadActivity(CurrThread, Active);
|
Process.Scheduler.SetThreadActivity(Thread, Active);
|
||||||
|
|
||||||
ThreadState.X0 = 0;
|
ThreadState.X0 = 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
using ChocolArm64;
|
using ChocolArm64;
|
||||||
using ChocolArm64.Events;
|
using ChocolArm64.Events;
|
||||||
using ChocolArm64.Memory;
|
using ChocolArm64.Memory;
|
||||||
|
using ChocolArm64.State;
|
||||||
using Ryujinx.Core.Loaders;
|
using Ryujinx.Core.Loaders;
|
||||||
using Ryujinx.Core.Loaders.Executables;
|
using Ryujinx.Core.Loaders.Executables;
|
||||||
using Ryujinx.Core.OsHle.Exceptions;
|
using Ryujinx.Core.OsHle.Exceptions;
|
||||||
|
@ -10,6 +11,7 @@ using Ryujinx.Core.OsHle.Services.Nv;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Concurrent;
|
using System.Collections.Concurrent;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
namespace Ryujinx.Core.OsHle
|
namespace Ryujinx.Core.OsHle
|
||||||
{
|
{
|
||||||
|
@ -47,9 +49,11 @@ namespace Ryujinx.Core.OsHle
|
||||||
|
|
||||||
private ConcurrentDictionary<long, KThread> Threads;
|
private ConcurrentDictionary<long, KThread> Threads;
|
||||||
|
|
||||||
|
private KThread MainThread;
|
||||||
|
|
||||||
private List<Executable> Executables;
|
private List<Executable> Executables;
|
||||||
|
|
||||||
private KThread MainThread;
|
private Dictionary<long, string> SymbolTable;
|
||||||
|
|
||||||
private long ImageBase;
|
private long ImageBase;
|
||||||
|
|
||||||
|
@ -121,6 +125,8 @@ namespace Ryujinx.Core.OsHle
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
MakeSymbolTable();
|
||||||
|
|
||||||
MapRWMemRegion(
|
MapRWMemRegion(
|
||||||
MemoryRegions.MainStackAddress,
|
MemoryRegions.MainStackAddress,
|
||||||
MemoryRegions.MainStackSize,
|
MemoryRegions.MainStackSize,
|
||||||
|
@ -227,11 +233,9 @@ namespace Ryujinx.Core.OsHle
|
||||||
throw new UndefinedInstructionException(e.Position, e.RawOpCode);
|
throw new UndefinedInstructionException(e.Position, e.RawOpCode);
|
||||||
}
|
}
|
||||||
|
|
||||||
private ATranslator GetTranslator()
|
private void MakeSymbolTable()
|
||||||
{
|
{
|
||||||
if (Translator == null)
|
SymbolTable = new Dictionary<long, string>();
|
||||||
{
|
|
||||||
Dictionary<long, string> SymbolTable = new Dictionary<long, string>();
|
|
||||||
|
|
||||||
foreach (Executable Exe in Executables)
|
foreach (Executable Exe in Executables)
|
||||||
{
|
{
|
||||||
|
@ -240,7 +244,12 @@ namespace Ryujinx.Core.OsHle
|
||||||
SymbolTable.TryAdd(Exe.ImageBase + KV.Key, KV.Value);
|
SymbolTable.TryAdd(Exe.ImageBase + KV.Key, KV.Value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private ATranslator GetTranslator()
|
||||||
|
{
|
||||||
|
if (Translator == null)
|
||||||
|
{
|
||||||
Translator = new ATranslator(SymbolTable);
|
Translator = new ATranslator(SymbolTable);
|
||||||
|
|
||||||
Translator.CpuTrace += CpuTraceHandler;
|
Translator.CpuTrace += CpuTraceHandler;
|
||||||
|
@ -249,6 +258,16 @@ namespace Ryujinx.Core.OsHle
|
||||||
return Translator;
|
return Translator;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void EnableCpuTracing()
|
||||||
|
{
|
||||||
|
Translator.EnableCpuTrace = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void DisableCpuTracing()
|
||||||
|
{
|
||||||
|
Translator.EnableCpuTrace = false;
|
||||||
|
}
|
||||||
|
|
||||||
private void CpuTraceHandler(object sender, ACpuTraceEventArgs e)
|
private void CpuTraceHandler(object sender, ACpuTraceEventArgs e)
|
||||||
{
|
{
|
||||||
string NsoName = string.Empty;
|
string NsoName = string.Empty;
|
||||||
|
@ -263,17 +282,47 @@ namespace Ryujinx.Core.OsHle
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Logging.Trace(LogClass.Loader, $"Executing at 0x{e.Position:x16} {e.SubName} {NsoName}");
|
Logging.Trace(LogClass.CPU, $"Executing at 0x{e.Position:x16} {e.SubName} {NsoName}");
|
||||||
}
|
}
|
||||||
|
|
||||||
public void EnableCpuTracing()
|
public void PrintStackTrace(AThreadState ThreadState)
|
||||||
{
|
{
|
||||||
Translator.EnableCpuTrace = true;
|
long[] Positions = ThreadState.GetCallStack();
|
||||||
|
|
||||||
|
StringBuilder Trace = new StringBuilder();
|
||||||
|
|
||||||
|
Trace.AppendLine("Guest stack trace:");
|
||||||
|
|
||||||
|
foreach (long Position in Positions)
|
||||||
|
{
|
||||||
|
if (!SymbolTable.TryGetValue(Position, out string SubName))
|
||||||
|
{
|
||||||
|
SubName = $"Sub{Position:x16}";
|
||||||
}
|
}
|
||||||
|
|
||||||
public void DisableCpuTracing()
|
Trace.AppendLine(" " + SubName + " (" + GetNsoNameAndAddress(Position) + ")");
|
||||||
|
}
|
||||||
|
|
||||||
|
Logging.Trace(LogClass.CPU, Trace.ToString());
|
||||||
|
}
|
||||||
|
|
||||||
|
private string GetNsoNameAndAddress(long Position)
|
||||||
{
|
{
|
||||||
Translator.EnableCpuTrace = false;
|
string Name = string.Empty;
|
||||||
|
|
||||||
|
for (int Index = Executables.Count - 1; Index >= 0; Index--)
|
||||||
|
{
|
||||||
|
if (Position >= Executables[Index].ImageBase)
|
||||||
|
{
|
||||||
|
long Offset = Position - Executables[Index].ImageBase;
|
||||||
|
|
||||||
|
Name = $"{Executables[Index].Name}:{Offset:x8}";
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return Name;
|
||||||
}
|
}
|
||||||
|
|
||||||
private int GetFreeTlsSlot(AThread Thread)
|
private int GetFreeTlsSlot(AThread Thread)
|
||||||
|
|
|
@ -18,6 +18,7 @@ namespace Ryujinx.Core.OsHle.Services.Am
|
||||||
{ 20, EnsureSaveData },
|
{ 20, EnsureSaveData },
|
||||||
{ 21, GetDesiredLanguage },
|
{ 21, GetDesiredLanguage },
|
||||||
{ 22, SetTerminateResult },
|
{ 22, SetTerminateResult },
|
||||||
|
{ 23, GetDisplayVersion },
|
||||||
{ 40, NotifyRunning }
|
{ 40, NotifyRunning }
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -67,6 +68,15 @@ namespace Ryujinx.Core.OsHle.Services.Am
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public long GetDisplayVersion(ServiceCtx Context)
|
||||||
|
{
|
||||||
|
//FIXME: Need to check correct version on a switch.
|
||||||
|
Context.ResponseData.Write(1L);
|
||||||
|
Context.ResponseData.Write(0L);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
public long NotifyRunning(ServiceCtx Context)
|
public long NotifyRunning(ServiceCtx Context)
|
||||||
{
|
{
|
||||||
Context.ResponseData.Write(1);
|
Context.ResponseData.Write(1);
|
||||||
|
|
|
@ -6,7 +6,7 @@ using System.Text;
|
||||||
|
|
||||||
namespace Ryujinx.Core.OsHle.Services.Aud
|
namespace Ryujinx.Core.OsHle.Services.Aud
|
||||||
{
|
{
|
||||||
class IAudioDeviceService : IpcService
|
class IAudioDevice : IpcService
|
||||||
{
|
{
|
||||||
private Dictionary<int, ServiceProcessRequest> m_Commands;
|
private Dictionary<int, ServiceProcessRequest> m_Commands;
|
||||||
|
|
||||||
|
@ -14,12 +14,13 @@ namespace Ryujinx.Core.OsHle.Services.Aud
|
||||||
|
|
||||||
private KEvent SystemEvent;
|
private KEvent SystemEvent;
|
||||||
|
|
||||||
public IAudioDeviceService()
|
public IAudioDevice()
|
||||||
{
|
{
|
||||||
m_Commands = new Dictionary<int, ServiceProcessRequest>()
|
m_Commands = new Dictionary<int, ServiceProcessRequest>()
|
||||||
{
|
{
|
||||||
{ 0, ListAudioDeviceName },
|
{ 0, ListAudioDeviceName },
|
||||||
{ 1, SetAudioDeviceOutputVolume },
|
{ 1, SetAudioDeviceOutputVolume },
|
||||||
|
{ 3, GetActiveAudioDeviceName },
|
||||||
{ 4, QueryAudioDeviceSystemEvent },
|
{ 4, QueryAudioDeviceSystemEvent },
|
||||||
{ 5, GetActiveChannelCount }
|
{ 5, GetActiveChannelCount }
|
||||||
};
|
};
|
||||||
|
@ -72,6 +73,20 @@ namespace Ryujinx.Core.OsHle.Services.Aud
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public long GetActiveAudioDeviceName(ServiceCtx Context)
|
||||||
|
{
|
||||||
|
string Name = "FIXME";
|
||||||
|
|
||||||
|
long Position = Context.Request.ReceiveBuff[0].Position;
|
||||||
|
long Size = Context.Request.ReceiveBuff[0].Size;
|
||||||
|
|
||||||
|
byte[] Buffer = Encoding.ASCII.GetBytes(Name + '\0');
|
||||||
|
|
||||||
|
AMemoryHelper.WriteBytes(Context.Memory, Position, Buffer);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
public long QueryAudioDeviceSystemEvent(ServiceCtx Context)
|
public long QueryAudioDeviceSystemEvent(ServiceCtx Context)
|
||||||
{
|
{
|
||||||
int Handle = Context.Process.HandleTable.OpenHandle(SystemEvent);
|
int Handle = Context.Process.HandleTable.OpenHandle(SystemEvent);
|
|
@ -53,7 +53,7 @@ namespace Ryujinx.Core.OsHle.Services.Aud
|
||||||
{
|
{
|
||||||
long UserId = Context.RequestData.ReadInt64();
|
long UserId = Context.RequestData.ReadInt64();
|
||||||
|
|
||||||
MakeObject(Context, new IAudioDeviceService());
|
MakeObject(Context, new IAudioDevice());
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
20
Ryujinx.Core/OsHle/Services/Caps/IAlbumAccessorService.cs
Normal file
20
Ryujinx.Core/OsHle/Services/Caps/IAlbumAccessorService.cs
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
using Ryujinx.Core.OsHle.Ipc;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace Ryujinx.Core.OsHle.Services.Caps
|
||||||
|
{
|
||||||
|
class IAlbumAccessorService : IpcService
|
||||||
|
{
|
||||||
|
private Dictionary<int, ServiceProcessRequest> m_Commands;
|
||||||
|
|
||||||
|
public override IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
|
||||||
|
|
||||||
|
public IAlbumAccessorService()
|
||||||
|
{
|
||||||
|
m_Commands = new Dictionary<int, ServiceProcessRequest>()
|
||||||
|
{
|
||||||
|
//...
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
20
Ryujinx.Core/OsHle/Services/Caps/IScreenshotService.cs
Normal file
20
Ryujinx.Core/OsHle/Services/Caps/IScreenshotService.cs
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
using Ryujinx.Core.OsHle.Ipc;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace Ryujinx.Core.OsHle.Services.Caps
|
||||||
|
{
|
||||||
|
class IScreenshotService : IpcService
|
||||||
|
{
|
||||||
|
private Dictionary<int, ServiceProcessRequest> m_Commands;
|
||||||
|
|
||||||
|
public override IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
|
||||||
|
|
||||||
|
public IScreenshotService()
|
||||||
|
{
|
||||||
|
m_Commands = new Dictionary<int, ServiceProcessRequest>()
|
||||||
|
{
|
||||||
|
//...
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -3,6 +3,7 @@ using Ryujinx.Core.OsHle.Services.Am;
|
||||||
using Ryujinx.Core.OsHle.Services.Apm;
|
using Ryujinx.Core.OsHle.Services.Apm;
|
||||||
using Ryujinx.Core.OsHle.Services.Aud;
|
using Ryujinx.Core.OsHle.Services.Aud;
|
||||||
using Ryujinx.Core.OsHle.Services.Bsd;
|
using Ryujinx.Core.OsHle.Services.Bsd;
|
||||||
|
using Ryujinx.Core.OsHle.Services.Caps;
|
||||||
using Ryujinx.Core.OsHle.Services.Friend;
|
using Ryujinx.Core.OsHle.Services.Friend;
|
||||||
using Ryujinx.Core.OsHle.Services.FspSrv;
|
using Ryujinx.Core.OsHle.Services.FspSrv;
|
||||||
using Ryujinx.Core.OsHle.Services.Hid;
|
using Ryujinx.Core.OsHle.Services.Hid;
|
||||||
|
@ -57,9 +58,18 @@ namespace Ryujinx.Core.OsHle.Services
|
||||||
case "bsd:u":
|
case "bsd:u":
|
||||||
return new IClient();
|
return new IClient();
|
||||||
|
|
||||||
|
case "caps:a":
|
||||||
|
return new IAlbumAccessorService();
|
||||||
|
|
||||||
|
case "caps:ss":
|
||||||
|
return new IScreenshotService();
|
||||||
|
|
||||||
case "friend:a":
|
case "friend:a":
|
||||||
return new IServiceCreator();
|
return new IServiceCreator();
|
||||||
|
|
||||||
|
case "friend:u":
|
||||||
|
return new IServiceCreator();
|
||||||
|
|
||||||
case "fsp-srv":
|
case "fsp-srv":
|
||||||
return new IFileSystemProxy();
|
return new IFileSystemProxy();
|
||||||
|
|
||||||
|
|
|
@ -16,10 +16,23 @@ namespace Ryujinx.Core.OsHle.Services.Time
|
||||||
{
|
{
|
||||||
m_Commands = new Dictionary<int, ServiceProcessRequest>()
|
m_Commands = new Dictionary<int, ServiceProcessRequest>()
|
||||||
{
|
{
|
||||||
|
{ 0, GetDeviceLocationName },
|
||||||
{ 101, ToCalendarTimeWithMyRule }
|
{ 101, ToCalendarTimeWithMyRule }
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public long GetDeviceLocationName(ServiceCtx Context)
|
||||||
|
{
|
||||||
|
Logging.Stub(LogClass.ServiceTime, "Stubbed");
|
||||||
|
|
||||||
|
for (int Index = 0; Index < 0x24; Index++)
|
||||||
|
{
|
||||||
|
Context.ResponseData.Write((byte)0);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
public long ToCalendarTimeWithMyRule(ServiceCtx Context)
|
public long ToCalendarTimeWithMyRule(ServiceCtx Context)
|
||||||
{
|
{
|
||||||
long PosixTime = Context.RequestData.ReadInt64();
|
long PosixTime = Context.RequestData.ReadInt64();
|
||||||
|
|
Loading…
Reference in a new issue