diff --git a/ARMeilleure/Instructions/InstEmitSystem.cs b/ARMeilleure/Instructions/InstEmitSystem.cs index d8769c1861..9650b1e5e1 100644 --- a/ARMeilleure/Instructions/InstEmitSystem.cs +++ b/ARMeilleure/Instructions/InstEmitSystem.cs @@ -1,5 +1,6 @@ using ARMeilleure.Decoders; using ARMeilleure.IntermediateRepresentation; +using ARMeilleure.State; using ARMeilleure.Translation; using System; @@ -32,6 +33,7 @@ namespace ARMeilleure.Instructions { case 0b11_011_0000_0000_001: name = nameof(NativeInterface.GetCtrEl0); break; case 0b11_011_0000_0000_111: name = nameof(NativeInterface.GetDczidEl0); break; + case 0b11_011_0100_0010_000: EmitGetNzcv(context); return; case 0b11_011_0100_0100_000: name = nameof(NativeInterface.GetFpcr); break; case 0b11_011_0100_0100_001: name = nameof(NativeInterface.GetFpsr); break; case 0b11_011_1101_0000_010: name = nameof(NativeInterface.GetTpidrEl0); break; @@ -53,6 +55,7 @@ namespace ARMeilleure.Instructions switch (GetPackedId(op)) { + case 0b11_011_0100_0010_000: EmitSetNzcv(context); return; case 0b11_011_0100_0100_000: name = nameof(NativeInterface.SetFpcr); break; case 0b11_011_0100_0100_001: name = nameof(NativeInterface.SetFpsr); break; case 0b11_011_1101_0000_010: name = nameof(NativeInterface.SetTpidrEl0); break; @@ -110,5 +113,44 @@ namespace ARMeilleure.Instructions return id; } + + private static void EmitGetNzcv(ArmEmitterContext context) + { + OpCodeSystem op = (OpCodeSystem)context.CurrOp; + + Operand vSh = context.ShiftLeft(GetFlag(PState.VFlag), Const((int)PState.VFlag)); + Operand cSh = context.ShiftLeft(GetFlag(PState.CFlag), Const((int)PState.CFlag)); + Operand zSh = context.ShiftLeft(GetFlag(PState.ZFlag), Const((int)PState.ZFlag)); + Operand nSh = context.ShiftLeft(GetFlag(PState.NFlag), Const((int)PState.NFlag)); + + Operand nzcvSh = context.BitwiseOr(context.BitwiseOr(nSh, zSh), context.BitwiseOr(cSh, vSh)); + + SetIntOrZR(context, op.Rt, nzcvSh); + } + + private static void EmitSetNzcv(ArmEmitterContext context) + { + OpCodeSystem op = (OpCodeSystem)context.CurrOp; + + Operand t = GetIntOrZR(context, op.Rt); + t = context.ConvertI64ToI32(t); + + Operand v = context.ShiftRightUI(t, Const((int)PState.VFlag)); + v = context.BitwiseAnd (v, Const(1)); + + Operand c = context.ShiftRightUI(t, Const((int)PState.CFlag)); + c = context.BitwiseAnd (c, Const(1)); + + Operand z = context.ShiftRightUI(t, Const((int)PState.ZFlag)); + z = context.BitwiseAnd (z, Const(1)); + + Operand n = context.ShiftRightUI(t, Const((int)PState.NFlag)); + n = context.BitwiseAnd (n, Const(1)); + + SetFlag(context, PState.VFlag, v); + SetFlag(context, PState.CFlag, c); + SetFlag(context, PState.ZFlag, z); + SetFlag(context, PState.NFlag, n); + } } } diff --git a/Ryujinx.HLE/HOS/Applets/AppletManager.cs b/Ryujinx.HLE/HOS/Applets/AppletManager.cs new file mode 100644 index 0000000000..e5426cd758 --- /dev/null +++ b/Ryujinx.HLE/HOS/Applets/AppletManager.cs @@ -0,0 +1,29 @@ +using Ryujinx.HLE.HOS.Services.Am.AppletAE; +using System; +using System.Collections.Generic; + +namespace Ryujinx.HLE.HOS.Applets +{ + static class AppletManager + { + private static Dictionary _appletMapping; + + static AppletManager() + { + _appletMapping = new Dictionary + { + { AppletId.PlayerSelect, typeof(PlayerSelectApplet) } + }; + } + + public static IApplet Create(AppletId applet, Horizon system) + { + if (_appletMapping.TryGetValue(applet, out Type appletClass)) + { + return (IApplet)Activator.CreateInstance(appletClass, system); + } + + throw new NotImplementedException($"{applet} applet is not implemented."); + } + } +} diff --git a/Ryujinx.HLE/HOS/Applets/IApplet.cs b/Ryujinx.HLE/HOS/Applets/IApplet.cs new file mode 100644 index 0000000000..aa248bf599 --- /dev/null +++ b/Ryujinx.HLE/HOS/Applets/IApplet.cs @@ -0,0 +1,13 @@ +using Ryujinx.HLE.HOS.Services.Am.AppletAE; +using System; + +namespace Ryujinx.HLE.HOS.Applets +{ + interface IApplet + { + event EventHandler AppletStateChanged; + + ResultCode Start(AppletFifo inData, AppletFifo outData); + ResultCode GetResult(); + } +} diff --git a/Ryujinx.HLE/HOS/Applets/PlayerSelect/PlayerSelectApplet.cs b/Ryujinx.HLE/HOS/Applets/PlayerSelect/PlayerSelectApplet.cs new file mode 100644 index 0000000000..7658c6db13 --- /dev/null +++ b/Ryujinx.HLE/HOS/Applets/PlayerSelect/PlayerSelectApplet.cs @@ -0,0 +1,55 @@ +using Ryujinx.HLE.HOS.Services.Account.Acc; +using Ryujinx.HLE.HOS.Services.Am.AppletAE; +using System; +using System.IO; + +namespace Ryujinx.HLE.HOS.Applets +{ + internal class PlayerSelectApplet : IApplet + { + private Horizon _system; + + private AppletFifo _inputData; + private AppletFifo _outputData; + + public event EventHandler AppletStateChanged; + + public PlayerSelectApplet(Horizon system) + { + _system = system; + } + + public ResultCode Start(AppletFifo inData, AppletFifo outData) + { + _inputData = inData; + _outputData = outData; + + // TODO(jduncanator): Parse PlayerSelectConfig from input data + _outputData.Push(BuildResponse()); + + AppletStateChanged?.Invoke(this, null); + + return ResultCode.Success; + } + + public ResultCode GetResult() + { + return ResultCode.Success; + } + + private byte[] BuildResponse() + { + UserProfile currentUser = _system.State.Account.LastOpenedUser; + + using (MemoryStream stream = new MemoryStream()) + using (BinaryWriter writer = new BinaryWriter(stream)) + { + writer.Write((ulong)PlayerSelectResult.Success); + + currentUser.UserId.Write(writer); + + return stream.ToArray(); + } + } + } +} diff --git a/Ryujinx.HLE/HOS/Applets/PlayerSelect/PlayerSelectResult.cs b/Ryujinx.HLE/HOS/Applets/PlayerSelect/PlayerSelectResult.cs new file mode 100644 index 0000000000..682e094ed6 --- /dev/null +++ b/Ryujinx.HLE/HOS/Applets/PlayerSelect/PlayerSelectResult.cs @@ -0,0 +1,8 @@ +namespace Ryujinx.HLE.HOS.Applets +{ + enum PlayerSelectResult : ulong + { + Success = 0, + Failure = 2 + } +} diff --git a/Ryujinx.HLE/HOS/Kernel/Threading/KThread.cs b/Ryujinx.HLE/HOS/Kernel/Threading/KThread.cs index d4965b4125..0232bc1684 100644 --- a/Ryujinx.HLE/HOS/Kernel/Threading/KThread.cs +++ b/Ryujinx.HLE/HOS/Kernel/Threading/KThread.cs @@ -162,6 +162,8 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading bool isAarch32 = (Owner.MmuFlags & 1) == 0; + Context.IsAarch32 = isAarch32; + Context.SetX(0, argsPtr); if (isAarch32) diff --git a/Ryujinx.HLE/HOS/ProgramLoader.cs b/Ryujinx.HLE/HOS/ProgramLoader.cs index 0bc6447e59..19c77380d4 100644 --- a/Ryujinx.HLE/HOS/ProgramLoader.cs +++ b/Ryujinx.HLE/HOS/ProgramLoader.cs @@ -127,7 +127,7 @@ namespace Ryujinx.HLE.HOS { if (!metaData.Is64Bits) { - Logger.PrintWarning(LogClass.Loader, "32-bits application detected!"); + Logger.PrintWarning(LogClass.Loader, "32-bits application detected."); } ulong argsStart = 0; @@ -309,4 +309,4 @@ namespace Ryujinx.HLE.HOS return SetProcessMemoryPermission(dataStart, end - dataStart, MemoryPermission.ReadAndWrite); } } -} \ No newline at end of file +} diff --git a/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/LibraryAppletCreator/ILibraryAppletAccessor.cs b/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/LibraryAppletCreator/ILibraryAppletAccessor.cs index 9d8e2a96f0..8c4d10084e 100644 --- a/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/LibraryAppletCreator/ILibraryAppletAccessor.cs +++ b/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/LibraryAppletCreator/ILibraryAppletAccessor.cs @@ -1,19 +1,37 @@ using Ryujinx.Common.Logging; +using Ryujinx.HLE.HOS.Applets; using Ryujinx.HLE.HOS.Ipc; using Ryujinx.HLE.HOS.Kernel.Common; using Ryujinx.HLE.HOS.Kernel.Threading; -using Ryujinx.HLE.HOS.Services.Am.AppletAE.Storage; using System; namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.LibraryAppletCreator { class ILibraryAppletAccessor : IpcService { + private IApplet _applet; + + private AppletFifo _inData; + private AppletFifo _outData; + private KEvent _stateChangedEvent; - public ILibraryAppletAccessor(Horizon system) + public ILibraryAppletAccessor(AppletId appletId, Horizon system) { _stateChangedEvent = new KEvent(system); + + _applet = AppletManager.Create(appletId, system); + _inData = new AppletFifo(); + _outData = new AppletFifo(); + + _applet.AppletStateChanged += OnAppletStateChanged; + + Logger.PrintInfo(LogClass.ServiceAm, $"Applet '{appletId}' created."); + } + + private void OnAppletStateChanged(object sender, EventArgs e) + { + _stateChangedEvent.ReadableEvent.Signal(); } [Command(0)] @@ -29,8 +47,6 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Lib context.Response.HandleDesc = IpcHandleDesc.MakeCopy(handle); - Logger.PrintStub(LogClass.ServiceAm); - return ResultCode.Success; } @@ -38,25 +54,23 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Lib // Start() public ResultCode Start(ServiceCtx context) { - Logger.PrintStub(LogClass.ServiceAm); - - return ResultCode.Success; + return (ResultCode)_applet.Start(_inData, _outData); } [Command(30)] // GetResult() public ResultCode GetResult(ServiceCtx context) { - Logger.PrintStub(LogClass.ServiceAm); - - return ResultCode.Success; + return (ResultCode)_applet.GetResult(); } [Command(100)] // PushInData(object) public ResultCode PushInData(ServiceCtx context) { - Logger.PrintStub(LogClass.ServiceAm); + IStorage data = GetObject(context, 0); + + _inData.Push(data.Data); return ResultCode.Success; } @@ -65,9 +79,11 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Lib // PopOutData() -> object public ResultCode PopOutData(ServiceCtx context) { - MakeObject(context, new IStorage(StorageHelper.MakeLaunchParams())); + byte[] data = _outData.Pop(); + MakeObject(context, new IStorage(data)); + return ResultCode.Success; } } -} \ No newline at end of file +} diff --git a/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/ILibraryAppletCreator.cs b/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/ILibraryAppletCreator.cs index 8b0b225bd9..094ed30508 100644 --- a/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/ILibraryAppletCreator.cs +++ b/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/ILibraryAppletCreator.cs @@ -1,3 +1,4 @@ +using Ryujinx.HLE.HOS.Applets; using Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.LibraryAppletCreator; namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.SystemAppletProxy @@ -10,7 +11,10 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Sys // CreateLibraryApplet(u32, u32) -> object public ResultCode CreateLibraryApplet(ServiceCtx context) { - MakeObject(context, new ILibraryAppletAccessor(context.Device.System)); + AppletId appletId = (AppletId)context.RequestData.ReadInt32(); + int libraryAppletMode = context.RequestData.ReadInt32(); + + MakeObject(context, new ILibraryAppletAccessor(appletId, context.Device.System)); return ResultCode.Success; } diff --git a/Ryujinx.HLE/HOS/Services/Am/AppletAE/AppletFifo.cs b/Ryujinx.HLE/HOS/Services/Am/AppletAE/AppletFifo.cs new file mode 100644 index 0000000000..2391ba5e2a --- /dev/null +++ b/Ryujinx.HLE/HOS/Services/Am/AppletAE/AppletFifo.cs @@ -0,0 +1,79 @@ +using System; +using System.Collections; +using System.Collections.Concurrent; +using System.Collections.Generic; + +namespace Ryujinx.HLE.HOS.Services.Am.AppletAE +{ + internal class AppletFifo : IEnumerable + { + private ConcurrentQueue _dataQueue; + + public int Count => _dataQueue.Count; + + public AppletFifo() + { + _dataQueue = new ConcurrentQueue(); + } + + public void Push(T item) + { + _dataQueue.Enqueue(item); + } + + public T Pop() + { + if (_dataQueue.TryDequeue(out T result)) + { + return result; + } + + throw new InvalidOperationException("FIFO empty."); + } + + public bool TryPop(out T result) + { + return _dataQueue.TryDequeue(out result); + } + + public T Peek() + { + if (_dataQueue.TryPeek(out T result)) + { + return result; + } + + throw new InvalidOperationException("FIFO empty."); + } + + public bool TryPeek(out T result) + { + return _dataQueue.TryPeek(out result); + } + + public void Clear() + { + _dataQueue.Clear(); + } + + public T[] ToArray() + { + return _dataQueue.ToArray(); + } + + public void CopyTo(T[] array, int arrayIndex) + { + _dataQueue.CopyTo(array, arrayIndex); + } + + public IEnumerator GetEnumerator() + { + return _dataQueue.GetEnumerator(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return _dataQueue.GetEnumerator(); + } + } +} diff --git a/Ryujinx.HLE/HOS/Services/Am/AppletAE/Types/AppletId.cs b/Ryujinx.HLE/HOS/Services/Am/AppletAE/Types/AppletId.cs new file mode 100644 index 0000000000..917f68658b --- /dev/null +++ b/Ryujinx.HLE/HOS/Services/Am/AppletAE/Types/AppletId.cs @@ -0,0 +1,27 @@ +namespace Ryujinx.HLE.HOS.Services.Am.AppletAE +{ + enum AppletId + { + Application = 0x01, + OverlayDisplay = 0x02, + QLaunch = 0x03, + Starter = 0x04, + Auth = 0x0A, + Cabinet = 0x0B, + Controller = 0x0C, + DataErase = 0x0D, + Error = 0x0E, + NetConnect = 0x0F, + PlayerSelect = 0x10, + SoftwareKeyboard = 0x11, + MiiEdit = 0x12, + LibAppletWeb = 0x13, + LibAppletShop = 0x14, + PhotoViewer = 0x15, + Settings = 0x16, + LibAppletOff = 0x17, + LibAppletWhitelisted = 0x18, + LibAppletAuth = 0x19, + MyPage = 0x1A + } +} diff --git a/Ryujinx.HLE/HOS/Services/Audio/IAudioRendererManager.cs b/Ryujinx.HLE/HOS/Services/Audio/IAudioRendererManager.cs index 563ba7e151..5ca968553b 100644 --- a/Ryujinx.HLE/HOS/Services/Audio/IAudioRendererManager.cs +++ b/Ryujinx.HLE/HOS/Services/Audio/IAudioRendererManager.cs @@ -134,11 +134,11 @@ namespace Ryujinx.HLE.HOS.Services.Audio } [Command(4)] // 4.0.0+ - // GetAudioDeviceServiceWithRevisionInfo(nn::applet::AppletResourceUserId, u32) -> object + // GetAudioDeviceServiceWithRevisionInfo(u32 revision_info, nn::applet::AppletResourceUserId) -> object public ResultCode GetAudioDeviceServiceWithRevisionInfo(ServiceCtx context) { - long appletResourceUserId = context.RequestData.ReadInt64(); int revisionInfo = context.RequestData.ReadInt32(); + long appletResourceUserId = context.RequestData.ReadInt64(); Logger.PrintStub(LogClass.ServiceAudio, new { appletResourceUserId, revisionInfo }); diff --git a/Ryujinx.Tests/Cpu/CpuTestSystem.cs b/Ryujinx.Tests/Cpu/CpuTestSystem.cs new file mode 100644 index 0000000000..02b1f1bd36 --- /dev/null +++ b/Ryujinx.Tests/Cpu/CpuTestSystem.cs @@ -0,0 +1,73 @@ +#define System + +using ARMeilleure.State; + +using NUnit.Framework; + +using System.Collections.Generic; + +namespace Ryujinx.Tests.Cpu +{ + [Category("System")] + public sealed class CpuTestSystem : CpuTest + { +#if System + +#region "ValueSource (Types)" + private static IEnumerable _GenNzcv_() + { + yield return 0x0000000000000000ul; + yield return 0x7FFFFFFFFFFFFFFFul; + yield return 0x8000000000000000ul; + yield return 0xFFFFFFFFFFFFFFFFul; + + bool v = TestContext.CurrentContext.Random.NextBool(); + bool c = TestContext.CurrentContext.Random.NextBool(); + bool z = TestContext.CurrentContext.Random.NextBool(); + bool n = TestContext.CurrentContext.Random.NextBool(); + + ulong rnd = 0UL; + + rnd |= (v ? 1UL : 0UL) << (int)PState.VFlag; + rnd |= (c ? 1UL : 0UL) << (int)PState.CFlag; + rnd |= (z ? 1UL : 0UL) << (int)PState.ZFlag; + rnd |= (n ? 1UL : 0UL) << (int)PState.NFlag; + + yield return rnd; + } +#endregion + +#region "ValueSource (Opcodes)" + private static uint[] _MrsMsr_Nzcv_() + { + return new uint[] + { + 0xD53B4200u, // MRS X0, NZCV + 0xD51B4200u // MSR NZCV, X0 + }; + } +#endregion + + private const int RndCnt = 2; + + [Test, Pairwise] + public void MrsMsr_Nzcv([ValueSource("_MrsMsr_Nzcv_")] uint opcodes, + [Values(0u, 1u, 31u)] uint rt, + [ValueSource("_GenNzcv_")] [Random(RndCnt)] ulong xt) + { + opcodes |= (rt & 31) << 0; + + bool v = TestContext.CurrentContext.Random.NextBool(); + bool c = TestContext.CurrentContext.Random.NextBool(); + bool z = TestContext.CurrentContext.Random.NextBool(); + bool n = TestContext.CurrentContext.Random.NextBool(); + + ulong x31 = TestContext.CurrentContext.Random.NextULong(); + + SingleOpcode(opcodes, x0: xt, x1: xt, x31: x31, overflow: v, carry: c, zero: z, negative: n); + + CompareAgainstUnicorn(); + } +#endif + } +}