aboutsummaryrefslogtreecommitdiff
path: root/Ryujinx.Graphics.Shader/Decoders
diff options
context:
space:
mode:
Diffstat (limited to 'Ryujinx.Graphics.Shader/Decoders')
-rw-r--r--Ryujinx.Graphics.Shader/Decoders/BitfieldExtensions.cs25
-rw-r--r--Ryujinx.Graphics.Shader/Decoders/Block.cs117
-rw-r--r--Ryujinx.Graphics.Shader/Decoders/Condition.cs45
-rw-r--r--Ryujinx.Graphics.Shader/Decoders/ConditionalOperation.cs10
-rw-r--r--Ryujinx.Graphics.Shader/Decoders/Decoder.cs407
-rw-r--r--Ryujinx.Graphics.Shader/Decoders/DecoderHelper.cs58
-rw-r--r--Ryujinx.Graphics.Shader/Decoders/FPHalfSwizzle.cs10
-rw-r--r--Ryujinx.Graphics.Shader/Decoders/FPType.cs9
-rw-r--r--Ryujinx.Graphics.Shader/Decoders/FmulScale.cs13
-rw-r--r--Ryujinx.Graphics.Shader/Decoders/IOpCode.cs16
-rw-r--r--Ryujinx.Graphics.Shader/Decoders/IOpCodeAlu.cs12
-rw-r--r--Ryujinx.Graphics.Shader/Decoders/IOpCodeCbuf.cs8
-rw-r--r--Ryujinx.Graphics.Shader/Decoders/IOpCodeFArith.cs12
-rw-r--r--Ryujinx.Graphics.Shader/Decoders/IOpCodeHfma.cs13
-rw-r--r--Ryujinx.Graphics.Shader/Decoders/IOpCodeImm.cs7
-rw-r--r--Ryujinx.Graphics.Shader/Decoders/IOpCodeImmF.cs7
-rw-r--r--Ryujinx.Graphics.Shader/Decoders/IOpCodeLop.cs10
-rw-r--r--Ryujinx.Graphics.Shader/Decoders/IOpCodeRa.cs7
-rw-r--r--Ryujinx.Graphics.Shader/Decoders/IOpCodeRc.cs7
-rw-r--r--Ryujinx.Graphics.Shader/Decoders/IOpCodeRd.cs7
-rw-r--r--Ryujinx.Graphics.Shader/Decoders/IOpCodeReg.cs7
-rw-r--r--Ryujinx.Graphics.Shader/Decoders/IOpCodeRegCbuf.cs8
-rw-r--r--Ryujinx.Graphics.Shader/Decoders/IntegerCondition.cs18
-rw-r--r--Ryujinx.Graphics.Shader/Decoders/IntegerHalfPart.cs9
-rw-r--r--Ryujinx.Graphics.Shader/Decoders/IntegerShift.cs9
-rw-r--r--Ryujinx.Graphics.Shader/Decoders/IntegerSize.cs13
-rw-r--r--Ryujinx.Graphics.Shader/Decoders/IntegerType.cs14
-rw-r--r--Ryujinx.Graphics.Shader/Decoders/InterpolationMode.cs10
-rw-r--r--Ryujinx.Graphics.Shader/Decoders/LogicalOperation.cs10
-rw-r--r--Ryujinx.Graphics.Shader/Decoders/MufuOperation.cs15
-rw-r--r--Ryujinx.Graphics.Shader/Decoders/OpCode.cs30
-rw-r--r--Ryujinx.Graphics.Shader/Decoders/OpCodeAlu.cs34
-rw-r--r--Ryujinx.Graphics.Shader/Decoders/OpCodeAluCbuf.cs16
-rw-r--r--Ryujinx.Graphics.Shader/Decoders/OpCodeAluImm.cs14
-rw-r--r--Ryujinx.Graphics.Shader/Decoders/OpCodeAluImm2x10.cs14
-rw-r--r--Ryujinx.Graphics.Shader/Decoders/OpCodeAluImm32.cs18
-rw-r--r--Ryujinx.Graphics.Shader/Decoders/OpCodeAluReg.cs14
-rw-r--r--Ryujinx.Graphics.Shader/Decoders/OpCodeAluRegCbuf.cs18
-rw-r--r--Ryujinx.Graphics.Shader/Decoders/OpCodeAttribute.cs16
-rw-r--r--Ryujinx.Graphics.Shader/Decoders/OpCodeBranch.cs19
-rw-r--r--Ryujinx.Graphics.Shader/Decoders/OpCodeExit.cs14
-rw-r--r--Ryujinx.Graphics.Shader/Decoders/OpCodeFArith.cs24
-rw-r--r--Ryujinx.Graphics.Shader/Decoders/OpCodeFArithCbuf.cs16
-rw-r--r--Ryujinx.Graphics.Shader/Decoders/OpCodeFArithImm.cs14
-rw-r--r--Ryujinx.Graphics.Shader/Decoders/OpCodeFArithImm32.cs30
-rw-r--r--Ryujinx.Graphics.Shader/Decoders/OpCodeFArithReg.cs14
-rw-r--r--Ryujinx.Graphics.Shader/Decoders/OpCodeFArithRegCbuf.cs16
-rw-r--r--Ryujinx.Graphics.Shader/Decoders/OpCodeFsetImm.cs14
-rw-r--r--Ryujinx.Graphics.Shader/Decoders/OpCodeHfma.cs22
-rw-r--r--Ryujinx.Graphics.Shader/Decoders/OpCodeHfmaCbuf.cs30
-rw-r--r--Ryujinx.Graphics.Shader/Decoders/OpCodeHfmaImm2x10.cs26
-rw-r--r--Ryujinx.Graphics.Shader/Decoders/OpCodeHfmaImm32.cs25
-rw-r--r--Ryujinx.Graphics.Shader/Decoders/OpCodeHfmaReg.cs29
-rw-r--r--Ryujinx.Graphics.Shader/Decoders/OpCodeHfmaRegCbuf.cs30
-rw-r--r--Ryujinx.Graphics.Shader/Decoders/OpCodeHsetImm2x10.cs14
-rw-r--r--Ryujinx.Graphics.Shader/Decoders/OpCodeIpa.cs20
-rw-r--r--Ryujinx.Graphics.Shader/Decoders/OpCodeLdc.cs26
-rw-r--r--Ryujinx.Graphics.Shader/Decoders/OpCodeLop.cs28
-rw-r--r--Ryujinx.Graphics.Shader/Decoders/OpCodeLopCbuf.cs16
-rw-r--r--Ryujinx.Graphics.Shader/Decoders/OpCodeLopImm.cs14
-rw-r--r--Ryujinx.Graphics.Shader/Decoders/OpCodeLopImm32.cs22
-rw-r--r--Ryujinx.Graphics.Shader/Decoders/OpCodeLopReg.cs14
-rw-r--r--Ryujinx.Graphics.Shader/Decoders/OpCodeMemory.cs28
-rw-r--r--Ryujinx.Graphics.Shader/Decoders/OpCodePsetp.cs20
-rw-r--r--Ryujinx.Graphics.Shader/Decoders/OpCodeSet.cs26
-rw-r--r--Ryujinx.Graphics.Shader/Decoders/OpCodeSetCbuf.cs16
-rw-r--r--Ryujinx.Graphics.Shader/Decoders/OpCodeSetImm.cs14
-rw-r--r--Ryujinx.Graphics.Shader/Decoders/OpCodeSetReg.cs14
-rw-r--r--Ryujinx.Graphics.Shader/Decoders/OpCodeSsy.cs20
-rw-r--r--Ryujinx.Graphics.Shader/Decoders/OpCodeSync.cs15
-rw-r--r--Ryujinx.Graphics.Shader/Decoders/OpCodeTable.cs227
-rw-r--r--Ryujinx.Graphics.Shader/Decoders/OpCodeTex.cs14
-rw-r--r--Ryujinx.Graphics.Shader/Decoders/OpCodeTexB.cs20
-rw-r--r--Ryujinx.Graphics.Shader/Decoders/OpCodeTexs.cs11
-rw-r--r--Ryujinx.Graphics.Shader/Decoders/OpCodeTexture.cs42
-rw-r--r--Ryujinx.Graphics.Shader/Decoders/OpCodeTextureScalar.cs62
-rw-r--r--Ryujinx.Graphics.Shader/Decoders/OpCodeTld.cs20
-rw-r--r--Ryujinx.Graphics.Shader/Decoders/OpCodeTld4.cs20
-rw-r--r--Ryujinx.Graphics.Shader/Decoders/OpCodeTld4s.cs22
-rw-r--r--Ryujinx.Graphics.Shader/Decoders/OpCodeTlds.cs11
-rw-r--r--Ryujinx.Graphics.Shader/Decoders/OpCodeVideo.cs24
-rw-r--r--Ryujinx.Graphics.Shader/Decoders/Register.cs36
-rw-r--r--Ryujinx.Graphics.Shader/Decoders/RegisterConsts.cs13
-rw-r--r--Ryujinx.Graphics.Shader/Decoders/RegisterType.cs9
-rw-r--r--Ryujinx.Graphics.Shader/Decoders/RoundingMode.cs10
-rw-r--r--Ryujinx.Graphics.Shader/Decoders/SystemRegister.cs12
-rw-r--r--Ryujinx.Graphics.Shader/Decoders/TexelLoadTarget.cs15
-rw-r--r--Ryujinx.Graphics.Shader/Decoders/TextureDimensions.cs10
-rw-r--r--Ryujinx.Graphics.Shader/Decoders/TextureGatherOffset.cs9
-rw-r--r--Ryujinx.Graphics.Shader/Decoders/TextureLodMode.cs12
-rw-r--r--Ryujinx.Graphics.Shader/Decoders/TextureProperty.cs13
-rw-r--r--Ryujinx.Graphics.Shader/Decoders/TextureTarget.cs20
-rw-r--r--Ryujinx.Graphics.Shader/Decoders/XmadCMode.cs11
93 files changed, 2360 insertions, 0 deletions
diff --git a/Ryujinx.Graphics.Shader/Decoders/BitfieldExtensions.cs b/Ryujinx.Graphics.Shader/Decoders/BitfieldExtensions.cs
new file mode 100644
index 00000000..3bb9bc1f
--- /dev/null
+++ b/Ryujinx.Graphics.Shader/Decoders/BitfieldExtensions.cs
@@ -0,0 +1,25 @@
+namespace Ryujinx.Graphics.Shader.Decoders
+{
+ static class BitfieldExtensions
+ {
+ public static bool Extract(this int value, int lsb)
+ {
+ return ((int)(value >> lsb) & 1) != 0;
+ }
+
+ public static int Extract(this int value, int lsb, int length)
+ {
+ return (int)(value >> lsb) & (int)(uint.MaxValue >> (32 - length));
+ }
+
+ public static bool Extract(this long value, int lsb)
+ {
+ return ((int)(value >> lsb) & 1) != 0;
+ }
+
+ public static int Extract(this long value, int lsb, int length)
+ {
+ return (int)(value >> lsb) & (int)(uint.MaxValue >> (32 - length));
+ }
+ }
+} \ No newline at end of file
diff --git a/Ryujinx.Graphics.Shader/Decoders/Block.cs b/Ryujinx.Graphics.Shader/Decoders/Block.cs
new file mode 100644
index 00000000..b5e610d7
--- /dev/null
+++ b/Ryujinx.Graphics.Shader/Decoders/Block.cs
@@ -0,0 +1,117 @@
+using System;
+using System.Collections.Generic;
+
+namespace Ryujinx.Graphics.Shader.Decoders
+{
+ class Block
+ {
+ public ulong Address { get; set; }
+ public ulong EndAddress { get; set; }
+
+ public Block Next { get; set; }
+ public Block Branch { get; set; }
+
+ public List<OpCode> OpCodes { get; }
+ public List<OpCodeSsy> SsyOpCodes { get; }
+
+ public Block(ulong address)
+ {
+ Address = address;
+
+ OpCodes = new List<OpCode>();
+ SsyOpCodes = new List<OpCodeSsy>();
+ }
+
+ public void Split(Block rightBlock)
+ {
+ int splitIndex = BinarySearch(OpCodes, rightBlock.Address);
+
+ if (OpCodes[splitIndex].Address < rightBlock.Address)
+ {
+ splitIndex++;
+ }
+
+ int splitCount = OpCodes.Count - splitIndex;
+
+ if (splitCount <= 0)
+ {
+ throw new ArgumentException("Can't split at right block address.");
+ }
+
+ rightBlock.EndAddress = EndAddress;
+
+ rightBlock.Next = Next;
+ rightBlock.Branch = Branch;
+
+ rightBlock.OpCodes.AddRange(OpCodes.GetRange(splitIndex, splitCount));
+
+ rightBlock.UpdateSsyOpCodes();
+
+ EndAddress = rightBlock.Address;
+
+ Next = rightBlock;
+ Branch = null;
+
+ OpCodes.RemoveRange(splitIndex, splitCount);
+
+ UpdateSsyOpCodes();
+ }
+
+ private static int BinarySearch(List<OpCode> opCodes, ulong address)
+ {
+ int left = 0;
+ int middle = 0;
+ int right = opCodes.Count - 1;
+
+ while (left <= right)
+ {
+ int size = right - left;
+
+ middle = left + (size >> 1);
+
+ OpCode opCode = opCodes[middle];
+
+ if (address == opCode.Address)
+ {
+ break;
+ }
+
+ if (address < opCode.Address)
+ {
+ right = middle - 1;
+ }
+ else
+ {
+ left = middle + 1;
+ }
+ }
+
+ return middle;
+ }
+
+ public OpCode GetLastOp()
+ {
+ if (OpCodes.Count != 0)
+ {
+ return OpCodes[OpCodes.Count - 1];
+ }
+
+ return null;
+ }
+
+ public void UpdateSsyOpCodes()
+ {
+ SsyOpCodes.Clear();
+
+ for (int index = 0; index < OpCodes.Count; index++)
+ {
+ if (!(OpCodes[index] is OpCodeSsy op))
+ {
+ continue;
+ }
+
+ SsyOpCodes.Add(op);
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/Ryujinx.Graphics.Shader/Decoders/Condition.cs b/Ryujinx.Graphics.Shader/Decoders/Condition.cs
new file mode 100644
index 00000000..10400f94
--- /dev/null
+++ b/Ryujinx.Graphics.Shader/Decoders/Condition.cs
@@ -0,0 +1,45 @@
+namespace Ryujinx.Graphics.Shader.Decoders
+{
+ enum Condition
+ {
+ Less = 1 << 0,
+ Equal = 1 << 1,
+ Greater = 1 << 2,
+ Nan = 1 << 3,
+ Unsigned = 1 << 4,
+
+ Never = 0,
+
+ LessOrEqual = Less | Equal,
+ NotEqual = Less | Greater,
+ GreaterOrEqual = Greater | Equal,
+ Number = Greater | Equal | Less,
+
+ LessUnordered = Less | Nan,
+ EqualUnordered = Equal | Nan,
+ LessOrEqualUnordered = LessOrEqual | Nan,
+ GreaterUnordered = Greater | Nan,
+ NotEqualUnordered = NotEqual | Nan,
+ GreaterOrEqualUnordered = GreaterOrEqual | Nan,
+
+ Always = 0xf,
+
+ Off = Unsigned | Never,
+ Lower = Unsigned | Less,
+ Sff = Unsigned | Equal,
+ LowerOrSame = Unsigned | LessOrEqual,
+ Higher = Unsigned | Greater,
+ Sft = Unsigned | NotEqual,
+ HigherOrSame = Unsigned | GreaterOrEqual,
+ Oft = Unsigned | Always,
+
+ CsmTa = 0x18,
+ CsmTr = 0x19,
+ CsmMx = 0x1a,
+ FcsmTa = 0x1b,
+ FcsmTr = 0x1c,
+ FcsmMx = 0x1d,
+ Rle = 0x1e,
+ Rgt = 0x1f
+ }
+} \ No newline at end of file
diff --git a/Ryujinx.Graphics.Shader/Decoders/ConditionalOperation.cs b/Ryujinx.Graphics.Shader/Decoders/ConditionalOperation.cs
new file mode 100644
index 00000000..4fc31e84
--- /dev/null
+++ b/Ryujinx.Graphics.Shader/Decoders/ConditionalOperation.cs
@@ -0,0 +1,10 @@
+namespace Ryujinx.Graphics.Shader.Decoders
+{
+ enum ConditionalOperation
+ {
+ False = 0,
+ True = 1,
+ Zero = 2,
+ NotZero = 3
+ }
+} \ No newline at end of file
diff --git a/Ryujinx.Graphics.Shader/Decoders/Decoder.cs b/Ryujinx.Graphics.Shader/Decoders/Decoder.cs
new file mode 100644
index 00000000..dd5347d9
--- /dev/null
+++ b/Ryujinx.Graphics.Shader/Decoders/Decoder.cs
@@ -0,0 +1,407 @@
+using Ryujinx.Graphics.Shader.Instructions;
+using System;
+using System.Buffers.Binary;
+using System.Collections.Concurrent;
+using System.Collections.Generic;
+using System.Linq;
+using System.Reflection.Emit;
+
+using static Ryujinx.Graphics.Shader.IntermediateRepresentation.OperandHelper;
+
+namespace Ryujinx.Graphics.Shader.Decoders
+{
+ static class Decoder
+ {
+ private delegate object OpActivator(InstEmitter emitter, ulong address, long opCode);
+
+ private static ConcurrentDictionary<Type, OpActivator> _opActivators;
+
+ static Decoder()
+ {
+ _opActivators = new ConcurrentDictionary<Type, OpActivator>();
+ }
+
+ public static Block[] Decode(Span<byte> code, ulong headerSize)
+ {
+ List<Block> blocks = new List<Block>();
+
+ Queue<Block> workQueue = new Queue<Block>();
+
+ Dictionary<ulong, Block> visited = new Dictionary<ulong, Block>();
+
+ Block GetBlock(ulong blkAddress)
+ {
+ if (!visited.TryGetValue(blkAddress, out Block block))
+ {
+ block = new Block(blkAddress);
+
+ workQueue.Enqueue(block);
+
+ visited.Add(blkAddress, block);
+ }
+
+ return block;
+ }
+
+ ulong startAddress = headerSize;
+
+ GetBlock(startAddress);
+
+ while (workQueue.TryDequeue(out Block currBlock))
+ {
+ // Check if the current block is inside another block.
+ if (BinarySearch(blocks, currBlock.Address, out int nBlkIndex))
+ {
+ Block nBlock = blocks[nBlkIndex];
+
+ if (nBlock.Address == currBlock.Address)
+ {
+ throw new InvalidOperationException("Found duplicate block address on the list.");
+ }
+
+ nBlock.Split(currBlock);
+
+ blocks.Insert(nBlkIndex + 1, currBlock);
+
+ continue;
+ }
+
+ // If we have a block after the current one, set the limit address.
+ ulong limitAddress = (ulong)code.Length;
+
+ if (nBlkIndex != blocks.Count)
+ {
+ Block nBlock = blocks[nBlkIndex];
+
+ int nextIndex = nBlkIndex + 1;
+
+ if (nBlock.Address < currBlock.Address && nextIndex < blocks.Count)
+ {
+ limitAddress = blocks[nextIndex].Address;
+ }
+ else if (nBlock.Address > currBlock.Address)
+ {
+ limitAddress = blocks[nBlkIndex].Address;
+ }
+ }
+
+ FillBlock(code, currBlock, limitAddress, startAddress);
+
+ if (currBlock.OpCodes.Count != 0)
+ {
+ foreach (OpCodeSsy ssyOp in currBlock.SsyOpCodes)
+ {
+ GetBlock(ssyOp.GetAbsoluteAddress());
+ }
+
+ // Set child blocks. "Branch" is the block the branch instruction
+ // points to (when taken), "Next" is the block at the next address,
+ // executed when the branch is not taken. For Unconditional Branches
+ // or end of program, Next is null.
+ OpCode lastOp = currBlock.GetLastOp();
+
+ if (lastOp is OpCodeBranch op)
+ {
+ currBlock.Branch = GetBlock(op.GetAbsoluteAddress());
+ }
+
+ if (!IsUnconditionalBranch(lastOp))
+ {
+ currBlock.Next = GetBlock(currBlock.EndAddress);
+ }
+ }
+
+ // Insert the new block on the list (sorted by address).
+ if (blocks.Count != 0)
+ {
+ Block nBlock = blocks[nBlkIndex];
+
+ blocks.Insert(nBlkIndex + (nBlock.Address < currBlock.Address ? 1 : 0), currBlock);
+ }
+ else
+ {
+ blocks.Add(currBlock);
+ }
+ }
+
+ foreach (Block ssyBlock in blocks.Where(x => x.SsyOpCodes.Count != 0))
+ {
+ for (int ssyIndex = 0; ssyIndex < ssyBlock.SsyOpCodes.Count; ssyIndex++)
+ {
+ PropagateSsy(visited, ssyBlock, ssyIndex);
+ }
+ }
+
+ return blocks.ToArray();
+ }
+
+ private static bool BinarySearch(List<Block> blocks, ulong address, out int index)
+ {
+ index = 0;
+
+ int left = 0;
+ int right = blocks.Count - 1;
+
+ while (left <= right)
+ {
+ int size = right - left;
+
+ int middle = left + (size >> 1);
+
+ Block block = blocks[middle];
+
+ index = middle;
+
+ if (address >= block.Address && address < block.EndAddress)
+ {
+ return true;
+ }
+
+ if (address < block.Address)
+ {
+ right = middle - 1;
+ }
+ else
+ {
+ left = middle + 1;
+ }
+ }
+
+ return false;
+ }
+
+ private static void FillBlock(
+ Span<byte> code,
+ Block block,
+ ulong limitAddress,
+ ulong startAddress)
+ {
+ ulong address = block.Address;
+
+ do
+ {
+ if (address >= limitAddress)
+ {
+ break;
+ }
+
+ // Ignore scheduling instructions, which are written every 32 bytes.
+ if (((address - startAddress) & 0x1f) == 0)
+ {
+ address += 8;
+
+ continue;
+ }
+
+ uint word0 = BinaryPrimitives.ReadUInt32LittleEndian(code.Slice((int)address));
+ uint word1 = BinaryPrimitives.ReadUInt32LittleEndian(code.Slice((int)address + 4));
+
+ ulong opAddress = address;
+
+ address += 8;
+
+ long opCode = word0 | (long)word1 << 32;
+
+ (InstEmitter emitter, Type opCodeType) = OpCodeTable.GetEmitter(opCode);
+
+ if (emitter == null)
+ {
+ // TODO: Warning, illegal encoding.
+
+ block.OpCodes.Add(new OpCode(null, opAddress, opCode));
+
+ continue;
+ }
+
+ OpCode op = MakeOpCode(opCodeType, emitter, opAddress, opCode);
+
+ block.OpCodes.Add(op);
+ }
+ while (!IsBranch(block.GetLastOp()));
+
+ block.EndAddress = address;
+
+ block.UpdateSsyOpCodes();
+ }
+
+ private static bool IsUnconditionalBranch(OpCode opCode)
+ {
+ return IsUnconditional(opCode) && IsBranch(opCode);
+ }
+
+ private static bool IsUnconditional(OpCode opCode)
+ {
+ if (opCode is OpCodeExit op && op.Condition != Condition.Always)
+ {
+ return false;
+ }
+
+ return opCode.Predicate.Index == RegisterConsts.PredicateTrueIndex && !opCode.InvertPredicate;
+ }
+
+ private static bool IsBranch(OpCode opCode)
+ {
+ return (opCode is OpCodeBranch && opCode.Emitter != InstEmit.Ssy) ||
+ opCode is OpCodeSync ||
+ opCode is OpCodeExit;
+ }
+
+ private static OpCode MakeOpCode(Type type, InstEmitter emitter, ulong address, long opCode)
+ {
+ if (type == null)
+ {
+ throw new ArgumentNullException(nameof(type));
+ }
+
+ OpActivator createInstance = _opActivators.GetOrAdd(type, CacheOpActivator);
+
+ return (OpCode)createInstance(emitter, address, opCode);
+ }
+
+ private static OpActivator CacheOpActivator(Type type)
+ {
+ Type[] argTypes = new Type[] { typeof(InstEmitter), typeof(ulong), typeof(long) };
+
+ DynamicMethod mthd = new DynamicMethod($"Make{type.Name}", type, argTypes);
+
+ ILGenerator generator = mthd.GetILGenerator();
+
+ generator.Emit(OpCodes.Ldarg_0);
+ generator.Emit(OpCodes.Ldarg_1);
+ generator.Emit(OpCodes.Ldarg_2);
+ generator.Emit(OpCodes.Newobj, type.GetConstructor(argTypes));
+ generator.Emit(OpCodes.Ret);
+
+ return (OpActivator)mthd.CreateDelegate(typeof(OpActivator));
+ }
+
+ private struct PathBlockState
+ {
+ public Block Block { get; }
+
+ private enum RestoreType
+ {
+ None,
+ PopSsy,
+ PushSync
+ }
+
+ private RestoreType _restoreType;
+
+ private ulong _restoreValue;
+
+ public bool ReturningFromVisit => _restoreType != RestoreType.None;
+
+ public PathBlockState(Block block)
+ {
+ Block = block;
+ _restoreType = RestoreType.None;
+ _restoreValue = 0;
+ }
+
+ public PathBlockState(int oldSsyStackSize)
+ {
+ Block = null;
+ _restoreType = RestoreType.PopSsy;
+ _restoreValue = (ulong)oldSsyStackSize;
+ }
+
+ public PathBlockState(ulong syncAddress)
+ {
+ Block = null;
+ _restoreType = RestoreType.PushSync;
+ _restoreValue = syncAddress;
+ }
+
+ public void RestoreStackState(Stack<ulong> ssyStack)
+ {
+ if (_restoreType == RestoreType.PushSync)
+ {
+ ssyStack.Push(_restoreValue);
+ }
+ else if (_restoreType == RestoreType.PopSsy)
+ {
+ while (ssyStack.Count > (uint)_restoreValue)
+ {
+ ssyStack.Pop();
+ }
+ }
+ }
+ }
+
+ private static void PropagateSsy(Dictionary<ulong, Block> blocks, Block ssyBlock, int ssyIndex)
+ {
+ OpCodeSsy ssyOp = ssyBlock.SsyOpCodes[ssyIndex];
+
+ Stack<PathBlockState> workQueue = new Stack<PathBlockState>();
+
+ HashSet<Block> visited = new HashSet<Block>();
+
+ Stack<ulong> ssyStack = new Stack<ulong>();
+
+ void Push(PathBlockState pbs)
+ {
+ if (pbs.Block == null || visited.Add(pbs.Block))
+ {
+ workQueue.Push(pbs);
+ }
+ }
+
+ Push(new PathBlockState(ssyBlock));
+
+ while (workQueue.TryPop(out PathBlockState pbs))
+ {
+ if (pbs.ReturningFromVisit)
+ {
+ pbs.RestoreStackState(ssyStack);
+
+ continue;
+ }
+
+ Block current = pbs.Block;
+
+ int ssyOpCodesCount = current.SsyOpCodes.Count;
+
+ if (ssyOpCodesCount != 0)
+ {
+ Push(new PathBlockState(ssyStack.Count));
+
+ for (int index = ssyIndex; index < ssyOpCodesCount; index++)
+ {
+ ssyStack.Push(current.SsyOpCodes[index].GetAbsoluteAddress());
+ }
+ }
+
+ ssyIndex = 0;
+
+ if (current.Next != null)
+ {
+ Push(new PathBlockState(current.Next));
+ }
+
+ if (current.Branch != null)
+ {
+ Push(new PathBlockState(current.Branch));
+ }
+ else if (current.GetLastOp() is OpCodeSync op)
+ {
+ ulong syncAddress = ssyStack.Pop();
+
+ if (ssyStack.Count == 0)
+ {
+ ssyStack.Push(syncAddress);
+
+ op.Targets.Add(ssyOp, op.Targets.Count);
+
+ ssyOp.Syncs.TryAdd(op, Local());
+ }
+ else
+ {
+ Push(new PathBlockState(syncAddress));
+ Push(new PathBlockState(blocks[syncAddress]));
+ }
+ }
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/Ryujinx.Graphics.Shader/Decoders/DecoderHelper.cs b/Ryujinx.Graphics.Shader/Decoders/DecoderHelper.cs
new file mode 100644
index 00000000..77cd1bf7
--- /dev/null
+++ b/Ryujinx.Graphics.Shader/Decoders/DecoderHelper.cs
@@ -0,0 +1,58 @@
+using System;
+
+namespace Ryujinx.Graphics.Shader.Decoders
+{
+ static class DecoderHelper
+ {
+ public static int DecodeS20Immediate(long opCode)
+ {
+ int imm = opCode.Extract(20, 19);
+
+ bool sign = opCode.Extract(56);
+
+ if (sign)
+ {
+ imm = (imm << 13) >> 13;
+ }
+
+ return imm;
+ }
+
+ public static int Decode2xF10Immediate(long opCode)
+ {
+ int immH0 = opCode.Extract(20, 9);
+ int immH1 = opCode.Extract(30, 9);
+
+ bool negateH0 = opCode.Extract(29);
+ bool negateH1 = opCode.Extract(56);
+
+ if (negateH0)
+ {
+ immH0 |= 1 << 9;
+ }
+
+ if (negateH1)
+ {
+ immH1 |= 1 << 9;
+ }
+
+ return immH1 << 22 | immH0 << 6;
+ }
+
+ public static float DecodeF20Immediate(long opCode)
+ {
+ int imm = opCode.Extract(20, 19);
+
+ bool negate = opCode.Extract(56);
+
+ imm <<= 12;
+
+ if (negate)
+ {
+ imm |= 1 << 31;
+ }
+
+ return BitConverter.Int32BitsToSingle(imm);
+ }
+ }
+} \ No newline at end of file
diff --git a/Ryujinx.Graphics.Shader/Decoders/FPHalfSwizzle.cs b/Ryujinx.Graphics.Shader/Decoders/FPHalfSwizzle.cs
new file mode 100644
index 00000000..3ddf17cf
--- /dev/null
+++ b/Ryujinx.Graphics.Shader/Decoders/FPHalfSwizzle.cs
@@ -0,0 +1,10 @@
+namespace Ryujinx.Graphics.Shader.Decoders
+{
+ enum FPHalfSwizzle
+ {
+ FP16 = 0,
+ FP32 = 1,
+ DupH0 = 2,
+ DupH1 = 3
+ }
+} \ No newline at end of file
diff --git a/Ryujinx.Graphics.Shader/Decoders/FPType.cs b/Ryujinx.Graphics.Shader/Decoders/FPType.cs
new file mode 100644
index 00000000..e602ad45
--- /dev/null
+++ b/Ryujinx.Graphics.Shader/Decoders/FPType.cs
@@ -0,0 +1,9 @@
+namespace Ryujinx.Graphics.Shader.Decoders
+{
+ enum FPType
+ {
+ FP16 = 1,
+ FP32 = 2,
+ FP64 = 3
+ }
+} \ No newline at end of file
diff --git a/Ryujinx.Graphics.Shader/Decoders/FmulScale.cs b/Ryujinx.Graphics.Shader/Decoders/FmulScale.cs
new file mode 100644
index 00000000..c35c6e48
--- /dev/null
+++ b/Ryujinx.Graphics.Shader/Decoders/FmulScale.cs
@@ -0,0 +1,13 @@
+namespace Ryujinx.Graphics.Shader.Decoders
+{
+ enum FmulScale
+ {
+ None = 0,
+ Divide2 = 1,
+ Divide4 = 2,
+ Divide8 = 3,
+ Multiply8 = 4,
+ Multiply4 = 5,
+ Multiply2 = 6
+ }
+} \ No newline at end of file
diff --git a/Ryujinx.Graphics.Shader/Decoders/IOpCode.cs b/Ryujinx.Graphics.Shader/Decoders/IOpCode.cs
new file mode 100644
index 00000000..dd6ad79a
--- /dev/null
+++ b/Ryujinx.Graphics.Shader/Decoders/IOpCode.cs
@@ -0,0 +1,16 @@
+using Ryujinx.Graphics.Shader.Instructions;
+
+namespace Ryujinx.Graphics.Shader.Decoders
+{
+ interface IOpCode
+ {
+ InstEmitter Emitter { get; }
+
+ ulong Address { get; }
+ long RawOpCode { get; }
+
+ Register Predicate { get; }
+
+ bool InvertPredicate { get; }
+ }
+} \ No newline at end of file
diff --git a/Ryujinx.Graphics.Shader/Decoders/IOpCodeAlu.cs b/Ryujinx.Graphics.Shader/Decoders/IOpCodeAlu.cs
new file mode 100644
index 00000000..d840d49d
--- /dev/null
+++ b/Ryujinx.Graphics.Shader/Decoders/IOpCodeAlu.cs
@@ -0,0 +1,12 @@
+namespace Ryujinx.Graphics.Shader.Decoders
+{
+ interface IOpCodeAlu : IOpCodeRd, IOpCodeRa
+ {
+ Register Predicate39 { get; }
+
+ bool InvertP { get; }
+ bool Extended { get; }
+ bool SetCondCode { get; }
+ bool Saturate { get; }
+ }
+} \ No newline at end of file
diff --git a/Ryujinx.Graphics.Shader/Decoders/IOpCodeCbuf.cs b/Ryujinx.Graphics.Shader/Decoders/IOpCodeCbuf.cs
new file mode 100644
index 00000000..42a17451
--- /dev/null
+++ b/Ryujinx.Graphics.Shader/Decoders/IOpCodeCbuf.cs
@@ -0,0 +1,8 @@
+namespace Ryujinx.Graphics.Shader.Decoders
+{
+ interface IOpCodeCbuf : IOpCode
+ {
+ int Offset { get; }
+ int Slot { get; }
+ }
+} \ No newline at end of file
diff --git a/Ryujinx.Graphics.Shader/Decoders/IOpCodeFArith.cs b/Ryujinx.Graphics.Shader/Decoders/IOpCodeFArith.cs
new file mode 100644
index 00000000..d68ccf59
--- /dev/null
+++ b/Ryujinx.Graphics.Shader/Decoders/IOpCodeFArith.cs
@@ -0,0 +1,12 @@
+namespace Ryujinx.Graphics.Shader.Decoders
+{
+ interface IOpCodeFArith : IOpCodeAlu
+ {
+ RoundingMode RoundingMode { get; }
+
+ FmulScale Scale { get; }
+
+ bool FlushToZero { get; }
+ bool AbsoluteA { get; }
+ }
+} \ No newline at end of file
diff --git a/Ryujinx.Graphics.Shader/Decoders/IOpCodeHfma.cs b/Ryujinx.Graphics.Shader/Decoders/IOpCodeHfma.cs
new file mode 100644
index 00000000..4638f660
--- /dev/null
+++ b/Ryujinx.Graphics.Shader/Decoders/IOpCodeHfma.cs
@@ -0,0 +1,13 @@
+namespace Ryujinx.Graphics.Shader.Decoders
+{
+ interface IOpCodeHfma : IOpCode
+ {
+ bool NegateB { get; }
+ bool NegateC { get; }
+ bool Saturate { get; }
+
+ FPHalfSwizzle SwizzleA { get; }
+ FPHalfSwizzle SwizzleB { get; }
+ FPHalfSwizzle SwizzleC { get; }
+ }
+} \ No newline at end of file
diff --git a/Ryujinx.Graphics.Shader/Decoders/IOpCodeImm.cs b/Ryujinx.Graphics.Shader/Decoders/IOpCodeImm.cs
new file mode 100644
index 00000000..9cfcd69b
--- /dev/null
+++ b/Ryujinx.Graphics.Shader/Decoders/IOpCodeImm.cs
@@ -0,0 +1,7 @@
+namespace Ryujinx.Graphics.Shader.Decoders
+{
+ interface IOpCodeImm : IOpCode
+ {
+ int Immediate { get; }
+ }
+} \ No newline at end of file
diff --git a/Ryujinx.Graphics.Shader/Decoders/IOpCodeImmF.cs b/Ryujinx.Graphics.Shader/Decoders/IOpCodeImmF.cs
new file mode 100644
index 00000000..629eff79
--- /dev/null
+++ b/Ryujinx.Graphics.Shader/Decoders/IOpCodeImmF.cs
@@ -0,0 +1,7 @@
+namespace Ryujinx.Graphics.Shader.Decoders
+{
+ interface IOpCodeImmF : IOpCode
+ {
+ float Immediate { get; }
+ }
+} \ No newline at end of file
diff --git a/Ryujinx.Graphics.Shader/Decoders/IOpCodeLop.cs b/Ryujinx.Graphics.Shader/Decoders/IOpCodeLop.cs
new file mode 100644
index 00000000..62c87bf4
--- /dev/null
+++ b/Ryujinx.Graphics.Shader/Decoders/IOpCodeLop.cs
@@ -0,0 +1,10 @@
+namespace Ryujinx.Graphics.Shader.Decoders
+{
+ interface IOpCodeLop : IOpCodeAlu
+ {
+ LogicalOperation LogicalOp { get; }
+
+ bool InvertA { get; }
+ bool InvertB { get; }
+ }
+} \ No newline at end of file
diff --git a/Ryujinx.Graphics.Shader/Decoders/IOpCodeRa.cs b/Ryujinx.Graphics.Shader/Decoders/IOpCodeRa.cs
new file mode 100644
index 00000000..e5902110
--- /dev/null
+++ b/Ryujinx.Graphics.Shader/Decoders/IOpCodeRa.cs
@@ -0,0 +1,7 @@
+namespace Ryujinx.Graphics.Shader.Decoders
+{
+ interface IOpCodeRa : IOpCode
+ {
+ Register Ra { get; }
+ }
+} \ No newline at end of file
diff --git a/Ryujinx.Graphics.Shader/Decoders/IOpCodeRc.cs b/Ryujinx.Graphics.Shader/Decoders/IOpCodeRc.cs
new file mode 100644
index 00000000..bb806b95
--- /dev/null
+++ b/Ryujinx.Graphics.Shader/Decoders/IOpCodeRc.cs
@@ -0,0 +1,7 @@
+namespace Ryujinx.Graphics.Shader.Decoders
+{
+ interface IOpCodeRc : IOpCode
+ {
+ Register Rc { get; }
+ }
+} \ No newline at end of file
diff --git a/Ryujinx.Graphics.Shader/Decoders/IOpCodeRd.cs b/Ryujinx.Graphics.Shader/Decoders/IOpCodeRd.cs
new file mode 100644
index 00000000..099c4061
--- /dev/null
+++ b/Ryujinx.Graphics.Shader/Decoders/IOpCodeRd.cs
@@ -0,0 +1,7 @@
+namespace Ryujinx.Graphics.Shader.Decoders
+{
+ interface IOpCodeRd : IOpCode
+ {
+ Register Rd { get; }
+ }
+} \ No newline at end of file
diff --git a/Ryujinx.Graphics.Shader/Decoders/IOpCodeReg.cs b/Ryujinx.Graphics.Shader/Decoders/IOpCodeReg.cs
new file mode 100644
index 00000000..3ed157e8
--- /dev/null
+++ b/Ryujinx.Graphics.Shader/Decoders/IOpCodeReg.cs
@@ -0,0 +1,7 @@
+namespace Ryujinx.Graphics.Shader.Decoders
+{
+ interface IOpCodeReg : IOpCode
+ {
+ Register Rb { get; }
+ }
+} \ No newline at end of file
diff --git a/Ryujinx.Graphics.Shader/Decoders/IOpCodeRegCbuf.cs b/Ryujinx.Graphics.Shader/Decoders/IOpCodeRegCbuf.cs
new file mode 100644
index 00000000..429f01bb
--- /dev/null
+++ b/Ryujinx.Graphics.Shader/Decoders/IOpCodeRegCbuf.cs
@@ -0,0 +1,8 @@
+namespace Ryujinx.Graphics.Shader.Decoders
+{
+ interface IOpCodeRegCbuf : IOpCodeRc
+ {
+ int Offset { get; }
+ int Slot { get; }
+ }
+} \ No newline at end of file
diff --git a/Ryujinx.Graphics.Shader/Decoders/IntegerCondition.cs b/Ryujinx.Graphics.Shader/Decoders/IntegerCondition.cs
new file mode 100644
index 00000000..a1937c2f
--- /dev/null
+++ b/Ryujinx.Graphics.Shader/Decoders/IntegerCondition.cs
@@ -0,0 +1,18 @@
+namespace Ryujinx.Graphics.Shader.Decoders
+{
+ enum IntegerCondition
+ {
+ Less = 1 << 0,
+ Equal = 1 << 1,
+ Greater = 1 << 2,
+
+ Never = 0,
+
+ LessOrEqual = Less | Equal,
+ NotEqual = Less | Greater,
+ GreaterOrEqual = Greater | Equal,
+ Number = Greater | Equal | Less,
+
+ Always = 7
+ }
+} \ No newline at end of file
diff --git a/Ryujinx.Graphics.Shader/Decoders/IntegerHalfPart.cs b/Ryujinx.Graphics.Shader/Decoders/IntegerHalfPart.cs
new file mode 100644
index 00000000..b779f44d
--- /dev/null
+++ b/Ryujinx.Graphics.Shader/Decoders/IntegerHalfPart.cs
@@ -0,0 +1,9 @@
+namespace Ryujinx.Graphics.Shader.Decoders
+{
+ enum IntegerHalfPart
+ {
+ B32 = 0,
+ H0 = 1,
+ H1 = 2
+ }
+} \ No newline at end of file
diff --git a/Ryujinx.Graphics.Shader/Decoders/IntegerShift.cs b/Ryujinx.Graphics.Shader/Decoders/IntegerShift.cs
new file mode 100644
index 00000000..ce4d9f3b
--- /dev/null
+++ b/Ryujinx.Graphics.Shader/Decoders/IntegerShift.cs
@@ -0,0 +1,9 @@
+namespace Ryujinx.Graphics.Shader.Decoders
+{
+ enum IntegerShift
+ {
+ NoShift = 0,
+ ShiftRight = 1,
+ ShiftLeft = 2
+ }
+} \ No newline at end of file
diff --git a/Ryujinx.Graphics.Shader/Decoders/IntegerSize.cs b/Ryujinx.Graphics.Shader/Decoders/IntegerSize.cs
new file mode 100644
index 00000000..3ff8e1b2
--- /dev/null
+++ b/Ryujinx.Graphics.Shader/Decoders/IntegerSize.cs
@@ -0,0 +1,13 @@
+namespace Ryujinx.Graphics.Shader.Decoders
+{
+ enum IntegerSize
+ {
+ U8 = 0,
+ S8 = 1,
+ U16 = 2,
+ S16 = 3,
+ B32 = 4,
+ B64 = 5,
+ B128 = 6
+ }
+} \ No newline at end of file
diff --git a/Ryujinx.Graphics.Shader/Decoders/IntegerType.cs b/Ryujinx.Graphics.Shader/Decoders/IntegerType.cs
new file mode 100644
index 00000000..46734dbe
--- /dev/null
+++ b/Ryujinx.Graphics.Shader/Decoders/IntegerType.cs
@@ -0,0 +1,14 @@
+namespace Ryujinx.Graphics.Shader.Decoders
+{
+ enum IntegerType
+ {
+ U8 = 0,
+ U16 = 1,
+ U32 = 2,
+ U64 = 3,
+ S8 = 4,
+ S16 = 5,
+ S32 = 6,
+ S64 = 7
+ }
+} \ No newline at end of file
diff --git a/Ryujinx.Graphics.Shader/Decoders/InterpolationMode.cs b/Ryujinx.Graphics.Shader/Decoders/InterpolationMode.cs
new file mode 100644
index 00000000..98ee3b97
--- /dev/null
+++ b/Ryujinx.Graphics.Shader/Decoders/InterpolationMode.cs
@@ -0,0 +1,10 @@
+namespace Ryujinx.Graphics.Shader.Decoders
+{
+ enum InterpolationMode
+ {
+ Pass,
+ Default,
+ Constant,
+ Sc
+ }
+} \ No newline at end of file
diff --git a/Ryujinx.Graphics.Shader/Decoders/LogicalOperation.cs b/Ryujinx.Graphics.Shader/Decoders/LogicalOperation.cs
new file mode 100644
index 00000000..52214425
--- /dev/null
+++ b/Ryujinx.Graphics.Shader/Decoders/LogicalOperation.cs
@@ -0,0 +1,10 @@
+namespace Ryujinx.Graphics.Shader.Decoders
+{
+ enum LogicalOperation
+ {
+ And = 0,
+ Or = 1,
+ ExclusiveOr = 2,
+ Passthrough = 3
+ }
+} \ No newline at end of file
diff --git a/Ryujinx.Graphics.Shader/Decoders/MufuOperation.cs b/Ryujinx.Graphics.Shader/Decoders/MufuOperation.cs
new file mode 100644
index 00000000..88bd1f5c
--- /dev/null
+++ b/Ryujinx.Graphics.Shader/Decoders/MufuOperation.cs
@@ -0,0 +1,15 @@
+namespace Ryujinx.Graphics.Shader.Decoders
+{
+ enum MufuOperation
+ {
+ Cosine = 0,
+ Sine = 1,
+ ExponentB2 = 2,
+ LogarithmB2 = 3,
+ Reciprocal = 4,
+ ReciprocalSquareRoot = 5,
+ Reciprocal64H = 6,
+ ReciprocalSquareRoot64H = 7,
+ SquareRoot = 8
+ }
+} \ No newline at end of file
diff --git a/Ryujinx.Graphics.Shader/Decoders/OpCode.cs b/Ryujinx.Graphics.Shader/Decoders/OpCode.cs
new file mode 100644
index 00000000..94af49e0
--- /dev/null
+++ b/Ryujinx.Graphics.Shader/Decoders/OpCode.cs
@@ -0,0 +1,30 @@
+using Ryujinx.Graphics.Shader.Instructions;
+
+namespace Ryujinx.Graphics.Shader.Decoders
+{
+ class OpCode
+ {
+ public InstEmitter Emitter { get; }
+
+ public ulong Address { get; }
+ public long RawOpCode { get; }
+
+ public Register Predicate { get; protected set; }
+
+ public bool InvertPredicate { get; protected set; }
+
+ // When inverted, the always true predicate == always false.
+ public bool NeverExecute => Predicate.Index == RegisterConsts.PredicateTrueIndex && InvertPredicate;
+
+ public OpCode(InstEmitter emitter, ulong address, long opCode)
+ {
+ Emitter = emitter;
+ Address = address;
+ RawOpCode = opCode;
+
+ Predicate = new Register(opCode.Extract(16, 3), RegisterType.Predicate);
+
+ InvertPredicate = opCode.Extract(19);
+ }
+ }
+} \ No newline at end of file
diff --git a/Ryujinx.Graphics.Shader/Decoders/OpCodeAlu.cs b/Ryujinx.Graphics.Shader/Decoders/OpCodeAlu.cs
new file mode 100644
index 00000000..15fbb9af
--- /dev/null
+++ b/Ryujinx.Graphics.Shader/Decoders/OpCodeAlu.cs
@@ -0,0 +1,34 @@
+using Ryujinx.Graphics.Shader.Instructions;
+
+namespace Ryujinx.Graphics.Shader.Decoders
+{
+ class OpCodeAlu : OpCode, IOpCodeAlu, IOpCodeRc
+ {
+ public Register Rd { get; }
+ public Register Ra { get; }
+ public Register Rc { get; }
+ public Register Predicate39 { get; }
+
+ public int ByteSelection { get; }
+
+ public bool InvertP { get; }
+ public bool Extended { get; protected set; }
+ public bool SetCondCode { get; protected set; }
+ public bool Saturate { get; protected set; }
+
+ public OpCodeAlu(InstEmitter emitter, ulong address, long opCode) : base(emitter, address, opCode)
+ {
+ Rd = new Register(opCode.Extract(0, 8), RegisterType.Gpr);
+ Ra = new Register(opCode.Extract(8, 8), RegisterType.Gpr);
+ Rc = new Register(opCode.Extract(39, 8), RegisterType.Gpr);
+ Predicate39 = new Register(opCode.Extract(39, 3), RegisterType.Predicate);
+
+ ByteSelection = opCode.Extract(41, 2);
+
+ InvertP = opCode.Extract(42);
+ Extended = opCode.Extract(43);
+ SetCondCode = opCode.Extract(47);
+ Saturate = opCode.Extract(50);
+ }
+ }
+} \ No newline at end of file
diff --git a/Ryujinx.Graphics.Shader/Decoders/OpCodeAluCbuf.cs b/Ryujinx.Graphics.Shader/Decoders/OpCodeAluCbuf.cs
new file mode 100644
index 00000000..9c127989
--- /dev/null
+++ b/Ryujinx.Graphics.Shader/Decoders/OpCodeAluCbuf.cs
@@ -0,0 +1,16 @@
+using Ryujinx.Graphics.Shader.Instructions;
+
+namespace Ryujinx.Graphics.Shader.Decoders
+{
+ class OpCodeAluCbuf : OpCodeAlu, IOpCodeCbuf
+ {
+ public int Offset { get; }
+ public int Slot { get; }
+
+ public OpCodeAluCbuf(InstEmitter emitter, ulong address, long opCode) : base(emitter, address, opCode)
+ {
+ Offset = opCode.Extract(20, 14);
+ Slot = opCode.Extract(34, 5);
+ }
+ }
+} \ No newline at end of file
diff --git a/Ryujinx.Graphics.Shader/Decoders/OpCodeAluImm.cs b/Ryujinx.Graphics.Shader/Decoders/OpCodeAluImm.cs
new file mode 100644
index 00000000..a407fc6b
--- /dev/null
+++ b/Ryujinx.Graphics.Shader/Decoders/OpCodeAluImm.cs
@@ -0,0 +1,14 @@
+using Ryujinx.Graphics.Shader.Instructions;
+
+namespace Ryujinx.Graphics.Shader.Decoders
+{
+ class OpCodeAluImm : OpCodeAlu, IOpCodeImm
+ {
+ public int Immediate { get; }
+
+ public OpCodeAluImm(InstEmitter emitter, ulong address, long opCode) : base(emitter, address, opCode)
+ {
+ Immediate = DecoderHelper.DecodeS20Immediate(opCode);
+ }
+ }
+} \ No newline at end of file
diff --git a/Ryujinx.Graphics.Shader/Decoders/OpCodeAluImm2x10.cs b/Ryujinx.Graphics.Shader/Decoders/OpCodeAluImm2x10.cs
new file mode 100644
index 00000000..9aeb32bd
--- /dev/null
+++ b/Ryujinx.Graphics.Shader/Decoders/OpCodeAluImm2x10.cs
@@ -0,0 +1,14 @@
+using Ryujinx.Graphics.Shader.Instructions;
+
+namespace Ryujinx.Graphics.Shader.Decoders
+{
+ class OpCodeAluImm2x10 : OpCodeAlu, IOpCodeImm
+ {
+ public int Immediate { get; }
+
+ public OpCodeAluImm2x10(InstEmitter emitter, ulong address, long opCode) : base(emitter, address, opCode)
+ {
+ Immediate = DecoderHelper.Decode2xF10Immediate(opCode);
+ }
+ }
+} \ No newline at end of file
diff --git a/Ryujinx.Graphics.Shader/Decoders/OpCodeAluImm32.cs b/Ryujinx.Graphics.Shader/Decoders/OpCodeAluImm32.cs
new file mode 100644
index 00000000..5941e0b9
--- /dev/null
+++ b/Ryujinx.Graphics.Shader/Decoders/OpCodeAluImm32.cs
@@ -0,0 +1,18 @@
+using Ryujinx.Graphics.Shader.Instructions;
+
+namespace Ryujinx.Graphics.Shader.Decoders
+{
+ class OpCodeAluImm32 : OpCodeAlu, IOpCodeImm
+ {
+ public int Immediate { get; }
+
+ public OpCodeAluImm32(InstEmitter emitter, ulong address, long opCode) : base(emitter, address, opCode)
+ {
+ Immediate = opCode.Extract(20, 32);
+
+ SetCondCode = opCode.Extract(52);
+ Extended = opCode.Extract(53);
+ Saturate = opCode.Extract(54);
+ }
+ }
+} \ No newline at end of file
diff --git a/Ryujinx.Graphics.Shader/Decoders/OpCodeAluReg.cs b/Ryujinx.Graphics.Shader/Decoders/OpCodeAluReg.cs
new file mode 100644
index 00000000..13b96a3a
--- /dev/null
+++ b/Ryujinx.Graphics.Shader/Decoders/OpCodeAluReg.cs
@@ -0,0 +1,14 @@
+using Ryujinx.Graphics.Shader.Instructions;
+
+namespace Ryujinx.Graphics.Shader.Decoders
+{
+ class OpCodeAluReg : OpCodeAlu, IOpCodeReg
+ {
+ public Register Rb { get; protected set; }
+
+ public OpCodeAluReg(InstEmitter emitter, ulong address, long opCode) : base(emitter, address, opCode)
+ {
+ Rb = new Register(opCode.Extract(20, 8), RegisterType.Gpr);
+ }
+ }
+} \ No newline at end of file
diff --git a/Ryujinx.Graphics.Shader/Decoders/OpCodeAluRegCbuf.cs b/Ryujinx.Graphics.Shader/Decoders/OpCodeAluRegCbuf.cs
new file mode 100644
index 00000000..6cf6bd2e
--- /dev/null
+++ b/Ryujinx.Graphics.Shader/Decoders/OpCodeAluRegCbuf.cs
@@ -0,0 +1,18 @@
+using Ryujinx.Graphics.Shader.Instructions;
+
+namespace Ryujinx.Graphics.Shader.Decoders
+{
+ class OpCodeAluRegCbuf : OpCodeAluReg, IOpCodeRegCbuf
+ {
+ public int Offset { get; }
+ public int Slot { get; }
+
+ public OpCodeAluRegCbuf(InstEmitter emitter, ulong address, long opCode) : base(emitter, address, opCode)
+ {
+ Offset = opCode.Extract(20, 14);
+ Slot = opCode.Extract(34, 5);
+
+ Rb = new Register(opCode.Extract(39, 8), RegisterType.Gpr);
+ }
+ }
+} \ No newline at end of file
diff --git a/Ryujinx.Graphics.Shader/Decoders/OpCodeAttribute.cs b/Ryujinx.Graphics.Shader/Decoders/OpCodeAttribute.cs
new file mode 100644
index 00000000..fd8e63fc
--- /dev/null
+++ b/Ryujinx.Graphics.Shader/Decoders/OpCodeAttribute.cs
@@ -0,0 +1,16 @@
+using Ryujinx.Graphics.Shader.Instructions;
+
+namespace Ryujinx.Graphics.Shader.Decoders
+{
+ class OpCodeAttribute : OpCodeAluReg
+ {
+ public int AttributeOffset { get; }
+ public int Count { get; }
+
+ public OpCodeAttribute(InstEmitter emitter, ulong address, long opCode) : base(emitter, address, opCode)
+ {
+ AttributeOffset = opCode.Extract(20, 10);
+ Count = opCode.Extract(47, 2) + 1;
+ }
+ }
+} \ No newline at end of file
diff --git a/Ryujinx.Graphics.Shader/Decoders/OpCodeBranch.cs b/Ryujinx.Graphics.Shader/Decoders/OpCodeBranch.cs
new file mode 100644
index 00000000..25941b39
--- /dev/null
+++ b/Ryujinx.Graphics.Shader/Decoders/OpCodeBranch.cs
@@ -0,0 +1,19 @@
+using Ryujinx.Graphics.Shader.Instructions;
+
+namespace Ryujinx.Graphics.Shader.Decoders
+{
+ class OpCodeBranch : OpCode
+ {
+ public int Offset { get; }
+
+ public OpCodeBranch(InstEmitter emitter, ulong address, long opCode) : base(emitter, address, opCode)
+ {
+ Offset = ((int)(opCode >> 20) << 8) >> 8;
+ }
+
+ public ulong GetAbsoluteAddress()
+ {
+ return (ulong)((long)Address + (long)Offset + 8);
+ }
+ }
+} \ No newline at end of file
diff --git a/Ryujinx.Graphics.Shader/Decoders/OpCodeExit.cs b/Ryujinx.Graphics.Shader/Decoders/OpCodeExit.cs
new file mode 100644
index 00000000..d50903eb
--- /dev/null
+++ b/Ryujinx.Graphics.Shader/Decoders/OpCodeExit.cs
@@ -0,0 +1,14 @@
+using Ryujinx.Graphics.Shader.Instructions;
+
+namespace Ryujinx.Graphics.Shader.Decoders
+{
+ class OpCodeExit : OpCode
+ {
+ public Condition Condition { get; }
+
+ public OpCodeExit(InstEmitter emitter, ulong address, long opCode) : base(emitter, address, opCode)
+ {
+ Condition = (Condition)opCode.Extract(0, 5);
+ }
+ }
+} \ No newline at end of file
diff --git a/Ryujinx.Graphics.Shader/Decoders/OpCodeFArith.cs b/Ryujinx.Graphics.Shader/Decoders/OpCodeFArith.cs
new file mode 100644
index 00000000..c88f7f0e
--- /dev/null
+++ b/Ryujinx.Graphics.Shader/Decoders/OpCodeFArith.cs
@@ -0,0 +1,24 @@
+using Ryujinx.Graphics.Shader.Instructions;
+
+namespace Ryujinx.Graphics.Shader.Decoders
+{
+ class OpCodeFArith : OpCodeAlu, IOpCodeFArith
+ {
+ public RoundingMode RoundingMode { get; }
+
+ public FmulScale Scale { get; }
+
+ public bool FlushToZero { get; }
+ public bool AbsoluteA { get; }
+
+ public OpCodeFArith(InstEmitter emitter, ulong address, long opCode) : base(emitter, address, opCode)
+ {
+ RoundingMode = (RoundingMode)opCode.Extract(39, 2);
+
+ Scale = (FmulScale)opCode.Extract(41, 3);
+
+ FlushToZero = opCode.Extract(44);
+ AbsoluteA = opCode.Extract(46);
+ }
+ }
+} \ No newline at end of file
diff --git a/Ryujinx.Graphics.Shader/Decoders/OpCodeFArithCbuf.cs b/Ryujinx.Graphics.Shader/Decoders/OpCodeFArithCbuf.cs
new file mode 100644
index 00000000..5486bb0b
--- /dev/null
+++ b/Ryujinx.Graphics.Shader/Decoders/OpCodeFArithCbuf.cs
@@ -0,0 +1,16 @@
+using Ryujinx.Graphics.Shader.Instructions;
+
+namespace Ryujinx.Graphics.Shader.Decoders
+{
+ class OpCodeFArithCbuf : OpCodeFArith, IOpCodeCbuf
+ {
+ public int Offset { get; }
+ public int Slot { get; }
+
+ public OpCodeFArithCbuf(InstEmitter emitter, ulong address, long opCode) : base(emitter, address, opCode)
+ {
+ Offset = opCode.Extract(20, 14);
+ Slot = opCode.Extract(34, 5);
+ }
+ }
+} \ No newline at end of file
diff --git a/Ryujinx.Graphics.Shader/Decoders/OpCodeFArithImm.cs b/Ryujinx.Graphics.Shader/Decoders/OpCodeFArithImm.cs
new file mode 100644
index 00000000..1bb6f425
--- /dev/null
+++ b/Ryujinx.Graphics.Shader/Decoders/OpCodeFArithImm.cs
@@ -0,0 +1,14 @@
+using Ryujinx.Graphics.Shader.Instructions;
+
+namespace Ryujinx.Graphics.Shader.Decoders
+{
+ class OpCodeFArithImm : OpCodeFArith, IOpCodeImmF
+ {
+ public float Immediate { get; }
+
+ public OpCodeFArithImm(InstEmitter emitter, ulong address, long opCode) : base(emitter, address, opCode)
+ {
+ Immediate = DecoderHelper.DecodeF20Immediate(opCode);
+ }
+ }
+} \ No newline at end of file
diff --git a/Ryujinx.Graphics.Shader/Decoders/OpCodeFArithImm32.cs b/Ryujinx.Graphics.Shader/Decoders/OpCodeFArithImm32.cs
new file mode 100644
index 00000000..ec9da6f3
--- /dev/null
+++ b/Ryujinx.Graphics.Shader/Decoders/OpCodeFArithImm32.cs
@@ -0,0 +1,30 @@
+using Ryujinx.Graphics.Shader.Instructions;
+using System;
+
+namespace Ryujinx.Graphics.Shader.Decoders
+{
+ class OpCodeFArithImm32 : OpCodeAlu, IOpCodeFArith, IOpCodeImmF
+ {
+ public RoundingMode RoundingMode => RoundingMode.ToNearest;
+
+ public FmulScale Scale => FmulScale.None;
+
+ public bool FlushToZero { get; }
+ public bool AbsoluteA { get; }
+
+ public float Immediate { get; }
+
+ public OpCodeFArithImm32(InstEmitter emitter, ulong address, long opCode) : base(emitter, address, opCode)
+ {
+ int imm = opCode.Extract(20, 32);
+
+ Immediate = BitConverter.Int32BitsToSingle(imm);
+
+ SetCondCode = opCode.Extract(52);
+ AbsoluteA = opCode.Extract(54);
+ FlushToZero = opCode.Extract(55);
+
+ Saturate = false;
+ }
+ }
+} \ No newline at end of file
diff --git a/Ryujinx.Graphics.Shader/Decoders/OpCodeFArithReg.cs b/Ryujinx.Graphics.Shader/Decoders/OpCodeFArithReg.cs
new file mode 100644
index 00000000..55cf4485
--- /dev/null
+++ b/Ryujinx.Graphics.Shader/Decoders/OpCodeFArithReg.cs
@@ -0,0 +1,14 @@
+using Ryujinx.Graphics.Shader.Instructions;
+
+namespace Ryujinx.Graphics.Shader.Decoders
+{
+ class OpCodeFArithReg : OpCodeFArith, IOpCodeReg
+ {
+ public Register Rb { get; protected set; }
+
+ public OpCodeFArithReg(InstEmitter emitter, ulong address, long opCode) : base(emitter, address, opCode)
+ {
+ Rb = new Register(opCode.Extract(20, 8), RegisterType.Gpr);
+ }
+ }
+} \ No newline at end of file
diff --git a/Ryujinx.Graphics.Shader/Decoders/OpCodeFArithRegCbuf.cs b/Ryujinx.Graphics.Shader/Decoders/OpCodeFArithRegCbuf.cs
new file mode 100644
index 00000000..315c2c8b
--- /dev/null
+++ b/Ryujinx.Graphics.Shader/Decoders/OpCodeFArithRegCbuf.cs
@@ -0,0 +1,16 @@
+using Ryujinx.Graphics.Shader.Instructions;
+
+namespace Ryujinx.Graphics.Shader.Decoders
+{
+ class OpCodeFArithRegCbuf : OpCodeFArith, IOpCodeRegCbuf
+ {
+ public int Offset { get; }
+ public int Slot { get; }
+
+ public OpCodeFArithRegCbuf(InstEmitter emitter, ulong address, long opCode) : base(emitter, address, opCode)
+ {
+ Offset = opCode.Extract(20, 14);
+ Slot = opCode.Extract(34, 5);
+ }
+ }
+} \ No newline at end of file
diff --git a/Ryujinx.Graphics.Shader/Decoders/OpCodeFsetImm.cs b/Ryujinx.Graphics.Shader/Decoders/OpCodeFsetImm.cs
new file mode 100644
index 00000000..cb5f155e
--- /dev/null
+++ b/Ryujinx.Graphics.Shader/Decoders/OpCodeFsetImm.cs
@@ -0,0 +1,14 @@
+using Ryujinx.Graphics.Shader.Instructions;
+
+namespace Ryujinx.Graphics.Shader.Decoders
+{
+ class OpCodeFsetImm : OpCodeSet, IOpCodeImmF
+ {
+ public float Immediate { get; }
+
+ public OpCodeFsetImm(InstEmitter emitter, ulong address, long opCode) : base(emitter, address, opCode)
+ {
+ Immediate = DecoderHelper.DecodeF20Immediate(opCode);
+ }
+ }
+} \ No newline at end of file
diff --git a/Ryujinx.Graphics.Shader/Decoders/OpCodeHfma.cs b/Ryujinx.Graphics.Shader/Decoders/OpCodeHfma.cs
new file mode 100644
index 00000000..32f3cd7a
--- /dev/null
+++ b/Ryujinx.Graphics.Shader/Decoders/OpCodeHfma.cs
@@ -0,0 +1,22 @@
+using Ryujinx.Graphics.Shader.Instructions;
+
+namespace Ryujinx.Graphics.Shader.Decoders
+{
+ class OpCodeHfma : OpCode, IOpCodeRd, IOpCodeRa, IOpCodeRc
+ {
+ public Register Rd { get; }
+ public Register Ra { get; }
+ public Register Rc { get; protected set; }
+
+ public FPHalfSwizzle SwizzleA { get; }
+
+ public OpCodeHfma(InstEmitter emitter, ulong address, long opCode) : base(emitter, address, opCode)
+ {
+ Rd = new Register(opCode.Extract(0, 8), RegisterType.Gpr);
+ Ra = new Register(opCode.Extract(8, 8), RegisterType.Gpr);
+ Rc = new Register(opCode.Extract(39, 8), RegisterType.Gpr);
+
+ SwizzleA = (FPHalfSwizzle)opCode.Extract(47, 2);
+ }
+ }
+} \ No newline at end of file
diff --git a/Ryujinx.Graphics.Shader/Decoders/OpCodeHfmaCbuf.cs b/Ryujinx.Graphics.Shader/Decoders/OpCodeHfmaCbuf.cs
new file mode 100644
index 00000000..33768c7d
--- /dev/null
+++ b/Ryujinx.Graphics.Shader/Decoders/OpCodeHfmaCbuf.cs
@@ -0,0 +1,30 @@
+using Ryujinx.Graphics.Shader.Instructions;
+
+namespace Ryujinx.Graphics.Shader.Decoders
+{
+ class OpCodeHfmaCbuf : OpCodeHfma, IOpCodeHfma, IOpCodeCbuf
+ {
+ public int Offset { get; }
+ public int Slot { get; }
+
+ public bool NegateB { get; }
+ public bool NegateC { get; }
+ public bool Saturate { get; }
+
+ public FPHalfSwizzle SwizzleB => FPHalfSwizzle.FP32;
+ public FPHalfSwizzle SwizzleC { get; }
+
+ public OpCodeHfmaCbuf(InstEmitter emitter, ulong address, long opCode) : base(emitter, address, opCode)
+ {
+ Offset = opCode.Extract(20, 14);
+ Slot = opCode.Extract(34, 5);
+
+ NegateC = opCode.Extract(51);
+ Saturate = opCode.Extract(52);
+
+ SwizzleC = (FPHalfSwizzle)opCode.Extract(53, 2);
+
+ NegateB = opCode.Extract(56);
+ }
+ }
+} \ No newline at end of file
diff --git a/Ryujinx.Graphics.Shader/Decoders/OpCodeHfmaImm2x10.cs b/Ryujinx.Graphics.Shader/Decoders/OpCodeHfmaImm2x10.cs
new file mode 100644
index 00000000..80a5a140
--- /dev/null
+++ b/Ryujinx.Graphics.Shader/Decoders/OpCodeHfmaImm2x10.cs
@@ -0,0 +1,26 @@
+using Ryujinx.Graphics.Shader.Instructions;
+
+namespace Ryujinx.Graphics.Shader.Decoders
+{
+ class OpCodeHfmaImm2x10 : OpCodeHfma, IOpCodeHfma, IOpCodeImm
+ {
+ public int Immediate { get; }
+
+ public bool NegateB => false;
+ public bool NegateC { get; }
+ public bool Saturate { get; }
+
+ public FPHalfSwizzle SwizzleB => FPHalfSwizzle.FP16;
+ public FPHalfSwizzle SwizzleC { get; }
+
+ public OpCodeHfmaImm2x10(InstEmitter emitter, ulong address, long opCode) : base(emitter, address, opCode)
+ {
+ Immediate = DecoderHelper.Decode2xF10Immediate(opCode);
+
+ NegateC = opCode.Extract(51);
+ Saturate = opCode.Extract(52);
+
+ SwizzleC = (FPHalfSwizzle)opCode.Extract(53, 2);
+ }
+ }
+} \ No newline at end of file
diff --git a/Ryujinx.Graphics.Shader/Decoders/OpCodeHfmaImm32.cs b/Ryujinx.Graphics.Shader/Decoders/OpCodeHfmaImm32.cs
new file mode 100644
index 00000000..05eb9ffe
--- /dev/null
+++ b/Ryujinx.Graphics.Shader/Decoders/OpCodeHfmaImm32.cs
@@ -0,0 +1,25 @@
+using Ryujinx.Graphics.Shader.Instructions;
+
+namespace Ryujinx.Graphics.Shader.Decoders
+{
+ class OpCodeHfmaImm32 : OpCodeHfma, IOpCodeHfma, IOpCodeImm
+ {
+ public int Immediate { get; }
+
+ public bool NegateB => false;
+ public bool NegateC { get; }
+ public bool Saturate => false;
+
+ public FPHalfSwizzle SwizzleB => FPHalfSwizzle.FP16;
+ public FPHalfSwizzle SwizzleC => FPHalfSwizzle.FP16;
+
+ public OpCodeHfmaImm32(InstEmitter emitter, ulong address, long opCode) : base(emitter, address, opCode)
+ {
+ Immediate = opCode.Extract(20, 32);
+
+ NegateC = opCode.Extract(52);
+
+ Rc = Rd;
+ }
+ }
+} \ No newline at end of file
diff --git a/Ryujinx.Graphics.Shader/Decoders/OpCodeHfmaReg.cs b/Ryujinx.Graphics.Shader/Decoders/OpCodeHfmaReg.cs
new file mode 100644
index 00000000..714c89de
--- /dev/null
+++ b/Ryujinx.Graphics.Shader/Decoders/OpCodeHfmaReg.cs
@@ -0,0 +1,29 @@
+using Ryujinx.Graphics.Shader.Instructions;
+
+namespace Ryujinx.Graphics.Shader.Decoders
+{
+ class OpCodeHfmaReg : OpCodeHfma, IOpCodeHfma, IOpCodeReg
+ {
+ public Register Rb { get; }
+
+ public bool NegateB { get; }
+ public bool NegateC { get; }
+ public bool Saturate { get; }
+
+ public FPHalfSwizzle SwizzleB { get; }
+ public FPHalfSwizzle SwizzleC { get; }
+
+ public OpCodeHfmaReg(InstEmitter emitter, ulong address, long opCode) : base(emitter, address, opCode)
+ {
+ Rb = new Register(opCode.Extract(20, 8), RegisterType.Gpr);
+
+ SwizzleB = (FPHalfSwizzle)opCode.Extract(28, 2);
+
+ NegateC = opCode.Extract(30);
+ NegateB = opCode.Extract(31);
+ Saturate = opCode.Extract(32);
+
+ SwizzleC = (FPHalfSwizzle)opCode.Extract(35, 2);
+ }
+ }
+} \ No newline at end of file
diff --git a/Ryujinx.Graphics.Shader/Decoders/OpCodeHfmaRegCbuf.cs b/Ryujinx.Graphics.Shader/Decoders/OpCodeHfmaRegCbuf.cs
new file mode 100644
index 00000000..c0001908
--- /dev/null
+++ b/Ryujinx.Graphics.Shader/Decoders/OpCodeHfmaRegCbuf.cs
@@ -0,0 +1,30 @@
+using Ryujinx.Graphics.Shader.Instructions;
+
+namespace Ryujinx.Graphics.Shader.Decoders
+{
+ class OpCodeHfmaRegCbuf : OpCodeHfma, IOpCodeHfma, IOpCodeRegCbuf
+ {
+ public int Offset { get; }
+ public int Slot { get; }
+
+ public bool NegateB { get; }
+ public bool NegateC { get; }
+ public bool Saturate { get; }
+
+ public FPHalfSwizzle SwizzleB { get; }
+ public FPHalfSwizzle SwizzleC => FPHalfSwizzle.FP32;
+
+ public OpCodeHfmaRegCbuf(InstEmitter emitter, ulong address, long opCode) : base(emitter, address, opCode)
+ {
+ Offset = opCode.Extract(20, 14);
+ Slot = opCode.Extract(34, 5);
+
+ NegateC = opCode.Extract(51);
+ Saturate = opCode.Extract(52);
+
+ SwizzleB = (FPHalfSwizzle)opCode.Extract(53, 2);
+
+ NegateB = opCode.Extract(56);
+ }
+ }
+} \ No newline at end of file
diff --git a/Ryujinx.Graphics.Shader/Decoders/OpCodeHsetImm2x10.cs b/Ryujinx.Graphics.Shader/Decoders/OpCodeHsetImm2x10.cs
new file mode 100644
index 00000000..03e1e44c
--- /dev/null
+++ b/Ryujinx.Graphics.Shader/Decoders/OpCodeHsetImm2x10.cs
@@ -0,0 +1,14 @@
+using Ryujinx.Graphics.Shader.Instructions;
+
+namespace Ryujinx.Graphics.Shader.Decoders
+{
+ class OpCodeHsetImm2x10 : OpCodeSet, IOpCodeImm
+ {
+ public int Immediate { get; }
+
+ public OpCodeHsetImm2x10(InstEmitter emitter, ulong address, long opCode) : base(emitter, address, opCode)
+ {
+ Immediate = DecoderHelper.Decode2xF10Immediate(opCode);
+ }
+ }
+} \ No newline at end of file
diff --git a/Ryujinx.Graphics.Shader/Decoders/OpCodeIpa.cs b/Ryujinx.Graphics.Shader/Decoders/OpCodeIpa.cs
new file mode 100644
index 00000000..b475b6a1
--- /dev/null
+++ b/Ryujinx.Graphics.Shader/Decoders/OpCodeIpa.cs
@@ -0,0 +1,20 @@
+using Ryujinx.Graphics.Shader.Instructions;
+
+namespace Ryujinx.Graphics.Shader.Decoders
+{
+ class OpCodeIpa : OpCodeAluReg
+ {
+ public int AttributeOffset { get; }
+
+ public InterpolationMode Mode { get; }
+
+ public OpCodeIpa(InstEmitter emitter, ulong address, long opCode) : base(emitter, address, opCode)
+ {
+ AttributeOffset = opCode.Extract(28, 10);
+
+ Saturate = opCode.Extract(51);
+
+ Mode = (InterpolationMode)opCode.Extract(54, 2);
+ }
+ }
+} \ No newline at end of file
diff --git a/Ryujinx.Graphics.Shader/Decoders/OpCodeLdc.cs b/Ryujinx.Graphics.Shader/Decoders/OpCodeLdc.cs
new file mode 100644
index 00000000..cc9f0658
--- /dev/null
+++ b/Ryujinx.Graphics.Shader/Decoders/OpCodeLdc.cs
@@ -0,0 +1,26 @@
+using Ryujinx.Graphics.Shader.Instructions;
+
+namespace Ryujinx.Graphics.Shader.Decoders
+{
+ class OpCodeLdc : OpCode, IOpCodeRd, IOpCodeRa, IOpCodeCbuf
+ {
+ public Register Rd { get; }
+ public Register Ra { get; }
+
+ public int Offset { get; }
+ public int Slot { get; }
+
+ public IntegerSize Size { get; }
+
+ public OpCodeLdc(InstEmitter emitter, ulong address, long opCode) : base(emitter, address, opCode)
+ {
+ Rd = new Register(opCode.Extract(0, 8), RegisterType.Gpr);
+ Ra = new Register(opCode.Extract(8, 8), RegisterType.Gpr);
+
+ Offset = opCode.Extract(22, 14);
+ Slot = opCode.Extract(36, 5);
+
+ Size = (IntegerSize)opCode.Extract(48, 3);
+ }
+ }
+} \ No newline at end of file
diff --git a/Ryujinx.Graphics.Shader/Decoders/OpCodeLop.cs b/Ryujinx.Graphics.Shader/Decoders/OpCodeLop.cs
new file mode 100644
index 00000000..c5f90345
--- /dev/null
+++ b/Ryujinx.Graphics.Shader/Decoders/OpCodeLop.cs
@@ -0,0 +1,28 @@
+using Ryujinx.Graphics.Shader.Instructions;
+
+namespace Ryujinx.Graphics.Shader.Decoders
+{
+ class OpCodeLop : OpCodeAlu, IOpCodeLop
+ {
+ public bool InvertA { get; protected set; }
+ public bool InvertB { get; protected set; }
+
+ public LogicalOperation LogicalOp { get; }
+
+ public ConditionalOperation CondOp { get; }
+
+ public Register Predicate48 { get; }
+
+ public OpCodeLop(InstEmitter emitter, ulong address, long opCode) : base(emitter, address, opCode)
+ {
+ InvertA = opCode.Extract(39);
+ InvertB = opCode.Extract(40);
+
+ LogicalOp = (LogicalOperation)opCode.Extract(41, 2);
+
+ CondOp = (ConditionalOperation)opCode.Extract(44, 2);
+
+ Predicate48 = new Register(opCode.Extract(48, 3), RegisterType.Predicate);
+ }
+ }
+} \ No newline at end of file
diff --git a/Ryujinx.Graphics.Shader/Decoders/OpCodeLopCbuf.cs b/Ryujinx.Graphics.Shader/Decoders/OpCodeLopCbuf.cs
new file mode 100644
index 00000000..f174733c
--- /dev/null
+++ b/Ryujinx.Graphics.Shader/Decoders/OpCodeLopCbuf.cs
@@ -0,0 +1,16 @@
+using Ryujinx.Graphics.Shader.Instructions;
+
+namespace Ryujinx.Graphics.Shader.Decoders
+{
+ class OpCodeLopCbuf : OpCodeLop, IOpCodeCbuf
+ {
+ public int Offset { get; }
+ public int Slot { get; }
+
+ public OpCodeLopCbuf(InstEmitter emitter, ulong address, long opCode) : base(emitter, address, opCode)
+ {
+ Offset = opCode.Extract(20, 14);
+ Slot = opCode.Extract(34, 5);
+ }
+ }
+} \ No newline at end of file
diff --git a/Ryujinx.Graphics.Shader/Decoders/OpCodeLopImm.cs b/Ryujinx.Graphics.Shader/Decoders/OpCodeLopImm.cs
new file mode 100644
index 00000000..a2f091a2
--- /dev/null
+++ b/Ryujinx.Graphics.Shader/Decoders/OpCodeLopImm.cs
@@ -0,0 +1,14 @@
+using Ryujinx.Graphics.Shader.Instructions;
+
+namespace Ryujinx.Graphics.Shader.Decoders
+{
+ class OpCodeLopImm : OpCodeLop, IOpCodeImm
+ {
+ public int Immediate { get; }
+
+ public OpCodeLopImm(InstEmitter emitter, ulong address, long opCode) : base(emitter, address, opCode)
+ {
+ Immediate = DecoderHelper.DecodeS20Immediate(opCode);
+ }
+ }
+} \ No newline at end of file
diff --git a/Ryujinx.Graphics.Shader/Decoders/OpCodeLopImm32.cs b/Ryujinx.Graphics.Shader/Decoders/OpCodeLopImm32.cs
new file mode 100644
index 00000000..cb48f3a6
--- /dev/null
+++ b/Ryujinx.Graphics.Shader/Decoders/OpCodeLopImm32.cs
@@ -0,0 +1,22 @@
+using Ryujinx.Graphics.Shader.Instructions;
+
+namespace Ryujinx.Graphics.Shader.Decoders
+{
+ class OpCodeLopImm32 : OpCodeAluImm32, IOpCodeLop, IOpCodeImm
+ {
+ public LogicalOperation LogicalOp { get; }
+
+ public bool InvertA { get; }
+ public bool InvertB { get; }
+
+ public OpCodeLopImm32(InstEmitter emitter, ulong address, long opCode) : base(emitter, address, opCode)
+ {
+ LogicalOp = (LogicalOperation)opCode.Extract(53, 2);
+
+ InvertA = opCode.Extract(55);
+ InvertB = opCode.Extract(56);
+
+ Extended = opCode.Extract(57);
+ }
+ }
+} \ No newline at end of file
diff --git a/Ryujinx.Graphics.Shader/Decoders/OpCodeLopReg.cs b/Ryujinx.Graphics.Shader/Decoders/OpCodeLopReg.cs
new file mode 100644
index 00000000..5f43db72
--- /dev/null
+++ b/Ryujinx.Graphics.Shader/Decoders/OpCodeLopReg.cs
@@ -0,0 +1,14 @@
+using Ryujinx.Graphics.Shader.Instructions;
+
+namespace Ryujinx.Graphics.Shader.Decoders
+{
+ class OpCodeLopReg : OpCodeLop, IOpCodeReg
+ {
+ public Register Rb { get; }
+
+ public OpCodeLopReg(InstEmitter emitter, ulong address, long opCode) : base(emitter, address, opCode)
+ {
+ Rb = new Register(opCode.Extract(20, 8), RegisterType.Gpr);
+ }
+ }
+} \ No newline at end of file
diff --git a/Ryujinx.Graphics.Shader/Decoders/OpCodeMemory.cs b/Ryujinx.Graphics.Shader/Decoders/OpCodeMemory.cs
new file mode 100644
index 00000000..bece4562
--- /dev/null
+++ b/Ryujinx.Graphics.Shader/Decoders/OpCodeMemory.cs
@@ -0,0 +1,28 @@
+using Ryujinx.Graphics.Shader.Instructions;
+
+namespace Ryujinx.Graphics.Shader.Decoders
+{
+ class OpCodeMemory : OpCode, IOpCodeRd, IOpCodeRa
+ {
+ public Register Rd { get; }
+ public Register Ra { get; }
+
+ public int Offset { get; }
+
+ public bool Extended { get; }
+
+ public IntegerSize Size { get; }
+
+ public OpCodeMemory(InstEmitter emitter, ulong address, long opCode) : base(emitter, address, opCode)
+ {
+ Rd = new Register(opCode.Extract(0, 8), RegisterType.Gpr);
+ Ra = new Register(opCode.Extract(8, 8), RegisterType.Gpr);
+
+ Offset = opCode.Extract(20, 24);
+
+ Extended = opCode.Extract(45);
+
+ Size = (IntegerSize)opCode.Extract(48, 3);
+ }
+ }
+} \ No newline at end of file
diff --git a/Ryujinx.Graphics.Shader/Decoders/OpCodePsetp.cs b/Ryujinx.Graphics.Shader/Decoders/OpCodePsetp.cs
new file mode 100644
index 00000000..729e3207
--- /dev/null
+++ b/Ryujinx.Graphics.Shader/Decoders/OpCodePsetp.cs
@@ -0,0 +1,20 @@
+using Ryujinx.Graphics.Shader.Instructions;
+
+namespace Ryujinx.Graphics.Shader.Decoders
+{
+ class OpCodePsetp : OpCodeSet
+ {
+ public Register Predicate12 { get; }
+ public Register Predicate29 { get; }
+
+ public LogicalOperation LogicalOpAB { get; }
+
+ public OpCodePsetp(InstEmitter emitter, ulong address, long opCode) : base(emitter, address, opCode)
+ {
+ Predicate12 = new Register(opCode.Extract(12, 3), RegisterType.Predicate);
+ Predicate29 = new Register(opCode.Extract(29, 3), RegisterType.Predicate);
+
+ LogicalOpAB = (LogicalOperation)opCode.Extract(24, 2);
+ }
+ }
+} \ No newline at end of file
diff --git a/Ryujinx.Graphics.Shader/Decoders/OpCodeSet.cs b/Ryujinx.Graphics.Shader/Decoders/OpCodeSet.cs
new file mode 100644
index 00000000..b4ee10fb
--- /dev/null
+++ b/Ryujinx.Graphics.Shader/Decoders/OpCodeSet.cs
@@ -0,0 +1,26 @@
+using Ryujinx.Graphics.Shader.Instructions;
+
+namespace Ryujinx.Graphics.Shader.Decoders
+{
+ class OpCodeSet : OpCodeAlu
+ {
+ public Register Predicate0 { get; }
+ public Register Predicate3 { get; }
+
+ public bool NegateP { get; }
+
+ public LogicalOperation LogicalOp { get; }
+
+ public bool FlushToZero { get; }
+
+ public OpCodeSet(InstEmitter emitter, ulong address, long opCode) : base(emitter, address, opCode)
+ {
+ Predicate0 = new Register(opCode.Extract(0, 3), RegisterType.Predicate);
+ Predicate3 = new Register(opCode.Extract(3, 3), RegisterType.Predicate);
+
+ LogicalOp = (LogicalOperation)opCode.Extract(45, 2);
+
+ FlushToZero = opCode.Extract(47);
+ }
+ }
+} \ No newline at end of file
diff --git a/Ryujinx.Graphics.Shader/Decoders/OpCodeSetCbuf.cs b/Ryujinx.Graphics.Shader/Decoders/OpCodeSetCbuf.cs
new file mode 100644
index 00000000..4f3dbd74
--- /dev/null
+++ b/Ryujinx.Graphics.Shader/Decoders/OpCodeSetCbuf.cs
@@ -0,0 +1,16 @@
+using Ryujinx.Graphics.Shader.Instructions;
+
+namespace Ryujinx.Graphics.Shader.Decoders
+{
+ class OpCodeSetCbuf : OpCodeSet, IOpCodeCbuf
+ {
+ public int Offset { get; }
+ public int Slot { get; }
+
+ public OpCodeSetCbuf(InstEmitter emitter, ulong address, long opCode) : base(emitter, address, opCode)
+ {
+ Offset = opCode.Extract(20, 14);
+ Slot = opCode.Extract(34, 5);
+ }
+ }
+} \ No newline at end of file
diff --git a/Ryujinx.Graphics.Shader/Decoders/OpCodeSetImm.cs b/Ryujinx.Graphics.Shader/Decoders/OpCodeSetImm.cs
new file mode 100644
index 00000000..bc63b9f4
--- /dev/null
+++ b/Ryujinx.Graphics.Shader/Decoders/OpCodeSetImm.cs
@@ -0,0 +1,14 @@
+using Ryujinx.Graphics.Shader.Instructions;
+
+namespace Ryujinx.Graphics.Shader.Decoders
+{
+ class OpCodeSetImm : OpCodeSet, IOpCodeImm
+ {
+ public int Immediate { get; }
+
+ public OpCodeSetImm(InstEmitter emitter, ulong address, long opCode) : base(emitter, address, opCode)
+ {
+ Immediate = DecoderHelper.DecodeS20Immediate(opCode);
+ }
+ }
+} \ No newline at end of file
diff --git a/Ryujinx.Graphics.Shader/Decoders/OpCodeSetReg.cs b/Ryujinx.Graphics.Shader/Decoders/OpCodeSetReg.cs
new file mode 100644
index 00000000..bbdee196
--- /dev/null
+++ b/Ryujinx.Graphics.Shader/Decoders/OpCodeSetReg.cs
@@ -0,0 +1,14 @@
+using Ryujinx.Graphics.Shader.Instructions;
+
+namespace Ryujinx.Graphics.Shader.Decoders
+{
+ class OpCodeSetReg : OpCodeSet, IOpCodeReg
+ {
+ public Register Rb { get; protected set; }
+
+ public OpCodeSetReg(InstEmitter emitter, ulong address, long opCode) : base(emitter, address, opCode)
+ {
+ Rb = new Register(opCode.Extract(20, 8), RegisterType.Gpr);
+ }
+ }
+} \ No newline at end of file
diff --git a/Ryujinx.Graphics.Shader/Decoders/OpCodeSsy.cs b/Ryujinx.Graphics.Shader/Decoders/OpCodeSsy.cs
new file mode 100644
index 00000000..499c0706
--- /dev/null
+++ b/Ryujinx.Graphics.Shader/Decoders/OpCodeSsy.cs
@@ -0,0 +1,20 @@
+using Ryujinx.Graphics.Shader.Instructions;
+using Ryujinx.Graphics.Shader.IntermediateRepresentation;
+using System.Collections.Generic;
+
+namespace Ryujinx.Graphics.Shader.Decoders
+{
+ class OpCodeSsy : OpCodeBranch
+ {
+ public Dictionary<OpCodeSync, Operand> Syncs { get; }
+
+ public OpCodeSsy(InstEmitter emitter, ulong address, long opCode) : base(emitter, address, opCode)
+ {
+ Syncs = new Dictionary<OpCodeSync, Operand>();
+
+ Predicate = new Register(RegisterConsts.PredicateTrueIndex, RegisterType.Predicate);
+
+ InvertPredicate = false;
+ }
+ }
+} \ No newline at end of file
diff --git a/Ryujinx.Graphics.Shader/Decoders/OpCodeSync.cs b/Ryujinx.Graphics.Shader/Decoders/OpCodeSync.cs
new file mode 100644
index 00000000..081d08a0
--- /dev/null
+++ b/Ryujinx.Graphics.Shader/Decoders/OpCodeSync.cs
@@ -0,0 +1,15 @@
+using Ryujinx.Graphics.Shader.Instructions;
+using System.Collections.Generic;
+
+namespace Ryujinx.Graphics.Shader.Decoders
+{
+ class OpCodeSync : OpCode
+ {
+ public Dictionary<OpCodeSsy, int> Targets { get; }
+
+ public OpCodeSync(InstEmitter emitter, ulong address, long opCode) : base(emitter, address, opCode)
+ {
+ Targets = new Dictionary<OpCodeSsy, int>();
+ }
+ }
+} \ No newline at end of file
diff --git a/Ryujinx.Graphics.Shader/Decoders/OpCodeTable.cs b/Ryujinx.Graphics.Shader/Decoders/OpCodeTable.cs
new file mode 100644
index 00000000..3fd1de86
--- /dev/null
+++ b/Ryujinx.Graphics.Shader/Decoders/OpCodeTable.cs
@@ -0,0 +1,227 @@
+using Ryujinx.Graphics.Shader.Instructions;
+using System;
+
+namespace Ryujinx.Graphics.Shader.Decoders
+{
+ static class OpCodeTable
+ {
+ private const int EncodingBits = 14;
+
+ private class TableEntry
+ {
+ public InstEmitter Emitter { get; }
+
+ public Type OpCodeType { get; }
+
+ public int XBits { get; }
+
+ public TableEntry(InstEmitter emitter, Type opCodeType, int xBits)
+ {
+ Emitter = emitter;
+ OpCodeType = opCodeType;
+ XBits = xBits;
+ }
+ }
+
+ private static TableEntry[] _opCodes;
+
+ static OpCodeTable()
+ {
+ _opCodes = new TableEntry[1 << EncodingBits];
+
+#region Instructions
+ Set("1110111111011x", InstEmit.Ald, typeof(OpCodeAttribute));
+ Set("1110111111110x", InstEmit.Ast, typeof(OpCodeAttribute));
+ Set("0100110000000x", InstEmit.Bfe, typeof(OpCodeAluCbuf));
+ Set("0011100x00000x", InstEmit.Bfe, typeof(OpCodeAluImm));
+ Set("0101110000000x", InstEmit.Bfe, typeof(OpCodeAluReg));
+ Set("111000100100xx", InstEmit.Bra, typeof(OpCodeBranch));
+ Set("0101000010100x", InstEmit.Csetp, typeof(OpCodePsetp));
+ Set("111000110000xx", InstEmit.Exit, typeof(OpCodeExit));
+ Set("0100110010101x", InstEmit.F2F, typeof(OpCodeFArithCbuf));
+ Set("0011100x10101x", InstEmit.F2F, typeof(OpCodeFArithImm));
+ Set("0101110010101x", InstEmit.F2F, typeof(OpCodeFArithReg));
+ Set("0100110010110x", InstEmit.F2I, typeof(OpCodeFArithCbuf));
+ Set("0011100x10110x", InstEmit.F2I, typeof(OpCodeFArithImm));
+ Set("0101110010110x", InstEmit.F2I, typeof(OpCodeFArithReg));
+ Set("0100110001011x", InstEmit.Fadd, typeof(OpCodeFArithCbuf));
+ Set("0011100x01011x", InstEmit.Fadd, typeof(OpCodeFArithImm));
+ Set("000010xxxxxxxx", InstEmit.Fadd, typeof(OpCodeFArithImm32));
+ Set("0101110001011x", InstEmit.Fadd, typeof(OpCodeFArithReg));
+ Set("010010011xxxxx", InstEmit.Ffma, typeof(OpCodeFArithCbuf));
+ Set("0011001x1xxxxx", InstEmit.Ffma, typeof(OpCodeFArithImm));
+ Set("010100011xxxxx", InstEmit.Ffma, typeof(OpCodeFArithRegCbuf));
+ Set("010110011xxxxx", InstEmit.Ffma, typeof(OpCodeFArithReg));
+ Set("0100110001100x", InstEmit.Fmnmx, typeof(OpCodeFArithCbuf));
+ Set("0011100x01100x", InstEmit.Fmnmx, typeof(OpCodeFArithImm));
+ Set("0101110001100x", InstEmit.Fmnmx, typeof(OpCodeFArithReg));
+ Set("0100110001101x", InstEmit.Fmul, typeof(OpCodeFArithCbuf));
+ Set("0011100x01101x", InstEmit.Fmul, typeof(OpCodeFArithImm));
+ Set("00011110xxxxxx", InstEmit.Fmul, typeof(OpCodeFArithImm32));
+ Set("0101110001101x", InstEmit.Fmul, typeof(OpCodeFArithReg));
+ Set("0100100xxxxxxx", InstEmit.Fset, typeof(OpCodeSetCbuf));
+ Set("0011000xxxxxxx", InstEmit.Fset, typeof(OpCodeFsetImm));
+ Set("01011000xxxxxx", InstEmit.Fset, typeof(OpCodeSetReg));
+ Set("010010111011xx", InstEmit.Fsetp, typeof(OpCodeSetCbuf));
+ Set("0011011x1011xx", InstEmit.Fsetp, typeof(OpCodeFsetImm));
+ Set("010110111011xx", InstEmit.Fsetp, typeof(OpCodeSetReg));
+ Set("0111101x1xxxxx", InstEmit.Hadd2, typeof(OpCodeAluCbuf));
+ Set("0111101x0xxxxx", InstEmit.Hadd2, typeof(OpCodeAluImm2x10));
+ Set("0010110xxxxxxx", InstEmit.Hadd2, typeof(OpCodeAluImm32));
+ Set("0101110100010x", InstEmit.Hadd2, typeof(OpCodeAluReg));
+ Set("01110xxx1xxxxx", InstEmit.Hfma2, typeof(OpCodeHfmaCbuf));
+ Set("01110xxx0xxxxx", InstEmit.Hfma2, typeof(OpCodeHfmaImm2x10));
+ Set("0010100xxxxxxx", InstEmit.Hfma2, typeof(OpCodeHfmaImm32));
+ Set("0101110100000x", InstEmit.Hfma2, typeof(OpCodeHfmaReg));
+ Set("01100xxx1xxxxx", InstEmit.Hfma2, typeof(OpCodeHfmaRegCbuf));
+ Set("0111100x1xxxxx", InstEmit.Hmul2, typeof(OpCodeAluCbuf));
+ Set("0111100x0xxxxx", InstEmit.Hmul2, typeof(OpCodeAluImm2x10));
+ Set("0010101xxxxxxx", InstEmit.Hmul2, typeof(OpCodeAluImm32));
+ Set("0101110100001x", InstEmit.Hmul2, typeof(OpCodeAluReg));
+ Set("0111111x1xxxxx", InstEmit.Hsetp2, typeof(OpCodeSetCbuf));
+ Set("0111111x0xxxxx", InstEmit.Hsetp2, typeof(OpCodeHsetImm2x10));
+ Set("0101110100100x", InstEmit.Hsetp2, typeof(OpCodeSetReg));
+ Set("0100110010111x", InstEmit.I2F, typeof(OpCodeAluCbuf));
+ Set("0011100x10111x", InstEmit.I2F, typeof(OpCodeAluImm));
+ Set("0101110010111x", InstEmit.I2F, typeof(OpCodeAluReg));
+ Set("0100110011100x", InstEmit.I2I, typeof(OpCodeAluCbuf));
+ Set("0011100x11100x", InstEmit.I2I, typeof(OpCodeAluImm));
+ Set("0101110011100x", InstEmit.I2I, typeof(OpCodeAluReg));
+ Set("0100110000010x", InstEmit.Iadd, typeof(OpCodeAluCbuf));
+ Set("0011100000010x", InstEmit.Iadd, typeof(OpCodeAluImm));
+ Set("0001110x0xxxxx", InstEmit.Iadd, typeof(OpCodeAluImm32));
+ Set("0101110000010x", InstEmit.Iadd, typeof(OpCodeAluReg));
+ Set("010011001100xx", InstEmit.Iadd3, typeof(OpCodeAluCbuf));
+ Set("001110001100xx", InstEmit.Iadd3, typeof(OpCodeAluImm));
+ Set("010111001100xx", InstEmit.Iadd3, typeof(OpCodeAluReg));
+ Set("0100110000100x", InstEmit.Imnmx, typeof(OpCodeAluCbuf));
+ Set("0011100x00100x", InstEmit.Imnmx, typeof(OpCodeAluImm));
+ Set("0101110000100x", InstEmit.Imnmx, typeof(OpCodeAluReg));
+ Set("11100000xxxxxx", InstEmit.Ipa, typeof(OpCodeIpa));
+ Set("1110111111010x", InstEmit.Isberd, typeof(OpCodeAlu));
+ Set("0100110000011x", InstEmit.Iscadd, typeof(OpCodeAluCbuf));
+ Set("0011100x00011x", InstEmit.Iscadd, typeof(OpCodeAluImm));
+ Set("000101xxxxxxxx", InstEmit.Iscadd, typeof(OpCodeAluImm32));
+ Set("0101110000011x", InstEmit.Iscadd, typeof(OpCodeAluReg));
+ Set("010010110101xx", InstEmit.Iset, typeof(OpCodeSetCbuf));
+ Set("001101100101xx", InstEmit.Iset, typeof(OpCodeSetImm));
+ Set("010110110101xx", InstEmit.Iset, typeof(OpCodeSetReg));
+ Set("010010110110xx", InstEmit.Isetp, typeof(OpCodeSetCbuf));
+ Set("0011011x0110xx", InstEmit.Isetp, typeof(OpCodeSetImm));
+ Set("010110110110xx", InstEmit.Isetp, typeof(OpCodeSetReg));
+ Set("111000110011xx", InstEmit.Kil, typeof(OpCodeExit));
+ Set("1110111101000x", InstEmit.Ld, typeof(OpCodeMemory));
+ Set("1110111110010x", InstEmit.Ldc, typeof(OpCodeLdc));
+ Set("1110111011010x", InstEmit.Ldg, typeof(OpCodeMemory));
+ Set("0100110001000x", InstEmit.Lop, typeof(OpCodeLopCbuf));
+ Set("0011100001000x", InstEmit.Lop, typeof(OpCodeLopImm));
+ Set("000001xxxxxxxx", InstEmit.Lop, typeof(OpCodeLopImm32));
+ Set("0101110001000x", InstEmit.Lop, typeof(OpCodeLopReg));
+ Set("0010000xxxxxxx", InstEmit.Lop3, typeof(OpCodeLopCbuf));
+ Set("001111xxxxxxxx", InstEmit.Lop3, typeof(OpCodeLopImm));
+ Set("0101101111100x", InstEmit.Lop3, typeof(OpCodeLopReg));
+ Set("0100110010011x", InstEmit.Mov, typeof(OpCodeAluCbuf));
+ Set("0011100x10011x", InstEmit.Mov, typeof(OpCodeAluImm));
+ Set("000000010000xx", InstEmit.Mov, typeof(OpCodeAluImm32));
+ Set("0101110010011x", InstEmit.Mov, typeof(OpCodeAluReg));
+ Set("0101000010000x", InstEmit.Mufu, typeof(OpCodeFArith));
+ Set("1111101111100x", InstEmit.Out, typeof(OpCode));
+ Set("0101000010010x", InstEmit.Psetp, typeof(OpCodePsetp));
+ Set("0100110010010x", InstEmit.Rro, typeof(OpCodeFArithCbuf));
+ Set("0011100x10010x", InstEmit.Rro, typeof(OpCodeFArithImm));
+ Set("0101110010010x", InstEmit.Rro, typeof(OpCodeFArithReg));
+ Set("1111000011001x", InstEmit.S2r, typeof(OpCodeAlu));
+ Set("0100110010100x", InstEmit.Sel, typeof(OpCodeAluCbuf));
+ Set("0011100x10100x", InstEmit.Sel, typeof(OpCodeAluImm));
+ Set("0101110010100x", InstEmit.Sel, typeof(OpCodeAluReg));
+ Set("0100110001001x", InstEmit.Shl, typeof(OpCodeAluCbuf));
+ Set("0011100x01001x", InstEmit.Shl, typeof(OpCodeAluImm));
+ Set("0101110001001x", InstEmit.Shl, typeof(OpCodeAluReg));
+ Set("0100110000101x", InstEmit.Shr, typeof(OpCodeAluCbuf));
+ Set("0011100x00101x", InstEmit.Shr, typeof(OpCodeAluImm));
+ Set("0101110000101x", InstEmit.Shr, typeof(OpCodeAluReg));
+ Set("111000101001xx", InstEmit.Ssy, typeof(OpCodeSsy));
+ Set("1110111101010x", InstEmit.St, typeof(OpCodeMemory));
+ Set("1110111011011x", InstEmit.Stg, typeof(OpCodeMemory));
+ Set("1111000011111x", InstEmit.Sync, typeof(OpCodeSync));
+ Set("110000xxxx111x", InstEmit.Tex, typeof(OpCodeTex));
+ Set("1101111010111x", InstEmit.TexB, typeof(OpCodeTexB));
+ Set("1101x00xxxxxxx", InstEmit.Texs, typeof(OpCodeTexs));
+ Set("1101x01xxxxxxx", InstEmit.Texs, typeof(OpCodeTlds));
+ Set("1101x11100xxxx", InstEmit.Texs, typeof(OpCodeTld4s));
+ Set("11011100xx111x", InstEmit.Tld, typeof(OpCodeTld));
+ Set("11011101xx111x", InstEmit.TldB, typeof(OpCodeTld));
+ Set("110010xxxx111x", InstEmit.Tld4, typeof(OpCodeTld4));
+ Set("1101111101001x", InstEmit.Txq, typeof(OpCodeTex));
+ Set("1101111101010x", InstEmit.TxqB, typeof(OpCodeTex));
+ Set("01011111xxxxxx", InstEmit.Vmad, typeof(OpCodeVideo));
+ Set("0100111xxxxxxx", InstEmit.Xmad, typeof(OpCodeAluCbuf));
+ Set("0011011x00xxxx", InstEmit.Xmad, typeof(OpCodeAluImm));
+ Set("010100010xxxxx", InstEmit.Xmad, typeof(OpCodeAluRegCbuf));
+ Set("0101101100xxxx", InstEmit.Xmad, typeof(OpCodeAluReg));
+#endregion
+ }
+
+ private static void Set(string encoding, InstEmitter emitter, Type opCodeType)
+ {
+ if (encoding.Length != EncodingBits)
+ {
+ throw new ArgumentException(nameof(encoding));
+ }
+
+ int bit = encoding.Length - 1;
+ int value = 0;
+ int xMask = 0;
+ int xBits = 0;
+
+ int[] xPos = new int[encoding.Length];
+
+ for (int index = 0; index < encoding.Length; index++, bit--)
+ {
+ char chr = encoding[index];
+
+ if (chr == '1')
+ {
+ value |= 1 << bit;
+ }
+ else if (chr == 'x')
+ {
+ xMask |= 1 << bit;
+
+ xPos[xBits++] = bit;
+ }
+ }
+
+ xMask = ~xMask;
+
+ TableEntry entry = new TableEntry(emitter, opCodeType, xBits);
+
+ for (int index = 0; index < (1 << xBits); index++)
+ {
+ value &= xMask;
+
+ for (int x = 0; x < xBits; x++)
+ {
+ value |= ((index >> x) & 1) << xPos[x];
+ }
+
+ if (_opCodes[value] == null || _opCodes[value].XBits > xBits)
+ {
+ _opCodes[value] = entry;
+ }
+ }
+ }
+
+ public static (InstEmitter emitter, Type opCodeType) GetEmitter(long opCode)
+ {
+ TableEntry entry = _opCodes[(ulong)opCode >> (64 - EncodingBits)];
+
+ if (entry != null)
+ {
+ return (entry.Emitter, entry.OpCodeType);
+ }
+
+ return (null, null);
+ }
+ }
+} \ No newline at end of file
diff --git a/Ryujinx.Graphics.Shader/Decoders/OpCodeTex.cs b/Ryujinx.Graphics.Shader/Decoders/OpCodeTex.cs
new file mode 100644
index 00000000..da8756b9
--- /dev/null
+++ b/Ryujinx.Graphics.Shader/Decoders/OpCodeTex.cs
@@ -0,0 +1,14 @@
+using Ryujinx.Graphics.Shader.Instructions;
+
+namespace Ryujinx.Graphics.Shader.Decoders
+{
+ class OpCodeTex : OpCodeTexture
+ {
+ public OpCodeTex(InstEmitter emitter, ulong address, long opCode) : base(emitter, address, opCode)
+ {
+ HasDepthCompare = opCode.Extract(50);
+
+ HasOffset = opCode.Extract(54);
+ }
+ }
+} \ No newline at end of file
diff --git a/Ryujinx.Graphics.Shader/Decoders/OpCodeTexB.cs b/Ryujinx.Graphics.Shader/Decoders/OpCodeTexB.cs
new file mode 100644
index 00000000..b18bf3be
--- /dev/null
+++ b/Ryujinx.Graphics.Shader/Decoders/OpCodeTexB.cs
@@ -0,0 +1,20 @@
+using Ryujinx.Graphics.Shader.Instructions;
+
+namespace Ryujinx.Graphics.Shader.Decoders
+{
+ class OpCodeTexB : OpCodeTex
+ {
+ public OpCodeTexB(InstEmitter emitter, ulong address, long opCode) : base(emitter, address, opCode)
+ {
+ switch (opCode.Extract(37, 3))
+ {
+ case 0: LodMode = TextureLodMode.None; break;
+ case 1: LodMode = TextureLodMode.LodZero; break;
+ case 2: LodMode = TextureLodMode.LodBias; break;
+ case 3: LodMode = TextureLodMode.LodLevel; break;
+ case 6: LodMode = TextureLodMode.LodBiasA; break;
+ case 7: LodMode = TextureLodMode.LodLevelA; break;
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/Ryujinx.Graphics.Shader/Decoders/OpCodeTexs.cs b/Ryujinx.Graphics.Shader/Decoders/OpCodeTexs.cs
new file mode 100644
index 00000000..fb90ccf6
--- /dev/null
+++ b/Ryujinx.Graphics.Shader/Decoders/OpCodeTexs.cs
@@ -0,0 +1,11 @@
+using Ryujinx.Graphics.Shader.Instructions;
+
+namespace Ryujinx.Graphics.Shader.Decoders
+{
+ class OpCodeTexs : OpCodeTextureScalar
+ {
+ public TextureTarget Target => (TextureTarget)RawType;
+
+ public OpCodeTexs(InstEmitter emitter, ulong address, long opCode) : base(emitter, address, opCode) { }
+ }
+} \ No newline at end of file
diff --git a/Ryujinx.Graphics.Shader/Decoders/OpCodeTexture.cs b/Ryujinx.Graphics.Shader/Decoders/OpCodeTexture.cs
new file mode 100644
index 00000000..7a7e8f46
--- /dev/null
+++ b/Ryujinx.Graphics.Shader/Decoders/OpCodeTexture.cs
@@ -0,0 +1,42 @@
+using Ryujinx.Graphics.Shader.Instructions;
+
+namespace Ryujinx.Graphics.Shader.Decoders
+{
+ class OpCodeTexture : OpCode
+ {
+ public Register Rd { get; }
+ public Register Ra { get; }
+ public Register Rb { get; }
+
+ public bool IsArray { get; }
+
+ public TextureDimensions Dimensions { get; }
+
+ public int ComponentMask { get; }
+
+ public int Immediate { get; }
+
+ public TextureLodMode LodMode { get; protected set; }
+
+ public bool HasOffset { get; protected set; }
+ public bool HasDepthCompare { get; protected set; }
+ public bool IsMultisample { get; protected set; }
+
+ public OpCodeTexture(InstEmitter emitter, ulong address, long opCode) : base(emitter, address, opCode)
+ {
+ Rd = new Register(opCode.Extract(0, 8), RegisterType.Gpr);
+ Ra = new Register(opCode.Extract(8, 8), RegisterType.Gpr);
+ Rb = new Register(opCode.Extract(20, 8), RegisterType.Gpr);
+
+ IsArray = opCode.Extract(28);
+
+ Dimensions = (TextureDimensions)opCode.Extract(29, 2);
+
+ ComponentMask = opCode.Extract(31, 4);
+
+ Immediate = opCode.Extract(36, 13);
+
+ LodMode = (TextureLodMode)opCode.Extract(55, 3);
+ }
+ }
+} \ No newline at end of file
diff --git a/Ryujinx.Graphics.Shader/Decoders/OpCodeTextureScalar.cs b/Ryujinx.Graphics.Shader/Decoders/OpCodeTextureScalar.cs
new file mode 100644
index 00000000..1c175e30
--- /dev/null
+++ b/Ryujinx.Graphics.Shader/Decoders/OpCodeTextureScalar.cs
@@ -0,0 +1,62 @@
+// ReSharper disable InconsistentNaming
+using Ryujinx.Graphics.Shader.Instructions;
+
+namespace Ryujinx.Graphics.Shader.Decoders
+{
+ class OpCodeTextureScalar : OpCode
+ {
+#region "Component mask LUT"
+ private const int ____ = 0x0;
+ private const int R___ = 0x1;
+ private const int _G__ = 0x2;
+ private const int RG__ = 0x3;
+ private const int __B_ = 0x4;
+ private const int RGB_ = 0x7;
+ private const int ___A = 0x8;
+ private const int R__A = 0x9;
+ private const int _G_A = 0xa;
+ private const int RG_A = 0xb;
+ private const int __BA = 0xc;
+ private const int R_BA = 0xd;
+ private const int _GBA = 0xe;
+ private const int RGBA = 0xf;
+
+ private static int[,] _maskLut = new int[,]
+ {
+ { R___, _G__, __B_, ___A, RG__, R__A, _G_A, __BA },
+ { RGB_, RG_A, R_BA, _GBA, RGBA, ____, ____, ____ }
+ };
+#endregion
+
+ public Register Rd0 { get; }
+ public Register Ra { get; }
+ public Register Rb { get; }
+ public Register Rd1 { get; }
+
+ public int Immediate { get; }
+
+ public int ComponentMask { get; protected set; }
+
+ protected int RawType;
+
+ public bool IsFp16 { get; }
+
+ public OpCodeTextureScalar(InstEmitter emitter, ulong address, long opCode) : base(emitter, address, opCode)
+ {
+ Rd0 = new Register(opCode.Extract(0, 8), RegisterType.Gpr);
+ Ra = new Register(opCode.Extract(8, 8), RegisterType.Gpr);
+ Rb = new Register(opCode.Extract(20, 8), RegisterType.Gpr);
+ Rd1 = new Register(opCode.Extract(28, 8), RegisterType.Gpr);
+
+ Immediate = opCode.Extract(36, 13);
+
+ int compSel = opCode.Extract(50, 3);
+
+ RawType = opCode.Extract(53, 4);
+
+ IsFp16 = !opCode.Extract(59);
+
+ ComponentMask = _maskLut[Rd1.IsRZ ? 0 : 1, compSel];
+ }
+ }
+} \ No newline at end of file
diff --git a/Ryujinx.Graphics.Shader/Decoders/OpCodeTld.cs b/Ryujinx.Graphics.Shader/Decoders/OpCodeTld.cs
new file mode 100644
index 00000000..61bd900b
--- /dev/null
+++ b/Ryujinx.Graphics.Shader/Decoders/OpCodeTld.cs
@@ -0,0 +1,20 @@
+using Ryujinx.Graphics.Shader.Instructions;
+
+namespace Ryujinx.Graphics.Shader.Decoders
+{
+ class OpCodeTld : OpCodeTexture
+ {
+ public OpCodeTld(InstEmitter emitter, ulong address, long opCode) : base(emitter, address, opCode)
+ {
+ HasOffset = opCode.Extract(35);
+
+ IsMultisample = opCode.Extract(50);
+
+ bool isLL = opCode.Extract(55);
+
+ LodMode = isLL
+ ? TextureLodMode.LodLevel
+ : TextureLodMode.LodZero;
+ }
+ }
+} \ No newline at end of file
diff --git a/Ryujinx.Graphics.Shader/Decoders/OpCodeTld4.cs b/Ryujinx.Graphics.Shader/Decoders/OpCodeTld4.cs
new file mode 100644
index 00000000..485edf93
--- /dev/null
+++ b/Ryujinx.Graphics.Shader/Decoders/OpCodeTld4.cs
@@ -0,0 +1,20 @@
+using Ryujinx.Graphics.Shader.Instructions;
+
+namespace Ryujinx.Graphics.Shader.Decoders
+{
+ class OpCodeTld4 : OpCodeTexture
+ {
+ public TextureGatherOffset Offset { get; }
+
+ public int GatherCompIndex { get; }
+
+ public OpCodeTld4(InstEmitter emitter, ulong address, long opCode) : base(emitter, address, opCode)
+ {
+ HasDepthCompare = opCode.Extract(50);
+
+ Offset = (TextureGatherOffset)opCode.Extract(54, 2);
+
+ GatherCompIndex = opCode.Extract(56, 2);
+ }
+ }
+} \ No newline at end of file
diff --git a/Ryujinx.Graphics.Shader/Decoders/OpCodeTld4s.cs b/Ryujinx.Graphics.Shader/Decoders/OpCodeTld4s.cs
new file mode 100644
index 00000000..7e51a9e5
--- /dev/null
+++ b/Ryujinx.Graphics.Shader/Decoders/OpCodeTld4s.cs
@@ -0,0 +1,22 @@
+using Ryujinx.Graphics.Shader.Instructions;
+
+namespace Ryujinx.Graphics.Shader.Decoders
+{
+ class OpCodeTld4s : OpCodeTextureScalar
+ {
+ public bool HasDepthCompare { get; }
+ public bool HasOffset { get; }
+
+ public int GatherCompIndex { get; }
+
+ public OpCodeTld4s(InstEmitter emitter, ulong address, long opCode) : base(emitter, address, opCode)
+ {
+ HasDepthCompare = opCode.Extract(50);
+ HasOffset = opCode.Extract(51);
+
+ GatherCompIndex = opCode.Extract(52, 2);
+
+ ComponentMask = Rd1.IsRZ ? 3 : 0xf;
+ }
+ }
+} \ No newline at end of file
diff --git a/Ryujinx.Graphics.Shader/Decoders/OpCodeTlds.cs b/Ryujinx.Graphics.Shader/Decoders/OpCodeTlds.cs
new file mode 100644
index 00000000..1e4e943f
--- /dev/null
+++ b/Ryujinx.Graphics.Shader/Decoders/OpCodeTlds.cs
@@ -0,0 +1,11 @@
+using Ryujinx.Graphics.Shader.Instructions;
+
+namespace Ryujinx.Graphics.Shader.Decoders
+{
+ class OpCodeTlds : OpCodeTextureScalar
+ {
+ public TexelLoadTarget Target => (TexelLoadTarget)RawType;
+
+ public OpCodeTlds(InstEmitter emitter, ulong address, long opCode) : base(emitter, address, opCode) { }
+ }
+} \ No newline at end of file
diff --git a/Ryujinx.Graphics.Shader/Decoders/OpCodeVideo.cs b/Ryujinx.Graphics.Shader/Decoders/OpCodeVideo.cs
new file mode 100644
index 00000000..15dcfa98
--- /dev/null
+++ b/Ryujinx.Graphics.Shader/Decoders/OpCodeVideo.cs
@@ -0,0 +1,24 @@
+using Ryujinx.Graphics.Shader.Instructions;
+
+namespace Ryujinx.Graphics.Shader.Decoders
+{
+ class OpCodeVideo : OpCode, IOpCodeRd, IOpCodeRa, IOpCodeRc
+ {
+ public Register Rd { get; }
+ public Register Ra { get; }
+ public Register Rc { get; }
+
+ public bool SetCondCode { get; protected set; }
+ public bool Saturate { get; protected set; }
+
+ public OpCodeVideo(InstEmitter emitter, ulong address, long opCode) : base(emitter, address, opCode)
+ {
+ Rd = new Register(opCode.Extract(0, 8), RegisterType.Gpr);
+ Ra = new Register(opCode.Extract(8, 8), RegisterType.Gpr);
+ Rc = new Register(opCode.Extract(39, 8), RegisterType.Gpr);
+
+ SetCondCode = opCode.Extract(47);
+ Saturate = opCode.Extract(55);
+ }
+ }
+} \ No newline at end of file
diff --git a/Ryujinx.Graphics.Shader/Decoders/Register.cs b/Ryujinx.Graphics.Shader/Decoders/Register.cs
new file mode 100644
index 00000000..30840d8c
--- /dev/null
+++ b/Ryujinx.Graphics.Shader/Decoders/Register.cs
@@ -0,0 +1,36 @@
+using System;
+
+namespace Ryujinx.Graphics.Shader.Decoders
+{
+ struct Register : IEquatable<Register>
+ {
+ public int Index { get; }
+
+ public RegisterType Type { get; }
+
+ public bool IsRZ => Type == RegisterType.Gpr && Index == RegisterConsts.RegisterZeroIndex;
+ public bool IsPT => Type == RegisterType.Predicate && Index == RegisterConsts.PredicateTrueIndex;
+
+ public Register(int index, RegisterType type)
+ {
+ Index = index;
+ Type = type;
+ }
+
+ public override int GetHashCode()
+ {
+ return (ushort)Index | ((ushort)Type << 16);
+ }
+
+ public override bool Equals(object obj)
+ {
+ return obj is Register reg && Equals(reg);
+ }
+
+ public bool Equals(Register other)
+ {
+ return other.Index == Index &&
+ other.Type == Type;
+ }
+ }
+} \ No newline at end of file
diff --git a/Ryujinx.Graphics.Shader/Decoders/RegisterConsts.cs b/Ryujinx.Graphics.Shader/Decoders/RegisterConsts.cs
new file mode 100644
index 00000000..d381f954
--- /dev/null
+++ b/Ryujinx.Graphics.Shader/Decoders/RegisterConsts.cs
@@ -0,0 +1,13 @@
+namespace Ryujinx.Graphics.Shader.Decoders
+{
+ static class RegisterConsts
+ {
+ public const int GprsCount = 255;
+ public const int PredsCount = 7;
+ public const int FlagsCount = 4;
+ public const int TotalCount = GprsCount + PredsCount + FlagsCount;
+
+ public const int RegisterZeroIndex = GprsCount;
+ public const int PredicateTrueIndex = PredsCount;
+ }
+} \ No newline at end of file
diff --git a/Ryujinx.Graphics.Shader/Decoders/RegisterType.cs b/Ryujinx.Graphics.Shader/Decoders/RegisterType.cs
new file mode 100644
index 00000000..648f816a
--- /dev/null
+++ b/Ryujinx.Graphics.Shader/Decoders/RegisterType.cs
@@ -0,0 +1,9 @@
+namespace Ryujinx.Graphics.Shader.Decoders
+{
+ enum RegisterType
+ {
+ Flag,
+ Gpr,
+ Predicate,
+ }
+} \ No newline at end of file
diff --git a/Ryujinx.Graphics.Shader/Decoders/RoundingMode.cs b/Ryujinx.Graphics.Shader/Decoders/RoundingMode.cs
new file mode 100644
index 00000000..13bb08dc
--- /dev/null
+++ b/Ryujinx.Graphics.Shader/Decoders/RoundingMode.cs
@@ -0,0 +1,10 @@
+namespace Ryujinx.Graphics.Shader.Decoders
+{
+ enum RoundingMode
+ {
+ ToNearest = 0,
+ TowardsNegativeInfinity = 1,
+ TowardsPositiveInfinity = 2,
+ TowardsZero = 3
+ }
+} \ No newline at end of file
diff --git a/Ryujinx.Graphics.Shader/Decoders/SystemRegister.cs b/Ryujinx.Graphics.Shader/Decoders/SystemRegister.cs
new file mode 100644
index 00000000..3948c899
--- /dev/null
+++ b/Ryujinx.Graphics.Shader/Decoders/SystemRegister.cs
@@ -0,0 +1,12 @@
+namespace Ryujinx.Graphics.Shader.Decoders
+{
+ enum SystemRegister
+ {
+ ThreadIdX = 0x21,
+ ThreadIdY = 0x22,
+ ThreadIdZ = 0x23,
+ CtaIdX = 0x25,
+ CtaIdY = 0x26,
+ CtaIdZ = 0x27
+ }
+} \ No newline at end of file
diff --git a/Ryujinx.Graphics.Shader/Decoders/TexelLoadTarget.cs b/Ryujinx.Graphics.Shader/Decoders/TexelLoadTarget.cs
new file mode 100644
index 00000000..478cac44
--- /dev/null
+++ b/Ryujinx.Graphics.Shader/Decoders/TexelLoadTarget.cs
@@ -0,0 +1,15 @@
+namespace Ryujinx.Graphics.Shader.Decoders
+{
+ enum TexelLoadTarget
+ {
+ Texture1DLodZero = 0x0,
+ Texture1DLodLevel = 0x1,
+ Texture2DLodZero = 0x2,
+ Texture2DLodZeroOffset = 0x4,
+ Texture2DLodLevel = 0x5,
+ Texture2DLodZeroMultisample = 0x6,
+ Texture3DLodZero = 0x7,
+ Texture2DArrayLodZero = 0x8,
+ Texture2DLodLevelOffset = 0xc
+ }
+} \ No newline at end of file
diff --git a/Ryujinx.Graphics.Shader/Decoders/TextureDimensions.cs b/Ryujinx.Graphics.Shader/Decoders/TextureDimensions.cs
new file mode 100644
index 00000000..dbdf1927
--- /dev/null
+++ b/Ryujinx.Graphics.Shader/Decoders/TextureDimensions.cs
@@ -0,0 +1,10 @@
+namespace Ryujinx.Graphics.Shader.Decoders
+{
+ enum TextureDimensions
+ {
+ Texture1D = 0,
+ Texture2D = 1,
+ Texture3D = 2,
+ TextureCube = 3
+ }
+} \ No newline at end of file
diff --git a/Ryujinx.Graphics.Shader/Decoders/TextureGatherOffset.cs b/Ryujinx.Graphics.Shader/Decoders/TextureGatherOffset.cs
new file mode 100644
index 00000000..4e9ade26
--- /dev/null
+++ b/Ryujinx.Graphics.Shader/Decoders/TextureGatherOffset.cs
@@ -0,0 +1,9 @@
+namespace Ryujinx.Graphics.Shader.Decoders
+{
+ enum TextureGatherOffset
+ {
+ None = 0,
+ Offset = 1,
+ Offsets = 2
+ }
+} \ No newline at end of file
diff --git a/Ryujinx.Graphics.Shader/Decoders/TextureLodMode.cs b/Ryujinx.Graphics.Shader/Decoders/TextureLodMode.cs
new file mode 100644
index 00000000..0cc6f714
--- /dev/null
+++ b/Ryujinx.Graphics.Shader/Decoders/TextureLodMode.cs
@@ -0,0 +1,12 @@
+namespace Ryujinx.Graphics.Shader.Decoders
+{
+ enum TextureLodMode
+ {
+ None = 0,
+ LodZero = 1,
+ LodBias = 2,
+ LodLevel = 3,
+ LodBiasA = 4, //?
+ LodLevelA = 5 //?
+ }
+} \ No newline at end of file
diff --git a/Ryujinx.Graphics.Shader/Decoders/TextureProperty.cs b/Ryujinx.Graphics.Shader/Decoders/TextureProperty.cs
new file mode 100644
index 00000000..ea35b1d1
--- /dev/null
+++ b/Ryujinx.Graphics.Shader/Decoders/TextureProperty.cs
@@ -0,0 +1,13 @@
+namespace Ryujinx.Graphics.Shader.Decoders
+{
+ enum TextureProperty
+ {
+ Dimensions = 0x1,
+ Type = 0x2,
+ SamplePos = 0x5,
+ Filter = 0xa,
+ Lod = 0xc,
+ Wrap = 0xe,
+ BorderColor = 0x10
+ }
+} \ No newline at end of file
diff --git a/Ryujinx.Graphics.Shader/Decoders/TextureTarget.cs b/Ryujinx.Graphics.Shader/Decoders/TextureTarget.cs
new file mode 100644
index 00000000..181a0a0d
--- /dev/null
+++ b/Ryujinx.Graphics.Shader/Decoders/TextureTarget.cs
@@ -0,0 +1,20 @@
+namespace Ryujinx.Graphics.Shader.Decoders
+{
+ enum TextureTarget
+ {
+ Texture1DLodZero = 0x0,
+ Texture2D = 0x1,
+ Texture2DLodZero = 0x2,
+ Texture2DLodLevel = 0x3,
+ Texture2DDepthCompare = 0x4,
+ Texture2DLodLevelDepthCompare = 0x5,
+ Texture2DLodZeroDepthCompare = 0x6,
+ Texture2DArray = 0x7,
+ Texture2DArrayLodZero = 0x8,
+ Texture2DArrayLodZeroDepthCompare = 0x9,
+ Texture3D = 0xa,
+ Texture3DLodZero = 0xb,
+ TextureCube = 0xc,
+ TextureCubeLodLevel = 0xd
+ }
+} \ No newline at end of file
diff --git a/Ryujinx.Graphics.Shader/Decoders/XmadCMode.cs b/Ryujinx.Graphics.Shader/Decoders/XmadCMode.cs
new file mode 100644
index 00000000..949a2ef7
--- /dev/null
+++ b/Ryujinx.Graphics.Shader/Decoders/XmadCMode.cs
@@ -0,0 +1,11 @@
+namespace Ryujinx.Graphics.Shader.Decoders
+{
+ enum XmadCMode
+ {
+ Cfull = 0,
+ Clo = 1,
+ Chi = 2,
+ Csfu = 3,
+ Cbcc = 4
+ }
+} \ No newline at end of file