From b19c4740823ed8fcebf62bf5741a7614a2ac0aa0 Mon Sep 17 00:00:00 2001
From: gdkchan <gab.dark.100@gmail.com>
Date: Thu, 17 May 2018 15:25:42 -0300
Subject: [PATCH] Added more shader instructions, including BFE, BRA (partial),
 FMNMX, ISCADD, SHL, LD_C, some shader related fixes, added support for
 texture component selection

---
 Ryujinx.Core/Gpu/TextureFactory.cs            |  15 +-
 Ryujinx.Core/OsHle/Kernel/SvcThread.cs        |   7 +
 Ryujinx.Core/OsHle/Services/Nifm/IRequest.cs  |  18 +-
 .../OsHle/Services/Prepo/IPrepoService.cs     |  10 +-
 Ryujinx.Graphics/Gal/GalTexture.cs            |  27 +-
 Ryujinx.Graphics/Gal/GalTextureSource.cs      |  13 +
 .../Gal/OpenGL/OGLEnumConverter.cs            |  16 +
 Ryujinx.Graphics/Gal/OpenGL/OGLShader.cs      |  20 +-
 Ryujinx.Graphics/Gal/OpenGL/OGLTexture.cs     |  10 +
 Ryujinx.Graphics/Gal/Shader/GlslDecl.cs       |  28 +-
 Ryujinx.Graphics/Gal/Shader/GlslDecompiler.cs | 139 +++++++--
 Ryujinx.Graphics/Gal/Shader/GlslProgram.cs    |   2 +-
 .../Gal/Shader/ShaderDecodeAlu.cs             | 285 +++++++++++++++---
 .../Gal/Shader/ShaderDecodeFlow.cs            |  18 ++
 .../Gal/Shader/ShaderDecodeHelper.cs          |  65 +++-
 .../Gal/Shader/ShaderDecodeMem.cs             |  37 +++
 .../Gal/Shader/ShaderDecodeMove.cs            |  40 ++-
 Ryujinx.Graphics/Gal/Shader/ShaderDecoder.cs  |  20 +-
 Ryujinx.Graphics/Gal/Shader/ShaderIrBlock.cs  |  28 ++
 Ryujinx.Graphics/Gal/Shader/ShaderIrCmnt.cs   |  12 +
 Ryujinx.Graphics/Gal/Shader/ShaderIrInst.cs   |   9 +
 Ryujinx.Graphics/Gal/Shader/ShaderIrLabel.cs  |   4 +
 .../Gal/Shader/ShaderIrOperCbuf.cs            |   7 +-
 .../Gal/Shader/ShaderOpCodeTable.cs           |  20 +-
 Ryujinx.Graphics/Gal/ShaderDeclInfo.cs        |   8 +
 Ryujinx.sln                                   |   6 +
 Ryushader/Program.cs                          |  48 +++
 Ryushader/Ryushader.csproj                    |  12 +
 28 files changed, 806 insertions(+), 118 deletions(-)
 create mode 100644 Ryujinx.Graphics/Gal/GalTextureSource.cs
 create mode 100644 Ryujinx.Graphics/Gal/Shader/ShaderIrCmnt.cs
 create mode 100644 Ryujinx.Graphics/Gal/Shader/ShaderIrLabel.cs
 create mode 100644 Ryushader/Program.cs
 create mode 100644 Ryushader/Ryushader.csproj

diff --git a/Ryujinx.Core/Gpu/TextureFactory.cs b/Ryujinx.Core/Gpu/TextureFactory.cs
index 68b48a1fb..5206c125b 100644
--- a/Ryujinx.Core/Gpu/TextureFactory.cs
+++ b/Ryujinx.Core/Gpu/TextureFactory.cs
@@ -11,6 +11,11 @@ namespace Ryujinx.Core.Gpu
 
             GalTextureFormat Format = (GalTextureFormat)(Tic[0] & 0x7f);
 
+            GalTextureSource XSource = (GalTextureSource)((Tic[0] >> 19) & 7);
+            GalTextureSource YSource = (GalTextureSource)((Tic[0] >> 22) & 7);
+            GalTextureSource ZSource = (GalTextureSource)((Tic[0] >> 25) & 7);
+            GalTextureSource WSource = (GalTextureSource)((Tic[0] >> 28) & 7);
+
             long TextureAddress = (uint)Tic[1];
 
             TextureAddress |= (long)((ushort)Tic[2]) << 32;
@@ -37,7 +42,15 @@ namespace Ryujinx.Core.Gpu
 
             byte[] Data = TextureReader.Read(Vmm, Texture);
 
-            return new GalTexture(Data, Width, Height, Format);
+            return new GalTexture(
+                Data,
+                Width,
+                Height,
+                Format,
+                XSource,
+                YSource,
+                ZSource,
+                WSource);
         }
 
         public static GalTextureSampler MakeSampler(NvGpu Gpu, NvGpuVmm Vmm, long TscPosition)
diff --git a/Ryujinx.Core/OsHle/Kernel/SvcThread.cs b/Ryujinx.Core/OsHle/Kernel/SvcThread.cs
index b504f81a3..8aa26dd3f 100644
--- a/Ryujinx.Core/OsHle/Kernel/SvcThread.cs
+++ b/Ryujinx.Core/OsHle/Kernel/SvcThread.cs
@@ -141,6 +141,13 @@ namespace Ryujinx.Core.OsHle.Kernel
 
         private void SvcSetThreadCoreMask(AThreadState ThreadState)
         {
+            //FIXME: This is wrong, but the "correct" way to handle
+            //this svc causes deadlocks when more often.
+            //There is probably something wrong with it still.
+            ThreadState.X0 = 0;
+
+            return;
+
             int  Handle    =  (int)ThreadState.X0;
             int  IdealCore =  (int)ThreadState.X1;
             long CoreMask  = (long)ThreadState.X2;
diff --git a/Ryujinx.Core/OsHle/Services/Nifm/IRequest.cs b/Ryujinx.Core/OsHle/Services/Nifm/IRequest.cs
index 7de5a4d9f..0d1aa5e40 100644
--- a/Ryujinx.Core/OsHle/Services/Nifm/IRequest.cs
+++ b/Ryujinx.Core/OsHle/Services/Nifm/IRequest.cs
@@ -18,11 +18,12 @@ namespace Ryujinx.Core.OsHle.Services.Nifm
         {
             m_Commands = new Dictionary<int, ServiceProcessRequest>()
             {
-                { 0, GetRequestState               },
-                { 1, GetResult                     },
-                { 2, GetSystemEventReadableHandles },
-                { 3, Cancel                        },
-                { 4, Submit                        },
+                { 0,  GetRequestState                 },
+                { 1,  GetResult                       },
+                { 2,  GetSystemEventReadableHandles   },
+                { 3,  Cancel                          },
+                { 4,  Submit                          },
+                { 11, SetConnectionConfirmationOption }
             };
 
             Event = new KEvent();
@@ -69,6 +70,13 @@ namespace Ryujinx.Core.OsHle.Services.Nifm
             return 0;
         }
 
+        public long SetConnectionConfirmationOption(ServiceCtx Context)
+        {
+            Context.Ns.Log.PrintStub(LogClass.ServiceNifm, "Stubbed.");
+
+            return 0;
+        }
+
         public void Dispose()
         {
             Dispose(true);
diff --git a/Ryujinx.Core/OsHle/Services/Prepo/IPrepoService.cs b/Ryujinx.Core/OsHle/Services/Prepo/IPrepoService.cs
index 42e438414..dbf01c6bd 100644
--- a/Ryujinx.Core/OsHle/Services/Prepo/IPrepoService.cs
+++ b/Ryujinx.Core/OsHle/Services/Prepo/IPrepoService.cs
@@ -1,3 +1,4 @@
+using Ryujinx.Core.Logging;
 using Ryujinx.Core.OsHle.Ipc;
 using System.Collections.Generic;
 
@@ -13,8 +14,15 @@ namespace Ryujinx.Core.OsHle.Services.Prepo
         {
             m_Commands = new Dictionary<int, ServiceProcessRequest>()
             {
-                //...
+                { 10101, SaveReportWithUser }
             };
         }
+
+        public static long SaveReportWithUser(ServiceCtx Context)
+        {
+            Context.Ns.Log.PrintStub(LogClass.ServicePrepo, "Stubbed.");
+
+            return 0;
+        }
     }
 }
\ No newline at end of file
diff --git a/Ryujinx.Graphics/Gal/GalTexture.cs b/Ryujinx.Graphics/Gal/GalTexture.cs
index fcf1f1ad2..75aef3072 100644
--- a/Ryujinx.Graphics/Gal/GalTexture.cs
+++ b/Ryujinx.Graphics/Gal/GalTexture.cs
@@ -9,12 +9,29 @@ namespace Ryujinx.Graphics.Gal
 
         public GalTextureFormat Format;
 
-        public GalTexture(byte[] Data, int Width, int Height, GalTextureFormat Format)
+        public GalTextureSource XSource;
+        public GalTextureSource YSource;
+        public GalTextureSource ZSource;
+        public GalTextureSource WSource;
+
+        public GalTexture(
+            byte[]           Data,
+            int              Width,
+            int              Height,
+            GalTextureFormat Format,
+            GalTextureSource XSource,
+            GalTextureSource YSource,
+            GalTextureSource ZSource,
+            GalTextureSource WSource)
         {
-            this.Data   = Data;
-            this.Width  = Width;
-            this.Height = Height;
-            this.Format = Format;
+            this.Data    = Data;
+            this.Width   = Width;
+            this.Height  = Height;
+            this.Format  = Format;
+            this.XSource = XSource;
+            this.YSource = YSource;
+            this.ZSource = ZSource;
+            this.WSource = WSource;
         }
     }
 }
\ No newline at end of file
diff --git a/Ryujinx.Graphics/Gal/GalTextureSource.cs b/Ryujinx.Graphics/Gal/GalTextureSource.cs
new file mode 100644
index 000000000..72dbec606
--- /dev/null
+++ b/Ryujinx.Graphics/Gal/GalTextureSource.cs
@@ -0,0 +1,13 @@
+namespace Ryujinx.Graphics.Gal
+{
+    public enum GalTextureSource
+    {
+        Zero     = 0,
+        Red      = 2,
+        Green    = 3,
+        Blue     = 4,
+        Alpha    = 5,
+        OneInt   = 6,
+        OneFloat = 7
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx.Graphics/Gal/OpenGL/OGLEnumConverter.cs b/Ryujinx.Graphics/Gal/OpenGL/OGLEnumConverter.cs
index 4cc0a0397..d266a87a8 100644
--- a/Ryujinx.Graphics/Gal/OpenGL/OGLEnumConverter.cs
+++ b/Ryujinx.Graphics/Gal/OpenGL/OGLEnumConverter.cs
@@ -81,6 +81,22 @@ namespace Ryujinx.Graphics.Gal.OpenGL
             throw new NotImplementedException(Format.ToString());
         }
 
+        public static All GetTextureSwizzle(GalTextureSource Source)
+        {
+            switch (Source)
+            {
+                case GalTextureSource.Zero:     return All.Zero;
+                case GalTextureSource.Red:      return All.Red;
+                case GalTextureSource.Green:    return All.Green;
+                case GalTextureSource.Blue:     return All.Blue;
+                case GalTextureSource.Alpha:    return All.Alpha;
+                case GalTextureSource.OneInt:   return All.One;
+                case GalTextureSource.OneFloat: return All.One;
+            }
+
+            throw new ArgumentException(nameof(Source));
+        }
+
         public static TextureWrapMode GetTextureWrapMode(GalTextureWrap Wrap)
         {
             switch (Wrap)
diff --git a/Ryujinx.Graphics/Gal/OpenGL/OGLShader.cs b/Ryujinx.Graphics/Gal/OpenGL/OGLShader.cs
index fff6362b4..e740a32e4 100644
--- a/Ryujinx.Graphics/Gal/OpenGL/OGLShader.cs
+++ b/Ryujinx.Graphics/Gal/OpenGL/OGLShader.cs
@@ -87,10 +87,10 @@ namespace Ryujinx.Graphics.Gal.OpenGL
 
         public void Create(long Tag, GalShaderType Type, byte[] Data)
         {
-            Stages.GetOrAdd(Tag, (Key) => ShaderStageFactory(Type, Data));
+            Stages.GetOrAdd(Tag, (Key) => ShaderStageFactory(Type, Tag, Data));
         }
 
-        private ShaderStage ShaderStageFactory(GalShaderType Type, byte[] Data)
+        private ShaderStage ShaderStageFactory(GalShaderType Type, long Tag, byte[] Data)
         {
             GlslProgram Program = GetGlslProgram(Data, Type);
 
@@ -140,11 +140,21 @@ namespace Ryujinx.Graphics.Gal.OpenGL
             {
                 foreach (ShaderDeclInfo DeclInfo in Stage.UniformUsage.Where(x => x.Cbuf == Cbuf))
                 {
-                    float Value = BitConverter.ToSingle(Data, DeclInfo.Index * 4);
-
                     int Location = GL.GetUniformLocation(CurrentProgramHandle, DeclInfo.Name);
 
-                    GL.Uniform1(Location, Value);
+                    int Count = Data.Length >> 2;
+
+                    //The Index is the index of the last element,
+                    //so we can add 1 to get the uniform array size.
+                    Count = Math.Min(Count, DeclInfo.Index + 1);
+
+                    unsafe
+                    {
+                        fixed (byte* Ptr = Data)
+                        {
+                            GL.Uniform1(Location, Count, (float*)Ptr);
+                        }
+                    }
                 }
             }
         }
diff --git a/Ryujinx.Graphics/Gal/OpenGL/OGLTexture.cs b/Ryujinx.Graphics/Gal/OpenGL/OGLTexture.cs
index 9ea25056c..8dcfb2bdb 100644
--- a/Ryujinx.Graphics/Gal/OpenGL/OGLTexture.cs
+++ b/Ryujinx.Graphics/Gal/OpenGL/OGLTexture.cs
@@ -51,6 +51,16 @@ namespace Ryujinx.Graphics.Gal.OpenGL
                     Type,
                     Texture.Data);
             }
+
+            int SwizzleR = (int)OGLEnumConverter.GetTextureSwizzle(Texture.XSource);
+            int SwizzleG = (int)OGLEnumConverter.GetTextureSwizzle(Texture.YSource);
+            int SwizzleB = (int)OGLEnumConverter.GetTextureSwizzle(Texture.ZSource);
+            int SwizzleA = (int)OGLEnumConverter.GetTextureSwizzle(Texture.WSource);
+
+            GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureSwizzleR, SwizzleR);
+            GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureSwizzleG, SwizzleG);
+            GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureSwizzleB, SwizzleB);
+            GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureSwizzleA, SwizzleA);
         }
 
         public void Bind(int Index)
diff --git a/Ryujinx.Graphics/Gal/Shader/GlslDecl.cs b/Ryujinx.Graphics/Gal/Shader/GlslDecl.cs
index cd901747d..2650569e6 100644
--- a/Ryujinx.Graphics/Gal/Shader/GlslDecl.cs
+++ b/Ryujinx.Graphics/Gal/Shader/GlslDecl.cs
@@ -28,7 +28,7 @@ namespace Ryujinx.Graphics.Gal.Shader
 
         private Dictionary<int, ShaderDeclInfo> m_Textures;
 
-        private Dictionary<(int, int), ShaderDeclInfo> m_Uniforms;
+        private Dictionary<int, ShaderDeclInfo> m_Uniforms;
 
         private Dictionary<int, ShaderDeclInfo> m_InAttributes;
         private Dictionary<int, ShaderDeclInfo> m_OutAttributes;
@@ -38,7 +38,7 @@ namespace Ryujinx.Graphics.Gal.Shader
 
         public IReadOnlyDictionary<int, ShaderDeclInfo> Textures => m_Textures;
 
-        public IReadOnlyDictionary<(int, int), ShaderDeclInfo> Uniforms => m_Uniforms;
+        public IReadOnlyDictionary<int, ShaderDeclInfo> Uniforms => m_Uniforms;
 
         public IReadOnlyDictionary<int, ShaderDeclInfo> InAttributes  => m_InAttributes;
         public IReadOnlyDictionary<int, ShaderDeclInfo> OutAttributes => m_OutAttributes;
@@ -54,7 +54,7 @@ namespace Ryujinx.Graphics.Gal.Shader
 
             StagePrefix = StagePrefixes[(int)ShaderType] + "_";
 
-            m_Uniforms = new Dictionary<(int, int), ShaderDeclInfo>();
+            m_Uniforms = new Dictionary<int, ShaderDeclInfo>();
 
             m_Textures = new Dictionary<int, ShaderDeclInfo>();
 
@@ -124,11 +124,27 @@ namespace Ryujinx.Graphics.Gal.Shader
 
                 case ShaderIrOperCbuf Cbuf:
                 {
-                    string Name = StagePrefix + UniformName + Cbuf.Index + "_" + Cbuf.Offs;
+                    if (m_Uniforms.TryGetValue(Cbuf.Index, out ShaderDeclInfo DeclInfo))
+                    {
+                        DeclInfo.SetCbufOffs(Cbuf.Pos);
+                    }
+                    else
+                    {
+                        string Name = StagePrefix + UniformName + Cbuf.Index;
 
-                    ShaderDeclInfo DeclInfo = new ShaderDeclInfo(Name, Cbuf.Offs, Cbuf.Index);
+                        DeclInfo = new ShaderDeclInfo(Name, Cbuf.Pos, Cbuf.Index);
 
-                    m_Uniforms.TryAdd((Cbuf.Index, Cbuf.Offs), DeclInfo);
+                        m_Uniforms.Add(Cbuf.Index, DeclInfo);
+                    }
+
+                    if (Cbuf.Offs != null)
+                    {
+                        //The constant buffer is being accessed as an array,
+                        //we have no way to know the max element it may access in this case.
+                        //Here, we just assume the array size with arbitrary values.
+                        //TODO: Find a better solution for this.
+                        DeclInfo.SetCbufOffs(Cbuf.Pos + 15);
+                    }
 
                     break;
                 }
diff --git a/Ryujinx.Graphics/Gal/Shader/GlslDecompiler.cs b/Ryujinx.Graphics/Gal/Shader/GlslDecompiler.cs
index 1e0824d2f..d6f171f4c 100644
--- a/Ryujinx.Graphics/Gal/Shader/GlslDecompiler.cs
+++ b/Ryujinx.Graphics/Gal/Shader/GlslDecompiler.cs
@@ -6,7 +6,7 @@ using System.Text;
 
 namespace Ryujinx.Graphics.Gal.Shader
 {
-    class GlslDecompiler
+    public class GlslDecompiler
     {
         private delegate string GetInstExpr(ShaderIrOp Op);
 
@@ -31,6 +31,8 @@ namespace Ryujinx.Graphics.Gal.Shader
         {
             InstsExpr = new Dictionary<ShaderIrInst, GetInstExpr>()
             {
+                { ShaderIrInst.Abs,    GetAbsExpr    },
+                { ShaderIrInst.Add,    GetAddExpr    },
                 { ShaderIrInst.And,    GetAndExpr    },
                 { ShaderIrInst.Asr,    GetAsrExpr    },
                 { ShaderIrInst.Band,   GetBandExpr   },
@@ -45,8 +47,8 @@ namespace Ryujinx.Graphics.Gal.Shader
                 { ShaderIrInst.Clt,    GetCltExpr    },
                 { ShaderIrInst.Cne,    GetCneExpr    },
                 { ShaderIrInst.Exit,   GetExitExpr   },
-                { ShaderIrInst.Fabs,   GetFabsExpr   },
-                { ShaderIrInst.Fadd,   GetFaddExpr   },
+                { ShaderIrInst.Fabs,   GetAbsExpr    },
+                { ShaderIrInst.Fadd,   GetAddExpr    },
                 { ShaderIrInst.Fceq,   GetCeqExpr    },
                 { ShaderIrInst.Fcge,   GetCgeExpr    },
                 { ShaderIrInst.Fcgt,   GetCgtExpr    },
@@ -59,8 +61,10 @@ namespace Ryujinx.Graphics.Gal.Shader
                 { ShaderIrInst.Ffma,   GetFfmaExpr   },
                 { ShaderIrInst.Flg2,   GetFlg2Expr   },
                 { ShaderIrInst.Floor,  GetFloorExpr  },
-                { ShaderIrInst.Fmul,   GetFmulExpr   },
-                { ShaderIrInst.Fneg,   GetFnegExpr   },
+                { ShaderIrInst.Fmax,   GetFmaxExpr   },
+                { ShaderIrInst.Fmin,   GetFminExpr   },
+                { ShaderIrInst.Fmul,   GetMulExpr    },
+                { ShaderIrInst.Fneg,   GetNegExpr    },
                 { ShaderIrInst.Frcp,   GetFrcpExpr   },
                 { ShaderIrInst.Frsq,   GetFrsqExpr   },
                 { ShaderIrInst.Fsin,   GetFsinExpr   },
@@ -68,10 +72,14 @@ namespace Ryujinx.Graphics.Gal.Shader
                 { ShaderIrInst.Ftou,   GetFtouExpr   },
                 { ShaderIrInst.Ipa,    GetIpaExpr    },
                 { ShaderIrInst.Kil,    GetKilExpr    },
+                { ShaderIrInst.Lsl,    GetLslExpr    },
                 { ShaderIrInst.Lsr,    GetLsrExpr    },
+                { ShaderIrInst.Mul,    GetMulExpr    },
+                { ShaderIrInst.Neg,    GetNegExpr    },
                 { ShaderIrInst.Not,    GetNotExpr    },
                 { ShaderIrInst.Or,     GetOrExpr     },
                 { ShaderIrInst.Stof,   GetStofExpr   },
+                { ShaderIrInst.Sub,    GetSubExpr    },
                 { ShaderIrInst.Texq,   GetTexqExpr   },
                 { ShaderIrInst.Texs,   GetTexsExpr   },
                 { ShaderIrInst.Trunc,  GetTruncExpr  },
@@ -100,7 +108,7 @@ namespace Ryujinx.Graphics.Gal.Shader
             PrintDeclGprs();
             PrintDeclPreds();
 
-            PrintBlockScope("void main()", 1, Nodes);
+            PrintBlockScope(Nodes, 0, Nodes.Length, "void main()", 1);
 
             string GlslCode = SB.ToString();
 
@@ -124,7 +132,7 @@ namespace Ryujinx.Graphics.Gal.Shader
 
             foreach (ShaderDeclInfo DeclInfo in Decl.Uniforms.Values.OrderBy(DeclKeySelector))
             {
-                SB.AppendLine($"uniform {GetDecl(DeclInfo)};");
+                SB.AppendLine($"uniform {GetDecl(DeclInfo)}[{DeclInfo.Index + 1}];");
             }
 
             if (Decl.Uniforms.Count > 0)
@@ -221,7 +229,12 @@ namespace Ryujinx.Graphics.Gal.Shader
             return ElemTypes[DeclInfo.Size - 1] + " " + DeclInfo.Name;
         }
 
-        private void PrintBlockScope(string ScopeName, int IdentationLevel, params ShaderIrNode[] Nodes)
+        private void PrintBlockScope(
+            ShaderIrNode[] Nodes,
+            int            Start,
+            int            Count,
+            string         ScopeName,
+            int            IdentationLevel)
         {
             string Identation = string.Empty;
 
@@ -244,7 +257,7 @@ namespace Ryujinx.Graphics.Gal.Shader
                 Identation += IdentationStr;
             }
 
-            for (int Index = 0; Index < Nodes.Length; Index++)
+            for (int Index = Start; Index < Start + Count; Index++)
             {
                 ShaderIrNode Node = Nodes[Index];
 
@@ -257,9 +270,44 @@ namespace Ryujinx.Graphics.Gal.Shader
                         IfExpr = "!(" + IfExpr + ")";
                     }
 
-                    string SubScopeName = "if (" + IfExpr + ")";
+                    if (Cond.Child is ShaderIrOp Op && Op.Inst == ShaderIrInst.Bra)
+                    {
+                        ShaderIrLabel Label = (ShaderIrLabel)Op.OperandA;
+
+                        int Target = FindLabel(Nodes, Label, Index + 1);
+
+                        int IfCount = Target - Index - 1;
+
+                        string SubScopeName = "if (!" + IfExpr + ")";
+
+                        if (Nodes[Index + IfCount] is ShaderIrOp LastOp && LastOp.Inst == ShaderIrInst.Bra)
+                        {
+                            Target = FindLabel(Nodes, (ShaderIrLabel)LastOp.OperandA, Index + 1);
+
+                            int ElseCount = Target - (Index + 1 + IfCount);
+
+                            PrintBlockScope(Nodes, Index + 1, IfCount - 1, SubScopeName, IdentationLevel + 1);
+
+                            PrintBlockScope(Nodes, Index + 1 + IfCount, ElseCount, "else", IdentationLevel + 1);
+
+                            Index += IfCount + ElseCount;
+                        }
+                        else
+                        {
+                            PrintBlockScope(Nodes, Index + 1, IfCount, SubScopeName, IdentationLevel + 1);
+
+                            Index += IfCount;
+                        }
+                    }
+                    else
+                    {
+                        string SubScopeName = "if (" + IfExpr + ")";
+
+                        ShaderIrNode[] Child = new ShaderIrNode[] { Cond.Child };
+
+                        PrintBlockScope(Child, 0, 1, SubScopeName, IdentationLevel + 1);
+                    }
 
-                    PrintBlockScope(SubScopeName, IdentationLevel + 1, Cond.Child);
                 }
                 else if (Node is ShaderIrAsg Asg)
                 {
@@ -288,6 +336,14 @@ namespace Ryujinx.Graphics.Gal.Shader
 
                     SB.AppendLine(Identation + GetSrcExpr(Op, true) + ";");
                 }
+                else if (Node is ShaderIrLabel Label)
+                {
+                    //TODO: Add support for loops here.
+                }
+                else if (Node is ShaderIrCmnt Cmnt)
+                {
+                    SB.AppendLine(Identation + "// " + Cmnt.Comment);
+                }
                 else
                 {
                     throw new InvalidOperationException();
@@ -297,6 +353,21 @@ namespace Ryujinx.Graphics.Gal.Shader
             SB.AppendLine(LastLine);
         }
 
+        private int FindLabel(ShaderIrNode[] Nodes, ShaderIrLabel Label, int Start)
+        {
+            int Target;
+
+            for (Target = Start; Target < Nodes.Length; Target++)
+            {
+                if (Nodes[Target] == Label)
+                {
+                    return Target;
+                }
+            }
+
+            throw new InvalidOperationException();
+        }
+
         private bool IsValidOutOper(ShaderIrNode Node)
         {
             if (Node is ShaderIrOperGpr Gpr && Gpr.IsConst)
@@ -383,12 +454,23 @@ namespace Ryujinx.Graphics.Gal.Shader
 
         private string GetName(ShaderIrOperCbuf Cbuf)
         {
-            if (!Decl.Uniforms.TryGetValue((Cbuf.Index, Cbuf.Offs), out ShaderDeclInfo DeclInfo))
+            if (!Decl.Uniforms.TryGetValue(Cbuf.Index, out ShaderDeclInfo DeclInfo))
             {
                 throw new InvalidOperationException();
             }
 
-            return DeclInfo.Name;
+            if (Cbuf.Offs != null)
+            {
+                //Note: We assume that the register value is always a multiple of 4.
+                //This may not be aways the case.
+                string Offset = "(floatBitsToInt(" + GetSrcExpr(Cbuf.Offs) + ") >> 2)";
+
+                return DeclInfo.Name + "[" + Cbuf.Pos + " + " + Offset + "]";
+            }
+            else
+            {
+                return DeclInfo.Name + "[" + Cbuf.Pos + "]";
+            }
         }
 
         private string GetOutAbufName(ShaderIrOperAbuf Abuf)
@@ -473,6 +555,10 @@ namespace Ryujinx.Graphics.Gal.Shader
             return "xyzw".Substring(Elem, 1);
         }
 
+        private string GetAbsExpr(ShaderIrOp Op) => GetUnaryCall(Op, "abs");
+
+        private string GetAddExpr(ShaderIrOp Op) => GetBinaryExpr(Op, "+");
+
         private string GetAndExpr(ShaderIrOp Op) => GetBinaryExpr(Op, "&");
 
         private string GetAsrExpr(ShaderIrOp Op) => GetBinaryExpr(Op, ">>");
@@ -506,10 +592,6 @@ namespace Ryujinx.Graphics.Gal.Shader
 
         private string GetExitExpr(ShaderIrOp Op) => "return";
 
-        private string GetFabsExpr(ShaderIrOp Op) => GetUnaryCall(Op, "abs");
-
-        private string GetFaddExpr(ShaderIrOp Op) => GetBinaryExpr(Op, "+");
-
         private string GetFcosExpr(ShaderIrOp Op) => GetUnaryCall(Op, "cos");
 
         private string GetFex2Expr(ShaderIrOp Op) => GetUnaryCall(Op, "exp2");
@@ -522,9 +604,8 @@ namespace Ryujinx.Graphics.Gal.Shader
 
         private string GetFloorExpr(ShaderIrOp Op) => GetUnaryCall(Op, "floor");
 
-        private string GetFmulExpr(ShaderIrOp Op) => GetBinaryExpr(Op, "*");
-
-        private string GetFnegExpr(ShaderIrOp Op) => GetUnaryExpr(Op, "-");
+        private string GetFmaxExpr(ShaderIrOp Op) => GetBinaryCall(Op, "max");
+        private string GetFminExpr(ShaderIrOp Op) => GetBinaryCall(Op, "min");
 
         private string GetFrcpExpr(ShaderIrOp Op) => GetUnaryExpr(Op, "1 / ");
 
@@ -546,12 +627,17 @@ namespace Ryujinx.Graphics.Gal.Shader
 
         private string GetKilExpr(ShaderIrOp Op) => "discard";
 
+        private string GetLslExpr(ShaderIrOp Op) => GetBinaryExpr(Op, "<<");
         private string GetLsrExpr(ShaderIrOp Op)
         {
             return "int(uint(" + GetOperExpr(Op, Op.OperandA) + ") >> " +
                                  GetOperExpr(Op, Op.OperandB) + ")";
         }
 
+        private string GetMulExpr(ShaderIrOp Op) => GetBinaryExpr(Op, "*");
+
+        private string GetNegExpr(ShaderIrOp Op) => GetUnaryExpr(Op, "-");
+
         private string GetNotExpr(ShaderIrOp Op) => GetUnaryExpr(Op, "~");
 
         private string GetOrExpr(ShaderIrOp Op) => GetBinaryExpr(Op, "|");
@@ -561,6 +647,8 @@ namespace Ryujinx.Graphics.Gal.Shader
             return "float(" + GetOperExpr(Op, Op.OperandA) + ")";
         }
 
+        private string GetSubExpr(ShaderIrOp Op) => GetBinaryExpr(Op, "-");
+
         private string GetTexqExpr(ShaderIrOp Op)
         {
             ShaderIrMetaTexq Meta = (ShaderIrMetaTexq)Op.MetaData;
@@ -621,6 +709,12 @@ namespace Ryujinx.Graphics.Gal.Shader
             return FuncName + "(" + GetOperExpr(Op, Op.OperandA) + ")";
         }
 
+        private string GetBinaryCall(ShaderIrOp Op, string FuncName)
+        {
+            return FuncName + "(" + GetOperExpr(Op, Op.OperandA) + ", " +
+                                    GetOperExpr(Op, Op.OperandB) + ")";
+        }
+
         private string GetTernaryCall(ShaderIrOp Op, string FuncName)
         {
             return FuncName + "(" + GetOperExpr(Op, Op.OperandA) + ", " +
@@ -717,7 +811,10 @@ namespace Ryujinx.Graphics.Gal.Shader
                         {
                             float Value = BitConverter.Int32BitsToSingle(Imm.Value);
 
-                            return Value.ToString(CultureInfo.InvariantCulture);
+                            if (!float.IsNaN(Value) && !float.IsInfinity(Value))
+                            {
+                                return Value.ToString(CultureInfo.InvariantCulture);
+                            }
                         }
                         break;
                     }
diff --git a/Ryujinx.Graphics/Gal/Shader/GlslProgram.cs b/Ryujinx.Graphics/Gal/Shader/GlslProgram.cs
index 729b6f1de..a7af05aef 100644
--- a/Ryujinx.Graphics/Gal/Shader/GlslProgram.cs
+++ b/Ryujinx.Graphics/Gal/Shader/GlslProgram.cs
@@ -2,7 +2,7 @@ using System.Collections.Generic;
 
 namespace Ryujinx.Graphics.Gal.Shader
 {
-    struct GlslProgram
+    public struct GlslProgram
     {
         public string Code { get; private set; }
 
diff --git a/Ryujinx.Graphics/Gal/Shader/ShaderDecodeAlu.cs b/Ryujinx.Graphics/Gal/Shader/ShaderDecodeAlu.cs
index 42609bcef..1bb5f4780 100644
--- a/Ryujinx.Graphics/Gal/Shader/ShaderDecodeAlu.cs
+++ b/Ryujinx.Graphics/Gal/Shader/ShaderDecodeAlu.cs
@@ -6,6 +6,21 @@ namespace Ryujinx.Graphics.Gal.Shader
 {
     static partial class ShaderDecode
     {
+        public static void Bfe_C(ShaderIrBlock Block, long OpCode)
+        {
+            EmitBfe(Block, OpCode, ShaderOper.CR);
+        }
+
+        public static void Bfe_I(ShaderIrBlock Block, long OpCode)
+        {
+            EmitBfe(Block, OpCode, ShaderOper.Imm);
+        }
+
+        public static void Bfe_R(ShaderIrBlock Block, long OpCode)
+        {
+            EmitBfe(Block, OpCode, ShaderOper.RR);
+        }
+
         public static void Fadd_C(ShaderIrBlock Block, long OpCode)
         {
             EmitAluBinaryF(Block, OpCode, ShaderOper.CR, ShaderIrInst.Fadd);
@@ -23,25 +38,40 @@ namespace Ryujinx.Graphics.Gal.Shader
 
         public static void Ffma_CR(ShaderIrBlock Block, long OpCode)
         {
-            EmitAluFfma(Block, OpCode, ShaderOper.CR);
+            EmitFfma(Block, OpCode, ShaderOper.CR);
         }
 
         public static void Ffma_I(ShaderIrBlock Block, long OpCode)
         {
-            EmitAluFfma(Block, OpCode, ShaderOper.Immf);
+            EmitFfma(Block, OpCode, ShaderOper.Immf);
         }
 
         public static void Ffma_RC(ShaderIrBlock Block, long OpCode)
         {
-            EmitAluFfma(Block, OpCode, ShaderOper.RC);
+            EmitFfma(Block, OpCode, ShaderOper.RC);
         }
 
         public static void Ffma_RR(ShaderIrBlock Block, long OpCode)
         {
-            EmitAluFfma(Block, OpCode, ShaderOper.RR);
+            EmitFfma(Block, OpCode, ShaderOper.RR);
         }
 
-        public static void Fmul32i(ShaderIrBlock Block, long OpCode)
+        public static void Fmnmx_C(ShaderIrBlock Block, long OpCode)
+        {
+            EmitFmnmx(Block, OpCode, ShaderOper.CR);
+        }
+
+        public static void Fmnmx_I(ShaderIrBlock Block, long OpCode)
+        {
+            EmitFmnmx(Block, OpCode, ShaderOper.Immf);
+        }
+
+        public static void Fmnmx_R(ShaderIrBlock Block, long OpCode)
+        {
+            EmitFmnmx(Block, OpCode, ShaderOper.RR);
+        }
+
+        public static void Fmul_I32(ShaderIrBlock Block, long OpCode)
         {
             ShaderIrNode OperA = GetOperGpr8     (OpCode);
             ShaderIrNode OperB = GetOperImmf32_20(OpCode);
@@ -106,6 +136,21 @@ namespace Ryujinx.Graphics.Gal.Shader
             Block.AddNode(GetPredNode(new ShaderIrAsg(GetOperGpr0(OpCode), Op), OpCode));
         }
 
+        public static void Iscadd_C(ShaderIrBlock Block, long OpCode)
+        {
+            EmitIscadd(Block, OpCode, ShaderOper.CR);
+        }
+
+        public static void Iscadd_I(ShaderIrBlock Block, long OpCode)
+        {
+            EmitIscadd(Block, OpCode, ShaderOper.Imm);
+        }
+
+        public static void Iscadd_R(ShaderIrBlock Block, long OpCode)
+        {
+            EmitIscadd(Block, OpCode, ShaderOper.RR);
+        }
+
         public static void Isetp_C(ShaderIrBlock Block, long OpCode)
         {
             EmitIsetp(Block, OpCode, ShaderOper.CR);
@@ -121,12 +166,12 @@ namespace Ryujinx.Graphics.Gal.Shader
             EmitIsetp(Block, OpCode, ShaderOper.RR);
         }
 
-        public static void Lop32i(ShaderIrBlock Block, long OpCode)
+        public static void Lop_I32(ShaderIrBlock Block, long OpCode)
         {
             int SubOp = (int)(OpCode >> 53) & 3;
 
-            bool Ia = ((OpCode >> 55) & 1) != 0;
-            bool Ib = ((OpCode >> 56) & 1) != 0;
+            bool InvA = ((OpCode >> 55) & 1) != 0;
+            bool InvB = ((OpCode >> 56) & 1) != 0;
 
             ShaderIrInst Inst = 0;
 
@@ -137,13 +182,13 @@ namespace Ryujinx.Graphics.Gal.Shader
                 case 2: Inst = ShaderIrInst.Xor; break;
             }
 
-            ShaderIrNode OperA = GetAluNot(GetOperGpr8(OpCode), Ia);
+            ShaderIrNode OperA = GetAluNot(GetOperGpr8(OpCode), InvA);
 
             //SubOp == 3 is pass, used by the not instruction
             //which just moves the inverted register value.
             if (SubOp < 3)
             {
-                ShaderIrNode OperB = GetAluNot(GetOperImm32_20(OpCode), Ib);
+                ShaderIrNode OperB = GetAluNot(GetOperImm32_20(OpCode), InvB);
 
                 ShaderIrOp Op = new ShaderIrOp(Inst, OperA, OperB);
 
@@ -159,8 +204,8 @@ namespace Ryujinx.Graphics.Gal.Shader
         {
             int SubOp = (int)(OpCode >> 20) & 7;
 
-            bool Aa = ((OpCode >> 46) & 1) != 0;
-            bool Na = ((OpCode >> 48) & 1) != 0;
+            bool AbsA = ((OpCode >> 46) & 1) != 0;
+            bool NegA = ((OpCode >> 48) & 1) != 0;
 
             ShaderIrInst Inst = 0;
 
@@ -178,11 +223,26 @@ namespace Ryujinx.Graphics.Gal.Shader
 
             ShaderIrNode OperA = GetOperGpr8(OpCode);
 
-            ShaderIrOp Op = new ShaderIrOp(Inst, GetAluAbsNeg(OperA, Aa, Na));
+            ShaderIrOp Op = new ShaderIrOp(Inst, GetAluFabsFneg(OperA, AbsA, NegA));
 
             Block.AddNode(GetPredNode(new ShaderIrAsg(GetOperGpr0(OpCode), Op), OpCode));
         }
 
+        public static void Shl_C(ShaderIrBlock Block, long OpCode)
+        {
+            EmitAluBinary(Block, OpCode, ShaderOper.CR, ShaderIrInst.Lsl);
+        }
+
+        public static void Shl_I(ShaderIrBlock Block, long OpCode)
+        {
+            EmitAluBinary(Block, OpCode, ShaderOper.Imm, ShaderIrInst.Lsl);
+        }
+
+        public static void Shl_R(ShaderIrBlock Block, long OpCode)
+        {
+            EmitAluBinary(Block, OpCode, ShaderOper.RR, ShaderIrInst.Lsl);
+        }
+
         public static void Shr_C(ShaderIrBlock Block, long OpCode)
         {
             EmitAluBinary(Block, OpCode, ShaderOper.CR, GetShrInst(OpCode));
@@ -233,16 +293,16 @@ namespace Ryujinx.Graphics.Gal.Shader
             ShaderOper    Oper,
             ShaderIrInst  Inst)
         {
-            bool Nb = ((OpCode >> 45) & 1) != 0;
-            bool Aa = ((OpCode >> 46) & 1) != 0;
-            bool Na = ((OpCode >> 48) & 1) != 0;
-            bool Ab = ((OpCode >> 49) & 1) != 0;
+            bool NegB = ((OpCode >> 45) & 1) != 0;
+            bool AbsA = ((OpCode >> 46) & 1) != 0;
+            bool NegA = ((OpCode >> 48) & 1) != 0;
+            bool AbsB = ((OpCode >> 49) & 1) != 0;
 
             ShaderIrNode OperA = GetOperGpr8(OpCode), OperB;
 
             if (Inst == ShaderIrInst.Fadd)
             {
-                OperA = GetAluAbsNeg(OperA, Aa, Na);
+                OperA = GetAluFabsFneg(OperA, AbsA, NegA);
             }
 
             switch (Oper)
@@ -254,17 +314,73 @@ namespace Ryujinx.Graphics.Gal.Shader
                 default: throw new ArgumentException(nameof(Oper));
             }
 
-            OperB = GetAluAbsNeg(OperB, Ab, Nb);
+            OperB = GetAluFabsFneg(OperB, AbsB, NegB);
 
             ShaderIrNode Op = new ShaderIrOp(Inst, OperA, OperB);
 
             Block.AddNode(GetPredNode(new ShaderIrAsg(GetOperGpr0(OpCode), Op), OpCode));
         }
 
-        private static void EmitAluFfma(ShaderIrBlock Block, long OpCode, ShaderOper Oper)
+        private static void EmitBfe(ShaderIrBlock Block, long OpCode, ShaderOper Oper)
         {
-            bool Nb = ((OpCode >> 48) & 1) != 0;
-            bool Nc = ((OpCode >> 49) & 1) != 0;
+            //TODO: Handle the case where position + length
+            //is greater than the word size, in this case the sign bit
+            //needs to be replicated to fill the remaining space.
+            bool NegB = ((OpCode >> 48) & 1) != 0;
+            bool NegA = ((OpCode >> 49) & 1) != 0;
+
+            ShaderIrNode OperA = GetOperGpr8(OpCode), OperB;
+
+            switch (Oper)
+            {
+                case ShaderOper.CR:  OperB = GetOperCbuf34  (OpCode); break;
+                case ShaderOper.Imm: OperB = GetOperImm19_20(OpCode); break;
+                case ShaderOper.RR:  OperB = GetOperGpr20   (OpCode); break;
+
+                default: throw new ArgumentException(nameof(Oper));
+            }
+
+            ShaderIrNode Op;
+
+            bool Signed = ((OpCode >> 48) & 1) != 0; //?
+
+            if (OperB is ShaderIrOperImm PosLen)
+            {
+                int Position = (PosLen.Value >> 0) & 0xff;
+                int Length   = (PosLen.Value >> 8) & 0xff;
+
+                int LSh = 32 - (Position + Length);
+
+                ShaderIrInst RightShift = Signed
+                    ? ShaderIrInst.Asr
+                    : ShaderIrInst.Lsr;
+
+                Op = new ShaderIrOp(ShaderIrInst.Lsl, OperA, new ShaderIrOperImm(LSh));
+                Op = new ShaderIrOp(RightShift,       Op,    new ShaderIrOperImm(LSh + Position));
+            }
+            else
+            {
+                ShaderIrOperImm Shift = new ShaderIrOperImm(8);
+                ShaderIrOperImm Mask  = new ShaderIrOperImm(0xff);
+
+                ShaderIrNode OpPos, OpLen;
+
+                OpPos = new ShaderIrOp(ShaderIrInst.And, OperB, Mask);
+                OpLen = new ShaderIrOp(ShaderIrInst.Lsr, OperB, Shift);
+                OpLen = new ShaderIrOp(ShaderIrInst.And, OpLen, Mask);
+
+                Op = new ShaderIrOp(ShaderIrInst.Lsr, OperA, OpPos);
+
+                Op = ExtendTo32(Op, Signed, OpLen);
+            }
+
+            Block.AddNode(GetPredNode(new ShaderIrAsg(GetOperGpr0(OpCode), Op), OpCode));
+        }
+
+        private static void EmitFfma(ShaderIrBlock Block, long OpCode, ShaderOper Oper)
+        {
+            bool NegB = ((OpCode >> 48) & 1) != 0;
+            bool NegC = ((OpCode >> 49) & 1) != 0;
 
             ShaderIrNode OperA = GetOperGpr8(OpCode), OperB, OperC;
 
@@ -278,15 +394,15 @@ namespace Ryujinx.Graphics.Gal.Shader
                 default: throw new ArgumentException(nameof(Oper));
             }
 
-            OperB = GetAluNeg(OperB, Nb);
+            OperB = GetAluFneg(OperB, NegB);
 
             if (Oper == ShaderOper.RC)
             {
-                OperC = GetAluNeg(GetOperCbuf34(OpCode), Nc);
+                OperC = GetAluFneg(GetOperCbuf34(OpCode), NegC);
             }
             else
             {
-                OperC = GetAluNeg(GetOperGpr39(OpCode), Nc);
+                OperC = GetAluFneg(GetOperGpr39(OpCode), NegC);
             }
 
             ShaderIrOp Op = new ShaderIrOp(ShaderIrInst.Ffma, OperA, OperB, OperC);
@@ -294,6 +410,84 @@ namespace Ryujinx.Graphics.Gal.Shader
             Block.AddNode(GetPredNode(new ShaderIrAsg(GetOperGpr0(OpCode), Op), OpCode));
         }
 
+        private static void EmitFmnmx(ShaderIrBlock Block, long OpCode, ShaderOper Oper)
+        {
+            bool NegB = ((OpCode >> 45) & 1) != 0;
+            bool AbsA = ((OpCode >> 46) & 1) != 0;
+            bool NegA = ((OpCode >> 48) & 1) != 0;
+            bool AbsB = ((OpCode >> 49) & 1) != 0;
+
+            ShaderIrNode OperA = GetOperGpr8(OpCode), OperB;
+
+            OperA = GetAluFabsFneg(OperA, AbsA, NegA);
+
+            switch (Oper)
+            {
+                case ShaderOper.CR:   OperB = GetOperCbuf34   (OpCode); break;
+                case ShaderOper.Immf: OperB = GetOperImmf19_20(OpCode); break;
+                case ShaderOper.RR:   OperB = GetOperGpr20    (OpCode); break;
+
+                default: throw new ArgumentException(nameof(Oper));
+            }
+
+            OperB = GetAluFabsFneg(OperB, AbsB, NegB);
+
+            ShaderIrOperPred Pred = GetOperPred39(OpCode);
+
+            ShaderIrOp Op;
+
+            if (Pred.IsConst)
+            {
+                bool IsMax = ((OpCode >> 42) & 1) != 0;
+
+                Op = new ShaderIrOp(IsMax
+                    ? ShaderIrInst.Fmax
+                    : ShaderIrInst.Fmin, OperA, OperB);
+
+                Block.AddNode(GetPredNode(new ShaderIrAsg(GetOperGpr0(OpCode), Op), OpCode));
+            }
+            else
+            {
+                ShaderIrNode PredN = GetOperPred39N(OpCode);
+
+                ShaderIrOp OpMax = new ShaderIrOp(ShaderIrInst.Fmax, OperA, OperB);
+                ShaderIrOp OpMin = new ShaderIrOp(ShaderIrInst.Fmin, OperA, OperB);
+
+                ShaderIrAsg AsgMax = new ShaderIrAsg(GetOperGpr0(OpCode), OpMax);
+                ShaderIrAsg AsgMin = new ShaderIrAsg(GetOperGpr0(OpCode), OpMin);
+
+                Block.AddNode(GetPredNode(new ShaderIrCond(PredN, AsgMax, Not: true),  OpCode));
+                Block.AddNode(GetPredNode(new ShaderIrCond(PredN, AsgMin, Not: false), OpCode));
+            }
+        }
+
+        private static void EmitIscadd(ShaderIrBlock Block, long OpCode, ShaderOper Oper)
+        {
+            bool NegB = ((OpCode >> 48) & 1) != 0;
+            bool NegA = ((OpCode >> 49) & 1) != 0;
+
+            ShaderIrNode OperA = GetOperGpr8(OpCode), OperB;
+
+            ShaderIrOperImm Scale = GetOperImm5_39(OpCode);
+
+            switch (Oper)
+            {
+                case ShaderOper.CR:  OperB = GetOperCbuf34  (OpCode); break;
+                case ShaderOper.Imm: OperB = GetOperImm19_20(OpCode); break;
+                case ShaderOper.RR:  OperB = GetOperGpr20   (OpCode); break;
+
+                default: throw new ArgumentException(nameof(Oper));
+            }
+
+            OperA = GetAluIneg(OperA, NegA);
+            OperB = GetAluIneg(OperB, NegB);
+
+            ShaderIrOp ScaleOp = new ShaderIrOp(ShaderIrInst.Lsl, OperA, Scale);
+            ShaderIrOp AddOp   = new ShaderIrOp(ShaderIrInst.Add, OperB, ScaleOp);
+
+            Block.AddNode(GetPredNode(new ShaderIrAsg(GetOperGpr0(OpCode), AddOp), OpCode));
+        }
+
         private static void EmitFset(ShaderIrBlock Block, long OpCode, ShaderOper Oper)
         {
             EmitSet(Block, OpCode, true, Oper);
@@ -306,10 +500,11 @@ namespace Ryujinx.Graphics.Gal.Shader
 
         private static void EmitSet(ShaderIrBlock Block, long OpCode, bool IsFloat, ShaderOper Oper)
         {
-            bool Na = ((OpCode >> 43) & 1) != 0;
-            bool Ab = ((OpCode >> 44) & 1) != 0;
-            bool Nb = ((OpCode >> 53) & 1) != 0;
-            bool Aa = ((OpCode >> 54) & 1) != 0;
+            bool NegA      = ((OpCode >> 43) & 1) != 0;
+            bool AbsB      = ((OpCode >> 44) & 1) != 0;
+            bool BoolFloat = ((OpCode >> 52) & 1) != 0;
+            bool NegB      = ((OpCode >> 53) & 1) != 0;
+            bool AbsA      = ((OpCode >> 54) & 1) != 0;
 
             ShaderIrNode OperA = GetOperGpr8(OpCode), OperB;
 
@@ -327,8 +522,8 @@ namespace Ryujinx.Graphics.Gal.Shader
 
             if (IsFloat)
             {
-                OperA = GetAluAbsNeg(OperA, Aa, Na);
-                OperB = GetAluAbsNeg(OperB, Ab, Nb);
+                OperA = GetAluFabsFneg(OperA, AbsA, NegA);
+                OperB = GetAluFabsFneg(OperB, AbsB, NegB);
 
                 CmpInst = GetCmpF(OpCode);
             }
@@ -343,8 +538,18 @@ namespace Ryujinx.Graphics.Gal.Shader
 
             ShaderIrOperPred PNode = GetOperPred39(OpCode);
 
-            ShaderIrOperImmf Imm0 = new ShaderIrOperImmf(0);
-            ShaderIrOperImmf Imm1 = new ShaderIrOperImmf(1);
+            ShaderIrNode Imm0, Imm1;
+
+            if (BoolFloat)
+            {
+                Imm0 = new ShaderIrOperImmf(0);
+                Imm1 = new ShaderIrOperImmf(1);
+            }
+            else
+            {
+                Imm0 = new ShaderIrOperImm(0);
+                Imm1 = new ShaderIrOperImm(-1);
+            }
 
             ShaderIrNode Asg0 = new ShaderIrAsg(GetOperGpr0(OpCode), Imm0);
             ShaderIrNode Asg1 = new ShaderIrAsg(GetOperGpr0(OpCode), Imm1);
@@ -378,10 +583,10 @@ namespace Ryujinx.Graphics.Gal.Shader
 
         private static void EmitSetp(ShaderIrBlock Block, long OpCode, bool IsFloat, ShaderOper Oper)
         {
-            bool Aa = ((OpCode >>  7) & 1) != 0;
-            bool Np = ((OpCode >> 42) & 1) != 0;
-            bool Na = ((OpCode >> 43) & 1) != 0;
-            bool Ab = ((OpCode >> 44) & 1) != 0;
+            bool AbsA = ((OpCode >>  7) & 1) != 0;
+            bool NegP = ((OpCode >> 42) & 1) != 0;
+            bool NegA = ((OpCode >> 43) & 1) != 0;
+            bool AbsB = ((OpCode >> 44) & 1) != 0;
 
             ShaderIrNode OperA = GetOperGpr8(OpCode), OperB;
 
@@ -399,8 +604,8 @@ namespace Ryujinx.Graphics.Gal.Shader
 
             if (IsFloat)
             {
-                OperA = GetAluAbsNeg(OperA, Aa, Na);
-                OperB = GetAluAbs   (OperB, Ab);
+                OperA = GetAluFabsFneg(OperA, AbsA, NegA);
+                OperB = GetAluFabs    (OperB, AbsB);
 
                 CmpInst = GetCmpF(OpCode);
             }
@@ -426,7 +631,7 @@ namespace Ryujinx.Graphics.Gal.Shader
 
             ShaderIrNode P2NNode = P2Node;
 
-            if (Np)
+            if (NegP)
             {
                 P2NNode = new ShaderIrOp(ShaderIrInst.Bnot, P2NNode);
             }
diff --git a/Ryujinx.Graphics/Gal/Shader/ShaderDecodeFlow.cs b/Ryujinx.Graphics/Gal/Shader/ShaderDecodeFlow.cs
index d3feb92e5..b5f3e0b72 100644
--- a/Ryujinx.Graphics/Gal/Shader/ShaderDecodeFlow.cs
+++ b/Ryujinx.Graphics/Gal/Shader/ShaderDecodeFlow.cs
@@ -1,9 +1,27 @@
+using System;
+
 using static Ryujinx.Graphics.Gal.Shader.ShaderDecodeHelper;
 
 namespace Ryujinx.Graphics.Gal.Shader
 {
     static partial class ShaderDecode
     {
+        public static void Bra(ShaderIrBlock Block, long OpCode)
+        {
+            if ((OpCode & 0x20) != 0)
+            {
+                //This reads the target offset from the constant buffer.
+                //Almost impossible to support with GLSL.
+                throw new NotImplementedException();
+            }
+
+            int Target = ((int)(OpCode >> 20) << 8) >> 8;
+
+            Target += Block.Position + 8;
+
+            Block.AddNode(GetPredNode(new ShaderIrOp(ShaderIrInst.Bra, Block.GetLabel(Target)), OpCode));
+        }
+
         public static void Exit(ShaderIrBlock Block, long OpCode)
         {
             Block.AddNode(GetPredNode(new ShaderIrOp(ShaderIrInst.Exit), OpCode));
diff --git a/Ryujinx.Graphics/Gal/Shader/ShaderDecodeHelper.cs b/Ryujinx.Graphics/Gal/Shader/ShaderDecodeHelper.cs
index efbce20f2..73775ccaf 100644
--- a/Ryujinx.Graphics/Gal/Shader/ShaderDecodeHelper.cs
+++ b/Ryujinx.Graphics/Gal/Shader/ShaderDecodeHelper.cs
@@ -35,6 +35,13 @@ namespace Ryujinx.Graphics.Gal.Shader
                 (int)(OpCode >> 20) & 0x3fff);
         }
 
+        public static ShaderIrOperCbuf GetOperCbuf36(long OpCode)
+        {
+            return new ShaderIrOperCbuf(
+                (int)(OpCode >> 36) & 0x1f,
+                (int)(OpCode >> 22) & 0x3fff, GetOperGpr8(OpCode));
+        }
+
         public static ShaderIrOperGpr GetOperGpr8(long OpCode)
         {
             return new ShaderIrOperGpr((int)(OpCode >> 8) & 0xff);
@@ -60,6 +67,11 @@ namespace Ryujinx.Graphics.Gal.Shader
             return new ShaderIrOperGpr((int)(OpCode >> 28) & 0xff);
         }
 
+        public static ShaderIrOperImm GetOperImm5_39(long OpCode)
+        {
+            return new ShaderIrOperImm((int)(OpCode >> 39) & 0x1f);
+        }
+
         public static ShaderIrOperImm GetOperImm13_36(long OpCode)
         {
             return new ShaderIrOperImm((int)(OpCode >> 36) & 0x1fff);
@@ -210,24 +222,69 @@ namespace Ryujinx.Graphics.Gal.Shader
             return new ShaderIrOperPred(Pred);
         }
 
-        public static ShaderIrNode GetAluAbsNeg(ShaderIrNode Node, bool Abs, bool Neg)
+        public static ShaderIrNode GetAluFabsFneg(ShaderIrNode Node, bool Abs, bool Neg)
         {
-            return GetAluNeg(GetAluAbs(Node, Abs), Neg);
+            return GetAluFneg(GetAluFabs(Node, Abs), Neg);
         }
 
-        public static ShaderIrNode GetAluAbs(ShaderIrNode Node, bool Abs)
+        public static ShaderIrNode GetAluFabs(ShaderIrNode Node, bool Abs)
         {
             return Abs ? new ShaderIrOp(ShaderIrInst.Fabs, Node) : Node;
         }
 
-        public static ShaderIrNode GetAluNeg(ShaderIrNode Node, bool Neg)
+        public static ShaderIrNode GetAluFneg(ShaderIrNode Node, bool Neg)
         {
             return Neg ? new ShaderIrOp(ShaderIrInst.Fneg, Node) : Node;
         }
 
+        public static ShaderIrNode GetAluIabsIneg(ShaderIrNode Node, bool Abs, bool Neg)
+        {
+            return GetAluIneg(GetAluIabs(Node, Abs), Neg);
+        }
+
+        public static ShaderIrNode GetAluIabs(ShaderIrNode Node, bool Abs)
+        {
+            return Abs ? new ShaderIrOp(ShaderIrInst.Abs, Node) : Node;
+        }
+
+        public static ShaderIrNode GetAluIneg(ShaderIrNode Node, bool Neg)
+        {
+            return Neg ? new ShaderIrOp(ShaderIrInst.Neg, Node) : Node;
+        }
+
         public static ShaderIrNode GetAluNot(ShaderIrNode Node, bool Not)
         {
             return Not ? new ShaderIrOp(ShaderIrInst.Not, Node) : Node;
         }
+
+        public static ShaderIrNode ExtendTo32(ShaderIrNode Node, bool Signed, int Size)
+        {
+            int Shift = 32 - Size;
+
+            ShaderIrInst RightShift = Signed
+                ? ShaderIrInst.Asr
+                : ShaderIrInst.Lsr;
+
+            Node = new ShaderIrOp(ShaderIrInst.Lsl, Node, new ShaderIrOperImm(Shift));
+            Node = new ShaderIrOp(RightShift,       Node, new ShaderIrOperImm(Shift));
+
+            return Node;
+        }
+
+        public static ShaderIrNode ExtendTo32(ShaderIrNode Node, bool Signed, ShaderIrNode Size)
+        {
+            ShaderIrOperImm WordSize = new ShaderIrOperImm(32);
+
+            ShaderIrOp Shift = new ShaderIrOp(ShaderIrInst.Sub, WordSize, Size);
+
+            ShaderIrInst RightShift = Signed
+                ? ShaderIrInst.Asr
+                : ShaderIrInst.Lsr;
+
+            Node = new ShaderIrOp(ShaderIrInst.Lsl, Node, Shift);
+            Node = new ShaderIrOp(RightShift,       Node, Shift);
+
+            return Node;
+        }
     }
 }
\ No newline at end of file
diff --git a/Ryujinx.Graphics/Gal/Shader/ShaderDecodeMem.cs b/Ryujinx.Graphics/Gal/Shader/ShaderDecodeMem.cs
index 24a61c0c2..5ca485a3b 100644
--- a/Ryujinx.Graphics/Gal/Shader/ShaderDecodeMem.cs
+++ b/Ryujinx.Graphics/Gal/Shader/ShaderDecodeMem.cs
@@ -1,3 +1,5 @@
+using System;
+
 using static Ryujinx.Graphics.Gal.Shader.ShaderDecodeHelper;
 
 namespace Ryujinx.Graphics.Gal.Shader
@@ -20,6 +22,41 @@ namespace Ryujinx.Graphics.Gal.Shader
             }
         }
 
+        public static void Ld_C(ShaderIrBlock Block, long OpCode)
+        {
+            int Type = (int)(OpCode >> 48) & 7;
+
+            if (Type > 5)
+            {
+                throw new InvalidOperationException();
+            }
+
+            int Count = Type == 5 ? 2 : 1;
+
+            for (int Index = 0; Index < Count; Index++)
+            {
+                ShaderIrOperCbuf OperA = GetOperCbuf36(OpCode);
+                ShaderIrOperGpr  OperD = GetOperGpr0  (OpCode);
+
+                OperA.Pos   += Index;
+                OperD.Index += Index;
+
+                ShaderIrNode Node = OperA;
+
+                if (Type < 4)
+                {
+                    //This is a 8 or 16 bits type.
+                    bool Signed = (Type & 1) != 0;
+
+                    int Size = 8 << (Type >> 1);
+
+                    Node = ExtendTo32(Node, Signed, Size);
+                }
+
+                Block.AddNode(GetPredNode(new ShaderIrAsg(OperD, Node), OpCode));
+            }
+        }
+
         public static void St_A(ShaderIrBlock Block, long OpCode)
         {
             ShaderIrNode[] Opers = GetOperAbuf20(OpCode);
diff --git a/Ryujinx.Graphics/Gal/Shader/ShaderDecodeMove.cs b/Ryujinx.Graphics/Gal/Shader/ShaderDecodeMove.cs
index b9cf02f1f..dfcea905a 100644
--- a/Ryujinx.Graphics/Gal/Shader/ShaderDecodeMove.cs
+++ b/Ryujinx.Graphics/Gal/Shader/ShaderDecodeMove.cs
@@ -99,6 +99,13 @@ namespace Ryujinx.Graphics.Gal.Shader
             Block.AddNode(GetPredNode(new ShaderIrAsg(GetOperGpr0(OpCode), Imm), OpCode));
         }
 
+        public static void Mov_I32(ShaderIrBlock Block, long OpCode)
+        {
+            ShaderIrOperImm Imm = GetOperImm32_20(OpCode);
+
+            Block.AddNode(GetPredNode(new ShaderIrAsg(GetOperGpr0(OpCode), Imm), OpCode));
+        }
+
         public static void Mov_R(ShaderIrBlock Block, long OpCode)
         {
             ShaderIrOperGpr Gpr = GetOperGpr20(OpCode);
@@ -106,17 +113,10 @@ namespace Ryujinx.Graphics.Gal.Shader
             Block.AddNode(GetPredNode(new ShaderIrAsg(GetOperGpr0(OpCode), Gpr), OpCode));
         }
 
-        public static void Mov32i(ShaderIrBlock Block, long OpCode)
-        {
-            ShaderIrOperImm Imm = GetOperImm32_20(OpCode);
-
-            Block.AddNode(GetPredNode(new ShaderIrAsg(GetOperGpr0(OpCode), Imm), OpCode));
-        }
-
         private static void EmitF2f(ShaderIrBlock Block, long OpCode, ShaderOper Oper)
         {
-            bool Na = ((OpCode >> 45) & 1) != 0;
-            bool Aa = ((OpCode >> 49) & 1) != 0;
+            bool NegA = ((OpCode >> 45) & 1) != 0;
+            bool AbsA = ((OpCode >> 49) & 1) != 0;
 
             ShaderIrNode OperA;
 
@@ -129,7 +129,7 @@ namespace Ryujinx.Graphics.Gal.Shader
                 default: throw new ArgumentException(nameof(Oper));
             }
 
-            OperA = GetAluAbsNeg(OperA, Aa, Na);
+            OperA = GetAluFabsFneg(OperA, AbsA, NegA);
 
             ShaderIrInst RoundInst = GetRoundInst(OpCode);
 
@@ -153,8 +153,8 @@ namespace Ryujinx.Graphics.Gal.Shader
                 throw new NotImplementedException();
             }
 
-            bool Na = ((OpCode >> 45) & 1) != 0;
-            bool Aa = ((OpCode >> 49) & 1) != 0;
+            bool NegA = ((OpCode >> 45) & 1) != 0;
+            bool AbsA = ((OpCode >> 49) & 1) != 0;
 
             ShaderIrNode OperA;
 
@@ -167,7 +167,7 @@ namespace Ryujinx.Graphics.Gal.Shader
                 default: throw new ArgumentException(nameof(Oper));
             }
 
-            OperA = GetAluAbsNeg(OperA, Aa, Na);
+            OperA = GetAluFabsFneg(OperA, AbsA, NegA);
 
             ShaderIrInst RoundInst = GetRoundInst(OpCode);
 
@@ -224,8 +224,8 @@ namespace Ryujinx.Graphics.Gal.Shader
 
             int Sel = (int)(OpCode >> 41) & 3;
 
-            bool Na = ((OpCode >> 45) & 1) != 0;
-            bool Aa = ((OpCode >> 49) & 1) != 0;
+            bool NegA = ((OpCode >> 45) & 1) != 0;
+            bool AbsA = ((OpCode >> 49) & 1) != 0;
 
             ShaderIrNode OperA;
 
@@ -238,7 +238,7 @@ namespace Ryujinx.Graphics.Gal.Shader
                 default: throw new ArgumentException(nameof(Oper));
             }
 
-            OperA = GetAluAbsNeg(OperA, Aa, Na);
+            OperA = GetAluIabsIneg(OperA, AbsA, NegA);
 
             bool Signed = Type >= IntType.S8;
 
@@ -253,9 +253,7 @@ namespace Ryujinx.Graphics.Gal.Shader
 
             if (Size < 32)
             {
-                uint Mask = uint.MaxValue >> (32 - Size);
-
-                OperA = new ShaderIrOp(ShaderIrInst.And, OperA, new ShaderIrOperImm((int)Mask));
+                OperA = ExtendTo32(OperA, Signed, Size);
             }
 
             ShaderIrInst Inst = Signed
@@ -296,7 +294,7 @@ namespace Ryujinx.Graphics.Gal.Shader
                 default: throw new ArgumentException(nameof(Oper));
             }
 
-            OperA = GetAluAbsNeg(OperA, AbsA, NegA);
+            OperA = GetAluIabsIneg(OperA, AbsA, NegA);
 
             bool Signed = Type >= IntType.S8;
 
@@ -335,7 +333,7 @@ namespace Ryujinx.Graphics.Gal.Shader
                 }
                 else
                 {
-                    OperA = new ShaderIrOp(ShaderIrInst.And, OperA, new ShaderIrOperImm((int)Mask));
+                    OperA = ExtendTo32(OperA, Signed, Size);
                 }
             }
 
diff --git a/Ryujinx.Graphics/Gal/Shader/ShaderDecoder.cs b/Ryujinx.Graphics/Gal/Shader/ShaderDecoder.cs
index e44d5b7d7..4958dfcf4 100644
--- a/Ryujinx.Graphics/Gal/Shader/ShaderDecoder.cs
+++ b/Ryujinx.Graphics/Gal/Shader/ShaderDecoder.cs
@@ -2,14 +2,21 @@ namespace Ryujinx.Graphics.Gal.Shader
 {
     static class ShaderDecoder
     {
+        private const bool AddDbgComments = true;
+
         public static ShaderIrBlock DecodeBasicBlock(int[] Code, int Offset)
         {
             ShaderIrBlock Block = new ShaderIrBlock();
 
             while (Offset + 2 <= Code.Length)
             {
-                //Ignore scheduling instructions, which are
-                //written every 32 bytes.
+                int InstPos = Offset * 4;
+
+                Block.Position = InstPos;
+
+                Block.MarkLabel(InstPos);
+
+                //Ignore scheduling instructions, which are written every 32 bytes.
                 if ((Offset & 7) == 0)
                 {
                     Offset += 2;
@@ -24,6 +31,13 @@ namespace Ryujinx.Graphics.Gal.Shader
 
                 ShaderDecodeFunc Decode = ShaderOpCodeTable.GetDecoder(OpCode);
 
+                if (AddDbgComments)
+                {
+                    string DbgOpCode = $"0x{InstPos:x8}: 0x{OpCode:x16} ";
+
+                    Block.AddNode(new ShaderIrCmnt(DbgOpCode + (Decode?.Method.Name ?? "???")));
+                }
+
                 if (Decode == null)
                 {
                     continue;
@@ -31,7 +45,7 @@ namespace Ryujinx.Graphics.Gal.Shader
 
                 Decode(Block, OpCode);
 
-                if (Block.GetLastNode() is ShaderIrOp Op && IsFlowChange(Op.Inst))
+                if (Block.GetLastNode() is ShaderIrOp Op && Op.Inst == ShaderIrInst.Exit)
                 {
                     break;
                 }
diff --git a/Ryujinx.Graphics/Gal/Shader/ShaderIrBlock.cs b/Ryujinx.Graphics/Gal/Shader/ShaderIrBlock.cs
index c920d9fa5..5f365d8c8 100644
--- a/Ryujinx.Graphics/Gal/Shader/ShaderIrBlock.cs
+++ b/Ryujinx.Graphics/Gal/Shader/ShaderIrBlock.cs
@@ -6,9 +6,15 @@ namespace Ryujinx.Graphics.Gal.Shader
     {
         private List<ShaderIrNode> Nodes;
 
+        private Dictionary<int, ShaderIrLabel> LabelsToInsert;
+
+        public int Position;
+
         public ShaderIrBlock()
         {
             Nodes = new List<ShaderIrNode>();
+
+            LabelsToInsert = new Dictionary<int, ShaderIrLabel>();
         }
 
         public void AddNode(ShaderIrNode Node)
@@ -16,6 +22,28 @@ namespace Ryujinx.Graphics.Gal.Shader
             Nodes.Add(Node);
         }
 
+        public ShaderIrLabel GetLabel(int Position)
+        {
+            if (LabelsToInsert.TryGetValue(Position, out ShaderIrLabel Label))
+            {
+                return Label;
+            }
+
+            Label = new ShaderIrLabel();
+
+            LabelsToInsert.Add(Position, Label);
+
+            return Label;
+        }
+
+        public void MarkLabel(int Position)
+        {
+            if (LabelsToInsert.TryGetValue(Position, out ShaderIrLabel Label))
+            {
+                Nodes.Add(Label);
+            }
+        }
+
         public ShaderIrNode[] GetNodes()
         {
             return Nodes.ToArray();
diff --git a/Ryujinx.Graphics/Gal/Shader/ShaderIrCmnt.cs b/Ryujinx.Graphics/Gal/Shader/ShaderIrCmnt.cs
new file mode 100644
index 000000000..03031ec5b
--- /dev/null
+++ b/Ryujinx.Graphics/Gal/Shader/ShaderIrCmnt.cs
@@ -0,0 +1,12 @@
+namespace Ryujinx.Graphics.Gal.Shader
+{
+    class ShaderIrCmnt : ShaderIrNode
+    {
+        public string Comment { get; private set; }
+
+        public ShaderIrCmnt(string Comment)
+        {
+            this.Comment = Comment;
+        }
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx.Graphics/Gal/Shader/ShaderIrInst.cs b/Ryujinx.Graphics/Gal/Shader/ShaderIrInst.cs
index ce2b98fe9..af2ccc3b3 100644
--- a/Ryujinx.Graphics/Gal/Shader/ShaderIrInst.cs
+++ b/Ryujinx.Graphics/Gal/Shader/ShaderIrInst.cs
@@ -36,6 +36,8 @@ namespace Ryujinx.Graphics.Gal.Shader
         Ffma,
         Flg2,
         Floor,
+        Fmax,
+        Fmin,
         Fmul,
         Fneg,
         Frcp,
@@ -49,6 +51,8 @@ namespace Ryujinx.Graphics.Gal.Shader
         F_End,
 
         I_Start,
+        Abs,
+        Add,
         And,
         Asr,
         Ceq,
@@ -59,16 +63,21 @@ namespace Ryujinx.Graphics.Gal.Shader
         Cle,
         Clt,
         Cne,
+        Lsl,
         Lsr,
+        Mul,
+        Neg,
         Not,
         Or,
         Stof,
+        Sub,
         Texq,
         Txlf,
         Utof,
         Xor,
         I_End,
 
+        Bra,
         Exit,
         Kil
     }
diff --git a/Ryujinx.Graphics/Gal/Shader/ShaderIrLabel.cs b/Ryujinx.Graphics/Gal/Shader/ShaderIrLabel.cs
new file mode 100644
index 000000000..f5b3585af
--- /dev/null
+++ b/Ryujinx.Graphics/Gal/Shader/ShaderIrLabel.cs
@@ -0,0 +1,4 @@
+namespace Ryujinx.Graphics.Gal.Shader
+{
+    class ShaderIrLabel : ShaderIrNode { }
+}
\ No newline at end of file
diff --git a/Ryujinx.Graphics/Gal/Shader/ShaderIrOperCbuf.cs b/Ryujinx.Graphics/Gal/Shader/ShaderIrOperCbuf.cs
index f22720563..b040c5c63 100644
--- a/Ryujinx.Graphics/Gal/Shader/ShaderIrOperCbuf.cs
+++ b/Ryujinx.Graphics/Gal/Shader/ShaderIrOperCbuf.cs
@@ -3,11 +3,14 @@ namespace Ryujinx.Graphics.Gal.Shader
     class ShaderIrOperCbuf : ShaderIrNode
     {
         public int Index { get; private set; }
-        public int Offs  { get; private set; }
+        public int Pos   { get; set; }
 
-        public ShaderIrOperCbuf(int Index, int Offs)
+        public ShaderIrNode Offs { get; private set; }
+
+        public ShaderIrOperCbuf(int Index, int Pos, ShaderIrNode Offs = null)
         {
             this.Index = Index;
+            this.Pos   = Pos;
             this.Offs  = Offs;
         }
     }
diff --git a/Ryujinx.Graphics/Gal/Shader/ShaderOpCodeTable.cs b/Ryujinx.Graphics/Gal/Shader/ShaderOpCodeTable.cs
index 65e249286..acfcc147c 100644
--- a/Ryujinx.Graphics/Gal/Shader/ShaderOpCodeTable.cs
+++ b/Ryujinx.Graphics/Gal/Shader/ShaderOpCodeTable.cs
@@ -26,6 +26,10 @@ namespace Ryujinx.Graphics.Gal.Shader
             OpCodes = new ShaderDecodeEntry[1 << EncodingBits];
 
 #region Instructions
+            Set("0100110000000x", ShaderDecode.Bfe_C);
+            Set("0011100x00000x", ShaderDecode.Bfe_I);
+            Set("0101110000000x", ShaderDecode.Bfe_R);
+            Set("111000100100xx", ShaderDecode.Bra);
             Set("111000110000xx", ShaderDecode.Exit);
             Set("0100110010101x", ShaderDecode.F2f_C);
             Set("0011100x10101x", ShaderDecode.F2f_I);
@@ -40,10 +44,13 @@ 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("00011110xxxxxx", ShaderDecode.Fmul_I32);
             Set("0100110001101x", ShaderDecode.Fmul_C);
             Set("0011100x01101x", ShaderDecode.Fmul_I);
             Set("0101110001101x", ShaderDecode.Fmul_R);
+            Set("0100110001100x", ShaderDecode.Fmnmx_C);
+            Set("0011100x01100x", ShaderDecode.Fmnmx_I);
+            Set("0101110001100x", ShaderDecode.Fmnmx_R);
             Set("0100100xxxxxxx", ShaderDecode.Fset_C);
             Set("0011000xxxxxxx", ShaderDecode.Fset_I);
             Set("01011000xxxxxx", ShaderDecode.Fset_R);
@@ -57,17 +64,24 @@ namespace Ryujinx.Graphics.Gal.Shader
             Set("0011100x11100x", ShaderDecode.I2i_I);
             Set("0101110011100x", ShaderDecode.I2i_R);
             Set("11100000xxxxxx", ShaderDecode.Ipa);
+            Set("0100110000011x", ShaderDecode.Iscadd_C);
+            Set("0011100x00011x", ShaderDecode.Iscadd_I);
+            Set("0101110000011x", ShaderDecode.Iscadd_R);
             Set("010010110110xx", ShaderDecode.Isetp_C);
             Set("0011011x0110xx", ShaderDecode.Isetp_I);
             Set("010110110110xx", ShaderDecode.Isetp_R);
             Set("111000110011xx", ShaderDecode.Kil);
             Set("1110111111011x", ShaderDecode.Ld_A);
-            Set("000001xxxxxxxx", ShaderDecode.Lop32i);
+            Set("1110111110010x", ShaderDecode.Ld_C);
+            Set("000001xxxxxxxx", ShaderDecode.Lop_I32);
             Set("0100110010011x", ShaderDecode.Mov_C);
             Set("0011100x10011x", ShaderDecode.Mov_I);
+            Set("000000010000xx", ShaderDecode.Mov_I32);
             Set("0101110010011x", ShaderDecode.Mov_R);
-            Set("000000010000xx", ShaderDecode.Mov32i);
             Set("0101000010000x", ShaderDecode.Mufu);
+            Set("0100110001001x", ShaderDecode.Shl_C);
+            Set("0011100x01001x", ShaderDecode.Shl_I);
+            Set("0101110001001x", ShaderDecode.Shl_R);
             Set("0100110000101x", ShaderDecode.Shr_C);
             Set("0011100x00101x", ShaderDecode.Shr_I);
             Set("0101110000101x", ShaderDecode.Shr_R);
diff --git a/Ryujinx.Graphics/Gal/ShaderDeclInfo.cs b/Ryujinx.Graphics/Gal/ShaderDeclInfo.cs
index d400850c8..e5ecee950 100644
--- a/Ryujinx.Graphics/Gal/ShaderDeclInfo.cs
+++ b/Ryujinx.Graphics/Gal/ShaderDeclInfo.cs
@@ -23,5 +23,13 @@ namespace Ryujinx.Graphics.Gal
                 Size = NewSize;
             }
         }
+
+        internal void SetCbufOffs(int Offs)
+        {
+            if (Index < Offs)
+            {
+                Index = Offs;
+            }
+        }
     }
 }
\ No newline at end of file
diff --git a/Ryujinx.sln b/Ryujinx.sln
index 036421f90..213f73866 100644
--- a/Ryujinx.sln
+++ b/Ryujinx.sln
@@ -15,6 +15,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Graphics", "Ryujinx
 EndProject
 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Audio", "Ryujinx.Audio\Ryujinx.Audio.csproj", "{5C1D818E-682A-46A5-9D54-30006E26C270}"
 EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ryushader", "Ryushader\Ryushader.csproj", "{3AB294D0-2230-468F-9EB3-BDFCAEAE99A5}"
+EndProject
 Global
 	GlobalSection(SolutionConfigurationPlatforms) = preSolution
 		Debug|Any CPU = Debug|Any CPU
@@ -45,6 +47,10 @@ Global
 		{5C1D818E-682A-46A5-9D54-30006E26C270}.Debug|Any CPU.Build.0 = Debug|Any CPU
 		{5C1D818E-682A-46A5-9D54-30006E26C270}.Release|Any CPU.ActiveCfg = Release|Any CPU
 		{5C1D818E-682A-46A5-9D54-30006E26C270}.Release|Any CPU.Build.0 = Release|Any CPU
+		{3AB294D0-2230-468F-9EB3-BDFCAEAE99A5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{3AB294D0-2230-468F-9EB3-BDFCAEAE99A5}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{3AB294D0-2230-468F-9EB3-BDFCAEAE99A5}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{3AB294D0-2230-468F-9EB3-BDFCAEAE99A5}.Release|Any CPU.Build.0 = Release|Any CPU
 	EndGlobalSection
 	GlobalSection(SolutionProperties) = preSolution
 		HideSolutionNode = FALSE
diff --git a/Ryushader/Program.cs b/Ryushader/Program.cs
new file mode 100644
index 000000000..21eb3d79b
--- /dev/null
+++ b/Ryushader/Program.cs
@@ -0,0 +1,48 @@
+using Ryujinx.Graphics.Gal;
+using Ryujinx.Graphics.Gal.Shader;
+using System;
+using System.IO;
+
+namespace Ryushader
+{
+    class Program
+    {
+        static void Main(string[] args)
+        {
+            if (args.Length == 2)
+            {
+                GlslDecompiler Decompiler = new GlslDecompiler();
+
+                GalShaderType ShaderType = GalShaderType.Vertex;
+
+                switch (args[0].ToLower())
+                {
+                    case "v":  ShaderType = GalShaderType.Vertex;         break;
+                    case "tc": ShaderType = GalShaderType.TessControl;    break;
+                    case "te": ShaderType = GalShaderType.TessEvaluation; break;
+                    case "g":  ShaderType = GalShaderType.Geometry;       break;
+                    case "f":  ShaderType = GalShaderType.Fragment;       break;
+                }
+
+                byte[] Data = File.ReadAllBytes(args[1]);
+
+                int[] Code = new int[Data.Length / 4];
+
+                for (int Offset = 0; Offset < Data.Length; Offset += 4)
+                {
+                    int Value = BitConverter.ToInt32(Data, Offset);
+
+                    Code[Offset >> 2] = Value;
+                }
+
+                GlslProgram Program = Decompiler.Decompile(Code, ShaderType);
+
+                Console.WriteLine(Program.Code);
+            }
+            else
+            {
+                Console.WriteLine("Usage: Ryushader [v|tc|te|g|f] shader.bin");
+            }
+        }
+    }
+}
diff --git a/Ryushader/Ryushader.csproj b/Ryushader/Ryushader.csproj
new file mode 100644
index 000000000..9eeee2ba9
--- /dev/null
+++ b/Ryushader/Ryushader.csproj
@@ -0,0 +1,12 @@
+<Project Sdk="Microsoft.NET.Sdk">
+
+  <ItemGroup>
+    <ProjectReference Include="..\Ryujinx.Graphics\Ryujinx.Graphics.csproj" />
+  </ItemGroup>
+
+  <PropertyGroup>
+    <OutputType>Exe</OutputType>
+    <TargetFramework>netcoreapp2.1</TargetFramework>
+  </PropertyGroup>
+
+</Project>