From 65105c2a3bf136202decd5ec1f5d9626d636033b Mon Sep 17 00:00:00 2001
From: gdkchan <gab.dark.100@gmail.com>
Date: Tue, 26 Jun 2018 01:09:32 -0300
Subject: [PATCH] Implement SvcGetThreadContext3

---
 ChocolArm64/Events/AInstExceptionEventArgs.cs |   8 +-
 ChocolArm64/Instruction/AInstEmitException.cs |   1 +
 ChocolArm64/State/AThreadState.cs             |  19 ++-
 .../OsHle/Handles/KProcessScheduler.cs        |  25 +++-
 Ryujinx.HLE/OsHle/Handles/KThread.cs          |   2 +
 Ryujinx.HLE/OsHle/Handles/SchedulerThread.cs  |   2 +
 Ryujinx.HLE/OsHle/Kernel/KernelErr.cs         |   2 +
 Ryujinx.HLE/OsHle/Kernel/SvcHandler.cs        |   3 +-
 Ryujinx.HLE/OsHle/Kernel/SvcThread.cs         | 111 ++++++++++++++++++
 Ryujinx.HLE/OsHle/Process.cs                  |   2 +
 10 files changed, 163 insertions(+), 12 deletions(-)

diff --git a/ChocolArm64/Events/AInstExceptionEventArgs.cs b/ChocolArm64/Events/AInstExceptionEventArgs.cs
index 34f90c8eec..a6853ea10e 100644
--- a/ChocolArm64/Events/AInstExceptionEventArgs.cs
+++ b/ChocolArm64/Events/AInstExceptionEventArgs.cs
@@ -4,11 +4,13 @@ namespace ChocolArm64.Events
 {
     public class AInstExceptionEventArgs : EventArgs
     {
-        public int Id { get; private set; }
+        public long Position { get; private set; }
+        public int  Id       { get; private set; }
 
-        public AInstExceptionEventArgs(int Id)
+        public AInstExceptionEventArgs(long Position, int Id)
         {
-            this.Id = Id;
+            this.Position = Position;
+            this.Id       = Id;
         }
     }
 }
\ No newline at end of file
diff --git a/ChocolArm64/Instruction/AInstEmitException.cs b/ChocolArm64/Instruction/AInstEmitException.cs
index 3e444c7304..73d2096732 100644
--- a/ChocolArm64/Instruction/AInstEmitException.cs
+++ b/ChocolArm64/Instruction/AInstEmitException.cs
@@ -25,6 +25,7 @@ namespace ChocolArm64.Instruction
 
             Context.EmitLdarg(ATranslatedSub.StateArgIdx);
 
+            Context.EmitLdc_I8(Op.Position);
             Context.EmitLdc_I4(Op.Id);
 
             Context.EmitPrivateCall(typeof(AThreadState), MthdName);
diff --git a/ChocolArm64/State/AThreadState.cs b/ChocolArm64/State/AThreadState.cs
index a93e4cf925..a84e3242bf 100644
--- a/ChocolArm64/State/AThreadState.cs
+++ b/ChocolArm64/State/AThreadState.cs
@@ -51,6 +51,17 @@ namespace ChocolArm64.State
         public int Fpcr { get; set; }
         public int Fpsr { get; set; }
 
+        public int Psr
+        {
+            get
+            {
+                return (Negative ? (int)APState.N : 0) |
+                       (Zero     ? (int)APState.Z : 0) |
+                       (Carry    ? (int)APState.C : 0) |
+                       (Overflow ? (int)APState.V : 0);
+            }
+        }
+
         public uint CtrEl0   => 0x8444c004;
         public uint DczidEl0 => 0x00000004;
 
@@ -89,14 +100,14 @@ namespace ChocolArm64.State
             TickCounter.Start();
         }
 
-        internal void OnBreak(int Imm)
+        internal void OnBreak(long Position, int Imm)
         {
-            Break?.Invoke(this, new AInstExceptionEventArgs(Imm));
+            Break?.Invoke(this, new AInstExceptionEventArgs(Position, Imm));
         }
 
-        internal void OnSvcCall(int Imm)
+        internal void OnSvcCall(long Position, int Imm)
         {
-            SvcCall?.Invoke(this, new AInstExceptionEventArgs(Imm));
+            SvcCall?.Invoke(this, new AInstExceptionEventArgs(Position, Imm));
         }
 
         internal void OnUndefined(long Position, int RawOpCode)
diff --git a/Ryujinx.HLE/OsHle/Handles/KProcessScheduler.cs b/Ryujinx.HLE/OsHle/Handles/KProcessScheduler.cs
index 722460b1ef..011d975426 100644
--- a/Ryujinx.HLE/OsHle/Handles/KProcessScheduler.cs
+++ b/Ryujinx.HLE/OsHle/Handles/KProcessScheduler.cs
@@ -47,6 +47,8 @@ namespace Ryujinx.HLE.OsHle.Handles
 
                 if (TryAddToCore(Thread))
                 {
+                    SchedThread.IsRunning = true;
+
                     Thread.Thread.Execute();
 
                     PrintDbgThreadInfo(Thread, "running.");
@@ -94,10 +96,7 @@ namespace Ryujinx.HLE.OsHle.Handles
 
         public void SetThreadActivity(KThread Thread, bool Active)
         {
-            if (!AllThreads.TryGetValue(Thread, out SchedulerThread SchedThread))
-            {
-                throw new InvalidOperationException();
-            }
+            SchedulerThread SchedThread = AllThreads[Thread];
 
             SchedThread.IsActive = Active;
 
@@ -111,6 +110,16 @@ namespace Ryujinx.HLE.OsHle.Handles
             }
         }
 
+        public bool IsThreadRunning(KThread Thread)
+        {
+            if (!AllThreads.TryGetValue(Thread, out SchedulerThread SchedThread))
+            {
+                return false;
+            }
+
+            return SchedThread.IsRunning;
+        }
+
         public void EnterWait(KThread Thread, int TimeoutMs = Timeout.Infinite)
         {
             SchedulerThread SchedThread = AllThreads[Thread];
@@ -161,6 +170,8 @@ namespace Ryujinx.HLE.OsHle.Handles
         {
             lock (SchedLock)
             {
+                AllThreads[Thread].IsRunning = false;
+
                 PrintDbgThreadInfo(Thread, "suspended.");
 
                 int ActualCore = Thread.ActualCore;
@@ -252,6 +263,8 @@ namespace Ryujinx.HLE.OsHle.Handles
 
         private void TryResumingExecution(SchedulerThread SchedThread)
         {
+            SchedThread.IsRunning = false;
+
             KThread Thread = SchedThread.Thread;
 
             PrintDbgThreadInfo(Thread, "trying to resume...");
@@ -262,6 +275,8 @@ namespace Ryujinx.HLE.OsHle.Handles
             {
                 if (TryAddToCore(Thread))
                 {
+                    SchedThread.IsRunning = true;
+
                     PrintDbgThreadInfo(Thread, "resuming execution...");
 
                     return;
@@ -291,6 +306,8 @@ namespace Ryujinx.HLE.OsHle.Handles
             {
                 PrintDbgThreadInfo(SchedThread.Thread, "running.");
             }
+
+            SchedThread.IsRunning = true;
         }
 
         public void Resort(KThread Thread)
diff --git a/Ryujinx.HLE/OsHle/Handles/KThread.cs b/Ryujinx.HLE/OsHle/Handles/KThread.cs
index a135bb282b..3db46f3d67 100644
--- a/Ryujinx.HLE/OsHle/Handles/KThread.cs
+++ b/Ryujinx.HLE/OsHle/Handles/KThread.cs
@@ -29,6 +29,8 @@ namespace Ryujinx.HLE.OsHle.Handles
 
         public int WaitHandle { get; set; }
 
+        public long LastPc { get; set; }
+
         public int ThreadId => Thread.ThreadId;
 
         public KThread(
diff --git a/Ryujinx.HLE/OsHle/Handles/SchedulerThread.cs b/Ryujinx.HLE/OsHle/Handles/SchedulerThread.cs
index 5bdefe74e9..0b7a3c30f7 100644
--- a/Ryujinx.HLE/OsHle/Handles/SchedulerThread.cs
+++ b/Ryujinx.HLE/OsHle/Handles/SchedulerThread.cs
@@ -11,6 +11,8 @@ namespace Ryujinx.HLE.OsHle.Handles
 
         public bool IsActive { get; set; }
 
+        public bool IsRunning { get; set; }
+
         public AutoResetEvent   WaitSync     { get; private set; }
         public ManualResetEvent WaitActivity { get; private set; }
         public AutoResetEvent   WaitSched    { get; private set; }
diff --git a/Ryujinx.HLE/OsHle/Kernel/KernelErr.cs b/Ryujinx.HLE/OsHle/Kernel/KernelErr.cs
index 643e5f687b..ad4fdfb6bd 100644
--- a/Ryujinx.HLE/OsHle/Kernel/KernelErr.cs
+++ b/Ryujinx.HLE/OsHle/Kernel/KernelErr.cs
@@ -13,5 +13,7 @@ namespace Ryujinx.HLE.OsHle.Kernel
         public const int Canceled         = 118;
         public const int CountOutOfRange  = 119;
         public const int InvalidInfo      = 120;
+        public const int InvalidThread    = 122;
+        public const int InvalidState     = 125;
     }
 }
\ No newline at end of file
diff --git a/Ryujinx.HLE/OsHle/Kernel/SvcHandler.cs b/Ryujinx.HLE/OsHle/Kernel/SvcHandler.cs
index f3a5fae03a..5b6279e3d6 100644
--- a/Ryujinx.HLE/OsHle/Kernel/SvcHandler.cs
+++ b/Ryujinx.HLE/OsHle/Kernel/SvcHandler.cs
@@ -72,7 +72,8 @@ namespace Ryujinx.HLE.OsHle.Kernel
                 { 0x29, SvcGetInfo                       },
                 { 0x2c, SvcMapPhysicalMemory             },
                 { 0x2d, SvcUnmapPhysicalMemory           },
-                { 0x32, SvcSetThreadActivity             }
+                { 0x32, SvcSetThreadActivity             },
+                { 0x33, SvcGetThreadContext3             }
             };
 
             this.Ns      = Ns;
diff --git a/Ryujinx.HLE/OsHle/Kernel/SvcThread.cs b/Ryujinx.HLE/OsHle/Kernel/SvcThread.cs
index 4501867f42..543d9fd2c9 100644
--- a/Ryujinx.HLE/OsHle/Kernel/SvcThread.cs
+++ b/Ryujinx.HLE/OsHle/Kernel/SvcThread.cs
@@ -280,5 +280,116 @@ namespace Ryujinx.HLE.OsHle.Kernel
                 ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidHandle);
             }
         }
+
+        private void SvcGetThreadContext3(AThreadState ThreadState)
+        {
+            long Position = (long)ThreadState.X0;
+            int  Handle   =  (int)ThreadState.X1;
+
+            KThread Thread = Process.HandleTable.GetData<KThread>(Handle);
+
+            if (Thread == null)
+            {
+                Ns.Log.PrintWarning(LogClass.KernelSvc, $"Invalid thread handle 0x{Handle:x8}!");
+
+                ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidHandle);
+
+                return;
+            }
+
+            if (Process.GetThread(ThreadState.Tpidr) == Thread)
+            {
+                Ns.Log.PrintWarning(LogClass.KernelSvc, $"Thread handle 0x{Handle:x8} is current thread!");
+
+                ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidThread);
+
+                return;
+            }
+
+            if (Process.Scheduler.IsThreadRunning(Thread))
+            {
+                Ns.Log.PrintWarning(LogClass.KernelSvc, $"Thread handle 0x{Handle:x8} is running!");
+
+                ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidState);
+
+                return;
+            }
+
+            Memory.WriteUInt64(Position + 0x0,  ThreadState.X0);
+            Memory.WriteUInt64(Position + 0x8,  ThreadState.X1);
+            Memory.WriteUInt64(Position + 0x10, ThreadState.X2);
+            Memory.WriteUInt64(Position + 0x18, ThreadState.X3);
+            Memory.WriteUInt64(Position + 0x20, ThreadState.X4);
+            Memory.WriteUInt64(Position + 0x28, ThreadState.X5);
+            Memory.WriteUInt64(Position + 0x30, ThreadState.X6);
+            Memory.WriteUInt64(Position + 0x38, ThreadState.X7);
+            Memory.WriteUInt64(Position + 0x40, ThreadState.X8);
+            Memory.WriteUInt64(Position + 0x48, ThreadState.X9);
+            Memory.WriteUInt64(Position + 0x50, ThreadState.X10);
+            Memory.WriteUInt64(Position + 0x58, ThreadState.X11);
+            Memory.WriteUInt64(Position + 0x60, ThreadState.X12);
+            Memory.WriteUInt64(Position + 0x68, ThreadState.X13);
+            Memory.WriteUInt64(Position + 0x70, ThreadState.X14);
+            Memory.WriteUInt64(Position + 0x78, ThreadState.X15);
+            Memory.WriteUInt64(Position + 0x80, ThreadState.X16);
+            Memory.WriteUInt64(Position + 0x88, ThreadState.X17);
+            Memory.WriteUInt64(Position + 0x90, ThreadState.X18);
+            Memory.WriteUInt64(Position + 0x98, ThreadState.X19);
+            Memory.WriteUInt64(Position + 0xa0, ThreadState.X20);
+            Memory.WriteUInt64(Position + 0xa8, ThreadState.X21);
+            Memory.WriteUInt64(Position + 0xb0, ThreadState.X22);
+            Memory.WriteUInt64(Position + 0xb8, ThreadState.X23);
+            Memory.WriteUInt64(Position + 0xc0, ThreadState.X24);
+            Memory.WriteUInt64(Position + 0xc8, ThreadState.X25);
+            Memory.WriteUInt64(Position + 0xd0, ThreadState.X26);
+            Memory.WriteUInt64(Position + 0xd8, ThreadState.X27);
+            Memory.WriteUInt64(Position + 0xe0, ThreadState.X28);
+            Memory.WriteUInt64(Position + 0xe8, ThreadState.X29);
+            Memory.WriteUInt64(Position + 0xf0, ThreadState.X30);
+            Memory.WriteUInt64(Position + 0xf8, ThreadState.X31);
+
+            Memory.WriteInt64(Position + 0x100, Thread.LastPc);
+
+            Memory.WriteUInt64(Position + 0x108, (ulong)ThreadState.Psr);
+
+            Memory.WriteVector128(Position + 0x110, ThreadState.V0);
+            Memory.WriteVector128(Position + 0x120, ThreadState.V1);
+            Memory.WriteVector128(Position + 0x130, ThreadState.V2);
+            Memory.WriteVector128(Position + 0x140, ThreadState.V3);
+            Memory.WriteVector128(Position + 0x150, ThreadState.V4);
+            Memory.WriteVector128(Position + 0x160, ThreadState.V5);
+            Memory.WriteVector128(Position + 0x170, ThreadState.V6);
+            Memory.WriteVector128(Position + 0x180, ThreadState.V7);
+            Memory.WriteVector128(Position + 0x190, ThreadState.V8);
+            Memory.WriteVector128(Position + 0x1a0, ThreadState.V9);
+            Memory.WriteVector128(Position + 0x1b0, ThreadState.V10);
+            Memory.WriteVector128(Position + 0x1c0, ThreadState.V11);
+            Memory.WriteVector128(Position + 0x1d0, ThreadState.V12);
+            Memory.WriteVector128(Position + 0x1e0, ThreadState.V13);
+            Memory.WriteVector128(Position + 0x1f0, ThreadState.V14);
+            Memory.WriteVector128(Position + 0x200, ThreadState.V15);
+            Memory.WriteVector128(Position + 0x210, ThreadState.V16);
+            Memory.WriteVector128(Position + 0x220, ThreadState.V17);
+            Memory.WriteVector128(Position + 0x230, ThreadState.V18);
+            Memory.WriteVector128(Position + 0x240, ThreadState.V19);
+            Memory.WriteVector128(Position + 0x250, ThreadState.V20);
+            Memory.WriteVector128(Position + 0x260, ThreadState.V21);
+            Memory.WriteVector128(Position + 0x270, ThreadState.V22);
+            Memory.WriteVector128(Position + 0x280, ThreadState.V23);
+            Memory.WriteVector128(Position + 0x290, ThreadState.V24);
+            Memory.WriteVector128(Position + 0x2a0, ThreadState.V25);
+            Memory.WriteVector128(Position + 0x2b0, ThreadState.V26);
+            Memory.WriteVector128(Position + 0x2c0, ThreadState.V27);
+            Memory.WriteVector128(Position + 0x2d0, ThreadState.V28);
+            Memory.WriteVector128(Position + 0x2e0, ThreadState.V29);
+            Memory.WriteVector128(Position + 0x2f0, ThreadState.V30);
+            Memory.WriteVector128(Position + 0x300, ThreadState.V31);
+
+            Memory.WriteInt32(Position + 0x310, ThreadState.Fpcr);
+            Memory.WriteInt32(Position + 0x314, ThreadState.Fpsr);
+            Memory.WriteInt64(Position + 0x318, ThreadState.Tpidr);
+
+            ThreadState.X0 = 0;
+        }
     }
 }
\ No newline at end of file
diff --git a/Ryujinx.HLE/OsHle/Process.cs b/Ryujinx.HLE/OsHle/Process.cs
index f01a092787..53e357ab9a 100644
--- a/Ryujinx.HLE/OsHle/Process.cs
+++ b/Ryujinx.HLE/OsHle/Process.cs
@@ -205,6 +205,8 @@ namespace Ryujinx.HLE.OsHle
 
             KThread Thread = new KThread(CpuThread, this, ProcessorId, Priority);
 
+            Thread.LastPc = EntryPoint;
+
             int Handle = HandleTable.OpenHandle(Thread);
 
             int ThreadId = GetFreeTlsSlot(CpuThread);