diff --git a/ARMeilleure/Decoders/OpCode32SimdCvtFI.cs b/ARMeilleure/Decoders/OpCode32SimdCvtFI.cs index aaedcb3cf..b654a1926 100644 --- a/ARMeilleure/Decoders/OpCode32SimdCvtFI.cs +++ b/ARMeilleure/Decoders/OpCode32SimdCvtFI.cs @@ -2,12 +2,20 @@ { class OpCode32SimdCvtFI : OpCode32SimdS { - public int Opc2 { get; private set; } - public OpCode32SimdCvtFI(InstDescriptor inst, ulong address, int opCode) : base(inst, address, opCode) { - Opc2 = (opCode >> 16) & 0x7; Opc = (opCode >> 7) & 0x1; + + bool toInteger = (Opc2 & 0b100) != 0; + + if (toInteger) + { + Vd = ((opCode >> 22) & 0x1) | ((opCode >> 11) & 0x1e); + } + else + { + Vm = ((opCode >> 5) & 0x1) | ((opCode << 1) & 0x1e); + } } } } diff --git a/ARMeilleure/Decoders/OpCode32SimdS.cs b/ARMeilleure/Decoders/OpCode32SimdS.cs index 2e860d9c8..766cf4bae 100644 --- a/ARMeilleure/Decoders/OpCode32SimdS.cs +++ b/ARMeilleure/Decoders/OpCode32SimdS.cs @@ -2,14 +2,17 @@ { class OpCode32SimdS : OpCode32, IOpCode32Simd { - public int Vd { get; private set; } - public int Vm { get; private set; } - public int Opc { get; protected set; } + public int Vd { get; protected set; } + public int Vm { get; protected set; } + public int Opc { get; protected set; } // "with_zero" (Opc<1>) [Vcmp, Vcmpe]. + public int Opc2 { get; private set; } // opc2 or RM (opc2<1:0>) [Vcvt, Vrint]. public int Size { get; protected set; } public OpCode32SimdS(InstDescriptor inst, ulong address, int opCode) : base(inst, address, opCode) { Opc = (opCode >> 15) & 0x3; + Opc2 = (opCode >> 16) & 0x7; + Size = (opCode >> 8) & 0x3; bool single = Size != 3; diff --git a/ARMeilleure/Decoders/OpCodeTable.cs b/ARMeilleure/Decoders/OpCodeTable.cs index bbcc15baf..4daccfdbf 100644 --- a/ARMeilleure/Decoders/OpCodeTable.cs +++ b/ARMeilleure/Decoders/OpCodeTable.cs @@ -825,15 +825,17 @@ namespace ARMeilleure.Decoders SetA32("<<<<11101x11010xxxxx101x01x0xxxx", InstName.Vcmp, InstEmit32.Vcmp, typeof(OpCode32SimdS)); SetA32("<<<<11101x11010xxxxx101x11x0xxxx", InstName.Vcmpe, InstEmit32.Vcmpe, typeof(OpCode32SimdS)); SetA32("<<<<11101x110111xxxx101x11x0xxxx", InstName.Vcvt, InstEmit32.Vcvt_FD, typeof(OpCode32SimdS)); // FP 32 and 64, scalar. - SetA32("<<<<11101x11110xxxxx10xx11x0xxxx", InstName.Vcvt, InstEmit32.Vcvt_FI, typeof(OpCode32SimdCvtFI)); // FP32 to int. - SetA32("<<<<11101x111000xxxx10xxx1x0xxxx", InstName.Vcvt, InstEmit32.Vcvt_FI, typeof(OpCode32SimdCvtFI)); // Int to FP32. - SetA32("111111101x1111xxxxxx10>>x1x0xxxx", InstName.Vcvt, InstEmit32.Vcvt_R, typeof(OpCode32SimdCvtFI)); // The many FP32 to int encodings (fp). + SetA32("<<<<11101x11110xxxxx101x11x0xxxx", InstName.Vcvt, InstEmit32.Vcvt_FI, typeof(OpCode32SimdCvtFI)); // FP32 to int. + SetA32("<<<<11101x111000xxxx101xx1x0xxxx", InstName.Vcvt, InstEmit32.Vcvt_FI, typeof(OpCode32SimdCvtFI)); // Int to FP32. + SetA32("111111101x1111xxxxxx101xx1x0xxxx", InstName.Vcvt, InstEmit32.Vcvt_RM, typeof(OpCode32SimdCvtFI)); // The many FP32 to int encodings (fp). SetA32("111100111x111011xxxx011xxxx0xxxx", InstName.Vcvt, InstEmit32.Vcvt_V, typeof(OpCode32SimdCmpZ)); // FP and integer, vector. SetA32("<<<<11101x00xxxxxxxx101xx0x0xxxx", InstName.Vdiv, InstEmit32.Vdiv_S, typeof(OpCode32SimdRegS)); SetA32("<<<<11101xx0xxxxxxxx1011x0x10000", InstName.Vdup, InstEmit32.Vdup, typeof(OpCode32SimdDupGP)); SetA32("111100111x11xxxxxxxx11000xx0xxxx", InstName.Vdup, InstEmit32.Vdup_1, typeof(OpCode32SimdDupElem)); SetA32("111100110x00xxxxxxxx0001xxx1xxxx", InstName.Veor, InstEmit32.Veor_I, typeof(OpCode32SimdBinary)); SetA32("111100101x11xxxxxxxxxxxxxxx0xxxx", InstName.Vext, InstEmit32.Vext, typeof(OpCode32SimdExt)); + SetA32("<<<<11101x10xxxxxxxx101xx0x0xxxx", InstName.Vfma, InstEmit32.Vfma_S, typeof(OpCode32SimdRegS)); + SetA32("<<<<11101x10xxxxxxxx101xx1x0xxxx", InstName.Vfms, InstEmit32.Vfms_S, typeof(OpCode32SimdRegS)); SetA32("111101001x10xxxxxxxxxx00xxxxxxxx", InstName.Vld1, InstEmit32.Vld1, typeof(OpCode32SimdMemSingle)); SetA32("111101000x10xxxxxxxx0111xxxxxxxx", InstName.Vld1, InstEmit32.Vld1, typeof(OpCode32SimdMemPair)); // Regs = 1. SetA32("111101000x10xxxxxxxx1010xxxxxxxx", InstName.Vld1, InstEmit32.Vld1, typeof(OpCode32SimdMemPair)); // Regs = 2. @@ -918,8 +920,8 @@ namespace ARMeilleure.Decoders SetA32("111100111x111011xxxx010x0xx0xxxx", InstName.Vrecpe, InstEmit32.Vrecpe, typeof(OpCode32SimdSqrte)); SetA32("111100100x00xxxxxxxx1111xxx1xxxx", InstName.Vrecps, InstEmit32.Vrecps, typeof(OpCode32SimdReg)); SetA32("111100111x11xx00xxxx000<>>xxxxxxx0010>xx1xxxx", InstName.Vrshr, InstEmit32.Vrshr, typeof(OpCode32SimdShImm)); SetA32("111100111x111011xxxx010x1xx0xxxx", InstName.Vrsqrte, InstEmit32.Vrsqrte, typeof(OpCode32SimdSqrte)); SetA32("111100100x10xxxxxxxx1111xxx1xxxx", InstName.Vrsqrts, InstEmit32.Vrsqrts, typeof(OpCode32SimdReg)); diff --git a/ARMeilleure/Instructions/InstEmitSimdArithmetic32.cs b/ARMeilleure/Instructions/InstEmitSimdArithmetic32.cs index f7f3d47e0..57176794b 100644 --- a/ARMeilleure/Instructions/InstEmitSimdArithmetic32.cs +++ b/ARMeilleure/Instructions/InstEmitSimdArithmetic32.cs @@ -231,6 +231,38 @@ namespace ARMeilleure.Instructions } } + public static void Vfma_S(ArmEmitterContext context) // Fused. + { + if (Optimizations.FastFP && Optimizations.UseSse2) + { + // TODO: Use FMA instruction set. + EmitScalarTernaryOpF32(context, Intrinsic.X86Mulss, Intrinsic.X86Mulsd, Intrinsic.X86Addss, Intrinsic.X86Addsd); + } + else + { + EmitScalarTernaryOpF32(context, (op1, op2, op3) => + { + return EmitSoftFloatCall(context, nameof(SoftFloat32.FPMulAdd), op1, op2, op3); + }); + } + } + + public static void Vfms_S(ArmEmitterContext context) // Fused. + { + if (Optimizations.FastFP && Optimizations.UseSse2) + { + // TODO: Use FMA instruction set. + EmitScalarTernaryOpF32(context, Intrinsic.X86Mulss, Intrinsic.X86Mulsd, Intrinsic.X86Subss, Intrinsic.X86Subsd); + } + else + { + EmitScalarTernaryOpF32(context, (op1, op2, op3) => + { + return EmitSoftFloatCall(context, nameof(SoftFloat32.FPMulSub), op1, op2, op3); + }); + } + } + public static void Vmov_S(ArmEmitterContext context) { if (Optimizations.FastFP && Optimizations.UseSse2) @@ -586,7 +618,8 @@ namespace ARMeilleure.Instructions { EmitScalarTernaryOpF32(context, (op1, op2, op3) => { - return EmitSoftFloatCall(context, nameof(SoftFloat32.FPMulAdd), op1, op2, op3); + Operand res = EmitSoftFloatCall(context, nameof(SoftFloat32.FPMul), op2, op3); + return EmitSoftFloatCall(context, nameof(SoftFloat32.FPAdd), op1, res); }); } } @@ -657,7 +690,8 @@ namespace ARMeilleure.Instructions { EmitScalarTernaryOpF32(context, (op1, op2, op3) => { - return EmitSoftFloatCall(context, nameof(SoftFloat32.FPMulSub), op1, op2, op3); + Operand res = EmitSoftFloatCall(context, nameof(SoftFloat32.FPMul), op2, op3); + return EmitSoftFloatCall(context, nameof(SoftFloat32.FPSub), op1, res); }); } } diff --git a/ARMeilleure/Instructions/InstEmitSimdCvt32.cs b/ARMeilleure/Instructions/InstEmitSimdCvt32.cs index 00b8ffd63..e4efea709 100644 --- a/ARMeilleure/Instructions/InstEmitSimdCvt32.cs +++ b/ARMeilleure/Instructions/InstEmitSimdCvt32.cs @@ -139,6 +139,7 @@ namespace ARMeilleure.Instructions } } + // VCVT (floating-point to integer, floating-point) | VCVT (integer to floating-point, floating-point). public static void Vcvt_FI(ArmEmitterContext context) { OpCode32SimdCvtFI op = (OpCode32SimdCvtFI)context.CurrOp; @@ -236,13 +237,14 @@ namespace ARMeilleure.Instructions return roundMode; } - public static void Vcvt_R(ArmEmitterContext context) + // VCVTA/M/N/P (floating-point). + public static void Vcvt_RM(ArmEmitterContext context) { - OpCode32SimdCvtFI op = (OpCode32SimdCvtFI)context.CurrOp; + OpCode32SimdCvtFI op = (OpCode32SimdCvtFI)context.CurrOp; // toInteger == true (opCode<18> == 1 => Opc2<2> == 1). OperandType floatSize = op.RegisterSize == RegisterSize.Int64 ? OperandType.FP64 : OperandType.FP32; - bool unsigned = (op.Opc & 1) == 0; + bool unsigned = op.Opc == 0; int rm = op.Opc2 & 3; if (Optimizations.UseSse41 && rm != 0b00) @@ -277,9 +279,10 @@ namespace ARMeilleure.Instructions } } + // VRINTA/M/N/P (floating-point). public static void Vrint_RM(ArmEmitterContext context) { - OpCode32SimdCvtFI op = (OpCode32SimdCvtFI)context.CurrOp; + OpCode32SimdS op = (OpCode32SimdS)context.CurrOp; OperandType floatSize = op.RegisterSize == RegisterSize.Int64 ? OperandType.FP64 : OperandType.FP32; @@ -320,9 +323,10 @@ namespace ARMeilleure.Instructions } } + // VRINTZ (floating-point). public static void Vrint_Z(ArmEmitterContext context) { - IOpCodeSimd op = (IOpCodeSimd)context.CurrOp; + OpCode32SimdS op = (OpCode32SimdS)context.CurrOp; if (Optimizations.UseSse2) { @@ -355,7 +359,7 @@ namespace ARMeilleure.Instructions private static void EmitSse41ConvertInt32(ArmEmitterContext context, FPRoundingMode roundMode, bool signed) { // A port of the similar round function in InstEmitSimdCvt. - OpCode32SimdS op = (OpCode32SimdS)context.CurrOp; + OpCode32SimdCvtFI op = (OpCode32SimdCvtFI)context.CurrOp; bool doubleSize = (op.Size & 1) != 0; int shift = doubleSize ? 1 : 2; diff --git a/ARMeilleure/Instructions/InstEmitSimdHelper32.cs b/ARMeilleure/Instructions/InstEmitSimdHelper32.cs index e045c6015..a962c0fc9 100644 --- a/ARMeilleure/Instructions/InstEmitSimdHelper32.cs +++ b/ARMeilleure/Instructions/InstEmitSimdHelper32.cs @@ -906,7 +906,7 @@ namespace ARMeilleure.Instructions OpCode32SimdRegS op = (OpCode32SimdRegS)context.CurrOp; bool doubleSize = (op.Size & 1) != 0; - int shift = doubleSize ? 1 : 2; + Intrinsic inst1 = doubleSize ? inst64pt1 : inst32pt1; Intrinsic inst2 = doubleSize ? inst64pt2 : inst32pt2; diff --git a/ARMeilleure/Instructions/InstEmitSimdMove32.cs b/ARMeilleure/Instructions/InstEmitSimdMove32.cs index b484381fe..522922424 100644 --- a/ARMeilleure/Instructions/InstEmitSimdMove32.cs +++ b/ARMeilleure/Instructions/InstEmitSimdMove32.cs @@ -559,7 +559,7 @@ namespace ARMeilleure.Instructions } } - public static void EmitVectorShuffleOpSimd32(ArmEmitterContext context, Func shuffleFunc) + private static void EmitVectorShuffleOpSimd32(ArmEmitterContext context, Func shuffleFunc) { OpCode32Simd op = (OpCode32Simd)context.CurrOp; diff --git a/ARMeilleure/Instructions/InstName.cs b/ARMeilleure/Instructions/InstName.cs index d72830292..9e820f6b6 100644 --- a/ARMeilleure/Instructions/InstName.cs +++ b/ARMeilleure/Instructions/InstName.cs @@ -563,6 +563,8 @@ namespace ARMeilleure.Instructions Vdup, Veor, Vext, + Vfma, + Vfms, Vld1, Vld2, Vld3, diff --git a/ARMeilleure/Translation/PTC/Ptc.cs b/ARMeilleure/Translation/PTC/Ptc.cs index 2ff98f853..ccb3f7054 100644 --- a/ARMeilleure/Translation/PTC/Ptc.cs +++ b/ARMeilleure/Translation/PTC/Ptc.cs @@ -20,7 +20,7 @@ namespace ARMeilleure.Translation.PTC { private const string HeaderMagic = "PTChd"; - private const int InternalVersion = 20; //! To be incremented manually for each change to the ARMeilleure project. + private const int InternalVersion = 1471; //! To be incremented manually for each change to the ARMeilleure project. private const string BaseDir = "Ryujinx"; diff --git a/Ryujinx.Memory.Tests/Ryujinx.Memory.Tests.csproj b/Ryujinx.Memory.Tests/Ryujinx.Memory.Tests.csproj index bc916ea2a..c7c151857 100644 --- a/Ryujinx.Memory.Tests/Ryujinx.Memory.Tests.csproj +++ b/Ryujinx.Memory.Tests/Ryujinx.Memory.Tests.csproj @@ -7,9 +7,9 @@ - - - + + + diff --git a/Ryujinx.Tests.Unicorn/Ryujinx.Tests.Unicorn.csproj b/Ryujinx.Tests.Unicorn/Ryujinx.Tests.Unicorn.csproj index 600f0b03b..efd84a084 100644 --- a/Ryujinx.Tests.Unicorn/Ryujinx.Tests.Unicorn.csproj +++ b/Ryujinx.Tests.Unicorn/Ryujinx.Tests.Unicorn.csproj @@ -22,8 +22,4 @@ false - - - - diff --git a/Ryujinx.Tests/Cpu/CpuTest.cs b/Ryujinx.Tests/Cpu/CpuTest.cs index 12afcfc7f..e0778e058 100644 --- a/Ryujinx.Tests/Cpu/CpuTest.cs +++ b/Ryujinx.Tests/Cpu/CpuTest.cs @@ -205,7 +205,9 @@ namespace Ryujinx.Tests.Cpu { if (Ignore_FpcrFz_FpcrDn) { +#pragma warning disable CS0162 fpcr &= ~((1 << (int)Fpcr.Fz) | (1 << (int)Fpcr.Dn)); +#pragma warning restore CS0162 } Opcode(opcode); @@ -319,7 +321,9 @@ namespace Ryujinx.Tests.Cpu if (IgnoreAllExcept_FpsrQc) { +#pragma warning disable CS0162 fpsrMask &= Fpsr.Qc; +#pragma warning restore CS0162 } if (fpSkips != FpSkips.None) diff --git a/Ryujinx.Tests/Cpu/CpuTest32.cs b/Ryujinx.Tests/Cpu/CpuTest32.cs index 3f457193a..ca9668407 100644 --- a/Ryujinx.Tests/Cpu/CpuTest32.cs +++ b/Ryujinx.Tests/Cpu/CpuTest32.cs @@ -470,6 +470,11 @@ namespace Ryujinx.Tests.Cpu protected static V128 MakeVectorE0E1(ulong e0, ulong e1) => new V128(e0, e1); + protected static V128 MakeVectorE0E1E2E3(uint e0, uint e1, uint e2, uint e3) + { + return new V128(e0, e1, e2, e3); + } + protected static ulong GetVectorE0(V128 vector) => vector.Extract(0); protected static ulong GetVectorE1(V128 vector) => vector.Extract(1); diff --git a/Ryujinx.Tests/Cpu/CpuTestSimdCvt32.cs b/Ryujinx.Tests/Cpu/CpuTestSimdCvt32.cs new file mode 100644 index 000000000..c25f2fa24 --- /dev/null +++ b/Ryujinx.Tests/Cpu/CpuTestSimdCvt32.cs @@ -0,0 +1,220 @@ +#define SimdCvt32 + +using ARMeilleure.State; +using NUnit.Framework; +using System.Collections.Generic; + +namespace Ryujinx.Tests.Cpu +{ + [Category("SimdCvt32")] + public sealed class CpuTestSimdCvt32 : CpuTest32 + { +#if SimdCvt32 + +#region "ValueSource (Opcodes)" +#endregion + +#region "ValueSource (Types)" + private static uint[] _1S_() + { + return new uint[] { 0x00000000u, 0x7FFFFFFFu, + 0x80000000u, 0xFFFFFFFFu }; + } + + private static IEnumerable _1S_F_() + { + yield return 0xFF7FFFFFu; // -Max Normal (float.MinValue) + yield return 0x80800000u; // -Min Normal + yield return 0x807FFFFFu; // -Max Subnormal + yield return 0x80000001u; // -Min Subnormal (-float.Epsilon) + yield return 0x7F7FFFFFu; // +Max Normal (float.MaxValue) + yield return 0x00800000u; // +Min Normal + yield return 0x007FFFFFu; // +Max Subnormal + yield return 0x00000001u; // +Min Subnormal (float.Epsilon) + + if (!NoZeros) + { + yield return 0x80000000u; // -Zero + yield return 0x00000000u; // +Zero + } + + if (!NoInfs) + { + yield return 0xFF800000u; // -Infinity + yield return 0x7F800000u; // +Infinity + } + + if (!NoNaNs) + { + yield return 0xFFC00000u; // -QNaN (all zeros payload) (float.NaN) + yield return 0xFFBFFFFFu; // -SNaN (all ones payload) + yield return 0x7FC00000u; // +QNaN (all zeros payload) (-float.NaN) (DefaultNaN) + yield return 0x7FBFFFFFu; // +SNaN (all ones payload) + } + + for (int cnt = 1; cnt <= RndCnt; cnt++) + { + yield return GenNormalS(); + yield return GenSubnormalS(); + } + } + + private static IEnumerable _1D_F_() + { + yield return 0xFFEFFFFFFFFFFFFFul; // -Max Normal (double.MinValue) + yield return 0x8010000000000000ul; // -Min Normal + yield return 0x800FFFFFFFFFFFFFul; // -Max Subnormal + yield return 0x8000000000000001ul; // -Min Subnormal (-double.Epsilon) + yield return 0x7FEFFFFFFFFFFFFFul; // +Max Normal (double.MaxValue) + yield return 0x0010000000000000ul; // +Min Normal + yield return 0x000FFFFFFFFFFFFFul; // +Max Subnormal + yield return 0x0000000000000001ul; // +Min Subnormal (double.Epsilon) + + if (!NoZeros) + { + yield return 0x8000000000000000ul; // -Zero + yield return 0x0000000000000000ul; // +Zero + } + + if (!NoInfs) + { + yield return 0xFFF0000000000000ul; // -Infinity + yield return 0x7FF0000000000000ul; // +Infinity + } + + if (!NoNaNs) + { + yield return 0xFFF8000000000000ul; // -QNaN (all zeros payload) (double.NaN) + yield return 0xFFF7FFFFFFFFFFFFul; // -SNaN (all ones payload) + yield return 0x7FF8000000000000ul; // +QNaN (all zeros payload) (-double.NaN) (DefaultNaN) + yield return 0x7FF7FFFFFFFFFFFFul; // +SNaN (all ones payload) + } + + for (int cnt = 1; cnt <= RndCnt; cnt++) + { + yield return GenNormalD(); + yield return GenSubnormalD(); + } + } +#endregion + + private const int RndCnt = 2; + + private static readonly bool NoZeros = false; + private static readonly bool NoInfs = false; + private static readonly bool NoNaNs = false; + + [Explicit] + [Test, Pairwise, Description("VCVT.
.F32 , ")] + public void Vcvt_F32_I32([Values(0u, 1u, 2u, 3u)] uint rd, + [Values(0u, 1u, 2u, 3u)] uint rm, + [ValueSource(nameof(_1S_F_))] uint s0, + [ValueSource(nameof(_1S_F_))] uint s1, + [ValueSource(nameof(_1S_F_))] uint s2, + [ValueSource(nameof(_1S_F_))] uint s3, + [Values] bool unsigned) // + { + uint opcode = 0xeebc0ac0u; // VCVT.U32.F32 S0, S0 + + if (!unsigned) + { + opcode |= 1 << 16; // opc2<0> + } + + opcode |= ((rd & 0x1e) << 11) | ((rd & 0x1) << 22); + opcode |= ((rm & 0x1e) >> 1) | ((rm & 0x1) << 5); + + V128 v0 = MakeVectorE0E1E2E3(s0, s1, s2, s3); + + SingleOpcode(opcode, v0: v0); + + CompareAgainstUnicorn(); + } + + [Explicit] + [Test, Pairwise, Description("VCVT.
.F64 , ")] + public void Vcvt_F64_I32([Values(0u, 1u, 2u, 3u)] uint rd, + [Values(0u, 1u)] uint rm, + [ValueSource(nameof(_1D_F_))] ulong d0, + [ValueSource(nameof(_1D_F_))] ulong d1, + [Values] bool unsigned) // + { + uint opcode = 0xeebc0bc0u; // VCVT.U32.F64 S0, D0 + + if (!unsigned) + { + opcode |= 1 << 16; // opc2<0> + } + + opcode |= ((rd & 0x1e) << 11) | ((rd & 0x1) << 22); + opcode |= ((rm & 0xf) << 0) | ((rm & 0x10) << 1); + + V128 v0 = MakeVectorE0E1(d0, d1); + + SingleOpcode(opcode, v0: v0); + + CompareAgainstUnicorn(); + } + + [Explicit] + [Test, Pairwise, Description("VCVT.F32.
, ")] + public void Vcvt_I32_F32([Values(0u, 1u, 2u, 3u)] uint rd, + [Values(0u, 1u, 2u, 3u)] uint rm, + [ValueSource(nameof(_1S_))] [Random(RndCnt)] uint s0, + [ValueSource(nameof(_1S_))] [Random(RndCnt)] uint s1, + [ValueSource(nameof(_1S_))] [Random(RndCnt)] uint s2, + [ValueSource(nameof(_1S_))] [Random(RndCnt)] uint s3, + [Values] bool unsigned, // + [Values(RMode.Rn)] RMode rMode) + { + uint opcode = 0xeeb80a40u; // VCVT.F32.U32 S0, S0 + + if (!unsigned) + { + opcode |= 1 << 7; // op + } + + opcode |= ((rm & 0x1e) >> 1) | ((rm & 0x1) << 5); + opcode |= ((rd & 0x1e) << 11) | ((rd & 0x1) << 22); + + V128 v0 = MakeVectorE0E1E2E3(s0, s1, s2, s3); + + int fpscr = (int)rMode << (int)Fpcr.RMode; + + SingleOpcode(opcode, v0: v0, fpscr: fpscr); + + CompareAgainstUnicorn(); + } + + [Explicit] + [Test, Pairwise, Description("VCVT.F64.
, ")] + public void Vcvt_I32_F64([Values(0u, 1u)] uint rd, + [Values(0u, 1u, 2u, 3u)] uint rm, + [ValueSource(nameof(_1S_))] [Random(RndCnt)] uint s0, + [ValueSource(nameof(_1S_))] [Random(RndCnt)] uint s1, + [ValueSource(nameof(_1S_))] [Random(RndCnt)] uint s2, + [ValueSource(nameof(_1S_))] [Random(RndCnt)] uint s3, + [Values] bool unsigned, // + [Values(RMode.Rn)] RMode rMode) + { + uint opcode = 0xeeb80b40u; // VCVT.F64.U32 D0, S0 + + if (!unsigned) + { + opcode |= 1 << 7; // op + } + + opcode |= ((rm & 0x1e) >> 1) | ((rm & 0x1) << 5); + opcode |= ((rd & 0xf) << 12) | ((rd & 0x10) << 18); + + V128 v0 = MakeVectorE0E1E2E3(s0, s1, s2, s3); + + int fpscr = (int)rMode << (int)Fpcr.RMode; + + SingleOpcode(opcode, v0: v0, fpscr: fpscr); + + CompareAgainstUnicorn(); + } +#endif + } +} diff --git a/Ryujinx.Tests/Ryujinx.Tests.csproj b/Ryujinx.Tests/Ryujinx.Tests.csproj index 67be33c4a..ff8e855ed 100644 --- a/Ryujinx.Tests/Ryujinx.Tests.csproj +++ b/Ryujinx.Tests/Ryujinx.Tests.csproj @@ -28,9 +28,9 @@ - + - +