diff --git a/Ryujinx.Tests/Cpu/CpuTestSimdMove.cs b/Ryujinx.Tests/Cpu/CpuTestSimdMove.cs new file mode 100644 index 00000000..d77ac9e5 --- /dev/null +++ b/Ryujinx.Tests/Cpu/CpuTestSimdMove.cs @@ -0,0 +1,45 @@ +using ChocolArm64.State; +using NUnit.Framework; + +namespace Ryujinx.Tests.Cpu +{ + [TestFixture] + public partial class CpuTest + { + [TestCase(0u, 0u, 0x2313221221112010ul, 0x0000000000000000ul)] + [TestCase(1u, 0u, 0x2313221221112010ul, 0x2717261625152414ul)] + [TestCase(0u, 1u, 0x2322131221201110ul, 0x0000000000000000ul)] + [TestCase(1u, 1u, 0x2322131221201110ul, 0x2726171625241514ul)] + [TestCase(0u, 2u, 0x2322212013121110ul, 0x0000000000000000ul)] + [TestCase(1u, 2u, 0x2322212013121110ul, 0x2726252417161514ul)] + [TestCase(1u, 3u, 0x1716151413121110ul, 0x2726252423222120ul)] + public void Zip1_V(uint Q, uint size, ulong Result_0, ulong Result_1) + { + // ZIP1 V0., V1., V2. + uint Opcode = 0x0E023820 | (Q << 30) | (size << 22); + AVec V1 = new AVec { X0 = 0x1716151413121110, X1 = 0x1F1E1D1C1B1A1918 }; + AVec V2 = new AVec { X0 = 0x2726252423222120, X1 = 0x2F2E2D2C2B2A2928 }; + AThreadState ThreadState = SingleOpcode(Opcode, V1: V1, V2: V2); + Assert.AreEqual(Result_0, ThreadState.V0.X0); + Assert.AreEqual(Result_1, ThreadState.V0.X1); + } + + [TestCase(0u, 0u, 0x2717261625152414ul, 0x0000000000000000ul)] + [TestCase(1u, 0u, 0x2B1B2A1A29192818ul, 0x2F1F2E1E2D1D2C1Cul)] + [TestCase(0u, 1u, 0x2726171625241514ul, 0x0000000000000000ul)] + [TestCase(1u, 1u, 0x2B2A1B1A29281918ul, 0x2F2E1F1E2D2C1D1Cul)] + [TestCase(0u, 2u, 0x2726252417161514ul, 0x0000000000000000ul)] + [TestCase(1u, 2u, 0x2B2A29281B1A1918ul, 0x2F2E2D2C1F1E1D1Cul)] + [TestCase(1u, 3u, 0x1F1E1D1C1B1A1918ul, 0x2F2E2D2C2B2A2928ul)] + public void Zip2_V(uint Q, uint size, ulong Result_0, ulong Result_1) + { + // ZIP2 V0., V1., V2. + uint Opcode = 0x0E027820 | (Q << 30) | (size << 22); + AVec V1 = new AVec { X0 = 0x1716151413121110, X1 = 0x1F1E1D1C1B1A1918 }; + AVec V2 = new AVec { X0 = 0x2726252423222120, X1 = 0x2F2E2D2C2B2A2928 }; + AThreadState ThreadState = SingleOpcode(Opcode, V1: V1, V2: V2); + Assert.AreEqual(Result_0, ThreadState.V0.X0); + Assert.AreEqual(Result_1, ThreadState.V0.X1); + } + } +} diff --git a/Ryujinx/Cpu/AOpCodeTable.cs b/Ryujinx/Cpu/AOpCodeTable.cs index e42136b8..43a0e63b 100644 --- a/Ryujinx/Cpu/AOpCodeTable.cs +++ b/Ryujinx/Cpu/AOpCodeTable.cs @@ -256,6 +256,8 @@ namespace ChocolArm64 Set("0x001110xx0xxxxx000110xxxxxxxxxx", AInstEmit.Uzp1_V, typeof(AOpCodeSimdReg)); Set("0x001110xx0xxxxx010110xxxxxxxxxx", AInstEmit.Uzp2_V, typeof(AOpCodeSimdReg)); Set("0x001110<<100001001010xxxxxxxxxx", AInstEmit.Xtn_V, typeof(AOpCodeSimd)); + Set("0x001110xx0xxxxx001110xxxxxxxxxx", AInstEmit.Zip1_V, typeof(AOpCodeSimdReg)); + Set("0x001110xx0xxxxx011110xxxxxxxxxx", AInstEmit.Zip2_V, typeof(AOpCodeSimdReg)); #endregion } diff --git a/Ryujinx/Cpu/Instruction/AInstEmitSimdMove.cs b/Ryujinx/Cpu/Instruction/AInstEmitSimdMove.cs index c8f69032..aabb8f34 100644 --- a/Ryujinx/Cpu/Instruction/AInstEmitSimdMove.cs +++ b/Ryujinx/Cpu/Instruction/AInstEmitSimdMove.cs @@ -263,6 +263,16 @@ namespace ChocolArm64.Instruction } } + public static void Zip1_V(AILEmitterCtx Context) + { + EmitVectorZip(Context, Part: 0); + } + + public static void Zip2_V(AILEmitterCtx Context) + { + EmitVectorZip(Context, Part: 1); + } + private static void EmitIntZeroHigherIfNeeded(AILEmitterCtx Context) { if (Context.CurrOp.RegisterSize == ARegisterSize.Int32) @@ -295,5 +305,29 @@ namespace ChocolArm64.Instruction EmitVectorZeroUpper(Context, Op.Rd); } } + + private static void EmitVectorZip(AILEmitterCtx Context, int Part) + { + AOpCodeSimdReg Op = (AOpCodeSimdReg)Context.CurrOp; + + int Bytes = Context.CurrOp.GetBitsCount() >> 3; + + int Elems = Bytes >> Op.Size; + int Half = Elems >> 1; + + for (int Index = 0; Index < Elems; Index++) + { + int Elem = Part * Half + (Index >> 1); + + EmitVectorExtractZx(Context, (Index & 1) == 0 ? Op.Rn : Op.Rm, Elem, Op.Size); + + EmitVectorInsert(Context, Op.Rd, Index, Op.Size); + } + + if (Op.RegisterSize == ARegisterSize.SIMD64) + { + EmitVectorZeroUpper(Context, Op.Rd); + } + } } } \ No newline at end of file