Compare commits

..

14 Commits

Author SHA1 Message Date
62585755fd Do not clear the rejit queue when overlaps count is equal to 0. (#3721)
* Do not clear the rejit queue when overlaps count is equal to 0.

* Ptc and PtcProfiler must be invalidated.

* Revert "Ptc and PtcProfiler must be invalidated."

This reverts commit f5b0ad9d7d.

* Fix #3710 slow path due to #3701.
2022-10-19 02:08:34 +00:00
56621615b1 Implement the GetSessionCacheMode in SSL servuce (#3735) 2022-10-19 01:27:11 +00:00
2099a3e84b Manage state of NfcManager (#3678)
* Manage state of NfcManager

Very basic state management but works with Hyrule Warriors Definitive Edition. Partially fixes #2122

* Fixes changes from review
2022-10-19 01:14:31 +00:00
7d26e4ac7b Fix mapping leaks caused by UnmapView not working on Linux (#3650)
* Add test for UnmapView mapping leaks

* Throw when UnmapView fails on Linux

* Fix UnmapView

* Remove throw
2022-10-19 01:02:45 +00:00
8d41402fa6 A32: Implement VCVTT, VCVTB (#3710)
* A32: Implement VCVTT, VCVTB

* A32: F16C implementation of VCVTT/VCVTB
2022-10-19 02:36:04 +02:00
5af8ce7c38 A64: Add fast path for Fcvtas_Gp/S/V, Fcvtau_Gp/S/V and Frinta_S/V in… (#3712)
* A64: Add fast path for Fcvtas_Gp/S/V, Fcvtau_Gp/S/V and Frinta_S/V instructions;

they use "Round to Nearest with Ties to Away" rounding mode not supported in x86.

All instructions involved have been tested locally in both release and debug modes, in both lowcq and highcq.

The titles Mario Strikers and Super Smash Bros. U. use these instructions intensively.

* Update Ptc.cs

* A32: Add fast path for Vcvta_RM, Vrinta_RM and Vrinta_V instructions aswell.
2022-10-19 00:21:33 +00:00
77c4291c34 Avalonia: Update Polish Translation (#3722)
* Add new string

You need the period there otherwise it could be read as "Głoś" -> "Preach"

* Update MainWindow.axaml

Updating to bring it in line with the other languages naming themselves in their respective languages

* Update pl_PL.json

realizing that period isn't necessary considering the string's usage (which to be fair, I should have checked when I added it)

* Update pl_PL.json

* Add Updater Message
2022-10-19 00:10:28 +00:00
6e92b7a378 Dispose Vulkan TextureStorage when views hit 0 instead of immediately (#3738)
Due to the `using` statement being scoped to the `CreateTextureView` method, `TextureStorage` would be disposed as soon as the view was returned.

This was largely fine as the TextureStorage resources were being kept alive by the views holding their own references to them, but it also meant that dispose is only called as soon as the texture is created.

Aliased Storages are TextureStorages created with the same allocation as another TextureStorage, if they have to be aliased as another format. We keep track of a TextureStorage's `_aliasedStorages` as they are created, and dispose them when the TextureStorage is disposed...

...except it is disposed immediately, before any aliased storages are even created. The aliased storages added after this will never be disposed.

This PR attempts to fix this by disposing TextureStorage when its view count reaches 0. The other use of texture storage - the D32S8 blit - still manually disposes the storage, but regular uses created via the GAL are now disposed by the view count.

I think this makes the most sense, as otherwise in the future this behaviour might be forgotton and more things could be added to the Dispose() method that don't work due to it not actually calling at the right time.

This should improve memory leaks in Super Mario Odyssey, most noticeable when resolution scaling. The memory usage of the game is still wildly unpredictable due to how it interacts with the texture cache, but now it shouldn't get considerably longer as you play... I hope. I've seen it typically recover back to the same level occasionally, though it can spike significantly.

Please test a bunch of games on multiple GPUs to make sure this doesn't break anything.
2022-10-18 23:52:08 +00:00
9b852c7481 Fix: Arguments Break when Updating (#3744)
* Wrap Args in quotes

-Wrap args in quotes to allow for spaces in dir paths when restarting Ryujinxs from Update.

* Wrap second instance of GetCommandLineArgs()

* Changed ryuArgs from string to string[]

* Update Ryujinx.Ava/Modules/Updater/Updater.cs

Co-authored-by: mageven <62494521+mageven@users.noreply.github.com>

* Update UpdateDialog.cs

Co-authored-by: mageven <62494521+mageven@users.noreply.github.com>
2022-10-18 23:41:16 +00:00
c40c3905e2 Avoid allocations in .Parse methods (#3760)
* Avoid allocations in .Parse methods

Use the Span overloads of the Parse methods when possible to avoid string allocations and remove one unnecessarry array allocation

* Avoid another string allocation
2022-10-18 23:31:34 +00:00
a6cd044f0f Vulkan: Fix blit levels/layers parameters being inverted (#3768) 2022-10-18 10:13:44 +02:00
f5a1de6ac5 Fix kernel VA allocation when random allocation fails (#3755)
* Fix kernel VA allocation when random allocation fails

* This was off by one
2022-10-17 22:12:49 +00:00
2aeb5b00e3 Update README.md (#3767)
Update compatibility numbers
2022-10-17 23:58:11 +02:00
60ba7b71f2 remove property changed call in time zone validation (#3752) 2022-10-17 16:48:14 +00:00
33 changed files with 566 additions and 83 deletions

View File

@ -0,0 +1,44 @@
namespace ARMeilleure.Decoders
{
class OpCode32SimdCvtTB : OpCode32, IOpCode32Simd
{
public int Vd { get; }
public int Vm { get; }
public bool Op { get; } // Convert to Half / Convert from Half
public bool T { get; } // Top / Bottom
public int Size { get; } // Double / Single
public new static OpCode Create(InstDescriptor inst, ulong address, int opCode) => new OpCode32SimdCvtTB(inst, address, opCode, false);
public static OpCode CreateT32(InstDescriptor inst, ulong address, int opCode) => new OpCode32SimdCvtTB(inst, address, opCode, true);
public OpCode32SimdCvtTB(InstDescriptor inst, ulong address, int opCode, bool isThumb) : base(inst, address, opCode)
{
IsThumb = isThumb;
Op = ((opCode >> 16) & 0x1) != 0;
T = ((opCode >> 7) & 0x1) != 0;
Size = ((opCode >> 8) & 0x1);
RegisterSize = Size == 1 ? RegisterSize.Int64 : RegisterSize.Int32;
if (Size == 1)
{
if (Op)
{
Vm = ((opCode >> 1) & 0x10) | ((opCode >> 0) & 0xf);
Vd = ((opCode >> 22) & 0x1) | ((opCode >> 11) & 0x1e);
}
else
{
Vm = ((opCode >> 5) & 0x1) | ((opCode << 1) & 0x1e);
Vd = ((opCode >> 18) & 0x10) | ((opCode >> 12) & 0xf);
}
}
else
{
Vm = ((opCode >> 5) & 0x1) | ((opCode << 1) & 0x1e);
Vd = ((opCode >> 22) & 0x1) | ((opCode >> 11) & 0x1e);
}
}
}
}

View File

@ -828,6 +828,7 @@ namespace ARMeilleure.Decoders
SetVfp("<<<<11101x11110xxxxx101x11x0xxxx", InstName.Vcvt, InstEmit32.Vcvt_FI, OpCode32SimdCvtFI.Create, OpCode32SimdCvtFI.CreateT32); // FP32 to int.
SetVfp("<<<<11101x111000xxxx101xx1x0xxxx", InstName.Vcvt, InstEmit32.Vcvt_FI, OpCode32SimdCvtFI.Create, OpCode32SimdCvtFI.CreateT32); // Int to FP32.
SetVfp("111111101x1111xxxxxx101xx1x0xxxx", InstName.Vcvt, InstEmit32.Vcvt_RM, OpCode32SimdCvtFI.Create, OpCode32SimdCvtFI.CreateT32); // The many FP32 to int encodings (fp).
SetVfp("<<<<11101x11001xxxxx101xx1x0xxxx", InstName.Vcvt, InstEmit32.Vcvt_TB, OpCode32SimdCvtTB.Create, OpCode32SimdCvtTB.CreateT32);
SetVfp("<<<<11101x00xxxxxxxx101xx0x0xxxx", InstName.Vdiv, InstEmit32.Vdiv_S, OpCode32SimdRegS.Create, OpCode32SimdRegS.CreateT32);
SetVfp("<<<<11101xx0xxxxxxxx1011x0x10000", InstName.Vdup, InstEmit32.Vdup, OpCode32SimdDupGP.Create, OpCode32SimdDupGP.CreateT32);
SetVfp("<<<<11101x10xxxxxxxx101xx0x0xxxx", InstName.Vfma, InstEmit32.Vfma_S, OpCode32SimdRegS.Create, OpCode32SimdRegS.CreateT32);

View File

@ -1617,18 +1617,32 @@ namespace ARMeilleure.Instructions
public static void Frinta_S(ArmEmitterContext context)
{
EmitScalarUnaryOpF(context, (op1) =>
if (Optimizations.UseSse41)
{
return EmitRoundMathCall(context, MidpointRounding.AwayFromZero, op1);
});
EmitSse41ScalarRoundOpF(context, FPRoundingMode.ToNearestAway);
}
else
{
EmitScalarUnaryOpF(context, (op1) =>
{
return EmitRoundMathCall(context, MidpointRounding.AwayFromZero, op1);
});
}
}
public static void Frinta_V(ArmEmitterContext context)
{
EmitVectorUnaryOpF(context, (op1) =>
if (Optimizations.UseSse41)
{
return EmitRoundMathCall(context, MidpointRounding.AwayFromZero, op1);
});
EmitSse41VectorRoundOpF(context, FPRoundingMode.ToNearestAway);
}
else
{
EmitVectorUnaryOpF(context, (op1) =>
{
return EmitRoundMathCall(context, MidpointRounding.AwayFromZero, op1);
});
}
}
public static void Frinti_S(ArmEmitterContext context)
@ -3516,9 +3530,18 @@ namespace ARMeilleure.Instructions
Operand n = GetVec(op.Rn);
Intrinsic inst = (op.Size & 1) != 0 ? Intrinsic.X86Roundsd : Intrinsic.X86Roundss;
Operand res;
Operand res = context.AddIntrinsic(inst, n, Const(X86GetRoundControl(roundMode)));
if (roundMode != FPRoundingMode.ToNearestAway)
{
Intrinsic inst = (op.Size & 1) != 0 ? Intrinsic.X86Roundsd : Intrinsic.X86Roundss;
res = context.AddIntrinsic(inst, n, Const(X86GetRoundControl(roundMode)));
}
else
{
res = EmitSse41RoundToNearestWithTiesToAwayOpF(context, n, scalar: true);
}
if ((op.Size & 1) != 0)
{
@ -3538,9 +3561,18 @@ namespace ARMeilleure.Instructions
Operand n = GetVec(op.Rn);
Intrinsic inst = (op.Size & 1) != 0 ? Intrinsic.X86Roundpd : Intrinsic.X86Roundps;
Operand res;
Operand res = context.AddIntrinsic(inst, n, Const(X86GetRoundControl(roundMode)));
if (roundMode != FPRoundingMode.ToNearestAway)
{
Intrinsic inst = (op.Size & 1) != 0 ? Intrinsic.X86Roundpd : Intrinsic.X86Roundps;
res = context.AddIntrinsic(inst, n, Const(X86GetRoundControl(roundMode)));
}
else
{
res = EmitSse41RoundToNearestWithTiesToAwayOpF(context, n, scalar: false);
}
if (op.RegisterSize == RegisterSize.Simd64)
{

View File

@ -164,32 +164,74 @@ namespace ARMeilleure.Instructions
public static void Fcvtas_Gp(ArmEmitterContext context)
{
EmitFcvt_s_Gp(context, (op1) => EmitRoundMathCall(context, MidpointRounding.AwayFromZero, op1));
if (Optimizations.UseSse41)
{
EmitSse41Fcvts_Gp(context, FPRoundingMode.ToNearestAway, isFixed: false);
}
else
{
EmitFcvt_s_Gp(context, (op1) => EmitRoundMathCall(context, MidpointRounding.AwayFromZero, op1));
}
}
public static void Fcvtas_S(ArmEmitterContext context)
{
EmitFcvt(context, (op1) => EmitRoundMathCall(context, MidpointRounding.AwayFromZero, op1), signed: true, scalar: true);
if (Optimizations.UseSse41)
{
EmitSse41FcvtsOpF(context, FPRoundingMode.ToNearestAway, scalar: true);
}
else
{
EmitFcvt(context, (op1) => EmitRoundMathCall(context, MidpointRounding.AwayFromZero, op1), signed: true, scalar: true);
}
}
public static void Fcvtas_V(ArmEmitterContext context)
{
EmitFcvt(context, (op1) => EmitRoundMathCall(context, MidpointRounding.AwayFromZero, op1), signed: true, scalar: false);
if (Optimizations.UseSse41)
{
EmitSse41FcvtsOpF(context, FPRoundingMode.ToNearestAway, scalar: false);
}
else
{
EmitFcvt(context, (op1) => EmitRoundMathCall(context, MidpointRounding.AwayFromZero, op1), signed: true, scalar: false);
}
}
public static void Fcvtau_Gp(ArmEmitterContext context)
{
EmitFcvt_u_Gp(context, (op1) => EmitRoundMathCall(context, MidpointRounding.AwayFromZero, op1));
if (Optimizations.UseSse41)
{
EmitSse41Fcvtu_Gp(context, FPRoundingMode.ToNearestAway, isFixed: false);
}
else
{
EmitFcvt_u_Gp(context, (op1) => EmitRoundMathCall(context, MidpointRounding.AwayFromZero, op1));
}
}
public static void Fcvtau_S(ArmEmitterContext context)
{
EmitFcvt(context, (op1) => EmitRoundMathCall(context, MidpointRounding.AwayFromZero, op1), signed: false, scalar: true);
if (Optimizations.UseSse41)
{
EmitSse41FcvtuOpF(context, FPRoundingMode.ToNearestAway, scalar: true);
}
else
{
EmitFcvt(context, (op1) => EmitRoundMathCall(context, MidpointRounding.AwayFromZero, op1), signed: false, scalar: true);
}
}
public static void Fcvtau_V(ArmEmitterContext context)
{
EmitFcvt(context, (op1) => EmitRoundMathCall(context, MidpointRounding.AwayFromZero, op1), signed: false, scalar: false);
if (Optimizations.UseSse41)
{
EmitSse41FcvtuOpF(context, FPRoundingMode.ToNearestAway, scalar: false);
}
else
{
EmitFcvt(context, (op1) => EmitRoundMathCall(context, MidpointRounding.AwayFromZero, op1), signed: false, scalar: false);
}
}
public static void Fcvtl_V(ArmEmitterContext context)
@ -1223,7 +1265,14 @@ namespace ARMeilleure.Instructions
nRes = context.AddIntrinsic(Intrinsic.X86Mulps, nRes, fpScaledMask);
}
nRes = context.AddIntrinsic(Intrinsic.X86Roundps, nRes, Const(X86GetRoundControl(roundMode)));
if (roundMode != FPRoundingMode.ToNearestAway)
{
nRes = context.AddIntrinsic(Intrinsic.X86Roundps, nRes, Const(X86GetRoundControl(roundMode)));
}
else
{
nRes = EmitSse41RoundToNearestWithTiesToAwayOpF(context, nRes, scalar);
}
Operand nInt = context.AddIntrinsic(Intrinsic.X86Cvtps2dq, nRes);
@ -1265,7 +1314,14 @@ namespace ARMeilleure.Instructions
nRes = context.AddIntrinsic(Intrinsic.X86Mulpd, nRes, fpScaledMask);
}
nRes = context.AddIntrinsic(Intrinsic.X86Roundpd, nRes, Const(X86GetRoundControl(roundMode)));
if (roundMode != FPRoundingMode.ToNearestAway)
{
nRes = context.AddIntrinsic(Intrinsic.X86Roundpd, nRes, Const(X86GetRoundControl(roundMode)));
}
else
{
nRes = EmitSse41RoundToNearestWithTiesToAwayOpF(context, nRes, scalar);
}
Operand nLong = EmitSse2CvtDoubleToInt64OpF(context, nRes, scalar);
@ -1314,7 +1370,14 @@ namespace ARMeilleure.Instructions
nRes = context.AddIntrinsic(Intrinsic.X86Mulps, nRes, fpScaledMask);
}
nRes = context.AddIntrinsic(Intrinsic.X86Roundps, nRes, Const(X86GetRoundControl(roundMode)));
if (roundMode != FPRoundingMode.ToNearestAway)
{
nRes = context.AddIntrinsic(Intrinsic.X86Roundps, nRes, Const(X86GetRoundControl(roundMode)));
}
else
{
nRes = EmitSse41RoundToNearestWithTiesToAwayOpF(context, nRes, scalar);
}
Operand zero = context.VectorZero();
@ -1369,7 +1432,14 @@ namespace ARMeilleure.Instructions
nRes = context.AddIntrinsic(Intrinsic.X86Mulpd, nRes, fpScaledMask);
}
nRes = context.AddIntrinsic(Intrinsic.X86Roundpd, nRes, Const(X86GetRoundControl(roundMode)));
if (roundMode != FPRoundingMode.ToNearestAway)
{
nRes = context.AddIntrinsic(Intrinsic.X86Roundpd, nRes, Const(X86GetRoundControl(roundMode)));
}
else
{
nRes = EmitSse41RoundToNearestWithTiesToAwayOpF(context, nRes, scalar);
}
Operand zero = context.VectorZero();
@ -1424,7 +1494,14 @@ namespace ARMeilleure.Instructions
nRes = context.AddIntrinsic(Intrinsic.X86Mulss, nRes, fpScaledMask);
}
nRes = context.AddIntrinsic(Intrinsic.X86Roundss, nRes, Const(X86GetRoundControl(roundMode)));
if (roundMode != FPRoundingMode.ToNearestAway)
{
nRes = context.AddIntrinsic(Intrinsic.X86Roundss, nRes, Const(X86GetRoundControl(roundMode)));
}
else
{
nRes = EmitSse41RoundToNearestWithTiesToAwayOpF(context, nRes, scalar: true);
}
Operand nIntOrLong = op.RegisterSize == RegisterSize.Int32
? context.AddIntrinsicInt (Intrinsic.X86Cvtss2si, nRes)
@ -1464,7 +1541,14 @@ namespace ARMeilleure.Instructions
nRes = context.AddIntrinsic(Intrinsic.X86Mulsd, nRes, fpScaledMask);
}
nRes = context.AddIntrinsic(Intrinsic.X86Roundsd, nRes, Const(X86GetRoundControl(roundMode)));
if (roundMode != FPRoundingMode.ToNearestAway)
{
nRes = context.AddIntrinsic(Intrinsic.X86Roundsd, nRes, Const(X86GetRoundControl(roundMode)));
}
else
{
nRes = EmitSse41RoundToNearestWithTiesToAwayOpF(context, nRes, scalar: true);
}
Operand nIntOrLong = op.RegisterSize == RegisterSize.Int32
? context.AddIntrinsicInt (Intrinsic.X86Cvtsd2si, nRes)
@ -1512,7 +1596,14 @@ namespace ARMeilleure.Instructions
nRes = context.AddIntrinsic(Intrinsic.X86Mulss, nRes, fpScaledMask);
}
nRes = context.AddIntrinsic(Intrinsic.X86Roundss, nRes, Const(X86GetRoundControl(roundMode)));
if (roundMode != FPRoundingMode.ToNearestAway)
{
nRes = context.AddIntrinsic(Intrinsic.X86Roundss, nRes, Const(X86GetRoundControl(roundMode)));
}
else
{
nRes = EmitSse41RoundToNearestWithTiesToAwayOpF(context, nRes, scalar: true);
}
Operand zero = context.VectorZero();
@ -1567,7 +1658,14 @@ namespace ARMeilleure.Instructions
nRes = context.AddIntrinsic(Intrinsic.X86Mulsd, nRes, fpScaledMask);
}
nRes = context.AddIntrinsic(Intrinsic.X86Roundsd, nRes, Const(X86GetRoundControl(roundMode)));
if (roundMode != FPRoundingMode.ToNearestAway)
{
nRes = context.AddIntrinsic(Intrinsic.X86Roundsd, nRes, Const(X86GetRoundControl(roundMode)));
}
else
{
nRes = EmitSse41RoundToNearestWithTiesToAwayOpF(context, nRes, scalar: true);
}
Operand zero = context.VectorZero();

View File

@ -203,6 +203,9 @@ namespace ARMeilleure.Instructions
FPRoundingMode roundMode;
switch (rm)
{
case 0b00:
roundMode = FPRoundingMode.ToNearestAway;
break;
case 0b01:
roundMode = FPRoundingMode.ToNearest;
break;
@ -228,7 +231,7 @@ namespace ARMeilleure.Instructions
bool unsigned = op.Opc == 0;
int rm = op.Opc2 & 3;
if (Optimizations.UseSse41 && rm != 0b00)
if (Optimizations.UseSse41)
{
EmitSse41ConvertInt32(context, RMToRoundMode(rm), !unsigned);
}
@ -258,6 +261,74 @@ namespace ARMeilleure.Instructions
}
}
public static void Vcvt_TB(ArmEmitterContext context)
{
OpCode32SimdCvtTB op = (OpCode32SimdCvtTB)context.CurrOp;
if (Optimizations.UseF16c)
{
Debug.Assert(!Optimizations.ForceLegacySse);
if (op.Op)
{
Operand res = ExtractScalar(context, op.Size == 1 ? OperandType.FP64 : OperandType.FP32, op.Vm);
if (op.Size == 1)
{
res = context.AddIntrinsic(Intrinsic.X86Cvtsd2ss, context.VectorZero(), res);
}
res = context.AddIntrinsic(Intrinsic.X86Vcvtps2ph, res, Const(X86GetRoundControl(FPRoundingMode.ToNearest)));
res = context.VectorExtract16(res, 0);
InsertScalar16(context, op.Vd, op.T, res);
}
else
{
Operand res = context.VectorCreateScalar(ExtractScalar16(context, op.Vm, op.T));
res = context.AddIntrinsic(Intrinsic.X86Vcvtph2ps, res);
if (op.Size == 1)
{
res = context.AddIntrinsic(Intrinsic.X86Cvtss2sd, context.VectorZero(), res);
}
res = context.VectorExtract(op.Size == 1 ? OperandType.I64 : OperandType.I32, res, 0);
InsertScalar(context, op.Vd, res);
}
}
else
{
if (op.Op)
{
// Convert to half.
Operand src = ExtractScalar(context, op.Size == 1 ? OperandType.FP64 : OperandType.FP32, op.Vm);
MethodInfo method = op.Size == 1
? typeof(SoftFloat64_16).GetMethod(nameof(SoftFloat64_16.FPConvert))
: typeof(SoftFloat32_16).GetMethod(nameof(SoftFloat32_16.FPConvert));
context.StoreToContext();
Operand res = context.Call(method, src);
context.LoadFromContext();
InsertScalar16(context, op.Vd, op.T, res);
}
else
{
// Convert from half.
Operand src = ExtractScalar16(context, op.Vm, op.T);
MethodInfo method = op.Size == 1
? typeof(SoftFloat16_64).GetMethod(nameof(SoftFloat16_64.FPConvert))
: typeof(SoftFloat16_32).GetMethod(nameof(SoftFloat16_32.FPConvert));
context.StoreToContext();
Operand res = context.Call(method, src);
context.LoadFromContext();
InsertScalar(context, op.Vd, res);
}
}
}
// VRINTA/M/N/P (floating-point).
public static void Vrint_RM(ArmEmitterContext context)
{
@ -267,15 +338,21 @@ namespace ARMeilleure.Instructions
int rm = op.Opc2 & 3;
if (Optimizations.UseSse2 && rm != 0b00)
if (Optimizations.UseSse41)
{
EmitScalarUnaryOpSimd32(context, (m) =>
{
Intrinsic inst = (op.Size & 1) == 0 ? Intrinsic.X86Roundss : Intrinsic.X86Roundsd;
FPRoundingMode roundMode = RMToRoundMode(rm);
return context.AddIntrinsic(inst, m, Const(X86GetRoundControl(roundMode)));
if (roundMode != FPRoundingMode.ToNearestAway)
{
Intrinsic inst = (op.Size & 1) == 0 ? Intrinsic.X86Roundss : Intrinsic.X86Roundsd;
return context.AddIntrinsic(inst, m, Const(X86GetRoundControl(roundMode)));
}
else
{
return EmitSse41RoundToNearestWithTiesToAwayOpF(context, m, scalar: true);
}
});
}
else
@ -305,7 +382,17 @@ namespace ARMeilleure.Instructions
// VRINTA (vector).
public static void Vrinta_V(ArmEmitterContext context)
{
EmitVectorUnaryOpF32(context, (m) => EmitRoundMathCall(context, MidpointRounding.AwayFromZero, m));
if (Optimizations.UseSse41)
{
EmitVectorUnaryOpSimd32(context, (m) =>
{
return EmitSse41RoundToNearestWithTiesToAwayOpF(context, m, scalar: false);
});
}
else
{
EmitVectorUnaryOpF32(context, (m) => EmitRoundMathCall(context, MidpointRounding.AwayFromZero, m));
}
}
// VRINTM (vector).
@ -413,7 +500,14 @@ namespace ARMeilleure.Instructions
Operand nRes = context.AddIntrinsic(Intrinsic.X86Cmpss, n, n, Const((int)CmpCondition.OrderedQ));
nRes = context.AddIntrinsic(Intrinsic.X86Pand, nRes, n);
nRes = context.AddIntrinsic(Intrinsic.X86Roundss, nRes, Const(X86GetRoundControl(roundMode)));
if (roundMode != FPRoundingMode.ToNearestAway)
{
nRes = context.AddIntrinsic(Intrinsic.X86Roundss, nRes, Const(X86GetRoundControl(roundMode)));
}
else
{
nRes = EmitSse41RoundToNearestWithTiesToAwayOpF(context, nRes, scalar: true);
}
Operand zero = context.VectorZero();
@ -464,7 +558,14 @@ namespace ARMeilleure.Instructions
Operand nRes = context.AddIntrinsic(Intrinsic.X86Cmpsd, n, n, Const((int)CmpCondition.OrderedQ));
nRes = context.AddIntrinsic(Intrinsic.X86Pand, nRes, n);
nRes = context.AddIntrinsic(Intrinsic.X86Roundsd, nRes, Const(X86GetRoundControl(roundMode)));
if (roundMode != FPRoundingMode.ToNearestAway)
{
nRes = context.AddIntrinsic(Intrinsic.X86Roundsd, nRes, Const(X86GetRoundControl(roundMode)));
}
else
{
nRes = EmitSse41RoundToNearestWithTiesToAwayOpF(context, nRes, scalar: true);
}
Operand zero = context.VectorZero();

View File

@ -33,6 +33,14 @@ namespace ARMeilleure.Instructions
};
public static readonly long ZeroMask = 128L << 56 | 128L << 48 | 128L << 40 | 128L << 32 | 128L << 24 | 128L << 16 | 128L << 8 | 128L << 0;
public static ulong X86GetGf2p8LogicalShiftLeft(int shift)
{
ulong identity = (0b00000001UL << 56) | (0b00000010UL << 48) | (0b00000100UL << 40) | (0b00001000UL << 32) |
(0b00010000UL << 24) | (0b00100000UL << 16) | (0b01000000UL << 8) | (0b10000000UL << 0);
return shift >= 0 ? identity >> (shift * 8) : identity << (-shift * 8);
}
#endregion
#region "X86 SSE Intrinsics"
@ -243,19 +251,44 @@ namespace ARMeilleure.Instructions
throw new ArgumentException($"Invalid rounding mode \"{roundMode}\".");
}
public static ulong X86GetGf2p8LogicalShiftLeft(int shift)
public static Operand EmitSse41RoundToNearestWithTiesToAwayOpF(ArmEmitterContext context, Operand n, bool scalar)
{
ulong identity =
(0b00000001UL << 56) |
(0b00000010UL << 48) |
(0b00000100UL << 40) |
(0b00001000UL << 32) |
(0b00010000UL << 24) |
(0b00100000UL << 16) |
(0b01000000UL << 8) |
(0b10000000UL << 0);
Debug.Assert(n.Type == OperandType.V128);
return shift >= 0 ? identity >> (shift * 8) : identity << (-shift * 8);
Operand nCopy = context.Copy(n);
Operand rC = Const(X86GetRoundControl(FPRoundingMode.TowardsZero));
IOpCodeSimd op = (IOpCodeSimd)context.CurrOp;
if ((op.Size & 1) == 0)
{
Operand signMask = scalar ? X86GetScalar(context, int.MinValue) : X86GetAllElements(context, int.MinValue);
signMask = context.AddIntrinsic(Intrinsic.X86Pand, signMask, nCopy);
// 0x3EFFFFFF == BitConverter.SingleToInt32Bits(0.5f) - 1
Operand valueMask = scalar ? X86GetScalar(context, 0x3EFFFFFF) : X86GetAllElements(context, 0x3EFFFFFF);
valueMask = context.AddIntrinsic(Intrinsic.X86Por, valueMask, signMask);
nCopy = context.AddIntrinsic(scalar ? Intrinsic.X86Addss : Intrinsic.X86Addps, nCopy, valueMask);
nCopy = context.AddIntrinsic(scalar ? Intrinsic.X86Roundss : Intrinsic.X86Roundps, nCopy, rC);
}
else
{
Operand signMask = scalar ? X86GetScalar(context, long.MinValue) : X86GetAllElements(context, long.MinValue);
signMask = context.AddIntrinsic(Intrinsic.X86Pand, signMask, nCopy);
// 0x3FDFFFFFFFFFFFFFL == BitConverter.DoubleToInt64Bits(0.5d) - 1L
Operand valueMask = scalar ? X86GetScalar(context, 0x3FDFFFFFFFFFFFFFL) : X86GetAllElements(context, 0x3FDFFFFFFFFFFFFFL);
valueMask = context.AddIntrinsic(Intrinsic.X86Por, valueMask, signMask);
nCopy = context.AddIntrinsic(scalar ? Intrinsic.X86Addsd : Intrinsic.X86Addpd, nCopy, valueMask);
nCopy = context.AddIntrinsic(scalar ? Intrinsic.X86Roundsd : Intrinsic.X86Roundpd, nCopy, rC);
}
return nCopy;
}
public static Operand EmitCountSetBits8(ArmEmitterContext context, Operand op) // "size" is 8 (SIMD&FP Inst.).

View File

@ -70,6 +70,22 @@ namespace ARMeilleure.Instructions
context.Copy(vec, insert);
}
public static Operand ExtractScalar16(ArmEmitterContext context, int reg, bool top)
{
return context.VectorExtract16(GetVecA32(reg >> 2), ((reg & 3) << 1) | (top ? 1 : 0));
}
public static void InsertScalar16(ArmEmitterContext context, int reg, bool top, Operand value)
{
Debug.Assert(value.Type == OperandType.FP32 || value.Type == OperandType.I32);
Operand vec, insert;
vec = GetVecA32(reg >> 2);
insert = context.VectorInsert16(vec, value, ((reg & 3) << 1) | (top ? 1 : 0));
context.Copy(vec, insert);
}
public static Operand ExtractElement(ArmEmitterContext context, int reg, int size, bool signed)
{
return EmitVectorExtract32(context, reg >> (4 - size), reg & ((16 >> size) - 1), size, signed);

View File

@ -2,9 +2,10 @@ namespace ARMeilleure.State
{
public enum FPRoundingMode
{
ToNearest = 0,
ToNearest = 0, // With ties to even.
TowardsPlusInfinity = 1,
TowardsMinusInfinity = 2,
TowardsZero = 3
TowardsZero = 3,
ToNearestAway = 4 // With ties to away.
}
}

View File

@ -27,7 +27,7 @@ namespace ARMeilleure.Translation.PTC
private const string OuterHeaderMagicString = "PTCohd\0\0";
private const string InnerHeaderMagicString = "PTCihd\0\0";
private const uint InternalVersion = 3710; //! To be incremented manually for each change to the ARMeilleure project.
private const uint InternalVersion = 3713; //! To be incremented manually for each change to the ARMeilleure project.
private const string ActualDir = "0";
private const string BackupDir = "1";

View File

@ -455,13 +455,16 @@ namespace ARMeilleure.Translation
public void InvalidateJitCacheRegion(ulong address, ulong size)
{
// If rejit is running, stop it as it may be trying to rejit a function on the invalidated region.
ClearRejitQueue(allowRequeue: true);
ulong[] overlapAddresses = Array.Empty<ulong>();
int overlapsCount = Functions.GetOverlaps(address, size, ref overlapAddresses);
if (overlapsCount != 0)
{
// If rejit is running, stop it as it may be trying to rejit a function on the invalidated region.
ClearRejitQueue(allowRequeue: true);
}
for (int index = 0; index < overlapsCount; index++)
{
ulong overlapAddress = overlapAddresses[index];

View File

@ -36,7 +36,7 @@
## Compatibility
As of September 2022, Ryujinx has been tested on approximately 3,600 titles; over 3,400 boot past menus and into gameplay, with roughly 2,700 of those being considered playable. You can check out the compatibility list [here](https://github.com/Ryujinx/Ryujinx-Games-List/issues).
As of October 2022, Ryujinx has been tested on approximately 3,600 titles; over 3,500 boot past menus and into gameplay, with roughly 3,000 of those being considered playable. You can check out the compatibility list [here](https://github.com/Ryujinx/Ryujinx-Games-List/issues).
Anyone is free to submit a new game test or update an existing game test entry; simply follow the new issue template and testing guidelines, or post as a reply to the applicable game issue. Use the search function to see if a game has been tested already!
## Usage

View File

@ -588,5 +588,9 @@
"SettingsTabGraphicsPreferredGpuTooltip": "Wybierz kartę graficzną, która będzie używana z backendem graficznym Vulkan.\n\nNie wpływa na GPU używane przez OpenGL.\n\nW razie wątpliwości ustaw flagę GPU jako \"dGPU\". Jeśli żadnej nie ma, pozostaw nietknięte.",
"SettingsAppRequiredRestartMessage": "Wymagane Zrestartowanie Ryujinx",
"SettingsGpuBackendRestartMessage": "Zmieniono ustawienia Backendu Graficznego lub GPU. Będzie to wymagało ponownego uruchomienia",
"SettingsGpuBackendRestartSubMessage": "Czy chcesz zrestartować teraz?"
"SettingsGpuBackendRestartSubMessage": "Czy chcesz zrestartować teraz?",
"RyujinxUpdaterMessage": "Czy chcesz zaktualizować Ryujinx do najnowszej wersji?",
"SettingsTabHotkeysVolumeUpHotkey": "Zwiększ Głośność:",
"SettingsTabHotkeysVolumeDownHotkey": "Zmniejsz Głośność:",
"VolumeShort": "Głoś"
}

View File

@ -278,7 +278,7 @@ namespace Ryujinx.Modules
{
string ryuName = Path.GetFileName(Environment.ProcessPath);
string ryuExe = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, ryuName);
string ryuArg = string.Join(" ", Environment.GetCommandLineArgs().Skip(1).ToArray());
var ryuArg = Environment.GetCommandLineArgs().Skip(1);
if (!OperatingSystem.IsWindows())
{

View File

@ -295,8 +295,6 @@ namespace Ryujinx.Ava.Ui.ViewModels
if (_validTzRegions.Contains(location))
{
TimeZone = location;
OnPropertyChanged(nameof(TimeZone));
}
}

View File

@ -164,7 +164,7 @@
<MenuItem
Command="{ReflectionBinding ChangeLanguage}"
CommandParameter="pl_PL"
Header="Polish" />
Header="Polski" />
<MenuItem
Command="{ReflectionBinding ChangeLanguage}"
CommandParameter="ru_RU"

View File

@ -37,7 +37,7 @@ namespace Ryujinx.Ava.Ui.Windows
InitializeComponent();
Load();
FuncMultiValueConverter<string, string> converter = new(parts => string.Format("{0} {1} {2}", parts.ToArray()));
FuncMultiValueConverter<string, string> converter = new(parts => string.Format("{0} {1} {2}", parts.ToArray()).Trim());
MultiBinding tzMultiBinding = new() { Converter = converter };
tzMultiBinding.Bindings.Add(new Binding("UtcDifference"));
tzMultiBinding.Bindings.Add(new Binding("Location"));

View File

@ -129,7 +129,7 @@ namespace Ryujinx.Common
private static (Assembly, string) ResolveManifestPath(string filename)
{
var segments = filename.Split(new[] { '/' }, 2, StringSplitOptions.RemoveEmptyEntries);
var segments = filename.Split('/', 2, StringSplitOptions.RemoveEmptyEntries);
if (segments.Length >= 2)
{

View File

@ -480,6 +480,8 @@ namespace Ryujinx.Graphics.Vulkan
if (--_viewsCount == 0)
{
_gd.PipelineInternal?.FlushCommandsIfWeightExceeding(_imageAuto, _size);
Dispose();
}
}

View File

@ -515,10 +515,10 @@ namespace Ryujinx.Graphics.Vulkan
dst.Info,
srcRegion,
dstRegion,
src.FirstLevel,
dst.FirstLevel,
src.FirstLayer,
dst.FirstLayer,
src.FirstLevel,
dst.FirstLevel,
layers,
levels,
linearFilter,

View File

@ -310,7 +310,7 @@ namespace Ryujinx.Graphics.Vulkan
internal TextureView CreateTextureView(TextureCreateInfo info, float scale)
{
// This should be disposed when all views are destroyed.
using var storage = CreateTextureStorage(info, scale);
var storage = CreateTextureStorage(info, scale);
return storage.CreateView(info, 0, 0);
}

View File

@ -918,7 +918,7 @@ namespace Ryujinx.HLE.HOS.Diagnostics.Demangler
return -1;
}
return int.Parse(part.Substring(0, numberLength));
return int.Parse(part.AsSpan(0, numberLength));
}
private string ParseNumber(bool isSigned = false)

View File

@ -2540,11 +2540,10 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
for (int attempt = 0; attempt < 8; attempt++)
{
address = BitUtils.AlignDown(regionStart + GetRandomValue(0, aslrMaxOffset) * (ulong)alignment, alignment);
ulong aslrAddress = BitUtils.AlignDown(regionStart + GetRandomValue(0, aslrMaxOffset) * (ulong)alignment, alignment);
ulong aslrEndAddr = aslrAddress + totalNeededSize;
ulong endAddr = address + totalNeededSize;
KMemoryInfo info = _blockManager.FindBlock(address).GetInfo();
KMemoryInfo info = _blockManager.FindBlock(aslrAddress).GetInfo();
if (info.State != MemoryState.Unmapped)
{
@ -2554,11 +2553,12 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
ulong currBaseAddr = info.Address + reservedPagesCount * PageSize;
ulong currEndAddr = info.Address + info.Size;
if (address >= regionStart &&
address >= currBaseAddr &&
endAddr - 1 <= regionEndAddr - 1 &&
endAddr - 1 <= currEndAddr - 1)
if (aslrAddress >= regionStart &&
aslrAddress >= currBaseAddr &&
aslrEndAddr - 1 <= regionEndAddr - 1 &&
aslrEndAddr - 1 <= currEndAddr - 1)
{
address = aslrAddress;
break;
}
}
@ -2603,7 +2603,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
ulong totalNeededSize = reservedSize + neededPagesCount * PageSize;
ulong regionEndAddr = regionStart + regionPagesCount * PageSize;
ulong regionEndAddr = (regionStart + regionPagesCount * PageSize) - 1;
KMemoryBlock currBlock = _blockManager.FindBlock(regionStart);

View File

@ -5,22 +5,48 @@ namespace Ryujinx.HLE.HOS.Services.Nfc.NfcManager
class INfc : IpcService
{
private NfcPermissionLevel _permissionLevel;
private State _state;
public INfc(NfcPermissionLevel permissionLevel)
{
_permissionLevel = permissionLevel;
_state = State.NonInitialized;
}
[CommandHipc(0)]
[CommandHipc(400)] // 4.0.0+
// Initialize()
// Initialize(u64, u64, pid, buffer<unknown, 5>)
public ResultCode Initialize(ServiceCtx context)
{
_state = State.Initialized;
Logger.Stub?.PrintStub(LogClass.ServiceNfc, new { _permissionLevel });
return ResultCode.Success;
}
[CommandHipc(1)]
[CommandHipc(401)] // 4.0.0+
// Finalize()
public ResultCode Finalize(ServiceCtx context)
{
_state = State.NonInitialized;
Logger.Stub?.PrintStub(LogClass.ServiceNfc, new { _permissionLevel });
return ResultCode.Success;
}
[CommandHipc(2)]
[CommandHipc(402)] // 4.0.0+
// GetState() -> u32
public ResultCode GetState(ServiceCtx context)
{
context.ResponseData.Write((int)_state);
return ResultCode.Success;
}
[CommandHipc(3)]
[CommandHipc(403)] // 4.0.0+
// IsNfcEnabled() -> b8

View File

@ -0,0 +1,8 @@
namespace Ryujinx.HLE.HOS.Services.Nfc.NfcManager
{
enum State
{
NonInitialized,
Initialized
}
}

View File

@ -816,11 +816,11 @@ namespace Ryujinx.HLE.HOS.Services.Nfc.Nfp
Reserved = new Array57<byte>()
};
modelInfo.CharacterId = BinaryPrimitives.ReverseEndianness(ushort.Parse(context.Device.System.NfpDevices[i].AmiiboId.Substring(0, 4), NumberStyles.HexNumber));
modelInfo.CharacterVariant = byte.Parse(context.Device.System.NfpDevices[i].AmiiboId.Substring(4, 2), NumberStyles.HexNumber);
modelInfo.Series = byte.Parse(context.Device.System.NfpDevices[i].AmiiboId.Substring(12, 2), NumberStyles.HexNumber);
modelInfo.ModelNumber = ushort.Parse(context.Device.System.NfpDevices[i].AmiiboId.Substring(8, 4), NumberStyles.HexNumber);
modelInfo.Type = byte.Parse(context.Device.System.NfpDevices[i].AmiiboId.Substring(6, 2), NumberStyles.HexNumber);
modelInfo.CharacterId = BinaryPrimitives.ReverseEndianness(ushort.Parse(context.Device.System.NfpDevices[i].AmiiboId.AsSpan(0, 4), NumberStyles.HexNumber));
modelInfo.CharacterVariant = byte.Parse(context.Device.System.NfpDevices[i].AmiiboId.AsSpan(4, 2), NumberStyles.HexNumber);
modelInfo.Series = byte.Parse(context.Device.System.NfpDevices[i].AmiiboId.AsSpan(12, 2), NumberStyles.HexNumber);
modelInfo.ModelNumber = ushort.Parse(context.Device.System.NfpDevices[i].AmiiboId.AsSpan(8, 4), NumberStyles.HexNumber);
modelInfo.Type = byte.Parse(context.Device.System.NfpDevices[i].AmiiboId.AsSpan(6, 2), NumberStyles.HexNumber);
context.Memory.Write(outputPosition, modelInfo);

View File

@ -349,7 +349,11 @@ namespace Ryujinx.HLE.HOS.Services.Ssl.SslService
// GetSessionCacheMode() -> nn::ssl::sf::SessionCacheMode
public ResultCode GetSessionCacheMode(ServiceCtx context)
{
throw new ServiceNotImplementedException(this, context);
context.ResponseData.Write((uint)_sessionCacheMode);
Logger.Stub?.PrintStub(LogClass.ServiceSsl, new { _sessionCacheMode });
return ResultCode.Success;
}
[CommandHipc(19)]

View File

@ -122,9 +122,8 @@ namespace Ryujinx.HLE.HOS.Tamper
for (int nybbleIndex = 0; nybbleIndex < wordSize; nybbleIndex++)
{
int index = wordIndex * wordSize + nybbleIndex;
string byteData = word.Substring(nybbleIndex, 1);
instruction[index] = byte.Parse(byteData, NumberStyles.HexNumber, CultureInfo.InvariantCulture);
instruction[index] = byte.Parse(word.AsSpan(nybbleIndex, 1), NumberStyles.HexNumber, CultureInfo.InvariantCulture);
}
}

View File

@ -132,7 +132,7 @@ namespace Ryujinx.HLE.Loaders.Mods
{
if (str[0] == '0' && (str[1] == 'x' || str[1] == 'X'))
{
return int.TryParse(str.Substring(2), System.Globalization.NumberStyles.HexNumber, null, out value);
return int.TryParse(str.AsSpan(2), System.Globalization.NumberStyles.HexNumber, null, out value);
}
else
{

View File

@ -54,7 +54,7 @@ namespace Ryujinx.HLE.Utilities
for (int index = 0; index < bytesInHex; index++)
{
output[index] = byte.Parse(hexString.Substring(index * 2, 2), NumberStyles.HexNumber);
output[index] = byte.Parse(hexString.AsSpan(index * 2, 2), NumberStyles.HexNumber);
}
return output;

View File

@ -92,5 +92,31 @@ namespace Ryujinx.Memory.Tests
}
}
}
[Test]
public void Test_AliasMapLeak()
{
if (OperatingSystem.IsMacOS())
{
// Memory aliasing tests fail on CI at the moment.
return;
}
ulong pageSize = 4096;
ulong size = 100000 * pageSize; // The mappings limit on Linux is usually around 65K, so let's make sure we are above that.
using MemoryBlock backing = new MemoryBlock(pageSize, MemoryAllocationFlags.Mirrorable);
using MemoryBlock toAlias = new MemoryBlock(size, MemoryAllocationFlags.Reserve | MemoryAllocationFlags.ViewCompatible);
for (ulong offset = 0; offset < size; offset += pageSize)
{
toAlias.MapView(backing, 0, offset, pageSize);
toAlias.Write(offset, 0xbadc0de);
Assert.AreEqual(0xbadc0de, backing.Read<int>(0));
toAlias.UnmapView(backing, offset, pageSize);
}
}
}
}

View File

@ -177,7 +177,7 @@ namespace Ryujinx.Memory
public static void UnmapView(IntPtr location, ulong size)
{
mmap(location, size, MmapProts.PROT_NONE, MmapFlags.MAP_FIXED, -1, 0);
mmap(location, size, MmapProts.PROT_NONE, MmapFlags.MAP_FIXED | MmapFlags.MAP_PRIVATE | MmapFlags.MAP_ANONYMOUS | MmapFlags.MAP_NORESERVE, -1, 0);
}
}
}

View File

@ -339,6 +339,93 @@ namespace Ryujinx.Tests.Cpu
CompareAgainstUnicorn();
}
[Explicit]
[Test, Pairwise, Description("VCVT<top>.F16.F32 <Sd>, <Dm>")]
public void Vcvt_F32_F16([Values(0u, 1u, 2u, 3u)] uint rd,
[Values(0u, 1u, 2u, 3u)] uint rm,
[ValueSource(nameof(_1S_))] [Random(RndCnt)] uint s0,
[ValueSource(nameof(_1S_))] [Random(RndCnt)] uint s1,
[ValueSource(nameof(_1S_))] [Random(RndCnt)] uint s2,
[ValueSource(nameof(_1S_))] [Random(RndCnt)] uint s3,
[Values] bool top)
{
uint opcode = 0xeeb30a40; // VCVTB.F16.F32 S0, D0
if (top)
{
opcode |= 1 << 7;
}
opcode |= ((rd & 0x1e) << 11) | ((rd & 0x1) << 22);
opcode |= ((rm & 0x1e) >> 1) | ((rm & 0x1) << 5);
V128 v0 = MakeVectorE0E1E2E3(s0, s1, s2, s3);
SingleOpcode(opcode, v0: v0);
CompareAgainstUnicorn();
}
[Explicit]
[Test, Pairwise, Description("VCVT<top>.F16.F64 <Sd>, <Dm>")]
public void Vcvt_F64_F16([Values(0u, 1u, 2u, 3u)] uint rd,
[Values(0u, 1u)] uint rm,
[ValueSource(nameof(_1D_F_))] ulong d0,
[ValueSource(nameof(_1D_F_))] ulong d1,
[Values] bool top)
{
uint opcode = 0xeeb30b40; // VCVTB.F16.F64 S0, D0
if (top)
{
opcode |= 1 << 7;
}
opcode |= ((rd & 0x1e) << 11) | ((rd & 0x1) << 22);
opcode |= ((rm & 0xf) << 0) | ((rm & 0x10) << 1);
V128 v0 = MakeVectorE0E1(d0, d1);
SingleOpcode(opcode, v0: v0);
CompareAgainstUnicorn();
}
[Explicit]
[Test, Pairwise, Description("VCVT<top>.F<size>.F16 <Vd>, <Sm>")]
public void Vcvt_F16_Fx([Values(0u, 1u, 2u, 3u)] uint rd,
[Values(0u, 1u, 2u, 3u)] uint rm,
[ValueSource(nameof(_1D_F_))] ulong d0,
[ValueSource(nameof(_1D_F_))] ulong d1,
[Values] bool top,
[Values] bool sz)
{
uint opcode = 0xeeb20a40; // VCVTB.F32.F16 S0, S0
if (top)
{
opcode |= 1 << 7;
}
if (sz)
{
opcode |= 1 << 8;
opcode |= ((rd & 0xf) << 12) | ((rd & 0x10) << 18);
}
else
{
opcode |= ((rd & 0x1e) << 11) | ((rd & 0x1) << 22);
}
opcode |= ((rm & 0xf) << 0) | ((rm & 0x10) << 1);
V128 v0 = MakeVectorE0E1(d0, d1);
SingleOpcode(opcode, v0: v0);
CompareAgainstUnicorn();
}
#endif
}
}

View File

@ -48,7 +48,7 @@ namespace Ryujinx.Modules
{
string ryuName = OperatingSystem.IsWindows() ? "Ryujinx.exe" : "Ryujinx";
string ryuExe = System.IO.Path.Combine(AppDomain.CurrentDomain.BaseDirectory, ryuName);
string ryuArg = string.Join(" ", Environment.GetCommandLineArgs().AsEnumerable().Skip(1).ToArray());
var ryuArg = Environment.GetCommandLineArgs().AsEnumerable().Skip(1);
Process.Start(ryuExe, ryuArg);