diff --git a/ChocolArm64/AOpCodeTable.cs b/ChocolArm64/AOpCodeTable.cs index aa8220e38d..f473af43aa 100644 --- a/ChocolArm64/AOpCodeTable.cs +++ b/ChocolArm64/AOpCodeTable.cs @@ -374,6 +374,8 @@ namespace ChocolArm64 SetA64("0x001110<<1xxxxx100000xxxxxxxxxx", AInstEmit.Smlal_V, typeof(AOpCodeSimdReg)); SetA64("0x001110<<1xxxxx101000xxxxxxxxxx", AInstEmit.Smlsl_V, typeof(AOpCodeSimdReg)); SetA64("0x001110<<1xxxxx110000xxxxxxxxxx", AInstEmit.Smull_V, typeof(AOpCodeSimdReg)); + SetA64("01011110xx1xxxxx000011xxxxxxxxxx", AInstEmit.Sqadd_S, typeof(AOpCodeSimdReg)); + SetA64("0>001110<<1xxxxx000011xxxxxxxxxx", AInstEmit.Sqadd_V, typeof(AOpCodeSimdReg)); SetA64("0x00111100>>>xxx100111xxxxxxxxxx", AInstEmit.Sqrshrn_V, typeof(AOpCodeSimdShImm)); SetA64("01011110<<100001010010xxxxxxxxxx", AInstEmit.Sqxtn_S, typeof(AOpCodeSimd)); SetA64("0x001110<<100001010010xxxxxxxxxx", AInstEmit.Sqxtn_V, typeof(AOpCodeSimd)); @@ -423,6 +425,8 @@ namespace ChocolArm64 SetA64("0x101110<<1xxxxx101011xxxxxxxxxx", AInstEmit.Uminp_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)); + SetA64("0>101110<<1xxxxx000011xxxxxxxxxx", AInstEmit.Uqadd_V, typeof(AOpCodeSimdReg)); SetA64("01111110<<100001010010xxxxxxxxxx", AInstEmit.Uqxtn_S, typeof(AOpCodeSimd)); SetA64("0x101110<<100001010010xxxxxxxxxx", AInstEmit.Uqxtn_V, typeof(AOpCodeSimd)); SetA64("0>101110<<1xxxxx010001xxxxxxxxxx", AInstEmit.Ushl_V, typeof(AOpCodeSimdReg)); diff --git a/ChocolArm64/Instruction/AInstEmitSimdArithmetic.cs b/ChocolArm64/Instruction/AInstEmitSimdArithmetic.cs index 8e7418611e..fbe1c5342e 100644 --- a/ChocolArm64/Instruction/AInstEmitSimdArithmetic.cs +++ b/ChocolArm64/Instruction/AInstEmitSimdArithmetic.cs @@ -1052,24 +1052,34 @@ namespace ChocolArm64.Instruction EmitVectorWidenRnRmBinaryOpSx(Context, () => Context.Emit(OpCodes.Mul)); } + public static void Sqadd_S(AILEmitterCtx Context) + { + EmitScalarBinarySaturatingOpSxSx(Context, () => Context.Emit(OpCodes.Add)); + } + + public static void Sqadd_V(AILEmitterCtx Context) + { + EmitVectorBinarySaturatingOpSxSx(Context, () => Context.Emit(OpCodes.Add)); + } + public static void Sqxtn_S(AILEmitterCtx Context) { - EmitScalarSaturatingNarrowOpSxSx(Context, () => { }); + EmitScalarUnarySaturatingNarrowOpSxSx(Context, () => { }); } public static void Sqxtn_V(AILEmitterCtx Context) { - EmitVectorSaturatingNarrowOpSxSx(Context, () => { }); + EmitVectorUnarySaturatingNarrowOpSxSx(Context, () => { }); } public static void Sqxtun_S(AILEmitterCtx Context) { - EmitScalarSaturatingNarrowOpSxZx(Context, () => { }); + EmitScalarUnarySaturatingNarrowOpSxZx(Context, () => { }); } public static void Sqxtun_V(AILEmitterCtx Context) { - EmitVectorSaturatingNarrowOpSxZx(Context, () => { }); + EmitVectorUnarySaturatingNarrowOpSxZx(Context, () => { }); } public static void Ssubw_V(AILEmitterCtx Context) @@ -1221,14 +1231,24 @@ namespace ChocolArm64.Instruction EmitVectorWidenRnRmBinaryOpZx(Context, () => Context.Emit(OpCodes.Mul)); } + public static void Uqadd_S(AILEmitterCtx Context) + { + EmitScalarBinarySaturatingOpZxZx(Context, () => Context.Emit(OpCodes.Add)); + } + + public static void Uqadd_V(AILEmitterCtx Context) + { + EmitVectorBinarySaturatingOpZxZx(Context, () => Context.Emit(OpCodes.Add)); + } + public static void Uqxtn_S(AILEmitterCtx Context) { - EmitScalarSaturatingNarrowOpZxZx(Context, () => { }); + EmitScalarUnarySaturatingNarrowOpZxZx(Context, () => { }); } public static void Uqxtn_V(AILEmitterCtx Context) { - EmitVectorSaturatingNarrowOpZxZx(Context, () => { }); + EmitVectorUnarySaturatingNarrowOpZxZx(Context, () => { }); } public static void Usubw_V(AILEmitterCtx Context) diff --git a/ChocolArm64/Instruction/AInstEmitSimdHelper.cs b/ChocolArm64/Instruction/AInstEmitSimdHelper.cs index 0bd1a62926..22f356bc7b 100644 --- a/ChocolArm64/Instruction/AInstEmitSimdHelper.cs +++ b/ChocolArm64/Instruction/AInstEmitSimdHelper.cs @@ -781,50 +781,121 @@ namespace ChocolArm64.Instruction } } - public static void EmitScalarSaturatingNarrowOpSxSx(AILEmitterCtx Context, Action Emit) + [Flags] + public enum SaturatingFlags { - EmitSaturatingNarrowOp(Context, Emit, true, true, true); + None = 0, + + SignedSrc = 1 << 0, + SignedDst = 1 << 1, + Scalar = 1 << 2, + Narrow = 1 << 3, + Binary = 1 << 4, + + SxSxScalarUnary = SignedSrc | SignedDst | Scalar, + SxSxScalarNarrowUnary = SignedSrc | SignedDst | Scalar | Narrow, + SxZxScalarUnary = SignedSrc | Scalar, + SxZxScalarNarrowUnary = SignedSrc | Scalar | Narrow, + ZxZxScalarUnary = Scalar, + ZxZxScalarNarrowUnary = Scalar | Narrow, + + SxSxVectorUnary = SignedSrc | SignedDst, + SxSxVectorNarrowUnary = SignedSrc | SignedDst | Narrow, + SxZxVectorUnary = SignedSrc, + SxZxVectorNarrowUnary = SignedSrc | Narrow, + ZxZxVectorUnary = 0, + ZxZxVectorNarrowUnary = Narrow, + + SxSxScalarBinary = SignedSrc | SignedDst | Scalar | Binary, + SxSxScalarNarrowBinary = SignedSrc | SignedDst | Scalar | Narrow | Binary, + SxZxScalarBinary = SignedSrc | Scalar | Binary, + SxZxScalarNarrowBinary = SignedSrc | Scalar | Narrow | Binary, + ZxZxScalarBinary = Scalar | Binary, + ZxZxScalarNarrowBinary = Scalar | Narrow | Binary, + + SxSxVectorBinary = SignedSrc | SignedDst | Binary, + SxSxVectorNarrowBinary = SignedSrc | SignedDst | Narrow | Binary, + SxZxVectorBinary = SignedSrc | Binary, + SxZxVectorNarrowBinary = SignedSrc | Narrow | Binary, + ZxZxVectorBinary = Binary, + ZxZxVectorNarrowBinary = Narrow | Binary } - public static void EmitScalarSaturatingNarrowOpSxZx(AILEmitterCtx Context, Action Emit) + public static void EmitScalarBinarySaturatingOpSxSx(AILEmitterCtx Context, Action Emit) { - EmitSaturatingNarrowOp(Context, Emit, true, false, true); + EmitSaturatingOp(Context, Emit, SaturatingFlags.SxSxScalarBinary); } - public static void EmitScalarSaturatingNarrowOpZxZx(AILEmitterCtx Context, Action Emit) + public static void EmitScalarBinarySaturatingOpZxZx(AILEmitterCtx Context, Action Emit) { - EmitSaturatingNarrowOp(Context, Emit, false, false, true); + EmitSaturatingOp(Context, Emit, SaturatingFlags.ZxZxScalarBinary); } - public static void EmitVectorSaturatingNarrowOpSxSx(AILEmitterCtx Context, Action Emit) + public static void EmitVectorBinarySaturatingOpSxSx(AILEmitterCtx Context, Action Emit) { - EmitSaturatingNarrowOp(Context, Emit, true, true, false); + EmitSaturatingOp(Context, Emit, SaturatingFlags.SxSxVectorBinary); } - public static void EmitVectorSaturatingNarrowOpSxZx(AILEmitterCtx Context, Action Emit) + public static void EmitVectorBinarySaturatingOpZxZx(AILEmitterCtx Context, Action Emit) { - EmitSaturatingNarrowOp(Context, Emit, true, false, false); + EmitSaturatingOp(Context, Emit, SaturatingFlags.ZxZxVectorBinary); } - public static void EmitVectorSaturatingNarrowOpZxZx(AILEmitterCtx Context, Action Emit) + public static void EmitScalarUnarySaturatingNarrowOpSxSx(AILEmitterCtx Context, Action Emit) { - EmitSaturatingNarrowOp(Context, Emit, false, false, false); + EmitSaturatingOp(Context, Emit, SaturatingFlags.SxSxScalarNarrowUnary); } - public static void EmitSaturatingNarrowOp( + public static void EmitScalarUnarySaturatingNarrowOpSxZx(AILEmitterCtx Context, Action Emit) + { + EmitSaturatingOp(Context, Emit, SaturatingFlags.SxZxScalarNarrowUnary); + } + + public static void EmitScalarUnarySaturatingNarrowOpZxZx(AILEmitterCtx Context, Action Emit) + { + EmitSaturatingOp(Context, Emit, SaturatingFlags.ZxZxScalarNarrowUnary); + } + + public static void EmitVectorUnarySaturatingNarrowOpSxSx(AILEmitterCtx Context, Action Emit) + { + EmitSaturatingOp(Context, Emit, SaturatingFlags.SxSxVectorNarrowUnary); + } + + public static void EmitVectorUnarySaturatingNarrowOpSxZx(AILEmitterCtx Context, Action Emit) + { + EmitSaturatingOp(Context, Emit, SaturatingFlags.SxZxVectorNarrowUnary); + } + + public static void EmitVectorUnarySaturatingNarrowOpZxZx(AILEmitterCtx Context, Action Emit) + { + EmitSaturatingOp(Context, Emit, SaturatingFlags.ZxZxVectorNarrowUnary); + } + + public static void EmitSaturatingOp( AILEmitterCtx Context, Action Emit, - bool SignedSrc, - bool SignedDst, - bool Scalar) + SaturatingFlags Flags) { + bool SignedSrc = (Flags & SaturatingFlags.SignedSrc) != 0; + bool SignedDst = (Flags & SaturatingFlags.SignedDst) != 0; + bool Scalar = (Flags & SaturatingFlags.Scalar) != 0; + bool Narrow = (Flags & SaturatingFlags.Narrow) != 0; + bool Binary = (Flags & SaturatingFlags.Binary) != 0; + AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp; - int Elems = !Scalar ? 8 >> Op.Size : 1; + int Bytes = Op.GetBitsCount() >> 3; + + int Elems = !Scalar ? (Narrow ? 8 : Bytes) >> Op.Size : 1; int ESize = 8 << Op.Size; - int Part = !Scalar && (Op.RegisterSize == ARegisterSize.SIMD128) ? Elems : 0; + int Part = 0; + + if (Narrow) + { + Part = !Scalar && (Op.RegisterSize == ARegisterSize.SIMD128) ? Elems : 0; + } long TMaxValue = SignedDst ? (1 << (ESize - 1)) - 1 : (long)(~0UL >> (64 - ESize)); long TMinValue = SignedDst ? -((1 << (ESize - 1))) : 0; @@ -832,7 +903,7 @@ namespace ChocolArm64.Instruction Context.EmitLdc_I8(0L); Context.EmitSttmp(); - if (Part != 0) + if (Part != 0 && Narrow) { Context.EmitLdvec(Op.Rd); Context.EmitStvectmp(); @@ -843,7 +914,12 @@ namespace ChocolArm64.Instruction AILLabel LblLe = new AILLabel(); AILLabel LblGeEnd = new AILLabel(); - EmitVectorExtract(Context, Op.Rn, Index, Op.Size + 1, SignedSrc); + EmitVectorExtract(Context, Op.Rn, Index, Narrow ? Op.Size + 1 : Op.Size, SignedSrc); + + if (Binary) + { + EmitVectorExtract(Context, ((AOpCodeSimdReg)Op).Rm, Index, Narrow ? Op.Size + 1 : Op.Size, SignedSrc); + } Emit(); @@ -882,13 +958,13 @@ namespace ChocolArm64.Instruction EmitVectorZeroLowerTmp(Context); } - EmitVectorInsertTmp(Context, Part + Index, Op.Size); + EmitVectorInsertTmp(Context, Narrow ? Index + Part : Index, Op.Size); } Context.EmitLdvectmp(); Context.EmitStvec(Op.Rd); - if (Part == 0) + if ((Op.RegisterSize == ARegisterSize.SIMD64) || Scalar || (Part == 0 && Narrow)) { EmitVectorZeroUpper(Context, Op.Rd); } diff --git a/ChocolArm64/Instruction/AInstEmitSimdShift.cs b/ChocolArm64/Instruction/AInstEmitSimdShift.cs index 6f6b56068e..721b1c8eaf 100644 --- a/ChocolArm64/Instruction/AInstEmitSimdShift.cs +++ b/ChocolArm64/Instruction/AInstEmitSimdShift.cs @@ -100,7 +100,7 @@ namespace ChocolArm64.Instruction Context.Emit(OpCodes.Shr); }; - EmitVectorSaturatingNarrowOpSxSx(Context, Emit); + EmitVectorUnarySaturatingNarrowOpSxSx(Context, Emit); } public static void Srshr_V(AILEmitterCtx Context) diff --git a/Ryujinx.Tests/Cpu/CpuTestSimd.cs b/Ryujinx.Tests/Cpu/CpuTestSimd.cs index 82591edaee..08aa0fd348 100644 --- a/Ryujinx.Tests/Cpu/CpuTestSimd.cs +++ b/Ryujinx.Tests/Cpu/CpuTestSimd.cs @@ -1126,6 +1126,19 @@ namespace Ryujinx.Tests.Cpu }); } + [TestCase(0x00000001u, 0x7FFFFFFFu, 0x7FFFFFFFu, true)] + public void Sqadd_S(uint A, uint B, uint Result, bool Fpsr) + { + Vector128 V1 = MakeVectorE0(A); + Vector128 V2 = MakeVectorE0(B); + AThreadState ThreadState = SingleOpcode(0x5EA20C20, V1: V1, V2: V2); + Assert.Multiple(() => + { + Assert.AreEqual(Result, GetVectorE0(ThreadState.V0)); + Assert.AreEqual(((ThreadState.Fpsr >> 27) & 1) == 1, Fpsr); + }); + } + [Test, Description("SQXTN , ")] public void Sqxtn_S_HB_SH_DS([Values(0u)] uint Rd, [Values(1u, 0u)] uint Rn,