From 42e4e02a648812c4dee1574a5cd9e7dddf7b2458 Mon Sep 17 00:00:00 2001 From: LDj3SNuD <35856442+LDj3SNuD@users.noreply.github.com> Date: Sat, 1 Sep 2018 16:52:51 +0200 Subject: [PATCH] Add Fcvtns_S, Fcvtns_V, Fcvtnu_S, Fcvtnu_V (AOpCodeSimd) FP & Umlal_V, Umlsl_V, Saddl_V, Ssubl_V, Usubl_V instructions; add 8 FP & 16 S/Umlal_V, S/Umlsl_V, S/Uaddl_V, S/Usubl_V Tests. (#390) * Update AOpCodeTable.cs * Update AInstEmitSimdCvt.cs * Update Pseudocode.cs * Update Instructions.cs * Update CpuTestSimd.cs * Update AOpCodeTable.cs * Update AInstEmitSimdArithmetic.cs * Update Instructions.cs * Update CpuTestSimdReg.cs * Update CpuTestSimd.cs * Update AOpCodeTable.cs * Update AInstEmitSimdArithmetic.cs * Update Instructions.cs * Update CpuTestSimdReg.cs * Add QCFlagBit. * Add QCFlagBit. --- ChocolArm64/AOpCodeTable.cs | 9 + .../Instruction/AInstEmitSimdArithmetic.cs | 33 + ChocolArm64/Instruction/AInstEmitSimdCvt.cs | 70 +- Ryujinx.Tests/Cpu/CpuTestSimd.cs | 642 ++++++++++++----- Ryujinx.Tests/Cpu/CpuTestSimdReg.cs | 678 +++++++++++++++--- Ryujinx.Tests/Cpu/Tester/Instructions.cs | 572 +++++++++++++++ Ryujinx.Tests/Cpu/Tester/Pseudocode.cs | 490 ++++++++++++- 7 files changed, 2208 insertions(+), 286 deletions(-) diff --git a/ChocolArm64/AOpCodeTable.cs b/ChocolArm64/AOpCodeTable.cs index bf030314e..a73466ae1 100644 --- a/ChocolArm64/AOpCodeTable.cs +++ b/ChocolArm64/AOpCodeTable.cs @@ -255,6 +255,10 @@ namespace ChocolArm64 SetA64("x00111100x110000000000xxxxxxxxxx", AInstEmit.Fcvtms_Gp, typeof(AOpCodeSimdCvt)); SetA64("x00111100x110001000000xxxxxxxxxx", AInstEmit.Fcvtmu_Gp, typeof(AOpCodeSimdCvt)); SetA64("0x0011100x100001011010xxxxxxxxxx", AInstEmit.Fcvtn_V, typeof(AOpCodeSimd)); + SetA64("010111100x100001101010xxxxxxxxxx", AInstEmit.Fcvtns_S, typeof(AOpCodeSimd)); + SetA64("0>0011100<100001101010xxxxxxxxxx", AInstEmit.Fcvtns_V, typeof(AOpCodeSimd)); + SetA64("011111100x100001101010xxxxxxxxxx", AInstEmit.Fcvtnu_S, typeof(AOpCodeSimd)); + SetA64("0>1011100<100001101010xxxxxxxxxx", AInstEmit.Fcvtnu_V, typeof(AOpCodeSimd)); SetA64("x00111100x101000000000xxxxxxxxxx", AInstEmit.Fcvtps_Gp, typeof(AOpCodeSimdCvt)); SetA64("x00111100x101001000000xxxxxxxxxx", AInstEmit.Fcvtpu_Gp, typeof(AOpCodeSimdCvt)); SetA64("x00111100x111000000000xxxxxxxxxx", AInstEmit.Fcvtzs_Gp, typeof(AOpCodeSimdCvt)); @@ -365,6 +369,7 @@ namespace ChocolArm64 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<<1xxxxx000000xxxxxxxxxx", AInstEmit.Saddl_V, typeof(AOpCodeSimdReg)); SetA64("0x001110<<100000001010xxxxxxxxxx", AInstEmit.Saddlp_V, typeof(AOpCodeSimd)); SetA64("0x001110<<1xxxxx000100xxxxxxxxxx", AInstEmit.Saddw_V, typeof(AOpCodeSimdReg)); SetA64("x0011110xx100010000000xxxxxxxxxx", AInstEmit.Scvtf_Gp, typeof(AOpCodeSimdCvt)); @@ -419,6 +424,7 @@ namespace ChocolArm64 SetA64("0100111101xxxxxx000001xxxxxxxxxx", AInstEmit.Sshr_V, typeof(AOpCodeSimdShImm)); SetA64("0x00111100>>>xxx000101xxxxxxxxxx", AInstEmit.Ssra_V, typeof(AOpCodeSimdShImm)); SetA64("0100111101xxxxxx000101xxxxxxxxxx", AInstEmit.Ssra_V, typeof(AOpCodeSimdShImm)); + SetA64("0x001110<<1xxxxx001000xxxxxxxxxx", AInstEmit.Ssubl_V, typeof(AOpCodeSimdReg)); SetA64("0x001110<<1xxxxx001100xxxxxxxxxx", AInstEmit.Ssubw_V, typeof(AOpCodeSimdReg)); SetA64("0x00110000000000xxxxxxxxxxxxxxxx", AInstEmit.St__Vms, typeof(AOpCodeSimdMemMs)); SetA64("0x001100100xxxxxxxxxxxxxxxxxxxxx", AInstEmit.St__Vms, typeof(AOpCodeSimdMemMs)); @@ -457,6 +463,8 @@ namespace ChocolArm64 SetA64("0x101110<<1xxxxx101001xxxxxxxxxx", AInstEmit.Umaxp_V, typeof(AOpCodeSimdReg)); SetA64("0x101110<<1xxxxx011011xxxxxxxxxx", AInstEmit.Umin_V, typeof(AOpCodeSimdReg)); SetA64("0x101110<<1xxxxx101011xxxxxxxxxx", AInstEmit.Uminp_V, typeof(AOpCodeSimdReg)); + SetA64("0x101110<<1xxxxx100000xxxxxxxxxx", AInstEmit.Umlal_V, typeof(AOpCodeSimdReg)); + SetA64("0x101110<<1xxxxx101000xxxxxxxxxx", AInstEmit.Umlsl_V, typeof(AOpCodeSimdReg)); SetA64("0x001110000xxxxx001111xxxxxxxxxx", AInstEmit.Umov_S, typeof(AOpCodeSimdIns)); SetA64("0x101110<<1xxxxx110000xxxxxxxxxx", AInstEmit.Umull_V, typeof(AOpCodeSimdReg)); SetA64("01111110xx1xxxxx000011xxxxxxxxxx", AInstEmit.Uqadd_S, typeof(AOpCodeSimdReg)); @@ -475,6 +483,7 @@ namespace ChocolArm64 SetA64("0>101110<<100000001110xxxxxxxxxx", AInstEmit.Usqadd_V, typeof(AOpCodeSimd)); SetA64("0x10111100>>>xxx000101xxxxxxxxxx", AInstEmit.Usra_V, typeof(AOpCodeSimdShImm)); SetA64("0110111101xxxxxx000101xxxxxxxxxx", AInstEmit.Usra_V, typeof(AOpCodeSimdShImm)); + SetA64("0x101110<<1xxxxx001000xxxxxxxxxx", AInstEmit.Usubl_V, typeof(AOpCodeSimdReg)); SetA64("0x101110<<1xxxxx001100xxxxxxxxxx", AInstEmit.Usubw_V, typeof(AOpCodeSimdReg)); SetA64("0>001110<<0xxxxx000110xxxxxxxxxx", AInstEmit.Uzp1_V, typeof(AOpCodeSimdReg)); SetA64("0>001110<<0xxxxx010110xxxxxxxxxx", AInstEmit.Uzp2_V, typeof(AOpCodeSimdReg)); diff --git a/ChocolArm64/Instruction/AInstEmitSimdArithmetic.cs b/ChocolArm64/Instruction/AInstEmitSimdArithmetic.cs index 1e4002a0e..a291a7e51 100644 --- a/ChocolArm64/Instruction/AInstEmitSimdArithmetic.cs +++ b/ChocolArm64/Instruction/AInstEmitSimdArithmetic.cs @@ -1032,6 +1032,11 @@ namespace ChocolArm64.Instruction EmitAddLongPairwise(Context, Signed: true, Accumulate: true); } + public static void Saddl_V(AILEmitterCtx Context) + { + EmitVectorWidenRnRmBinaryOpSx(Context, () => Context.Emit(OpCodes.Add)); + } + public static void Saddlp_V(AILEmitterCtx Context) { EmitAddLongPairwise(Context, Signed: true, Accumulate: false); @@ -1217,6 +1222,11 @@ namespace ChocolArm64.Instruction }); } + public static void Ssubl_V(AILEmitterCtx Context) + { + EmitVectorWidenRnRmBinaryOpSx(Context, () => Context.Emit(OpCodes.Sub)); + } + public static void Ssubw_V(AILEmitterCtx Context) { EmitVectorWidenRmBinaryOpSx(Context, () => Context.Emit(OpCodes.Sub)); @@ -1391,6 +1401,24 @@ namespace ChocolArm64.Instruction EmitVectorPairwiseOpZx(Context, () => Context.EmitCall(MthdInfo)); } + public static void Umlal_V(AILEmitterCtx Context) + { + EmitVectorWidenRnRmTernaryOpZx(Context, () => + { + Context.Emit(OpCodes.Mul); + Context.Emit(OpCodes.Add); + }); + } + + public static void Umlsl_V(AILEmitterCtx Context) + { + EmitVectorWidenRnRmTernaryOpZx(Context, () => + { + Context.Emit(OpCodes.Mul); + Context.Emit(OpCodes.Sub); + }); + } + public static void Umull_V(AILEmitterCtx Context) { EmitVectorWidenRnRmBinaryOpZx(Context, () => Context.Emit(OpCodes.Mul)); @@ -1450,6 +1478,11 @@ namespace ChocolArm64.Instruction EmitVectorSaturatingBinaryOpZx(Context, SaturatingFlags.Accumulate); } + public static void Usubl_V(AILEmitterCtx Context) + { + EmitVectorWidenRnRmBinaryOpZx(Context, () => Context.Emit(OpCodes.Sub)); + } + public static void Usubw_V(AILEmitterCtx Context) { EmitVectorWidenRmBinaryOpZx(Context, () => Context.Emit(OpCodes.Sub)); diff --git a/ChocolArm64/Instruction/AInstEmitSimdCvt.cs b/ChocolArm64/Instruction/AInstEmitSimdCvt.cs index 7b355494d..231de0aff 100644 --- a/ChocolArm64/Instruction/AInstEmitSimdCvt.cs +++ b/ChocolArm64/Instruction/AInstEmitSimdCvt.cs @@ -106,6 +106,26 @@ namespace ChocolArm64.Instruction } } + public static void Fcvtns_S(AILEmitterCtx Context) + { + EmitFcvtn(Context, Signed: true, Scalar: true); + } + + public static void Fcvtns_V(AILEmitterCtx Context) + { + EmitFcvtn(Context, Signed: true, Scalar: false); + } + + public static void Fcvtnu_S(AILEmitterCtx Context) + { + EmitFcvtn(Context, Signed: false, Scalar: true); + } + + public static void Fcvtnu_V(AILEmitterCtx Context) + { + EmitFcvtn(Context, Signed: false, Scalar: false); + } + public static void Fcvtps_Gp(AILEmitterCtx Context) { EmitFcvt_s_Gp(Context, () => EmitUnaryMathCall(Context, nameof(Math.Ceiling))); @@ -250,6 +270,54 @@ namespace ChocolArm64.Instruction } } + private static void EmitFcvtn(AILEmitterCtx Context, bool Signed, bool Scalar) + { + AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp; + + int SizeF = Op.Size & 1; + int SizeI = SizeF + 2; + + int Bytes = Op.GetBitsCount() >> 3; + int Elems = !Scalar ? Bytes >> SizeI : 1; + + if (Scalar && (SizeF == 0)) + { + EmitVectorZeroLowerTmp(Context); + } + + for (int Index = 0; Index < Elems; Index++) + { + EmitVectorExtractF(Context, Op.Rn, Index, SizeF); + + EmitRoundMathCall(Context, MidpointRounding.ToEven); + + if (SizeF == 0) + { + AVectorHelper.EmitCall(Context, Signed + ? nameof(AVectorHelper.SatF32ToS32) + : nameof(AVectorHelper.SatF32ToU32)); + + Context.Emit(OpCodes.Conv_U8); + } + else /* if (SizeF == 1) */ + { + AVectorHelper.EmitCall(Context, Signed + ? nameof(AVectorHelper.SatF64ToS64) + : nameof(AVectorHelper.SatF64ToU64)); + } + + EmitVectorInsertTmp(Context, Index, SizeI); + } + + Context.EmitLdvectmp(); + Context.EmitStvec(Op.Rd); + + if ((Op.RegisterSize == ARegisterSize.SIMD64) || Scalar) + { + EmitVectorZeroUpper(Context, Op.Rd); + } + } + private static void EmitFcvt_s_Gp(AILEmitterCtx Context, Action Emit) { EmitFcvt___Gp(Context, Emit, true); @@ -569,4 +637,4 @@ namespace ChocolArm64.Instruction } } } -} \ No newline at end of file +} diff --git a/Ryujinx.Tests/Cpu/CpuTestSimd.cs b/Ryujinx.Tests/Cpu/CpuTestSimd.cs index dbf6b3c21..37fb3e976 100644 --- a/Ryujinx.Tests/Cpu/CpuTestSimd.cs +++ b/Ryujinx.Tests/Cpu/CpuTestSimd.cs @@ -85,11 +85,80 @@ namespace Ryujinx.Tests.Cpu 0x8000000080000000ul, 0x7FFFFFFFFFFFFFFFul, 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul }; } + + private static ulong[] _1S_F_() + { + return new ulong[] + { + 0x00000000FFFFFFFFul, // -QNaN (all ones payload) + 0x00000000FFBFFFFFul, // -SNaN (all ones payload) + 0x00000000FF800000ul, // -INF + 0x00000000FF7FFFFFul, // -Max Normal, float.MinValue + 0x0000000080800000ul, // -Min Normal + 0x00000000807FFFFFul, // -Max SubNormal + 0x0000000080000001ul, // -Min SubNormal + 0x0000000080000000ul, // -0 + 0x0000000000000000ul, // +0 + 0x0000000000000001ul, // +Min SubNormal + 0x00000000007FFFFFul, // +Max SubNormal + 0x0000000000800000ul, // +Min Normal + 0x000000007F7FFFFFul, // +Max Normal, float.MaxValue + 0x000000007F800000ul, // +INF + 0x000000007FBFFFFFul, // +SNaN (all ones payload) + 0x000000007FFFFFFFul // +QNaN (all ones payload) + }; + } + + private static ulong[] _2S_F_() + { + return new ulong[] + { + 0xFFFFFFFFFFFFFFFFul, // -QNaN (all ones payload) + 0xFFBFFFFFFFBFFFFFul, // -SNaN (all ones payload) + 0xFF800000FF800000ul, // -INF + 0xFF7FFFFFFF7FFFFFul, // -Max Normal, float.MinValue + 0x8080000080800000ul, // -Min Normal + 0x807FFFFF807FFFFFul, // -Max SubNormal + 0x8000000180000001ul, // -Min SubNormal + 0x8000000080000000ul, // -0 + 0x0000000000000000ul, // +0 + 0x0000000100000001ul, // +Min SubNormal + 0x007FFFFF007FFFFFul, // +Max SubNormal + 0x0080000000800000ul, // +Min Normal + 0x7F7FFFFF7F7FFFFFul, // +Max Normal, float.MaxValue + 0x7F8000007F800000ul, // +INF + 0x7FBFFFFF7FBFFFFFul, // +SNaN (all ones payload) + 0x7FFFFFFF7FFFFFFFul // +QNaN (all ones payload) + }; + } + + private static ulong[] _1D_F_() + { + return new ulong[] + { + 0xFFFFFFFFFFFFFFFFul, // -QNaN (all ones payload) + 0xFFF7FFFFFFFFFFFFul, // -SNaN (all ones payload) + 0xFFF0000000000000ul, // -INF + 0xFFEFFFFFFFFFFFFFul, // -Max Normal, double.MinValue + 0x8010000000000000ul, // -Min Normal + 0x800FFFFFFFFFFFFFul, // -Max SubNormal + 0x8000000000000001ul, // -Min SubNormal + 0x8000000000000000ul, // -0 + 0x0000000000000000ul, // +0 + 0x0000000000000001ul, // +Min SubNormal + 0x000FFFFFFFFFFFFFul, // +Max SubNormal + 0x0010000000000000ul, // +Min Normal + 0x7FEFFFFFFFFFFFFFul, // +Max Normal, double.MaxValue + 0x7FF0000000000000ul, // +INF + 0x7FF7FFFFFFFFFFFFul, // +SNaN (all ones payload) + 0x7FFFFFFFFFFFFFFFul // +QNaN (all ones payload) + }; + } #endregion - private const int RndCnt = 1; + private const int RndCnt = 4; - [Test, Description("ABS , ")] + [Test, Pairwise, Description("ABS , ")] public void Abs_S_D([Values(0u)] uint Rd, [Values(1u, 0u)] uint Rn, [ValueSource("_1D_")] [Random(RndCnt)] ulong Z, @@ -115,7 +184,7 @@ namespace Ryujinx.Tests.Cpu CompareAgainstUnicorn(); } - [Test, Description("ABS ., .")] + [Test, Pairwise, Description("ABS ., .")] public void Abs_V_8B_4H_2S([Values(0u)] uint Rd, [Values(1u, 0u)] uint Rn, [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong Z, @@ -143,7 +212,7 @@ namespace Ryujinx.Tests.Cpu CompareAgainstUnicorn(); } - [Test, Description("ABS ., .")] + [Test, Pairwise, Description("ABS ., .")] public void Abs_V_16B_8H_4S_2D([Values(0u)] uint Rd, [Values(1u, 0u)] uint Rn, [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong Z, @@ -171,7 +240,7 @@ namespace Ryujinx.Tests.Cpu CompareAgainstUnicorn(); } - [Test, Description("ADDP , .")] + [Test, Pairwise, Description("ADDP , .")] public void Addp_S_2DD([Values(0u)] uint Rd, [Values(1u, 0u)] uint Rn, [ValueSource("_1D_")] [Random(RndCnt)] ulong Z, @@ -197,7 +266,7 @@ namespace Ryujinx.Tests.Cpu CompareAgainstUnicorn(); } - [Test, Description("ADDV , .")] + [Test, Pairwise, Description("ADDV , .")] public void Addv_V_8BB_4HH([Values(0u)] uint Rd, [Values(1u, 0u)] uint Rn, [ValueSource("_8B4H_")] [Random(RndCnt)] ulong Z, @@ -225,7 +294,7 @@ namespace Ryujinx.Tests.Cpu CompareAgainstUnicorn(); } - [Test, Description("ADDV , .")] + [Test, Pairwise, Description("ADDV , .")] public void Addv_V_16BB_8HH_4SS([Values(0u)] uint Rd, [Values(1u, 0u)] uint Rn, [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong Z, @@ -253,7 +322,7 @@ namespace Ryujinx.Tests.Cpu CompareAgainstUnicorn(); } - [Test, Description("CLS ., .")] + [Test, Pairwise, Description("CLS ., .")] public void Cls_V_8B_4H_2S([Values(0u)] uint Rd, [Values(1u, 0u)] uint Rn, [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong Z, @@ -281,7 +350,7 @@ namespace Ryujinx.Tests.Cpu CompareAgainstUnicorn(); } - [Test, Description("CLS ., .")] + [Test, Pairwise, Description("CLS ., .")] public void Cls_V_16B_8H_4S([Values(0u)] uint Rd, [Values(1u, 0u)] uint Rn, [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong Z, @@ -309,7 +378,7 @@ namespace Ryujinx.Tests.Cpu CompareAgainstUnicorn(); } - [Test, Description("CLZ ., .")] + [Test, Pairwise, Description("CLZ ., .")] public void Clz_V_8B_4H_2S([Values(0u)] uint Rd, [Values(1u, 0u)] uint Rn, [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong Z, @@ -337,7 +406,7 @@ namespace Ryujinx.Tests.Cpu CompareAgainstUnicorn(); } - [Test, Description("CLZ ., .")] + [Test, Pairwise, Description("CLZ ., .")] public void Clz_V_16B_8H_4S([Values(0u)] uint Rd, [Values(1u, 0u)] uint Rn, [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong Z, @@ -365,7 +434,7 @@ namespace Ryujinx.Tests.Cpu CompareAgainstUnicorn(); } - [Test, Description("CMEQ , , #0")] + [Test, Pairwise, Description("CMEQ , , #0")] public void Cmeq_S_D([Values(0u)] uint Rd, [Values(1u, 0u)] uint Rn, [ValueSource("_1D_")] [Random(RndCnt)] ulong Z, @@ -391,7 +460,7 @@ namespace Ryujinx.Tests.Cpu CompareAgainstUnicorn(); } - [Test, Description("CMEQ ., ., #0")] + [Test, Pairwise, Description("CMEQ ., ., #0")] public void Cmeq_V_8B_4H_2S([Values(0u)] uint Rd, [Values(1u, 0u)] uint Rn, [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong Z, @@ -419,7 +488,7 @@ namespace Ryujinx.Tests.Cpu CompareAgainstUnicorn(); } - [Test, Description("CMEQ ., ., #0")] + [Test, Pairwise, Description("CMEQ ., ., #0")] public void Cmeq_V_16B_8H_4S_2D([Values(0u)] uint Rd, [Values(1u, 0u)] uint Rn, [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong Z, @@ -447,7 +516,7 @@ namespace Ryujinx.Tests.Cpu CompareAgainstUnicorn(); } - [Test, Description("CMGE , , #0")] + [Test, Pairwise, Description("CMGE , , #0")] public void Cmge_S_D([Values(0u)] uint Rd, [Values(1u, 0u)] uint Rn, [ValueSource("_1D_")] [Random(RndCnt)] ulong Z, @@ -473,7 +542,7 @@ namespace Ryujinx.Tests.Cpu CompareAgainstUnicorn(); } - [Test, Description("CMGE ., ., #0")] + [Test, Pairwise, Description("CMGE ., ., #0")] public void Cmge_V_8B_4H_2S([Values(0u)] uint Rd, [Values(1u, 0u)] uint Rn, [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong Z, @@ -501,7 +570,7 @@ namespace Ryujinx.Tests.Cpu CompareAgainstUnicorn(); } - [Test, Description("CMGE ., ., #0")] + [Test, Pairwise, Description("CMGE ., ., #0")] public void Cmge_V_16B_8H_4S_2D([Values(0u)] uint Rd, [Values(1u, 0u)] uint Rn, [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong Z, @@ -529,7 +598,7 @@ namespace Ryujinx.Tests.Cpu CompareAgainstUnicorn(); } - [Test, Description("CMGT , , #0")] + [Test, Pairwise, Description("CMGT , , #0")] public void Cmgt_S_D([Values(0u)] uint Rd, [Values(1u, 0u)] uint Rn, [ValueSource("_1D_")] [Random(RndCnt)] ulong Z, @@ -555,7 +624,7 @@ namespace Ryujinx.Tests.Cpu CompareAgainstUnicorn(); } - [Test, Description("CMGT ., ., #0")] + [Test, Pairwise, Description("CMGT ., ., #0")] public void Cmgt_V_8B_4H_2S([Values(0u)] uint Rd, [Values(1u, 0u)] uint Rn, [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong Z, @@ -583,7 +652,7 @@ namespace Ryujinx.Tests.Cpu CompareAgainstUnicorn(); } - [Test, Description("CMGT ., ., #0")] + [Test, Pairwise, Description("CMGT ., ., #0")] public void Cmgt_V_16B_8H_4S_2D([Values(0u)] uint Rd, [Values(1u, 0u)] uint Rn, [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong Z, @@ -611,7 +680,7 @@ namespace Ryujinx.Tests.Cpu CompareAgainstUnicorn(); } - [Test, Description("CMLE , , #0")] + [Test, Pairwise, Description("CMLE , , #0")] public void Cmle_S_D([Values(0u)] uint Rd, [Values(1u, 0u)] uint Rn, [ValueSource("_1D_")] [Random(RndCnt)] ulong Z, @@ -637,7 +706,7 @@ namespace Ryujinx.Tests.Cpu CompareAgainstUnicorn(); } - [Test, Description("CMLE ., ., #0")] + [Test, Pairwise, Description("CMLE ., ., #0")] public void Cmle_V_8B_4H_2S([Values(0u)] uint Rd, [Values(1u, 0u)] uint Rn, [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong Z, @@ -665,7 +734,7 @@ namespace Ryujinx.Tests.Cpu CompareAgainstUnicorn(); } - [Test, Description("CMLE ., ., #0")] + [Test, Pairwise, Description("CMLE ., ., #0")] public void Cmle_V_16B_8H_4S_2D([Values(0u)] uint Rd, [Values(1u, 0u)] uint Rn, [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong Z, @@ -693,7 +762,7 @@ namespace Ryujinx.Tests.Cpu CompareAgainstUnicorn(); } - [Test, Description("CMLT , , #0")] + [Test, Pairwise, Description("CMLT , , #0")] public void Cmlt_S_D([Values(0u)] uint Rd, [Values(1u, 0u)] uint Rn, [ValueSource("_1D_")] [Random(RndCnt)] ulong Z, @@ -719,7 +788,7 @@ namespace Ryujinx.Tests.Cpu CompareAgainstUnicorn(); } - [Test, Description("CMLT ., ., #0")] + [Test, Pairwise, Description("CMLT ., ., #0")] public void Cmlt_V_8B_4H_2S([Values(0u)] uint Rd, [Values(1u, 0u)] uint Rn, [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong Z, @@ -747,7 +816,7 @@ namespace Ryujinx.Tests.Cpu CompareAgainstUnicorn(); } - [Test, Description("CMLT ., ., #0")] + [Test, Pairwise, Description("CMLT ., ., #0")] public void Cmlt_V_16B_8H_4S_2D([Values(0u)] uint Rd, [Values(1u, 0u)] uint Rn, [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong Z, @@ -775,7 +844,7 @@ namespace Ryujinx.Tests.Cpu CompareAgainstUnicorn(); } - [Test, Description("CNT ., .")] + [Test, Pairwise, Description("CNT ., .")] public void Cnt_V_8B([Values(0u)] uint Rd, [Values(1u, 0u)] uint Rn, [ValueSource("_8B_")] [Random(RndCnt)] ulong Z, @@ -801,7 +870,7 @@ namespace Ryujinx.Tests.Cpu CompareAgainstUnicorn(); } - [Test, Description("CNT ., .")] + [Test, Pairwise, Description("CNT ., .")] public void Cnt_V_16B([Values(0u)] uint Rd, [Values(1u, 0u)] uint Rn, [ValueSource("_8B_")] [Random(RndCnt)] ulong Z, @@ -827,7 +896,243 @@ namespace Ryujinx.Tests.Cpu CompareAgainstUnicorn(); } - [Test, Description("NEG , ")] + [Test, Pairwise, Description("FCVTNS , ")] + public void Fcvtns_S_S([Values(0u)] uint Rd, + [Values(1u, 0u)] uint Rn, + [ValueSource("_1S_F_")] [Random(RndCnt)] ulong Z, + [ValueSource("_1S_F_")] [Random(RndCnt)] ulong A) + { + //const int FZFlagBit = 24; // Flush-to-zero mode control bit. + + //const int IDCFlagBit = 7; // Input Denormal cumulative floating-point exception bit. + //const int IXCFlagBit = 4; // Inexact cumulative floating-point exception bit. + //const int IOCFlagBit = 0; // Invalid Operation cumulative floating-point exception bit. + + uint Opcode = 0x5E21A800; // FCVTNS S0, S0 + Opcode |= ((Rn & 31) << 5) | ((Rd & 31) << 0); + Bits Op = new Bits(Opcode); + + //int Fpcr = 1 << FZFlagBit; // Flush-to-zero mode enabled. + + Vector128 V0 = MakeVectorE0E1(Z, Z); + Vector128 V1 = MakeVectorE0(A); + AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1/*, Fpcr: Fpcr*/); + + AArch64.Vpart(0, 0, new Bits(Z)); AArch64.Vpart(0, 1, new Bits(Z)); + AArch64.V(1, new Bits(A)); + //Shared.FPCR = new Bits((uint)Fpcr); + SimdFp.Fcvtns_S(Op[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())); + }); + + /*Assert.Multiple(() => + { + Assert.That(((ThreadState.Fpsr >> IDCFlagBit) & 1) != 0, Is.EqualTo(Shared.FPSR[IDCFlagBit])); + Assert.That(((ThreadState.Fpsr >> IXCFlagBit) & 1) != 0, Is.EqualTo(Shared.FPSR[IXCFlagBit])); + Assert.That(((ThreadState.Fpsr >> IOCFlagBit) & 1) != 0, Is.EqualTo(Shared.FPSR[IOCFlagBit])); + });*/ + } + + [Test, Pairwise, Description("FCVTNS , ")] + public void Fcvtns_S_D([Values(0u)] uint Rd, + [Values(1u, 0u)] uint Rn, + [ValueSource("_1D_F_")] [Random(RndCnt)] ulong Z, + [ValueSource("_1D_F_")] [Random(RndCnt)] ulong A) + { + uint Opcode = 0x5E61A800; // FCVTNS D0, D0 + Opcode |= ((Rn & 31) << 5) | ((Rd & 31) << 0); + 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.Fcvtns_S(Op[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, Pairwise, Description("FCVTNS ., .")] + public void Fcvtns_V_2S_4S([Values(0u)] uint Rd, + [Values(1u, 0u)] uint Rn, + [ValueSource("_2S_F_")] [Random(RndCnt)] ulong Z, + [ValueSource("_2S_F_")] [Random(RndCnt)] ulong A, + [Values(0b0u, 0b1u)] uint Q) // <2S, 4S> + { + uint Opcode = 0x0E21A800; // FCVTNS V0.2S, V0.2S + Opcode |= ((Rn & 31) << 5) | ((Rd & 31) << 0); + Opcode |= ((Q & 1) << 30); + Bits Op = new Bits(Opcode); + + Vector128 V0 = MakeVectorE0E1(Z, Z); + Vector128 V1 = MakeVectorE0E1(A, A * Q); + 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 * Q)); + SimdFp.Fcvtns_V(Op[30], Op[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, Pairwise, Description("FCVTNS ., .")] + public void Fcvtns_V_2D([Values(0u)] uint Rd, + [Values(1u, 0u)] uint Rn, + [ValueSource("_1D_F_")] [Random(RndCnt)] ulong Z, + [ValueSource("_1D_F_")] [Random(RndCnt)] ulong A) + { + uint Opcode = 0x4E61A800; // FCVTNS V0.2D, V0.2D + Opcode |= ((Rn & 31) << 5) | ((Rd & 31) << 0); + 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.Fcvtns_V(Op[30], Op[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, Pairwise, Description("FCVTNU , ")] + public void Fcvtnu_S_S([Values(0u)] uint Rd, + [Values(1u, 0u)] uint Rn, + [ValueSource("_1S_F_")] [Random(RndCnt)] ulong Z, + [ValueSource("_1S_F_")] [Random(RndCnt)] ulong A) + { + //const int FZFlagBit = 24; // Flush-to-zero mode control bit. + + //const int IDCFlagBit = 7; // Input Denormal cumulative floating-point exception bit. + //const int IXCFlagBit = 4; // Inexact cumulative floating-point exception bit. + //const int IOCFlagBit = 0; // Invalid Operation cumulative floating-point exception bit. + + uint Opcode = 0x7E21A800; // FCVTNU S0, S0 + Opcode |= ((Rn & 31) << 5) | ((Rd & 31) << 0); + Bits Op = new Bits(Opcode); + + //int Fpcr = 1 << FZFlagBit; // Flush-to-zero mode enabled. + + Vector128 V0 = MakeVectorE0E1(Z, Z); + Vector128 V1 = MakeVectorE0(A); + AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1/*, Fpcr: Fpcr*/); + + AArch64.Vpart(0, 0, new Bits(Z)); AArch64.Vpart(0, 1, new Bits(Z)); + AArch64.V(1, new Bits(A)); + //Shared.FPCR = new Bits((uint)Fpcr); + SimdFp.Fcvtnu_S(Op[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())); + }); + + /*Assert.Multiple(() => + { + Assert.That(((ThreadState.Fpsr >> IDCFlagBit) & 1) != 0, Is.EqualTo(Shared.FPSR[IDCFlagBit])); + Assert.That(((ThreadState.Fpsr >> IXCFlagBit) & 1) != 0, Is.EqualTo(Shared.FPSR[IXCFlagBit])); + Assert.That(((ThreadState.Fpsr >> IOCFlagBit) & 1) != 0, Is.EqualTo(Shared.FPSR[IOCFlagBit])); + });*/ + } + + [Test, Pairwise, Description("FCVTNU , ")] + public void Fcvtnu_S_D([Values(0u)] uint Rd, + [Values(1u, 0u)] uint Rn, + [ValueSource("_1D_F_")] [Random(RndCnt)] ulong Z, + [ValueSource("_1D_F_")] [Random(RndCnt)] ulong A) + { + uint Opcode = 0x7E61A800; // FCVTNU D0, D0 + Opcode |= ((Rn & 31) << 5) | ((Rd & 31) << 0); + 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.Fcvtnu_S(Op[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, Pairwise, Description("FCVTNU ., .")] + public void Fcvtnu_V_2S_4S([Values(0u)] uint Rd, + [Values(1u, 0u)] uint Rn, + [ValueSource("_2S_F_")] [Random(RndCnt)] ulong Z, + [ValueSource("_2S_F_")] [Random(RndCnt)] ulong A, + [Values(0b0u, 0b1u)] uint Q) // <2S, 4S> + { + uint Opcode = 0x2E21A800; // FCVTNU V0.2S, V0.2S + Opcode |= ((Rn & 31) << 5) | ((Rd & 31) << 0); + Opcode |= ((Q & 1) << 30); + Bits Op = new Bits(Opcode); + + Vector128 V0 = MakeVectorE0E1(Z, Z); + Vector128 V1 = MakeVectorE0E1(A, A * Q); + 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 * Q)); + SimdFp.Fcvtnu_V(Op[30], Op[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, Pairwise, Description("FCVTNU ., .")] + public void Fcvtnu_V_2D([Values(0u)] uint Rd, + [Values(1u, 0u)] uint Rn, + [ValueSource("_1D_F_")] [Random(RndCnt)] ulong Z, + [ValueSource("_1D_F_")] [Random(RndCnt)] ulong A) + { + uint Opcode = 0x6E61A800; // FCVTNU V0.2D, V0.2D + Opcode |= ((Rn & 31) << 5) | ((Rd & 31) << 0); + 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.Fcvtnu_V(Op[30], Op[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, Pairwise, Description("NEG , ")] public void Neg_S_D([Values(0u)] uint Rd, [Values(1u, 0u)] uint Rn, [ValueSource("_1D_")] [Random(RndCnt)] ulong Z, @@ -853,7 +1158,7 @@ namespace Ryujinx.Tests.Cpu CompareAgainstUnicorn(); } - [Test, Description("NEG ., .")] + [Test, Pairwise, Description("NEG ., .")] public void Neg_V_8B_4H_2S([Values(0u)] uint Rd, [Values(1u, 0u)] uint Rn, [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong Z, @@ -881,7 +1186,7 @@ namespace Ryujinx.Tests.Cpu CompareAgainstUnicorn(); } - [Test, Description("NEG ., .")] + [Test, Pairwise, Description("NEG ., .")] public void Neg_V_16B_8H_4S_2D([Values(0u)] uint Rd, [Values(1u, 0u)] uint Rn, [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong Z, @@ -909,7 +1214,7 @@ namespace Ryujinx.Tests.Cpu CompareAgainstUnicorn(); } - [Test, Description("NOT ., .")] + [Test, Pairwise, Description("NOT ., .")] public void Not_V_8B([Values(0u)] uint Rd, [Values(1u, 0u)] uint Rn, [ValueSource("_8B_")] [Random(RndCnt)] ulong Z, @@ -935,7 +1240,7 @@ namespace Ryujinx.Tests.Cpu CompareAgainstUnicorn(); } - [Test, Description("NOT ., .")] + [Test, Pairwise, Description("NOT ., .")] public void Not_V_16B([Values(0u)] uint Rd, [Values(1u, 0u)] uint Rn, [ValueSource("_8B_")] [Random(RndCnt)] ulong Z, @@ -961,7 +1266,7 @@ namespace Ryujinx.Tests.Cpu CompareAgainstUnicorn(); } - [Test, Description("RBIT ., .")] + [Test, Pairwise, Description("RBIT ., .")] public void Rbit_V_8B([Values(0u)] uint Rd, [Values(1u, 0u)] uint Rn, [ValueSource("_8B_")] [Random(RndCnt)] ulong Z, @@ -987,7 +1292,7 @@ namespace Ryujinx.Tests.Cpu CompareAgainstUnicorn(); } - [Test, Description("RBIT ., .")] + [Test, Pairwise, Description("RBIT ., .")] public void Rbit_V_16B([Values(0u)] uint Rd, [Values(1u, 0u)] uint Rn, [ValueSource("_8B_")] [Random(RndCnt)] ulong Z, @@ -1013,7 +1318,7 @@ namespace Ryujinx.Tests.Cpu CompareAgainstUnicorn(); } - [Test, Description("REV16 ., .")] + [Test, Pairwise, Description("REV16 ., .")] public void Rev16_V_8B([Values(0u)] uint Rd, [Values(1u, 0u)] uint Rn, [ValueSource("_8B_")] [Random(RndCnt)] ulong Z, @@ -1039,7 +1344,7 @@ namespace Ryujinx.Tests.Cpu CompareAgainstUnicorn(); } - [Test, Description("REV16 ., .")] + [Test, Pairwise, Description("REV16 ., .")] public void Rev16_V_16B([Values(0u)] uint Rd, [Values(1u, 0u)] uint Rn, [ValueSource("_8B_")] [Random(RndCnt)] ulong Z, @@ -1065,7 +1370,7 @@ namespace Ryujinx.Tests.Cpu CompareAgainstUnicorn(); } - [Test, Description("REV32 ., .")] + [Test, Pairwise, Description("REV32 ., .")] public void Rev32_V_8B_4H([Values(0u)] uint Rd, [Values(1u, 0u)] uint Rn, [ValueSource("_8B4H_")] [Random(RndCnt)] ulong Z, @@ -1093,7 +1398,7 @@ namespace Ryujinx.Tests.Cpu CompareAgainstUnicorn(); } - [Test, Description("REV32 ., .")] + [Test, Pairwise, Description("REV32 ., .")] public void Rev32_V_16B_8H([Values(0u)] uint Rd, [Values(1u, 0u)] uint Rn, [ValueSource("_8B4H_")] [Random(RndCnt)] ulong Z, @@ -1121,7 +1426,7 @@ namespace Ryujinx.Tests.Cpu CompareAgainstUnicorn(); } - [Test, Description("REV64 ., .")] + [Test, Pairwise, Description("REV64 ., .")] public void Rev64_V_8B_4H_2S([Values(0u)] uint Rd, [Values(1u, 0u)] uint Rn, [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong Z, @@ -1149,7 +1454,7 @@ namespace Ryujinx.Tests.Cpu CompareAgainstUnicorn(); } - [Test, Description("REV64 ., .")] + [Test, Pairwise, Description("REV64 ., .")] public void Rev64_V_16B_8H_4S([Values(0u)] uint Rd, [Values(1u, 0u)] uint Rn, [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong Z, @@ -1177,7 +1482,7 @@ namespace Ryujinx.Tests.Cpu CompareAgainstUnicorn(); } - [Test, Description("SADALP ., .")] + [Test, Pairwise, Description("SADALP ., .")] public void Sadalp_V_8B4H_4H2S_2S1D([Values(0u)] uint Rd, [Values(1u, 0u)] uint Rn, [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong Z, @@ -1205,7 +1510,7 @@ namespace Ryujinx.Tests.Cpu CompareAgainstUnicorn(); } - [Test, Description("SADALP ., .")] + [Test, Pairwise, Description("SADALP ., .")] public void Sadalp_V_16B8H_8H4S_4S2D([Values(0u)] uint Rd, [Values(1u, 0u)] uint Rn, [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong Z, @@ -1233,7 +1538,7 @@ namespace Ryujinx.Tests.Cpu CompareAgainstUnicorn(); } - [Test, Description("SADDLP ., .")] + [Test, Pairwise, Description("SADDLP ., .")] public void Saddlp_V_8B4H_4H2S_2S1D([Values(0u)] uint Rd, [Values(1u, 0u)] uint Rn, [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong Z, @@ -1261,7 +1566,7 @@ namespace Ryujinx.Tests.Cpu CompareAgainstUnicorn(); } - [Test, Description("SADDLP ., .")] + [Test, Pairwise, Description("SADDLP ., .")] public void Saddlp_V_16B8H_8H4S_4S2D([Values(0u)] uint Rd, [Values(1u, 0u)] uint Rn, [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong Z, @@ -1292,8 +1597,8 @@ namespace Ryujinx.Tests.Cpu [Test, Pairwise, Description("SHA256SU0 .4S, .4S")] public void Sha256su0_V([Values(0u)] uint Rd, [Values(1u, 0u)] uint Rn, - [Random(RndCnt * 2)] ulong Z0, [Random(RndCnt * 2)] ulong Z1, - [Random(RndCnt * 2)] ulong A0, [Random(RndCnt * 2)] ulong A1) + [Random(RndCnt / 2)] ulong Z0, [Random(RndCnt / 2)] ulong Z1, + [Random(RndCnt / 2)] ulong A0, [Random(RndCnt / 2)] ulong A1) { uint Opcode = 0x5E282800; // SHA256SU0 V0.4S, V0.4S Opcode |= ((Rn & 31) << 5) | ((Rd & 31) << 0); @@ -1320,27 +1625,26 @@ namespace Ryujinx.Tests.Cpu CompareAgainstUnicorn(); } - [Test, Description("SQABS , ")] + [Test, Pairwise, Description("SQABS , ")] public void Sqabs_S_B_H_S_D([Values(0u)] uint Rd, [Values(1u, 0u)] uint Rn, [ValueSource("_1B1H1S1D_")] [Random(RndCnt)] ulong Z, [ValueSource("_1B1H1S1D_")] [Random(RndCnt)] ulong A, [Values(0b00u, 0b01u, 0b10u, 0b11u)] uint size) // { + const int QCFlagBit = 27; // Cumulative saturation bit. + uint Opcode = 0x5E207800; // SQABS B0, B0 Opcode |= ((Rn & 31) << 5) | ((Rd & 31) << 0); Opcode |= ((size & 3) << 22); Bits Op = new Bits(Opcode); - int Fpsr = (int)TestContext.CurrentContext.Random.NextUInt(); - Vector128 V0 = MakeVectorE0E1(Z, Z); Vector128 V1 = MakeVectorE0(A); - AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, Fpsr: Fpsr); + 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)); - Shared.FPSR = new Bits((uint)Fpsr); SimdFp.Sqabs_S(Op[23, 22], Op[9, 5], Op[4, 0]); Assert.Multiple(() => @@ -1348,31 +1652,30 @@ namespace Ryujinx.Tests.Cpu 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())); }); - Assert.That(ThreadState.Fpsr, Is.EqualTo((int)Shared.FPSR.ToUInt32())); + Assert.That(((ThreadState.Fpsr >> QCFlagBit) & 1) != 0, Is.EqualTo(Shared.FPSR[QCFlagBit])); CompareAgainstUnicorn(); } - [Test, Description("SQABS ., .")] + [Test, Pairwise, Description("SQABS ., .")] public void Sqabs_V_8B_4H_2S([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) // <8B, 4H, 2S> { + const int QCFlagBit = 27; // Cumulative saturation bit. + uint Opcode = 0x0E207800; // SQABS V0.8B, V0.8B Opcode |= ((Rn & 31) << 5) | ((Rd & 31) << 0); Opcode |= ((size & 3) << 22); Bits Op = new Bits(Opcode); - int Fpsr = (int)TestContext.CurrentContext.Random.NextUInt(); - Vector128 V0 = MakeVectorE0E1(Z, Z); Vector128 V1 = MakeVectorE0(A); - AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, Fpsr: Fpsr); + 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)); - Shared.FPSR = new Bits((uint)Fpsr); SimdFp.Sqabs_V(Op[30], Op[23, 22], Op[9, 5], Op[4, 0]); Assert.Multiple(() => @@ -1380,31 +1683,30 @@ namespace Ryujinx.Tests.Cpu 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())); }); - Assert.That(ThreadState.Fpsr, Is.EqualTo((int)Shared.FPSR.ToUInt32())); + Assert.That(((ThreadState.Fpsr >> QCFlagBit) & 1) != 0, Is.EqualTo(Shared.FPSR[QCFlagBit])); CompareAgainstUnicorn(); } - [Test, Description("SQABS ., .")] + [Test, Pairwise, Description("SQABS ., .")] public void Sqabs_V_16B_8H_4S_2D([Values(0u)] uint Rd, [Values(1u, 0u)] uint Rn, [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong Z, [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong A, [Values(0b00u, 0b01u, 0b10u, 0b11u)] uint size) // <16B, 8H, 4S, 2D> { + const int QCFlagBit = 27; // Cumulative saturation bit. + uint Opcode = 0x4E207800; // SQABS V0.16B, V0.16B Opcode |= ((Rn & 31) << 5) | ((Rd & 31) << 0); Opcode |= ((size & 3) << 22); Bits Op = new Bits(Opcode); - int Fpsr = (int)TestContext.CurrentContext.Random.NextUInt(); - Vector128 V0 = MakeVectorE0E1(Z, Z); Vector128 V1 = MakeVectorE0E1(A, A); - AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, Fpsr: Fpsr); + 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)); - Shared.FPSR = new Bits((uint)Fpsr); SimdFp.Sqabs_V(Op[30], Op[23, 22], Op[9, 5], Op[4, 0]); Assert.Multiple(() => @@ -1412,31 +1714,30 @@ namespace Ryujinx.Tests.Cpu 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())); }); - Assert.That(ThreadState.Fpsr, Is.EqualTo((int)Shared.FPSR.ToUInt32())); + Assert.That(((ThreadState.Fpsr >> QCFlagBit) & 1) != 0, Is.EqualTo(Shared.FPSR[QCFlagBit])); CompareAgainstUnicorn(); } - [Test, Description("SQNEG , ")] + [Test, Pairwise, Description("SQNEG , ")] public void Sqneg_S_B_H_S_D([Values(0u)] uint Rd, [Values(1u, 0u)] uint Rn, [ValueSource("_1B1H1S1D_")] [Random(RndCnt)] ulong Z, [ValueSource("_1B1H1S1D_")] [Random(RndCnt)] ulong A, [Values(0b00u, 0b01u, 0b10u, 0b11u)] uint size) // { + const int QCFlagBit = 27; // Cumulative saturation bit. + uint Opcode = 0x7E207800; // SQNEG B0, B0 Opcode |= ((Rn & 31) << 5) | ((Rd & 31) << 0); Opcode |= ((size & 3) << 22); Bits Op = new Bits(Opcode); - int Fpsr = (int)TestContext.CurrentContext.Random.NextUInt(); - Vector128 V0 = MakeVectorE0E1(Z, Z); Vector128 V1 = MakeVectorE0(A); - AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, Fpsr: Fpsr); + 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)); - Shared.FPSR = new Bits((uint)Fpsr); SimdFp.Sqneg_S(Op[23, 22], Op[9, 5], Op[4, 0]); Assert.Multiple(() => @@ -1444,31 +1745,30 @@ namespace Ryujinx.Tests.Cpu 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())); }); - Assert.That(ThreadState.Fpsr, Is.EqualTo((int)Shared.FPSR.ToUInt32())); + Assert.That(((ThreadState.Fpsr >> QCFlagBit) & 1) != 0, Is.EqualTo(Shared.FPSR[QCFlagBit])); CompareAgainstUnicorn(); } - [Test, Description("SQNEG ., .")] + [Test, Pairwise, Description("SQNEG ., .")] public void Sqneg_V_8B_4H_2S([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) // <8B, 4H, 2S> { + const int QCFlagBit = 27; // Cumulative saturation bit. + uint Opcode = 0x2E207800; // SQNEG V0.8B, V0.8B Opcode |= ((Rn & 31) << 5) | ((Rd & 31) << 0); Opcode |= ((size & 3) << 22); Bits Op = new Bits(Opcode); - int Fpsr = (int)TestContext.CurrentContext.Random.NextUInt(); - Vector128 V0 = MakeVectorE0E1(Z, Z); Vector128 V1 = MakeVectorE0(A); - AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, Fpsr: Fpsr); + 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)); - Shared.FPSR = new Bits((uint)Fpsr); SimdFp.Sqneg_V(Op[30], Op[23, 22], Op[9, 5], Op[4, 0]); Assert.Multiple(() => @@ -1476,31 +1776,30 @@ namespace Ryujinx.Tests.Cpu 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())); }); - Assert.That(ThreadState.Fpsr, Is.EqualTo((int)Shared.FPSR.ToUInt32())); + Assert.That(((ThreadState.Fpsr >> QCFlagBit) & 1) != 0, Is.EqualTo(Shared.FPSR[QCFlagBit])); CompareAgainstUnicorn(); } - [Test, Description("SQNEG ., .")] + [Test, Pairwise, Description("SQNEG ., .")] public void Sqneg_V_16B_8H_4S_2D([Values(0u)] uint Rd, [Values(1u, 0u)] uint Rn, [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong Z, [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong A, [Values(0b00u, 0b01u, 0b10u, 0b11u)] uint size) // <16B, 8H, 4S, 2D> { + const int QCFlagBit = 27; // Cumulative saturation bit. + uint Opcode = 0x6E207800; // SQNEG V0.16B, V0.16B Opcode |= ((Rn & 31) << 5) | ((Rd & 31) << 0); Opcode |= ((size & 3) << 22); Bits Op = new Bits(Opcode); - int Fpsr = (int)TestContext.CurrentContext.Random.NextUInt(); - Vector128 V0 = MakeVectorE0E1(Z, Z); Vector128 V1 = MakeVectorE0E1(A, A); - AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, Fpsr: Fpsr); + 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)); - Shared.FPSR = new Bits((uint)Fpsr); SimdFp.Sqneg_V(Op[30], Op[23, 22], Op[9, 5], Op[4, 0]); Assert.Multiple(() => @@ -1508,31 +1807,30 @@ namespace Ryujinx.Tests.Cpu 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())); }); - Assert.That(ThreadState.Fpsr, Is.EqualTo((int)Shared.FPSR.ToUInt32())); + Assert.That(((ThreadState.Fpsr >> QCFlagBit) & 1) != 0, Is.EqualTo(Shared.FPSR[QCFlagBit])); CompareAgainstUnicorn(); } - [Test, Description("SQXTN , ")] + [Test, Pairwise, Description("SQXTN , ")] public void Sqxtn_S_HB_SH_DS([Values(0u)] uint Rd, [Values(1u, 0u)] uint Rn, [ValueSource("_1H1S1D_")] [Random(RndCnt)] ulong Z, [ValueSource("_1H1S1D_")] [Random(RndCnt)] ulong A, [Values(0b00u, 0b01u, 0b10u)] uint size) // { + const int QCFlagBit = 27; // Cumulative saturation bit. + uint Opcode = 0x5E214800; // SQXTN B0, H0 Opcode |= ((Rn & 31) << 5) | ((Rd & 31) << 0); Opcode |= ((size & 3) << 22); Bits Op = new Bits(Opcode); - int Fpsr = (int)TestContext.CurrentContext.Random.NextUInt(); - Vector128 V0 = MakeVectorE0E1(Z, Z); Vector128 V1 = MakeVectorE0(A); - AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, Fpsr: Fpsr); + 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)); - Shared.FPSR = new Bits((uint)Fpsr); SimdFp.Sqxtn_S(Op[23, 22], Op[9, 5], Op[4, 0]); Assert.Multiple(() => @@ -1540,31 +1838,30 @@ namespace Ryujinx.Tests.Cpu 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())); }); - Assert.That(ThreadState.Fpsr, Is.EqualTo((int)Shared.FPSR.ToUInt32())); + Assert.That(((ThreadState.Fpsr >> QCFlagBit) & 1) != 0, Is.EqualTo(Shared.FPSR[QCFlagBit])); CompareAgainstUnicorn(); } - [Test, Description("SQXTN{2} ., .")] + [Test, Pairwise, Description("SQXTN{2} ., .")] public void Sqxtn_V_8H8B_4S4H_2D2S([Values(0u)] uint Rd, [Values(1u, 0u)] uint Rn, [ValueSource("_4H2S1D_")] [Random(RndCnt)] ulong Z, [ValueSource("_4H2S1D_")] [Random(RndCnt)] ulong A, [Values(0b00u, 0b01u, 0b10u)] uint size) // <8H8B, 4S4H, 2D2S> { + const int QCFlagBit = 27; // Cumulative saturation bit. + uint Opcode = 0x0E214800; // SQXTN V0.8B, V0.8H Opcode |= ((Rn & 31) << 5) | ((Rd & 31) << 0); Opcode |= ((size & 3) << 22); Bits Op = new Bits(Opcode); - int Fpsr = (int)TestContext.CurrentContext.Random.NextUInt(); - Vector128 V0 = MakeVectorE0E1(Z, Z); Vector128 V1 = MakeVectorE0E1(A, A); - AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, Fpsr: Fpsr); + 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)); - Shared.FPSR = new Bits((uint)Fpsr); SimdFp.Sqxtn_V(Op[30], Op[23, 22], Op[9, 5], Op[4, 0]); Assert.Multiple(() => @@ -1572,31 +1869,30 @@ namespace Ryujinx.Tests.Cpu 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())); }); - Assert.That(ThreadState.Fpsr, Is.EqualTo((int)Shared.FPSR.ToUInt32())); + Assert.That(((ThreadState.Fpsr >> QCFlagBit) & 1) != 0, Is.EqualTo(Shared.FPSR[QCFlagBit])); CompareAgainstUnicorn(); } - [Test, Description("SQXTN{2} ., .")] + [Test, Pairwise, Description("SQXTN{2} ., .")] public void Sqxtn_V_8H16B_4S8H_2D4S([Values(0u)] uint Rd, [Values(1u, 0u)] uint Rn, [ValueSource("_4H2S1D_")] [Random(RndCnt)] ulong Z, [ValueSource("_4H2S1D_")] [Random(RndCnt)] ulong A, [Values(0b00u, 0b01u, 0b10u)] uint size) // <8H16B, 4S8H, 2D4S> { + const int QCFlagBit = 27; // Cumulative saturation bit. + uint Opcode = 0x4E214800; // SQXTN2 V0.16B, V0.8H Opcode |= ((Rn & 31) << 5) | ((Rd & 31) << 0); Opcode |= ((size & 3) << 22); Bits Op = new Bits(Opcode); - int Fpsr = (int)TestContext.CurrentContext.Random.NextUInt(); - Vector128 V0 = MakeVectorE0E1(Z, Z); Vector128 V1 = MakeVectorE0E1(A, A); - AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, Fpsr: Fpsr); + 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)); - Shared.FPSR = new Bits((uint)Fpsr); SimdFp.Sqxtn_V(Op[30], Op[23, 22], Op[9, 5], Op[4, 0]); Assert.Multiple(() => @@ -1604,31 +1900,30 @@ namespace Ryujinx.Tests.Cpu 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())); }); - Assert.That(ThreadState.Fpsr, Is.EqualTo((int)Shared.FPSR.ToUInt32())); + Assert.That(((ThreadState.Fpsr >> QCFlagBit) & 1) != 0, Is.EqualTo(Shared.FPSR[QCFlagBit])); CompareAgainstUnicorn(); } - [Test, Description("SQXTUN , ")] + [Test, Pairwise, Description("SQXTUN , ")] public void Sqxtun_S_HB_SH_DS([Values(0u)] uint Rd, [Values(1u, 0u)] uint Rn, [ValueSource("_1H1S1D_")] [Random(RndCnt)] ulong Z, [ValueSource("_1H1S1D_")] [Random(RndCnt)] ulong A, [Values(0b00u, 0b01u, 0b10u)] uint size) // { + const int QCFlagBit = 27; // Cumulative saturation bit. + uint Opcode = 0x7E212800; // SQXTUN B0, H0 Opcode |= ((Rn & 31) << 5) | ((Rd & 31) << 0); Opcode |= ((size & 3) << 22); Bits Op = new Bits(Opcode); - int Fpsr = (int)TestContext.CurrentContext.Random.NextUInt(); - Vector128 V0 = MakeVectorE0E1(Z, Z); Vector128 V1 = MakeVectorE0(A); - AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, Fpsr: Fpsr); + 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)); - Shared.FPSR = new Bits((uint)Fpsr); SimdFp.Sqxtun_S(Op[23, 22], Op[9, 5], Op[4, 0]); Assert.Multiple(() => @@ -1636,31 +1931,30 @@ namespace Ryujinx.Tests.Cpu 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())); }); - Assert.That(ThreadState.Fpsr, Is.EqualTo((int)Shared.FPSR.ToUInt32())); + Assert.That(((ThreadState.Fpsr >> QCFlagBit) & 1) != 0, Is.EqualTo(Shared.FPSR[QCFlagBit])); CompareAgainstUnicorn(); } - [Test, Description("SQXTUN{2} ., .")] + [Test, Pairwise, Description("SQXTUN{2} ., .")] public void Sqxtun_V_8H8B_4S4H_2D2S([Values(0u)] uint Rd, [Values(1u, 0u)] uint Rn, [ValueSource("_4H2S1D_")] [Random(RndCnt)] ulong Z, [ValueSource("_4H2S1D_")] [Random(RndCnt)] ulong A, [Values(0b00u, 0b01u, 0b10u)] uint size) // <8H8B, 4S4H, 2D2S> { + const int QCFlagBit = 27; // Cumulative saturation bit. + uint Opcode = 0x2E212800; // SQXTUN V0.8B, V0.8H Opcode |= ((Rn & 31) << 5) | ((Rd & 31) << 0); Opcode |= ((size & 3) << 22); Bits Op = new Bits(Opcode); - int Fpsr = (int)TestContext.CurrentContext.Random.NextUInt(); - Vector128 V0 = MakeVectorE0E1(Z, Z); Vector128 V1 = MakeVectorE0E1(A, A); - AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, Fpsr: Fpsr); + 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)); - Shared.FPSR = new Bits((uint)Fpsr); SimdFp.Sqxtun_V(Op[30], Op[23, 22], Op[9, 5], Op[4, 0]); Assert.Multiple(() => @@ -1668,31 +1962,30 @@ namespace Ryujinx.Tests.Cpu 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())); }); - Assert.That(ThreadState.Fpsr, Is.EqualTo((int)Shared.FPSR.ToUInt32())); + Assert.That(((ThreadState.Fpsr >> QCFlagBit) & 1) != 0, Is.EqualTo(Shared.FPSR[QCFlagBit])); CompareAgainstUnicorn(); } - [Test, Description("SQXTUN{2} ., .")] + [Test, Pairwise, Description("SQXTUN{2} ., .")] public void Sqxtun_V_8H16B_4S8H_2D4S([Values(0u)] uint Rd, [Values(1u, 0u)] uint Rn, [ValueSource("_4H2S1D_")] [Random(RndCnt)] ulong Z, [ValueSource("_4H2S1D_")] [Random(RndCnt)] ulong A, [Values(0b00u, 0b01u, 0b10u)] uint size) // <8H16B, 4S8H, 2D4S> { + const int QCFlagBit = 27; // Cumulative saturation bit. + uint Opcode = 0x6E212800; // SQXTUN2 V0.16B, V0.8H Opcode |= ((Rn & 31) << 5) | ((Rd & 31) << 0); Opcode |= ((size & 3) << 22); Bits Op = new Bits(Opcode); - int Fpsr = (int)TestContext.CurrentContext.Random.NextUInt(); - Vector128 V0 = MakeVectorE0E1(Z, Z); Vector128 V1 = MakeVectorE0E1(A, A); - AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, Fpsr: Fpsr); + 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)); - Shared.FPSR = new Bits((uint)Fpsr); SimdFp.Sqxtun_V(Op[30], Op[23, 22], Op[9, 5], Op[4, 0]); Assert.Multiple(() => @@ -1700,31 +1993,30 @@ namespace Ryujinx.Tests.Cpu 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())); }); - Assert.That(ThreadState.Fpsr, Is.EqualTo((int)Shared.FPSR.ToUInt32())); + Assert.That(((ThreadState.Fpsr >> QCFlagBit) & 1) != 0, Is.EqualTo(Shared.FPSR[QCFlagBit])); CompareAgainstUnicorn(); } - [Test, Description("SUQADD , ")] + [Test, Pairwise, Description("SUQADD , ")] public void Suqadd_S_B_H_S_D([Values(0u)] uint Rd, [Values(1u, 0u)] uint Rn, [ValueSource("_1B1H1S1D_")] [Random(RndCnt)] ulong Z, [ValueSource("_1B1H1S1D_")] [Random(RndCnt)] ulong A, [Values(0b00u, 0b01u, 0b10u, 0b11u)] uint size) // { + const int QCFlagBit = 27; // Cumulative saturation bit. + uint Opcode = 0x5E203800; // SUQADD B0, B0 Opcode |= ((Rn & 31) << 5) | ((Rd & 31) << 0); Opcode |= ((size & 3) << 22); Bits Op = new Bits(Opcode); - int Fpsr = (int)TestContext.CurrentContext.Random.NextUInt(); - Vector128 V0 = MakeVectorE0E1(Z, Z); Vector128 V1 = MakeVectorE0(A); - AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, Fpsr: Fpsr); + 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)); - Shared.FPSR = new Bits((uint)Fpsr); SimdFp.Suqadd_S(Op[23, 22], Op[9, 5], Op[4, 0]); Assert.Multiple(() => @@ -1732,31 +2024,30 @@ namespace Ryujinx.Tests.Cpu 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())); }); - Assert.That(ThreadState.Fpsr, Is.EqualTo((int)Shared.FPSR.ToUInt32())); + Assert.That(((ThreadState.Fpsr >> QCFlagBit) & 1) != 0, Is.EqualTo(Shared.FPSR[QCFlagBit])); CompareAgainstUnicorn(); } - [Test, Description("SUQADD ., .")] + [Test, Pairwise, Description("SUQADD ., .")] public void Suqadd_V_8B_4H_2S([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) // <8B, 4H, 2S> { + const int QCFlagBit = 27; // Cumulative saturation bit. + uint Opcode = 0x0E203800; // SUQADD V0.8B, V0.8B Opcode |= ((Rn & 31) << 5) | ((Rd & 31) << 0); Opcode |= ((size & 3) << 22); Bits Op = new Bits(Opcode); - int Fpsr = (int)TestContext.CurrentContext.Random.NextUInt(); - Vector128 V0 = MakeVectorE0E1(Z, Z); Vector128 V1 = MakeVectorE0(A); - AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, Fpsr: Fpsr); + 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)); - Shared.FPSR = new Bits((uint)Fpsr); SimdFp.Suqadd_V(Op[30], Op[23, 22], Op[9, 5], Op[4, 0]); Assert.Multiple(() => @@ -1764,31 +2055,30 @@ namespace Ryujinx.Tests.Cpu 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())); }); - Assert.That(ThreadState.Fpsr, Is.EqualTo((int)Shared.FPSR.ToUInt32())); + Assert.That(((ThreadState.Fpsr >> QCFlagBit) & 1) != 0, Is.EqualTo(Shared.FPSR[QCFlagBit])); CompareAgainstUnicorn(); } - [Test, Description("SUQADD ., .")] + [Test, Pairwise, Description("SUQADD ., .")] public void Suqadd_V_16B_8H_4S_2D([Values(0u)] uint Rd, [Values(1u, 0u)] uint Rn, [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong Z, [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong A, [Values(0b00u, 0b01u, 0b10u, 0b11u)] uint size) // <16B, 8H, 4S, 2D> { + const int QCFlagBit = 27; // Cumulative saturation bit. + uint Opcode = 0x4E203800; // SUQADD V0.16B, V0.16B Opcode |= ((Rn & 31) << 5) | ((Rd & 31) << 0); Opcode |= ((size & 3) << 22); Bits Op = new Bits(Opcode); - int Fpsr = (int)TestContext.CurrentContext.Random.NextUInt(); - Vector128 V0 = MakeVectorE0E1(Z, Z); Vector128 V1 = MakeVectorE0E1(A, A); - AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, Fpsr: Fpsr); + 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)); - Shared.FPSR = new Bits((uint)Fpsr); SimdFp.Suqadd_V(Op[30], Op[23, 22], Op[9, 5], Op[4, 0]); Assert.Multiple(() => @@ -1796,11 +2086,11 @@ namespace Ryujinx.Tests.Cpu 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())); }); - Assert.That(ThreadState.Fpsr, Is.EqualTo((int)Shared.FPSR.ToUInt32())); + Assert.That(((ThreadState.Fpsr >> QCFlagBit) & 1) != 0, Is.EqualTo(Shared.FPSR[QCFlagBit])); CompareAgainstUnicorn(); } - [Test, Description("UADALP ., .")] + [Test, Pairwise, Description("UADALP ., .")] public void Uadalp_V_8B4H_4H2S_2S1D([Values(0u)] uint Rd, [Values(1u, 0u)] uint Rn, [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong Z, @@ -1828,7 +2118,7 @@ namespace Ryujinx.Tests.Cpu CompareAgainstUnicorn(); } - [Test, Description("UADALP ., .")] + [Test, Pairwise, Description("UADALP ., .")] public void Uadalp_V_16B8H_8H4S_4S2D([Values(0u)] uint Rd, [Values(1u, 0u)] uint Rn, [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong Z, @@ -1856,7 +2146,7 @@ namespace Ryujinx.Tests.Cpu CompareAgainstUnicorn(); } - [Test, Description("UADDLP ., .")] + [Test, Pairwise, Description("UADDLP ., .")] public void Uaddlp_V_8B4H_4H2S_2S1D([Values(0u)] uint Rd, [Values(1u, 0u)] uint Rn, [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong Z, @@ -1884,7 +2174,7 @@ namespace Ryujinx.Tests.Cpu CompareAgainstUnicorn(); } - [Test, Description("UADDLP ., .")] + [Test, Pairwise, Description("UADDLP ., .")] public void Uaddlp_V_16B8H_8H4S_4S2D([Values(0u)] uint Rd, [Values(1u, 0u)] uint Rn, [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong Z, @@ -1912,27 +2202,26 @@ namespace Ryujinx.Tests.Cpu CompareAgainstUnicorn(); } - [Test, Description("UQXTN , ")] + [Test, Pairwise, Description("UQXTN , ")] public void Uqxtn_S_HB_SH_DS([Values(0u)] uint Rd, [Values(1u, 0u)] uint Rn, [ValueSource("_1H1S1D_")] [Random(RndCnt)] ulong Z, [ValueSource("_1H1S1D_")] [Random(RndCnt)] ulong A, [Values(0b00u, 0b01u, 0b10u)] uint size) // { + const int QCFlagBit = 27; // Cumulative saturation bit. + uint Opcode = 0x7E214800; // UQXTN B0, H0 Opcode |= ((Rn & 31) << 5) | ((Rd & 31) << 0); Opcode |= ((size & 3) << 22); Bits Op = new Bits(Opcode); - int Fpsr = (int)TestContext.CurrentContext.Random.NextUInt(); - Vector128 V0 = MakeVectorE0E1(Z, Z); Vector128 V1 = MakeVectorE0(A); - AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, Fpsr: Fpsr); + 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)); - Shared.FPSR = new Bits((uint)Fpsr); SimdFp.Uqxtn_S(Op[23, 22], Op[9, 5], Op[4, 0]); Assert.Multiple(() => @@ -1940,31 +2229,30 @@ namespace Ryujinx.Tests.Cpu 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())); }); - Assert.That(ThreadState.Fpsr, Is.EqualTo((int)Shared.FPSR.ToUInt32())); + Assert.That(((ThreadState.Fpsr >> QCFlagBit) & 1) != 0, Is.EqualTo(Shared.FPSR[QCFlagBit])); CompareAgainstUnicorn(); } - [Test, Description("UQXTN{2} ., .")] + [Test, Pairwise, Description("UQXTN{2} ., .")] public void Uqxtn_V_8H8B_4S4H_2D2S([Values(0u)] uint Rd, [Values(1u, 0u)] uint Rn, [ValueSource("_4H2S1D_")] [Random(RndCnt)] ulong Z, [ValueSource("_4H2S1D_")] [Random(RndCnt)] ulong A, [Values(0b00u, 0b01u, 0b10u)] uint size) // <8H8B, 4S4H, 2D2S> { + const int QCFlagBit = 27; // Cumulative saturation bit. + uint Opcode = 0x2E214800; // UQXTN V0.8B, V0.8H Opcode |= ((Rn & 31) << 5) | ((Rd & 31) << 0); Opcode |= ((size & 3) << 22); Bits Op = new Bits(Opcode); - int Fpsr = (int)TestContext.CurrentContext.Random.NextUInt(); - Vector128 V0 = MakeVectorE0E1(Z, Z); Vector128 V1 = MakeVectorE0E1(A, A); - AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, Fpsr: Fpsr); + 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)); - Shared.FPSR = new Bits((uint)Fpsr); SimdFp.Uqxtn_V(Op[30], Op[23, 22], Op[9, 5], Op[4, 0]); Assert.Multiple(() => @@ -1972,31 +2260,30 @@ namespace Ryujinx.Tests.Cpu 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())); }); - Assert.That(ThreadState.Fpsr, Is.EqualTo((int)Shared.FPSR.ToUInt32())); + Assert.That(((ThreadState.Fpsr >> QCFlagBit) & 1) != 0, Is.EqualTo(Shared.FPSR[QCFlagBit])); CompareAgainstUnicorn(); } - [Test, Description("UQXTN{2} ., .")] + [Test, Pairwise, Description("UQXTN{2} ., .")] public void Uqxtn_V_8H16B_4S8H_2D4S([Values(0u)] uint Rd, [Values(1u, 0u)] uint Rn, [ValueSource("_4H2S1D_")] [Random(RndCnt)] ulong Z, [ValueSource("_4H2S1D_")] [Random(RndCnt)] ulong A, [Values(0b00u, 0b01u, 0b10u)] uint size) // <8H16B, 4S8H, 2D4S> { + const int QCFlagBit = 27; // Cumulative saturation bit. + uint Opcode = 0x6E214800; // UQXTN2 V0.16B, V0.8H Opcode |= ((Rn & 31) << 5) | ((Rd & 31) << 0); Opcode |= ((size & 3) << 22); Bits Op = new Bits(Opcode); - int Fpsr = (int)TestContext.CurrentContext.Random.NextUInt(); - Vector128 V0 = MakeVectorE0E1(Z, Z); Vector128 V1 = MakeVectorE0E1(A, A); - AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, Fpsr: Fpsr); + 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)); - Shared.FPSR = new Bits((uint)Fpsr); SimdFp.Uqxtn_V(Op[30], Op[23, 22], Op[9, 5], Op[4, 0]); Assert.Multiple(() => @@ -2004,31 +2291,30 @@ namespace Ryujinx.Tests.Cpu 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())); }); - Assert.That(ThreadState.Fpsr, Is.EqualTo((int)Shared.FPSR.ToUInt32())); + Assert.That(((ThreadState.Fpsr >> QCFlagBit) & 1) != 0, Is.EqualTo(Shared.FPSR[QCFlagBit])); CompareAgainstUnicorn(); } - [Test, Description("USQADD , ")] + [Test, Pairwise, Description("USQADD , ")] public void Usqadd_S_B_H_S_D([Values(0u)] uint Rd, [Values(1u, 0u)] uint Rn, [ValueSource("_1B1H1S1D_")] [Random(RndCnt)] ulong Z, [ValueSource("_1B1H1S1D_")] [Random(RndCnt)] ulong A, [Values(0b00u, 0b01u, 0b10u, 0b11u)] uint size) // { + const int QCFlagBit = 27; // Cumulative saturation bit. + uint Opcode = 0x7E203800; // USQADD B0, B0 Opcode |= ((Rn & 31) << 5) | ((Rd & 31) << 0); Opcode |= ((size & 3) << 22); Bits Op = new Bits(Opcode); - int Fpsr = (int)TestContext.CurrentContext.Random.NextUInt(); - Vector128 V0 = MakeVectorE0E1(Z, Z); Vector128 V1 = MakeVectorE0(A); - AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, Fpsr: Fpsr); + 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)); - Shared.FPSR = new Bits((uint)Fpsr); SimdFp.Usqadd_S(Op[23, 22], Op[9, 5], Op[4, 0]); Assert.Multiple(() => @@ -2036,31 +2322,30 @@ namespace Ryujinx.Tests.Cpu 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())); }); - Assert.That(ThreadState.Fpsr, Is.EqualTo((int)Shared.FPSR.ToUInt32())); + Assert.That(((ThreadState.Fpsr >> QCFlagBit) & 1) != 0, Is.EqualTo(Shared.FPSR[QCFlagBit])); CompareAgainstUnicorn(); } - [Test, Description("USQADD ., .")] + [Test, Pairwise, Description("USQADD ., .")] public void Usqadd_V_8B_4H_2S([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) // <8B, 4H, 2S> { + const int QCFlagBit = 27; // Cumulative saturation bit. + uint Opcode = 0x2E203800; // USQADD V0.8B, V0.8B Opcode |= ((Rn & 31) << 5) | ((Rd & 31) << 0); Opcode |= ((size & 3) << 22); Bits Op = new Bits(Opcode); - int Fpsr = (int)TestContext.CurrentContext.Random.NextUInt(); - Vector128 V0 = MakeVectorE0E1(Z, Z); Vector128 V1 = MakeVectorE0(A); - AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, Fpsr: Fpsr); + 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)); - Shared.FPSR = new Bits((uint)Fpsr); SimdFp.Usqadd_V(Op[30], Op[23, 22], Op[9, 5], Op[4, 0]); Assert.Multiple(() => @@ -2068,31 +2353,30 @@ namespace Ryujinx.Tests.Cpu 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())); }); - Assert.That(ThreadState.Fpsr, Is.EqualTo((int)Shared.FPSR.ToUInt32())); + Assert.That(((ThreadState.Fpsr >> QCFlagBit) & 1) != 0, Is.EqualTo(Shared.FPSR[QCFlagBit])); CompareAgainstUnicorn(); } - [Test, Description("USQADD ., .")] + [Test, Pairwise, Description("USQADD ., .")] public void Usqadd_V_16B_8H_4S_2D([Values(0u)] uint Rd, [Values(1u, 0u)] uint Rn, [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong Z, [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong A, [Values(0b00u, 0b01u, 0b10u, 0b11u)] uint size) // <16B, 8H, 4S, 2D> { + const int QCFlagBit = 27; // Cumulative saturation bit. + uint Opcode = 0x6E203800; // USQADD V0.16B, V0.16B Opcode |= ((Rn & 31) << 5) | ((Rd & 31) << 0); Opcode |= ((size & 3) << 22); Bits Op = new Bits(Opcode); - int Fpsr = (int)TestContext.CurrentContext.Random.NextUInt(); - Vector128 V0 = MakeVectorE0E1(Z, Z); Vector128 V1 = MakeVectorE0E1(A, A); - AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, Fpsr: Fpsr); + 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)); - Shared.FPSR = new Bits((uint)Fpsr); SimdFp.Usqadd_V(Op[30], Op[23, 22], Op[9, 5], Op[4, 0]); Assert.Multiple(() => @@ -2100,11 +2384,11 @@ namespace Ryujinx.Tests.Cpu 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())); }); - Assert.That(ThreadState.Fpsr, Is.EqualTo((int)Shared.FPSR.ToUInt32())); + Assert.That(((ThreadState.Fpsr >> QCFlagBit) & 1) != 0, Is.EqualTo(Shared.FPSR[QCFlagBit])); CompareAgainstUnicorn(); } - [Test, Description("XTN{2} ., .")] + [Test, Pairwise, Description("XTN{2} ., .")] public void Xtn_V_8H8B_4S4H_2D2S([Values(0u)] uint Rd, [Values(1u, 0u)] uint Rn, [ValueSource("_4H2S1D_")] [Random(RndCnt)] ulong Z, @@ -2132,7 +2416,7 @@ namespace Ryujinx.Tests.Cpu CompareAgainstUnicorn(); } - [Test, Description("XTN{2} ., .")] + [Test, Pairwise, Description("XTN{2} ., .")] public void Xtn_V_8H16B_4S8H_2D4S([Values(0u)] uint Rd, [Values(1u, 0u)] uint Rn, [ValueSource("_4H2S1D_")] [Random(RndCnt)] ulong Z, diff --git a/Ryujinx.Tests/Cpu/CpuTestSimdReg.cs b/Ryujinx.Tests/Cpu/CpuTestSimdReg.cs index 782e5921e..b7633e8be 100644 --- a/Ryujinx.Tests/Cpu/CpuTestSimdReg.cs +++ b/Ryujinx.Tests/Cpu/CpuTestSimdReg.cs @@ -86,7 +86,7 @@ namespace Ryujinx.Tests.Cpu } #endregion - private const int RndCnt = 4; + private const int RndCnt = 2; [Test, Pairwise, Description("ADD , , ")] public void Add_S_D([Values(0u)] uint Rd, @@ -1738,6 +1738,68 @@ namespace Ryujinx.Tests.Cpu CompareAgainstUnicorn(); } + [Test, Pairwise, Description("SADDL{2} ., ., .")] + public void Saddl_V_8B8H_4H4S_2S2D([Values(0u)] uint Rd, + [Values(1u, 0u)] uint Rn, + [Values(2u, 0u)] uint Rm, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong Z, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong A, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong B, + [Values(0b00u, 0b01u, 0b10u)] uint size) // <8B8H, 4H4S, 2S2D> + { + uint Opcode = 0x0E200000; // SADDL V0.8H, V0.8B, V0.8B + Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); + Opcode |= ((size & 3) << 22); + Bits Op = new Bits(Opcode); + + Vector128 V0 = MakeVectorE0E1(Z, Z); + Vector128 V1 = MakeVectorE0(A); + Vector128 V2 = MakeVectorE0(B); + AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2); + + AArch64.Vpart(0, 0, new Bits(Z)); AArch64.Vpart(0, 1, new Bits(Z)); + AArch64.Vpart(1, 0, new Bits(A)); + AArch64.Vpart(2, 0, new Bits(B)); + SimdFp.Saddl_V(Op[30], Op[23, 22], Op[20, 16], 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, Pairwise, Description("SADDL{2} ., ., .")] + public void Saddl_V_16B8H_8H4S_4S2D([Values(0u)] uint Rd, + [Values(1u, 0u)] uint Rn, + [Values(2u, 0u)] uint Rm, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong Z, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong A, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong B, + [Values(0b00u, 0b01u, 0b10u)] uint size) // <16B8H, 8H4S, 4S2D> + { + uint Opcode = 0x4E200000; // SADDL2 V0.8H, V0.16B, V0.16B + Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); + Opcode |= ((size & 3) << 22); + Bits Op = new Bits(Opcode); + + Vector128 V0 = MakeVectorE0E1(Z, Z); + Vector128 V1 = MakeVectorE1(A); + Vector128 V2 = MakeVectorE1(B); + AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2); + + AArch64.Vpart(0, 0, new Bits(Z)); AArch64.Vpart(0, 1, new Bits(Z)); + AArch64.Vpart(1, 1, new Bits(A)); + AArch64.Vpart(2, 1, new Bits(B)); + SimdFp.Saddl_V(Op[30], Op[23, 22], Op[20, 16], 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, Pairwise, Description("SADDW{2} ., ., .")] public void Saddw_V_8B8H8H_4H4S4S_2S2D2D([Values(0u)] uint Rd, [Values(1u, 0u)] uint Rn, @@ -2040,6 +2102,130 @@ namespace Ryujinx.Tests.Cpu }); } + [Test, Pairwise, Description("SMLAL{2} ., ., .")] + public void Smlal_V_8B8H_4H4S_2S2D([Values(0u)] uint Rd, + [Values(1u, 0u)] uint Rn, + [Values(2u, 0u)] uint Rm, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong Z, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong A, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong B, + [Values(0b00u, 0b01u, 0b10u)] uint size) // <8B8H, 4H4S, 2S2D> + { + uint Opcode = 0x0E208000; // SMLAL V0.8H, V0.8B, V0.8B + Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); + Opcode |= ((size & 3) << 22); + Bits Op = new Bits(Opcode); + + Vector128 V0 = MakeVectorE0E1(Z, Z); + Vector128 V1 = MakeVectorE0(A); + Vector128 V2 = MakeVectorE0(B); + AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2); + + AArch64.Vpart(0, 0, new Bits(Z)); AArch64.Vpart(0, 1, new Bits(Z)); + AArch64.Vpart(1, 0, new Bits(A)); + AArch64.Vpart(2, 0, new Bits(B)); + SimdFp.Smlal_V(Op[30], Op[23, 22], Op[20, 16], 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, Pairwise, Description("SMLAL{2} ., ., .")] + public void Smlal_V_16B8H_8H4S_4S2D([Values(0u)] uint Rd, + [Values(1u, 0u)] uint Rn, + [Values(2u, 0u)] uint Rm, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong Z, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong A, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong B, + [Values(0b00u, 0b01u, 0b10u)] uint size) // <16B8H, 8H4S, 4S2D> + { + uint Opcode = 0x4E208000; // SMLAL2 V0.8H, V0.16B, V0.16B + Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); + Opcode |= ((size & 3) << 22); + Bits Op = new Bits(Opcode); + + Vector128 V0 = MakeVectorE0E1(Z, Z); + Vector128 V1 = MakeVectorE1(A); + Vector128 V2 = MakeVectorE1(B); + AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2); + + AArch64.Vpart(0, 0, new Bits(Z)); AArch64.Vpart(0, 1, new Bits(Z)); + AArch64.Vpart(1, 1, new Bits(A)); + AArch64.Vpart(2, 1, new Bits(B)); + SimdFp.Smlal_V(Op[30], Op[23, 22], Op[20, 16], 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, Pairwise, Description("SMLSL{2} ., ., .")] + public void Smlsl_V_8B8H_4H4S_2S2D([Values(0u)] uint Rd, + [Values(1u, 0u)] uint Rn, + [Values(2u, 0u)] uint Rm, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong Z, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong A, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong B, + [Values(0b00u, 0b01u, 0b10u)] uint size) // <8B8H, 4H4S, 2S2D> + { + uint Opcode = 0x0E20A000; // SMLSL V0.8H, V0.8B, V0.8B + Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); + Opcode |= ((size & 3) << 22); + Bits Op = new Bits(Opcode); + + Vector128 V0 = MakeVectorE0E1(Z, Z); + Vector128 V1 = MakeVectorE0(A); + Vector128 V2 = MakeVectorE0(B); + AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2); + + AArch64.Vpart(0, 0, new Bits(Z)); AArch64.Vpart(0, 1, new Bits(Z)); + AArch64.Vpart(1, 0, new Bits(A)); + AArch64.Vpart(2, 0, new Bits(B)); + SimdFp.Smlsl_V(Op[30], Op[23, 22], Op[20, 16], 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, Pairwise, Description("SMLSL{2} ., ., .")] + public void Smlsl_V_16B8H_8H4S_4S2D([Values(0u)] uint Rd, + [Values(1u, 0u)] uint Rn, + [Values(2u, 0u)] uint Rm, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong Z, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong A, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong B, + [Values(0b00u, 0b01u, 0b10u)] uint size) // <16B8H, 8H4S, 4S2D> + { + uint Opcode = 0x4E20A000; // SMLSL2 V0.8H, V0.16B, V0.16B + Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); + Opcode |= ((size & 3) << 22); + Bits Op = new Bits(Opcode); + + Vector128 V0 = MakeVectorE0E1(Z, Z); + Vector128 V1 = MakeVectorE1(A); + Vector128 V2 = MakeVectorE1(B); + AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2); + + AArch64.Vpart(0, 0, new Bits(Z)); AArch64.Vpart(0, 1, new Bits(Z)); + AArch64.Vpart(1, 1, new Bits(A)); + AArch64.Vpart(2, 1, new Bits(B)); + SimdFp.Smlsl_V(Op[30], Op[23, 22], Op[20, 16], 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, Pairwise, Description("SQADD , , ")] public void Sqadd_S_B_H_S_D([Values(0u)] uint Rd, [Values(1u, 0u)] uint Rn, @@ -2049,22 +2235,21 @@ namespace Ryujinx.Tests.Cpu [ValueSource("_1B1H1S1D_")] [Random(RndCnt)] ulong B, [Values(0b00u, 0b01u, 0b10u, 0b11u)] uint size) // { + const int QCFlagBit = 27; // Cumulative saturation bit. + uint Opcode = 0x5E200C00; // SQADD B0, B0, B0 Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); Opcode |= ((size & 3) << 22); Bits Op = new Bits(Opcode); - int Fpsr = (int)TestContext.CurrentContext.Random.NextUInt(); - Vector128 V0 = MakeVectorE0E1(Z, Z); Vector128 V1 = MakeVectorE0(A); Vector128 V2 = MakeVectorE0(B); - AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2, Fpsr: Fpsr); + AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2); AArch64.Vpart(0, 0, new Bits(Z)); AArch64.Vpart(0, 1, new Bits(Z)); AArch64.V(1, new Bits(A)); AArch64.V(2, new Bits(B)); - Shared.FPSR = new Bits((uint)Fpsr); SimdFp.Sqadd_S(Op[23, 22], Op[20, 16], Op[9, 5], Op[4, 0]); Assert.Multiple(() => @@ -2072,7 +2257,8 @@ namespace Ryujinx.Tests.Cpu 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())); }); - Assert.That(ThreadState.Fpsr, Is.EqualTo((int)Shared.FPSR.ToUInt32())); + + Assert.That(((ThreadState.Fpsr >> QCFlagBit) & 1) != 0, Is.EqualTo(Shared.FPSR[QCFlagBit])); } [Test, Pairwise, Description("SQADD ., ., .")] @@ -2084,22 +2270,21 @@ namespace Ryujinx.Tests.Cpu [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong B, [Values(0b00u, 0b01u, 0b10u)] uint size) // <8B, 4H, 2S> { + const int QCFlagBit = 27; // Cumulative saturation bit. + uint Opcode = 0x0E200C00; // SQADD V0.8B, V0.8B, V0.8B Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); Opcode |= ((size & 3) << 22); Bits Op = new Bits(Opcode); - int Fpsr = (int)TestContext.CurrentContext.Random.NextUInt(); - Vector128 V0 = MakeVectorE0E1(Z, Z); Vector128 V1 = MakeVectorE0(A); Vector128 V2 = MakeVectorE0(B); - AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2, Fpsr: Fpsr); + AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2); AArch64.Vpart(0, 0, new Bits(Z)); AArch64.Vpart(0, 1, new Bits(Z)); AArch64.V(1, new Bits(A)); AArch64.V(2, new Bits(B)); - Shared.FPSR = new Bits((uint)Fpsr); SimdFp.Sqadd_V(Op[30], Op[23, 22], Op[20, 16], Op[9, 5], Op[4, 0]); Assert.Multiple(() => @@ -2107,7 +2292,8 @@ namespace Ryujinx.Tests.Cpu 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())); }); - Assert.That(ThreadState.Fpsr, Is.EqualTo((int)Shared.FPSR.ToUInt32())); + + Assert.That(((ThreadState.Fpsr >> QCFlagBit) & 1) != 0, Is.EqualTo(Shared.FPSR[QCFlagBit])); } [Test, Pairwise, Description("SQADD ., ., .")] @@ -2119,22 +2305,21 @@ namespace Ryujinx.Tests.Cpu [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong B, [Values(0b00u, 0b01u, 0b10u, 0b11u)] uint size) // <16B, 8H, 4S, 2D> { + const int QCFlagBit = 27; // Cumulative saturation bit. + uint Opcode = 0x4E200C00; // SQADD V0.16B, V0.16B, V0.16B Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); Opcode |= ((size & 3) << 22); Bits Op = new Bits(Opcode); - int Fpsr = (int)TestContext.CurrentContext.Random.NextUInt(); - Vector128 V0 = MakeVectorE0E1(Z, Z); Vector128 V1 = MakeVectorE0E1(A, A); Vector128 V2 = MakeVectorE0E1(B, B); - AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2, Fpsr: Fpsr); + AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2); 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)); AArch64.Vpart(2, 0, new Bits(B)); AArch64.Vpart(2, 1, new Bits(B)); - Shared.FPSR = new Bits((uint)Fpsr); SimdFp.Sqadd_V(Op[30], Op[23, 22], Op[20, 16], Op[9, 5], Op[4, 0]); Assert.Multiple(() => @@ -2142,7 +2327,8 @@ namespace Ryujinx.Tests.Cpu 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())); }); - Assert.That(ThreadState.Fpsr, Is.EqualTo((int)Shared.FPSR.ToUInt32())); + + Assert.That(((ThreadState.Fpsr >> QCFlagBit) & 1) != 0, Is.EqualTo(Shared.FPSR[QCFlagBit])); } [Test, Pairwise, Description("SQDMULH , , ")] @@ -2154,22 +2340,21 @@ namespace Ryujinx.Tests.Cpu [ValueSource("_1H1S_")] [Random(RndCnt)] ulong B, [Values(0b01u, 0b10u)] uint size) // { + const int QCFlagBit = 27; // Cumulative saturation bit. + uint Opcode = 0x5E20B400; // SQDMULH B0, B0, B0 (RESERVED) Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); Opcode |= ((size & 3) << 22); Bits Op = new Bits(Opcode); - int Fpsr = (int)TestContext.CurrentContext.Random.NextUInt(); - Vector128 V0 = MakeVectorE0E1(Z, Z); Vector128 V1 = MakeVectorE0(A); Vector128 V2 = MakeVectorE0(B); - AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2, Fpsr: Fpsr); + AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2); AArch64.Vpart(0, 0, new Bits(Z)); AArch64.Vpart(0, 1, new Bits(Z)); AArch64.V(1, new Bits(A)); AArch64.V(2, new Bits(B)); - Shared.FPSR = new Bits((uint)Fpsr); SimdFp.Sqdmulh_S(Op[23, 22], Op[20, 16], Op[9, 5], Op[4, 0]); Assert.Multiple(() => @@ -2177,7 +2362,8 @@ namespace Ryujinx.Tests.Cpu 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())); }); - Assert.That(ThreadState.Fpsr, Is.EqualTo((int)Shared.FPSR.ToUInt32())); + + Assert.That(((ThreadState.Fpsr >> QCFlagBit) & 1) != 0, Is.EqualTo(Shared.FPSR[QCFlagBit])); } [Test, Pairwise, Description("SQDMULH ., ., .")] @@ -2189,22 +2375,21 @@ namespace Ryujinx.Tests.Cpu [ValueSource("_4H2S_")] [Random(RndCnt)] ulong B, [Values(0b01u, 0b10u)] uint size) // <4H, 2S> { + const int QCFlagBit = 27; // Cumulative saturation bit. + uint Opcode = 0x0E20B400; // SQDMULH V0.8B, V0.8B, V0.8B (RESERVED) Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); Opcode |= ((size & 3) << 22); Bits Op = new Bits(Opcode); - int Fpsr = (int)TestContext.CurrentContext.Random.NextUInt(); - Vector128 V0 = MakeVectorE0E1(Z, Z); Vector128 V1 = MakeVectorE0(A); Vector128 V2 = MakeVectorE0(B); - AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2, Fpsr: Fpsr); + AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2); AArch64.Vpart(0, 0, new Bits(Z)); AArch64.Vpart(0, 1, new Bits(Z)); AArch64.V(1, new Bits(A)); AArch64.V(2, new Bits(B)); - Shared.FPSR = new Bits((uint)Fpsr); SimdFp.Sqdmulh_V(Op[30], Op[23, 22], Op[20, 16], Op[9, 5], Op[4, 0]); Assert.Multiple(() => @@ -2212,7 +2397,8 @@ namespace Ryujinx.Tests.Cpu 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())); }); - Assert.That(ThreadState.Fpsr, Is.EqualTo((int)Shared.FPSR.ToUInt32())); + + Assert.That(((ThreadState.Fpsr >> QCFlagBit) & 1) != 0, Is.EqualTo(Shared.FPSR[QCFlagBit])); } [Test, Pairwise, Description("SQDMULH ., ., .")] @@ -2224,22 +2410,21 @@ namespace Ryujinx.Tests.Cpu [ValueSource("_4H2S_")] [Random(RndCnt)] ulong B, [Values(0b01u, 0b10u)] uint size) // <8H, 4S> { + const int QCFlagBit = 27; // Cumulative saturation bit. + uint Opcode = 0x4E20B400; // SQDMULH V0.16B, V0.16B, V0.16B (RESERVED) Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); Opcode |= ((size & 3) << 22); Bits Op = new Bits(Opcode); - int Fpsr = (int)TestContext.CurrentContext.Random.NextUInt(); - Vector128 V0 = MakeVectorE0E1(Z, Z); Vector128 V1 = MakeVectorE0E1(A, A); Vector128 V2 = MakeVectorE0E1(B, B); - AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2, Fpsr: Fpsr); + AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2); 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)); AArch64.Vpart(2, 0, new Bits(B)); AArch64.Vpart(2, 1, new Bits(B)); - Shared.FPSR = new Bits((uint)Fpsr); SimdFp.Sqdmulh_V(Op[30], Op[23, 22], Op[20, 16], Op[9, 5], Op[4, 0]); Assert.Multiple(() => @@ -2247,7 +2432,8 @@ namespace Ryujinx.Tests.Cpu 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())); }); - Assert.That(ThreadState.Fpsr, Is.EqualTo((int)Shared.FPSR.ToUInt32())); + + Assert.That(((ThreadState.Fpsr >> QCFlagBit) & 1) != 0, Is.EqualTo(Shared.FPSR[QCFlagBit])); } [Test, Pairwise, Description("SQRDMULH , , ")] @@ -2259,22 +2445,21 @@ namespace Ryujinx.Tests.Cpu [ValueSource("_1H1S_")] [Random(RndCnt)] ulong B, [Values(0b01u, 0b10u)] uint size) // { + const int QCFlagBit = 27; // Cumulative saturation bit. + uint Opcode = 0x7E20B400; // SQRDMULH B0, B0, B0 (RESERVED) Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); Opcode |= ((size & 3) << 22); Bits Op = new Bits(Opcode); - int Fpsr = (int)TestContext.CurrentContext.Random.NextUInt(); - Vector128 V0 = MakeVectorE0E1(Z, Z); Vector128 V1 = MakeVectorE0(A); Vector128 V2 = MakeVectorE0(B); - AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2, Fpsr: Fpsr); + AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2); AArch64.Vpart(0, 0, new Bits(Z)); AArch64.Vpart(0, 1, new Bits(Z)); AArch64.V(1, new Bits(A)); AArch64.V(2, new Bits(B)); - Shared.FPSR = new Bits((uint)Fpsr); SimdFp.Sqrdmulh_S(Op[23, 22], Op[20, 16], Op[9, 5], Op[4, 0]); Assert.Multiple(() => @@ -2282,7 +2467,8 @@ namespace Ryujinx.Tests.Cpu 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())); }); - Assert.That(ThreadState.Fpsr, Is.EqualTo((int)Shared.FPSR.ToUInt32())); + + Assert.That(((ThreadState.Fpsr >> QCFlagBit) & 1) != 0, Is.EqualTo(Shared.FPSR[QCFlagBit])); } [Test, Pairwise, Description("SQRDMULH ., ., .")] @@ -2294,22 +2480,21 @@ namespace Ryujinx.Tests.Cpu [ValueSource("_4H2S_")] [Random(RndCnt)] ulong B, [Values(0b01u, 0b10u)] uint size) // <4H, 2S> { + const int QCFlagBit = 27; // Cumulative saturation bit. + uint Opcode = 0x2E20B400; // SQRDMULH V0.8B, V0.8B, V0.8B (RESERVED) Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); Opcode |= ((size & 3) << 22); Bits Op = new Bits(Opcode); - int Fpsr = (int)TestContext.CurrentContext.Random.NextUInt(); - Vector128 V0 = MakeVectorE0E1(Z, Z); Vector128 V1 = MakeVectorE0(A); Vector128 V2 = MakeVectorE0(B); - AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2, Fpsr: Fpsr); + AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2); AArch64.Vpart(0, 0, new Bits(Z)); AArch64.Vpart(0, 1, new Bits(Z)); AArch64.V(1, new Bits(A)); AArch64.V(2, new Bits(B)); - Shared.FPSR = new Bits((uint)Fpsr); SimdFp.Sqrdmulh_V(Op[30], Op[23, 22], Op[20, 16], Op[9, 5], Op[4, 0]); Assert.Multiple(() => @@ -2317,7 +2502,8 @@ namespace Ryujinx.Tests.Cpu 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())); }); - Assert.That(ThreadState.Fpsr, Is.EqualTo((int)Shared.FPSR.ToUInt32())); + + Assert.That(((ThreadState.Fpsr >> QCFlagBit) & 1) != 0, Is.EqualTo(Shared.FPSR[QCFlagBit])); } [Test, Pairwise, Description("SQRDMULH ., ., .")] @@ -2329,22 +2515,21 @@ namespace Ryujinx.Tests.Cpu [ValueSource("_4H2S_")] [Random(RndCnt)] ulong B, [Values(0b01u, 0b10u)] uint size) // <8H, 4S> { + const int QCFlagBit = 27; // Cumulative saturation bit. + uint Opcode = 0x6E20B400; // SQRDMULH V0.16B, V0.16B, V0.16B (RESERVED) Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); Opcode |= ((size & 3) << 22); Bits Op = new Bits(Opcode); - int Fpsr = (int)TestContext.CurrentContext.Random.NextUInt(); - Vector128 V0 = MakeVectorE0E1(Z, Z); Vector128 V1 = MakeVectorE0E1(A, A); Vector128 V2 = MakeVectorE0E1(B, B); - AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2, Fpsr: Fpsr); + AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2); 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)); AArch64.Vpart(2, 0, new Bits(B)); AArch64.Vpart(2, 1, new Bits(B)); - Shared.FPSR = new Bits((uint)Fpsr); SimdFp.Sqrdmulh_V(Op[30], Op[23, 22], Op[20, 16], Op[9, 5], Op[4, 0]); Assert.Multiple(() => @@ -2352,7 +2537,8 @@ namespace Ryujinx.Tests.Cpu 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())); }); - Assert.That(ThreadState.Fpsr, Is.EqualTo((int)Shared.FPSR.ToUInt32())); + + Assert.That(((ThreadState.Fpsr >> QCFlagBit) & 1) != 0, Is.EqualTo(Shared.FPSR[QCFlagBit])); } [Test, Pairwise, Description("SQSUB , , ")] @@ -2364,22 +2550,21 @@ namespace Ryujinx.Tests.Cpu [ValueSource("_1B1H1S1D_")] [Random(RndCnt)] ulong B, [Values(0b00u, 0b01u, 0b10u, 0b11u)] uint size) // { + const int QCFlagBit = 27; // Cumulative saturation bit. + uint Opcode = 0x5E202C00; // SQSUB B0, B0, B0 Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); Opcode |= ((size & 3) << 22); Bits Op = new Bits(Opcode); - int Fpsr = (int)TestContext.CurrentContext.Random.NextUInt(); - Vector128 V0 = MakeVectorE0E1(Z, Z); Vector128 V1 = MakeVectorE0(A); Vector128 V2 = MakeVectorE0(B); - AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2, Fpsr: Fpsr); + AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2); AArch64.Vpart(0, 0, new Bits(Z)); AArch64.Vpart(0, 1, new Bits(Z)); AArch64.V(1, new Bits(A)); AArch64.V(2, new Bits(B)); - Shared.FPSR = new Bits((uint)Fpsr); SimdFp.Sqsub_S(Op[23, 22], Op[20, 16], Op[9, 5], Op[4, 0]); Assert.Multiple(() => @@ -2387,7 +2572,8 @@ namespace Ryujinx.Tests.Cpu 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())); }); - Assert.That(ThreadState.Fpsr, Is.EqualTo((int)Shared.FPSR.ToUInt32())); + + Assert.That(((ThreadState.Fpsr >> QCFlagBit) & 1) != 0, Is.EqualTo(Shared.FPSR[QCFlagBit])); } [Test, Pairwise, Description("SQSUB ., ., .")] @@ -2399,22 +2585,21 @@ namespace Ryujinx.Tests.Cpu [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong B, [Values(0b00u, 0b01u, 0b10u)] uint size) // <8B, 4H, 2S> { + const int QCFlagBit = 27; // Cumulative saturation bit. + uint Opcode = 0x0E202C00; // SQSUB V0.8B, V0.8B, V0.8B Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); Opcode |= ((size & 3) << 22); Bits Op = new Bits(Opcode); - int Fpsr = (int)TestContext.CurrentContext.Random.NextUInt(); - Vector128 V0 = MakeVectorE0E1(Z, Z); Vector128 V1 = MakeVectorE0(A); Vector128 V2 = MakeVectorE0(B); - AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2, Fpsr: Fpsr); + AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2); AArch64.Vpart(0, 0, new Bits(Z)); AArch64.Vpart(0, 1, new Bits(Z)); AArch64.V(1, new Bits(A)); AArch64.V(2, new Bits(B)); - Shared.FPSR = new Bits((uint)Fpsr); SimdFp.Sqsub_V(Op[30], Op[23, 22], Op[20, 16], Op[9, 5], Op[4, 0]); Assert.Multiple(() => @@ -2422,7 +2607,8 @@ namespace Ryujinx.Tests.Cpu 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())); }); - Assert.That(ThreadState.Fpsr, Is.EqualTo((int)Shared.FPSR.ToUInt32())); + + Assert.That(((ThreadState.Fpsr >> QCFlagBit) & 1) != 0, Is.EqualTo(Shared.FPSR[QCFlagBit])); } [Test, Pairwise, Description("SQSUB ., ., .")] @@ -2434,22 +2620,21 @@ namespace Ryujinx.Tests.Cpu [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong B, [Values(0b00u, 0b01u, 0b10u, 0b11u)] uint size) // <16B, 8H, 4S, 2D> { + const int QCFlagBit = 27; // Cumulative saturation bit. + uint Opcode = 0x4E202C00; // SQSUB V0.16B, V0.16B, V0.16B Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); Opcode |= ((size & 3) << 22); Bits Op = new Bits(Opcode); - int Fpsr = (int)TestContext.CurrentContext.Random.NextUInt(); - Vector128 V0 = MakeVectorE0E1(Z, Z); Vector128 V1 = MakeVectorE0E1(A, A); Vector128 V2 = MakeVectorE0E1(B, B); - AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2, Fpsr: Fpsr); + AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2); 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)); AArch64.Vpart(2, 0, new Bits(B)); AArch64.Vpart(2, 1, new Bits(B)); - Shared.FPSR = new Bits((uint)Fpsr); SimdFp.Sqsub_V(Op[30], Op[23, 22], Op[20, 16], Op[9, 5], Op[4, 0]); Assert.Multiple(() => @@ -2457,7 +2642,8 @@ namespace Ryujinx.Tests.Cpu 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())); }); - Assert.That(ThreadState.Fpsr, Is.EqualTo((int)Shared.FPSR.ToUInt32())); + + Assert.That(((ThreadState.Fpsr >> QCFlagBit) & 1) != 0, Is.EqualTo(Shared.FPSR[QCFlagBit])); } [Test, Pairwise, Description("SRHADD ., ., .")] @@ -2522,6 +2708,68 @@ namespace Ryujinx.Tests.Cpu }); } + [Test, Pairwise, Description("SSUBL{2} ., ., .")] + public void Ssubl_V_8B8H_4H4S_2S2D([Values(0u)] uint Rd, + [Values(1u, 0u)] uint Rn, + [Values(2u, 0u)] uint Rm, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong Z, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong A, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong B, + [Values(0b00u, 0b01u, 0b10u)] uint size) // <8B8H, 4H4S, 2S2D> + { + uint Opcode = 0x0E202000; // SSUBL V0.8H, V0.8B, V0.8B + Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); + Opcode |= ((size & 3) << 22); + Bits Op = new Bits(Opcode); + + Vector128 V0 = MakeVectorE0E1(Z, Z); + Vector128 V1 = MakeVectorE0(A); + Vector128 V2 = MakeVectorE0(B); + AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2); + + AArch64.Vpart(0, 0, new Bits(Z)); AArch64.Vpart(0, 1, new Bits(Z)); + AArch64.Vpart(1, 0, new Bits(A)); + AArch64.Vpart(2, 0, new Bits(B)); + SimdFp.Ssubl_V(Op[30], Op[23, 22], Op[20, 16], 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, Pairwise, Description("SSUBL{2} ., ., .")] + public void Ssubl_V_16B8H_8H4S_4S2D([Values(0u)] uint Rd, + [Values(1u, 0u)] uint Rn, + [Values(2u, 0u)] uint Rm, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong Z, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong A, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong B, + [Values(0b00u, 0b01u, 0b10u)] uint size) // <16B8H, 8H4S, 4S2D> + { + uint Opcode = 0x4E202000; // SSUBL2 V0.8H, V0.16B, V0.16B + Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); + Opcode |= ((size & 3) << 22); + Bits Op = new Bits(Opcode); + + Vector128 V0 = MakeVectorE0E1(Z, Z); + Vector128 V1 = MakeVectorE1(A); + Vector128 V2 = MakeVectorE1(B); + AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2); + + AArch64.Vpart(0, 0, new Bits(Z)); AArch64.Vpart(0, 1, new Bits(Z)); + AArch64.Vpart(1, 1, new Bits(A)); + AArch64.Vpart(2, 1, new Bits(B)); + SimdFp.Ssubl_V(Op[30], Op[23, 22], Op[20, 16], 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, Pairwise, Description("SSUBW{2} ., ., .")] public void Ssubw_V_8B8H8H_4H4S4S_2S2D2D([Values(0u)] uint Rd, [Values(1u, 0u)] uint Rn, @@ -3128,6 +3376,68 @@ namespace Ryujinx.Tests.Cpu CompareAgainstUnicorn(); } + [Test, Pairwise, Description("UADDL{2} ., ., .")] + public void Uaddl_V_8B8H_4H4S_2S2D([Values(0u)] uint Rd, + [Values(1u, 0u)] uint Rn, + [Values(2u, 0u)] uint Rm, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong Z, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong A, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong B, + [Values(0b00u, 0b01u, 0b10u)] uint size) // <8B8H, 4H4S, 2S2D> + { + uint Opcode = 0x2E200000; // UADDL V0.8H, V0.8B, V0.8B + Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); + Opcode |= ((size & 3) << 22); + Bits Op = new Bits(Opcode); + + Vector128 V0 = MakeVectorE0E1(Z, Z); + Vector128 V1 = MakeVectorE0(A); + Vector128 V2 = MakeVectorE0(B); + AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2); + + AArch64.Vpart(0, 0, new Bits(Z)); AArch64.Vpart(0, 1, new Bits(Z)); + AArch64.Vpart(1, 0, new Bits(A)); + AArch64.Vpart(2, 0, new Bits(B)); + SimdFp.Uaddl_V(Op[30], Op[23, 22], Op[20, 16], 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, Pairwise, Description("UADDL{2} ., ., .")] + public void Uaddl_V_16B8H_8H4S_4S2D([Values(0u)] uint Rd, + [Values(1u, 0u)] uint Rn, + [Values(2u, 0u)] uint Rm, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong Z, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong A, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong B, + [Values(0b00u, 0b01u, 0b10u)] uint size) // <16B8H, 8H4S, 4S2D> + { + uint Opcode = 0x6E200000; // UADDL2 V0.8H, V0.16B, V0.16B + Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); + Opcode |= ((size & 3) << 22); + Bits Op = new Bits(Opcode); + + Vector128 V0 = MakeVectorE0E1(Z, Z); + Vector128 V1 = MakeVectorE1(A); + Vector128 V2 = MakeVectorE1(B); + AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2); + + AArch64.Vpart(0, 0, new Bits(Z)); AArch64.Vpart(0, 1, new Bits(Z)); + AArch64.Vpart(1, 1, new Bits(A)); + AArch64.Vpart(2, 1, new Bits(B)); + SimdFp.Uaddl_V(Op[30], Op[23, 22], Op[20, 16], 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, Pairwise, Description("UADDW{2} ., ., .")] public void Uaddw_V_8B8H8H_4H4S4S_2S2D2D([Values(0u)] uint Rd, [Values(1u, 0u)] uint Rn, @@ -3316,6 +3626,130 @@ namespace Ryujinx.Tests.Cpu }); } + [Test, Pairwise, Description("UMLAL{2} ., ., .")] + public void Umlal_V_8B8H_4H4S_2S2D([Values(0u)] uint Rd, + [Values(1u, 0u)] uint Rn, + [Values(2u, 0u)] uint Rm, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong Z, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong A, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong B, + [Values(0b00u, 0b01u, 0b10u)] uint size) // <8B8H, 4H4S, 2S2D> + { + uint Opcode = 0x2E208000; // UMLAL V0.8H, V0.8B, V0.8B + Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); + Opcode |= ((size & 3) << 22); + Bits Op = new Bits(Opcode); + + Vector128 V0 = MakeVectorE0E1(Z, Z); + Vector128 V1 = MakeVectorE0(A); + Vector128 V2 = MakeVectorE0(B); + AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2); + + AArch64.Vpart(0, 0, new Bits(Z)); AArch64.Vpart(0, 1, new Bits(Z)); + AArch64.Vpart(1, 0, new Bits(A)); + AArch64.Vpart(2, 0, new Bits(B)); + SimdFp.Umlal_V(Op[30], Op[23, 22], Op[20, 16], 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, Pairwise, Description("UMLAL{2} ., ., .")] + public void Umlal_V_16B8H_8H4S_4S2D([Values(0u)] uint Rd, + [Values(1u, 0u)] uint Rn, + [Values(2u, 0u)] uint Rm, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong Z, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong A, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong B, + [Values(0b00u, 0b01u, 0b10u)] uint size) // <16B8H, 8H4S, 4S2D> + { + uint Opcode = 0x6E208000; // UMLAL2 V0.8H, V0.16B, V0.16B + Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); + Opcode |= ((size & 3) << 22); + Bits Op = new Bits(Opcode); + + Vector128 V0 = MakeVectorE0E1(Z, Z); + Vector128 V1 = MakeVectorE1(A); + Vector128 V2 = MakeVectorE1(B); + AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2); + + AArch64.Vpart(0, 0, new Bits(Z)); AArch64.Vpart(0, 1, new Bits(Z)); + AArch64.Vpart(1, 1, new Bits(A)); + AArch64.Vpart(2, 1, new Bits(B)); + SimdFp.Umlal_V(Op[30], Op[23, 22], Op[20, 16], 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, Pairwise, Description("UMLSL{2} ., ., .")] + public void Umlsl_V_8B8H_4H4S_2S2D([Values(0u)] uint Rd, + [Values(1u, 0u)] uint Rn, + [Values(2u, 0u)] uint Rm, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong Z, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong A, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong B, + [Values(0b00u, 0b01u, 0b10u)] uint size) // <8B8H, 4H4S, 2S2D> + { + uint Opcode = 0x2E20A000; // UMLSL V0.8H, V0.8B, V0.8B + Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); + Opcode |= ((size & 3) << 22); + Bits Op = new Bits(Opcode); + + Vector128 V0 = MakeVectorE0E1(Z, Z); + Vector128 V1 = MakeVectorE0(A); + Vector128 V2 = MakeVectorE0(B); + AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2); + + AArch64.Vpart(0, 0, new Bits(Z)); AArch64.Vpart(0, 1, new Bits(Z)); + AArch64.Vpart(1, 0, new Bits(A)); + AArch64.Vpart(2, 0, new Bits(B)); + SimdFp.Umlsl_V(Op[30], Op[23, 22], Op[20, 16], 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, Pairwise, Description("UMLSL{2} ., ., .")] + public void Umlsl_V_16B8H_8H4S_4S2D([Values(0u)] uint Rd, + [Values(1u, 0u)] uint Rn, + [Values(2u, 0u)] uint Rm, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong Z, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong A, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong B, + [Values(0b00u, 0b01u, 0b10u)] uint size) // <16B8H, 8H4S, 4S2D> + { + uint Opcode = 0x6E20A000; // UMLSL2 V0.8H, V0.16B, V0.16B + Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); + Opcode |= ((size & 3) << 22); + Bits Op = new Bits(Opcode); + + Vector128 V0 = MakeVectorE0E1(Z, Z); + Vector128 V1 = MakeVectorE1(A); + Vector128 V2 = MakeVectorE1(B); + AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2); + + AArch64.Vpart(0, 0, new Bits(Z)); AArch64.Vpart(0, 1, new Bits(Z)); + AArch64.Vpart(1, 1, new Bits(A)); + AArch64.Vpart(2, 1, new Bits(B)); + SimdFp.Umlsl_V(Op[30], Op[23, 22], Op[20, 16], 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, Pairwise, Description("UQADD , , ")] public void Uqadd_S_B_H_S_D([Values(0u)] uint Rd, [Values(1u, 0u)] uint Rn, @@ -3325,22 +3759,21 @@ namespace Ryujinx.Tests.Cpu [ValueSource("_1B1H1S1D_")] [Random(RndCnt)] ulong B, [Values(0b00u, 0b01u, 0b10u, 0b11u)] uint size) // { + const int QCFlagBit = 27; // Cumulative saturation bit. + uint Opcode = 0x7E200C00; // UQADD B0, B0, B0 Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); Opcode |= ((size & 3) << 22); Bits Op = new Bits(Opcode); - int Fpsr = (int)TestContext.CurrentContext.Random.NextUInt(); - Vector128 V0 = MakeVectorE0E1(Z, Z); Vector128 V1 = MakeVectorE0(A); Vector128 V2 = MakeVectorE0(B); - AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2, Fpsr: Fpsr); + AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2); AArch64.Vpart(0, 0, new Bits(Z)); AArch64.Vpart(0, 1, new Bits(Z)); AArch64.V(1, new Bits(A)); AArch64.V(2, new Bits(B)); - Shared.FPSR = new Bits((uint)Fpsr); SimdFp.Uqadd_S(Op[23, 22], Op[20, 16], Op[9, 5], Op[4, 0]); Assert.Multiple(() => @@ -3348,7 +3781,8 @@ namespace Ryujinx.Tests.Cpu 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())); }); - Assert.That(ThreadState.Fpsr, Is.EqualTo((int)Shared.FPSR.ToUInt32())); + + Assert.That(((ThreadState.Fpsr >> QCFlagBit) & 1) != 0, Is.EqualTo(Shared.FPSR[QCFlagBit])); } [Test, Pairwise, Description("UQADD ., ., .")] @@ -3360,22 +3794,21 @@ namespace Ryujinx.Tests.Cpu [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong B, [Values(0b00u, 0b01u, 0b10u)] uint size) // <8B, 4H, 2S> { + const int QCFlagBit = 27; // Cumulative saturation bit. + uint Opcode = 0x2E200C00; // UQADD V0.8B, V0.8B, V0.8B Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); Opcode |= ((size & 3) << 22); Bits Op = new Bits(Opcode); - int Fpsr = (int)TestContext.CurrentContext.Random.NextUInt(); - Vector128 V0 = MakeVectorE0E1(Z, Z); Vector128 V1 = MakeVectorE0(A); Vector128 V2 = MakeVectorE0(B); - AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2, Fpsr: Fpsr); + AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2); AArch64.Vpart(0, 0, new Bits(Z)); AArch64.Vpart(0, 1, new Bits(Z)); AArch64.V(1, new Bits(A)); AArch64.V(2, new Bits(B)); - Shared.FPSR = new Bits((uint)Fpsr); SimdFp.Uqadd_V(Op[30], Op[23, 22], Op[20, 16], Op[9, 5], Op[4, 0]); Assert.Multiple(() => @@ -3383,7 +3816,8 @@ namespace Ryujinx.Tests.Cpu 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())); }); - Assert.That(ThreadState.Fpsr, Is.EqualTo((int)Shared.FPSR.ToUInt32())); + + Assert.That(((ThreadState.Fpsr >> QCFlagBit) & 1) != 0, Is.EqualTo(Shared.FPSR[QCFlagBit])); } [Test, Pairwise, Description("UQADD ., ., .")] @@ -3395,22 +3829,21 @@ namespace Ryujinx.Tests.Cpu [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong B, [Values(0b00u, 0b01u, 0b10u, 0b11u)] uint size) // <16B, 8H, 4S, 2D> { + const int QCFlagBit = 27; // Cumulative saturation bit. + uint Opcode = 0x6E200C00; // UQADD V0.16B, V0.16B, V0.16B Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); Opcode |= ((size & 3) << 22); Bits Op = new Bits(Opcode); - int Fpsr = (int)TestContext.CurrentContext.Random.NextUInt(); - Vector128 V0 = MakeVectorE0E1(Z, Z); Vector128 V1 = MakeVectorE0E1(A, A); Vector128 V2 = MakeVectorE0E1(B, B); - AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2, Fpsr: Fpsr); + AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2); 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)); AArch64.Vpart(2, 0, new Bits(B)); AArch64.Vpart(2, 1, new Bits(B)); - Shared.FPSR = new Bits((uint)Fpsr); SimdFp.Uqadd_V(Op[30], Op[23, 22], Op[20, 16], Op[9, 5], Op[4, 0]); Assert.Multiple(() => @@ -3418,7 +3851,8 @@ namespace Ryujinx.Tests.Cpu 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())); }); - Assert.That(ThreadState.Fpsr, Is.EqualTo((int)Shared.FPSR.ToUInt32())); + + Assert.That(((ThreadState.Fpsr >> QCFlagBit) & 1) != 0, Is.EqualTo(Shared.FPSR[QCFlagBit])); } [Test, Pairwise, Description("UQSUB , , ")] @@ -3430,22 +3864,21 @@ namespace Ryujinx.Tests.Cpu [ValueSource("_1B1H1S1D_")] [Random(RndCnt)] ulong B, [Values(0b00u, 0b01u, 0b10u, 0b11u)] uint size) // { + const int QCFlagBit = 27; // Cumulative saturation bit. + uint Opcode = 0x7E202C00; // UQSUB B0, B0, B0 Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); Opcode |= ((size & 3) << 22); Bits Op = new Bits(Opcode); - int Fpsr = (int)TestContext.CurrentContext.Random.NextUInt(); - Vector128 V0 = MakeVectorE0E1(Z, Z); Vector128 V1 = MakeVectorE0(A); Vector128 V2 = MakeVectorE0(B); - AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2, Fpsr: Fpsr); + AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2); AArch64.Vpart(0, 0, new Bits(Z)); AArch64.Vpart(0, 1, new Bits(Z)); AArch64.V(1, new Bits(A)); AArch64.V(2, new Bits(B)); - Shared.FPSR = new Bits((uint)Fpsr); SimdFp.Uqsub_S(Op[23, 22], Op[20, 16], Op[9, 5], Op[4, 0]); Assert.Multiple(() => @@ -3453,7 +3886,8 @@ namespace Ryujinx.Tests.Cpu 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())); }); - Assert.That(ThreadState.Fpsr, Is.EqualTo((int)Shared.FPSR.ToUInt32())); + + Assert.That(((ThreadState.Fpsr >> QCFlagBit) & 1) != 0, Is.EqualTo(Shared.FPSR[QCFlagBit])); } [Test, Pairwise, Description("UQSUB ., ., .")] @@ -3465,22 +3899,21 @@ namespace Ryujinx.Tests.Cpu [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong B, [Values(0b00u, 0b01u, 0b10u)] uint size) // <8B, 4H, 2S> { + const int QCFlagBit = 27; // Cumulative saturation bit. + uint Opcode = 0x2E202C00; // UQSUB V0.8B, V0.8B, V0.8B Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); Opcode |= ((size & 3) << 22); Bits Op = new Bits(Opcode); - int Fpsr = (int)TestContext.CurrentContext.Random.NextUInt(); - Vector128 V0 = MakeVectorE0E1(Z, Z); Vector128 V1 = MakeVectorE0(A); Vector128 V2 = MakeVectorE0(B); - AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2, Fpsr: Fpsr); + AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2); AArch64.Vpart(0, 0, new Bits(Z)); AArch64.Vpart(0, 1, new Bits(Z)); AArch64.V(1, new Bits(A)); AArch64.V(2, new Bits(B)); - Shared.FPSR = new Bits((uint)Fpsr); SimdFp.Uqsub_V(Op[30], Op[23, 22], Op[20, 16], Op[9, 5], Op[4, 0]); Assert.Multiple(() => @@ -3488,7 +3921,8 @@ namespace Ryujinx.Tests.Cpu 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())); }); - Assert.That(ThreadState.Fpsr, Is.EqualTo((int)Shared.FPSR.ToUInt32())); + + Assert.That(((ThreadState.Fpsr >> QCFlagBit) & 1) != 0, Is.EqualTo(Shared.FPSR[QCFlagBit])); } [Test, Pairwise, Description("UQSUB ., ., .")] @@ -3500,22 +3934,21 @@ namespace Ryujinx.Tests.Cpu [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong B, [Values(0b00u, 0b01u, 0b10u, 0b11u)] uint size) // <16B, 8H, 4S, 2D> { + const int QCFlagBit = 27; // Cumulative saturation bit. + uint Opcode = 0x6E202C00; // UQSUB V0.16B, V0.16B, V0.16B Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); Opcode |= ((size & 3) << 22); Bits Op = new Bits(Opcode); - int Fpsr = (int)TestContext.CurrentContext.Random.NextUInt(); - Vector128 V0 = MakeVectorE0E1(Z, Z); Vector128 V1 = MakeVectorE0E1(A, A); Vector128 V2 = MakeVectorE0E1(B, B); - AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2, Fpsr: Fpsr); + AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2); 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)); AArch64.Vpart(2, 0, new Bits(B)); AArch64.Vpart(2, 1, new Bits(B)); - Shared.FPSR = new Bits((uint)Fpsr); SimdFp.Uqsub_V(Op[30], Op[23, 22], Op[20, 16], Op[9, 5], Op[4, 0]); Assert.Multiple(() => @@ -3523,7 +3956,8 @@ namespace Ryujinx.Tests.Cpu 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())); }); - Assert.That(ThreadState.Fpsr, Is.EqualTo((int)Shared.FPSR.ToUInt32())); + + Assert.That(((ThreadState.Fpsr >> QCFlagBit) & 1) != 0, Is.EqualTo(Shared.FPSR[QCFlagBit])); } [Test, Pairwise, Description("URHADD ., ., .")] @@ -3588,6 +4022,68 @@ namespace Ryujinx.Tests.Cpu }); } + [Test, Pairwise, Description("USUBL{2} ., ., .")] + public void Usubl_V_8B8H_4H4S_2S2D([Values(0u)] uint Rd, + [Values(1u, 0u)] uint Rn, + [Values(2u, 0u)] uint Rm, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong Z, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong A, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong B, + [Values(0b00u, 0b01u, 0b10u)] uint size) // <8B8H, 4H4S, 2S2D> + { + uint Opcode = 0x2E202000; // USUBL V0.8H, V0.8B, V0.8B + Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); + Opcode |= ((size & 3) << 22); + Bits Op = new Bits(Opcode); + + Vector128 V0 = MakeVectorE0E1(Z, Z); + Vector128 V1 = MakeVectorE0(A); + Vector128 V2 = MakeVectorE0(B); + AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2); + + AArch64.Vpart(0, 0, new Bits(Z)); AArch64.Vpart(0, 1, new Bits(Z)); + AArch64.Vpart(1, 0, new Bits(A)); + AArch64.Vpart(2, 0, new Bits(B)); + SimdFp.Usubl_V(Op[30], Op[23, 22], Op[20, 16], 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, Pairwise, Description("USUBL{2} ., ., .")] + public void Usubl_V_16B8H_8H4S_4S2D([Values(0u)] uint Rd, + [Values(1u, 0u)] uint Rn, + [Values(2u, 0u)] uint Rm, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong Z, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong A, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong B, + [Values(0b00u, 0b01u, 0b10u)] uint size) // <16B8H, 8H4S, 4S2D> + { + uint Opcode = 0x6E202000; // USUBL2 V0.8H, V0.16B, V0.16B + Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); + Opcode |= ((size & 3) << 22); + Bits Op = new Bits(Opcode); + + Vector128 V0 = MakeVectorE0E1(Z, Z); + Vector128 V1 = MakeVectorE1(A); + Vector128 V2 = MakeVectorE1(B); + AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2); + + AArch64.Vpart(0, 0, new Bits(Z)); AArch64.Vpart(0, 1, new Bits(Z)); + AArch64.Vpart(1, 1, new Bits(A)); + AArch64.Vpart(2, 1, new Bits(B)); + SimdFp.Usubl_V(Op[30], Op[23, 22], Op[20, 16], 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, Pairwise, Description("USUBW{2} ., ., .")] public void Usubw_V_8B8H8H_4H4S4S_2S2D2D([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 206d3963b..e3aa4e40b 100644 --- a/Ryujinx.Tests/Cpu/Tester/Instructions.cs +++ b/Ryujinx.Tests/Cpu/Tester/Instructions.cs @@ -2685,6 +2685,154 @@ namespace Ryujinx.Tests.Cpu.Tester V(d, result); } + // fcvtns_advsimd.html#FCVTNS_asisdmisc_R + public static void Fcvtns_S(Bits sz, Bits Rn, Bits Rd) + { + const bool U = false; + const bool o2 = false; + const bool o1 = false; + + /* Decode Scalar */ + int d = (int)UInt(Rd); + int n = (int)UInt(Rn); + + int esize = 32 << (int)UInt(sz); + int datasize = esize; + int elements = 1; + + FPRounding rounding = FPDecodeRounding(Bits.Concat(o1, o2)); + + bool unsigned = (U == true); + + /* Operation */ + /* CheckFPAdvSIMDEnabled64(); */ + + Bits result = new Bits(datasize); + Bits operand = V(datasize, n); + Bits element; + + for (int e = 0; e <= elements - 1; e++) + { + element = Elem(operand, e, esize); + + Elem(result, e, esize, FPToFixed(esize, element, 0, unsigned, FPCR, rounding)); + } + + V(d, result); + } + + // fcvtns_advsimd.html#FCVTNS_asimdmisc_R + public static void Fcvtns_V(bool Q, Bits sz, Bits Rn, Bits Rd) + { + const bool U = false; + const bool o2 = false; + const bool o1 = false; + + /* Decode Vector */ + int d = (int)UInt(Rd); + int n = (int)UInt(Rn); + + /* if sz:Q == '10' then ReservedValue(); */ + + int esize = 32 << (int)UInt(sz); + int datasize = (Q ? 128 : 64); + int elements = datasize / esize; + + FPRounding rounding = FPDecodeRounding(Bits.Concat(o1, o2)); + + bool unsigned = (U == true); + + /* Operation */ + /* CheckFPAdvSIMDEnabled64(); */ + + Bits result = new Bits(datasize); + Bits operand = V(datasize, n); + Bits element; + + for (int e = 0; e <= elements - 1; e++) + { + element = Elem(operand, e, esize); + + Elem(result, e, esize, FPToFixed(esize, element, 0, unsigned, FPCR, rounding)); + } + + V(d, result); + } + + // fcvtnu_advsimd.html#FCVTNU_asisdmisc_R + public static void Fcvtnu_S(Bits sz, Bits Rn, Bits Rd) + { + const bool U = true; + const bool o2 = false; + const bool o1 = false; + + /* Decode Scalar */ + int d = (int)UInt(Rd); + int n = (int)UInt(Rn); + + int esize = 32 << (int)UInt(sz); + int datasize = esize; + int elements = 1; + + FPRounding rounding = FPDecodeRounding(Bits.Concat(o1, o2)); + + bool unsigned = (U == true); + + /* Operation */ + /* CheckFPAdvSIMDEnabled64(); */ + + Bits result = new Bits(datasize); + Bits operand = V(datasize, n); + Bits element; + + for (int e = 0; e <= elements - 1; e++) + { + element = Elem(operand, e, esize); + + Elem(result, e, esize, FPToFixed(esize, element, 0, unsigned, FPCR, rounding)); + } + + V(d, result); + } + + // fcvtnu_advsimd.html#FCVTNU_asimdmisc_R + public static void Fcvtnu_V(bool Q, Bits sz, Bits Rn, Bits Rd) + { + const bool U = true; + const bool o2 = false; + const bool o1 = false; + + /* Decode Vector */ + int d = (int)UInt(Rd); + int n = (int)UInt(Rn); + + /* if sz:Q == '10' then ReservedValue(); */ + + int esize = 32 << (int)UInt(sz); + int datasize = (Q ? 128 : 64); + int elements = datasize / esize; + + FPRounding rounding = FPDecodeRounding(Bits.Concat(o1, o2)); + + bool unsigned = (U == true); + + /* Operation */ + /* CheckFPAdvSIMDEnabled64(); */ + + Bits result = new Bits(datasize); + Bits operand = V(datasize, n); + Bits element; + + for (int e = 0; e <= elements - 1; e++) + { + element = Elem(operand, e, esize); + + Elem(result, e, esize, FPToFixed(esize, element, 0, unsigned, FPCR, rounding)); + } + + V(d, result); + } + // neg_advsimd.html#NEG_asisdmisc_R public static void Neg_S(Bits size, Bits Rn, Bits Rd) { @@ -5122,6 +5270,57 @@ namespace Ryujinx.Tests.Cpu.Tester V(d, result); } + // saddl_advsimd.html + public static void Saddl_V(bool Q, Bits size, Bits Rm, Bits Rn, Bits Rd) + { + const bool U = false; + const 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 unsigned = (U == true); + + /* Operation */ + /* CheckFPAdvSIMDEnabled64(); */ + + Bits result = new Bits(2 * datasize); + Bits operand1 = Vpart(datasize, n, part); + Bits operand2 = Vpart(datasize, m, part); + BigInteger element1; + BigInteger element2; + BigInteger sum; + + for (int e = 0; e <= elements - 1; e++) + { + element1 = Int(Elem(operand1, e, esize), unsigned); + element2 = Int(Elem(operand2, e, esize), unsigned); + + if (sub_op) + { + sum = element1 - element2; + } + else + { + sum = element1 + element2; + } + + Elem(result, e, 2 * esize, sum.SubBigInteger(2 * esize - 1, 0)); + } + + V(d, result); + } + // saddw_advsimd.html public static void Saddw_V(bool Q, Bits size, Bits Rm, Bits Rn, Bits Rd) { @@ -5333,6 +5532,116 @@ namespace Ryujinx.Tests.Cpu.Tester V(d, result); } + // smlal_advsimd_vec.html + public static void Smlal_V(bool Q, Bits size, Bits Rm, Bits Rn, Bits Rd) + { + const bool U = false; + const 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 unsigned = (U == true); + + /* Operation */ + /* CheckFPAdvSIMDEnabled64(); */ + + Bits result = new Bits(2 * datasize); + Bits operand1 = Vpart(datasize, n, part); + Bits operand2 = Vpart(datasize, m, part); + Bits operand3 = V(2 * datasize, d); + BigInteger element1; + BigInteger element2; + Bits product; + Bits accum; + + for (int e = 0; e <= elements - 1; e++) + { + element1 = Int(Elem(operand1, e, esize), unsigned); + element2 = Int(Elem(operand2, e, esize), unsigned); + + product = (element1 * element2).SubBigInteger(2 * esize - 1, 0); + + if (sub_op) + { + accum = Elem(operand3, e, 2 * esize) - product; + } + else + { + accum = Elem(operand3, e, 2 * esize) + product; + } + + Elem(result, e, 2 * esize, accum); + } + + V(d, result); + } + + // smlsl_advsimd_vec.html + public static void Smlsl_V(bool Q, Bits size, Bits Rm, Bits Rn, Bits Rd) + { + const bool U = false; + const 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 unsigned = (U == true); + + /* Operation */ + /* CheckFPAdvSIMDEnabled64(); */ + + Bits result = new Bits(2 * datasize); + Bits operand1 = Vpart(datasize, n, part); + Bits operand2 = Vpart(datasize, m, part); + Bits operand3 = V(2 * datasize, d); + BigInteger element1; + BigInteger element2; + Bits product; + Bits accum; + + for (int e = 0; e <= elements - 1; e++) + { + element1 = Int(Elem(operand1, e, esize), unsigned); + element2 = Int(Elem(operand2, e, esize), unsigned); + + product = (element1 * element2).SubBigInteger(2 * esize - 1, 0); + + if (sub_op) + { + accum = Elem(operand3, e, 2 * esize) - product; + } + else + { + accum = Elem(operand3, e, 2 * esize) + product; + } + + Elem(result, e, 2 * esize, accum); + } + + V(d, result); + } + // sqadd_advsimd.html#SQADD_asisdsame_only public static void Sqadd_S(Bits size, Bits Rm, Bits Rn, Bits Rd) { @@ -5771,6 +6080,57 @@ namespace Ryujinx.Tests.Cpu.Tester V(d, result); } + // ssubl_advsimd.html + public static void Ssubl_V(bool Q, Bits size, Bits Rm, Bits Rn, Bits Rd) + { + const bool U = false; + const 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 unsigned = (U == true); + + /* Operation */ + /* CheckFPAdvSIMDEnabled64(); */ + + Bits result = new Bits(2 * datasize); + Bits operand1 = Vpart(datasize, n, part); + Bits operand2 = Vpart(datasize, m, part); + BigInteger element1; + BigInteger element2; + BigInteger sum; + + for (int e = 0; e <= elements - 1; e++) + { + element1 = Int(Elem(operand1, e, esize), unsigned); + element2 = Int(Elem(operand2, e, esize), unsigned); + + if (sub_op) + { + sum = element1 - element2; + } + else + { + sum = element1 + element2; + } + + Elem(result, e, 2 * esize, sum.SubBigInteger(2 * esize - 1, 0)); + } + + V(d, result); + } + // ssubw_advsimd.html public static void Ssubw_V(bool Q, Bits size, Bits Rm, Bits Rn, Bits Rd) { @@ -6212,6 +6572,57 @@ namespace Ryujinx.Tests.Cpu.Tester V(d, result); } + // uaddl_advsimd.html + public static void Uaddl_V(bool Q, Bits size, Bits Rm, Bits Rn, Bits Rd) + { + const bool U = true; + const 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 unsigned = (U == true); + + /* Operation */ + /* CheckFPAdvSIMDEnabled64(); */ + + Bits result = new Bits(2 * datasize); + Bits operand1 = Vpart(datasize, n, part); + Bits operand2 = Vpart(datasize, m, part); + BigInteger element1; + BigInteger element2; + BigInteger sum; + + for (int e = 0; e <= elements - 1; e++) + { + element1 = Int(Elem(operand1, e, esize), unsigned); + element2 = Int(Elem(operand2, e, esize), unsigned); + + if (sub_op) + { + sum = element1 - element2; + } + else + { + sum = element1 + element2; + } + + Elem(result, e, 2 * esize, sum.SubBigInteger(2 * esize - 1, 0)); + } + + V(d, result); + } + // uaddw_advsimd.html public static void Uaddw_V(bool Q, Bits size, Bits Rm, Bits Rn, Bits Rd) { @@ -6345,6 +6756,116 @@ namespace Ryujinx.Tests.Cpu.Tester V(d, result); } + // umlal_advsimd_vec.html + public static void Umlal_V(bool Q, Bits size, Bits Rm, Bits Rn, Bits Rd) + { + const bool U = true; + const 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 unsigned = (U == true); + + /* Operation */ + /* CheckFPAdvSIMDEnabled64(); */ + + Bits result = new Bits(2 * datasize); + Bits operand1 = Vpart(datasize, n, part); + Bits operand2 = Vpart(datasize, m, part); + Bits operand3 = V(2 * datasize, d); + BigInteger element1; + BigInteger element2; + Bits product; + Bits accum; + + for (int e = 0; e <= elements - 1; e++) + { + element1 = Int(Elem(operand1, e, esize), unsigned); + element2 = Int(Elem(operand2, e, esize), unsigned); + + product = (element1 * element2).SubBigInteger(2 * esize - 1, 0); + + if (sub_op) + { + accum = Elem(operand3, e, 2 * esize) - product; + } + else + { + accum = Elem(operand3, e, 2 * esize) + product; + } + + Elem(result, e, 2 * esize, accum); + } + + V(d, result); + } + + // umlsl_advsimd_vec.html + public static void Umlsl_V(bool Q, Bits size, Bits Rm, Bits Rn, Bits Rd) + { + const bool U = true; + const 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 unsigned = (U == true); + + /* Operation */ + /* CheckFPAdvSIMDEnabled64(); */ + + Bits result = new Bits(2 * datasize); + Bits operand1 = Vpart(datasize, n, part); + Bits operand2 = Vpart(datasize, m, part); + Bits operand3 = V(2 * datasize, d); + BigInteger element1; + BigInteger element2; + Bits product; + Bits accum; + + for (int e = 0; e <= elements - 1; e++) + { + element1 = Int(Elem(operand1, e, esize), unsigned); + element2 = Int(Elem(operand2, e, esize), unsigned); + + product = (element1 * element2).SubBigInteger(2 * esize - 1, 0); + + if (sub_op) + { + accum = Elem(operand3, e, 2 * esize) - product; + } + else + { + accum = Elem(operand3, e, 2 * esize) + product; + } + + Elem(result, e, 2 * esize, accum); + } + + V(d, result); + } + // uqadd_advsimd.html#UQADD_asisdsame_only public static void Uqadd_S(Bits size, Bits Rm, Bits Rn, Bits Rd) { @@ -6579,6 +7100,57 @@ namespace Ryujinx.Tests.Cpu.Tester V(d, result); } + // usubl_advsimd.html + public static void Usubl_V(bool Q, Bits size, Bits Rm, Bits Rn, Bits Rd) + { + const bool U = true; + const 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 unsigned = (U == true); + + /* Operation */ + /* CheckFPAdvSIMDEnabled64(); */ + + Bits result = new Bits(2 * datasize); + Bits operand1 = Vpart(datasize, n, part); + Bits operand2 = Vpart(datasize, m, part); + BigInteger element1; + BigInteger element2; + BigInteger sum; + + for (int e = 0; e <= elements - 1; e++) + { + element1 = Int(Elem(operand1, e, esize), unsigned); + element2 = Int(Elem(operand2, e, esize), unsigned); + + if (sub_op) + { + sum = element1 - element2; + } + else + { + sum = element1 + element2; + } + + Elem(result, e, 2 * esize, sum.SubBigInteger(2 * esize - 1, 0)); + } + + V(d, result); + } + // usubw_advsimd.html public static void Usubw_V(bool Q, Bits size, Bits Rm, Bits Rn, Bits Rd) { diff --git a/Ryujinx.Tests/Cpu/Tester/Pseudocode.cs b/Ryujinx.Tests/Cpu/Tester/Pseudocode.cs index 40bec9c54..f37774763 100644 --- a/Ryujinx.Tests/Cpu/Tester/Pseudocode.cs +++ b/Ryujinx.Tests/Cpu/Tester/Pseudocode.cs @@ -5,21 +5,19 @@ // https://alastairreid.github.io/asl-lexical-syntax/ -// | ------------------------|----------------------------------- | -// | ASL | C# | -// | ------------------------|----------------------------------- | -// | bit, bits(1); boolean | bool | -// | bits | Bits | -// | integer | BigInteger, int | -// | real | decimal | -// | ------------------------|----------------------------------- | -// | '0'; FALSE | false | -// | '1'; TRUE | true | -// | '010' | "010" | -// | bitsX IN {bitsY, bitsZ} | (bitsX == bitsY || bitsX == bitsZ) | -// | DIV | / | -// | MOD | % | -// | ------------------------|----------------------------------- | +// | ------------------------|-------------------------------- | +// | ASL | C# | +// | ------------------------|-------------------------------- | +// | bit, bits(1); boolean | bool | +// | bits | Bits | +// | integer | BigInteger, int | +// | real | decimal; double, float | +// | ------------------------|-------------------------------- | +// | '0'; FALSE | false | +// | '1'; TRUE | true | +// | '010' | "010" | +// | DIV, MOD | /, % | +// | ------------------------|-------------------------------- | using System; using System.Numerics; @@ -107,6 +105,7 @@ namespace Ryujinx.Tests.Cpu.Tester /* SP_EL1 = bits(64) UNKNOWN; */ SP_EL1.SetAll(false); + FPCR.SetAll(false); // TODO: Add named fields. FPSR.SetAll(false); // TODO: Add named fields. } @@ -458,6 +457,7 @@ namespace Ryujinx.Tests.Cpu.Tester #endregion #region "instrs/vector/reduce/reduceop/" + // shared_pseudocode.html#impl-aarch64.Reduce.3 public static Bits Reduce(ReduceOp op, Bits input, int esize) { int N = input.Count; @@ -528,6 +528,7 @@ namespace Ryujinx.Tests.Cpu.Tester SP_EL0 = new Bits(64, false); SP_EL1 = new Bits(64, false); + FPCR = new Bits(32, false); // TODO: Add named fields. FPSR = new Bits(32, false); // TODO: Add named fields. PSTATE.N = false; @@ -817,6 +818,36 @@ namespace Ryujinx.Tests.Cpu.Tester return (decimal)value; } + /* */ + public static float Real_32(BigInteger value) + { + if (value == BigInteger.Pow((BigInteger)2.0f, 1000)) + { + return float.PositiveInfinity; + } + if (value == -BigInteger.Pow((BigInteger)2.0f, 1000)) + { + return float.NegativeInfinity; + } + + return (float)value; + } + + /* */ + public static double Real_64(BigInteger value) + { + if (value == BigInteger.Pow((BigInteger)2.0, 10000)) + { + return double.PositiveInfinity; + } + if (value == -BigInteger.Pow((BigInteger)2.0, 10000)) + { + return double.NegativeInfinity; + } + + return (double)value; + } + // shared_pseudocode.html#impl-shared.ROR.2 public static Bits ROR(Bits x, int shift) { @@ -881,6 +912,36 @@ namespace Ryujinx.Tests.Cpu.Tester return (BigInteger)Decimal.Floor(x); } + /* */ + public static BigInteger RoundDown_32(float x) + { + if (float.IsPositiveInfinity(x)) + { + return BigInteger.Pow((BigInteger)2.0f, 1000); + } + if (float.IsNegativeInfinity(x)) + { + return -BigInteger.Pow((BigInteger)2.0f, 1000); + } + + return (BigInteger)MathF.Floor(x); + } + + /* */ + public static BigInteger RoundDown_64(double x) + { + if (double.IsPositiveInfinity(x)) + { + return BigInteger.Pow((BigInteger)2.0, 10000); + } + if (double.IsNegativeInfinity(x)) + { + return -BigInteger.Pow((BigInteger)2.0, 10000); + } + + return (BigInteger)Math.Floor(x); + } + // shared_pseudocode.html#impl-shared.RoundTowardsZero.1 public static BigInteger RoundTowardsZero(decimal x) { @@ -1091,6 +1152,398 @@ namespace Ryujinx.Tests.Cpu.Tester } #endregion +#region "functions/float/fpdecoderounding/" + /* shared_pseudocode.html#impl-shared.FPDecodeRounding.1 */ + public static FPRounding FPDecodeRounding(Bits rmode) + { + switch (rmode) + { + default: + case Bits bits when bits == "00": + return FPRounding.FPRounding_TIEEVEN; // N + case Bits bits when bits == "01": + return FPRounding.FPRounding_POSINF; // P + case Bits bits when bits == "10": + return FPRounding.FPRounding_NEGINF; // M + case Bits bits when bits == "11": + return FPRounding.FPRounding_ZERO; // Z + } + } +#endregion + +#region "functions/float/fpexc/" + // shared_pseudocode.html#FPExc + public enum FPExc {FPExc_InvalidOp, FPExc_DivideByZero, FPExc_Overflow, + FPExc_Underflow, FPExc_Inexact, FPExc_InputDenorm}; +#endregion + +#region "functions/float/fpprocessexception/" + // shared_pseudocode.html#impl-shared.FPProcessException.2 + public static void FPProcessException(FPExc exception, Bits _fpcr) + { + Bits fpcr = new Bits(_fpcr); // Clone. + + int cumul; + + // Determine the cumulative exception bit number + switch (exception) + { + default: + case FPExc.FPExc_InvalidOp: cumul = 0; break; + case FPExc.FPExc_DivideByZero: cumul = 1; break; + case FPExc.FPExc_Overflow: cumul = 2; break; + case FPExc.FPExc_Underflow: cumul = 3; break; + case FPExc.FPExc_Inexact: cumul = 4; break; + case FPExc.FPExc_InputDenorm: cumul = 7; break; + } + + int enable = cumul + 8; + + if (fpcr[enable]) + { + // Trapping of the exception enabled. + // It is IMPLEMENTATION DEFINED whether the enable bit may be set at all, and + // if so then how exceptions may be accumulated before calling FPTrapException() + /* IMPLEMENTATION_DEFINED "floating-point trap handling"; */ + + throw new NotImplementedException(); + }/* + else if (UsingAArch32()) + { + // Set the cumulative exception bit + FPSCR = '1'; + }*/ + else + { + // Set the cumulative exception bit + FPSR[cumul] = true; + } + } +#endregion + +#region "functions/float/fprounding/" + // shared_pseudocode.html#FPRounding + public enum FPRounding {FPRounding_TIEEVEN, FPRounding_POSINF, + FPRounding_NEGINF, FPRounding_ZERO, + FPRounding_TIEAWAY, FPRounding_ODD}; +#endregion + +#region "functions/float/fptofixed/" + /* shared_pseudocode.html#impl-shared.FPToFixed.5 */ + public static Bits FPToFixed(int M, Bits op, int fbits, bool unsigned, Bits _fpcr, FPRounding rounding) + { + int N = op.Count; + + /* assert N IN {16,32,64}; */ + /* assert M IN {16,32,64}; */ + /* assert fbits >= 0; */ + /* assert rounding != FPRounding_ODD; */ + + Bits fpcr = new Bits(_fpcr); // Clone. + + if (N == 16) + { + throw new NotImplementedException(); + } + else if (N == 32) + { + // Unpack using fpcr to determine if subnormals are flushed-to-zero + (FPType type, bool sign, float value) = FPUnpack_32(op, fpcr); + + // If NaN, set cumulative flag or take exception + if (type == FPType.FPType_SNaN || type == FPType.FPType_QNaN) + { + FPProcessException(FPExc.FPExc_InvalidOp, fpcr); + } + + // Scale by fractional bits and produce integer rounded towards minus-infinity + value = value * MathF.Pow(2.0f, fbits); + BigInteger int_result = RoundDown_32(value); + float error = value - Real_32(int_result); + + if (float.IsNaN(error)) + { + error = 0.0f; + } + + // Determine whether supplied rounding mode requires an increment + bool round_up; + + switch (rounding) + { + default: + case FPRounding.FPRounding_TIEEVEN: + round_up = (error > 0.5f || (error == 0.5f && int_result.SubBigInteger(0))); + break; + case FPRounding.FPRounding_POSINF: + round_up = (error != 0.0f); + break; + case FPRounding.FPRounding_NEGINF: + round_up = false; + break; + case FPRounding.FPRounding_ZERO: + round_up = (error != 0.0f && int_result < (BigInteger)0); + break; + case FPRounding.FPRounding_TIEAWAY: + round_up = (error > 0.5f || (error == 0.5f && int_result >= (BigInteger)0)); + break; + } + + if (round_up) + { + int_result = int_result + 1; + } + + // Generate saturated result and exceptions + (Bits result, bool overflow) = SatQ(int_result, M, unsigned); + + if (overflow) + { + FPProcessException(FPExc.FPExc_InvalidOp, fpcr); + } + else if (error != 0.0f) + { + FPProcessException(FPExc.FPExc_Inexact, fpcr); + } + + return result; + } + else /* if (N == 64) */ + { + // Unpack using fpcr to determine if subnormals are flushed-to-zero + (FPType type, bool sign, double value) = FPUnpack_64(op, fpcr); + + // If NaN, set cumulative flag or take exception + if (type == FPType.FPType_SNaN || type == FPType.FPType_QNaN) + { + FPProcessException(FPExc.FPExc_InvalidOp, fpcr); + } + + // Scale by fractional bits and produce integer rounded towards minus-infinity + value = value * Math.Pow(2.0, fbits); + BigInteger int_result = RoundDown_64(value); + double error = value - Real_64(int_result); + + if (double.IsNaN(error)) + { + error = 0.0; + } + + // Determine whether supplied rounding mode requires an increment + bool round_up; + + switch (rounding) + { + default: + case FPRounding.FPRounding_TIEEVEN: + round_up = (error > 0.5 || (error == 0.5 && int_result.SubBigInteger(0))); + break; + case FPRounding.FPRounding_POSINF: + round_up = (error != 0.0); + break; + case FPRounding.FPRounding_NEGINF: + round_up = false; + break; + case FPRounding.FPRounding_ZERO: + round_up = (error != 0.0 && int_result < (BigInteger)0); + break; + case FPRounding.FPRounding_TIEAWAY: + round_up = (error > 0.5 || (error == 0.5 && int_result >= (BigInteger)0)); + break; + } + + if (round_up) + { + int_result = int_result + 1; + } + + // Generate saturated result and exceptions + (Bits result, bool overflow) = SatQ(int_result, M, unsigned); + + if (overflow) + { + FPProcessException(FPExc.FPExc_InvalidOp, fpcr); + } + else if (error != 0.0) + { + FPProcessException(FPExc.FPExc_Inexact, fpcr); + } + + return result; + } + } +#endregion + +#region "functions/float/fptype/" + // shared_pseudocode.html#FPType + public enum FPType {FPType_Nonzero, FPType_Zero, FPType_Infinity, + FPType_QNaN, FPType_SNaN}; +#endregion + +#region "functions/float/fpunpack/" + /* shared_pseudocode.html#impl-shared.FPUnpack.2 */ + /* shared_pseudocode.html#impl-shared.FPUnpackBase.2 */ + /*public static (FPType, bool, real) FPUnpack_16(Bits fpval, Bits _fpcr) + { + int N = fpval.Count; + + // assert N == 16; + + Bits fpcr = new Bits(_fpcr); // Clone. + + fpcr[26] = false; + + return FPUnpackBase_16(fpval, fpcr); + }*/ + public static (FPType, bool, float) FPUnpack_32(Bits fpval, Bits _fpcr) + { + int N = fpval.Count; + + /* assert N == 32; */ + + Bits fpcr = new Bits(_fpcr); // Clone. + + FPType type; + float value; + + bool sign = fpval[31]; + Bits exp32 = fpval[30, 23]; + Bits frac32 = fpval[22, 0]; + + if (IsZero(exp32)) + { + // Produce zero if value is zero or flush-to-zero is selected. + if (IsZero(frac32) || fpcr[24]) + { + type = FPType.FPType_Zero; + value = 0.0f; + + // Denormalized input flushed to zero + if (!IsZero(frac32)) + { + FPProcessException(FPExc.FPExc_InputDenorm, fpcr); + } + } + else + { + type = FPType.FPType_Nonzero; + value = MathF.Pow(2.0f, -126) * (Real_32(UInt(frac32)) * MathF.Pow(2.0f, -23)); + } + } + else if (IsOnes(exp32)) + { + if (IsZero(frac32)) + { + type = FPType.FPType_Infinity; + /* value = 2.0^1000000; */ + value = MathF.Pow(2.0f, 1000); + } + else + { + type = frac32[22] ? FPType.FPType_QNaN : FPType.FPType_SNaN; + value = 0.0f; + } + } + else + { + type = FPType.FPType_Nonzero; + value = MathF.Pow(2.0f, (int)UInt(exp32) - 127) * (1.0f + Real_32(UInt(frac32)) * MathF.Pow(2.0f, -23)); + } + + if (sign) + { + value = -value; + } + + return (type, sign, value); + } + public static (FPType, bool, double) FPUnpack_64(Bits fpval, Bits _fpcr) + { + int N = fpval.Count; + + /* assert N == 64; */ + + Bits fpcr = new Bits(_fpcr); // Clone. + + FPType type; + double value; + + bool sign = fpval[63]; + Bits exp64 = fpval[62, 52]; + Bits frac64 = fpval[51, 0]; + + if (IsZero(exp64)) + { + // Produce zero if value is zero or flush-to-zero is selected. + if (IsZero(frac64) || fpcr[24]) + { + type = FPType.FPType_Zero; + value = 0.0; + + // Denormalized input flushed to zero + if (!IsZero(frac64)) + { + FPProcessException(FPExc.FPExc_InputDenorm, fpcr); + } + } + else + { + type = FPType.FPType_Nonzero; + value = Math.Pow(2.0, -1022) * (Real_64(UInt(frac64)) * Math.Pow(2.0, -52)); + } + } + else if (IsOnes(exp64)) + { + if (IsZero(frac64)) + { + type = FPType.FPType_Infinity; + /* value = 2.0^1000000; */ + value = Math.Pow(2.0, 10000); + } + else + { + type = frac64[51] ? FPType.FPType_QNaN : FPType.FPType_SNaN; + value = 0.0; + } + } + else + { + type = FPType.FPType_Nonzero; + value = Math.Pow(2.0, (int)UInt(exp64) - 1023) * (1.0 + Real_64(UInt(frac64)) * Math.Pow(2.0, -52)); + } + + if (sign) + { + value = -value; + } + + return (type, sign, value); + } + + /* shared_pseudocode.html#impl-shared.FPUnpackCV.2 */ + /* shared_pseudocode.html#impl-shared.FPUnpackBase.2 */ + /*public static (FPType, bool, real) FPUnpackCV_16(Bits fpval, Bits _fpcr) + { + int N = fpval.Count; + + // assert N == 16; + + Bits fpcr = new Bits(_fpcr); // Clone. + + fpcr[19] = false; + + return FPUnpackBase_16(fpval, fpcr); + }*/ + public static (FPType, bool, float) FPUnpackCV_32(Bits fpval, Bits _fpcr) + { + return FPUnpack_32(fpval, _fpcr); + } + public static (FPType, bool, double) FPUnpackCV_64(Bits fpval, Bits _fpcr) + { + return FPUnpack_64(fpval, _fpcr); + } +#endregion + #region "functions/integer/" /* shared_pseudocode.html#impl-shared.AddWithCarry.3 */ public static (Bits, Bits) AddWithCarry(int N, Bits x, Bits y, bool carry_in) @@ -1117,7 +1570,12 @@ namespace Ryujinx.Tests.Cpu.Tester public static Bits SP_EL0; public static Bits SP_EL1; + public static Bits FPCR; // TODO: Add named fields. + // [ 31 | 30 | 29 | 28 | 27 | 26 | 25 | 24 | 23 22 | 21 20 | 19 | 18 17 16 | 15 | 14 | 13 | 12 | 11 | 10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 ] + // [ 0 | 0 | 0 | 0 | 0 | AHP | DN | FZ | RMode | Stride | FZ16 | Len | IDE | 0 | 0 | IXE | UFE | OFE | DZE | IOE | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 ] public static Bits FPSR; // TODO: Add named fields. + // [ 31 | 30 | 29 | 28 | 27 | 26 | 25 | 24 | 23 | 22 | 21 | 20 | 19 | 18 | 17 | 16 | 15 | 14 | 13 | 12 | 11 | 10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 ] + // [ N | Z | C | V | QC | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | IDC | 0 | 0 | IXC | UFC | OFC | DZC | IOC ] #endregion #region "functions/system/" @@ -1178,6 +1636,8 @@ namespace Ryujinx.Tests.Cpu.Tester /* shared_pseudocode.html#impl-shared.HaveEL.1 */ public static bool HaveEL(Bits el) { + // TODO: Implement ASL: "IN" as C#: "Bits.In()". + /* if el IN {EL1,EL0} then */ if (el == EL1 || el == EL0) { return true; // EL1 and EL0 must exist