Merge pull request #11 from gdkchan/master

Working...
This commit is contained in:
Dr.Hacknik 2018-02-14 14:05:32 -05:00 committed by GitHub
commit 44895eff70
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
25 changed files with 864 additions and 281 deletions

View file

@ -191,7 +191,7 @@ void main(void) {
ScreenTex = new ScreenTexture(Ns, Renderer, 1280, 720); ScreenTex = new ScreenTexture(Ns, Renderer, 1280, 720);
} }
protected override void OnLoad (EventArgs e) protected override void OnLoad(EventArgs e)
{ {
VSync = VSyncMode.On; VSync = VSyncMode.On;

View file

@ -53,7 +53,7 @@ namespace Ryujinx
Screen.Run(60.0); Screen.Run(60.0);
} }
Ns.Os.StopAllProcesses(); Ns.Os.FinalizeAllProcesses();
Ns.Dispose(); Ns.Dispose();
} }

View file

@ -219,14 +219,17 @@ namespace ChocolArm64
Set("0x0011100x100001110110xxxxxxxxxx", AInstEmit.Scvtf_V, typeof(AOpCodeSimd)); Set("0x0011100x100001110110xxxxxxxxxx", AInstEmit.Scvtf_V, typeof(AOpCodeSimd));
Set("010111110>>>>xxx010101xxxxxxxxxx", AInstEmit.Shl_S, typeof(AOpCodeSimdShImm)); Set("010111110>>>>xxx010101xxxxxxxxxx", AInstEmit.Shl_S, typeof(AOpCodeSimdShImm));
Set("0x0011110>>>>xxx010101xxxxxxxxxx", AInstEmit.Shl_V, typeof(AOpCodeSimdShImm)); Set("0x0011110>>>>xxx010101xxxxxxxxxx", AInstEmit.Shl_V, typeof(AOpCodeSimdShImm));
Set("0x00111100>>>xxx100001xxxxxxxxxx", AInstEmit.Shrn_V, typeof(AOpCodeSimdShImm));
Set("0x001110<<1xxxxx011001xxxxxxxxxx", AInstEmit.Smax_V, typeof(AOpCodeSimdReg)); Set("0x001110<<1xxxxx011001xxxxxxxxxx", AInstEmit.Smax_V, typeof(AOpCodeSimdReg));
Set("0x001110<<1xxxxx011011xxxxxxxxxx", AInstEmit.Smin_V, typeof(AOpCodeSimdReg)); Set("0x001110<<1xxxxx011011xxxxxxxxxx", AInstEmit.Smin_V, typeof(AOpCodeSimdReg));
Set("0>001110<<1xxxxx010001xxxxxxxxxx", AInstEmit.Sshl_V, typeof(AOpCodeSimdReg)); Set("0>001110<<1xxxxx010001xxxxxxxxxx", AInstEmit.Sshl_V, typeof(AOpCodeSimdReg));
Set("0x00111100>>>xxx101001xxxxxxxxxx", AInstEmit.Sshll_V, typeof(AOpCodeSimdShImm)); Set("0x00111100>>>xxx101001xxxxxxxxxx", AInstEmit.Sshll_V, typeof(AOpCodeSimdShImm));
Set("010111110>>>>xxx000001xxxxxxxxxx", AInstEmit.Sshr_S, typeof(AOpCodeSimdShImm)); Set("010111110>>>>xxx000001xxxxxxxxxx", AInstEmit.Sshr_S, typeof(AOpCodeSimdShImm));
Set("0x0011110>>>>xxx000001xxxxxxxxxx", AInstEmit.Sshr_V, typeof(AOpCodeSimdShImm)); Set("0x0011110>>>>xxx000001xxxxxxxxxx", AInstEmit.Sshr_V, typeof(AOpCodeSimdShImm));
Set("0x00110000000000xxxxxxxxxxxxxxxx", AInstEmit.St__V, typeof(AOpCodeSimdMemMs)); Set("0x00110000000000xxxxxxxxxxxxxxxx", AInstEmit.St__Vms, typeof(AOpCodeSimdMemMs));
Set("0x001100100xxxxxxxxxxxxxxxxxxxxx", AInstEmit.St__V, typeof(AOpCodeSimdMemMs)); Set("0x001100100xxxxxxxxxxxxxxxxxxxxx", AInstEmit.St__Vms, typeof(AOpCodeSimdMemMs));
Set("0x00110100000000xx0xxxxxxxxxxxxx", AInstEmit.St__Vss, typeof(AOpCodeSimdMemSs));
Set("0x001101100xxxxxxx0xxxxxxxxxxxxx", AInstEmit.St__Vss, typeof(AOpCodeSimdMemSs));
Set("xx10110xx0xxxxxxxxxxxxxxxxxxxxxx", AInstEmit.Stp, typeof(AOpCodeSimdMemPair)); Set("xx10110xx0xxxxxxxxxxxxxxxxxxxxxx", AInstEmit.Stp, typeof(AOpCodeSimdMemPair));
Set("xx111100x00xxxxxxxxx00xxxxxxxxxx", AInstEmit.Str, typeof(AOpCodeSimdMemImm)); Set("xx111100x00xxxxxxxxx00xxxxxxxxxx", AInstEmit.Str, typeof(AOpCodeSimdMemImm));
Set("xx111100x00xxxxxxxxx01xxxxxxxxxx", AInstEmit.Str, typeof(AOpCodeSimdMemImm)); Set("xx111100x00xxxxxxxxx01xxxxxxxxxx", AInstEmit.Str, typeof(AOpCodeSimdMemImm));
@ -293,7 +296,7 @@ namespace ChocolArm64
//The < means that we should never have ALL bits with the '<' set. //The < means that we should never have ALL bits with the '<' set.
//So, when the encoding has <<, it means that 00, 01, and 10 are valid, //So, when the encoding has <<, it means that 00, 01, and 10 are valid,
//but not 11. <<< is 000, 001, ..., 110 but NOT 111, and so on... //but not 11. <<< is 000, 001, ..., 110 but NOT 111, and so on...
//For >, the invalid value is zero. So, for << 01, 10 and 11 are valid, //For >, the invalid value is zero. So, for >> 01, 10 and 11 are valid,
//but 00 isn't. //but 00 isn't.
switch (Encoding[Index]) switch (Encoding[Index])
{ {

View file

@ -10,8 +10,13 @@ namespace ChocolArm64
public ARegisters Registers { get; private set; } public ARegisters Registers { get; private set; }
public AMemory Memory { get; private set; } public AMemory Memory { get; private set; }
public long EntryPoint { get; private set; }
private ATranslator Translator; private ATranslator Translator;
private Thread Work;
private ThreadPriority Priority;
private Thread Work;
public event EventHandler WorkFinished; public event EventHandler WorkFinished;
@ -19,25 +24,35 @@ namespace ChocolArm64
public bool IsAlive => Work.IsAlive; public bool IsAlive => Work.IsAlive;
public long EntryPoint { get; private set; } private bool IsExecuting;
public int Priority { get; private set; }
public AThread(AMemory Memory, long EntryPoint = 0, int Priority = 0) private object ExecuteLock;
public AThread(AMemory Memory, ThreadPriority Priority, long EntryPoint)
{ {
this.Memory = Memory; this.Memory = Memory;
this.EntryPoint = EntryPoint;
this.Priority = Priority; this.Priority = Priority;
this.EntryPoint = EntryPoint;
Registers = new ARegisters(); Registers = new ARegisters();
Translator = new ATranslator(this); Translator = new ATranslator(this);
ExecuteLock = new object();
} }
public void StopExecution() => Translator.StopExecution(); public void StopExecution() => Translator.StopExecution();
public void Execute() => Execute(EntryPoint); public bool Execute()
public void Execute(long EntryPoint)
{ {
lock (ExecuteLock)
{
if (IsExecuting)
{
return false;
}
IsExecuting = true;
}
Work = new Thread(delegate() Work = new Thread(delegate()
{ {
Translator.ExecuteSubroutine(EntryPoint); Translator.ExecuteSubroutine(EntryPoint);
@ -47,28 +62,11 @@ namespace ChocolArm64
WorkFinished?.Invoke(this, EventArgs.Empty); WorkFinished?.Invoke(this, EventArgs.Empty);
}); });
if (Priority < 12) Work.Priority = Priority;
{
Work.Priority = ThreadPriority.Highest;
}
else if (Priority < 24)
{
Work.Priority = ThreadPriority.AboveNormal;
}
else if (Priority < 36)
{
Work.Priority = ThreadPriority.Normal;
}
else if (Priority < 48)
{
Work.Priority = ThreadPriority.BelowNormal;
}
else
{
Work.Priority = ThreadPriority.Lowest;
}
Work.Start(); Work.Start();
return true;
} }
} }
} }

View file

@ -273,6 +273,13 @@ namespace ChocolArm64.Instruction
EmitVectorImmBinaryZx(Context, OpCodes.Shl, Op.Imm - (8 << Op.Size)); EmitVectorImmBinaryZx(Context, OpCodes.Shl, Op.Imm - (8 << Op.Size));
} }
public static void Shrn_V(AILEmitterCtx Context)
{
AOpCodeSimdShImm Op = (AOpCodeSimdShImm)Context.CurrOp;
EmitVectorImmNarrowBinaryZx(Context, OpCodes.Shr_Un, (8 << (Op.Size + 1)) - Op.Imm);
}
public static void Smax_V(AILEmitterCtx Context) => EmitVectorSmax(Context); public static void Smax_V(AILEmitterCtx Context) => EmitVectorSmax(Context);
public static void Smin_V(AILEmitterCtx Context) => EmitVectorSmin(Context); public static void Smin_V(AILEmitterCtx Context) => EmitVectorSmin(Context);
@ -300,7 +307,8 @@ namespace ChocolArm64.Instruction
EmitVectorImmBinarySx(Context, OpCodes.Shr, (8 << (Op.Size + 1)) - Op.Imm); EmitVectorImmBinarySx(Context, OpCodes.Shr, (8 << (Op.Size + 1)) - Op.Imm);
} }
public static void St__V(AILEmitterCtx Context) => EmitSimdMemMs(Context, IsLoad: false); public static void St__Vms(AILEmitterCtx Context) => EmitSimdMemMs(Context, IsLoad: false);
public static void St__Vss(AILEmitterCtx Context) => EmitSimdMemSs(Context, IsLoad: false);
public static void Sub_V(AILEmitterCtx Context) => EmitVectorBinaryZx(Context, OpCodes.Sub); public static void Sub_V(AILEmitterCtx Context) => EmitVectorBinaryZx(Context, OpCodes.Sub);
@ -622,7 +630,7 @@ namespace ChocolArm64.Instruction
for (int Index = 1; Index < (Bytes >> Op.Size); Index++) for (int Index = 1; Index < (Bytes >> Op.Size); Index++)
{ {
EmitVectorExtractZx(Context, Op.Rn, Op.Size, Index); EmitVectorExtractZx(Context, Op.Rn, Index, Op.Size);
Context.Emit(OpCodes.Add); Context.Emit(OpCodes.Add);
} }
@ -986,10 +994,6 @@ namespace ChocolArm64.Instruction
for (int Index = 0; Index < (Bytes >> Op.Size); Index++) for (int Index = 0; Index < (Bytes >> Op.Size); Index++)
{ {
Context.EmitLdvec(Op.Rd);
Context.EmitLdc_I4(Index);
Context.EmitLdc_I4(Op.Size);
if (Opers.HasFlag(OperFlags.Rd)) if (Opers.HasFlag(OperFlags.Rd))
{ {
EmitVectorExtract(Context, Op.Rd, Index, Op.Size, Signed); EmitVectorExtract(Context, Op.Rd, Index, Op.Size, Signed);
@ -1007,9 +1011,7 @@ namespace ChocolArm64.Instruction
Emit(); Emit();
ASoftFallback.EmitCall(Context, nameof(ASoftFallback.InsertVec)); EmitVectorInsert(Context, Op.Rd, Index, Op.Size);
Context.EmitStvec(Op.Rd);
} }
if (Op.RegisterSize == ARegisterSize.SIMD64) if (Op.RegisterSize == ARegisterSize.SIMD64)
@ -1018,27 +1020,27 @@ namespace ChocolArm64.Instruction
} }
} }
private static void EmitVectorImmBinarySx(AILEmitterCtx Context, OpCode ILOp, long Imm) private static void EmitVectorImmBinarySx(AILEmitterCtx Context, OpCode ILOp, int Imm)
{ {
EmitVectorImmBinarySx(Context, () => Context.Emit(ILOp), Imm); EmitVectorImmBinarySx(Context, () => Context.Emit(ILOp), Imm);
} }
private static void EmitVectorImmBinaryZx(AILEmitterCtx Context, OpCode ILOp, long Imm) private static void EmitVectorImmBinaryZx(AILEmitterCtx Context, OpCode ILOp, int Imm)
{ {
EmitVectorImmBinaryZx(Context, () => Context.Emit(ILOp), Imm); EmitVectorImmBinaryZx(Context, () => Context.Emit(ILOp), Imm);
} }
private static void EmitVectorImmBinarySx(AILEmitterCtx Context, Action Emit, long Imm) private static void EmitVectorImmBinarySx(AILEmitterCtx Context, Action Emit, int Imm)
{ {
EmitVectorImmBinaryOp(Context, Emit, Imm, true); EmitVectorImmBinaryOp(Context, Emit, Imm, true);
} }
private static void EmitVectorImmBinaryZx(AILEmitterCtx Context, Action Emit, long Imm) private static void EmitVectorImmBinaryZx(AILEmitterCtx Context, Action Emit, int Imm)
{ {
EmitVectorImmBinaryOp(Context, Emit, Imm, false); EmitVectorImmBinaryOp(Context, Emit, Imm, false);
} }
private static void EmitVectorImmBinaryOp(AILEmitterCtx Context, Action Emit, long Imm, bool Signed) private static void EmitVectorImmBinaryOp(AILEmitterCtx Context, Action Emit, int Imm, bool Signed)
{ {
AOpCodeSimdShImm Op = (AOpCodeSimdShImm)Context.CurrOp; AOpCodeSimdShImm Op = (AOpCodeSimdShImm)Context.CurrOp;
@ -1046,19 +1048,13 @@ namespace ChocolArm64.Instruction
for (int Index = 0; Index < (Bytes >> Op.Size); Index++) for (int Index = 0; Index < (Bytes >> Op.Size); Index++)
{ {
Context.EmitLdvec(Op.Rd); EmitVectorExtract(Context, Op.Rn, Index, Op.Size, Signed);
Context.EmitLdc_I4(Index);
Context.EmitLdc_I4(Op.Size);
EmitVectorExtract(Context, Op.Rn, Index, Op.Size, Signed); Context.EmitLdc_I4(Imm);
Context.EmitLdc_I8(Imm);
Emit(); Emit();
ASoftFallback.EmitCall(Context, nameof(ASoftFallback.InsertVec)); EmitVectorInsert(Context, Op.Rd, Index, Op.Size);
Context.EmitStvec(Op.Rd);
} }
if (Op.RegisterSize == ARegisterSize.SIMD64) if (Op.RegisterSize == ARegisterSize.SIMD64)
@ -1067,6 +1063,56 @@ namespace ChocolArm64.Instruction
} }
} }
private static void EmitVectorImmNarrowBinarySx(AILEmitterCtx Context, OpCode ILOp, int Imm)
{
EmitVectorImmNarrowBinarySx(Context, () => Context.Emit(ILOp), Imm);
}
private static void EmitVectorImmNarrowBinaryZx(AILEmitterCtx Context, OpCode ILOp, int Imm)
{
EmitVectorImmNarrowBinaryZx(Context, () => Context.Emit(ILOp), Imm);
}
private static void EmitVectorImmNarrowBinarySx(AILEmitterCtx Context, Action Emit, int Imm)
{
EmitVectorImmNarrowBinaryOp(Context, Emit, Imm, true);
}
private static void EmitVectorImmNarrowBinaryZx(AILEmitterCtx Context, Action Emit, int Imm)
{
EmitVectorImmNarrowBinaryOp(Context, Emit, Imm, false);
}
private static void EmitVectorImmNarrowBinaryOp(AILEmitterCtx Context, Action Emit, int Imm, bool Signed)
{
AOpCodeSimdShImm Op = (AOpCodeSimdShImm)Context.CurrOp;
if (Op.Size < 0 || Op.Size > 2)
{
throw new InvalidOperationException(Op.Size.ToString());
}
int Elems = 8 >> Op.Size;
int Part = Op.RegisterSize == ARegisterSize.SIMD128 ? Elems : 0;
for (int Index = 0; Index < Elems; Index++)
{
EmitVectorExtract(Context, Op.Rn, Index, Op.Size + 1, Signed);
Context.EmitLdc_I4(Imm);
Emit();
EmitVectorInsert(Context, Op.Rd, Part + Index, Op.Size);
}
if (Part == 0)
{
EmitVectorZeroUpper(Context, Op.Rd);
}
}
private static void EmitVectorCmp(AILEmitterCtx Context, OpCode ILOp) private static void EmitVectorCmp(AILEmitterCtx Context, OpCode ILOp)
{ {
AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp; AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp;
@ -1141,6 +1187,11 @@ namespace ChocolArm64.Instruction
private static void EmitVectorExtract(AILEmitterCtx Context, int Reg, int Index, int Size, bool Signed) private static void EmitVectorExtract(AILEmitterCtx Context, int Reg, int Index, int Size, bool Signed)
{ {
if (Size < 0 || Size > 3)
{
throw new ArgumentOutOfRangeException(nameof(Size));
}
IAOpCodeSimd Op = (IAOpCodeSimd)Context.CurrOp; IAOpCodeSimd Op = (IAOpCodeSimd)Context.CurrOp;
Context.EmitLdvec(Reg); Context.EmitLdvec(Reg);
@ -1185,6 +1236,11 @@ namespace ChocolArm64.Instruction
private static void EmitVectorInsert(AILEmitterCtx Context, int Reg, int Index, int Size) private static void EmitVectorInsert(AILEmitterCtx Context, int Reg, int Index, int Size)
{ {
if (Size < 0 || Size > 3)
{
throw new ArgumentOutOfRangeException(nameof(Size));
}
Context.EmitLdvec(Reg); Context.EmitLdvec(Reg);
Context.EmitLdc_I4(Index); Context.EmitLdc_I4(Index);
Context.EmitLdc_I4(Size); Context.EmitLdc_I4(Size);
@ -1196,6 +1252,11 @@ namespace ChocolArm64.Instruction
private static void EmitVectorInsert(AILEmitterCtx Context, int Reg, int Index, int Size, long Value) private static void EmitVectorInsert(AILEmitterCtx Context, int Reg, int Index, int Size, long Value)
{ {
if (Size < 0 || Size > 3)
{
throw new ArgumentOutOfRangeException(nameof(Size));
}
Context.EmitLdvec(Reg); Context.EmitLdvec(Reg);
Context.EmitLdc_I4(Index); Context.EmitLdc_I4(Index);
Context.EmitLdc_I4(Size); Context.EmitLdc_I4(Size);

View file

@ -197,38 +197,38 @@ namespace ChocolArm64.Instruction
return ValueF; return ValueF;
} }
public static float Int32ToDouble(int Value, int FBits) public static double Int32ToDouble(int Value, int FBits)
{ {
float ValueF = Value; double ValueF = Value;
if (FBits != 0) ValueF *= 1 / MathF.Pow(2, FBits); if (FBits != 0) ValueF *= 1 / Math.Pow(2, FBits);
return ValueF; return ValueF;
} }
public static float Int64ToDouble(long Value, int FBits) public static double Int64ToDouble(long Value, int FBits)
{ {
float ValueF = Value; double ValueF = Value;
if (FBits != 0) ValueF *= 1 / MathF.Pow(2, FBits); if (FBits != 0) ValueF *= 1 / Math.Pow(2, FBits);
return ValueF; return ValueF;
} }
public static float UInt32ToDouble(uint Value, int FBits) public static double UInt32ToDouble(uint Value, int FBits)
{ {
float ValueF = Value; double ValueF = Value;
if (FBits != 0) ValueF *= 1 / MathF.Pow(2, FBits); if (FBits != 0) ValueF *= 1 / Math.Pow(2, FBits);
return ValueF; return ValueF;
} }
public static float UInt64ToDouble(ulong Value, int FBits) public static double UInt64ToDouble(ulong Value, int FBits)
{ {
float ValueF = Value; double ValueF = Value;
if (FBits != 0) ValueF *= 1 / MathF.Pow(2, FBits); if (FBits != 0) ValueF *= 1 / Math.Pow(2, FBits);
return ValueF; return ValueF;
} }

View file

@ -66,10 +66,10 @@ namespace ChocolArm64.Memory
public void SetExclusive(ARegisters Registers, long Position) public void SetExclusive(ARegisters Registers, long Position)
{ {
Position &= ~ErgMask;
lock (Monitors) lock (Monitors)
{ {
Position &= ~ErgMask;
if (Monitors.TryGetValue(Registers.ThreadId, out ExMonitor Monitor)) if (Monitors.TryGetValue(Registers.ThreadId, out ExMonitor Monitor))
{ {
ExAddrs.Remove(Monitor.Position); ExAddrs.Remove(Monitor.Position);
@ -88,10 +88,10 @@ namespace ChocolArm64.Memory
public bool TestExclusive(ARegisters Registers, long Position) public bool TestExclusive(ARegisters Registers, long Position)
{ {
Position &= ~ErgMask;
lock (Monitors) lock (Monitors)
{ {
Position &= ~ErgMask;
if (!Monitors.TryGetValue(Registers.ThreadId, out ExMonitor Monitor)) if (!Monitors.TryGetValue(Registers.ThreadId, out ExMonitor Monitor))
{ {
return false; return false;
@ -113,6 +113,26 @@ namespace ChocolArm64.Memory
} }
} }
public bool AcquireAddress(long Position)
{
Position &= ~ErgMask;
lock (Monitors)
{
return ExAddrs.Add(Position);
}
}
public void ReleaseAddress(long Position)
{
Position &= ~ErgMask;
lock (Monitors)
{
ExAddrs.Remove(Position);
}
}
public sbyte ReadSByte(long Position) => (sbyte)ReadByte (Position); public sbyte ReadSByte(long Position) => (sbyte)ReadByte (Position);
public short ReadInt16(long Position) => (short)ReadUInt16(Position); public short ReadInt16(long Position) => (short)ReadUInt16(Position);
public int ReadInt32(long Position) => (int)ReadUInt32(Position); public int ReadInt32(long Position) => (int)ReadUInt32(Position);

View file

@ -1,5 +1,6 @@
using System.IO; using System.IO;
using System.Text; using System.Text;
using System.Threading;
namespace ChocolArm64.Memory namespace ChocolArm64.Memory
{ {
@ -20,6 +21,32 @@ namespace ChocolArm64.Memory
} }
} }
public static int ReadInt32Exclusive(AMemory Memory, long Position)
{
while (!Memory.AcquireAddress(Position))
{
Thread.Yield();
}
int Value = Memory.ReadInt32(Position);
Memory.ReleaseAddress(Position);
return Value;
}
public static void WriteInt32Exclusive(AMemory Memory, long Position, int Value)
{
while (!Memory.AcquireAddress(Position))
{
Thread.Yield();
}
Memory.WriteInt32(Position, Value);
Memory.ReleaseAddress(Position);
}
public static byte[] ReadBytes(AMemory Memory, long Position, int Size) public static byte[] ReadBytes(AMemory Memory, long Position, int Size)
{ {
byte[] Data = new byte[Size]; byte[] Data = new byte[Size];

View file

@ -1,86 +1,103 @@
using ChocolArm64.Memory; using ChocolArm64.Memory;
using System.Collections.Concurrent; using Ryujinx.OsHle.Handles;
using System.Threading; using System.Collections.Generic;
namespace Ryujinx.OsHle namespace Ryujinx.OsHle
{ {
class CondVar class CondVar
{ {
private AMemory Memory; private Process Process;
private long CondVarAddress; private long CondVarAddress;
private long Timeout; private long Timeout;
private class WaitingThread private List<HThread> WaitingThreads;
public CondVar(Process Process, long CondVarAddress, long Timeout)
{ {
public int Handle; this.Process = Process;
public ManualResetEvent Event;
public WaitingThread(int Handle, ManualResetEvent Event)
{
this.Handle = Handle;
this.Event = Event;
}
}
private ConcurrentQueue<WaitingThread> WaitingThreads;
public CondVar(AMemory Memory, long CondVarAddress, long Timeout)
{
this.Memory = Memory;
this.CondVarAddress = CondVarAddress; this.CondVarAddress = CondVarAddress;
this.Timeout = Timeout; this.Timeout = Timeout;
WaitingThreads = new ConcurrentQueue<WaitingThread>(); WaitingThreads = new List<HThread>();
} }
public void WaitForSignal(int ThreadHandle) public void WaitForSignal(HThread Thread)
{ {
int Count = Memory.ReadInt32(CondVarAddress); int Count = ReadCondVarValue();
if (Count <= 0) if (Count <= 0)
{ {
//FIXME: We shouldn't need to do that?
Process.Scheduler.Yield(Thread);
return; return;
} }
Memory.WriteInt32(CondVarAddress, Count - 1); WriteCondVarValue(Count - 1);
ManualResetEvent Event = new ManualResetEvent(false); lock (WaitingThreads)
{
WaitingThreads.Enqueue(new WaitingThread(ThreadHandle, Event)); WaitingThreads.Add(Thread);
}
if (Timeout != -1) if (Timeout != -1)
{ {
Event.WaitOne((int)(Timeout / 1000000)); Process.Scheduler.WaitForSignal(Thread, (int)(Timeout / 1000000));
} }
else else
{ {
Event.WaitOne(); Process.Scheduler.WaitForSignal(Thread);
} }
} }
public void SetSignal(int Count) public void SetSignal(int Count)
{ {
if (Count == -1) lock (WaitingThreads)
{ {
while (WaitingThreads.TryDequeue(out WaitingThread Thread)) if (Count == -1)
{ {
Thread.Event.Set(); Process.Scheduler.Signal(WaitingThreads.ToArray());
}
Memory.WriteInt32(CondVarAddress, WaitingThreads.Count); WriteCondVarValue(WaitingThreads.Count);
}
else WaitingThreads.Clear();
{ }
//TODO: Threads with the highest priority needs to be signaled first. else
if (WaitingThreads.TryDequeue(out WaitingThread Thread))
{ {
Thread.Event.Set(); if (WaitingThreads.Count > 0)
} {
int HighestPriority = WaitingThreads[0].Priority;
int HighestPrioIndex = 0;
Memory.WriteInt32(CondVarAddress, Count); for (int Index = 1; Index < WaitingThreads.Count; Index++)
{
if (HighestPriority > WaitingThreads[Index].Priority)
{
HighestPriority = WaitingThreads[Index].Priority;
HighestPrioIndex = Index;
}
}
Process.Scheduler.Signal(WaitingThreads[HighestPrioIndex]);
WaitingThreads.RemoveAt(HighestPrioIndex);
}
WriteCondVarValue(Count);
}
} }
} }
private int ReadCondVarValue()
{
return AMemoryHelper.ReadInt32Exclusive(Process.Memory, CondVarAddress);
}
private void WriteCondVarValue(int Value)
{
AMemoryHelper.WriteInt32Exclusive(Process.Memory, CondVarAddress, Value);
}
} }
} }

View file

@ -18,9 +18,9 @@ namespace Ryujinx.OsHle.Handles
protected virtual void Dispose(bool Disposing) protected virtual void Dispose(bool Disposing)
{ {
if(Disposing && Obj != null) if (Disposing && Obj != null)
{ {
if(Obj is IDisposable DisposableObj) if (Obj is IDisposable DisposableObj)
{ {
DisposableObj.Dispose(); DisposableObj.Dispose();
} }

View file

@ -6,9 +6,16 @@ namespace Ryujinx.OsHle.Handles
{ {
public AThread Thread { get; private set; } public AThread Thread { get; private set; }
public HThread(AThread Thread) public int ProcessorId { get; private set; }
public int Priority { get; private set; }
public int ThreadId => Thread.ThreadId;
public HThread(AThread Thread, int ProcessorId, int Priority)
{ {
this.Thread = Thread; this.Thread = Thread;
this.ProcessorId = ProcessorId;
this.Priority = Priority;
} }
} }
} }

View file

@ -0,0 +1,335 @@
using System;
using System.Collections.Generic;
using System.Threading;
namespace Ryujinx.OsHle.Handles
{
class KProcessScheduler : IDisposable
{
private enum ThreadState
{
WaitingToRun,
WaitingSignal,
Running
}
private class SchedulerThread : IDisposable
{
public bool Signaled { get; set; }
public ThreadState State { get; set; }
public HThread Thread { get; private set; }
public AutoResetEvent WaitEvent { get; private set; }
public SchedulerThread(HThread Thread)
{
this.Thread = Thread;
WaitEvent = new AutoResetEvent(false);
}
public void Dispose()
{
Dispose(true);
}
protected virtual void Dispose(bool Disposing)
{
if (Disposing)
{
WaitEvent.Dispose();
}
}
}
private Dictionary<HThread, SchedulerThread> AllThreads;
private Queue<SchedulerThread>[] WaitingThreads;
private HashSet<int> ActiveProcessors;
private object SchedLock;
public KProcessScheduler()
{
AllThreads = new Dictionary<HThread, SchedulerThread>();
WaitingThreads = new Queue<SchedulerThread>[4];
for (int Index = 0; Index < WaitingThreads.Length; Index++)
{
WaitingThreads[Index] = new Queue<SchedulerThread>();
}
ActiveProcessors = new HashSet<int>();
SchedLock = new object();
}
public void StartThread(HThread Thread)
{
lock (SchedLock)
{
if (AllThreads.ContainsKey(Thread))
{
return;
}
SchedulerThread SchedThread = new SchedulerThread(Thread);
AllThreads.Add(Thread, SchedThread);
if (!ActiveProcessors.Contains(Thread.ProcessorId))
{
ActiveProcessors.Add(Thread.ProcessorId);
Thread.Thread.Execute();
SetThreadAsRunning(SchedThread);
SchedThread.State = ThreadState.Running;
}
else
{
InsertSorted(SchedThread);
SchedThread.State = ThreadState.WaitingToRun;
Logging.Debug($"{GetDbgThreadInfo(SchedThread.Thread)} waiting to run.");
}
}
}
public void WaitForSignal(HThread Thread, int TimeoutMs)
{
Logging.Debug($"{GetDbgThreadInfo(Thread)} entering signal wait state with timeout.");
PutThreadToWait(Thread, ThreadState.WaitingSignal, TimeoutMs);
}
public void WaitForSignal(HThread Thread)
{
Logging.Debug($"{GetDbgThreadInfo(Thread)} entering signal wait state.");
PutThreadToWait(Thread, ThreadState.WaitingSignal);
}
public void Yield(HThread Thread)
{
Logging.Debug($"{GetDbgThreadInfo(Thread)} yielded execution.");
if (WaitingThreads[Thread.ProcessorId].Count == 0)
{
Logging.Debug($"{GetDbgThreadInfo(Thread)} resumed because theres nothing to run.");
return;
}
PutThreadToWait(Thread, ThreadState.WaitingToRun);
}
private void PutThreadToWait(HThread Thread, ThreadState State, int TimeoutMs = -1)
{
SchedulerThread SchedThread;
lock (SchedLock)
{
if (!AllThreads.TryGetValue(Thread, out SchedThread))
{
return;
}
if (SchedThread.Signaled && SchedThread.State == ThreadState.WaitingSignal)
{
SchedThread.Signaled = false;
return;
}
ActiveProcessors.Remove(Thread.ProcessorId);
SchedThread.State = State;
TryRunningWaitingThead(SchedThread.Thread.ProcessorId);
if (State == ThreadState.WaitingSignal)
{
InsertSorted(SchedThread);
}
else
{
InsertAtEnd(SchedThread);
}
}
if (TimeoutMs >= 0)
{
Logging.Debug($"{GetDbgThreadInfo(SchedThread.Thread)} waiting with timeout of {TimeoutMs}ms.");
SchedThread.WaitEvent.WaitOne(TimeoutMs);
}
else
{
Logging.Debug($"{GetDbgThreadInfo(SchedThread.Thread)} waiting indefinitely.");
SchedThread.WaitEvent.WaitOne();
}
while (true)
{
lock (SchedLock)
{
Logging.Debug($"Trying to run {GetDbgThreadInfo(SchedThread.Thread)}.");
if (!ActiveProcessors.Contains(SchedThread.Thread.ProcessorId))
{
SetThreadAsRunning(SchedThread);
break;
}
else
{
SchedThread.State = ThreadState.WaitingToRun;
Logging.Debug($"{GetDbgThreadInfo(SchedThread.Thread)} waiting to run.");
}
}
SchedThread.WaitEvent.WaitOne();
}
}
public void Signal(params HThread[] Threads)
{
lock (SchedLock)
{
HashSet<int> SignaledProcessorIds = new HashSet<int>();
foreach (HThread Thread in Threads)
{
Logging.Debug($"{GetDbgThreadInfo(Thread)} signaled.");
if (AllThreads.TryGetValue(Thread, out SchedulerThread SchedThread))
{
if (SchedThread.State == ThreadState.WaitingSignal)
{
SchedThread.State = ThreadState.WaitingToRun;
SignaledProcessorIds.Add(Thread.ProcessorId);
}
SchedThread.Signaled = true;
}
}
foreach (int ProcessorId in SignaledProcessorIds)
{
TryRunningWaitingThead(ProcessorId);
}
}
}
private void TryRunningWaitingThead(int ProcessorId)
{
Logging.Debug($"TryRunningWaitingThead core {ProcessorId}.");
lock (SchedLock)
{
if (!ActiveProcessors.Contains(ProcessorId) && WaitingThreads[ProcessorId].Count > 0)
{
SchedulerThread SchedThread = WaitingThreads[ProcessorId].Dequeue();
Logging.Debug($"Now trying to run {GetDbgThreadInfo(SchedThread.Thread)}.");
if (!SchedThread.Thread.Thread.Execute())
{
SchedThread.WaitEvent.Set();
}
else
{
SetThreadAsRunning(SchedThread);
}
}
else
{
Logging.Debug($"Processor id {ProcessorId} already being used or no waiting threads.");
}
}
}
private void SetThreadAsRunning(SchedulerThread SchedThread)
{
ActiveProcessors.Add(SchedThread.Thread.ProcessorId);
SchedThread.State = ThreadState.Running;
SchedThread.Signaled = false;
Logging.Debug($"{GetDbgThreadInfo(SchedThread.Thread)} running.");
}
private void InsertSorted(SchedulerThread SchedThread)
{
HThread Thread = SchedThread.Thread;
Queue<SchedulerThread> CoreQueue = WaitingThreads[Thread.ProcessorId];
Queue<SchedulerThread> TempQueue = new Queue<SchedulerThread>(CoreQueue.Count);
while (CoreQueue.Count > 0)
{
if (CoreQueue.Peek().Thread.Priority >= Thread.Priority)
{
break;
}
TempQueue.Enqueue(CoreQueue.Dequeue());
}
CoreQueue.Enqueue(SchedThread);
while (CoreQueue.Count > 0)
{
TempQueue.Enqueue(CoreQueue.Dequeue());
}
while (TempQueue.Count > 0)
{
CoreQueue.Enqueue(TempQueue.Dequeue());
}
}
private void InsertAtEnd(SchedulerThread SchedThread)
{
WaitingThreads[SchedThread.Thread.ProcessorId].Enqueue(SchedThread);
}
private string GetDbgThreadInfo(HThread Thread)
{
return $"Thread {Thread.ThreadId} (core {Thread.ProcessorId}) prio {Thread.Priority}";
}
public void Dispose()
{
Dispose(true);
}
protected virtual void Dispose(bool Disposing)
{
if (Disposing)
{
foreach (Queue<SchedulerThread> SchedThreads in WaitingThreads)
{
foreach (SchedulerThread SchedThread in SchedThreads)
{
SchedThread.Dispose();
}
SchedThreads.Clear();
}
}
}
}
}

View file

@ -127,11 +127,12 @@ namespace Ryujinx.OsHle
Processes.TryAdd(ProcessId, MainProcess); Processes.TryAdd(ProcessId, MainProcess);
} }
public void StopAllProcesses() public void FinalizeAllProcesses()
{ {
foreach (Process Process in Processes.Values) foreach (Process Process in Processes.Values)
{ {
Process.StopAllThreads(); Process.StopAllThreads();
Process.Dispose();
} }
} }

View file

@ -62,10 +62,12 @@ namespace Ryujinx.OsHle.Ipc
{ ( "time:u", 1), Service.TimeGetStandardNetworkSystemClock }, { ( "time:u", 1), Service.TimeGetStandardNetworkSystemClock },
{ ( "time:u", 2), Service.TimeGetStandardSteadyClock }, { ( "time:u", 2), Service.TimeGetStandardSteadyClock },
{ ( "time:u", 3), Service.TimeGetTimeZoneService }, { ( "time:u", 3), Service.TimeGetTimeZoneService },
{ ( "time:u", 4), Service.TimeGetStandardLocalSystemClock },
{ ( "time:s", 0), Service.TimeGetStandardUserSystemClock }, { ( "time:s", 0), Service.TimeGetStandardUserSystemClock },
{ ( "time:s", 1), Service.TimeGetStandardNetworkSystemClock }, { ( "time:s", 1), Service.TimeGetStandardNetworkSystemClock },
{ ( "time:s", 2), Service.TimeGetStandardSteadyClock }, { ( "time:s", 2), Service.TimeGetStandardSteadyClock },
{ ( "time:s", 3), Service.TimeGetTimeZoneService }, { ( "time:s", 3), Service.TimeGetTimeZoneService },
{ ( "time:s", 4), Service.TimeGetStandardLocalSystemClock },
{ ( "vi:m", 2), Service.ViGetDisplayService }, { ( "vi:m", 2), Service.ViGetDisplayService },
}; };

View file

@ -1,5 +1,6 @@
using ChocolArm64;
using ChocolArm64.Memory; using ChocolArm64.Memory;
using Ryujinx.OsHle.Handles;
using System.Collections.Concurrent;
using System.Threading; using System.Threading;
namespace Ryujinx.OsHle namespace Ryujinx.OsHle
@ -8,33 +9,31 @@ namespace Ryujinx.OsHle
{ {
private const int MutexHasListenersMask = 0x40000000; private const int MutexHasListenersMask = 0x40000000;
private AMemory Memory; private Process Process;
private long MutexAddress; private long MutexAddress;
private int CurrRequestingThreadHandle;
private int HighestPriority;
private ManualResetEvent ThreadEvent;
private object EnterWaitLock; private object EnterWaitLock;
public Mutex(AMemory Memory, long MutexAddress) private ConcurrentQueue<HThread> WaitingThreads;
public Mutex(Process Process, long MutexAddress, int OwnerThreadHandle)
{ {
this.Memory = Memory; this.Process = Process;
this.MutexAddress = MutexAddress; this.MutexAddress = MutexAddress;
ThreadEvent = new ManualResetEvent(false); //Process.Memory.WriteInt32(MutexAddress, OwnerThreadHandle);
EnterWaitLock = new object(); EnterWaitLock = new object();
WaitingThreads = new ConcurrentQueue<HThread>();
} }
public void WaitForLock(AThread RequestingThread, int RequestingThreadHandle) public void WaitForLock(HThread RequestingThread, int RequestingThreadHandle)
{ {
lock (EnterWaitLock) lock (EnterWaitLock)
{ {
int CurrentThreadHandle = Memory.ReadInt32(MutexAddress) & ~MutexHasListenersMask; int CurrentThreadHandle = ReadMutexValue() & ~MutexHasListenersMask;
if (CurrentThreadHandle == RequestingThreadHandle || if (CurrentThreadHandle == RequestingThreadHandle ||
CurrentThreadHandle == 0) CurrentThreadHandle == 0)
@ -42,23 +41,19 @@ namespace Ryujinx.OsHle
return; return;
} }
if (CurrRequestingThreadHandle == 0 || RequestingThread.Priority < HighestPriority) WriteMutexValue(CurrentThreadHandle | MutexHasListenersMask);
{
CurrRequestingThreadHandle = RequestingThreadHandle;
HighestPriority = RequestingThread.Priority; WaitingThreads.Enqueue(RequestingThread);
}
} }
ThreadEvent.Reset(); Process.Scheduler.WaitForSignal(RequestingThread);
ThreadEvent.WaitOne();
} }
public void GiveUpLock(int ThreadHandle) public void GiveUpLock(int ThreadHandle)
{ {
lock (EnterWaitLock) lock (EnterWaitLock)
{ {
int CurrentThread = Memory.ReadInt32(MutexAddress) & ~MutexHasListenersMask; int CurrentThread = ReadMutexValue() & ~MutexHasListenersMask;
if (CurrentThread == ThreadHandle) if (CurrentThread == ThreadHandle)
{ {
@ -71,19 +66,31 @@ namespace Ryujinx.OsHle
{ {
lock (EnterWaitLock) lock (EnterWaitLock)
{ {
if (CurrRequestingThreadHandle != 0) int HasListeners = WaitingThreads.Count > 1 ? MutexHasListenersMask : 0;
WriteMutexValue(HasListeners);
HThread[] UnlockedThreads = new HThread[WaitingThreads.Count];
int Index = 0;
while (WaitingThreads.TryDequeue(out HThread Thread))
{ {
Memory.WriteInt32(MutexAddress, CurrRequestingThreadHandle); UnlockedThreads[Index++] = Thread;
}
else
{
Memory.WriteInt32(MutexAddress, 0);
} }
CurrRequestingThreadHandle = 0; Process.Scheduler.Signal(UnlockedThreads);
ThreadEvent.Set();
} }
} }
private int ReadMutexValue()
{
return AMemoryHelper.ReadInt32Exclusive(Process.Memory, MutexAddress);
}
private void WriteMutexValue(int Value)
{
AMemoryHelper.WriteInt32Exclusive(Process.Memory, MutexAddress, Value);
}
} }
} }

View file

@ -12,16 +12,28 @@ namespace Ryujinx.OsHle.Objects.Time
private static DateTime Epoch = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc); private static DateTime Epoch = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
public ISystemClock() private SystemClockType ClockType;
public ISystemClock(SystemClockType ClockType)
{ {
m_Commands = new Dictionary<int, ServiceProcessRequest>() m_Commands = new Dictionary<int, ServiceProcessRequest>()
{ {
{ 0, GetCurrentTime } { 0, GetCurrentTime }
}; };
this.ClockType = ClockType;
} }
public long GetCurrentTime(ServiceCtx Context) public long GetCurrentTime(ServiceCtx Context)
{ {
DateTime CurrentTime = DateTime.Now;
if (ClockType == SystemClockType.Standard ||
ClockType == SystemClockType.Network)
{
CurrentTime = CurrentTime.ToUniversalTime();
}
Context.ResponseData.Write((long)(DateTime.Now - Epoch).TotalSeconds); Context.ResponseData.Write((long)(DateTime.Now - Epoch).TotalSeconds);
return 0; return 0;

View file

@ -0,0 +1,9 @@
namespace Ryujinx.OsHle.Objects.Time
{
enum SystemClockType
{
Standard,
Network,
Local
}
}

View file

@ -9,10 +9,11 @@ using Ryujinx.OsHle.Svc;
using System; using System;
using System.Collections.Concurrent; using System.Collections.Concurrent;
using System.Collections.Generic; using System.Collections.Generic;
using System.Threading;
namespace Ryujinx.OsHle namespace Ryujinx.OsHle
{ {
class Process class Process : IDisposable
{ {
private const int MaxStackSize = 8 * 1024 * 1024; private const int MaxStackSize = 8 * 1024 * 1024;
@ -27,14 +28,18 @@ namespace Ryujinx.OsHle
public AMemory Memory { get; private set; } public AMemory Memory { get; private set; }
private SvcHandler SvcHandler; public KProcessScheduler Scheduler { get; private set; }
private AThread MainThread; private SvcHandler SvcHandler;
private ConcurrentDictionary<int, AThread> TlsSlots; private ConcurrentDictionary<int, AThread> TlsSlots;
private ConcurrentDictionary<long, HThread> ThreadsByTpidr;
private List<Executable> Executables; private List<Executable> Executables;
private HThread MainThread;
private long ImageBase; private long ImageBase;
public Process(Switch Ns, AMemoryAlloc Allocator, int ProcessId) public Process(Switch Ns, AMemoryAlloc Allocator, int ProcessId)
@ -42,9 +47,16 @@ namespace Ryujinx.OsHle
this.Ns = Ns; this.Ns = Ns;
this.ProcessId = ProcessId; this.ProcessId = ProcessId;
Memory = new AMemory(Ns.Ram, Allocator); Memory = new AMemory(Ns.Ram, Allocator);
SvcHandler = new SvcHandler(Ns, Memory);
TlsSlots = new ConcurrentDictionary<int, AThread>(); Scheduler = new KProcessScheduler();
SvcHandler = new SvcHandler(Ns, this);
TlsSlots = new ConcurrentDictionary<int, AThread>();
ThreadsByTpidr = new ConcurrentDictionary<long, HThread>();
Executables = new List<Executable>(); Executables = new List<Executable>();
ImageBase = 0x8000000; ImageBase = 0x8000000;
@ -86,16 +98,16 @@ namespace Ryujinx.OsHle
Memory.Manager.MapPhys(StackBot, MaxStackSize, (int)MemoryType.Normal, AMemoryPerm.RW); Memory.Manager.MapPhys(StackBot, MaxStackSize, (int)MemoryType.Normal, AMemoryPerm.RW);
int Handle = MakeThread(Executables[0].ImageBase, TlsPageAddr, 0, 48, 0); int Handle = MakeThread(Executables[0].ImageBase, TlsPageAddr, 0, 0, 0);
if (Handle == -1) if (Handle == -1)
{ {
return false; return false;
} }
MainThread = Ns.Os.Handles.GetData<HThread>(Handle).Thread; MainThread = Ns.Os.Handles.GetData<HThread>(Handle);
MainThread.Execute(); Scheduler.StartThread(MainThread);
return true; return true;
} }
@ -104,9 +116,9 @@ namespace Ryujinx.OsHle
{ {
if (MainThread != null) if (MainThread != null)
{ {
while (MainThread.IsAlive) while (MainThread.Thread.IsAlive)
{ {
MainThread.StopExecution(); MainThread.Thread.StopExecution();
} }
} }
@ -126,12 +138,37 @@ namespace Ryujinx.OsHle
int Priority, int Priority,
int ProcessorId) int ProcessorId)
{ {
AThread Thread = new AThread(Memory, EntryPoint, Priority); ThreadPriority ThreadPrio;
if (Priority < 12)
{
ThreadPrio = ThreadPriority.Highest;
}
else if (Priority < 24)
{
ThreadPrio = ThreadPriority.AboveNormal;
}
else if (Priority < 36)
{
ThreadPrio = ThreadPriority.Normal;
}
else if (Priority < 48)
{
ThreadPrio = ThreadPriority.BelowNormal;
}
else
{
ThreadPrio = ThreadPriority.Lowest;
}
AThread Thread = new AThread(Memory, ThreadPrio, EntryPoint);
HThread ThreadHnd = new HThread(Thread, ProcessorId, Priority);
int Handle = Ns.Os.Handles.GenerateId(ThreadHnd);
int TlsSlot = GetFreeTlsSlot(Thread); int TlsSlot = GetFreeTlsSlot(Thread);
int Handle = Ns.Os.Handles.GenerateId(new HThread(Thread));
if (TlsSlot == -1 || Handle == -1) if (TlsSlot == -1 || Handle == -1)
{ {
return -1; return -1;
@ -149,6 +186,8 @@ namespace Ryujinx.OsHle
Thread.WorkFinished += ThreadFinished; Thread.WorkFinished += ThreadFinished;
ThreadsByTpidr.TryAdd(Thread.Registers.Tpidr, ThreadHnd);
return Handle; return Handle;
} }
@ -189,5 +228,23 @@ namespace Ryujinx.OsHle
{ {
return (int)((Position - TlsPageAddr) / TlsSize); return (int)((Position - TlsPageAddr) / TlsSize);
} }
public bool TryGetThread(long Tpidr, out HThread Thread)
{
return ThreadsByTpidr.TryGetValue(Tpidr, out Thread);
}
public void Dispose()
{
Dispose(true);
}
protected virtual void Dispose(bool Disposing)
{
if (Disposing)
{
Scheduler.Dispose();
}
}
} }
} }

View file

@ -8,14 +8,14 @@ namespace Ryujinx.OsHle.Services
{ {
public static long TimeGetStandardUserSystemClock(ServiceCtx Context) public static long TimeGetStandardUserSystemClock(ServiceCtx Context)
{ {
MakeObject(Context, new ISystemClock()); MakeObject(Context, new ISystemClock(SystemClockType.Standard));
return 0; return 0;
} }
public static long TimeGetStandardNetworkSystemClock(ServiceCtx Context) public static long TimeGetStandardNetworkSystemClock(ServiceCtx Context)
{ {
MakeObject(Context, new ISystemClock()); MakeObject(Context, new ISystemClock(SystemClockType.Network));
return 0; return 0;
} }
@ -33,5 +33,13 @@ namespace Ryujinx.OsHle.Services
return 0; return 0;
} }
public static long TimeGetStandardLocalSystemClock(ServiceCtx Context)
{
MakeObject(Context, new ISystemClock(SystemClockType.Local));
return 0;
}
} }
} }

View file

@ -7,55 +7,50 @@ namespace Ryujinx.OsHle.Svc
{ {
partial class SvcHandler partial class SvcHandler
{ {
private delegate void SvcFunc(Switch Ns, ARegisters Registers, AMemory Memory); private delegate void SvcFunc(ARegisters Registers);
private Dictionary<int, SvcFunc> SvcFuncs = new Dictionary<int, SvcFunc>() private Dictionary<int, SvcFunc> SvcFuncs;
{
{ 0x01, SvcSetHeapSize },
{ 0x03, SvcSetMemoryAttribute },
{ 0x04, SvcMapMemory },
{ 0x06, SvcQueryMemory },
{ 0x08, SvcCreateThread },
{ 0x09, SvcStartThread },
{ 0x0b, SvcSleepThread },
{ 0x0c, SvcGetThreadPriority },
{ 0x13, SvcMapSharedMemory },
{ 0x14, SvcUnmapSharedMemory },
{ 0x15, SvcCreateTransferMemory },
{ 0x16, SvcCloseHandle },
{ 0x17, SvcResetSignal },
{ 0x18, SvcWaitSynchronization },
{ 0x1a, SvcArbitrateLock },
{ 0x1b, SvcArbitrateUnlock },
{ 0x1c, SvcWaitProcessWideKeyAtomic },
{ 0x1d, SvcSignalProcessWideKey },
{ 0x1e, SvcGetSystemTick },
{ 0x1f, SvcConnectToNamedPort },
{ 0x21, SvcSendSyncRequest },
{ 0x22, SvcSendSyncRequestWithUserBuffer },
{ 0x26, SvcBreak },
{ 0x27, SvcOutputDebugString },
{ 0x29, SvcGetInfo }
};
enum SvcResult
{
Success = 0,
ErrBadHandle = 0xe401,
ErrTimeout = 0xea01,
ErrBadInfo = 0xf001,
ErrBadIpcReq = 0xf601
}
private Switch Ns; private Switch Ns;
private Process Process;
private AMemory Memory; private AMemory Memory;
private static Random Rng; private static Random Rng;
public SvcHandler(Switch Ns, AMemory Memory) public SvcHandler(Switch Ns, Process Process)
{ {
this.Ns = Ns; SvcFuncs = new Dictionary<int, SvcFunc>()
this.Memory = Memory; {
{ 0x01, SvcSetHeapSize },
{ 0x03, SvcSetMemoryAttribute },
{ 0x04, SvcMapMemory },
{ 0x06, SvcQueryMemory },
{ 0x08, SvcCreateThread },
{ 0x09, SvcStartThread },
{ 0x0b, SvcSleepThread },
{ 0x0c, SvcGetThreadPriority },
{ 0x13, SvcMapSharedMemory },
{ 0x14, SvcUnmapSharedMemory },
{ 0x15, SvcCreateTransferMemory },
{ 0x16, SvcCloseHandle },
{ 0x17, SvcResetSignal },
{ 0x18, SvcWaitSynchronization },
{ 0x1a, SvcArbitrateLock },
{ 0x1b, SvcArbitrateUnlock },
{ 0x1c, SvcWaitProcessWideKeyAtomic },
{ 0x1d, SvcSignalProcessWideKey },
{ 0x1e, SvcGetSystemTick },
{ 0x1f, SvcConnectToNamedPort },
{ 0x21, SvcSendSyncRequest },
{ 0x22, SvcSendSyncRequestWithUserBuffer },
{ 0x26, SvcBreak },
{ 0x27, SvcOutputDebugString },
{ 0x29, SvcGetInfo }
};
this.Ns = Ns;
this.Process = Process;
this.Memory = Process.Memory;
} }
static SvcHandler() static SvcHandler()
@ -69,9 +64,11 @@ namespace Ryujinx.OsHle.Svc
if (SvcFuncs.TryGetValue(e.Id, out SvcFunc Func)) if (SvcFuncs.TryGetValue(e.Id, out SvcFunc Func))
{ {
Logging.Trace($"{Func.Method.Name} called."); Logging.Trace($"(Thread {Registers.ThreadId}) {Func.Method.Name} called.");
Func(Ns, Registers, Memory);
Logging.Trace($"{Func.Method.Name} ended."); Func(Registers);
Logging.Trace($"(Thread {Registers.ThreadId}) {Func.Method.Name} ended.");
} }
else else
{ {

View file

@ -6,7 +6,7 @@ namespace Ryujinx.OsHle.Svc
{ {
partial class SvcHandler partial class SvcHandler
{ {
private static void SvcSetHeapSize(Switch Ns, ARegisters Registers, AMemory Memory) private void SvcSetHeapSize(ARegisters Registers)
{ {
uint Size = (uint)Registers.X1; uint Size = (uint)Registers.X1;
@ -16,7 +16,7 @@ namespace Ryujinx.OsHle.Svc
Registers.X1 = (ulong)Memory.Manager.HeapAddr; Registers.X1 = (ulong)Memory.Manager.HeapAddr;
} }
private static void SvcSetMemoryAttribute(Switch Ns, ARegisters Registers, AMemory Memory) private void SvcSetMemoryAttribute(ARegisters Registers)
{ {
long Position = (long)Registers.X0; long Position = (long)Registers.X0;
long Size = (long)Registers.X1; long Size = (long)Registers.X1;
@ -28,7 +28,7 @@ namespace Ryujinx.OsHle.Svc
Registers.X0 = (int)SvcResult.Success; Registers.X0 = (int)SvcResult.Success;
} }
private static void SvcMapMemory(Switch Ns, ARegisters Registers, AMemory Memory) private void SvcMapMemory(ARegisters Registers)
{ {
long Dst = (long)Registers.X0; long Dst = (long)Registers.X0;
long Src = (long)Registers.X1; long Src = (long)Registers.X1;
@ -39,7 +39,7 @@ namespace Ryujinx.OsHle.Svc
Registers.X0 = (int)SvcResult.Success; Registers.X0 = (int)SvcResult.Success;
} }
private static void SvcQueryMemory(Switch Ns, ARegisters Registers, AMemory Memory) private void SvcQueryMemory(ARegisters Registers)
{ {
long InfoPtr = (long)Registers.X0; long InfoPtr = (long)Registers.X0;
long Position = (long)Registers.X2; long Position = (long)Registers.X2;
@ -63,7 +63,7 @@ namespace Ryujinx.OsHle.Svc
Registers.X1 = 0; Registers.X1 = 0;
} }
private static void SvcMapSharedMemory(Switch Ns, ARegisters Registers, AMemory Memory) private void SvcMapSharedMemory(ARegisters Registers)
{ {
int Handle = (int)Registers.X0; int Handle = (int)Registers.X0;
long Position = (long)Registers.X1; long Position = (long)Registers.X1;
@ -87,7 +87,7 @@ namespace Ryujinx.OsHle.Svc
//TODO: Error codes. //TODO: Error codes.
} }
private static void SvcUnmapSharedMemory(Switch Ns, ARegisters Registers, AMemory Memory) private void SvcUnmapSharedMemory(ARegisters Registers)
{ {
int Handle = (int)Registers.X0; int Handle = (int)Registers.X0;
long Position = (long)Registers.X1; long Position = (long)Registers.X1;
@ -103,7 +103,7 @@ namespace Ryujinx.OsHle.Svc
//TODO: Error codes. //TODO: Error codes.
} }
private static void SvcCreateTransferMemory(Switch Ns, ARegisters Registers, AMemory Memory) private void SvcCreateTransferMemory(ARegisters Registers)
{ {
long Position = (long)Registers.X1; long Position = (long)Registers.X1;
long Size = (long)Registers.X2; long Size = (long)Registers.X2;

View file

@ -0,0 +1,11 @@
namespace Ryujinx.OsHle.Svc
{
enum SvcResult
{
Success = 0,
ErrBadHandle = 0xe401,
ErrTimeout = 0xea01,
ErrBadInfo = 0xf001,
ErrBadIpcReq = 0xf601
}
}

View file

@ -1,5 +1,6 @@
using ChocolArm64.Memory; using ChocolArm64.Memory;
using ChocolArm64.State; using ChocolArm64.State;
using Ryujinx.OsHle.Exceptions;
using Ryujinx.OsHle.Handles; using Ryujinx.OsHle.Handles;
using Ryujinx.OsHle.Ipc; using Ryujinx.OsHle.Ipc;
using System; using System;
@ -8,7 +9,7 @@ namespace Ryujinx.OsHle.Svc
{ {
partial class SvcHandler partial class SvcHandler
{ {
private static void SvcCloseHandle(Switch Ns, ARegisters Registers, AMemory Memory) private void SvcCloseHandle(ARegisters Registers)
{ {
int Handle = (int)Registers.X0; int Handle = (int)Registers.X0;
@ -17,7 +18,7 @@ namespace Ryujinx.OsHle.Svc
Registers.X0 = (int)SvcResult.Success; Registers.X0 = (int)SvcResult.Success;
} }
private static void SvcResetSignal(Switch Ns, ARegisters Registers, AMemory Memory) private void SvcResetSignal(ARegisters Registers)
{ {
int Handle = (int)Registers.X0; int Handle = (int)Registers.X0;
@ -26,7 +27,7 @@ namespace Ryujinx.OsHle.Svc
Registers.X0 = (int)SvcResult.Success; Registers.X0 = (int)SvcResult.Success;
} }
private static void SvcWaitSynchronization(Switch Ns, ARegisters Registers, AMemory Memory) private void SvcWaitSynchronization(ARegisters Registers)
{ {
long HandlesPtr = (long)Registers.X0; long HandlesPtr = (long)Registers.X0;
int HandlesCount = (int)Registers.X2; int HandlesCount = (int)Registers.X2;
@ -34,15 +35,26 @@ namespace Ryujinx.OsHle.Svc
//TODO: Implement events. //TODO: Implement events.
//Logging.Info($"SvcWaitSynchronization Thread {Registers.ThreadId}");
if (Process.TryGetThread(Registers.Tpidr, out HThread Thread))
{
Process.Scheduler.Yield(Thread);
}
else
{
Logging.Error($"Thread with TPIDR_EL0 0x{Registers.Tpidr:x16} not found!");
}
Registers.X0 = (int)SvcResult.Success; Registers.X0 = (int)SvcResult.Success;
} }
private static void SvcGetSystemTick(Switch Ns, ARegisters Registers, AMemory Memory) private void SvcGetSystemTick(ARegisters Registers)
{ {
Registers.X0 = (ulong)Registers.CntpctEl0; Registers.X0 = (ulong)Registers.CntpctEl0;
} }
private static void SvcConnectToNamedPort(Switch Ns, ARegisters Registers, AMemory Memory) private void SvcConnectToNamedPort(ARegisters Registers)
{ {
long StackPtr = (long)Registers.X0; long StackPtr = (long)Registers.X0;
long NamePtr = (long)Registers.X1; long NamePtr = (long)Registers.X1;
@ -58,23 +70,23 @@ namespace Ryujinx.OsHle.Svc
Registers.X0 = (int)SvcResult.Success; Registers.X0 = (int)SvcResult.Success;
} }
private static void SvcSendSyncRequest(Switch Ns, ARegisters Registers, AMemory Memory) private void SvcSendSyncRequest(ARegisters Registers)
{ {
SendSyncRequest(Ns, Registers, Memory, false); SendSyncRequest(Registers, false);
} }
private static void SvcSendSyncRequestWithUserBuffer(Switch Ns, ARegisters Registers, AMemory Memory) private void SvcSendSyncRequestWithUserBuffer(ARegisters Registers)
{ {
SendSyncRequest(Ns, Registers, Memory, true); SendSyncRequest(Registers, true);
} }
private static void SendSyncRequest(Switch Ns, ARegisters Registers, AMemory Memory, bool IsUser) private void SendSyncRequest(ARegisters Registers, bool UserBuffer)
{ {
long CmdPtr = Registers.Tpidr; long CmdPtr = Registers.Tpidr;
long Size = 0x100; long Size = 0x100;
int Handle = 0; int Handle = 0;
if (IsUser) if (UserBuffer)
{ {
CmdPtr = (long)Registers.X0; CmdPtr = (long)Registers.X0;
Size = (long)Registers.X1; Size = (long)Registers.X1;
@ -105,16 +117,16 @@ namespace Ryujinx.OsHle.Svc
} }
} }
private static void SvcBreak(Switch Ns, ARegisters Registers, AMemory Memory) private void SvcBreak(ARegisters Registers)
{ {
long Reason = (long)Registers.X0; long Reason = (long)Registers.X0;
long Unknown = (long)Registers.X1; long Unknown = (long)Registers.X1;
long Info = (long)Registers.X2; long Info = (long)Registers.X2;
throw new Exception($"SvcBreak: {Reason} {Unknown} {Info}"); throw new GuestBrokeExecutionException();
} }
private static void SvcOutputDebugString(Switch Ns, ARegisters Registers, AMemory Memory) private void SvcOutputDebugString(ARegisters Registers)
{ {
long Position = (long)Registers.X0; long Position = (long)Registers.X0;
long Size = (long)Registers.X1; long Size = (long)Registers.X1;
@ -126,7 +138,7 @@ namespace Ryujinx.OsHle.Svc
Registers.X0 = (int)SvcResult.Success; Registers.X0 = (int)SvcResult.Success;
} }
private static void SvcGetInfo(Switch Ns, ARegisters Registers, AMemory Memory) private void SvcGetInfo(ARegisters Registers)
{ {
long StackPtr = (long)Registers.X0; long StackPtr = (long)Registers.X0;
int InfoType = (int)Registers.X1; int InfoType = (int)Registers.X1;
@ -148,8 +160,8 @@ namespace Ryujinx.OsHle.Svc
case 3: Registers.X1 = GetMapRegionSize(); break; case 3: Registers.X1 = GetMapRegionSize(); break;
case 4: Registers.X1 = GetHeapRegionBaseAddr(); break; case 4: Registers.X1 = GetHeapRegionBaseAddr(); break;
case 5: Registers.X1 = GetHeapRegionSize(); break; case 5: Registers.X1 = GetHeapRegionSize(); break;
case 6: Registers.X1 = GetTotalMem(Memory); break; case 6: Registers.X1 = GetTotalMem(); break;
case 7: Registers.X1 = GetUsedMem(Memory); break; case 7: Registers.X1 = GetUsedMem(); break;
case 11: Registers.X1 = GetRnd64(); break; case 11: Registers.X1 = GetRnd64(); break;
case 12: Registers.X1 = GetAddrSpaceBaseAddr(); break; case 12: Registers.X1 = GetAddrSpaceBaseAddr(); break;
case 13: Registers.X1 = GetAddrSpaceSize(); break; case 13: Registers.X1 = GetAddrSpaceSize(); break;
@ -162,47 +174,47 @@ namespace Ryujinx.OsHle.Svc
Registers.X0 = (int)SvcResult.Success; Registers.X0 = (int)SvcResult.Success;
} }
private static ulong GetTotalMem(AMemory Memory) private ulong GetTotalMem()
{ {
return (ulong)Memory.Manager.GetTotalMemorySize(); return (ulong)Memory.Manager.GetTotalMemorySize();
} }
private static ulong GetUsedMem(AMemory Memory) private ulong GetUsedMem()
{ {
return (ulong)Memory.Manager.GetUsedMemorySize(); return (ulong)Memory.Manager.GetUsedMemorySize();
} }
private static ulong GetRnd64() private ulong GetRnd64()
{ {
return (ulong)Rng.Next() + ((ulong)Rng.Next() << 32); return (ulong)Rng.Next() + ((ulong)Rng.Next() << 32);
} }
private static ulong GetAddrSpaceBaseAddr() private ulong GetAddrSpaceBaseAddr()
{ {
return 0x08000000; return 0x08000000;
} }
private static ulong GetAddrSpaceSize() private ulong GetAddrSpaceSize()
{ {
return AMemoryMgr.AddrSize - GetAddrSpaceBaseAddr(); return AMemoryMgr.AddrSize - GetAddrSpaceBaseAddr();
} }
private static ulong GetMapRegionBaseAddr() private ulong GetMapRegionBaseAddr()
{ {
return 0x80000000; return 0x80000000;
} }
private static ulong GetMapRegionSize() private ulong GetMapRegionSize()
{ {
return 0x40000000; return 0x40000000;
} }
private static ulong GetHeapRegionBaseAddr() private ulong GetHeapRegionBaseAddr()
{ {
return GetMapRegionBaseAddr() + GetMapRegionSize(); return GetMapRegionBaseAddr() + GetMapRegionSize();
} }
private static ulong GetHeapRegionSize() private ulong GetHeapRegionSize()
{ {
return 0x40000000; return 0x40000000;
} }

View file

@ -1,4 +1,3 @@
using ChocolArm64.Memory;
using ChocolArm64.State; using ChocolArm64.State;
using Ryujinx.OsHle.Handles; using Ryujinx.OsHle.Handles;
using System.Threading; using System.Threading;
@ -7,7 +6,7 @@ namespace Ryujinx.OsHle.Svc
{ {
partial class SvcHandler partial class SvcHandler
{ {
private static void SvcCreateThread(Switch Ns, ARegisters Registers, AMemory Memory) private void SvcCreateThread(ARegisters Registers)
{ {
long EntryPoint = (long)Registers.X1; long EntryPoint = (long)Registers.X1;
long ArgsPtr = (long)Registers.X2; long ArgsPtr = (long)Registers.X2;
@ -17,6 +16,11 @@ namespace Ryujinx.OsHle.Svc
if (Ns.Os.TryGetProcess(Registers.ProcessId, out Process Process)) if (Ns.Os.TryGetProcess(Registers.ProcessId, out Process Process))
{ {
if (ProcessorId == -2)
{
ProcessorId = 0;
}
int Handle = Process.MakeThread( int Handle = Process.MakeThread(
EntryPoint, EntryPoint,
StackTop, StackTop,
@ -31,15 +35,15 @@ namespace Ryujinx.OsHle.Svc
//TODO: Error codes. //TODO: Error codes.
} }
private static void SvcStartThread(Switch Ns, ARegisters Registers, AMemory Memory) private void SvcStartThread(ARegisters Registers)
{ {
int Handle = (int)Registers.X0; int Handle = (int)Registers.X0;
HThread HndData = Ns.Os.Handles.GetData<HThread>(Handle); HThread Thread = Ns.Os.Handles.GetData<HThread>(Handle);
if (HndData != null) if (Thread != null)
{ {
HndData.Thread.Execute(); Process.Scheduler.StartThread(Thread);
Registers.X0 = (int)SvcResult.Success; Registers.X0 = (int)SvcResult.Success;
} }
@ -47,29 +51,31 @@ namespace Ryujinx.OsHle.Svc
//TODO: Error codes. //TODO: Error codes.
} }
private static void SvcSleepThread(Switch Ns, ARegisters Registers, AMemory Memory) private void SvcSleepThread(ARegisters Registers)
{ {
ulong NanoSecs = Registers.X0; ulong NanoSecs = Registers.X0;
if (NanoSecs == 0) if (Process.TryGetThread(Registers.Tpidr, out HThread CurrThread))
{ {
Thread.Yield(); Process.Scheduler.Yield(CurrThread);
} }
else else
{ {
Thread.Sleep((int)(NanoSecs / 1000000)); Logging.Error($"Thread with TPIDR_EL0 0x{Registers.Tpidr:x16} not found!");
} }
Thread.Sleep((int)(NanoSecs / 1000000));
} }
private static void SvcGetThreadPriority(Switch Ns, ARegisters Registers, AMemory Memory) private void SvcGetThreadPriority(ARegisters Registers)
{ {
int Handle = (int)Registers.X1; int Handle = (int)Registers.X1;
HThread HndData = Ns.Os.Handles.GetData<HThread>(Handle); HThread Thread = Ns.Os.Handles.GetData<HThread>(Handle);
if (HndData != null) if (Thread != null)
{ {
Registers.X1 = (ulong)HndData.Thread.Priority; Registers.X1 = (ulong)Thread.Priority;
Registers.X0 = (int)SvcResult.Success; Registers.X0 = (int)SvcResult.Success;
} }

View file

@ -1,5 +1,4 @@
using ChocolArm64; using ChocolArm64;
using ChocolArm64.Memory;
using ChocolArm64.State; using ChocolArm64.State;
using Ryujinx.OsHle.Handles; using Ryujinx.OsHle.Handles;
@ -7,27 +6,24 @@ namespace Ryujinx.OsHle.Svc
{ {
partial class SvcHandler partial class SvcHandler
{ {
private static void SvcArbitrateLock(Switch Ns, ARegisters Registers, AMemory Memory) private void SvcArbitrateLock(ARegisters Registers)
{ {
int OwnerThreadHandle = (int)Registers.X0; int OwnerThreadHandle = (int)Registers.X0;
long MutexAddress = (long)Registers.X1; long MutexAddress = (long)Registers.X1;
int RequestingThreadHandle = (int)Registers.X2; int RequestingThreadHandle = (int)Registers.X2;
AThread RequestingThread = Ns.Os.Handles.GetData<HThread>(RequestingThreadHandle).Thread; HThread RequestingThread = Ns.Os.Handles.GetData<HThread>(RequestingThreadHandle);
Mutex M = new Mutex(Memory, MutexAddress); Mutex M = new Mutex(Process, MutexAddress, OwnerThreadHandle);
M = Ns.Os.Mutexes.GetOrAdd(MutexAddress, M); M = Ns.Os.Mutexes.GetOrAdd(MutexAddress, M);
//FIXME M.WaitForLock(RequestingThread, RequestingThreadHandle);
//M.WaitForLock(RequestingThread, RequestingThreadHandle);
Memory.WriteInt32(MutexAddress, 0);
Registers.X0 = (int)SvcResult.Success; Registers.X0 = (int)SvcResult.Success;
} }
private static void SvcArbitrateUnlock(Switch Ns, ARegisters Registers, AMemory Memory) private void SvcArbitrateUnlock(ARegisters Registers)
{ {
long MutexAddress = (long)Registers.X0; long MutexAddress = (long)Registers.X0;
@ -39,39 +35,36 @@ namespace Ryujinx.OsHle.Svc
Registers.X0 = (int)SvcResult.Success; Registers.X0 = (int)SvcResult.Success;
} }
private static void SvcWaitProcessWideKeyAtomic(Switch Ns, ARegisters Registers, AMemory Memory) private void SvcWaitProcessWideKeyAtomic(ARegisters Registers)
{ {
long MutexAddress = (long)Registers.X0; long MutexAddress = (long)Registers.X0;
long CondVarAddress = (long)Registers.X1; long CondVarAddress = (long)Registers.X1;
int ThreadHandle = (int)Registers.X2; int ThreadHandle = (int)Registers.X2;
long Timeout = (long)Registers.X3; long Timeout = (long)Registers.X3;
AThread Thread = Ns.Os.Handles.GetData<HThread>(ThreadHandle).Thread; HThread Thread = Ns.Os.Handles.GetData<HThread>(ThreadHandle);
if (Ns.Os.Mutexes.TryGetValue(MutexAddress, out Mutex M)) if (Ns.Os.Mutexes.TryGetValue(MutexAddress, out Mutex M))
{ {
M.GiveUpLock(ThreadHandle); M.GiveUpLock(ThreadHandle);
} }
CondVar Signal = new CondVar(Memory, CondVarAddress, Timeout); CondVar Cv = new CondVar(Process, CondVarAddress, Timeout);
Signal = Ns.Os.CondVars.GetOrAdd(CondVarAddress, Signal); Cv = Ns.Os.CondVars.GetOrAdd(CondVarAddress, Cv);
Signal.WaitForSignal(ThreadHandle); Cv.WaitForSignal(Thread);
M = new Mutex(Memory, MutexAddress); M = new Mutex(Process, MutexAddress, ThreadHandle);
M = Ns.Os.Mutexes.GetOrAdd(MutexAddress, M); M = Ns.Os.Mutexes.GetOrAdd(MutexAddress, M);
//FIXME M.WaitForLock(Thread, ThreadHandle);
//M.WaitForLock(Thread, ThreadHandle);
Memory.WriteInt32(MutexAddress, 0);
Registers.X0 = (int)SvcResult.Success; Registers.X0 = (int)SvcResult.Success;
} }
private static void SvcSignalProcessWideKey(Switch Ns, ARegisters Registers, AMemory Memory) private void SvcSignalProcessWideKey(ARegisters Registers)
{ {
long CondVarAddress = (long)Registers.X0; long CondVarAddress = (long)Registers.X0;
int Count = (int)Registers.X1; int Count = (int)Registers.X1;