#define SimdMemory32 using ARMeilleure.State; using NUnit.Framework; using Ryujinx.Memory; using System; namespace Ryujinx.Tests.Cpu { [Category("SimdMemory32")] public sealed class CpuTestSimdMemory32 : CpuTest32 { private static readonly uint TestOffset = DataBaseAddress + 0x500; #if SimdMemory32 private uint[] _ldStModes = { // LD1 0b0111, 0b1010, 0b0110, 0b0010, // LD2 0b1000, 0b1001, 0b0011, // LD3 0b0100, 0b0101, // LD4 0b0000, 0b0001 }; [Test, Pairwise, Description("VLDn. , [ {:}]{ /!/, } (single n element structure)")] public void Vldn_Single([Values(0u, 1u, 2u)] uint size, [Values(0u, 13u)] uint rn, [Values(1u, 13u, 15u)] uint rm, [Values(0u, 1u, 2u, 3u, 4u, 5u, 6u, 7u)] uint vd, [Range(0u, 7u)] uint index, [Range(0u, 3u)] uint n, [Values(0x0u)] uint offset) { var data = GenerateVectorSequence((int)MemoryBlock.GetPageSize()); SetWorkingMemory(0, data); uint opcode = 0xf4a00000u; // VLD1.8 {D0[0]}, [R0], R0 opcode |= ((size & 3) << 10) | ((rn & 15) << 16) | (rm & 15); uint index_align = (index << (int)(1 + size)) & 15; opcode |= (index_align) << 4; opcode |= ((vd & 0x10) << 18); opcode |= ((vd & 0xf) << 12); opcode |= (n & 3) << 8; // LD1 is 0, LD2 is 1 etc. SingleOpcode(opcode, r0: TestOffset, r1: offset, sp: TestOffset); CompareAgainstUnicorn(); } [Test, Pairwise, Description("VLDn. , [ {:}]{ /!/, } (all lanes)")] public void Vldn_All([Values(0u, 13u)] uint rn, [Values(1u, 13u, 15u)] uint rm, [Values(0u, 1u, 2u, 3u, 4u, 5u, 6u, 7u)] uint vd, [Range(0u, 3u)] uint n, [Range(0u, 2u)] uint size, [Values] bool t, [Values(0x0u)] uint offset) { var data = GenerateVectorSequence((int)MemoryBlock.GetPageSize()); SetWorkingMemory(0, data); uint opcode = 0xf4a00c00u; // VLD1.8 {D0[0]}, [R0], R0 opcode |= ((size & 3) << 6) | ((rn & 15) << 16) | (rm & 15); opcode |= ((vd & 0x10) << 18); opcode |= ((vd & 0xf) << 12); opcode |= (n & 3) << 8; // LD1 is 0, LD2 is 1 etc. if (t) opcode |= 1 << 5; SingleOpcode(opcode, r0: TestOffset, r1: offset, sp: TestOffset); CompareAgainstUnicorn(); } [Test, Pairwise, Description("VLDn. , [ {:}]{ /!/, } (multiple n element structures)")] public void Vldn_Pair([Values(0u, 1u, 2u, 3u)] uint size, [Values(0u, 13u)] uint rn, [Values(1u, 13u, 15u)] uint rm, [Values(0u, 1u, 2u, 3u, 4u, 5u, 6u, 7u)] uint vd, [Range(0u, 10u)] uint mode, [Values(0x0u)] uint offset) { var data = GenerateVectorSequence((int)MemoryBlock.GetPageSize()); SetWorkingMemory(0, data); uint opcode = 0xf4200000u; // VLD4.8 {D0, D1, D2, D3}, [R0], R0 if (mode > 3 && size == 3) { // A size of 3 is only valid for VLD1. size = 2; } opcode |= ((size & 3) << 6) | ((rn & 15) << 16) | (rm & 15) | (_ldStModes[mode] << 8); opcode |= ((vd & 0x10) << 18); opcode |= ((vd & 0xf) << 12); SingleOpcode(opcode, r0: TestOffset, r1: offset, sp: TestOffset); CompareAgainstUnicorn(); } [Test, Pairwise, Description("VSTn. , [ {:}]{ /!/, } (single n element structure)")] public void Vstn_Single([Values(0u, 1u, 2u)] uint size, [Values(0u, 13u)] uint rn, [Values(1u, 13u, 15u)] uint rm, [Values(0u, 1u, 2u, 3u, 4u, 5u, 6u, 7u)] uint vd, [Range(0u, 7u)] uint index, [Range(0u, 3u)] uint n, [Values(0x0u)] uint offset) { var data = GenerateVectorSequence((int)MemoryBlock.GetPageSize()); SetWorkingMemory(0, data); (V128 vec1, V128 vec2, V128 vec3, V128 vec4) = GenerateTestVectors(); uint opcode = 0xf4800000u; // VST1.8 {D0[0]}, [R0], R0 opcode |= ((size & 3) << 10) | ((rn & 15) << 16) | (rm & 15); uint index_align = (index << (int)(1 + size)) & 15; opcode |= (index_align) << 4; opcode |= ((vd & 0x10) << 18); opcode |= ((vd & 0xf) << 12); opcode |= (n & 3) << 8; // ST1 is 0, ST2 is 1 etc. SingleOpcode(opcode, r0: TestOffset, r1: offset, v1: vec1, v2: vec2, v3: vec3, v4: vec4, sp: TestOffset); CompareAgainstUnicorn(); } [Test, Pairwise, Description("VSTn. , [ {:}]{ /!/, } (multiple n element structures)")] public void Vstn_Pair([Values(0u, 1u, 2u, 3u)] uint size, [Values(0u, 13u)] uint rn, [Values(1u, 13u, 15u)] uint rm, [Values(0u, 1u, 2u, 3u, 4u, 5u, 6u, 7u)] uint vd, [Range(0u, 10u)] uint mode, [Values(0x0u)] uint offset) { var data = GenerateVectorSequence((int)MemoryBlock.GetPageSize()); SetWorkingMemory(0, data); (V128 vec1, V128 vec2, V128 vec3, V128 vec4) = GenerateTestVectors(); uint opcode = 0xf4000000u; // VST4.8 {D0, D1, D2, D3}, [R0], R0 if (mode > 3 && size == 3) { // A size of 3 is only valid for VST1. size = 2; } opcode |= ((size & 3) << 6) | ((rn & 15) << 16) | (rm & 15) | (_ldStModes[mode] << 8); opcode |= ((vd & 0x10) << 18); opcode |= ((vd & 0xf) << 12); SingleOpcode(opcode, r0: TestOffset, r1: offset, v1: vec1, v2: vec2, v3: vec3, v4: vec4, sp: TestOffset); CompareAgainstUnicorn(); } [Test, Pairwise, Description("VLDM. {!}, ")] public void Vldm([Values(0u, 13u)] uint rn, [Values(0u, 1u, 2u, 3u, 4u, 5u, 6u, 7u)] uint vd, [Range(0u, 2u)] uint mode, [Values(0x1u, 0x32u)] uint regs, [Values] bool single) { var data = GenerateVectorSequence((int)MemoryBlock.GetPageSize()); SetWorkingMemory(0, data); uint opcode = 0xec100a00u; // VST4.8 {D0, D1, D2, D3}, [R0], R0 uint[] vldmModes = { // Note: 3rd 0 leaves a space for "D". 0b0100, // Increment after. 0b0101, // Increment after. (!) 0b1001 // Decrement before. (!) }; opcode |= ((vldmModes[mode] & 15) << 21); opcode |= ((rn & 15) << 16); opcode |= ((vd & 0x10) << 18); opcode |= ((vd & 0xf) << 12); opcode |= ((uint)(single ? 0 : 1) << 8); if (!single) regs = (regs << 1); // Low bit must be 0 - must be even number of registers. uint regSize = single ? 1u : 2u; if (vd + (regs / regSize) > 32) // Can't address further than S31 or D31. { regs -= (vd + (regs / regSize)) - 32; } if (regs / regSize > 16) // Can't do more than 16 registers at a time. { regs = 16 * regSize; } opcode |= regs & 0xff; SingleOpcode(opcode, r0: TestOffset, sp: TestOffset); CompareAgainstUnicorn(); } [Test, Pairwise, Description("VLDR. , [ {, #{+/-}}]")] public void Vldr([Values(2u, 3u)] uint size, // FP16 is not supported for now [Values(0u)] uint rn, [Values(0u, 1u, 2u, 3u, 4u, 5u, 6u, 7u)] uint sd, [Values(0x0u)] uint imm, [Values] bool sub) { var data = GenerateVectorSequence((int)MemoryBlock.GetPageSize()); SetWorkingMemory(0, data); uint opcode = 0xed900a00u; // VLDR.32 S0, [R0, #0] opcode |= ((size & 3) << 8) | ((rn & 15) << 16); if (sub) { opcode &= ~(uint)(1 << 23); } if (size == 2) { opcode |= ((sd & 0x1) << 22); opcode |= ((sd & 0x1e) << 11); } else { opcode |= ((sd & 0x10) << 18); opcode |= ((sd & 0xf) << 12); } opcode |= imm & 0xff; SingleOpcode(opcode, r0: TestOffset); CompareAgainstUnicorn(); } [Test, Pairwise, Description("VSTR. , [ {, #{+/-}}]")] public void Vstr([Values(2u, 3u)] uint size, // FP16 is not supported for now [Values(0u)] uint rn, [Values(0u, 1u, 2u, 3u, 4u, 5u, 6u, 7u)] uint sd, [Values(0x0u)] uint imm, [Values] bool sub) { var data = GenerateVectorSequence((int)MemoryBlock.GetPageSize()); SetWorkingMemory(0, data); uint opcode = 0xed800a00u; // VSTR.32 S0, [R0, #0] opcode |= ((size & 3) << 8) | ((rn & 15) << 16); if (sub) { opcode &= ~(uint)(1 << 23); } if (size == 2) { opcode |= ((sd & 0x1) << 22); opcode |= ((sd & 0x1e) << 11); } else { opcode |= ((sd & 0x10) << 18); opcode |= ((sd & 0xf) << 12); } opcode |= imm & 0xff; (V128 vec1, V128 vec2, _, _) = GenerateTestVectors(); SingleOpcode(opcode, r0: TestOffset, v0: vec1, v1: vec2); CompareAgainstUnicorn(); } private (V128, V128, V128, V128) GenerateTestVectors() { return ( new V128(-12.43f, 1872.23f, 4456.23f, -5622.2f), new V128(0.0f, float.NaN, float.PositiveInfinity, float.NegativeInfinity), new V128(1.23e10f, -0.0f, -0.123f, 0.123f), new V128(float.Epsilon, 3.5f, 925.23f, -104.9f) ); } private byte[] GenerateVectorSequence(int length) { int floatLength = length >> 2; float[] data = new float[floatLength]; for (int i = 0; i < floatLength; i++) { data[i] = i + (i / 9f); } var result = new byte[length]; Buffer.BlockCopy(data, 0, result, 0, result.Length); return result; } #endif } }