From 7cda630aba8b99b65379c0ef0e92483491e4fbb3 Mon Sep 17 00:00:00 2001 From: LDj3SNuD <35856442+LDj3SNuD@users.noreply.github.com> Date: Mon, 30 Apr 2018 01:39:58 +0200 Subject: [PATCH] Add Sqxtn_S, Sqxtn_V, Uqxtn_S, Uqxtn_V instructions and Tests (6). (#110) * Update ILGeneratorEx.cs * Update AOpCodeTable.cs * Update AInstEmitSimdArithmetic.cs * Update CpuTestSimd.cs * Update CpuTestSimdReg.cs * Update CpuTest.cs * Update Pseudocode.cs * Update Instructions.cs * Update AInstEmitSimdArithmetic.cs * Update AInstEmitSimdArithmetic.cs * Update AInstEmitSimdArithmetic.cs --- ChocolArm64/AOpCodeTable.cs | 4 + .../Instruction/AInstEmitSimdArithmetic.cs | 98 ++++++++ ChocolArm64/Translation/ILGeneratorEx.cs | 4 +- Ryujinx.Tests/Cpu/CpuTest.cs | 9 +- Ryujinx.Tests/Cpu/CpuTestSimd.cs | 218 ++++++++++++++-- Ryujinx.Tests/Cpu/CpuTestSimdReg.cs | 84 +++++-- Ryujinx.Tests/Cpu/Tester/Instructions.cs | 232 ++++++++++++++++++ Ryujinx.Tests/Cpu/Tester/Pseudocode.cs | 67 +++++ 8 files changed, 673 insertions(+), 43 deletions(-) diff --git a/ChocolArm64/AOpCodeTable.cs b/ChocolArm64/AOpCodeTable.cs index be22de1e..64b7e10f 100644 --- a/ChocolArm64/AOpCodeTable.cs +++ b/ChocolArm64/AOpCodeTable.cs @@ -337,6 +337,8 @@ namespace ChocolArm64 Set("0x001110<<1xxxxx011011xxxxxxxxxx", AInstEmit.Smin_V, typeof(AOpCodeSimdReg)); Set("0x001110<<1xxxxx100000xxxxxxxxxx", AInstEmit.Smlal_V, typeof(AOpCodeSimdReg)); Set("0x001110<<1xxxxx110000xxxxxxxxxx", AInstEmit.Smull_V, typeof(AOpCodeSimdReg)); + Set("01011110<<100001010010xxxxxxxxxx", AInstEmit.Sqxtn_S, typeof(AOpCodeSimd)); + Set("0x001110<<100001010010xxxxxxxxxx", AInstEmit.Sqxtn_V, typeof(AOpCodeSimd)); Set("0>001110<<1xxxxx010001xxxxxxxxxx", AInstEmit.Sshl_V, typeof(AOpCodeSimdReg)); Set("0x00111100>>>xxx101001xxxxxxxxxx", AInstEmit.Sshll_V, typeof(AOpCodeSimdShImm)); Set("010111110>>>>xxx000001xxxxxxxxxx", AInstEmit.Sshr_S, typeof(AOpCodeSimdShImm)); @@ -370,6 +372,8 @@ namespace ChocolArm64 Set("0x101110<<1xxxxx000001xxxxxxxxxx", AInstEmit.Uhadd_V, typeof(AOpCodeSimdReg)); Set("0x001110000xxxxx001111xxxxxxxxxx", AInstEmit.Umov_S, typeof(AOpCodeSimdIns)); Set("0x101110<<1xxxxx110000xxxxxxxxxx", AInstEmit.Umull_V, typeof(AOpCodeSimdReg)); + Set("01111110<<100001010010xxxxxxxxxx", AInstEmit.Uqxtn_S, typeof(AOpCodeSimd)); + Set("0x101110<<100001010010xxxxxxxxxx", AInstEmit.Uqxtn_V, typeof(AOpCodeSimd)); Set("0>101110<<1xxxxx010001xxxxxxxxxx", AInstEmit.Ushl_V, typeof(AOpCodeSimdReg)); Set("0x10111100>>>xxx101001xxxxxxxxxx", AInstEmit.Ushll_V, typeof(AOpCodeSimdShImm)); Set("011111110>>>>xxx000001xxxxxxxxxx", AInstEmit.Ushr_S, typeof(AOpCodeSimdShImm)); diff --git a/ChocolArm64/Instruction/AInstEmitSimdArithmetic.cs b/ChocolArm64/Instruction/AInstEmitSimdArithmetic.cs index f4dcf864..3a4b2210 100644 --- a/ChocolArm64/Instruction/AInstEmitSimdArithmetic.cs +++ b/ChocolArm64/Instruction/AInstEmitSimdArithmetic.cs @@ -205,6 +205,84 @@ namespace ChocolArm64.Instruction } } + private static void EmitQxtn(AILEmitterCtx Context, bool Signed, bool Scalar) + { + AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp; + + int Elems = (!Scalar ? 8 >> Op.Size : 1); + int ESize = 8 << Op.Size; + + int TMaxValue = (Signed ? (1 << (ESize - 1)) - 1 : (int)((1L << ESize) - 1L)); + int TMinValue = (Signed ? -((1 << (ESize - 1))) : 0); + + int Part = (!Scalar & (Op.RegisterSize == ARegisterSize.SIMD128) ? Elems : 0); + + Context.EmitLdc_I8(0L); + Context.EmitSttmp(); + + for (int Index = 0; Index < Elems; Index++) + { + AILLabel LblLe = new AILLabel(); + AILLabel LblGeEnd = new AILLabel(); + + EmitVectorExtract(Context, Op.Rn, Index, Op.Size + 1, Signed); + + Context.Emit(OpCodes.Dup); + + Context.EmitLdc_I4(TMaxValue); + Context.Emit(OpCodes.Conv_U8); + + Context.Emit(Signed ? OpCodes.Ble_S : OpCodes.Ble_Un_S, LblLe); + + Context.Emit(OpCodes.Pop); + + Context.EmitLdc_I4(TMaxValue); + + Context.EmitLdc_I8(0x8000000L); + Context.EmitSttmp(); + + Context.Emit(OpCodes.Br_S, LblGeEnd); + + Context.MarkLabel(LblLe); + + Context.Emit(OpCodes.Dup); + + Context.EmitLdc_I4(TMinValue); + Context.Emit(OpCodes.Conv_I8); + + Context.Emit(Signed ? OpCodes.Bge_S : OpCodes.Bge_Un_S, LblGeEnd); + + Context.Emit(OpCodes.Pop); + + Context.EmitLdc_I4(TMinValue); + + Context.EmitLdc_I8(0x8000000L); + Context.EmitSttmp(); + + Context.MarkLabel(LblGeEnd); + + if (Scalar) + { + EmitVectorZeroLower(Context, Op.Rd); + } + + EmitVectorInsert(Context, Op.Rd, Part + Index, Op.Size); + } + + if (Part == 0) + { + EmitVectorZeroUpper(Context, Op.Rd); + } + + Context.EmitLdarg(ATranslatedSub.StateArgIdx); + Context.EmitLdarg(ATranslatedSub.StateArgIdx); + Context.EmitCallPropGet(typeof(AThreadState), nameof(AThreadState.Fpsr)); + Context.EmitLdtmp(); + Context.Emit(OpCodes.Conv_I4); + Context.Emit(OpCodes.Or); + Context.EmitCallPropSet(typeof(AThreadState), nameof(AThreadState.Fpsr)); + } + public static void Fabd_S(AILEmitterCtx Context) { EmitScalarBinaryOpF(Context, () => @@ -971,6 +1049,16 @@ namespace ChocolArm64.Instruction EmitVectorWidenRnRmBinaryOpSx(Context, () => Context.Emit(OpCodes.Mul)); } + public static void Sqxtn_S(AILEmitterCtx Context) + { + EmitQxtn(Context, Signed: true, Scalar: true); + } + + public static void Sqxtn_V(AILEmitterCtx Context) + { + EmitQxtn(Context, Signed: true, Scalar: false); + } + public static void Sub_S(AILEmitterCtx Context) { EmitScalarBinaryOpZx(Context, () => Context.Emit(OpCodes.Sub)); @@ -1049,5 +1137,15 @@ namespace ChocolArm64.Instruction { EmitVectorWidenRnRmBinaryOpZx(Context, () => Context.Emit(OpCodes.Mul)); } + + public static void Uqxtn_S(AILEmitterCtx Context) + { + EmitQxtn(Context, Signed: false, Scalar: true); + } + + public static void Uqxtn_V(AILEmitterCtx Context) + { + EmitQxtn(Context, Signed: false, Scalar: false); + } } } diff --git a/ChocolArm64/Translation/ILGeneratorEx.cs b/ChocolArm64/Translation/ILGeneratorEx.cs index 61299308..52eb4746 100644 --- a/ChocolArm64/Translation/ILGeneratorEx.cs +++ b/ChocolArm64/Translation/ILGeneratorEx.cs @@ -6,7 +6,7 @@ namespace ChocolArm64 static class ILGeneratorEx { - public static void EmitLdc_I4(this ILGenerator Generator,int Value) + public static void EmitLdc_I4(this ILGenerator Generator, int Value) { switch (Value) { @@ -126,4 +126,4 @@ namespace ChocolArm64 } } } -} \ No newline at end of file +} diff --git a/Ryujinx.Tests/Cpu/CpuTest.cs b/Ryujinx.Tests/Cpu/CpuTest.cs index 14ef0a15..67e836f0 100644 --- a/Ryujinx.Tests/Cpu/CpuTest.cs +++ b/Ryujinx.Tests/Cpu/CpuTest.cs @@ -55,7 +55,8 @@ namespace Ryujinx.Tests.Cpu protected void SetThreadState(ulong X0 = 0, ulong X1 = 0, ulong X2 = 0, ulong X3 = 0, ulong X31 = 0, AVec V0 = default(AVec), AVec V1 = default(AVec), AVec V2 = default(AVec), - bool Overflow = false, bool Carry = false, bool Zero = false, bool Negative = false, int Fpcr = 0x0) + bool Overflow = false, bool Carry = false, bool Zero = false, bool Negative = false, + int Fpcr = 0x0, int Fpsr = 0x0) { Thread.ThreadState.X0 = X0; Thread.ThreadState.X1 = X1; @@ -70,6 +71,7 @@ namespace Ryujinx.Tests.Cpu Thread.ThreadState.Zero = Zero; Thread.ThreadState.Negative = Negative; Thread.ThreadState.Fpcr = Fpcr; + Thread.ThreadState.Fpsr = Fpsr; } protected void ExecuteOpcodes() @@ -92,12 +94,13 @@ namespace Ryujinx.Tests.Cpu protected AThreadState SingleOpcode(uint Opcode, ulong X0 = 0, ulong X1 = 0, ulong X2 = 0, ulong X3 = 0, ulong X31 = 0, AVec V0 = default(AVec), AVec V1 = default(AVec), AVec V2 = default(AVec), - bool Overflow = false, bool Carry = false, bool Zero = false, bool Negative = false, int Fpcr = 0x0) + bool Overflow = false, bool Carry = false, bool Zero = false, bool Negative = false, + int Fpcr = 0x0, int Fpsr = 0x0) { this.Opcode(Opcode); this.Opcode(0xD4200000); // BRK #0 this.Opcode(0xD65F03C0); // RET - SetThreadState(X0, X1, X2, X3, X31, V0, V1, V2, Overflow, Carry, Zero, Negative, Fpcr); + SetThreadState(X0, X1, X2, X3, X31, V0, V1, V2, Overflow, Carry, Zero, Negative, Fpcr, Fpsr); ExecuteOpcodes(); return GetThreadState(); diff --git a/Ryujinx.Tests/Cpu/CpuTestSimd.cs b/Ryujinx.Tests/Cpu/CpuTestSimd.cs index c41301c7..95fca86a 100644 --- a/Ryujinx.Tests/Cpu/CpuTestSimd.cs +++ b/Ryujinx.Tests/Cpu/CpuTestSimd.cs @@ -26,6 +26,23 @@ namespace Ryujinx.Tests.Cpu 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul }; } + private static ulong[] _1H1S1D_() + { + return new ulong[] { 0x0000000000000000ul, 0x0000000000007FFFul, + 0x0000000000008000ul, 0x000000000000FFFFul, + 0x000000007FFFFFFFul, 0x0000000080000000ul, + 0x00000000FFFFFFFFul, 0x7FFFFFFFFFFFFFFFul, + 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul }; + } + + private static ulong[] _4H2S1D_() + { + return new ulong[] { 0x0000000000000000ul, 0x7FFF7FFF7FFF7FFFul, + 0x8000800080008000ul, 0x7FFFFFFF7FFFFFFFul, + 0x8000000080000000ul, 0x7FFFFFFFFFFFFFFFul, + 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul }; + } + private static ulong[] _8B4H_() { return new ulong[] { 0x0000000000000000ul, 0x7F7F7F7F7F7F7F7Ful, @@ -64,8 +81,11 @@ namespace Ryujinx.Tests.Cpu AArch64.V(1, new Bits(A)); SimdFp.Abs_S(Op[23, 22], Op[9, 5], Op[4, 0]); - Assert.That(ThreadState.V0.X0, Is.EqualTo(AArch64.V(64, 0).ToUInt64())); - Assert.That(ThreadState.V0.X1, Is.Zero); + Assert.Multiple(() => + { + Assert.That(ThreadState.V0.X0, Is.EqualTo(AArch64.V(64, 0).ToUInt64())); + Assert.That(ThreadState.V0.X1, Is.Zero); + }); } [Test, Description("ABS ., .")] @@ -83,8 +103,11 @@ namespace Ryujinx.Tests.Cpu AArch64.V(1, new Bits(A)); SimdFp.Abs_V(Op[30], Op[23, 22], Op[9, 5], Op[4, 0]); - Assert.That(ThreadState.V0.X0, Is.EqualTo(AArch64.V(64, 0).ToUInt64())); - Assert.That(ThreadState.V0.X1, Is.Zero); + Assert.Multiple(() => + { + Assert.That(ThreadState.V0.X0, Is.EqualTo(AArch64.V(64, 0).ToUInt64())); + Assert.That(ThreadState.V0.X1, Is.Zero); + }); } [Test, Pairwise, Description("ABS ., .")] @@ -149,8 +172,11 @@ namespace Ryujinx.Tests.Cpu AArch64.V(1, new Bits(A)); SimdFp.Addv_V(Op[30], Op[23, 22], Op[9, 5], Op[4, 0]); - Assert.That(ThreadState.V0.X0, Is.EqualTo(AArch64.V(64, 0).ToUInt64())); - Assert.That(ThreadState.V0.X1, Is.Zero); + Assert.Multiple(() => + { + Assert.That(ThreadState.V0.X0, Is.EqualTo(AArch64.V(64, 0).ToUInt64())); + Assert.That(ThreadState.V0.X1, Is.Zero); + }); } [Test, Pairwise, Description("ADDV , .")] @@ -194,8 +220,11 @@ namespace Ryujinx.Tests.Cpu AArch64.V(1, new Bits(A)); SimdFp.Cls_V(Op[30], Op[23, 22], Op[9, 5], Op[4, 0]); - Assert.That(ThreadState.V0.X0, Is.EqualTo(AArch64.V(64, 0).ToUInt64())); - Assert.That(ThreadState.V0.X1, Is.Zero); + Assert.Multiple(() => + { + Assert.That(ThreadState.V0.X0, Is.EqualTo(AArch64.V(64, 0).ToUInt64())); + Assert.That(ThreadState.V0.X1, Is.Zero); + }); } [Test, Pairwise, Description("CLS ., .")] @@ -236,8 +265,11 @@ namespace Ryujinx.Tests.Cpu AArch64.V(1, new Bits(A)); SimdFp.Clz_V(Op[30], Op[23, 22], Op[9, 5], Op[4, 0]); - Assert.That(ThreadState.V0.X0, Is.EqualTo(AArch64.V(64, 0).ToUInt64())); - Assert.That(ThreadState.V0.X1, Is.Zero); + Assert.Multiple(() => + { + Assert.That(ThreadState.V0.X0, Is.EqualTo(AArch64.V(64, 0).ToUInt64())); + Assert.That(ThreadState.V0.X1, Is.Zero); + }); } [Test, Pairwise, Description("CLZ ., .")] @@ -276,8 +308,11 @@ namespace Ryujinx.Tests.Cpu AArch64.V(1, new Bits(A)); SimdFp.Neg_S(Op[23, 22], Op[9, 5], Op[4, 0]); - Assert.That(ThreadState.V0.X0, Is.EqualTo(AArch64.V(64, 0).ToUInt64())); - Assert.That(ThreadState.V0.X1, Is.Zero); + Assert.Multiple(() => + { + Assert.That(ThreadState.V0.X0, Is.EqualTo(AArch64.V(64, 0).ToUInt64())); + Assert.That(ThreadState.V0.X1, Is.Zero); + }); } [Test, Description("NEG ., .")] @@ -295,8 +330,11 @@ namespace Ryujinx.Tests.Cpu AArch64.V(1, new Bits(A)); SimdFp.Neg_V(Op[30], Op[23, 22], Op[9, 5], Op[4, 0]); - Assert.That(ThreadState.V0.X0, Is.EqualTo(AArch64.V(64, 0).ToUInt64())); - Assert.That(ThreadState.V0.X1, Is.Zero); + Assert.Multiple(() => + { + Assert.That(ThreadState.V0.X0, Is.EqualTo(AArch64.V(64, 0).ToUInt64())); + Assert.That(ThreadState.V0.X1, Is.Zero); + }); } [Test, Pairwise, Description("NEG ., .")] @@ -321,6 +359,158 @@ namespace Ryujinx.Tests.Cpu Assert.That(ThreadState.V0.X1, Is.EqualTo(AArch64.Vpart(64, 0, 1).ToUInt64())); }); } + + [Test, Description("SQXTN , ")] + public void Sqxtn_S_HB_SH_DS([ValueSource("_1H1S1D_")] [Random(1)] ulong A, + [Values(0b00u, 0b01u, 0b10u)] uint size) // + { + uint Opcode = 0x5E214820; // SQXTN B0, H1 + Opcode |= ((size & 3) << 22); + Bits Op = new Bits(Opcode); + + AVec V0 = new AVec { X0 = TestContext.CurrentContext.Random.NextULong(), + X1 = TestContext.CurrentContext.Random.NextULong() }; + AVec V1 = new AVec { X0 = A }; + AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1); + + AArch64.Vpart(0, 0, new Bits(TestContext.CurrentContext.Random.NextULong())); + AArch64.V(1, new Bits(A)); + SimdFp.Sqxtn_S(Op[23, 22], Op[9, 5], Op[4, 0]); + + Assert.Multiple(() => + { + Assert.That(ThreadState.V0.X0, Is.EqualTo(AArch64.V(64, 0).ToUInt64())); + Assert.That(ThreadState.V0.X1, Is.Zero); + }); + Assert.That(((ThreadState.Fpsr >> 27) & 1) != 0, Is.EqualTo(Shared.FPSR[27])); // FIXME: Temporary solution. + } + + [Test, Pairwise, Description("SQXTN{2} ., .")] + public void Sqxtn_V_8H8B_4S4H_2D2S([ValueSource("_4H2S1D_")] [Random(1)] ulong A0, + [ValueSource("_4H2S1D_")] [Random(1)] ulong A1, + [Values(0b00u, 0b01u, 0b10u)] uint size) // <8H8B, 4S4H, 2D2S> + { + uint Opcode = 0x0E214820; // SQXTN V0.8B, V1.8H + Opcode |= ((size & 3) << 22); + Bits Op = new Bits(Opcode); + + AVec V0 = new AVec { X1 = TestContext.CurrentContext.Random.NextULong() }; + AVec V1 = new AVec { X0 = A0, X1 = A1 }; + AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1); + + AArch64.Vpart(1, 0, new Bits(A0)); + AArch64.Vpart(1, 1, new Bits(A1)); + SimdFp.Sqxtn_V(Op[30], Op[23, 22], Op[9, 5], Op[4, 0]); + + Assert.Multiple(() => + { + Assert.That(ThreadState.V0.X0, Is.EqualTo(AArch64.V(64, 0).ToUInt64())); + Assert.That(ThreadState.V0.X1, Is.Zero); + }); + Assert.That(((ThreadState.Fpsr >> 27) & 1) != 0, Is.EqualTo(Shared.FPSR[27])); // FIXME: Temporary solution. + } + + [Test, Pairwise, Description("SQXTN{2} ., .")] + public void Sqxtn_V_8H16B_4S8H_2D4S([ValueSource("_4H2S1D_")] [Random(1)] ulong A0, + [ValueSource("_4H2S1D_")] [Random(1)] ulong A1, + [Values(0b00u, 0b01u, 0b10u)] uint size) // <8H16B, 4S8H, 2D4S> + { + uint Opcode = 0x4E214820; // SQXTN2 V0.16B, V1.8H + Opcode |= ((size & 3) << 22); + Bits Op = new Bits(Opcode); + + ulong _X0 = TestContext.CurrentContext.Random.NextULong(); + AVec V0 = new AVec { X0 = _X0 }; + AVec V1 = new AVec { X0 = A0, X1 = A1 }; + AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1); + + AArch64.Vpart(1, 0, new Bits(A0)); + AArch64.Vpart(1, 1, new Bits(A1)); + SimdFp.Sqxtn_V(Op[30], Op[23, 22], Op[9, 5], Op[4, 0]); + + Assert.Multiple(() => + { + Assert.That(ThreadState.V0.X0, Is.EqualTo(_X0)); + Assert.That(ThreadState.V0.X1, Is.EqualTo(AArch64.Vpart(64, 0, 1).ToUInt64())); + }); + Assert.That(((ThreadState.Fpsr >> 27) & 1) != 0, Is.EqualTo(Shared.FPSR[27])); // FIXME: Temporary solution. + } + + [Test, Description("UQXTN , ")] + public void Uqxtn_S_HB_SH_DS([ValueSource("_1H1S1D_")] [Random(1)] ulong A, + [Values(0b00u, 0b01u, 0b10u)] uint size) // + { + uint Opcode = 0x7E214820; // UQXTN B0, H1 + Opcode |= ((size & 3) << 22); + Bits Op = new Bits(Opcode); + + AVec V0 = new AVec { X0 = TestContext.CurrentContext.Random.NextULong(), + X1 = TestContext.CurrentContext.Random.NextULong() }; + AVec V1 = new AVec { X0 = A }; + AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1); + + AArch64.Vpart(0, 0, new Bits(TestContext.CurrentContext.Random.NextULong())); + AArch64.V(1, new Bits(A)); + SimdFp.Uqxtn_S(Op[23, 22], Op[9, 5], Op[4, 0]); + + Assert.Multiple(() => + { + Assert.That(ThreadState.V0.X0, Is.EqualTo(AArch64.V(64, 0).ToUInt64())); + Assert.That(ThreadState.V0.X1, Is.Zero); + }); + Assert.That(((ThreadState.Fpsr >> 27) & 1) != 0, Is.EqualTo(Shared.FPSR[27])); // FIXME: Temporary solution. + } + + [Test, Pairwise, Description("UQXTN{2} ., .")] + public void Uqxtn_V_8H8B_4S4H_2D2S([ValueSource("_4H2S1D_")] [Random(1)] ulong A0, + [ValueSource("_4H2S1D_")] [Random(1)] ulong A1, + [Values(0b00u, 0b01u, 0b10u)] uint size) // <8H8B, 4S4H, 2D2S> + { + uint Opcode = 0x2E214820; // UQXTN V0.8B, V1.8H + Opcode |= ((size & 3) << 22); + Bits Op = new Bits(Opcode); + + AVec V0 = new AVec { X1 = TestContext.CurrentContext.Random.NextULong() }; + AVec V1 = new AVec { X0 = A0, X1 = A1 }; + AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1); + + AArch64.Vpart(1, 0, new Bits(A0)); + AArch64.Vpart(1, 1, new Bits(A1)); + SimdFp.Uqxtn_V(Op[30], Op[23, 22], Op[9, 5], Op[4, 0]); + + Assert.Multiple(() => + { + Assert.That(ThreadState.V0.X0, Is.EqualTo(AArch64.V(64, 0).ToUInt64())); + Assert.That(ThreadState.V0.X1, Is.Zero); + }); + Assert.That(((ThreadState.Fpsr >> 27) & 1) != 0, Is.EqualTo(Shared.FPSR[27])); // FIXME: Temporary solution. + } + + [Test, Pairwise, Description("UQXTN{2} ., .")] + public void Uqxtn_V_8H16B_4S8H_2D4S([ValueSource("_4H2S1D_")] [Random(1)] ulong A0, + [ValueSource("_4H2S1D_")] [Random(1)] ulong A1, + [Values(0b00u, 0b01u, 0b10u)] uint size) // <8H16B, 4S8H, 2D4S> + { + uint Opcode = 0x6E214820; // UQXTN2 V0.16B, V1.8H + Opcode |= ((size & 3) << 22); + Bits Op = new Bits(Opcode); + + ulong _X0 = TestContext.CurrentContext.Random.NextULong(); + AVec V0 = new AVec { X0 = _X0 }; + AVec V1 = new AVec { X0 = A0, X1 = A1 }; + AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1); + + AArch64.Vpart(1, 0, new Bits(A0)); + AArch64.Vpart(1, 1, new Bits(A1)); + SimdFp.Uqxtn_V(Op[30], Op[23, 22], Op[9, 5], Op[4, 0]); + + Assert.Multiple(() => + { + Assert.That(ThreadState.V0.X0, Is.EqualTo(_X0)); + Assert.That(ThreadState.V0.X1, Is.EqualTo(AArch64.Vpart(64, 0, 1).ToUInt64())); + }); + Assert.That(((ThreadState.Fpsr >> 27) & 1) != 0, Is.EqualTo(Shared.FPSR[27])); // FIXME: Temporary solution. + } #endif } } diff --git a/Ryujinx.Tests/Cpu/CpuTestSimdReg.cs b/Ryujinx.Tests/Cpu/CpuTestSimdReg.cs index 32218180..e9d53eb7 100644 --- a/Ryujinx.Tests/Cpu/CpuTestSimdReg.cs +++ b/Ryujinx.Tests/Cpu/CpuTestSimdReg.cs @@ -74,8 +74,11 @@ namespace Ryujinx.Tests.Cpu AArch64.V(2, new Bits(B)); SimdFp.Add_S(Op[23, 22], Op[20, 16], Op[9, 5], Op[4, 0]); - Assert.That(ThreadState.V0.X0, Is.EqualTo(AArch64.V(64, 0).ToUInt64())); - Assert.That(ThreadState.V0.X1, Is.Zero); + Assert.Multiple(() => + { + Assert.That(ThreadState.V0.X0, Is.EqualTo(AArch64.V(64, 0).ToUInt64())); + Assert.That(ThreadState.V0.X1, Is.Zero); + }); } [Test, Description("ADD ., ., .")] @@ -96,8 +99,11 @@ namespace Ryujinx.Tests.Cpu AArch64.V(2, new Bits(B)); SimdFp.Add_V(Op[30], Op[23, 22], Op[20, 16], Op[9, 5], Op[4, 0]); - Assert.That(ThreadState.V0.X0, Is.EqualTo(AArch64.V(64, 0).ToUInt64())); - Assert.That(ThreadState.V0.X1, Is.Zero); + Assert.Multiple(() => + { + Assert.That(ThreadState.V0.X0, Is.EqualTo(AArch64.V(64, 0).ToUInt64())); + Assert.That(ThreadState.V0.X1, Is.Zero); + }); } [Test, Pairwise, Description("ADD ., ., .")] @@ -205,8 +211,11 @@ namespace Ryujinx.Tests.Cpu AArch64.V(2, new Bits(B)); SimdFp.Addp_V(Op[30], Op[23, 22], Op[20, 16], Op[9, 5], Op[4, 0]); - Assert.That(ThreadState.V0.X0, Is.EqualTo(AArch64.V(64, 0).ToUInt64())); - Assert.That(ThreadState.V0.X1, Is.Zero); + Assert.Multiple(() => + { + Assert.That(ThreadState.V0.X0, Is.EqualTo(AArch64.V(64, 0).ToUInt64())); + Assert.That(ThreadState.V0.X1, Is.Zero); + }); } [Test, Pairwise, Description("ADDP ., ., .")] @@ -253,8 +262,11 @@ namespace Ryujinx.Tests.Cpu AArch64.V(2, new Bits(B)); SimdFp.And_V(Op[30], Op[20, 16], Op[9, 5], Op[4, 0]); - Assert.That(ThreadState.V0.X0, Is.EqualTo(AArch64.V(64, 0).ToUInt64())); - Assert.That(ThreadState.V0.X1, Is.Zero); + Assert.Multiple(() => + { + Assert.That(ThreadState.V0.X0, Is.EqualTo(AArch64.V(64, 0).ToUInt64())); + Assert.That(ThreadState.V0.X1, Is.Zero); + }); } [Test, Pairwise, Description("AND ., ., .")] @@ -299,8 +311,11 @@ namespace Ryujinx.Tests.Cpu AArch64.V(2, new Bits(B)); SimdFp.Bic_V(Op[30], Op[20, 16], Op[9, 5], Op[4, 0]); - Assert.That(ThreadState.V0.X0, Is.EqualTo(AArch64.V(64, 0).ToUInt64())); - Assert.That(ThreadState.V0.X1, Is.Zero); + Assert.Multiple(() => + { + Assert.That(ThreadState.V0.X0, Is.EqualTo(AArch64.V(64, 0).ToUInt64())); + Assert.That(ThreadState.V0.X1, Is.Zero); + }); } [Test, Pairwise, Description("BIC ., ., .")] @@ -347,8 +362,11 @@ namespace Ryujinx.Tests.Cpu AArch64.V(2, new Bits(B)); SimdFp.Bif_V(Op[30], Op[20, 16], Op[9, 5], Op[4, 0]); - Assert.That(ThreadState.V0.X0, Is.EqualTo(AArch64.V(64, 0).ToUInt64())); - Assert.That(ThreadState.V0.X1, Is.Zero); + Assert.Multiple(() => + { + Assert.That(ThreadState.V0.X0, Is.EqualTo(AArch64.V(64, 0).ToUInt64())); + Assert.That(ThreadState.V0.X1, Is.Zero); + }); } [Test, Pairwise, Description("BIF ., ., .")] @@ -400,8 +418,11 @@ namespace Ryujinx.Tests.Cpu AArch64.V(2, new Bits(B)); SimdFp.Bit_V(Op[30], Op[20, 16], Op[9, 5], Op[4, 0]); - Assert.That(ThreadState.V0.X0, Is.EqualTo(AArch64.V(64, 0).ToUInt64())); - Assert.That(ThreadState.V0.X1, Is.Zero); + Assert.Multiple(() => + { + Assert.That(ThreadState.V0.X0, Is.EqualTo(AArch64.V(64, 0).ToUInt64())); + Assert.That(ThreadState.V0.X1, Is.Zero); + }); } [Test, Pairwise, Description("BIT ., ., .")] @@ -453,8 +474,11 @@ namespace Ryujinx.Tests.Cpu AArch64.V(2, new Bits(B)); SimdFp.Bsl_V(Op[30], Op[20, 16], Op[9, 5], Op[4, 0]); - Assert.That(ThreadState.V0.X0, Is.EqualTo(AArch64.V(64, 0).ToUInt64())); - Assert.That(ThreadState.V0.X1, Is.Zero); + Assert.Multiple(() => + { + Assert.That(ThreadState.V0.X0, Is.EqualTo(AArch64.V(64, 0).ToUInt64())); + Assert.That(ThreadState.V0.X1, Is.Zero); + }); } [Test, Pairwise, Description("BSL ., ., .")] @@ -504,8 +528,11 @@ namespace Ryujinx.Tests.Cpu AArch64.V(2, new Bits(B)); SimdFp.Orn_V(Op[30], Op[20, 16], Op[9, 5], Op[4, 0]); - Assert.That(ThreadState.V0.X0, Is.EqualTo(AArch64.V(64, 0).ToUInt64())); - Assert.That(ThreadState.V0.X1, Is.Zero); + Assert.Multiple(() => + { + Assert.That(ThreadState.V0.X0, Is.EqualTo(AArch64.V(64, 0).ToUInt64())); + Assert.That(ThreadState.V0.X1, Is.Zero); + }); } [Test, Pairwise, Description("ORN ., ., .")] @@ -550,8 +577,11 @@ namespace Ryujinx.Tests.Cpu AArch64.V(2, new Bits(B)); SimdFp.Orr_V(Op[30], Op[20, 16], Op[9, 5], Op[4, 0]); - Assert.That(ThreadState.V0.X0, Is.EqualTo(AArch64.V(64, 0).ToUInt64())); - Assert.That(ThreadState.V0.X1, Is.Zero); + Assert.Multiple(() => + { + Assert.That(ThreadState.V0.X0, Is.EqualTo(AArch64.V(64, 0).ToUInt64())); + Assert.That(ThreadState.V0.X1, Is.Zero); + }); } [Test, Pairwise, Description("ORR ., ., .")] @@ -714,8 +744,11 @@ namespace Ryujinx.Tests.Cpu AArch64.V(2, new Bits(B)); SimdFp.Sub_S(Op[23, 22], Op[20, 16], Op[9, 5], Op[4, 0]); - Assert.That(ThreadState.V0.X0, Is.EqualTo(AArch64.V(64, 0).ToUInt64())); - Assert.That(ThreadState.V0.X1, Is.Zero); + Assert.Multiple(() => + { + Assert.That(ThreadState.V0.X0, Is.EqualTo(AArch64.V(64, 0).ToUInt64())); + Assert.That(ThreadState.V0.X1, Is.Zero); + }); } [Test, Description("SUB ., ., .")] @@ -736,8 +769,11 @@ namespace Ryujinx.Tests.Cpu AArch64.V(2, new Bits(B)); SimdFp.Sub_V(Op[30], Op[23, 22], Op[20, 16], Op[9, 5], Op[4, 0]); - Assert.That(ThreadState.V0.X0, Is.EqualTo(AArch64.V(64, 0).ToUInt64())); - Assert.That(ThreadState.V0.X1, Is.Zero); + Assert.Multiple(() => + { + Assert.That(ThreadState.V0.X0, Is.EqualTo(AArch64.V(64, 0).ToUInt64())); + Assert.That(ThreadState.V0.X1, Is.Zero); + }); } [Test, Pairwise, Description("SUB ., ., .")] diff --git a/Ryujinx.Tests/Cpu/Tester/Instructions.cs b/Ryujinx.Tests/Cpu/Tester/Instructions.cs index 7439aa83..efea80bb 100644 --- a/Ryujinx.Tests/Cpu/Tester/Instructions.cs +++ b/Ryujinx.Tests/Cpu/Tester/Instructions.cs @@ -21,6 +21,7 @@ namespace Ryujinx.Tests.Cpu.Tester /* Decode */ int d = (int)UInt(Rd); int n = (int)UInt(Rn); + int datasize = (sf ? 64 : 32); /* Operation */ @@ -37,6 +38,7 @@ namespace Ryujinx.Tests.Cpu.Tester /* Decode */ int d = (int)UInt(Rd); int n = (int)UInt(Rn); + int datasize = (sf ? 64 : 32); /* Operation */ @@ -53,6 +55,7 @@ namespace Ryujinx.Tests.Cpu.Tester /* Decode */ int d = (int)UInt(Rd); int n = (int)UInt(Rn); + int datasize = (sf ? 64 : 32); /* Operation */ @@ -75,6 +78,7 @@ namespace Ryujinx.Tests.Cpu.Tester /* Decode */ int d = (int)UInt(Rd); int n = (int)UInt(Rn); + int datasize = (sf ? 64 : 32); int container_size = 16; @@ -113,6 +117,7 @@ namespace Ryujinx.Tests.Cpu.Tester /* Decode */ int d = (int)UInt(Rd); int n = (int)UInt(Rn); + int datasize = (sf ? 64 : 32); int container_size = 32; @@ -187,6 +192,7 @@ namespace Ryujinx.Tests.Cpu.Tester /* Decode */ int d = (int)UInt(Rd); int n = (int)UInt(Rn); + int datasize = (sf ? 64 : 32); Bits imm; @@ -225,6 +231,7 @@ namespace Ryujinx.Tests.Cpu.Tester /* Decode */ int d = (int)UInt(Rd); int n = (int)UInt(Rn); + int datasize = (sf ? 64 : 32); Bits imm; @@ -259,6 +266,7 @@ namespace Ryujinx.Tests.Cpu.Tester /* Decode */ int d = (int)UInt(Rd); int n = (int)UInt(Rn); + int datasize = (sf ? 64 : 32); Bits imm; @@ -288,6 +296,7 @@ namespace Ryujinx.Tests.Cpu.Tester /* Decode */ int d = (int)UInt(Rd); int n = (int)UInt(Rn); + int datasize = (sf ? 64 : 32); Bits imm; @@ -312,6 +321,7 @@ namespace Ryujinx.Tests.Cpu.Tester /* Decode */ int d = (int)UInt(Rd); int n = (int)UInt(Rn); + int datasize = (sf ? 64 : 32); Bits imm; @@ -341,6 +351,7 @@ namespace Ryujinx.Tests.Cpu.Tester /* Decode */ int d = (int)UInt(Rd); int n = (int)UInt(Rn); + int datasize = (sf ? 64 : 32); Bits imm; @@ -370,6 +381,7 @@ namespace Ryujinx.Tests.Cpu.Tester /* Decode */ int d = (int)UInt(Rd); int n = (int)UInt(Rn); + int datasize = (sf ? 64 : 32); Bits imm; @@ -409,6 +421,7 @@ namespace Ryujinx.Tests.Cpu.Tester /* Decode */ int d = (int)UInt(Rd); int n = (int)UInt(Rn); + int datasize = (sf ? 64 : 32); Bits imm; @@ -447,6 +460,7 @@ namespace Ryujinx.Tests.Cpu.Tester int d = (int)UInt(Rd); int n = (int)UInt(Rn); int m = (int)UInt(Rm); + int datasize = (sf ? 64 : 32); /* Operation */ @@ -466,6 +480,7 @@ namespace Ryujinx.Tests.Cpu.Tester int d = (int)UInt(Rd); int n = (int)UInt(Rn); int m = (int)UInt(Rm); + int datasize = (sf ? 64 : 32); /* Operation */ @@ -488,6 +503,7 @@ namespace Ryujinx.Tests.Cpu.Tester int d = (int)UInt(Rd); int n = (int)UInt(Rn); int m = (int)UInt(Rm); + int datasize = (sf ? 64 : 32); /* if shift == '11' then ReservedValue(); */ @@ -513,6 +529,7 @@ namespace Ryujinx.Tests.Cpu.Tester int d = (int)UInt(Rd); int n = (int)UInt(Rn); int m = (int)UInt(Rm); + int datasize = (sf ? 64 : 32); /* if shift == '11' then ReservedValue(); */ @@ -541,6 +558,7 @@ namespace Ryujinx.Tests.Cpu.Tester int d = (int)UInt(Rd); int n = (int)UInt(Rn); int m = (int)UInt(Rm); + int datasize = (sf ? 64 : 32); /* if sf == '0' && imm6<5> == '1' then ReservedValue(); */ @@ -564,6 +582,7 @@ namespace Ryujinx.Tests.Cpu.Tester int d = (int)UInt(Rd); int n = (int)UInt(Rn); int m = (int)UInt(Rm); + int datasize = (sf ? 64 : 32); /* if sf == '0' && imm6<5> == '1' then ReservedValue(); */ @@ -591,6 +610,7 @@ namespace Ryujinx.Tests.Cpu.Tester int d = (int)UInt(Rd); int n = (int)UInt(Rn); int m = (int)UInt(Rm); + int datasize = (sf ? 64 : 32); ShiftType shift_type = DecodeShift(op2); @@ -610,6 +630,7 @@ namespace Ryujinx.Tests.Cpu.Tester int d = (int)UInt(Rd); int n = (int)UInt(Rn); int m = (int)UInt(Rm); + int datasize = (sf ? 64 : 32); /* if sf == '0' && imm6<5> == '1' then ReservedValue(); */ @@ -635,6 +656,7 @@ namespace Ryujinx.Tests.Cpu.Tester int d = (int)UInt(Rd); int n = (int)UInt(Rn); int m = (int)UInt(Rm); + int datasize = (sf ? 64 : 32); /* if sf == '0' && imm6<5> == '1' then ReservedValue(); */ @@ -716,6 +738,7 @@ namespace Ryujinx.Tests.Cpu.Tester int d = (int)UInt(Rd); int n = (int)UInt(Rn); int m = (int)UInt(Rm); + int datasize = (sf ? 64 : 32); /* if sf == '0' && imm6<5> == '1' then ReservedValue(); */ @@ -741,6 +764,7 @@ namespace Ryujinx.Tests.Cpu.Tester int d = (int)UInt(Rd); int n = (int)UInt(Rn); int m = (int)UInt(Rm); + int datasize = (sf ? 64 : 32); /* if sf == '0' && imm6<5> == '1' then ReservedValue(); */ @@ -764,6 +788,7 @@ namespace Ryujinx.Tests.Cpu.Tester int d = (int)UInt(Rd); int n = (int)UInt(Rn); int m = (int)UInt(Rm); + int datasize = (sf ? 64 : 32); /* if N != sf then UnallocatedEncoding(); */ @@ -790,6 +815,7 @@ namespace Ryujinx.Tests.Cpu.Tester int d = (int)UInt(Rd); int n = (int)UInt(Rn); int m = (int)UInt(Rm); + int datasize = (sf ? 64 : 32); ShiftType shift_type = DecodeShift(op2); @@ -811,6 +837,7 @@ namespace Ryujinx.Tests.Cpu.Tester int d = (int)UInt(Rd); int n = (int)UInt(Rn); int m = (int)UInt(Rm); + int datasize = (sf ? 64 : 32); ShiftType shift_type = DecodeShift(op2); @@ -830,6 +857,7 @@ namespace Ryujinx.Tests.Cpu.Tester int d = (int)UInt(Rd); int n = (int)UInt(Rn); int m = (int)UInt(Rm); + int datasize = (sf ? 64 : 32); /* if sf == '0' && imm6<5> == '1' then ReservedValue(); */ @@ -855,6 +883,7 @@ namespace Ryujinx.Tests.Cpu.Tester int d = (int)UInt(Rd); int n = (int)UInt(Rn); int m = (int)UInt(Rm); + int datasize = (sf ? 64 : 32); /* if sf == '0' && imm6<5> == '1' then ReservedValue(); */ @@ -880,6 +909,7 @@ namespace Ryujinx.Tests.Cpu.Tester int d = (int)UInt(Rd); int n = (int)UInt(Rn); int m = (int)UInt(Rm); + int datasize = (sf ? 64 : 32); ShiftType shift_type = DecodeShift(op2); @@ -899,6 +929,7 @@ namespace Ryujinx.Tests.Cpu.Tester int d = (int)UInt(Rd); int n = (int)UInt(Rn); int m = (int)UInt(Rm); + int datasize = (sf ? 64 : 32); /* Operation */ @@ -920,6 +951,7 @@ namespace Ryujinx.Tests.Cpu.Tester int d = (int)UInt(Rd); int n = (int)UInt(Rn); int m = (int)UInt(Rm); + int datasize = (sf ? 64 : 32); /* Operation */ @@ -944,6 +976,7 @@ namespace Ryujinx.Tests.Cpu.Tester int d = (int)UInt(Rd); int n = (int)UInt(Rn); int m = (int)UInt(Rm); + int datasize = (sf ? 64 : 32); /* Operation */ @@ -970,6 +1003,7 @@ namespace Ryujinx.Tests.Cpu.Tester int d = (int)UInt(Rd); int n = (int)UInt(Rn); int m = (int)UInt(Rm); + int datasize = (sf ? 64 : 32); /* if shift == '11' then ReservedValue(); */ @@ -997,6 +1031,7 @@ namespace Ryujinx.Tests.Cpu.Tester int d = (int)UInt(Rd); int n = (int)UInt(Rn); int m = (int)UInt(Rm); + int datasize = (sf ? 64 : 32); /* if shift == '11' then ReservedValue(); */ @@ -1027,6 +1062,7 @@ namespace Ryujinx.Tests.Cpu.Tester int d = (int)UInt(Rd); int n = (int)UInt(Rn); int m = (int)UInt(Rm); + int datasize = (sf ? 64 : 32); /* Operation */ @@ -1055,6 +1091,7 @@ namespace Ryujinx.Tests.Cpu.Tester int d = (int)UInt(Rd); int n = (int)UInt(Rn); int m = (int)UInt(Rm); + int datasize = (sf ? 64 : 32); ExtendType extend_type = DecodeRegExtend(option); @@ -1086,6 +1123,7 @@ namespace Ryujinx.Tests.Cpu.Tester int d = (int)UInt(Rd); int n = (int)UInt(Rn); int m = (int)UInt(Rm); + int datasize = (sf ? 64 : 32); ExtendType extend_type = DecodeRegExtend(option); @@ -1113,6 +1151,7 @@ namespace Ryujinx.Tests.Cpu.Tester int d = (int)UInt(Rd); int n = (int)UInt(Rn); int m = (int)UInt(Rm); + int datasize = (sf ? 64 : 32); ExtendType extend_type = DecodeRegExtend(option); @@ -1146,6 +1185,7 @@ namespace Ryujinx.Tests.Cpu.Tester int d = (int)UInt(Rd); int n = (int)UInt(Rn); int m = (int)UInt(Rm); + int datasize = (sf ? 64 : 32); ExtendType extend_type = DecodeRegExtend(option); @@ -1176,6 +1216,7 @@ namespace Ryujinx.Tests.Cpu.Tester /* Decode */ int d = (int)UInt(Rd); int n = (int)UInt(Rn); + int datasize = (sf ? 64 : 32); int R; @@ -1205,6 +1246,7 @@ namespace Ryujinx.Tests.Cpu.Tester /* Decode */ int d = (int)UInt(Rd); int n = (int)UInt(Rn); + int datasize = (sf ? 64 : 32); int R; @@ -1238,6 +1280,7 @@ namespace Ryujinx.Tests.Cpu.Tester /* Decode */ int d = (int)UInt(Rd); int n = (int)UInt(Rn); + int datasize = (sf ? 64 : 32); int R; @@ -1267,6 +1310,7 @@ namespace Ryujinx.Tests.Cpu.Tester { /* Decode */ int n = (int)UInt(Rn); + int datasize = (sf ? 64 : 32); Bits flags = nzcv; @@ -1288,6 +1332,7 @@ namespace Ryujinx.Tests.Cpu.Tester { /* Decode */ int n = (int)UInt(Rn); + int datasize = (sf ? 64 : 32); Bits flags = nzcv; @@ -1314,6 +1359,7 @@ namespace Ryujinx.Tests.Cpu.Tester /* Decode */ int n = (int)UInt(Rn); int m = (int)UInt(Rm); + int datasize = (sf ? 64 : 32); Bits flags = nzcv; @@ -1336,6 +1382,7 @@ namespace Ryujinx.Tests.Cpu.Tester /* Decode */ int n = (int)UInt(Rn); int m = (int)UInt(Rm); + int datasize = (sf ? 64 : 32); Bits flags = nzcv; @@ -1362,6 +1409,7 @@ namespace Ryujinx.Tests.Cpu.Tester int d = (int)UInt(Rd); int n = (int)UInt(Rn); int m = (int)UInt(Rm); + int datasize = (sf ? 64 : 32); /* Operation */ @@ -1388,6 +1436,7 @@ namespace Ryujinx.Tests.Cpu.Tester int d = (int)UInt(Rd); int n = (int)UInt(Rn); int m = (int)UInt(Rm); + int datasize = (sf ? 64 : 32); /* Operation */ @@ -1414,6 +1463,7 @@ namespace Ryujinx.Tests.Cpu.Tester int d = (int)UInt(Rd); int n = (int)UInt(Rn); int m = (int)UInt(Rm); + int datasize = (sf ? 64 : 32); /* Operation */ @@ -1440,6 +1490,7 @@ namespace Ryujinx.Tests.Cpu.Tester int d = (int)UInt(Rd); int n = (int)UInt(Rn); int m = (int)UInt(Rm); + int datasize = (sf ? 64 : 32); /* Operation */ @@ -1467,6 +1518,7 @@ namespace Ryujinx.Tests.Cpu.Tester { /* Decode */ int d = (int)UInt(Rd); + int datasize = (sf ? 64 : 32); /* if sf == '0' && hw<1> == '1' then UnallocatedEncoding(); */ @@ -1486,6 +1538,7 @@ namespace Ryujinx.Tests.Cpu.Tester { /* Decode */ int d = (int)UInt(Rd); + int datasize = (sf ? 64 : 32); /* if sf == '0' && hw<1> == '1' then UnallocatedEncoding(); */ @@ -1506,6 +1559,7 @@ namespace Ryujinx.Tests.Cpu.Tester { /* Decode */ int d = (int)UInt(Rd); + int datasize = (sf ? 64 : 32); /* if sf == '0' && hw<1> == '1' then UnallocatedEncoding(); */ @@ -1530,6 +1584,7 @@ namespace Ryujinx.Tests.Cpu.Tester int n = (int)UInt(Rn); int m = (int)UInt(Rm); int a = (int)UInt(Ra); + int datasize = (sf ? 64 : 32); /* Operation */ @@ -1550,6 +1605,7 @@ namespace Ryujinx.Tests.Cpu.Tester int n = (int)UInt(Rn); int m = (int)UInt(Rm); int a = (int)UInt(Ra); + int datasize = (sf ? 64 : 32); /* Operation */ @@ -1983,6 +2039,182 @@ namespace Ryujinx.Tests.Cpu.Tester V(d, result); } + + // https://meriac.github.io/archex/A64_v83A_ISA/sqxtn_advsimd.xml#SQXTN_asisdmisc_N + public static void Sqxtn_S(Bits size, Bits Rn, Bits Rd) + { + bool U = false; + + /* Decode Scalar */ + int d = (int)UInt(Rd); + int n = (int)UInt(Rn); + + /* if size == '11' then ReservedValue(); */ + + int esize = 8 << (int)UInt(size); + int datasize = esize; + int part = 0; + int elements = 1; + + bool unsigned = (U == true); + + /* Operation */ + /* CheckFPAdvSIMDEnabled64(); */ + + Bits result = new Bits(datasize); + Bits operand = V(2 * datasize, n); + Bits element; + bool sat; + + for (int e = 0; e <= elements - 1; e++) + { + element = Elem(operand, e, 2 * esize); + + (Bits _result, bool _sat) = SatQ(Int(element, unsigned), esize, unsigned); + Elem(result, e, esize, _result); + sat = _sat; + + if (sat) + { + /* FPSR.QC = '1'; */ + FPSR[27] = true; // FIXME: Temporary solution. + } + } + + Vpart(d, part, result); + } + + // https://meriac.github.io/archex/A64_v83A_ISA/sqxtn_advsimd.xml#SQXTN_asimdmisc_N + public static void Sqxtn_V(bool Q, Bits size, Bits Rn, Bits Rd) + { + bool U = 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 = 64; + int part = (int)UInt(Q); + int elements = datasize / esize; + + bool unsigned = (U == true); + + /* Operation */ + /* CheckFPAdvSIMDEnabled64(); */ + + Bits result = new Bits(datasize); + Bits operand = V(2 * datasize, n); + Bits element; + bool sat; + + for (int e = 0; e <= elements - 1; e++) + { + element = Elem(operand, e, 2 * esize); + + (Bits _result, bool _sat) = SatQ(Int(element, unsigned), esize, unsigned); + Elem(result, e, esize, _result); + sat = _sat; + + if (sat) + { + /* FPSR.QC = '1'; */ + FPSR[27] = true; // FIXME: Temporary solution. + } + } + + Vpart(d, part, result); + } + + // https://meriac.github.io/archex/A64_v83A_ISA/uqxtn_advsimd.xml#UQXTN_asisdmisc_N + public static void Uqxtn_S(Bits size, Bits Rn, Bits Rd) + { + bool U = true; + + /* Decode Scalar */ + int d = (int)UInt(Rd); + int n = (int)UInt(Rn); + + /* if size == '11' then ReservedValue(); */ + + int esize = 8 << (int)UInt(size); + int datasize = esize; + int part = 0; + int elements = 1; + + bool unsigned = (U == true); + + /* Operation */ + /* CheckFPAdvSIMDEnabled64(); */ + + Bits result = new Bits(datasize); + Bits operand = V(2 * datasize, n); + Bits element; + bool sat; + + for (int e = 0; e <= elements - 1; e++) + { + element = Elem(operand, e, 2 * esize); + + (Bits _result, bool _sat) = SatQ(Int(element, unsigned), esize, unsigned); + Elem(result, e, esize, _result); + sat = _sat; + + if (sat) + { + /* FPSR.QC = '1'; */ + FPSR[27] = true; // FIXME: Temporary solution. + } + } + + Vpart(d, part, result); + } + + // https://meriac.github.io/archex/A64_v83A_ISA/uqxtn_advsimd.xml#UQXTN_asimdmisc_N + public static void Uqxtn_V(bool Q, Bits size, Bits Rn, Bits Rd) + { + bool U = 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 = 64; + int part = (int)UInt(Q); + int elements = datasize / esize; + + bool unsigned = (U == true); + + /* Operation */ + /* CheckFPAdvSIMDEnabled64(); */ + + Bits result = new Bits(datasize); + Bits operand = V(2 * datasize, n); + Bits element; + bool sat; + + for (int e = 0; e <= elements - 1; e++) + { + element = Elem(operand, e, 2 * esize); + + (Bits _result, bool _sat) = SatQ(Int(element, unsigned), esize, unsigned); + Elem(result, e, esize, _result); + sat = _sat; + + if (sat) + { + /* FPSR.QC = '1'; */ + FPSR[27] = true; // FIXME: Temporary solution. + } + } + + Vpart(d, part, result); + } #endregion #region "SimdReg" diff --git a/Ryujinx.Tests/Cpu/Tester/Pseudocode.cs b/Ryujinx.Tests/Cpu/Tester/Pseudocode.cs index 72e0bd78..18a1f441 100644 --- a/Ryujinx.Tests/Cpu/Tester/Pseudocode.cs +++ b/Ryujinx.Tests/Cpu/Tester/Pseudocode.cs @@ -104,6 +104,8 @@ namespace Ryujinx.Tests.Cpu.Tester SP_EL0.SetAll(false); /* SP_EL1 = bits(64) UNKNOWN; */ SP_EL1.SetAll(false); + + FPSR.SetAll(false); // FIXME: Temporary solution. } // #impl-aarch64.SP.write.0 @@ -518,6 +520,8 @@ namespace Ryujinx.Tests.Cpu.Tester SP_EL0 = new Bits(64, false); SP_EL1 = new Bits(64, false); + FPSR = new Bits(32, false); // FIXME: Temporary solution. + PSTATE.N = false; PSTATE.Z = false; PSTATE.C = false; @@ -1016,6 +1020,8 @@ namespace Ryujinx.Tests.Cpu.Tester public static Bits SP_EL0; public static Bits SP_EL1; + + public static Bits FPSR; // FIXME: Temporary solution. #endregion #region "functions/system/" @@ -1081,6 +1087,7 @@ namespace Ryujinx.Tests.Cpu.Tester return true; // EL1 and EL0 must exist } + /* return boolean IMPLEMENTATION_DEFINED; */ return false; } @@ -1113,5 +1120,65 @@ namespace Ryujinx.Tests.Cpu.Tester public bool SP; // Stack pointer select: 0=SP0, 1=SPx [AArch64 only] } #endregion + +#region "functions/vector/" + // #impl-shared.SatQ.3 + public static (Bits, bool) SatQ(BigInteger i, int N, bool unsigned) + { + (Bits result, bool sat) = (unsigned ? UnsignedSatQ(i, N) : SignedSatQ(i, N)); + + return (result, sat); + } + + // #impl-shared.SignedSatQ.2 + public static (Bits, bool) SignedSatQ(BigInteger i, int N) + { + BigInteger result; + bool saturated; + + if (i > BigInteger.Pow(2, N - 1) - 1) + { + result = BigInteger.Pow(2, N - 1) - 1; + saturated = true; + } + else if (i < -(BigInteger.Pow(2, N - 1))) + { + result = -(BigInteger.Pow(2, N - 1)); + saturated = true; + } + else + { + result = i; + saturated = false; + } + + return (result.SubBigInteger(N - 1, 0), saturated); + } + + // #impl-shared.UnsignedSatQ.2 + public static (Bits, bool) UnsignedSatQ(BigInteger i, int N) + { + BigInteger result; + bool saturated; + + if (i > BigInteger.Pow(2, N) - 1) + { + result = BigInteger.Pow(2, N) - 1; + saturated = true; + } + else if (i < 0) + { + result = 0; + saturated = true; + } + else + { + result = i; + saturated = false; + } + + return (result.SubBigInteger(N - 1, 0), saturated); + } +#endregion } }