From 98c6ceede564eda4aed528e51219a9b0d6bea1c4 Mon Sep 17 00:00:00 2001 From: gdkchan Date: Sat, 14 Jul 2018 23:57:41 -0300 Subject: [PATCH 01/33] Audio Renderer improvements (#210) * Partial voice implementation on audio renderer * Implemented audren resampler (based on original impl) * Fix BiquadFilter struct * Pause audio playback on last stream buffer * Split audren/audout files into separate folders, some minor cleanup * Use AudioRendererParameter on GetWorkBufferSize aswell * Bump audren version to REV4, name a few things, increase sample buffer size * Remove useless new lines --- Ryujinx.Audio/Adpcm/AdpcmDecoder.cs | 91 +++++ Ryujinx.Audio/Adpcm/AdpcmDecoderContext.cs | 10 + Ryujinx.Audio/DspUtils.cs | 16 + Ryujinx.Audio/IAalOutput.cs | 8 +- Ryujinx.Audio/OpenAL/OpenALAudioOut.cs | 53 +-- Ryujinx.HLE/OsHle/Services/Aud/AudErr.cs | 1 + .../Aud/{ => AudioOut}/AudioOutData.cs | 2 +- .../Services/Aud/{ => AudioOut}/IAudioOut.cs | 3 +- .../Services/Aud/AudioRenderer/AudioConsts.cs | 8 + .../Services/Aud/AudioRenderer/BehaviorIn.cs | 11 + .../Aud/AudioRenderer/BiquadFilter.cs | 16 + .../Aud/AudioRenderer/IAudioRenderer.cs | 316 ++++++++++++++++++ .../Aud/AudioRenderer/MemoryPoolContext.cs | 12 + .../Aud/AudioRenderer/MemoryPoolIn.cs | 14 + .../Aud/AudioRenderer/MemoryPoolOut.cs | 12 + .../{ => AudioRenderer}/MemoryPoolState.cs | 2 +- .../Services/Aud/AudioRenderer/PlayState.cs | 9 + .../Services/Aud/AudioRenderer/Resampler.cs | 191 +++++++++++ .../{ => AudioRenderer}/UpdateDataHeader.cs | 12 +- .../AudioRenderer/VoiceChannelResourceIn.cs | 10 + .../Aud/AudioRenderer/VoiceContext.cs | 188 +++++++++++ .../Services/Aud/AudioRenderer/VoiceIn.cs | 49 +++ .../Services/Aud/AudioRenderer/VoiceOut.cs | 12 + .../Services/Aud/AudioRenderer/WaveBuffer.cs | 20 ++ .../Services/Aud/AudioRendererParameter.cs | 8 +- .../OsHle/Services/Aud/IAudioOutManager.cs | 5 +- .../OsHle/Services/Aud/IAudioRenderer.cs | 136 -------- .../Services/Aud/IAudioRendererManager.cs | 129 +++---- .../OsHle/Services/Aud/SampleFormat.cs | 8 +- Ryujinx.HLE/OsHle/Services/Aud/VoiceState.cs | 9 - .../OsHle/Services/Nv/NvMap/NvMapIoctl.cs | 4 +- Ryujinx.HLE/OsHle/Utilities/IntUtils.cs | 4 +- Ryujinx.HLE/OsHle/Utilities/StructReader.cs | 45 +++ Ryujinx.HLE/OsHle/Utilities/StructWriter.cs | 25 ++ 34 files changed, 1168 insertions(+), 271 deletions(-) create mode 100644 Ryujinx.Audio/Adpcm/AdpcmDecoder.cs create mode 100644 Ryujinx.Audio/Adpcm/AdpcmDecoderContext.cs create mode 100644 Ryujinx.Audio/DspUtils.cs rename Ryujinx.HLE/OsHle/Services/Aud/{ => AudioOut}/AudioOutData.cs (86%) rename Ryujinx.HLE/OsHle/Services/Aud/{ => AudioOut}/IAudioOut.cs (98%) create mode 100644 Ryujinx.HLE/OsHle/Services/Aud/AudioRenderer/AudioConsts.cs create mode 100644 Ryujinx.HLE/OsHle/Services/Aud/AudioRenderer/BehaviorIn.cs create mode 100644 Ryujinx.HLE/OsHle/Services/Aud/AudioRenderer/BiquadFilter.cs create mode 100644 Ryujinx.HLE/OsHle/Services/Aud/AudioRenderer/IAudioRenderer.cs create mode 100644 Ryujinx.HLE/OsHle/Services/Aud/AudioRenderer/MemoryPoolContext.cs create mode 100644 Ryujinx.HLE/OsHle/Services/Aud/AudioRenderer/MemoryPoolIn.cs create mode 100644 Ryujinx.HLE/OsHle/Services/Aud/AudioRenderer/MemoryPoolOut.cs rename Ryujinx.HLE/OsHle/Services/Aud/{ => AudioRenderer}/MemoryPoolState.cs (80%) create mode 100644 Ryujinx.HLE/OsHle/Services/Aud/AudioRenderer/PlayState.cs create mode 100644 Ryujinx.HLE/OsHle/Services/Aud/AudioRenderer/Resampler.cs rename Ryujinx.HLE/OsHle/Services/Aud/{ => AudioRenderer}/UpdateDataHeader.cs (65%) create mode 100644 Ryujinx.HLE/OsHle/Services/Aud/AudioRenderer/VoiceChannelResourceIn.cs create mode 100644 Ryujinx.HLE/OsHle/Services/Aud/AudioRenderer/VoiceContext.cs create mode 100644 Ryujinx.HLE/OsHle/Services/Aud/AudioRenderer/VoiceIn.cs create mode 100644 Ryujinx.HLE/OsHle/Services/Aud/AudioRenderer/VoiceOut.cs create mode 100644 Ryujinx.HLE/OsHle/Services/Aud/AudioRenderer/WaveBuffer.cs delete mode 100644 Ryujinx.HLE/OsHle/Services/Aud/IAudioRenderer.cs rename Ryujinx.Audio/AudioFormat.cs => Ryujinx.HLE/OsHle/Services/Aud/SampleFormat.cs (52%) delete mode 100644 Ryujinx.HLE/OsHle/Services/Aud/VoiceState.cs create mode 100644 Ryujinx.HLE/OsHle/Utilities/StructReader.cs create mode 100644 Ryujinx.HLE/OsHle/Utilities/StructWriter.cs diff --git a/Ryujinx.Audio/Adpcm/AdpcmDecoder.cs b/Ryujinx.Audio/Adpcm/AdpcmDecoder.cs new file mode 100644 index 0000000000..24455b4187 --- /dev/null +++ b/Ryujinx.Audio/Adpcm/AdpcmDecoder.cs @@ -0,0 +1,91 @@ +namespace Ryujinx.Audio.Adpcm +{ + public static class AdpcmDecoder + { + private const int SamplesPerFrame = 14; + private const int BytesPerFrame = 8; + + public static int[] Decode(byte[] Buffer, AdpcmDecoderContext Context) + { + int Samples = GetSamplesCountFromSize(Buffer.Length); + + int[] Pcm = new int[Samples * 2]; + + short History0 = Context.History0; + short History1 = Context.History1; + + int InputOffset = 0; + int OutputOffset = 0; + + while (InputOffset < Buffer.Length) + { + byte Header = Buffer[InputOffset++]; + + int Scale = 0x800 << (Header & 0xf); + + int CoeffIndex = (Header >> 4) & 7; + + short Coeff0 = Context.Coefficients[CoeffIndex * 2 + 0]; + short Coeff1 = Context.Coefficients[CoeffIndex * 2 + 1]; + + int FrameSamples = SamplesPerFrame; + + if (FrameSamples > Samples) + { + FrameSamples = Samples; + } + + int Value = 0; + + for (int SampleIndex = 0; SampleIndex < FrameSamples; SampleIndex++) + { + int Sample; + + if ((SampleIndex & 1) == 0) + { + Value = Buffer[InputOffset++]; + + Sample = (Value << 24) >> 28; + } + else + { + Sample = (Value << 28) >> 28; + } + + int Prediction = Coeff0 * History0 + Coeff1 * History1; + + Sample = (Sample * Scale + Prediction + 0x400) >> 11; + + short SaturatedSample = DspUtils.Saturate(Sample); + + History1 = History0; + History0 = SaturatedSample; + + Pcm[OutputOffset++] = SaturatedSample; + Pcm[OutputOffset++] = SaturatedSample; + } + + Samples -= FrameSamples; + } + + Context.History0 = History0; + Context.History1 = History1; + + return Pcm; + } + + public static long GetSizeFromSamplesCount(int SamplesCount) + { + int Frames = SamplesCount / SamplesPerFrame; + + return Frames * BytesPerFrame; + } + + public static int GetSamplesCountFromSize(long Size) + { + int Frames = (int)(Size / BytesPerFrame); + + return Frames * SamplesPerFrame; + } + } +} \ No newline at end of file diff --git a/Ryujinx.Audio/Adpcm/AdpcmDecoderContext.cs b/Ryujinx.Audio/Adpcm/AdpcmDecoderContext.cs new file mode 100644 index 0000000000..91730333c8 --- /dev/null +++ b/Ryujinx.Audio/Adpcm/AdpcmDecoderContext.cs @@ -0,0 +1,10 @@ +namespace Ryujinx.Audio.Adpcm +{ + public class AdpcmDecoderContext + { + public short[] Coefficients; + + public short History0; + public short History1; + } +} \ No newline at end of file diff --git a/Ryujinx.Audio/DspUtils.cs b/Ryujinx.Audio/DspUtils.cs new file mode 100644 index 0000000000..c048161dae --- /dev/null +++ b/Ryujinx.Audio/DspUtils.cs @@ -0,0 +1,16 @@ +namespace Ryujinx.Audio.Adpcm +{ + public static class DspUtils + { + public static short Saturate(int Value) + { + if (Value > short.MaxValue) + Value = short.MaxValue; + + if (Value < short.MinValue) + Value = short.MinValue; + + return (short)Value; + } + } +} \ No newline at end of file diff --git a/Ryujinx.Audio/IAalOutput.cs b/Ryujinx.Audio/IAalOutput.cs index f9978ee4d9..e903c5c5cc 100644 --- a/Ryujinx.Audio/IAalOutput.cs +++ b/Ryujinx.Audio/IAalOutput.cs @@ -2,11 +2,7 @@ namespace Ryujinx.Audio { public interface IAalOutput { - int OpenTrack( - int SampleRate, - int Channels, - ReleaseCallback Callback, - out AudioFormat Format); + int OpenTrack(int SampleRate, int Channels, ReleaseCallback Callback); void CloseTrack(int Track); @@ -14,7 +10,7 @@ namespace Ryujinx.Audio long[] GetReleasedBuffers(int Track, int MaxCount); - void AppendBuffer(int Track, long Tag, byte[] Buffer); + void AppendBuffer(int Track, long Tag, T[] Buffer) where T : struct; void Start(int Track); void Stop(int Track); diff --git a/Ryujinx.Audio/OpenAL/OpenALAudioOut.cs b/Ryujinx.Audio/OpenAL/OpenALAudioOut.cs index 2860dc2e2d..1dd63202ba 100644 --- a/Ryujinx.Audio/OpenAL/OpenALAudioOut.cs +++ b/Ryujinx.Audio/OpenAL/OpenALAudioOut.cs @@ -3,6 +3,7 @@ using OpenTK.Audio.OpenAL; using System; using System.Collections.Concurrent; using System.Collections.Generic; +using System.Runtime.InteropServices; using System.Threading; namespace Ryujinx.Audio.OpenAL @@ -226,15 +227,9 @@ namespace Ryujinx.Audio.OpenAL while (KeepPolling); } - public int OpenTrack( - int SampleRate, - int Channels, - ReleaseCallback Callback, - out AudioFormat Format) + public int OpenTrack(int SampleRate, int Channels, ReleaseCallback Callback) { - Format = AudioFormat.PcmInt16; - - Track Td = new Track(SampleRate, GetALFormat(Channels, Format), Callback); + Track Td = new Track(SampleRate, GetALFormat(Channels), Callback); for (int Id = 0; Id < MaxTracks; Id++) { @@ -247,38 +242,16 @@ namespace Ryujinx.Audio.OpenAL return -1; } - private ALFormat GetALFormat(int Channels, AudioFormat Format) + private ALFormat GetALFormat(int Channels) { - if (Channels == 1) + switch (Channels) { - switch (Format) - { - case AudioFormat.PcmInt8: return ALFormat.Mono8; - case AudioFormat.PcmInt16: return ALFormat.Mono16; - } - } - else if (Channels == 2) - { - switch (Format) - { - case AudioFormat.PcmInt8: return ALFormat.Stereo8; - case AudioFormat.PcmInt16: return ALFormat.Stereo16; - } - } - else if (Channels == 6) - { - switch (Format) - { - case AudioFormat.PcmInt8: return ALFormat.Multi51Chn8Ext; - case AudioFormat.PcmInt16: return ALFormat.Multi51Chn16Ext; - } - } - else - { - throw new ArgumentOutOfRangeException(nameof(Channels)); + case 1: return ALFormat.Mono16; + case 2: return ALFormat.Stereo16; + case 6: return ALFormat.Multi51Chn16Ext; } - throw new ArgumentException(nameof(Format)); + throw new ArgumentOutOfRangeException(nameof(Channels)); } public void CloseTrack(int Track) @@ -309,13 +282,15 @@ namespace Ryujinx.Audio.OpenAL return null; } - public void AppendBuffer(int Track, long Tag, byte[] Buffer) + public void AppendBuffer(int Track, long Tag, T[] Buffer) where T : struct { if (Tracks.TryGetValue(Track, out Track Td)) { int BufferId = Td.AppendBuffer(Tag); - AL.BufferData(BufferId, Td.Format, Buffer, Buffer.Length, Td.SampleRate); + int Size = Buffer.Length * Marshal.SizeOf(); + + AL.BufferData(BufferId, Td.Format, Buffer, Size, Td.SampleRate); AL.SourceQueueBuffer(Td.SourceId, BufferId); @@ -366,7 +341,5 @@ namespace Ryujinx.Audio.OpenAL return PlaybackState.Stopped; } - - } } \ No newline at end of file diff --git a/Ryujinx.HLE/OsHle/Services/Aud/AudErr.cs b/Ryujinx.HLE/OsHle/Services/Aud/AudErr.cs index fa201d8cdf..72c3e65eb9 100644 --- a/Ryujinx.HLE/OsHle/Services/Aud/AudErr.cs +++ b/Ryujinx.HLE/OsHle/Services/Aud/AudErr.cs @@ -3,6 +3,7 @@ namespace Ryujinx.HLE.OsHle.Services.Aud static class AudErr { public const int DeviceNotFound = 1; + public const int UnsupportedRevision = 2; public const int UnsupportedSampleRate = 3; } } \ No newline at end of file diff --git a/Ryujinx.HLE/OsHle/Services/Aud/AudioOutData.cs b/Ryujinx.HLE/OsHle/Services/Aud/AudioOut/AudioOutData.cs similarity index 86% rename from Ryujinx.HLE/OsHle/Services/Aud/AudioOutData.cs rename to Ryujinx.HLE/OsHle/Services/Aud/AudioOut/AudioOutData.cs index 9d68c24ab2..6887a38bed 100644 --- a/Ryujinx.HLE/OsHle/Services/Aud/AudioOutData.cs +++ b/Ryujinx.HLE/OsHle/Services/Aud/AudioOut/AudioOutData.cs @@ -1,6 +1,6 @@ using System.Runtime.InteropServices; -namespace Ryujinx.HLE.OsHle.Services.Aud +namespace Ryujinx.HLE.OsHle.Services.Aud.AudioOut { [StructLayout(LayoutKind.Sequential)] struct AudioOutData diff --git a/Ryujinx.HLE/OsHle/Services/Aud/IAudioOut.cs b/Ryujinx.HLE/OsHle/Services/Aud/AudioOut/IAudioOut.cs similarity index 98% rename from Ryujinx.HLE/OsHle/Services/Aud/IAudioOut.cs rename to Ryujinx.HLE/OsHle/Services/Aud/AudioOut/IAudioOut.cs index 49c87a5616..d89fc293af 100644 --- a/Ryujinx.HLE/OsHle/Services/Aud/IAudioOut.cs +++ b/Ryujinx.HLE/OsHle/Services/Aud/AudioOut/IAudioOut.cs @@ -1,12 +1,11 @@ using ChocolArm64.Memory; using Ryujinx.Audio; -using Ryujinx.HLE.Logging; using Ryujinx.HLE.OsHle.Handles; using Ryujinx.HLE.OsHle.Ipc; using System; using System.Collections.Generic; -namespace Ryujinx.HLE.OsHle.Services.Aud +namespace Ryujinx.HLE.OsHle.Services.Aud.AudioOut { class IAudioOut : IpcService, IDisposable { diff --git a/Ryujinx.HLE/OsHle/Services/Aud/AudioRenderer/AudioConsts.cs b/Ryujinx.HLE/OsHle/Services/Aud/AudioRenderer/AudioConsts.cs new file mode 100644 index 0000000000..fed41959d7 --- /dev/null +++ b/Ryujinx.HLE/OsHle/Services/Aud/AudioRenderer/AudioConsts.cs @@ -0,0 +1,8 @@ +namespace Ryujinx.HLE.OsHle.Services.Aud.AudioRenderer +{ + static class AudioConsts + { + public const int HostSampleRate = 48000; + public const int HostChannelsCount = 2; + } +} diff --git a/Ryujinx.HLE/OsHle/Services/Aud/AudioRenderer/BehaviorIn.cs b/Ryujinx.HLE/OsHle/Services/Aud/AudioRenderer/BehaviorIn.cs new file mode 100644 index 0000000000..4e33de6214 --- /dev/null +++ b/Ryujinx.HLE/OsHle/Services/Aud/AudioRenderer/BehaviorIn.cs @@ -0,0 +1,11 @@ +using System.Runtime.InteropServices; + +namespace Ryujinx.HLE.OsHle.Services.Aud.AudioRenderer +{ + [StructLayout(LayoutKind.Sequential, Size = 0x10, Pack = 4)] + struct BehaviorIn + { + public long Unknown0; + public long Unknown8; + } +} \ No newline at end of file diff --git a/Ryujinx.HLE/OsHle/Services/Aud/AudioRenderer/BiquadFilter.cs b/Ryujinx.HLE/OsHle/Services/Aud/AudioRenderer/BiquadFilter.cs new file mode 100644 index 0000000000..9fa4cd3303 --- /dev/null +++ b/Ryujinx.HLE/OsHle/Services/Aud/AudioRenderer/BiquadFilter.cs @@ -0,0 +1,16 @@ +using System.Runtime.InteropServices; + +namespace Ryujinx.HLE.OsHle.Services.Aud.AudioRenderer +{ + [StructLayout(LayoutKind.Sequential, Size = 0xc, Pack = 1)] + struct BiquadFilter + { + public byte Enable; + public byte Padding; + public short B0; + public short B1; + public short B2; + public short A1; + public short A2; + } +} diff --git a/Ryujinx.HLE/OsHle/Services/Aud/AudioRenderer/IAudioRenderer.cs b/Ryujinx.HLE/OsHle/Services/Aud/AudioRenderer/IAudioRenderer.cs new file mode 100644 index 0000000000..f91a8da37e --- /dev/null +++ b/Ryujinx.HLE/OsHle/Services/Aud/AudioRenderer/IAudioRenderer.cs @@ -0,0 +1,316 @@ +using ChocolArm64.Memory; +using Ryujinx.Audio; +using Ryujinx.Audio.Adpcm; +using Ryujinx.HLE.Logging; +using Ryujinx.HLE.OsHle.Handles; +using Ryujinx.HLE.OsHle.Ipc; +using Ryujinx.HLE.OsHle.Utilities; +using System; +using System.Collections.Generic; +using System.Runtime.InteropServices; + +namespace Ryujinx.HLE.OsHle.Services.Aud.AudioRenderer +{ + class IAudioRenderer : IpcService, IDisposable + { + //This is the amount of samples that are going to be appended + //each time that RequestUpdateAudioRenderer is called. Ideally, + //this value shouldn't be neither too small (to avoid the player + //starving due to running out of samples) or too large (to avoid + //high latency). + private const int MixBufferSamplesCount = 960; + + private Dictionary m_Commands; + + public override IReadOnlyDictionary Commands => m_Commands; + + private KEvent UpdateEvent; + + private AMemory Memory; + + private IAalOutput AudioOut; + + private AudioRendererParameter Params; + + private MemoryPoolContext[] MemoryPools; + + private VoiceContext[] Voices; + + private int Track; + + public IAudioRenderer(AMemory Memory, IAalOutput AudioOut, AudioRendererParameter Params) + { + m_Commands = new Dictionary() + { + { 4, RequestUpdateAudioRenderer }, + { 5, StartAudioRenderer }, + { 6, StopAudioRenderer }, + { 7, QuerySystemEvent } + }; + + UpdateEvent = new KEvent(); + + this.Memory = Memory; + this.AudioOut = AudioOut; + this.Params = Params; + + Track = AudioOut.OpenTrack( + AudioConsts.HostSampleRate, + AudioConsts.HostChannelsCount, + AudioCallback); + + MemoryPools = CreateArray(Params.EffectCount + Params.VoiceCount * 4); + + Voices = CreateArray(Params.VoiceCount); + + InitializeAudioOut(); + } + + private void AudioCallback() + { + UpdateEvent.WaitEvent.Set(); + } + + private static T[] CreateArray(int Size) where T : new() + { + T[] Output = new T[Size]; + + for (int Index = 0; Index < Size; Index++) + { + Output[Index] = new T(); + } + + return Output; + } + + private void InitializeAudioOut() + { + AppendMixedBuffer(0); + AppendMixedBuffer(1); + AppendMixedBuffer(2); + + AudioOut.Start(Track); + } + + public long RequestUpdateAudioRenderer(ServiceCtx Context) + { + long OutputPosition = Context.Request.ReceiveBuff[0].Position; + long OutputSize = Context.Request.ReceiveBuff[0].Size; + + AMemoryHelper.FillWithZeros(Context.Memory, OutputPosition, (int)OutputSize); + + long InputPosition = Context.Request.SendBuff[0].Position; + + StructReader Reader = new StructReader(Context.Memory, InputPosition); + StructWriter Writer = new StructWriter(Context.Memory, OutputPosition); + + UpdateDataHeader InputHeader = Reader.Read(); + + Reader.Read(InputHeader.BehaviorSize); + + MemoryPoolIn[] MemoryPoolsIn = Reader.Read(InputHeader.MemoryPoolSize); + + for (int Index = 0; Index < MemoryPoolsIn.Length; Index++) + { + MemoryPoolIn MemoryPool = MemoryPoolsIn[Index]; + + if (MemoryPool.State == MemoryPoolState.RequestAttach) + { + MemoryPools[Index].OutStatus.State = MemoryPoolState.Attached; + } + else if (MemoryPool.State == MemoryPoolState.RequestDetach) + { + MemoryPools[Index].OutStatus.State = MemoryPoolState.Detached; + } + } + + Reader.Read(InputHeader.VoiceResourceSize); + + VoiceIn[] VoicesIn = Reader.Read(InputHeader.VoiceSize); + + for (int Index = 0; Index < VoicesIn.Length; Index++) + { + VoiceIn Voice = VoicesIn[Index]; + + VoiceContext VoiceCtx = Voices[Index]; + + VoiceCtx.SetAcquireState(Voice.Acquired != 0); + + if (Voice.Acquired == 0) + { + continue; + } + + if (Voice.FirstUpdate != 0) + { + VoiceCtx.AdpcmCtx = GetAdpcmDecoderContext( + Voice.AdpcmCoeffsPosition, + Voice.AdpcmCoeffsSize); + + VoiceCtx.SampleFormat = Voice.SampleFormat; + VoiceCtx.SampleRate = Voice.SampleRate; + VoiceCtx.ChannelsCount = Voice.ChannelsCount; + + VoiceCtx.SetBufferIndex(Voice.BaseWaveBufferIndex); + } + + VoiceCtx.WaveBuffers[0] = Voice.WaveBuffer0; + VoiceCtx.WaveBuffers[1] = Voice.WaveBuffer1; + VoiceCtx.WaveBuffers[2] = Voice.WaveBuffer2; + VoiceCtx.WaveBuffers[3] = Voice.WaveBuffer3; + VoiceCtx.Volume = Voice.Volume; + VoiceCtx.PlayState = Voice.PlayState; + } + + UpdateAudio(); + + UpdateDataHeader OutputHeader = new UpdateDataHeader(); + + int UpdateHeaderSize = Marshal.SizeOf(); + + OutputHeader.Revision = IAudioRendererManager.RevMagic; + OutputHeader.BehaviorSize = 0xb0; + OutputHeader.MemoryPoolSize = (Params.EffectCount + Params.VoiceCount * 4) * 0x10; + OutputHeader.VoiceSize = Params.VoiceCount * 0x10; + OutputHeader.EffectSize = Params.EffectCount * 0x10; + OutputHeader.SinkSize = Params.SinkCount * 0x20; + OutputHeader.PerformanceManagerSize = 0x10; + OutputHeader.TotalSize = UpdateHeaderSize + + OutputHeader.BehaviorSize + + OutputHeader.MemoryPoolSize + + OutputHeader.VoiceSize + + OutputHeader.EffectSize + + OutputHeader.SinkSize + + OutputHeader.PerformanceManagerSize; + + Writer.Write(OutputHeader); + + foreach (MemoryPoolContext MemoryPool in MemoryPools) + { + Writer.Write(MemoryPool.OutStatus); + } + + foreach (VoiceContext Voice in Voices) + { + Writer.Write(Voice.OutStatus); + } + + return 0; + } + + public long StartAudioRenderer(ServiceCtx Context) + { + Context.Ns.Log.PrintStub(LogClass.ServiceAudio, "Stubbed."); + + return 0; + } + + public long StopAudioRenderer(ServiceCtx Context) + { + Context.Ns.Log.PrintStub(LogClass.ServiceAudio, "Stubbed."); + + return 0; + } + + public long QuerySystemEvent(ServiceCtx Context) + { + int Handle = Context.Process.HandleTable.OpenHandle(UpdateEvent); + + Context.Response.HandleDesc = IpcHandleDesc.MakeCopy(Handle); + + return 0; + } + + private AdpcmDecoderContext GetAdpcmDecoderContext(long Position, long Size) + { + if (Size == 0) + { + return null; + } + + AdpcmDecoderContext Context = new AdpcmDecoderContext(); + + Context.Coefficients = new short[Size >> 1]; + + for (int Offset = 0; Offset < Size; Offset += 2) + { + Context.Coefficients[Offset >> 1] = Memory.ReadInt16(Position + Offset); + } + + return Context; + } + + private void UpdateAudio() + { + long[] Released = AudioOut.GetReleasedBuffers(Track, 2); + + for (int Index = 0; Index < Released.Length; Index++) + { + AppendMixedBuffer(Released[Index]); + } + } + + private void AppendMixedBuffer(long Tag) + { + int[] MixBuffer = new int[MixBufferSamplesCount * AudioConsts.HostChannelsCount]; + + foreach (VoiceContext Voice in Voices) + { + if (!Voice.Playing) + { + continue; + } + + int OutOffset = 0; + + int PendingSamples = MixBufferSamplesCount; + + while (PendingSamples > 0) + { + int[] Samples = Voice.GetBufferData(Memory, PendingSamples, out int ReturnedSamples); + + if (ReturnedSamples == 0) + { + break; + } + + PendingSamples -= ReturnedSamples; + + for (int Offset = 0; Offset < Samples.Length; Offset++) + { + int Sample = (int)(Samples[Offset] * Voice.Volume); + + MixBuffer[OutOffset++] += Sample; + } + } + } + + AudioOut.AppendBuffer(Track, Tag, GetFinalBuffer(MixBuffer)); + } + + private static short[] GetFinalBuffer(int[] Buffer) + { + short[] Output = new short[Buffer.Length]; + + for (int Offset = 0; Offset < Buffer.Length; Offset++) + { + Output[Offset] = DspUtils.Saturate(Buffer[Offset]); + } + + return Output; + } + + public void Dispose() + { + Dispose(true); + } + + protected virtual void Dispose(bool Disposing) + { + if (Disposing) + { + UpdateEvent.Dispose(); + } + } + } +} diff --git a/Ryujinx.HLE/OsHle/Services/Aud/AudioRenderer/MemoryPoolContext.cs b/Ryujinx.HLE/OsHle/Services/Aud/AudioRenderer/MemoryPoolContext.cs new file mode 100644 index 0000000000..b7af1d3f82 --- /dev/null +++ b/Ryujinx.HLE/OsHle/Services/Aud/AudioRenderer/MemoryPoolContext.cs @@ -0,0 +1,12 @@ +namespace Ryujinx.HLE.OsHle.Services.Aud.AudioRenderer +{ + class MemoryPoolContext + { + public MemoryPoolOut OutStatus; + + public MemoryPoolContext() + { + OutStatus.State = MemoryPoolState.Detached; + } + } +} diff --git a/Ryujinx.HLE/OsHle/Services/Aud/AudioRenderer/MemoryPoolIn.cs b/Ryujinx.HLE/OsHle/Services/Aud/AudioRenderer/MemoryPoolIn.cs new file mode 100644 index 0000000000..c852b5198a --- /dev/null +++ b/Ryujinx.HLE/OsHle/Services/Aud/AudioRenderer/MemoryPoolIn.cs @@ -0,0 +1,14 @@ +using System.Runtime.InteropServices; + +namespace Ryujinx.HLE.OsHle.Services.Aud.AudioRenderer +{ + [StructLayout(LayoutKind.Sequential, Size = 0x20, Pack = 4)] + struct MemoryPoolIn + { + public long Address; + public long Size; + public MemoryPoolState State; + public int Unknown14; + public long Unknown18; + } +} diff --git a/Ryujinx.HLE/OsHle/Services/Aud/AudioRenderer/MemoryPoolOut.cs b/Ryujinx.HLE/OsHle/Services/Aud/AudioRenderer/MemoryPoolOut.cs new file mode 100644 index 0000000000..dd65df8641 --- /dev/null +++ b/Ryujinx.HLE/OsHle/Services/Aud/AudioRenderer/MemoryPoolOut.cs @@ -0,0 +1,12 @@ +using System.Runtime.InteropServices; + +namespace Ryujinx.HLE.OsHle.Services.Aud.AudioRenderer +{ + [StructLayout(LayoutKind.Sequential, Size = 0x10, Pack = 4)] + struct MemoryPoolOut + { + public MemoryPoolState State; + public int Unknown14; + public long Unknown18; + } +} diff --git a/Ryujinx.HLE/OsHle/Services/Aud/MemoryPoolState.cs b/Ryujinx.HLE/OsHle/Services/Aud/AudioRenderer/MemoryPoolState.cs similarity index 80% rename from Ryujinx.HLE/OsHle/Services/Aud/MemoryPoolState.cs rename to Ryujinx.HLE/OsHle/Services/Aud/AudioRenderer/MemoryPoolState.cs index 892cde49e3..f96a0c09d7 100644 --- a/Ryujinx.HLE/OsHle/Services/Aud/MemoryPoolState.cs +++ b/Ryujinx.HLE/OsHle/Services/Aud/AudioRenderer/MemoryPoolState.cs @@ -1,4 +1,4 @@ -namespace Ryujinx.HLE.OsHle.Services.Aud +namespace Ryujinx.HLE.OsHle.Services.Aud.AudioRenderer { enum MemoryPoolState : int { diff --git a/Ryujinx.HLE/OsHle/Services/Aud/AudioRenderer/PlayState.cs b/Ryujinx.HLE/OsHle/Services/Aud/AudioRenderer/PlayState.cs new file mode 100644 index 0000000000..e8bcf64f3f --- /dev/null +++ b/Ryujinx.HLE/OsHle/Services/Aud/AudioRenderer/PlayState.cs @@ -0,0 +1,9 @@ +namespace Ryujinx.HLE.OsHle.Services.Aud.AudioRenderer +{ + enum PlayState : byte + { + Playing = 0, + Stopped = 1, + Paused = 2 + } +} diff --git a/Ryujinx.HLE/OsHle/Services/Aud/AudioRenderer/Resampler.cs b/Ryujinx.HLE/OsHle/Services/Aud/AudioRenderer/Resampler.cs new file mode 100644 index 0000000000..31e0ebecd9 --- /dev/null +++ b/Ryujinx.HLE/OsHle/Services/Aud/AudioRenderer/Resampler.cs @@ -0,0 +1,191 @@ +using System; + +namespace Ryujinx.HLE.OsHle.Services.Aud.AudioRenderer +{ + static class Resampler + { +#region "LookUp Tables" + private static short[] CurveLut0 = new short[] + { + 6600, 19426, 6722, 3, 6479, 19424, 6845, 9, 6359, 19419, 6968, 15, 6239, 19412, 7093, 22, + 6121, 19403, 7219, 28, 6004, 19391, 7345, 34, 5888, 19377, 7472, 41, 5773, 19361, 7600, 48, + 5659, 19342, 7728, 55, 5546, 19321, 7857, 62, 5434, 19298, 7987, 69, 5323, 19273, 8118, 77, + 5213, 19245, 8249, 84, 5104, 19215, 8381, 92, 4997, 19183, 8513, 101, 4890, 19148, 8646, 109, + 4785, 19112, 8780, 118, 4681, 19073, 8914, 127, 4579, 19031, 9048, 137, 4477, 18988, 9183, 147, + 4377, 18942, 9318, 157, 4277, 18895, 9454, 168, 4179, 18845, 9590, 179, 4083, 18793, 9726, 190, + 3987, 18738, 9863, 202, 3893, 18682, 10000, 215, 3800, 18624, 10137, 228, 3709, 18563, 10274, 241, + 3618, 18500, 10411, 255, 3529, 18436, 10549, 270, 3441, 18369, 10687, 285, 3355, 18300, 10824, 300, + 3269, 18230, 10962, 317, 3186, 18157, 11100, 334, 3103, 18082, 11238, 351, 3022, 18006, 11375, 369, + 2942, 17927, 11513, 388, 2863, 17847, 11650, 408, 2785, 17765, 11788, 428, 2709, 17681, 11925, 449, + 2635, 17595, 12062, 471, 2561, 17507, 12198, 494, 2489, 17418, 12334, 517, 2418, 17327, 12470, 541, + 2348, 17234, 12606, 566, 2280, 17140, 12741, 592, 2213, 17044, 12876, 619, 2147, 16946, 13010, 647, + 2083, 16846, 13144, 675, 2020, 16745, 13277, 704, 1958, 16643, 13409, 735, 1897, 16539, 13541, 766, + 1838, 16434, 13673, 798, 1780, 16327, 13803, 832, 1723, 16218, 13933, 866, 1667, 16109, 14062, 901, + 1613, 15998, 14191, 937, 1560, 15885, 14318, 975, 1508, 15772, 14445, 1013, 1457, 15657, 14571, 1052, + 1407, 15540, 14695, 1093, 1359, 15423, 14819, 1134, 1312, 15304, 14942, 1177, 1266, 15185, 15064, 1221, + 1221, 15064, 15185, 1266, 1177, 14942, 15304, 1312, 1134, 14819, 15423, 1359, 1093, 14695, 15540, 1407, + 1052, 14571, 15657, 1457, 1013, 14445, 15772, 1508, 975, 14318, 15885, 1560, 937, 14191, 15998, 1613, + 901, 14062, 16109, 1667, 866, 13933, 16218, 1723, 832, 13803, 16327, 1780, 798, 13673, 16434, 1838, + 766, 13541, 16539, 1897, 735, 13409, 16643, 1958, 704, 13277, 16745, 2020, 675, 13144, 16846, 2083, + 647, 13010, 16946, 2147, 619, 12876, 17044, 2213, 592, 12741, 17140, 2280, 566, 12606, 17234, 2348, + 541, 12470, 17327, 2418, 517, 12334, 17418, 2489, 494, 12198, 17507, 2561, 471, 12062, 17595, 2635, + 449, 11925, 17681, 2709, 428, 11788, 17765, 2785, 408, 11650, 17847, 2863, 388, 11513, 17927, 2942, + 369, 11375, 18006, 3022, 351, 11238, 18082, 3103, 334, 11100, 18157, 3186, 317, 10962, 18230, 3269, + 300, 10824, 18300, 3355, 285, 10687, 18369, 3441, 270, 10549, 18436, 3529, 255, 10411, 18500, 3618, + 241, 10274, 18563, 3709, 228, 10137, 18624, 3800, 215, 10000, 18682, 3893, 202, 9863, 18738, 3987, + 190, 9726, 18793, 4083, 179, 9590, 18845, 4179, 168, 9454, 18895, 4277, 157, 9318, 18942, 4377, + 147, 9183, 18988, 4477, 137, 9048, 19031, 4579, 127, 8914, 19073, 4681, 118, 8780, 19112, 4785, + 109, 8646, 19148, 4890, 101, 8513, 19183, 4997, 92, 8381, 19215, 5104, 84, 8249, 19245, 5213, + 77, 8118, 19273, 5323, 69, 7987, 19298, 5434, 62, 7857, 19321, 5546, 55, 7728, 19342, 5659, + 48, 7600, 19361, 5773, 41, 7472, 19377, 5888, 34, 7345, 19391, 6004, 28, 7219, 19403, 6121, + 22, 7093, 19412, 6239, 15, 6968, 19419, 6359, 9, 6845, 19424, 6479, 3, 6722, 19426, 6600 + }; + + private static short[] CurveLut1 = new short[] + { + -68, 32639, 69, -5, -200, 32630, 212, -15, -328, 32613, 359, -26, -450, 32586, 512, -36, + -568, 32551, 669, -47, -680, 32507, 832, -58, -788, 32454, 1000, -69, -891, 32393, 1174, -80, + -990, 32323, 1352, -92, -1084, 32244, 1536, -103, -1173, 32157, 1724, -115, -1258, 32061, 1919, -128, + -1338, 31956, 2118, -140, -1414, 31844, 2322, -153, -1486, 31723, 2532, -167, -1554, 31593, 2747, -180, + -1617, 31456, 2967, -194, -1676, 31310, 3192, -209, -1732, 31157, 3422, -224, -1783, 30995, 3657, -240, + -1830, 30826, 3897, -256, -1874, 30649, 4143, -272, -1914, 30464, 4393, -289, -1951, 30272, 4648, -307, + -1984, 30072, 4908, -325, -2014, 29866, 5172, -343, -2040, 29652, 5442, -362, -2063, 29431, 5716, -382, + -2083, 29203, 5994, -403, -2100, 28968, 6277, -424, -2114, 28727, 6565, -445, -2125, 28480, 6857, -468, + -2133, 28226, 7153, -490, -2139, 27966, 7453, -514, -2142, 27700, 7758, -538, -2142, 27428, 8066, -563, + -2141, 27151, 8378, -588, -2136, 26867, 8694, -614, -2130, 26579, 9013, -641, -2121, 26285, 9336, -668, + -2111, 25987, 9663, -696, -2098, 25683, 9993, -724, -2084, 25375, 10326, -753, -2067, 25063, 10662, -783, + -2049, 24746, 11000, -813, -2030, 24425, 11342, -844, -2009, 24100, 11686, -875, -1986, 23771, 12033, -907, + -1962, 23438, 12382, -939, -1937, 23103, 12733, -972, -1911, 22764, 13086, -1005, -1883, 22422, 13441, -1039, + -1855, 22077, 13798, -1072, -1825, 21729, 14156, -1107, -1795, 21380, 14516, -1141, -1764, 21027, 14877, -1176, + -1732, 20673, 15239, -1211, -1700, 20317, 15602, -1246, -1667, 19959, 15965, -1282, -1633, 19600, 16329, -1317, + -1599, 19239, 16694, -1353, -1564, 18878, 17058, -1388, -1530, 18515, 17423, -1424, -1495, 18151, 17787, -1459, + -1459, 17787, 18151, -1495, -1424, 17423, 18515, -1530, -1388, 17058, 18878, -1564, -1353, 16694, 19239, -1599, + -1317, 16329, 19600, -1633, -1282, 15965, 19959, -1667, -1246, 15602, 20317, -1700, -1211, 15239, 20673, -1732, + -1176, 14877, 21027, -1764, -1141, 14516, 21380, -1795, -1107, 14156, 21729, -1825, -1072, 13798, 22077, -1855, + -1039, 13441, 22422, -1883, -1005, 13086, 22764, -1911, -972, 12733, 23103, -1937, -939, 12382, 23438, -1962, + -907, 12033, 23771, -1986, -875, 11686, 24100, -2009, -844, 11342, 24425, -2030, -813, 11000, 24746, -2049, + -783, 10662, 25063, -2067, -753, 10326, 25375, -2084, -724, 9993, 25683, -2098, -696, 9663, 25987, -2111, + -668, 9336, 26285, -2121, -641, 9013, 26579, -2130, -614, 8694, 26867, -2136, -588, 8378, 27151, -2141, + -563, 8066, 27428, -2142, -538, 7758, 27700, -2142, -514, 7453, 27966, -2139, -490, 7153, 28226, -2133, + -468, 6857, 28480, -2125, -445, 6565, 28727, -2114, -424, 6277, 28968, -2100, -403, 5994, 29203, -2083, + -382, 5716, 29431, -2063, -362, 5442, 29652, -2040, -343, 5172, 29866, -2014, -325, 4908, 30072, -1984, + -307, 4648, 30272, -1951, -289, 4393, 30464, -1914, -272, 4143, 30649, -1874, -256, 3897, 30826, -1830, + -240, 3657, 30995, -1783, -224, 3422, 31157, -1732, -209, 3192, 31310, -1676, -194, 2967, 31456, -1617, + -180, 2747, 31593, -1554, -167, 2532, 31723, -1486, -153, 2322, 31844, -1414, -140, 2118, 31956, -1338, + -128, 1919, 32061, -1258, -115, 1724, 32157, -1173, -103, 1536, 32244, -1084, -92, 1352, 32323, -990, + -80, 1174, 32393, -891, -69, 1000, 32454, -788, -58, 832, 32507, -680, -47, 669, 32551, -568, + -36, 512, 32586, -450, -26, 359, 32613, -328, -15, 212, 32630, -200, -5, 69, 32639, -68 + }; + + private static short[] CurveLut2 = new short[] + { + 3195, 26287, 3329, -32, 3064, 26281, 3467, -34, 2936, 26270, 3608, -38, 2811, 26253, 3751, -42, + 2688, 26230, 3897, -46, 2568, 26202, 4046, -50, 2451, 26169, 4199, -54, 2338, 26130, 4354, -58, + 2227, 26085, 4512, -63, 2120, 26035, 4673, -67, 2015, 25980, 4837, -72, 1912, 25919, 5004, -76, + 1813, 25852, 5174, -81, 1716, 25780, 5347, -87, 1622, 25704, 5522, -92, 1531, 25621, 5701, -98, + 1442, 25533, 5882, -103, 1357, 25440, 6066, -109, 1274, 25342, 6253, -115, 1193, 25239, 6442, -121, + 1115, 25131, 6635, -127, 1040, 25018, 6830, -133, 967, 24899, 7027, -140, 897, 24776, 7227, -146, + 829, 24648, 7430, -153, 764, 24516, 7635, -159, 701, 24379, 7842, -166, 641, 24237, 8052, -174, + 583, 24091, 8264, -181, 526, 23940, 8478, -187, 472, 23785, 8695, -194, 420, 23626, 8914, -202, + 371, 23462, 9135, -209, 324, 23295, 9358, -215, 279, 23123, 9583, -222, 236, 22948, 9809, -230, + 194, 22769, 10038, -237, 154, 22586, 10269, -243, 117, 22399, 10501, -250, 81, 22208, 10735, -258, + 47, 22015, 10970, -265, 15, 21818, 11206, -271, -16, 21618, 11444, -277, -44, 21415, 11684, -283, + -71, 21208, 11924, -290, -97, 20999, 12166, -296, -121, 20786, 12409, -302, -143, 20571, 12653, -306, + -163, 20354, 12898, -311, -183, 20134, 13143, -316, -201, 19911, 13389, -321, -218, 19686, 13635, -325, + -234, 19459, 13882, -328, -248, 19230, 14130, -332, -261, 18998, 14377, -335, -273, 18765, 14625, -337, + -284, 18531, 14873, -339, -294, 18295, 15121, -341, -302, 18057, 15369, -341, -310, 17817, 15617, -341, + -317, 17577, 15864, -340, -323, 17335, 16111, -340, -328, 17092, 16357, -338, -332, 16848, 16603, -336, + -336, 16603, 16848, -332, -338, 16357, 17092, -328, -340, 16111, 17335, -323, -340, 15864, 17577, -317, + -341, 15617, 17817, -310, -341, 15369, 18057, -302, -341, 15121, 18295, -294, -339, 14873, 18531, -284, + -337, 14625, 18765, -273, -335, 14377, 18998, -261, -332, 14130, 19230, -248, -328, 13882, 19459, -234, + -325, 13635, 19686, -218, -321, 13389, 19911, -201, -316, 13143, 20134, -183, -311, 12898, 20354, -163, + -306, 12653, 20571, -143, -302, 12409, 20786, -121, -296, 12166, 20999, -97, -290, 11924, 21208, -71, + -283, 11684, 21415, -44, -277, 11444, 21618, -16, -271, 11206, 21818, 15, -265, 10970, 22015, 47, + -258, 10735, 22208, 81, -250, 10501, 22399, 117, -243, 10269, 22586, 154, -237, 10038, 22769, 194, + -230, 9809, 22948, 236, -222, 9583, 23123, 279, -215, 9358, 23295, 324, -209, 9135, 23462, 371, + -202, 8914, 23626, 420, -194, 8695, 23785, 472, -187, 8478, 23940, 526, -181, 8264, 24091, 583, + -174, 8052, 24237, 641, -166, 7842, 24379, 701, -159, 7635, 24516, 764, -153, 7430, 24648, 829, + -146, 7227, 24776, 897, -140, 7027, 24899, 967, -133, 6830, 25018, 1040, -127, 6635, 25131, 1115, + -121, 6442, 25239, 1193, -115, 6253, 25342, 1274, -109, 6066, 25440, 1357, -103, 5882, 25533, 1442, + -98, 5701, 25621, 1531, -92, 5522, 25704, 1622, -87, 5347, 25780, 1716, -81, 5174, 25852, 1813, + -76, 5004, 25919, 1912, -72, 4837, 25980, 2015, -67, 4673, 26035, 2120, -63, 4512, 26085, 2227, + -58, 4354, 26130, 2338, -54, 4199, 26169, 2451, -50, 4046, 26202, 2568, -46, 3897, 26230, 2688, + -42, 3751, 26253, 2811, -38, 3608, 26270, 2936, -34, 3467, 26281, 3064, -32, 3329, 26287, 3195 + }; +#endregion + + public static int[] Resample2Ch( + int[] Buffer, + int SrcSampleRate, + int DstSampleRate, + int SamplesCount, + ref int FracPart) + { + if (Buffer == null) + { + throw new ArgumentNullException(nameof(Buffer)); + } + + if (SrcSampleRate <= 0) + { + throw new ArgumentOutOfRangeException(nameof(SrcSampleRate)); + } + + if (DstSampleRate <= 0) + { + throw new ArgumentOutOfRangeException(nameof(DstSampleRate)); + } + + double Ratio = (double)SrcSampleRate / DstSampleRate; + + int NewSamplesCount = (int)(SamplesCount / Ratio); + + int Step = (int)(Ratio * 0x8000); + + int[] Output = new int[NewSamplesCount * 2]; + + short[] Lut; + + if (Step > 0xaaaa) + { + Lut = CurveLut0; + } + else if (Step <= 0x8000) + { + Lut = CurveLut1; + } + else + { + Lut = CurveLut2; + } + + int InOffs = 0; + + for (int OutOffs = 0; OutOffs < Output.Length; OutOffs += 2) + { + int LutIndex = (FracPart >> 8) * 4; + + int Sample0 = Buffer[(InOffs + 0) * 2 + 0] * Lut[LutIndex + 0] + + Buffer[(InOffs + 1) * 2 + 0] * Lut[LutIndex + 1] + + Buffer[(InOffs + 2) * 2 + 0] * Lut[LutIndex + 2] + + Buffer[(InOffs + 3) * 2 + 0] * Lut[LutIndex + 3]; + + int Sample1 = Buffer[(InOffs + 0) * 2 + 1] * Lut[LutIndex + 0] + + Buffer[(InOffs + 1) * 2 + 1] * Lut[LutIndex + 1] + + Buffer[(InOffs + 2) * 2 + 1] * Lut[LutIndex + 2] + + Buffer[(InOffs + 3) * 2 + 1] * Lut[LutIndex + 3]; + + int NewOffset = FracPart + Step; + + InOffs += NewOffset >> 15; + + FracPart = NewOffset & 0x7fff; + + Output[OutOffs + 0] = Sample0 >> 15; + Output[OutOffs + 1] = Sample1 >> 15; + } + + return Output; + } + } +} \ No newline at end of file diff --git a/Ryujinx.HLE/OsHle/Services/Aud/UpdateDataHeader.cs b/Ryujinx.HLE/OsHle/Services/Aud/AudioRenderer/UpdateDataHeader.cs similarity index 65% rename from Ryujinx.HLE/OsHle/Services/Aud/UpdateDataHeader.cs rename to Ryujinx.HLE/OsHle/Services/Aud/AudioRenderer/UpdateDataHeader.cs index f944b302e0..a6dfbc0bce 100644 --- a/Ryujinx.HLE/OsHle/Services/Aud/UpdateDataHeader.cs +++ b/Ryujinx.HLE/OsHle/Services/Aud/AudioRenderer/UpdateDataHeader.cs @@ -1,15 +1,15 @@ -namespace Ryujinx.HLE.OsHle.Services.Aud +namespace Ryujinx.HLE.OsHle.Services.Aud.AudioRenderer { struct UpdateDataHeader { public int Revision; public int BehaviorSize; - public int MemoryPoolsSize; - public int VoicesSize; + public int MemoryPoolSize; + public int VoiceSize; public int VoiceResourceSize; - public int EffectsSize; - public int MixesSize; - public int SinksSize; + public int EffectSize; + public int MixeSize; + public int SinkSize; public int PerformanceManagerSize; public int Unknown24; public int Unknown28; diff --git a/Ryujinx.HLE/OsHle/Services/Aud/AudioRenderer/VoiceChannelResourceIn.cs b/Ryujinx.HLE/OsHle/Services/Aud/AudioRenderer/VoiceChannelResourceIn.cs new file mode 100644 index 0000000000..0916b03e2e --- /dev/null +++ b/Ryujinx.HLE/OsHle/Services/Aud/AudioRenderer/VoiceChannelResourceIn.cs @@ -0,0 +1,10 @@ +using System.Runtime.InteropServices; + +namespace Ryujinx.HLE.OsHle.Services.Aud.AudioRenderer +{ + [StructLayout(LayoutKind.Sequential, Size = 0x70, Pack = 1)] + struct VoiceChannelResourceIn + { + //??? + } +} diff --git a/Ryujinx.HLE/OsHle/Services/Aud/AudioRenderer/VoiceContext.cs b/Ryujinx.HLE/OsHle/Services/Aud/AudioRenderer/VoiceContext.cs new file mode 100644 index 0000000000..1bf9ed73e8 --- /dev/null +++ b/Ryujinx.HLE/OsHle/Services/Aud/AudioRenderer/VoiceContext.cs @@ -0,0 +1,188 @@ +using ChocolArm64.Memory; +using Ryujinx.Audio.Adpcm; +using System; + +namespace Ryujinx.HLE.OsHle.Services.Aud.AudioRenderer +{ + class VoiceContext + { + private bool Acquired; + private bool BufferReload; + + private int ResamplerFracPart; + + private int BufferIndex; + private int Offset; + + public int SampleRate; + public int ChannelsCount; + + public float Volume; + + public PlayState PlayState; + + public SampleFormat SampleFormat; + + public AdpcmDecoderContext AdpcmCtx; + + public WaveBuffer[] WaveBuffers; + + public VoiceOut OutStatus; + + private int[] Samples; + + public bool Playing => Acquired && PlayState == PlayState.Playing; + + public VoiceContext() + { + WaveBuffers = new WaveBuffer[4]; + } + + public void SetAcquireState(bool NewState) + { + if (Acquired && !NewState) + { + //Release. + Reset(); + } + + Acquired = NewState; + } + + private void Reset() + { + BufferReload = true; + + BufferIndex = 0; + Offset = 0; + + OutStatus.PlayedSamplesCount = 0; + OutStatus.PlayedWaveBuffersCount = 0; + OutStatus.VoiceDropsCount = 0; + } + + public int[] GetBufferData(AMemory Memory, int MaxSamples, out int SamplesCount) + { + if (!Playing) + { + SamplesCount = 0; + + return null; + } + + if (BufferReload) + { + BufferReload = false; + + UpdateBuffer(Memory); + } + + WaveBuffer Wb = WaveBuffers[BufferIndex]; + + int MaxSize = Samples.Length - Offset; + + int Size = MaxSamples * AudioConsts.HostChannelsCount; + + if (Size > MaxSize) + { + Size = MaxSize; + } + + int[] Output = new int[Size]; + + Array.Copy(Samples, Offset, Output, 0, Size); + + SamplesCount = Size / AudioConsts.HostChannelsCount; + + OutStatus.PlayedSamplesCount += SamplesCount; + + Offset += Size; + + if (Offset == Samples.Length) + { + Offset = 0; + + if (Wb.Looping == 0) + { + SetBufferIndex((BufferIndex + 1) & 3); + } + + OutStatus.PlayedWaveBuffersCount++; + + if (Wb.LastBuffer != 0) + { + PlayState = PlayState.Paused; + } + } + + return Output; + } + + private void UpdateBuffer(AMemory Memory) + { + //TODO: Implement conversion for formats other + //than interleaved stereo (2 channels). + //As of now, it assumes that HostChannelsCount == 2. + WaveBuffer Wb = WaveBuffers[BufferIndex]; + + if (SampleFormat == SampleFormat.PcmInt16) + { + int SamplesCount = (int)(Wb.Size / (sizeof(short) * ChannelsCount)); + + Samples = new int[SamplesCount * AudioConsts.HostChannelsCount]; + + if (ChannelsCount == 1) + { + for (int Index = 0; Index < SamplesCount; Index++) + { + short Sample = Memory.ReadInt16(Wb.Position + Index * 2); + + Samples[Index * 2 + 0] = Sample; + Samples[Index * 2 + 1] = Sample; + } + } + else + { + for (int Index = 0; Index < SamplesCount * 2; Index++) + { + Samples[Index] = Memory.ReadInt16(Wb.Position + Index * 2); + } + } + } + else if (SampleFormat == SampleFormat.Adpcm) + { + byte[] Buffer = Memory.ReadBytes(Wb.Position, Wb.Size); + + Samples = AdpcmDecoder.Decode(Buffer, AdpcmCtx); + } + else + { + throw new InvalidOperationException(); + } + + if (SampleRate != AudioConsts.HostSampleRate) + { + //TODO: We should keep the frames being discarded (see the 4 below) + //on a buffer and include it on the next samples buffer, to allow + //the resampler to do seamless interpolation between wave buffers. + int SamplesCount = Samples.Length / AudioConsts.HostChannelsCount; + + SamplesCount = Math.Max(SamplesCount - 4, 0); + + Samples = Resampler.Resample2Ch( + Samples, + SampleRate, + AudioConsts.HostSampleRate, + SamplesCount, + ref ResamplerFracPart); + } + } + + public void SetBufferIndex(int Index) + { + BufferIndex = Index & 3; + + BufferReload = true; + } + } +} diff --git a/Ryujinx.HLE/OsHle/Services/Aud/AudioRenderer/VoiceIn.cs b/Ryujinx.HLE/OsHle/Services/Aud/AudioRenderer/VoiceIn.cs new file mode 100644 index 0000000000..790affb20e --- /dev/null +++ b/Ryujinx.HLE/OsHle/Services/Aud/AudioRenderer/VoiceIn.cs @@ -0,0 +1,49 @@ +using System.Runtime.InteropServices; + +namespace Ryujinx.HLE.OsHle.Services.Aud.AudioRenderer +{ + [StructLayout(LayoutKind.Sequential, Size = 0x170, Pack = 1)] + struct VoiceIn + { + public int VoiceSlot; + public int NodeId; + + public byte FirstUpdate; + public byte Acquired; + + public PlayState PlayState; + + public SampleFormat SampleFormat; + + public int SampleRate; + + public int Priority; + + public int Unknown14; + + public int ChannelsCount; + + public float Pitch; + public float Volume; + + public BiquadFilter BiquadFilter0; + public BiquadFilter BiquadFilter1; + + public int AppendedWaveBuffersCount; + + public int BaseWaveBufferIndex; + + public int Unknown44; + + public long AdpcmCoeffsPosition; + public long AdpcmCoeffsSize; + + public int VoiceDestination; + public int Padding; + + public WaveBuffer WaveBuffer0; + public WaveBuffer WaveBuffer1; + public WaveBuffer WaveBuffer2; + public WaveBuffer WaveBuffer3; + } +} diff --git a/Ryujinx.HLE/OsHle/Services/Aud/AudioRenderer/VoiceOut.cs b/Ryujinx.HLE/OsHle/Services/Aud/AudioRenderer/VoiceOut.cs new file mode 100644 index 0000000000..1fcf929f27 --- /dev/null +++ b/Ryujinx.HLE/OsHle/Services/Aud/AudioRenderer/VoiceOut.cs @@ -0,0 +1,12 @@ +using System.Runtime.InteropServices; + +namespace Ryujinx.HLE.OsHle.Services.Aud.AudioRenderer +{ + [StructLayout(LayoutKind.Sequential, Size = 0x10, Pack = 4)] + struct VoiceOut + { + public long PlayedSamplesCount; + public int PlayedWaveBuffersCount; + public int VoiceDropsCount; //? + } +} diff --git a/Ryujinx.HLE/OsHle/Services/Aud/AudioRenderer/WaveBuffer.cs b/Ryujinx.HLE/OsHle/Services/Aud/AudioRenderer/WaveBuffer.cs new file mode 100644 index 0000000000..6b56b9081a --- /dev/null +++ b/Ryujinx.HLE/OsHle/Services/Aud/AudioRenderer/WaveBuffer.cs @@ -0,0 +1,20 @@ +using System.Runtime.InteropServices; + +namespace Ryujinx.HLE.OsHle.Services.Aud.AudioRenderer +{ + [StructLayout(LayoutKind.Sequential, Size = 0x38, Pack = 1)] + struct WaveBuffer + { + public long Position; + public long Size; + public int FirstSampleOffset; + public int LastSampleOffset; + public byte Looping; + public byte LastBuffer; + public short Unknown1A; + public int Unknown1C; + public long AdpcmLoopContextPosition; + public long AdpcmLoopContextSize; + public long Unknown30; + } +} diff --git a/Ryujinx.HLE/OsHle/Services/Aud/AudioRendererParameter.cs b/Ryujinx.HLE/OsHle/Services/Aud/AudioRendererParameter.cs index 0a0792ec5b..d7e1df01ac 100644 --- a/Ryujinx.HLE/OsHle/Services/Aud/AudioRendererParameter.cs +++ b/Ryujinx.HLE/OsHle/Services/Aud/AudioRendererParameter.cs @@ -8,14 +8,14 @@ namespace Ryujinx.HLE.OsHle.Services.Aud public int SampleRate; public int SampleCount; public int Unknown8; - public int UnknownC; + public int MixCount; public int VoiceCount; public int SinkCount; public int EffectCount; - public int Unknown1C; - public int Unknown20; + public int PerformanceManagerCount; + public int VoiceDropEnable; public int SplitterCount; - public int Unknown28; + public int SplitterDestinationDataCount; public int Unknown2C; public int Revision; } diff --git a/Ryujinx.HLE/OsHle/Services/Aud/IAudioOutManager.cs b/Ryujinx.HLE/OsHle/Services/Aud/IAudioOutManager.cs index 8c78d1d493..7a3bc4d4fa 100644 --- a/Ryujinx.HLE/OsHle/Services/Aud/IAudioOutManager.cs +++ b/Ryujinx.HLE/OsHle/Services/Aud/IAudioOutManager.cs @@ -3,6 +3,7 @@ using Ryujinx.Audio; using Ryujinx.HLE.Logging; using Ryujinx.HLE.OsHle.Handles; using Ryujinx.HLE.OsHle.Ipc; +using Ryujinx.HLE.OsHle.Services.Aud.AudioOut; using System.Collections.Generic; using System.Text; @@ -154,13 +155,13 @@ namespace Ryujinx.HLE.OsHle.Services.Aud IAalOutput AudioOut = Context.Ns.AudioOut; - int Track = AudioOut.OpenTrack(SampleRate, Channels, Callback, out AudioFormat Format); + int Track = AudioOut.OpenTrack(SampleRate, Channels, Callback); MakeObject(Context, new IAudioOut(AudioOut, ReleaseEvent, Track)); Context.ResponseData.Write(SampleRate); Context.ResponseData.Write(Channels); - Context.ResponseData.Write((int)Format); + Context.ResponseData.Write((int)SampleFormat.PcmInt16); Context.ResponseData.Write((int)PlaybackState.Stopped); return 0; diff --git a/Ryujinx.HLE/OsHle/Services/Aud/IAudioRenderer.cs b/Ryujinx.HLE/OsHle/Services/Aud/IAudioRenderer.cs deleted file mode 100644 index bd9188c341..0000000000 --- a/Ryujinx.HLE/OsHle/Services/Aud/IAudioRenderer.cs +++ /dev/null @@ -1,136 +0,0 @@ -using ChocolArm64.Memory; -using Ryujinx.HLE.Logging; -using Ryujinx.HLE.OsHle.Handles; -using Ryujinx.HLE.OsHle.Ipc; -using System; -using System.Collections.Generic; -using System.Runtime.InteropServices; - -namespace Ryujinx.HLE.OsHle.Services.Aud -{ - class IAudioRenderer : IpcService, IDisposable - { - private Dictionary m_Commands; - - public override IReadOnlyDictionary Commands => m_Commands; - - private KEvent UpdateEvent; - - private AudioRendererParameter Params; - - public IAudioRenderer(AudioRendererParameter Params) - { - m_Commands = new Dictionary() - { - { 4, RequestUpdateAudioRenderer }, - { 5, StartAudioRenderer }, - { 6, StopAudioRenderer }, - { 7, QuerySystemEvent } - }; - - UpdateEvent = new KEvent(); - - this.Params = Params; - } - - public long RequestUpdateAudioRenderer(ServiceCtx Context) - { - long OutputPosition = Context.Request.ReceiveBuff[0].Position; - long OutputSize = Context.Request.ReceiveBuff[0].Size; - - AMemoryHelper.FillWithZeros(Context.Memory, OutputPosition, (int)OutputSize); - - long InputPosition = Context.Request.SendBuff[0].Position; - - UpdateDataHeader InputDataHeader = AMemoryHelper.Read(Context.Memory, InputPosition); - - UpdateDataHeader OutputDataHeader = new UpdateDataHeader(); - - int UpdateHeaderSize = Marshal.SizeOf(); - - OutputDataHeader.Revision = Params.Revision; - OutputDataHeader.BehaviorSize = 0xb0; - OutputDataHeader.MemoryPoolsSize = (Params.EffectCount + Params.VoiceCount * 4) * 0x10; - OutputDataHeader.VoicesSize = Params.VoiceCount * 0x10; - OutputDataHeader.EffectsSize = Params.EffectCount * 0x10; - OutputDataHeader.SinksSize = Params.SinkCount * 0x20; - OutputDataHeader.PerformanceManagerSize = 0x10; - OutputDataHeader.TotalSize = UpdateHeaderSize + - OutputDataHeader.BehaviorSize + - OutputDataHeader.MemoryPoolsSize + - OutputDataHeader.VoicesSize + - OutputDataHeader.EffectsSize + - OutputDataHeader.SinksSize + - OutputDataHeader.PerformanceManagerSize; - - AMemoryHelper.Write(Context.Memory, OutputPosition, OutputDataHeader); - - int InMemoryPoolOffset = UpdateHeaderSize + InputDataHeader.BehaviorSize; - - int OutMemoryPoolOffset = UpdateHeaderSize; - - for (int Offset = 0; Offset < OutputDataHeader.MemoryPoolsSize; Offset += 0x10, InMemoryPoolOffset += 0x20) - { - MemoryPoolState PoolState = (MemoryPoolState)Context.Memory.ReadInt32(InputPosition + InMemoryPoolOffset + 0x10); - - //TODO: Figure out what the other values does. - if (PoolState == MemoryPoolState.RequestAttach) - { - Context.Memory.WriteInt32(OutputPosition + OutMemoryPoolOffset + Offset, (int)MemoryPoolState.Attached); - } - else if (PoolState == MemoryPoolState.RequestDetach) - { - Context.Memory.WriteInt32(OutputPosition + OutMemoryPoolOffset + Offset, (int)MemoryPoolState.Detached); - } - } - - int OutVoicesOffset = OutMemoryPoolOffset + OutputDataHeader.MemoryPoolsSize; - - for (int Offset = 0; Offset < OutputDataHeader.VoicesSize; Offset += 0x10) - { - Context.Memory.WriteInt32(OutputPosition + OutVoicesOffset + Offset + 8, (int)VoicePlaybackState.Finished); - } - - //TODO: We shouldn't be signaling this here. - UpdateEvent.WaitEvent.Set(); - - return 0; - } - - public long StartAudioRenderer(ServiceCtx Context) - { - Context.Ns.Log.PrintStub(LogClass.ServiceAudio, "Stubbed."); - - return 0; - } - - public long StopAudioRenderer(ServiceCtx Context) - { - Context.Ns.Log.PrintStub(LogClass.ServiceAudio, "Stubbed."); - - return 0; - } - - public long QuerySystemEvent(ServiceCtx Context) - { - int Handle = Context.Process.HandleTable.OpenHandle(UpdateEvent); - - Context.Response.HandleDesc = IpcHandleDesc.MakeCopy(Handle); - - return 0; - } - - public void Dispose() - { - Dispose(true); - } - - protected virtual void Dispose(bool Disposing) - { - if (Disposing) - { - UpdateEvent.Dispose(); - } - } - } -} diff --git a/Ryujinx.HLE/OsHle/Services/Aud/IAudioRendererManager.cs b/Ryujinx.HLE/OsHle/Services/Aud/IAudioRendererManager.cs index a7daeedd58..021c363577 100644 --- a/Ryujinx.HLE/OsHle/Services/Aud/IAudioRendererManager.cs +++ b/Ryujinx.HLE/OsHle/Services/Aud/IAudioRendererManager.cs @@ -1,7 +1,11 @@ +using Ryujinx.Audio; using Ryujinx.HLE.Logging; using Ryujinx.HLE.OsHle.Ipc; +using Ryujinx.HLE.OsHle.Services.Aud.AudioRenderer; +using Ryujinx.HLE.OsHle.Utilities; using System.Collections.Generic; -using System.Runtime.InteropServices; + +using static Ryujinx.HLE.OsHle.ErrorCode; namespace Ryujinx.HLE.OsHle.Services.Aud { @@ -12,6 +16,10 @@ namespace Ryujinx.HLE.OsHle.Services.Aud ('V' << 16) | ('0' << 24); + private const int Rev = 4; + + public const int RevMagic = Rev0Magic + Rev; + private Dictionary m_Commands; public override IReadOnlyDictionary Commands => m_Commands; @@ -28,76 +36,69 @@ namespace Ryujinx.HLE.OsHle.Services.Aud public long OpenAudioRenderer(ServiceCtx Context) { - //Same buffer as GetAudioRendererWorkBufferSize is receive here. + IAalOutput AudioOut = Context.Ns.AudioOut; - AudioRendererParameter Params = new AudioRendererParameter(); + AudioRendererParameter Params = GetAudioRendererParameter(Context); - Params.SampleRate = Context.RequestData.ReadInt32(); - Params.SampleCount = Context.RequestData.ReadInt32(); - Params.Unknown8 = Context.RequestData.ReadInt32(); - Params.UnknownC = Context.RequestData.ReadInt32(); - Params.VoiceCount = Context.RequestData.ReadInt32(); - Params.SinkCount = Context.RequestData.ReadInt32(); - Params.EffectCount = Context.RequestData.ReadInt32(); - Params.Unknown1C = Context.RequestData.ReadInt32(); - Params.Unknown20 = Context.RequestData.ReadInt32(); - Params.SplitterCount = Context.RequestData.ReadInt32(); - Params.Unknown28 = Context.RequestData.ReadInt32(); - Params.Unknown2C = Context.RequestData.ReadInt32(); - Params.Revision = Context.RequestData.ReadInt32(); - - MakeObject(Context, new IAudioRenderer(Params)); + MakeObject(Context, new IAudioRenderer(Context.Memory, AudioOut, Params)); return 0; } public long GetAudioRendererWorkBufferSize(ServiceCtx Context) { - long SampleRate = Context.RequestData.ReadUInt32(); - long Unknown4 = Context.RequestData.ReadUInt32(); - long Unknown8 = Context.RequestData.ReadUInt32(); - long UnknownC = Context.RequestData.ReadUInt32(); - long Unknown10 = Context.RequestData.ReadUInt32(); //VoiceCount - long Unknown14 = Context.RequestData.ReadUInt32(); //SinkCount - long Unknown18 = Context.RequestData.ReadUInt32(); //EffectCount - long Unknown1c = Context.RequestData.ReadUInt32(); //Boolean - long Unknown20 = Context.RequestData.ReadUInt32(); //Not used here in FW3.0.1 - Boolean - long Unknown24 = Context.RequestData.ReadUInt32(); - long Unknown28 = Context.RequestData.ReadUInt32(); //SplitterCount - long Unknown2c = Context.RequestData.ReadUInt32(); //Not used here in FW3.0.1 - int RevMagic = Context.RequestData.ReadInt32(); + AudioRendererParameter Params = GetAudioRendererParameter(Context); - int Version = (RevMagic - Rev0Magic) >> 24; + int Revision = (Params.Revision - Rev0Magic) >> 24; - if (Version <= 3) //REV3 Max is supported + if (Revision <= Rev) //REV3 Max is supported { - long Size = RoundUp(Unknown8 * 4, 64); - Size += (UnknownC << 10); - Size += (UnknownC + 1) * 0x940; - Size += Unknown10 * 0x3F0; - Size += RoundUp((UnknownC + 1) * 8, 16); - Size += RoundUp(Unknown10 * 8, 16); - Size += RoundUp((0x3C0 * (Unknown14 + UnknownC) + 4 * Unknown4) * (Unknown8 + 6), 64); - Size += 0x2C0 * (Unknown14 + UnknownC) + 0x30 * (Unknown18 + (4 * Unknown10)) + 0x50; + bool IsSplitterSupported = Revision >= 3; - if (Version >= 3) //IsSplitterSupported + long Size; + + Size = IntUtils.AlignUp(Params.Unknown8 * 4, 64); + Size += Params.MixCount * 0x400; + Size += (Params.MixCount + 1) * 0x940; + Size += Params.VoiceCount * 0x3F0; + Size += IntUtils.AlignUp((Params.MixCount + 1) * 8, 16); + Size += IntUtils.AlignUp(Params.VoiceCount * 8, 16); + Size += IntUtils.AlignUp( + ((Params.SinkCount + Params.MixCount) * 0x3C0 + Params.SampleCount * 4) * + (Params.Unknown8 + 6), 64); + Size += (Params.SinkCount + Params.MixCount) * 0x2C0; + Size += (Params.EffectCount + 4 * Params.VoiceCount) * 0x30 + 0x50; + + if (IsSplitterSupported) { - Size += RoundUp((NodeStatesGetWorkBufferSize((int)UnknownC + 1) + EdgeMatrixGetWorkBufferSize((int)UnknownC + 1)), 16); - Size += 0xE0 * Unknown28 + 0x20 * Unknown24 + RoundUp(Unknown28 * 4, 16); + Size += IntUtils.AlignUp(( + NodeStatesGetWorkBufferSize(Params.MixCount + 1) + + EdgeMatrixGetWorkBufferSize(Params.MixCount + 1)), 16); + + Size += Params.SplitterDestinationDataCount * 0xE0; + Size += Params.SplitterCount * 0x20; + Size += IntUtils.AlignUp(Params.SplitterDestinationDataCount * 4, 16); } - Size = 0x4C0 * Unknown18 + RoundUp(Size, 64) + 0x170 * Unknown14 + ((Unknown10 << 8) | 0x40); + Size = Params.EffectCount * 0x4C0 + + Params.SinkCount * 0x170 + + Params.VoiceCount * 0x100 + + IntUtils.AlignUp(Size, 64) + 0x40; - if (Unknown1c >= 1) + if (Params.PerformanceManagerCount >= 1) { - Size += ((((Unknown18 + Unknown14 + Unknown10 + UnknownC + 1) * 16) + 0x658) * (Unknown1c + 1) + 0x13F) & ~0x3FL; + Size += (((Params.EffectCount + + Params.SinkCount + + Params.VoiceCount + + Params.MixCount + 1) * 16 + 0x658) * + (Params.PerformanceManagerCount + 1) + 0x13F) & ~0x3FL; } - long WorkBufferSize = (Size + 0x1907D) & ~0xFFFL; + Size = (Size + 0x1907D) & ~0xFFFL; - Context.ResponseData.Write(WorkBufferSize); + Context.ResponseData.Write(Size); - Context.Ns.Log.PrintDebug(LogClass.ServiceAudio, $"WorkBufferSize is 0x{WorkBufferSize:x16}."); + Context.Ns.Log.PrintDebug(LogClass.ServiceAudio, $"WorkBufferSize is 0x{Size:x16}."); return 0; } @@ -105,20 +106,36 @@ namespace Ryujinx.HLE.OsHle.Services.Aud { Context.ResponseData.Write(0L); - Context.Ns.Log.PrintError(LogClass.ServiceAudio, $"Library Revision 0x{RevMagic:x8} is not supported!"); + Context.Ns.Log.PrintWarning(LogClass.ServiceAudio, $"Library Revision 0x{Params.Revision:x8} is not supported!"); - return 0x499; + return MakeError(ErrorModule.Audio, AudErr.UnsupportedRevision); } } - private static long RoundUp(long Value, int Size) + private AudioRendererParameter GetAudioRendererParameter(ServiceCtx Context) { - return (Value + (Size - 1)) & ~((long)Size - 1); + AudioRendererParameter Params = new AudioRendererParameter(); + + Params.SampleRate = Context.RequestData.ReadInt32(); + Params.SampleCount = Context.RequestData.ReadInt32(); + Params.Unknown8 = Context.RequestData.ReadInt32(); + Params.MixCount = Context.RequestData.ReadInt32(); + Params.VoiceCount = Context.RequestData.ReadInt32(); + Params.SinkCount = Context.RequestData.ReadInt32(); + Params.EffectCount = Context.RequestData.ReadInt32(); + Params.PerformanceManagerCount = Context.RequestData.ReadInt32(); + Params.VoiceDropEnable = Context.RequestData.ReadInt32(); + Params.SplitterCount = Context.RequestData.ReadInt32(); + Params.SplitterDestinationDataCount = Context.RequestData.ReadInt32(); + Params.Unknown2C = Context.RequestData.ReadInt32(); + Params.Revision = Context.RequestData.ReadInt32(); + + return Params; } private static int NodeStatesGetWorkBufferSize(int Value) { - int Result = (int)RoundUp(Value, 64); + int Result = IntUtils.AlignUp(Value, 64); if (Result < 0) { @@ -130,7 +147,7 @@ namespace Ryujinx.HLE.OsHle.Services.Aud private static int EdgeMatrixGetWorkBufferSize(int Value) { - int Result = (int)RoundUp(Value * Value, 64); + int Result = IntUtils.AlignUp(Value * Value, 64); if (Result < 0) { diff --git a/Ryujinx.Audio/AudioFormat.cs b/Ryujinx.HLE/OsHle/Services/Aud/SampleFormat.cs similarity index 52% rename from Ryujinx.Audio/AudioFormat.cs rename to Ryujinx.HLE/OsHle/Services/Aud/SampleFormat.cs index 8250d1368e..06ab492996 100644 --- a/Ryujinx.Audio/AudioFormat.cs +++ b/Ryujinx.HLE/OsHle/Services/Aud/SampleFormat.cs @@ -1,12 +1,12 @@ -namespace Ryujinx.Audio +namespace Ryujinx.HLE.OsHle.Services.Aud { - public enum AudioFormat + enum SampleFormat : byte { Invalid = 0, PcmInt8 = 1, PcmInt16 = 2, - PcmImt24 = 3, - PcmImt32 = 4, + PcmInt24 = 3, + PcmInt32 = 4, PcmFloat = 5, Adpcm = 6 } diff --git a/Ryujinx.HLE/OsHle/Services/Aud/VoiceState.cs b/Ryujinx.HLE/OsHle/Services/Aud/VoiceState.cs deleted file mode 100644 index 8b34332392..0000000000 --- a/Ryujinx.HLE/OsHle/Services/Aud/VoiceState.cs +++ /dev/null @@ -1,9 +0,0 @@ -namespace Ryujinx.HLE.OsHle.Services.Aud -{ - enum VoicePlaybackState : int - { - Playing = 0, - Finished = 1, - Paused = 2 - } -} diff --git a/Ryujinx.HLE/OsHle/Services/Nv/NvMap/NvMapIoctl.cs b/Ryujinx.HLE/OsHle/Services/Nv/NvMap/NvMapIoctl.cs index ec10a37588..4c6107f8f8 100644 --- a/Ryujinx.HLE/OsHle/Services/Nv/NvMap/NvMapIoctl.cs +++ b/Ryujinx.HLE/OsHle/Services/Nv/NvMap/NvMapIoctl.cs @@ -48,7 +48,7 @@ namespace Ryujinx.HLE.OsHle.Services.Nv.NvMap return NvResult.InvalidInput; } - int Size = IntUtils.RoundUp(Args.Size, NvGpuVmm.PageSize); + int Size = IntUtils.AlignUp(Args.Size, NvGpuVmm.PageSize); Args.Handle = AddNvMap(Context, new NvMapHandle(Size)); @@ -121,7 +121,7 @@ namespace Ryujinx.HLE.OsHle.Services.Nv.NvMap Map.Align = Args.Align; Map.Kind = (byte)Args.Kind; - int Size = IntUtils.RoundUp(Map.Size, NvGpuVmm.PageSize); + int Size = IntUtils.AlignUp(Map.Size, NvGpuVmm.PageSize); long Address = Args.Address; diff --git a/Ryujinx.HLE/OsHle/Utilities/IntUtils.cs b/Ryujinx.HLE/OsHle/Utilities/IntUtils.cs index ba0726c317..39cced280a 100644 --- a/Ryujinx.HLE/OsHle/Utilities/IntUtils.cs +++ b/Ryujinx.HLE/OsHle/Utilities/IntUtils.cs @@ -2,12 +2,12 @@ namespace Ryujinx.HLE.OsHle.Utilities { static class IntUtils { - public static int RoundUp(int Value, int Size) + public static int AlignUp(int Value, int Size) { return (Value + (Size - 1)) & ~(Size - 1); } - public static long RoundUp(long Value, int Size) + public static long AlignUp(long Value, int Size) { return (Value + (Size - 1)) & ~((long)Size - 1); } diff --git a/Ryujinx.HLE/OsHle/Utilities/StructReader.cs b/Ryujinx.HLE/OsHle/Utilities/StructReader.cs new file mode 100644 index 0000000000..e218288b6e --- /dev/null +++ b/Ryujinx.HLE/OsHle/Utilities/StructReader.cs @@ -0,0 +1,45 @@ +using ChocolArm64.Memory; +using System.Runtime.InteropServices; + +namespace Ryujinx.HLE.OsHle.Utilities +{ + class StructReader + { + private AMemory Memory; + + public long Position { get; private set; } + + public StructReader(AMemory Memory, long Position) + { + this.Memory = Memory; + this.Position = Position; + } + + public T Read() where T : struct + { + T Value = AMemoryHelper.Read(Memory, Position); + + Position += Marshal.SizeOf(); + + return Value; + } + + public T[] Read(int Size) where T : struct + { + int StructSize = Marshal.SizeOf(); + + int Count = Size / StructSize; + + T[] Output = new T[Count]; + + for (int Index = 0; Index < Count; Index++) + { + Output[Index] = AMemoryHelper.Read(Memory, Position); + + Position += StructSize; + } + + return Output; + } + } +} diff --git a/Ryujinx.HLE/OsHle/Utilities/StructWriter.cs b/Ryujinx.HLE/OsHle/Utilities/StructWriter.cs new file mode 100644 index 0000000000..7daa95fb63 --- /dev/null +++ b/Ryujinx.HLE/OsHle/Utilities/StructWriter.cs @@ -0,0 +1,25 @@ +using ChocolArm64.Memory; +using System.Runtime.InteropServices; + +namespace Ryujinx.HLE.OsHle.Utilities +{ + class StructWriter + { + private AMemory Memory; + + public long Position { get; private set; } + + public StructWriter(AMemory Memory, long Position) + { + this.Memory = Memory; + this.Position = Position; + } + + public void Write(T Value) where T : struct + { + AMemoryHelper.Write(Memory, Position, Value); + + Position += Marshal.SizeOf(); + } + } +} From 21e590c3abe64a0cbe4e51ebcc9f4bb0f945fc00 Mon Sep 17 00:00:00 2001 From: gdkchan Date: Sun, 15 Jul 2018 00:04:46 -0300 Subject: [PATCH 02/33] Add support for ioctl2, SetTimeout and KickoffPbWithAttr (#261) * Add support for ioctl2, SetTimeout and KickoffPbWithAttr * Call UnloadProcess on NvHostChannelIoctl aswell --- .../OsHle/Services/Nv/INvDrvServices.cs | 17 +-- .../Services/Nv/NvHostChannel/NvChannel.cs | 7 ++ .../Nv/NvHostChannel/NvChannelName.cs | 7 ++ .../Nv/NvHostChannel/NvHostChannelIoctl.cs | 116 +++++++++++++++--- .../NvHostChannelSubmitGpfifo.cs | 2 +- 5 files changed, 123 insertions(+), 26 deletions(-) create mode 100644 Ryujinx.HLE/OsHle/Services/Nv/NvHostChannel/NvChannel.cs create mode 100644 Ryujinx.HLE/OsHle/Services/Nv/NvHostChannel/NvChannelName.cs diff --git a/Ryujinx.HLE/OsHle/Services/Nv/INvDrvServices.cs b/Ryujinx.HLE/OsHle/Services/Nv/INvDrvServices.cs index 5c1748bdb4..4654d15f65 100644 --- a/Ryujinx.HLE/OsHle/Services/Nv/INvDrvServices.cs +++ b/Ryujinx.HLE/OsHle/Services/Nv/INvDrvServices.cs @@ -23,11 +23,11 @@ namespace Ryujinx.HLE.OsHle.Services.Nv private static Dictionary IoctlProcessors = new Dictionary() { - { "/dev/nvhost-as-gpu", ProcessIoctlNvGpuAS }, - { "/dev/nvhost-ctrl", ProcessIoctlNvHostCtrl }, - { "/dev/nvhost-ctrl-gpu", ProcessIoctlNvGpuGpu }, - { "/dev/nvhost-gpu", ProcessIoctlNvHostChannel }, - { "/dev/nvmap", ProcessIoctlNvMap } + { "/dev/nvhost-as-gpu", ProcessIoctlNvGpuAS }, + { "/dev/nvhost-ctrl", ProcessIoctlNvHostCtrl }, + { "/dev/nvhost-ctrl-gpu", ProcessIoctlNvGpuGpu }, + { "/dev/nvhost-gpu", ProcessIoctlNvHostGpu }, + { "/dev/nvmap", ProcessIoctlNvMap } }; public static GlobalStateTable Fds { get; private set; } @@ -44,6 +44,7 @@ namespace Ryujinx.HLE.OsHle.Services.Nv { 3, Initialize }, { 4, QueryEvent }, { 8, SetClientPid }, + { 11, Ioctl }, { 13, FinishInitialize } }; @@ -162,9 +163,9 @@ namespace Ryujinx.HLE.OsHle.Services.Nv return ProcessIoctl(Context, Cmd, NvGpuGpuIoctl.ProcessIoctl); } - private static int ProcessIoctlNvHostChannel(ServiceCtx Context, int Cmd) + private static int ProcessIoctlNvHostGpu(ServiceCtx Context, int Cmd) { - return ProcessIoctl(Context, Cmd, NvHostChannelIoctl.ProcessIoctl); + return ProcessIoctl(Context, Cmd, NvHostChannelIoctl.ProcessIoctlGpu); } private static int ProcessIoctlNvMap(ServiceCtx Context, int Cmd) @@ -207,6 +208,8 @@ namespace Ryujinx.HLE.OsHle.Services.Nv NvGpuASIoctl.UnloadProcess(Process); + NvHostChannelIoctl.UnloadProcess(Process); + NvHostCtrlIoctl.UnloadProcess(Process); NvMapIoctl.UnloadProcess(Process); diff --git a/Ryujinx.HLE/OsHle/Services/Nv/NvHostChannel/NvChannel.cs b/Ryujinx.HLE/OsHle/Services/Nv/NvHostChannel/NvChannel.cs new file mode 100644 index 0000000000..486c38069e --- /dev/null +++ b/Ryujinx.HLE/OsHle/Services/Nv/NvHostChannel/NvChannel.cs @@ -0,0 +1,7 @@ +namespace Ryujinx.HLE.OsHle.Services.Nv.NvHostChannel +{ + class NvChannel + { + public int Timeout; + } +} \ No newline at end of file diff --git a/Ryujinx.HLE/OsHle/Services/Nv/NvHostChannel/NvChannelName.cs b/Ryujinx.HLE/OsHle/Services/Nv/NvHostChannel/NvChannelName.cs new file mode 100644 index 0000000000..a46a6d9874 --- /dev/null +++ b/Ryujinx.HLE/OsHle/Services/Nv/NvHostChannel/NvChannelName.cs @@ -0,0 +1,7 @@ +namespace Ryujinx.HLE.OsHle.Services.Nv.NvHostChannel +{ + enum NvChannelName + { + Gpu + } +} \ No newline at end of file diff --git a/Ryujinx.HLE/OsHle/Services/Nv/NvHostChannel/NvHostChannelIoctl.cs b/Ryujinx.HLE/OsHle/Services/Nv/NvHostChannel/NvHostChannelIoctl.cs index 8f3d3cd7d6..411a579a2f 100644 --- a/Ryujinx.HLE/OsHle/Services/Nv/NvHostChannel/NvHostChannelIoctl.cs +++ b/Ryujinx.HLE/OsHle/Services/Nv/NvHostChannel/NvHostChannelIoctl.cs @@ -3,23 +3,50 @@ using Ryujinx.HLE.Gpu.Memory; using Ryujinx.HLE.Logging; using Ryujinx.HLE.OsHle.Services.Nv.NvGpuAS; using System; +using System.Collections.Concurrent; namespace Ryujinx.HLE.OsHle.Services.Nv.NvHostChannel { class NvHostChannelIoctl { - public static int ProcessIoctl(ServiceCtx Context, int Cmd) + private class ChannelsPerProcess + { + public ConcurrentDictionary Channels { get; private set; } + + public ChannelsPerProcess() + { + Channels = new ConcurrentDictionary(); + + Channels.TryAdd(NvChannelName.Gpu, new NvChannel()); + } + } + + private static ConcurrentDictionary Channels; + + static NvHostChannelIoctl() + { + Channels = new ConcurrentDictionary(); + } + + public static int ProcessIoctlGpu(ServiceCtx Context, int Cmd) + { + return ProcessIoctl(Context, NvChannelName.Gpu, Cmd); + } + + public static int ProcessIoctl(ServiceCtx Context, NvChannelName Channel, int Cmd) { switch (Cmd & 0xffff) { - case 0x4714: return SetUserData (Context); - case 0x4801: return SetNvMap (Context); - case 0x4808: return SubmitGpfifo (Context); - case 0x4809: return AllocObjCtx (Context); - case 0x480b: return ZcullBind (Context); - case 0x480c: return SetErrorNotifier(Context); - case 0x480d: return SetPriority (Context); - case 0x481a: return AllocGpfifoEx2 (Context); + case 0x4714: return SetUserData (Context); + case 0x4801: return SetNvMap (Context); + case 0x4803: return SetTimeout (Context, Channel); + case 0x4808: return SubmitGpfifo (Context); + case 0x4809: return AllocObjCtx (Context); + case 0x480b: return ZcullBind (Context); + case 0x480c: return SetErrorNotifier (Context); + case 0x480d: return SetPriority (Context); + case 0x481a: return AllocGpfifoEx2 (Context); + case 0x481b: return KickoffPbWithAttr(Context); } throw new NotImplementedException(Cmd.ToString("x8")); @@ -45,6 +72,15 @@ namespace Ryujinx.HLE.OsHle.Services.Nv.NvHostChannel return NvResult.Success; } + private static int SetTimeout(ServiceCtx Context, NvChannelName Channel) + { + long InputPosition = Context.Request.GetBufferType0x21().Position; + + GetChannel(Context, Channel).Timeout = Context.Memory.ReadInt32(InputPosition); + + return NvResult.Success; + } + private static int SubmitGpfifo(ServiceCtx Context) { long InputPosition = Context.Request.GetBufferType0x21().Position; @@ -58,15 +94,7 @@ namespace Ryujinx.HLE.OsHle.Services.Nv.NvHostChannel { long Gpfifo = Context.Memory.ReadInt64(InputPosition + 0x18 + Index * 8); - long VA = Gpfifo & 0xff_ffff_ffff; - - int Size = (int)(Gpfifo >> 40) & 0x7ffffc; - - byte[] Data = Vmm.ReadBytes(VA, Size); - - NvGpuPBEntry[] PushBuffer = NvGpuPushBuffer.Decode(Data); - - Context.Ns.Gpu.Fifo.PushBuffer(Vmm, PushBuffer); + PushGpfifo(Context, Vmm, Gpfifo); } Args.SyncptId = 0; @@ -126,5 +154,57 @@ namespace Ryujinx.HLE.OsHle.Services.Nv.NvHostChannel return NvResult.Success; } + + private static int KickoffPbWithAttr(ServiceCtx Context) + { + long InputPosition = Context.Request.GetBufferType0x21().Position; + long OutputPosition = Context.Request.GetBufferType0x22().Position; + + NvHostChannelSubmitGpfifo Args = AMemoryHelper.Read(Context.Memory, InputPosition); + + NvGpuVmm Vmm = NvGpuASIoctl.GetVmm(Context); + + for (int Index = 0; Index < Args.NumEntries; Index++) + { + long Gpfifo = Context.Memory.ReadInt64(Args.Address + Index * 8); + + PushGpfifo(Context, Vmm, Gpfifo); + } + + Args.SyncptId = 0; + Args.SyncptValue = 0; + + AMemoryHelper.Write(Context.Memory, OutputPosition, Args); + + return NvResult.Success; + } + + private static void PushGpfifo(ServiceCtx Context, NvGpuVmm Vmm, long Gpfifo) + { + long VA = Gpfifo & 0xff_ffff_ffff; + + int Size = (int)(Gpfifo >> 40) & 0x7ffffc; + + byte[] Data = Vmm.ReadBytes(VA, Size); + + NvGpuPBEntry[] PushBuffer = NvGpuPushBuffer.Decode(Data); + + Context.Ns.Gpu.Fifo.PushBuffer(Vmm, PushBuffer); + } + + public static NvChannel GetChannel(ServiceCtx Context, NvChannelName Channel) + { + ChannelsPerProcess Cpp = Channels.GetOrAdd(Context.Process, (Key) => + { + return new ChannelsPerProcess(); + }); + + return Cpp.Channels[Channel]; + } + + public static void UnloadProcess(Process Process) + { + Channels.TryRemove(Process, out _); + } } } \ No newline at end of file diff --git a/Ryujinx.HLE/OsHle/Services/Nv/NvHostChannel/NvHostChannelSubmitGpfifo.cs b/Ryujinx.HLE/OsHle/Services/Nv/NvHostChannel/NvHostChannelSubmitGpfifo.cs index 9541f7018c..ee945839ea 100644 --- a/Ryujinx.HLE/OsHle/Services/Nv/NvHostChannel/NvHostChannelSubmitGpfifo.cs +++ b/Ryujinx.HLE/OsHle/Services/Nv/NvHostChannel/NvHostChannelSubmitGpfifo.cs @@ -2,7 +2,7 @@ namespace Ryujinx.HLE.OsHle.Services.Nv.NvHostChannel { struct NvHostChannelSubmitGpfifo { - public long Gpfifo; + public long Address; public int NumEntries; public int Flags; public int SyncptId; From 8652dbb57ca3a111c73b779a6f38ec24cc32c2f5 Mon Sep 17 00:00:00 2001 From: gdkchan Date: Sun, 15 Jul 2018 00:34:12 -0300 Subject: [PATCH 03/33] Small nit on GetAudioRendererWorkBufferSize --- Ryujinx.HLE/OsHle/Services/Aud/IAudioRendererManager.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Ryujinx.HLE/OsHle/Services/Aud/IAudioRendererManager.cs b/Ryujinx.HLE/OsHle/Services/Aud/IAudioRendererManager.cs index 021c363577..a71abebe3c 100644 --- a/Ryujinx.HLE/OsHle/Services/Aud/IAudioRendererManager.cs +++ b/Ryujinx.HLE/OsHle/Services/Aud/IAudioRendererManager.cs @@ -67,7 +67,7 @@ namespace Ryujinx.HLE.OsHle.Services.Aud ((Params.SinkCount + Params.MixCount) * 0x3C0 + Params.SampleCount * 4) * (Params.Unknown8 + 6), 64); Size += (Params.SinkCount + Params.MixCount) * 0x2C0; - Size += (Params.EffectCount + 4 * Params.VoiceCount) * 0x30 + 0x50; + Size += (Params.EffectCount + Params.VoiceCount * 4) * 0x30 + 0x50; if (IsSplitterSupported) { From a48ed788bf0a0fa678ea0259271058cabec8e1b9 Mon Sep 17 00:00:00 2001 From: gdkchan Date: Sun, 15 Jul 2018 00:37:30 -0300 Subject: [PATCH 04/33] Remove outdated comment --- Ryujinx.HLE/OsHle/Services/Aud/IAudioRendererManager.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Ryujinx.HLE/OsHle/Services/Aud/IAudioRendererManager.cs b/Ryujinx.HLE/OsHle/Services/Aud/IAudioRendererManager.cs index a71abebe3c..700b365561 100644 --- a/Ryujinx.HLE/OsHle/Services/Aud/IAudioRendererManager.cs +++ b/Ryujinx.HLE/OsHle/Services/Aud/IAudioRendererManager.cs @@ -51,7 +51,7 @@ namespace Ryujinx.HLE.OsHle.Services.Aud int Revision = (Params.Revision - Rev0Magic) >> 24; - if (Revision <= Rev) //REV3 Max is supported + if (Revision <= Rev) { bool IsSplitterSupported = Revision >= 3; From 50b706e2baef0a7a80af94de51fd9e3bd31ae1ff Mon Sep 17 00:00:00 2001 From: gdkchan Date: Sun, 15 Jul 2018 00:42:59 -0300 Subject: [PATCH 05/33] Fix RevMagic on audren --- Ryujinx.HLE/OsHle/Services/Aud/IAudioRendererManager.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Ryujinx.HLE/OsHle/Services/Aud/IAudioRendererManager.cs b/Ryujinx.HLE/OsHle/Services/Aud/IAudioRendererManager.cs index 700b365561..5e2dec9f2e 100644 --- a/Ryujinx.HLE/OsHle/Services/Aud/IAudioRendererManager.cs +++ b/Ryujinx.HLE/OsHle/Services/Aud/IAudioRendererManager.cs @@ -18,7 +18,7 @@ namespace Ryujinx.HLE.OsHle.Services.Aud private const int Rev = 4; - public const int RevMagic = Rev0Magic + Rev; + public const int RevMagic = Rev0Magic + (Rev << 24); private Dictionary m_Commands; From 063fae50fe25388d10e9ec1915c561dc0f4d519d Mon Sep 17 00:00:00 2001 From: LDj3SNuD <35856442+LDj3SNuD@users.noreply.github.com> Date: Sun, 15 Jul 2018 05:53:26 +0200 Subject: [PATCH 06/33] Fix EmitHighNarrow(), EmitSaturatingNarrowOp() when Rd == Rn || Rd == Rm (& Part != 0). Optimization of EmitVectorTranspose(), EmitVectorUnzip(), EmitVectorZip() algorithms (reduction of the number of operations and their complexity). Add 12 Tests about Trn1/2, Uzp1/2, Zip1/2 (V) instructions. (#268) * Update CpuTestSimdArithmetic.cs * Update CpuTestSimd.cs * Update CpuTestSimdReg.cs * Update Instructions.cs * Update AInstEmitSimdArithmetic.cs * Update AInstEmitSimdHelper.cs * Update AInstEmitSimdMove.cs * Delete CpuTestSimdMove.cs --- .../Instruction/AInstEmitSimdArithmetic.cs | 12 +- .../Instruction/AInstEmitSimdHelper.cs | 12 +- ChocolArm64/Instruction/AInstEmitSimdMove.cs | 47 +- Ryujinx.Tests/Cpu/CpuTestSimd.cs | 913 +++--- Ryujinx.Tests/Cpu/CpuTestSimdArithmetic.cs | 41 +- Ryujinx.Tests/Cpu/CpuTestSimdMove.cs | 136 - Ryujinx.Tests/Cpu/CpuTestSimdReg.cs | 2532 ++++++++++------- Ryujinx.Tests/Cpu/Tester/Instructions.cs | 208 ++ 8 files changed, 2385 insertions(+), 1516 deletions(-) delete mode 100644 Ryujinx.Tests/Cpu/CpuTestSimdMove.cs diff --git a/ChocolArm64/Instruction/AInstEmitSimdArithmetic.cs b/ChocolArm64/Instruction/AInstEmitSimdArithmetic.cs index a39ffc093e..36bb1cbf16 100644 --- a/ChocolArm64/Instruction/AInstEmitSimdArithmetic.cs +++ b/ChocolArm64/Instruction/AInstEmitSimdArithmetic.cs @@ -163,12 +163,19 @@ namespace ChocolArm64.Instruction AOpCodeSimdReg Op = (AOpCodeSimdReg)Context.CurrOp; int Elems = 8 >> Op.Size; + int ESize = 8 << Op.Size; int Part = Op.RegisterSize == ARegisterSize.SIMD128 ? Elems : 0; long RoundConst = 1L << (ESize - 1); + if (Part != 0) + { + Context.EmitLdvec(Op.Rd); + Context.EmitStvectmp(); + } + for (int Index = 0; Index < Elems; Index++) { EmitVectorExtractZx(Context, Op.Rn, Index, Op.Size + 1); @@ -185,9 +192,12 @@ namespace ChocolArm64.Instruction Context.EmitLsr(ESize); - EmitVectorInsert(Context, Op.Rd, Part + Index, Op.Size); + EmitVectorInsertTmp(Context, Part + Index, Op.Size); } + Context.EmitLdvectmp(); + Context.EmitStvec(Op.Rd); + if (Part == 0) { EmitVectorZeroUpper(Context, Op.Rd); diff --git a/ChocolArm64/Instruction/AInstEmitSimdHelper.cs b/ChocolArm64/Instruction/AInstEmitSimdHelper.cs index 1f7a2dad13..7716e2987a 100644 --- a/ChocolArm64/Instruction/AInstEmitSimdHelper.cs +++ b/ChocolArm64/Instruction/AInstEmitSimdHelper.cs @@ -813,6 +813,7 @@ namespace ChocolArm64.Instruction AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp; int Elems = !Scalar ? 8 >> Op.Size : 1; + int ESize = 8 << Op.Size; int Part = !Scalar && (Op.RegisterSize == ARegisterSize.SIMD128) ? Elems : 0; @@ -823,6 +824,12 @@ namespace ChocolArm64.Instruction Context.EmitLdc_I8(0L); Context.EmitSttmp(); + if (Part != 0) + { + Context.EmitLdvec(Op.Rd); + Context.EmitStvectmp(); + } + for (int Index = 0; Index < Elems; Index++) { AILLabel LblLe = new AILLabel(); @@ -867,9 +874,12 @@ namespace ChocolArm64.Instruction EmitVectorZeroLower(Context, Op.Rd); } - EmitVectorInsert(Context, Op.Rd, Part + Index, Op.Size); + EmitVectorInsertTmp(Context, Part + Index, Op.Size); } + Context.EmitLdvectmp(); + Context.EmitStvec(Op.Rd); + if (Part == 0) { EmitVectorZeroUpper(Context, Op.Rd); diff --git a/ChocolArm64/Instruction/AInstEmitSimdMove.cs b/ChocolArm64/Instruction/AInstEmitSimdMove.cs index 739f01c62b..592cab733e 100644 --- a/ChocolArm64/Instruction/AInstEmitSimdMove.cs +++ b/ChocolArm64/Instruction/AInstEmitSimdMove.cs @@ -331,17 +331,18 @@ namespace ChocolArm64.Instruction { AOpCodeSimdReg Op = (AOpCodeSimdReg)Context.CurrOp; - int Bytes = Op.GetBitsCount() >> 3; + int Words = Op.GetBitsCount() >> 4; + int Pairs = Words >> Op.Size; - int Elems = Bytes >> Op.Size; - - for (int Index = 0; Index < Elems; Index++) + for (int Index = 0; Index < Pairs; Index++) { - int Elem = (Index & ~1) + Part; + int Idx = Index << 1; - EmitVectorExtractZx(Context, (Index & 1) == 0 ? Op.Rn : Op.Rm, Elem, Op.Size); + EmitVectorExtractZx(Context, Op.Rn, Idx + Part, Op.Size); + EmitVectorExtractZx(Context, Op.Rm, Idx + Part, Op.Size); - EmitVectorInsertTmp(Context, Index, Op.Size); + EmitVectorInsertTmp(Context, Idx + 1, Op.Size); + EmitVectorInsertTmp(Context, Idx , Op.Size); } Context.EmitLdvectmp(); @@ -357,18 +358,18 @@ namespace ChocolArm64.Instruction { AOpCodeSimdReg Op = (AOpCodeSimdReg)Context.CurrOp; - int Bytes = Op.GetBitsCount() >> 3; + int Words = Op.GetBitsCount() >> 4; + int Pairs = Words >> Op.Size; - int Elems = Bytes >> Op.Size; - int Half = Elems >> 1; - - for (int Index = 0; Index < Elems; Index++) + for (int Index = 0; Index < Pairs; Index++) { - int Elem = Part + ((Index & (Half - 1)) << 1); + int Idx = Index << 1; - EmitVectorExtractZx(Context, Index < Half ? Op.Rn : Op.Rm, Elem, Op.Size); + EmitVectorExtractZx(Context, Op.Rn, Idx + Part, Op.Size); + EmitVectorExtractZx(Context, Op.Rm, Idx + Part, Op.Size); - EmitVectorInsertTmp(Context, Index, Op.Size); + EmitVectorInsertTmp(Context, Pairs + Index, Op.Size); + EmitVectorInsertTmp(Context, Index, Op.Size); } Context.EmitLdvectmp(); @@ -384,18 +385,20 @@ namespace ChocolArm64.Instruction { AOpCodeSimdReg Op = (AOpCodeSimdReg)Context.CurrOp; - int Bytes = Op.GetBitsCount() >> 3; + int Words = Op.GetBitsCount() >> 4; + int Pairs = Words >> Op.Size; - int Elems = Bytes >> Op.Size; - int Half = Elems >> 1; + int Base = Part != 0 ? Pairs : 0; - for (int Index = 0; Index < Elems; Index++) + for (int Index = 0; Index < Pairs; Index++) { - int Elem = Part * Half + (Index >> 1); + int Idx = Index << 1; - EmitVectorExtractZx(Context, (Index & 1) == 0 ? Op.Rn : Op.Rm, Elem, Op.Size); + EmitVectorExtractZx(Context, Op.Rn, Base + Index, Op.Size); + EmitVectorExtractZx(Context, Op.Rm, Base + Index, Op.Size); - EmitVectorInsertTmp(Context, Index, Op.Size); + EmitVectorInsertTmp(Context, Idx + 1, Op.Size); + EmitVectorInsertTmp(Context, Idx , Op.Size); } Context.EmitLdvectmp(); diff --git a/Ryujinx.Tests/Cpu/CpuTestSimd.cs b/Ryujinx.Tests/Cpu/CpuTestSimd.cs index 02c5b25b24..b84d29575d 100644 --- a/Ryujinx.Tests/Cpu/CpuTestSimd.cs +++ b/Ryujinx.Tests/Cpu/CpuTestSimd.cs @@ -76,64 +76,53 @@ namespace Ryujinx.Tests.Cpu } #endregion + private const int RndCnt = 1; + [Test, Description("ABS , ")] - public void Abs_S_D([ValueSource("_1D_")] [Random(1)] ulong A) + 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) { - uint Opcode = 0x5EE0B820; // ABS D0, D1 + uint Opcode = 0x5EE0B800; // ABS D0, D0 + Opcode |= ((Rn & 31) << 5) | ((Rd & 31) << 0); Bits Op = new Bits(Opcode); - Vector128 V0 = MakeVectorE1(TestContext.CurrentContext.Random.NextULong()); + Vector128 V0 = MakeVectorE0E1(Z, Z); Vector128 V1 = MakeVectorE0(A); AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1); + AArch64.Vpart(0, 0, new Bits(Z)); AArch64.Vpart(0, 1, new Bits(Z)); AArch64.V(1, new Bits(A)); SimdFp.Abs_S(Op[23, 22], Op[9, 5], Op[4, 0]); Assert.Multiple(() => { - Assert.That(GetVectorE0(ThreadState.V0), Is.EqualTo(AArch64.V(64, 0).ToUInt64())); - Assert.That(GetVectorE1(ThreadState.V0), Is.Zero); + 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())); }); } [Test, Description("ABS ., .")] - public void Abs_V_8B_4H_2S([ValueSource("_8B4H2S_")] [Random(1)] ulong A, + 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, [Values(0b00u, 0b01u, 0b10u)] uint size) // <8B, 4H, 2S> { - uint Opcode = 0x0E20B820; // ABS V0.8B, V1.8B + uint Opcode = 0x0E20B800; // ABS V0.8B, V0.8B + Opcode |= ((Rn & 31) << 5) | ((Rd & 31) << 0); Opcode |= ((size & 3) << 22); Bits Op = new Bits(Opcode); - Vector128 V0 = MakeVectorE1(TestContext.CurrentContext.Random.NextULong()); + Vector128 V0 = MakeVectorE0E1(Z, Z); Vector128 V1 = MakeVectorE0(A); AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1); + AArch64.Vpart(0, 0, new Bits(Z)); AArch64.Vpart(0, 1, new Bits(Z)); AArch64.V(1, new Bits(A)); SimdFp.Abs_V(Op[30], Op[23, 22], Op[9, 5], Op[4, 0]); - Assert.Multiple(() => - { - Assert.That(GetVectorE0(ThreadState.V0), Is.EqualTo(AArch64.V(64, 0).ToUInt64())); - Assert.That(GetVectorE1(ThreadState.V0), Is.Zero); - }); - } - - [Test, Pairwise, Description("ABS ., .")] - public void Abs_V_16B_8H_4S_2D([ValueSource("_8B4H2S1D_")] [Random(1)] ulong A0, - [ValueSource("_8B4H2S1D_")] [Random(1)] ulong A1, - [Values(0b00u, 0b01u, 0b10u, 0b11u)] uint size) // <16B, 8H, 4S, 2D> - { - uint Opcode = 0x4E20B820; // ABS V0.16B, V1.16B - Opcode |= ((size & 3) << 22); - Bits Op = new Bits(Opcode); - - Vector128 V1 = MakeVectorE0E1(A0, A1); - AThreadState ThreadState = SingleOpcode(Opcode, V1: V1); - - AArch64.Vpart(1, 0, new Bits(A0)); - AArch64.Vpart(1, 1, new Bits(A1)); - SimdFp.Abs_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())); @@ -141,114 +130,157 @@ namespace Ryujinx.Tests.Cpu }); } - [Test, Pairwise, Description("ADDP , .")] - public void Addp_S_2DD([ValueSource("_1D_")] [Random(1)] ulong A0, - [ValueSource("_1D_")] [Random(1)] ulong A1) + [Test, Description("ABS ., .")] + 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, + [Values(0b00u, 0b01u, 0b10u, 0b11u)] uint size) // <16B, 8H, 4S, 2D> { - uint Opcode = 0x5EF1B820; // ADDP D0, V1.2D + uint Opcode = 0x4E20B800; // ABS V0.16B, V0.16B + Opcode |= ((Rn & 31) << 5) | ((Rd & 31) << 0); + Opcode |= ((size & 3) << 22); Bits Op = new Bits(Opcode); - Vector128 V0 = MakeVectorE1(TestContext.CurrentContext.Random.NextULong()); - Vector128 V1 = MakeVectorE0E1(A0, A1); + Vector128 V0 = MakeVectorE0E1(Z, Z); + Vector128 V1 = MakeVectorE0E1(A, A); AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1); - AArch64.Vpart(1, 0, new Bits(A0)); - AArch64.Vpart(1, 1, new Bits(A1)); + 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)); + SimdFp.Abs_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())); + }); + } + + [Test, Description("ADDP , .")] + 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) + { + uint Opcode = 0x5EF1B800; // ADDP D0, V0.2D + Opcode |= ((Rn & 31) << 5) | ((Rd & 31) << 0); + Bits Op = new Bits(Opcode); + + Vector128 V0 = MakeVectorE0E1(Z, Z); + Vector128 V1 = MakeVectorE0E1(A, A); + AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1); + + 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)); SimdFp.Addp_S(Op[23, 22], Op[9, 5], Op[4, 0]); Assert.Multiple(() => { - Assert.That(GetVectorE0(ThreadState.V0), Is.EqualTo(AArch64.V(64, 0).ToUInt64())); - Assert.That(GetVectorE1(ThreadState.V0), Is.Zero); + 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())); }); } [Test, Description("ADDV , .")] - public void Addv_V_8BB_4HH([ValueSource("_8B4H_")] [Random(1)] ulong A, - [Values(0b00u, 0b01u)] uint size) // <8B, 4H> + 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, + [Values(0b00u, 0b01u)] uint size) // <8BB, 4HH> { - uint Opcode = 0x0E31B820; // ADDV B0, V1.8B + uint Opcode = 0x0E31B800; // ADDV B0, V0.8B + Opcode |= ((Rn & 31) << 5) | ((Rd & 31) << 0); Opcode |= ((size & 3) << 22); Bits Op = new Bits(Opcode); - Vector128 V0 = MakeVectorE0E1(TestContext.CurrentContext.Random.NextULong(), - TestContext.CurrentContext.Random.NextULong()); + Vector128 V0 = MakeVectorE0E1(Z, Z); Vector128 V1 = MakeVectorE0(A); AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1); - AArch64.Vpart(0, 0, new Bits(TestContext.CurrentContext.Random.NextULong())); + AArch64.Vpart(0, 0, new Bits(Z)); AArch64.Vpart(0, 1, new Bits(Z)); AArch64.V(1, new Bits(A)); SimdFp.Addv_V(Op[30], Op[23, 22], Op[9, 5], Op[4, 0]); Assert.Multiple(() => { - Assert.That(GetVectorE0(ThreadState.V0), Is.EqualTo(AArch64.V(64, 0).ToUInt64())); - Assert.That(GetVectorE1(ThreadState.V0), Is.Zero); + 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())); }); } - [Test, Pairwise, Description("ADDV , .")] - public void Addv_V_16BB_8HH_4SS([ValueSource("_8B4H2S_")] [Random(1)] ulong A0, - [ValueSource("_8B4H2S_")] [Random(1)] ulong A1, - [Values(0b00u, 0b01u, 0b10u)] uint size) // <16B, 8H, 4S> + [Test, Description("ADDV , .")] + 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, + [Values(0b00u, 0b01u, 0b10u)] uint size) // <16BB, 8HH, 4SS> { - uint Opcode = 0x4E31B820; // ADDV B0, V1.16B + uint Opcode = 0x4E31B800; // ADDV B0, V0.16B + Opcode |= ((Rn & 31) << 5) | ((Rd & 31) << 0); Opcode |= ((size & 3) << 22); Bits Op = new Bits(Opcode); - Vector128 V0 = MakeVectorE0E1(TestContext.CurrentContext.Random.NextULong(), - TestContext.CurrentContext.Random.NextULong()); - Vector128 V1 = MakeVectorE0E1(A0, A1); + Vector128 V0 = MakeVectorE0E1(Z, Z); + Vector128 V1 = MakeVectorE0E1(A, A); AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1); - AArch64.Vpart(0, 0, new Bits(TestContext.CurrentContext.Random.NextULong())); - AArch64.Vpart(1, 0, new Bits(A0)); - AArch64.Vpart(1, 1, new Bits(A1)); + 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)); SimdFp.Addv_V(Op[30], Op[23, 22], Op[9, 5], Op[4, 0]); Assert.Multiple(() => { - Assert.That(GetVectorE0(ThreadState.V0), Is.EqualTo(AArch64.V(64, 0).ToUInt64())); - Assert.That(GetVectorE1(ThreadState.V0), Is.Zero); + 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())); }); } [Test, Description("CLS ., .")] - public void Cls_V_8B_4H_2S([ValueSource("_8B4H2S_")] [Random(1)] ulong A, + 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, [Values(0b00u, 0b01u, 0b10u)] uint size) // <8B, 4H, 2S> { - uint Opcode = 0x0E204820; // CLS V0.8B, V1.8B + uint Opcode = 0x0E204800; // CLS V0.8B, V0.8B + Opcode |= ((Rn & 31) << 5) | ((Rd & 31) << 0); Opcode |= ((size & 3) << 22); Bits Op = new Bits(Opcode); - Vector128 V0 = MakeVectorE1(TestContext.CurrentContext.Random.NextULong()); + Vector128 V0 = MakeVectorE0E1(Z, Z); Vector128 V1 = MakeVectorE0(A); AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1); + AArch64.Vpart(0, 0, new Bits(Z)); AArch64.Vpart(0, 1, new Bits(Z)); AArch64.V(1, new Bits(A)); SimdFp.Cls_V(Op[30], Op[23, 22], Op[9, 5], Op[4, 0]); Assert.Multiple(() => { - Assert.That(GetVectorE0(ThreadState.V0), Is.EqualTo(AArch64.V(64, 0).ToUInt64())); - Assert.That(GetVectorE1(ThreadState.V0), Is.Zero); + 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())); }); } - [Test, Pairwise, Description("CLS ., .")] - public void Cls_V_16B_8H_4S([ValueSource("_8B4H2S_")] [Random(1)] ulong A0, - [ValueSource("_8B4H2S_")] [Random(1)] ulong A1, + [Test, Description("CLS ., .")] + 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, [Values(0b00u, 0b01u, 0b10u)] uint size) // <16B, 8H, 4S> { - uint Opcode = 0x4E204820; // CLS V0.16B, V1.16B + uint Opcode = 0x4E204800; // CLS V0.16B, V0.16B + Opcode |= ((Rn & 31) << 5) | ((Rd & 31) << 0); Opcode |= ((size & 3) << 22); Bits Op = new Bits(Opcode); - Vector128 V1 = MakeVectorE0E1(A0, A1); - AThreadState ThreadState = SingleOpcode(Opcode, V1: V1); + Vector128 V0 = MakeVectorE0E1(Z, Z); + Vector128 V1 = MakeVectorE0E1(A, A); + AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1); - AArch64.Vpart(1, 0, new Bits(A0)); - AArch64.Vpart(1, 1, new Bits(A1)); + 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)); SimdFp.Cls_V(Op[30], Op[23, 22], Op[9, 5], Op[4, 0]); Assert.Multiple(() => @@ -259,41 +291,50 @@ namespace Ryujinx.Tests.Cpu } [Test, Description("CLZ ., .")] - public void Clz_V_8B_4H_2S([ValueSource("_8B4H2S_")] [Random(1)] ulong A, + 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, [Values(0b00u, 0b01u, 0b10u)] uint size) // <8B, 4H, 2S> { - uint Opcode = 0x2E204820; // CLZ V0.8B, V1.8B + uint Opcode = 0x2E204800; // CLZ V0.8B, V0.8B + Opcode |= ((Rn & 31) << 5) | ((Rd & 31) << 0); Opcode |= ((size & 3) << 22); Bits Op = new Bits(Opcode); - Vector128 V0 = MakeVectorE1(TestContext.CurrentContext.Random.NextULong()); + Vector128 V0 = MakeVectorE0E1(Z, Z); Vector128 V1 = MakeVectorE0(A); AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1); + AArch64.Vpart(0, 0, new Bits(Z)); AArch64.Vpart(0, 1, new Bits(Z)); AArch64.V(1, new Bits(A)); SimdFp.Clz_V(Op[30], Op[23, 22], Op[9, 5], Op[4, 0]); Assert.Multiple(() => { - Assert.That(GetVectorE0(ThreadState.V0), Is.EqualTo(AArch64.V(64, 0).ToUInt64())); - Assert.That(GetVectorE1(ThreadState.V0), Is.Zero); + 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())); }); } - [Test, Pairwise, Description("CLZ ., .")] - public void Clz_V_16B_8H_4S([ValueSource("_8B4H2S_")] [Random(1)] ulong A0, - [ValueSource("_8B4H2S_")] [Random(1)] ulong A1, + [Test, Description("CLZ ., .")] + 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, [Values(0b00u, 0b01u, 0b10u)] uint size) // <16B, 8H, 4S> { - uint Opcode = 0x6E204820; // CLZ V0.16B, V1.16B + uint Opcode = 0x6E204800; // CLZ V0.16B, V0.16B + Opcode |= ((Rn & 31) << 5) | ((Rd & 31) << 0); Opcode |= ((size & 3) << 22); Bits Op = new Bits(Opcode); - Vector128 V1 = MakeVectorE0E1(A0, A1); - AThreadState ThreadState = SingleOpcode(Opcode, V1: V1); + Vector128 V0 = MakeVectorE0E1(Z, Z); + Vector128 V1 = MakeVectorE0E1(A, A); + AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1); - AArch64.Vpart(1, 0, new Bits(A0)); - AArch64.Vpart(1, 1, new Bits(A1)); + 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)); SimdFp.Clz_V(Op[30], Op[23, 22], Op[9, 5], Op[4, 0]); Assert.Multiple(() => @@ -304,61 +345,75 @@ namespace Ryujinx.Tests.Cpu } [Test, Description("CMEQ , , #0")] - public void Cmeq_S_D([ValueSource("_1D_")] [Random(1)] ulong A) + 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) { - uint Opcode = 0x5EE09820; // CMEQ D0, D1, #0 + uint Opcode = 0x5EE09800; // CMEQ D0, D0, #0 + Opcode |= ((Rn & 31) << 5) | ((Rd & 31) << 0); Bits Op = new Bits(Opcode); - Vector128 V0 = MakeVectorE1(TestContext.CurrentContext.Random.NextULong()); + Vector128 V0 = MakeVectorE0E1(Z, Z); Vector128 V1 = MakeVectorE0(A); AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1); + AArch64.Vpart(0, 0, new Bits(Z)); AArch64.Vpart(0, 1, new Bits(Z)); AArch64.V(1, new Bits(A)); SimdFp.Cmeq_Zero_S(Op[23, 22], Op[9, 5], Op[4, 0]); Assert.Multiple(() => { - Assert.That(GetVectorE0(ThreadState.V0), Is.EqualTo(AArch64.V(64, 0).ToUInt64())); - Assert.That(GetVectorE1(ThreadState.V0), Is.Zero); + 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())); }); } [Test, Description("CMEQ ., ., #0")] - public void Cmeq_V_8B_4H_2S([ValueSource("_8B4H2S_")] [Random(1)] ulong A, + 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, [Values(0b00u, 0b01u, 0b10u)] uint size) // <8B, 4H, 2S> { - uint Opcode = 0x0E209820; // CMEQ V0.8B, V1.8B, #0 + uint Opcode = 0x0E209800; // CMEQ V0.8B, V0.8B, #0 + Opcode |= ((Rn & 31) << 5) | ((Rd & 31) << 0); Opcode |= ((size & 3) << 22); Bits Op = new Bits(Opcode); - Vector128 V0 = MakeVectorE1(TestContext.CurrentContext.Random.NextULong()); + Vector128 V0 = MakeVectorE0E1(Z, Z); Vector128 V1 = MakeVectorE0(A); AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1); + AArch64.Vpart(0, 0, new Bits(Z)); AArch64.Vpart(0, 1, new Bits(Z)); AArch64.V(1, new Bits(A)); SimdFp.Cmeq_Zero_V(Op[30], Op[23, 22], Op[9, 5], Op[4, 0]); Assert.Multiple(() => { - Assert.That(GetVectorE0(ThreadState.V0), Is.EqualTo(AArch64.V(64, 0).ToUInt64())); - Assert.That(GetVectorE1(ThreadState.V0), Is.Zero); + 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())); }); } - [Test, Pairwise, Description("CMEQ ., ., #0")] - public void Cmeq_V_16B_8H_4S_2D([ValueSource("_8B4H2S1D_")] [Random(1)] ulong A0, - [ValueSource("_8B4H2S1D_")] [Random(1)] ulong A1, + [Test, Description("CMEQ ., ., #0")] + 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, [Values(0b00u, 0b01u, 0b10u, 0b11u)] uint size) // <16B, 8H, 4S, 2D> { - uint Opcode = 0x4E209820; // CMEQ V0.16B, V1.16B, #0 + uint Opcode = 0x4E209800; // CMEQ V0.16B, V0.16B, #0 + Opcode |= ((Rn & 31) << 5) | ((Rd & 31) << 0); Opcode |= ((size & 3) << 22); Bits Op = new Bits(Opcode); - Vector128 V1 = MakeVectorE0E1(A0, A1); - AThreadState ThreadState = SingleOpcode(Opcode, V1: V1); + Vector128 V0 = MakeVectorE0E1(Z, Z); + Vector128 V1 = MakeVectorE0E1(A, A); + AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1); - AArch64.Vpart(1, 0, new Bits(A0)); - AArch64.Vpart(1, 1, new Bits(A1)); + 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)); SimdFp.Cmeq_Zero_V(Op[30], Op[23, 22], Op[9, 5], Op[4, 0]); Assert.Multiple(() => @@ -369,61 +424,75 @@ namespace Ryujinx.Tests.Cpu } [Test, Description("CMGE , , #0")] - public void Cmge_S_D([ValueSource("_1D_")] [Random(1)] ulong A) + 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) { - uint Opcode = 0x7EE08820; // CMGE D0, D1, #0 + uint Opcode = 0x7EE08800; // CMGE D0, D0, #0 + Opcode |= ((Rn & 31) << 5) | ((Rd & 31) << 0); Bits Op = new Bits(Opcode); - Vector128 V0 = MakeVectorE1(TestContext.CurrentContext.Random.NextULong()); + Vector128 V0 = MakeVectorE0E1(Z, Z); Vector128 V1 = MakeVectorE0(A); AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1); + AArch64.Vpart(0, 0, new Bits(Z)); AArch64.Vpart(0, 1, new Bits(Z)); AArch64.V(1, new Bits(A)); SimdFp.Cmge_Zero_S(Op[23, 22], Op[9, 5], Op[4, 0]); Assert.Multiple(() => { - Assert.That(GetVectorE0(ThreadState.V0), Is.EqualTo(AArch64.V(64, 0).ToUInt64())); - Assert.That(GetVectorE1(ThreadState.V0), Is.Zero); + 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())); }); } [Test, Description("CMGE ., ., #0")] - public void Cmge_V_8B_4H_2S([ValueSource("_8B4H2S_")] [Random(1)] ulong A, + 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, [Values(0b00u, 0b01u, 0b10u)] uint size) // <8B, 4H, 2S> { - uint Opcode = 0x2E208820; // CMGE V0.8B, V1.8B, #0 + uint Opcode = 0x2E208800; // CMGE V0.8B, V0.8B, #0 + Opcode |= ((Rn & 31) << 5) | ((Rd & 31) << 0); Opcode |= ((size & 3) << 22); Bits Op = new Bits(Opcode); - Vector128 V0 = MakeVectorE1(TestContext.CurrentContext.Random.NextULong()); + Vector128 V0 = MakeVectorE0E1(Z, Z); Vector128 V1 = MakeVectorE0(A); AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1); + AArch64.Vpart(0, 0, new Bits(Z)); AArch64.Vpart(0, 1, new Bits(Z)); AArch64.V(1, new Bits(A)); SimdFp.Cmge_Zero_V(Op[30], Op[23, 22], Op[9, 5], Op[4, 0]); Assert.Multiple(() => { - Assert.That(GetVectorE0(ThreadState.V0), Is.EqualTo(AArch64.V(64, 0).ToUInt64())); - Assert.That(GetVectorE1(ThreadState.V0), Is.Zero); + 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())); }); } - [Test, Pairwise, Description("CMGE ., ., #0")] - public void Cmge_V_16B_8H_4S_2D([ValueSource("_8B4H2S1D_")] [Random(1)] ulong A0, - [ValueSource("_8B4H2S1D_")] [Random(1)] ulong A1, + [Test, Description("CMGE ., ., #0")] + 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, [Values(0b00u, 0b01u, 0b10u, 0b11u)] uint size) // <16B, 8H, 4S, 2D> { - uint Opcode = 0x6E208820; // CMGE V0.16B, V1.16B, #0 + uint Opcode = 0x6E208800; // CMGE V0.16B, V0.16B, #0 + Opcode |= ((Rn & 31) << 5) | ((Rd & 31) << 0); Opcode |= ((size & 3) << 22); Bits Op = new Bits(Opcode); - Vector128 V1 = MakeVectorE0E1(A0, A1); - AThreadState ThreadState = SingleOpcode(Opcode, V1: V1); + Vector128 V0 = MakeVectorE0E1(Z, Z); + Vector128 V1 = MakeVectorE0E1(A, A); + AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1); - AArch64.Vpart(1, 0, new Bits(A0)); - AArch64.Vpart(1, 1, new Bits(A1)); + 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)); SimdFp.Cmge_Zero_V(Op[30], Op[23, 22], Op[9, 5], Op[4, 0]); Assert.Multiple(() => @@ -434,61 +503,75 @@ namespace Ryujinx.Tests.Cpu } [Test, Description("CMGT , , #0")] - public void Cmgt_S_D([ValueSource("_1D_")] [Random(1)] ulong A) + 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) { - uint Opcode = 0x5EE08820; // CMGT D0, D1, #0 + uint Opcode = 0x5EE08800; // CMGT D0, D0, #0 + Opcode |= ((Rn & 31) << 5) | ((Rd & 31) << 0); Bits Op = new Bits(Opcode); - Vector128 V0 = MakeVectorE1(TestContext.CurrentContext.Random.NextULong()); + Vector128 V0 = MakeVectorE0E1(Z, Z); Vector128 V1 = MakeVectorE0(A); AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1); + AArch64.Vpart(0, 0, new Bits(Z)); AArch64.Vpart(0, 1, new Bits(Z)); AArch64.V(1, new Bits(A)); SimdFp.Cmgt_Zero_S(Op[23, 22], Op[9, 5], Op[4, 0]); Assert.Multiple(() => { - Assert.That(GetVectorE0(ThreadState.V0), Is.EqualTo(AArch64.V(64, 0).ToUInt64())); - Assert.That(GetVectorE1(ThreadState.V0), Is.Zero); + 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())); }); } [Test, Description("CMGT ., ., #0")] - public void Cmgt_V_8B_4H_2S([ValueSource("_8B4H2S_")] [Random(1)] ulong A, + 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, [Values(0b00u, 0b01u, 0b10u)] uint size) // <8B, 4H, 2S> { - uint Opcode = 0x0E208820; // CMGT V0.8B, V1.8B, #0 + uint Opcode = 0x0E208800; // CMGT V0.8B, V0.8B, #0 + Opcode |= ((Rn & 31) << 5) | ((Rd & 31) << 0); Opcode |= ((size & 3) << 22); Bits Op = new Bits(Opcode); - Vector128 V0 = MakeVectorE1(TestContext.CurrentContext.Random.NextULong()); + Vector128 V0 = MakeVectorE0E1(Z, Z); Vector128 V1 = MakeVectorE0(A); AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1); + AArch64.Vpart(0, 0, new Bits(Z)); AArch64.Vpart(0, 1, new Bits(Z)); AArch64.V(1, new Bits(A)); SimdFp.Cmgt_Zero_V(Op[30], Op[23, 22], Op[9, 5], Op[4, 0]); Assert.Multiple(() => { - Assert.That(GetVectorE0(ThreadState.V0), Is.EqualTo(AArch64.V(64, 0).ToUInt64())); - Assert.That(GetVectorE1(ThreadState.V0), Is.Zero); + 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())); }); } - [Test, Pairwise, Description("CMGT ., ., #0")] - public void Cmgt_V_16B_8H_4S_2D([ValueSource("_8B4H2S1D_")] [Random(1)] ulong A0, - [ValueSource("_8B4H2S1D_")] [Random(1)] ulong A1, + [Test, Description("CMGT ., ., #0")] + 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, [Values(0b00u, 0b01u, 0b10u, 0b11u)] uint size) // <16B, 8H, 4S, 2D> { - uint Opcode = 0x4E208820; // CMGT V0.16B, V1.16B, #0 + uint Opcode = 0x4E208800; // CMGT V0.16B, V0.16B, #0 + Opcode |= ((Rn & 31) << 5) | ((Rd & 31) << 0); Opcode |= ((size & 3) << 22); Bits Op = new Bits(Opcode); - Vector128 V1 = MakeVectorE0E1(A0, A1); - AThreadState ThreadState = SingleOpcode(Opcode, V1: V1); + Vector128 V0 = MakeVectorE0E1(Z, Z); + Vector128 V1 = MakeVectorE0E1(A, A); + AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1); - AArch64.Vpart(1, 0, new Bits(A0)); - AArch64.Vpart(1, 1, new Bits(A1)); + 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)); SimdFp.Cmgt_Zero_V(Op[30], Op[23, 22], Op[9, 5], Op[4, 0]); Assert.Multiple(() => @@ -499,61 +582,75 @@ namespace Ryujinx.Tests.Cpu } [Test, Description("CMLE , , #0")] - public void Cmle_S_D([ValueSource("_1D_")] [Random(1)] ulong A) + 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) { - uint Opcode = 0x7EE09820; // CMLE D0, D1, #0 + uint Opcode = 0x7EE09800; // CMLE D0, D0, #0 + Opcode |= ((Rn & 31) << 5) | ((Rd & 31) << 0); Bits Op = new Bits(Opcode); - Vector128 V0 = MakeVectorE1(TestContext.CurrentContext.Random.NextULong()); + Vector128 V0 = MakeVectorE0E1(Z, Z); Vector128 V1 = MakeVectorE0(A); AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1); + AArch64.Vpart(0, 0, new Bits(Z)); AArch64.Vpart(0, 1, new Bits(Z)); AArch64.V(1, new Bits(A)); SimdFp.Cmle_S(Op[23, 22], Op[9, 5], Op[4, 0]); Assert.Multiple(() => { - Assert.That(GetVectorE0(ThreadState.V0), Is.EqualTo(AArch64.V(64, 0).ToUInt64())); - Assert.That(GetVectorE1(ThreadState.V0), Is.Zero); + 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())); }); } [Test, Description("CMLE ., ., #0")] - public void Cmle_V_8B_4H_2S([ValueSource("_8B4H2S_")] [Random(1)] ulong A, + 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, [Values(0b00u, 0b01u, 0b10u)] uint size) // <8B, 4H, 2S> { - uint Opcode = 0x2E209820; // CMLE V0.8B, V1.8B, #0 + uint Opcode = 0x2E209800; // CMLE V0.8B, V0.8B, #0 + Opcode |= ((Rn & 31) << 5) | ((Rd & 31) << 0); Opcode |= ((size & 3) << 22); Bits Op = new Bits(Opcode); - Vector128 V0 = MakeVectorE1(TestContext.CurrentContext.Random.NextULong()); + Vector128 V0 = MakeVectorE0E1(Z, Z); Vector128 V1 = MakeVectorE0(A); AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1); + AArch64.Vpart(0, 0, new Bits(Z)); AArch64.Vpart(0, 1, new Bits(Z)); AArch64.V(1, new Bits(A)); SimdFp.Cmle_V(Op[30], Op[23, 22], Op[9, 5], Op[4, 0]); Assert.Multiple(() => { - Assert.That(GetVectorE0(ThreadState.V0), Is.EqualTo(AArch64.V(64, 0).ToUInt64())); - Assert.That(GetVectorE1(ThreadState.V0), Is.Zero); + 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())); }); } - [Test, Pairwise, Description("CMLE ., ., #0")] - public void Cmle_V_16B_8H_4S_2D([ValueSource("_8B4H2S1D_")] [Random(1)] ulong A0, - [ValueSource("_8B4H2S1D_")] [Random(1)] ulong A1, + [Test, Description("CMLE ., ., #0")] + 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, [Values(0b00u, 0b01u, 0b10u, 0b11u)] uint size) // <16B, 8H, 4S, 2D> { - uint Opcode = 0x6E209820; // CMLE V0.16B, V1.16B, #0 + uint Opcode = 0x6E209800; // CMLE V0.16B, V0.16B, #0 + Opcode |= ((Rn & 31) << 5) | ((Rd & 31) << 0); Opcode |= ((size & 3) << 22); Bits Op = new Bits(Opcode); - Vector128 V1 = MakeVectorE0E1(A0, A1); - AThreadState ThreadState = SingleOpcode(Opcode, V1: V1); + Vector128 V0 = MakeVectorE0E1(Z, Z); + Vector128 V1 = MakeVectorE0E1(A, A); + AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1); - AArch64.Vpart(1, 0, new Bits(A0)); - AArch64.Vpart(1, 1, new Bits(A1)); + 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)); SimdFp.Cmle_V(Op[30], Op[23, 22], Op[9, 5], Op[4, 0]); Assert.Multiple(() => @@ -564,61 +661,75 @@ namespace Ryujinx.Tests.Cpu } [Test, Description("CMLT , , #0")] - public void Cmlt_S_D([ValueSource("_1D_")] [Random(1)] ulong A) + 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) { - uint Opcode = 0x5EE0A820; // CMLT D0, D1, #0 + uint Opcode = 0x5EE0A800; // CMLT D0, D0, #0 + Opcode |= ((Rn & 31) << 5) | ((Rd & 31) << 0); Bits Op = new Bits(Opcode); - Vector128 V0 = MakeVectorE1(TestContext.CurrentContext.Random.NextULong()); + Vector128 V0 = MakeVectorE0E1(Z, Z); Vector128 V1 = MakeVectorE0(A); AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1); + AArch64.Vpart(0, 0, new Bits(Z)); AArch64.Vpart(0, 1, new Bits(Z)); AArch64.V(1, new Bits(A)); SimdFp.Cmlt_S(Op[23, 22], Op[9, 5], Op[4, 0]); Assert.Multiple(() => { - Assert.That(GetVectorE0(ThreadState.V0), Is.EqualTo(AArch64.V(64, 0).ToUInt64())); - Assert.That(GetVectorE1(ThreadState.V0), Is.Zero); + 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())); }); } [Test, Description("CMLT ., ., #0")] - public void Cmlt_V_8B_4H_2S([ValueSource("_8B4H2S_")] [Random(1)] ulong A, + 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, [Values(0b00u, 0b01u, 0b10u)] uint size) // <8B, 4H, 2S> { - uint Opcode = 0x0E20A820; // CMLT V0.8B, V1.8B, #0 + uint Opcode = 0x0E20A800; // CMLT V0.8B, V0.8B, #0 + Opcode |= ((Rn & 31) << 5) | ((Rd & 31) << 0); Opcode |= ((size & 3) << 22); Bits Op = new Bits(Opcode); - Vector128 V0 = MakeVectorE1(TestContext.CurrentContext.Random.NextULong()); + Vector128 V0 = MakeVectorE0E1(Z, Z); Vector128 V1 = MakeVectorE0(A); AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1); + AArch64.Vpart(0, 0, new Bits(Z)); AArch64.Vpart(0, 1, new Bits(Z)); AArch64.V(1, new Bits(A)); SimdFp.Cmlt_V(Op[30], Op[23, 22], Op[9, 5], Op[4, 0]); Assert.Multiple(() => { - Assert.That(GetVectorE0(ThreadState.V0), Is.EqualTo(AArch64.V(64, 0).ToUInt64())); - Assert.That(GetVectorE1(ThreadState.V0), Is.Zero); + 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())); }); } - [Test, Pairwise, Description("CMLT ., ., #0")] - public void Cmlt_V_16B_8H_4S_2D([ValueSource("_8B4H2S1D_")] [Random(1)] ulong A0, - [ValueSource("_8B4H2S1D_")] [Random(1)] ulong A1, + [Test, Description("CMLT ., ., #0")] + 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, [Values(0b00u, 0b01u, 0b10u, 0b11u)] uint size) // <16B, 8H, 4S, 2D> { - uint Opcode = 0x4E20A820; // CMLT V0.16B, V1.16B, #0 + uint Opcode = 0x4E20A800; // CMLT V0.16B, V0.16B, #0 + Opcode |= ((Rn & 31) << 5) | ((Rd & 31) << 0); Opcode |= ((size & 3) << 22); Bits Op = new Bits(Opcode); - Vector128 V1 = MakeVectorE0E1(A0, A1); - AThreadState ThreadState = SingleOpcode(Opcode, V1: V1); + Vector128 V0 = MakeVectorE0E1(Z, Z); + Vector128 V1 = MakeVectorE0E1(A, A); + AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1); - AArch64.Vpart(1, 0, new Bits(A0)); - AArch64.Vpart(1, 1, new Bits(A1)); + 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)); SimdFp.Cmlt_V(Op[30], Op[23, 22], Op[9, 5], Op[4, 0]); Assert.Multiple(() => @@ -629,37 +740,46 @@ namespace Ryujinx.Tests.Cpu } [Test, Description("CNT ., .")] - public void Cnt_V_8B([ValueSource("_8B_")] [Random(1)] ulong A) + 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) { - uint Opcode = 0x0E205820; // CNT V0.8B, V1.8B + uint Opcode = 0x0E205800; // CNT V0.8B, V0.8B + Opcode |= ((Rn & 31) << 5) | ((Rd & 31) << 0); Bits Op = new Bits(Opcode); - Vector128 V0 = MakeVectorE1(TestContext.CurrentContext.Random.NextULong()); + Vector128 V0 = MakeVectorE0E1(Z, Z); Vector128 V1 = MakeVectorE0(A); AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1); + AArch64.Vpart(0, 0, new Bits(Z)); AArch64.Vpart(0, 1, new Bits(Z)); AArch64.V(1, new Bits(A)); SimdFp.Cnt_V(Op[30], Op[23, 22], Op[9, 5], Op[4, 0]); Assert.Multiple(() => { - Assert.That(GetVectorE0(ThreadState.V0), Is.EqualTo(AArch64.V(64, 0).ToUInt64())); - Assert.That(GetVectorE1(ThreadState.V0), Is.Zero); + 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())); }); } - [Test, Pairwise, Description("CNT ., .")] - public void Cnt_V_16B([ValueSource("_8B_")] [Random(1)] ulong A0, - [ValueSource("_8B_")] [Random(1)] ulong A1) + [Test, Description("CNT ., .")] + 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) { - uint Opcode = 0x4E205820; // CNT V0.16B, V1.16B + uint Opcode = 0x4E205800; // CNT V0.16B, V0.16B + Opcode |= ((Rn & 31) << 5) | ((Rd & 31) << 0); Bits Op = new Bits(Opcode); - Vector128 V1 = MakeVectorE0E1(A0, A1); - AThreadState ThreadState = SingleOpcode(Opcode, V1: V1); + Vector128 V0 = MakeVectorE0E1(Z, Z); + Vector128 V1 = MakeVectorE0E1(A, A); + AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1); - AArch64.Vpart(1, 0, new Bits(A0)); - AArch64.Vpart(1, 1, new Bits(A1)); + 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)); SimdFp.Cnt_V(Op[30], Op[23, 22], Op[9, 5], Op[4, 0]); Assert.Multiple(() => @@ -670,61 +790,75 @@ namespace Ryujinx.Tests.Cpu } [Test, Description("NEG , ")] - public void Neg_S_D([ValueSource("_1D_")] [Random(1)] ulong A) + 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) { - uint Opcode = 0x7EE0B820; // NEG D0, D1 + uint Opcode = 0x7EE0B800; // NEG D0, D0 + Opcode |= ((Rn & 31) << 5) | ((Rd & 31) << 0); Bits Op = new Bits(Opcode); - Vector128 V0 = MakeVectorE1(TestContext.CurrentContext.Random.NextULong()); + Vector128 V0 = MakeVectorE0E1(Z, Z); Vector128 V1 = MakeVectorE0(A); AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1); + AArch64.Vpart(0, 0, new Bits(Z)); AArch64.Vpart(0, 1, new Bits(Z)); AArch64.V(1, new Bits(A)); SimdFp.Neg_S(Op[23, 22], Op[9, 5], Op[4, 0]); Assert.Multiple(() => { - Assert.That(GetVectorE0(ThreadState.V0), Is.EqualTo(AArch64.V(64, 0).ToUInt64())); - Assert.That(GetVectorE1(ThreadState.V0), Is.Zero); + 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())); }); } [Test, Description("NEG ., .")] - public void Neg_V_8B_4H_2S([ValueSource("_8B4H2S_")] [Random(1)] ulong A, + 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, [Values(0b00u, 0b01u, 0b10u)] uint size) // <8B, 4H, 2S> { - uint Opcode = 0x2E20B820; // NEG V0.8B, V1.8B + uint Opcode = 0x2E20B800; // NEG V0.8B, V0.8B + Opcode |= ((Rn & 31) << 5) | ((Rd & 31) << 0); Opcode |= ((size & 3) << 22); Bits Op = new Bits(Opcode); - Vector128 V0 = MakeVectorE1(TestContext.CurrentContext.Random.NextULong()); + Vector128 V0 = MakeVectorE0E1(Z, Z); Vector128 V1 = MakeVectorE0(A); AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1); + AArch64.Vpart(0, 0, new Bits(Z)); AArch64.Vpart(0, 1, new Bits(Z)); AArch64.V(1, new Bits(A)); SimdFp.Neg_V(Op[30], Op[23, 22], Op[9, 5], Op[4, 0]); Assert.Multiple(() => { - Assert.That(GetVectorE0(ThreadState.V0), Is.EqualTo(AArch64.V(64, 0).ToUInt64())); - Assert.That(GetVectorE1(ThreadState.V0), Is.Zero); + 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())); }); } - [Test, Pairwise, Description("NEG ., .")] - public void Neg_V_16B_8H_4S_2D([ValueSource("_8B4H2S1D_")] [Random(1)] ulong A0, - [ValueSource("_8B4H2S1D_")] [Random(1)] ulong A1, + [Test, Description("NEG ., .")] + 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, [Values(0b00u, 0b01u, 0b10u, 0b11u)] uint size) // <16B, 8H, 4S, 2D> { - uint Opcode = 0x6E20B820; // NEG V0.16B, V1.16B + uint Opcode = 0x6E20B800; // NEG V0.16B, V0.16B + Opcode |= ((Rn & 31) << 5) | ((Rd & 31) << 0); Opcode |= ((size & 3) << 22); Bits Op = new Bits(Opcode); - Vector128 V1 = MakeVectorE0E1(A0, A1); - AThreadState ThreadState = SingleOpcode(Opcode, V1: V1); + Vector128 V0 = MakeVectorE0E1(Z, Z); + Vector128 V1 = MakeVectorE0E1(A, A); + AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1); - AArch64.Vpart(1, 0, new Bits(A0)); - AArch64.Vpart(1, 1, new Bits(A1)); + 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)); SimdFp.Neg_V(Op[30], Op[23, 22], Op[9, 5], Op[4, 0]); Assert.Multiple(() => @@ -735,37 +869,46 @@ namespace Ryujinx.Tests.Cpu } [Test, Description("NOT ., .")] - public void Not_V_8B([ValueSource("_8B_")] [Random(1)] ulong A) + 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) { - uint Opcode = 0x2E205820; // NOT V0.8B, V1.8B + uint Opcode = 0x2E205800; // NOT V0.8B, V0.8B + Opcode |= ((Rn & 31) << 5) | ((Rd & 31) << 0); Bits Op = new Bits(Opcode); - Vector128 V0 = MakeVectorE1(TestContext.CurrentContext.Random.NextULong()); + Vector128 V0 = MakeVectorE0E1(Z, Z); Vector128 V1 = MakeVectorE0(A); AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1); + AArch64.Vpart(0, 0, new Bits(Z)); AArch64.Vpart(0, 1, new Bits(Z)); AArch64.V(1, new Bits(A)); SimdFp.Not_V(Op[30], Op[9, 5], Op[4, 0]); Assert.Multiple(() => { - Assert.That(GetVectorE0(ThreadState.V0), Is.EqualTo(AArch64.V(64, 0).ToUInt64())); - Assert.That(GetVectorE1(ThreadState.V0), Is.Zero); + 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())); }); } - [Test, Pairwise, Description("NOT ., .")] - public void Not_V_16B([ValueSource("_8B_")] [Random(1)] ulong A0, - [ValueSource("_8B_")] [Random(1)] ulong A1) + [Test, Description("NOT ., .")] + 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) { - uint Opcode = 0x6E205820; // NOT V0.16B, V1.16B + uint Opcode = 0x6E205800; // NOT V0.16B, V0.16B + Opcode |= ((Rn & 31) << 5) | ((Rd & 31) << 0); Bits Op = new Bits(Opcode); - Vector128 V1 = MakeVectorE0E1(A0, A1); - AThreadState ThreadState = SingleOpcode(Opcode, V1: V1); + Vector128 V0 = MakeVectorE0E1(Z, Z); + Vector128 V1 = MakeVectorE0E1(A, A); + AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1); - AArch64.Vpart(1, 0, new Bits(A0)); - AArch64.Vpart(1, 1, new Bits(A1)); + 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)); SimdFp.Not_V(Op[30], Op[9, 5], Op[4, 0]); Assert.Multiple(() => @@ -776,37 +919,46 @@ namespace Ryujinx.Tests.Cpu } [Test, Description("RBIT ., .")] - public void Rbit_V_8B([ValueSource("_8B_")] [Random(1)] ulong A) + 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) { - uint Opcode = 0x2E605820; // RBIT V0.8B, V1.8B + uint Opcode = 0x2E605800; // RBIT V0.8B, V0.8B + Opcode |= ((Rn & 31) << 5) | ((Rd & 31) << 0); Bits Op = new Bits(Opcode); - Vector128 V0 = MakeVectorE1(TestContext.CurrentContext.Random.NextULong()); + Vector128 V0 = MakeVectorE0E1(Z, Z); Vector128 V1 = MakeVectorE0(A); AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1); + AArch64.Vpart(0, 0, new Bits(Z)); AArch64.Vpart(0, 1, new Bits(Z)); AArch64.V(1, new Bits(A)); SimdFp.Rbit_V(Op[30], Op[9, 5], Op[4, 0]); Assert.Multiple(() => { - Assert.That(GetVectorE0(ThreadState.V0), Is.EqualTo(AArch64.V(64, 0).ToUInt64())); - Assert.That(GetVectorE1(ThreadState.V0), Is.Zero); + 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())); }); } - [Test, Pairwise, Description("RBIT ., .")] - public void Rbit_V_16B([ValueSource("_8B_")] [Random(1)] ulong A0, - [ValueSource("_8B_")] [Random(1)] ulong A1) + [Test, Description("RBIT ., .")] + 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) { - uint Opcode = 0x6E605820; // RBIT V0.16B, V1.16B + uint Opcode = 0x6E605800; // RBIT V0.16B, V0.16B + Opcode |= ((Rn & 31) << 5) | ((Rd & 31) << 0); Bits Op = new Bits(Opcode); - Vector128 V1 = MakeVectorE0E1(A0, A1); - AThreadState ThreadState = SingleOpcode(Opcode, V1: V1); + Vector128 V0 = MakeVectorE0E1(Z, Z); + Vector128 V1 = MakeVectorE0E1(A, A); + AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1); - AArch64.Vpart(1, 0, new Bits(A0)); - AArch64.Vpart(1, 1, new Bits(A1)); + 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)); SimdFp.Rbit_V(Op[30], Op[9, 5], Op[4, 0]); Assert.Multiple(() => @@ -817,37 +969,46 @@ namespace Ryujinx.Tests.Cpu } [Test, Description("REV16 ., .")] - public void Rev16_V_8B([ValueSource("_8B_")] [Random(1)] ulong A) + 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) { - uint Opcode = 0x0E201820; // REV16 V0.8B, V1.8B + uint Opcode = 0x0E201800; // REV16 V0.8B, V0.8B + Opcode |= ((Rn & 31) << 5) | ((Rd & 31) << 0); Bits Op = new Bits(Opcode); - Vector128 V0 = MakeVectorE1(TestContext.CurrentContext.Random.NextULong()); + Vector128 V0 = MakeVectorE0E1(Z, Z); Vector128 V1 = MakeVectorE0(A); AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1); + AArch64.Vpart(0, 0, new Bits(Z)); AArch64.Vpart(0, 1, new Bits(Z)); AArch64.V(1, new Bits(A)); SimdFp.Rev16_V(Op[30], Op[23, 22], Op[9, 5], Op[4, 0]); Assert.Multiple(() => { - Assert.That(GetVectorE0(ThreadState.V0), Is.EqualTo(AArch64.V(64, 0).ToUInt64())); - Assert.That(GetVectorE1(ThreadState.V0), Is.Zero); + 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())); }); } - [Test, Pairwise, Description("REV16 ., .")] - public void Rev16_V_16B([ValueSource("_8B_")] [Random(1)] ulong A0, - [ValueSource("_8B_")] [Random(1)] ulong A1) + [Test, Description("REV16 ., .")] + 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) { - uint Opcode = 0x4E201820; // REV16 V0.16B, V1.16B + uint Opcode = 0x4E201800; // REV16 V0.16B, V0.16B + Opcode |= ((Rn & 31) << 5) | ((Rd & 31) << 0); Bits Op = new Bits(Opcode); - Vector128 V1 = MakeVectorE0E1(A0, A1); - AThreadState ThreadState = SingleOpcode(Opcode, V1: V1); + Vector128 V0 = MakeVectorE0E1(Z, Z); + Vector128 V1 = MakeVectorE0E1(A, A); + AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1); - AArch64.Vpart(1, 0, new Bits(A0)); - AArch64.Vpart(1, 1, new Bits(A1)); + 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)); SimdFp.Rev16_V(Op[30], Op[23, 22], Op[9, 5], Op[4, 0]); Assert.Multiple(() => @@ -858,41 +1019,50 @@ namespace Ryujinx.Tests.Cpu } [Test, Description("REV32 ., .")] - public void Rev32_V_8B_4H([ValueSource("_8B4H_")] [Random(1)] ulong A, + 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, [Values(0b00u, 0b01u)] uint size) // <8B, 4H> { - uint Opcode = 0x2E200820; // REV32 V0.8B, V1.8B + uint Opcode = 0x2E200800; // REV32 V0.8B, V0.8B + Opcode |= ((Rn & 31) << 5) | ((Rd & 31) << 0); Opcode |= ((size & 3) << 22); Bits Op = new Bits(Opcode); - Vector128 V0 = MakeVectorE1(TestContext.CurrentContext.Random.NextULong()); + Vector128 V0 = MakeVectorE0E1(Z, Z); Vector128 V1 = MakeVectorE0(A); AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1); + AArch64.Vpart(0, 0, new Bits(Z)); AArch64.Vpart(0, 1, new Bits(Z)); AArch64.V(1, new Bits(A)); SimdFp.Rev32_V(Op[30], Op[23, 22], Op[9, 5], Op[4, 0]); Assert.Multiple(() => { - Assert.That(GetVectorE0(ThreadState.V0), Is.EqualTo(AArch64.V(64, 0).ToUInt64())); - Assert.That(GetVectorE1(ThreadState.V0), Is.Zero); + 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())); }); } - [Test, Pairwise, Description("REV32 ., .")] - public void Rev32_V_16B_8H([ValueSource("_8B4H_")] [Random(1)] ulong A0, - [ValueSource("_8B4H_")] [Random(1)] ulong A1, + [Test, Description("REV32 ., .")] + 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, [Values(0b00u, 0b01u)] uint size) // <16B, 8H> { - uint Opcode = 0x6E200820; // REV32 V0.16B, V1.16B + uint Opcode = 0x6E200800; // REV32 V0.16B, V0.16B + Opcode |= ((Rn & 31) << 5) | ((Rd & 31) << 0); Opcode |= ((size & 3) << 22); Bits Op = new Bits(Opcode); - Vector128 V1 = MakeVectorE0E1(A0, A1); - AThreadState ThreadState = SingleOpcode(Opcode, V1: V1); + Vector128 V0 = MakeVectorE0E1(Z, Z); + Vector128 V1 = MakeVectorE0E1(A, A); + AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1); - AArch64.Vpart(1, 0, new Bits(A0)); - AArch64.Vpart(1, 1, new Bits(A1)); + 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)); SimdFp.Rev32_V(Op[30], Op[23, 22], Op[9, 5], Op[4, 0]); Assert.Multiple(() => @@ -903,41 +1073,50 @@ namespace Ryujinx.Tests.Cpu } [Test, Description("REV64 ., .")] - public void Rev64_V_8B_4H_2S([ValueSource("_8B4H2S_")] [Random(1)] ulong A, + 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, [Values(0b00u, 0b01u, 0b10u)] uint size) // <8B, 4H, 2S> { - uint Opcode = 0x0E200820; // REV64 V0.8B, V1.8B + uint Opcode = 0x0E200800; // REV64 V0.8B, V0.8B + Opcode |= ((Rn & 31) << 5) | ((Rd & 31) << 0); Opcode |= ((size & 3) << 22); Bits Op = new Bits(Opcode); - Vector128 V0 = MakeVectorE1(TestContext.CurrentContext.Random.NextULong()); + Vector128 V0 = MakeVectorE0E1(Z, Z); Vector128 V1 = MakeVectorE0(A); AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1); + AArch64.Vpart(0, 0, new Bits(Z)); AArch64.Vpart(0, 1, new Bits(Z)); AArch64.V(1, new Bits(A)); SimdFp.Rev64_V(Op[30], Op[23, 22], Op[9, 5], Op[4, 0]); Assert.Multiple(() => { - Assert.That(GetVectorE0(ThreadState.V0), Is.EqualTo(AArch64.V(64, 0).ToUInt64())); - Assert.That(GetVectorE1(ThreadState.V0), Is.Zero); + 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())); }); } - [Test, Pairwise, Description("REV64 ., .")] - public void Rev64_V_16B_8H_4S([ValueSource("_8B4H2S_")] [Random(1)] ulong A0, - [ValueSource("_8B4H2S_")] [Random(1)] ulong A1, + [Test, Description("REV64 ., .")] + 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, [Values(0b00u, 0b01u, 0b10u)] uint size) // <16B, 8H, 4S> { - uint Opcode = 0x4E200820; // REV64 V0.16B, V1.16B + uint Opcode = 0x4E200800; // REV64 V0.16B, V0.16B + Opcode |= ((Rn & 31) << 5) | ((Rd & 31) << 0); Opcode |= ((size & 3) << 22); Bits Op = new Bits(Opcode); - Vector128 V1 = MakeVectorE0E1(A0, A1); - AThreadState ThreadState = SingleOpcode(Opcode, V1: V1); + Vector128 V0 = MakeVectorE0E1(Z, Z); + Vector128 V1 = MakeVectorE0E1(A, A); + AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1); - AArch64.Vpart(1, 0, new Bits(A0)); - AArch64.Vpart(1, 1, new Bits(A1)); + 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)); SimdFp.Rev64_V(Op[30], Op[23, 22], Op[9, 5], Op[4, 0]); Assert.Multiple(() => @@ -948,228 +1127,252 @@ namespace Ryujinx.Tests.Cpu } [Test, Description("SQXTN , ")] - public void Sqxtn_S_HB_SH_DS([ValueSource("_1H1S1D_")] [Random(1)] ulong A, + 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, [Values(0b00u, 0b01u, 0b10u)] uint size) // { - uint Opcode = 0x5E214820; // SQXTN B0, H1 + uint Opcode = 0x5E214800; // SQXTN B0, H0 + Opcode |= ((Rn & 31) << 5) | ((Rd & 31) << 0); Opcode |= ((size & 3) << 22); Bits Op = new Bits(Opcode); - Vector128 V0 = MakeVectorE0E1(TestContext.CurrentContext.Random.NextULong(), - TestContext.CurrentContext.Random.NextULong()); + Vector128 V0 = MakeVectorE0E1(Z, Z); Vector128 V1 = MakeVectorE0(A); AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1); - AArch64.Vpart(0, 0, new Bits(TestContext.CurrentContext.Random.NextULong())); + AArch64.Vpart(0, 0, new Bits(Z)); AArch64.Vpart(0, 1, new Bits(Z)); AArch64.V(1, new Bits(A)); SimdFp.Sqxtn_S(Op[23, 22], Op[9, 5], Op[4, 0]); Assert.Multiple(() => { - Assert.That(GetVectorE0(ThreadState.V0), Is.EqualTo(AArch64.V(64, 0).ToUInt64())); - Assert.That(GetVectorE1(ThreadState.V0), Is.Zero); + 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])); } - [Test, Pairwise, Description("SQXTN{2} ., .")] - public void Sqxtn_V_8H8B_4S4H_2D2S([ValueSource("_4H2S1D_")] [Random(1)] ulong A0, - [ValueSource("_4H2S1D_")] [Random(1)] ulong A1, + [Test, Description("SQXTN{2} ., .")] + 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, [Values(0b00u, 0b01u, 0b10u)] uint size) // <8H8B, 4S4H, 2D2S> { - uint Opcode = 0x0E214820; // SQXTN V0.8B, V1.8H + uint Opcode = 0x0E214800; // SQXTN V0.8B, V0.8H + Opcode |= ((Rn & 31) << 5) | ((Rd & 31) << 0); Opcode |= ((size & 3) << 22); Bits Op = new Bits(Opcode); - Vector128 V0 = MakeVectorE1(TestContext.CurrentContext.Random.NextULong()); - Vector128 V1 = MakeVectorE0E1(A0, A1); + Vector128 V0 = MakeVectorE0E1(Z, Z); + Vector128 V1 = MakeVectorE0E1(A, A); AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1); - AArch64.Vpart(1, 0, new Bits(A0)); - AArch64.Vpart(1, 1, new Bits(A1)); + 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)); SimdFp.Sqxtn_V(Op[30], Op[23, 22], Op[9, 5], Op[4, 0]); Assert.Multiple(() => { - Assert.That(GetVectorE0(ThreadState.V0), Is.EqualTo(AArch64.V(64, 0).ToUInt64())); - Assert.That(GetVectorE1(ThreadState.V0), Is.Zero); + 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])); } - [Test, Pairwise, Description("SQXTN{2} ., .")] - public void Sqxtn_V_8H16B_4S8H_2D4S([ValueSource("_4H2S1D_")] [Random(1)] ulong A0, - [ValueSource("_4H2S1D_")] [Random(1)] ulong A1, + [Test, Description("SQXTN{2} ., .")] + 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, [Values(0b00u, 0b01u, 0b10u)] uint size) // <8H16B, 4S8H, 2D4S> { - uint Opcode = 0x4E214820; // SQXTN2 V0.16B, V1.8H + uint Opcode = 0x4E214800; // SQXTN2 V0.16B, V0.8H + Opcode |= ((Rn & 31) << 5) | ((Rd & 31) << 0); Opcode |= ((size & 3) << 22); Bits Op = new Bits(Opcode); - ulong _E0 = TestContext.CurrentContext.Random.NextULong(); - Vector128 V0 = MakeVectorE0(_E0); - Vector128 V1 = MakeVectorE0E1(A0, A1); + Vector128 V0 = MakeVectorE0E1(Z, Z); + Vector128 V1 = MakeVectorE0E1(A, A); AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1); - AArch64.Vpart(1, 0, new Bits(A0)); - AArch64.Vpart(1, 1, new Bits(A1)); + 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)); SimdFp.Sqxtn_V(Op[30], Op[23, 22], Op[9, 5], Op[4, 0]); Assert.Multiple(() => { - Assert.That(GetVectorE0(ThreadState.V0), Is.EqualTo(_E0)); + 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])); } [Test, Description("SQXTUN , ")] - public void Sqxtun_S_HB_SH_DS([ValueSource("_1H1S1D_")] [Random(1)] ulong A, + 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, [Values(0b00u, 0b01u, 0b10u)] uint size) // { - uint Opcode = 0x7E212820; // SQXTUN B0, H1 + uint Opcode = 0x7E212800; // SQXTUN B0, H0 + Opcode |= ((Rn & 31) << 5) | ((Rd & 31) << 0); Opcode |= ((size & 3) << 22); Bits Op = new Bits(Opcode); - Vector128 V0 = MakeVectorE0E1(TestContext.CurrentContext.Random.NextULong(), - TestContext.CurrentContext.Random.NextULong()); + Vector128 V0 = MakeVectorE0E1(Z, Z); Vector128 V1 = MakeVectorE0(A); AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1); - AArch64.Vpart(0, 0, new Bits(TestContext.CurrentContext.Random.NextULong())); + AArch64.Vpart(0, 0, new Bits(Z)); AArch64.Vpart(0, 1, new Bits(Z)); AArch64.V(1, new Bits(A)); SimdFp.Sqxtun_S(Op[23, 22], Op[9, 5], Op[4, 0]); Assert.Multiple(() => { - Assert.That(GetVectorE0(ThreadState.V0), Is.EqualTo(AArch64.V(64, 0).ToUInt64())); - Assert.That(GetVectorE1(ThreadState.V0), Is.Zero); + 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])); } - [Test, Pairwise, Description("SQXTUN{2} ., .")] - public void Sqxtun_V_8H8B_4S4H_2D2S([ValueSource("_4H2S1D_")] [Random(1)] ulong A0, - [ValueSource("_4H2S1D_")] [Random(1)] ulong A1, + [Test, Description("SQXTUN{2} ., .")] + 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, [Values(0b00u, 0b01u, 0b10u)] uint size) // <8H8B, 4S4H, 2D2S> { - uint Opcode = 0x2E212820; // SQXTUN V0.8B, V1.8H + uint Opcode = 0x2E212800; // SQXTUN V0.8B, V0.8H + Opcode |= ((Rn & 31) << 5) | ((Rd & 31) << 0); Opcode |= ((size & 3) << 22); Bits Op = new Bits(Opcode); - Vector128 V0 = MakeVectorE1(TestContext.CurrentContext.Random.NextULong()); - Vector128 V1 = MakeVectorE0E1(A0, A1); + Vector128 V0 = MakeVectorE0E1(Z, Z); + Vector128 V1 = MakeVectorE0E1(A, A); AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1); - AArch64.Vpart(1, 0, new Bits(A0)); - AArch64.Vpart(1, 1, new Bits(A1)); + 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)); SimdFp.Sqxtun_V(Op[30], Op[23, 22], Op[9, 5], Op[4, 0]); Assert.Multiple(() => { - Assert.That(GetVectorE0(ThreadState.V0), Is.EqualTo(AArch64.V(64, 0).ToUInt64())); - Assert.That(GetVectorE1(ThreadState.V0), Is.Zero); + 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])); } - [Test, Pairwise, Description("SQXTUN{2} ., .")] - public void Sqxtun_V_8H16B_4S8H_2D4S([ValueSource("_4H2S1D_")] [Random(1)] ulong A0, - [ValueSource("_4H2S1D_")] [Random(1)] ulong A1, + [Test, Description("SQXTUN{2} ., .")] + 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, [Values(0b00u, 0b01u, 0b10u)] uint size) // <8H16B, 4S8H, 2D4S> { - uint Opcode = 0x6E212820; // SQXTUN2 V0.16B, V1.8H + uint Opcode = 0x6E212800; // SQXTUN2 V0.16B, V0.8H + Opcode |= ((Rn & 31) << 5) | ((Rd & 31) << 0); Opcode |= ((size & 3) << 22); Bits Op = new Bits(Opcode); - ulong _E0 = TestContext.CurrentContext.Random.NextULong(); - Vector128 V0 = MakeVectorE0(_E0); - Vector128 V1 = MakeVectorE0E1(A0, A1); + Vector128 V0 = MakeVectorE0E1(Z, Z); + Vector128 V1 = MakeVectorE0E1(A, A); AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1); - AArch64.Vpart(1, 0, new Bits(A0)); - AArch64.Vpart(1, 1, new Bits(A1)); + 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)); SimdFp.Sqxtun_V(Op[30], Op[23, 22], Op[9, 5], Op[4, 0]); Assert.Multiple(() => { - Assert.That(GetVectorE0(ThreadState.V0), Is.EqualTo(_E0)); + 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])); } [Test, Description("UQXTN , ")] - public void Uqxtn_S_HB_SH_DS([ValueSource("_1H1S1D_")] [Random(1)] ulong A, + 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, [Values(0b00u, 0b01u, 0b10u)] uint size) // { - uint Opcode = 0x7E214820; // UQXTN B0, H1 + uint Opcode = 0x7E214800; // UQXTN B0, H0 + Opcode |= ((Rn & 31) << 5) | ((Rd & 31) << 0); Opcode |= ((size & 3) << 22); Bits Op = new Bits(Opcode); - Vector128 V0 = MakeVectorE0E1(TestContext.CurrentContext.Random.NextULong(), - TestContext.CurrentContext.Random.NextULong()); + Vector128 V0 = MakeVectorE0E1(Z, Z); Vector128 V1 = MakeVectorE0(A); AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1); - AArch64.Vpart(0, 0, new Bits(TestContext.CurrentContext.Random.NextULong())); + AArch64.Vpart(0, 0, new Bits(Z)); AArch64.Vpart(0, 1, new Bits(Z)); AArch64.V(1, new Bits(A)); SimdFp.Uqxtn_S(Op[23, 22], Op[9, 5], Op[4, 0]); Assert.Multiple(() => { - Assert.That(GetVectorE0(ThreadState.V0), Is.EqualTo(AArch64.V(64, 0).ToUInt64())); - Assert.That(GetVectorE1(ThreadState.V0), Is.Zero); + 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])); } - [Test, Pairwise, Description("UQXTN{2} ., .")] - public void Uqxtn_V_8H8B_4S4H_2D2S([ValueSource("_4H2S1D_")] [Random(1)] ulong A0, - [ValueSource("_4H2S1D_")] [Random(1)] ulong A1, + [Test, Description("UQXTN{2} ., .")] + 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, [Values(0b00u, 0b01u, 0b10u)] uint size) // <8H8B, 4S4H, 2D2S> { - uint Opcode = 0x2E214820; // UQXTN V0.8B, V1.8H + uint Opcode = 0x2E214800; // UQXTN V0.8B, V0.8H + Opcode |= ((Rn & 31) << 5) | ((Rd & 31) << 0); Opcode |= ((size & 3) << 22); Bits Op = new Bits(Opcode); - Vector128 V0 = MakeVectorE1(TestContext.CurrentContext.Random.NextULong()); - Vector128 V1 = MakeVectorE0E1(A0, A1); + Vector128 V0 = MakeVectorE0E1(Z, Z); + Vector128 V1 = MakeVectorE0E1(A, A); AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1); - AArch64.Vpart(1, 0, new Bits(A0)); - AArch64.Vpart(1, 1, new Bits(A1)); + 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)); SimdFp.Uqxtn_V(Op[30], Op[23, 22], Op[9, 5], Op[4, 0]); Assert.Multiple(() => { - Assert.That(GetVectorE0(ThreadState.V0), Is.EqualTo(AArch64.V(64, 0).ToUInt64())); - Assert.That(GetVectorE1(ThreadState.V0), Is.Zero); + 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])); } - [Test, Pairwise, Description("UQXTN{2} ., .")] - public void Uqxtn_V_8H16B_4S8H_2D4S([ValueSource("_4H2S1D_")] [Random(1)] ulong A0, - [ValueSource("_4H2S1D_")] [Random(1)] ulong A1, + [Test, Description("UQXTN{2} ., .")] + 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, [Values(0b00u, 0b01u, 0b10u)] uint size) // <8H16B, 4S8H, 2D4S> { - uint Opcode = 0x6E214820; // UQXTN2 V0.16B, V1.8H + uint Opcode = 0x6E214800; // UQXTN2 V0.16B, V0.8H + Opcode |= ((Rn & 31) << 5) | ((Rd & 31) << 0); Opcode |= ((size & 3) << 22); Bits Op = new Bits(Opcode); - ulong _E0 = TestContext.CurrentContext.Random.NextULong(); - Vector128 V0 = MakeVectorE0(_E0); - Vector128 V1 = MakeVectorE0E1(A0, A1); + Vector128 V0 = MakeVectorE0E1(Z, Z); + Vector128 V1 = MakeVectorE0E1(A, A); AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1); - AArch64.Vpart(1, 0, new Bits(A0)); - AArch64.Vpart(1, 1, new Bits(A1)); + 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)); SimdFp.Uqxtn_V(Op[30], Op[23, 22], Op[9, 5], Op[4, 0]); Assert.Multiple(() => { - Assert.That(GetVectorE0(ThreadState.V0), Is.EqualTo(_E0)); + 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])); diff --git a/Ryujinx.Tests/Cpu/CpuTestSimdArithmetic.cs b/Ryujinx.Tests/Cpu/CpuTestSimdArithmetic.cs index 2a0f5ed919..8e2d9a366e 100644 --- a/Ryujinx.Tests/Cpu/CpuTestSimdArithmetic.cs +++ b/Ryujinx.Tests/Cpu/CpuTestSimdArithmetic.cs @@ -9,46 +9,6 @@ namespace Ryujinx.Tests.Cpu { public class CpuTestSimdArithmetic : CpuTest { - [TestCase(0xE228420u, 0x0000000000000000ul, 0x0000000000000000ul, 0x0000000000000000ul, 0x0000000000000000ul, 0x0000000000000000ul, 0x0000000000000000ul)] - [TestCase(0xE228420u, 0x00000000FFFFFFFFul, 0x00000000FFFFFFFFul, 0x0000000000000001ul, 0x0000000000000001ul, 0x00000000FFFFFF00ul, 0x0000000000000000ul)] - [TestCase(0xE228420u, 0xFFFFFFFFFFFFFFFFul, 0xFFFFFFFFFFFFFFFFul, 0xFFFFFFFFFFFFFFFFul, 0xFFFFFFFFFFFFFFFFul, 0xFEFEFEFEFEFEFEFEul, 0x0000000000000000ul)] - [TestCase(0xE228420u, 0x0102030405060708ul, 0xAAAAAAAAAAAAAAAAul, 0x0807060504030201ul, 0x2222222222222222ul, 0x0909090909090909ul, 0x0000000000000000ul)] - [TestCase(0x4E228420u, 0x0000000000000000ul, 0x0000000000000000ul, 0x0000000000000000ul, 0x0000000000000000ul, 0x0000000000000000ul, 0x0000000000000000ul)] - [TestCase(0x4E228420u, 0x00000000FFFFFFFFul, 0x00000000FFFFFFFFul, 0x0000000000000001ul, 0x0000000000000001ul, 0x00000000FFFFFF00ul, 0x00000000FFFFFF00ul)] - [TestCase(0x4E228420u, 0xFFFFFFFFFFFFFFFFul, 0xFFFFFFFFFFFFFFFFul, 0xFFFFFFFFFFFFFFFFul, 0xFFFFFFFFFFFFFFFFul, 0xFEFEFEFEFEFEFEFEul, 0xFEFEFEFEFEFEFEFEul)] - [TestCase(0x4E228420u, 0x0102030405060708ul, 0xAAAAAAAAAAAAAAAAul, 0x0807060504030201ul, 0x2222222222222222ul, 0x0909090909090909ul, 0xCCCCCCCCCCCCCCCCul)] - [TestCase(0xE628420u, 0x0000000000000000ul, 0x0000000000000000ul, 0x0000000000000000ul, 0x0000000000000000ul, 0x0000000000000000ul, 0x0000000000000000ul)] - [TestCase(0xE628420u, 0x00000000FFFFFFFFul, 0x00000000FFFFFFFFul, 0x0000000000000001ul, 0x0000000000000001ul, 0x00000000FFFF0000ul, 0x0000000000000000ul)] - [TestCase(0xE628420u, 0xFFFFFFFFFFFFFFFFul, 0xFFFFFFFFFFFFFFFFul, 0xFFFFFFFFFFFFFFFFul, 0xFFFFFFFFFFFFFFFFul, 0xFFFEFFFEFFFEFFFEul, 0x0000000000000000ul)] - [TestCase(0xE628420u, 0x0102030405060708ul, 0xAAAAAAAAAAAAAAAAul, 0x0807060504030201ul, 0x2222222222222222ul, 0x0909090909090909ul, 0x0000000000000000ul)] - [TestCase(0x4E628420u, 0x0000000000000000ul, 0x0000000000000000ul, 0x0000000000000000ul, 0x0000000000000000ul, 0x0000000000000000ul, 0x0000000000000000ul)] - [TestCase(0x4E628420u, 0x00000000FFFFFFFFul, 0x00000000FFFFFFFFul, 0x0000000000000001ul, 0x0000000000000001ul, 0x00000000FFFF0000ul, 0x00000000FFFF0000ul)] - [TestCase(0x4E628420u, 0xFFFFFFFFFFFFFFFFul, 0xFFFFFFFFFFFFFFFFul, 0xFFFFFFFFFFFFFFFFul, 0xFFFFFFFFFFFFFFFFul, 0xFFFEFFFEFFFEFFFEul, 0xFFFEFFFEFFFEFFFEul)] - [TestCase(0x4E628420u, 0x0102030405060708ul, 0xAAAAAAAAAAAAAAAAul, 0x0807060504030201ul, 0x2222222222222222ul, 0x0909090909090909ul, 0xCCCCCCCCCCCCCCCCul)] - [TestCase(0xEA28420u, 0x0000000000000000ul, 0x0000000000000000ul, 0x0000000000000000ul, 0x0000000000000000ul, 0x0000000000000000ul, 0x0000000000000000ul)] - [TestCase(0xEA28420u, 0x00000000FFFFFFFFul, 0x00000000FFFFFFFFul, 0x0000000000000001ul, 0x0000000000000001ul, 0x0000000000000000ul, 0x0000000000000000ul)] - [TestCase(0xEA28420u, 0xFFFFFFFFFFFFFFFFul, 0xFFFFFFFFFFFFFFFFul, 0xFFFFFFFFFFFFFFFFul, 0xFFFFFFFFFFFFFFFFul, 0xFFFFFFFEFFFFFFFEul, 0x0000000000000000ul)] - [TestCase(0xEA28420u, 0x0102030405060708ul, 0xAAAAAAAAAAAAAAAAul, 0x0807060504030201ul, 0x2222222222222222ul, 0x0909090909090909ul, 0x0000000000000000ul)] - [TestCase(0x4EA28420u, 0x0000000000000000ul, 0x0000000000000000ul, 0x0000000000000000ul, 0x0000000000000000ul, 0x0000000000000000ul, 0x0000000000000000ul)] - [TestCase(0x4EA28420u, 0x00000000FFFFFFFFul, 0x00000000FFFFFFFFul, 0x0000000000000001ul, 0x0000000000000001ul, 0x0000000000000000ul, 0x0000000000000000ul)] - [TestCase(0x4EA28420u, 0xFFFFFFFFFFFFFFFFul, 0xFFFFFFFFFFFFFFFFul, 0xFFFFFFFFFFFFFFFFul, 0xFFFFFFFFFFFFFFFFul, 0xFFFFFFFEFFFFFFFEul, 0xFFFFFFFEFFFFFFFEul)] - [TestCase(0x4EA28420u, 0x0102030405060708ul, 0xAAAAAAAAAAAAAAAAul, 0x0807060504030201ul, 0x2222222222222222ul, 0x0909090909090909ul, 0xCCCCCCCCCCCCCCCCul)] - [TestCase(0x4EE28420u, 0x0000000000000000ul, 0x0000000000000000ul, 0x0000000000000000ul, 0x0000000000000000ul, 0x0000000000000000ul, 0x0000000000000000ul)] - [TestCase(0x4EE28420u, 0x00000000FFFFFFFFul, 0x00000000FFFFFFFFul, 0x0000000000000001ul, 0x0000000000000001ul, 0x0000000100000000ul, 0x0000000100000000ul)] - [TestCase(0x4EE28420u, 0xFFFFFFFFFFFFFFFFul, 0xFFFFFFFFFFFFFFFFul, 0xFFFFFFFFFFFFFFFFul, 0xFFFFFFFFFFFFFFFFul, 0xFFFFFFFFFFFFFFFEul, 0xFFFFFFFFFFFFFFFEul)] - [TestCase(0x4EE28420u, 0x0102030405060708ul, 0xAAAAAAAAAAAAAAAAul, 0x0807060504030201ul, 0x2222222222222222ul, 0x0909090909090909ul, 0xCCCCCCCCCCCCCCCCul)] - public void Add_V(uint Opcode, ulong A0, ulong A1, ulong B0, ulong B1, ulong Result0, ulong Result1) - { - Vector128 V1 = MakeVectorE0E1(A0, A1); - Vector128 V2 = MakeVectorE0E1(B0, B1); - AThreadState ThreadState = SingleOpcode(Opcode, V1: V1, V2: V2); - Assert.Multiple(() => - { - Assert.AreEqual(Result0, GetVectorE0(ThreadState.V0)); - Assert.AreEqual(Result1, GetVectorE1(ThreadState.V0)); - }); - } - [TestCase(0x1E224820u, 0x0000000000000000ul, 0x0000000080000000ul, 0x0000000000000000ul)] [TestCase(0x1E224820u, 0x0000000080000000ul, 0x0000000000000000ul, 0x0000000000000000ul)] [TestCase(0x1E224820u, 0x0000000080000000ul, 0x0000000080000000ul, 0x0000000080000000ul)] @@ -195,6 +155,7 @@ namespace Ryujinx.Tests.Cpu V0: Sse.SetAllVector128(B)); float Result = (float)(2 - ((double)A * (double)B)); + Assert.Multiple(() => { Assert.That(Sse41.Extract(ThreadState.V4, (byte)0), Is.EqualTo(Result)); diff --git a/Ryujinx.Tests/Cpu/CpuTestSimdMove.cs b/Ryujinx.Tests/Cpu/CpuTestSimdMove.cs deleted file mode 100644 index 055e08689c..0000000000 --- a/Ryujinx.Tests/Cpu/CpuTestSimdMove.cs +++ /dev/null @@ -1,136 +0,0 @@ -using ChocolArm64.State; - -using NUnit.Framework; - -using System.Runtime.Intrinsics; -using System.Runtime.Intrinsics.X86; - -namespace Ryujinx.Tests.Cpu -{ - public class CpuTestSimdMove : CpuTest - { - [Test, Description("TRN1 V0.4S, V1.4S, V2.4S")] - public void Trn1_V_4S([Random(2)] uint A0, [Random(2)] uint A1, [Random(2)] uint A2, [Random(2)] uint A3, - [Random(2)] uint B0, [Random(2)] uint B1, [Random(2)] uint B2, [Random(2)] uint B3) - { - uint Opcode = 0x4E822820; - Vector128 V1 = Sse.StaticCast(Sse2.SetVector128(A3, A2, A1, A0)); - Vector128 V2 = Sse.StaticCast(Sse2.SetVector128(B3, B2, B1, B0)); - - AThreadState ThreadState = SingleOpcode(Opcode, V1: V1, V2: V2); - - Assert.Multiple(() => - { - Assert.That(Sse41.Extract(Sse.StaticCast(ThreadState.V0), (byte)0), Is.EqualTo(A0)); - Assert.That(Sse41.Extract(Sse.StaticCast(ThreadState.V0), (byte)1), Is.EqualTo(B0)); - Assert.That(Sse41.Extract(Sse.StaticCast(ThreadState.V0), (byte)2), Is.EqualTo(A2)); - Assert.That(Sse41.Extract(Sse.StaticCast(ThreadState.V0), (byte)3), Is.EqualTo(B2)); - }); - } - - [Test, Description("TRN1 V0.8B, V1.8B, V2.8B")] - public void Trn1_V_8B([Random(2)] byte A0, [Random(1)] byte A1, [Random(2)] byte A2, [Random(1)] byte A3, - [Random(2)] byte A4, [Random(1)] byte A5, [Random(2)] byte A6, [Random(1)] byte A7, - [Random(2)] byte B0, [Random(1)] byte B1, [Random(2)] byte B2, [Random(1)] byte B3, - [Random(2)] byte B4, [Random(1)] byte B5, [Random(2)] byte B6, [Random(1)] byte B7) - { - uint Opcode = 0x0E022820; - Vector128 V1 = Sse.StaticCast(Sse2.SetVector128(0, 0, 0, 0, 0, 0, 0, 0, A7, A6, A5, A4, A3, A2, A1, A0)); - Vector128 V2 = Sse.StaticCast(Sse2.SetVector128(0, 0, 0, 0, 0, 0, 0, 0, B7, B6, B5, B4, B3, B2, B1, B0)); - - AThreadState ThreadState = SingleOpcode(Opcode, V1: V1, V2: V2); - - Assert.Multiple(() => - { - Assert.That(Sse41.Extract(Sse.StaticCast(ThreadState.V0), (byte)0), Is.EqualTo(A0)); - Assert.That(Sse41.Extract(Sse.StaticCast(ThreadState.V0), (byte)1), Is.EqualTo(B0)); - Assert.That(Sse41.Extract(Sse.StaticCast(ThreadState.V0), (byte)2), Is.EqualTo(A2)); - Assert.That(Sse41.Extract(Sse.StaticCast(ThreadState.V0), (byte)3), Is.EqualTo(B2)); - Assert.That(Sse41.Extract(Sse.StaticCast(ThreadState.V0), (byte)4), Is.EqualTo(A4)); - Assert.That(Sse41.Extract(Sse.StaticCast(ThreadState.V0), (byte)5), Is.EqualTo(B4)); - Assert.That(Sse41.Extract(Sse.StaticCast(ThreadState.V0), (byte)6), Is.EqualTo(A6)); - Assert.That(Sse41.Extract(Sse.StaticCast(ThreadState.V0), (byte)7), Is.EqualTo(B6)); - }); - } - - [Test, Description("TRN2 V0.4S, V1.4S, V2.4S")] - public void Trn2_V_4S([Random(2)] uint A0, [Random(2)] uint A1, [Random(2)] uint A2, [Random(2)] uint A3, - [Random(2)] uint B0, [Random(2)] uint B1, [Random(2)] uint B2, [Random(2)] uint B3) - { - uint Opcode = 0x4E826820; - Vector128 V1 = Sse.StaticCast(Sse2.SetVector128(A3, A2, A1, A0)); - Vector128 V2 = Sse.StaticCast(Sse2.SetVector128(B3, B2, B1, B0)); - - AThreadState ThreadState = SingleOpcode(Opcode, V1: V1, V2: V2); - - Assert.Multiple(() => - { - Assert.That(Sse41.Extract(Sse.StaticCast(ThreadState.V0), (byte)0), Is.EqualTo(A1)); - Assert.That(Sse41.Extract(Sse.StaticCast(ThreadState.V0), (byte)1), Is.EqualTo(B1)); - Assert.That(Sse41.Extract(Sse.StaticCast(ThreadState.V0), (byte)2), Is.EqualTo(A3)); - Assert.That(Sse41.Extract(Sse.StaticCast(ThreadState.V0), (byte)3), Is.EqualTo(B3)); - }); - } - - [Test, Description("TRN2 V0.8B, V1.8B, V2.8B")] - public void Trn2_V_8B([Random(1)] byte A0, [Random(2)] byte A1, [Random(1)] byte A2, [Random(2)] byte A3, - [Random(1)] byte A4, [Random(2)] byte A5, [Random(1)] byte A6, [Random(2)] byte A7, - [Random(1)] byte B0, [Random(2)] byte B1, [Random(1)] byte B2, [Random(2)] byte B3, - [Random(1)] byte B4, [Random(2)] byte B5, [Random(1)] byte B6, [Random(2)] byte B7) - { - uint Opcode = 0x0E026820; - Vector128 V1 = Sse.StaticCast(Sse2.SetVector128(0, 0, 0, 0, 0, 0, 0, 0, A7, A6, A5, A4, A3, A2, A1, A0)); - Vector128 V2 = Sse.StaticCast(Sse2.SetVector128(0, 0, 0, 0, 0, 0, 0, 0, B7, B6, B5, B4, B3, B2, B1, B0)); - - AThreadState ThreadState = SingleOpcode(Opcode, V1: V1, V2: V2); - - Assert.Multiple(() => - { - Assert.That(Sse41.Extract(Sse.StaticCast(ThreadState.V0), (byte)0), Is.EqualTo(A1)); - Assert.That(Sse41.Extract(Sse.StaticCast(ThreadState.V0), (byte)1), Is.EqualTo(B1)); - Assert.That(Sse41.Extract(Sse.StaticCast(ThreadState.V0), (byte)2), Is.EqualTo(A3)); - Assert.That(Sse41.Extract(Sse.StaticCast(ThreadState.V0), (byte)3), Is.EqualTo(B3)); - Assert.That(Sse41.Extract(Sse.StaticCast(ThreadState.V0), (byte)4), Is.EqualTo(A5)); - Assert.That(Sse41.Extract(Sse.StaticCast(ThreadState.V0), (byte)5), Is.EqualTo(B5)); - Assert.That(Sse41.Extract(Sse.StaticCast(ThreadState.V0), (byte)6), Is.EqualTo(A7)); - Assert.That(Sse41.Extract(Sse.StaticCast(ThreadState.V0), (byte)7), Is.EqualTo(B7)); - }); - } - - [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); - Vector128 V1 = MakeVectorE0E1(0x1716151413121110, 0x1F1E1D1C1B1A1918); - Vector128 V2 = MakeVectorE0E1(0x2726252423222120, 0x2F2E2D2C2B2A2928); - AThreadState ThreadState = SingleOpcode(Opcode, V1: V1, V2: V2); - Assert.AreEqual(Result_0, GetVectorE0(ThreadState.V0)); - Assert.AreEqual(Result_1, GetVectorE1(ThreadState.V0)); - } - - [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); - Vector128 V1 = MakeVectorE0E1(0x1716151413121110, 0x1F1E1D1C1B1A1918); - Vector128 V2 = MakeVectorE0E1(0x2726252423222120, 0x2F2E2D2C2B2A2928); - AThreadState ThreadState = SingleOpcode(Opcode, V1: V1, V2: V2); - Assert.AreEqual(Result_0, GetVectorE0(ThreadState.V0)); - Assert.AreEqual(Result_1, GetVectorE1(ThreadState.V0)); - } - } -} diff --git a/Ryujinx.Tests/Cpu/CpuTestSimdReg.cs b/Ryujinx.Tests/Cpu/CpuTestSimdReg.cs index 5e14f55d36..51db857c31 100644 --- a/Ryujinx.Tests/Cpu/CpuTestSimdReg.cs +++ b/Ryujinx.Tests/Cpu/CpuTestSimdReg.cs @@ -60,73 +60,90 @@ namespace Ryujinx.Tests.Cpu } #endregion - [Test, Description("ADD , , ")] - public void Add_S_D([ValueSource("_1D_")] [Random(1)] ulong A, - [ValueSource("_1D_")] [Random(1)] ulong B) + private const int RndCnt = 4; + + [Test, Pairwise, Description("ADD , , ")] + 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, + [ValueSource("_1D_")] [Random(RndCnt)] ulong A, + [ValueSource("_1D_")] [Random(RndCnt)] ulong B) { - uint Opcode = 0x5EE28420; // ADD D0, D1, D2 + uint Opcode = 0x5EE08400; // ADD D0, D0, D0 + Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); Bits Op = new Bits(Opcode); - Vector128 V0 = MakeVectorE1(TestContext.CurrentContext.Random.NextULong()); + Vector128 V0 = MakeVectorE0E1(Z, Z); Vector128 V1 = MakeVectorE0(A); Vector128 V2 = MakeVectorE0(B); AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2); + 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)); SimdFp.Add_S(Op[23, 22], Op[20, 16], Op[9, 5], Op[4, 0]); Assert.Multiple(() => { - Assert.That(GetVectorE0(ThreadState.V0), Is.EqualTo(AArch64.V(64, 0).ToUInt64())); - Assert.That(GetVectorE1(ThreadState.V0), Is.Zero); - }); - } - - [Test, Description("ADD ., ., .")] - public void Add_V_8B_4H_2S([ValueSource("_8B4H2S_")] [Random(1)] ulong A, - [ValueSource("_8B4H2S_")] [Random(1)] ulong B, - [Values(0b00u, 0b01u, 0b10u)] uint size) // <8B, 4H, 2S> - { - uint Opcode = 0x0E228420; // ADD V0.8B, V1.8B, V2.8B - Opcode |= ((size & 3) << 22); - Bits Op = new Bits(Opcode); - - Vector128 V0 = MakeVectorE1(TestContext.CurrentContext.Random.NextULong()); - Vector128 V1 = MakeVectorE0(A); - Vector128 V2 = MakeVectorE0(B); - AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2); - - AArch64.V(1, new Bits(A)); - AArch64.V(2, new Bits(B)); - SimdFp.Add_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.V(64, 0).ToUInt64())); - Assert.That(GetVectorE1(ThreadState.V0), Is.Zero); + 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())); }); } [Test, Pairwise, Description("ADD ., ., .")] - public void Add_V_16B_8H_4S_2D([ValueSource("_8B4H2S1D_")] [Random(1)] ulong A0, - [ValueSource("_8B4H2S1D_")] [Random(1)] ulong A1, - [ValueSource("_8B4H2S1D_")] [Random(1)] ulong B0, - [ValueSource("_8B4H2S1D_")] [Random(1)] ulong B1, - [Values(0b00u, 0b01u, 0b10u, 0b11u)] uint size) // <16B, 8H, 4S, 2D> + 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, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong A, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong B, + [Values(0b00u, 0b01u, 0b10u)] uint size) // <8B, 4H, 2S> { - uint Opcode = 0x4E228420; // ADD V0.16B, V1.16B, V2.16B + uint Opcode = 0x0E208400; // ADD 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); - Vector128 V1 = MakeVectorE0E1(A0, A1); - Vector128 V2 = MakeVectorE0E1(B0, B1); - AThreadState ThreadState = SingleOpcode(Opcode, V1: V1, V2: V2); + Vector128 V0 = MakeVectorE0E1(Z, Z); + Vector128 V1 = MakeVectorE0(A); + Vector128 V2 = MakeVectorE0(B); + AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2); - AArch64.Vpart(1, 0, new Bits(A0)); - AArch64.Vpart(1, 1, new Bits(A1)); - AArch64.Vpart(2, 0, new Bits(B0)); - AArch64.Vpart(2, 1, new Bits(B1)); + 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)); + SimdFp.Add_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())); + }); + } + + [Test, Pairwise, Description("ADD ., ., .")] + 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, + [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 = 0x4E208400; // ADD 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); + + Vector128 V0 = MakeVectorE0E1(Z, Z); + Vector128 V1 = MakeVectorE0E1(A, A); + Vector128 V2 = MakeVectorE0E1(B, B); + AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2); + + 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)); SimdFp.Add_V(Op[30], Op[23, 22], Op[20, 16], Op[9, 5], Op[4, 0]); Assert.Multiple(() => @@ -137,108 +154,89 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise, Description("ADDHN{2} ., ., .")] - public void Addhn_V_8H8B_4S4H_2D2S([ValueSource("_4H2S1D_")] [Random(1)] ulong A0, - [ValueSource("_4H2S1D_")] [Random(1)] ulong A1, - [ValueSource("_4H2S1D_")] [Random(1)] ulong B0, - [ValueSource("_4H2S1D_")] [Random(1)] ulong B1, + 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, + [ValueSource("_4H2S1D_")] [Random(RndCnt)] ulong A, + [ValueSource("_4H2S1D_")] [Random(RndCnt)] ulong B, [Values(0b00u, 0b01u, 0b10u)] uint size) // <8H8B, 4S4H, 2D2S> { - uint Opcode = 0x0E224020; // ADDHN V0.8B, V1.8H, V2.8H + uint Opcode = 0x0E204000; // ADDHN V0.8B, V0.8H, V0.8H + Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); Opcode |= ((size & 3) << 22); Bits Op = new Bits(Opcode); - Vector128 V0 = MakeVectorE1(TestContext.CurrentContext.Random.NextULong()); - Vector128 V1 = MakeVectorE0E1(A0, A1); - Vector128 V2 = MakeVectorE0E1(B0, B1); + Vector128 V0 = MakeVectorE0E1(Z, Z); + Vector128 V1 = MakeVectorE0E1(A, A); + Vector128 V2 = MakeVectorE0E1(B, B); AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2); - AArch64.Vpart(1, 0, new Bits(A0)); - AArch64.Vpart(1, 1, new Bits(A1)); - AArch64.Vpart(2, 0, new Bits(B0)); - AArch64.Vpart(2, 1, new Bits(B1)); + 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)); SimdFp.Addhn_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.V(64, 0).ToUInt64())); - Assert.That(GetVectorE1(ThreadState.V0), Is.Zero); + 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())); }); } [Test, Pairwise, Description("ADDHN{2} ., ., .")] - public void Addhn_V_8H16B_4S8H_2D4S([ValueSource("_4H2S1D_")] [Random(1)] ulong A0, - [ValueSource("_4H2S1D_")] [Random(1)] ulong A1, - [ValueSource("_4H2S1D_")] [Random(1)] ulong B0, - [ValueSource("_4H2S1D_")] [Random(1)] ulong B1, + 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, + [ValueSource("_4H2S1D_")] [Random(RndCnt)] ulong A, + [ValueSource("_4H2S1D_")] [Random(RndCnt)] ulong B, [Values(0b00u, 0b01u, 0b10u)] uint size) // <8H16B, 4S8H, 2D4S> { - uint Opcode = 0x4E224020; // ADDHN2 V0.16B, V1.8H, V2.8H + uint Opcode = 0x4E204000; // ADDHN2 V0.16B, V0.8H, V0.8H + Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); Opcode |= ((size & 3) << 22); Bits Op = new Bits(Opcode); - ulong _E0 = TestContext.CurrentContext.Random.NextULong(); - Vector128 V0 = MakeVectorE0(_E0); - Vector128 V1 = MakeVectorE0E1(A0, A1); - Vector128 V2 = MakeVectorE0E1(B0, B1); + Vector128 V0 = MakeVectorE0E1(Z, Z); + Vector128 V1 = MakeVectorE0E1(A, A); + Vector128 V2 = MakeVectorE0E1(B, B); AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2); - AArch64.Vpart(1, 0, new Bits(A0)); - AArch64.Vpart(1, 1, new Bits(A1)); - AArch64.Vpart(2, 0, new Bits(B0)); - AArch64.Vpart(2, 1, new Bits(B1)); + 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)); SimdFp.Addhn_V(Op[30], Op[23, 22], Op[20, 16], Op[9, 5], Op[4, 0]); Assert.Multiple(() => { - Assert.That(GetVectorE0(ThreadState.V0), Is.EqualTo(_E0)); + 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())); }); } - [Test, Description("ADDP ., ., .")] - public void Addp_V_8B_4H_2S([ValueSource("_8B4H2S_")] [Random(1)] ulong A, - [ValueSource("_8B4H2S_")] [Random(1)] ulong B, - [Values(0b00u, 0b01u, 0b10u)] uint size) // <8B, 4H, 2S> - { - uint Opcode = 0x0E22BC20; // ADDP V0.8B, V1.8B, V2.8B - Opcode |= ((size & 3) << 22); - Bits Op = new Bits(Opcode); - - Vector128 V0 = MakeVectorE1(TestContext.CurrentContext.Random.NextULong()); - Vector128 V1 = MakeVectorE0(A); - Vector128 V2 = MakeVectorE0(B); - AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2); - - AArch64.V(1, new Bits(A)); - AArch64.V(2, new Bits(B)); - SimdFp.Addp_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.V(64, 0).ToUInt64())); - Assert.That(GetVectorE1(ThreadState.V0), Is.Zero); - }); - } - [Test, Pairwise, Description("ADDP ., ., .")] - public void Addp_V_16B_8H_4S_2D([ValueSource("_8B4H2S1D_")] [Random(1)] ulong A0, - [ValueSource("_8B4H2S1D_")] [Random(1)] ulong A1, - [ValueSource("_8B4H2S1D_")] [Random(1)] ulong B0, - [ValueSource("_8B4H2S1D_")] [Random(1)] ulong B1, - [Values(0b00u, 0b01u, 0b10u, 0b11u)] uint size) // <16B, 8H, 4S, 2D> + 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, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong A, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong B, + [Values(0b00u, 0b01u, 0b10u)] uint size) // <8B, 4H, 2S> { - uint Opcode = 0x4E22BC20; // ADDP V0.16B, V1.16B, V2.16B + uint Opcode = 0x0E20BC00; // ADDP 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); - Vector128 V1 = MakeVectorE0E1(A0, A1); - Vector128 V2 = MakeVectorE0E1(B0, B1); - AThreadState ThreadState = SingleOpcode(Opcode, V1: V1, V2: V2); + Vector128 V0 = MakeVectorE0E1(Z, Z); + Vector128 V1 = MakeVectorE0(A); + Vector128 V2 = MakeVectorE0(B); + AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2); - AArch64.Vpart(1, 0, new Bits(A0)); - AArch64.Vpart(1, 1, new Bits(A1)); - AArch64.Vpart(2, 0, new Bits(B0)); - AArch64.Vpart(2, 1, new Bits(B1)); + 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)); SimdFp.Addp_V(Op[30], Op[23, 22], Op[20, 16], Op[9, 5], Op[4, 0]); Assert.Multiple(() => @@ -248,46 +246,57 @@ namespace Ryujinx.Tests.Cpu }); } - [Test, Description("AND ., ., .")] - public void And_V_8B([ValueSource("_8B_")] [Random(1)] ulong A, - [ValueSource("_8B_")] [Random(1)] ulong B) + [Test, Pairwise, Description("ADDP ., ., .")] + 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, + [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 = 0x0E221C20; // AND V0.8B, V1.8B, V2.8B + uint Opcode = 0x4E20BC00; // ADDP 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); - Vector128 V0 = MakeVectorE1(TestContext.CurrentContext.Random.NextULong()); - Vector128 V1 = MakeVectorE0(A); - Vector128 V2 = MakeVectorE0(B); + Vector128 V0 = MakeVectorE0E1(Z, Z); + Vector128 V1 = MakeVectorE0E1(A, A); + Vector128 V2 = MakeVectorE0E1(B, B); AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2); - AArch64.V(1, new Bits(A)); - AArch64.V(2, new Bits(B)); - SimdFp.And_V(Op[30], Op[20, 16], Op[9, 5], Op[4, 0]); + 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)); + SimdFp.Addp_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.V(64, 0).ToUInt64())); - Assert.That(GetVectorE1(ThreadState.V0), Is.Zero); + 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())); }); } [Test, Pairwise, Description("AND ., ., .")] - public void And_V_16B([ValueSource("_8B_")] [Random(1)] ulong A0, - [ValueSource("_8B_")] [Random(1)] ulong A1, - [ValueSource("_8B_")] [Random(1)] ulong B0, - [ValueSource("_8B_")] [Random(1)] ulong B1) + 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, + [ValueSource("_8B_")] [Random(RndCnt)] ulong A, + [ValueSource("_8B_")] [Random(RndCnt)] ulong B) { - uint Opcode = 0x4E221C20; // AND V0.16B, V1.16B, V2.16B + uint Opcode = 0x0E201C00; // AND V0.8B, V0.8B, V0.8B + Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); Bits Op = new Bits(Opcode); - Vector128 V1 = MakeVectorE0E1(A0, A1); - Vector128 V2 = MakeVectorE0E1(B0, B1); - AThreadState ThreadState = SingleOpcode(Opcode, V1: V1, V2: V2); + Vector128 V0 = MakeVectorE0E1(Z, Z); + Vector128 V1 = MakeVectorE0(A); + Vector128 V2 = MakeVectorE0(B); + AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2); - AArch64.Vpart(1, 0, new Bits(A0)); - AArch64.Vpart(1, 1, new Bits(A1)); - AArch64.Vpart(2, 0, new Bits(B0)); - AArch64.Vpart(2, 1, new Bits(B1)); + 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)); SimdFp.And_V(Op[30], Op[20, 16], Op[9, 5], Op[4, 0]); Assert.Multiple(() => @@ -297,46 +306,55 @@ namespace Ryujinx.Tests.Cpu }); } - [Test, Description("BIC ., ., .")] - public void Bic_V_8B([ValueSource("_8B_")] [Random(1)] ulong A, - [ValueSource("_8B_")] [Random(1)] ulong B) + [Test, Pairwise, Description("AND ., ., .")] + 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, + [ValueSource("_8B_")] [Random(RndCnt)] ulong A, + [ValueSource("_8B_")] [Random(RndCnt)] ulong B) { - uint Opcode = 0x0E621C20; // BIC V0.8B, V1.8B, V2.8B + uint Opcode = 0x4E201C00; // AND V0.16B, V0.16B, V0.16B + Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); Bits Op = new Bits(Opcode); - Vector128 V0 = MakeVectorE1(TestContext.CurrentContext.Random.NextULong()); - Vector128 V1 = MakeVectorE0(A); - Vector128 V2 = MakeVectorE0(B); + Vector128 V0 = MakeVectorE0E1(Z, Z); + Vector128 V1 = MakeVectorE0E1(A, A); + Vector128 V2 = MakeVectorE0E1(B, B); AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2); - AArch64.V(1, new Bits(A)); - AArch64.V(2, new Bits(B)); - SimdFp.Bic_V(Op[30], Op[20, 16], Op[9, 5], Op[4, 0]); + 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)); + SimdFp.And_V(Op[30], Op[20, 16], Op[9, 5], Op[4, 0]); Assert.Multiple(() => { - Assert.That(GetVectorE0(ThreadState.V0), Is.EqualTo(AArch64.V(64, 0).ToUInt64())); - Assert.That(GetVectorE1(ThreadState.V0), Is.Zero); + 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())); }); } [Test, Pairwise, Description("BIC ., ., .")] - public void Bic_V_16B([ValueSource("_8B_")] [Random(1)] ulong A0, - [ValueSource("_8B_")] [Random(1)] ulong A1, - [ValueSource("_8B_")] [Random(1)] ulong B0, - [ValueSource("_8B_")] [Random(1)] ulong B1) + 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, + [ValueSource("_8B_")] [Random(RndCnt)] ulong A, + [ValueSource("_8B_")] [Random(RndCnt)] ulong B) { - uint Opcode = 0x4E621C20; // BIC V0.16B, V1.16B, V2.16B + uint Opcode = 0x0E601C00; // BIC V0.8B, V0.8B, V0.8B + Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); Bits Op = new Bits(Opcode); - Vector128 V1 = MakeVectorE0E1(A0, A1); - Vector128 V2 = MakeVectorE0E1(B0, B1); - AThreadState ThreadState = SingleOpcode(Opcode, V1: V1, V2: V2); + Vector128 V0 = MakeVectorE0E1(Z, Z); + Vector128 V1 = MakeVectorE0(A); + Vector128 V2 = MakeVectorE0(B); + AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2); - AArch64.Vpart(1, 0, new Bits(A0)); - AArch64.Vpart(1, 1, new Bits(A1)); - AArch64.Vpart(2, 0, new Bits(B0)); - AArch64.Vpart(2, 1, new Bits(B1)); + 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)); SimdFp.Bic_V(Op[30], Op[20, 16], Op[9, 5], Op[4, 0]); Assert.Multiple(() => @@ -346,53 +364,55 @@ namespace Ryujinx.Tests.Cpu }); } - [Test, Description("BIF ., ., .")] - public void Bif_V_8B([ValueSource("_8B_")] [Random(1)] ulong _Z, - [ValueSource("_8B_")] [Random(1)] ulong A, - [ValueSource("_8B_")] [Random(1)] ulong B) + [Test, Pairwise, Description("BIC ., ., .")] + 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, + [ValueSource("_8B_")] [Random(RndCnt)] ulong A, + [ValueSource("_8B_")] [Random(RndCnt)] ulong B) { - uint Opcode = 0x2EE21C20; // BIF V0.8B, V1.8B, V2.8B + uint Opcode = 0x4E601C00; // BIC V0.16B, V0.16B, V0.16B + Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); Bits Op = new Bits(Opcode); - Vector128 V0 = MakeVectorE0E1(_Z, TestContext.CurrentContext.Random.NextULong()); - Vector128 V1 = MakeVectorE0(A); - Vector128 V2 = MakeVectorE0(B); + Vector128 V0 = MakeVectorE0E1(Z, Z); + Vector128 V1 = MakeVectorE0E1(A, A); + Vector128 V2 = MakeVectorE0E1(B, B); AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2); - AArch64.Vpart(0, 0, new Bits(_Z)); - AArch64.V(1, new Bits(A)); - AArch64.V(2, new Bits(B)); - SimdFp.Bif_V(Op[30], Op[20, 16], Op[9, 5], Op[4, 0]); + 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)); + SimdFp.Bic_V(Op[30], Op[20, 16], Op[9, 5], Op[4, 0]); Assert.Multiple(() => { - Assert.That(GetVectorE0(ThreadState.V0), Is.EqualTo(AArch64.V(64, 0).ToUInt64())); - Assert.That(GetVectorE1(ThreadState.V0), Is.Zero); + 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())); }); } [Test, Pairwise, Description("BIF ., ., .")] - public void Bif_V_16B([ValueSource("_8B_")] [Random(1)] ulong _Z0, - [ValueSource("_8B_")] [Random(1)] ulong _Z1, - [ValueSource("_8B_")] [Random(1)] ulong A0, - [ValueSource("_8B_")] [Random(1)] ulong A1, - [ValueSource("_8B_")] [Random(1)] ulong B0, - [ValueSource("_8B_")] [Random(1)] ulong B1) + 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, + [ValueSource("_8B_")] [Random(RndCnt)] ulong A, + [ValueSource("_8B_")] [Random(RndCnt)] ulong B) { - uint Opcode = 0x6EE21C20; // BIF V0.16B, V1.16B, V2.16B + uint Opcode = 0x2EE01C00; // BIF V0.8B, V0.8B, V0.8B + Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); Bits Op = new Bits(Opcode); - Vector128 V0 = MakeVectorE0E1(_Z0, _Z1); - Vector128 V1 = MakeVectorE0E1(A0, A1); - Vector128 V2 = MakeVectorE0E1(B0, B1); + Vector128 V0 = MakeVectorE0E1(Z, Z); + Vector128 V1 = MakeVectorE0(A); + Vector128 V2 = MakeVectorE0(B); AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2); - AArch64.Vpart(0, 0, new Bits(_Z0)); - AArch64.Vpart(0, 1, new Bits(_Z1)); - AArch64.Vpart(1, 0, new Bits(A0)); - AArch64.Vpart(1, 1, new Bits(A1)); - AArch64.Vpart(2, 0, new Bits(B0)); - AArch64.Vpart(2, 1, new Bits(B1)); + 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)); SimdFp.Bif_V(Op[30], Op[20, 16], Op[9, 5], Op[4, 0]); Assert.Multiple(() => @@ -402,53 +422,55 @@ namespace Ryujinx.Tests.Cpu }); } - [Test, Description("BIT ., ., .")] - public void Bit_V_8B([ValueSource("_8B_")] [Random(1)] ulong _Z, - [ValueSource("_8B_")] [Random(1)] ulong A, - [ValueSource("_8B_")] [Random(1)] ulong B) + [Test, Pairwise, Description("BIF ., ., .")] + 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, + [ValueSource("_8B_")] [Random(RndCnt)] ulong A, + [ValueSource("_8B_")] [Random(RndCnt)] ulong B) { - uint Opcode = 0x2EA21C20; // BIT V0.8B, V1.8B, V2.8B + uint Opcode = 0x6EE01C00; // BIF V0.16B, V0.16B, V0.16B + Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); Bits Op = new Bits(Opcode); - Vector128 V0 = MakeVectorE0E1(_Z, TestContext.CurrentContext.Random.NextULong()); - Vector128 V1 = MakeVectorE0(A); - Vector128 V2 = MakeVectorE0(B); + Vector128 V0 = MakeVectorE0E1(Z, Z); + Vector128 V1 = MakeVectorE0E1(A, A); + Vector128 V2 = MakeVectorE0E1(B, B); AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2); - AArch64.Vpart(0, 0, new Bits(_Z)); - AArch64.V(1, new Bits(A)); - AArch64.V(2, new Bits(B)); - SimdFp.Bit_V(Op[30], Op[20, 16], Op[9, 5], Op[4, 0]); + 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)); + SimdFp.Bif_V(Op[30], Op[20, 16], Op[9, 5], Op[4, 0]); Assert.Multiple(() => { - Assert.That(GetVectorE0(ThreadState.V0), Is.EqualTo(AArch64.V(64, 0).ToUInt64())); - Assert.That(GetVectorE1(ThreadState.V0), Is.Zero); + 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())); }); } [Test, Pairwise, Description("BIT ., ., .")] - public void Bit_V_16B([ValueSource("_8B_")] [Random(1)] ulong _Z0, - [ValueSource("_8B_")] [Random(1)] ulong _Z1, - [ValueSource("_8B_")] [Random(1)] ulong A0, - [ValueSource("_8B_")] [Random(1)] ulong A1, - [ValueSource("_8B_")] [Random(1)] ulong B0, - [ValueSource("_8B_")] [Random(1)] ulong B1) + 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, + [ValueSource("_8B_")] [Random(RndCnt)] ulong A, + [ValueSource("_8B_")] [Random(RndCnt)] ulong B) { - uint Opcode = 0x6EA21C20; // BIT V0.16B, V1.16B, V2.16B + uint Opcode = 0x2EA01C00; // BIT V0.8B, V0.8B, V0.8B + Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); Bits Op = new Bits(Opcode); - Vector128 V0 = MakeVectorE0E1(_Z0, _Z1); - Vector128 V1 = MakeVectorE0E1(A0, A1); - Vector128 V2 = MakeVectorE0E1(B0, B1); + Vector128 V0 = MakeVectorE0E1(Z, Z); + Vector128 V1 = MakeVectorE0(A); + Vector128 V2 = MakeVectorE0(B); AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2); - AArch64.Vpart(0, 0, new Bits(_Z0)); - AArch64.Vpart(0, 1, new Bits(_Z1)); - AArch64.Vpart(1, 0, new Bits(A0)); - AArch64.Vpart(1, 1, new Bits(A1)); - AArch64.Vpart(2, 0, new Bits(B0)); - AArch64.Vpart(2, 1, new Bits(B1)); + 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)); SimdFp.Bit_V(Op[30], Op[20, 16], Op[9, 5], Op[4, 0]); Assert.Multiple(() => @@ -458,53 +480,55 @@ namespace Ryujinx.Tests.Cpu }); } - [Test, Description("BSL ., ., .")] - public void Bsl_V_8B([ValueSource("_8B_")] [Random(1)] ulong _Z, - [ValueSource("_8B_")] [Random(1)] ulong A, - [ValueSource("_8B_")] [Random(1)] ulong B) + [Test, Pairwise, Description("BIT ., ., .")] + 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, + [ValueSource("_8B_")] [Random(RndCnt)] ulong A, + [ValueSource("_8B_")] [Random(RndCnt)] ulong B) { - uint Opcode = 0x2E621C20; // BSL V0.8B, V1.8B, V2.8B + uint Opcode = 0x6EA01C00; // BIT V0.16B, V0.16B, V0.16B + Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); Bits Op = new Bits(Opcode); - Vector128 V0 = MakeVectorE0E1(_Z, TestContext.CurrentContext.Random.NextULong()); - Vector128 V1 = MakeVectorE0(A); - Vector128 V2 = MakeVectorE0(B); + Vector128 V0 = MakeVectorE0E1(Z, Z); + Vector128 V1 = MakeVectorE0E1(A, A); + Vector128 V2 = MakeVectorE0E1(B, B); AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2); - AArch64.Vpart(0, 0, new Bits(_Z)); - AArch64.V(1, new Bits(A)); - AArch64.V(2, new Bits(B)); - SimdFp.Bsl_V(Op[30], Op[20, 16], Op[9, 5], Op[4, 0]); + 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)); + SimdFp.Bit_V(Op[30], Op[20, 16], Op[9, 5], Op[4, 0]); Assert.Multiple(() => { - Assert.That(GetVectorE0(ThreadState.V0), Is.EqualTo(AArch64.V(64, 0).ToUInt64())); - Assert.That(GetVectorE1(ThreadState.V0), Is.Zero); + 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())); }); } [Test, Pairwise, Description("BSL ., ., .")] - public void Bsl_V_16B([ValueSource("_8B_")] [Random(1)] ulong _Z0, - [ValueSource("_8B_")] [Random(1)] ulong _Z1, - [ValueSource("_8B_")] [Random(1)] ulong A0, - [ValueSource("_8B_")] [Random(1)] ulong A1, - [ValueSource("_8B_")] [Random(1)] ulong B0, - [ValueSource("_8B_")] [Random(1)] ulong B1) + 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, + [ValueSource("_8B_")] [Random(RndCnt)] ulong A, + [ValueSource("_8B_")] [Random(RndCnt)] ulong B) { - uint Opcode = 0x6E621C20; // BSL V0.16B, V1.16B, V2.16B + uint Opcode = 0x2E601C00; // BSL V0.8B, V0.8B, V0.8B + Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); Bits Op = new Bits(Opcode); - Vector128 V0 = MakeVectorE0E1(_Z0, _Z1); - Vector128 V1 = MakeVectorE0E1(A0, A1); - Vector128 V2 = MakeVectorE0E1(B0, B1); + Vector128 V0 = MakeVectorE0E1(Z, Z); + Vector128 V1 = MakeVectorE0(A); + Vector128 V2 = MakeVectorE0(B); AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2); - AArch64.Vpart(0, 0, new Bits(_Z0)); - AArch64.Vpart(0, 1, new Bits(_Z1)); - AArch64.Vpart(1, 0, new Bits(A0)); - AArch64.Vpart(1, 1, new Bits(A1)); - AArch64.Vpart(2, 0, new Bits(B0)); - AArch64.Vpart(2, 1, new Bits(B1)); + 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)); SimdFp.Bsl_V(Op[30], Op[20, 16], Op[9, 5], Op[4, 0]); Assert.Multiple(() => @@ -514,73 +538,86 @@ namespace Ryujinx.Tests.Cpu }); } - [Test, Description("CMEQ , , ")] - public void Cmeq_S_D([ValueSource("_1D_")] [Random(1)] ulong A, - [ValueSource("_1D_")] [Random(1)] ulong B) + [Test, Pairwise, Description("BSL ., ., .")] + 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, + [ValueSource("_8B_")] [Random(RndCnt)] ulong A, + [ValueSource("_8B_")] [Random(RndCnt)] ulong B) { - uint Opcode = 0x7EE28C20; // CMEQ D0, D1, D2 + uint Opcode = 0x6E601C00; // BSL V0.16B, V0.16B, V0.16B + Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); Bits Op = new Bits(Opcode); - Vector128 V0 = MakeVectorE1(TestContext.CurrentContext.Random.NextULong()); + Vector128 V0 = MakeVectorE0E1(Z, Z); + Vector128 V1 = MakeVectorE0E1(A, A); + Vector128 V2 = MakeVectorE0E1(B, B); + AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2); + + 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)); + SimdFp.Bsl_V(Op[30], 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())); + }); + } + + [Test, Pairwise, Description("CMEQ , , ")] + 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, + [ValueSource("_1D_")] [Random(RndCnt)] ulong A, + [ValueSource("_1D_")] [Random(RndCnt)] ulong B) + { + uint Opcode = 0x7EE08C00; // CMEQ D0, D0, D0 + Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); + Bits Op = new Bits(Opcode); + + Vector128 V0 = MakeVectorE0E1(Z, Z); Vector128 V1 = MakeVectorE0(A); Vector128 V2 = MakeVectorE0(B); AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2); + 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)); SimdFp.Cmeq_Reg_S(Op[23, 22], Op[20, 16], Op[9, 5], Op[4, 0]); Assert.Multiple(() => { - Assert.That(GetVectorE0(ThreadState.V0), Is.EqualTo(AArch64.V(64, 0).ToUInt64())); - Assert.That(GetVectorE1(ThreadState.V0), Is.Zero); - }); - } - - [Test, Description("CMEQ ., ., .")] - public void Cmeq_V_8B_4H_2S([ValueSource("_8B4H2S_")] [Random(1)] ulong A, - [ValueSource("_8B4H2S_")] [Random(1)] ulong B, - [Values(0b00u, 0b01u, 0b10u)] uint size) // <8B, 4H, 2S> - { - uint Opcode = 0x2E228C20; // CMEQ V0.8B, V1.8B, V2.8B - Opcode |= ((size & 3) << 22); - Bits Op = new Bits(Opcode); - - Vector128 V0 = MakeVectorE1(TestContext.CurrentContext.Random.NextULong()); - Vector128 V1 = MakeVectorE0(A); - Vector128 V2 = MakeVectorE0(B); - AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2); - - AArch64.V(1, new Bits(A)); - AArch64.V(2, new Bits(B)); - SimdFp.Cmeq_Reg_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.V(64, 0).ToUInt64())); - Assert.That(GetVectorE1(ThreadState.V0), Is.Zero); + 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())); }); } [Test, Pairwise, Description("CMEQ ., ., .")] - public void Cmeq_V_16B_8H_4S_2D([ValueSource("_8B4H2S1D_")] [Random(1)] ulong A0, - [ValueSource("_8B4H2S1D_")] [Random(1)] ulong A1, - [ValueSource("_8B4H2S1D_")] [Random(1)] ulong B0, - [ValueSource("_8B4H2S1D_")] [Random(1)] ulong B1, - [Values(0b00u, 0b01u, 0b10u, 0b11u)] uint size) // <16B, 8H, 4S, 2D> + 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, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong A, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong B, + [Values(0b00u, 0b01u, 0b10u)] uint size) // <8B, 4H, 2S> { - uint Opcode = 0x6E228C20; // CMEQ V0.16B, V1.16B, V2.16B + uint Opcode = 0x2E208C00; // CMEQ 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); - Vector128 V1 = MakeVectorE0E1(A0, A1); - Vector128 V2 = MakeVectorE0E1(B0, B1); - AThreadState ThreadState = SingleOpcode(Opcode, V1: V1, V2: V2); + Vector128 V0 = MakeVectorE0E1(Z, Z); + Vector128 V1 = MakeVectorE0(A); + Vector128 V2 = MakeVectorE0(B); + AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2); - AArch64.Vpart(1, 0, new Bits(A0)); - AArch64.Vpart(1, 1, new Bits(A1)); - AArch64.Vpart(2, 0, new Bits(B0)); - AArch64.Vpart(2, 1, new Bits(B1)); + 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)); SimdFp.Cmeq_Reg_V(Op[30], Op[23, 22], Op[20, 16], Op[9, 5], Op[4, 0]); Assert.Multiple(() => @@ -590,73 +627,88 @@ namespace Ryujinx.Tests.Cpu }); } - [Test, Description("CMGE , , ")] - public void Cmge_S_D([ValueSource("_1D_")] [Random(1)] ulong A, - [ValueSource("_1D_")] [Random(1)] ulong B) + [Test, Pairwise, Description("CMEQ ., ., .")] + 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, + [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 = 0x5EE23C20; // CMGE D0, D1, D2 + uint Opcode = 0x6E208C00; // CMEQ 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); - Vector128 V0 = MakeVectorE1(TestContext.CurrentContext.Random.NextULong()); + Vector128 V0 = MakeVectorE0E1(Z, Z); + Vector128 V1 = MakeVectorE0E1(A, A); + Vector128 V2 = MakeVectorE0E1(B, B); + AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2); + + 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)); + SimdFp.Cmeq_Reg_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())); + }); + } + + [Test, Pairwise, Description("CMGE , , ")] + 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, + [ValueSource("_1D_")] [Random(RndCnt)] ulong A, + [ValueSource("_1D_")] [Random(RndCnt)] ulong B) + { + uint Opcode = 0x5EE03C00; // CMGE D0, D0, D0 + Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); + Bits Op = new Bits(Opcode); + + Vector128 V0 = MakeVectorE0E1(Z, Z); Vector128 V1 = MakeVectorE0(A); Vector128 V2 = MakeVectorE0(B); AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2); + 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)); SimdFp.Cmge_Reg_S(Op[23, 22], Op[20, 16], Op[9, 5], Op[4, 0]); Assert.Multiple(() => { - Assert.That(GetVectorE0(ThreadState.V0), Is.EqualTo(AArch64.V(64, 0).ToUInt64())); - Assert.That(GetVectorE1(ThreadState.V0), Is.Zero); - }); - } - - [Test, Description("CMGE ., ., .")] - public void Cmge_V_8B_4H_2S([ValueSource("_8B4H2S_")] [Random(1)] ulong A, - [ValueSource("_8B4H2S_")] [Random(1)] ulong B, - [Values(0b00u, 0b01u, 0b10u)] uint size) // <8B, 4H, 2S> - { - uint Opcode = 0x0E223C20; // CMGE V0.8B, V1.8B, V2.8B - Opcode |= ((size & 3) << 22); - Bits Op = new Bits(Opcode); - - Vector128 V0 = MakeVectorE1(TestContext.CurrentContext.Random.NextULong()); - Vector128 V1 = MakeVectorE0(A); - Vector128 V2 = MakeVectorE0(B); - AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2); - - AArch64.V(1, new Bits(A)); - AArch64.V(2, new Bits(B)); - SimdFp.Cmge_Reg_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.V(64, 0).ToUInt64())); - Assert.That(GetVectorE1(ThreadState.V0), Is.Zero); + 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())); }); } [Test, Pairwise, Description("CMGE ., ., .")] - public void Cmge_V_16B_8H_4S_2D([ValueSource("_8B4H2S1D_")] [Random(1)] ulong A0, - [ValueSource("_8B4H2S1D_")] [Random(1)] ulong A1, - [ValueSource("_8B4H2S1D_")] [Random(1)] ulong B0, - [ValueSource("_8B4H2S1D_")] [Random(1)] ulong B1, - [Values(0b00u, 0b01u, 0b10u, 0b11u)] uint size) // <16B, 8H, 4S, 2D> + 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, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong A, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong B, + [Values(0b00u, 0b01u, 0b10u)] uint size) // <8B, 4H, 2S> { - uint Opcode = 0x4E223C20; // CMGE V0.16B, V1.16B, V2.16B + uint Opcode = 0x0E203C00; // CMGE 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); - Vector128 V1 = MakeVectorE0E1(A0, A1); - Vector128 V2 = MakeVectorE0E1(B0, B1); - AThreadState ThreadState = SingleOpcode(Opcode, V1: V1, V2: V2); + Vector128 V0 = MakeVectorE0E1(Z, Z); + Vector128 V1 = MakeVectorE0(A); + Vector128 V2 = MakeVectorE0(B); + AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2); - AArch64.Vpart(1, 0, new Bits(A0)); - AArch64.Vpart(1, 1, new Bits(A1)); - AArch64.Vpart(2, 0, new Bits(B0)); - AArch64.Vpart(2, 1, new Bits(B1)); + 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)); SimdFp.Cmge_Reg_V(Op[30], Op[23, 22], Op[20, 16], Op[9, 5], Op[4, 0]); Assert.Multiple(() => @@ -666,73 +718,88 @@ namespace Ryujinx.Tests.Cpu }); } - [Test, Description("CMGT , , ")] - public void Cmgt_S_D([ValueSource("_1D_")] [Random(1)] ulong A, - [ValueSource("_1D_")] [Random(1)] ulong B) + [Test, Pairwise, Description("CMGE ., ., .")] + 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, + [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 = 0x5EE23420; // CMGT D0, D1, D2 + uint Opcode = 0x4E203C00; // CMGE 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); - Vector128 V0 = MakeVectorE1(TestContext.CurrentContext.Random.NextULong()); + Vector128 V0 = MakeVectorE0E1(Z, Z); + Vector128 V1 = MakeVectorE0E1(A, A); + Vector128 V2 = MakeVectorE0E1(B, B); + AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2); + + 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)); + SimdFp.Cmge_Reg_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())); + }); + } + + [Test, Pairwise, Description("CMGT , , ")] + 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, + [ValueSource("_1D_")] [Random(RndCnt)] ulong A, + [ValueSource("_1D_")] [Random(RndCnt)] ulong B) + { + uint Opcode = 0x5EE03400; // CMGT D0, D0, D0 + Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); + Bits Op = new Bits(Opcode); + + Vector128 V0 = MakeVectorE0E1(Z, Z); Vector128 V1 = MakeVectorE0(A); Vector128 V2 = MakeVectorE0(B); AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2); + 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)); SimdFp.Cmgt_Reg_S(Op[23, 22], Op[20, 16], Op[9, 5], Op[4, 0]); Assert.Multiple(() => { - Assert.That(GetVectorE0(ThreadState.V0), Is.EqualTo(AArch64.V(64, 0).ToUInt64())); - Assert.That(GetVectorE1(ThreadState.V0), Is.Zero); - }); - } - - [Test, Description("CMGT ., ., .")] - public void Cmgt_V_8B_4H_2S([ValueSource("_8B4H2S_")] [Random(1)] ulong A, - [ValueSource("_8B4H2S_")] [Random(1)] ulong B, - [Values(0b00u, 0b01u, 0b10u)] uint size) // <8B, 4H, 2S> - { - uint Opcode = 0x0E223420; // CMGT V0.8B, V1.8B, V2.8B - Opcode |= ((size & 3) << 22); - Bits Op = new Bits(Opcode); - - Vector128 V0 = MakeVectorE1(TestContext.CurrentContext.Random.NextULong()); - Vector128 V1 = MakeVectorE0(A); - Vector128 V2 = MakeVectorE0(B); - AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2); - - AArch64.V(1, new Bits(A)); - AArch64.V(2, new Bits(B)); - SimdFp.Cmgt_Reg_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.V(64, 0).ToUInt64())); - Assert.That(GetVectorE1(ThreadState.V0), Is.Zero); + 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())); }); } [Test, Pairwise, Description("CMGT ., ., .")] - public void Cmgt_V_16B_8H_4S_2D([ValueSource("_8B4H2S1D_")] [Random(1)] ulong A0, - [ValueSource("_8B4H2S1D_")] [Random(1)] ulong A1, - [ValueSource("_8B4H2S1D_")] [Random(1)] ulong B0, - [ValueSource("_8B4H2S1D_")] [Random(1)] ulong B1, - [Values(0b00u, 0b01u, 0b10u, 0b11u)] uint size) // <16B, 8H, 4S, 2D> + 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, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong A, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong B, + [Values(0b00u, 0b01u, 0b10u)] uint size) // <8B, 4H, 2S> { - uint Opcode = 0x4E223420; // CMGT V0.16B, V1.16B, V2.16B + uint Opcode = 0x0E203400; // CMGT 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); - Vector128 V1 = MakeVectorE0E1(A0, A1); - Vector128 V2 = MakeVectorE0E1(B0, B1); - AThreadState ThreadState = SingleOpcode(Opcode, V1: V1, V2: V2); + Vector128 V0 = MakeVectorE0E1(Z, Z); + Vector128 V1 = MakeVectorE0(A); + Vector128 V2 = MakeVectorE0(B); + AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2); - AArch64.Vpart(1, 0, new Bits(A0)); - AArch64.Vpart(1, 1, new Bits(A1)); - AArch64.Vpart(2, 0, new Bits(B0)); - AArch64.Vpart(2, 1, new Bits(B1)); + 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)); SimdFp.Cmgt_Reg_V(Op[30], Op[23, 22], Op[20, 16], Op[9, 5], Op[4, 0]); Assert.Multiple(() => @@ -742,73 +809,88 @@ namespace Ryujinx.Tests.Cpu }); } - [Test, Description("CMHI , , ")] - public void Cmhi_S_D([ValueSource("_1D_")] [Random(1)] ulong A, - [ValueSource("_1D_")] [Random(1)] ulong B) + [Test, Pairwise, Description("CMGT ., ., .")] + 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, + [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 = 0x7EE23420; // CMHI D0, D1, D2 + uint Opcode = 0x4E203400; // CMGT 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); - Vector128 V0 = MakeVectorE1(TestContext.CurrentContext.Random.NextULong()); + Vector128 V0 = MakeVectorE0E1(Z, Z); + Vector128 V1 = MakeVectorE0E1(A, A); + Vector128 V2 = MakeVectorE0E1(B, B); + AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2); + + 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)); + SimdFp.Cmgt_Reg_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())); + }); + } + + [Test, Pairwise, Description("CMHI , , ")] + 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, + [ValueSource("_1D_")] [Random(RndCnt)] ulong A, + [ValueSource("_1D_")] [Random(RndCnt)] ulong B) + { + uint Opcode = 0x7EE03400; // CMHI D0, D0, D0 + Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); + Bits Op = new Bits(Opcode); + + Vector128 V0 = MakeVectorE0E1(Z, Z); Vector128 V1 = MakeVectorE0(A); Vector128 V2 = MakeVectorE0(B); AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2); + 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)); SimdFp.Cmhi_S(Op[23, 22], Op[20, 16], Op[9, 5], Op[4, 0]); Assert.Multiple(() => { - Assert.That(GetVectorE0(ThreadState.V0), Is.EqualTo(AArch64.V(64, 0).ToUInt64())); - Assert.That(GetVectorE1(ThreadState.V0), Is.Zero); - }); - } - - [Test, Description("CMHI ., ., .")] - public void Cmhi_V_8B_4H_2S([ValueSource("_8B4H2S_")] [Random(1)] ulong A, - [ValueSource("_8B4H2S_")] [Random(1)] ulong B, - [Values(0b00u, 0b01u, 0b10u)] uint size) // <8B, 4H, 2S> - { - uint Opcode = 0x2E223420; // CMHI V0.8B, V1.8B, V2.8B - Opcode |= ((size & 3) << 22); - Bits Op = new Bits(Opcode); - - Vector128 V0 = MakeVectorE1(TestContext.CurrentContext.Random.NextULong()); - Vector128 V1 = MakeVectorE0(A); - Vector128 V2 = MakeVectorE0(B); - AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2); - - AArch64.V(1, new Bits(A)); - AArch64.V(2, new Bits(B)); - SimdFp.Cmhi_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.V(64, 0).ToUInt64())); - Assert.That(GetVectorE1(ThreadState.V0), Is.Zero); + 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())); }); } [Test, Pairwise, Description("CMHI ., ., .")] - public void Cmhi_V_16B_8H_4S_2D([ValueSource("_8B4H2S1D_")] [Random(1)] ulong A0, - [ValueSource("_8B4H2S1D_")] [Random(1)] ulong A1, - [ValueSource("_8B4H2S1D_")] [Random(1)] ulong B0, - [ValueSource("_8B4H2S1D_")] [Random(1)] ulong B1, - [Values(0b00u, 0b01u, 0b10u, 0b11u)] uint size) // <16B, 8H, 4S, 2D> + 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, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong A, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong B, + [Values(0b00u, 0b01u, 0b10u)] uint size) // <8B, 4H, 2S> { - uint Opcode = 0x6E223420; // CMHI V0.16B, V1.16B, V2.16B + uint Opcode = 0x2E203400; // CMHI 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); - Vector128 V1 = MakeVectorE0E1(A0, A1); - Vector128 V2 = MakeVectorE0E1(B0, B1); - AThreadState ThreadState = SingleOpcode(Opcode, V1: V1, V2: V2); + Vector128 V0 = MakeVectorE0E1(Z, Z); + Vector128 V1 = MakeVectorE0(A); + Vector128 V2 = MakeVectorE0(B); + AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2); - AArch64.Vpart(1, 0, new Bits(A0)); - AArch64.Vpart(1, 1, new Bits(A1)); - AArch64.Vpart(2, 0, new Bits(B0)); - AArch64.Vpart(2, 1, new Bits(B1)); + 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)); SimdFp.Cmhi_V(Op[30], Op[23, 22], Op[20, 16], Op[9, 5], Op[4, 0]); Assert.Multiple(() => @@ -818,73 +900,88 @@ namespace Ryujinx.Tests.Cpu }); } - [Test, Description("CMHS , , ")] - public void Cmhs_S_D([ValueSource("_1D_")] [Random(1)] ulong A, - [ValueSource("_1D_")] [Random(1)] ulong B) + [Test, Pairwise, Description("CMHI ., ., .")] + 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, + [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 = 0x7EE23C20; // CMHS D0, D1, D2 + uint Opcode = 0x6E203400; // CMHI 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); - Vector128 V0 = MakeVectorE1(TestContext.CurrentContext.Random.NextULong()); + Vector128 V0 = MakeVectorE0E1(Z, Z); + Vector128 V1 = MakeVectorE0E1(A, A); + Vector128 V2 = MakeVectorE0E1(B, B); + AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2); + + 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)); + SimdFp.Cmhi_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())); + }); + } + + [Test, Pairwise, Description("CMHS , , ")] + 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, + [ValueSource("_1D_")] [Random(RndCnt)] ulong A, + [ValueSource("_1D_")] [Random(RndCnt)] ulong B) + { + uint Opcode = 0x7EE03C00; // CMHS D0, D0, D0 + Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); + Bits Op = new Bits(Opcode); + + Vector128 V0 = MakeVectorE0E1(Z, Z); Vector128 V1 = MakeVectorE0(A); Vector128 V2 = MakeVectorE0(B); AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2); + 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)); SimdFp.Cmhs_S(Op[23, 22], Op[20, 16], Op[9, 5], Op[4, 0]); Assert.Multiple(() => { - Assert.That(GetVectorE0(ThreadState.V0), Is.EqualTo(AArch64.V(64, 0).ToUInt64())); - Assert.That(GetVectorE1(ThreadState.V0), Is.Zero); - }); - } - - [Test, Description("CMHS ., ., .")] - public void Cmhs_V_8B_4H_2S([ValueSource("_8B4H2S_")] [Random(1)] ulong A, - [ValueSource("_8B4H2S_")] [Random(1)] ulong B, - [Values(0b00u, 0b01u, 0b10u)] uint size) // <8B, 4H, 2S> - { - uint Opcode = 0x2E223C20; // CMHS V0.8B, V1.8B, V2.8B - Opcode |= ((size & 3) << 22); - Bits Op = new Bits(Opcode); - - Vector128 V0 = MakeVectorE1(TestContext.CurrentContext.Random.NextULong()); - Vector128 V1 = MakeVectorE0(A); - Vector128 V2 = MakeVectorE0(B); - AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2); - - AArch64.V(1, new Bits(A)); - AArch64.V(2, new Bits(B)); - SimdFp.Cmhs_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.V(64, 0).ToUInt64())); - Assert.That(GetVectorE1(ThreadState.V0), Is.Zero); + 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())); }); } [Test, Pairwise, Description("CMHS ., ., .")] - public void Cmhs_V_16B_8H_4S_2D([ValueSource("_8B4H2S1D_")] [Random(1)] ulong A0, - [ValueSource("_8B4H2S1D_")] [Random(1)] ulong A1, - [ValueSource("_8B4H2S1D_")] [Random(1)] ulong B0, - [ValueSource("_8B4H2S1D_")] [Random(1)] ulong B1, - [Values(0b00u, 0b01u, 0b10u, 0b11u)] uint size) // <16B, 8H, 4S, 2D> + 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, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong A, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong B, + [Values(0b00u, 0b01u, 0b10u)] uint size) // <8B, 4H, 2S> { - uint Opcode = 0x6E223C20; // CMHS V0.16B, V1.16B, V2.16B + uint Opcode = 0x2E203C00; // CMHS 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); - Vector128 V1 = MakeVectorE0E1(A0, A1); - Vector128 V2 = MakeVectorE0E1(B0, B1); - AThreadState ThreadState = SingleOpcode(Opcode, V1: V1, V2: V2); + Vector128 V0 = MakeVectorE0E1(Z, Z); + Vector128 V1 = MakeVectorE0(A); + Vector128 V2 = MakeVectorE0(B); + AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2); - AArch64.Vpart(1, 0, new Bits(A0)); - AArch64.Vpart(1, 1, new Bits(A1)); - AArch64.Vpart(2, 0, new Bits(B0)); - AArch64.Vpart(2, 1, new Bits(B1)); + 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)); SimdFp.Cmhs_V(Op[30], Op[23, 22], Op[20, 16], Op[9, 5], Op[4, 0]); Assert.Multiple(() => @@ -894,73 +991,88 @@ namespace Ryujinx.Tests.Cpu }); } - [Test, Description("CMTST , , ")] - public void Cmtst_S_D([ValueSource("_1D_")] [Random(1)] ulong A, - [ValueSource("_1D_")] [Random(1)] ulong B) + [Test, Pairwise, Description("CMHS ., ., .")] + 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, + [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 = 0x5EE28C20; // CMTST D0, D1, D2 + uint Opcode = 0x6E203C00; // CMHS 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); - Vector128 V0 = MakeVectorE1(TestContext.CurrentContext.Random.NextULong()); + Vector128 V0 = MakeVectorE0E1(Z, Z); + Vector128 V1 = MakeVectorE0E1(A, A); + Vector128 V2 = MakeVectorE0E1(B, B); + AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2); + + 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)); + SimdFp.Cmhs_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())); + }); + } + + [Test, Pairwise, Description("CMTST , , ")] + 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, + [ValueSource("_1D_")] [Random(RndCnt)] ulong A, + [ValueSource("_1D_")] [Random(RndCnt)] ulong B) + { + uint Opcode = 0x5EE08C00; // CMTST D0, D0, D0 + Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); + Bits Op = new Bits(Opcode); + + Vector128 V0 = MakeVectorE0E1(Z, Z); Vector128 V1 = MakeVectorE0(A); Vector128 V2 = MakeVectorE0(B); AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2); + 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)); SimdFp.Cmtst_S(Op[23, 22], Op[20, 16], Op[9, 5], Op[4, 0]); Assert.Multiple(() => { - Assert.That(GetVectorE0(ThreadState.V0), Is.EqualTo(AArch64.V(64, 0).ToUInt64())); - Assert.That(GetVectorE1(ThreadState.V0), Is.Zero); - }); - } - - [Test, Description("CMTST ., ., .")] - public void Cmtst_V_8B_4H_2S([ValueSource("_8B4H2S_")] [Random(1)] ulong A, - [ValueSource("_8B4H2S_")] [Random(1)] ulong B, - [Values(0b00u, 0b01u, 0b10u)] uint size) // <8B, 4H, 2S> - { - uint Opcode = 0x0E228C20; // CMTST V0.8B, V1.8B, V2.8B - Opcode |= ((size & 3) << 22); - Bits Op = new Bits(Opcode); - - Vector128 V0 = MakeVectorE1(TestContext.CurrentContext.Random.NextULong()); - Vector128 V1 = MakeVectorE0(A); - Vector128 V2 = MakeVectorE0(B); - AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2); - - AArch64.V(1, new Bits(A)); - AArch64.V(2, new Bits(B)); - SimdFp.Cmtst_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.V(64, 0).ToUInt64())); - Assert.That(GetVectorE1(ThreadState.V0), Is.Zero); + 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())); }); } [Test, Pairwise, Description("CMTST ., ., .")] - public void Cmtst_V_16B_8H_4S_2D([ValueSource("_8B4H2S1D_")] [Random(1)] ulong A0, - [ValueSource("_8B4H2S1D_")] [Random(1)] ulong A1, - [ValueSource("_8B4H2S1D_")] [Random(1)] ulong B0, - [ValueSource("_8B4H2S1D_")] [Random(1)] ulong B1, - [Values(0b00u, 0b01u, 0b10u, 0b11u)] uint size) // <16B, 8H, 4S, 2D> + 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, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong A, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong B, + [Values(0b00u, 0b01u, 0b10u)] uint size) // <8B, 4H, 2S> { - uint Opcode = 0x4E228C20; // CMTST V0.16B, V1.16B, V2.16B + uint Opcode = 0x0E208C00; // CMTST 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); - Vector128 V1 = MakeVectorE0E1(A0, A1); - Vector128 V2 = MakeVectorE0E1(B0, B1); - AThreadState ThreadState = SingleOpcode(Opcode, V1: V1, V2: V2); + Vector128 V0 = MakeVectorE0E1(Z, Z); + Vector128 V1 = MakeVectorE0(A); + Vector128 V2 = MakeVectorE0(B); + AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2); - AArch64.Vpart(1, 0, new Bits(A0)); - AArch64.Vpart(1, 1, new Bits(A1)); - AArch64.Vpart(2, 0, new Bits(B0)); - AArch64.Vpart(2, 1, new Bits(B1)); + 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)); SimdFp.Cmtst_V(Op[30], Op[23, 22], Op[20, 16], Op[9, 5], Op[4, 0]); Assert.Multiple(() => @@ -970,46 +1082,57 @@ namespace Ryujinx.Tests.Cpu }); } - [Test, Description("EOR ., ., .")] - public void Eor_V_8B([ValueSource("_8B_")] [Random(1)] ulong A, - [ValueSource("_8B_")] [Random(1)] ulong B) + [Test, Pairwise, Description("CMTST ., ., .")] + 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, + [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 = 0x2E221C20; // EOR V0.8B, V1.8B, V2.8B + uint Opcode = 0x4E208C00; // CMTST 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); - Vector128 V0 = MakeVectorE1(TestContext.CurrentContext.Random.NextULong()); - Vector128 V1 = MakeVectorE0(A); - Vector128 V2 = MakeVectorE0(B); + Vector128 V0 = MakeVectorE0E1(Z, Z); + Vector128 V1 = MakeVectorE0E1(A, A); + Vector128 V2 = MakeVectorE0E1(B, B); AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2); - AArch64.V(1, new Bits(A)); - AArch64.V(2, new Bits(B)); - SimdFp.Eor_V(Op[30], Op[20, 16], Op[9, 5], Op[4, 0]); + 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)); + SimdFp.Cmtst_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.V(64, 0).ToUInt64())); - Assert.That(GetVectorE1(ThreadState.V0), Is.Zero); + 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())); }); } [Test, Pairwise, Description("EOR ., ., .")] - public void Eor_V_16B([ValueSource("_8B_")] [Random(1)] ulong A0, - [ValueSource("_8B_")] [Random(1)] ulong A1, - [ValueSource("_8B_")] [Random(1)] ulong B0, - [ValueSource("_8B_")] [Random(1)] ulong B1) + 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, + [ValueSource("_8B_")] [Random(RndCnt)] ulong A, + [ValueSource("_8B_")] [Random(RndCnt)] ulong B) { - uint Opcode = 0x6E221C20; // EOR V0.16B, V1.16B, V2.16B + uint Opcode = 0x2E201C00; // EOR V0.8B, V0.8B, V0.8B + Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); Bits Op = new Bits(Opcode); - Vector128 V1 = MakeVectorE0E1(A0, A1); - Vector128 V2 = MakeVectorE0E1(B0, B1); - AThreadState ThreadState = SingleOpcode(Opcode, V1: V1, V2: V2); + Vector128 V0 = MakeVectorE0E1(Z, Z); + Vector128 V1 = MakeVectorE0(A); + Vector128 V2 = MakeVectorE0(B); + AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2); - AArch64.Vpart(1, 0, new Bits(A0)); - AArch64.Vpart(1, 1, new Bits(A1)); - AArch64.Vpart(2, 0, new Bits(B0)); - AArch64.Vpart(2, 1, new Bits(B1)); + 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)); SimdFp.Eor_V(Op[30], Op[20, 16], Op[9, 5], Op[4, 0]); Assert.Multiple(() => @@ -1019,46 +1142,55 @@ namespace Ryujinx.Tests.Cpu }); } - [Test, Description("ORN ., ., .")] - public void Orn_V_8B([ValueSource("_8B_")] [Random(1)] ulong A, - [ValueSource("_8B_")] [Random(1)] ulong B) + [Test, Pairwise, Description("EOR ., ., .")] + 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, + [ValueSource("_8B_")] [Random(RndCnt)] ulong A, + [ValueSource("_8B_")] [Random(RndCnt)] ulong B) { - uint Opcode = 0x0EE21C20; // ORN V0.8B, V1.8B, V2.8B + uint Opcode = 0x6E201C00; // EOR V0.16B, V0.16B, V0.16B + Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); Bits Op = new Bits(Opcode); - Vector128 V0 = MakeVectorE1(TestContext.CurrentContext.Random.NextULong()); - Vector128 V1 = MakeVectorE0(A); - Vector128 V2 = MakeVectorE0(B); + Vector128 V0 = MakeVectorE0E1(Z, Z); + Vector128 V1 = MakeVectorE0E1(A, A); + Vector128 V2 = MakeVectorE0E1(B, B); AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2); - AArch64.V(1, new Bits(A)); - AArch64.V(2, new Bits(B)); - SimdFp.Orn_V(Op[30], Op[20, 16], Op[9, 5], Op[4, 0]); + 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)); + SimdFp.Eor_V(Op[30], Op[20, 16], Op[9, 5], Op[4, 0]); Assert.Multiple(() => { - Assert.That(GetVectorE0(ThreadState.V0), Is.EqualTo(AArch64.V(64, 0).ToUInt64())); - Assert.That(GetVectorE1(ThreadState.V0), Is.Zero); + 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())); }); } [Test, Pairwise, Description("ORN ., ., .")] - public void Orn_V_16B([ValueSource("_8B_")] [Random(1)] ulong A0, - [ValueSource("_8B_")] [Random(1)] ulong A1, - [ValueSource("_8B_")] [Random(1)] ulong B0, - [ValueSource("_8B_")] [Random(1)] ulong B1) + 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, + [ValueSource("_8B_")] [Random(RndCnt)] ulong A, + [ValueSource("_8B_")] [Random(RndCnt)] ulong B) { - uint Opcode = 0x4EE21C20; // ORN V0.16B, V1.16B, V2.16B + uint Opcode = 0x0EE01C00; // ORN V0.8B, V0.8B, V0.8B + Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); Bits Op = new Bits(Opcode); - Vector128 V1 = MakeVectorE0E1(A0, A1); - Vector128 V2 = MakeVectorE0E1(B0, B1); - AThreadState ThreadState = SingleOpcode(Opcode, V1: V1, V2: V2); + Vector128 V0 = MakeVectorE0E1(Z, Z); + Vector128 V1 = MakeVectorE0(A); + Vector128 V2 = MakeVectorE0(B); + AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2); - AArch64.Vpart(1, 0, new Bits(A0)); - AArch64.Vpart(1, 1, new Bits(A1)); - AArch64.Vpart(2, 0, new Bits(B0)); - AArch64.Vpart(2, 1, new Bits(B1)); + 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)); SimdFp.Orn_V(Op[30], Op[20, 16], Op[9, 5], Op[4, 0]); Assert.Multiple(() => @@ -1068,46 +1200,84 @@ namespace Ryujinx.Tests.Cpu }); } - [Test, Description("ORR ., ., .")] - public void Orr_V_8B([ValueSource("_8B_")] [Random(1)] ulong A, - [ValueSource("_8B_")] [Random(1)] ulong B) + [Test, Pairwise, Description("ORN ., ., .")] + 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, + [ValueSource("_8B_")] [Random(RndCnt)] ulong A, + [ValueSource("_8B_")] [Random(RndCnt)] ulong B) { - uint Opcode = 0x0EA21C20; // ORR V0.8B, V1.8B, V2.8B + uint Opcode = 0x4EE01C00; // ORN V0.16B, V0.16B, V0.16B + Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); Bits Op = new Bits(Opcode); - Vector128 V0 = MakeVectorE1(TestContext.CurrentContext.Random.NextULong()); - Vector128 V1 = MakeVectorE0(A); - Vector128 V2 = MakeVectorE0(B); + Vector128 V0 = MakeVectorE0E1(Z, Z); + Vector128 V1 = MakeVectorE0E1(A, A); + Vector128 V2 = MakeVectorE0E1(B, B); AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2); - AArch64.V(1, new Bits(A)); - AArch64.V(2, new Bits(B)); - SimdFp.Orr_V(Op[30], Op[20, 16], Op[9, 5], Op[4, 0]); + 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)); + SimdFp.Orn_V(Op[30], Op[20, 16], Op[9, 5], Op[4, 0]); Assert.Multiple(() => { - Assert.That(GetVectorE0(ThreadState.V0), Is.EqualTo(AArch64.V(64, 0).ToUInt64())); - Assert.That(GetVectorE1(ThreadState.V0), Is.Zero); + 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())); }); } [Test, Pairwise, Description("ORR ., ., .")] - public void Orr_V_16B([ValueSource("_8B_")] [Random(1)] ulong A0, - [ValueSource("_8B_")] [Random(1)] ulong A1, - [ValueSource("_8B_")] [Random(1)] ulong B0, - [ValueSource("_8B_")] [Random(1)] ulong B1) + 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, + [ValueSource("_8B_")] [Random(RndCnt)] ulong A, + [ValueSource("_8B_")] [Random(RndCnt)] ulong B) { - uint Opcode = 0x4EA21C20; // ORR V0.16B, V1.16B, V2.16B + uint Opcode = 0x0EA01C00; // ORR V0.8B, V0.8B, V0.8B + Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); Bits Op = new Bits(Opcode); - Vector128 V1 = MakeVectorE0E1(A0, A1); - Vector128 V2 = MakeVectorE0E1(B0, B1); - AThreadState ThreadState = SingleOpcode(Opcode, V1: V1, V2: V2); + Vector128 V0 = MakeVectorE0E1(Z, Z); + Vector128 V1 = MakeVectorE0(A); + Vector128 V2 = MakeVectorE0(B); + AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2); - AArch64.Vpart(1, 0, new Bits(A0)); - AArch64.Vpart(1, 1, new Bits(A1)); - AArch64.Vpart(2, 0, new Bits(B0)); - AArch64.Vpart(2, 1, new Bits(B1)); + 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)); + SimdFp.Orr_V(Op[30], 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())); + }); + } + + [Test, Pairwise, Description("ORR ., ., .")] + 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, + [ValueSource("_8B_")] [Random(RndCnt)] ulong A, + [ValueSource("_8B_")] [Random(RndCnt)] ulong B) + { + uint Opcode = 0x4EA01C00; // ORR V0.16B, V0.16B, V0.16B + Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); + Bits Op = new Bits(Opcode); + + Vector128 V0 = MakeVectorE0E1(Z, Z); + Vector128 V1 = MakeVectorE0E1(A, A); + Vector128 V2 = MakeVectorE0E1(B, B); + AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2); + + 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)); SimdFp.Orr_V(Op[30], Op[20, 16], Op[9, 5], Op[4, 0]); Assert.Multiple(() => @@ -1118,174 +1288,182 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise, Description("RADDHN{2} ., ., .")] - public void Raddhn_V_8H8B_4S4H_2D2S([ValueSource("_4H2S1D_")] [Random(1)] ulong A0, - [ValueSource("_4H2S1D_")] [Random(1)] ulong A1, - [ValueSource("_4H2S1D_")] [Random(1)] ulong B0, - [ValueSource("_4H2S1D_")] [Random(1)] ulong B1, + 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, + [ValueSource("_4H2S1D_")] [Random(RndCnt)] ulong A, + [ValueSource("_4H2S1D_")] [Random(RndCnt)] ulong B, [Values(0b00u, 0b01u, 0b10u)] uint size) // <8H8B, 4S4H, 2D2S> { - uint Opcode = 0x2E224020; // RADDHN V0.8B, V1.8H, V2.8H + uint Opcode = 0x2E204000; // RADDHN V0.8B, V0.8H, V0.8H + Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); Opcode |= ((size & 3) << 22); Bits Op = new Bits(Opcode); - Vector128 V0 = MakeVectorE1(TestContext.CurrentContext.Random.NextULong()); - Vector128 V1 = MakeVectorE0E1(A0, A1); - Vector128 V2 = MakeVectorE0E1(B0, B1); + Vector128 V0 = MakeVectorE0E1(Z, Z); + Vector128 V1 = MakeVectorE0E1(A, A); + Vector128 V2 = MakeVectorE0E1(B, B); AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2); - AArch64.Vpart(1, 0, new Bits(A0)); - AArch64.Vpart(1, 1, new Bits(A1)); - AArch64.Vpart(2, 0, new Bits(B0)); - AArch64.Vpart(2, 1, new Bits(B1)); + 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)); SimdFp.Raddhn_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.V(64, 0).ToUInt64())); - Assert.That(GetVectorE1(ThreadState.V0), Is.Zero); + 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())); }); } [Test, Pairwise, Description("RADDHN{2} ., ., .")] - public void Raddhn_V_8H16B_4S8H_2D4S([ValueSource("_4H2S1D_")] [Random(1)] ulong A0, - [ValueSource("_4H2S1D_")] [Random(1)] ulong A1, - [ValueSource("_4H2S1D_")] [Random(1)] ulong B0, - [ValueSource("_4H2S1D_")] [Random(1)] ulong B1, + 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, + [ValueSource("_4H2S1D_")] [Random(RndCnt)] ulong A, + [ValueSource("_4H2S1D_")] [Random(RndCnt)] ulong B, [Values(0b00u, 0b01u, 0b10u)] uint size) // <8H16B, 4S8H, 2D4S> { - uint Opcode = 0x6E224020; // RADDHN2 V0.16B, V1.8H, V2.8H + uint Opcode = 0x6E204000; // RADDHN2 V0.16B, V0.8H, V0.8H + Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); Opcode |= ((size & 3) << 22); Bits Op = new Bits(Opcode); - ulong _E0 = TestContext.CurrentContext.Random.NextULong(); - Vector128 V0 = MakeVectorE0(_E0); - Vector128 V1 = MakeVectorE0E1(A0, A1); - Vector128 V2 = MakeVectorE0E1(B0, B1); + Vector128 V0 = MakeVectorE0E1(Z, Z); + Vector128 V1 = MakeVectorE0E1(A, A); + Vector128 V2 = MakeVectorE0E1(B, B); AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2); - AArch64.Vpart(1, 0, new Bits(A0)); - AArch64.Vpart(1, 1, new Bits(A1)); - AArch64.Vpart(2, 0, new Bits(B0)); - AArch64.Vpart(2, 1, new Bits(B1)); + 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)); SimdFp.Raddhn_V(Op[30], Op[23, 22], Op[20, 16], Op[9, 5], Op[4, 0]); Assert.Multiple(() => { - Assert.That(GetVectorE0(ThreadState.V0), Is.EqualTo(_E0)); + 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())); }); } [Test, Pairwise, Description("RSUBHN{2} ., ., .")] - public void Rsubhn_V_8H8B_4S4H_2D2S([ValueSource("_4H2S1D_")] [Random(1)] ulong A0, - [ValueSource("_4H2S1D_")] [Random(1)] ulong A1, - [ValueSource("_4H2S1D_")] [Random(1)] ulong B0, - [ValueSource("_4H2S1D_")] [Random(1)] ulong B1, + 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, + [ValueSource("_4H2S1D_")] [Random(RndCnt)] ulong A, + [ValueSource("_4H2S1D_")] [Random(RndCnt)] ulong B, [Values(0b00u, 0b01u, 0b10u)] uint size) // <8H8B, 4S4H, 2D2S> { - uint Opcode = 0x2E226020; // RSUBHN V0.8B, V1.8H, V2.8H + uint Opcode = 0x2E206000; // RSUBHN V0.8B, V0.8H, V0.8H + Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); Opcode |= ((size & 3) << 22); Bits Op = new Bits(Opcode); - Vector128 V0 = MakeVectorE1(TestContext.CurrentContext.Random.NextULong()); - Vector128 V1 = MakeVectorE0E1(A0, A1); - Vector128 V2 = MakeVectorE0E1(B0, B1); + Vector128 V0 = MakeVectorE0E1(Z, Z); + Vector128 V1 = MakeVectorE0E1(A, A); + Vector128 V2 = MakeVectorE0E1(B, B); AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2); - AArch64.Vpart(1, 0, new Bits(A0)); - AArch64.Vpart(1, 1, new Bits(A1)); - AArch64.Vpart(2, 0, new Bits(B0)); - AArch64.Vpart(2, 1, new Bits(B1)); + 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)); SimdFp.Rsubhn_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.V(64, 0).ToUInt64())); - Assert.That(GetVectorE1(ThreadState.V0), Is.Zero); - }); - } - - [Test, Pairwise, Description("RSUBHN{2} ., ., .")] - public void Rsubhn_V_8H16B_4S8H_2D4S([ValueSource("_4H2S1D_")] [Random(1)] ulong A0, - [ValueSource("_4H2S1D_")] [Random(1)] ulong A1, - [ValueSource("_4H2S1D_")] [Random(1)] ulong B0, - [ValueSource("_4H2S1D_")] [Random(1)] ulong B1, - [Values(0b00u, 0b01u, 0b10u)] uint size) // <8H16B, 4S8H, 2D4S> - { - uint Opcode = 0x6E226020; // RSUBHN2 V0.16B, V1.8H, V2.8H - Opcode |= ((size & 3) << 22); - Bits Op = new Bits(Opcode); - - ulong _E0 = TestContext.CurrentContext.Random.NextULong(); - Vector128 V0 = MakeVectorE0(_E0); - Vector128 V1 = MakeVectorE0E1(A0, A1); - Vector128 V2 = MakeVectorE0E1(B0, B1); - AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2); - - AArch64.Vpart(1, 0, new Bits(A0)); - AArch64.Vpart(1, 1, new Bits(A1)); - AArch64.Vpart(2, 0, new Bits(B0)); - AArch64.Vpart(2, 1, new Bits(B1)); - SimdFp.Rsubhn_V(Op[30], Op[23, 22], Op[20, 16], Op[9, 5], Op[4, 0]); - - Assert.Multiple(() => - { - Assert.That(GetVectorE0(ThreadState.V0), Is.EqualTo(_E0)); + 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())); }); } - [Test, Description("SABA ., ., .")] - public void Saba_V_8B_4H_2S([ValueSource("_8B4H2S_")] [Random(1)] ulong _Z, - [ValueSource("_8B4H2S_")] [Random(1)] ulong A, - [ValueSource("_8B4H2S_")] [Random(1)] ulong B, - [Values(0b00u, 0b01u, 0b10u)] uint size) // <8B, 4H, 2S> + [Test, Pairwise, Description("RSUBHN{2} ., ., .")] + 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, + [ValueSource("_4H2S1D_")] [Random(RndCnt)] ulong A, + [ValueSource("_4H2S1D_")] [Random(RndCnt)] ulong B, + [Values(0b00u, 0b01u, 0b10u)] uint size) // <8H16B, 4S8H, 2D4S> { - uint Opcode = 0x0E227C20; // SABA V0.8B, V1.8B, V2.8B + uint Opcode = 0x6E206000; // RSUBHN2 V0.16B, V0.8H, V0.8H + Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); Opcode |= ((size & 3) << 22); Bits Op = new Bits(Opcode); - Vector128 V0 = MakeVectorE0E1(_Z, TestContext.CurrentContext.Random.NextULong()); - Vector128 V1 = MakeVectorE0(A); - Vector128 V2 = MakeVectorE0(B); + Vector128 V0 = MakeVectorE0E1(Z, Z); + Vector128 V1 = MakeVectorE0E1(A, A); + Vector128 V2 = MakeVectorE0E1(B, B); AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2); - AArch64.Vpart(0, 0, new Bits(_Z)); - AArch64.V(1, new Bits(A)); - AArch64.V(2, new Bits(B)); - SimdFp.Saba_V(Op[30], Op[23, 22], Op[20, 16], Op[9, 5], Op[4, 0]); + 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)); + SimdFp.Rsubhn_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.V(64, 0).ToUInt64())); - Assert.That(GetVectorE1(ThreadState.V0), Is.Zero); + 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())); }); } [Test, Pairwise, Description("SABA ., ., .")] - public void Saba_V_16B_8H_4S([ValueSource("_8B4H2S_")] [Random(1)] ulong _Z0, - [ValueSource("_8B4H2S_")] [Random(1)] ulong _Z1, - [ValueSource("_8B4H2S_")] [Random(1)] ulong A0, - [ValueSource("_8B4H2S_")] [Random(1)] ulong A1, - [ValueSource("_8B4H2S_")] [Random(1)] ulong B0, - [ValueSource("_8B4H2S_")] [Random(1)] ulong B1, - [Values(0b00u, 0b01u, 0b10u)] uint size) // <16B, 8H, 4S> + 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, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong A, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong B, + [Values(0b00u, 0b01u, 0b10u)] uint size) // <8B, 4H, 2S> { - uint Opcode = 0x4E227C20; // SABA V0.16B, V1.16B, V2.16B + uint Opcode = 0x0E207C00; // SABA 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); - Vector128 V0 = MakeVectorE0E1(_Z0, _Z1); - Vector128 V1 = MakeVectorE0E1(A0, A1); - Vector128 V2 = MakeVectorE0E1(B0, B1); + Vector128 V0 = MakeVectorE0E1(Z, Z); + Vector128 V1 = MakeVectorE0(A); + Vector128 V2 = MakeVectorE0(B); AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2); - AArch64.Vpart(0, 0, new Bits(_Z0)); - AArch64.Vpart(0, 1, new Bits(_Z1)); - AArch64.Vpart(1, 0, new Bits(A0)); - AArch64.Vpart(1, 1, new Bits(A1)); - AArch64.Vpart(2, 0, new Bits(B0)); - AArch64.Vpart(2, 1, new Bits(B1)); + 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)); + SimdFp.Saba_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())); + }); + } + + [Test, Pairwise, Description("SABA ., ., .")] + 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, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong A, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong B, + [Values(0b00u, 0b01u, 0b10u)] uint size) // <16B, 8H, 4S> + { + uint Opcode = 0x4E207C00; // SABA 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); + + Vector128 V0 = MakeVectorE0E1(Z, Z); + Vector128 V1 = MakeVectorE0E1(A, A); + Vector128 V2 = MakeVectorE0E1(B, B); + AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2); + + 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)); SimdFp.Saba_V(Op[30], Op[23, 22], Op[20, 16], Op[9, 5], Op[4, 0]); Assert.Multiple(() => @@ -1296,25 +1474,27 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise, Description("SABAL{2} ., ., .")] - public void Sabal_V_8B8H_4H4S_2S2D([ValueSource("_8B4H2S_")] [Random(1)] ulong _Z0, - [ValueSource("_8B4H2S_")] [Random(1)] ulong _Z1, - [ValueSource("_8B4H2S_")] [Random(1)] ulong A0, - [ValueSource("_8B4H2S_")] [Random(1)] ulong B0, - [Values(0b00u, 0b01u, 0b10u)] uint size) // <8B, 4H, 2S> + 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, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong A, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong B, + [Values(0b00u, 0b01u, 0b10u)] uint size) // <8B8H, 4H4S, 2S2D> { - uint Opcode = 0x0E225020; // SABAL V0.8H, V1.8B, V2.8B + uint Opcode = 0x0E205000; // SABAL V0.8H, V0.8B, V0.8B + Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); Opcode |= ((size & 3) << 22); Bits Op = new Bits(Opcode); - Vector128 V0 = MakeVectorE0E1(_Z0, _Z1); - Vector128 V1 = MakeVectorE0(A0); - Vector128 V2 = MakeVectorE0(B0); + Vector128 V0 = MakeVectorE0E1(Z, Z); + Vector128 V1 = MakeVectorE0(A); + Vector128 V2 = MakeVectorE0(B); AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2); - AArch64.Vpart(0, 0, new Bits(_Z0)); - AArch64.Vpart(0, 1, new Bits(_Z1)); - AArch64.Vpart(1, 0, new Bits(A0)); - AArch64.Vpart(2, 0, new Bits(B0)); + AArch64.Vpart(0, 0, new Bits(Z)); AArch64.Vpart(0, 1, new Bits(Z)); + AArch64.Vpart(1, 0, new Bits(A)); + AArch64.Vpart(2, 0, new Bits(B)); SimdFp.Sabal_V(Op[30], Op[23, 22], Op[20, 16], Op[9, 5], Op[4, 0]); Assert.Multiple(() => @@ -1325,25 +1505,27 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise, Description("SABAL{2} ., ., .")] - public void Sabal_V_16B8H_8H4S_4S2D([ValueSource("_8B4H2S_")] [Random(1)] ulong _Z0, - [ValueSource("_8B4H2S_")] [Random(1)] ulong _Z1, - [ValueSource("_8B4H2S_")] [Random(1)] ulong A1, - [ValueSource("_8B4H2S_")] [Random(1)] ulong B1, - [Values(0b00u, 0b01u, 0b10u)] uint size) // <16B, 8H, 4S> + 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, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong A, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong B, + [Values(0b00u, 0b01u, 0b10u)] uint size) // <16B8H, 8H4S, 4S2D> { - uint Opcode = 0x4E225020; // SABAL2 V0.8H, V1.16B, V2.16B + uint Opcode = 0x4E205000; // SABAL2 V0.8H, V0.16B, V0.16B + Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); Opcode |= ((size & 3) << 22); Bits Op = new Bits(Opcode); - Vector128 V0 = MakeVectorE0E1(_Z0, _Z1); - Vector128 V1 = MakeVectorE1(A1); - Vector128 V2 = MakeVectorE1(B1); + Vector128 V0 = MakeVectorE0E1(Z, Z); + Vector128 V1 = MakeVectorE1(A); + Vector128 V2 = MakeVectorE1(B); AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2); - AArch64.Vpart(0, 0, new Bits(_Z0)); - AArch64.Vpart(0, 1, new Bits(_Z1)); - AArch64.Vpart(1, 1, new Bits(A1)); - AArch64.Vpart(2, 1, new Bits(B1)); + AArch64.Vpart(0, 0, new Bits(Z)); AArch64.Vpart(0, 1, new Bits(Z)); + AArch64.Vpart(1, 1, new Bits(A)); + AArch64.Vpart(2, 1, new Bits(B)); SimdFp.Sabal_V(Op[30], Op[23, 22], Op[20, 16], Op[9, 5], Op[4, 0]); Assert.Multiple(() => @@ -1353,53 +1535,59 @@ namespace Ryujinx.Tests.Cpu }); } - [Test, Description("SABD ., ., .")] - public void Sabd_V_8B_4H_2S([ValueSource("_8B4H2S_")] [Random(1)] ulong A, - [ValueSource("_8B4H2S_")] [Random(1)] ulong B, + [Test, Pairwise, Description("SABD ., ., .")] + 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, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong A, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong B, [Values(0b00u, 0b01u, 0b10u)] uint size) // <8B, 4H, 2S> { - uint Opcode = 0x0E227420; // SABD V0.8B, V1.8B, V2.8B + uint Opcode = 0x0E207400; // SABD 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); - Vector128 V0 = MakeVectorE0E1(TestContext.CurrentContext.Random.NextULong(), - TestContext.CurrentContext.Random.NextULong()); + Vector128 V0 = MakeVectorE0E1(Z, Z); Vector128 V1 = MakeVectorE0(A); Vector128 V2 = MakeVectorE0(B); AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2); + 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)); SimdFp.Sabd_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.V(64, 0).ToUInt64())); - Assert.That(GetVectorE1(ThreadState.V0), Is.Zero); + 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())); }); } [Test, Pairwise, Description("SABD ., ., .")] - public void Sabd_V_16B_8H_4S([ValueSource("_8B4H2S_")] [Random(1)] ulong A0, - [ValueSource("_8B4H2S_")] [Random(1)] ulong A1, - [ValueSource("_8B4H2S_")] [Random(1)] ulong B0, - [ValueSource("_8B4H2S_")] [Random(1)] ulong B1, + 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, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong A, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong B, [Values(0b00u, 0b01u, 0b10u)] uint size) // <16B, 8H, 4S> { - uint Opcode = 0x4E227420; // SABD V0.16B, V1.16B, V2.16B + uint Opcode = 0x4E207400; // SABD 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); - Vector128 V0 = MakeVectorE0E1(TestContext.CurrentContext.Random.NextULong(), - TestContext.CurrentContext.Random.NextULong()); - Vector128 V1 = MakeVectorE0E1(A0, A1); - Vector128 V2 = MakeVectorE0E1(B0, B1); + Vector128 V0 = MakeVectorE0E1(Z, Z); + Vector128 V1 = MakeVectorE0E1(A, A); + Vector128 V2 = MakeVectorE0E1(B, B); AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2); - AArch64.Vpart(1, 0, new Bits(A0)); - AArch64.Vpart(1, 1, new Bits(A1)); - AArch64.Vpart(2, 0, new Bits(B0)); - AArch64.Vpart(2, 1, new Bits(B1)); + 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)); SimdFp.Sabd_V(Op[30], Op[23, 22], Op[20, 16], Op[9, 5], Op[4, 0]); Assert.Multiple(() => @@ -1409,125 +1597,150 @@ namespace Ryujinx.Tests.Cpu }); } - [Test, Description("SABDL{2} ., ., .")] - public void Sabdl_V_8B8H_4H4S_2S2D([ValueSource("_8B4H2S_")] [Random(1)] ulong A0, - [ValueSource("_8B4H2S_")] [Random(1)] ulong B0, - [Values(0b00u, 0b01u, 0b10u)] uint size) // <8B, 4H, 2S> + [Test, Pairwise, Description("SABDL{2} ., ., .")] + 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, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong A, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong B, + [Values(0b00u, 0b01u, 0b10u)] uint size) // <8B8H, 4H4S, 2S2D> { - uint Opcode = 0x0E227020; // SABDL V0.8H, V1.8B, V2.8B + uint Opcode = 0x0E207000; // SABDL V0.8H, V0.8B, V0.8B + Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); Opcode |= ((size & 3) << 22); Bits Op = new Bits(Opcode); - Vector128 V0 = MakeVectorE0E1(TestContext.CurrentContext.Random.NextULong(), - TestContext.CurrentContext.Random.NextULong()); - Vector128 V1 = MakeVectorE0(A0); - Vector128 V2 = MakeVectorE0(B0); - AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2); - - AArch64.Vpart(1, 0, new Bits(A0)); - AArch64.Vpart(2, 0, new Bits(B0)); - SimdFp.Sabdl_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())); - }); - } - - [Test, Description("SABDL{2} ., ., .")] - public void Sabdl_V_16B8H_8H4S_4S2D([ValueSource("_8B4H2S_")] [Random(1)] ulong A1, - [ValueSource("_8B4H2S_")] [Random(1)] ulong B1, - [Values(0b00u, 0b01u, 0b10u)] uint size) // <16B, 8H, 4S> - { - uint Opcode = 0x4E227020; // SABDL2 V0.8H, V1.16B, V2.16B - Opcode |= ((size & 3) << 22); - Bits Op = new Bits(Opcode); - - Vector128 V0 = MakeVectorE0E1(TestContext.CurrentContext.Random.NextULong(), - TestContext.CurrentContext.Random.NextULong()); - Vector128 V1 = MakeVectorE1(A1); - Vector128 V2 = MakeVectorE1(B1); - AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2); - - AArch64.Vpart(1, 1, new Bits(A1)); - AArch64.Vpart(2, 1, new Bits(B1)); - SimdFp.Sabdl_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())); - }); - } - - [Test, Description("SUB , , ")] - public void Sub_S_D([ValueSource("_1D_")] [Random(1)] ulong A, - [ValueSource("_1D_")] [Random(1)] ulong B) - { - uint Opcode = 0x7EE28420; // SUB D0, D1, D2 - Bits Op = new Bits(Opcode); - - Vector128 V0 = MakeVectorE1(TestContext.CurrentContext.Random.NextULong()); + Vector128 V0 = MakeVectorE0E1(Z, Z); Vector128 V1 = MakeVectorE0(A); Vector128 V2 = MakeVectorE0(B); AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2); + AArch64.Vpart(0, 0, new Bits(Z)); AArch64.Vpart(0, 1, new Bits(Z)); + AArch64.Vpart(1, 0, new Bits(A)); + AArch64.Vpart(2, 0, new Bits(B)); + SimdFp.Sabdl_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())); + }); + } + + [Test, Pairwise, Description("SABDL{2} ., ., .")] + 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, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong A, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong B, + [Values(0b00u, 0b01u, 0b10u)] uint size) // <16B8H, 8H4S, 4S2D> + { + uint Opcode = 0x4E207000; // SABDL2 V0.8H, V0.16B, V0.16B + Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); + Opcode |= ((size & 3) << 22); + Bits Op = new Bits(Opcode); + + Vector128 V0 = MakeVectorE0E1(Z, Z); + Vector128 V1 = MakeVectorE1(A); + Vector128 V2 = MakeVectorE1(B); + AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2); + + AArch64.Vpart(0, 0, new Bits(Z)); AArch64.Vpart(0, 1, new Bits(Z)); + AArch64.Vpart(1, 1, new Bits(A)); + AArch64.Vpart(2, 1, new Bits(B)); + SimdFp.Sabdl_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())); + }); + } + + [Test, Pairwise, Description("SUB , , ")] + 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, + [ValueSource("_1D_")] [Random(RndCnt)] ulong A, + [ValueSource("_1D_")] [Random(RndCnt)] ulong B) + { + uint Opcode = 0x7EE08400; // SUB D0, D0, D0 + Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); + Bits Op = new Bits(Opcode); + + Vector128 V0 = MakeVectorE0E1(Z, Z); + Vector128 V1 = MakeVectorE0(A); + Vector128 V2 = MakeVectorE0(B); + AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2); + + 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)); SimdFp.Sub_S(Op[23, 22], Op[20, 16], Op[9, 5], Op[4, 0]); Assert.Multiple(() => { - Assert.That(GetVectorE0(ThreadState.V0), Is.EqualTo(AArch64.V(64, 0).ToUInt64())); - Assert.That(GetVectorE1(ThreadState.V0), Is.Zero); - }); - } - - [Test, Description("SUB ., ., .")] - public void Sub_V_8B_4H_2S([ValueSource("_8B4H2S_")] [Random(1)] ulong A, - [ValueSource("_8B4H2S_")] [Random(1)] ulong B, - [Values(0b00u, 0b01u, 0b10u)] uint size) // <8B, 4H, 2S> - { - uint Opcode = 0x2E228420; // SUB V0.8B, V1.8B, V2.8B - Opcode |= ((size & 3) << 22); - Bits Op = new Bits(Opcode); - - Vector128 V0 = MakeVectorE1(TestContext.CurrentContext.Random.NextULong()); - Vector128 V1 = MakeVectorE0(A); - Vector128 V2 = MakeVectorE0(B); - AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2); - - AArch64.V(1, new Bits(A)); - AArch64.V(2, new Bits(B)); - SimdFp.Sub_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.V(64, 0).ToUInt64())); - Assert.That(GetVectorE1(ThreadState.V0), Is.Zero); + 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())); }); } [Test, Pairwise, Description("SUB ., ., .")] - public void Sub_V_16B_8H_4S_2D([ValueSource("_8B4H2S1D_")] [Random(1)] ulong A0, - [ValueSource("_8B4H2S1D_")] [Random(1)] ulong A1, - [ValueSource("_8B4H2S1D_")] [Random(1)] ulong B0, - [ValueSource("_8B4H2S1D_")] [Random(1)] ulong B1, - [Values(0b00u, 0b01u, 0b10u, 0b11u)] uint size) // <16B, 8H, 4S, 2D> + 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, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong A, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong B, + [Values(0b00u, 0b01u, 0b10u)] uint size) // <8B, 4H, 2S> { - uint Opcode = 0x6E228420; // SUB V0.16B, V1.16B, V2.16B + uint Opcode = 0x2E208400; // SUB 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); - Vector128 V1 = MakeVectorE0E1(A0, A1); - Vector128 V2 = MakeVectorE0E1(B0, B1); - AThreadState ThreadState = SingleOpcode(Opcode, V1: V1, V2: V2); + Vector128 V0 = MakeVectorE0E1(Z, Z); + Vector128 V1 = MakeVectorE0(A); + Vector128 V2 = MakeVectorE0(B); + AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2); - AArch64.Vpart(1, 0, new Bits(A0)); - AArch64.Vpart(1, 1, new Bits(A1)); - AArch64.Vpart(2, 0, new Bits(B0)); - AArch64.Vpart(2, 1, new Bits(B1)); + 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)); + SimdFp.Sub_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())); + }); + } + + [Test, Pairwise, Description("SUB ., ., .")] + 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, + [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 = 0x6E208400; // SUB 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); + + Vector128 V0 = MakeVectorE0E1(Z, Z); + Vector128 V1 = MakeVectorE0E1(A, A); + Vector128 V2 = MakeVectorE0E1(B, B); + AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2); + + 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)); SimdFp.Sub_V(Op[30], Op[23, 22], Op[20, 16], Op[9, 5], Op[4, 0]); Assert.Multiple(() => @@ -1538,115 +1751,244 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise, Description("SUBHN{2} ., ., .")] - public void Subhn_V_8H8B_4S4H_2D2S([ValueSource("_4H2S1D_")] [Random(1)] ulong A0, - [ValueSource("_4H2S1D_")] [Random(1)] ulong A1, - [ValueSource("_4H2S1D_")] [Random(1)] ulong B0, - [ValueSource("_4H2S1D_")] [Random(1)] ulong B1, + 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, + [ValueSource("_4H2S1D_")] [Random(RndCnt)] ulong A, + [ValueSource("_4H2S1D_")] [Random(RndCnt)] ulong B, [Values(0b00u, 0b01u, 0b10u)] uint size) // <8H8B, 4S4H, 2D2S> { - uint Opcode = 0x0E226020; // SUBHN V0.8B, V1.8H, V2.8H + uint Opcode = 0x0E206000; // SUBHN V0.8B, V0.8H, V0.8H + Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); Opcode |= ((size & 3) << 22); Bits Op = new Bits(Opcode); - Vector128 V0 = MakeVectorE1(TestContext.CurrentContext.Random.NextULong()); - Vector128 V1 = MakeVectorE0E1(A0, A1); - Vector128 V2 = MakeVectorE0E1(B0, B1); + Vector128 V0 = MakeVectorE0E1(Z, Z); + Vector128 V1 = MakeVectorE0E1(A, A); + Vector128 V2 = MakeVectorE0E1(B, B); AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2); - AArch64.Vpart(1, 0, new Bits(A0)); - AArch64.Vpart(1, 1, new Bits(A1)); - AArch64.Vpart(2, 0, new Bits(B0)); - AArch64.Vpart(2, 1, new Bits(B1)); + 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)); SimdFp.Subhn_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.V(64, 0).ToUInt64())); - Assert.That(GetVectorE1(ThreadState.V0), Is.Zero); + 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())); }); } [Test, Pairwise, Description("SUBHN{2} ., ., .")] - public void Subhn_V_8H16B_4S8H_2D4S([ValueSource("_4H2S1D_")] [Random(1)] ulong A0, - [ValueSource("_4H2S1D_")] [Random(1)] ulong A1, - [ValueSource("_4H2S1D_")] [Random(1)] ulong B0, - [ValueSource("_4H2S1D_")] [Random(1)] ulong B1, + 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, + [ValueSource("_4H2S1D_")] [Random(RndCnt)] ulong A, + [ValueSource("_4H2S1D_")] [Random(RndCnt)] ulong B, [Values(0b00u, 0b01u, 0b10u)] uint size) // <8H16B, 4S8H, 2D4S> { - uint Opcode = 0x4E226020; // SUBHN2 V0.16B, V1.8H, V2.8H + uint Opcode = 0x4E206000; // SUBHN2 V0.16B, V0.8H, V0.8H + Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); Opcode |= ((size & 3) << 22); Bits Op = new Bits(Opcode); - ulong _E0 = TestContext.CurrentContext.Random.NextULong(); - Vector128 V0 = MakeVectorE0(_E0); - Vector128 V1 = MakeVectorE0E1(A0, A1); - Vector128 V2 = MakeVectorE0E1(B0, B1); + Vector128 V0 = MakeVectorE0E1(Z, Z); + Vector128 V1 = MakeVectorE0E1(A, A); + Vector128 V2 = MakeVectorE0E1(B, B); AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2); - AArch64.Vpart(1, 0, new Bits(A0)); - AArch64.Vpart(1, 1, new Bits(A1)); - AArch64.Vpart(2, 0, new Bits(B0)); - AArch64.Vpart(2, 1, new Bits(B1)); + 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)); SimdFp.Subhn_V(Op[30], Op[23, 22], Op[20, 16], Op[9, 5], Op[4, 0]); Assert.Multiple(() => { - Assert.That(GetVectorE0(ThreadState.V0), Is.EqualTo(_E0)); + 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())); }); } - [Test, Description("UABA ., ., .")] - public void Uaba_V_8B_4H_2S([ValueSource("_8B4H2S_")] [Random(1)] ulong _Z, - [ValueSource("_8B4H2S_")] [Random(1)] ulong A, - [ValueSource("_8B4H2S_")] [Random(1)] ulong B, + [Test, Pairwise, Description("TRN1 ., ., .")] + 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, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong A, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong B, [Values(0b00u, 0b01u, 0b10u)] uint size) // <8B, 4H, 2S> { - uint Opcode = 0x2E227C20; // UABA V0.8B, V1.8B, V2.8B + uint Opcode = 0x0E002800; // TRN1 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); - Vector128 V0 = MakeVectorE0E1(_Z, TestContext.CurrentContext.Random.NextULong()); + Vector128 V0 = MakeVectorE0E1(Z, Z); Vector128 V1 = MakeVectorE0(A); Vector128 V2 = MakeVectorE0(B); AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2); - AArch64.Vpart(0, 0, new Bits(_Z)); + 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)); - SimdFp.Uaba_V(Op[30], Op[23, 22], Op[20, 16], Op[9, 5], Op[4, 0]); + SimdFp.Trn1_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.V(64, 0).ToUInt64())); - Assert.That(GetVectorE1(ThreadState.V0), Is.Zero); + 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())); + }); + } + + [Test, Pairwise, Description("TRN1 ., ., .")] + 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, + [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 = 0x4E002800; // TRN1 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); + + Vector128 V0 = MakeVectorE0E1(Z, Z); + Vector128 V1 = MakeVectorE0E1(A, A); + Vector128 V2 = MakeVectorE0E1(B, B); + AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2); + + 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)); + SimdFp.Trn1_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())); + }); + } + + [Test, Pairwise, Description("TRN2 ., ., .")] + 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, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong A, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong B, + [Values(0b00u, 0b01u, 0b10u)] uint size) // <8B, 4H, 2S> + { + uint Opcode = 0x0E006800; // TRN2 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); + + Vector128 V0 = MakeVectorE0E1(Z, Z); + Vector128 V1 = MakeVectorE0(A); + Vector128 V2 = MakeVectorE0(B); + AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2); + + 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)); + SimdFp.Trn2_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())); + }); + } + + [Test, Pairwise, Description("TRN2 ., ., .")] + 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, + [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 = 0x4E006800; // TRN2 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); + + Vector128 V0 = MakeVectorE0E1(Z, Z); + Vector128 V1 = MakeVectorE0E1(A, A); + Vector128 V2 = MakeVectorE0E1(B, B); + AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2); + + 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)); + SimdFp.Trn2_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())); }); } [Test, Pairwise, Description("UABA ., ., .")] - public void Uaba_V_16B_8H_4S([ValueSource("_8B4H2S_")] [Random(1)] ulong _Z0, - [ValueSource("_8B4H2S_")] [Random(1)] ulong _Z1, - [ValueSource("_8B4H2S_")] [Random(1)] ulong A0, - [ValueSource("_8B4H2S_")] [Random(1)] ulong A1, - [ValueSource("_8B4H2S_")] [Random(1)] ulong B0, - [ValueSource("_8B4H2S_")] [Random(1)] ulong B1, - [Values(0b00u, 0b01u, 0b10u)] uint size) // <16B, 8H, 4S> + 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, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong A, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong B, + [Values(0b00u, 0b01u, 0b10u)] uint size) // <8B, 4H, 2S> { - uint Opcode = 0x6E227C20; // UABA V0.16B, V1.16B, V2.16B + uint Opcode = 0x2E207C00; // UABA 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); - Vector128 V0 = MakeVectorE0E1(_Z0, _Z1); - Vector128 V1 = MakeVectorE0E1(A0, A1); - Vector128 V2 = MakeVectorE0E1(B0, B1); + Vector128 V0 = MakeVectorE0E1(Z, Z); + Vector128 V1 = MakeVectorE0(A); + Vector128 V2 = MakeVectorE0(B); AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2); - AArch64.Vpart(0, 0, new Bits(_Z0)); - AArch64.Vpart(0, 1, new Bits(_Z1)); - AArch64.Vpart(1, 0, new Bits(A0)); - AArch64.Vpart(1, 1, new Bits(A1)); - AArch64.Vpart(2, 0, new Bits(B0)); - AArch64.Vpart(2, 1, new Bits(B1)); + 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)); + SimdFp.Uaba_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())); + }); + } + + [Test, Pairwise, Description("UABA ., ., .")] + 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, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong A, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong B, + [Values(0b00u, 0b01u, 0b10u)] uint size) // <16B, 8H, 4S> + { + uint Opcode = 0x6E207C00; // UABA 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); + + Vector128 V0 = MakeVectorE0E1(Z, Z); + Vector128 V1 = MakeVectorE0E1(A, A); + Vector128 V2 = MakeVectorE0E1(B, B); + AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2); + + 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)); SimdFp.Uaba_V(Op[30], Op[23, 22], Op[20, 16], Op[9, 5], Op[4, 0]); Assert.Multiple(() => @@ -1657,25 +1999,27 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise, Description("UABAL{2} ., ., .")] - public void Uabal_V_8B8H_4H4S_2S2D([ValueSource("_8B4H2S_")] [Random(1)] ulong _Z0, - [ValueSource("_8B4H2S_")] [Random(1)] ulong _Z1, - [ValueSource("_8B4H2S_")] [Random(1)] ulong A0, - [ValueSource("_8B4H2S_")] [Random(1)] ulong B0, - [Values(0b00u, 0b01u, 0b10u)] uint size) // <8B, 4H, 2S> + 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, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong A, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong B, + [Values(0b00u, 0b01u, 0b10u)] uint size) // <8B8H, 4H4S, 2S2D> { - uint Opcode = 0x2E225020; // UABAL V0.8H, V1.8B, V2.8B + uint Opcode = 0x2E205000; // UABAL V0.8H, V0.8B, V0.8B + Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); Opcode |= ((size & 3) << 22); Bits Op = new Bits(Opcode); - Vector128 V0 = MakeVectorE0E1(_Z0, _Z1); - Vector128 V1 = MakeVectorE0(A0); - Vector128 V2 = MakeVectorE0(B0); + Vector128 V0 = MakeVectorE0E1(Z, Z); + Vector128 V1 = MakeVectorE0(A); + Vector128 V2 = MakeVectorE0(B); AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2); - AArch64.Vpart(0, 0, new Bits(_Z0)); - AArch64.Vpart(0, 1, new Bits(_Z1)); - AArch64.Vpart(1, 0, new Bits(A0)); - AArch64.Vpart(2, 0, new Bits(B0)); + AArch64.Vpart(0, 0, new Bits(Z)); AArch64.Vpart(0, 1, new Bits(Z)); + AArch64.Vpart(1, 0, new Bits(A)); + AArch64.Vpart(2, 0, new Bits(B)); SimdFp.Uabal_V(Op[30], Op[23, 22], Op[20, 16], Op[9, 5], Op[4, 0]); Assert.Multiple(() => @@ -1686,25 +2030,27 @@ namespace Ryujinx.Tests.Cpu } [Test, Pairwise, Description("UABAL{2} ., ., .")] - public void Uabal_V_16B8H_8H4S_4S2D([ValueSource("_8B4H2S_")] [Random(1)] ulong _Z0, - [ValueSource("_8B4H2S_")] [Random(1)] ulong _Z1, - [ValueSource("_8B4H2S_")] [Random(1)] ulong A1, - [ValueSource("_8B4H2S_")] [Random(1)] ulong B1, - [Values(0b00u, 0b01u, 0b10u)] uint size) // <16B, 8H, 4S> + 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, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong A, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong B, + [Values(0b00u, 0b01u, 0b10u)] uint size) // <16B8H, 8H4S, 4S2D> { - uint Opcode = 0x6E225020; // UABAL2 V0.8H, V1.16B, V2.16B + uint Opcode = 0x6E205000; // UABAL2 V0.8H, V0.16B, V0.16B + Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); Opcode |= ((size & 3) << 22); Bits Op = new Bits(Opcode); - Vector128 V0 = MakeVectorE0E1(_Z0, _Z1); - Vector128 V1 = MakeVectorE1(A1); - Vector128 V2 = MakeVectorE1(B1); + Vector128 V0 = MakeVectorE0E1(Z, Z); + Vector128 V1 = MakeVectorE1(A); + Vector128 V2 = MakeVectorE1(B); AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2); - AArch64.Vpart(0, 0, new Bits(_Z0)); - AArch64.Vpart(0, 1, new Bits(_Z1)); - AArch64.Vpart(1, 1, new Bits(A1)); - AArch64.Vpart(2, 1, new Bits(B1)); + AArch64.Vpart(0, 0, new Bits(Z)); AArch64.Vpart(0, 1, new Bits(Z)); + AArch64.Vpart(1, 1, new Bits(A)); + AArch64.Vpart(2, 1, new Bits(B)); SimdFp.Uabal_V(Op[30], Op[23, 22], Op[20, 16], Op[9, 5], Op[4, 0]); Assert.Multiple(() => @@ -1714,53 +2060,59 @@ namespace Ryujinx.Tests.Cpu }); } - [Test, Description("UABD ., ., .")] - public void Uabd_V_8B_4H_2S([ValueSource("_8B4H2S_")] [Random(1)] ulong A, - [ValueSource("_8B4H2S_")] [Random(1)] ulong B, + [Test, Pairwise, Description("UABD ., ., .")] + 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, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong A, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong B, [Values(0b00u, 0b01u, 0b10u)] uint size) // <8B, 4H, 2S> { - uint Opcode = 0x2E227420; // UABD V0.8B, V1.8B, V2.8B + uint Opcode = 0x2E207400; // UABD 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); - Vector128 V0 = MakeVectorE0E1(TestContext.CurrentContext.Random.NextULong(), - TestContext.CurrentContext.Random.NextULong()); + Vector128 V0 = MakeVectorE0E1(Z, Z); Vector128 V1 = MakeVectorE0(A); Vector128 V2 = MakeVectorE0(B); AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2); + 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)); SimdFp.Uabd_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.V(64, 0).ToUInt64())); - Assert.That(GetVectorE1(ThreadState.V0), Is.Zero); + 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())); }); } [Test, Pairwise, Description("UABD ., ., .")] - public void Uabd_V_16B_8H_4S([ValueSource("_8B4H2S_")] [Random(1)] ulong A0, - [ValueSource("_8B4H2S_")] [Random(1)] ulong A1, - [ValueSource("_8B4H2S_")] [Random(1)] ulong B0, - [ValueSource("_8B4H2S_")] [Random(1)] ulong B1, + 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, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong A, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong B, [Values(0b00u, 0b01u, 0b10u)] uint size) // <16B, 8H, 4S> { - uint Opcode = 0x6E227420; // UABD V0.16B, V1.16B, V2.16B + uint Opcode = 0x6E207400; // UABD 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); - Vector128 V0 = MakeVectorE0E1(TestContext.CurrentContext.Random.NextULong(), - TestContext.CurrentContext.Random.NextULong()); - Vector128 V1 = MakeVectorE0E1(A0, A1); - Vector128 V2 = MakeVectorE0E1(B0, B1); + Vector128 V0 = MakeVectorE0E1(Z, Z); + Vector128 V1 = MakeVectorE0E1(A, A); + Vector128 V2 = MakeVectorE0E1(B, B); AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2); - AArch64.Vpart(1, 0, new Bits(A0)); - AArch64.Vpart(1, 1, new Bits(A1)); - AArch64.Vpart(2, 0, new Bits(B0)); - AArch64.Vpart(2, 1, new Bits(B1)); + 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)); SimdFp.Uabd_V(Op[30], Op[23, 22], Op[20, 16], Op[9, 5], Op[4, 0]); Assert.Multiple(() => @@ -1770,23 +2122,28 @@ namespace Ryujinx.Tests.Cpu }); } - [Test, Description("UABDL{2} ., ., .")] - public void Uabdl_V_8B8H_4H4S_2S2D([ValueSource("_8B4H2S_")] [Random(1)] ulong A0, - [ValueSource("_8B4H2S_")] [Random(1)] ulong B0, - [Values(0b00u, 0b01u, 0b10u)] uint size) // <8B, 4H, 2S> + [Test, Pairwise, Description("UABDL{2} ., ., .")] + 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, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong A, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong B, + [Values(0b00u, 0b01u, 0b10u)] uint size) // <8B8H, 4H4S, 2S2D> { - uint Opcode = 0x2E227020; // UABDL V0.8H, V1.8B, V2.8B + uint Opcode = 0x2E207000; // UABDL V0.8H, V0.8B, V0.8B + Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); Opcode |= ((size & 3) << 22); Bits Op = new Bits(Opcode); - Vector128 V0 = MakeVectorE0E1(TestContext.CurrentContext.Random.NextULong(), - TestContext.CurrentContext.Random.NextULong()); - Vector128 V1 = MakeVectorE0(A0); - Vector128 V2 = MakeVectorE0(B0); + Vector128 V0 = MakeVectorE0E1(Z, Z); + Vector128 V1 = MakeVectorE0(A); + Vector128 V2 = MakeVectorE0(B); AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2); - AArch64.Vpart(1, 0, new Bits(A0)); - AArch64.Vpart(2, 0, new Bits(B0)); + AArch64.Vpart(0, 0, new Bits(Z)); AArch64.Vpart(0, 1, new Bits(Z)); + AArch64.Vpart(1, 0, new Bits(A)); + AArch64.Vpart(2, 0, new Bits(B)); SimdFp.Uabdl_V(Op[30], Op[23, 22], Op[20, 16], Op[9, 5], Op[4, 0]); Assert.Multiple(() => @@ -1796,23 +2153,28 @@ namespace Ryujinx.Tests.Cpu }); } - [Test, Description("UABDL{2} ., ., .")] - public void Uabdl_V_16B8H_8H4S_4S2D([ValueSource("_8B4H2S_")] [Random(1)] ulong A1, - [ValueSource("_8B4H2S_")] [Random(1)] ulong B1, - [Values(0b00u, 0b01u, 0b10u)] uint size) // <16B, 8H, 4S> + [Test, Pairwise, Description("UABDL{2} ., ., .")] + 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, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong A, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong B, + [Values(0b00u, 0b01u, 0b10u)] uint size) // <16B8H, 8H4S, 4S2D> { - uint Opcode = 0x6E227020; // UABDL2 V0.8H, V1.16B, V2.16B + uint Opcode = 0x6E207000; // UABDL2 V0.8H, V0.16B, V0.16B + Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); Opcode |= ((size & 3) << 22); Bits Op = new Bits(Opcode); - Vector128 V0 = MakeVectorE0E1(TestContext.CurrentContext.Random.NextULong(), - TestContext.CurrentContext.Random.NextULong()); - Vector128 V1 = MakeVectorE1(A1); - Vector128 V2 = MakeVectorE1(B1); + Vector128 V0 = MakeVectorE0E1(Z, Z); + Vector128 V1 = MakeVectorE1(A); + Vector128 V2 = MakeVectorE1(B); AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2); - AArch64.Vpart(1, 1, new Bits(A1)); - AArch64.Vpart(2, 1, new Bits(B1)); + AArch64.Vpart(0, 0, new Bits(Z)); AArch64.Vpart(0, 1, new Bits(Z)); + AArch64.Vpart(1, 1, new Bits(A)); + AArch64.Vpart(2, 1, new Bits(B)); SimdFp.Uabdl_V(Op[30], Op[23, 22], Op[20, 16], Op[9, 5], Op[4, 0]); Assert.Multiple(() => @@ -1821,6 +2183,254 @@ namespace Ryujinx.Tests.Cpu Assert.That(GetVectorE1(ThreadState.V0), Is.EqualTo(AArch64.Vpart(64, 0, 1).ToUInt64())); }); } + + [Test, Pairwise, Description("UZP1 ., ., .")] + 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, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong A, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong B, + [Values(0b00u, 0b01u, 0b10u)] uint size) // <8B, 4H, 2S> + { + uint Opcode = 0x0E001800; // UZP1 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); + + Vector128 V0 = MakeVectorE0E1(Z, Z); + Vector128 V1 = MakeVectorE0(A); + Vector128 V2 = MakeVectorE0(B); + AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2); + + 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)); + SimdFp.Uzp1_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())); + }); + } + + [Test, Pairwise, Description("UZP1 ., ., .")] + 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, + [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 = 0x4E001800; // UZP1 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); + + Vector128 V0 = MakeVectorE0E1(Z, Z); + Vector128 V1 = MakeVectorE0E1(A, A); + Vector128 V2 = MakeVectorE0E1(B, B); + AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2); + + 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)); + SimdFp.Uzp1_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())); + }); + } + + [Test, Pairwise, Description("UZP2 ., ., .")] + 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, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong A, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong B, + [Values(0b00u, 0b01u, 0b10u)] uint size) // <8B, 4H, 2S> + { + uint Opcode = 0x0E005800; // UZP2 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); + + Vector128 V0 = MakeVectorE0E1(Z, Z); + Vector128 V1 = MakeVectorE0(A); + Vector128 V2 = MakeVectorE0(B); + AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2); + + 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)); + SimdFp.Uzp2_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())); + }); + } + + [Test, Pairwise, Description("UZP2 ., ., .")] + 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, + [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 = 0x4E005800; // UZP2 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); + + Vector128 V0 = MakeVectorE0E1(Z, Z); + Vector128 V1 = MakeVectorE0E1(A, A); + Vector128 V2 = MakeVectorE0E1(B, B); + AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2); + + 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)); + SimdFp.Uzp2_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())); + }); + } + + [Test, Pairwise, Description("ZIP1 ., ., .")] + 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, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong A, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong B, + [Values(0b00u, 0b01u, 0b10u)] uint size) // <8B, 4H, 2S> + { + uint Opcode = 0x0E003800; // ZIP1 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); + + Vector128 V0 = MakeVectorE0E1(Z, Z); + Vector128 V1 = MakeVectorE0(A); + Vector128 V2 = MakeVectorE0(B); + AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2); + + 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)); + SimdFp.Zip1_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())); + }); + } + + [Test, Pairwise, Description("ZIP1 ., ., .")] + 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, + [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 = 0x4E003800; // ZIP1 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); + + Vector128 V0 = MakeVectorE0E1(Z, Z); + Vector128 V1 = MakeVectorE0E1(A, A); + Vector128 V2 = MakeVectorE0E1(B, B); + AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2); + + 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)); + SimdFp.Zip1_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())); + }); + } + + [Test, Pairwise, Description("ZIP2 ., ., .")] + 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, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong A, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong B, + [Values(0b00u, 0b01u, 0b10u)] uint size) // <8B, 4H, 2S> + { + uint Opcode = 0x0E007800; // ZIP2 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); + + Vector128 V0 = MakeVectorE0E1(Z, Z); + Vector128 V1 = MakeVectorE0(A); + Vector128 V2 = MakeVectorE0(B); + AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2); + + 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)); + SimdFp.Zip2_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())); + }); + } + + [Test, Pairwise, Description("ZIP2 ., ., .")] + 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, + [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 = 0x4E007800; // ZIP2 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); + + Vector128 V0 = MakeVectorE0E1(Z, Z); + Vector128 V1 = MakeVectorE0E1(A, A); + Vector128 V2 = MakeVectorE0E1(B, B); + AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2); + + 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)); + SimdFp.Zip2_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())); + }); + } #endif } } diff --git a/Ryujinx.Tests/Cpu/Tester/Instructions.cs b/Ryujinx.Tests/Cpu/Tester/Instructions.cs index 1590019a70..68f83423ba 100644 --- a/Ryujinx.Tests/Cpu/Tester/Instructions.cs +++ b/Ryujinx.Tests/Cpu/Tester/Instructions.cs @@ -4655,6 +4655,74 @@ namespace Ryujinx.Tests.Cpu.Tester Vpart(d, part, result); } + // trn1_advsimd.html + public static void Trn1_V(bool Q, Bits size, Bits Rm, Bits Rn, Bits Rd) + { + const bool op = false; + + /* Decode */ + 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; + int part = (int)UInt(op); + int pairs = elements / 2; + + /* Operation */ + /* CheckFPAdvSIMDEnabled64(); */ + + Bits result = new Bits(datasize); + Bits operand1 = V(datasize, n); + Bits operand2 = V(datasize, m); + + for (int p = 0; p <= pairs - 1; p++) + { + Elem(result, 2 * p + 0, esize, Elem(operand1, 2 * p + part, esize)); + Elem(result, 2 * p + 1, esize, Elem(operand2, 2 * p + part, esize)); + } + + V(d, result); + } + + // trn2_advsimd.html + public static void Trn2_V(bool Q, Bits size, Bits Rm, Bits Rn, Bits Rd) + { + const bool op = true; + + /* Decode */ + 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; + int part = (int)UInt(op); + int pairs = elements / 2; + + /* Operation */ + /* CheckFPAdvSIMDEnabled64(); */ + + Bits result = new Bits(datasize); + Bits operand1 = V(datasize, n); + Bits operand2 = V(datasize, m); + + for (int p = 0; p <= pairs - 1; p++) + { + Elem(result, 2 * p + 0, esize, Elem(operand1, 2 * p + part, esize)); + Elem(result, 2 * p + 1, esize, Elem(operand2, 2 * p + part, esize)); + } + + V(d, result); + } + // uaba_advsimd.html public static void Uaba_V(bool Q, Bits size, Bits Rm, Bits Rn, Bits Rd) { @@ -4832,6 +4900,146 @@ namespace Ryujinx.Tests.Cpu.Tester V(d, result); } + + // uzp1_advsimd.html + public static void Uzp1_V(bool Q, Bits size, Bits Rm, Bits Rn, Bits Rd) + { + const bool op = false; + + /* Decode */ + 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; + int part = (int)UInt(op); + + /* Operation */ + /* CheckFPAdvSIMDEnabled64(); */ + + Bits result = new Bits(datasize); + Bits operandl = V(datasize, n); + Bits operandh = V(datasize, m); + + Bits zipped = Bits.Concat(operandh, operandl); + + for (int e = 0; e <= elements - 1; e++) + { + Elem(result, e, esize, Elem(zipped, 2 * e + part, esize)); + } + + V(d, result); + } + + // uzp2_advsimd.html + public static void Uzp2_V(bool Q, Bits size, Bits Rm, Bits Rn, Bits Rd) + { + const bool op = true; + + /* Decode */ + 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; + int part = (int)UInt(op); + + /* Operation */ + /* CheckFPAdvSIMDEnabled64(); */ + + Bits result = new Bits(datasize); + Bits operandl = V(datasize, n); + Bits operandh = V(datasize, m); + + Bits zipped = Bits.Concat(operandh, operandl); + + for (int e = 0; e <= elements - 1; e++) + { + Elem(result, e, esize, Elem(zipped, 2 * e + part, esize)); + } + + V(d, result); + } + + // zip1_advsimd.html + public static void Zip1_V(bool Q, Bits size, Bits Rm, Bits Rn, Bits Rd) + { + const bool op = false; + + /* Decode */ + 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; + int part = (int)UInt(op); + int pairs = elements / 2; + + /* Operation */ + /* CheckFPAdvSIMDEnabled64(); */ + + Bits result = new Bits(datasize); + Bits operand1 = V(datasize, n); + Bits operand2 = V(datasize, m); + + int @base = part * pairs; + + for (int p = 0; p <= pairs - 1; p++) + { + Elem(result, 2 * p + 0, esize, Elem(operand1, @base + p, esize)); + Elem(result, 2 * p + 1, esize, Elem(operand2, @base + p, esize)); + } + + V(d, result); + } + + // zip2_advsimd.html + public static void Zip2_V(bool Q, Bits size, Bits Rm, Bits Rn, Bits Rd) + { + const bool op = true; + + /* Decode */ + 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; + int part = (int)UInt(op); + int pairs = elements / 2; + + /* Operation */ + /* CheckFPAdvSIMDEnabled64(); */ + + Bits result = new Bits(datasize); + Bits operand1 = V(datasize, n); + Bits operand2 = V(datasize, m); + + int @base = part * pairs; + + for (int p = 0; p <= pairs - 1; p++) + { + Elem(result, 2 * p + 0, esize, Elem(operand1, @base + p, esize)); + Elem(result, 2 * p + 1, esize, Elem(operand2, @base + p, esize)); + } + + V(d, result); + } #endregion } } From 3e13b40b353a61fe57d1bc1440e1db9bc133df08 Mon Sep 17 00:00:00 2001 From: ReinUsesLisp Date: Sun, 15 Jul 2018 19:37:27 -0300 Subject: [PATCH 07/33] Add config key to dump shaders in local directory (#265) * Add config key to dump shaders in local directory * Address feedback --- Ryujinx.Graphics/Gal/OpenGL/OGLShader.cs | 5 ++ Ryujinx.Graphics/Gal/ShaderDumper.cs | 96 ++++++++++++++++++++++++ Ryujinx.Graphics/GraphicsConfig.cs | 4 + Ryujinx/Config.cs | 2 + Ryujinx/Ryujinx.conf | 3 + 5 files changed, 110 insertions(+) create mode 100644 Ryujinx.Graphics/Gal/ShaderDumper.cs create mode 100644 Ryujinx.Graphics/GraphicsConfig.cs diff --git a/Ryujinx.Graphics/Gal/OpenGL/OGLShader.cs b/Ryujinx.Graphics/Gal/OpenGL/OGLShader.cs index c55a758b4a..ad71775502 100644 --- a/Ryujinx.Graphics/Gal/OpenGL/OGLShader.cs +++ b/Ryujinx.Graphics/Gal/OpenGL/OGLShader.cs @@ -118,6 +118,9 @@ namespace Ryujinx.Graphics.Gal.OpenGL if (IsDualVp) { + ShaderDumper.Dump(Memory, Position + 0x50, Type, "a"); + ShaderDumper.Dump(Memory, PositionB + 0x50, Type, "b"); + Program = Decompiler.Decompile( Memory, Position + 0x50, @@ -126,6 +129,8 @@ namespace Ryujinx.Graphics.Gal.OpenGL } else { + ShaderDumper.Dump(Memory, Position + 0x50, Type); + Program = Decompiler.Decompile(Memory, Position + 0x50, Type); } diff --git a/Ryujinx.Graphics/Gal/ShaderDumper.cs b/Ryujinx.Graphics/Gal/ShaderDumper.cs new file mode 100644 index 0000000000..7cd56b21ef --- /dev/null +++ b/Ryujinx.Graphics/Gal/ShaderDumper.cs @@ -0,0 +1,96 @@ +using System; +using System.IO; + +namespace Ryujinx.Graphics.Gal +{ + static class ShaderDumper + { + private static string RuntimeDir; + + private static int DumpIndex = 1; + + public static void Dump(IGalMemory Memory, long Position, GalShaderType Type, string ExtSuffix = "") + { + if (string.IsNullOrWhiteSpace(GraphicsConfig.ShadersDumpPath)) + { + return; + } + + string FileName = "Shader" + DumpIndex.ToString("d4") + "." + ShaderExtension(Type) + ExtSuffix + ".bin"; + + string FilePath = Path.Combine(DumpDir(), FileName); + + DumpIndex++; + + using (FileStream Output = File.Create(FilePath)) + using (BinaryWriter Writer = new BinaryWriter(Output)) + { + long Offset = 0; + + ulong Instruction = 0; + + //Dump until a NOP instruction is found + while ((Instruction >> 52 & 0xfff8) != 0x50b0) + { + uint Word0 = (uint)Memory.ReadInt32(Position + Offset + 0); + uint Word1 = (uint)Memory.ReadInt32(Position + Offset + 4); + + Instruction = Word0 | (ulong)Word1 << 32; + + //Zero instructions (other kind of NOP) stop immediatly, + //this is to avoid two rows of zeroes + if (Instruction == 0) + { + break; + } + + Writer.Write(Instruction); + + Offset += 8; + } + + //Align to meet nvdisasm requeriments + while (Offset % 0x20 != 0) + { + Writer.Write(0); + + Offset += 4; + } + } + } + + private static string DumpDir() + { + if (string.IsNullOrEmpty(RuntimeDir)) + { + int Index = 1; + + do + { + RuntimeDir = Path.Combine(GraphicsConfig.ShadersDumpPath, "Dumps" + Index.ToString("d2")); + + Index++; + } + while (Directory.Exists(RuntimeDir)); + + Directory.CreateDirectory(RuntimeDir); + } + + return RuntimeDir; + } + + private static string ShaderExtension(GalShaderType Type) + { + switch (Type) + { + case GalShaderType.Vertex: return "vert"; + case GalShaderType.TessControl: return "tesc"; + case GalShaderType.TessEvaluation: return "tese"; + case GalShaderType.Geometry: return "geom"; + case GalShaderType.Fragment: return "frag"; + + default: throw new ArgumentException(nameof(Type)); + } + } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/GraphicsConfig.cs b/Ryujinx.Graphics/GraphicsConfig.cs new file mode 100644 index 0000000000..3e3ef4ffa7 --- /dev/null +++ b/Ryujinx.Graphics/GraphicsConfig.cs @@ -0,0 +1,4 @@ +public static class GraphicsConfig +{ + public static string ShadersDumpPath; +} diff --git a/Ryujinx/Config.cs b/Ryujinx/Config.cs index 940753ba53..0f346122af 100644 --- a/Ryujinx/Config.cs +++ b/Ryujinx/Config.cs @@ -29,6 +29,8 @@ namespace Ryujinx AOptimizations.DisableMemoryChecks = !Convert.ToBoolean(Parser.Value("Enable_Memory_Checks")); + GraphicsConfig.ShadersDumpPath = Parser.Value("Graphics_Shaders_Dump_Path"); + Log.SetEnable(LogLevel.Debug, Convert.ToBoolean(Parser.Value("Logging_Enable_Debug"))); Log.SetEnable(LogLevel.Stub, Convert.ToBoolean(Parser.Value("Logging_Enable_Stub"))); Log.SetEnable(LogLevel.Info, Convert.ToBoolean(Parser.Value("Logging_Enable_Info"))); diff --git a/Ryujinx/Ryujinx.conf b/Ryujinx/Ryujinx.conf index 59f7f859e7..063bb2de4e 100644 --- a/Ryujinx/Ryujinx.conf +++ b/Ryujinx/Ryujinx.conf @@ -1,6 +1,9 @@ #Enable cpu memory checks (slow) Enable_Memory_Checks = false +#Dump shaders in local directory (e.g. `C:\ShaderDumps`) +Graphics_Shaders_Dump_Path = + #Enable print debug logs Logging_Enable_Debug = false From e71da4fb747aaff45eafd40bbbd62638b4d659c7 Mon Sep 17 00:00:00 2001 From: greggameplayer <33609333+greggameplayer@users.noreply.github.com> Date: Mon, 16 Jul 2018 20:09:34 +0200 Subject: [PATCH 08/33] Implement BC6H_SF16 & BC6H_UF16 Texture Formats (#255) * Implement BC6H_SF16 & BC6H_UF16 * correct coding style (1/5) * correct coding style (2/5) * correct coding style (3/5) * correct coding style (4/5) * correct coding style (5/5) --- Ryujinx.Graphics/Gal/GalTextureFormat.cs | 2 ++ Ryujinx.Graphics/Gal/OpenGL/OGLEnumConverter.cs | 16 +++++++++------- Ryujinx.Graphics/Gal/OpenGL/OGLTexture.cs | 4 +++- Ryujinx.HLE/Gpu/Texture/TextureHelper.cs | 2 ++ Ryujinx.HLE/Gpu/Texture/TextureReader.cs | 2 ++ 5 files changed, 18 insertions(+), 8 deletions(-) diff --git a/Ryujinx.Graphics/Gal/GalTextureFormat.cs b/Ryujinx.Graphics/Gal/GalTextureFormat.cs index 231d33ec0e..30f5c22974 100644 --- a/Ryujinx.Graphics/Gal/GalTextureFormat.cs +++ b/Ryujinx.Graphics/Gal/GalTextureFormat.cs @@ -6,6 +6,8 @@ namespace Ryujinx.Graphics.Gal R16G16B16A16 = 0x3, A8B8G8R8 = 0x8, R32 = 0xf, + BC6H_SF16 = 0x10, + BC6H_UF16 = 0x11, A1B5G5R5 = 0x14, B5G6R5 = 0x15, BC7U = 0x17, diff --git a/Ryujinx.Graphics/Gal/OpenGL/OGLEnumConverter.cs b/Ryujinx.Graphics/Gal/OpenGL/OGLEnumConverter.cs index 8f189d2b08..5d20c931ef 100644 --- a/Ryujinx.Graphics/Gal/OpenGL/OGLEnumConverter.cs +++ b/Ryujinx.Graphics/Gal/OpenGL/OGLEnumConverter.cs @@ -148,12 +148,14 @@ namespace Ryujinx.Graphics.Gal.OpenGL { switch (Format) { - case GalTextureFormat.BC7U: return InternalFormat.CompressedRgbaBptcUnorm; - case GalTextureFormat.BC1: return InternalFormat.CompressedRgbaS3tcDxt1Ext; - case GalTextureFormat.BC2: return InternalFormat.CompressedRgbaS3tcDxt3Ext; - case GalTextureFormat.BC3: return InternalFormat.CompressedRgbaS3tcDxt5Ext; - case GalTextureFormat.BC4: return InternalFormat.CompressedRedRgtc1; - case GalTextureFormat.BC5: return InternalFormat.CompressedRgRgtc2; + case GalTextureFormat.BC6H_UF16: return InternalFormat.CompressedRgbBptcUnsignedFloat; + case GalTextureFormat.BC6H_SF16: return InternalFormat.CompressedRgbBptcSignedFloat; + case GalTextureFormat.BC7U: return InternalFormat.CompressedRgbaBptcUnorm; + case GalTextureFormat.BC1: return InternalFormat.CompressedRgbaS3tcDxt1Ext; + case GalTextureFormat.BC2: return InternalFormat.CompressedRgbaS3tcDxt3Ext; + case GalTextureFormat.BC3: return InternalFormat.CompressedRgbaS3tcDxt5Ext; + case GalTextureFormat.BC4: return InternalFormat.CompressedRedRgtc1; + case GalTextureFormat.BC5: return InternalFormat.CompressedRgRgtc2; } throw new NotImplementedException(Format.ToString()); @@ -264,4 +266,4 @@ namespace Ryujinx.Graphics.Gal.OpenGL throw new ArgumentException(nameof(BlendFactor)); } } -} \ No newline at end of file +} diff --git a/Ryujinx.Graphics/Gal/OpenGL/OGLTexture.cs b/Ryujinx.Graphics/Gal/OpenGL/OGLTexture.cs index 5caca6ecde..ac30e6fd81 100644 --- a/Ryujinx.Graphics/Gal/OpenGL/OGLTexture.cs +++ b/Ryujinx.Graphics/Gal/OpenGL/OGLTexture.cs @@ -212,6 +212,8 @@ namespace Ryujinx.Graphics.Gal.OpenGL { switch (Format) { + case GalTextureFormat.BC6H_UF16: + case GalTextureFormat.BC6H_SF16: case GalTextureFormat.BC7U: case GalTextureFormat.BC1: case GalTextureFormat.BC2: @@ -224,4 +226,4 @@ namespace Ryujinx.Graphics.Gal.OpenGL return false; } } -} \ No newline at end of file +} diff --git a/Ryujinx.HLE/Gpu/Texture/TextureHelper.cs b/Ryujinx.HLE/Gpu/Texture/TextureHelper.cs index c0749d6a25..dbbc87d409 100644 --- a/Ryujinx.HLE/Gpu/Texture/TextureHelper.cs +++ b/Ryujinx.HLE/Gpu/Texture/TextureHelper.cs @@ -54,6 +54,8 @@ namespace Ryujinx.HLE.Gpu.Texture return CompressedTextureSize(Texture.Width, Texture.Height, 4, 4, 8); } + case GalTextureFormat.BC6H_SF16: + case GalTextureFormat.BC6H_UF16: case GalTextureFormat.BC7U: case GalTextureFormat.BC2: case GalTextureFormat.BC3: diff --git a/Ryujinx.HLE/Gpu/Texture/TextureReader.cs b/Ryujinx.HLE/Gpu/Texture/TextureReader.cs index 6c08cd6c4d..1d8c8056a6 100644 --- a/Ryujinx.HLE/Gpu/Texture/TextureReader.cs +++ b/Ryujinx.HLE/Gpu/Texture/TextureReader.cs @@ -19,6 +19,8 @@ namespace Ryujinx.HLE.Gpu.Texture case GalTextureFormat.G8R8: return Read2Bpp (Memory, Texture); case GalTextureFormat.R16: return Read2Bpp (Memory, Texture); case GalTextureFormat.R8: return Read1Bpp (Memory, Texture); + case GalTextureFormat.BC6H_SF16: return Read16BptCompressedTexture(Memory, Texture, 4, 4); + case GalTextureFormat.BC6H_UF16: return Read16BptCompressedTexture(Memory, Texture, 4, 4); case GalTextureFormat.BC7U: return Read16BptCompressedTexture(Memory, Texture, 4, 4); case GalTextureFormat.BC1: return Read8Bpt4x4 (Memory, Texture); case GalTextureFormat.BC2: return Read16BptCompressedTexture(Memory, Texture, 4, 4); From 5d698a7d8d240b9d324e385fb05c0ce9f26fb410 Mon Sep 17 00:00:00 2001 From: gdkchan Date: Mon, 16 Jul 2018 15:57:15 -0300 Subject: [PATCH 09/33] Fix LDXP/LDAXP when Rt == Rn (#274) --- ChocolArm64/Instruction/AInstEmitMemoryEx.cs | 23 ++++++++++++++------ 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/ChocolArm64/Instruction/AInstEmitMemoryEx.cs b/ChocolArm64/Instruction/AInstEmitMemoryEx.cs index ba8eeceb7b..c8cf9110e3 100644 --- a/ChocolArm64/Instruction/AInstEmitMemoryEx.cs +++ b/ChocolArm64/Instruction/AInstEmitMemoryEx.cs @@ -48,18 +48,24 @@ namespace ChocolArm64.Instruction { AOpCodeMemEx Op = (AOpCodeMemEx)Context.CurrOp; - if (AccType.HasFlag(AccessType.Ordered)) + bool Ordered = (AccType & AccessType.Ordered) != 0; + bool Exclusive = (AccType & AccessType.Exclusive) != 0; + + if (Ordered) { EmitBarrier(Context); } - if (AccType.HasFlag(AccessType.Exclusive)) + if (Exclusive) { EmitMemoryCall(Context, nameof(AMemory.SetExclusive), Op.Rn); } - Context.EmitLdarg(ATranslatedSub.MemoryArgIdx); Context.EmitLdint(Op.Rn); + Context.EmitSttmp(); + + Context.EmitLdarg(ATranslatedSub.MemoryArgIdx); + Context.EmitLdtmp(); EmitReadZxCall(Context, Op.Size); @@ -68,7 +74,7 @@ namespace ChocolArm64.Instruction if (Pair) { Context.EmitLdarg(ATranslatedSub.MemoryArgIdx); - Context.EmitLdint(Op.Rn); + Context.EmitLdtmp(); Context.EmitLdc_I(8 << Op.Size); Context.Emit(OpCodes.Add); @@ -104,7 +110,10 @@ namespace ChocolArm64.Instruction { AOpCodeMemEx Op = (AOpCodeMemEx)Context.CurrOp; - if (AccType.HasFlag(AccessType.Ordered)) + bool Ordered = (AccType & AccessType.Ordered) != 0; + bool Exclusive = (AccType & AccessType.Exclusive) != 0; + + if (Ordered) { EmitBarrier(Context); } @@ -112,7 +121,7 @@ namespace ChocolArm64.Instruction AILLabel LblEx = new AILLabel(); AILLabel LblEnd = new AILLabel(); - if (AccType.HasFlag(AccessType.Exclusive)) + if (Exclusive) { EmitMemoryCall(Context, nameof(AMemory.TestExclusive), Op.Rn); @@ -145,7 +154,7 @@ namespace ChocolArm64.Instruction EmitWriteCall(Context, Op.Size); } - if (AccType.HasFlag(AccessType.Exclusive)) + if (Exclusive) { Context.EmitLdc_I8(0); Context.EmitStintzr(Op.Rs); From c2c765b30fdfa9184df580133e22ae946eebc022 Mon Sep 17 00:00:00 2001 From: Thomas Guillemard Date: Tue, 17 Jul 2018 21:14:27 +0200 Subject: [PATCH 10/33] hbabi: Implement argv (#272) This commit implements the argv config key in Ryujinx (by creating a temporary copy of the homebrew executable in the sdmc VFS) to make it possible to load libnx's "romfs" files. This commit also call Os.Dispose in Ns.OnFinish to dispose all resources when exiting --- Ryujinx.HLE/Loaders/Executable.cs | 6 +++- .../Loaders/Executables/IExecutable.cs | 2 +- Ryujinx.HLE/Loaders/Executables/Nro.cs | 6 ++-- Ryujinx.HLE/Loaders/Executables/Nso.cs | 4 +-- Ryujinx.HLE/OsHle/Homebrew.cs | 10 ++++++- Ryujinx.HLE/OsHle/Horizon.cs | 29 +++++++++++++++---- Ryujinx.HLE/OsHle/Process.cs | 10 ++++++- Ryujinx.HLE/Switch.cs | 3 +- Ryujinx.HLE/VirtualFileSystem.cs | 29 +++++++++++++++++++ Ryujinx/Ui/Program.cs | 1 + 10 files changed, 84 insertions(+), 16 deletions(-) diff --git a/Ryujinx.HLE/Loaders/Executable.cs b/Ryujinx.HLE/Loaders/Executable.cs index 618ee241a6..43193245af 100644 --- a/Ryujinx.HLE/Loaders/Executable.cs +++ b/Ryujinx.HLE/Loaders/Executable.cs @@ -2,6 +2,7 @@ using ChocolArm64.Memory; using Ryujinx.HLE.Loaders.Executables; using Ryujinx.HLE.OsHle; using System.Collections.Generic; +using System.IO; namespace Ryujinx.HLE.Loaders { @@ -15,6 +16,8 @@ namespace Ryujinx.HLE.Loaders public string Name { get; private set; } + public string FilePath { get; private set; } + private AMemory Memory; public long ImageBase { get; private set; } @@ -26,8 +29,9 @@ namespace Ryujinx.HLE.Loaders m_SymbolTable = new Dictionary(); - Name = Exe.Name; + FilePath = Exe.FilePath; + Name = Path.GetFileNameWithoutExtension(FilePath.Replace(Homebrew.TemporaryNroSuffix, "")); this.Memory = Memory; this.ImageBase = ImageBase; this.ImageEnd = ImageBase; diff --git a/Ryujinx.HLE/Loaders/Executables/IExecutable.cs b/Ryujinx.HLE/Loaders/Executables/IExecutable.cs index 1e8e569a5b..44bad61497 100644 --- a/Ryujinx.HLE/Loaders/Executables/IExecutable.cs +++ b/Ryujinx.HLE/Loaders/Executables/IExecutable.cs @@ -2,7 +2,7 @@ namespace Ryujinx.HLE.Loaders.Executables { public interface IExecutable { - string Name { get; } + string FilePath { get; } byte[] Text { get; } byte[] RO { get; } diff --git a/Ryujinx.HLE/Loaders/Executables/Nro.cs b/Ryujinx.HLE/Loaders/Executables/Nro.cs index 9e2b7e9073..0b5068d7b9 100644 --- a/Ryujinx.HLE/Loaders/Executables/Nro.cs +++ b/Ryujinx.HLE/Loaders/Executables/Nro.cs @@ -4,7 +4,7 @@ namespace Ryujinx.HLE.Loaders.Executables { class Nro : IExecutable { - public string Name { get; private set; } + public string FilePath { get; private set; } public byte[] Text { get; private set; } public byte[] RO { get; private set; } @@ -16,9 +16,9 @@ namespace Ryujinx.HLE.Loaders.Executables public int DataOffset { get; private set; } public int BssSize { get; private set; } - public Nro(Stream Input, string Name) + public Nro(Stream Input, string FilePath) { - this.Name = Name; + this.FilePath = FilePath; BinaryReader Reader = new BinaryReader(Input); diff --git a/Ryujinx.HLE/Loaders/Executables/Nso.cs b/Ryujinx.HLE/Loaders/Executables/Nso.cs index a5e1a361ee..6a55c755a8 100644 --- a/Ryujinx.HLE/Loaders/Executables/Nso.cs +++ b/Ryujinx.HLE/Loaders/Executables/Nso.cs @@ -6,7 +6,7 @@ namespace Ryujinx.HLE.Loaders.Executables { class Nso : IExecutable { - public string Name { get; private set; } + public string FilePath { get; private set; } public byte[] Text { get; private set; } public byte[] RO { get; private set; } @@ -31,7 +31,7 @@ namespace Ryujinx.HLE.Loaders.Executables public Nso(Stream Input, string Name) { - this.Name = Name; + this.FilePath = FilePath; BinaryReader Reader = new BinaryReader(Input); diff --git a/Ryujinx.HLE/OsHle/Homebrew.cs b/Ryujinx.HLE/OsHle/Homebrew.cs index 4266c8db46..778e52fe54 100644 --- a/Ryujinx.HLE/OsHle/Homebrew.cs +++ b/Ryujinx.HLE/OsHle/Homebrew.cs @@ -1,11 +1,14 @@ using ChocolArm64.Memory; +using System.Text; namespace Ryujinx.HLE.OsHle { static class Homebrew { + public const string TemporaryNroSuffix = ".ryu_tmp.nro"; + //http://switchbrew.org/index.php?title=Homebrew_ABI - public static void WriteHbAbiData(AMemory Memory, long Position, int MainThreadHandle) + public static void WriteHbAbiData(AMemory Memory, long Position, int MainThreadHandle, string SwitchPath) { Memory.Manager.Map(Position, AMemoryMgr.PageSize, (int)MemoryType.Normal, AMemoryPerm.RW); @@ -15,6 +18,11 @@ namespace Ryujinx.HLE.OsHle //NextLoadPath WriteConfigEntry(Memory, ref Position, 2, 0, Position + 0x200, Position + 0x400); + // Argv + long ArgvPosition = Position + 0xC00; + WriteConfigEntry(Memory, ref Position, 5, 0, 0, ArgvPosition); + Memory.WriteBytes(ArgvPosition, Encoding.ASCII.GetBytes(SwitchPath + "\0")); + //AppletType WriteConfigEntry(Memory, ref Position, 7); diff --git a/Ryujinx.HLE/OsHle/Horizon.cs b/Ryujinx.HLE/OsHle/Horizon.cs index a5bf0616c6..9d8a937ff2 100644 --- a/Ryujinx.HLE/OsHle/Horizon.cs +++ b/Ryujinx.HLE/OsHle/Horizon.cs @@ -87,19 +87,36 @@ namespace Ryujinx.HLE.OsHle MainProcess.Run(); } - public void LoadProgram(string FileName) + public void LoadProgram(string FilePath) { - bool IsNro = Path.GetExtension(FileName).ToLower() == ".nro"; + bool IsNro = Path.GetExtension(FilePath).ToLower() == ".nro"; - string Name = Path.GetFileNameWithoutExtension(FileName); + string Name = Path.GetFileNameWithoutExtension(FilePath); + string SwitchFilePath = Ns.VFs.SystemPathToSwitchPath(FilePath); + + if (IsNro && (SwitchFilePath == null || !SwitchFilePath.StartsWith("sdmc:/"))) + { + // TODO: avoid copying the file if we are already inside a sdmc directory + string SwitchPath = $"sdmc:/switch/{Name}{Homebrew.TemporaryNroSuffix}"; + string TempPath = Ns.VFs.SwitchPathToSystemPath(SwitchPath); + + string SwitchDir = Path.GetDirectoryName(TempPath); + if (!Directory.Exists(SwitchDir)) + { + Directory.CreateDirectory(SwitchDir); + } + File.Copy(FilePath, TempPath, true); + + FilePath = TempPath; + } Process MainProcess = MakeProcess(); - using (FileStream Input = new FileStream(FileName, FileMode.Open)) + using (FileStream Input = new FileStream(FilePath, FileMode.Open)) { MainProcess.LoadProgram(IsNro - ? (IExecutable)new Nro(Input, Name) - : (IExecutable)new Nso(Input, Name)); + ? (IExecutable)new Nro(Input, FilePath) + : (IExecutable)new Nso(Input, FilePath)); } MainProcess.SetEmptyArgs(); diff --git a/Ryujinx.HLE/OsHle/Process.cs b/Ryujinx.HLE/OsHle/Process.cs index 53e357ab9a..be27dcc288 100644 --- a/Ryujinx.HLE/OsHle/Process.cs +++ b/Ryujinx.HLE/OsHle/Process.cs @@ -13,6 +13,7 @@ using Ryujinx.HLE.OsHle.Services.Nv; using System; using System.Collections.Concurrent; using System.Collections.Generic; +using System.IO; using System.Text; namespace Ryujinx.HLE.OsHle @@ -155,7 +156,9 @@ namespace Ryujinx.HLE.OsHle { HbAbiDataPosition = AMemoryHelper.PageRoundUp(Executables[0].ImageEnd); - Homebrew.WriteHbAbiData(Memory, HbAbiDataPosition, Handle); + string SwitchPath = Ns.VFs.SystemPathToSwitchPath(Executables[0].FilePath); + + Homebrew.WriteHbAbiData(Memory, HbAbiDataPosition, Handle, SwitchPath); MainThread.Thread.ThreadState.X0 = (ulong)HbAbiDataPosition; MainThread.Thread.ThreadState.X1 = ulong.MaxValue; @@ -400,6 +403,11 @@ namespace Ryujinx.HLE.OsHle { if (Disposing && !Disposed) { + if (NeedsHbAbi && Executables[0].FilePath.EndsWith(Homebrew.TemporaryNroSuffix)) + { + File.Delete(Executables[0].FilePath); + } + //If there is still some thread running, disposing the objects is not //safe as the thread may try to access those resources. Instead, we set //the flag to have the Process disposed when all threads finishes. diff --git a/Ryujinx.HLE/Switch.cs b/Ryujinx.HLE/Switch.cs index 1946b187ba..74c0564a9f 100644 --- a/Ryujinx.HLE/Switch.cs +++ b/Ryujinx.HLE/Switch.cs @@ -81,8 +81,9 @@ namespace Ryujinx.HLE Gpu.Fifo.DispatchCalls(); } - internal virtual void OnFinish(EventArgs e) + public virtual void OnFinish(EventArgs e) { + Os.Dispose(); Finish?.Invoke(this, e); } diff --git a/Ryujinx.HLE/VirtualFileSystem.cs b/Ryujinx.HLE/VirtualFileSystem.cs index 8b71caa97a..38df81f87d 100644 --- a/Ryujinx.HLE/VirtualFileSystem.cs +++ b/Ryujinx.HLE/VirtualFileSystem.cs @@ -45,6 +45,35 @@ namespace Ryujinx.HLE public string GetGameSavesPath() => MakeDirAndGetFullPath(NandPath); + public string SwitchPathToSystemPath(string SwitchPath) + { + string[] Parts = SwitchPath.Split(":"); + if (Parts.Length != 2) + { + return null; + } + return GetFullPath(MakeDirAndGetFullPath(Parts[0]), Parts[1]); + } + + public string SystemPathToSwitchPath(string SystemPath) + { + string BaseSystemPath = GetBasePath() + "/"; + if (SystemPath.StartsWith(BaseSystemPath)) + { + string RawPath = SystemPath.Replace(BaseSystemPath, ""); + int FirstSeparatorOffset = RawPath.IndexOf('/'); + if (FirstSeparatorOffset == -1) + { + return $"{RawPath}:/"; + } + + string BasePath = RawPath.Substring(0, FirstSeparatorOffset); + string FileName = RawPath.Substring(FirstSeparatorOffset + 1); + return $"{BasePath}:/{FileName}"; + } + return null; + } + private string MakeDirAndGetFullPath(string Dir) { string FullPath = Path.Combine(GetBasePath(), Dir); diff --git a/Ryujinx/Ui/Program.cs b/Ryujinx/Ui/Program.cs index 5cacc6228b..879b9c20bf 100644 --- a/Ryujinx/Ui/Program.cs +++ b/Ryujinx/Ui/Program.cs @@ -68,6 +68,7 @@ namespace Ryujinx }; Screen.MainLoop(); + Ns.OnFinish(EventArgs.Empty); } Environment.Exit(0); From 571848536b347a85c85955745a16d4f7b9a0c04a Mon Sep 17 00:00:00 2001 From: ReinUsesLisp Date: Tue, 17 Jul 2018 16:50:00 -0300 Subject: [PATCH 11/33] Implement some shader instructions (#252) * Add IADDI32, IADD and SEL shader instructions * Add LOP shader instruction and fix LOP32I pass_b * Add ISET shader instruction * Add IADD3 shader instruction * Address feedback * Fixup OperA in Iadd_I32 --- .../Gal/Shader/ShaderDecodeAlu.cs | 214 +++++++++++++++++- .../Gal/Shader/ShaderDecodeHelper.cs | 5 + .../Gal/Shader/ShaderDecodeMove.cs | 37 +++ .../Gal/Shader/ShaderOpCodeTable.cs | 16 ++ 4 files changed, 269 insertions(+), 3 deletions(-) diff --git a/Ryujinx.Graphics/Gal/Shader/ShaderDecodeAlu.cs b/Ryujinx.Graphics/Gal/Shader/ShaderDecodeAlu.cs index 2e022fbd44..a44073513c 100644 --- a/Ryujinx.Graphics/Gal/Shader/ShaderDecodeAlu.cs +++ b/Ryujinx.Graphics/Gal/Shader/ShaderDecodeAlu.cs @@ -144,6 +144,50 @@ namespace Ryujinx.Graphics.Gal.Shader EmitFsetp(Block, OpCode, ShaderOper.RR); } + public static void Iadd_C(ShaderIrBlock Block, long OpCode) + { + EmitIadd(Block, OpCode, ShaderOper.CR); + } + + public static void Iadd_I(ShaderIrBlock Block, long OpCode) + { + EmitIadd(Block, OpCode, ShaderOper.Imm); + } + + public static void Iadd_I32(ShaderIrBlock Block, long OpCode) + { + ShaderIrNode OperA = GetOperGpr8 (OpCode); + ShaderIrNode OperB = GetOperImm32_20(OpCode); + + bool NegA = ((OpCode >> 56) & 1) != 0; + + OperA = GetAluIneg(OperA, NegA); + + ShaderIrOp Op = new ShaderIrOp(ShaderIrInst.Add, OperA, OperB); + + Block.AddNode(GetPredNode(new ShaderIrAsg(GetOperGpr0(OpCode), Op), OpCode)); + } + + public static void Iadd_R(ShaderIrBlock Block, long OpCode) + { + EmitIadd(Block, OpCode, ShaderOper.RR); + } + + public static void Iadd3_C(ShaderIrBlock Block, long OpCode) + { + EmitIadd3(Block, OpCode, ShaderOper.CR); + } + + public static void Iadd3_I(ShaderIrBlock Block, long OpCode) + { + EmitIadd3(Block, OpCode, ShaderOper.Imm); + } + + public static void Iadd3_R(ShaderIrBlock Block, long OpCode) + { + EmitIadd3(Block, OpCode, ShaderOper.RR); + } + public static void Imnmx_C(ShaderIrBlock Block, long OpCode) { EmitImnmx(Block, OpCode, ShaderOper.CR); @@ -184,6 +228,21 @@ namespace Ryujinx.Graphics.Gal.Shader EmitIscadd(Block, OpCode, ShaderOper.RR); } + public static void Iset_C(ShaderIrBlock Block, long OpCode) + { + EmitIset(Block, OpCode, ShaderOper.CR); + } + + public static void Iset_I(ShaderIrBlock Block, long OpCode) + { + EmitIset(Block, OpCode, ShaderOper.Imm); + } + + public static void Iset_R(ShaderIrBlock Block, long OpCode) + { + EmitIset(Block, OpCode, ShaderOper.RR); + } + public static void Isetp_C(ShaderIrBlock Block, long OpCode) { EmitIsetp(Block, OpCode, ShaderOper.CR); @@ -215,13 +274,13 @@ namespace Ryujinx.Graphics.Gal.Shader case 2: Inst = ShaderIrInst.Xor; break; } - ShaderIrNode OperA = GetAluNot(GetOperGpr8(OpCode), InvA); + ShaderIrNode OperB = GetAluNot(GetOperImm32_20(OpCode), InvB); //SubOp == 3 is pass, used by the not instruction //which just moves the inverted register value. if (SubOp < 3) { - ShaderIrNode OperB = GetAluNot(GetOperImm32_20(OpCode), InvB); + ShaderIrNode OperA = GetAluNot(GetOperGpr8(OpCode), InvA); ShaderIrOp Op = new ShaderIrOp(Inst, OperA, OperB); @@ -229,10 +288,25 @@ namespace Ryujinx.Graphics.Gal.Shader } else { - Block.AddNode(GetPredNode(new ShaderIrAsg(GetOperGpr0(OpCode), OperA), OpCode)); + Block.AddNode(GetPredNode(new ShaderIrAsg(GetOperGpr0(OpCode), OperB), OpCode)); } } + public static void Lop_C(ShaderIrBlock Block, long OpCode) + { + EmitLop(Block, OpCode, ShaderOper.CR); + } + + public static void Lop_I(ShaderIrBlock Block, long OpCode) + { + EmitLop(Block, OpCode, ShaderOper.Imm); + } + + public static void Lop_R(ShaderIrBlock Block, long OpCode) + { + EmitLop(Block, OpCode, ShaderOper.RR); + } + public static void Mufu(ShaderIrBlock Block, long OpCode) { int SubOp = (int)(OpCode >> 20) & 0xf; @@ -533,6 +607,92 @@ namespace Ryujinx.Graphics.Gal.Shader Block.AddNode(GetPredNode(new ShaderIrAsg(GetOperGpr0(OpCode), Op), OpCode)); } + private static void EmitIadd(ShaderIrBlock Block, long OpCode, ShaderOper Oper) + { + ShaderIrNode OperA = GetOperGpr8(OpCode); + ShaderIrNode OperB; + + switch (Oper) + { + case ShaderOper.CR: OperB = GetOperCbuf34 (OpCode); break; + case ShaderOper.Imm: OperB = GetOperImm19_20(OpCode); break; + case ShaderOper.RR: OperB = GetOperGpr20 (OpCode); break; + + default: throw new ArgumentException(nameof(Oper)); + } + + bool NegA = ((OpCode >> 49) & 1) != 0; + bool NegB = ((OpCode >> 48) & 1) != 0; + + OperA = GetAluIneg(OperA, NegA); + OperB = GetAluIneg(OperB, NegB); + + ShaderIrOp Op = new ShaderIrOp(ShaderIrInst.Add, OperA, OperB); + + Block.AddNode(GetPredNode(new ShaderIrAsg(GetOperGpr0(OpCode), Op), OpCode)); + } + + private static void EmitIadd3(ShaderIrBlock Block, long OpCode, ShaderOper Oper) + { + int Mode = (int)((OpCode >> 37) & 3); + + bool Neg1 = ((OpCode >> 51) & 1) != 0; + bool Neg2 = ((OpCode >> 50) & 1) != 0; + bool Neg3 = ((OpCode >> 49) & 1) != 0; + + int Height1 = (int)((OpCode >> 35) & 3); + int Height2 = (int)((OpCode >> 33) & 3); + int Height3 = (int)((OpCode >> 31) & 3); + + ShaderIrNode OperB; + + switch (Oper) + { + case ShaderOper.CR: OperB = GetOperCbuf34 (OpCode); break; + case ShaderOper.Imm: OperB = GetOperImm19_20(OpCode); break; + case ShaderOper.RR: OperB = GetOperGpr20 (OpCode); break; + + default: throw new ArgumentException(nameof(Oper)); + } + + ShaderIrNode ApplyHeight(ShaderIrNode Src, int Height) + { + if (Oper != ShaderOper.RR) + { + return Src; + } + + switch (Height) + { + case 0: return Src; + case 1: return new ShaderIrOp(ShaderIrInst.And, Src, new ShaderIrOperImm(0xffff)); + case 2: return new ShaderIrOp(ShaderIrInst.Lsr, Src, new ShaderIrOperImm(16)); + + default: throw new InvalidOperationException(); + } + } + + ShaderIrNode Src1 = GetAluIneg(ApplyHeight(GetOperGpr8(OpCode), Height1), Neg1); + ShaderIrNode Src2 = GetAluIneg(ApplyHeight(OperB, Height2), Neg2); + ShaderIrNode Src3 = GetAluIneg(ApplyHeight(GetOperGpr39(OpCode), Height3), Neg3); + + ShaderIrOp Sum = new ShaderIrOp(ShaderIrInst.Add, Src1, Src2); + + if (Oper == ShaderOper.RR) + { + switch (Mode) + { + case 1: Sum = new ShaderIrOp(ShaderIrInst.Lsr, Sum, new ShaderIrOperImm(16)); break; + case 2: Sum = new ShaderIrOp(ShaderIrInst.Lsl, Sum, new ShaderIrOperImm(16)); break; + } + } + + //Note: Here there should be a "+ 1" when carry flag is set + //but since carry is mostly ignored by other instructions, it's excluded for now + + Block.AddNode(GetPredNode(new ShaderIrAsg(GetOperGpr0(OpCode), new ShaderIrOp(ShaderIrInst.Add, Sum, Src3)), OpCode)); + } + private static void EmitIscadd(ShaderIrBlock Block, long OpCode, ShaderOper Oper) { bool NegB = ((OpCode >> 48) & 1) != 0; @@ -821,6 +981,54 @@ namespace Ryujinx.Graphics.Gal.Shader Block.AddNode(GetPredNode(new ShaderIrAsg(P0Node, Op), OpCode)); } + private static void EmitLop(ShaderIrBlock Block, long OpCode, ShaderOper Oper) + { + int SubOp = (int)(OpCode >> 41) & 3; + + bool InvA = ((OpCode >> 39) & 1) != 0; + bool InvB = ((OpCode >> 40) & 1) != 0; + + ShaderIrInst Inst = 0; + + switch (SubOp) + { + case 0: Inst = ShaderIrInst.And; break; + case 1: Inst = ShaderIrInst.Or; break; + case 2: Inst = ShaderIrInst.Xor; break; + } + + ShaderIrNode OperA = GetAluNot(GetOperGpr8(OpCode), InvA); + ShaderIrNode OperB; + + switch (Oper) + { + case ShaderOper.CR: OperB = GetOperCbuf34 (OpCode); break; + case ShaderOper.Imm: OperB = GetOperImm19_20(OpCode); break; + case ShaderOper.RR: OperB = GetOperGpr20 (OpCode); break; + + default: throw new ArgumentException(nameof(Oper)); + } + + OperB = GetAluNot(OperB, InvB); + + ShaderIrNode Op; + + if (SubOp < 3) + { + Op = new ShaderIrOp(Inst, OperA, OperB); + } + else + { + Op = OperB; + } + + ShaderIrNode Compare = new ShaderIrOp(ShaderIrInst.Cne, Op, new ShaderIrOperImm(0)); + + Block.AddNode(GetPredNode(new ShaderIrAsg(GetOperPred48(OpCode), Compare), OpCode)); + + Block.AddNode(GetPredNode(new ShaderIrAsg(GetOperGpr0(OpCode), Op), OpCode)); + } + private static void EmitXmad(ShaderIrBlock Block, long OpCode, ShaderOper Oper) { //TODO: Confirm SignAB/C, it is just a guess. diff --git a/Ryujinx.Graphics/Gal/Shader/ShaderDecodeHelper.cs b/Ryujinx.Graphics/Gal/Shader/ShaderDecodeHelper.cs index a8ad5ec2ec..1f1b158ef8 100644 --- a/Ryujinx.Graphics/Gal/Shader/ShaderDecodeHelper.cs +++ b/Ryujinx.Graphics/Gal/Shader/ShaderDecodeHelper.cs @@ -156,6 +156,11 @@ namespace Ryujinx.Graphics.Gal.Shader return new ShaderIrOperPred((int)(OpCode >> 39) & 7); } + public static ShaderIrOperPred GetOperPred48(long OpCode) + { + return new ShaderIrOperPred((int)((OpCode >> 48) & 7)); + } + public static ShaderIrInst GetCmp(long OpCode) { switch ((int)(OpCode >> 49) & 7) diff --git a/Ryujinx.Graphics/Gal/Shader/ShaderDecodeMove.cs b/Ryujinx.Graphics/Gal/Shader/ShaderDecodeMove.cs index dfcea905ad..4c9e59cf0c 100644 --- a/Ryujinx.Graphics/Gal/Shader/ShaderDecodeMove.cs +++ b/Ryujinx.Graphics/Gal/Shader/ShaderDecodeMove.cs @@ -113,6 +113,21 @@ namespace Ryujinx.Graphics.Gal.Shader Block.AddNode(GetPredNode(new ShaderIrAsg(GetOperGpr0(OpCode), Gpr), OpCode)); } + public static void Sel_C(ShaderIrBlock Block, long OpCode) + { + EmitSel(Block, OpCode, ShaderOper.CR); + } + + public static void Sel_I(ShaderIrBlock Block, long OpCode) + { + EmitSel(Block, OpCode, ShaderOper.Imm); + } + + public static void Sel_R(ShaderIrBlock Block, long OpCode) + { + EmitSel(Block, OpCode, ShaderOper.RR); + } + private static void EmitF2f(ShaderIrBlock Block, long OpCode, ShaderOper Oper) { bool NegA = ((OpCode >> 45) & 1) != 0; @@ -340,6 +355,28 @@ namespace Ryujinx.Graphics.Gal.Shader Block.AddNode(GetPredNode(new ShaderIrAsg(GetOperGpr0(OpCode), OperA), OpCode)); } + private static void EmitSel(ShaderIrBlock Block, long OpCode, ShaderOper Oper) + { + ShaderIrOperGpr Dst = GetOperGpr0 (OpCode); + ShaderIrNode Pred = GetOperPred39N(OpCode); + + ShaderIrNode ResultA = GetOperGpr8(OpCode); + ShaderIrNode ResultB; + + switch (Oper) + { + case ShaderOper.CR: ResultB = GetOperCbuf34 (OpCode); break; + case ShaderOper.Imm: ResultB = GetOperImm19_20(OpCode); break; + case ShaderOper.RR: ResultB = GetOperGpr20 (OpCode); break; + + default: throw new ArgumentException(nameof(Oper)); + } + + Block.AddNode(GetPredNode(new ShaderIrCond(Pred, new ShaderIrAsg(Dst, ResultA), false), OpCode)); + + Block.AddNode(GetPredNode(new ShaderIrCond(Pred, new ShaderIrAsg(Dst, ResultB), true), OpCode)); + } + private static IntType GetIntType(long OpCode) { bool Signed = ((OpCode >> 13) & 1) != 0; diff --git a/Ryujinx.Graphics/Gal/Shader/ShaderOpCodeTable.cs b/Ryujinx.Graphics/Gal/Shader/ShaderOpCodeTable.cs index b4f51e5084..1ac1178512 100644 --- a/Ryujinx.Graphics/Gal/Shader/ShaderOpCodeTable.cs +++ b/Ryujinx.Graphics/Gal/Shader/ShaderOpCodeTable.cs @@ -64,6 +64,13 @@ namespace Ryujinx.Graphics.Gal.Shader Set("0100110011100x", ShaderDecode.I2i_C); Set("0011100x11100x", ShaderDecode.I2i_I); Set("0101110011100x", ShaderDecode.I2i_R); + Set("0100110000010x", ShaderDecode.Iadd_C); + Set("0011100000010x", ShaderDecode.Iadd_I); + Set("0001110x0xxxxx", ShaderDecode.Iadd_I32); + Set("0101110000010x", ShaderDecode.Iadd_R); + Set("010011001100xx", ShaderDecode.Iadd3_C); + Set("001110001100xx", ShaderDecode.Iadd3_I); + Set("010111001100xx", ShaderDecode.Iadd3_R); Set("0100110000100x", ShaderDecode.Imnmx_C); Set("0011100x00100x", ShaderDecode.Imnmx_I); Set("0101110000100x", ShaderDecode.Imnmx_R); @@ -71,13 +78,19 @@ namespace Ryujinx.Graphics.Gal.Shader Set("0100110000011x", ShaderDecode.Iscadd_C); Set("0011100x00011x", ShaderDecode.Iscadd_I); Set("0101110000011x", ShaderDecode.Iscadd_R); + Set("010010110101xx", ShaderDecode.Iset_C); + Set("001101100101xx", ShaderDecode.Iset_I); + Set("010110110101xx", ShaderDecode.Iset_R); Set("010010110110xx", ShaderDecode.Isetp_C); Set("0011011x0110xx", ShaderDecode.Isetp_I); Set("010110110110xx", ShaderDecode.Isetp_R); Set("111000110011xx", ShaderDecode.Kil); Set("1110111111011x", ShaderDecode.Ld_A); Set("1110111110010x", ShaderDecode.Ld_C); + Set("0100110001000x", ShaderDecode.Lop_C); + Set("0011100001000x", ShaderDecode.Lop_I); Set("000001xxxxxxxx", ShaderDecode.Lop_I32); + Set("0101110001000x", ShaderDecode.Lop_R); Set("0100110010011x", ShaderDecode.Mov_C); Set("0011100x10011x", ShaderDecode.Mov_I); Set("000000010000xx", ShaderDecode.Mov_I32); @@ -87,6 +100,9 @@ namespace Ryujinx.Graphics.Gal.Shader Set("0100110010010x", ShaderDecode.Rro_C); Set("0011100x10010x", ShaderDecode.Rro_I); Set("0101110010010x", ShaderDecode.Rro_R); + Set("0100110010100x", ShaderDecode.Sel_C); + Set("0011100010100x", ShaderDecode.Sel_I); + Set("0101110010100x", ShaderDecode.Sel_R); Set("0100110001001x", ShaderDecode.Shl_C); Set("0011100x01001x", ShaderDecode.Shl_I); Set("0101110001001x", ShaderDecode.Shl_R); From 98223b0e7ddcba50a591413894c20519bc9d653d Mon Sep 17 00:00:00 2001 From: gdkchan Date: Tue, 17 Jul 2018 17:28:41 -0300 Subject: [PATCH 12/33] Fix wrong assignment and allow null FilePaths on Executable --- Ryujinx.HLE/Loaders/Executable.cs | 6 +++++- Ryujinx.HLE/Loaders/Executables/Nso.cs | 2 +- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/Ryujinx.HLE/Loaders/Executable.cs b/Ryujinx.HLE/Loaders/Executable.cs index 43193245af..84f5ff3904 100644 --- a/Ryujinx.HLE/Loaders/Executable.cs +++ b/Ryujinx.HLE/Loaders/Executable.cs @@ -31,7 +31,11 @@ namespace Ryujinx.HLE.Loaders FilePath = Exe.FilePath; - Name = Path.GetFileNameWithoutExtension(FilePath.Replace(Homebrew.TemporaryNroSuffix, "")); + if (FilePath != null) + { + Name = Path.GetFileNameWithoutExtension(FilePath.Replace(Homebrew.TemporaryNroSuffix, "")); + } + this.Memory = Memory; this.ImageBase = ImageBase; this.ImageEnd = ImageBase; diff --git a/Ryujinx.HLE/Loaders/Executables/Nso.cs b/Ryujinx.HLE/Loaders/Executables/Nso.cs index 6a55c755a8..fef9c4b853 100644 --- a/Ryujinx.HLE/Loaders/Executables/Nso.cs +++ b/Ryujinx.HLE/Loaders/Executables/Nso.cs @@ -29,7 +29,7 @@ namespace Ryujinx.HLE.Loaders.Executables HasDataHash = 1 << 5 } - public Nso(Stream Input, string Name) + public Nso(Stream Input, string FilePath) { this.FilePath = FilePath; From 2236f4b2c372e3764d14997f25ae2dee0de6f1ad Mon Sep 17 00:00:00 2001 From: emmauss Date: Wed, 18 Jul 2018 22:05:17 +0300 Subject: [PATCH 13/33] Implement IFileSystem:CleanDirectoryRecursively (#283) * implement ifilesys:cleandirectoryrecursively * clean up Ifilesystem --- .../OsHle/Services/FspSrv/IFileSystem.cs | 51 ++++++++++++------- 1 file changed, 32 insertions(+), 19 deletions(-) diff --git a/Ryujinx.HLE/OsHle/Services/FspSrv/IFileSystem.cs b/Ryujinx.HLE/OsHle/Services/FspSrv/IFileSystem.cs index 441b7e8add..61c6d11507 100644 --- a/Ryujinx.HLE/OsHle/Services/FspSrv/IFileSystem.cs +++ b/Ryujinx.HLE/OsHle/Services/FspSrv/IFileSystem.cs @@ -35,7 +35,7 @@ namespace Ryujinx.HLE.OsHle.Services.FspSrv { 10, Commit }, { 11, GetFreeSpaceSize }, { 12, GetTotalSpaceSize }, - //{ 13, CleanDirectoryRecursively }, + { 13, CleanDirectoryRecursively }, //{ 14, GetFileTimeStampRaw } }; @@ -46,8 +46,6 @@ namespace Ryujinx.HLE.OsHle.Services.FspSrv public long CreateFile(ServiceCtx Context) { - long Position = Context.Request.PtrBuff[0].Position; - string Name = ReadUtf8String(Context); long Mode = Context.RequestData.ReadInt64(); @@ -80,8 +78,6 @@ namespace Ryujinx.HLE.OsHle.Services.FspSrv public long DeleteFile(ServiceCtx Context) { - long Position = Context.Request.PtrBuff[0].Position; - string Name = ReadUtf8String(Context); string FileName = Context.Ns.VFs.GetFullPath(Path, Name); @@ -103,8 +99,6 @@ namespace Ryujinx.HLE.OsHle.Services.FspSrv public long CreateDirectory(ServiceCtx Context) { - long Position = Context.Request.PtrBuff[0].Position; - string Name = ReadUtf8String(Context); string DirName = Context.Ns.VFs.GetFullPath(Path, Name); @@ -141,8 +135,6 @@ namespace Ryujinx.HLE.OsHle.Services.FspSrv private long DeleteDirectory(ServiceCtx Context, bool Recursive) { - long Position = Context.Request.PtrBuff[0].Position; - string Name = ReadUtf8String(Context); string DirName = Context.Ns.VFs.GetFullPath(Path, Name); @@ -220,8 +212,6 @@ namespace Ryujinx.HLE.OsHle.Services.FspSrv public long GetEntryType(ServiceCtx Context) { - long Position = Context.Request.PtrBuff[0].Position; - string Name = ReadUtf8String(Context); string FileName = Context.Ns.VFs.GetFullPath(Path, Name); @@ -246,8 +236,6 @@ namespace Ryujinx.HLE.OsHle.Services.FspSrv public long OpenFile(ServiceCtx Context) { - long Position = Context.Request.PtrBuff[0].Position; - int FilterFlags = Context.RequestData.ReadInt32(); string Name = ReadUtf8String(Context); @@ -282,8 +270,6 @@ namespace Ryujinx.HLE.OsHle.Services.FspSrv public long OpenDirectory(ServiceCtx Context) { - long Position = Context.Request.PtrBuff[0].Position; - int FilterFlags = Context.RequestData.ReadInt32(); string Name = ReadUtf8String(Context); @@ -321,8 +307,6 @@ namespace Ryujinx.HLE.OsHle.Services.FspSrv public long GetFreeSpaceSize(ServiceCtx Context) { - long Position = Context.Request.PtrBuff[0].Position; - string Name = ReadUtf8String(Context); Context.ResponseData.Write(Context.Ns.VFs.GetDrive().AvailableFreeSpace); @@ -332,8 +316,6 @@ namespace Ryujinx.HLE.OsHle.Services.FspSrv public long GetTotalSpaceSize(ServiceCtx Context) { - long Position = Context.Request.PtrBuff[0].Position; - string Name = ReadUtf8String(Context); Context.ResponseData.Write(Context.Ns.VFs.GetDrive().TotalSize); @@ -341,6 +323,37 @@ namespace Ryujinx.HLE.OsHle.Services.FspSrv return 0; } + public long CleanDirectoryRecursively(ServiceCtx Context) + { + string Name = ReadUtf8String(Context); + + string DirName = Context.Ns.VFs.GetFullPath(Path, Name); + + if (!Directory.Exists(DirName)) + { + return MakeError(ErrorModule.Fs, FsErr.PathDoesNotExist); + } + + if (IsPathAlreadyInUse(DirName)) + { + return MakeError(ErrorModule.Fs, FsErr.PathAlreadyInUse); + } + + foreach (string Entry in Directory.EnumerateFileSystemEntries(DirName)) + { + if (Directory.Exists(Entry)) + { + Directory.Delete(Entry, true); + } + else if (File.Exists(Entry)) + { + File.Delete(Entry); + } + } + + return 0; + } + private bool IsPathAlreadyInUse(string Path) { lock (OpenPaths) From 0a13900bc93066dcb453f86d9154d52020255d32 Mon Sep 17 00:00:00 2001 From: greggameplayer <33609333+greggameplayer@users.noreply.github.com> Date: Thu, 19 Jul 2018 01:19:37 +0200 Subject: [PATCH 14/33] Implement Z24S8 TextureFormat (#247) * add Z24S8 TextureFormat * return correct PixelFormat & PixelType * return correct texture size * return correct Bytes Per Pixel * Correct PixelType --- Ryujinx.Graphics/Gal/GalTextureFormat.cs | 1 + Ryujinx.Graphics/Gal/OpenGL/OGLEnumConverter.cs | 1 + Ryujinx.HLE/Gpu/Texture/TextureHelper.cs | 1 + Ryujinx.HLE/Gpu/Texture/TextureReader.cs | 3 ++- 4 files changed, 5 insertions(+), 1 deletion(-) diff --git a/Ryujinx.Graphics/Gal/GalTextureFormat.cs b/Ryujinx.Graphics/Gal/GalTextureFormat.cs index 30f5c22974..f1e025f2a2 100644 --- a/Ryujinx.Graphics/Gal/GalTextureFormat.cs +++ b/Ryujinx.Graphics/Gal/GalTextureFormat.cs @@ -19,6 +19,7 @@ namespace Ryujinx.Graphics.Gal BC3 = 0x26, BC4 = 0x27, BC5 = 0x28, + Z24S8 = 0x29, ZF32 = 0x2f, Astc2D4x4 = 0x40, Astc2D5x5 = 0x41, diff --git a/Ryujinx.Graphics/Gal/OpenGL/OGLEnumConverter.cs b/Ryujinx.Graphics/Gal/OpenGL/OGLEnumConverter.cs index 5d20c931ef..2b0b7403a3 100644 --- a/Ryujinx.Graphics/Gal/OpenGL/OGLEnumConverter.cs +++ b/Ryujinx.Graphics/Gal/OpenGL/OGLEnumConverter.cs @@ -139,6 +139,7 @@ namespace Ryujinx.Graphics.Gal.OpenGL case GalTextureFormat.R16: return (PixelFormat.Red, PixelType.HalfFloat); case GalTextureFormat.R8: return (PixelFormat.Red, PixelType.UnsignedByte); case GalTextureFormat.ZF32: return (PixelFormat.DepthComponent, PixelType.Float); + case GalTextureFormat.Z24S8: return (PixelFormat.DepthStencil, PixelType.UnsignedInt248); } throw new NotImplementedException(Format.ToString()); diff --git a/Ryujinx.HLE/Gpu/Texture/TextureHelper.cs b/Ryujinx.HLE/Gpu/Texture/TextureHelper.cs index dbbc87d409..e934e363d8 100644 --- a/Ryujinx.HLE/Gpu/Texture/TextureHelper.cs +++ b/Ryujinx.HLE/Gpu/Texture/TextureHelper.cs @@ -37,6 +37,7 @@ namespace Ryujinx.HLE.Gpu.Texture case GalTextureFormat.A8B8G8R8: case GalTextureFormat.R32: case GalTextureFormat.ZF32: + case GalTextureFormat.Z24S8: return Texture.Width * Texture.Height * 4; case GalTextureFormat.A1B5G5R5: diff --git a/Ryujinx.HLE/Gpu/Texture/TextureReader.cs b/Ryujinx.HLE/Gpu/Texture/TextureReader.cs index 1d8c8056a6..3a5037bbb7 100644 --- a/Ryujinx.HLE/Gpu/Texture/TextureReader.cs +++ b/Ryujinx.HLE/Gpu/Texture/TextureReader.cs @@ -14,6 +14,7 @@ namespace Ryujinx.HLE.Gpu.Texture case GalTextureFormat.R16G16B16A16: return Read8Bpp (Memory, Texture); case GalTextureFormat.A8B8G8R8: return Read4Bpp (Memory, Texture); case GalTextureFormat.R32: return Read4Bpp (Memory, Texture); + case GalTextureFormat.Z24S8: return Read4Bpp (Memory, Texture); case GalTextureFormat.A1B5G5R5: return Read5551 (Memory, Texture); case GalTextureFormat.B5G6R5: return Read565 (Memory, Texture); case GalTextureFormat.G8R8: return Read2Bpp (Memory, Texture); @@ -42,7 +43,7 @@ namespace Ryujinx.HLE.Gpu.Texture case GalTextureFormat.Astc2D8x5: return Read16BptCompressedTexture(Memory, Texture, 8, 5); case GalTextureFormat.Astc2D10x5: return Read16BptCompressedTexture(Memory, Texture, 10, 5); case GalTextureFormat.Astc2D10x6: return Read16BptCompressedTexture(Memory, Texture, 10, 6); - } + } throw new NotImplementedException(Texture.Format.ToString()); } From 120fe6b74a0d2903471bfaeb25ef8265712fc576 Mon Sep 17 00:00:00 2001 From: greggameplayer <33609333+greggameplayer@users.noreply.github.com> Date: Thu, 19 Jul 2018 01:26:28 +0200 Subject: [PATCH 15/33] Implement BF10GF11RF11 TextureFormat (#246) * add BF10GF11RF11 TextureFormat * return correct PixelFormat & PixelType * return correct texture size * return correct Bytes Per Pixel * correct PixelType --- Ryujinx.Graphics/Gal/GalTextureFormat.cs | 1 + Ryujinx.Graphics/Gal/OpenGL/OGLEnumConverter.cs | 1 + Ryujinx.HLE/Gpu/Texture/TextureHelper.cs | 1 + Ryujinx.HLE/Gpu/Texture/TextureReader.cs | 1 + 4 files changed, 4 insertions(+) diff --git a/Ryujinx.Graphics/Gal/GalTextureFormat.cs b/Ryujinx.Graphics/Gal/GalTextureFormat.cs index f1e025f2a2..c478428325 100644 --- a/Ryujinx.Graphics/Gal/GalTextureFormat.cs +++ b/Ryujinx.Graphics/Gal/GalTextureFormat.cs @@ -14,6 +14,7 @@ namespace Ryujinx.Graphics.Gal G8R8 = 0x18, R16 = 0x1b, R8 = 0x1d, + BF10GF11RF11 = 0x21, BC1 = 0x24, BC2 = 0x25, BC3 = 0x26, diff --git a/Ryujinx.Graphics/Gal/OpenGL/OGLEnumConverter.cs b/Ryujinx.Graphics/Gal/OpenGL/OGLEnumConverter.cs index 2b0b7403a3..9004e9bae2 100644 --- a/Ryujinx.Graphics/Gal/OpenGL/OGLEnumConverter.cs +++ b/Ryujinx.Graphics/Gal/OpenGL/OGLEnumConverter.cs @@ -139,6 +139,7 @@ namespace Ryujinx.Graphics.Gal.OpenGL case GalTextureFormat.R16: return (PixelFormat.Red, PixelType.HalfFloat); case GalTextureFormat.R8: return (PixelFormat.Red, PixelType.UnsignedByte); case GalTextureFormat.ZF32: return (PixelFormat.DepthComponent, PixelType.Float); + case GalTextureFormat.BF10GF11RF11: return (PixelFormat.Rgb, PixelType.UnsignedInt10F11F11FRev); case GalTextureFormat.Z24S8: return (PixelFormat.DepthStencil, PixelType.UnsignedInt248); } diff --git a/Ryujinx.HLE/Gpu/Texture/TextureHelper.cs b/Ryujinx.HLE/Gpu/Texture/TextureHelper.cs index e934e363d8..de26c397d5 100644 --- a/Ryujinx.HLE/Gpu/Texture/TextureHelper.cs +++ b/Ryujinx.HLE/Gpu/Texture/TextureHelper.cs @@ -37,6 +37,7 @@ namespace Ryujinx.HLE.Gpu.Texture case GalTextureFormat.A8B8G8R8: case GalTextureFormat.R32: case GalTextureFormat.ZF32: + case GalTextureFormat.BF10GF11RF11: case GalTextureFormat.Z24S8: return Texture.Width * Texture.Height * 4; diff --git a/Ryujinx.HLE/Gpu/Texture/TextureReader.cs b/Ryujinx.HLE/Gpu/Texture/TextureReader.cs index 3a5037bbb7..26129877ab 100644 --- a/Ryujinx.HLE/Gpu/Texture/TextureReader.cs +++ b/Ryujinx.HLE/Gpu/Texture/TextureReader.cs @@ -14,6 +14,7 @@ namespace Ryujinx.HLE.Gpu.Texture case GalTextureFormat.R16G16B16A16: return Read8Bpp (Memory, Texture); case GalTextureFormat.A8B8G8R8: 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); case GalTextureFormat.A1B5G5R5: return Read5551 (Memory, Texture); case GalTextureFormat.B5G6R5: return Read565 (Memory, Texture); From fa5545aab80c056fa7e1f8d516a5add79eb30d8b Mon Sep 17 00:00:00 2001 From: LDj3SNuD <35856442+LDj3SNuD@users.noreply.github.com> Date: Thu, 19 Jul 2018 02:06:28 +0200 Subject: [PATCH 16/33] Implement Ssubw_V and Usubw_V instructions. (#287) * Update AOpCodeTable.cs * Update AInstEmitSimdHelper.cs * Update AInstEmitSimdArithmetic.cs * Update AInstEmitSimdMove.cs * Update AInstEmitSimdCmp.cs * Update Instructions.cs * Update CpuTestSimd.cs * Update CpuTestSimdReg.cs --- ChocolArm64/AOpCodeTable.cs | 2 + .../Instruction/AInstEmitSimdArithmetic.cs | 10 + ChocolArm64/Instruction/AInstEmitSimdCmp.cs | 6 +- .../Instruction/AInstEmitSimdHelper.cs | 77 ++++-- ChocolArm64/Instruction/AInstEmitSimdMove.cs | 15 +- Ryujinx.Tests/Cpu/CpuTestSimd.cs | 54 ++++ Ryujinx.Tests/Cpu/CpuTestSimdReg.cs | 248 ++++++++++++++++++ Ryujinx.Tests/Cpu/Tester/Instructions.cs | 243 ++++++++++++++++- 8 files changed, 620 insertions(+), 35 deletions(-) diff --git a/ChocolArm64/AOpCodeTable.cs b/ChocolArm64/AOpCodeTable.cs index 0e979aa44f..c69de38f5a 100644 --- a/ChocolArm64/AOpCodeTable.cs +++ b/ChocolArm64/AOpCodeTable.cs @@ -387,6 +387,7 @@ namespace ChocolArm64 SetA64("0100111101xxxxxx000001xxxxxxxxxx", AInstEmit.Sshr_V, typeof(AOpCodeSimdShImm)); SetA64("0x00111100>>>xxx000101xxxxxxxxxx", AInstEmit.Ssra_V, typeof(AOpCodeSimdShImm)); SetA64("0100111101xxxxxx000101xxxxxxxxxx", AInstEmit.Ssra_V, typeof(AOpCodeSimdShImm)); + SetA64("0x001110<<1xxxxx001100xxxxxxxxxx", AInstEmit.Ssubw_V, typeof(AOpCodeSimdReg)); SetA64("0x00110000000000xxxxxxxxxxxxxxxx", AInstEmit.St__Vms, typeof(AOpCodeSimdMemMs)); SetA64("0x001100100xxxxxxxxxxxxxxxxxxxxx", AInstEmit.St__Vms, typeof(AOpCodeSimdMemMs)); SetA64("0x00110100x00000xxxxxxxxxxxxxxxx", AInstEmit.St__Vss, typeof(AOpCodeSimdMemSs)); @@ -430,6 +431,7 @@ namespace ChocolArm64 SetA64("0110111101xxxxxx000001xxxxxxxxxx", AInstEmit.Ushr_V, typeof(AOpCodeSimdShImm)); SetA64("0x10111100>>>xxx000101xxxxxxxxxx", AInstEmit.Usra_V, typeof(AOpCodeSimdShImm)); SetA64("0110111101xxxxxx000101xxxxxxxxxx", AInstEmit.Usra_V, typeof(AOpCodeSimdShImm)); + SetA64("0x101110<<1xxxxx001100xxxxxxxxxx", AInstEmit.Usubw_V, typeof(AOpCodeSimdReg)); SetA64("0>001110<<0xxxxx000110xxxxxxxxxx", AInstEmit.Uzp1_V, typeof(AOpCodeSimdReg)); SetA64("0>001110<<0xxxxx010110xxxxxxxxxx", AInstEmit.Uzp2_V, typeof(AOpCodeSimdReg)); SetA64("0x001110<<100001001010xxxxxxxxxx", AInstEmit.Xtn_V, typeof(AOpCodeSimd)); diff --git a/ChocolArm64/Instruction/AInstEmitSimdArithmetic.cs b/ChocolArm64/Instruction/AInstEmitSimdArithmetic.cs index 36bb1cbf16..8e7418611e 100644 --- a/ChocolArm64/Instruction/AInstEmitSimdArithmetic.cs +++ b/ChocolArm64/Instruction/AInstEmitSimdArithmetic.cs @@ -1072,6 +1072,11 @@ namespace ChocolArm64.Instruction EmitVectorSaturatingNarrowOpSxZx(Context, () => { }); } + public static void Ssubw_V(AILEmitterCtx Context) + { + EmitVectorWidenRmBinaryOpSx(Context, () => Context.Emit(OpCodes.Sub)); + } + public static void Sub_S(AILEmitterCtx Context) { EmitScalarBinaryOpZx(Context, () => Context.Emit(OpCodes.Sub)); @@ -1225,5 +1230,10 @@ namespace ChocolArm64.Instruction { EmitVectorSaturatingNarrowOpZxZx(Context, () => { }); } + + public static void Usubw_V(AILEmitterCtx Context) + { + EmitVectorWidenRmBinaryOpZx(Context, () => Context.Emit(OpCodes.Sub)); + } } } diff --git a/ChocolArm64/Instruction/AInstEmitSimdCmp.cs b/ChocolArm64/Instruction/AInstEmitSimdCmp.cs index 773d989447..c2d47747ed 100644 --- a/ChocolArm64/Instruction/AInstEmitSimdCmp.cs +++ b/ChocolArm64/Instruction/AInstEmitSimdCmp.cs @@ -364,7 +364,7 @@ namespace ChocolArm64.Instruction AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp; int Bytes = Op.GetBitsCount() >> 3; - int Elems = (!Scalar ? Bytes >> Op.Size : 1); + int Elems = !Scalar ? Bytes >> Op.Size : 1; ulong SzMask = ulong.MaxValue >> (64 - (8 << Op.Size)); @@ -408,7 +408,7 @@ namespace ChocolArm64.Instruction AOpCodeSimdReg Op = (AOpCodeSimdReg)Context.CurrOp; int Bytes = Op.GetBitsCount() >> 3; - int Elems = (!Scalar ? Bytes >> Op.Size : 1); + int Elems = !Scalar ? Bytes >> Op.Size : 1; ulong SzMask = ulong.MaxValue >> (64 - (8 << Op.Size)); @@ -522,4 +522,4 @@ namespace ChocolArm64.Instruction Context.MarkLabel(LblEnd); } } -} \ No newline at end of file +} diff --git a/ChocolArm64/Instruction/AInstEmitSimdHelper.cs b/ChocolArm64/Instruction/AInstEmitSimdHelper.cs index 7716e2987a..0bd1a62926 100644 --- a/ChocolArm64/Instruction/AInstEmitSimdHelper.cs +++ b/ChocolArm64/Instruction/AInstEmitSimdHelper.cs @@ -419,20 +419,25 @@ namespace ChocolArm64.Instruction int SizeF = Op.Size & 1; int Bytes = Op.GetBitsCount() >> 3; + int Elems = Bytes >> SizeF + 2; - for (int Index = 0; Index < (Bytes >> SizeF + 2); Index++) + bool Rd = (Opers & OperFlags.Rd) != 0; + bool Rn = (Opers & OperFlags.Rn) != 0; + bool Rm = (Opers & OperFlags.Rm) != 0; + + for (int Index = 0; Index < Elems; Index++) { - if (Opers.HasFlag(OperFlags.Rd)) + if (Rd) { EmitVectorExtractF(Context, Op.Rd, Index, SizeF); } - if (Opers.HasFlag(OperFlags.Rn)) + if (Rn) { EmitVectorExtractF(Context, Op.Rn, Index, SizeF); } - if (Opers.HasFlag(OperFlags.Rm)) + if (Rm) { EmitVectorExtractF(Context, ((AOpCodeSimdReg)Op).Rm, Index, SizeF); } @@ -469,8 +474,9 @@ namespace ChocolArm64.Instruction int SizeF = Op.Size & 1; int Bytes = Op.GetBitsCount() >> 3; + int Elems = Bytes >> SizeF + 2; - for (int Index = 0; Index < (Bytes >> SizeF + 2); Index++) + for (int Index = 0; Index < Elems; Index++) { if (Ternary) { @@ -531,19 +537,23 @@ namespace ChocolArm64.Instruction int Bytes = Op.GetBitsCount() >> 3; int Elems = Bytes >> Op.Size; + bool Rd = (Opers & OperFlags.Rd) != 0; + bool Rn = (Opers & OperFlags.Rn) != 0; + bool Rm = (Opers & OperFlags.Rm) != 0; + for (int Index = 0; Index < Elems; Index++) { - if (Opers.HasFlag(OperFlags.Rd)) + if (Rd) { EmitVectorExtract(Context, Op.Rd, Index, Op.Size, Signed); } - if (Opers.HasFlag(OperFlags.Rn)) + if (Rn) { EmitVectorExtract(Context, Op.Rn, Index, Op.Size, Signed); } - if (Opers.HasFlag(OperFlags.Rm)) + if (Rm) { EmitVectorExtract(Context, ((AOpCodeSimdReg)Op).Rm, Index, Op.Size, Signed); } @@ -662,9 +672,6 @@ namespace ChocolArm64.Instruction { AOpCodeSimdReg Op = (AOpCodeSimdReg)Context.CurrOp; - Context.EmitLdvec(Op.Rd); - Context.EmitStvectmp(); - int Elems = 8 >> Op.Size; int Part = Op.RegisterSize == ARegisterSize.SIMD128 ? Elems : 0; @@ -707,9 +714,6 @@ namespace ChocolArm64.Instruction { AOpCodeSimdReg Op = (AOpCodeSimdReg)Context.CurrOp; - Context.EmitLdvec(Op.Rd); - Context.EmitStvectmp(); - int Elems = 8 >> Op.Size; int Part = Op.RegisterSize == ARegisterSize.SIMD128 ? Elems : 0; @@ -747,21 +751,25 @@ namespace ChocolArm64.Instruction { AOpCodeSimdReg Op = (AOpCodeSimdReg)Context.CurrOp; - int Bytes = Op.GetBitsCount() >> 3; + int Words = Op.GetBitsCount() >> 4; + int Pairs = Words >> Op.Size; - int Elems = Bytes >> Op.Size; - int Half = Elems >> 1; - - for (int Index = 0; Index < Elems; Index++) + for (int Index = 0; Index < Pairs; Index++) { - int Elem = (Index & (Half - 1)) << 1; + int Idx = Index << 1; - EmitVectorExtract(Context, Index < Half ? Op.Rn : Op.Rm, Elem + 0, Op.Size, Signed); - EmitVectorExtract(Context, Index < Half ? Op.Rn : Op.Rm, Elem + 1, Op.Size, Signed); + EmitVectorExtract(Context, Op.Rn, Idx, Op.Size, Signed); + EmitVectorExtract(Context, Op.Rn, Idx + 1, Op.Size, Signed); Emit(); - EmitVectorInsertTmp(Context, Index, Op.Size); + EmitVectorExtract(Context, Op.Rm, Idx, Op.Size, Signed); + EmitVectorExtract(Context, Op.Rm, Idx + 1, Op.Size, Signed); + + Emit(); + + EmitVectorInsertTmp(Context, Pairs + Index, Op.Size); + EmitVectorInsertTmp(Context, Index, Op.Size); } Context.EmitLdvectmp(); @@ -818,7 +826,7 @@ namespace ChocolArm64.Instruction int Part = !Scalar && (Op.RegisterSize == ARegisterSize.SIMD128) ? Elems : 0; - long TMaxValue = SignedDst ? (1 << (ESize - 1)) - 1 : (1L << ESize) - 1L; + long TMaxValue = SignedDst ? (1 << (ESize - 1)) - 1 : (long)(~0UL >> (64 - ESize)); long TMinValue = SignedDst ? -((1 << (ESize - 1))) : 0; Context.EmitLdc_I8(0L); @@ -871,7 +879,7 @@ namespace ChocolArm64.Instruction if (Scalar) { - EmitVectorZeroLower(Context, Op.Rd); + EmitVectorZeroLowerTmp(Context); } EmitVectorInsertTmp(Context, Part + Index, Op.Size); @@ -963,6 +971,11 @@ namespace ChocolArm64.Instruction EmitVectorInsert(Context, Rd, 0, 3, 0); } + public static void EmitVectorZeroLowerTmp(AILEmitterCtx Context) + { + EmitVectorInsertTmp(Context, 0, 3, 0); + } + public static void EmitVectorZeroUpper(AILEmitterCtx Context, int Rd) { EmitVectorInsert(Context, Rd, 1, 3, 0); @@ -1008,6 +1021,20 @@ namespace ChocolArm64.Instruction Context.EmitStvec(Reg); } + public static void EmitVectorInsertTmp(AILEmitterCtx Context, int Index, int Size, long Value) + { + ThrowIfInvalid(Index, Size); + + Context.EmitLdc_I8(Value); + Context.EmitLdvectmp(); + Context.EmitLdc_I4(Index); + Context.EmitLdc_I4(Size); + + AVectorHelper.EmitCall(Context, nameof(AVectorHelper.VectorInsertInt)); + + Context.EmitStvectmp(); + } + public static void EmitVectorInsertF(AILEmitterCtx Context, int Reg, int Index, int Size) { ThrowIfInvalidF(Index, Size); diff --git a/ChocolArm64/Instruction/AInstEmitSimdMove.cs b/ChocolArm64/Instruction/AInstEmitSimdMove.cs index 592cab733e..3bf1e4635b 100644 --- a/ChocolArm64/Instruction/AInstEmitSimdMove.cs +++ b/ChocolArm64/Instruction/AInstEmitSimdMove.cs @@ -295,13 +295,22 @@ namespace ChocolArm64.Instruction int Part = Op.RegisterSize == ARegisterSize.SIMD128 ? Elems : 0; + if (Part != 0) + { + Context.EmitLdvec(Op.Rd); + Context.EmitStvectmp(); + } + for (int Index = 0; Index < Elems; Index++) { EmitVectorExtractZx(Context, Op.Rn, Index, Op.Size + 1); - EmitVectorInsert(Context, Op.Rd, Part + Index, Op.Size); + EmitVectorInsertTmp(Context, Part + Index, Op.Size); } + Context.EmitLdvectmp(); + Context.EmitStvec(Op.Rd); + if (Part == 0) { EmitVectorZeroUpper(Context, Op.Rd); @@ -342,7 +351,7 @@ namespace ChocolArm64.Instruction EmitVectorExtractZx(Context, Op.Rm, Idx + Part, Op.Size); EmitVectorInsertTmp(Context, Idx + 1, Op.Size); - EmitVectorInsertTmp(Context, Idx , Op.Size); + EmitVectorInsertTmp(Context, Idx, Op.Size); } Context.EmitLdvectmp(); @@ -398,7 +407,7 @@ namespace ChocolArm64.Instruction EmitVectorExtractZx(Context, Op.Rm, Base + Index, Op.Size); EmitVectorInsertTmp(Context, Idx + 1, Op.Size); - EmitVectorInsertTmp(Context, Idx , Op.Size); + EmitVectorInsertTmp(Context, Idx, Op.Size); } Context.EmitLdvectmp(); diff --git a/Ryujinx.Tests/Cpu/CpuTestSimd.cs b/Ryujinx.Tests/Cpu/CpuTestSimd.cs index b84d29575d..82591edaee 100644 --- a/Ryujinx.Tests/Cpu/CpuTestSimd.cs +++ b/Ryujinx.Tests/Cpu/CpuTestSimd.cs @@ -1377,6 +1377,60 @@ namespace Ryujinx.Tests.Cpu }); Assert.That(((ThreadState.Fpsr >> 27) & 1) != 0, Is.EqualTo(Shared.FPSR[27])); } + + [Test, Description("XTN{2} ., .")] + 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, + [Values(0b00u, 0b01u, 0b10u)] uint size) // <8H8B, 4S4H, 2D2S> + { + uint Opcode = 0x0E212800; // XTN V0.8B, V0.8H + Opcode |= ((Rn & 31) << 5) | ((Rd & 31) << 0); + Opcode |= ((size & 3) << 22); + Bits Op = new Bits(Opcode); + + Vector128 V0 = MakeVectorE0E1(Z, Z); + Vector128 V1 = MakeVectorE0E1(A, A); + AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1); + + 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)); + SimdFp.Xtn_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())); + }); + } + + [Test, Description("XTN{2} ., .")] + 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, + [Values(0b00u, 0b01u, 0b10u)] uint size) // <8H16B, 4S8H, 2D4S> + { + uint Opcode = 0x4E212800; // XTN2 V0.16B, V0.8H + Opcode |= ((Rn & 31) << 5) | ((Rd & 31) << 0); + Opcode |= ((size & 3) << 22); + Bits Op = new Bits(Opcode); + + Vector128 V0 = MakeVectorE0E1(Z, Z); + Vector128 V1 = MakeVectorE0E1(A, A); + AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1); + + 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)); + SimdFp.Xtn_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())); + }); + } #endif } } diff --git a/Ryujinx.Tests/Cpu/CpuTestSimdReg.cs b/Ryujinx.Tests/Cpu/CpuTestSimdReg.cs index 51db857c31..c67348d1da 100644 --- a/Ryujinx.Tests/Cpu/CpuTestSimdReg.cs +++ b/Ryujinx.Tests/Cpu/CpuTestSimdReg.cs @@ -1659,6 +1659,130 @@ namespace Ryujinx.Tests.Cpu }); } + [Test, Pairwise, Description("SADDW{2} ., ., .")] + 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, + [ValueSource("_4H2S1D_")] [Random(RndCnt)] ulong A, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong B, + [Values(0b00u, 0b01u, 0b10u)] uint size) // <8B8H8H, 4H4S4S, 2S2D2D> + { + uint Opcode = 0x0E201000; // SADDW V0.8H, V0.8H, V0.8B + Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); + Opcode |= ((size & 3) << 22); + Bits Op = new Bits(Opcode); + + Vector128 V0 = MakeVectorE0E1(Z, Z); + Vector128 V1 = MakeVectorE0E1(A, A); + Vector128 V2 = MakeVectorE0(B); + AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2); + + 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)); + SimdFp.Saddw_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())); + }); + } + + [Test, Pairwise, Description("SADDW{2} ., ., .")] + 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, + [ValueSource("_4H2S1D_")] [Random(RndCnt)] ulong A, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong B, + [Values(0b00u, 0b01u, 0b10u)] uint size) // <16B8H8H, 8H4S4S, 4S2D2D> + { + uint Opcode = 0x4E201000; // SADDW2 V0.8H, V0.8H, V0.16B + Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); + Opcode |= ((size & 3) << 22); + Bits Op = new Bits(Opcode); + + Vector128 V0 = MakeVectorE0E1(Z, Z); + Vector128 V1 = MakeVectorE0E1(A, A); + Vector128 V2 = MakeVectorE1(B); + AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2); + + 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, 1, new Bits(B)); + SimdFp.Saddw_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())); + }); + } + + [Test, Pairwise, Description("SSUBW{2} ., ., .")] + 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, + [ValueSource("_4H2S1D_")] [Random(RndCnt)] ulong A, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong B, + [Values(0b00u, 0b01u, 0b10u)] uint size) // <8B8H8H, 4H4S4S, 2S2D2D> + { + uint Opcode = 0x0E203000; // SSUBW V0.8H, V0.8H, V0.8B + Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); + Opcode |= ((size & 3) << 22); + Bits Op = new Bits(Opcode); + + Vector128 V0 = MakeVectorE0E1(Z, Z); + Vector128 V1 = MakeVectorE0E1(A, A); + Vector128 V2 = MakeVectorE0(B); + AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2); + + 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)); + SimdFp.Ssubw_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())); + }); + } + + [Test, Pairwise, Description("SSUBW{2} ., ., .")] + 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, + [ValueSource("_4H2S1D_")] [Random(RndCnt)] ulong A, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong B, + [Values(0b00u, 0b01u, 0b10u)] uint size) // <16B8H8H, 8H4S4S, 4S2D2D> + { + uint Opcode = 0x4E203000; // SSUBW2 V0.8H, V0.8H, V0.16B + Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); + Opcode |= ((size & 3) << 22); + Bits Op = new Bits(Opcode); + + Vector128 V0 = MakeVectorE0E1(Z, Z); + Vector128 V1 = MakeVectorE0E1(A, A); + Vector128 V2 = MakeVectorE1(B); + AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2); + + 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, 1, new Bits(B)); + SimdFp.Ssubw_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())); + }); + } + [Test, Pairwise, Description("SUB , , ")] public void Sub_S_D([Values(0u)] uint Rd, [Values(1u, 0u)] uint Rn, @@ -2184,6 +2308,130 @@ namespace Ryujinx.Tests.Cpu }); } + [Test, Pairwise, Description("UADDW{2} ., ., .")] + 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, + [ValueSource("_4H2S1D_")] [Random(RndCnt)] ulong A, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong B, + [Values(0b00u, 0b01u, 0b10u)] uint size) // <8B8H8H, 4H4S4S, 2S2D2D> + { + uint Opcode = 0x2E201000; // UADDW V0.8H, V0.8H, V0.8B + Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); + Opcode |= ((size & 3) << 22); + Bits Op = new Bits(Opcode); + + Vector128 V0 = MakeVectorE0E1(Z, Z); + Vector128 V1 = MakeVectorE0E1(A, A); + Vector128 V2 = MakeVectorE0(B); + AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2); + + 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)); + SimdFp.Uaddw_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())); + }); + } + + [Test, Pairwise, Description("UADDW{2} ., ., .")] + 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, + [ValueSource("_4H2S1D_")] [Random(RndCnt)] ulong A, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong B, + [Values(0b00u, 0b01u, 0b10u)] uint size) // <16B8H8H, 8H4S4S, 4S2D2D> + { + uint Opcode = 0x6E201000; // UADDW2 V0.8H, V0.8H, V0.16B + Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); + Opcode |= ((size & 3) << 22); + Bits Op = new Bits(Opcode); + + Vector128 V0 = MakeVectorE0E1(Z, Z); + Vector128 V1 = MakeVectorE0E1(A, A); + Vector128 V2 = MakeVectorE1(B); + AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2); + + 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, 1, new Bits(B)); + SimdFp.Uaddw_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())); + }); + } + + [Test, Pairwise, Description("USUBW{2} ., ., .")] + 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, + [ValueSource("_4H2S1D_")] [Random(RndCnt)] ulong A, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong B, + [Values(0b00u, 0b01u, 0b10u)] uint size) // <8B8H8H, 4H4S4S, 2S2D2D> + { + uint Opcode = 0x2E203000; // USUBW V0.8H, V0.8H, V0.8B + Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); + Opcode |= ((size & 3) << 22); + Bits Op = new Bits(Opcode); + + Vector128 V0 = MakeVectorE0E1(Z, Z); + Vector128 V1 = MakeVectorE0E1(A, A); + Vector128 V2 = MakeVectorE0(B); + AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2); + + 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)); + SimdFp.Usubw_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())); + }); + } + + [Test, Pairwise, Description("USUBW{2} ., ., .")] + 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, + [ValueSource("_4H2S1D_")] [Random(RndCnt)] ulong A, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong B, + [Values(0b00u, 0b01u, 0b10u)] uint size) // <16B8H8H, 8H4S4S, 4S2D2D> + { + uint Opcode = 0x6E203000; // USUBW2 V0.8H, V0.8H, V0.16B + Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); + Opcode |= ((size & 3) << 22); + Bits Op = new Bits(Opcode); + + Vector128 V0 = MakeVectorE0E1(Z, Z); + Vector128 V1 = MakeVectorE0E1(A, A); + Vector128 V2 = MakeVectorE1(B); + AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2); + + 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, 1, new Bits(B)); + SimdFp.Usubw_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())); + }); + } + [Test, Pairwise, Description("UZP1 ., ., .")] public void Uzp1_V_8B_4H_2S([Values(0u)] uint Rd, [Values(1u, 0u)] uint Rn, diff --git a/Ryujinx.Tests/Cpu/Tester/Instructions.cs b/Ryujinx.Tests/Cpu/Tester/Instructions.cs index 68f83423ba..2f52dcbf55 100644 --- a/Ryujinx.Tests/Cpu/Tester/Instructions.cs +++ b/Ryujinx.Tests/Cpu/Tester/Instructions.cs @@ -3315,6 +3315,37 @@ namespace Ryujinx.Tests.Cpu.Tester Vpart(d, part, result); } + + // xtn_advsimd.html + public static void Xtn_V(bool Q, Bits size, Bits Rn, Bits Rd) + { + /* Decode Vector */ + int d = (int)UInt(Rd); + int n = (int)UInt(Rn); + + /* if size == '11' then ReservedValue(); */ + + int esize = 8 << (int)UInt(size); + int datasize = 64; + int part = (int)UInt(Q); + int elements = datasize / esize; + + /* Operation */ + /* CheckFPAdvSIMDEnabled64(); */ + + Bits result = new Bits(datasize); + Bits operand = V(2 * datasize, n); + Bits element; + + for (int e = 0; e <= elements - 1; e++) + { + element = Elem(operand, e, 2 * esize); + + Elem(result, e, esize, element[esize - 1, 0]); + } + + Vpart(d, part, result); + } #endregion #region "SimdReg" @@ -4395,8 +4426,8 @@ namespace Ryujinx.Tests.Cpu.Tester int part = (int)UInt(Q); int elements = datasize / esize; - bool unsigned = (U == true); bool accumulate = (op == false); + bool unsigned = (U == true); /* Operation */ /* CheckFPAdvSIMDEnabled64(); */ @@ -4484,8 +4515,8 @@ namespace Ryujinx.Tests.Cpu.Tester int part = (int)UInt(Q); int elements = datasize / esize; - bool unsigned = (U == true); bool accumulate = (op == false); + bool unsigned = (U == true); /* Operation */ /* CheckFPAdvSIMDEnabled64(); */ @@ -4511,6 +4542,108 @@ namespace Ryujinx.Tests.Cpu.Tester V(d, result); } + // saddw_advsimd.html + public static void Saddw_V(bool Q, Bits size, Bits Rm, Bits Rn, Bits Rd) + { + const bool U = false; + const bool o1 = false; + + /* Decode */ + int d = (int)UInt(Rd); + int n = (int)UInt(Rn); + int m = (int)UInt(Rm); + + /* if size == '11' then ReservedValue(); */ + + int esize = 8 << (int)UInt(size); + int datasize = 64; + int part = (int)UInt(Q); + int elements = datasize / esize; + + bool sub_op = (o1 == true); + bool unsigned = (U == true); + + /* Operation */ + /* CheckFPAdvSIMDEnabled64(); */ + + Bits result = new Bits(2 * datasize); + Bits operand1 = V(2 * datasize, n); + Bits operand2 = Vpart(datasize, m, part); + BigInteger element1; + BigInteger element2; + BigInteger sum; + + for (int e = 0; e <= elements - 1; e++) + { + element1 = Int(Elem(operand1, e, 2 * esize), unsigned); + element2 = Int(Elem(operand2, e, esize), unsigned); + + if (sub_op) + { + sum = element1 - element2; + } + else + { + sum = element1 + element2; + } + + Elem(result, e, 2 * esize, sum.SubBigInteger(2 * esize - 1, 0)); + } + + V(d, result); + } + + // ssubw_advsimd.html + public static void Ssubw_V(bool Q, Bits size, Bits Rm, Bits Rn, Bits Rd) + { + const bool U = false; + const bool o1 = true; + + /* Decode */ + int d = (int)UInt(Rd); + int n = (int)UInt(Rn); + int m = (int)UInt(Rm); + + /* if size == '11' then ReservedValue(); */ + + int esize = 8 << (int)UInt(size); + int datasize = 64; + int part = (int)UInt(Q); + int elements = datasize / esize; + + bool sub_op = (o1 == true); + bool unsigned = (U == true); + + /* Operation */ + /* CheckFPAdvSIMDEnabled64(); */ + + Bits result = new Bits(2 * datasize); + Bits operand1 = V(2 * datasize, n); + Bits operand2 = Vpart(datasize, m, part); + BigInteger element1; + BigInteger element2; + BigInteger sum; + + for (int e = 0; e <= elements - 1; e++) + { + element1 = Int(Elem(operand1, e, 2 * esize), unsigned); + element2 = Int(Elem(operand2, e, esize), unsigned); + + if (sub_op) + { + sum = element1 - element2; + } + else + { + sum = element1 + element2; + } + + Elem(result, e, 2 * esize, sum.SubBigInteger(2 * esize - 1, 0)); + } + + V(d, result); + } + // sub_advsimd.html#SUB_asisdsame_only public static void Sub_S(Bits size, Bits Rm, Bits Rn, Bits Rd) { @@ -4785,8 +4918,8 @@ namespace Ryujinx.Tests.Cpu.Tester int part = (int)UInt(Q); int elements = datasize / esize; - bool unsigned = (U == true); bool accumulate = (op == false); + bool unsigned = (U == true); /* Operation */ /* CheckFPAdvSIMDEnabled64(); */ @@ -4874,8 +5007,8 @@ namespace Ryujinx.Tests.Cpu.Tester int part = (int)UInt(Q); int elements = datasize / esize; - bool unsigned = (U == true); bool accumulate = (op == false); + bool unsigned = (U == true); /* Operation */ /* CheckFPAdvSIMDEnabled64(); */ @@ -4901,6 +5034,108 @@ namespace Ryujinx.Tests.Cpu.Tester V(d, result); } + // uaddw_advsimd.html + public static void Uaddw_V(bool Q, Bits size, Bits Rm, Bits Rn, Bits Rd) + { + const bool U = true; + const bool o1 = false; + + /* Decode */ + int d = (int)UInt(Rd); + int n = (int)UInt(Rn); + int m = (int)UInt(Rm); + + /* if size == '11' then ReservedValue(); */ + + int esize = 8 << (int)UInt(size); + int datasize = 64; + int part = (int)UInt(Q); + int elements = datasize / esize; + + bool sub_op = (o1 == true); + bool unsigned = (U == true); + + /* Operation */ + /* CheckFPAdvSIMDEnabled64(); */ + + Bits result = new Bits(2 * datasize); + Bits operand1 = V(2 * datasize, n); + Bits operand2 = Vpart(datasize, m, part); + BigInteger element1; + BigInteger element2; + BigInteger sum; + + for (int e = 0; e <= elements - 1; e++) + { + element1 = Int(Elem(operand1, e, 2 * esize), unsigned); + element2 = Int(Elem(operand2, e, esize), unsigned); + + if (sub_op) + { + sum = element1 - element2; + } + else + { + sum = element1 + element2; + } + + Elem(result, e, 2 * esize, sum.SubBigInteger(2 * esize - 1, 0)); + } + + V(d, result); + } + + // usubw_advsimd.html + public static void Usubw_V(bool Q, Bits size, Bits Rm, Bits Rn, Bits Rd) + { + const bool U = true; + const bool o1 = true; + + /* Decode */ + int d = (int)UInt(Rd); + int n = (int)UInt(Rn); + int m = (int)UInt(Rm); + + /* if size == '11' then ReservedValue(); */ + + int esize = 8 << (int)UInt(size); + int datasize = 64; + int part = (int)UInt(Q); + int elements = datasize / esize; + + bool sub_op = (o1 == true); + bool unsigned = (U == true); + + /* Operation */ + /* CheckFPAdvSIMDEnabled64(); */ + + Bits result = new Bits(2 * datasize); + Bits operand1 = V(2 * datasize, n); + Bits operand2 = Vpart(datasize, m, part); + BigInteger element1; + BigInteger element2; + BigInteger sum; + + for (int e = 0; e <= elements - 1; e++) + { + element1 = Int(Elem(operand1, e, 2 * esize), unsigned); + element2 = Int(Elem(operand2, e, esize), unsigned); + + if (sub_op) + { + sum = element1 - element2; + } + else + { + sum = element1 + element2; + } + + Elem(result, e, 2 * esize, sum.SubBigInteger(2 * esize - 1, 0)); + } + + V(d, result); + } + // uzp1_advsimd.html public static void Uzp1_V(bool Q, Bits size, Bits Rm, Bits Rn, Bits Rd) { From 2795af038df06d2f8e0dbbf0fd271bbac5da59a2 Mon Sep 17 00:00:00 2001 From: simonmkwii-dev <40786398+simonmkwii-dev@users.noreply.github.com> Date: Thu, 19 Jul 2018 11:06:45 +1000 Subject: [PATCH 17/33] Implement GetCurrentIpAddress() and stub GetCurrentNetworkProfile() (#271) * Implement GetCurrentIpAddress() ...and stub GetCurrentNetworkProfile() * Update IGeneralService.cs * Actually implement it properly this time... * * Made some requested changes * Added requested changes. * Added more requested changes. * oof * Local > Address * Cyuubumped * Change PrintInfo > PrintDebug * Revert change --- .../OsHle/Services/Nifm/IGeneralService.cs | 29 +++++++++++++++++-- 1 file changed, 27 insertions(+), 2 deletions(-) diff --git a/Ryujinx.HLE/OsHle/Services/Nifm/IGeneralService.cs b/Ryujinx.HLE/OsHle/Services/Nifm/IGeneralService.cs index e289a8db8f..66b1f1b610 100644 --- a/Ryujinx.HLE/OsHle/Services/Nifm/IGeneralService.cs +++ b/Ryujinx.HLE/OsHle/Services/Nifm/IGeneralService.cs @@ -1,6 +1,11 @@ using Ryujinx.HLE.Logging; using Ryujinx.HLE.OsHle.Ipc; +using System; using System.Collections.Generic; +using System.Linq; +using System.Net; +using System.Net.Sockets; +using System.Net.NetworkInformation; namespace Ryujinx.HLE.OsHle.Services.Nifm { @@ -14,10 +19,13 @@ namespace Ryujinx.HLE.OsHle.Services.Nifm { m_Commands = new Dictionary() { - { 4, CreateRequest } + { 4, CreateRequest }, + { 12, GetCurrentIpAddress } }; } + public const int NoInternetConnection = 0x2586e; + //CreateRequest(i32) public long CreateRequest(ServiceCtx Context) { @@ -29,5 +37,22 @@ namespace Ryujinx.HLE.OsHle.Services.Nifm return 0; } + + public long GetCurrentIpAddress(ServiceCtx Context) + { + if (!NetworkInterface.GetIsNetworkAvailable()) + { + return NoInternetConnection; + } + + IPHostEntry Host = Dns.GetHostEntry(Dns.GetHostName()); + IPAddress Address = Host.AddressList.FirstOrDefault(A => A.AddressFamily == AddressFamily.InterNetwork); + + Context.ResponseData.Write(BitConverter.ToUInt32(Address.GetAddressBytes())); + + Context.Ns.Log.PrintInfo(LogClass.ServiceNifm, "Console's local IP is " + Address.ToString()); + + return 0; + } } -} \ No newline at end of file +} From 6a69001aa26fe6f3688fa98901ca40e641743d55 Mon Sep 17 00:00:00 2001 From: Ac_K Date: Thu, 19 Jul 2018 03:10:51 +0200 Subject: [PATCH 18/33] Update IGeneralService.cs Fixed little mistake on the debug string. --- Ryujinx.HLE/OsHle/Services/Nifm/IGeneralService.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Ryujinx.HLE/OsHle/Services/Nifm/IGeneralService.cs b/Ryujinx.HLE/OsHle/Services/Nifm/IGeneralService.cs index 66b1f1b610..83bb9f370c 100644 --- a/Ryujinx.HLE/OsHle/Services/Nifm/IGeneralService.cs +++ b/Ryujinx.HLE/OsHle/Services/Nifm/IGeneralService.cs @@ -50,7 +50,7 @@ namespace Ryujinx.HLE.OsHle.Services.Nifm Context.ResponseData.Write(BitConverter.ToUInt32(Address.GetAddressBytes())); - Context.Ns.Log.PrintInfo(LogClass.ServiceNifm, "Console's local IP is " + Address.ToString()); + Context.Ns.Log.PrintInfo(LogClass.ServiceNifm, $"Console's local IP is {Address.ToString()}"); return 0; } From 8b685b12f0b7a901139999dff17b24b049b9084b Mon Sep 17 00:00:00 2001 From: Ac_K Date: Thu, 19 Jul 2018 06:03:53 +0200 Subject: [PATCH 19/33] Implement SvcWaitForAddress 0x34 (#289) * Implement SvcWaitForAddress 0x34 Currently needed by Sonic Mania Plus * Fix mistake * read-decrement-write locked --- Ryujinx.HLE/OsHle/Handles/KThread.cs | 6 +- Ryujinx.HLE/OsHle/Kernel/AddressArbiter.cs | 112 +++++++++++++++++++++ Ryujinx.HLE/OsHle/Kernel/KernelErr.cs | 2 +- Ryujinx.HLE/OsHle/Kernel/SvcHandler.cs | 3 +- Ryujinx.HLE/OsHle/Kernel/SvcSystem.cs | 2 +- Ryujinx.HLE/OsHle/Kernel/SvcThreadSync.cs | 51 ++++++++++ 6 files changed, 171 insertions(+), 5 deletions(-) create mode 100644 Ryujinx.HLE/OsHle/Kernel/AddressArbiter.cs diff --git a/Ryujinx.HLE/OsHle/Handles/KThread.cs b/Ryujinx.HLE/OsHle/Handles/KThread.cs index 3db46f3d67..2b980d17b1 100644 --- a/Ryujinx.HLE/OsHle/Handles/KThread.cs +++ b/Ryujinx.HLE/OsHle/Handles/KThread.cs @@ -9,10 +9,12 @@ namespace Ryujinx.HLE.OsHle.Handles public int CoreMask { get; set; } - public long MutexAddress { get; set; } - public long CondVarAddress { get; set; } + public long MutexAddress { get; set; } + public long CondVarAddress { get; set; } + public long ArbiterWaitAddress { get; set; } public bool CondVarSignaled { get; set; } + public bool ArbiterSignaled { get; set; } private Process Process; diff --git a/Ryujinx.HLE/OsHle/Kernel/AddressArbiter.cs b/Ryujinx.HLE/OsHle/Kernel/AddressArbiter.cs new file mode 100644 index 0000000000..ce9ef0cd87 --- /dev/null +++ b/Ryujinx.HLE/OsHle/Kernel/AddressArbiter.cs @@ -0,0 +1,112 @@ +using ChocolArm64.Memory; +using ChocolArm64.State; +using Ryujinx.HLE.OsHle.Handles; + +using static Ryujinx.HLE.OsHle.ErrorCode; + +namespace Ryujinx.HLE.OsHle.Kernel +{ + static class AddressArbiter + { + static ulong WaitForAddress(Process Process, AThreadState ThreadState, long Address, ulong Timeout) + { + KThread CurrentThread = Process.GetThread(ThreadState.Tpidr); + + Process.Scheduler.SetReschedule(CurrentThread.ProcessorId); + + CurrentThread.ArbiterWaitAddress = Address; + CurrentThread.ArbiterSignaled = false; + + Process.Scheduler.EnterWait(CurrentThread, NsTimeConverter.GetTimeMs(Timeout)); + + if (!CurrentThread.ArbiterSignaled) + { + return MakeError(ErrorModule.Kernel, KernelErr.Timeout); + } + + return 0; + } + + public static ulong WaitForAddressIfLessThan(Process Process, + AThreadState ThreadState, + AMemory Memory, + long Address, + int Value, + ulong Timeout, + bool ShouldDecrement) + { + Memory.SetExclusive(ThreadState, Address); + + int CurrentValue = Memory.ReadInt32(Address); + + while (true) + { + if (Memory.TestExclusive(ThreadState, Address)) + { + if (CurrentValue < Value) + { + if (ShouldDecrement) + { + Memory.WriteInt32(Address, CurrentValue - 1); + } + + Memory.ClearExclusiveForStore(ThreadState); + } + else + { + Memory.ClearExclusiveForStore(ThreadState); + + return MakeError(ErrorModule.Kernel, KernelErr.InvalidState); + } + + break; + } + + Memory.SetExclusive(ThreadState, Address); + + CurrentValue = Memory.ReadInt32(Address); + } + + if (Timeout == 0) + { + return MakeError(ErrorModule.Kernel, KernelErr.Timeout); + } + + return WaitForAddress(Process, ThreadState, Address, Timeout); + } + + public static ulong WaitForAddressIfEqual(Process Process, + AThreadState ThreadState, + AMemory Memory, + long Address, + int Value, + ulong Timeout) + { + if (Memory.ReadInt32(Address) != Value) + { + return MakeError(ErrorModule.Kernel, KernelErr.InvalidState); + } + + if (Timeout == 0) + { + return MakeError(ErrorModule.Kernel, KernelErr.Timeout); + } + + return WaitForAddress(Process, ThreadState, Address, Timeout); + } + } + + enum ArbitrationType : int + { + WaitIfLessThan, + DecrementAndWaitIfLessThan, + WaitIfEqual + } + + enum SignalType : int + { + Signal, + IncrementAndSignalIfEqual, + ModifyByWaitingCountAndSignalIfEqual + } +} diff --git a/Ryujinx.HLE/OsHle/Kernel/KernelErr.cs b/Ryujinx.HLE/OsHle/Kernel/KernelErr.cs index ad4fdfb6bd..bbae532555 100644 --- a/Ryujinx.HLE/OsHle/Kernel/KernelErr.cs +++ b/Ryujinx.HLE/OsHle/Kernel/KernelErr.cs @@ -12,7 +12,7 @@ namespace Ryujinx.HLE.OsHle.Kernel public const int Timeout = 117; public const int Canceled = 118; public const int CountOutOfRange = 119; - public const int InvalidInfo = 120; + public const int InvalidEnumValue = 120; public const int InvalidThread = 122; public const int InvalidState = 125; } diff --git a/Ryujinx.HLE/OsHle/Kernel/SvcHandler.cs b/Ryujinx.HLE/OsHle/Kernel/SvcHandler.cs index e05073fda3..e816c44ec3 100644 --- a/Ryujinx.HLE/OsHle/Kernel/SvcHandler.cs +++ b/Ryujinx.HLE/OsHle/Kernel/SvcHandler.cs @@ -73,7 +73,8 @@ namespace Ryujinx.HLE.OsHle.Kernel { 0x2c, SvcMapPhysicalMemory }, { 0x2d, SvcUnmapPhysicalMemory }, { 0x32, SvcSetThreadActivity }, - { 0x33, SvcGetThreadContext3 } + { 0x33, SvcGetThreadContext3 }, + { 0x34, SvcWaitForAddress } }; this.Ns = Ns; diff --git a/Ryujinx.HLE/OsHle/Kernel/SvcSystem.cs b/Ryujinx.HLE/OsHle/Kernel/SvcSystem.cs index a32b2d86fd..08305522f5 100644 --- a/Ryujinx.HLE/OsHle/Kernel/SvcSystem.cs +++ b/Ryujinx.HLE/OsHle/Kernel/SvcSystem.cs @@ -294,7 +294,7 @@ namespace Ryujinx.HLE.OsHle.Kernel InfoType == 19 || InfoType == 20) { - ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidInfo); + ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidEnumValue); return; } diff --git a/Ryujinx.HLE/OsHle/Kernel/SvcThreadSync.cs b/Ryujinx.HLE/OsHle/Kernel/SvcThreadSync.cs index ec9c40e08b..9fc426176c 100644 --- a/Ryujinx.HLE/OsHle/Kernel/SvcThreadSync.cs +++ b/Ryujinx.HLE/OsHle/Kernel/SvcThreadSync.cs @@ -197,6 +197,57 @@ namespace Ryujinx.HLE.OsHle.Kernel Process.Scheduler.EnterWait(CurrThread); } + private void SvcWaitForAddress(AThreadState ThreadState) + { + long Address = (long)ThreadState.X0; + ArbitrationType Type = (ArbitrationType)ThreadState.X1; + int Value = (int)ThreadState.X2; + ulong Timeout = ThreadState.X3; + + Ns.Log.PrintDebug(LogClass.KernelSvc, + "Address = " + Address.ToString("x16") + ", " + + "ArbitrationType = " + Type .ToString() + ", " + + "Value = " + Value .ToString("x8") + ", " + + "Timeout = " + Timeout.ToString("x16")); + + if (IsPointingInsideKernel(Address)) + { + Ns.Log.PrintWarning(LogClass.KernelSvc, $"Invalid address 0x{Address:x16}!"); + + ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidAddress); + + return; + } + + if (IsWordAddressUnaligned(Address)) + { + Ns.Log.PrintWarning(LogClass.KernelSvc, $"Unaligned address 0x{Address:x16}!"); + + ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidAlignment); + + return; + } + + switch (Type) + { + case ArbitrationType.WaitIfLessThan: + ThreadState.X0 = AddressArbiter.WaitForAddressIfLessThan(Process, ThreadState, Memory, Address, Value, Timeout, false); + break; + + case ArbitrationType.DecrementAndWaitIfLessThan: + ThreadState.X0 = AddressArbiter.WaitForAddressIfLessThan(Process, ThreadState, Memory, Address, Value, Timeout, true); + break; + + case ArbitrationType.WaitIfEqual: + ThreadState.X0 = AddressArbiter.WaitForAddressIfEqual(Process, ThreadState, Memory, Address, Value, Timeout); + break; + + default: + ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidEnumValue); + break; + } + } + private void MutexUnlock(KThread CurrThread, long MutexAddress) { lock (Process.ThreadSyncLock) From 60f2198a1e8e61fe1cfb8da30a6afcd86a672a85 Mon Sep 17 00:00:00 2001 From: gdkchan Date: Thu, 19 Jul 2018 02:30:21 -0300 Subject: [PATCH 20/33] Support deswizzle of sparse tiled textures and some frame buffer fixes (#275) * Attempt to support deswizzle of sparse tiled textures * Use correct frame buffer and viewport sizes, started to clean up the copy engine * Correct texture width alignment * Use Scale/Translate registers to calculate viewport rect * Allow texture copy between frame buffers --- Ryujinx.Graphics/Gal/IGalFrameBuffer.cs | 19 +++ Ryujinx.Graphics/Gal/OpenGL/OGLFrameBuffer.cs | 82 +++++++++-- Ryujinx.HLE/Gpu/Engines/NvGpuEngine2d.cs | 130 ++++++++++++------ Ryujinx.HLE/Gpu/Engines/NvGpuEngine3d.cs | 26 +++- Ryujinx.HLE/Gpu/Engines/NvGpuEngine3dReg.cs | 14 +- Ryujinx.HLE/Gpu/Texture/TextureFactory.cs | 5 +- Ryujinx.HLE/Gpu/Texture/TextureHelper.cs | 8 +- Ryujinx.HLE/Gpu/Texture/TextureInfo.cs | 19 ++- Ryujinx.HLE/Gpu/Texture/TextureReader.cs | 18 +-- Ryujinx.HLE/Gpu/Texture/TextureWriter.cs | 28 +--- Ryujinx.HLE/OsHle/Services/Vi/NvFlinger.cs | 4 +- 11 files changed, 237 insertions(+), 116 deletions(-) diff --git a/Ryujinx.Graphics/Gal/IGalFrameBuffer.cs b/Ryujinx.Graphics/Gal/IGalFrameBuffer.cs index eaae0a492e..1f62bdb379 100644 --- a/Ryujinx.Graphics/Gal/IGalFrameBuffer.cs +++ b/Ryujinx.Graphics/Gal/IGalFrameBuffer.cs @@ -22,6 +22,25 @@ namespace Ryujinx.Graphics.Gal void Render(); + void Copy( + long SrcKey, + long DstKey, + int SrcX0, + int SrcY0, + int SrcX1, + int SrcY1, + int DstX0, + int DstY0, + int DstX1, + int DstY1); + void GetBufferData(long Key, Action Callback); + + void SetBufferData( + long Key, + int Width, + int Height, + GalTextureFormat Format, + byte[] Buffer); } } \ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/OpenGL/OGLFrameBuffer.cs b/Ryujinx.Graphics/Gal/OpenGL/OGLFrameBuffer.cs index 305fa37d8f..cd52762c79 100644 --- a/Ryujinx.Graphics/Gal/OpenGL/OGLFrameBuffer.cs +++ b/Ryujinx.Graphics/Gal/OpenGL/OGLFrameBuffer.cs @@ -78,11 +78,6 @@ namespace Ryujinx.Graphics.Gal.OpenGL public void Create(long Key, int Width, int Height) { - //TODO: We should either use the original frame buffer size, - //or just remove the Width/Height arguments. - Width = Window.Width; - Height = Window.Height; - if (Fbs.TryGetValue(Key, out FrameBuffer Fb)) { if (Fb.Width != Width || @@ -125,8 +120,6 @@ namespace Ryujinx.Graphics.Gal.OpenGL GL.DrawBuffer(DrawBufferMode.ColorAttachment0); - GL.Viewport(0, 0, Width, Height); - Fbs.Add(Key, Fb); } @@ -230,7 +223,16 @@ namespace Ryujinx.Graphics.Gal.OpenGL { Viewport = new Rect(X, Y, Width, Height); - //TODO + SetViewport(Viewport); + } + + private void SetViewport(Rect Viewport) + { + GL.Viewport( + Viewport.X, + Viewport.Y, + Viewport.Width, + Viewport.Height); } public void Render() @@ -300,10 +302,38 @@ namespace Ryujinx.Graphics.Gal.OpenGL GL.Enable(EnableCap.Blend); } - //GL.Viewport(0, 0, 1280, 720); + SetViewport(Viewport); } } + public void Copy( + long SrcKey, + long DstKey, + int SrcX0, + int SrcY0, + int SrcX1, + int SrcY1, + int DstX0, + int DstY0, + int DstX1, + int DstY1) + { + if (Fbs.TryGetValue(SrcKey, out FrameBuffer SrcFb) && + Fbs.TryGetValue(DstKey, out FrameBuffer DstFb)) + { + GL.BindFramebuffer(FramebufferTarget.ReadFramebuffer, SrcFb.Handle); + GL.BindFramebuffer(FramebufferTarget.DrawFramebuffer, DstFb.Handle); + + GL.Clear(ClearBufferMask.ColorBufferBit); + + GL.BlitFramebuffer( + SrcX0, SrcY0, SrcX1, SrcY1, + DstX0, DstY0, DstX1, DstY1, + ClearBufferMask.ColorBufferBit, + BlitFramebufferFilter.Linear); + } +} + public void GetBufferData(long Key, Action Callback) { if (Fbs.TryGetValue(Key, out FrameBuffer Fb)) @@ -329,13 +359,35 @@ namespace Ryujinx.Graphics.Gal.OpenGL } } - private void SetViewport(Rect Viewport) + public void SetBufferData( + long Key, + int Width, + int Height, + GalTextureFormat Format, + byte[] Buffer) { - GL.Viewport( - Viewport.X, - Viewport.Y, - Viewport.Width, - Viewport.Height); + if (Fbs.TryGetValue(Key, out FrameBuffer Fb)) + { + GL.BindTexture(TextureTarget.Texture2D, Fb.TexHandle); + + const int Level = 0; + const int Border = 0; + + const PixelInternalFormat InternalFmt = PixelInternalFormat.Rgba; + + (PixelFormat GlFormat, PixelType Type) = OGLEnumConverter.GetTextureFormat(Format); + + GL.TexImage2D( + TextureTarget.Texture2D, + Level, + InternalFmt, + Width, + Height, + Border, + GlFormat, + Type, + Buffer); + } } private void EnsureInitialized() diff --git a/Ryujinx.HLE/Gpu/Engines/NvGpuEngine2d.cs b/Ryujinx.HLE/Gpu/Engines/NvGpuEngine2d.cs index f150b3f5e9..d2c5f1262b 100644 --- a/Ryujinx.HLE/Gpu/Engines/NvGpuEngine2d.cs +++ b/Ryujinx.HLE/Gpu/Engines/NvGpuEngine2d.cs @@ -1,6 +1,7 @@ using Ryujinx.Graphics.Gal; using Ryujinx.HLE.Gpu.Memory; using Ryujinx.HLE.Gpu.Texture; +using System; using System.Collections.Generic; namespace Ryujinx.HLE.Gpu.Engines @@ -64,6 +65,8 @@ namespace Ryujinx.HLE.Gpu.Engines bool SrcLinear = ReadRegister(NvGpuEngine2dReg.SrcLinear) != 0; int SrcWidth = ReadRegister(NvGpuEngine2dReg.SrcWidth); int SrcHeight = ReadRegister(NvGpuEngine2dReg.SrcHeight); + int SrcPitch = ReadRegister(NvGpuEngine2dReg.SrcPitch); + int SrcBlkDim = ReadRegister(NvGpuEngine2dReg.SrcBlockDimensions); bool DstLinear = ReadRegister(NvGpuEngine2dReg.DstLinear) != 0; int DstWidth = ReadRegister(NvGpuEngine2dReg.DstWidth); @@ -71,75 +74,114 @@ namespace Ryujinx.HLE.Gpu.Engines int DstPitch = ReadRegister(NvGpuEngine2dReg.DstPitch); int DstBlkDim = ReadRegister(NvGpuEngine2dReg.DstBlockDimensions); + TextureSwizzle SrcSwizzle = SrcLinear + ? TextureSwizzle.Pitch + : TextureSwizzle.BlockLinear; + TextureSwizzle DstSwizzle = DstLinear ? TextureSwizzle.Pitch : TextureSwizzle.BlockLinear; + int SrcBlockHeight = 1 << ((SrcBlkDim >> 4) & 0xf); int DstBlockHeight = 1 << ((DstBlkDim >> 4) & 0xf); - long Key = Vmm.GetPhysicalAddress(MakeInt64From2xInt32(NvGpuEngine2dReg.SrcAddress)); - long SrcAddress = MakeInt64From2xInt32(NvGpuEngine2dReg.SrcAddress); long DstAddress = MakeInt64From2xInt32(NvGpuEngine2dReg.DstAddress); - bool IsFbTexture = Gpu.Engine3d.IsFrameBufferPosition(Key); + long SrcKey = Vmm.GetPhysicalAddress(SrcAddress); + long DstKey = Vmm.GetPhysicalAddress(DstAddress); - if (IsFbTexture && DstLinear) + bool IsSrcFb = Gpu.Engine3d.IsFrameBufferPosition(SrcKey); + bool IsDstFb = Gpu.Engine3d.IsFrameBufferPosition(DstKey); + + TextureInfo SrcTexture() { - DstSwizzle = TextureSwizzle.BlockLinear; + return new TextureInfo( + SrcAddress, + SrcWidth, + SrcHeight, + SrcPitch, + SrcBlockHeight, 1, + SrcSwizzle, + GalTextureFormat.A8B8G8R8); } - TextureInfo DstTexture = new TextureInfo( - DstAddress, - DstWidth, - DstHeight, - DstBlockHeight, - DstBlockHeight, - DstSwizzle, - GalTextureFormat.A8B8G8R8); - - if (IsFbTexture) + TextureInfo DstTexture() { - //TODO: Change this when the correct frame buffer resolution is used. - //Currently, the frame buffer size is hardcoded to 1280x720. - SrcWidth = 1280; - SrcHeight = 720; + return new TextureInfo( + DstAddress, + DstWidth, + DstHeight, + DstPitch, + DstBlockHeight, 1, + DstSwizzle, + GalTextureFormat.A8B8G8R8); + } - Gpu.Renderer.FrameBuffer.GetBufferData(Key, (byte[] Buffer) => + //TODO: fb -> fb copies, tex -> fb copies, formats other than RGBA8, + //make it throw for unimpl stuff (like the copy mode)... + if (IsSrcFb && IsDstFb) + { + //Frame Buffer -> Frame Buffer copy. + Gpu.Renderer.FrameBuffer.Copy( + SrcKey, + DstKey, + 0, + 0, + SrcWidth, + SrcHeight, + 0, + 0, + DstWidth, + DstHeight); + } + if (IsSrcFb) + { + //Frame Buffer -> Texture copy. + Gpu.Renderer.FrameBuffer.GetBufferData(SrcKey, (byte[] Buffer) => { - CopyTexture( - Vmm, - DstTexture, - Buffer, - SrcWidth, - SrcHeight); + TextureInfo Src = SrcTexture(); + TextureInfo Dst = DstTexture(); + + if (Src.Width != Dst.Width || + Src.Height != Dst.Height) + { + throw new NotImplementedException("Texture resizing is not supported"); + } + + TextureWriter.Write(Vmm, Dst, Buffer); }); } + else if (IsDstFb) + { + //Texture -> Frame Buffer copy. + const GalTextureFormat Format = GalTextureFormat.A8B8G8R8; + + byte[] Buffer = TextureReader.Read(Vmm, SrcTexture()); + + Gpu.Renderer.FrameBuffer.SetBufferData( + DstKey, + DstWidth, + DstHeight, + Format, + Buffer); + } else { - long Size = SrcWidth * SrcHeight * 4; + //Texture -> Texture copy. + TextureInfo Src = SrcTexture(); + TextureInfo Dst = DstTexture(); - byte[] Buffer = Vmm.ReadBytes(SrcAddress, Size); + if (Src.Width != Dst.Width || + Src.Height != Dst.Height) + { + throw new NotImplementedException("Texture resizing is not supported"); + } - CopyTexture( - Vmm, - DstTexture, - Buffer, - SrcWidth, - SrcHeight); + TextureWriter.Write(Vmm, Dst, TextureReader.Read(Vmm, Src)); } } - private void CopyTexture( - NvGpuVmm Vmm, - TextureInfo Texture, - byte[] Buffer, - int Width, - int Height) - { - TextureWriter.Write(Vmm, Texture, Buffer, Width, Height); - } - private long MakeInt64From2xInt32(NvGpuEngine2dReg Reg) { return diff --git a/Ryujinx.HLE/Gpu/Engines/NvGpuEngine3d.cs b/Ryujinx.HLE/Gpu/Engines/NvGpuEngine3d.cs index 5c474ab0bc..dce25a5e94 100644 --- a/Ryujinx.HLE/Gpu/Engines/NvGpuEngine3d.cs +++ b/Ryujinx.HLE/Gpu/Engines/NvGpuEngine3d.cs @@ -132,10 +132,22 @@ namespace Ryujinx.HLE.Gpu.Engines int Width = ReadRegister(NvGpuEngine3dReg.FrameBufferNWidth + FbIndex * 0x10); int Height = ReadRegister(NvGpuEngine3dReg.FrameBufferNHeight + FbIndex * 0x10); - //Note: Using the Width/Height results seems to give incorrect results. - //Maybe the size of all frame buffers is hardcoded to screen size? This seems unlikely. - Gpu.Renderer.FrameBuffer.Create(Key, 1280, 720); + float TX = ReadRegisterFloat(NvGpuEngine3dReg.ViewportNTranslateX + FbIndex * 4); + float TY = ReadRegisterFloat(NvGpuEngine3dReg.ViewportNTranslateY + FbIndex * 4); + + float SX = ReadRegisterFloat(NvGpuEngine3dReg.ViewportNScaleX + FbIndex * 4); + float SY = ReadRegisterFloat(NvGpuEngine3dReg.ViewportNScaleY + FbIndex * 4); + + int VpX = (int)MathF.Max(0, TX - MathF.Abs(SX)); + int VpY = (int)MathF.Max(0, TY - MathF.Abs(SY)); + + int VpW = (int)(TX + MathF.Abs(SX)) - VpX; + int VpH = (int)(TY + MathF.Abs(SY)) - VpY; + + Gpu.Renderer.FrameBuffer.Create(Key, Width, Height); Gpu.Renderer.FrameBuffer.Bind(Key); + + Gpu.Renderer.FrameBuffer.SetViewport(VpX, VpY, VpW, VpH); } private long[] UploadShaders(NvGpuVmm Vmm) @@ -195,8 +207,8 @@ namespace Ryujinx.HLE.Gpu.Engines Gpu.Renderer.Shader.Bind(Key); } - float SignX = GetFlipSign(NvGpuEngine3dReg.ViewportScaleX); - float SignY = GetFlipSign(NvGpuEngine3dReg.ViewportScaleY); + float SignX = GetFlipSign(NvGpuEngine3dReg.ViewportNScaleX); + float SignY = GetFlipSign(NvGpuEngine3dReg.ViewportNScaleY); Gpu.Renderer.Shader.SetFlip(SignX, SignY); @@ -220,8 +232,8 @@ namespace Ryujinx.HLE.Gpu.Engines private void SetFrontFace() { - float SignX = GetFlipSign(NvGpuEngine3dReg.ViewportScaleX); - float SignY = GetFlipSign(NvGpuEngine3dReg.ViewportScaleY); + float SignX = GetFlipSign(NvGpuEngine3dReg.ViewportNScaleX); + float SignY = GetFlipSign(NvGpuEngine3dReg.ViewportNScaleY); GalFrontFace FrontFace = (GalFrontFace)ReadRegister(NvGpuEngine3dReg.FrontFace); diff --git a/Ryujinx.HLE/Gpu/Engines/NvGpuEngine3dReg.cs b/Ryujinx.HLE/Gpu/Engines/NvGpuEngine3dReg.cs index 3de2885ef2..e7dabe44a4 100644 --- a/Ryujinx.HLE/Gpu/Engines/NvGpuEngine3dReg.cs +++ b/Ryujinx.HLE/Gpu/Engines/NvGpuEngine3dReg.cs @@ -6,12 +6,14 @@ namespace Ryujinx.HLE.Gpu.Engines FrameBufferNWidth = 0x202, FrameBufferNHeight = 0x203, FrameBufferNFormat = 0x204, - ViewportScaleX = 0x280, - ViewportScaleY = 0x281, - ViewportScaleZ = 0x282, - ViewportTranslateX = 0x283, - ViewportTranslateY = 0x284, - ViewportTranslateZ = 0x285, + ViewportNScaleX = 0x280, + ViewportNScaleY = 0x281, + ViewportNScaleZ = 0x282, + ViewportNTranslateX = 0x283, + ViewportNTranslateY = 0x284, + ViewportNTranslateZ = 0x285, + ViewportNHoriz = 0x300, + ViewportNVert = 0x301, VertexArrayFirst = 0x35d, VertexArrayCount = 0x35e, ClearDepth = 0x364, diff --git a/Ryujinx.HLE/Gpu/Texture/TextureFactory.cs b/Ryujinx.HLE/Gpu/Texture/TextureFactory.cs index 9df0b60002..4db0b6f106 100644 --- a/Ryujinx.HLE/Gpu/Texture/TextureFactory.cs +++ b/Ryujinx.HLE/Gpu/Texture/TextureFactory.cs @@ -55,9 +55,11 @@ namespace Ryujinx.HLE.Gpu.Texture int Pitch = (Tic[3] & 0xffff) << 5; - int BlockHeightLog2 = (Tic[3] >> 3) & 7; + int BlockHeightLog2 = (Tic[3] >> 3) & 7; + int TileWidthLog2 = (Tic[3] >> 10) & 7; int BlockHeight = 1 << BlockHeightLog2; + int TileWidth = 1 << TileWidthLog2; int Width = (Tic[4] & 0xffff) + 1; int Height = (Tic[5] & 0xffff) + 1; @@ -68,6 +70,7 @@ namespace Ryujinx.HLE.Gpu.Texture Height, Pitch, BlockHeight, + TileWidth, Swizzle, Format); diff --git a/Ryujinx.HLE/Gpu/Texture/TextureHelper.cs b/Ryujinx.HLE/Gpu/Texture/TextureHelper.cs index de26c397d5..ecf2b6bf5c 100644 --- a/Ryujinx.HLE/Gpu/Texture/TextureHelper.cs +++ b/Ryujinx.HLE/Gpu/Texture/TextureHelper.cs @@ -7,8 +7,14 @@ namespace Ryujinx.HLE.Gpu.Texture { static class TextureHelper { - public static ISwizzle GetSwizzle(TextureInfo Texture, int Width, int Bpp) + public static ISwizzle GetSwizzle(TextureInfo Texture, int BlockWidth, int Bpp) { + int Width = (Texture.Width + (BlockWidth - 1)) / BlockWidth; + + int AlignMask = Texture.TileWidth * (64 / Bpp) - 1; + + Width = (Width + AlignMask) & ~AlignMask; + switch (Texture.Swizzle) { case TextureSwizzle._1dBuffer: diff --git a/Ryujinx.HLE/Gpu/Texture/TextureInfo.cs b/Ryujinx.HLE/Gpu/Texture/TextureInfo.cs index 31784bbc58..2a98ce00fd 100644 --- a/Ryujinx.HLE/Gpu/Texture/TextureInfo.cs +++ b/Ryujinx.HLE/Gpu/Texture/TextureInfo.cs @@ -11,6 +11,7 @@ namespace Ryujinx.HLE.Gpu.Texture public int Pitch { get; private set; } public int BlockHeight { get; private set; } + public int TileWidth { get; private set; } public TextureSwizzle Swizzle { get; private set; } @@ -29,6 +30,8 @@ namespace Ryujinx.HLE.Gpu.Texture BlockHeight = 16; + TileWidth = 1; + Swizzle = TextureSwizzle.BlockLinear; Format = GalTextureFormat.A8B8G8R8; @@ -40,16 +43,18 @@ namespace Ryujinx.HLE.Gpu.Texture int Height, int Pitch, int BlockHeight, + int TileWidth, TextureSwizzle Swizzle, GalTextureFormat Format) { - this.Position = Position; - this.Width = Width; - this.Height = Height; - this.Pitch = Pitch; - this.BlockHeight = BlockHeight; - this.Swizzle = Swizzle; - this.Format = Format; + this.Position = Position; + this.Width = Width; + this.Height = Height; + this.Pitch = Pitch; + this.BlockHeight = BlockHeight; + this.TileWidth = TileWidth; + this.Swizzle = Swizzle; + this.Format = Format; } } } \ No newline at end of file diff --git a/Ryujinx.HLE/Gpu/Texture/TextureReader.cs b/Ryujinx.HLE/Gpu/Texture/TextureReader.cs index 26129877ab..350ab825f4 100644 --- a/Ryujinx.HLE/Gpu/Texture/TextureReader.cs +++ b/Ryujinx.HLE/Gpu/Texture/TextureReader.cs @@ -56,7 +56,7 @@ namespace Ryujinx.HLE.Gpu.Texture byte[] Output = new byte[Width * Height]; - ISwizzle Swizzle = TextureHelper.GetSwizzle(Texture, Width, 1); + ISwizzle Swizzle = TextureHelper.GetSwizzle(Texture, 1, 1); (AMemory CpuMem, long Position) = TextureHelper.GetMemoryAndPosition( Memory, @@ -89,7 +89,7 @@ namespace Ryujinx.HLE.Gpu.Texture byte[] Output = new byte[Width * Height * 2]; - ISwizzle Swizzle = TextureHelper.GetSwizzle(Texture, Width, 2); + ISwizzle Swizzle = TextureHelper.GetSwizzle(Texture, 1, 2); (AMemory CpuMem, long Position) = TextureHelper.GetMemoryAndPosition( Memory, @@ -127,7 +127,7 @@ namespace Ryujinx.HLE.Gpu.Texture byte[] Output = new byte[Width * Height * 2]; - ISwizzle Swizzle = TextureHelper.GetSwizzle(Texture, Width, 2); + ISwizzle Swizzle = TextureHelper.GetSwizzle(Texture, 1, 2); (AMemory CpuMem, long Position) = TextureHelper.GetMemoryAndPosition( Memory, @@ -164,7 +164,7 @@ namespace Ryujinx.HLE.Gpu.Texture byte[] Output = new byte[Width * Height * 2]; - ISwizzle Swizzle = TextureHelper.GetSwizzle(Texture, Width, 2); + ISwizzle Swizzle = TextureHelper.GetSwizzle(Texture, 1, 2); (AMemory CpuMem, long Position) = TextureHelper.GetMemoryAndPosition( Memory, @@ -197,7 +197,7 @@ namespace Ryujinx.HLE.Gpu.Texture byte[] Output = new byte[Width * Height * 4]; - ISwizzle Swizzle = TextureHelper.GetSwizzle(Texture, Width, 4); + ISwizzle Swizzle = TextureHelper.GetSwizzle(Texture, 1, 4); (AMemory CpuMem, long Position) = TextureHelper.GetMemoryAndPosition( Memory, @@ -230,7 +230,7 @@ namespace Ryujinx.HLE.Gpu.Texture byte[] Output = new byte[Width * Height * 8]; - ISwizzle Swizzle = TextureHelper.GetSwizzle(Texture, Width, 8); + ISwizzle Swizzle = TextureHelper.GetSwizzle(Texture, 1, 8); (AMemory CpuMem, long Position) = TextureHelper.GetMemoryAndPosition( Memory, @@ -263,7 +263,7 @@ namespace Ryujinx.HLE.Gpu.Texture byte[] Output = new byte[Width * Height * 16]; - ISwizzle Swizzle = TextureHelper.GetSwizzle(Texture, Width, 16); + ISwizzle Swizzle = TextureHelper.GetSwizzle(Texture, 1, 16); (AMemory CpuMem, long Position) = TextureHelper.GetMemoryAndPosition( Memory, @@ -298,7 +298,7 @@ namespace Ryujinx.HLE.Gpu.Texture byte[] Output = new byte[Width * Height * 8]; - ISwizzle Swizzle = TextureHelper.GetSwizzle(Texture, Width, 8); + ISwizzle Swizzle = TextureHelper.GetSwizzle(Texture, 4, 8); (AMemory CpuMem, long Position) = TextureHelper.GetMemoryAndPosition( Memory, @@ -331,7 +331,7 @@ namespace Ryujinx.HLE.Gpu.Texture byte[] Output = new byte[Width * Height * 16]; - ISwizzle Swizzle = TextureHelper.GetSwizzle(Texture, Width, 16); + ISwizzle Swizzle = TextureHelper.GetSwizzle(Texture, BlockWidth, 16); (AMemory CpuMem, long Position) = TextureHelper.GetMemoryAndPosition( Memory, diff --git a/Ryujinx.HLE/Gpu/Texture/TextureWriter.cs b/Ryujinx.HLE/Gpu/Texture/TextureWriter.cs index b64302a5ab..a87d4545b0 100644 --- a/Ryujinx.HLE/Gpu/Texture/TextureWriter.cs +++ b/Ryujinx.HLE/Gpu/Texture/TextureWriter.cs @@ -6,29 +6,9 @@ namespace Ryujinx.HLE.Gpu.Texture { static class TextureWriter { - public static void Write( - IAMemory Memory, - TextureInfo Texture, - byte[] Data, - int Width, - int Height) + public unsafe static void Write(IAMemory Memory, TextureInfo Texture, byte[] Data) { - switch (Texture.Format) - { - case GalTextureFormat.A8B8G8R8: Write4Bpp(Memory, Texture, Data, Width, Height); break; - - default: throw new NotImplementedException(Texture.Format.ToString()); - } - } - - private unsafe static void Write4Bpp( - IAMemory Memory, - TextureInfo Texture, - byte[] Data, - int Width, - int Height) - { - ISwizzle Swizzle = TextureHelper.GetSwizzle(Texture, Width, 4); + ISwizzle Swizzle = TextureHelper.GetSwizzle(Texture, 1, 4); (AMemory CpuMem, long Position) = TextureHelper.GetMemoryAndPosition( Memory, @@ -38,8 +18,8 @@ namespace Ryujinx.HLE.Gpu.Texture { long InOffs = 0; - for (int Y = 0; Y < Height; Y++) - for (int X = 0; X < Width; X++) + for (int Y = 0; Y < Texture.Height; Y++) + for (int X = 0; X < Texture.Width; X++) { long Offset = (uint)Swizzle.GetSwizzleOffset(X, Y); diff --git a/Ryujinx.HLE/OsHle/Services/Vi/NvFlinger.cs b/Ryujinx.HLE/OsHle/Services/Vi/NvFlinger.cs index a3ed3ab51c..5307127be4 100644 --- a/Ryujinx.HLE/OsHle/Services/Vi/NvFlinger.cs +++ b/Ryujinx.HLE/OsHle/Services/Vi/NvFlinger.cs @@ -279,8 +279,8 @@ namespace Ryujinx.HLE.OsHle.Services.Android private void SendFrameBuffer(ServiceCtx Context, int Slot) { - int FbWidth = 1280; - int FbHeight = 720; + int FbWidth = BufferQueue[Slot].Data.Width; + int FbHeight = BufferQueue[Slot].Data.Height; int NvMapHandle = BitConverter.ToInt32(BufferQueue[Slot].Data.RawData, 0x4c); int BufferOffset = BitConverter.ToInt32(BufferQueue[Slot].Data.RawData, 0x50); From bdb6cbb43514f6d8eb96847a22b70709ae705827 Mon Sep 17 00:00:00 2001 From: Merry Date: Thu, 19 Jul 2018 06:32:37 +0100 Subject: [PATCH 21/33] AOpCodeTable: Speed up instruction decoding (#284) --- ChocolArm64/AOpCodeTable.cs | 71 +++++++++++++++++++++++++------------ 1 file changed, 49 insertions(+), 22 deletions(-) diff --git a/ChocolArm64/AOpCodeTable.cs b/ChocolArm64/AOpCodeTable.cs index c69de38f5a..689e039237 100644 --- a/ChocolArm64/AOpCodeTable.cs +++ b/ChocolArm64/AOpCodeTable.cs @@ -4,6 +4,7 @@ using ChocolArm64.Instruction; using ChocolArm64.Instruction32; using ChocolArm64.State; using System; +using System.Collections.Generic; namespace ChocolArm64 { @@ -438,18 +439,43 @@ namespace ChocolArm64 SetA64("0>001110<<0xxxxx001110xxxxxxxxxx", AInstEmit.Zip1_V, typeof(AOpCodeSimdReg)); SetA64("0>001110<<0xxxxx011110xxxxxxxxxx", AInstEmit.Zip2_V, typeof(AOpCodeSimdReg)); #endregion + +#region "Generate InstA64FastLookup Table (AArch64)" + var Tmp = new List[FastLookupSize]; + for (int i = 0; i < FastLookupSize; i++) + { + Tmp[i] = new List(); + } + + foreach (var Inst in AllInstA64) + { + int Mask = ToFastLookupIndex(Inst.Mask); + int Value = ToFastLookupIndex(Inst.Value); + + for (int i = 0; i < FastLookupSize; i++) + { + if ((i & Mask) == Value) + { + Tmp[i].Add(Inst); + } + } + } + + for (int i = 0; i < FastLookupSize; i++) + { + InstA64FastLookup[i] = Tmp[i].ToArray(); + } +#endregion } - private class TreeNode + private class InstInfo { public int Mask; public int Value; - public TreeNode Next; - public AInst Inst; - public TreeNode(int Mask, int Value, AInst Inst) + public InstInfo(int Mask, int Value, AInst Inst) { this.Mask = Mask; this.Value = Value; @@ -457,8 +483,11 @@ namespace ChocolArm64 } } - private static TreeNode InstHeadA32; - private static TreeNode InstHeadA64; + private static List AllInstA32 = new List(); + private static List AllInstA64 = new List(); + + private static int FastLookupSize = 0x1000; + private static InstInfo[][] InstA64FastLookup = new InstInfo[FastLookupSize][]; private static void SetA32(string Encoding, AInstInterpreter Interpreter, Type Type) { @@ -519,7 +548,7 @@ namespace ChocolArm64 if (XBits == 0) { - InsertTop(XMask, Value, Inst, Mode); + InsertInst(XMask, Value, Inst, Mode); return; } @@ -535,55 +564,53 @@ namespace ChocolArm64 if (Mask != Blacklisted) { - InsertTop(XMask, Value | Mask, Inst, Mode); + InsertInst(XMask, Value | Mask, Inst, Mode); } } } - private static void InsertTop( + private static void InsertInst( int XMask, int Value, AInst Inst, AExecutionMode Mode) { - TreeNode Node = new TreeNode(XMask, Value, Inst); + InstInfo Info = new InstInfo(XMask, Value, Inst); if (Mode == AExecutionMode.AArch64) { - Node.Next = InstHeadA64; - - InstHeadA64 = Node; + AllInstA64.Add(Info); } else { - Node.Next = InstHeadA32; - - InstHeadA32 = Node; + AllInstA32.Add(Info); } } public static AInst GetInstA32(int OpCode) { - return GetInst(InstHeadA32, OpCode); + return GetInstFromList(AllInstA32, OpCode); } public static AInst GetInstA64(int OpCode) { - return GetInst(InstHeadA64, OpCode); + return GetInstFromList(InstA64FastLookup[ToFastLookupIndex(OpCode)], OpCode); } - private static AInst GetInst(TreeNode Head, int OpCode) + private static int ToFastLookupIndex(int Value) { - TreeNode Node = Head; + return ((Value >> 10) & 0x00F) | ((Value >> 18) & 0xFF0); + } - do + private static AInst GetInstFromList(IEnumerable InstList, int OpCode) + { + foreach (var Node in InstList) { if ((OpCode & Node.Mask) == Node.Value) { return Node.Inst; } } - while ((Node = Node.Next) != null); return AInst.Undefined; } From cd203e98f2bed076798972da1d108bb64b1884ec Mon Sep 17 00:00:00 2001 From: ReinUsesLisp Date: Thu, 19 Jul 2018 02:33:27 -0300 Subject: [PATCH 22/33] Implement Geometry shaders (#280) * Implement Geometry shaders * Add EmitVertex() and EndPrimitive() * Read output geometry data from header * Stub Vmad * Add Iadd_I32 * Stub Mov_S (S2R) * Stub Isberd * Change vertex index to gpr39 in Abuf * Add stub messages for consistency * Do not print input block when there is no attributes * Use GL_ARB_enhanced_layouts * Skip geometry shaders when there's no GL_ARB_enhanced_layouts * Address feedback * Address feedback --- Ryujinx.Graphics/Gal/OpenGL/OGLExtension.cs | 43 +++++ Ryujinx.Graphics/Gal/OpenGL/OGLShader.cs | 22 ++- Ryujinx.Graphics/Gal/Shader/GlslDecl.cs | 7 +- Ryujinx.Graphics/Gal/Shader/GlslDecompiler.cs | 156 ++++++++++++++++-- .../Gal/Shader/ShaderDecodeAlu.cs | 37 +++++ .../Gal/Shader/ShaderDecodeHelper.cs | 8 +- .../Gal/Shader/ShaderDecodeMem.cs | 3 + .../Gal/Shader/ShaderDecodeMove.cs | 20 +++ .../Gal/Shader/ShaderDecodeSpecial.cs | 29 ++++ Ryujinx.Graphics/Gal/Shader/ShaderDecoder.cs | 12 +- Ryujinx.Graphics/Gal/Shader/ShaderHeader.cs | 73 ++++++++ Ryujinx.Graphics/Gal/Shader/ShaderIrInst.cs | 5 +- .../Gal/Shader/ShaderIrOperAbuf.cs | 11 +- .../Gal/Shader/ShaderOpCodeTable.cs | 4 + Ryujinx.Graphics/Gal/ShaderDumper.cs | 44 ++++- 15 files changed, 426 insertions(+), 48 deletions(-) create mode 100644 Ryujinx.Graphics/Gal/OpenGL/OGLExtension.cs create mode 100644 Ryujinx.Graphics/Gal/Shader/ShaderDecodeSpecial.cs create mode 100644 Ryujinx.Graphics/Gal/Shader/ShaderHeader.cs diff --git a/Ryujinx.Graphics/Gal/OpenGL/OGLExtension.cs b/Ryujinx.Graphics/Gal/OpenGL/OGLExtension.cs new file mode 100644 index 0000000000..69fce6d31d --- /dev/null +++ b/Ryujinx.Graphics/Gal/OpenGL/OGLExtension.cs @@ -0,0 +1,43 @@ +using OpenTK.Graphics.OpenGL; + +namespace Ryujinx.Graphics.Gal.OpenGL +{ + static class OGLExtension + { + private static bool Initialized = false; + + private static bool EnhancedLayouts; + + public static bool HasEnhancedLayouts() + { + EnsureInitialized(); + + return EnhancedLayouts; + } + + private static void EnsureInitialized() + { + if (Initialized) + { + return; + } + + EnhancedLayouts = HasExtension("GL_ARB_enhanced_layouts"); + } + + private static bool HasExtension(string Name) + { + int NumExtensions = GL.GetInteger(GetPName.NumExtensions); + + for (int Extension = 0; Extension < NumExtensions; Extension++) + { + if (GL.GetString(StringNameIndexed.Extensions, Extension) == Name) + { + return true; + } + } + + return false; + } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/OpenGL/OGLShader.cs b/Ryujinx.Graphics/Gal/OpenGL/OGLShader.cs index ad71775502..fe98aa0911 100644 --- a/Ryujinx.Graphics/Gal/OpenGL/OGLShader.cs +++ b/Ryujinx.Graphics/Gal/OpenGL/OGLShader.cs @@ -118,20 +118,20 @@ namespace Ryujinx.Graphics.Gal.OpenGL if (IsDualVp) { - ShaderDumper.Dump(Memory, Position + 0x50, Type, "a"); - ShaderDumper.Dump(Memory, PositionB + 0x50, Type, "b"); + ShaderDumper.Dump(Memory, Position, Type, "a"); + ShaderDumper.Dump(Memory, PositionB, Type, "b"); Program = Decompiler.Decompile( Memory, - Position + 0x50, - PositionB + 0x50, + Position, + PositionB, Type); } else { - ShaderDumper.Dump(Memory, Position + 0x50, Type); + ShaderDumper.Dump(Memory, Position, Type); - Program = Decompiler.Decompile(Memory, Position + 0x50, Type); + Program = Decompiler.Decompile(Memory, Position, Type); } return new ShaderStage( @@ -198,6 +198,16 @@ namespace Ryujinx.Graphics.Gal.OpenGL private void Bind(ShaderStage Stage) { + if (Stage.Type == GalShaderType.Geometry) + { + //Enhanced layouts are required for Geometry shaders + //skip this stage if current driver has no ARB_enhanced_layouts + if (!OGLExtension.HasEnhancedLayouts()) + { + return; + } + } + switch (Stage.Type) { case GalShaderType.Vertex: Current.Vertex = Stage; break; diff --git a/Ryujinx.Graphics/Gal/Shader/GlslDecl.cs b/Ryujinx.Graphics/Gal/Shader/GlslDecl.cs index d3284f9f55..7688545c02 100644 --- a/Ryujinx.Graphics/Gal/Shader/GlslDecl.cs +++ b/Ryujinx.Graphics/Gal/Shader/GlslDecl.cs @@ -4,13 +4,13 @@ namespace Ryujinx.Graphics.Gal.Shader { class GlslDecl { + public const int LayerAttr = 0x064; public const int TessCoordAttrX = 0x2f0; public const int TessCoordAttrY = 0x2f4; public const int TessCoordAttrZ = 0x2f8; public const int InstanceIdAttr = 0x2f8; public const int VertexIdAttr = 0x2fc; public const int FaceAttr = 0x3fc; - public const int GlPositionWAttr = 0x7c; public const int MaxUboSize = 1024; @@ -210,7 +210,8 @@ namespace Ryujinx.Graphics.Gal.Shader //This is a built-in input variable. if (Abuf.Offs == VertexIdAttr || Abuf.Offs == InstanceIdAttr || - Abuf.Offs == FaceAttr) + Abuf.Offs == FaceAttr || + Abuf.Offs == LayerAttr) { break; } @@ -254,6 +255,8 @@ namespace Ryujinx.Graphics.Gal.Shader m_Attributes.Add(Index, DeclInfo); } + + Traverse(Abuf, Abuf.Vertex); break; } diff --git a/Ryujinx.Graphics/Gal/Shader/GlslDecompiler.cs b/Ryujinx.Graphics/Gal/Shader/GlslDecompiler.cs index 575fb72f9a..a338f40413 100644 --- a/Ryujinx.Graphics/Gal/Shader/GlslDecompiler.cs +++ b/Ryujinx.Graphics/Gal/Shader/GlslDecompiler.cs @@ -21,10 +21,14 @@ namespace Ryujinx.Graphics.Gal.Shader private const string IdentationStr = " "; + private const int MaxVertexInput = 3; + private static string[] ElemTypes = new string[] { "float", "vec2", "vec3", "vec4" }; private GlslDecl Decl; + private ShaderHeader Header, HeaderB; + private ShaderIrBlock[] Blocks, BlocksB; private StringBuilder SB; @@ -50,6 +54,7 @@ namespace Ryujinx.Graphics.Gal.Shader { ShaderIrInst.Cle, GetCleExpr }, { ShaderIrInst.Clt, GetCltExpr }, { ShaderIrInst.Cne, GetCneExpr }, + { ShaderIrInst.Cut, GetCutExpr }, { ShaderIrInst.Exit, GetExitExpr }, { ShaderIrInst.Fabs, GetAbsExpr }, { ShaderIrInst.Fadd, GetAddExpr }, @@ -110,6 +115,9 @@ namespace Ryujinx.Graphics.Gal.Shader long VpBPosition, GalShaderType ShaderType) { + Header = new ShaderHeader(Memory, VpAPosition); + HeaderB = new ShaderHeader(Memory, VpBPosition); + Blocks = ShaderDecoder.Decode(Memory, VpAPosition); BlocksB = ShaderDecoder.Decode(Memory, VpBPosition); @@ -123,6 +131,9 @@ namespace Ryujinx.Graphics.Gal.Shader public GlslProgram Decompile(IGalMemory Memory, long Position, GalShaderType ShaderType) { + Header = new ShaderHeader(Memory, Position); + HeaderB = null; + Blocks = ShaderDecoder.Decode(Memory, Position); BlocksB = null; @@ -137,6 +148,7 @@ namespace Ryujinx.Graphics.Gal.Shader SB.AppendLine("#version 410 core"); + PrintDeclHeader(); PrintDeclTextures(); PrintDeclUniforms(); PrintDeclAttributes(); @@ -170,6 +182,37 @@ namespace Ryujinx.Graphics.Gal.Shader Decl.Uniforms.Values); } + private void PrintDeclHeader() + { + if (Decl.ShaderType == GalShaderType.Geometry) + { + int MaxVertices = Header.MaxOutputVertexCount; + + string OutputTopology; + + switch (Header.OutputTopology) + { + case ShaderHeader.PointList: OutputTopology = "points"; break; + case ShaderHeader.LineStrip: OutputTopology = "line_strip"; break; + case ShaderHeader.TriangleStrip: OutputTopology = "triangle_strip"; break; + + default: throw new InvalidOperationException(); + } + + SB.AppendLine("#extension GL_ARB_enhanced_layouts : require"); + + SB.AppendLine(); + + SB.AppendLine("// Stubbed. Maxwell geometry shaders don't inform input geometry type"); + + SB.AppendLine("layout(triangles) in;" + Environment.NewLine); + + SB.AppendLine($"layout({OutputTopology}, max_vertices = {MaxVertices}) out;"); + + SB.AppendLine(); + } + } + private void PrintDeclTextures() { PrintDecls(Decl.Textures, "uniform sampler2D"); @@ -201,7 +244,9 @@ namespace Ryujinx.Graphics.Gal.Shader private void PrintDeclAttributes() { - PrintDecls(Decl.Attributes); + string GeometryArray = (Decl.ShaderType == GalShaderType.Geometry) ? "[" + MaxVertexInput + "]" : ""; + + PrintDecls(Decl.Attributes, Suffix: GeometryArray); } private void PrintDeclInAttributes() @@ -211,7 +256,27 @@ namespace Ryujinx.Graphics.Gal.Shader SB.AppendLine("layout (location = " + GlslDecl.PositionOutAttrLocation + ") in vec4 " + GlslDecl.PositionOutAttrName + ";"); } - PrintDeclAttributes(Decl.InAttributes.Values, "in"); + if (Decl.ShaderType == GalShaderType.Geometry) + { + if (Decl.InAttributes.Count > 0) + { + SB.AppendLine("in Vertex {"); + + foreach (ShaderDeclInfo DeclInfo in Decl.InAttributes.Values.OrderBy(DeclKeySelector)) + { + if (DeclInfo.Index >= 0) + { + SB.AppendLine(IdentationStr + "layout (location = " + DeclInfo.Index + ") " + GetDecl(DeclInfo) + "; "); + } + } + + SB.AppendLine("} block_in[];" + Environment.NewLine); + } + } + else + { + PrintDeclAttributes(Decl.InAttributes.Values, "in"); + } } private void PrintDeclOutAttributes() @@ -254,7 +319,7 @@ namespace Ryujinx.Graphics.Gal.Shader PrintDecls(Decl.Preds, "bool"); } - private void PrintDecls(IReadOnlyDictionary Dict, string CustomType = null) + private void PrintDecls(IReadOnlyDictionary Dict, string CustomType = null, string Suffix = "") { foreach (ShaderDeclInfo DeclInfo in Dict.Values.OrderBy(DeclKeySelector)) { @@ -262,15 +327,15 @@ namespace Ryujinx.Graphics.Gal.Shader if (CustomType != null) { - Name = CustomType + " " + DeclInfo.Name + ";"; + Name = CustomType + " " + DeclInfo.Name + Suffix + ";"; } else if (DeclInfo.Name == GlslDecl.FragmentOutputName) { - Name = "layout (location = 0) out " + GetDecl(DeclInfo) + ";" + Environment.NewLine; + Name = "layout (location = 0) out " + GetDecl(DeclInfo) + Suffix + ";" + Environment.NewLine; } else { - Name = GetDecl(DeclInfo) + ";"; + Name = GetDecl(DeclInfo) + Suffix + ";"; } SB.AppendLine(Name); @@ -307,7 +372,21 @@ namespace Ryujinx.Graphics.Gal.Shader string Swizzle = ".xyzw".Substring(0, DeclInfo.Size + 1); - SB.AppendLine(IdentationStr + Attr.Name + Swizzle + " = " + DeclInfo.Name + ";"); + if (Decl.ShaderType == GalShaderType.Geometry) + { + for (int Vertex = 0; Vertex < MaxVertexInput; Vertex++) + { + string Dst = Attr.Name + "[" + Vertex + "]" + Swizzle; + + string Src = "block_in[" + Vertex + "]." + DeclInfo.Name; + + SB.AppendLine(IdentationStr + Dst + " = " + Src + ";"); + } + } + else + { + SB.AppendLine(IdentationStr + Attr.Name + Swizzle + " = " + DeclInfo.Name + ";"); + } } if (BlocksB != null) @@ -320,6 +399,16 @@ namespace Ryujinx.Graphics.Gal.Shader SB.AppendLine(IdentationStr + GlslDecl.ProgramName + "();"); } + if (Decl.ShaderType != GalShaderType.Geometry) + { + PrintAttrToOutput(); + } + + SB.AppendLine("}"); + } + + private void PrintAttrToOutput(string Identation = IdentationStr) + { foreach (KeyValuePair KV in Decl.OutAttributes) { if (!Decl.Attributes.TryGetValue(KV.Key, out ShaderDeclInfo Attr)) @@ -331,21 +420,26 @@ namespace Ryujinx.Graphics.Gal.Shader string Swizzle = ".xyzw".Substring(0, DeclInfo.Size + 1); - SB.AppendLine(IdentationStr + DeclInfo.Name + " = " + Attr.Name + Swizzle + ";"); + string Name = Attr.Name; + + if (Decl.ShaderType == GalShaderType.Geometry) + { + Name += "[0]"; + } + + SB.AppendLine(Identation + DeclInfo.Name + " = " + Name + Swizzle + ";"); } if (Decl.ShaderType == GalShaderType.Vertex) { - SB.AppendLine(IdentationStr + "gl_Position.xy *= " + GlslDecl.FlipUniformName + ";"); + SB.AppendLine(Identation + "gl_Position.xy *= " + GlslDecl.FlipUniformName + ";"); } if (Decl.ShaderType != GalShaderType.Fragment) { - SB.AppendLine(IdentationStr + GlslDecl.PositionOutAttrName + " = gl_Position;"); - SB.AppendLine(IdentationStr + GlslDecl.PositionOutAttrName + ".w = 1;"); + SB.AppendLine(Identation + GlslDecl.PositionOutAttrName + " = gl_Position;"); + SB.AppendLine(Identation + GlslDecl.PositionOutAttrName + ".w = 1;"); } - - SB.AppendLine("}"); } private void PrintBlockScope( @@ -484,11 +578,17 @@ namespace Ryujinx.Graphics.Gal.Shader { SB.AppendLine(Identation + "continue;"); } - - continue; } + else if (Op.Inst == ShaderIrInst.Emit) + { + PrintAttrToOutput(Identation); - SB.AppendLine(Identation + GetSrcExpr(Op, true) + ";"); + SB.AppendLine(Identation + "EmitVertex();"); + } + else + { + SB.AppendLine(Identation + GetSrcExpr(Op, true) + ";"); + } } else if (Node is ShaderIrCmnt Cmnt) { @@ -634,6 +734,14 @@ namespace Ryujinx.Graphics.Gal.Shader private string GetOutAbufName(ShaderIrOperAbuf Abuf) { + if (Decl.ShaderType == GalShaderType.Geometry) + { + switch (Abuf.Offs) + { + case GlslDecl.LayerAttr: return "gl_Layer"; + } + } + return GetAttrTempName(Abuf); } @@ -692,7 +800,16 @@ namespace Ryujinx.Graphics.Gal.Shader throw new InvalidOperationException(); } - return DeclInfo.Name + Swizzle; + if (Decl.ShaderType == GalShaderType.Geometry) + { + string Vertex = "floatBitsToInt(" + GetSrcExpr(Abuf.Vertex) + ")"; + + return DeclInfo.Name + "[" + Vertex + "]" + Swizzle; + } + else + { + return DeclInfo.Name + Swizzle; + } } private string GetName(ShaderIrOperGpr Gpr) @@ -805,6 +922,8 @@ namespace Ryujinx.Graphics.Gal.Shader private string GetCneExpr(ShaderIrOp Op) => GetBinaryExpr(Op, "!="); + private string GetCutExpr(ShaderIrOp Op) => "EndPrimitive()"; + private string GetCneuExpr(ShaderIrOp Op) => GetBinaryExprWithNaN(Op, "!="); private string GetCnumExpr(ShaderIrOp Op) => GetUnaryCall(Op, "!isnan"); @@ -1104,8 +1223,9 @@ namespace Ryujinx.Graphics.Gal.Shader switch (Node) { case ShaderIrOperAbuf Abuf: - return Abuf.Offs == GlslDecl.VertexIdAttr || + return Abuf.Offs == GlslDecl.LayerAttr || Abuf.Offs == GlslDecl.InstanceIdAttr || + Abuf.Offs == GlslDecl.VertexIdAttr || Abuf.Offs == GlslDecl.FaceAttr ? OperType.I32 : OperType.F32; diff --git a/Ryujinx.Graphics/Gal/Shader/ShaderDecodeAlu.cs b/Ryujinx.Graphics/Gal/Shader/ShaderDecodeAlu.cs index a44073513c..00f072f192 100644 --- a/Ryujinx.Graphics/Gal/Shader/ShaderDecodeAlu.cs +++ b/Ryujinx.Graphics/Gal/Shader/ShaderDecodeAlu.cs @@ -442,6 +442,41 @@ namespace Ryujinx.Graphics.Gal.Shader return Signed ? ShaderIrInst.Asr : ShaderIrInst.Lsr; } + public static void Vmad(ShaderIrBlock Block, long OpCode) + { + ShaderIrNode OperA = GetOperGpr8(OpCode); + + ShaderIrNode OperB; + + if (((OpCode >> 50) & 1) != 0) + { + OperB = GetOperGpr20(OpCode); + } + else + { + OperB = GetOperImm19_20(OpCode); + } + + ShaderIrOperGpr OperC = GetOperGpr39(OpCode); + + ShaderIrNode Tmp = new ShaderIrOp(ShaderIrInst.Mul, OperA, OperB); + + ShaderIrNode Final = new ShaderIrOp(ShaderIrInst.Add, Tmp, OperC); + + int Shr = (int)((OpCode >> 51) & 3); + + if (Shr != 0) + { + int Shift = (Shr == 2) ? 15 : 7; + + Final = new ShaderIrOp(ShaderIrInst.Lsr, Final, new ShaderIrOperImm(Shift)); + } + + Block.AddNode(new ShaderIrCmnt("Stubbed. Instruction is reduced to a * b + c")); + + Block.AddNode(GetPredNode(new ShaderIrAsg(GetOperGpr0(OpCode), Final), OpCode)); + } + public static void Xmad_CR(ShaderIrBlock Block, long OpCode) { EmitXmad(Block, OpCode, ShaderOper.CR); @@ -819,6 +854,8 @@ namespace Ryujinx.Graphics.Gal.Shader OperA = GetAluFabsFneg(OperA, AbsA, NegA); + Block.AddNode(new ShaderIrCmnt("Stubbed.")); + Block.AddNode(GetPredNode(new ShaderIrAsg(GetOperGpr0(OpCode), OperA), OpCode)); } diff --git a/Ryujinx.Graphics/Gal/Shader/ShaderDecodeHelper.cs b/Ryujinx.Graphics/Gal/Shader/ShaderDecodeHelper.cs index 1f1b158ef8..7d7b2f6c6d 100644 --- a/Ryujinx.Graphics/Gal/Shader/ShaderDecodeHelper.cs +++ b/Ryujinx.Graphics/Gal/Shader/ShaderDecodeHelper.cs @@ -7,14 +7,15 @@ namespace Ryujinx.Graphics.Gal.Shader public static ShaderIrOperAbuf[] GetOperAbuf20(long OpCode) { int Abuf = (int)(OpCode >> 20) & 0x3ff; - int Reg = (int)(OpCode >> 39) & 0xff; int Size = (int)(OpCode >> 47) & 3; + ShaderIrOperGpr Vertex = GetOperGpr39(OpCode); + ShaderIrOperAbuf[] Opers = new ShaderIrOperAbuf[Size + 1]; for (int Index = 0; Index <= Size; Index++) { - Opers[Index] = new ShaderIrOperAbuf(Abuf + Index * 4, Reg); + Opers[Index] = new ShaderIrOperAbuf(Abuf + Index * 4, Vertex); } return Opers; @@ -23,9 +24,8 @@ namespace Ryujinx.Graphics.Gal.Shader public static ShaderIrOperAbuf GetOperAbuf28(long OpCode) { int Abuf = (int)(OpCode >> 28) & 0x3ff; - int Reg = (int)(OpCode >> 39) & 0xff; - return new ShaderIrOperAbuf(Abuf, Reg); + return new ShaderIrOperAbuf(Abuf, GetOperGpr39(OpCode)); } public static ShaderIrOperCbuf GetOperCbuf34(long OpCode) diff --git a/Ryujinx.Graphics/Gal/Shader/ShaderDecodeMem.cs b/Ryujinx.Graphics/Gal/Shader/ShaderDecodeMem.cs index 083b0c63a1..aea7e744dc 100644 --- a/Ryujinx.Graphics/Gal/Shader/ShaderDecodeMem.cs +++ b/Ryujinx.Graphics/Gal/Shader/ShaderDecodeMem.cs @@ -35,6 +35,9 @@ namespace Ryujinx.Graphics.Gal.Shader { ShaderIrNode[] Opers = GetOperAbuf20(OpCode); + //Used by GS + ShaderIrOperGpr Vertex = GetOperGpr39(OpCode); + int Index = 0; foreach (ShaderIrNode OperA in Opers) diff --git a/Ryujinx.Graphics/Gal/Shader/ShaderDecodeMove.cs b/Ryujinx.Graphics/Gal/Shader/ShaderDecodeMove.cs index 4c9e59cf0c..c6b71fb01a 100644 --- a/Ryujinx.Graphics/Gal/Shader/ShaderDecodeMove.cs +++ b/Ryujinx.Graphics/Gal/Shader/ShaderDecodeMove.cs @@ -85,6 +85,16 @@ namespace Ryujinx.Graphics.Gal.Shader EmitI2i(Block, OpCode, ShaderOper.RR); } + public static void Isberd(ShaderIrBlock Block, long OpCode) + { + //This instruction seems to be used to translate from an address to a vertex index in a GS + //Stub it as such + + Block.AddNode(new ShaderIrCmnt("Stubbed.")); + + Block.AddNode(GetPredNode(new ShaderIrAsg(GetOperGpr0(OpCode), GetOperGpr8(OpCode)), OpCode)); + } + public static void Mov_C(ShaderIrBlock Block, long OpCode) { ShaderIrOperCbuf Cbuf = GetOperCbuf34(OpCode); @@ -128,6 +138,16 @@ namespace Ryujinx.Graphics.Gal.Shader EmitSel(Block, OpCode, ShaderOper.RR); } + public static void Mov_S(ShaderIrBlock Block, long OpCode) + { + Block.AddNode(new ShaderIrCmnt("Stubbed.")); + + //Zero is used as a special number to get a valid "0 * 0 + VertexIndex" in a GS + ShaderIrNode Source = new ShaderIrOperImm(0); + + Block.AddNode(GetPredNode(new ShaderIrAsg(GetOperGpr0(OpCode), Source), OpCode)); + } + private static void EmitF2f(ShaderIrBlock Block, long OpCode, ShaderOper Oper) { bool NegA = ((OpCode >> 45) & 1) != 0; diff --git a/Ryujinx.Graphics/Gal/Shader/ShaderDecodeSpecial.cs b/Ryujinx.Graphics/Gal/Shader/ShaderDecodeSpecial.cs new file mode 100644 index 0000000000..591631ff99 --- /dev/null +++ b/Ryujinx.Graphics/Gal/Shader/ShaderDecodeSpecial.cs @@ -0,0 +1,29 @@ +using System; + +using static Ryujinx.Graphics.Gal.Shader.ShaderDecodeHelper; + +namespace Ryujinx.Graphics.Gal.Shader +{ + static partial class ShaderDecode + { + public static void Out_R(ShaderIrBlock Block, long OpCode) + { + //TODO: Those registers have to be used for something + ShaderIrOperGpr Gpr0 = GetOperGpr0(OpCode); + ShaderIrOperGpr Gpr8 = GetOperGpr8(OpCode); + ShaderIrOperGpr Gpr20 = GetOperGpr20(OpCode); + + int Type = (int)((OpCode >> 39) & 3); + + if ((Type & 1) != 0) + { + Block.AddNode(GetPredNode(new ShaderIrOp(ShaderIrInst.Emit), OpCode)); + } + + if ((Type & 2) != 0) + { + Block.AddNode(GetPredNode(new ShaderIrOp(ShaderIrInst.Cut), OpCode)); + } + } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/Shader/ShaderDecoder.cs b/Ryujinx.Graphics/Gal/Shader/ShaderDecoder.cs index 85522ff950..98f371b573 100644 --- a/Ryujinx.Graphics/Gal/Shader/ShaderDecoder.cs +++ b/Ryujinx.Graphics/Gal/Shader/ShaderDecoder.cs @@ -4,6 +4,8 @@ namespace Ryujinx.Graphics.Gal.Shader { static class ShaderDecoder { + private const long HeaderSize = 0x50; + private const bool AddDbgComments = true; public static ShaderIrBlock[] Decode(IGalMemory Memory, long Start) @@ -32,13 +34,13 @@ namespace Ryujinx.Graphics.Gal.Shader return Output; } - ShaderIrBlock Entry = Enqueue(Start); + ShaderIrBlock Entry = Enqueue(Start + HeaderSize); while (Blocks.Count > 0) { ShaderIrBlock Current = Blocks.Dequeue(); - FillBlock(Memory, Current); + FillBlock(Memory, Current, Start + HeaderSize); //Set child blocks. "Branch" is the block the branch instruction //points to (when taken), "Next" is the block at the next address, @@ -122,14 +124,14 @@ namespace Ryujinx.Graphics.Gal.Shader return Graph; } - private static void FillBlock(IGalMemory Memory, ShaderIrBlock Block) + private static void FillBlock(IGalMemory Memory, ShaderIrBlock Block, long Beginning) { long Position = Block.Position; do { //Ignore scheduling instructions, which are written every 32 bytes. - if ((Position & 0x1f) == 0) + if (((Position - Beginning) & 0x1f) == 0) { Position += 8; @@ -147,7 +149,7 @@ namespace Ryujinx.Graphics.Gal.Shader if (AddDbgComments) { - string DbgOpCode = $"0x{(Position - 8):x16}: 0x{OpCode:x16} "; + string DbgOpCode = $"0x{(Position - Beginning - 8):x16}: 0x{OpCode:x16} "; DbgOpCode += (Decode?.Method.Name ?? "???"); diff --git a/Ryujinx.Graphics/Gal/Shader/ShaderHeader.cs b/Ryujinx.Graphics/Gal/Shader/ShaderHeader.cs new file mode 100644 index 0000000000..8e5057ed9e --- /dev/null +++ b/Ryujinx.Graphics/Gal/Shader/ShaderHeader.cs @@ -0,0 +1,73 @@ +namespace Ryujinx.Graphics.Gal.Shader +{ + class ShaderHeader + { + public const int PointList = 1; + public const int LineStrip = 6; + public const int TriangleStrip = 7; + + public int SphType { get; private set; } + public int Version { get; private set; } + public int ShaderType { get; private set; } + public bool MrtEnable { get; private set; } + public bool KillsPixels { get; private set; } + public bool DoesGlobalStore { get; private set; } + public int SassVersion { get; private set; } + public bool DoesLoadOrStore { get; private set; } + public bool DoesFp64 { get; private set; } + public int StreamOutMask { get; private set; } + + public int ShaderLocalMemoryLowSize { get; private set; } + public int PerPatchAttributeCount { get; private set; } + + public int ShaderLocalMemoryHighSize { get; private set; } + public int ThreadsPerInputPrimitive { get; private set; } + + public int ShaderLocalMemoryCrsSize { get; private set; } + public int OutputTopology { get; private set; } + + public int MaxOutputVertexCount { get; private set; } + public int StoreReqStart { get; private set; } + public int StoreReqEnd { get; private set; } + + public ShaderHeader(IGalMemory Memory, long Position) + { + uint CommonWord0 = (uint)Memory.ReadInt32(Position + 0); + uint CommonWord1 = (uint)Memory.ReadInt32(Position + 4); + uint CommonWord2 = (uint)Memory.ReadInt32(Position + 8); + uint CommonWord3 = (uint)Memory.ReadInt32(Position + 12); + uint CommonWord4 = (uint)Memory.ReadInt32(Position + 16); + + SphType = ReadBits(CommonWord0, 0, 5); + Version = ReadBits(CommonWord0, 5, 5); + ShaderType = ReadBits(CommonWord0, 10, 4); + MrtEnable = ReadBits(CommonWord0, 14, 1) != 0; + KillsPixels = ReadBits(CommonWord0, 15, 1) != 0; + DoesGlobalStore = ReadBits(CommonWord0, 16, 1) != 0; + SassVersion = ReadBits(CommonWord0, 17, 4); + DoesLoadOrStore = ReadBits(CommonWord0, 26, 1) != 0; + DoesFp64 = ReadBits(CommonWord0, 27, 1) != 0; + StreamOutMask = ReadBits(CommonWord0, 28, 4); + + ShaderLocalMemoryLowSize = ReadBits(CommonWord1, 0, 24); + PerPatchAttributeCount = ReadBits(CommonWord1, 24, 8); + + ShaderLocalMemoryHighSize = ReadBits(CommonWord2, 0, 24); + ThreadsPerInputPrimitive = ReadBits(CommonWord2, 24, 8); + + ShaderLocalMemoryCrsSize = ReadBits(CommonWord3, 0, 24); + OutputTopology = ReadBits(CommonWord3, 24, 4); + + MaxOutputVertexCount = ReadBits(CommonWord4, 0, 12); + StoreReqStart = ReadBits(CommonWord4, 12, 8); + StoreReqEnd = ReadBits(CommonWord4, 24, 8); + } + + private static int ReadBits(uint Word, int Offset, int BitWidth) + { + uint Mask = (1u << BitWidth) - 1u; + + return (int)((Word >> Offset) & Mask); + } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/Shader/ShaderIrInst.cs b/Ryujinx.Graphics/Gal/Shader/ShaderIrInst.cs index 9841f58ff0..fd86cadb10 100644 --- a/Ryujinx.Graphics/Gal/Shader/ShaderIrInst.cs +++ b/Ryujinx.Graphics/Gal/Shader/ShaderIrInst.cs @@ -82,6 +82,9 @@ namespace Ryujinx.Graphics.Gal.Shader Bra, Exit, - Kil + Kil, + + Emit, + Cut } } \ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/Shader/ShaderIrOperAbuf.cs b/Ryujinx.Graphics/Gal/Shader/ShaderIrOperAbuf.cs index fa612de76a..f17d9c0e62 100644 --- a/Ryujinx.Graphics/Gal/Shader/ShaderIrOperAbuf.cs +++ b/Ryujinx.Graphics/Gal/Shader/ShaderIrOperAbuf.cs @@ -2,13 +2,14 @@ namespace Ryujinx.Graphics.Gal.Shader { class ShaderIrOperAbuf : ShaderIrNode { - public int Offs { get; private set; } - public int GprIndex { get; private set; } + public int Offs { get; private set; } - public ShaderIrOperAbuf(int Offs, int GprIndex) + public ShaderIrNode Vertex { get; private set; } + + public ShaderIrOperAbuf(int Offs, ShaderIrNode Vertex) { - this.Offs = Offs; - this.GprIndex = GprIndex; + this.Offs = Offs; + this.Vertex = Vertex; } } } \ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/Shader/ShaderOpCodeTable.cs b/Ryujinx.Graphics/Gal/Shader/ShaderOpCodeTable.cs index 1ac1178512..3f20dc4465 100644 --- a/Ryujinx.Graphics/Gal/Shader/ShaderOpCodeTable.cs +++ b/Ryujinx.Graphics/Gal/Shader/ShaderOpCodeTable.cs @@ -74,6 +74,7 @@ namespace Ryujinx.Graphics.Gal.Shader Set("0100110000100x", ShaderDecode.Imnmx_C); Set("0011100x00100x", ShaderDecode.Imnmx_I); Set("0101110000100x", ShaderDecode.Imnmx_R); + Set("1110111111010x", ShaderDecode.Isberd); Set("11100000xxxxxx", ShaderDecode.Ipa); Set("0100110000011x", ShaderDecode.Iscadd_C); Set("0011100x00011x", ShaderDecode.Iscadd_I); @@ -95,7 +96,9 @@ namespace Ryujinx.Graphics.Gal.Shader Set("0011100x10011x", ShaderDecode.Mov_I); Set("000000010000xx", ShaderDecode.Mov_I32); Set("0101110010011x", ShaderDecode.Mov_R); + Set("1111000011001x", ShaderDecode.Mov_S); Set("0101000010000x", ShaderDecode.Mufu); + Set("1111101111100x", ShaderDecode.Out_R); Set("0101000010010x", ShaderDecode.Psetp); Set("0100110010010x", ShaderDecode.Rro_C); Set("0011100x10010x", ShaderDecode.Rro_I); @@ -114,6 +117,7 @@ namespace Ryujinx.Graphics.Gal.Shader Set("1101111101001x", ShaderDecode.Texq); Set("1101100xxxxxxx", ShaderDecode.Texs); Set("1101101xxxxxxx", ShaderDecode.Tlds); + Set("01011111xxxxxx", ShaderDecode.Vmad); Set("0100111xxxxxxx", ShaderDecode.Xmad_CR); Set("0011011x00xxxx", ShaderDecode.Xmad_I); Set("010100010xxxxx", ShaderDecode.Xmad_RC); diff --git a/Ryujinx.Graphics/Gal/ShaderDumper.cs b/Ryujinx.Graphics/Gal/ShaderDumper.cs index 7cd56b21ef..541368e89b 100644 --- a/Ryujinx.Graphics/Gal/ShaderDumper.cs +++ b/Ryujinx.Graphics/Gal/ShaderDumper.cs @@ -18,13 +18,21 @@ namespace Ryujinx.Graphics.Gal string FileName = "Shader" + DumpIndex.ToString("d4") + "." + ShaderExtension(Type) + ExtSuffix + ".bin"; - string FilePath = Path.Combine(DumpDir(), FileName); + string FullPath = Path.Combine(FullDir(), FileName); + string CodePath = Path.Combine(CodeDir(), FileName); DumpIndex++; - using (FileStream Output = File.Create(FilePath)) - using (BinaryWriter Writer = new BinaryWriter(Output)) + using (FileStream FullFile = File.Create(FullPath)) + using (FileStream CodeFile = File.Create(CodePath)) + using (BinaryWriter FullWriter = new BinaryWriter(FullFile)) + using (BinaryWriter CodeWriter = new BinaryWriter(CodeFile)) { + for (long i = 0; i < 0x50; i += 4) + { + FullWriter.Write(Memory.ReadInt32(Position + i)); + } + long Offset = 0; ulong Instruction = 0; @@ -32,8 +40,8 @@ namespace Ryujinx.Graphics.Gal //Dump until a NOP instruction is found while ((Instruction >> 52 & 0xfff8) != 0x50b0) { - uint Word0 = (uint)Memory.ReadInt32(Position + Offset + 0); - uint Word1 = (uint)Memory.ReadInt32(Position + Offset + 4); + uint Word0 = (uint)Memory.ReadInt32(Position + 0x50 + Offset + 0); + uint Word1 = (uint)Memory.ReadInt32(Position + 0x50 + Offset + 4); Instruction = Word0 | (ulong)Word1 << 32; @@ -44,7 +52,8 @@ namespace Ryujinx.Graphics.Gal break; } - Writer.Write(Instruction); + FullWriter.Write(Instruction); + CodeWriter.Write(Instruction); Offset += 8; } @@ -52,13 +61,24 @@ namespace Ryujinx.Graphics.Gal //Align to meet nvdisasm requeriments while (Offset % 0x20 != 0) { - Writer.Write(0); + FullWriter.Write(0); + CodeWriter.Write(0); Offset += 4; } } } + private static string FullDir() + { + return CreateAndReturn(Path.Combine(DumpDir(), "Full")); + } + + private static string CodeDir() + { + return CreateAndReturn(Path.Combine(DumpDir(), "Code")); + } + private static string DumpDir() { if (string.IsNullOrEmpty(RuntimeDir)) @@ -79,6 +99,16 @@ namespace Ryujinx.Graphics.Gal return RuntimeDir; } + private static string CreateAndReturn(string Dir) + { + if (!Directory.Exists(Dir)) + { + Directory.CreateDirectory(Dir); + } + + return Dir; + } + private static string ShaderExtension(GalShaderType Type) { switch (Type) From ee064a2fb8d5e6167122477d5014beae509f92ed Mon Sep 17 00:00:00 2001 From: mailwl Date: Thu, 19 Jul 2018 19:45:50 +0300 Subject: [PATCH 23/33] .gitignore: ignore autogenerated launchSettings.json (#292) thanks to @Cyuubi --- .gitignore | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.gitignore b/.gitignore index 82d9719b59..123f46184d 100644 --- a/.gitignore +++ b/.gitignore @@ -158,3 +158,6 @@ $RECYCLE.BIN/ # Mac desktop service store files .DS_Store + +# VS Launch Settings +launchSettings.json From 8b67297711003dfac432acb3278c5a406617e662 Mon Sep 17 00:00:00 2001 From: emmauss Date: Thu, 19 Jul 2018 20:49:34 +0300 Subject: [PATCH 24/33] Added appveyor configuration file (#277) --- appveyor.yml | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 appveyor.yml diff --git a/appveyor.yml b/appveyor.yml new file mode 100644 index 0000000000..eced37d77e --- /dev/null +++ b/appveyor.yml @@ -0,0 +1,28 @@ +version: 1.0.{build} +branches: + only: + - master +image: Visual Studio 2017 +configuration: Release +build_script: +- ps: >- + dotnet --version + + dotnet publish -c Release -r win-x64 + + dotnet publish -c Release -r linux-x64 + + dotnet publish -c Release -r osx-x64 + + 7z a ryujinx-$env:APPVEYOR_BUILD_VERSION-win_x64.zip $env:APPVEYOR_BUILD_FOLDER\Ryujinx\bin\Release\netcoreapp2.1\win-x64\publish\ + + 7z a ryujinx-$env:APPVEYOR_BUILD_VERSION-linux_x64.tar $env:APPVEYOR_BUILD_FOLDER\Ryujinx\bin\Release\netcoreapp2.1\linux-x64\publish\ + + 7z a ryujinx-$env:APPVEYOR_BUILD_VERSION-linux_x64.tar.gz ryujinx-$env:APPVEYOR_BUILD_VERSION-linux_x64.tar + + 7z a ryujinx-$env:APPVEYOR_BUILD_VERSION-osx_x64.zip $env:APPVEYOR_BUILD_FOLDER\Ryujinx\bin\Release\netcoreapp2.1\osx-x64\publish\ + +artifacts: +- path: ryujinx-%APPVEYOR_BUILD_VERSION%-win_x64.zip +- path: ryujinx-%APPVEYOR_BUILD_VERSION%-linux_x64.tar.gz +- path: ryujinx-%APPVEYOR_BUILD_VERSION%-osx_x64.zip From c9fc52edb6abc014d5d5671c1634b01ace48de2f Mon Sep 17 00:00:00 2001 From: Thomas Guillemard Date: Thu, 19 Jul 2018 20:44:52 +0200 Subject: [PATCH 25/33] Fix SystemPathToSwitchPath platform issues and make sure to delete temporary NRO after sessions dispose (#293) --- Ryujinx.HLE/OsHle/Horizon.cs | 1 - Ryujinx.HLE/OsHle/Process.cs | 10 +++++----- Ryujinx.HLE/VirtualFileSystem.cs | 4 ++-- 3 files changed, 7 insertions(+), 8 deletions(-) diff --git a/Ryujinx.HLE/OsHle/Horizon.cs b/Ryujinx.HLE/OsHle/Horizon.cs index 9d8a937ff2..70ae24be32 100644 --- a/Ryujinx.HLE/OsHle/Horizon.cs +++ b/Ryujinx.HLE/OsHle/Horizon.cs @@ -96,7 +96,6 @@ namespace Ryujinx.HLE.OsHle if (IsNro && (SwitchFilePath == null || !SwitchFilePath.StartsWith("sdmc:/"))) { - // TODO: avoid copying the file if we are already inside a sdmc directory string SwitchPath = $"sdmc:/switch/{Name}{Homebrew.TemporaryNroSuffix}"; string TempPath = Ns.VFs.SwitchPathToSystemPath(SwitchPath); diff --git a/Ryujinx.HLE/OsHle/Process.cs b/Ryujinx.HLE/OsHle/Process.cs index be27dcc288..c7606dc902 100644 --- a/Ryujinx.HLE/OsHle/Process.cs +++ b/Ryujinx.HLE/OsHle/Process.cs @@ -403,11 +403,6 @@ namespace Ryujinx.HLE.OsHle { if (Disposing && !Disposed) { - if (NeedsHbAbi && Executables[0].FilePath.EndsWith(Homebrew.TemporaryNroSuffix)) - { - File.Delete(Executables[0].FilePath); - } - //If there is still some thread running, disposing the objects is not //safe as the thread may try to access those resources. Instead, we set //the flag to have the Process disposed when all threads finishes. @@ -431,6 +426,11 @@ namespace Ryujinx.HLE.OsHle } } + if (NeedsHbAbi && Executables.Count > 0 && Executables[0].FilePath.EndsWith(Homebrew.TemporaryNroSuffix)) + { + File.Delete(Executables[0].FilePath); + } + INvDrvServices.UnloadProcess(this); AppletState.Dispose(); diff --git a/Ryujinx.HLE/VirtualFileSystem.cs b/Ryujinx.HLE/VirtualFileSystem.cs index 38df81f87d..df1fc9db13 100644 --- a/Ryujinx.HLE/VirtualFileSystem.cs +++ b/Ryujinx.HLE/VirtualFileSystem.cs @@ -57,11 +57,11 @@ namespace Ryujinx.HLE public string SystemPathToSwitchPath(string SystemPath) { - string BaseSystemPath = GetBasePath() + "/"; + string BaseSystemPath = GetBasePath() + Path.DirectorySeparatorChar; if (SystemPath.StartsWith(BaseSystemPath)) { string RawPath = SystemPath.Replace(BaseSystemPath, ""); - int FirstSeparatorOffset = RawPath.IndexOf('/'); + int FirstSeparatorOffset = RawPath.IndexOf(Path.DirectorySeparatorChar); if (FirstSeparatorOffset == -1) { return $"{RawPath}:/"; From 45bb24dbae7b4fb4118036aa74024605995510fd Mon Sep 17 00:00:00 2001 From: emmauss Date: Thu, 19 Jul 2018 18:53:49 +0000 Subject: [PATCH 26/33] fix extra space --- appveyor.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/appveyor.yml b/appveyor.yml index eced37d77e..5392128132 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -18,7 +18,7 @@ build_script: 7z a ryujinx-$env:APPVEYOR_BUILD_VERSION-linux_x64.tar $env:APPVEYOR_BUILD_FOLDER\Ryujinx\bin\Release\netcoreapp2.1\linux-x64\publish\ - 7z a ryujinx-$env:APPVEYOR_BUILD_VERSION-linux_x64.tar.gz ryujinx-$env:APPVEYOR_BUILD_VERSION-linux_x64.tar + 7z a ryujinx-$env:APPVEYOR_BUILD_VERSION-linux_x64.tar.gz ryujinx-$env:APPVEYOR_BUILD_VERSION-linux_x64.tar 7z a ryujinx-$env:APPVEYOR_BUILD_VERSION-osx_x64.zip $env:APPVEYOR_BUILD_FOLDER\Ryujinx\bin\Release\netcoreapp2.1\osx-x64\publish\ From 5fe0bc584b21c660e083a9cb37aa0a8be4719f95 Mon Sep 17 00:00:00 2001 From: ReinUsesLisp Date: Thu, 19 Jul 2018 16:02:51 -0300 Subject: [PATCH 27/33] Send data to OpenGL host without client-side copies (#285) * Directly send host address to buffer data * Cleanup OGLShader * Directly copy vertex and index data too * Revert shader bind "cache" * Address feedback --- ChocolArm64/Memory/AMemory.cs | 7 ++ Ryujinx.Graphics/Gal/IGalRasterizer.cs | 6 +- Ryujinx.Graphics/Gal/IGalShader.cs | 3 +- Ryujinx.Graphics/Gal/OpenGL/OGLRasterizer.cs | 16 ++-- Ryujinx.Graphics/Gal/OpenGL/OGLShader.cs | 22 ++--- .../Gal/OpenGL/OGLStreamBuffer.cs | 93 +++---------------- Ryujinx.HLE/Gpu/Engines/NvGpuEngine3d.cs | 12 +-- Ryujinx.HLE/Gpu/Memory/NvGpuVmm.cs | 6 ++ 8 files changed, 55 insertions(+), 110 deletions(-) diff --git a/ChocolArm64/Memory/AMemory.cs b/ChocolArm64/Memory/AMemory.cs index da5cf00749..054277b295 100644 --- a/ChocolArm64/Memory/AMemory.cs +++ b/ChocolArm64/Memory/AMemory.cs @@ -204,6 +204,13 @@ namespace ChocolArm64.Memory return Modified; } + public IntPtr GetHostAddress(long Position, long Size) + { + EnsureRangeIsValid(Position, Size, AMemoryPerm.Read); + + return (IntPtr)(RamPtr + (ulong)Position); + } + public sbyte ReadSByte(long Position) { return (sbyte)ReadByte(Position); diff --git a/Ryujinx.Graphics/Gal/IGalRasterizer.cs b/Ryujinx.Graphics/Gal/IGalRasterizer.cs index 0c5d37e40e..a87d36c38f 100644 --- a/Ryujinx.Graphics/Gal/IGalRasterizer.cs +++ b/Ryujinx.Graphics/Gal/IGalRasterizer.cs @@ -1,3 +1,5 @@ +using System; + namespace Ryujinx.Graphics.Gal { public interface IGalRasterizer @@ -45,9 +47,9 @@ namespace Ryujinx.Graphics.Gal void SetPrimitiveRestartIndex(uint Index); - void CreateVbo(long Key, byte[] Buffer); + void CreateVbo(long Key, int DataSize, IntPtr HostAddress); - void CreateIbo(long Key, byte[] Buffer); + void CreateIbo(long Key, int DataSize, IntPtr HostAddress); void SetVertexArray(int Stride, long VboKey, GalVertexAttrib[] Attribs); diff --git a/Ryujinx.Graphics/Gal/IGalShader.cs b/Ryujinx.Graphics/Gal/IGalShader.cs index 9adaceaf50..56235a0709 100644 --- a/Ryujinx.Graphics/Gal/IGalShader.cs +++ b/Ryujinx.Graphics/Gal/IGalShader.cs @@ -1,3 +1,4 @@ +using System; using System.Collections.Generic; namespace Ryujinx.Graphics.Gal @@ -10,7 +11,7 @@ namespace Ryujinx.Graphics.Gal IEnumerable GetTextureUsage(long Key); - void SetConstBuffer(long Key, int Cbuf, byte[] Data); + void SetConstBuffer(long Key, int Cbuf, int DataSize, IntPtr HostAddress); void EnsureTextureBinding(string UniformName, int Value); diff --git a/Ryujinx.Graphics/Gal/OpenGL/OGLRasterizer.cs b/Ryujinx.Graphics/Gal/OpenGL/OGLRasterizer.cs index 0dc56966b3..f2e5859e5e 100644 --- a/Ryujinx.Graphics/Gal/OpenGL/OGLRasterizer.cs +++ b/Ryujinx.Graphics/Gal/OpenGL/OGLRasterizer.cs @@ -211,28 +211,28 @@ namespace Ryujinx.Graphics.Gal.OpenGL GL.PrimitiveRestartIndex(Index); } - public void CreateVbo(long Key, byte[] Buffer) + public void CreateVbo(long Key, int DataSize, IntPtr HostAddress) { int Handle = GL.GenBuffer(); - VboCache.AddOrUpdate(Key, Handle, (uint)Buffer.Length); + VboCache.AddOrUpdate(Key, Handle, (uint)DataSize); - IntPtr Length = new IntPtr(Buffer.Length); + IntPtr Length = new IntPtr(DataSize); GL.BindBuffer(BufferTarget.ArrayBuffer, Handle); - GL.BufferData(BufferTarget.ArrayBuffer, Length, Buffer, BufferUsageHint.StreamDraw); + GL.BufferData(BufferTarget.ArrayBuffer, Length, HostAddress, BufferUsageHint.StreamDraw); } - public void CreateIbo(long Key, byte[] Buffer) + public void CreateIbo(long Key, int DataSize, IntPtr HostAddress) { int Handle = GL.GenBuffer(); - IboCache.AddOrUpdate(Key, Handle, (uint)Buffer.Length); + IboCache.AddOrUpdate(Key, Handle, (uint)DataSize); - IntPtr Length = new IntPtr(Buffer.Length); + IntPtr Length = new IntPtr(DataSize); GL.BindBuffer(BufferTarget.ElementArrayBuffer, Handle); - GL.BufferData(BufferTarget.ElementArrayBuffer, Length, Buffer, BufferUsageHint.StreamDraw); + GL.BufferData(BufferTarget.ElementArrayBuffer, Length, HostAddress, BufferUsageHint.StreamDraw); } public void SetVertexArray(int Stride, long VboKey, GalVertexAttrib[] Attribs) diff --git a/Ryujinx.Graphics/Gal/OpenGL/OGLShader.cs b/Ryujinx.Graphics/Gal/OpenGL/OGLShader.cs index fe98aa0911..37213d8ed1 100644 --- a/Ryujinx.Graphics/Gal/OpenGL/OGLShader.cs +++ b/Ryujinx.Graphics/Gal/OpenGL/OGLShader.cs @@ -5,6 +5,8 @@ using System.Collections.Concurrent; using System.Collections.Generic; using System.Linq; +using Buffer = System.Buffer; + namespace Ryujinx.Graphics.Gal.OpenGL { public class OGLShader : IGalShader @@ -151,7 +153,7 @@ namespace Ryujinx.Graphics.Gal.OpenGL return Enumerable.Empty(); } - public void SetConstBuffer(long Key, int Cbuf, byte[] Data) + public void SetConstBuffer(long Key, int Cbuf, int DataSize, IntPtr HostAddress) { if (Stages.TryGetValue(Key, out ShaderStage Stage)) { @@ -159,13 +161,9 @@ namespace Ryujinx.Graphics.Gal.OpenGL { OGLStreamBuffer Buffer = GetConstBuffer(Stage.Type, Cbuf); - int Size = Math.Min(Data.Length, Buffer.Size); + int Size = Math.Min(DataSize, Buffer.Size); - byte[] Destiny = Buffer.Map(Size); - - Array.Copy(Data, Destiny, Size); - - Buffer.Unmap(Size); + Buffer.SetData(Size, HostAddress); } } } @@ -278,7 +276,7 @@ namespace Ryujinx.Graphics.Gal.OpenGL { int FreeBinding = 0; - int BindUniformBlocksIfNotNull(ShaderStage Stage) + void BindUniformBlocksIfNotNull(ShaderStage Stage) { if (Stage != null) { @@ -297,8 +295,6 @@ namespace Ryujinx.Graphics.Gal.OpenGL FreeBinding++; } } - - return FreeBinding; } BindUniformBlocksIfNotNull(Current.Vertex); @@ -312,7 +308,7 @@ namespace Ryujinx.Graphics.Gal.OpenGL { int FreeBinding = 0; - int BindUniformBuffersIfNotNull(ShaderStage Stage) + void BindUniformBuffersIfNotNull(ShaderStage Stage) { if (Stage != null) { @@ -325,8 +321,6 @@ namespace Ryujinx.Graphics.Gal.OpenGL FreeBinding++; } } - - return FreeBinding; } BindUniformBuffersIfNotNull(Current.Vertex); @@ -347,7 +341,7 @@ namespace Ryujinx.Graphics.Gal.OpenGL //Allocate a maximum of 64 KiB int Size = Math.Min(GL.GetInteger(GetPName.MaxUniformBlockSize), 64 * 1024); - Buffer = OGLStreamBuffer.Create(BufferTarget.UniformBuffer, Size); + Buffer = new OGLStreamBuffer(BufferTarget.UniformBuffer, Size); ConstBuffers[StageIndex][Cbuf] = Buffer; } diff --git a/Ryujinx.Graphics/Gal/OpenGL/OGLStreamBuffer.cs b/Ryujinx.Graphics/Gal/OpenGL/OGLStreamBuffer.cs index 329c5b5df7..0d5dee93f5 100644 --- a/Ryujinx.Graphics/Gal/OpenGL/OGLStreamBuffer.cs +++ b/Ryujinx.Graphics/Gal/OpenGL/OGLStreamBuffer.cs @@ -1,9 +1,9 @@ -using System; using OpenTK.Graphics.OpenGL; +using System; namespace Ryujinx.Graphics.Gal.OpenGL { - abstract class OGLStreamBuffer : IDisposable + class OGLStreamBuffer : IDisposable { public int Handle { get; protected set; } @@ -11,53 +11,25 @@ namespace Ryujinx.Graphics.Gal.OpenGL protected BufferTarget Target { get; private set; } - private bool Mapped = false; - - public OGLStreamBuffer(BufferTarget Target, int MaxSize) + public OGLStreamBuffer(BufferTarget Target, int Size) { - Handle = 0; - Mapped = false; - this.Target = Target; - this.Size = MaxSize; + this.Size = Size; + + Handle = GL.GenBuffer(); + + GL.BindBuffer(Target, Handle); + + GL.BufferData(Target, Size, IntPtr.Zero, BufferUsageHint.StreamDraw); } - public static OGLStreamBuffer Create(BufferTarget Target, int MaxSize) + public void SetData(int Size, IntPtr HostAddress) { - //TODO: Query here for ARB_buffer_storage and use when available - return new SubDataBuffer(Target, MaxSize); + GL.BindBuffer(Target, Handle); + + GL.BufferSubData(Target, IntPtr.Zero, Size, HostAddress); } - public byte[] Map(int Size) - { - if (Handle == 0 || Mapped || Size > this.Size) - { - throw new InvalidOperationException(); - } - - byte[] Memory = InternMap(Size); - - Mapped = true; - - return Memory; - } - - public void Unmap(int UsedSize) - { - if (Handle == 0 || !Mapped) - { - throw new InvalidOperationException(); - } - - InternUnmap(UsedSize); - - Mapped = false; - } - - protected abstract byte[] InternMap(int Size); - - protected abstract void InternUnmap(int UsedSize); - public void Dispose() { Dispose(true); @@ -73,41 +45,4 @@ namespace Ryujinx.Graphics.Gal.OpenGL } } } - - class SubDataBuffer : OGLStreamBuffer - { - private byte[] Memory; - - public SubDataBuffer(BufferTarget Target, int MaxSize) - : base(Target, MaxSize) - { - Memory = new byte[MaxSize]; - - GL.GenBuffers(1, out int Handle); - - GL.BindBuffer(Target, Handle); - - GL.BufferData(Target, Size, IntPtr.Zero, BufferUsageHint.StreamDraw); - - this.Handle = Handle; - } - - protected override byte[] InternMap(int Size) - { - return Memory; - } - - protected override void InternUnmap(int UsedSize) - { - GL.BindBuffer(Target, Handle); - - unsafe - { - fixed (byte* MemoryPtr = Memory) - { - GL.BufferSubData(Target, IntPtr.Zero, UsedSize, (IntPtr)MemoryPtr); - } - } - } - } } diff --git a/Ryujinx.HLE/Gpu/Engines/NvGpuEngine3d.cs b/Ryujinx.HLE/Gpu/Engines/NvGpuEngine3d.cs index dce25a5e94..c3e7a77fcb 100644 --- a/Ryujinx.HLE/Gpu/Engines/NvGpuEngine3d.cs +++ b/Ryujinx.HLE/Gpu/Engines/NvGpuEngine3d.cs @@ -560,9 +560,9 @@ namespace Ryujinx.HLE.Gpu.Engines if (Cb.Enabled) { - byte[] Data = Vmm.ReadBytes(Cb.Position, (uint)Cb.Size); + IntPtr DataAddress = Vmm.GetHostAddress(Cb.Position, Cb.Size); - Gpu.Renderer.Shader.SetConstBuffer(BasePosition + (uint)Offset, Cbuf, Data); + Gpu.Renderer.Shader.SetConstBuffer(BasePosition + (uint)Offset, Cbuf, Cb.Size, DataAddress); } } } @@ -595,9 +595,9 @@ namespace Ryujinx.HLE.Gpu.Engines if (!IboCached || Vmm.IsRegionModified(IboKey, (uint)IbSize, NvGpuBufferType.Index)) { - byte[] Data = Vmm.ReadBytes(IndexPosition, (uint)IbSize); + IntPtr DataAddress = Vmm.GetHostAddress(IndexPosition, IbSize); - Gpu.Renderer.Rasterizer.CreateIbo(IboKey, Data); + Gpu.Renderer.Rasterizer.CreateIbo(IboKey, IbSize, DataAddress); } Gpu.Renderer.Rasterizer.SetIndexArray(IbSize, IndexFormat); @@ -659,9 +659,9 @@ namespace Ryujinx.HLE.Gpu.Engines if (!VboCached || Vmm.IsRegionModified(VboKey, VbSize, NvGpuBufferType.Vertex)) { - byte[] Data = Vmm.ReadBytes(VertexPosition, VbSize); + IntPtr DataAddress = Vmm.GetHostAddress(VertexPosition, VbSize); - Gpu.Renderer.Rasterizer.CreateVbo(VboKey, Data); + Gpu.Renderer.Rasterizer.CreateVbo(VboKey, (int)VbSize, DataAddress); } Gpu.Renderer.Rasterizer.SetVertexArray(Stride, VboKey, Attribs[Index].ToArray()); diff --git a/Ryujinx.HLE/Gpu/Memory/NvGpuVmm.cs b/Ryujinx.HLE/Gpu/Memory/NvGpuVmm.cs index 0c81dd1508..7b23e49fac 100644 --- a/Ryujinx.HLE/Gpu/Memory/NvGpuVmm.cs +++ b/Ryujinx.HLE/Gpu/Memory/NvGpuVmm.cs @@ -1,5 +1,6 @@ using ChocolArm64.Memory; using Ryujinx.Graphics.Gal; +using System; using System.Collections.Concurrent; namespace Ryujinx.HLE.Gpu.Memory @@ -279,6 +280,11 @@ namespace Ryujinx.HLE.Gpu.Memory return Cache.IsRegionModified(Memory, BufferType, PA, Size); } + public IntPtr GetHostAddress(long Position, long Size) + { + return Memory.GetHostAddress(GetPhysicalAddress(Position), Size); + } + public byte ReadByte(long Position) { Position = GetPhysicalAddress(Position); From 241b46540d4e1ab909cc8b1b494189c4800ebcfd Mon Sep 17 00:00:00 2001 From: Rygnus Date: Fri, 20 Jul 2018 00:27:50 +0100 Subject: [PATCH 28/33] Stub AppletOE UnlockExit (#279) * Implement AppletOE UnlockExit --- .../OsHle/Services/Am/ISelfController.cs | 20 ++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/Ryujinx.HLE/OsHle/Services/Am/ISelfController.cs b/Ryujinx.HLE/OsHle/Services/Am/ISelfController.cs index ee0fb91569..10d69b9b08 100644 --- a/Ryujinx.HLE/OsHle/Services/Am/ISelfController.cs +++ b/Ryujinx.HLE/OsHle/Services/Am/ISelfController.cs @@ -17,7 +17,9 @@ namespace Ryujinx.HLE.OsHle.Services.Am { m_Commands = new Dictionary() { + { 0, Exit }, { 1, LockExit }, + { 2, UnlockExit }, { 9, GetLibraryAppletLaunchableEvent }, { 10, SetScreenShotPermission }, { 11, SetOperationModeChangedNotification }, @@ -31,8 +33,24 @@ namespace Ryujinx.HLE.OsHle.Services.Am LaunchableEvent = new KEvent(); } + public long Exit(ServiceCtx Context) + { + Context.Ns.Log.PrintStub(LogClass.ServiceAm, "Stubbed."); + + return 0; + } + public long LockExit(ServiceCtx Context) { + Context.Ns.Log.PrintStub(LogClass.ServiceAm, "Stubbed."); + + return 0; + } + + public long UnlockExit(ServiceCtx Context) + { + Context.Ns.Log.PrintStub(LogClass.ServiceAm, "Stubbed."); + return 0; } @@ -114,4 +132,4 @@ namespace Ryujinx.HLE.OsHle.Services.Am return 0; } } -} \ No newline at end of file +} From ed075ae3cdc99163aa910b628bcd9de03dae03dd Mon Sep 17 00:00:00 2001 From: Ac_K Date: Sun, 22 Jul 2018 19:38:29 +0000 Subject: [PATCH 29/33] Update ITimeZoneService.cs Fix typo --- Ryujinx.HLE/OsHle/Services/Time/ITimeZoneService.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Ryujinx.HLE/OsHle/Services/Time/ITimeZoneService.cs b/Ryujinx.HLE/OsHle/Services/Time/ITimeZoneService.cs index a2206a126e..38440eef90 100644 --- a/Ryujinx.HLE/OsHle/Services/Time/ITimeZoneService.cs +++ b/Ryujinx.HLE/OsHle/Services/Time/ITimeZoneService.cs @@ -117,7 +117,7 @@ namespace Ryujinx.HLE.OsHle.Services.Time TimeZoneInfo Info = TimeZoneInfo.FindSystemTimeZoneById(TzID); byte[] TzData = Encoding.ASCII.GetBytes(Info.Id); - // FIXME: This is not in ANY cases accurate, but the games don't about the content of the buffer, they only pass it. + // FIXME: This is not in ANY cases accurate, but the games don't care about the content of the buffer, they only pass it. // TODO: Reverse the TZif2 conversion in PCV to make this match with real hardware. Context.Memory.WriteBytes(BufferPosition, TzData); } From ed29982f9b0414d42fd75acdcf2182d14174fe90 Mon Sep 17 00:00:00 2001 From: greggameplayer <33609333+greggameplayer@users.noreply.github.com> Date: Mon, 23 Jul 2018 16:20:16 +0200 Subject: [PATCH 30/33] Link BCAT:U & BCAT:A & BCAT:M & BCAT:S (#257) * Link BCAT:U & BCAT:A & BCAT:M & BCAT:S * delete unneeded using * delete unneeded spaces * delete unneeded using * Add comment (1/2) * Add comment (2/2) * delete unneeded using --- .../OsHle/Services/Bcat/IBcatService.cs | 21 ++++++++++ .../Bcat/IDeliveryCacheStorageService.cs | 21 ++++++++++ .../OsHle/Services/Bcat/IServiceCreator.cs | 39 +++++++++++++++++++ Ryujinx.HLE/OsHle/Services/ServiceFactory.cs | 17 +++++++- 4 files changed, 96 insertions(+), 2 deletions(-) create mode 100644 Ryujinx.HLE/OsHle/Services/Bcat/IBcatService.cs create mode 100644 Ryujinx.HLE/OsHle/Services/Bcat/IDeliveryCacheStorageService.cs create mode 100644 Ryujinx.HLE/OsHle/Services/Bcat/IServiceCreator.cs diff --git a/Ryujinx.HLE/OsHle/Services/Bcat/IBcatService.cs b/Ryujinx.HLE/OsHle/Services/Bcat/IBcatService.cs new file mode 100644 index 0000000000..b7754d6b63 --- /dev/null +++ b/Ryujinx.HLE/OsHle/Services/Bcat/IBcatService.cs @@ -0,0 +1,21 @@ +using Ryujinx.HLE.OsHle.Ipc; +using System.Collections.Generic; + +namespace Ryujinx.HLE.OsHle.Services.Bcat +{ + class IBcatService : IpcService + { + private Dictionary m_Commands; + + public override IReadOnlyDictionary Commands => m_Commands; + + public IBcatService() + { + m_Commands = new Dictionary() + { + //... + }; + } + + } +} diff --git a/Ryujinx.HLE/OsHle/Services/Bcat/IDeliveryCacheStorageService.cs b/Ryujinx.HLE/OsHle/Services/Bcat/IDeliveryCacheStorageService.cs new file mode 100644 index 0000000000..0b84d809e4 --- /dev/null +++ b/Ryujinx.HLE/OsHle/Services/Bcat/IDeliveryCacheStorageService.cs @@ -0,0 +1,21 @@ +using Ryujinx.HLE.OsHle.Ipc; +using System.Collections.Generic; + +namespace Ryujinx.HLE.OsHle.Services.Bcat +{ + class IDeliveryCacheStorageService : IpcService + { + private Dictionary m_Commands; + + public override IReadOnlyDictionary Commands => m_Commands; + + public IDeliveryCacheStorageService() + { + m_Commands = new Dictionary() + { + //... + }; + } + + } +} diff --git a/Ryujinx.HLE/OsHle/Services/Bcat/IServiceCreator.cs b/Ryujinx.HLE/OsHle/Services/Bcat/IServiceCreator.cs new file mode 100644 index 0000000000..cc1fc6f8c8 --- /dev/null +++ b/Ryujinx.HLE/OsHle/Services/Bcat/IServiceCreator.cs @@ -0,0 +1,39 @@ +using Ryujinx.HLE.OsHle.Ipc; +using System.Collections.Generic; + +namespace Ryujinx.HLE.OsHle.Services.Bcat +{ + class IServiceCreator : IpcService + { + private Dictionary m_Commands; + + public override IReadOnlyDictionary Commands => m_Commands; + + public IServiceCreator() + { + m_Commands = new Dictionary() + { + { 0, CreateBcatService }, + { 1, CreateDeliveryCacheStorageService } + }; + } + + public long CreateBcatService(ServiceCtx Context) + { + long Id = Context.RequestData.ReadInt64(); + + MakeObject(Context, new IBcatService()); + + return 0; + } + + public long CreateDeliveryCacheStorageService(ServiceCtx Context) + { + long Id = Context.RequestData.ReadInt64(); + + MakeObject(Context, new IDeliveryCacheStorageService()); + + return 0; + } + } +} diff --git a/Ryujinx.HLE/OsHle/Services/ServiceFactory.cs b/Ryujinx.HLE/OsHle/Services/ServiceFactory.cs index b69fc9f884..712698b9af 100644 --- a/Ryujinx.HLE/OsHle/Services/ServiceFactory.cs +++ b/Ryujinx.HLE/OsHle/Services/ServiceFactory.cs @@ -2,6 +2,7 @@ using Ryujinx.HLE.OsHle.Services.Acc; using Ryujinx.HLE.OsHle.Services.Am; using Ryujinx.HLE.OsHle.Services.Apm; using Ryujinx.HLE.OsHle.Services.Aud; +using Ryujinx.HLE.OsHle.Services.Bcat; using Ryujinx.HLE.OsHle.Services.Bsd; using Ryujinx.HLE.OsHle.Services.Caps; using Ryujinx.HLE.OsHle.Services.Friend; @@ -55,6 +56,18 @@ namespace Ryujinx.HLE.OsHle.Services case "audren:u": return new IAudioRendererManager(); + case "bcat:a": + return new Bcat.IServiceCreator(); + + case "bcat:m": + return new Bcat.IServiceCreator(); + + case "bcat:u": + return new Bcat.IServiceCreator(); + + case "bcat:s": + return new Bcat.IServiceCreator(); + case "bsd:s": return new IClient(); @@ -71,10 +84,10 @@ namespace Ryujinx.HLE.OsHle.Services return new IRandomInterface(); case "friend:a": - return new IServiceCreator(); + return new Friend.IServiceCreator(); case "friend:u": - return new IServiceCreator(); + return new Friend.IServiceCreator(); case "fsp-srv": return new IFileSystemProxy(); From 1344a47c774ab2b7491c59e363e04a499fa432f3 Mon Sep 17 00:00:00 2001 From: ReinUsesLisp Date: Mon, 23 Jul 2018 11:21:05 -0300 Subject: [PATCH 31/33] Blit framebuffer without shaders (#229) * Blit framebuffer without shaders * De-hardcode native size values * Adapt to dehardcoded framebuffers and address feedback * Remove framebuffer rebinding --- Ryujinx.Graphics/Gal/IGalFrameBuffer.cs | 2 +- Ryujinx.Graphics/Gal/OpenGL/FbFragShader.glsl | 13 - Ryujinx.Graphics/Gal/OpenGL/FbVtxShader.glsl | 28 -- Ryujinx.Graphics/Gal/OpenGL/OGLFrameBuffer.cs | 284 ++++++------------ Ryujinx.Graphics/Ryujinx.Graphics.csproj | 9 - Ryujinx.HLE/OsHle/Services/Vi/NvFlinger.cs | 53 +--- 6 files changed, 101 insertions(+), 288 deletions(-) delete mode 100644 Ryujinx.Graphics/Gal/OpenGL/FbFragShader.glsl delete mode 100644 Ryujinx.Graphics/Gal/OpenGL/FbVtxShader.glsl diff --git a/Ryujinx.Graphics/Gal/IGalFrameBuffer.cs b/Ryujinx.Graphics/Gal/IGalFrameBuffer.cs index 1f62bdb379..c0287ef8be 100644 --- a/Ryujinx.Graphics/Gal/IGalFrameBuffer.cs +++ b/Ryujinx.Graphics/Gal/IGalFrameBuffer.cs @@ -14,7 +14,7 @@ namespace Ryujinx.Graphics.Gal void Set(byte[] Data, int Width, int Height); - void SetTransform(float SX, float SY, float Rotate, float TX, float TY); + void SetTransform(bool FlipX, bool FlipY, int Top, int Left, int Right, int Bottom); void SetWindowSize(int Width, int Height); diff --git a/Ryujinx.Graphics/Gal/OpenGL/FbFragShader.glsl b/Ryujinx.Graphics/Gal/OpenGL/FbFragShader.glsl deleted file mode 100644 index 74e33bd7c0..0000000000 --- a/Ryujinx.Graphics/Gal/OpenGL/FbFragShader.glsl +++ /dev/null @@ -1,13 +0,0 @@ -#version 330 core - -precision highp float; - -uniform sampler2D tex; - -in vec2 tex_coord; - -out vec4 out_frag_color; - -void main(void) { - out_frag_color = texture(tex, tex_coord); -} \ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/OpenGL/FbVtxShader.glsl b/Ryujinx.Graphics/Gal/OpenGL/FbVtxShader.glsl deleted file mode 100644 index 35d560c09b..0000000000 --- a/Ryujinx.Graphics/Gal/OpenGL/FbVtxShader.glsl +++ /dev/null @@ -1,28 +0,0 @@ -#version 330 core - -precision highp float; - -uniform mat2 transform; -uniform vec2 window_size; -uniform vec2 offset; - -layout(location = 0) in vec2 in_position; -layout(location = 1) in vec2 in_tex_coord; - -out vec2 tex_coord; - -// Have a fixed aspect ratio, fit the image within the available space. -vec2 get_scale_ratio(void) { - vec2 native_size = vec2(1280, 720); - vec2 ratio = vec2( - (window_size.y * native_size.x) / (native_size.y * window_size.x), - (window_size.x * native_size.y) / (native_size.x * window_size.y) - ); - return min(ratio, 1); -} - -void main(void) { - tex_coord = in_tex_coord; - vec2 t_pos = (transform * in_position) + offset; - gl_Position = vec4(t_pos * get_scale_ratio(), 0, 1); -} \ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/OpenGL/OGLFrameBuffer.cs b/Ryujinx.Graphics/Gal/OpenGL/OGLFrameBuffer.cs index cd52762c79..30a3de64a0 100644 --- a/Ryujinx.Graphics/Gal/OpenGL/OGLFrameBuffer.cs +++ b/Ryujinx.Graphics/Gal/OpenGL/OGLFrameBuffer.cs @@ -32,48 +32,45 @@ namespace Ryujinx.Graphics.Gal.OpenGL public int RbHandle { get; private set; } public int TexHandle { get; private set; } - public FrameBuffer(int Width, int Height) + public FrameBuffer(int Width, int Height, bool HasRenderBuffer) { this.Width = Width; this.Height = Height; Handle = GL.GenFramebuffer(); - RbHandle = GL.GenRenderbuffer(); TexHandle = GL.GenTexture(); + + if (HasRenderBuffer) + { + RbHandle = GL.GenRenderbuffer(); + } } } - private struct ShaderProgram - { - public int Handle; - public int VpHandle; - public int FpHandle; - } + private const int NativeWidth = 1280; + private const int NativeHeight = 720; private Dictionary Fbs; - private ShaderProgram Shader; - private Rect Viewport; private Rect Window; - private bool IsInitialized; + private FrameBuffer CurrFb; + private FrameBuffer CurrReadFb; - private int RawFbTexWidth; - private int RawFbTexHeight; - private int RawFbTexHandle; + private FrameBuffer RawFb; - private int CurrFbHandle; - private int CurrTexHandle; + private bool FlipX; + private bool FlipY; - private int VaoHandle; - private int VboHandle; + private int CropTop; + private int CropLeft; + private int CropRight; + private int CropBottom; public OGLFrameBuffer() { Fbs = new Dictionary(); - - Shader = new ShaderProgram(); } public void Create(long Key, int Width, int Height) @@ -92,7 +89,7 @@ namespace Ryujinx.Graphics.Gal.OpenGL return; } - Fb = new FrameBuffer(Width, Height); + Fb = new FrameBuffer(Width, Height, true); SetupTexture(Fb.TexHandle, Width, Height); @@ -129,7 +126,7 @@ namespace Ryujinx.Graphics.Gal.OpenGL { GL.BindFramebuffer(FramebufferTarget.Framebuffer, Fb.Handle); - CurrFbHandle = Fb.Handle; + CurrFb = Fb; } } @@ -147,75 +144,50 @@ namespace Ryujinx.Graphics.Gal.OpenGL { if (Fbs.TryGetValue(Key, out FrameBuffer Fb)) { - CurrTexHandle = Fb.TexHandle; + CurrReadFb = Fb; } } public void Set(byte[] Data, int Width, int Height) { - if (RawFbTexHandle == 0) + if (RawFb == null) { - RawFbTexHandle = GL.GenTexture(); + CreateRawFb(Width, Height); } - if (RawFbTexWidth != Width || - RawFbTexHeight != Height) + if (RawFb.Width != Width || + RawFb.Height != Height) { - SetupTexture(RawFbTexHandle, Width, Height); + SetupTexture(RawFb.TexHandle, Width, Height); - RawFbTexWidth = Width; - RawFbTexHeight = Height; + RawFb.Width = Width; + RawFb.Height = Height; } GL.ActiveTexture(TextureUnit.Texture0); - GL.BindTexture(TextureTarget.Texture2D, RawFbTexHandle); + GL.BindTexture(TextureTarget.Texture2D, RawFb.TexHandle); (PixelFormat Format, PixelType Type) = OGLEnumConverter.GetTextureFormat(GalTextureFormat.A8B8G8R8); GL.TexSubImage2D(TextureTarget.Texture2D, 0, 0, 0, Width, Height, Format, Type, Data); - CurrTexHandle = RawFbTexHandle; + CurrReadFb = RawFb; } - public void SetTransform(float SX, float SY, float Rotate, float TX, float TY) + public void SetTransform(bool FlipX, bool FlipY, int Top, int Left, int Right, int Bottom) { - EnsureInitialized(); + this.FlipX = FlipX; + this.FlipY = FlipY; - Matrix2 Transform; - - Transform = Matrix2.CreateScale(SX, SY); - Transform *= Matrix2.CreateRotation(Rotate); - - Vector2 Offs = new Vector2(TX, TY); - - int CurrentProgram = GL.GetInteger(GetPName.CurrentProgram); - - GL.UseProgram(Shader.Handle); - - int TransformUniformLocation = GL.GetUniformLocation(Shader.Handle, "transform"); - - GL.UniformMatrix2(TransformUniformLocation, false, ref Transform); - - int OffsetUniformLocation = GL.GetUniformLocation(Shader.Handle, "offset"); - - GL.Uniform2(OffsetUniformLocation, ref Offs); - - GL.UseProgram(CurrentProgram); + CropTop = Top; + CropLeft = Left; + CropRight = Right; + CropBottom = Bottom; } public void SetWindowSize(int Width, int Height) { - int CurrentProgram = GL.GetInteger(GetPName.CurrentProgram); - - GL.UseProgram(Shader.Handle); - - int WindowSizeUniformLocation = GL.GetUniformLocation(Shader.Handle, "window_size"); - - GL.Uniform2(WindowSizeUniformLocation, new Vector2(Width, Height)); - - GL.UseProgram(CurrentProgram); - Window = new Rect(0, 0, Width, Height); } @@ -237,72 +209,59 @@ namespace Ryujinx.Graphics.Gal.OpenGL public void Render() { - if (CurrTexHandle != 0) + if (CurrReadFb != null) { - EnsureInitialized(); + int SrcX0, SrcX1, SrcY0, SrcY1; - //bool CullFaceEnable = GL.IsEnabled(EnableCap.CullFace); + if (CropLeft == 0 && CropRight == 0) + { + SrcX0 = 0; + SrcX1 = CurrReadFb.Width; + } + else + { + SrcX0 = CropLeft; + SrcX1 = CropRight; + } - bool DepthTestEnable = GL.IsEnabled(EnableCap.DepthTest); + if (CropTop == 0 && CropBottom == 0) + { + SrcY0 = 0; + SrcY1 = CurrReadFb.Height; + } + else + { + SrcY0 = CropTop; + SrcY1 = CropBottom; + } - bool StencilTestEnable = GL.IsEnabled(EnableCap.StencilTest); + float RatioX = MathF.Min(1f, (Window.Height * (float)NativeWidth) / ((float)NativeHeight * Window.Width)); + float RatioY = MathF.Min(1f, (Window.Width * (float)NativeHeight) / ((float)NativeWidth * Window.Height)); - bool AlphaBlendEnable = GL.IsEnabled(EnableCap.Blend); + int DstWidth = (int)(Window.Width * RatioX); + int DstHeight = (int)(Window.Height * RatioY); - //GL.Disable(EnableCap.CullFace); + int DstPaddingX = (Window.Width - DstWidth) / 2; + int DstPaddingY = (Window.Height - DstHeight) / 2; - GL.Disable(EnableCap.DepthTest); + int DstX0 = FlipX ? Window.Width - DstPaddingX : DstPaddingX; + int DstX1 = FlipX ? DstPaddingX : Window.Width - DstPaddingX; - GL.Disable(EnableCap.StencilTest); - - GL.Disable(EnableCap.Blend); - - GL.ActiveTexture(TextureUnit.Texture0); - - GL.BindTexture(TextureTarget.Texture2D, CurrTexHandle); - - int CurrentProgram = GL.GetInteger(GetPName.CurrentProgram); + int DstY0 = FlipY ? DstPaddingY : Window.Height - DstPaddingY; + int DstY1 = FlipY ? Window.Height - DstPaddingY : DstPaddingY; GL.BindFramebuffer(FramebufferTarget.Framebuffer, 0); - SetViewport(Window); + GL.Viewport(0, 0, Window.Width, Window.Height); - GL.Clear( - ClearBufferMask.ColorBufferBit | - ClearBufferMask.DepthBufferBit); + GL.BindFramebuffer(FramebufferTarget.ReadFramebuffer, CurrReadFb.Handle); - GL.BindVertexArray(VaoHandle); + GL.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit); - GL.UseProgram(Shader.Handle); - - GL.DrawArrays(PrimitiveType.TriangleStrip, 0, 4); - - //Restore the original state. - GL.BindFramebuffer(FramebufferTarget.Framebuffer, CurrFbHandle); - - GL.UseProgram(CurrentProgram); - - //if (CullFaceEnable) - //{ - // GL.Enable(EnableCap.CullFace); - //} - - if (DepthTestEnable) - { - GL.Enable(EnableCap.DepthTest); - } - - if (StencilTestEnable) - { - GL.Enable(EnableCap.StencilTest); - } - - if (AlphaBlendEnable) - { - GL.Enable(EnableCap.Blend); - } - - SetViewport(Viewport); + GL.BlitFramebuffer( + SrcX0, SrcY0, SrcX1, SrcY1, + DstX0, DstY0, DstX1, DstY1, + ClearBufferMask.ColorBufferBit, BlitFramebufferFilter.Linear); } } @@ -354,8 +313,6 @@ namespace Ryujinx.Graphics.Gal.OpenGL Data); Callback(Data); - - GL.BindFramebuffer(FramebufferTarget.ReadFramebuffer, CurrFbHandle); } } @@ -390,86 +347,29 @@ namespace Ryujinx.Graphics.Gal.OpenGL } } - private void EnsureInitialized() + private void CreateRawFb(int Width, int Height) { - if (!IsInitialized) + if (RawFb == null) { - IsInitialized = true; + RawFb = new FrameBuffer(Width, Height, false); - SetupShader(); - SetupVertex(); + SetupTexture(RawFb.TexHandle, Width, Height); + + RawFb.Width = Width; + RawFb.Height = Height; + + GL.BindFramebuffer(FramebufferTarget.Framebuffer, RawFb.Handle); + + GL.FramebufferTexture( + FramebufferTarget.Framebuffer, + FramebufferAttachment.ColorAttachment0, + RawFb.TexHandle, + 0); + + GL.Viewport(0, 0, Width, Height); } } - private void SetupShader() - { - Shader.VpHandle = GL.CreateShader(ShaderType.VertexShader); - Shader.FpHandle = GL.CreateShader(ShaderType.FragmentShader); - - string VpSource = EmbeddedResource.GetString("GlFbVtxShader"); - string FpSource = EmbeddedResource.GetString("GlFbFragShader"); - - GL.ShaderSource(Shader.VpHandle, VpSource); - GL.ShaderSource(Shader.FpHandle, FpSource); - GL.CompileShader(Shader.VpHandle); - GL.CompileShader(Shader.FpHandle); - - Shader.Handle = GL.CreateProgram(); - - GL.AttachShader(Shader.Handle, Shader.VpHandle); - GL.AttachShader(Shader.Handle, Shader.FpHandle); - GL.LinkProgram(Shader.Handle); - GL.UseProgram(Shader.Handle); - - Matrix2 Transform = Matrix2.Identity; - - int TexUniformLocation = GL.GetUniformLocation(Shader.Handle, "tex"); - - GL.Uniform1(TexUniformLocation, 0); - - int WindowSizeUniformLocation = GL.GetUniformLocation(Shader.Handle, "window_size"); - - GL.Uniform2(WindowSizeUniformLocation, new Vector2(1280.0f, 720.0f)); - - int TransformUniformLocation = GL.GetUniformLocation(Shader.Handle, "transform"); - - GL.UniformMatrix2(TransformUniformLocation, false, ref Transform); - } - - private void SetupVertex() - { - VaoHandle = GL.GenVertexArray(); - VboHandle = GL.GenBuffer(); - - float[] Buffer = new float[] - { - -1, 1, 0, 0, - 1, 1, 1, 0, - -1, -1, 0, 1, - 1, -1, 1, 1 - }; - - IntPtr Length = new IntPtr(Buffer.Length * 4); - - GL.BindBuffer(BufferTarget.ArrayBuffer, VboHandle); - GL.BufferData(BufferTarget.ArrayBuffer, Length, Buffer, BufferUsageHint.StreamDraw); - GL.BindBuffer(BufferTarget.ArrayBuffer, 0); - - GL.BindVertexArray(VaoHandle); - - GL.EnableVertexAttribArray(0); - - GL.BindBuffer(BufferTarget.ArrayBuffer, VboHandle); - - GL.VertexAttribPointer(0, 2, VertexAttribPointerType.Float, false, 16, 0); - - GL.EnableVertexAttribArray(1); - - GL.BindBuffer(BufferTarget.ArrayBuffer, VboHandle); - - GL.VertexAttribPointer(1, 2, VertexAttribPointerType.Float, false, 16, 8); - } - private void SetupTexture(int Handle, int Width, int Height) { GL.BindTexture(TextureTarget.Texture2D, Handle); diff --git a/Ryujinx.Graphics/Ryujinx.Graphics.csproj b/Ryujinx.Graphics/Ryujinx.Graphics.csproj index d0fad10765..7d86cbe134 100644 --- a/Ryujinx.Graphics/Ryujinx.Graphics.csproj +++ b/Ryujinx.Graphics/Ryujinx.Graphics.csproj @@ -21,13 +21,4 @@ - - - GlFbVtxShader - - - GlFbFragShader - - - diff --git a/Ryujinx.HLE/OsHle/Services/Vi/NvFlinger.cs b/Ryujinx.HLE/OsHle/Services/Vi/NvFlinger.cs index 5307127be4..41f2916f4a 100644 --- a/Ryujinx.HLE/OsHle/Services/Vi/NvFlinger.cs +++ b/Ryujinx.HLE/OsHle/Services/Vi/NvFlinger.cs @@ -293,54 +293,17 @@ namespace Ryujinx.HLE.OsHle.Services.Android Rect Crop = BufferQueue[Slot].Crop; - int RealWidth = FbWidth; - int RealHeight = FbHeight; + bool FlipX = BufferQueue[Slot].Transform.HasFlag(HalTransform.FlipX); + bool FlipY = BufferQueue[Slot].Transform.HasFlag(HalTransform.FlipY); - float XSign = BufferQueue[Slot].Transform.HasFlag(HalTransform.FlipX) ? -1 : 1; - float YSign = BufferQueue[Slot].Transform.HasFlag(HalTransform.FlipY) ? -1 : 1; + //Rotation is being ignored - float ScaleX = 1; - float ScaleY = 1; + int Top = Crop.Top; + int Left = Crop.Left; + int Right = Crop.Right; + int Bottom = Crop.Bottom; - float OffsX = 0; - float OffsY = 0; - - if (Crop.Right != 0 && - Crop.Bottom != 0) - { - //Who knows if this is right, I was never good with math... - RealWidth = Crop.Right - Crop.Left; - RealHeight = Crop.Bottom - Crop.Top; - - if (BufferQueue[Slot].Transform.HasFlag(HalTransform.Rotate90)) - { - ScaleY = (float)FbHeight / RealHeight; - ScaleX = (float)FbWidth / RealWidth; - - OffsY = ((-(float)Crop.Left / Crop.Right) + ScaleX - 1) * -XSign; - OffsX = ((-(float)Crop.Top / Crop.Bottom) + ScaleY - 1) * -YSign; - } - else - { - ScaleX = (float)FbWidth / RealWidth; - ScaleY = (float)FbHeight / RealHeight; - - OffsX = ((-(float)Crop.Left / Crop.Right) + ScaleX - 1) * XSign; - OffsY = ((-(float)Crop.Top / Crop.Bottom) + ScaleY - 1) * -YSign; - } - } - - ScaleX *= XSign; - ScaleY *= YSign; - - float Rotate = 0; - - if (BufferQueue[Slot].Transform.HasFlag(HalTransform.Rotate90)) - { - Rotate = -MathF.PI * 0.5f; - } - - Renderer.QueueAction(() => Renderer.FrameBuffer.SetTransform(ScaleX, ScaleY, Rotate, OffsX, OffsY)); + Renderer.QueueAction(() => Renderer.FrameBuffer.SetTransform(FlipX, FlipY, Top, Left, Right, Bottom)); //TODO: Support double buffering here aswell, it is broken for GPU //frame buffers because it seems to be completely out of sync. From 39d36145ba30ce80d7cfeea757014e213f5e5287 Mon Sep 17 00:00:00 2001 From: Arthur Chen Date: Fri, 27 Jul 2018 00:46:05 +0800 Subject: [PATCH 32/33] update encoding for branch instruction (#305) --- ChocolArm64/AOpCodeTable.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/ChocolArm64/AOpCodeTable.cs b/ChocolArm64/AOpCodeTable.cs index 689e039237..aa8220e38d 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)); From 51605fafc04e7c34d354bd2dd8aeffc5d912f6a9 Mon Sep 17 00:00:00 2001 From: ReinUsesLisp Date: Thu, 26 Jul 2018 13:49:29 -0300 Subject: [PATCH 33/33] Avoid calling buffer binding when shader didn't change (#295) --- Ryujinx.Graphics/Gal/OpenGL/OGLShader.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Ryujinx.Graphics/Gal/OpenGL/OGLShader.cs b/Ryujinx.Graphics/Gal/OpenGL/OGLShader.cs index 37213d8ed1..3f3f23b8a2 100644 --- a/Ryujinx.Graphics/Gal/OpenGL/OGLShader.cs +++ b/Ryujinx.Graphics/Gal/OpenGL/OGLShader.cs @@ -257,7 +257,10 @@ namespace Ryujinx.Graphics.Gal.OpenGL GL.UseProgram(Handle); - BindUniformBuffers(Handle); + if (CurrentProgramHandle != Handle) + { + BindUniformBuffers(Handle); + } CurrentProgramHandle = Handle; }