diff --git a/Ryujinx.Graphics/Gal/Shader/ShaderDecode.cs b/Ryujinx.Graphics/Gal/Shader/ShaderDecode.cs new file mode 100644 index 0000000000..ef0fd78bd3 --- /dev/null +++ b/Ryujinx.Graphics/Gal/Shader/ShaderDecode.cs @@ -0,0 +1,4 @@ +namespace Ryujinx.Graphics.Gal.Shader +{ + delegate void ShaderDecodeFunc(ShaderIrBlock Block, long OpCode); +} \ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/Shader/ShaderDecodeAlu.cs b/Ryujinx.Graphics/Gal/Shader/ShaderDecodeAlu.cs new file mode 100644 index 0000000000..66fae3127d --- /dev/null +++ b/Ryujinx.Graphics/Gal/Shader/ShaderDecodeAlu.cs @@ -0,0 +1,83 @@ +namespace Ryujinx.Graphics.Gal.Shader +{ + static partial class ShaderDecode + { + public static void Fadd(ShaderIrBlock Block, long OpCode) + { + int Rd = (int)(OpCode >> 0) & 0xff; + int Ra = (int)(OpCode >> 8) & 0xff; + int Ob = (int)(OpCode >> 20) & 0x3fff; + int Cb = (int)(OpCode >> 34) & 0x1f; + + 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 Ad = ((OpCode >> 50) & 1) != 0; + + Block.AddNode(new ShaderIrNodeLdr(Ra)); + + if (Aa) + { + Block.AddNode(new ShaderIrNode(ShaderIrInst.Fabs)); + } + + if (Na) + { + Block.AddNode(new ShaderIrNode(ShaderIrInst.Fneg)); + } + + Block.AddNode(new ShaderIrNodeLdb(Cb, Ob)); + + if (Ab) + { + Block.AddNode(new ShaderIrNode(ShaderIrInst.Fabs)); + } + + if (Nb) + { + Block.AddNode(new ShaderIrNode(ShaderIrInst.Fneg)); + } + + Block.AddNode(new ShaderIrNode(ShaderIrInst.Fadd)); + + if (Ad) + { + Block.AddNode(new ShaderIrNode(ShaderIrInst.Fabs)); + } + + Block.AddNode(new ShaderIrNodeStr(Rd)); + } + + public static void Ffma(ShaderIrBlock Block, long OpCode) + { + int Rd = (int)(OpCode >> 0) & 0xff; + int Ra = (int)(OpCode >> 8) & 0xff; + int Ob = (int)(OpCode >> 20) & 0x3fff; + int Cb = (int)(OpCode >> 34) & 0x1f; + int Rc = (int)(OpCode >> 39) & 0xff; + + bool Nb = ((OpCode >> 48) & 1) != 0; + bool Nc = ((OpCode >> 49) & 1) != 0; + + Block.AddNode(new ShaderIrNodeLdr(Ra)); + Block.AddNode(new ShaderIrNodeLdb(Cb, Ob)); + + if (Nb) + { + Block.AddNode(new ShaderIrNode(ShaderIrInst.Fneg)); + } + + Block.AddNode(new ShaderIrNode(ShaderIrInst.Fmul)); + Block.AddNode(new ShaderIrNodeLdr(Rc)); + + if (Nc) + { + Block.AddNode(new ShaderIrNode(ShaderIrInst.Fneg)); + } + + Block.AddNode(new ShaderIrNode(ShaderIrInst.Fadd)); + Block.AddNode(new ShaderIrNodeStr(Rd)); + } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/Shader/ShaderDecoder.cs b/Ryujinx.Graphics/Gal/Shader/ShaderDecoder.cs new file mode 100644 index 0000000000..cbf89a7c65 --- /dev/null +++ b/Ryujinx.Graphics/Gal/Shader/ShaderDecoder.cs @@ -0,0 +1,29 @@ +namespace Ryujinx.Graphics.Gal.Shader +{ + static class ShaderDecoder + { + public static ShaderIrBlock DecodeBasicBlock(int[] Code, int Offset) + { + ShaderIrBlock Block = new ShaderIrBlock(); + + while (Offset + 2 <= Code.Length) + { + uint Word0 = (uint)Code[Offset++]; + uint Word1 = (uint)Code[Offset++]; + + long OpCode = Word0 | (long)Word1 << 32; + + ShaderDecodeFunc Decode = ShaderOpCodeTable.GetDecoder(OpCode); + + if (Decode == null) + { + continue; + } + + Decode(Block, OpCode); + } + + return Block; + } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/Shader/ShaderIrBlock.cs b/Ryujinx.Graphics/Gal/Shader/ShaderIrBlock.cs new file mode 100644 index 0000000000..c4a2c335fc --- /dev/null +++ b/Ryujinx.Graphics/Gal/Shader/ShaderIrBlock.cs @@ -0,0 +1,24 @@ +using System.Collections.Generic; + +namespace Ryujinx.Graphics.Gal.Shader +{ + class ShaderIrBlock + { + private List Nodes; + + public ShaderIrBlock() + { + Nodes = new List(); + } + + public void AddNode(ShaderIrNode Node) + { + Nodes.Add(Node); + } + + public ShaderIrNode[] GetNodes() + { + return Nodes.ToArray(); + } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/Shader/ShaderIrInst.cs b/Ryujinx.Graphics/Gal/Shader/ShaderIrInst.cs new file mode 100644 index 0000000000..665c3db3f2 --- /dev/null +++ b/Ryujinx.Graphics/Gal/Shader/ShaderIrInst.cs @@ -0,0 +1,12 @@ +namespace Ryujinx.Graphics.Gal.Shader +{ + enum ShaderIrInst + { + Fabs, + Fadd, + Fmul, + Fneg, + Ld, + St + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/Shader/ShaderIrNode.cs b/Ryujinx.Graphics/Gal/Shader/ShaderIrNode.cs new file mode 100644 index 0000000000..6d97b3c525 --- /dev/null +++ b/Ryujinx.Graphics/Gal/Shader/ShaderIrNode.cs @@ -0,0 +1,12 @@ +namespace Ryujinx.Graphics.Gal.Shader +{ + class ShaderIrNode + { + public ShaderIrInst Inst; + + public ShaderIrNode(ShaderIrInst Inst) + { + this.Inst = Inst; + } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/Shader/ShaderIrNodeLdb.cs b/Ryujinx.Graphics/Gal/Shader/ShaderIrNodeLdb.cs new file mode 100644 index 0000000000..6561792d85 --- /dev/null +++ b/Ryujinx.Graphics/Gal/Shader/ShaderIrNodeLdb.cs @@ -0,0 +1,14 @@ +namespace Ryujinx.Graphics.Gal.Shader +{ + class ShaderIrNodeLdb : ShaderIrNode + { + public int Cbuf { get; private set; } + public int Offs { get; private set; } + + public ShaderIrNodeLdb(int Cbuf, int Offs) : base(ShaderIrInst.Ld) + { + this.Cbuf = Cbuf; + this.Offs = Offs; + } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/Shader/ShaderIrNodeLdr.cs b/Ryujinx.Graphics/Gal/Shader/ShaderIrNodeLdr.cs new file mode 100644 index 0000000000..e19b325e4a --- /dev/null +++ b/Ryujinx.Graphics/Gal/Shader/ShaderIrNodeLdr.cs @@ -0,0 +1,12 @@ +namespace Ryujinx.Graphics.Gal.Shader +{ + class ShaderIrNodeLdr : ShaderIrNode + { + public int GprIndex { get; private set; } + + public ShaderIrNodeLdr(int GprIndex) : base(ShaderIrInst.Ld) + { + this.GprIndex = GprIndex; + } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/Shader/ShaderIrNodeStr.cs b/Ryujinx.Graphics/Gal/Shader/ShaderIrNodeStr.cs new file mode 100644 index 0000000000..31c396acc4 --- /dev/null +++ b/Ryujinx.Graphics/Gal/Shader/ShaderIrNodeStr.cs @@ -0,0 +1,12 @@ +namespace Ryujinx.Graphics.Gal.Shader +{ + class ShaderIrNodeStr : ShaderIrNode + { + public int GprIndex { get; private set; } + + public ShaderIrNodeStr(int GprIndex) : base(ShaderIrInst.St) + { + this.GprIndex = GprIndex; + } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/Shader/ShaderIrOper.cs b/Ryujinx.Graphics/Gal/Shader/ShaderIrOper.cs new file mode 100644 index 0000000000..df34955263 --- /dev/null +++ b/Ryujinx.Graphics/Gal/Shader/ShaderIrOper.cs @@ -0,0 +1,4 @@ +namespace Ryujinx.Graphics.Gal.Shader +{ + class ShaderIrOper { } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/Shader/ShaderIrOperReg.cs b/Ryujinx.Graphics/Gal/Shader/ShaderIrOperReg.cs new file mode 100644 index 0000000000..24f9aa3598 --- /dev/null +++ b/Ryujinx.Graphics/Gal/Shader/ShaderIrOperReg.cs @@ -0,0 +1,12 @@ +namespace Ryujinx.Graphics.Gal.Shader +{ + class ShaderIrOperReg : ShaderIrOper + { + public int GprIndex { get; private set; } + + public ShaderIrOperReg(int GprIndex) + { + this.GprIndex = GprIndex; + } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/Shader/ShaderOpCodeTable.cs b/Ryujinx.Graphics/Gal/Shader/ShaderOpCodeTable.cs new file mode 100644 index 0000000000..ff5c2271da --- /dev/null +++ b/Ryujinx.Graphics/Gal/Shader/ShaderOpCodeTable.cs @@ -0,0 +1,71 @@ +using System; + +namespace Ryujinx.Graphics.Gal.Shader +{ + static class ShaderOpCodeTable + { + private const int EncodingBits = 14; + + private static ShaderDecodeFunc[] OpCodes; + + static ShaderOpCodeTable() + { + OpCodes = new ShaderDecodeFunc[1 << EncodingBits]; + +#region Instructions + Set("0100110001011x", ShaderDecode.Fadd); + Set("010010011xxxxx", ShaderDecode.Ffma); +#endregion + } + + private static void Set(string Encoding, ShaderDecodeFunc Func) + { + 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; + + for (int Index = 0; Index < (1 << XBits); Index++) + { + Value &= XMask; + + for (int X = 0; X < XBits; X++) + { + Value |= ((Index >> X) & 1) << XPos[X]; + } + + OpCodes[Value] = Func; + } + } + + public static ShaderDecodeFunc GetDecoder(long OpCode) + { + return OpCodes[(ulong)OpCode >> (64 - EncodingBits)]; + } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/Shader/ShaderTest.cs b/Ryujinx.Graphics/Gal/Shader/ShaderTest.cs new file mode 100644 index 0000000000..8c2bd6403c --- /dev/null +++ b/Ryujinx.Graphics/Gal/Shader/ShaderTest.cs @@ -0,0 +1,50 @@ +namespace Ryujinx.Graphics.Gal.Shader +{ + public static class ShaderTest + { + public static void Test() + { + System.Console.WriteLine("Starting test code..."); + + System.Collections.Generic.List CodeList = new System.Collections.Generic.List(); + + using (System.IO.FileStream FS = new System.IO.FileStream("D:\\puyo_vsh.bin", System.IO.FileMode.Open)) + { + System.IO.BinaryReader Reader = new System.IO.BinaryReader(FS); + + while (FS.Position + 8 <= FS.Length) + { + CodeList.Add(Reader.ReadInt32()); + } + } + + int[] Code = CodeList.ToArray(); + + ShaderIrBlock Block = ShaderDecoder.DecodeBasicBlock(Code, 0); + + ShaderIrNode[] Nodes = Block.GetNodes(); + + foreach (ShaderIrNode Node in Nodes) + { + System.Console.Write(Node.Inst); + + if (Node is ShaderIrNodeLdr LdrNode) + { + System.Console.Write($" r{LdrNode.GprIndex}"); + } + else if (Node is ShaderIrNodeStr StrNode) + { + System.Console.Write($" r{StrNode.GprIndex}"); + } + else if (Node is ShaderIrNodeLdb LdbNode) + { + System.Console.Write($" c{LdbNode.Cbuf}[0x{LdbNode.Offs.ToString("x")}]"); + } + + System.Console.WriteLine(string.Empty); + } + + System.Console.WriteLine("Test code finished!"); + } + } +} \ No newline at end of file