0
0
Fork 0

Add Sqxtn_S, Sqxtn_V, Uqxtn_S, Uqxtn_V instructions and Tests (6). (#110)

* Update ILGeneratorEx.cs

* Update AOpCodeTable.cs

* Update AInstEmitSimdArithmetic.cs

* Update CpuTestSimd.cs

* Update CpuTestSimdReg.cs

* Update CpuTest.cs

* Update Pseudocode.cs

* Update Instructions.cs

* Update AInstEmitSimdArithmetic.cs

* Update AInstEmitSimdArithmetic.cs

* Update AInstEmitSimdArithmetic.cs
This commit is contained in:
LDj3SNuD 2018-04-30 01:39:58 +02:00 committed by gdkchan
parent 071754aaeb
commit 7cda630aba
8 changed files with 673 additions and 43 deletions

View file

@ -337,6 +337,8 @@ namespace ChocolArm64
Set("0x001110<<1xxxxx011011xxxxxxxxxx", AInstEmit.Smin_V, typeof(AOpCodeSimdReg)); Set("0x001110<<1xxxxx011011xxxxxxxxxx", AInstEmit.Smin_V, typeof(AOpCodeSimdReg));
Set("0x001110<<1xxxxx100000xxxxxxxxxx", AInstEmit.Smlal_V, typeof(AOpCodeSimdReg)); Set("0x001110<<1xxxxx100000xxxxxxxxxx", AInstEmit.Smlal_V, typeof(AOpCodeSimdReg));
Set("0x001110<<1xxxxx110000xxxxxxxxxx", AInstEmit.Smull_V, typeof(AOpCodeSimdReg)); Set("0x001110<<1xxxxx110000xxxxxxxxxx", AInstEmit.Smull_V, typeof(AOpCodeSimdReg));
Set("01011110<<100001010010xxxxxxxxxx", AInstEmit.Sqxtn_S, typeof(AOpCodeSimd));
Set("0x001110<<100001010010xxxxxxxxxx", AInstEmit.Sqxtn_V, typeof(AOpCodeSimd));
Set("0>001110<<1xxxxx010001xxxxxxxxxx", AInstEmit.Sshl_V, typeof(AOpCodeSimdReg)); Set("0>001110<<1xxxxx010001xxxxxxxxxx", AInstEmit.Sshl_V, typeof(AOpCodeSimdReg));
Set("0x00111100>>>xxx101001xxxxxxxxxx", AInstEmit.Sshll_V, typeof(AOpCodeSimdShImm)); Set("0x00111100>>>xxx101001xxxxxxxxxx", AInstEmit.Sshll_V, typeof(AOpCodeSimdShImm));
Set("010111110>>>>xxx000001xxxxxxxxxx", AInstEmit.Sshr_S, typeof(AOpCodeSimdShImm)); Set("010111110>>>>xxx000001xxxxxxxxxx", AInstEmit.Sshr_S, typeof(AOpCodeSimdShImm));
@ -370,6 +372,8 @@ namespace ChocolArm64
Set("0x101110<<1xxxxx000001xxxxxxxxxx", AInstEmit.Uhadd_V, typeof(AOpCodeSimdReg)); Set("0x101110<<1xxxxx000001xxxxxxxxxx", AInstEmit.Uhadd_V, typeof(AOpCodeSimdReg));
Set("0x001110000xxxxx001111xxxxxxxxxx", AInstEmit.Umov_S, typeof(AOpCodeSimdIns)); Set("0x001110000xxxxx001111xxxxxxxxxx", AInstEmit.Umov_S, typeof(AOpCodeSimdIns));
Set("0x101110<<1xxxxx110000xxxxxxxxxx", AInstEmit.Umull_V, typeof(AOpCodeSimdReg)); Set("0x101110<<1xxxxx110000xxxxxxxxxx", AInstEmit.Umull_V, typeof(AOpCodeSimdReg));
Set("01111110<<100001010010xxxxxxxxxx", AInstEmit.Uqxtn_S, typeof(AOpCodeSimd));
Set("0x101110<<100001010010xxxxxxxxxx", AInstEmit.Uqxtn_V, typeof(AOpCodeSimd));
Set("0>101110<<1xxxxx010001xxxxxxxxxx", AInstEmit.Ushl_V, typeof(AOpCodeSimdReg)); Set("0>101110<<1xxxxx010001xxxxxxxxxx", AInstEmit.Ushl_V, typeof(AOpCodeSimdReg));
Set("0x10111100>>>xxx101001xxxxxxxxxx", AInstEmit.Ushll_V, typeof(AOpCodeSimdShImm)); Set("0x10111100>>>xxx101001xxxxxxxxxx", AInstEmit.Ushll_V, typeof(AOpCodeSimdShImm));
Set("011111110>>>>xxx000001xxxxxxxxxx", AInstEmit.Ushr_S, typeof(AOpCodeSimdShImm)); Set("011111110>>>>xxx000001xxxxxxxxxx", AInstEmit.Ushr_S, typeof(AOpCodeSimdShImm));

View file

@ -205,6 +205,84 @@ namespace ChocolArm64.Instruction
} }
} }
private static void EmitQxtn(AILEmitterCtx Context, bool Signed, bool Scalar)
{
AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp;
int Elems = (!Scalar ? 8 >> Op.Size : 1);
int ESize = 8 << Op.Size;
int TMaxValue = (Signed ? (1 << (ESize - 1)) - 1 : (int)((1L << ESize) - 1L));
int TMinValue = (Signed ? -((1 << (ESize - 1))) : 0);
int Part = (!Scalar & (Op.RegisterSize == ARegisterSize.SIMD128) ? Elems : 0);
Context.EmitLdc_I8(0L);
Context.EmitSttmp();
for (int Index = 0; Index < Elems; Index++)
{
AILLabel LblLe = new AILLabel();
AILLabel LblGeEnd = new AILLabel();
EmitVectorExtract(Context, Op.Rn, Index, Op.Size + 1, Signed);
Context.Emit(OpCodes.Dup);
Context.EmitLdc_I4(TMaxValue);
Context.Emit(OpCodes.Conv_U8);
Context.Emit(Signed ? OpCodes.Ble_S : OpCodes.Ble_Un_S, LblLe);
Context.Emit(OpCodes.Pop);
Context.EmitLdc_I4(TMaxValue);
Context.EmitLdc_I8(0x8000000L);
Context.EmitSttmp();
Context.Emit(OpCodes.Br_S, LblGeEnd);
Context.MarkLabel(LblLe);
Context.Emit(OpCodes.Dup);
Context.EmitLdc_I4(TMinValue);
Context.Emit(OpCodes.Conv_I8);
Context.Emit(Signed ? OpCodes.Bge_S : OpCodes.Bge_Un_S, LblGeEnd);
Context.Emit(OpCodes.Pop);
Context.EmitLdc_I4(TMinValue);
Context.EmitLdc_I8(0x8000000L);
Context.EmitSttmp();
Context.MarkLabel(LblGeEnd);
if (Scalar)
{
EmitVectorZeroLower(Context, Op.Rd);
}
EmitVectorInsert(Context, Op.Rd, Part + Index, Op.Size);
}
if (Part == 0)
{
EmitVectorZeroUpper(Context, Op.Rd);
}
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));
}
public static void Fabd_S(AILEmitterCtx Context) public static void Fabd_S(AILEmitterCtx Context)
{ {
EmitScalarBinaryOpF(Context, () => EmitScalarBinaryOpF(Context, () =>
@ -971,6 +1049,16 @@ namespace ChocolArm64.Instruction
EmitVectorWidenRnRmBinaryOpSx(Context, () => Context.Emit(OpCodes.Mul)); EmitVectorWidenRnRmBinaryOpSx(Context, () => Context.Emit(OpCodes.Mul));
} }
public static void Sqxtn_S(AILEmitterCtx Context)
{
EmitQxtn(Context, Signed: true, Scalar: true);
}
public static void Sqxtn_V(AILEmitterCtx Context)
{
EmitQxtn(Context, Signed: true, Scalar: false);
}
public static void Sub_S(AILEmitterCtx Context) public static void Sub_S(AILEmitterCtx Context)
{ {
EmitScalarBinaryOpZx(Context, () => Context.Emit(OpCodes.Sub)); EmitScalarBinaryOpZx(Context, () => Context.Emit(OpCodes.Sub));
@ -1049,5 +1137,15 @@ namespace ChocolArm64.Instruction
{ {
EmitVectorWidenRnRmBinaryOpZx(Context, () => Context.Emit(OpCodes.Mul)); EmitVectorWidenRnRmBinaryOpZx(Context, () => Context.Emit(OpCodes.Mul));
} }
public static void Uqxtn_S(AILEmitterCtx Context)
{
EmitQxtn(Context, Signed: false, Scalar: true);
}
public static void Uqxtn_V(AILEmitterCtx Context)
{
EmitQxtn(Context, Signed: false, Scalar: false);
}
} }
} }

View file

@ -6,7 +6,7 @@ namespace ChocolArm64
static class ILGeneratorEx static class ILGeneratorEx
{ {
public static void EmitLdc_I4(this ILGenerator Generator,int Value) public static void EmitLdc_I4(this ILGenerator Generator, int Value)
{ {
switch (Value) switch (Value)
{ {

View file

@ -55,7 +55,8 @@ namespace Ryujinx.Tests.Cpu
protected void SetThreadState(ulong X0 = 0, ulong X1 = 0, ulong X2 = 0, ulong X3 = 0, ulong X31 = 0, protected void SetThreadState(ulong X0 = 0, ulong X1 = 0, ulong X2 = 0, ulong X3 = 0, ulong X31 = 0,
AVec V0 = default(AVec), AVec V1 = default(AVec), AVec V2 = default(AVec), AVec V0 = default(AVec), AVec V1 = default(AVec), AVec V2 = default(AVec),
bool Overflow = false, bool Carry = false, bool Zero = false, bool Negative = false, int Fpcr = 0x0) bool Overflow = false, bool Carry = false, bool Zero = false, bool Negative = false,
int Fpcr = 0x0, int Fpsr = 0x0)
{ {
Thread.ThreadState.X0 = X0; Thread.ThreadState.X0 = X0;
Thread.ThreadState.X1 = X1; Thread.ThreadState.X1 = X1;
@ -70,6 +71,7 @@ namespace Ryujinx.Tests.Cpu
Thread.ThreadState.Zero = Zero; Thread.ThreadState.Zero = Zero;
Thread.ThreadState.Negative = Negative; Thread.ThreadState.Negative = Negative;
Thread.ThreadState.Fpcr = Fpcr; Thread.ThreadState.Fpcr = Fpcr;
Thread.ThreadState.Fpsr = Fpsr;
} }
protected void ExecuteOpcodes() protected void ExecuteOpcodes()
@ -92,12 +94,13 @@ namespace Ryujinx.Tests.Cpu
protected AThreadState SingleOpcode(uint Opcode, protected AThreadState SingleOpcode(uint Opcode,
ulong X0 = 0, ulong X1 = 0, ulong X2 = 0, ulong X3 = 0, ulong X31 = 0, ulong X0 = 0, ulong X1 = 0, ulong X2 = 0, ulong X3 = 0, ulong X31 = 0,
AVec V0 = default(AVec), AVec V1 = default(AVec), AVec V2 = default(AVec), AVec V0 = default(AVec), AVec V1 = default(AVec), AVec V2 = default(AVec),
bool Overflow = false, bool Carry = false, bool Zero = false, bool Negative = false, int Fpcr = 0x0) bool Overflow = false, bool Carry = false, bool Zero = false, bool Negative = false,
int Fpcr = 0x0, int Fpsr = 0x0)
{ {
this.Opcode(Opcode); this.Opcode(Opcode);
this.Opcode(0xD4200000); // BRK #0 this.Opcode(0xD4200000); // BRK #0
this.Opcode(0xD65F03C0); // RET this.Opcode(0xD65F03C0); // RET
SetThreadState(X0, X1, X2, X3, X31, V0, V1, V2, Overflow, Carry, Zero, Negative, Fpcr); SetThreadState(X0, X1, X2, X3, X31, V0, V1, V2, Overflow, Carry, Zero, Negative, Fpcr, Fpsr);
ExecuteOpcodes(); ExecuteOpcodes();
return GetThreadState(); return GetThreadState();

View file

@ -26,6 +26,23 @@ namespace Ryujinx.Tests.Cpu
0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul }; 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul };
} }
private static ulong[] _1H1S1D_()
{
return new ulong[] { 0x0000000000000000ul, 0x0000000000007FFFul,
0x0000000000008000ul, 0x000000000000FFFFul,
0x000000007FFFFFFFul, 0x0000000080000000ul,
0x00000000FFFFFFFFul, 0x7FFFFFFFFFFFFFFFul,
0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul };
}
private static ulong[] _4H2S1D_()
{
return new ulong[] { 0x0000000000000000ul, 0x7FFF7FFF7FFF7FFFul,
0x8000800080008000ul, 0x7FFFFFFF7FFFFFFFul,
0x8000000080000000ul, 0x7FFFFFFFFFFFFFFFul,
0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul };
}
private static ulong[] _8B4H_() private static ulong[] _8B4H_()
{ {
return new ulong[] { 0x0000000000000000ul, 0x7F7F7F7F7F7F7F7Ful, return new ulong[] { 0x0000000000000000ul, 0x7F7F7F7F7F7F7F7Ful,
@ -64,8 +81,11 @@ namespace Ryujinx.Tests.Cpu
AArch64.V(1, new Bits(A)); AArch64.V(1, new Bits(A));
SimdFp.Abs_S(Op[23, 22], Op[9, 5], Op[4, 0]); SimdFp.Abs_S(Op[23, 22], Op[9, 5], Op[4, 0]);
Assert.Multiple(() =>
{
Assert.That(ThreadState.V0.X0, Is.EqualTo(AArch64.V(64, 0).ToUInt64())); Assert.That(ThreadState.V0.X0, Is.EqualTo(AArch64.V(64, 0).ToUInt64()));
Assert.That(ThreadState.V0.X1, Is.Zero); Assert.That(ThreadState.V0.X1, Is.Zero);
});
} }
[Test, Description("ABS <Vd>.<T>, <Vn>.<T>")] [Test, Description("ABS <Vd>.<T>, <Vn>.<T>")]
@ -83,8 +103,11 @@ namespace Ryujinx.Tests.Cpu
AArch64.V(1, new Bits(A)); AArch64.V(1, new Bits(A));
SimdFp.Abs_V(Op[30], Op[23, 22], Op[9, 5], Op[4, 0]); SimdFp.Abs_V(Op[30], Op[23, 22], Op[9, 5], Op[4, 0]);
Assert.Multiple(() =>
{
Assert.That(ThreadState.V0.X0, Is.EqualTo(AArch64.V(64, 0).ToUInt64())); Assert.That(ThreadState.V0.X0, Is.EqualTo(AArch64.V(64, 0).ToUInt64()));
Assert.That(ThreadState.V0.X1, Is.Zero); Assert.That(ThreadState.V0.X1, Is.Zero);
});
} }
[Test, Pairwise, Description("ABS <Vd>.<T>, <Vn>.<T>")] [Test, Pairwise, Description("ABS <Vd>.<T>, <Vn>.<T>")]
@ -149,8 +172,11 @@ namespace Ryujinx.Tests.Cpu
AArch64.V(1, new Bits(A)); AArch64.V(1, new Bits(A));
SimdFp.Addv_V(Op[30], Op[23, 22], Op[9, 5], Op[4, 0]); SimdFp.Addv_V(Op[30], Op[23, 22], Op[9, 5], Op[4, 0]);
Assert.Multiple(() =>
{
Assert.That(ThreadState.V0.X0, Is.EqualTo(AArch64.V(64, 0).ToUInt64())); Assert.That(ThreadState.V0.X0, Is.EqualTo(AArch64.V(64, 0).ToUInt64()));
Assert.That(ThreadState.V0.X1, Is.Zero); Assert.That(ThreadState.V0.X1, Is.Zero);
});
} }
[Test, Pairwise, Description("ADDV <V><d>, <Vn>.<T>")] [Test, Pairwise, Description("ADDV <V><d>, <Vn>.<T>")]
@ -194,8 +220,11 @@ namespace Ryujinx.Tests.Cpu
AArch64.V(1, new Bits(A)); AArch64.V(1, new Bits(A));
SimdFp.Cls_V(Op[30], Op[23, 22], Op[9, 5], Op[4, 0]); SimdFp.Cls_V(Op[30], Op[23, 22], Op[9, 5], Op[4, 0]);
Assert.Multiple(() =>
{
Assert.That(ThreadState.V0.X0, Is.EqualTo(AArch64.V(64, 0).ToUInt64())); Assert.That(ThreadState.V0.X0, Is.EqualTo(AArch64.V(64, 0).ToUInt64()));
Assert.That(ThreadState.V0.X1, Is.Zero); Assert.That(ThreadState.V0.X1, Is.Zero);
});
} }
[Test, Pairwise, Description("CLS <Vd>.<T>, <Vn>.<T>")] [Test, Pairwise, Description("CLS <Vd>.<T>, <Vn>.<T>")]
@ -236,8 +265,11 @@ namespace Ryujinx.Tests.Cpu
AArch64.V(1, new Bits(A)); AArch64.V(1, new Bits(A));
SimdFp.Clz_V(Op[30], Op[23, 22], Op[9, 5], Op[4, 0]); SimdFp.Clz_V(Op[30], Op[23, 22], Op[9, 5], Op[4, 0]);
Assert.Multiple(() =>
{
Assert.That(ThreadState.V0.X0, Is.EqualTo(AArch64.V(64, 0).ToUInt64())); Assert.That(ThreadState.V0.X0, Is.EqualTo(AArch64.V(64, 0).ToUInt64()));
Assert.That(ThreadState.V0.X1, Is.Zero); Assert.That(ThreadState.V0.X1, Is.Zero);
});
} }
[Test, Pairwise, Description("CLZ <Vd>.<T>, <Vn>.<T>")] [Test, Pairwise, Description("CLZ <Vd>.<T>, <Vn>.<T>")]
@ -276,8 +308,11 @@ namespace Ryujinx.Tests.Cpu
AArch64.V(1, new Bits(A)); AArch64.V(1, new Bits(A));
SimdFp.Neg_S(Op[23, 22], Op[9, 5], Op[4, 0]); SimdFp.Neg_S(Op[23, 22], Op[9, 5], Op[4, 0]);
Assert.Multiple(() =>
{
Assert.That(ThreadState.V0.X0, Is.EqualTo(AArch64.V(64, 0).ToUInt64())); Assert.That(ThreadState.V0.X0, Is.EqualTo(AArch64.V(64, 0).ToUInt64()));
Assert.That(ThreadState.V0.X1, Is.Zero); Assert.That(ThreadState.V0.X1, Is.Zero);
});
} }
[Test, Description("NEG <Vd>.<T>, <Vn>.<T>")] [Test, Description("NEG <Vd>.<T>, <Vn>.<T>")]
@ -295,8 +330,11 @@ namespace Ryujinx.Tests.Cpu
AArch64.V(1, new Bits(A)); AArch64.V(1, new Bits(A));
SimdFp.Neg_V(Op[30], Op[23, 22], Op[9, 5], Op[4, 0]); SimdFp.Neg_V(Op[30], Op[23, 22], Op[9, 5], Op[4, 0]);
Assert.Multiple(() =>
{
Assert.That(ThreadState.V0.X0, Is.EqualTo(AArch64.V(64, 0).ToUInt64())); Assert.That(ThreadState.V0.X0, Is.EqualTo(AArch64.V(64, 0).ToUInt64()));
Assert.That(ThreadState.V0.X1, Is.Zero); Assert.That(ThreadState.V0.X1, Is.Zero);
});
} }
[Test, Pairwise, Description("NEG <Vd>.<T>, <Vn>.<T>")] [Test, Pairwise, Description("NEG <Vd>.<T>, <Vn>.<T>")]
@ -321,6 +359,158 @@ namespace Ryujinx.Tests.Cpu
Assert.That(ThreadState.V0.X1, Is.EqualTo(AArch64.Vpart(64, 0, 1).ToUInt64())); Assert.That(ThreadState.V0.X1, Is.EqualTo(AArch64.Vpart(64, 0, 1).ToUInt64()));
}); });
} }
[Test, Description("SQXTN <Vb><d>, <Va><n>")]
public void Sqxtn_S_HB_SH_DS([ValueSource("_1H1S1D_")] [Random(1)] ulong A,
[Values(0b00u, 0b01u, 0b10u)] uint size) // <HB, SH, DS>
{
uint Opcode = 0x5E214820; // SQXTN B0, H1
Opcode |= ((size & 3) << 22);
Bits Op = new Bits(Opcode);
AVec V0 = new AVec { X0 = TestContext.CurrentContext.Random.NextULong(),
X1 = TestContext.CurrentContext.Random.NextULong() };
AVec V1 = new AVec { X0 = A };
AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1);
AArch64.Vpart(0, 0, new Bits(TestContext.CurrentContext.Random.NextULong()));
AArch64.V(1, new Bits(A));
SimdFp.Sqxtn_S(Op[23, 22], Op[9, 5], Op[4, 0]);
Assert.Multiple(() =>
{
Assert.That(ThreadState.V0.X0, Is.EqualTo(AArch64.V(64, 0).ToUInt64()));
Assert.That(ThreadState.V0.X1, Is.Zero);
});
Assert.That(((ThreadState.Fpsr >> 27) & 1) != 0, Is.EqualTo(Shared.FPSR[27])); // FIXME: Temporary solution.
}
[Test, Pairwise, Description("SQXTN{2} <Vd>.<Tb>, <Vn>.<Ta>")]
public void Sqxtn_V_8H8B_4S4H_2D2S([ValueSource("_4H2S1D_")] [Random(1)] ulong A0,
[ValueSource("_4H2S1D_")] [Random(1)] ulong A1,
[Values(0b00u, 0b01u, 0b10u)] uint size) // <8H8B, 4S4H, 2D2S>
{
uint Opcode = 0x0E214820; // SQXTN V0.8B, V1.8H
Opcode |= ((size & 3) << 22);
Bits Op = new Bits(Opcode);
AVec V0 = new AVec { X1 = TestContext.CurrentContext.Random.NextULong() };
AVec V1 = new AVec { X0 = A0, X1 = A1 };
AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1);
AArch64.Vpart(1, 0, new Bits(A0));
AArch64.Vpart(1, 1, new Bits(A1));
SimdFp.Sqxtn_V(Op[30], Op[23, 22], Op[9, 5], Op[4, 0]);
Assert.Multiple(() =>
{
Assert.That(ThreadState.V0.X0, Is.EqualTo(AArch64.V(64, 0).ToUInt64()));
Assert.That(ThreadState.V0.X1, Is.Zero);
});
Assert.That(((ThreadState.Fpsr >> 27) & 1) != 0, Is.EqualTo(Shared.FPSR[27])); // FIXME: Temporary solution.
}
[Test, Pairwise, Description("SQXTN{2} <Vd>.<Tb>, <Vn>.<Ta>")]
public void Sqxtn_V_8H16B_4S8H_2D4S([ValueSource("_4H2S1D_")] [Random(1)] ulong A0,
[ValueSource("_4H2S1D_")] [Random(1)] ulong A1,
[Values(0b00u, 0b01u, 0b10u)] uint size) // <8H16B, 4S8H, 2D4S>
{
uint Opcode = 0x4E214820; // SQXTN2 V0.16B, V1.8H
Opcode |= ((size & 3) << 22);
Bits Op = new Bits(Opcode);
ulong _X0 = TestContext.CurrentContext.Random.NextULong();
AVec V0 = new AVec { X0 = _X0 };
AVec V1 = new AVec { X0 = A0, X1 = A1 };
AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1);
AArch64.Vpart(1, 0, new Bits(A0));
AArch64.Vpart(1, 1, new Bits(A1));
SimdFp.Sqxtn_V(Op[30], Op[23, 22], Op[9, 5], Op[4, 0]);
Assert.Multiple(() =>
{
Assert.That(ThreadState.V0.X0, Is.EqualTo(_X0));
Assert.That(ThreadState.V0.X1, Is.EqualTo(AArch64.Vpart(64, 0, 1).ToUInt64()));
});
Assert.That(((ThreadState.Fpsr >> 27) & 1) != 0, Is.EqualTo(Shared.FPSR[27])); // FIXME: Temporary solution.
}
[Test, Description("UQXTN <Vb><d>, <Va><n>")]
public void Uqxtn_S_HB_SH_DS([ValueSource("_1H1S1D_")] [Random(1)] ulong A,
[Values(0b00u, 0b01u, 0b10u)] uint size) // <HB, SH, DS>
{
uint Opcode = 0x7E214820; // UQXTN B0, H1
Opcode |= ((size & 3) << 22);
Bits Op = new Bits(Opcode);
AVec V0 = new AVec { X0 = TestContext.CurrentContext.Random.NextULong(),
X1 = TestContext.CurrentContext.Random.NextULong() };
AVec V1 = new AVec { X0 = A };
AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1);
AArch64.Vpart(0, 0, new Bits(TestContext.CurrentContext.Random.NextULong()));
AArch64.V(1, new Bits(A));
SimdFp.Uqxtn_S(Op[23, 22], Op[9, 5], Op[4, 0]);
Assert.Multiple(() =>
{
Assert.That(ThreadState.V0.X0, Is.EqualTo(AArch64.V(64, 0).ToUInt64()));
Assert.That(ThreadState.V0.X1, Is.Zero);
});
Assert.That(((ThreadState.Fpsr >> 27) & 1) != 0, Is.EqualTo(Shared.FPSR[27])); // FIXME: Temporary solution.
}
[Test, Pairwise, Description("UQXTN{2} <Vd>.<Tb>, <Vn>.<Ta>")]
public void Uqxtn_V_8H8B_4S4H_2D2S([ValueSource("_4H2S1D_")] [Random(1)] ulong A0,
[ValueSource("_4H2S1D_")] [Random(1)] ulong A1,
[Values(0b00u, 0b01u, 0b10u)] uint size) // <8H8B, 4S4H, 2D2S>
{
uint Opcode = 0x2E214820; // UQXTN V0.8B, V1.8H
Opcode |= ((size & 3) << 22);
Bits Op = new Bits(Opcode);
AVec V0 = new AVec { X1 = TestContext.CurrentContext.Random.NextULong() };
AVec V1 = new AVec { X0 = A0, X1 = A1 };
AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1);
AArch64.Vpart(1, 0, new Bits(A0));
AArch64.Vpart(1, 1, new Bits(A1));
SimdFp.Uqxtn_V(Op[30], Op[23, 22], Op[9, 5], Op[4, 0]);
Assert.Multiple(() =>
{
Assert.That(ThreadState.V0.X0, Is.EqualTo(AArch64.V(64, 0).ToUInt64()));
Assert.That(ThreadState.V0.X1, Is.Zero);
});
Assert.That(((ThreadState.Fpsr >> 27) & 1) != 0, Is.EqualTo(Shared.FPSR[27])); // FIXME: Temporary solution.
}
[Test, Pairwise, Description("UQXTN{2} <Vd>.<Tb>, <Vn>.<Ta>")]
public void Uqxtn_V_8H16B_4S8H_2D4S([ValueSource("_4H2S1D_")] [Random(1)] ulong A0,
[ValueSource("_4H2S1D_")] [Random(1)] ulong A1,
[Values(0b00u, 0b01u, 0b10u)] uint size) // <8H16B, 4S8H, 2D4S>
{
uint Opcode = 0x6E214820; // UQXTN2 V0.16B, V1.8H
Opcode |= ((size & 3) << 22);
Bits Op = new Bits(Opcode);
ulong _X0 = TestContext.CurrentContext.Random.NextULong();
AVec V0 = new AVec { X0 = _X0 };
AVec V1 = new AVec { X0 = A0, X1 = A1 };
AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1);
AArch64.Vpart(1, 0, new Bits(A0));
AArch64.Vpart(1, 1, new Bits(A1));
SimdFp.Uqxtn_V(Op[30], Op[23, 22], Op[9, 5], Op[4, 0]);
Assert.Multiple(() =>
{
Assert.That(ThreadState.V0.X0, Is.EqualTo(_X0));
Assert.That(ThreadState.V0.X1, Is.EqualTo(AArch64.Vpart(64, 0, 1).ToUInt64()));
});
Assert.That(((ThreadState.Fpsr >> 27) & 1) != 0, Is.EqualTo(Shared.FPSR[27])); // FIXME: Temporary solution.
}
#endif #endif
} }
} }

View file

@ -74,8 +74,11 @@ namespace Ryujinx.Tests.Cpu
AArch64.V(2, new Bits(B)); AArch64.V(2, new Bits(B));
SimdFp.Add_S(Op[23, 22], Op[20, 16], Op[9, 5], Op[4, 0]); SimdFp.Add_S(Op[23, 22], Op[20, 16], Op[9, 5], Op[4, 0]);
Assert.Multiple(() =>
{
Assert.That(ThreadState.V0.X0, Is.EqualTo(AArch64.V(64, 0).ToUInt64())); Assert.That(ThreadState.V0.X0, Is.EqualTo(AArch64.V(64, 0).ToUInt64()));
Assert.That(ThreadState.V0.X1, Is.Zero); Assert.That(ThreadState.V0.X1, Is.Zero);
});
} }
[Test, Description("ADD <Vd>.<T>, <Vn>.<T>, <Vm>.<T>")] [Test, Description("ADD <Vd>.<T>, <Vn>.<T>, <Vm>.<T>")]
@ -96,8 +99,11 @@ namespace Ryujinx.Tests.Cpu
AArch64.V(2, new Bits(B)); AArch64.V(2, new Bits(B));
SimdFp.Add_V(Op[30], Op[23, 22], Op[20, 16], Op[9, 5], Op[4, 0]); SimdFp.Add_V(Op[30], Op[23, 22], Op[20, 16], Op[9, 5], Op[4, 0]);
Assert.Multiple(() =>
{
Assert.That(ThreadState.V0.X0, Is.EqualTo(AArch64.V(64, 0).ToUInt64())); Assert.That(ThreadState.V0.X0, Is.EqualTo(AArch64.V(64, 0).ToUInt64()));
Assert.That(ThreadState.V0.X1, Is.Zero); Assert.That(ThreadState.V0.X1, Is.Zero);
});
} }
[Test, Pairwise, Description("ADD <Vd>.<T>, <Vn>.<T>, <Vm>.<T>")] [Test, Pairwise, Description("ADD <Vd>.<T>, <Vn>.<T>, <Vm>.<T>")]
@ -205,8 +211,11 @@ namespace Ryujinx.Tests.Cpu
AArch64.V(2, new Bits(B)); AArch64.V(2, new Bits(B));
SimdFp.Addp_V(Op[30], Op[23, 22], Op[20, 16], Op[9, 5], Op[4, 0]); SimdFp.Addp_V(Op[30], Op[23, 22], Op[20, 16], Op[9, 5], Op[4, 0]);
Assert.Multiple(() =>
{
Assert.That(ThreadState.V0.X0, Is.EqualTo(AArch64.V(64, 0).ToUInt64())); Assert.That(ThreadState.V0.X0, Is.EqualTo(AArch64.V(64, 0).ToUInt64()));
Assert.That(ThreadState.V0.X1, Is.Zero); Assert.That(ThreadState.V0.X1, Is.Zero);
});
} }
[Test, Pairwise, Description("ADDP <Vd>.<T>, <Vn>.<T>, <Vm>.<T>")] [Test, Pairwise, Description("ADDP <Vd>.<T>, <Vn>.<T>, <Vm>.<T>")]
@ -253,8 +262,11 @@ namespace Ryujinx.Tests.Cpu
AArch64.V(2, new Bits(B)); AArch64.V(2, new Bits(B));
SimdFp.And_V(Op[30], Op[20, 16], Op[9, 5], Op[4, 0]); SimdFp.And_V(Op[30], Op[20, 16], Op[9, 5], Op[4, 0]);
Assert.Multiple(() =>
{
Assert.That(ThreadState.V0.X0, Is.EqualTo(AArch64.V(64, 0).ToUInt64())); Assert.That(ThreadState.V0.X0, Is.EqualTo(AArch64.V(64, 0).ToUInt64()));
Assert.That(ThreadState.V0.X1, Is.Zero); Assert.That(ThreadState.V0.X1, Is.Zero);
});
} }
[Test, Pairwise, Description("AND <Vd>.<T>, <Vn>.<T>, <Vm>.<T>")] [Test, Pairwise, Description("AND <Vd>.<T>, <Vn>.<T>, <Vm>.<T>")]
@ -299,8 +311,11 @@ namespace Ryujinx.Tests.Cpu
AArch64.V(2, new Bits(B)); AArch64.V(2, new Bits(B));
SimdFp.Bic_V(Op[30], Op[20, 16], Op[9, 5], Op[4, 0]); SimdFp.Bic_V(Op[30], Op[20, 16], Op[9, 5], Op[4, 0]);
Assert.Multiple(() =>
{
Assert.That(ThreadState.V0.X0, Is.EqualTo(AArch64.V(64, 0).ToUInt64())); Assert.That(ThreadState.V0.X0, Is.EqualTo(AArch64.V(64, 0).ToUInt64()));
Assert.That(ThreadState.V0.X1, Is.Zero); Assert.That(ThreadState.V0.X1, Is.Zero);
});
} }
[Test, Pairwise, Description("BIC <Vd>.<T>, <Vn>.<T>, <Vm>.<T>")] [Test, Pairwise, Description("BIC <Vd>.<T>, <Vn>.<T>, <Vm>.<T>")]
@ -347,8 +362,11 @@ namespace Ryujinx.Tests.Cpu
AArch64.V(2, new Bits(B)); AArch64.V(2, new Bits(B));
SimdFp.Bif_V(Op[30], Op[20, 16], Op[9, 5], Op[4, 0]); SimdFp.Bif_V(Op[30], Op[20, 16], Op[9, 5], Op[4, 0]);
Assert.Multiple(() =>
{
Assert.That(ThreadState.V0.X0, Is.EqualTo(AArch64.V(64, 0).ToUInt64())); Assert.That(ThreadState.V0.X0, Is.EqualTo(AArch64.V(64, 0).ToUInt64()));
Assert.That(ThreadState.V0.X1, Is.Zero); Assert.That(ThreadState.V0.X1, Is.Zero);
});
} }
[Test, Pairwise, Description("BIF <Vd>.<T>, <Vn>.<T>, <Vm>.<T>")] [Test, Pairwise, Description("BIF <Vd>.<T>, <Vn>.<T>, <Vm>.<T>")]
@ -400,8 +418,11 @@ namespace Ryujinx.Tests.Cpu
AArch64.V(2, new Bits(B)); AArch64.V(2, new Bits(B));
SimdFp.Bit_V(Op[30], Op[20, 16], Op[9, 5], Op[4, 0]); SimdFp.Bit_V(Op[30], Op[20, 16], Op[9, 5], Op[4, 0]);
Assert.Multiple(() =>
{
Assert.That(ThreadState.V0.X0, Is.EqualTo(AArch64.V(64, 0).ToUInt64())); Assert.That(ThreadState.V0.X0, Is.EqualTo(AArch64.V(64, 0).ToUInt64()));
Assert.That(ThreadState.V0.X1, Is.Zero); Assert.That(ThreadState.V0.X1, Is.Zero);
});
} }
[Test, Pairwise, Description("BIT <Vd>.<T>, <Vn>.<T>, <Vm>.<T>")] [Test, Pairwise, Description("BIT <Vd>.<T>, <Vn>.<T>, <Vm>.<T>")]
@ -453,8 +474,11 @@ namespace Ryujinx.Tests.Cpu
AArch64.V(2, new Bits(B)); AArch64.V(2, new Bits(B));
SimdFp.Bsl_V(Op[30], Op[20, 16], Op[9, 5], Op[4, 0]); SimdFp.Bsl_V(Op[30], Op[20, 16], Op[9, 5], Op[4, 0]);
Assert.Multiple(() =>
{
Assert.That(ThreadState.V0.X0, Is.EqualTo(AArch64.V(64, 0).ToUInt64())); Assert.That(ThreadState.V0.X0, Is.EqualTo(AArch64.V(64, 0).ToUInt64()));
Assert.That(ThreadState.V0.X1, Is.Zero); Assert.That(ThreadState.V0.X1, Is.Zero);
});
} }
[Test, Pairwise, Description("BSL <Vd>.<T>, <Vn>.<T>, <Vm>.<T>")] [Test, Pairwise, Description("BSL <Vd>.<T>, <Vn>.<T>, <Vm>.<T>")]
@ -504,8 +528,11 @@ namespace Ryujinx.Tests.Cpu
AArch64.V(2, new Bits(B)); AArch64.V(2, new Bits(B));
SimdFp.Orn_V(Op[30], Op[20, 16], Op[9, 5], Op[4, 0]); SimdFp.Orn_V(Op[30], Op[20, 16], Op[9, 5], Op[4, 0]);
Assert.Multiple(() =>
{
Assert.That(ThreadState.V0.X0, Is.EqualTo(AArch64.V(64, 0).ToUInt64())); Assert.That(ThreadState.V0.X0, Is.EqualTo(AArch64.V(64, 0).ToUInt64()));
Assert.That(ThreadState.V0.X1, Is.Zero); Assert.That(ThreadState.V0.X1, Is.Zero);
});
} }
[Test, Pairwise, Description("ORN <Vd>.<T>, <Vn>.<T>, <Vm>.<T>")] [Test, Pairwise, Description("ORN <Vd>.<T>, <Vn>.<T>, <Vm>.<T>")]
@ -550,8 +577,11 @@ namespace Ryujinx.Tests.Cpu
AArch64.V(2, new Bits(B)); AArch64.V(2, new Bits(B));
SimdFp.Orr_V(Op[30], Op[20, 16], Op[9, 5], Op[4, 0]); SimdFp.Orr_V(Op[30], Op[20, 16], Op[9, 5], Op[4, 0]);
Assert.Multiple(() =>
{
Assert.That(ThreadState.V0.X0, Is.EqualTo(AArch64.V(64, 0).ToUInt64())); Assert.That(ThreadState.V0.X0, Is.EqualTo(AArch64.V(64, 0).ToUInt64()));
Assert.That(ThreadState.V0.X1, Is.Zero); Assert.That(ThreadState.V0.X1, Is.Zero);
});
} }
[Test, Pairwise, Description("ORR <Vd>.<T>, <Vn>.<T>, <Vm>.<T>")] [Test, Pairwise, Description("ORR <Vd>.<T>, <Vn>.<T>, <Vm>.<T>")]
@ -714,8 +744,11 @@ namespace Ryujinx.Tests.Cpu
AArch64.V(2, new Bits(B)); AArch64.V(2, new Bits(B));
SimdFp.Sub_S(Op[23, 22], Op[20, 16], Op[9, 5], Op[4, 0]); SimdFp.Sub_S(Op[23, 22], Op[20, 16], Op[9, 5], Op[4, 0]);
Assert.Multiple(() =>
{
Assert.That(ThreadState.V0.X0, Is.EqualTo(AArch64.V(64, 0).ToUInt64())); Assert.That(ThreadState.V0.X0, Is.EqualTo(AArch64.V(64, 0).ToUInt64()));
Assert.That(ThreadState.V0.X1, Is.Zero); Assert.That(ThreadState.V0.X1, Is.Zero);
});
} }
[Test, Description("SUB <Vd>.<T>, <Vn>.<T>, <Vm>.<T>")] [Test, Description("SUB <Vd>.<T>, <Vn>.<T>, <Vm>.<T>")]
@ -736,8 +769,11 @@ namespace Ryujinx.Tests.Cpu
AArch64.V(2, new Bits(B)); AArch64.V(2, new Bits(B));
SimdFp.Sub_V(Op[30], Op[23, 22], Op[20, 16], Op[9, 5], Op[4, 0]); SimdFp.Sub_V(Op[30], Op[23, 22], Op[20, 16], Op[9, 5], Op[4, 0]);
Assert.Multiple(() =>
{
Assert.That(ThreadState.V0.X0, Is.EqualTo(AArch64.V(64, 0).ToUInt64())); Assert.That(ThreadState.V0.X0, Is.EqualTo(AArch64.V(64, 0).ToUInt64()));
Assert.That(ThreadState.V0.X1, Is.Zero); Assert.That(ThreadState.V0.X1, Is.Zero);
});
} }
[Test, Pairwise, Description("SUB <Vd>.<T>, <Vn>.<T>, <Vm>.<T>")] [Test, Pairwise, Description("SUB <Vd>.<T>, <Vn>.<T>, <Vm>.<T>")]

View file

@ -21,6 +21,7 @@ namespace Ryujinx.Tests.Cpu.Tester
/* Decode */ /* Decode */
int d = (int)UInt(Rd); int d = (int)UInt(Rd);
int n = (int)UInt(Rn); int n = (int)UInt(Rn);
int datasize = (sf ? 64 : 32); int datasize = (sf ? 64 : 32);
/* Operation */ /* Operation */
@ -37,6 +38,7 @@ namespace Ryujinx.Tests.Cpu.Tester
/* Decode */ /* Decode */
int d = (int)UInt(Rd); int d = (int)UInt(Rd);
int n = (int)UInt(Rn); int n = (int)UInt(Rn);
int datasize = (sf ? 64 : 32); int datasize = (sf ? 64 : 32);
/* Operation */ /* Operation */
@ -53,6 +55,7 @@ namespace Ryujinx.Tests.Cpu.Tester
/* Decode */ /* Decode */
int d = (int)UInt(Rd); int d = (int)UInt(Rd);
int n = (int)UInt(Rn); int n = (int)UInt(Rn);
int datasize = (sf ? 64 : 32); int datasize = (sf ? 64 : 32);
/* Operation */ /* Operation */
@ -75,6 +78,7 @@ namespace Ryujinx.Tests.Cpu.Tester
/* Decode */ /* Decode */
int d = (int)UInt(Rd); int d = (int)UInt(Rd);
int n = (int)UInt(Rn); int n = (int)UInt(Rn);
int datasize = (sf ? 64 : 32); int datasize = (sf ? 64 : 32);
int container_size = 16; int container_size = 16;
@ -113,6 +117,7 @@ namespace Ryujinx.Tests.Cpu.Tester
/* Decode */ /* Decode */
int d = (int)UInt(Rd); int d = (int)UInt(Rd);
int n = (int)UInt(Rn); int n = (int)UInt(Rn);
int datasize = (sf ? 64 : 32); int datasize = (sf ? 64 : 32);
int container_size = 32; int container_size = 32;
@ -187,6 +192,7 @@ namespace Ryujinx.Tests.Cpu.Tester
/* Decode */ /* Decode */
int d = (int)UInt(Rd); int d = (int)UInt(Rd);
int n = (int)UInt(Rn); int n = (int)UInt(Rn);
int datasize = (sf ? 64 : 32); int datasize = (sf ? 64 : 32);
Bits imm; Bits imm;
@ -225,6 +231,7 @@ namespace Ryujinx.Tests.Cpu.Tester
/* Decode */ /* Decode */
int d = (int)UInt(Rd); int d = (int)UInt(Rd);
int n = (int)UInt(Rn); int n = (int)UInt(Rn);
int datasize = (sf ? 64 : 32); int datasize = (sf ? 64 : 32);
Bits imm; Bits imm;
@ -259,6 +266,7 @@ namespace Ryujinx.Tests.Cpu.Tester
/* Decode */ /* Decode */
int d = (int)UInt(Rd); int d = (int)UInt(Rd);
int n = (int)UInt(Rn); int n = (int)UInt(Rn);
int datasize = (sf ? 64 : 32); int datasize = (sf ? 64 : 32);
Bits imm; Bits imm;
@ -288,6 +296,7 @@ namespace Ryujinx.Tests.Cpu.Tester
/* Decode */ /* Decode */
int d = (int)UInt(Rd); int d = (int)UInt(Rd);
int n = (int)UInt(Rn); int n = (int)UInt(Rn);
int datasize = (sf ? 64 : 32); int datasize = (sf ? 64 : 32);
Bits imm; Bits imm;
@ -312,6 +321,7 @@ namespace Ryujinx.Tests.Cpu.Tester
/* Decode */ /* Decode */
int d = (int)UInt(Rd); int d = (int)UInt(Rd);
int n = (int)UInt(Rn); int n = (int)UInt(Rn);
int datasize = (sf ? 64 : 32); int datasize = (sf ? 64 : 32);
Bits imm; Bits imm;
@ -341,6 +351,7 @@ namespace Ryujinx.Tests.Cpu.Tester
/* Decode */ /* Decode */
int d = (int)UInt(Rd); int d = (int)UInt(Rd);
int n = (int)UInt(Rn); int n = (int)UInt(Rn);
int datasize = (sf ? 64 : 32); int datasize = (sf ? 64 : 32);
Bits imm; Bits imm;
@ -370,6 +381,7 @@ namespace Ryujinx.Tests.Cpu.Tester
/* Decode */ /* Decode */
int d = (int)UInt(Rd); int d = (int)UInt(Rd);
int n = (int)UInt(Rn); int n = (int)UInt(Rn);
int datasize = (sf ? 64 : 32); int datasize = (sf ? 64 : 32);
Bits imm; Bits imm;
@ -409,6 +421,7 @@ namespace Ryujinx.Tests.Cpu.Tester
/* Decode */ /* Decode */
int d = (int)UInt(Rd); int d = (int)UInt(Rd);
int n = (int)UInt(Rn); int n = (int)UInt(Rn);
int datasize = (sf ? 64 : 32); int datasize = (sf ? 64 : 32);
Bits imm; Bits imm;
@ -447,6 +460,7 @@ namespace Ryujinx.Tests.Cpu.Tester
int d = (int)UInt(Rd); int d = (int)UInt(Rd);
int n = (int)UInt(Rn); int n = (int)UInt(Rn);
int m = (int)UInt(Rm); int m = (int)UInt(Rm);
int datasize = (sf ? 64 : 32); int datasize = (sf ? 64 : 32);
/* Operation */ /* Operation */
@ -466,6 +480,7 @@ namespace Ryujinx.Tests.Cpu.Tester
int d = (int)UInt(Rd); int d = (int)UInt(Rd);
int n = (int)UInt(Rn); int n = (int)UInt(Rn);
int m = (int)UInt(Rm); int m = (int)UInt(Rm);
int datasize = (sf ? 64 : 32); int datasize = (sf ? 64 : 32);
/* Operation */ /* Operation */
@ -488,6 +503,7 @@ namespace Ryujinx.Tests.Cpu.Tester
int d = (int)UInt(Rd); int d = (int)UInt(Rd);
int n = (int)UInt(Rn); int n = (int)UInt(Rn);
int m = (int)UInt(Rm); int m = (int)UInt(Rm);
int datasize = (sf ? 64 : 32); int datasize = (sf ? 64 : 32);
/* if shift == '11' then ReservedValue(); */ /* if shift == '11' then ReservedValue(); */
@ -513,6 +529,7 @@ namespace Ryujinx.Tests.Cpu.Tester
int d = (int)UInt(Rd); int d = (int)UInt(Rd);
int n = (int)UInt(Rn); int n = (int)UInt(Rn);
int m = (int)UInt(Rm); int m = (int)UInt(Rm);
int datasize = (sf ? 64 : 32); int datasize = (sf ? 64 : 32);
/* if shift == '11' then ReservedValue(); */ /* if shift == '11' then ReservedValue(); */
@ -541,6 +558,7 @@ namespace Ryujinx.Tests.Cpu.Tester
int d = (int)UInt(Rd); int d = (int)UInt(Rd);
int n = (int)UInt(Rn); int n = (int)UInt(Rn);
int m = (int)UInt(Rm); int m = (int)UInt(Rm);
int datasize = (sf ? 64 : 32); int datasize = (sf ? 64 : 32);
/* if sf == '0' && imm6<5> == '1' then ReservedValue(); */ /* if sf == '0' && imm6<5> == '1' then ReservedValue(); */
@ -564,6 +582,7 @@ namespace Ryujinx.Tests.Cpu.Tester
int d = (int)UInt(Rd); int d = (int)UInt(Rd);
int n = (int)UInt(Rn); int n = (int)UInt(Rn);
int m = (int)UInt(Rm); int m = (int)UInt(Rm);
int datasize = (sf ? 64 : 32); int datasize = (sf ? 64 : 32);
/* if sf == '0' && imm6<5> == '1' then ReservedValue(); */ /* if sf == '0' && imm6<5> == '1' then ReservedValue(); */
@ -591,6 +610,7 @@ namespace Ryujinx.Tests.Cpu.Tester
int d = (int)UInt(Rd); int d = (int)UInt(Rd);
int n = (int)UInt(Rn); int n = (int)UInt(Rn);
int m = (int)UInt(Rm); int m = (int)UInt(Rm);
int datasize = (sf ? 64 : 32); int datasize = (sf ? 64 : 32);
ShiftType shift_type = DecodeShift(op2); ShiftType shift_type = DecodeShift(op2);
@ -610,6 +630,7 @@ namespace Ryujinx.Tests.Cpu.Tester
int d = (int)UInt(Rd); int d = (int)UInt(Rd);
int n = (int)UInt(Rn); int n = (int)UInt(Rn);
int m = (int)UInt(Rm); int m = (int)UInt(Rm);
int datasize = (sf ? 64 : 32); int datasize = (sf ? 64 : 32);
/* if sf == '0' && imm6<5> == '1' then ReservedValue(); */ /* if sf == '0' && imm6<5> == '1' then ReservedValue(); */
@ -635,6 +656,7 @@ namespace Ryujinx.Tests.Cpu.Tester
int d = (int)UInt(Rd); int d = (int)UInt(Rd);
int n = (int)UInt(Rn); int n = (int)UInt(Rn);
int m = (int)UInt(Rm); int m = (int)UInt(Rm);
int datasize = (sf ? 64 : 32); int datasize = (sf ? 64 : 32);
/* if sf == '0' && imm6<5> == '1' then ReservedValue(); */ /* if sf == '0' && imm6<5> == '1' then ReservedValue(); */
@ -716,6 +738,7 @@ namespace Ryujinx.Tests.Cpu.Tester
int d = (int)UInt(Rd); int d = (int)UInt(Rd);
int n = (int)UInt(Rn); int n = (int)UInt(Rn);
int m = (int)UInt(Rm); int m = (int)UInt(Rm);
int datasize = (sf ? 64 : 32); int datasize = (sf ? 64 : 32);
/* if sf == '0' && imm6<5> == '1' then ReservedValue(); */ /* if sf == '0' && imm6<5> == '1' then ReservedValue(); */
@ -741,6 +764,7 @@ namespace Ryujinx.Tests.Cpu.Tester
int d = (int)UInt(Rd); int d = (int)UInt(Rd);
int n = (int)UInt(Rn); int n = (int)UInt(Rn);
int m = (int)UInt(Rm); int m = (int)UInt(Rm);
int datasize = (sf ? 64 : 32); int datasize = (sf ? 64 : 32);
/* if sf == '0' && imm6<5> == '1' then ReservedValue(); */ /* if sf == '0' && imm6<5> == '1' then ReservedValue(); */
@ -764,6 +788,7 @@ namespace Ryujinx.Tests.Cpu.Tester
int d = (int)UInt(Rd); int d = (int)UInt(Rd);
int n = (int)UInt(Rn); int n = (int)UInt(Rn);
int m = (int)UInt(Rm); int m = (int)UInt(Rm);
int datasize = (sf ? 64 : 32); int datasize = (sf ? 64 : 32);
/* if N != sf then UnallocatedEncoding(); */ /* if N != sf then UnallocatedEncoding(); */
@ -790,6 +815,7 @@ namespace Ryujinx.Tests.Cpu.Tester
int d = (int)UInt(Rd); int d = (int)UInt(Rd);
int n = (int)UInt(Rn); int n = (int)UInt(Rn);
int m = (int)UInt(Rm); int m = (int)UInt(Rm);
int datasize = (sf ? 64 : 32); int datasize = (sf ? 64 : 32);
ShiftType shift_type = DecodeShift(op2); ShiftType shift_type = DecodeShift(op2);
@ -811,6 +837,7 @@ namespace Ryujinx.Tests.Cpu.Tester
int d = (int)UInt(Rd); int d = (int)UInt(Rd);
int n = (int)UInt(Rn); int n = (int)UInt(Rn);
int m = (int)UInt(Rm); int m = (int)UInt(Rm);
int datasize = (sf ? 64 : 32); int datasize = (sf ? 64 : 32);
ShiftType shift_type = DecodeShift(op2); ShiftType shift_type = DecodeShift(op2);
@ -830,6 +857,7 @@ namespace Ryujinx.Tests.Cpu.Tester
int d = (int)UInt(Rd); int d = (int)UInt(Rd);
int n = (int)UInt(Rn); int n = (int)UInt(Rn);
int m = (int)UInt(Rm); int m = (int)UInt(Rm);
int datasize = (sf ? 64 : 32); int datasize = (sf ? 64 : 32);
/* if sf == '0' && imm6<5> == '1' then ReservedValue(); */ /* if sf == '0' && imm6<5> == '1' then ReservedValue(); */
@ -855,6 +883,7 @@ namespace Ryujinx.Tests.Cpu.Tester
int d = (int)UInt(Rd); int d = (int)UInt(Rd);
int n = (int)UInt(Rn); int n = (int)UInt(Rn);
int m = (int)UInt(Rm); int m = (int)UInt(Rm);
int datasize = (sf ? 64 : 32); int datasize = (sf ? 64 : 32);
/* if sf == '0' && imm6<5> == '1' then ReservedValue(); */ /* if sf == '0' && imm6<5> == '1' then ReservedValue(); */
@ -880,6 +909,7 @@ namespace Ryujinx.Tests.Cpu.Tester
int d = (int)UInt(Rd); int d = (int)UInt(Rd);
int n = (int)UInt(Rn); int n = (int)UInt(Rn);
int m = (int)UInt(Rm); int m = (int)UInt(Rm);
int datasize = (sf ? 64 : 32); int datasize = (sf ? 64 : 32);
ShiftType shift_type = DecodeShift(op2); ShiftType shift_type = DecodeShift(op2);
@ -899,6 +929,7 @@ namespace Ryujinx.Tests.Cpu.Tester
int d = (int)UInt(Rd); int d = (int)UInt(Rd);
int n = (int)UInt(Rn); int n = (int)UInt(Rn);
int m = (int)UInt(Rm); int m = (int)UInt(Rm);
int datasize = (sf ? 64 : 32); int datasize = (sf ? 64 : 32);
/* Operation */ /* Operation */
@ -920,6 +951,7 @@ namespace Ryujinx.Tests.Cpu.Tester
int d = (int)UInt(Rd); int d = (int)UInt(Rd);
int n = (int)UInt(Rn); int n = (int)UInt(Rn);
int m = (int)UInt(Rm); int m = (int)UInt(Rm);
int datasize = (sf ? 64 : 32); int datasize = (sf ? 64 : 32);
/* Operation */ /* Operation */
@ -944,6 +976,7 @@ namespace Ryujinx.Tests.Cpu.Tester
int d = (int)UInt(Rd); int d = (int)UInt(Rd);
int n = (int)UInt(Rn); int n = (int)UInt(Rn);
int m = (int)UInt(Rm); int m = (int)UInt(Rm);
int datasize = (sf ? 64 : 32); int datasize = (sf ? 64 : 32);
/* Operation */ /* Operation */
@ -970,6 +1003,7 @@ namespace Ryujinx.Tests.Cpu.Tester
int d = (int)UInt(Rd); int d = (int)UInt(Rd);
int n = (int)UInt(Rn); int n = (int)UInt(Rn);
int m = (int)UInt(Rm); int m = (int)UInt(Rm);
int datasize = (sf ? 64 : 32); int datasize = (sf ? 64 : 32);
/* if shift == '11' then ReservedValue(); */ /* if shift == '11' then ReservedValue(); */
@ -997,6 +1031,7 @@ namespace Ryujinx.Tests.Cpu.Tester
int d = (int)UInt(Rd); int d = (int)UInt(Rd);
int n = (int)UInt(Rn); int n = (int)UInt(Rn);
int m = (int)UInt(Rm); int m = (int)UInt(Rm);
int datasize = (sf ? 64 : 32); int datasize = (sf ? 64 : 32);
/* if shift == '11' then ReservedValue(); */ /* if shift == '11' then ReservedValue(); */
@ -1027,6 +1062,7 @@ namespace Ryujinx.Tests.Cpu.Tester
int d = (int)UInt(Rd); int d = (int)UInt(Rd);
int n = (int)UInt(Rn); int n = (int)UInt(Rn);
int m = (int)UInt(Rm); int m = (int)UInt(Rm);
int datasize = (sf ? 64 : 32); int datasize = (sf ? 64 : 32);
/* Operation */ /* Operation */
@ -1055,6 +1091,7 @@ namespace Ryujinx.Tests.Cpu.Tester
int d = (int)UInt(Rd); int d = (int)UInt(Rd);
int n = (int)UInt(Rn); int n = (int)UInt(Rn);
int m = (int)UInt(Rm); int m = (int)UInt(Rm);
int datasize = (sf ? 64 : 32); int datasize = (sf ? 64 : 32);
ExtendType extend_type = DecodeRegExtend(option); ExtendType extend_type = DecodeRegExtend(option);
@ -1086,6 +1123,7 @@ namespace Ryujinx.Tests.Cpu.Tester
int d = (int)UInt(Rd); int d = (int)UInt(Rd);
int n = (int)UInt(Rn); int n = (int)UInt(Rn);
int m = (int)UInt(Rm); int m = (int)UInt(Rm);
int datasize = (sf ? 64 : 32); int datasize = (sf ? 64 : 32);
ExtendType extend_type = DecodeRegExtend(option); ExtendType extend_type = DecodeRegExtend(option);
@ -1113,6 +1151,7 @@ namespace Ryujinx.Tests.Cpu.Tester
int d = (int)UInt(Rd); int d = (int)UInt(Rd);
int n = (int)UInt(Rn); int n = (int)UInt(Rn);
int m = (int)UInt(Rm); int m = (int)UInt(Rm);
int datasize = (sf ? 64 : 32); int datasize = (sf ? 64 : 32);
ExtendType extend_type = DecodeRegExtend(option); ExtendType extend_type = DecodeRegExtend(option);
@ -1146,6 +1185,7 @@ namespace Ryujinx.Tests.Cpu.Tester
int d = (int)UInt(Rd); int d = (int)UInt(Rd);
int n = (int)UInt(Rn); int n = (int)UInt(Rn);
int m = (int)UInt(Rm); int m = (int)UInt(Rm);
int datasize = (sf ? 64 : 32); int datasize = (sf ? 64 : 32);
ExtendType extend_type = DecodeRegExtend(option); ExtendType extend_type = DecodeRegExtend(option);
@ -1176,6 +1216,7 @@ namespace Ryujinx.Tests.Cpu.Tester
/* Decode */ /* Decode */
int d = (int)UInt(Rd); int d = (int)UInt(Rd);
int n = (int)UInt(Rn); int n = (int)UInt(Rn);
int datasize = (sf ? 64 : 32); int datasize = (sf ? 64 : 32);
int R; int R;
@ -1205,6 +1246,7 @@ namespace Ryujinx.Tests.Cpu.Tester
/* Decode */ /* Decode */
int d = (int)UInt(Rd); int d = (int)UInt(Rd);
int n = (int)UInt(Rn); int n = (int)UInt(Rn);
int datasize = (sf ? 64 : 32); int datasize = (sf ? 64 : 32);
int R; int R;
@ -1238,6 +1280,7 @@ namespace Ryujinx.Tests.Cpu.Tester
/* Decode */ /* Decode */
int d = (int)UInt(Rd); int d = (int)UInt(Rd);
int n = (int)UInt(Rn); int n = (int)UInt(Rn);
int datasize = (sf ? 64 : 32); int datasize = (sf ? 64 : 32);
int R; int R;
@ -1267,6 +1310,7 @@ namespace Ryujinx.Tests.Cpu.Tester
{ {
/* Decode */ /* Decode */
int n = (int)UInt(Rn); int n = (int)UInt(Rn);
int datasize = (sf ? 64 : 32); int datasize = (sf ? 64 : 32);
Bits flags = nzcv; Bits flags = nzcv;
@ -1288,6 +1332,7 @@ namespace Ryujinx.Tests.Cpu.Tester
{ {
/* Decode */ /* Decode */
int n = (int)UInt(Rn); int n = (int)UInt(Rn);
int datasize = (sf ? 64 : 32); int datasize = (sf ? 64 : 32);
Bits flags = nzcv; Bits flags = nzcv;
@ -1314,6 +1359,7 @@ namespace Ryujinx.Tests.Cpu.Tester
/* Decode */ /* Decode */
int n = (int)UInt(Rn); int n = (int)UInt(Rn);
int m = (int)UInt(Rm); int m = (int)UInt(Rm);
int datasize = (sf ? 64 : 32); int datasize = (sf ? 64 : 32);
Bits flags = nzcv; Bits flags = nzcv;
@ -1336,6 +1382,7 @@ namespace Ryujinx.Tests.Cpu.Tester
/* Decode */ /* Decode */
int n = (int)UInt(Rn); int n = (int)UInt(Rn);
int m = (int)UInt(Rm); int m = (int)UInt(Rm);
int datasize = (sf ? 64 : 32); int datasize = (sf ? 64 : 32);
Bits flags = nzcv; Bits flags = nzcv;
@ -1362,6 +1409,7 @@ namespace Ryujinx.Tests.Cpu.Tester
int d = (int)UInt(Rd); int d = (int)UInt(Rd);
int n = (int)UInt(Rn); int n = (int)UInt(Rn);
int m = (int)UInt(Rm); int m = (int)UInt(Rm);
int datasize = (sf ? 64 : 32); int datasize = (sf ? 64 : 32);
/* Operation */ /* Operation */
@ -1388,6 +1436,7 @@ namespace Ryujinx.Tests.Cpu.Tester
int d = (int)UInt(Rd); int d = (int)UInt(Rd);
int n = (int)UInt(Rn); int n = (int)UInt(Rn);
int m = (int)UInt(Rm); int m = (int)UInt(Rm);
int datasize = (sf ? 64 : 32); int datasize = (sf ? 64 : 32);
/* Operation */ /* Operation */
@ -1414,6 +1463,7 @@ namespace Ryujinx.Tests.Cpu.Tester
int d = (int)UInt(Rd); int d = (int)UInt(Rd);
int n = (int)UInt(Rn); int n = (int)UInt(Rn);
int m = (int)UInt(Rm); int m = (int)UInt(Rm);
int datasize = (sf ? 64 : 32); int datasize = (sf ? 64 : 32);
/* Operation */ /* Operation */
@ -1440,6 +1490,7 @@ namespace Ryujinx.Tests.Cpu.Tester
int d = (int)UInt(Rd); int d = (int)UInt(Rd);
int n = (int)UInt(Rn); int n = (int)UInt(Rn);
int m = (int)UInt(Rm); int m = (int)UInt(Rm);
int datasize = (sf ? 64 : 32); int datasize = (sf ? 64 : 32);
/* Operation */ /* Operation */
@ -1467,6 +1518,7 @@ namespace Ryujinx.Tests.Cpu.Tester
{ {
/* Decode */ /* Decode */
int d = (int)UInt(Rd); int d = (int)UInt(Rd);
int datasize = (sf ? 64 : 32); int datasize = (sf ? 64 : 32);
/* if sf == '0' && hw<1> == '1' then UnallocatedEncoding(); */ /* if sf == '0' && hw<1> == '1' then UnallocatedEncoding(); */
@ -1486,6 +1538,7 @@ namespace Ryujinx.Tests.Cpu.Tester
{ {
/* Decode */ /* Decode */
int d = (int)UInt(Rd); int d = (int)UInt(Rd);
int datasize = (sf ? 64 : 32); int datasize = (sf ? 64 : 32);
/* if sf == '0' && hw<1> == '1' then UnallocatedEncoding(); */ /* if sf == '0' && hw<1> == '1' then UnallocatedEncoding(); */
@ -1506,6 +1559,7 @@ namespace Ryujinx.Tests.Cpu.Tester
{ {
/* Decode */ /* Decode */
int d = (int)UInt(Rd); int d = (int)UInt(Rd);
int datasize = (sf ? 64 : 32); int datasize = (sf ? 64 : 32);
/* if sf == '0' && hw<1> == '1' then UnallocatedEncoding(); */ /* if sf == '0' && hw<1> == '1' then UnallocatedEncoding(); */
@ -1530,6 +1584,7 @@ namespace Ryujinx.Tests.Cpu.Tester
int n = (int)UInt(Rn); int n = (int)UInt(Rn);
int m = (int)UInt(Rm); int m = (int)UInt(Rm);
int a = (int)UInt(Ra); int a = (int)UInt(Ra);
int datasize = (sf ? 64 : 32); int datasize = (sf ? 64 : 32);
/* Operation */ /* Operation */
@ -1550,6 +1605,7 @@ namespace Ryujinx.Tests.Cpu.Tester
int n = (int)UInt(Rn); int n = (int)UInt(Rn);
int m = (int)UInt(Rm); int m = (int)UInt(Rm);
int a = (int)UInt(Ra); int a = (int)UInt(Ra);
int datasize = (sf ? 64 : 32); int datasize = (sf ? 64 : 32);
/* Operation */ /* Operation */
@ -1983,6 +2039,182 @@ namespace Ryujinx.Tests.Cpu.Tester
V(d, result); V(d, result);
} }
// https://meriac.github.io/archex/A64_v83A_ISA/sqxtn_advsimd.xml#SQXTN_asisdmisc_N
public static void Sqxtn_S(Bits size, Bits Rn, Bits Rd)
{
bool U = false;
/* Decode Scalar */
int d = (int)UInt(Rd);
int n = (int)UInt(Rn);
/* if size == '11' then ReservedValue(); */
int esize = 8 << (int)UInt(size);
int datasize = esize;
int part = 0;
int elements = 1;
bool unsigned = (U == true);
/* Operation */
/* CheckFPAdvSIMDEnabled64(); */
Bits result = new Bits(datasize);
Bits operand = V(2 * datasize, n);
Bits element;
bool sat;
for (int e = 0; e <= elements - 1; e++)
{
element = Elem(operand, e, 2 * esize);
(Bits _result, bool _sat) = SatQ(Int(element, unsigned), esize, unsigned);
Elem(result, e, esize, _result);
sat = _sat;
if (sat)
{
/* FPSR.QC = '1'; */
FPSR[27] = true; // FIXME: Temporary solution.
}
}
Vpart(d, part, result);
}
// https://meriac.github.io/archex/A64_v83A_ISA/sqxtn_advsimd.xml#SQXTN_asimdmisc_N
public static void Sqxtn_V(bool Q, Bits size, Bits Rn, Bits Rd)
{
bool U = false;
/* Decode Vector */
int d = (int)UInt(Rd);
int n = (int)UInt(Rn);
/* if size == '11' then ReservedValue(); */
int esize = 8 << (int)UInt(size);
int datasize = 64;
int part = (int)UInt(Q);
int elements = datasize / esize;
bool unsigned = (U == true);
/* Operation */
/* CheckFPAdvSIMDEnabled64(); */
Bits result = new Bits(datasize);
Bits operand = V(2 * datasize, n);
Bits element;
bool sat;
for (int e = 0; e <= elements - 1; e++)
{
element = Elem(operand, e, 2 * esize);
(Bits _result, bool _sat) = SatQ(Int(element, unsigned), esize, unsigned);
Elem(result, e, esize, _result);
sat = _sat;
if (sat)
{
/* FPSR.QC = '1'; */
FPSR[27] = true; // FIXME: Temporary solution.
}
}
Vpart(d, part, result);
}
// https://meriac.github.io/archex/A64_v83A_ISA/uqxtn_advsimd.xml#UQXTN_asisdmisc_N
public static void Uqxtn_S(Bits size, Bits Rn, Bits Rd)
{
bool U = true;
/* Decode Scalar */
int d = (int)UInt(Rd);
int n = (int)UInt(Rn);
/* if size == '11' then ReservedValue(); */
int esize = 8 << (int)UInt(size);
int datasize = esize;
int part = 0;
int elements = 1;
bool unsigned = (U == true);
/* Operation */
/* CheckFPAdvSIMDEnabled64(); */
Bits result = new Bits(datasize);
Bits operand = V(2 * datasize, n);
Bits element;
bool sat;
for (int e = 0; e <= elements - 1; e++)
{
element = Elem(operand, e, 2 * esize);
(Bits _result, bool _sat) = SatQ(Int(element, unsigned), esize, unsigned);
Elem(result, e, esize, _result);
sat = _sat;
if (sat)
{
/* FPSR.QC = '1'; */
FPSR[27] = true; // FIXME: Temporary solution.
}
}
Vpart(d, part, result);
}
// https://meriac.github.io/archex/A64_v83A_ISA/uqxtn_advsimd.xml#UQXTN_asimdmisc_N
public static void Uqxtn_V(bool Q, Bits size, Bits Rn, Bits Rd)
{
bool U = true;
/* Decode Vector */
int d = (int)UInt(Rd);
int n = (int)UInt(Rn);
/* if size == '11' then ReservedValue(); */
int esize = 8 << (int)UInt(size);
int datasize = 64;
int part = (int)UInt(Q);
int elements = datasize / esize;
bool unsigned = (U == true);
/* Operation */
/* CheckFPAdvSIMDEnabled64(); */
Bits result = new Bits(datasize);
Bits operand = V(2 * datasize, n);
Bits element;
bool sat;
for (int e = 0; e <= elements - 1; e++)
{
element = Elem(operand, e, 2 * esize);
(Bits _result, bool _sat) = SatQ(Int(element, unsigned), esize, unsigned);
Elem(result, e, esize, _result);
sat = _sat;
if (sat)
{
/* FPSR.QC = '1'; */
FPSR[27] = true; // FIXME: Temporary solution.
}
}
Vpart(d, part, result);
}
#endregion #endregion
#region "SimdReg" #region "SimdReg"

View file

@ -104,6 +104,8 @@ namespace Ryujinx.Tests.Cpu.Tester
SP_EL0.SetAll(false); SP_EL0.SetAll(false);
/* SP_EL1 = bits(64) UNKNOWN; */ /* SP_EL1 = bits(64) UNKNOWN; */
SP_EL1.SetAll(false); SP_EL1.SetAll(false);
FPSR.SetAll(false); // FIXME: Temporary solution.
} }
// #impl-aarch64.SP.write.0 // #impl-aarch64.SP.write.0
@ -518,6 +520,8 @@ namespace Ryujinx.Tests.Cpu.Tester
SP_EL0 = new Bits(64, false); SP_EL0 = new Bits(64, false);
SP_EL1 = new Bits(64, false); SP_EL1 = new Bits(64, false);
FPSR = new Bits(32, false); // FIXME: Temporary solution.
PSTATE.N = false; PSTATE.N = false;
PSTATE.Z = false; PSTATE.Z = false;
PSTATE.C = false; PSTATE.C = false;
@ -1016,6 +1020,8 @@ namespace Ryujinx.Tests.Cpu.Tester
public static Bits SP_EL0; public static Bits SP_EL0;
public static Bits SP_EL1; public static Bits SP_EL1;
public static Bits FPSR; // FIXME: Temporary solution.
#endregion #endregion
#region "functions/system/" #region "functions/system/"
@ -1081,6 +1087,7 @@ namespace Ryujinx.Tests.Cpu.Tester
return true; // EL1 and EL0 must exist return true; // EL1 and EL0 must exist
} }
/* return boolean IMPLEMENTATION_DEFINED; */
return false; return false;
} }
@ -1113,5 +1120,65 @@ namespace Ryujinx.Tests.Cpu.Tester
public bool SP; // Stack pointer select: 0=SP0, 1=SPx [AArch64 only] public bool SP; // Stack pointer select: 0=SP0, 1=SPx [AArch64 only]
} }
#endregion #endregion
#region "functions/vector/"
// #impl-shared.SatQ.3
public static (Bits, bool) SatQ(BigInteger i, int N, bool unsigned)
{
(Bits result, bool sat) = (unsigned ? UnsignedSatQ(i, N) : SignedSatQ(i, N));
return (result, sat);
}
// #impl-shared.SignedSatQ.2
public static (Bits, bool) SignedSatQ(BigInteger i, int N)
{
BigInteger result;
bool saturated;
if (i > BigInteger.Pow(2, N - 1) - 1)
{
result = BigInteger.Pow(2, N - 1) - 1;
saturated = true;
}
else if (i < -(BigInteger.Pow(2, N - 1)))
{
result = -(BigInteger.Pow(2, N - 1));
saturated = true;
}
else
{
result = i;
saturated = false;
}
return (result.SubBigInteger(N - 1, 0), saturated);
}
// #impl-shared.UnsignedSatQ.2
public static (Bits, bool) UnsignedSatQ(BigInteger i, int N)
{
BigInteger result;
bool saturated;
if (i > BigInteger.Pow(2, N) - 1)
{
result = BigInteger.Pow(2, N) - 1;
saturated = true;
}
else if (i < 0)
{
result = 0;
saturated = true;
}
else
{
result = i;
saturated = false;
}
return (result.SubBigInteger(N - 1, 0), saturated);
}
#endregion
} }
} }