From 571848536b347a85c85955745a16d4f7b9a0c04a Mon Sep 17 00:00:00 2001 From: ReinUsesLisp Date: Tue, 17 Jul 2018 16:50:00 -0300 Subject: [PATCH] Implement some shader instructions (#252) * Add IADDI32, IADD and SEL shader instructions * Add LOP shader instruction and fix LOP32I pass_b * Add ISET shader instruction * Add IADD3 shader instruction * Address feedback * Fixup OperA in Iadd_I32 --- .../Gal/Shader/ShaderDecodeAlu.cs | 214 +++++++++++++++++- .../Gal/Shader/ShaderDecodeHelper.cs | 5 + .../Gal/Shader/ShaderDecodeMove.cs | 37 +++ .../Gal/Shader/ShaderOpCodeTable.cs | 16 ++ 4 files changed, 269 insertions(+), 3 deletions(-) diff --git a/Ryujinx.Graphics/Gal/Shader/ShaderDecodeAlu.cs b/Ryujinx.Graphics/Gal/Shader/ShaderDecodeAlu.cs index 2e022fbd4..a44073513 100644 --- a/Ryujinx.Graphics/Gal/Shader/ShaderDecodeAlu.cs +++ b/Ryujinx.Graphics/Gal/Shader/ShaderDecodeAlu.cs @@ -144,6 +144,50 @@ namespace Ryujinx.Graphics.Gal.Shader EmitFsetp(Block, OpCode, ShaderOper.RR); } + public static void Iadd_C(ShaderIrBlock Block, long OpCode) + { + EmitIadd(Block, OpCode, ShaderOper.CR); + } + + public static void Iadd_I(ShaderIrBlock Block, long OpCode) + { + EmitIadd(Block, OpCode, ShaderOper.Imm); + } + + public static void Iadd_I32(ShaderIrBlock Block, long OpCode) + { + ShaderIrNode OperA = GetOperGpr8 (OpCode); + ShaderIrNode OperB = GetOperImm32_20(OpCode); + + bool NegA = ((OpCode >> 56) & 1) != 0; + + OperA = GetAluIneg(OperA, NegA); + + ShaderIrOp Op = new ShaderIrOp(ShaderIrInst.Add, OperA, OperB); + + Block.AddNode(GetPredNode(new ShaderIrAsg(GetOperGpr0(OpCode), Op), OpCode)); + } + + public static void Iadd_R(ShaderIrBlock Block, long OpCode) + { + EmitIadd(Block, OpCode, ShaderOper.RR); + } + + public static void Iadd3_C(ShaderIrBlock Block, long OpCode) + { + EmitIadd3(Block, OpCode, ShaderOper.CR); + } + + public static void Iadd3_I(ShaderIrBlock Block, long OpCode) + { + EmitIadd3(Block, OpCode, ShaderOper.Imm); + } + + public static void Iadd3_R(ShaderIrBlock Block, long OpCode) + { + EmitIadd3(Block, OpCode, ShaderOper.RR); + } + public static void Imnmx_C(ShaderIrBlock Block, long OpCode) { EmitImnmx(Block, OpCode, ShaderOper.CR); @@ -184,6 +228,21 @@ namespace Ryujinx.Graphics.Gal.Shader EmitIscadd(Block, OpCode, ShaderOper.RR); } + public static void Iset_C(ShaderIrBlock Block, long OpCode) + { + EmitIset(Block, OpCode, ShaderOper.CR); + } + + public static void Iset_I(ShaderIrBlock Block, long OpCode) + { + EmitIset(Block, OpCode, ShaderOper.Imm); + } + + public static void Iset_R(ShaderIrBlock Block, long OpCode) + { + EmitIset(Block, OpCode, ShaderOper.RR); + } + public static void Isetp_C(ShaderIrBlock Block, long OpCode) { EmitIsetp(Block, OpCode, ShaderOper.CR); @@ -215,13 +274,13 @@ namespace Ryujinx.Graphics.Gal.Shader case 2: Inst = ShaderIrInst.Xor; break; } - ShaderIrNode OperA = GetAluNot(GetOperGpr8(OpCode), InvA); + ShaderIrNode OperB = GetAluNot(GetOperImm32_20(OpCode), InvB); //SubOp == 3 is pass, used by the not instruction //which just moves the inverted register value. if (SubOp < 3) { - ShaderIrNode OperB = GetAluNot(GetOperImm32_20(OpCode), InvB); + ShaderIrNode OperA = GetAluNot(GetOperGpr8(OpCode), InvA); ShaderIrOp Op = new ShaderIrOp(Inst, OperA, OperB); @@ -229,10 +288,25 @@ namespace Ryujinx.Graphics.Gal.Shader } else { - Block.AddNode(GetPredNode(new ShaderIrAsg(GetOperGpr0(OpCode), OperA), OpCode)); + Block.AddNode(GetPredNode(new ShaderIrAsg(GetOperGpr0(OpCode), OperB), OpCode)); } } + public static void Lop_C(ShaderIrBlock Block, long OpCode) + { + EmitLop(Block, OpCode, ShaderOper.CR); + } + + public static void Lop_I(ShaderIrBlock Block, long OpCode) + { + EmitLop(Block, OpCode, ShaderOper.Imm); + } + + public static void Lop_R(ShaderIrBlock Block, long OpCode) + { + EmitLop(Block, OpCode, ShaderOper.RR); + } + public static void Mufu(ShaderIrBlock Block, long OpCode) { int SubOp = (int)(OpCode >> 20) & 0xf; @@ -533,6 +607,92 @@ namespace Ryujinx.Graphics.Gal.Shader Block.AddNode(GetPredNode(new ShaderIrAsg(GetOperGpr0(OpCode), Op), OpCode)); } + private static void EmitIadd(ShaderIrBlock Block, long OpCode, ShaderOper Oper) + { + ShaderIrNode OperA = GetOperGpr8(OpCode); + ShaderIrNode OperB; + + switch (Oper) + { + case ShaderOper.CR: OperB = GetOperCbuf34 (OpCode); break; + case ShaderOper.Imm: OperB = GetOperImm19_20(OpCode); break; + case ShaderOper.RR: OperB = GetOperGpr20 (OpCode); break; + + default: throw new ArgumentException(nameof(Oper)); + } + + bool NegA = ((OpCode >> 49) & 1) != 0; + bool NegB = ((OpCode >> 48) & 1) != 0; + + OperA = GetAluIneg(OperA, NegA); + OperB = GetAluIneg(OperB, NegB); + + ShaderIrOp Op = new ShaderIrOp(ShaderIrInst.Add, OperA, OperB); + + Block.AddNode(GetPredNode(new ShaderIrAsg(GetOperGpr0(OpCode), Op), OpCode)); + } + + private static void EmitIadd3(ShaderIrBlock Block, long OpCode, ShaderOper Oper) + { + int Mode = (int)((OpCode >> 37) & 3); + + bool Neg1 = ((OpCode >> 51) & 1) != 0; + bool Neg2 = ((OpCode >> 50) & 1) != 0; + bool Neg3 = ((OpCode >> 49) & 1) != 0; + + int Height1 = (int)((OpCode >> 35) & 3); + int Height2 = (int)((OpCode >> 33) & 3); + int Height3 = (int)((OpCode >> 31) & 3); + + ShaderIrNode OperB; + + switch (Oper) + { + case ShaderOper.CR: OperB = GetOperCbuf34 (OpCode); break; + case ShaderOper.Imm: OperB = GetOperImm19_20(OpCode); break; + case ShaderOper.RR: OperB = GetOperGpr20 (OpCode); break; + + default: throw new ArgumentException(nameof(Oper)); + } + + ShaderIrNode ApplyHeight(ShaderIrNode Src, int Height) + { + if (Oper != ShaderOper.RR) + { + return Src; + } + + switch (Height) + { + case 0: return Src; + case 1: return new ShaderIrOp(ShaderIrInst.And, Src, new ShaderIrOperImm(0xffff)); + case 2: return new ShaderIrOp(ShaderIrInst.Lsr, Src, new ShaderIrOperImm(16)); + + default: throw new InvalidOperationException(); + } + } + + ShaderIrNode Src1 = GetAluIneg(ApplyHeight(GetOperGpr8(OpCode), Height1), Neg1); + ShaderIrNode Src2 = GetAluIneg(ApplyHeight(OperB, Height2), Neg2); + ShaderIrNode Src3 = GetAluIneg(ApplyHeight(GetOperGpr39(OpCode), Height3), Neg3); + + ShaderIrOp Sum = new ShaderIrOp(ShaderIrInst.Add, Src1, Src2); + + if (Oper == ShaderOper.RR) + { + switch (Mode) + { + case 1: Sum = new ShaderIrOp(ShaderIrInst.Lsr, Sum, new ShaderIrOperImm(16)); break; + case 2: Sum = new ShaderIrOp(ShaderIrInst.Lsl, Sum, new ShaderIrOperImm(16)); break; + } + } + + //Note: Here there should be a "+ 1" when carry flag is set + //but since carry is mostly ignored by other instructions, it's excluded for now + + Block.AddNode(GetPredNode(new ShaderIrAsg(GetOperGpr0(OpCode), new ShaderIrOp(ShaderIrInst.Add, Sum, Src3)), OpCode)); + } + private static void EmitIscadd(ShaderIrBlock Block, long OpCode, ShaderOper Oper) { bool NegB = ((OpCode >> 48) & 1) != 0; @@ -821,6 +981,54 @@ namespace Ryujinx.Graphics.Gal.Shader Block.AddNode(GetPredNode(new ShaderIrAsg(P0Node, Op), OpCode)); } + private static void EmitLop(ShaderIrBlock Block, long OpCode, ShaderOper Oper) + { + int SubOp = (int)(OpCode >> 41) & 3; + + bool InvA = ((OpCode >> 39) & 1) != 0; + bool InvB = ((OpCode >> 40) & 1) != 0; + + ShaderIrInst Inst = 0; + + switch (SubOp) + { + case 0: Inst = ShaderIrInst.And; break; + case 1: Inst = ShaderIrInst.Or; break; + case 2: Inst = ShaderIrInst.Xor; break; + } + + ShaderIrNode OperA = GetAluNot(GetOperGpr8(OpCode), InvA); + ShaderIrNode OperB; + + switch (Oper) + { + case ShaderOper.CR: OperB = GetOperCbuf34 (OpCode); break; + case ShaderOper.Imm: OperB = GetOperImm19_20(OpCode); break; + case ShaderOper.RR: OperB = GetOperGpr20 (OpCode); break; + + default: throw new ArgumentException(nameof(Oper)); + } + + OperB = GetAluNot(OperB, InvB); + + ShaderIrNode Op; + + if (SubOp < 3) + { + Op = new ShaderIrOp(Inst, OperA, OperB); + } + else + { + Op = OperB; + } + + ShaderIrNode Compare = new ShaderIrOp(ShaderIrInst.Cne, Op, new ShaderIrOperImm(0)); + + Block.AddNode(GetPredNode(new ShaderIrAsg(GetOperPred48(OpCode), Compare), OpCode)); + + Block.AddNode(GetPredNode(new ShaderIrAsg(GetOperGpr0(OpCode), Op), OpCode)); + } + private static void EmitXmad(ShaderIrBlock Block, long OpCode, ShaderOper Oper) { //TODO: Confirm SignAB/C, it is just a guess. diff --git a/Ryujinx.Graphics/Gal/Shader/ShaderDecodeHelper.cs b/Ryujinx.Graphics/Gal/Shader/ShaderDecodeHelper.cs index a8ad5ec2e..1f1b158ef 100644 --- a/Ryujinx.Graphics/Gal/Shader/ShaderDecodeHelper.cs +++ b/Ryujinx.Graphics/Gal/Shader/ShaderDecodeHelper.cs @@ -156,6 +156,11 @@ namespace Ryujinx.Graphics.Gal.Shader return new ShaderIrOperPred((int)(OpCode >> 39) & 7); } + public static ShaderIrOperPred GetOperPred48(long OpCode) + { + return new ShaderIrOperPred((int)((OpCode >> 48) & 7)); + } + public static ShaderIrInst GetCmp(long OpCode) { switch ((int)(OpCode >> 49) & 7) diff --git a/Ryujinx.Graphics/Gal/Shader/ShaderDecodeMove.cs b/Ryujinx.Graphics/Gal/Shader/ShaderDecodeMove.cs index dfcea905a..4c9e59cf0 100644 --- a/Ryujinx.Graphics/Gal/Shader/ShaderDecodeMove.cs +++ b/Ryujinx.Graphics/Gal/Shader/ShaderDecodeMove.cs @@ -113,6 +113,21 @@ namespace Ryujinx.Graphics.Gal.Shader Block.AddNode(GetPredNode(new ShaderIrAsg(GetOperGpr0(OpCode), Gpr), OpCode)); } + public static void Sel_C(ShaderIrBlock Block, long OpCode) + { + EmitSel(Block, OpCode, ShaderOper.CR); + } + + public static void Sel_I(ShaderIrBlock Block, long OpCode) + { + EmitSel(Block, OpCode, ShaderOper.Imm); + } + + public static void Sel_R(ShaderIrBlock Block, long OpCode) + { + EmitSel(Block, OpCode, ShaderOper.RR); + } + private static void EmitF2f(ShaderIrBlock Block, long OpCode, ShaderOper Oper) { bool NegA = ((OpCode >> 45) & 1) != 0; @@ -340,6 +355,28 @@ namespace Ryujinx.Graphics.Gal.Shader Block.AddNode(GetPredNode(new ShaderIrAsg(GetOperGpr0(OpCode), OperA), OpCode)); } + private static void EmitSel(ShaderIrBlock Block, long OpCode, ShaderOper Oper) + { + ShaderIrOperGpr Dst = GetOperGpr0 (OpCode); + ShaderIrNode Pred = GetOperPred39N(OpCode); + + ShaderIrNode ResultA = GetOperGpr8(OpCode); + ShaderIrNode ResultB; + + switch (Oper) + { + case ShaderOper.CR: ResultB = GetOperCbuf34 (OpCode); break; + case ShaderOper.Imm: ResultB = GetOperImm19_20(OpCode); break; + case ShaderOper.RR: ResultB = GetOperGpr20 (OpCode); break; + + default: throw new ArgumentException(nameof(Oper)); + } + + Block.AddNode(GetPredNode(new ShaderIrCond(Pred, new ShaderIrAsg(Dst, ResultA), false), OpCode)); + + Block.AddNode(GetPredNode(new ShaderIrCond(Pred, new ShaderIrAsg(Dst, ResultB), true), OpCode)); + } + private static IntType GetIntType(long OpCode) { bool Signed = ((OpCode >> 13) & 1) != 0; diff --git a/Ryujinx.Graphics/Gal/Shader/ShaderOpCodeTable.cs b/Ryujinx.Graphics/Gal/Shader/ShaderOpCodeTable.cs index b4f51e508..1ac117851 100644 --- a/Ryujinx.Graphics/Gal/Shader/ShaderOpCodeTable.cs +++ b/Ryujinx.Graphics/Gal/Shader/ShaderOpCodeTable.cs @@ -64,6 +64,13 @@ namespace Ryujinx.Graphics.Gal.Shader Set("0100110011100x", ShaderDecode.I2i_C); Set("0011100x11100x", ShaderDecode.I2i_I); Set("0101110011100x", ShaderDecode.I2i_R); + Set("0100110000010x", ShaderDecode.Iadd_C); + Set("0011100000010x", ShaderDecode.Iadd_I); + Set("0001110x0xxxxx", ShaderDecode.Iadd_I32); + Set("0101110000010x", ShaderDecode.Iadd_R); + Set("010011001100xx", ShaderDecode.Iadd3_C); + Set("001110001100xx", ShaderDecode.Iadd3_I); + Set("010111001100xx", ShaderDecode.Iadd3_R); Set("0100110000100x", ShaderDecode.Imnmx_C); Set("0011100x00100x", ShaderDecode.Imnmx_I); Set("0101110000100x", ShaderDecode.Imnmx_R); @@ -71,13 +78,19 @@ namespace Ryujinx.Graphics.Gal.Shader Set("0100110000011x", ShaderDecode.Iscadd_C); Set("0011100x00011x", ShaderDecode.Iscadd_I); Set("0101110000011x", ShaderDecode.Iscadd_R); + Set("010010110101xx", ShaderDecode.Iset_C); + Set("001101100101xx", ShaderDecode.Iset_I); + Set("010110110101xx", ShaderDecode.Iset_R); Set("010010110110xx", ShaderDecode.Isetp_C); Set("0011011x0110xx", ShaderDecode.Isetp_I); Set("010110110110xx", ShaderDecode.Isetp_R); Set("111000110011xx", ShaderDecode.Kil); Set("1110111111011x", ShaderDecode.Ld_A); Set("1110111110010x", ShaderDecode.Ld_C); + Set("0100110001000x", ShaderDecode.Lop_C); + Set("0011100001000x", ShaderDecode.Lop_I); Set("000001xxxxxxxx", ShaderDecode.Lop_I32); + Set("0101110001000x", ShaderDecode.Lop_R); Set("0100110010011x", ShaderDecode.Mov_C); Set("0011100x10011x", ShaderDecode.Mov_I); Set("000000010000xx", ShaderDecode.Mov_I32); @@ -87,6 +100,9 @@ namespace Ryujinx.Graphics.Gal.Shader Set("0100110010010x", ShaderDecode.Rro_C); Set("0011100x10010x", ShaderDecode.Rro_I); Set("0101110010010x", ShaderDecode.Rro_R); + Set("0100110010100x", ShaderDecode.Sel_C); + Set("0011100010100x", ShaderDecode.Sel_I); + Set("0101110010100x", ShaderDecode.Sel_R); Set("0100110001001x", ShaderDecode.Shl_C); Set("0011100x01001x", ShaderDecode.Shl_I); Set("0101110001001x", ShaderDecode.Shl_R);