From 03002f6537e3208e6951bc9092e958985e200c7d Mon Sep 17 00:00:00 2001
From: gdkchan <gab.dark.100@gmail.com>
Date: Thu, 19 Apr 2018 16:18:30 -0300
Subject: [PATCH] Add SvcSetThreadActivity, tweak SignalProcessWideKey, add
 fmul32i shader instructions and other small fixes

---
 ChocolArm64/AThread.cs                        | 10 ++-
 .../OsHle/Handles/KProcessScheduler.cs        | 85 ++++++++++++++++---
 .../OsHle/Kernel/ConditionVariable.cs         | 21 +++--
 Ryujinx.Core/OsHle/Kernel/SvcHandler.cs       |  3 +-
 Ryujinx.Core/OsHle/Kernel/SvcThread.cs        | 23 ++++-
 Ryujinx.Graphics/Gal/Shader/GlslDecompiler.cs |  2 +-
 .../Gal/Shader/ShaderDecodeAlu.cs             | 13 ++-
 .../Gal/Shader/ShaderDecodeHelper.cs          |  5 ++
 .../Gal/Shader/ShaderOpCodeTable.cs           |  1 +
 9 files changed, 139 insertions(+), 24 deletions(-)

diff --git a/ChocolArm64/AThread.cs b/ChocolArm64/AThread.cs
index 62f9d2d396..16804a7c51 100644
--- a/ChocolArm64/AThread.cs
+++ b/ChocolArm64/AThread.cs
@@ -54,6 +54,14 @@ namespace ChocolArm64
             return true;
         }
 
-        public void StopExecution() => ThreadState.Running = false;
+        public void StopExecution()
+        {
+            ThreadState.Running = false;
+        }
+
+        public bool IsCurrentThread()
+        {
+            return Thread.CurrentThread == Work;
+        }
     }
 }
\ No newline at end of file
diff --git a/Ryujinx.Core/OsHle/Handles/KProcessScheduler.cs b/Ryujinx.Core/OsHle/Handles/KProcessScheduler.cs
index 1300d2185e..2f69460018 100644
--- a/Ryujinx.Core/OsHle/Handles/KProcessScheduler.cs
+++ b/Ryujinx.Core/OsHle/Handles/KProcessScheduler.cs
@@ -15,11 +15,15 @@ namespace Ryujinx.Core.OsHle.Handles
 
             public AutoResetEvent WaitEvent { get; private set; }
 
+            public bool Active { get; set; }
+
             public SchedulerThread(KThread Thread)
             {
                 this.Thread = Thread;
 
                 WaitEvent = new AutoResetEvent(false);
+
+                Active = true;
             }
 
             public void Dispose()
@@ -98,7 +102,10 @@ namespace Ryujinx.Core.OsHle.Handles
 
             public bool Remove(SchedulerThread SchedThread)
             {
-                return Threads.Remove(SchedThread);
+                lock (Threads)
+                {
+                    return Threads.Remove(SchedThread);
+                }
             }
         }
 
@@ -180,6 +187,55 @@ namespace Ryujinx.Core.OsHle.Handles
             }
         }
 
+        public void SetThreadActivity(KThread Thread, bool Active)
+        {
+            if (!AllThreads.TryGetValue(Thread, out SchedulerThread SchedThread))
+            {
+                throw new InvalidOperationException();
+            }
+
+            lock (SchedLock)
+            {
+                bool OldState = SchedThread.Active;
+
+                SchedThread.Active = Active;
+
+                if (!OldState && Active)
+                {
+                    if (ActiveProcessors.Add(Thread.ProcessorId))
+                    {
+                        RunThread(SchedThread);
+                    }
+                    else
+                    {
+                        WaitingToRun[Thread.ProcessorId].Push(SchedThread);
+
+                        PrintDbgThreadInfo(Thread, "entering wait state...");
+                    }
+                }
+                else if (OldState && !Active)
+                {
+                    if (Thread.Thread.IsCurrentThread())
+                    {
+                        Suspend(Thread.ProcessorId);
+
+                        PrintDbgThreadInfo(Thread, "entering inactive wait state...");
+                    }
+                    else
+                    {
+                        WaitingToRun[Thread.ProcessorId].Remove(SchedThread);
+                    }
+                }
+            }
+
+            if (!Active && Thread.Thread.IsCurrentThread())
+            {
+                SchedThread.WaitEvent.WaitOne();
+
+                PrintDbgThreadInfo(Thread, "resuming execution...");
+            }
+        }
+
         public void Suspend(int ProcessorId)
         {
             lock (SchedLock)
@@ -222,9 +278,7 @@ namespace Ryujinx.Core.OsHle.Handles
 
         public void Resume(KThread Thread)
         {
-            SchedulerThread SchedThread;
-
-            if (!AllThreads.TryGetValue(Thread, out SchedThread))
+            if (!AllThreads.TryGetValue(Thread, out SchedulerThread SchedThread))
             {
                 throw new InvalidOperationException();
             }
@@ -236,18 +290,25 @@ namespace Ryujinx.Core.OsHle.Handles
         {
             KThread Thread = SchedThread.Thread;
 
-            lock (SchedLock)
+            if (SchedThread.Active)
             {
-                if (ActiveProcessors.Add(Thread.ProcessorId))
+                lock (SchedLock)
                 {
-                    PrintDbgThreadInfo(Thread, "resuming execution...");
+                    if (ActiveProcessors.Add(Thread.ProcessorId))
+                    {
+                        PrintDbgThreadInfo(Thread, "resuming execution...");
 
-                    return;
+                        return;
+                    }
+
+                    WaitingToRun[Thread.ProcessorId].Push(SchedThread);
+
+                    PrintDbgThreadInfo(Thread, "entering wait state...");
                 }
-
-                PrintDbgThreadInfo(Thread, "entering wait state...");
-
-                WaitingToRun[Thread.ProcessorId].Push(SchedThread);
+            }
+            else
+            {
+                PrintDbgThreadInfo(Thread, "entering inactive wait state...");
             }
 
             SchedThread.WaitEvent.WaitOne();
diff --git a/Ryujinx.Core/OsHle/Kernel/ConditionVariable.cs b/Ryujinx.Core/OsHle/Kernel/ConditionVariable.cs
index 91ed91587c..f765737620 100644
--- a/Ryujinx.Core/OsHle/Kernel/ConditionVariable.cs
+++ b/Ryujinx.Core/OsHle/Kernel/ConditionVariable.cs
@@ -57,7 +57,7 @@ namespace Ryujinx.Core.OsHle.Kernel
 
             Count = Process.Memory.ReadInt32(CondVarAddress);
 
-            if (Count > 0)
+            if (Result && Count > 0)
             {
                 Process.Memory.WriteInt32(CondVarAddress, Count - 1);
             }
@@ -73,10 +73,10 @@ namespace Ryujinx.Core.OsHle.Kernel
             {
                 if (Count < 0)
                 {
-                    Process.Memory.WriteInt32(CondVarAddress, WaitingThreads.Count);
-
                     foreach ((_, AutoResetEvent WaitEvent) in WaitingThreads)
                     {
+                        IncrementCondVarValue();
+
                         WaitEvent.Set();
                     }
 
@@ -84,8 +84,6 @@ namespace Ryujinx.Core.OsHle.Kernel
                 }
                 else
                 {
-                    Process.Memory.WriteInt32(CondVarAddress, Count);
-
                     while (WaitingThreads.Count > 0 && Count-- > 0)
                     {
                         int HighestPriority  = WaitingThreads[0].Thread.Priority;
@@ -101,6 +99,8 @@ namespace Ryujinx.Core.OsHle.Kernel
                             }
                         }
 
+                        IncrementCondVarValue();
+
                         WaitingThreads[HighestPrioIndex].WaitEvent.Set();
 
                         WaitingThreads.RemoveAt(HighestPrioIndex);
@@ -111,6 +111,17 @@ namespace Ryujinx.Core.OsHle.Kernel
             Process.Scheduler.Yield(Thread);
         }
 
+        private void IncrementCondVarValue()
+        {
+            AcquireCondVarValue();
+
+            int Count = Process.Memory.ReadInt32(CondVarAddress);
+
+            Process.Memory.WriteInt32(CondVarAddress, Count + 1);
+
+            ReleaseCondVarValue();
+        }
+
         private void AcquireCondVarValue()
         {
             if (!OwnsCondVarValue)
diff --git a/Ryujinx.Core/OsHle/Kernel/SvcHandler.cs b/Ryujinx.Core/OsHle/Kernel/SvcHandler.cs
index fa772988fe..16ef86978a 100644
--- a/Ryujinx.Core/OsHle/Kernel/SvcHandler.cs
+++ b/Ryujinx.Core/OsHle/Kernel/SvcHandler.cs
@@ -63,7 +63,8 @@ namespace Ryujinx.Core.OsHle.Kernel
                 { 0x25, SvcGetThreadId                   },
                 { 0x26, SvcBreak                         },
                 { 0x27, SvcOutputDebugString             },
-                { 0x29, SvcGetInfo                       }
+                { 0x29, SvcGetInfo                       },
+                { 0x32, SvcSetThreadActivity             }
             };
 
             this.Ns      = Ns;
diff --git a/Ryujinx.Core/OsHle/Kernel/SvcThread.cs b/Ryujinx.Core/OsHle/Kernel/SvcThread.cs
index 2534c9d99d..06147b28ff 100644
--- a/Ryujinx.Core/OsHle/Kernel/SvcThread.cs
+++ b/Ryujinx.Core/OsHle/Kernel/SvcThread.cs
@@ -147,7 +147,28 @@ namespace Ryujinx.Core.OsHle.Kernel
             }
             else
             {
-                Logging.Warn(LogClass.KernelSvc, $"Tried to GetThreadId on invalid thread handle 0x{Handle:x8}!");
+                Logging.Warn(LogClass.KernelSvc, $"Invalid thread handle 0x{Handle:x8}!");
+
+                ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidHandle);
+            }
+        }
+
+        private void SvcSetThreadActivity(AThreadState ThreadState)
+        {
+            int  Handle = (int)ThreadState.X0;
+            bool Active = (int)ThreadState.X1 != 0;
+
+            KThread CurrThread = Process.HandleTable.GetData<KThread>(Handle);
+
+            if (CurrThread != null)
+            {
+                Process.Scheduler.SetThreadActivity(CurrThread, Active);
+
+                ThreadState.X0 = 0;
+            }
+            else
+            {
+                Logging.Warn(LogClass.KernelSvc, $"Invalid thread handle 0x{Handle:x8}!");
 
                 ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidHandle);
             }
diff --git a/Ryujinx.Graphics/Gal/Shader/GlslDecompiler.cs b/Ryujinx.Graphics/Gal/Shader/GlslDecompiler.cs
index 457192dc2a..d7173bcd63 100644
--- a/Ryujinx.Graphics/Gal/Shader/GlslDecompiler.cs
+++ b/Ryujinx.Graphics/Gal/Shader/GlslDecompiler.cs
@@ -193,7 +193,7 @@ namespace Ryujinx.Graphics.Gal.Shader
                 }
                 else if (DeclInfo.Name == GlslDecl.FragmentOutputName)
                 {
-                    Name = "layout (location = 0) out " + GetDecl(DeclInfo) + ";";
+                    Name = "layout (location = 0) out " + GetDecl(DeclInfo) + ";" + Environment.NewLine;
                 }
                 else
                 {
diff --git a/Ryujinx.Graphics/Gal/Shader/ShaderDecodeAlu.cs b/Ryujinx.Graphics/Gal/Shader/ShaderDecodeAlu.cs
index b796ab2882..830aeb8cb1 100644
--- a/Ryujinx.Graphics/Gal/Shader/ShaderDecodeAlu.cs
+++ b/Ryujinx.Graphics/Gal/Shader/ShaderDecodeAlu.cs
@@ -41,6 +41,16 @@ namespace Ryujinx.Graphics.Gal.Shader
             EmitAluFfma(Block, OpCode, ShaderOper.RR);
         }
 
+        public static void Fmul32i(ShaderIrBlock Block, long OpCode)
+        {
+            ShaderIrNode OperA = GetOperGpr8     (OpCode);
+            ShaderIrNode OperB = GetOperImmf32_20(OpCode);
+
+            ShaderIrOp Op = new ShaderIrOp(ShaderIrInst.Fmul, OperA, OperB);
+
+            Block.AddNode(GetPredNode(new ShaderIrAsg(GetOperGpr0(OpCode), Op), OpCode));
+        }
+
         public static void Fmul_C(ShaderIrBlock Block, long OpCode)
         {
             EmitAluBinaryF(Block, OpCode, ShaderOper.CR, ShaderIrInst.Fmul);
@@ -212,7 +222,6 @@ namespace Ryujinx.Graphics.Gal.Shader
             bool Aa = ((OpCode >> 46) & 1) != 0;
             bool Na = ((OpCode >> 48) & 1) != 0;
             bool Ab = ((OpCode >> 49) & 1) != 0;
-            bool Ad = ((OpCode >> 50) & 1) != 0;
 
             ShaderIrNode OperA = GetOperGpr8(OpCode), OperB;
 
@@ -234,8 +243,6 @@ namespace Ryujinx.Graphics.Gal.Shader
 
             ShaderIrNode Op = new ShaderIrOp(Inst, OperA, OperB);
 
-            Op = GetAluAbs(Op, Ad);
-
             Block.AddNode(GetPredNode(new ShaderIrAsg(GetOperGpr0(OpCode), Op), OpCode));
         }
 
diff --git a/Ryujinx.Graphics/Gal/Shader/ShaderDecodeHelper.cs b/Ryujinx.Graphics/Gal/Shader/ShaderDecodeHelper.cs
index de932dce62..efbce20f22 100644
--- a/Ryujinx.Graphics/Gal/Shader/ShaderDecodeHelper.cs
+++ b/Ryujinx.Graphics/Gal/Shader/ShaderDecodeHelper.cs
@@ -70,6 +70,11 @@ namespace Ryujinx.Graphics.Gal.Shader
             return new ShaderIrOperImm((int)(OpCode >> 20));
         }
 
+        public static ShaderIrOperImmf GetOperImmf32_20(long OpCode)
+        {
+            return new ShaderIrOperImmf(BitConverter.Int32BitsToSingle((int)(OpCode >> 20)));
+        }
+
         public static ShaderIrOperImm GetOperImm19_20(long OpCode)
         {
             int Value = (int)(OpCode >> 20) & 0x7ffff;
diff --git a/Ryujinx.Graphics/Gal/Shader/ShaderOpCodeTable.cs b/Ryujinx.Graphics/Gal/Shader/ShaderOpCodeTable.cs
index a234f7f74f..762544cb90 100644
--- a/Ryujinx.Graphics/Gal/Shader/ShaderOpCodeTable.cs
+++ b/Ryujinx.Graphics/Gal/Shader/ShaderOpCodeTable.cs
@@ -27,6 +27,7 @@ namespace Ryujinx.Graphics.Gal.Shader
             Set("001100101xxxxx", ShaderDecode.Ffma_I);
             Set("010100011xxxxx", ShaderDecode.Ffma_RC);
             Set("010110011xxxxx", ShaderDecode.Ffma_RR);
+            Set("00011110xxxxxx", ShaderDecode.Fmul32i);
             Set("0100110001101x", ShaderDecode.Fmul_C);
             Set("0011100x01101x", ShaderDecode.Fmul_I);
             Set("0101110001101x", ShaderDecode.Fmul_R);