diff options
84 files changed, 5132 insertions, 1416 deletions
diff --git a/ChocolArm64/AOpCodeTable.cs b/ChocolArm64/AOpCodeTable.cs index 689e0392..74d4915b 100644 --- a/ChocolArm64/AOpCodeTable.cs +++ b/ChocolArm64/AOpCodeTable.cs @@ -45,7 +45,7 @@ namespace ChocolArm64 SetA64("11101010xx0xxxxxxxxxxxxxxxxxxxxx", AInstEmit.Ands, typeof(AOpCodeAluRs)); SetA64("x0011010110xxxxx001010xxxxxxxxxx", AInstEmit.Asrv, typeof(AOpCodeAluRs)); SetA64("000101xxxxxxxxxxxxxxxxxxxxxxxxxx", AInstEmit.B, typeof(AOpCodeBImmAl)); - SetA64("01010100xxxxxxxxxxxxxxxxxxxxxxxx", AInstEmit.B_Cond, typeof(AOpCodeBImmCond)); + SetA64("01010100xxxxxxxxxxxxxxxxxxx0xxxx", AInstEmit.B_Cond, typeof(AOpCodeBImmCond)); SetA64("00110011000xxxxx0xxxxxxxxxxxxxxx", AInstEmit.Bfm, typeof(AOpCodeBfm)); SetA64("1011001101xxxxxxxxxxxxxxxxxxxxxx", AInstEmit.Bfm, typeof(AOpCodeBfm)); SetA64("00001010xx1xxxxx0xxxxxxxxxxxxxxx", AInstEmit.Bic, typeof(AOpCodeAluRs)); @@ -53,8 +53,8 @@ namespace ChocolArm64 SetA64("01101010xx1xxxxx0xxxxxxxxxxxxxxx", AInstEmit.Bics, typeof(AOpCodeAluRs)); SetA64("11101010xx1xxxxxxxxxxxxxxxxxxxxx", AInstEmit.Bics, typeof(AOpCodeAluRs)); SetA64("100101xxxxxxxxxxxxxxxxxxxxxxxxxx", AInstEmit.Bl, typeof(AOpCodeBImmAl)); - SetA64("11010110001xxxxx000000xxxxxxxxxx", AInstEmit.Blr, typeof(AOpCodeBReg)); - SetA64("11010110000xxxxx000000xxxxxxxxxx", AInstEmit.Br, typeof(AOpCodeBReg)); + SetA64("1101011000111111000000xxxxx00000", AInstEmit.Blr, typeof(AOpCodeBReg)); + SetA64("1101011000011111000000xxxxx00000", AInstEmit.Br, typeof(AOpCodeBReg)); SetA64("11010100001xxxxxxxxxxxxxxxx00000", AInstEmit.Brk, typeof(AOpCodeException)); SetA64("x0110101xxxxxxxxxxxxxxxxxxxxxxxx", AInstEmit.Cbnz, typeof(AOpCodeBImmCmp)); SetA64("x0110100xxxxxxxxxxxxxxxxxxxxxxxx", AInstEmit.Cbz, typeof(AOpCodeBImmCmp)); @@ -127,7 +127,7 @@ namespace ChocolArm64 SetA64("11111000100xxxxxxxxx00xxxxxxxxxx", AInstEmit.Pfrm, typeof(AOpCodeMemImm)); SetA64("11011000xxxxxxxxxxxxxxxxxxxxxxxx", AInstEmit.Pfrm, typeof(AOpCodeMemLit)); SetA64("x101101011000000000000xxxxxxxxxx", AInstEmit.Rbit, typeof(AOpCodeAlu)); - SetA64("11010110010xxxxx000000xxxxxxxxxx", AInstEmit.Ret, typeof(AOpCodeBReg)); + SetA64("1101011001011111000000xxxxx00000", AInstEmit.Ret, typeof(AOpCodeBReg)); SetA64("x101101011000000000001xxxxxxxxxx", AInstEmit.Rev16, typeof(AOpCodeAlu)); SetA64("x101101011000000000010xxxxxxxxxx", AInstEmit.Rev32, typeof(AOpCodeAlu)); SetA64("1101101011000000000011xxxxxxxxxx", AInstEmit.Rev64, typeof(AOpCodeAlu)); @@ -267,11 +267,13 @@ namespace ChocolArm64 SetA64("0>1011100<1xxxxx111111xxxxxxxxxx", AInstEmit.Fdiv_V, typeof(AOpCodeSimdReg)); SetA64("000111110x0xxxxx0xxxxxxxxxxxxxxx", AInstEmit.Fmadd_S, typeof(AOpCodeSimdReg)); SetA64("000111100x1xxxxx010010xxxxxxxxxx", AInstEmit.Fmax_S, typeof(AOpCodeSimdReg)); - SetA64("0x0011100x1xxxxx111101xxxxxxxxxx", AInstEmit.Fmax_V, typeof(AOpCodeSimdReg)); + SetA64("0>0011100<1xxxxx111101xxxxxxxxxx", AInstEmit.Fmax_V, typeof(AOpCodeSimdReg)); SetA64("000111100x1xxxxx011010xxxxxxxxxx", AInstEmit.Fmaxnm_S, typeof(AOpCodeSimdReg)); + SetA64("0>0011100<1xxxxx110001xxxxxxxxxx", AInstEmit.Fmaxnm_V, typeof(AOpCodeSimdReg)); SetA64("000111100x1xxxxx010110xxxxxxxxxx", AInstEmit.Fmin_S, typeof(AOpCodeSimdReg)); - SetA64("0x0011101x1xxxxx111101xxxxxxxxxx", AInstEmit.Fmin_V, typeof(AOpCodeSimdReg)); + SetA64("0>0011101<1xxxxx111101xxxxxxxxxx", AInstEmit.Fmin_V, typeof(AOpCodeSimdReg)); SetA64("000111100x1xxxxx011110xxxxxxxxxx", AInstEmit.Fminnm_S, typeof(AOpCodeSimdReg)); + SetA64("0>0011101<1xxxxx110001xxxxxxxxxx", AInstEmit.Fminnm_V, typeof(AOpCodeSimdReg)); SetA64("010111111<<xxxxx0001x0xxxxxxxxxx", AInstEmit.Fmla_Se, typeof(AOpCodeSimdRegElemF)); SetA64("0>0011100<1xxxxx110011xxxxxxxxxx", AInstEmit.Fmla_V, typeof(AOpCodeSimdReg)); SetA64("0x0011111<<xxxxx0001x0xxxxxxxxxx", AInstEmit.Fmla_Ve, typeof(AOpCodeSimdRegElemF)); @@ -374,7 +376,23 @@ namespace ChocolArm64 SetA64("0x001110<<1xxxxx100000xxxxxxxxxx", AInstEmit.Smlal_V, typeof(AOpCodeSimdReg)); SetA64("0x001110<<1xxxxx101000xxxxxxxxxx", AInstEmit.Smlsl_V, typeof(AOpCodeSimdReg)); SetA64("0x001110<<1xxxxx110000xxxxxxxxxx", AInstEmit.Smull_V, typeof(AOpCodeSimdReg)); + SetA64("01011110xx100000011110xxxxxxxxxx", AInstEmit.Sqabs_S, typeof(AOpCodeSimd)); + SetA64("0>001110<<100000011110xxxxxxxxxx", AInstEmit.Sqabs_V, typeof(AOpCodeSimd)); + SetA64("01011110xx1xxxxx000011xxxxxxxxxx", AInstEmit.Sqadd_S, typeof(AOpCodeSimdReg)); + SetA64("0>001110<<1xxxxx000011xxxxxxxxxx", AInstEmit.Sqadd_V, typeof(AOpCodeSimdReg)); + SetA64("01011110011xxxxx101101xxxxxxxxxx", AInstEmit.Sqdmulh_S, typeof(AOpCodeSimdReg)); + SetA64("01011110101xxxxx101101xxxxxxxxxx", AInstEmit.Sqdmulh_S, typeof(AOpCodeSimdReg)); + SetA64("0x001110011xxxxx101101xxxxxxxxxx", AInstEmit.Sqdmulh_V, typeof(AOpCodeSimdReg)); + SetA64("0x001110101xxxxx101101xxxxxxxxxx", AInstEmit.Sqdmulh_V, typeof(AOpCodeSimdReg)); + SetA64("01111110xx100000011110xxxxxxxxxx", AInstEmit.Sqneg_S, typeof(AOpCodeSimd)); + SetA64("0>101110<<100000011110xxxxxxxxxx", AInstEmit.Sqneg_V, typeof(AOpCodeSimd)); + SetA64("01111110011xxxxx101101xxxxxxxxxx", AInstEmit.Sqrdmulh_S, typeof(AOpCodeSimdReg)); + SetA64("01111110101xxxxx101101xxxxxxxxxx", AInstEmit.Sqrdmulh_S, typeof(AOpCodeSimdReg)); + SetA64("0x101110011xxxxx101101xxxxxxxxxx", AInstEmit.Sqrdmulh_V, typeof(AOpCodeSimdReg)); + SetA64("0x101110101xxxxx101101xxxxxxxxxx", AInstEmit.Sqrdmulh_V, typeof(AOpCodeSimdReg)); SetA64("0x00111100>>>xxx100111xxxxxxxxxx", AInstEmit.Sqrshrn_V, typeof(AOpCodeSimdShImm)); + SetA64("01011110xx1xxxxx001011xxxxxxxxxx", AInstEmit.Sqsub_S, typeof(AOpCodeSimdReg)); + SetA64("0>001110<<1xxxxx001011xxxxxxxxxx", AInstEmit.Sqsub_V, typeof(AOpCodeSimdReg)); SetA64("01011110<<100001010010xxxxxxxxxx", AInstEmit.Sqxtn_S, typeof(AOpCodeSimd)); SetA64("0x001110<<100001010010xxxxxxxxxx", AInstEmit.Sqxtn_V, typeof(AOpCodeSimd)); SetA64("01111110<<100001001010xxxxxxxxxx", AInstEmit.Sqxtun_S, typeof(AOpCodeSimd)); @@ -402,6 +420,8 @@ namespace ChocolArm64 SetA64("01111110111xxxxx100001xxxxxxxxxx", AInstEmit.Sub_S, typeof(AOpCodeSimdReg)); SetA64("0>101110<<1xxxxx100001xxxxxxxxxx", AInstEmit.Sub_V, typeof(AOpCodeSimdReg)); SetA64("0x001110<<1xxxxx011000xxxxxxxxxx", AInstEmit.Subhn_V, typeof(AOpCodeSimdReg)); + SetA64("01011110xx100000001110xxxxxxxxxx", AInstEmit.Suqadd_S, typeof(AOpCodeSimd)); + SetA64("0>001110<<100000001110xxxxxxxxxx", AInstEmit.Suqadd_V, typeof(AOpCodeSimd)); 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)); @@ -423,6 +443,10 @@ namespace ChocolArm64 SetA64("0x101110<<1xxxxx101011xxxxxxxxxx", AInstEmit.Uminp_V, typeof(AOpCodeSimdReg)); SetA64("0x001110000xxxxx001111xxxxxxxxxx", AInstEmit.Umov_S, typeof(AOpCodeSimdIns)); SetA64("0x101110<<1xxxxx110000xxxxxxxxxx", AInstEmit.Umull_V, typeof(AOpCodeSimdReg)); + SetA64("01111110xx1xxxxx000011xxxxxxxxxx", AInstEmit.Uqadd_S, typeof(AOpCodeSimdReg)); + SetA64("0>101110<<1xxxxx000011xxxxxxxxxx", AInstEmit.Uqadd_V, typeof(AOpCodeSimdReg)); + SetA64("01111110xx1xxxxx001011xxxxxxxxxx", AInstEmit.Uqsub_S, typeof(AOpCodeSimdReg)); + SetA64("0>101110<<1xxxxx001011xxxxxxxxxx", AInstEmit.Uqsub_V, typeof(AOpCodeSimdReg)); SetA64("01111110<<100001010010xxxxxxxxxx", AInstEmit.Uqxtn_S, typeof(AOpCodeSimd)); SetA64("0x101110<<100001010010xxxxxxxxxx", AInstEmit.Uqxtn_V, typeof(AOpCodeSimd)); SetA64("0>101110<<1xxxxx010001xxxxxxxxxx", AInstEmit.Ushl_V, typeof(AOpCodeSimdReg)); @@ -430,6 +454,8 @@ namespace ChocolArm64 SetA64("0111111101xxxxxx000001xxxxxxxxxx", AInstEmit.Ushr_S, typeof(AOpCodeSimdShImm)); SetA64("0x10111100>>>xxx000001xxxxxxxxxx", AInstEmit.Ushr_V, typeof(AOpCodeSimdShImm)); SetA64("0110111101xxxxxx000001xxxxxxxxxx", AInstEmit.Ushr_V, typeof(AOpCodeSimdShImm)); + SetA64("01111110xx100000001110xxxxxxxxxx", AInstEmit.Usqadd_S, typeof(AOpCodeSimd)); + SetA64("0>101110<<100000001110xxxxxxxxxx", AInstEmit.Usqadd_V, typeof(AOpCodeSimd)); SetA64("0x10111100>>>xxx000101xxxxxxxxxx", AInstEmit.Usra_V, typeof(AOpCodeSimdShImm)); SetA64("0110111101xxxxxx000101xxxxxxxxxx", AInstEmit.Usra_V, typeof(AOpCodeSimdShImm)); SetA64("0x101110<<1xxxxx001100xxxxxxxxxx", AInstEmit.Usubw_V, typeof(AOpCodeSimdReg)); diff --git a/ChocolArm64/Instruction/AInstEmitMemoryEx.cs b/ChocolArm64/Instruction/AInstEmitMemoryEx.cs index c8cf9110..e59cadd4 100644 --- a/ChocolArm64/Instruction/AInstEmitMemoryEx.cs +++ b/ChocolArm64/Instruction/AInstEmitMemoryEx.cs @@ -75,7 +75,7 @@ namespace ChocolArm64.Instruction { Context.EmitLdarg(ATranslatedSub.MemoryArgIdx); Context.EmitLdtmp(); - Context.EmitLdc_I(8 << Op.Size); + Context.EmitLdc_I8(1 << Op.Size); Context.Emit(OpCodes.Add); @@ -145,7 +145,7 @@ namespace ChocolArm64.Instruction { Context.EmitLdarg(ATranslatedSub.MemoryArgIdx); Context.EmitLdint(Op.Rn); - Context.EmitLdc_I(8 << Op.Size); + Context.EmitLdc_I8(1 << Op.Size); Context.Emit(OpCodes.Add); diff --git a/ChocolArm64/Instruction/AInstEmitSimdArithmetic.cs b/ChocolArm64/Instruction/AInstEmitSimdArithmetic.cs index 8e741861..02e903f6 100644 --- a/ChocolArm64/Instruction/AInstEmitSimdArithmetic.cs +++ b/ChocolArm64/Instruction/AInstEmitSimdArithmetic.cs @@ -158,6 +158,42 @@ namespace ChocolArm64.Instruction Context.MarkLabel(LblTrue); } + private static void EmitDoublingMultiplyHighHalf(AILEmitterCtx Context, bool Round) + { + AOpCodeSimdReg Op = (AOpCodeSimdReg)Context.CurrOp; + + int ESize = 8 << Op.Size; + + Context.Emit(OpCodes.Mul); + + if (!Round) + { + Context.EmitAsr(ESize - 1); + } + else + { + long RoundConst = 1L << (ESize - 1); + + AILLabel LblTrue = new AILLabel(); + + Context.EmitLsl(1); + + Context.EmitLdc_I8(RoundConst); + + Context.Emit(OpCodes.Add); + + Context.EmitAsr(ESize); + + Context.Emit(OpCodes.Dup); + Context.EmitLdc_I8((long)int.MinValue); + Context.Emit(OpCodes.Bne_Un_S, LblTrue); + + Context.Emit(OpCodes.Neg); + + Context.MarkLabel(LblTrue); + } + } + private static void EmitHighNarrow(AILEmitterCtx Context, Action Emit, bool Round) { AOpCodeSimdReg Op = (AOpCodeSimdReg)Context.CurrOp; @@ -335,98 +371,66 @@ namespace ChocolArm64.Instruction public static void Fmax_S(AILEmitterCtx Context) { - AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp; - EmitScalarBinaryOpF(Context, () => { - if (Op.Size == 0) - { - AVectorHelper.EmitCall(Context, nameof(AVectorHelper.MaxF)); - } - else if (Op.Size == 1) - { - AVectorHelper.EmitCall(Context, nameof(AVectorHelper.Max)); - } - else - { - throw new InvalidOperationException(); - } + EmitBinarySoftFloatCall(Context, nameof(ASoftFloat.Max)); }); } public static void Fmax_V(AILEmitterCtx Context) { - AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp; + EmitVectorBinaryOpF(Context, () => + { + EmitBinarySoftFloatCall(Context, nameof(ASoftFloat.Max)); + }); + } + + public static void Fmaxnm_S(AILEmitterCtx Context) + { + EmitScalarBinaryOpF(Context, () => + { + EmitBinarySoftFloatCall(Context, nameof(ASoftFloat.MaxNum)); + }); + } + public static void Fmaxnm_V(AILEmitterCtx Context) + { EmitVectorBinaryOpF(Context, () => { - if (Op.Size == 0) - { - AVectorHelper.EmitCall(Context, nameof(AVectorHelper.MaxF)); - } - else if (Op.Size == 1) - { - AVectorHelper.EmitCall(Context, nameof(AVectorHelper.Max)); - } - else - { - throw new InvalidOperationException(); - } + EmitBinarySoftFloatCall(Context, nameof(ASoftFloat.MaxNum)); }); } public static void Fmin_S(AILEmitterCtx Context) { - AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp; - EmitScalarBinaryOpF(Context, () => { - if (Op.Size == 0) - { - AVectorHelper.EmitCall(Context, nameof(AVectorHelper.MinF)); - } - else if (Op.Size == 1) - { - AVectorHelper.EmitCall(Context, nameof(AVectorHelper.Min)); - } - else - { - throw new InvalidOperationException(); - } + EmitBinarySoftFloatCall(Context, nameof(ASoftFloat.Min)); }); } public static void Fmin_V(AILEmitterCtx Context) { - AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp; - - int SizeF = Op.Size & 1; - EmitVectorBinaryOpF(Context, () => { - if (SizeF == 0) - { - AVectorHelper.EmitCall(Context, nameof(AVectorHelper.MinF)); - } - else if (SizeF == 1) - { - AVectorHelper.EmitCall(Context, nameof(AVectorHelper.Min)); - } - else - { - throw new InvalidOperationException(); - } + EmitBinarySoftFloatCall(Context, nameof(ASoftFloat.Min)); }); } - public static void Fmaxnm_S(AILEmitterCtx Context) + public static void Fminnm_S(AILEmitterCtx Context) { - Fmax_S(Context); + EmitScalarBinaryOpF(Context, () => + { + EmitBinarySoftFloatCall(Context, nameof(ASoftFloat.MinNum)); + }); } - public static void Fminnm_S(AILEmitterCtx Context) + public static void Fminnm_V(AILEmitterCtx Context) { - Fmin_S(Context); + EmitVectorBinaryOpF(Context, () => + { + EmitBinarySoftFloatCall(Context, nameof(ASoftFloat.MinNum)); + }); } public static void Fmla_Se(AILEmitterCtx Context) @@ -1052,6 +1056,66 @@ namespace ChocolArm64.Instruction EmitVectorWidenRnRmBinaryOpSx(Context, () => Context.Emit(OpCodes.Mul)); } + public static void Sqabs_S(AILEmitterCtx Context) + { + EmitScalarSaturatingUnaryOpSx(Context, () => EmitAbs(Context)); + } + + public static void Sqabs_V(AILEmitterCtx Context) + { + EmitVectorSaturatingUnaryOpSx(Context, () => EmitAbs(Context)); + } + + public static void Sqadd_S(AILEmitterCtx Context) + { + EmitScalarSaturatingBinaryOpSx(Context, SaturatingFlags.Add); + } + + public static void Sqadd_V(AILEmitterCtx Context) + { + EmitVectorSaturatingBinaryOpSx(Context, SaturatingFlags.Add); + } + + public static void Sqdmulh_S(AILEmitterCtx Context) + { + EmitSaturatingBinaryOp(Context, () => EmitDoublingMultiplyHighHalf(Context, Round: false), SaturatingFlags.ScalarSx); + } + + public static void Sqdmulh_V(AILEmitterCtx Context) + { + EmitSaturatingBinaryOp(Context, () => EmitDoublingMultiplyHighHalf(Context, Round: false), SaturatingFlags.VectorSx); + } + + public static void Sqneg_S(AILEmitterCtx Context) + { + EmitScalarSaturatingUnaryOpSx(Context, () => Context.Emit(OpCodes.Neg)); + } + + public static void Sqneg_V(AILEmitterCtx Context) + { + EmitVectorSaturatingUnaryOpSx(Context, () => Context.Emit(OpCodes.Neg)); + } + + public static void Sqrdmulh_S(AILEmitterCtx Context) + { + EmitSaturatingBinaryOp(Context, () => EmitDoublingMultiplyHighHalf(Context, Round: true), SaturatingFlags.ScalarSx); + } + + public static void Sqrdmulh_V(AILEmitterCtx Context) + { + EmitSaturatingBinaryOp(Context, () => EmitDoublingMultiplyHighHalf(Context, Round: true), SaturatingFlags.VectorSx); + } + + public static void Sqsub_S(AILEmitterCtx Context) + { + EmitScalarSaturatingBinaryOpSx(Context, SaturatingFlags.Sub); + } + + public static void Sqsub_V(AILEmitterCtx Context) + { + EmitVectorSaturatingBinaryOpSx(Context, SaturatingFlags.Sub); + } + public static void Sqxtn_S(AILEmitterCtx Context) { EmitScalarSaturatingNarrowOpSxSx(Context, () => { }); @@ -1099,6 +1163,16 @@ namespace ChocolArm64.Instruction EmitHighNarrow(Context, () => Context.Emit(OpCodes.Sub), Round: false); } + public static void Suqadd_S(AILEmitterCtx Context) + { + EmitScalarSaturatingBinaryOpSx(Context, SaturatingFlags.Accumulate); + } + + public static void Suqadd_V(AILEmitterCtx Context) + { + EmitVectorSaturatingBinaryOpSx(Context, SaturatingFlags.Accumulate); + } + public static void Uaba_V(AILEmitterCtx Context) { EmitVectorTernaryOpZx(Context, () => @@ -1221,6 +1295,26 @@ namespace ChocolArm64.Instruction EmitVectorWidenRnRmBinaryOpZx(Context, () => Context.Emit(OpCodes.Mul)); } + public static void Uqadd_S(AILEmitterCtx Context) + { + EmitScalarSaturatingBinaryOpZx(Context, SaturatingFlags.Add); + } + + public static void Uqadd_V(AILEmitterCtx Context) + { + EmitVectorSaturatingBinaryOpZx(Context, SaturatingFlags.Add); + } + + public static void Uqsub_S(AILEmitterCtx Context) + { + EmitScalarSaturatingBinaryOpZx(Context, SaturatingFlags.Sub); + } + + public static void Uqsub_V(AILEmitterCtx Context) + { + EmitVectorSaturatingBinaryOpZx(Context, SaturatingFlags.Sub); + } + public static void Uqxtn_S(AILEmitterCtx Context) { EmitScalarSaturatingNarrowOpZxZx(Context, () => { }); @@ -1231,6 +1325,16 @@ namespace ChocolArm64.Instruction EmitVectorSaturatingNarrowOpZxZx(Context, () => { }); } + public static void Usqadd_S(AILEmitterCtx Context) + { + EmitScalarSaturatingBinaryOpZx(Context, SaturatingFlags.Accumulate); + } + + public static void Usqadd_V(AILEmitterCtx Context) + { + EmitVectorSaturatingBinaryOpZx(Context, SaturatingFlags.Accumulate); + } + public static void Usubw_V(AILEmitterCtx Context) { EmitVectorWidenRmBinaryOpZx(Context, () => Context.Emit(OpCodes.Sub)); diff --git a/ChocolArm64/Instruction/AInstEmitSimdHelper.cs b/ChocolArm64/Instruction/AInstEmitSimdHelper.cs index 0bd1a629..a9af3902 100644 --- a/ChocolArm64/Instruction/AInstEmitSimdHelper.cs +++ b/ChocolArm64/Instruction/AInstEmitSimdHelper.cs @@ -336,17 +336,21 @@ namespace ChocolArm64.Instruction { AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp; - if (Opers.HasFlag(OperFlags.Rd)) + bool Rd = (Opers & OperFlags.Rd) != 0; + bool Rn = (Opers & OperFlags.Rn) != 0; + bool Rm = (Opers & OperFlags.Rm) != 0; + + if (Rd) { EmitVectorExtract(Context, Op.Rd, 0, Op.Size, Signed); } - if (Opers.HasFlag(OperFlags.Rn)) + if (Rn) { EmitVectorExtract(Context, Op.Rn, 0, Op.Size, Signed); } - if (Opers.HasFlag(OperFlags.Rm)) + if (Rm) { EmitVectorExtract(Context, ((AOpCodeSimdReg)Op).Rm, 0, Op.Size, Signed); } @@ -377,17 +381,21 @@ namespace ChocolArm64.Instruction int SizeF = Op.Size & 1; - if (Opers.HasFlag(OperFlags.Ra)) + bool Ra = (Opers & OperFlags.Ra) != 0; + bool Rn = (Opers & OperFlags.Rn) != 0; + bool Rm = (Opers & OperFlags.Rm) != 0; + + if (Ra) { EmitVectorExtractF(Context, ((AOpCodeSimdReg)Op).Ra, 0, SizeF); } - if (Opers.HasFlag(OperFlags.Rn)) + if (Rn) { EmitVectorExtractF(Context, Op.Rn, 0, SizeF); } - if (Opers.HasFlag(OperFlags.Rm)) + if (Rm) { EmitVectorExtractF(Context, ((AOpCodeSimdReg)Op).Rm, 0, SizeF); } @@ -769,7 +777,7 @@ namespace ChocolArm64.Instruction Emit(); EmitVectorInsertTmp(Context, Pairs + Index, Op.Size); - EmitVectorInsertTmp(Context, Index, Op.Size); + EmitVectorInsertTmp(Context, Index, Op.Size); } Context.EmitLdvectmp(); @@ -781,56 +789,262 @@ namespace ChocolArm64.Instruction } } + [Flags] + public enum SaturatingFlags + { + Scalar = 1 << 0, + Signed = 1 << 1, + + Add = 1 << 2, + Sub = 1 << 3, + + Accumulate = 1 << 4, + + ScalarSx = Scalar | Signed, + ScalarZx = Scalar, + + VectorSx = Signed, + VectorZx = 0 + } + + public static void EmitScalarSaturatingUnaryOpSx(AILEmitterCtx Context, Action Emit) + { + EmitSaturatingUnaryOpSx(Context, Emit, SaturatingFlags.ScalarSx); + } + + public static void EmitVectorSaturatingUnaryOpSx(AILEmitterCtx Context, Action Emit) + { + EmitSaturatingUnaryOpSx(Context, Emit, SaturatingFlags.VectorSx); + } + + public static void EmitSaturatingUnaryOpSx(AILEmitterCtx Context, Action Emit, SaturatingFlags Flags) + { + AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp; + + bool Scalar = (Flags & SaturatingFlags.Scalar) != 0; + + int Bytes = Op.GetBitsCount() >> 3; + int Elems = !Scalar ? Bytes >> Op.Size : 1; + + if (Scalar) + { + EmitVectorZeroLowerTmp(Context); + } + + for (int Index = 0; Index < Elems; Index++) + { + EmitVectorExtractSx(Context, Op.Rn, Index, Op.Size); + + Emit(); + + if (Op.Size <= 2) + { + EmitSatQ(Context, Op.Size, true, true); + } + else /* if (Op.Size == 3) */ + { + EmitUnarySignedSatQAbsOrNeg(Context); + } + + EmitVectorInsertTmp(Context, Index, Op.Size); + } + + Context.EmitLdvectmp(); + Context.EmitStvec(Op.Rd); + + if ((Op.RegisterSize == ARegisterSize.SIMD64) || Scalar) + { + EmitVectorZeroUpper(Context, Op.Rd); + } + } + + public static void EmitScalarSaturatingBinaryOpSx(AILEmitterCtx Context, SaturatingFlags Flags) + { + EmitSaturatingBinaryOp(Context, () => { }, SaturatingFlags.ScalarSx | Flags); + } + + public static void EmitScalarSaturatingBinaryOpZx(AILEmitterCtx Context, SaturatingFlags Flags) + { + EmitSaturatingBinaryOp(Context, () => { }, SaturatingFlags.ScalarZx | Flags); + } + + public static void EmitVectorSaturatingBinaryOpSx(AILEmitterCtx Context, SaturatingFlags Flags) + { + EmitSaturatingBinaryOp(Context, () => { }, SaturatingFlags.VectorSx | Flags); + } + + public static void EmitVectorSaturatingBinaryOpZx(AILEmitterCtx Context, SaturatingFlags Flags) + { + EmitSaturatingBinaryOp(Context, () => { }, SaturatingFlags.VectorZx | Flags); + } + + public static void EmitSaturatingBinaryOp(AILEmitterCtx Context, Action Emit, SaturatingFlags Flags) + { + AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp; + + bool Scalar = (Flags & SaturatingFlags.Scalar) != 0; + bool Signed = (Flags & SaturatingFlags.Signed) != 0; + + bool Add = (Flags & SaturatingFlags.Add) != 0; + bool Sub = (Flags & SaturatingFlags.Sub) != 0; + + bool Accumulate = (Flags & SaturatingFlags.Accumulate) != 0; + + int Bytes = Op.GetBitsCount() >> 3; + int Elems = !Scalar ? Bytes >> Op.Size : 1; + + if (Scalar) + { + EmitVectorZeroLowerTmp(Context); + } + + if (Add || Sub) + { + for (int Index = 0; Index < Elems; Index++) + { + EmitVectorExtract(Context, Op.Rn, Index, Op.Size, Signed); + EmitVectorExtract(Context, ((AOpCodeSimdReg)Op).Rm, Index, Op.Size, Signed); + + if (Op.Size <= 2) + { + Context.Emit(Add ? OpCodes.Add : OpCodes.Sub); + + EmitSatQ(Context, Op.Size, true, Signed); + } + else /* if (Op.Size == 3) */ + { + if (Add) + { + EmitBinarySatQAdd(Context, Signed); + } + else /* if (Sub) */ + { + EmitBinarySatQSub(Context, Signed); + } + } + + EmitVectorInsertTmp(Context, Index, Op.Size); + } + } + else if (Accumulate) + { + for (int Index = 0; Index < Elems; Index++) + { + EmitVectorExtract(Context, Op.Rn, Index, Op.Size, !Signed); + EmitVectorExtract(Context, Op.Rd, Index, Op.Size, Signed); + + if (Op.Size <= 2) + { + Context.Emit(OpCodes.Add); + + EmitSatQ(Context, Op.Size, true, Signed); + } + else /* if (Op.Size == 3) */ + { + EmitBinarySatQAccumulate(Context, Signed); + } + + EmitVectorInsertTmp(Context, Index, Op.Size); + } + } + else + { + for (int Index = 0; Index < Elems; Index++) + { + EmitVectorExtract(Context, Op.Rn, Index, Op.Size, Signed); + EmitVectorExtract(Context, ((AOpCodeSimdReg)Op).Rm, Index, Op.Size, Signed); + + Emit(); + + EmitSatQ(Context, Op.Size, true, Signed); + + EmitVectorInsertTmp(Context, Index, Op.Size); + } + } + + Context.EmitLdvectmp(); + Context.EmitStvec(Op.Rd); + + if ((Op.RegisterSize == ARegisterSize.SIMD64) || Scalar) + { + EmitVectorZeroUpper(Context, Op.Rd); + } + } + + [Flags] + public enum SaturatingNarrowFlags + { + Scalar = 1 << 0, + SignedSrc = 1 << 1, + SignedDst = 1 << 2, + + ScalarSxSx = Scalar | SignedSrc | SignedDst, + ScalarSxZx = Scalar | SignedSrc, + ScalarZxSx = Scalar | SignedDst, + ScalarZxZx = Scalar, + + VectorSxSx = SignedSrc | SignedDst, + VectorSxZx = SignedSrc, + VectorZxSx = SignedDst, + VectorZxZx = 0 + } + public static void EmitScalarSaturatingNarrowOpSxSx(AILEmitterCtx Context, Action Emit) { - EmitSaturatingNarrowOp(Context, Emit, true, true, true); + EmitSaturatingNarrowOp(Context, Emit, SaturatingNarrowFlags.ScalarSxSx); } public static void EmitScalarSaturatingNarrowOpSxZx(AILEmitterCtx Context, Action Emit) { - EmitSaturatingNarrowOp(Context, Emit, true, false, true); + EmitSaturatingNarrowOp(Context, Emit, SaturatingNarrowFlags.ScalarSxZx); + } + + public static void EmitScalarSaturatingNarrowOpZxSx(AILEmitterCtx Context, Action Emit) + { + EmitSaturatingNarrowOp(Context, Emit, SaturatingNarrowFlags.ScalarZxSx); } public static void EmitScalarSaturatingNarrowOpZxZx(AILEmitterCtx Context, Action Emit) { - EmitSaturatingNarrowOp(Context, Emit, false, false, true); + EmitSaturatingNarrowOp(Context, Emit, SaturatingNarrowFlags.ScalarZxZx); } public static void EmitVectorSaturatingNarrowOpSxSx(AILEmitterCtx Context, Action Emit) { - EmitSaturatingNarrowOp(Context, Emit, true, true, false); + EmitSaturatingNarrowOp(Context, Emit, SaturatingNarrowFlags.VectorSxSx); } public static void EmitVectorSaturatingNarrowOpSxZx(AILEmitterCtx Context, Action Emit) { - EmitSaturatingNarrowOp(Context, Emit, true, false, false); + EmitSaturatingNarrowOp(Context, Emit, SaturatingNarrowFlags.VectorSxZx); + } + + public static void EmitVectorSaturatingNarrowOpZxSx(AILEmitterCtx Context, Action Emit) + { + EmitSaturatingNarrowOp(Context, Emit, SaturatingNarrowFlags.VectorZxSx); } public static void EmitVectorSaturatingNarrowOpZxZx(AILEmitterCtx Context, Action Emit) { - EmitSaturatingNarrowOp(Context, Emit, false, false, false); + EmitSaturatingNarrowOp(Context, Emit, SaturatingNarrowFlags.VectorZxZx); } - public static void EmitSaturatingNarrowOp( - AILEmitterCtx Context, - Action Emit, - bool SignedSrc, - bool SignedDst, - bool Scalar) + public static void EmitSaturatingNarrowOp(AILEmitterCtx Context, Action Emit, SaturatingNarrowFlags Flags) { AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp; - int Elems = !Scalar ? 8 >> Op.Size : 1; + bool Scalar = (Flags & SaturatingNarrowFlags.Scalar) != 0; + bool SignedSrc = (Flags & SaturatingNarrowFlags.SignedSrc) != 0; + bool SignedDst = (Flags & SaturatingNarrowFlags.SignedDst) != 0; - int ESize = 8 << Op.Size; + int Elems = !Scalar ? 8 >> Op.Size : 1; int Part = !Scalar && (Op.RegisterSize == ARegisterSize.SIMD128) ? Elems : 0; - long TMaxValue = SignedDst ? (1 << (ESize - 1)) - 1 : (long)(~0UL >> (64 - ESize)); - long TMinValue = SignedDst ? -((1 << (ESize - 1))) : 0; - - Context.EmitLdc_I8(0L); - Context.EmitSttmp(); + if (Scalar) + { + EmitVectorZeroLowerTmp(Context); + } if (Part != 0) { @@ -840,66 +1054,109 @@ namespace ChocolArm64.Instruction for (int Index = 0; Index < Elems; Index++) { - AILLabel LblLe = new AILLabel(); - AILLabel LblGeEnd = new AILLabel(); - EmitVectorExtract(Context, Op.Rn, Index, Op.Size + 1, SignedSrc); Emit(); - Context.Emit(OpCodes.Dup); - - Context.EmitLdc_I8(TMaxValue); + EmitSatQ(Context, Op.Size, SignedSrc, SignedDst); - Context.Emit(SignedSrc ? OpCodes.Ble_S : OpCodes.Ble_Un_S, LblLe); + EmitVectorInsertTmp(Context, Part + Index, Op.Size); + } - Context.Emit(OpCodes.Pop); + Context.EmitLdvectmp(); + Context.EmitStvec(Op.Rd); - Context.EmitLdc_I8(TMaxValue); - Context.EmitLdc_I8(0x8000000L); - Context.EmitSttmp(); + if (Part == 0) + { + EmitVectorZeroUpper(Context, Op.Rd); + } + } - Context.Emit(OpCodes.Br_S, LblGeEnd); + // TSrc (16bit, 32bit, 64bit; signed, unsigned) > TDst (8bit, 16bit, 32bit; signed, unsigned). + public static void EmitSatQ( + AILEmitterCtx Context, + int SizeDst, + bool SignedSrc, + bool SignedDst) + { + if (SizeDst > 2) + { + throw new ArgumentOutOfRangeException(nameof(SizeDst)); + } - Context.MarkLabel(LblLe); + Context.EmitLdc_I4(SizeDst); + Context.EmitLdarg(ATranslatedSub.StateArgIdx); - Context.Emit(OpCodes.Dup); + if (SignedSrc) + { + ASoftFallback.EmitCall(Context, SignedDst + ? nameof(ASoftFallback.SignedSrcSignedDstSatQ) + : nameof(ASoftFallback.SignedSrcUnsignedDstSatQ)); + } + else + { + ASoftFallback.EmitCall(Context, SignedDst + ? nameof(ASoftFallback.UnsignedSrcSignedDstSatQ) + : nameof(ASoftFallback.UnsignedSrcUnsignedDstSatQ)); + } + } - Context.EmitLdc_I8(TMinValue); + // TSrc (64bit) == TDst (64bit); signed. + public static void EmitUnarySignedSatQAbsOrNeg(AILEmitterCtx Context) + { + if (((AOpCodeSimd)Context.CurrOp).Size < 3) + { + throw new InvalidOperationException(); + } - Context.Emit(SignedSrc ? OpCodes.Bge_S : OpCodes.Bge_Un_S, LblGeEnd); + Context.EmitLdarg(ATranslatedSub.StateArgIdx); - Context.Emit(OpCodes.Pop); + ASoftFallback.EmitCall(Context, nameof(ASoftFallback.UnarySignedSatQAbsOrNeg)); + } - Context.EmitLdc_I8(TMinValue); - Context.EmitLdc_I8(0x8000000L); - Context.EmitSttmp(); + // TSrcs (64bit) == TDst (64bit); signed, unsigned. + public static void EmitBinarySatQAdd(AILEmitterCtx Context, bool Signed) + { + if (((AOpCodeSimdReg)Context.CurrOp).Size < 3) + { + throw new InvalidOperationException(); + } - Context.MarkLabel(LblGeEnd); + Context.EmitLdarg(ATranslatedSub.StateArgIdx); - if (Scalar) - { - EmitVectorZeroLowerTmp(Context); - } + ASoftFallback.EmitCall(Context, Signed + ? nameof(ASoftFallback.BinarySignedSatQAdd) + : nameof(ASoftFallback.BinaryUnsignedSatQAdd)); + } - EmitVectorInsertTmp(Context, Part + Index, Op.Size); + // TSrcs (64bit) == TDst (64bit); signed, unsigned. + public static void EmitBinarySatQSub(AILEmitterCtx Context, bool Signed) + { + if (((AOpCodeSimdReg)Context.CurrOp).Size < 3) + { + throw new InvalidOperationException(); } - Context.EmitLdvectmp(); - Context.EmitStvec(Op.Rd); + Context.EmitLdarg(ATranslatedSub.StateArgIdx); - if (Part == 0) + ASoftFallback.EmitCall(Context, Signed + ? nameof(ASoftFallback.BinarySignedSatQSub) + : nameof(ASoftFallback.BinaryUnsignedSatQSub)); + } + + // TSrcs (64bit) == TDst (64bit); signed, unsigned. + public static void EmitBinarySatQAccumulate(AILEmitterCtx Context, bool Signed) + { + if (((AOpCodeSimd)Context.CurrOp).Size < 3) { - EmitVectorZeroUpper(Context, Op.Rd); + throw new InvalidOperationException(); } Context.EmitLdarg(ATranslatedSub.StateArgIdx); - Context.EmitLdarg(ATranslatedSub.StateArgIdx); - Context.EmitCallPropGet(typeof(AThreadState), nameof(AThreadState.Fpsr)); - Context.EmitLdtmp(); - Context.Emit(OpCodes.Conv_I4); - Context.Emit(OpCodes.Or); - Context.EmitCallPropSet(typeof(AThreadState), nameof(AThreadState.Fpsr)); + + ASoftFallback.EmitCall(Context, Signed + ? nameof(ASoftFallback.BinarySignedSatQAcc) + : nameof(ASoftFallback.BinaryUnsignedSatQAcc)); } public static void EmitScalarSet(AILEmitterCtx Context, int Reg, int Size) diff --git a/ChocolArm64/Instruction/ASoftFallback.cs b/ChocolArm64/Instruction/ASoftFallback.cs index ae3994b0..40898578 100644 --- a/ChocolArm64/Instruction/ASoftFallback.cs +++ b/ChocolArm64/Instruction/ASoftFallback.cs @@ -1,3 +1,4 @@ +using ChocolArm64.State; using ChocolArm64.Translation; using System; @@ -10,6 +11,290 @@ namespace ChocolArm64.Instruction Context.EmitCall(typeof(ASoftFallback), MthdName); } +#region "Saturating" + public static long SignedSrcSignedDstSatQ(long op, int Size, AThreadState State) + { + int ESize = 8 << Size; + + long TMaxValue = (1L << (ESize - 1)) - 1L; + long TMinValue = -(1L << (ESize - 1)); + + if (op > TMaxValue) + { + SetFpsrQCFlag(State); + + return TMaxValue; + } + else if (op < TMinValue) + { + SetFpsrQCFlag(State); + + return TMinValue; + } + else + { + return op; + } + } + + public static ulong SignedSrcUnsignedDstSatQ(long op, int Size, AThreadState State) + { + int ESize = 8 << Size; + + ulong TMaxValue = (1UL << ESize) - 1UL; + ulong TMinValue = 0UL; + + if (op > (long)TMaxValue) + { + SetFpsrQCFlag(State); + + return TMaxValue; + } + else if (op < (long)TMinValue) + { + SetFpsrQCFlag(State); + + return TMinValue; + } + else + { + return (ulong)op; + } + } + + public static long UnsignedSrcSignedDstSatQ(ulong op, int Size, AThreadState State) + { + int ESize = 8 << Size; + + long TMaxValue = (1L << (ESize - 1)) - 1L; + + if (op > (ulong)TMaxValue) + { + SetFpsrQCFlag(State); + + return TMaxValue; + } + else + { + return (long)op; + } + } + + public static ulong UnsignedSrcUnsignedDstSatQ(ulong op, int Size, AThreadState State) + { + int ESize = 8 << Size; + + ulong TMaxValue = (1UL << ESize) - 1UL; + + if (op > TMaxValue) + { + SetFpsrQCFlag(State); + + return TMaxValue; + } + else + { + return op; + } + } + + public static long UnarySignedSatQAbsOrNeg(long op, AThreadState State) + { + if (op == long.MinValue) + { + SetFpsrQCFlag(State); + + return long.MaxValue; + } + else + { + return op; + } + } + + public static long BinarySignedSatQAdd(long op1, long op2, AThreadState State) + { + long Add = op1 + op2; + + if ((~(op1 ^ op2) & (op1 ^ Add)) < 0L) + { + SetFpsrQCFlag(State); + + if (op1 < 0L) + { + return long.MinValue; + } + else + { + return long.MaxValue; + } + } + else + { + return Add; + } + } + + public static ulong BinaryUnsignedSatQAdd(ulong op1, ulong op2, AThreadState State) + { + ulong Add = op1 + op2; + + if ((Add < op1) && (Add < op2)) + { + SetFpsrQCFlag(State); + + return ulong.MaxValue; + } + else + { + return Add; + } + } + + public static long BinarySignedSatQSub(long op1, long op2, AThreadState State) + { + long Sub = op1 - op2; + + if (((op1 ^ op2) & (op1 ^ Sub)) < 0L) + { + SetFpsrQCFlag(State); + + if (op1 < 0L) + { + return long.MinValue; + } + else + { + return long.MaxValue; + } + } + else + { + return Sub; + } + } + + public static ulong BinaryUnsignedSatQSub(ulong op1, ulong op2, AThreadState State) + { + ulong Sub = op1 - op2; + + if (op1 < op2) + { + SetFpsrQCFlag(State); + + return ulong.MinValue; + } + else + { + return Sub; + } + } + + public static long BinarySignedSatQAcc(ulong op1, long op2, AThreadState State) + { + if (op1 <= (ulong)long.MaxValue) + { + // op1 from ulong.MinValue to (ulong)long.MaxValue + // op2 from long.MinValue to long.MaxValue + + long Add = (long)op1 + op2; + + if ((~op2 & Add) < 0L) + { + SetFpsrQCFlag(State); + + return long.MaxValue; + } + else + { + return Add; + } + } + else if (op2 >= 0L) + { + // op1 from (ulong)long.MaxValue + 1UL to ulong.MaxValue + // op2 from (long)ulong.MinValue to long.MaxValue + + SetFpsrQCFlag(State); + + return long.MaxValue; + } + else + { + // op1 from (ulong)long.MaxValue + 1UL to ulong.MaxValue + // op2 from long.MinValue to (long)ulong.MinValue - 1L + + ulong Add = op1 + (ulong)op2; + + if (Add > (ulong)long.MaxValue) + { + SetFpsrQCFlag(State); + + return long.MaxValue; + } + else + { + return (long)Add; + } + } + } + + public static ulong BinaryUnsignedSatQAcc(long op1, ulong op2, AThreadState State) + { + if (op1 >= 0L) + { + // op1 from (long)ulong.MinValue to long.MaxValue + // op2 from ulong.MinValue to ulong.MaxValue + + ulong Add = (ulong)op1 + op2; + + if ((Add < (ulong)op1) && (Add < op2)) + { + SetFpsrQCFlag(State); + + return ulong.MaxValue; + } + else + { + return Add; + } + } + else if (op2 > (ulong)long.MaxValue) + { + // op1 from long.MinValue to (long)ulong.MinValue - 1L + // op2 from (ulong)long.MaxValue + 1UL to ulong.MaxValue + + return (ulong)op1 + op2; + } + else + { + // op1 from long.MinValue to (long)ulong.MinValue - 1L + // op2 from ulong.MinValue to (ulong)long.MaxValue + + long Add = op1 + (long)op2; + + if (Add < (long)ulong.MinValue) + { + SetFpsrQCFlag(State); + + return ulong.MinValue; + } + else + { + return (ulong)Add; + } + } + } + + private static void SetFpsrQCFlag(AThreadState State) + { + const int QCFlagBit = 27; + + State.Fpsr |= 1 << QCFlagBit; + } +#endregion + +#region "Count" public static ulong CountLeadingSigns(ulong Value, int Size) { Value ^= Value >> 1; @@ -57,7 +342,9 @@ namespace ChocolArm64.Instruction return (Value >> 4) + (Value & 0x0f); } +#endregion +#region "Crc32" private const uint Crc32RevPoly = 0xedb88320; private const uint Crc32cRevPoly = 0x82f63b78; @@ -116,7 +403,9 @@ namespace ChocolArm64.Instruction return Crc; } +#endregion +#region "Reverse" public static uint ReverseBits8(uint Value) { Value = ((Value & 0xaa) >> 1) | ((Value & 0x55) << 1); @@ -185,7 +474,9 @@ namespace ChocolArm64.Instruction throw new ArgumentException(nameof(Size)); } +#endregion +#region "MultiplyHigh" public static long SMulHi128(long LHS, long RHS) { long Result = (long)UMulHi128((ulong)LHS, (ulong)RHS); @@ -211,5 +502,6 @@ namespace ChocolArm64.Instruction return LHigh * RHigh + Z0 + (Z1 >> 32); } +#endregion } } diff --git a/ChocolArm64/Instruction/ASoftFloat.cs b/ChocolArm64/Instruction/ASoftFloat.cs index 27f4f7fb..e3f067ed 100644 --- a/ChocolArm64/Instruction/ASoftFloat.cs +++ b/ChocolArm64/Instruction/ASoftFloat.cs @@ -79,7 +79,7 @@ namespace ChocolArm64.Instruction if (scaled == 0) { // Zero -> Infinity - return BitConverter.Int64BitsToDouble((long)(x_sign | 0x7ff0000000000000)); + return BitConverter.Int64BitsToDouble((long)(x_sign | 0x7FF0000000000000)); } // Denormal @@ -94,7 +94,7 @@ namespace ChocolArm64.Instruction if (x_sign != 0) { // Negative -> NaN - return BitConverter.Int64BitsToDouble((long)0x7ff8000000000000); + return BitConverter.Int64BitsToDouble((long)0x7FF8000000000000); } if (x_exp == 0x7ff && scaled == 0) @@ -153,7 +153,7 @@ namespace ChocolArm64.Instruction if (scaled == 0) { // Zero -> Infinity - return BitConverter.Int64BitsToDouble((long)(x_sign | 0x7ff0000000000000)); + return BitConverter.Int64BitsToDouble((long)(x_sign | 0x7FF0000000000000)); } // Denormal @@ -208,8 +208,8 @@ namespace ChocolArm64.Instruction ulong op1_other = op1_bits & 0x7FFFFFFFFFFFFFFF; ulong op2_other = op2_bits & 0x7FFFFFFFFFFFFFFF; - bool inf1 = op1_other == 0x7ff0000000000000; - bool inf2 = op2_other == 0x7ff0000000000000; + bool inf1 = op1_other == 0x7FF0000000000000; + bool inf2 = op2_other == 0x7FF0000000000000; bool zero1 = op1_other == 0; bool zero2 = op2_other == 0; @@ -220,7 +220,7 @@ namespace ChocolArm64.Instruction else if (inf1 || inf2) { // Infinity - return BitConverter.Int64BitsToDouble((long)(0x7ff0000000000000 | (op1_sign ^ op2_sign))); + return BitConverter.Int64BitsToDouble((long)(0x7FF0000000000000 | (op1_sign ^ op2_sign))); } return 2.0 + op1 * op2; @@ -261,5 +261,277 @@ namespace ChocolArm64.Instruction uint new_exp = (uint)((exponent + 127) & 0xFF) << 23; return BitConverter.Int32BitsToSingle((int)((x_sign << 31) | new_exp | (x_mantissa << 13))); } + + public static float MaxNum(float op1, float op2) + { + uint op1_bits = (uint)BitConverter.SingleToInt32Bits(op1); + uint op2_bits = (uint)BitConverter.SingleToInt32Bits(op2); + + if (IsQNaN(op1_bits) && !IsQNaN(op2_bits)) + { + op1 = float.NegativeInfinity; + } + else if (!IsQNaN(op1_bits) && IsQNaN(op2_bits)) + { + op2 = float.NegativeInfinity; + } + + return Max(op1, op2); + } + + public static double MaxNum(double op1, double op2) + { + ulong op1_bits = (ulong)BitConverter.DoubleToInt64Bits(op1); + ulong op2_bits = (ulong)BitConverter.DoubleToInt64Bits(op2); + + if (IsQNaN(op1_bits) && !IsQNaN(op2_bits)) + { + op1 = double.NegativeInfinity; + } + else if (!IsQNaN(op1_bits) && IsQNaN(op2_bits)) + { + op2 = double.NegativeInfinity; + } + + return Max(op1, op2); + } + + public static float Max(float op1, float op2) + { + // Fast path + if (op1 > op2) + { + return op1; + } + + if (op1 < op2 || (op1 == op2 && op2 != 0)) + { + return op2; + } + + uint op1_bits = (uint)BitConverter.SingleToInt32Bits(op1); + uint op2_bits = (uint)BitConverter.SingleToInt32Bits(op2); + + // Handle NaN cases + if (ProcessNaNs(op1_bits, op2_bits, out uint op_bits)) + { + return BitConverter.Int32BitsToSingle((int)op_bits); + } + + // Return the most positive zero + if ((op1_bits & op2_bits) == 0x80000000u) + { + return BitConverter.Int32BitsToSingle(int.MinValue); + } + + return 0; + } + + public static double Max(double op1, double op2) + { + // Fast path + if (op1 > op2) + { + return op1; + } + + if (op1 < op2 || (op1 == op2 && op2 != 0)) + { + return op2; + } + + ulong op1_bits = (ulong)BitConverter.DoubleToInt64Bits(op1); + ulong op2_bits = (ulong)BitConverter.DoubleToInt64Bits(op2); + + // Handle NaN cases + if (ProcessNaNs(op1_bits, op2_bits, out ulong op_bits)) + { + return BitConverter.Int64BitsToDouble((long)op_bits); + } + + // Return the most positive zero + if ((op1_bits & op2_bits) == 0x8000000000000000ul) + { + return BitConverter.Int64BitsToDouble(long.MinValue); + } + + return 0; + } + + public static float MinNum(float op1, float op2) + { + uint op1_bits = (uint)BitConverter.SingleToInt32Bits(op1); + uint op2_bits = (uint)BitConverter.SingleToInt32Bits(op2); + + if (IsQNaN(op1_bits) && !IsQNaN(op2_bits)) + { + op1 = float.PositiveInfinity; + } + else if (!IsQNaN(op1_bits) && IsQNaN(op2_bits)) + { + op2 = float.PositiveInfinity; + } + + return Min(op1, op2); + } + + public static double MinNum(double op1, double op2) + { + ulong op1_bits = (ulong)BitConverter.DoubleToInt64Bits(op1); + ulong op2_bits = (ulong)BitConverter.DoubleToInt64Bits(op2); + + if (IsQNaN(op1_bits) && !IsQNaN(op2_bits)) + { + op1 = double.PositiveInfinity; + } + else if (!IsQNaN(op1_bits) && IsQNaN(op2_bits)) + { + op2 = double.PositiveInfinity; + } + + return Min(op1, op2); + } + + public static float Min(float op1, float op2) + { + // Fast path + if (op1 < op2) + { + return op1; + } + + if (op1 > op2 || (op1 == op2 && op2 != 0)) + { + return op2; + } + + uint op1_bits = (uint)BitConverter.SingleToInt32Bits(op1); + uint op2_bits = (uint)BitConverter.SingleToInt32Bits(op2); + + // Handle NaN cases + if (ProcessNaNs(op1_bits, op2_bits, out uint op_bits)) + { + return BitConverter.Int32BitsToSingle((int)op_bits); + } + + // Return the most negative zero + if ((op1_bits | op2_bits) == 0x80000000u) + { + return BitConverter.Int32BitsToSingle(int.MinValue); + } + + return 0; + } + + public static double Min(double op1, double op2) + { + // Fast path + if (op1 < op2) + { + return op1; + } + + if (op1 > op2 || (op1 == op2 && op2 != 0)) + { + return op2; + } + + ulong op1_bits = (ulong)BitConverter.DoubleToInt64Bits(op1); + ulong op2_bits = (ulong)BitConverter.DoubleToInt64Bits(op2); + + // Handle NaN cases + if (ProcessNaNs(op1_bits, op2_bits, out ulong op_bits)) + { + return BitConverter.Int64BitsToDouble((long)op_bits); + } + + // Return the most negative zero + if ((op1_bits | op2_bits) == 0x8000000000000000ul) + { + return BitConverter.Int64BitsToDouble(long.MinValue); + } + + return 0; + } + + private static bool ProcessNaNs(uint op1_bits, uint op2_bits, out uint op_bits) + { + if (IsSNaN(op1_bits)) + { + op_bits = op1_bits | (1u << 22); // op1 is SNaN, return QNaN op1 + } + else if (IsSNaN(op2_bits)) + { + op_bits = op2_bits | (1u << 22); // op2 is SNaN, return QNaN op2 + } + else if (IsQNaN(op1_bits)) + { + op_bits = op1_bits; // op1 is QNaN, return QNaN op1 + } + else if (IsQNaN(op2_bits)) + { + op_bits = op2_bits; // op2 is QNaN, return QNaN op2 + } + else + { + op_bits = 0; + + return false; + } + + return true; + } + + private static bool ProcessNaNs(ulong op1_bits, ulong op2_bits, out ulong op_bits) + { + if (IsSNaN(op1_bits)) + { + op_bits = op1_bits | (1ul << 51); // op1 is SNaN, return QNaN op1 + } + else if (IsSNaN(op2_bits)) + { + op_bits = op2_bits | (1ul << 51); // op2 is SNaN, return QNaN op2 + } + else if (IsQNaN(op1_bits)) + { + op_bits = op1_bits; // op1 is QNaN, return QNaN op1 + } + else if (IsQNaN(op2_bits)) + { + op_bits = op2_bits; // op2 is QNaN, return QNaN op2 + } + else + { + op_bits = 0; + + return false; + } + + return true; + } + + private static bool IsQNaN(uint op_bits) + { + return (op_bits & 0x007FFFFF) != 0 && + (op_bits & 0x7FC00000) == 0x7FC00000; + } + + private static bool IsQNaN(ulong op_bits) + { + return (op_bits & 0x000FFFFFFFFFFFFF) != 0 && + (op_bits & 0x7FF8000000000000) == 0x7FF8000000000000; + } + + private static bool IsSNaN(uint op_bits) + { + return (op_bits & 0x007FFFFF) != 0 && + (op_bits & 0x7FC00000) == 0x7F800000; + } + + private static bool IsSNaN(ulong op_bits) + { + return (op_bits & 0x000FFFFFFFFFFFFF) != 0 && + (op_bits & 0x7FF8000000000000) == 0x7FF0000000000000; + } } }
\ No newline at end of file diff --git a/ChocolArm64/Instruction/AVectorHelper.cs b/ChocolArm64/Instruction/AVectorHelper.cs index a0f887b0..b2d53740 100644 --- a/ChocolArm64/Instruction/AVectorHelper.cs +++ b/ChocolArm64/Instruction/AVectorHelper.cs @@ -93,86 +93,6 @@ namespace ChocolArm64.Instruction Value < ulong.MinValue ? ulong.MinValue : (ulong)Value; } - public static double Max(double LHS, double RHS) - { - if (LHS == 0.0 && RHS == 0.0) - { - if (BitConverter.DoubleToInt64Bits(LHS) < 0 && - BitConverter.DoubleToInt64Bits(RHS) < 0) - return -0.0; - - return 0.0; - } - - if (LHS > RHS) - return LHS; - - if (double.IsNaN(LHS)) - return LHS; - - return RHS; - } - - public static float MaxF(float LHS, float RHS) - { - if (LHS == 0.0 && RHS == 0.0) - { - if (BitConverter.SingleToInt32Bits(LHS) < 0 && - BitConverter.SingleToInt32Bits(RHS) < 0) - return -0.0f; - - return 0.0f; - } - - if (LHS > RHS) - return LHS; - - if (float.IsNaN(LHS)) - return LHS; - - return RHS; - } - - public static double Min(double LHS, double RHS) - { - if (LHS == 0.0 && RHS == 0.0) - { - if (BitConverter.DoubleToInt64Bits(LHS) < 0 || - BitConverter.DoubleToInt64Bits(RHS) < 0) - return -0.0; - - return 0.0; - } - - if (LHS < RHS) - return LHS; - - if (double.IsNaN(LHS)) - return LHS; - - return RHS; - } - - public static float MinF(float LHS, float RHS) - { - if (LHS == 0.0 && RHS == 0.0) - { - if (BitConverter.SingleToInt32Bits(LHS) < 0 || - BitConverter.SingleToInt32Bits(RHS) < 0) - return -0.0f; - - return 0.0f; - } - - if (LHS < RHS) - return LHS; - - if (float.IsNaN(LHS)) - return LHS; - - return RHS; - } - public static double Round(double Value, int Fpcr) { switch ((ARoundMode)((Fpcr >> 22) & 3)) diff --git a/ChocolArm64/Memory/AMemory.cs b/ChocolArm64/Memory/AMemory.cs index 054277b2..e969cca5 100644 --- a/ChocolArm64/Memory/AMemory.cs +++ b/ChocolArm64/Memory/AMemory.cs @@ -160,23 +160,23 @@ namespace ChocolArm64.Memory return HostPageSize; } - public bool[] IsRegionModified(long Position, long Size) + public (bool[], long) IsRegionModified(long Position, long Size) { if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { - return null; + return (null, 0); } long EndPos = Position + Size; if ((ulong)EndPos < (ulong)Position) { - return null; + return (null, 0); } if ((ulong)EndPos > AMemoryMgr.RamSize) { - return null; + return (null, 0); } IntPtr MemAddress = new IntPtr(RamPtr + Position); @@ -201,7 +201,7 @@ namespace ChocolArm64.Memory Modified[(VA - Position) / HostPageSize] = true; } - return Modified; + return (Modified, Count); } public IntPtr GetHostAddress(long Position, long Size) diff --git a/Ryujinx.Audio/OpenAL/OpenALAudioOut.cs b/Ryujinx.Audio/OpenAL/OpenALAudioOut.cs index 1dd63202..85e2d803 100644 --- a/Ryujinx.Audio/OpenAL/OpenALAudioOut.cs +++ b/Ryujinx.Audio/OpenAL/OpenALAudioOut.cs @@ -222,7 +222,8 @@ namespace Ryujinx.Audio.OpenAL Td.CallReleaseCallbackIfNeeded(); } - Thread.Yield(); + //If it's not slept it will waste cycles + Thread.Sleep(10); } while (KeepPolling); } diff --git a/Ryujinx.Graphics/Gal/GalPipelineState.cs b/Ryujinx.Graphics/Gal/GalPipelineState.cs new file mode 100644 index 00000000..d1ffbe76 --- /dev/null +++ b/Ryujinx.Graphics/Gal/GalPipelineState.cs @@ -0,0 +1,73 @@ +namespace Ryujinx.Graphics.Gal +{ + public struct GalVertexBinding + { + //VboKey shouldn't be here, but ARB_vertex_attrib_binding is core since 4.3 + + public bool Enabled; + public int Stride; + public long VboKey; + public GalVertexAttrib[] Attribs; + } + + public class GalPipelineState + { + public const int Stages = 5; + public const int ConstBuffersPerStage = 18; + + public long[][] ConstBufferKeys; + + public GalVertexBinding[] VertexBindings; + + public float FlipX; + public float FlipY; + + public GalFrontFace FrontFace; + + public bool CullFaceEnabled; + public GalCullFace CullFace; + + public bool DepthTestEnabled; + public GalComparisonOp DepthFunc; + + public bool StencilTestEnabled; + + public GalComparisonOp StencilBackFuncFunc; + public int StencilBackFuncRef; + public uint StencilBackFuncMask; + public GalStencilOp StencilBackOpFail; + public GalStencilOp StencilBackOpZFail; + public GalStencilOp StencilBackOpZPass; + public uint StencilBackMask; + + public GalComparisonOp StencilFrontFuncFunc; + public int StencilFrontFuncRef; + public uint StencilFrontFuncMask; + public GalStencilOp StencilFrontOpFail; + public GalStencilOp StencilFrontOpZFail; + public GalStencilOp StencilFrontOpZPass; + public uint StencilFrontMask; + + public bool BlendEnabled; + public bool BlendSeparateAlpha; + public GalBlendEquation BlendEquationRgb; + public GalBlendFactor BlendFuncSrcRgb; + public GalBlendFactor BlendFuncDstRgb; + public GalBlendEquation BlendEquationAlpha; + public GalBlendFactor BlendFuncSrcAlpha; + public GalBlendFactor BlendFuncDstAlpha; + + public bool PrimitiveRestartEnabled; + public uint PrimitiveRestartIndex; + + public GalPipelineState() + { + ConstBufferKeys = new long[Stages][]; + + for (int Stage = 0; Stage < Stages; Stage++) + { + ConstBufferKeys[Stage] = new long[ConstBuffersPerStage]; + } + } + } +}
\ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/GalTextureFormat.cs b/Ryujinx.Graphics/Gal/GalTextureFormat.cs index c4784283..7e3e65e8 100644 --- a/Ryujinx.Graphics/Gal/GalTextureFormat.cs +++ b/Ryujinx.Graphics/Gal/GalTextureFormat.cs @@ -5,6 +5,7 @@ namespace Ryujinx.Graphics.Gal R32G32B32A32 = 0x1, R16G16B16A16 = 0x3, A8B8G8R8 = 0x8, + A2B10G10R10 = 0x9, R32 = 0xf, BC6H_SF16 = 0x10, BC6H_UF16 = 0x11, diff --git a/Ryujinx.Graphics/Gal/IGalBlend.cs b/Ryujinx.Graphics/Gal/IGalBlend.cs deleted file mode 100644 index 5c96a492..00000000 --- a/Ryujinx.Graphics/Gal/IGalBlend.cs +++ /dev/null @@ -1,22 +0,0 @@ -namespace Ryujinx.Graphics.Gal -{ - public interface IGalBlend - { - void Enable(); - - void Disable(); - - void Set( - GalBlendEquation Equation, - GalBlendFactor FuncSrc, - GalBlendFactor FuncDst); - - void SetSeparate( - GalBlendEquation EquationRgb, - GalBlendEquation EquationAlpha, - GalBlendFactor FuncSrcRgb, - GalBlendFactor FuncDstRgb, - GalBlendFactor FuncSrcAlpha, - GalBlendFactor FuncDstAlpha); - } -}
\ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/IGalConstBuffer.cs b/Ryujinx.Graphics/Gal/IGalConstBuffer.cs new file mode 100644 index 00000000..37545b2a --- /dev/null +++ b/Ryujinx.Graphics/Gal/IGalConstBuffer.cs @@ -0,0 +1,16 @@ +using System; + +namespace Ryujinx.Graphics.Gal +{ + public interface IGalConstBuffer + { + void LockCache(); + void UnlockCache(); + + void Create(long Key, long Size); + + bool IsCached(long Key, long Size); + + void SetData(long Key, long Size, IntPtr HostAddress); + } +}
\ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/IGalPipeline.cs b/Ryujinx.Graphics/Gal/IGalPipeline.cs new file mode 100644 index 00000000..d8cf266a --- /dev/null +++ b/Ryujinx.Graphics/Gal/IGalPipeline.cs @@ -0,0 +1,7 @@ +namespace Ryujinx.Graphics.Gal +{ + public interface IGalPipeline + { + void Bind(GalPipelineState State); + } +}
\ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/IGalRasterizer.cs b/Ryujinx.Graphics/Gal/IGalRasterizer.cs index a87d36c3..89e50b1f 100644 --- a/Ryujinx.Graphics/Gal/IGalRasterizer.cs +++ b/Ryujinx.Graphics/Gal/IGalRasterizer.cs @@ -7,52 +7,20 @@ namespace Ryujinx.Graphics.Gal void LockCaches(); void UnlockCaches(); - void ClearBuffers(GalClearBufferFlags Flags); + void ClearBuffers( + GalClearBufferFlags Flags, + float Red, float Green, float Blue, float Alpha, + float Depth, + int Stencil); bool IsVboCached(long Key, long DataSize); 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 EnablePrimitiveRestart(); - - void DisablePrimitiveRestart(); - - void SetPrimitiveRestartIndex(uint Index); - void CreateVbo(long Key, int DataSize, IntPtr HostAddress); void CreateIbo(long Key, int DataSize, IntPtr HostAddress); - void SetVertexArray(int Stride, long VboKey, GalVertexAttrib[] Attribs); - void SetIndexArray(int Size, GalIndexFormat Format); void DrawArrays(int First, int PrimCount, GalPrimitiveType PrimType); diff --git a/Ryujinx.Graphics/Gal/IGalRenderer.cs b/Ryujinx.Graphics/Gal/IGalRenderer.cs index c6324c4a..b47ac71d 100644 --- a/Ryujinx.Graphics/Gal/IGalRenderer.cs +++ b/Ryujinx.Graphics/Gal/IGalRenderer.cs @@ -8,7 +8,7 @@ namespace Ryujinx.Graphics.Gal void RunActions(); - IGalBlend Blend { get; } + IGalConstBuffer Buffer { get; } IGalFrameBuffer FrameBuffer { get; } @@ -16,6 +16,8 @@ namespace Ryujinx.Graphics.Gal IGalShader Shader { get; } + IGalPipeline Pipeline { get; } + IGalTexture Texture { get; } } }
\ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/IGalShader.cs b/Ryujinx.Graphics/Gal/IGalShader.cs index 56235a07..a9bd1381 100644 --- a/Ryujinx.Graphics/Gal/IGalShader.cs +++ b/Ryujinx.Graphics/Gal/IGalShader.cs @@ -11,12 +11,8 @@ namespace Ryujinx.Graphics.Gal IEnumerable<ShaderDeclInfo> GetTextureUsage(long Key); - void SetConstBuffer(long Key, int Cbuf, int DataSize, IntPtr HostAddress); - void EnsureTextureBinding(string UniformName, int Value); - void SetFlip(float X, float Y); - void Bind(long Key); void Unbind(GalShaderType Type); diff --git a/Ryujinx.Graphics/Gal/OpenGL/OGLBlend.cs b/Ryujinx.Graphics/Gal/OpenGL/OGLBlend.cs deleted file mode 100644 index 7175e3a0..00000000 --- a/Ryujinx.Graphics/Gal/OpenGL/OGLBlend.cs +++ /dev/null @@ -1,49 +0,0 @@ -using OpenTK.Graphics.OpenGL; - -namespace Ryujinx.Graphics.Gal.OpenGL -{ - public class OGLBlend : IGalBlend - { - public void Enable() - { - GL.Enable(EnableCap.Blend); - } - - public void Disable() - { - GL.Disable(EnableCap.Blend); - } - - public void Set( - GalBlendEquation Equation, - GalBlendFactor FuncSrc, - GalBlendFactor FuncDst) - { - GL.BlendEquation( - OGLEnumConverter.GetBlendEquation(Equation)); - - GL.BlendFunc( - OGLEnumConverter.GetBlendFactor(FuncSrc), - OGLEnumConverter.GetBlendFactor(FuncDst)); - } - - public void SetSeparate( - GalBlendEquation EquationRgb, - GalBlendEquation EquationAlpha, - GalBlendFactor FuncSrcRgb, - GalBlendFactor FuncDstRgb, - GalBlendFactor FuncSrcAlpha, - GalBlendFactor FuncDstAlpha) - { - GL.BlendEquationSeparate( - OGLEnumConverter.GetBlendEquation(EquationRgb), - OGLEnumConverter.GetBlendEquation(EquationAlpha)); - - GL.BlendFuncSeparate( - (BlendingFactorSrc)OGLEnumConverter.GetBlendFactor(FuncSrcRgb), - (BlendingFactorDest)OGLEnumConverter.GetBlendFactor(FuncDstRgb), - (BlendingFactorSrc)OGLEnumConverter.GetBlendFactor(FuncSrcAlpha), - (BlendingFactorDest)OGLEnumConverter.GetBlendFactor(FuncDstAlpha)); - } - } -}
\ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/OpenGL/OGLConstBuffer.cs b/Ryujinx.Graphics/Gal/OpenGL/OGLConstBuffer.cs new file mode 100644 index 00000000..50825541 --- /dev/null +++ b/Ryujinx.Graphics/Gal/OpenGL/OGLConstBuffer.cs @@ -0,0 +1,66 @@ +using OpenTK.Graphics.OpenGL; +using System; + +namespace Ryujinx.Graphics.Gal.OpenGL +{ + class OGLConstBuffer : IGalConstBuffer + { + private OGLCachedResource<OGLStreamBuffer> Cache; + + public OGLConstBuffer() + { + Cache = new OGLCachedResource<OGLStreamBuffer>(DeleteBuffer); + } + + public void LockCache() + { + Cache.Lock(); + } + + public void UnlockCache() + { + Cache.Unlock(); + } + + public void Create(long Key, long Size) + { + OGLStreamBuffer Buffer = new OGLStreamBuffer(BufferTarget.UniformBuffer, Size); + + Cache.AddOrUpdate(Key, Buffer, Size); + } + + public bool IsCached(long Key, long Size) + { + return Cache.TryGetSize(Key, out long CachedSize) && CachedSize == Size; + } + + public void SetData(long Key, long Size, IntPtr HostAddress) + { + if (!Cache.TryGetValue(Key, out OGLStreamBuffer Buffer)) + { + throw new InvalidOperationException(); + } + + Buffer.SetData(Size, HostAddress); + } + + public bool TryGetUbo(long Key, out int UboHandle) + { + if (Cache.TryGetValue(Key, out OGLStreamBuffer Buffer)) + { + UboHandle = Buffer.Handle; + + return true; + } + + UboHandle = 0; + + return false; + } + + private static void DeleteBuffer(OGLStreamBuffer Buffer) + { + Buffer.Dispose(); + } + } +}
\ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/OpenGL/OGLEnumConverter.cs b/Ryujinx.Graphics/Gal/OpenGL/OGLEnumConverter.cs index 9004e9ba..3c42e5d3 100644 --- a/Ryujinx.Graphics/Gal/OpenGL/OGLEnumConverter.cs +++ b/Ryujinx.Graphics/Gal/OpenGL/OGLEnumConverter.cs @@ -132,6 +132,7 @@ namespace Ryujinx.Graphics.Gal.OpenGL case GalTextureFormat.R32G32B32A32: return (PixelFormat.Rgba, PixelType.Float); case GalTextureFormat.R16G16B16A16: return (PixelFormat.Rgba, PixelType.HalfFloat); case GalTextureFormat.A8B8G8R8: return (PixelFormat.Rgba, PixelType.UnsignedByte); + case GalTextureFormat.A2B10G10R10: return (PixelFormat.Rgba, PixelType.UnsignedInt2101010Reversed); case GalTextureFormat.R32: return (PixelFormat.Red, PixelType.Float); case GalTextureFormat.A1B5G5R5: return (PixelFormat.Rgba, PixelType.UnsignedShort5551); case GalTextureFormat.B5G6R5: return (PixelFormat.Rgb, PixelType.UnsignedShort565); diff --git a/Ryujinx.Graphics/Gal/OpenGL/OGLPipeline.cs b/Ryujinx.Graphics/Gal/OpenGL/OGLPipeline.cs new file mode 100644 index 00000000..54f984cd --- /dev/null +++ b/Ryujinx.Graphics/Gal/OpenGL/OGLPipeline.cs @@ -0,0 +1,379 @@ +using OpenTK.Graphics.OpenGL; +using System; +using System.Collections.Generic; + +namespace Ryujinx.Graphics.Gal.OpenGL +{ + class OGLPipeline : IGalPipeline + { + private static Dictionary<GalVertexAttribSize, int> AttribElements = + new Dictionary<GalVertexAttribSize, int>() + { + { GalVertexAttribSize._32_32_32_32, 4 }, + { GalVertexAttribSize._32_32_32, 3 }, + { GalVertexAttribSize._16_16_16_16, 4 }, + { GalVertexAttribSize._32_32, 2 }, + { GalVertexAttribSize._16_16_16, 3 }, + { GalVertexAttribSize._8_8_8_8, 4 }, + { GalVertexAttribSize._16_16, 2 }, + { GalVertexAttribSize._32, 1 }, + { GalVertexAttribSize._8_8_8, 3 }, + { GalVertexAttribSize._8_8, 2 }, + { GalVertexAttribSize._16, 1 }, + { GalVertexAttribSize._8, 1 }, + { GalVertexAttribSize._10_10_10_2, 4 }, + { GalVertexAttribSize._11_11_10, 3 } + }; + + private static Dictionary<GalVertexAttribSize, VertexAttribPointerType> AttribTypes = + new Dictionary<GalVertexAttribSize, VertexAttribPointerType>() + { + { GalVertexAttribSize._32_32_32_32, VertexAttribPointerType.Int }, + { GalVertexAttribSize._32_32_32, VertexAttribPointerType.Int }, + { GalVertexAttribSize._16_16_16_16, VertexAttribPointerType.Short }, + { GalVertexAttribSize._32_32, VertexAttribPointerType.Int }, + { GalVertexAttribSize._16_16_16, VertexAttribPointerType.Short }, + { GalVertexAttribSize._8_8_8_8, VertexAttribPointerType.Byte }, + { GalVertexAttribSize._16_16, VertexAttribPointerType.Short }, + { GalVertexAttribSize._32, VertexAttribPointerType.Int }, + { GalVertexAttribSize._8_8_8, VertexAttribPointerType.Byte }, + { GalVertexAttribSize._8_8, VertexAttribPointerType.Byte }, + { GalVertexAttribSize._16, VertexAttribPointerType.Short }, + { GalVertexAttribSize._8, VertexAttribPointerType.Byte }, + { GalVertexAttribSize._10_10_10_2, VertexAttribPointerType.Int }, //? + { GalVertexAttribSize._11_11_10, VertexAttribPointerType.Int } //? + }; + + private GalPipelineState Old; + + private OGLConstBuffer Buffer; + private OGLRasterizer Rasterizer; + private OGLShader Shader; + + private int VaoHandle; + + public OGLPipeline(OGLConstBuffer Buffer, OGLRasterizer Rasterizer, OGLShader Shader) + { + this.Buffer = Buffer; + this.Rasterizer = Rasterizer; + this.Shader = Shader; + + //These values match OpenGL's defaults + Old = new GalPipelineState + { + FrontFace = GalFrontFace.CCW, + + CullFaceEnabled = false, + CullFace = GalCullFace.Back, + + DepthTestEnabled = false, + DepthFunc = GalComparisonOp.Less, + + StencilTestEnabled = false, + + StencilBackFuncFunc = GalComparisonOp.Always, + StencilBackFuncRef = 0, + StencilBackFuncMask = UInt32.MaxValue, + StencilBackOpFail = GalStencilOp.Keep, + StencilBackOpZFail = GalStencilOp.Keep, + StencilBackOpZPass = GalStencilOp.Keep, + StencilBackMask = UInt32.MaxValue, + + StencilFrontFuncFunc = GalComparisonOp.Always, + StencilFrontFuncRef = 0, + StencilFrontFuncMask = UInt32.MaxValue, + StencilFrontOpFail = GalStencilOp.Keep, + StencilFrontOpZFail = GalStencilOp.Keep, + StencilFrontOpZPass = GalStencilOp.Keep, + StencilFrontMask = UInt32.MaxValue, + + BlendEnabled = false, + BlendSeparateAlpha = false, + + BlendEquationRgb = 0, + BlendFuncSrcRgb = GalBlendFactor.One, + BlendFuncDstRgb = GalBlendFactor.Zero, + BlendEquationAlpha = 0, + BlendFuncSrcAlpha = GalBlendFactor.One, + BlendFuncDstAlpha = GalBlendFactor.Zero, + + PrimitiveRestartEnabled = false, + PrimitiveRestartIndex = 0 + }; + } + + public void Bind(GalPipelineState New) + { + BindConstBuffers(New); + + BindVertexLayout(New); + + if (New.FlipX != Old.FlipX || New.FlipY != Old.FlipY) + { + Shader.SetFlip(New.FlipX, New.FlipY); + } + + //Note: Uncomment SetFrontFace and SetCullFace when flipping issues are solved + + //if (New.FrontFace != O.FrontFace) + //{ + // GL.FrontFace(OGLEnumConverter.GetFrontFace(New.FrontFace)); + //} + + //if (New.CullFaceEnabled != O.CullFaceEnabled) + //{ + // Enable(EnableCap.CullFace, New.CullFaceEnabled); + //} + + //if (New.CullFaceEnabled) + //{ + // if (New.CullFace != O.CullFace) + // { + // GL.CullFace(OGLEnumConverter.GetCullFace(New.CullFace)); + // } + //} + + if (New.DepthTestEnabled != Old.DepthTestEnabled) + { + Enable(EnableCap.DepthTest, New.DepthTestEnabled); + } + + if (New.DepthTestEnabled) + { + if (New.DepthFunc != Old.DepthFunc) + { + GL.DepthFunc(OGLEnumConverter.GetDepthFunc(New.DepthFunc)); + } + } + + if (New.StencilTestEnabled != Old.StencilTestEnabled) + { + Enable(EnableCap.StencilTest, New.StencilTestEnabled); + } + + if (New.StencilTestEnabled) + { + if (New.StencilBackFuncFunc != Old.StencilBackFuncFunc || + New.StencilBackFuncRef != Old.StencilBackFuncRef || + New.StencilBackFuncMask != Old.StencilBackFuncMask) + { + GL.StencilFuncSeparate( + StencilFace.Back, + OGLEnumConverter.GetStencilFunc(New.StencilBackFuncFunc), + New.StencilBackFuncRef, + New.StencilBackFuncMask); + } + + if (New.StencilBackOpFail != Old.StencilBackOpFail || + New.StencilBackOpZFail != Old.StencilBackOpZFail || + New.StencilBackOpZPass != Old.StencilBackOpZPass) + { + GL.StencilOpSeparate( + StencilFace.Back, + OGLEnumConverter.GetStencilOp(New.StencilBackOpFail), + OGLEnumConverter.GetStencilOp(New.StencilBackOpZFail), + OGLEnumConverter.GetStencilOp(New.StencilBackOpZPass)); + } + + if (New.StencilBackMask != Old.StencilBackMask) + { + GL.StencilMaskSeparate(StencilFace.Back, New.StencilBackMask); + } + + if (New.StencilFrontFuncFunc != Old.StencilFrontFuncFunc || + New.StencilFrontFuncRef != Old.StencilFrontFuncRef || + New.StencilFrontFuncMask != Old.StencilFrontFuncMask) + { + GL.StencilFuncSeparate( + StencilFace.Front, + OGLEnumConverter.GetStencilFunc(New.StencilFrontFuncFunc), + New.StencilFrontFuncRef, + New.StencilFrontFuncMask); + } + + if (New.StencilFrontOpFail != Old.StencilFrontOpFail || + New.StencilFrontOpZFail != Old.StencilFrontOpZFail || + New.StencilFrontOpZPass != Old.StencilFrontOpZPass) + { + GL.StencilOpSeparate( + StencilFace.Front, + OGLEnumConverter.GetStencilOp(New.StencilFrontOpFail), + OGLEnumConverter.GetStencilOp(New.StencilFrontOpZFail), + OGLEnumConverter.GetStencilOp(New.StencilFrontOpZPass)); + } + + if (New.StencilFrontMask != Old.StencilFrontMask) + { + GL.StencilMaskSeparate(StencilFace.Front, New.StencilFrontMask); + } + } + + if (New.BlendEnabled != Old.BlendEnabled) + { + Enable(EnableCap.Blend, New.BlendEnabled); + } + + if (New.BlendEnabled) + { + if (New.BlendSeparateAlpha) + { + if (New.BlendEquationRgb != Old.BlendEquationRgb || + New.BlendEquationAlpha != Old.BlendEquationAlpha) + { + GL.BlendEquationSeparate( + OGLEnumConverter.GetBlendEquation(New.BlendEquationRgb), + OGLEnumConverter.GetBlendEquation(New.BlendEquationAlpha)); + } + + if (New.BlendFuncSrcRgb != Old.BlendFuncSrcRgb || + New.BlendFuncDstRgb != Old.BlendFuncDstRgb || + New.BlendFuncSrcAlpha != Old.BlendFuncSrcAlpha || + New.BlendFuncDstAlpha != Old.BlendFuncDstAlpha) + { + GL.BlendFuncSeparate( + (BlendingFactorSrc) OGLEnumConverter.GetBlendFactor(New.BlendFuncSrcRgb), + (BlendingFactorDest)OGLEnumConverter.GetBlendFactor(New.BlendFuncDstRgb), + (BlendingFactorSrc) OGLEnumConverter.GetBlendFactor(New.BlendFuncSrcAlpha), + (BlendingFactorDest)OGLEnumConverter.GetBlendFactor(New.BlendFuncDstAlpha)); + } + } + else + { + if (New.BlendEquationRgb != Old.BlendEquationRgb) + { + GL.BlendEquation(OGLEnumConverter.GetBlendEquation(New.BlendEquationRgb)); + } + + if (New.BlendFuncSrcRgb != Old.BlendFuncSrcRgb || + New.BlendFuncDstRgb != Old.BlendFuncDstRgb) + { + GL.BlendFunc( + OGLEnumConverter.GetBlendFactor(New.BlendFuncSrcRgb), + OGLEnumConverter.GetBlendFactor(New.BlendFuncDstRgb)); + } + } + } + + if (New.PrimitiveRestartEnabled != Old.PrimitiveRestartEnabled) + { + Enable(EnableCap.PrimitiveRestart, New.PrimitiveRestartEnabled); + } + + if (New.PrimitiveRestartEnabled) + { + if (New.PrimitiveRestartIndex != Old.PrimitiveRestartIndex) + { + GL.PrimitiveRestartIndex(New.PrimitiveRestartIndex); + } + } + + Old = New; + } + + private void BindConstBuffers(GalPipelineState New) + { + //Index 0 is reserved + int FreeBinding = 1; + + void BindIfNotNull(OGLShaderStage Stage) + { + if (Stage != null) + { + foreach (ShaderDeclInfo DeclInfo in Stage.UniformUsage) + { + long Key = New.ConstBufferKeys[(int)Stage.Type][DeclInfo.Cbuf]; + + if (Key != 0 && Buffer.TryGetUbo(Key, out int UboHandle)) + { + GL.BindBufferBase(BufferRangeTarget.UniformBuffer, FreeBinding, UboHandle); + } + + FreeBinding++; + } + } + } + + BindIfNotNull(Shader.Current.Vertex); + BindIfNotNull(Shader.Current.TessControl); + BindIfNotNull(Shader.Current.TessEvaluation); + BindIfNotNull(Shader.Current.Geometry); + BindIfNotNull(Shader.Current.Fragment); + } + + private void BindVertexLayout(GalPipelineState New) + { + foreach (GalVertexBinding Binding in New.VertexBindings) + { + if (!Binding.Enabled || !Rasterizer.TryGetVbo(Binding.VboKey, out int VboHandle)) + { + continue; + } + + if (VaoHandle == 0) + { + VaoHandle = GL.GenVertexArray(); + + //Vertex arrays shouldn't be used anywhere else in OpenGL's backend + //if you want to use it, move this line out of the if + GL.BindVertexArray(VaoHandle); + } + + foreach (GalVertexAttrib Attrib in Binding.Attribs) + { + GL.EnableVertexAttribArray(Attrib.Index); + + GL.BindBuffer(BufferTarget.ArrayBuffer, VboHandle); + + bool Unsigned = + Attrib.Type == GalVertexAttribType.Unorm || + Attrib.Type == GalVertexAttribType.Uint || + Attrib.Type == GalVertexAttribType.Uscaled; + + bool Normalize = + Attrib.Type == GalVertexAttribType.Snorm || + Attrib.Type == GalVertexAttribType.Unorm; + + VertexAttribPointerType Type = 0; + + if (Attrib.Type == GalVertexAttribType.Float) + { + Type = VertexAttribPointerType.Float; + } + else + { + Type = AttribTypes[Attrib.Size] + (Unsigned ? 1 : 0); + } + + int Size = AttribElements[Attrib.Size]; + int Offset = Attrib.Offset; + + if (Attrib.Type == GalVertexAttribType.Sint || + Attrib.Type == GalVertexAttribType.Uint) + { + IntPtr Pointer = new IntPtr(Offset); + + VertexAttribIntegerType IType = (VertexAttribIntegerType)Type; + + GL.VertexAttribIPointer(Attrib.Index, Size, IType, Binding.Stride, Pointer); + } + else + { + GL.VertexAttribPointer(Attrib.Index, Size, Type, Normalize, Binding.Stride, Offset); + } + } + } + } + + private void Enable(EnableCap Cap, bool Enabled) + { + if (Enabled) + { + GL.Enable(Cap); + } + else + { + GL.Disable(Cap); + } + } + } +}
\ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/OpenGL/OGLRasterizer.cs b/Ryujinx.Graphics/Gal/OpenGL/OGLRasterizer.cs index f2e5859e..08021478 100644 --- a/Ryujinx.Graphics/Gal/OpenGL/OGLRasterizer.cs +++ b/Ryujinx.Graphics/Gal/OpenGL/OGLRasterizer.cs @@ -6,46 +6,6 @@ namespace Ryujinx.Graphics.Gal.OpenGL { public class OGLRasterizer : IGalRasterizer { - private static Dictionary<GalVertexAttribSize, int> AttribElements = - new Dictionary<GalVertexAttribSize, int>() - { - { GalVertexAttribSize._32_32_32_32, 4 }, - { GalVertexAttribSize._32_32_32, 3 }, - { GalVertexAttribSize._16_16_16_16, 4 }, - { GalVertexAttribSize._32_32, 2 }, - { GalVertexAttribSize._16_16_16, 3 }, - { GalVertexAttribSize._8_8_8_8, 4 }, - { GalVertexAttribSize._16_16, 2 }, - { GalVertexAttribSize._32, 1 }, - { GalVertexAttribSize._8_8_8, 3 }, - { GalVertexAttribSize._8_8, 2 }, - { GalVertexAttribSize._16, 1 }, - { GalVertexAttribSize._8, 1 }, - { GalVertexAttribSize._10_10_10_2, 4 }, - { GalVertexAttribSize._11_11_10, 3 } - }; - - private static Dictionary<GalVertexAttribSize, VertexAttribPointerType> AttribTypes = - new Dictionary<GalVertexAttribSize, VertexAttribPointerType>() - { - { GalVertexAttribSize._32_32_32_32, VertexAttribPointerType.Int }, - { GalVertexAttribSize._32_32_32, VertexAttribPointerType.Int }, - { GalVertexAttribSize._16_16_16_16, VertexAttribPointerType.Short }, - { GalVertexAttribSize._32_32, VertexAttribPointerType.Int }, - { GalVertexAttribSize._16_16_16, VertexAttribPointerType.Short }, - { GalVertexAttribSize._8_8_8_8, VertexAttribPointerType.Byte }, - { GalVertexAttribSize._16_16, VertexAttribPointerType.Short }, - { GalVertexAttribSize._32, VertexAttribPointerType.Int }, - { GalVertexAttribSize._8_8_8, VertexAttribPointerType.Byte }, - { GalVertexAttribSize._8_8, VertexAttribPointerType.Byte }, - { GalVertexAttribSize._16, VertexAttribPointerType.Short }, - { GalVertexAttribSize._8, VertexAttribPointerType.Byte }, - { GalVertexAttribSize._10_10_10_2, VertexAttribPointerType.Int }, //? - { GalVertexAttribSize._11_11_10, VertexAttribPointerType.Int } //? - }; - - private int VaoHandle; - private int[] VertexBuffers; private OGLCachedResource<int> VboCache; @@ -83,7 +43,11 @@ namespace Ryujinx.Graphics.Gal.OpenGL IboCache.Unlock(); } - public void ClearBuffers(GalClearBufferFlags Flags) + public void ClearBuffers( + GalClearBufferFlags Flags, + float Red, float Green, float Blue, float Alpha, + float Depth, + int Stencil) { ClearBufferMask Mask = ClearBufferMask.ColorBufferBit; @@ -103,6 +67,12 @@ namespace Ryujinx.Graphics.Gal.OpenGL Mask |= ClearBufferMask.StencilBufferBit; } + GL.ClearColor(Red, Green, Blue, Alpha); + + GL.ClearDepth(Depth); + + GL.ClearStencil(Stencil); + GL.Clear(Mask); GL.ColorMask(true, true, true, true); @@ -118,99 +88,6 @@ 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); - } - - public void DisableCullFace() - { - GL.Disable(EnableCap.CullFace); - } - - public void SetCullFace(GalCullFace CullFace) - { - GL.CullFace(OGLEnumConverter.GetCullFace(CullFace)); - } - - public void EnableDepthTest() - { - GL.Enable(EnableCap.DepthTest); - } - - public void DisableDepthTest() - { - GL.Disable(EnableCap.DepthTest); - } - - public void SetDepthFunction(GalComparisonOp Func) - { - 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 EnablePrimitiveRestart() - { - GL.Enable(EnableCap.PrimitiveRestart); - } - - public void DisablePrimitiveRestart() - { - GL.Disable(EnableCap.PrimitiveRestart); - } - - public void SetPrimitiveRestartIndex(uint Index) - { - GL.PrimitiveRestartIndex(Index); - } - public void CreateVbo(long Key, int DataSize, IntPtr HostAddress) { int Handle = GL.GenBuffer(); @@ -235,53 +112,6 @@ namespace Ryujinx.Graphics.Gal.OpenGL GL.BufferData(BufferTarget.ElementArrayBuffer, Length, HostAddress, BufferUsageHint.StreamDraw); } - public void SetVertexArray(int Stride, long VboKey, GalVertexAttrib[] Attribs) - { - if (!VboCache.TryGetValue(VboKey, out int VboHandle)) - { - return; - } - - if (VaoHandle == 0) - { - VaoHandle = GL.GenVertexArray(); - } - - GL.BindVertexArray(VaoHandle); - - foreach (GalVertexAttrib Attrib in Attribs) - { - GL.EnableVertexAttribArray(Attrib.Index); - - GL.BindBuffer(BufferTarget.ArrayBuffer, VboHandle); - - bool Unsigned = - Attrib.Type == GalVertexAttribType.Unorm || - Attrib.Type == GalVertexAttribType.Uint || - Attrib.Type == GalVertexAttribType.Uscaled; - - bool Normalize = - Attrib.Type == GalVertexAttribType.Snorm || - Attrib.Type == GalVertexAttribType.Unorm; - - VertexAttribPointerType Type = 0; - - if (Attrib.Type == GalVertexAttribType.Float) - { - Type = VertexAttribPointerType.Float; - } - else - { - Type = AttribTypes[Attrib.Size] + (Unsigned ? 1 : 0); - } - - int Size = AttribElements[Attrib.Size]; - int Offset = Attrib.Offset; - - GL.VertexAttribPointer(Attrib.Index, Size, Type, Normalize, Stride, Offset); - } - } - public void SetIndexArray(int Size, GalIndexFormat Format) { IndexBuffer.Type = OGLEnumConverter.GetDrawElementsType(Format); @@ -298,8 +128,6 @@ namespace Ryujinx.Graphics.Gal.OpenGL return; } - GL.BindVertexArray(VaoHandle); - GL.DrawArrays(OGLEnumConverter.GetPrimitiveType(PrimType), First, PrimCount); } @@ -312,8 +140,6 @@ namespace Ryujinx.Graphics.Gal.OpenGL PrimitiveType Mode = OGLEnumConverter.GetPrimitiveType(PrimType); - GL.BindVertexArray(VaoHandle); - GL.BindBuffer(BufferTarget.ElementArrayBuffer, IboHandle); First <<= IndexBuffer.ElemSizeLog2; @@ -329,5 +155,10 @@ namespace Ryujinx.Graphics.Gal.OpenGL GL.DrawElements(Mode, IndexBuffer.Count, IndexBuffer.Type, First); } } + + public bool TryGetVbo(long VboKey, out int VboHandle) + { + return VboCache.TryGetValue(VboKey, out VboHandle); + } } }
\ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/OpenGL/OGLRenderer.cs b/Ryujinx.Graphics/Gal/OpenGL/OGLRenderer.cs index ca70d4f6..b0f6da45 100644 --- a/Ryujinx.Graphics/Gal/OpenGL/OGLRenderer.cs +++ b/Ryujinx.Graphics/Gal/OpenGL/OGLRenderer.cs @@ -5,7 +5,7 @@ namespace Ryujinx.Graphics.Gal.OpenGL { public class OGLRenderer : IGalRenderer { - public IGalBlend Blend { get; private set; } + public IGalConstBuffer Buffer { get; private set; } public IGalFrameBuffer FrameBuffer { get; private set; } @@ -13,19 +13,23 @@ namespace Ryujinx.Graphics.Gal.OpenGL public IGalShader Shader { get; private set; } + public IGalPipeline Pipeline { get; private set; } + public IGalTexture Texture { get; private set; } private ConcurrentQueue<Action> ActionsQueue; public OGLRenderer() { - Blend = new OGLBlend(); + Buffer = new OGLConstBuffer(); FrameBuffer = new OGLFrameBuffer(); Rasterizer = new OGLRasterizer(); - Shader = new OGLShader(); + Shader = new OGLShader(Buffer as OGLConstBuffer); + + Pipeline = new OGLPipeline(Buffer as OGLConstBuffer, Rasterizer as OGLRasterizer, Shader as OGLShader); Texture = new OGLTexture(); diff --git a/Ryujinx.Graphics/Gal/OpenGL/OGLShader.cs b/Ryujinx.Graphics/Gal/OpenGL/OGLShader.cs index 37213d8e..4792bc5e 100644 --- a/Ryujinx.Graphics/Gal/OpenGL/OGLShader.cs +++ b/Ryujinx.Graphics/Gal/OpenGL/OGLShader.cs @@ -5,96 +5,29 @@ using System.Collections.Concurrent; using System.Collections.Generic; using System.Linq; -using Buffer = System.Buffer; - namespace Ryujinx.Graphics.Gal.OpenGL { - public class OGLShader : IGalShader + class OGLShader : IGalShader { - private class ShaderStage : IDisposable - { - public int Handle { get; private set; } - - public bool IsCompiled { get; private set; } - - public GalShaderType Type { get; private set; } - - public string Code { get; private set; } - - public IEnumerable<ShaderDeclInfo> TextureUsage { get; private set; } - public IEnumerable<ShaderDeclInfo> UniformUsage { get; private set; } - - public ShaderStage( - GalShaderType Type, - string Code, - IEnumerable<ShaderDeclInfo> TextureUsage, - IEnumerable<ShaderDeclInfo> UniformUsage) - { - this.Type = Type; - this.Code = Code; - this.TextureUsage = TextureUsage; - this.UniformUsage = UniformUsage; - } - - public void Compile() - { - if (Handle == 0) - { - Handle = GL.CreateShader(OGLEnumConverter.GetShaderType(Type)); - - CompileAndCheck(Handle, Code); - } - } - - public void Dispose() - { - Dispose(true); - } - - protected virtual void Dispose(bool Disposing) - { - if (Disposing && Handle != 0) - { - GL.DeleteShader(Handle); - - Handle = 0; - } - } - } - - private struct ShaderProgram - { - public ShaderStage Vertex; - public ShaderStage TessControl; - public ShaderStage TessEvaluation; - public ShaderStage Geometry; - public ShaderStage Fragment; - } - - const int ConstBuffersPerStage = 18; - - private ShaderProgram Current; + public OGLShaderProgram Current; - private ConcurrentDictionary<long, ShaderStage> Stages; + private ConcurrentDictionary<long, OGLShaderStage> Stages; - private Dictionary<ShaderProgram, int> Programs; + private Dictionary<OGLShaderProgram, int> Programs; public int CurrentProgramHandle { get; private set; } - private OGLStreamBuffer[][] ConstBuffers; + private OGLConstBuffer Buffer; - public OGLShader() - { - Stages = new ConcurrentDictionary<long, ShaderStage>(); + private int ExtraUboHandle; - Programs = new Dictionary<ShaderProgram, int>(); + public OGLShader(OGLConstBuffer Buffer) + { + this.Buffer = Buffer; - ConstBuffers = new OGLStreamBuffer[5][]; + Stages = new ConcurrentDictionary<long, OGLShaderStage>(); - for (int i = 0; i < 5; i++) - { - ConstBuffers[i] = new OGLStreamBuffer[ConstBuffersPerStage]; - } + Programs = new Dictionary<OGLShaderProgram, int>(); } public void Create(IGalMemory Memory, long Key, GalShaderType Type) @@ -107,7 +40,7 @@ namespace Ryujinx.Graphics.Gal.OpenGL Stages.GetOrAdd(Key, (Stage) => ShaderStageFactory(Memory, VpAPos, Key, true, Type)); } - private ShaderStage ShaderStageFactory( + private OGLShaderStage ShaderStageFactory( IGalMemory Memory, long Position, long PositionB, @@ -136,7 +69,7 @@ namespace Ryujinx.Graphics.Gal.OpenGL Program = Decompiler.Decompile(Memory, Position, Type); } - return new ShaderStage( + return new OGLShaderStage( Type, Program.Code, Program.Textures, @@ -145,7 +78,7 @@ namespace Ryujinx.Graphics.Gal.OpenGL public IEnumerable<ShaderDeclInfo> GetTextureUsage(long Key) { - if (Stages.TryGetValue(Key, out ShaderStage Stage)) + if (Stages.TryGetValue(Key, out OGLShaderStage Stage)) { return Stage.TextureUsage; } @@ -153,21 +86,6 @@ namespace Ryujinx.Graphics.Gal.OpenGL return Enumerable.Empty<ShaderDeclInfo>(); } - public void SetConstBuffer(long Key, int Cbuf, int DataSize, IntPtr HostAddress) - { - if (Stages.TryGetValue(Key, out ShaderStage Stage)) - { - foreach (ShaderDeclInfo DeclInfo in Stage.UniformUsage.Where(x => x.Cbuf == Cbuf)) - { - OGLStreamBuffer Buffer = GetConstBuffer(Stage.Type, Cbuf); - - int Size = Math.Min(DataSize, Buffer.Size); - - Buffer.SetData(Size, HostAddress); - } - } - } - public void EnsureTextureBinding(string UniformName, int Value) { BindProgram(); @@ -177,24 +95,33 @@ namespace Ryujinx.Graphics.Gal.OpenGL GL.Uniform1(Location, Value); } - public void SetFlip(float X, float Y) + public unsafe void SetFlip(float X, float Y) { BindProgram(); - int Location = GL.GetUniformLocation(CurrentProgramHandle, GlslDecl.FlipUniformName); + EnsureExtraBlock(); + + GL.BindBuffer(BufferTarget.UniformBuffer, ExtraUboHandle); + + float* Data = stackalloc float[4]; + Data[0] = X; + Data[1] = Y; - GL.Uniform2(Location, X, Y); + //Invalidate buffer + GL.BufferData(BufferTarget.UniformBuffer, 4 * sizeof(float), IntPtr.Zero, BufferUsageHint.StreamDraw); + + GL.BufferSubData(BufferTarget.UniformBuffer, IntPtr.Zero, 4 * sizeof(float), (IntPtr)Data); } public void Bind(long Key) { - if (Stages.TryGetValue(Key, out ShaderStage Stage)) + if (Stages.TryGetValue(Key, out OGLShaderStage Stage)) { Bind(Stage); } } - private void Bind(ShaderStage Stage) + private void Bind(OGLShaderStage Stage) { if (Stage.Type == GalShaderType.Geometry) { @@ -257,12 +184,24 @@ namespace Ryujinx.Graphics.Gal.OpenGL GL.UseProgram(Handle); - BindUniformBuffers(Handle); - CurrentProgramHandle = Handle; } - private void AttachIfNotNull(int ProgramHandle, ShaderStage Stage) + private void EnsureExtraBlock() + { + if (ExtraUboHandle == 0) + { + ExtraUboHandle = GL.GenBuffer(); + + GL.BindBuffer(BufferTarget.UniformBuffer, ExtraUboHandle); + + GL.BufferData(BufferTarget.UniformBuffer, 4 * sizeof(float), IntPtr.Zero, BufferUsageHint.StreamDraw); + + GL.BindBufferBase(BufferRangeTarget.UniformBuffer, 0, ExtraUboHandle); + } + } + + private void AttachIfNotNull(int ProgramHandle, OGLShaderStage Stage) { if (Stage != null) { @@ -274,9 +213,14 @@ namespace Ryujinx.Graphics.Gal.OpenGL private void BindUniformBlocks(int ProgramHandle) { - int FreeBinding = 0; + int ExtraBlockindex = GL.GetUniformBlockIndex(ProgramHandle, GlslDecl.ExtraUniformBlockName); + + GL.UniformBlockBinding(ProgramHandle, ExtraBlockindex, 0); + + //First index is reserved + int FreeBinding = 1; - void BindUniformBlocksIfNotNull(ShaderStage Stage) + void BindUniformBlocksIfNotNull(OGLShaderStage Stage) { if (Stage != null) { @@ -304,71 +248,6 @@ namespace Ryujinx.Graphics.Gal.OpenGL BindUniformBlocksIfNotNull(Current.Fragment); } - private void BindUniformBuffers(int ProgramHandle) - { - int FreeBinding = 0; - - void BindUniformBuffersIfNotNull(ShaderStage Stage) - { - if (Stage != null) - { - foreach (ShaderDeclInfo DeclInfo in Stage.UniformUsage) - { - OGLStreamBuffer Buffer = GetConstBuffer(Stage.Type, DeclInfo.Cbuf); - - GL.BindBufferBase(BufferRangeTarget.UniformBuffer, FreeBinding, Buffer.Handle); - - FreeBinding++; - } - } - } - - BindUniformBuffersIfNotNull(Current.Vertex); - BindUniformBuffersIfNotNull(Current.TessControl); - BindUniformBuffersIfNotNull(Current.TessEvaluation); - BindUniformBuffersIfNotNull(Current.Geometry); - BindUniformBuffersIfNotNull(Current.Fragment); - } - - private OGLStreamBuffer GetConstBuffer(GalShaderType StageType, int Cbuf) - { - int StageIndex = (int)StageType; - - OGLStreamBuffer Buffer = ConstBuffers[StageIndex][Cbuf]; - - if (Buffer == null) - { - //Allocate a maximum of 64 KiB - int Size = Math.Min(GL.GetInteger(GetPName.MaxUniformBlockSize), 64 * 1024); - - Buffer = new OGLStreamBuffer(BufferTarget.UniformBuffer, Size); - - ConstBuffers[StageIndex][Cbuf] = Buffer; - } - - return Buffer; - } - - public static void CompileAndCheck(int Handle, string Code) - { - GL.ShaderSource(Handle, Code); - GL.CompileShader(Handle); - - CheckCompilation(Handle); - } - - private static void CheckCompilation(int Handle) - { - int Status = 0; - - GL.GetShader(Handle, ShaderParameter.CompileStatus, out Status); - - if (Status == 0) - { - throw new ShaderException(GL.GetShaderInfoLog(Handle)); - } - } - private static void CheckProgramLink(int Handle) { int Status = 0; diff --git a/Ryujinx.Graphics/Gal/OpenGL/OGLShaderProgram.cs b/Ryujinx.Graphics/Gal/OpenGL/OGLShaderProgram.cs new file mode 100644 index 00000000..731994ce --- /dev/null +++ b/Ryujinx.Graphics/Gal/OpenGL/OGLShaderProgram.cs @@ -0,0 +1,86 @@ +using OpenTK.Graphics.OpenGL; +using System; +using System.Collections.Generic; + +namespace Ryujinx.Graphics.Gal.OpenGL +{ + struct OGLShaderProgram + { + public OGLShaderStage Vertex; + public OGLShaderStage TessControl; + public OGLShaderStage TessEvaluation; + public OGLShaderStage Geometry; + public OGLShaderStage Fragment; + } + + class OGLShaderStage : IDisposable + { + public int Handle { get; private set; } + + public bool IsCompiled { get; private set; } + + public GalShaderType Type { get; private set; } + + public string Code { get; private set; } + + public IEnumerable<ShaderDeclInfo> TextureUsage { get; private set; } + public IEnumerable<ShaderDeclInfo> UniformUsage { get; private set; } + + public OGLShaderStage( + GalShaderType Type, + string Code, + IEnumerable<ShaderDeclInfo> TextureUsage, + IEnumerable<ShaderDeclInfo> UniformUsage) + { + this.Type = Type; + this.Code = Code; + this.TextureUsage = TextureUsage; + this.UniformUsage = UniformUsage; + } + + public void Compile() + { + if (Handle == 0) + { + Handle = GL.CreateShader(OGLEnumConverter.GetShaderType(Type)); + + CompileAndCheck(Handle, Code); + } + } + + public void Dispose() + { + Dispose(true); + } + + protected virtual void Dispose(bool Disposing) + { + if (Disposing && Handle != 0) + { + GL.DeleteShader(Handle); + + Handle = 0; + } + } + + public static void CompileAndCheck(int Handle, string Code) + { + GL.ShaderSource(Handle, Code); + GL.CompileShader(Handle); + + CheckCompilation(Handle); + } + + private static void CheckCompilation(int Handle) + { + int Status = 0; + + GL.GetShader(Handle, ShaderParameter.CompileStatus, out Status); + + if (Status == 0) + { + throw new ShaderException(GL.GetShaderInfoLog(Handle)); + } + } + } +}
\ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/OpenGL/OGLStreamBuffer.cs b/Ryujinx.Graphics/Gal/OpenGL/OGLStreamBuffer.cs index 0d5dee93..94639405 100644 --- a/Ryujinx.Graphics/Gal/OpenGL/OGLStreamBuffer.cs +++ b/Ryujinx.Graphics/Gal/OpenGL/OGLStreamBuffer.cs @@ -7,11 +7,11 @@ namespace Ryujinx.Graphics.Gal.OpenGL { public int Handle { get; protected set; } - public int Size { get; protected set; } + public long Size { get; protected set; } protected BufferTarget Target { get; private set; } - public OGLStreamBuffer(BufferTarget Target, int Size) + public OGLStreamBuffer(BufferTarget Target, long Size) { this.Target = Target; this.Size = Size; @@ -20,14 +20,14 @@ namespace Ryujinx.Graphics.Gal.OpenGL GL.BindBuffer(Target, Handle); - GL.BufferData(Target, Size, IntPtr.Zero, BufferUsageHint.StreamDraw); + GL.BufferData(Target, (IntPtr)Size, IntPtr.Zero, BufferUsageHint.StreamDraw); } - public void SetData(int Size, IntPtr HostAddress) + public void SetData(long Size, IntPtr HostAddress) { GL.BindBuffer(Target, Handle); - GL.BufferSubData(Target, IntPtr.Zero, Size, HostAddress); + GL.BufferSubData(Target, IntPtr.Zero, (IntPtr)Size, HostAddress); } public void Dispose() diff --git a/Ryujinx.Graphics/Gal/Shader/GlslDecl.cs b/Ryujinx.Graphics/Gal/Shader/GlslDecl.cs index 7688545c..a0c747ba 100644 --- a/Ryujinx.Graphics/Gal/Shader/GlslDecl.cs +++ b/Ryujinx.Graphics/Gal/Shader/GlslDecl.cs @@ -35,6 +35,7 @@ namespace Ryujinx.Graphics.Gal.Shader public const string FragmentOutputName = "FragColor"; + public const string ExtraUniformBlockName = "Extra"; public const string FlipUniformName = "flip"; public const string ProgramName = "program"; diff --git a/Ryujinx.Graphics/Gal/Shader/GlslDecompiler.cs b/Ryujinx.Graphics/Gal/Shader/GlslDecompiler.cs index a338f404..aa1803a5 100644 --- a/Ryujinx.Graphics/Gal/Shader/GlslDecompiler.cs +++ b/Ryujinx.Graphics/Gal/Shader/GlslDecompiler.cs @@ -23,8 +23,6 @@ namespace Ryujinx.Graphics.Gal.Shader private const int MaxVertexInput = 3; - private static string[] ElemTypes = new string[] { "float", "vec2", "vec3", "vec4" }; - private GlslDecl Decl; private ShaderHeader Header, HeaderB; @@ -222,7 +220,11 @@ namespace Ryujinx.Graphics.Gal.Shader { if (Decl.ShaderType == GalShaderType.Vertex) { - SB.AppendLine("uniform vec2 " + GlslDecl.FlipUniformName + ";"); + SB.AppendLine("layout (std140) uniform " + GlslDecl.ExtraUniformBlockName + "{"); + + SB.AppendLine(IdentationStr + "vec2 " + GlslDecl.FlipUniformName + ";"); + + SB.AppendLine("};"); } SB.AppendLine(); @@ -266,7 +268,7 @@ namespace Ryujinx.Graphics.Gal.Shader { if (DeclInfo.Index >= 0) { - SB.AppendLine(IdentationStr + "layout (location = " + DeclInfo.Index + ") " + GetDecl(DeclInfo) + "; "); + SB.AppendLine(IdentationStr + "layout (location = " + DeclInfo.Index + ") vec4 " + DeclInfo.Name + "; "); } } @@ -297,7 +299,7 @@ namespace Ryujinx.Graphics.Gal.Shader { if (DeclInfo.Index >= 0) { - SB.AppendLine("layout (location = " + DeclInfo.Index + ") " + InOut + " " + GetDecl(DeclInfo) + ";"); + SB.AppendLine("layout (location = " + DeclInfo.Index + ") " + InOut + " vec4 " + DeclInfo.Name + ";"); Count++; } @@ -331,7 +333,7 @@ namespace Ryujinx.Graphics.Gal.Shader } else if (DeclInfo.Name == GlslDecl.FragmentOutputName) { - Name = "layout (location = 0) out " + GetDecl(DeclInfo) + Suffix + ";" + Environment.NewLine; + Name = "layout (location = 0) out vec4 " + DeclInfo.Name + Suffix + ";" + Environment.NewLine; } else { @@ -354,7 +356,14 @@ namespace Ryujinx.Graphics.Gal.Shader private string GetDecl(ShaderDeclInfo DeclInfo) { - return ElemTypes[DeclInfo.Size - 1] + " " + DeclInfo.Name; + if (DeclInfo.Size == 4) + { + return "vec4 " + DeclInfo.Name; + } + else + { + return "float " + DeclInfo.Name; + } } private void PrintMain() @@ -370,13 +379,11 @@ namespace Ryujinx.Graphics.Gal.Shader ShaderDeclInfo DeclInfo = KV.Value; - string Swizzle = ".xyzw".Substring(0, DeclInfo.Size + 1); - if (Decl.ShaderType == GalShaderType.Geometry) { for (int Vertex = 0; Vertex < MaxVertexInput; Vertex++) { - string Dst = Attr.Name + "[" + Vertex + "]" + Swizzle; + string Dst = Attr.Name + "[" + Vertex + "]"; string Src = "block_in[" + Vertex + "]." + DeclInfo.Name; @@ -385,7 +392,7 @@ namespace Ryujinx.Graphics.Gal.Shader } else { - SB.AppendLine(IdentationStr + Attr.Name + Swizzle + " = " + DeclInfo.Name + ";"); + SB.AppendLine(IdentationStr + Attr.Name + " = " + DeclInfo.Name + ";"); } } @@ -418,8 +425,6 @@ namespace Ryujinx.Graphics.Gal.Shader ShaderDeclInfo DeclInfo = KV.Value; - string Swizzle = ".xyzw".Substring(0, DeclInfo.Size + 1); - string Name = Attr.Name; if (Decl.ShaderType == GalShaderType.Geometry) @@ -427,7 +432,7 @@ namespace Ryujinx.Graphics.Gal.Shader Name += "[0]"; } - SB.AppendLine(Identation + DeclInfo.Name + " = " + Name + Swizzle + ";"); + SB.AppendLine(Identation + DeclInfo.Name + " = " + Name + ";"); } if (Decl.ShaderType == GalShaderType.Vertex) @@ -1258,4 +1263,4 @@ namespace Ryujinx.Graphics.Gal.Shader throw new ArgumentException(nameof(Node)); } } -}
\ No newline at end of file +} diff --git a/Ryujinx.Graphics/Gal/Shader/ShaderDecodeAlu.cs b/Ryujinx.Graphics/Gal/Shader/ShaderDecodeAlu.cs index 00f072f1..8486d8a7 100644 --- a/Ryujinx.Graphics/Gal/Shader/ShaderDecodeAlu.cs +++ b/Ryujinx.Graphics/Gal/Shader/ShaderDecodeAlu.cs @@ -871,11 +871,12 @@ namespace Ryujinx.Graphics.Gal.Shader private static void EmitSet(ShaderIrBlock Block, long OpCode, bool IsFloat, ShaderOper Oper) { - bool NegA = ((OpCode >> 43) & 1) != 0; - bool AbsB = ((OpCode >> 44) & 1) != 0; - bool BoolFloat = ((OpCode >> 52) & 1) != 0; - bool NegB = ((OpCode >> 53) & 1) != 0; - bool AbsA = ((OpCode >> 54) & 1) != 0; + bool NegA = ((OpCode >> 43) & 1) != 0; + bool AbsB = ((OpCode >> 44) & 1) != 0; + bool NegB = ((OpCode >> 53) & 1) != 0; + bool AbsA = ((OpCode >> 54) & 1) != 0; + + bool BoolFloat = ((OpCode >> (IsFloat ? 52 : 44)) & 1) != 0; ShaderIrNode OperA = GetOperGpr8(OpCode), OperB; diff --git a/Ryujinx.HLE/Font/SharedFontManager.cs b/Ryujinx.HLE/Font/SharedFontManager.cs new file mode 100644 index 00000000..fce270de --- /dev/null +++ b/Ryujinx.HLE/Font/SharedFontManager.cs @@ -0,0 +1,177 @@ +using ChocolArm64.Exceptions; +using ChocolArm64.Memory; +using Ryujinx.HLE.Logging; +using Ryujinx.HLE.OsHle; +using Ryujinx.HLE.OsHle.Handles; +using Ryujinx.HLE.Resource; +using System; +using System.Collections.Generic; +using System.IO; + + +namespace Ryujinx.HLE.Font +{ + public class SharedFontManager + { + private const uint SharedMemorySize = 0x1100000; + private Logger Log; + + private string FontsPath; + + private object ShMemLock; + + private (AMemory, long, long)[] ShMemPositions; + + private Dictionary<SharedFontType, byte[]> FontData; + + private uint[] LoadedFonts; + + public SharedFontManager(Logger Log, string SystemPath) + { + this.Log = Log; + this.FontsPath = Path.Combine(SystemPath, "fonts"); + + ShMemLock = new object(); + + ShMemPositions = new(AMemory, long, long)[0]; + + FontData = new Dictionary<SharedFontType, byte[]>() + { + { SharedFontType.JapanUsEurope, GetData("FontStandard") }, + { SharedFontType.SimplifiedChinese, GetData("FontChineseSimplified") }, + { SharedFontType.SimplifiedChineseEx, GetData("FontExtendedChineseSimplified") }, + { SharedFontType.TraditionalChinese, GetData("FontChineseTraditional") }, + { SharedFontType.Korean, GetData("FontKorean") }, + { SharedFontType.NintendoEx, GetData("FontNintendoExtended") } + }; + + int FontMemoryUsage = 0; + foreach (byte[] data in FontData.Values) + { + FontMemoryUsage += data.Length; + FontMemoryUsage += 0x8; + } + + if (FontMemoryUsage > SharedMemorySize) + { + throw new InvalidSystemResourceException($"The sum of all fonts size exceed the shared memory size. Please make sure that the fonts don't exceed {SharedMemorySize} bytes in total. (actual size: {FontMemoryUsage} bytes)"); + } + + LoadedFonts = new uint[FontData.Count]; + } + + public byte[] GetData(string FontName) + { + string FontFilePath = Path.Combine(FontsPath, $"{FontName}.ttf"); + if (File.Exists(FontFilePath)) + { + return File.ReadAllBytes(FontFilePath); + } + else + { + throw new InvalidSystemResourceException($"Font \"{FontName}.ttf\" not found. Please provide it in \"{FontsPath}\"."); + } + } + + public void MapFont(SharedFontType FontType, AMemory Memory, long Position) + { + uint SharedMemoryAddressOffset = GetSharedMemoryAddressOffset(FontType); + // TODO: find what are the 8 bytes before the font + Memory.WriteUInt64(Position + SharedMemoryAddressOffset - 8, 0); + Memory.WriteBytes(Position + SharedMemoryAddressOffset, FontData[FontType]); + } + + public void PropagateNewMapFont(SharedFontType Type) + { + lock (ShMemLock) + { + foreach ((AMemory Memory, long Position, long Size) in ShMemPositions) + { + AMemoryMapInfo MemoryInfo = Memory.Manager.GetMapInfo(Position); + + if (MemoryInfo == null) + { + throw new VmmPageFaultException(Position); + } + + // The memory is read only, we need to changes that to add the new font + AMemoryPerm originalPerms = MemoryInfo.Perm; + Memory.Manager.Reprotect(Position, Size, AMemoryPerm.RW); + MapFont(Type, Memory, Position); + Memory.Manager.Reprotect(Position, Size, originalPerms); + } + } + } + + internal void ShMemMap(object sender, EventArgs e) + { + HSharedMem SharedMem = (HSharedMem)sender; + + lock (ShMemLock) + { + ShMemPositions = SharedMem.GetVirtualPositions(); + + (AMemory Memory, long Position, long Size) = ShMemPositions[ShMemPositions.Length - 1]; + + for (int Type = 0; Type < LoadedFonts.Length; Type++) + { + if (LoadedFonts[(int)Type] == 1) + { + MapFont((SharedFontType)Type, Memory, Position); + } + } + } + } + + internal void ShMemUnmap(object sender, EventArgs e) + { + HSharedMem SharedMem = (HSharedMem)sender; + + lock (ShMemLock) + { + ShMemPositions = SharedMem.GetVirtualPositions(); + } + } + + public void Load(SharedFontType FontType) + { + if (LoadedFonts[(int)FontType] == 0) + { + PropagateNewMapFont(FontType); + } + + LoadedFonts[(int)FontType] = 1; + } + + public uint GetLoadState(SharedFontType FontType) + { + if (LoadedFonts[(int)FontType] != 1) + { + // Some games don't request a load, so we need to load it here. + Load(FontType); + return 0; + } + return LoadedFonts[(int)FontType]; + } + + public uint GetFontSize(SharedFontType FontType) + { + return (uint)FontData[FontType].Length; + } + + public uint GetSharedMemoryAddressOffset(SharedFontType FontType) + { + uint Pos = 0x8; + + for (SharedFontType Type = SharedFontType.JapanUsEurope; Type < FontType; Type++) + { + Pos += GetFontSize(Type); + Pos += 0x8; + } + + return Pos; + } + + public int Count => FontData.Count; + } +} diff --git a/Ryujinx.HLE/OsHle/Services/Pl/SharedFontType.cs b/Ryujinx.HLE/Font/SharedFontType.cs index 97fd95dc..ca8a42b0 100644 --- a/Ryujinx.HLE/OsHle/Services/Pl/SharedFontType.cs +++ b/Ryujinx.HLE/Font/SharedFontType.cs @@ -1,6 +1,6 @@ -namespace Ryujinx.HLE.OsHle.Services.Pl +namespace Ryujinx.HLE.Font { - enum SharedFontType + public enum SharedFontType { JapanUsEurope = 0, SimplifiedChinese = 1, diff --git a/Ryujinx.HLE/Gpu/Engines/NvGpuEngine3d.cs b/Ryujinx.HLE/Gpu/Engines/NvGpuEngine3d.cs index c3e7a77f..0576601f 100644 --- a/Ryujinx.HLE/Gpu/Engines/NvGpuEngine3d.cs +++ b/Ryujinx.HLE/Gpu/Engines/NvGpuEngine3d.cs @@ -25,6 +25,8 @@ namespace Ryujinx.HLE.Gpu.Engines private HashSet<long> FrameBuffers; + private List<long>[] UploadedKeys; + public NvGpuEngine3d(NvGpu Gpu) { this.Gpu = Gpu; @@ -57,6 +59,13 @@ namespace Ryujinx.HLE.Gpu.Engines } FrameBuffers = new HashSet<long>(); + + UploadedKeys = new List<long>[(int)NvGpuBufferType.Count]; + + for (int i = 0; i < UploadedKeys.Length; i++) + { + UploadedKeys[i] = new List<long>(); + } } public void CallMethod(NvGpuVmm Vmm, NvGpuPBEntry PBEntry) @@ -75,35 +84,41 @@ namespace Ryujinx.HLE.Gpu.Engines { LockCaches(); + GalPipelineState State = new GalPipelineState(); + + SetFlip(State); + SetFrontFace(State); + SetCullFace(State); + SetDepth(State); + SetStencil(State); + SetAlphaBlending(State); + SetPrimitiveRestart(State); + SetFrameBuffer(Vmm, 0); long[] Keys = UploadShaders(Vmm); Gpu.Renderer.Shader.BindProgram(); - //Note: Uncomment SetFrontFace SetCullFace when flipping issues are solved - //SetFrontFace(); - //SetCullFace(); - SetDepth(); - SetStencil(); - SetAlphaBlending(); - SetPrimitiveRestart(); + UploadTextures(Vmm, State, Keys); + UploadConstBuffers(Vmm, State); + UploadVertexArrays(Vmm, State); - UploadTextures(Vmm, Keys); - UploadUniforms(Vmm); - UploadVertexArrays(Vmm); + DispatchRender(Vmm, State); UnlockCaches(); } private void LockCaches() { + Gpu.Renderer.Buffer.LockCache(); Gpu.Renderer.Rasterizer.LockCaches(); Gpu.Renderer.Texture.LockCache(); } private void UnlockCaches() { + Gpu.Renderer.Buffer.UnlockCache(); Gpu.Renderer.Rasterizer.UnlockCaches(); Gpu.Renderer.Texture.UnlockCache(); } @@ -116,9 +131,22 @@ namespace Ryujinx.HLE.Gpu.Engines GalClearBufferFlags Flags = (GalClearBufferFlags)(Arg0 & 0x3f); + float Red = ReadRegisterFloat(NvGpuEngine3dReg.ClearNColor + 0); + float Green = ReadRegisterFloat(NvGpuEngine3dReg.ClearNColor + 1); + float Blue = ReadRegisterFloat(NvGpuEngine3dReg.ClearNColor + 2); + float Alpha = ReadRegisterFloat(NvGpuEngine3dReg.ClearNColor + 3); + + float Depth = ReadRegisterFloat(NvGpuEngine3dReg.ClearDepth); + + int Stencil = ReadRegister(NvGpuEngine3dReg.ClearStencil); + SetFrameBuffer(Vmm, FbIndex); - Gpu.Renderer.Rasterizer.ClearBuffers(Flags); + Gpu.Renderer.Rasterizer.ClearBuffers( + Flags, + Red, Green, Blue, Alpha, + Depth, + Stencil); } private void SetFrameBuffer(NvGpuVmm Vmm, int FbIndex) @@ -176,6 +204,8 @@ namespace Ryujinx.HLE.Gpu.Engines long VpAPos = BasePosition + (uint)VpAOffset; long VpBPos = BasePosition + (uint)VpBOffset; + Keys[(int)GalShaderType.Vertex] = VpBPos; + Gpu.Renderer.Shader.Create(Vmm, VpAPos, VpBPos, GalShaderType.Vertex); Gpu.Renderer.Shader.Bind(VpBPos); @@ -207,11 +237,6 @@ namespace Ryujinx.HLE.Gpu.Engines Gpu.Renderer.Shader.Bind(Key); } - float SignX = GetFlipSign(NvGpuEngine3dReg.ViewportNScaleX); - float SignY = GetFlipSign(NvGpuEngine3dReg.ViewportNScaleY); - - Gpu.Renderer.Shader.SetFlip(SignX, SignY); - return Keys; } @@ -230,7 +255,13 @@ namespace Ryujinx.HLE.Gpu.Engines throw new ArgumentOutOfRangeException(nameof(Program)); } - private void SetFrontFace() + private void SetFlip(GalPipelineState State) + { + State.FlipX = GetFlipSign(NvGpuEngine3dReg.ViewportNScaleX); + State.FlipY = GetFlipSign(NvGpuEngine3dReg.ViewportNScaleY); + } + + private void SetFrontFace(GalPipelineState State) { float SignX = GetFlipSign(NvGpuEngine3dReg.ViewportNScaleX); float SignY = GetFlipSign(NvGpuEngine3dReg.ViewportNScaleY); @@ -252,198 +283,82 @@ namespace Ryujinx.HLE.Gpu.Engines } } - Gpu.Renderer.Rasterizer.SetFrontFace(FrontFace); + State.FrontFace = FrontFace; } - private void SetCullFace() + private void SetCullFace(GalPipelineState State) { - bool Enable = (ReadRegister(NvGpuEngine3dReg.CullFaceEnable) & 1) != 0; + State.CullFaceEnabled = (ReadRegister(NvGpuEngine3dReg.CullFaceEnable) & 1) != 0; - if (Enable) - { - Gpu.Renderer.Rasterizer.EnableCullFace(); - } - else + if (State.CullFaceEnabled) { - Gpu.Renderer.Rasterizer.DisableCullFace(); + State.CullFace = (GalCullFace)ReadRegister(NvGpuEngine3dReg.CullFace); } - - if (!Enable) - { - return; - } - - GalCullFace CullFace = (GalCullFace)ReadRegister(NvGpuEngine3dReg.CullFace); - - Gpu.Renderer.Rasterizer.SetCullFace(CullFace); } - private void SetDepth() + private void SetDepth(GalPipelineState State) { - 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(); - } + State.DepthTestEnabled = (ReadRegister(NvGpuEngine3dReg.DepthTestEnable) & 1) != 0; - if (!Enable) + if (State.DepthTestEnabled) { - return; + State.DepthFunc = (GalComparisonOp)ReadRegister(NvGpuEngine3dReg.DepthTestFunction); } - - GalComparisonOp Func = (GalComparisonOp)ReadRegister(NvGpuEngine3dReg.DepthTestFunction); - - Gpu.Renderer.Rasterizer.SetDepthFunction(Func); } - private void SetStencil() + private void SetStencil(GalPipelineState State) { - 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(); + State.StencilTestEnabled = (ReadRegister(NvGpuEngine3dReg.StencilEnable) & 1) != 0; + + if (State.StencilTestEnabled) + { + State.StencilBackFuncFunc = (GalComparisonOp)ReadRegister(NvGpuEngine3dReg.StencilBackFuncFunc); + State.StencilBackFuncRef = ReadRegister(NvGpuEngine3dReg.StencilBackFuncRef); + State.StencilBackFuncMask = (uint)ReadRegister(NvGpuEngine3dReg.StencilBackFuncMask); + State.StencilBackOpFail = (GalStencilOp)ReadRegister(NvGpuEngine3dReg.StencilBackOpFail); + State.StencilBackOpZFail = (GalStencilOp)ReadRegister(NvGpuEngine3dReg.StencilBackOpZFail); + State.StencilBackOpZPass = (GalStencilOp)ReadRegister(NvGpuEngine3dReg.StencilBackOpZPass); + State.StencilBackMask = (uint)ReadRegister(NvGpuEngine3dReg.StencilBackMask); + + State.StencilFrontFuncFunc = (GalComparisonOp)ReadRegister(NvGpuEngine3dReg.StencilFrontFuncFunc); + State.StencilFrontFuncRef = ReadRegister(NvGpuEngine3dReg.StencilFrontFuncRef); + State.StencilFrontFuncMask = (uint)ReadRegister(NvGpuEngine3dReg.StencilFrontFuncMask); + State.StencilFrontOpFail = (GalStencilOp)ReadRegister(NvGpuEngine3dReg.StencilFrontOpFail); + State.StencilFrontOpZFail = (GalStencilOp)ReadRegister(NvGpuEngine3dReg.StencilFrontOpZFail); + State.StencilFrontOpZPass = (GalStencilOp)ReadRegister(NvGpuEngine3dReg.StencilFrontOpZPass); + State.StencilFrontMask = (uint)ReadRegister(NvGpuEngine3dReg.StencilFrontMask); } - - 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() + private void SetAlphaBlending(GalPipelineState State) { //TODO: Support independent blend properly. - bool Enable = (ReadRegister(NvGpuEngine3dReg.IBlendNEnable) & 1) != 0; + State.BlendEnabled = (ReadRegister(NvGpuEngine3dReg.IBlendNEnable) & 1) != 0; - if (Enable) - { - Gpu.Renderer.Blend.Enable(); - } - else + if (State.BlendEnabled) { - Gpu.Renderer.Blend.Disable(); - } + State.BlendSeparateAlpha = (ReadRegister(NvGpuEngine3dReg.IBlendNSeparateAlpha) & 1) != 0; - if (!Enable) - { - //If blend is not enabled, then the other values have no effect. - //Note that if it is disabled, the register may contain invalid values. - return; - } - - bool BlendSeparateAlpha = (ReadRegister(NvGpuEngine3dReg.IBlendNSeparateAlpha) & 1) != 0; - - GalBlendEquation EquationRgb = (GalBlendEquation)ReadRegister(NvGpuEngine3dReg.IBlendNEquationRgb); - - GalBlendFactor FuncSrcRgb = (GalBlendFactor)ReadRegister(NvGpuEngine3dReg.IBlendNFuncSrcRgb); - GalBlendFactor FuncDstRgb = (GalBlendFactor)ReadRegister(NvGpuEngine3dReg.IBlendNFuncDstRgb); - - if (BlendSeparateAlpha) - { - GalBlendEquation EquationAlpha = (GalBlendEquation)ReadRegister(NvGpuEngine3dReg.IBlendNEquationAlpha); - - GalBlendFactor FuncSrcAlpha = (GalBlendFactor)ReadRegister(NvGpuEngine3dReg.IBlendNFuncSrcAlpha); - GalBlendFactor FuncDstAlpha = (GalBlendFactor)ReadRegister(NvGpuEngine3dReg.IBlendNFuncDstAlpha); - - Gpu.Renderer.Blend.SetSeparate( - EquationRgb, - EquationAlpha, - FuncSrcRgb, - FuncDstRgb, - FuncSrcAlpha, - FuncDstAlpha); - } - else - { - Gpu.Renderer.Blend.Set(EquationRgb, FuncSrcRgb, FuncDstRgb); + State.BlendEquationRgb = (GalBlendEquation)ReadRegister(NvGpuEngine3dReg.IBlendNEquationRgb); + State.BlendFuncSrcRgb = (GalBlendFactor)ReadRegister(NvGpuEngine3dReg.IBlendNFuncSrcRgb); + State.BlendFuncDstRgb = (GalBlendFactor)ReadRegister(NvGpuEngine3dReg.IBlendNFuncDstRgb); + State.BlendEquationAlpha = (GalBlendEquation)ReadRegister(NvGpuEngine3dReg.IBlendNEquationAlpha); + State.BlendFuncSrcAlpha = (GalBlendFactor)ReadRegister(NvGpuEngine3dReg.IBlendNFuncSrcAlpha); + State.BlendFuncDstAlpha = (GalBlendFactor)ReadRegister(NvGpuEngine3dReg.IBlendNFuncDstAlpha); } } - private void SetPrimitiveRestart() + private void SetPrimitiveRestart(GalPipelineState State) { - bool Enable = (ReadRegister(NvGpuEngine3dReg.PrimRestartEnable) & 1) != 0; - - if (Enable) - { - Gpu.Renderer.Rasterizer.EnablePrimitiveRestart(); - } - else - { - Gpu.Renderer.Rasterizer.DisablePrimitiveRestart(); - } + State.PrimitiveRestartEnabled = (ReadRegister(NvGpuEngine3dReg.PrimRestartEnable) & 1) != 0; - if (!Enable) + if (State.PrimitiveRestartEnabled) { - return; + State.PrimitiveRestartIndex = (uint)ReadRegister(NvGpuEngine3dReg.PrimRestartIndex); } - - uint Index = (uint)ReadRegister(NvGpuEngine3dReg.PrimRestartIndex); - - Gpu.Renderer.Rasterizer.SetPrimitiveRestartIndex(Index); } - private void UploadTextures(NvGpuVmm Vmm, long[] Keys) + private void UploadTextures(NvGpuVmm Vmm, GalPipelineState State, long[] Keys) { long BaseShPosition = MakeInt64From2xInt32(NvGpuEngine3dReg.ShaderAddress); @@ -516,7 +431,7 @@ namespace Ryujinx.HLE.Gpu.Engines if (Gpu.Renderer.Texture.TryGetCachedTexture(Key, Size, out GalTexture Texture)) { - if (NewTexture.Equals(Texture) && !Vmm.IsRegionModified(Key, Size, NvGpuBufferType.Texture)) + if (NewTexture.Equals(Texture) && !QueryKeyUpload(Vmm, Key, Size, NvGpuBufferType.Texture)) { Gpu.Renderer.Texture.Bind(Key, TexIndex); @@ -537,45 +452,35 @@ namespace Ryujinx.HLE.Gpu.Engines Gpu.Renderer.Texture.SetSampler(Sampler); } - private void UploadUniforms(NvGpuVmm Vmm) + private void UploadConstBuffers(NvGpuVmm Vmm, GalPipelineState State) { - long BasePosition = MakeInt64From2xInt32(NvGpuEngine3dReg.ShaderAddress); - - for (int Index = 0; Index < 5; Index++) + for (int Stage = 0; Stage < State.ConstBufferKeys.Length; Stage++) { - int Control = ReadRegister(NvGpuEngine3dReg.ShaderNControl + (Index + 1) * 0x10); - int Offset = ReadRegister(NvGpuEngine3dReg.ShaderNOffset + (Index + 1) * 0x10); - - //Note: Vertex Program (B) is always enabled. - bool Enable = (Control & 1) != 0 || Index == 0; - - if (!Enable) + for (int Index = 0; Index < State.ConstBufferKeys[Stage].Length; Index++) { - continue; - } + ConstBuffer Cb = ConstBuffers[Stage][Index]; - for (int Cbuf = 0; Cbuf < ConstBuffers[Index].Length; Cbuf++) - { - ConstBuffer Cb = ConstBuffers[Index][Cbuf]; + long Key = Cb.Position; - if (Cb.Enabled) + if (Cb.Enabled && QueryKeyUpload(Vmm, Key, Cb.Size, NvGpuBufferType.ConstBuffer)) { - IntPtr DataAddress = Vmm.GetHostAddress(Cb.Position, Cb.Size); + IntPtr Source = Vmm.GetHostAddress(Key, Cb.Size); - Gpu.Renderer.Shader.SetConstBuffer(BasePosition + (uint)Offset, Cbuf, Cb.Size, DataAddress); + Gpu.Renderer.Buffer.SetData(Key, Cb.Size, Source); } + + State.ConstBufferKeys[Stage][Index] = Key; } } } - private void UploadVertexArrays(NvGpuVmm Vmm) + private void UploadVertexArrays(NvGpuVmm Vmm, GalPipelineState State) { long IndexPosition = MakeInt64From2xInt32(NvGpuEngine3dReg.IndexArrayAddress); long IboKey = Vmm.GetPhysicalAddress(IndexPosition); int IndexEntryFmt = ReadRegister(NvGpuEngine3dReg.IndexArrayFormat); - int IndexFirst = ReadRegister(NvGpuEngine3dReg.IndexBatchFirst); int IndexCount = ReadRegister(NvGpuEngine3dReg.IndexBatchCount); GalIndexFormat IndexFormat = (GalIndexFormat)IndexEntryFmt; @@ -593,7 +498,7 @@ namespace Ryujinx.HLE.Gpu.Engines bool IboCached = Gpu.Renderer.Rasterizer.IsIboCached(IboKey, (uint)IbSize); - if (!IboCached || Vmm.IsRegionModified(IboKey, (uint)IbSize, NvGpuBufferType.Index)) + if (!IboCached || QueryKeyUpload(Vmm, IboKey, (uint)IbSize, NvGpuBufferType.Index)) { IntPtr DataAddress = Vmm.GetHostAddress(IndexPosition, IbSize); @@ -625,10 +530,7 @@ namespace Ryujinx.HLE.Gpu.Engines ((Packed >> 31) & 0x1) != 0)); } - int VertexFirst = ReadRegister(NvGpuEngine3dReg.VertexArrayFirst); - int VertexCount = ReadRegister(NvGpuEngine3dReg.VertexArrayCount); - - int PrimCtrl = ReadRegister(NvGpuEngine3dReg.VertexBeginGl); + State.VertexBindings = new GalVertexBinding[32]; for (int Index = 0; Index < 32; Index++) { @@ -641,14 +543,14 @@ namespace Ryujinx.HLE.Gpu.Engines bool Enable = (Control & 0x1000) != 0; - long VertexPosition = MakeInt64From2xInt32(NvGpuEngine3dReg.VertexArrayNAddress + Index * 4); - long VertexEndPos = MakeInt64From2xInt32(NvGpuEngine3dReg.VertexArrayNEndAddr + Index * 2); - if (!Enable) { continue; } + long VertexPosition = MakeInt64From2xInt32(NvGpuEngine3dReg.VertexArrayNAddress + Index * 4); + long VertexEndPos = MakeInt64From2xInt32(NvGpuEngine3dReg.VertexArrayNEndAddr + Index * 2); + long VboKey = Vmm.GetPhysicalAddress(VertexPosition); int Stride = Control & 0xfff; @@ -657,26 +559,46 @@ namespace Ryujinx.HLE.Gpu.Engines bool VboCached = Gpu.Renderer.Rasterizer.IsVboCached(VboKey, VbSize); - if (!VboCached || Vmm.IsRegionModified(VboKey, VbSize, NvGpuBufferType.Vertex)) + if (!VboCached || QueryKeyUpload(Vmm, VboKey, VbSize, NvGpuBufferType.Vertex)) { IntPtr DataAddress = Vmm.GetHostAddress(VertexPosition, VbSize); Gpu.Renderer.Rasterizer.CreateVbo(VboKey, (int)VbSize, DataAddress); } - Gpu.Renderer.Rasterizer.SetVertexArray(Stride, VboKey, Attribs[Index].ToArray()); + State.VertexBindings[Index].Enabled = true; + State.VertexBindings[Index].Stride = Stride; + State.VertexBindings[Index].VboKey = VboKey; + State.VertexBindings[Index].Attribs = Attribs[Index].ToArray(); } + } + + private void DispatchRender(NvGpuVmm Vmm, GalPipelineState State) + { + int IndexCount = ReadRegister(NvGpuEngine3dReg.IndexBatchCount); + int PrimCtrl = ReadRegister(NvGpuEngine3dReg.VertexBeginGl); GalPrimitiveType PrimType = (GalPrimitiveType)(PrimCtrl & 0xffff); + Gpu.Renderer.Pipeline.Bind(State); + if (IndexCount != 0) { - int VertexBase = ReadRegister(NvGpuEngine3dReg.VertexArrayElemBase); + int IndexEntryFmt = ReadRegister(NvGpuEngine3dReg.IndexArrayFormat); + int IndexFirst = ReadRegister(NvGpuEngine3dReg.IndexBatchFirst); + int VertexBase = ReadRegister(NvGpuEngine3dReg.VertexArrayElemBase); + + long IndexPosition = MakeInt64From2xInt32(NvGpuEngine3dReg.IndexArrayAddress); + + long IboKey = Vmm.GetPhysicalAddress(IndexPosition); Gpu.Renderer.Rasterizer.DrawElements(IboKey, IndexFirst, VertexBase, PrimType); } else { + int VertexFirst = ReadRegister(NvGpuEngine3dReg.VertexArrayFirst); + int VertexCount = ReadRegister(NvGpuEngine3dReg.VertexArrayCount); + Gpu.Renderer.Rasterizer.DrawArrays(VertexFirst, VertexCount, PrimType); } } @@ -692,6 +614,11 @@ namespace Ryujinx.HLE.Gpu.Engines if (Mode == 0) { + foreach (List<long> Uploaded in UploadedKeys) + { + Uploaded.Clear(); + } + //Write mode. Vmm.WriteInt32(Position, Seq); } @@ -727,10 +654,21 @@ namespace Ryujinx.HLE.Gpu.Engines long Position = MakeInt64From2xInt32(NvGpuEngine3dReg.ConstBufferAddress); - ConstBuffers[Stage][Index].Position = Position; - ConstBuffers[Stage][Index].Enabled = Enabled; + int Size = ReadRegister(NvGpuEngine3dReg.ConstBufferSize); - ConstBuffers[Stage][Index].Size = ReadRegister(NvGpuEngine3dReg.ConstBufferSize); + if (!Gpu.Renderer.Buffer.IsCached(Position, Size)) + { + Gpu.Renderer.Buffer.Create(Position, Size); + } + + ConstBuffer Cb = ConstBuffers[Stage][Index]; + + if (Cb.Position != Position || Cb.Enabled != Enabled || Cb.Size != Size) + { + ConstBuffers[Stage][Index].Position = Position; + ConstBuffers[Stage][Index].Enabled = Enabled; + ConstBuffers[Stage][Index].Size = Size; + } } private float GetFlipSign(NvGpuEngine3dReg Reg) @@ -774,5 +712,19 @@ namespace Ryujinx.HLE.Gpu.Engines { return FrameBuffers.Contains(Position); } + + private bool QueryKeyUpload(NvGpuVmm Vmm, long Key, long Size, NvGpuBufferType Type) + { + List<long> Uploaded = UploadedKeys[(int)Type]; + + if (Uploaded.Contains(Key)) + { + return false; + } + + Uploaded.Add(Key); + + return Vmm.IsRegionModified(Key, Size, Type); + } } }
\ No newline at end of file diff --git a/Ryujinx.HLE/Gpu/Engines/NvGpuEngine3dReg.cs b/Ryujinx.HLE/Gpu/Engines/NvGpuEngine3dReg.cs index e7dabe44..39a5ee8c 100644 --- a/Ryujinx.HLE/Gpu/Engines/NvGpuEngine3dReg.cs +++ b/Ryujinx.HLE/Gpu/Engines/NvGpuEngine3dReg.cs @@ -16,6 +16,7 @@ namespace Ryujinx.HLE.Gpu.Engines ViewportNVert = 0x301, VertexArrayFirst = 0x35d, VertexArrayCount = 0x35e, + ClearNColor = 0x360, ClearDepth = 0x364, ClearStencil = 0x368, StencilBackFuncRef = 0x3d5, diff --git a/Ryujinx.HLE/Gpu/Memory/NvGpuBufferType.cs b/Ryujinx.HLE/Gpu/Memory/NvGpuBufferType.cs index 469cd6cd..a6c03f42 100644 --- a/Ryujinx.HLE/Gpu/Memory/NvGpuBufferType.cs +++ b/Ryujinx.HLE/Gpu/Memory/NvGpuBufferType.cs @@ -5,6 +5,7 @@ namespace Ryujinx.HLE.Gpu.Memory Index, Vertex, Texture, + ConstBuffer, Count } }
\ No newline at end of file diff --git a/Ryujinx.HLE/Gpu/Memory/NvGpuVmmCache.cs b/Ryujinx.HLE/Gpu/Memory/NvGpuVmmCache.cs index ac9bd850..3c256044 100644 --- a/Ryujinx.HLE/Gpu/Memory/NvGpuVmmCache.cs +++ b/Ryujinx.HLE/Gpu/Memory/NvGpuVmmCache.cs @@ -19,12 +19,14 @@ namespace Ryujinx.HLE.Gpu.Memory public Range(long Start, long End) { this.Start = Start; - this.End = End; + this.End = End; } } private List<Range>[] Regions; + private HashSet<long> ResidencyKeys; + public LinkedListNode<long> Node { get; set; } public int Timestamp { get; private set; } @@ -37,6 +39,27 @@ namespace Ryujinx.HLE.Gpu.Memory { Regions[Index] = new List<Range>(); } + + ResidencyKeys = new HashSet<long>(); + } + + public void AddResidency(long Key) + { + ResidencyKeys.Add(Key); + } + + public void RemoveResidency(HashSet<long>[] Residency, long PageSize) + { + for (int i = 0; i < (int)NvGpuBufferType.Count; i++) + { + foreach (Range Region in Regions[i]) + { + foreach (long Key in ResidencyKeys) + { + Residency[Region.Start / PageSize].Remove(Key); + } + } + } } public bool AddRange(long Start, long End, NvGpuBufferType BufferType) @@ -89,6 +112,10 @@ namespace Ryujinx.HLE.Gpu.Memory private LinkedList<long> SortedCache; + private HashSet<long>[] Residency; + + private long ResidencyPageSize; + private int CpCount; public NvGpuVmmCache() @@ -100,7 +127,7 @@ namespace Ryujinx.HLE.Gpu.Memory public bool IsRegionModified(AMemory Memory, NvGpuBufferType BufferType, long PA, long Size) { - bool[] Modified = Memory.IsRegionModified(PA, Size); + (bool[] Modified, long ModifiedCount) = Memory.IsRegionModified(PA, Size); if (Modified == null) { @@ -111,8 +138,19 @@ namespace Ryujinx.HLE.Gpu.Memory long PageSize = Memory.GetHostPageSize(); + EnsureResidencyInitialized(PageSize); + + bool HasResidents = AddResidency(PA, Size); + + if (!HasResidents && ModifiedCount == 0) + { + return false; + } + long Mask = PageSize - 1; + long ResidencyKey = PA; + long PAEnd = PA + Size; bool RegMod = false; @@ -147,6 +185,8 @@ namespace Ryujinx.HLE.Gpu.Memory Cache[Key] = Cp; } + Cp.AddResidency(ResidencyKey); + Cp.Node = SortedCache.AddLast(Key); RegMod |= Cp.AddRange(PA, PAPgEnd, BufferType); @@ -159,6 +199,53 @@ namespace Ryujinx.HLE.Gpu.Memory return RegMod; } + private bool AddResidency(long PA, long Size) + { + long PageSize = ResidencyPageSize; + + long Mask = PageSize - 1; + + long Key = PA; + + bool ResidentFound = false; + + for (long Cursor = PA & ~Mask; Cursor < ((PA + Size + PageSize - 1) & ~Mask); Cursor += PageSize) + { + long PageIndex = Cursor / PageSize; + + Residency[PageIndex].Add(Key); + + if (Residency[PageIndex].Count > 1) + { + ResidentFound = true; + } + } + + return ResidentFound; + } + + private void EnsureResidencyInitialized(long PageSize) + { + if (Residency == null) + { + Residency = new HashSet<long>[AMemoryMgr.RamSize / PageSize]; + + for (int i = 0; i < Residency.Length; i++) + { + Residency[i] = new HashSet<long>(); + } + + ResidencyPageSize = PageSize; + } + else + { + if (ResidencyPageSize != PageSize) + { + throw new InvalidOperationException("Tried to change residency page size"); + } + } + } + private void ClearCachedPagesIfNeeded() { if (CpCount <= MaxCpCount) @@ -179,6 +266,8 @@ namespace Ryujinx.HLE.Gpu.Memory CachedPage Cp = Cache[Key]; + Cp.RemoveResidency(Residency, ResidencyPageSize); + Cache.Remove(Key); CpCount -= Cp.GetTotalCount(); diff --git a/Ryujinx.HLE/Gpu/Texture/TextureHelper.cs b/Ryujinx.HLE/Gpu/Texture/TextureHelper.cs index ecf2b6bf..1d621c92 100644 --- a/Ryujinx.HLE/Gpu/Texture/TextureHelper.cs +++ b/Ryujinx.HLE/Gpu/Texture/TextureHelper.cs @@ -41,6 +41,7 @@ namespace Ryujinx.HLE.Gpu.Texture return Texture.Width * Texture.Height * 8; case GalTextureFormat.A8B8G8R8: + case GalTextureFormat.A2B10G10R10: case GalTextureFormat.R32: case GalTextureFormat.ZF32: case GalTextureFormat.BF10GF11RF11: diff --git a/Ryujinx.HLE/Gpu/Texture/TextureReader.cs b/Ryujinx.HLE/Gpu/Texture/TextureReader.cs index 350ab825..f8cd6765 100644 --- a/Ryujinx.HLE/Gpu/Texture/TextureReader.cs +++ b/Ryujinx.HLE/Gpu/Texture/TextureReader.cs @@ -13,6 +13,7 @@ namespace Ryujinx.HLE.Gpu.Texture case GalTextureFormat.R32G32B32A32: return Read16Bpp (Memory, Texture); case GalTextureFormat.R16G16B16A16: return Read8Bpp (Memory, Texture); case GalTextureFormat.A8B8G8R8: return Read4Bpp (Memory, Texture); + case GalTextureFormat.A2B10G10R10: return Read4Bpp (Memory, Texture); case GalTextureFormat.R32: return Read4Bpp (Memory, Texture); case GalTextureFormat.BF10GF11RF11: return Read4Bpp (Memory, Texture); case GalTextureFormat.Z24S8: return Read4Bpp (Memory, Texture); diff --git a/Ryujinx.HLE/Hid/Hid.cs b/Ryujinx.HLE/Hid/Hid.cs index 2f007f1f..43c040bb 100644 --- a/Ryujinx.HLE/Hid/Hid.cs +++ b/Ryujinx.HLE/Hid/Hid.cs @@ -67,7 +67,7 @@ namespace Ryujinx.HLE.Input private object ShMemLock; - private (AMemory, long)[] ShMemPositions; + private (AMemory, long, long)[] ShMemPositions; public Hid(Logger Log) { @@ -75,7 +75,7 @@ namespace Ryujinx.HLE.Input ShMemLock = new object(); - ShMemPositions = new (AMemory, long)[0]; + ShMemPositions = new (AMemory, long, long)[0]; } internal void ShMemMap(object sender, EventArgs e) @@ -86,7 +86,7 @@ namespace Ryujinx.HLE.Input { ShMemPositions = SharedMem.GetVirtualPositions(); - (AMemory Memory, long Position) = ShMemPositions[ShMemPositions.Length - 1]; + (AMemory Memory, long Position, long Size) = ShMemPositions[ShMemPositions.Length - 1]; for (long Offset = 0; Offset < Horizon.HidSize; Offset += 8) { @@ -167,7 +167,7 @@ namespace Ryujinx.HLE.Input { lock (ShMemLock) { - foreach ((AMemory Memory, long Position) in ShMemPositions) + foreach ((AMemory Memory, long Position, long Size) in ShMemPositions) { long ControllerOffset = Position + HidControllersOffset; @@ -218,7 +218,7 @@ namespace Ryujinx.HLE.Input { lock (ShMemLock) { - foreach ((AMemory Memory, long Position) in ShMemPositions) + foreach ((AMemory Memory, long Position, long Size) in ShMemPositions) { long TouchScreenOffset = Position + HidTouchScreenOffset; diff --git a/Ryujinx.HLE/Logging/LogClass.cs b/Ryujinx.HLE/Logging/LogClass.cs index c377ace6..95cae7e0 100644 --- a/Ryujinx.HLE/Logging/LogClass.cs +++ b/Ryujinx.HLE/Logging/LogClass.cs @@ -4,6 +4,7 @@ namespace Ryujinx.HLE.Logging { Audio, Cpu, + Font, Gpu, Hid, Kernel, diff --git a/Ryujinx.HLE/OsHle/Handles/HSharedMem.cs b/Ryujinx.HLE/OsHle/Handles/HSharedMem.cs index 6426e585..cd3d8223 100644 --- a/Ryujinx.HLE/OsHle/Handles/HSharedMem.cs +++ b/Ryujinx.HLE/OsHle/Handles/HSharedMem.cs @@ -6,37 +6,37 @@ namespace Ryujinx.HLE.OsHle.Handles { class HSharedMem { - private List<(AMemory, long)> Positions; + private List<(AMemory, long, long)> Positions; public EventHandler<EventArgs> MemoryMapped; public EventHandler<EventArgs> MemoryUnmapped; public HSharedMem() { - Positions = new List<(AMemory, long)>(); + Positions = new List<(AMemory, long, long)>(); } - public void AddVirtualPosition(AMemory Memory, long Position) + public void AddVirtualPosition(AMemory Memory, long Position, long Size) { lock (Positions) { - Positions.Add((Memory, Position)); + Positions.Add((Memory, Position, Size)); MemoryMapped?.Invoke(this, EventArgs.Empty); } } - public void RemoveVirtualPosition(AMemory Memory, long Position) + public void RemoveVirtualPosition(AMemory Memory, long Position, long Size) { lock (Positions) { - Positions.Remove((Memory, Position)); + Positions.Remove((Memory, Position, Size)); MemoryUnmapped?.Invoke(this, EventArgs.Empty); } } - public (AMemory, long)[] GetVirtualPositions() + public (AMemory, long, long)[] GetVirtualPositions() { return Positions.ToArray(); } diff --git a/Ryujinx.HLE/OsHle/Horizon.cs b/Ryujinx.HLE/OsHle/Horizon.cs index 70ae24be..f420dc0d 100644 --- a/Ryujinx.HLE/OsHle/Horizon.cs +++ b/Ryujinx.HLE/OsHle/Horizon.cs @@ -1,4 +1,5 @@ using Ryujinx.HLE.Loaders.Executables; +using Ryujinx.HLE.Loaders.Npdm; using Ryujinx.HLE.Logging; using Ryujinx.HLE.OsHle.Handles; using System; @@ -76,6 +77,25 @@ namespace Ryujinx.HLE.OsHle } } + void LoadNpdm(string FileName) + { + string File = Directory.GetFiles(ExeFsDir, FileName)[0]; + + Ns.Log.PrintInfo(LogClass.Loader, "Loading Title Metadata..."); + + using (FileStream Input = new FileStream(File, FileMode.Open)) + { + MainProcess.Metadata = new Npdm(Input); + } + } + + LoadNpdm("*.npdm"); + + if (!MainProcess.Metadata.Is64Bits) + { + throw new NotImplementedException("32-bit titles are unsupported!"); + } + LoadNso("rtld"); MainProcess.SetEmptyArgs(); diff --git a/Ryujinx.HLE/OsHle/IdDictionary.cs b/Ryujinx.HLE/OsHle/IdDictionary.cs index 7a93f634..dcb8d0f7 100644 --- a/Ryujinx.HLE/OsHle/IdDictionary.cs +++ b/Ryujinx.HLE/OsHle/IdDictionary.cs @@ -8,8 +8,6 @@ namespace Ryujinx.HLE.OsHle { private ConcurrentDictionary<int, object> Objs; - private int FreeIdHint = 1; - public IdDictionary() { Objs = new ConcurrentDictionary<int, object>(); @@ -22,16 +20,6 @@ namespace Ryujinx.HLE.OsHle public int Add(object Data) { - if (Objs.TryAdd(FreeIdHint, Data)) - { - return FreeIdHint++; - } - - return AddSlow(Data); - } - - private int AddSlow(object Data) - { for (int Id = 1; Id < int.MaxValue; Id++) { if (Objs.TryAdd(Id, Data)) @@ -67,8 +55,6 @@ namespace Ryujinx.HLE.OsHle { if (Objs.TryRemove(Id, out object Obj)) { - FreeIdHint = Id; - return Obj; } diff --git a/Ryujinx.HLE/OsHle/Ipc/IpcHandleDesc.cs b/Ryujinx.HLE/OsHle/Ipc/IpcHandleDesc.cs index 953cac76..609cc6e0 100644 --- a/Ryujinx.HLE/OsHle/Ipc/IpcHandleDesc.cs +++ b/Ryujinx.HLE/OsHle/Ipc/IpcHandleDesc.cs @@ -47,13 +47,15 @@ namespace Ryujinx.HLE.OsHle.Ipc HasPId = true; } - public static IpcHandleDesc MakeCopy(int Handle) => new IpcHandleDesc( - new int[] { Handle }, - new int[0]); + public static IpcHandleDesc MakeCopy(params int[] Handles) + { + return new IpcHandleDesc(Handles, new int[0]); + } - public static IpcHandleDesc MakeMove(int Handle) => new IpcHandleDesc( - new int[0], - new int[] { Handle }); + public static IpcHandleDesc MakeMove(params int[] Handles) + { + return new IpcHandleDesc(new int[0], Handles); + } public byte[] GetBytes() { diff --git a/Ryujinx.HLE/OsHle/Ipc/IpcHandler.cs b/Ryujinx.HLE/OsHle/Ipc/IpcHandler.cs index 9b46cf4b..cdb844cf 100644 --- a/Ryujinx.HLE/OsHle/Ipc/IpcHandler.cs +++ b/Ryujinx.HLE/OsHle/Ipc/IpcHandler.cs @@ -21,7 +21,8 @@ namespace Ryujinx.HLE.OsHle.Ipc { BinaryReader ReqReader = new BinaryReader(Raw); - if (Request.Type == IpcMessageType.Request) + if (Request.Type == IpcMessageType.Request || + Request.Type == IpcMessageType.RequestWithContext) { Response.Type = IpcMessageType.Response; @@ -44,7 +45,8 @@ namespace Ryujinx.HLE.OsHle.Ipc Response.RawData = ResMS.ToArray(); } } - else if (Request.Type == IpcMessageType.Control) + else if (Request.Type == IpcMessageType.Control || + Request.Type == IpcMessageType.ControlWithContext) { long Magic = ReqReader.ReadInt64(); long CmdId = ReqReader.ReadInt64(); diff --git a/Ryujinx.HLE/OsHle/Ipc/IpcMessage.cs b/Ryujinx.HLE/OsHle/Ipc/IpcMessage.cs index 4e648aec..0a64a164 100644 --- a/Ryujinx.HLE/OsHle/Ipc/IpcMessage.cs +++ b/Ryujinx.HLE/OsHle/Ipc/IpcMessage.cs @@ -15,7 +15,7 @@ namespace Ryujinx.HLE.OsHle.Ipc public List<IpcBuffDesc> ExchangeBuff { get; private set; } public List<IpcRecvListBuffDesc> RecvListBuff { get; private set; } - public List<int> ResponseObjIds { get; private set; } + public List<int> ObjectIds { get; private set; } public byte[] RawData { get; set; } @@ -27,7 +27,7 @@ namespace Ryujinx.HLE.OsHle.Ipc ExchangeBuff = new List<IpcBuffDesc>(); RecvListBuff = new List<IpcRecvListBuffDesc>(); - ResponseObjIds = new List<int>(); + ObjectIds = new List<int>(); } public IpcMessage(byte[] Data, long CmdPtr) : this() diff --git a/Ryujinx.HLE/OsHle/Ipc/IpcMessageType.cs b/Ryujinx.HLE/OsHle/Ipc/IpcMessageType.cs index f596fea4..3db6844e 100644 --- a/Ryujinx.HLE/OsHle/Ipc/IpcMessageType.cs +++ b/Ryujinx.HLE/OsHle/Ipc/IpcMessageType.cs @@ -2,9 +2,11 @@ namespace Ryujinx.HLE.OsHle.Ipc { enum IpcMessageType { - Response = 0, - CloseSession = 2, - Request = 4, - Control = 5 + Response = 0, + CloseSession = 2, + Request = 4, + Control = 5, + RequestWithContext = 6, + ControlWithContext = 7 } }
\ No newline at end of file diff --git a/Ryujinx.HLE/OsHle/Kernel/SvcHandler.cs b/Ryujinx.HLE/OsHle/Kernel/SvcHandler.cs index e816c44e..6f7bc42f 100644 --- a/Ryujinx.HLE/OsHle/Kernel/SvcHandler.cs +++ b/Ryujinx.HLE/OsHle/Kernel/SvcHandler.cs @@ -22,7 +22,7 @@ namespace Ryujinx.HLE.OsHle.Kernel private ConcurrentDictionary<KThread, AutoResetEvent> SyncWaits; - private HashSet<(HSharedMem, long)> MappedSharedMems; + private HashSet<(HSharedMem, long, long)> MappedSharedMems; private ulong CurrentHeapSize; @@ -83,7 +83,7 @@ namespace Ryujinx.HLE.OsHle.Kernel SyncWaits = new ConcurrentDictionary<KThread, AutoResetEvent>(); - MappedSharedMems = new HashSet<(HSharedMem, long)>(); + MappedSharedMems = new HashSet<(HSharedMem, long, long)>(); } static SvcHandler() @@ -138,9 +138,9 @@ namespace Ryujinx.HLE.OsHle.Kernel { lock (MappedSharedMems) { - foreach ((HSharedMem SharedMem, long Position) in MappedSharedMems) + foreach ((HSharedMem SharedMem, long Position, long Size) in MappedSharedMems) { - SharedMem.RemoveVirtualPosition(Memory, Position); + SharedMem.RemoveVirtualPosition(Memory, Position, Size); } MappedSharedMems.Clear(); diff --git a/Ryujinx.HLE/OsHle/Kernel/SvcMemory.cs b/Ryujinx.HLE/OsHle/Kernel/SvcMemory.cs index bb73f1ea..f10cad7a 100644 --- a/Ryujinx.HLE/OsHle/Kernel/SvcMemory.cs +++ b/Ryujinx.HLE/OsHle/Kernel/SvcMemory.cs @@ -174,15 +174,15 @@ namespace Ryujinx.HLE.OsHle.Kernel AMemoryHelper.FillWithZeros(Memory, Src, (int)Size); + SharedMem.AddVirtualPosition(Memory, Src, Size); + Memory.Manager.Reprotect(Src, Size, (AMemoryPerm)Perm); lock (MappedSharedMems) { - MappedSharedMems.Add((SharedMem, Src)); + MappedSharedMems.Add((SharedMem, Src, Size)); } - SharedMem.AddVirtualPosition(Memory, Src); - ThreadState.X0 = 0; } @@ -210,11 +210,11 @@ namespace Ryujinx.HLE.OsHle.Kernel { Memory.Manager.Unmap(Src, Size, (int)MemoryType.SharedMemory); - SharedMem.RemoveVirtualPosition(Memory, Src); + SharedMem.RemoveVirtualPosition(Memory, Src, Size); lock (MappedSharedMems) { - MappedSharedMems.Remove((SharedMem, Src)); + MappedSharedMems.Remove((SharedMem, Src, Size)); } ThreadState.X0 = 0; diff --git a/Ryujinx.HLE/OsHle/Kernel/SvcSystem.cs b/Ryujinx.HLE/OsHle/Kernel/SvcSystem.cs index 08305522..a968a1db 100644 --- a/Ryujinx.HLE/OsHle/Kernel/SvcSystem.cs +++ b/Ryujinx.HLE/OsHle/Kernel/SvcSystem.cs @@ -242,7 +242,6 @@ namespace Ryujinx.HLE.OsHle.Kernel Process.Scheduler.Suspend(CurrThread); IpcMessage Cmd = new IpcMessage(CmdData, CmdPtr); - long Result = IpcHandler.IpcCall(Ns, Process, Memory, Session, Cmd, CmdPtr); Thread.Yield(); diff --git a/Ryujinx.HLE/OsHle/Process.cs b/Ryujinx.HLE/OsHle/Process.cs index c7606dc9..ac5ed456 100644 --- a/Ryujinx.HLE/OsHle/Process.cs +++ b/Ryujinx.HLE/OsHle/Process.cs @@ -4,6 +4,7 @@ using ChocolArm64.Memory; using ChocolArm64.State; using Ryujinx.HLE.Loaders; using Ryujinx.HLE.Loaders.Executables; +using Ryujinx.HLE.Loaders.Npdm; using Ryujinx.HLE.Logging; using Ryujinx.HLE.OsHle.Diagnostics; using Ryujinx.HLE.OsHle.Exceptions; @@ -48,6 +49,8 @@ namespace Ryujinx.HLE.OsHle public AppletStateMgr AppletState { get; private set; } + public Npdm Metadata { get; set; } + private SvcHandler SvcHandler; private ConcurrentDictionary<int, AThread> TlsSlots; diff --git a/Ryujinx.HLE/OsHle/Services/Am/ICommonStateGetter.cs b/Ryujinx.HLE/OsHle/Services/Am/ICommonStateGetter.cs index e1a13c59..0a29b8f3 100644 --- a/Ryujinx.HLE/OsHle/Services/Am/ICommonStateGetter.cs +++ b/Ryujinx.HLE/OsHle/Services/Am/ICommonStateGetter.cs @@ -3,6 +3,7 @@ using Ryujinx.HLE.OsHle.Handles; using Ryujinx.HLE.OsHle.Ipc; using System.Collections.Generic; +using static Ryujinx.HLE.OsHle.SystemStateMgr; using static Ryujinx.HLE.OsHle.ErrorCode; namespace Ryujinx.HLE.OsHle.Services.Am @@ -57,14 +58,18 @@ namespace Ryujinx.HLE.OsHle.Services.Am public long GetOperationMode(ServiceCtx Context) { - Context.ResponseData.Write((byte)OperationMode.Handheld); + OperationMode Mode = DockedMode ? OperationMode.Docked : OperationMode.Handheld; + + Context.ResponseData.Write((byte)Mode); return 0; } public long GetPerformanceMode(ServiceCtx Context) { - Context.ResponseData.Write((byte)Apm.PerformanceMode.Handheld); + Apm.PerformanceMode Mode = DockedMode ? Apm.PerformanceMode.Docked : Apm.PerformanceMode.Handheld; + + Context.ResponseData.Write((byte)Mode); return 0; } @@ -104,4 +109,4 @@ namespace Ryujinx.HLE.OsHle.Services.Am return 0; } } -}
\ No newline at end of file +} diff --git a/Ryujinx.HLE/OsHle/Services/Am/ISelfController.cs b/Ryujinx.HLE/OsHle/Services/Am/ISelfController.cs index 10d69b9b..9b3dded2 100644 --- a/Ryujinx.HLE/OsHle/Services/Am/ISelfController.cs +++ b/Ryujinx.HLE/OsHle/Services/Am/ISelfController.cs @@ -27,6 +27,7 @@ namespace Ryujinx.HLE.OsHle.Services.Am { 13, SetFocusHandlingMode }, { 14, SetRestartMessageEnabled }, { 16, SetOutOfFocusSuspendingEnabled }, + { 19, SetScreenShotImageOrientation }, { 50, SetHandlesRequestToDisplay } }; @@ -123,6 +124,15 @@ namespace Ryujinx.HLE.OsHle.Services.Am return 0; } + public long SetScreenShotImageOrientation(ServiceCtx Context) + { + int Orientation = Context.RequestData.ReadInt32(); + + Context.Ns.Log.PrintStub(LogClass.ServiceAm, "Stubbed."); + + return 0; + } + public long SetHandlesRequestToDisplay(ServiceCtx Context) { bool Enable = Context.RequestData.ReadByte() != 0 ? true : false; diff --git a/Ryujinx.HLE/OsHle/Services/IpcService.cs b/Ryujinx.HLE/OsHle/Services/IpcService.cs index 25fd56fe..3c1a136f 100644 --- a/Ryujinx.HLE/OsHle/Services/IpcService.cs +++ b/Ryujinx.HLE/OsHle/Services/IpcService.cs @@ -50,9 +50,18 @@ namespace Ryujinx.HLE.OsHle.Services int DomainWord0 = Context.RequestData.ReadInt32(); int DomainObjId = Context.RequestData.ReadInt32(); - long Padding = Context.RequestData.ReadInt64(); + int DomainCmd = (DomainWord0 >> 0) & 0xff; + int InputObjCount = (DomainWord0 >> 8) & 0xff; + int DataPayloadSize = (DomainWord0 >> 16) & 0xffff; - int DomainCmd = DomainWord0 & 0xff; + Context.RequestData.BaseStream.Seek(0x10 + DataPayloadSize, SeekOrigin.Begin); + + for (int Index = 0; Index < InputObjCount; Index++) + { + Context.Request.ObjectIds.Add(Context.RequestData.ReadInt32()); + } + + Context.RequestData.BaseStream.Seek(0x10, SeekOrigin.Begin); if (DomainCmd == 1) { @@ -88,14 +97,14 @@ namespace Ryujinx.HLE.OsHle.Services if (IsDomain) { - foreach (int Id in Context.Response.ResponseObjIds) + foreach (int Id in Context.Response.ObjectIds) { Context.ResponseData.Write(Id); } Context.ResponseData.BaseStream.Seek(0, SeekOrigin.Begin); - Context.ResponseData.Write(Context.Response.ResponseObjIds.Count); + Context.ResponseData.Write(Context.Response.ObjectIds.Count); } Context.ResponseData.BaseStream.Seek(IsDomain ? 0x10 : 0, SeekOrigin.Begin); @@ -117,7 +126,7 @@ namespace Ryujinx.HLE.OsHle.Services if (Service.IsDomain) { - Context.Response.ResponseObjIds.Add(Service.Add(Obj)); + Context.Response.ObjectIds.Add(Service.Add(Obj)); } else { @@ -129,6 +138,26 @@ namespace Ryujinx.HLE.OsHle.Services } } + protected static T GetObject<T>(ServiceCtx Context, int Index) where T : IpcService + { + IpcService Service = Context.Session.Service; + + if (!Service.IsDomain) + { + int Handle = Context.Request.HandleDesc.ToMove[Index]; + + KSession Session = Context.Process.HandleTable.GetData<KSession>(Handle); + + return Session?.Service is T ? (T)Session.Service : null; + } + + int ObjId = Context.Request.ObjectIds[Index]; + + IIpcService Obj = Service.GetObject(ObjId); + + return Obj is T ? (T)Obj : null; + } + private int Add(IIpcService Obj) { return DomainObjects.Add(Obj); diff --git a/Ryujinx.HLE/OsHle/Services/Nifm/IRequest.cs b/Ryujinx.HLE/OsHle/Services/Nifm/IRequest.cs index c8c679c4..2056187d 100644 --- a/Ryujinx.HLE/OsHle/Services/Nifm/IRequest.cs +++ b/Ryujinx.HLE/OsHle/Services/Nifm/IRequest.cs @@ -12,7 +12,8 @@ namespace Ryujinx.HLE.OsHle.Services.Nifm public override IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands; - private KEvent Event; + private KEvent Event0; + private KEvent Event1; public IRequest() { @@ -26,12 +27,13 @@ namespace Ryujinx.HLE.OsHle.Services.Nifm { 11, SetConnectionConfirmationOption } }; - Event = new KEvent(); + Event0 = new KEvent(); + Event1 = new KEvent(); } public long GetRequestState(ServiceCtx Context) { - Context.ResponseData.Write(0); + Context.ResponseData.Write(1); Context.Ns.Log.PrintStub(LogClass.ServiceNifm, "Stubbed."); @@ -45,13 +47,12 @@ namespace Ryujinx.HLE.OsHle.Services.Nifm return 0; } - //GetSystemEventReadableHandles() -> (KObject, KObject) public long GetSystemEventReadableHandles(ServiceCtx Context) { - //FIXME: Is this supposed to return 2 events? - int Handle = Context.Process.HandleTable.OpenHandle(Event); + int Handle0 = Context.Process.HandleTable.OpenHandle(Event0); + int Handle1 = Context.Process.HandleTable.OpenHandle(Event1); - Context.Response.HandleDesc = IpcHandleDesc.MakeMove(Handle); + Context.Response.HandleDesc = IpcHandleDesc.MakeCopy(Handle0, Handle1); return 0; } @@ -86,7 +87,8 @@ namespace Ryujinx.HLE.OsHle.Services.Nifm { if (Disposing) { - Event.Dispose(); + Event0.Dispose(); + Event1.Dispose(); } } } diff --git a/Ryujinx.HLE/OsHle/Services/Pl/ISharedFontManager.cs b/Ryujinx.HLE/OsHle/Services/Pl/ISharedFontManager.cs index 9f85f3d1..b8447ac6 100644 --- a/Ryujinx.HLE/OsHle/Services/Pl/ISharedFontManager.cs +++ b/Ryujinx.HLE/OsHle/Services/Pl/ISharedFontManager.cs @@ -1,3 +1,4 @@ +using Ryujinx.HLE.Font; using Ryujinx.HLE.OsHle.Ipc; using System.Collections.Generic; @@ -13,11 +14,12 @@ namespace Ryujinx.HLE.OsHle.Services.Pl { m_Commands = new Dictionary<int, ServiceProcessRequest>() { - { 0, RequestLoad }, - { 1, GetLoadState }, - { 2, GetFontSize }, - { 3, GetSharedMemoryAddressOffset }, - { 4, GetSharedMemoryNativeHandle } + { 0, RequestLoad }, + { 1, GetLoadState }, + { 2, GetFontSize }, + { 3, GetSharedMemoryAddressOffset }, + { 4, GetSharedMemoryNativeHandle }, + { 5, GetSharedFontInOrderOfPriority } }; } @@ -25,26 +27,34 @@ namespace Ryujinx.HLE.OsHle.Services.Pl { SharedFontType FontType = (SharedFontType)Context.RequestData.ReadInt32(); + Context.Ns.Font.Load(FontType); + return 0; } public long GetLoadState(ServiceCtx Context) { - Context.ResponseData.Write(1); //Loaded + SharedFontType FontType = (SharedFontType)Context.RequestData.ReadInt32(); + + Context.ResponseData.Write(Context.Ns.Font.GetLoadState(FontType)); return 0; } public long GetFontSize(ServiceCtx Context) { - Context.ResponseData.Write(Horizon.FontSize); + SharedFontType FontType = (SharedFontType)Context.RequestData.ReadInt32(); + + Context.ResponseData.Write(Context.Ns.Font.GetFontSize(FontType)); return 0; } public long GetSharedMemoryAddressOffset(ServiceCtx Context) { - Context.ResponseData.Write(0); + SharedFontType FontType = (SharedFontType)Context.RequestData.ReadInt32(); + + Context.ResponseData.Write(Context.Ns.Font.GetSharedMemoryAddressOffset(FontType)); return 0; } @@ -57,5 +67,51 @@ namespace Ryujinx.HLE.OsHle.Services.Pl return 0; } + + private uint AddFontToOrderOfPriorityList(ServiceCtx Context, SharedFontType FontType, uint BufferPos, out uint LoadState) + { + long TypesPosition = Context.Request.ReceiveBuff[0].Position; + long TypesSize = Context.Request.ReceiveBuff[0].Size; + + long OffsetsPosition = Context.Request.ReceiveBuff[1].Position; + long OffsetsSize = Context.Request.ReceiveBuff[1].Size; + + long FontSizeBufferPosition = Context.Request.ReceiveBuff[2].Position; + long FontSizeBufferSize = Context.Request.ReceiveBuff[2].Size; + + LoadState = Context.Ns.Font.GetLoadState(FontType); + + if (BufferPos >= TypesSize || BufferPos >= OffsetsSize || BufferPos >= FontSizeBufferSize) + { + return 0; + } + + Context.Memory.WriteUInt32(TypesPosition + BufferPos, (uint)FontType); + Context.Memory.WriteUInt32(OffsetsPosition + BufferPos, Context.Ns.Font.GetSharedMemoryAddressOffset(FontType)); + Context.Memory.WriteUInt32(FontSizeBufferPosition + BufferPos, Context.Ns.Font.GetFontSize(FontType)); + + BufferPos += 4; + + return BufferPos; + } + + public long GetSharedFontInOrderOfPriority(ServiceCtx Context) + { + ulong LanguageCode = Context.RequestData.ReadUInt64(); + uint LoadedCount = 0; + uint BufferPos = 0; + uint Loaded = 0; + + for (int Type = 0; Type < Context.Ns.Font.Count; Type++) + { + BufferPos = AddFontToOrderOfPriorityList(Context, (SharedFontType)Type, BufferPos, out Loaded); + LoadedCount += Loaded; + } + + Context.ResponseData.Write(LoadedCount); + Context.ResponseData.Write(Context.Ns.Font.Count); + + return 0; + } } }
\ No newline at end of file diff --git a/Ryujinx.HLE/OsHle/SystemStateMgr.cs b/Ryujinx.HLE/OsHle/SystemStateMgr.cs index e78082c4..87e3de1e 100644 --- a/Ryujinx.HLE/OsHle/SystemStateMgr.cs +++ b/Ryujinx.HLE/OsHle/SystemStateMgr.cs @@ -1,3 +1,4 @@ +using Ryujinx.HLE.Loaders.Npdm; using System; namespace Ryujinx.HLE.OsHle @@ -35,6 +36,8 @@ namespace Ryujinx.HLE.OsHle internal long DesiredLanguageCode { get; private set; } internal string ActiveAudioOutput { get; private set; } + + public bool DockedMode { get; set; } public SystemStateMgr() { @@ -81,4 +84,4 @@ namespace Ryujinx.HLE.OsHle return Code; } } -}
\ No newline at end of file +} diff --git a/Ryujinx.HLE/Resource/InvalidSystemResourceException.cs b/Ryujinx.HLE/Resource/InvalidSystemResourceException.cs new file mode 100644 index 00000000..35c4874a --- /dev/null +++ b/Ryujinx.HLE/Resource/InvalidSystemResourceException.cs @@ -0,0 +1,13 @@ +using System; + +namespace Ryujinx.HLE.Resource +{ + public class InvalidSystemResourceException : Exception + { + public InvalidSystemResourceException(string message) + : base(message) + { + } + + } +} diff --git a/Ryujinx.HLE/Ryujinx.HLE.csproj b/Ryujinx.HLE/Ryujinx.HLE.csproj index acef4be9..f7fb84a5 100644 --- a/Ryujinx.HLE/Ryujinx.HLE.csproj +++ b/Ryujinx.HLE/Ryujinx.HLE.csproj @@ -1,4 +1,4 @@ -<Project Sdk="Microsoft.NET.Sdk"> +<Project Sdk="Microsoft.NET.Sdk"> <PropertyGroup> <TargetFramework>netcoreapp2.1</TargetFramework> diff --git a/Ryujinx.HLE/Switch.cs b/Ryujinx.HLE/Switch.cs index 74c0564a..a80ca86c 100644 --- a/Ryujinx.HLE/Switch.cs +++ b/Ryujinx.HLE/Switch.cs @@ -1,5 +1,6 @@ using Ryujinx.Audio; using Ryujinx.Graphics.Gal; +using Ryujinx.HLE.Font; using Ryujinx.HLE.Gpu; using Ryujinx.HLE.Input; using Ryujinx.HLE.Logging; @@ -27,6 +28,8 @@ namespace Ryujinx.HLE public Hid Hid { get; private set; } + public SharedFontManager Font { get; private set; } + public event EventHandler Finish; public Switch(IGalRenderer Renderer, IAalOutput AudioOut) @@ -57,8 +60,13 @@ namespace Ryujinx.HLE Hid = new Hid(Log); - Os.HidSharedMem.MemoryMapped += Hid.ShMemMap; - Os.HidSharedMem.MemoryUnmapped += Hid.ShMemUnmap; + Font = new SharedFontManager(Log, VFs.GetSystemPath()); + + Os.HidSharedMem.MemoryMapped += Hid.ShMemMap; + Os.HidSharedMem.MemoryUnmapped += Hid.ShMemUnmap; + + Os.FontSharedMem.MemoryMapped += Font.ShMemMap; + Os.FontSharedMem.MemoryUnmapped += Font.ShMemUnmap; } public void LoadCart(string ExeFsDir, string RomFsFile = null) diff --git a/Ryujinx.HLE/VirtualFileSystem.cs b/Ryujinx.HLE/VirtualFileSystem.cs index df1fc9db..31b8e184 100644 --- a/Ryujinx.HLE/VirtualFileSystem.cs +++ b/Ryujinx.HLE/VirtualFileSystem.cs @@ -8,6 +8,7 @@ namespace Ryujinx.HLE private const string BasePath = "RyuFs"; private const string NandPath = "nand"; private const string SdCardPath = "sdmc"; + private const string SystemPath = "system"; public Stream RomFs { get; private set; } @@ -45,6 +46,8 @@ namespace Ryujinx.HLE public string GetGameSavesPath() => MakeDirAndGetFullPath(NandPath); + public string GetSystemPath() => MakeDirAndGetFullPath(SystemPath); + public string SwitchPathToSystemPath(string SwitchPath) { string[] Parts = SwitchPath.Split(":"); diff --git a/Ryujinx.LLE/Luea.csproj b/Ryujinx.LLE/Luea.csproj index de1c5f61..5c571568 100644 --- a/Ryujinx.LLE/Luea.csproj +++ b/Ryujinx.LLE/Luea.csproj @@ -1,9 +1,9 @@ <Project Sdk="Microsoft.NET.Sdk"> <PropertyGroup> - <OutputType>Exe</OutputType> <TargetFramework>netcoreapp2.1</TargetFramework> <RuntimeIdentifiers>win10-x64;osx-x64;linux-x64</RuntimeIdentifiers> + <OutputType>Exe</OutputType> </PropertyGroup> </Project> diff --git a/Ryujinx.ShaderTools/Ryujinx.ShaderTools.csproj b/Ryujinx.ShaderTools/Ryujinx.ShaderTools.csproj index 24f31efe..18452f0a 100644 --- a/Ryujinx.ShaderTools/Ryujinx.ShaderTools.csproj +++ b/Ryujinx.ShaderTools/Ryujinx.ShaderTools.csproj @@ -1,13 +1,13 @@ <Project Sdk="Microsoft.NET.Sdk"> - <ItemGroup> - <ProjectReference Include="..\Ryujinx.Graphics\Ryujinx.Graphics.csproj" /> - </ItemGroup> - <PropertyGroup> - <OutputType>Exe</OutputType> <TargetFramework>netcoreapp2.1</TargetFramework> <RuntimeIdentifiers>win10-x64;osx-x64;linux-x64</RuntimeIdentifiers> + <OutputType>Exe</OutputType> </PropertyGroup> + <ItemGroup> + <ProjectReference Include="..\Ryujinx.Graphics\Ryujinx.Graphics.csproj" /> + </ItemGroup> + </Project> diff --git a/Ryujinx.Tests/Cpu/CpuTestAlu.cs b/Ryujinx.Tests/Cpu/CpuTestAlu.cs index 564fadec..c89cb646 100644 --- a/Ryujinx.Tests/Cpu/CpuTestAlu.cs +++ b/Ryujinx.Tests/Cpu/CpuTestAlu.cs @@ -9,7 +9,7 @@ namespace Ryujinx.Tests.Cpu using Tester; using Tester.Types; - [Category("Alu"), Ignore("Tested: first half of 2018.")] + [Category("Alu"), Ignore("Tested: second half of 2018.")] public sealed class CpuTestAlu : CpuTest { #if Alu diff --git a/Ryujinx.Tests/Cpu/CpuTestAluImm.cs b/Ryujinx.Tests/Cpu/CpuTestAluImm.cs index 5d1f0b6b..d436d65f 100644 --- a/Ryujinx.Tests/Cpu/CpuTestAluImm.cs +++ b/Ryujinx.Tests/Cpu/CpuTestAluImm.cs @@ -9,7 +9,7 @@ namespace Ryujinx.Tests.Cpu using Tester; using Tester.Types; - [Category("AluImm"), Ignore("Tested: first half of 2018.")] + [Category("AluImm"), Ignore("Tested: second half of 2018.")] public sealed class CpuTestAluImm : CpuTest { #if AluImm diff --git a/Ryujinx.Tests/Cpu/CpuTestAluRs.cs b/Ryujinx.Tests/Cpu/CpuTestAluRs.cs index b81f7100..880794cf 100644 --- a/Ryujinx.Tests/Cpu/CpuTestAluRs.cs +++ b/Ryujinx.Tests/Cpu/CpuTestAluRs.cs @@ -9,7 +9,7 @@ namespace Ryujinx.Tests.Cpu using Tester; using Tester.Types; - [Category("AluRs"), Ignore("Tested: first half of 2018.")] + [Category("AluRs"), Ignore("Tested: second half of 2018.")] public sealed class CpuTestAluRs : CpuTest { #if AluRs diff --git a/Ryujinx.Tests/Cpu/CpuTestAluRx.cs b/Ryujinx.Tests/Cpu/CpuTestAluRx.cs index 26169bca..d6cf8dc4 100644 --- a/Ryujinx.Tests/Cpu/CpuTestAluRx.cs +++ b/Ryujinx.Tests/Cpu/CpuTestAluRx.cs @@ -9,7 +9,7 @@ namespace Ryujinx.Tests.Cpu using Tester; using Tester.Types; - [Category("AluRx"), Ignore("Tested: first half of 2018.")] + [Category("AluRx"), Ignore("Tested: second half of 2018.")] public sealed class CpuTestAluRx : CpuTest { #if AluRx diff --git a/Ryujinx.Tests/Cpu/CpuTestBfm.cs b/Ryujinx.Tests/Cpu/CpuTestBfm.cs index 2952bca4..0a2f9ad3 100644 --- a/Ryujinx.Tests/Cpu/CpuTestBfm.cs +++ b/Ryujinx.Tests/Cpu/CpuTestBfm.cs @@ -9,7 +9,7 @@ namespace Ryujinx.Tests.Cpu using Tester; using Tester.Types; - [Category("Bfm"), Ignore("Tested: first half of 2018.")] + [Category("Bfm"), Ignore("Tested: second half of 2018.")] public sealed class CpuTestBfm : CpuTest { #if Bfm diff --git a/Ryujinx.Tests/Cpu/CpuTestCcmpImm.cs b/Ryujinx.Tests/Cpu/CpuTestCcmpImm.cs index 38d73878..7ba44ed9 100644 --- a/Ryujinx.Tests/Cpu/CpuTestCcmpImm.cs +++ b/Ryujinx.Tests/Cpu/CpuTestCcmpImm.cs @@ -9,7 +9,7 @@ namespace Ryujinx.Tests.Cpu using Tester; using Tester.Types; - [Category("CcmpImm"), Ignore("Tested: first half of 2018.")] + [Category("CcmpImm"), Ignore("Tested: second half of 2018.")] public sealed class CpuTestCcmpImm : CpuTest { #if CcmpImm diff --git a/Ryujinx.Tests/Cpu/CpuTestCcmpReg.cs b/Ryujinx.Tests/Cpu/CpuTestCcmpReg.cs index eb1c3abf..82556464 100644 --- a/Ryujinx.Tests/Cpu/CpuTestCcmpReg.cs +++ b/Ryujinx.Tests/Cpu/CpuTestCcmpReg.cs @@ -9,7 +9,7 @@ namespace Ryujinx.Tests.Cpu using Tester; using Tester.Types; - [Category("CcmpReg"), Ignore("Tested: first half of 2018.")] + [Category("CcmpReg"), Ignore("Tested: second half of 2018.")] public sealed class CpuTestCcmpReg : CpuTest { #if CcmpReg diff --git a/Ryujinx.Tests/Cpu/CpuTestCsel.cs b/Ryujinx.Tests/Cpu/CpuTestCsel.cs index 9dd61957..fef56fee 100644 --- a/Ryujinx.Tests/Cpu/CpuTestCsel.cs +++ b/Ryujinx.Tests/Cpu/CpuTestCsel.cs @@ -9,7 +9,7 @@ namespace Ryujinx.Tests.Cpu using Tester; using Tester.Types; - [Category("Csel"), Ignore("Tested: first half of 2018.")] + [Category("Csel"), Ignore("Tested: second half of 2018.")] public sealed class CpuTestCsel : CpuTest { #if Csel diff --git a/Ryujinx.Tests/Cpu/CpuTestMov.cs b/Ryujinx.Tests/Cpu/CpuTestMov.cs index 9c7e3255..db544f86 100644 --- a/Ryujinx.Tests/Cpu/CpuTestMov.cs +++ b/Ryujinx.Tests/Cpu/CpuTestMov.cs @@ -9,7 +9,7 @@ namespace Ryujinx.Tests.Cpu using Tester; using Tester.Types; - [Category("Mov"), Ignore("Tested: first half of 2018.")] + [Category("Mov"), Ignore("Tested: second half of 2018.")] public sealed class CpuTestMov : CpuTest { #if Mov diff --git a/Ryujinx.Tests/Cpu/CpuTestMul.cs b/Ryujinx.Tests/Cpu/CpuTestMul.cs index 9bdc1fa6..19b8ecd0 100644 --- a/Ryujinx.Tests/Cpu/CpuTestMul.cs +++ b/Ryujinx.Tests/Cpu/CpuTestMul.cs @@ -9,7 +9,7 @@ namespace Ryujinx.Tests.Cpu using Tester; using Tester.Types; - [Category("Mul"), Ignore("Tested: first half of 2018.")] + [Category("Mul"), Ignore("Tested: second half of 2018.")] public sealed class CpuTestMul : CpuTest { #if Mul diff --git a/Ryujinx.Tests/Cpu/CpuTestSimd.cs b/Ryujinx.Tests/Cpu/CpuTestSimd.cs index 82591eda..bb60273a 100644 --- a/Ryujinx.Tests/Cpu/CpuTestSimd.cs +++ b/Ryujinx.Tests/Cpu/CpuTestSimd.cs @@ -22,6 +22,17 @@ namespace Ryujinx.Tests.Cpu } #region "ValueSource" + private static ulong[] _1B1H1S1D_() + { + return new ulong[] { 0x0000000000000000ul, 0x000000000000007Ful, + 0x0000000000000080ul, 0x00000000000000FFul, + 0x0000000000007FFFul, 0x0000000000008000ul, + 0x000000000000FFFFul, 0x000000007FFFFFFFul, + 0x0000000080000000ul, 0x00000000FFFFFFFFul, + 0x7FFFFFFFFFFFFFFFul, 0x8000000000000000ul, + 0xFFFFFFFFFFFFFFFFul }; + } + private static ulong[] _1D_() { return new ulong[] { 0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul, @@ -79,7 +90,7 @@ namespace Ryujinx.Tests.Cpu private const int RndCnt = 1; [Test, Description("ABS <V><d>, <V><n>")] - public void Abs_S_D([Values(0u)] uint Rd, + public void Abs_S_D([Values(0u)] uint Rd, [Values(1u, 0u)] uint Rn, [ValueSource("_1D_")] [Random(RndCnt)] ulong Z, [ValueSource("_1D_")] [Random(RndCnt)] ulong A) @@ -104,7 +115,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Description("ABS <Vd>.<T>, <Vn>.<T>")] - public void Abs_V_8B_4H_2S([Values(0u)] uint Rd, + public void Abs_V_8B_4H_2S([Values(0u)] uint Rd, [Values(1u, 0u)] uint Rn, [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong Z, [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong A, @@ -131,7 +142,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Description("ABS <Vd>.<T>, <Vn>.<T>")] - public void Abs_V_16B_8H_4S_2D([Values(0u)] uint Rd, + public void Abs_V_16B_8H_4S_2D([Values(0u)] uint Rd, [Values(1u, 0u)] uint Rn, [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong Z, [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong A, @@ -158,7 +169,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Description("ADDP <V><d>, <Vn>.<T>")] - public void Addp_S_2DD([Values(0u)] uint Rd, + public void Addp_S_2DD([Values(0u)] uint Rd, [Values(1u, 0u)] uint Rn, [ValueSource("_1D_")] [Random(RndCnt)] ulong Z, [ValueSource("_1D_")] [Random(RndCnt)] ulong A) @@ -183,7 +194,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Description("ADDV <V><d>, <Vn>.<T>")] - public void Addv_V_8BB_4HH([Values(0u)] uint Rd, + public void Addv_V_8BB_4HH([Values(0u)] uint Rd, [Values(1u, 0u)] uint Rn, [ValueSource("_8B4H_")] [Random(RndCnt)] ulong Z, [ValueSource("_8B4H_")] [Random(RndCnt)] ulong A, @@ -210,7 +221,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Description("ADDV <V><d>, <Vn>.<T>")] - public void Addv_V_16BB_8HH_4SS([Values(0u)] uint Rd, + public void Addv_V_16BB_8HH_4SS([Values(0u)] uint Rd, [Values(1u, 0u)] uint Rn, [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong Z, [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong A, @@ -237,7 +248,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Description("CLS <Vd>.<T>, <Vn>.<T>")] - public void Cls_V_8B_4H_2S([Values(0u)] uint Rd, + public void Cls_V_8B_4H_2S([Values(0u)] uint Rd, [Values(1u, 0u)] uint Rn, [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong Z, [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong A, @@ -264,7 +275,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Description("CLS <Vd>.<T>, <Vn>.<T>")] - public void Cls_V_16B_8H_4S([Values(0u)] uint Rd, + public void Cls_V_16B_8H_4S([Values(0u)] uint Rd, [Values(1u, 0u)] uint Rn, [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong Z, [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong A, @@ -291,7 +302,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Description("CLZ <Vd>.<T>, <Vn>.<T>")] - public void Clz_V_8B_4H_2S([Values(0u)] uint Rd, + public void Clz_V_8B_4H_2S([Values(0u)] uint Rd, [Values(1u, 0u)] uint Rn, [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong Z, [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong A, @@ -318,7 +329,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Description("CLZ <Vd>.<T>, <Vn>.<T>")] - public void Clz_V_16B_8H_4S([Values(0u)] uint Rd, + public void Clz_V_16B_8H_4S([Values(0u)] uint Rd, [Values(1u, 0u)] uint Rn, [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong Z, [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong A, @@ -345,7 +356,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Description("CMEQ <V><d>, <V><n>, #0")] - public void Cmeq_S_D([Values(0u)] uint Rd, + public void Cmeq_S_D([Values(0u)] uint Rd, [Values(1u, 0u)] uint Rn, [ValueSource("_1D_")] [Random(RndCnt)] ulong Z, [ValueSource("_1D_")] [Random(RndCnt)] ulong A) @@ -370,7 +381,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Description("CMEQ <Vd>.<T>, <Vn>.<T>, #0")] - public void Cmeq_V_8B_4H_2S([Values(0u)] uint Rd, + public void Cmeq_V_8B_4H_2S([Values(0u)] uint Rd, [Values(1u, 0u)] uint Rn, [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong Z, [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong A, @@ -397,7 +408,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Description("CMEQ <Vd>.<T>, <Vn>.<T>, #0")] - public void Cmeq_V_16B_8H_4S_2D([Values(0u)] uint Rd, + public void Cmeq_V_16B_8H_4S_2D([Values(0u)] uint Rd, [Values(1u, 0u)] uint Rn, [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong Z, [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong A, @@ -424,7 +435,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Description("CMGE <V><d>, <V><n>, #0")] - public void Cmge_S_D([Values(0u)] uint Rd, + public void Cmge_S_D([Values(0u)] uint Rd, [Values(1u, 0u)] uint Rn, [ValueSource("_1D_")] [Random(RndCnt)] ulong Z, [ValueSource("_1D_")] [Random(RndCnt)] ulong A) @@ -449,7 +460,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Description("CMGE <Vd>.<T>, <Vn>.<T>, #0")] - public void Cmge_V_8B_4H_2S([Values(0u)] uint Rd, + public void Cmge_V_8B_4H_2S([Values(0u)] uint Rd, [Values(1u, 0u)] uint Rn, [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong Z, [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong A, @@ -476,7 +487,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Description("CMGE <Vd>.<T>, <Vn>.<T>, #0")] - public void Cmge_V_16B_8H_4S_2D([Values(0u)] uint Rd, + public void Cmge_V_16B_8H_4S_2D([Values(0u)] uint Rd, [Values(1u, 0u)] uint Rn, [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong Z, [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong A, @@ -503,7 +514,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Description("CMGT <V><d>, <V><n>, #0")] - public void Cmgt_S_D([Values(0u)] uint Rd, + public void Cmgt_S_D([Values(0u)] uint Rd, [Values(1u, 0u)] uint Rn, [ValueSource("_1D_")] [Random(RndCnt)] ulong Z, [ValueSource("_1D_")] [Random(RndCnt)] ulong A) @@ -528,7 +539,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Description("CMGT <Vd>.<T>, <Vn>.<T>, #0")] - public void Cmgt_V_8B_4H_2S([Values(0u)] uint Rd, + public void Cmgt_V_8B_4H_2S([Values(0u)] uint Rd, [Values(1u, 0u)] uint Rn, [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong Z, [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong A, @@ -555,7 +566,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Description("CMGT <Vd>.<T>, <Vn>.<T>, #0")] - public void Cmgt_V_16B_8H_4S_2D([Values(0u)] uint Rd, + public void Cmgt_V_16B_8H_4S_2D([Values(0u)] uint Rd, [Values(1u, 0u)] uint Rn, [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong Z, [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong A, @@ -582,7 +593,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Description("CMLE <V><d>, <V><n>, #0")] - public void Cmle_S_D([Values(0u)] uint Rd, + public void Cmle_S_D([Values(0u)] uint Rd, [Values(1u, 0u)] uint Rn, [ValueSource("_1D_")] [Random(RndCnt)] ulong Z, [ValueSource("_1D_")] [Random(RndCnt)] ulong A) @@ -607,7 +618,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Description("CMLE <Vd>.<T>, <Vn>.<T>, #0")] - public void Cmle_V_8B_4H_2S([Values(0u)] uint Rd, + public void Cmle_V_8B_4H_2S([Values(0u)] uint Rd, [Values(1u, 0u)] uint Rn, [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong Z, [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong A, @@ -634,7 +645,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Description("CMLE <Vd>.<T>, <Vn>.<T>, #0")] - public void Cmle_V_16B_8H_4S_2D([Values(0u)] uint Rd, + public void Cmle_V_16B_8H_4S_2D([Values(0u)] uint Rd, [Values(1u, 0u)] uint Rn, [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong Z, [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong A, @@ -661,7 +672,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Description("CMLT <V><d>, <V><n>, #0")] - public void Cmlt_S_D([Values(0u)] uint Rd, + public void Cmlt_S_D([Values(0u)] uint Rd, [Values(1u, 0u)] uint Rn, [ValueSource("_1D_")] [Random(RndCnt)] ulong Z, [ValueSource("_1D_")] [Random(RndCnt)] ulong A) @@ -686,7 +697,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Description("CMLT <Vd>.<T>, <Vn>.<T>, #0")] - public void Cmlt_V_8B_4H_2S([Values(0u)] uint Rd, + public void Cmlt_V_8B_4H_2S([Values(0u)] uint Rd, [Values(1u, 0u)] uint Rn, [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong Z, [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong A, @@ -713,7 +724,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Description("CMLT <Vd>.<T>, <Vn>.<T>, #0")] - public void Cmlt_V_16B_8H_4S_2D([Values(0u)] uint Rd, + public void Cmlt_V_16B_8H_4S_2D([Values(0u)] uint Rd, [Values(1u, 0u)] uint Rn, [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong Z, [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong A, @@ -740,7 +751,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Description("CNT <Vd>.<T>, <Vn>.<T>")] - public void Cnt_V_8B([Values(0u)] uint Rd, + public void Cnt_V_8B([Values(0u)] uint Rd, [Values(1u, 0u)] uint Rn, [ValueSource("_8B_")] [Random(RndCnt)] ulong Z, [ValueSource("_8B_")] [Random(RndCnt)] ulong A) @@ -765,7 +776,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Description("CNT <Vd>.<T>, <Vn>.<T>")] - public void Cnt_V_16B([Values(0u)] uint Rd, + public void Cnt_V_16B([Values(0u)] uint Rd, [Values(1u, 0u)] uint Rn, [ValueSource("_8B_")] [Random(RndCnt)] ulong Z, [ValueSource("_8B_")] [Random(RndCnt)] ulong A) @@ -790,7 +801,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Description("NEG <V><d>, <V><n>")] - public void Neg_S_D([Values(0u)] uint Rd, + public void Neg_S_D([Values(0u)] uint Rd, [Values(1u, 0u)] uint Rn, [ValueSource("_1D_")] [Random(RndCnt)] ulong Z, [ValueSource("_1D_")] [Random(RndCnt)] ulong A) @@ -815,7 +826,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Description("NEG <Vd>.<T>, <Vn>.<T>")] - public void Neg_V_8B_4H_2S([Values(0u)] uint Rd, + public void Neg_V_8B_4H_2S([Values(0u)] uint Rd, [Values(1u, 0u)] uint Rn, [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong Z, [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong A, @@ -842,7 +853,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Description("NEG <Vd>.<T>, <Vn>.<T>")] - public void Neg_V_16B_8H_4S_2D([Values(0u)] uint Rd, + public void Neg_V_16B_8H_4S_2D([Values(0u)] uint Rd, [Values(1u, 0u)] uint Rn, [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong Z, [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong A, @@ -869,7 +880,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Description("NOT <Vd>.<T>, <Vn>.<T>")] - public void Not_V_8B([Values(0u)] uint Rd, + public void Not_V_8B([Values(0u)] uint Rd, [Values(1u, 0u)] uint Rn, [ValueSource("_8B_")] [Random(RndCnt)] ulong Z, [ValueSource("_8B_")] [Random(RndCnt)] ulong A) @@ -894,7 +905,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Description("NOT <Vd>.<T>, <Vn>.<T>")] - public void Not_V_16B([Values(0u)] uint Rd, + public void Not_V_16B([Values(0u)] uint Rd, [Values(1u, 0u)] uint Rn, [ValueSource("_8B_")] [Random(RndCnt)] ulong Z, [ValueSource("_8B_")] [Random(RndCnt)] ulong A) @@ -919,7 +930,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Description("RBIT <Vd>.<T>, <Vn>.<T>")] - public void Rbit_V_8B([Values(0u)] uint Rd, + public void Rbit_V_8B([Values(0u)] uint Rd, [Values(1u, 0u)] uint Rn, [ValueSource("_8B_")] [Random(RndCnt)] ulong Z, [ValueSource("_8B_")] [Random(RndCnt)] ulong A) @@ -944,7 +955,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Description("RBIT <Vd>.<T>, <Vn>.<T>")] - public void Rbit_V_16B([Values(0u)] uint Rd, + public void Rbit_V_16B([Values(0u)] uint Rd, [Values(1u, 0u)] uint Rn, [ValueSource("_8B_")] [Random(RndCnt)] ulong Z, [ValueSource("_8B_")] [Random(RndCnt)] ulong A) @@ -969,7 +980,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Description("REV16 <Vd>.<T>, <Vn>.<T>")] - public void Rev16_V_8B([Values(0u)] uint Rd, + public void Rev16_V_8B([Values(0u)] uint Rd, [Values(1u, 0u)] uint Rn, [ValueSource("_8B_")] [Random(RndCnt)] ulong Z, [ValueSource("_8B_")] [Random(RndCnt)] ulong A) @@ -994,7 +1005,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Description("REV16 <Vd>.<T>, <Vn>.<T>")] - public void Rev16_V_16B([Values(0u)] uint Rd, + public void Rev16_V_16B([Values(0u)] uint Rd, [Values(1u, 0u)] uint Rn, [ValueSource("_8B_")] [Random(RndCnt)] ulong Z, [ValueSource("_8B_")] [Random(RndCnt)] ulong A) @@ -1019,7 +1030,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Description("REV32 <Vd>.<T>, <Vn>.<T>")] - public void Rev32_V_8B_4H([Values(0u)] uint Rd, + public void Rev32_V_8B_4H([Values(0u)] uint Rd, [Values(1u, 0u)] uint Rn, [ValueSource("_8B4H_")] [Random(RndCnt)] ulong Z, [ValueSource("_8B4H_")] [Random(RndCnt)] ulong A, @@ -1046,7 +1057,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Description("REV32 <Vd>.<T>, <Vn>.<T>")] - public void Rev32_V_16B_8H([Values(0u)] uint Rd, + public void Rev32_V_16B_8H([Values(0u)] uint Rd, [Values(1u, 0u)] uint Rn, [ValueSource("_8B4H_")] [Random(RndCnt)] ulong Z, [ValueSource("_8B4H_")] [Random(RndCnt)] ulong A, @@ -1073,7 +1084,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Description("REV64 <Vd>.<T>, <Vn>.<T>")] - public void Rev64_V_8B_4H_2S([Values(0u)] uint Rd, + public void Rev64_V_8B_4H_2S([Values(0u)] uint Rd, [Values(1u, 0u)] uint Rn, [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong Z, [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong A, @@ -1100,7 +1111,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Description("REV64 <Vd>.<T>, <Vn>.<T>")] - public void Rev64_V_16B_8H_4S([Values(0u)] uint Rd, + public void Rev64_V_16B_8H_4S([Values(0u)] uint Rd, [Values(1u, 0u)] uint Rn, [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong Z, [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong A, @@ -1126,8 +1137,194 @@ namespace Ryujinx.Tests.Cpu }); } + [Test, Description("SQABS <V><d>, <V><n>")] + public void Sqabs_S_B_H_S_D([Values(0u)] uint Rd, + [Values(1u, 0u)] uint Rn, + [ValueSource("_1B1H1S1D_")] [Random(RndCnt)] ulong Z, + [ValueSource("_1B1H1S1D_")] [Random(RndCnt)] ulong A, + [Values(0b00u, 0b01u, 0b10u, 0b11u)] uint size) // <B, H, S, D> + { + uint Opcode = 0x5E207800; // SQABS B0, B0 + Opcode |= ((Rn & 31) << 5) | ((Rd & 31) << 0); + Opcode |= ((size & 3) << 22); + Bits Op = new Bits(Opcode); + + int Fpsr = (int)TestContext.CurrentContext.Random.NextUInt(); + + Vector128<float> V0 = MakeVectorE0E1(Z, Z); + Vector128<float> V1 = MakeVectorE0(A); + AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, Fpsr: Fpsr); + + AArch64.Vpart(0, 0, new Bits(Z)); AArch64.Vpart(0, 1, new Bits(Z)); + AArch64.V(1, new Bits(A)); + Shared.FPSR = new Bits((uint)Fpsr); + SimdFp.Sqabs_S(Op[23, 22], Op[9, 5], Op[4, 0]); + + Assert.Multiple(() => + { + Assert.That(GetVectorE0(ThreadState.V0), Is.EqualTo(AArch64.Vpart(64, 0, 0).ToUInt64())); + Assert.That(GetVectorE1(ThreadState.V0), Is.EqualTo(AArch64.Vpart(64, 0, 1).ToUInt64())); + }); + Assert.That(ThreadState.Fpsr, Is.EqualTo((int)Shared.FPSR.ToUInt32())); + } + + [Test, Description("SQABS <Vd>.<T>, <Vn>.<T>")] + public void Sqabs_V_8B_4H_2S([Values(0u)] uint Rd, + [Values(1u, 0u)] uint Rn, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong Z, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong A, + [Values(0b00u, 0b01u, 0b10u)] uint size) // <8B, 4H, 2S> + { + uint Opcode = 0x0E207800; // SQABS V0.8B, V0.8B + Opcode |= ((Rn & 31) << 5) | ((Rd & 31) << 0); + Opcode |= ((size & 3) << 22); + Bits Op = new Bits(Opcode); + + int Fpsr = (int)TestContext.CurrentContext.Random.NextUInt(); + + Vector128<float> V0 = MakeVectorE0E1(Z, Z); + Vector128<float> V1 = MakeVectorE0(A); + AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, Fpsr: Fpsr); + + AArch64.Vpart(0, 0, new Bits(Z)); AArch64.Vpart(0, 1, new Bits(Z)); + AArch64.V(1, new Bits(A)); + Shared.FPSR = new Bits((uint)Fpsr); + SimdFp.Sqabs_V(Op[30], Op[23, 22], Op[9, 5], Op[4, 0]); + + Assert.Multiple(() => + { + Assert.That(GetVectorE0(ThreadState.V0), Is.EqualTo(AArch64.Vpart(64, 0, 0).ToUInt64())); + Assert.That(GetVectorE1(ThreadState.V0), Is.EqualTo(AArch64.Vpart(64, 0, 1).ToUInt64())); + }); + Assert.That(ThreadState.Fpsr, Is.EqualTo((int)Shared.FPSR.ToUInt32())); + } + + [Test, Description("SQABS <Vd>.<T>, <Vn>.<T>")] + public void Sqabs_V_16B_8H_4S_2D([Values(0u)] uint Rd, + [Values(1u, 0u)] uint Rn, + [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong Z, + [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong A, + [Values(0b00u, 0b01u, 0b10u, 0b11u)] uint size) // <16B, 8H, 4S, 2D> + { + uint Opcode = 0x4E207800; // SQABS V0.16B, V0.16B + Opcode |= ((Rn & 31) << 5) | ((Rd & 31) << 0); + Opcode |= ((size & 3) << 22); + Bits Op = new Bits(Opcode); + + int Fpsr = (int)TestContext.CurrentContext.Random.NextUInt(); + + Vector128<float> V0 = MakeVectorE0E1(Z, Z); + Vector128<float> V1 = MakeVectorE0E1(A, A); + AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, Fpsr: Fpsr); + + AArch64.Vpart(0, 0, new Bits(Z)); AArch64.Vpart(0, 1, new Bits(Z)); + AArch64.Vpart(1, 0, new Bits(A)); AArch64.Vpart(1, 1, new Bits(A)); + Shared.FPSR = new Bits((uint)Fpsr); + SimdFp.Sqabs_V(Op[30], Op[23, 22], Op[9, 5], Op[4, 0]); + + Assert.Multiple(() => + { + Assert.That(GetVectorE0(ThreadState.V0), Is.EqualTo(AArch64.Vpart(64, 0, 0).ToUInt64())); + Assert.That(GetVectorE1(ThreadState.V0), Is.EqualTo(AArch64.Vpart(64, 0, 1).ToUInt64())); + }); + Assert.That(ThreadState.Fpsr, Is.EqualTo((int)Shared.FPSR.ToUInt32())); + } + + [Test, Description("SQNEG <V><d>, <V><n>")] + public void Sqneg_S_B_H_S_D([Values(0u)] uint Rd, + [Values(1u, 0u)] uint Rn, + [ValueSource("_1B1H1S1D_")] [Random(RndCnt)] ulong Z, + [ValueSource("_1B1H1S1D_")] [Random(RndCnt)] ulong A, + [Values(0b00u, 0b01u, 0b10u, 0b11u)] uint size) // <B, H, S, D> + { + uint Opcode = 0x7E207800; // SQNEG B0, B0 + Opcode |= ((Rn & 31) << 5) | ((Rd & 31) << 0); + Opcode |= ((size & 3) << 22); + Bits Op = new Bits(Opcode); + + int Fpsr = (int)TestContext.CurrentContext.Random.NextUInt(); + + Vector128<float> V0 = MakeVectorE0E1(Z, Z); + Vector128<float> V1 = MakeVectorE0(A); + AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, Fpsr: Fpsr); + + AArch64.Vpart(0, 0, new Bits(Z)); AArch64.Vpart(0, 1, new Bits(Z)); + AArch64.V(1, new Bits(A)); + Shared.FPSR = new Bits((uint)Fpsr); + SimdFp.Sqneg_S(Op[23, 22], Op[9, 5], Op[4, 0]); + + Assert.Multiple(() => + { + Assert.That(GetVectorE0(ThreadState.V0), Is.EqualTo(AArch64.Vpart(64, 0, 0).ToUInt64())); + Assert.That(GetVectorE1(ThreadState.V0), Is.EqualTo(AArch64.Vpart(64, 0, 1).ToUInt64())); + }); + Assert.That(ThreadState.Fpsr, Is.EqualTo((int)Shared.FPSR.ToUInt32())); + } + + [Test, Description("SQNEG <Vd>.<T>, <Vn>.<T>")] + public void Sqneg_V_8B_4H_2S([Values(0u)] uint Rd, + [Values(1u, 0u)] uint Rn, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong Z, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong A, + [Values(0b00u, 0b01u, 0b10u)] uint size) // <8B, 4H, 2S> + { + uint Opcode = 0x2E207800; // SQNEG V0.8B, V0.8B + Opcode |= ((Rn & 31) << 5) | ((Rd & 31) << 0); + Opcode |= ((size & 3) << 22); + Bits Op = new Bits(Opcode); + + int Fpsr = (int)TestContext.CurrentContext.Random.NextUInt(); + + Vector128<float> V0 = MakeVectorE0E1(Z, Z); + Vector128<float> V1 = MakeVectorE0(A); + AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, Fpsr: Fpsr); + + AArch64.Vpart(0, 0, new Bits(Z)); AArch64.Vpart(0, 1, new Bits(Z)); + AArch64.V(1, new Bits(A)); + Shared.FPSR = new Bits((uint)Fpsr); + SimdFp.Sqneg_V(Op[30], Op[23, 22], Op[9, 5], Op[4, 0]); + + Assert.Multiple(() => + { + Assert.That(GetVectorE0(ThreadState.V0), Is.EqualTo(AArch64.Vpart(64, 0, 0).ToUInt64())); + Assert.That(GetVectorE1(ThreadState.V0), Is.EqualTo(AArch64.Vpart(64, 0, 1).ToUInt64())); + }); + Assert.That(ThreadState.Fpsr, Is.EqualTo((int)Shared.FPSR.ToUInt32())); + } + + [Test, Description("SQNEG <Vd>.<T>, <Vn>.<T>")] + public void Sqneg_V_16B_8H_4S_2D([Values(0u)] uint Rd, + [Values(1u, 0u)] uint Rn, + [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong Z, + [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong A, + [Values(0b00u, 0b01u, 0b10u, 0b11u)] uint size) // <16B, 8H, 4S, 2D> + { + uint Opcode = 0x6E207800; // SQNEG V0.16B, V0.16B + Opcode |= ((Rn & 31) << 5) | ((Rd & 31) << 0); + Opcode |= ((size & 3) << 22); + Bits Op = new Bits(Opcode); + + int Fpsr = (int)TestContext.CurrentContext.Random.NextUInt(); + + Vector128<float> V0 = MakeVectorE0E1(Z, Z); + Vector128<float> V1 = MakeVectorE0E1(A, A); + AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, Fpsr: Fpsr); + + AArch64.Vpart(0, 0, new Bits(Z)); AArch64.Vpart(0, 1, new Bits(Z)); + AArch64.Vpart(1, 0, new Bits(A)); AArch64.Vpart(1, 1, new Bits(A)); + Shared.FPSR = new Bits((uint)Fpsr); + SimdFp.Sqneg_V(Op[30], Op[23, 22], Op[9, 5], Op[4, 0]); + + Assert.Multiple(() => + { + Assert.That(GetVectorE0(ThreadState.V0), Is.EqualTo(AArch64.Vpart(64, 0, 0).ToUInt64())); + Assert.That(GetVectorE1(ThreadState.V0), Is.EqualTo(AArch64.Vpart(64, 0, 1).ToUInt64())); + }); + Assert.That(ThreadState.Fpsr, Is.EqualTo((int)Shared.FPSR.ToUInt32())); + } + [Test, Description("SQXTN <Vb><d>, <Va><n>")] - public void Sqxtn_S_HB_SH_DS([Values(0u)] uint Rd, + public void Sqxtn_S_HB_SH_DS([Values(0u)] uint Rd, [Values(1u, 0u)] uint Rn, [ValueSource("_1H1S1D_")] [Random(RndCnt)] ulong Z, [ValueSource("_1H1S1D_")] [Random(RndCnt)] ulong A, @@ -1138,12 +1335,15 @@ namespace Ryujinx.Tests.Cpu Opcode |= ((size & 3) << 22); Bits Op = new Bits(Opcode); + int Fpsr = (int)TestContext.CurrentContext.Random.NextUInt(); + Vector128<float> V0 = MakeVectorE0E1(Z, Z); Vector128<float> V1 = MakeVectorE0(A); - AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1); + AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, Fpsr: Fpsr); AArch64.Vpart(0, 0, new Bits(Z)); AArch64.Vpart(0, 1, new Bits(Z)); AArch64.V(1, new Bits(A)); + Shared.FPSR = new Bits((uint)Fpsr); SimdFp.Sqxtn_S(Op[23, 22], Op[9, 5], Op[4, 0]); Assert.Multiple(() => @@ -1151,11 +1351,11 @@ namespace Ryujinx.Tests.Cpu Assert.That(GetVectorE0(ThreadState.V0), Is.EqualTo(AArch64.Vpart(64, 0, 0).ToUInt64())); Assert.That(GetVectorE1(ThreadState.V0), Is.EqualTo(AArch64.Vpart(64, 0, 1).ToUInt64())); }); - Assert.That(((ThreadState.Fpsr >> 27) & 1) != 0, Is.EqualTo(Shared.FPSR[27])); + Assert.That(ThreadState.Fpsr, Is.EqualTo((int)Shared.FPSR.ToUInt32())); } [Test, Description("SQXTN{2} <Vd>.<Tb>, <Vn>.<Ta>")] - public void Sqxtn_V_8H8B_4S4H_2D2S([Values(0u)] uint Rd, + public void Sqxtn_V_8H8B_4S4H_2D2S([Values(0u)] uint Rd, [Values(1u, 0u)] uint Rn, [ValueSource("_4H2S1D_")] [Random(RndCnt)] ulong Z, [ValueSource("_4H2S1D_")] [Random(RndCnt)] ulong A, @@ -1166,12 +1366,15 @@ namespace Ryujinx.Tests.Cpu Opcode |= ((size & 3) << 22); Bits Op = new Bits(Opcode); + int Fpsr = (int)TestContext.CurrentContext.Random.NextUInt(); + Vector128<float> V0 = MakeVectorE0E1(Z, Z); Vector128<float> V1 = MakeVectorE0E1(A, A); - AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1); + AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, Fpsr: Fpsr); AArch64.Vpart(0, 0, new Bits(Z)); AArch64.Vpart(0, 1, new Bits(Z)); AArch64.Vpart(1, 0, new Bits(A)); AArch64.Vpart(1, 1, new Bits(A)); + Shared.FPSR = new Bits((uint)Fpsr); SimdFp.Sqxtn_V(Op[30], Op[23, 22], Op[9, 5], Op[4, 0]); Assert.Multiple(() => @@ -1179,11 +1382,11 @@ namespace Ryujinx.Tests.Cpu Assert.That(GetVectorE0(ThreadState.V0), Is.EqualTo(AArch64.Vpart(64, 0, 0).ToUInt64())); Assert.That(GetVectorE1(ThreadState.V0), Is.EqualTo(AArch64.Vpart(64, 0, 1).ToUInt64())); }); - Assert.That(((ThreadState.Fpsr >> 27) & 1) != 0, Is.EqualTo(Shared.FPSR[27])); + Assert.That(ThreadState.Fpsr, Is.EqualTo((int)Shared.FPSR.ToUInt32())); } [Test, Description("SQXTN{2} <Vd>.<Tb>, <Vn>.<Ta>")] - public void Sqxtn_V_8H16B_4S8H_2D4S([Values(0u)] uint Rd, + public void Sqxtn_V_8H16B_4S8H_2D4S([Values(0u)] uint Rd, [Values(1u, 0u)] uint Rn, [ValueSource("_4H2S1D_")] [Random(RndCnt)] ulong Z, [ValueSource("_4H2S1D_")] [Random(RndCnt)] ulong A, @@ -1194,12 +1397,15 @@ namespace Ryujinx.Tests.Cpu Opcode |= ((size & 3) << 22); Bits Op = new Bits(Opcode); + int Fpsr = (int)TestContext.CurrentContext.Random.NextUInt(); + Vector128<float> V0 = MakeVectorE0E1(Z, Z); Vector128<float> V1 = MakeVectorE0E1(A, A); - AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1); + AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, Fpsr: Fpsr); AArch64.Vpart(0, 0, new Bits(Z)); AArch64.Vpart(0, 1, new Bits(Z)); AArch64.Vpart(1, 0, new Bits(A)); AArch64.Vpart(1, 1, new Bits(A)); + Shared.FPSR = new Bits((uint)Fpsr); SimdFp.Sqxtn_V(Op[30], Op[23, 22], Op[9, 5], Op[4, 0]); Assert.Multiple(() => @@ -1207,11 +1413,11 @@ namespace Ryujinx.Tests.Cpu Assert.That(GetVectorE0(ThreadState.V0), Is.EqualTo(AArch64.Vpart(64, 0, 0).ToUInt64())); Assert.That(GetVectorE1(ThreadState.V0), Is.EqualTo(AArch64.Vpart(64, 0, 1).ToUInt64())); }); - Assert.That(((ThreadState.Fpsr >> 27) & 1) != 0, Is.EqualTo(Shared.FPSR[27])); + Assert.That(ThreadState.Fpsr, Is.EqualTo((int)Shared.FPSR.ToUInt32())); } [Test, Description("SQXTUN <Vb><d>, <Va><n>")] - public void Sqxtun_S_HB_SH_DS([Values(0u)] uint Rd, + public void Sqxtun_S_HB_SH_DS([Values(0u)] uint Rd, [Values(1u, 0u)] uint Rn, [ValueSource("_1H1S1D_")] [Random(RndCnt)] ulong Z, [ValueSource("_1H1S1D_")] [Random(RndCnt)] ulong A, @@ -1222,12 +1428,15 @@ namespace Ryujinx.Tests.Cpu Opcode |= ((size & 3) << 22); Bits Op = new Bits(Opcode); + int Fpsr = (int)TestContext.CurrentContext.Random.NextUInt(); + Vector128<float> V0 = MakeVectorE0E1(Z, Z); Vector128<float> V1 = MakeVectorE0(A); - AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1); + AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, Fpsr: Fpsr); AArch64.Vpart(0, 0, new Bits(Z)); AArch64.Vpart(0, 1, new Bits(Z)); AArch64.V(1, new Bits(A)); + Shared.FPSR = new Bits((uint)Fpsr); SimdFp.Sqxtun_S(Op[23, 22], Op[9, 5], Op[4, 0]); Assert.Multiple(() => @@ -1235,11 +1444,11 @@ namespace Ryujinx.Tests.Cpu Assert.That(GetVectorE0(ThreadState.V0), Is.EqualTo(AArch64.Vpart(64, 0, 0).ToUInt64())); Assert.That(GetVectorE1(ThreadState.V0), Is.EqualTo(AArch64.Vpart(64, 0, 1).ToUInt64())); }); - Assert.That(((ThreadState.Fpsr >> 27) & 1) != 0, Is.EqualTo(Shared.FPSR[27])); + Assert.That(ThreadState.Fpsr, Is.EqualTo((int)Shared.FPSR.ToUInt32())); } [Test, Description("SQXTUN{2} <Vd>.<Tb>, <Vn>.<Ta>")] - public void Sqxtun_V_8H8B_4S4H_2D2S([Values(0u)] uint Rd, + public void Sqxtun_V_8H8B_4S4H_2D2S([Values(0u)] uint Rd, [Values(1u, 0u)] uint Rn, [ValueSource("_4H2S1D_")] [Random(RndCnt)] ulong Z, [ValueSource("_4H2S1D_")] [Random(RndCnt)] ulong A, @@ -1250,12 +1459,15 @@ namespace Ryujinx.Tests.Cpu Opcode |= ((size & 3) << 22); Bits Op = new Bits(Opcode); + int Fpsr = (int)TestContext.CurrentContext.Random.NextUInt(); + Vector128<float> V0 = MakeVectorE0E1(Z, Z); Vector128<float> V1 = MakeVectorE0E1(A, A); - AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1); + AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, Fpsr: Fpsr); AArch64.Vpart(0, 0, new Bits(Z)); AArch64.Vpart(0, 1, new Bits(Z)); AArch64.Vpart(1, 0, new Bits(A)); AArch64.Vpart(1, 1, new Bits(A)); + Shared.FPSR = new Bits((uint)Fpsr); SimdFp.Sqxtun_V(Op[30], Op[23, 22], Op[9, 5], Op[4, 0]); Assert.Multiple(() => @@ -1263,11 +1475,11 @@ namespace Ryujinx.Tests.Cpu Assert.That(GetVectorE0(ThreadState.V0), Is.EqualTo(AArch64.Vpart(64, 0, 0).ToUInt64())); Assert.That(GetVectorE1(ThreadState.V0), Is.EqualTo(AArch64.Vpart(64, 0, 1).ToUInt64())); }); - Assert.That(((ThreadState.Fpsr >> 27) & 1) != 0, Is.EqualTo(Shared.FPSR[27])); + Assert.That(ThreadState.Fpsr, Is.EqualTo((int)Shared.FPSR.ToUInt32())); } [Test, Description("SQXTUN{2} <Vd>.<Tb>, <Vn>.<Ta>")] - public void Sqxtun_V_8H16B_4S8H_2D4S([Values(0u)] uint Rd, + public void Sqxtun_V_8H16B_4S8H_2D4S([Values(0u)] uint Rd, [Values(1u, 0u)] uint Rn, [ValueSource("_4H2S1D_")] [Random(RndCnt)] ulong Z, [ValueSource("_4H2S1D_")] [Random(RndCnt)] ulong A, @@ -1278,12 +1490,15 @@ namespace Ryujinx.Tests.Cpu Opcode |= ((size & 3) << 22); Bits Op = new Bits(Opcode); + int Fpsr = (int)TestContext.CurrentContext.Random.NextUInt(); + Vector128<float> V0 = MakeVectorE0E1(Z, Z); Vector128<float> V1 = MakeVectorE0E1(A, A); - AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1); + AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, Fpsr: Fpsr); AArch64.Vpart(0, 0, new Bits(Z)); AArch64.Vpart(0, 1, new Bits(Z)); AArch64.Vpart(1, 0, new Bits(A)); AArch64.Vpart(1, 1, new Bits(A)); + Shared.FPSR = new Bits((uint)Fpsr); SimdFp.Sqxtun_V(Op[30], Op[23, 22], Op[9, 5], Op[4, 0]); Assert.Multiple(() => @@ -1291,11 +1506,104 @@ namespace Ryujinx.Tests.Cpu Assert.That(GetVectorE0(ThreadState.V0), Is.EqualTo(AArch64.Vpart(64, 0, 0).ToUInt64())); Assert.That(GetVectorE1(ThreadState.V0), Is.EqualTo(AArch64.Vpart(64, 0, 1).ToUInt64())); }); - Assert.That(((ThreadState.Fpsr >> 27) & 1) != 0, Is.EqualTo(Shared.FPSR[27])); + Assert.That(ThreadState.Fpsr, Is.EqualTo((int)Shared.FPSR.ToUInt32())); + } + + [Test, Description("SUQADD <V><d>, <V><n>")] + public void Suqadd_S_B_H_S_D([Values(0u)] uint Rd, + [Values(1u, 0u)] uint Rn, + [ValueSource("_1B1H1S1D_")] [Random(RndCnt)] ulong Z, + [ValueSource("_1B1H1S1D_")] [Random(RndCnt)] ulong A, + [Values(0b00u, 0b01u, 0b10u, 0b11u)] uint size) // <B, H, S, D> + { + uint Opcode = 0x5E203800; // SUQADD B0, B0 + Opcode |= ((Rn & 31) << 5) | ((Rd & 31) << 0); + Opcode |= ((size & 3) << 22); + Bits Op = new Bits(Opcode); + + int Fpsr = (int)TestContext.CurrentContext.Random.NextUInt(); + + Vector128<float> V0 = MakeVectorE0E1(Z, Z); + Vector128<float> V1 = MakeVectorE0(A); + AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, Fpsr: Fpsr); + + AArch64.Vpart(0, 0, new Bits(Z)); AArch64.Vpart(0, 1, new Bits(Z)); + AArch64.V(1, new Bits(A)); + Shared.FPSR = new Bits((uint)Fpsr); + SimdFp.Suqadd_S(Op[23, 22], Op[9, 5], Op[4, 0]); + + Assert.Multiple(() => + { + Assert.That(GetVectorE0(ThreadState.V0), Is.EqualTo(AArch64.Vpart(64, 0, 0).ToUInt64())); + Assert.That(GetVectorE1(ThreadState.V0), Is.EqualTo(AArch64.Vpart(64, 0, 1).ToUInt64())); + }); + Assert.That(ThreadState.Fpsr, Is.EqualTo((int)Shared.FPSR.ToUInt32())); + } + + [Test, Description("SUQADD <Vd>.<T>, <Vn>.<T>")] + public void Suqadd_V_8B_4H_2S([Values(0u)] uint Rd, + [Values(1u, 0u)] uint Rn, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong Z, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong A, + [Values(0b00u, 0b01u, 0b10u)] uint size) // <8B, 4H, 2S> + { + uint Opcode = 0x0E203800; // SUQADD V0.8B, V0.8B + Opcode |= ((Rn & 31) << 5) | ((Rd & 31) << 0); + Opcode |= ((size & 3) << 22); + Bits Op = new Bits(Opcode); + + int Fpsr = (int)TestContext.CurrentContext.Random.NextUInt(); + + Vector128<float> V0 = MakeVectorE0E1(Z, Z); + Vector128<float> V1 = MakeVectorE0(A); + AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, Fpsr: Fpsr); + + AArch64.Vpart(0, 0, new Bits(Z)); AArch64.Vpart(0, 1, new Bits(Z)); + AArch64.V(1, new Bits(A)); + Shared.FPSR = new Bits((uint)Fpsr); + SimdFp.Suqadd_V(Op[30], Op[23, 22], Op[9, 5], Op[4, 0]); + + Assert.Multiple(() => + { + Assert.That(GetVectorE0(ThreadState.V0), Is.EqualTo(AArch64.Vpart(64, 0, 0).ToUInt64())); + Assert.That(GetVectorE1(ThreadState.V0), Is.EqualTo(AArch64.Vpart(64, 0, 1).ToUInt64())); + }); + Assert.That(ThreadState.Fpsr, Is.EqualTo((int)Shared.FPSR.ToUInt32())); + } + + [Test, Description("SUQADD <Vd>.<T>, <Vn>.<T>")] + public void Suqadd_V_16B_8H_4S_2D([Values(0u)] uint Rd, + [Values(1u, 0u)] uint Rn, + [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong Z, + [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong A, + [Values(0b00u, 0b01u, 0b10u, 0b11u)] uint size) // <16B, 8H, 4S, 2D> + { + uint Opcode = 0x4E203800; // SUQADD V0.16B, V0.16B + Opcode |= ((Rn & 31) << 5) | ((Rd & 31) << 0); + Opcode |= ((size & 3) << 22); + Bits Op = new Bits(Opcode); + + int Fpsr = (int)TestContext.CurrentContext.Random.NextUInt(); + + Vector128<float> V0 = MakeVectorE0E1(Z, Z); + Vector128<float> V1 = MakeVectorE0E1(A, A); + AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, Fpsr: Fpsr); + + AArch64.Vpart(0, 0, new Bits(Z)); AArch64.Vpart(0, 1, new Bits(Z)); + AArch64.Vpart(1, 0, new Bits(A)); AArch64.Vpart(1, 1, new Bits(A)); + Shared.FPSR = new Bits((uint)Fpsr); + SimdFp.Suqadd_V(Op[30], Op[23, 22], Op[9, 5], Op[4, 0]); + + Assert.Multiple(() => + { + Assert.That(GetVectorE0(ThreadState.V0), Is.EqualTo(AArch64.Vpart(64, 0, 0).ToUInt64())); + Assert.That(GetVectorE1(ThreadState.V0), Is.EqualTo(AArch64.Vpart(64, 0, 1).ToUInt64())); + }); + Assert.That(ThreadState.Fpsr, Is.EqualTo((int)Shared.FPSR.ToUInt32())); } [Test, Description("UQXTN <Vb><d>, <Va><n>")] - public void Uqxtn_S_HB_SH_DS([Values(0u)] uint Rd, + public void Uqxtn_S_HB_SH_DS([Values(0u)] uint Rd, [Values(1u, 0u)] uint Rn, [ValueSource("_1H1S1D_")] [Random(RndCnt)] ulong Z, [ValueSource("_1H1S1D_")] [Random(RndCnt)] ulong A, @@ -1306,12 +1614,15 @@ namespace Ryujinx.Tests.Cpu Opcode |= ((size & 3) << 22); Bits Op = new Bits(Opcode); + int Fpsr = (int)TestContext.CurrentContext.Random.NextUInt(); + Vector128<float> V0 = MakeVectorE0E1(Z, Z); Vector128<float> V1 = MakeVectorE0(A); - AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1); + AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, Fpsr: Fpsr); AArch64.Vpart(0, 0, new Bits(Z)); AArch64.Vpart(0, 1, new Bits(Z)); AArch64.V(1, new Bits(A)); + Shared.FPSR = new Bits((uint)Fpsr); SimdFp.Uqxtn_S(Op[23, 22], Op[9, 5], Op[4, 0]); Assert.Multiple(() => @@ -1319,11 +1630,11 @@ namespace Ryujinx.Tests.Cpu Assert.That(GetVectorE0(ThreadState.V0), Is.EqualTo(AArch64.Vpart(64, 0, 0).ToUInt64())); Assert.That(GetVectorE1(ThreadState.V0), Is.EqualTo(AArch64.Vpart(64, 0, 1).ToUInt64())); }); - Assert.That(((ThreadState.Fpsr >> 27) & 1) != 0, Is.EqualTo(Shared.FPSR[27])); + Assert.That(ThreadState.Fpsr, Is.EqualTo((int)Shared.FPSR.ToUInt32())); } [Test, Description("UQXTN{2} <Vd>.<Tb>, <Vn>.<Ta>")] - public void Uqxtn_V_8H8B_4S4H_2D2S([Values(0u)] uint Rd, + public void Uqxtn_V_8H8B_4S4H_2D2S([Values(0u)] uint Rd, [Values(1u, 0u)] uint Rn, [ValueSource("_4H2S1D_")] [Random(RndCnt)] ulong Z, [ValueSource("_4H2S1D_")] [Random(RndCnt)] ulong A, @@ -1334,12 +1645,15 @@ namespace Ryujinx.Tests.Cpu Opcode |= ((size & 3) << 22); Bits Op = new Bits(Opcode); + int Fpsr = (int)TestContext.CurrentContext.Random.NextUInt(); + Vector128<float> V0 = MakeVectorE0E1(Z, Z); Vector128<float> V1 = MakeVectorE0E1(A, A); - AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1); + AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, Fpsr: Fpsr); AArch64.Vpart(0, 0, new Bits(Z)); AArch64.Vpart(0, 1, new Bits(Z)); AArch64.Vpart(1, 0, new Bits(A)); AArch64.Vpart(1, 1, new Bits(A)); + Shared.FPSR = new Bits((uint)Fpsr); SimdFp.Uqxtn_V(Op[30], Op[23, 22], Op[9, 5], Op[4, 0]); Assert.Multiple(() => @@ -1347,11 +1661,11 @@ namespace Ryujinx.Tests.Cpu Assert.That(GetVectorE0(ThreadState.V0), Is.EqualTo(AArch64.Vpart(64, 0, 0).ToUInt64())); Assert.That(GetVectorE1(ThreadState.V0), Is.EqualTo(AArch64.Vpart(64, 0, 1).ToUInt64())); }); - Assert.That(((ThreadState.Fpsr >> 27) & 1) != 0, Is.EqualTo(Shared.FPSR[27])); + Assert.That(ThreadState.Fpsr, Is.EqualTo((int)Shared.FPSR.ToUInt32())); } [Test, Description("UQXTN{2} <Vd>.<Tb>, <Vn>.<Ta>")] - public void Uqxtn_V_8H16B_4S8H_2D4S([Values(0u)] uint Rd, + public void Uqxtn_V_8H16B_4S8H_2D4S([Values(0u)] uint Rd, [Values(1u, 0u)] uint Rn, [ValueSource("_4H2S1D_")] [Random(RndCnt)] ulong Z, [ValueSource("_4H2S1D_")] [Random(RndCnt)] ulong A, @@ -1362,12 +1676,15 @@ namespace Ryujinx.Tests.Cpu Opcode |= ((size & 3) << 22); Bits Op = new Bits(Opcode); + int Fpsr = (int)TestContext.CurrentContext.Random.NextUInt(); + Vector128<float> V0 = MakeVectorE0E1(Z, Z); Vector128<float> V1 = MakeVectorE0E1(A, A); - AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1); + AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, Fpsr: Fpsr); AArch64.Vpart(0, 0, new Bits(Z)); AArch64.Vpart(0, 1, new Bits(Z)); AArch64.Vpart(1, 0, new Bits(A)); AArch64.Vpart(1, 1, new Bits(A)); + Shared.FPSR = new Bits((uint)Fpsr); SimdFp.Uqxtn_V(Op[30], Op[23, 22], Op[9, 5], Op[4, 0]); Assert.Multiple(() => @@ -1375,11 +1692,104 @@ namespace Ryujinx.Tests.Cpu Assert.That(GetVectorE0(ThreadState.V0), Is.EqualTo(AArch64.Vpart(64, 0, 0).ToUInt64())); Assert.That(GetVectorE1(ThreadState.V0), Is.EqualTo(AArch64.Vpart(64, 0, 1).ToUInt64())); }); - Assert.That(((ThreadState.Fpsr >> 27) & 1) != 0, Is.EqualTo(Shared.FPSR[27])); + Assert.That(ThreadState.Fpsr, Is.EqualTo((int)Shared.FPSR.ToUInt32())); + } + + [Test, Description("USQADD <V><d>, <V><n>")] + public void Usqadd_S_B_H_S_D([Values(0u)] uint Rd, + [Values(1u, 0u)] uint Rn, + [ValueSource("_1B1H1S1D_")] [Random(RndCnt)] ulong Z, + [ValueSource("_1B1H1S1D_")] [Random(RndCnt)] ulong A, + [Values(0b00u, 0b01u, 0b10u, 0b11u)] uint size) // <B, H, S, D> + { + uint Opcode = 0x7E203800; // USQADD B0, B0 + Opcode |= ((Rn & 31) << 5) | ((Rd & 31) << 0); + Opcode |= ((size & 3) << 22); + Bits Op = new Bits(Opcode); + + int Fpsr = (int)TestContext.CurrentContext.Random.NextUInt(); + + Vector128<float> V0 = MakeVectorE0E1(Z, Z); + Vector128<float> V1 = MakeVectorE0(A); + AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, Fpsr: Fpsr); + + AArch64.Vpart(0, 0, new Bits(Z)); AArch64.Vpart(0, 1, new Bits(Z)); + AArch64.V(1, new Bits(A)); + Shared.FPSR = new Bits((uint)Fpsr); + SimdFp.Usqadd_S(Op[23, 22], Op[9, 5], Op[4, 0]); + + Assert.Multiple(() => + { + Assert.That(GetVectorE0(ThreadState.V0), Is.EqualTo(AArch64.Vpart(64, 0, 0).ToUInt64())); + Assert.That(GetVectorE1(ThreadState.V0), Is.EqualTo(AArch64.Vpart(64, 0, 1).ToUInt64())); + }); + Assert.That(ThreadState.Fpsr, Is.EqualTo((int)Shared.FPSR.ToUInt32())); + } + + [Test, Description("USQADD <Vd>.<T>, <Vn>.<T>")] + public void Usqadd_V_8B_4H_2S([Values(0u)] uint Rd, + [Values(1u, 0u)] uint Rn, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong Z, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong A, + [Values(0b00u, 0b01u, 0b10u)] uint size) // <8B, 4H, 2S> + { + uint Opcode = 0x2E203800; // USQADD V0.8B, V0.8B + Opcode |= ((Rn & 31) << 5) | ((Rd & 31) << 0); + Opcode |= ((size & 3) << 22); + Bits Op = new Bits(Opcode); + + int Fpsr = (int)TestContext.CurrentContext.Random.NextUInt(); + + Vector128<float> V0 = MakeVectorE0E1(Z, Z); + Vector128<float> V1 = MakeVectorE0(A); + AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, Fpsr: Fpsr); + + AArch64.Vpart(0, 0, new Bits(Z)); AArch64.Vpart(0, 1, new Bits(Z)); + AArch64.V(1, new Bits(A)); + Shared.FPSR = new Bits((uint)Fpsr); + SimdFp.Usqadd_V(Op[30], Op[23, 22], Op[9, 5], Op[4, 0]); + + Assert.Multiple(() => + { + Assert.That(GetVectorE0(ThreadState.V0), Is.EqualTo(AArch64.Vpart(64, 0, 0).ToUInt64())); + Assert.That(GetVectorE1(ThreadState.V0), Is.EqualTo(AArch64.Vpart(64, 0, 1).ToUInt64())); + }); + Assert.That(ThreadState.Fpsr, Is.EqualTo((int)Shared.FPSR.ToUInt32())); + } + + [Test, Description("USQADD <Vd>.<T>, <Vn>.<T>")] + public void Usqadd_V_16B_8H_4S_2D([Values(0u)] uint Rd, + [Values(1u, 0u)] uint Rn, + [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong Z, + [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong A, + [Values(0b00u, 0b01u, 0b10u, 0b11u)] uint size) // <16B, 8H, 4S, 2D> + { + uint Opcode = 0x6E203800; // USQADD V0.16B, V0.16B + Opcode |= ((Rn & 31) << 5) | ((Rd & 31) << 0); + Opcode |= ((size & 3) << 22); + Bits Op = new Bits(Opcode); + + int Fpsr = (int)TestContext.CurrentContext.Random.NextUInt(); + + Vector128<float> V0 = MakeVectorE0E1(Z, Z); + Vector128<float> V1 = MakeVectorE0E1(A, A); + AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, Fpsr: Fpsr); + + AArch64.Vpart(0, 0, new Bits(Z)); AArch64.Vpart(0, 1, new Bits(Z)); + AArch64.Vpart(1, 0, new Bits(A)); AArch64.Vpart(1, 1, new Bits(A)); + Shared.FPSR = new Bits((uint)Fpsr); + SimdFp.Usqadd_V(Op[30], Op[23, 22], Op[9, 5], Op[4, 0]); + + Assert.Multiple(() => + { + Assert.That(GetVectorE0(ThreadState.V0), Is.EqualTo(AArch64.Vpart(64, 0, 0).ToUInt64())); + Assert.That(GetVectorE1(ThreadState.V0), Is.EqualTo(AArch64.Vpart(64, 0, 1).ToUInt64())); + }); + Assert.That(ThreadState.Fpsr, Is.EqualTo((int)Shared.FPSR.ToUInt32())); } [Test, Description("XTN{2} <Vd>.<Tb>, <Vn>.<Ta>")] - public void Xtn_V_8H8B_4S4H_2D2S([Values(0u)] uint Rd, + public void Xtn_V_8H8B_4S4H_2D2S([Values(0u)] uint Rd, [Values(1u, 0u)] uint Rn, [ValueSource("_4H2S1D_")] [Random(RndCnt)] ulong Z, [ValueSource("_4H2S1D_")] [Random(RndCnt)] ulong A, @@ -1406,7 +1816,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Description("XTN{2} <Vd>.<Tb>, <Vn>.<Ta>")] - public void Xtn_V_8H16B_4S8H_2D4S([Values(0u)] uint Rd, + public void Xtn_V_8H16B_4S8H_2D4S([Values(0u)] uint Rd, [Values(1u, 0u)] uint Rn, [ValueSource("_4H2S1D_")] [Random(RndCnt)] ulong Z, [ValueSource("_4H2S1D_")] [Random(RndCnt)] ulong A, diff --git a/Ryujinx.Tests/Cpu/CpuTestSimdArithmetic.cs b/Ryujinx.Tests/Cpu/CpuTestSimdArithmetic.cs index 8e2d9a36..53e15542 100644 --- a/Ryujinx.Tests/Cpu/CpuTestSimdArithmetic.cs +++ b/Ryujinx.Tests/Cpu/CpuTestSimdArithmetic.cs @@ -19,9 +19,9 @@ namespace Ryujinx.Tests.Cpu [TestCase(0x1E224820u, 0x000000007F7FFFFFul, 0x00000000807FFFFFul, 0x000000007F7FFFFFul)] [TestCase(0x1E224820u, 0x000000007FC00000ul, 0x000000003F800000ul, 0x000000007FC00000ul)] [TestCase(0x1E224820u, 0x000000003F800000ul, 0x000000007FC00000ul, 0x000000007FC00000ul)] - [TestCase(0x1E224820u, 0x000000007F800001ul, 0x000000007FC00042ul, 0x000000007FC00001ul, Ignore = "NaN test.")] - [TestCase(0x1E224820u, 0x000000007FC00042ul, 0x000000007F800001ul, 0x000000007FC00001ul, Ignore = "NaN test.")] - [TestCase(0x1E224820u, 0x000000007FC0000Aul, 0x000000007FC0000Bul, 0x000000007FC0000Aul, Ignore = "NaN test.")] + [TestCase(0x1E224820u, 0x000000007F800001ul, 0x000000007FC00042ul, 0x000000007FC00001ul)] + [TestCase(0x1E224820u, 0x000000007FC00042ul, 0x000000007F800001ul, 0x000000007FC00001ul)] + [TestCase(0x1E224820u, 0x000000007FC0000Aul, 0x000000007FC0000Bul, 0x000000007FC0000Aul)] [TestCase(0x1E624820u, 0x0000000000000000ul, 0x8000000000000000ul, 0x0000000000000000ul)] [TestCase(0x1E624820u, 0x8000000000000000ul, 0x0000000000000000ul, 0x0000000000000000ul)] [TestCase(0x1E624820u, 0x8000000000000000ul, 0x8000000000000000ul, 0x8000000000000000ul)] @@ -45,9 +45,9 @@ namespace Ryujinx.Tests.Cpu [TestCase(0x7F7FFFFFu, 0x7F7FFFFFu, 0x807FFFFFu, 0x807FFFFFu, 0x7F7FFFFFu, 0x7F7FFFFFu)] [TestCase(0x7FC00000u, 0x7FC00000u, 0x3F800000u, 0x3F800000u, 0x7FC00000u, 0x7FC00000u)] [TestCase(0x3F800000u, 0x3F800000u, 0x7FC00000u, 0x7FC00000u, 0x7FC00000u, 0x7FC00000u)] - [TestCase(0x7F800001u, 0x7F800001u, 0x7FC00042u, 0x7FC00042u, 0x7FC00001u, 0x7FC00001u, Ignore = "NaN test.")] - [TestCase(0x7FC00042u, 0x7FC00042u, 0x7F800001u, 0x7F800001u, 0x7FC00001u, 0x7FC00001u, Ignore = "NaN test.")] - [TestCase(0x7FC0000Au, 0x7FC0000Au, 0x7FC0000Bu, 0x7FC0000Bu, 0x7FC0000Au, 0x7FC0000Au, Ignore = "NaN test.")] + [TestCase(0x7F800001u, 0x7F800001u, 0x7FC00042u, 0x7FC00042u, 0x7FC00001u, 0x7FC00001u)] + [TestCase(0x7FC00042u, 0x7FC00042u, 0x7F800001u, 0x7F800001u, 0x7FC00001u, 0x7FC00001u)] + [TestCase(0x7FC0000Au, 0x7FC0000Au, 0x7FC0000Bu, 0x7FC0000Bu, 0x7FC0000Au, 0x7FC0000Au)] public void Fmax_V(uint A, uint B, uint C, uint D, uint Result0, uint Result1) { uint Opcode = 0x4E22F420; @@ -71,9 +71,9 @@ namespace Ryujinx.Tests.Cpu [TestCase(0x1E225820u, 0x000000007F7FFFFFul, 0x00000000807FFFFFul, 0x00000000807FFFFFul)] [TestCase(0x1E225820u, 0x000000007FC00000ul, 0x000000003F800000ul, 0x000000007FC00000ul)] [TestCase(0x1E225820u, 0x000000003F800000ul, 0x000000007FC00000ul, 0x000000007FC00000ul)] - [TestCase(0x1E225820u, 0x000000007F800001ul, 0x000000007FC00042ul, 0x000000007FC00001ul, Ignore = "NaN test.")] - [TestCase(0x1E225820u, 0x000000007FC00042ul, 0x000000007F800001ul, 0x000000007FC00001ul, Ignore = "NaN test.")] - [TestCase(0x1E225820u, 0x000000007FC0000Aul, 0x000000007FC0000Bul, 0x000000007FC0000Aul, Ignore = "NaN test.")] + [TestCase(0x1E225820u, 0x000000007F800001ul, 0x000000007FC00042ul, 0x000000007FC00001ul)] + [TestCase(0x1E225820u, 0x000000007FC00042ul, 0x000000007F800001ul, 0x000000007FC00001ul)] + [TestCase(0x1E225820u, 0x000000007FC0000Aul, 0x000000007FC0000Bul, 0x000000007FC0000Aul)] [TestCase(0x1E625820u, 0x0000000000000000ul, 0x8000000000000000ul, 0x8000000000000000ul)] [TestCase(0x1E625820u, 0x8000000000000000ul, 0x0000000000000000ul, 0x8000000000000000ul)] [TestCase(0x1E625820u, 0x8000000000000000ul, 0x8000000000000000ul, 0x8000000000000000ul)] @@ -97,9 +97,9 @@ namespace Ryujinx.Tests.Cpu [TestCase(0x7F7FFFFFu, 0x7F7FFFFFu, 0x807FFFFFu, 0x807FFFFFu, 0x807FFFFFu, 0x807FFFFFu)] [TestCase(0x7FC00000u, 0x7FC00000u, 0x3F800000u, 0x3F800000u, 0x7FC00000u, 0x7FC00000u)] [TestCase(0x3F800000u, 0x3F800000u, 0x7FC00000u, 0x7FC00000u, 0x7FC00000u, 0x7FC00000u)] - [TestCase(0x7F800001u, 0x7F800001u, 0x7FC00042u, 0x7FC00042u, 0x7FC00001u, 0x7FC00001u, Ignore = "NaN test.")] - [TestCase(0x7FC00042u, 0x7FC00042u, 0x7F800001u, 0x7F800001u, 0x7FC00001u, 0x7FC00001u, Ignore = "NaN test.")] - [TestCase(0x7FC0000Au, 0x7FC0000Au, 0x7FC0000Bu, 0x7FC0000Bu, 0x7FC0000Au, 0x7FC0000Au, Ignore = "NaN test.")] + [TestCase(0x7F800001u, 0x7F800001u, 0x7FC00042u, 0x7FC00042u, 0x7FC00001u, 0x7FC00001u)] + [TestCase(0x7FC00042u, 0x7FC00042u, 0x7F800001u, 0x7F800001u, 0x7FC00001u, 0x7FC00001u)] + [TestCase(0x7FC0000Au, 0x7FC0000Au, 0x7FC0000Bu, 0x7FC0000Bu, 0x7FC0000Au, 0x7FC0000Au)] public void Fmin_V(uint A, uint B, uint C, uint D, uint Result0, uint Result1) { uint Opcode = 0x4EA2F420; diff --git a/Ryujinx.Tests/Cpu/CpuTestSimdReg.cs b/Ryujinx.Tests/Cpu/CpuTestSimdReg.cs index c67348d1..c1cf812e 100644 --- a/Ryujinx.Tests/Cpu/CpuTestSimdReg.cs +++ b/Ryujinx.Tests/Cpu/CpuTestSimdReg.cs @@ -22,12 +22,38 @@ namespace Ryujinx.Tests.Cpu } #region "ValueSource" + private static ulong[] _1B1H1S1D_() + { + return new ulong[] { 0x0000000000000000ul, 0x000000000000007Ful, + 0x0000000000000080ul, 0x00000000000000FFul, + 0x0000000000007FFFul, 0x0000000000008000ul, + 0x000000000000FFFFul, 0x000000007FFFFFFFul, + 0x0000000080000000ul, 0x00000000FFFFFFFFul, + 0x7FFFFFFFFFFFFFFFul, 0x8000000000000000ul, + 0xFFFFFFFFFFFFFFFFul }; + } + private static ulong[] _1D_() { return new ulong[] { 0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul, 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul }; } + private static ulong[] _1H1S_() + { + return new ulong[] { 0x0000000000000000ul, 0x0000000000007FFFul, + 0x0000000000008000ul, 0x000000000000FFFFul, + 0x000000007FFFFFFFul, 0x0000000080000000ul, + 0x00000000FFFFFFFFul }; + } + + private static ulong[] _4H2S_() + { + return new ulong[] { 0x0000000000000000ul, 0x7FFF7FFF7FFF7FFFul, + 0x8000800080008000ul, 0x7FFFFFFF7FFFFFFFul, + 0x8000000080000000ul, 0xFFFFFFFFFFFFFFFFul }; + } + private static ulong[] _4H2S1D_() { return new ulong[] { 0x0000000000000000ul, 0x7FFF7FFF7FFF7FFFul, @@ -63,7 +89,7 @@ namespace Ryujinx.Tests.Cpu private const int RndCnt = 4; [Test, Pairwise, Description("ADD <V><d>, <V><n>, <V><m>")] - public void Add_S_D([Values(0u)] uint Rd, + public void Add_S_D([Values(0u)] uint Rd, [Values(1u, 0u)] uint Rn, [Values(2u, 0u)] uint Rm, [ValueSource("_1D_")] [Random(RndCnt)] ulong Z, @@ -92,7 +118,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise, Description("ADD <Vd>.<T>, <Vn>.<T>, <Vm>.<T>")] - public void Add_V_8B_4H_2S([Values(0u)] uint Rd, + public void Add_V_8B_4H_2S([Values(0u)] uint Rd, [Values(1u, 0u)] uint Rn, [Values(2u, 0u)] uint Rm, [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong Z, @@ -123,7 +149,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise, Description("ADD <Vd>.<T>, <Vn>.<T>, <Vm>.<T>")] - public void Add_V_16B_8H_4S_2D([Values(0u)] uint Rd, + public void Add_V_16B_8H_4S_2D([Values(0u)] uint Rd, [Values(1u, 0u)] uint Rn, [Values(2u, 0u)] uint Rm, [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong Z, @@ -154,7 +180,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise, Description("ADDHN{2} <Vd>.<Tb>, <Vn>.<Ta>, <Vm>.<Ta>")] - public void Addhn_V_8H8B_4S4H_2D2S([Values(0u)] uint Rd, + public void Addhn_V_8H8B_4S4H_2D2S([Values(0u)] uint Rd, [Values(1u, 0u)] uint Rn, [Values(2u, 0u)] uint Rm, [ValueSource("_4H2S1D_")] [Random(RndCnt)] ulong Z, @@ -185,7 +211,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise, Description("ADDHN{2} <Vd>.<Tb>, <Vn>.<Ta>, <Vm>.<Ta>")] - public void Addhn_V_8H16B_4S8H_2D4S([Values(0u)] uint Rd, + public void Addhn_V_8H16B_4S8H_2D4S([Values(0u)] uint Rd, [Values(1u, 0u)] uint Rn, [Values(2u, 0u)] uint Rm, [ValueSource("_4H2S1D_")] [Random(RndCnt)] ulong Z, @@ -216,7 +242,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise, Description("ADDP <Vd>.<T>, <Vn>.<T>, <Vm>.<T>")] - public void Addp_V_8B_4H_2S([Values(0u)] uint Rd, + public void Addp_V_8B_4H_2S([Values(0u)] uint Rd, [Values(1u, 0u)] uint Rn, [Values(2u, 0u)] uint Rm, [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong Z, @@ -247,7 +273,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise, Description("ADDP <Vd>.<T>, <Vn>.<T>, <Vm>.<T>")] - public void Addp_V_16B_8H_4S_2D([Values(0u)] uint Rd, + public void Addp_V_16B_8H_4S_2D([Values(0u)] uint Rd, [Values(1u, 0u)] uint Rn, [Values(2u, 0u)] uint Rm, [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong Z, @@ -278,7 +304,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise, Description("AND <Vd>.<T>, <Vn>.<T>, <Vm>.<T>")] - public void And_V_8B([Values(0u)] uint Rd, + public void And_V_8B([Values(0u)] uint Rd, [Values(1u, 0u)] uint Rn, [Values(2u, 0u)] uint Rm, [ValueSource("_8B_")] [Random(RndCnt)] ulong Z, @@ -307,7 +333,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise, Description("AND <Vd>.<T>, <Vn>.<T>, <Vm>.<T>")] - public void And_V_16B([Values(0u)] uint Rd, + public void And_V_16B([Values(0u)] uint Rd, [Values(1u, 0u)] uint Rn, [Values(2u, 0u)] uint Rm, [ValueSource("_8B_")] [Random(RndCnt)] ulong Z, @@ -336,7 +362,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise, Description("BIC <Vd>.<T>, <Vn>.<T>, <Vm>.<T>")] - public void Bic_V_8B([Values(0u)] uint Rd, + public void Bic_V_8B([Values(0u)] uint Rd, [Values(1u, 0u)] uint Rn, [Values(2u, 0u)] uint Rm, [ValueSource("_8B_")] [Random(RndCnt)] ulong Z, @@ -365,7 +391,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise, Description("BIC <Vd>.<T>, <Vn>.<T>, <Vm>.<T>")] - public void Bic_V_16B([Values(0u)] uint Rd, + public void Bic_V_16B([Values(0u)] uint Rd, [Values(1u, 0u)] uint Rn, [Values(2u, 0u)] uint Rm, [ValueSource("_8B_")] [Random(RndCnt)] ulong Z, @@ -394,7 +420,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise, Description("BIF <Vd>.<T>, <Vn>.<T>, <Vm>.<T>")] - public void Bif_V_8B([Values(0u)] uint Rd, + public void Bif_V_8B([Values(0u)] uint Rd, [Values(1u, 0u)] uint Rn, [Values(2u, 0u)] uint Rm, [ValueSource("_8B_")] [Random(RndCnt)] ulong Z, @@ -423,7 +449,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise, Description("BIF <Vd>.<T>, <Vn>.<T>, <Vm>.<T>")] - public void Bif_V_16B([Values(0u)] uint Rd, + public void Bif_V_16B([Values(0u)] uint Rd, [Values(1u, 0u)] uint Rn, [Values(2u, 0u)] uint Rm, [ValueSource("_8B_")] [Random(RndCnt)] ulong Z, @@ -452,7 +478,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise, Description("BIT <Vd>.<T>, <Vn>.<T>, <Vm>.<T>")] - public void Bit_V_8B([Values(0u)] uint Rd, + public void Bit_V_8B([Values(0u)] uint Rd, [Values(1u, 0u)] uint Rn, [Values(2u, 0u)] uint Rm, [ValueSource("_8B_")] [Random(RndCnt)] ulong Z, @@ -481,7 +507,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise, Description("BIT <Vd>.<T>, <Vn>.<T>, <Vm>.<T>")] - public void Bit_V_16B([Values(0u)] uint Rd, + public void Bit_V_16B([Values(0u)] uint Rd, [Values(1u, 0u)] uint Rn, [Values(2u, 0u)] uint Rm, [ValueSource("_8B_")] [Random(RndCnt)] ulong Z, @@ -510,7 +536,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise, Description("BSL <Vd>.<T>, <Vn>.<T>, <Vm>.<T>")] - public void Bsl_V_8B([Values(0u)] uint Rd, + public void Bsl_V_8B([Values(0u)] uint Rd, [Values(1u, 0u)] uint Rn, [Values(2u, 0u)] uint Rm, [ValueSource("_8B_")] [Random(RndCnt)] ulong Z, @@ -539,7 +565,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise, Description("BSL <Vd>.<T>, <Vn>.<T>, <Vm>.<T>")] - public void Bsl_V_16B([Values(0u)] uint Rd, + public void Bsl_V_16B([Values(0u)] uint Rd, [Values(1u, 0u)] uint Rn, [Values(2u, 0u)] uint Rm, [ValueSource("_8B_")] [Random(RndCnt)] ulong Z, @@ -568,7 +594,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise, Description("CMEQ <V><d>, <V><n>, <V><m>")] - public void Cmeq_S_D([Values(0u)] uint Rd, + public void Cmeq_S_D([Values(0u)] uint Rd, [Values(1u, 0u)] uint Rn, [Values(2u, 0u)] uint Rm, [ValueSource("_1D_")] [Random(RndCnt)] ulong Z, @@ -597,7 +623,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise, Description("CMEQ <Vd>.<T>, <Vn>.<T>, <Vm>.<T>")] - public void Cmeq_V_8B_4H_2S([Values(0u)] uint Rd, + public void Cmeq_V_8B_4H_2S([Values(0u)] uint Rd, [Values(1u, 0u)] uint Rn, [Values(2u, 0u)] uint Rm, [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong Z, @@ -628,7 +654,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise, Description("CMEQ <Vd>.<T>, <Vn>.<T>, <Vm>.<T>")] - public void Cmeq_V_16B_8H_4S_2D([Values(0u)] uint Rd, + public void Cmeq_V_16B_8H_4S_2D([Values(0u)] uint Rd, [Values(1u, 0u)] uint Rn, [Values(2u, 0u)] uint Rm, [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong Z, @@ -659,7 +685,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise, Description("CMGE <V><d>, <V><n>, <V><m>")] - public void Cmge_S_D([Values(0u)] uint Rd, + public void Cmge_S_D([Values(0u)] uint Rd, [Values(1u, 0u)] uint Rn, [Values(2u, 0u)] uint Rm, [ValueSource("_1D_")] [Random(RndCnt)] ulong Z, @@ -688,7 +714,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise, Description("CMGE <Vd>.<T>, <Vn>.<T>, <Vm>.<T>")] - public void Cmge_V_8B_4H_2S([Values(0u)] uint Rd, + public void Cmge_V_8B_4H_2S([Values(0u)] uint Rd, [Values(1u, 0u)] uint Rn, [Values(2u, 0u)] uint Rm, [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong Z, @@ -719,7 +745,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise, Description("CMGE <Vd>.<T>, <Vn>.<T>, <Vm>.<T>")] - public void Cmge_V_16B_8H_4S_2D([Values(0u)] uint Rd, + public void Cmge_V_16B_8H_4S_2D([Values(0u)] uint Rd, [Values(1u, 0u)] uint Rn, [Values(2u, 0u)] uint Rm, [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong Z, @@ -750,7 +776,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise, Description("CMGT <V><d>, <V><n>, <V><m>")] - public void Cmgt_S_D([Values(0u)] uint Rd, + public void Cmgt_S_D([Values(0u)] uint Rd, [Values(1u, 0u)] uint Rn, [Values(2u, 0u)] uint Rm, [ValueSource("_1D_")] [Random(RndCnt)] ulong Z, @@ -779,7 +805,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise, Description("CMGT <Vd>.<T>, <Vn>.<T>, <Vm>.<T>")] - public void Cmgt_V_8B_4H_2S([Values(0u)] uint Rd, + public void Cmgt_V_8B_4H_2S([Values(0u)] uint Rd, [Values(1u, 0u)] uint Rn, [Values(2u, 0u)] uint Rm, [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong Z, @@ -810,7 +836,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise, Description("CMGT <Vd>.<T>, <Vn>.<T>, <Vm>.<T>")] - public void Cmgt_V_16B_8H_4S_2D([Values(0u)] uint Rd, + public void Cmgt_V_16B_8H_4S_2D([Values(0u)] uint Rd, [Values(1u, 0u)] uint Rn, [Values(2u, 0u)] uint Rm, [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong Z, @@ -841,7 +867,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise, Description("CMHI <V><d>, <V><n>, <V><m>")] - public void Cmhi_S_D([Values(0u)] uint Rd, + public void Cmhi_S_D([Values(0u)] uint Rd, [Values(1u, 0u)] uint Rn, [Values(2u, 0u)] uint Rm, [ValueSource("_1D_")] [Random(RndCnt)] ulong Z, @@ -870,7 +896,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise, Description("CMHI <Vd>.<T>, <Vn>.<T>, <Vm>.<T>")] - public void Cmhi_V_8B_4H_2S([Values(0u)] uint Rd, + public void Cmhi_V_8B_4H_2S([Values(0u)] uint Rd, [Values(1u, 0u)] uint Rn, [Values(2u, 0u)] uint Rm, [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong Z, @@ -901,7 +927,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise, Description("CMHI <Vd>.<T>, <Vn>.<T>, <Vm>.<T>")] - public void Cmhi_V_16B_8H_4S_2D([Values(0u)] uint Rd, + public void Cmhi_V_16B_8H_4S_2D([Values(0u)] uint Rd, [Values(1u, 0u)] uint Rn, [Values(2u, 0u)] uint Rm, [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong Z, @@ -932,7 +958,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise, Description("CMHS <V><d>, <V><n>, <V><m>")] - public void Cmhs_S_D([Values(0u)] uint Rd, + public void Cmhs_S_D([Values(0u)] uint Rd, [Values(1u, 0u)] uint Rn, [Values(2u, 0u)] uint Rm, [ValueSource("_1D_")] [Random(RndCnt)] ulong Z, @@ -961,7 +987,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise, Description("CMHS <Vd>.<T>, <Vn>.<T>, <Vm>.<T>")] - public void Cmhs_V_8B_4H_2S([Values(0u)] uint Rd, + public void Cmhs_V_8B_4H_2S([Values(0u)] uint Rd, [Values(1u, 0u)] uint Rn, [Values(2u, 0u)] uint Rm, [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong Z, @@ -992,7 +1018,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise, Description("CMHS <Vd>.<T>, <Vn>.<T>, <Vm>.<T>")] - public void Cmhs_V_16B_8H_4S_2D([Values(0u)] uint Rd, + public void Cmhs_V_16B_8H_4S_2D([Values(0u)] uint Rd, [Values(1u, 0u)] uint Rn, [Values(2u, 0u)] uint Rm, [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong Z, @@ -1023,7 +1049,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise, Description("CMTST <V><d>, <V><n>, <V><m>")] - public void Cmtst_S_D([Values(0u)] uint Rd, + public void Cmtst_S_D([Values(0u)] uint Rd, [Values(1u, 0u)] uint Rn, [Values(2u, 0u)] uint Rm, [ValueSource("_1D_")] [Random(RndCnt)] ulong Z, @@ -1052,7 +1078,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise, Description("CMTST <Vd>.<T>, <Vn>.<T>, <Vm>.<T>")] - public void Cmtst_V_8B_4H_2S([Values(0u)] uint Rd, + public void Cmtst_V_8B_4H_2S([Values(0u)] uint Rd, [Values(1u, 0u)] uint Rn, [Values(2u, 0u)] uint Rm, [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong Z, @@ -1083,7 +1109,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise, Description("CMTST <Vd>.<T>, <Vn>.<T>, <Vm>.<T>")] - public void Cmtst_V_16B_8H_4S_2D([Values(0u)] uint Rd, + public void Cmtst_V_16B_8H_4S_2D([Values(0u)] uint Rd, [Values(1u, 0u)] uint Rn, [Values(2u, 0u)] uint Rm, [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong Z, @@ -1114,7 +1140,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise, Description("EOR <Vd>.<T>, <Vn>.<T>, <Vm>.<T>")] - public void Eor_V_8B([Values(0u)] uint Rd, + public void Eor_V_8B([Values(0u)] uint Rd, [Values(1u, 0u)] uint Rn, [Values(2u, 0u)] uint Rm, [ValueSource("_8B_")] [Random(RndCnt)] ulong Z, @@ -1143,7 +1169,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise, Description("EOR <Vd>.<T>, <Vn>.<T>, <Vm>.<T>")] - public void Eor_V_16B([Values(0u)] uint Rd, + public void Eor_V_16B([Values(0u)] uint Rd, [Values(1u, 0u)] uint Rn, [Values(2u, 0u)] uint Rm, [ValueSource("_8B_")] [Random(RndCnt)] ulong Z, @@ -1172,7 +1198,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise, Description("ORN <Vd>.<T>, <Vn>.<T>, <Vm>.<T>")] - public void Orn_V_8B([Values(0u)] uint Rd, + public void Orn_V_8B([Values(0u)] uint Rd, [Values(1u, 0u)] uint Rn, [Values(2u, 0u)] uint Rm, [ValueSource("_8B_")] [Random(RndCnt)] ulong Z, @@ -1201,7 +1227,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise, Description("ORN <Vd>.<T>, <Vn>.<T>, <Vm>.<T>")] - public void Orn_V_16B([Values(0u)] uint Rd, + public void Orn_V_16B([Values(0u)] uint Rd, [Values(1u, 0u)] uint Rn, [Values(2u, 0u)] uint Rm, [ValueSource("_8B_")] [Random(RndCnt)] ulong Z, @@ -1230,7 +1256,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise, Description("ORR <Vd>.<T>, <Vn>.<T>, <Vm>.<T>")] - public void Orr_V_8B([Values(0u)] uint Rd, + public void Orr_V_8B([Values(0u)] uint Rd, [Values(1u, 0u)] uint Rn, [Values(2u, 0u)] uint Rm, [ValueSource("_8B_")] [Random(RndCnt)] ulong Z, @@ -1259,7 +1285,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise, Description("ORR <Vd>.<T>, <Vn>.<T>, <Vm>.<T>")] - public void Orr_V_16B([Values(0u)] uint Rd, + public void Orr_V_16B([Values(0u)] uint Rd, [Values(1u, 0u)] uint Rn, [Values(2u, 0u)] uint Rm, [ValueSource("_8B_")] [Random(RndCnt)] ulong Z, @@ -1288,7 +1314,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise, Description("RADDHN{2} <Vd>.<Tb>, <Vn>.<Ta>, <Vm>.<Ta>")] - public void Raddhn_V_8H8B_4S4H_2D2S([Values(0u)] uint Rd, + public void Raddhn_V_8H8B_4S4H_2D2S([Values(0u)] uint Rd, [Values(1u, 0u)] uint Rn, [Values(2u, 0u)] uint Rm, [ValueSource("_4H2S1D_")] [Random(RndCnt)] ulong Z, @@ -1319,7 +1345,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise, Description("RADDHN{2} <Vd>.<Tb>, <Vn>.<Ta>, <Vm>.<Ta>")] - public void Raddhn_V_8H16B_4S8H_2D4S([Values(0u)] uint Rd, + public void Raddhn_V_8H16B_4S8H_2D4S([Values(0u)] uint Rd, [Values(1u, 0u)] uint Rn, [Values(2u, 0u)] uint Rm, [ValueSource("_4H2S1D_")] [Random(RndCnt)] ulong Z, @@ -1350,7 +1376,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise, Description("RSUBHN{2} <Vd>.<Tb>, <Vn>.<Ta>, <Vm>.<Ta>")] - public void Rsubhn_V_8H8B_4S4H_2D2S([Values(0u)] uint Rd, + public void Rsubhn_V_8H8B_4S4H_2D2S([Values(0u)] uint Rd, [Values(1u, 0u)] uint Rn, [Values(2u, 0u)] uint Rm, [ValueSource("_4H2S1D_")] [Random(RndCnt)] ulong Z, @@ -1381,7 +1407,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise, Description("RSUBHN{2} <Vd>.<Tb>, <Vn>.<Ta>, <Vm>.<Ta>")] - public void Rsubhn_V_8H16B_4S8H_2D4S([Values(0u)] uint Rd, + public void Rsubhn_V_8H16B_4S8H_2D4S([Values(0u)] uint Rd, [Values(1u, 0u)] uint Rn, [Values(2u, 0u)] uint Rm, [ValueSource("_4H2S1D_")] [Random(RndCnt)] ulong Z, @@ -1412,7 +1438,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise, Description("SABA <Vd>.<T>, <Vn>.<T>, <Vm>.<T>")] - public void Saba_V_8B_4H_2S([Values(0u)] uint Rd, + public void Saba_V_8B_4H_2S([Values(0u)] uint Rd, [Values(1u, 0u)] uint Rn, [Values(2u, 0u)] uint Rm, [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong Z, @@ -1443,7 +1469,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise, Description("SABA <Vd>.<T>, <Vn>.<T>, <Vm>.<T>")] - public void Saba_V_16B_8H_4S([Values(0u)] uint Rd, + public void Saba_V_16B_8H_4S([Values(0u)] uint Rd, [Values(1u, 0u)] uint Rn, [Values(2u, 0u)] uint Rm, [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong Z, @@ -1474,7 +1500,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise, Description("SABAL{2} <Vd>.<Ta>, <Vn>.<Tb>, <Vm>.<Tb>")] - public void Sabal_V_8B8H_4H4S_2S2D([Values(0u)] uint Rd, + public void Sabal_V_8B8H_4H4S_2S2D([Values(0u)] uint Rd, [Values(1u, 0u)] uint Rn, [Values(2u, 0u)] uint Rm, [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong Z, @@ -1505,7 +1531,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise, Description("SABAL{2} <Vd>.<Ta>, <Vn>.<Tb>, <Vm>.<Tb>")] - public void Sabal_V_16B8H_8H4S_4S2D([Values(0u)] uint Rd, + public void Sabal_V_16B8H_8H4S_4S2D([Values(0u)] uint Rd, [Values(1u, 0u)] uint Rn, [Values(2u, 0u)] uint Rm, [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong Z, @@ -1536,7 +1562,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise, Description("SABD <Vd>.<T>, <Vn>.<T>, <Vm>.<T>")] - public void Sabd_V_8B_4H_2S([Values(0u)] uint Rd, + public void Sabd_V_8B_4H_2S([Values(0u)] uint Rd, [Values(1u, 0u)] uint Rn, [Values(2u, 0u)] uint Rm, [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong Z, @@ -1567,7 +1593,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise, Description("SABD <Vd>.<T>, <Vn>.<T>, <Vm>.<T>")] - public void Sabd_V_16B_8H_4S([Values(0u)] uint Rd, + public void Sabd_V_16B_8H_4S([Values(0u)] uint Rd, [Values(1u, 0u)] uint Rn, [Values(2u, 0u)] uint Rm, [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong Z, @@ -1598,7 +1624,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise, Description("SABDL{2} <Vd>.<Ta>, <Vn>.<Tb>, <Vm>.<Tb>")] - public void Sabdl_V_8B8H_4H4S_2S2D([Values(0u)] uint Rd, + public void Sabdl_V_8B8H_4H4S_2S2D([Values(0u)] uint Rd, [Values(1u, 0u)] uint Rn, [Values(2u, 0u)] uint Rm, [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong Z, @@ -1629,7 +1655,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise, Description("SABDL{2} <Vd>.<Ta>, <Vn>.<Tb>, <Vm>.<Tb>")] - public void Sabdl_V_16B8H_8H4S_4S2D([Values(0u)] uint Rd, + public void Sabdl_V_16B8H_8H4S_4S2D([Values(0u)] uint Rd, [Values(1u, 0u)] uint Rn, [Values(2u, 0u)] uint Rm, [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong Z, @@ -1660,7 +1686,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise, Description("SADDW{2} <Vd>.<Ta>, <Vn>.<Ta>, <Vm>.<Tb>")] - public void Saddw_V_8B8H8H_4H4S4S_2S2D2D([Values(0u)] uint Rd, + public void Saddw_V_8B8H8H_4H4S4S_2S2D2D([Values(0u)] uint Rd, [Values(1u, 0u)] uint Rn, [Values(2u, 0u)] uint Rm, [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong Z, @@ -1691,7 +1717,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise, Description("SADDW{2} <Vd>.<Ta>, <Vn>.<Ta>, <Vm>.<Tb>")] - public void Saddw_V_16B8H8H_8H4S4S_4S2D2D([Values(0u)] uint Rd, + public void Saddw_V_16B8H8H_8H4S4S_4S2D2D([Values(0u)] uint Rd, [Values(1u, 0u)] uint Rn, [Values(2u, 0u)] uint Rm, [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong Z, @@ -1721,8 +1747,428 @@ namespace Ryujinx.Tests.Cpu }); } + [Test, Pairwise, Description("SQADD <V><d>, <V><n>, <V><m>")] + public void Sqadd_S_B_H_S_D([Values(0u)] uint Rd, + [Values(1u, 0u)] uint Rn, + [Values(2u, 0u)] uint Rm, + [ValueSource("_1B1H1S1D_")] [Random(RndCnt)] ulong Z, + [ValueSource("_1B1H1S1D_")] [Random(RndCnt)] ulong A, + [ValueSource("_1B1H1S1D_")] [Random(RndCnt)] ulong B, + [Values(0b00u, 0b01u, 0b10u, 0b11u)] uint size) // <B, H, S, D> + { + uint Opcode = 0x5E200C00; // SQADD B0, B0, B0 + Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); + Opcode |= ((size & 3) << 22); + Bits Op = new Bits(Opcode); + + int Fpsr = (int)TestContext.CurrentContext.Random.NextUInt(); + + Vector128<float> V0 = MakeVectorE0E1(Z, Z); + Vector128<float> V1 = MakeVectorE0(A); + Vector128<float> V2 = MakeVectorE0(B); + AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2, Fpsr: Fpsr); + + AArch64.Vpart(0, 0, new Bits(Z)); AArch64.Vpart(0, 1, new Bits(Z)); + AArch64.V(1, new Bits(A)); + AArch64.V(2, new Bits(B)); + Shared.FPSR = new Bits((uint)Fpsr); + SimdFp.Sqadd_S(Op[23, 22], Op[20, 16], Op[9, 5], Op[4, 0]); + + Assert.Multiple(() => + { + Assert.That(GetVectorE0(ThreadState.V0), Is.EqualTo(AArch64.Vpart(64, 0, 0).ToUInt64())); + Assert.That(GetVectorE1(ThreadState.V0), Is.EqualTo(AArch64.Vpart(64, 0, 1).ToUInt64())); + }); + Assert.That(ThreadState.Fpsr, Is.EqualTo((int)Shared.FPSR.ToUInt32())); + } + + [Test, Pairwise, Description("SQADD <Vd>.<T>, <Vn>.<T>, <Vm>.<T>")] + public void Sqadd_V_8B_4H_2S([Values(0u)] uint Rd, + [Values(1u, 0u)] uint Rn, + [Values(2u, 0u)] uint Rm, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong Z, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong A, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong B, + [Values(0b00u, 0b01u, 0b10u)] uint size) // <8B, 4H, 2S> + { + uint Opcode = 0x0E200C00; // SQADD V0.8B, V0.8B, V0.8B + Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); + Opcode |= ((size & 3) << 22); + Bits Op = new Bits(Opcode); + + int Fpsr = (int)TestContext.CurrentContext.Random.NextUInt(); + + Vector128<float> V0 = MakeVectorE0E1(Z, Z); + Vector128<float> V1 = MakeVectorE0(A); + Vector128<float> V2 = MakeVectorE0(B); + AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2, Fpsr: Fpsr); + + AArch64.Vpart(0, 0, new Bits(Z)); AArch64.Vpart(0, 1, new Bits(Z)); + AArch64.V(1, new Bits(A)); + AArch64.V(2, new Bits(B)); + Shared.FPSR = new Bits((uint)Fpsr); + SimdFp.Sqadd_V(Op[30], Op[23, 22], Op[20, 16], Op[9, 5], Op[4, 0]); + + Assert.Multiple(() => + { + Assert.That(GetVectorE0(ThreadState.V0), Is.EqualTo(AArch64.Vpart(64, 0, 0).ToUInt64())); + Assert.That(GetVectorE1(ThreadState.V0), Is.EqualTo(AArch64.Vpart(64, 0, 1).ToUInt64())); + }); + Assert.That(ThreadState.Fpsr, Is.EqualTo((int)Shared.FPSR.ToUInt32())); + } + + [Test, Pairwise, Description("SQADD <Vd>.<T>, <Vn>.<T>, <Vm>.<T>")] + public void Sqadd_V_16B_8H_4S_2D([Values(0u)] uint Rd, + [Values(1u, 0u)] uint Rn, + [Values(2u, 0u)] uint Rm, + [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong Z, + [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong A, + [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong B, + [Values(0b00u, 0b01u, 0b10u, 0b11u)] uint size) // <16B, 8H, 4S, 2D> + { + uint Opcode = 0x4E200C00; // SQADD V0.16B, V0.16B, V0.16B + Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); + Opcode |= ((size & 3) << 22); + Bits Op = new Bits(Opcode); + + int Fpsr = (int)TestContext.CurrentContext.Random.NextUInt(); + + Vector128<float> V0 = MakeVectorE0E1(Z, Z); + Vector128<float> V1 = MakeVectorE0E1(A, A); + Vector128<float> V2 = MakeVectorE0E1(B, B); + AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2, Fpsr: Fpsr); + + AArch64.Vpart(0, 0, new Bits(Z)); AArch64.Vpart(0, 1, new Bits(Z)); + AArch64.Vpart(1, 0, new Bits(A)); AArch64.Vpart(1, 1, new Bits(A)); + AArch64.Vpart(2, 0, new Bits(B)); AArch64.Vpart(2, 1, new Bits(B)); + Shared.FPSR = new Bits((uint)Fpsr); + SimdFp.Sqadd_V(Op[30], Op[23, 22], Op[20, 16], Op[9, 5], Op[4, 0]); + + Assert.Multiple(() => + { + Assert.That(GetVectorE0(ThreadState.V0), Is.EqualTo(AArch64.Vpart(64, 0, 0).ToUInt64())); + Assert.That(GetVectorE1(ThreadState.V0), Is.EqualTo(AArch64.Vpart(64, 0, 1).ToUInt64())); + }); + Assert.That(ThreadState.Fpsr, Is.EqualTo((int)Shared.FPSR.ToUInt32())); + } + + [Test, Pairwise, Description("SQDMULH <V><d>, <V><n>, <V><m>")] + public void Sqdmulh_S_H_S([Values(0u)] uint Rd, + [Values(1u, 0u)] uint Rn, + [Values(2u, 0u)] uint Rm, + [ValueSource("_1H1S_")] [Random(RndCnt)] ulong Z, + [ValueSource("_1H1S_")] [Random(RndCnt)] ulong A, + [ValueSource("_1H1S_")] [Random(RndCnt)] ulong B, + [Values(0b01u, 0b10u)] uint size) // <H, S> + { + uint Opcode = 0x5E20B400; // SQDMULH B0, B0, B0 (RESERVED) + Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); + Opcode |= ((size & 3) << 22); + Bits Op = new Bits(Opcode); + + int Fpsr = (int)TestContext.CurrentContext.Random.NextUInt(); + + Vector128<float> V0 = MakeVectorE0E1(Z, Z); + Vector128<float> V1 = MakeVectorE0(A); + Vector128<float> V2 = MakeVectorE0(B); + AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2, Fpsr: Fpsr); + + AArch64.Vpart(0, 0, new Bits(Z)); AArch64.Vpart(0, 1, new Bits(Z)); + AArch64.V(1, new Bits(A)); + AArch64.V(2, new Bits(B)); + Shared.FPSR = new Bits((uint)Fpsr); + SimdFp.Sqdmulh_S(Op[23, 22], Op[20, 16], Op[9, 5], Op[4, 0]); + + Assert.Multiple(() => + { + Assert.That(GetVectorE0(ThreadState.V0), Is.EqualTo(AArch64.Vpart(64, 0, 0).ToUInt64())); + Assert.That(GetVectorE1(ThreadState.V0), Is.EqualTo(AArch64.Vpart(64, 0, 1).ToUInt64())); + }); + Assert.That(ThreadState.Fpsr, Is.EqualTo((int)Shared.FPSR.ToUInt32())); + } + + [Test, Pairwise, Description("SQDMULH <Vd>.<T>, <Vn>.<T>, <Vm>.<T>")] + public void Sqdmulh_V_4H_2S([Values(0u)] uint Rd, + [Values(1u, 0u)] uint Rn, + [Values(2u, 0u)] uint Rm, + [ValueSource("_4H2S_")] [Random(RndCnt)] ulong Z, + [ValueSource("_4H2S_")] [Random(RndCnt)] ulong A, + [ValueSource("_4H2S_")] [Random(RndCnt)] ulong B, + [Values(0b01u, 0b10u)] uint size) // <4H, 2S> + { + uint Opcode = 0x0E20B400; // SQDMULH V0.8B, V0.8B, V0.8B (RESERVED) + Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); + Opcode |= ((size & 3) << 22); + Bits Op = new Bits(Opcode); + + int Fpsr = (int)TestContext.CurrentContext.Random.NextUInt(); + + Vector128<float> V0 = MakeVectorE0E1(Z, Z); + Vector128<float> V1 = MakeVectorE0(A); + Vector128<float> V2 = MakeVectorE0(B); + AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2, Fpsr: Fpsr); + + AArch64.Vpart(0, 0, new Bits(Z)); AArch64.Vpart(0, 1, new Bits(Z)); + AArch64.V(1, new Bits(A)); + AArch64.V(2, new Bits(B)); + Shared.FPSR = new Bits((uint)Fpsr); + SimdFp.Sqdmulh_V(Op[30], Op[23, 22], Op[20, 16], Op[9, 5], Op[4, 0]); + + Assert.Multiple(() => + { + Assert.That(GetVectorE0(ThreadState.V0), Is.EqualTo(AArch64.Vpart(64, 0, 0).ToUInt64())); + Assert.That(GetVectorE1(ThreadState.V0), Is.EqualTo(AArch64.Vpart(64, 0, 1).ToUInt64())); + }); + Assert.That(ThreadState.Fpsr, Is.EqualTo((int)Shared.FPSR.ToUInt32())); + } + + [Test, Pairwise, Description("SQDMULH <Vd>.<T>, <Vn>.<T>, <Vm>.<T>")] + public void Sqdmulh_V_8H_4S([Values(0u)] uint Rd, + [Values(1u, 0u)] uint Rn, + [Values(2u, 0u)] uint Rm, + [ValueSource("_4H2S_")] [Random(RndCnt)] ulong Z, + [ValueSource("_4H2S_")] [Random(RndCnt)] ulong A, + [ValueSource("_4H2S_")] [Random(RndCnt)] ulong B, + [Values(0b01u, 0b10u)] uint size) // <8H, 4S> + { + uint Opcode = 0x4E20B400; // SQDMULH V0.16B, V0.16B, V0.16B (RESERVED) + Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); + Opcode |= ((size & 3) << 22); + Bits Op = new Bits(Opcode); + + int Fpsr = (int)TestContext.CurrentContext.Random.NextUInt(); + + Vector128<float> V0 = MakeVectorE0E1(Z, Z); + Vector128<float> V1 = MakeVectorE0E1(A, A); + Vector128<float> V2 = MakeVectorE0E1(B, B); + AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2, Fpsr: Fpsr); + + AArch64.Vpart(0, 0, new Bits(Z)); AArch64.Vpart(0, 1, new Bits(Z)); + AArch64.Vpart(1, 0, new Bits(A)); AArch64.Vpart(1, 1, new Bits(A)); + AArch64.Vpart(2, 0, new Bits(B)); AArch64.Vpart(2, 1, new Bits(B)); + Shared.FPSR = new Bits((uint)Fpsr); + SimdFp.Sqdmulh_V(Op[30], Op[23, 22], Op[20, 16], Op[9, 5], Op[4, 0]); + + Assert.Multiple(() => + { + Assert.That(GetVectorE0(ThreadState.V0), Is.EqualTo(AArch64.Vpart(64, 0, 0).ToUInt64())); + Assert.That(GetVectorE1(ThreadState.V0), Is.EqualTo(AArch64.Vpart(64, 0, 1).ToUInt64())); + }); + Assert.That(ThreadState.Fpsr, Is.EqualTo((int)Shared.FPSR.ToUInt32())); + } + + [Test, Pairwise, Description("SQRDMULH <V><d>, <V><n>, <V><m>")] + public void Sqrdmulh_S_H_S([Values(0u)] uint Rd, + [Values(1u, 0u)] uint Rn, + [Values(2u, 0u)] uint Rm, + [ValueSource("_1H1S_")] [Random(RndCnt)] ulong Z, + [ValueSource("_1H1S_")] [Random(RndCnt)] ulong A, + [ValueSource("_1H1S_")] [Random(RndCnt)] ulong B, + [Values(0b01u, 0b10u)] uint size) // <H, S> + { + uint Opcode = 0x7E20B400; // SQRDMULH B0, B0, B0 (RESERVED) + Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); + Opcode |= ((size & 3) << 22); + Bits Op = new Bits(Opcode); + + int Fpsr = (int)TestContext.CurrentContext.Random.NextUInt(); + + Vector128<float> V0 = MakeVectorE0E1(Z, Z); + Vector128<float> V1 = MakeVectorE0(A); + Vector128<float> V2 = MakeVectorE0(B); + AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2, Fpsr: Fpsr); + + AArch64.Vpart(0, 0, new Bits(Z)); AArch64.Vpart(0, 1, new Bits(Z)); + AArch64.V(1, new Bits(A)); + AArch64.V(2, new Bits(B)); + Shared.FPSR = new Bits((uint)Fpsr); + SimdFp.Sqrdmulh_S(Op[23, 22], Op[20, 16], Op[9, 5], Op[4, 0]); + + Assert.Multiple(() => + { + Assert.That(GetVectorE0(ThreadState.V0), Is.EqualTo(AArch64.Vpart(64, 0, 0).ToUInt64())); + Assert.That(GetVectorE1(ThreadState.V0), Is.EqualTo(AArch64.Vpart(64, 0, 1).ToUInt64())); + }); + Assert.That(ThreadState.Fpsr, Is.EqualTo((int)Shared.FPSR.ToUInt32())); + } + + [Test, Pairwise, Description("SQRDMULH <Vd>.<T>, <Vn>.<T>, <Vm>.<T>")] + public void Sqrdmulh_V_4H_2S([Values(0u)] uint Rd, + [Values(1u, 0u)] uint Rn, + [Values(2u, 0u)] uint Rm, + [ValueSource("_4H2S_")] [Random(RndCnt)] ulong Z, + [ValueSource("_4H2S_")] [Random(RndCnt)] ulong A, + [ValueSource("_4H2S_")] [Random(RndCnt)] ulong B, + [Values(0b01u, 0b10u)] uint size) // <4H, 2S> + { + uint Opcode = 0x2E20B400; // SQRDMULH V0.8B, V0.8B, V0.8B (RESERVED) + Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); + Opcode |= ((size & 3) << 22); + Bits Op = new Bits(Opcode); + + int Fpsr = (int)TestContext.CurrentContext.Random.NextUInt(); + + Vector128<float> V0 = MakeVectorE0E1(Z, Z); + Vector128<float> V1 = MakeVectorE0(A); + Vector128<float> V2 = MakeVectorE0(B); + AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2, Fpsr: Fpsr); + + AArch64.Vpart(0, 0, new Bits(Z)); AArch64.Vpart(0, 1, new Bits(Z)); + AArch64.V(1, new Bits(A)); + AArch64.V(2, new Bits(B)); + Shared.FPSR = new Bits((uint)Fpsr); + SimdFp.Sqrdmulh_V(Op[30], Op[23, 22], Op[20, 16], Op[9, 5], Op[4, 0]); + + Assert.Multiple(() => + { + Assert.That(GetVectorE0(ThreadState.V0), Is.EqualTo(AArch64.Vpart(64, 0, 0).ToUInt64())); + Assert.That(GetVectorE1(ThreadState.V0), Is.EqualTo(AArch64.Vpart(64, 0, 1).ToUInt64())); + }); + Assert.That(ThreadState.Fpsr, Is.EqualTo((int)Shared.FPSR.ToUInt32())); + } + + [Test, Pairwise, Description("SQRDMULH <Vd>.<T>, <Vn>.<T>, <Vm>.<T>")] + public void Sqrdmulh_V_8H_4S([Values(0u)] uint Rd, + [Values(1u, 0u)] uint Rn, + [Values(2u, 0u)] uint Rm, + [ValueSource("_4H2S_")] [Random(RndCnt)] ulong Z, + [ValueSource("_4H2S_")] [Random(RndCnt)] ulong A, + [ValueSource("_4H2S_")] [Random(RndCnt)] ulong B, + [Values(0b01u, 0b10u)] uint size) // <8H, 4S> + { + uint Opcode = 0x6E20B400; // SQRDMULH V0.16B, V0.16B, V0.16B (RESERVED) + Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); + Opcode |= ((size & 3) << 22); + Bits Op = new Bits(Opcode); + + int Fpsr = (int)TestContext.CurrentContext.Random.NextUInt(); + + Vector128<float> V0 = MakeVectorE0E1(Z, Z); + Vector128<float> V1 = MakeVectorE0E1(A, A); + Vector128<float> V2 = MakeVectorE0E1(B, B); + AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2, Fpsr: Fpsr); + + AArch64.Vpart(0, 0, new Bits(Z)); AArch64.Vpart(0, 1, new Bits(Z)); + AArch64.Vpart(1, 0, new Bits(A)); AArch64.Vpart(1, 1, new Bits(A)); + AArch64.Vpart(2, 0, new Bits(B)); AArch64.Vpart(2, 1, new Bits(B)); + Shared.FPSR = new Bits((uint)Fpsr); + SimdFp.Sqrdmulh_V(Op[30], Op[23, 22], Op[20, 16], Op[9, 5], Op[4, 0]); + + Assert.Multiple(() => + { + Assert.That(GetVectorE0(ThreadState.V0), Is.EqualTo(AArch64.Vpart(64, 0, 0).ToUInt64())); + Assert.That(GetVectorE1(ThreadState.V0), Is.EqualTo(AArch64.Vpart(64, 0, 1).ToUInt64())); + }); + Assert.That(ThreadState.Fpsr, Is.EqualTo((int)Shared.FPSR.ToUInt32())); + } + + [Test, Pairwise, Description("SQSUB <V><d>, <V><n>, <V><m>")] + public void Sqsub_S_B_H_S_D([Values(0u)] uint Rd, + [Values(1u, 0u)] uint Rn, + [Values(2u, 0u)] uint Rm, + [ValueSource("_1B1H1S1D_")] [Random(RndCnt)] ulong Z, + [ValueSource("_1B1H1S1D_")] [Random(RndCnt)] ulong A, + [ValueSource("_1B1H1S1D_")] [Random(RndCnt)] ulong B, + [Values(0b00u, 0b01u, 0b10u, 0b11u)] uint size) // <B, H, S, D> + { + uint Opcode = 0x5E202C00; // SQSUB B0, B0, B0 + Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); + Opcode |= ((size & 3) << 22); + Bits Op = new Bits(Opcode); + + int Fpsr = (int)TestContext.CurrentContext.Random.NextUInt(); + + Vector128<float> V0 = MakeVectorE0E1(Z, Z); + Vector128<float> V1 = MakeVectorE0(A); + Vector128<float> V2 = MakeVectorE0(B); + AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2, Fpsr: Fpsr); + + AArch64.Vpart(0, 0, new Bits(Z)); AArch64.Vpart(0, 1, new Bits(Z)); + AArch64.V(1, new Bits(A)); + AArch64.V(2, new Bits(B)); + Shared.FPSR = new Bits((uint)Fpsr); + SimdFp.Sqsub_S(Op[23, 22], Op[20, 16], Op[9, 5], Op[4, 0]); + + Assert.Multiple(() => + { + Assert.That(GetVectorE0(ThreadState.V0), Is.EqualTo(AArch64.Vpart(64, 0, 0).ToUInt64())); + Assert.That(GetVectorE1(ThreadState.V0), Is.EqualTo(AArch64.Vpart(64, 0, 1).ToUInt64())); + }); + Assert.That(ThreadState.Fpsr, Is.EqualTo((int)Shared.FPSR.ToUInt32())); + } + + [Test, Pairwise, Description("SQSUB <Vd>.<T>, <Vn>.<T>, <Vm>.<T>")] + public void Sqsub_V_8B_4H_2S([Values(0u)] uint Rd, + [Values(1u, 0u)] uint Rn, + [Values(2u, 0u)] uint Rm, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong Z, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong A, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong B, + [Values(0b00u, 0b01u, 0b10u)] uint size) // <8B, 4H, 2S> + { + uint Opcode = 0x0E202C00; // SQSUB V0.8B, V0.8B, V0.8B + Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); + Opcode |= ((size & 3) << 22); + Bits Op = new Bits(Opcode); + + int Fpsr = (int)TestContext.CurrentContext.Random.NextUInt(); + + Vector128<float> V0 = MakeVectorE0E1(Z, Z); + Vector128<float> V1 = MakeVectorE0(A); + Vector128<float> V2 = MakeVectorE0(B); + AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2, Fpsr: Fpsr); + + AArch64.Vpart(0, 0, new Bits(Z)); AArch64.Vpart(0, 1, new Bits(Z)); + AArch64.V(1, new Bits(A)); + AArch64.V(2, new Bits(B)); + Shared.FPSR = new Bits((uint)Fpsr); + SimdFp.Sqsub_V(Op[30], Op[23, 22], Op[20, 16], Op[9, 5], Op[4, 0]); + + Assert.Multiple(() => + { + Assert.That(GetVectorE0(ThreadState.V0), Is.EqualTo(AArch64.Vpart(64, 0, 0).ToUInt64())); + Assert.That(GetVectorE1(ThreadState.V0), Is.EqualTo(AArch64.Vpart(64, 0, 1).ToUInt64())); + }); + Assert.That(ThreadState.Fpsr, Is.EqualTo((int)Shared.FPSR.ToUInt32())); + } + + [Test, Pairwise, Description("SQSUB <Vd>.<T>, <Vn>.<T>, <Vm>.<T>")] + public void Sqsub_V_16B_8H_4S_2D([Values(0u)] uint Rd, + [Values(1u, 0u)] uint Rn, + [Values(2u, 0u)] uint Rm, + [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong Z, + [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong A, + [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong B, + [Values(0b00u, 0b01u, 0b10u, 0b11u)] uint size) // <16B, 8H, 4S, 2D> + { + uint Opcode = 0x4E202C00; // SQSUB V0.16B, V0.16B, V0.16B + Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); + Opcode |= ((size & 3) << 22); + Bits Op = new Bits(Opcode); + + int Fpsr = (int)TestContext.CurrentContext.Random.NextUInt(); + + Vector128<float> V0 = MakeVectorE0E1(Z, Z); + Vector128<float> V1 = MakeVectorE0E1(A, A); + Vector128<float> V2 = MakeVectorE0E1(B, B); + AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2, Fpsr: Fpsr); + + AArch64.Vpart(0, 0, new Bits(Z)); AArch64.Vpart(0, 1, new Bits(Z)); + AArch64.Vpart(1, 0, new Bits(A)); AArch64.Vpart(1, 1, new Bits(A)); + AArch64.Vpart(2, 0, new Bits(B)); AArch64.Vpart(2, 1, new Bits(B)); + Shared.FPSR = new Bits((uint)Fpsr); + SimdFp.Sqsub_V(Op[30], Op[23, 22], Op[20, 16], Op[9, 5], Op[4, 0]); + + Assert.Multiple(() => + { + Assert.That(GetVectorE0(ThreadState.V0), Is.EqualTo(AArch64.Vpart(64, 0, 0).ToUInt64())); + Assert.That(GetVectorE1(ThreadState.V0), Is.EqualTo(AArch64.Vpart(64, 0, 1).ToUInt64())); + }); + Assert.That(ThreadState.Fpsr, Is.EqualTo((int)Shared.FPSR.ToUInt32())); + } + [Test, Pairwise, Description("SSUBW{2} <Vd>.<Ta>, <Vn>.<Ta>, <Vm>.<Tb>")] - public void Ssubw_V_8B8H8H_4H4S4S_2S2D2D([Values(0u)] uint Rd, + public void Ssubw_V_8B8H8H_4H4S4S_2S2D2D([Values(0u)] uint Rd, [Values(1u, 0u)] uint Rn, [Values(2u, 0u)] uint Rm, [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong Z, @@ -1753,7 +2199,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise, Description("SSUBW{2} <Vd>.<Ta>, <Vn>.<Ta>, <Vm>.<Tb>")] - public void Ssubw_V_16B8H8H_8H4S4S_4S2D2D([Values(0u)] uint Rd, + public void Ssubw_V_16B8H8H_8H4S4S_4S2D2D([Values(0u)] uint Rd, [Values(1u, 0u)] uint Rn, [Values(2u, 0u)] uint Rm, [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong Z, @@ -1784,7 +2230,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise, Description("SUB <V><d>, <V><n>, <V><m>")] - public void Sub_S_D([Values(0u)] uint Rd, + public void Sub_S_D([Values(0u)] uint Rd, [Values(1u, 0u)] uint Rn, [Values(2u, 0u)] uint Rm, [ValueSource("_1D_")] [Random(RndCnt)] ulong Z, @@ -1813,7 +2259,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise, Description("SUB <Vd>.<T>, <Vn>.<T>, <Vm>.<T>")] - public void Sub_V_8B_4H_2S([Values(0u)] uint Rd, + public void Sub_V_8B_4H_2S([Values(0u)] uint Rd, [Values(1u, 0u)] uint Rn, [Values(2u, 0u)] uint Rm, [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong Z, @@ -1844,7 +2290,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise, Description("SUB <Vd>.<T>, <Vn>.<T>, <Vm>.<T>")] - public void Sub_V_16B_8H_4S_2D([Values(0u)] uint Rd, + public void Sub_V_16B_8H_4S_2D([Values(0u)] uint Rd, [Values(1u, 0u)] uint Rn, [Values(2u, 0u)] uint Rm, [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong Z, @@ -1875,7 +2321,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise, Description("SUBHN{2} <Vd>.<Tb>, <Vn>.<Ta>, <Vm>.<Ta>")] - public void Subhn_V_8H8B_4S4H_2D2S([Values(0u)] uint Rd, + public void Subhn_V_8H8B_4S4H_2D2S([Values(0u)] uint Rd, [Values(1u, 0u)] uint Rn, [Values(2u, 0u)] uint Rm, [ValueSource("_4H2S1D_")] [Random(RndCnt)] ulong Z, @@ -1906,7 +2352,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise, Description("SUBHN{2} <Vd>.<Tb>, <Vn>.<Ta>, <Vm>.<Ta>")] - public void Subhn_V_8H16B_4S8H_2D4S([Values(0u)] uint Rd, + public void Subhn_V_8H16B_4S8H_2D4S([Values(0u)] uint Rd, [Values(1u, 0u)] uint Rn, [Values(2u, 0u)] uint Rm, [ValueSource("_4H2S1D_")] [Random(RndCnt)] ulong Z, @@ -1937,7 +2383,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise, Description("TRN1 <Vd>.<T>, <Vn>.<T>, <Vm>.<T>")] - public void Trn1_V_8B_4H_2S([Values(0u)] uint Rd, + public void Trn1_V_8B_4H_2S([Values(0u)] uint Rd, [Values(1u, 0u)] uint Rn, [Values(2u, 0u)] uint Rm, [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong Z, @@ -1968,7 +2414,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise, Description("TRN1 <Vd>.<T>, <Vn>.<T>, <Vm>.<T>")] - public void Trn1_V_16B_8H_4S_2D([Values(0u)] uint Rd, + public void Trn1_V_16B_8H_4S_2D([Values(0u)] uint Rd, [Values(1u, 0u)] uint Rn, [Values(2u, 0u)] uint Rm, [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong Z, @@ -1999,7 +2445,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise, Description("TRN2 <Vd>.<T>, <Vn>.<T>, <Vm>.<T>")] - public void Trn2_V_8B_4H_2S([Values(0u)] uint Rd, + public void Trn2_V_8B_4H_2S([Values(0u)] uint Rd, [Values(1u, 0u)] uint Rn, [Values(2u, 0u)] uint Rm, [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong Z, @@ -2030,7 +2476,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise, Description("TRN2 <Vd>.<T>, <Vn>.<T>, <Vm>.<T>")] - public void Trn2_V_16B_8H_4S_2D([Values(0u)] uint Rd, + public void Trn2_V_16B_8H_4S_2D([Values(0u)] uint Rd, [Values(1u, 0u)] uint Rn, [Values(2u, 0u)] uint Rm, [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong Z, @@ -2061,7 +2507,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise, Description("UABA <Vd>.<T>, <Vn>.<T>, <Vm>.<T>")] - public void Uaba_V_8B_4H_2S([Values(0u)] uint Rd, + public void Uaba_V_8B_4H_2S([Values(0u)] uint Rd, [Values(1u, 0u)] uint Rn, [Values(2u, 0u)] uint Rm, [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong Z, @@ -2092,7 +2538,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise, Description("UABA <Vd>.<T>, <Vn>.<T>, <Vm>.<T>")] - public void Uaba_V_16B_8H_4S([Values(0u)] uint Rd, + public void Uaba_V_16B_8H_4S([Values(0u)] uint Rd, [Values(1u, 0u)] uint Rn, [Values(2u, 0u)] uint Rm, [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong Z, @@ -2123,7 +2569,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise, Description("UABAL{2} <Vd>.<Ta>, <Vn>.<Tb>, <Vm>.<Tb>")] - public void Uabal_V_8B8H_4H4S_2S2D([Values(0u)] uint Rd, + public void Uabal_V_8B8H_4H4S_2S2D([Values(0u)] uint Rd, [Values(1u, 0u)] uint Rn, [Values(2u, 0u)] uint Rm, [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong Z, @@ -2154,7 +2600,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise, Description("UABAL{2} <Vd>.<Ta>, <Vn>.<Tb>, <Vm>.<Tb>")] - public void Uabal_V_16B8H_8H4S_4S2D([Values(0u)] uint Rd, + public void Uabal_V_16B8H_8H4S_4S2D([Values(0u)] uint Rd, [Values(1u, 0u)] uint Rn, [Values(2u, 0u)] uint Rm, [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong Z, @@ -2185,7 +2631,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise, Description("UABD <Vd>.<T>, <Vn>.<T>, <Vm>.<T>")] - public void Uabd_V_8B_4H_2S([Values(0u)] uint Rd, + public void Uabd_V_8B_4H_2S([Values(0u)] uint Rd, [Values(1u, 0u)] uint Rn, [Values(2u, 0u)] uint Rm, [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong Z, @@ -2216,7 +2662,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise, Description("UABD <Vd>.<T>, <Vn>.<T>, <Vm>.<T>")] - public void Uabd_V_16B_8H_4S([Values(0u)] uint Rd, + public void Uabd_V_16B_8H_4S([Values(0u)] uint Rd, [Values(1u, 0u)] uint Rn, [Values(2u, 0u)] uint Rm, [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong Z, @@ -2247,7 +2693,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise, Description("UABDL{2} <Vd>.<Ta>, <Vn>.<Tb>, <Vm>.<Tb>")] - public void Uabdl_V_8B8H_4H4S_2S2D([Values(0u)] uint Rd, + public void Uabdl_V_8B8H_4H4S_2S2D([Values(0u)] uint Rd, [Values(1u, 0u)] uint Rn, [Values(2u, 0u)] uint Rm, [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong Z, @@ -2278,7 +2724,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise, Description("UABDL{2} <Vd>.<Ta>, <Vn>.<Tb>, <Vm>.<Tb>")] - public void Uabdl_V_16B8H_8H4S_4S2D([Values(0u)] uint Rd, + public void Uabdl_V_16B8H_8H4S_4S2D([Values(0u)] uint Rd, [Values(1u, 0u)] uint Rn, [Values(2u, 0u)] uint Rm, [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong Z, @@ -2309,7 +2755,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise, Description("UADDW{2} <Vd>.<Ta>, <Vn>.<Ta>, <Vm>.<Tb>")] - public void Uaddw_V_8B8H8H_4H4S4S_2S2D2D([Values(0u)] uint Rd, + public void Uaddw_V_8B8H8H_4H4S4S_2S2D2D([Values(0u)] uint Rd, [Values(1u, 0u)] uint Rn, [Values(2u, 0u)] uint Rm, [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong Z, @@ -2340,7 +2786,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise, Description("UADDW{2} <Vd>.<Ta>, <Vn>.<Ta>, <Vm>.<Tb>")] - public void Uaddw_V_16B8H8H_8H4S4S_4S2D2D([Values(0u)] uint Rd, + public void Uaddw_V_16B8H8H_8H4S4S_4S2D2D([Values(0u)] uint Rd, [Values(1u, 0u)] uint Rn, [Values(2u, 0u)] uint Rm, [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong Z, @@ -2370,8 +2816,218 @@ namespace Ryujinx.Tests.Cpu }); } + [Test, Pairwise, Description("UQADD <V><d>, <V><n>, <V><m>")] + public void Uqadd_S_B_H_S_D([Values(0u)] uint Rd, + [Values(1u, 0u)] uint Rn, + [Values(2u, 0u)] uint Rm, + [ValueSource("_1B1H1S1D_")] [Random(RndCnt)] ulong Z, + [ValueSource("_1B1H1S1D_")] [Random(RndCnt)] ulong A, + [ValueSource("_1B1H1S1D_")] [Random(RndCnt)] ulong B, + [Values(0b00u, 0b01u, 0b10u, 0b11u)] uint size) // <B, H, S, D> + { + uint Opcode = 0x7E200C00; // UQADD B0, B0, B0 + Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); + Opcode |= ((size & 3) << 22); + Bits Op = new Bits(Opcode); + + int Fpsr = (int)TestContext.CurrentContext.Random.NextUInt(); + + Vector128<float> V0 = MakeVectorE0E1(Z, Z); + Vector128<float> V1 = MakeVectorE0(A); + Vector128<float> V2 = MakeVectorE0(B); + AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2, Fpsr: Fpsr); + + AArch64.Vpart(0, 0, new Bits(Z)); AArch64.Vpart(0, 1, new Bits(Z)); + AArch64.V(1, new Bits(A)); + AArch64.V(2, new Bits(B)); + Shared.FPSR = new Bits((uint)Fpsr); + SimdFp.Uqadd_S(Op[23, 22], Op[20, 16], Op[9, 5], Op[4, 0]); + + Assert.Multiple(() => + { + Assert.That(GetVectorE0(ThreadState.V0), Is.EqualTo(AArch64.Vpart(64, 0, 0).ToUInt64())); + Assert.That(GetVectorE1(ThreadState.V0), Is.EqualTo(AArch64.Vpart(64, 0, 1).ToUInt64())); + }); + Assert.That(ThreadState.Fpsr, Is.EqualTo((int)Shared.FPSR.ToUInt32())); + } + + [Test, Pairwise, Description("UQADD <Vd>.<T>, <Vn>.<T>, <Vm>.<T>")] + public void Uqadd_V_8B_4H_2S([Values(0u)] uint Rd, + [Values(1u, 0u)] uint Rn, + [Values(2u, 0u)] uint Rm, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong Z, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong A, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong B, + [Values(0b00u, 0b01u, 0b10u)] uint size) // <8B, 4H, 2S> + { + uint Opcode = 0x2E200C00; // UQADD V0.8B, V0.8B, V0.8B + Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); + Opcode |= ((size & 3) << 22); + Bits Op = new Bits(Opcode); + + int Fpsr = (int)TestContext.CurrentContext.Random.NextUInt(); + + Vector128<float> V0 = MakeVectorE0E1(Z, Z); + Vector128<float> V1 = MakeVectorE0(A); + Vector128<float> V2 = MakeVectorE0(B); + AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2, Fpsr: Fpsr); + + AArch64.Vpart(0, 0, new Bits(Z)); AArch64.Vpart(0, 1, new Bits(Z)); + AArch64.V(1, new Bits(A)); + AArch64.V(2, new Bits(B)); + Shared.FPSR = new Bits((uint)Fpsr); + SimdFp.Uqadd_V(Op[30], Op[23, 22], Op[20, 16], Op[9, 5], Op[4, 0]); + + Assert.Multiple(() => + { + Assert.That(GetVectorE0(ThreadState.V0), Is.EqualTo(AArch64.Vpart(64, 0, 0).ToUInt64())); + Assert.That(GetVectorE1(ThreadState.V0), Is.EqualTo(AArch64.Vpart(64, 0, 1).ToUInt64())); + }); + Assert.That(ThreadState.Fpsr, Is.EqualTo((int)Shared.FPSR.ToUInt32())); + } + + [Test, Pairwise, Description("UQADD <Vd>.<T>, <Vn>.<T>, <Vm>.<T>")] + public void Uqadd_V_16B_8H_4S_2D([Values(0u)] uint Rd, + [Values(1u, 0u)] uint Rn, + [Values(2u, 0u)] uint Rm, + [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong Z, + [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong A, + [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong B, + [Values(0b00u, 0b01u, 0b10u, 0b11u)] uint size) // <16B, 8H, 4S, 2D> + { + uint Opcode = 0x6E200C00; // UQADD V0.16B, V0.16B, V0.16B + Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); + Opcode |= ((size & 3) << 22); + Bits Op = new Bits(Opcode); + + int Fpsr = (int)TestContext.CurrentContext.Random.NextUInt(); + + Vector128<float> V0 = MakeVectorE0E1(Z, Z); + Vector128<float> V1 = MakeVectorE0E1(A, A); + Vector128<float> V2 = MakeVectorE0E1(B, B); + AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2, Fpsr: Fpsr); + + AArch64.Vpart(0, 0, new Bits(Z)); AArch64.Vpart(0, 1, new Bits(Z)); + AArch64.Vpart(1, 0, new Bits(A)); AArch64.Vpart(1, 1, new Bits(A)); + AArch64.Vpart(2, 0, new Bits(B)); AArch64.Vpart(2, 1, new Bits(B)); + Shared.FPSR = new Bits((uint)Fpsr); + SimdFp.Uqadd_V(Op[30], Op[23, 22], Op[20, 16], Op[9, 5], Op[4, 0]); + + Assert.Multiple(() => + { + Assert.That(GetVectorE0(ThreadState.V0), Is.EqualTo(AArch64.Vpart(64, 0, 0).ToUInt64())); + Assert.That(GetVectorE1(ThreadState.V0), Is.EqualTo(AArch64.Vpart(64, 0, 1).ToUInt64())); + }); + Assert.That(ThreadState.Fpsr, Is.EqualTo((int)Shared.FPSR.ToUInt32())); + } + + [Test, Pairwise, Description("UQSUB <V><d>, <V><n>, <V><m>")] + public void Uqsub_S_B_H_S_D([Values(0u)] uint Rd, + [Values(1u, 0u)] uint Rn, + [Values(2u, 0u)] uint Rm, + [ValueSource("_1B1H1S1D_")] [Random(RndCnt)] ulong Z, + [ValueSource("_1B1H1S1D_")] [Random(RndCnt)] ulong A, + [ValueSource("_1B1H1S1D_")] [Random(RndCnt)] ulong B, + [Values(0b00u, 0b01u, 0b10u, 0b11u)] uint size) // <B, H, S, D> + { + uint Opcode = 0x7E202C00; // UQSUB B0, B0, B0 + Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); + Opcode |= ((size & 3) << 22); + Bits Op = new Bits(Opcode); + + int Fpsr = (int)TestContext.CurrentContext.Random.NextUInt(); + + Vector128<float> V0 = MakeVectorE0E1(Z, Z); + Vector128<float> V1 = MakeVectorE0(A); + Vector128<float> V2 = MakeVectorE0(B); + AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2, Fpsr: Fpsr); + + AArch64.Vpart(0, 0, new Bits(Z)); AArch64.Vpart(0, 1, new Bits(Z)); + AArch64.V(1, new Bits(A)); + AArch64.V(2, new Bits(B)); + Shared.FPSR = new Bits((uint)Fpsr); + SimdFp.Uqsub_S(Op[23, 22], Op[20, 16], Op[9, 5], Op[4, 0]); + + Assert.Multiple(() => + { + Assert.That(GetVectorE0(ThreadState.V0), Is.EqualTo(AArch64.Vpart(64, 0, 0).ToUInt64())); + Assert.That(GetVectorE1(ThreadState.V0), Is.EqualTo(AArch64.Vpart(64, 0, 1).ToUInt64())); + }); + Assert.That(ThreadState.Fpsr, Is.EqualTo((int)Shared.FPSR.ToUInt32())); + } + + [Test, Pairwise, Description("UQSUB <Vd>.<T>, <Vn>.<T>, <Vm>.<T>")] + public void Uqsub_V_8B_4H_2S([Values(0u)] uint Rd, + [Values(1u, 0u)] uint Rn, + [Values(2u, 0u)] uint Rm, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong Z, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong A, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong B, + [Values(0b00u, 0b01u, 0b10u)] uint size) // <8B, 4H, 2S> + { + uint Opcode = 0x2E202C00; // UQSUB V0.8B, V0.8B, V0.8B + Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); + Opcode |= ((size & 3) << 22); + Bits Op = new Bits(Opcode); + + int Fpsr = (int)TestContext.CurrentContext.Random.NextUInt(); + + Vector128<float> V0 = MakeVectorE0E1(Z, Z); + Vector128<float> V1 = MakeVectorE0(A); + Vector128<float> V2 = MakeVectorE0(B); + AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2, Fpsr: Fpsr); + + AArch64.Vpart(0, 0, new Bits(Z)); AArch64.Vpart(0, 1, new Bits(Z)); + AArch64.V(1, new Bits(A)); + AArch64.V(2, new Bits(B)); + Shared.FPSR = new Bits((uint)Fpsr); + SimdFp.Uqsub_V(Op[30], Op[23, 22], Op[20, 16], Op[9, 5], Op[4, 0]); + + Assert.Multiple(() => + { + Assert.That(GetVectorE0(ThreadState.V0), Is.EqualTo(AArch64.Vpart(64, 0, 0).ToUInt64())); + Assert.That(GetVectorE1(ThreadState.V0), Is.EqualTo(AArch64.Vpart(64, 0, 1).ToUInt64())); + }); + Assert.That(ThreadState.Fpsr, Is.EqualTo((int)Shared.FPSR.ToUInt32())); + } + + [Test, Pairwise, Description("UQSUB <Vd>.<T>, <Vn>.<T>, <Vm>.<T>")] + public void Uqsub_V_16B_8H_4S_2D([Values(0u)] uint Rd, + [Values(1u, 0u)] uint Rn, + [Values(2u, 0u)] uint Rm, + [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong Z, + [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong A, + [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong B, + [Values(0b00u, 0b01u, 0b10u, 0b11u)] uint size) // <16B, 8H, 4S, 2D> + { + uint Opcode = 0x6E202C00; // UQSUB V0.16B, V0.16B, V0.16B + Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); + Opcode |= ((size & 3) << 22); + Bits Op = new Bits(Opcode); + + int Fpsr = (int)TestContext.CurrentContext.Random.NextUInt(); + + Vector128<float> V0 = MakeVectorE0E1(Z, Z); + Vector128<float> V1 = MakeVectorE0E1(A, A); + Vector128<float> V2 = MakeVectorE0E1(B, B); + AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2, Fpsr: Fpsr); + + AArch64.Vpart(0, 0, new Bits(Z)); AArch64.Vpart(0, 1, new Bits(Z)); + AArch64.Vpart(1, 0, new Bits(A)); AArch64.Vpart(1, 1, new Bits(A)); + AArch64.Vpart(2, 0, new Bits(B)); AArch64.Vpart(2, 1, new Bits(B)); + Shared.FPSR = new Bits((uint)Fpsr); + SimdFp.Uqsub_V(Op[30], Op[23, 22], Op[20, 16], Op[9, 5], Op[4, 0]); + + Assert.Multiple(() => + { + Assert.That(GetVectorE0(ThreadState.V0), Is.EqualTo(AArch64.Vpart(64, 0, 0).ToUInt64())); + Assert.That(GetVectorE1(ThreadState.V0), Is.EqualTo(AArch64.Vpart(64, 0, 1).ToUInt64())); + }); + Assert.That(ThreadState.Fpsr, Is.EqualTo((int)Shared.FPSR.ToUInt32())); + } + [Test, Pairwise, Description("USUBW{2} <Vd>.<Ta>, <Vn>.<Ta>, <Vm>.<Tb>")] - public void Usubw_V_8B8H8H_4H4S4S_2S2D2D([Values(0u)] uint Rd, + public void Usubw_V_8B8H8H_4H4S4S_2S2D2D([Values(0u)] uint Rd, [Values(1u, 0u)] uint Rn, [Values(2u, 0u)] uint Rm, [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong Z, @@ -2402,7 +3058,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise, Description("USUBW{2} <Vd>.<Ta>, <Vn>.<Ta>, <Vm>.<Tb>")] - public void Usubw_V_16B8H8H_8H4S4S_4S2D2D([Values(0u)] uint Rd, + public void Usubw_V_16B8H8H_8H4S4S_4S2D2D([Values(0u)] uint Rd, [Values(1u, 0u)] uint Rn, [Values(2u, 0u)] uint Rm, [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong Z, @@ -2433,7 +3089,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise, Description("UZP1 <Vd>.<T>, <Vn>.<T>, <Vm>.<T>")] - public void Uzp1_V_8B_4H_2S([Values(0u)] uint Rd, + public void Uzp1_V_8B_4H_2S([Values(0u)] uint Rd, [Values(1u, 0u)] uint Rn, [Values(2u, 0u)] uint Rm, [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong Z, @@ -2464,7 +3120,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise, Description("UZP1 <Vd>.<T>, <Vn>.<T>, <Vm>.<T>")] - public void Uzp1_V_16B_8H_4S_2D([Values(0u)] uint Rd, + public void Uzp1_V_16B_8H_4S_2D([Values(0u)] uint Rd, [Values(1u, 0u)] uint Rn, [Values(2u, 0u)] uint Rm, [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong Z, @@ -2495,7 +3151,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise, Description("UZP2 <Vd>.<T>, <Vn>.<T>, <Vm>.<T>")] - public void Uzp2_V_8B_4H_2S([Values(0u)] uint Rd, + public void Uzp2_V_8B_4H_2S([Values(0u)] uint Rd, [Values(1u, 0u)] uint Rn, [Values(2u, 0u)] uint Rm, [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong Z, @@ -2526,7 +3182,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise, Description("UZP2 <Vd>.<T>, <Vn>.<T>, <Vm>.<T>")] - public void Uzp2_V_16B_8H_4S_2D([Values(0u)] uint Rd, + public void Uzp2_V_16B_8H_4S_2D([Values(0u)] uint Rd, [Values(1u, 0u)] uint Rn, [Values(2u, 0u)] uint Rm, [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong Z, @@ -2557,7 +3213,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise, Description("ZIP1 <Vd>.<T>, <Vn>.<T>, <Vm>.<T>")] - public void Zip1_V_8B_4H_2S([Values(0u)] uint Rd, + public void Zip1_V_8B_4H_2S([Values(0u)] uint Rd, [Values(1u, 0u)] uint Rn, [Values(2u, 0u)] uint Rm, [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong Z, @@ -2588,7 +3244,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise, Description("ZIP1 <Vd>.<T>, <Vn>.<T>, <Vm>.<T>")] - public void Zip1_V_16B_8H_4S_2D([Values(0u)] uint Rd, + public void Zip1_V_16B_8H_4S_2D([Values(0u)] uint Rd, [Values(1u, 0u)] uint Rn, [Values(2u, 0u)] uint Rm, [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong Z, @@ -2619,7 +3275,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise, Description("ZIP2 <Vd>.<T>, <Vn>.<T>, <Vm>.<T>")] - public void Zip2_V_8B_4H_2S([Values(0u)] uint Rd, + public void Zip2_V_8B_4H_2S([Values(0u)] uint Rd, [Values(1u, 0u)] uint Rn, [Values(2u, 0u)] uint Rm, [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong Z, @@ -2650,7 +3306,7 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise, Description("ZIP2 <Vd>.<T>, <Vn>.<T>, <Vm>.<T>")] - public void Zip2_V_16B_8H_4S_2D([Values(0u)] uint Rd, + public void Zip2_V_16B_8H_4S_2D([Values(0u)] uint Rd, [Values(1u, 0u)] uint Rn, [Values(2u, 0u)] uint Rm, [ValueSource("_8B4H2S1D_")] [Random(RndCnt)] ulong Z, diff --git a/Ryujinx.Tests/Cpu/Tester/Instructions.cs b/Ryujinx.Tests/Cpu/Tester/Instructions.cs index 2f52dcbf..25873718 100644 --- a/Ryujinx.Tests/Cpu/Tester/Instructions.cs +++ b/Ryujinx.Tests/Cpu/Tester/Instructions.cs @@ -3060,6 +3060,210 @@ namespace Ryujinx.Tests.Cpu.Tester V(d, result); } + // sqabs_advsimd.html#SQABS_asisdmisc_R + public static void Sqabs_S(Bits size, Bits Rn, Bits Rd) + { + const bool U = false; + + /* Decode Scalar */ + int d = (int)UInt(Rd); + int n = (int)UInt(Rn); + + int esize = 8 << (int)UInt(size); + int datasize = esize; + int elements = 1; + + bool neg = (U == true); + + /* Operation */ + /* CheckFPAdvSIMDEnabled64(); */ + + Bits result = new Bits(datasize); + Bits operand = V(datasize, n); + BigInteger element; + bool sat; + + for (int e = 0; e <= elements - 1; e++) + { + element = SInt(Elem(operand, e, esize)); + + if (neg) + { + element = -element; + } + else + { + element = Abs(element); + } + + (Bits _result, bool _sat) = SignedSatQ(element, esize); + Elem(result, e, esize, _result); + sat = _sat; + + if (sat) + { + /* FPSR.QC = '1'; */ + FPSR[27] = true; // TODO: Add named fields. + } + } + + V(d, result); + } + + // sqabs_advsimd.html#SQABS_asimdmisc_R + public static void Sqabs_V(bool Q, Bits size, Bits Rn, Bits Rd) + { + const bool U = false; + + /* Decode Vector */ + int d = (int)UInt(Rd); + int n = (int)UInt(Rn); + + /* if size:Q == '110' then ReservedValue(); */ + + int esize = 8 << (int)UInt(size); + int datasize = (Q ? 128 : 64); + int elements = datasize / esize; + + bool neg = (U == true); + + /* Operation */ + /* CheckFPAdvSIMDEnabled64(); */ + + Bits result = new Bits(datasize); + Bits operand = V(datasize, n); + BigInteger element; + bool sat; + + for (int e = 0; e <= elements - 1; e++) + { + element = SInt(Elem(operand, e, esize)); + + if (neg) + { + element = -element; + } + else + { + element = Abs(element); + } + + (Bits _result, bool _sat) = SignedSatQ(element, esize); + Elem(result, e, esize, _result); + sat = _sat; + + if (sat) + { + /* FPSR.QC = '1'; */ + FPSR[27] = true; // TODO: Add named fields. + } + } + + V(d, result); + } + + // sqneg_advsimd.html#SQNEG_asisdmisc_R + public static void Sqneg_S(Bits size, Bits Rn, Bits Rd) + { + const bool U = true; + + /* Decode Scalar */ + int d = (int)UInt(Rd); + int n = (int)UInt(Rn); + + int esize = 8 << (int)UInt(size); + int datasize = esize; + int elements = 1; + + bool neg = (U == true); + + /* Operation */ + /* CheckFPAdvSIMDEnabled64(); */ + + Bits result = new Bits(datasize); + Bits operand = V(datasize, n); + BigInteger element; + bool sat; + + for (int e = 0; e <= elements - 1; e++) + { + element = SInt(Elem(operand, e, esize)); + + if (neg) + { + element = -element; + } + else + { + element = Abs(element); + } + + (Bits _result, bool _sat) = SignedSatQ(element, esize); + Elem(result, e, esize, _result); + sat = _sat; + + if (sat) + { + /* FPSR.QC = '1'; */ + FPSR[27] = true; // TODO: Add named fields. + } + } + + V(d, result); + } + + // sqneg_advsimd.html#SQNEG_asimdmisc_R + public static void Sqneg_V(bool Q, Bits size, Bits Rn, Bits Rd) + { + const bool U = true; + + /* Decode Vector */ + int d = (int)UInt(Rd); + int n = (int)UInt(Rn); + + /* if size:Q == '110' then ReservedValue(); */ + + int esize = 8 << (int)UInt(size); + int datasize = (Q ? 128 : 64); + int elements = datasize / esize; + + bool neg = (U == true); + + /* Operation */ + /* CheckFPAdvSIMDEnabled64(); */ + + Bits result = new Bits(datasize); + Bits operand = V(datasize, n); + BigInteger element; + bool sat; + + for (int e = 0; e <= elements - 1; e++) + { + element = SInt(Elem(operand, e, esize)); + + if (neg) + { + element = -element; + } + else + { + element = Abs(element); + } + + (Bits _result, bool _sat) = SignedSatQ(element, esize); + Elem(result, e, esize, _result); + sat = _sat; + + if (sat) + { + /* FPSR.QC = '1'; */ + FPSR[27] = true; // TODO: Add named fields. + } + } + + V(d, result); + } + // sqxtn_advsimd.html#SQXTN_asisdmisc_N public static void Sqxtn_S(Bits size, Bits Rn, Bits Rd) { @@ -3228,6 +3432,96 @@ namespace Ryujinx.Tests.Cpu.Tester Vpart(d, part, result); } + // suqadd_advsimd.html#SUQADD_asisdmisc_R + public static void Suqadd_S(Bits size, Bits Rn, Bits Rd) + { + const bool U = false; + + /* Decode Scalar */ + int d = (int)UInt(Rd); + int n = (int)UInt(Rn); + + int esize = 8 << (int)UInt(size); + int datasize = esize; + int elements = 1; + + bool unsigned = (U == true); + + /* Operation */ + /* CheckFPAdvSIMDEnabled64(); */ + + Bits result = new Bits(datasize); + Bits operand = V(datasize, n); + Bits operand2 = V(datasize, d); + BigInteger op1; + BigInteger op2; + bool sat; + + for (int e = 0; e <= elements - 1; e++) + { + op1 = Int(Elem(operand, e, esize), !unsigned); + op2 = Int(Elem(operand2, e, esize), unsigned); + + (Bits _result, bool _sat) = SatQ(op1 + op2, esize, unsigned); + Elem(result, e, esize, _result); + sat = _sat; + + if (sat) + { + /* FPSR.QC = '1'; */ + FPSR[27] = true; // TODO: Add named fields. + } + } + + V(d, result); + } + + // suqadd_advsimd.html#SUQADD_asimdmisc_R + public static void Suqadd_V(bool Q, Bits size, Bits Rn, Bits Rd) + { + const bool U = false; + + /* Decode Vector */ + int d = (int)UInt(Rd); + int n = (int)UInt(Rn); + + /* if size:Q == '110' then ReservedValue(); */ + + int esize = 8 << (int)UInt(size); + int datasize = (Q ? 128 : 64); + int elements = datasize / esize; + + bool unsigned = (U == true); + + /* Operation */ + /* CheckFPAdvSIMDEnabled64(); */ + + Bits result = new Bits(datasize); + Bits operand = V(datasize, n); + Bits operand2 = V(datasize, d); + BigInteger op1; + BigInteger op2; + bool sat; + + for (int e = 0; e <= elements - 1; e++) + { + op1 = Int(Elem(operand, e, esize), !unsigned); + op2 = Int(Elem(operand2, e, esize), unsigned); + + (Bits _result, bool _sat) = SatQ(op1 + op2, esize, unsigned); + Elem(result, e, esize, _result); + sat = _sat; + + if (sat) + { + /* FPSR.QC = '1'; */ + FPSR[27] = true; // TODO: Add named fields. + } + } + + V(d, result); + } + // uqxtn_advsimd.html#UQXTN_asisdmisc_N public static void Uqxtn_S(Bits size, Bits Rn, Bits Rd) { @@ -3316,6 +3610,96 @@ namespace Ryujinx.Tests.Cpu.Tester Vpart(d, part, result); } + // usqadd_advsimd.html#USQADD_asisdmisc_R + public static void Usqadd_S(Bits size, Bits Rn, Bits Rd) + { + const bool U = true; + + /* Decode Scalar */ + int d = (int)UInt(Rd); + int n = (int)UInt(Rn); + + int esize = 8 << (int)UInt(size); + int datasize = esize; + int elements = 1; + + bool unsigned = (U == true); + + /* Operation */ + /* CheckFPAdvSIMDEnabled64(); */ + + Bits result = new Bits(datasize); + Bits operand = V(datasize, n); + Bits operand2 = V(datasize, d); + BigInteger op1; + BigInteger op2; + bool sat; + + for (int e = 0; e <= elements - 1; e++) + { + op1 = Int(Elem(operand, e, esize), !unsigned); + op2 = Int(Elem(operand2, e, esize), unsigned); + + (Bits _result, bool _sat) = SatQ(op1 + op2, esize, unsigned); + Elem(result, e, esize, _result); + sat = _sat; + + if (sat) + { + /* FPSR.QC = '1'; */ + FPSR[27] = true; // TODO: Add named fields. + } + } + + V(d, result); + } + + // usqadd_advsimd.html#USQADD_asimdmisc_R + public static void Usqadd_V(bool Q, Bits size, Bits Rn, Bits Rd) + { + const bool U = true; + + /* Decode Vector */ + int d = (int)UInt(Rd); + int n = (int)UInt(Rn); + + /* if size:Q == '110' then ReservedValue(); */ + + int esize = 8 << (int)UInt(size); + int datasize = (Q ? 128 : 64); + int elements = datasize / esize; + + bool unsigned = (U == true); + + /* Operation */ + /* CheckFPAdvSIMDEnabled64(); */ + + Bits result = new Bits(datasize); + Bits operand = V(datasize, n); + Bits operand2 = V(datasize, d); + BigInteger op1; + BigInteger op2; + bool sat; + + for (int e = 0; e <= elements - 1; e++) + { + op1 = Int(Elem(operand, e, esize), !unsigned); + op2 = Int(Elem(operand2, e, esize), unsigned); + + (Bits _result, bool _sat) = SatQ(op1 + op2, esize, unsigned); + Elem(result, e, esize, _result); + sat = _sat; + + if (sat) + { + /* FPSR.QC = '1'; */ + FPSR[27] = true; // TODO: Add named fields. + } + } + + V(d, result); + } + // xtn_advsimd.html public static void Xtn_V(bool Q, Bits size, Bits Rn, Bits Rd) { @@ -4593,6 +4977,406 @@ namespace Ryujinx.Tests.Cpu.Tester V(d, result); } + // sqadd_advsimd.html#SQADD_asisdsame_only + public static void Sqadd_S(Bits size, Bits Rm, Bits Rn, Bits Rd) + { + const bool U = false; + + /* Decode Scalar */ + int d = (int)UInt(Rd); + int n = (int)UInt(Rn); + int m = (int)UInt(Rm); + + int esize = 8 << (int)UInt(size); + int datasize = esize; + int elements = 1; + + bool unsigned = (U == true); + + /* Operation */ + /* CheckFPAdvSIMDEnabled64(); */ + + Bits result = new Bits(datasize); + Bits operand1 = V(datasize, n); + Bits operand2 = V(datasize, m); + BigInteger element1; + BigInteger element2; + BigInteger sum; + bool sat; + + for (int e = 0; e <= elements - 1; e++) + { + element1 = Int(Elem(operand1, e, esize), unsigned); + element2 = Int(Elem(operand2, e, esize), unsigned); + + sum = element1 + element2; + + (Bits _result, bool _sat) = SatQ(sum, esize, unsigned); + Elem(result, e, esize, _result); + sat = _sat; + + if (sat) + { + /* FPSR.QC = '1'; */ + FPSR[27] = true; // TODO: Add named fields. + } + } + + V(d, result); + } + + // sqadd_advsimd.html#SQADD_asimdsame_only + public static void Sqadd_V(bool Q, Bits size, Bits Rm, Bits Rn, Bits Rd) + { + const bool U = false; + + /* Decode Vector */ + int d = (int)UInt(Rd); + int n = (int)UInt(Rn); + int m = (int)UInt(Rm); + + /* if size:Q == '110' then ReservedValue(); */ + + int esize = 8 << (int)UInt(size); + int datasize = (Q ? 128 : 64); + int elements = datasize / esize; + + bool unsigned = (U == true); + + /* Operation */ + /* CheckFPAdvSIMDEnabled64(); */ + + Bits result = new Bits(datasize); + Bits operand1 = V(datasize, n); + Bits operand2 = V(datasize, m); + BigInteger element1; + BigInteger element2; + BigInteger sum; + bool sat; + + for (int e = 0; e <= elements - 1; e++) + { + element1 = Int(Elem(operand1, e, esize), unsigned); + element2 = Int(Elem(operand2, e, esize), unsigned); + + sum = element1 + element2; + + (Bits _result, bool _sat) = SatQ(sum, esize, unsigned); + Elem(result, e, esize, _result); + sat = _sat; + + if (sat) + { + /* FPSR.QC = '1'; */ + FPSR[27] = true; // TODO: Add named fields. + } + } + + V(d, result); + } + + // sqdmulh_advsimd_vec.html#SQDMULH_asisdsame_only + public static void Sqdmulh_S(Bits size, Bits Rm, Bits Rn, Bits Rd) + { + const bool U = false; + + /* Decode Scalar */ + int d = (int)UInt(Rd); + int n = (int)UInt(Rn); + int m = (int)UInt(Rm); + + /* if size == '11' || size == '00' then ReservedValue(); */ + + int esize = 8 << (int)UInt(size); + int datasize = esize; + int elements = 1; + + bool rounding = (U == true); + + /* Operation */ + /* CheckFPAdvSIMDEnabled64(); */ + + Bits result = new Bits(datasize); + Bits operand1 = V(datasize, n); + Bits operand2 = V(datasize, m); + BigInteger round_const = (rounding ? (BigInteger)1 << (esize - 1) : 0); + BigInteger element1; + BigInteger element2; + BigInteger product; + bool sat; + + for (int e = 0; e <= elements - 1; e++) + { + element1 = SInt(Elem(operand1, e, esize)); + element2 = SInt(Elem(operand2, e, esize)); + + product = (2 * element1 * element2) + round_const; + + (Bits _result, bool _sat) = SignedSatQ(product >> esize, esize); + Elem(result, e, esize, _result); + sat = _sat; + + if (sat) + { + /* FPSR.QC = '1'; */ + FPSR[27] = true; // TODO: Add named fields. + } + } + + V(d, result); + } + + // sqdmulh_advsimd_vec.html#SQDMULH_asimdsame_only + public static void Sqdmulh_V(bool Q, Bits size, Bits Rm, Bits Rn, Bits Rd) + { + const bool U = false; + + /* Decode Vector */ + int d = (int)UInt(Rd); + int n = (int)UInt(Rn); + int m = (int)UInt(Rm); + + /* if size == '11' || size == '00' then ReservedValue(); */ + + int esize = 8 << (int)UInt(size); + int datasize = (Q ? 128 : 64); + int elements = datasize / esize; + + bool rounding = (U == true); + + /* Operation */ + /* CheckFPAdvSIMDEnabled64(); */ + + Bits result = new Bits(datasize); + Bits operand1 = V(datasize, n); + Bits operand2 = V(datasize, m); + BigInteger round_const = (rounding ? (BigInteger)1 << (esize - 1) : 0); + BigInteger element1; + BigInteger element2; + BigInteger product; + bool sat; + + for (int e = 0; e <= elements - 1; e++) + { + element1 = SInt(Elem(operand1, e, esize)); + element2 = SInt(Elem(operand2, e, esize)); + + product = (2 * element1 * element2) + round_const; + + (Bits _result, bool _sat) = SignedSatQ(product >> esize, esize); + Elem(result, e, esize, _result); + sat = _sat; + + if (sat) + { + /* FPSR.QC = '1'; */ + FPSR[27] = true; // TODO: Add named fields. + } + } + + V(d, result); + } + + // sqrdmulh_advsimd_vec.html#SQRDMULH_asisdsame_only + public static void Sqrdmulh_S(Bits size, Bits Rm, Bits Rn, Bits Rd) + { + const bool U = true; + + /* Decode Scalar */ + int d = (int)UInt(Rd); + int n = (int)UInt(Rn); + int m = (int)UInt(Rm); + + /* if size == '11' || size == '00' then ReservedValue(); */ + + int esize = 8 << (int)UInt(size); + int datasize = esize; + int elements = 1; + + bool rounding = (U == true); + + /* Operation */ + /* CheckFPAdvSIMDEnabled64(); */ + + Bits result = new Bits(datasize); + Bits operand1 = V(datasize, n); + Bits operand2 = V(datasize, m); + BigInteger round_const = (rounding ? (BigInteger)1 << (esize - 1) : 0); + BigInteger element1; + BigInteger element2; + BigInteger product; + bool sat; + + for (int e = 0; e <= elements - 1; e++) + { + element1 = SInt(Elem(operand1, e, esize)); + element2 = SInt(Elem(operand2, e, esize)); + + product = (2 * element1 * element2) + round_const; + + (Bits _result, bool _sat) = SignedSatQ(product >> esize, esize); + Elem(result, e, esize, _result); + sat = _sat; + + if (sat) + { + /* FPSR.QC = '1'; */ + FPSR[27] = true; // TODO: Add named fields. + } + } + + V(d, result); + } + + // sqrdmulh_advsimd_vec.html#SQRDMULH_asimdsame_only + public static void Sqrdmulh_V(bool Q, Bits size, Bits Rm, Bits Rn, Bits Rd) + { + const bool U = true; + + /* Decode Vector */ + int d = (int)UInt(Rd); + int n = (int)UInt(Rn); + int m = (int)UInt(Rm); + + /* if size == '11' || size == '00' then ReservedValue(); */ + + int esize = 8 << (int)UInt(size); + int datasize = (Q ? 128 : 64); + int elements = datasize / esize; + + bool rounding = (U == true); + + /* Operation */ + /* CheckFPAdvSIMDEnabled64(); */ + + Bits result = new Bits(datasize); + Bits operand1 = V(datasize, n); + Bits operand2 = V(datasize, m); + BigInteger round_const = (rounding ? (BigInteger)1 << (esize - 1) : 0); + BigInteger element1; + BigInteger element2; + BigInteger product; + bool sat; + + for (int e = 0; e <= elements - 1; e++) + { + element1 = SInt(Elem(operand1, e, esize)); + element2 = SInt(Elem(operand2, e, esize)); + + product = (2 * element1 * element2) + round_const; + + (Bits _result, bool _sat) = SignedSatQ(product >> esize, esize); + Elem(result, e, esize, _result); + sat = _sat; + + if (sat) + { + /* FPSR.QC = '1'; */ + FPSR[27] = true; // TODO: Add named fields. + } + } + + V(d, result); + } + + // sqsub_advsimd.html#SQSUB_asisdsame_only + public static void Sqsub_S(Bits size, Bits Rm, Bits Rn, Bits Rd) + { + const bool U = false; + + /* Decode Scalar */ + int d = (int)UInt(Rd); + int n = (int)UInt(Rn); + int m = (int)UInt(Rm); + + int esize = 8 << (int)UInt(size); + int datasize = esize; + int elements = 1; + + bool unsigned = (U == true); + + /* Operation */ + /* CheckFPAdvSIMDEnabled64(); */ + + Bits result = new Bits(datasize); + Bits operand1 = V(datasize, n); + Bits operand2 = V(datasize, m); + BigInteger element1; + BigInteger element2; + BigInteger diff; + bool sat; + + for (int e = 0; e <= elements - 1; e++) + { + element1 = Int(Elem(operand1, e, esize), unsigned); + element2 = Int(Elem(operand2, e, esize), unsigned); + + diff = element1 - element2; + + (Bits _result, bool _sat) = SatQ(diff, esize, unsigned); + Elem(result, e, esize, _result); + sat = _sat; + + if (sat) + { + /* FPSR.QC = '1'; */ + FPSR[27] = true; // TODO: Add named fields. + } + } + + V(d, result); + } + + // sqsub_advsimd.html#SQSUB_asimdsame_only + public static void Sqsub_V(bool Q, Bits size, Bits Rm, Bits Rn, Bits Rd) + { + const bool U = false; + + /* Decode Vector */ + int d = (int)UInt(Rd); + int n = (int)UInt(Rn); + int m = (int)UInt(Rm); + + /* if size:Q == '110' then ReservedValue(); */ + + int esize = 8 << (int)UInt(size); + int datasize = (Q ? 128 : 64); + int elements = datasize / esize; + + bool unsigned = (U == true); + + /* Operation */ + /* CheckFPAdvSIMDEnabled64(); */ + + Bits result = new Bits(datasize); + Bits operand1 = V(datasize, n); + Bits operand2 = V(datasize, m); + BigInteger element1; + BigInteger element2; + BigInteger diff; + bool sat; + + for (int e = 0; e <= elements - 1; e++) + { + element1 = Int(Elem(operand1, e, esize), unsigned); + element2 = Int(Elem(operand2, e, esize), unsigned); + + diff = element1 - element2; + + (Bits _result, bool _sat) = SatQ(diff, esize, unsigned); + Elem(result, e, esize, _result); + sat = _sat; + + if (sat) + { + /* FPSR.QC = '1'; */ + FPSR[27] = true; // TODO: Add named fields. + } + } + + V(d, result); + } + // ssubw_advsimd.html public static void Ssubw_V(bool Q, Bits size, Bits Rm, Bits Rn, Bits Rd) { @@ -5085,6 +5869,202 @@ namespace Ryujinx.Tests.Cpu.Tester V(d, result); } + // uqadd_advsimd.html#UQADD_asisdsame_only + public static void Uqadd_S(Bits size, Bits Rm, Bits Rn, Bits Rd) + { + const bool U = true; + + /* Decode Scalar */ + int d = (int)UInt(Rd); + int n = (int)UInt(Rn); + int m = (int)UInt(Rm); + + int esize = 8 << (int)UInt(size); + int datasize = esize; + int elements = 1; + + bool unsigned = (U == true); + + /* Operation */ + /* CheckFPAdvSIMDEnabled64(); */ + + Bits result = new Bits(datasize); + Bits operand1 = V(datasize, n); + Bits operand2 = V(datasize, m); + BigInteger element1; + BigInteger element2; + BigInteger sum; + bool sat; + + for (int e = 0; e <= elements - 1; e++) + { + element1 = Int(Elem(operand1, e, esize), unsigned); + element2 = Int(Elem(operand2, e, esize), unsigned); + + sum = element1 + element2; + + (Bits _result, bool _sat) = SatQ(sum, esize, unsigned); + Elem(result, e, esize, _result); + sat = _sat; + + if (sat) + { + /* FPSR.QC = '1'; */ + FPSR[27] = true; // TODO: Add named fields. + } + } + + V(d, result); + } + + // uqadd_advsimd.html#UQADD_asimdsame_only + public static void Uqadd_V(bool Q, Bits size, Bits Rm, Bits Rn, Bits Rd) + { + const bool U = true; + + /* Decode Vector */ + int d = (int)UInt(Rd); + int n = (int)UInt(Rn); + int m = (int)UInt(Rm); + + /* if size:Q == '110' then ReservedValue(); */ + + int esize = 8 << (int)UInt(size); + int datasize = (Q ? 128 : 64); + int elements = datasize / esize; + + bool unsigned = (U == true); + + /* Operation */ + /* CheckFPAdvSIMDEnabled64(); */ + + Bits result = new Bits(datasize); + Bits operand1 = V(datasize, n); + Bits operand2 = V(datasize, m); + BigInteger element1; + BigInteger element2; + BigInteger sum; + bool sat; + + for (int e = 0; e <= elements - 1; e++) + { + element1 = Int(Elem(operand1, e, esize), unsigned); + element2 = Int(Elem(operand2, e, esize), unsigned); + + sum = element1 + element2; + + (Bits _result, bool _sat) = SatQ(sum, esize, unsigned); + Elem(result, e, esize, _result); + sat = _sat; + + if (sat) + { + /* FPSR.QC = '1'; */ + FPSR[27] = true; // TODO: Add named fields. + } + } + + V(d, result); + } + + // uqsub_advsimd.html#UQSUB_asisdsame_only + public static void Uqsub_S(Bits size, Bits Rm, Bits Rn, Bits Rd) + { + const bool U = true; + + /* Decode Scalar */ + int d = (int)UInt(Rd); + int n = (int)UInt(Rn); + int m = (int)UInt(Rm); + + int esize = 8 << (int)UInt(size); + int datasize = esize; + int elements = 1; + + bool unsigned = (U == true); + + /* Operation */ + /* CheckFPAdvSIMDEnabled64(); */ + + Bits result = new Bits(datasize); + Bits operand1 = V(datasize, n); + Bits operand2 = V(datasize, m); + BigInteger element1; + BigInteger element2; + BigInteger diff; + bool sat; + + for (int e = 0; e <= elements - 1; e++) + { + element1 = Int(Elem(operand1, e, esize), unsigned); + element2 = Int(Elem(operand2, e, esize), unsigned); + + diff = element1 - element2; + + (Bits _result, bool _sat) = SatQ(diff, esize, unsigned); + Elem(result, e, esize, _result); + sat = _sat; + + if (sat) + { + /* FPSR.QC = '1'; */ + FPSR[27] = true; // TODO: Add named fields. + } + } + + V(d, result); + } + + // uqsub_advsimd.html#UQSUB_asimdsame_only + public static void Uqsub_V(bool Q, Bits size, Bits Rm, Bits Rn, Bits Rd) + { + const bool U = true; + + /* Decode Vector */ + int d = (int)UInt(Rd); + int n = (int)UInt(Rn); + int m = (int)UInt(Rm); + + /* if size:Q == '110' then ReservedValue(); */ + + int esize = 8 << (int)UInt(size); + int datasize = (Q ? 128 : 64); + int elements = datasize / esize; + + bool unsigned = (U == true); + + /* Operation */ + /* CheckFPAdvSIMDEnabled64(); */ + + Bits result = new Bits(datasize); + Bits operand1 = V(datasize, n); + Bits operand2 = V(datasize, m); + BigInteger element1; + BigInteger element2; + BigInteger diff; + bool sat; + + for (int e = 0; e <= elements - 1; e++) + { + element1 = Int(Elem(operand1, e, esize), unsigned); + element2 = Int(Elem(operand2, e, esize), unsigned); + + diff = element1 - element2; + + (Bits _result, bool _sat) = SatQ(diff, esize, unsigned); + Elem(result, e, esize, _result); + sat = _sat; + + if (sat) + { + /* FPSR.QC = '1'; */ + FPSR[27] = true; // TODO: Add named fields. + } + } + + V(d, result); + } + // usubw_advsimd.html public static void Usubw_V(bool Q, Bits size, Bits Rm, Bits Rn, Bits Rd) { diff --git a/Ryujinx.Tests/Cpu/Tester/Pseudocode.cs b/Ryujinx.Tests/Cpu/Tester/Pseudocode.cs index 3a877fb1..6c4dfa92 100644 --- a/Ryujinx.Tests/Cpu/Tester/Pseudocode.cs +++ b/Ryujinx.Tests/Cpu/Tester/Pseudocode.cs @@ -1193,9 +1193,9 @@ namespace Ryujinx.Tests.Cpu.Tester result = BigInteger.Pow(2, N) - 1; saturated = true; } - else if (i < 0) + else if (i < (BigInteger)0) { - result = 0; + result = (BigInteger)0; saturated = true; } else diff --git a/Ryujinx.Tests/Ryujinx.Tests.csproj b/Ryujinx.Tests/Ryujinx.Tests.csproj index b4ba379a..a68b34e9 100644 --- a/Ryujinx.Tests/Ryujinx.Tests.csproj +++ b/Ryujinx.Tests/Ryujinx.Tests.csproj @@ -1,20 +1,25 @@ -<Project Sdk="Microsoft.NET.Sdk"> +<Project Sdk="Microsoft.NET.Sdk"> + <PropertyGroup> <TargetFramework>netcoreapp2.1</TargetFramework> <RuntimeIdentifiers>win10-x64;osx-x64;linux-x64</RuntimeIdentifiers> <OutputType>Exe</OutputType> <IsPackable>false</IsPackable> </PropertyGroup> + <PropertyGroup> <GenerateAssemblyInfo>false</GenerateAssemblyInfo> </PropertyGroup> + <ItemGroup> - <PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.7.0" /> + <PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.8.0" /> <PackageReference Include="NUnit" Version="3.10.1" /> <PackageReference Include="NUnit3TestAdapter" Version="3.10.0" /> <PackageReference Include="System.Runtime.Intrinsics.Experimental" Version="4.5.0-rc1" /> </ItemGroup> + <ItemGroup> <ProjectReference Include="..\ChocolArm64\ChocolArm64.csproj" /> </ItemGroup> + </Project> diff --git a/Ryujinx/Config.cs b/Ryujinx/Config.cs index 0f346122..b63455ee 100644 --- a/Ryujinx/Config.cs +++ b/Ryujinx/Config.cs @@ -7,6 +7,8 @@ using System.IO; using System.Linq; using System.Reflection; +using static Ryujinx.HLE.OsHle.SystemStateMgr; + namespace Ryujinx { public static class Config @@ -14,11 +16,6 @@ namespace Ryujinx public static JoyConKeyboard JoyConKeyboard { get; private set; } public static JoyConController JoyConController { get; private set; } - public static float GamePadDeadzone { get; private set; } - public static bool GamePadEnable { get; private set; } - public static int GamePadIndex { get; private set; } - public static float GamePadTriggerThreshold { get; private set; } - public static void Read(Logger Log) { string IniFolder = Path.GetDirectoryName(Assembly.GetEntryAssembly().Location); @@ -36,11 +33,8 @@ namespace Ryujinx Log.SetEnable(LogLevel.Info, Convert.ToBoolean(Parser.Value("Logging_Enable_Info"))); Log.SetEnable(LogLevel.Warning, Convert.ToBoolean(Parser.Value("Logging_Enable_Warn"))); Log.SetEnable(LogLevel.Error, Convert.ToBoolean(Parser.Value("Logging_Enable_Error"))); - - GamePadEnable = Convert.ToBoolean(Parser.Value("GamePad_Enable")); - GamePadIndex = Convert.ToInt32 (Parser.Value("GamePad_Index")); - GamePadDeadzone = (float)Convert.ToDouble (Parser.Value("GamePad_Deadzone"), CultureInfo.InvariantCulture); - GamePadTriggerThreshold = (float)Convert.ToDouble (Parser.Value("GamePad_Trigger_Threshold"), CultureInfo.InvariantCulture); + + DockedMode = Convert.ToBoolean(Parser.Value("Docked_Mode")); string[] FilteredLogClasses = Parser.Value("Logging_Filtered_Classes").Split(',', StringSplitOptions.RemoveEmptyEntries); @@ -70,9 +64,9 @@ namespace Ryujinx } } - JoyConKeyboard = new JoyConKeyboard - { - Left = new JoyConKeyboardLeft + JoyConKeyboard = new JoyConKeyboard( + + new JoyConKeyboardLeft { StickUp = Convert.ToInt16(Parser.Value("Controls_Left_JoyConKeyboard_Stick_Up")), StickDown = Convert.ToInt16(Parser.Value("Controls_Left_JoyConKeyboard_Stick_Down")), @@ -88,7 +82,7 @@ namespace Ryujinx ButtonZL = Convert.ToInt16(Parser.Value("Controls_Left_JoyConKeyboard_Button_ZL")) }, - Right = new JoyConKeyboardRight + new JoyConKeyboardRight { StickUp = Convert.ToInt16(Parser.Value("Controls_Right_JoyConKeyboard_Stick_Up")), StickDown = Convert.ToInt16(Parser.Value("Controls_Right_JoyConKeyboard_Stick_Down")), @@ -102,37 +96,69 @@ namespace Ryujinx ButtonPlus = Convert.ToInt16(Parser.Value("Controls_Right_JoyConKeyboard_Button_Plus")), ButtonR = Convert.ToInt16(Parser.Value("Controls_Right_JoyConKeyboard_Button_R")), ButtonZR = Convert.ToInt16(Parser.Value("Controls_Right_JoyConKeyboard_Button_ZR")) - } - }; + }); - JoyConController = new JoyConController - { - Left = new JoyConControllerLeft + JoyConController = new JoyConController( + + Convert.ToBoolean(Parser.Value("GamePad_Enable")), + Convert.ToInt32 (Parser.Value("GamePad_Index")), + (float)Convert.ToDouble (Parser.Value("GamePad_Deadzone"), CultureInfo.InvariantCulture), + (float)Convert.ToDouble (Parser.Value("GamePad_Trigger_Threshold"), CultureInfo.InvariantCulture), + + new JoyConControllerLeft { - Stick = Parser.Value("Controls_Left_JoyConController_Stick"), - StickButton = Parser.Value("Controls_Left_JoyConController_Stick_Button"), - DPadUp = Parser.Value("Controls_Left_JoyConController_DPad_Up"), - DPadDown = Parser.Value("Controls_Left_JoyConController_DPad_Down"), - DPadLeft = Parser.Value("Controls_Left_JoyConController_DPad_Left"), - DPadRight = Parser.Value("Controls_Left_JoyConController_DPad_Right"), - ButtonMinus = Parser.Value("Controls_Left_JoyConController_Button_Minus"), - ButtonL = Parser.Value("Controls_Left_JoyConController_Button_L"), - ButtonZL = Parser.Value("Controls_Left_JoyConController_Button_ZL") + Stick = ToID(Parser.Value("Controls_Left_JoyConController_Stick")), + StickButton = ToID(Parser.Value("Controls_Left_JoyConController_Stick_Button")), + DPadUp = ToID(Parser.Value("Controls_Left_JoyConController_DPad_Up")), + DPadDown = ToID(Parser.Value("Controls_Left_JoyConController_DPad_Down")), + DPadLeft = ToID(Parser.Value("Controls_Left_JoyConController_DPad_Left")), + DPadRight = ToID(Parser.Value("Controls_Left_JoyConController_DPad_Right")), + ButtonMinus = ToID(Parser.Value("Controls_Left_JoyConController_Button_Minus")), + ButtonL = ToID(Parser.Value("Controls_Left_JoyConController_Button_L")), + ButtonZL = ToID(Parser.Value("Controls_Left_JoyConController_Button_ZL")) }, - Right = new JoyConControllerRight + new JoyConControllerRight { - Stick = Parser.Value("Controls_Right_JoyConController_Stick"), - StickButton = Parser.Value("Controls_Right_JoyConController_Stick_Button"), - ButtonA = Parser.Value("Controls_Right_JoyConController_Button_A"), - ButtonB = Parser.Value("Controls_Right_JoyConController_Button_B"), - ButtonX = Parser.Value("Controls_Right_JoyConController_Button_X"), - ButtonY = Parser.Value("Controls_Right_JoyConController_Button_Y"), - ButtonPlus = Parser.Value("Controls_Right_JoyConController_Button_Plus"), - ButtonR = Parser.Value("Controls_Right_JoyConController_Button_R"), - ButtonZR = Parser.Value("Controls_Right_JoyConController_Button_ZR") - } - }; + Stick = ToID(Parser.Value("Controls_Right_JoyConController_Stick")), + StickButton = ToID(Parser.Value("Controls_Right_JoyConController_Stick_Button")), + ButtonA = ToID(Parser.Value("Controls_Right_JoyConController_Button_A")), + ButtonB = ToID(Parser.Value("Controls_Right_JoyConController_Button_B")), + ButtonX = ToID(Parser.Value("Controls_Right_JoyConController_Button_X")), + ButtonY = ToID(Parser.Value("Controls_Right_JoyConController_Button_Y")), + ButtonPlus = ToID(Parser.Value("Controls_Right_JoyConController_Button_Plus")), + ButtonR = ToID(Parser.Value("Controls_Right_JoyConController_Button_R")), + ButtonZR = ToID(Parser.Value("Controls_Right_JoyConController_Button_ZR")) + }); + } + + private static ControllerInputID ToID(string Key) + { + switch (Key.ToUpper()) + { + case "LSTICK": return ControllerInputID.LStick; + case "DPADUP": return ControllerInputID.DPadUp; + case "DPADDOWN": return ControllerInputID.DPadDown; + case "DPADLEFT": return ControllerInputID.DPadLeft; + case "DPADRIGHT": return ControllerInputID.DPadRight; + case "BACK": return ControllerInputID.Back; + case "LSHOULDER": return ControllerInputID.LShoulder; + case "LTRIGGER": return ControllerInputID.LTrigger; + + case "RSTICK": return ControllerInputID.RStick; + case "A": return ControllerInputID.A; + case "B": return ControllerInputID.B; + case "X": return ControllerInputID.X; + case "Y": return ControllerInputID.Y; + case "START": return ControllerInputID.Start; + case "RSHOULDER": return ControllerInputID.RShoulder; + case "RTRIGGER": return ControllerInputID.RTrigger; + + case "LJOYSTICK": return ControllerInputID.LJoystick; + case "RJOYSTICK": return ControllerInputID.RJoystick; + + default: return ControllerInputID.Invalid; + } } } diff --git a/Ryujinx/Ryujinx.conf b/Ryujinx/Ryujinx.conf index 063bb2de..3225acc3 100644 --- a/Ryujinx/Ryujinx.conf +++ b/Ryujinx/Ryujinx.conf @@ -22,6 +22,9 @@ Logging_Enable_Error = true #Filtered log classes, seperated by ", ", eg. `Logging_Filtered_Classes = Loader, ServiceFS` Logging_Filtered_Classes = +#Enable or Disable Docked Mode +Docked_Mode = false + #Controller Device Index GamePad_Index = 0 @@ -82,4 +85,4 @@ Controls_Right_JoyConController_Button_R = RShoulder Controls_Right_JoyConController_Button_ZR = RTrigger Controls_Left_JoyConController_Stick = LJoystick -Controls_Right_JoyConController_Stick = RJoystick
\ No newline at end of file +Controls_Right_JoyConController_Stick = RJoystick diff --git a/Ryujinx/Ryujinx.csproj b/Ryujinx/Ryujinx.csproj index 443e7bba..ad132ada 100644 --- a/Ryujinx/Ryujinx.csproj +++ b/Ryujinx/Ryujinx.csproj @@ -1,23 +1,28 @@ -<Project Sdk="Microsoft.NET.Sdk"> +<Project Sdk="Microsoft.NET.Sdk"> + <PropertyGroup> - <OutputType>Exe</OutputType> <TargetFramework>netcoreapp2.1</TargetFramework> - <AllowUnsafeBlocks>true</AllowUnsafeBlocks> <RuntimeIdentifiers>win10-x64;osx-x64;linux-x64</RuntimeIdentifiers> + <OutputType>Exe</OutputType> + <AllowUnsafeBlocks>true</AllowUnsafeBlocks> </PropertyGroup> + <ItemGroup> <PackageReference Include="OpenTK.NetStandard" Version="1.0.4" /> <PackageReference Include="System.Runtime.CompilerServices.Unsafe" Version="4.4.0" /> </ItemGroup> + <ItemGroup> <ProjectReference Include="..\ChocolArm64\ChocolArm64.csproj" /> <ProjectReference Include="..\Ryujinx.Audio\Ryujinx.Audio.csproj" /> - <ProjectReference Include="..\Ryujinx.HLE\Ryujinx.HLE.csproj" /> <ProjectReference Include="..\Ryujinx.Graphics\Ryujinx.Graphics.csproj" /> + <ProjectReference Include="..\Ryujinx.HLE\Ryujinx.HLE.csproj" /> </ItemGroup> + <ItemGroup> <None Update="Ryujinx.conf"> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> </None> </ItemGroup> -</Project>
\ No newline at end of file + +</Project> diff --git a/Ryujinx/Ui/GLScreen.cs b/Ryujinx/Ui/GLScreen.cs index 9b5dda4f..dfc0b9a4 100644 --- a/Ryujinx/Ui/GLScreen.cs +++ b/Ryujinx/Ui/GLScreen.cs @@ -4,6 +4,7 @@ using OpenTK.Input; using Ryujinx.Graphics.Gal; using Ryujinx.HLE; using Ryujinx.HLE.Input; +using Ryujinx.UI.Input; using System; using System.Threading; @@ -16,9 +17,6 @@ namespace Ryujinx private const int TouchScreenWidth = 1280; private const int TouchScreenHeight = 720; - private const float TouchScreenRatioX = (float)TouchScreenWidth / TouchScreenHeight; - private const float TouchScreenRatioY = (float)TouchScreenHeight / TouchScreenWidth; - private const int TargetFPS = 60; private Switch Ns; @@ -49,10 +47,6 @@ namespace Ryujinx Location = new Point( (DisplayDevice.Default.Width / 2) - (Width / 2), (DisplayDevice.Default.Height / 2) - (Height / 2)); - - ResizeEvent = false; - - TitleEvent = false; } private void RenderLoop() @@ -127,60 +121,9 @@ namespace Ryujinx Title = NewTitle; } } - } - } - - private bool IsGamePadButtonPressedFromString(GamePadState GamePad, string Button) - { - if (Button.ToUpper() == "LTRIGGER" || Button.ToUpper() == "RTRIGGER") - { - return GetGamePadTriggerFromString(GamePad, Button) >= Config.GamePadTriggerThreshold; - } - else - { - return (GetGamePadButtonFromString(GamePad, Button) == ButtonState.Pressed); - } - } - - private ButtonState GetGamePadButtonFromString(GamePadState GamePad, string Button) - { - switch (Button.ToUpper()) - { - case "A": return GamePad.Buttons.A; - case "B": return GamePad.Buttons.B; - case "X": return GamePad.Buttons.X; - case "Y": return GamePad.Buttons.Y; - case "LSTICK": return GamePad.Buttons.LeftStick; - case "RSTICK": return GamePad.Buttons.RightStick; - case "LSHOULDER": return GamePad.Buttons.LeftShoulder; - case "RSHOULDER": return GamePad.Buttons.RightShoulder; - case "DPADUP": return GamePad.DPad.Up; - case "DPADDOWN": return GamePad.DPad.Down; - case "DPADLEFT": return GamePad.DPad.Left; - case "DPADRIGHT": return GamePad.DPad.Right; - case "START": return GamePad.Buttons.Start; - case "BACK": return GamePad.Buttons.Back; - default: throw new ArgumentException(); - } - } - - private float GetGamePadTriggerFromString(GamePadState GamePad, string Trigger) - { - switch (Trigger.ToUpper()) - { - case "LTRIGGER": return GamePad.Triggers.Left; - case "RTRIGGER": return GamePad.Triggers.Right; - default: throw new ArgumentException(); - } - } - private Vector2 GetJoystickAxisFromString(GamePadState GamePad, string Joystick) - { - switch (Joystick.ToUpper()) - { - case "LJOYSTICK": return GamePad.ThumbSticks.Left; - case "RJOYSTICK": return new Vector2(-GamePad.ThumbSticks.Right.Y, -GamePad.ThumbSticks.Right.X); - default: throw new ArgumentException(); + //Polling becomes expensive if it's not slept + Thread.Sleep(1); } } @@ -190,95 +133,37 @@ namespace Ryujinx HidJoystickPosition LeftJoystick; HidJoystickPosition RightJoystick; - int LeftJoystickDX = 0; - int LeftJoystickDY = 0; - int RightJoystickDX = 0; - int RightJoystickDY = 0; - float AnalogStickDeadzone = Config.GamePadDeadzone; + int LeftJoystickDX = 0; + int LeftJoystickDY = 0; + int RightJoystickDX = 0; + int RightJoystickDY = 0; //Keyboard Input if (Keyboard.HasValue) { KeyboardState Keyboard = this.Keyboard.Value; - if (Keyboard[Key.Escape]) this.Exit(); - - //LeftJoystick - if (Keyboard[(Key)Config.JoyConKeyboard.Left.StickUp]) LeftJoystickDY = short.MaxValue; - if (Keyboard[(Key)Config.JoyConKeyboard.Left.StickDown]) LeftJoystickDY = -short.MaxValue; - if (Keyboard[(Key)Config.JoyConKeyboard.Left.StickLeft]) LeftJoystickDX = -short.MaxValue; - if (Keyboard[(Key)Config.JoyConKeyboard.Left.StickRight]) LeftJoystickDX = short.MaxValue; - - //LeftButtons - if (Keyboard[(Key)Config.JoyConKeyboard.Left.StickButton]) CurrentButton |= HidControllerButtons.KEY_LSTICK; - if (Keyboard[(Key)Config.JoyConKeyboard.Left.DPadUp]) CurrentButton |= HidControllerButtons.KEY_DUP; - if (Keyboard[(Key)Config.JoyConKeyboard.Left.DPadDown]) CurrentButton |= HidControllerButtons.KEY_DDOWN; - if (Keyboard[(Key)Config.JoyConKeyboard.Left.DPadLeft]) CurrentButton |= HidControllerButtons.KEY_DLEFT; - if (Keyboard[(Key)Config.JoyConKeyboard.Left.DPadRight]) CurrentButton |= HidControllerButtons.KEY_DRIGHT; - if (Keyboard[(Key)Config.JoyConKeyboard.Left.ButtonMinus]) CurrentButton |= HidControllerButtons.KEY_MINUS; - if (Keyboard[(Key)Config.JoyConKeyboard.Left.ButtonL]) CurrentButton |= HidControllerButtons.KEY_L; - if (Keyboard[(Key)Config.JoyConKeyboard.Left.ButtonZL]) CurrentButton |= HidControllerButtons.KEY_ZL; - - //RightJoystick - if (Keyboard[(Key)Config.JoyConKeyboard.Right.StickUp]) RightJoystickDY = short.MaxValue; - if (Keyboard[(Key)Config.JoyConKeyboard.Right.StickDown]) RightJoystickDY = -short.MaxValue; - if (Keyboard[(Key)Config.JoyConKeyboard.Right.StickLeft]) RightJoystickDX = -short.MaxValue; - if (Keyboard[(Key)Config.JoyConKeyboard.Right.StickRight]) RightJoystickDX = short.MaxValue; - - //RightButtons - if (Keyboard[(Key)Config.JoyConKeyboard.Right.StickButton]) CurrentButton |= HidControllerButtons.KEY_RSTICK; - if (Keyboard[(Key)Config.JoyConKeyboard.Right.ButtonA]) CurrentButton |= HidControllerButtons.KEY_A; - if (Keyboard[(Key)Config.JoyConKeyboard.Right.ButtonB]) CurrentButton |= HidControllerButtons.KEY_B; - if (Keyboard[(Key)Config.JoyConKeyboard.Right.ButtonX]) CurrentButton |= HidControllerButtons.KEY_X; - if (Keyboard[(Key)Config.JoyConKeyboard.Right.ButtonY]) CurrentButton |= HidControllerButtons.KEY_Y; - if (Keyboard[(Key)Config.JoyConKeyboard.Right.ButtonPlus]) CurrentButton |= HidControllerButtons.KEY_PLUS; - if (Keyboard[(Key)Config.JoyConKeyboard.Right.ButtonR]) CurrentButton |= HidControllerButtons.KEY_R; - if (Keyboard[(Key)Config.JoyConKeyboard.Right.ButtonZR]) CurrentButton |= HidControllerButtons.KEY_ZR; + CurrentButton = Config.JoyConKeyboard.GetButtons(Keyboard); + + (LeftJoystickDX, LeftJoystickDY) = Config.JoyConKeyboard.GetLeftStick(Keyboard); + + (RightJoystickDX, RightJoystickDY) = Config.JoyConKeyboard.GetRightStick(Keyboard); } //Controller Input - if (Config.GamePadEnable) + CurrentButton |= Config.JoyConController.GetButtons(); + + //Keyboard has priority stick-wise + if (LeftJoystickDX == 0 && LeftJoystickDY == 0) { - GamePadState GamePad = OpenTK.Input.GamePad.GetState(Config.GamePadIndex); - //LeftButtons - if (IsGamePadButtonPressedFromString(GamePad, Config.JoyConController.Left.DPadUp)) CurrentButton |= HidControllerButtons.KEY_DUP; - if (IsGamePadButtonPressedFromString(GamePad, Config.JoyConController.Left.DPadDown)) CurrentButton |= HidControllerButtons.KEY_DDOWN; - if (IsGamePadButtonPressedFromString(GamePad, Config.JoyConController.Left.DPadLeft)) CurrentButton |= HidControllerButtons.KEY_DLEFT; - if (IsGamePadButtonPressedFromString(GamePad, Config.JoyConController.Left.DPadRight)) CurrentButton |= HidControllerButtons.KEY_DRIGHT; - if (IsGamePadButtonPressedFromString(GamePad, Config.JoyConController.Left.StickButton)) CurrentButton |= HidControllerButtons.KEY_LSTICK; - if (IsGamePadButtonPressedFromString(GamePad, Config.JoyConController.Left.ButtonMinus)) CurrentButton |= HidControllerButtons.KEY_MINUS; - if (IsGamePadButtonPressedFromString(GamePad, Config.JoyConController.Left.ButtonL)) CurrentButton |= HidControllerButtons.KEY_L; - if (IsGamePadButtonPressedFromString(GamePad, Config.JoyConController.Left.ButtonZL)) CurrentButton |= HidControllerButtons.KEY_ZL; - - //RightButtons - if (IsGamePadButtonPressedFromString(GamePad, Config.JoyConController.Right.ButtonA)) CurrentButton |= HidControllerButtons.KEY_A; - if (IsGamePadButtonPressedFromString(GamePad, Config.JoyConController.Right.ButtonB)) CurrentButton |= HidControllerButtons.KEY_B; - if (IsGamePadButtonPressedFromString(GamePad, Config.JoyConController.Right.ButtonX)) CurrentButton |= HidControllerButtons.KEY_X; - if (IsGamePadButtonPressedFromString(GamePad, Config.JoyConController.Right.ButtonY)) CurrentButton |= HidControllerButtons.KEY_Y; - if (IsGamePadButtonPressedFromString(GamePad, Config.JoyConController.Right.StickButton)) CurrentButton |= HidControllerButtons.KEY_RSTICK; - if (IsGamePadButtonPressedFromString(GamePad, Config.JoyConController.Right.ButtonPlus)) CurrentButton |= HidControllerButtons.KEY_PLUS; - if (IsGamePadButtonPressedFromString(GamePad, Config.JoyConController.Right.ButtonR)) CurrentButton |= HidControllerButtons.KEY_R; - if (IsGamePadButtonPressedFromString(GamePad, Config.JoyConController.Right.ButtonZR)) CurrentButton |= HidControllerButtons.KEY_ZR; - - //LeftJoystick - if (GetJoystickAxisFromString(GamePad, Config.JoyConController.Left.Stick).X >= AnalogStickDeadzone - || GetJoystickAxisFromString(GamePad, Config.JoyConController.Left.Stick).X <= -AnalogStickDeadzone) - LeftJoystickDX = (int)(GetJoystickAxisFromString(GamePad, Config.JoyConController.Left.Stick).X * short.MaxValue); - - if (GetJoystickAxisFromString(GamePad, Config.JoyConController.Left.Stick).Y >= AnalogStickDeadzone - || GetJoystickAxisFromString(GamePad, Config.JoyConController.Left.Stick).Y <= -AnalogStickDeadzone) - LeftJoystickDY = (int)(GetJoystickAxisFromString(GamePad, Config.JoyConController.Left.Stick).Y * short.MaxValue); - - //RightJoystick - if (GetJoystickAxisFromString(GamePad, Config.JoyConController.Right.Stick).X >= AnalogStickDeadzone - || GetJoystickAxisFromString(GamePad, Config.JoyConController.Right.Stick).X <= -AnalogStickDeadzone) - RightJoystickDX = (int)(GetJoystickAxisFromString(GamePad, Config.JoyConController.Right.Stick).X * short.MaxValue); - - if (GetJoystickAxisFromString(GamePad, Config.JoyConController.Right.Stick).Y >= AnalogStickDeadzone - || GetJoystickAxisFromString(GamePad, Config.JoyConController.Right.Stick).Y <= -AnalogStickDeadzone) - RightJoystickDY = (int)(GetJoystickAxisFromString(GamePad, Config.JoyConController.Right.Stick).Y * short.MaxValue); + (LeftJoystickDX, LeftJoystickDY) = Config.JoyConController.GetLeftStick(); } + if (RightJoystickDX == 0 && RightJoystickDY == 0) + { + (RightJoystickDX, RightJoystickDY) = Config.JoyConController.GetRightStick(); + } + LeftJoystick = new HidJoystickPosition { DX = LeftJoystickDX, @@ -302,13 +187,13 @@ namespace Ryujinx int ScrnWidth = Width; int ScrnHeight = Height; - if (Width > Height * TouchScreenRatioX) + if (Width > (Height * TouchScreenWidth) / TouchScreenHeight) { - ScrnWidth = (int)(Height * TouchScreenRatioX); + ScrnWidth = (Height * TouchScreenWidth) / TouchScreenHeight; } else { - ScrnHeight = (int)(Width * TouchScreenRatioY); + ScrnHeight = (Width * TouchScreenHeight) / TouchScreenWidth; } int StartX = (Width - ScrnWidth) >> 1; @@ -325,8 +210,8 @@ namespace Ryujinx int ScrnMouseX = Mouse.X - StartX; int ScrnMouseY = Mouse.Y - StartY; - int MX = (int)(((float)ScrnMouseX / ScrnWidth) * TouchScreenWidth); - int MY = (int)(((float)ScrnMouseY / ScrnHeight) * TouchScreenHeight); + int MX = (ScrnMouseX * TouchScreenWidth) / ScrnWidth; + int MY = (ScrnMouseY * TouchScreenHeight) / ScrnHeight; HidTouchPoint CurrentPoint = new HidTouchPoint { @@ -397,6 +282,29 @@ namespace Ryujinx protected override void OnKeyDown(KeyboardKeyEventArgs e) { + bool ToggleFullscreen = e.Key == Key.F11 || + (e.Modifiers.HasFlag(KeyModifiers.Alt) && e.Key == Key.Enter); + + if (WindowState == WindowState.Fullscreen) + { + if (e.Key == Key.Escape || ToggleFullscreen) + { + WindowState = WindowState.Normal; + } + } + else + { + if (e.Key == Key.Escape) + { + Exit(); + } + + if (ToggleFullscreen) + { + WindowState = WindowState.Fullscreen; + } + } + Keyboard = e.Keyboard; } diff --git a/Ryujinx/Ui/JoyConController.cs b/Ryujinx/Ui/JoyConController.cs index e525017d..aac8efd7 100644 --- a/Ryujinx/Ui/JoyConController.cs +++ b/Ryujinx/Ui/JoyConController.cs @@ -1,38 +1,216 @@ -using System; -using System.Collections.Generic; -using System.Text; +using OpenTK; +using OpenTK.Input; +using Ryujinx.HLE.Input; +using System; namespace Ryujinx.UI.Input { + public enum ControllerInputID + { + Invalid, + + LStick, + DPadUp, + DPadDown, + DPadLeft, + DPadRight, + Back, + LShoulder, + + RStick, + A, + B, + X, + Y, + Start, + RShoulder, + + LTrigger, + RTrigger, + + LJoystick, + RJoystick + } + public struct JoyConControllerLeft { - public string Stick; - public string StickButton; - public string DPadUp; - public string DPadDown; - public string DPadLeft; - public string DPadRight; - public string ButtonMinus; - public string ButtonL; - public string ButtonZL; + public ControllerInputID Stick; + public ControllerInputID StickButton; + public ControllerInputID DPadUp; + public ControllerInputID DPadDown; + public ControllerInputID DPadLeft; + public ControllerInputID DPadRight; + public ControllerInputID ButtonMinus; + public ControllerInputID ButtonL; + public ControllerInputID ButtonZL; } public struct JoyConControllerRight { - public string Stick; - public string StickButton; - public string ButtonA; - public string ButtonB; - public string ButtonX; - public string ButtonY; - public string ButtonPlus; - public string ButtonR; - public string ButtonZR; + public ControllerInputID Stick; + public ControllerInputID StickButton; + public ControllerInputID ButtonA; + public ControllerInputID ButtonB; + public ControllerInputID ButtonX; + public ControllerInputID ButtonY; + public ControllerInputID ButtonPlus; + public ControllerInputID ButtonR; + public ControllerInputID ButtonZR; } - public struct JoyConController + public class JoyConController { - public JoyConControllerLeft Left; - public JoyConControllerRight Right; + public bool Enabled { private set; get; } + public int Index { private set; get; } + public float Deadzone { private set; get; } + public float TriggerThreshold { private set; get; } + + public JoyConControllerLeft Left { private set; get; } + public JoyConControllerRight Right { private set; get; } + + public JoyConController( + bool Enabled, + int Index, + float Deadzone, + float TriggerThreshold, + JoyConControllerLeft Left, + JoyConControllerRight Right) + { + this.Enabled = Enabled; + this.Index = Index; + this.Deadzone = Deadzone; + this.TriggerThreshold = TriggerThreshold; + this.Left = Left; + this.Right = Right; + + //Unmapped controllers are problematic, skip them + if (GamePad.GetName(Index) == "Unmapped Controller") + { + this.Enabled = false; + } + } + + public HidControllerButtons GetButtons() + { + if (!Enabled) + { + return 0; + } + + GamePadState GpState = GamePad.GetState(Index); + + HidControllerButtons Buttons = 0; + + if (IsPressed(GpState, Left.DPadUp)) Buttons |= HidControllerButtons.KEY_DUP; + if (IsPressed(GpState, Left.DPadDown)) Buttons |= HidControllerButtons.KEY_DDOWN; + if (IsPressed(GpState, Left.DPadLeft)) Buttons |= HidControllerButtons.KEY_DLEFT; + if (IsPressed(GpState, Left.DPadRight)) Buttons |= HidControllerButtons.KEY_DRIGHT; + if (IsPressed(GpState, Left.StickButton)) Buttons |= HidControllerButtons.KEY_LSTICK; + if (IsPressed(GpState, Left.ButtonMinus)) Buttons |= HidControllerButtons.KEY_MINUS; + if (IsPressed(GpState, Left.ButtonL)) Buttons |= HidControllerButtons.KEY_L; + if (IsPressed(GpState, Left.ButtonZL)) Buttons |= HidControllerButtons.KEY_ZL; + + if (IsPressed(GpState, Right.ButtonA)) Buttons |= HidControllerButtons.KEY_A; + if (IsPressed(GpState, Right.ButtonB)) Buttons |= HidControllerButtons.KEY_B; + if (IsPressed(GpState, Right.ButtonX)) Buttons |= HidControllerButtons.KEY_X; + if (IsPressed(GpState, Right.ButtonY)) Buttons |= HidControllerButtons.KEY_Y; + if (IsPressed(GpState, Right.StickButton)) Buttons |= HidControllerButtons.KEY_RSTICK; + if (IsPressed(GpState, Right.ButtonPlus)) Buttons |= HidControllerButtons.KEY_PLUS; + if (IsPressed(GpState, Right.ButtonR)) Buttons |= HidControllerButtons.KEY_R; + if (IsPressed(GpState, Right.ButtonZR)) Buttons |= HidControllerButtons.KEY_ZR; + + return Buttons; + } + + public (short, short) GetLeftStick() + { + if (!Enabled) + { + return (0, 0); + } + + return GetStick(Left.Stick); + } + + public (short, short) GetRightStick() + { + if (!Enabled) + { + return (0, 0); + } + + return GetStick(Right.Stick); + } + + private (short, short) GetStick(ControllerInputID Joystick) + { + GamePadState GpState = GamePad.GetState(Index); + + switch (Joystick) + { + case ControllerInputID.LJoystick: + return ApplyDeadzone(GpState.ThumbSticks.Left); + + case ControllerInputID.RJoystick: + return ApplyDeadzone(GpState.ThumbSticks.Right); + + default: + return (0, 0); + } + } + + private (short, short) ApplyDeadzone(Vector2 Axis) + { + return (ClampAxis(MathF.Abs(Axis.X) > Deadzone ? Axis.X : 0f), + ClampAxis(MathF.Abs(Axis.Y) > Deadzone ? Axis.Y : 0f)); + } + + private static short ClampAxis(float Value) + { + if (Value <= -short.MaxValue) + { + return -short.MaxValue; + } + else + { + return (short)(Value * short.MaxValue); + } + } + + private bool IsPressed(GamePadState GpState, ControllerInputID Button) + { + switch (Button) + { + case ControllerInputID.A: return GpState.Buttons.A == ButtonState.Pressed; + case ControllerInputID.B: return GpState.Buttons.B == ButtonState.Pressed; + case ControllerInputID.X: return GpState.Buttons.X == ButtonState.Pressed; + case ControllerInputID.Y: return GpState.Buttons.Y == ButtonState.Pressed; + case ControllerInputID.LStick: return GpState.Buttons.LeftStick == ButtonState.Pressed; + case ControllerInputID.RStick: return GpState.Buttons.RightStick == ButtonState.Pressed; + case ControllerInputID.LShoulder: return GpState.Buttons.LeftShoulder == ButtonState.Pressed; + case ControllerInputID.RShoulder: return GpState.Buttons.RightShoulder == ButtonState.Pressed; + case ControllerInputID.DPadUp: return GpState.DPad.Up == ButtonState.Pressed; + case ControllerInputID.DPadDown: return GpState.DPad.Down == ButtonState.Pressed; + case ControllerInputID.DPadLeft: return GpState.DPad.Left == ButtonState.Pressed; + case ControllerInputID.DPadRight: return GpState.DPad.Right == ButtonState.Pressed; + case ControllerInputID.Start: return GpState.Buttons.Start == ButtonState.Pressed; + case ControllerInputID.Back: return GpState.Buttons.Back == ButtonState.Pressed; + + case ControllerInputID.LTrigger: return GpState.Triggers.Left >= TriggerThreshold; + case ControllerInputID.RTrigger: return GpState.Triggers.Right >= TriggerThreshold; + + //Using thumbsticks as buttons is not common, but it would be nice not to ignore them + case ControllerInputID.LJoystick: + return GpState.ThumbSticks.Left.X >= Deadzone || + GpState.ThumbSticks.Left.Y >= Deadzone; + + case ControllerInputID.RJoystick: + return GpState.ThumbSticks.Right.X >= Deadzone || + GpState.ThumbSticks.Right.Y >= Deadzone; + + default: + return false; + } + } } } diff --git a/Ryujinx/Ui/JoyConKeyboard.cs b/Ryujinx/Ui/JoyConKeyboard.cs index b329d9ec..ea964553 100644 --- a/Ryujinx/Ui/JoyConKeyboard.cs +++ b/Ryujinx/Ui/JoyConKeyboard.cs @@ -1,3 +1,6 @@ +using OpenTK.Input; +using Ryujinx.HLE.Input; + namespace Ryujinx.UI.Input { public struct JoyConKeyboardLeft @@ -32,9 +35,68 @@ namespace Ryujinx.UI.Input public int ButtonZR; } - public struct JoyConKeyboard + public class JoyConKeyboard { - public JoyConKeyboardLeft Left; + public JoyConKeyboardLeft Left; public JoyConKeyboardRight Right; + + public JoyConKeyboard( + JoyConKeyboardLeft Left, + JoyConKeyboardRight Right) + { + this.Left = Left; + this.Right = Right; + } + + public HidControllerButtons GetButtons(KeyboardState Keyboard) + { + HidControllerButtons Buttons = 0; + + if (Keyboard[(Key)Left.StickButton]) Buttons |= HidControllerButtons.KEY_LSTICK; + if (Keyboard[(Key)Left.DPadUp]) Buttons |= HidControllerButtons.KEY_DUP; + if (Keyboard[(Key)Left.DPadDown]) Buttons |= HidControllerButtons.KEY_DDOWN; + if (Keyboard[(Key)Left.DPadLeft]) Buttons |= HidControllerButtons.KEY_DLEFT; + if (Keyboard[(Key)Left.DPadRight]) Buttons |= HidControllerButtons.KEY_DRIGHT; + if (Keyboard[(Key)Left.ButtonMinus]) Buttons |= HidControllerButtons.KEY_MINUS; + if (Keyboard[(Key)Left.ButtonL]) Buttons |= HidControllerButtons.KEY_L; + if (Keyboard[(Key)Left.ButtonZL]) Buttons |= HidControllerButtons.KEY_ZL; + + if (Keyboard[(Key)Right.StickButton]) Buttons |= HidControllerButtons.KEY_RSTICK; + if (Keyboard[(Key)Right.ButtonA]) Buttons |= HidControllerButtons.KEY_A; + if (Keyboard[(Key)Right.ButtonB]) Buttons |= HidControllerButtons.KEY_B; + if (Keyboard[(Key)Right.ButtonX]) Buttons |= HidControllerButtons.KEY_X; + if (Keyboard[(Key)Right.ButtonY]) Buttons |= HidControllerButtons.KEY_Y; + if (Keyboard[(Key)Right.ButtonPlus]) Buttons |= HidControllerButtons.KEY_PLUS; + if (Keyboard[(Key)Right.ButtonR]) Buttons |= HidControllerButtons.KEY_R; + if (Keyboard[(Key)Right.ButtonZR]) Buttons |= HidControllerButtons.KEY_ZR; + + return Buttons; + } + + public (short, short) GetLeftStick(KeyboardState Keyboard) + { + short DX = 0; + short DY = 0; + + if (Keyboard[(Key)Left.StickUp]) DY = short.MaxValue; + if (Keyboard[(Key)Left.StickDown]) DY = -short.MaxValue; + if (Keyboard[(Key)Left.StickLeft]) DX = -short.MaxValue; + if (Keyboard[(Key)Left.StickRight]) DX = short.MaxValue; + + return (DX, DY); + } + + public (short, short) GetRightStick(KeyboardState Keyboard) + { + short DX = 0; + short DY = 0; + + if (Keyboard[(Key)Right.StickUp]) DY = short.MaxValue; + if (Keyboard[(Key)Right.StickDown]) DY = -short.MaxValue; + if (Keyboard[(Key)Right.StickLeft]) DX = -short.MaxValue; + if (Keyboard[(Key)Right.StickRight]) DX = short.MaxValue; + + return (DX, DY); + } } } |
