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