From 344fc8a55de471121d3f600cca9e95315d0e502f Mon Sep 17 00:00:00 2001 From: gdkchan Date: Sun, 4 Mar 2018 20:32:18 -0300 Subject: [PATCH 01/28] Try fixing NvFlinger rotation with scaling, return correct error code on WaitSignal timeout, always display window at the center of the screen --- ChocolArm64/ATranslator.cs | 1 - Ryujinx.Core/OsHle/CondVar.cs | 8 +++- .../OsHle/Handles/KProcessScheduler.cs | 12 ++++-- Ryujinx.Core/OsHle/Services/Vi/NvFlinger.cs | 38 +++++++++++-------- Ryujinx.Core/OsHle/Svc/SvcThreadSync.cs | 7 +++- Ryujinx/Ui/GLScreen.cs | 4 ++ 6 files changed, 46 insertions(+), 24 deletions(-) diff --git a/ChocolArm64/ATranslator.cs b/ChocolArm64/ATranslator.cs index ab434e2267..1f4a922ab2 100644 --- a/ChocolArm64/ATranslator.cs +++ b/ChocolArm64/ATranslator.cs @@ -7,7 +7,6 @@ using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.Reflection.Emit; -using System.Threading; namespace ChocolArm64 { diff --git a/Ryujinx.Core/OsHle/CondVar.cs b/Ryujinx.Core/OsHle/CondVar.cs index 7b3e18521a..ac3ba57498 100644 --- a/Ryujinx.Core/OsHle/CondVar.cs +++ b/Ryujinx.Core/OsHle/CondVar.cs @@ -24,7 +24,7 @@ namespace Ryujinx.Core.OsHle WaitingThreads = new List(); } - public void WaitForSignal(HThread Thread) + public bool WaitForSignal(HThread Thread) { int Count = Process.Memory.ReadInt32(CondVarAddress); @@ -41,12 +41,14 @@ namespace Ryujinx.Core.OsHle } else { - Process.Scheduler.WaitForSignal(Thread, (int)(Timeout / 1000000)); + bool Result = Process.Scheduler.WaitForSignal(Thread, (int)(Timeout / 1000000)); lock (WaitingThreads) { WaitingThreads.Remove(Thread); } + + return Result; } } @@ -60,6 +62,8 @@ namespace Ryujinx.Core.OsHle } ReleaseCondVarValue(); + + return true; } public void SetSignal(HThread Thread, int Count) diff --git a/Ryujinx.Core/OsHle/Handles/KProcessScheduler.cs b/Ryujinx.Core/OsHle/Handles/KProcessScheduler.cs index 2045f8a17a..b111607811 100644 --- a/Ryujinx.Core/OsHle/Handles/KProcessScheduler.cs +++ b/Ryujinx.Core/OsHle/Handles/KProcessScheduler.cs @@ -183,7 +183,7 @@ namespace Ryujinx.Core.OsHle.Handles TryResumingExecution(SchedThread); } - public void WaitForSignal(HThread Thread, int Timeout = -1) + public bool WaitForSignal(HThread Thread, int Timeout = -1) { SchedulerThread SchedThread; @@ -206,22 +206,26 @@ namespace Ryujinx.Core.OsHle.Handles { Logging.Error($"{GetDbgThreadInfo(Thread)} was not found on the scheduler queue!"); - return; + return false; } } + bool Result; + if (Timeout >= 0) { Logging.Debug($"{GetDbgThreadInfo(Thread)} has wait timeout of {Timeout}ms."); - SchedThread.WaitEvent.WaitOne(Timeout); + Result = SchedThread.WaitEvent.WaitOne(Timeout); } else { - SchedThread.WaitEvent.WaitOne(); + Result = SchedThread.WaitEvent.WaitOne(); } TryResumingExecution(SchedThread); + + return Result; } private void TryResumingExecution(SchedulerThread SchedThread) diff --git a/Ryujinx.Core/OsHle/Services/Vi/NvFlinger.cs b/Ryujinx.Core/OsHle/Services/Vi/NvFlinger.cs index b745867e92..4b5f9819aa 100644 --- a/Ryujinx.Core/OsHle/Services/Vi/NvFlinger.cs +++ b/Ryujinx.Core/OsHle/Services/Vi/NvFlinger.cs @@ -278,39 +278,45 @@ namespace Ryujinx.Core.OsHle.IpcServices.Android int RealWidth = FbWidth; int RealHeight = FbHeight; + float XSign = BufferQueue[Slot].Transform.HasFlag(HalTransform.FlipX) ? -1 : 1; + float YSign = BufferQueue[Slot].Transform.HasFlag(HalTransform.FlipY) ? -1 : 1; + float ScaleX = 1; float ScaleY = 1; + 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; - ScaleX = (float)FbWidth / RealWidth; - ScaleY = (float)FbHeight / RealHeight; + if (BufferQueue[Slot].Transform.HasFlag(HalTransform.Rotate90)) + { + ScaleY = (float)FbHeight / RealHeight; + ScaleX = (float)FbWidth / RealWidth; - OffsX = -(float)Crop.Left / Crop.Right; - OffsY = -(float)Crop.Top / Crop.Bottom; + 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 += ScaleX - 1; - OffsY += ScaleY - 1; + 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.FlipX)) - { - ScaleX = -ScaleX; - } - - if (BufferQueue[Slot].Transform.HasFlag(HalTransform.FlipY)) - { - ScaleY = -ScaleY; - } - if (BufferQueue[Slot].Transform.HasFlag(HalTransform.Rotate90)) { Rotate = -MathF.PI * 0.5f; diff --git a/Ryujinx.Core/OsHle/Svc/SvcThreadSync.cs b/Ryujinx.Core/OsHle/Svc/SvcThreadSync.cs index dec13f75e3..2705272a39 100644 --- a/Ryujinx.Core/OsHle/Svc/SvcThreadSync.cs +++ b/Ryujinx.Core/OsHle/Svc/SvcThreadSync.cs @@ -53,7 +53,12 @@ namespace Ryujinx.Core.OsHle.Svc Cv = Ns.Os.CondVars.GetOrAdd(CondVarAddress, Cv); - Cv.WaitForSignal(Thread); + if (!Cv.WaitForSignal(Thread)) + { + ThreadState.X0 = (int)SvcResult.ErrTimeout; + + return; + } M.WaitForLock(Thread, ThreadHandle); diff --git a/Ryujinx/Ui/GLScreen.cs b/Ryujinx/Ui/GLScreen.cs index 1e48b28056..4c7cee4672 100644 --- a/Ryujinx/Ui/GLScreen.cs +++ b/Ryujinx/Ui/GLScreen.cs @@ -29,6 +29,10 @@ namespace Ryujinx { this.Ns = Ns; this.Renderer = Renderer; + + Location = new Point( + (DisplayDevice.Default.Width / 2) - (Width / 2), + (DisplayDevice.Default.Height / 2) - (Height / 2)); } protected override void OnLoad(EventArgs e) From 73cc30cc8015685cdb2b8630492703e16f8b4bd9 Mon Sep 17 00:00:00 2001 From: gdkchan Date: Mon, 5 Mar 2018 02:09:52 -0300 Subject: [PATCH 02/28] Allow multiple hid shared memory locations --- Ryujinx.Core/Hid/Hid.cs | 200 ++++++++++++++--------- Ryujinx.Core/OsHle/Handles/HSharedMem.cs | 31 +--- Ryujinx.Core/OsHle/Horizon.cs | 16 +- Ryujinx.Core/Switch.cs | 10 +- 4 files changed, 132 insertions(+), 125 deletions(-) diff --git a/Ryujinx.Core/Hid/Hid.cs b/Ryujinx.Core/Hid/Hid.cs index d7227889a6..249747c152 100644 --- a/Ryujinx.Core/Hid/Hid.cs +++ b/Ryujinx.Core/Hid/Hid.cs @@ -1,4 +1,6 @@ using ChocolArm64.Memory; +using Ryujinx.Core.OsHle.Handles; +using System; using System.Diagnostics; namespace Ryujinx.Core.Input @@ -60,33 +62,65 @@ namespace Ryujinx.Core.Input private const int HidEntryCount = 17; - private long SharedMemOffset; + private object ShMemLock; - private Switch Ns; + private long[] ShMemPositions; - public Hid(Switch Ns) + private IntPtr Ram; + + public Hid(IntPtr Ram) { - this.Ns = Ns; + this.Ram = Ram; + + ShMemLock = new object(); + + ShMemPositions = new long[0]; } - public void Init(long HidOffset) + internal void ShMemMap(object sender, EventArgs e) { - SharedMemOffset = HidOffset; + HSharedMem SharedMem = (HSharedMem)sender; + lock (ShMemLock) + { + ShMemPositions = SharedMem.GetVirtualPositions(); + + long BasePosition = ShMemPositions[ShMemPositions.Length - 1]; + + Logging.Info($"HID shared memory successfully mapped to 0x{BasePosition:x16}!"); + + Init(BasePosition); + } + } + + internal void ShMemUnmap(object sender, EventArgs e) + { + HSharedMem SharedMem = (HSharedMem)sender; + + lock (ShMemLock) + { + ShMemPositions = SharedMem.GetVirtualPositions(); + } + } + + private void Init(long BasePosition) + { InitializeJoyconPair( + BasePosition, JoyConColor.Body_Neon_Red, JoyConColor.Buttons_Neon_Red, JoyConColor.Body_Neon_Blue, JoyConColor.Buttons_Neon_Blue); } - public void InitializeJoyconPair( + private void InitializeJoyconPair( + long BasePosition, JoyConColor LeftColorBody, JoyConColor LeftColorButtons, JoyConColor RightColorBody, JoyConColor RightColorButtons) { - long BaseControllerOffset = HidControllersOffset + 8 * HidControllerSize; + long BaseControllerOffset = BasePosition + HidControllersOffset + 8 * HidControllerSize; HidControllerType Type = HidControllerType.ControllerType_Handheld | @@ -125,115 +159,123 @@ namespace Ryujinx.Core.Input HidJoystickPosition LeftStick, HidJoystickPosition RightStick) { - long ControllerOffset = HidControllersOffset + (int)ControllerId * HidControllerSize; + lock (ShMemLock) + { + foreach (long Position in ShMemPositions) + { + long ControllerOffset = Position + HidControllersOffset; - ControllerOffset += HidControllerHeaderSize; + ControllerOffset += (int)ControllerId * HidControllerSize; - ControllerOffset += (int)ControllerLayout * HidControllerLayoutsSize; + ControllerOffset += HidControllerHeaderSize; - long LastEntry = ReadInt64(ControllerOffset + 0x10); + ControllerOffset += (int)ControllerLayout * HidControllerLayoutsSize; - long CurrEntry = (LastEntry + 1) % HidEntryCount; + long LastEntry = ReadInt64(ControllerOffset + 0x10); - long Timestamp = Stopwatch.GetTimestamp(); + long CurrEntry = (LastEntry + 1) % HidEntryCount; - WriteInt64(ControllerOffset + 0x0, Timestamp); - WriteInt64(ControllerOffset + 0x8, HidEntryCount); - WriteInt64(ControllerOffset + 0x10, CurrEntry); - WriteInt64(ControllerOffset + 0x18, HidEntryCount - 1); + long Timestamp = Stopwatch.GetTimestamp(); - ControllerOffset += HidControllersLayoutHeaderSize; + WriteInt64(ControllerOffset + 0x0, Timestamp); + WriteInt64(ControllerOffset + 0x8, HidEntryCount); + WriteInt64(ControllerOffset + 0x10, CurrEntry); + WriteInt64(ControllerOffset + 0x18, HidEntryCount - 1); - ControllerOffset += CurrEntry * HidControllersInputEntrySize; + ControllerOffset += HidControllersLayoutHeaderSize; - WriteInt64(ControllerOffset + 0x0, Timestamp); - WriteInt64(ControllerOffset + 0x8, Timestamp); + ControllerOffset += CurrEntry * HidControllersInputEntrySize; - WriteInt64(ControllerOffset + 0x10, (uint)Buttons); + WriteInt64(ControllerOffset + 0x0, Timestamp); + WriteInt64(ControllerOffset + 0x8, Timestamp); - WriteInt32(ControllerOffset + 0x18, LeftStick.DX); - WriteInt32(ControllerOffset + 0x1c, LeftStick.DY); + WriteInt64(ControllerOffset + 0x10, (uint)Buttons); - WriteInt64(ControllerOffset + 0x20, RightStick.DX); - WriteInt64(ControllerOffset + 0x24, RightStick.DY); + WriteInt32(ControllerOffset + 0x18, LeftStick.DX); + WriteInt32(ControllerOffset + 0x1c, LeftStick.DY); - WriteInt64(ControllerOffset + 0x28, - (uint)HidControllerConnState.Controller_State_Connected | - (uint)HidControllerConnState.Controller_State_Wired); + WriteInt64(ControllerOffset + 0x20, RightStick.DX); + WriteInt64(ControllerOffset + 0x24, RightStick.DY); + + WriteInt64(ControllerOffset + 0x28, + (uint)HidControllerConnState.Controller_State_Connected | + (uint)HidControllerConnState.Controller_State_Wired); + } + } } public void SetTouchPoints(params HidTouchPoint[] Points) { - long LastEntry = ReadInt64(HidTouchScreenOffset + 0x10); - - long CurrEntry = (LastEntry + 1) % HidEntryCount; - - long Timestamp = Stopwatch.GetTimestamp(); - - WriteInt64(HidTouchScreenOffset + 0x0, Timestamp); - WriteInt64(HidTouchScreenOffset + 0x8, HidEntryCount); - WriteInt64(HidTouchScreenOffset + 0x10, CurrEntry); - WriteInt64(HidTouchScreenOffset + 0x18, HidEntryCount - 1); - WriteInt64(HidTouchScreenOffset + 0x20, Timestamp); - - long TouchEntryOffset = HidTouchScreenOffset + HidTouchHeaderSize; - - long LastEntryOffset = TouchEntryOffset + LastEntry * HidTouchEntrySize; - - long LastTimestamp = ReadInt64(LastEntryOffset); - - TouchEntryOffset += CurrEntry * HidTouchEntrySize; - - WriteInt64(TouchEntryOffset + 0x0, LastTimestamp + 1); - WriteInt64(TouchEntryOffset + 0x8, Points.Length); - - TouchEntryOffset += HidTouchEntryHeaderSize; - - const int Padding = 0; - - int Index = 0; - - foreach (HidTouchPoint Point in Points) + lock (ShMemLock) { - WriteInt64(TouchEntryOffset + 0x0, Timestamp); - WriteInt32(TouchEntryOffset + 0x8, Padding); - WriteInt32(TouchEntryOffset + 0xc, Index++); - WriteInt32(TouchEntryOffset + 0x10, Point.X); - WriteInt32(TouchEntryOffset + 0x14, Point.Y); - WriteInt32(TouchEntryOffset + 0x18, Point.DiameterX); - WriteInt32(TouchEntryOffset + 0x1c, Point.DiameterY); - WriteInt32(TouchEntryOffset + 0x20, Point.Angle); - WriteInt32(TouchEntryOffset + 0x24, Padding); + foreach (long Position in ShMemPositions) + { + long TouchScreenOffset = Position + HidTouchScreenOffset; - TouchEntryOffset += HidTouchEntryTouchSize; + long LastEntry = ReadInt64(TouchScreenOffset + 0x10); + + long CurrEntry = (LastEntry + 1) % HidEntryCount; + + long Timestamp = ReadInt64(TouchScreenOffset) + 1; + + WriteInt64(TouchScreenOffset + 0x0, Timestamp); + WriteInt64(TouchScreenOffset + 0x8, HidEntryCount); + WriteInt64(TouchScreenOffset + 0x10, CurrEntry); + WriteInt64(TouchScreenOffset + 0x18, HidEntryCount - 1); + WriteInt64(TouchScreenOffset + 0x20, Timestamp); + + long TouchEntryOffset = TouchScreenOffset + HidTouchHeaderSize; + + long LastEntryOffset = TouchEntryOffset + LastEntry * HidTouchEntrySize; + + TouchEntryOffset += CurrEntry * HidTouchEntrySize; + + WriteInt64(TouchEntryOffset + 0x0, Timestamp); + WriteInt64(TouchEntryOffset + 0x8, Points.Length); + + TouchEntryOffset += HidTouchEntryHeaderSize; + + const int Padding = 0; + + int Index = 0; + + foreach (HidTouchPoint Point in Points) + { + WriteInt64(TouchEntryOffset + 0x0, Timestamp); + WriteInt32(TouchEntryOffset + 0x8, Padding); + WriteInt32(TouchEntryOffset + 0xc, Index++); + WriteInt32(TouchEntryOffset + 0x10, Point.X); + WriteInt32(TouchEntryOffset + 0x14, Point.Y); + WriteInt32(TouchEntryOffset + 0x18, Point.DiameterX); + WriteInt32(TouchEntryOffset + 0x1c, Point.DiameterY); + WriteInt32(TouchEntryOffset + 0x20, Point.Angle); + WriteInt32(TouchEntryOffset + 0x24, Padding); + + TouchEntryOffset += HidTouchEntryTouchSize; + } + } } } private unsafe long ReadInt64(long Position) { - Position += SharedMemOffset; - if ((ulong)Position + 8 > AMemoryMgr.AddrSize) return 0; - return *((long*)((byte*)Ns.Ram + Position)); + return *((long*)((byte*)Ram + Position)); } private unsafe void WriteInt32(long Position, int Value) { - Position += SharedMemOffset; - if ((ulong)Position + 4 > AMemoryMgr.AddrSize) return; - *((int*)((byte*)Ns.Ram + Position)) = Value; + *((int*)((byte*)Ram + Position)) = Value; } private unsafe void WriteInt64(long Position, long Value) { - Position += SharedMemOffset; - if ((ulong)Position + 8 > AMemoryMgr.AddrSize) return; - *((long*)((byte*)Ns.Ram + Position)) = Value; + *((long*)((byte*)Ram + Position)) = Value; } } } \ No newline at end of file diff --git a/Ryujinx.Core/OsHle/Handles/HSharedMem.cs b/Ryujinx.Core/OsHle/Handles/HSharedMem.cs index 9cd0a0d47e..eb173e9652 100644 --- a/Ryujinx.Core/OsHle/Handles/HSharedMem.cs +++ b/Ryujinx.Core/OsHle/Handles/HSharedMem.cs @@ -7,8 +7,6 @@ namespace Ryujinx.Core.OsHle.Handles { private List Positions; - public int PositionsCount => Positions.Count; - public EventHandler MemoryMapped; public EventHandler MemoryUnmapped; @@ -37,34 +35,9 @@ namespace Ryujinx.Core.OsHle.Handles } } - public long GetVirtualPosition(int Index) + public long[] GetVirtualPositions() { - lock (Positions) - { - if (Index < 0 || Index >= Positions.Count) - { - throw new ArgumentOutOfRangeException(nameof(Index)); - } - - return Positions[Index]; - } - } - - public bool TryGetLastVirtualPosition(out long Position) - { - lock (Positions) - { - if (Positions.Count > 0) - { - Position = Positions[Positions.Count - 1]; - - return true; - } - - Position = 0; - - return false; - } + return Positions.ToArray(); } } } \ No newline at end of file diff --git a/Ryujinx.Core/OsHle/Horizon.cs b/Ryujinx.Core/OsHle/Horizon.cs index c238914398..a8f5df7c9d 100644 --- a/Ryujinx.Core/OsHle/Horizon.cs +++ b/Ryujinx.Core/OsHle/Horizon.cs @@ -27,7 +27,7 @@ namespace Ryujinx.Core.OsHle private ConcurrentDictionary Processes; - private HSharedMem HidSharedMem; + internal HSharedMem HidSharedMem; private Switch Ns; @@ -49,8 +49,6 @@ namespace Ryujinx.Core.OsHle HidSharedMem = new HSharedMem(); - HidSharedMem.MemoryMapped += HidInit; - HidHandle = Handles.GenerateId(HidSharedMem); FontHandle = Handles.GenerateId(new HSharedMem()); @@ -166,17 +164,5 @@ namespace Ryujinx.Core.OsHle Handles.Delete(Handle); } - - private void HidInit(object sender, EventArgs e) - { - HSharedMem SharedMem = (HSharedMem)sender; - - if (SharedMem.TryGetLastVirtualPosition(out long Position)) - { - Logging.Info($"HID shared memory successfully mapped to 0x{Position:x16}!"); - - Ns.Hid.Init(Position); - } - } } } \ No newline at end of file diff --git a/Ryujinx.Core/Switch.cs b/Ryujinx.Core/Switch.cs index 6f41da8144..3f7b1e2b81 100644 --- a/Ryujinx.Core/Switch.cs +++ b/Ryujinx.Core/Switch.cs @@ -27,10 +27,16 @@ namespace Ryujinx.Core Ram = Marshal.AllocHGlobal((IntPtr)AMemoryMgr.RamSize); Gpu = new NsGpu(Renderer); + VFs = new VirtualFs(); - Hid = new Hid(this); - Os = new Horizon(this); + Hid = new Hid(Ram); + + Os = new Horizon(this); + + Os.HidSharedMem.MemoryMapped += Hid.ShMemMap; + Os.HidSharedMem.MemoryUnmapped += Hid.ShMemUnmap; + Settings = new SetSys(); } From c9ef25681dc05ff87c6fb7d0da9d555964e201c1 Mon Sep 17 00:00:00 2001 From: MS-DOS1999 Date: Mon, 5 Mar 2018 13:21:19 +0100 Subject: [PATCH 03/28] Add Frintx_S, ASRV test, update ADCS, use Assert.Multiple and indent (#44) * add 'ADC 32bit and Overflow' test * Add WZR/WSP tests * fix ADC and ADDS * add ADCS test * add SBCS test * indent my code and delete comment * '/' <- i hate you x) * remove spacebar char * remove false tab * add frintx_S test * update frintx_S test * add ASRV test * fix new line * fix PR * fix indent --- Ryujinx.Tests/Cpu/CpuTest.cs | 7 +- Ryujinx.Tests/Cpu/CpuTestAlu.cs | 111 ++++++++++++++------- Ryujinx.Tests/Cpu/CpuTestMisc.cs | 7 +- Ryujinx.Tests/Cpu/CpuTestScalar.cs | 6 +- Ryujinx.Tests/Cpu/CpuTestSimdArithmetic.cs | 78 +++++++++++++++ 5 files changed, 165 insertions(+), 44 deletions(-) create mode 100644 Ryujinx.Tests/Cpu/CpuTestSimdArithmetic.cs diff --git a/Ryujinx.Tests/Cpu/CpuTest.cs b/Ryujinx.Tests/Cpu/CpuTest.cs index a56eeb7281..a4a3b33fc5 100644 --- a/Ryujinx.Tests/Cpu/CpuTest.cs +++ b/Ryujinx.Tests/Cpu/CpuTest.cs @@ -57,7 +57,7 @@ namespace Ryujinx.Tests.Cpu protected void SetThreadState(ulong X0 = 0, ulong X1 = 0, ulong X2 = 0, ulong X31 = 0, AVec V0 = default(AVec), AVec V1 = default(AVec), AVec V2 = default(AVec), - bool Overflow = false, bool Carry = false, bool Zero = false, bool Negative = false) + bool Overflow = false, bool Carry = false, bool Zero = false, bool Negative = false, int Fpcr = 0x0) { Thread.ThreadState.X0 = X0; Thread.ThreadState.X1 = X1; @@ -70,6 +70,7 @@ namespace Ryujinx.Tests.Cpu Thread.ThreadState.Carry = Carry; Thread.ThreadState.Zero = Zero; Thread.ThreadState.Negative = Negative; + Thread.ThreadState.Fpcr = Fpcr; } protected void ExecuteOpcodes() @@ -92,12 +93,12 @@ namespace Ryujinx.Tests.Cpu protected AThreadState SingleOpcode(uint Opcode, ulong X0 = 0, ulong X1 = 0, ulong X2 = 0, ulong X31 = 0, AVec V0 = default(AVec), AVec V1 = default(AVec), AVec V2 = default(AVec), - bool Overflow = false, bool Carry = false, bool Zero = false, bool Negative = false) + bool Overflow = false, bool Carry = false, bool Zero = false, bool Negative = false, int Fpcr = 0x0) { this.Opcode(Opcode); this.Opcode(0xD4200000); // BRK #0 this.Opcode(0xD65F03C0); // RET - SetThreadState(X0, X1, X2, X31, V0, V1, V2, Overflow, Carry, Zero, Negative); + SetThreadState(X0, X1, X2, X31, V0, V1, V2, Overflow, Carry, Zero, Negative, Fpcr); ExecuteOpcodes(); return GetThreadState(); diff --git a/Ryujinx.Tests/Cpu/CpuTestAlu.cs b/Ryujinx.Tests/Cpu/CpuTestAlu.cs index 0445c97ab2..8116fc7c70 100644 --- a/Ryujinx.Tests/Cpu/CpuTestAlu.cs +++ b/Ryujinx.Tests/Cpu/CpuTestAlu.cs @@ -17,20 +17,24 @@ namespace Ryujinx.Tests.Cpu Assert.AreEqual(Result, ThreadState.X0); } - [TestCase(0x3A020020u, 2u, 3u, false, false, false, 5u)] - [TestCase(0x3A020020u, 2u, 3u, true, false, false, 6u)] - [TestCase(0xBA020020u, 2u, 3u, false, false, false, 5u)] - [TestCase(0xBA020020u, 2u, 3u, true, false, false, 6u)] - [TestCase(0x3A020020u, 0xFFFFFFFEu, 0x1u, true, true, true, 0x0u)] - public void Adcs(uint Opcode, uint A, uint B, bool CarryState, bool Zero, bool Carry, uint Result) + [TestCase(0x3A020020u, 2u, 3u, false, false, false, false, 5u)] + [TestCase(0x3A020020u, 2u, 3u, true, false, false, false, 6u)] + [TestCase(0xBA020020u, 2u, 3u, false, false, false, false, 5u)] + [TestCase(0xBA020020u, 2u, 3u, true, false, false, false, 6u)] + [TestCase(0x3A020020u, 0xFFFFFFFEu, 0x1u, true, false, true, true, 0x0u)] + [TestCase(0x3A020020u, 0xFFFFFFFFu, 0xFFFFFFFFu, true, true, false, true, 0xFFFFFFFFu)] + public void Adcs(uint Opcode, uint A, uint B, bool CarryState, bool Negative, bool Zero, bool Carry, uint Result) { //ADCS (X0/W0), (X1, W1), (X2/W2) AThreadState ThreadState = SingleOpcode(Opcode, X1: A, X2: B, Carry: CarryState); - Assert.IsFalse(ThreadState.Negative); - Assert.IsFalse(ThreadState.Overflow); - Assert.AreEqual(Zero, ThreadState.Zero); - Assert.AreEqual(Carry, ThreadState.Carry); - Assert.AreEqual(Result, ThreadState.X0); + Assert.Multiple(() => + { + Assert.IsFalse(ThreadState.Overflow); + Assert.AreEqual(Negative, ThreadState.Negative); + Assert.AreEqual(Zero, ThreadState.Zero); + Assert.AreEqual(Carry, ThreadState.Carry); + Assert.AreEqual(Result, ThreadState.X0); + }); } [Test] @@ -50,11 +54,14 @@ namespace Ryujinx.Tests.Cpu { //ADDS WZR, WSP, #5 AThreadState ThreadState = SingleOpcode(0x310017FF, X31: A); - Assert.IsFalse(ThreadState.Negative); - Assert.AreEqual(Zero, ThreadState.Zero); - Assert.AreEqual(Carry, ThreadState.Carry); - Assert.IsFalse(ThreadState.Overflow); - Assert.AreEqual(A, ThreadState.X31); + Assert.Multiple(() => + { + Assert.IsFalse(ThreadState.Negative); + Assert.IsFalse(ThreadState.Overflow); + Assert.AreEqual(Zero, ThreadState.Zero); + Assert.AreEqual(Carry, ThreadState.Carry); + Assert.AreEqual(A, ThreadState.X31); + }); } [TestCase(0xFFFFFFFFu, 0xFFFFFFFFu, 0xFFFFFFFFul, true, false)] @@ -65,26 +72,55 @@ namespace Ryujinx.Tests.Cpu // ANDS W0, W1, W2 uint Opcode = 0x6A020020; AThreadState ThreadState = SingleOpcode(Opcode, X1: A, X2: B); - Assert.AreEqual(Result, ThreadState.X0); - Assert.AreEqual(Negative, ThreadState.Negative); - Assert.AreEqual(Zero, ThreadState.Zero); + Assert.Multiple(() => + { + Assert.AreEqual(Result, ThreadState.X0); + Assert.AreEqual(Negative, ThreadState.Negative); + Assert.AreEqual(Zero, ThreadState.Zero); + }); } - [Test] - public void OrrBitmasks() + [TestCase(0x0000FF44u, 0x00000004u, 0x00000FF4u)] + [TestCase(0x00000000u, 0x00000004u, 0x00000000u)] + [TestCase(0x0000FF44u, 0x00000008u, 0x000000FFu)] + [TestCase(0xFFFFFFFFu, 0x00000004u, 0xFFFFFFFFu)] + [TestCase(0xFFFFFFFFu, 0x00000008u, 0xFFFFFFFFu)] + [TestCase(0xFFFFFFFFu, 0x00000020u, 0xFFFFFFFFu)] + [TestCase(0x0FFFFFFFu, 0x0000001Cu, 0x00000000u)] + [TestCase(0x80000000u, 0x0000001Fu, 0xFFFFFFFFu)] + [TestCase(0xCAFE0000u, 0x00000020u, 0xCAFE0000u)] + public void Asrv32(uint A, uint ShiftValue, uint Result) { - // ORR W0, WZR, #0x01010101 - Assert.AreEqual(0x01010101, SingleOpcode(0x3200C3E0).X0); + // ASRV W0, W1, W2 + AThreadState ThreadState = SingleOpcode(0x1AC22820, X1: A, X2: ShiftValue); + Assert.AreEqual(Result, ThreadState.X0); + } - Reset(); + [TestCase(0x000000000000FF44ul, 0x00000004u, 0x0000000000000FF4ul)] + [TestCase(0x0000000000000000ul, 0x00000004u, 0x0000000000000000ul)] + [TestCase(0x000000000000FF44ul, 0x00000008u, 0x00000000000000FFul)] + [TestCase(0x00000000FFFFFFFFul, 0x00000004u, 0x000000000FFFFFFFul)] + [TestCase(0x00000000FFFFFFFFul, 0x00000008u, 0x0000000000FFFFFFul)] + [TestCase(0x00000000FFFFFFFFul, 0x00000020u, 0x0000000000000000ul)] + [TestCase(0x000000000FFFFFFFul, 0x0000001Cu, 0x0000000000000000ul)] + [TestCase(0x000CC4488FFFFFFFul, 0x0000001Cu, 0x0000000000CC4488ul)] + [TestCase(0xFFFFFFFFFFFFFFFFul, 0x0000001Cu, 0xFFFFFFFFFFFFFFFFul)] + [TestCase(0x8000000000000000ul, 0x0000003Fu, 0xFFFFFFFFFFFFFFFFul)] + [TestCase(0xCAFE000000000000ul, 0x00000040u, 0xCAFE000000000000ul)] + public void Asrv64(ulong A, uint ShiftValue, ulong Result) + { + // ASRV X0, X1, X2 + AThreadState ThreadState = SingleOpcode(0x9AC22820, X1: A, X2: ShiftValue); + Assert.AreEqual(Result, ThreadState.X0); + } - // ORR W1, WZR, #0x00F000F0 - Assert.AreEqual(0x00F000F0, SingleOpcode(0x320C8FE1).X1); - - Reset(); - - // ORR W2, WZR, #1 - Assert.AreEqual(0x00000001, SingleOpcode(0x320003E2).X2); + [TestCase(0x01010101u, 0x3200C3E2u)] + [TestCase(0x00F000F0u, 0x320C8FE2u)] + [TestCase(0x00000001u, 0x320003E2u)] + public void OrrBitmasks(uint Bitmask, uint Opcode) + { + // ORR W2, WZR, #Bitmask + Assert.AreEqual(Bitmask, SingleOpcode(Opcode).X2); } [Test] @@ -113,11 +149,14 @@ namespace Ryujinx.Tests.Cpu { //SBCS (X0/W0), (X1, W1), (X2/W2) AThreadState ThreadState = SingleOpcode(Opcode, X1: A, X2: B, Carry: CarryState); - Assert.AreEqual(Negative, ThreadState.Negative); - Assert.IsFalse(ThreadState.Overflow); - Assert.AreEqual(Zero, ThreadState.Zero); - Assert.AreEqual(Carry, ThreadState.Carry); - Assert.AreEqual(Result, ThreadState.X0); + Assert.Multiple(() => + { + Assert.IsFalse(ThreadState.Overflow); + Assert.AreEqual(Negative, ThreadState.Negative); + Assert.AreEqual(Zero, ThreadState.Zero); + Assert.AreEqual(Carry, ThreadState.Carry); + Assert.AreEqual(Result, ThreadState.X0); + }); } } } diff --git a/Ryujinx.Tests/Cpu/CpuTestMisc.cs b/Ryujinx.Tests/Cpu/CpuTestMisc.cs index 97947b9a93..3a995fe261 100644 --- a/Ryujinx.Tests/Cpu/CpuTestMisc.cs +++ b/Ryujinx.Tests/Cpu/CpuTestMisc.cs @@ -197,8 +197,11 @@ namespace Ryujinx.Tests.Cpu Opcode(0xD4200000); Opcode(0xD65F03C0); ExecuteOpcodes(); - Assert.AreEqual(0, GetThreadState().X0); - Assert.IsTrue(GetThreadState().Zero); + Assert.Multiple(() => + { + Assert.AreEqual(0, GetThreadState().X0); + Assert.IsTrue(GetThreadState().Zero); + }); } [Test] diff --git a/Ryujinx.Tests/Cpu/CpuTestScalar.cs b/Ryujinx.Tests/Cpu/CpuTestScalar.cs index ffe01a299b..a178be272f 100644 --- a/Ryujinx.Tests/Cpu/CpuTestScalar.cs +++ b/Ryujinx.Tests/Cpu/CpuTestScalar.cs @@ -14,9 +14,9 @@ namespace Ryujinx.Tests.Cpu [TestCase(0x7F7FFFFFu, 0x807FFFFFu, 0x7F7FFFFFu)] [TestCase(0x7FC00000u, 0x3F800000u, 0x7FC00000u)] [TestCase(0x3F800000u, 0x7FC00000u, 0x7FC00000u)] - [TestCase(0x7F800001u, 0x7FC00042u, 0x7FC00001u, Ignore = "NaN test.")] - [TestCase(0x7FC00042u, 0x7F800001u, 0x7FC00001u, Ignore = "NaN test.")] - [TestCase(0x7FC0000Au, 0x7FC0000Bu, 0x7FC0000Au, Ignore = "NaN test.")] + [TestCase(0x7F800001u, 0x7FC00042u, 0x7FC00001u)] + [TestCase(0x7FC00042u, 0x7F800001u, 0x7FC00001u)] + [TestCase(0x7FC0000Au, 0x7FC0000Bu, 0x7FC0000Au)] public void Fmax_S(uint A, uint B, uint Result) { // FMAX S0, S1, S2 diff --git a/Ryujinx.Tests/Cpu/CpuTestSimdArithmetic.cs b/Ryujinx.Tests/Cpu/CpuTestSimdArithmetic.cs new file mode 100644 index 0000000000..56aaef4881 --- /dev/null +++ b/Ryujinx.Tests/Cpu/CpuTestSimdArithmetic.cs @@ -0,0 +1,78 @@ +using ChocolArm64.State; +using NUnit.Framework; + +namespace Ryujinx.Tests.Cpu +{ + public class CpuTestSimdArithmetic : CpuTest + { + [TestCase(0x3FE66666u, 'N', false, 0x40000000u)] + [TestCase(0x3F99999Au, 'N', false, 0x3F800000u)] + [TestCase(0x404CCCCDu, 'P', false, 0x40800000u)] + [TestCase(0x40733333u, 'P', false, 0x40800000u)] + [TestCase(0x404CCCCDu, 'M', false, 0x40400000u)] + [TestCase(0x40733333u, 'M', false, 0x40400000u)] + [TestCase(0x3F99999Au, 'Z', false, 0x3F800000u)] + [TestCase(0x3FE66666u, 'Z', false, 0x3F800000u)] + [TestCase(0x00000000u, 'N', false, 0x00000000u)] + [TestCase(0x00000000u, 'P', false, 0x00000000u)] + [TestCase(0x00000000u, 'M', false, 0x00000000u)] + [TestCase(0x00000000u, 'Z', false, 0x00000000u)] + [TestCase(0x80000000u, 'N', false, 0x80000000u)] + [TestCase(0x80000000u, 'P', false, 0x80000000u)] + [TestCase(0x80000000u, 'M', false, 0x80000000u)] + [TestCase(0x80000000u, 'Z', false, 0x80000000u)] + [TestCase(0x7F800000u, 'N', false, 0x7F800000u)] + [TestCase(0x7F800000u, 'P', false, 0x7F800000u)] + [TestCase(0x7F800000u, 'M', false, 0x7F800000u)] + [TestCase(0x7F800000u, 'Z', false, 0x7F800000u)] + [TestCase(0xFF800000u, 'N', false, 0xFF800000u)] + [TestCase(0xFF800000u, 'P', false, 0xFF800000u)] + [TestCase(0xFF800000u, 'M', false, 0xFF800000u)] + [TestCase(0xFF800000u, 'Z', false, 0xFF800000u)] + [TestCase(0xFF800001u, 'N', false, 0xFFC00001u)] + [TestCase(0xFF800001u, 'P', false, 0xFFC00001u)] + [TestCase(0xFF800001u, 'M', false, 0xFFC00001u)] + [TestCase(0xFF800001u, 'Z', false, 0xFFC00001u)] + [TestCase(0xFF800001u, 'N', true, 0x7FC00000u)] + [TestCase(0xFF800001u, 'P', true, 0x7FC00000u)] + [TestCase(0xFF800001u, 'M', true, 0x7FC00000u)] + [TestCase(0xFF800001u, 'Z', true, 0x7FC00000u)] + [TestCase(0x7FC00002u, 'N', false, 0x7FC00002u)] + [TestCase(0x7FC00002u, 'P', false, 0x7FC00002u)] + [TestCase(0x7FC00002u, 'M', false, 0x7FC00002u)] + [TestCase(0x7FC00002u, 'Z', false, 0x7FC00002u)] + [TestCase(0x7FC00002u, 'N', true, 0x7FC00000u)] + [TestCase(0x7FC00002u, 'P', true, 0x7FC00000u)] + [TestCase(0x7FC00002u, 'M', true, 0x7FC00000u)] + [TestCase(0x7FC00002u, 'Z', true, 0x7FC00000u)] + public void Frintx_S(uint A, char RoundType, bool DefaultNaN, uint Result) + { + int FpcrTemp = 0x0; + switch(RoundType) + { + case 'N': + FpcrTemp = 0x0; + break; + + case 'P': + FpcrTemp = 0x400000; + break; + + case 'M': + FpcrTemp = 0x800000; + break; + + case 'Z': + FpcrTemp = 0xC00000; + break; + } + if(DefaultNaN) + { + FpcrTemp |= 1 << 25; + } + AVec V1 = new AVec { X0 = A }; + AThreadState ThreadState = SingleOpcode(0x1E274020, V1: V1, Fpcr: FpcrTemp); + Assert.AreEqual(Result, ThreadState.V0.X0); + } + } +} From 0e343a748d9dcfe50b885b8c0c5e886bc44080ac Mon Sep 17 00:00:00 2001 From: gdkchan Date: Mon, 5 Mar 2018 12:58:19 -0300 Subject: [PATCH 04/28] Add FCVTL and FCVTN instruction (no Half support yet), stub SvcClearEvent --- ChocolArm64/AOpCodeTable.cs | 2 + ChocolArm64/Instruction/AInstEmitSimdCvt.cs | 60 +++++++++++++++++++++ Ryujinx.Core/OsHle/Svc/SvcHandler.cs | 1 + Ryujinx.Core/OsHle/Svc/SvcSystem.cs | 9 ++++ 4 files changed, 72 insertions(+) diff --git a/ChocolArm64/AOpCodeTable.cs b/ChocolArm64/AOpCodeTable.cs index e192f5ec5e..5db495af94 100644 --- a/ChocolArm64/AOpCodeTable.cs +++ b/ChocolArm64/AOpCodeTable.cs @@ -161,8 +161,10 @@ namespace ChocolArm64 Set("000111100x10001xx10000xxxxxxxxxx", AInstEmit.Fcvt_S, typeof(AOpCodeSimd)); Set("x00111100x100100000000xxxxxxxxxx", AInstEmit.Fcvtas_Gp, typeof(AOpCodeSimdCvt)); Set("x00111100x100101000000xxxxxxxxxx", AInstEmit.Fcvtau_Gp, typeof(AOpCodeSimdCvt)); + Set("0x0011100x100001011110xxxxxxxxxx", AInstEmit.Fcvtl_V, typeof(AOpCodeSimd)); Set("x00111100x110000000000xxxxxxxxxx", AInstEmit.Fcvtms_Gp, typeof(AOpCodeSimdCvt)); Set("x00111100x110001000000xxxxxxxxxx", AInstEmit.Fcvtmu_Gp, typeof(AOpCodeSimdCvt)); + Set("0x0011100x100001011010xxxxxxxxxx", AInstEmit.Fcvtn_V, typeof(AOpCodeSimd)); Set("x00111100x101000000000xxxxxxxxxx", AInstEmit.Fcvtps_Gp, typeof(AOpCodeSimdCvt)); Set("x00111100x101001000000xxxxxxxxxx", AInstEmit.Fcvtpu_Gp, typeof(AOpCodeSimdCvt)); Set("x00111100x111000000000xxxxxxxxxx", AInstEmit.Fcvtzs_Gp, typeof(AOpCodeSimdCvt)); diff --git a/ChocolArm64/Instruction/AInstEmitSimdCvt.cs b/ChocolArm64/Instruction/AInstEmitSimdCvt.cs index 688f05a209..e97027777e 100644 --- a/ChocolArm64/Instruction/AInstEmitSimdCvt.cs +++ b/ChocolArm64/Instruction/AInstEmitSimdCvt.cs @@ -31,6 +31,36 @@ namespace ChocolArm64.Instruction EmitFcvt_u_Gp(Context, () => EmitRoundMathCall(Context, MidpointRounding.AwayFromZero)); } + public static void Fcvtl_V(AILEmitterCtx Context) + { + AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp; + + int SizeF = Op.Size & 1; + + int Elems = 4 >> SizeF; + + int Part = Context.CurrOp.RegisterSize == ARegisterSize.SIMD128 ? Elems : 0; + + for (int Index = 0; Index < Elems; Index++) + { + if (SizeF == 0) + { + //TODO: This need the half precision floating point type, + //that is not yet supported on .NET. We should probably + //do our own implementation on the meantime. + throw new NotImplementedException(); + } + else /* if (SizeF == 1) */ + { + EmitVectorExtractF(Context, Op.Rn, Part + Index, 0); + + Context.Emit(OpCodes.Conv_R8); + } + + EmitVectorInsertF(Context, Op.Rd, Index, SizeF); + } + } + public static void Fcvtms_Gp(AILEmitterCtx Context) { EmitFcvt_s_Gp(Context, () => EmitUnaryMathCall(Context, nameof(Math.Floor))); @@ -41,6 +71,36 @@ namespace ChocolArm64.Instruction EmitFcvt_u_Gp(Context, () => EmitUnaryMathCall(Context, nameof(Math.Floor))); } + public static void Fcvtn_V(AILEmitterCtx Context) + { + AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp; + + int SizeF = Op.Size & 1; + + int Elems = 4 >> SizeF; + + int Part = Context.CurrOp.RegisterSize == ARegisterSize.SIMD128 ? Elems : 0; + + for (int Index = 0; Index < Elems; Index++) + { + EmitVectorExtractF(Context, Op.Rd, Index, SizeF); + + if (SizeF == 0) + { + //TODO: This need the half precision floating point type, + //that is not yet supported on .NET. We should probably + //do our own implementation on the meantime. + throw new NotImplementedException(); + } + else /* if (SizeF == 1) */ + { + Context.Emit(OpCodes.Conv_R4); + + EmitVectorInsertF(Context, Op.Rd, Part + Index, 0); + } + } + } + public static void Fcvtps_Gp(AILEmitterCtx Context) { EmitFcvt_s_Gp(Context, () => EmitUnaryMathCall(Context, nameof(Math.Ceiling))); diff --git a/Ryujinx.Core/OsHle/Svc/SvcHandler.cs b/Ryujinx.Core/OsHle/Svc/SvcHandler.cs index 212d30a6c3..b3e6262db1 100644 --- a/Ryujinx.Core/OsHle/Svc/SvcHandler.cs +++ b/Ryujinx.Core/OsHle/Svc/SvcHandler.cs @@ -36,6 +36,7 @@ namespace Ryujinx.Core.OsHle.Svc { 0x0c, SvcGetThreadPriority }, { 0x0d, SvcSetThreadPriority }, { 0x0f, SvcSetThreadCoreMask }, + { 0x12, SvcClearEvent }, { 0x13, SvcMapSharedMemory }, { 0x14, SvcUnmapSharedMemory }, { 0x15, SvcCreateTransferMemory }, diff --git a/Ryujinx.Core/OsHle/Svc/SvcSystem.cs b/Ryujinx.Core/OsHle/Svc/SvcSystem.cs index 51b2e26c6f..d0459f2f24 100644 --- a/Ryujinx.Core/OsHle/Svc/SvcSystem.cs +++ b/Ryujinx.Core/OsHle/Svc/SvcSystem.cs @@ -20,6 +20,15 @@ namespace Ryujinx.Core.OsHle.Svc Ns.Os.ExitProcess(ThreadState.ProcessId); } + private void SvcClearEvent(AThreadState ThreadState) + { + int Handle = (int)ThreadState.X0; + + //TODO: Implement events. + + ThreadState.X0 = (int)SvcResult.Success; + } + private void SvcCloseHandle(AThreadState ThreadState) { int Handle = (int)ThreadState.X0; From 59d1b2ad83385dad49cf930e826ce0693b9cee2c Mon Sep 17 00:00:00 2001 From: gdkchan Date: Mon, 5 Mar 2018 16:18:37 -0300 Subject: [PATCH 05/28] Add MUL (vector by element), fix FCVTN, make svcs use MakeError too --- ChocolArm64/AOpCodeTable.cs | 5 +- ChocolArm64/Decoder/AOpCodeSimdReg.cs | 6 +- ChocolArm64/Decoder/AOpCodeSimdRegElem.cs | 26 +++++-- ChocolArm64/Decoder/AOpCodeSimdRegElemF.cs | 22 ++++++ .../Instruction/AInstEmitSimdArithmetic.cs | 5 ++ ChocolArm64/Instruction/AInstEmitSimdCvt.cs | 5 ++ .../Instruction/AInstEmitSimdHelper.cs | 76 +++++++++++++++---- Ryujinx.Core/OsHle/ErrorCode.cs | 10 +++ .../OsHle/{Services => }/ErrorModule.cs | 2 +- Ryujinx.Core/OsHle/KernelErr.cs | 11 +++ Ryujinx.Core/OsHle/Services/ErrorCode.cs | 10 --- .../OsHle/Services/FspSrv/IFileSystem.cs | 2 +- Ryujinx.Core/OsHle/Svc/SvcMemory.cs | 23 +++--- Ryujinx.Core/OsHle/Svc/SvcResult.cs | 11 --- Ryujinx.Core/OsHle/Svc/SvcSystem.cs | 22 +++--- Ryujinx.Core/OsHle/Svc/SvcThread.cs | 12 +-- Ryujinx.Core/OsHle/Svc/SvcThreadSync.cs | 12 +-- 17 files changed, 180 insertions(+), 80 deletions(-) create mode 100644 ChocolArm64/Decoder/AOpCodeSimdRegElemF.cs create mode 100644 Ryujinx.Core/OsHle/ErrorCode.cs rename Ryujinx.Core/OsHle/{Services => }/ErrorModule.cs (98%) create mode 100644 Ryujinx.Core/OsHle/KernelErr.cs delete mode 100644 Ryujinx.Core/OsHle/Services/ErrorCode.cs delete mode 100644 Ryujinx.Core/OsHle/Svc/SvcResult.cs diff --git a/ChocolArm64/AOpCodeTable.cs b/ChocolArm64/AOpCodeTable.cs index 5db495af94..6b250b4b00 100644 --- a/ChocolArm64/AOpCodeTable.cs +++ b/ChocolArm64/AOpCodeTable.cs @@ -183,7 +183,7 @@ namespace ChocolArm64 Set("000111100x1xxxxx010110xxxxxxxxxx", AInstEmit.Fmin_S, typeof(AOpCodeSimdReg)); Set("000111100x1xxxxx011110xxxxxxxxxx", AInstEmit.Fminnm_S, typeof(AOpCodeSimdReg)); Set("0>0011100<1xxxxx110011xxxxxxxxxx", AInstEmit.Fmla_V, typeof(AOpCodeSimdReg)); - Set("0x0011111<1011100<1xxxxx110111xxxxxxxxxx", AInstEmit.Fmul_V, typeof(AOpCodeSimdReg)); - Set("0x0011111<> 11) & 1; - } - else - { - Index = (OpCode >> 21) & 1 | - (OpCode >> 10) & 2; + case 1: + Index = (OpCode >> 21) & 1 | + (OpCode >> 10) & 2 | + (OpCode >> 18) & 4; + + Rm &= 0xf; + + break; + + case 2: + Index = (OpCode >> 21) & 1 | + (OpCode >> 10) & 2; + + break; + + default: Emitter = AInstEmit.Und; return; } + + } } } \ No newline at end of file diff --git a/ChocolArm64/Decoder/AOpCodeSimdRegElemF.cs b/ChocolArm64/Decoder/AOpCodeSimdRegElemF.cs new file mode 100644 index 0000000000..e61d7093a7 --- /dev/null +++ b/ChocolArm64/Decoder/AOpCodeSimdRegElemF.cs @@ -0,0 +1,22 @@ +using ChocolArm64.Instruction; + +namespace ChocolArm64.Decoder +{ + class AOpCodeSimdRegElemF : AOpCodeSimdReg + { + public int Index { get; private set; } + + public AOpCodeSimdRegElemF(AInst Inst, long Position, int OpCode) : base(Inst, Position, OpCode) + { + if ((Size & 1) != 0) + { + Index = (OpCode >> 11) & 1; + } + else + { + Index = (OpCode >> 21) & 1 | + (OpCode >> 10) & 2; + } + } + } +} \ No newline at end of file diff --git a/ChocolArm64/Instruction/AInstEmitSimdArithmetic.cs b/ChocolArm64/Instruction/AInstEmitSimdArithmetic.cs index 9c1bc28626..9f5cc64f4c 100644 --- a/ChocolArm64/Instruction/AInstEmitSimdArithmetic.cs +++ b/ChocolArm64/Instruction/AInstEmitSimdArithmetic.cs @@ -341,6 +341,11 @@ namespace ChocolArm64.Instruction EmitVectorBinaryOpZx(Context, () => Context.Emit(OpCodes.Mul)); } + public static void Mul_Ve(AILEmitterCtx Context) + { + EmitVectorBinaryOpByElemZx(Context, () => Context.Emit(OpCodes.Mul)); + } + public static void Neg_V(AILEmitterCtx Context) { EmitVectorUnaryOpSx(Context, () => Context.Emit(OpCodes.Neg)); diff --git a/ChocolArm64/Instruction/AInstEmitSimdCvt.cs b/ChocolArm64/Instruction/AInstEmitSimdCvt.cs index e97027777e..b9944e565f 100644 --- a/ChocolArm64/Instruction/AInstEmitSimdCvt.cs +++ b/ChocolArm64/Instruction/AInstEmitSimdCvt.cs @@ -99,6 +99,11 @@ namespace ChocolArm64.Instruction EmitVectorInsertF(Context, Op.Rd, Part + Index, 0); } } + + if (Op.RegisterSize == ARegisterSize.SIMD64) + { + EmitVectorZeroUpper(Context, Op.Rd); + } } public static void Fcvtps_Gp(AILEmitterCtx Context) diff --git a/ChocolArm64/Instruction/AInstEmitSimdHelper.cs b/ChocolArm64/Instruction/AInstEmitSimdHelper.cs index 68ee3d3e4e..33e4d54874 100644 --- a/ChocolArm64/Instruction/AInstEmitSimdHelper.cs +++ b/ChocolArm64/Instruction/AInstEmitSimdHelper.cs @@ -200,20 +200,6 @@ namespace ChocolArm64.Instruction EmitVectorOpF(Context, Emit, OperFlags.RdRnRm); } - public static void EmitVectorBinaryOpByElemF(AILEmitterCtx Context, Action Emit) - { - AOpCodeSimdRegElem Op = (AOpCodeSimdRegElem)Context.CurrOp; - - EmitVectorOpByElemF(Context, Emit, Op.Index, Ternary: false); - } - - public static void EmitVectorTernaryOpByElemF(AILEmitterCtx Context, Action Emit) - { - AOpCodeSimdRegElem Op = (AOpCodeSimdRegElem)Context.CurrOp; - - EmitVectorOpByElemF(Context, Emit, Op.Index, Ternary: true); - } - public static void EmitVectorOpF(AILEmitterCtx Context, Action Emit, OperFlags Opers) { AOpCodeSimdReg Op = (AOpCodeSimdReg)Context.CurrOp; @@ -250,6 +236,20 @@ namespace ChocolArm64.Instruction } } + public static void EmitVectorBinaryOpByElemF(AILEmitterCtx Context, Action Emit) + { + AOpCodeSimdRegElemF Op = (AOpCodeSimdRegElemF)Context.CurrOp; + + EmitVectorOpByElemF(Context, Emit, Op.Index, Ternary: false); + } + + public static void EmitVectorTernaryOpByElemF(AILEmitterCtx Context, Action Emit) + { + AOpCodeSimdRegElemF Op = (AOpCodeSimdRegElemF)Context.CurrOp; + + EmitVectorOpByElemF(Context, Emit, Op.Index, Ternary: true); + } + public static void EmitVectorOpByElemF(AILEmitterCtx Context, Action Emit, int Elem, bool Ternary) { AOpCodeSimdReg Op = (AOpCodeSimdReg)Context.CurrOp; @@ -341,6 +341,54 @@ namespace ChocolArm64.Instruction } } + public static void EmitVectorBinaryOpByElemSx(AILEmitterCtx Context, Action Emit) + { + AOpCodeSimdRegElem Op = (AOpCodeSimdRegElem)Context.CurrOp; + + EmitVectorOpByElem(Context, Emit, Op.Index, false, true); + } + + public static void EmitVectorBinaryOpByElemZx(AILEmitterCtx Context, Action Emit) + { + AOpCodeSimdRegElem Op = (AOpCodeSimdRegElem)Context.CurrOp; + + EmitVectorOpByElem(Context, Emit, Op.Index, false, false); + } + + public static void EmitVectorTernaryOpByElemZx(AILEmitterCtx Context, Action Emit) + { + AOpCodeSimdRegElem Op = (AOpCodeSimdRegElem)Context.CurrOp; + + EmitVectorOpByElem(Context, Emit, Op.Index, true, false); + } + + public static void EmitVectorOpByElem(AILEmitterCtx Context, Action Emit, int Elem, bool Ternary, bool Signed) + { + AOpCodeSimdReg Op = (AOpCodeSimdReg)Context.CurrOp; + + int Bytes = Context.CurrOp.GetBitsCount() >> 3; + + for (int Index = 0; Index < (Bytes >> Op.Size); Index++) + { + if (Ternary) + { + EmitVectorExtract(Context, Op.Rd, Index, Op.Size, Signed); + } + + EmitVectorExtract(Context, Op.Rn, Index, Op.Size, Signed); + EmitVectorExtract(Context, Op.Rm, Index, Op.Size, Signed); + + Emit(); + + EmitVectorInsert(Context, Op.Rd, Index, Op.Size); + } + + if (Op.RegisterSize == ARegisterSize.SIMD64) + { + EmitVectorZeroUpper(Context, Op.Rd); + } + } + public static void EmitVectorImmUnaryOp(AILEmitterCtx Context, Action Emit) { EmitVectorImmOp(Context, Emit, false); diff --git a/Ryujinx.Core/OsHle/ErrorCode.cs b/Ryujinx.Core/OsHle/ErrorCode.cs new file mode 100644 index 0000000000..4210b23000 --- /dev/null +++ b/Ryujinx.Core/OsHle/ErrorCode.cs @@ -0,0 +1,10 @@ +namespace Ryujinx.Core.OsHle +{ + static class ErrorCode + { + public static uint MakeError(ErrorModule Module, int Code) + { + return (uint)Module | ((uint)Code << 9); + } + } +} \ No newline at end of file diff --git a/Ryujinx.Core/OsHle/Services/ErrorModule.cs b/Ryujinx.Core/OsHle/ErrorModule.cs similarity index 98% rename from Ryujinx.Core/OsHle/Services/ErrorModule.cs rename to Ryujinx.Core/OsHle/ErrorModule.cs index 78af6195db..f23e8d2703 100644 --- a/Ryujinx.Core/OsHle/Services/ErrorModule.cs +++ b/Ryujinx.Core/OsHle/ErrorModule.cs @@ -1,4 +1,4 @@ -namespace Ryujinx.Core.OsHle.IpcServices +namespace Ryujinx.Core.OsHle { enum ErrorModule { diff --git a/Ryujinx.Core/OsHle/KernelErr.cs b/Ryujinx.Core/OsHle/KernelErr.cs new file mode 100644 index 0000000000..19983af19d --- /dev/null +++ b/Ryujinx.Core/OsHle/KernelErr.cs @@ -0,0 +1,11 @@ +namespace Ryujinx.Core.OsHle +{ + static class KernelErr + { + public const int InvalidMemRange = 110; + public const int InvalidHandle = 114; + public const int Timeout = 117; + public const int InvalidInfo = 120; + public const int InvalidIpcReq = 123; + } +} \ No newline at end of file diff --git a/Ryujinx.Core/OsHle/Services/ErrorCode.cs b/Ryujinx.Core/OsHle/Services/ErrorCode.cs deleted file mode 100644 index a4e197b225..0000000000 --- a/Ryujinx.Core/OsHle/Services/ErrorCode.cs +++ /dev/null @@ -1,10 +0,0 @@ -namespace Ryujinx.Core.OsHle.IpcServices -{ - static class ErrorCode - { - public static long MakeError(ErrorModule Module, int Code) - { - return (int)Module | (Code << 9); - } - } -} \ No newline at end of file diff --git a/Ryujinx.Core/OsHle/Services/FspSrv/IFileSystem.cs b/Ryujinx.Core/OsHle/Services/FspSrv/IFileSystem.cs index 569a7dd6b4..62bcb8e8b2 100644 --- a/Ryujinx.Core/OsHle/Services/FspSrv/IFileSystem.cs +++ b/Ryujinx.Core/OsHle/Services/FspSrv/IFileSystem.cs @@ -4,7 +4,7 @@ using System.Collections.Generic; using System.IO; using System.Text; -using static Ryujinx.Core.OsHle.IpcServices.ErrorCode; +using static Ryujinx.Core.OsHle.ErrorCode; using static Ryujinx.Core.OsHle.IpcServices.ObjHelper; namespace Ryujinx.Core.OsHle.IpcServices.FspSrv diff --git a/Ryujinx.Core/OsHle/Svc/SvcMemory.cs b/Ryujinx.Core/OsHle/Svc/SvcMemory.cs index 6969d37936..1bb3011b83 100644 --- a/Ryujinx.Core/OsHle/Svc/SvcMemory.cs +++ b/Ryujinx.Core/OsHle/Svc/SvcMemory.cs @@ -2,6 +2,8 @@ using ChocolArm64.Memory; using ChocolArm64.State; using Ryujinx.Core.OsHle.Handles; +using static Ryujinx.Core.OsHle.ErrorCode; + namespace Ryujinx.Core.OsHle.Svc { partial class SvcHandler @@ -23,7 +25,7 @@ namespace Ryujinx.Core.OsHle.Svc CurrentHeapSize = Size; - ThreadState.X0 = (int)SvcResult.Success; + ThreadState.X0 = 0; ThreadState.X1 = (ulong)Position; } @@ -44,7 +46,7 @@ namespace Ryujinx.Core.OsHle.Svc Memory.Manager.SetAttrBit(Position, Size, 3); } - ThreadState.X0 = (int)SvcResult.Success; + ThreadState.X0 = 0; } private void SvcMapMemory(AThreadState ThreadState) @@ -61,7 +63,7 @@ namespace Ryujinx.Core.OsHle.Svc Memory.Manager.SetAttrBit(Src, Size, 0); - ThreadState.X0 = (int)SvcResult.Success; + ThreadState.X0 = 0; } private void SvcUnmapMemory(AThreadState ThreadState) @@ -78,7 +80,7 @@ namespace Ryujinx.Core.OsHle.Svc Memory.Manager.ClearAttrBit(Src, Size, 0); - ThreadState.X0 = (int)SvcResult.Success; + ThreadState.X0 = 0; } private void SvcQueryMemory(AThreadState ThreadState) @@ -86,12 +88,13 @@ namespace Ryujinx.Core.OsHle.Svc long InfoPtr = (long)ThreadState.X0; long Position = (long)ThreadState.X2; + Position &= uint.MaxValue; + AMemoryMapInfo MapInfo = Memory.Manager.GetMapInfo(Position); if (MapInfo == null) { - //TODO: Correct error code. - ThreadState.X0 = ulong.MaxValue; + ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidMemRange); return; } @@ -106,7 +109,7 @@ namespace Ryujinx.Core.OsHle.Svc Memory.WriteInt32(InfoPtr + 0x24, 0); //TODO: X1. - ThreadState.X0 = (int)SvcResult.Success; + ThreadState.X0 = 0; ThreadState.X1 = 0; } @@ -127,7 +130,7 @@ namespace Ryujinx.Core.OsHle.Svc Memory.Manager.Map(Src, Size, (int)MemoryType.SharedMemory, (AMemoryPerm)Perm); - ThreadState.X0 = (int)SvcResult.Success; + ThreadState.X0 = 0; } //TODO: Error codes. @@ -143,7 +146,7 @@ namespace Ryujinx.Core.OsHle.Svc if (HndData != null) { - ThreadState.X0 = (int)SvcResult.Success; + ThreadState.X0 = 0; } //TODO: Error codes. @@ -164,7 +167,7 @@ namespace Ryujinx.Core.OsHle.Svc int Handle = Ns.Os.Handles.GenerateId(HndData); ThreadState.X1 = (ulong)Handle; - ThreadState.X0 = (int)SvcResult.Success; + ThreadState.X0 = 0; } } } \ No newline at end of file diff --git a/Ryujinx.Core/OsHle/Svc/SvcResult.cs b/Ryujinx.Core/OsHle/Svc/SvcResult.cs deleted file mode 100644 index a5be9a9457..0000000000 --- a/Ryujinx.Core/OsHle/Svc/SvcResult.cs +++ /dev/null @@ -1,11 +0,0 @@ -namespace Ryujinx.Core.OsHle.Svc -{ - enum SvcResult - { - Success = 0, - ErrBadHandle = 0xe401, - ErrTimeout = 0xea01, - ErrBadInfo = 0xf001, - ErrBadIpcReq = 0xf601 - } -} \ No newline at end of file diff --git a/Ryujinx.Core/OsHle/Svc/SvcSystem.cs b/Ryujinx.Core/OsHle/Svc/SvcSystem.cs index d0459f2f24..8a0a391750 100644 --- a/Ryujinx.Core/OsHle/Svc/SvcSystem.cs +++ b/Ryujinx.Core/OsHle/Svc/SvcSystem.cs @@ -7,6 +7,8 @@ using Ryujinx.Core.OsHle.IpcServices; using System; using System.Threading; +using static Ryujinx.Core.OsHle.ErrorCode; + namespace Ryujinx.Core.OsHle.Svc { partial class SvcHandler @@ -26,7 +28,7 @@ namespace Ryujinx.Core.OsHle.Svc //TODO: Implement events. - ThreadState.X0 = (int)SvcResult.Success; + ThreadState.X0 = 0; } private void SvcCloseHandle(AThreadState ThreadState) @@ -35,7 +37,7 @@ namespace Ryujinx.Core.OsHle.Svc Ns.Os.CloseHandle(Handle); - ThreadState.X0 = (int)SvcResult.Success; + ThreadState.X0 = 0; } private void SvcResetSignal(AThreadState ThreadState) @@ -44,7 +46,7 @@ namespace Ryujinx.Core.OsHle.Svc //TODO: Implement events. - ThreadState.X0 = (int)SvcResult.Success; + ThreadState.X0 = 0; } private void SvcWaitSynchronization(AThreadState ThreadState) @@ -60,7 +62,7 @@ namespace Ryujinx.Core.OsHle.Svc Process.Scheduler.Suspend(CurrThread.ProcessorId); Process.Scheduler.Resume(CurrThread); - ThreadState.X0 = (int)SvcResult.Success; + ThreadState.X0 = 0; } private void SvcGetSystemTick(AThreadState ThreadState) @@ -81,7 +83,7 @@ namespace Ryujinx.Core.OsHle.Svc HSession Session = new HSession(ServiceFactory.MakeService(Name)); ThreadState.X1 = (ulong)Ns.Os.Handles.GenerateId(Session); - ThreadState.X0 = (int)SvcResult.Success; + ThreadState.X0 = 0; } private void SvcSendSyncRequest(AThreadState ThreadState) @@ -127,11 +129,11 @@ namespace Ryujinx.Core.OsHle.Svc byte[] Response = AMemoryHelper.ReadBytes(Memory, CmdPtr, (int)Size); - ThreadState.X0 = (int)SvcResult.Success; + ThreadState.X0 = 0; } else { - ThreadState.X0 = (int)SvcResult.ErrBadIpcReq; + ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidIpcReq); } Thread.Yield(); @@ -157,7 +159,7 @@ namespace Ryujinx.Core.OsHle.Svc Logging.Info($"SvcOutputDebugString: {Str}"); - ThreadState.X0 = (int)SvcResult.Success; + ThreadState.X0 = 0; } private void SvcGetInfo(AThreadState ThreadState) @@ -171,7 +173,7 @@ namespace Ryujinx.Core.OsHle.Svc if (InfoType == 18 || InfoType == 19) { - ThreadState.X0 = (int)SvcResult.ErrBadInfo; + ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidInfo); return; } @@ -233,7 +235,7 @@ namespace Ryujinx.Core.OsHle.Svc default: throw new NotImplementedException($"SvcGetInfo: {InfoType} {Handle} {InfoId}"); } - ThreadState.X0 = (int)SvcResult.Success; + ThreadState.X0 = 0; } } } diff --git a/Ryujinx.Core/OsHle/Svc/SvcThread.cs b/Ryujinx.Core/OsHle/Svc/SvcThread.cs index cc0f980be3..6afd2e6106 100644 --- a/Ryujinx.Core/OsHle/Svc/SvcThread.cs +++ b/Ryujinx.Core/OsHle/Svc/SvcThread.cs @@ -28,7 +28,7 @@ namespace Ryujinx.Core.OsHle.Svc Priority, ProcessorId); - ThreadState.X0 = (int)SvcResult.Success; + ThreadState.X0 = 0; ThreadState.X1 = (ulong)Handle; } @@ -45,7 +45,7 @@ namespace Ryujinx.Core.OsHle.Svc { Process.Scheduler.StartThread(Thread); - ThreadState.X0 = (int)SvcResult.Success; + ThreadState.X0 = 0; } //TODO: Error codes. @@ -75,8 +75,8 @@ namespace Ryujinx.Core.OsHle.Svc if (Thread != null) { + ThreadState.X0 = 0; ThreadState.X1 = (ulong)Thread.Priority; - ThreadState.X0 = (int)SvcResult.Success; } //TODO: Error codes. @@ -93,7 +93,7 @@ namespace Ryujinx.Core.OsHle.Svc { Thread.Priority = Prio; - ThreadState.X0 = (int)SvcResult.Success; + ThreadState.X0 = 0; } //TODO: Error codes. @@ -101,7 +101,7 @@ namespace Ryujinx.Core.OsHle.Svc private void SvcSetThreadCoreMask(AThreadState ThreadState) { - ThreadState.X0 = (int)SvcResult.Success; + ThreadState.X0 = 0; //TODO: Error codes. } @@ -114,8 +114,8 @@ namespace Ryujinx.Core.OsHle.Svc if (Thread != null) { + ThreadState.X0 = 0; ThreadState.X1 = (ulong)Thread.ThreadId; - ThreadState.X0 = (int)SvcResult.Success; } //TODO: Error codes. diff --git a/Ryujinx.Core/OsHle/Svc/SvcThreadSync.cs b/Ryujinx.Core/OsHle/Svc/SvcThreadSync.cs index 2705272a39..6e488da589 100644 --- a/Ryujinx.Core/OsHle/Svc/SvcThreadSync.cs +++ b/Ryujinx.Core/OsHle/Svc/SvcThreadSync.cs @@ -1,6 +1,8 @@ using ChocolArm64.State; using Ryujinx.Core.OsHle.Handles; +using static Ryujinx.Core.OsHle.ErrorCode; + namespace Ryujinx.Core.OsHle.Svc { partial class SvcHandler @@ -19,7 +21,7 @@ namespace Ryujinx.Core.OsHle.Svc M.WaitForLock(RequestingThread, RequestingThreadHandle); - ThreadState.X0 = (int)SvcResult.Success; + ThreadState.X0 = 0; } private void SvcArbitrateUnlock(AThreadState ThreadState) @@ -31,7 +33,7 @@ namespace Ryujinx.Core.OsHle.Svc M.Unlock(); } - ThreadState.X0 = (int)SvcResult.Success; + ThreadState.X0 = 0; } private void SvcWaitProcessWideKeyAtomic(AThreadState ThreadState) @@ -55,14 +57,14 @@ namespace Ryujinx.Core.OsHle.Svc if (!Cv.WaitForSignal(Thread)) { - ThreadState.X0 = (int)SvcResult.ErrTimeout; + ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.Timeout); return; } M.WaitForLock(Thread, ThreadHandle); - ThreadState.X0 = (int)SvcResult.Success; + ThreadState.X0 = 0; } private void SvcSignalProcessWideKey(AThreadState ThreadState) @@ -77,7 +79,7 @@ namespace Ryujinx.Core.OsHle.Svc Cv.SetSignal(CurrThread, Count); } - ThreadState.X0 = (int)SvcResult.Success; + ThreadState.X0 = 0; } } } \ No newline at end of file From 2d9edddf8ccc32ce1b179a10e23bca1276289447 Mon Sep 17 00:00:00 2001 From: gdkchan Date: Mon, 5 Mar 2018 16:20:30 -0300 Subject: [PATCH 06/28] Remove QueryMemory workaround --- ChocolArm64/Decoder/AOpCodeSimdRegElem.cs | 2 -- Ryujinx.Core/OsHle/Svc/SvcMemory.cs | 2 -- 2 files changed, 4 deletions(-) diff --git a/ChocolArm64/Decoder/AOpCodeSimdRegElem.cs b/ChocolArm64/Decoder/AOpCodeSimdRegElem.cs index 127debd17f..721da88faa 100644 --- a/ChocolArm64/Decoder/AOpCodeSimdRegElem.cs +++ b/ChocolArm64/Decoder/AOpCodeSimdRegElem.cs @@ -27,8 +27,6 @@ namespace ChocolArm64.Decoder default: Emitter = AInstEmit.Und; return; } - - } } } \ No newline at end of file diff --git a/Ryujinx.Core/OsHle/Svc/SvcMemory.cs b/Ryujinx.Core/OsHle/Svc/SvcMemory.cs index 1bb3011b83..bf946e4f86 100644 --- a/Ryujinx.Core/OsHle/Svc/SvcMemory.cs +++ b/Ryujinx.Core/OsHle/Svc/SvcMemory.cs @@ -88,8 +88,6 @@ namespace Ryujinx.Core.OsHle.Svc long InfoPtr = (long)ThreadState.X0; long Position = (long)ThreadState.X2; - Position &= uint.MaxValue; - AMemoryMapInfo MapInfo = Memory.Manager.GetMapInfo(Position); if (MapInfo == null) From 39ed14a3d6304eeec8ce1fa34819b39c3ae28956 Mon Sep 17 00:00:00 2001 From: emmauss Date: Tue, 6 Mar 2018 15:25:26 +0200 Subject: [PATCH 07/28] stub IApplicationDisplayService:CloseDisplay (#52) --- .../OsHle/Services/Vi/IApplicationDisplayService.cs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/Ryujinx.Core/OsHle/Services/Vi/IApplicationDisplayService.cs b/Ryujinx.Core/OsHle/Services/Vi/IApplicationDisplayService.cs index a899cdd56d..04dfee158c 100644 --- a/Ryujinx.Core/OsHle/Services/Vi/IApplicationDisplayService.cs +++ b/Ryujinx.Core/OsHle/Services/Vi/IApplicationDisplayService.cs @@ -24,6 +24,7 @@ namespace Ryujinx.Core.OsHle.IpcServices.Vi { 102, GetManagerDisplayService }, { 103, GetIndirectDisplayTransactionService }, { 1010, OpenDisplay }, + { 1020, CloseDisplay }, { 2020, OpenLayer }, { 2030, CreateStrayLayer }, { 2101, SetLayerScalingMode }, @@ -70,6 +71,15 @@ namespace Ryujinx.Core.OsHle.IpcServices.Vi return 0; } + public long CloseDisplay(ServiceCtx Context) + { + int DisplayId = Context.RequestData.ReadInt32(); + + Context.Ns.Os.Displays.Delete(DisplayId); + + return 0; + } + public long OpenLayer(ServiceCtx Context) { long LayerId = Context.RequestData.ReadInt64(); From 4038e63de14bf12c0cfbe885e2cac44577fe8a6a Mon Sep 17 00:00:00 2001 From: emmauss Date: Tue, 6 Mar 2018 22:18:49 +0200 Subject: [PATCH 08/28] Implement basic performance statistics (#53) * implement basic frame time stats * added game frame time * made performancestatictics class non-static * report average framerate instead of current framerate --- Ryujinx.Core/Logging.cs | 2 - Ryujinx.Core/OsHle/Services/Vi/NvFlinger.cs | 2 + Ryujinx.Core/PerformanceStatistics.cs | 87 +++++++++++++++++++++ Ryujinx.Core/Switch.cs | 7 +- Ryujinx/Ui/GLScreen.cs | 7 +- 5 files changed, 100 insertions(+), 5 deletions(-) create mode 100644 Ryujinx.Core/PerformanceStatistics.cs diff --git a/Ryujinx.Core/Logging.cs b/Ryujinx.Core/Logging.cs index b14f266539..d544a5d64f 100644 --- a/Ryujinx.Core/Logging.cs +++ b/Ryujinx.Core/Logging.cs @@ -22,8 +22,6 @@ namespace Ryujinx.Core static Logging() { - ExecutionTime.Start(); - if (File.Exists(LogFileName)) File.Delete(LogFileName); } diff --git a/Ryujinx.Core/OsHle/Services/Vi/NvFlinger.cs b/Ryujinx.Core/OsHle/Services/Vi/NvFlinger.cs index 4b5f9819aa..720dd44f93 100644 --- a/Ryujinx.Core/OsHle/Services/Vi/NvFlinger.cs +++ b/Ryujinx.Core/OsHle/Services/Vi/NvFlinger.cs @@ -154,6 +154,8 @@ namespace Ryujinx.Core.OsHle.IpcServices.Android private long GbpQueueBuffer(ServiceCtx Context, BinaryReader ParcelReader) { + Context.Ns.Statistics.RecordGameFrameTime(); + //TODO: Errors. int Slot = ParcelReader.ReadInt32(); int Unknown4 = ParcelReader.ReadInt32(); diff --git a/Ryujinx.Core/PerformanceStatistics.cs b/Ryujinx.Core/PerformanceStatistics.cs new file mode 100644 index 0000000000..3740daa584 --- /dev/null +++ b/Ryujinx.Core/PerformanceStatistics.cs @@ -0,0 +1,87 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Text; +using System.Timers; + +namespace Ryujinx.Core +{ + public class PerformanceStatistics + { + Stopwatch ExecutionTime = new Stopwatch(); + Timer ResetTimer = new Timer(1000); + + long CurrentGameFrameEnded; + long CurrentSystemFrameEnded; + long CurrentSystemFrameStart; + long LastGameFrameEnded; + long LastSystemFrameEnded; + + double AccumulatedGameFrameTime; + double AccumulatedSystemFrameTime; + double CurrentGameFrameTime; + double CurrentSystemFrameTime; + double PreviousGameFrameTime; + double PreviousSystemFrameTime; + public double GameFrameRate { get; private set; } + public double SystemFrameRate { get; private set; } + public long SystemFramesRendered; + public long GameFramesRendered; + public long ElapsedMilliseconds => ExecutionTime.ElapsedMilliseconds; + public long ElapsedMicroseconds => (long) + (((double)ExecutionTime.ElapsedTicks / Stopwatch.Frequency) * 1000000); + public long ElapsedNanoseconds => (long) + (((double)ExecutionTime.ElapsedTicks / Stopwatch.Frequency) * 1000000000); + + public PerformanceStatistics() + { + ExecutionTime.Start(); + ResetTimer.Elapsed += ResetTimerElapsed; + ResetTimer.AutoReset = true; + ResetTimer.Start(); + } + + private void ResetTimerElapsed(object sender, ElapsedEventArgs e) + { + ResetStatistics(); + } + + public void StartSystemFrame() + { + PreviousSystemFrameTime = CurrentSystemFrameTime; + LastSystemFrameEnded = CurrentSystemFrameEnded; + CurrentSystemFrameStart = ElapsedMicroseconds; + } + + public void EndSystemFrame() + { + CurrentSystemFrameEnded = ElapsedMicroseconds; + CurrentSystemFrameTime = CurrentSystemFrameEnded - CurrentSystemFrameStart; + AccumulatedSystemFrameTime += CurrentSystemFrameTime; + SystemFramesRendered++; + } + + public void RecordGameFrameTime() + { + CurrentGameFrameEnded = ElapsedMicroseconds; + CurrentGameFrameTime = CurrentGameFrameEnded - LastGameFrameEnded; + PreviousGameFrameTime = CurrentGameFrameTime; + LastGameFrameEnded = CurrentGameFrameEnded; + AccumulatedGameFrameTime += CurrentGameFrameTime; + GameFramesRendered++; + } + + public void ResetStatistics() + { + GameFrameRate = 1000 / ((AccumulatedGameFrameTime / GameFramesRendered) / 1000); + GameFrameRate = double.IsNaN(GameFrameRate) ? 0 : GameFrameRate; + SystemFrameRate = 1000 / ((AccumulatedSystemFrameTime / SystemFramesRendered) / 1000); + SystemFrameRate = double.IsNaN(SystemFrameRate) ? 0 : SystemFrameRate; + + GameFramesRendered = 0; + SystemFramesRendered = 0; + AccumulatedGameFrameTime = 0; + AccumulatedSystemFrameTime = 0; + } + } +} diff --git a/Ryujinx.Core/Switch.cs b/Ryujinx.Core/Switch.cs index 3f7b1e2b81..1acd87f018 100644 --- a/Ryujinx.Core/Switch.cs +++ b/Ryujinx.Core/Switch.cs @@ -17,8 +17,9 @@ namespace Ryujinx.Core internal Horizon Os { get; private set; } internal VirtualFs VFs { get; private set; } - public Hid Hid { get; private set; } - public SetSys Settings { get; private set; } + public Hid Hid { get; private set; } + public SetSys Settings { get; private set; } + public PerformanceStatistics Statistics { get; private set; } public event EventHandler Finish; @@ -32,6 +33,8 @@ namespace Ryujinx.Core Hid = new Hid(Ram); + Statistics = new PerformanceStatistics(); + Os = new Horizon(this); Os.HidSharedMem.MemoryMapped += Hid.ShMemMap; diff --git a/Ryujinx/Ui/GLScreen.cs b/Ryujinx/Ui/GLScreen.cs index 4c7cee4672..b0dca81b77 100644 --- a/Ryujinx/Ui/GLScreen.cs +++ b/Ryujinx/Ui/GLScreen.cs @@ -166,9 +166,12 @@ namespace Ryujinx protected override void OnRenderFrame(FrameEventArgs e) { + Ns.Statistics.StartSystemFrame(); + GL.Viewport(0, 0, Width, Height); - Title = $"Ryujinx Screen - (Vsync: {VSync} - FPS: {1f / e.Time:0})"; + Title = $"Ryujinx Screen - (Vsync: {VSync} - FPS: {Ns.Statistics.SystemFrameRate:0} - Guest FPS: " + + $"{Ns.Statistics.GameFrameRate:0})"; GL.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit); @@ -176,6 +179,8 @@ namespace Ryujinx Renderer.Render(); SwapBuffers(); + + Ns.Statistics.EndSystemFrame(); } protected override void OnResize(EventArgs e) From 4f177c9ee7452274f5e792349e9b443d78a27816 Mon Sep 17 00:00:00 2001 From: gdkchan Date: Tue, 6 Mar 2018 17:27:50 -0300 Subject: [PATCH 09/28] More Vi/NvFlinger/NvDrv stubs, allow paths starting with //, do not allow paths that don't start with at least a /, increase map region size --- Ryujinx.Core/OsHle/MemoryRegions.cs | 2 +- .../OsHle/Services/Nv/ServiceNvDrv.cs | 20 +++++++++++++++++++ .../Services/Vi/IApplicationDisplayService.cs | 6 ++++++ .../Services/Vi/IManagerDisplayService.cs | 10 ++++++++-- Ryujinx.Core/OsHle/Services/Vi/NvFlinger.cs | 6 ++++++ Ryujinx.Core/VirtualFs.cs | 10 +++++++++- 6 files changed, 50 insertions(+), 4 deletions(-) diff --git a/Ryujinx.Core/OsHle/MemoryRegions.cs b/Ryujinx.Core/OsHle/MemoryRegions.cs index e362ba9fd7..7f5ab0edeb 100644 --- a/Ryujinx.Core/OsHle/MemoryRegions.cs +++ b/Ryujinx.Core/OsHle/MemoryRegions.cs @@ -7,7 +7,7 @@ namespace Ryujinx.Core.OsHle public const long AddrSpaceStart = 0x08000000; public const long MapRegionAddress = 0x10000000; - public const long MapRegionSize = 0x10000000; + public const long MapRegionSize = 0x20000000; public const long MainStackSize = 0x100000; diff --git a/Ryujinx.Core/OsHle/Services/Nv/ServiceNvDrv.cs b/Ryujinx.Core/OsHle/Services/Nv/ServiceNvDrv.cs index 0ea1d2ac0a..515c15e034 100644 --- a/Ryujinx.Core/OsHle/Services/Nv/ServiceNvDrv.cs +++ b/Ryujinx.Core/OsHle/Services/Nv/ServiceNvDrv.cs @@ -38,6 +38,7 @@ namespace Ryujinx.Core.OsHle.IpcServices.NvServices { ("/dev/nvmap", 0x0101), NvMapIocCreate }, { ("/dev/nvmap", 0x0103), NvMapIocFromId }, { ("/dev/nvmap", 0x0104), NvMapIocAlloc }, + { ("/dev/nvmap", 0x0105), NvMapIocFree }, { ("/dev/nvmap", 0x0109), NvMapIocParam }, { ("/dev/nvmap", 0x010e), NvMapIocGetId }, }; @@ -585,6 +586,25 @@ namespace Ryujinx.Core.OsHle.IpcServices.NvServices return 0; } + private static long NvMapIocFree(ServiceCtx Context) + { + long Position = Context.Request.GetSendBuffPtr(); + + MemReader Reader = new MemReader(Context.Memory, Position); + MemWriter Writer = new MemWriter(Context.Memory, Position + 8); + + int Handle = Reader.ReadInt32(); + int Padding = Reader.ReadInt32(); + + HNvMap NvMap = Context.Ns.Os.Handles.GetData(Handle); + + Writer.WriteInt64(0); + Writer.WriteInt32(NvMap.Size); + Writer.WriteInt32(0); + + return 0; + } + private static long NvMapIocParam(ServiceCtx Context) { long Position = Context.Request.GetSendBuffPtr(); diff --git a/Ryujinx.Core/OsHle/Services/Vi/IApplicationDisplayService.cs b/Ryujinx.Core/OsHle/Services/Vi/IApplicationDisplayService.cs index 04dfee158c..4e40b99b3d 100644 --- a/Ryujinx.Core/OsHle/Services/Vi/IApplicationDisplayService.cs +++ b/Ryujinx.Core/OsHle/Services/Vi/IApplicationDisplayService.cs @@ -26,6 +26,7 @@ namespace Ryujinx.Core.OsHle.IpcServices.Vi { 1010, OpenDisplay }, { 1020, CloseDisplay }, { 2020, OpenLayer }, + { 2021, CloseLayer }, { 2030, CreateStrayLayer }, { 2101, SetLayerScalingMode }, { 5202, GetDisplayVSyncEvent } @@ -96,6 +97,11 @@ namespace Ryujinx.Core.OsHle.IpcServices.Vi return 0; } + public long CloseLayer(ServiceCtx Context) + { + return 0; + } + public long CreateStrayLayer(ServiceCtx Context) { long LayerFlags = Context.RequestData.ReadInt64(); diff --git a/Ryujinx.Core/OsHle/Services/Vi/IManagerDisplayService.cs b/Ryujinx.Core/OsHle/Services/Vi/IManagerDisplayService.cs index 5adee78d1e..69dbff47a9 100644 --- a/Ryujinx.Core/OsHle/Services/Vi/IManagerDisplayService.cs +++ b/Ryujinx.Core/OsHle/Services/Vi/IManagerDisplayService.cs @@ -13,8 +13,9 @@ namespace Ryujinx.Core.OsHle.IpcServices.Vi { m_Commands = new Dictionary() { - { 2010, CreateManagedLayer }, - { 6000, AddToLayerStack } + { 2010, CreateManagedLayer }, + { 2011, DestroyManagedLayer }, + { 6000, AddToLayerStack } }; } @@ -25,6 +26,11 @@ namespace Ryujinx.Core.OsHle.IpcServices.Vi return 0; } + public long DestroyManagedLayer(ServiceCtx Context) + { + return 0; + } + public static long AddToLayerStack(ServiceCtx Context) { return 0; diff --git a/Ryujinx.Core/OsHle/Services/Vi/NvFlinger.cs b/Ryujinx.Core/OsHle/Services/Vi/NvFlinger.cs index 720dd44f93..740a35f9c4 100644 --- a/Ryujinx.Core/OsHle/Services/Vi/NvFlinger.cs +++ b/Ryujinx.Core/OsHle/Services/Vi/NvFlinger.cs @@ -70,6 +70,7 @@ namespace Ryujinx.Core.OsHle.IpcServices.Android { ("android.gui.IGraphicBufferProducer", 0x8), GbpCancelBuffer }, { ("android.gui.IGraphicBufferProducer", 0x9), GbpQuery }, { ("android.gui.IGraphicBufferProducer", 0xa), GbpConnect }, + { ("android.gui.IGraphicBufferProducer", 0xb), GbpDisconnect }, { ("android.gui.IGraphicBufferProducer", 0xe), GbpPreallocBuffer } }; @@ -212,6 +213,11 @@ namespace Ryujinx.Core.OsHle.IpcServices.Android return MakeReplyParcel(Context, 1280, 720, 0, 0, 0); } + private long GbpDisconnect(ServiceCtx Context, BinaryReader ParcelReader) + { + return MakeReplyParcel(Context, 0); + } + private long GbpPreallocBuffer(ServiceCtx Context, BinaryReader ParcelReader) { int Slot = ParcelReader.ReadInt32(); diff --git a/Ryujinx.Core/VirtualFs.cs b/Ryujinx.Core/VirtualFs.cs index 195fb6a3c6..c0858e0ee6 100644 --- a/Ryujinx.Core/VirtualFs.cs +++ b/Ryujinx.Core/VirtualFs.cs @@ -18,10 +18,18 @@ namespace Ryujinx.Core public string GetFullPath(string BasePath, string FileName) { - if (FileName.StartsWith('/')) + if (FileName.StartsWith("//")) + { + FileName = FileName.Substring(2); + } + else if (FileName.StartsWith('/')) { FileName = FileName.Substring(1); } + else + { + return null; + } string FullPath = Path.GetFullPath(Path.Combine(BasePath, FileName)); From be0e4007dc92e24a77bdc36a40d2450c41d9b560 Mon Sep 17 00:00:00 2001 From: gdkchan Date: Tue, 6 Mar 2018 21:36:49 -0300 Subject: [PATCH 10/28] Add SMLAL (vector), fix EXT instruction --- ChocolArm64/AOpCodeTable.cs | 1 + ChocolArm64/Decoder/AOpCodeSimdExt.cs | 2 +- .../Instruction/AInstEmitSimdArithmetic.cs | 9 ++++++++ .../Instruction/AInstEmitSimdHelper.cs | 21 ++++++++++++++++--- ChocolArm64/Instruction/AInstEmitSimdMove.cs | 13 +++++++----- 5 files changed, 37 insertions(+), 9 deletions(-) diff --git a/ChocolArm64/AOpCodeTable.cs b/ChocolArm64/AOpCodeTable.cs index 6b250b4b00..5afdbd1ffe 100644 --- a/ChocolArm64/AOpCodeTable.cs +++ b/ChocolArm64/AOpCodeTable.cs @@ -243,6 +243,7 @@ namespace ChocolArm64 Set("0x00111100>>>xxx100001xxxxxxxxxx", AInstEmit.Shrn_V, typeof(AOpCodeSimdShImm)); Set("0x001110<<1xxxxx011001xxxxxxxxxx", AInstEmit.Smax_V, typeof(AOpCodeSimdReg)); Set("0x001110<<1xxxxx011011xxxxxxxxxx", AInstEmit.Smin_V, typeof(AOpCodeSimdReg)); + Set("0x001110<<1xxxxx100000xxxxxxxxxx", AInstEmit.Smlal_V, typeof(AOpCodeSimdReg)); Set("0x001110<<1xxxxx110000xxxxxxxxxx", AInstEmit.Smull_V, typeof(AOpCodeSimdReg)); Set("0>001110<<1xxxxx010001xxxxxxxxxx", AInstEmit.Sshl_V, typeof(AOpCodeSimdReg)); Set("0x00111100>>>xxx101001xxxxxxxxxx", AInstEmit.Sshll_V, typeof(AOpCodeSimdShImm)); diff --git a/ChocolArm64/Decoder/AOpCodeSimdExt.cs b/ChocolArm64/Decoder/AOpCodeSimdExt.cs index cf22d6546f..888e447030 100644 --- a/ChocolArm64/Decoder/AOpCodeSimdExt.cs +++ b/ChocolArm64/Decoder/AOpCodeSimdExt.cs @@ -8,7 +8,7 @@ namespace ChocolArm64.Decoder public AOpCodeSimdExt(AInst Inst, long Position, int OpCode) : base(Inst, Position, OpCode) { - int Imm4 = (OpCode >> 11) & 0xf; + Imm4 = (OpCode >> 11) & 0xf; } } } \ No newline at end of file diff --git a/ChocolArm64/Instruction/AInstEmitSimdArithmetic.cs b/ChocolArm64/Instruction/AInstEmitSimdArithmetic.cs index 9f5cc64f4c..b2d190f1a9 100644 --- a/ChocolArm64/Instruction/AInstEmitSimdArithmetic.cs +++ b/ChocolArm64/Instruction/AInstEmitSimdArithmetic.cs @@ -374,6 +374,15 @@ namespace ChocolArm64.Instruction EmitVectorBinaryOpSx(Context, () => Context.EmitCall(MthdInfo)); } + public static void Smlal_V(AILEmitterCtx Context) + { + EmitVectorWidenRnRmTernaryOpSx(Context, () => + { + Context.Emit(OpCodes.Mul); + Context.Emit(OpCodes.Add); + }); + } + public static void Smull_V(AILEmitterCtx Context) { EmitVectorWidenRnRmBinaryOpSx(Context, () => Context.Emit(OpCodes.Mul)); diff --git a/ChocolArm64/Instruction/AInstEmitSimdHelper.cs b/ChocolArm64/Instruction/AInstEmitSimdHelper.cs index 33e4d54874..e6ead99aa2 100644 --- a/ChocolArm64/Instruction/AInstEmitSimdHelper.cs +++ b/ChocolArm64/Instruction/AInstEmitSimdHelper.cs @@ -459,15 +459,25 @@ namespace ChocolArm64.Instruction public static void EmitVectorWidenRnRmBinaryOpSx(AILEmitterCtx Context, Action Emit) { - EmitVectorWidenRnRmBinaryOp(Context, Emit, true); + EmitVectorWidenRnRmOp(Context, Emit, false, true); } public static void EmitVectorWidenRnRmBinaryOpZx(AILEmitterCtx Context, Action Emit) { - EmitVectorWidenRnRmBinaryOp(Context, Emit, false); + EmitVectorWidenRnRmOp(Context, Emit, false, false); } - public static void EmitVectorWidenRnRmBinaryOp(AILEmitterCtx Context, Action Emit, bool Signed) + public static void EmitVectorWidenRnRmTernaryOpSx(AILEmitterCtx Context, Action Emit) + { + EmitVectorWidenRnRmOp(Context, Emit, true, true); + } + + public static void EmitVectorWidenRnRmTernaryOpZx(AILEmitterCtx Context, Action Emit) + { + EmitVectorWidenRnRmOp(Context, Emit, true, false); + } + + public static void EmitVectorWidenRnRmOp(AILEmitterCtx Context, Action Emit, bool Ternary, bool Signed) { AOpCodeSimdReg Op = (AOpCodeSimdReg)Context.CurrOp; @@ -477,6 +487,11 @@ namespace ChocolArm64.Instruction for (int Index = 0; Index < Elems; Index++) { + if (Ternary) + { + EmitVectorExtract(Context, Op.Rd, Index, Op.Size + 1, Signed); + } + EmitVectorExtract(Context, Op.Rn, Part + Index, Op.Size, Signed); EmitVectorExtract(Context, Op.Rm, Part + Index, Op.Size, Signed); diff --git a/ChocolArm64/Instruction/AInstEmitSimdMove.cs b/ChocolArm64/Instruction/AInstEmitSimdMove.cs index a4e533709e..3f427ad8ad 100644 --- a/ChocolArm64/Instruction/AInstEmitSimdMove.cs +++ b/ChocolArm64/Instruction/AInstEmitSimdMove.cs @@ -63,15 +63,18 @@ namespace ChocolArm64.Instruction int Bytes = Context.CurrOp.GetBitsCount() >> 3; + int Position = Op.Imm4; + for (int Index = 0; Index < Bytes; Index++) { - int Position = Op.Imm4 + Index; + int Reg = Op.Imm4 + Index < Bytes ? Op.Rn : Op.Rm; - int Reg = Position < Bytes ? Op.Rn : Op.Rm; + if (Position == Bytes) + { + Position = 0; + } - Position &= Bytes - 1; - - EmitVectorExtractZx(Context, Reg, Position, 0); + EmitVectorExtractZx(Context, Reg, Position++, 0); EmitVectorInsert(Context, Op.Rd, Index, 0); } From 5912bd2beb42e1853fdcf11e4bb87e063a0ef35b Mon Sep 17 00:00:00 2001 From: gdkchan Date: Fri, 9 Mar 2018 23:12:57 -0300 Subject: [PATCH 11/28] Disable memory checks by default, even on debug, move ram memory allocation inside the CPU, since the size if fixed anyway, better heap region size --- ChocolArm64/AOptimizations.cs | 2 +- ChocolArm64/AThread.cs | 2 +- ChocolArm64/Memory/AMemory.cs | 113 ++++------ ChocolArm64/Memory/AMemoryMgr.cs | 6 +- Ryujinx.Core/Hid/Hid.cs | 222 ++++++++++---------- Ryujinx.Core/OsHle/MemoryRegions.cs | 5 +- Ryujinx.Core/OsHle/Process.cs | 2 +- Ryujinx.Core/OsHle/Services/Vi/NvFlinger.cs | 6 +- Ryujinx.Core/OsHle/Svc/SvcSystem.cs | 2 +- Ryujinx.Core/PerformanceStatistics.cs | 5 +- Ryujinx.Core/Switch.cs | 11 +- Ryujinx.Tests/Cpu/CpuTest.cs | 8 +- 12 files changed, 177 insertions(+), 207 deletions(-) diff --git a/ChocolArm64/AOptimizations.cs b/ChocolArm64/AOptimizations.cs index cbfd1ce51b..0cdbd76e28 100644 --- a/ChocolArm64/AOptimizations.cs +++ b/ChocolArm64/AOptimizations.cs @@ -1,4 +1,4 @@ public static class AOptimizations { - + public static bool EnableMemoryChecks = false; } \ No newline at end of file diff --git a/ChocolArm64/AThread.cs b/ChocolArm64/AThread.cs index 6e018db684..cec268175f 100644 --- a/ChocolArm64/AThread.cs +++ b/ChocolArm64/AThread.cs @@ -10,7 +10,7 @@ namespace ChocolArm64 public AThreadState ThreadState { get; private set; } public AMemory Memory { get; private set; } - public long EntryPoint { get; private set; } + private long EntryPoint; private ATranslator Translator; diff --git a/ChocolArm64/Memory/AMemory.cs b/ChocolArm64/Memory/AMemory.cs index 0d202fed30..7f2dd7d3f4 100644 --- a/ChocolArm64/Memory/AMemory.cs +++ b/ChocolArm64/Memory/AMemory.cs @@ -3,10 +3,11 @@ using ChocolArm64.State; using System; using System.Collections.Generic; using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; namespace ChocolArm64.Memory { - public unsafe class AMemory + public unsafe class AMemory : IDisposable { private const long ErgMask = (4 << AThreadState.ErgSizeLog2) - 1; @@ -39,9 +40,11 @@ namespace ChocolArm64.Memory private HashSet ExAddrs; + public IntPtr Ram { get; private set; } + private byte* RamPtr; - public AMemory(IntPtr Ram) + public AMemory() { Manager = new AMemoryMgr(); @@ -49,6 +52,8 @@ namespace ChocolArm64.Memory ExAddrs = new HashSet(); + Ram = Marshal.AllocHGlobal((IntPtr)AMemoryMgr.RamSize + AMemoryMgr.PageSize); + RamPtr = (byte*)Ram; } @@ -142,9 +147,7 @@ namespace ChocolArm64.Memory [MethodImpl(MethodImplOptions.AggressiveInlining)] public byte ReadByte(long Position) { -#if DEBUG EnsureAccessIsValid(Position, AMemoryPerm.Read); -#endif return *((byte*)(RamPtr + (uint)Position)); } @@ -152,9 +155,8 @@ namespace ChocolArm64.Memory [MethodImpl(MethodImplOptions.AggressiveInlining)] public ushort ReadUInt16(long Position) { -#if DEBUG - EnsureAccessIsValid(Position, AMemoryPerm.Read); -#endif + EnsureAccessIsValid(Position + 0, AMemoryPerm.Read); + EnsureAccessIsValid(Position + 1, AMemoryPerm.Read); return *((ushort*)(RamPtr + (uint)Position)); } @@ -162,9 +164,8 @@ namespace ChocolArm64.Memory [MethodImpl(MethodImplOptions.AggressiveInlining)] public uint ReadUInt32(long Position) { -#if DEBUG - EnsureAccessIsValid(Position, AMemoryPerm.Read); -#endif + EnsureAccessIsValid(Position + 0, AMemoryPerm.Read); + EnsureAccessIsValid(Position + 3, AMemoryPerm.Read); return *((uint*)(RamPtr + (uint)Position)); } @@ -172,9 +173,8 @@ namespace ChocolArm64.Memory [MethodImpl(MethodImplOptions.AggressiveInlining)] public ulong ReadUInt64(long Position) { -#if DEBUG - EnsureAccessIsValid(Position, AMemoryPerm.Read); -#endif + EnsureAccessIsValid(Position + 0, AMemoryPerm.Read); + EnsureAccessIsValid(Position + 7, AMemoryPerm.Read); return *((ulong*)(RamPtr + (uint)Position)); } @@ -182,50 +182,30 @@ namespace ChocolArm64.Memory [MethodImpl(MethodImplOptions.AggressiveInlining)] public AVec ReadVector8(long Position) { -#if DEBUG - EnsureAccessIsValid(Position, AMemoryPerm.Read); -#endif - return new AVec() { B0 = ReadByte(Position) }; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public AVec ReadVector16(long Position) { -#if DEBUG - EnsureAccessIsValid(Position, AMemoryPerm.Read); -#endif - return new AVec() { H0 = ReadUInt16(Position) }; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public AVec ReadVector32(long Position) { -#if DEBUG - EnsureAccessIsValid(Position, AMemoryPerm.Read); -#endif - return new AVec() { W0 = ReadUInt32(Position) }; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public AVec ReadVector64(long Position) { -#if DEBUG - EnsureAccessIsValid(Position, AMemoryPerm.Read); -#endif - return new AVec() { X0 = ReadUInt64(Position) }; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public AVec ReadVector128(long Position) { -#if DEBUG - EnsureAccessIsValid(Position, AMemoryPerm.Read); -#endif - return new AVec() { X0 = ReadUInt64(Position + 0), @@ -241,9 +221,7 @@ namespace ChocolArm64.Memory [MethodImpl(MethodImplOptions.AggressiveInlining)] public void WriteByte(long Position, byte Value) { -#if DEBUG EnsureAccessIsValid(Position, AMemoryPerm.Write); -#endif *((byte*)(RamPtr + (uint)Position)) = Value; } @@ -251,9 +229,8 @@ namespace ChocolArm64.Memory [MethodImpl(MethodImplOptions.AggressiveInlining)] public void WriteUInt16(long Position, ushort Value) { -#if DEBUG - EnsureAccessIsValid(Position, AMemoryPerm.Write); -#endif + EnsureAccessIsValid(Position + 0, AMemoryPerm.Write); + EnsureAccessIsValid(Position + 1, AMemoryPerm.Write); *((ushort*)(RamPtr + (uint)Position)) = Value; } @@ -261,9 +238,8 @@ namespace ChocolArm64.Memory [MethodImpl(MethodImplOptions.AggressiveInlining)] public void WriteUInt32(long Position, uint Value) { -#if DEBUG - EnsureAccessIsValid(Position, AMemoryPerm.Write); -#endif + EnsureAccessIsValid(Position + 0, AMemoryPerm.Write); + EnsureAccessIsValid(Position + 3, AMemoryPerm.Write); *((uint*)(RamPtr + (uint)Position)) = Value; } @@ -271,9 +247,8 @@ namespace ChocolArm64.Memory [MethodImpl(MethodImplOptions.AggressiveInlining)] public void WriteUInt64(long Position, ulong Value) { -#if DEBUG - EnsureAccessIsValid(Position, AMemoryPerm.Write); -#endif + EnsureAccessIsValid(Position + 0, AMemoryPerm.Write); + EnsureAccessIsValid(Position + 7, AMemoryPerm.Write); *((ulong*)(RamPtr + (uint)Position)) = Value; } @@ -281,64 +256,64 @@ namespace ChocolArm64.Memory [MethodImpl(MethodImplOptions.AggressiveInlining)] public void WriteVector8(long Position, AVec Value) { -#if DEBUG - EnsureAccessIsValid(Position, AMemoryPerm.Write); -#endif - WriteByte(Position, Value.B0); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void WriteVector16(long Position, AVec Value) { -#if DEBUG - EnsureAccessIsValid(Position, AMemoryPerm.Write); -#endif - WriteUInt16(Position, Value.H0); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void WriteVector32(long Position, AVec Value) { -#if DEBUG - EnsureAccessIsValid(Position, AMemoryPerm.Write); -#endif - WriteUInt32(Position, Value.W0); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void WriteVector64(long Position, AVec Value) { -#if DEBUG - EnsureAccessIsValid(Position, AMemoryPerm.Write); -#endif - WriteUInt64(Position, Value.X0); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void WriteVector128(long Position, AVec Value) { -#if DEBUG - EnsureAccessIsValid(Position, AMemoryPerm.Write); -#endif - WriteUInt64(Position + 0, Value.X0); WriteUInt64(Position + 8, Value.X1); } private void EnsureAccessIsValid(long Position, AMemoryPerm Perm) { - if (!Manager.IsMapped(Position)) +#if DEBUG + if (AOptimizations.EnableMemoryChecks) { - throw new VmmPageFaultException(Position); - } + if (!Manager.IsMapped(Position)) + { + throw new VmmPageFaultException(Position); + } - if (!Manager.HasPermission(Position, Perm)) + if (!Manager.HasPermission(Position, Perm)) + { + throw new VmmAccessViolationException(Position, Perm); + } + } +#endif + } + + public void Dispose() + { + Dispose(true); + } + + protected virtual void Dispose(bool disposing) + { + if (Ram != IntPtr.Zero) { - throw new VmmAccessViolationException(Position, Perm); + Marshal.FreeHGlobal(Ram); + + Ram = IntPtr.Zero; } } } diff --git a/ChocolArm64/Memory/AMemoryMgr.cs b/ChocolArm64/Memory/AMemoryMgr.cs index bc36445242..8a165b0796 100644 --- a/ChocolArm64/Memory/AMemoryMgr.cs +++ b/ChocolArm64/Memory/AMemoryMgr.cs @@ -4,8 +4,8 @@ namespace ChocolArm64.Memory { public class AMemoryMgr { - public const long AddrSize = RamSize; public const long RamSize = 4L * 1024 * 1024 * 1024; + public const long AddrSize = RamSize; private const int PTLvl0Bits = 10; private const int PTLvl1Bits = 10; @@ -19,8 +19,8 @@ namespace ChocolArm64.Memory private const int PTLvl1Mask = PTLvl1Size - 1; public const int PageMask = PageSize - 1; - private const int PTLvl0Bit = PTPageBits + PTLvl1Bits; - private const int PTLvl1Bit = PTPageBits; + private const int PTLvl0Bit = PTPageBits + PTLvl1Bits; + private const int PTLvl1Bit = PTPageBits; private enum PTMap { diff --git a/Ryujinx.Core/Hid/Hid.cs b/Ryujinx.Core/Hid/Hid.cs index 249747c152..169768892e 100644 --- a/Ryujinx.Core/Hid/Hid.cs +++ b/Ryujinx.Core/Hid/Hid.cs @@ -1,7 +1,5 @@ -using ChocolArm64.Memory; -using Ryujinx.Core.OsHle.Handles; +using Ryujinx.Core.OsHle.Handles; using System; -using System.Diagnostics; namespace Ryujinx.Core.Input { @@ -66,11 +64,15 @@ namespace Ryujinx.Core.Input private long[] ShMemPositions; - private IntPtr Ram; + private long CurrControllerEntry; + private long CurrTouchEntry; + private long CurrTouchSampleCounter; - public Hid(IntPtr Ram) + private Switch Ns; + + public Hid(Switch Ns) { - this.Ram = Ram; + this.Ns = Ns; ShMemLock = new object(); @@ -136,20 +138,20 @@ namespace Ryujinx.Core.Input HidControllerColorDesc SplitColorDesc = 0; - WriteInt32(BaseControllerOffset + 0x0, (int)Type); + Ns.Memory.WriteInt32(BaseControllerOffset + 0x0, (int)Type); - WriteInt32(BaseControllerOffset + 0x4, IsHalf ? 1 : 0); + Ns.Memory.WriteInt32(BaseControllerOffset + 0x4, IsHalf ? 1 : 0); - WriteInt32(BaseControllerOffset + 0x8, (int)SingleColorDesc); - WriteInt32(BaseControllerOffset + 0xc, (int)SingleColorBody); - WriteInt32(BaseControllerOffset + 0x10, (int)SingleColorButtons); - WriteInt32(BaseControllerOffset + 0x14, (int)SplitColorDesc); + Ns.Memory.WriteInt32(BaseControllerOffset + 0x8, (int)SingleColorDesc); + Ns.Memory.WriteInt32(BaseControllerOffset + 0xc, (int)SingleColorBody); + Ns.Memory.WriteInt32(BaseControllerOffset + 0x10, (int)SingleColorButtons); + Ns.Memory.WriteInt32(BaseControllerOffset + 0x14, (int)SplitColorDesc); - WriteInt32(BaseControllerOffset + 0x18, (int)LeftColorBody); - WriteInt32(BaseControllerOffset + 0x1c, (int)LeftColorButtons); + Ns.Memory.WriteInt32(BaseControllerOffset + 0x18, (int)LeftColorBody); + Ns.Memory.WriteInt32(BaseControllerOffset + 0x1c, (int)LeftColorButtons); - WriteInt32(BaseControllerOffset + 0x20, (int)RightColorBody); - WriteInt32(BaseControllerOffset + 0x24, (int)RightColorButtons); + Ns.Memory.WriteInt32(BaseControllerOffset + 0x20, (int)RightColorBody); + Ns.Memory.WriteInt32(BaseControllerOffset + 0x24, (int)RightColorButtons); } public void SetJoyconButton( @@ -163,119 +165,119 @@ namespace Ryujinx.Core.Input { foreach (long Position in ShMemPositions) { - long ControllerOffset = Position + HidControllersOffset; - - ControllerOffset += (int)ControllerId * HidControllerSize; - - ControllerOffset += HidControllerHeaderSize; - - ControllerOffset += (int)ControllerLayout * HidControllerLayoutsSize; - - long LastEntry = ReadInt64(ControllerOffset + 0x10); - - long CurrEntry = (LastEntry + 1) % HidEntryCount; - - long Timestamp = Stopwatch.GetTimestamp(); - - WriteInt64(ControllerOffset + 0x0, Timestamp); - WriteInt64(ControllerOffset + 0x8, HidEntryCount); - WriteInt64(ControllerOffset + 0x10, CurrEntry); - WriteInt64(ControllerOffset + 0x18, HidEntryCount - 1); - - ControllerOffset += HidControllersLayoutHeaderSize; - - ControllerOffset += CurrEntry * HidControllersInputEntrySize; - - WriteInt64(ControllerOffset + 0x0, Timestamp); - WriteInt64(ControllerOffset + 0x8, Timestamp); - - WriteInt64(ControllerOffset + 0x10, (uint)Buttons); - - WriteInt32(ControllerOffset + 0x18, LeftStick.DX); - WriteInt32(ControllerOffset + 0x1c, LeftStick.DY); - - WriteInt64(ControllerOffset + 0x20, RightStick.DX); - WriteInt64(ControllerOffset + 0x24, RightStick.DY); - - WriteInt64(ControllerOffset + 0x28, - (uint)HidControllerConnState.Controller_State_Connected | - (uint)HidControllerConnState.Controller_State_Wired); + WriteJoyconButtons( + Position, + ControllerId, + ControllerLayout, + Buttons, + LeftStick, + RightStick); } } } + private void WriteJoyconButtons( + long BasePosition, + HidControllerId ControllerId, + HidControllerLayouts ControllerLayout, + HidControllerButtons Buttons, + HidJoystickPosition LeftStick, + HidJoystickPosition RightStick) + { + long ControllerOffset = BasePosition + HidControllersOffset; + + ControllerOffset += (int)ControllerId * HidControllerSize; + + ControllerOffset += HidControllerHeaderSize; + + ControllerOffset += (int)ControllerLayout * HidControllerLayoutsSize; + + CurrControllerEntry = (CurrControllerEntry + 1) % HidEntryCount; + + long Timestamp = GetTimestamp(); + + Ns.Memory.WriteInt64(ControllerOffset + 0x0, Timestamp); + Ns.Memory.WriteInt64(ControllerOffset + 0x8, HidEntryCount); + Ns.Memory.WriteInt64(ControllerOffset + 0x10, CurrControllerEntry); + Ns.Memory.WriteInt64(ControllerOffset + 0x18, HidEntryCount - 1); + + ControllerOffset += HidControllersLayoutHeaderSize; + + ControllerOffset += CurrControllerEntry * HidControllersInputEntrySize; + + Ns.Memory.WriteInt64(ControllerOffset + 0x0, Timestamp); + Ns.Memory.WriteInt64(ControllerOffset + 0x8, Timestamp); + + Ns.Memory.WriteInt64(ControllerOffset + 0x10, (uint)Buttons); + + Ns.Memory.WriteInt32(ControllerOffset + 0x18, LeftStick.DX); + Ns.Memory.WriteInt32(ControllerOffset + 0x1c, LeftStick.DY); + + Ns.Memory.WriteInt32(ControllerOffset + 0x20, RightStick.DX); + Ns.Memory.WriteInt32(ControllerOffset + 0x24, RightStick.DY); + + Ns.Memory.WriteInt64(ControllerOffset + 0x28, + (uint)HidControllerConnState.Controller_State_Connected | + (uint)HidControllerConnState.Controller_State_Wired); + } + public void SetTouchPoints(params HidTouchPoint[] Points) { lock (ShMemLock) { foreach (long Position in ShMemPositions) { - long TouchScreenOffset = Position + HidTouchScreenOffset; - - long LastEntry = ReadInt64(TouchScreenOffset + 0x10); - - long CurrEntry = (LastEntry + 1) % HidEntryCount; - - long Timestamp = ReadInt64(TouchScreenOffset) + 1; - - WriteInt64(TouchScreenOffset + 0x0, Timestamp); - WriteInt64(TouchScreenOffset + 0x8, HidEntryCount); - WriteInt64(TouchScreenOffset + 0x10, CurrEntry); - WriteInt64(TouchScreenOffset + 0x18, HidEntryCount - 1); - WriteInt64(TouchScreenOffset + 0x20, Timestamp); - - long TouchEntryOffset = TouchScreenOffset + HidTouchHeaderSize; - - long LastEntryOffset = TouchEntryOffset + LastEntry * HidTouchEntrySize; - - TouchEntryOffset += CurrEntry * HidTouchEntrySize; - - WriteInt64(TouchEntryOffset + 0x0, Timestamp); - WriteInt64(TouchEntryOffset + 0x8, Points.Length); - - TouchEntryOffset += HidTouchEntryHeaderSize; - - const int Padding = 0; - - int Index = 0; - - foreach (HidTouchPoint Point in Points) - { - WriteInt64(TouchEntryOffset + 0x0, Timestamp); - WriteInt32(TouchEntryOffset + 0x8, Padding); - WriteInt32(TouchEntryOffset + 0xc, Index++); - WriteInt32(TouchEntryOffset + 0x10, Point.X); - WriteInt32(TouchEntryOffset + 0x14, Point.Y); - WriteInt32(TouchEntryOffset + 0x18, Point.DiameterX); - WriteInt32(TouchEntryOffset + 0x1c, Point.DiameterY); - WriteInt32(TouchEntryOffset + 0x20, Point.Angle); - WriteInt32(TouchEntryOffset + 0x24, Padding); - - TouchEntryOffset += HidTouchEntryTouchSize; - } + WriteTouchPoints(Position, Points); } } } - private unsafe long ReadInt64(long Position) + private void WriteTouchPoints(long BasePosition, params HidTouchPoint[] Points) { - if ((ulong)Position + 8 > AMemoryMgr.AddrSize) return 0; + long TouchScreenOffset = BasePosition + HidTouchScreenOffset; - return *((long*)((byte*)Ram + Position)); + long Timestamp = GetTimestamp(); + + CurrTouchEntry = (CurrTouchEntry + 1) % HidEntryCount; + + Ns.Memory.WriteInt64(TouchScreenOffset + 0x0, Timestamp); + Ns.Memory.WriteInt64(TouchScreenOffset + 0x8, HidEntryCount); + Ns.Memory.WriteInt64(TouchScreenOffset + 0x10, CurrTouchEntry); + Ns.Memory.WriteInt64(TouchScreenOffset + 0x18, HidEntryCount - 1); + Ns.Memory.WriteInt64(TouchScreenOffset + 0x20, Timestamp); + + long TouchEntryOffset = TouchScreenOffset + HidTouchHeaderSize; + + TouchEntryOffset += CurrTouchEntry * HidTouchEntrySize; + + Ns.Memory.WriteInt64(TouchEntryOffset + 0x0, CurrTouchSampleCounter++); + Ns.Memory.WriteInt64(TouchEntryOffset + 0x8, Points.Length); + + TouchEntryOffset += HidTouchEntryHeaderSize; + + const int Padding = 0; + + int Index = 0; + + foreach (HidTouchPoint Point in Points) + { + Ns.Memory.WriteInt64(TouchEntryOffset + 0x0, Timestamp); + Ns.Memory.WriteInt32(TouchEntryOffset + 0x8, Padding); + Ns.Memory.WriteInt32(TouchEntryOffset + 0xc, Index++); + Ns.Memory.WriteInt32(TouchEntryOffset + 0x10, Point.X); + Ns.Memory.WriteInt32(TouchEntryOffset + 0x14, Point.Y); + Ns.Memory.WriteInt32(TouchEntryOffset + 0x18, Point.DiameterX); + Ns.Memory.WriteInt32(TouchEntryOffset + 0x1c, Point.DiameterY); + Ns.Memory.WriteInt32(TouchEntryOffset + 0x20, Point.Angle); + Ns.Memory.WriteInt32(TouchEntryOffset + 0x24, Padding); + + TouchEntryOffset += HidTouchEntryTouchSize; + } } - private unsafe void WriteInt32(long Position, int Value) + private long GetTimestamp() { - if ((ulong)Position + 4 > AMemoryMgr.AddrSize) return; - - *((int*)((byte*)Ram + Position)) = Value; - } - - private unsafe void WriteInt64(long Position, long Value) - { - if ((ulong)Position + 8 > AMemoryMgr.AddrSize) return; - - *((long*)((byte*)Ram + Position)) = Value; + return Environment.TickCount * 19_200; } } } \ No newline at end of file diff --git a/Ryujinx.Core/OsHle/MemoryRegions.cs b/Ryujinx.Core/OsHle/MemoryRegions.cs index 7f5ab0edeb..75b97b1f2c 100644 --- a/Ryujinx.Core/OsHle/MemoryRegions.cs +++ b/Ryujinx.Core/OsHle/MemoryRegions.cs @@ -9,6 +9,9 @@ namespace Ryujinx.Core.OsHle public const long MapRegionAddress = 0x10000000; public const long MapRegionSize = 0x20000000; + public const long HeapRegionAddress = MapRegionAddress + MapRegionSize; + public const long HeapRegionSize = TlsPagesAddress - HeapRegionAddress; + public const long MainStackSize = 0x100000; public const long MainStackAddress = AMemoryMgr.AddrSize - MainStackSize; @@ -17,8 +20,6 @@ namespace Ryujinx.Core.OsHle public const long TlsPagesAddress = MainStackAddress - TlsPagesSize; - public const long HeapRegionAddress = MapRegionAddress + MapRegionSize; - public const long TotalMemoryUsed = HeapRegionAddress + TlsPagesSize + MainStackSize; public const long TotalMemoryAvailable = AMemoryMgr.RamSize - AddrSpaceStart; diff --git a/Ryujinx.Core/OsHle/Process.cs b/Ryujinx.Core/OsHle/Process.cs index f549b02792..a919f1af6d 100644 --- a/Ryujinx.Core/OsHle/Process.cs +++ b/Ryujinx.Core/OsHle/Process.cs @@ -45,7 +45,7 @@ namespace Ryujinx.Core.OsHle this.Ns = Ns; this.ProcessId = ProcessId; - Memory = new AMemory(Ns.Ram); + Memory = Ns.Memory; Scheduler = new KProcessScheduler(); diff --git a/Ryujinx.Core/OsHle/Services/Vi/NvFlinger.cs b/Ryujinx.Core/OsHle/Services/Vi/NvFlinger.cs index 740a35f9c4..1d394fb4a7 100644 --- a/Ryujinx.Core/OsHle/Services/Vi/NvFlinger.cs +++ b/Ryujinx.Core/OsHle/Services/Vi/NvFlinger.cs @@ -264,11 +264,11 @@ namespace Ryujinx.Core.OsHle.IpcServices.Android int FbWidth = BufferQueue[Slot].Data.Width; int FbHeight = BufferQueue[Slot].Data.Height; - int FbSize = FbWidth * FbHeight * 4; + long FbSize = (uint)FbWidth * FbHeight * 4; HNvMap NvMap = GetNvMap(Context, Slot); - if (FbSize < 0 || NvMap.Address < 0 || NvMap.Address + FbSize > AMemoryMgr.AddrSize) + if ((ulong)(NvMap.Address + FbSize) > AMemoryMgr.AddrSize) { Logging.Error($"Frame buffer address {NvMap.Address:x16} is invalid!"); @@ -330,7 +330,7 @@ namespace Ryujinx.Core.OsHle.IpcServices.Android Rotate = -MathF.PI * 0.5f; } - byte* Fb = (byte*)Context.Ns.Ram + NvMap.Address; + byte* Fb = (byte*)Context.Ns.Memory.Ram + NvMap.Address; Context.Ns.Gpu.Renderer.QueueAction(delegate() { diff --git a/Ryujinx.Core/OsHle/Svc/SvcSystem.cs b/Ryujinx.Core/OsHle/Svc/SvcSystem.cs index 8a0a391750..a276fe3c85 100644 --- a/Ryujinx.Core/OsHle/Svc/SvcSystem.cs +++ b/Ryujinx.Core/OsHle/Svc/SvcSystem.cs @@ -197,7 +197,7 @@ namespace Ryujinx.Core.OsHle.Svc break; case 5: - ThreadState.X1 = CurrentHeapSize; + ThreadState.X1 = MemoryRegions.HeapRegionSize; break; case 6: diff --git a/Ryujinx.Core/PerformanceStatistics.cs b/Ryujinx.Core/PerformanceStatistics.cs index 3740daa584..e5d930f934 100644 --- a/Ryujinx.Core/PerformanceStatistics.cs +++ b/Ryujinx.Core/PerformanceStatistics.cs @@ -1,7 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.Text; +using System.Diagnostics; using System.Timers; namespace Ryujinx.Core diff --git a/Ryujinx.Core/Switch.cs b/Ryujinx.Core/Switch.cs index 1acd87f018..dff7802b7d 100644 --- a/Ryujinx.Core/Switch.cs +++ b/Ryujinx.Core/Switch.cs @@ -5,13 +5,12 @@ using Ryujinx.Core.Settings; using Ryujinx.Graphics.Gal; using Ryujinx.Graphics.Gpu; using System; -using System.Runtime.InteropServices; namespace Ryujinx.Core { public class Switch : IDisposable { - public IntPtr Ram {get; private set; } + internal AMemory Memory { get; private set; } internal NsGpu Gpu { get; private set; } internal Horizon Os { get; private set; } @@ -25,13 +24,13 @@ namespace Ryujinx.Core public Switch(IGalRenderer Renderer) { - Ram = Marshal.AllocHGlobal((IntPtr)AMemoryMgr.RamSize); + Memory = new AMemory(); Gpu = new NsGpu(Renderer); VFs = new VirtualFs(); - Hid = new Hid(Ram); + Hid = new Hid(this); Statistics = new PerformanceStatistics(); @@ -72,10 +71,10 @@ namespace Ryujinx.Core { if (disposing) { + Memory.Dispose(); + VFs.Dispose(); } - - Marshal.FreeHGlobal(Ram); } } } \ No newline at end of file diff --git a/Ryujinx.Tests/Cpu/CpuTest.cs b/Ryujinx.Tests/Cpu/CpuTest.cs index a4a3b33fc5..c4f0bbd74a 100644 --- a/Ryujinx.Tests/Cpu/CpuTest.cs +++ b/Ryujinx.Tests/Cpu/CpuTest.cs @@ -2,8 +2,6 @@ using ChocolArm64; using ChocolArm64.Memory; using ChocolArm64.State; using NUnit.Framework; -using System; -using System.Runtime.InteropServices; using System.Threading; namespace Ryujinx.Tests.Cpu @@ -16,7 +14,6 @@ namespace Ryujinx.Tests.Cpu private long EntryPoint; - private IntPtr Ram; private AMemory Memory; private AThread Thread; @@ -28,9 +25,8 @@ namespace Ryujinx.Tests.Cpu EntryPoint = Position; - Ram = Marshal.AllocHGlobal((IntPtr)AMemoryMgr.RamSize); ATranslator Translator = new ATranslator(); - Memory = new AMemory(Ram); + Memory = new AMemory(); Memory.Manager.Map(Position, Size, 2, AMemoryPerm.Read | AMemoryPerm.Write | AMemoryPerm.Execute); Thread = new AThread(Translator, Memory, ThreadPriority.Normal, EntryPoint); } @@ -38,9 +34,9 @@ namespace Ryujinx.Tests.Cpu [TearDown] public void Teardown() { + Memory.Dispose(); Thread = null; Memory = null; - Marshal.FreeHGlobal(Ram); } protected void Reset() From aa2d2b3149536792bf49a9d5695778efea64c53a Mon Sep 17 00:00:00 2001 From: gdkchan Date: Fri, 9 Mar 2018 23:28:38 -0300 Subject: [PATCH 12/28] Add SHLL instruction --- ChocolArm64/AOpCodeTable.cs | 1 + ChocolArm64/Instruction/AInstEmitSimdShift.cs | 15 ++++++++++++--- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/ChocolArm64/AOpCodeTable.cs b/ChocolArm64/AOpCodeTable.cs index 5afdbd1ffe..357d63db0e 100644 --- a/ChocolArm64/AOpCodeTable.cs +++ b/ChocolArm64/AOpCodeTable.cs @@ -240,6 +240,7 @@ namespace ChocolArm64 Set("0x0011100x100001110110xxxxxxxxxx", AInstEmit.Scvtf_V, typeof(AOpCodeSimd)); Set("010111110>>>>xxx010101xxxxxxxxxx", AInstEmit.Shl_S, typeof(AOpCodeSimdShImm)); Set("0x0011110>>>>xxx010101xxxxxxxxxx", AInstEmit.Shl_V, typeof(AOpCodeSimdShImm)); + Set("0x101110<<100001001110xxxxxxxxxx", AInstEmit.Shll_V, typeof(AOpCodeSimd)); Set("0x00111100>>>xxx100001xxxxxxxxxx", AInstEmit.Shrn_V, typeof(AOpCodeSimdShImm)); Set("0x001110<<1xxxxx011001xxxxxxxxxx", AInstEmit.Smax_V, typeof(AOpCodeSimdReg)); Set("0x001110<<1xxxxx011011xxxxxxxxxx", AInstEmit.Smin_V, typeof(AOpCodeSimdReg)); diff --git a/ChocolArm64/Instruction/AInstEmitSimdShift.cs b/ChocolArm64/Instruction/AInstEmitSimdShift.cs index 8740ba4d69..01ed3a28bc 100644 --- a/ChocolArm64/Instruction/AInstEmitSimdShift.cs +++ b/ChocolArm64/Instruction/AInstEmitSimdShift.cs @@ -32,6 +32,15 @@ namespace ChocolArm64.Instruction EmitVectorShImmBinaryZx(Context, () => Context.Emit(OpCodes.Shl), Shift); } + public static void Shll_V(AILEmitterCtx Context) + { + AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp; + + int Shift = 8 << Op.Size; + + EmitVectorShImmWidenBinaryZx(Context, () => Context.Emit(OpCodes.Shl), Shift); + } + public static void Shrn_V(AILEmitterCtx Context) { AOpCodeSimdShImm Op = (AOpCodeSimdShImm)Context.CurrOp; @@ -205,7 +214,7 @@ namespace ChocolArm64.Instruction private static void EmitVectorShImmBinaryOp(AILEmitterCtx Context, Action Emit, int Imm, bool Signed) { - AOpCodeSimdShImm Op = (AOpCodeSimdShImm)Context.CurrOp; + AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp; int Bytes = Context.CurrOp.GetBitsCount() >> 3; @@ -238,7 +247,7 @@ namespace ChocolArm64.Instruction private static void EmitVectorShImmNarrowBinaryOp(AILEmitterCtx Context, Action Emit, int Imm, bool Signed) { - AOpCodeSimdShImm Op = (AOpCodeSimdShImm)Context.CurrOp; + AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp; int Elems = 8 >> Op.Size; @@ -273,7 +282,7 @@ namespace ChocolArm64.Instruction private static void EmitVectorShImmWidenBinaryOp(AILEmitterCtx Context, Action Emit, int Imm, bool Signed) { - AOpCodeSimdShImm Op = (AOpCodeSimdShImm)Context.CurrOp; + AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp; int Elems = 8 >> Op.Size; From 30bcb8da33c328be1abd922b0a6a95dc5c4d6c64 Mon Sep 17 00:00:00 2001 From: gdkchan Date: Fri, 9 Mar 2018 23:41:05 -0300 Subject: [PATCH 13/28] Add FRINTM (vector) instruction --- ChocolArm64/AOpCodeTable.cs | 1 + ChocolArm64/Instruction/AInstEmitSimdArithmetic.cs | 8 ++++++++ ChocolArm64/Instruction/AInstEmitSimdHelper.cs | 5 +++++ 3 files changed, 14 insertions(+) diff --git a/ChocolArm64/AOpCodeTable.cs b/ChocolArm64/AOpCodeTable.cs index 357d63db0e..f25d697346 100644 --- a/ChocolArm64/AOpCodeTable.cs +++ b/ChocolArm64/AOpCodeTable.cs @@ -200,6 +200,7 @@ namespace ChocolArm64 Set("000111100x1xxxxx100010xxxxxxxxxx", AInstEmit.Fnmul_S, typeof(AOpCodeSimdReg)); Set("000111100x100110010000xxxxxxxxxx", AInstEmit.Frinta_S, typeof(AOpCodeSimd)); Set("000111100x100101010000xxxxxxxxxx", AInstEmit.Frintm_S, typeof(AOpCodeSimd)); + Set("0>0011100<100001100110xxxxxxxxxx", AInstEmit.Frintm_V, typeof(AOpCodeSimd)); Set("000111100x100100110000xxxxxxxxxx", AInstEmit.Frintp_S, typeof(AOpCodeSimd)); Set("000111100x100111010000xxxxxxxxxx", AInstEmit.Frintx_S, typeof(AOpCodeSimd)); Set("000111100x100001110000xxxxxxxxxx", AInstEmit.Fsqrt_S, typeof(AOpCodeSimd)); diff --git a/ChocolArm64/Instruction/AInstEmitSimdArithmetic.cs b/ChocolArm64/Instruction/AInstEmitSimdArithmetic.cs index b2d190f1a9..bf980a581e 100644 --- a/ChocolArm64/Instruction/AInstEmitSimdArithmetic.cs +++ b/ChocolArm64/Instruction/AInstEmitSimdArithmetic.cs @@ -267,6 +267,14 @@ namespace ChocolArm64.Instruction }); } + public static void Frintm_V(AILEmitterCtx Context) + { + EmitVectorUnaryOpF(Context, () => + { + EmitUnaryMathCall(Context, nameof(Math.Floor)); + }); + } + public static void Frintp_S(AILEmitterCtx Context) { EmitScalarUnaryOpF(Context, () => diff --git a/ChocolArm64/Instruction/AInstEmitSimdHelper.cs b/ChocolArm64/Instruction/AInstEmitSimdHelper.cs index e6ead99aa2..1f78b71a1f 100644 --- a/ChocolArm64/Instruction/AInstEmitSimdHelper.cs +++ b/ChocolArm64/Instruction/AInstEmitSimdHelper.cs @@ -190,6 +190,11 @@ namespace ChocolArm64.Instruction EmitScalarSetF(Context, Op.Rd, SizeF); } + public static void EmitVectorUnaryOpF(AILEmitterCtx Context, Action Emit) + { + EmitVectorOpF(Context, Emit, OperFlags.Rn); + } + public static void EmitVectorBinaryOpF(AILEmitterCtx Context, Action Emit) { EmitVectorOpF(Context, Emit, OperFlags.RnRm); From 553f6c2976818b3abcd0fd09de582dc71f03736e Mon Sep 17 00:00:00 2001 From: gdkchan Date: Sat, 10 Mar 2018 00:00:31 -0300 Subject: [PATCH 14/28] Fix EmitScalarUnaryOpF and add SSRA (vector) --- ChocolArm64/AOpCodeTable.cs | 1 + .../Instruction/AInstEmitSimdHelper.cs | 4 +- ChocolArm64/Instruction/AInstEmitSimdShift.cs | 39 +++++++++++++++---- 3 files changed, 35 insertions(+), 9 deletions(-) diff --git a/ChocolArm64/AOpCodeTable.cs b/ChocolArm64/AOpCodeTable.cs index f25d697346..60646d0ee5 100644 --- a/ChocolArm64/AOpCodeTable.cs +++ b/ChocolArm64/AOpCodeTable.cs @@ -251,6 +251,7 @@ namespace ChocolArm64 Set("0x00111100>>>xxx101001xxxxxxxxxx", AInstEmit.Sshll_V, typeof(AOpCodeSimdShImm)); Set("010111110>>>>xxx000001xxxxxxxxxx", AInstEmit.Sshr_S, typeof(AOpCodeSimdShImm)); Set("0x0011110>>>>xxx000001xxxxxxxxxx", AInstEmit.Sshr_V, typeof(AOpCodeSimdShImm)); + Set("0x0011110>>>>xxx000101xxxxxxxxxx", AInstEmit.Ssra_V, typeof(AOpCodeSimdShImm)); Set("0x00110000000000xxxxxxxxxxxxxxxx", AInstEmit.St__Vms, typeof(AOpCodeSimdMemMs)); Set("0x001100100xxxxxxxxxxxxxxxxxxxxx", AInstEmit.St__Vms, typeof(AOpCodeSimdMemMs)); Set("0x00110100000000xx0xxxxxxxxxxxxx", AInstEmit.St__Vss, typeof(AOpCodeSimdMemSs)); diff --git a/ChocolArm64/Instruction/AInstEmitSimdHelper.cs b/ChocolArm64/Instruction/AInstEmitSimdHelper.cs index 1f78b71a1f..4e45a11d6a 100644 --- a/ChocolArm64/Instruction/AInstEmitSimdHelper.cs +++ b/ChocolArm64/Instruction/AInstEmitSimdHelper.cs @@ -207,7 +207,7 @@ namespace ChocolArm64.Instruction public static void EmitVectorOpF(AILEmitterCtx Context, Action Emit, OperFlags Opers) { - AOpCodeSimdReg Op = (AOpCodeSimdReg)Context.CurrOp; + AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp; int SizeF = Op.Size & 1; @@ -227,7 +227,7 @@ namespace ChocolArm64.Instruction if (Opers.HasFlag(OperFlags.Rm)) { - EmitVectorExtractF(Context, Op.Rm, Index, SizeF); + EmitVectorExtractF(Context, ((AOpCodeSimdReg)Op).Rm, Index, SizeF); } Emit(); diff --git a/ChocolArm64/Instruction/AInstEmitSimdShift.cs b/ChocolArm64/Instruction/AInstEmitSimdShift.cs index 01ed3a28bc..bb8a8f178b 100644 --- a/ChocolArm64/Instruction/AInstEmitSimdShift.cs +++ b/ChocolArm64/Instruction/AInstEmitSimdShift.cs @@ -29,7 +29,7 @@ namespace ChocolArm64.Instruction int Shift = Op.Imm - (8 << Op.Size); - EmitVectorShImmBinaryZx(Context, () => Context.Emit(OpCodes.Shl), Shift); + EmitVectorBinaryShImmBinaryZx(Context, () => Context.Emit(OpCodes.Shl), Shift); } public static void Shll_V(AILEmitterCtx Context) @@ -83,7 +83,22 @@ namespace ChocolArm64.Instruction int Shift = (8 << (Op.Size + 1)) - Op.Imm; - EmitVectorShImmBinarySx(Context, () => Context.Emit(OpCodes.Shr), Shift); + EmitVectorBinaryShImmBinarySx(Context, () => Context.Emit(OpCodes.Shr), Shift); + } + + public static void Ssra_V(AILEmitterCtx Context) + { + AOpCodeSimdShImm Op = (AOpCodeSimdShImm)Context.CurrOp; + + int Shift = (8 << (Op.Size + 1)) - Op.Imm; + + Action Emit = () => + { + Context.Emit(OpCodes.Shr); + Context.Emit(OpCodes.Add); + }; + + EmitVectorTernaryShImmBinarySx(Context, Emit, Shift); } public static void Ushl_V(AILEmitterCtx Context) @@ -202,17 +217,22 @@ namespace ChocolArm64.Instruction } } - private static void EmitVectorShImmBinarySx(AILEmitterCtx Context, Action Emit, int Imm) + private static void EmitVectorBinaryShImmBinarySx(AILEmitterCtx Context, Action Emit, int Imm) { - EmitVectorShImmBinaryOp(Context, Emit, Imm, true); + EmitVectorShImmBinaryOp(Context, Emit, Imm, false, true); } - private static void EmitVectorShImmBinaryZx(AILEmitterCtx Context, Action Emit, int Imm) + private static void EmitVectorTernaryShImmBinarySx(AILEmitterCtx Context, Action Emit, int Imm) { - EmitVectorShImmBinaryOp(Context, Emit, Imm, false); + EmitVectorShImmBinaryOp(Context, Emit, Imm, true, true); } - private static void EmitVectorShImmBinaryOp(AILEmitterCtx Context, Action Emit, int Imm, bool Signed) + private static void EmitVectorBinaryShImmBinaryZx(AILEmitterCtx Context, Action Emit, int Imm) + { + EmitVectorShImmBinaryOp(Context, Emit, Imm, false, false); + } + + private static void EmitVectorShImmBinaryOp(AILEmitterCtx Context, Action Emit, int Imm, bool Ternary, bool Signed) { AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp; @@ -220,6 +240,11 @@ namespace ChocolArm64.Instruction for (int Index = 0; Index < (Bytes >> Op.Size); Index++) { + if (Ternary) + { + EmitVectorExtract(Context, Op.Rd, Index, Op.Size, Signed); + } + EmitVectorExtract(Context, Op.Rn, Index, Op.Size, Signed); Context.EmitLdc_I4(Imm); From 3777fb44cf03d05fdedee00f1a19d30fac73b31b Mon Sep 17 00:00:00 2001 From: gdkchan Date: Sat, 10 Mar 2018 20:39:16 -0300 Subject: [PATCH 15/28] Allow to enable/disable memory checks even on release mode through the flag, return error for invalid addresses on SvcMap*Memory svcs, do not return error on SvcQueryMemory (instead, return reserved for the end of the address space), other minor tweaks --- ChocolArm64/AOptimizations.cs | 2 +- .../Instruction/AInstEmitMemoryHelper.cs | 86 +++++-- ChocolArm64/Memory/AMemory.cs | 235 ++++++++++++++---- Ryujinx.Core/Hid/Hid.cs | 211 ++++++++-------- Ryujinx.Core/Loaders/Executable.cs | 13 +- .../Loaders/Executables/IExecutable.cs | 8 +- Ryujinx.Core/Loaders/Executables/Nro.cs | 18 +- Ryujinx.Core/Loaders/Executables/Nso.cs | 35 ++- Ryujinx.Core/OsHle/Horizon.cs | 2 + Ryujinx.Core/OsHle/Ipc/IpcMessage.cs | 14 +- .../OsHle/Services/Nv/ServiceNvDrv.cs | 42 ++-- Ryujinx.Core/OsHle/Svc/SvcMemory.cs | 107 +++++++- Ryujinx.Core/Switch.cs | 2 +- Ryujinx.Tests/Cpu/CpuTest.cs | 2 +- Ryujinx/Ui/Program.cs | 2 + 15 files changed, 516 insertions(+), 263 deletions(-) diff --git a/ChocolArm64/AOptimizations.cs b/ChocolArm64/AOptimizations.cs index 0cdbd76e28..a3c82dccde 100644 --- a/ChocolArm64/AOptimizations.cs +++ b/ChocolArm64/AOptimizations.cs @@ -1,4 +1,4 @@ public static class AOptimizations { - public static bool EnableMemoryChecks = false; + public static bool DisableMemoryChecks = false; } \ No newline at end of file diff --git a/ChocolArm64/Instruction/AInstEmitMemoryHelper.cs b/ChocolArm64/Instruction/AInstEmitMemoryHelper.cs index d5a0051b5a..6ffcf2dc95 100644 --- a/ChocolArm64/Instruction/AInstEmitMemoryHelper.cs +++ b/ChocolArm64/Instruction/AInstEmitMemoryHelper.cs @@ -45,21 +45,46 @@ namespace ChocolArm64.Instruction { switch (Size) { - case 0: Name = nameof(AMemory.ReadVector8); break; - case 1: Name = nameof(AMemory.ReadVector16); break; - case 2: Name = nameof(AMemory.ReadVector32); break; - case 3: Name = nameof(AMemory.ReadVector64); break; - case 4: Name = nameof(AMemory.ReadVector128); break; + case 0: Name = AOptimizations.DisableMemoryChecks + ? nameof(AMemory.ReadVector8Unchecked) + : nameof(AMemory.ReadVector8); break; + + case 1: Name = AOptimizations.DisableMemoryChecks + ? nameof(AMemory.ReadVector16Unchecked) + : nameof(AMemory.ReadVector16); break; + + case 2: Name = AOptimizations.DisableMemoryChecks + ? nameof(AMemory.ReadVector32Unchecked) + : nameof(AMemory.ReadVector32); break; + + case 3: Name = AOptimizations.DisableMemoryChecks + ? nameof(AMemory.ReadVector64Unchecked) + : nameof(AMemory.ReadVector64); break; + + case 4: Name = AOptimizations.DisableMemoryChecks + ? nameof(AMemory.ReadVector128Unchecked) + : nameof(AMemory.ReadVector128); break; } } else { switch (Size) { - case 0: Name = nameof(AMemory.ReadByte); break; - case 1: Name = nameof(AMemory.ReadUInt16); break; - case 2: Name = nameof(AMemory.ReadUInt32); break; - case 3: Name = nameof(AMemory.ReadUInt64); break; + case 0: Name = AOptimizations.DisableMemoryChecks + ? nameof(AMemory.ReadByteUnchecked) + : nameof(AMemory.ReadByte); break; + + case 1: Name = AOptimizations.DisableMemoryChecks + ? nameof(AMemory.ReadUInt16Unchecked) + : nameof(AMemory.ReadUInt16); break; + + case 2: Name = AOptimizations.DisableMemoryChecks + ? nameof(AMemory.ReadUInt32Unchecked) + : nameof(AMemory.ReadUInt32); break; + + case 3: Name = AOptimizations.DisableMemoryChecks + ? nameof(AMemory.ReadUInt64Unchecked) + : nameof(AMemory.ReadUInt64); break; } } @@ -107,21 +132,46 @@ namespace ChocolArm64.Instruction { switch (Size) { - case 0: Name = nameof(AMemory.WriteVector8); break; - case 1: Name = nameof(AMemory.WriteVector16); break; - case 2: Name = nameof(AMemory.WriteVector32); break; - case 3: Name = nameof(AMemory.WriteVector64); break; - case 4: Name = nameof(AMemory.WriteVector128); break; + case 0: Name = AOptimizations.DisableMemoryChecks + ? nameof(AMemory.WriteVector8Unchecked) + : nameof(AMemory.WriteVector8); break; + + case 1: Name = AOptimizations.DisableMemoryChecks + ? nameof(AMemory.WriteVector16Unchecked) + : nameof(AMemory.WriteVector16); break; + + case 2: Name = AOptimizations.DisableMemoryChecks + ? nameof(AMemory.WriteVector32Unchecked) + : nameof(AMemory.WriteVector32); break; + + case 3: Name = AOptimizations.DisableMemoryChecks + ? nameof(AMemory.WriteVector64Unchecked) + : nameof(AMemory.WriteVector64); break; + + case 4: Name = AOptimizations.DisableMemoryChecks + ? nameof(AMemory.WriteVector128Unchecked) + : nameof(AMemory.WriteVector128); break; } } else { switch (Size) { - case 0: Name = nameof(AMemory.WriteByte); break; - case 1: Name = nameof(AMemory.WriteUInt16); break; - case 2: Name = nameof(AMemory.WriteUInt32); break; - case 3: Name = nameof(AMemory.WriteUInt64); break; + case 0: Name = AOptimizations.DisableMemoryChecks + ? nameof(AMemory.WriteByteUnchecked) + : nameof(AMemory.WriteByte); break; + + case 1: Name = AOptimizations.DisableMemoryChecks + ? nameof(AMemory.WriteUInt16Unchecked) + : nameof(AMemory.WriteUInt16); break; + + case 2: Name = AOptimizations.DisableMemoryChecks + ? nameof(AMemory.WriteUInt32Unchecked) + : nameof(AMemory.WriteUInt32); break; + + case 3: Name = AOptimizations.DisableMemoryChecks + ? nameof(AMemory.WriteUInt64Unchecked) + : nameof(AMemory.WriteUInt64); break; } } diff --git a/ChocolArm64/Memory/AMemory.cs b/ChocolArm64/Memory/AMemory.cs index 7f2dd7d3f4..d7e11189f6 100644 --- a/ChocolArm64/Memory/AMemory.cs +++ b/ChocolArm64/Memory/AMemory.cs @@ -2,7 +2,6 @@ using ChocolArm64.Exceptions; using ChocolArm64.State; using System; using System.Collections.Generic; -using System.Runtime.CompilerServices; using System.Runtime.InteropServices; namespace ChocolArm64.Memory @@ -139,71 +138,77 @@ namespace ChocolArm64.Memory } } - public sbyte ReadSByte(long Position) => (sbyte)ReadByte (Position); - public short ReadInt16(long Position) => (short)ReadUInt16(Position); - public int ReadInt32(long Position) => (int)ReadUInt32(Position); - public long ReadInt64(long Position) => (long)ReadUInt64(Position); + public sbyte ReadSByte(long Position) + { + return (sbyte)ReadByte(Position); + } + + public short ReadInt16(long Position) + { + return (short)ReadUInt16(Position); + } + + public int ReadInt32(long Position) + { + return (int)ReadUInt32(Position); + } + + public long ReadInt64(long Position) + { + return (long)ReadUInt64(Position); + } - [MethodImpl(MethodImplOptions.AggressiveInlining)] public byte ReadByte(long Position) { EnsureAccessIsValid(Position, AMemoryPerm.Read); - return *((byte*)(RamPtr + (uint)Position)); + return ReadByteUnchecked(Position); } - [MethodImpl(MethodImplOptions.AggressiveInlining)] public ushort ReadUInt16(long Position) { EnsureAccessIsValid(Position + 0, AMemoryPerm.Read); EnsureAccessIsValid(Position + 1, AMemoryPerm.Read); - return *((ushort*)(RamPtr + (uint)Position)); + return ReadUInt16Unchecked(Position); } - [MethodImpl(MethodImplOptions.AggressiveInlining)] public uint ReadUInt32(long Position) { EnsureAccessIsValid(Position + 0, AMemoryPerm.Read); EnsureAccessIsValid(Position + 3, AMemoryPerm.Read); - return *((uint*)(RamPtr + (uint)Position)); + return ReadUInt32Unchecked(Position); } - [MethodImpl(MethodImplOptions.AggressiveInlining)] public ulong ReadUInt64(long Position) { EnsureAccessIsValid(Position + 0, AMemoryPerm.Read); EnsureAccessIsValid(Position + 7, AMemoryPerm.Read); - return *((ulong*)(RamPtr + (uint)Position)); + return ReadUInt64Unchecked(Position); } - [MethodImpl(MethodImplOptions.AggressiveInlining)] public AVec ReadVector8(long Position) { return new AVec() { B0 = ReadByte(Position) }; } - [MethodImpl(MethodImplOptions.AggressiveInlining)] public AVec ReadVector16(long Position) { return new AVec() { H0 = ReadUInt16(Position) }; } - [MethodImpl(MethodImplOptions.AggressiveInlining)] public AVec ReadVector32(long Position) { return new AVec() { W0 = ReadUInt32(Position) }; } - [MethodImpl(MethodImplOptions.AggressiveInlining)] public AVec ReadVector64(long Position) { return new AVec() { X0 = ReadUInt64(Position) }; } - [MethodImpl(MethodImplOptions.AggressiveInlining)] public AVec ReadVector128(long Position) { return new AVec() @@ -213,93 +218,229 @@ namespace ChocolArm64.Memory }; } - public void WriteSByte(long Position, sbyte Value) => WriteByte (Position, (byte)Value); - public void WriteInt16(long Position, short Value) => WriteUInt16(Position, (ushort)Value); - public void WriteInt32(long Position, int Value) => WriteUInt32(Position, (uint)Value); - public void WriteInt64(long Position, long Value) => WriteUInt64(Position, (ulong)Value); + public sbyte ReadSByteUnchecked(long Position) + { + return (sbyte)ReadByteUnchecked(Position); + } + + public short ReadInt16Unchecked(long Position) + { + return (short)ReadUInt16Unchecked(Position); + } + + public int ReadInt32Unchecked(long Position) + { + return (int)ReadUInt32Unchecked(Position); + } + + public long ReadInt64Unchecked(long Position) + { + return (long)ReadUInt64Unchecked(Position); + } + + public byte ReadByteUnchecked(long Position) + { + return *((byte*)(RamPtr + (uint)Position)); + } + + public ushort ReadUInt16Unchecked(long Position) + { + return *((ushort*)(RamPtr + (uint)Position)); + } + + public uint ReadUInt32Unchecked(long Position) + { + return *((uint*)(RamPtr + (uint)Position)); + } + + public ulong ReadUInt64Unchecked(long Position) + { + return *((ulong*)(RamPtr + (uint)Position)); + } + + public AVec ReadVector8Unchecked(long Position) + { + return new AVec() { B0 = ReadByteUnchecked(Position) }; + } + + public AVec ReadVector16Unchecked(long Position) + { + return new AVec() { H0 = ReadUInt16Unchecked(Position) }; + } + + public AVec ReadVector32Unchecked(long Position) + { + return new AVec() { W0 = ReadUInt32Unchecked(Position) }; + } + + public AVec ReadVector64Unchecked(long Position) + { + return new AVec() { X0 = ReadUInt64Unchecked(Position) }; + } + + public AVec ReadVector128Unchecked(long Position) + { + return new AVec() + { + X0 = ReadUInt64Unchecked(Position + 0), + X1 = ReadUInt64Unchecked(Position + 8) + }; + } + + public void WriteSByte(long Position, sbyte Value) + { + WriteByte(Position, (byte)Value); + } + + public void WriteInt16(long Position, short Value) + { + WriteUInt16(Position, (ushort)Value); + } + + public void WriteInt32(long Position, int Value) + { + WriteUInt32(Position, (uint)Value); + } + + public void WriteInt64(long Position, long Value) + { + WriteUInt64(Position, (ulong)Value); + } - [MethodImpl(MethodImplOptions.AggressiveInlining)] public void WriteByte(long Position, byte Value) { EnsureAccessIsValid(Position, AMemoryPerm.Write); - *((byte*)(RamPtr + (uint)Position)) = Value; + WriteByteUnchecked(Position, Value); } - [MethodImpl(MethodImplOptions.AggressiveInlining)] public void WriteUInt16(long Position, ushort Value) { EnsureAccessIsValid(Position + 0, AMemoryPerm.Write); EnsureAccessIsValid(Position + 1, AMemoryPerm.Write); - *((ushort*)(RamPtr + (uint)Position)) = Value; + WriteUInt16Unchecked(Position, Value); } - [MethodImpl(MethodImplOptions.AggressiveInlining)] public void WriteUInt32(long Position, uint Value) { EnsureAccessIsValid(Position + 0, AMemoryPerm.Write); EnsureAccessIsValid(Position + 3, AMemoryPerm.Write); - *((uint*)(RamPtr + (uint)Position)) = Value; + WriteUInt32Unchecked(Position, Value); } - [MethodImpl(MethodImplOptions.AggressiveInlining)] public void WriteUInt64(long Position, ulong Value) { EnsureAccessIsValid(Position + 0, AMemoryPerm.Write); EnsureAccessIsValid(Position + 7, AMemoryPerm.Write); - *((ulong*)(RamPtr + (uint)Position)) = Value; + WriteUInt64Unchecked(Position, Value); } - [MethodImpl(MethodImplOptions.AggressiveInlining)] public void WriteVector8(long Position, AVec Value) { WriteByte(Position, Value.B0); } - [MethodImpl(MethodImplOptions.AggressiveInlining)] public void WriteVector16(long Position, AVec Value) { WriteUInt16(Position, Value.H0); } - [MethodImpl(MethodImplOptions.AggressiveInlining)] public void WriteVector32(long Position, AVec Value) { WriteUInt32(Position, Value.W0); } - [MethodImpl(MethodImplOptions.AggressiveInlining)] public void WriteVector64(long Position, AVec Value) { WriteUInt64(Position, Value.X0); } - [MethodImpl(MethodImplOptions.AggressiveInlining)] public void WriteVector128(long Position, AVec Value) { WriteUInt64(Position + 0, Value.X0); WriteUInt64(Position + 8, Value.X1); } + public void WriteSByteUnchecked(long Position, sbyte Value) + { + WriteByteUnchecked(Position, (byte)Value); + } + + public void WriteInt16Unchecked(long Position, short Value) + { + WriteUInt16Unchecked(Position, (ushort)Value); + } + + public void WriteInt32Unchecked(long Position, int Value) + { + WriteUInt32Unchecked(Position, (uint)Value); + } + + public void WriteInt64Unchecked(long Position, long Value) + { + WriteUInt64Unchecked(Position, (ulong)Value); + } + + public void WriteByteUnchecked(long Position, byte Value) + { + *((byte*)(RamPtr + (uint)Position)) = Value; + } + + public void WriteUInt16Unchecked(long Position, ushort Value) + { + *((ushort*)(RamPtr + (uint)Position)) = Value; + } + + public void WriteUInt32Unchecked(long Position, uint Value) + { + *((uint*)(RamPtr + (uint)Position)) = Value; + } + + public void WriteUInt64Unchecked(long Position, ulong Value) + { + *((ulong*)(RamPtr + (uint)Position)) = Value; + } + + public void WriteVector8Unchecked(long Position, AVec Value) + { + WriteByteUnchecked(Position, Value.B0); + } + + public void WriteVector16Unchecked(long Position, AVec Value) + { + WriteUInt16Unchecked(Position, Value.H0); + } + + public void WriteVector32Unchecked(long Position, AVec Value) + { + WriteUInt32Unchecked(Position, Value.W0); + } + + public void WriteVector64Unchecked(long Position, AVec Value) + { + WriteUInt64Unchecked(Position, Value.X0); + } + + public void WriteVector128Unchecked(long Position, AVec Value) + { + WriteUInt64Unchecked(Position + 0, Value.X0); + WriteUInt64Unchecked(Position + 8, Value.X1); + } + private void EnsureAccessIsValid(long Position, AMemoryPerm Perm) { -#if DEBUG - if (AOptimizations.EnableMemoryChecks) + if (!Manager.IsMapped(Position)) { - if (!Manager.IsMapped(Position)) - { - throw new VmmPageFaultException(Position); - } - - if (!Manager.HasPermission(Position, Perm)) - { - throw new VmmAccessViolationException(Position, Perm); - } + throw new VmmPageFaultException(Position); + } + + if (!Manager.HasPermission(Position, Perm)) + { + throw new VmmAccessViolationException(Position, Perm); } -#endif } public void Dispose() diff --git a/Ryujinx.Core/Hid/Hid.cs b/Ryujinx.Core/Hid/Hid.cs index 169768892e..76ecf5ffdf 100644 --- a/Ryujinx.Core/Hid/Hid.cs +++ b/Ryujinx.Core/Hid/Hid.cs @@ -1,4 +1,5 @@ -using Ryujinx.Core.OsHle.Handles; +using ChocolArm64.Memory; +using Ryujinx.Core.OsHle.Handles; using System; namespace Ryujinx.Core.Input @@ -64,15 +65,11 @@ namespace Ryujinx.Core.Input private long[] ShMemPositions; - private long CurrControllerEntry; - private long CurrTouchEntry; - private long CurrTouchSampleCounter; + private AMemory Memory; - private Switch Ns; - - public Hid(Switch Ns) + public Hid(AMemory Memory) { - this.Ns = Ns; + this.Memory = Memory; ShMemLock = new object(); @@ -138,20 +135,20 @@ namespace Ryujinx.Core.Input HidControllerColorDesc SplitColorDesc = 0; - Ns.Memory.WriteInt32(BaseControllerOffset + 0x0, (int)Type); + Memory.WriteInt32Unchecked(BaseControllerOffset + 0x0, (int)Type); - Ns.Memory.WriteInt32(BaseControllerOffset + 0x4, IsHalf ? 1 : 0); + Memory.WriteInt32Unchecked(BaseControllerOffset + 0x4, IsHalf ? 1 : 0); - Ns.Memory.WriteInt32(BaseControllerOffset + 0x8, (int)SingleColorDesc); - Ns.Memory.WriteInt32(BaseControllerOffset + 0xc, (int)SingleColorBody); - Ns.Memory.WriteInt32(BaseControllerOffset + 0x10, (int)SingleColorButtons); - Ns.Memory.WriteInt32(BaseControllerOffset + 0x14, (int)SplitColorDesc); + Memory.WriteInt32Unchecked(BaseControllerOffset + 0x8, (int)SingleColorDesc); + Memory.WriteInt32Unchecked(BaseControllerOffset + 0xc, (int)SingleColorBody); + Memory.WriteInt32Unchecked(BaseControllerOffset + 0x10, (int)SingleColorButtons); + Memory.WriteInt32Unchecked(BaseControllerOffset + 0x14, (int)SplitColorDesc); - Ns.Memory.WriteInt32(BaseControllerOffset + 0x18, (int)LeftColorBody); - Ns.Memory.WriteInt32(BaseControllerOffset + 0x1c, (int)LeftColorButtons); + Memory.WriteInt32Unchecked(BaseControllerOffset + 0x18, (int)LeftColorBody); + Memory.WriteInt32Unchecked(BaseControllerOffset + 0x1c, (int)LeftColorButtons); - Ns.Memory.WriteInt32(BaseControllerOffset + 0x20, (int)RightColorBody); - Ns.Memory.WriteInt32(BaseControllerOffset + 0x24, (int)RightColorButtons); + Memory.WriteInt32Unchecked(BaseControllerOffset + 0x20, (int)RightColorBody); + Memory.WriteInt32Unchecked(BaseControllerOffset + 0x24, (int)RightColorButtons); } public void SetJoyconButton( @@ -165,116 +162,102 @@ namespace Ryujinx.Core.Input { foreach (long Position in ShMemPositions) { - WriteJoyconButtons( - Position, - ControllerId, - ControllerLayout, - Buttons, - LeftStick, - RightStick); + long ControllerOffset = Position + HidControllersOffset; + + ControllerOffset += (int)ControllerId * HidControllerSize; + + ControllerOffset += HidControllerHeaderSize; + + ControllerOffset += (int)ControllerLayout * HidControllerLayoutsSize; + + long LastEntry = Memory.ReadInt64Unchecked(ControllerOffset + 0x10); + + long CurrEntry = (LastEntry + 1) % HidEntryCount; + + long Timestamp = GetTimestamp(); + + Memory.WriteInt64Unchecked(ControllerOffset + 0x0, Timestamp); + Memory.WriteInt64Unchecked(ControllerOffset + 0x8, HidEntryCount); + Memory.WriteInt64Unchecked(ControllerOffset + 0x10, CurrEntry); + Memory.WriteInt64Unchecked(ControllerOffset + 0x18, HidEntryCount - 1); + + ControllerOffset += HidControllersLayoutHeaderSize; + + ControllerOffset += CurrEntry * HidControllersInputEntrySize; + + Memory.WriteInt64Unchecked(ControllerOffset + 0x0, Timestamp); + Memory.WriteInt64Unchecked(ControllerOffset + 0x8, Timestamp); + + Memory.WriteInt64Unchecked(ControllerOffset + 0x10, (uint)Buttons); + + Memory.WriteInt32Unchecked(ControllerOffset + 0x18, LeftStick.DX); + Memory.WriteInt32Unchecked(ControllerOffset + 0x1c, LeftStick.DY); + + Memory.WriteInt64Unchecked(ControllerOffset + 0x20, RightStick.DX); + Memory.WriteInt64Unchecked(ControllerOffset + 0x24, RightStick.DY); + + Memory.WriteInt64Unchecked(ControllerOffset + 0x28, + (uint)HidControllerConnState.Controller_State_Connected | + (uint)HidControllerConnState.Controller_State_Wired); } } } - private void WriteJoyconButtons( - long BasePosition, - HidControllerId ControllerId, - HidControllerLayouts ControllerLayout, - HidControllerButtons Buttons, - HidJoystickPosition LeftStick, - HidJoystickPosition RightStick) - { - long ControllerOffset = BasePosition + HidControllersOffset; - - ControllerOffset += (int)ControllerId * HidControllerSize; - - ControllerOffset += HidControllerHeaderSize; - - ControllerOffset += (int)ControllerLayout * HidControllerLayoutsSize; - - CurrControllerEntry = (CurrControllerEntry + 1) % HidEntryCount; - - long Timestamp = GetTimestamp(); - - Ns.Memory.WriteInt64(ControllerOffset + 0x0, Timestamp); - Ns.Memory.WriteInt64(ControllerOffset + 0x8, HidEntryCount); - Ns.Memory.WriteInt64(ControllerOffset + 0x10, CurrControllerEntry); - Ns.Memory.WriteInt64(ControllerOffset + 0x18, HidEntryCount - 1); - - ControllerOffset += HidControllersLayoutHeaderSize; - - ControllerOffset += CurrControllerEntry * HidControllersInputEntrySize; - - Ns.Memory.WriteInt64(ControllerOffset + 0x0, Timestamp); - Ns.Memory.WriteInt64(ControllerOffset + 0x8, Timestamp); - - Ns.Memory.WriteInt64(ControllerOffset + 0x10, (uint)Buttons); - - Ns.Memory.WriteInt32(ControllerOffset + 0x18, LeftStick.DX); - Ns.Memory.WriteInt32(ControllerOffset + 0x1c, LeftStick.DY); - - Ns.Memory.WriteInt32(ControllerOffset + 0x20, RightStick.DX); - Ns.Memory.WriteInt32(ControllerOffset + 0x24, RightStick.DY); - - Ns.Memory.WriteInt64(ControllerOffset + 0x28, - (uint)HidControllerConnState.Controller_State_Connected | - (uint)HidControllerConnState.Controller_State_Wired); - } - public void SetTouchPoints(params HidTouchPoint[] Points) { lock (ShMemLock) { foreach (long Position in ShMemPositions) { - WriteTouchPoints(Position, Points); + long TouchScreenOffset = Position + HidTouchScreenOffset; + + long LastEntry = Memory.ReadInt64Unchecked(TouchScreenOffset + 0x10); + + long CurrEntry = (LastEntry + 1) % HidEntryCount; + + long Timestamp = GetTimestamp(); + + Memory.WriteInt64Unchecked(TouchScreenOffset + 0x0, Timestamp); + Memory.WriteInt64Unchecked(TouchScreenOffset + 0x8, HidEntryCount); + Memory.WriteInt64Unchecked(TouchScreenOffset + 0x10, CurrEntry); + Memory.WriteInt64Unchecked(TouchScreenOffset + 0x18, HidEntryCount - 1); + Memory.WriteInt64Unchecked(TouchScreenOffset + 0x20, Timestamp); + + long TouchEntryOffset = TouchScreenOffset + HidTouchHeaderSize; + + long LastEntryOffset = TouchEntryOffset + LastEntry * HidTouchEntrySize; + + long SampleCounter = Memory.ReadInt64Unchecked(LastEntryOffset) + 1; + + TouchEntryOffset += CurrEntry * HidTouchEntrySize; + + Memory.WriteInt64Unchecked(TouchEntryOffset + 0x0, SampleCounter); + Memory.WriteInt64Unchecked(TouchEntryOffset + 0x8, Points.Length); + + TouchEntryOffset += HidTouchEntryHeaderSize; + + const int Padding = 0; + + int Index = 0; + + foreach (HidTouchPoint Point in Points) + { + Memory.WriteInt64Unchecked(TouchEntryOffset + 0x0, Timestamp); + Memory.WriteInt32Unchecked(TouchEntryOffset + 0x8, Padding); + Memory.WriteInt32Unchecked(TouchEntryOffset + 0xc, Index++); + Memory.WriteInt32Unchecked(TouchEntryOffset + 0x10, Point.X); + Memory.WriteInt32Unchecked(TouchEntryOffset + 0x14, Point.Y); + Memory.WriteInt32Unchecked(TouchEntryOffset + 0x18, Point.DiameterX); + Memory.WriteInt32Unchecked(TouchEntryOffset + 0x1c, Point.DiameterY); + Memory.WriteInt32Unchecked(TouchEntryOffset + 0x20, Point.Angle); + Memory.WriteInt32Unchecked(TouchEntryOffset + 0x24, Padding); + + TouchEntryOffset += HidTouchEntryTouchSize; + } } } } - private void WriteTouchPoints(long BasePosition, params HidTouchPoint[] Points) - { - long TouchScreenOffset = BasePosition + HidTouchScreenOffset; - - long Timestamp = GetTimestamp(); - - CurrTouchEntry = (CurrTouchEntry + 1) % HidEntryCount; - - Ns.Memory.WriteInt64(TouchScreenOffset + 0x0, Timestamp); - Ns.Memory.WriteInt64(TouchScreenOffset + 0x8, HidEntryCount); - Ns.Memory.WriteInt64(TouchScreenOffset + 0x10, CurrTouchEntry); - Ns.Memory.WriteInt64(TouchScreenOffset + 0x18, HidEntryCount - 1); - Ns.Memory.WriteInt64(TouchScreenOffset + 0x20, Timestamp); - - long TouchEntryOffset = TouchScreenOffset + HidTouchHeaderSize; - - TouchEntryOffset += CurrTouchEntry * HidTouchEntrySize; - - Ns.Memory.WriteInt64(TouchEntryOffset + 0x0, CurrTouchSampleCounter++); - Ns.Memory.WriteInt64(TouchEntryOffset + 0x8, Points.Length); - - TouchEntryOffset += HidTouchEntryHeaderSize; - - const int Padding = 0; - - int Index = 0; - - foreach (HidTouchPoint Point in Points) - { - Ns.Memory.WriteInt64(TouchEntryOffset + 0x0, Timestamp); - Ns.Memory.WriteInt32(TouchEntryOffset + 0x8, Padding); - Ns.Memory.WriteInt32(TouchEntryOffset + 0xc, Index++); - Ns.Memory.WriteInt32(TouchEntryOffset + 0x10, Point.X); - Ns.Memory.WriteInt32(TouchEntryOffset + 0x14, Point.Y); - Ns.Memory.WriteInt32(TouchEntryOffset + 0x18, Point.DiameterX); - Ns.Memory.WriteInt32(TouchEntryOffset + 0x1c, Point.DiameterY); - Ns.Memory.WriteInt32(TouchEntryOffset + 0x20, Point.Angle); - Ns.Memory.WriteInt32(TouchEntryOffset + 0x24, Padding); - - TouchEntryOffset += HidTouchEntryTouchSize; - } - } - private long GetTimestamp() { return Environment.TickCount * 19_200; diff --git a/Ryujinx.Core/Loaders/Executable.cs b/Ryujinx.Core/Loaders/Executable.cs index fa204460f6..943b8e5107 100644 --- a/Ryujinx.Core/Loaders/Executable.cs +++ b/Ryujinx.Core/Loaders/Executable.cs @@ -34,7 +34,7 @@ namespace Ryujinx.Core.Loaders if (Exe.Mod0Offset == 0) { - int BssOffset = Exe.DataOffset + Exe.Data.Count; + int BssOffset = Exe.DataOffset + Exe.Data.Length; int BssSize = Exe.BssSize; MapBss(ImageBase + BssOffset, BssSize); @@ -92,18 +92,15 @@ namespace Ryujinx.Core.Loaders private void WriteData( long Position, - IList Data, + byte[] Data, MemoryType Type, AMemoryPerm Perm) { - Memory.Manager.Map(Position, Data.Count, (int)Type, AMemoryPerm.Write); + Memory.Manager.Map(Position, Data.Length, (int)Type, AMemoryPerm.Write); - for (int Index = 0; Index < Data.Count; Index++) - { - Memory.WriteByte(Position + Index, Data[Index]); - } + AMemoryHelper.WriteBytes(Memory, Position, Data); - Memory.Manager.Reprotect(Position, Data.Count, Perm); + Memory.Manager.Reprotect(Position, Data.Length, Perm); } private void MapBss(long Position, long Size) diff --git a/Ryujinx.Core/Loaders/Executables/IExecutable.cs b/Ryujinx.Core/Loaders/Executables/IExecutable.cs index 73787b1d24..09d0aab236 100644 --- a/Ryujinx.Core/Loaders/Executables/IExecutable.cs +++ b/Ryujinx.Core/Loaders/Executables/IExecutable.cs @@ -1,12 +1,10 @@ -using System.Collections.ObjectModel; - namespace Ryujinx.Core.Loaders.Executables { public interface IExecutable { - ReadOnlyCollection Text { get; } - ReadOnlyCollection RO { get; } - ReadOnlyCollection Data { get; } + byte[] Text { get; } + byte[] RO { get; } + byte[] Data { get; } int Mod0Offset { get; } int TextOffset { get; } diff --git a/Ryujinx.Core/Loaders/Executables/Nro.cs b/Ryujinx.Core/Loaders/Executables/Nro.cs index 3cbc4c5d49..9f4ef59f57 100644 --- a/Ryujinx.Core/Loaders/Executables/Nro.cs +++ b/Ryujinx.Core/Loaders/Executables/Nro.cs @@ -1,18 +1,12 @@ -using System; -using System.Collections.ObjectModel; using System.IO; namespace Ryujinx.Core.Loaders.Executables { class Nro : IExecutable { - private byte[] m_Text; - private byte[] m_RO; - private byte[] m_Data; - - public ReadOnlyCollection Text => Array.AsReadOnly(m_Text); - public ReadOnlyCollection RO => Array.AsReadOnly(m_RO); - public ReadOnlyCollection Data => Array.AsReadOnly(m_Data); + public byte[] Text { get; private set; } + public byte[] RO { get; private set; } + public byte[] Data { get; private set; } public int Mod0Offset { get; private set; } public int TextOffset { get; private set; } @@ -54,9 +48,9 @@ namespace Ryujinx.Core.Loaders.Executables return Reader.ReadBytes(Size); } - m_Text = Read(TextOffset, TextSize); - m_RO = Read(ROOffset, ROSize); - m_Data = Read(DataOffset, DataSize); + Text = Read(TextOffset, TextSize); + RO = Read(ROOffset, ROSize); + Data = Read(DataOffset, DataSize); } } } \ No newline at end of file diff --git a/Ryujinx.Core/Loaders/Executables/Nso.cs b/Ryujinx.Core/Loaders/Executables/Nso.cs index 7b8bf253a7..7341ba6226 100644 --- a/Ryujinx.Core/Loaders/Executables/Nso.cs +++ b/Ryujinx.Core/Loaders/Executables/Nso.cs @@ -1,19 +1,14 @@ using Ryujinx.Core.Loaders.Compression; using System; -using System.Collections.ObjectModel; using System.IO; namespace Ryujinx.Core.Loaders.Executables { class Nso : IExecutable { - private byte[] m_Text; - private byte[] m_RO; - private byte[] m_Data; - - public ReadOnlyCollection Text => Array.AsReadOnly(m_Text); - public ReadOnlyCollection RO => Array.AsReadOnly(m_RO); - public ReadOnlyCollection Data => Array.AsReadOnly(m_Data); + public byte[] Text { get; private set; } + public byte[] RO { get; private set; } + public byte[] Data { get; private set; } public int Mod0Offset { get; private set; } public int TextOffset { get; private set; } @@ -57,9 +52,9 @@ namespace Ryujinx.Core.Loaders.Executables byte[] BuildId = Reader.ReadBytes(0x20); - int TextSize = Reader.ReadInt32(); - int ROSize = Reader.ReadInt32(); - int DataSize = Reader.ReadInt32(); + int TextSize = Reader.ReadInt32(); + int ROSize = Reader.ReadInt32(); + int DataSize = Reader.ReadInt32(); Input.Seek(0x24, SeekOrigin.Current); @@ -82,38 +77,38 @@ namespace Ryujinx.Core.Loaders.Executables //Text segment Input.Seek(TextOffset, SeekOrigin.Begin); - m_Text = Reader.ReadBytes(TextSize); + Text = Reader.ReadBytes(TextSize); if (Flags.HasFlag(NsoFlags.IsTextCompressed) || true) { - m_Text = Lz4.Decompress(m_Text, TextDecSize); + Text = Lz4.Decompress(Text, TextDecSize); } //Read-only data segment Input.Seek(ROOffset, SeekOrigin.Begin); - m_RO = Reader.ReadBytes(ROSize); + RO = Reader.ReadBytes(ROSize); if (Flags.HasFlag(NsoFlags.IsROCompressed) || true) { - m_RO = Lz4.Decompress(m_RO, RODecSize); + RO = Lz4.Decompress(RO, RODecSize); } //Data segment Input.Seek(DataOffset, SeekOrigin.Begin); - m_Data = Reader.ReadBytes(DataSize); + Data = Reader.ReadBytes(DataSize); if (Flags.HasFlag(NsoFlags.IsDataCompressed) || true) { - m_Data = Lz4.Decompress(m_Data, DataDecSize); + Data = Lz4.Decompress(Data, DataDecSize); } - using (MemoryStream Text = new MemoryStream(m_Text)) + using (MemoryStream TextMS = new MemoryStream(Text)) { - BinaryReader TextReader = new BinaryReader(Text); + BinaryReader TextReader = new BinaryReader(TextMS); - Text.Seek(4, SeekOrigin.Begin); + TextMS.Seek(4, SeekOrigin.Begin); Mod0Offset = TextReader.ReadInt32(); } diff --git a/Ryujinx.Core/OsHle/Horizon.cs b/Ryujinx.Core/OsHle/Horizon.cs index a8f5df7c9d..c61e18e98f 100644 --- a/Ryujinx.Core/OsHle/Horizon.cs +++ b/Ryujinx.Core/OsHle/Horizon.cs @@ -52,6 +52,8 @@ namespace Ryujinx.Core.OsHle HidHandle = Handles.GenerateId(HidSharedMem); FontHandle = Handles.GenerateId(new HSharedMem()); + + HidSharedMem.AddVirtualPosition(0); } public void LoadCart(string ExeFsDir, string RomFsFile = null) diff --git a/Ryujinx.Core/OsHle/Ipc/IpcMessage.cs b/Ryujinx.Core/OsHle/Ipc/IpcMessage.cs index cc26df1082..ebb3dbca04 100644 --- a/Ryujinx.Core/OsHle/Ipc/IpcMessage.cs +++ b/Ryujinx.Core/OsHle/Ipc/IpcMessage.cs @@ -217,16 +217,26 @@ namespace Ryujinx.Core.OsHle.Ipc public long GetSendBuffPtr() { - if (SendBuff.Count > 0 && SendBuff[0].Position != 0) + if (SendBuff.Count > 0 && SendBuff[0].Size != 0) { return SendBuff[0].Position; } - if (PtrBuff.Count > 0 && PtrBuff[0].Position != 0) + if (PtrBuff.Count > 0 && PtrBuff[0].Size != 0) { return PtrBuff[0].Position; } + if (ReceiveBuff.Count > 0 && ReceiveBuff[0].Size != 0) + { + return ReceiveBuff[0].Position; + } + + if (RecvListBuff.Count > 0 && RecvListBuff[0].Size != 0) + { + return RecvListBuff[0].Position; + } + return -1; } } diff --git a/Ryujinx.Core/OsHle/Services/Nv/ServiceNvDrv.cs b/Ryujinx.Core/OsHle/Services/Nv/ServiceNvDrv.cs index 515c15e034..3c0c46fec9 100644 --- a/Ryujinx.Core/OsHle/Services/Nv/ServiceNvDrv.cs +++ b/Ryujinx.Core/OsHle/Services/Nv/ServiceNvDrv.cs @@ -81,7 +81,7 @@ namespace Ryujinx.Core.OsHle.IpcServices.NvServices FileDesc FdData = Context.Ns.Os.Fds.GetData(Fd); - long Position = Context.Request.PtrBuff[0].Position; + long Position = Context.Request.GetSendBuffPtr(); Context.ResponseData.Write(0); @@ -139,7 +139,7 @@ namespace Ryujinx.Core.OsHle.IpcServices.NvServices private static long NvGpuAsIoctlBindChannel(ServiceCtx Context) { - long Position = Context.Request.PtrBuff[0].Position; + long Position = Context.Request.GetSendBuffPtr(); int Fd = Context.Memory.ReadInt32(Position); @@ -148,7 +148,7 @@ namespace Ryujinx.Core.OsHle.IpcServices.NvServices private static long NvGpuAsIoctlAllocSpace(ServiceCtx Context) { - long Position = Context.Request.PtrBuff[0].Position; + long Position = Context.Request.GetSendBuffPtr(); MemReader Reader = new MemReader(Context.Memory, Position); @@ -174,7 +174,7 @@ namespace Ryujinx.Core.OsHle.IpcServices.NvServices private static long NvGpuAsIoctlMapBufferEx(ServiceCtx Context) { - long Position = Context.Request.PtrBuff[0].Position; + long Position = Context.Request.GetSendBuffPtr(); MemReader Reader = new MemReader(Context.Memory, Position); @@ -207,7 +207,7 @@ namespace Ryujinx.Core.OsHle.IpcServices.NvServices private static long NvGpuAsIoctlGetVaRegions(ServiceCtx Context) { - long Position = Context.Request.PtrBuff[0].Position; + long Position = Context.Request.GetSendBuffPtr(); MemReader Reader = new MemReader(Context.Memory, Position); MemWriter Writer = new MemWriter(Context.Memory, Position); @@ -237,7 +237,7 @@ namespace Ryujinx.Core.OsHle.IpcServices.NvServices private static long NvGpuAsIoctlInitializeEx(ServiceCtx Context) { - long Position = Context.Request.PtrBuff[0].Position; + long Position = Context.Request.GetSendBuffPtr(); MemReader Reader = new MemReader(Context.Memory, Position); @@ -254,7 +254,7 @@ namespace Ryujinx.Core.OsHle.IpcServices.NvServices private static long NvHostIoctlCtrlGetConfig(ServiceCtx Context) { - long Position = Context.Request.PtrBuff[0].Position; + long Position = Context.Request.GetSendBuffPtr(); MemReader Reader = new MemReader(Context.Memory, Position); MemWriter Writer = new MemWriter(Context.Memory, Position + 0x82); @@ -269,7 +269,7 @@ namespace Ryujinx.Core.OsHle.IpcServices.NvServices private static long NvHostIoctlCtrlEventWait(ServiceCtx Context) { - long Position = Context.Request.PtrBuff[0].Position; + long Position = Context.Request.GetSendBuffPtr(); MemReader Reader = new MemReader(Context.Memory, Position); @@ -285,7 +285,7 @@ namespace Ryujinx.Core.OsHle.IpcServices.NvServices private static long NvGpuIoctlZcullGetCtxSize(ServiceCtx Context) { - long Position = Context.Request.PtrBuff[0].Position; + long Position = Context.Request.GetSendBuffPtr(); Context.Memory.WriteInt32(Position, 1); @@ -294,7 +294,7 @@ namespace Ryujinx.Core.OsHle.IpcServices.NvServices private static long NvGpuIoctlZcullGetInfo(ServiceCtx Context) { - long Position = Context.Request.PtrBuff[0].Position; + long Position = Context.Request.GetSendBuffPtr(); MemWriter Writer = new MemWriter(Context.Memory, Position); @@ -314,7 +314,7 @@ namespace Ryujinx.Core.OsHle.IpcServices.NvServices private static long NvGpuIoctlGetCharacteristics(ServiceCtx Context) { - long Position = Context.Request.PtrBuff[0].Position; + long Position = Context.Request.GetSendBuffPtr(); MemReader Reader = new MemReader(Context.Memory, Position); MemWriter Writer = new MemWriter(Context.Memory, Position); @@ -376,7 +376,7 @@ namespace Ryujinx.Core.OsHle.IpcServices.NvServices private static long NvGpuIoctlGetTpcMasks(ServiceCtx Context) { - long Position = Context.Request.PtrBuff[0].Position; + long Position = Context.Request.GetSendBuffPtr(); MemReader Reader = new MemReader(Context.Memory, Position); @@ -390,7 +390,7 @@ namespace Ryujinx.Core.OsHle.IpcServices.NvServices private static long NvGpuIoctlZbcGetActiveSlotMask(ServiceCtx Context) { - long Position = Context.Request.PtrBuff[0].Position; + long Position = Context.Request.GetSendBuffPtr(); Context.Memory.WriteInt32(Position + 0, 7); Context.Memory.WriteInt32(Position + 4, 1); @@ -400,14 +400,14 @@ namespace Ryujinx.Core.OsHle.IpcServices.NvServices private static long NvMapIoctlChannelSetUserData(ServiceCtx Context) { - long Position = Context.Request.PtrBuff[0].Position; + long Position = Context.Request.GetSendBuffPtr(); return 0; } private static long NvMapIoctlChannelSetNvMap(ServiceCtx Context) { - long Position = Context.Request.PtrBuff[0].Position; + long Position = Context.Request.GetSendBuffPtr(); int Fd = Context.Memory.ReadInt32(Position); @@ -416,7 +416,7 @@ namespace Ryujinx.Core.OsHle.IpcServices.NvServices private static long NvMapIoctlChannelSubmitGpFifo(ServiceCtx Context) { - long Position = Context.Request.PtrBuff[0].Position; + long Position = Context.Request.GetSendBuffPtr(); MemReader Reader = new MemReader(Context.Memory, Position); MemWriter Writer = new MemWriter(Context.Memory, Position + 0x10); @@ -455,7 +455,7 @@ namespace Ryujinx.Core.OsHle.IpcServices.NvServices private static long NvMapIoctlChannelAllocObjCtx(ServiceCtx Context) { - long Position = Context.Request.PtrBuff[0].Position; + long Position = Context.Request.GetSendBuffPtr(); int ClassNum = Context.Memory.ReadInt32(Position + 0); int Flags = Context.Memory.ReadInt32(Position + 4); @@ -467,7 +467,7 @@ namespace Ryujinx.Core.OsHle.IpcServices.NvServices private static long NvMapIoctlChannelZcullBind(ServiceCtx Context) { - long Position = Context.Request.PtrBuff[0].Position; + long Position = Context.Request.GetSendBuffPtr(); MemReader Reader = new MemReader(Context.Memory, Position); @@ -480,7 +480,7 @@ namespace Ryujinx.Core.OsHle.IpcServices.NvServices private static long NvMapIoctlChannelSetErrorNotifier(ServiceCtx Context) { - long Position = Context.Request.PtrBuff[0].Position; + long Position = Context.Request.GetSendBuffPtr(); MemReader Reader = new MemReader(Context.Memory, Position); @@ -494,7 +494,7 @@ namespace Ryujinx.Core.OsHle.IpcServices.NvServices private static long NvMapIoctlChannelSetPriority(ServiceCtx Context) { - long Position = Context.Request.PtrBuff[0].Position; + long Position = Context.Request.GetSendBuffPtr(); int Priority = Context.Memory.ReadInt32(Position); @@ -503,7 +503,7 @@ namespace Ryujinx.Core.OsHle.IpcServices.NvServices private static long NvMapIoctlChannelAllocGpFifoEx2(ServiceCtx Context) { - long Position = Context.Request.PtrBuff[0].Position; + long Position = Context.Request.GetSendBuffPtr(); MemReader Reader = new MemReader(Context.Memory, Position); MemWriter Writer = new MemWriter(Context.Memory, Position + 0xc); diff --git a/Ryujinx.Core/OsHle/Svc/SvcMemory.cs b/Ryujinx.Core/OsHle/Svc/SvcMemory.cs index bf946e4f86..c15f449bf1 100644 --- a/Ryujinx.Core/OsHle/Svc/SvcMemory.cs +++ b/Ryujinx.Core/OsHle/Svc/SvcMemory.cs @@ -55,6 +55,24 @@ namespace Ryujinx.Core.OsHle.Svc long Src = (long)ThreadState.X1; long Size = (long)ThreadState.X2; + if (!IsValidPosition(Src)) + { + Logging.Warn($"Tried to map Memory at invalid src address {Src:x16}!"); + + ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidMemRange); + + return; + } + + if (!IsValidMapPosition(Dst)) + { + Logging.Warn($"Tried to map Memory at invalid dst address {Dst:x16}!"); + + ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidMemRange); + + return; + } + AMemoryMapInfo SrcInfo = Memory.Manager.GetMapInfo(Src); Memory.Manager.Map(Dst, Size, (int)MemoryType.MappedMemory, SrcInfo.Perm); @@ -72,6 +90,24 @@ namespace Ryujinx.Core.OsHle.Svc long Src = (long)ThreadState.X1; long Size = (long)ThreadState.X2; + if (!IsValidPosition(Src)) + { + Logging.Warn($"Tried to unmap Memory at invalid src address {Src:x16}!"); + + ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidMemRange); + + return; + } + + if (!IsValidMapPosition(Dst)) + { + Logging.Warn($"Tried to unmap Memory at invalid dst address {Dst:x16}!"); + + ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidMemRange); + + return; + } + AMemoryMapInfo DstInfo = Memory.Manager.GetMapInfo(Dst); Memory.Manager.Unmap(Dst, Size, (int)MemoryType.MappedMemory); @@ -92,9 +128,11 @@ namespace Ryujinx.Core.OsHle.Svc if (MapInfo == null) { - ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidMemRange); + long AddrSpaceEnd = MemoryRegions.AddrSpaceStart + MemoryRegions.AddrSpaceSize; - return; + long ReservedSize = (long)(ulong.MaxValue - (ulong)AddrSpaceEnd) + 1; + + MapInfo = new AMemoryMapInfo(AddrSpaceEnd, ReservedSize, (int)MemoryType.Reserved, 0, AMemoryPerm.None); } Memory.WriteInt64(InfoPtr + 0x00, MapInfo.Position); @@ -118,15 +156,26 @@ namespace Ryujinx.Core.OsHle.Svc long Size = (long)ThreadState.X2; int Perm = (int)ThreadState.X3; + if (!IsValidPosition(Src)) + { + Logging.Warn($"Tried to map SharedMemory at invalid address {Src:x16}!"); + + ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidMemRange); + + return; + } + HSharedMem SharedMem = Ns.Os.Handles.GetData(Handle); if (SharedMem != null) { + Memory.Manager.Map(Src, Size, (int)MemoryType.SharedMemory, AMemoryPerm.Write); + AMemoryHelper.FillWithZeros(Memory, Src, (int)Size); - SharedMem.AddVirtualPosition(Src); + Memory.Manager.Reprotect(Src, Size, (AMemoryPerm)Perm); - Memory.Manager.Map(Src, Size, (int)MemoryType.SharedMemory, (AMemoryPerm)Perm); + SharedMem.AddVirtualPosition(Src); ThreadState.X0 = 0; } @@ -136,14 +185,25 @@ namespace Ryujinx.Core.OsHle.Svc private void SvcUnmapSharedMemory(AThreadState ThreadState) { - int Handle = (int)ThreadState.X0; - long Position = (long)ThreadState.X1; - long Size = (long)ThreadState.X2; + int Handle = (int)ThreadState.X0; + long Src = (long)ThreadState.X1; + long Size = (long)ThreadState.X2; + + if (!IsValidPosition(Src)) + { + Logging.Warn($"Tried to unmap SharedMemory at invalid address {Src:x16}!"); + + ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidMemRange); + + return; + } HSharedMem HndData = Ns.Os.Handles.GetData(Handle); if (HndData != null) { + Memory.Manager.Unmap(Src, Size, (int)MemoryType.SharedMemory); + ThreadState.X0 = 0; } @@ -152,20 +212,41 @@ namespace Ryujinx.Core.OsHle.Svc private void SvcCreateTransferMemory(AThreadState ThreadState) { - long Position = (long)ThreadState.X1; - long Size = (long)ThreadState.X2; - int Perm = (int)ThreadState.X3; + long Src = (long)ThreadState.X1; + long Size = (long)ThreadState.X2; + int Perm = (int)ThreadState.X3; - AMemoryMapInfo MapInfo = Memory.Manager.GetMapInfo(Position); + if (!IsValidPosition(Src)) + { + Logging.Warn($"Tried to create TransferMemory at invalid address {Src:x16}!"); - Memory.Manager.Reprotect(Position, Size, (AMemoryPerm)Perm); + ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidMemRange); - HTransferMem HndData = new HTransferMem(Memory, MapInfo.Perm, Position, Size); + return; + } + + AMemoryMapInfo MapInfo = Memory.Manager.GetMapInfo(Src); + + Memory.Manager.Reprotect(Src, Size, (AMemoryPerm)Perm); + + HTransferMem HndData = new HTransferMem(Memory, MapInfo.Perm, Src, Size); int Handle = Ns.Os.Handles.GenerateId(HndData); ThreadState.X1 = (ulong)Handle; ThreadState.X0 = 0; } + + private static bool IsValidPosition(long Position) + { + return Position >= MemoryRegions.AddrSpaceStart && + Position < MemoryRegions.AddrSpaceStart + MemoryRegions.AddrSpaceSize; + } + + private static bool IsValidMapPosition(long Position) + { + return Position >= MemoryRegions.MapRegionAddress && + Position < MemoryRegions.MapRegionAddress + MemoryRegions.MapRegionSize; + } } } \ No newline at end of file diff --git a/Ryujinx.Core/Switch.cs b/Ryujinx.Core/Switch.cs index dff7802b7d..f7ce109f6f 100644 --- a/Ryujinx.Core/Switch.cs +++ b/Ryujinx.Core/Switch.cs @@ -30,7 +30,7 @@ namespace Ryujinx.Core VFs = new VirtualFs(); - Hid = new Hid(this); + Hid = new Hid(Memory); Statistics = new PerformanceStatistics(); diff --git a/Ryujinx.Tests/Cpu/CpuTest.cs b/Ryujinx.Tests/Cpu/CpuTest.cs index c4f0bbd74a..0b35a7a6c3 100644 --- a/Ryujinx.Tests/Cpu/CpuTest.cs +++ b/Ryujinx.Tests/Cpu/CpuTest.cs @@ -47,7 +47,7 @@ namespace Ryujinx.Tests.Cpu protected void Opcode(uint Opcode) { - Thread.Memory.WriteUInt32(Position, Opcode); + Thread.Memory.WriteUInt32Unchecked(Position, Opcode); Position += 4; } diff --git a/Ryujinx/Ui/Program.cs b/Ryujinx/Ui/Program.cs index b5f8cb9766..b67e52bdc3 100644 --- a/Ryujinx/Ui/Program.cs +++ b/Ryujinx/Ui/Program.cs @@ -10,6 +10,8 @@ namespace Ryujinx { static void Main(string[] args) { + AOptimizations.DisableMemoryChecks = true; + Config.Read(); Console.Title = "Ryujinx Console"; From 28275a897696a707e222ee14dce20d4b8f65ed58 Mon Sep 17 00:00:00 2001 From: gdkchan Date: Sat, 10 Mar 2018 20:51:55 -0300 Subject: [PATCH 16/28] Do not sign-extend timestamps --- ChocolArm64/State/AThreadState.cs | 6 +++--- Ryujinx.Core/Hid/Hid.cs | 4 ++-- Ryujinx.Core/OsHle/Svc/SvcSystem.cs | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/ChocolArm64/State/AThreadState.cs b/ChocolArm64/State/AThreadState.cs index 2d988a6536..d86f5bf9c8 100644 --- a/ChocolArm64/State/AThreadState.cs +++ b/ChocolArm64/State/AThreadState.cs @@ -38,10 +38,10 @@ namespace ChocolArm64.State public uint CtrEl0 => 0x8444c004; public uint DczidEl0 => 0x00000004; - private const long TicksPerS = 19_200_000; - private const long TicksPerMS = TicksPerS / 1_000; + private const ulong TicksPerS = 19_200_000; + private const ulong TicksPerMS = TicksPerS / 1_000; - public long CntpctEl0 => Environment.TickCount * TicksPerMS; + public ulong CntpctEl0 => (ulong)Environment.TickCount * TicksPerMS; public event EventHandler Break; public event EventHandler SvcCall; diff --git a/Ryujinx.Core/Hid/Hid.cs b/Ryujinx.Core/Hid/Hid.cs index 76ecf5ffdf..c287564d3a 100644 --- a/Ryujinx.Core/Hid/Hid.cs +++ b/Ryujinx.Core/Hid/Hid.cs @@ -258,9 +258,9 @@ namespace Ryujinx.Core.Input } } - private long GetTimestamp() + private static long GetTimestamp() { - return Environment.TickCount * 19_200; + return (long)((ulong)Environment.TickCount * 19_200); } } } \ No newline at end of file diff --git a/Ryujinx.Core/OsHle/Svc/SvcSystem.cs b/Ryujinx.Core/OsHle/Svc/SvcSystem.cs index a276fe3c85..f700903526 100644 --- a/Ryujinx.Core/OsHle/Svc/SvcSystem.cs +++ b/Ryujinx.Core/OsHle/Svc/SvcSystem.cs @@ -67,7 +67,7 @@ namespace Ryujinx.Core.OsHle.Svc private void SvcGetSystemTick(AThreadState ThreadState) { - ThreadState.X0 = (ulong)ThreadState.CntpctEl0; + ThreadState.X0 = ThreadState.CntpctEl0; } private void SvcConnectToNamedPort(AThreadState ThreadState) From 3aaa4717b6f7400bac862e589a1f345e70e78d56 Mon Sep 17 00:00:00 2001 From: Ac_K Date: Mon, 12 Mar 2018 02:05:39 +0100 Subject: [PATCH 17/28] Implement BSD Service (#54) * Implement BSD Service - Implementation of bsd:s & bsd:u. - Adding an EndianSwap class. * Corrections #1 * Correction2 --- Ryujinx.Core/OsHle/Services/Bsd/ServiceBsd.cs | 434 +++++++++++++++++- Ryujinx.Core/OsHle/Services/ServiceFactory.cs | 1 + Ryujinx.Core/OsHle/Utilities/EndianSwap.cs | 7 + 3 files changed, 423 insertions(+), 19 deletions(-) create mode 100644 Ryujinx.Core/OsHle/Utilities/EndianSwap.cs diff --git a/Ryujinx.Core/OsHle/Services/Bsd/ServiceBsd.cs b/Ryujinx.Core/OsHle/Services/Bsd/ServiceBsd.cs index b594441272..da1e51e17c 100644 --- a/Ryujinx.Core/OsHle/Services/Bsd/ServiceBsd.cs +++ b/Ryujinx.Core/OsHle/Services/Bsd/ServiceBsd.cs @@ -1,14 +1,64 @@ +using ChocolArm64.Memory; using Ryujinx.Core.OsHle.Ipc; +using Ryujinx.Core.OsHle.Utilities; +using System; using System.Collections.Generic; +using System.IO; +using System.Net; +using System.Net.Sockets; +using System.Threading.Tasks; namespace Ryujinx.Core.OsHle.IpcServices.Bsd { + + //bsd_errno == (SocketException.ErrorCode - 10000) + //https://github.com/freebsd/freebsd/blob/master/sys/sys/errno.h + public enum BsdError + { + ENOTSOCK = 38, /* Socket operation on non-socket */ + EDESTADDRREQ = 39, /* Destination address required */ + EMSGSIZE = 40, /* Message too long */ + EPROTOTYPE = 41, /* Protocol wrong type for socket */ + ENOPROTOOPT = 42, /* Protocol not available */ + EPROTONOSUPPORT = 43, /* Protocol not supported */ + ESOCKTNOSUPPORT = 44, /* Socket type not supported */ + EOPNOTSUPP = 45, /* Operation not supported */ + EPFNOSUPPORT = 46, /* Protocol family not supported */ + EAFNOSUPPORT = 47, /* Address family not supported by protocol family */ + EADDRINUSE = 48, /* Address already in use */ + EADDRNOTAVAIL = 49, /* Can't assign requested address */ + ENETDOWN = 50, /* Network is down */ + ENETUNREACH = 51, /* Network is unreachable */ + ENETRESET = 52, /* Network dropped connection on reset */ + ECONNABORTED = 53, /* Software caused connection abort */ + ECONNRESET = 54, /* Connection reset by peer */ + ENOBUFS = 55, /* No buffer space available */ + EISCONN = 56, /* Socket is already connected */ + ENOTCONN = 57, /* Socket is not connected */ + ESHUTDOWN = 58, /* Can't send after socket shutdown */ + ETOOMANYREFS = 59, /* Too many references: can't splice */ + ETIMEDOUT = 60, /* Operation timed out */ + ECONNREFUSED = 61 /* Connection refused */ + } + + class SocketBsd + { + public int Family; + public int Type; + public int Protocol; + public IPAddress IpAddress; + public IPEndPoint RemoteEP; + public Socket Handle; + } + class ServiceBsd : IIpcService { private Dictionary m_Commands; public IReadOnlyDictionary Commands => m_Commands; + private List Sockets = new List(); + public ServiceBsd() { m_Commands = new Dictionary() @@ -16,26 +66,31 @@ namespace Ryujinx.Core.OsHle.IpcServices.Bsd { 0, Initialize }, { 1, StartMonitoring }, { 2, Socket }, + { 6, Poll }, + { 8, Recv }, { 10, Send }, - { 14, Connect } + { 11, SendTo }, + { 12, Accept }, + { 13, Bind }, + { 14, Connect }, + { 18, Listen }, + { 21, SetSockOpt }, + { 26, Close } }; } - //Initialize(u32, u32, u32, u32, u32, u32, u32, u32, u64 pid, u64 transferMemorySize, pid, KObject) -> u32 bsd_errno + //(u32, u32, u32, u32, u32, u32, u32, u32, u64 pid, u64 transferMemorySize, pid, KObject) -> u32 bsd_errno public long Initialize(ServiceCtx Context) { /* typedef struct { u32 version; // Observed 1 on 2.0 LibAppletWeb, 2 on 3.0. - u32 tcp_tx_buf_size; // Size of the TCP transfer (send) buffer (initial or fixed). u32 tcp_rx_buf_size; // Size of the TCP recieve buffer (initial or fixed). u32 tcp_tx_buf_max_size; // Maximum size of the TCP transfer (send) buffer. If it is 0, the size of the buffer is fixed to its initial value. u32 tcp_rx_buf_max_size; // Maximum size of the TCP receive buffer. If it is 0, the size of the buffer is fixed to its initial value. - u32 udp_tx_buf_size; // Size of the UDP transfer (send) buffer (typically 0x2400 bytes). u32 udp_rx_buf_size; // Size of the UDP receive buffer (typically 0xA500 bytes). - u32 sb_efficiency; // Number of buffers for each socket (standard values range from 1 to 8). } BsdBufferConfig; */ @@ -52,7 +107,7 @@ namespace Ryujinx.Core.OsHle.IpcServices.Bsd return 0; } - //StartMonitoring(u64, pid) + //(u64, pid) public long StartMonitoring(ServiceCtx Context) { //Todo: Stub @@ -60,37 +115,378 @@ namespace Ryujinx.Core.OsHle.IpcServices.Bsd return 0; } - //Socket(u32 domain, u32 type, u32 protocol) -> (i32 ret, u32 bsd_errno) + //(u32 domain, u32 type, u32 protocol) -> (i32 ret, u32 bsd_errno) public long Socket(ServiceCtx Context) { - Context.ResponseData.Write(0); - Context.ResponseData.Write(0); + SocketBsd NewBSDSocket = new SocketBsd + { + Family = Context.RequestData.ReadInt32(), + Type = Context.RequestData.ReadInt32(), + Protocol = Context.RequestData.ReadInt32() + }; - //Todo: Stub + Sockets.Add(NewBSDSocket); + + Sockets[Sockets.Count - 1].Handle = new Socket((AddressFamily)Sockets[Sockets.Count - 1].Family, + (SocketType)Sockets[Sockets.Count - 1].Type, + (ProtocolType)Sockets[Sockets.Count - 1].Protocol); + + Context.ResponseData.Write(Sockets.Count - 1); + Context.ResponseData.Write(0); return 0; } - //Connect(u32 socket, buffer) -> (i32 ret, u32 bsd_errno) - public long Connect(ServiceCtx Context) + //(u32, u32, buffer) -> (i32 ret, u32 bsd_errno, buffer) + public long Poll(ServiceCtx Context) { - Context.ResponseData.Write(0); - Context.ResponseData.Write(0); + int PollCount = Context.RequestData.ReadInt32(); + int TimeOut = Context.RequestData.ReadInt32(); - //Todo: Stub + //https://github.com/torvalds/linux/blob/master/include/uapi/asm-generic/poll.h + //https://msdn.microsoft.com/fr-fr/library/system.net.sockets.socket.poll(v=vs.110).aspx + //https://github.com/switchbrew/libnx/blob/e0457c4534b3c37426d83e1a620f82cb28c3b528/nx/source/services/bsd.c#L343 + //https://github.com/TuxSH/ftpd/blob/switch_pr/source/ftp.c#L1634 + //https://linux.die.net/man/2/poll + + byte[] SentBuffer = AMemoryHelper.ReadBytes(Context.Memory, + Context.Request.SendBuff[0].Position, + (int)Context.Request.SendBuff[0].Size); + int SocketId = Get32(SentBuffer, 0); + short RequestedEvents = (short)Get16(SentBuffer, 4); + short ReturnedEvents = (short)Get16(SentBuffer, 6); + + //Todo: Stub - Need to implemented the Type-22 buffer. + + Context.ResponseData.Write(1); + Context.ResponseData.Write(0); return 0; } - //Send(u32 socket, u32 flags, buffer) -> (i32 ret, u32 bsd_errno) + //(u32 socket, u32 flags) -> (i32 ret, u32 bsd_errno, buffer message) + public long Recv(ServiceCtx Context) + { + try + { + int SocketId = Context.RequestData.ReadInt32(); + int SocketFlags = Context.RequestData.ReadInt32(); + byte[] ReceivedBuffer = new byte[Context.Request.ReceiveBuff[0].Size]; + int ReadedBytes = Sockets[SocketId].Handle.Receive(ReceivedBuffer); + + //Logging.Debug("Received Buffer:" + Environment.NewLine + Logging.HexDump(ReceivedBuffer)); + + AMemoryHelper.WriteBytes(Context.Memory, Context.Request.ReceiveBuff[0].Position, ReceivedBuffer); + + Context.ResponseData.Write(ReadedBytes); + Context.ResponseData.Write(0); + } + catch (SocketException Ex) + { + Context.ResponseData.Write(-1); + Context.ResponseData.Write(Ex.ErrorCode - 10000); + } + + return 0; + } + + //(u32 socket, u32 flags, buffer) -> (i32 ret, u32 bsd_errno) public long Send(ServiceCtx Context) { - Context.ResponseData.Write(0); - Context.ResponseData.Write(0); + int SocketId = Context.RequestData.ReadInt32(); + int SocketFlags = Context.RequestData.ReadInt32(); + byte[] SentBuffer = AMemoryHelper.ReadBytes(Context.Memory, + Context.Request.SendBuff[0].Position, + (int)Context.Request.SendBuff[0].Size); - //Todo: Stub + try + { + //Logging.Debug("Sended Buffer:" + Environment.NewLine + Logging.HexDump(SendedBuffer)); + + int BytesSent = Sockets[SocketId].Handle.Send(SentBuffer); + + Context.ResponseData.Write(BytesSent); + Context.ResponseData.Write(0); + } + catch (SocketException Ex) + { + Context.ResponseData.Write(-1); + Context.ResponseData.Write(Ex.ErrorCode - 10000); + } return 0; } + + //(u32 socket, u32 flags, buffer, buffer) -> (i32 ret, u32 bsd_errno) + public long SendTo(ServiceCtx Context) + { + int SocketId = Context.RequestData.ReadInt32(); + int SocketFlags = Context.RequestData.ReadInt32(); + byte[] SentBuffer = AMemoryHelper.ReadBytes(Context.Memory, + Context.Request.SendBuff[0].Position, + (int)Context.Request.SendBuff[0].Size); + byte[] AddressBuffer = AMemoryHelper.ReadBytes(Context.Memory, + Context.Request.SendBuff[1].Position, + (int)Context.Request.SendBuff[1].Size); + + if (!Sockets[SocketId].Handle.Connected) + { + try + { + ParseAddrBuffer(SocketId, AddressBuffer); + + Sockets[SocketId].Handle.Connect(Sockets[SocketId].RemoteEP); + } + catch (SocketException Ex) + { + Context.ResponseData.Write(-1); + Context.ResponseData.Write(Ex.ErrorCode - 10000); + } + } + + try + { + //Logging.Debug("Sended Buffer:" + Environment.NewLine + Logging.HexDump(SendedBuffer)); + + int BytesSent = Sockets[SocketId].Handle.Send(SentBuffer); + + Context.ResponseData.Write(BytesSent); + Context.ResponseData.Write(0); + } + catch (SocketException Ex) + { + Context.ResponseData.Write(-1); + Context.ResponseData.Write(Ex.ErrorCode - 10000); + } + + return 0; + } + + //(u32 socket) -> (i32 ret, u32 bsd_errno, u32 addrlen, buffer addr) + public long Accept(ServiceCtx Context) + { + int SocketId = Context.RequestData.ReadInt32(); + long AddrBufferPtr = Context.Request.ReceiveBuff[0].Position; + + Socket HandleAccept = null; + + var TimeOut = Task.Factory.StartNew(() => + { + try + { + HandleAccept = Sockets[SocketId].Handle.Accept(); + } + catch (SocketException Ex) + { + Context.ResponseData.Write(-1); + Context.ResponseData.Write(Ex.ErrorCode - 10000); + } + }); + + TimeOut.Wait(10000); + + if (HandleAccept != null) + { + SocketBsd NewBSDSocket = new SocketBsd + { + IpAddress = ((IPEndPoint)Sockets[SocketId].Handle.LocalEndPoint).Address, + RemoteEP = ((IPEndPoint)Sockets[SocketId].Handle.LocalEndPoint), + Handle = HandleAccept + }; + + Sockets.Add(NewBSDSocket); + + using (MemoryStream MS = new MemoryStream()) + { + BinaryWriter Writer = new BinaryWriter(MS); + + Writer.Write((byte)0); + Writer.Write((byte)Sockets[Sockets.Count - 1].Handle.AddressFamily); + Writer.Write((Int16)((IPEndPoint)Sockets[Sockets.Count - 1].Handle.LocalEndPoint).Port); + + string[] IpAdress = Sockets[Sockets.Count - 1].IpAddress.ToString().Split('.'); + Writer.Write(byte.Parse(IpAdress[0])); + Writer.Write(byte.Parse(IpAdress[1])); + Writer.Write(byte.Parse(IpAdress[2])); + Writer.Write(byte.Parse(IpAdress[3])); + + AMemoryHelper.WriteBytes(Context.Memory, AddrBufferPtr, MS.ToArray()); + + Context.ResponseData.Write(Sockets.Count - 1); + Context.ResponseData.Write(0); + Context.ResponseData.Write(MS.Length); + } + } + else + { + Context.ResponseData.Write(-1); + Context.ResponseData.Write((int)BsdError.ETIMEDOUT); + } + + return 0; + } + + //(u32 socket, buffer) -> (i32 ret, u32 bsd_errno) + public long Bind(ServiceCtx Context) + { + int SocketId = Context.RequestData.ReadInt32(); + + byte[] AddressBuffer = AMemoryHelper.ReadBytes(Context.Memory, + Context.Request.SendBuff[0].Position, + (int)Context.Request.SendBuff[0].Size); + + try + { + ParseAddrBuffer(SocketId, AddressBuffer); + + Context.ResponseData.Write(0); + Context.ResponseData.Write(0); + } + catch (SocketException Ex) + { + Context.ResponseData.Write(-1); + Context.ResponseData.Write(Ex.ErrorCode - 10000); + } + + return 0; + } + + //(u32 socket, buffer) -> (i32 ret, u32 bsd_errno) + public long Connect(ServiceCtx Context) + { + int SocketId = Context.RequestData.ReadInt32(); + + byte[] AddressBuffer = AMemoryHelper.ReadBytes(Context.Memory, + Context.Request.SendBuff[0].Position, + (int)Context.Request.SendBuff[0].Size); + + try + { + ParseAddrBuffer(SocketId, AddressBuffer); + + Sockets[SocketId].Handle.Connect(Sockets[SocketId].RemoteEP); + + Context.ResponseData.Write(0); + Context.ResponseData.Write(0); + } + catch (SocketException Ex) + { + Context.ResponseData.Write(-1); + Context.ResponseData.Write(Ex.ErrorCode - 10000); + } + + return 0; + } + + //(u32 socket, u32 backlog) -> (i32 ret, u32 bsd_errno) + public long Listen(ServiceCtx Context) + { + int SocketId = Context.RequestData.ReadInt32(); + int BackLog = Context.RequestData.ReadInt32(); + + try + { + Sockets[SocketId].Handle.Bind(Sockets[SocketId].RemoteEP); + Sockets[SocketId].Handle.Listen(BackLog); + + Context.ResponseData.Write(0); + Context.ResponseData.Write(0); + } + catch (SocketException Ex) + { + Context.ResponseData.Write(-1); + Context.ResponseData.Write(Ex.ErrorCode - 10000); + } + + return 0; + } + + //(u32 socket, u32 level, u32 option_name, buffer) -> (i32 ret, u32 bsd_errno) + public long SetSockOpt(ServiceCtx Context) + { + int SocketId = Context.RequestData.ReadInt32(); + int SocketLevel = Context.RequestData.ReadInt32(); + int SocketOptionName = Context.RequestData.ReadInt32(); + + byte[] SocketOptionValue = AMemoryHelper.ReadBytes(Context.Memory, + Context.Request.PtrBuff[0].Position, + Context.Request.PtrBuff[0].Size); + + try + { + Sockets[SocketId].Handle.SetSocketOption((SocketOptionLevel)SocketLevel, + (SocketOptionName)SocketOptionName, + Get32(SocketOptionValue, 0)); + + Context.ResponseData.Write(0); + Context.ResponseData.Write(0); + } + catch (SocketException Ex) + { + Context.ResponseData.Write(-1); + Context.ResponseData.Write(Ex.ErrorCode - 10000); + } + + return 0; + } + + //(u32 socket) -> (i32 ret, u32 bsd_errno) + public long Close(ServiceCtx Context) + { + int SocketId = Context.RequestData.ReadInt32(); + + try + { + Sockets[SocketId].Handle.Close(); + Sockets[SocketId] = null; + + Context.ResponseData.Write(0); + Context.ResponseData.Write(0); + } + catch (SocketException Ex) + { + Context.ResponseData.Write(-1); + Context.ResponseData.Write(Ex.ErrorCode - 10000); + } + + return 0; + } + + public void ParseAddrBuffer(int SocketId, byte[] AddrBuffer) + { + using (MemoryStream MS = new MemoryStream(AddrBuffer)) + { + BinaryReader Reader = new BinaryReader(MS); + + int Size = Reader.ReadByte(); + int Family = Reader.ReadByte(); + int Port = EndianSwap.Swap16(Reader.ReadInt16()); + string IpAddress = Reader.ReadByte().ToString() + + "." + Reader.ReadByte().ToString() + + "." + Reader.ReadByte().ToString() + + "." + Reader.ReadByte().ToString(); + + Logging.Debug($"Try to connect to {IpAddress}:{Port}"); + + Sockets[SocketId].IpAddress = IPAddress.Parse(IpAddress); + Sockets[SocketId].RemoteEP = new IPEndPoint(Sockets[SocketId].IpAddress, Port); + } + } + + private int Get16(byte[] Data, int Address) + { + return + Data[Address + 0] << 0 | + Data[Address + 1] << 8; + } + + private int Get32(byte[] Data, int Address) + { + return + Data[Address + 0] << 0 | + Data[Address + 1] << 8 | + Data[Address + 2] << 16 | + Data[Address + 3] << 24; + } } } \ No newline at end of file diff --git a/Ryujinx.Core/OsHle/Services/ServiceFactory.cs b/Ryujinx.Core/OsHle/Services/ServiceFactory.cs index 31c8aa2c86..2b855d25b7 100644 --- a/Ryujinx.Core/OsHle/Services/ServiceFactory.cs +++ b/Ryujinx.Core/OsHle/Services/ServiceFactory.cs @@ -35,6 +35,7 @@ namespace Ryujinx.Core.OsHle.IpcServices case "appletOE": return new ServiceAppletOE(); case "audout:u": return new ServiceAudOut(); case "audren:u": return new ServiceAudRen(); + case "bsd:s": return new ServiceBsd(); case "bsd:u": return new ServiceBsd(); case "friend:a": return new ServiceFriend(); case "fsp-srv": return new ServiceFspSrv(); diff --git a/Ryujinx.Core/OsHle/Utilities/EndianSwap.cs b/Ryujinx.Core/OsHle/Utilities/EndianSwap.cs new file mode 100644 index 0000000000..6758f1f2f3 --- /dev/null +++ b/Ryujinx.Core/OsHle/Utilities/EndianSwap.cs @@ -0,0 +1,7 @@ +namespace Ryujinx.Core.OsHle.Utilities +{ + static class EndianSwap + { + public static short Swap16(short Value) => (short)(((Value >> 8) & 0xff) | (Value << 8)); + } +} From 7a27990faa557c5c93f52e5cb082d551ad119ed0 Mon Sep 17 00:00:00 2001 From: gdkchan Date: Mon, 12 Mar 2018 01:04:52 -0300 Subject: [PATCH 18/28] Allow more than one process, free resources on process dispose, implement SvcExitThread --- ChocolArm64/AThread.cs | 29 +- ChocolArm64/ATranslator.cs | 8 +- ChocolArm64/Instruction/AInstEmitException.cs | 16 ++ ChocolArm64/State/AThreadState.cs | 2 + Ryujinx.Core/Hid/Hid.cs | 30 +- Ryujinx.Core/Logging.cs | 7 +- Ryujinx.Core/OsHle/CondVar.cs | 2 +- Ryujinx.Core/OsHle/Handles/HDomain.cs | 62 ++-- Ryujinx.Core/OsHle/Handles/HNvMap.cs | 18 -- Ryujinx.Core/OsHle/Handles/HSharedMem.cs | 15 +- .../OsHle/Handles/KProcessHandleTable.cs | 63 +++++ Ryujinx.Core/OsHle/Homebrew.cs | 28 ++ Ryujinx.Core/OsHle/Horizon.cs | 139 ++++----- .../IdPoolWithObj.cs => IdDictionary.cs} | 59 ++-- Ryujinx.Core/OsHle/Ipc/IpcHandler.cs | 75 +++-- Ryujinx.Core/OsHle/Ipc/IpcMessageType.cs | 8 +- Ryujinx.Core/OsHle/Mutex.cs | 2 +- Ryujinx.Core/OsHle/Process.cs | 145 ++++++---- Ryujinx.Core/OsHle/ServiceCtx.cs | 3 + Ryujinx.Core/OsHle/ServiceMgr.cs | 109 +++++++ Ryujinx.Core/OsHle/Services/Aud/IAudioOut.cs | 2 +- .../OsHle/Services/Aud/IAudioRenderer.cs | 2 +- .../OsHle/Services/Hid/IAppletResource.cs | 12 +- Ryujinx.Core/OsHle/Services/Hid/ServiceHid.cs | 5 +- Ryujinx.Core/OsHle/Services/Nv/NvFd.cs | 12 + Ryujinx.Core/OsHle/Services/Nv/NvMap.cs | 12 + .../OsHle/Services/Nv/ServiceNvDrv.cs | 266 +++++++++++------- Ryujinx.Core/OsHle/Services/ObjHelper.cs | 4 +- Ryujinx.Core/OsHle/Services/Pl/ServicePl.cs | 12 +- Ryujinx.Core/OsHle/Services/ServiceFactory.cs | 64 ----- Ryujinx.Core/OsHle/Services/Sm/ServiceSm.cs | 4 +- .../OsHle/{ => Services/Vi}/Display.cs | 2 +- .../Services/Vi/IApplicationDisplayService.cs | 24 +- .../OsHle/Services/Vi/IHOSBinderDriver.cs | 5 +- Ryujinx.Core/OsHle/Services/Vi/NvFlinger.cs | 63 ++++- Ryujinx.Core/OsHle/Svc/SvcHandler.cs | 33 ++- Ryujinx.Core/OsHle/Svc/SvcMemory.cs | 28 +- Ryujinx.Core/OsHle/Svc/SvcSystem.cs | 21 +- Ryujinx.Core/OsHle/Svc/SvcThread.cs | 15 +- Ryujinx.Core/OsHle/Svc/SvcThreadSync.cs | 4 +- Ryujinx.Core/OsHle/Utilities/IdPool.cs | 53 ---- Ryujinx.Core/Switch.cs | 19 +- Ryujinx.Graphics/Gal/IGalRenderer.cs | 1 + Ryujinx.Graphics/Gal/OpenGL/FrameBuffer.cs | 34 ++- Ryujinx.Graphics/Gal/OpenGL/OpenGLRenderer.cs | 5 + Ryujinx.Tests/Cpu/CpuTest.cs | 2 +- 46 files changed, 926 insertions(+), 598 deletions(-) delete mode 100644 Ryujinx.Core/OsHle/Handles/HNvMap.cs create mode 100644 Ryujinx.Core/OsHle/Handles/KProcessHandleTable.cs rename Ryujinx.Core/OsHle/{Utilities/IdPoolWithObj.cs => IdDictionary.cs} (50%) create mode 100644 Ryujinx.Core/OsHle/ServiceMgr.cs create mode 100644 Ryujinx.Core/OsHle/Services/Nv/NvFd.cs create mode 100644 Ryujinx.Core/OsHle/Services/Nv/NvMap.cs delete mode 100644 Ryujinx.Core/OsHle/Services/ServiceFactory.cs rename Ryujinx.Core/OsHle/{ => Services/Vi}/Display.cs (79%) delete mode 100644 Ryujinx.Core/OsHle/Utilities/IdPool.cs diff --git a/ChocolArm64/AThread.cs b/ChocolArm64/AThread.cs index cec268175f..62f9d2d396 100644 --- a/ChocolArm64/AThread.cs +++ b/ChocolArm64/AThread.cs @@ -14,43 +14,30 @@ namespace ChocolArm64 private ATranslator Translator; - private ThreadPriority Priority; - private Thread Work; public event EventHandler WorkFinished; public int ThreadId => ThreadState.ThreadId; - public bool IsAlive => Work.IsAlive; + private int IsExecuting; - private bool IsExecuting; - - private object ExecuteLock; - - public AThread(ATranslator Translator, AMemory Memory, ThreadPriority Priority, long EntryPoint) + public AThread(ATranslator Translator, AMemory Memory, long EntryPoint) { this.Translator = Translator; this.Memory = Memory; - this.Priority = Priority; this.EntryPoint = EntryPoint; ThreadState = new AThreadState(); - ExecuteLock = new object(); - } - public void StopExecution() => Translator.StopExecution(); + ThreadState.Running = true; + } public bool Execute() { - lock (ExecuteLock) + if (Interlocked.Exchange(ref IsExecuting, 1) == 1) { - if (IsExecuting) - { - return false; - } - - IsExecuting = true; + return false; } Work = new Thread(delegate() @@ -62,11 +49,11 @@ namespace ChocolArm64 WorkFinished?.Invoke(this, EventArgs.Empty); }); - Work.Priority = Priority; - Work.Start(); return true; } + + public void StopExecution() => ThreadState.Running = false; } } \ No newline at end of file diff --git a/ChocolArm64/ATranslator.cs b/ChocolArm64/ATranslator.cs index 1f4a922ab2..02c18efd2b 100644 --- a/ChocolArm64/ATranslator.cs +++ b/ChocolArm64/ATranslator.cs @@ -22,8 +22,6 @@ namespace ChocolArm64 public bool EnableCpuTrace { get; set; } - private bool KeepRunning; - public ATranslator(IReadOnlyDictionary SymbolTable = null) { SubBlocks = new HashSet(); @@ -38,12 +36,8 @@ namespace ChocolArm64 { this.SymbolTable = new ConcurrentDictionary(); } - - KeepRunning = true; } - internal void StopExecution() => KeepRunning = false; - internal void ExecuteSubroutine(AThread Thread, long Position) { do @@ -70,7 +64,7 @@ namespace ChocolArm64 Position = Sub.Execute(Thread.ThreadState, Thread.Memory); } - while (Position != 0 && KeepRunning); + while (Position != 0 && Thread.ThreadState.Running); } internal bool TryGetCachedSub(AOpCode OpCode, out ATranslatedSub Sub) diff --git a/ChocolArm64/Instruction/AInstEmitException.cs b/ChocolArm64/Instruction/AInstEmitException.cs index fe348edd17..3964c94979 100644 --- a/ChocolArm64/Instruction/AInstEmitException.cs +++ b/ChocolArm64/Instruction/AInstEmitException.cs @@ -34,6 +34,22 @@ namespace ChocolArm64.Instruction Context.EmitCall(MthdInfo); + //Check if the thread should still be running, if it isn't then we return 0 + //to force a return to the dispatcher and then exit the thread. + Context.EmitLdarg(ATranslatedSub.StateArgIdx); + + Context.EmitCallPropGet(typeof(AThreadState), nameof(AThreadState.Running)); + + AILLabel LblEnd = new AILLabel(); + + Context.Emit(OpCodes.Brtrue_S, LblEnd); + + Context.EmitLdc_I8(0); + + Context.Emit(OpCodes.Ret); + + Context.MarkLabel(LblEnd); + if (Context.CurrBlock.Next != null) { Context.EmitLoadState(Context.CurrBlock.Next); diff --git a/ChocolArm64/State/AThreadState.cs b/ChocolArm64/State/AThreadState.cs index d86f5bf9c8..ec8621b89b 100644 --- a/ChocolArm64/State/AThreadState.cs +++ b/ChocolArm64/State/AThreadState.cs @@ -29,6 +29,8 @@ namespace ChocolArm64.State public int ProcessId; public int ThreadId; + public bool Running { get; set; } + public long TpidrEl0 { get; set; } public long Tpidr { get; set; } diff --git a/Ryujinx.Core/Hid/Hid.cs b/Ryujinx.Core/Hid/Hid.cs index c287564d3a..f25a943752 100644 --- a/Ryujinx.Core/Hid/Hid.cs +++ b/Ryujinx.Core/Hid/Hid.cs @@ -63,17 +63,13 @@ namespace Ryujinx.Core.Input private object ShMemLock; - private long[] ShMemPositions; + private (AMemory, long)[] ShMemPositions; - private AMemory Memory; - - public Hid(AMemory Memory) + public Hid() { - this.Memory = Memory; - ShMemLock = new object(); - ShMemPositions = new long[0]; + ShMemPositions = new (AMemory, long)[0]; } internal void ShMemMap(object sender, EventArgs e) @@ -84,11 +80,11 @@ namespace Ryujinx.Core.Input { ShMemPositions = SharedMem.GetVirtualPositions(); - long BasePosition = ShMemPositions[ShMemPositions.Length - 1]; + (AMemory Memory, long Position) ShMem = ShMemPositions[ShMemPositions.Length - 1]; - Logging.Info($"HID shared memory successfully mapped to 0x{BasePosition:x16}!"); + Logging.Info($"HID shared memory successfully mapped to 0x{ShMem.Position:x16}!"); - Init(BasePosition); + Init(ShMem.Memory, ShMem.Position); } } @@ -102,10 +98,11 @@ namespace Ryujinx.Core.Input } } - private void Init(long BasePosition) + private void Init(AMemory Memory, long Position) { InitializeJoyconPair( - BasePosition, + Memory, + Position, JoyConColor.Body_Neon_Red, JoyConColor.Buttons_Neon_Red, JoyConColor.Body_Neon_Blue, @@ -113,13 +110,14 @@ namespace Ryujinx.Core.Input } private void InitializeJoyconPair( - long BasePosition, + AMemory Memory, + long Position, JoyConColor LeftColorBody, JoyConColor LeftColorButtons, JoyConColor RightColorBody, JoyConColor RightColorButtons) { - long BaseControllerOffset = BasePosition + HidControllersOffset + 8 * HidControllerSize; + long BaseControllerOffset = Position + HidControllersOffset + 8 * HidControllerSize; HidControllerType Type = HidControllerType.ControllerType_Handheld | @@ -160,7 +158,7 @@ namespace Ryujinx.Core.Input { lock (ShMemLock) { - foreach (long Position in ShMemPositions) + foreach ((AMemory Memory, long Position) in ShMemPositions) { long ControllerOffset = Position + HidControllersOffset; @@ -207,7 +205,7 @@ namespace Ryujinx.Core.Input { lock (ShMemLock) { - foreach (long Position in ShMemPositions) + foreach ((AMemory Memory, long Position) in ShMemPositions) { long TouchScreenOffset = Position + HidTouchScreenOffset; diff --git a/Ryujinx.Core/Logging.cs b/Ryujinx.Core/Logging.cs index d544a5d64f..89064ccbc8 100644 --- a/Ryujinx.Core/Logging.cs +++ b/Ryujinx.Core/Logging.cs @@ -8,7 +8,8 @@ namespace Ryujinx.Core { public static class Logging { - private static Stopwatch ExecutionTime = new Stopwatch(); + private static Stopwatch ExecutionTime; + private const string LogFileName = "Ryujinx.log"; private static bool EnableInfo = Config.LoggingEnableInfo; @@ -23,6 +24,10 @@ namespace Ryujinx.Core static Logging() { if (File.Exists(LogFileName)) File.Delete(LogFileName); + + ExecutionTime = new Stopwatch(); + + ExecutionTime.Start(); } public static string GetExecutionTime() diff --git a/Ryujinx.Core/OsHle/CondVar.cs b/Ryujinx.Core/OsHle/CondVar.cs index ac3ba57498..f5fe3d292e 100644 --- a/Ryujinx.Core/OsHle/CondVar.cs +++ b/Ryujinx.Core/OsHle/CondVar.cs @@ -4,7 +4,7 @@ using System.Threading; namespace Ryujinx.Core.OsHle { - public class CondVar + class CondVar { private Process Process; diff --git a/Ryujinx.Core/OsHle/Handles/HDomain.cs b/Ryujinx.Core/OsHle/Handles/HDomain.cs index ca287a5fda..ac99b03a6b 100644 --- a/Ryujinx.Core/OsHle/Handles/HDomain.cs +++ b/Ryujinx.Core/OsHle/Handles/HDomain.cs @@ -1,58 +1,48 @@ -using Ryujinx.Core.OsHle.Utilities; using System; -using System.Collections.Generic; namespace Ryujinx.Core.OsHle.Handles { - class HDomain : HSession + class HDomain : HSession, IDisposable { - private Dictionary Objects; - - private IdPool ObjIds; + private IdDictionary Objects; public HDomain(HSession Session) : base(Session) { - Objects = new Dictionary(); - - ObjIds = new IdPool(); + Objects = new IdDictionary(); } - public int GenerateObjectId(object Obj) + public int Add(object Obj) { - int Id = ObjIds.GenerateId(); - - if (Id == -1) - { - throw new InvalidOperationException(); - } - - Objects.Add(Id, Obj); - - return Id; + return Objects.Add(Obj); } - public void DeleteObject(int Id) + public bool Delete(int Id) { - if (Objects.TryGetValue(Id, out object Obj)) - { - if (Obj is IDisposable DisposableObj) - { - DisposableObj.Dispose(); - } - - ObjIds.DeleteId(Id); - Objects.Remove(Id); - } + return Objects.Delete(Id); } public object GetObject(int Id) { - if (Objects.TryGetValue(Id, out object Obj)) - { - return Obj; - } + return Objects.GetData(Id); + } - return null; + public void Dispose() + { + Dispose(true); + } + + protected virtual void Dispose(bool Disposing) + { + if (Disposing) + { + foreach (object Obj in Objects) + { + if (Obj is IDisposable DisposableObj) + { + DisposableObj.Dispose(); + } + } + } } } } \ No newline at end of file diff --git a/Ryujinx.Core/OsHle/Handles/HNvMap.cs b/Ryujinx.Core/OsHle/Handles/HNvMap.cs deleted file mode 100644 index 09173730b3..0000000000 --- a/Ryujinx.Core/OsHle/Handles/HNvMap.cs +++ /dev/null @@ -1,18 +0,0 @@ -namespace Ryujinx.Core.OsHle.Handles -{ - class HNvMap - { - public int Id { get; private set; } - public int Size { get; private set; } - - public int Align { get; set; } - public int Kind { get; set; } - public long Address { get; set; } - - public HNvMap(int Id, int Size) - { - this.Id = Id; - this.Size = Size; - } - } -} \ No newline at end of file diff --git a/Ryujinx.Core/OsHle/Handles/HSharedMem.cs b/Ryujinx.Core/OsHle/Handles/HSharedMem.cs index eb173e9652..b6bdc89878 100644 --- a/Ryujinx.Core/OsHle/Handles/HSharedMem.cs +++ b/Ryujinx.Core/OsHle/Handles/HSharedMem.cs @@ -1,3 +1,4 @@ +using ChocolArm64.Memory; using System; using System.Collections.Generic; @@ -5,37 +6,37 @@ namespace Ryujinx.Core.OsHle.Handles { class HSharedMem { - private List Positions; + private List<(AMemory, long)> Positions; public EventHandler MemoryMapped; public EventHandler MemoryUnmapped; public HSharedMem() { - Positions = new List(); + Positions = new List<(AMemory, long)>(); } - public void AddVirtualPosition(long Position) + public void AddVirtualPosition(AMemory Memory, long Position) { lock (Positions) { - Positions.Add(Position); + Positions.Add((Memory, Position)); MemoryMapped?.Invoke(this, EventArgs.Empty); } } - public void RemoveVirtualPosition(long Position) + public void RemoveVirtualPosition(AMemory Memory, long Position) { lock (Positions) { - Positions.Remove(Position); + Positions.Remove((Memory, Position)); MemoryUnmapped?.Invoke(this, EventArgs.Empty); } } - public long[] GetVirtualPositions() + public (AMemory, long)[] GetVirtualPositions() { return Positions.ToArray(); } diff --git a/Ryujinx.Core/OsHle/Handles/KProcessHandleTable.cs b/Ryujinx.Core/OsHle/Handles/KProcessHandleTable.cs new file mode 100644 index 0000000000..1156e035fd --- /dev/null +++ b/Ryujinx.Core/OsHle/Handles/KProcessHandleTable.cs @@ -0,0 +1,63 @@ +using System; + +namespace Ryujinx.Core.OsHle.Handles +{ + class KProcessHandleTable : IDisposable + { + private IdDictionary Handles; + + public KProcessHandleTable() + { + Handles = new IdDictionary(); + } + + public int OpenHandle(object Obj) + { + return Handles.Add(Obj); + } + + public T GetData(int Handle) + { + return Handles.GetData(Handle); + } + + public bool ReplaceData(int Id, object Data) + { + return Handles.ReplaceData(Id, Data); + } + + public bool CloseHandle(int Handle) + { + object Data = Handles.GetData(Handle); + + if (Data is HTransferMem TMem) + { + TMem.Memory.Manager.Reprotect( + TMem.Position, + TMem.Size, + TMem.Perm); + } + + return Handles.Delete(Handle); + } + + public void Dispose() + { + Dispose(true); + } + + protected virtual void Dispose(bool Disposing) + { + if (Disposing) + { + foreach (object Obj in Handles) + { + if (Obj is IDisposable DisposableObj) + { + DisposableObj.Dispose(); + } + } + } + } + } +} \ No newline at end of file diff --git a/Ryujinx.Core/OsHle/Homebrew.cs b/Ryujinx.Core/OsHle/Homebrew.cs index e2e95e4d41..2a717ca738 100644 --- a/Ryujinx.Core/OsHle/Homebrew.cs +++ b/Ryujinx.Core/OsHle/Homebrew.cs @@ -37,5 +37,33 @@ namespace Ryujinx.Core.OsHle Position += 0x18; } + + public static string ReadHbAbiNextLoadPath(AMemory Memory, long Position) + { + string FileName = null; + + while (true) + { + long Key = Memory.ReadInt64(Position); + + if (Key == 2) + { + long Value0 = Memory.ReadInt64(Position + 0x08); + long Value1 = Memory.ReadInt64(Position + 0x10); + + FileName = AMemoryHelper.ReadAsciiString(Memory, Value0, (int)(Value1 - Value0)); + + break; + } + else if (Key == 0) + { + break; + } + + Position += 0x18; + } + + return FileName; + } } } diff --git a/Ryujinx.Core/OsHle/Horizon.cs b/Ryujinx.Core/OsHle/Horizon.cs index c61e18e98f..c3f8cd8b00 100644 --- a/Ryujinx.Core/OsHle/Horizon.cs +++ b/Ryujinx.Core/OsHle/Horizon.cs @@ -1,33 +1,23 @@ using Ryujinx.Core.Loaders.Executables; using Ryujinx.Core.OsHle.Handles; -using Ryujinx.Core.OsHle.Utilities; using System; using System.Collections.Concurrent; using System.IO; namespace Ryujinx.Core.OsHle { - public class Horizon + public class Horizon : IDisposable { internal const int HidSize = 0x40000; internal const int FontSize = 0x50; - internal int HidHandle { get; private set; } - internal int FontHandle { get; private set; } - - internal IdPool IdGen { get; private set; } - internal IdPool NvMapIds { get; private set; } - - internal IdPoolWithObj Handles { get; private set; } - internal IdPoolWithObj Fds { get; private set; } - internal IdPoolWithObj Displays { get; private set; } - - public ConcurrentDictionary Mutexes { get; private set; } - public ConcurrentDictionary CondVars { get; private set; } + internal ConcurrentDictionary Mutexes { get; private set; } + internal ConcurrentDictionary CondVars { get; private set; } private ConcurrentDictionary Processes; internal HSharedMem HidSharedMem; + internal HSharedMem FontSharedMem; private Switch Ns; @@ -35,25 +25,13 @@ namespace Ryujinx.Core.OsHle { this.Ns = Ns; - IdGen = new IdPool(); - NvMapIds = new IdPool(); - - Handles = new IdPoolWithObj(); - Fds = new IdPoolWithObj(); - Displays = new IdPoolWithObj(); - Mutexes = new ConcurrentDictionary(); CondVars = new ConcurrentDictionary(); Processes = new ConcurrentDictionary(); - HidSharedMem = new HSharedMem(); - - HidHandle = Handles.GenerateId(HidSharedMem); - - FontHandle = Handles.GenerateId(new HSharedMem()); - - HidSharedMem.AddVirtualPosition(0); + HidSharedMem = new HSharedMem(); + FontSharedMem = new HSharedMem(); } public void LoadCart(string ExeFsDir, string RomFsFile = null) @@ -63,9 +41,7 @@ namespace Ryujinx.Core.OsHle Ns.VFs.LoadRomFs(RomFsFile); } - int ProcessId = IdGen.GenerateId(); - - Process MainProcess = new Process(Ns, ProcessId); + Process MainProcess = MakeProcess(); void LoadNso(string FileName) { @@ -96,17 +72,13 @@ namespace Ryujinx.Core.OsHle LoadNso("sdk"); MainProcess.Run(); - - Processes.TryAdd(ProcessId, MainProcess); } public void LoadProgram(string FileName) { bool IsNro = Path.GetExtension(FileName).ToLower() == ".nro"; - int ProcessId = IdGen.GenerateId(); - - Process MainProcess = new Process(Ns, ProcessId); + Process MainProcess = MakeProcess(); using (FileStream Input = new FileStream(FileName, FileMode.Open)) { @@ -117,34 +89,67 @@ namespace Ryujinx.Core.OsHle MainProcess.SetEmptyArgs(); MainProcess.Run(IsNro); - - Processes.TryAdd(ProcessId, MainProcess); } - public void FinalizeAllProcesses() + private Process MakeProcess() { - foreach (Process Process in Processes.Values) + Process Process; + + lock (Processes) { - Process.StopAllThreads(); + int ProcessId = 0; + + while (Processes.ContainsKey(ProcessId)) + { + ProcessId++; + } + + Process = new Process(Ns, ProcessId); + + Processes.TryAdd(ProcessId, Process); + } + + return Process; + } + + internal void ExitProcess(int ProcessId) + { + if (Processes.TryGetValue(ProcessId, out Process Process) && Process.NeedsHbAbi) + { + string NextNro = Homebrew.ReadHbAbiNextLoadPath(Process.Memory, Process.HbAbiDataPosition); + + Logging.Info($"HbAbi NextLoadPath {NextNro}"); + + if (NextNro == string.Empty) + { + NextNro = "sdmc:/hbmenu.nro"; + } + + NextNro = NextNro.Replace("sdmc:", string.Empty); + + NextNro = Ns.VFs.GetFullPath(Ns.VFs.GetSdCardPath(), NextNro); + + if (File.Exists(NextNro)) + { + //TODO: Those dictionaries shouldn't even exist, + //the Mutex and CondVar helper classes should be static. + Mutexes.Clear(); + CondVars.Clear(); + + LoadProgram(NextNro); + } + } + + if (Processes.TryRemove(ProcessId, out Process)) + { + Process.StopAllThreadsAsync(); Process.Dispose(); - } - } - internal bool ExitProcess(int ProcessId) - { - bool Success = Processes.TryRemove(ProcessId, out Process Process); - - if (Success) - { - Process.StopAllThreads(); + if (Processes.Count == 0) + { + Ns.OnFinish(EventArgs.Empty); + } } - - if (Processes.Count == 0) - { - Ns.OnFinish(EventArgs.Empty); - } - - return Success; } internal bool TryGetProcess(int ProcessId, out Process Process) @@ -152,19 +157,21 @@ namespace Ryujinx.Core.OsHle return Processes.TryGetValue(ProcessId, out Process); } - internal void CloseHandle(int Handle) + public void Dispose() { - object HndData = Handles.GetData(Handle); + Dispose(true); + } - if (HndData is HTransferMem TransferMem) + protected virtual void Dispose(bool Disposing) + { + if (Disposing) { - TransferMem.Memory.Manager.Reprotect( - TransferMem.Position, - TransferMem.Size, - TransferMem.Perm); + foreach (Process Process in Processes.Values) + { + Process.StopAllThreadsAsync(); + Process.Dispose(); + } } - - Handles.Delete(Handle); } } } \ No newline at end of file diff --git a/Ryujinx.Core/OsHle/Utilities/IdPoolWithObj.cs b/Ryujinx.Core/OsHle/IdDictionary.cs similarity index 50% rename from Ryujinx.Core/OsHle/Utilities/IdPoolWithObj.cs rename to Ryujinx.Core/OsHle/IdDictionary.cs index f0a339df3a..0b90924616 100644 --- a/Ryujinx.Core/OsHle/Utilities/IdPoolWithObj.cs +++ b/Ryujinx.Core/OsHle/IdDictionary.cs @@ -3,31 +3,40 @@ using System.Collections; using System.Collections.Concurrent; using System.Collections.Generic; -namespace Ryujinx.Core.OsHle.Utilities +namespace Ryujinx.Core.OsHle { - class IdPoolWithObj : IEnumerable> + class IdDictionary : IEnumerable { - private IdPool Ids; - private ConcurrentDictionary Objs; - public IdPoolWithObj() - { - Ids = new IdPool(); + private int FreeIdHint = 1; + public IdDictionary() + { Objs = new ConcurrentDictionary(); } - public int GenerateId(object Data) + public int Add(object Data) { - int Id = Ids.GenerateId(); - - if (Id == -1 || !Objs.TryAdd(Id, Data)) + if (Objs.TryAdd(FreeIdHint, Data)) { - throw new InvalidOperationException(); + return FreeIdHint++; } - return Id; + return AddSlow(Data); + } + + private int AddSlow(object Data) + { + for (int Id = 1; Id < int.MaxValue; Id++) + { + if (Objs.TryAdd(Id, Data)) + { + return Id; + } + } + + throw new InvalidOperationException(); } public bool ReplaceData(int Id, object Data) @@ -42,6 +51,16 @@ namespace Ryujinx.Core.OsHle.Utilities return false; } + public object GetData(int Id) + { + if (Objs.TryGetValue(Id, out object Data)) + { + return Data; + } + + return null; + } + public T GetData(int Id) { if (Objs.TryGetValue(Id, out object Data) && Data is T) @@ -52,7 +71,7 @@ namespace Ryujinx.Core.OsHle.Utilities return default(T); } - public void Delete(int Id) + public bool Delete(int Id) { if (Objs.TryRemove(Id, out object Obj)) { @@ -61,18 +80,22 @@ namespace Ryujinx.Core.OsHle.Utilities DisposableObj.Dispose(); } - Ids.DeleteId(Id); + FreeIdHint = Id; + + return true; } + + return false; } - IEnumerator> IEnumerable>.GetEnumerator() + IEnumerator IEnumerable.GetEnumerator() { - return Objs.GetEnumerator(); + return Objs.Values.GetEnumerator(); } IEnumerator IEnumerable.GetEnumerator() { - return Objs.GetEnumerator(); + return Objs.Values.GetEnumerator(); } } } \ No newline at end of file diff --git a/Ryujinx.Core/OsHle/Ipc/IpcHandler.cs b/Ryujinx.Core/OsHle/Ipc/IpcHandler.cs index 2495d7014c..f2179a962c 100644 --- a/Ryujinx.Core/OsHle/Ipc/IpcHandler.cs +++ b/Ryujinx.Core/OsHle/Ipc/IpcHandler.cs @@ -13,6 +13,7 @@ namespace Ryujinx.Core.OsHle.Ipc public static void IpcCall( Switch Ns, + Process Process, AMemory Memory, HSession Session, IpcMessage Request, @@ -60,7 +61,7 @@ namespace Ryujinx.Core.OsHle.Ipc } else if (Request.DomCmd == IpcDomCmd.DeleteObj) { - Dom.DeleteObject(Request.DomObjId); + Dom.Delete(Request.DomObjId); Response = FillResponse(Response, 0); @@ -100,6 +101,7 @@ namespace Ryujinx.Core.OsHle.Ipc ServiceCtx Context = new ServiceCtx( Ns, + Process, Memory, Session, Request, @@ -124,15 +126,43 @@ namespace Ryujinx.Core.OsHle.Ipc switch (CmdId) { - case 0: Request = IpcConvertSessionToDomain(Ns, Session, Response, HndId); break; - case 3: Request = IpcQueryBufferPointerSize(Response); break; - case 2: //IpcDuplicateSession, differences is unknown. - case 4: Request = IpcDuplicateSessionEx(Ns, Session, Response, ReqReader); break; + case 0: + { + HDomain Dom = new HDomain(Session); + + Process.HandleTable.ReplaceData(HndId, Dom); + + Request = FillResponse(Response, 0, Dom.Add(Dom)); + + break; + } + + case 3: + { + Request = FillResponse(Response, 0, 0x500); + + break; + } + + //TODO: Whats the difference between IpcDuplicateSession/Ex? + case 2: + case 4: + { + int Unknown = ReqReader.ReadInt32(); + + int Handle = Process.HandleTable.OpenHandle(Session); + + Response.HandleDesc = IpcHandleDesc.MakeMove(Handle); + + Request = FillResponse(Response, 0); + + break; + } default: throw new NotImplementedException(CmdId.ToString()); } } - else if (Request.Type == IpcMessageType.Unknown2) + else if (Request.Type == IpcMessageType.CloseSession) { //TODO } @@ -145,39 +175,6 @@ namespace Ryujinx.Core.OsHle.Ipc } } - private static IpcMessage IpcConvertSessionToDomain( - Switch Ns, - HSession Session, - IpcMessage Response, - int HndId) - { - HDomain Dom = new HDomain(Session); - - Ns.Os.Handles.ReplaceData(HndId, Dom); - - return FillResponse(Response, 0, Dom.GenerateObjectId(Dom)); - } - - private static IpcMessage IpcDuplicateSessionEx( - Switch Ns, - HSession Session, - IpcMessage Response, - BinaryReader ReqReader) - { - int Unknown = ReqReader.ReadInt32(); - - int Handle = Ns.Os.Handles.GenerateId(Session); - - Response.HandleDesc = IpcHandleDesc.MakeMove(Handle); - - return FillResponse(Response, 0); - } - - private static IpcMessage IpcQueryBufferPointerSize(IpcMessage Response) - { - return FillResponse(Response, 0, 0x500); - } - private static IpcMessage FillResponse(IpcMessage Response, long Result, params int[] Values) { using (MemoryStream MS = new MemoryStream()) diff --git a/Ryujinx.Core/OsHle/Ipc/IpcMessageType.cs b/Ryujinx.Core/OsHle/Ipc/IpcMessageType.cs index 8027508de2..560af41e74 100644 --- a/Ryujinx.Core/OsHle/Ipc/IpcMessageType.cs +++ b/Ryujinx.Core/OsHle/Ipc/IpcMessageType.cs @@ -2,9 +2,9 @@ namespace Ryujinx.Core.OsHle.Ipc { enum IpcMessageType { - Response = 0, - Unknown2 = 2, - Request = 4, - Control = 5 + Response = 0, + CloseSession = 2, + Request = 4, + Control = 5 } } \ No newline at end of file diff --git a/Ryujinx.Core/OsHle/Mutex.cs b/Ryujinx.Core/OsHle/Mutex.cs index c95ed77164..c619e12174 100644 --- a/Ryujinx.Core/OsHle/Mutex.cs +++ b/Ryujinx.Core/OsHle/Mutex.cs @@ -4,7 +4,7 @@ using System.Threading; namespace Ryujinx.Core.OsHle { - public class Mutex + class Mutex { private const int MutexHasListenersMask = 0x40000000; diff --git a/Ryujinx.Core/OsHle/Process.cs b/Ryujinx.Core/OsHle/Process.cs index a919f1af6d..a8719e1c29 100644 --- a/Ryujinx.Core/OsHle/Process.cs +++ b/Ryujinx.Core/OsHle/Process.cs @@ -9,25 +9,32 @@ using Ryujinx.Core.OsHle.Svc; using System; using System.Collections.Concurrent; using System.Collections.Generic; -using System.Threading; namespace Ryujinx.Core.OsHle { - public class Process : IDisposable + class Process : IDisposable { - private const int TlsSize = 0x200; - private const int TotalTlsSlots = 32; + private const int TlsSize = 0x200; + private const int TotalTlsSlots = 32; private Switch Ns; + public bool NeedsHbAbi { get; private set; } + + public long HbAbiDataPosition { get; private set; } + public int ProcessId { get; private set; } private ATranslator Translator; public AMemory Memory { get; private set; } + public ServiceMgr Services { get; private set; } + public KProcessScheduler Scheduler { get; private set; } + public KProcessHandleTable HandleTable { get; private set; } + private SvcHandler SvcHandler; private ConcurrentDictionary TlsSlots; @@ -40,14 +47,22 @@ namespace Ryujinx.Core.OsHle private long ImageBase; + private bool ShouldDispose; + + private bool Disposed; + public Process(Switch Ns, int ProcessId) { this.Ns = Ns; this.ProcessId = ProcessId; - Memory = Ns.Memory; + Memory = new AMemory(); - Scheduler = new KProcessScheduler(); + Services = new ServiceMgr(); + + HandleTable = new KProcessHandleTable(); + + Scheduler = new KProcessScheduler(); SvcHandler = new SvcHandler(Ns, this); @@ -67,6 +82,11 @@ namespace Ryujinx.Core.OsHle public void LoadProgram(IExecutable Program) { + if (Disposed) + { + throw new ObjectDisposedException(nameof(Process)); + } + Logging.Info($"Image base at 0x{ImageBase:x16}."); Executable Executable = new Executable(Program, Memory, ImageBase); @@ -78,11 +98,19 @@ namespace Ryujinx.Core.OsHle public void SetEmptyArgs() { + //TODO: This should be part of Run. ImageBase += AMemoryMgr.PageSize; } - public bool Run(bool UseHbAbi = false) + public bool Run(bool NeedsHbAbi = false) { + if (Disposed) + { + throw new ObjectDisposedException(nameof(Process)); + } + + this.NeedsHbAbi = NeedsHbAbi; + if (Executables.Count == 0) { return false; @@ -102,11 +130,11 @@ namespace Ryujinx.Core.OsHle return false; } - MainThread = Ns.Os.Handles.GetData(Handle); + MainThread = HandleTable.GetData(Handle); - if (UseHbAbi) + if (NeedsHbAbi) { - long HbAbiDataPosition = AMemoryHelper.PageRoundUp(Executables[0].ImageEnd); + HbAbiDataPosition = AMemoryHelper.PageRoundUp(Executables[0].ImageEnd); Homebrew.WriteHbAbiData(Memory, HbAbiDataPosition, Handle); @@ -124,22 +152,21 @@ namespace Ryujinx.Core.OsHle Memory.Manager.Map(Position, Size, (int)Type, AMemoryPerm.RW); } - public void StopAllThreads() + public void StopAllThreadsAsync() { + if (Disposed) + { + throw new ObjectDisposedException(nameof(Process)); + } + if (MainThread != null) { - while (MainThread.Thread.IsAlive) - { - MainThread.Thread.StopExecution(); - } + MainThread.Thread.StopExecution(); } foreach (AThread Thread in TlsSlots.Values) { - while (Thread.IsAlive) - { - Thread.StopExecution(); - } + Thread.StopExecution(); } } @@ -150,49 +177,26 @@ namespace Ryujinx.Core.OsHle int Priority, int ProcessorId) { - ThreadPriority ThreadPrio; - - if (Priority < 12) + if (Disposed) { - ThreadPrio = ThreadPriority.Highest; - } - else if (Priority < 24) - { - ThreadPrio = ThreadPriority.AboveNormal; - } - else if (Priority < 36) - { - ThreadPrio = ThreadPriority.Normal; - } - else if (Priority < 48) - { - ThreadPrio = ThreadPriority.BelowNormal; - } - else - { - ThreadPrio = ThreadPriority.Lowest; + throw new ObjectDisposedException(nameof(Process)); } - AThread Thread = new AThread(GetTranslator(), Memory, ThreadPrio, EntryPoint); + AThread Thread = new AThread(GetTranslator(), Memory, EntryPoint); HThread ThreadHnd = new HThread(Thread, ProcessorId, Priority); - int Handle = Ns.Os.Handles.GenerateId(ThreadHnd); + int Handle = HandleTable.OpenHandle(ThreadHnd); - int TlsSlot = GetFreeTlsSlot(Thread); + int ThreadId = GetFreeTlsSlot(Thread); - if (TlsSlot == -1 || Handle == -1) - { - return -1; - } - - long Tpidr = MemoryRegions.TlsPagesAddress + TlsSlot * TlsSize; + long Tpidr = MemoryRegions.TlsPagesAddress + ThreadId * TlsSize; Thread.ThreadState.Break += BreakHandler; Thread.ThreadState.SvcCall += SvcHandler.SvcCall; Thread.ThreadState.Undefined += UndefinedHandler; Thread.ThreadState.ProcessId = ProcessId; - Thread.ThreadState.ThreadId = Ns.Os.IdGen.GenerateId(); + Thread.ThreadState.ThreadId = ThreadId; Thread.ThreadState.Tpidr = Tpidr; Thread.ThreadState.X0 = (ulong)ArgsPtr; Thread.ThreadState.X1 = (ulong)Handle; @@ -224,7 +228,7 @@ namespace Ryujinx.Core.OsHle foreach (Executable Exe in Executables) { foreach (KeyValuePair KV in Exe.SymbolTable) - { + { SymbolTable.TryAdd(Exe.ImageBase + KV.Key, KV.Value); } } @@ -274,16 +278,28 @@ namespace Ryujinx.Core.OsHle } } - return -1; + throw new InvalidOperationException(); } private void ThreadFinished(object sender, EventArgs e) { if (sender is AThread Thread) { - TlsSlots.TryRemove(GetTlsSlot(Thread.ThreadState.Tpidr), out _); + Logging.Info($"Thread {Thread.ThreadId} exiting..."); - Ns.Os.IdGen.DeleteId(Thread.ThreadId); + TlsSlots.TryRemove(GetTlsSlot(Thread.ThreadState.Tpidr), out _); + } + + if (TlsSlots.Count == 0) + { + if (ShouldDispose) + { + Dispose(); + } + + Logging.Info($"No threads running, now exiting Process {ProcessId}..."); + + Ns.Os.ExitProcess(ProcessId); } } @@ -309,9 +325,30 @@ namespace Ryujinx.Core.OsHle protected virtual void Dispose(bool Disposing) { - if (Disposing) + if (Disposing && !Disposed) { + //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. + //Note: This may not happen if the guest code gets stuck on a infinite loop. + if (TlsSlots.Count > 0) + { + ShouldDispose = true; + + Logging.Info($"Process {ProcessId} waiting all threads terminate..."); + + return; + } + + Disposed = true; + + Services.Dispose(); + HandleTable.Dispose(); Scheduler.Dispose(); + SvcHandler.Dispose(); + Memory.Dispose(); + + Logging.Info($"Process {ProcessId} exiting..."); } } } diff --git a/Ryujinx.Core/OsHle/ServiceCtx.cs b/Ryujinx.Core/OsHle/ServiceCtx.cs index 31ecce3dec..60c378d5a7 100644 --- a/Ryujinx.Core/OsHle/ServiceCtx.cs +++ b/Ryujinx.Core/OsHle/ServiceCtx.cs @@ -8,6 +8,7 @@ namespace Ryujinx.Core.OsHle class ServiceCtx { public Switch Ns { get; private set; } + public Process Process { get; private set; } public AMemory Memory { get; private set; } public HSession Session { get; private set; } public IpcMessage Request { get; private set; } @@ -17,6 +18,7 @@ namespace Ryujinx.Core.OsHle public ServiceCtx( Switch Ns, + Process Process, AMemory Memory, HSession Session, IpcMessage Request, @@ -25,6 +27,7 @@ namespace Ryujinx.Core.OsHle BinaryWriter ResponseData) { this.Ns = Ns; + this.Process = Process; this.Memory = Memory; this.Session = Session; this.Request = Request; diff --git a/Ryujinx.Core/OsHle/ServiceMgr.cs b/Ryujinx.Core/OsHle/ServiceMgr.cs new file mode 100644 index 0000000000..cbf6386f9d --- /dev/null +++ b/Ryujinx.Core/OsHle/ServiceMgr.cs @@ -0,0 +1,109 @@ +using Ryujinx.Core.OsHle.IpcServices; +using Ryujinx.Core.OsHle.IpcServices.Acc; +using Ryujinx.Core.OsHle.IpcServices.Am; +using Ryujinx.Core.OsHle.IpcServices.Apm; +using Ryujinx.Core.OsHle.IpcServices.Aud; +using Ryujinx.Core.OsHle.IpcServices.Bsd; +using Ryujinx.Core.OsHle.IpcServices.Friend; +using Ryujinx.Core.OsHle.IpcServices.FspSrv; +using Ryujinx.Core.OsHle.IpcServices.Hid; +using Ryujinx.Core.OsHle.IpcServices.Lm; +using Ryujinx.Core.OsHle.IpcServices.Nifm; +using Ryujinx.Core.OsHle.IpcServices.Ns; +using Ryujinx.Core.OsHle.IpcServices.NvServices; +using Ryujinx.Core.OsHle.IpcServices.Pctl; +using Ryujinx.Core.OsHle.IpcServices.Pl; +using Ryujinx.Core.OsHle.IpcServices.Set; +using Ryujinx.Core.OsHle.IpcServices.Sfdnsres; +using Ryujinx.Core.OsHle.IpcServices.Sm; +using Ryujinx.Core.OsHle.IpcServices.Ssl; +using Ryujinx.Core.OsHle.IpcServices.Time; +using Ryujinx.Core.OsHle.IpcServices.Vi; +using System; +using System.Collections.Generic; + +namespace Ryujinx.Core.OsHle +{ + class ServiceMgr : IDisposable + { + private Dictionary Services; + + public ServiceMgr() + { + Services = new Dictionary(); + } + + public IIpcService GetService(string Name) + { + lock (Services) + { + if (!Services.TryGetValue(Name, out IIpcService Service)) + { + switch (Name) + { + case "acc:u0": Service = new ServiceAcc(); break; + case "aoc:u": Service = new ServiceNs(); break; + case "apm": Service = new ServiceApm(); break; + case "apm:p": Service = new ServiceApm(); break; + case "appletOE": Service = new ServiceAppletOE(); break; + case "audout:u": Service = new ServiceAudOut(); break; + case "audren:u": Service = new ServiceAudRen(); break; + case "bsd:u": Service = new ServiceBsd(); break; + case "friend:a": Service = new ServiceFriend(); break; + case "fsp-srv": Service = new ServiceFspSrv(); break; + case "hid": Service = new ServiceHid(); break; + case "lm": Service = new ServiceLm(); break; + case "nifm:u": Service = new ServiceNifm(); break; + case "nvdrv": Service = new ServiceNvDrv(); break; + case "nvdrv:a": Service = new ServiceNvDrv(); break; + case "pctl:a": Service = new ServicePctl(); break; + case "pl:u": Service = new ServicePl(); break; + case "set": Service = new ServiceSet(); break; + case "set:sys": Service = new ServiceSetSys(); break; + case "sfdnsres": Service = new ServiceSfdnsres(); break; + case "sm:": Service = new ServiceSm(); break; + case "ssl": Service = new ServiceSsl(); break; + case "time:s": Service = new ServiceTime(); break; + case "time:u": Service = new ServiceTime(); break; + case "vi:m": Service = new ServiceVi(); break; + case "vi:s": Service = new ServiceVi(); break; + case "vi:u": Service = new ServiceVi(); break; + } + + if (Service == null) + { + throw new NotImplementedException(Name); + } + + Services.Add(Name, Service); + } + + return Service; + } + } + + public void Dispose() + { + Dispose(true); + } + + protected virtual void Dispose(bool Disposing) + { + if (Disposing) + { + lock (Services) + { + foreach (IIpcService Service in Services.Values) + { + if (Service is IDisposable DisposableSrv) + { + DisposableSrv.Dispose(); + } + } + + Services.Clear(); + } + } + } + } +} \ No newline at end of file diff --git a/Ryujinx.Core/OsHle/Services/Aud/IAudioOut.cs b/Ryujinx.Core/OsHle/Services/Aud/IAudioOut.cs index c58d9e804c..2312920f6c 100644 --- a/Ryujinx.Core/OsHle/Services/Aud/IAudioOut.cs +++ b/Ryujinx.Core/OsHle/Services/Aud/IAudioOut.cs @@ -139,7 +139,7 @@ namespace Ryujinx.Core.OsHle.IpcServices.Aud public long RegisterBufferEvent(ServiceCtx Context) { - int Handle = Context.Ns.Os.Handles.GenerateId(new HEvent()); + int Handle = Context.Process.HandleTable.OpenHandle(new HEvent()); Context.Response.HandleDesc = IpcHandleDesc.MakeCopy(Handle); diff --git a/Ryujinx.Core/OsHle/Services/Aud/IAudioRenderer.cs b/Ryujinx.Core/OsHle/Services/Aud/IAudioRenderer.cs index bfde0b65b1..4d29371fbd 100644 --- a/Ryujinx.Core/OsHle/Services/Aud/IAudioRenderer.cs +++ b/Ryujinx.Core/OsHle/Services/Aud/IAudioRenderer.cs @@ -56,7 +56,7 @@ namespace Ryujinx.Core.OsHle.IpcServices.Aud public long QuerySystemEvent(ServiceCtx Context) { - int Handle = Context.Ns.Os.Handles.GenerateId(new HEvent()); + int Handle = Context.Process.HandleTable.OpenHandle(new HEvent()); Context.Response.HandleDesc = IpcHandleDesc.MakeCopy(Handle); diff --git a/Ryujinx.Core/OsHle/Services/Hid/IAppletResource.cs b/Ryujinx.Core/OsHle/Services/Hid/IAppletResource.cs index d22e0ff23b..ef437c022f 100644 --- a/Ryujinx.Core/OsHle/Services/Hid/IAppletResource.cs +++ b/Ryujinx.Core/OsHle/Services/Hid/IAppletResource.cs @@ -10,21 +10,23 @@ namespace Ryujinx.Core.OsHle.IpcServices.Hid public IReadOnlyDictionary Commands => m_Commands; - private HSharedMem Handle; + private HSharedMem HidSharedMem; - public IAppletResource(HSharedMem Handle) + public IAppletResource(HSharedMem HidSharedMem) { m_Commands = new Dictionary() { { 0, GetSharedMemoryHandle } }; - this.Handle = Handle; + this.HidSharedMem = HidSharedMem; } - public static long GetSharedMemoryHandle(ServiceCtx Context) + public long GetSharedMemoryHandle(ServiceCtx Context) { - Context.Response.HandleDesc = IpcHandleDesc.MakeCopy(Context.Ns.Os.HidHandle); + int Handle = Context.Process.HandleTable.OpenHandle(HidSharedMem); + + Context.Response.HandleDesc = IpcHandleDesc.MakeCopy(Handle); return 0; } diff --git a/Ryujinx.Core/OsHle/Services/Hid/ServiceHid.cs b/Ryujinx.Core/OsHle/Services/Hid/ServiceHid.cs index cc30a771e0..b1f930725f 100644 --- a/Ryujinx.Core/OsHle/Services/Hid/ServiceHid.cs +++ b/Ryujinx.Core/OsHle/Services/Hid/ServiceHid.cs @@ -1,4 +1,3 @@ -using Ryujinx.Core.OsHle.Handles; using Ryujinx.Core.OsHle.Ipc; using System.Collections.Generic; @@ -32,9 +31,7 @@ namespace Ryujinx.Core.OsHle.IpcServices.Hid public long CreateAppletResource(ServiceCtx Context) { - HSharedMem HidHndData = Context.Ns.Os.Handles.GetData(Context.Ns.Os.HidHandle); - - MakeObject(Context, new IAppletResource(HidHndData)); + MakeObject(Context, new IAppletResource(Context.Ns.Os.HidSharedMem)); return 0; } diff --git a/Ryujinx.Core/OsHle/Services/Nv/NvFd.cs b/Ryujinx.Core/OsHle/Services/Nv/NvFd.cs new file mode 100644 index 0000000000..dbce74adfc --- /dev/null +++ b/Ryujinx.Core/OsHle/Services/Nv/NvFd.cs @@ -0,0 +1,12 @@ +namespace Ryujinx.Core.OsHle.IpcServices.NvServices +{ + class NvFd + { + public string Name { get; private set; } + + public NvFd(string Name) + { + this.Name = Name; + } + } +} \ No newline at end of file diff --git a/Ryujinx.Core/OsHle/Services/Nv/NvMap.cs b/Ryujinx.Core/OsHle/Services/Nv/NvMap.cs new file mode 100644 index 0000000000..ca844f9f28 --- /dev/null +++ b/Ryujinx.Core/OsHle/Services/Nv/NvMap.cs @@ -0,0 +1,12 @@ +namespace Ryujinx.Core.OsHle.IpcServices.NvServices +{ + class NvMap + { + public int Handle; + public int Id; + public int Size; + public int Align; + public int Kind; + public long Address; + } +} \ No newline at end of file diff --git a/Ryujinx.Core/OsHle/Services/Nv/ServiceNvDrv.cs b/Ryujinx.Core/OsHle/Services/Nv/ServiceNvDrv.cs index 3c0c46fec9..67ad44919a 100644 --- a/Ryujinx.Core/OsHle/Services/Nv/ServiceNvDrv.cs +++ b/Ryujinx.Core/OsHle/Services/Nv/ServiceNvDrv.cs @@ -1,5 +1,4 @@ using ChocolArm64.Memory; -using Ryujinx.Core.OsHle.Handles; using Ryujinx.Core.OsHle.Ipc; using Ryujinx.Core.OsHle.Utilities; using Ryujinx.Graphics.Gpu; @@ -12,41 +11,17 @@ namespace Ryujinx.Core.OsHle.IpcServices.NvServices { private delegate long ServiceProcessIoctl(ServiceCtx Context); - private static Dictionary<(string, int), ServiceProcessIoctl> IoctlCmds = - new Dictionary<(string, int), ServiceProcessIoctl>() - { - { ("/dev/nvhost-as-gpu", 0x4101), NvGpuAsIoctlBindChannel }, - { ("/dev/nvhost-as-gpu", 0x4102), NvGpuAsIoctlAllocSpace }, - { ("/dev/nvhost-as-gpu", 0x4106), NvGpuAsIoctlMapBufferEx }, - { ("/dev/nvhost-as-gpu", 0x4108), NvGpuAsIoctlGetVaRegions }, - { ("/dev/nvhost-as-gpu", 0x4109), NvGpuAsIoctlInitializeEx }, - { ("/dev/nvhost-ctrl", 0x001b), NvHostIoctlCtrlGetConfig }, - { ("/dev/nvhost-ctrl", 0x001d), NvHostIoctlCtrlEventWait }, - { ("/dev/nvhost-ctrl-gpu", 0x4701), NvGpuIoctlZcullGetCtxSize }, - { ("/dev/nvhost-ctrl-gpu", 0x4702), NvGpuIoctlZcullGetInfo }, - { ("/dev/nvhost-ctrl-gpu", 0x4705), NvGpuIoctlGetCharacteristics }, - { ("/dev/nvhost-ctrl-gpu", 0x4706), NvGpuIoctlGetTpcMasks }, - { ("/dev/nvhost-ctrl-gpu", 0x4714), NvGpuIoctlZbcGetActiveSlotMask }, - { ("/dev/nvhost-gpu", 0x4714), NvMapIoctlChannelSetUserData }, - { ("/dev/nvhost-gpu", 0x4801), NvMapIoctlChannelSetNvMap }, - { ("/dev/nvhost-gpu", 0x4808), NvMapIoctlChannelSubmitGpFifo }, - { ("/dev/nvhost-gpu", 0x4809), NvMapIoctlChannelAllocObjCtx }, - { ("/dev/nvhost-gpu", 0x480b), NvMapIoctlChannelZcullBind }, - { ("/dev/nvhost-gpu", 0x480c), NvMapIoctlChannelSetErrorNotifier }, - { ("/dev/nvhost-gpu", 0x480d), NvMapIoctlChannelSetPriority }, - { ("/dev/nvhost-gpu", 0x481a), NvMapIoctlChannelAllocGpFifoEx2 }, - { ("/dev/nvmap", 0x0101), NvMapIocCreate }, - { ("/dev/nvmap", 0x0103), NvMapIocFromId }, - { ("/dev/nvmap", 0x0104), NvMapIocAlloc }, - { ("/dev/nvmap", 0x0105), NvMapIocFree }, - { ("/dev/nvmap", 0x0109), NvMapIocParam }, - { ("/dev/nvmap", 0x010e), NvMapIocGetId }, - }; - private Dictionary m_Commands; public IReadOnlyDictionary Commands => m_Commands; + private Dictionary<(string, int), ServiceProcessIoctl> IoctlCmds; + + private IdDictionary Fds; + + private IdDictionary NvMaps; + private IdDictionary NvMapsById; + public ServiceNvDrv() { m_Commands = new Dictionary() @@ -58,15 +33,50 @@ namespace Ryujinx.Core.OsHle.IpcServices.NvServices { 4, QueryEvent }, { 8, SetClientPid }, }; + + IoctlCmds = new Dictionary<(string, int), ServiceProcessIoctl>() + { + { ("/dev/nvhost-as-gpu", 0x4101), NvGpuAsIoctlBindChannel }, + { ("/dev/nvhost-as-gpu", 0x4102), NvGpuAsIoctlAllocSpace }, + { ("/dev/nvhost-as-gpu", 0x4106), NvGpuAsIoctlMapBufferEx }, + { ("/dev/nvhost-as-gpu", 0x4108), NvGpuAsIoctlGetVaRegions }, + { ("/dev/nvhost-as-gpu", 0x4109), NvGpuAsIoctlInitializeEx }, + { ("/dev/nvhost-ctrl", 0x001b), NvHostIoctlCtrlGetConfig }, + { ("/dev/nvhost-ctrl", 0x001d), NvHostIoctlCtrlEventWait }, + { ("/dev/nvhost-ctrl-gpu", 0x4701), NvGpuIoctlZcullGetCtxSize }, + { ("/dev/nvhost-ctrl-gpu", 0x4702), NvGpuIoctlZcullGetInfo }, + { ("/dev/nvhost-ctrl-gpu", 0x4705), NvGpuIoctlGetCharacteristics }, + { ("/dev/nvhost-ctrl-gpu", 0x4706), NvGpuIoctlGetTpcMasks }, + { ("/dev/nvhost-ctrl-gpu", 0x4714), NvGpuIoctlZbcGetActiveSlotMask }, + { ("/dev/nvhost-gpu", 0x4714), NvMapIoctlChannelSetUserData }, + { ("/dev/nvhost-gpu", 0x4801), NvMapIoctlChannelSetNvMap }, + { ("/dev/nvhost-gpu", 0x4808), NvMapIoctlChannelSubmitGpFifo }, + { ("/dev/nvhost-gpu", 0x4809), NvMapIoctlChannelAllocObjCtx }, + { ("/dev/nvhost-gpu", 0x480b), NvMapIoctlChannelZcullBind }, + { ("/dev/nvhost-gpu", 0x480c), NvMapIoctlChannelSetErrorNotifier }, + { ("/dev/nvhost-gpu", 0x480d), NvMapIoctlChannelSetPriority }, + { ("/dev/nvhost-gpu", 0x481a), NvMapIoctlChannelAllocGpFifoEx2 }, + { ("/dev/nvmap", 0x0101), NvMapIocCreate }, + { ("/dev/nvmap", 0x0103), NvMapIocFromId }, + { ("/dev/nvmap", 0x0104), NvMapIocAlloc }, + { ("/dev/nvmap", 0x0105), NvMapIocFree }, + { ("/dev/nvmap", 0x0109), NvMapIocParam }, + { ("/dev/nvmap", 0x010e), NvMapIocGetId }, + }; + + Fds = new IdDictionary(); + + NvMaps = new IdDictionary(); + NvMapsById = new IdDictionary(); } - public static long Open(ServiceCtx Context) + public long Open(ServiceCtx Context) { long NamePtr = Context.Request.SendBuff[0].Position; string Name = AMemoryHelper.ReadAsciiString(Context.Memory, NamePtr); - int Fd = Context.Ns.Os.Fds.GenerateId(new FileDesc(Name)); + int Fd = Fds.Add(new NvFd(Name)); Context.ResponseData.Write(Fd); Context.ResponseData.Write(0); @@ -74,12 +84,12 @@ namespace Ryujinx.Core.OsHle.IpcServices.NvServices return 0; } - public static long Ioctl(ServiceCtx Context) + public long Ioctl(ServiceCtx Context) { int Fd = Context.RequestData.ReadInt32(); int Cmd = Context.RequestData.ReadInt32() & 0xffff; - FileDesc FdData = Context.Ns.Os.Fds.GetData(Fd); + NvFd FdData = Fds.GetData(Fd); long Position = Context.Request.GetSendBuffPtr(); @@ -95,18 +105,18 @@ namespace Ryujinx.Core.OsHle.IpcServices.NvServices } } - public static long Close(ServiceCtx Context) + public long Close(ServiceCtx Context) { int Fd = Context.RequestData.ReadInt32(); - Context.Ns.Os.Fds.Delete(Fd); + Fds.Delete(Fd); Context.ResponseData.Write(0); return 0; } - public static long Initialize(ServiceCtx Context) + public long Initialize(ServiceCtx Context) { long TransferMemSize = Context.RequestData.ReadInt64(); int TransferMemHandle = Context.Request.HandleDesc.ToCopy[0]; @@ -116,7 +126,7 @@ namespace Ryujinx.Core.OsHle.IpcServices.NvServices return 0; } - public static long QueryEvent(ServiceCtx Context) + public long QueryEvent(ServiceCtx Context) { int Fd = Context.RequestData.ReadInt32(); int EventId = Context.RequestData.ReadInt32(); @@ -128,7 +138,7 @@ namespace Ryujinx.Core.OsHle.IpcServices.NvServices return 0; } - public static long SetClientPid(ServiceCtx Context) + public long SetClientPid(ServiceCtx Context) { long Pid = Context.RequestData.ReadInt64(); @@ -137,7 +147,7 @@ namespace Ryujinx.Core.OsHle.IpcServices.NvServices return 0; } - private static long NvGpuAsIoctlBindChannel(ServiceCtx Context) + private long NvGpuAsIoctlBindChannel(ServiceCtx Context) { long Position = Context.Request.GetSendBuffPtr(); @@ -146,7 +156,7 @@ namespace Ryujinx.Core.OsHle.IpcServices.NvServices return 0; } - private static long NvGpuAsIoctlAllocSpace(ServiceCtx Context) + private long NvGpuAsIoctlAllocSpace(ServiceCtx Context) { long Position = Context.Request.GetSendBuffPtr(); @@ -172,7 +182,7 @@ namespace Ryujinx.Core.OsHle.IpcServices.NvServices return 0; } - private static long NvGpuAsIoctlMapBufferEx(ServiceCtx Context) + private long NvGpuAsIoctlMapBufferEx(ServiceCtx Context) { long Position = Context.Request.GetSendBuffPtr(); @@ -186,18 +196,29 @@ namespace Ryujinx.Core.OsHle.IpcServices.NvServices long MapSize = Reader.ReadInt64(); long Offset = Reader.ReadInt64(); - HNvMap NvMap = Context.Ns.Os.Handles.GetData(Handle); - - if (NvMap != null) + if (Handle == 0) { - if ((Flags & 1) != 0) - { - Offset = Context.Ns.Gpu.MapMemory(NvMap.Address, Offset, NvMap.Size); - } - else - { - Offset = Context.Ns.Gpu.MapMemory(NvMap.Address, NvMap.Size); - } + //Handle 0 is valid here, but it refers to something else. + //TODO: Figure out what, for now just return success. + return 0; + } + + NvMap Map = NvMaps.GetData(Handle); + + if (Map == null) + { + Logging.Warn($"Trying to use invalid NvMap Handle {Handle}!"); + + return -1; //TODO: Corrent error code. + } + + if ((Flags & 1) != 0) + { + Offset = Context.Ns.Gpu.MapMemory(Map.Address, Offset, Map.Size); + } + else + { + Offset = Context.Ns.Gpu.MapMemory(Map.Address, Map.Size); } Context.Memory.WriteInt64(Position + 0x20, Offset); @@ -205,7 +226,7 @@ namespace Ryujinx.Core.OsHle.IpcServices.NvServices return 0; } - private static long NvGpuAsIoctlGetVaRegions(ServiceCtx Context) + private long NvGpuAsIoctlGetVaRegions(ServiceCtx Context) { long Position = Context.Request.GetSendBuffPtr(); @@ -235,7 +256,7 @@ namespace Ryujinx.Core.OsHle.IpcServices.NvServices return 0; } - private static long NvGpuAsIoctlInitializeEx(ServiceCtx Context) + private long NvGpuAsIoctlInitializeEx(ServiceCtx Context) { long Position = Context.Request.GetSendBuffPtr(); @@ -252,7 +273,7 @@ namespace Ryujinx.Core.OsHle.IpcServices.NvServices return 0; } - private static long NvHostIoctlCtrlGetConfig(ServiceCtx Context) + private long NvHostIoctlCtrlGetConfig(ServiceCtx Context) { long Position = Context.Request.GetSendBuffPtr(); @@ -267,7 +288,7 @@ namespace Ryujinx.Core.OsHle.IpcServices.NvServices return 0; } - private static long NvHostIoctlCtrlEventWait(ServiceCtx Context) + private long NvHostIoctlCtrlEventWait(ServiceCtx Context) { long Position = Context.Request.GetSendBuffPtr(); @@ -283,7 +304,7 @@ namespace Ryujinx.Core.OsHle.IpcServices.NvServices return 0; } - private static long NvGpuIoctlZcullGetCtxSize(ServiceCtx Context) + private long NvGpuIoctlZcullGetCtxSize(ServiceCtx Context) { long Position = Context.Request.GetSendBuffPtr(); @@ -292,7 +313,7 @@ namespace Ryujinx.Core.OsHle.IpcServices.NvServices return 0; } - private static long NvGpuIoctlZcullGetInfo(ServiceCtx Context) + private long NvGpuIoctlZcullGetInfo(ServiceCtx Context) { long Position = Context.Request.GetSendBuffPtr(); @@ -312,7 +333,7 @@ namespace Ryujinx.Core.OsHle.IpcServices.NvServices return 0; } - private static long NvGpuIoctlGetCharacteristics(ServiceCtx Context) + private long NvGpuIoctlGetCharacteristics(ServiceCtx Context) { long Position = Context.Request.GetSendBuffPtr(); @@ -374,7 +395,7 @@ namespace Ryujinx.Core.OsHle.IpcServices.NvServices return 0; } - private static long NvGpuIoctlGetTpcMasks(ServiceCtx Context) + private long NvGpuIoctlGetTpcMasks(ServiceCtx Context) { long Position = Context.Request.GetSendBuffPtr(); @@ -388,7 +409,7 @@ namespace Ryujinx.Core.OsHle.IpcServices.NvServices return 0; } - private static long NvGpuIoctlZbcGetActiveSlotMask(ServiceCtx Context) + private long NvGpuIoctlZbcGetActiveSlotMask(ServiceCtx Context) { long Position = Context.Request.GetSendBuffPtr(); @@ -398,14 +419,14 @@ namespace Ryujinx.Core.OsHle.IpcServices.NvServices return 0; } - private static long NvMapIoctlChannelSetUserData(ServiceCtx Context) + private long NvMapIoctlChannelSetUserData(ServiceCtx Context) { long Position = Context.Request.GetSendBuffPtr(); return 0; } - private static long NvMapIoctlChannelSetNvMap(ServiceCtx Context) + private long NvMapIoctlChannelSetNvMap(ServiceCtx Context) { long Position = Context.Request.GetSendBuffPtr(); @@ -414,7 +435,7 @@ namespace Ryujinx.Core.OsHle.IpcServices.NvServices return 0; } - private static long NvMapIoctlChannelSubmitGpFifo(ServiceCtx Context) + private long NvMapIoctlChannelSubmitGpFifo(ServiceCtx Context) { long Position = Context.Request.GetSendBuffPtr(); @@ -453,7 +474,7 @@ namespace Ryujinx.Core.OsHle.IpcServices.NvServices return 0; } - private static long NvMapIoctlChannelAllocObjCtx(ServiceCtx Context) + private long NvMapIoctlChannelAllocObjCtx(ServiceCtx Context) { long Position = Context.Request.GetSendBuffPtr(); @@ -465,7 +486,7 @@ namespace Ryujinx.Core.OsHle.IpcServices.NvServices return 0; } - private static long NvMapIoctlChannelZcullBind(ServiceCtx Context) + private long NvMapIoctlChannelZcullBind(ServiceCtx Context) { long Position = Context.Request.GetSendBuffPtr(); @@ -478,7 +499,7 @@ namespace Ryujinx.Core.OsHle.IpcServices.NvServices return 0; } - private static long NvMapIoctlChannelSetErrorNotifier(ServiceCtx Context) + private long NvMapIoctlChannelSetErrorNotifier(ServiceCtx Context) { long Position = Context.Request.GetSendBuffPtr(); @@ -492,7 +513,7 @@ namespace Ryujinx.Core.OsHle.IpcServices.NvServices return 0; } - private static long NvMapIoctlChannelSetPriority(ServiceCtx Context) + private long NvMapIoctlChannelSetPriority(ServiceCtx Context) { long Position = Context.Request.GetSendBuffPtr(); @@ -501,7 +522,7 @@ namespace Ryujinx.Core.OsHle.IpcServices.NvServices return 0; } - private static long NvMapIoctlChannelAllocGpFifoEx2(ServiceCtx Context) + private long NvMapIoctlChannelAllocGpFifoEx2(ServiceCtx Context) { long Position = Context.Request.GetSendBuffPtr(); @@ -521,47 +542,46 @@ namespace Ryujinx.Core.OsHle.IpcServices.NvServices return 0; } - private static long NvMapIocCreate(ServiceCtx Context) + private long NvMapIocCreate(ServiceCtx Context) { long Position = Context.Request.GetSendBuffPtr(); int Size = Context.Memory.ReadInt32(Position); - int Id = Context.Ns.Os.NvMapIds.GenerateId(); + NvMap Map = new NvMap() { Size = Size }; - int Handle = Context.Ns.Os.Handles.GenerateId(new HNvMap(Id, Size)); + Map.Handle = NvMaps.Add(Map); - Context.Memory.WriteInt32(Position + 4, Handle); + Map.Id = NvMapsById.Add(Map); - Logging.Info($"NvMap {Id} created with size {Size:x8}!"); + Context.Memory.WriteInt32(Position + 4, Map.Handle); + + Logging.Info($"NvMap {Map.Id} created with size {Size:x8}!"); return 0; } - private static long NvMapIocFromId(ServiceCtx Context) + private long NvMapIocFromId(ServiceCtx Context) { long Position = Context.Request.GetSendBuffPtr(); int Id = Context.Memory.ReadInt32(Position); - int Handle = -1; + NvMap Map = NvMapsById.GetData(Id); - foreach (KeyValuePair KV in Context.Ns.Os.Handles) + if (Map == null) { - if (KV.Value is HNvMap NvMap && NvMap.Id == Id) - { - Handle = KV.Key; - - break; - } + Logging.Warn($"Trying to use invalid NvMap Id {Id}!"); + + return -1; //TODO: Corrent error code. } - Context.Memory.WriteInt32(Position + 4, Handle); + Context.Memory.WriteInt32(Position + 4, Map.Handle); return 0; } - private static long NvMapIocAlloc(ServiceCtx Context) + private long NvMapIocAlloc(ServiceCtx Context) { long Position = Context.Request.GetSendBuffPtr(); @@ -574,38 +594,49 @@ namespace Ryujinx.Core.OsHle.IpcServices.NvServices byte Kind = (byte)Reader.ReadInt64(); long Addr = Reader.ReadInt64(); - HNvMap NvMap = Context.Ns.Os.Handles.GetData(Handle); + NvMap Map = NvMaps.GetData(Handle); - if (NvMap != null) + if (Map == null) { - NvMap.Address = Addr; - NvMap.Align = Align; - NvMap.Kind = Kind; + Logging.Warn($"Trying to use invalid NvMap Handle {Handle}!"); + + return -1; //TODO: Corrent error code. } + Map.Address = Addr; + Map.Align = Align; + Map.Kind = Kind; + return 0; } - private static long NvMapIocFree(ServiceCtx Context) + private long NvMapIocFree(ServiceCtx Context) { long Position = Context.Request.GetSendBuffPtr(); MemReader Reader = new MemReader(Context.Memory, Position); MemWriter Writer = new MemWriter(Context.Memory, Position + 8); - int Handle = Reader.ReadInt32(); - int Padding = Reader.ReadInt32(); + int Handle = Reader.ReadInt32(); + int Padding = Reader.ReadInt32(); - HNvMap NvMap = Context.Ns.Os.Handles.GetData(Handle); + NvMap Map = NvMaps.GetData(Handle); + + if (Map == null) + { + Logging.Warn($"Trying to use invalid NvMap Handle {Handle}!"); + + return -1; //TODO: Corrent error code. + } Writer.WriteInt64(0); - Writer.WriteInt32(NvMap.Size); + Writer.WriteInt32(Map.Size); Writer.WriteInt32(0); return 0; } - private static long NvMapIocParam(ServiceCtx Context) + private long NvMapIocParam(ServiceCtx Context) { long Position = Context.Request.GetSendBuffPtr(); @@ -614,16 +645,23 @@ namespace Ryujinx.Core.OsHle.IpcServices.NvServices int Handle = Reader.ReadInt32(); int Param = Reader.ReadInt32(); - HNvMap NvMap = Context.Ns.Os.Handles.GetData(Handle); + NvMap Map = NvMaps.GetData(Handle); + + if (Map == null) + { + Logging.Warn($"Trying to use invalid NvMap Handle {Handle}!"); + + return -1; //TODO: Corrent error code. + } int Response = 0; - + switch (Param) { - case 1: Response = NvMap.Size; break; - case 2: Response = NvMap.Align; break; - case 4: Response = 0x40000000; break; - case 5: Response = NvMap.Kind; break; + case 1: Response = Map.Size; break; + case 2: Response = Map.Align; break; + case 4: Response = 0x40000000; break; + case 5: Response = Map.Kind; break; } Context.Memory.WriteInt32(Position + 8, Response); @@ -631,17 +669,29 @@ namespace Ryujinx.Core.OsHle.IpcServices.NvServices return 0; } - private static long NvMapIocGetId(ServiceCtx Context) + private long NvMapIocGetId(ServiceCtx Context) { long Position = Context.Request.GetSendBuffPtr(); int Handle = Context.Memory.ReadInt32(Position + 4); - HNvMap NvMap = Context.Ns.Os.Handles.GetData(Handle); + NvMap Map = NvMaps.GetData(Handle); - Context.Memory.WriteInt32(Position, NvMap.Id); + if (Map == null) + { + Logging.Warn($"Trying to use invalid NvMap Handle {Handle}!"); + + return -1; //TODO: Corrent error code. + } + + Context.Memory.WriteInt32(Position, Map.Id); return 0; } + + public NvMap GetNvMap(int Handle) + { + return NvMaps.GetData(Handle); + } } } \ No newline at end of file diff --git a/Ryujinx.Core/OsHle/Services/ObjHelper.cs b/Ryujinx.Core/OsHle/Services/ObjHelper.cs index ff71838a69..89d986aeb0 100644 --- a/Ryujinx.Core/OsHle/Services/ObjHelper.cs +++ b/Ryujinx.Core/OsHle/Services/ObjHelper.cs @@ -9,13 +9,13 @@ namespace Ryujinx.Core.OsHle.IpcServices { if (Context.Session is HDomain Dom) { - Context.Response.ResponseObjIds.Add(Dom.GenerateObjectId(Obj)); + Context.Response.ResponseObjIds.Add(Dom.Add(Obj)); } else { HSessionObj HndData = new HSessionObj(Context.Session, Obj); - int VHandle = Context.Ns.Os.Handles.GenerateId(HndData); + int VHandle = Context.Process.HandleTable.OpenHandle(HndData); Context.Response.HandleDesc = IpcHandleDesc.MakeMove(VHandle); } diff --git a/Ryujinx.Core/OsHle/Services/Pl/ServicePl.cs b/Ryujinx.Core/OsHle/Services/Pl/ServicePl.cs index abc34ed213..bb795f3f49 100644 --- a/Ryujinx.Core/OsHle/Services/Pl/ServicePl.cs +++ b/Ryujinx.Core/OsHle/Services/Pl/ServicePl.cs @@ -20,30 +20,32 @@ namespace Ryujinx.Core.OsHle.IpcServices.Pl }; } - public static long GetLoadState(ServiceCtx Context) + public long GetLoadState(ServiceCtx Context) { Context.ResponseData.Write(1); //Loaded return 0; } - public static long GetFontSize(ServiceCtx Context) + public long GetFontSize(ServiceCtx Context) { Context.ResponseData.Write(Horizon.FontSize); return 0; } - public static long GetSharedMemoryAddressOffset(ServiceCtx Context) + public long GetSharedMemoryAddressOffset(ServiceCtx Context) { Context.ResponseData.Write(0); return 0; } - public static long GetSharedMemoryNativeHandle(ServiceCtx Context) + public long GetSharedMemoryNativeHandle(ServiceCtx Context) { - Context.Response.HandleDesc = IpcHandleDesc.MakeCopy(Context.Ns.Os.FontHandle); + int Handle = Context.Process.HandleTable.OpenHandle(Context.Ns.Os.FontSharedMem); + + Context.Response.HandleDesc = IpcHandleDesc.MakeCopy(Handle); return 0; } diff --git a/Ryujinx.Core/OsHle/Services/ServiceFactory.cs b/Ryujinx.Core/OsHle/Services/ServiceFactory.cs deleted file mode 100644 index 2b855d25b7..0000000000 --- a/Ryujinx.Core/OsHle/Services/ServiceFactory.cs +++ /dev/null @@ -1,64 +0,0 @@ -using Ryujinx.Core.OsHle.IpcServices.Acc; -using Ryujinx.Core.OsHle.IpcServices.Am; -using Ryujinx.Core.OsHle.IpcServices.Apm; -using Ryujinx.Core.OsHle.IpcServices.Aud; -using Ryujinx.Core.OsHle.IpcServices.Bsd; -using Ryujinx.Core.OsHle.IpcServices.Friend; -using Ryujinx.Core.OsHle.IpcServices.FspSrv; -using Ryujinx.Core.OsHle.IpcServices.Hid; -using Ryujinx.Core.OsHle.IpcServices.Lm; -using Ryujinx.Core.OsHle.IpcServices.Nifm; -using Ryujinx.Core.OsHle.IpcServices.Ns; -using Ryujinx.Core.OsHle.IpcServices.NvServices; -using Ryujinx.Core.OsHle.IpcServices.Pctl; -using Ryujinx.Core.OsHle.IpcServices.Pl; -using Ryujinx.Core.OsHle.IpcServices.Set; -using Ryujinx.Core.OsHle.IpcServices.Sfdnsres; -using Ryujinx.Core.OsHle.IpcServices.Sm; -using Ryujinx.Core.OsHle.IpcServices.Ssl; -using Ryujinx.Core.OsHle.IpcServices.Time; -using Ryujinx.Core.OsHle.IpcServices.Vi; -using System; - -namespace Ryujinx.Core.OsHle.IpcServices -{ - static class ServiceFactory - { - public static IIpcService MakeService(string Name) - { - switch (Name) - { - case "acc:u0": return new ServiceAcc(); - case "aoc:u": return new ServiceNs(); - case "apm": return new ServiceApm(); - case "apm:p": return new ServiceApm(); - case "appletOE": return new ServiceAppletOE(); - case "audout:u": return new ServiceAudOut(); - case "audren:u": return new ServiceAudRen(); - case "bsd:s": return new ServiceBsd(); - case "bsd:u": return new ServiceBsd(); - case "friend:a": return new ServiceFriend(); - case "fsp-srv": return new ServiceFspSrv(); - case "hid": return new ServiceHid(); - case "lm": return new ServiceLm(); - case "nifm:u": return new ServiceNifm(); - case "nvdrv": return new ServiceNvDrv(); - case "nvdrv:a": return new ServiceNvDrv(); - case "pctl:a": return new ServicePctl(); - case "pl:u": return new ServicePl(); - case "set": return new ServiceSet(); - case "set:sys": return new ServiceSetSys(); - case "sfdnsres": return new ServiceSfdnsres(); - case "sm:": return new ServiceSm(); - case "ssl": return new ServiceSsl(); - case "time:s": return new ServiceTime(); - case "time:u": return new ServiceTime(); - case "vi:m": return new ServiceVi(); - case "vi:s": return new ServiceVi(); - case "vi:u": return new ServiceVi(); - } - - throw new NotImplementedException(Name); - } - } -} \ No newline at end of file diff --git a/Ryujinx.Core/OsHle/Services/Sm/ServiceSm.cs b/Ryujinx.Core/OsHle/Services/Sm/ServiceSm.cs index a5f1b329b8..cb745e3738 100644 --- a/Ryujinx.Core/OsHle/Services/Sm/ServiceSm.cs +++ b/Ryujinx.Core/OsHle/Services/Sm/ServiceSm.cs @@ -55,9 +55,9 @@ namespace Ryujinx.Core.OsHle.IpcServices.Sm return 0; } - HSession Session = new HSession(ServiceFactory.MakeService(Name)); + HSession Session = new HSession(Context.Process.Services.GetService(Name)); - int Handle = Context.Ns.Os.Handles.GenerateId(Session); + int Handle = Context.Process.HandleTable.OpenHandle(Session); Context.Response.HandleDesc = IpcHandleDesc.MakeMove(Handle); diff --git a/Ryujinx.Core/OsHle/Display.cs b/Ryujinx.Core/OsHle/Services/Vi/Display.cs similarity index 79% rename from Ryujinx.Core/OsHle/Display.cs rename to Ryujinx.Core/OsHle/Services/Vi/Display.cs index 590841fc50..ceadc39311 100644 --- a/Ryujinx.Core/OsHle/Display.cs +++ b/Ryujinx.Core/OsHle/Services/Vi/Display.cs @@ -1,4 +1,4 @@ -namespace Ryujinx.Core.OsHle +namespace Ryujinx.Core.OsHle.IpcServices.Vi { class Display { diff --git a/Ryujinx.Core/OsHle/Services/Vi/IApplicationDisplayService.cs b/Ryujinx.Core/OsHle/Services/Vi/IApplicationDisplayService.cs index 4e40b99b3d..0ff1f90993 100644 --- a/Ryujinx.Core/OsHle/Services/Vi/IApplicationDisplayService.cs +++ b/Ryujinx.Core/OsHle/Services/Vi/IApplicationDisplayService.cs @@ -15,6 +15,8 @@ namespace Ryujinx.Core.OsHle.IpcServices.Vi public IReadOnlyDictionary Commands => m_Commands; + private IdDictionary Displays; + public IApplicationDisplayService() { m_Commands = new Dictionary() @@ -28,14 +30,17 @@ namespace Ryujinx.Core.OsHle.IpcServices.Vi { 2020, OpenLayer }, { 2021, CloseLayer }, { 2030, CreateStrayLayer }, + { 2031, DestroyStrayLayer }, { 2101, SetLayerScalingMode }, { 5202, GetDisplayVSyncEvent } }; + + Displays = new IdDictionary(); } public long GetRelayService(ServiceCtx Context) { - MakeObject(Context, new IHOSBinderDriver()); + MakeObject(Context, new IHOSBinderDriver(Context.Ns.Gpu.Renderer)); return 0; } @@ -56,7 +61,7 @@ namespace Ryujinx.Core.OsHle.IpcServices.Vi public long GetIndirectDisplayTransactionService(ServiceCtx Context) { - MakeObject(Context, new IHOSBinderDriver()); + MakeObject(Context, new IHOSBinderDriver(Context.Ns.Gpu.Renderer)); return 0; } @@ -65,7 +70,7 @@ namespace Ryujinx.Core.OsHle.IpcServices.Vi { string Name = GetDisplayName(Context); - long DisplayId = Context.Ns.Os.Displays.GenerateId(new Display(Name)); + long DisplayId = Displays.Add(new Display(Name)); Context.ResponseData.Write(DisplayId); @@ -76,7 +81,7 @@ namespace Ryujinx.Core.OsHle.IpcServices.Vi { int DisplayId = Context.RequestData.ReadInt32(); - Context.Ns.Os.Displays.Delete(DisplayId); + Displays.Delete(DisplayId); return 0; } @@ -99,6 +104,8 @@ namespace Ryujinx.Core.OsHle.IpcServices.Vi public long CloseLayer(ServiceCtx Context) { + long LayerId = Context.RequestData.ReadInt64(); + return 0; } @@ -109,7 +116,7 @@ namespace Ryujinx.Core.OsHle.IpcServices.Vi long ParcelPtr = Context.Request.ReceiveBuff[0].Position; - Display Disp = Context.Ns.Os.Displays.GetData((int)DisplayId); + Display Disp = Displays.GetData((int)DisplayId); byte[] Parcel = MakeIGraphicsBufferProducer(ParcelPtr); @@ -121,6 +128,11 @@ namespace Ryujinx.Core.OsHle.IpcServices.Vi return 0; } + public long DestroyStrayLayer(ServiceCtx Context) + { + return 0; + } + public long SetLayerScalingMode(ServiceCtx Context) { int ScalingMode = Context.RequestData.ReadInt32(); @@ -133,7 +145,7 @@ namespace Ryujinx.Core.OsHle.IpcServices.Vi { string Name = GetDisplayName(Context); - int Handle = Context.Ns.Os.Handles.GenerateId(new HEvent()); + int Handle = Context.Process.HandleTable.OpenHandle(new HEvent()); Context.Response.HandleDesc = IpcHandleDesc.MakeCopy(Handle); diff --git a/Ryujinx.Core/OsHle/Services/Vi/IHOSBinderDriver.cs b/Ryujinx.Core/OsHle/Services/Vi/IHOSBinderDriver.cs index a89c1df824..b24a773bfe 100644 --- a/Ryujinx.Core/OsHle/Services/Vi/IHOSBinderDriver.cs +++ b/Ryujinx.Core/OsHle/Services/Vi/IHOSBinderDriver.cs @@ -1,6 +1,7 @@ using ChocolArm64.Memory; using Ryujinx.Core.OsHle.Ipc; using Ryujinx.Core.OsHle.IpcServices.Android; +using Ryujinx.Graphics.Gal; using System; using System.Collections.Generic; @@ -14,7 +15,7 @@ namespace Ryujinx.Core.OsHle.IpcServices.Vi private NvFlinger Flinger; - public IHOSBinderDriver() + public IHOSBinderDriver(IGalRenderer Renderer) { m_Commands = new Dictionary() { @@ -23,7 +24,7 @@ namespace Ryujinx.Core.OsHle.IpcServices.Vi { 2, GetNativeHandle } }; - Flinger = new NvFlinger(); + Flinger = new NvFlinger(Renderer); } public long TransactParcel(ServiceCtx Context) diff --git a/Ryujinx.Core/OsHle/Services/Vi/NvFlinger.cs b/Ryujinx.Core/OsHle/Services/Vi/NvFlinger.cs index 1d394fb4a7..5309dcabb7 100644 --- a/Ryujinx.Core/OsHle/Services/Vi/NvFlinger.cs +++ b/Ryujinx.Core/OsHle/Services/Vi/NvFlinger.cs @@ -1,5 +1,6 @@ using ChocolArm64.Memory; -using Ryujinx.Core.OsHle.Handles; +using Ryujinx.Core.OsHle.IpcServices.NvServices; +using Ryujinx.Graphics.Gal; using System; using System.IO; using System.Collections.Generic; @@ -54,18 +55,27 @@ namespace Ryujinx.Core.OsHle.IpcServices.Android public GbpBuffer Data; } + private IGalRenderer Renderer; + private BufferEntry[] BufferQueue; private ManualResetEvent WaitBufferFree; + + private object RenderQueueLock; + + private int RenderQueueCount; + + private bool NvFlingerDisposed; private bool KeepRunning; - public NvFlinger() + public NvFlinger(IGalRenderer Renderer) { Commands = new Dictionary<(string, int), ServiceProcessParcel>() { { ("android.gui.IGraphicBufferProducer", 0x1), GbpRequestBuffer }, { ("android.gui.IGraphicBufferProducer", 0x3), GbpDequeueBuffer }, + { ("android.gui.IGraphicBufferProducer", 0x4), GbpDetachBuffer }, { ("android.gui.IGraphicBufferProducer", 0x7), GbpQueueBuffer }, { ("android.gui.IGraphicBufferProducer", 0x8), GbpCancelBuffer }, { ("android.gui.IGraphicBufferProducer", 0x9), GbpQuery }, @@ -74,10 +84,14 @@ namespace Ryujinx.Core.OsHle.IpcServices.Android { ("android.gui.IGraphicBufferProducer", 0xe), GbpPreallocBuffer } }; + this.Renderer = Renderer; + BufferQueue = new BufferEntry[0x40]; WaitBufferFree = new ManualResetEvent(false); + RenderQueueLock = new object(); + KeepRunning = true; } @@ -193,6 +207,11 @@ namespace Ryujinx.Core.OsHle.IpcServices.Android return MakeReplyParcel(Context, 1280, 720, 0, 0, 0); } + private long GbpDetachBuffer(ServiceCtx Context, BinaryReader ParcelReader) + { + return MakeReplyParcel(Context, 0); + } + private long GbpCancelBuffer(ServiceCtx Context, BinaryReader ParcelReader) { //TODO: Errors. @@ -266,7 +285,7 @@ namespace Ryujinx.Core.OsHle.IpcServices.Android long FbSize = (uint)FbWidth * FbHeight * 4; - HNvMap NvMap = GetNvMap(Context, Slot); + NvMap NvMap = GetNvMap(Context, Slot); if ((ulong)(NvMap.Address + FbSize) > AMemoryMgr.AddrSize) { @@ -330,7 +349,17 @@ namespace Ryujinx.Core.OsHle.IpcServices.Android Rotate = -MathF.PI * 0.5f; } - byte* Fb = (byte*)Context.Ns.Memory.Ram + NvMap.Address; + lock (RenderQueueLock) + { + if (NvFlingerDisposed) + { + return; + } + + Interlocked.Increment(ref RenderQueueCount); + } + + byte* Fb = (byte*)Context.Memory.Ram + NvMap.Address; Context.Ns.Gpu.Renderer.QueueAction(delegate() { @@ -346,6 +375,8 @@ namespace Ryujinx.Core.OsHle.IpcServices.Android BufferQueue[Slot].State = BufferState.Free; + Interlocked.Decrement(ref RenderQueueCount); + lock (WaitBufferFree) { WaitBufferFree.Set(); @@ -353,7 +384,7 @@ namespace Ryujinx.Core.OsHle.IpcServices.Android }); } - private HNvMap GetNvMap(ServiceCtx Context, int Slot) + private NvMap GetNvMap(ServiceCtx Context, int Slot) { int NvMapHandle = BitConverter.ToInt32(BufferQueue[Slot].Data.RawData, 0x4c); @@ -366,7 +397,9 @@ namespace Ryujinx.Core.OsHle.IpcServices.Android NvMapHandle = BitConverter.ToInt32(RawValue, 0); } - return Context.Ns.Os.Handles.GetData(NvMapHandle); + ServiceNvDrv NvDrv = (ServiceNvDrv)Context.Process.Services.GetService("nvdrv"); + + return NvDrv.GetNvMap(NvMapHandle); } private int GetFreeSlotBlocking(int Width, int Height) @@ -432,10 +465,24 @@ namespace Ryujinx.Core.OsHle.IpcServices.Android Dispose(true); } - protected virtual void Dispose(bool disposing) + protected virtual void Dispose(bool Disposing) { - if (disposing) + if (Disposing && !NvFlingerDisposed) { + lock (RenderQueueLock) + { + NvFlingerDisposed = true; + } + + //Ensure that all pending actions was sent before + //we can safely assume that the class was disposed. + while (RenderQueueCount > 0) + { + Thread.Yield(); + } + + Renderer.ResetFrameBuffer(); + lock (WaitBufferFree) { KeepRunning = false; diff --git a/Ryujinx.Core/OsHle/Svc/SvcHandler.cs b/Ryujinx.Core/OsHle/Svc/SvcHandler.cs index b3e6262db1..3ce56a3cd7 100644 --- a/Ryujinx.Core/OsHle/Svc/SvcHandler.cs +++ b/Ryujinx.Core/OsHle/Svc/SvcHandler.cs @@ -1,12 +1,13 @@ using ChocolArm64.Events; using ChocolArm64.Memory; using ChocolArm64.State; +using Ryujinx.Core.OsHle.Handles; using System; using System.Collections.Generic; namespace Ryujinx.Core.OsHle.Svc { - partial class SvcHandler + partial class SvcHandler : IDisposable { private delegate void SvcFunc(AThreadState ThreadState); @@ -16,10 +17,12 @@ namespace Ryujinx.Core.OsHle.Svc private Process Process; private AMemory Memory; - private static Random Rng; - + private HashSet<(HSharedMem, long)> MappedSharedMems; + private ulong CurrentHeapSize; + private static Random Rng; + public SvcHandler(Switch Ns, Process Process) { SvcFuncs = new Dictionary() @@ -32,6 +35,7 @@ namespace Ryujinx.Core.OsHle.Svc { 0x07, SvcExitProcess }, { 0x08, SvcCreateThread }, { 0x09, SvcStartThread }, + { 0x0a, SvcExitThread }, { 0x0b, SvcSleepThread }, { 0x0c, SvcGetThreadPriority }, { 0x0d, SvcSetThreadPriority }, @@ -60,6 +64,8 @@ namespace Ryujinx.Core.OsHle.Svc this.Ns = Ns; this.Process = Process; this.Memory = Process.Memory; + + MappedSharedMems = new HashSet<(HSharedMem, long)>(); } static SvcHandler() @@ -84,5 +90,26 @@ namespace Ryujinx.Core.OsHle.Svc throw new NotImplementedException(e.Id.ToString("x4")); } } + + public void Dispose() + { + Dispose(true); + } + + protected virtual void Dispose(bool Disposing) + { + if (Disposing) + { + lock (MappedSharedMems) + { + foreach ((HSharedMem SharedMem, long Position) in MappedSharedMems) + { + SharedMem.RemoveVirtualPosition(Memory, Position); + } + + MappedSharedMems.Clear(); + } + } + } } } \ No newline at end of file diff --git a/Ryujinx.Core/OsHle/Svc/SvcMemory.cs b/Ryujinx.Core/OsHle/Svc/SvcMemory.cs index c15f449bf1..80f24d2bd5 100644 --- a/Ryujinx.Core/OsHle/Svc/SvcMemory.cs +++ b/Ryujinx.Core/OsHle/Svc/SvcMemory.cs @@ -165,7 +165,7 @@ namespace Ryujinx.Core.OsHle.Svc return; } - HSharedMem SharedMem = Ns.Os.Handles.GetData(Handle); + HSharedMem SharedMem = Process.HandleTable.GetData(Handle); if (SharedMem != null) { @@ -175,7 +175,12 @@ namespace Ryujinx.Core.OsHle.Svc Memory.Manager.Reprotect(Src, Size, (AMemoryPerm)Perm); - SharedMem.AddVirtualPosition(Src); + lock (MappedSharedMems) + { + MappedSharedMems.Add((SharedMem, Src)); + } + + SharedMem.AddVirtualPosition(Memory, Src); ThreadState.X0 = 0; } @@ -198,12 +203,19 @@ namespace Ryujinx.Core.OsHle.Svc return; } - HSharedMem HndData = Ns.Os.Handles.GetData(Handle); + HSharedMem SharedMem = Process.HandleTable.GetData(Handle); - if (HndData != null) + if (SharedMem != null) { Memory.Manager.Unmap(Src, Size, (int)MemoryType.SharedMemory); + SharedMem.RemoveVirtualPosition(Memory, Src); + + lock (MappedSharedMems) + { + MappedSharedMems.Remove((SharedMem, Src)); + } + ThreadState.X0 = 0; } @@ -229,12 +241,12 @@ namespace Ryujinx.Core.OsHle.Svc Memory.Manager.Reprotect(Src, Size, (AMemoryPerm)Perm); - HTransferMem HndData = new HTransferMem(Memory, MapInfo.Perm, Src, Size); + HTransferMem TMem = new HTransferMem(Memory, MapInfo.Perm, Src, Size); - int Handle = Ns.Os.Handles.GenerateId(HndData); - - ThreadState.X1 = (ulong)Handle; + ulong Handle = (ulong)Process.HandleTable.OpenHandle(TMem); + ThreadState.X0 = 0; + ThreadState.X1 = Handle; } private static bool IsValidPosition(long Position) diff --git a/Ryujinx.Core/OsHle/Svc/SvcSystem.cs b/Ryujinx.Core/OsHle/Svc/SvcSystem.cs index f700903526..671a32d361 100644 --- a/Ryujinx.Core/OsHle/Svc/SvcSystem.cs +++ b/Ryujinx.Core/OsHle/Svc/SvcSystem.cs @@ -3,7 +3,6 @@ using ChocolArm64.State; using Ryujinx.Core.OsHle.Exceptions; using Ryujinx.Core.OsHle.Handles; using Ryujinx.Core.OsHle.Ipc; -using Ryujinx.Core.OsHle.IpcServices; using System; using System.Threading; @@ -35,7 +34,7 @@ namespace Ryujinx.Core.OsHle.Svc { int Handle = (int)ThreadState.X0; - Ns.Os.CloseHandle(Handle); + Process.HandleTable.CloseHandle(Handle); ThreadState.X0 = 0; } @@ -80,10 +79,12 @@ namespace Ryujinx.Core.OsHle.Svc //TODO: Validate that app has perms to access the service, and that the service //actually exists, return error codes otherwise. - HSession Session = new HSession(ServiceFactory.MakeService(Name)); + HSession Session = new HSession(Process.Services.GetService(Name)); - ThreadState.X1 = (ulong)Ns.Os.Handles.GenerateId(Session); + ulong Handle = (ulong)Process.HandleTable.OpenHandle(Session); + ThreadState.X0 = 0; + ThreadState.X1 = Handle; } private void SvcSendSyncRequest(AThreadState ThreadState) @@ -119,13 +120,21 @@ namespace Ryujinx.Core.OsHle.Svc byte[] CmdData = AMemoryHelper.ReadBytes(Memory, CmdPtr, (int)Size); - HSession Session = Ns.Os.Handles.GetData(Handle); + HSession Session = Process.HandleTable.GetData(Handle); IpcMessage Cmd = new IpcMessage(CmdData, CmdPtr, Session is HDomain); if (Session != null) { - IpcHandler.IpcCall(Ns, Memory, Session, Cmd, ThreadState.ThreadId, CmdPtr, Handle); + IpcHandler.IpcCall( + Ns, + Process, + Memory, + Session, + Cmd, + ThreadState.ThreadId, + CmdPtr, + Handle); byte[] Response = AMemoryHelper.ReadBytes(Memory, CmdPtr, (int)Size); diff --git a/Ryujinx.Core/OsHle/Svc/SvcThread.cs b/Ryujinx.Core/OsHle/Svc/SvcThread.cs index 6afd2e6106..231ee2a237 100644 --- a/Ryujinx.Core/OsHle/Svc/SvcThread.cs +++ b/Ryujinx.Core/OsHle/Svc/SvcThread.cs @@ -39,7 +39,7 @@ namespace Ryujinx.Core.OsHle.Svc { int Handle = (int)ThreadState.X0; - HThread Thread = Ns.Os.Handles.GetData(Handle); + HThread Thread = Process.HandleTable.GetData(Handle); if (Thread != null) { @@ -51,6 +51,13 @@ namespace Ryujinx.Core.OsHle.Svc //TODO: Error codes. } + private void SvcExitThread(AThreadState ThreadState) + { + HThread CurrThread = Process.GetThread(ThreadState.Tpidr); + + CurrThread.Thread.StopExecution(); + } + private void SvcSleepThread(AThreadState ThreadState) { ulong NanoSecs = ThreadState.X0; @@ -71,7 +78,7 @@ namespace Ryujinx.Core.OsHle.Svc { int Handle = (int)ThreadState.X1; - HThread Thread = Ns.Os.Handles.GetData(Handle); + HThread Thread = Process.HandleTable.GetData(Handle); if (Thread != null) { @@ -87,7 +94,7 @@ namespace Ryujinx.Core.OsHle.Svc int Handle = (int)ThreadState.X1; int Prio = (int)ThreadState.X0; - HThread Thread = Ns.Os.Handles.GetData(Handle); + HThread Thread = Process.HandleTable.GetData(Handle); if (Thread != null) { @@ -110,7 +117,7 @@ namespace Ryujinx.Core.OsHle.Svc { int Handle = (int)ThreadState.X0; - HThread Thread = Ns.Os.Handles.GetData(Handle); + HThread Thread = Process.HandleTable.GetData(Handle); if (Thread != null) { diff --git a/Ryujinx.Core/OsHle/Svc/SvcThreadSync.cs b/Ryujinx.Core/OsHle/Svc/SvcThreadSync.cs index 6e488da589..38356073e6 100644 --- a/Ryujinx.Core/OsHle/Svc/SvcThreadSync.cs +++ b/Ryujinx.Core/OsHle/Svc/SvcThreadSync.cs @@ -13,7 +13,7 @@ namespace Ryujinx.Core.OsHle.Svc long MutexAddress = (long)ThreadState.X1; int RequestingThreadHandle = (int)ThreadState.X2; - HThread RequestingThread = Ns.Os.Handles.GetData(RequestingThreadHandle); + HThread RequestingThread = Process.HandleTable.GetData(RequestingThreadHandle); Mutex M = new Mutex(Process, MutexAddress, OwnerThreadHandle); @@ -43,7 +43,7 @@ namespace Ryujinx.Core.OsHle.Svc int ThreadHandle = (int)ThreadState.X2; long Timeout = (long)ThreadState.X3; - HThread Thread = Ns.Os.Handles.GetData(ThreadHandle); + HThread Thread = Process.HandleTable.GetData(ThreadHandle); Mutex M = new Mutex(Process, MutexAddress, ThreadHandle); diff --git a/Ryujinx.Core/OsHle/Utilities/IdPool.cs b/Ryujinx.Core/OsHle/Utilities/IdPool.cs deleted file mode 100644 index a7e181fa23..0000000000 --- a/Ryujinx.Core/OsHle/Utilities/IdPool.cs +++ /dev/null @@ -1,53 +0,0 @@ -using System.Collections.Generic; - -namespace Ryujinx.Core.OsHle.Utilities -{ - class IdPool - { - private HashSet Ids; - - private int CurrId; - private int MinId; - private int MaxId; - - public IdPool(int Min, int Max) - { - Ids = new HashSet(); - - CurrId = Min; - MinId = Min; - MaxId = Max; - } - - public IdPool() : this(1, int.MaxValue) { } - - public int GenerateId() - { - lock (Ids) - { - for (int Cnt = MinId; Cnt < MaxId; Cnt++) - { - if (Ids.Add(CurrId)) - { - return CurrId; - } - - if (CurrId++ == MaxId) - { - CurrId = MinId; - } - } - - return -1; - } - } - - public bool DeleteId(int Id) - { - lock (Ids) - { - return Ids.Remove(Id); - } - } - } -} \ No newline at end of file diff --git a/Ryujinx.Core/Switch.cs b/Ryujinx.Core/Switch.cs index f7ce109f6f..487f3bdb99 100644 --- a/Ryujinx.Core/Switch.cs +++ b/Ryujinx.Core/Switch.cs @@ -1,4 +1,3 @@ -using ChocolArm64.Memory; using Ryujinx.Core.Input; using Ryujinx.Core.OsHle; using Ryujinx.Core.Settings; @@ -10,8 +9,6 @@ namespace Ryujinx.Core { public class Switch : IDisposable { - internal AMemory Memory { get; private set; } - internal NsGpu Gpu { get; private set; } internal Horizon Os { get; private set; } internal VirtualFs VFs { get; private set; } @@ -24,13 +21,11 @@ namespace Ryujinx.Core public Switch(IGalRenderer Renderer) { - Memory = new AMemory(); - Gpu = new NsGpu(Renderer); VFs = new VirtualFs(); - Hid = new Hid(Memory); + Hid = new Hid(); Statistics = new PerformanceStatistics(); @@ -42,11 +37,6 @@ namespace Ryujinx.Core Settings = new SetSys(); } - public void FinalizeAllProcesses() - { - Os.FinalizeAllProcesses(); - } - public void LoadCart(string ExeFsDir, string RomFsFile = null) { Os.LoadCart(ExeFsDir, RomFsFile); @@ -67,12 +57,11 @@ namespace Ryujinx.Core Dispose(true); } - protected virtual void Dispose(bool disposing) + protected virtual void Dispose(bool Disposing) { - if (disposing) + if (Disposing) { - Memory.Dispose(); - + Os.Dispose(); VFs.Dispose(); } } diff --git a/Ryujinx.Graphics/Gal/IGalRenderer.cs b/Ryujinx.Graphics/Gal/IGalRenderer.cs index 83d2c699f8..aa4eac4e14 100644 --- a/Ryujinx.Graphics/Gal/IGalRenderer.cs +++ b/Ryujinx.Graphics/Gal/IGalRenderer.cs @@ -8,6 +8,7 @@ namespace Ryujinx.Graphics.Gal void RunActions(); void InitializeFrameBuffer(); + void ResetFrameBuffer(); void Render(); void SetWindowSize(int Width, int Height); void SetFrameBuffer( diff --git a/Ryujinx.Graphics/Gal/OpenGL/FrameBuffer.cs b/Ryujinx.Graphics/Gal/OpenGL/FrameBuffer.cs index 7dc4bffe34..b761811f64 100644 --- a/Ryujinx.Graphics/Gal/OpenGL/FrameBuffer.cs +++ b/Ryujinx.Graphics/Gal/OpenGL/FrameBuffer.cs @@ -24,6 +24,8 @@ namespace Ryujinx.Graphics.Gal.OpenGL private byte* FbPtr; + private object FbPtrLock; + public FrameBuffer(int Width, int Height) { if (Width < 0) @@ -36,6 +38,8 @@ namespace Ryujinx.Graphics.Gal.OpenGL throw new ArgumentOutOfRangeException(nameof(Height)); } + FbPtrLock = new object(); + TexWidth = Width; TexHeight = Height; @@ -152,7 +156,10 @@ namespace Ryujinx.Graphics.Gal.OpenGL throw new ArgumentOutOfRangeException(nameof(Height)); } - FbPtr = Fb; + lock (FbPtrLock) + { + FbPtr = Fb; + } if (Width != TexWidth || Height != TexHeight) @@ -178,17 +185,28 @@ namespace Ryujinx.Graphics.Gal.OpenGL GL.Uniform2(OffsetUniformLocation, Offs); } + public void Reset() + { + lock (FbPtrLock) + { + FbPtr = null; + } + } + public void Render() { - if (FbPtr == null) + lock (FbPtrLock) { - return; - } + if (FbPtr == null) + { + return; + } - for (int Y = 0; Y < TexHeight; Y++) - for (int X = 0; X < TexWidth; X++) - { - Pixels[X + Y * TexWidth] = *((int*)(FbPtr + GetSwizzleOffset(X, Y))); + for (int Y = 0; Y < TexHeight; Y++) + for (int X = 0; X < TexWidth; X++) + { + Pixels[X + Y * TexWidth] = *((int*)(FbPtr + GetSwizzleOffset(X, Y))); + } } GL.BindTexture(TextureTarget.Texture2D, TexHandle); diff --git a/Ryujinx.Graphics/Gal/OpenGL/OpenGLRenderer.cs b/Ryujinx.Graphics/Gal/OpenGL/OpenGLRenderer.cs index bdedfc1a87..002e54dadc 100644 --- a/Ryujinx.Graphics/Gal/OpenGL/OpenGLRenderer.cs +++ b/Ryujinx.Graphics/Gal/OpenGL/OpenGLRenderer.cs @@ -43,6 +43,11 @@ namespace Ryujinx.Graphics.Gal.OpenGL FbRenderer = new FrameBuffer(1280, 720); } + public void ResetFrameBuffer() + { + FbRenderer.Reset(); + } + public void QueueAction(Action ActionMthd) { ActionsQueue.Enqueue(ActionMthd); diff --git a/Ryujinx.Tests/Cpu/CpuTest.cs b/Ryujinx.Tests/Cpu/CpuTest.cs index 0b35a7a6c3..b5bbdbc171 100644 --- a/Ryujinx.Tests/Cpu/CpuTest.cs +++ b/Ryujinx.Tests/Cpu/CpuTest.cs @@ -28,7 +28,7 @@ namespace Ryujinx.Tests.Cpu ATranslator Translator = new ATranslator(); Memory = new AMemory(); Memory.Manager.Map(Position, Size, 2, AMemoryPerm.Read | AMemoryPerm.Write | AMemoryPerm.Execute); - Thread = new AThread(Translator, Memory, ThreadPriority.Normal, EntryPoint); + Thread = new AThread(Translator, Memory, EntryPoint); } [TearDown] From 8cb23c133baf2a371f6dfaaba0c7db7216c2efd1 Mon Sep 17 00:00:00 2001 From: gdkchan Date: Mon, 12 Mar 2018 01:15:39 -0300 Subject: [PATCH 19/28] Add bsd:s service variant again --- Ryujinx.Core/OsHle/ServiceMgr.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/Ryujinx.Core/OsHle/ServiceMgr.cs b/Ryujinx.Core/OsHle/ServiceMgr.cs index cbf6386f9d..f59647afef 100644 --- a/Ryujinx.Core/OsHle/ServiceMgr.cs +++ b/Ryujinx.Core/OsHle/ServiceMgr.cs @@ -48,6 +48,7 @@ namespace Ryujinx.Core.OsHle case "appletOE": Service = new ServiceAppletOE(); break; case "audout:u": Service = new ServiceAudOut(); break; case "audren:u": Service = new ServiceAudRen(); break; + case "bsd:s": Service = new ServiceBsd(); break; case "bsd:u": Service = new ServiceBsd(); break; case "friend:a": Service = new ServiceFriend(); break; case "fsp-srv": Service = new ServiceFspSrv(); break; From e32f27158fa314492af35800eb0c84ca0e7b5bd4 Mon Sep 17 00:00:00 2001 From: gdkchan Date: Mon, 12 Mar 2018 02:07:48 -0300 Subject: [PATCH 20/28] Stub GetAudioRenderersProcessMasterVolume and avoid blowing up the stack inside HDomains Dispose method --- Ryujinx.Core/OsHle/Handles/HDomain.cs | 2 +- Ryujinx.Core/OsHle/Services/Aud/ServiceAudRen.cs | 12 ++++++++++-- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/Ryujinx.Core/OsHle/Handles/HDomain.cs b/Ryujinx.Core/OsHle/Handles/HDomain.cs index ac99b03a6b..26c604554f 100644 --- a/Ryujinx.Core/OsHle/Handles/HDomain.cs +++ b/Ryujinx.Core/OsHle/Handles/HDomain.cs @@ -37,7 +37,7 @@ namespace Ryujinx.Core.OsHle.Handles { foreach (object Obj in Objects) { - if (Obj is IDisposable DisposableObj) + if (Obj != this && Obj is IDisposable DisposableObj) { DisposableObj.Dispose(); } diff --git a/Ryujinx.Core/OsHle/Services/Aud/ServiceAudRen.cs b/Ryujinx.Core/OsHle/Services/Aud/ServiceAudRen.cs index eccc699c43..15bda04cc2 100644 --- a/Ryujinx.Core/OsHle/Services/Aud/ServiceAudRen.cs +++ b/Ryujinx.Core/OsHle/Services/Aud/ServiceAudRen.cs @@ -15,8 +15,9 @@ namespace Ryujinx.Core.OsHle.IpcServices.Aud { m_Commands = new Dictionary() { - { 0, OpenAudioRenderer }, - { 1, GetAudioRendererWorkBufferSize }, + { 0, OpenAudioRenderer }, + { 1, GetAudioRendererWorkBufferSize }, + { 2, GetAudioRenderersProcessMasterVolume } }; } @@ -47,5 +48,12 @@ namespace Ryujinx.Core.OsHle.IpcServices.Aud return 0; } + + public long GetAudioRenderersProcessMasterVolume(ServiceCtx Context) + { + Context.ResponseData.Write(0); + + return 0; + } } } \ No newline at end of file From d88b5c762143d42367b9cbcdb48337db482dbed3 Mon Sep 17 00:00:00 2001 From: gdkchan Date: Mon, 12 Mar 2018 16:29:06 -0300 Subject: [PATCH 21/28] Fix GetAudioRenderersProcessMasterVolume which was totally wrong --- ChocolArm64/Instruction/AInstEmitSystem.cs | 10 ++- .../OsHle/Services/Aud/IAudioDeviceService.cs | 63 +++++++++++++++++++ .../OsHle/Services/Aud/ServiceAudRen.cs | 13 ++-- 3 files changed, 80 insertions(+), 6 deletions(-) create mode 100644 Ryujinx.Core/OsHle/Services/Aud/IAudioDeviceService.cs diff --git a/ChocolArm64/Instruction/AInstEmitSystem.cs b/ChocolArm64/Instruction/AInstEmitSystem.cs index f9d186020e..80b566049b 100644 --- a/ChocolArm64/Instruction/AInstEmitSystem.cs +++ b/ChocolArm64/Instruction/AInstEmitSystem.cs @@ -89,6 +89,9 @@ namespace ChocolArm64.Instruction //We treat it as no-op here since we don't have any cache being emulated anyway. AOpCodeSystem Op = (AOpCodeSystem)Context.CurrOp; + //TODO: We should throw on unimplemented sys instructions here, + //since it causing some problems when the programs expects some values + //that never return. switch (GetPackedId(Op)) { case 0b11_011_0111_0100_001: @@ -97,7 +100,7 @@ namespace ChocolArm64.Instruction for (int Offs = 0; Offs < (4 << AThreadState.DczSizeLog2); Offs += 8) { Context.EmitLdarg(ATranslatedSub.MemoryArgIdx); - Context.EmitLdint(Op.Rt); + Context.EmitLdintzr(Op.Rt); Context.EmitLdc_I(Offs); Context.Emit(OpCodes.Add); @@ -106,8 +109,13 @@ namespace ChocolArm64.Instruction AInstEmitMemoryHelper.EmitWriteCall(Context, 3); } + break; } + + //No-op + case 0b11_011_0111_1110_001: //DC CIVAC + break; } } diff --git a/Ryujinx.Core/OsHle/Services/Aud/IAudioDeviceService.cs b/Ryujinx.Core/OsHle/Services/Aud/IAudioDeviceService.cs new file mode 100644 index 0000000000..9ebf140a26 --- /dev/null +++ b/Ryujinx.Core/OsHle/Services/Aud/IAudioDeviceService.cs @@ -0,0 +1,63 @@ +using ChocolArm64.Memory; +using Ryujinx.Core.OsHle.Ipc; +using System.Collections.Generic; +using System.Text; + +namespace Ryujinx.Core.OsHle.IpcServices.Aud +{ + class IAudioDevice : IIpcService + { + private Dictionary m_Commands; + + public IReadOnlyDictionary Commands => m_Commands; + + public IAudioDevice() + { + m_Commands = new Dictionary() + { + { 0, ListAudioDeviceName }, + { 1, SetAudioDeviceOutputVolume }, + }; + } + + public long ListAudioDeviceName(ServiceCtx Context) + { + string[] Names = new string[] { "FIXME" }; + + Context.ResponseData.Write(Names.Length); + + long Position = Context.Request.ReceiveBuff[0].Position; + long Size = Context.Request.ReceiveBuff[0].Size; + + long BasePosition = Position; + + foreach (string Name in Names) + { + byte[] Buffer = Encoding.ASCII.GetBytes(Name + '\0'); + + if ((Position - BasePosition) + Buffer.Length > Size) + { + break; + } + + AMemoryHelper.WriteBytes(Context.Memory, Position, Buffer); + + Position += Buffer.Length; + } + + return 0; + } + + public long SetAudioDeviceOutputVolume(ServiceCtx Context) + { + float Volume = Context.RequestData.ReadSingle(); + + long Position = Context.Request.SendBuff[0].Position; + long Size = Context.Request.SendBuff[0].Size; + + string Name = AMemoryHelper.ReadAsciiString(Context.Memory, Position, (int)Size); + + return 0; + } + } +} \ No newline at end of file diff --git a/Ryujinx.Core/OsHle/Services/Aud/ServiceAudRen.cs b/Ryujinx.Core/OsHle/Services/Aud/ServiceAudRen.cs index 15bda04cc2..155d042543 100644 --- a/Ryujinx.Core/OsHle/Services/Aud/ServiceAudRen.cs +++ b/Ryujinx.Core/OsHle/Services/Aud/ServiceAudRen.cs @@ -1,3 +1,4 @@ +using Ryujinx.Core.OsHle.Handles; using Ryujinx.Core.OsHle.Ipc; using System.Collections.Generic; @@ -15,9 +16,9 @@ namespace Ryujinx.Core.OsHle.IpcServices.Aud { m_Commands = new Dictionary() { - { 0, OpenAudioRenderer }, - { 1, GetAudioRendererWorkBufferSize }, - { 2, GetAudioRenderersProcessMasterVolume } + { 0, OpenAudioRenderer }, + { 1, GetAudioRendererWorkBufferSize }, + { 2, GetAudioDevice } }; } @@ -49,9 +50,11 @@ namespace Ryujinx.Core.OsHle.IpcServices.Aud return 0; } - public long GetAudioRenderersProcessMasterVolume(ServiceCtx Context) + public long GetAudioDevice(ServiceCtx Context) { - Context.ResponseData.Write(0); + long UserId = Context.RequestData.ReadInt64(); + + MakeObject(Context, new IAudioDevice()); return 0; } From 6f4282daf8b5bfa650dc8c43714c7955dc779cd1 Mon Sep 17 00:00:00 2001 From: gdkchan Date: Mon, 12 Mar 2018 16:31:09 -0300 Subject: [PATCH 22/28] IAudioDeviceService -> IAudioDevice --- ChocolArm64/Instruction/AInstEmitSystem.cs | 3 --- .../Services/Aud/{IAudioDeviceService.cs => IAudioDevice.cs} | 0 Ryujinx.Core/OsHle/Services/Aud/ServiceAudRen.cs | 1 - 3 files changed, 4 deletions(-) rename Ryujinx.Core/OsHle/Services/Aud/{IAudioDeviceService.cs => IAudioDevice.cs} (100%) diff --git a/ChocolArm64/Instruction/AInstEmitSystem.cs b/ChocolArm64/Instruction/AInstEmitSystem.cs index 80b566049b..42a62009b5 100644 --- a/ChocolArm64/Instruction/AInstEmitSystem.cs +++ b/ChocolArm64/Instruction/AInstEmitSystem.cs @@ -89,9 +89,6 @@ namespace ChocolArm64.Instruction //We treat it as no-op here since we don't have any cache being emulated anyway. AOpCodeSystem Op = (AOpCodeSystem)Context.CurrOp; - //TODO: We should throw on unimplemented sys instructions here, - //since it causing some problems when the programs expects some values - //that never return. switch (GetPackedId(Op)) { case 0b11_011_0111_0100_001: diff --git a/Ryujinx.Core/OsHle/Services/Aud/IAudioDeviceService.cs b/Ryujinx.Core/OsHle/Services/Aud/IAudioDevice.cs similarity index 100% rename from Ryujinx.Core/OsHle/Services/Aud/IAudioDeviceService.cs rename to Ryujinx.Core/OsHle/Services/Aud/IAudioDevice.cs diff --git a/Ryujinx.Core/OsHle/Services/Aud/ServiceAudRen.cs b/Ryujinx.Core/OsHle/Services/Aud/ServiceAudRen.cs index 155d042543..c3a0a8b436 100644 --- a/Ryujinx.Core/OsHle/Services/Aud/ServiceAudRen.cs +++ b/Ryujinx.Core/OsHle/Services/Aud/ServiceAudRen.cs @@ -1,4 +1,3 @@ -using Ryujinx.Core.OsHle.Handles; using Ryujinx.Core.OsHle.Ipc; using System.Collections.Generic; From 4d90d60119992556b28cde119e9a94b8ac4aebf8 Mon Sep 17 00:00:00 2001 From: emmauss Date: Tue, 13 Mar 2018 23:59:17 +0200 Subject: [PATCH 23/28] stub some hid service functions (#57) --- Ryujinx.Core/OsHle/Services/Hid/ServiceHid.cs | 60 +++++++++++++++---- 1 file changed, 49 insertions(+), 11 deletions(-) diff --git a/Ryujinx.Core/OsHle/Services/Hid/ServiceHid.cs b/Ryujinx.Core/OsHle/Services/Hid/ServiceHid.cs index b1f930725f..d7b53fc9c8 100644 --- a/Ryujinx.Core/OsHle/Services/Hid/ServiceHid.cs +++ b/Ryujinx.Core/OsHle/Services/Hid/ServiceHid.cs @@ -1,5 +1,6 @@ using Ryujinx.Core.OsHle.Ipc; using System.Collections.Generic; +using Ryujinx.Core.Input; using static Ryujinx.Core.OsHle.IpcServices.ObjHelper; @@ -15,17 +16,20 @@ namespace Ryujinx.Core.OsHle.IpcServices.Hid { m_Commands = new Dictionary() { - { 0, CreateAppletResource }, - { 11, ActivateTouchScreen }, - { 100, SetSupportedNpadStyleSet }, - { 101, GetSupportedNpadStyleSet }, - { 102, SetSupportedNpadIdType }, - { 103, ActivateNpad }, - { 120, SetNpadJoyHoldType }, - { 121, GetNpadJoyHoldType }, - { 200, GetVibrationDeviceInfo }, - { 203, CreateActiveVibrationDeviceList }, - { 206, SendVibrationValues } + { 0, CreateAppletResource }, + { 11, ActivateTouchScreen }, + { 100, SetSupportedNpadStyleSet }, + { 101, GetSupportedNpadStyleSet }, + { 102, SetSupportedNpadIdType }, + { 103, ActivateNpad }, + { 120, SetNpadJoyHoldType }, + { 122, SetNpadJoyAssignmentModeSingleByDefault }, + { 123, SetNpadJoyAssignmentModeSingle }, + { 124, SetNpadJoyAssignmentModeDual }, + { 125, MergeSingleJoyAsDualJoy }, + { 200, GetVibrationDeviceInfo }, + { 203, CreateActiveVibrationDeviceList }, + { 206, SendVibrationValues } }; } @@ -87,6 +91,40 @@ namespace Ryujinx.Core.OsHle.IpcServices.Hid return 0; } + public long SetNpadJoyAssignmentModeSingleByDefault(ServiceCtx Context) + { + HidControllerId HidControllerId = (HidControllerId)Context.RequestData.ReadInt32(); + long AppletUserResourseId = Context.RequestData.ReadInt64(); + + return 0; + } + + public long SetNpadJoyAssignmentModeSingle(ServiceCtx Context) + { + HidControllerId HidControllerId = (HidControllerId)Context.RequestData.ReadInt32(); + long AppletUserResourseId = Context.RequestData.ReadInt64(); + long NpadJoyDeviceType = Context.RequestData.ReadInt64(); + + return 0; + } + + public long SetNpadJoyAssignmentModeDual(ServiceCtx Context) + { + HidControllerId HidControllerId = (HidControllerId)Context.RequestData.ReadInt32(); + long AppletUserResourseId = Context.RequestData.ReadInt64(); + + return 0; + } + + public long MergeSingleJoyAsDualJoy(ServiceCtx Context) + { + long Unknown0 = Context.RequestData.ReadInt32(); + long Unknown8 = Context.RequestData.ReadInt32(); + long AppletUserResourseId = Context.RequestData.ReadInt64(); + + return 0; + } + public long GetVibrationDeviceInfo(ServiceCtx Context) { int VibrationDeviceHandle = Context.RequestData.ReadInt32(); From 2ed24af756f60391dade07ac371a2cf630628eb3 Mon Sep 17 00:00:00 2001 From: gdkchan Date: Tue, 13 Mar 2018 21:24:17 -0300 Subject: [PATCH 24/28] Add pl:u stub, use higher precision on CNTPCT_EL0 register tick count --- ChocolArm64/Instruction/AInstEmitSystem.cs | 1 + ChocolArm64/State/AThreadState.cs | 26 ++++++++++++++++--- Ryujinx.Core/OsHle/Process.cs | 3 +++ Ryujinx.Core/OsHle/Services/Pl/ServicePl.cs | 8 ++++++ .../OsHle/Services/Pl/SharedFontType.cs | 12 +++++++++ 5 files changed, 47 insertions(+), 3 deletions(-) create mode 100644 Ryujinx.Core/OsHle/Services/Pl/SharedFontType.cs diff --git a/ChocolArm64/Instruction/AInstEmitSystem.cs b/ChocolArm64/Instruction/AInstEmitSystem.cs index 42a62009b5..1c5d02634f 100644 --- a/ChocolArm64/Instruction/AInstEmitSystem.cs +++ b/ChocolArm64/Instruction/AInstEmitSystem.cs @@ -30,6 +30,7 @@ namespace ChocolArm64.Instruction case 0b11_011_0100_0100_001: PropName = nameof(AThreadState.Fpsr); break; case 0b11_011_1101_0000_010: PropName = nameof(AThreadState.TpidrEl0); break; case 0b11_011_1101_0000_011: PropName = nameof(AThreadState.Tpidr); break; + case 0b11_011_1110_0000_000: PropName = nameof(AThreadState.CntfrqEl0); break; case 0b11_011_1110_0000_001: PropName = nameof(AThreadState.CntpctEl0); break; default: throw new NotImplementedException($"Unknown MRS at {Op.Position:x16}"); diff --git a/ChocolArm64/State/AThreadState.cs b/ChocolArm64/State/AThreadState.cs index ec8621b89b..6f3f62f69f 100644 --- a/ChocolArm64/State/AThreadState.cs +++ b/ChocolArm64/State/AThreadState.cs @@ -1,5 +1,6 @@ using ChocolArm64.Events; using System; +using System.Diagnostics; namespace ChocolArm64.State { @@ -40,15 +41,34 @@ namespace ChocolArm64.State public uint CtrEl0 => 0x8444c004; public uint DczidEl0 => 0x00000004; - private const ulong TicksPerS = 19_200_000; - private const ulong TicksPerMS = TicksPerS / 1_000; + public ulong CntfrqEl0 { get; set; } + public ulong CntpctEl0 + { + get + { + double Ticks = TickCounter.ElapsedTicks * HostTickFreq; - public ulong CntpctEl0 => (ulong)Environment.TickCount * TicksPerMS; + return (ulong)(Ticks * CntfrqEl0); + } + } public event EventHandler Break; public event EventHandler SvcCall; public event EventHandler Undefined; + private static Stopwatch TickCounter; + + private static double HostTickFreq; + + static AThreadState() + { + HostTickFreq = 1.0 / Stopwatch.Frequency; + + TickCounter = new Stopwatch(); + + TickCounter.Start(); + } + internal void OnBreak(int Imm) { Break?.Invoke(this, new AInstExceptionEventArgs(Imm)); diff --git a/Ryujinx.Core/OsHle/Process.cs b/Ryujinx.Core/OsHle/Process.cs index a8719e1c29..239b198039 100644 --- a/Ryujinx.Core/OsHle/Process.cs +++ b/Ryujinx.Core/OsHle/Process.cs @@ -17,6 +17,8 @@ namespace Ryujinx.Core.OsHle private const int TlsSize = 0x200; private const int TotalTlsSlots = 32; + private const int TickFreq = 19_200_000; + private Switch Ns; public bool NeedsHbAbi { get; private set; } @@ -197,6 +199,7 @@ namespace Ryujinx.Core.OsHle Thread.ThreadState.Undefined += UndefinedHandler; Thread.ThreadState.ProcessId = ProcessId; Thread.ThreadState.ThreadId = ThreadId; + Thread.ThreadState.CntfrqEl0 = TickFreq; Thread.ThreadState.Tpidr = Tpidr; Thread.ThreadState.X0 = (ulong)ArgsPtr; Thread.ThreadState.X1 = (ulong)Handle; diff --git a/Ryujinx.Core/OsHle/Services/Pl/ServicePl.cs b/Ryujinx.Core/OsHle/Services/Pl/ServicePl.cs index bb795f3f49..9a61779934 100644 --- a/Ryujinx.Core/OsHle/Services/Pl/ServicePl.cs +++ b/Ryujinx.Core/OsHle/Services/Pl/ServicePl.cs @@ -13,6 +13,7 @@ namespace Ryujinx.Core.OsHle.IpcServices.Pl { m_Commands = new Dictionary() { + { 0, RequestLoad }, { 1, GetLoadState }, { 2, GetFontSize }, { 3, GetSharedMemoryAddressOffset }, @@ -20,6 +21,13 @@ namespace Ryujinx.Core.OsHle.IpcServices.Pl }; } + public long RequestLoad(ServiceCtx Context) + { + SharedFontType FontType = (SharedFontType)Context.RequestData.ReadInt32(); + + return 0; + } + public long GetLoadState(ServiceCtx Context) { Context.ResponseData.Write(1); //Loaded diff --git a/Ryujinx.Core/OsHle/Services/Pl/SharedFontType.cs b/Ryujinx.Core/OsHle/Services/Pl/SharedFontType.cs new file mode 100644 index 0000000000..29fe02b8a4 --- /dev/null +++ b/Ryujinx.Core/OsHle/Services/Pl/SharedFontType.cs @@ -0,0 +1,12 @@ +namespace Ryujinx.Core.OsHle.IpcServices.Pl +{ + enum SharedFontType + { + JapanUsEurope = 0, + SimplifiedChinese = 1, + SimplifiedChineseEx = 2, + TraditionalChinese = 3, + Korean = 4, + NintendoEx = 5 + } +} \ No newline at end of file From 19564e570baab98754cb808c704fbd9c924abc60 Mon Sep 17 00:00:00 2001 From: gdkchan Date: Tue, 13 Mar 2018 21:26:14 -0300 Subject: [PATCH 25/28] Stub StartSixAxisSensor --- Ryujinx.Core/OsHle/Services/Hid/ServiceHid.cs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/Ryujinx.Core/OsHle/Services/Hid/ServiceHid.cs b/Ryujinx.Core/OsHle/Services/Hid/ServiceHid.cs index d7b53fc9c8..6735c6ad2c 100644 --- a/Ryujinx.Core/OsHle/Services/Hid/ServiceHid.cs +++ b/Ryujinx.Core/OsHle/Services/Hid/ServiceHid.cs @@ -18,6 +18,7 @@ namespace Ryujinx.Core.OsHle.IpcServices.Hid { { 0, CreateAppletResource }, { 11, ActivateTouchScreen }, + { 66, StartSixAxisSensor }, { 100, SetSupportedNpadStyleSet }, { 101, GetSupportedNpadStyleSet }, { 102, SetSupportedNpadIdType }, @@ -47,6 +48,15 @@ namespace Ryujinx.Core.OsHle.IpcServices.Hid return 0; } + public long StartSixAxisSensor(ServiceCtx Context) + { + int Handle = Context.RequestData.ReadInt32(); + + long AppletResourceUserId = Context.RequestData.ReadInt64(); + + return 0; + } + public long GetSupportedNpadStyleSet(ServiceCtx Context) { Context.ResponseData.Write(0); From 553ba659c40013cde0198c2bdd10b31bdd6f3d97 Mon Sep 17 00:00:00 2001 From: gdkchan Date: Wed, 14 Mar 2018 00:12:05 -0300 Subject: [PATCH 26/28] Add CRC32 instruction and SLI (vector) --- ChocolArm64/AOpCodeTable.cs | 9 +++ ChocolArm64/Instruction/AInstEmitHash.cs | 74 ++++++++++++++++++ ChocolArm64/Instruction/AInstEmitSimdShift.cs | 54 ++++++++++--- ChocolArm64/Instruction/ASoftFallback.cs | 78 +++++++++++++++++++ 4 files changed, 205 insertions(+), 10 deletions(-) create mode 100644 ChocolArm64/Instruction/AInstEmitHash.cs diff --git a/ChocolArm64/AOpCodeTable.cs b/ChocolArm64/AOpCodeTable.cs index 60646d0ee5..d32bd9cd24 100644 --- a/ChocolArm64/AOpCodeTable.cs +++ b/ChocolArm64/AOpCodeTable.cs @@ -42,6 +42,14 @@ namespace ChocolArm64 Set("x1111010010xxxxxxxxx00xxxxxxxxxx", AInstEmit.Ccmp, typeof(AOpCodeCcmpReg)); Set("11010101000000110011xxxx01011111", AInstEmit.Clrex, typeof(AOpCodeSystem)); Set("x101101011000000000100xxxxxxxxxx", AInstEmit.Clz, typeof(AOpCodeAlu)); + Set("x0011010110xxxxx010000xxxxxxxxxx", AInstEmit.Crc32b, typeof(AOpCodeAluRs)); + Set("x0011010110xxxxx010001xxxxxxxxxx", AInstEmit.Crc32h, typeof(AOpCodeAluRs)); + Set("x0011010110xxxxx010010xxxxxxxxxx", AInstEmit.Crc32w, typeof(AOpCodeAluRs)); + Set("x0011010110xxxxx010011xxxxxxxxxx", AInstEmit.Crc32x, typeof(AOpCodeAluRs)); + Set("x0011010110xxxxx010100xxxxxxxxxx", AInstEmit.Crc32cb, typeof(AOpCodeAluRs)); + Set("x0011010110xxxxx010101xxxxxxxxxx", AInstEmit.Crc32ch, typeof(AOpCodeAluRs)); + Set("x0011010110xxxxx010110xxxxxxxxxx", AInstEmit.Crc32cw, typeof(AOpCodeAluRs)); + Set("x0011010110xxxxx010111xxxxxxxxxx", AInstEmit.Crc32cx, typeof(AOpCodeAluRs)); Set("x0011010100xxxxxxxxx00xxxxxxxxxx", AInstEmit.Csel, typeof(AOpCodeCsel)); Set("x0011010100xxxxxxxxx01xxxxxxxxxx", AInstEmit.Csinc, typeof(AOpCodeCsel)); Set("x1011010100xxxxxxxxx00xxxxxxxxxx", AInstEmit.Csinv, typeof(AOpCodeCsel)); @@ -243,6 +251,7 @@ namespace ChocolArm64 Set("0x0011110>>>>xxx010101xxxxxxxxxx", AInstEmit.Shl_V, typeof(AOpCodeSimdShImm)); Set("0x101110<<100001001110xxxxxxxxxx", AInstEmit.Shll_V, typeof(AOpCodeSimd)); Set("0x00111100>>>xxx100001xxxxxxxxxx", AInstEmit.Shrn_V, typeof(AOpCodeSimdShImm)); + Set("0x1011110>>>>xxx010101xxxxxxxxxx", AInstEmit.Sli_V, typeof(AOpCodeSimdShImm)); Set("0x001110<<1xxxxx011001xxxxxxxxxx", AInstEmit.Smax_V, typeof(AOpCodeSimdReg)); Set("0x001110<<1xxxxx011011xxxxxxxxxx", AInstEmit.Smin_V, typeof(AOpCodeSimdReg)); Set("0x001110<<1xxxxx100000xxxxxxxxxx", AInstEmit.Smlal_V, typeof(AOpCodeSimdReg)); diff --git a/ChocolArm64/Instruction/AInstEmitHash.cs b/ChocolArm64/Instruction/AInstEmitHash.cs new file mode 100644 index 0000000000..fd98f5632a --- /dev/null +++ b/ChocolArm64/Instruction/AInstEmitHash.cs @@ -0,0 +1,74 @@ +using ChocolArm64.Decoder; +using ChocolArm64.State; +using ChocolArm64.Translation; +using System.Reflection.Emit; + +namespace ChocolArm64.Instruction +{ + static partial class AInstEmit + { + + public static void Crc32b(AILEmitterCtx Context) + { + EmitCrc32(Context, nameof(ASoftFallback.Crc32b)); + } + + public static void Crc32h(AILEmitterCtx Context) + { + EmitCrc32(Context, nameof(ASoftFallback.Crc32h)); + } + + public static void Crc32w(AILEmitterCtx Context) + { + EmitCrc32(Context, nameof(ASoftFallback.Crc32w)); + } + + public static void Crc32x(AILEmitterCtx Context) + { + EmitCrc32(Context, nameof(ASoftFallback.Crc32x)); + } + + public static void Crc32cb(AILEmitterCtx Context) + { + EmitCrc32(Context, nameof(ASoftFallback.Crc32cb)); + } + + public static void Crc32ch(AILEmitterCtx Context) + { + EmitCrc32(Context, nameof(ASoftFallback.Crc32ch)); + } + + public static void Crc32cw(AILEmitterCtx Context) + { + EmitCrc32(Context, nameof(ASoftFallback.Crc32cw)); + } + + public static void Crc32cx(AILEmitterCtx Context) + { + EmitCrc32(Context, nameof(ASoftFallback.Crc32cx)); + } + + private static void EmitCrc32(AILEmitterCtx Context, string Name) + { + AOpCodeAluRs Op = (AOpCodeAluRs)Context.CurrOp; + + Context.EmitLdintzr(Op.Rn); + + if (Op.RegisterSize != ARegisterSize.Int32) + { + Context.Emit(OpCodes.Conv_U4); + } + + Context.EmitLdintzr(Op.Rm); + + ASoftFallback.EmitCall(Context, Name); + + if (Op.RegisterSize != ARegisterSize.Int32) + { + Context.Emit(OpCodes.Conv_U8); + } + + Context.EmitStintzr(Op.Rd); + } + } +} \ No newline at end of file diff --git a/ChocolArm64/Instruction/AInstEmitSimdShift.cs b/ChocolArm64/Instruction/AInstEmitSimdShift.cs index bb8a8f178b..bffed57ed4 100644 --- a/ChocolArm64/Instruction/AInstEmitSimdShift.cs +++ b/ChocolArm64/Instruction/AInstEmitSimdShift.cs @@ -29,7 +29,7 @@ namespace ChocolArm64.Instruction int Shift = Op.Imm - (8 << Op.Size); - EmitVectorBinaryShImmBinaryZx(Context, () => Context.Emit(OpCodes.Shl), Shift); + EmitVectorShImmBinaryZx(Context, () => Context.Emit(OpCodes.Shl), Shift); } public static void Shll_V(AILEmitterCtx Context) @@ -50,6 +50,40 @@ namespace ChocolArm64.Instruction EmitVectorShImmNarrowBinaryZx(Context, () => Context.Emit(OpCodes.Shr_Un), Shift); } + public static void Sli_V(AILEmitterCtx Context) + { + AOpCodeSimdShImm Op = (AOpCodeSimdShImm)Context.CurrOp; + + int Bytes = Context.CurrOp.GetBitsCount() >> 3; + + int Shift = Op.Imm - (8 << Op.Size); + + ulong Mask = ulong.MaxValue >> (64 - Shift); + + for (int Index = 0; Index < (Bytes >> Op.Size); Index++) + { + EmitVectorExtractZx(Context, Op.Rn, Index, Op.Size); + + Context.EmitLdc_I4(Shift); + + Context.Emit(OpCodes.Shl); + + EmitVectorExtractZx(Context, Op.Rd, Index, Op.Size); + + Context.EmitLdc_I8((long)Mask); + + Context.Emit(OpCodes.And); + Context.Emit(OpCodes.Or); + + EmitVectorInsert(Context, Op.Rd, Index, Op.Size); + } + + if (Op.RegisterSize == ARegisterSize.SIMD64) + { + EmitVectorZeroUpper(Context, Op.Rd); + } + } + public static void Sshl_V(AILEmitterCtx Context) { EmitVectorShl(Context, Signed: true); @@ -83,7 +117,7 @@ namespace ChocolArm64.Instruction int Shift = (8 << (Op.Size + 1)) - Op.Imm; - EmitVectorBinaryShImmBinarySx(Context, () => Context.Emit(OpCodes.Shr), Shift); + EmitVectorShImmBinarySx(Context, () => Context.Emit(OpCodes.Shr), Shift); } public static void Ssra_V(AILEmitterCtx Context) @@ -98,7 +132,7 @@ namespace ChocolArm64.Instruction Context.Emit(OpCodes.Add); }; - EmitVectorTernaryShImmBinarySx(Context, Emit, Shift); + EmitVectorShImmTernarySx(Context, Emit, Shift); } public static void Ushl_V(AILEmitterCtx Context) @@ -217,22 +251,22 @@ namespace ChocolArm64.Instruction } } - private static void EmitVectorBinaryShImmBinarySx(AILEmitterCtx Context, Action Emit, int Imm) + private static void EmitVectorShImmBinarySx(AILEmitterCtx Context, Action Emit, int Imm) { - EmitVectorShImmBinaryOp(Context, Emit, Imm, false, true); + EmitVectorShImmOp(Context, Emit, Imm, false, true); } - private static void EmitVectorTernaryShImmBinarySx(AILEmitterCtx Context, Action Emit, int Imm) + private static void EmitVectorShImmTernarySx(AILEmitterCtx Context, Action Emit, int Imm) { - EmitVectorShImmBinaryOp(Context, Emit, Imm, true, true); + EmitVectorShImmOp(Context, Emit, Imm, true, true); } - private static void EmitVectorBinaryShImmBinaryZx(AILEmitterCtx Context, Action Emit, int Imm) + private static void EmitVectorShImmBinaryZx(AILEmitterCtx Context, Action Emit, int Imm) { - EmitVectorShImmBinaryOp(Context, Emit, Imm, false, false); + EmitVectorShImmOp(Context, Emit, Imm, false, false); } - private static void EmitVectorShImmBinaryOp(AILEmitterCtx Context, Action Emit, int Imm, bool Ternary, bool Signed) + private static void EmitVectorShImmOp(AILEmitterCtx Context, Action Emit, int Imm, bool Ternary, bool Signed) { AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp; diff --git a/ChocolArm64/Instruction/ASoftFallback.cs b/ChocolArm64/Instruction/ASoftFallback.cs index 797d815736..7e5b3dba27 100644 --- a/ChocolArm64/Instruction/ASoftFallback.cs +++ b/ChocolArm64/Instruction/ASoftFallback.cs @@ -38,6 +38,84 @@ namespace ChocolArm64.Instruction return (ulong)Size; } + private const uint Crc32RevPoly = 0xedb88320; + private const uint Crc32cRevPoly = 0x82f63b78; + + public static uint Crc32b(uint Crc, byte Val) => Crc32 (Crc, Crc32RevPoly, Val); + public static uint Crc32h(uint Crc, byte Val) => Crc32h(Crc, Crc32RevPoly, Val); + public static uint Crc32w(uint Crc, byte Val) => Crc32w(Crc, Crc32RevPoly, Val); + public static uint Crc32x(uint Crc, byte Val) => Crc32x(Crc, Crc32RevPoly, Val); + + public static uint Crc32cb(uint Crc, byte Val) => Crc32 (Crc, Crc32cRevPoly, Val); + public static uint Crc32ch(uint Crc, byte Val) => Crc32h(Crc, Crc32cRevPoly, Val); + public static uint Crc32cw(uint Crc, byte Val) => Crc32w(Crc, Crc32cRevPoly, Val); + public static uint Crc32cx(uint Crc, byte Val) => Crc32x(Crc, Crc32cRevPoly, Val); + + private static uint Crc32h(uint Crc, uint Poly, ushort Val) + { + Crc = Crc32(Crc, Poly, (byte)(Val >> 0)); + Crc = Crc32(Crc, Poly, (byte)(Val >> 8)); + + return Crc; + } + + private static uint Crc32w(uint Crc, uint Poly, uint Val) + { + Crc = Crc32(Crc, Poly, (byte)(Val >> 0)); + Crc = Crc32(Crc, Poly, (byte)(Val >> 8)); + Crc = Crc32(Crc, Poly, (byte)(Val >> 16)); + Crc = Crc32(Crc, Poly, (byte)(Val >> 24)); + + return Crc; + } + + private static uint Crc32x(uint Crc, uint Poly, ulong Val) + { + Crc = Crc32(Crc, Poly, (byte)(Val >> 0)); + Crc = Crc32(Crc, Poly, (byte)(Val >> 8)); + Crc = Crc32(Crc, Poly, (byte)(Val >> 16)); + Crc = Crc32(Crc, Poly, (byte)(Val >> 24)); + Crc = Crc32(Crc, Poly, (byte)(Val >> 32)); + Crc = Crc32(Crc, Poly, (byte)(Val >> 40)); + Crc = Crc32(Crc, Poly, (byte)(Val >> 48)); + Crc = Crc32(Crc, Poly, (byte)(Val >> 56)); + + return Crc; + } + + private static uint Crc32(uint Crc, uint Poly, byte Val) + { + Crc ^= Val; + + for (int Bit = 7; Bit >= 0; Bit--) + { + uint Mask = (uint)(-(int)(Crc & 1)); + + Crc = (Crc >> 1) ^ (Poly & Mask); + } + + return Crc; + } + + public static uint ReverseBits8(uint Value) + { + Value = ((Value & 0xaa) >> 1) | ((Value & 0x55) << 1); + Value = ((Value & 0xcc) >> 2) | ((Value & 0x33) << 2); + Value = ((Value & 0xf0) >> 4) | ((Value & 0x0f) << 4); + + return Value; + } + + public static uint ReverseBits16(uint Value) + { + Value = ((Value & 0xaaaa) >> 1) | ((Value & 0x5555) << 1); + Value = ((Value & 0xcccc) >> 2) | ((Value & 0x3333) << 2); + Value = ((Value & 0xf0f0) >> 4) | ((Value & 0x0f0f) << 4); + Value = ((Value & 0xff00) >> 8) | ((Value & 0x00ff) << 8); + + return Value; + } + public static uint ReverseBits32(uint Value) { Value = ((Value & 0xaaaaaaaa) >> 1) | ((Value & 0x55555555) << 1); From d067b4d5e0e7860702bb9a10c832a46d15176fd8 Mon Sep 17 00:00:00 2001 From: gdkchan Date: Wed, 14 Mar 2018 00:57:07 -0300 Subject: [PATCH 27/28] Remove unused function from CPU --- ChocolArm64/Instruction/AInstEmitHash.cs | 1 - ChocolArm64/Instruction/ASoftFallback.cs | 19 ------------------- 2 files changed, 20 deletions(-) diff --git a/ChocolArm64/Instruction/AInstEmitHash.cs b/ChocolArm64/Instruction/AInstEmitHash.cs index fd98f5632a..94e03f6c1f 100644 --- a/ChocolArm64/Instruction/AInstEmitHash.cs +++ b/ChocolArm64/Instruction/AInstEmitHash.cs @@ -7,7 +7,6 @@ namespace ChocolArm64.Instruction { static partial class AInstEmit { - public static void Crc32b(AILEmitterCtx Context) { EmitCrc32(Context, nameof(ASoftFallback.Crc32b)); diff --git a/ChocolArm64/Instruction/ASoftFallback.cs b/ChocolArm64/Instruction/ASoftFallback.cs index 7e5b3dba27..f79628ad03 100644 --- a/ChocolArm64/Instruction/ASoftFallback.cs +++ b/ChocolArm64/Instruction/ASoftFallback.cs @@ -97,25 +97,6 @@ namespace ChocolArm64.Instruction return Crc; } - public static uint ReverseBits8(uint Value) - { - Value = ((Value & 0xaa) >> 1) | ((Value & 0x55) << 1); - Value = ((Value & 0xcc) >> 2) | ((Value & 0x33) << 2); - Value = ((Value & 0xf0) >> 4) | ((Value & 0x0f) << 4); - - return Value; - } - - public static uint ReverseBits16(uint Value) - { - Value = ((Value & 0xaaaa) >> 1) | ((Value & 0x5555) << 1); - Value = ((Value & 0xcccc) >> 2) | ((Value & 0x3333) << 2); - Value = ((Value & 0xf0f0) >> 4) | ((Value & 0x0f0f) << 4); - Value = ((Value & 0xff00) >> 8) | ((Value & 0x00ff) << 8); - - return Value; - } - public static uint ReverseBits32(uint Value) { Value = ((Value & 0xaaaaaaaa) >> 1) | ((Value & 0x55555555) << 1); From b50bc46888cf9a8d94ae1590c0941be62a083533 Mon Sep 17 00:00:00 2001 From: gdkchan Date: Wed, 14 Mar 2018 01:59:22 -0300 Subject: [PATCH 28/28] CPU fix for the cases using a Mask with shift = 0 --- ChocolArm64/Decoder/AOpCodeSimdImm.cs | 9 ++++++++- ChocolArm64/Instruction/AInstEmitSimdShift.cs | 2 +- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/ChocolArm64/Decoder/AOpCodeSimdImm.cs b/ChocolArm64/Decoder/AOpCodeSimdImm.cs index 2959aee6de..e7dfe62114 100644 --- a/ChocolArm64/Decoder/AOpCodeSimdImm.cs +++ b/ChocolArm64/Decoder/AOpCodeSimdImm.cs @@ -88,7 +88,14 @@ namespace ChocolArm64.Decoder private static long ShlOnes(long Value, int Shift) { - return Value << Shift | (long)(ulong.MaxValue >> (64 - Shift)); + if (Shift != 0) + { + return Value << Shift | (long)(ulong.MaxValue >> (64 - Shift)); + } + else + { + return Value; + } } } } \ No newline at end of file diff --git a/ChocolArm64/Instruction/AInstEmitSimdShift.cs b/ChocolArm64/Instruction/AInstEmitSimdShift.cs index bffed57ed4..24d35abe4c 100644 --- a/ChocolArm64/Instruction/AInstEmitSimdShift.cs +++ b/ChocolArm64/Instruction/AInstEmitSimdShift.cs @@ -58,7 +58,7 @@ namespace ChocolArm64.Instruction int Shift = Op.Imm - (8 << Op.Size); - ulong Mask = ulong.MaxValue >> (64 - Shift); + ulong Mask = Shift != 0 ? ulong.MaxValue >> (64 - Shift) : 0; for (int Index = 0; Index < (Bytes >> Op.Size); Index++) {