commit
b03d4d7866
78 changed files with 5353 additions and 786 deletions
|
@ -255,6 +255,10 @@ namespace ChocolArm64
|
|||
SetA64("x00111100x110000000000xxxxxxxxxx", AInstEmit.Fcvtms_Gp, typeof(AOpCodeSimdCvt));
|
||||
SetA64("x00111100x110001000000xxxxxxxxxx", AInstEmit.Fcvtmu_Gp, typeof(AOpCodeSimdCvt));
|
||||
SetA64("0x0011100x100001011010xxxxxxxxxx", AInstEmit.Fcvtn_V, typeof(AOpCodeSimd));
|
||||
SetA64("010111100x100001101010xxxxxxxxxx", AInstEmit.Fcvtns_S, typeof(AOpCodeSimd));
|
||||
SetA64("0>0011100<100001101010xxxxxxxxxx", AInstEmit.Fcvtns_V, typeof(AOpCodeSimd));
|
||||
SetA64("011111100x100001101010xxxxxxxxxx", AInstEmit.Fcvtnu_S, typeof(AOpCodeSimd));
|
||||
SetA64("0>1011100<100001101010xxxxxxxxxx", AInstEmit.Fcvtnu_V, typeof(AOpCodeSimd));
|
||||
SetA64("x00111100x101000000000xxxxxxxxxx", AInstEmit.Fcvtps_Gp, typeof(AOpCodeSimdCvt));
|
||||
SetA64("x00111100x101001000000xxxxxxxxxx", AInstEmit.Fcvtpu_Gp, typeof(AOpCodeSimdCvt));
|
||||
SetA64("x00111100x111000000000xxxxxxxxxx", AInstEmit.Fcvtzs_Gp, typeof(AOpCodeSimdCvt));
|
||||
|
@ -365,6 +369,7 @@ namespace ChocolArm64
|
|||
SetA64("0x001110<<1xxxxx011101xxxxxxxxxx", AInstEmit.Sabd_V, typeof(AOpCodeSimdReg));
|
||||
SetA64("0x001110<<1xxxxx011100xxxxxxxxxx", AInstEmit.Sabdl_V, typeof(AOpCodeSimdReg));
|
||||
SetA64("0x001110<<100000011010xxxxxxxxxx", AInstEmit.Sadalp_V, typeof(AOpCodeSimd));
|
||||
SetA64("0x001110<<1xxxxx000000xxxxxxxxxx", AInstEmit.Saddl_V, typeof(AOpCodeSimdReg));
|
||||
SetA64("0x001110<<100000001010xxxxxxxxxx", AInstEmit.Saddlp_V, typeof(AOpCodeSimd));
|
||||
SetA64("0x001110<<1xxxxx000100xxxxxxxxxx", AInstEmit.Saddw_V, typeof(AOpCodeSimdReg));
|
||||
SetA64("x0011110xx100010000000xxxxxxxxxx", AInstEmit.Scvtf_Gp, typeof(AOpCodeSimdCvt));
|
||||
|
@ -374,10 +379,12 @@ namespace ChocolArm64
|
|||
SetA64("01011110000xxxxx010100xxxxxxxxxx", AInstEmit.Sha256h2_V, typeof(AOpCodeSimdReg));
|
||||
SetA64("0101111000101000001010xxxxxxxxxx", AInstEmit.Sha256su0_V, typeof(AOpCodeSimd));
|
||||
SetA64("01011110000xxxxx011000xxxxxxxxxx", AInstEmit.Sha256su1_V, typeof(AOpCodeSimdReg));
|
||||
SetA64("0x001110<<1xxxxx000001xxxxxxxxxx", AInstEmit.Shadd_V, typeof(AOpCodeSimdReg));
|
||||
SetA64("010111110>>>>xxx010101xxxxxxxxxx", AInstEmit.Shl_S, typeof(AOpCodeSimdShImm));
|
||||
SetA64("0x0011110>>>>xxx010101xxxxxxxxxx", AInstEmit.Shl_V, typeof(AOpCodeSimdShImm));
|
||||
SetA64("0x101110<<100001001110xxxxxxxxxx", AInstEmit.Shll_V, typeof(AOpCodeSimd));
|
||||
SetA64("0x00111100>>>xxx100001xxxxxxxxxx", AInstEmit.Shrn_V, typeof(AOpCodeSimdShImm));
|
||||
SetA64("0x001110<<1xxxxx001001xxxxxxxxxx", AInstEmit.Shsub_V, typeof(AOpCodeSimdReg));
|
||||
SetA64("0x1011110>>>>xxx010101xxxxxxxxxx", AInstEmit.Sli_V, typeof(AOpCodeSimdShImm));
|
||||
SetA64("0x001110<<1xxxxx011001xxxxxxxxxx", AInstEmit.Smax_V, typeof(AOpCodeSimdReg));
|
||||
SetA64("0x001110<<1xxxxx101001xxxxxxxxxx", AInstEmit.Smaxp_V, typeof(AOpCodeSimdReg));
|
||||
|
@ -407,6 +414,7 @@ namespace ChocolArm64
|
|||
SetA64("0x001110<<100001010010xxxxxxxxxx", AInstEmit.Sqxtn_V, typeof(AOpCodeSimd));
|
||||
SetA64("01111110<<100001001010xxxxxxxxxx", AInstEmit.Sqxtun_S, typeof(AOpCodeSimd));
|
||||
SetA64("0x101110<<100001001010xxxxxxxxxx", AInstEmit.Sqxtun_V, typeof(AOpCodeSimd));
|
||||
SetA64("0x001110<<1xxxxx000101xxxxxxxxxx", AInstEmit.Srhadd_V, typeof(AOpCodeSimdReg));
|
||||
SetA64("0x00111100>>>xxx001001xxxxxxxxxx", AInstEmit.Srshr_V, typeof(AOpCodeSimdShImm));
|
||||
SetA64("0100111101xxxxxx001001xxxxxxxxxx", AInstEmit.Srshr_V, typeof(AOpCodeSimdShImm));
|
||||
SetA64("0>001110<<1xxxxx010001xxxxxxxxxx", AInstEmit.Sshl_V, typeof(AOpCodeSimdReg));
|
||||
|
@ -416,6 +424,7 @@ namespace ChocolArm64
|
|||
SetA64("0100111101xxxxxx000001xxxxxxxxxx", AInstEmit.Sshr_V, typeof(AOpCodeSimdShImm));
|
||||
SetA64("0x00111100>>>xxx000101xxxxxxxxxx", AInstEmit.Ssra_V, typeof(AOpCodeSimdShImm));
|
||||
SetA64("0100111101xxxxxx000101xxxxxxxxxx", AInstEmit.Ssra_V, typeof(AOpCodeSimdShImm));
|
||||
SetA64("0x001110<<1xxxxx001000xxxxxxxxxx", AInstEmit.Ssubl_V, typeof(AOpCodeSimdReg));
|
||||
SetA64("0x001110<<1xxxxx001100xxxxxxxxxx", AInstEmit.Ssubw_V, typeof(AOpCodeSimdReg));
|
||||
SetA64("0x00110000000000xxxxxxxxxxxxxxxx", AInstEmit.St__Vms, typeof(AOpCodeSimdMemMs));
|
||||
SetA64("0x001100100xxxxxxxxxxxxxxxxxxxxx", AInstEmit.St__Vms, typeof(AOpCodeSimdMemMs));
|
||||
|
@ -449,10 +458,13 @@ namespace ChocolArm64
|
|||
SetA64("011111100x100001110110xxxxxxxxxx", AInstEmit.Ucvtf_S, typeof(AOpCodeSimd));
|
||||
SetA64("0x1011100x100001110110xxxxxxxxxx", AInstEmit.Ucvtf_V, typeof(AOpCodeSimd));
|
||||
SetA64("0x101110<<1xxxxx000001xxxxxxxxxx", AInstEmit.Uhadd_V, typeof(AOpCodeSimdReg));
|
||||
SetA64("0x101110<<1xxxxx001001xxxxxxxxxx", AInstEmit.Uhsub_V, typeof(AOpCodeSimdReg));
|
||||
SetA64("0x101110<<1xxxxx011001xxxxxxxxxx", AInstEmit.Umax_V, typeof(AOpCodeSimdReg));
|
||||
SetA64("0x101110<<1xxxxx101001xxxxxxxxxx", AInstEmit.Umaxp_V, typeof(AOpCodeSimdReg));
|
||||
SetA64("0x101110<<1xxxxx011011xxxxxxxxxx", AInstEmit.Umin_V, typeof(AOpCodeSimdReg));
|
||||
SetA64("0x101110<<1xxxxx101011xxxxxxxxxx", AInstEmit.Uminp_V, typeof(AOpCodeSimdReg));
|
||||
SetA64("0x101110<<1xxxxx100000xxxxxxxxxx", AInstEmit.Umlal_V, typeof(AOpCodeSimdReg));
|
||||
SetA64("0x101110<<1xxxxx101000xxxxxxxxxx", AInstEmit.Umlsl_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));
|
||||
|
@ -461,6 +473,7 @@ namespace ChocolArm64
|
|||
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("0x101110<<1xxxxx000101xxxxxxxxxx", AInstEmit.Urhadd_V, typeof(AOpCodeSimdReg));
|
||||
SetA64("0>101110<<1xxxxx010001xxxxxxxxxx", AInstEmit.Ushl_V, typeof(AOpCodeSimdReg));
|
||||
SetA64("0x10111100>>>xxx101001xxxxxxxxxx", AInstEmit.Ushll_V, typeof(AOpCodeSimdShImm));
|
||||
SetA64("0111111101xxxxxx000001xxxxxxxxxx", AInstEmit.Ushr_S, typeof(AOpCodeSimdShImm));
|
||||
|
@ -470,6 +483,7 @@ namespace ChocolArm64
|
|||
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<<1xxxxx001000xxxxxxxxxx", AInstEmit.Usubl_V, typeof(AOpCodeSimdReg));
|
||||
SetA64("0x101110<<1xxxxx001100xxxxxxxxxx", AInstEmit.Usubw_V, typeof(AOpCodeSimdReg));
|
||||
SetA64("0>001110<<0xxxxx000110xxxxxxxxxx", AInstEmit.Uzp1_V, typeof(AOpCodeSimdReg));
|
||||
SetA64("0>001110<<0xxxxx010110xxxxxxxxxx", AInstEmit.Uzp2_V, typeof(AOpCodeSimdReg));
|
||||
|
|
|
@ -1032,6 +1032,11 @@ namespace ChocolArm64.Instruction
|
|||
EmitAddLongPairwise(Context, Signed: true, Accumulate: true);
|
||||
}
|
||||
|
||||
public static void Saddl_V(AILEmitterCtx Context)
|
||||
{
|
||||
EmitVectorWidenRnRmBinaryOpSx(Context, () => Context.Emit(OpCodes.Add));
|
||||
}
|
||||
|
||||
public static void Saddlp_V(AILEmitterCtx Context)
|
||||
{
|
||||
EmitAddLongPairwise(Context, Signed: true, Accumulate: false);
|
||||
|
@ -1042,6 +1047,28 @@ namespace ChocolArm64.Instruction
|
|||
EmitVectorWidenRmBinaryOpSx(Context, () => Context.Emit(OpCodes.Add));
|
||||
}
|
||||
|
||||
public static void Shadd_V(AILEmitterCtx Context)
|
||||
{
|
||||
EmitVectorBinaryOpSx(Context, () =>
|
||||
{
|
||||
Context.Emit(OpCodes.Add);
|
||||
|
||||
Context.Emit(OpCodes.Ldc_I4_1);
|
||||
Context.Emit(OpCodes.Shr);
|
||||
});
|
||||
}
|
||||
|
||||
public static void Shsub_V(AILEmitterCtx Context)
|
||||
{
|
||||
EmitVectorBinaryOpSx(Context, () =>
|
||||
{
|
||||
Context.Emit(OpCodes.Sub);
|
||||
|
||||
Context.Emit(OpCodes.Ldc_I4_1);
|
||||
Context.Emit(OpCodes.Shr);
|
||||
});
|
||||
}
|
||||
|
||||
public static void Smax_V(AILEmitterCtx Context)
|
||||
{
|
||||
Type[] Types = new Type[] { typeof(long), typeof(long) };
|
||||
|
@ -1181,6 +1208,25 @@ namespace ChocolArm64.Instruction
|
|||
EmitVectorSaturatingNarrowOpSxZx(Context, () => { });
|
||||
}
|
||||
|
||||
public static void Srhadd_V(AILEmitterCtx Context)
|
||||
{
|
||||
EmitVectorBinaryOpSx(Context, () =>
|
||||
{
|
||||
Context.Emit(OpCodes.Add);
|
||||
|
||||
Context.Emit(OpCodes.Ldc_I4_1);
|
||||
Context.Emit(OpCodes.Add);
|
||||
|
||||
Context.Emit(OpCodes.Ldc_I4_1);
|
||||
Context.Emit(OpCodes.Shr);
|
||||
});
|
||||
}
|
||||
|
||||
public static void Ssubl_V(AILEmitterCtx Context)
|
||||
{
|
||||
EmitVectorWidenRnRmBinaryOpSx(Context, () => Context.Emit(OpCodes.Sub));
|
||||
}
|
||||
|
||||
public static void Ssubw_V(AILEmitterCtx Context)
|
||||
{
|
||||
EmitVectorWidenRmBinaryOpSx(Context, () => Context.Emit(OpCodes.Sub));
|
||||
|
@ -1303,12 +1349,40 @@ namespace ChocolArm64.Instruction
|
|||
{
|
||||
Context.Emit(OpCodes.Add);
|
||||
|
||||
Context.EmitLdc_I4(1);
|
||||
|
||||
Context.Emit(OpCodes.Ldc_I4_1);
|
||||
Context.Emit(OpCodes.Shr_Un);
|
||||
});
|
||||
}
|
||||
|
||||
public static void Uhsub_V(AILEmitterCtx Context)
|
||||
{
|
||||
EmitVectorBinaryOpZx(Context, () =>
|
||||
{
|
||||
Context.Emit(OpCodes.Sub);
|
||||
|
||||
Context.Emit(OpCodes.Ldc_I4_1);
|
||||
Context.Emit(OpCodes.Shr_Un);
|
||||
});
|
||||
}
|
||||
|
||||
public static void Umax_V(AILEmitterCtx Context)
|
||||
{
|
||||
Type[] Types = new Type[] { typeof(ulong), typeof(ulong) };
|
||||
|
||||
MethodInfo MthdInfo = typeof(Math).GetMethod(nameof(Math.Max), Types);
|
||||
|
||||
EmitVectorBinaryOpZx(Context, () => Context.EmitCall(MthdInfo));
|
||||
}
|
||||
|
||||
public static void Umaxp_V(AILEmitterCtx Context)
|
||||
{
|
||||
Type[] Types = new Type[] { typeof(ulong), typeof(ulong) };
|
||||
|
||||
MethodInfo MthdInfo = typeof(Math).GetMethod(nameof(Math.Max), Types);
|
||||
|
||||
EmitVectorPairwiseOpZx(Context, () => Context.EmitCall(MthdInfo));
|
||||
}
|
||||
|
||||
public static void Umin_V(AILEmitterCtx Context)
|
||||
{
|
||||
Type[] Types = new Type[] { typeof(ulong), typeof(ulong) };
|
||||
|
@ -1327,22 +1401,22 @@ namespace ChocolArm64.Instruction
|
|||
EmitVectorPairwiseOpZx(Context, () => Context.EmitCall(MthdInfo));
|
||||
}
|
||||
|
||||
public static void Umax_V(AILEmitterCtx Context)
|
||||
public static void Umlal_V(AILEmitterCtx Context)
|
||||
{
|
||||
Type[] Types = new Type[] { typeof(ulong), typeof(ulong) };
|
||||
|
||||
MethodInfo MthdInfo = typeof(Math).GetMethod(nameof(Math.Max), Types);
|
||||
|
||||
EmitVectorBinaryOpZx(Context, () => Context.EmitCall(MthdInfo));
|
||||
EmitVectorWidenRnRmTernaryOpZx(Context, () =>
|
||||
{
|
||||
Context.Emit(OpCodes.Mul);
|
||||
Context.Emit(OpCodes.Add);
|
||||
});
|
||||
}
|
||||
|
||||
public static void Umaxp_V(AILEmitterCtx Context)
|
||||
public static void Umlsl_V(AILEmitterCtx Context)
|
||||
{
|
||||
Type[] Types = new Type[] { typeof(ulong), typeof(ulong) };
|
||||
|
||||
MethodInfo MthdInfo = typeof(Math).GetMethod(nameof(Math.Max), Types);
|
||||
|
||||
EmitVectorPairwiseOpZx(Context, () => Context.EmitCall(MthdInfo));
|
||||
EmitVectorWidenRnRmTernaryOpZx(Context, () =>
|
||||
{
|
||||
Context.Emit(OpCodes.Mul);
|
||||
Context.Emit(OpCodes.Sub);
|
||||
});
|
||||
}
|
||||
|
||||
public static void Umull_V(AILEmitterCtx Context)
|
||||
|
@ -1380,6 +1454,20 @@ namespace ChocolArm64.Instruction
|
|||
EmitVectorSaturatingNarrowOpZxZx(Context, () => { });
|
||||
}
|
||||
|
||||
public static void Urhadd_V(AILEmitterCtx Context)
|
||||
{
|
||||
EmitVectorBinaryOpZx(Context, () =>
|
||||
{
|
||||
Context.Emit(OpCodes.Add);
|
||||
|
||||
Context.Emit(OpCodes.Ldc_I4_1);
|
||||
Context.Emit(OpCodes.Add);
|
||||
|
||||
Context.Emit(OpCodes.Ldc_I4_1);
|
||||
Context.Emit(OpCodes.Shr_Un);
|
||||
});
|
||||
}
|
||||
|
||||
public static void Usqadd_S(AILEmitterCtx Context)
|
||||
{
|
||||
EmitScalarSaturatingBinaryOpZx(Context, SaturatingFlags.Accumulate);
|
||||
|
@ -1390,6 +1478,11 @@ namespace ChocolArm64.Instruction
|
|||
EmitVectorSaturatingBinaryOpZx(Context, SaturatingFlags.Accumulate);
|
||||
}
|
||||
|
||||
public static void Usubl_V(AILEmitterCtx Context)
|
||||
{
|
||||
EmitVectorWidenRnRmBinaryOpZx(Context, () => Context.Emit(OpCodes.Sub));
|
||||
}
|
||||
|
||||
public static void Usubw_V(AILEmitterCtx Context)
|
||||
{
|
||||
EmitVectorWidenRmBinaryOpZx(Context, () => Context.Emit(OpCodes.Sub));
|
||||
|
|
|
@ -106,6 +106,26 @@ namespace ChocolArm64.Instruction
|
|||
}
|
||||
}
|
||||
|
||||
public static void Fcvtns_S(AILEmitterCtx Context)
|
||||
{
|
||||
EmitFcvtn(Context, Signed: true, Scalar: true);
|
||||
}
|
||||
|
||||
public static void Fcvtns_V(AILEmitterCtx Context)
|
||||
{
|
||||
EmitFcvtn(Context, Signed: true, Scalar: false);
|
||||
}
|
||||
|
||||
public static void Fcvtnu_S(AILEmitterCtx Context)
|
||||
{
|
||||
EmitFcvtn(Context, Signed: false, Scalar: true);
|
||||
}
|
||||
|
||||
public static void Fcvtnu_V(AILEmitterCtx Context)
|
||||
{
|
||||
EmitFcvtn(Context, Signed: false, Scalar: false);
|
||||
}
|
||||
|
||||
public static void Fcvtps_Gp(AILEmitterCtx Context)
|
||||
{
|
||||
EmitFcvt_s_Gp(Context, () => EmitUnaryMathCall(Context, nameof(Math.Ceiling)));
|
||||
|
@ -250,6 +270,54 @@ namespace ChocolArm64.Instruction
|
|||
}
|
||||
}
|
||||
|
||||
private static void EmitFcvtn(AILEmitterCtx Context, bool Signed, bool Scalar)
|
||||
{
|
||||
AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp;
|
||||
|
||||
int SizeF = Op.Size & 1;
|
||||
int SizeI = SizeF + 2;
|
||||
|
||||
int Bytes = Op.GetBitsCount() >> 3;
|
||||
int Elems = !Scalar ? Bytes >> SizeI : 1;
|
||||
|
||||
if (Scalar && (SizeF == 0))
|
||||
{
|
||||
EmitVectorZeroLowerTmp(Context);
|
||||
}
|
||||
|
||||
for (int Index = 0; Index < Elems; Index++)
|
||||
{
|
||||
EmitVectorExtractF(Context, Op.Rn, Index, SizeF);
|
||||
|
||||
EmitRoundMathCall(Context, MidpointRounding.ToEven);
|
||||
|
||||
if (SizeF == 0)
|
||||
{
|
||||
AVectorHelper.EmitCall(Context, Signed
|
||||
? nameof(AVectorHelper.SatF32ToS32)
|
||||
: nameof(AVectorHelper.SatF32ToU32));
|
||||
|
||||
Context.Emit(OpCodes.Conv_U8);
|
||||
}
|
||||
else /* if (SizeF == 1) */
|
||||
{
|
||||
AVectorHelper.EmitCall(Context, Signed
|
||||
? nameof(AVectorHelper.SatF64ToS64)
|
||||
: nameof(AVectorHelper.SatF64ToU64));
|
||||
}
|
||||
|
||||
EmitVectorInsertTmp(Context, Index, SizeI);
|
||||
}
|
||||
|
||||
Context.EmitLdvectmp();
|
||||
Context.EmitStvec(Op.Rd);
|
||||
|
||||
if ((Op.RegisterSize == ARegisterSize.SIMD64) || Scalar)
|
||||
{
|
||||
EmitVectorZeroUpper(Context, Op.Rd);
|
||||
}
|
||||
}
|
||||
|
||||
private static void EmitFcvt_s_Gp(AILEmitterCtx Context, Action Emit)
|
||||
{
|
||||
EmitFcvt___Gp(Context, Emit, true);
|
||||
|
@ -569,4 +637,4 @@ namespace ChocolArm64.Instruction
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,6 +7,8 @@
|
|||
public bool Enabled;
|
||||
public int Stride;
|
||||
public long VboKey;
|
||||
public bool Instanced;
|
||||
public int Divisor;
|
||||
public GalVertexAttrib[] Attribs;
|
||||
}
|
||||
|
||||
|
@ -22,6 +24,8 @@
|
|||
public float FlipX;
|
||||
public float FlipY;
|
||||
|
||||
public int Instance;
|
||||
|
||||
public GalFrontFace FrontFace;
|
||||
|
||||
public bool CullFaceEnabled;
|
||||
|
|
|
@ -4,6 +4,7 @@ namespace Ryujinx.Graphics.Gal
|
|||
{
|
||||
R32G32B32A32 = 0x1,
|
||||
R16G16B16A16 = 0x3,
|
||||
R32G32 = 0x4,
|
||||
A8B8G8R8 = 0x8,
|
||||
A2B10G10R10 = 0x9,
|
||||
R32 = 0xf,
|
||||
|
@ -24,6 +25,7 @@ namespace Ryujinx.Graphics.Gal
|
|||
BC5 = 0x28,
|
||||
Z24S8 = 0x29,
|
||||
ZF32 = 0x2f,
|
||||
ZF32_X24S8 = 0x30,
|
||||
Astc2D4x4 = 0x40,
|
||||
Astc2D5x5 = 0x41,
|
||||
Astc2D6x6 = 0x42,
|
||||
|
|
|
@ -18,6 +18,8 @@ namespace Ryujinx.Graphics.Gal
|
|||
|
||||
void Set(byte[] Data, int Width, int Height);
|
||||
|
||||
void SetMap(int[] Map);
|
||||
|
||||
void SetTransform(bool FlipX, bool FlipY, int Top, int Left, int Right, int Bottom);
|
||||
|
||||
void SetWindowSize(int Width, int Height);
|
||||
|
|
|
@ -11,8 +11,6 @@ namespace Ryujinx.Graphics.Gal
|
|||
IEnumerable<ShaderDeclInfo> GetConstBufferUsage(long Key);
|
||||
IEnumerable<ShaderDeclInfo> GetTextureUsage(long Key);
|
||||
|
||||
void EnsureTextureBinding(string UniformName, int Value);
|
||||
|
||||
void Bind(long Key);
|
||||
|
||||
void Unbind(GalShaderType Type);
|
||||
|
|
|
@ -53,6 +53,7 @@ namespace Ryujinx.Graphics.Gal
|
|||
case GalTextureFormat.BC4: return GalImageFormat.BC4_UNORM_BLOCK;
|
||||
case GalTextureFormat.BC5: return GalImageFormat.BC5_UNORM_BLOCK;
|
||||
case GalTextureFormat.Z24S8: return GalImageFormat.D24_UNORM_S8_UINT;
|
||||
case GalTextureFormat.ZF32_X24S8: return GalImageFormat.D32_SFLOAT_S8_UINT;
|
||||
case GalTextureFormat.Astc2D4x4: return GalImageFormat.ASTC_4x4_UNORM_BLOCK;
|
||||
case GalTextureFormat.Astc2D5x5: return GalImageFormat.ASTC_5x5_UNORM_BLOCK;
|
||||
case GalTextureFormat.Astc2D6x6: return GalImageFormat.ASTC_6x6_UNORM_BLOCK;
|
||||
|
@ -75,6 +76,7 @@ namespace Ryujinx.Graphics.Gal
|
|||
{
|
||||
case GalTextureFormat.R32G32B32A32: return GalImageFormat.R32G32B32A32_SINT;
|
||||
case GalTextureFormat.R16G16B16A16: return GalImageFormat.R16G16B16A16_SINT;
|
||||
case GalTextureFormat.R32G32: return GalImageFormat.R32G32_SINT;
|
||||
case GalTextureFormat.A8B8G8R8: return GalImageFormat.A8B8G8R8_SINT_PACK32;
|
||||
case GalTextureFormat.A2B10G10R10: return GalImageFormat.A2B10G10R10_SINT_PACK32;
|
||||
case GalTextureFormat.R32: return GalImageFormat.R32_SINT;
|
||||
|
@ -89,6 +91,7 @@ namespace Ryujinx.Graphics.Gal
|
|||
{
|
||||
case GalTextureFormat.R32G32B32A32: return GalImageFormat.R32G32B32A32_UINT;
|
||||
case GalTextureFormat.R16G16B16A16: return GalImageFormat.R16G16B16A16_UINT;
|
||||
case GalTextureFormat.R32G32: return GalImageFormat.R32G32_UINT;
|
||||
case GalTextureFormat.A8B8G8R8: return GalImageFormat.A8B8G8R8_UINT_PACK32;
|
||||
case GalTextureFormat.A2B10G10R10: return GalImageFormat.A2B10G10R10_UINT_PACK32;
|
||||
case GalTextureFormat.R32: return GalImageFormat.R32_UINT;
|
||||
|
@ -111,6 +114,7 @@ namespace Ryujinx.Graphics.Gal
|
|||
{
|
||||
case GalTextureFormat.R32G32B32A32: return GalImageFormat.R32G32B32A32_SFLOAT;
|
||||
case GalTextureFormat.R16G16B16A16: return GalImageFormat.R16G16B16A16_SFLOAT;
|
||||
case GalTextureFormat.R32G32: return GalImageFormat.R32G32_SFLOAT;
|
||||
case GalTextureFormat.R32: return GalImageFormat.R32_SFLOAT;
|
||||
case GalTextureFormat.BC6H_SF16: return GalImageFormat.BC6H_SFLOAT_BLOCK;
|
||||
case GalTextureFormat.BC6H_UF16: return GalImageFormat.BC6H_UFLOAT_BLOCK;
|
||||
|
@ -121,7 +125,7 @@ namespace Ryujinx.Graphics.Gal
|
|||
break;
|
||||
}
|
||||
|
||||
throw new NotImplementedException("0x" + Format.ToString("x2") + " " + Type.ToString());
|
||||
throw new NotImplementedException("0x" + ((int)Format).ToString("x2") + " " + Type.ToString());
|
||||
}
|
||||
|
||||
public static GalImageFormat ConvertFrameBuffer(GalFrameBufferFormat Format)
|
||||
|
@ -142,6 +146,11 @@ namespace Ryujinx.Graphics.Gal
|
|||
case GalFrameBufferFormat.RG8Snorm: return GalImageFormat.R8_SNORM;
|
||||
case GalFrameBufferFormat.RGBA8Snorm: return GalImageFormat.A8B8G8R8_SNORM_PACK32;
|
||||
case GalFrameBufferFormat.RG8Unorm: return GalImageFormat.R8G8_UNORM;
|
||||
case GalFrameBufferFormat.BGRA8Unorm: return GalImageFormat.A8B8G8R8_UNORM_PACK32;
|
||||
case GalFrameBufferFormat.BGRA8Srgb: return GalImageFormat.A8B8G8R8_SRGB_PACK32;
|
||||
case GalFrameBufferFormat.RG32Float: return GalImageFormat.R32G32_SFLOAT;
|
||||
case GalFrameBufferFormat.RG32Sint: return GalImageFormat.R32G32_SINT;
|
||||
case GalFrameBufferFormat.RG32Uint: return GalImageFormat.R32G32_UINT;
|
||||
}
|
||||
|
||||
throw new NotImplementedException(Format.ToString());
|
||||
|
@ -151,9 +160,10 @@ namespace Ryujinx.Graphics.Gal
|
|||
{
|
||||
switch (Format)
|
||||
{
|
||||
case GalZetaFormat.Z32Float: return GalImageFormat.D32_SFLOAT;
|
||||
case GalZetaFormat.S8Z24Unorm: return GalImageFormat.D24_UNORM_S8_UINT;
|
||||
case GalZetaFormat.Z16Unorm: return GalImageFormat.D16_UNORM;
|
||||
case GalZetaFormat.Z32Float: return GalImageFormat.D32_SFLOAT;
|
||||
case GalZetaFormat.S8Z24Unorm: return GalImageFormat.D24_UNORM_S8_UINT;
|
||||
case GalZetaFormat.Z16Unorm: return GalImageFormat.D16_UNORM;
|
||||
case GalZetaFormat.Z32S8X24Float: return GalImageFormat.D32_SFLOAT_S8_UINT;
|
||||
}
|
||||
|
||||
throw new NotImplementedException(Format.ToString());
|
||||
|
@ -169,6 +179,9 @@ namespace Ryujinx.Graphics.Gal
|
|||
case GalImageFormat.R16G16B16A16_SFLOAT:
|
||||
case GalImageFormat.R16G16B16A16_SINT:
|
||||
case GalImageFormat.R16G16B16A16_UINT:
|
||||
case GalImageFormat.R32G32_SFLOAT:
|
||||
case GalImageFormat.R32G32_SINT:
|
||||
case GalImageFormat.R32G32_UINT:
|
||||
case GalImageFormat.A8B8G8R8_SNORM_PACK32:
|
||||
case GalImageFormat.A8B8G8R8_UNORM_PACK32:
|
||||
case GalImageFormat.A8B8G8R8_SINT_PACK32:
|
||||
|
@ -228,7 +241,8 @@ namespace Ryujinx.Graphics.Gal
|
|||
case GalImageFormat.D24_UNORM_S8_UINT:
|
||||
case GalImageFormat.D32_SFLOAT:
|
||||
case GalImageFormat.D16_UNORM:
|
||||
return true;
|
||||
case GalImageFormat.D32_SFLOAT_S8_UINT:
|
||||
return false;
|
||||
}
|
||||
|
||||
throw new NotImplementedException(Format.ToString());
|
||||
|
@ -241,6 +255,7 @@ namespace Ryujinx.Graphics.Gal
|
|||
case GalImageFormat.D24_UNORM_S8_UINT:
|
||||
case GalImageFormat.D32_SFLOAT:
|
||||
case GalImageFormat.D16_UNORM:
|
||||
case GalImageFormat.D32_SFLOAT_S8_UINT:
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -254,6 +269,7 @@ namespace Ryujinx.Graphics.Gal
|
|||
switch (Format)
|
||||
{
|
||||
case GalImageFormat.D24_UNORM_S8_UINT:
|
||||
case GalImageFormat.D32_SFLOAT_S8_UINT:
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -135,6 +135,9 @@ namespace Ryujinx.Graphics.Gal.OpenGL
|
|||
case GalImageFormat.R16G16B16A16_SFLOAT: return (PixelInternalFormat.Rgba16f, PixelFormat.Rgba, PixelType.HalfFloat);
|
||||
case GalImageFormat.R16G16B16A16_SINT: return (PixelInternalFormat.Rgba16i, PixelFormat.RgbaInteger, PixelType.Short);
|
||||
case GalImageFormat.R16G16B16A16_UINT: return (PixelInternalFormat.Rgba16ui, PixelFormat.RgbaInteger, PixelType.UnsignedShort);
|
||||
case GalImageFormat.R32G32_SFLOAT: return (PixelInternalFormat.Rg32f, PixelFormat.Rg, PixelType.Float);
|
||||
case GalImageFormat.R32G32_SINT: return (PixelInternalFormat.Rg32i, PixelFormat.RgInteger, PixelType.Int);
|
||||
case GalImageFormat.R32G32_UINT: return (PixelInternalFormat.Rg32ui, PixelFormat.RgInteger, PixelType.UnsignedInt);
|
||||
case GalImageFormat.A8B8G8R8_SNORM_PACK32: return (PixelInternalFormat.Rgba8Snorm, PixelFormat.Rgba, PixelType.Byte);
|
||||
case GalImageFormat.A8B8G8R8_UNORM_PACK32: return (PixelInternalFormat.Rgba8, PixelFormat.Rgba, PixelType.UnsignedByte);
|
||||
case GalImageFormat.A8B8G8R8_SINT_PACK32: return (PixelInternalFormat.Rgba8i, PixelFormat.RgbaInteger, PixelType.Byte);
|
||||
|
@ -168,9 +171,10 @@ namespace Ryujinx.Graphics.Gal.OpenGL
|
|||
|
||||
case GalImageFormat.R4G4B4A4_UNORM_PACK16_REVERSED: return (PixelInternalFormat.Rgba4, PixelFormat.Rgba, PixelType.UnsignedShort4444Reversed);
|
||||
|
||||
case GalImageFormat.D24_UNORM_S8_UINT: return (PixelInternalFormat.Depth24Stencil8, PixelFormat.DepthStencil, PixelType.UnsignedInt248);
|
||||
case GalImageFormat.D32_SFLOAT: return (PixelInternalFormat.DepthComponent32f, PixelFormat.DepthComponent, PixelType.Float);
|
||||
case GalImageFormat.D16_UNORM: return (PixelInternalFormat.DepthComponent16, PixelFormat.DepthComponent, PixelType.UnsignedShort);
|
||||
case GalImageFormat.D24_UNORM_S8_UINT: return (PixelInternalFormat.Depth24Stencil8, PixelFormat.DepthStencil, PixelType.UnsignedInt248);
|
||||
case GalImageFormat.D32_SFLOAT: return (PixelInternalFormat.DepthComponent32f, PixelFormat.DepthComponent, PixelType.Float);
|
||||
case GalImageFormat.D16_UNORM: return (PixelInternalFormat.DepthComponent16, PixelFormat.DepthComponent, PixelType.UnsignedShort);
|
||||
case GalImageFormat.D32_SFLOAT_S8_UINT: return (PixelInternalFormat.Depth32fStencil8, PixelFormat.DepthStencil, PixelType.Float32UnsignedInt248Rev);
|
||||
}
|
||||
|
||||
throw new NotImplementedException(Format.ToString());
|
||||
|
@ -215,16 +219,31 @@ namespace Ryujinx.Graphics.Gal.OpenGL
|
|||
{
|
||||
switch (Wrap)
|
||||
{
|
||||
case GalTextureWrap.Repeat: return TextureWrapMode.Repeat;
|
||||
case GalTextureWrap.MirroredRepeat: return TextureWrapMode.MirroredRepeat;
|
||||
case GalTextureWrap.ClampToEdge: return TextureWrapMode.ClampToEdge;
|
||||
case GalTextureWrap.ClampToBorder: return TextureWrapMode.ClampToBorder;
|
||||
case GalTextureWrap.Clamp: return TextureWrapMode.Clamp;
|
||||
case GalTextureWrap.Repeat: return TextureWrapMode.Repeat;
|
||||
case GalTextureWrap.MirroredRepeat: return TextureWrapMode.MirroredRepeat;
|
||||
case GalTextureWrap.ClampToEdge: return TextureWrapMode.ClampToEdge;
|
||||
case GalTextureWrap.ClampToBorder: return TextureWrapMode.ClampToBorder;
|
||||
case GalTextureWrap.Clamp: return TextureWrapMode.Clamp;
|
||||
}
|
||||
|
||||
//TODO: Those needs extensions (and are currently wrong).
|
||||
case GalTextureWrap.MirrorClampToEdge: return TextureWrapMode.ClampToEdge;
|
||||
case GalTextureWrap.MirrorClampToBorder: return TextureWrapMode.ClampToBorder;
|
||||
case GalTextureWrap.MirrorClamp: return TextureWrapMode.Clamp;
|
||||
if (OGLExtension.HasTextureMirrorClamp())
|
||||
{
|
||||
switch (Wrap)
|
||||
{
|
||||
case GalTextureWrap.MirrorClampToEdge: return (TextureWrapMode)ExtTextureMirrorClamp.MirrorClampToEdgeExt;
|
||||
case GalTextureWrap.MirrorClampToBorder: return (TextureWrapMode)ExtTextureMirrorClamp.MirrorClampToBorderExt;
|
||||
case GalTextureWrap.MirrorClamp: return (TextureWrapMode)ExtTextureMirrorClamp.MirrorClampExt;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
//Fallback to non-mirrored clamps
|
||||
switch (Wrap)
|
||||
{
|
||||
case GalTextureWrap.MirrorClampToEdge: return TextureWrapMode.ClampToEdge;
|
||||
case GalTextureWrap.MirrorClampToBorder: return TextureWrapMode.ClampToBorder;
|
||||
case GalTextureWrap.MirrorClamp: return TextureWrapMode.Clamp;
|
||||
}
|
||||
}
|
||||
|
||||
throw new ArgumentException(nameof(Wrap));
|
||||
|
|
|
@ -8,6 +8,8 @@ namespace Ryujinx.Graphics.Gal.OpenGL
|
|||
|
||||
private static bool EnhancedLayouts;
|
||||
|
||||
private static bool TextureMirrorClamp;
|
||||
|
||||
public static bool HasEnhancedLayouts()
|
||||
{
|
||||
EnsureInitialized();
|
||||
|
@ -15,6 +17,13 @@ namespace Ryujinx.Graphics.Gal.OpenGL
|
|||
return EnhancedLayouts;
|
||||
}
|
||||
|
||||
public static bool HasTextureMirrorClamp()
|
||||
{
|
||||
EnsureInitialized();
|
||||
|
||||
return TextureMirrorClamp;
|
||||
}
|
||||
|
||||
private static void EnsureInitialized()
|
||||
{
|
||||
if (Initialized)
|
||||
|
@ -23,6 +32,8 @@ namespace Ryujinx.Graphics.Gal.OpenGL
|
|||
}
|
||||
|
||||
EnhancedLayouts = HasExtension("GL_ARB_enhanced_layouts");
|
||||
|
||||
TextureMirrorClamp = HasExtension("GL_EXT_texture_mirror_clamp");
|
||||
}
|
||||
|
||||
private static bool HasExtension(string Name)
|
||||
|
|
|
@ -21,18 +21,6 @@ namespace Ryujinx.Graphics.Gal.OpenGL
|
|||
}
|
||||
}
|
||||
|
||||
private static readonly DrawBuffersEnum[] DrawBuffers = new DrawBuffersEnum[]
|
||||
{
|
||||
DrawBuffersEnum.ColorAttachment0,
|
||||
DrawBuffersEnum.ColorAttachment1,
|
||||
DrawBuffersEnum.ColorAttachment2,
|
||||
DrawBuffersEnum.ColorAttachment3,
|
||||
DrawBuffersEnum.ColorAttachment4,
|
||||
DrawBuffersEnum.ColorAttachment5,
|
||||
DrawBuffersEnum.ColorAttachment6,
|
||||
DrawBuffersEnum.ColorAttachment7,
|
||||
};
|
||||
|
||||
private const int NativeWidth = 1280;
|
||||
private const int NativeHeight = 720;
|
||||
|
||||
|
@ -194,6 +182,25 @@ namespace Ryujinx.Graphics.Gal.OpenGL
|
|||
ReadTex = RawTex;
|
||||
}
|
||||
|
||||
public void SetMap(int[] Map)
|
||||
{
|
||||
if (Map != null && Map.Length > 0)
|
||||
{
|
||||
DrawBuffersEnum[] Mode = new DrawBuffersEnum[Map.Length];
|
||||
|
||||
for (int i = 0; i < Map.Length; i++)
|
||||
{
|
||||
Mode[i] = DrawBuffersEnum.ColorAttachment0 + Map[i];
|
||||
}
|
||||
|
||||
GL.DrawBuffers(Mode.Length, Mode);
|
||||
}
|
||||
else
|
||||
{
|
||||
GL.DrawBuffer(DrawBufferMode.ColorAttachment0);
|
||||
}
|
||||
}
|
||||
|
||||
public void SetTransform(bool FlipX, bool FlipY, int Top, int Left, int Right, int Bottom)
|
||||
{
|
||||
this.FlipX = FlipX;
|
||||
|
@ -421,8 +428,6 @@ namespace Ryujinx.Graphics.Gal.OpenGL
|
|||
}
|
||||
|
||||
GL.BindFramebuffer(FramebufferTarget.DrawFramebuffer, DummyFrameBuffer);
|
||||
|
||||
GL.DrawBuffers(8, DrawBuffers);
|
||||
}
|
||||
|
||||
private void Attach(ref int OldHandle, int NewHandle, FramebufferAttachment FbAttachment)
|
||||
|
|
|
@ -25,23 +25,41 @@ namespace Ryujinx.Graphics.Gal.OpenGL
|
|||
{ GalVertexAttribSize._11_11_10, 3 }
|
||||
};
|
||||
|
||||
private static Dictionary<GalVertexAttribSize, VertexAttribPointerType> AttribTypes =
|
||||
private static Dictionary<GalVertexAttribSize, VertexAttribPointerType> SignedAttribTypes =
|
||||
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 } //?
|
||||
{ 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.Int2101010Rev }
|
||||
};
|
||||
|
||||
private static Dictionary<GalVertexAttribSize, VertexAttribPointerType> UnsignedAttribTypes =
|
||||
new Dictionary<GalVertexAttribSize, VertexAttribPointerType>()
|
||||
{
|
||||
{ GalVertexAttribSize._32_32_32_32, VertexAttribPointerType.UnsignedInt },
|
||||
{ GalVertexAttribSize._32_32_32, VertexAttribPointerType.UnsignedInt },
|
||||
{ GalVertexAttribSize._16_16_16_16, VertexAttribPointerType.UnsignedShort },
|
||||
{ GalVertexAttribSize._32_32, VertexAttribPointerType.UnsignedInt },
|
||||
{ GalVertexAttribSize._16_16_16, VertexAttribPointerType.UnsignedShort },
|
||||
{ GalVertexAttribSize._8_8_8_8, VertexAttribPointerType.UnsignedByte },
|
||||
{ GalVertexAttribSize._16_16, VertexAttribPointerType.UnsignedShort },
|
||||
{ GalVertexAttribSize._32, VertexAttribPointerType.UnsignedInt },
|
||||
{ GalVertexAttribSize._8_8_8, VertexAttribPointerType.UnsignedByte },
|
||||
{ GalVertexAttribSize._8_8, VertexAttribPointerType.UnsignedByte },
|
||||
{ GalVertexAttribSize._16, VertexAttribPointerType.UnsignedShort },
|
||||
{ GalVertexAttribSize._8, VertexAttribPointerType.UnsignedByte },
|
||||
{ GalVertexAttribSize._10_10_10_2, VertexAttribPointerType.UnsignedInt2101010Rev },
|
||||
{ GalVertexAttribSize._11_11_10, VertexAttribPointerType.UnsignedInt10F11F11FRev }
|
||||
};
|
||||
|
||||
private GalPipelineState Old;
|
||||
|
@ -108,9 +126,9 @@ namespace Ryujinx.Graphics.Gal.OpenGL
|
|||
|
||||
BindVertexLayout(New);
|
||||
|
||||
if (New.FlipX != Old.FlipX || New.FlipY != Old.FlipY)
|
||||
if (New.FlipX != Old.FlipX || New.FlipY != Old.FlipY || New.Instance != Old.Instance)
|
||||
{
|
||||
Shader.SetFlip(New.FlipX, New.FlipY);
|
||||
Shader.SetExtraData(New.FlipX, New.FlipY, New.Instance);
|
||||
}
|
||||
|
||||
//Note: Uncomment SetFrontFace and SetCullFace when flipping issues are solved
|
||||
|
@ -272,8 +290,7 @@ namespace Ryujinx.Graphics.Gal.OpenGL
|
|||
|
||||
private void BindConstBuffers(GalPipelineState New)
|
||||
{
|
||||
//Index 0 is reserved
|
||||
int FreeBinding = 1;
|
||||
int FreeBinding = OGLShader.ReservedCbufCount;
|
||||
|
||||
void BindIfNotNull(OGLShaderStage Stage)
|
||||
{
|
||||
|
@ -326,7 +343,7 @@ namespace Ryujinx.Graphics.Gal.OpenGL
|
|||
|
||||
bool Unsigned =
|
||||
Attrib.Type == GalVertexAttribType.Unorm ||
|
||||
Attrib.Type == GalVertexAttribType.Uint ||
|
||||
Attrib.Type == GalVertexAttribType.Uint ||
|
||||
Attrib.Type == GalVertexAttribType.Uscaled;
|
||||
|
||||
bool Normalize =
|
||||
|
@ -341,7 +358,14 @@ namespace Ryujinx.Graphics.Gal.OpenGL
|
|||
}
|
||||
else
|
||||
{
|
||||
Type = AttribTypes[Attrib.Size] + (Unsigned ? 1 : 0);
|
||||
if (Unsigned)
|
||||
{
|
||||
Type = UnsignedAttribTypes[Attrib.Size];
|
||||
}
|
||||
else
|
||||
{
|
||||
Type = SignedAttribTypes[Attrib.Size];
|
||||
}
|
||||
}
|
||||
|
||||
int Size = AttribElements[Attrib.Size];
|
||||
|
@ -360,6 +384,15 @@ namespace Ryujinx.Graphics.Gal.OpenGL
|
|||
{
|
||||
GL.VertexAttribPointer(Attrib.Index, Size, Type, Normalize, Binding.Stride, Offset);
|
||||
}
|
||||
|
||||
if (Binding.Instanced && Binding.Divisor != 0)
|
||||
{
|
||||
GL.VertexAttribDivisor(Attrib.Index, 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
GL.VertexAttribDivisor(Attrib.Index, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,6 +9,10 @@ namespace Ryujinx.Graphics.Gal.OpenGL
|
|||
{
|
||||
class OGLShader : IGalShader
|
||||
{
|
||||
public const int ReservedCbufCount = 1;
|
||||
|
||||
private const int ExtraDataSize = 4;
|
||||
|
||||
public OGLShaderProgram Current;
|
||||
|
||||
private ConcurrentDictionary<long, OGLShaderStage> Stages;
|
||||
|
@ -96,16 +100,7 @@ namespace Ryujinx.Graphics.Gal.OpenGL
|
|||
return Enumerable.Empty<ShaderDeclInfo>();
|
||||
}
|
||||
|
||||
public void EnsureTextureBinding(string UniformName, int Value)
|
||||
{
|
||||
BindProgram();
|
||||
|
||||
int Location = GL.GetUniformLocation(CurrentProgramHandle, UniformName);
|
||||
|
||||
GL.Uniform1(Location, Value);
|
||||
}
|
||||
|
||||
public unsafe void SetFlip(float X, float Y)
|
||||
public unsafe void SetExtraData(float FlipX, float FlipY, int Instance)
|
||||
{
|
||||
BindProgram();
|
||||
|
||||
|
@ -113,14 +108,15 @@ namespace Ryujinx.Graphics.Gal.OpenGL
|
|||
|
||||
GL.BindBuffer(BufferTarget.UniformBuffer, ExtraUboHandle);
|
||||
|
||||
float* Data = stackalloc float[4];
|
||||
Data[0] = X;
|
||||
Data[1] = Y;
|
||||
float* Data = stackalloc float[ExtraDataSize];
|
||||
Data[0] = FlipX;
|
||||
Data[1] = FlipY;
|
||||
Data[2] = BitConverter.Int32BitsToSingle(Instance);
|
||||
|
||||
//Invalidate buffer
|
||||
GL.BufferData(BufferTarget.UniformBuffer, 4 * sizeof(float), IntPtr.Zero, BufferUsageHint.StreamDraw);
|
||||
GL.BufferData(BufferTarget.UniformBuffer, ExtraDataSize * sizeof(float), IntPtr.Zero, BufferUsageHint.StreamDraw);
|
||||
|
||||
GL.BufferSubData(BufferTarget.UniformBuffer, IntPtr.Zero, 4 * sizeof(float), (IntPtr)Data);
|
||||
GL.BufferSubData(BufferTarget.UniformBuffer, IntPtr.Zero, ExtraDataSize * sizeof(float), (IntPtr)Data);
|
||||
}
|
||||
|
||||
public void Bind(long Key)
|
||||
|
@ -188,6 +184,7 @@ namespace Ryujinx.Graphics.Gal.OpenGL
|
|||
CheckProgramLink(Handle);
|
||||
|
||||
BindUniformBlocks(Handle);
|
||||
BindTextureLocations(Handle);
|
||||
|
||||
Programs.Add(Current, Handle);
|
||||
}
|
||||
|
@ -205,7 +202,7 @@ namespace Ryujinx.Graphics.Gal.OpenGL
|
|||
|
||||
GL.BindBuffer(BufferTarget.UniformBuffer, ExtraUboHandle);
|
||||
|
||||
GL.BufferData(BufferTarget.UniformBuffer, 4 * sizeof(float), IntPtr.Zero, BufferUsageHint.StreamDraw);
|
||||
GL.BufferData(BufferTarget.UniformBuffer, ExtraDataSize * sizeof(float), IntPtr.Zero, BufferUsageHint.StreamDraw);
|
||||
|
||||
GL.BindBufferBase(BufferRangeTarget.UniformBuffer, 0, ExtraUboHandle);
|
||||
}
|
||||
|
@ -227,8 +224,7 @@ namespace Ryujinx.Graphics.Gal.OpenGL
|
|||
|
||||
GL.UniformBlockBinding(ProgramHandle, ExtraBlockindex, 0);
|
||||
|
||||
//First index is reserved
|
||||
int FreeBinding = 1;
|
||||
int FreeBinding = ReservedCbufCount;
|
||||
|
||||
void BindUniformBlocksIfNotNull(OGLShaderStage Stage)
|
||||
{
|
||||
|
@ -258,6 +254,34 @@ namespace Ryujinx.Graphics.Gal.OpenGL
|
|||
BindUniformBlocksIfNotNull(Current.Fragment);
|
||||
}
|
||||
|
||||
private void BindTextureLocations(int ProgramHandle)
|
||||
{
|
||||
int Index = 0;
|
||||
|
||||
void BindTexturesIfNotNull(OGLShaderStage Stage)
|
||||
{
|
||||
if (Stage != null)
|
||||
{
|
||||
foreach (ShaderDeclInfo Decl in Stage.TextureUsage)
|
||||
{
|
||||
int Location = GL.GetUniformLocation(ProgramHandle, Decl.Name);
|
||||
|
||||
GL.Uniform1(Location, Index);
|
||||
|
||||
Index++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
GL.UseProgram(ProgramHandle);
|
||||
|
||||
BindTexturesIfNotNull(Current.Vertex);
|
||||
BindTexturesIfNotNull(Current.TessControl);
|
||||
BindTexturesIfNotNull(Current.TessEvaluation);
|
||||
BindTexturesIfNotNull(Current.Geometry);
|
||||
BindTexturesIfNotNull(Current.Fragment);
|
||||
}
|
||||
|
||||
private static void CheckProgramLink(int Handle)
|
||||
{
|
||||
int Status = 0;
|
||||
|
|
|
@ -16,7 +16,6 @@ namespace Ryujinx.Graphics.Gal.Shader
|
|||
public const int VertexIdAttr = 0x2fc;
|
||||
public const int FaceAttr = 0x3fc;
|
||||
|
||||
public const int MaxFrameBufferAttachments = 8;
|
||||
public const int MaxUboSize = 1024;
|
||||
|
||||
public const int GlPositionVec4Index = 7;
|
||||
|
@ -42,10 +41,15 @@ namespace Ryujinx.Graphics.Gal.Shader
|
|||
|
||||
public const string ExtraUniformBlockName = "Extra";
|
||||
public const string FlipUniformName = "flip";
|
||||
public const string InstanceUniformName = "instance";
|
||||
|
||||
public const string ProgramName = "program";
|
||||
public const string ProgramAName = ProgramName + "_a";
|
||||
public const string ProgramBName = ProgramName + "_b";
|
||||
public const string BasicBlockName = "bb";
|
||||
public const string BasicBlockAName = BasicBlockName + "_a";
|
||||
public const string BasicBlockBName = BasicBlockName + "_b";
|
||||
|
||||
public const int SsyStackSize = 16;
|
||||
public const string SsyStackName = "ssy_stack";
|
||||
public const string SsyCursorName = "ssy_cursor";
|
||||
|
||||
private string[] StagePrefixes = new string[] { "vp", "tcp", "tep", "gp", "fp" };
|
||||
|
||||
|
@ -94,16 +98,33 @@ namespace Ryujinx.Graphics.Gal.Shader
|
|||
m_Preds = new Dictionary<int, ShaderDeclInfo>();
|
||||
}
|
||||
|
||||
public GlslDecl(ShaderIrBlock[] Blocks, GalShaderType ShaderType) : this(ShaderType)
|
||||
public GlslDecl(ShaderIrBlock[] Blocks, GalShaderType ShaderType, ShaderHeader Header)
|
||||
: this(ShaderType)
|
||||
{
|
||||
StagePrefix = StagePrefixes[(int)ShaderType] + "_";
|
||||
|
||||
if (ShaderType == GalShaderType.Fragment)
|
||||
{
|
||||
//Note: Replace 1 with MaxFrameBufferAttachments when attachments start to work
|
||||
for (int Index = 0; Index < 1; Index++)
|
||||
int Index = 0;
|
||||
|
||||
for (int Attachment = 0; Attachment < 8; Attachment++)
|
||||
{
|
||||
m_Gprs.Add(Index * 4, new ShaderDeclInfo(FragmentOutputName + Index, Index * 4, false, 0, 4));
|
||||
for (int Component = 0; Component < 4; Component++)
|
||||
{
|
||||
if (Header.OmapTargets[Attachment].ComponentEnabled(Component))
|
||||
{
|
||||
m_Gprs.TryAdd(Index, new ShaderDeclInfo(GetGprName(Index), Index));
|
||||
|
||||
Index++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (Header.OmapDepth)
|
||||
{
|
||||
Index = Header.DepthRegister;
|
||||
|
||||
m_Gprs.TryAdd(Index, new ShaderDeclInfo(GetGprName(Index), Index));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -153,6 +174,11 @@ namespace Ryujinx.Graphics.Gal.Shader
|
|||
return Combined;
|
||||
}
|
||||
|
||||
public static string GetGprName(int Index)
|
||||
{
|
||||
return GprName + Index;
|
||||
}
|
||||
|
||||
private static void Merge(
|
||||
Dictionary<int, ShaderDeclInfo> C,
|
||||
Dictionary<int, ShaderDeclInfo> A,
|
||||
|
@ -316,9 +342,9 @@ namespace Ryujinx.Graphics.Gal.Shader
|
|||
|
||||
case ShaderIrOperGpr Gpr:
|
||||
{
|
||||
if (!Gpr.IsConst && !HasName(m_Gprs, Gpr.Index))
|
||||
if (!Gpr.IsConst)
|
||||
{
|
||||
string Name = GprName + Gpr.Index;
|
||||
string Name = GetGprName(Gpr.Index);
|
||||
|
||||
m_Gprs.TryAdd(Gpr.Index, new ShaderDeclInfo(Name, Gpr.Index));
|
||||
}
|
||||
|
|
|
@ -120,8 +120,8 @@ namespace Ryujinx.Graphics.Gal.Shader
|
|||
Blocks = ShaderDecoder.Decode(Memory, VpAPosition);
|
||||
BlocksB = ShaderDecoder.Decode(Memory, VpBPosition);
|
||||
|
||||
GlslDecl DeclVpA = new GlslDecl(Blocks, ShaderType);
|
||||
GlslDecl DeclVpB = new GlslDecl(BlocksB, ShaderType);
|
||||
GlslDecl DeclVpA = new GlslDecl(Blocks, ShaderType, Header);
|
||||
GlslDecl DeclVpB = new GlslDecl(BlocksB, ShaderType, HeaderB);
|
||||
|
||||
Decl = GlslDecl.Merge(DeclVpA, DeclVpB);
|
||||
|
||||
|
@ -136,7 +136,7 @@ namespace Ryujinx.Graphics.Gal.Shader
|
|||
Blocks = ShaderDecoder.Decode(Memory, Position);
|
||||
BlocksB = null;
|
||||
|
||||
Decl = new GlslDecl(Blocks, ShaderType);
|
||||
Decl = new GlslDecl(Blocks, ShaderType, Header);
|
||||
|
||||
return Decompile();
|
||||
}
|
||||
|
@ -155,18 +155,19 @@ namespace Ryujinx.Graphics.Gal.Shader
|
|||
PrintDeclOutAttributes();
|
||||
PrintDeclGprs();
|
||||
PrintDeclPreds();
|
||||
PrintDeclSsy();
|
||||
|
||||
if (BlocksB != null)
|
||||
{
|
||||
PrintBlockScope(Blocks[0], null, null, "void " + GlslDecl.ProgramAName + "()", IdentationStr);
|
||||
PrintBlockScope(Blocks, GlslDecl.BasicBlockAName);
|
||||
|
||||
SB.AppendLine();
|
||||
|
||||
PrintBlockScope(BlocksB[0], null, null, "void " + GlslDecl.ProgramBName + "()", IdentationStr);
|
||||
PrintBlockScope(BlocksB, GlslDecl.BasicBlockBName);
|
||||
}
|
||||
else
|
||||
{
|
||||
PrintBlockScope(Blocks[0], null, null, "void " + GlslDecl.ProgramName + "()", IdentationStr);
|
||||
PrintBlockScope(Blocks, GlslDecl.BasicBlockName);
|
||||
}
|
||||
|
||||
SB.AppendLine();
|
||||
|
@ -241,10 +242,15 @@ namespace Ryujinx.Graphics.Gal.Shader
|
|||
{
|
||||
if (Decl.ShaderType == GalShaderType.Vertex)
|
||||
{
|
||||
SB.AppendLine("layout (std140) uniform " + GlslDecl.ExtraUniformBlockName + "{");
|
||||
//Memory layout here is [flip_x, flip_y, instance, unused]
|
||||
//It's using 4 bytes, not 8
|
||||
|
||||
SB.AppendLine("layout (std140) uniform " + GlslDecl.ExtraUniformBlockName + " {");
|
||||
|
||||
SB.AppendLine(IdentationStr + "vec2 " + GlslDecl.FlipUniformName + ";");
|
||||
|
||||
SB.AppendLine(IdentationStr + "int " + GlslDecl.InstanceUniformName + ";");
|
||||
|
||||
SB.AppendLine("};");
|
||||
}
|
||||
|
||||
|
@ -304,7 +310,17 @@ namespace Ryujinx.Graphics.Gal.Shader
|
|||
|
||||
private void PrintDeclOutAttributes()
|
||||
{
|
||||
if (Decl.ShaderType != GalShaderType.Fragment)
|
||||
if (Decl.ShaderType == GalShaderType.Fragment)
|
||||
{
|
||||
for (int Attachment = 0; Attachment < 8; Attachment++)
|
||||
{
|
||||
if (Header.OmapTargets[Attachment].Enabled)
|
||||
{
|
||||
SB.AppendLine("layout (location = " + Attachment + ") out vec4 " + GlslDecl.FragmentOutputName + Attachment + ";");
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
SB.AppendLine("layout (location = " + GlslDecl.PositionOutAttrLocation + ") out vec4 " + GlslDecl.PositionOutAttrName + ";");
|
||||
}
|
||||
|
@ -342,6 +358,13 @@ namespace Ryujinx.Graphics.Gal.Shader
|
|||
PrintDecls(Decl.Preds, "bool");
|
||||
}
|
||||
|
||||
private void PrintDeclSsy()
|
||||
{
|
||||
SB.AppendLine("uint " + GlslDecl.SsyCursorName + ";");
|
||||
|
||||
SB.AppendLine("uint " + GlslDecl.SsyStackName + "[" + GlslDecl.SsyStackSize + "];" + Environment.NewLine);
|
||||
}
|
||||
|
||||
private void PrintDecls(IReadOnlyDictionary<int, ShaderDeclInfo> Dict, string CustomType = null, string Suffix = "")
|
||||
{
|
||||
foreach (ShaderDeclInfo DeclInfo in Dict.Values.OrderBy(DeclKeySelector))
|
||||
|
@ -417,14 +440,16 @@ namespace Ryujinx.Graphics.Gal.Shader
|
|||
}
|
||||
}
|
||||
|
||||
SB.AppendLine(IdentationStr + "uint pc;");
|
||||
|
||||
if (BlocksB != null)
|
||||
{
|
||||
SB.AppendLine(IdentationStr + GlslDecl.ProgramAName + "();");
|
||||
SB.AppendLine(IdentationStr + GlslDecl.ProgramBName + "();");
|
||||
PrintProgram(Blocks, GlslDecl.BasicBlockAName);
|
||||
PrintProgram(BlocksB, GlslDecl.BasicBlockBName);
|
||||
}
|
||||
else
|
||||
{
|
||||
SB.AppendLine(IdentationStr + GlslDecl.ProgramName + "();");
|
||||
PrintProgram(Blocks, GlslDecl.BasicBlockName);
|
||||
}
|
||||
|
||||
if (Decl.ShaderType != GalShaderType.Geometry)
|
||||
|
@ -432,9 +457,62 @@ namespace Ryujinx.Graphics.Gal.Shader
|
|||
PrintAttrToOutput();
|
||||
}
|
||||
|
||||
if (Decl.ShaderType == GalShaderType.Fragment)
|
||||
{
|
||||
if (Header.OmapDepth)
|
||||
{
|
||||
SB.AppendLine(IdentationStr + "gl_FragDepth = " + GlslDecl.GetGprName(Header.DepthRegister) + ";");
|
||||
}
|
||||
|
||||
int GprIndex = 0;
|
||||
|
||||
for (int Attachment = 0; Attachment < 8; Attachment++)
|
||||
{
|
||||
string Output = GlslDecl.FragmentOutputName + Attachment;
|
||||
|
||||
OmapTarget Target = Header.OmapTargets[Attachment];
|
||||
|
||||
for (int Component = 0; Component < 4; Component++)
|
||||
{
|
||||
if (Target.ComponentEnabled(Component))
|
||||
{
|
||||
SB.AppendLine(IdentationStr + Output + "[" + Component + "] = " + GlslDecl.GetGprName(GprIndex) + ";");
|
||||
|
||||
GprIndex++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SB.AppendLine("}");
|
||||
}
|
||||
|
||||
private void PrintProgram(ShaderIrBlock[] Blocks, string Name)
|
||||
{
|
||||
const string Ident1 = IdentationStr;
|
||||
const string Ident2 = Ident1 + IdentationStr;
|
||||
const string Ident3 = Ident2 + IdentationStr;
|
||||
const string Ident4 = Ident3 + IdentationStr;
|
||||
|
||||
SB.AppendLine(Ident1 + "pc = " + GetBlockPosition(Blocks[0]) + ";");
|
||||
SB.AppendLine(Ident1 + "do {");
|
||||
SB.AppendLine(Ident2 + "switch (pc) {");
|
||||
|
||||
foreach (ShaderIrBlock Block in Blocks)
|
||||
{
|
||||
string FunctionName = Block.Position.ToString("x8");
|
||||
|
||||
SB.AppendLine(Ident3 + "case 0x" + FunctionName + ": pc = " + Name + "_" + FunctionName + "(); break;");
|
||||
}
|
||||
|
||||
SB.AppendLine(Ident3 + "default:");
|
||||
SB.AppendLine(Ident4 + "pc = 0;");
|
||||
SB.AppendLine(Ident4 + "break;");
|
||||
|
||||
SB.AppendLine(Ident2 + "}");
|
||||
SB.AppendLine(Ident1 + "} while (pc != 0);");
|
||||
}
|
||||
|
||||
private void PrintAttrToOutput(string Identation = IdentationStr)
|
||||
{
|
||||
foreach (KeyValuePair<int, ShaderDeclInfo> KV in Decl.OutAttributes)
|
||||
|
@ -468,193 +546,145 @@ namespace Ryujinx.Graphics.Gal.Shader
|
|||
}
|
||||
}
|
||||
|
||||
private void PrintBlockScope(
|
||||
ShaderIrBlock Block,
|
||||
ShaderIrBlock EndBlock,
|
||||
ShaderIrBlock LoopBlock,
|
||||
string ScopeName,
|
||||
string Identation,
|
||||
bool IsDoWhile = false)
|
||||
private void PrintBlockScope(ShaderIrBlock[] Blocks, string Name)
|
||||
{
|
||||
string UpIdent = Identation.Substring(0, Identation.Length - IdentationStr.Length);
|
||||
foreach (ShaderIrBlock Block in Blocks)
|
||||
{
|
||||
SB.AppendLine("uint " + Name + "_" + Block.Position.ToString("x8") + "() {");
|
||||
|
||||
if (IsDoWhile)
|
||||
{
|
||||
SB.AppendLine(UpIdent + "do {");
|
||||
}
|
||||
else
|
||||
{
|
||||
SB.AppendLine(UpIdent + ScopeName + " {");
|
||||
}
|
||||
PrintNodes(Block, Block.GetNodes());
|
||||
|
||||
while (Block != null && Block != EndBlock)
|
||||
{
|
||||
ShaderIrNode[] Nodes = Block.GetNodes();
|
||||
|
||||
Block = PrintNodes(Block, EndBlock, LoopBlock, Identation, Nodes);
|
||||
}
|
||||
|
||||
if (IsDoWhile)
|
||||
{
|
||||
SB.AppendLine(UpIdent + "} " + ScopeName + ";");
|
||||
}
|
||||
else
|
||||
{
|
||||
SB.AppendLine(UpIdent + "}");
|
||||
SB.AppendLine("}" + Environment.NewLine);
|
||||
}
|
||||
}
|
||||
|
||||
private ShaderIrBlock PrintNodes(
|
||||
ShaderIrBlock Block,
|
||||
ShaderIrBlock EndBlock,
|
||||
ShaderIrBlock LoopBlock,
|
||||
string Identation,
|
||||
params ShaderIrNode[] Nodes)
|
||||
private void PrintNode(ShaderIrBlock Block, ShaderIrNode Node, string Identation)
|
||||
{
|
||||
/*
|
||||
* Notes about control flow and if-else/loop generation:
|
||||
* The code assumes that the program has sane control flow,
|
||||
* that is, there's no jumps to a location after another jump or
|
||||
* jump target (except for the end of an if-else block), and backwards
|
||||
* jumps to a location before the last loop dominator.
|
||||
* Such cases needs to be transformed on a step before the GLSL code
|
||||
* generation to ensure that we have sane graphs to work with.
|
||||
* TODO: Such transformation is not yet implemented.
|
||||
*/
|
||||
string NewIdent = Identation + IdentationStr;
|
||||
|
||||
ShaderIrBlock LoopTail = GetLoopTailBlock(Block);
|
||||
|
||||
if (LoopTail != null && LoopBlock != Block)
|
||||
if (Node is ShaderIrCond Cond)
|
||||
{
|
||||
//Shoock! kuma shock! We have a loop here!
|
||||
//The entire sequence needs to be inside a do-while block.
|
||||
ShaderIrBlock LoopEnd = GetDownBlock(LoopTail);
|
||||
string IfExpr = GetSrcExpr(Cond.Pred, true);
|
||||
|
||||
PrintBlockScope(Block, LoopEnd, Block, "while (false)", NewIdent, IsDoWhile: true);
|
||||
if (Cond.Not)
|
||||
{
|
||||
IfExpr = "!(" + IfExpr + ")";
|
||||
}
|
||||
|
||||
return LoopEnd;
|
||||
SB.AppendLine(Identation + "if (" + IfExpr + ") {");
|
||||
|
||||
if (Cond.Child is ShaderIrOp Op && Op.Inst == ShaderIrInst.Bra)
|
||||
{
|
||||
SB.AppendLine(Identation + IdentationStr + "return " + GetBlockPosition(Block.Branch) + ";");
|
||||
}
|
||||
else
|
||||
{
|
||||
PrintNode(Block, Cond.Child, Identation + IdentationStr);
|
||||
}
|
||||
|
||||
SB.AppendLine(Identation + "}");
|
||||
}
|
||||
|
||||
foreach (ShaderIrNode Node in Nodes)
|
||||
else if (Node is ShaderIrAsg Asg)
|
||||
{
|
||||
if (Node is ShaderIrCond Cond)
|
||||
if (IsValidOutOper(Asg.Dst))
|
||||
{
|
||||
string IfExpr = GetSrcExpr(Cond.Pred, true);
|
||||
string Expr = GetSrcExpr(Asg.Src, true);
|
||||
|
||||
if (Cond.Not)
|
||||
{
|
||||
IfExpr = "!(" + IfExpr + ")";
|
||||
}
|
||||
Expr = GetExprWithCast(Asg.Dst, Asg.Src, Expr);
|
||||
|
||||
if (Cond.Child is ShaderIrOp Op && Op.Inst == ShaderIrInst.Bra)
|
||||
{
|
||||
//Branch is a loop branch and would result in infinite recursion.
|
||||
if (Block.Branch.Position <= Block.Position)
|
||||
{
|
||||
SB.AppendLine(Identation + "if (" + IfExpr + ") {");
|
||||
|
||||
SB.AppendLine(Identation + IdentationStr + "continue;");
|
||||
|
||||
SB.AppendLine(Identation + "}");
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
string SubScopeName = "if (!" + IfExpr + ")";
|
||||
|
||||
PrintBlockScope(Block.Next, Block.Branch, LoopBlock, SubScopeName, NewIdent);
|
||||
|
||||
ShaderIrBlock IfElseEnd = GetUpBlock(Block.Branch).Branch;
|
||||
|
||||
if (IfElseEnd?.Position > Block.Branch.Position)
|
||||
{
|
||||
PrintBlockScope(Block.Branch, IfElseEnd, LoopBlock, "else", NewIdent);
|
||||
|
||||
return IfElseEnd;
|
||||
}
|
||||
|
||||
return Block.Branch;
|
||||
}
|
||||
else
|
||||
{
|
||||
SB.AppendLine(Identation + "if (" + IfExpr + ") {");
|
||||
|
||||
PrintNodes(Block, EndBlock, LoopBlock, NewIdent, Cond.Child);
|
||||
|
||||
SB.AppendLine(Identation + "}");
|
||||
}
|
||||
SB.AppendLine(Identation + GetDstOperName(Asg.Dst) + " = " + Expr + ";");
|
||||
}
|
||||
else if (Node is ShaderIrAsg Asg)
|
||||
}
|
||||
else if (Node is ShaderIrOp Op)
|
||||
{
|
||||
switch (Op.Inst)
|
||||
{
|
||||
if (IsValidOutOper(Asg.Dst))
|
||||
case ShaderIrInst.Bra:
|
||||
{
|
||||
string Expr = GetSrcExpr(Asg.Src, true);
|
||||
SB.AppendLine(Identation + "return " + GetBlockPosition(Block.Branch) + ";");
|
||||
|
||||
Expr = GetExprWithCast(Asg.Dst, Asg.Src, Expr);
|
||||
break;
|
||||
}
|
||||
|
||||
SB.AppendLine(Identation + GetDstOperName(Asg.Dst) + " = " + Expr + ";");
|
||||
}
|
||||
}
|
||||
else if (Node is ShaderIrOp Op)
|
||||
{
|
||||
if (Op.Inst == ShaderIrInst.Bra)
|
||||
{
|
||||
if (Block.Branch.Position <= Block.Position)
|
||||
{
|
||||
SB.AppendLine(Identation + "continue;");
|
||||
}
|
||||
}
|
||||
else if (Op.Inst == ShaderIrInst.Emit)
|
||||
case ShaderIrInst.Emit:
|
||||
{
|
||||
PrintAttrToOutput(Identation);
|
||||
|
||||
SB.AppendLine(Identation + "EmitVertex();");
|
||||
|
||||
break;
|
||||
}
|
||||
else
|
||||
|
||||
case ShaderIrInst.Ssy:
|
||||
{
|
||||
string StackIndex = GlslDecl.SsyStackName + "[" + GlslDecl.SsyCursorName + "]";
|
||||
|
||||
int TargetPosition = (Op.OperandA as ShaderIrOperImm).Value;
|
||||
|
||||
string Target = "0x" + TargetPosition.ToString("x8") + "u";
|
||||
|
||||
SB.AppendLine(Identation + StackIndex + " = " + Target + ";");
|
||||
|
||||
SB.AppendLine(Identation + GlslDecl.SsyCursorName + "++;");
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case ShaderIrInst.Sync:
|
||||
{
|
||||
SB.AppendLine(Identation + GlslDecl.SsyCursorName + "--;");
|
||||
|
||||
string Target = GlslDecl.SsyStackName + "[" + GlslDecl.SsyCursorName + "]";
|
||||
|
||||
SB.AppendLine(Identation + "return " + Target + ";");
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
SB.AppendLine(Identation + GetSrcExpr(Op, true) + ";");
|
||||
}
|
||||
}
|
||||
else if (Node is ShaderIrCmnt Cmnt)
|
||||
{
|
||||
SB.AppendLine(Identation + "// " + Cmnt.Comment);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new InvalidOperationException();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return Block.Next;
|
||||
}
|
||||
|
||||
private ShaderIrBlock GetUpBlock(ShaderIrBlock Block)
|
||||
{
|
||||
return Blocks.FirstOrDefault(x => x.EndPosition == Block.Position);
|
||||
}
|
||||
|
||||
private ShaderIrBlock GetDownBlock(ShaderIrBlock Block)
|
||||
{
|
||||
return Blocks.FirstOrDefault(x => x.Position == Block.EndPosition);
|
||||
}
|
||||
|
||||
private ShaderIrBlock GetLoopTailBlock(ShaderIrBlock LoopHead)
|
||||
{
|
||||
ShaderIrBlock Tail = null;
|
||||
|
||||
foreach (ShaderIrBlock Block in LoopHead.Sources)
|
||||
else if (Node is ShaderIrCmnt Cmnt)
|
||||
{
|
||||
if (Block.Position >= LoopHead.Position)
|
||||
{
|
||||
if (Tail == null || Tail.Position < Block.Position)
|
||||
{
|
||||
Tail = Block;
|
||||
}
|
||||
}
|
||||
SB.AppendLine(Identation + "// " + Cmnt.Comment);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new InvalidOperationException();
|
||||
}
|
||||
}
|
||||
|
||||
private void PrintNodes(ShaderIrBlock Block, ShaderIrNode[] Nodes)
|
||||
{
|
||||
foreach (ShaderIrNode Node in Nodes)
|
||||
{
|
||||
PrintNode(Block, Node, IdentationStr);
|
||||
}
|
||||
|
||||
return Tail;
|
||||
if (Nodes.Length > 0)
|
||||
{
|
||||
ShaderIrNode Last = Nodes[Nodes.Length - 1];
|
||||
|
||||
bool UnconditionalFlowChange = false;
|
||||
|
||||
if (Last is ShaderIrOp Op)
|
||||
{
|
||||
switch (Op.Inst)
|
||||
{
|
||||
case ShaderIrInst.Bra:
|
||||
case ShaderIrInst.Exit:
|
||||
case ShaderIrInst.Kil:
|
||||
case ShaderIrInst.Sync:
|
||||
UnconditionalFlowChange = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!UnconditionalFlowChange)
|
||||
{
|
||||
SB.AppendLine(IdentationStr + "return " + GetBlockPosition(Block.Next) + ";");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private bool IsValidOutOper(ShaderIrNode Node)
|
||||
|
@ -779,7 +809,7 @@ namespace Ryujinx.Graphics.Gal.Shader
|
|||
switch (Abuf.Offs)
|
||||
{
|
||||
case GlslDecl.VertexIdAttr: return "gl_VertexID";
|
||||
case GlslDecl.InstanceIdAttr: return "gl_InstanceID";
|
||||
case GlslDecl.InstanceIdAttr: return GlslDecl.InstanceUniformName;
|
||||
}
|
||||
}
|
||||
else if (Decl.ShaderType == GalShaderType.TessEvaluation)
|
||||
|
@ -964,7 +994,7 @@ namespace Ryujinx.Graphics.Gal.Shader
|
|||
|
||||
private string GetCnumExpr(ShaderIrOp Op) => GetUnaryCall(Op, "!isnan");
|
||||
|
||||
private string GetExitExpr(ShaderIrOp Op) => "return";
|
||||
private string GetExitExpr(ShaderIrOp Op) => "return 0u";
|
||||
|
||||
private string GetFcosExpr(ShaderIrOp Op) => GetUnaryCall(Op, "cos");
|
||||
|
||||
|
@ -996,7 +1026,31 @@ namespace Ryujinx.Graphics.Gal.Shader
|
|||
return "int(uint(" + GetOperExpr(Op, Op.OperandA) + "))";
|
||||
}
|
||||
|
||||
private string GetIpaExpr(ShaderIrOp Op) => GetSrcExpr(Op.OperandA);
|
||||
private string GetIpaExpr(ShaderIrOp Op)
|
||||
{
|
||||
ShaderIrMetaIpa Meta = (ShaderIrMetaIpa)Op.MetaData;
|
||||
|
||||
ShaderIrOperAbuf Abuf = (ShaderIrOperAbuf)Op.OperandA;
|
||||
|
||||
if (Meta.Mode == ShaderIpaMode.Pass)
|
||||
{
|
||||
int Index = Abuf.Offs >> 4;
|
||||
int Elem = (Abuf.Offs >> 2) & 3;
|
||||
|
||||
if (Decl.ShaderType == GalShaderType.Fragment && Index == GlslDecl.GlPositionVec4Index)
|
||||
{
|
||||
switch (Elem)
|
||||
{
|
||||
case 0: return "gl_FragCoord.x";
|
||||
case 1: return "gl_FragCoord.y";
|
||||
case 2: return "gl_FragCoord.z";
|
||||
case 3: return "1";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return GetSrcExpr(Op.OperandA);
|
||||
}
|
||||
|
||||
private string GetKilExpr(ShaderIrOp Op) => "discard";
|
||||
|
||||
|
@ -1309,5 +1363,17 @@ namespace Ryujinx.Graphics.Gal.Shader
|
|||
|
||||
throw new ArgumentException(nameof(Node));
|
||||
}
|
||||
|
||||
private static string GetBlockPosition(ShaderIrBlock Block)
|
||||
{
|
||||
if (Block != null)
|
||||
{
|
||||
return "0x" + Block.Position.ToString("x8") + "u";
|
||||
}
|
||||
else
|
||||
{
|
||||
return "0u";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
namespace Ryujinx.Graphics.Gal.Shader
|
||||
{
|
||||
delegate void ShaderDecodeFunc(ShaderIrBlock Block, long OpCode);
|
||||
delegate void ShaderDecodeFunc(ShaderIrBlock Block, long OpCode, long Position);
|
||||
}
|
|
@ -6,32 +6,32 @@ namespace Ryujinx.Graphics.Gal.Shader
|
|||
{
|
||||
static partial class ShaderDecode
|
||||
{
|
||||
public static void Bfe_C(ShaderIrBlock Block, long OpCode)
|
||||
public static void Bfe_C(ShaderIrBlock Block, long OpCode, long Position)
|
||||
{
|
||||
EmitBfe(Block, OpCode, ShaderOper.CR);
|
||||
}
|
||||
|
||||
public static void Bfe_I(ShaderIrBlock Block, long OpCode)
|
||||
public static void Bfe_I(ShaderIrBlock Block, long OpCode, long Position)
|
||||
{
|
||||
EmitBfe(Block, OpCode, ShaderOper.Imm);
|
||||
}
|
||||
|
||||
public static void Bfe_R(ShaderIrBlock Block, long OpCode)
|
||||
public static void Bfe_R(ShaderIrBlock Block, long OpCode, long Position)
|
||||
{
|
||||
EmitBfe(Block, OpCode, ShaderOper.RR);
|
||||
}
|
||||
|
||||
public static void Fadd_C(ShaderIrBlock Block, long OpCode)
|
||||
public static void Fadd_C(ShaderIrBlock Block, long OpCode, long Position)
|
||||
{
|
||||
EmitFadd(Block, OpCode, ShaderOper.CR);
|
||||
}
|
||||
|
||||
public static void Fadd_I(ShaderIrBlock Block, long OpCode)
|
||||
public static void Fadd_I(ShaderIrBlock Block, long OpCode, long Position)
|
||||
{
|
||||
EmitFadd(Block, OpCode, ShaderOper.Immf);
|
||||
}
|
||||
|
||||
public static void Fadd_I32(ShaderIrBlock Block, long OpCode)
|
||||
public static void Fadd_I32(ShaderIrBlock Block, long OpCode, long Position)
|
||||
{
|
||||
ShaderIrNode OperA = GetOperGpr8 (OpCode);
|
||||
ShaderIrNode OperB = GetOperImmf32_20(OpCode);
|
||||
|
@ -49,47 +49,47 @@ namespace Ryujinx.Graphics.Gal.Shader
|
|||
Block.AddNode(GetPredNode(new ShaderIrAsg(GetOperGpr0(OpCode), Op), OpCode));
|
||||
}
|
||||
|
||||
public static void Fadd_R(ShaderIrBlock Block, long OpCode)
|
||||
public static void Fadd_R(ShaderIrBlock Block, long OpCode, long Position)
|
||||
{
|
||||
EmitFadd(Block, OpCode, ShaderOper.RR);
|
||||
}
|
||||
|
||||
public static void Ffma_CR(ShaderIrBlock Block, long OpCode)
|
||||
public static void Ffma_CR(ShaderIrBlock Block, long OpCode, long Position)
|
||||
{
|
||||
EmitFfma(Block, OpCode, ShaderOper.CR);
|
||||
}
|
||||
|
||||
public static void Ffma_I(ShaderIrBlock Block, long OpCode)
|
||||
public static void Ffma_I(ShaderIrBlock Block, long OpCode, long Position)
|
||||
{
|
||||
EmitFfma(Block, OpCode, ShaderOper.Immf);
|
||||
}
|
||||
|
||||
public static void Ffma_RC(ShaderIrBlock Block, long OpCode)
|
||||
public static void Ffma_RC(ShaderIrBlock Block, long OpCode, long Position)
|
||||
{
|
||||
EmitFfma(Block, OpCode, ShaderOper.RC);
|
||||
}
|
||||
|
||||
public static void Ffma_RR(ShaderIrBlock Block, long OpCode)
|
||||
public static void Ffma_RR(ShaderIrBlock Block, long OpCode, long Position)
|
||||
{
|
||||
EmitFfma(Block, OpCode, ShaderOper.RR);
|
||||
}
|
||||
|
||||
public static void Fmnmx_C(ShaderIrBlock Block, long OpCode)
|
||||
public static void Fmnmx_C(ShaderIrBlock Block, long OpCode, long Position)
|
||||
{
|
||||
EmitFmnmx(Block, OpCode, ShaderOper.CR);
|
||||
}
|
||||
|
||||
public static void Fmnmx_I(ShaderIrBlock Block, long OpCode)
|
||||
public static void Fmnmx_I(ShaderIrBlock Block, long OpCode, long Position)
|
||||
{
|
||||
EmitFmnmx(Block, OpCode, ShaderOper.Immf);
|
||||
}
|
||||
|
||||
public static void Fmnmx_R(ShaderIrBlock Block, long OpCode)
|
||||
public static void Fmnmx_R(ShaderIrBlock Block, long OpCode, long Position)
|
||||
{
|
||||
EmitFmnmx(Block, OpCode, ShaderOper.RR);
|
||||
}
|
||||
|
||||
public static void Fmul_I32(ShaderIrBlock Block, long OpCode)
|
||||
public static void Fmul_I32(ShaderIrBlock Block, long OpCode, long Position)
|
||||
{
|
||||
ShaderIrNode OperA = GetOperGpr8 (OpCode);
|
||||
ShaderIrNode OperB = GetOperImmf32_20(OpCode);
|
||||
|
@ -99,62 +99,62 @@ namespace Ryujinx.Graphics.Gal.Shader
|
|||
Block.AddNode(GetPredNode(new ShaderIrAsg(GetOperGpr0(OpCode), Op), OpCode));
|
||||
}
|
||||
|
||||
public static void Fmul_C(ShaderIrBlock Block, long OpCode)
|
||||
public static void Fmul_C(ShaderIrBlock Block, long OpCode, long Position)
|
||||
{
|
||||
EmitFmul(Block, OpCode, ShaderOper.CR);
|
||||
}
|
||||
|
||||
public static void Fmul_I(ShaderIrBlock Block, long OpCode)
|
||||
public static void Fmul_I(ShaderIrBlock Block, long OpCode, long Position)
|
||||
{
|
||||
EmitFmul(Block, OpCode, ShaderOper.Immf);
|
||||
}
|
||||
|
||||
public static void Fmul_R(ShaderIrBlock Block, long OpCode)
|
||||
public static void Fmul_R(ShaderIrBlock Block, long OpCode, long Position)
|
||||
{
|
||||
EmitFmul(Block, OpCode, ShaderOper.RR);
|
||||
}
|
||||
|
||||
public static void Fset_C(ShaderIrBlock Block, long OpCode)
|
||||
public static void Fset_C(ShaderIrBlock Block, long OpCode, long Position)
|
||||
{
|
||||
EmitFset(Block, OpCode, ShaderOper.CR);
|
||||
}
|
||||
|
||||
public static void Fset_I(ShaderIrBlock Block, long OpCode)
|
||||
public static void Fset_I(ShaderIrBlock Block, long OpCode, long Position)
|
||||
{
|
||||
EmitFset(Block, OpCode, ShaderOper.Immf);
|
||||
}
|
||||
|
||||
public static void Fset_R(ShaderIrBlock Block, long OpCode)
|
||||
public static void Fset_R(ShaderIrBlock Block, long OpCode, long Position)
|
||||
{
|
||||
EmitFset(Block, OpCode, ShaderOper.RR);
|
||||
}
|
||||
|
||||
public static void Fsetp_C(ShaderIrBlock Block, long OpCode)
|
||||
public static void Fsetp_C(ShaderIrBlock Block, long OpCode, long Position)
|
||||
{
|
||||
EmitFsetp(Block, OpCode, ShaderOper.CR);
|
||||
}
|
||||
|
||||
public static void Fsetp_I(ShaderIrBlock Block, long OpCode)
|
||||
public static void Fsetp_I(ShaderIrBlock Block, long OpCode, long Position)
|
||||
{
|
||||
EmitFsetp(Block, OpCode, ShaderOper.Immf);
|
||||
}
|
||||
|
||||
public static void Fsetp_R(ShaderIrBlock Block, long OpCode)
|
||||
public static void Fsetp_R(ShaderIrBlock Block, long OpCode, long Position)
|
||||
{
|
||||
EmitFsetp(Block, OpCode, ShaderOper.RR);
|
||||
}
|
||||
|
||||
public static void Iadd_C(ShaderIrBlock Block, long OpCode)
|
||||
public static void Iadd_C(ShaderIrBlock Block, long OpCode, long Position)
|
||||
{
|
||||
EmitIadd(Block, OpCode, ShaderOper.CR);
|
||||
}
|
||||
|
||||
public static void Iadd_I(ShaderIrBlock Block, long OpCode)
|
||||
public static void Iadd_I(ShaderIrBlock Block, long OpCode, long Position)
|
||||
{
|
||||
EmitIadd(Block, OpCode, ShaderOper.Imm);
|
||||
}
|
||||
|
||||
public static void Iadd_I32(ShaderIrBlock Block, long OpCode)
|
||||
public static void Iadd_I32(ShaderIrBlock Block, long OpCode, long Position)
|
||||
{
|
||||
ShaderIrNode OperA = GetOperGpr8 (OpCode);
|
||||
ShaderIrNode OperB = GetOperImm32_20(OpCode);
|
||||
|
@ -168,97 +168,101 @@ namespace Ryujinx.Graphics.Gal.Shader
|
|||
Block.AddNode(GetPredNode(new ShaderIrAsg(GetOperGpr0(OpCode), Op), OpCode));
|
||||
}
|
||||
|
||||
public static void Iadd_R(ShaderIrBlock Block, long OpCode)
|
||||
public static void Iadd_R(ShaderIrBlock Block, long OpCode, long Position)
|
||||
{
|
||||
EmitIadd(Block, OpCode, ShaderOper.RR);
|
||||
}
|
||||
|
||||
public static void Iadd3_C(ShaderIrBlock Block, long OpCode)
|
||||
public static void Iadd3_C(ShaderIrBlock Block, long OpCode, long Position)
|
||||
{
|
||||
EmitIadd3(Block, OpCode, ShaderOper.CR);
|
||||
}
|
||||
|
||||
public static void Iadd3_I(ShaderIrBlock Block, long OpCode)
|
||||
public static void Iadd3_I(ShaderIrBlock Block, long OpCode, long Position)
|
||||
{
|
||||
EmitIadd3(Block, OpCode, ShaderOper.Imm);
|
||||
}
|
||||
|
||||
public static void Iadd3_R(ShaderIrBlock Block, long OpCode)
|
||||
public static void Iadd3_R(ShaderIrBlock Block, long OpCode, long Position)
|
||||
{
|
||||
EmitIadd3(Block, OpCode, ShaderOper.RR);
|
||||
}
|
||||
|
||||
public static void Imnmx_C(ShaderIrBlock Block, long OpCode)
|
||||
public static void Imnmx_C(ShaderIrBlock Block, long OpCode, long Position)
|
||||
{
|
||||
EmitImnmx(Block, OpCode, ShaderOper.CR);
|
||||
}
|
||||
|
||||
public static void Imnmx_I(ShaderIrBlock Block, long OpCode)
|
||||
public static void Imnmx_I(ShaderIrBlock Block, long OpCode, long Position)
|
||||
{
|
||||
EmitImnmx(Block, OpCode, ShaderOper.Imm);
|
||||
}
|
||||
|
||||
public static void Imnmx_R(ShaderIrBlock Block, long OpCode)
|
||||
public static void Imnmx_R(ShaderIrBlock Block, long OpCode, long Position)
|
||||
{
|
||||
EmitImnmx(Block, OpCode, ShaderOper.RR);
|
||||
}
|
||||
|
||||
public static void Ipa(ShaderIrBlock Block, long OpCode)
|
||||
public static void Ipa(ShaderIrBlock Block, long OpCode, long Position)
|
||||
{
|
||||
ShaderIrNode OperA = GetOperAbuf28(OpCode);
|
||||
ShaderIrNode OperB = GetOperGpr20 (OpCode);
|
||||
|
||||
ShaderIrOp Op = new ShaderIrOp(ShaderIrInst.Ipa, OperA, OperB);
|
||||
ShaderIpaMode Mode = (ShaderIpaMode)((OpCode >> 54) & 3);
|
||||
|
||||
ShaderIrMetaIpa Meta = new ShaderIrMetaIpa(Mode);
|
||||
|
||||
ShaderIrOp Op = new ShaderIrOp(ShaderIrInst.Ipa, OperA, OperB, null, Meta);
|
||||
|
||||
Block.AddNode(GetPredNode(new ShaderIrAsg(GetOperGpr0(OpCode), Op), OpCode));
|
||||
}
|
||||
|
||||
public static void Iscadd_C(ShaderIrBlock Block, long OpCode)
|
||||
public static void Iscadd_C(ShaderIrBlock Block, long OpCode, long Position)
|
||||
{
|
||||
EmitIscadd(Block, OpCode, ShaderOper.CR);
|
||||
}
|
||||
|
||||
public static void Iscadd_I(ShaderIrBlock Block, long OpCode)
|
||||
public static void Iscadd_I(ShaderIrBlock Block, long OpCode, long Position)
|
||||
{
|
||||
EmitIscadd(Block, OpCode, ShaderOper.Imm);
|
||||
}
|
||||
|
||||
public static void Iscadd_R(ShaderIrBlock Block, long OpCode)
|
||||
public static void Iscadd_R(ShaderIrBlock Block, long OpCode, long Position)
|
||||
{
|
||||
EmitIscadd(Block, OpCode, ShaderOper.RR);
|
||||
}
|
||||
|
||||
public static void Iset_C(ShaderIrBlock Block, long OpCode)
|
||||
public static void Iset_C(ShaderIrBlock Block, long OpCode, long Position)
|
||||
{
|
||||
EmitIset(Block, OpCode, ShaderOper.CR);
|
||||
}
|
||||
|
||||
public static void Iset_I(ShaderIrBlock Block, long OpCode)
|
||||
public static void Iset_I(ShaderIrBlock Block, long OpCode, long Position)
|
||||
{
|
||||
EmitIset(Block, OpCode, ShaderOper.Imm);
|
||||
}
|
||||
|
||||
public static void Iset_R(ShaderIrBlock Block, long OpCode)
|
||||
public static void Iset_R(ShaderIrBlock Block, long OpCode, long Position)
|
||||
{
|
||||
EmitIset(Block, OpCode, ShaderOper.RR);
|
||||
}
|
||||
|
||||
public static void Isetp_C(ShaderIrBlock Block, long OpCode)
|
||||
public static void Isetp_C(ShaderIrBlock Block, long OpCode, long Position)
|
||||
{
|
||||
EmitIsetp(Block, OpCode, ShaderOper.CR);
|
||||
}
|
||||
|
||||
public static void Isetp_I(ShaderIrBlock Block, long OpCode)
|
||||
public static void Isetp_I(ShaderIrBlock Block, long OpCode, long Position)
|
||||
{
|
||||
EmitIsetp(Block, OpCode, ShaderOper.Imm);
|
||||
}
|
||||
|
||||
public static void Isetp_R(ShaderIrBlock Block, long OpCode)
|
||||
public static void Isetp_R(ShaderIrBlock Block, long OpCode, long Position)
|
||||
{
|
||||
EmitIsetp(Block, OpCode, ShaderOper.RR);
|
||||
}
|
||||
|
||||
public static void Lop_I32(ShaderIrBlock Block, long OpCode)
|
||||
public static void Lop_I32(ShaderIrBlock Block, long OpCode, long Position)
|
||||
{
|
||||
int SubOp = (int)(OpCode >> 53) & 3;
|
||||
|
||||
|
@ -292,22 +296,22 @@ namespace Ryujinx.Graphics.Gal.Shader
|
|||
}
|
||||
}
|
||||
|
||||
public static void Lop_C(ShaderIrBlock Block, long OpCode)
|
||||
public static void Lop_C(ShaderIrBlock Block, long OpCode, long Position)
|
||||
{
|
||||
EmitLop(Block, OpCode, ShaderOper.CR);
|
||||
}
|
||||
|
||||
public static void Lop_I(ShaderIrBlock Block, long OpCode)
|
||||
public static void Lop_I(ShaderIrBlock Block, long OpCode, long Position)
|
||||
{
|
||||
EmitLop(Block, OpCode, ShaderOper.Imm);
|
||||
}
|
||||
|
||||
public static void Lop_R(ShaderIrBlock Block, long OpCode)
|
||||
public static void Lop_R(ShaderIrBlock Block, long OpCode, long Position)
|
||||
{
|
||||
EmitLop(Block, OpCode, ShaderOper.RR);
|
||||
}
|
||||
|
||||
public static void Mufu(ShaderIrBlock Block, long OpCode)
|
||||
public static void Mufu(ShaderIrBlock Block, long OpCode, long Position)
|
||||
{
|
||||
int SubOp = (int)(OpCode >> 20) & 0xf;
|
||||
|
||||
|
@ -336,7 +340,7 @@ namespace Ryujinx.Graphics.Gal.Shader
|
|||
Block.AddNode(GetPredNode(new ShaderIrAsg(GetOperGpr0(OpCode), Op), OpCode));
|
||||
}
|
||||
|
||||
public static void Psetp(ShaderIrBlock Block, long OpCode)
|
||||
public static void Psetp(ShaderIrBlock Block, long OpCode, long Position)
|
||||
{
|
||||
bool NegA = ((OpCode >> 15) & 1) != 0;
|
||||
bool NegB = ((OpCode >> 32) & 1) != 0;
|
||||
|
@ -390,47 +394,47 @@ namespace Ryujinx.Graphics.Gal.Shader
|
|||
Block.AddNode(GetPredNode(new ShaderIrAsg(P0Node, Op), OpCode));
|
||||
}
|
||||
|
||||
public static void Rro_C(ShaderIrBlock Block, long OpCode)
|
||||
public static void Rro_C(ShaderIrBlock Block, long OpCode, long Position)
|
||||
{
|
||||
EmitRro(Block, OpCode, ShaderOper.CR);
|
||||
}
|
||||
|
||||
public static void Rro_I(ShaderIrBlock Block, long OpCode)
|
||||
public static void Rro_I(ShaderIrBlock Block, long OpCode, long Position)
|
||||
{
|
||||
EmitRro(Block, OpCode, ShaderOper.Immf);
|
||||
}
|
||||
|
||||
public static void Rro_R(ShaderIrBlock Block, long OpCode)
|
||||
public static void Rro_R(ShaderIrBlock Block, long OpCode, long Position)
|
||||
{
|
||||
EmitRro(Block, OpCode, ShaderOper.RR);
|
||||
}
|
||||
|
||||
public static void Shl_C(ShaderIrBlock Block, long OpCode)
|
||||
public static void Shl_C(ShaderIrBlock Block, long OpCode, long Position)
|
||||
{
|
||||
EmitAluBinary(Block, OpCode, ShaderOper.CR, ShaderIrInst.Lsl);
|
||||
}
|
||||
|
||||
public static void Shl_I(ShaderIrBlock Block, long OpCode)
|
||||
public static void Shl_I(ShaderIrBlock Block, long OpCode, long Position)
|
||||
{
|
||||
EmitAluBinary(Block, OpCode, ShaderOper.Imm, ShaderIrInst.Lsl);
|
||||
}
|
||||
|
||||
public static void Shl_R(ShaderIrBlock Block, long OpCode)
|
||||
public static void Shl_R(ShaderIrBlock Block, long OpCode, long Position)
|
||||
{
|
||||
EmitAluBinary(Block, OpCode, ShaderOper.RR, ShaderIrInst.Lsl);
|
||||
}
|
||||
|
||||
public static void Shr_C(ShaderIrBlock Block, long OpCode)
|
||||
public static void Shr_C(ShaderIrBlock Block, long OpCode, long Position)
|
||||
{
|
||||
EmitAluBinary(Block, OpCode, ShaderOper.CR, GetShrInst(OpCode));
|
||||
}
|
||||
|
||||
public static void Shr_I(ShaderIrBlock Block, long OpCode)
|
||||
public static void Shr_I(ShaderIrBlock Block, long OpCode, long Position)
|
||||
{
|
||||
EmitAluBinary(Block, OpCode, ShaderOper.Imm, GetShrInst(OpCode));
|
||||
}
|
||||
|
||||
public static void Shr_R(ShaderIrBlock Block, long OpCode)
|
||||
public static void Shr_R(ShaderIrBlock Block, long OpCode, long Position)
|
||||
{
|
||||
EmitAluBinary(Block, OpCode, ShaderOper.RR, GetShrInst(OpCode));
|
||||
}
|
||||
|
@ -442,7 +446,7 @@ namespace Ryujinx.Graphics.Gal.Shader
|
|||
return Signed ? ShaderIrInst.Asr : ShaderIrInst.Lsr;
|
||||
}
|
||||
|
||||
public static void Vmad(ShaderIrBlock Block, long OpCode)
|
||||
public static void Vmad(ShaderIrBlock Block, long OpCode, long Position)
|
||||
{
|
||||
ShaderIrNode OperA = GetOperGpr8(OpCode);
|
||||
|
||||
|
@ -477,22 +481,22 @@ namespace Ryujinx.Graphics.Gal.Shader
|
|||
Block.AddNode(GetPredNode(new ShaderIrAsg(GetOperGpr0(OpCode), Final), OpCode));
|
||||
}
|
||||
|
||||
public static void Xmad_CR(ShaderIrBlock Block, long OpCode)
|
||||
public static void Xmad_CR(ShaderIrBlock Block, long OpCode, long Position)
|
||||
{
|
||||
EmitXmad(Block, OpCode, ShaderOper.CR);
|
||||
}
|
||||
|
||||
public static void Xmad_I(ShaderIrBlock Block, long OpCode)
|
||||
public static void Xmad_I(ShaderIrBlock Block, long OpCode, long Position)
|
||||
{
|
||||
EmitXmad(Block, OpCode, ShaderOper.Imm);
|
||||
}
|
||||
|
||||
public static void Xmad_RC(ShaderIrBlock Block, long OpCode)
|
||||
public static void Xmad_RC(ShaderIrBlock Block, long OpCode, long Position)
|
||||
{
|
||||
EmitXmad(Block, OpCode, ShaderOper.RC);
|
||||
}
|
||||
|
||||
public static void Xmad_RR(ShaderIrBlock Block, long OpCode)
|
||||
public static void Xmad_RR(ShaderIrBlock Block, long OpCode, long Position)
|
||||
{
|
||||
EmitXmad(Block, OpCode, ShaderOper.RR);
|
||||
}
|
||||
|
|
|
@ -6,7 +6,7 @@ namespace Ryujinx.Graphics.Gal.Shader
|
|||
{
|
||||
static partial class ShaderDecode
|
||||
{
|
||||
public static void Bra(ShaderIrBlock Block, long OpCode)
|
||||
public static void Bra(ShaderIrBlock Block, long OpCode, long Position)
|
||||
{
|
||||
if ((OpCode & 0x20) != 0)
|
||||
{
|
||||
|
@ -22,7 +22,7 @@ namespace Ryujinx.Graphics.Gal.Shader
|
|||
Block.AddNode(GetPredNode(new ShaderIrOp(ShaderIrInst.Bra, Imm), OpCode));
|
||||
}
|
||||
|
||||
public static void Exit(ShaderIrBlock Block, long OpCode)
|
||||
public static void Exit(ShaderIrBlock Block, long OpCode, long Position)
|
||||
{
|
||||
int CCode = (int)OpCode & 0x1f;
|
||||
|
||||
|
@ -34,9 +34,34 @@ namespace Ryujinx.Graphics.Gal.Shader
|
|||
|
||||
}
|
||||
|
||||
public static void Kil(ShaderIrBlock Block, long OpCode)
|
||||
public static void Kil(ShaderIrBlock Block, long OpCode, long Position)
|
||||
{
|
||||
Block.AddNode(GetPredNode(new ShaderIrOp(ShaderIrInst.Kil), OpCode));
|
||||
}
|
||||
|
||||
public static void Ssy(ShaderIrBlock Block, long OpCode, long Position)
|
||||
{
|
||||
if ((OpCode & 0x20) != 0)
|
||||
{
|
||||
//This reads the target offset from the constant buffer.
|
||||
//Almost impossible to support with GLSL.
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
int Offset = ((int)(OpCode >> 20) << 8) >> 8;
|
||||
|
||||
int Target = (int)(Position + Offset);
|
||||
|
||||
ShaderIrOperImm Imm = new ShaderIrOperImm(Target);
|
||||
|
||||
Block.AddNode(new ShaderIrOp(ShaderIrInst.Ssy, Imm));
|
||||
}
|
||||
|
||||
public static void Sync(ShaderIrBlock Block, long OpCode, long Position)
|
||||
{
|
||||
//TODO: Implement Sync condition codes
|
||||
|
||||
Block.AddNode(GetPredNode(new ShaderIrOp(ShaderIrInst.Sync), OpCode));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -35,13 +35,6 @@ namespace Ryujinx.Graphics.Gal.Shader
|
|||
(int)(OpCode >> 20) & 0x3fff);
|
||||
}
|
||||
|
||||
public static ShaderIrOperCbuf GetOperCbuf36(long OpCode)
|
||||
{
|
||||
return new ShaderIrOperCbuf(
|
||||
(int)(OpCode >> 36) & 0x1f,
|
||||
(int)(OpCode >> 22) & 0x3fff, GetOperGpr8(OpCode));
|
||||
}
|
||||
|
||||
public static ShaderIrOperGpr GetOperGpr8(long OpCode)
|
||||
{
|
||||
return new ShaderIrOperGpr((int)(OpCode >> 8) & 0xff);
|
||||
|
|
|
@ -31,7 +31,7 @@ namespace Ryujinx.Graphics.Gal.Shader
|
|||
{ RGB_, RG_A, R_BA, _GBA, RGBA, ____, ____, ____ }
|
||||
};
|
||||
|
||||
public static void Ld_A(ShaderIrBlock Block, long OpCode)
|
||||
public static void Ld_A(ShaderIrBlock Block, long OpCode, long Position)
|
||||
{
|
||||
ShaderIrNode[] Opers = GetOperAbuf20(OpCode);
|
||||
|
||||
|
@ -50,25 +50,37 @@ namespace Ryujinx.Graphics.Gal.Shader
|
|||
}
|
||||
}
|
||||
|
||||
public static void Ld_C(ShaderIrBlock Block, long OpCode)
|
||||
public static void Ld_C(ShaderIrBlock Block, long OpCode, long Position)
|
||||
{
|
||||
int Type = (int)(OpCode >> 48) & 7;
|
||||
int CbufPos = (int)(OpCode >> 22) & 0x3fff;
|
||||
int CbufIndex = (int)(OpCode >> 36) & 0x1f;
|
||||
int Type = (int)(OpCode >> 48) & 7;
|
||||
|
||||
if (Type > 5)
|
||||
{
|
||||
throw new InvalidOperationException();
|
||||
}
|
||||
|
||||
ShaderIrOperGpr Temp = ShaderIrOperGpr.MakeTemporary();
|
||||
|
||||
Block.AddNode(new ShaderIrAsg(Temp, GetOperGpr8(OpCode)));
|
||||
|
||||
int Count = Type == 5 ? 2 : 1;
|
||||
|
||||
for (int Index = 0; Index < Count; Index++)
|
||||
{
|
||||
ShaderIrOperCbuf OperA = GetOperCbuf36(OpCode);
|
||||
ShaderIrOperGpr OperD = GetOperGpr0 (OpCode);
|
||||
ShaderIrOperCbuf OperA = new ShaderIrOperCbuf(CbufIndex, CbufPos, Temp);
|
||||
|
||||
ShaderIrOperGpr OperD = GetOperGpr0(OpCode);
|
||||
|
||||
OperA.Pos += Index;
|
||||
OperD.Index += Index;
|
||||
|
||||
if (!OperD.IsValidRegister)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
ShaderIrNode Node = OperA;
|
||||
|
||||
if (Type < 4)
|
||||
|
@ -85,7 +97,7 @@ namespace Ryujinx.Graphics.Gal.Shader
|
|||
}
|
||||
}
|
||||
|
||||
public static void St_A(ShaderIrBlock Block, long OpCode)
|
||||
public static void St_A(ShaderIrBlock Block, long OpCode, long Position)
|
||||
{
|
||||
ShaderIrNode[] Opers = GetOperAbuf20(OpCode);
|
||||
|
||||
|
@ -101,7 +113,7 @@ namespace Ryujinx.Graphics.Gal.Shader
|
|||
}
|
||||
}
|
||||
|
||||
public static void Texq(ShaderIrBlock Block, long OpCode)
|
||||
public static void Texq(ShaderIrBlock Block, long OpCode, long Position)
|
||||
{
|
||||
ShaderIrNode OperD = GetOperGpr0(OpCode);
|
||||
ShaderIrNode OperA = GetOperGpr8(OpCode);
|
||||
|
@ -120,12 +132,12 @@ namespace Ryujinx.Graphics.Gal.Shader
|
|||
Block.AddNode(GetPredNode(new ShaderIrAsg(OperA, Op1), OpCode)); //Is this right?
|
||||
}
|
||||
|
||||
public static void Tex(ShaderIrBlock Block, long OpCode)
|
||||
public static void Tex(ShaderIrBlock Block, long OpCode, long Position)
|
||||
{
|
||||
EmitTex(Block, OpCode, GprHandle: false);
|
||||
}
|
||||
|
||||
public static void Tex_B(ShaderIrBlock Block, long OpCode)
|
||||
public static void Tex_B(ShaderIrBlock Block, long OpCode, long Position)
|
||||
{
|
||||
EmitTex(Block, OpCode, GprHandle: true);
|
||||
}
|
||||
|
@ -190,12 +202,12 @@ namespace Ryujinx.Graphics.Gal.Shader
|
|||
}
|
||||
}
|
||||
|
||||
public static void Texs(ShaderIrBlock Block, long OpCode)
|
||||
public static void Texs(ShaderIrBlock Block, long OpCode, long Position)
|
||||
{
|
||||
EmitTexs(Block, OpCode, ShaderIrInst.Texs);
|
||||
}
|
||||
|
||||
public static void Tlds(ShaderIrBlock Block, long OpCode)
|
||||
public static void Tlds(ShaderIrBlock Block, long OpCode, long Position)
|
||||
{
|
||||
EmitTexs(Block, OpCode, ShaderIrInst.Txlf);
|
||||
}
|
||||
|
|
|
@ -25,67 +25,67 @@ namespace Ryujinx.Graphics.Gal.Shader
|
|||
F64 = 3
|
||||
}
|
||||
|
||||
public static void F2f_C(ShaderIrBlock Block, long OpCode)
|
||||
public static void F2f_C(ShaderIrBlock Block, long OpCode, long Position)
|
||||
{
|
||||
EmitF2f(Block, OpCode, ShaderOper.CR);
|
||||
}
|
||||
|
||||
public static void F2f_I(ShaderIrBlock Block, long OpCode)
|
||||
public static void F2f_I(ShaderIrBlock Block, long OpCode, long Position)
|
||||
{
|
||||
EmitF2f(Block, OpCode, ShaderOper.Immf);
|
||||
}
|
||||
|
||||
public static void F2f_R(ShaderIrBlock Block, long OpCode)
|
||||
public static void F2f_R(ShaderIrBlock Block, long OpCode, long Position)
|
||||
{
|
||||
EmitF2f(Block, OpCode, ShaderOper.RR);
|
||||
}
|
||||
|
||||
public static void F2i_C(ShaderIrBlock Block, long OpCode)
|
||||
public static void F2i_C(ShaderIrBlock Block, long OpCode, long Position)
|
||||
{
|
||||
EmitF2i(Block, OpCode, ShaderOper.CR);
|
||||
}
|
||||
|
||||
public static void F2i_I(ShaderIrBlock Block, long OpCode)
|
||||
public static void F2i_I(ShaderIrBlock Block, long OpCode, long Position)
|
||||
{
|
||||
EmitF2i(Block, OpCode, ShaderOper.Immf);
|
||||
}
|
||||
|
||||
public static void F2i_R(ShaderIrBlock Block, long OpCode)
|
||||
public static void F2i_R(ShaderIrBlock Block, long OpCode, long Position)
|
||||
{
|
||||
EmitF2i(Block, OpCode, ShaderOper.RR);
|
||||
}
|
||||
|
||||
public static void I2f_C(ShaderIrBlock Block, long OpCode)
|
||||
public static void I2f_C(ShaderIrBlock Block, long OpCode, long Position)
|
||||
{
|
||||
EmitI2f(Block, OpCode, ShaderOper.CR);
|
||||
}
|
||||
|
||||
public static void I2f_I(ShaderIrBlock Block, long OpCode)
|
||||
public static void I2f_I(ShaderIrBlock Block, long OpCode, long Position)
|
||||
{
|
||||
EmitI2f(Block, OpCode, ShaderOper.Imm);
|
||||
}
|
||||
|
||||
public static void I2f_R(ShaderIrBlock Block, long OpCode)
|
||||
public static void I2f_R(ShaderIrBlock Block, long OpCode, long Position)
|
||||
{
|
||||
EmitI2f(Block, OpCode, ShaderOper.RR);
|
||||
}
|
||||
|
||||
public static void I2i_C(ShaderIrBlock Block, long OpCode)
|
||||
public static void I2i_C(ShaderIrBlock Block, long OpCode, long Position)
|
||||
{
|
||||
EmitI2i(Block, OpCode, ShaderOper.CR);
|
||||
}
|
||||
|
||||
public static void I2i_I(ShaderIrBlock Block, long OpCode)
|
||||
public static void I2i_I(ShaderIrBlock Block, long OpCode, long Position)
|
||||
{
|
||||
EmitI2i(Block, OpCode, ShaderOper.Imm);
|
||||
}
|
||||
|
||||
public static void I2i_R(ShaderIrBlock Block, long OpCode)
|
||||
public static void I2i_R(ShaderIrBlock Block, long OpCode, long Position)
|
||||
{
|
||||
EmitI2i(Block, OpCode, ShaderOper.RR);
|
||||
}
|
||||
|
||||
public static void Isberd(ShaderIrBlock Block, long OpCode)
|
||||
public static void Isberd(ShaderIrBlock Block, long OpCode, long Position)
|
||||
{
|
||||
//This instruction seems to be used to translate from an address to a vertex index in a GS
|
||||
//Stub it as such
|
||||
|
@ -95,50 +95,50 @@ namespace Ryujinx.Graphics.Gal.Shader
|
|||
Block.AddNode(GetPredNode(new ShaderIrAsg(GetOperGpr0(OpCode), GetOperGpr8(OpCode)), OpCode));
|
||||
}
|
||||
|
||||
public static void Mov_C(ShaderIrBlock Block, long OpCode)
|
||||
public static void Mov_C(ShaderIrBlock Block, long OpCode, long Position)
|
||||
{
|
||||
ShaderIrOperCbuf Cbuf = GetOperCbuf34(OpCode);
|
||||
|
||||
Block.AddNode(GetPredNode(new ShaderIrAsg(GetOperGpr0(OpCode), Cbuf), OpCode));
|
||||
}
|
||||
|
||||
public static void Mov_I(ShaderIrBlock Block, long OpCode)
|
||||
public static void Mov_I(ShaderIrBlock Block, long OpCode, long Position)
|
||||
{
|
||||
ShaderIrOperImm Imm = GetOperImm19_20(OpCode);
|
||||
|
||||
Block.AddNode(GetPredNode(new ShaderIrAsg(GetOperGpr0(OpCode), Imm), OpCode));
|
||||
}
|
||||
|
||||
public static void Mov_I32(ShaderIrBlock Block, long OpCode)
|
||||
public static void Mov_I32(ShaderIrBlock Block, long OpCode, long Position)
|
||||
{
|
||||
ShaderIrOperImm Imm = GetOperImm32_20(OpCode);
|
||||
|
||||
Block.AddNode(GetPredNode(new ShaderIrAsg(GetOperGpr0(OpCode), Imm), OpCode));
|
||||
}
|
||||
|
||||
public static void Mov_R(ShaderIrBlock Block, long OpCode)
|
||||
public static void Mov_R(ShaderIrBlock Block, long OpCode, long Position)
|
||||
{
|
||||
ShaderIrOperGpr Gpr = GetOperGpr20(OpCode);
|
||||
|
||||
Block.AddNode(GetPredNode(new ShaderIrAsg(GetOperGpr0(OpCode), Gpr), OpCode));
|
||||
}
|
||||
|
||||
public static void Sel_C(ShaderIrBlock Block, long OpCode)
|
||||
public static void Sel_C(ShaderIrBlock Block, long OpCode, long Position)
|
||||
{
|
||||
EmitSel(Block, OpCode, ShaderOper.CR);
|
||||
}
|
||||
|
||||
public static void Sel_I(ShaderIrBlock Block, long OpCode)
|
||||
public static void Sel_I(ShaderIrBlock Block, long OpCode, long Position)
|
||||
{
|
||||
EmitSel(Block, OpCode, ShaderOper.Imm);
|
||||
}
|
||||
|
||||
public static void Sel_R(ShaderIrBlock Block, long OpCode)
|
||||
public static void Sel_R(ShaderIrBlock Block, long OpCode, long Position)
|
||||
{
|
||||
EmitSel(Block, OpCode, ShaderOper.RR);
|
||||
}
|
||||
|
||||
public static void Mov_S(ShaderIrBlock Block, long OpCode)
|
||||
public static void Mov_S(ShaderIrBlock Block, long OpCode, long Position)
|
||||
{
|
||||
Block.AddNode(new ShaderIrCmnt("Stubbed."));
|
||||
|
||||
|
|
|
@ -4,7 +4,7 @@ namespace Ryujinx.Graphics.Gal.Shader
|
|||
{
|
||||
static partial class ShaderDecode
|
||||
{
|
||||
public static void Out_R(ShaderIrBlock Block, long OpCode)
|
||||
public static void Out_R(ShaderIrBlock Block, long OpCode, long Position)
|
||||
{
|
||||
//TODO: Those registers have to be used for something
|
||||
ShaderIrOperGpr Gpr0 = GetOperGpr0(OpCode);
|
||||
|
|
|
@ -50,17 +50,29 @@ namespace Ryujinx.Graphics.Gal.Shader
|
|||
{
|
||||
ShaderIrNode LastNode = Current.GetLastNode();
|
||||
|
||||
ShaderIrOp Op = GetInnermostOp(LastNode);
|
||||
ShaderIrOp InnerOp = GetInnermostOp(LastNode);
|
||||
|
||||
if (Op?.Inst == ShaderIrInst.Bra)
|
||||
if (InnerOp?.Inst == ShaderIrInst.Bra)
|
||||
{
|
||||
int Offset = ((ShaderIrOperImm)Op.OperandA).Value;
|
||||
int Offset = ((ShaderIrOperImm)InnerOp.OperandA).Value;
|
||||
|
||||
long Target = Current.EndPosition + Offset;
|
||||
|
||||
Current.Branch = Enqueue(Target, Current);
|
||||
}
|
||||
|
||||
foreach (ShaderIrNode Node in Current.Nodes)
|
||||
{
|
||||
if (Node is ShaderIrOp CurrOp && CurrOp.Inst == ShaderIrInst.Ssy)
|
||||
{
|
||||
int Offset = ((ShaderIrOperImm)CurrOp.OperandA).Value;
|
||||
|
||||
long Target = Offset;
|
||||
|
||||
Current.Branch = Enqueue(Target, Current);
|
||||
}
|
||||
}
|
||||
|
||||
if (NodeHasNext(LastNode))
|
||||
{
|
||||
Current.Next = Enqueue(Current.EndPosition);
|
||||
|
@ -157,7 +169,7 @@ namespace Ryujinx.Graphics.Gal.Shader
|
|||
{
|
||||
int Offset = ((int)(OpCode >> 20) << 8) >> 8;
|
||||
|
||||
long Target = Position + Offset;
|
||||
long Target = Position + Offset - Beginning;
|
||||
|
||||
DbgOpCode += " (0x" + Target.ToString("x16") + ")";
|
||||
}
|
||||
|
@ -170,7 +182,7 @@ namespace Ryujinx.Graphics.Gal.Shader
|
|||
continue;
|
||||
}
|
||||
|
||||
Decode(Block, OpCode);
|
||||
Decode(Block, OpCode, Position);
|
||||
}
|
||||
while (!IsFlowChange(Block.GetLastNode()));
|
||||
|
||||
|
|
|
@ -1,5 +1,30 @@
|
|||
namespace Ryujinx.Graphics.Gal.Shader
|
||||
using System;
|
||||
|
||||
namespace Ryujinx.Graphics.Gal.Shader
|
||||
{
|
||||
struct OmapTarget
|
||||
{
|
||||
public bool Red;
|
||||
public bool Green;
|
||||
public bool Blue;
|
||||
public bool Alpha;
|
||||
|
||||
public bool Enabled => Red || Green || Blue || Alpha;
|
||||
|
||||
public bool ComponentEnabled(int Component)
|
||||
{
|
||||
switch (Component)
|
||||
{
|
||||
case 0: return Red;
|
||||
case 1: return Green;
|
||||
case 2: return Blue;
|
||||
case 3: return Alpha;
|
||||
}
|
||||
|
||||
throw new ArgumentException(nameof(Component));
|
||||
}
|
||||
}
|
||||
|
||||
class ShaderHeader
|
||||
{
|
||||
public const int PointList = 1;
|
||||
|
@ -30,6 +55,10 @@
|
|||
public int StoreReqStart { get; private set; }
|
||||
public int StoreReqEnd { get; private set; }
|
||||
|
||||
public OmapTarget[] OmapTargets { get; private set; }
|
||||
public bool OmapSampleMask { get; private set; }
|
||||
public bool OmapDepth { get; private set; }
|
||||
|
||||
public ShaderHeader(IGalMemory Memory, long Position)
|
||||
{
|
||||
uint CommonWord0 = (uint)Memory.ReadInt32(Position + 0);
|
||||
|
@ -61,6 +90,50 @@
|
|||
MaxOutputVertexCount = ReadBits(CommonWord4, 0, 12);
|
||||
StoreReqStart = ReadBits(CommonWord4, 12, 8);
|
||||
StoreReqEnd = ReadBits(CommonWord4, 24, 8);
|
||||
|
||||
//Type 2 (fragment?) reading
|
||||
uint Type2OmapTarget = (uint)Memory.ReadInt32(Position + 72);
|
||||
uint Type2Omap = (uint)Memory.ReadInt32(Position + 76);
|
||||
|
||||
OmapTargets = new OmapTarget[8];
|
||||
|
||||
for (int i = 0; i < OmapTargets.Length; i++)
|
||||
{
|
||||
int Offset = i * 4;
|
||||
|
||||
OmapTargets[i] = new OmapTarget
|
||||
{
|
||||
Red = ReadBits(Type2OmapTarget, Offset + 0, 1) != 0,
|
||||
Green = ReadBits(Type2OmapTarget, Offset + 1, 1) != 0,
|
||||
Blue = ReadBits(Type2OmapTarget, Offset + 2, 1) != 0,
|
||||
Alpha = ReadBits(Type2OmapTarget, Offset + 3, 1) != 0
|
||||
};
|
||||
}
|
||||
|
||||
OmapSampleMask = ReadBits(Type2Omap, 0, 1) != 0;
|
||||
OmapDepth = ReadBits(Type2Omap, 1, 1) != 0;
|
||||
}
|
||||
|
||||
public int DepthRegister
|
||||
{
|
||||
get
|
||||
{
|
||||
int Count = 0;
|
||||
|
||||
for (int Index = 0; Index < OmapTargets.Length; Index++)
|
||||
{
|
||||
for (int Component = 0; Component < 4; Component++)
|
||||
{
|
||||
if (OmapTargets[Index].ComponentEnabled(Component))
|
||||
{
|
||||
Count++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Depth register is always two registers after the last color output
|
||||
return Count + 1;
|
||||
}
|
||||
}
|
||||
|
||||
private static int ReadBits(uint Word, int Offset, int BitWidth)
|
||||
|
|
10
Ryujinx.Graphics/Gal/Shader/ShaderIpaMode.cs
Normal file
10
Ryujinx.Graphics/Gal/Shader/ShaderIpaMode.cs
Normal file
|
@ -0,0 +1,10 @@
|
|||
namespace Ryujinx.Graphics.Gal.Shader
|
||||
{
|
||||
enum ShaderIpaMode
|
||||
{
|
||||
Pass = 0,
|
||||
None = 1,
|
||||
Constant = 2,
|
||||
Sc = 3
|
||||
}
|
||||
}
|
|
@ -84,6 +84,8 @@ namespace Ryujinx.Graphics.Gal.Shader
|
|||
Bra,
|
||||
Exit,
|
||||
Kil,
|
||||
Ssy,
|
||||
Sync,
|
||||
|
||||
Emit,
|
||||
Cut
|
||||
|
|
12
Ryujinx.Graphics/Gal/Shader/ShaderIrMetaIpa.cs
Normal file
12
Ryujinx.Graphics/Gal/Shader/ShaderIrMetaIpa.cs
Normal file
|
@ -0,0 +1,12 @@
|
|||
namespace Ryujinx.Graphics.Gal.Shader
|
||||
{
|
||||
class ShaderIrMetaIpa : ShaderIrMeta
|
||||
{
|
||||
public ShaderIpaMode Mode { get; private set; }
|
||||
|
||||
public ShaderIrMetaIpa(ShaderIpaMode Mode)
|
||||
{
|
||||
this.Mode = Mode;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -6,11 +6,18 @@ namespace Ryujinx.Graphics.Gal.Shader
|
|||
|
||||
public bool IsConst => Index == ZRIndex;
|
||||
|
||||
public bool IsValidRegister => (Index <= ZRIndex);
|
||||
|
||||
public int Index { get; set; }
|
||||
|
||||
public ShaderIrOperGpr(int Index)
|
||||
{
|
||||
this.Index = Index;
|
||||
}
|
||||
|
||||
public static ShaderIrOperGpr MakeTemporary(int Index = 0)
|
||||
{
|
||||
return new ShaderIrOperGpr(0x100 + Index);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -112,7 +112,9 @@ namespace Ryujinx.Graphics.Gal.Shader
|
|||
Set("0100110000101x", ShaderDecode.Shr_C);
|
||||
Set("0011100x00101x", ShaderDecode.Shr_I);
|
||||
Set("0101110000101x", ShaderDecode.Shr_R);
|
||||
Set("1110001010010x", ShaderDecode.Ssy);
|
||||
Set("1110111111110x", ShaderDecode.St_A);
|
||||
Set("1111000011111x", ShaderDecode.Sync);
|
||||
Set("110000xxxx111x", ShaderDecode.Tex);
|
||||
Set("1101111010111x", ShaderDecode.Tex_B);
|
||||
Set("1101111101001x", ShaderDecode.Texq);
|
||||
|
|
|
@ -27,6 +27,8 @@ namespace Ryujinx.HLE.Gpu.Engines
|
|||
|
||||
private List<long>[] UploadedKeys;
|
||||
|
||||
private int CurrentInstance = 0;
|
||||
|
||||
public NvGpuEngine3d(NvGpu Gpu)
|
||||
{
|
||||
this.Gpu = Gpu;
|
||||
|
@ -102,10 +104,15 @@ namespace Ryujinx.HLE.Gpu.Engines
|
|||
SetAlphaBlending(State);
|
||||
SetPrimitiveRestart(State);
|
||||
|
||||
//Enabling multiple framebuffer attachments cause graphics reggresions
|
||||
SetFrameBuffer(Vmm, 0);
|
||||
for (int FbIndex = 0; FbIndex < 8; FbIndex++)
|
||||
{
|
||||
SetFrameBuffer(Vmm, 0);
|
||||
}
|
||||
|
||||
SetZeta(Vmm);
|
||||
|
||||
SetRenderTargets();
|
||||
|
||||
long[] Keys = UploadShaders(Vmm);
|
||||
|
||||
Gpu.Renderer.Shader.BindProgram();
|
||||
|
@ -221,7 +228,7 @@ namespace Ryujinx.HLE.Gpu.Engines
|
|||
}
|
||||
|
||||
long Key = Vmm.GetPhysicalAddress(ZA);
|
||||
|
||||
|
||||
int Width = ReadRegister(NvGpuEngine3dReg.ZetaHoriz);
|
||||
int Height = ReadRegister(NvGpuEngine3dReg.ZetaVert);
|
||||
|
||||
|
@ -415,6 +422,33 @@ namespace Ryujinx.HLE.Gpu.Engines
|
|||
}
|
||||
}
|
||||
|
||||
private void SetRenderTargets()
|
||||
{
|
||||
bool SeparateFragData = (ReadRegister(NvGpuEngine3dReg.RTSeparateFragData) & 1) != 0;
|
||||
|
||||
if (SeparateFragData)
|
||||
{
|
||||
uint Control = (uint)(ReadRegister(NvGpuEngine3dReg.RTControl));
|
||||
|
||||
uint Count = Control & 0xf;
|
||||
|
||||
int[] Map = new int[Count];
|
||||
|
||||
for (int i = 0; i < Count; i++)
|
||||
{
|
||||
int Shift = 4 + i * 3;
|
||||
|
||||
Map[i] = (int)((Control >> Shift) & 7);
|
||||
}
|
||||
|
||||
Gpu.Renderer.FrameBuffer.SetMap(Map);
|
||||
}
|
||||
else
|
||||
{
|
||||
Gpu.Renderer.FrameBuffer.SetMap(null);
|
||||
}
|
||||
}
|
||||
|
||||
private void UploadTextures(NvGpuVmm Vmm, GalPipelineState State, long[] Keys)
|
||||
{
|
||||
long BaseShPosition = MakeInt64From2xInt32(NvGpuEngine3dReg.ShaderAddress);
|
||||
|
@ -442,8 +476,6 @@ namespace Ryujinx.HLE.Gpu.Engines
|
|||
|
||||
UploadTexture(Vmm, TexIndex, TextureHandle);
|
||||
|
||||
Gpu.Renderer.Shader.EnsureTextureBinding(DeclInfo.Name, TexIndex);
|
||||
|
||||
TexIndex++;
|
||||
}
|
||||
}
|
||||
|
@ -624,10 +656,25 @@ namespace Ryujinx.HLE.Gpu.Engines
|
|||
long VertexPosition = MakeInt64From2xInt32(NvGpuEngine3dReg.VertexArrayNAddress + Index * 4);
|
||||
long VertexEndPos = MakeInt64From2xInt32(NvGpuEngine3dReg.VertexArrayNEndAddr + Index * 2);
|
||||
|
||||
long VboKey = Vmm.GetPhysicalAddress(VertexPosition);
|
||||
int VertexDivisor = ReadRegister(NvGpuEngine3dReg.VertexArrayNDivisor + Index * 4);
|
||||
|
||||
bool Instanced = (ReadRegister(NvGpuEngine3dReg.VertexArrayNInstance + Index) & 1) != 0;
|
||||
|
||||
int Stride = Control & 0xfff;
|
||||
|
||||
if (Instanced && VertexDivisor != 0)
|
||||
{
|
||||
VertexPosition += Stride * (CurrentInstance / VertexDivisor);
|
||||
}
|
||||
|
||||
if (VertexPosition > VertexEndPos)
|
||||
{
|
||||
//Instance is invalid, ignore the draw call
|
||||
continue;
|
||||
}
|
||||
|
||||
long VboKey = Vmm.GetPhysicalAddress(VertexPosition);
|
||||
|
||||
long VbSize = (VertexEndPos - VertexPosition) + 1;
|
||||
|
||||
bool VboCached = Gpu.Renderer.Rasterizer.IsVboCached(VboKey, VbSize);
|
||||
|
@ -639,10 +686,12 @@ namespace Ryujinx.HLE.Gpu.Engines
|
|||
Gpu.Renderer.Rasterizer.CreateVbo(VboKey, (int)VbSize, DataAddress);
|
||||
}
|
||||
|
||||
State.VertexBindings[Index].Enabled = true;
|
||||
State.VertexBindings[Index].Stride = Stride;
|
||||
State.VertexBindings[Index].VboKey = VboKey;
|
||||
State.VertexBindings[Index].Attribs = Attribs[Index].ToArray();
|
||||
State.VertexBindings[Index].Enabled = true;
|
||||
State.VertexBindings[Index].Stride = Stride;
|
||||
State.VertexBindings[Index].VboKey = VboKey;
|
||||
State.VertexBindings[Index].Instanced = Instanced;
|
||||
State.VertexBindings[Index].Divisor = VertexDivisor;
|
||||
State.VertexBindings[Index].Attribs = Attribs[Index].ToArray();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -653,6 +702,25 @@ namespace Ryujinx.HLE.Gpu.Engines
|
|||
|
||||
GalPrimitiveType PrimType = (GalPrimitiveType)(PrimCtrl & 0xffff);
|
||||
|
||||
bool InstanceNext = ((PrimCtrl >> 26) & 1) != 0;
|
||||
bool InstanceCont = ((PrimCtrl >> 27) & 1) != 0;
|
||||
|
||||
if (InstanceNext && InstanceCont)
|
||||
{
|
||||
throw new InvalidOperationException("GPU tried to increase and reset instance count at the same time");
|
||||
}
|
||||
|
||||
if (InstanceNext)
|
||||
{
|
||||
CurrentInstance++;
|
||||
}
|
||||
else if (!InstanceCont)
|
||||
{
|
||||
CurrentInstance = 0;
|
||||
}
|
||||
|
||||
State.Instance = CurrentInstance;
|
||||
|
||||
Gpu.Renderer.Pipeline.Bind(State);
|
||||
|
||||
if (IndexCount != 0)
|
||||
|
@ -680,22 +748,43 @@ namespace Ryujinx.HLE.Gpu.Engines
|
|||
WriteRegister(NvGpuEngine3dReg.IndexBatchCount, 0);
|
||||
}
|
||||
|
||||
private enum QueryMode
|
||||
{
|
||||
WriteSeq,
|
||||
Sync,
|
||||
WriteCounterAndTimestamp
|
||||
}
|
||||
|
||||
private void QueryControl(NvGpuVmm Vmm, NvGpuPBEntry PBEntry)
|
||||
{
|
||||
WriteRegister(PBEntry);
|
||||
|
||||
long Position = MakeInt64From2xInt32(NvGpuEngine3dReg.QueryAddress);
|
||||
|
||||
int Seq = Registers[(int)NvGpuEngine3dReg.QuerySequence];
|
||||
int Ctrl = Registers[(int)NvGpuEngine3dReg.QueryControl];
|
||||
|
||||
int Mode = Ctrl & 3;
|
||||
QueryMode Mode = (QueryMode)(Ctrl & 3);
|
||||
|
||||
if (Mode == 0)
|
||||
switch (Mode)
|
||||
{
|
||||
//Write mode.
|
||||
Vmm.WriteInt32(Position, Seq);
|
||||
}
|
||||
case QueryMode.WriteSeq: Vmm.WriteInt32(Position, Seq); break;
|
||||
|
||||
WriteRegister(PBEntry);
|
||||
case QueryMode.WriteCounterAndTimestamp:
|
||||
{
|
||||
//TODO: Implement counters.
|
||||
long Counter = 1;
|
||||
|
||||
long Timestamp = (uint)Environment.TickCount;
|
||||
|
||||
Timestamp = (long)(Timestamp * 615384.615385);
|
||||
|
||||
Vmm.WriteInt64(Position + 0, Counter);
|
||||
Vmm.WriteInt64(Position + 8, Timestamp);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void CbData(NvGpuVmm Vmm, NvGpuPBEntry PBEntry)
|
||||
|
@ -803,4 +892,4 @@ namespace Ryujinx.HLE.Gpu.Engines
|
|||
return Vmm.IsRegionModified(Key, Size, Type);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -22,11 +22,13 @@ namespace Ryujinx.HLE.Gpu.Engines
|
|||
StencilBackFuncRef = 0x3d5,
|
||||
StencilBackMask = 0x3d6,
|
||||
StencilBackFuncMask = 0x3d7,
|
||||
RTSeparateFragData = 0x3eb,
|
||||
ZetaAddress = 0x3f8,
|
||||
ZetaFormat = 0x3fa,
|
||||
ZetaBlockDimensions = 0x3fb,
|
||||
ZetaLayerStride = 0x3fc,
|
||||
VertexAttribNFormat = 0x458,
|
||||
RTControl = 0x487,
|
||||
ZetaHoriz = 0x48a,
|
||||
ZetaVert = 0x48b,
|
||||
ZetaArrayMode = 0x48c,
|
||||
|
@ -51,6 +53,7 @@ namespace Ryujinx.HLE.Gpu.Engines
|
|||
StencilFrontFuncMask = 0x4e6,
|
||||
StencilFrontMask = 0x4e7,
|
||||
VertexArrayElemBase = 0x50d,
|
||||
VertexArrayInstBase = 0x50e,
|
||||
ZetaEnable = 0x54e,
|
||||
TexHeaderPoolOffset = 0x55d,
|
||||
TexSamplerPoolOffset = 0x557,
|
||||
|
@ -68,6 +71,7 @@ namespace Ryujinx.HLE.Gpu.Engines
|
|||
IndexArrayFormat = 0x5f6,
|
||||
IndexBatchFirst = 0x5f7,
|
||||
IndexBatchCount = 0x5f8,
|
||||
VertexArrayNInstance = 0x620,
|
||||
CullFaceEnable = 0x646,
|
||||
FrontFace = 0x647,
|
||||
CullFace = 0x648,
|
||||
|
|
|
@ -44,6 +44,10 @@ namespace Ryujinx.HLE.Gpu.Texture
|
|||
case GalImageFormat.R16G16B16A16_SNORM:
|
||||
case GalImageFormat.R16G16B16A16_UINT:
|
||||
case GalImageFormat.R16G16B16A16_UNORM:
|
||||
case GalImageFormat.D32_SFLOAT_S8_UINT:
|
||||
case GalImageFormat.R32G32_SFLOAT:
|
||||
case GalImageFormat.R32G32_SINT:
|
||||
case GalImageFormat.R32G32_UINT:
|
||||
return Image.Width * Image.Height * 8;
|
||||
|
||||
case GalImageFormat.A8B8G8R8_SINT_PACK32:
|
||||
|
@ -174,7 +178,7 @@ namespace Ryujinx.HLE.Gpu.Texture
|
|||
}
|
||||
}
|
||||
|
||||
throw new NotImplementedException("0x" + Image.Format.ToString("x2"));
|
||||
throw new NotImplementedException(Image.Format.ToString());
|
||||
}
|
||||
|
||||
public static int CompressedTextureSize(int TextureWidth, int TextureHeight, int BlockWidth, int BlockHeight, int Bpb)
|
||||
|
|
|
@ -12,6 +12,7 @@ namespace Ryujinx.HLE.Gpu.Texture
|
|||
{
|
||||
case GalTextureFormat.R32G32B32A32: return Read16Bpp (Memory, Texture);
|
||||
case GalTextureFormat.R16G16B16A16: return Read8Bpp (Memory, Texture);
|
||||
case GalTextureFormat.R32G32: 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);
|
||||
|
@ -32,6 +33,7 @@ namespace Ryujinx.HLE.Gpu.Texture
|
|||
case GalTextureFormat.BC4: return Read8Bpt4x4 (Memory, Texture);
|
||||
case GalTextureFormat.BC5: return Read16BptCompressedTexture(Memory, Texture, 4, 4);
|
||||
case GalTextureFormat.ZF32: return Read4Bpp (Memory, Texture);
|
||||
case GalTextureFormat.ZF32_X24S8: return Read8Bpp (Memory, Texture);
|
||||
case GalTextureFormat.Astc2D4x4: return Read16BptCompressedTexture(Memory, Texture, 4, 4);
|
||||
case GalTextureFormat.Astc2D5x5: return Read16BptCompressedTexture(Memory, Texture, 5, 5);
|
||||
case GalTextureFormat.Astc2D6x6: return Read16BptCompressedTexture(Memory, Texture, 6, 6);
|
||||
|
@ -48,7 +50,7 @@ namespace Ryujinx.HLE.Gpu.Texture
|
|||
case GalTextureFormat.Astc2D10x6: return Read16BptCompressedTexture(Memory, Texture, 10, 6);
|
||||
}
|
||||
|
||||
throw new NotImplementedException("0x" + Texture.Format.ToString("x2"));
|
||||
throw new NotImplementedException("0x" + ((int)Texture.Format).ToString("x2"));
|
||||
}
|
||||
|
||||
private unsafe static byte[] Read1Bpp(IAMemory Memory, TextureInfo Texture)
|
||||
|
|
|
@ -174,39 +174,39 @@ namespace Ryujinx.HLE.HOS.Ipc
|
|||
return 0;
|
||||
}
|
||||
|
||||
public (long Position, long Size) GetBufferType0x21()
|
||||
public (long Position, long Size) GetBufferType0x21(int Index = 0)
|
||||
{
|
||||
if (PtrBuff.Count != 0 &&
|
||||
PtrBuff[0].Position != 0 &&
|
||||
PtrBuff[0].Size != 0)
|
||||
if (PtrBuff.Count > Index &&
|
||||
PtrBuff[Index].Position != 0 &&
|
||||
PtrBuff[Index].Size != 0)
|
||||
{
|
||||
return (PtrBuff[0].Position, PtrBuff[0].Size);
|
||||
return (PtrBuff[Index].Position, PtrBuff[Index].Size);
|
||||
}
|
||||
|
||||
if (SendBuff.Count != 0 &&
|
||||
SendBuff[0].Position != 0 &&
|
||||
SendBuff[0].Size != 0)
|
||||
if (SendBuff.Count > Index &&
|
||||
SendBuff[Index].Position != 0 &&
|
||||
SendBuff[Index].Size != 0)
|
||||
{
|
||||
return (SendBuff[0].Position, SendBuff[0].Size);
|
||||
return (SendBuff[Index].Position, SendBuff[Index].Size);
|
||||
}
|
||||
|
||||
return (0, 0);
|
||||
}
|
||||
|
||||
public (long Position, long Size) GetBufferType0x22()
|
||||
public (long Position, long Size) GetBufferType0x22(int Index = 0)
|
||||
{
|
||||
if (RecvListBuff.Count != 0 &&
|
||||
RecvListBuff[0].Position != 0 &&
|
||||
RecvListBuff[0].Size != 0)
|
||||
if (RecvListBuff.Count > Index &&
|
||||
RecvListBuff[Index].Position != 0 &&
|
||||
RecvListBuff[Index].Size != 0)
|
||||
{
|
||||
return (RecvListBuff[0].Position, RecvListBuff[0].Size);
|
||||
return (RecvListBuff[Index].Position, RecvListBuff[Index].Size);
|
||||
}
|
||||
|
||||
if (ReceiveBuff.Count != 0 &&
|
||||
ReceiveBuff[0].Position != 0 &&
|
||||
ReceiveBuff[0].Size != 0)
|
||||
if (ReceiveBuff.Count > Index &&
|
||||
ReceiveBuff[Index].Position != 0 &&
|
||||
ReceiveBuff[Index].Size != 0)
|
||||
{
|
||||
return (ReceiveBuff[0].Position, ReceiveBuff[0].Size);
|
||||
return (ReceiveBuff[Index].Position, ReceiveBuff[Index].Size);
|
||||
}
|
||||
|
||||
return (0, 0);
|
||||
|
|
|
@ -9,9 +9,9 @@ namespace Ryujinx.HLE.HOS.Kernel
|
|||
{
|
||||
private void SvcSetHeapSize(AThreadState ThreadState)
|
||||
{
|
||||
long Size = (long)ThreadState.X1;
|
||||
ulong Size = ThreadState.X1;
|
||||
|
||||
if ((Size & 0x1fffff) != 0 || Size != (uint)Size)
|
||||
if ((Size & 0xFFFFFFFE001FFFFF) != 0)
|
||||
{
|
||||
Device.Log.PrintWarning(LogClass.KernelSvc, $"Heap size 0x{Size:x16} is not aligned!");
|
||||
|
||||
|
@ -20,7 +20,7 @@ namespace Ryujinx.HLE.HOS.Kernel
|
|||
return;
|
||||
}
|
||||
|
||||
long Result = Process.MemoryManager.TrySetHeapSize(Size, out long Position);
|
||||
long Result = Process.MemoryManager.TrySetHeapSize((long)Size, out long Position);
|
||||
|
||||
ThreadState.X0 = (ulong)Result;
|
||||
|
||||
|
|
|
@ -7,13 +7,13 @@ using static Ryujinx.HLE.HOS.ErrorCode;
|
|||
|
||||
namespace Ryujinx.HLE.HOS.Services.Acc
|
||||
{
|
||||
class IAccountServiceForApplication : IpcService
|
||||
class IAccountService : IpcService
|
||||
{
|
||||
private Dictionary<int, ServiceProcessRequest> m_Commands;
|
||||
|
||||
public override IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
|
||||
|
||||
public IAccountServiceForApplication()
|
||||
public IAccountService()
|
||||
{
|
||||
m_Commands = new Dictionary<int, ServiceProcessRequest>()
|
||||
{
|
|
@ -3,7 +3,10 @@ using Ryujinx.HLE.HOS.Ipc;
|
|||
using Ryujinx.HLE.HOS.SystemState;
|
||||
using Ryujinx.HLE.Logging;
|
||||
using Ryujinx.HLE.Utilities;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
|
||||
namespace Ryujinx.HLE.HOS.Services.Acc
|
||||
|
@ -16,15 +19,21 @@ namespace Ryujinx.HLE.HOS.Services.Acc
|
|||
|
||||
private UserProfile Profile;
|
||||
|
||||
private Stream ProfilePictureStream;
|
||||
|
||||
public IProfile(UserProfile Profile)
|
||||
{
|
||||
m_Commands = new Dictionary<int, ServiceProcessRequest>()
|
||||
{
|
||||
{ 0, Get },
|
||||
{ 1, GetBase }
|
||||
{ 0, Get },
|
||||
{ 1, GetBase },
|
||||
{ 10, GetImageSize },
|
||||
{ 11, LoadImage },
|
||||
};
|
||||
|
||||
this.Profile = Profile;
|
||||
|
||||
ProfilePictureStream = Assembly.GetCallingAssembly().GetManifestResourceStream("Ryujinx.HLE.RyujinxProfileImage.jpg");
|
||||
}
|
||||
|
||||
public long Get(ServiceCtx Context)
|
||||
|
@ -54,5 +63,28 @@ namespace Ryujinx.HLE.HOS.Services.Acc
|
|||
|
||||
return 0;
|
||||
}
|
||||
|
||||
private long LoadImage(ServiceCtx Context)
|
||||
{
|
||||
long BufferPosition = Context.Request.ReceiveBuff[0].Position;
|
||||
long BufferLen = Context.Request.ReceiveBuff[0].Size;
|
||||
|
||||
byte[] ProfilePictureData = new byte[BufferLen];
|
||||
|
||||
ProfilePictureStream.Read(ProfilePictureData, 0, ProfilePictureData.Length);
|
||||
|
||||
Context.Memory.WriteBytes(BufferPosition, ProfilePictureData);
|
||||
|
||||
Context.ResponseData.Write(ProfilePictureStream.Length);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
private long GetImageSize(ServiceCtx Context)
|
||||
{
|
||||
Context.ResponseData.Write(ProfilePictureStream.Length);
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -32,6 +32,8 @@ namespace Ryujinx.HLE.HOS.Services.Bsd
|
|||
{ 14, Connect },
|
||||
{ 18, Listen },
|
||||
{ 21, SetSockOpt },
|
||||
{ 24, Write },
|
||||
{ 25, Read },
|
||||
{ 26, Close }
|
||||
};
|
||||
}
|
||||
|
@ -122,15 +124,15 @@ namespace Ryujinx.HLE.HOS.Services.Bsd
|
|||
int SocketId = Context.RequestData.ReadInt32();
|
||||
int SocketFlags = Context.RequestData.ReadInt32();
|
||||
|
||||
byte[] ReceivedBuffer = new byte[Context.Request.ReceiveBuff[0].Size];
|
||||
(long ReceivePosition, long ReceiveLength) = Context.Request.GetBufferType0x22();
|
||||
|
||||
byte[] ReceivedBuffer = new byte[ReceiveLength];
|
||||
|
||||
try
|
||||
{
|
||||
int BytesRead = Sockets[SocketId].Handle.Receive(ReceivedBuffer);
|
||||
|
||||
//Logging.Debug("Received Buffer:" + Environment.NewLine + Logging.HexDump(ReceivedBuffer));
|
||||
|
||||
Context.Memory.WriteBytes(Context.Request.ReceiveBuff[0].Position, ReceivedBuffer);
|
||||
Context.Memory.WriteBytes(ReceivePosition, ReceivedBuffer);
|
||||
|
||||
Context.ResponseData.Write(BytesRead);
|
||||
Context.ResponseData.Write(0);
|
||||
|
@ -150,13 +152,12 @@ namespace Ryujinx.HLE.HOS.Services.Bsd
|
|||
int SocketId = Context.RequestData.ReadInt32();
|
||||
int SocketFlags = Context.RequestData.ReadInt32();
|
||||
|
||||
byte[] SentBuffer = Context.Memory.ReadBytes(Context.Request.SendBuff[0].Position,
|
||||
Context.Request.SendBuff[0].Size);
|
||||
(long SentPosition, long SentSize) = Context.Request.GetBufferType0x21();
|
||||
|
||||
byte[] SentBuffer = Context.Memory.ReadBytes(SentPosition, SentSize);
|
||||
|
||||
try
|
||||
{
|
||||
//Logging.Debug("Sent Buffer:" + Environment.NewLine + Logging.HexDump(SentBuffer));
|
||||
|
||||
int BytesSent = Sockets[SocketId].Handle.Send(SentBuffer);
|
||||
|
||||
Context.ResponseData.Write(BytesSent);
|
||||
|
@ -180,8 +181,9 @@ namespace Ryujinx.HLE.HOS.Services.Bsd
|
|||
byte[] SentBuffer = Context.Memory.ReadBytes(Context.Request.SendBuff[0].Position,
|
||||
Context.Request.SendBuff[0].Size);
|
||||
|
||||
byte[] AddressBuffer = Context.Memory.ReadBytes(Context.Request.SendBuff[1].Position,
|
||||
Context.Request.SendBuff[1].Size);
|
||||
(long AddressPosition, long AddressSize) = Context.Request.GetBufferType0x21(Index: 1);
|
||||
|
||||
byte[] AddressBuffer = Context.Memory.ReadBytes(AddressPosition, AddressSize);
|
||||
|
||||
if (!Sockets[SocketId].Handle.Connected)
|
||||
{
|
||||
|
@ -200,8 +202,6 @@ namespace Ryujinx.HLE.HOS.Services.Bsd
|
|||
|
||||
try
|
||||
{
|
||||
//Logging.Debug("Sent Buffer:" + Environment.NewLine + Logging.HexDump(SentBuffer));
|
||||
|
||||
int BytesSent = Sockets[SocketId].Handle.Send(SentBuffer);
|
||||
|
||||
Context.ResponseData.Write(BytesSent);
|
||||
|
@ -221,7 +221,7 @@ namespace Ryujinx.HLE.HOS.Services.Bsd
|
|||
{
|
||||
int SocketId = Context.RequestData.ReadInt32();
|
||||
|
||||
long AddrBufferPtr = Context.Request.ReceiveBuff[0].Position;
|
||||
(long AddrBufferPosition, long AddrBuffSize) = Context.Request.GetBufferType0x22();
|
||||
|
||||
Socket HandleAccept = null;
|
||||
|
||||
|
@ -246,7 +246,7 @@ namespace Ryujinx.HLE.HOS.Services.Bsd
|
|||
{
|
||||
IpAddress = ((IPEndPoint)Sockets[SocketId].Handle.LocalEndPoint).Address,
|
||||
RemoteEP = ((IPEndPoint)Sockets[SocketId].Handle.LocalEndPoint),
|
||||
Handle = HandleAccept
|
||||
Handle = HandleAccept
|
||||
};
|
||||
|
||||
Sockets.Add(NewBsdSocket);
|
||||
|
@ -265,7 +265,7 @@ namespace Ryujinx.HLE.HOS.Services.Bsd
|
|||
|
||||
Writer.Write(IpAddress);
|
||||
|
||||
Context.Memory.WriteBytes(AddrBufferPtr, MS.ToArray());
|
||||
Context.Memory.WriteBytes(AddrBufferPosition, MS.ToArray());
|
||||
|
||||
Context.ResponseData.Write(Sockets.Count - 1);
|
||||
Context.ResponseData.Write(0);
|
||||
|
@ -286,8 +286,9 @@ namespace Ryujinx.HLE.HOS.Services.Bsd
|
|||
{
|
||||
int SocketId = Context.RequestData.ReadInt32();
|
||||
|
||||
byte[] AddressBuffer = Context.Memory.ReadBytes(Context.Request.SendBuff[0].Position,
|
||||
Context.Request.SendBuff[0].Size);
|
||||
(long AddressPosition, long AddressSize) = Context.Request.GetBufferType0x21();
|
||||
|
||||
byte[] AddressBuffer = Context.Memory.ReadBytes(AddressPosition, AddressSize);
|
||||
|
||||
try
|
||||
{
|
||||
|
@ -310,8 +311,9 @@ namespace Ryujinx.HLE.HOS.Services.Bsd
|
|||
{
|
||||
int SocketId = Context.RequestData.ReadInt32();
|
||||
|
||||
byte[] AddressBuffer = Context.Memory.ReadBytes(Context.Request.SendBuff[0].Position,
|
||||
Context.Request.SendBuff[0].Size);
|
||||
(long AddressPosition, long AddressSize) = Context.Request.GetBufferType0x21();
|
||||
|
||||
byte[] AddressBuffer = Context.Memory.ReadBytes(AddressPosition, AddressSize);
|
||||
|
||||
try
|
||||
{
|
||||
|
@ -359,8 +361,8 @@ namespace Ryujinx.HLE.HOS.Services.Bsd
|
|||
{
|
||||
int SocketId = Context.RequestData.ReadInt32();
|
||||
|
||||
SocketOptionLevel SocketLevel = (SocketOptionLevel)Context.RequestData.ReadInt32();
|
||||
SocketOptionName SocketOptionName = (SocketOptionName)Context.RequestData.ReadInt32();
|
||||
SocketOptionLevel SocketLevel = (SocketOptionLevel)Context.RequestData.ReadInt32();
|
||||
SocketOptionName SocketOptionName = (SocketOptionName)Context.RequestData.ReadInt32();
|
||||
|
||||
byte[] SocketOptionValue = Context.Memory.ReadBytes(Context.Request.PtrBuff[0].Position,
|
||||
Context.Request.PtrBuff[0].Size);
|
||||
|
@ -383,6 +385,60 @@ namespace Ryujinx.HLE.HOS.Services.Bsd
|
|||
return 0;
|
||||
}
|
||||
|
||||
//(u32 socket, buffer<i8, 0x21, 0> message) -> (i32 ret, u32 bsd_errno)
|
||||
public long Write(ServiceCtx Context)
|
||||
{
|
||||
int SocketId = Context.RequestData.ReadInt32();
|
||||
|
||||
(long SentPosition, long SentSize) = Context.Request.GetBufferType0x21();
|
||||
|
||||
byte[] SentBuffer = Context.Memory.ReadBytes(SentPosition, SentSize);
|
||||
|
||||
try
|
||||
{
|
||||
//Logging.Debug("Wrote Buffer:" + Environment.NewLine + Logging.HexDump(SentBuffer));
|
||||
|
||||
int BytesSent = Sockets[SocketId].Handle.Send(SentBuffer);
|
||||
|
||||
Context.ResponseData.Write(BytesSent);
|
||||
Context.ResponseData.Write(0);
|
||||
}
|
||||
catch (SocketException Ex)
|
||||
{
|
||||
Context.ResponseData.Write(-1);
|
||||
Context.ResponseData.Write(Ex.ErrorCode - 10000);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
//(u32 socket) -> (i32 ret, u32 bsd_errno, buffer<i8, 0x22, 0> message)
|
||||
public long Read(ServiceCtx Context)
|
||||
{
|
||||
int SocketId = Context.RequestData.ReadInt32();
|
||||
|
||||
(long ReceivePosition, long ReceiveLength) = Context.Request.GetBufferType0x22();
|
||||
|
||||
byte[] ReceivedBuffer = new byte[ReceiveLength];
|
||||
|
||||
try
|
||||
{
|
||||
int BytesRead = Sockets[SocketId].Handle.Receive(ReceivedBuffer);
|
||||
|
||||
Context.Memory.WriteBytes(ReceivePosition, ReceivedBuffer);
|
||||
|
||||
Context.ResponseData.Write(BytesRead);
|
||||
Context.ResponseData.Write(0);
|
||||
}
|
||||
catch (SocketException Ex)
|
||||
{
|
||||
Context.ResponseData.Write(-1);
|
||||
Context.ResponseData.Write(Ex.ErrorCode - 10000);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
//(u32 socket) -> (i32 ret, u32 bsd_errno)
|
||||
public long Close(ServiceCtx Context)
|
||||
{
|
||||
|
@ -413,7 +469,7 @@ namespace Ryujinx.HLE.HOS.Services.Bsd
|
|||
|
||||
int Size = Reader.ReadByte();
|
||||
int Family = Reader.ReadByte();
|
||||
int Port = EndianSwap.Swap16(Reader.ReadInt16());
|
||||
int Port = EndianSwap.Swap16(Reader.ReadUInt16());
|
||||
|
||||
string IpAddress = Reader.ReadByte().ToString() + "." +
|
||||
Reader.ReadByte().ToString() + "." +
|
||||
|
@ -421,8 +477,7 @@ namespace Ryujinx.HLE.HOS.Services.Bsd
|
|||
Reader.ReadByte().ToString();
|
||||
|
||||
Sockets[SocketId].IpAddress = IPAddress.Parse(IpAddress);
|
||||
|
||||
Sockets[SocketId].RemoteEP = new IPEndPoint(Sockets[SocketId].IpAddress, Port);
|
||||
Sockets[SocketId].RemoteEP = new IPEndPoint(Sockets[SocketId].IpAddress, Port);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -22,9 +22,9 @@ namespace Ryujinx.HLE.HOS.Services.Lm
|
|||
|
||||
public long Log(ServiceCtx Context)
|
||||
{
|
||||
byte[] LogBuffer = Context.Memory.ReadBytes(
|
||||
Context.Request.PtrBuff[0].Position,
|
||||
Context.Request.PtrBuff[0].Size);
|
||||
|
||||
(long BufPos, long BufSize) = Context.Request.GetBufferType0x21();
|
||||
byte[] LogBuffer = Context.Memory.ReadBytes(BufPos, BufSize);
|
||||
|
||||
using (MemoryStream MS = new MemoryStream(LogBuffer))
|
||||
{
|
||||
|
@ -50,20 +50,36 @@ namespace Ryujinx.HLE.HOS.Services.Lm
|
|||
|
||||
string FieldStr = string.Empty;
|
||||
|
||||
if (Field == LmLogField.Skip)
|
||||
if (Field == LmLogField.Start)
|
||||
{
|
||||
Reader.ReadByte();
|
||||
Reader.ReadBytes(Size);
|
||||
|
||||
continue;
|
||||
}
|
||||
else if (Field == LmLogField.Stop)
|
||||
{
|
||||
break;
|
||||
}
|
||||
else if (Field == LmLogField.Line)
|
||||
{
|
||||
FieldStr = Field + ": " + Reader.ReadInt32();
|
||||
}
|
||||
else
|
||||
else if (Field == LmLogField.DropCount)
|
||||
{
|
||||
FieldStr = Field + ": " + Reader.ReadInt64();
|
||||
}
|
||||
else if (Field == LmLogField.Time)
|
||||
{
|
||||
FieldStr = Field + ": " + Reader.ReadInt64() + "s";
|
||||
}
|
||||
else if (Field < LmLogField.Count)
|
||||
{
|
||||
FieldStr = Field + ": \"" + Encoding.UTF8.GetString(Reader.ReadBytes(Size)) + "\"";
|
||||
}
|
||||
else
|
||||
{
|
||||
FieldStr = "Field" + Field + ": \"" + Encoding.UTF8.GetString(Reader.ReadBytes(Size)) + "\"";
|
||||
}
|
||||
|
||||
SB.AppendLine(" " + FieldStr);
|
||||
}
|
||||
|
|
|
@ -2,12 +2,17 @@ namespace Ryujinx.HLE.HOS.Services.Lm
|
|||
{
|
||||
enum LmLogField
|
||||
{
|
||||
Skip = 1,
|
||||
Message = 2,
|
||||
Line = 3,
|
||||
Filename = 4,
|
||||
Function = 5,
|
||||
Module = 6,
|
||||
Thread = 7
|
||||
Start = 0,
|
||||
Stop = 1,
|
||||
Message = 2,
|
||||
Line = 3,
|
||||
Filename = 4,
|
||||
Function = 5,
|
||||
Module = 6,
|
||||
Thread = 7,
|
||||
DropCount = 8,
|
||||
Time = 9,
|
||||
ProgramName = 10,
|
||||
Count
|
||||
}
|
||||
}
|
|
@ -187,7 +187,10 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvGpuAS
|
|||
{
|
||||
Left = Middle + 1;
|
||||
|
||||
LtRg = Rg;
|
||||
if ((ulong)Position > Rg.Start)
|
||||
{
|
||||
LtRg = Rg;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -31,7 +31,10 @@ namespace Ryujinx.HLE.HOS.Services
|
|||
switch (Name)
|
||||
{
|
||||
case "acc:u0":
|
||||
return new IAccountServiceForApplication();
|
||||
return new IAccountService();
|
||||
|
||||
case "acc:u1":
|
||||
return new IAccountService();
|
||||
|
||||
case "aoc:u":
|
||||
return new IAddOnContentManager();
|
||||
|
|
|
@ -13,6 +13,14 @@
|
|||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Remove="RyujinxProfileImage.jpg" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<EmbeddedResource Include="RyujinxProfileImage.jpg" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\ChocolArm64\ChocolArm64.csproj" />
|
||||
<ProjectReference Include="..\Ryujinx.Audio\Ryujinx.Audio.csproj" />
|
||||
|
|
BIN
Ryujinx.HLE/RyujinxProfileImage.jpg
Normal file
BIN
Ryujinx.HLE/RyujinxProfileImage.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 5.3 KiB |
|
@ -2,7 +2,7 @@
|
|||
{
|
||||
static class EndianSwap
|
||||
{
|
||||
public static short Swap16(short Value) => (short)(((Value >> 8) & 0xff) | (Value << 8));
|
||||
public static ushort Swap16(ushort Value) => (ushort)(((Value >> 8) & 0xff) | (Value << 8));
|
||||
|
||||
public static int Swap32(int Value)
|
||||
{
|
||||
|
|
28
Ryujinx.Tests.Unicorn/IndexedProperty.cs
Normal file
28
Ryujinx.Tests.Unicorn/IndexedProperty.cs
Normal file
|
@ -0,0 +1,28 @@
|
|||
using System;
|
||||
|
||||
namespace Ryujinx.Tests.Unicorn
|
||||
{
|
||||
public class IndexedProperty<TIndex, TValue>
|
||||
{
|
||||
readonly Action<TIndex, TValue> SetAction;
|
||||
readonly Func<TIndex, TValue> GetFunc;
|
||||
|
||||
public IndexedProperty(Func<TIndex, TValue> getFunc, Action<TIndex, TValue> setAction)
|
||||
{
|
||||
this.GetFunc = getFunc;
|
||||
this.SetAction = setAction;
|
||||
}
|
||||
|
||||
public TValue this[TIndex i]
|
||||
{
|
||||
get
|
||||
{
|
||||
return GetFunc(i);
|
||||
}
|
||||
set
|
||||
{
|
||||
SetAction(i, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
13
Ryujinx.Tests.Unicorn/MemoryPermission.cs
Normal file
13
Ryujinx.Tests.Unicorn/MemoryPermission.cs
Normal file
|
@ -0,0 +1,13 @@
|
|||
using System;
|
||||
|
||||
namespace Ryujinx.Tests.Unicorn
|
||||
{
|
||||
public enum MemoryPermission
|
||||
{
|
||||
NONE = 0,
|
||||
READ = 1,
|
||||
WRITE = 2,
|
||||
EXEC = 4,
|
||||
ALL = 7,
|
||||
}
|
||||
}
|
296
Ryujinx.Tests.Unicorn/Native/ArmRegister.cs
Normal file
296
Ryujinx.Tests.Unicorn/Native/ArmRegister.cs
Normal file
|
@ -0,0 +1,296 @@
|
|||
using System;
|
||||
|
||||
namespace Ryujinx.Tests.Unicorn.Native
|
||||
{
|
||||
public enum ArmRegister
|
||||
{
|
||||
INVALID = 0,
|
||||
|
||||
X29,
|
||||
X30,
|
||||
NZCV,
|
||||
SP,
|
||||
WSP,
|
||||
WZR,
|
||||
XZR,
|
||||
B0,
|
||||
B1,
|
||||
B2,
|
||||
B3,
|
||||
B4,
|
||||
B5,
|
||||
B6,
|
||||
B7,
|
||||
B8,
|
||||
B9,
|
||||
B10,
|
||||
B11,
|
||||
B12,
|
||||
B13,
|
||||
B14,
|
||||
B15,
|
||||
B16,
|
||||
B17,
|
||||
B18,
|
||||
B19,
|
||||
B20,
|
||||
B21,
|
||||
B22,
|
||||
B23,
|
||||
B24,
|
||||
B25,
|
||||
B26,
|
||||
B27,
|
||||
B28,
|
||||
B29,
|
||||
B30,
|
||||
B31,
|
||||
D0,
|
||||
D1,
|
||||
D2,
|
||||
D3,
|
||||
D4,
|
||||
D5,
|
||||
D6,
|
||||
D7,
|
||||
D8,
|
||||
D9,
|
||||
D10,
|
||||
D11,
|
||||
D12,
|
||||
D13,
|
||||
D14,
|
||||
D15,
|
||||
D16,
|
||||
D17,
|
||||
D18,
|
||||
D19,
|
||||
D20,
|
||||
D21,
|
||||
D22,
|
||||
D23,
|
||||
D24,
|
||||
D25,
|
||||
D26,
|
||||
D27,
|
||||
D28,
|
||||
D29,
|
||||
D30,
|
||||
D31,
|
||||
H0,
|
||||
H1,
|
||||
H2,
|
||||
H3,
|
||||
H4,
|
||||
H5,
|
||||
H6,
|
||||
H7,
|
||||
H8,
|
||||
H9,
|
||||
H10,
|
||||
H11,
|
||||
H12,
|
||||
H13,
|
||||
H14,
|
||||
H15,
|
||||
H16,
|
||||
H17,
|
||||
H18,
|
||||
H19,
|
||||
H20,
|
||||
H21,
|
||||
H22,
|
||||
H23,
|
||||
H24,
|
||||
H25,
|
||||
H26,
|
||||
H27,
|
||||
H28,
|
||||
H29,
|
||||
H30,
|
||||
H31,
|
||||
Q0,
|
||||
Q1,
|
||||
Q2,
|
||||
Q3,
|
||||
Q4,
|
||||
Q5,
|
||||
Q6,
|
||||
Q7,
|
||||
Q8,
|
||||
Q9,
|
||||
Q10,
|
||||
Q11,
|
||||
Q12,
|
||||
Q13,
|
||||
Q14,
|
||||
Q15,
|
||||
Q16,
|
||||
Q17,
|
||||
Q18,
|
||||
Q19,
|
||||
Q20,
|
||||
Q21,
|
||||
Q22,
|
||||
Q23,
|
||||
Q24,
|
||||
Q25,
|
||||
Q26,
|
||||
Q27,
|
||||
Q28,
|
||||
Q29,
|
||||
Q30,
|
||||
Q31,
|
||||
S0,
|
||||
S1,
|
||||
S2,
|
||||
S3,
|
||||
S4,
|
||||
S5,
|
||||
S6,
|
||||
S7,
|
||||
S8,
|
||||
S9,
|
||||
S10,
|
||||
S11,
|
||||
S12,
|
||||
S13,
|
||||
S14,
|
||||
S15,
|
||||
S16,
|
||||
S17,
|
||||
S18,
|
||||
S19,
|
||||
S20,
|
||||
S21,
|
||||
S22,
|
||||
S23,
|
||||
S24,
|
||||
S25,
|
||||
S26,
|
||||
S27,
|
||||
S28,
|
||||
S29,
|
||||
S30,
|
||||
S31,
|
||||
W0,
|
||||
W1,
|
||||
W2,
|
||||
W3,
|
||||
W4,
|
||||
W5,
|
||||
W6,
|
||||
W7,
|
||||
W8,
|
||||
W9,
|
||||
W10,
|
||||
W11,
|
||||
W12,
|
||||
W13,
|
||||
W14,
|
||||
W15,
|
||||
W16,
|
||||
W17,
|
||||
W18,
|
||||
W19,
|
||||
W20,
|
||||
W21,
|
||||
W22,
|
||||
W23,
|
||||
W24,
|
||||
W25,
|
||||
W26,
|
||||
W27,
|
||||
W28,
|
||||
W29,
|
||||
W30,
|
||||
X0,
|
||||
X1,
|
||||
X2,
|
||||
X3,
|
||||
X4,
|
||||
X5,
|
||||
X6,
|
||||
X7,
|
||||
X8,
|
||||
X9,
|
||||
X10,
|
||||
X11,
|
||||
X12,
|
||||
X13,
|
||||
X14,
|
||||
X15,
|
||||
X16,
|
||||
X17,
|
||||
X18,
|
||||
X19,
|
||||
X20,
|
||||
X21,
|
||||
X22,
|
||||
X23,
|
||||
X24,
|
||||
X25,
|
||||
X26,
|
||||
X27,
|
||||
X28,
|
||||
|
||||
V0,
|
||||
V1,
|
||||
V2,
|
||||
V3,
|
||||
V4,
|
||||
V5,
|
||||
V6,
|
||||
V7,
|
||||
V8,
|
||||
V9,
|
||||
V10,
|
||||
V11,
|
||||
V12,
|
||||
V13,
|
||||
V14,
|
||||
V15,
|
||||
V16,
|
||||
V17,
|
||||
V18,
|
||||
V19,
|
||||
V20,
|
||||
V21,
|
||||
V22,
|
||||
V23,
|
||||
V24,
|
||||
V25,
|
||||
V26,
|
||||
V27,
|
||||
V28,
|
||||
V29,
|
||||
V30,
|
||||
V31,
|
||||
|
||||
//> pseudo registers
|
||||
PC, // program counter register
|
||||
|
||||
CPACR_EL1,
|
||||
ESR,
|
||||
|
||||
//> thread registers
|
||||
TPIDR_EL0,
|
||||
TPIDRRO_EL0,
|
||||
TPIDR_EL1,
|
||||
|
||||
PSTATE, // PSTATE pseudoregister
|
||||
|
||||
//> floating point control and status registers
|
||||
FPCR,
|
||||
FPSR,
|
||||
|
||||
ENDING, // <-- mark the end of the list of registers
|
||||
|
||||
//> alias registers
|
||||
|
||||
IP0 = X16,
|
||||
IP1 = X17,
|
||||
FP = X29,
|
||||
LR = X30,
|
||||
}
|
||||
}
|
68
Ryujinx.Tests.Unicorn/Native/Interface.cs
Normal file
68
Ryujinx.Tests.Unicorn/Native/Interface.cs
Normal file
|
@ -0,0 +1,68 @@
|
|||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
using Ryujinx.Tests.Unicorn;
|
||||
|
||||
namespace Ryujinx.Tests.Unicorn.Native
|
||||
{
|
||||
public class Interface
|
||||
{
|
||||
public static void Checked(UnicornError error)
|
||||
{
|
||||
if (error != UnicornError.UC_ERR_OK)
|
||||
{
|
||||
throw new UnicornException(error);
|
||||
}
|
||||
}
|
||||
|
||||
public static void MarshalArrayOf<T>(IntPtr input, int length, out T[] output)
|
||||
{
|
||||
var size = Marshal.SizeOf(typeof(T));
|
||||
output = new T[length];
|
||||
|
||||
for (int i = 0; i < length; i++)
|
||||
{
|
||||
IntPtr item = new IntPtr(input.ToInt64() + i * size);
|
||||
output[i] = Marshal.PtrToStructure<T>(item);
|
||||
}
|
||||
}
|
||||
|
||||
[DllImport("unicorn", CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern uint uc_version(out uint major, out uint minor);
|
||||
|
||||
[DllImport("unicorn", CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern UnicornError uc_open(uint arch, uint mode, out IntPtr uc);
|
||||
|
||||
[DllImport("unicorn", CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern UnicornError uc_close(IntPtr uc);
|
||||
|
||||
[DllImport("unicorn", CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern IntPtr uc_strerror(UnicornError err);
|
||||
|
||||
[DllImport("unicorn", CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern UnicornError uc_reg_write(IntPtr uc, int regid, byte[] value);
|
||||
|
||||
[DllImport("unicorn", CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern UnicornError uc_reg_read(IntPtr uc, int regid, byte[] value);
|
||||
|
||||
[DllImport("unicorn", CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern UnicornError uc_mem_write(IntPtr uc, ulong address, byte[] bytes, ulong size);
|
||||
|
||||
[DllImport("unicorn", CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern UnicornError uc_mem_read(IntPtr uc, ulong address, byte[] bytes, ulong size);
|
||||
|
||||
[DllImport("unicorn", CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern UnicornError uc_emu_start(IntPtr uc, ulong begin, ulong until, ulong timeout, ulong count);
|
||||
|
||||
[DllImport("unicorn", CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern UnicornError uc_mem_map(IntPtr uc, ulong address, ulong size, uint perms);
|
||||
|
||||
[DllImport("unicorn", CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern UnicornError uc_mem_unmap(IntPtr uc, ulong address, ulong size);
|
||||
|
||||
[DllImport("unicorn", CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern UnicornError uc_mem_protect(IntPtr uc, ulong address, ulong size, uint perms);
|
||||
|
||||
[DllImport("unicorn", CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern UnicornError uc_mem_regions(IntPtr uc, out IntPtr regions, out uint count);
|
||||
}
|
||||
}
|
16
Ryujinx.Tests.Unicorn/Native/UnicornArch.cs
Normal file
16
Ryujinx.Tests.Unicorn/Native/UnicornArch.cs
Normal file
|
@ -0,0 +1,16 @@
|
|||
using System;
|
||||
|
||||
namespace Ryujinx.Tests.Unicorn.Native
|
||||
{
|
||||
public enum UnicornArch
|
||||
{
|
||||
UC_ARCH_ARM = 1, // ARM architecture (including Thumb, Thumb-2)
|
||||
UC_ARCH_ARM64, // ARM-64, also called AArch64
|
||||
UC_ARCH_MIPS, // Mips architecture
|
||||
UC_ARCH_X86, // X86 architecture (including x86 & x86-64)
|
||||
UC_ARCH_PPC, // PowerPC architecture (currently unsupported)
|
||||
UC_ARCH_SPARC, // Sparc architecture
|
||||
UC_ARCH_M68K, // M68K architecture
|
||||
UC_ARCH_MAX,
|
||||
}
|
||||
}
|
13
Ryujinx.Tests.Unicorn/Native/UnicornMemoryRegion.cs
Normal file
13
Ryujinx.Tests.Unicorn/Native/UnicornMemoryRegion.cs
Normal file
|
@ -0,0 +1,13 @@
|
|||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Ryujinx.Tests.Unicorn.Native
|
||||
{
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct UnicornMemoryRegion
|
||||
{
|
||||
public UInt64 begin; // begin address of the region (inclusive)
|
||||
public UInt64 end; // end address of the region (inclusive)
|
||||
public UInt32 perms; // memory permissions of the region
|
||||
}
|
||||
}
|
34
Ryujinx.Tests.Unicorn/Native/UnicornMode.cs
Normal file
34
Ryujinx.Tests.Unicorn/Native/UnicornMode.cs
Normal file
|
@ -0,0 +1,34 @@
|
|||
using System;
|
||||
|
||||
namespace Ryujinx.Tests.Unicorn.Native
|
||||
{
|
||||
public enum UnicornMode
|
||||
{
|
||||
UC_MODE_LITTLE_ENDIAN = 0, // little-endian mode (default mode)
|
||||
UC_MODE_BIG_ENDIAN = 1 << 30, // big-endian mode
|
||||
// arm / arm64
|
||||
UC_MODE_ARM = 0, // ARM mode
|
||||
UC_MODE_THUMB = 1 << 4, // THUMB mode (including Thumb-2)
|
||||
UC_MODE_MCLASS = 1 << 5, // ARM's Cortex-M series (currently unsupported)
|
||||
UC_MODE_V8 = 1 << 6, // ARMv8 A32 encodings for ARM (currently unsupported)
|
||||
// mips
|
||||
UC_MODE_MICRO = 1 << 4, // MicroMips mode (currently unsupported)
|
||||
UC_MODE_MIPS3 = 1 << 5, // Mips III ISA (currently unsupported)
|
||||
UC_MODE_MIPS32R6 = 1 << 6, // Mips32r6 ISA (currently unsupported)
|
||||
UC_MODE_MIPS32 = 1 << 2, // Mips32 ISA
|
||||
UC_MODE_MIPS64 = 1 << 3, // Mips64 ISA
|
||||
// x86 / x64
|
||||
UC_MODE_16 = 1 << 1, // 16-bit mode
|
||||
UC_MODE_32 = 1 << 2, // 32-bit mode
|
||||
UC_MODE_64 = 1 << 3, // 64-bit mode
|
||||
// ppc
|
||||
UC_MODE_PPC32 = 1 << 2, // 32-bit mode (currently unsupported)
|
||||
UC_MODE_PPC64 = 1 << 3, // 64-bit mode (currently unsupported)
|
||||
UC_MODE_QPX = 1 << 4, // Quad Processing eXtensions mode (currently unsupported)
|
||||
// sparc
|
||||
UC_MODE_SPARC32 = 1 << 2, // 32-bit mode
|
||||
UC_MODE_SPARC64 = 1 << 3, // 64-bit mode
|
||||
UC_MODE_V9 = 1 << 4, // SparcV9 mode (currently unsupported)
|
||||
// m68k
|
||||
}
|
||||
}
|
18
Ryujinx.Tests.Unicorn/Ryujinx.Tests.Unicorn.csproj
Normal file
18
Ryujinx.Tests.Unicorn/Ryujinx.Tests.Unicorn.csproj
Normal file
|
@ -0,0 +1,18 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netcoreapp2.1</TargetFramework>
|
||||
<RuntimeIdentifiers>win10-x64;osx-x64;linux-x64</RuntimeIdentifiers>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.8.0" />
|
||||
<PackageReference Include="System.Runtime.Intrinsics.Experimental" Version="4.5.0-rc1" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
315
Ryujinx.Tests.Unicorn/UnicornAArch64.cs
Normal file
315
Ryujinx.Tests.Unicorn/UnicornAArch64.cs
Normal file
|
@ -0,0 +1,315 @@
|
|||
using System;
|
||||
using System.Diagnostics.Contracts;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Runtime.Intrinsics;
|
||||
using System.Runtime.Intrinsics.X86;
|
||||
|
||||
namespace Ryujinx.Tests.Unicorn
|
||||
{
|
||||
public class UnicornAArch64
|
||||
{
|
||||
internal readonly IntPtr uc;
|
||||
|
||||
public IndexedProperty<int, ulong> X
|
||||
{
|
||||
get
|
||||
{
|
||||
return new IndexedProperty<int, ulong>(
|
||||
(int i) => GetX(i),
|
||||
(int i, ulong value) => SetX(i, value));
|
||||
}
|
||||
}
|
||||
|
||||
public IndexedProperty<int, Vector128<float>> Q
|
||||
{
|
||||
get
|
||||
{
|
||||
return new IndexedProperty<int, Vector128<float>>(
|
||||
(int i) => GetQ(i),
|
||||
(int i, Vector128<float> value) => SetQ(i, value));
|
||||
}
|
||||
}
|
||||
|
||||
public ulong LR
|
||||
{
|
||||
get { return GetRegister(Native.ArmRegister.LR); }
|
||||
set { SetRegister(Native.ArmRegister.LR, value); }
|
||||
}
|
||||
|
||||
public ulong SP
|
||||
{
|
||||
get { return GetRegister(Native.ArmRegister.SP); }
|
||||
set { SetRegister(Native.ArmRegister.SP, value); }
|
||||
}
|
||||
|
||||
public ulong PC
|
||||
{
|
||||
get { return GetRegister(Native.ArmRegister.PC); }
|
||||
set { SetRegister(Native.ArmRegister.PC, value); }
|
||||
}
|
||||
|
||||
public uint Pstate
|
||||
{
|
||||
get { return (uint)GetRegister(Native.ArmRegister.PSTATE); }
|
||||
set { SetRegister(Native.ArmRegister.PSTATE, (uint)value); }
|
||||
}
|
||||
|
||||
public int Fpcr
|
||||
{
|
||||
get { return (int)GetRegister(Native.ArmRegister.FPCR); }
|
||||
set { SetRegister(Native.ArmRegister.FPCR, (uint)value); }
|
||||
}
|
||||
|
||||
public int Fpsr
|
||||
{
|
||||
get { return (int)GetRegister(Native.ArmRegister.FPSR); }
|
||||
set { SetRegister(Native.ArmRegister.FPSR, (uint)value); }
|
||||
}
|
||||
|
||||
public bool OverflowFlag
|
||||
{
|
||||
get { return (Pstate & 0x10000000u) != 0; }
|
||||
set { Pstate = (Pstate & ~0x10000000u) | (value ? 0x10000000u : 0u); }
|
||||
}
|
||||
|
||||
public bool CarryFlag
|
||||
{
|
||||
get { return (Pstate & 0x20000000u) != 0; }
|
||||
set { Pstate = (Pstate & ~0x20000000u) | (value ? 0x20000000u : 0u); }
|
||||
}
|
||||
|
||||
public bool ZeroFlag
|
||||
{
|
||||
get { return (Pstate & 0x40000000u) != 0; }
|
||||
set { Pstate = (Pstate & ~0x40000000u) | (value ? 0x40000000u : 0u); }
|
||||
}
|
||||
|
||||
public bool NegativeFlag
|
||||
{
|
||||
get { return (Pstate & 0x80000000u) != 0; }
|
||||
set { Pstate = (Pstate & ~0x80000000u) | (value ? 0x80000000u : 0u); }
|
||||
}
|
||||
|
||||
public UnicornAArch64()
|
||||
{
|
||||
Native.Interface.Checked(Native.Interface.uc_open((uint)Native.UnicornArch.UC_ARCH_ARM64, (uint)Native.UnicornMode.UC_MODE_LITTLE_ENDIAN, out uc));
|
||||
SetRegister(Native.ArmRegister.CPACR_EL1, 0x00300000);
|
||||
}
|
||||
|
||||
~UnicornAArch64()
|
||||
{
|
||||
Native.Interface.Checked(Native.Interface.uc_close(uc));
|
||||
}
|
||||
|
||||
public void RunForCount(ulong count)
|
||||
{
|
||||
Native.Interface.Checked(Native.Interface.uc_emu_start(uc, this.PC, 0xFFFFFFFFFFFFFFFFu, 0, count));
|
||||
}
|
||||
|
||||
public void Step()
|
||||
{
|
||||
RunForCount(1);
|
||||
}
|
||||
|
||||
internal static Native.ArmRegister[] X_registers = new Native.ArmRegister[31]
|
||||
{
|
||||
Native.ArmRegister.X0,
|
||||
Native.ArmRegister.X1,
|
||||
Native.ArmRegister.X2,
|
||||
Native.ArmRegister.X3,
|
||||
Native.ArmRegister.X4,
|
||||
Native.ArmRegister.X5,
|
||||
Native.ArmRegister.X6,
|
||||
Native.ArmRegister.X7,
|
||||
Native.ArmRegister.X8,
|
||||
Native.ArmRegister.X9,
|
||||
Native.ArmRegister.X10,
|
||||
Native.ArmRegister.X11,
|
||||
Native.ArmRegister.X12,
|
||||
Native.ArmRegister.X13,
|
||||
Native.ArmRegister.X14,
|
||||
Native.ArmRegister.X15,
|
||||
Native.ArmRegister.X16,
|
||||
Native.ArmRegister.X17,
|
||||
Native.ArmRegister.X18,
|
||||
Native.ArmRegister.X19,
|
||||
Native.ArmRegister.X20,
|
||||
Native.ArmRegister.X21,
|
||||
Native.ArmRegister.X22,
|
||||
Native.ArmRegister.X23,
|
||||
Native.ArmRegister.X24,
|
||||
Native.ArmRegister.X25,
|
||||
Native.ArmRegister.X26,
|
||||
Native.ArmRegister.X27,
|
||||
Native.ArmRegister.X28,
|
||||
Native.ArmRegister.X29,
|
||||
Native.ArmRegister.X30,
|
||||
};
|
||||
|
||||
internal static Native.ArmRegister[] Q_registers = new Native.ArmRegister[32]
|
||||
{
|
||||
Native.ArmRegister.Q0,
|
||||
Native.ArmRegister.Q1,
|
||||
Native.ArmRegister.Q2,
|
||||
Native.ArmRegister.Q3,
|
||||
Native.ArmRegister.Q4,
|
||||
Native.ArmRegister.Q5,
|
||||
Native.ArmRegister.Q6,
|
||||
Native.ArmRegister.Q7,
|
||||
Native.ArmRegister.Q8,
|
||||
Native.ArmRegister.Q9,
|
||||
Native.ArmRegister.Q10,
|
||||
Native.ArmRegister.Q11,
|
||||
Native.ArmRegister.Q12,
|
||||
Native.ArmRegister.Q13,
|
||||
Native.ArmRegister.Q14,
|
||||
Native.ArmRegister.Q15,
|
||||
Native.ArmRegister.Q16,
|
||||
Native.ArmRegister.Q17,
|
||||
Native.ArmRegister.Q18,
|
||||
Native.ArmRegister.Q19,
|
||||
Native.ArmRegister.Q20,
|
||||
Native.ArmRegister.Q21,
|
||||
Native.ArmRegister.Q22,
|
||||
Native.ArmRegister.Q23,
|
||||
Native.ArmRegister.Q24,
|
||||
Native.ArmRegister.Q25,
|
||||
Native.ArmRegister.Q26,
|
||||
Native.ArmRegister.Q27,
|
||||
Native.ArmRegister.Q28,
|
||||
Native.ArmRegister.Q29,
|
||||
Native.ArmRegister.Q30,
|
||||
Native.ArmRegister.Q31,
|
||||
};
|
||||
|
||||
internal ulong GetRegister(Native.ArmRegister register)
|
||||
{
|
||||
byte[] value_bytes = new byte[8];
|
||||
Native.Interface.Checked(Native.Interface.uc_reg_read(uc, (int)register, value_bytes));
|
||||
return (ulong)BitConverter.ToInt64(value_bytes, 0);
|
||||
}
|
||||
|
||||
internal void SetRegister(Native.ArmRegister register, ulong value)
|
||||
{
|
||||
byte[] value_bytes = BitConverter.GetBytes(value);
|
||||
Native.Interface.Checked(Native.Interface.uc_reg_write(uc, (int)register, value_bytes));
|
||||
}
|
||||
|
||||
internal Vector128<float> GetVector(Native.ArmRegister register)
|
||||
{
|
||||
byte[] value_bytes = new byte[16];
|
||||
Native.Interface.Checked(Native.Interface.uc_reg_read(uc, (int)register, value_bytes));
|
||||
unsafe
|
||||
{
|
||||
fixed (byte* p = &value_bytes[0])
|
||||
{
|
||||
return Sse.LoadVector128((float*)p);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal void SetVector(Native.ArmRegister register, Vector128<float> value)
|
||||
{
|
||||
byte[] value_bytes = new byte[16];
|
||||
unsafe
|
||||
{
|
||||
fixed (byte* p = &value_bytes[0])
|
||||
{
|
||||
Sse.Store((float*)p, value);
|
||||
}
|
||||
}
|
||||
Native.Interface.Checked(Native.Interface.uc_reg_write(uc, (int)register, value_bytes));
|
||||
}
|
||||
|
||||
public ulong GetX(int index)
|
||||
{
|
||||
Contract.Requires(index <= 30, "invalid register");
|
||||
|
||||
return GetRegister(X_registers[index]);
|
||||
}
|
||||
|
||||
public void SetX(int index, ulong value)
|
||||
{
|
||||
Contract.Requires(index <= 30, "invalid register");
|
||||
|
||||
SetRegister(X_registers[index], value);
|
||||
}
|
||||
|
||||
public Vector128<float> GetQ(int index)
|
||||
{
|
||||
Contract.Requires(index <= 31, "invalid vector");
|
||||
|
||||
return GetVector(Q_registers[index]);
|
||||
}
|
||||
|
||||
public void SetQ(int index, Vector128<float> value)
|
||||
{
|
||||
Contract.Requires(index <= 31, "invalid vector");
|
||||
|
||||
SetVector(Q_registers[index], value);
|
||||
}
|
||||
|
||||
public byte[] MemoryRead(ulong address, ulong size)
|
||||
{
|
||||
byte[] value = new byte[size];
|
||||
Native.Interface.Checked(Native.Interface.uc_mem_read(uc, address, value, size));
|
||||
return value;
|
||||
}
|
||||
|
||||
public byte MemoryRead8 (ulong address) { return MemoryRead(address, 1)[0]; }
|
||||
public UInt16 MemoryRead16(ulong address) { return (UInt16)BitConverter.ToInt16(MemoryRead(address, 2), 0); }
|
||||
public UInt32 MemoryRead32(ulong address) { return (UInt32)BitConverter.ToInt32(MemoryRead(address, 4), 0); }
|
||||
public UInt64 MemoryRead64(ulong address) { return (UInt64)BitConverter.ToInt64(MemoryRead(address, 8), 0); }
|
||||
|
||||
public void MemoryWrite(ulong address, byte[] value)
|
||||
{
|
||||
Native.Interface.Checked(Native.Interface.uc_mem_write(uc, address, value, (ulong)value.Length));
|
||||
}
|
||||
|
||||
public void MemoryWrite8 (ulong address, byte value) { MemoryWrite(address, new byte[]{value}); }
|
||||
public void MemoryWrite16(ulong address, Int16 value) { MemoryWrite(address, BitConverter.GetBytes(value)); }
|
||||
public void MemoryWrite16(ulong address, UInt16 value) { MemoryWrite(address, BitConverter.GetBytes(value)); }
|
||||
public void MemoryWrite32(ulong address, Int32 value) { MemoryWrite(address, BitConverter.GetBytes(value)); }
|
||||
public void MemoryWrite32(ulong address, UInt32 value) { MemoryWrite(address, BitConverter.GetBytes(value)); }
|
||||
public void MemoryWrite64(ulong address, Int64 value) { MemoryWrite(address, BitConverter.GetBytes(value)); }
|
||||
public void MemoryWrite64(ulong address, UInt64 value) { MemoryWrite(address, BitConverter.GetBytes(value)); }
|
||||
|
||||
public void MemoryMap(ulong address, ulong size, MemoryPermission permissions)
|
||||
{
|
||||
Native.Interface.Checked(Native.Interface.uc_mem_map(uc, address, size, (uint)permissions));
|
||||
}
|
||||
|
||||
public void MemoryUnmap(ulong address, ulong size)
|
||||
{
|
||||
Native.Interface.Checked(Native.Interface.uc_mem_unmap(uc, address, size));
|
||||
}
|
||||
|
||||
public void MemoryProtect(ulong address, ulong size, MemoryPermission permissions)
|
||||
{
|
||||
Native.Interface.Checked(Native.Interface.uc_mem_protect(uc, address, size, (uint)permissions));
|
||||
}
|
||||
|
||||
public void DumpMemoryInformation()
|
||||
{
|
||||
Native.Interface.Checked(Native.Interface.uc_mem_regions(uc, out IntPtr regions_raw, out uint length));
|
||||
Native.Interface.MarshalArrayOf<Native.UnicornMemoryRegion>(regions_raw, (int)length, out var regions);
|
||||
foreach (var region in regions)
|
||||
{
|
||||
Console.WriteLine("region: begin {0:X16} end {1:X16} perms {2:X8}", region.begin, region.end, region.perms);
|
||||
}
|
||||
}
|
||||
|
||||
public static bool IsAvailable()
|
||||
{
|
||||
try
|
||||
{
|
||||
Native.Interface.uc_version(out uint major, out uint minor);
|
||||
return true;
|
||||
}
|
||||
catch (DllNotFoundException)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
30
Ryujinx.Tests.Unicorn/UnicornError.cs
Normal file
30
Ryujinx.Tests.Unicorn/UnicornError.cs
Normal file
|
@ -0,0 +1,30 @@
|
|||
using System;
|
||||
|
||||
namespace Ryujinx.Tests.Unicorn
|
||||
{
|
||||
public enum UnicornError
|
||||
{
|
||||
UC_ERR_OK = 0, // No error: everything was fine
|
||||
UC_ERR_NOMEM, // Out-Of-Memory error: uc_open(), uc_emulate()
|
||||
UC_ERR_ARCH, // Unsupported architecture: uc_open()
|
||||
UC_ERR_HANDLE, // Invalid handle
|
||||
UC_ERR_MODE, // Invalid/unsupported mode: uc_open()
|
||||
UC_ERR_VERSION, // Unsupported version (bindings)
|
||||
UC_ERR_READ_UNMAPPED, // Quit emulation due to READ on unmapped memory: uc_emu_start()
|
||||
UC_ERR_WRITE_UNMAPPED, // Quit emulation due to WRITE on unmapped memory: uc_emu_start()
|
||||
UC_ERR_FETCH_UNMAPPED, // Quit emulation due to FETCH on unmapped memory: uc_emu_start()
|
||||
UC_ERR_HOOK, // Invalid hook type: uc_hook_add()
|
||||
UC_ERR_INSN_INVALID, // Quit emulation due to invalid instruction: uc_emu_start()
|
||||
UC_ERR_MAP, // Invalid memory mapping: uc_mem_map()
|
||||
UC_ERR_WRITE_PROT, // Quit emulation due to UC_MEM_WRITE_PROT violation: uc_emu_start()
|
||||
UC_ERR_READ_PROT, // Quit emulation due to UC_MEM_READ_PROT violation: uc_emu_start()
|
||||
UC_ERR_FETCH_PROT, // Quit emulation due to UC_MEM_FETCH_PROT violation: uc_emu_start()
|
||||
UC_ERR_ARG, // Inavalid argument provided to uc_xxx function (See specific function API)
|
||||
UC_ERR_READ_UNALIGNED, // Unaligned read
|
||||
UC_ERR_WRITE_UNALIGNED, // Unaligned write
|
||||
UC_ERR_FETCH_UNALIGNED, // Unaligned fetch
|
||||
UC_ERR_HOOK_EXIST, // hook for this event already existed
|
||||
UC_ERR_RESOURCE, // Insufficient resource: uc_emu_start()
|
||||
UC_ERR_EXCEPTION // Unhandled CPU exception
|
||||
}
|
||||
}
|
23
Ryujinx.Tests.Unicorn/UnicornException.cs
Normal file
23
Ryujinx.Tests.Unicorn/UnicornException.cs
Normal file
|
@ -0,0 +1,23 @@
|
|||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Ryujinx.Tests.Unicorn
|
||||
{
|
||||
public class UnicornException : Exception
|
||||
{
|
||||
public readonly UnicornError Error;
|
||||
|
||||
internal UnicornException(UnicornError error)
|
||||
{
|
||||
Error = error;
|
||||
}
|
||||
|
||||
public override string Message
|
||||
{
|
||||
get
|
||||
{
|
||||
return Marshal.PtrToStringAnsi(Native.Interface.uc_strerror(Error));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
3
Ryujinx.Tests.Unicorn/libs/README.md
Normal file
3
Ryujinx.Tests.Unicorn/libs/README.md
Normal file
|
@ -0,0 +1,3 @@
|
|||
The pre-compiled dynamic libraries in this directory are licenced under the GPLv2.
|
||||
|
||||
The source code for windows/unicorn.dll is available at: https://github.com/MerryMage/UnicornDotNet/tree/299451c02d9c810d2feca51f5e9cb6d8b2f38960
|
BIN
Ryujinx.Tests.Unicorn/libs/windows/unicorn.dll
Normal file
BIN
Ryujinx.Tests.Unicorn/libs/windows/unicorn.dll
Normal file
Binary file not shown.
|
@ -4,6 +4,8 @@ using ChocolArm64.State;
|
|||
|
||||
using NUnit.Framework;
|
||||
|
||||
using Ryujinx.Tests.Unicorn;
|
||||
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Runtime.Intrinsics;
|
||||
|
@ -25,10 +27,22 @@ namespace Ryujinx.Tests.Cpu
|
|||
private AMemory Memory;
|
||||
private AThread Thread;
|
||||
|
||||
private static bool UnicornAvailable;
|
||||
private UnicornAArch64 UnicornEmu;
|
||||
|
||||
static CpuTest()
|
||||
{
|
||||
UnicornAvailable = UnicornAArch64.IsAvailable();
|
||||
if (!UnicornAvailable)
|
||||
{
|
||||
Console.WriteLine("WARNING: Could not find unicorn");
|
||||
}
|
||||
}
|
||||
|
||||
[SetUp]
|
||||
public void Setup()
|
||||
{
|
||||
Position = 0x0;
|
||||
Position = 0x1000;
|
||||
Size = 0x1000;
|
||||
|
||||
EntryPoint = Position;
|
||||
|
@ -38,6 +52,13 @@ namespace Ryujinx.Tests.Cpu
|
|||
Memory = new AMemory(RamPointer);
|
||||
Memory.Map(Position, 0, Size);
|
||||
Thread = new AThread(Translator, Memory, EntryPoint);
|
||||
|
||||
if (UnicornAvailable)
|
||||
{
|
||||
UnicornEmu = new UnicornAArch64();
|
||||
UnicornEmu.MemoryMap((ulong)Position, (ulong)Size, MemoryPermission.READ | MemoryPermission.EXEC);
|
||||
UnicornEmu.PC = (ulong)EntryPoint;
|
||||
}
|
||||
}
|
||||
|
||||
[TearDown]
|
||||
|
@ -46,6 +67,7 @@ namespace Ryujinx.Tests.Cpu
|
|||
Marshal.FreeHGlobal(RamPointer);
|
||||
Memory = null;
|
||||
Thread = null;
|
||||
UnicornEmu = null;
|
||||
}
|
||||
|
||||
protected void Reset()
|
||||
|
@ -57,6 +79,10 @@ namespace Ryujinx.Tests.Cpu
|
|||
protected void Opcode(uint Opcode)
|
||||
{
|
||||
Thread.Memory.WriteUInt32(Position, Opcode);
|
||||
if (UnicornAvailable)
|
||||
{
|
||||
UnicornEmu.MemoryWrite32((ulong)Position, Opcode);
|
||||
}
|
||||
Position += 4;
|
||||
}
|
||||
|
||||
|
@ -81,6 +107,24 @@ namespace Ryujinx.Tests.Cpu
|
|||
Thread.ThreadState.Negative = Negative;
|
||||
Thread.ThreadState.Fpcr = Fpcr;
|
||||
Thread.ThreadState.Fpsr = Fpsr;
|
||||
|
||||
if (UnicornAvailable)
|
||||
{
|
||||
UnicornEmu.X[0] = X0;
|
||||
UnicornEmu.X[1] = X1;
|
||||
UnicornEmu.X[2] = X2;
|
||||
UnicornEmu.X[3] = X3;
|
||||
UnicornEmu.SP = X31;
|
||||
UnicornEmu.Q[0] = V0;
|
||||
UnicornEmu.Q[1] = V1;
|
||||
UnicornEmu.Q[2] = V2;
|
||||
UnicornEmu.OverflowFlag = Overflow;
|
||||
UnicornEmu.CarryFlag = Carry;
|
||||
UnicornEmu.ZeroFlag = Zero;
|
||||
UnicornEmu.NegativeFlag = Negative;
|
||||
UnicornEmu.Fpcr = Fpcr;
|
||||
UnicornEmu.Fpsr = Fpsr;
|
||||
}
|
||||
}
|
||||
|
||||
protected void ExecuteOpcodes()
|
||||
|
@ -93,6 +137,11 @@ namespace Ryujinx.Tests.Cpu
|
|||
Thread.Execute();
|
||||
Wait.WaitOne();
|
||||
}
|
||||
|
||||
if (UnicornAvailable)
|
||||
{
|
||||
UnicornEmu.RunForCount((ulong)(Position - EntryPoint - 8) / 4);
|
||||
}
|
||||
}
|
||||
|
||||
protected AThreadState GetThreadState()
|
||||
|
@ -117,24 +166,124 @@ namespace Ryujinx.Tests.Cpu
|
|||
return GetThreadState();
|
||||
}
|
||||
|
||||
protected void CompareAgainstUnicorn()
|
||||
{
|
||||
if (!UnicornAvailable)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Assert.That(Thread.ThreadState.X0, Is.EqualTo(UnicornEmu.X[0]));
|
||||
Assert.That(Thread.ThreadState.X1, Is.EqualTo(UnicornEmu.X[1]));
|
||||
Assert.That(Thread.ThreadState.X2, Is.EqualTo(UnicornEmu.X[2]));
|
||||
Assert.That(Thread.ThreadState.X3, Is.EqualTo(UnicornEmu.X[3]));
|
||||
Assert.That(Thread.ThreadState.X4, Is.EqualTo(UnicornEmu.X[4]));
|
||||
Assert.That(Thread.ThreadState.X5, Is.EqualTo(UnicornEmu.X[5]));
|
||||
Assert.That(Thread.ThreadState.X6, Is.EqualTo(UnicornEmu.X[6]));
|
||||
Assert.That(Thread.ThreadState.X7, Is.EqualTo(UnicornEmu.X[7]));
|
||||
Assert.That(Thread.ThreadState.X8, Is.EqualTo(UnicornEmu.X[8]));
|
||||
Assert.That(Thread.ThreadState.X9, Is.EqualTo(UnicornEmu.X[9]));
|
||||
Assert.That(Thread.ThreadState.X10, Is.EqualTo(UnicornEmu.X[10]));
|
||||
Assert.That(Thread.ThreadState.X11, Is.EqualTo(UnicornEmu.X[11]));
|
||||
Assert.That(Thread.ThreadState.X12, Is.EqualTo(UnicornEmu.X[12]));
|
||||
Assert.That(Thread.ThreadState.X13, Is.EqualTo(UnicornEmu.X[13]));
|
||||
Assert.That(Thread.ThreadState.X14, Is.EqualTo(UnicornEmu.X[14]));
|
||||
Assert.That(Thread.ThreadState.X15, Is.EqualTo(UnicornEmu.X[15]));
|
||||
Assert.That(Thread.ThreadState.X16, Is.EqualTo(UnicornEmu.X[16]));
|
||||
Assert.That(Thread.ThreadState.X17, Is.EqualTo(UnicornEmu.X[17]));
|
||||
Assert.That(Thread.ThreadState.X18, Is.EqualTo(UnicornEmu.X[18]));
|
||||
Assert.That(Thread.ThreadState.X19, Is.EqualTo(UnicornEmu.X[19]));
|
||||
Assert.That(Thread.ThreadState.X20, Is.EqualTo(UnicornEmu.X[20]));
|
||||
Assert.That(Thread.ThreadState.X21, Is.EqualTo(UnicornEmu.X[21]));
|
||||
Assert.That(Thread.ThreadState.X22, Is.EqualTo(UnicornEmu.X[22]));
|
||||
Assert.That(Thread.ThreadState.X23, Is.EqualTo(UnicornEmu.X[23]));
|
||||
Assert.That(Thread.ThreadState.X24, Is.EqualTo(UnicornEmu.X[24]));
|
||||
Assert.That(Thread.ThreadState.X25, Is.EqualTo(UnicornEmu.X[25]));
|
||||
Assert.That(Thread.ThreadState.X26, Is.EqualTo(UnicornEmu.X[26]));
|
||||
Assert.That(Thread.ThreadState.X27, Is.EqualTo(UnicornEmu.X[27]));
|
||||
Assert.That(Thread.ThreadState.X28, Is.EqualTo(UnicornEmu.X[28]));
|
||||
Assert.That(Thread.ThreadState.X29, Is.EqualTo(UnicornEmu.X[29]));
|
||||
Assert.That(Thread.ThreadState.X30, Is.EqualTo(UnicornEmu.X[30]));
|
||||
Assert.That(Thread.ThreadState.X31, Is.EqualTo(UnicornEmu.SP));
|
||||
Assert.That(Thread.ThreadState.V0, Is.EqualTo(UnicornEmu.Q[0]));
|
||||
Assert.That(Thread.ThreadState.V1, Is.EqualTo(UnicornEmu.Q[1]));
|
||||
Assert.That(Thread.ThreadState.V2, Is.EqualTo(UnicornEmu.Q[2]));
|
||||
Assert.That(Thread.ThreadState.V3, Is.EqualTo(UnicornEmu.Q[3]));
|
||||
Assert.That(Thread.ThreadState.V4, Is.EqualTo(UnicornEmu.Q[4]));
|
||||
Assert.That(Thread.ThreadState.V5, Is.EqualTo(UnicornEmu.Q[5]));
|
||||
Assert.That(Thread.ThreadState.V6, Is.EqualTo(UnicornEmu.Q[6]));
|
||||
Assert.That(Thread.ThreadState.V7, Is.EqualTo(UnicornEmu.Q[7]));
|
||||
Assert.That(Thread.ThreadState.V8, Is.EqualTo(UnicornEmu.Q[8]));
|
||||
Assert.That(Thread.ThreadState.V9, Is.EqualTo(UnicornEmu.Q[9]));
|
||||
Assert.That(Thread.ThreadState.V10, Is.EqualTo(UnicornEmu.Q[10]));
|
||||
Assert.That(Thread.ThreadState.V11, Is.EqualTo(UnicornEmu.Q[11]));
|
||||
Assert.That(Thread.ThreadState.V12, Is.EqualTo(UnicornEmu.Q[12]));
|
||||
Assert.That(Thread.ThreadState.V13, Is.EqualTo(UnicornEmu.Q[13]));
|
||||
Assert.That(Thread.ThreadState.V14, Is.EqualTo(UnicornEmu.Q[14]));
|
||||
Assert.That(Thread.ThreadState.V15, Is.EqualTo(UnicornEmu.Q[15]));
|
||||
Assert.That(Thread.ThreadState.V16, Is.EqualTo(UnicornEmu.Q[16]));
|
||||
Assert.That(Thread.ThreadState.V17, Is.EqualTo(UnicornEmu.Q[17]));
|
||||
Assert.That(Thread.ThreadState.V18, Is.EqualTo(UnicornEmu.Q[18]));
|
||||
Assert.That(Thread.ThreadState.V19, Is.EqualTo(UnicornEmu.Q[19]));
|
||||
Assert.That(Thread.ThreadState.V20, Is.EqualTo(UnicornEmu.Q[20]));
|
||||
Assert.That(Thread.ThreadState.V21, Is.EqualTo(UnicornEmu.Q[21]));
|
||||
Assert.That(Thread.ThreadState.V22, Is.EqualTo(UnicornEmu.Q[22]));
|
||||
Assert.That(Thread.ThreadState.V23, Is.EqualTo(UnicornEmu.Q[23]));
|
||||
Assert.That(Thread.ThreadState.V24, Is.EqualTo(UnicornEmu.Q[24]));
|
||||
Assert.That(Thread.ThreadState.V25, Is.EqualTo(UnicornEmu.Q[25]));
|
||||
Assert.That(Thread.ThreadState.V26, Is.EqualTo(UnicornEmu.Q[26]));
|
||||
Assert.That(Thread.ThreadState.V27, Is.EqualTo(UnicornEmu.Q[27]));
|
||||
Assert.That(Thread.ThreadState.V28, Is.EqualTo(UnicornEmu.Q[28]));
|
||||
Assert.That(Thread.ThreadState.V29, Is.EqualTo(UnicornEmu.Q[29]));
|
||||
Assert.That(Thread.ThreadState.V30, Is.EqualTo(UnicornEmu.Q[30]));
|
||||
Assert.That(Thread.ThreadState.V31, Is.EqualTo(UnicornEmu.Q[31]));
|
||||
Assert.That(Thread.ThreadState.V31, Is.EqualTo(UnicornEmu.Q[31]));
|
||||
Assert.That(Thread.ThreadState.Fpcr, Is.EqualTo(UnicornEmu.Fpcr));
|
||||
Assert.That(Thread.ThreadState.Fpsr & 0x08000000, Is.EqualTo(UnicornEmu.Fpsr & 0x08000000));
|
||||
Assert.That(Thread.ThreadState.Overflow, Is.EqualTo(UnicornEmu.OverflowFlag));
|
||||
Assert.That(Thread.ThreadState.Carry, Is.EqualTo(UnicornEmu.CarryFlag));
|
||||
Assert.That(Thread.ThreadState.Zero, Is.EqualTo(UnicornEmu.ZeroFlag));
|
||||
Assert.That(Thread.ThreadState.Negative, Is.EqualTo(UnicornEmu.NegativeFlag));
|
||||
}
|
||||
|
||||
protected static Vector128<float> MakeVectorE0(double E0)
|
||||
{
|
||||
if (!Sse2.IsSupported)
|
||||
{
|
||||
throw new PlatformNotSupportedException();
|
||||
}
|
||||
|
||||
return Sse.StaticCast<long, float>(Sse2.SetVector128(0, BitConverter.DoubleToInt64Bits(E0)));
|
||||
}
|
||||
|
||||
protected static Vector128<float> MakeVectorE0E1(double E0, double E1)
|
||||
{
|
||||
return Sse.StaticCast<long, float>(Sse2.SetVector128(BitConverter.DoubleToInt64Bits(E1),
|
||||
BitConverter.DoubleToInt64Bits(E0)));
|
||||
if (!Sse2.IsSupported)
|
||||
{
|
||||
throw new PlatformNotSupportedException();
|
||||
}
|
||||
|
||||
return Sse.StaticCast<long, float>(
|
||||
Sse2.SetVector128(BitConverter.DoubleToInt64Bits(E1), BitConverter.DoubleToInt64Bits(E0)));
|
||||
}
|
||||
|
||||
protected static Vector128<float> MakeVectorE1(double E1)
|
||||
{
|
||||
if (!Sse2.IsSupported)
|
||||
{
|
||||
throw new PlatformNotSupportedException();
|
||||
}
|
||||
|
||||
return Sse.StaticCast<long, float>(Sse2.SetVector128(BitConverter.DoubleToInt64Bits(E1), 0));
|
||||
}
|
||||
|
||||
protected static double VectorExtractDouble(Vector128<float> Vector, byte Index)
|
||||
{
|
||||
if (!Sse41.IsSupported)
|
||||
{
|
||||
throw new PlatformNotSupportedException();
|
||||
}
|
||||
|
||||
long Value = Sse41.Extract(Sse.StaticCast<float, long>(Vector), Index);
|
||||
|
||||
return BitConverter.Int64BitsToDouble(Value);
|
||||
|
@ -142,26 +291,51 @@ namespace Ryujinx.Tests.Cpu
|
|||
|
||||
protected static Vector128<float> MakeVectorE0(ulong E0)
|
||||
{
|
||||
if (!Sse2.IsSupported)
|
||||
{
|
||||
throw new PlatformNotSupportedException();
|
||||
}
|
||||
|
||||
return Sse.StaticCast<ulong, float>(Sse2.SetVector128(0, E0));
|
||||
}
|
||||
|
||||
protected static Vector128<float> MakeVectorE0E1(ulong E0, ulong E1)
|
||||
{
|
||||
if (!Sse2.IsSupported)
|
||||
{
|
||||
throw new PlatformNotSupportedException();
|
||||
}
|
||||
|
||||
return Sse.StaticCast<ulong, float>(Sse2.SetVector128(E1, E0));
|
||||
}
|
||||
|
||||
protected static Vector128<float> MakeVectorE1(ulong E1)
|
||||
{
|
||||
if (!Sse2.IsSupported)
|
||||
{
|
||||
throw new PlatformNotSupportedException();
|
||||
}
|
||||
|
||||
return Sse.StaticCast<ulong, float>(Sse2.SetVector128(E1, 0));
|
||||
}
|
||||
|
||||
protected static ulong GetVectorE0(Vector128<float> Vector)
|
||||
{
|
||||
if (!Sse41.IsSupported)
|
||||
{
|
||||
throw new PlatformNotSupportedException();
|
||||
}
|
||||
|
||||
return Sse41.Extract(Sse.StaticCast<float, ulong>(Vector), (byte)0);
|
||||
}
|
||||
|
||||
protected static ulong GetVectorE1(Vector128<float> Vector)
|
||||
{
|
||||
if (!Sse41.IsSupported)
|
||||
{
|
||||
throw new PlatformNotSupportedException();
|
||||
}
|
||||
|
||||
return Sse41.Extract(Sse.StaticCast<float, ulong>(Vector), (byte)1);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -45,6 +45,7 @@ namespace Ryujinx.Tests.Cpu
|
|||
{
|
||||
Assert.That((ulong)ThreadState.X31, Is.EqualTo(_X31));
|
||||
}
|
||||
CompareAgainstUnicorn();
|
||||
}
|
||||
|
||||
[Test, Description("CLS <Wd>, <Wn>")]
|
||||
|
@ -101,6 +102,7 @@ namespace Ryujinx.Tests.Cpu
|
|||
{
|
||||
Assert.That((ulong)ThreadState.X31, Is.EqualTo(_X31));
|
||||
}
|
||||
CompareAgainstUnicorn();
|
||||
}
|
||||
|
||||
[Test, Description("CLZ <Wd>, <Wn>")]
|
||||
|
@ -129,6 +131,7 @@ namespace Ryujinx.Tests.Cpu
|
|||
{
|
||||
Assert.That((uint)ThreadState.X31, Is.EqualTo(_W31));
|
||||
}
|
||||
CompareAgainstUnicorn();
|
||||
}
|
||||
|
||||
[Test, Description("RBIT <Xd>, <Xn>")]
|
||||
|
@ -157,6 +160,7 @@ namespace Ryujinx.Tests.Cpu
|
|||
{
|
||||
Assert.That((ulong)ThreadState.X31, Is.EqualTo(_X31));
|
||||
}
|
||||
CompareAgainstUnicorn();
|
||||
}
|
||||
|
||||
[Test, Description("RBIT <Wd>, <Wn>")]
|
||||
|
@ -185,6 +189,7 @@ namespace Ryujinx.Tests.Cpu
|
|||
{
|
||||
Assert.That((uint)ThreadState.X31, Is.EqualTo(_W31));
|
||||
}
|
||||
CompareAgainstUnicorn();
|
||||
}
|
||||
|
||||
[Test, Description("REV16 <Xd>, <Xn>")]
|
||||
|
@ -213,6 +218,7 @@ namespace Ryujinx.Tests.Cpu
|
|||
{
|
||||
Assert.That((ulong)ThreadState.X31, Is.EqualTo(_X31));
|
||||
}
|
||||
CompareAgainstUnicorn();
|
||||
}
|
||||
|
||||
[Test, Description("REV16 <Wd>, <Wn>")]
|
||||
|
@ -241,6 +247,7 @@ namespace Ryujinx.Tests.Cpu
|
|||
{
|
||||
Assert.That((uint)ThreadState.X31, Is.EqualTo(_W31));
|
||||
}
|
||||
CompareAgainstUnicorn();
|
||||
}
|
||||
|
||||
[Test, Description("REV32 <Xd>, <Xn>")]
|
||||
|
@ -269,6 +276,7 @@ namespace Ryujinx.Tests.Cpu
|
|||
{
|
||||
Assert.That((ulong)ThreadState.X31, Is.EqualTo(_X31));
|
||||
}
|
||||
CompareAgainstUnicorn();
|
||||
}
|
||||
|
||||
[Test, Description("REV <Wd>, <Wn>")]
|
||||
|
@ -297,6 +305,7 @@ namespace Ryujinx.Tests.Cpu
|
|||
{
|
||||
Assert.That((uint)ThreadState.X31, Is.EqualTo(_W31));
|
||||
}
|
||||
CompareAgainstUnicorn();
|
||||
}
|
||||
|
||||
[Test, Description("REV64 <Xd>, <Xn>")]
|
||||
|
@ -325,6 +334,7 @@ namespace Ryujinx.Tests.Cpu
|
|||
{
|
||||
Assert.That((ulong)ThreadState.X31, Is.EqualTo(_X31));
|
||||
}
|
||||
CompareAgainstUnicorn();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
|
|
@ -61,6 +61,7 @@ namespace Ryujinx.Tests.Cpu
|
|||
|
||||
Assert.That((ulong)ThreadState.X31, Is.EqualTo(SP));
|
||||
}
|
||||
CompareAgainstUnicorn();
|
||||
}
|
||||
|
||||
[Test, Description("ADD <Wd|WSP>, <Wn|WSP>, #<imm>{, <shift>}")]
|
||||
|
@ -105,6 +106,7 @@ namespace Ryujinx.Tests.Cpu
|
|||
|
||||
Assert.That((uint)ThreadState.X31, Is.EqualTo(WSP));
|
||||
}
|
||||
CompareAgainstUnicorn();
|
||||
}
|
||||
|
||||
[Test, Description("ADDS <Xd>, <Xn|SP>, #<imm>{, <shift>}")]
|
||||
|
@ -156,6 +158,7 @@ namespace Ryujinx.Tests.Cpu
|
|||
Assert.That(ThreadState.Carry, Is.EqualTo(Shared.PSTATE.C));
|
||||
Assert.That(ThreadState.Overflow, Is.EqualTo(Shared.PSTATE.V));
|
||||
});
|
||||
CompareAgainstUnicorn();
|
||||
}
|
||||
|
||||
[Test, Description("ADDS <Wd>, <Wn|WSP>, #<imm>{, <shift>}")]
|
||||
|
@ -207,6 +210,7 @@ namespace Ryujinx.Tests.Cpu
|
|||
Assert.That(ThreadState.Carry, Is.EqualTo(Shared.PSTATE.C));
|
||||
Assert.That(ThreadState.Overflow, Is.EqualTo(Shared.PSTATE.V));
|
||||
});
|
||||
CompareAgainstUnicorn();
|
||||
}
|
||||
|
||||
[Test, Description("AND <Xd|SP>, <Xn>, #<imm>")]
|
||||
|
@ -240,6 +244,7 @@ namespace Ryujinx.Tests.Cpu
|
|||
|
||||
Assert.That((ulong)ThreadState.X31, Is.EqualTo(SP));
|
||||
}
|
||||
CompareAgainstUnicorn();
|
||||
}
|
||||
|
||||
[Test, Description("AND <Xd|SP>, <Xn>, #<imm>")]
|
||||
|
@ -273,6 +278,7 @@ namespace Ryujinx.Tests.Cpu
|
|||
|
||||
Assert.That((ulong)ThreadState.X31, Is.EqualTo(SP));
|
||||
}
|
||||
CompareAgainstUnicorn();
|
||||
}
|
||||
|
||||
[Test, Description("AND <Wd|WSP>, <Wn>, #<imm>")]
|
||||
|
@ -306,6 +312,7 @@ namespace Ryujinx.Tests.Cpu
|
|||
|
||||
Assert.That((uint)ThreadState.X31, Is.EqualTo(WSP));
|
||||
}
|
||||
CompareAgainstUnicorn();
|
||||
}
|
||||
|
||||
[Test, Description("ANDS <Xd>, <Xn>, #<imm>")]
|
||||
|
@ -343,6 +350,7 @@ namespace Ryujinx.Tests.Cpu
|
|||
Assert.That(ThreadState.Carry, Is.EqualTo(Shared.PSTATE.C));
|
||||
Assert.That(ThreadState.Overflow, Is.EqualTo(Shared.PSTATE.V));
|
||||
});
|
||||
CompareAgainstUnicorn();
|
||||
}
|
||||
|
||||
[Test, Description("ANDS <Xd>, <Xn>, #<imm>")]
|
||||
|
@ -380,6 +388,7 @@ namespace Ryujinx.Tests.Cpu
|
|||
Assert.That(ThreadState.Carry, Is.EqualTo(Shared.PSTATE.C));
|
||||
Assert.That(ThreadState.Overflow, Is.EqualTo(Shared.PSTATE.V));
|
||||
});
|
||||
CompareAgainstUnicorn();
|
||||
}
|
||||
|
||||
[Test, Description("ANDS <Wd>, <Wn>, #<imm>")]
|
||||
|
@ -417,6 +426,7 @@ namespace Ryujinx.Tests.Cpu
|
|||
Assert.That(ThreadState.Carry, Is.EqualTo(Shared.PSTATE.C));
|
||||
Assert.That(ThreadState.Overflow, Is.EqualTo(Shared.PSTATE.V));
|
||||
});
|
||||
CompareAgainstUnicorn();
|
||||
}
|
||||
|
||||
[Test, Description("EOR <Xd|SP>, <Xn>, #<imm>")]
|
||||
|
@ -450,6 +460,7 @@ namespace Ryujinx.Tests.Cpu
|
|||
|
||||
Assert.That((ulong)ThreadState.X31, Is.EqualTo(SP));
|
||||
}
|
||||
CompareAgainstUnicorn();
|
||||
}
|
||||
|
||||
[Test, Description("EOR <Xd|SP>, <Xn>, #<imm>")]
|
||||
|
@ -483,6 +494,7 @@ namespace Ryujinx.Tests.Cpu
|
|||
|
||||
Assert.That((ulong)ThreadState.X31, Is.EqualTo(SP));
|
||||
}
|
||||
CompareAgainstUnicorn();
|
||||
}
|
||||
|
||||
[Test, Description("EOR <Wd>, <Wn>, #<imm>")]
|
||||
|
@ -516,6 +528,7 @@ namespace Ryujinx.Tests.Cpu
|
|||
|
||||
Assert.That((uint)ThreadState.X31, Is.EqualTo(WSP));
|
||||
}
|
||||
CompareAgainstUnicorn();
|
||||
}
|
||||
|
||||
[Test, Description("ORR <Xd|SP>, <Xn>, #<imm>")]
|
||||
|
@ -549,6 +562,7 @@ namespace Ryujinx.Tests.Cpu
|
|||
|
||||
Assert.That((ulong)ThreadState.X31, Is.EqualTo(SP));
|
||||
}
|
||||
CompareAgainstUnicorn();
|
||||
}
|
||||
|
||||
[Test, Description("ORR <Xd|SP>, <Xn>, #<imm>")]
|
||||
|
@ -582,6 +596,7 @@ namespace Ryujinx.Tests.Cpu
|
|||
|
||||
Assert.That((ulong)ThreadState.X31, Is.EqualTo(SP));
|
||||
}
|
||||
CompareAgainstUnicorn();
|
||||
}
|
||||
|
||||
[Test, Description("ORR <Wd|WSP>, <Wn>, #<imm>")]
|
||||
|
@ -615,6 +630,7 @@ namespace Ryujinx.Tests.Cpu
|
|||
|
||||
Assert.That((uint)ThreadState.X31, Is.EqualTo(WSP));
|
||||
}
|
||||
CompareAgainstUnicorn();
|
||||
}
|
||||
|
||||
[Test, Description("SUB <Xd|SP>, <Xn|SP>, #<imm>{, <shift>}")]
|
||||
|
@ -659,6 +675,7 @@ namespace Ryujinx.Tests.Cpu
|
|||
|
||||
Assert.That((ulong)ThreadState.X31, Is.EqualTo(SP));
|
||||
}
|
||||
CompareAgainstUnicorn();
|
||||
}
|
||||
|
||||
[Test, Description("SUB <Wd|WSP>, <Wn|WSP>, #<imm>{, <shift>}")]
|
||||
|
@ -703,6 +720,7 @@ namespace Ryujinx.Tests.Cpu
|
|||
|
||||
Assert.That((uint)ThreadState.X31, Is.EqualTo(WSP));
|
||||
}
|
||||
CompareAgainstUnicorn();
|
||||
}
|
||||
|
||||
[Test, Description("SUBS <Xd>, <Xn|SP>, #<imm>{, <shift>}")]
|
||||
|
@ -754,6 +772,7 @@ namespace Ryujinx.Tests.Cpu
|
|||
Assert.That(ThreadState.Carry, Is.EqualTo(Shared.PSTATE.C));
|
||||
Assert.That(ThreadState.Overflow, Is.EqualTo(Shared.PSTATE.V));
|
||||
});
|
||||
CompareAgainstUnicorn();
|
||||
}
|
||||
|
||||
[Test, Description("SUBS <Wd>, <Wn|WSP>, #<imm>{, <shift>}")]
|
||||
|
@ -805,6 +824,7 @@ namespace Ryujinx.Tests.Cpu
|
|||
Assert.That(ThreadState.Carry, Is.EqualTo(Shared.PSTATE.C));
|
||||
Assert.That(ThreadState.Overflow, Is.EqualTo(Shared.PSTATE.V));
|
||||
});
|
||||
CompareAgainstUnicorn();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
|
|
@ -51,6 +51,7 @@ namespace Ryujinx.Tests.Cpu
|
|||
{
|
||||
Assert.That((ulong)ThreadState.X31, Is.EqualTo(_X31));
|
||||
}
|
||||
CompareAgainstUnicorn();
|
||||
}
|
||||
|
||||
[Test, Description("ADC <Wd>, <Wn>, <Wm>")]
|
||||
|
@ -85,6 +86,7 @@ namespace Ryujinx.Tests.Cpu
|
|||
{
|
||||
Assert.That((uint)ThreadState.X31, Is.EqualTo(_W31));
|
||||
}
|
||||
CompareAgainstUnicorn();
|
||||
}
|
||||
|
||||
[Test, Description("ADCS <Xd>, <Xn>, <Xm>")]
|
||||
|
@ -125,6 +127,7 @@ namespace Ryujinx.Tests.Cpu
|
|||
Assert.That(ThreadState.Carry, Is.EqualTo(Shared.PSTATE.C));
|
||||
Assert.That(ThreadState.Overflow, Is.EqualTo(Shared.PSTATE.V));
|
||||
});
|
||||
CompareAgainstUnicorn();
|
||||
}
|
||||
|
||||
[Test, Description("ADCS <Wd>, <Wn>, <Wm>")]
|
||||
|
@ -165,6 +168,7 @@ namespace Ryujinx.Tests.Cpu
|
|||
Assert.That(ThreadState.Carry, Is.EqualTo(Shared.PSTATE.C));
|
||||
Assert.That(ThreadState.Overflow, Is.EqualTo(Shared.PSTATE.V));
|
||||
});
|
||||
CompareAgainstUnicorn();
|
||||
}
|
||||
|
||||
[Test, Description("ADD <Xd>, <Xn>, <Xm>{, <shift> #<amount>}")]
|
||||
|
@ -200,6 +204,7 @@ namespace Ryujinx.Tests.Cpu
|
|||
{
|
||||
Assert.That((ulong)ThreadState.X31, Is.EqualTo(_X31));
|
||||
}
|
||||
CompareAgainstUnicorn();
|
||||
}
|
||||
|
||||
[Test, Description("ADD <Wd>, <Wn>, <Wm>{, <shift> #<amount>}")]
|
||||
|
@ -234,6 +239,7 @@ namespace Ryujinx.Tests.Cpu
|
|||
else
|
||||
{
|
||||
Assert.That((uint)ThreadState.X31, Is.EqualTo(_W31));
|
||||
CompareAgainstUnicorn();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -276,6 +282,7 @@ namespace Ryujinx.Tests.Cpu
|
|||
Assert.That(ThreadState.Carry, Is.EqualTo(Shared.PSTATE.C));
|
||||
Assert.That(ThreadState.Overflow, Is.EqualTo(Shared.PSTATE.V));
|
||||
});
|
||||
CompareAgainstUnicorn();
|
||||
}
|
||||
|
||||
[Test, Description("ADDS <Wd>, <Wn>, <Wm>{, <shift> #<amount>}")]
|
||||
|
@ -317,6 +324,7 @@ namespace Ryujinx.Tests.Cpu
|
|||
Assert.That(ThreadState.Carry, Is.EqualTo(Shared.PSTATE.C));
|
||||
Assert.That(ThreadState.Overflow, Is.EqualTo(Shared.PSTATE.V));
|
||||
});
|
||||
CompareAgainstUnicorn();
|
||||
}
|
||||
|
||||
[Test, Description("AND <Xd>, <Xn>, <Xm>{, <shift> #<amount>}")]
|
||||
|
@ -352,6 +360,7 @@ namespace Ryujinx.Tests.Cpu
|
|||
{
|
||||
Assert.That((ulong)ThreadState.X31, Is.EqualTo(_X31));
|
||||
}
|
||||
CompareAgainstUnicorn();
|
||||
}
|
||||
|
||||
[Test, Description("AND <Wd>, <Wn>, <Wm>{, <shift> #<amount>}")]
|
||||
|
@ -387,6 +396,7 @@ namespace Ryujinx.Tests.Cpu
|
|||
{
|
||||
Assert.That((uint)ThreadState.X31, Is.EqualTo(_W31));
|
||||
}
|
||||
CompareAgainstUnicorn();
|
||||
}
|
||||
|
||||
[Test, Description("ANDS <Xd>, <Xn>, <Xm>{, <shift> #<amount>}")]
|
||||
|
@ -428,6 +438,7 @@ namespace Ryujinx.Tests.Cpu
|
|||
Assert.That(ThreadState.Carry, Is.EqualTo(Shared.PSTATE.C));
|
||||
Assert.That(ThreadState.Overflow, Is.EqualTo(Shared.PSTATE.V));
|
||||
});
|
||||
CompareAgainstUnicorn();
|
||||
}
|
||||
|
||||
[Test, Description("ANDS <Wd>, <Wn>, <Wm>{, <shift> #<amount>}")]
|
||||
|
@ -469,6 +480,7 @@ namespace Ryujinx.Tests.Cpu
|
|||
Assert.That(ThreadState.Carry, Is.EqualTo(Shared.PSTATE.C));
|
||||
Assert.That(ThreadState.Overflow, Is.EqualTo(Shared.PSTATE.V));
|
||||
});
|
||||
CompareAgainstUnicorn();
|
||||
}
|
||||
|
||||
[Test, Description("ASRV <Xd>, <Xn>, <Xm>")]
|
||||
|
@ -501,6 +513,7 @@ namespace Ryujinx.Tests.Cpu
|
|||
{
|
||||
Assert.That((ulong)ThreadState.X31, Is.EqualTo(_X31));
|
||||
}
|
||||
CompareAgainstUnicorn();
|
||||
}
|
||||
|
||||
[Test, Description("ASRV <Wd>, <Wn>, <Wm>")]
|
||||
|
@ -533,6 +546,7 @@ namespace Ryujinx.Tests.Cpu
|
|||
{
|
||||
Assert.That((uint)ThreadState.X31, Is.EqualTo(_W31));
|
||||
}
|
||||
CompareAgainstUnicorn();
|
||||
}
|
||||
|
||||
[Test, Description("BIC <Xd>, <Xn>, <Xm>{, <shift> #<amount>}")]
|
||||
|
@ -568,6 +582,7 @@ namespace Ryujinx.Tests.Cpu
|
|||
{
|
||||
Assert.That((ulong)ThreadState.X31, Is.EqualTo(_X31));
|
||||
}
|
||||
CompareAgainstUnicorn();
|
||||
}
|
||||
|
||||
[Test, Description("BIC <Wd>, <Wn>, <Wm>{, <shift> #<amount>}")]
|
||||
|
@ -603,6 +618,7 @@ namespace Ryujinx.Tests.Cpu
|
|||
{
|
||||
Assert.That((uint)ThreadState.X31, Is.EqualTo(_W31));
|
||||
}
|
||||
CompareAgainstUnicorn();
|
||||
}
|
||||
|
||||
[Test, Description("BICS <Xd>, <Xn>, <Xm>{, <shift> #<amount>}")]
|
||||
|
@ -644,6 +660,7 @@ namespace Ryujinx.Tests.Cpu
|
|||
Assert.That(ThreadState.Carry, Is.EqualTo(Shared.PSTATE.C));
|
||||
Assert.That(ThreadState.Overflow, Is.EqualTo(Shared.PSTATE.V));
|
||||
});
|
||||
CompareAgainstUnicorn();
|
||||
}
|
||||
|
||||
[Test, Description("BICS <Wd>, <Wn>, <Wm>{, <shift> #<amount>}")]
|
||||
|
@ -685,6 +702,7 @@ namespace Ryujinx.Tests.Cpu
|
|||
Assert.That(ThreadState.Carry, Is.EqualTo(Shared.PSTATE.C));
|
||||
Assert.That(ThreadState.Overflow, Is.EqualTo(Shared.PSTATE.V));
|
||||
});
|
||||
CompareAgainstUnicorn();
|
||||
}
|
||||
|
||||
[Test, Description("CRC32X <Wd>, <Wn>, <Xm>")]
|
||||
|
@ -718,6 +736,7 @@ namespace Ryujinx.Tests.Cpu
|
|||
{
|
||||
Assert.That((uint)ThreadState.X31, Is.EqualTo(_W31));
|
||||
}
|
||||
CompareAgainstUnicorn();
|
||||
}
|
||||
|
||||
[Test, Description("CRC32W <Wd>, <Wn>, <Wm>")]
|
||||
|
@ -749,6 +768,7 @@ namespace Ryujinx.Tests.Cpu
|
|||
{
|
||||
Assert.That((uint)ThreadState.X31, Is.EqualTo(_W31));
|
||||
}
|
||||
CompareAgainstUnicorn();
|
||||
}
|
||||
|
||||
[Test, Description("CRC32H <Wd>, <Wn>, <Wm>")]
|
||||
|
@ -780,6 +800,7 @@ namespace Ryujinx.Tests.Cpu
|
|||
{
|
||||
Assert.That((uint)ThreadState.X31, Is.EqualTo(_W31));
|
||||
}
|
||||
CompareAgainstUnicorn();
|
||||
}
|
||||
|
||||
[Test, Description("CRC32B <Wd>, <Wn>, <Wm>")]
|
||||
|
@ -811,6 +832,7 @@ namespace Ryujinx.Tests.Cpu
|
|||
{
|
||||
Assert.That((uint)ThreadState.X31, Is.EqualTo(_W31));
|
||||
}
|
||||
CompareAgainstUnicorn();
|
||||
}
|
||||
|
||||
[Test, Description("CRC32CX <Wd>, <Wn>, <Xm>")]
|
||||
|
@ -844,6 +866,7 @@ namespace Ryujinx.Tests.Cpu
|
|||
{
|
||||
Assert.That((uint)ThreadState.X31, Is.EqualTo(_W31));
|
||||
}
|
||||
CompareAgainstUnicorn();
|
||||
}
|
||||
|
||||
[Test, Description("CRC32CW <Wd>, <Wn>, <Wm>")]
|
||||
|
@ -875,6 +898,7 @@ namespace Ryujinx.Tests.Cpu
|
|||
{
|
||||
Assert.That((uint)ThreadState.X31, Is.EqualTo(_W31));
|
||||
}
|
||||
CompareAgainstUnicorn();
|
||||
}
|
||||
|
||||
[Test, Description("CRC32CH <Wd>, <Wn>, <Wm>")]
|
||||
|
@ -906,6 +930,7 @@ namespace Ryujinx.Tests.Cpu
|
|||
{
|
||||
Assert.That((uint)ThreadState.X31, Is.EqualTo(_W31));
|
||||
}
|
||||
CompareAgainstUnicorn();
|
||||
}
|
||||
|
||||
[Test, Description("CRC32CB <Wd>, <Wn>, <Wm>")]
|
||||
|
@ -937,6 +962,7 @@ namespace Ryujinx.Tests.Cpu
|
|||
{
|
||||
Assert.That((uint)ThreadState.X31, Is.EqualTo(_W31));
|
||||
}
|
||||
CompareAgainstUnicorn();
|
||||
}
|
||||
|
||||
[Test, Description("EON <Xd>, <Xn>, <Xm>{, <shift> #<amount>}")]
|
||||
|
@ -972,6 +998,7 @@ namespace Ryujinx.Tests.Cpu
|
|||
{
|
||||
Assert.That((ulong)ThreadState.X31, Is.EqualTo(_X31));
|
||||
}
|
||||
CompareAgainstUnicorn();
|
||||
}
|
||||
|
||||
[Test, Description("EON <Wd>, <Wn>, <Wm>{, <shift> #<amount>}")]
|
||||
|
@ -1007,6 +1034,7 @@ namespace Ryujinx.Tests.Cpu
|
|||
{
|
||||
Assert.That((uint)ThreadState.X31, Is.EqualTo(_W31));
|
||||
}
|
||||
CompareAgainstUnicorn();
|
||||
}
|
||||
|
||||
[Test, Description("EOR <Xd>, <Xn>, <Xm>{, <shift> #<amount>}")]
|
||||
|
@ -1042,6 +1070,7 @@ namespace Ryujinx.Tests.Cpu
|
|||
{
|
||||
Assert.That((ulong)ThreadState.X31, Is.EqualTo(_X31));
|
||||
}
|
||||
CompareAgainstUnicorn();
|
||||
}
|
||||
|
||||
[Test, Description("EOR <Wd>, <Wn>, <Wm>{, <shift> #<amount>}")]
|
||||
|
@ -1077,6 +1106,7 @@ namespace Ryujinx.Tests.Cpu
|
|||
{
|
||||
Assert.That((uint)ThreadState.X31, Is.EqualTo(_W31));
|
||||
}
|
||||
CompareAgainstUnicorn();
|
||||
}
|
||||
|
||||
[Test, Description("EXTR <Xd>, <Xn>, <Xm>, #<lsb>")]
|
||||
|
@ -1111,6 +1141,7 @@ namespace Ryujinx.Tests.Cpu
|
|||
{
|
||||
Assert.That((ulong)ThreadState.X31, Is.EqualTo(_X31));
|
||||
}
|
||||
CompareAgainstUnicorn();
|
||||
}
|
||||
|
||||
[Test, Description("EXTR <Wd>, <Wn>, <Wm>, #<lsb>")]
|
||||
|
@ -1145,6 +1176,7 @@ namespace Ryujinx.Tests.Cpu
|
|||
{
|
||||
Assert.That((uint)ThreadState.X31, Is.EqualTo(_W31));
|
||||
}
|
||||
CompareAgainstUnicorn();
|
||||
}
|
||||
|
||||
[Test, Description("LSLV <Xd>, <Xn>, <Xm>")]
|
||||
|
@ -1177,6 +1209,7 @@ namespace Ryujinx.Tests.Cpu
|
|||
{
|
||||
Assert.That((ulong)ThreadState.X31, Is.EqualTo(_X31));
|
||||
}
|
||||
CompareAgainstUnicorn();
|
||||
}
|
||||
|
||||
[Test, Description("LSLV <Wd>, <Wn>, <Wm>")]
|
||||
|
@ -1209,6 +1242,7 @@ namespace Ryujinx.Tests.Cpu
|
|||
{
|
||||
Assert.That((uint)ThreadState.X31, Is.EqualTo(_W31));
|
||||
}
|
||||
CompareAgainstUnicorn();
|
||||
}
|
||||
|
||||
[Test, Description("LSRV <Xd>, <Xn>, <Xm>")]
|
||||
|
@ -1241,6 +1275,7 @@ namespace Ryujinx.Tests.Cpu
|
|||
{
|
||||
Assert.That((ulong)ThreadState.X31, Is.EqualTo(_X31));
|
||||
}
|
||||
CompareAgainstUnicorn();
|
||||
}
|
||||
|
||||
[Test, Description("LSRV <Wd>, <Wn>, <Wm>")]
|
||||
|
@ -1273,6 +1308,7 @@ namespace Ryujinx.Tests.Cpu
|
|||
{
|
||||
Assert.That((uint)ThreadState.X31, Is.EqualTo(_W31));
|
||||
}
|
||||
CompareAgainstUnicorn();
|
||||
}
|
||||
|
||||
[Test, Description("ORN <Xd>, <Xn>, <Xm>{, <shift> #<amount>}")]
|
||||
|
@ -1308,6 +1344,7 @@ namespace Ryujinx.Tests.Cpu
|
|||
{
|
||||
Assert.That((ulong)ThreadState.X31, Is.EqualTo(_X31));
|
||||
}
|
||||
CompareAgainstUnicorn();
|
||||
}
|
||||
|
||||
[Test, Description("ORN <Wd>, <Wn>, <Wm>{, <shift> #<amount>}")]
|
||||
|
@ -1343,6 +1380,7 @@ namespace Ryujinx.Tests.Cpu
|
|||
{
|
||||
Assert.That((uint)ThreadState.X31, Is.EqualTo(_W31));
|
||||
}
|
||||
CompareAgainstUnicorn();
|
||||
}
|
||||
|
||||
[Test, Description("ORR <Xd>, <Xn>, <Xm>{, <shift> #<amount>}")]
|
||||
|
@ -1378,6 +1416,7 @@ namespace Ryujinx.Tests.Cpu
|
|||
{
|
||||
Assert.That((ulong)ThreadState.X31, Is.EqualTo(_X31));
|
||||
}
|
||||
CompareAgainstUnicorn();
|
||||
}
|
||||
|
||||
[Test, Description("ORR <Wd>, <Wn>, <Wm>{, <shift> #<amount>}")]
|
||||
|
@ -1413,6 +1452,7 @@ namespace Ryujinx.Tests.Cpu
|
|||
{
|
||||
Assert.That((uint)ThreadState.X31, Is.EqualTo(_W31));
|
||||
}
|
||||
CompareAgainstUnicorn();
|
||||
}
|
||||
|
||||
[Test, Description("RORV <Xd>, <Xn>, <Xm>")]
|
||||
|
@ -1445,6 +1485,7 @@ namespace Ryujinx.Tests.Cpu
|
|||
{
|
||||
Assert.That((ulong)ThreadState.X31, Is.EqualTo(_X31));
|
||||
}
|
||||
CompareAgainstUnicorn();
|
||||
}
|
||||
|
||||
[Test, Description("RORV <Wd>, <Wn>, <Wm>")]
|
||||
|
@ -1477,6 +1518,7 @@ namespace Ryujinx.Tests.Cpu
|
|||
{
|
||||
Assert.That((uint)ThreadState.X31, Is.EqualTo(_W31));
|
||||
}
|
||||
CompareAgainstUnicorn();
|
||||
}
|
||||
|
||||
[Test, Description("SBC <Xd>, <Xn>, <Xm>")]
|
||||
|
@ -1511,6 +1553,7 @@ namespace Ryujinx.Tests.Cpu
|
|||
{
|
||||
Assert.That((ulong)ThreadState.X31, Is.EqualTo(_X31));
|
||||
}
|
||||
CompareAgainstUnicorn();
|
||||
}
|
||||
|
||||
[Test, Description("SBC <Wd>, <Wn>, <Wm>")]
|
||||
|
@ -1545,6 +1588,7 @@ namespace Ryujinx.Tests.Cpu
|
|||
{
|
||||
Assert.That((uint)ThreadState.X31, Is.EqualTo(_W31));
|
||||
}
|
||||
CompareAgainstUnicorn();
|
||||
}
|
||||
|
||||
[Test, Description("SBCS <Xd>, <Xn>, <Xm>")]
|
||||
|
@ -1585,6 +1629,7 @@ namespace Ryujinx.Tests.Cpu
|
|||
Assert.That(ThreadState.Carry, Is.EqualTo(Shared.PSTATE.C));
|
||||
Assert.That(ThreadState.Overflow, Is.EqualTo(Shared.PSTATE.V));
|
||||
});
|
||||
CompareAgainstUnicorn();
|
||||
}
|
||||
|
||||
[Test, Description("SBCS <Wd>, <Wn>, <Wm>")]
|
||||
|
@ -1625,6 +1670,7 @@ namespace Ryujinx.Tests.Cpu
|
|||
Assert.That(ThreadState.Carry, Is.EqualTo(Shared.PSTATE.C));
|
||||
Assert.That(ThreadState.Overflow, Is.EqualTo(Shared.PSTATE.V));
|
||||
});
|
||||
CompareAgainstUnicorn();
|
||||
}
|
||||
|
||||
[Test, Description("SDIV <Xd>, <Xn>, <Xm>")]
|
||||
|
@ -1657,6 +1703,7 @@ namespace Ryujinx.Tests.Cpu
|
|||
{
|
||||
Assert.That((ulong)ThreadState.X31, Is.EqualTo(_X31));
|
||||
}
|
||||
CompareAgainstUnicorn();
|
||||
}
|
||||
|
||||
[Test, Description("SDIV <Wd>, <Wn>, <Wm>")]
|
||||
|
@ -1689,6 +1736,7 @@ namespace Ryujinx.Tests.Cpu
|
|||
{
|
||||
Assert.That((uint)ThreadState.X31, Is.EqualTo(_W31));
|
||||
}
|
||||
CompareAgainstUnicorn();
|
||||
}
|
||||
|
||||
[Test, Description("SUB <Xd>, <Xn>, <Xm>{, <shift> #<amount>}")]
|
||||
|
@ -1724,6 +1772,7 @@ namespace Ryujinx.Tests.Cpu
|
|||
{
|
||||
Assert.That((ulong)ThreadState.X31, Is.EqualTo(_X31));
|
||||
}
|
||||
CompareAgainstUnicorn();
|
||||
}
|
||||
|
||||
[Test, Description("SUB <Wd>, <Wn>, <Wm>{, <shift> #<amount>}")]
|
||||
|
@ -1759,6 +1808,7 @@ namespace Ryujinx.Tests.Cpu
|
|||
{
|
||||
Assert.That((uint)ThreadState.X31, Is.EqualTo(_W31));
|
||||
}
|
||||
CompareAgainstUnicorn();
|
||||
}
|
||||
|
||||
[Test, Description("SUBS <Xd>, <Xn>, <Xm>{, <shift> #<amount>}")]
|
||||
|
@ -1800,6 +1850,7 @@ namespace Ryujinx.Tests.Cpu
|
|||
Assert.That(ThreadState.Carry, Is.EqualTo(Shared.PSTATE.C));
|
||||
Assert.That(ThreadState.Overflow, Is.EqualTo(Shared.PSTATE.V));
|
||||
});
|
||||
CompareAgainstUnicorn();
|
||||
}
|
||||
|
||||
[Test, Description("SUBS <Wd>, <Wn>, <Wm>{, <shift> #<amount>}")]
|
||||
|
@ -1841,6 +1892,7 @@ namespace Ryujinx.Tests.Cpu
|
|||
Assert.That(ThreadState.Carry, Is.EqualTo(Shared.PSTATE.C));
|
||||
Assert.That(ThreadState.Overflow, Is.EqualTo(Shared.PSTATE.V));
|
||||
});
|
||||
CompareAgainstUnicorn();
|
||||
}
|
||||
|
||||
[Test, Description("UDIV <Xd>, <Xn>, <Xm>")]
|
||||
|
@ -1873,6 +1925,7 @@ namespace Ryujinx.Tests.Cpu
|
|||
{
|
||||
Assert.That((ulong)ThreadState.X31, Is.EqualTo(_X31));
|
||||
}
|
||||
CompareAgainstUnicorn();
|
||||
}
|
||||
|
||||
[Test, Description("UDIV <Wd>, <Wn>, <Wm>")]
|
||||
|
@ -1905,6 +1958,7 @@ namespace Ryujinx.Tests.Cpu
|
|||
{
|
||||
Assert.That((uint)ThreadState.X31, Is.EqualTo(_W31));
|
||||
}
|
||||
CompareAgainstUnicorn();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
|
|
@ -66,6 +66,7 @@ namespace Ryujinx.Tests.Cpu
|
|||
|
||||
Assert.That((ulong)ThreadState.X31, Is.EqualTo(SP));
|
||||
}
|
||||
CompareAgainstUnicorn();
|
||||
}
|
||||
|
||||
[Test, Description("ADD <Xd|SP>, <Xn|SP>, <W><m>{, <extend> {#<amount>}}")]
|
||||
|
@ -116,6 +117,7 @@ namespace Ryujinx.Tests.Cpu
|
|||
|
||||
Assert.That((ulong)ThreadState.X31, Is.EqualTo(SP));
|
||||
}
|
||||
CompareAgainstUnicorn();
|
||||
}
|
||||
|
||||
[Test, Description("ADD <Xd|SP>, <Xn|SP>, <W><m>{, <extend> {#<amount>}}")]
|
||||
|
@ -166,6 +168,7 @@ namespace Ryujinx.Tests.Cpu
|
|||
|
||||
Assert.That((ulong)ThreadState.X31, Is.EqualTo(SP));
|
||||
}
|
||||
CompareAgainstUnicorn();
|
||||
}
|
||||
|
||||
[Test, Description("ADD <Xd|SP>, <Xn|SP>, <W><m>{, <extend> {#<amount>}}")]
|
||||
|
@ -216,6 +219,7 @@ namespace Ryujinx.Tests.Cpu
|
|||
|
||||
Assert.That((ulong)ThreadState.X31, Is.EqualTo(SP));
|
||||
}
|
||||
CompareAgainstUnicorn();
|
||||
}
|
||||
|
||||
[Test, Description("ADD <Wd|WSP>, <Wn|WSP>, <Wm>{, <extend> {#<amount>}}")]
|
||||
|
@ -266,6 +270,7 @@ namespace Ryujinx.Tests.Cpu
|
|||
|
||||
Assert.That((uint)ThreadState.X31, Is.EqualTo(WSP));
|
||||
}
|
||||
CompareAgainstUnicorn();
|
||||
}
|
||||
|
||||
[Test, Description("ADD <Wd|WSP>, <Wn|WSP>, <Wm>{, <extend> {#<amount>}}")]
|
||||
|
@ -316,6 +321,7 @@ namespace Ryujinx.Tests.Cpu
|
|||
|
||||
Assert.That((uint)ThreadState.X31, Is.EqualTo(WSP));
|
||||
}
|
||||
CompareAgainstUnicorn();
|
||||
}
|
||||
|
||||
[Test, Description("ADD <Wd|WSP>, <Wn|WSP>, <Wm>{, <extend> {#<amount>}}")]
|
||||
|
@ -366,6 +372,7 @@ namespace Ryujinx.Tests.Cpu
|
|||
|
||||
Assert.That((uint)ThreadState.X31, Is.EqualTo(WSP));
|
||||
}
|
||||
CompareAgainstUnicorn();
|
||||
}
|
||||
|
||||
[Test, Description("ADDS <Xd>, <Xn|SP>, <X><m>{, <extend> {#<amount>}}")]
|
||||
|
@ -410,6 +417,7 @@ namespace Ryujinx.Tests.Cpu
|
|||
Assert.That(ThreadState.Carry, Is.EqualTo(Shared.PSTATE.C));
|
||||
Assert.That(ThreadState.Overflow, Is.EqualTo(Shared.PSTATE.V));
|
||||
});
|
||||
CompareAgainstUnicorn();
|
||||
}
|
||||
|
||||
[Test, Description("ADDS <Xd>, <Xn|SP>, <W><m>{, <extend> {#<amount>}}")]
|
||||
|
@ -455,6 +463,7 @@ namespace Ryujinx.Tests.Cpu
|
|||
Assert.That(ThreadState.Carry, Is.EqualTo(Shared.PSTATE.C));
|
||||
Assert.That(ThreadState.Overflow, Is.EqualTo(Shared.PSTATE.V));
|
||||
});
|
||||
CompareAgainstUnicorn();
|
||||
}
|
||||
|
||||
[Test, Description("ADDS <Xd>, <Xn|SP>, <W><m>{, <extend> {#<amount>}}")]
|
||||
|
@ -500,6 +509,7 @@ namespace Ryujinx.Tests.Cpu
|
|||
Assert.That(ThreadState.Carry, Is.EqualTo(Shared.PSTATE.C));
|
||||
Assert.That(ThreadState.Overflow, Is.EqualTo(Shared.PSTATE.V));
|
||||
});
|
||||
CompareAgainstUnicorn();
|
||||
}
|
||||
|
||||
[Test, Description("ADDS <Xd>, <Xn|SP>, <W><m>{, <extend> {#<amount>}}")]
|
||||
|
@ -545,6 +555,7 @@ namespace Ryujinx.Tests.Cpu
|
|||
Assert.That(ThreadState.Carry, Is.EqualTo(Shared.PSTATE.C));
|
||||
Assert.That(ThreadState.Overflow, Is.EqualTo(Shared.PSTATE.V));
|
||||
});
|
||||
CompareAgainstUnicorn();
|
||||
}
|
||||
|
||||
[Test, Description("ADDS <Wd>, <Wn|WSP>, <Wm>{, <extend> {#<amount>}}")]
|
||||
|
@ -590,6 +601,7 @@ namespace Ryujinx.Tests.Cpu
|
|||
Assert.That(ThreadState.Carry, Is.EqualTo(Shared.PSTATE.C));
|
||||
Assert.That(ThreadState.Overflow, Is.EqualTo(Shared.PSTATE.V));
|
||||
});
|
||||
CompareAgainstUnicorn();
|
||||
}
|
||||
|
||||
[Test, Description("ADDS <Wd>, <Wn|WSP>, <Wm>{, <extend> {#<amount>}}")]
|
||||
|
@ -635,6 +647,7 @@ namespace Ryujinx.Tests.Cpu
|
|||
Assert.That(ThreadState.Carry, Is.EqualTo(Shared.PSTATE.C));
|
||||
Assert.That(ThreadState.Overflow, Is.EqualTo(Shared.PSTATE.V));
|
||||
});
|
||||
CompareAgainstUnicorn();
|
||||
}
|
||||
|
||||
[Test, Description("ADDS <Wd>, <Wn|WSP>, <Wm>{, <extend> {#<amount>}}")]
|
||||
|
@ -680,6 +693,7 @@ namespace Ryujinx.Tests.Cpu
|
|||
Assert.That(ThreadState.Carry, Is.EqualTo(Shared.PSTATE.C));
|
||||
Assert.That(ThreadState.Overflow, Is.EqualTo(Shared.PSTATE.V));
|
||||
});
|
||||
CompareAgainstUnicorn();
|
||||
}
|
||||
|
||||
[Test, Description("SUB <Xd|SP>, <Xn|SP>, <X><m>{, <extend> {#<amount>}}")]
|
||||
|
@ -729,6 +743,7 @@ namespace Ryujinx.Tests.Cpu
|
|||
|
||||
Assert.That((ulong)ThreadState.X31, Is.EqualTo(SP));
|
||||
}
|
||||
CompareAgainstUnicorn();
|
||||
}
|
||||
|
||||
[Test, Description("SUB <Xd|SP>, <Xn|SP>, <W><m>{, <extend> {#<amount>}}")]
|
||||
|
@ -779,6 +794,7 @@ namespace Ryujinx.Tests.Cpu
|
|||
|
||||
Assert.That((ulong)ThreadState.X31, Is.EqualTo(SP));
|
||||
}
|
||||
CompareAgainstUnicorn();
|
||||
}
|
||||
|
||||
[Test, Description("SUB <Xd|SP>, <Xn|SP>, <W><m>{, <extend> {#<amount>}}")]
|
||||
|
@ -829,6 +845,7 @@ namespace Ryujinx.Tests.Cpu
|
|||
|
||||
Assert.That((ulong)ThreadState.X31, Is.EqualTo(SP));
|
||||
}
|
||||
CompareAgainstUnicorn();
|
||||
}
|
||||
|
||||
[Test, Description("SUB <Xd|SP>, <Xn|SP>, <W><m>{, <extend> {#<amount>}}")]
|
||||
|
@ -879,6 +896,7 @@ namespace Ryujinx.Tests.Cpu
|
|||
|
||||
Assert.That((ulong)ThreadState.X31, Is.EqualTo(SP));
|
||||
}
|
||||
CompareAgainstUnicorn();
|
||||
}
|
||||
|
||||
[Test, Description("SUB <Wd|WSP>, <Wn|WSP>, <Wm>{, <extend> {#<amount>}}")]
|
||||
|
@ -929,6 +947,7 @@ namespace Ryujinx.Tests.Cpu
|
|||
|
||||
Assert.That((uint)ThreadState.X31, Is.EqualTo(WSP));
|
||||
}
|
||||
CompareAgainstUnicorn();
|
||||
}
|
||||
|
||||
[Test, Description("SUB <Wd|WSP>, <Wn|WSP>, <Wm>{, <extend> {#<amount>}}")]
|
||||
|
@ -979,6 +998,7 @@ namespace Ryujinx.Tests.Cpu
|
|||
|
||||
Assert.That((uint)ThreadState.X31, Is.EqualTo(WSP));
|
||||
}
|
||||
CompareAgainstUnicorn();
|
||||
}
|
||||
|
||||
[Test, Description("SUB <Wd|WSP>, <Wn|WSP>, <Wm>{, <extend> {#<amount>}}")]
|
||||
|
@ -1029,6 +1049,7 @@ namespace Ryujinx.Tests.Cpu
|
|||
|
||||
Assert.That((uint)ThreadState.X31, Is.EqualTo(WSP));
|
||||
}
|
||||
CompareAgainstUnicorn();
|
||||
}
|
||||
|
||||
[Test, Description("SUBS <Xd>, <Xn|SP>, <X><m>{, <extend> {#<amount>}}")]
|
||||
|
@ -1073,6 +1094,7 @@ namespace Ryujinx.Tests.Cpu
|
|||
Assert.That(ThreadState.Carry, Is.EqualTo(Shared.PSTATE.C));
|
||||
Assert.That(ThreadState.Overflow, Is.EqualTo(Shared.PSTATE.V));
|
||||
});
|
||||
CompareAgainstUnicorn();
|
||||
}
|
||||
|
||||
[Test, Description("SUBS <Xd>, <Xn|SP>, <W><m>{, <extend> {#<amount>}}")]
|
||||
|
@ -1118,6 +1140,7 @@ namespace Ryujinx.Tests.Cpu
|
|||
Assert.That(ThreadState.Carry, Is.EqualTo(Shared.PSTATE.C));
|
||||
Assert.That(ThreadState.Overflow, Is.EqualTo(Shared.PSTATE.V));
|
||||
});
|
||||
CompareAgainstUnicorn();
|
||||
}
|
||||
|
||||
[Test, Description("SUBS <Xd>, <Xn|SP>, <W><m>{, <extend> {#<amount>}}")]
|
||||
|
@ -1163,6 +1186,7 @@ namespace Ryujinx.Tests.Cpu
|
|||
Assert.That(ThreadState.Carry, Is.EqualTo(Shared.PSTATE.C));
|
||||
Assert.That(ThreadState.Overflow, Is.EqualTo(Shared.PSTATE.V));
|
||||
});
|
||||
CompareAgainstUnicorn();
|
||||
}
|
||||
|
||||
[Test, Description("SUBS <Xd>, <Xn|SP>, <W><m>{, <extend> {#<amount>}}")]
|
||||
|
@ -1208,6 +1232,7 @@ namespace Ryujinx.Tests.Cpu
|
|||
Assert.That(ThreadState.Carry, Is.EqualTo(Shared.PSTATE.C));
|
||||
Assert.That(ThreadState.Overflow, Is.EqualTo(Shared.PSTATE.V));
|
||||
});
|
||||
CompareAgainstUnicorn();
|
||||
}
|
||||
|
||||
[Test, Description("SUBS <Wd>, <Wn|WSP>, <Wm>{, <extend> {#<amount>}}")]
|
||||
|
@ -1253,6 +1278,7 @@ namespace Ryujinx.Tests.Cpu
|
|||
Assert.That(ThreadState.Carry, Is.EqualTo(Shared.PSTATE.C));
|
||||
Assert.That(ThreadState.Overflow, Is.EqualTo(Shared.PSTATE.V));
|
||||
});
|
||||
CompareAgainstUnicorn();
|
||||
}
|
||||
|
||||
[Test, Description("SUBS <Wd>, <Wn|WSP>, <Wm>{, <extend> {#<amount>}}")]
|
||||
|
@ -1298,6 +1324,7 @@ namespace Ryujinx.Tests.Cpu
|
|||
Assert.That(ThreadState.Carry, Is.EqualTo(Shared.PSTATE.C));
|
||||
Assert.That(ThreadState.Overflow, Is.EqualTo(Shared.PSTATE.V));
|
||||
});
|
||||
CompareAgainstUnicorn();
|
||||
}
|
||||
|
||||
[Test, Description("SUBS <Wd>, <Wn|WSP>, <Wm>{, <extend> {#<amount>}}")]
|
||||
|
@ -1343,6 +1370,7 @@ namespace Ryujinx.Tests.Cpu
|
|||
Assert.That(ThreadState.Carry, Is.EqualTo(Shared.PSTATE.C));
|
||||
Assert.That(ThreadState.Overflow, Is.EqualTo(Shared.PSTATE.V));
|
||||
});
|
||||
CompareAgainstUnicorn();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
|
|
@ -50,6 +50,7 @@ namespace Ryujinx.Tests.Cpu
|
|||
{
|
||||
Assert.That((ulong)ThreadState.X31, Is.EqualTo(_X31));
|
||||
}
|
||||
CompareAgainstUnicorn();
|
||||
}
|
||||
|
||||
[Test, Description("BFM <Wd>, <Wn>, #<immr>, #<imms>")]
|
||||
|
@ -83,6 +84,7 @@ namespace Ryujinx.Tests.Cpu
|
|||
{
|
||||
Assert.That((uint)ThreadState.X31, Is.EqualTo(_W31));
|
||||
}
|
||||
CompareAgainstUnicorn();
|
||||
}
|
||||
|
||||
[Test, Description("SBFM <Xd>, <Xn>, #<immr>, #<imms>")]
|
||||
|
@ -114,6 +116,7 @@ namespace Ryujinx.Tests.Cpu
|
|||
{
|
||||
Assert.That((ulong)ThreadState.X31, Is.EqualTo(_X31));
|
||||
}
|
||||
CompareAgainstUnicorn();
|
||||
}
|
||||
|
||||
[Test, Description("SBFM <Wd>, <Wn>, #<immr>, #<imms>")]
|
||||
|
@ -145,6 +148,7 @@ namespace Ryujinx.Tests.Cpu
|
|||
{
|
||||
Assert.That((uint)ThreadState.X31, Is.EqualTo(_W31));
|
||||
}
|
||||
CompareAgainstUnicorn();
|
||||
}
|
||||
|
||||
[Test, Description("UBFM <Xd>, <Xn>, #<immr>, #<imms>")]
|
||||
|
@ -176,6 +180,7 @@ namespace Ryujinx.Tests.Cpu
|
|||
{
|
||||
Assert.That((ulong)ThreadState.X31, Is.EqualTo(_X31));
|
||||
}
|
||||
CompareAgainstUnicorn();
|
||||
}
|
||||
|
||||
[Test, Description("UBFM <Wd>, <Wn>, #<immr>, #<imms>")]
|
||||
|
@ -207,6 +212,7 @@ namespace Ryujinx.Tests.Cpu
|
|||
{
|
||||
Assert.That((uint)ThreadState.X31, Is.EqualTo(_W31));
|
||||
}
|
||||
CompareAgainstUnicorn();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
|
|
@ -49,6 +49,7 @@ namespace Ryujinx.Tests.Cpu
|
|||
Assert.That(ThreadState.Carry, Is.EqualTo(Shared.PSTATE.C));
|
||||
Assert.That(ThreadState.Overflow, Is.EqualTo(Shared.PSTATE.V));
|
||||
});
|
||||
CompareAgainstUnicorn();
|
||||
}
|
||||
|
||||
[Test, Description("CCMN <Wn>, #<imm>, #<nzcv>, <cond>")]
|
||||
|
@ -81,6 +82,7 @@ namespace Ryujinx.Tests.Cpu
|
|||
Assert.That(ThreadState.Carry, Is.EqualTo(Shared.PSTATE.C));
|
||||
Assert.That(ThreadState.Overflow, Is.EqualTo(Shared.PSTATE.V));
|
||||
});
|
||||
CompareAgainstUnicorn();
|
||||
}
|
||||
|
||||
[Test, Description("CCMP <Xn>, #<imm>, #<nzcv>, <cond>")]
|
||||
|
@ -113,6 +115,7 @@ namespace Ryujinx.Tests.Cpu
|
|||
Assert.That(ThreadState.Carry, Is.EqualTo(Shared.PSTATE.C));
|
||||
Assert.That(ThreadState.Overflow, Is.EqualTo(Shared.PSTATE.V));
|
||||
});
|
||||
CompareAgainstUnicorn();
|
||||
}
|
||||
|
||||
[Test, Description("CCMP <Wn>, #<imm>, #<nzcv>, <cond>")]
|
||||
|
@ -145,6 +148,7 @@ namespace Ryujinx.Tests.Cpu
|
|||
Assert.That(ThreadState.Carry, Is.EqualTo(Shared.PSTATE.C));
|
||||
Assert.That(ThreadState.Overflow, Is.EqualTo(Shared.PSTATE.V));
|
||||
});
|
||||
CompareAgainstUnicorn();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
|
|
@ -52,6 +52,7 @@ namespace Ryujinx.Tests.Cpu
|
|||
Assert.That(ThreadState.Carry, Is.EqualTo(Shared.PSTATE.C));
|
||||
Assert.That(ThreadState.Overflow, Is.EqualTo(Shared.PSTATE.V));
|
||||
});
|
||||
CompareAgainstUnicorn();
|
||||
}
|
||||
|
||||
[Test, Description("CCMN <Wn>, <Wm>, #<nzcv>, <cond>")]
|
||||
|
@ -87,6 +88,7 @@ namespace Ryujinx.Tests.Cpu
|
|||
Assert.That(ThreadState.Carry, Is.EqualTo(Shared.PSTATE.C));
|
||||
Assert.That(ThreadState.Overflow, Is.EqualTo(Shared.PSTATE.V));
|
||||
});
|
||||
CompareAgainstUnicorn();
|
||||
}
|
||||
|
||||
[Test, Description("CCMP <Xn>, <Xm>, #<nzcv>, <cond>")]
|
||||
|
@ -122,6 +124,7 @@ namespace Ryujinx.Tests.Cpu
|
|||
Assert.That(ThreadState.Carry, Is.EqualTo(Shared.PSTATE.C));
|
||||
Assert.That(ThreadState.Overflow, Is.EqualTo(Shared.PSTATE.V));
|
||||
});
|
||||
CompareAgainstUnicorn();
|
||||
}
|
||||
|
||||
[Test, Description("CCMP <Wn>, <Wm>, #<nzcv>, <cond>")]
|
||||
|
@ -157,6 +160,7 @@ namespace Ryujinx.Tests.Cpu
|
|||
Assert.That(ThreadState.Carry, Is.EqualTo(Shared.PSTATE.C));
|
||||
Assert.That(ThreadState.Overflow, Is.EqualTo(Shared.PSTATE.V));
|
||||
});
|
||||
CompareAgainstUnicorn();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
|
|
@ -54,6 +54,7 @@ namespace Ryujinx.Tests.Cpu
|
|||
{
|
||||
Assert.That((ulong)ThreadState.X31, Is.EqualTo(_X31));
|
||||
}
|
||||
CompareAgainstUnicorn();
|
||||
}
|
||||
|
||||
[Test, Description("CSEL <Wd>, <Wn>, <Wm>, <cond>")]
|
||||
|
@ -91,6 +92,7 @@ namespace Ryujinx.Tests.Cpu
|
|||
{
|
||||
Assert.That((uint)ThreadState.X31, Is.EqualTo(_W31));
|
||||
}
|
||||
CompareAgainstUnicorn();
|
||||
}
|
||||
|
||||
[Test, Description("CSINC <Xd>, <Xn>, <Xm>, <cond>")]
|
||||
|
@ -128,6 +130,7 @@ namespace Ryujinx.Tests.Cpu
|
|||
{
|
||||
Assert.That((ulong)ThreadState.X31, Is.EqualTo(_X31));
|
||||
}
|
||||
CompareAgainstUnicorn();
|
||||
}
|
||||
|
||||
[Test, Description("CSINC <Wd>, <Wn>, <Wm>, <cond>")]
|
||||
|
@ -165,6 +168,7 @@ namespace Ryujinx.Tests.Cpu
|
|||
{
|
||||
Assert.That((uint)ThreadState.X31, Is.EqualTo(_W31));
|
||||
}
|
||||
CompareAgainstUnicorn();
|
||||
}
|
||||
|
||||
[Test, Description("CSINV <Xd>, <Xn>, <Xm>, <cond>")]
|
||||
|
@ -202,6 +206,7 @@ namespace Ryujinx.Tests.Cpu
|
|||
{
|
||||
Assert.That((ulong)ThreadState.X31, Is.EqualTo(_X31));
|
||||
}
|
||||
CompareAgainstUnicorn();
|
||||
}
|
||||
|
||||
[Test, Description("CSINV <Wd>, <Wn>, <Wm>, <cond>")]
|
||||
|
@ -239,6 +244,7 @@ namespace Ryujinx.Tests.Cpu
|
|||
{
|
||||
Assert.That((uint)ThreadState.X31, Is.EqualTo(_W31));
|
||||
}
|
||||
CompareAgainstUnicorn();
|
||||
}
|
||||
|
||||
[Test, Description("CSNEG <Xd>, <Xn>, <Xm>, <cond>")]
|
||||
|
@ -276,6 +282,7 @@ namespace Ryujinx.Tests.Cpu
|
|||
{
|
||||
Assert.That((ulong)ThreadState.X31, Is.EqualTo(_X31));
|
||||
}
|
||||
CompareAgainstUnicorn();
|
||||
}
|
||||
|
||||
[Test, Description("CSNEG <Wd>, <Wn>, <Wm>, <cond>")]
|
||||
|
@ -313,6 +320,7 @@ namespace Ryujinx.Tests.Cpu
|
|||
{
|
||||
Assert.That((uint)ThreadState.X31, Is.EqualTo(_W31));
|
||||
}
|
||||
CompareAgainstUnicorn();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
|
|
@ -46,6 +46,7 @@ namespace Ryujinx.Tests.Cpu
|
|||
{
|
||||
Assert.That((ulong)ThreadState.X31, Is.EqualTo(_X31));
|
||||
}
|
||||
CompareAgainstUnicorn();
|
||||
}
|
||||
|
||||
[Test, Description("MOVK <Wd>, #<imm>{, LSL #<shift>}")]
|
||||
|
@ -75,6 +76,7 @@ namespace Ryujinx.Tests.Cpu
|
|||
{
|
||||
Assert.That((uint)ThreadState.X31, Is.EqualTo(_W31));
|
||||
}
|
||||
CompareAgainstUnicorn();
|
||||
}
|
||||
|
||||
[Test, Description("MOVN <Xd>, #<imm>{, LSL #<shift>}")]
|
||||
|
@ -102,6 +104,7 @@ namespace Ryujinx.Tests.Cpu
|
|||
{
|
||||
Assert.That((ulong)ThreadState.X31, Is.EqualTo(_X31));
|
||||
}
|
||||
CompareAgainstUnicorn();
|
||||
}
|
||||
|
||||
[Test, Description("MOVN <Wd>, #<imm>{, LSL #<shift>}")]
|
||||
|
@ -129,6 +132,7 @@ namespace Ryujinx.Tests.Cpu
|
|||
{
|
||||
Assert.That((uint)ThreadState.X31, Is.EqualTo(_W31));
|
||||
}
|
||||
CompareAgainstUnicorn();
|
||||
}
|
||||
|
||||
[Test, Description("MOVZ <Xd>, #<imm>{, LSL #<shift>}")]
|
||||
|
@ -156,6 +160,7 @@ namespace Ryujinx.Tests.Cpu
|
|||
{
|
||||
Assert.That((ulong)ThreadState.X31, Is.EqualTo(_X31));
|
||||
}
|
||||
CompareAgainstUnicorn();
|
||||
}
|
||||
|
||||
[Test, Description("MOVZ <Wd>, #<imm>{, LSL #<shift>}")]
|
||||
|
@ -183,6 +188,7 @@ namespace Ryujinx.Tests.Cpu
|
|||
{
|
||||
Assert.That((uint)ThreadState.X31, Is.EqualTo(_W31));
|
||||
}
|
||||
CompareAgainstUnicorn();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
|
|
@ -53,6 +53,7 @@ namespace Ryujinx.Tests.Cpu
|
|||
{
|
||||
Assert.That((ulong)ThreadState.X31, Is.EqualTo(_X31));
|
||||
}
|
||||
CompareAgainstUnicorn();
|
||||
}
|
||||
|
||||
[Test, Description("MADD <Wd>, <Wn>, <Wm>, <Wa>")]
|
||||
|
@ -89,6 +90,7 @@ namespace Ryujinx.Tests.Cpu
|
|||
{
|
||||
Assert.That((uint)ThreadState.X31, Is.EqualTo(_W31));
|
||||
}
|
||||
CompareAgainstUnicorn();
|
||||
}
|
||||
|
||||
[Test, Description("MSUB <Xd>, <Xn>, <Xm>, <Xa>")]
|
||||
|
@ -125,6 +127,7 @@ namespace Ryujinx.Tests.Cpu
|
|||
{
|
||||
Assert.That((ulong)ThreadState.X31, Is.EqualTo(_X31));
|
||||
}
|
||||
CompareAgainstUnicorn();
|
||||
}
|
||||
|
||||
[Test, Description("MSUB <Wd>, <Wn>, <Wm>, <Wa>")]
|
||||
|
@ -161,6 +164,7 @@ namespace Ryujinx.Tests.Cpu
|
|||
{
|
||||
Assert.That((uint)ThreadState.X31, Is.EqualTo(_W31));
|
||||
}
|
||||
CompareAgainstUnicorn();
|
||||
}
|
||||
|
||||
[Test, Description("SMADDL <Xd>, <Wn>, <Wm>, <Xa>")]
|
||||
|
@ -197,6 +201,7 @@ namespace Ryujinx.Tests.Cpu
|
|||
{
|
||||
Assert.That((ulong)ThreadState.X31, Is.EqualTo(_X31));
|
||||
}
|
||||
CompareAgainstUnicorn();
|
||||
}
|
||||
|
||||
[Test, Description("UMADDL <Xd>, <Wn>, <Wm>, <Xa>")]
|
||||
|
@ -233,6 +238,7 @@ namespace Ryujinx.Tests.Cpu
|
|||
{
|
||||
Assert.That((ulong)ThreadState.X31, Is.EqualTo(_X31));
|
||||
}
|
||||
CompareAgainstUnicorn();
|
||||
}
|
||||
|
||||
[Test, Description("SMSUBL <Xd>, <Wn>, <Wm>, <Xa>")]
|
||||
|
@ -269,6 +275,7 @@ namespace Ryujinx.Tests.Cpu
|
|||
{
|
||||
Assert.That((ulong)ThreadState.X31, Is.EqualTo(_X31));
|
||||
}
|
||||
CompareAgainstUnicorn();
|
||||
}
|
||||
|
||||
[Test, Description("UMSUBL <Xd>, <Wn>, <Wm>, <Xa>")]
|
||||
|
@ -305,6 +312,7 @@ namespace Ryujinx.Tests.Cpu
|
|||
{
|
||||
Assert.That((ulong)ThreadState.X31, Is.EqualTo(_X31));
|
||||
}
|
||||
CompareAgainstUnicorn();
|
||||
}
|
||||
|
||||
[Test, Description("SMULH <Xd>, <Xn>, <Xm>")]
|
||||
|
@ -337,6 +345,7 @@ namespace Ryujinx.Tests.Cpu
|
|||
{
|
||||
Assert.That((ulong)ThreadState.X31, Is.EqualTo(_X31));
|
||||
}
|
||||
CompareAgainstUnicorn();
|
||||
}
|
||||
|
||||
[Test, Description("UMULH <Xd>, <Xn>, <Xm>")]
|
||||
|
@ -369,6 +378,7 @@ namespace Ryujinx.Tests.Cpu
|
|||
{
|
||||
Assert.That((ulong)ThreadState.X31, Is.EqualTo(_X31));
|
||||
}
|
||||
CompareAgainstUnicorn();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -33,6 +33,7 @@ namespace Ryujinx.Tests.Cpu
|
|||
V1: Sse.StaticCast<ulong, float>(Sse2.SetVector128(0, A)),
|
||||
V2: Sse.StaticCast<ulong, float>(Sse2.SetVector128(0, B)));
|
||||
Assert.AreEqual(Result, Sse41.Extract(Sse.StaticCast<float, ulong>(ThreadState.V0), 0));
|
||||
CompareAgainstUnicorn();
|
||||
}
|
||||
|
||||
[TestCase(0x80000000u, 0x80000000u, 0x00000000u, 0x00000000u, 0x00000000u, 0x00000000u)]
|
||||
|
@ -59,6 +60,7 @@ namespace Ryujinx.Tests.Cpu
|
|||
Assert.AreEqual(Result0, GetVectorE0(ThreadState.V0));
|
||||
Assert.AreEqual(Result1, GetVectorE1(ThreadState.V0));
|
||||
});
|
||||
CompareAgainstUnicorn();
|
||||
}
|
||||
|
||||
[TestCase(0x1E225820u, 0x0000000000000000ul, 0x0000000080000000ul, 0x0000000080000000ul)]
|
||||
|
@ -85,6 +87,7 @@ namespace Ryujinx.Tests.Cpu
|
|||
V1: Sse.StaticCast<ulong, float>(Sse2.SetVector128(0, A)),
|
||||
V2: Sse.StaticCast<ulong, float>(Sse2.SetVector128(0, B)));
|
||||
Assert.AreEqual(Result, Sse41.Extract(Sse.StaticCast<float, ulong>(ThreadState.V0), 0));
|
||||
CompareAgainstUnicorn();
|
||||
}
|
||||
|
||||
[TestCase(0x80000000u, 0x80000000u, 0x00000000u, 0x00000000u, 0x80000000u, 0x80000000u)]
|
||||
|
@ -111,6 +114,7 @@ namespace Ryujinx.Tests.Cpu
|
|||
Assert.AreEqual(Result0, GetVectorE0(ThreadState.V0));
|
||||
Assert.AreEqual(Result1, GetVectorE1(ThreadState.V0));
|
||||
});
|
||||
CompareAgainstUnicorn();
|
||||
}
|
||||
|
||||
[Test, Description("FMUL S6, S1, V0.S[2]")]
|
||||
|
@ -121,6 +125,7 @@ namespace Ryujinx.Tests.Cpu
|
|||
V0: Sse.SetVector128(0, B, 0, 0));
|
||||
|
||||
Assert.That(Sse41.Extract(ThreadState.V6, (byte)0), Is.EqualTo(A * B));
|
||||
CompareAgainstUnicorn();
|
||||
}
|
||||
|
||||
[TestCase(0x00000000u, 0x7F800000u)]
|
||||
|
@ -135,6 +140,7 @@ namespace Ryujinx.Tests.Cpu
|
|||
Vector128<float> V1 = MakeVectorE0(A);
|
||||
AThreadState ThreadState = SingleOpcode(0x5EA1D820, V1: V1);
|
||||
Assert.AreEqual(Result, GetVectorE0(ThreadState.V0));
|
||||
CompareAgainstUnicorn();
|
||||
}
|
||||
|
||||
[Test, Description("FRECPS D0, D1, D2")]
|
||||
|
@ -145,6 +151,7 @@ namespace Ryujinx.Tests.Cpu
|
|||
V2: MakeVectorE0(B));
|
||||
|
||||
Assert.That(VectorExtractDouble(ThreadState.V0, 0), Is.EqualTo(2 - (A * B)));
|
||||
//CompareAgainstUnicorn(); // Not accurate enough
|
||||
}
|
||||
|
||||
[Test, Description("FRECPS V4.4S, V2.4S, V0.4S")]
|
||||
|
@ -163,6 +170,7 @@ namespace Ryujinx.Tests.Cpu
|
|||
Assert.That(Sse41.Extract(ThreadState.V4, (byte)2), Is.EqualTo(Result));
|
||||
Assert.That(Sse41.Extract(ThreadState.V4, (byte)3), Is.EqualTo(Result));
|
||||
});
|
||||
CompareAgainstUnicorn();
|
||||
}
|
||||
|
||||
[TestCase(0x3FE66666u, false, 0x40000000u)]
|
||||
|
@ -213,6 +221,7 @@ namespace Ryujinx.Tests.Cpu
|
|||
Vector128<float> V1 = MakeVectorE0(A);
|
||||
AThreadState ThreadState = SingleOpcode(0x1E264020, V1: V1, Fpcr: FpcrTemp);
|
||||
Assert.AreEqual(Result, GetVectorE0(ThreadState.V0));
|
||||
CompareAgainstUnicorn();
|
||||
}
|
||||
|
||||
[TestCase(0x6E618820u, 0x3FF3333333333333ul, 0x3FF3333333333333ul, false, 0x3FF0000000000000ul, 0x3FF0000000000000ul)]
|
||||
|
@ -240,6 +249,7 @@ namespace Ryujinx.Tests.Cpu
|
|||
Assert.AreEqual(Result0, GetVectorE0(ThreadState.V0));
|
||||
Assert.AreEqual(Result1, GetVectorE1(ThreadState.V0));
|
||||
});
|
||||
CompareAgainstUnicorn();
|
||||
}
|
||||
|
||||
[TestCase(0x3FE66666u, 'N', false, 0x40000000u)]
|
||||
|
@ -310,6 +320,7 @@ namespace Ryujinx.Tests.Cpu
|
|||
Vector128<float> V1 = MakeVectorE0(A);
|
||||
AThreadState ThreadState = SingleOpcode(0x1E27C020, V1: V1, Fpcr: FpcrTemp);
|
||||
Assert.AreEqual(Result, GetVectorE0(ThreadState.V0));
|
||||
CompareAgainstUnicorn();
|
||||
}
|
||||
|
||||
[TestCase(0x6EE19820u, 0x3FF3333333333333ul, 0x3FF3333333333333ul, 'N', false, 0x3FF0000000000000ul, 0x3FF0000000000000ul)]
|
||||
|
@ -376,6 +387,7 @@ namespace Ryujinx.Tests.Cpu
|
|||
Assert.AreEqual(Result0, GetVectorE0(ThreadState.V0));
|
||||
Assert.AreEqual(Result1, GetVectorE1(ThreadState.V0));
|
||||
});
|
||||
CompareAgainstUnicorn();
|
||||
}
|
||||
|
||||
[TestCase(0x3FE66666u, false, 0x3F800000u)]
|
||||
|
@ -426,6 +438,7 @@ namespace Ryujinx.Tests.Cpu
|
|||
Vector128<float> V1 = MakeVectorE0(A);
|
||||
AThreadState ThreadState = SingleOpcode(0x1E254020, V1: V1, Fpcr: FpcrTemp);
|
||||
Assert.AreEqual(Result, GetVectorE0(ThreadState.V0));
|
||||
CompareAgainstUnicorn();
|
||||
}
|
||||
|
||||
[TestCase(0x4E619820u, 0x3FF3333333333333ul, 0x3FF3333333333333ul, false, 0x3FF0000000000000ul, 0x3FF0000000000000ul)]
|
||||
|
@ -450,6 +463,7 @@ namespace Ryujinx.Tests.Cpu
|
|||
Assert.AreEqual(Result0, GetVectorE0(ThreadState.V0));
|
||||
Assert.AreEqual(Result1, GetVectorE1(ThreadState.V0));
|
||||
});
|
||||
CompareAgainstUnicorn();
|
||||
}
|
||||
|
||||
[TestCase(0x3FE66666u, false, 0x40000000u)]
|
||||
|
@ -500,6 +514,7 @@ namespace Ryujinx.Tests.Cpu
|
|||
Vector128<float> V1 = MakeVectorE0(A);
|
||||
AThreadState ThreadState = SingleOpcode(0x1E264020, V1: V1, Fpcr: FpcrTemp);
|
||||
Assert.AreEqual(Result, GetVectorE0(ThreadState.V0));
|
||||
CompareAgainstUnicorn();
|
||||
}
|
||||
|
||||
[TestCase(0x4E618820u, 0x3FF3333333333333ul, 0x3FF3333333333333ul, false, 0x3FF0000000000000ul, 0x3FF0000000000000ul)]
|
||||
|
@ -527,6 +542,7 @@ namespace Ryujinx.Tests.Cpu
|
|||
Assert.AreEqual(Result0, GetVectorE0(ThreadState.V0));
|
||||
Assert.AreEqual(Result1, GetVectorE1(ThreadState.V0));
|
||||
});
|
||||
CompareAgainstUnicorn();
|
||||
}
|
||||
|
||||
[TestCase(0x3FE66666u, false, 0x40000000u)]
|
||||
|
@ -577,6 +593,7 @@ namespace Ryujinx.Tests.Cpu
|
|||
Vector128<float> V1 = MakeVectorE0(A);
|
||||
AThreadState ThreadState = SingleOpcode(0x1E24C020, V1: V1, Fpcr: FpcrTemp);
|
||||
Assert.AreEqual(Result, GetVectorE0(ThreadState.V0));
|
||||
CompareAgainstUnicorn();
|
||||
}
|
||||
|
||||
[TestCase(0x4EE18820u, 0x3FF3333333333333ul, 0x3FF3333333333333ul, false, 0x4000000000000000ul, 0x4000000000000000ul)]
|
||||
|
@ -601,6 +618,7 @@ namespace Ryujinx.Tests.Cpu
|
|||
Assert.AreEqual(Result0, GetVectorE0(ThreadState.V0));
|
||||
Assert.AreEqual(Result1, GetVectorE1(ThreadState.V0));
|
||||
});
|
||||
CompareAgainstUnicorn();
|
||||
}
|
||||
|
||||
[TestCase(0x3FE66666u, 'N', false, 0x40000000u)]
|
||||
|
@ -671,6 +689,7 @@ namespace Ryujinx.Tests.Cpu
|
|||
Vector128<float> V1 = MakeVectorE0(A);
|
||||
AThreadState ThreadState = SingleOpcode(0x1E274020, V1: V1, Fpcr: FpcrTemp);
|
||||
Assert.AreEqual(Result, GetVectorE0(ThreadState.V0));
|
||||
CompareAgainstUnicorn();
|
||||
}
|
||||
|
||||
[TestCase(0x6E619820u, 0x3FF3333333333333ul, 0x3FF3333333333333ul, 'N', false, 0x3FF0000000000000ul, 0x3FF0000000000000ul)]
|
||||
|
@ -737,6 +756,7 @@ namespace Ryujinx.Tests.Cpu
|
|||
Assert.AreEqual(Result0, GetVectorE0(ThreadState.V0));
|
||||
Assert.AreEqual(Result1, GetVectorE1(ThreadState.V0));
|
||||
});
|
||||
CompareAgainstUnicorn();
|
||||
}
|
||||
|
||||
[TestCase(0x41200000u, 0x3EA18000u)]
|
||||
|
@ -745,6 +765,7 @@ namespace Ryujinx.Tests.Cpu
|
|||
Vector128<float> V1 = MakeVectorE0(A);
|
||||
AThreadState ThreadState = SingleOpcode(0x7EA1D820, V1: V1);
|
||||
Assert.AreEqual(Result, GetVectorE0(ThreadState.V0));
|
||||
CompareAgainstUnicorn();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -52,6 +52,7 @@ namespace Ryujinx.Tests.Cpu
|
|||
Assert.That(BitConverter.GetBytes(VectorExtractDouble(ThreadState.V0, (byte)0)), Is.EquivalentTo(Exp));
|
||||
Assert.That(VectorExtractDouble(ThreadState.V0, (byte)1), Is.Zero);
|
||||
});
|
||||
CompareAgainstUnicorn();
|
||||
}
|
||||
|
||||
[Test, Description("FCMEQ S0, S1, S2 | FCMGE S0, S1, S2 | FCMGT S0, S1, S2")]
|
||||
|
@ -84,6 +85,7 @@ namespace Ryujinx.Tests.Cpu
|
|||
Assert.That(Sse41.Extract(ThreadState.V0, (byte)2), Is.Zero);
|
||||
Assert.That(Sse41.Extract(ThreadState.V0, (byte)3), Is.Zero);
|
||||
});
|
||||
CompareAgainstUnicorn();
|
||||
}
|
||||
|
||||
[Test, Description("FCMEQ V0.2D, V1.2D, V2.2D | FCMGE V0.2D, V1.2D, V2.2D | FCMGT V0.2D, V1.2D, V2.2D")]
|
||||
|
@ -113,6 +115,7 @@ namespace Ryujinx.Tests.Cpu
|
|||
Assert.That(BitConverter.GetBytes(VectorExtractDouble(ThreadState.V0, (byte)0)), Is.EquivalentTo(Exp));
|
||||
Assert.That(BitConverter.GetBytes(VectorExtractDouble(ThreadState.V0, (byte)1)), Is.EquivalentTo(Exp));
|
||||
});
|
||||
CompareAgainstUnicorn();
|
||||
}
|
||||
|
||||
[Test, Description("FCMEQ V0.2S, V1.2S, V2.2S | FCMGE V0.2S, V1.2S, V2.2S | FCMGT V0.2S, V1.2S, V2.2S")]
|
||||
|
@ -145,6 +148,7 @@ namespace Ryujinx.Tests.Cpu
|
|||
Assert.That(Sse41.Extract(ThreadState.V0, (byte)2), Is.Zero);
|
||||
Assert.That(Sse41.Extract(ThreadState.V0, (byte)3), Is.Zero);
|
||||
});
|
||||
CompareAgainstUnicorn();
|
||||
}
|
||||
|
||||
[Test, Description("FCMEQ V0.4S, V1.4S, V2.4S | FCMGE V0.4S, V1.4S, V2.4S | FCMGT V0.4S, V1.4S, V2.4S")]
|
||||
|
@ -176,6 +180,7 @@ namespace Ryujinx.Tests.Cpu
|
|||
Assert.That(BitConverter.GetBytes(Sse41.Extract(ThreadState.V0, (byte)2)), Is.EquivalentTo(Exp));
|
||||
Assert.That(BitConverter.GetBytes(Sse41.Extract(ThreadState.V0, (byte)3)), Is.EquivalentTo(Exp));
|
||||
});
|
||||
CompareAgainstUnicorn();
|
||||
}
|
||||
|
||||
[Test, Description("FCMGT D0, D1, #0.0 | FCMGE D0, D1, #0.0 | FCMEQ D0, D1, #0.0 | FCMLE D0, D1, #0.0 | FCMLT D0, D1, #0.0")]
|
||||
|
@ -214,6 +219,7 @@ namespace Ryujinx.Tests.Cpu
|
|||
Assert.That(BitConverter.GetBytes(VectorExtractDouble(ThreadState.V0, (byte)0)), Is.EquivalentTo(Exp));
|
||||
Assert.That(VectorExtractDouble(ThreadState.V0, (byte)1), Is.Zero);
|
||||
});
|
||||
CompareAgainstUnicorn();
|
||||
}
|
||||
|
||||
[Test, Description("FCMGT S0, S1, #0.0 | FCMGE S0, S1, #0.0 | FCMEQ S0, S1, #0.0 | FCMLE S0, S1, #0.0 | FCMLT S0, S1, #0.0")]
|
||||
|
@ -254,6 +260,7 @@ namespace Ryujinx.Tests.Cpu
|
|||
Assert.That(Sse41.Extract(ThreadState.V0, (byte)2), Is.Zero);
|
||||
Assert.That(Sse41.Extract(ThreadState.V0, (byte)3), Is.Zero);
|
||||
});
|
||||
CompareAgainstUnicorn();
|
||||
}
|
||||
|
||||
[Test, Description("FCMGT V0.2D, V1.2D, #0.0 | FCMGE V0.2D, V1.2D, #0.0 | FCMEQ V0.2D, V1.2D, #0.0 | FCMLE V0.2D, V1.2D, #0.0 | FCMLT V0.2D, V1.2D, #0.0")]
|
||||
|
@ -291,6 +298,7 @@ namespace Ryujinx.Tests.Cpu
|
|||
Assert.That(BitConverter.GetBytes(VectorExtractDouble(ThreadState.V0, (byte)0)), Is.EquivalentTo(Exp));
|
||||
Assert.That(BitConverter.GetBytes(VectorExtractDouble(ThreadState.V0, (byte)1)), Is.EquivalentTo(Exp));
|
||||
});
|
||||
CompareAgainstUnicorn();
|
||||
}
|
||||
|
||||
[Test, Description("FCMGT V0.2S, V1.2S, #0.0 | FCMGE V0.2S, V1.2S, #0.0 | FCMEQ V0.2S, V1.2S, #0.0 | FCMLE V0.2S, V1.2S, #0.0 | FCMLT V0.2S, V1.2S, #0.0")]
|
||||
|
@ -331,6 +339,7 @@ namespace Ryujinx.Tests.Cpu
|
|||
Assert.That(Sse41.Extract(ThreadState.V0, (byte)2), Is.Zero);
|
||||
Assert.That(Sse41.Extract(ThreadState.V0, (byte)3), Is.Zero);
|
||||
});
|
||||
CompareAgainstUnicorn();
|
||||
}
|
||||
|
||||
[Test, Description("FCMGT V0.4S, V1.4S, #0.0 | FCMGE V0.4S, V1.4S, #0.0 | FCMEQ V0.4S, V1.4S, #0.0 | FCMLE V0.4S, V1.4S, #0.0 | FCMLT V0.4S, V1.4S, #0.0")]
|
||||
|
@ -370,6 +379,7 @@ namespace Ryujinx.Tests.Cpu
|
|||
Assert.That(BitConverter.GetBytes(Sse41.Extract(ThreadState.V0, (byte)2)), Is.EquivalentTo(Exp));
|
||||
Assert.That(BitConverter.GetBytes(Sse41.Extract(ThreadState.V0, (byte)3)), Is.EquivalentTo(Exp));
|
||||
});
|
||||
CompareAgainstUnicorn();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,7 +10,7 @@ namespace Ryujinx.Tests.Cpu
|
|||
{
|
||||
public class CpuTestSimdCrypto : CpuTest
|
||||
{
|
||||
[Test, Explicit, Description("AESD <Vd>.16B, <Vn>.16B")]
|
||||
[Test, Description("AESD <Vd>.16B, <Vn>.16B")]
|
||||
public void Aesd_V([Values(0u)] uint Rd,
|
||||
[Values(1u)] uint Rn,
|
||||
[Values(0x7B5B546573745665ul)] ulong ValueH,
|
||||
|
@ -37,9 +37,10 @@ namespace Ryujinx.Tests.Cpu
|
|||
Assert.That(GetVectorE0(ThreadState.V1), Is.EqualTo(RoundKeyL));
|
||||
Assert.That(GetVectorE1(ThreadState.V1), Is.EqualTo(RoundKeyH));
|
||||
});
|
||||
CompareAgainstUnicorn();
|
||||
}
|
||||
|
||||
[Test, Explicit, Description("AESE <Vd>.16B, <Vn>.16B")]
|
||||
[Test, Description("AESE <Vd>.16B, <Vn>.16B")]
|
||||
public void Aese_V([Values(0u)] uint Rd,
|
||||
[Values(1u)] uint Rn,
|
||||
[Values(0x7B5B546573745665ul)] ulong ValueH,
|
||||
|
@ -66,9 +67,10 @@ namespace Ryujinx.Tests.Cpu
|
|||
Assert.That(GetVectorE0(ThreadState.V1), Is.EqualTo(RoundKeyL));
|
||||
Assert.That(GetVectorE1(ThreadState.V1), Is.EqualTo(RoundKeyH));
|
||||
});
|
||||
CompareAgainstUnicorn();
|
||||
}
|
||||
|
||||
[Test, Explicit, Description("AESIMC <Vd>.16B, <Vn>.16B")]
|
||||
[Test, Description("AESIMC <Vd>.16B, <Vn>.16B")]
|
||||
public void Aesimc_V([Values(0u)] uint Rd,
|
||||
[Values(1u, 0u)] uint Rn,
|
||||
[Values(0x8DCAB9DC035006BCul)] ulong ValueH,
|
||||
|
@ -98,9 +100,10 @@ namespace Ryujinx.Tests.Cpu
|
|||
Assert.That(GetVectorE1(ThreadState.V1), Is.EqualTo(ValueH));
|
||||
});
|
||||
}
|
||||
CompareAgainstUnicorn();
|
||||
}
|
||||
|
||||
[Test, Explicit, Description("AESMC <Vd>.16B, <Vn>.16B")]
|
||||
[Test, Description("AESMC <Vd>.16B, <Vn>.16B")]
|
||||
public void Aesmc_V([Values(0u)] uint Rd,
|
||||
[Values(1u, 0u)] uint Rn,
|
||||
[Values(0x627A6F6644B109C8ul)] ulong ValueH,
|
||||
|
@ -130,6 +133,7 @@ namespace Ryujinx.Tests.Cpu
|
|||
Assert.That(GetVectorE1(ThreadState.V1), Is.EqualTo(ValueH));
|
||||
});
|
||||
}
|
||||
CompareAgainstUnicorn();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -35,6 +35,7 @@ namespace Ryujinx.Tests.Cpu
|
|||
Assert.That(Sse41.Extract(Sse.StaticCast<float, uint>(ThreadState.V1), (byte)2), Is.EqualTo(Result));
|
||||
Assert.That(Sse41.Extract(Sse.StaticCast<float, uint>(ThreadState.V1), (byte)3), Is.EqualTo(Result));
|
||||
});
|
||||
CompareAgainstUnicorn();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -2685,6 +2685,154 @@ namespace Ryujinx.Tests.Cpu.Tester
|
|||
V(d, result);
|
||||
}
|
||||
|
||||
// fcvtns_advsimd.html#FCVTNS_asisdmisc_R
|
||||
public static void Fcvtns_S(Bits sz, Bits Rn, Bits Rd)
|
||||
{
|
||||
const bool U = false;
|
||||
const bool o2 = false;
|
||||
const bool o1 = false;
|
||||
|
||||
/* Decode Scalar */
|
||||
int d = (int)UInt(Rd);
|
||||
int n = (int)UInt(Rn);
|
||||
|
||||
int esize = 32 << (int)UInt(sz);
|
||||
int datasize = esize;
|
||||
int elements = 1;
|
||||
|
||||
FPRounding rounding = FPDecodeRounding(Bits.Concat(o1, o2));
|
||||
|
||||
bool unsigned = (U == true);
|
||||
|
||||
/* Operation */
|
||||
/* CheckFPAdvSIMDEnabled64(); */
|
||||
|
||||
Bits result = new Bits(datasize);
|
||||
Bits operand = V(datasize, n);
|
||||
Bits element;
|
||||
|
||||
for (int e = 0; e <= elements - 1; e++)
|
||||
{
|
||||
element = Elem(operand, e, esize);
|
||||
|
||||
Elem(result, e, esize, FPToFixed(esize, element, 0, unsigned, FPCR, rounding));
|
||||
}
|
||||
|
||||
V(d, result);
|
||||
}
|
||||
|
||||
// fcvtns_advsimd.html#FCVTNS_asimdmisc_R
|
||||
public static void Fcvtns_V(bool Q, Bits sz, Bits Rn, Bits Rd)
|
||||
{
|
||||
const bool U = false;
|
||||
const bool o2 = false;
|
||||
const bool o1 = false;
|
||||
|
||||
/* Decode Vector */
|
||||
int d = (int)UInt(Rd);
|
||||
int n = (int)UInt(Rn);
|
||||
|
||||
/* if sz:Q == '10' then ReservedValue(); */
|
||||
|
||||
int esize = 32 << (int)UInt(sz);
|
||||
int datasize = (Q ? 128 : 64);
|
||||
int elements = datasize / esize;
|
||||
|
||||
FPRounding rounding = FPDecodeRounding(Bits.Concat(o1, o2));
|
||||
|
||||
bool unsigned = (U == true);
|
||||
|
||||
/* Operation */
|
||||
/* CheckFPAdvSIMDEnabled64(); */
|
||||
|
||||
Bits result = new Bits(datasize);
|
||||
Bits operand = V(datasize, n);
|
||||
Bits element;
|
||||
|
||||
for (int e = 0; e <= elements - 1; e++)
|
||||
{
|
||||
element = Elem(operand, e, esize);
|
||||
|
||||
Elem(result, e, esize, FPToFixed(esize, element, 0, unsigned, FPCR, rounding));
|
||||
}
|
||||
|
||||
V(d, result);
|
||||
}
|
||||
|
||||
// fcvtnu_advsimd.html#FCVTNU_asisdmisc_R
|
||||
public static void Fcvtnu_S(Bits sz, Bits Rn, Bits Rd)
|
||||
{
|
||||
const bool U = true;
|
||||
const bool o2 = false;
|
||||
const bool o1 = false;
|
||||
|
||||
/* Decode Scalar */
|
||||
int d = (int)UInt(Rd);
|
||||
int n = (int)UInt(Rn);
|
||||
|
||||
int esize = 32 << (int)UInt(sz);
|
||||
int datasize = esize;
|
||||
int elements = 1;
|
||||
|
||||
FPRounding rounding = FPDecodeRounding(Bits.Concat(o1, o2));
|
||||
|
||||
bool unsigned = (U == true);
|
||||
|
||||
/* Operation */
|
||||
/* CheckFPAdvSIMDEnabled64(); */
|
||||
|
||||
Bits result = new Bits(datasize);
|
||||
Bits operand = V(datasize, n);
|
||||
Bits element;
|
||||
|
||||
for (int e = 0; e <= elements - 1; e++)
|
||||
{
|
||||
element = Elem(operand, e, esize);
|
||||
|
||||
Elem(result, e, esize, FPToFixed(esize, element, 0, unsigned, FPCR, rounding));
|
||||
}
|
||||
|
||||
V(d, result);
|
||||
}
|
||||
|
||||
// fcvtnu_advsimd.html#FCVTNU_asimdmisc_R
|
||||
public static void Fcvtnu_V(bool Q, Bits sz, Bits Rn, Bits Rd)
|
||||
{
|
||||
const bool U = true;
|
||||
const bool o2 = false;
|
||||
const bool o1 = false;
|
||||
|
||||
/* Decode Vector */
|
||||
int d = (int)UInt(Rd);
|
||||
int n = (int)UInt(Rn);
|
||||
|
||||
/* if sz:Q == '10' then ReservedValue(); */
|
||||
|
||||
int esize = 32 << (int)UInt(sz);
|
||||
int datasize = (Q ? 128 : 64);
|
||||
int elements = datasize / esize;
|
||||
|
||||
FPRounding rounding = FPDecodeRounding(Bits.Concat(o1, o2));
|
||||
|
||||
bool unsigned = (U == true);
|
||||
|
||||
/* Operation */
|
||||
/* CheckFPAdvSIMDEnabled64(); */
|
||||
|
||||
Bits result = new Bits(datasize);
|
||||
Bits operand = V(datasize, n);
|
||||
Bits element;
|
||||
|
||||
for (int e = 0; e <= elements - 1; e++)
|
||||
{
|
||||
element = Elem(operand, e, esize);
|
||||
|
||||
Elem(result, e, esize, FPToFixed(esize, element, 0, unsigned, FPCR, rounding));
|
||||
}
|
||||
|
||||
V(d, result);
|
||||
}
|
||||
|
||||
// neg_advsimd.html#NEG_asisdmisc_R
|
||||
public static void Neg_S(Bits size, Bits Rn, Bits Rd)
|
||||
{
|
||||
|
@ -5122,6 +5270,57 @@ namespace Ryujinx.Tests.Cpu.Tester
|
|||
V(d, result);
|
||||
}
|
||||
|
||||
// saddl_advsimd.html
|
||||
public static void Saddl_V(bool Q, Bits size, Bits Rm, Bits Rn, Bits Rd)
|
||||
{
|
||||
const bool U = false;
|
||||
const bool o1 = false;
|
||||
|
||||
/* Decode */
|
||||
int d = (int)UInt(Rd);
|
||||
int n = (int)UInt(Rn);
|
||||
int m = (int)UInt(Rm);
|
||||
|
||||
/* if size == '11' then ReservedValue(); */
|
||||
|
||||
int esize = 8 << (int)UInt(size);
|
||||
int datasize = 64;
|
||||
int part = (int)UInt(Q);
|
||||
int elements = datasize / esize;
|
||||
|
||||
bool sub_op = (o1 == true);
|
||||
bool unsigned = (U == true);
|
||||
|
||||
/* Operation */
|
||||
/* CheckFPAdvSIMDEnabled64(); */
|
||||
|
||||
Bits result = new Bits(2 * datasize);
|
||||
Bits operand1 = Vpart(datasize, n, part);
|
||||
Bits operand2 = Vpart(datasize, m, part);
|
||||
BigInteger element1;
|
||||
BigInteger element2;
|
||||
BigInteger sum;
|
||||
|
||||
for (int e = 0; e <= elements - 1; e++)
|
||||
{
|
||||
element1 = Int(Elem(operand1, e, esize), unsigned);
|
||||
element2 = Int(Elem(operand2, e, esize), unsigned);
|
||||
|
||||
if (sub_op)
|
||||
{
|
||||
sum = element1 - element2;
|
||||
}
|
||||
else
|
||||
{
|
||||
sum = element1 + element2;
|
||||
}
|
||||
|
||||
Elem(result, e, 2 * esize, sum.SubBigInteger(2 * esize - 1, 0));
|
||||
}
|
||||
|
||||
V(d, result);
|
||||
}
|
||||
|
||||
// saddw_advsimd.html
|
||||
public static void Saddw_V(bool Q, Bits size, Bits Rm, Bits Rn, Bits Rd)
|
||||
{
|
||||
|
@ -5251,6 +5450,198 @@ namespace Ryujinx.Tests.Cpu.Tester
|
|||
V(d, result);
|
||||
}
|
||||
|
||||
// shadd_advsimd.html
|
||||
public static void Shadd_V(bool Q, Bits size, Bits Rm, Bits Rn, Bits Rd)
|
||||
{
|
||||
const bool U = false;
|
||||
|
||||
/* Decode */
|
||||
int d = (int)UInt(Rd);
|
||||
int n = (int)UInt(Rn);
|
||||
int m = (int)UInt(Rm);
|
||||
|
||||
/* if size == '11' then ReservedValue(); */
|
||||
|
||||
int esize = 8 << (int)UInt(size);
|
||||
int datasize = (Q ? 128 : 64);
|
||||
int elements = datasize / esize;
|
||||
|
||||
bool unsigned = (U == true);
|
||||
|
||||
/* Operation */
|
||||
/* CheckFPAdvSIMDEnabled64(); */
|
||||
|
||||
Bits result = new Bits(datasize);
|
||||
Bits operand1 = V(datasize, n);
|
||||
Bits operand2 = V(datasize, m);
|
||||
BigInteger element1;
|
||||
BigInteger element2;
|
||||
BigInteger sum;
|
||||
|
||||
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;
|
||||
|
||||
Elem(result, e, esize, sum.SubBigInteger(esize, 1));
|
||||
}
|
||||
|
||||
V(d, result);
|
||||
}
|
||||
|
||||
// shsub_advsimd.html
|
||||
public static void Shsub_V(bool Q, Bits size, Bits Rm, Bits Rn, Bits Rd)
|
||||
{
|
||||
const bool U = false;
|
||||
|
||||
/* Decode */
|
||||
int d = (int)UInt(Rd);
|
||||
int n = (int)UInt(Rn);
|
||||
int m = (int)UInt(Rm);
|
||||
|
||||
/* if size == '11' then ReservedValue(); */
|
||||
|
||||
int esize = 8 << (int)UInt(size);
|
||||
int datasize = (Q ? 128 : 64);
|
||||
int elements = datasize / esize;
|
||||
|
||||
bool unsigned = (U == true);
|
||||
|
||||
/* Operation */
|
||||
/* CheckFPAdvSIMDEnabled64(); */
|
||||
|
||||
Bits result = new Bits(datasize);
|
||||
Bits operand1 = V(datasize, n);
|
||||
Bits operand2 = V(datasize, m);
|
||||
BigInteger element1;
|
||||
BigInteger element2;
|
||||
BigInteger diff;
|
||||
|
||||
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;
|
||||
|
||||
Elem(result, e, esize, diff.SubBigInteger(esize, 1));
|
||||
}
|
||||
|
||||
V(d, result);
|
||||
}
|
||||
|
||||
// smlal_advsimd_vec.html
|
||||
public static void Smlal_V(bool Q, Bits size, Bits Rm, Bits Rn, Bits Rd)
|
||||
{
|
||||
const bool U = false;
|
||||
const bool o1 = false;
|
||||
|
||||
/* Decode */
|
||||
int d = (int)UInt(Rd);
|
||||
int n = (int)UInt(Rn);
|
||||
int m = (int)UInt(Rm);
|
||||
|
||||
/* if size == '11' then ReservedValue(); */
|
||||
|
||||
int esize = 8 << (int)UInt(size);
|
||||
int datasize = 64;
|
||||
int part = (int)UInt(Q);
|
||||
int elements = datasize / esize;
|
||||
|
||||
bool sub_op = (o1 == true);
|
||||
bool unsigned = (U == true);
|
||||
|
||||
/* Operation */
|
||||
/* CheckFPAdvSIMDEnabled64(); */
|
||||
|
||||
Bits result = new Bits(2 * datasize);
|
||||
Bits operand1 = Vpart(datasize, n, part);
|
||||
Bits operand2 = Vpart(datasize, m, part);
|
||||
Bits operand3 = V(2 * datasize, d);
|
||||
BigInteger element1;
|
||||
BigInteger element2;
|
||||
Bits product;
|
||||
Bits accum;
|
||||
|
||||
for (int e = 0; e <= elements - 1; e++)
|
||||
{
|
||||
element1 = Int(Elem(operand1, e, esize), unsigned);
|
||||
element2 = Int(Elem(operand2, e, esize), unsigned);
|
||||
|
||||
product = (element1 * element2).SubBigInteger(2 * esize - 1, 0);
|
||||
|
||||
if (sub_op)
|
||||
{
|
||||
accum = Elem(operand3, e, 2 * esize) - product;
|
||||
}
|
||||
else
|
||||
{
|
||||
accum = Elem(operand3, e, 2 * esize) + product;
|
||||
}
|
||||
|
||||
Elem(result, e, 2 * esize, accum);
|
||||
}
|
||||
|
||||
V(d, result);
|
||||
}
|
||||
|
||||
// smlsl_advsimd_vec.html
|
||||
public static void Smlsl_V(bool Q, Bits size, Bits Rm, Bits Rn, Bits Rd)
|
||||
{
|
||||
const bool U = false;
|
||||
const bool o1 = true;
|
||||
|
||||
/* Decode */
|
||||
int d = (int)UInt(Rd);
|
||||
int n = (int)UInt(Rn);
|
||||
int m = (int)UInt(Rm);
|
||||
|
||||
/* if size == '11' then ReservedValue(); */
|
||||
|
||||
int esize = 8 << (int)UInt(size);
|
||||
int datasize = 64;
|
||||
int part = (int)UInt(Q);
|
||||
int elements = datasize / esize;
|
||||
|
||||
bool sub_op = (o1 == true);
|
||||
bool unsigned = (U == true);
|
||||
|
||||
/* Operation */
|
||||
/* CheckFPAdvSIMDEnabled64(); */
|
||||
|
||||
Bits result = new Bits(2 * datasize);
|
||||
Bits operand1 = Vpart(datasize, n, part);
|
||||
Bits operand2 = Vpart(datasize, m, part);
|
||||
Bits operand3 = V(2 * datasize, d);
|
||||
BigInteger element1;
|
||||
BigInteger element2;
|
||||
Bits product;
|
||||
Bits accum;
|
||||
|
||||
for (int e = 0; e <= elements - 1; e++)
|
||||
{
|
||||
element1 = Int(Elem(operand1, e, esize), unsigned);
|
||||
element2 = Int(Elem(operand2, e, esize), unsigned);
|
||||
|
||||
product = (element1 * element2).SubBigInteger(2 * esize - 1, 0);
|
||||
|
||||
if (sub_op)
|
||||
{
|
||||
accum = Elem(operand3, e, 2 * esize) - product;
|
||||
}
|
||||
else
|
||||
{
|
||||
accum = Elem(operand3, e, 2 * esize) + product;
|
||||
}
|
||||
|
||||
Elem(result, e, 2 * esize, accum);
|
||||
}
|
||||
|
||||
V(d, result);
|
||||
}
|
||||
|
||||
// sqadd_advsimd.html#SQADD_asisdsame_only
|
||||
public static void Sqadd_S(Bits size, Bits Rm, Bits Rn, Bits Rd)
|
||||
{
|
||||
|
@ -5651,6 +6042,95 @@ namespace Ryujinx.Tests.Cpu.Tester
|
|||
V(d, result);
|
||||
}
|
||||
|
||||
// srhadd_advsimd.html
|
||||
public static void Srhadd_V(bool Q, Bits size, Bits Rm, Bits Rn, Bits Rd)
|
||||
{
|
||||
const bool U = false;
|
||||
|
||||
/* Decode */
|
||||
int d = (int)UInt(Rd);
|
||||
int n = (int)UInt(Rn);
|
||||
int m = (int)UInt(Rm);
|
||||
|
||||
/* if size == '11' then ReservedValue(); */
|
||||
|
||||
int esize = 8 << (int)UInt(size);
|
||||
int datasize = (Q ? 128 : 64);
|
||||
int elements = datasize / esize;
|
||||
|
||||
bool unsigned = (U == true);
|
||||
|
||||
/* Operation */
|
||||
/* CheckFPAdvSIMDEnabled64(); */
|
||||
|
||||
Bits result = new Bits(datasize);
|
||||
Bits operand1 = V(datasize, n);
|
||||
Bits operand2 = V(datasize, m);
|
||||
BigInteger element1;
|
||||
BigInteger element2;
|
||||
|
||||
for (int e = 0; e <= elements - 1; e++)
|
||||
{
|
||||
element1 = Int(Elem(operand1, e, esize), unsigned);
|
||||
element2 = Int(Elem(operand2, e, esize), unsigned);
|
||||
|
||||
Elem(result, e, esize, (element1 + element2 + 1).SubBigInteger(esize, 1));
|
||||
}
|
||||
|
||||
V(d, result);
|
||||
}
|
||||
|
||||
// ssubl_advsimd.html
|
||||
public static void Ssubl_V(bool Q, Bits size, Bits Rm, Bits Rn, Bits Rd)
|
||||
{
|
||||
const bool U = false;
|
||||
const bool o1 = true;
|
||||
|
||||
/* Decode */
|
||||
int d = (int)UInt(Rd);
|
||||
int n = (int)UInt(Rn);
|
||||
int m = (int)UInt(Rm);
|
||||
|
||||
/* if size == '11' then ReservedValue(); */
|
||||
|
||||
int esize = 8 << (int)UInt(size);
|
||||
int datasize = 64;
|
||||
int part = (int)UInt(Q);
|
||||
int elements = datasize / esize;
|
||||
|
||||
bool sub_op = (o1 == true);
|
||||
bool unsigned = (U == true);
|
||||
|
||||
/* Operation */
|
||||
/* CheckFPAdvSIMDEnabled64(); */
|
||||
|
||||
Bits result = new Bits(2 * datasize);
|
||||
Bits operand1 = Vpart(datasize, n, part);
|
||||
Bits operand2 = Vpart(datasize, m, part);
|
||||
BigInteger element1;
|
||||
BigInteger element2;
|
||||
BigInteger sum;
|
||||
|
||||
for (int e = 0; e <= elements - 1; e++)
|
||||
{
|
||||
element1 = Int(Elem(operand1, e, esize), unsigned);
|
||||
element2 = Int(Elem(operand2, e, esize), unsigned);
|
||||
|
||||
if (sub_op)
|
||||
{
|
||||
sum = element1 - element2;
|
||||
}
|
||||
else
|
||||
{
|
||||
sum = element1 + element2;
|
||||
}
|
||||
|
||||
Elem(result, e, 2 * esize, sum.SubBigInteger(2 * esize - 1, 0));
|
||||
}
|
||||
|
||||
V(d, result);
|
||||
}
|
||||
|
||||
// ssubw_advsimd.html
|
||||
public static void Ssubw_V(bool Q, Bits size, Bits Rm, Bits Rn, Bits Rd)
|
||||
{
|
||||
|
@ -6092,6 +6572,57 @@ namespace Ryujinx.Tests.Cpu.Tester
|
|||
V(d, result);
|
||||
}
|
||||
|
||||
// uaddl_advsimd.html
|
||||
public static void Uaddl_V(bool Q, Bits size, Bits Rm, Bits Rn, Bits Rd)
|
||||
{
|
||||
const bool U = true;
|
||||
const bool o1 = false;
|
||||
|
||||
/* Decode */
|
||||
int d = (int)UInt(Rd);
|
||||
int n = (int)UInt(Rn);
|
||||
int m = (int)UInt(Rm);
|
||||
|
||||
/* if size == '11' then ReservedValue(); */
|
||||
|
||||
int esize = 8 << (int)UInt(size);
|
||||
int datasize = 64;
|
||||
int part = (int)UInt(Q);
|
||||
int elements = datasize / esize;
|
||||
|
||||
bool sub_op = (o1 == true);
|
||||
bool unsigned = (U == true);
|
||||
|
||||
/* Operation */
|
||||
/* CheckFPAdvSIMDEnabled64(); */
|
||||
|
||||
Bits result = new Bits(2 * datasize);
|
||||
Bits operand1 = Vpart(datasize, n, part);
|
||||
Bits operand2 = Vpart(datasize, m, part);
|
||||
BigInteger element1;
|
||||
BigInteger element2;
|
||||
BigInteger sum;
|
||||
|
||||
for (int e = 0; e <= elements - 1; e++)
|
||||
{
|
||||
element1 = Int(Elem(operand1, e, esize), unsigned);
|
||||
element2 = Int(Elem(operand2, e, esize), unsigned);
|
||||
|
||||
if (sub_op)
|
||||
{
|
||||
sum = element1 - element2;
|
||||
}
|
||||
else
|
||||
{
|
||||
sum = element1 + element2;
|
||||
}
|
||||
|
||||
Elem(result, e, 2 * esize, sum.SubBigInteger(2 * esize - 1, 0));
|
||||
}
|
||||
|
||||
V(d, result);
|
||||
}
|
||||
|
||||
// uaddw_advsimd.html
|
||||
public static void Uaddw_V(bool Q, Bits size, Bits Rm, Bits Rn, Bits Rd)
|
||||
{
|
||||
|
@ -6143,6 +6674,198 @@ namespace Ryujinx.Tests.Cpu.Tester
|
|||
V(d, result);
|
||||
}
|
||||
|
||||
// uhadd_advsimd.html
|
||||
public static void Uhadd_V(bool Q, Bits size, Bits Rm, Bits Rn, Bits Rd)
|
||||
{
|
||||
const bool U = true;
|
||||
|
||||
/* Decode */
|
||||
int d = (int)UInt(Rd);
|
||||
int n = (int)UInt(Rn);
|
||||
int m = (int)UInt(Rm);
|
||||
|
||||
/* if size == '11' then ReservedValue(); */
|
||||
|
||||
int esize = 8 << (int)UInt(size);
|
||||
int datasize = (Q ? 128 : 64);
|
||||
int elements = datasize / esize;
|
||||
|
||||
bool unsigned = (U == true);
|
||||
|
||||
/* Operation */
|
||||
/* CheckFPAdvSIMDEnabled64(); */
|
||||
|
||||
Bits result = new Bits(datasize);
|
||||
Bits operand1 = V(datasize, n);
|
||||
Bits operand2 = V(datasize, m);
|
||||
BigInteger element1;
|
||||
BigInteger element2;
|
||||
BigInteger sum;
|
||||
|
||||
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;
|
||||
|
||||
Elem(result, e, esize, sum.SubBigInteger(esize, 1));
|
||||
}
|
||||
|
||||
V(d, result);
|
||||
}
|
||||
|
||||
// uhsub_advsimd.html
|
||||
public static void Uhsub_V(bool Q, Bits size, Bits Rm, Bits Rn, Bits Rd)
|
||||
{
|
||||
const bool U = true;
|
||||
|
||||
/* Decode */
|
||||
int d = (int)UInt(Rd);
|
||||
int n = (int)UInt(Rn);
|
||||
int m = (int)UInt(Rm);
|
||||
|
||||
/* if size == '11' then ReservedValue(); */
|
||||
|
||||
int esize = 8 << (int)UInt(size);
|
||||
int datasize = (Q ? 128 : 64);
|
||||
int elements = datasize / esize;
|
||||
|
||||
bool unsigned = (U == true);
|
||||
|
||||
/* Operation */
|
||||
/* CheckFPAdvSIMDEnabled64(); */
|
||||
|
||||
Bits result = new Bits(datasize);
|
||||
Bits operand1 = V(datasize, n);
|
||||
Bits operand2 = V(datasize, m);
|
||||
BigInteger element1;
|
||||
BigInteger element2;
|
||||
BigInteger diff;
|
||||
|
||||
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;
|
||||
|
||||
Elem(result, e, esize, diff.SubBigInteger(esize, 1));
|
||||
}
|
||||
|
||||
V(d, result);
|
||||
}
|
||||
|
||||
// umlal_advsimd_vec.html
|
||||
public static void Umlal_V(bool Q, Bits size, Bits Rm, Bits Rn, Bits Rd)
|
||||
{
|
||||
const bool U = true;
|
||||
const bool o1 = false;
|
||||
|
||||
/* Decode */
|
||||
int d = (int)UInt(Rd);
|
||||
int n = (int)UInt(Rn);
|
||||
int m = (int)UInt(Rm);
|
||||
|
||||
/* if size == '11' then ReservedValue(); */
|
||||
|
||||
int esize = 8 << (int)UInt(size);
|
||||
int datasize = 64;
|
||||
int part = (int)UInt(Q);
|
||||
int elements = datasize / esize;
|
||||
|
||||
bool sub_op = (o1 == true);
|
||||
bool unsigned = (U == true);
|
||||
|
||||
/* Operation */
|
||||
/* CheckFPAdvSIMDEnabled64(); */
|
||||
|
||||
Bits result = new Bits(2 * datasize);
|
||||
Bits operand1 = Vpart(datasize, n, part);
|
||||
Bits operand2 = Vpart(datasize, m, part);
|
||||
Bits operand3 = V(2 * datasize, d);
|
||||
BigInteger element1;
|
||||
BigInteger element2;
|
||||
Bits product;
|
||||
Bits accum;
|
||||
|
||||
for (int e = 0; e <= elements - 1; e++)
|
||||
{
|
||||
element1 = Int(Elem(operand1, e, esize), unsigned);
|
||||
element2 = Int(Elem(operand2, e, esize), unsigned);
|
||||
|
||||
product = (element1 * element2).SubBigInteger(2 * esize - 1, 0);
|
||||
|
||||
if (sub_op)
|
||||
{
|
||||
accum = Elem(operand3, e, 2 * esize) - product;
|
||||
}
|
||||
else
|
||||
{
|
||||
accum = Elem(operand3, e, 2 * esize) + product;
|
||||
}
|
||||
|
||||
Elem(result, e, 2 * esize, accum);
|
||||
}
|
||||
|
||||
V(d, result);
|
||||
}
|
||||
|
||||
// umlsl_advsimd_vec.html
|
||||
public static void Umlsl_V(bool Q, Bits size, Bits Rm, Bits Rn, Bits Rd)
|
||||
{
|
||||
const bool U = true;
|
||||
const bool o1 = true;
|
||||
|
||||
/* Decode */
|
||||
int d = (int)UInt(Rd);
|
||||
int n = (int)UInt(Rn);
|
||||
int m = (int)UInt(Rm);
|
||||
|
||||
/* if size == '11' then ReservedValue(); */
|
||||
|
||||
int esize = 8 << (int)UInt(size);
|
||||
int datasize = 64;
|
||||
int part = (int)UInt(Q);
|
||||
int elements = datasize / esize;
|
||||
|
||||
bool sub_op = (o1 == true);
|
||||
bool unsigned = (U == true);
|
||||
|
||||
/* Operation */
|
||||
/* CheckFPAdvSIMDEnabled64(); */
|
||||
|
||||
Bits result = new Bits(2 * datasize);
|
||||
Bits operand1 = Vpart(datasize, n, part);
|
||||
Bits operand2 = Vpart(datasize, m, part);
|
||||
Bits operand3 = V(2 * datasize, d);
|
||||
BigInteger element1;
|
||||
BigInteger element2;
|
||||
Bits product;
|
||||
Bits accum;
|
||||
|
||||
for (int e = 0; e <= elements - 1; e++)
|
||||
{
|
||||
element1 = Int(Elem(operand1, e, esize), unsigned);
|
||||
element2 = Int(Elem(operand2, e, esize), unsigned);
|
||||
|
||||
product = (element1 * element2).SubBigInteger(2 * esize - 1, 0);
|
||||
|
||||
if (sub_op)
|
||||
{
|
||||
accum = Elem(operand3, e, 2 * esize) - product;
|
||||
}
|
||||
else
|
||||
{
|
||||
accum = Elem(operand3, e, 2 * esize) + product;
|
||||
}
|
||||
|
||||
Elem(result, e, 2 * esize, accum);
|
||||
}
|
||||
|
||||
V(d, result);
|
||||
}
|
||||
|
||||
// uqadd_advsimd.html#UQADD_asisdsame_only
|
||||
public static void Uqadd_S(Bits size, Bits Rm, Bits Rn, Bits Rd)
|
||||
{
|
||||
|
@ -6339,6 +7062,95 @@ namespace Ryujinx.Tests.Cpu.Tester
|
|||
V(d, result);
|
||||
}
|
||||
|
||||
// urhadd_advsimd.html
|
||||
public static void Urhadd_V(bool Q, Bits size, Bits Rm, Bits Rn, Bits Rd)
|
||||
{
|
||||
const bool U = true;
|
||||
|
||||
/* Decode */
|
||||
int d = (int)UInt(Rd);
|
||||
int n = (int)UInt(Rn);
|
||||
int m = (int)UInt(Rm);
|
||||
|
||||
/* if size == '11' then ReservedValue(); */
|
||||
|
||||
int esize = 8 << (int)UInt(size);
|
||||
int datasize = (Q ? 128 : 64);
|
||||
int elements = datasize / esize;
|
||||
|
||||
bool unsigned = (U == true);
|
||||
|
||||
/* Operation */
|
||||
/* CheckFPAdvSIMDEnabled64(); */
|
||||
|
||||
Bits result = new Bits(datasize);
|
||||
Bits operand1 = V(datasize, n);
|
||||
Bits operand2 = V(datasize, m);
|
||||
BigInteger element1;
|
||||
BigInteger element2;
|
||||
|
||||
for (int e = 0; e <= elements - 1; e++)
|
||||
{
|
||||
element1 = Int(Elem(operand1, e, esize), unsigned);
|
||||
element2 = Int(Elem(operand2, e, esize), unsigned);
|
||||
|
||||
Elem(result, e, esize, (element1 + element2 + 1).SubBigInteger(esize, 1));
|
||||
}
|
||||
|
||||
V(d, result);
|
||||
}
|
||||
|
||||
// usubl_advsimd.html
|
||||
public static void Usubl_V(bool Q, Bits size, Bits Rm, Bits Rn, Bits Rd)
|
||||
{
|
||||
const bool U = true;
|
||||
const bool o1 = true;
|
||||
|
||||
/* Decode */
|
||||
int d = (int)UInt(Rd);
|
||||
int n = (int)UInt(Rn);
|
||||
int m = (int)UInt(Rm);
|
||||
|
||||
/* if size == '11' then ReservedValue(); */
|
||||
|
||||
int esize = 8 << (int)UInt(size);
|
||||
int datasize = 64;
|
||||
int part = (int)UInt(Q);
|
||||
int elements = datasize / esize;
|
||||
|
||||
bool sub_op = (o1 == true);
|
||||
bool unsigned = (U == true);
|
||||
|
||||
/* Operation */
|
||||
/* CheckFPAdvSIMDEnabled64(); */
|
||||
|
||||
Bits result = new Bits(2 * datasize);
|
||||
Bits operand1 = Vpart(datasize, n, part);
|
||||
Bits operand2 = Vpart(datasize, m, part);
|
||||
BigInteger element1;
|
||||
BigInteger element2;
|
||||
BigInteger sum;
|
||||
|
||||
for (int e = 0; e <= elements - 1; e++)
|
||||
{
|
||||
element1 = Int(Elem(operand1, e, esize), unsigned);
|
||||
element2 = Int(Elem(operand2, e, esize), unsigned);
|
||||
|
||||
if (sub_op)
|
||||
{
|
||||
sum = element1 - element2;
|
||||
}
|
||||
else
|
||||
{
|
||||
sum = element1 + element2;
|
||||
}
|
||||
|
||||
Elem(result, e, 2 * esize, sum.SubBigInteger(2 * esize - 1, 0));
|
||||
}
|
||||
|
||||
V(d, result);
|
||||
}
|
||||
|
||||
// usubw_advsimd.html
|
||||
public static void Usubw_V(bool Q, Bits size, Bits Rm, Bits Rn, Bits Rd)
|
||||
{
|
||||
|
|
|
@ -5,21 +5,19 @@
|
|||
|
||||
// https://alastairreid.github.io/asl-lexical-syntax/
|
||||
|
||||
// | ------------------------|----------------------------------- |
|
||||
// | ASL | C# |
|
||||
// | ------------------------|----------------------------------- |
|
||||
// | bit, bits(1); boolean | bool |
|
||||
// | bits | Bits |
|
||||
// | integer | BigInteger, int |
|
||||
// | real | decimal |
|
||||
// | ------------------------|----------------------------------- |
|
||||
// | '0'; FALSE | false |
|
||||
// | '1'; TRUE | true |
|
||||
// | '010' | "010" |
|
||||
// | bitsX IN {bitsY, bitsZ} | (bitsX == bitsY || bitsX == bitsZ) |
|
||||
// | DIV | / |
|
||||
// | MOD | % |
|
||||
// | ------------------------|----------------------------------- |
|
||||
// | ------------------------|-------------------------------- |
|
||||
// | ASL | C# |
|
||||
// | ------------------------|-------------------------------- |
|
||||
// | bit, bits(1); boolean | bool |
|
||||
// | bits | Bits |
|
||||
// | integer | BigInteger, int |
|
||||
// | real | decimal; double, float |
|
||||
// | ------------------------|-------------------------------- |
|
||||
// | '0'; FALSE | false |
|
||||
// | '1'; TRUE | true |
|
||||
// | '010' | "010" |
|
||||
// | DIV, MOD | /, % |
|
||||
// | ------------------------|-------------------------------- |
|
||||
|
||||
using System;
|
||||
using System.Numerics;
|
||||
|
@ -107,6 +105,7 @@ namespace Ryujinx.Tests.Cpu.Tester
|
|||
/* SP_EL1 = bits(64) UNKNOWN; */
|
||||
SP_EL1.SetAll(false);
|
||||
|
||||
FPCR.SetAll(false); // TODO: Add named fields.
|
||||
FPSR.SetAll(false); // TODO: Add named fields.
|
||||
}
|
||||
|
||||
|
@ -458,6 +457,7 @@ namespace Ryujinx.Tests.Cpu.Tester
|
|||
#endregion
|
||||
|
||||
#region "instrs/vector/reduce/reduceop/"
|
||||
// shared_pseudocode.html#impl-aarch64.Reduce.3
|
||||
public static Bits Reduce(ReduceOp op, Bits input, int esize)
|
||||
{
|
||||
int N = input.Count;
|
||||
|
@ -528,6 +528,7 @@ namespace Ryujinx.Tests.Cpu.Tester
|
|||
SP_EL0 = new Bits(64, false);
|
||||
SP_EL1 = new Bits(64, false);
|
||||
|
||||
FPCR = new Bits(32, false); // TODO: Add named fields.
|
||||
FPSR = new Bits(32, false); // TODO: Add named fields.
|
||||
|
||||
PSTATE.N = false;
|
||||
|
@ -817,6 +818,36 @@ namespace Ryujinx.Tests.Cpu.Tester
|
|||
return (decimal)value;
|
||||
}
|
||||
|
||||
/* */
|
||||
public static float Real_32(BigInteger value)
|
||||
{
|
||||
if (value == BigInteger.Pow((BigInteger)2.0f, 1000))
|
||||
{
|
||||
return float.PositiveInfinity;
|
||||
}
|
||||
if (value == -BigInteger.Pow((BigInteger)2.0f, 1000))
|
||||
{
|
||||
return float.NegativeInfinity;
|
||||
}
|
||||
|
||||
return (float)value;
|
||||
}
|
||||
|
||||
/* */
|
||||
public static double Real_64(BigInteger value)
|
||||
{
|
||||
if (value == BigInteger.Pow((BigInteger)2.0, 10000))
|
||||
{
|
||||
return double.PositiveInfinity;
|
||||
}
|
||||
if (value == -BigInteger.Pow((BigInteger)2.0, 10000))
|
||||
{
|
||||
return double.NegativeInfinity;
|
||||
}
|
||||
|
||||
return (double)value;
|
||||
}
|
||||
|
||||
// shared_pseudocode.html#impl-shared.ROR.2
|
||||
public static Bits ROR(Bits x, int shift)
|
||||
{
|
||||
|
@ -881,6 +912,36 @@ namespace Ryujinx.Tests.Cpu.Tester
|
|||
return (BigInteger)Decimal.Floor(x);
|
||||
}
|
||||
|
||||
/* */
|
||||
public static BigInteger RoundDown_32(float x)
|
||||
{
|
||||
if (float.IsPositiveInfinity(x))
|
||||
{
|
||||
return BigInteger.Pow((BigInteger)2.0f, 1000);
|
||||
}
|
||||
if (float.IsNegativeInfinity(x))
|
||||
{
|
||||
return -BigInteger.Pow((BigInteger)2.0f, 1000);
|
||||
}
|
||||
|
||||
return (BigInteger)MathF.Floor(x);
|
||||
}
|
||||
|
||||
/* */
|
||||
public static BigInteger RoundDown_64(double x)
|
||||
{
|
||||
if (double.IsPositiveInfinity(x))
|
||||
{
|
||||
return BigInteger.Pow((BigInteger)2.0, 10000);
|
||||
}
|
||||
if (double.IsNegativeInfinity(x))
|
||||
{
|
||||
return -BigInteger.Pow((BigInteger)2.0, 10000);
|
||||
}
|
||||
|
||||
return (BigInteger)Math.Floor(x);
|
||||
}
|
||||
|
||||
// shared_pseudocode.html#impl-shared.RoundTowardsZero.1
|
||||
public static BigInteger RoundTowardsZero(decimal x)
|
||||
{
|
||||
|
@ -1091,6 +1152,398 @@ namespace Ryujinx.Tests.Cpu.Tester
|
|||
}
|
||||
#endregion
|
||||
|
||||
#region "functions/float/fpdecoderounding/"
|
||||
/* shared_pseudocode.html#impl-shared.FPDecodeRounding.1 */
|
||||
public static FPRounding FPDecodeRounding(Bits rmode)
|
||||
{
|
||||
switch (rmode)
|
||||
{
|
||||
default:
|
||||
case Bits bits when bits == "00":
|
||||
return FPRounding.FPRounding_TIEEVEN; // N
|
||||
case Bits bits when bits == "01":
|
||||
return FPRounding.FPRounding_POSINF; // P
|
||||
case Bits bits when bits == "10":
|
||||
return FPRounding.FPRounding_NEGINF; // M
|
||||
case Bits bits when bits == "11":
|
||||
return FPRounding.FPRounding_ZERO; // Z
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region "functions/float/fpexc/"
|
||||
// shared_pseudocode.html#FPExc
|
||||
public enum FPExc {FPExc_InvalidOp, FPExc_DivideByZero, FPExc_Overflow,
|
||||
FPExc_Underflow, FPExc_Inexact, FPExc_InputDenorm};
|
||||
#endregion
|
||||
|
||||
#region "functions/float/fpprocessexception/"
|
||||
// shared_pseudocode.html#impl-shared.FPProcessException.2
|
||||
public static void FPProcessException(FPExc exception, Bits _fpcr)
|
||||
{
|
||||
Bits fpcr = new Bits(_fpcr); // Clone.
|
||||
|
||||
int cumul;
|
||||
|
||||
// Determine the cumulative exception bit number
|
||||
switch (exception)
|
||||
{
|
||||
default:
|
||||
case FPExc.FPExc_InvalidOp: cumul = 0; break;
|
||||
case FPExc.FPExc_DivideByZero: cumul = 1; break;
|
||||
case FPExc.FPExc_Overflow: cumul = 2; break;
|
||||
case FPExc.FPExc_Underflow: cumul = 3; break;
|
||||
case FPExc.FPExc_Inexact: cumul = 4; break;
|
||||
case FPExc.FPExc_InputDenorm: cumul = 7; break;
|
||||
}
|
||||
|
||||
int enable = cumul + 8;
|
||||
|
||||
if (fpcr[enable])
|
||||
{
|
||||
// Trapping of the exception enabled.
|
||||
// It is IMPLEMENTATION DEFINED whether the enable bit may be set at all, and
|
||||
// if so then how exceptions may be accumulated before calling FPTrapException()
|
||||
/* IMPLEMENTATION_DEFINED "floating-point trap handling"; */
|
||||
|
||||
throw new NotImplementedException();
|
||||
}/*
|
||||
else if (UsingAArch32())
|
||||
{
|
||||
// Set the cumulative exception bit
|
||||
FPSCR<cumul> = '1';
|
||||
}*/
|
||||
else
|
||||
{
|
||||
// Set the cumulative exception bit
|
||||
FPSR[cumul] = true;
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region "functions/float/fprounding/"
|
||||
// shared_pseudocode.html#FPRounding
|
||||
public enum FPRounding {FPRounding_TIEEVEN, FPRounding_POSINF,
|
||||
FPRounding_NEGINF, FPRounding_ZERO,
|
||||
FPRounding_TIEAWAY, FPRounding_ODD};
|
||||
#endregion
|
||||
|
||||
#region "functions/float/fptofixed/"
|
||||
/* shared_pseudocode.html#impl-shared.FPToFixed.5 */
|
||||
public static Bits FPToFixed(int M, Bits op, int fbits, bool unsigned, Bits _fpcr, FPRounding rounding)
|
||||
{
|
||||
int N = op.Count;
|
||||
|
||||
/* assert N IN {16,32,64}; */
|
||||
/* assert M IN {16,32,64}; */
|
||||
/* assert fbits >= 0; */
|
||||
/* assert rounding != FPRounding_ODD; */
|
||||
|
||||
Bits fpcr = new Bits(_fpcr); // Clone.
|
||||
|
||||
if (N == 16)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
else if (N == 32)
|
||||
{
|
||||
// Unpack using fpcr to determine if subnormals are flushed-to-zero
|
||||
(FPType type, bool sign, float value) = FPUnpack_32(op, fpcr);
|
||||
|
||||
// If NaN, set cumulative flag or take exception
|
||||
if (type == FPType.FPType_SNaN || type == FPType.FPType_QNaN)
|
||||
{
|
||||
FPProcessException(FPExc.FPExc_InvalidOp, fpcr);
|
||||
}
|
||||
|
||||
// Scale by fractional bits and produce integer rounded towards minus-infinity
|
||||
value = value * MathF.Pow(2.0f, fbits);
|
||||
BigInteger int_result = RoundDown_32(value);
|
||||
float error = value - Real_32(int_result);
|
||||
|
||||
if (float.IsNaN(error))
|
||||
{
|
||||
error = 0.0f;
|
||||
}
|
||||
|
||||
// Determine whether supplied rounding mode requires an increment
|
||||
bool round_up;
|
||||
|
||||
switch (rounding)
|
||||
{
|
||||
default:
|
||||
case FPRounding.FPRounding_TIEEVEN:
|
||||
round_up = (error > 0.5f || (error == 0.5f && int_result.SubBigInteger(0)));
|
||||
break;
|
||||
case FPRounding.FPRounding_POSINF:
|
||||
round_up = (error != 0.0f);
|
||||
break;
|
||||
case FPRounding.FPRounding_NEGINF:
|
||||
round_up = false;
|
||||
break;
|
||||
case FPRounding.FPRounding_ZERO:
|
||||
round_up = (error != 0.0f && int_result < (BigInteger)0);
|
||||
break;
|
||||
case FPRounding.FPRounding_TIEAWAY:
|
||||
round_up = (error > 0.5f || (error == 0.5f && int_result >= (BigInteger)0));
|
||||
break;
|
||||
}
|
||||
|
||||
if (round_up)
|
||||
{
|
||||
int_result = int_result + 1;
|
||||
}
|
||||
|
||||
// Generate saturated result and exceptions
|
||||
(Bits result, bool overflow) = SatQ(int_result, M, unsigned);
|
||||
|
||||
if (overflow)
|
||||
{
|
||||
FPProcessException(FPExc.FPExc_InvalidOp, fpcr);
|
||||
}
|
||||
else if (error != 0.0f)
|
||||
{
|
||||
FPProcessException(FPExc.FPExc_Inexact, fpcr);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
else /* if (N == 64) */
|
||||
{
|
||||
// Unpack using fpcr to determine if subnormals are flushed-to-zero
|
||||
(FPType type, bool sign, double value) = FPUnpack_64(op, fpcr);
|
||||
|
||||
// If NaN, set cumulative flag or take exception
|
||||
if (type == FPType.FPType_SNaN || type == FPType.FPType_QNaN)
|
||||
{
|
||||
FPProcessException(FPExc.FPExc_InvalidOp, fpcr);
|
||||
}
|
||||
|
||||
// Scale by fractional bits and produce integer rounded towards minus-infinity
|
||||
value = value * Math.Pow(2.0, fbits);
|
||||
BigInteger int_result = RoundDown_64(value);
|
||||
double error = value - Real_64(int_result);
|
||||
|
||||
if (double.IsNaN(error))
|
||||
{
|
||||
error = 0.0;
|
||||
}
|
||||
|
||||
// Determine whether supplied rounding mode requires an increment
|
||||
bool round_up;
|
||||
|
||||
switch (rounding)
|
||||
{
|
||||
default:
|
||||
case FPRounding.FPRounding_TIEEVEN:
|
||||
round_up = (error > 0.5 || (error == 0.5 && int_result.SubBigInteger(0)));
|
||||
break;
|
||||
case FPRounding.FPRounding_POSINF:
|
||||
round_up = (error != 0.0);
|
||||
break;
|
||||
case FPRounding.FPRounding_NEGINF:
|
||||
round_up = false;
|
||||
break;
|
||||
case FPRounding.FPRounding_ZERO:
|
||||
round_up = (error != 0.0 && int_result < (BigInteger)0);
|
||||
break;
|
||||
case FPRounding.FPRounding_TIEAWAY:
|
||||
round_up = (error > 0.5 || (error == 0.5 && int_result >= (BigInteger)0));
|
||||
break;
|
||||
}
|
||||
|
||||
if (round_up)
|
||||
{
|
||||
int_result = int_result + 1;
|
||||
}
|
||||
|
||||
// Generate saturated result and exceptions
|
||||
(Bits result, bool overflow) = SatQ(int_result, M, unsigned);
|
||||
|
||||
if (overflow)
|
||||
{
|
||||
FPProcessException(FPExc.FPExc_InvalidOp, fpcr);
|
||||
}
|
||||
else if (error != 0.0)
|
||||
{
|
||||
FPProcessException(FPExc.FPExc_Inexact, fpcr);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region "functions/float/fptype/"
|
||||
// shared_pseudocode.html#FPType
|
||||
public enum FPType {FPType_Nonzero, FPType_Zero, FPType_Infinity,
|
||||
FPType_QNaN, FPType_SNaN};
|
||||
#endregion
|
||||
|
||||
#region "functions/float/fpunpack/"
|
||||
/* shared_pseudocode.html#impl-shared.FPUnpack.2 */
|
||||
/* shared_pseudocode.html#impl-shared.FPUnpackBase.2 */
|
||||
/*public static (FPType, bool, real) FPUnpack_16(Bits fpval, Bits _fpcr)
|
||||
{
|
||||
int N = fpval.Count;
|
||||
|
||||
// assert N == 16;
|
||||
|
||||
Bits fpcr = new Bits(_fpcr); // Clone.
|
||||
|
||||
fpcr[26] = false;
|
||||
|
||||
return FPUnpackBase_16(fpval, fpcr);
|
||||
}*/
|
||||
public static (FPType, bool, float) FPUnpack_32(Bits fpval, Bits _fpcr)
|
||||
{
|
||||
int N = fpval.Count;
|
||||
|
||||
/* assert N == 32; */
|
||||
|
||||
Bits fpcr = new Bits(_fpcr); // Clone.
|
||||
|
||||
FPType type;
|
||||
float value;
|
||||
|
||||
bool sign = fpval[31];
|
||||
Bits exp32 = fpval[30, 23];
|
||||
Bits frac32 = fpval[22, 0];
|
||||
|
||||
if (IsZero(exp32))
|
||||
{
|
||||
// Produce zero if value is zero or flush-to-zero is selected.
|
||||
if (IsZero(frac32) || fpcr[24])
|
||||
{
|
||||
type = FPType.FPType_Zero;
|
||||
value = 0.0f;
|
||||
|
||||
// Denormalized input flushed to zero
|
||||
if (!IsZero(frac32))
|
||||
{
|
||||
FPProcessException(FPExc.FPExc_InputDenorm, fpcr);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
type = FPType.FPType_Nonzero;
|
||||
value = MathF.Pow(2.0f, -126) * (Real_32(UInt(frac32)) * MathF.Pow(2.0f, -23));
|
||||
}
|
||||
}
|
||||
else if (IsOnes(exp32))
|
||||
{
|
||||
if (IsZero(frac32))
|
||||
{
|
||||
type = FPType.FPType_Infinity;
|
||||
/* value = 2.0^1000000; */
|
||||
value = MathF.Pow(2.0f, 1000);
|
||||
}
|
||||
else
|
||||
{
|
||||
type = frac32[22] ? FPType.FPType_QNaN : FPType.FPType_SNaN;
|
||||
value = 0.0f;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
type = FPType.FPType_Nonzero;
|
||||
value = MathF.Pow(2.0f, (int)UInt(exp32) - 127) * (1.0f + Real_32(UInt(frac32)) * MathF.Pow(2.0f, -23));
|
||||
}
|
||||
|
||||
if (sign)
|
||||
{
|
||||
value = -value;
|
||||
}
|
||||
|
||||
return (type, sign, value);
|
||||
}
|
||||
public static (FPType, bool, double) FPUnpack_64(Bits fpval, Bits _fpcr)
|
||||
{
|
||||
int N = fpval.Count;
|
||||
|
||||
/* assert N == 64; */
|
||||
|
||||
Bits fpcr = new Bits(_fpcr); // Clone.
|
||||
|
||||
FPType type;
|
||||
double value;
|
||||
|
||||
bool sign = fpval[63];
|
||||
Bits exp64 = fpval[62, 52];
|
||||
Bits frac64 = fpval[51, 0];
|
||||
|
||||
if (IsZero(exp64))
|
||||
{
|
||||
// Produce zero if value is zero or flush-to-zero is selected.
|
||||
if (IsZero(frac64) || fpcr[24])
|
||||
{
|
||||
type = FPType.FPType_Zero;
|
||||
value = 0.0;
|
||||
|
||||
// Denormalized input flushed to zero
|
||||
if (!IsZero(frac64))
|
||||
{
|
||||
FPProcessException(FPExc.FPExc_InputDenorm, fpcr);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
type = FPType.FPType_Nonzero;
|
||||
value = Math.Pow(2.0, -1022) * (Real_64(UInt(frac64)) * Math.Pow(2.0, -52));
|
||||
}
|
||||
}
|
||||
else if (IsOnes(exp64))
|
||||
{
|
||||
if (IsZero(frac64))
|
||||
{
|
||||
type = FPType.FPType_Infinity;
|
||||
/* value = 2.0^1000000; */
|
||||
value = Math.Pow(2.0, 10000);
|
||||
}
|
||||
else
|
||||
{
|
||||
type = frac64[51] ? FPType.FPType_QNaN : FPType.FPType_SNaN;
|
||||
value = 0.0;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
type = FPType.FPType_Nonzero;
|
||||
value = Math.Pow(2.0, (int)UInt(exp64) - 1023) * (1.0 + Real_64(UInt(frac64)) * Math.Pow(2.0, -52));
|
||||
}
|
||||
|
||||
if (sign)
|
||||
{
|
||||
value = -value;
|
||||
}
|
||||
|
||||
return (type, sign, value);
|
||||
}
|
||||
|
||||
/* shared_pseudocode.html#impl-shared.FPUnpackCV.2 */
|
||||
/* shared_pseudocode.html#impl-shared.FPUnpackBase.2 */
|
||||
/*public static (FPType, bool, real) FPUnpackCV_16(Bits fpval, Bits _fpcr)
|
||||
{
|
||||
int N = fpval.Count;
|
||||
|
||||
// assert N == 16;
|
||||
|
||||
Bits fpcr = new Bits(_fpcr); // Clone.
|
||||
|
||||
fpcr[19] = false;
|
||||
|
||||
return FPUnpackBase_16(fpval, fpcr);
|
||||
}*/
|
||||
public static (FPType, bool, float) FPUnpackCV_32(Bits fpval, Bits _fpcr)
|
||||
{
|
||||
return FPUnpack_32(fpval, _fpcr);
|
||||
}
|
||||
public static (FPType, bool, double) FPUnpackCV_64(Bits fpval, Bits _fpcr)
|
||||
{
|
||||
return FPUnpack_64(fpval, _fpcr);
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region "functions/integer/"
|
||||
/* shared_pseudocode.html#impl-shared.AddWithCarry.3 */
|
||||
public static (Bits, Bits) AddWithCarry(int N, Bits x, Bits y, bool carry_in)
|
||||
|
@ -1117,7 +1570,12 @@ namespace Ryujinx.Tests.Cpu.Tester
|
|||
public static Bits SP_EL0;
|
||||
public static Bits SP_EL1;
|
||||
|
||||
public static Bits FPCR; // TODO: Add named fields.
|
||||
// [ 31 | 30 | 29 | 28 | 27 | 26 | 25 | 24 | 23 22 | 21 20 | 19 | 18 17 16 | 15 | 14 | 13 | 12 | 11 | 10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 ]
|
||||
// [ 0 | 0 | 0 | 0 | 0 | AHP | DN | FZ | RMode | Stride | FZ16 | Len | IDE | 0 | 0 | IXE | UFE | OFE | DZE | IOE | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 ]
|
||||
public static Bits FPSR; // TODO: Add named fields.
|
||||
// [ 31 | 30 | 29 | 28 | 27 | 26 | 25 | 24 | 23 | 22 | 21 | 20 | 19 | 18 | 17 | 16 | 15 | 14 | 13 | 12 | 11 | 10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 ]
|
||||
// [ N | Z | C | V | QC | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | IDC | 0 | 0 | IXC | UFC | OFC | DZC | IOC ]
|
||||
#endregion
|
||||
|
||||
#region "functions/system/"
|
||||
|
@ -1178,6 +1636,8 @@ namespace Ryujinx.Tests.Cpu.Tester
|
|||
/* shared_pseudocode.html#impl-shared.HaveEL.1 */
|
||||
public static bool HaveEL(Bits el)
|
||||
{
|
||||
// TODO: Implement ASL: "IN" as C#: "Bits.In()".
|
||||
/* if el IN {EL1,EL0} then */
|
||||
if (el == EL1 || el == EL0)
|
||||
{
|
||||
return true; // EL1 and EL0 must exist
|
||||
|
|
|
@ -5,6 +5,10 @@
|
|||
<RuntimeIdentifiers>win10-x64;osx-x64;linux-x64</RuntimeIdentifiers>
|
||||
<OutputType>Exe</OutputType>
|
||||
<IsPackable>false</IsPackable>
|
||||
|
||||
<TargetOS Condition="'$([System.Runtime.InteropServices.RuntimeInformation]::IsOSPlatform($([System.Runtime.InteropServices.OSPlatform]::Windows)))' == 'true'">windows</TargetOS>
|
||||
<TargetOS Condition="'$([System.Runtime.InteropServices.RuntimeInformation]::IsOSPlatform($([System.Runtime.InteropServices.OSPlatform]::OSX)))' == 'true'">osx</TargetOS>
|
||||
<TargetOS Condition="'$([System.Runtime.InteropServices.RuntimeInformation]::IsOSPlatform($([System.Runtime.InteropServices.OSPlatform]::Linux)))' == 'true'">linux</TargetOS>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
|
@ -20,6 +24,13 @@
|
|||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\ChocolArm64\ChocolArm64.csproj" />
|
||||
<ProjectReference Include="..\Ryujinx.Tests.Unicorn\Ryujinx.Tests.Unicorn.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<Target Name="CopyUnicorn" AfterTargets="Build">
|
||||
<Copy SourceFiles="..\Ryujinx.Tests.Unicorn\libs\$(TargetOS)\unicorn.dll" DestinationFolder="$(OutputPath)" ContinueOnError="true" />
|
||||
<Copy SourceFiles="..\Ryujinx.Tests.Unicorn\libs\$(TargetOS)\libunicorn.dylib" DestinationFolder="$(OutputPath)" ContinueOnError="true" />
|
||||
<Copy SourceFiles="..\Ryujinx.Tests.Unicorn\libs\$(TargetOS)\libunicorn.so" DestinationFolder="$(OutputPath)" ContinueOnError="true" />
|
||||
</Target>
|
||||
|
||||
</Project>
|
||||
|
|
Loading…
Add table
Reference in a new issue