aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLDj3SNuD <35856442+LDj3SNuD@users.noreply.github.com>2018-04-20 17:40:15 +0200
committergdkchan <gab.dark.100@gmail.com>2018-04-20 12:40:15 -0300
commit2ccd995cb27d95d056f8b6f270cb74012bfb7a33 (patch)
tree4c550626f7a352b0456edcaa22bf9d5d5bf87a9a
parent03002f6537e3208e6951bc9092e958985e200c7d (diff)
Add ADDHN{2}, RADDHN{2}, SUBHN{2}, RSUBHN{2} (vector) instructions. Add 8 Tests. (#92)
* Update AOpCodeTable.cs * Update AInstEmitSimdArithmetic.cs * Update Pseudocode.cs * Update Instructions.cs * Update Bits.cs * Create CpuTestSimd.cs * Create CpuTestSimdReg.cs * Update CpuTestSimd.cs Provide a better supply of input values for the 20 Simd Tests. * Update CpuTestSimdReg.cs Provide a better supply of input values for the 20 Simd Tests. * Update AOpCodeTable.cs * Update AInstEmitSimdArithmetic.cs * Update CpuTestSimd.cs * Update CpuTestSimdReg.cs
-rw-r--r--ChocolArm64/AOpCodeTable.cs4
-rw-r--r--ChocolArm64/Instruction/AInstEmitSimdArithmetic.cs55
-rw-r--r--Ryujinx.Tests/Cpu/CpuTestSimd.cs166
-rw-r--r--Ryujinx.Tests/Cpu/CpuTestSimdReg.cs432
-rw-r--r--Ryujinx.Tests/Cpu/Tester/Instructions.cs577
-rw-r--r--Ryujinx.Tests/Cpu/Tester/Pseudocode.cs112
-rw-r--r--Ryujinx.Tests/Cpu/Tester/Types/Bits.cs81
7 files changed, 1410 insertions, 17 deletions
diff --git a/ChocolArm64/AOpCodeTable.cs b/ChocolArm64/AOpCodeTable.cs
index fb74f892..154affec 100644
--- a/ChocolArm64/AOpCodeTable.cs
+++ b/ChocolArm64/AOpCodeTable.cs
@@ -137,6 +137,7 @@ namespace ChocolArm64
Set("0>001110<<100000101110xxxxxxxxxx", AInstEmit.Abs_V, typeof(AOpCodeSimd));
Set("01011110111xxxxx100001xxxxxxxxxx", AInstEmit.Add_S, typeof(AOpCodeSimdReg));
Set("0>001110<<1xxxxx100001xxxxxxxxxx", AInstEmit.Add_V, typeof(AOpCodeSimdReg));
+ Set("0x001110<<1xxxxx010000xxxxxxxxxx", AInstEmit.Addhn_V, typeof(AOpCodeSimdReg));
Set("01011110xx110001101110xxxxxxxxxx", AInstEmit.Addp_S, typeof(AOpCodeSimd));
Set("0>001110<<1xxxxx101111xxxxxxxxxx", AInstEmit.Addp_V, typeof(AOpCodeSimdReg));
Set("000011100x110001101110xxxxxxxxxx", AInstEmit.Addv_V, typeof(AOpCodeSimd));
@@ -290,7 +291,9 @@ namespace ChocolArm64
Set("0x10111000100000010110xxxxxxxxxx", AInstEmit.Not_V, typeof(AOpCodeSimd));
Set("0x001110101xxxxx000111xxxxxxxxxx", AInstEmit.Orr_V, typeof(AOpCodeSimdReg));
Set("0x00111100000xxx<<x101xxxxxxxxxx", AInstEmit.Orr_Vi, typeof(AOpCodeSimdImm));
+ Set("0x101110<<1xxxxx010000xxxxxxxxxx", AInstEmit.Raddhn_V, typeof(AOpCodeSimdReg));
Set("0x001110<<100000000010xxxxxxxxxx", AInstEmit.Rev64_V, typeof(AOpCodeSimd));
+ Set("0x101110<<1xxxxx011000xxxxxxxxxx", AInstEmit.Rsubhn_V, typeof(AOpCodeSimdReg));
Set("0x001110<<1xxxxx000100xxxxxxxxxx", AInstEmit.Saddw_V, typeof(AOpCodeSimdReg));
Set("x0011110xx100010000000xxxxxxxxxx", AInstEmit.Scvtf_Gp, typeof(AOpCodeSimdCvt));
Set("010111100x100001110110xxxxxxxxxx", AInstEmit.Scvtf_S, typeof(AOpCodeSimd));
@@ -321,6 +324,7 @@ namespace ChocolArm64
Set("xx111100x01xxxxxxxxx10xxxxxxxxxx", AInstEmit.Str, typeof(AOpCodeSimdMemReg));
Set("01111110111xxxxx100001xxxxxxxxxx", AInstEmit.Sub_S, typeof(AOpCodeSimdReg));
Set("0>101110<<1xxxxx100001xxxxxxxxxx", AInstEmit.Sub_V, typeof(AOpCodeSimdReg));
+ Set("0x001110<<1xxxxx011000xxxxxxxxxx", AInstEmit.Subhn_V, typeof(AOpCodeSimdReg));
Set("0x001110000xxxxx0xx000xxxxxxxxxx", AInstEmit.Tbl_V, typeof(AOpCodeSimdTbl));
Set("0>001110<<0xxxxx001010xxxxxxxxxx", AInstEmit.Trn1_V, typeof(AOpCodeSimdReg));
Set("0>001110<<0xxxxx011010xxxxxxxxxx", AInstEmit.Trn2_V, typeof(AOpCodeSimdReg));
diff --git a/ChocolArm64/Instruction/AInstEmitSimdArithmetic.cs b/ChocolArm64/Instruction/AInstEmitSimdArithmetic.cs
index 6b65c156..2dce7410 100644
--- a/ChocolArm64/Instruction/AInstEmitSimdArithmetic.cs
+++ b/ChocolArm64/Instruction/AInstEmitSimdArithmetic.cs
@@ -26,7 +26,6 @@ namespace ChocolArm64.Instruction
AILLabel LblTrue = new AILLabel();
Context.Emit(OpCodes.Dup);
-
Context.Emit(OpCodes.Ldc_I4_0);
Context.Emit(OpCodes.Bge_S, LblTrue);
@@ -45,6 +44,11 @@ namespace ChocolArm64.Instruction
EmitVectorBinaryOpZx(Context, () => Context.Emit(OpCodes.Add));
}
+ public static void Addhn_V(AILEmitterCtx Context)
+ {
+ EmitHighNarrow(Context, () => Context.Emit(OpCodes.Add), Round: false);
+ }
+
public static void Addp_S(AILEmitterCtx Context)
{
AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp;
@@ -130,6 +134,40 @@ namespace ChocolArm64.Instruction
}
}
+ private static void EmitHighNarrow(AILEmitterCtx Context, Action Emit, bool Round)
+ {
+ AOpCodeSimdReg Op = (AOpCodeSimdReg)Context.CurrOp;
+
+ int Elems = 8 >> Op.Size;
+ int ESize = 8 << Op.Size;
+
+ int Part = Op.RegisterSize == ARegisterSize.SIMD128 ? Elems : 0;
+
+ for (int Index = 0; Index < Elems; Index++)
+ {
+ EmitVectorExtractZx(Context, Op.Rn, Index, Op.Size + 1);
+ EmitVectorExtractZx(Context, Op.Rm, Index, Op.Size + 1);
+
+ Emit();
+
+ if (Round)
+ {
+ Context.EmitLdc_I8(1L << (ESize - 1));
+
+ Context.Emit(OpCodes.Add);
+ }
+
+ Context.EmitLsr(ESize);
+
+ EmitVectorInsert(Context, Op.Rd, Part + Index, Op.Size);
+ }
+
+ if (Part == 0)
+ {
+ EmitVectorZeroUpper(Context, Op.Rd);
+ }
+ }
+
public static void Fabd_S(AILEmitterCtx Context)
{
EmitScalarBinaryOpF(Context, () =>
@@ -849,6 +887,16 @@ namespace ChocolArm64.Instruction
EmitVectorUnaryOpSx(Context, () => Context.Emit(OpCodes.Neg));
}
+ public static void Raddhn_V(AILEmitterCtx Context)
+ {
+ EmitHighNarrow(Context, () => Context.Emit(OpCodes.Add), Round: true);
+ }
+
+ public static void Rsubhn_V(AILEmitterCtx Context)
+ {
+ EmitHighNarrow(Context, () => Context.Emit(OpCodes.Sub), Round: true);
+ }
+
public static void Saddw_V(AILEmitterCtx Context)
{
EmitVectorWidenRmBinaryOpSx(Context, () => Context.Emit(OpCodes.Add));
@@ -896,6 +944,11 @@ namespace ChocolArm64.Instruction
EmitVectorBinaryOpZx(Context, () => Context.Emit(OpCodes.Sub));
}
+ public static void Subhn_V(AILEmitterCtx Context)
+ {
+ EmitHighNarrow(Context, () => Context.Emit(OpCodes.Sub), Round: false);
+ }
+
public static void Uabd_V(AILEmitterCtx Context)
{
EmitVectorBinaryOpZx(Context, () => EmitAbd(Context));
diff --git a/Ryujinx.Tests/Cpu/CpuTestSimd.cs b/Ryujinx.Tests/Cpu/CpuTestSimd.cs
new file mode 100644
index 00000000..9107a8f8
--- /dev/null
+++ b/Ryujinx.Tests/Cpu/CpuTestSimd.cs
@@ -0,0 +1,166 @@
+#define Simd
+
+using ChocolArm64.State;
+
+using NUnit.Framework;
+
+namespace Ryujinx.Tests.Cpu
+{
+ using Tester;
+ using Tester.Types;
+
+ [Category("Simd")]
+ public sealed class CpuTestSimd : CpuTest
+ {
+#if Simd
+ [SetUp]
+ public void SetupTester()
+ {
+ AArch64.TakeReset(false);
+ }
+
+#region "ValueSource"
+ private static ulong[] _D_()
+ {
+ return new ulong[] { 0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul,
+ 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul };
+ }
+
+ private static ulong[] _8B4H2S_()
+ {
+ return new ulong[] { 0x0000000000000000ul, 0x7F7F7F7F7F7F7F7Ful,
+ 0x8080808080808080ul, 0x7FFF7FFF7FFF7FFFul,
+ 0x8000800080008000ul, 0x7FFFFFFF7FFFFFFFul,
+ 0x8000000080000000ul, 0xFFFFFFFFFFFFFFFFul };
+ }
+
+ private static ulong[] _16B8H4S2D_()
+ {
+ return new ulong[] { 0x0000000000000000ul, 0x7F7F7F7F7F7F7F7Ful,
+ 0x8080808080808080ul, 0x7FFF7FFF7FFF7FFFul,
+ 0x8000800080008000ul, 0x7FFFFFFF7FFFFFFFul,
+ 0x8000000080000000ul, 0x7FFFFFFFFFFFFFFFul,
+ 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul };
+ }
+#endregion
+
+ [Test, Description("ABS <V><d>, <V><n>")]
+ public void Abs_S_D([ValueSource("_D_")] [Random(1)] ulong A)
+ {
+ uint Opcode = 0x5EE0B820; // ABS D0, D1
+ Bits Op = new Bits(Opcode);
+
+ AVec V0 = new AVec { X1 = TestContext.CurrentContext.Random.NextULong() };
+ AVec V1 = new AVec { X0 = A };
+ AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1);
+
+ 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);
+ }
+
+ [Test, Description("ABS <Vd>.<T>, <Vn>.<T>")]
+ public void Abs_V_8B_4H_2S([ValueSource("_8B4H2S_")] [Random(1)] ulong A,
+ [Values(0b00u, 0b01u, 0b10u)] uint size) // <8B, 4H, 2S>
+ {
+ uint Opcode = 0x0E20B820; // ABS V0.8B, V1.8B
+ Opcode |= ((size & 3) << 22);
+ Bits Op = new Bits(Opcode);
+
+ AVec V0 = new AVec { X1 = TestContext.CurrentContext.Random.NextULong() };
+ AVec V1 = new AVec { X0 = A };
+ AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1);
+
+ 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);
+ }
+
+ [Test, Pairwise, Description("ABS <Vd>.<T>, <Vn>.<T>")]
+ public void Abs_V_16B_8H_4S_2D([ValueSource("_16B8H4S2D_")] [Random(1)] ulong A0,
+ [ValueSource("_16B8H4S2D_")] [Random(1)] ulong A1,
+ [Values(0b00u, 0b01u, 0b10u, 0b11u)] uint size) // <16B, 8H, 4S, 2D>
+ {
+ uint Opcode = 0x4E20B820; // ABS V0.16B, V1.16B
+ Opcode |= ((size & 3) << 22);
+ Bits Op = new Bits(Opcode);
+
+ AVec V1 = new AVec { X0 = A0, X1 = A1 };
+ AThreadState ThreadState = SingleOpcode(Opcode, V1: V1);
+
+ AArch64.Vpart(1, 0, new Bits(A0));
+ AArch64.Vpart(1, 1, new Bits(A1));
+ SimdFp.Abs_V(Op[30], Op[23, 22], Op[9, 5], Op[4, 0]);
+
+ Assert.Multiple(() =>
+ {
+ Assert.That(ThreadState.V0.X0, Is.EqualTo(AArch64.Vpart(64, 0, 0).ToUInt64()));
+ Assert.That(ThreadState.V0.X1, Is.EqualTo(AArch64.Vpart(64, 0, 1).ToUInt64()));
+ });
+ }
+
+ [Test, Description("NEG <V><d>, <V><n>")]
+ public void Neg_S_D([ValueSource("_D_")] [Random(1)] ulong A)
+ {
+ uint Opcode = 0x7EE0B820; // NEG D0, D1
+ Bits Op = new Bits(Opcode);
+
+ AVec V0 = new AVec { X1 = TestContext.CurrentContext.Random.NextULong() };
+ AVec V1 = new AVec { X0 = A };
+ AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1);
+
+ 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);
+ }
+
+ [Test, Description("NEG <Vd>.<T>, <Vn>.<T>")]
+ public void Neg_V_8B_4H_2S([ValueSource("_8B4H2S_")] [Random(1)] ulong A,
+ [Values(0b00u, 0b01u, 0b10u)] uint size) // <8B, 4H, 2S>
+ {
+ uint Opcode = 0x2E20B820; // NEG V0.8B, V1.8B
+ Opcode |= ((size & 3) << 22);
+ Bits Op = new Bits(Opcode);
+
+ AVec V0 = new AVec { X1 = TestContext.CurrentContext.Random.NextULong() };
+ AVec V1 = new AVec { X0 = A };
+ AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1);
+
+ 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);
+ }
+
+ [Test, Pairwise, Description("NEG <Vd>.<T>, <Vn>.<T>")]
+ public void Neg_V_16B_8H_4S_2D([ValueSource("_16B8H4S2D_")] [Random(1)] ulong A0,
+ [ValueSource("_16B8H4S2D_")] [Random(1)] ulong A1,
+ [Values(0b00u, 0b01u, 0b10u, 0b11u)] uint size) // <16B, 8H, 4S, 2D>
+ {
+ uint Opcode = 0x6E20B820; // NEG V0.16B, V1.16B
+ Opcode |= ((size & 3) << 22);
+ Bits Op = new Bits(Opcode);
+
+ AVec V1 = new AVec { X0 = A0, X1 = A1 };
+ AThreadState ThreadState = SingleOpcode(Opcode, V1: V1);
+
+ AArch64.Vpart(1, 0, new Bits(A0));
+ AArch64.Vpart(1, 1, new Bits(A1));
+ SimdFp.Neg_V(Op[30], Op[23, 22], Op[9, 5], Op[4, 0]);
+
+ Assert.Multiple(() =>
+ {
+ Assert.That(ThreadState.V0.X0, Is.EqualTo(AArch64.Vpart(64, 0, 0).ToUInt64()));
+ Assert.That(ThreadState.V0.X1, Is.EqualTo(AArch64.Vpart(64, 0, 1).ToUInt64()));
+ });
+ }
+#endif
+ }
+}
diff --git a/Ryujinx.Tests/Cpu/CpuTestSimdReg.cs b/Ryujinx.Tests/Cpu/CpuTestSimdReg.cs
new file mode 100644
index 00000000..04cef60f
--- /dev/null
+++ b/Ryujinx.Tests/Cpu/CpuTestSimdReg.cs
@@ -0,0 +1,432 @@
+#define SimdReg
+
+using ChocolArm64.State;
+
+using NUnit.Framework;
+
+namespace Ryujinx.Tests.Cpu
+{
+ using Tester;
+ using Tester.Types;
+
+ [Category("SimdReg")]
+ public sealed class CpuTestSimdReg : CpuTest
+ {
+#if SimdReg
+ [SetUp]
+ public void SetupTester()
+ {
+ AArch64.TakeReset(false);
+ }
+
+#region "ValueSource"
+ private static ulong[] _D_()
+ {
+ return new ulong[] { 0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul,
+ 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul };
+ }
+
+ private static ulong[] _8B4H2S_()
+ {
+ return new ulong[] { 0x0000000000000000ul, 0x7F7F7F7F7F7F7F7Ful,
+ 0x8080808080808080ul, 0x7FFF7FFF7FFF7FFFul,
+ 0x8000800080008000ul, 0x7FFFFFFF7FFFFFFFul,
+ 0x8000000080000000ul, 0xFFFFFFFFFFFFFFFFul };
+ }
+
+ private static ulong[] _16B8H4S2D_()
+ {
+ return new ulong[] { 0x0000000000000000ul, 0x7F7F7F7F7F7F7F7Ful,
+ 0x8080808080808080ul, 0x7FFF7FFF7FFF7FFFul,
+ 0x8000800080008000ul, 0x7FFFFFFF7FFFFFFFul,
+ 0x8000000080000000ul, 0x7FFFFFFFFFFFFFFFul,
+ 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul };
+ }
+
+ private static ulong[] _8H4S2D_()
+ {
+ return new ulong[] { 0x0000000000000000ul, 0x7FFF7FFF7FFF7FFFul,
+ 0x8000800080008000ul, 0x7FFFFFFF7FFFFFFFul,
+ 0x8000000080000000ul, 0x7FFFFFFFFFFFFFFFul,
+ 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul };
+ }
+#endregion
+
+ [Test, Description("ADD <V><d>, <V><n>, <V><m>")]
+ public void Add_S_D([ValueSource("_D_")] [Random(1)] ulong A,
+ [ValueSource("_D_")] [Random(1)] ulong B)
+ {
+ uint Opcode = 0x5EE28420; // ADD D0, D1, D2
+ Bits Op = new Bits(Opcode);
+
+ AVec V0 = new AVec { X1 = TestContext.CurrentContext.Random.NextULong() };
+ AVec V1 = new AVec { X0 = A };
+ AVec V2 = new AVec { X0 = B };
+ AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2);
+
+ AArch64.V(1, new Bits(A));
+ 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);
+ }
+
+ [Test, Description("ADD <Vd>.<T>, <Vn>.<T>, <Vm>.<T>")]
+ public void Add_V_8B_4H_2S([ValueSource("_8B4H2S_")] [Random(1)] ulong A,
+ [ValueSource("_8B4H2S_")] [Random(1)] ulong B,
+ [Values(0b00u, 0b01u, 0b10u)] uint size) // <8B, 4H, 2S>
+ {
+ uint Opcode = 0x0E228420; // ADD V0.8B, V1.8B, V2.8B
+ Opcode |= ((size & 3) << 22);
+ Bits Op = new Bits(Opcode);
+
+ AVec V0 = new AVec { X1 = TestContext.CurrentContext.Random.NextULong() };
+ AVec V1 = new AVec { X0 = A };
+ AVec V2 = new AVec { X0 = B };
+ AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2);
+
+ AArch64.V(1, new Bits(A));
+ 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);
+ }
+
+ [Test, Pairwise, Description("ADD <Vd>.<T>, <Vn>.<T>, <Vm>.<T>")]
+ public void Add_V_16B_8H_4S_2D([ValueSource("_16B8H4S2D_")] [Random(1)] ulong A0,
+ [ValueSource("_16B8H4S2D_")] [Random(1)] ulong A1,
+ [ValueSource("_16B8H4S2D_")] [Random(1)] ulong B0,
+ [ValueSource("_16B8H4S2D_")] [Random(1)] ulong B1,
+ [Values(0b00u, 0b01u, 0b10u, 0b11u)] uint size) // <16B, 8H, 4S, 2D>
+ {
+ uint Opcode = 0x4E228420; // ADD V0.16B, V1.16B, V2.16B
+ Opcode |= ((size & 3) << 22);
+ Bits Op = new Bits(Opcode);
+
+ AVec V1 = new AVec { X0 = A0, X1 = A1 };
+ AVec V2 = new AVec { X0 = B0, X1 = B1 };
+ AThreadState ThreadState = SingleOpcode(Opcode, V1: V1, V2: V2);
+
+ AArch64.Vpart(1, 0, new Bits(A0));
+ AArch64.Vpart(1, 1, new Bits(A1));
+ AArch64.Vpart(2, 0, new Bits(B0));
+ AArch64.Vpart(2, 1, new Bits(B1));
+ SimdFp.Add_V(Op[30], Op[23, 22], Op[20, 16], Op[9, 5], Op[4, 0]);
+
+ Assert.Multiple(() =>
+ {
+ Assert.That(ThreadState.V0.X0, Is.EqualTo(AArch64.Vpart(64, 0, 0).ToUInt64()));
+ Assert.That(ThreadState.V0.X1, Is.EqualTo(AArch64.Vpart(64, 0, 1).ToUInt64()));
+ });
+ }
+
+ [Test, Pairwise, Description("ADDHN{2} <Vd>.<Tb>, <Vn>.<Ta>, <Vm>.<Ta>")]
+ public void Addhn_V_8H8B_4S4H_2D2S([ValueSource("_8H4S2D_")] [Random(1)] ulong A0,
+ [ValueSource("_8H4S2D_")] [Random(1)] ulong A1,
+ [ValueSource("_8H4S2D_")] [Random(1)] ulong B0,
+ [ValueSource("_8H4S2D_")] [Random(1)] ulong B1,
+ [Values(0b00u, 0b01u, 0b10u)] uint size) // <8H8B, 4S4H, 2D2S>
+ {
+ uint Opcode = 0x0E224020; // ADDHN V0.8B, V1.8H, V2.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 };
+ AVec V2 = new AVec { X0 = B0, X1 = B1 };
+ AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2);
+
+ AArch64.Vpart(1, 0, new Bits(A0));
+ AArch64.Vpart(1, 1, new Bits(A1));
+ AArch64.Vpart(2, 0, new Bits(B0));
+ AArch64.Vpart(2, 1, new Bits(B1));
+ SimdFp.Addhn_V(Op[30], Op[23, 22], Op[20, 16], 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);
+ });
+ }
+
+ [Test, Pairwise, Description("ADDHN{2} <Vd>.<Tb>, <Vn>.<Ta>, <Vm>.<Ta>")]
+ public void Addhn_V_8H16B_4S8H_2D4S([ValueSource("_8H4S2D_")] [Random(1)] ulong A0,
+ [ValueSource("_8H4S2D_")] [Random(1)] ulong A1,
+ [ValueSource("_8H4S2D_")] [Random(1)] ulong B0,
+ [ValueSource("_8H4S2D_")] [Random(1)] ulong B1,
+ [Values(0b00u, 0b01u, 0b10u)] uint size) // <8H16B, 4S8H, 2D4S>
+ {
+ uint Opcode = 0x4E224020; // ADDHN2 V0.16B, V1.8H, V2.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 };
+ AVec V2 = new AVec { X0 = B0, X1 = B1 };
+ AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2);
+
+ AArch64.Vpart(1, 0, new Bits(A0));
+ AArch64.Vpart(1, 1, new Bits(A1));
+ AArch64.Vpart(2, 0, new Bits(B0));
+ AArch64.Vpart(2, 1, new Bits(B1));
+ SimdFp.Addhn_V(Op[30], Op[23, 22], Op[20, 16], 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()));
+ });
+ }
+
+ [Test, Pairwise, Description("RADDHN{2} <Vd>.<Tb>, <Vn>.<Ta>, <Vm>.<Ta>")]
+ public void Raddhn_V_8H8B_4S4H_2D2S([ValueSource("_8H4S2D_")] [Random(1)] ulong A0,
+ [ValueSource("_8H4S2D_")] [Random(1)] ulong A1,
+ [ValueSource("_8H4S2D_")] [Random(1)] ulong B0,
+ [ValueSource("_8H4S2D_")] [Random(1)] ulong B1,
+ [Values(0b00u, 0b01u, 0b10u)] uint size) // <8H8B, 4S4H, 2D2S>
+ {
+ uint Opcode = 0x2E224020; // RADDHN V0.8B, V1.8H, V2.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 };
+ AVec V2 = new AVec { X0 = B0, X1 = B1 };
+ AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2);
+
+ AArch64.Vpart(1, 0, new Bits(A0));
+ AArch64.Vpart(1, 1, new Bits(A1));
+ AArch64.Vpart(2, 0, new Bits(B0));
+ AArch64.Vpart(2, 1, new Bits(B1));
+ SimdFp.Raddhn_V(Op[30], Op[23, 22], Op[20, 16], 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);
+ });
+ }
+
+ [Test, Pairwise, Description("RADDHN{2} <Vd>.<Tb>, <Vn>.<Ta>, <Vm>.<Ta>")]
+ public void Raddhn_V_8H16B_4S8H_2D4S([ValueSource("_8H4S2D_")] [Random(1)] ulong A0,
+ [ValueSource("_8H4S2D_")] [Random(1)] ulong A1,
+ [ValueSource("_8H4S2D_")] [Random(1)] ulong B0,
+ [ValueSource("_8H4S2D_")] [Random(1)] ulong B1,
+ [Values(0b00u, 0b01u, 0b10u)] uint size) // <8H16B, 4S8H, 2D4S>
+ {
+ uint Opcode = 0x6E224020; // RADDHN2 V0.16B, V1.8H, V2.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 };
+ AVec V2 = new AVec { X0 = B0, X1 = B1 };
+ AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2);
+
+ AArch64.Vpart(1, 0, new Bits(A0));
+ AArch64.Vpart(1, 1, new Bits(A1));
+ AArch64.Vpart(2, 0, new Bits(B0));
+ AArch64.Vpart(2, 1, new Bits(B1));
+ SimdFp.Raddhn_V(Op[30], Op[23, 22], Op[20, 16], 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()));
+ });
+ }
+
+ [Test, Pairwise, Description("RSUBHN{2} <Vd>.<Tb>, <Vn>.<Ta>, <Vm>.<Ta>")]
+ public void Rsubhn_V_8H8B_4S4H_2D2S([ValueSource("_8H4S2D_")] [Random(1)] ulong A0,
+ [ValueSource("_8H4S2D_")] [Random(1)] ulong A1,
+ [ValueSource("_8H4S2D_")] [Random(1)] ulong B0,
+ [ValueSource("_8H4S2D_")] [Random(1)] ulong B1,
+ [Values(0b00u, 0b01u, 0b10u)] uint size) // <8H8B, 4S4H, 2D2S>
+ {
+ uint Opcode = 0x2E226020; // RSUBHN V0.8B, V1.8H, V2.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 };
+ AVec V2 = new AVec { X0 = B0, X1 = B1 };
+ AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2);
+
+ AArch64.Vpart(1, 0, new Bits(A0));
+ AArch64.Vpart(1, 1, new Bits(A1));
+ AArch64.Vpart(2, 0, new Bits(B0));
+ AArch64.Vpart(2, 1, new Bits(B1));
+ SimdFp.Rsubhn_V(Op[30], Op[23, 22], Op[20, 16], 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);
+ });
+ }
+
+ [Test, Pairwise, Description("RSUBHN{2} <Vd>.<Tb>, <Vn>.<Ta>, <Vm>.<Ta>")]
+ public void Rsubhn_V_8H16B_4S8H_2D4S([ValueSource("_8H4S2D_")] [Random(1)] ulong A0,
+ [ValueSource("_8H4S2D_")] [Random(1)] ulong A1,
+ [ValueSource("_8H4S2D_")] [Random(1)] ulong B0,
+ [ValueSource("_8H4S2D_")] [Random(1)] ulong B1,
+ [Values(0b00u, 0b01u, 0b10u)] uint size) // <8H16B, 4S8H, 2D4S>
+ {
+ uint Opcode = 0x6E226020; // RSUBHN2 V0.16B, V1.8H, V2.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 };
+ AVec V2 = new AVec { X0 = B0, X1 = B1 };
+ AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2);
+
+ AArch64.Vpart(1, 0, new Bits(A0));
+ AArch64.Vpart(1, 1, new Bits(A1));
+ AArch64.Vpart(2, 0, new Bits(B0));
+ AArch64.Vpart(2, 1, new Bits(B1));
+ SimdFp.Rsubhn_V(Op[30], Op[23, 22], Op[20, 16], 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()));
+ });
+ }
+
+ [Test, Description("SUB <V><d>, <V><n>, <V><m>")]
+ public void Sub_S_D([ValueSource("_D_")] [Random(1)] ulong A,
+ [ValueSource("_D_")] [Random(1)] ulong B)
+ {
+ uint Opcode = 0x7EE28420; // SUB D0, D1, D2
+ Bits Op = new Bits(Opcode);
+
+ AVec V0 = new AVec { X1 = TestContext.CurrentContext.Random.NextULong() };
+ AVec V1 = new AVec { X0 = A };
+ AVec V2 = new AVec { X0 = B };
+ AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2);
+
+ AArch64.V(1, new Bits(A));
+ 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);
+ }
+
+ [Test, Description("SUB <Vd>.<T>, <Vn>.<T>, <Vm>.<T>")]
+ public void Sub_V_8B_4H_2S([ValueSource("_8B4H2S_")] [Random(1)] ulong A,
+ [ValueSource("_8B4H2S_")] [Random(1)] ulong B,
+ [Values(0b00u, 0b01u, 0b10u)] uint size) // <8B, 4H, 2S>
+ {
+ uint Opcode = 0x2E228420; // SUB V0.8B, V1.8B, V2.8B
+ Opcode |= ((size & 3) << 22);
+ Bits Op = new Bits(Opcode);
+
+ AVec V0 = new AVec { X1 = TestContext.CurrentContext.Random.NextULong() };
+ AVec V1 = new AVec { X0 = A };
+ AVec V2 = new AVec { X0 = B };
+ AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2);
+
+ AArch64.V(1, new Bits(A));
+ 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);
+ }
+
+ [Test, Pairwise, Description("SUB <Vd>.<T>, <Vn>.<T>, <Vm>.<T>")]
+ public void Sub_V_16B_8H_4S_2D([ValueSource("_16B8H4S2D_")] [Random(1)] ulong A0,
+ [ValueSource("_16B8H4S2D_")] [Random(1)] ulong A1,
+ [ValueSource("_16B8H4S2D_")] [Random(1)] ulong B0,
+ [ValueSource("_16B8H4S2D_")] [Random(1)] ulong B1,
+ [Values(0b00u, 0b01u, 0b10u, 0b11u)] uint size) // <16B, 8H, 4S, 2D>
+ {
+ uint Opcode = 0x6E228420; // SUB V0.16B, V1.16B, V2.16B
+ Opcode |= ((size & 3) << 22);
+ Bits Op = new Bits(Opcode);
+
+ AVec V1 = new AVec { X0 = A0, X1 = A1 };
+ AVec V2 = new AVec { X0 = B0, X1 = B1 };
+ AThreadState ThreadState = SingleOpcode(Opcode, V1: V1, V2: V2);
+
+ AArch64.Vpart(1, 0, new Bits(A0));
+ AArch64.Vpart(1, 1, new Bits(A1));
+ AArch64.Vpart(2, 0, new Bits(B0));
+ AArch64.Vpart(2, 1, new Bits(B1));
+ SimdFp.Sub_V(Op[30], Op[23, 22], Op[20, 16], Op[9, 5], Op[4, 0]);
+
+ Assert.Multiple(() =>
+ {
+ Assert.That(ThreadState.V0.X0, Is.EqualTo(AArch64.Vpart(64, 0, 0).ToUInt64()));
+ Assert.That(ThreadState.V0.X1, Is.EqualTo(AArch64.Vpart(64, 0, 1).ToUInt64()));
+ });
+ }
+
+ [Test, Pairwise, Description("SUBHN{2} <Vd>.<Tb>, <Vn>.<Ta>, <Vm>.<Ta>")]
+ public void Subhn_V_8H8B_4S4H_2D2S([ValueSource("_8H4S2D_")] [Random(1)] ulong A0,
+ [ValueSource("_8H4S2D_")] [Random(1)] ulong A1,
+ [ValueSource("_8H4S2D_")] [Random(1)] ulong B0,
+ [ValueSource("_8H4S2D_")] [Random(1)] ulong B1,
+ [Values(0b00u, 0b01u, 0b10u)] uint size) // <8H8B, 4S4H, 2D2S>
+ {
+ uint Opcode = 0x0E226020; // SUBHN V0.8B, V1.8H, V2.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 };
+ AVec V2 = new AVec { X0 = B0, X1 = B1 };
+ AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2);
+
+ AArch64.Vpart(1, 0, new Bits(A0));
+ AArch64.Vpart(1, 1, new Bits(A1));
+ AArch64.Vpart(2, 0, new Bits(B0));
+ AArch64.Vpart(2, 1, new Bits(B1));
+ SimdFp.Subhn_V(Op[30], Op[23, 22], Op[20, 16], 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);
+ });
+ }
+
+ [Test, Pairwise, Description("SUBHN{2} <Vd>.<Tb>, <Vn>.<Ta>, <Vm>.<Ta>")]
+ public void Subhn_V_8H16B_4S8H_2D4S([ValueSource("_8H4S2D_")] [Random(1)] ulong A0,
+ [ValueSource("_8H4S2D_")] [Random(1)] ulong A1,
+ [ValueSource("_8H4S2D_")] [Random(1)] ulong B0,
+ [ValueSource("_8H4S2D_")] [Random(1)] ulong B1,
+ [Values(0b00u, 0b01u, 0b10u)] uint size) // <8H16B, 4S8H, 2D4S>
+ {
+ uint Opcode = 0x4E226020; // SUBHN2 V0.16B, V1.8H, V2.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 };
+ AVec V2 = new AVec { X0 = B0, X1 = B1 };
+ AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2);
+
+ AArch64.Vpart(1, 0, new Bits(A0));
+ AArch64.Vpart(1, 1, new Bits(A1));
+ AArch64.Vpart(2, 0, new Bits(B0));
+ AArch64.Vpart(2, 1, new Bits(B1));
+ SimdFp.Subhn_V(Op[30], Op[23, 22], Op[20, 16], 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()));
+ });
+ }
+#endif
+ }
+}
diff --git a/Ryujinx.Tests/Cpu/Tester/Instructions.cs b/Ryujinx.Tests/Cpu/Tester/Instructions.cs
index 7a51923f..2ca76ffd 100644
--- a/Ryujinx.Tests/Cpu/Tester/Instructions.cs
+++ b/Ryujinx.Tests/Cpu/Tester/Instructions.cs
@@ -1,7 +1,7 @@
// https://github.com/LDj3SNuD/ARM_v8-A_AArch64_Instructions_Tester/blob/master/Tester/Instructions.cs
// https://meriac.github.io/archex/A64_v83A_ISA/index.xml
-/* https://meriac.github.io/archex/A64_v83A_ISA/fpsimdindex.xml */
+// https://meriac.github.io/archex/A64_v83A_ISA/fpsimdindex.xml
using System.Numerics;
@@ -1674,9 +1674,578 @@ namespace Ryujinx.Tests.Cpu.Tester
#endregion
}
- /*
- internal static class Advanced
+ internal static class SimdFp
{
+#region "Simd"
+ // https://meriac.github.io/archex/A64_v83A_ISA/abs_advsimd.xml#ABS_asisdmisc_R
+ public static void Abs_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 elements = 1;
+
+ bool neg = (U == true);
+
+ /* Operation */
+ /* CheckFPAdvSIMDEnabled64(); */
+
+ Bits result = new Bits(datasize);
+ Bits operand = V(datasize, n);
+ BigInteger element;
+
+ for (int e = 0; e <= elements - 1; e++)
+ {
+ element = SInt(Elem(operand, e, esize));
+
+ if (neg)
+ {
+ element = -element;
+ }
+ else
+ {
+ element = Abs(element);
+ }
+
+ Elem(result, e, esize, element.SubBigInteger(esize - 1, 0));
+ }
+
+ V(d, result);
+ }
+
+ // https://meriac.github.io/archex/A64_v83A_ISA/abs_advsimd.xml#ABS_asimdmisc_R
+ public static void Abs_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:Q == '110' then ReservedValue(); */
+
+ int esize = 8 << (int)UInt(size);
+ int datasize = (Q ? 128 : 64);
+ int elements = datasize / esize;
+
+ bool neg = (U == true);
+
+ /* Operation */
+ /* CheckFPAdvSIMDEnabled64(); */
+
+ Bits result = new Bits(datasize);
+ Bits operand = V(datasize, n);
+ BigInteger element;
+
+ for (int e = 0; e <= elements - 1; e++)
+ {
+ element = SInt(Elem(operand, e, esize));
+
+ if (neg)
+ {
+ element = -element;
+ }
+ else
+ {
+ element = Abs(element);
+ }
+
+ Elem(result, e, esize, element.SubBigInteger(esize - 1, 0));
+ }
+
+ V(d, result);
+ }
+
+ // https://meriac.github.io/archex/A64_v83A_ISA/neg_advsimd.xml#NEG_asisdmisc_R
+ public static void Neg_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 elements = 1;
+
+ bool neg = (U == true);
+
+ /* Operation */
+ /* CheckFPAdvSIMDEnabled64(); */
+
+ Bits result = new Bits(datasize);
+ Bits operand = V(datasize, n);
+ BigInteger element;
+
+ for (int e = 0; e <= elements - 1; e++)
+ {
+ element = SInt(Elem(operand, e, esize));
+
+ if (neg)
+ {
+ element = -element;
+ }
+ else
+ {
+ element = Abs(element);
+ }
+
+ Elem(result, e, esize, element.SubBigInteger(esize - 1, 0));
+ }
+
+ V(d, result);
+ }
+
+ // https://meriac.github.io/archex/A64_v83A_ISA/neg_advsimd.xml#NEG_asimdmisc_R
+ public static void Neg_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:Q == '110' then ReservedValue(); */
+
+ int esize = 8 << (int)UInt(size);
+ int datasize = (Q ? 128 : 64);
+ int elements = datasize / esize;
+
+ bool neg = (U == true);
+
+ /* Operation */
+ /* CheckFPAdvSIMDEnabled64(); */
+
+ Bits result = new Bits(datasize);
+ Bits operand = V(datasize, n);
+ BigInteger element;
+
+ for (int e = 0; e <= elements - 1; e++)
+ {
+ element = SInt(Elem(operand, e, esize));
+
+ if (neg)
+ {
+ element = -element;
+ }
+ else
+ {
+ element = Abs(element);
+ }
+
+ Elem(result, e, esize, element.SubBigInteger(esize - 1, 0));
+ }
+
+ V(d, result);
+ }
+#endregion
+
+#region "SimdReg"
+ // https://meriac.github.io/archex/A64_v83A_ISA/add_advsimd.xml#ADD_asisdsame_only
+ public static void Add_S(Bits size, Bits Rm, Bits Rn, Bits Rd)
+ {
+ bool U = false;
+
+ /* Decode Scalar */
+ int d = (int)UInt(Rd);
+ int n = (int)UInt(Rn);
+ int m = (int)UInt(Rm);
+
+ /* if size != '11' then ReservedValue(); */
+
+ int esize = 8 << (int)UInt(size);
+ int datasize = esize;
+ int elements = 1;
+
+ bool sub_op = (U == true);
+
+ /* Operation */
+ /* CheckFPAdvSIMDEnabled64(); */
+
+ Bits result = new Bits(datasize);
+ Bits operand1 = V(datasize, n);
+ Bits operand2 = V(datasize, m);
+ Bits element1;
+ Bits element2;
+
+ for (int e = 0; e <= elements - 1; e++)
+ {
+ element1 = Elem(operand1, e, esize);
+ element2 = Elem(operand2, e, esize);
+
+ if (sub_op)
+ {
+ Elem(result, e, esize, element1 - element2);
+ }
+ else
+ {
+ Elem(result, e, esize, element1 + element2);
+ }
+ }
+
+ V(d, result);
+ }
+
+ // https://meriac.github.io/archex/A64_v83A_ISA/add_advsimd.xml#ADD_asimdsame_only
+ public static void Add_V(bool Q, Bits size, Bits Rm, Bits Rn, Bits Rd)
+ {
+ bool U = false;
+
+ /* Decode Vector */
+ int d = (int)UInt(Rd);
+ int n = (int)UInt(Rn);
+ int m = (int)UInt(Rm);
+
+ /* if size:Q == '110' then ReservedValue(); */
+
+ int esize = 8 << (int)UInt(size);
+ int datasize = (Q ? 128 : 64);
+ int elements = datasize / esize;
+
+ bool sub_op = (U == true);
+
+ /* Operation */
+ /* CheckFPAdvSIMDEnabled64(); */
+
+ Bits result = new Bits(datasize);
+ Bits operand1 = V(datasize, n);
+ Bits operand2 = V(datasize, m);
+ Bits element1;
+ Bits element2;
+
+ for (int e = 0; e <= elements - 1; e++)
+ {
+ element1 = Elem(operand1, e, esize);
+ element2 = Elem(operand2, e, esize);
+
+ if (sub_op)
+ {
+ Elem(result, e, esize, element1 - element2);
+ }
+ else
+ {
+ Elem(result, e, esize, element1 + element2);
+ }
+ }
+
+ V(d, result);
+ }
+
+ // https://meriac.github.io/archex/A64_v83A_ISA/addhn_advsimd.xml
+ public static void Addhn_V(bool Q, Bits size, Bits Rm, Bits Rn, Bits Rd)
+ {
+ bool U = false;
+ bool o1 = false;
+
+ /* Decode */
+ int d = (int)UInt(Rd);
+ int n = (int)UInt(Rn);
+ int m = (int)UInt(Rm);
+
+ /* if size == '11' then ReservedValue(); */
+
+ int esize = 8 << (int)UInt(size);
+ int datasize = 64;
+ int part = (int)UInt(Q);
+ int elements = datasize / esize;
+
+ bool sub_op = (o1 == true);
+ bool round = (U == true);
+
+ /* Operation */
+ /* CheckFPAdvSIMDEnabled64(); */
+
+ Bits result = new Bits(datasize);
+ Bits operand1 = V(2 * datasize, n);
+ Bits operand2 = V(2 * datasize, m);
+ BigInteger round_const = (round ? (BigInteger)1 << (esize - 1) : 0);
+ Bits sum;
+ Bits element1;
+ Bits element2;
+
+ for (int e = 0; e <= elements - 1; e++)
+ {
+ element1 = Elem(operand1, e, 2 * esize);
+ element2 = Elem(operand2, e, 2 * esize);
+
+ if (sub_op)
+ {
+ sum = element1 - element2;
+ }
+ else
+ {
+ sum = element1 + element2;
+ }
+
+ sum = sum + round_const;
+
+ Elem(result, e, esize, sum[2 * esize - 1, esize]);
+ }
+
+ Vpart(d, part, result);
+ }
+
+ // https://meriac.github.io/archex/A64_v83A_ISA/raddhn_advsimd.xml
+ public static void Raddhn_V(bool Q, Bits size, Bits Rm, Bits Rn, Bits Rd)
+ {
+ bool U = true;
+ bool o1 = false;
+
+ /* Decode */
+ int d = (int)UInt(Rd);
+ int n = (int)UInt(Rn);
+ int m = (int)UInt(Rm);
+
+ /* if size == '11' then ReservedValue(); */
+
+ int esize = 8 << (int)UInt(size);
+ int datasize = 64;
+ int part = (int)UInt(Q);
+ int elements = datasize / esize;
+
+ bool sub_op = (o1 == true);
+ bool round = (U == true);
+
+ /* Operation */
+ /* CheckFPAdvSIMDEnabled64(); */
+
+ Bits result = new Bits(datasize);
+ Bits operand1 = V(2 * datasize, n);
+ Bits operand2 = V(2 * datasize, m);
+ BigInteger round_const = (round ? (BigInteger)1 << (esize - 1) : 0);
+ Bits sum;
+ Bits element1;
+ Bits element2;
+
+ for (int e = 0; e <= elements - 1; e++)
+ {
+ element1 = Elem(operand1, e, 2 * esize);
+ element2 = Elem(operand2, e, 2 * esize);
+
+ if (sub_op)
+ {
+ sum = element1 - element2;
+ }
+ else
+ {
+ sum = element1 + element2;
+ }
+
+ sum = sum + round_const;
+
+ Elem(result, e, esize, sum[2 * esize - 1, esize]);
+ }
+
+ Vpart(d, part, result);
+ }
+
+ // https://meriac.github.io/archex/A64_v83A_ISA/rsubhn_advsimd.xml
+ public static void Rsubhn_V(bool Q, Bits size, Bits Rm, Bits Rn, Bits Rd)
+ {
+ bool U = true;
+ bool o1 = true;
+
+ /* Decode */
+ int d = (int)UInt(Rd);
+ int n = (int)UInt(Rn);
+ int m = (int)UInt(Rm);
+
+ /* if size == '11' then ReservedValue(); */
+
+ int esize = 8 << (int)UInt(size);
+ int datasize = 64;
+ int part = (int)UInt(Q);
+ int elements = datasize / esize;
+
+ bool sub_op = (o1 == true);
+ bool round = (U == true);
+
+ /* Operation */
+ /* CheckFPAdvSIMDEnabled64(); */
+
+ Bits result = new Bits(datasize);
+ Bits operand1 = V(2 * datasize, n);
+ Bits operand2 = V(2 * datasize, m);
+ BigInteger round_const = (round ? (BigInteger)1 << (esize - 1) : 0);
+ Bits sum;
+ Bits element1;
+ Bits element2;
+
+ for (int e = 0; e <= elements - 1; e++)
+ {
+ element1 = Elem(operand1, e, 2 * esize);
+ element2 = Elem(operand2, e, 2 * esize);
+
+ if (sub_op)
+ {
+ sum = element1 - element2;
+ }
+ else
+ {
+ sum = element1 + element2;
+ }
+
+ sum = sum + round_const;
+
+ Elem(result, e, esize, sum[2 * esize - 1, esize]);
+ }
+
+ Vpart(d, part, result);
+ }
+
+ // https://meriac.github.io/archex/A64_v83A_ISA/sub_advsimd.xml#SUB_asisdsame_only
+ public static void Sub_S(Bits size, Bits Rm, Bits Rn, Bits Rd)
+ {
+ bool U = true;
+
+ /* Decode Scalar */
+ int d = (int)UInt(Rd);
+ int n = (int)UInt(Rn);
+ int m = (int)UInt(Rm);
+
+ /* if size != '11' then ReservedValue(); */
+
+ int esize = 8 << (int)UInt(size);
+ int datasize = esize;
+ int elements = 1;
+
+ bool sub_op = (U == true);
+
+ /* Operation */
+ /* CheckFPAdvSIMDEnabled64(); */
+
+ Bits result = new Bits(datasize);
+ Bits operand1 = V(datasize, n);
+ Bits operand2 = V(datasize, m);
+ Bits element1;
+ Bits element2;
+
+ for (int e = 0; e <= elements - 1; e++)
+ {
+ element1 = Elem(operand1, e, esize);
+ element2 = Elem(operand2, e, esize);
+
+ if (sub_op)
+ {
+ Elem(result, e, esize, element1 - element2);
+ }
+ else
+ {
+ Elem(result, e, esize, element1 + element2);
+ }
+ }
+
+ V(d, result);
+ }
+
+ // https://meriac.github.io/archex/A64_v83A_ISA/sub_advsimd.xml#SUB_asimdsame_only
+ public static void Sub_V(bool Q, Bits size, Bits Rm, Bits Rn, Bits Rd)
+ {
+ bool U = true;
+
+ /* Decode Vector */
+ int d = (int)UInt(Rd);
+ int n = (int)UInt(Rn);
+ int m = (int)UInt(Rm);
+
+ /* if size:Q == '110' then ReservedValue(); */
+
+ int esize = 8 << (int)UInt(size);
+ int datasize = (Q ? 128 : 64);
+ int elements = datasize / esize;
+
+ bool sub_op = (U == true);
+
+ /* Operation */
+ /* CheckFPAdvSIMDEnabled64(); */
+
+ Bits result = new Bits(datasize);
+ Bits operand1 = V(datasize, n);
+ Bits operand2 = V(datasize, m);
+ Bits element1;
+ Bits element2;
+
+ for (int e = 0; e <= elements - 1; e++)
+ {
+ element1 = Elem(operand1, e, esize);
+ element2 = Elem(operand2, e, esize);
+
+ if (sub_op)
+ {
+ Elem(result, e, esize, element1 - element2);
+ }
+ else
+ {
+ Elem(result, e, esize, element1 + element2);
+ }
+ }
+
+ V(d, result);
+ }
+
+ // https://meriac.github.io/archex/A64_v83A_ISA/subhn_advsimd.xml
+ public static void Subhn_V(bool Q, Bits size, Bits Rm, Bits Rn, Bits Rd)
+ {
+ bool U = false;
+ bool o1 = true;
+
+ /* Decode */
+ int d = (int)UInt(Rd);
+ int n = (int)UInt(Rn);
+ int m = (int)UInt(Rm);
+
+ /* if size == '11' then ReservedValue(); */
+
+ int esize = 8 << (int)UInt(size);
+ int datasize = 64;
+ int part = (int)UInt(Q);
+ int elements = datasize / esize;
+
+ bool sub_op = (o1 == true);
+ bool round = (U == true);
+
+ /* Operation */
+ /* CheckFPAdvSIMDEnabled64(); */
+
+ Bits result = new Bits(datasize);
+ Bits operand1 = V(2 * datasize, n);
+ Bits operand2 = V(2 * datasize, m);
+ BigInteger round_const = (round ? (BigInteger)1 << (esize - 1) : 0);
+ Bits sum;
+ Bits element1;
+ Bits element2;
+
+ for (int e = 0; e <= elements - 1; e++)
+ {
+ element1 = Elem(operand1, e, 2 * esize);
+ element2 = Elem(operand2, e, 2 * esize);
+
+ if (sub_op)
+ {
+ sum = element1 - element2;
+ }
+ else
+ {
+ sum = element1 + element2;
+ }
+
+ sum = sum + round_const;
+
+ Elem(result, e, esize, sum[2 * esize - 1, esize]);
+ }
+
+ Vpart(d, part, result);
+ }
+#endregion
}
- */
}
diff --git a/Ryujinx.Tests/Cpu/Tester/Pseudocode.cs b/Ryujinx.Tests/Cpu/Tester/Pseudocode.cs
index 1da17c49..21c3935c 100644
--- a/Ryujinx.Tests/Cpu/Tester/Pseudocode.cs
+++ b/Ryujinx.Tests/Cpu/Tester/Pseudocode.cs
@@ -70,6 +70,7 @@ namespace Ryujinx.Tests.Cpu.Tester
// ELR_ELx and SPSR_ELx have UNKNOWN values, so that it
// is impossible to return from a reset in an architecturally defined way.
AArch64.ResetGeneralRegisters();
+ AArch64.ResetSIMDFPRegisters();
AArch64.ResetSpecialRegisters();
}
#endregion
@@ -85,6 +86,16 @@ namespace Ryujinx.Tests.Cpu.Tester
}
}
+ /* #AArch64.ResetSIMDFPRegisters.0 */
+ public static void ResetSIMDFPRegisters()
+ {
+ for (int i = 0; i <= 31; i++)
+ {
+ /* V[i] = bits(128) UNKNOWN; */
+ _V[i].SetAll(false);
+ }
+ }
+
/* #AArch64.ResetSpecialRegisters.0 */
public static void ResetSpecialRegisters()
{
@@ -116,8 +127,8 @@ namespace Ryujinx.Tests.Cpu.Tester
default:
case Bits bits when bits == EL1:
SP_EL1 = ZeroExtend(64, value);
- break;
- /*case Bits bits when bits == EL2:
+ break;/*
+ case Bits bits when bits == EL2:
SP_EL2 = ZeroExtend(64, value);
break;
case Bits bits when bits == EL3:
@@ -144,8 +155,8 @@ namespace Ryujinx.Tests.Cpu.Tester
return SP_EL0[width - 1, 0];
default:
case Bits bits when bits == EL1:
- return SP_EL1[width - 1, 0];
- /*case Bits bits when bits == EL2:
+ return SP_EL1[width - 1, 0];/*
+ case Bits bits when bits == EL2:
return SP_EL2[width - 1, 0];
case Bits bits when bits == EL3:
return SP_EL3[width - 1, 0];*/
@@ -153,6 +164,64 @@ namespace Ryujinx.Tests.Cpu.Tester
}
}
+ // #impl-aarch64.V.write.1
+ public static void V(int n, Bits value)
+ {
+ /* int width = value.Count; */
+
+ /* assert n >= 0 && n <= 31; */
+ /* assert width IN {8,16,32,64,128}; */
+
+ _V[n] = ZeroExtend(128, value);
+ }
+
+ /* #impl-aarch64.V.read.1 */
+ public static Bits V(int width, int n)
+ {
+ /* assert n >= 0 && n <= 31; */
+ /* assert width IN {8,16,32,64,128}; */
+
+ return _V[n][width - 1, 0];
+ }
+
+ /* #impl-aarch64.Vpart.read.2 */
+ public static Bits Vpart(int width, int n, int part)
+ {
+ /* assert n >= 0 && n <= 31; */
+ /* assert part IN {0, 1}; */
+
+ if (part == 0)
+ {
+ /* assert width IN {8,16,32,64}; */
+ return _V[n][width - 1, 0];
+ }
+ else
+ {
+ /* assert width == 64; */
+ return _V[n][(width * 2) - 1, width];
+ }
+ }
+
+ // #impl-aarch64.Vpart.write.2
+ public static void Vpart(int n, int part, Bits value)
+ {
+ int width = value.Count;
+
+ /* assert n >= 0 && n <= 31; */
+ /* assert part IN {0, 1}; */
+
+ if (part == 0)
+ {
+ /* assert width IN {8,16,32,64}; */
+ _V[n] = ZeroExtend(128, value);
+ }
+ else
+ {
+ /* assert width == 64; */
+ _V[n][(width * 2) - 1, width] = value[width - 1, 0];
+ }
+ }
+
// #impl-aarch64.X.write.1
public static void X(int n, Bits value)
{
@@ -214,6 +283,7 @@ namespace Ryujinx.Tests.Cpu.Tester
public static Bits ExtendReg(int N, int reg, ExtendType type, int shift)
{
/* assert shift >= 0 && shift <= 4; */
+
Bits val = X(N, reg);
bool unsigned;
int len;
@@ -384,6 +454,12 @@ namespace Ryujinx.Tests.Cpu.Tester
_R[i] = new Bits(64, false);
}
+ _V = new Bits[32];
+ for (int i = 0; i <= 31; i++)
+ {
+ _V[i] = new Bits(128, false);
+ }
+
SP_EL0 = new Bits(64, false);
SP_EL1 = new Bits(64, false);
@@ -437,6 +513,12 @@ namespace Ryujinx.Tests.Cpu.Tester
return (result, carry_out);
}
+ // #impl-shared.Abs.1
+ public static BigInteger Abs(BigInteger x)
+ {
+ return (x >= 0 ? x : -x);
+ }
+
// #impl-shared.CountLeadingSignBits.1
public static int CountLeadingSignBits(Bits x)
{
@@ -453,6 +535,26 @@ namespace Ryujinx.Tests.Cpu.Tester
return (N - 1 - HighestSetBit(x));
}
+ // #impl-shared.Elem.read.3
+ public static Bits Elem(/*in */Bits vector, int e, int size)
+ {
+ /* int N = vector.Count; */
+
+ /* assert e >= 0 && (e+1)*size <= N; */
+
+ return vector[e * size + size - 1, e * size];
+ }
+
+ // #impl-shared.Elem.write.3
+ public static void Elem(/*out */Bits vector, int e, int size, Bits value)
+ {
+ /* int N = vector.Count; */
+
+ /* assert e >= 0 && (e+1)*size <= N; */
+
+ vector[(e + 1) * size - 1, e * size] = value;
+ }
+
/* */
public static Bits EOR(Bits x, Bits y)
{
@@ -855,6 +957,8 @@ namespace Ryujinx.Tests.Cpu.Tester
#region "functions/registers/"
public static readonly Bits[] _R;
+ public static readonly Bits[] _V;
+
public static Bits SP_EL0;
public static Bits SP_EL1;
#endregion
diff --git a/Ryujinx.Tests/Cpu/Tester/Types/Bits.cs b/Ryujinx.Tests/Cpu/Tester/Types/Bits.cs
index f4ee966c..30d63264 100644
--- a/Ryujinx.Tests/Cpu/Tester/Types/Bits.cs
+++ b/Ryujinx.Tests/Cpu/Tester/Types/Bits.cs
@@ -15,14 +15,15 @@ namespace Ryujinx.Tests.Cpu.Tester.Types
public Bits(bool[] values) => bits = new BitArray(values);
public Bits(byte[] bytes) => bits = new BitArray(bytes);
public Bits(Bits bits) => this.bits = new BitArray(bits.bits);
- private Bits(BitArray bitArray) => bits = new BitArray(bitArray);
public Bits(int length) => bits = new BitArray(length);
public Bits(int length, bool defaultValue) => bits = new BitArray(length, defaultValue);
+ private Bits(BitArray bitArray) => bits = new BitArray(bitArray);
public Bits(ulong value) => bits = new BitArray(BitConverter.GetBytes(value));
public Bits(uint value) => bits = new BitArray(BitConverter.GetBytes(value));
public Bits(ushort value) => bits = new BitArray(BitConverter.GetBytes(value));
public Bits(byte value) => bits = new BitArray(new byte[1] {value});
+ private BitArray ToBitArray() => new BitArray(bits);
public ulong ToUInt64()
{
byte[] dst = new byte[8];
@@ -39,7 +40,22 @@ namespace Ryujinx.Tests.Cpu.Tester.Types
return BitConverter.ToUInt32(dst, 0);
}
- private BitArray ToBitArray() => new BitArray(bits);
+ public ushort ToUInt16()
+ {
+ byte[] dst = new byte[2];
+
+ bits.CopyTo(dst, 0);
+
+ return BitConverter.ToUInt16(dst, 0);
+ }
+ public byte ToByte()
+ {
+ byte[] dst = new byte[1];
+
+ bits.CopyTo(dst, 0);
+
+ return dst[0];
+ }
public bool this[int index] // ASL: "<>".
{
@@ -166,17 +182,66 @@ namespace Ryujinx.Tests.Cpu.Tester.Types
BigInteger dst;
- if (left.Count <= 32)
+ switch (left.Count)
{
- dst = left.ToUInt32() + right;
+ case 8: dst = left.ToByte() + right; break;
+ case 16: dst = left.ToUInt16() + right; break;
+ case 32: dst = left.ToUInt32() + right; break;
+ case 64: dst = left.ToUInt64() + right; break;
+
+ default: throw new ArgumentOutOfRangeException();
}
- else if (left.Count <= 64)
+
+ return dst.SubBigInteger(left.Count - 1, 0);
+ }
+ public static Bits operator +(Bits left, Bits right) // ASL: "+".
+ {
+ if (((object)left == null) || ((object)right == null))
{
- dst = left.ToUInt64() + right;
+ throw new ArgumentNullException();
}
- else
+
+ if (left.Count != right.Count)
{
- throw new ArgumentOutOfRangeException();
+ throw new ArgumentException();
+ }
+
+ BigInteger dst;
+
+ switch (left.Count)
+ {
+ case 8: dst = left.ToByte() + (BigInteger)right.ToByte(); break;
+ case 16: dst = left.ToUInt16() + (BigInteger)right.ToUInt16(); break;
+ case 32: dst = left.ToUInt32() + (BigInteger)right.ToUInt32(); break;
+ case 64: dst = left.ToUInt64() + (BigInteger)right.ToUInt64(); break;
+
+ default: throw new ArgumentOutOfRangeException();
+ }
+
+ return dst.SubBigInteger(left.Count - 1, 0);
+ }
+ public static Bits operator -(Bits left, Bits right) // ASL: "-".
+ {
+ if (((object)left == null) || ((object)right == null))
+ {
+ throw new ArgumentNullException();
+ }
+
+ if (left.Count != right.Count)
+ {
+ throw new ArgumentException();
+ }
+
+ BigInteger dst;
+
+ switch (left.Count)
+ {
+ case 8: dst = left.ToByte() - (BigInteger)right.ToByte(); break;
+ case 16: dst = left.ToUInt16() - (BigInteger)right.ToUInt16(); break;
+ case 32: dst = left.ToUInt32() - (BigInteger)right.ToUInt32(); break;
+ case 64: dst = left.ToUInt64() - (BigInteger)right.ToUInt64(); break;
+
+ default: throw new ArgumentOutOfRangeException();
}
return dst.SubBigInteger(left.Count - 1, 0);