diff --git a/ChocolArm64/AOpCodeTable.cs b/ChocolArm64/AOpCodeTable.cs index 1f60be1c3..fb4763ef8 100644 --- a/ChocolArm64/AOpCodeTable.cs +++ b/ChocolArm64/AOpCodeTable.cs @@ -348,10 +348,15 @@ namespace ChocolArm64 SetA64("0x001110101xxxxx000111xxxxxxxxxx", AInstEmit.Orr_V, typeof(AOpCodeSimdReg)); SetA64("0x00111100000xxx<>>xxx100001xxxxxxxxxx", AInstEmit.Shrn_V, typeof(AOpCodeSimdShImm)); SetA64("0x1011110>>>>xxx010101xxxxxxxxxx", AInstEmit.Sli_V, typeof(AOpCodeSimdShImm)); SetA64("0x001110<<1xxxxx011001xxxxxxxxxx", AInstEmit.Smax_V, typeof(AOpCodeSimdReg)); + SetA64("0x001110<<1xxxxx101001xxxxxxxxxx", AInstEmit.Smaxp_V, typeof(AOpCodeSimdReg)); SetA64("0x001110<<1xxxxx011011xxxxxxxxxx", AInstEmit.Smin_V, typeof(AOpCodeSimdReg)); + SetA64("0x001110<<1xxxxx101011xxxxxxxxxx", AInstEmit.Sminp_V, typeof(AOpCodeSimdReg)); SetA64("0x001110<<1xxxxx100000xxxxxxxxxx", AInstEmit.Smlal_V, typeof(AOpCodeSimdReg)); SetA64("0x001110<<1xxxxx110000xxxxxxxxxx", AInstEmit.Smull_V, typeof(AOpCodeSimdReg)); SetA64("01011110<<100001010010xxxxxxxxxx", AInstEmit.Sqxtn_S, typeof(AOpCodeSimd)); @@ -390,6 +397,8 @@ namespace ChocolArm64 SetA64("0x001110000xxxxx0xx000xxxxxxxxxx", AInstEmit.Tbl_V, typeof(AOpCodeSimdTbl)); SetA64("0>001110<<0xxxxx001010xxxxxxxxxx", AInstEmit.Trn1_V, typeof(AOpCodeSimdReg)); SetA64("0>001110<<0xxxxx011010xxxxxxxxxx", AInstEmit.Trn2_V, typeof(AOpCodeSimdReg)); + SetA64("0x101110<<1xxxxx011111xxxxxxxxxx", AInstEmit.Uaba_V, typeof(AOpCodeSimdReg)); + SetA64("0x101110<<1xxxxx010100xxxxxxxxxx", AInstEmit.Uabal_V, typeof(AOpCodeSimdReg)); SetA64("0x101110<<1xxxxx011101xxxxxxxxxx", AInstEmit.Uabd_V, typeof(AOpCodeSimdReg)); SetA64("0x101110<<1xxxxx011100xxxxxxxxxx", AInstEmit.Uabdl_V, typeof(AOpCodeSimdReg)); SetA64("0x101110<<1xxxxx000000xxxxxxxxxx", AInstEmit.Uaddl_V, typeof(AOpCodeSimdReg)); @@ -400,6 +409,10 @@ namespace ChocolArm64 SetA64("011111100x100001110110xxxxxxxxxx", AInstEmit.Ucvtf_S, typeof(AOpCodeSimd)); SetA64("0x1011100x100001110110xxxxxxxxxx", AInstEmit.Ucvtf_V, typeof(AOpCodeSimd)); SetA64("0x101110<<1xxxxx000001xxxxxxxxxx", AInstEmit.Uhadd_V, typeof(AOpCodeSimdReg)); + SetA64("0x101110<<1xxxxx011001xxxxxxxxxx", AInstEmit.Umax_V, typeof(AOpCodeSimdReg)); + 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("0x001110000xxxxx001111xxxxxxxxxx", AInstEmit.Umov_S, typeof(AOpCodeSimdIns)); SetA64("0x101110<<1xxxxx110000xxxxxxxxxx", AInstEmit.Umull_V, typeof(AOpCodeSimdReg)); SetA64("01111110<<100001010010xxxxxxxxxx", AInstEmit.Uqxtn_S, typeof(AOpCodeSimd)); diff --git a/ChocolArm64/ChocolArm64.csproj b/ChocolArm64/ChocolArm64.csproj index c878bdac1..1156e361f 100644 --- a/ChocolArm64/ChocolArm64.csproj +++ b/ChocolArm64/ChocolArm64.csproj @@ -2,6 +2,7 @@ netcoreapp2.1 + win10-x64;osx-x64;linux-x64 diff --git a/ChocolArm64/Instruction/AInstEmitAlu.cs b/ChocolArm64/Instruction/AInstEmitAlu.cs index 0e546f7ab..490387e12 100644 --- a/ChocolArm64/Instruction/AInstEmitAlu.cs +++ b/ChocolArm64/Instruction/AInstEmitAlu.cs @@ -50,8 +50,6 @@ namespace ChocolArm64.Instruction public static void Adds(AILEmitterCtx Context) { - Context.TryOptMarkCondWithoutCmp(); - EmitDataLoadOpers(Context); Context.Emit(OpCodes.Add); diff --git a/ChocolArm64/Instruction/AInstEmitSimdArithmetic.cs b/ChocolArm64/Instruction/AInstEmitSimdArithmetic.cs index efd3cc6e8..b96b71be4 100644 --- a/ChocolArm64/Instruction/AInstEmitSimdArithmetic.cs +++ b/ChocolArm64/Instruction/AInstEmitSimdArithmetic.cs @@ -22,19 +22,6 @@ namespace ChocolArm64.Instruction EmitVectorUnaryOpSx(Context, () => EmitAbs(Context)); } - private static void EmitAbs(AILEmitterCtx Context) - { - AILLabel LblTrue = new AILLabel(); - - Context.Emit(OpCodes.Dup); - Context.Emit(OpCodes.Ldc_I4_0); - Context.Emit(OpCodes.Bge_S, LblTrue); - - Context.Emit(OpCodes.Neg); - - Context.MarkLabel(LblTrue); - } - public static void Add_S(AILEmitterCtx Context) { EmitScalarBinaryOpZx(Context, () => Context.Emit(OpCodes.Add)); @@ -71,32 +58,7 @@ namespace ChocolArm64.Instruction public static void Addp_V(AILEmitterCtx Context) { - AOpCodeSimdReg Op = (AOpCodeSimdReg)Context.CurrOp; - - int Bytes = Context.CurrOp.GetBitsCount() >> 3; - - int Elems = Bytes >> Op.Size; - int Half = Elems >> 1; - - for (int Index = 0; Index < Elems; Index++) - { - int Elem = (Index & (Half - 1)) << 1; - - EmitVectorExtractZx(Context, Index < Half ? Op.Rn : Op.Rm, Elem + 0, Op.Size); - EmitVectorExtractZx(Context, Index < Half ? Op.Rn : Op.Rm, Elem + 1, Op.Size); - - Context.Emit(OpCodes.Add); - - EmitVectorInsertTmp(Context, Index, Op.Size); - } - - Context.EmitLdvectmp(); - Context.EmitStvec(Op.Rd); - - if (Op.RegisterSize == ARegisterSize.SIMD64) - { - EmitVectorZeroUpper(Context, Op.Rd); - } + EmitVectorPairwiseOpZx(Context, () => Context.Emit(OpCodes.Add)); } public static void Addv_V(AILEmitterCtx Context) @@ -164,9 +126,9 @@ namespace ChocolArm64.Instruction { EmitVectorExtractZx(Context, Op.Rn, Index, 0); - Context.Emit(OpCodes.Conv_U1); + Context.Emit(OpCodes.Conv_U4); - AVectorHelper.EmitCall(Context, nameof(AVectorHelper.CountSetBits8)); + ASoftFallback.EmitCall(Context, nameof(ASoftFallback.CountSetBits8)); Context.Emit(OpCodes.Conv_U8); @@ -179,6 +141,19 @@ namespace ChocolArm64.Instruction } } + private static void EmitAbs(AILEmitterCtx Context) + { + AILLabel LblTrue = new AILLabel(); + + Context.Emit(OpCodes.Dup); + Context.Emit(OpCodes.Ldc_I4_0); + Context.Emit(OpCodes.Bge_S, LblTrue); + + Context.Emit(OpCodes.Neg); + + Context.MarkLabel(LblTrue); + } + private static void EmitHighNarrow(AILEmitterCtx Context, Action Emit, bool Round) { AOpCodeSimdReg Op = (AOpCodeSimdReg)Context.CurrOp; @@ -188,6 +163,8 @@ namespace ChocolArm64.Instruction int Part = Op.RegisterSize == ARegisterSize.SIMD128 ? Elems : 0; + long RoundConst = 1L << (ESize - 1); + for (int Index = 0; Index < Elems; Index++) { EmitVectorExtractZx(Context, Op.Rn, Index, Op.Size + 1); @@ -197,7 +174,7 @@ namespace ChocolArm64.Instruction if (Round) { - Context.EmitLdc_I8(1L << (ESize - 1)); + Context.EmitLdc_I8(RoundConst); Context.Emit(OpCodes.Add); } @@ -220,11 +197,11 @@ namespace ChocolArm64.Instruction int Elems = (!Scalar ? 8 >> Op.Size : 1); int ESize = 8 << Op.Size; + int Part = (!Scalar & (Op.RegisterSize == ARegisterSize.SIMD128) ? Elems : 0); + int TMaxValue = (SignedDst ? (1 << (ESize - 1)) - 1 : (int)((1L << ESize) - 1L)); int TMinValue = (SignedDst ? -((1 << (ESize - 1))) : 0); - int Part = (!Scalar & (Op.RegisterSize == ARegisterSize.SIMD128) ? Elems : 0); - Context.EmitLdc_I8(0L); Context.EmitSttmp(); @@ -1107,6 +1084,46 @@ namespace ChocolArm64.Instruction EmitHighNarrow(Context, () => Context.Emit(OpCodes.Sub), Round: true); } + public static void Saba_V(AILEmitterCtx Context) + { + EmitVectorTernaryOpSx(Context, () => + { + Context.Emit(OpCodes.Sub); + EmitAbs(Context); + + Context.Emit(OpCodes.Add); + }); + } + + public static void Sabal_V(AILEmitterCtx Context) + { + EmitVectorWidenRnRmTernaryOpSx(Context, () => + { + Context.Emit(OpCodes.Sub); + EmitAbs(Context); + + Context.Emit(OpCodes.Add); + }); + } + + public static void Sabd_V(AILEmitterCtx Context) + { + EmitVectorBinaryOpSx(Context, () => + { + Context.Emit(OpCodes.Sub); + EmitAbs(Context); + }); + } + + public static void Sabdl_V(AILEmitterCtx Context) + { + EmitVectorWidenRnRmBinaryOpSx(Context, () => + { + Context.Emit(OpCodes.Sub); + EmitAbs(Context); + }); + } + public static void Saddw_V(AILEmitterCtx Context) { EmitVectorWidenRmBinaryOpSx(Context, () => Context.Emit(OpCodes.Add)); @@ -1121,6 +1138,15 @@ namespace ChocolArm64.Instruction EmitVectorBinaryOpSx(Context, () => Context.EmitCall(MthdInfo)); } + public static void Smaxp_V(AILEmitterCtx Context) + { + Type[] Types = new Type[] { typeof(long), typeof(long) }; + + MethodInfo MthdInfo = typeof(Math).GetMethod(nameof(Math.Max), Types); + + EmitVectorPairwiseOpSx(Context, () => Context.EmitCall(MthdInfo)); + } + public static void Smin_V(AILEmitterCtx Context) { Type[] Types = new Type[] { typeof(long), typeof(long) }; @@ -1130,6 +1156,15 @@ namespace ChocolArm64.Instruction EmitVectorBinaryOpSx(Context, () => Context.EmitCall(MthdInfo)); } + public static void Sminp_V(AILEmitterCtx Context) + { + Type[] Types = new Type[] { typeof(long), typeof(long) }; + + MethodInfo MthdInfo = typeof(Math).GetMethod(nameof(Math.Min), Types); + + EmitVectorPairwiseOpSx(Context, () => Context.EmitCall(MthdInfo)); + } + public static void Smlal_V(AILEmitterCtx Context) { EmitVectorWidenRnRmTernaryOpSx(Context, () => @@ -1186,23 +1221,44 @@ namespace ChocolArm64.Instruction EmitHighNarrow(Context, () => Context.Emit(OpCodes.Sub), Round: false); } + public static void Uaba_V(AILEmitterCtx Context) + { + EmitVectorTernaryOpZx(Context, () => + { + Context.Emit(OpCodes.Sub); + EmitAbs(Context); + + Context.Emit(OpCodes.Add); + }); + } + + public static void Uabal_V(AILEmitterCtx Context) + { + EmitVectorWidenRnRmTernaryOpZx(Context, () => + { + Context.Emit(OpCodes.Sub); + EmitAbs(Context); + + Context.Emit(OpCodes.Add); + }); + } + public static void Uabd_V(AILEmitterCtx Context) { - EmitVectorBinaryOpZx(Context, () => EmitAbd(Context)); + EmitVectorBinaryOpZx(Context, () => + { + Context.Emit(OpCodes.Sub); + EmitAbs(Context); + }); } public static void Uabdl_V(AILEmitterCtx Context) { - EmitVectorWidenRnRmBinaryOpZx(Context, () => EmitAbd(Context)); - } - - private static void EmitAbd(AILEmitterCtx Context) - { - Context.Emit(OpCodes.Sub); - - Type[] Types = new Type[] { typeof(long) }; - - Context.EmitCall(typeof(Math).GetMethod(nameof(Math.Abs), Types)); + EmitVectorWidenRnRmBinaryOpZx(Context, () => + { + Context.Emit(OpCodes.Sub); + EmitAbs(Context); + }); } public static void Uaddl_V(AILEmitterCtx Context) @@ -1245,6 +1301,42 @@ namespace ChocolArm64.Instruction }); } + public static void Umin_V(AILEmitterCtx Context) + { + Type[] Types = new Type[] { typeof(ulong), typeof(ulong) }; + + MethodInfo MthdInfo = typeof(Math).GetMethod(nameof(Math.Min), Types); + + EmitVectorBinaryOpZx(Context, () => Context.EmitCall(MthdInfo)); + } + + public static void Uminp_V(AILEmitterCtx Context) + { + Type[] Types = new Type[] { typeof(ulong), typeof(ulong) }; + + MethodInfo MthdInfo = typeof(Math).GetMethod(nameof(Math.Min), Types); + + EmitVectorPairwiseOpZx(Context, () => Context.EmitCall(MthdInfo)); + } + + public static void Umax_V(AILEmitterCtx Context) + { + Type[] Types = new Type[] { typeof(ulong), typeof(ulong) }; + + MethodInfo MthdInfo = typeof(Math).GetMethod(nameof(Math.Max), Types); + + EmitVectorBinaryOpZx(Context, () => Context.EmitCall(MthdInfo)); + } + + public static void Umaxp_V(AILEmitterCtx Context) + { + Type[] Types = new Type[] { typeof(ulong), typeof(ulong) }; + + MethodInfo MthdInfo = typeof(Math).GetMethod(nameof(Math.Max), Types); + + EmitVectorPairwiseOpZx(Context, () => Context.EmitCall(MthdInfo)); + } + public static void Umull_V(AILEmitterCtx Context) { EmitVectorWidenRnRmBinaryOpZx(Context, () => Context.Emit(OpCodes.Mul)); diff --git a/ChocolArm64/Instruction/AInstEmitSimdHelper.cs b/ChocolArm64/Instruction/AInstEmitSimdHelper.cs index bca456497..0f6ea42ce 100644 --- a/ChocolArm64/Instruction/AInstEmitSimdHelper.cs +++ b/ChocolArm64/Instruction/AInstEmitSimdHelper.cs @@ -132,12 +132,12 @@ namespace ChocolArm64.Instruction if (SizeF == 0) { - Type = typeof(Sse); + Type = typeof(Sse); BaseType = typeof(Vector128); } else /* if (SizeF == 1) */ { - Type = typeof(Sse2); + Type = typeof(Sse2); BaseType = typeof(Vector128); } @@ -483,6 +483,11 @@ namespace ChocolArm64.Instruction EmitVectorOp(Context, Emit, OperFlags.RnRm, true); } + public static void EmitVectorTernaryOpSx(AILEmitterCtx Context, Action Emit) + { + EmitVectorOp(Context, Emit, OperFlags.RdRnRm, true); + } + public static void EmitVectorUnaryOpZx(AILEmitterCtx Context, Action Emit) { EmitVectorOp(Context, Emit, OperFlags.Rn, false); @@ -704,6 +709,46 @@ namespace ChocolArm64.Instruction Context.EmitStvec(Op.Rd); } + public static void EmitVectorPairwiseOpSx(AILEmitterCtx Context, Action Emit) + { + EmitVectorPairwiseOp(Context, Emit, true); + } + + public static void EmitVectorPairwiseOpZx(AILEmitterCtx Context, Action Emit) + { + EmitVectorPairwiseOp(Context, Emit, false); + } + + private static void EmitVectorPairwiseOp(AILEmitterCtx Context, Action Emit, bool Signed) + { + AOpCodeSimdReg Op = (AOpCodeSimdReg)Context.CurrOp; + + int Bytes = Context.CurrOp.GetBitsCount() >> 3; + + int Elems = Bytes >> Op.Size; + int Half = Elems >> 1; + + for (int Index = 0; Index < Elems; Index++) + { + int Elem = (Index & (Half - 1)) << 1; + + EmitVectorExtract(Context, Index < Half ? Op.Rn : Op.Rm, Elem + 0, Op.Size, Signed); + EmitVectorExtract(Context, Index < Half ? Op.Rn : Op.Rm, Elem + 1, Op.Size, Signed); + + Emit(); + + EmitVectorInsertTmp(Context, Index, Op.Size); + } + + Context.EmitLdvectmp(); + Context.EmitStvec(Op.Rd); + + if (Op.RegisterSize == ARegisterSize.SIMD64) + { + EmitVectorZeroUpper(Context, Op.Rd); + } + } + public static void EmitScalarSet(AILEmitterCtx Context, int Reg, int Size) { EmitVectorZeroAll(Context, Reg); diff --git a/ChocolArm64/Instruction/AInstEmitSimdLogical.cs b/ChocolArm64/Instruction/AInstEmitSimdLogical.cs index 163151f84..8475a8a47 100644 --- a/ChocolArm64/Instruction/AInstEmitSimdLogical.cs +++ b/ChocolArm64/Instruction/AInstEmitSimdLogical.cs @@ -56,8 +56,9 @@ namespace ChocolArm64.Instruction AOpCodeSimdReg Op = (AOpCodeSimdReg)Context.CurrOp; int Bytes = Context.CurrOp.GetBitsCount() >> 3; + int Elems = Bytes >> Op.Size; - for (int Index = 0; Index < (Bytes >> Op.Size); Index++) + for (int Index = 0; Index < Elems; Index++) { EmitVectorExtractZx(Context, Op.Rd, Index, Op.Size); EmitVectorExtractZx(Context, Op.Rn, Index, Op.Size); @@ -145,6 +146,31 @@ namespace ChocolArm64.Instruction EmitVectorImmBinaryOp(Context, () => Context.Emit(OpCodes.Or)); } + public static void Rbit_V(AILEmitterCtx Context) + { + AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp; + + int Elems = Op.RegisterSize == ARegisterSize.SIMD128 ? 16 : 8; + + for (int Index = 0; Index < Elems; Index++) + { + EmitVectorExtractZx(Context, Op.Rn, Index, 0); + + Context.Emit(OpCodes.Conv_U4); + + ASoftFallback.EmitCall(Context, nameof(ASoftFallback.ReverseBits8)); + + Context.Emit(OpCodes.Conv_U8); + + EmitVectorInsert(Context, Op.Rd, Index, 0); + } + + if (Op.RegisterSize == ARegisterSize.SIMD64) + { + EmitVectorZeroUpper(Context, Op.Rd); + } + } + public static void Rev16_V(AILEmitterCtx Context) { EmitRev_V(Context, ContainerSize: 1); @@ -164,18 +190,17 @@ namespace ChocolArm64.Instruction { AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp; - int Bytes = Context.CurrOp.GetBitsCount() >> 3; - - int Elems = Bytes >> Op.Size; - if (Op.Size >= ContainerSize) { throw new InvalidOperationException(); } + int Bytes = Context.CurrOp.GetBitsCount() >> 3; + int Elems = Bytes >> Op.Size; + int ContainerMask = (1 << (ContainerSize - Op.Size)) - 1; - for (int Index = 0; Index < (Bytes >> Op.Size); Index++) + for (int Index = 0; Index < Elems; Index++) { int RevIndex = Index ^ ContainerMask; diff --git a/ChocolArm64/Instruction/ASoftFallback.cs b/ChocolArm64/Instruction/ASoftFallback.cs index d626622ab..5c0a9c8e3 100644 --- a/ChocolArm64/Instruction/ASoftFallback.cs +++ b/ChocolArm64/Instruction/ASoftFallback.cs @@ -30,6 +30,14 @@ namespace ChocolArm64.Instruction return (ulong)Size; } + public static uint CountSetBits8(uint Value) + { + Value = ((Value >> 1) & 0x55) + (Value & 0x55); + Value = ((Value >> 2) & 0x33) + (Value & 0x33); + + return (Value >> 4) + (Value & 0x0f); + } + private const uint Crc32RevPoly = 0xedb88320; private const uint Crc32cRevPoly = 0x82f63b78; @@ -89,6 +97,14 @@ namespace ChocolArm64.Instruction return Crc; } + public static uint ReverseBits8(uint Value) + { + Value = ((Value & 0xaa) >> 1) | ((Value & 0x55) << 1); + Value = ((Value & 0xcc) >> 2) | ((Value & 0x33) << 2); + + return (Value >> 4) | ((Value & 0x0f) << 4); + } + public static uint ReverseBits32(uint Value) { Value = ((Value & 0xaaaaaaaa) >> 1) | ((Value & 0x55555555) << 1); @@ -101,10 +117,10 @@ namespace ChocolArm64.Instruction public static ulong ReverseBits64(ulong Value) { - Value = ((Value & 0xaaaaaaaaaaaaaaaa) >> 1) | ((Value & 0x5555555555555555) << 1); - Value = ((Value & 0xcccccccccccccccc) >> 2) | ((Value & 0x3333333333333333) << 2); - Value = ((Value & 0xf0f0f0f0f0f0f0f0) >> 4) | ((Value & 0x0f0f0f0f0f0f0f0f) << 4); - Value = ((Value & 0xff00ff00ff00ff00) >> 8) | ((Value & 0x00ff00ff00ff00ff) << 8); + Value = ((Value & 0xaaaaaaaaaaaaaaaa) >> 1 ) | ((Value & 0x5555555555555555) << 1 ); + Value = ((Value & 0xcccccccccccccccc) >> 2 ) | ((Value & 0x3333333333333333) << 2 ); + Value = ((Value & 0xf0f0f0f0f0f0f0f0) >> 4 ) | ((Value & 0x0f0f0f0f0f0f0f0f) << 4 ); + Value = ((Value & 0xff00ff00ff00ff00) >> 8 ) | ((Value & 0x00ff00ff00ff00ff) << 8 ); Value = ((Value & 0xffff0000ffff0000) >> 16) | ((Value & 0x0000ffff0000ffff) << 16); return (Value >> 32) | (Value << 32); diff --git a/ChocolArm64/Instruction/AVectorHelper.cs b/ChocolArm64/Instruction/AVectorHelper.cs index dbfaab756..a0f887b04 100644 --- a/ChocolArm64/Instruction/AVectorHelper.cs +++ b/ChocolArm64/Instruction/AVectorHelper.cs @@ -93,14 +93,6 @@ namespace ChocolArm64.Instruction Value < ulong.MinValue ? ulong.MinValue : (ulong)Value; } - public static int CountSetBits8(byte Value) - { - return ((Value >> 0) & 1) + ((Value >> 1) & 1) + - ((Value >> 2) & 1) + ((Value >> 3) & 1) + - ((Value >> 4) & 1) + ((Value >> 5) & 1) + - ((Value >> 6) & 1) + (Value >> 7); - } - public static double Max(double LHS, double RHS) { if (LHS == 0.0 && RHS == 0.0) @@ -646,4 +638,4 @@ namespace ChocolArm64.Instruction throw new PlatformNotSupportedException(); } } -} \ No newline at end of file +} diff --git a/ChocolArm64/Translation/AILEmitterCtx.cs b/ChocolArm64/Translation/AILEmitterCtx.cs index a004a9665..3fa46e96d 100644 --- a/ChocolArm64/Translation/AILEmitterCtx.cs +++ b/ChocolArm64/Translation/AILEmitterCtx.cs @@ -187,11 +187,6 @@ namespace ChocolArm64.Translation Ldloc(Tmp3Index, AIoType.Int, OptOpLastCompare.RegisterSize); Ldloc(Tmp4Index, AIoType.Int, OptOpLastCompare.RegisterSize); - if (OptOpLastCompare.Emitter == AInstEmit.Adds) - { - Emit(OpCodes.Neg); - } - ILOp = BranchOps[Cond]; } else if (IntCond < 14) diff --git a/ChocolArm64/Translation/ILGeneratorEx.cs b/ChocolArm64/Translation/ILGeneratorEx.cs index 52eb47466..40c6efa4d 100644 --- a/ChocolArm64/Translation/ILGeneratorEx.cs +++ b/ChocolArm64/Translation/ILGeneratorEx.cs @@ -63,7 +63,7 @@ namespace ChocolArm64 else { throw new ArgumentOutOfRangeException(nameof(Index)); - } + } } public static void EmitLdloc(this ILGenerator Generator, int Index) @@ -89,7 +89,7 @@ namespace ChocolArm64 throw new ArgumentOutOfRangeException(nameof(Index)); } break; - } + } } public static void EmitStloc(this ILGenerator Generator, int Index) @@ -123,7 +123,7 @@ namespace ChocolArm64 for (int Index = 0; Index < Count; Index++) { Generator.EmitLdarg(Index); - } + } } } } diff --git a/Ryujinx.Audio/Ryujinx.Audio.csproj b/Ryujinx.Audio/Ryujinx.Audio.csproj index 30f3df573..2cd38add9 100644 --- a/Ryujinx.Audio/Ryujinx.Audio.csproj +++ b/Ryujinx.Audio/Ryujinx.Audio.csproj @@ -2,6 +2,7 @@ netcoreapp2.1 + win10-x64;osx-x64;linux-x64 diff --git a/Ryujinx.Graphics/Gal/GalBlendFactor.cs b/Ryujinx.Graphics/Gal/GalBlendFactor.cs index 7237c4eda..001aaaeca 100644 --- a/Ryujinx.Graphics/Gal/GalBlendFactor.cs +++ b/Ryujinx.Graphics/Gal/GalBlendFactor.cs @@ -20,6 +20,7 @@ namespace Ryujinx.Graphics.Gal ConstantColor = 0x61, OneMinusConstantColor = 0x62, ConstantAlpha = 0x63, - OneMinusConstantAlpha = 0x64 + OneMinusConstantAlpha = 0x64, + ConstantColorG80 = 0xc001 } } \ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/GalComparisonOp.cs b/Ryujinx.Graphics/Gal/GalComparisonOp.cs index ddddecebb..f26a77533 100644 --- a/Ryujinx.Graphics/Gal/GalComparisonOp.cs +++ b/Ryujinx.Graphics/Gal/GalComparisonOp.cs @@ -2,13 +2,13 @@ namespace Ryujinx.Graphics.Gal { public enum GalComparisonOp { - Never = 0x200, - Less = 0x201, - Equal = 0x202, - Lequal = 0x203, - Greater = 0x204, - NotEqual = 0x205, - Gequal = 0x206, - Always = 0x207 + Never = 0x1, + Less = 0x2, + Equal = 0x3, + Lequal = 0x4, + Greater = 0x5, + NotEqual = 0x6, + Gequal = 0x7, + Always = 0x8 } } \ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/GalCullFace.cs b/Ryujinx.Graphics/Gal/GalCullFace.cs new file mode 100644 index 000000000..4ab3e1742 --- /dev/null +++ b/Ryujinx.Graphics/Gal/GalCullFace.cs @@ -0,0 +1,9 @@ +namespace Ryujinx.Graphics.Gal +{ + public enum GalCullFace + { + Front = 0x404, + Back = 0x405, + FrontAndBack = 0x408 + } +} diff --git a/Ryujinx.Graphics/Gal/GalFrontFace.cs b/Ryujinx.Graphics/Gal/GalFrontFace.cs new file mode 100644 index 000000000..17ad11267 --- /dev/null +++ b/Ryujinx.Graphics/Gal/GalFrontFace.cs @@ -0,0 +1,8 @@ +namespace Ryujinx.Graphics.Gal +{ + public enum GalFrontFace + { + CW = 0x900, + CCW = 0x901 + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/GalStencilOp.cs b/Ryujinx.Graphics/Gal/GalStencilOp.cs new file mode 100644 index 000000000..fc83ca5ea --- /dev/null +++ b/Ryujinx.Graphics/Gal/GalStencilOp.cs @@ -0,0 +1,14 @@ +namespace Ryujinx.Graphics.Gal +{ + public enum GalStencilOp + { + Keep = 0x1, + Zero = 0x2, + Replace = 0x3, + Incr = 0x4, + Decr = 0x5, + Invert = 0x6, + IncrWrap = 0x7, + DecrWrap = 0x8 + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/IGalRasterizer.cs b/Ryujinx.Graphics/Gal/IGalRasterizer.cs index e0469382f..586eae6ba 100644 --- a/Ryujinx.Graphics/Gal/IGalRasterizer.cs +++ b/Ryujinx.Graphics/Gal/IGalRasterizer.cs @@ -8,16 +8,34 @@ namespace Ryujinx.Graphics.Gal bool IsIboCached(long Key, long DataSize); + void SetFrontFace(GalFrontFace FrontFace); + void EnableCullFace(); void DisableCullFace(); + void SetCullFace(GalCullFace CullFace); + void EnableDepthTest(); void DisableDepthTest(); void SetDepthFunction(GalComparisonOp Func); + void SetClearDepth(float Depth); + + void EnableStencilTest(); + + void DisableStencilTest(); + + void SetStencilFunction(bool IsFrontFace, GalComparisonOp Func, int Ref, int Mask); + + void SetStencilOp(bool IsFrontFace, GalStencilOp Fail, GalStencilOp ZFail, GalStencilOp ZPass); + + void SetStencilMask(bool IsFrontFace, int Mask); + + void SetClearStencil(int Stencil); + void CreateVbo(long Key, byte[] Buffer); void CreateIbo(long Key, byte[] Buffer); diff --git a/Ryujinx.Graphics/Gal/OpenGL/OGLEnumConverter.cs b/Ryujinx.Graphics/Gal/OpenGL/OGLEnumConverter.cs index cb2c1a0e8..3a81150d6 100644 --- a/Ryujinx.Graphics/Gal/OpenGL/OGLEnumConverter.cs +++ b/Ryujinx.Graphics/Gal/OpenGL/OGLEnumConverter.cs @@ -5,17 +5,76 @@ namespace Ryujinx.Graphics.Gal.OpenGL { static class OGLEnumConverter { + public static FrontFaceDirection GetFrontFace(GalFrontFace FrontFace) + { + switch (FrontFace) + { + case GalFrontFace.CW: return FrontFaceDirection.Cw; + case GalFrontFace.CCW: return FrontFaceDirection.Ccw; + } + + throw new ArgumentException(nameof(FrontFace)); + } + + public static CullFaceMode GetCullFace(GalCullFace CullFace) + { + switch (CullFace) + { + case GalCullFace.Front: return CullFaceMode.Front; + case GalCullFace.Back: return CullFaceMode.Back; + case GalCullFace.FrontAndBack: return CullFaceMode.FrontAndBack; + } + + throw new ArgumentException(nameof(CullFace)); + } + + public static StencilOp GetStencilOp(GalStencilOp Op) + { + switch (Op) + { + case GalStencilOp.Keep: return StencilOp.Keep; + case GalStencilOp.Zero: return StencilOp.Zero; + case GalStencilOp.Replace: return StencilOp.Replace; + case GalStencilOp.Incr: return StencilOp.Incr; + case GalStencilOp.Decr: return StencilOp.Decr; + case GalStencilOp.Invert: return StencilOp.Invert; + case GalStencilOp.IncrWrap: return StencilOp.IncrWrap; + case GalStencilOp.DecrWrap: return StencilOp.DecrWrap; + } + + throw new ArgumentException(nameof(Op)); + } + public static DepthFunction GetDepthFunc(GalComparisonOp Func) { + //Looks like the GPU can take it's own values (described in GalComparisonOp) and OpenGL values alike if ((int)Func >= (int)DepthFunction.Never && (int)Func <= (int)DepthFunction.Always) { return (DepthFunction)Func; } + switch (Func) + { + case GalComparisonOp.Never: return DepthFunction.Never; + case GalComparisonOp.Less: return DepthFunction.Less; + case GalComparisonOp.Equal: return DepthFunction.Equal; + case GalComparisonOp.Lequal: return DepthFunction.Lequal; + case GalComparisonOp.Greater: return DepthFunction.Greater; + case GalComparisonOp.NotEqual: return DepthFunction.Notequal; + case GalComparisonOp.Gequal: return DepthFunction.Gequal; + case GalComparisonOp.Always: return DepthFunction.Always; + } + throw new ArgumentException(nameof(Func)); } + public static StencilFunction GetStencilFunc(GalComparisonOp Func) + { + //OGL comparison values match, it's just an enum cast + return (StencilFunction)GetDepthFunc(Func); + } + public static DrawElementsType GetDrawElementsType(GalIndexFormat Format) { switch (Format) @@ -187,7 +246,6 @@ namespace Ryujinx.Graphics.Gal.OpenGL case GalBlendFactor.OneMinusSrcAlpha: return BlendingFactor.OneMinusSrcAlpha; case GalBlendFactor.DstAlpha: return BlendingFactor.DstAlpha; case GalBlendFactor.OneMinusDstAlpha: return BlendingFactor.OneMinusDstAlpha; - case GalBlendFactor.ConstantColor: return BlendingFactor.ConstantColor; case GalBlendFactor.OneMinusConstantColor: return BlendingFactor.OneMinusConstantColor; case GalBlendFactor.ConstantAlpha: return BlendingFactor.ConstantAlpha; case GalBlendFactor.OneMinusConstantAlpha: return BlendingFactor.OneMinusConstantAlpha; @@ -196,6 +254,10 @@ namespace Ryujinx.Graphics.Gal.OpenGL case GalBlendFactor.OneMinusSrc1Color: return (BlendingFactor)BlendingFactorSrc.OneMinusSrc1Color; case GalBlendFactor.Src1Alpha: return BlendingFactor.Src1Alpha; case GalBlendFactor.OneMinusSrc1Alpha: return (BlendingFactor)BlendingFactorSrc.OneMinusSrc1Alpha; + + case GalBlendFactor.ConstantColor: + case GalBlendFactor.ConstantColorG80: + return BlendingFactor.ConstantColor; } throw new ArgumentException(nameof(BlendFactor)); diff --git a/Ryujinx.Graphics/Gal/OpenGL/OGLRasterizer.cs b/Ryujinx.Graphics/Gal/OpenGL/OGLRasterizer.cs index 8bff6bb3e..b98857117 100644 --- a/Ryujinx.Graphics/Gal/OpenGL/OGLRasterizer.cs +++ b/Ryujinx.Graphics/Gal/OpenGL/OGLRasterizer.cs @@ -106,6 +106,11 @@ namespace Ryujinx.Graphics.Gal.OpenGL return IboCache.TryGetSize(Key, out long Size) && Size == DataSize; } + public void SetFrontFace(GalFrontFace FrontFace) + { + GL.FrontFace(OGLEnumConverter.GetFrontFace(FrontFace)); + } + public void EnableCullFace() { GL.Enable(EnableCap.CullFace); @@ -116,6 +121,11 @@ namespace Ryujinx.Graphics.Gal.OpenGL GL.Disable(EnableCap.CullFace); } + public void SetCullFace(GalCullFace CullFace) + { + GL.CullFace(OGLEnumConverter.GetCullFace(CullFace)); + } + public void EnableDepthTest() { GL.Enable(EnableCap.DepthTest); @@ -131,6 +141,49 @@ namespace Ryujinx.Graphics.Gal.OpenGL GL.DepthFunc(OGLEnumConverter.GetDepthFunc(Func)); } + public void SetClearDepth(float Depth) + { + GL.ClearDepth(Depth); + } + + public void EnableStencilTest() + { + GL.Enable(EnableCap.StencilTest); + } + + public void DisableStencilTest() + { + GL.Disable(EnableCap.StencilTest); + } + + public void SetStencilFunction(bool IsFrontFace, GalComparisonOp Func, int Ref, int Mask) + { + GL.StencilFuncSeparate( + IsFrontFace ? StencilFace.Front : StencilFace.Back, + OGLEnumConverter.GetStencilFunc(Func), + Ref, + Mask); + } + + public void SetStencilOp(bool IsFrontFace, GalStencilOp Fail, GalStencilOp ZFail, GalStencilOp ZPass) + { + GL.StencilOpSeparate( + IsFrontFace ? StencilFace.Front : StencilFace.Back, + OGLEnumConverter.GetStencilOp(Fail), + OGLEnumConverter.GetStencilOp(ZFail), + OGLEnumConverter.GetStencilOp(ZPass)); + } + + public void SetStencilMask(bool IsFrontFace, int Mask) + { + GL.StencilMaskSeparate(IsFrontFace ? StencilFace.Front : StencilFace.Back, Mask); + } + + public void SetClearStencil(int Stencil) + { + GL.ClearStencil(Stencil); + } + public void CreateVbo(long Key, byte[] Buffer) { int Handle = GL.GenBuffer(); diff --git a/Ryujinx.Graphics/Gal/Shader/GlslDecl.cs b/Ryujinx.Graphics/Gal/Shader/GlslDecl.cs index 867f90160..d3284f9f5 100644 --- a/Ryujinx.Graphics/Gal/Shader/GlslDecl.cs +++ b/Ryujinx.Graphics/Gal/Shader/GlslDecl.cs @@ -9,6 +9,7 @@ namespace Ryujinx.Graphics.Gal.Shader public const int TessCoordAttrZ = 0x2f8; public const int InstanceIdAttr = 0x2f8; public const int VertexIdAttr = 0x2fc; + public const int FaceAttr = 0x3fc; public const int GlPositionWAttr = 0x7c; public const int MaxUboSize = 1024; @@ -208,7 +209,8 @@ namespace Ryujinx.Graphics.Gal.Shader { //This is a built-in input variable. if (Abuf.Offs == VertexIdAttr || - Abuf.Offs == InstanceIdAttr) + Abuf.Offs == InstanceIdAttr || + Abuf.Offs == FaceAttr) { break; } diff --git a/Ryujinx.Graphics/Gal/Shader/GlslDecompiler.cs b/Ryujinx.Graphics/Gal/Shader/GlslDecompiler.cs index 87d448689..f3075a504 100644 --- a/Ryujinx.Graphics/Gal/Shader/GlslDecompiler.cs +++ b/Ryujinx.Graphics/Gal/Shader/GlslDecompiler.cs @@ -658,6 +658,14 @@ namespace Ryujinx.Graphics.Gal.Shader case GlslDecl.TessCoordAttrZ: return "gl_TessCoord.z"; } } + else if (Decl.ShaderType == GalShaderType.Fragment) + { + switch (Abuf.Offs) + { + //Note: It's a guess that Maxwell's face is 1 when gl_FrontFacing == true + case GlslDecl.FaceAttr: return "(gl_FrontFacing ? 1 : 0)"; + } + } return GetAttrTempName(Abuf); } @@ -1084,7 +1092,8 @@ namespace Ryujinx.Graphics.Gal.Shader { case ShaderIrOperAbuf Abuf: return Abuf.Offs == GlslDecl.VertexIdAttr || - Abuf.Offs == GlslDecl.InstanceIdAttr + Abuf.Offs == GlslDecl.InstanceIdAttr || + Abuf.Offs == GlslDecl.FaceAttr ? OperType.I32 : OperType.F32; diff --git a/Ryujinx.Graphics/Gal/Shader/ShaderDecodeMem.cs b/Ryujinx.Graphics/Gal/Shader/ShaderDecodeMem.cs index c50f0144b..083b0c63a 100644 --- a/Ryujinx.Graphics/Gal/Shader/ShaderDecodeMem.cs +++ b/Ryujinx.Graphics/Gal/Shader/ShaderDecodeMem.cs @@ -8,6 +8,29 @@ namespace Ryujinx.Graphics.Gal.Shader { private const int TempRegStart = 0x100; + private const int ____ = 0x0; + private const int R___ = 0x1; + private const int _G__ = 0x2; + private const int RG__ = 0x3; + private const int __B_ = 0x4; + private const int RGB_ = 0x7; + private const int ___A = 0x8; + private const int R__A = 0x9; + private const int _G_A = 0xa; + private const int RG_A = 0xb; + private const int __BA = 0xc; + private const int R_BA = 0xd; + private const int _GBA = 0xe; + private const int RGBA = 0xf; + + private static int[,] MaskLut = new int[,] + { + { ____, ____, ____, ____, ____, ____, ____, ____ }, + { R___, _G__, __B_, ___A, RG__, ____, ____, ____ }, + { R___, _G__, __B_, ___A, RG__, R__A, _G_A, __BA }, + { RGB_, RG_A, R_BA, _GBA, RGBA, ____, ____, ____ } + }; + public static void Ld_A(ShaderIrBlock Block, long OpCode) { ShaderIrNode[] Opers = GetOperAbuf20(OpCode); @@ -167,20 +190,12 @@ namespace Ryujinx.Graphics.Gal.Shader ShaderIrNode OperB = GetOperGpr20 (OpCode); ShaderIrNode OperC = GetOperImm13_36(OpCode); - bool TwoDests = GetOperGpr28(OpCode).Index != ShaderIrOperGpr.ZRIndex; + int LutIndex; - int ChMask; + LutIndex = GetOperGpr0(OpCode).Index != ShaderIrOperGpr.ZRIndex ? 1 : 0; + LutIndex |= GetOperGpr28(OpCode).Index != ShaderIrOperGpr.ZRIndex ? 2 : 0; - switch ((OpCode >> 50) & 7) - { - case 0: ChMask = TwoDests ? 0x7 : 0x1; break; - case 1: ChMask = TwoDests ? 0xb : 0x2; break; - case 2: ChMask = TwoDests ? 0xd : 0x4; break; - case 3: ChMask = TwoDests ? 0xe : 0x8; break; - case 4: ChMask = TwoDests ? 0xf : 0x3; break; - - default: throw new InvalidOperationException(); - } + int ChMask = MaskLut[LutIndex, (OpCode >> 50) & 7]; for (int Ch = 0; Ch < 4; Ch++) { diff --git a/Ryujinx.Graphics/Ryujinx.Graphics.csproj b/Ryujinx.Graphics/Ryujinx.Graphics.csproj index 55cb39111..d0fad1076 100644 --- a/Ryujinx.Graphics/Ryujinx.Graphics.csproj +++ b/Ryujinx.Graphics/Ryujinx.Graphics.csproj @@ -2,6 +2,7 @@ netcoreapp2.1 + win10-x64;osx-x64;linux-x64 diff --git a/Ryujinx.HLE/Gpu/Engines/NvGpuEngine3d.cs b/Ryujinx.HLE/Gpu/Engines/NvGpuEngine3d.cs index b27f5c142..e0e769d48 100644 --- a/Ryujinx.HLE/Gpu/Engines/NvGpuEngine3d.cs +++ b/Ryujinx.HLE/Gpu/Engines/NvGpuEngine3d.cs @@ -79,8 +79,10 @@ namespace Ryujinx.HLE.Gpu.Engines Gpu.Renderer.Shader.BindProgram(); + SetFrontFace(); SetCullFace(); SetDepth(); + SetStencil(); SetAlphaBlending(); UploadTextures(Vmm, Keys); @@ -173,14 +175,8 @@ namespace Ryujinx.HLE.Gpu.Engines Gpu.Renderer.Shader.Bind(Key); } - int RawSX = ReadRegister(NvGpuEngine3dReg.ViewportScaleX); - int RawSY = ReadRegister(NvGpuEngine3dReg.ViewportScaleY); - - float SX = BitConverter.Int32BitsToSingle(RawSX); - float SY = BitConverter.Int32BitsToSingle(RawSY); - - float SignX = MathF.Sign(SX); - float SignY = MathF.Sign(SY); + float SignX = GetFlipSign(NvGpuEngine3dReg.ViewportScaleX); + float SignY = GetFlipSign(NvGpuEngine3dReg.ViewportScaleY); Gpu.Renderer.Shader.SetFlip(SignX, SignY); @@ -202,14 +198,145 @@ namespace Ryujinx.HLE.Gpu.Engines throw new ArgumentOutOfRangeException(nameof(Program)); } + private void SetFrontFace() + { + float SignX = GetFlipSign(NvGpuEngine3dReg.ViewportScaleX); + float SignY = GetFlipSign(NvGpuEngine3dReg.ViewportScaleY); + + GalFrontFace FrontFace = (GalFrontFace)ReadRegister(NvGpuEngine3dReg.FrontFace); + + //Flipping breaks facing. Flipping front facing too fixes it + if (SignX != SignY) + { + switch (FrontFace) + { + case GalFrontFace.CW: + FrontFace = GalFrontFace.CCW; + break; + + case GalFrontFace.CCW: + FrontFace = GalFrontFace.CW; + break; + } + } + + Gpu.Renderer.Rasterizer.SetFrontFace(FrontFace); + } + private void SetCullFace() { - //TODO. + bool Enable = (ReadRegister(NvGpuEngine3dReg.CullFaceEnable) & 1) != 0; + + if (Enable) + { + Gpu.Renderer.Rasterizer.EnableCullFace(); + } + else + { + Gpu.Renderer.Rasterizer.DisableCullFace(); + } + + if (!Enable) + { + return; + } + + GalCullFace CullFace = (GalCullFace)ReadRegister(NvGpuEngine3dReg.CullFace); + + Gpu.Renderer.Rasterizer.SetCullFace(CullFace); } private void SetDepth() { - //TODO. + float ClearDepth = ReadRegisterFloat(NvGpuEngine3dReg.ClearDepth); + + Gpu.Renderer.Rasterizer.SetClearDepth(ClearDepth); + + bool Enable = (ReadRegister(NvGpuEngine3dReg.DepthTestEnable) & 1) != 0; + + if (Enable) + { + Gpu.Renderer.Rasterizer.EnableDepthTest(); + } + else + { + Gpu.Renderer.Rasterizer.DisableDepthTest(); + } + + if (!Enable) + { + return; + } + + GalComparisonOp Func = (GalComparisonOp)ReadRegister(NvGpuEngine3dReg.DepthTestFunction); + + Gpu.Renderer.Rasterizer.SetDepthFunction(Func); + } + + private void SetStencil() + { + int ClearStencil = ReadRegister(NvGpuEngine3dReg.ClearStencil); + + Gpu.Renderer.Rasterizer.SetClearStencil(ClearStencil); + + bool Enable = (ReadRegister(NvGpuEngine3dReg.StencilEnable) & 1) != 0; + + if (Enable) + { + Gpu.Renderer.Rasterizer.EnableStencilTest(); + } + else + { + Gpu.Renderer.Rasterizer.DisableStencilTest(); + } + + if (!Enable) + { + return; + } + + void SetFaceStencil( + bool IsFrontFace, + NvGpuEngine3dReg Func, + NvGpuEngine3dReg FuncRef, + NvGpuEngine3dReg FuncMask, + NvGpuEngine3dReg OpFail, + NvGpuEngine3dReg OpZFail, + NvGpuEngine3dReg OpZPass, + NvGpuEngine3dReg Mask) + { + Gpu.Renderer.Rasterizer.SetStencilFunction( + IsFrontFace, + (GalComparisonOp)ReadRegister(Func), + ReadRegister(FuncRef), + ReadRegister(FuncMask)); + + Gpu.Renderer.Rasterizer.SetStencilOp( + IsFrontFace, + (GalStencilOp)ReadRegister(OpFail), + (GalStencilOp)ReadRegister(OpZFail), + (GalStencilOp)ReadRegister(OpZPass)); + + Gpu.Renderer.Rasterizer.SetStencilMask(IsFrontFace, ReadRegister(Mask)); + } + + SetFaceStencil(false, + NvGpuEngine3dReg.StencilBackFuncFunc, + NvGpuEngine3dReg.StencilBackFuncRef, + NvGpuEngine3dReg.StencilBackFuncMask, + NvGpuEngine3dReg.StencilBackOpFail, + NvGpuEngine3dReg.StencilBackOpZFail, + NvGpuEngine3dReg.StencilBackOpZPass, + NvGpuEngine3dReg.StencilBackMask); + + SetFaceStencil(true, + NvGpuEngine3dReg.StencilFrontFuncFunc, + NvGpuEngine3dReg.StencilFrontFuncRef, + NvGpuEngine3dReg.StencilFrontFuncMask, + NvGpuEngine3dReg.StencilFrontOpFail, + NvGpuEngine3dReg.StencilFrontOpZFail, + NvGpuEngine3dReg.StencilFrontOpZPass, + NvGpuEngine3dReg.StencilFrontMask); } private void SetAlphaBlending() @@ -549,6 +676,11 @@ namespace Ryujinx.HLE.Gpu.Engines ConstBuffers[Stage][Index].Size = ReadRegister(NvGpuEngine3dReg.ConstBufferSize); } + private float GetFlipSign(NvGpuEngine3dReg Reg) + { + return MathF.Sign(ReadRegisterFloat(Reg)); + } + private long MakeInt64From2xInt32(NvGpuEngine3dReg Reg) { return @@ -571,6 +703,11 @@ namespace Ryujinx.HLE.Gpu.Engines return Registers[(int)Reg]; } + private float ReadRegisterFloat(NvGpuEngine3dReg Reg) + { + return BitConverter.Int32BitsToSingle(ReadRegister(Reg)); + } + private void WriteRegister(NvGpuEngine3dReg Reg, int Value) { Registers[(int)Reg] = Value; diff --git a/Ryujinx.HLE/Gpu/Engines/NvGpuEngine3dReg.cs b/Ryujinx.HLE/Gpu/Engines/NvGpuEngine3dReg.cs index 64866ce9a..9eb2966d9 100644 --- a/Ryujinx.HLE/Gpu/Engines/NvGpuEngine3dReg.cs +++ b/Ryujinx.HLE/Gpu/Engines/NvGpuEngine3dReg.cs @@ -14,6 +14,11 @@ namespace Ryujinx.HLE.Gpu.Engines ViewportTranslateZ = 0x285, VertexArrayFirst = 0x35d, VertexArrayCount = 0x35e, + ClearDepth = 0x364, + ClearStencil = 0x368, + StencilBackFuncRef = 0x3d5, + StencilBackMask = 0x3d6, + StencilBackFuncMask = 0x3d7, VertexAttribNFormat = 0x458, DepthTestEnable = 0x4b3, IBlendEnable = 0x4b9, @@ -27,9 +32,22 @@ namespace Ryujinx.HLE.Gpu.Engines BlendFuncDstAlpha = 0x4d6, BlendEnableMaster = 0x4d7, IBlendNEnable = 0x4d8, + StencilEnable = 0x4e0, + StencilFrontOpFail = 0x4e1, + StencilFrontOpZFail = 0x4e2, + StencilFrontOpZPass = 0x4e3, + StencilFrontFuncFunc = 0x4e4, + StencilFrontFuncRef = 0x4e5, + StencilFrontFuncMask = 0x4e6, + StencilFrontMask = 0x4e7, VertexArrayElemBase = 0x50d, TexHeaderPoolOffset = 0x55d, TexSamplerPoolOffset = 0x557, + StencilTwoSideEnable = 0x565, + StencilBackOpFail = 0x566, + StencilBackOpZFail = 0x567, + StencilBackOpZPass = 0x568, + StencilBackFuncFunc = 0x569, ShaderAddress = 0x582, VertexBeginGl = 0x586, IndexArrayAddress = 0x5f2, diff --git a/Ryujinx.HLE/OsHle/Services/Aud/IAudioOut.cs b/Ryujinx.HLE/OsHle/Services/Aud/IAudioOut.cs index ef8bd89ba..49c87a561 100644 --- a/Ryujinx.HLE/OsHle/Services/Aud/IAudioOut.cs +++ b/Ryujinx.HLE/OsHle/Services/Aud/IAudioOut.cs @@ -24,15 +24,15 @@ namespace Ryujinx.HLE.OsHle.Services.Aud { m_Commands = new Dictionary() { - { 0, GetAudioOutState }, - { 1, StartAudioOut }, - { 2, StopAudioOut }, - { 3, AppendAudioOutBuffer }, - { 4, RegisterBufferEvent }, - { 5, GetReleasedAudioOutBuffer }, - { 6, ContainsAudioOutBuffer }, - { 7, AppendAudioOutBufferEx }, - { 8, GetReleasedAudioOutBufferEx } + { 0, GetAudioOutState }, + { 1, StartAudioOut }, + { 2, StopAudioOut }, + { 3, AppendAudioOutBuffer }, + { 4, RegisterBufferEvent }, + { 5, GetReleasedAudioOutBuffer }, + { 6, ContainsAudioOutBuffer }, + { 7, AppendAudioOutBufferAuto }, + { 8, GetReleasedAudioOutBufferAuto } }; this.AudioOut = AudioOut; @@ -63,19 +63,7 @@ namespace Ryujinx.HLE.OsHle.Services.Aud public long AppendAudioOutBuffer(ServiceCtx Context) { - long Tag = Context.RequestData.ReadInt64(); - - AudioOutData Data = AMemoryHelper.Read( - Context.Memory, - Context.Request.SendBuff[0].Position); - - byte[] Buffer = Context.Memory.ReadBytes( - Data.SampleBufferPtr, - Data.SampleBufferSize); - - AudioOut.AppendBuffer(Track, Tag, Buffer); - - return 0; + return AppendAudioOutBufferImpl(Context, Context.Request.SendBuff[0].Position); } public long RegisterBufferEvent(ServiceCtx Context) @@ -92,6 +80,51 @@ namespace Ryujinx.HLE.OsHle.Services.Aud long Position = Context.Request.ReceiveBuff[0].Position; long Size = Context.Request.ReceiveBuff[0].Size; + return GetReleasedAudioOutBufferImpl(Context, Position, Size); + } + + public long ContainsAudioOutBuffer(ServiceCtx Context) + { + long Tag = Context.RequestData.ReadInt64(); + + Context.ResponseData.Write(AudioOut.ContainsBuffer(Track, Tag) ? 1 : 0); + + return 0; + } + + public long AppendAudioOutBufferAuto(ServiceCtx Context) + { + (long Position, long Size) = Context.Request.GetBufferType0x21(); + + return AppendAudioOutBufferImpl(Context, Position); + } + + public long AppendAudioOutBufferImpl(ServiceCtx Context, long Position) + { + long Tag = Context.RequestData.ReadInt64(); + + AudioOutData Data = AMemoryHelper.Read( + Context.Memory, + Position); + + byte[] Buffer = Context.Memory.ReadBytes( + Data.SampleBufferPtr, + Data.SampleBufferSize); + + AudioOut.AppendBuffer(Track, Tag, Buffer); + + return 0; + } + + public long GetReleasedAudioOutBufferAuto(ServiceCtx Context) + { + (long Position, long Size) = Context.Request.GetBufferType0x22(); + + return GetReleasedAudioOutBufferImpl(Context, Position, Size); + } + + public long GetReleasedAudioOutBufferImpl(ServiceCtx Context, long Position, long Size) + { uint Count = (uint)((ulong)Size >> 3); long[] ReleasedBuffers = AudioOut.GetReleasedBuffers(Track, (int)Count); @@ -113,29 +146,6 @@ namespace Ryujinx.HLE.OsHle.Services.Aud return 0; } - public long ContainsAudioOutBuffer(ServiceCtx Context) - { - long Tag = Context.RequestData.ReadInt64(); - - Context.ResponseData.Write(AudioOut.ContainsBuffer(Track, Tag) ? 1 : 0); - - return 0; - } - - public long AppendAudioOutBufferEx(ServiceCtx Context) - { - Context.Ns.Log.PrintStub(LogClass.ServiceAudio, "Stubbed."); - - return 0; - } - - public long GetReleasedAudioOutBufferEx(ServiceCtx Context) - { - Context.Ns.Log.PrintStub(LogClass.ServiceAudio, "Stubbed."); - - return 0; - } - public void Dispose() { Dispose(true); diff --git a/Ryujinx.HLE/OsHle/Services/Set/ISettingsServer.cs b/Ryujinx.HLE/OsHle/Services/Set/ISettingsServer.cs index abab93822..5c4f82676 100644 --- a/Ryujinx.HLE/OsHle/Services/Set/ISettingsServer.cs +++ b/Ryujinx.HLE/OsHle/Services/Set/ISettingsServer.cs @@ -28,10 +28,13 @@ namespace Ryujinx.HLE.OsHle.Services.Set } public static long GetAvailableLanguageCodes(ServiceCtx Context) - { - GetAvailableLanguagesCodesMethod(Context, Context.Request.RecvListBuff[0].Position, Context.Request.RecvListBuff[0].Size); - - return 0; + { + GetAvailableLanguagesCodesImpl( + Context, + Context.Request.RecvListBuff[0].Position, + Context.Request.RecvListBuff[0].Size); + + return 0; } public static long GetAvailableLanguageCodeCount(ServiceCtx Context) @@ -40,16 +43,19 @@ namespace Ryujinx.HLE.OsHle.Services.Set return 0; } - + public static long GetAvailableLanguageCodes2(ServiceCtx Context) { - GetAvailableLanguagesCodesMethod(Context, Context.Request.ReceiveBuff[0].Position, Context.Request.ReceiveBuff[0].Size); - - return 0; + GetAvailableLanguagesCodesImpl( + Context, + Context.Request.ReceiveBuff[0].Position, + Context.Request.ReceiveBuff[0].Size); + + return 0; } - - public static long GetAvailableLanguagesCodesMethod(ServiceCtx Context, long Position, long Size) - { + + public static long GetAvailableLanguagesCodesImpl(ServiceCtx Context, long Position, long Size) + { int Count = (int)(Size / 8); if (Count > SystemStateMgr.LanguageCodes.Length) @@ -65,8 +71,8 @@ namespace Ryujinx.HLE.OsHle.Services.Set } Context.ResponseData.Write(Count); - - return 0; - } + + return 0; + } } } diff --git a/Ryujinx.HLE/OsHle/Services/Time/IStaticService.cs b/Ryujinx.HLE/OsHle/Services/Time/IStaticService.cs index 1f0121446..55601a894 100644 --- a/Ryujinx.HLE/OsHle/Services/Time/IStaticService.cs +++ b/Ryujinx.HLE/OsHle/Services/Time/IStaticService.cs @@ -1,4 +1,5 @@ using Ryujinx.HLE.OsHle.Ipc; +using System; using System.Collections.Generic; namespace Ryujinx.HLE.OsHle.Services.Time @@ -9,15 +10,18 @@ namespace Ryujinx.HLE.OsHle.Services.Time public override IReadOnlyDictionary Commands => m_Commands; + private static readonly DateTime StartupDate = DateTime.UtcNow; + public IStaticService() { m_Commands = new Dictionary() { - { 0, GetStandardUserSystemClock }, - { 1, GetStandardNetworkSystemClock }, - { 2, GetStandardSteadyClock }, - { 3, GetTimeZoneService }, - { 4, GetStandardLocalSystemClock } + { 0, GetStandardUserSystemClock }, + { 1, GetStandardNetworkSystemClock }, + { 2, GetStandardSteadyClock }, + { 3, GetTimeZoneService }, + { 4, GetStandardLocalSystemClock }, + { 300, CalculateMonotonicSystemClockBaseTimePoint } }; } @@ -56,5 +60,15 @@ namespace Ryujinx.HLE.OsHle.Services.Time return 0; } + public long CalculateMonotonicSystemClockBaseTimePoint(ServiceCtx Context) + { + long TimeOffset = (long)(DateTime.UtcNow - StartupDate).TotalSeconds; + long SystemClockContextEpoch = Context.RequestData.ReadInt64(); + + Context.ResponseData.Write(TimeOffset + SystemClockContextEpoch); + + return 0; + } + } } \ No newline at end of file diff --git a/Ryujinx.HLE/OsHle/Services/Time/ISteadyClock.cs b/Ryujinx.HLE/OsHle/Services/Time/ISteadyClock.cs index 6be097b7d..e2cb34e3c 100644 --- a/Ryujinx.HLE/OsHle/Services/Time/ISteadyClock.cs +++ b/Ryujinx.HLE/OsHle/Services/Time/ISteadyClock.cs @@ -1,4 +1,5 @@ using Ryujinx.HLE.OsHle.Ipc; +using System; using System.Collections.Generic; namespace Ryujinx.HLE.OsHle.Services.Time @@ -9,12 +10,44 @@ namespace Ryujinx.HLE.OsHle.Services.Time public override IReadOnlyDictionary Commands => m_Commands; + private ulong TestOffset; + public ISteadyClock() { m_Commands = new Dictionary() { - //... + { 0, GetCurrentTimePoint }, + { 1, GetTestOffset }, + { 2, SetTestOffset } }; + + TestOffset = 0; + } + + public long GetCurrentTimePoint(ServiceCtx Context) + { + Context.ResponseData.Write((long)(System.Diagnostics.Process.GetCurrentProcess().StartTime - DateTime.Now).TotalSeconds); + + for (int i = 0; i < 0x10; i++) + { + Context.ResponseData.Write((byte)0); + } + + return 0; + } + + public long GetTestOffset(ServiceCtx Context) + { + Context.ResponseData.Write(TestOffset); + + return 0; + } + + public long SetTestOffset(ServiceCtx Context) + { + TestOffset = Context.RequestData.ReadUInt64(); + + return 0; } } } \ No newline at end of file diff --git a/Ryujinx.HLE/OsHle/Services/Time/ISystemClock.cs b/Ryujinx.HLE/OsHle/Services/Time/ISystemClock.cs index 787f86c22..27b65c3c6 100644 --- a/Ryujinx.HLE/OsHle/Services/Time/ISystemClock.cs +++ b/Ryujinx.HLE/OsHle/Services/Time/ISystemClock.cs @@ -14,14 +14,36 @@ namespace Ryujinx.HLE.OsHle.Services.Time private SystemClockType ClockType; + private DateTime SystemClockContextEpoch; + + private long SystemClockTimePoint; + + private byte[] SystemClockContextEnding; + + private long TimeOffset; + public ISystemClock(SystemClockType ClockType) { m_Commands = new Dictionary() { - { 0, GetCurrentTime } + { 0, GetCurrentTime }, + { 1, SetCurrentTime }, + { 2, GetSystemClockContext }, + { 3, SetSystemClockContext } }; - this.ClockType = ClockType; + this.ClockType = ClockType; + SystemClockContextEpoch = System.Diagnostics.Process.GetCurrentProcess().StartTime; + SystemClockContextEnding = new byte[0x10]; + TimeOffset = 0; + + if (ClockType == SystemClockType.User || + ClockType == SystemClockType.Network) + { + SystemClockContextEpoch = SystemClockContextEpoch.ToUniversalTime(); + } + + SystemClockTimePoint = (long)(SystemClockContextEpoch - Epoch).TotalSeconds; } public long GetCurrentTime(ServiceCtx Context) @@ -34,7 +56,50 @@ namespace Ryujinx.HLE.OsHle.Services.Time CurrentTime = CurrentTime.ToUniversalTime(); } - Context.ResponseData.Write((long)(DateTime.Now - Epoch).TotalSeconds); + Context.ResponseData.Write((long)((CurrentTime - Epoch).TotalSeconds) + TimeOffset); + + return 0; + } + + public long SetCurrentTime(ServiceCtx Context) + { + DateTime CurrentTime = DateTime.Now; + + if (ClockType == SystemClockType.User || + ClockType == SystemClockType.Network) + { + CurrentTime = CurrentTime.ToUniversalTime(); + } + + TimeOffset = (Context.RequestData.ReadInt64() - (long)(CurrentTime - Epoch).TotalSeconds); + + return 0; + } + + public long GetSystemClockContext(ServiceCtx Context) + { + Context.ResponseData.Write((long)(SystemClockContextEpoch - Epoch).TotalSeconds); + + // The point in time, TODO: is there a link between epoch and this? + Context.ResponseData.Write(SystemClockTimePoint); + + // This seems to be some kind of identifier? + for (int i = 0; i < 0x10; i++) + { + Context.ResponseData.Write(SystemClockContextEnding[i]); + } + + return 0; + } + + public long SetSystemClockContext(ServiceCtx Context) + { + long NewSystemClockEpoch = Context.RequestData.ReadInt64(); + long NewSystemClockTimePoint = Context.RequestData.ReadInt64(); + + SystemClockContextEpoch = Epoch.Add(TimeSpan.FromSeconds(NewSystemClockEpoch)); + SystemClockTimePoint = NewSystemClockTimePoint; + SystemClockContextEnding = Context.RequestData.ReadBytes(0x10); return 0; } diff --git a/Ryujinx.HLE/OsHle/Services/Time/SystemClockType.cs b/Ryujinx.HLE/OsHle/Services/Time/SystemClockType.cs index 518e1eb0c..d581d9ca5 100644 --- a/Ryujinx.HLE/OsHle/Services/Time/SystemClockType.cs +++ b/Ryujinx.HLE/OsHle/Services/Time/SystemClockType.cs @@ -4,6 +4,7 @@ namespace Ryujinx.HLE.OsHle.Services.Time { User, Network, - Local + Local, + EphemeralNetwork } } \ No newline at end of file diff --git a/Ryujinx.HLE/Ryujinx.HLE.csproj b/Ryujinx.HLE/Ryujinx.HLE.csproj index 27e43626d..acef4be9e 100644 --- a/Ryujinx.HLE/Ryujinx.HLE.csproj +++ b/Ryujinx.HLE/Ryujinx.HLE.csproj @@ -2,6 +2,7 @@ netcoreapp2.1 + win10-x64;osx-x64;linux-x64 diff --git a/Ryujinx.LLE/Luea.csproj b/Ryujinx.LLE/Luea.csproj index 23df6047f..de1c5f61d 100644 --- a/Ryujinx.LLE/Luea.csproj +++ b/Ryujinx.LLE/Luea.csproj @@ -3,6 +3,7 @@ Exe netcoreapp2.1 + win10-x64;osx-x64;linux-x64 diff --git a/Ryujinx.ShaderTools/Ryujinx.ShaderTools.csproj b/Ryujinx.ShaderTools/Ryujinx.ShaderTools.csproj index 9eeee2ba9..24f31efef 100644 --- a/Ryujinx.ShaderTools/Ryujinx.ShaderTools.csproj +++ b/Ryujinx.ShaderTools/Ryujinx.ShaderTools.csproj @@ -7,6 +7,7 @@ Exe netcoreapp2.1 + win10-x64;osx-x64;linux-x64 diff --git a/Ryujinx.Tests/Cpu/CpuTest.cs b/Ryujinx.Tests/Cpu/CpuTest.cs index 2af50c6c8..70a8e1924 100644 --- a/Ryujinx.Tests/Cpu/CpuTest.cs +++ b/Ryujinx.Tests/Cpu/CpuTest.cs @@ -113,20 +113,20 @@ namespace Ryujinx.Tests.Cpu return GetThreadState(); } - protected static Vector128 MakeVectorE0(double A0) + protected static Vector128 MakeVectorE0(double E0) { - return Sse.StaticCast(Sse2.SetVector128(0, BitConverter.DoubleToInt64Bits(A0))); + return Sse.StaticCast(Sse2.SetVector128(0, BitConverter.DoubleToInt64Bits(E0))); } - protected static Vector128 MakeVectorE0E1(double A0, double A1) + protected static Vector128 MakeVectorE0E1(double E0, double E1) { - return Sse.StaticCast(Sse2.SetVector128(BitConverter.DoubleToInt64Bits(A1), - BitConverter.DoubleToInt64Bits(A0))); + return Sse.StaticCast(Sse2.SetVector128(BitConverter.DoubleToInt64Bits(E1), + BitConverter.DoubleToInt64Bits(E0))); } - protected static Vector128 MakeVectorE1(double A1) + protected static Vector128 MakeVectorE1(double E1) { - return Sse.StaticCast(Sse2.SetVector128(BitConverter.DoubleToInt64Bits(A1), 0)); + return Sse.StaticCast(Sse2.SetVector128(BitConverter.DoubleToInt64Bits(E1), 0)); } protected static double VectorExtractDouble(Vector128 Vector, byte Index) @@ -136,29 +136,29 @@ namespace Ryujinx.Tests.Cpu return BitConverter.Int64BitsToDouble(Value); } - protected static Vector128 MakeVectorE0(ulong A0) + protected static Vector128 MakeVectorE0(ulong E0) { - return Sse.StaticCast(Sse2.SetVector128(0, A0)); + return Sse.StaticCast(Sse2.SetVector128(0, E0)); } - protected static Vector128 MakeVectorE0E1(ulong A0, ulong A1) + protected static Vector128 MakeVectorE0E1(ulong E0, ulong E1) { - return Sse.StaticCast(Sse2.SetVector128(A1, A0)); + return Sse.StaticCast(Sse2.SetVector128(E1, E0)); } - protected static Vector128 MakeVectorE1(ulong A1) + protected static Vector128 MakeVectorE1(ulong E1) { - return Sse.StaticCast(Sse2.SetVector128(A1, 0)); + return Sse.StaticCast(Sse2.SetVector128(E1, 0)); } protected static ulong GetVectorE0(Vector128 Vector) { - return Sse41.Extract(Sse.StaticCast(Vector), 0); + return Sse41.Extract(Sse.StaticCast(Vector), (byte)0); } protected static ulong GetVectorE1(Vector128 Vector) { - return Sse41.Extract(Sse.StaticCast(Vector), 1); + return Sse41.Extract(Sse.StaticCast(Vector), (byte)1); } } } diff --git a/Ryujinx.Tests/Cpu/CpuTestSimd.cs b/Ryujinx.Tests/Cpu/CpuTestSimd.cs index 100f6e0a8..02c5b25b2 100644 --- a/Ryujinx.Tests/Cpu/CpuTestSimd.cs +++ b/Ryujinx.Tests/Cpu/CpuTestSimd.cs @@ -11,7 +11,7 @@ namespace Ryujinx.Tests.Cpu using Tester; using Tester.Types; - [Category("Simd")/*, Ignore("Tested: first half of 2018.")*/] + [Category("Simd")/*, Ignore("Tested: second half of 2018.")*/] public sealed class CpuTestSimd : CpuTest { #if Simd @@ -775,6 +775,178 @@ namespace Ryujinx.Tests.Cpu }); } + [Test, Description("RBIT ., .")] + public void Rbit_V_8B([ValueSource("_8B_")] [Random(1)] ulong A) + { + uint Opcode = 0x2E605820; // RBIT V0.8B, V1.8B + Bits Op = new Bits(Opcode); + + Vector128 V0 = MakeVectorE1(TestContext.CurrentContext.Random.NextULong()); + Vector128 V1 = MakeVectorE0(A); + AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1); + + AArch64.V(1, new Bits(A)); + SimdFp.Rbit_V(Op[30], Op[9, 5], Op[4, 0]); + + Assert.Multiple(() => + { + Assert.That(GetVectorE0(ThreadState.V0), Is.EqualTo(AArch64.V(64, 0).ToUInt64())); + Assert.That(GetVectorE1(ThreadState.V0), Is.Zero); + }); + } + + [Test, Pairwise, Description("RBIT ., .")] + public void Rbit_V_16B([ValueSource("_8B_")] [Random(1)] ulong A0, + [ValueSource("_8B_")] [Random(1)] ulong A1) + { + uint Opcode = 0x6E605820; // RBIT V0.16B, V1.16B + Bits Op = new Bits(Opcode); + + Vector128 V1 = MakeVectorE0E1(A0, A1); + AThreadState ThreadState = SingleOpcode(Opcode, V1: V1); + + AArch64.Vpart(1, 0, new Bits(A0)); + AArch64.Vpart(1, 1, new Bits(A1)); + SimdFp.Rbit_V(Op[30], 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, Description("REV16 ., .")] + public void Rev16_V_8B([ValueSource("_8B_")] [Random(1)] ulong A) + { + uint Opcode = 0x0E201820; // REV16 V0.8B, V1.8B + Bits Op = new Bits(Opcode); + + Vector128 V0 = MakeVectorE1(TestContext.CurrentContext.Random.NextULong()); + Vector128 V1 = MakeVectorE0(A); + AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1); + + AArch64.V(1, new Bits(A)); + SimdFp.Rev16_V(Op[30], Op[23, 22], Op[9, 5], Op[4, 0]); + + Assert.Multiple(() => + { + Assert.That(GetVectorE0(ThreadState.V0), Is.EqualTo(AArch64.V(64, 0).ToUInt64())); + Assert.That(GetVectorE1(ThreadState.V0), Is.Zero); + }); + } + + [Test, Pairwise, Description("REV16 ., .")] + public void Rev16_V_16B([ValueSource("_8B_")] [Random(1)] ulong A0, + [ValueSource("_8B_")] [Random(1)] ulong A1) + { + uint Opcode = 0x4E201820; // REV16 V0.16B, V1.16B + Bits Op = new Bits(Opcode); + + Vector128 V1 = MakeVectorE0E1(A0, A1); + AThreadState ThreadState = SingleOpcode(Opcode, V1: V1); + + AArch64.Vpart(1, 0, new Bits(A0)); + AArch64.Vpart(1, 1, new Bits(A1)); + SimdFp.Rev16_V(Op[30], Op[23, 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, Description("REV32 ., .")] + public void Rev32_V_8B_4H([ValueSource("_8B4H_")] [Random(1)] ulong A, + [Values(0b00u, 0b01u)] uint size) // <8B, 4H> + { + uint Opcode = 0x2E200820; // REV32 V0.8B, V1.8B + Opcode |= ((size & 3) << 22); + Bits Op = new Bits(Opcode); + + Vector128 V0 = MakeVectorE1(TestContext.CurrentContext.Random.NextULong()); + Vector128 V1 = MakeVectorE0(A); + AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1); + + AArch64.V(1, new Bits(A)); + SimdFp.Rev32_V(Op[30], Op[23, 22], Op[9, 5], Op[4, 0]); + + Assert.Multiple(() => + { + Assert.That(GetVectorE0(ThreadState.V0), Is.EqualTo(AArch64.V(64, 0).ToUInt64())); + Assert.That(GetVectorE1(ThreadState.V0), Is.Zero); + }); + } + + [Test, Pairwise, Description("REV32 ., .")] + public void Rev32_V_16B_8H([ValueSource("_8B4H_")] [Random(1)] ulong A0, + [ValueSource("_8B4H_")] [Random(1)] ulong A1, + [Values(0b00u, 0b01u)] uint size) // <16B, 8H> + { + uint Opcode = 0x6E200820; // REV32 V0.16B, V1.16B + Opcode |= ((size & 3) << 22); + Bits Op = new Bits(Opcode); + + Vector128 V1 = MakeVectorE0E1(A0, A1); + AThreadState ThreadState = SingleOpcode(Opcode, V1: V1); + + AArch64.Vpart(1, 0, new Bits(A0)); + AArch64.Vpart(1, 1, new Bits(A1)); + SimdFp.Rev32_V(Op[30], Op[23, 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, Description("REV64 ., .")] + public void Rev64_V_8B_4H_2S([ValueSource("_8B4H2S_")] [Random(1)] ulong A, + [Values(0b00u, 0b01u, 0b10u)] uint size) // <8B, 4H, 2S> + { + uint Opcode = 0x0E200820; // REV64 V0.8B, V1.8B + Opcode |= ((size & 3) << 22); + Bits Op = new Bits(Opcode); + + Vector128 V0 = MakeVectorE1(TestContext.CurrentContext.Random.NextULong()); + Vector128 V1 = MakeVectorE0(A); + AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1); + + AArch64.V(1, new Bits(A)); + SimdFp.Rev64_V(Op[30], Op[23, 22], Op[9, 5], Op[4, 0]); + + Assert.Multiple(() => + { + Assert.That(GetVectorE0(ThreadState.V0), Is.EqualTo(AArch64.V(64, 0).ToUInt64())); + Assert.That(GetVectorE1(ThreadState.V0), Is.Zero); + }); + } + + [Test, Pairwise, Description("REV64 ., .")] + public void Rev64_V_16B_8H_4S([ValueSource("_8B4H2S_")] [Random(1)] ulong A0, + [ValueSource("_8B4H2S_")] [Random(1)] ulong A1, + [Values(0b00u, 0b01u, 0b10u)] uint size) // <16B, 8H, 4S> + { + uint Opcode = 0x4E200820; // REV64 V0.16B, V1.16B + Opcode |= ((size & 3) << 22); + Bits Op = new Bits(Opcode); + + Vector128 V1 = MakeVectorE0E1(A0, A1); + AThreadState ThreadState = SingleOpcode(Opcode, V1: V1); + + AArch64.Vpart(1, 0, new Bits(A0)); + AArch64.Vpart(1, 1, new Bits(A1)); + SimdFp.Rev64_V(Op[30], Op[23, 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, Description("SQXTN , ")] public void Sqxtn_S_HB_SH_DS([ValueSource("_1H1S1D_")] [Random(1)] ulong A, [Values(0b00u, 0b01u, 0b10u)] uint size) // @@ -834,8 +1006,8 @@ namespace Ryujinx.Tests.Cpu Opcode |= ((size & 3) << 22); Bits Op = new Bits(Opcode); - ulong _X0 = TestContext.CurrentContext.Random.NextULong(); - Vector128 V0 = MakeVectorE0(_X0); + ulong _E0 = TestContext.CurrentContext.Random.NextULong(); + Vector128 V0 = MakeVectorE0(_E0); Vector128 V1 = MakeVectorE0E1(A0, A1); AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1); @@ -845,7 +1017,7 @@ namespace Ryujinx.Tests.Cpu Assert.Multiple(() => { - Assert.That(GetVectorE0(ThreadState.V0), Is.EqualTo(_X0)); + Assert.That(GetVectorE0(ThreadState.V0), Is.EqualTo(_E0)); Assert.That(GetVectorE1(ThreadState.V0), Is.EqualTo(AArch64.Vpart(64, 0, 1).ToUInt64())); }); Assert.That(((ThreadState.Fpsr >> 27) & 1) != 0, Is.EqualTo(Shared.FPSR[27])); @@ -910,8 +1082,8 @@ namespace Ryujinx.Tests.Cpu Opcode |= ((size & 3) << 22); Bits Op = new Bits(Opcode); - ulong _X0 = TestContext.CurrentContext.Random.NextULong(); - Vector128 V0 = MakeVectorE0(_X0); + ulong _E0 = TestContext.CurrentContext.Random.NextULong(); + Vector128 V0 = MakeVectorE0(_E0); Vector128 V1 = MakeVectorE0E1(A0, A1); AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1); @@ -921,7 +1093,7 @@ namespace Ryujinx.Tests.Cpu Assert.Multiple(() => { - Assert.That(GetVectorE0(ThreadState.V0), Is.EqualTo(_X0)); + Assert.That(GetVectorE0(ThreadState.V0), Is.EqualTo(_E0)); Assert.That(GetVectorE1(ThreadState.V0), Is.EqualTo(AArch64.Vpart(64, 0, 1).ToUInt64())); }); Assert.That(((ThreadState.Fpsr >> 27) & 1) != 0, Is.EqualTo(Shared.FPSR[27])); @@ -986,8 +1158,8 @@ namespace Ryujinx.Tests.Cpu Opcode |= ((size & 3) << 22); Bits Op = new Bits(Opcode); - ulong _X0 = TestContext.CurrentContext.Random.NextULong(); - Vector128 V0 = MakeVectorE0(_X0); + ulong _E0 = TestContext.CurrentContext.Random.NextULong(); + Vector128 V0 = MakeVectorE0(_E0); Vector128 V1 = MakeVectorE0E1(A0, A1); AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1); @@ -997,7 +1169,7 @@ namespace Ryujinx.Tests.Cpu Assert.Multiple(() => { - Assert.That(GetVectorE0(ThreadState.V0), Is.EqualTo(_X0)); + Assert.That(GetVectorE0(ThreadState.V0), Is.EqualTo(_E0)); Assert.That(GetVectorE1(ThreadState.V0), Is.EqualTo(AArch64.Vpart(64, 0, 1).ToUInt64())); }); Assert.That(((ThreadState.Fpsr >> 27) & 1) != 0, Is.EqualTo(Shared.FPSR[27])); diff --git a/Ryujinx.Tests/Cpu/CpuTestSimdReg.cs b/Ryujinx.Tests/Cpu/CpuTestSimdReg.cs index c05a862c1..5e14f55d3 100644 --- a/Ryujinx.Tests/Cpu/CpuTestSimdReg.cs +++ b/Ryujinx.Tests/Cpu/CpuTestSimdReg.cs @@ -11,7 +11,7 @@ namespace Ryujinx.Tests.Cpu using Tester; using Tester.Types; - [Category("SimdReg")/*, Ignore("Tested: first half of 2018.")*/] + [Category("SimdReg")/*, Ignore("Tested: second half of 2018.")*/] public sealed class CpuTestSimdReg : CpuTest { #if SimdReg @@ -176,8 +176,8 @@ namespace Ryujinx.Tests.Cpu Opcode |= ((size & 3) << 22); Bits Op = new Bits(Opcode); - ulong _X0 = TestContext.CurrentContext.Random.NextULong(); - Vector128 V0 = MakeVectorE0(_X0); + ulong _E0 = TestContext.CurrentContext.Random.NextULong(); + Vector128 V0 = MakeVectorE0(_E0); Vector128 V1 = MakeVectorE0E1(A0, A1); Vector128 V2 = MakeVectorE0E1(B0, B1); AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2); @@ -190,7 +190,7 @@ namespace Ryujinx.Tests.Cpu Assert.Multiple(() => { - Assert.That(GetVectorE0(ThreadState.V0), Is.EqualTo(_X0)); + Assert.That(GetVectorE0(ThreadState.V0), Is.EqualTo(_E0)); Assert.That(GetVectorE1(ThreadState.V0), Is.EqualTo(AArch64.Vpart(64, 0, 1).ToUInt64())); }); } @@ -1157,8 +1157,8 @@ namespace Ryujinx.Tests.Cpu Opcode |= ((size & 3) << 22); Bits Op = new Bits(Opcode); - ulong _X0 = TestContext.CurrentContext.Random.NextULong(); - Vector128 V0 = MakeVectorE0(_X0); + ulong _E0 = TestContext.CurrentContext.Random.NextULong(); + Vector128 V0 = MakeVectorE0(_E0); Vector128 V1 = MakeVectorE0E1(A0, A1); Vector128 V2 = MakeVectorE0E1(B0, B1); AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2); @@ -1171,7 +1171,7 @@ namespace Ryujinx.Tests.Cpu Assert.Multiple(() => { - Assert.That(GetVectorE0(ThreadState.V0), Is.EqualTo(_X0)); + Assert.That(GetVectorE0(ThreadState.V0), Is.EqualTo(_E0)); Assert.That(GetVectorE1(ThreadState.V0), Is.EqualTo(AArch64.Vpart(64, 0, 1).ToUInt64())); }); } @@ -1216,8 +1216,8 @@ namespace Ryujinx.Tests.Cpu Opcode |= ((size & 3) << 22); Bits Op = new Bits(Opcode); - ulong _X0 = TestContext.CurrentContext.Random.NextULong(); - Vector128 V0 = MakeVectorE0(_X0); + ulong _E0 = TestContext.CurrentContext.Random.NextULong(); + Vector128 V0 = MakeVectorE0(_E0); Vector128 V1 = MakeVectorE0E1(A0, A1); Vector128 V2 = MakeVectorE0E1(B0, B1); AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2); @@ -1230,7 +1230,233 @@ namespace Ryujinx.Tests.Cpu Assert.Multiple(() => { - Assert.That(GetVectorE0(ThreadState.V0), Is.EqualTo(_X0)); + Assert.That(GetVectorE0(ThreadState.V0), Is.EqualTo(_E0)); + Assert.That(GetVectorE1(ThreadState.V0), Is.EqualTo(AArch64.Vpart(64, 0, 1).ToUInt64())); + }); + } + + [Test, Description("SABA ., ., .")] + public void Saba_V_8B_4H_2S([ValueSource("_8B4H2S_")] [Random(1)] ulong _Z, + [ValueSource("_8B4H2S_")] [Random(1)] ulong A, + [ValueSource("_8B4H2S_")] [Random(1)] ulong B, + [Values(0b00u, 0b01u, 0b10u)] uint size) // <8B, 4H, 2S> + { + uint Opcode = 0x0E227C20; // SABA V0.8B, V1.8B, V2.8B + Opcode |= ((size & 3) << 22); + Bits Op = new Bits(Opcode); + + Vector128 V0 = MakeVectorE0E1(_Z, TestContext.CurrentContext.Random.NextULong()); + 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.V(1, new Bits(A)); + AArch64.V(2, new Bits(B)); + SimdFp.Saba_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.V(64, 0).ToUInt64())); + Assert.That(GetVectorE1(ThreadState.V0), Is.Zero); + }); + } + + [Test, Pairwise, Description("SABA ., ., .")] + public void Saba_V_16B_8H_4S([ValueSource("_8B4H2S_")] [Random(1)] ulong _Z0, + [ValueSource("_8B4H2S_")] [Random(1)] ulong _Z1, + [ValueSource("_8B4H2S_")] [Random(1)] ulong A0, + [ValueSource("_8B4H2S_")] [Random(1)] ulong A1, + [ValueSource("_8B4H2S_")] [Random(1)] ulong B0, + [ValueSource("_8B4H2S_")] [Random(1)] ulong B1, + [Values(0b00u, 0b01u, 0b10u)] uint size) // <16B, 8H, 4S> + { + uint Opcode = 0x4E227C20; // SABA V0.16B, V1.16B, V2.16B + Opcode |= ((size & 3) << 22); + Bits Op = new Bits(Opcode); + + Vector128 V0 = MakeVectorE0E1(_Z0, _Z1); + Vector128 V1 = MakeVectorE0E1(A0, A1); + Vector128 V2 = MakeVectorE0E1(B0, B1); + AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2); + + AArch64.Vpart(0, 0, new Bits(_Z0)); + AArch64.Vpart(0, 1, new Bits(_Z1)); + AArch64.Vpart(1, 0, new Bits(A0)); + AArch64.Vpart(1, 1, new Bits(A1)); + AArch64.Vpart(2, 0, new Bits(B0)); + AArch64.Vpart(2, 1, new Bits(B1)); + SimdFp.Saba_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("SABAL{2} ., ., .")] + public void Sabal_V_8B8H_4H4S_2S2D([ValueSource("_8B4H2S_")] [Random(1)] ulong _Z0, + [ValueSource("_8B4H2S_")] [Random(1)] ulong _Z1, + [ValueSource("_8B4H2S_")] [Random(1)] ulong A0, + [ValueSource("_8B4H2S_")] [Random(1)] ulong B0, + [Values(0b00u, 0b01u, 0b10u)] uint size) // <8B, 4H, 2S> + { + uint Opcode = 0x0E225020; // SABAL V0.8H, V1.8B, V2.8B + Opcode |= ((size & 3) << 22); + Bits Op = new Bits(Opcode); + + Vector128 V0 = MakeVectorE0E1(_Z0, _Z1); + Vector128 V1 = MakeVectorE0(A0); + Vector128 V2 = MakeVectorE0(B0); + AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2); + + AArch64.Vpart(0, 0, new Bits(_Z0)); + AArch64.Vpart(0, 1, new Bits(_Z1)); + AArch64.Vpart(1, 0, new Bits(A0)); + AArch64.Vpart(2, 0, new Bits(B0)); + SimdFp.Sabal_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("SABAL{2} ., ., .")] + public void Sabal_V_16B8H_8H4S_4S2D([ValueSource("_8B4H2S_")] [Random(1)] ulong _Z0, + [ValueSource("_8B4H2S_")] [Random(1)] ulong _Z1, + [ValueSource("_8B4H2S_")] [Random(1)] ulong A1, + [ValueSource("_8B4H2S_")] [Random(1)] ulong B1, + [Values(0b00u, 0b01u, 0b10u)] uint size) // <16B, 8H, 4S> + { + uint Opcode = 0x4E225020; // SABAL2 V0.8H, V1.16B, V2.16B + Opcode |= ((size & 3) << 22); + Bits Op = new Bits(Opcode); + + Vector128 V0 = MakeVectorE0E1(_Z0, _Z1); + Vector128 V1 = MakeVectorE1(A1); + Vector128 V2 = MakeVectorE1(B1); + AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2); + + AArch64.Vpart(0, 0, new Bits(_Z0)); + AArch64.Vpart(0, 1, new Bits(_Z1)); + AArch64.Vpart(1, 1, new Bits(A1)); + AArch64.Vpart(2, 1, new Bits(B1)); + SimdFp.Sabal_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, Description("SABD ., ., .")] + public void Sabd_V_8B_4H_2S([ValueSource("_8B4H2S_")] [Random(1)] ulong A, + [ValueSource("_8B4H2S_")] [Random(1)] ulong B, + [Values(0b00u, 0b01u, 0b10u)] uint size) // <8B, 4H, 2S> + { + uint Opcode = 0x0E227420; // SABD V0.8B, V1.8B, V2.8B + Opcode |= ((size & 3) << 22); + Bits Op = new Bits(Opcode); + + Vector128 V0 = MakeVectorE0E1(TestContext.CurrentContext.Random.NextULong(), + TestContext.CurrentContext.Random.NextULong()); + Vector128 V1 = MakeVectorE0(A); + Vector128 V2 = MakeVectorE0(B); + AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2); + + AArch64.V(1, new Bits(A)); + AArch64.V(2, new Bits(B)); + SimdFp.Sabd_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.V(64, 0).ToUInt64())); + Assert.That(GetVectorE1(ThreadState.V0), Is.Zero); + }); + } + + [Test, Pairwise, Description("SABD ., ., .")] + public void Sabd_V_16B_8H_4S([ValueSource("_8B4H2S_")] [Random(1)] ulong A0, + [ValueSource("_8B4H2S_")] [Random(1)] ulong A1, + [ValueSource("_8B4H2S_")] [Random(1)] ulong B0, + [ValueSource("_8B4H2S_")] [Random(1)] ulong B1, + [Values(0b00u, 0b01u, 0b10u)] uint size) // <16B, 8H, 4S> + { + uint Opcode = 0x4E227420; // SABD V0.16B, V1.16B, V2.16B + Opcode |= ((size & 3) << 22); + Bits Op = new Bits(Opcode); + + Vector128 V0 = MakeVectorE0E1(TestContext.CurrentContext.Random.NextULong(), + TestContext.CurrentContext.Random.NextULong()); + Vector128 V1 = MakeVectorE0E1(A0, A1); + Vector128 V2 = MakeVectorE0E1(B0, B1); + AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2); + + AArch64.Vpart(1, 0, new Bits(A0)); + AArch64.Vpart(1, 1, new Bits(A1)); + AArch64.Vpart(2, 0, new Bits(B0)); + AArch64.Vpart(2, 1, new Bits(B1)); + SimdFp.Sabd_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, Description("SABDL{2} ., ., .")] + public void Sabdl_V_8B8H_4H4S_2S2D([ValueSource("_8B4H2S_")] [Random(1)] ulong A0, + [ValueSource("_8B4H2S_")] [Random(1)] ulong B0, + [Values(0b00u, 0b01u, 0b10u)] uint size) // <8B, 4H, 2S> + { + uint Opcode = 0x0E227020; // SABDL V0.8H, V1.8B, V2.8B + Opcode |= ((size & 3) << 22); + Bits Op = new Bits(Opcode); + + Vector128 V0 = MakeVectorE0E1(TestContext.CurrentContext.Random.NextULong(), + TestContext.CurrentContext.Random.NextULong()); + Vector128 V1 = MakeVectorE0(A0); + Vector128 V2 = MakeVectorE0(B0); + AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2); + + AArch64.Vpart(1, 0, new Bits(A0)); + AArch64.Vpart(2, 0, new Bits(B0)); + SimdFp.Sabdl_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, Description("SABDL{2} ., ., .")] + public void Sabdl_V_16B8H_8H4S_4S2D([ValueSource("_8B4H2S_")] [Random(1)] ulong A1, + [ValueSource("_8B4H2S_")] [Random(1)] ulong B1, + [Values(0b00u, 0b01u, 0b10u)] uint size) // <16B, 8H, 4S> + { + uint Opcode = 0x4E227020; // SABDL2 V0.8H, V1.16B, V2.16B + Opcode |= ((size & 3) << 22); + Bits Op = new Bits(Opcode); + + Vector128 V0 = MakeVectorE0E1(TestContext.CurrentContext.Random.NextULong(), + TestContext.CurrentContext.Random.NextULong()); + Vector128 V1 = MakeVectorE1(A1); + Vector128 V2 = MakeVectorE1(B1); + AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2); + + AArch64.Vpart(1, 1, new Bits(A1)); + AArch64.Vpart(2, 1, new Bits(B1)); + SimdFp.Sabdl_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())); }); } @@ -1351,8 +1577,8 @@ namespace Ryujinx.Tests.Cpu Opcode |= ((size & 3) << 22); Bits Op = new Bits(Opcode); - ulong _X0 = TestContext.CurrentContext.Random.NextULong(); - Vector128 V0 = MakeVectorE0(_X0); + ulong _E0 = TestContext.CurrentContext.Random.NextULong(); + Vector128 V0 = MakeVectorE0(_E0); Vector128 V1 = MakeVectorE0E1(A0, A1); Vector128 V2 = MakeVectorE0E1(B0, B1); AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2); @@ -1365,7 +1591,233 @@ namespace Ryujinx.Tests.Cpu Assert.Multiple(() => { - Assert.That(GetVectorE0(ThreadState.V0), Is.EqualTo(_X0)); + Assert.That(GetVectorE0(ThreadState.V0), Is.EqualTo(_E0)); + Assert.That(GetVectorE1(ThreadState.V0), Is.EqualTo(AArch64.Vpart(64, 0, 1).ToUInt64())); + }); + } + + [Test, Description("UABA ., ., .")] + public void Uaba_V_8B_4H_2S([ValueSource("_8B4H2S_")] [Random(1)] ulong _Z, + [ValueSource("_8B4H2S_")] [Random(1)] ulong A, + [ValueSource("_8B4H2S_")] [Random(1)] ulong B, + [Values(0b00u, 0b01u, 0b10u)] uint size) // <8B, 4H, 2S> + { + uint Opcode = 0x2E227C20; // UABA V0.8B, V1.8B, V2.8B + Opcode |= ((size & 3) << 22); + Bits Op = new Bits(Opcode); + + Vector128 V0 = MakeVectorE0E1(_Z, TestContext.CurrentContext.Random.NextULong()); + 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.V(1, new Bits(A)); + AArch64.V(2, new Bits(B)); + SimdFp.Uaba_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.V(64, 0).ToUInt64())); + Assert.That(GetVectorE1(ThreadState.V0), Is.Zero); + }); + } + + [Test, Pairwise, Description("UABA ., ., .")] + public void Uaba_V_16B_8H_4S([ValueSource("_8B4H2S_")] [Random(1)] ulong _Z0, + [ValueSource("_8B4H2S_")] [Random(1)] ulong _Z1, + [ValueSource("_8B4H2S_")] [Random(1)] ulong A0, + [ValueSource("_8B4H2S_")] [Random(1)] ulong A1, + [ValueSource("_8B4H2S_")] [Random(1)] ulong B0, + [ValueSource("_8B4H2S_")] [Random(1)] ulong B1, + [Values(0b00u, 0b01u, 0b10u)] uint size) // <16B, 8H, 4S> + { + uint Opcode = 0x6E227C20; // UABA V0.16B, V1.16B, V2.16B + Opcode |= ((size & 3) << 22); + Bits Op = new Bits(Opcode); + + Vector128 V0 = MakeVectorE0E1(_Z0, _Z1); + Vector128 V1 = MakeVectorE0E1(A0, A1); + Vector128 V2 = MakeVectorE0E1(B0, B1); + AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2); + + AArch64.Vpart(0, 0, new Bits(_Z0)); + AArch64.Vpart(0, 1, new Bits(_Z1)); + AArch64.Vpart(1, 0, new Bits(A0)); + AArch64.Vpart(1, 1, new Bits(A1)); + AArch64.Vpart(2, 0, new Bits(B0)); + AArch64.Vpart(2, 1, new Bits(B1)); + SimdFp.Uaba_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("UABAL{2} ., ., .")] + public void Uabal_V_8B8H_4H4S_2S2D([ValueSource("_8B4H2S_")] [Random(1)] ulong _Z0, + [ValueSource("_8B4H2S_")] [Random(1)] ulong _Z1, + [ValueSource("_8B4H2S_")] [Random(1)] ulong A0, + [ValueSource("_8B4H2S_")] [Random(1)] ulong B0, + [Values(0b00u, 0b01u, 0b10u)] uint size) // <8B, 4H, 2S> + { + uint Opcode = 0x2E225020; // UABAL V0.8H, V1.8B, V2.8B + Opcode |= ((size & 3) << 22); + Bits Op = new Bits(Opcode); + + Vector128 V0 = MakeVectorE0E1(_Z0, _Z1); + Vector128 V1 = MakeVectorE0(A0); + Vector128 V2 = MakeVectorE0(B0); + AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2); + + AArch64.Vpart(0, 0, new Bits(_Z0)); + AArch64.Vpart(0, 1, new Bits(_Z1)); + AArch64.Vpart(1, 0, new Bits(A0)); + AArch64.Vpart(2, 0, new Bits(B0)); + SimdFp.Uabal_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("UABAL{2} ., ., .")] + public void Uabal_V_16B8H_8H4S_4S2D([ValueSource("_8B4H2S_")] [Random(1)] ulong _Z0, + [ValueSource("_8B4H2S_")] [Random(1)] ulong _Z1, + [ValueSource("_8B4H2S_")] [Random(1)] ulong A1, + [ValueSource("_8B4H2S_")] [Random(1)] ulong B1, + [Values(0b00u, 0b01u, 0b10u)] uint size) // <16B, 8H, 4S> + { + uint Opcode = 0x6E225020; // UABAL2 V0.8H, V1.16B, V2.16B + Opcode |= ((size & 3) << 22); + Bits Op = new Bits(Opcode); + + Vector128 V0 = MakeVectorE0E1(_Z0, _Z1); + Vector128 V1 = MakeVectorE1(A1); + Vector128 V2 = MakeVectorE1(B1); + AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2); + + AArch64.Vpart(0, 0, new Bits(_Z0)); + AArch64.Vpart(0, 1, new Bits(_Z1)); + AArch64.Vpart(1, 1, new Bits(A1)); + AArch64.Vpart(2, 1, new Bits(B1)); + SimdFp.Uabal_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, Description("UABD ., ., .")] + public void Uabd_V_8B_4H_2S([ValueSource("_8B4H2S_")] [Random(1)] ulong A, + [ValueSource("_8B4H2S_")] [Random(1)] ulong B, + [Values(0b00u, 0b01u, 0b10u)] uint size) // <8B, 4H, 2S> + { + uint Opcode = 0x2E227420; // UABD V0.8B, V1.8B, V2.8B + Opcode |= ((size & 3) << 22); + Bits Op = new Bits(Opcode); + + Vector128 V0 = MakeVectorE0E1(TestContext.CurrentContext.Random.NextULong(), + TestContext.CurrentContext.Random.NextULong()); + Vector128 V1 = MakeVectorE0(A); + Vector128 V2 = MakeVectorE0(B); + AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2); + + AArch64.V(1, new Bits(A)); + AArch64.V(2, new Bits(B)); + SimdFp.Uabd_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.V(64, 0).ToUInt64())); + Assert.That(GetVectorE1(ThreadState.V0), Is.Zero); + }); + } + + [Test, Pairwise, Description("UABD ., ., .")] + public void Uabd_V_16B_8H_4S([ValueSource("_8B4H2S_")] [Random(1)] ulong A0, + [ValueSource("_8B4H2S_")] [Random(1)] ulong A1, + [ValueSource("_8B4H2S_")] [Random(1)] ulong B0, + [ValueSource("_8B4H2S_")] [Random(1)] ulong B1, + [Values(0b00u, 0b01u, 0b10u)] uint size) // <16B, 8H, 4S> + { + uint Opcode = 0x6E227420; // UABD V0.16B, V1.16B, V2.16B + Opcode |= ((size & 3) << 22); + Bits Op = new Bits(Opcode); + + Vector128 V0 = MakeVectorE0E1(TestContext.CurrentContext.Random.NextULong(), + TestContext.CurrentContext.Random.NextULong()); + Vector128 V1 = MakeVectorE0E1(A0, A1); + Vector128 V2 = MakeVectorE0E1(B0, B1); + AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2); + + AArch64.Vpart(1, 0, new Bits(A0)); + AArch64.Vpart(1, 1, new Bits(A1)); + AArch64.Vpart(2, 0, new Bits(B0)); + AArch64.Vpart(2, 1, new Bits(B1)); + SimdFp.Uabd_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, Description("UABDL{2} ., ., .")] + public void Uabdl_V_8B8H_4H4S_2S2D([ValueSource("_8B4H2S_")] [Random(1)] ulong A0, + [ValueSource("_8B4H2S_")] [Random(1)] ulong B0, + [Values(0b00u, 0b01u, 0b10u)] uint size) // <8B, 4H, 2S> + { + uint Opcode = 0x2E227020; // UABDL V0.8H, V1.8B, V2.8B + Opcode |= ((size & 3) << 22); + Bits Op = new Bits(Opcode); + + Vector128 V0 = MakeVectorE0E1(TestContext.CurrentContext.Random.NextULong(), + TestContext.CurrentContext.Random.NextULong()); + Vector128 V1 = MakeVectorE0(A0); + Vector128 V2 = MakeVectorE0(B0); + AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2); + + AArch64.Vpart(1, 0, new Bits(A0)); + AArch64.Vpart(2, 0, new Bits(B0)); + SimdFp.Uabdl_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, Description("UABDL{2} ., ., .")] + public void Uabdl_V_16B8H_8H4S_4S2D([ValueSource("_8B4H2S_")] [Random(1)] ulong A1, + [ValueSource("_8B4H2S_")] [Random(1)] ulong B1, + [Values(0b00u, 0b01u, 0b10u)] uint size) // <16B, 8H, 4S> + { + uint Opcode = 0x6E227020; // UABDL2 V0.8H, V1.16B, V2.16B + Opcode |= ((size & 3) << 22); + Bits Op = new Bits(Opcode); + + Vector128 V0 = MakeVectorE0E1(TestContext.CurrentContext.Random.NextULong(), + TestContext.CurrentContext.Random.NextULong()); + Vector128 V1 = MakeVectorE1(A1); + Vector128 V2 = MakeVectorE1(B1); + AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2); + + AArch64.Vpart(1, 1, new Bits(A1)); + AArch64.Vpart(2, 1, new Bits(B1)); + SimdFp.Uabdl_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())); }); } diff --git a/Ryujinx.Tests/Cpu/Tester/Instructions.cs b/Ryujinx.Tests/Cpu/Tester/Instructions.cs index 5738f5993..1590019a7 100644 --- a/Ryujinx.Tests/Cpu/Tester/Instructions.cs +++ b/Ryujinx.Tests/Cpu/Tester/Instructions.cs @@ -1974,13 +1974,13 @@ namespace Ryujinx.Tests.Cpu.Tester switch (Bits.Concat(op, U)) { - default: case Bits bits when bits == "00": comparison = CompareOp.CompareOp_GT; break; case Bits bits when bits == "01": comparison = CompareOp.CompareOp_GE; break; + default: case Bits bits when bits == "10": comparison = CompareOp.CompareOp_EQ; break; @@ -2004,13 +2004,13 @@ namespace Ryujinx.Tests.Cpu.Tester switch (comparison) { - default: case CompareOp.CompareOp_GT: test_passed = (element > (BigInteger)0); break; case CompareOp.CompareOp_GE: test_passed = (element >= (BigInteger)0); break; + default: case CompareOp.CompareOp_EQ: test_passed = (element == (BigInteger)0); break; @@ -2048,13 +2048,13 @@ namespace Ryujinx.Tests.Cpu.Tester switch (Bits.Concat(op, U)) { - default: case Bits bits when bits == "00": comparison = CompareOp.CompareOp_GT; break; case Bits bits when bits == "01": comparison = CompareOp.CompareOp_GE; break; + default: case Bits bits when bits == "10": comparison = CompareOp.CompareOp_EQ; break; @@ -2078,13 +2078,13 @@ namespace Ryujinx.Tests.Cpu.Tester switch (comparison) { - default: case CompareOp.CompareOp_GT: test_passed = (element > (BigInteger)0); break; case CompareOp.CompareOp_GE: test_passed = (element >= (BigInteger)0); break; + default: case CompareOp.CompareOp_EQ: test_passed = (element == (BigInteger)0); break; @@ -2122,10 +2122,10 @@ namespace Ryujinx.Tests.Cpu.Tester switch (Bits.Concat(op, U)) { - default: case Bits bits when bits == "00": comparison = CompareOp.CompareOp_GT; break; + default: case Bits bits when bits == "01": comparison = CompareOp.CompareOp_GE; break; @@ -2152,10 +2152,10 @@ namespace Ryujinx.Tests.Cpu.Tester switch (comparison) { - default: case CompareOp.CompareOp_GT: test_passed = (element > (BigInteger)0); break; + default: case CompareOp.CompareOp_GE: test_passed = (element >= (BigInteger)0); break; @@ -2196,10 +2196,10 @@ namespace Ryujinx.Tests.Cpu.Tester switch (Bits.Concat(op, U)) { - default: case Bits bits when bits == "00": comparison = CompareOp.CompareOp_GT; break; + default: case Bits bits when bits == "01": comparison = CompareOp.CompareOp_GE; break; @@ -2226,10 +2226,10 @@ namespace Ryujinx.Tests.Cpu.Tester switch (comparison) { - default: case CompareOp.CompareOp_GT: test_passed = (element > (BigInteger)0); break; + default: case CompareOp.CompareOp_GE: test_passed = (element >= (BigInteger)0); break; @@ -2418,7 +2418,6 @@ namespace Ryujinx.Tests.Cpu.Tester switch (Bits.Concat(op, U)) { - default: case Bits bits when bits == "00": comparison = CompareOp.CompareOp_GT; break; @@ -2428,6 +2427,7 @@ namespace Ryujinx.Tests.Cpu.Tester case Bits bits when bits == "10": comparison = CompareOp.CompareOp_EQ; break; + default: case Bits bits when bits == "11": comparison = CompareOp.CompareOp_LE; break; @@ -2448,7 +2448,6 @@ namespace Ryujinx.Tests.Cpu.Tester switch (comparison) { - default: case CompareOp.CompareOp_GT: test_passed = (element > (BigInteger)0); break; @@ -2458,6 +2457,7 @@ namespace Ryujinx.Tests.Cpu.Tester case CompareOp.CompareOp_EQ: test_passed = (element == (BigInteger)0); break; + default: case CompareOp.CompareOp_LE: test_passed = (element <= (BigInteger)0); break; @@ -2492,7 +2492,6 @@ namespace Ryujinx.Tests.Cpu.Tester switch (Bits.Concat(op, U)) { - default: case Bits bits when bits == "00": comparison = CompareOp.CompareOp_GT; break; @@ -2502,6 +2501,7 @@ namespace Ryujinx.Tests.Cpu.Tester case Bits bits when bits == "10": comparison = CompareOp.CompareOp_EQ; break; + default: case Bits bits when bits == "11": comparison = CompareOp.CompareOp_LE; break; @@ -2522,7 +2522,6 @@ namespace Ryujinx.Tests.Cpu.Tester switch (comparison) { - default: case CompareOp.CompareOp_GT: test_passed = (element > (BigInteger)0); break; @@ -2532,6 +2531,7 @@ namespace Ryujinx.Tests.Cpu.Tester case CompareOp.CompareOp_EQ: test_passed = (element == (BigInteger)0); break; + default: case CompareOp.CompareOp_LE: test_passed = (element <= (BigInteger)0); break; @@ -2576,7 +2576,6 @@ namespace Ryujinx.Tests.Cpu.Tester switch (comparison) { - default: case CompareOp.CompareOp_GT: test_passed = (element > (BigInteger)0); break; @@ -2589,6 +2588,7 @@ namespace Ryujinx.Tests.Cpu.Tester case CompareOp.CompareOp_LE: test_passed = (element <= (BigInteger)0); break; + default: case CompareOp.CompareOp_LT: test_passed = (element < (BigInteger)0); break; @@ -2630,7 +2630,6 @@ namespace Ryujinx.Tests.Cpu.Tester switch (comparison) { - default: case CompareOp.CompareOp_GT: test_passed = (element > (BigInteger)0); break; @@ -2643,6 +2642,7 @@ namespace Ryujinx.Tests.Cpu.Tester case CompareOp.CompareOp_LE: test_passed = (element <= (BigInteger)0); break; + default: case CompareOp.CompareOp_LT: test_passed = (element < (BigInteger)0); break; @@ -2801,6 +2801,265 @@ namespace Ryujinx.Tests.Cpu.Tester V(d, result); } + // rbit_advsimd.html + public static void Rbit_V(bool Q, Bits Rn, Bits Rd) + { + /* Decode Vector */ + int d = (int)UInt(Rd); + int n = (int)UInt(Rn); + + int esize = 8; + int datasize = (Q ? 128 : 64); + int elements = datasize / 8; + + /* Operation */ + /* CheckFPAdvSIMDEnabled64(); */ + + Bits result = new Bits(datasize); + Bits operand = V(datasize, n); + Bits element; + Bits rev = new Bits(esize); + + for (int e = 0; e <= elements - 1; e++) + { + element = Elem(operand, e, esize); + + for (int i = 0; i <= esize - 1; i++) + { + rev[esize - 1 - i] = element[i]; + } + + Elem(result, e, esize, rev); + } + + V(d, result); + } + + // rev16_advsimd.html + public static void Rev16_V(bool Q, Bits size, Bits Rn, Bits Rd) + { + const bool U = false; + const bool o0 = true; + + /* Decode Vector */ + int d = (int)UInt(Rd); + int n = (int)UInt(Rn); + + // size=esize: B(0), H(1), S(1), D(S) + int esize = 8 << (int)UInt(size); + int datasize = (Q ? 128 : 64); + + // op=REVx: 64(0), 32(1), 16(2) + Bits op = Bits.Concat(o0, U); + + // => op+size: + // 64+B = 0, 64+H = 1, 64+S = 2, 64+D = X + // 32+B = 1, 32+H = 2, 32+S = X, 32+D = X + // 16+B = 2, 16+H = X, 16+S = X, 16+D = X + // 8+B = X, 8+H = X, 8+S = X, 8+D = X + // => 3-(op+size) (index bits in group) + // 64/B = 3, 64+H = 2, 64+S = 1, 64+D = X + // 32+B = 2, 32+H = 1, 32+S = X, 32+D = X + // 16+B = 1, 16+H = X, 16+S = X, 16+D = X + // 8+B = X, 8+H = X, 8+S = X, 8+D = X + + // index bits within group: 1, 2, 3 + /* if UInt(op) + UInt(size) >= 3 then UnallocatedEncoding(); */ + + int container_size; + + switch (op) + { + default: + case Bits bits when bits == "10": + container_size = 16; + break; + case Bits bits when bits == "01": + container_size = 32; + break; + case Bits bits when bits == "00": + container_size = 64; + break; + } + + int containers = datasize / container_size; + int elements_per_container = container_size / esize; + + /* Operation */ + /* CheckFPAdvSIMDEnabled64(); */ + + Bits result = new Bits(datasize); + Bits operand = V(datasize, n); + + int element = 0; + int rev_element; + + for (int c = 0; c <= containers - 1; c++) + { + rev_element = element + elements_per_container - 1; + + for (int e = 0; e <= elements_per_container - 1; e++) + { + Elem(result, rev_element, esize, Elem(operand, element, esize)); + + element = element + 1; + rev_element = rev_element - 1; + } + } + + V(d, result); + } + + // rev32_advsimd.html + public static void Rev32_V(bool Q, Bits size, Bits Rn, Bits Rd) + { + const bool U = true; + const bool o0 = false; + + /* Decode Vector */ + int d = (int)UInt(Rd); + int n = (int)UInt(Rn); + + // size=esize: B(0), H(1), S(1), D(S) + int esize = 8 << (int)UInt(size); + int datasize = (Q ? 128 : 64); + + // op=REVx: 64(0), 32(1), 16(2) + Bits op = Bits.Concat(o0, U); + + // => op+size: + // 64+B = 0, 64+H = 1, 64+S = 2, 64+D = X + // 32+B = 1, 32+H = 2, 32+S = X, 32+D = X + // 16+B = 2, 16+H = X, 16+S = X, 16+D = X + // 8+B = X, 8+H = X, 8+S = X, 8+D = X + // => 3-(op+size) (index bits in group) + // 64/B = 3, 64+H = 2, 64+S = 1, 64+D = X + // 32+B = 2, 32+H = 1, 32+S = X, 32+D = X + // 16+B = 1, 16+H = X, 16+S = X, 16+D = X + // 8+B = X, 8+H = X, 8+S = X, 8+D = X + + // index bits within group: 1, 2, 3 + /* if UInt(op) + UInt(size) >= 3 then UnallocatedEncoding(); */ + + int container_size; + + switch (op) + { + case Bits bits when bits == "10": + container_size = 16; + break; + default: + case Bits bits when bits == "01": + container_size = 32; + break; + case Bits bits when bits == "00": + container_size = 64; + break; + } + + int containers = datasize / container_size; + int elements_per_container = container_size / esize; + + /* Operation */ + /* CheckFPAdvSIMDEnabled64(); */ + + Bits result = new Bits(datasize); + Bits operand = V(datasize, n); + + int element = 0; + int rev_element; + + for (int c = 0; c <= containers - 1; c++) + { + rev_element = element + elements_per_container - 1; + + for (int e = 0; e <= elements_per_container - 1; e++) + { + Elem(result, rev_element, esize, Elem(operand, element, esize)); + + element = element + 1; + rev_element = rev_element - 1; + } + } + + V(d, result); + } + + // rev64_advsimd.html + public static void Rev64_V(bool Q, Bits size, Bits Rn, Bits Rd) + { + const bool U = false; + const bool o0 = false; + + /* Decode Vector */ + int d = (int)UInt(Rd); + int n = (int)UInt(Rn); + + // size=esize: B(0), H(1), S(1), D(S) + int esize = 8 << (int)UInt(size); + int datasize = (Q ? 128 : 64); + + // op=REVx: 64(0), 32(1), 16(2) + Bits op = Bits.Concat(o0, U); + + // => op+size: + // 64+B = 0, 64+H = 1, 64+S = 2, 64+D = X + // 32+B = 1, 32+H = 2, 32+S = X, 32+D = X + // 16+B = 2, 16+H = X, 16+S = X, 16+D = X + // 8+B = X, 8+H = X, 8+S = X, 8+D = X + // => 3-(op+size) (index bits in group) + // 64/B = 3, 64+H = 2, 64+S = 1, 64+D = X + // 32+B = 2, 32+H = 1, 32+S = X, 32+D = X + // 16+B = 1, 16+H = X, 16+S = X, 16+D = X + // 8+B = X, 8+H = X, 8+S = X, 8+D = X + + // index bits within group: 1, 2, 3 + /* if UInt(op) + UInt(size) >= 3 then UnallocatedEncoding(); */ + + int container_size; + + switch (op) + { + case Bits bits when bits == "10": + container_size = 16; + break; + case Bits bits when bits == "01": + container_size = 32; + break; + default: + case Bits bits when bits == "00": + container_size = 64; + break; + } + + int containers = datasize / container_size; + int elements_per_container = container_size / esize; + + /* Operation */ + /* CheckFPAdvSIMDEnabled64(); */ + + Bits result = new Bits(datasize); + Bits operand = V(datasize, n); + + int element = 0; + int rev_element; + + for (int c = 0; c <= containers - 1; c++) + { + rev_element = element + elements_per_container - 1; + + for (int e = 0; e <= elements_per_container - 1; e++) + { + Elem(result, rev_element, esize, Elem(operand, element, esize)); + + element = element + 1; + rev_element = rev_element - 1; + } + } + + V(d, result); + } + // sqxtn_advsimd.html#SQXTN_asisdmisc_N public static void Sqxtn_S(Bits size, Bits Rn, Bits Rd) { @@ -4074,6 +4333,184 @@ namespace Ryujinx.Tests.Cpu.Tester Vpart(d, part, result); } + // saba_advsimd.html + public static void Saba_V(bool Q, Bits size, Bits Rm, Bits Rn, Bits Rd) + { + const bool U = false; + const bool ac = 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 = (Q ? 128 : 64); + int elements = datasize / esize; + + bool unsigned = (U == true); + bool accumulate = (ac == true); + + /* Operation */ + /* CheckFPAdvSIMDEnabled64(); */ + + Bits operand1 = V(datasize, n); + Bits operand2 = V(datasize, m); + BigInteger element1; + BigInteger element2; + Bits absdiff; + + Bits result = (accumulate ? V(datasize, d) : Zeros(datasize)); + + for (int e = 0; e <= elements - 1; e++) + { + element1 = Int(Elem(operand1, e, esize), unsigned); + element2 = Int(Elem(operand2, e, esize), unsigned); + + absdiff = Abs(element1 - element2).SubBigInteger(esize - 1, 0); + + Elem(result, e, esize, Elem(result, e, esize) + absdiff); + } + + V(d, result); + } + + // sabal_advsimd.html + public static void Sabal_V(bool Q, Bits size, Bits Rm, Bits Rn, Bits Rd) + { + const bool U = false; + const bool op = 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 unsigned = (U == true); + bool accumulate = (op == false); + + /* Operation */ + /* CheckFPAdvSIMDEnabled64(); */ + + Bits operand1 = Vpart(datasize, n, part); + Bits operand2 = Vpart(datasize, m, part); + BigInteger element1; + BigInteger element2; + Bits absdiff; + + Bits result = (accumulate ? V(2 * datasize, d) : Zeros(2 * datasize)); + + for (int e = 0; e <= elements - 1; e++) + { + element1 = Int(Elem(operand1, e, esize), unsigned); + element2 = Int(Elem(operand2, e, esize), unsigned); + + absdiff = Abs(element1 - element2).SubBigInteger(2 * esize - 1, 0); + + Elem(result, e, 2 * esize, Elem(result, e, 2 * esize) + absdiff); + } + + V(d, result); + } + + // sabd_advsimd.html + public static void Sabd_V(bool Q, Bits size, Bits Rm, Bits Rn, Bits Rd) + { + const bool U = false; + const bool ac = 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 = (Q ? 128 : 64); + int elements = datasize / esize; + + bool unsigned = (U == true); + bool accumulate = (ac == true); + + /* Operation */ + /* CheckFPAdvSIMDEnabled64(); */ + + Bits operand1 = V(datasize, n); + Bits operand2 = V(datasize, m); + BigInteger element1; + BigInteger element2; + Bits absdiff; + + Bits result = (accumulate ? V(datasize, d) : Zeros(datasize)); + + for (int e = 0; e <= elements - 1; e++) + { + element1 = Int(Elem(operand1, e, esize), unsigned); + element2 = Int(Elem(operand2, e, esize), unsigned); + + absdiff = Abs(element1 - element2).SubBigInteger(esize - 1, 0); + + Elem(result, e, esize, Elem(result, e, esize) + absdiff); + } + + V(d, result); + } + + // sabdl_advsimd.html + public static void Sabdl_V(bool Q, Bits size, Bits Rm, Bits Rn, Bits Rd) + { + const bool U = false; + const bool op = 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 unsigned = (U == true); + bool accumulate = (op == false); + + /* Operation */ + /* CheckFPAdvSIMDEnabled64(); */ + + Bits operand1 = Vpart(datasize, n, part); + Bits operand2 = Vpart(datasize, m, part); + BigInteger element1; + BigInteger element2; + Bits absdiff; + + Bits result = (accumulate ? V(2 * datasize, d) : Zeros(2 * datasize)); + + for (int e = 0; e <= elements - 1; e++) + { + element1 = Int(Elem(operand1, e, esize), unsigned); + element2 = Int(Elem(operand2, e, esize), unsigned); + + absdiff = Abs(element1 - element2).SubBigInteger(2 * esize - 1, 0); + + Elem(result, e, 2 * esize, Elem(result, e, 2 * esize) + absdiff); + } + + V(d, result); + } + // sub_advsimd.html#SUB_asisdsame_only public static void Sub_S(Bits size, Bits Rm, Bits Rn, Bits Rd) { @@ -4217,6 +4654,184 @@ namespace Ryujinx.Tests.Cpu.Tester Vpart(d, part, result); } + + // uaba_advsimd.html + public static void Uaba_V(bool Q, Bits size, Bits Rm, Bits Rn, Bits Rd) + { + const bool U = true; + const bool ac = 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 = (Q ? 128 : 64); + int elements = datasize / esize; + + bool unsigned = (U == true); + bool accumulate = (ac == true); + + /* Operation */ + /* CheckFPAdvSIMDEnabled64(); */ + + Bits operand1 = V(datasize, n); + Bits operand2 = V(datasize, m); + BigInteger element1; + BigInteger element2; + Bits absdiff; + + Bits result = (accumulate ? V(datasize, d) : Zeros(datasize)); + + for (int e = 0; e <= elements - 1; e++) + { + element1 = Int(Elem(operand1, e, esize), unsigned); + element2 = Int(Elem(operand2, e, esize), unsigned); + + absdiff = Abs(element1 - element2).SubBigInteger(esize - 1, 0); + + Elem(result, e, esize, Elem(result, e, esize) + absdiff); + } + + V(d, result); + } + + // uabal_advsimd.html + public static void Uabal_V(bool Q, Bits size, Bits Rm, Bits Rn, Bits Rd) + { + const bool U = true; + const bool op = 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 unsigned = (U == true); + bool accumulate = (op == false); + + /* Operation */ + /* CheckFPAdvSIMDEnabled64(); */ + + Bits operand1 = Vpart(datasize, n, part); + Bits operand2 = Vpart(datasize, m, part); + BigInteger element1; + BigInteger element2; + Bits absdiff; + + Bits result = (accumulate ? V(2 * datasize, d) : Zeros(2 * datasize)); + + for (int e = 0; e <= elements - 1; e++) + { + element1 = Int(Elem(operand1, e, esize), unsigned); + element2 = Int(Elem(operand2, e, esize), unsigned); + + absdiff = Abs(element1 - element2).SubBigInteger(2 * esize - 1, 0); + + Elem(result, e, 2 * esize, Elem(result, e, 2 * esize) + absdiff); + } + + V(d, result); + } + + // uabd_advsimd.html + public static void Uabd_V(bool Q, Bits size, Bits Rm, Bits Rn, Bits Rd) + { + const bool U = true; + const bool ac = 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 = (Q ? 128 : 64); + int elements = datasize / esize; + + bool unsigned = (U == true); + bool accumulate = (ac == true); + + /* Operation */ + /* CheckFPAdvSIMDEnabled64(); */ + + Bits operand1 = V(datasize, n); + Bits operand2 = V(datasize, m); + BigInteger element1; + BigInteger element2; + Bits absdiff; + + Bits result = (accumulate ? V(datasize, d) : Zeros(datasize)); + + for (int e = 0; e <= elements - 1; e++) + { + element1 = Int(Elem(operand1, e, esize), unsigned); + element2 = Int(Elem(operand2, e, esize), unsigned); + + absdiff = Abs(element1 - element2).SubBigInteger(esize - 1, 0); + + Elem(result, e, esize, Elem(result, e, esize) + absdiff); + } + + V(d, result); + } + + // uabdl_advsimd.html + public static void Uabdl_V(bool Q, Bits size, Bits Rm, Bits Rn, Bits Rd) + { + const bool U = true; + const bool op = 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 unsigned = (U == true); + bool accumulate = (op == false); + + /* Operation */ + /* CheckFPAdvSIMDEnabled64(); */ + + Bits operand1 = Vpart(datasize, n, part); + Bits operand2 = Vpart(datasize, m, part); + BigInteger element1; + BigInteger element2; + Bits absdiff; + + Bits result = (accumulate ? V(2 * datasize, d) : Zeros(2 * datasize)); + + for (int e = 0; e <= elements - 1; e++) + { + element1 = Int(Elem(operand1, e, esize), unsigned); + element2 = Int(Elem(operand2, e, esize), unsigned); + + absdiff = Abs(element1 - element2).SubBigInteger(2 * esize - 1, 0); + + Elem(result, e, 2 * esize, Elem(result, e, 2 * esize) + absdiff); + } + + V(d, result); + } #endregion } } diff --git a/Ryujinx.Tests/Ryujinx.Tests.csproj b/Ryujinx.Tests/Ryujinx.Tests.csproj index 6ff0fcf4d..b4ba379a1 100644 --- a/Ryujinx.Tests/Ryujinx.Tests.csproj +++ b/Ryujinx.Tests/Ryujinx.Tests.csproj @@ -1,7 +1,7 @@  netcoreapp2.1 - win10-x64 + win10-x64;osx-x64;linux-x64 Exe false diff --git a/Ryujinx/Ryujinx.csproj b/Ryujinx/Ryujinx.csproj index 6f717675c..443e7bbad 100644 --- a/Ryujinx/Ryujinx.csproj +++ b/Ryujinx/Ryujinx.csproj @@ -3,7 +3,7 @@ Exe netcoreapp2.1 true - win10-x64;osx-x64 + win10-x64;osx-x64;linux-x64