From 035efc913e7f2a68315d4206cef46672005b8442 Mon Sep 17 00:00:00 2001
From: gdkchan <gab.dark.100@gmail.com>
Date: Sat, 24 Feb 2018 11:19:28 -0300
Subject: [PATCH] Fix cpu issue with cmp optimization, add HINT and FRINTX
 (scalar) instructions, fix for NvFlinger sometimes missing free buffers

---
 ChocolArm64/AOpCodeTable.cs                   |  2 ++
 ChocolArm64/ATranslator.cs                    |  6 ++---
 ChocolArm64/Decoder/AOpCodeSimd.cs            |  2 --
 .../Instruction/AInstEmitSimdArithmetic.cs    | 26 +++++++++++++++++++
 ChocolArm64/Instruction/AInstEmitSystem.cs    |  5 ++++
 ChocolArm64/Instruction/ASoftFallback.cs      | 26 +++++++++++++++++++
 ChocolArm64/State/ARoundMode.cs               | 10 +++++++
 ChocolArm64/Translation/AILEmitterCtx.cs      | 22 +++++++++-------
 Ryujinx.Core/OsHle/MemoryRegions.cs           |  2 --
 .../OsHle/Objects/Android/NvFlinger.cs        | 19 ++++++++------
 10 files changed, 96 insertions(+), 24 deletions(-)
 create mode 100644 ChocolArm64/State/ARoundMode.cs

diff --git a/ChocolArm64/AOpCodeTable.cs b/ChocolArm64/AOpCodeTable.cs
index b8fe278dd6..71a944cf87 100644
--- a/ChocolArm64/AOpCodeTable.cs
+++ b/ChocolArm64/AOpCodeTable.cs
@@ -51,6 +51,7 @@ namespace ChocolArm64
             Set("x10100100xxxxxxxxxxxxxxxxxxxxxxx", AInstEmit.Eor,           typeof(AOpCodeAluImm));
             Set("x1001010xx0xxxxxxxxxxxxxxxxxxxxx", AInstEmit.Eor,           typeof(AOpCodeAluRs));
             Set("x00100111x0xxxxxxxxxxxxxxxxxxxxx", AInstEmit.Extr,          typeof(AOpCodeAluRs));
+            Set("11010101000000110010xxxxxxx11111", AInstEmit.Hint,          typeof(AOpCodeSystem));
             Set("xx001000110xxxxx1xxxxxxxxxxxxxxx", AInstEmit.Ldar,          typeof(AOpCodeMemEx));
             Set("1x001000011xxxxx1xxxxxxxxxxxxxxx", AInstEmit.Ldaxp,         typeof(AOpCodeMemEx));
             Set("xx001000010xxxxx1xxxxxxxxxxxxxxx", AInstEmit.Ldaxr,         typeof(AOpCodeMemEx));
@@ -193,6 +194,7 @@ namespace ChocolArm64
             Set("000111100x100110010000xxxxxxxxxx", AInstEmit.Frinta_S,      typeof(AOpCodeSimd));
             Set("000111100x100101010000xxxxxxxxxx", AInstEmit.Frintm_S,      typeof(AOpCodeSimd));
             Set("000111100x100100110000xxxxxxxxxx", AInstEmit.Frintp_S,      typeof(AOpCodeSimd));
+            Set("000111100x100111010000xxxxxxxxxx", AInstEmit.Frintx_S,      typeof(AOpCodeSimd));
             Set("000111100x100001110000xxxxxxxxxx", AInstEmit.Fsqrt_S,       typeof(AOpCodeSimd));
             Set("000111100x1xxxxx001110xxxxxxxxxx", AInstEmit.Fsub_S,        typeof(AOpCodeSimdReg));
             Set("0>0011101<1xxxxx110101xxxxxxxxxx", AInstEmit.Fsub_V,        typeof(AOpCodeSimdReg));
diff --git a/ChocolArm64/ATranslator.cs b/ChocolArm64/ATranslator.cs
index 96bbc89ead..04cef44f12 100644
--- a/ChocolArm64/ATranslator.cs
+++ b/ChocolArm64/ATranslator.cs
@@ -6,7 +6,7 @@ using System.Reflection.Emit;
 
 namespace ChocolArm64
 {
-    class ATranslator
+    public class ATranslator
     {
         public AThread Thread { get; private set; }
 
@@ -41,7 +41,7 @@ namespace ChocolArm64
             while (Position != 0 && KeepRunning);
         }
 
-        public bool TryGetCachedSub(AOpCode OpCode, out ATranslatedSub Sub)
+        internal bool TryGetCachedSub(AOpCode OpCode, out ATranslatedSub Sub)
         {
             if (OpCode.Emitter != AInstEmit.Bl)
             {
@@ -53,7 +53,7 @@ namespace ChocolArm64
             return TryGetCachedSub(((AOpCodeBImmAl)OpCode).Imm, out Sub);
         }
 
-        public bool TryGetCachedSub(long Position, out ATranslatedSub Sub)
+        internal bool TryGetCachedSub(long Position, out ATranslatedSub Sub)
         {
             return CachedSubs.TryGetValue(Position, out Sub);
         }
diff --git a/ChocolArm64/Decoder/AOpCodeSimd.cs b/ChocolArm64/Decoder/AOpCodeSimd.cs
index 5f940b2202..4170851721 100644
--- a/ChocolArm64/Decoder/AOpCodeSimd.cs
+++ b/ChocolArm64/Decoder/AOpCodeSimd.cs
@@ -10,8 +10,6 @@ namespace ChocolArm64.Decoder
         public int Opc  { get; private   set; }
         public int Size { get; protected set; }
 
-        public int SizeF => Size & 1;
-
         public AOpCodeSimd(AInst Inst, long Position, int OpCode) : base(Inst, Position, OpCode)
         {
             Rd   = (OpCode >>  0) & 0x1f;
diff --git a/ChocolArm64/Instruction/AInstEmitSimdArithmetic.cs b/ChocolArm64/Instruction/AInstEmitSimdArithmetic.cs
index 6665f219e2..e1fd56e003 100644
--- a/ChocolArm64/Instruction/AInstEmitSimdArithmetic.cs
+++ b/ChocolArm64/Instruction/AInstEmitSimdArithmetic.cs
@@ -265,6 +265,32 @@ namespace ChocolArm64.Instruction
             });
         }
 
+        public static void Frintx_S(AILEmitterCtx Context)
+        {
+            AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp;
+
+            EmitVectorExtractF(Context, Op.Rn, 0, Op.Size);
+
+            Context.EmitLdarg(ATranslatedSub.StateArgIdx);
+
+            Context.EmitCallPropGet(typeof(AThreadState), nameof(AThreadState.Fpcr));
+
+            if (Op.Size == 0)
+            {
+                ASoftFallback.EmitCall(Context, nameof(ASoftFallback.RoundF));
+            }
+            else if (Op.Size == 1)
+            {
+                ASoftFallback.EmitCall(Context, nameof(ASoftFallback.Round));
+            }
+            else
+            {
+                throw new InvalidOperationException();
+            }
+
+            EmitScalarSetF(Context, Op.Rd, Op.Size);
+        }
+
         public static void Fsqrt_S(AILEmitterCtx Context)
         {
             EmitScalarUnaryOpF(Context, () =>
diff --git a/ChocolArm64/Instruction/AInstEmitSystem.cs b/ChocolArm64/Instruction/AInstEmitSystem.cs
index 6754be7a87..f9d186020e 100644
--- a/ChocolArm64/Instruction/AInstEmitSystem.cs
+++ b/ChocolArm64/Instruction/AInstEmitSystem.cs
@@ -9,6 +9,11 @@ namespace ChocolArm64.Instruction
 {
     static partial class AInstEmit
     {
+        public static void Hint(AILEmitterCtx Context)
+        {
+            //Execute as no-op.
+        }
+
         public static void Mrs(AILEmitterCtx Context)
         {
             AOpCodeSystem Op = (AOpCodeSystem)Context.CurrOp;
diff --git a/ChocolArm64/Instruction/ASoftFallback.cs b/ChocolArm64/Instruction/ASoftFallback.cs
index a57966baf8..5127182ddc 100644
--- a/ChocolArm64/Instruction/ASoftFallback.cs
+++ b/ChocolArm64/Instruction/ASoftFallback.cs
@@ -189,6 +189,32 @@ namespace ChocolArm64.Instruction
                    (Value >> 6) & 1 + (Value >> 7);
         }
 
+        public static float RoundF(float Value, int Fpcr)
+        {
+            switch ((ARoundMode)((Fpcr >> 22) & 3))
+            {
+                case ARoundMode.ToNearest:            return MathF.Round   (Value);
+                case ARoundMode.TowardsPlusInfinity:  return MathF.Ceiling (Value);
+                case ARoundMode.TowardsMinusInfinity: return MathF.Floor   (Value);
+                case ARoundMode.TowardsZero:          return MathF.Truncate(Value);
+            }
+
+            throw new InvalidOperationException();
+        }
+
+        public static double Round(double Value, int Fpcr)
+        {
+            switch ((ARoundMode)((Fpcr >> 22) & 3))
+            {
+                case ARoundMode.ToNearest:            return Math.Round   (Value);
+                case ARoundMode.TowardsPlusInfinity:  return Math.Ceiling (Value);
+                case ARoundMode.TowardsMinusInfinity: return Math.Floor   (Value);
+                case ARoundMode.TowardsZero:          return Math.Truncate(Value);
+            }
+
+            throw new InvalidOperationException();
+        }
+
         public static AVec Tbl1_V64(AVec Vector, AVec Tb0)
         {
             return Tbl(Vector, 8, Tb0);
diff --git a/ChocolArm64/State/ARoundMode.cs b/ChocolArm64/State/ARoundMode.cs
new file mode 100644
index 0000000000..9896f3075e
--- /dev/null
+++ b/ChocolArm64/State/ARoundMode.cs
@@ -0,0 +1,10 @@
+namespace ChocolArm64.State
+{
+    public enum ARoundMode
+    {
+        ToNearest            = 0,
+        TowardsPlusInfinity  = 1,
+        TowardsMinusInfinity = 2,
+        TowardsZero          = 3
+    }
+}
\ No newline at end of file
diff --git a/ChocolArm64/Translation/AILEmitterCtx.cs b/ChocolArm64/Translation/AILEmitterCtx.cs
index cf6445401b..9199eddc6f 100644
--- a/ChocolArm64/Translation/AILEmitterCtx.cs
+++ b/ChocolArm64/Translation/AILEmitterCtx.cs
@@ -18,8 +18,8 @@ namespace ChocolArm64.Translation
 
         private AILBlock ILBlock;
 
-        private AOpCode LastCmpOp;
-        private AOpCode LastFlagOp;
+        private AOpCode OptOpLastCompare;
+        private AOpCode OptOpLastFlagSet;
 
         private int BlkIndex;
         private int OpcIndex;
@@ -75,6 +75,9 @@ namespace ChocolArm64.Translation
                 BlkIndex++;
                 OpcIndex = -1;
 
+                OptOpLastFlagSet = null;
+                OptOpLastCompare = null;
+
                 ILBlock = Emitter.GetILBlock(BlkIndex);
             }
 
@@ -120,7 +123,7 @@ namespace ChocolArm64.Translation
 
         public void TryOptMarkCondWithoutCmp()
         {
-            LastCmpOp = CurrOp;
+            OptOpLastCompare = CurrOp;
 
             AInstEmitAluHelper.EmitDataLoadOpers(this);
 
@@ -146,14 +149,15 @@ namespace ChocolArm64.Translation
         {
             OpCode ILOp;
 
-            int IntCond = (int)Cond;            
+            int IntCond = (int)Cond;
 
-            if (LastCmpOp != null && LastFlagOp == LastCmpOp && BranchOps.ContainsKey(Cond))
+            if (OptOpLastCompare != null &&
+                OptOpLastCompare == OptOpLastFlagSet && BranchOps.ContainsKey(Cond))
             {
-                Ldloc(Tmp3Index, AIoType.Int, LastCmpOp.RegisterSize);
-                Ldloc(Tmp4Index, AIoType.Int, LastCmpOp.RegisterSize);
+                Ldloc(Tmp3Index, AIoType.Int, OptOpLastCompare.RegisterSize);
+                Ldloc(Tmp4Index, AIoType.Int, OptOpLastCompare.RegisterSize);
 
-                if (LastCmpOp.Emitter == AInstEmit.Adds)
+                if (OptOpLastCompare.Emitter == AInstEmit.Adds)
                 {
                     Emit(OpCodes.Neg);
                 }
@@ -356,7 +360,7 @@ namespace ChocolArm64.Translation
         public void EmitLdflg(int Index) => Ldloc(Index, AIoType.Flag);
         public void EmitStflg(int Index)
         {
-            LastFlagOp = CurrOp;
+            OptOpLastFlagSet = CurrOp;
 
             Stloc(Index, AIoType.Flag);
         }
diff --git a/Ryujinx.Core/OsHle/MemoryRegions.cs b/Ryujinx.Core/OsHle/MemoryRegions.cs
index e8ededc8fc..86d266f60d 100644
--- a/Ryujinx.Core/OsHle/MemoryRegions.cs
+++ b/Ryujinx.Core/OsHle/MemoryRegions.cs
@@ -1,5 +1,3 @@
-using ChocolArm64.Memory;
-
 namespace Ryujinx.Core.OsHle
 {
     static class MemoryRegions
diff --git a/Ryujinx.Core/OsHle/Objects/Android/NvFlinger.cs b/Ryujinx.Core/OsHle/Objects/Android/NvFlinger.cs
index 5458838368..11069cb22f 100644
--- a/Ryujinx.Core/OsHle/Objects/Android/NvFlinger.cs
+++ b/Ryujinx.Core/OsHle/Objects/Android/NvFlinger.cs
@@ -291,7 +291,10 @@ namespace Ryujinx.Core.OsHle.Objects.Android
 
                 BufferQueue[Slot].State = BufferState.Free;
 
-                WaitBufferFree.Set();
+                lock (WaitBufferFree)
+                {
+                    WaitBufferFree.Set();
+                }
             });
         }
 
@@ -317,15 +320,15 @@ namespace Ryujinx.Core.OsHle.Objects.Android
 
             do
             {
-                if ((Slot = GetFreeSlot(Width, Height)) != -1)
-                {
-                    break;
-                }
-
-                Logging.Debug("Waiting for a free BufferQueue slot...");
-
                 lock (WaitBufferFree)
                 {
+                    if ((Slot = GetFreeSlot(Width, Height)) != -1)
+                    {
+                        break;
+                    }
+
+                    Logging.Debug("Waiting for a free BufferQueue slot...");
+
                     if (!KeepRunning)
                     {
                         break;