From c3d62bd0783a20efb78fa0776f4c620970774cf9 Mon Sep 17 00:00:00 2001
From: gdkchan <gab.dark.100@gmail.com>
Date: Mon, 9 Nov 2020 21:06:46 -0300
Subject: [PATCH] Implement ATOM shader instruction (#1687)

* Implement ATOM shader instruction

* Fix reduction type decoding
---
 Ryujinx.Graphics.Shader/Decoders/AtomicOp.cs  |  3 +-
 .../Decoders/OpCodeAtom.cs                    | 13 -------
 .../Decoders/OpCodeTable.cs                   |  1 +
 .../Decoders/ReductionType.cs                 | 12 +++---
 .../Instructions/InstEmitMemory.cs            | 38 ++++++++++++++++++-
 5 files changed, 45 insertions(+), 22 deletions(-)

diff --git a/Ryujinx.Graphics.Shader/Decoders/AtomicOp.cs b/Ryujinx.Graphics.Shader/Decoders/AtomicOp.cs
index 065a57c4..19955192 100644
--- a/Ryujinx.Graphics.Shader/Decoders/AtomicOp.cs
+++ b/Ryujinx.Graphics.Shader/Decoders/AtomicOp.cs
@@ -10,6 +10,7 @@ namespace Ryujinx.Graphics.Shader.Decoders
         BitwiseAnd         = 5,
         BitwiseOr          = 6,
         BitwiseExclusiveOr = 7,
-        Swap               = 8
+        Swap               = 8,
+        SafeAdd            = 10 // Only supported by ATOM.
     }
 }
\ No newline at end of file
diff --git a/Ryujinx.Graphics.Shader/Decoders/OpCodeAtom.cs b/Ryujinx.Graphics.Shader/Decoders/OpCodeAtom.cs
index 1bf5d0cd..006a91f3 100644
--- a/Ryujinx.Graphics.Shader/Decoders/OpCodeAtom.cs
+++ b/Ryujinx.Graphics.Shader/Decoders/OpCodeAtom.cs
@@ -8,10 +8,6 @@ namespace Ryujinx.Graphics.Shader.Decoders
         public Register Ra { get; }
         public Register Rb { get; }
 
-        public ReductionType Type { get; }
-
-        public int Offset { get; }
-
         public bool Extended { get; }
 
         public AtomicOp AtomicOp { get; }
@@ -24,15 +20,6 @@ namespace Ryujinx.Graphics.Shader.Decoders
             Ra = new Register(opCode.Extract(8,  8), RegisterType.Gpr);
             Rb = new Register(opCode.Extract(20, 8), RegisterType.Gpr);
 
-            Type = (ReductionType)opCode.Extract(28, 2);
-
-            if (Type == ReductionType.FP32FtzRn)
-            {
-                Type = ReductionType.S64;
-            }
-
-            Offset = opCode.Extract(30, 22);
-
             Extended = opCode.Extract(48);
 
             AtomicOp = (AtomicOp)opCode.Extract(52, 4);
diff --git a/Ryujinx.Graphics.Shader/Decoders/OpCodeTable.cs b/Ryujinx.Graphics.Shader/Decoders/OpCodeTable.cs
index 302f1fc4..8b9cb20d 100644
--- a/Ryujinx.Graphics.Shader/Decoders/OpCodeTable.cs
+++ b/Ryujinx.Graphics.Shader/Decoders/OpCodeTable.cs
@@ -34,6 +34,7 @@ namespace Ryujinx.Graphics.Shader.Decoders
 #region Instructions
             Set("1110111111011x", InstEmit.Ald,     OpCodeAttribute.Create);
             Set("1110111111110x", InstEmit.Ast,     OpCodeAttribute.Create);
+            Set("11101101xxxxxx", InstEmit.Atom,    OpCodeAtom.Create);
             Set("11101100xxxxxx", InstEmit.Atoms,   OpCodeAtom.Create);
             Set("1111000010101x", InstEmit.Bar,     OpCodeBarrier.Create);
             Set("0100110000000x", InstEmit.Bfe,     OpCodeAluCbuf.Create);
diff --git a/Ryujinx.Graphics.Shader/Decoders/ReductionType.cs b/Ryujinx.Graphics.Shader/Decoders/ReductionType.cs
index aaa2186e..4c8bd37e 100644
--- a/Ryujinx.Graphics.Shader/Decoders/ReductionType.cs
+++ b/Ryujinx.Graphics.Shader/Decoders/ReductionType.cs
@@ -2,11 +2,11 @@ namespace Ryujinx.Graphics.Shader.Decoders
 {
     enum ReductionType
     {
-        U32       = 0,
-        S32       = 1,
-        U64       = 2,
-        FP32FtzRn = 3,
-        U128      = 4,
-        S64       = 5
+        U32         = 0,
+        S32         = 1,
+        U64         = 2,
+        FP32FtzRn   = 3,
+        FP16x2FtzRn = 4,
+        S64         = 5
     }
 }
\ No newline at end of file
diff --git a/Ryujinx.Graphics.Shader/Instructions/InstEmitMemory.cs b/Ryujinx.Graphics.Shader/Instructions/InstEmitMemory.cs
index 2a2c8927..63f9cff7 100644
--- a/Ryujinx.Graphics.Shader/Instructions/InstEmitMemory.cs
+++ b/Ryujinx.Graphics.Shader/Instructions/InstEmitMemory.cs
@@ -57,13 +57,47 @@ namespace Ryujinx.Graphics.Shader.Instructions
             }
         }
 
+        public static void Atom(EmitterContext context)
+        {
+            OpCodeAtom op = (OpCodeAtom)context.CurrOp;
+
+            ReductionType type = (ReductionType)op.RawOpCode.Extract(49, 2);
+
+            int sOffset = (op.RawOpCode.Extract(28, 20) << 12) >> 12;
+
+            (Operand addrLow, Operand addrHigh) = Get40BitsAddress(context, op.Ra, op.Extended, sOffset);
+
+            Operand value = GetSrcB(context);
+
+            Operand res = EmitAtomicOp(
+                context,
+                Instruction.MrGlobal,
+                op.AtomicOp,
+                type,
+                addrLow,
+                addrHigh,
+                value);
+
+            context.Copy(GetDest(context), res);
+        }
+
         public static void Atoms(EmitterContext context)
         {
             OpCodeAtom op = (OpCodeAtom)context.CurrOp;
 
+            ReductionType type = op.RawOpCode.Extract(28, 2) switch
+            {
+                0 => ReductionType.U32,
+                1 => ReductionType.S32,
+                2 => ReductionType.U64,
+                _ => ReductionType.S64
+            };
+
             Operand offset = context.ShiftRightU32(GetSrcA(context), Const(2));
 
-            offset = context.IAdd(offset, Const(op.Offset));
+            int sOffset = (op.RawOpCode.Extract(30, 22) << 10) >> 10;
+
+            offset = context.IAdd(offset, Const(sOffset));
 
             Operand value = GetSrcB(context);
 
@@ -71,7 +105,7 @@ namespace Ryujinx.Graphics.Shader.Instructions
                 context,
                 Instruction.MrShared,
                 op.AtomicOp,
-                op.Type,
+                type,
                 offset,
                 Const(0),
                 value);