From 4518c52c65f64a5f7be8866a299056fdf94ef860 Mon Sep 17 00:00:00 2001 From: LDj3SNuD <35856442+LDj3SNuD@users.noreply.github.com> Date: Mon, 13 Aug 2018 23:10:02 +0200 Subject: [PATCH 1/5] Add Sadalp_V, Saddlp_V, Uadalp_V, Uaddlp_V instructions; add 8 Tests. (#340) * Update Instructions.cs * Update CpuTestSimd.cs * Update AOpCodeTable.cs * Update AInstEmitSimdArithmetic.cs --- ChocolArm64/AOpCodeTable.cs | 4 + .../Instruction/AInstEmitSimdArithmetic.cs | 55 +++++ Ryujinx.Tests/Cpu/CpuTestSimd.cs | 216 ++++++++++++++++++ Ryujinx.Tests/Cpu/Tester/Instructions.cs | 168 ++++++++++++++ 4 files changed, 443 insertions(+) diff --git a/ChocolArm64/AOpCodeTable.cs b/ChocolArm64/AOpCodeTable.cs index 74d4915b9..e50f3f987 100644 --- a/ChocolArm64/AOpCodeTable.cs +++ b/ChocolArm64/AOpCodeTable.cs @@ -360,6 +360,8 @@ namespace ChocolArm64 SetA64("0x001110<<1xxxxx010100xxxxxxxxxx", AInstEmit.Sabal_V, typeof(AOpCodeSimdReg)); SetA64("0x001110<<1xxxxx011101xxxxxxxxxx", AInstEmit.Sabd_V, typeof(AOpCodeSimdReg)); SetA64("0x001110<<1xxxxx011100xxxxxxxxxx", AInstEmit.Sabdl_V, typeof(AOpCodeSimdReg)); + SetA64("0x001110<<100000011010xxxxxxxxxx", AInstEmit.Sadalp_V, typeof(AOpCodeSimd)); + SetA64("0x001110<<100000001010xxxxxxxxxx", AInstEmit.Saddlp_V, typeof(AOpCodeSimd)); SetA64("0x001110<<1xxxxx000100xxxxxxxxxx", AInstEmit.Saddw_V, typeof(AOpCodeSimdReg)); SetA64("x0011110xx100010000000xxxxxxxxxx", AInstEmit.Scvtf_Gp, typeof(AOpCodeSimdCvt)); SetA64("010111100x100001110110xxxxxxxxxx", AInstEmit.Scvtf_S, typeof(AOpCodeSimd)); @@ -429,7 +431,9 @@ namespace ChocolArm64 SetA64("0x101110<<1xxxxx010100xxxxxxxxxx", AInstEmit.Uabal_V, typeof(AOpCodeSimdReg)); SetA64("0x101110<<1xxxxx011101xxxxxxxxxx", AInstEmit.Uabd_V, typeof(AOpCodeSimdReg)); SetA64("0x101110<<1xxxxx011100xxxxxxxxxx", AInstEmit.Uabdl_V, typeof(AOpCodeSimdReg)); + SetA64("0x101110<<100000011010xxxxxxxxxx", AInstEmit.Uadalp_V, typeof(AOpCodeSimd)); SetA64("0x101110<<1xxxxx000000xxxxxxxxxx", AInstEmit.Uaddl_V, typeof(AOpCodeSimdReg)); + SetA64("0x101110<<100000001010xxxxxxxxxx", AInstEmit.Uaddlp_V, typeof(AOpCodeSimd)); SetA64("001011100x110000001110xxxxxxxxxx", AInstEmit.Uaddlv_V, typeof(AOpCodeSimd)); SetA64("01101110<<110000001110xxxxxxxxxx", AInstEmit.Uaddlv_V, typeof(AOpCodeSimd)); SetA64("0x101110<<1xxxxx000100xxxxxxxxxx", AInstEmit.Uaddw_V, typeof(AOpCodeSimdReg)); diff --git a/ChocolArm64/Instruction/AInstEmitSimdArithmetic.cs b/ChocolArm64/Instruction/AInstEmitSimdArithmetic.cs index 02e903f6f..1d7b16dd9 100644 --- a/ChocolArm64/Instruction/AInstEmitSimdArithmetic.cs +++ b/ChocolArm64/Instruction/AInstEmitSimdArithmetic.cs @@ -158,6 +158,41 @@ namespace ChocolArm64.Instruction Context.MarkLabel(LblTrue); } + private static void EmitAddLongPairwise(AILEmitterCtx Context, bool Signed, bool Accumulate) + { + AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp; + + int Words = Op.GetBitsCount() >> 4; + int Pairs = Words >> Op.Size; + + for (int Index = 0; Index < Pairs; Index++) + { + int Idx = Index << 1; + + EmitVectorExtract(Context, Op.Rn, Idx, Op.Size, Signed); + EmitVectorExtract(Context, Op.Rn, Idx + 1, Op.Size, Signed); + + Context.Emit(OpCodes.Add); + + if (Accumulate) + { + EmitVectorExtract(Context, Op.Rd, Index, Op.Size + 1, Signed); + + Context.Emit(OpCodes.Add); + } + + EmitVectorInsertTmp(Context, Index, Op.Size + 1); + } + + Context.EmitLdvectmp(); + Context.EmitStvec(Op.Rd); + + if (Op.RegisterSize == ARegisterSize.SIMD64) + { + EmitVectorZeroUpper(Context, Op.Rd); + } + } + private static void EmitDoublingMultiplyHighHalf(AILEmitterCtx Context, bool Round) { AOpCodeSimdReg Op = (AOpCodeSimdReg)Context.CurrOp; @@ -992,6 +1027,16 @@ namespace ChocolArm64.Instruction }); } + public static void Sadalp_V(AILEmitterCtx Context) + { + EmitAddLongPairwise(Context, Signed: true, Accumulate: true); + } + + public static void Saddlp_V(AILEmitterCtx Context) + { + EmitAddLongPairwise(Context, Signed: true, Accumulate: false); + } + public static void Saddw_V(AILEmitterCtx Context) { EmitVectorWidenRmBinaryOpSx(Context, () => Context.Emit(OpCodes.Add)); @@ -1213,11 +1258,21 @@ namespace ChocolArm64.Instruction }); } + public static void Uadalp_V(AILEmitterCtx Context) + { + EmitAddLongPairwise(Context, Signed: false, Accumulate: true); + } + public static void Uaddl_V(AILEmitterCtx Context) { EmitVectorWidenRnRmBinaryOpZx(Context, () => Context.Emit(OpCodes.Add)); } + public static void Uaddlp_V(AILEmitterCtx Context) + { + EmitAddLongPairwise(Context, Signed: false, Accumulate: false); + } + public static void Uaddlv_V(AILEmitterCtx Context) { AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp; diff --git a/Ryujinx.Tests/Cpu/CpuTestSimd.cs b/Ryujinx.Tests/Cpu/CpuTestSimd.cs index bb60273a7..15162c8ed 100644 --- a/Ryujinx.Tests/Cpu/CpuTestSimd.cs +++ b/Ryujinx.Tests/Cpu/CpuTestSimd.cs @@ -1137,6 +1137,114 @@ namespace Ryujinx.Tests.Cpu }); } + [Test, Description("SADALP ., .")] + public void Sadalp_V_8B4H_4H2S_2S1D([Values(0u)] uint Rd, + [Values(1u, 0u)] uint Rn, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong Z, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong A, + [Values(0b00u, 0b01u, 0b10u)] uint size) // <8B4H, 4H2S, 2S1D> + { + uint Opcode = 0x0E206800; // SADALP V0.4H, V0.8B + Opcode |= ((Rn & 31) << 5) | ((Rd & 31) << 0); + Opcode |= ((size & 3) << 22); + Bits Op = new Bits(Opcode); + + Vector128 V0 = MakeVectorE0E1(Z, Z); + Vector128 V1 = MakeVectorE0(A); + AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1); + + AArch64.Vpart(0, 0, new Bits(Z)); AArch64.Vpart(0, 1, new Bits(Z)); + AArch64.V(1, new Bits(A)); + SimdFp.Sadalp_V(Op[30], Op[23, 22], Op[9, 5], Op[4, 0]); + + Assert.Multiple(() => + { + Assert.That(GetVectorE0(ThreadState.V0), Is.EqualTo(AArch64.Vpart(64, 0, 0).ToUInt64())); + Assert.That(GetVectorE1(ThreadState.V0), Is.EqualTo(AArch64.Vpart(64, 0, 1).ToUInt64())); + }); + } + + [Test, Description("SADALP ., .")] + public void Sadalp_V_16B8H_8H4S_4S2D([Values(0u)] uint Rd, + [Values(1u, 0u)] uint Rn, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong Z, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong A, + [Values(0b00u, 0b01u, 0b10u)] uint size) // <16B8H, 8H4S, 4S2D> + { + uint Opcode = 0x4E206800; // SADALP V0.8H, V0.16B + Opcode |= ((Rn & 31) << 5) | ((Rd & 31) << 0); + Opcode |= ((size & 3) << 22); + Bits Op = new Bits(Opcode); + + Vector128 V0 = MakeVectorE0E1(Z, Z); + Vector128 V1 = MakeVectorE0E1(A, A); + AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1); + + AArch64.Vpart(0, 0, new Bits(Z)); AArch64.Vpart(0, 1, new Bits(Z)); + AArch64.Vpart(1, 0, new Bits(A)); AArch64.Vpart(1, 1, new Bits(A)); + SimdFp.Sadalp_V(Op[30], Op[23, 22], Op[9, 5], Op[4, 0]); + + Assert.Multiple(() => + { + Assert.That(GetVectorE0(ThreadState.V0), Is.EqualTo(AArch64.Vpart(64, 0, 0).ToUInt64())); + Assert.That(GetVectorE1(ThreadState.V0), Is.EqualTo(AArch64.Vpart(64, 0, 1).ToUInt64())); + }); + } + + [Test, Description("SADDLP ., .")] + public void Saddlp_V_8B4H_4H2S_2S1D([Values(0u)] uint Rd, + [Values(1u, 0u)] uint Rn, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong Z, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong A, + [Values(0b00u, 0b01u, 0b10u)] uint size) // <8B4H, 4H2S, 2S1D> + { + uint Opcode = 0x0E202800; // SADDLP V0.4H, V0.8B + Opcode |= ((Rn & 31) << 5) | ((Rd & 31) << 0); + Opcode |= ((size & 3) << 22); + Bits Op = new Bits(Opcode); + + Vector128 V0 = MakeVectorE0E1(Z, Z); + Vector128 V1 = MakeVectorE0(A); + AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1); + + AArch64.Vpart(0, 0, new Bits(Z)); AArch64.Vpart(0, 1, new Bits(Z)); + AArch64.V(1, new Bits(A)); + SimdFp.Saddlp_V(Op[30], Op[23, 22], Op[9, 5], Op[4, 0]); + + Assert.Multiple(() => + { + Assert.That(GetVectorE0(ThreadState.V0), Is.EqualTo(AArch64.Vpart(64, 0, 0).ToUInt64())); + Assert.That(GetVectorE1(ThreadState.V0), Is.EqualTo(AArch64.Vpart(64, 0, 1).ToUInt64())); + }); + } + + [Test, Description("SADDLP ., .")] + public void Saddlp_V_16B8H_8H4S_4S2D([Values(0u)] uint Rd, + [Values(1u, 0u)] uint Rn, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong Z, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong A, + [Values(0b00u, 0b01u, 0b10u)] uint size) // <16B8H, 8H4S, 4S2D> + { + uint Opcode = 0x4E202800; // SADDLP V0.8H, V0.16B + Opcode |= ((Rn & 31) << 5) | ((Rd & 31) << 0); + Opcode |= ((size & 3) << 22); + Bits Op = new Bits(Opcode); + + Vector128 V0 = MakeVectorE0E1(Z, Z); + Vector128 V1 = MakeVectorE0E1(A, A); + AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1); + + AArch64.Vpart(0, 0, new Bits(Z)); AArch64.Vpart(0, 1, new Bits(Z)); + AArch64.Vpart(1, 0, new Bits(A)); AArch64.Vpart(1, 1, new Bits(A)); + SimdFp.Saddlp_V(Op[30], Op[23, 22], Op[9, 5], Op[4, 0]); + + Assert.Multiple(() => + { + Assert.That(GetVectorE0(ThreadState.V0), Is.EqualTo(AArch64.Vpart(64, 0, 0).ToUInt64())); + Assert.That(GetVectorE1(ThreadState.V0), Is.EqualTo(AArch64.Vpart(64, 0, 1).ToUInt64())); + }); + } + [Test, Description("SQABS , ")] public void Sqabs_S_B_H_S_D([Values(0u)] uint Rd, [Values(1u, 0u)] uint Rn, @@ -1602,6 +1710,114 @@ namespace Ryujinx.Tests.Cpu Assert.That(ThreadState.Fpsr, Is.EqualTo((int)Shared.FPSR.ToUInt32())); } + [Test, Description("UADALP ., .")] + public void Uadalp_V_8B4H_4H2S_2S1D([Values(0u)] uint Rd, + [Values(1u, 0u)] uint Rn, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong Z, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong A, + [Values(0b00u, 0b01u, 0b10u)] uint size) // <8B4H, 4H2S, 2S1D> + { + uint Opcode = 0x2E206800; // UADALP V0.4H, V0.8B + Opcode |= ((Rn & 31) << 5) | ((Rd & 31) << 0); + Opcode |= ((size & 3) << 22); + Bits Op = new Bits(Opcode); + + Vector128 V0 = MakeVectorE0E1(Z, Z); + Vector128 V1 = MakeVectorE0(A); + AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1); + + AArch64.Vpart(0, 0, new Bits(Z)); AArch64.Vpart(0, 1, new Bits(Z)); + AArch64.V(1, new Bits(A)); + SimdFp.Uadalp_V(Op[30], Op[23, 22], Op[9, 5], Op[4, 0]); + + Assert.Multiple(() => + { + Assert.That(GetVectorE0(ThreadState.V0), Is.EqualTo(AArch64.Vpart(64, 0, 0).ToUInt64())); + Assert.That(GetVectorE1(ThreadState.V0), Is.EqualTo(AArch64.Vpart(64, 0, 1).ToUInt64())); + }); + } + + [Test, Description("UADALP ., .")] + public void Uadalp_V_16B8H_8H4S_4S2D([Values(0u)] uint Rd, + [Values(1u, 0u)] uint Rn, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong Z, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong A, + [Values(0b00u, 0b01u, 0b10u)] uint size) // <16B8H, 8H4S, 4S2D> + { + uint Opcode = 0x6E206800; // UADALP V0.8H, V0.16B + Opcode |= ((Rn & 31) << 5) | ((Rd & 31) << 0); + Opcode |= ((size & 3) << 22); + Bits Op = new Bits(Opcode); + + Vector128 V0 = MakeVectorE0E1(Z, Z); + Vector128 V1 = MakeVectorE0E1(A, A); + AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1); + + AArch64.Vpart(0, 0, new Bits(Z)); AArch64.Vpart(0, 1, new Bits(Z)); + AArch64.Vpart(1, 0, new Bits(A)); AArch64.Vpart(1, 1, new Bits(A)); + SimdFp.Uadalp_V(Op[30], Op[23, 22], Op[9, 5], Op[4, 0]); + + Assert.Multiple(() => + { + Assert.That(GetVectorE0(ThreadState.V0), Is.EqualTo(AArch64.Vpart(64, 0, 0).ToUInt64())); + Assert.That(GetVectorE1(ThreadState.V0), Is.EqualTo(AArch64.Vpart(64, 0, 1).ToUInt64())); + }); + } + + [Test, Description("UADDLP ., .")] + public void Uaddlp_V_8B4H_4H2S_2S1D([Values(0u)] uint Rd, + [Values(1u, 0u)] uint Rn, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong Z, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong A, + [Values(0b00u, 0b01u, 0b10u)] uint size) // <8B4H, 4H2S, 2S1D> + { + uint Opcode = 0x2E202800; // UADDLP V0.4H, V0.8B + Opcode |= ((Rn & 31) << 5) | ((Rd & 31) << 0); + Opcode |= ((size & 3) << 22); + Bits Op = new Bits(Opcode); + + Vector128 V0 = MakeVectorE0E1(Z, Z); + Vector128 V1 = MakeVectorE0(A); + AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1); + + AArch64.Vpart(0, 0, new Bits(Z)); AArch64.Vpart(0, 1, new Bits(Z)); + AArch64.V(1, new Bits(A)); + SimdFp.Uaddlp_V(Op[30], Op[23, 22], Op[9, 5], Op[4, 0]); + + Assert.Multiple(() => + { + Assert.That(GetVectorE0(ThreadState.V0), Is.EqualTo(AArch64.Vpart(64, 0, 0).ToUInt64())); + Assert.That(GetVectorE1(ThreadState.V0), Is.EqualTo(AArch64.Vpart(64, 0, 1).ToUInt64())); + }); + } + + [Test, Description("UADDLP ., .")] + public void Uaddlp_V_16B8H_8H4S_4S2D([Values(0u)] uint Rd, + [Values(1u, 0u)] uint Rn, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong Z, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong A, + [Values(0b00u, 0b01u, 0b10u)] uint size) // <16B8H, 8H4S, 4S2D> + { + uint Opcode = 0x6E202800; // UADDLP V0.8H, V0.16B + Opcode |= ((Rn & 31) << 5) | ((Rd & 31) << 0); + Opcode |= ((size & 3) << 22); + Bits Op = new Bits(Opcode); + + Vector128 V0 = MakeVectorE0E1(Z, Z); + Vector128 V1 = MakeVectorE0E1(A, A); + AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1); + + AArch64.Vpart(0, 0, new Bits(Z)); AArch64.Vpart(0, 1, new Bits(Z)); + AArch64.Vpart(1, 0, new Bits(A)); AArch64.Vpart(1, 1, new Bits(A)); + SimdFp.Uaddlp_V(Op[30], Op[23, 22], Op[9, 5], Op[4, 0]); + + Assert.Multiple(() => + { + Assert.That(GetVectorE0(ThreadState.V0), Is.EqualTo(AArch64.Vpart(64, 0, 0).ToUInt64())); + Assert.That(GetVectorE1(ThreadState.V0), Is.EqualTo(AArch64.Vpart(64, 0, 1).ToUInt64())); + }); + } + [Test, Description("UQXTN , ")] public void Uqxtn_S_HB_SH_DS([Values(0u)] uint Rd, [Values(1u, 0u)] uint Rn, diff --git a/Ryujinx.Tests/Cpu/Tester/Instructions.cs b/Ryujinx.Tests/Cpu/Tester/Instructions.cs index 258737185..8e171474e 100644 --- a/Ryujinx.Tests/Cpu/Tester/Instructions.cs +++ b/Ryujinx.Tests/Cpu/Tester/Instructions.cs @@ -3060,6 +3060,90 @@ namespace Ryujinx.Tests.Cpu.Tester V(d, result); } + // sadalp_advsimd.html + public static void Sadalp_V(bool Q, Bits size, Bits Rn, Bits Rd) + { + const bool U = false; + const bool op = true; + + /* Decode Vector */ + int d = (int)UInt(Rd); + int n = (int)UInt(Rn); + + /* if size == '11' then ReservedValue(); */ + + int esize = 8 << (int)UInt(size); + int datasize = (Q ? 128 : 64); + int elements = datasize / (2 * esize); + + bool acc = (op == true); + bool unsigned = (U == true); + + /* Operation */ + /* CheckFPAdvSIMDEnabled64(); */ + + Bits operand = V(datasize, n); + Bits sum; + BigInteger op1; + BigInteger op2; + + Bits result = (acc ? V(datasize, d) : Zeros(datasize)); + + for (int e = 0; e <= elements - 1; e++) + { + op1 = Int(Elem(operand, 2 * e + 0, esize), unsigned); + op2 = Int(Elem(operand, 2 * e + 1, esize), unsigned); + + sum = (op1 + op2).SubBigInteger(2 * esize - 1, 0); + + Elem(result, e, 2 * esize, Elem(result, e, 2 * esize) + sum); + } + + V(d, result); + } + + // saddlp_advsimd.html + public static void Saddlp_V(bool Q, Bits size, Bits Rn, Bits Rd) + { + const bool U = false; + const bool op = false; + + /* Decode Vector */ + int d = (int)UInt(Rd); + int n = (int)UInt(Rn); + + /* if size == '11' then ReservedValue(); */ + + int esize = 8 << (int)UInt(size); + int datasize = (Q ? 128 : 64); + int elements = datasize / (2 * esize); + + bool acc = (op == true); + bool unsigned = (U == true); + + /* Operation */ + /* CheckFPAdvSIMDEnabled64(); */ + + Bits operand = V(datasize, n); + Bits sum; + BigInteger op1; + BigInteger op2; + + Bits result = (acc ? V(datasize, d) : Zeros(datasize)); + + for (int e = 0; e <= elements - 1; e++) + { + op1 = Int(Elem(operand, 2 * e + 0, esize), unsigned); + op2 = Int(Elem(operand, 2 * e + 1, esize), unsigned); + + sum = (op1 + op2).SubBigInteger(2 * esize - 1, 0); + + Elem(result, e, 2 * esize, Elem(result, e, 2 * esize) + sum); + } + + V(d, result); + } + // sqabs_advsimd.html#SQABS_asisdmisc_R public static void Sqabs_S(Bits size, Bits Rn, Bits Rd) { @@ -3522,6 +3606,90 @@ namespace Ryujinx.Tests.Cpu.Tester V(d, result); } + // uadalp_advsimd.html + public static void Uadalp_V(bool Q, Bits size, Bits Rn, Bits Rd) + { + const bool U = true; + const bool op = true; + + /* Decode Vector */ + int d = (int)UInt(Rd); + int n = (int)UInt(Rn); + + /* if size == '11' then ReservedValue(); */ + + int esize = 8 << (int)UInt(size); + int datasize = (Q ? 128 : 64); + int elements = datasize / (2 * esize); + + bool acc = (op == true); + bool unsigned = (U == true); + + /* Operation */ + /* CheckFPAdvSIMDEnabled64(); */ + + Bits operand = V(datasize, n); + Bits sum; + BigInteger op1; + BigInteger op2; + + Bits result = (acc ? V(datasize, d) : Zeros(datasize)); + + for (int e = 0; e <= elements - 1; e++) + { + op1 = Int(Elem(operand, 2 * e + 0, esize), unsigned); + op2 = Int(Elem(operand, 2 * e + 1, esize), unsigned); + + sum = (op1 + op2).SubBigInteger(2 * esize - 1, 0); + + Elem(result, e, 2 * esize, Elem(result, e, 2 * esize) + sum); + } + + V(d, result); + } + + // uaddlp_advsimd.html + public static void Uaddlp_V(bool Q, Bits size, Bits Rn, Bits Rd) + { + const bool U = true; + const bool op = false; + + /* Decode Vector */ + int d = (int)UInt(Rd); + int n = (int)UInt(Rn); + + /* if size == '11' then ReservedValue(); */ + + int esize = 8 << (int)UInt(size); + int datasize = (Q ? 128 : 64); + int elements = datasize / (2 * esize); + + bool acc = (op == true); + bool unsigned = (U == true); + + /* Operation */ + /* CheckFPAdvSIMDEnabled64(); */ + + Bits operand = V(datasize, n); + Bits sum; + BigInteger op1; + BigInteger op2; + + Bits result = (acc ? V(datasize, d) : Zeros(datasize)); + + for (int e = 0; e <= elements - 1; e++) + { + op1 = Int(Elem(operand, 2 * e + 0, esize), unsigned); + op2 = Int(Elem(operand, 2 * e + 1, esize), unsigned); + + sum = (op1 + op2).SubBigInteger(2 * esize - 1, 0); + + Elem(result, e, 2 * esize, Elem(result, e, 2 * esize) + sum); + } + + V(d, result); + } + // uqxtn_advsimd.html#UQXTN_asisdmisc_N public static void Uqxtn_S(Bits size, Bits Rn, Bits Rd) { From 09593ae6d85b204b7bded803a56f0d81fbda1127 Mon Sep 17 00:00:00 2001 From: gdkchan Date: Mon, 13 Aug 2018 18:22:09 -0300 Subject: [PATCH 2/5] Add partial support to the TEX.B shader instruction (#342) * Add partial support to the TEX.B shader instruction, fix for mixed indexed and non-indexed drawing * Better exception --- Ryujinx.Graphics/Gal/Shader/GlslDecl.cs | 74 +++++++++++++++---- Ryujinx.Graphics/Gal/Shader/GlslDecompiler.cs | 45 ++++++++++- .../Gal/Shader/ShaderDecodeMem.cs | 26 +++++-- Ryujinx.Graphics/Gal/Shader/ShaderIrInst.cs | 3 +- .../Gal/Shader/ShaderOpCodeTable.cs | 1 + Ryujinx.Graphics/Gal/ShaderDeclInfo.cs | 15 +++- Ryujinx.HLE/Gpu/Engines/NvGpuEngine3d.cs | 31 +++++--- 7 files changed, 153 insertions(+), 42 deletions(-) diff --git a/Ryujinx.Graphics/Gal/Shader/GlslDecl.cs b/Ryujinx.Graphics/Gal/Shader/GlslDecl.cs index a0c747bac..691ab8007 100644 --- a/Ryujinx.Graphics/Gal/Shader/GlslDecl.cs +++ b/Ryujinx.Graphics/Gal/Shader/GlslDecl.cs @@ -1,3 +1,4 @@ +using System; using System.Collections.Generic; namespace Ryujinx.Graphics.Gal.Shader @@ -19,7 +20,7 @@ namespace Ryujinx.Graphics.Gal.Shader public const int PositionOutAttrLocation = 15; private const int AttrStartIndex = 8; - private const int TexStartIndex = 8; + private const int TexStartIndex = 8; public const string PositionOutAttrName = "position"; @@ -46,6 +47,8 @@ namespace Ryujinx.Graphics.Gal.Shader private string StagePrefix; + private Dictionary m_CbTextures; + private Dictionary m_Textures; private Dictionary m_Uniforms; @@ -56,6 +59,8 @@ namespace Ryujinx.Graphics.Gal.Shader private Dictionary m_Gprs; private Dictionary m_Preds; + public IReadOnlyDictionary CbTextures => m_CbTextures; + public IReadOnlyDictionary Textures => m_Textures; public IReadOnlyDictionary Uniforms => m_Uniforms; @@ -72,8 +77,10 @@ namespace Ryujinx.Graphics.Gal.Shader { this.ShaderType = ShaderType; - m_Uniforms = new Dictionary(); + m_CbTextures = new Dictionary(); + m_Textures = new Dictionary(); + m_Uniforms = new Dictionary(); m_Attributes = new Dictionary(); m_InAttributes = new Dictionary(); @@ -89,14 +96,16 @@ namespace Ryujinx.Graphics.Gal.Shader if (ShaderType == GalShaderType.Fragment) { - m_Gprs.Add(0, new ShaderDeclInfo(FragmentOutputName, 0, 0, 4)); + m_Gprs.Add(0, new ShaderDeclInfo(FragmentOutputName, 0, false, 0, 4)); } foreach (ShaderIrBlock Block in Blocks) { - foreach (ShaderIrNode Node in Block.GetNodes()) + ShaderIrNode[] Nodes = Block.GetNodes(); + + foreach (ShaderIrNode Node in Nodes) { - Traverse(null, Node); + Traverse(Nodes, null, Node); } } } @@ -152,31 +161,31 @@ namespace Ryujinx.Graphics.Gal.Shader } } - private void Traverse(ShaderIrNode Parent, ShaderIrNode Node) + private void Traverse(ShaderIrNode[] Nodes, ShaderIrNode Parent, ShaderIrNode Node) { switch (Node) { case ShaderIrAsg Asg: { - Traverse(Asg, Asg.Dst); - Traverse(Asg, Asg.Src); + Traverse(Nodes, Asg, Asg.Dst); + Traverse(Nodes, Asg, Asg.Src); break; } case ShaderIrCond Cond: { - Traverse(Cond, Cond.Pred); - Traverse(Cond, Cond.Child); + Traverse(Nodes, Cond, Cond.Pred); + Traverse(Nodes, Cond, Cond.Child); break; } case ShaderIrOp Op: { - Traverse(Op, Op.OperandA); - Traverse(Op, Op.OperandB); - Traverse(Op, Op.OperandC); + Traverse(Nodes, Op, Op.OperandA); + Traverse(Nodes, Op, Op.OperandB); + Traverse(Nodes, Op, Op.OperandC); if (Op.Inst == ShaderIrInst.Texq || Op.Inst == ShaderIrInst.Texs || @@ -190,6 +199,38 @@ namespace Ryujinx.Graphics.Gal.Shader m_Textures.TryAdd(Handle, new ShaderDeclInfo(Name, Handle)); } + else if (Op.Inst == ShaderIrInst.Texb) + { + ShaderIrNode HandleSrc = null; + + int Index = Array.IndexOf(Nodes, Parent) - 1; + + for (; Index >= 0; Index--) + { + ShaderIrNode Curr = Nodes[Index]; + + if (Curr is ShaderIrAsg Asg && Asg.Dst is ShaderIrOperGpr Gpr) + { + if (Gpr.Index == ((ShaderIrOperGpr)Op.OperandC).Index) + { + HandleSrc = Asg.Src; + + break; + } + } + } + + if (HandleSrc != null && HandleSrc is ShaderIrOperCbuf Cbuf) + { + string Name = StagePrefix + TextureName + "_cb" + Cbuf.Index + "_" + Cbuf.Pos; + + m_CbTextures.Add(Op, new ShaderDeclInfo(Name, Cbuf.Pos, true, Cbuf.Index)); + } + else + { + throw new NotImplementedException("Shader TEX.B instruction is not fully supported!"); + } + } break; } @@ -199,7 +240,7 @@ namespace Ryujinx.Graphics.Gal.Shader { string Name = StagePrefix + UniformName + Cbuf.Index; - ShaderDeclInfo DeclInfo = new ShaderDeclInfo(Name, Cbuf.Pos, Cbuf.Index); + ShaderDeclInfo DeclInfo = new ShaderDeclInfo(Name, Cbuf.Pos, true, Cbuf.Index); m_Uniforms.Add(Cbuf.Index, DeclInfo); } @@ -252,12 +293,13 @@ namespace Ryujinx.Graphics.Gal.Shader if (!m_Attributes.ContainsKey(Index)) { - DeclInfo = new ShaderDeclInfo(AttrName + GlslIndex, GlslIndex, 0, 4); + DeclInfo = new ShaderDeclInfo(AttrName + GlslIndex, GlslIndex, false, 0, 4); m_Attributes.Add(Index, DeclInfo); } - Traverse(Abuf, Abuf.Vertex); + Traverse(Nodes, Abuf, Abuf.Vertex); + break; } diff --git a/Ryujinx.Graphics/Gal/Shader/GlslDecompiler.cs b/Ryujinx.Graphics/Gal/Shader/GlslDecompiler.cs index aa1803a5d..94bdd2fa1 100644 --- a/Ryujinx.Graphics/Gal/Shader/GlslDecompiler.cs +++ b/Ryujinx.Graphics/Gal/Shader/GlslDecompiler.cs @@ -98,6 +98,7 @@ namespace Ryujinx.Graphics.Gal.Shader { ShaderIrInst.Or, GetOrExpr }, { ShaderIrInst.Stof, GetStofExpr }, { ShaderIrInst.Sub, GetSubExpr }, + { ShaderIrInst.Texb, GetTexbExpr }, { ShaderIrInst.Texq, GetTexqExpr }, { ShaderIrInst.Texs, GetTexsExpr }, { ShaderIrInst.Trunc, GetTruncExpr }, @@ -174,10 +175,12 @@ namespace Ryujinx.Graphics.Gal.Shader string GlslCode = SB.ToString(); - return new GlslProgram( - GlslCode, - Decl.Textures.Values, - Decl.Uniforms.Values); + List TextureInfo = new List(); + + TextureInfo.AddRange(Decl.Textures.Values); + TextureInfo.AddRange(IterateCbTextures()); + + return new GlslProgram(GlslCode, TextureInfo, Decl.Uniforms.Values); } private void PrintDeclHeader() @@ -213,9 +216,27 @@ namespace Ryujinx.Graphics.Gal.Shader private void PrintDeclTextures() { + foreach (ShaderDeclInfo DeclInfo in IterateCbTextures()) + { + SB.AppendLine("uniform sampler2D " + DeclInfo.Name + ";"); + } + PrintDecls(Decl.Textures, "uniform sampler2D"); } + private IEnumerable IterateCbTextures() + { + HashSet Names = new HashSet(); + + foreach (ShaderDeclInfo DeclInfo in Decl.CbTextures.Values.OrderBy(DeclKeySelector)) + { + if (Names.Add(DeclInfo.Name)) + { + yield return DeclInfo; + } + } + } + private void PrintDeclUniforms() { if (Decl.ShaderType == GalShaderType.Vertex) @@ -994,6 +1015,22 @@ namespace Ryujinx.Graphics.Gal.Shader private string GetSubExpr(ShaderIrOp Op) => GetBinaryExpr(Op, "-"); + private string GetTexbExpr(ShaderIrOp Op) + { + ShaderIrMetaTex Meta = (ShaderIrMetaTex)Op.MetaData; + + if (!Decl.CbTextures.TryGetValue(Op, out ShaderDeclInfo DeclInfo)) + { + throw new InvalidOperationException(); + } + + string Coords = GetTexSamplerCoords(Op); + + string Ch = "rgba".Substring(Meta.Elem, 1); + + return "texture(" + DeclInfo.Name + ", " + Coords + ")." + Ch; + } + private string GetTexqExpr(ShaderIrOp Op) { ShaderIrMetaTexq Meta = (ShaderIrMetaTexq)Op.MetaData; diff --git a/Ryujinx.Graphics/Gal/Shader/ShaderDecodeMem.cs b/Ryujinx.Graphics/Gal/Shader/ShaderDecodeMem.cs index aea7e744d..5ef2e2e5f 100644 --- a/Ryujinx.Graphics/Gal/Shader/ShaderDecodeMem.cs +++ b/Ryujinx.Graphics/Gal/Shader/ShaderDecodeMem.cs @@ -121,6 +121,16 @@ namespace Ryujinx.Graphics.Gal.Shader } public static void Tex(ShaderIrBlock Block, long OpCode) + { + EmitTex(Block, OpCode, GprHandle: false); + } + + public static void Tex_B(ShaderIrBlock Block, long OpCode) + { + EmitTex(Block, OpCode, GprHandle: true); + } + + private static void EmitTex(ShaderIrBlock Block, long OpCode, bool GprHandle) { //TODO: Support other formats. ShaderIrOperGpr[] Coords = new ShaderIrOperGpr[2]; @@ -139,7 +149,11 @@ namespace Ryujinx.Graphics.Gal.Shader int ChMask = (int)(OpCode >> 31) & 0xf; - ShaderIrNode OperC = GetOperImm13_36(OpCode); + ShaderIrNode OperC = GprHandle + ? (ShaderIrNode)GetOperGpr20 (OpCode) + : (ShaderIrNode)GetOperImm13_36(OpCode); + + ShaderIrInst Inst = GprHandle ? ShaderIrInst.Texb : ShaderIrInst.Texs; for (int Ch = 0; Ch < 4; Ch++) { @@ -147,7 +161,7 @@ namespace Ryujinx.Graphics.Gal.Shader ShaderIrMetaTex Meta = new ShaderIrMetaTex(Ch); - ShaderIrOp Op = new ShaderIrOp(ShaderIrInst.Texs, Coords[0], Coords[1], OperC, Meta); + ShaderIrOp Op = new ShaderIrOp(Inst, Coords[0], Coords[1], OperC, Meta); Block.AddNode(GetPredNode(new ShaderIrAsg(Dst, Op), OpCode)); } @@ -178,15 +192,15 @@ namespace Ryujinx.Graphics.Gal.Shader public static void Texs(ShaderIrBlock Block, long OpCode) { - EmitTex(Block, OpCode, ShaderIrInst.Texs); + EmitTexs(Block, OpCode, ShaderIrInst.Texs); } public static void Tlds(ShaderIrBlock Block, long OpCode) { - EmitTex(Block, OpCode, ShaderIrInst.Txlf); + EmitTexs(Block, OpCode, ShaderIrInst.Txlf); } - private static void EmitTex(ShaderIrBlock Block, long OpCode, ShaderIrInst Inst) + private static void EmitTexs(ShaderIrBlock Block, long OpCode, ShaderIrInst Inst) { //TODO: Support other formats. ShaderIrNode OperA = GetOperGpr8 (OpCode); @@ -195,7 +209,7 @@ namespace Ryujinx.Graphics.Gal.Shader int LutIndex; - LutIndex = GetOperGpr0(OpCode).Index != ShaderIrOperGpr.ZRIndex ? 1 : 0; + LutIndex = GetOperGpr0(OpCode).Index != ShaderIrOperGpr.ZRIndex ? 1 : 0; LutIndex |= GetOperGpr28(OpCode).Index != ShaderIrOperGpr.ZRIndex ? 2 : 0; int ChMask = MaskLut[LutIndex, (OpCode >> 50) & 7]; diff --git a/Ryujinx.Graphics/Gal/Shader/ShaderIrInst.cs b/Ryujinx.Graphics/Gal/Shader/ShaderIrInst.cs index fd86cadb1..d197835a7 100644 --- a/Ryujinx.Graphics/Gal/Shader/ShaderIrInst.cs +++ b/Ryujinx.Graphics/Gal/Shader/ShaderIrInst.cs @@ -47,6 +47,7 @@ namespace Ryujinx.Graphics.Gal.Shader Ftos, Ftou, Ipa, + Texb, Texs, Trunc, F_End, @@ -83,7 +84,7 @@ namespace Ryujinx.Graphics.Gal.Shader Bra, Exit, Kil, - + Emit, Cut } diff --git a/Ryujinx.Graphics/Gal/Shader/ShaderOpCodeTable.cs b/Ryujinx.Graphics/Gal/Shader/ShaderOpCodeTable.cs index 3f20dc446..95b8e467d 100644 --- a/Ryujinx.Graphics/Gal/Shader/ShaderOpCodeTable.cs +++ b/Ryujinx.Graphics/Gal/Shader/ShaderOpCodeTable.cs @@ -114,6 +114,7 @@ namespace Ryujinx.Graphics.Gal.Shader Set("0101110000101x", ShaderDecode.Shr_R); Set("1110111111110x", ShaderDecode.St_A); Set("110000xxxx111x", ShaderDecode.Tex); + Set("1101111010111x", ShaderDecode.Tex_B); Set("1101111101001x", ShaderDecode.Texq); Set("1101100xxxxxxx", ShaderDecode.Texs); Set("1101101xxxxxxx", ShaderDecode.Tlds); diff --git a/Ryujinx.Graphics/Gal/ShaderDeclInfo.cs b/Ryujinx.Graphics/Gal/ShaderDeclInfo.cs index d400850c8..ef47ca2e1 100644 --- a/Ryujinx.Graphics/Gal/ShaderDeclInfo.cs +++ b/Ryujinx.Graphics/Gal/ShaderDeclInfo.cs @@ -4,14 +4,21 @@ namespace Ryujinx.Graphics.Gal { public string Name { get; private set; } - public int Index { get; private set; } - public int Cbuf { get; private set; } - public int Size { get; private set; } + public int Index { get; private set; } + public bool IsCb { get; private set; } + public int Cbuf { get; private set; } + public int Size { get; private set; } - public ShaderDeclInfo(string Name, int Index, int Cbuf = 0, int Size = 1) + public ShaderDeclInfo( + string Name, + int Index, + bool IsCb = false, + int Cbuf = 0, + int Size = 1) { this.Name = Name; this.Index = Index; + this.IsCb = IsCb; this.Cbuf = Cbuf; this.Size = Size; } diff --git a/Ryujinx.HLE/Gpu/Engines/NvGpuEngine3d.cs b/Ryujinx.HLE/Gpu/Engines/NvGpuEngine3d.cs index 0576601f5..38f8d1c9b 100644 --- a/Ryujinx.HLE/Gpu/Engines/NvGpuEngine3d.cs +++ b/Ryujinx.HLE/Gpu/Engines/NvGpuEngine3d.cs @@ -309,7 +309,7 @@ namespace Ryujinx.HLE.Gpu.Engines private void SetStencil(GalPipelineState State) { State.StencilTestEnabled = (ReadRegister(NvGpuEngine3dReg.StencilEnable) & 1) != 0; - + if (State.StencilTestEnabled) { State.StencilBackFuncFunc = (GalComparisonOp)ReadRegister(NvGpuEngine3dReg.StencilBackFuncFunc); @@ -364,17 +364,26 @@ namespace Ryujinx.HLE.Gpu.Engines int TextureCbIndex = ReadRegister(NvGpuEngine3dReg.TextureCbIndex); - //Note: On the emulator renderer, Texture Unit 0 is - //reserved for drawing the frame buffer. - int TexIndex = 1; + int TexIndex = 0; for (int Index = 0; Index < Keys.Length; Index++) { foreach (ShaderDeclInfo DeclInfo in Gpu.Renderer.Shader.GetTextureUsage(Keys[Index])) { - long Position = ConstBuffers[Index][TextureCbIndex].Position; + long Position; - UploadTexture(Vmm, Position, TexIndex, DeclInfo.Index); + if (DeclInfo.IsCb) + { + Position = ConstBuffers[Index][DeclInfo.Cbuf].Position; + } + else + { + Position = ConstBuffers[Index][TextureCbIndex].Position; + } + + int TextureHandle = Vmm.ReadInt32(Position + DeclInfo.Index * 4); + + UploadTexture(Vmm, TexIndex, TextureHandle); Gpu.Renderer.Shader.EnsureTextureBinding(DeclInfo.Name, TexIndex); @@ -383,12 +392,8 @@ namespace Ryujinx.HLE.Gpu.Engines } } - private void UploadTexture(NvGpuVmm Vmm, long BasePosition, int TexIndex, int HndIndex) + private void UploadTexture(NvGpuVmm Vmm, int TexIndex, int TextureHandle) { - long Position = BasePosition + HndIndex * 4; - - int TextureHandle = Vmm.ReadInt32(Position); - if (TextureHandle == 0) { //TODO: Is this correct? @@ -601,6 +606,10 @@ namespace Ryujinx.HLE.Gpu.Engines Gpu.Renderer.Rasterizer.DrawArrays(VertexFirst, VertexCount, PrimType); } + + //Is the GPU really clearing those registers after draw? + WriteRegister(NvGpuEngine3dReg.IndexBatchFirst, 0); + WriteRegister(NvGpuEngine3dReg.IndexBatchCount, 0); } private void QueryControl(NvGpuVmm Vmm, NvGpuPBEntry PBEntry) From 6e483120527c749854066b1ab0c5a1d53d49f0e3 Mon Sep 17 00:00:00 2001 From: emmauss Date: Tue, 14 Aug 2018 00:31:09 +0300 Subject: [PATCH 3/5] Implement AcquireNpadStyleSetUpdate event (#306) * implement get npad set event * move npad style set event to hid service * fix spacing * change event to field * dispose event --- Ryujinx.HLE/OsHle/Horizon.cs | 2 +- Ryujinx.HLE/OsHle/Services/Hid/IHidServer.cs | 31 +++++++++++++++++++- 2 files changed, 31 insertions(+), 2 deletions(-) diff --git a/Ryujinx.HLE/OsHle/Horizon.cs b/Ryujinx.HLE/OsHle/Horizon.cs index f420dc0df..f12bf37d7 100644 --- a/Ryujinx.HLE/OsHle/Horizon.cs +++ b/Ryujinx.HLE/OsHle/Horizon.cs @@ -233,4 +233,4 @@ namespace Ryujinx.HLE.OsHle } } } -} \ No newline at end of file +} diff --git a/Ryujinx.HLE/OsHle/Services/Hid/IHidServer.cs b/Ryujinx.HLE/OsHle/Services/Hid/IHidServer.cs index 79d37fd4c..f23164ce3 100644 --- a/Ryujinx.HLE/OsHle/Services/Hid/IHidServer.cs +++ b/Ryujinx.HLE/OsHle/Services/Hid/IHidServer.cs @@ -1,14 +1,18 @@ using Ryujinx.HLE.Input; using Ryujinx.HLE.Logging; using Ryujinx.HLE.OsHle.Ipc; +using Ryujinx.HLE.OsHle.Handles; +using System; using System.Collections.Generic; namespace Ryujinx.HLE.OsHle.Services.Hid { - class IHidServer : IpcService + class IHidServer : IpcService, IDisposable { private Dictionary m_Commands; + private KEvent NpadStyleSetUpdateEvent; + public override IReadOnlyDictionary Commands => m_Commands; public IHidServer() @@ -26,6 +30,7 @@ namespace Ryujinx.HLE.OsHle.Services.Hid { 101, GetSupportedNpadStyleSet }, { 102, SetSupportedNpadIdType }, { 103, ActivateNpad }, + { 106, AcquireNpadStyleSetUpdateEventHandle }, { 108, GetPlayerLedPattern }, { 120, SetNpadJoyHoldType }, { 121, GetNpadJoyHoldType }, @@ -39,6 +44,8 @@ namespace Ryujinx.HLE.OsHle.Services.Hid { 203, CreateActiveVibrationDeviceList }, { 206, SendVibrationValues } }; + + NpadStyleSetUpdateEvent = new KEvent(); } public long CreateAppletResource(ServiceCtx Context) @@ -104,6 +111,15 @@ namespace Ryujinx.HLE.OsHle.Services.Hid return 0; } + public long AcquireNpadStyleSetUpdateEventHandle(ServiceCtx Context) + { + int Handle = Context.Process.HandleTable.OpenHandle(NpadStyleSetUpdateEvent); + + Context.Response.HandleDesc = IpcHandleDesc.MakeCopy(Handle); + + return 0; + } + public long GetSupportedNpadStyleSet(ServiceCtx Context) { Context.ResponseData.Write(0); @@ -266,5 +282,18 @@ namespace Ryujinx.HLE.OsHle.Services.Hid return 0; } + + public void Dispose() + { + Dispose(true); + } + + protected virtual void Dispose(bool Disposing) + { + if (Disposing) + { + NpadStyleSetUpdateEvent.Dispose(); + } + } } } From 4f499b6845f7f46542cde945bad8f3275d3a11d5 Mon Sep 17 00:00:00 2001 From: gdkchan Date: Mon, 13 Aug 2018 19:46:36 -0300 Subject: [PATCH 4/5] Fix FMUL and TEXS shader instructions (#347) --- .../Gal/Shader/ShaderDecodeAlu.cs | 95 +++++++++++-------- .../Gal/Shader/ShaderDecodeMem.cs | 43 ++++++--- 2 files changed, 87 insertions(+), 51 deletions(-) diff --git a/Ryujinx.Graphics/Gal/Shader/ShaderDecodeAlu.cs b/Ryujinx.Graphics/Gal/Shader/ShaderDecodeAlu.cs index 8486d8a7d..b60da7c1c 100644 --- a/Ryujinx.Graphics/Gal/Shader/ShaderDecodeAlu.cs +++ b/Ryujinx.Graphics/Gal/Shader/ShaderDecodeAlu.cs @@ -23,12 +23,12 @@ namespace Ryujinx.Graphics.Gal.Shader public static void Fadd_C(ShaderIrBlock Block, long OpCode) { - EmitAluBinaryF(Block, OpCode, ShaderOper.CR, ShaderIrInst.Fadd); + EmitFadd(Block, OpCode, ShaderOper.CR); } public static void Fadd_I(ShaderIrBlock Block, long OpCode) { - EmitAluBinaryF(Block, OpCode, ShaderOper.Immf, ShaderIrInst.Fadd); + EmitFadd(Block, OpCode, ShaderOper.Immf); } public static void Fadd_I32(ShaderIrBlock Block, long OpCode) @@ -51,7 +51,7 @@ namespace Ryujinx.Graphics.Gal.Shader public static void Fadd_R(ShaderIrBlock Block, long OpCode) { - EmitAluBinaryF(Block, OpCode, ShaderOper.RR, ShaderIrInst.Fadd); + EmitFadd(Block, OpCode, ShaderOper.RR); } public static void Ffma_CR(ShaderIrBlock Block, long OpCode) @@ -101,17 +101,17 @@ namespace Ryujinx.Graphics.Gal.Shader public static void Fmul_C(ShaderIrBlock Block, long OpCode) { - EmitAluBinaryF(Block, OpCode, ShaderOper.CR, ShaderIrInst.Fmul); + EmitFmul(Block, OpCode, ShaderOper.CR); } public static void Fmul_I(ShaderIrBlock Block, long OpCode) { - EmitAluBinaryF(Block, OpCode, ShaderOper.Immf, ShaderIrInst.Fmul); + EmitFmul(Block, OpCode, ShaderOper.Immf); } public static void Fmul_R(ShaderIrBlock Block, long OpCode) { - EmitAluBinaryF(Block, OpCode, ShaderOper.RR, ShaderIrInst.Fmul); + EmitFmul(Block, OpCode, ShaderOper.RR); } public static void Fset_C(ShaderIrBlock Block, long OpCode) @@ -519,40 +519,6 @@ namespace Ryujinx.Graphics.Gal.Shader Block.AddNode(GetPredNode(new ShaderIrAsg(GetOperGpr0(OpCode), Op), OpCode)); } - private static void EmitAluBinaryF( - ShaderIrBlock Block, - long OpCode, - ShaderOper Oper, - ShaderIrInst Inst) - { - bool NegB = ((OpCode >> 45) & 1) != 0; - bool AbsA = ((OpCode >> 46) & 1) != 0; - bool NegA = ((OpCode >> 48) & 1) != 0; - bool AbsB = ((OpCode >> 49) & 1) != 0; - - ShaderIrNode OperA = GetOperGpr8(OpCode), OperB; - - if (Inst == ShaderIrInst.Fadd) - { - OperA = GetAluFabsFneg(OperA, AbsA, NegA); - } - - switch (Oper) - { - case ShaderOper.CR: OperB = GetOperCbuf34 (OpCode); break; - case ShaderOper.Immf: OperB = GetOperImmf19_20(OpCode); break; - case ShaderOper.RR: OperB = GetOperGpr20 (OpCode); break; - - default: throw new ArgumentException(nameof(Oper)); - } - - OperB = GetAluFabsFneg(OperB, AbsB, NegB); - - ShaderIrNode Op = new ShaderIrOp(Inst, OperA, OperB); - - Block.AddNode(GetPredNode(new ShaderIrAsg(GetOperGpr0(OpCode), Op), OpCode)); - } - private static void EmitBfe(ShaderIrBlock Block, long OpCode, ShaderOper Oper) { //TODO: Handle the case where position + length @@ -609,6 +575,55 @@ namespace Ryujinx.Graphics.Gal.Shader Block.AddNode(GetPredNode(new ShaderIrAsg(GetOperGpr0(OpCode), Op), OpCode)); } + private static void EmitFadd(ShaderIrBlock Block, long OpCode, ShaderOper Oper) + { + bool NegB = ((OpCode >> 45) & 1) != 0; + bool AbsA = ((OpCode >> 46) & 1) != 0; + bool NegA = ((OpCode >> 48) & 1) != 0; + bool AbsB = ((OpCode >> 49) & 1) != 0; + + ShaderIrNode OperA = GetOperGpr8(OpCode), OperB; + + OperA = GetAluFabsFneg(OperA, AbsA, NegA); + + switch (Oper) + { + case ShaderOper.CR: OperB = GetOperCbuf34 (OpCode); break; + case ShaderOper.Immf: OperB = GetOperImmf19_20(OpCode); break; + case ShaderOper.RR: OperB = GetOperGpr20 (OpCode); break; + + default: throw new ArgumentException(nameof(Oper)); + } + + OperB = GetAluFabsFneg(OperB, AbsB, NegB); + + ShaderIrNode Op = new ShaderIrOp(ShaderIrInst.Fadd, OperA, OperB); + + Block.AddNode(GetPredNode(new ShaderIrAsg(GetOperGpr0(OpCode), Op), OpCode)); + } + + private static void EmitFmul(ShaderIrBlock Block, long OpCode, ShaderOper Oper) + { + bool NegB = ((OpCode >> 48) & 1) != 0; + + ShaderIrNode OperA = GetOperGpr8(OpCode), OperB; + + switch (Oper) + { + case ShaderOper.CR: OperB = GetOperCbuf34 (OpCode); break; + case ShaderOper.Immf: OperB = GetOperImmf19_20(OpCode); break; + case ShaderOper.RR: OperB = GetOperGpr20 (OpCode); break; + + default: throw new ArgumentException(nameof(Oper)); + } + + OperB = GetAluFneg(OperB, NegB); + + ShaderIrNode Op = new ShaderIrOp(ShaderIrInst.Fmul, OperA, OperB); + + Block.AddNode(GetPredNode(new ShaderIrAsg(GetOperGpr0(OpCode), Op), OpCode)); + } + private static void EmitFfma(ShaderIrBlock Block, long OpCode, ShaderOper Oper) { bool NegB = ((OpCode >> 48) & 1) != 0; diff --git a/Ryujinx.Graphics/Gal/Shader/ShaderDecodeMem.cs b/Ryujinx.Graphics/Gal/Shader/ShaderDecodeMem.cs index 5ef2e2e5f..e794e1f87 100644 --- a/Ryujinx.Graphics/Gal/Shader/ShaderDecodeMem.cs +++ b/Ryujinx.Graphics/Gal/Shader/ShaderDecodeMem.cs @@ -26,8 +26,8 @@ namespace Ryujinx.Graphics.Gal.Shader private static int[,] MaskLut = new int[,] { { ____, ____, ____, ____, ____, ____, ____, ____ }, - { R___, _G__, __B_, ___A, RG__, ____, ____, ____ }, { R___, _G__, __B_, ___A, RG__, R__A, _G_A, __BA }, + { R___, _G__, __B_, ___A, RG__, ____, ____, ____ }, { RGB_, RG_A, R_BA, _GBA, RGBA, ____, ____, ____ } }; @@ -209,9 +209,16 @@ namespace Ryujinx.Graphics.Gal.Shader int LutIndex; - LutIndex = GetOperGpr0(OpCode).Index != ShaderIrOperGpr.ZRIndex ? 1 : 0; + LutIndex = GetOperGpr0 (OpCode).Index != ShaderIrOperGpr.ZRIndex ? 1 : 0; LutIndex |= GetOperGpr28(OpCode).Index != ShaderIrOperGpr.ZRIndex ? 2 : 0; + if (LutIndex == 0) + { + //Both registers are RZ, color is not written anywhere. + //So, the intruction is basically a no-op. + return; + } + int ChMask = MaskLut[LutIndex, (OpCode >> 50) & 7]; for (int Ch = 0; Ch < 4; Ch++) @@ -227,6 +234,26 @@ namespace Ryujinx.Graphics.Gal.Shader int RegInc = 0; + ShaderIrOperGpr GetDst() + { + ShaderIrOperGpr Dst; + + switch (LutIndex) + { + case 1: Dst = GetOperGpr0 (OpCode); break; + case 2: Dst = GetOperGpr28(OpCode); break; + case 3: Dst = (RegInc >> 1) != 0 + ? GetOperGpr28(OpCode) + : GetOperGpr0 (OpCode); break; + + default: throw new InvalidOperationException(); + } + + Dst.Index += RegInc++ & 1; + + return Dst; + } + for (int Ch = 0; Ch < 4; Ch++) { if (!IsChannelUsed(ChMask, Ch)) @@ -236,18 +263,12 @@ namespace Ryujinx.Graphics.Gal.Shader ShaderIrOperGpr Src = new ShaderIrOperGpr(TempRegStart + Ch); - ShaderIrOperGpr Dst = (RegInc >> 1) != 0 - ? GetOperGpr28(OpCode) - : GetOperGpr0 (OpCode); + ShaderIrOperGpr Dst = GetDst(); - Dst.Index += RegInc++ & 1; - - if (Dst.Index >= ShaderIrOperGpr.ZRIndex) + if (Dst.Index != ShaderIrOperGpr.ZRIndex) { - continue; + Block.AddNode(GetPredNode(new ShaderIrAsg(Dst, Src), OpCode)); } - - Block.AddNode(GetPredNode(new ShaderIrAsg(Dst, Src), OpCode)); } } From 17f54b5d78ac9ee83419dc204a02e3887bdca8e4 Mon Sep 17 00:00:00 2001 From: emmauss Date: Tue, 14 Aug 2018 03:13:01 +0300 Subject: [PATCH 5/5] Implement IProfile Get and GetBase (#266) * implement IProfile Get(Base) * use default user * changed userid --- Ryujinx.HLE/OsHle/Profile.cs | 8 +++ Ryujinx.HLE/OsHle/Services/Acc/IProfile.cs | 35 ++++++++++--- Ryujinx.HLE/OsHle/Services/Acc/ProfileBase.cs | 52 +++++++++++++++++++ Ryujinx.HLE/OsHle/Utilities/StringUtils.cs | 52 +++++++++++++++++++ Ryujinx.HLE/Settings/SystemSettings.cs | 6 ++- Ryujinx.HLE/Switch.cs | 8 ++- Ryujinx/Config.cs | 4 +- Ryujinx/Ryujinx.conf | 2 +- 8 files changed, 154 insertions(+), 13 deletions(-) create mode 100644 Ryujinx.HLE/OsHle/Profile.cs create mode 100644 Ryujinx.HLE/OsHle/Services/Acc/ProfileBase.cs create mode 100644 Ryujinx.HLE/OsHle/Utilities/StringUtils.cs diff --git a/Ryujinx.HLE/OsHle/Profile.cs b/Ryujinx.HLE/OsHle/Profile.cs new file mode 100644 index 000000000..80b4487bf --- /dev/null +++ b/Ryujinx.HLE/OsHle/Profile.cs @@ -0,0 +1,8 @@ +namespace Ryujinx.HLE.OsHle +{ + public struct Profile + { + public string Username; + public string UserId; + } +} diff --git a/Ryujinx.HLE/OsHle/Services/Acc/IProfile.cs b/Ryujinx.HLE/OsHle/Services/Acc/IProfile.cs index 24daa3d57..0639b09c2 100644 --- a/Ryujinx.HLE/OsHle/Services/Acc/IProfile.cs +++ b/Ryujinx.HLE/OsHle/Services/Acc/IProfile.cs @@ -1,6 +1,9 @@ +using ChocolArm64.Memory; using Ryujinx.HLE.Logging; using Ryujinx.HLE.OsHle.Ipc; +using Ryujinx.HLE.OsHle.Utilities; using System.Collections.Generic; +using System.Text; namespace Ryujinx.HLE.OsHle.Services.Acc { @@ -14,21 +17,37 @@ namespace Ryujinx.HLE.OsHle.Services.Acc { m_Commands = new Dictionary() { + { 0, Get }, { 1, GetBase } }; } - public long GetBase(ServiceCtx Context) + public long Get(ServiceCtx Context) { Context.Ns.Log.PrintStub(LogClass.ServiceAcc, "Stubbed."); - Context.ResponseData.Write(0L); - Context.ResponseData.Write(0L); - Context.ResponseData.Write(0L); - Context.ResponseData.Write(0L); - Context.ResponseData.Write(0L); - Context.ResponseData.Write(0L); - Context.ResponseData.Write(0L); + long Position = Context.Request.ReceiveBuff[0].Position; + + AMemoryHelper.FillWithZeros(Context.Memory, Position, 0x80); + + Context.Memory.WriteInt32(Position, 0); + Context.Memory.WriteInt32(Position + 4, 1); + Context.Memory.WriteByte(Position + 8, 1); + + return GetBase(Context); + } + + public long GetBase(ServiceCtx Context) + { + ProfileBase ProfileBase = new ProfileBase(Context.Ns.Settings.User); + + Context.ResponseData.Write(ProfileBase.UserId.ToBytes()); + Context.ResponseData.Write(ProfileBase.Timestamp); + + int ByteCount = Encoding.UTF8.GetByteCount(ProfileBase.Username); + byte[] Username = StringUtils.GetFixedLengthBytes(ProfileBase.Username, 0x20, Encoding.UTF8); + + Context.ResponseData.Write(Username); return 0; } diff --git a/Ryujinx.HLE/OsHle/Services/Acc/ProfileBase.cs b/Ryujinx.HLE/OsHle/Services/Acc/ProfileBase.cs new file mode 100644 index 000000000..69914a86e --- /dev/null +++ b/Ryujinx.HLE/OsHle/Services/Acc/ProfileBase.cs @@ -0,0 +1,52 @@ +using Ryujinx.HLE.OsHle.Utilities; +using System; +using System.Linq; + +namespace Ryujinx.HLE.OsHle.Services.Acc +{ + struct ProfileBase + { + public UserId UserId; + public long Timestamp; + public string Username; + + public ProfileBase(Profile User) + { + UserId = new UserId(User.UserId); + Username = User.Username; + Timestamp = ((DateTimeOffset)DateTime.Today).ToUnixTimeSeconds(); + } + } + + struct UserId + { + private readonly ulong LowBytes; + private readonly ulong HighBytes; + + public UserId(string UserIdHex) + { + if (UserIdHex == null || UserIdHex.Length != 32 || !UserIdHex.All("0123456789abcdefABCDEF".Contains)) + { + throw new ArgumentException("UserId is not a valid Hex string", "UserIdHex"); + } + + byte[] HexBytes = StringUtils.HexToBytes(UserIdHex); + + LowBytes = BitConverter.ToUInt64(HexBytes, 8); + + Array.Resize(ref HexBytes, 8); + + HighBytes = BitConverter.ToUInt64(HexBytes, 0); + } + + public byte[] ToBytes() + { + return BitConverter.GetBytes(HighBytes).Concat(BitConverter.GetBytes(LowBytes)).ToArray(); + } + + public override string ToString() + { + return BitConverter.ToString(ToBytes()).ToLower().Replace("-", string.Empty); + } + } +} diff --git a/Ryujinx.HLE/OsHle/Utilities/StringUtils.cs b/Ryujinx.HLE/OsHle/Utilities/StringUtils.cs new file mode 100644 index 000000000..b0117f836 --- /dev/null +++ b/Ryujinx.HLE/OsHle/Utilities/StringUtils.cs @@ -0,0 +1,52 @@ +using System; +using System.Globalization; +using System.Linq; +using System.Text; + +namespace Ryujinx.HLE.OsHle.Utilities +{ + static class StringUtils + { + public static byte[] GetFixedLengthBytes(string InputString, int Size, Encoding Encoding) + { + InputString = InputString + "\0"; + + int ByteCount = Encoding.GetByteCount(InputString); + + byte[] Output = new byte[Size]; + + if (ByteCount < Size) + { + Encoding.GetBytes(InputString, 0, InputString.Length, Output, Size - ByteCount); + } + else + { + int NullSize = Encoding.GetByteCount("\0"); + + Output = Encoding.GetBytes(InputString); + + Array.Resize(ref Output, Size - NullSize); + + Output = Output.Concat(Encoding.GetBytes("\0")).ToArray(); + } + + return Output; + } + + public static byte[] HexToBytes(string HexString) + { + //Ignore last charactor if HexLength % 2 != 0 + int BytesInHex = HexString.Length / 2; + + byte[] Output = new byte[BytesInHex]; + + for (int Index = 0; Index < BytesInHex; Index++) + { + Output[Index] = byte.Parse(HexString.Substring(Index * 2, 2), + NumberStyles.HexNumber); + } + + return Output; + } + } +} diff --git a/Ryujinx.HLE/Settings/SystemSettings.cs b/Ryujinx.HLE/Settings/SystemSettings.cs index 555e83bba..fb94807e3 100644 --- a/Ryujinx.HLE/Settings/SystemSettings.cs +++ b/Ryujinx.HLE/Settings/SystemSettings.cs @@ -1,7 +1,11 @@ +using System.Collections.Generic; +using Ryujinx.HLE.OsHle; + namespace Ryujinx.HLE.Settings { public class SystemSettings { - public ColorSet ThemeColor; + public Profile User { get; set; } + public ColorSet ThemeColor { get; set; } } } diff --git a/Ryujinx.HLE/Switch.cs b/Ryujinx.HLE/Switch.cs index a80ca86c1..b219d7740 100644 --- a/Ryujinx.HLE/Switch.cs +++ b/Ryujinx.HLE/Switch.cs @@ -67,6 +67,12 @@ namespace Ryujinx.HLE Os.FontSharedMem.MemoryMapped += Font.ShMemMap; Os.FontSharedMem.MemoryUnmapped += Font.ShMemUnmap; + + Settings.User = new Profile() + { + Username = "Ryujinx", + UserId = "000123456789abcdef09876543210000" + }; } public void LoadCart(string ExeFsDir, string RomFsFile = null) @@ -109,4 +115,4 @@ namespace Ryujinx.HLE } } } -} \ No newline at end of file +} diff --git a/Ryujinx/Config.cs b/Ryujinx/Config.cs index b63455ee8..095682172 100644 --- a/Ryujinx/Config.cs +++ b/Ryujinx/Config.cs @@ -1,4 +1,4 @@ -using Ryujinx.UI.Input; +using Ryujinx.UI.Input; using Ryujinx.HLE.Logging; using System; using System.Globalization; @@ -180,4 +180,4 @@ namespace Ryujinx return Values.TryGetValue(Name, out string Value) ? Value : null; } } -} +} \ No newline at end of file diff --git a/Ryujinx/Ryujinx.conf b/Ryujinx/Ryujinx.conf index 3225acc34..6e15a6ac2 100644 --- a/Ryujinx/Ryujinx.conf +++ b/Ryujinx/Ryujinx.conf @@ -85,4 +85,4 @@ Controls_Right_JoyConController_Button_R = RShoulder Controls_Right_JoyConController_Button_ZR = RTrigger Controls_Left_JoyConController_Stick = LJoystick -Controls_Right_JoyConController_Stick = RJoystick +Controls_Right_JoyConController_Stick = RJoystick \ No newline at end of file