diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 000000000..9c6ca236b --- /dev/null +++ b/.travis.yml @@ -0,0 +1,9 @@ +os: osx +language: csharp +solution: Ryujinx.sln +mono: none +dotnet: 2.0.0 +script: + - dotnet restore + - dotnet build + - cd Ryujinx.Tests && dotnet test diff --git a/CONFIG.md b/CONFIG.md new file mode 100644 index 000000000..09fa13b7b --- /dev/null +++ b/CONFIG.md @@ -0,0 +1,75 @@ +## Config File + +`Ryujinx.conf` should be present in executable folder (It's an *.ini file) following this format: + +- `Logging_Enable_Info` *(bool)* + + Enable the Informations Logging. + +- `Logging_Enable_Trace` *(bool)* + + Enable the Trace Logging (Enabled in Debug recommanded). + +- `Logging_Enable_Debug` *(bool)* + + Enable the Debug Logging (Enabled in Debug recommanded). + +- `Logging_Enable_Warn` *(bool)* + + Enable the Warning Logging (Enabled in Debug recommanded). + +- `Logging_Enable_Error` *(bool)* + + Enable the Error Logging (Enabled in Debug recommanded). + +- `Logging_Enable_Fatal` *(bool)* + + Enable the Fatal Logging (Enabled in Debug recommanded). + +- `Logging_Enable_LogFile` *(bool)* + + Enable writing the logging inside a Ryujinx.log file. + +- `Controls_Left_FakeJoycon_XX` *(int)* + ``` + Controls_Left_FakeJoycon_Stick_Up (int) + Controls_Left_FakeJoycon_Stick_Down (int) + Controls_Left_FakeJoycon_Stick_Left (int) + Controls_Left_FakeJoycon_Stick_Right (int) + Controls_Left_FakeJoycon_Stick_Button (int) + Controls_Left_FakeJoycon_DPad_Up (int) + Controls_Left_FakeJoycon_DPad_Down (int) + Controls_Left_FakeJoycon_DPad_Left (int) + Controls_Left_FakeJoycon_DPad_Right (int) + Controls_Left_FakeJoycon_Button_Minus (int) + Controls_Left_FakeJoycon_Button_L (int) + Controls_Left_FakeJoycon_Button_ZL (int) + ``` + + Keys of the Left Emulated Joycon, the values depend of the [OpenTK Enum Keys](https://github.com/opentk/opentk/blob/develop/src/OpenTK/Input/Key.cs). + + OpenTK use a QWERTY layout, so pay attention if you use another Keyboard Layout. + + Ex: `Controls_Left_FakeJoycon_Button_Minus = 52` > Tab key (All Layout). + +- `Controls_Right_FakeJoycon_XX` *(int)* + ``` + Controls_Right_FakeJoycon_Stick_Up (int) + Controls_Right_FakeJoycon_Stick_Down (int) + Controls_Right_FakeJoycon_Stick_Left (int) + Controls_Right_FakeJoycon_Stick_Right (int) + Controls_Right_FakeJoycon_Stick_Button (int) + Controls_Right_FakeJoycon_Button_A (int) + Controls_Right_FakeJoycon_Button_B (int) + Controls_Right_FakeJoycon_Button_X (int) + Controls_Right_FakeJoycon_Button_Y (int) + Controls_Right_FakeJoycon_Button_Plus (int) + Controls_Right_FakeJoycon_Button_R (int) + Controls_Right_FakeJoycon_Button_ZR (int) + ``` + + Keys of the right Emulated Joycon, the values depend of the [OpenTK Enum Keys](https://github.com/opentk/opentk/blob/develop/src/OpenTK/Input/Key.cs). + + OpenTK use a QWERTY layout, so pay attention if you use another Keyboard Layout. + + Ex: `Controls_Right_FakeJoycon_Button_A = 83` > A key (QWERTY Layout) / Q key (AZERTY Layout). diff --git a/README.md b/README.md index 8f8be0544..f39d2f029 100644 --- a/README.md +++ b/README.md @@ -21,37 +21,23 @@ https://openal.org/downloads/OpenAL11CoreSDK.zip - Arrows. - Enter > "Start" & Tab > "Select" - Qwerty: - - A > "A" + - A > "A" (QWERTY) / Q > "A" (AZERTY) - S > "B" - - Z > "X" + - Z > "X" (QWERTY) / W > "X" (AZERTY) - X > "Y" - - Azerty: - - Q > "A" - - S > "B" - - W > "X" - - X > "Y" + - Key_Up > "Right Stick Up" + - Key_Down > "Right Stick Down" + - Key_Left > "Right Stick Left" + - Key_Right > "Right Stick Right" + - I > "Left Stick Up" + - K > "Left Stick Down" + - J > "Left Stick Left" + - L > "Left Stick Right" + - Tab > "Minus" + - Enter > "Plus" - - Config File: `Ryujinx.conf` should be present on executable folder. - - Logging_Enable_Info (bool) - Enable the Informations Logging. - - - Logging_Enable_Trace (bool) - Enable the Trace Logging (Enabled in Debug recommanded). - - - Logging_Enable_Debug (bool) - Enable the Debug Logging (Enabled in Debug recommanded). - - - Logging_Enable_Warn (bool) - Enable the Warning Logging (Enabled in Debug recommanded). - - - Logging_Enable_Error (bool) - Enable the Error Logging (Enabled in Debug recommanded). - - - Logging_Enable_Fatal (bool) - Enable the Fatal Logging (Enabled in Debug recommanded). - - - Logging_Enable_LogFile (bool) - Enable writing the logging inside a Ryujinx.log file. + - Config File: `Ryujinx.conf` should be present in executable folder. + For more informations [you can go here](CONFIG.md). **Help** @@ -68,7 +54,7 @@ To run this emulator, you need the .NET Core 2.0 (or higher) SDK. Run `dotnet run -c Release -- path\to\homebrew.nro` inside the Ryujinx solution folder to run homebrew apps. Run `dotnet run -c Release -- path\to\game_exefs_and_romfs_folder` to run official games (they need to be decrypted and extracted first!). -Audio is partially supported (glitched) on Windows, you need to install the OpenAL Core SDK: +Audio is partially supported (glitched) on Linux and macOS, for Windows you need to install the OpenAL Core SDK: https://openal.org/downloads/OpenAL11CoreSDK.zip **Lastest build** diff --git a/Ryujinx.Tests/Cpu/CpuTest.cs b/Ryujinx.Tests/Cpu/CpuTest.cs new file mode 100644 index 000000000..0f1e64134 --- /dev/null +++ b/Ryujinx.Tests/Cpu/CpuTest.cs @@ -0,0 +1,73 @@ +using ChocolArm64; +using ChocolArm64.Memory; +using ChocolArm64.State; +using NUnit.Framework; +using System; +using System.Runtime.InteropServices; +using System.Threading; + +namespace Ryujinx.Tests.Cpu +{ + [TestFixture] + public partial class CpuTest + { + IntPtr Ram; + AMemoryAlloc Allocator; + AMemory Memory; + + [SetUp] + public void Setup() + { + Ram = Marshal.AllocHGlobal((IntPtr)AMemoryMgr.RamSize); + Allocator = new AMemoryAlloc(); + Memory = new AMemory(Ram, Allocator); + Memory.Manager.MapPhys(0x1000, 0x1000, 2, AMemoryPerm.Read | AMemoryPerm.Write | AMemoryPerm.Execute); + } + + [TearDown] + public void Teardown() + { + Marshal.FreeHGlobal(Ram); + } + + private void Execute(AThread Thread) + { + AutoResetEvent Wait = new AutoResetEvent(false); + Thread.ThreadState.Break += (sender, e) => Thread.StopExecution(); + Thread.WorkFinished += (sender, e) => Wait.Set(); + + Wait.Reset(); + Thread.Execute(); + Wait.WaitOne(); + } + + private AThreadState SingleOpcode(uint Opcode, + ulong X0 = 0, ulong X1 = 0, ulong X2 = 0, + AVec V0 = new AVec(), AVec V1 = new AVec(), AVec V2 = new AVec()) + { + Memory.WriteUInt32(0x1000, Opcode); + Memory.WriteUInt32(0x1004, 0xD4200000); // BRK #0 + Memory.WriteUInt32(0x1008, 0xD65F03C0); // RET + + AThread Thread = new AThread(Memory, ThreadPriority.Normal, 0x1000); + Thread.ThreadState.X0 = X0; + Thread.ThreadState.X1 = X1; + Thread.ThreadState.X2 = X2; + Thread.ThreadState.V0 = V0; + Thread.ThreadState.V1 = V1; + Thread.ThreadState.V2 = V2; + Execute(Thread); + return Thread.ThreadState; + } + + [Test] + public void SanityCheck() + { + uint Opcode = 0xD503201F; // NOP + Assert.AreEqual(SingleOpcode(Opcode, X0: 0).X0, 0); + Assert.AreEqual(SingleOpcode(Opcode, X0: 1).X0, 1); + Assert.AreEqual(SingleOpcode(Opcode, X0: 2).X0, 2); + Assert.AreEqual(SingleOpcode(Opcode, X0: 42).X0, 42); + } + } +} diff --git a/Ryujinx.Tests/Cpu/CpuTestAlu.cs b/Ryujinx.Tests/Cpu/CpuTestAlu.cs new file mode 100644 index 000000000..a05c0b199 --- /dev/null +++ b/Ryujinx.Tests/Cpu/CpuTestAlu.cs @@ -0,0 +1,65 @@ +using ChocolArm64.State; +using NUnit.Framework; + +namespace Ryujinx.Tests.Cpu +{ + [TestFixture] + public partial class CpuTest + { + [Test] + public void Add() + { + // ADD X0, X1, X2 + AThreadState ThreadState = SingleOpcode(0x8B020020, X1: 1, X2: 2); + Assert.AreEqual(3, ThreadState.X0); + } + + [Test] + public void Ands() + { + // ANDS W0, W1, W2 + uint Opcode = 0x6A020020; + var tests = new[] + { + new { W1 = 0xFFFFFFFFul, W2 = 0xFFFFFFFFul, Result = 0xFFFFFFFFul, Negative = true, Zero = false }, + new { W1 = 0xFFFFFFFFul, W2 = 0x00000000ul, Result = 0x00000000ul, Negative = false, Zero = true }, + new { W1 = 0x12345678ul, W2 = 0x7324A993ul, Result = 0x12240010ul, Negative = false, Zero = false }, + }; + + foreach (var test in tests) + { + AThreadState ThreadState = SingleOpcode(Opcode, X1: test.W1, X2: test.W2); + Assert.AreEqual(test.Result, ThreadState.X0); + Assert.AreEqual(test.Negative, ThreadState.Negative); + Assert.AreEqual(test.Zero, ThreadState.Zero); + } + } + + [Test] + public void OrrBitmasks() + { + // ORR W0, WZR, #0x01010101 + Assert.AreEqual(0x01010101, SingleOpcode(0x3200C3E0).X0); + // ORR W1, WZR, #0x00F000F0 + Assert.AreEqual(0x00F000F0, SingleOpcode(0x320C8FE1).X1); + // ORR W2, WZR, #1 + Assert.AreEqual(0x00000001, SingleOpcode(0x320003E2).X2); + } + + [Test] + public void RevX0X0() + { + // REV X0, X0 + AThreadState ThreadState = SingleOpcode(0xDAC00C00, X0: 0xAABBCCDDEEFF1100); + Assert.AreEqual(0x0011FFEEDDCCBBAA, ThreadState.X0); + } + + [Test] + public void RevW1W1() + { + // REV W1, W1 + AThreadState ThreadState = SingleOpcode(0x5AC00821, X1: 0x12345678); + Assert.AreEqual(0x78563412, ThreadState.X1); + } + } +} diff --git a/Ryujinx.Tests/Cpu/CpuTestScalar.cs b/Ryujinx.Tests/Cpu/CpuTestScalar.cs new file mode 100644 index 000000000..26549b872 --- /dev/null +++ b/Ryujinx.Tests/Cpu/CpuTestScalar.cs @@ -0,0 +1,30 @@ +using ChocolArm64.State; +using NUnit.Framework; + +namespace Ryujinx.Tests.Cpu +{ + [TestFixture] + public partial class CpuTest + { + [TestCase(0x00000000u, 0x80000000u, 0x00000000u)] + [TestCase(0x80000000u, 0x00000000u, 0x00000000u)] + [TestCase(0x80000000u, 0x80000000u, 0x80000000u)] + [TestCase(0x3DCCCCCDu, 0x3C9623B1u, 0x3DCCCCCDu)] + [TestCase(0x8BA98D27u, 0x00000076u, 0x00000076u)] + [TestCase(0x807FFFFFu, 0x7F7FFFFFu, 0x7F7FFFFFu)] + [TestCase(0x7F7FFFFFu, 0x807FFFFFu, 0x7F7FFFFFu)] + [TestCase(0x7FC00000u, 0x3F800000u, 0x7FC00000u)] + [TestCase(0x3F800000u, 0x7FC00000u, 0x7FC00000u)] + // NaN tests + //[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 + uint Opcode = 0x1E224820; + AThreadState ThreadState = SingleOpcode(Opcode, V1: new AVec { W0 = A }, V2: new AVec { W0 = B }); + Assert.AreEqual(Result, ThreadState.V0.W0); + } + } +} diff --git a/Ryujinx.Tests/Ryujinx.Tests.csproj b/Ryujinx.Tests/Ryujinx.Tests.csproj new file mode 100644 index 000000000..f8b430e62 --- /dev/null +++ b/Ryujinx.Tests/Ryujinx.Tests.csproj @@ -0,0 +1,17 @@ + + + netcoreapp2.0 + false + + + false + + + + + + + + + + diff --git a/Ryujinx.conf b/Ryujinx.conf deleted file mode 100644 index ee21ad904..000000000 --- a/Ryujinx.conf +++ /dev/null @@ -1,20 +0,0 @@ -#Enabled print informations logs -Logging_Enable_Info = true - -#Enabled print trace logs -Logging_Enable_Trace = false - -#Enabled print debug logs -Logging_Enable_Debug = false - -#Enabled print warning logs -Logging_Enable_Warn = true - -#Enabled print error logs -Logging_Enable_Error = true - -#Enabled print fatal logs -Logging_Enable_Fatal = true - -#Saved logs into Ryujinx.log -Logging_Enable_LogFile = false diff --git a/Ryujinx.sln b/Ryujinx.sln index c75364f71..777539885 100644 --- a/Ryujinx.sln +++ b/Ryujinx.sln @@ -3,7 +3,9 @@ Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 15 VisualStudioVersion = 15.0.26730.8 MinimumVisualStudioVersion = 10.0.40219.1 -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx", "Ryujinx.csproj", "{074045D4-3ED2-4711-9169-E385F2BFB5A0}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ryujinx", "Ryujinx\Ryujinx.csproj", "{074045D4-3ED2-4711-9169-E385F2BFB5A0}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ryujinx.Tests", "Ryujinx.Tests\Ryujinx.Tests.csproj", "{EBB55AEA-C7D7-4DEB-BF96-FA1789E225E9}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -15,6 +17,10 @@ Global {074045D4-3ED2-4711-9169-E385F2BFB5A0}.Debug|Any CPU.Build.0 = Debug|Any CPU {074045D4-3ED2-4711-9169-E385F2BFB5A0}.Release|Any CPU.ActiveCfg = Release|Any CPU {074045D4-3ED2-4711-9169-E385F2BFB5A0}.Release|Any CPU.Build.0 = Release|Any CPU + {EBB55AEA-C7D7-4DEB-BF96-FA1789E225E9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {EBB55AEA-C7D7-4DEB-BF96-FA1789E225E9}.Debug|Any CPU.Build.0 = Debug|Any CPU + {EBB55AEA-C7D7-4DEB-BF96-FA1789E225E9}.Release|Any CPU.ActiveCfg = Release|Any CPU + {EBB55AEA-C7D7-4DEB-BF96-FA1789E225E9}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/Ryujinx/Config.cs b/Ryujinx/Config.cs index c27060d2c..e211441fb 100644 --- a/Ryujinx/Config.cs +++ b/Ryujinx/Config.cs @@ -16,6 +16,8 @@ namespace Ryujinx public static bool LoggingEnableFatal { get; private set; } public static bool LoggingEnableLogFile { get; private set; } + public static JoyCon FakeJoyCon { get; private set; } + public static void Read() { IniParser Parser = new IniParser(Path.Combine(Path.GetDirectoryName(Assembly.GetEntryAssembly().Location), "Ryujinx.conf")); @@ -27,6 +29,41 @@ namespace Ryujinx LoggingEnableError = Convert.ToBoolean(Parser.Value("Logging_Enable_Error")); LoggingEnableFatal = Convert.ToBoolean(Parser.Value("Logging_Enable_Fatal")); LoggingEnableLogFile = Convert.ToBoolean(Parser.Value("Logging_Enable_LogFile")); + + FakeJoyCon = new JoyCon + { + Left = new JoyConLeft + { + StickUp = Convert.ToInt16(Parser.Value("Controls_Left_FakeJoycon_Stick_Up")), + StickDown = Convert.ToInt16(Parser.Value("Controls_Left_FakeJoycon_Stick_Down")), + StickLeft = Convert.ToInt16(Parser.Value("Controls_Left_FakeJoycon_Stick_Left")), + StickRight = Convert.ToInt16(Parser.Value("Controls_Left_FakeJoycon_Stick_Right")), + StickButton = Convert.ToInt16(Parser.Value("Controls_Left_FakeJoycon_Stick_Button")), + DPadUp = Convert.ToInt16(Parser.Value("Controls_Left_FakeJoycon_DPad_Up")), + DPadDown = Convert.ToInt16(Parser.Value("Controls_Left_FakeJoycon_DPad_Down")), + DPadLeft = Convert.ToInt16(Parser.Value("Controls_Left_FakeJoycon_DPad_Left")), + DPadRight = Convert.ToInt16(Parser.Value("Controls_Left_FakeJoycon_DPad_Right")), + ButtonMinus = Convert.ToInt16(Parser.Value("Controls_Left_FakeJoycon_Button_Minus")), + ButtonL = Convert.ToInt16(Parser.Value("Controls_Left_FakeJoycon_Button_L")), + ButtonZL = Convert.ToInt16(Parser.Value("Controls_Left_FakeJoycon_Button_ZL")) + }, + + Right = new JoyConRight + { + StickUp = Convert.ToInt16(Parser.Value("Controls_Right_FakeJoycon_Stick_Up")), + StickDown = Convert.ToInt16(Parser.Value("Controls_Right_FakeJoycon_Stick_Down")), + StickLeft = Convert.ToInt16(Parser.Value("Controls_Right_FakeJoycon_Stick_Left")), + StickRight = Convert.ToInt16(Parser.Value("Controls_Right_FakeJoycon_Stick_Right")), + StickButton = Convert.ToInt16(Parser.Value("Controls_Right_FakeJoycon_Stick_Button")), + ButtonA = Convert.ToInt16(Parser.Value("Controls_Right_FakeJoycon_Button_A")), + ButtonB = Convert.ToInt16(Parser.Value("Controls_Right_FakeJoycon_Button_B")), + ButtonX = Convert.ToInt16(Parser.Value("Controls_Right_FakeJoycon_Button_X")), + ButtonY = Convert.ToInt16(Parser.Value("Controls_Right_FakeJoycon_Button_Y")), + ButtonPlus = Convert.ToInt16(Parser.Value("Controls_Right_FakeJoycon_Button_Plus")), + ButtonR = Convert.ToInt16(Parser.Value("Controls_Right_FakeJoycon_Button_R")), + ButtonZR = Convert.ToInt16(Parser.Value("Controls_Right_FakeJoycon_Button_ZR")) + } + }; } } diff --git a/Ryujinx/Cpu/AOpCodeTable.cs b/Ryujinx/Cpu/AOpCodeTable.cs index 267b0d80b..e42136b8c 100644 --- a/Ryujinx/Cpu/AOpCodeTable.cs +++ b/Ryujinx/Cpu/AOpCodeTable.cs @@ -139,8 +139,8 @@ namespace ChocolArm64 Set("0>101110<<100000100110xxxxxxxxxx", AInstEmit.Cmle_V, typeof(AOpCodeSimd)); Set("0>001110<<100000101010xxxxxxxxxx", AInstEmit.Cmlt_V, typeof(AOpCodeSimd)); Set("0x00111000100000010110xxxxxxxxxx", AInstEmit.Cnt_V, typeof(AOpCodeSimd)); - Set("01011110000xxxxx000001xxxxxxxxxx", AInstEmit.Dup_S, typeof(AOpCodeSimdIns)); Set("0x001110000xxxxx000011xxxxxxxxxx", AInstEmit.Dup_Gp, typeof(AOpCodeSimdIns)); + Set("01011110000xxxxx000001xxxxxxxxxx", AInstEmit.Dup_S, typeof(AOpCodeSimdIns)); Set("0x001110000xxxxx000001xxxxxxxxxx", AInstEmit.Dup_V, typeof(AOpCodeSimdIns)); Set("0x101110001xxxxx000111xxxxxxxxxx", AInstEmit.Eor_V, typeof(AOpCodeSimdReg)); Set("00011110xx100000110000xxxxxxxxxx", AInstEmit.Fabs_S, typeof(AOpCodeSimd)); @@ -151,14 +151,16 @@ namespace ChocolArm64 Set("00011110xx1xxxxx001000xxxxx1x000", AInstEmit.Fcmpe_S, typeof(AOpCodeSimdReg)); Set("00011110xx1xxxxxxxxx11xxxxxxxxxx", AInstEmit.Fcsel_S, typeof(AOpCodeSimdFcond)); Set("00011110xx10001xx10000xxxxxxxxxx", AInstEmit.Fcvt_S, typeof(AOpCodeSimd)); - Set("x0011110xx110000000000xxxxxxxxxx", AInstEmit.Fcvtms_S, typeof(AOpCodeSimdCvt)); - Set("x0011110xx101000000000xxxxxxxxxx", AInstEmit.Fcvtps_S, typeof(AOpCodeSimdCvt)); - Set("x0011110xx111000000000xxxxxxxxxx", AInstEmit.Fcvtzs_S, typeof(AOpCodeSimdCvt)); - Set("x0011110xx011000xxxxxxxxxxxxxxxx", AInstEmit.Fcvtzs_Fix, typeof(AOpCodeSimdCvt)); + Set("x0011110xx100100000000xxxxxxxxxx", AInstEmit.Fcvtas_Gp, typeof(AOpCodeSimdCvt)); + Set("x0011110xx100101000000xxxxxxxxxx", AInstEmit.Fcvtau_Gp, typeof(AOpCodeSimdCvt)); + Set("x0011110xx110000000000xxxxxxxxxx", AInstEmit.Fcvtms_Gp, typeof(AOpCodeSimdCvt)); + Set("x0011110xx101000000000xxxxxxxxxx", AInstEmit.Fcvtps_Gp, typeof(AOpCodeSimdCvt)); + Set("x0011110xx111000000000xxxxxxxxxx", AInstEmit.Fcvtzs_Gp, typeof(AOpCodeSimdCvt)); + Set("x0011110xx011000xxxxxxxxxxxxxxxx", AInstEmit.Fcvtzs_Gp_Fix, typeof(AOpCodeSimdCvt)); Set("0x0011101x100001101110xxxxxxxxxx", AInstEmit.Fcvtzs_V, typeof(AOpCodeSimd)); Set("0x0011110>>xxxxx111111xxxxxxxxxx", AInstEmit.Fcvtzs_V, typeof(AOpCodeSimdShImm)); - Set("x0011110xx111001000000xxxxxxxxxx", AInstEmit.Fcvtzu_S, typeof(AOpCodeSimdCvt)); - Set("x0011110xx011001xxxxxxxxxxxxxxxx", AInstEmit.Fcvtzu_Fix, typeof(AOpCodeSimdCvt)); + Set("x0011110xx111001000000xxxxxxxxxx", AInstEmit.Fcvtzu_Gp, typeof(AOpCodeSimdCvt)); + Set("x0011110xx011001xxxxxxxxxxxxxxxx", AInstEmit.Fcvtzu_Gp_Fix, typeof(AOpCodeSimdCvt)); Set("0x1011101x100001101110xxxxxxxxxx", AInstEmit.Fcvtzu_V, typeof(AOpCodeSimd)); Set("0x1011110>>xxxxx111111xxxxxxxxxx", AInstEmit.Fcvtzu_V, typeof(AOpCodeSimdShImm)); Set("00011110xx1xxxxx000110xxxxxxxxxx", AInstEmit.Fdiv_S, typeof(AOpCodeSimdReg)); @@ -168,7 +170,7 @@ namespace ChocolArm64 Set("00011110xx1xxxxx010110xxxxxxxxxx", AInstEmit.Fmin_S, typeof(AOpCodeSimdReg)); Set("00011110xx1xxxxx011110xxxxxxxxxx", AInstEmit.Fminnm_S, typeof(AOpCodeSimdReg)); Set("0x0011100x1xxxxx110011xxxxxxxxxx", AInstEmit.Fmla_V, typeof(AOpCodeSimdReg)); - Set("0x0011111<101110<<1xxxxx010001xxxxxxxxxx", AInstEmit.Ushl_V, typeof(AOpCodeSimdReg)); Set("0x10111100>>>xxx101001xxxxxxxxxx", AInstEmit.Ushll_V, typeof(AOpCodeSimdShImm)); Set("0x1011110>>>>xxx000001xxxxxxxxxx", AInstEmit.Ushr_V, typeof(AOpCodeSimdShImm)); Set("0x1011110>>>>xxx000101xxxxxxxxxx", AInstEmit.Usra_V, typeof(AOpCodeSimdShImm)); Set("0x001110xx0xxxxx000110xxxxxxxxxx", AInstEmit.Uzp1_V, typeof(AOpCodeSimdReg)); + Set("0x001110xx0xxxxx010110xxxxxxxxxx", AInstEmit.Uzp2_V, typeof(AOpCodeSimdReg)); Set("0x001110<<100001001010xxxxxxxxxx", AInstEmit.Xtn_V, typeof(AOpCodeSimd)); #endregion } @@ -317,7 +322,7 @@ namespace ChocolArm64 } else if (ZCount != 0 && OCount != 0) { - //When both the > and the <, then a value is blacklisted, + //When both the > and the < are used, then a value is blacklisted, //with > indicating 0, and < indicating 1. So, for example, ><< //blacklists the pattern 011, but 000, 001, 010, 100, 101, //110 and 111 are valid. diff --git a/Ryujinx/Cpu/AThread.cs b/Ryujinx/Cpu/AThread.cs index 93331825c..5c0322894 100644 --- a/Ryujinx/Cpu/AThread.cs +++ b/Ryujinx/Cpu/AThread.cs @@ -5,10 +5,10 @@ using System.Threading; namespace ChocolArm64 { - class AThread + public class AThread { - public ARegisters Registers { get; private set; } - public AMemory Memory { get; private set; } + public AThreadState ThreadState { get; private set; } + public AMemory Memory { get; private set; } public long EntryPoint { get; private set; } @@ -20,7 +20,7 @@ namespace ChocolArm64 public event EventHandler WorkFinished; - public int ThreadId => Registers.ThreadId; + public int ThreadId => ThreadState.ThreadId; public bool IsAlive => Work.IsAlive; @@ -34,7 +34,7 @@ namespace ChocolArm64 this.Priority = Priority; this.EntryPoint = EntryPoint; - Registers = new ARegisters(); + ThreadState = new AThreadState(); Translator = new ATranslator(this); ExecuteLock = new object(); } diff --git a/Ryujinx/Cpu/ATranslatedSub.cs b/Ryujinx/Cpu/ATranslatedSub.cs index 1a821f479..71a6793a2 100644 --- a/Ryujinx/Cpu/ATranslatedSub.cs +++ b/Ryujinx/Cpu/ATranslatedSub.cs @@ -9,7 +9,7 @@ namespace ChocolArm64 { class ATranslatedSub { - private delegate long AA64Subroutine(ARegisters Register, AMemory Memory); + private delegate long AA64Subroutine(AThreadState Register, AMemory Memory); private AA64Subroutine ExecDelegate; @@ -17,8 +17,8 @@ namespace ChocolArm64 public static Type[] FixedArgTypes { get; private set; } - public static int RegistersArgIdx { get; private set; } - public static int MemoryArgIdx { get; private set; } + public static int StateArgIdx { get; private set; } + public static int MemoryArgIdx { get; private set; } public DynamicMethod Method { get; private set; } @@ -58,9 +58,9 @@ namespace ChocolArm64 FixedArgTypes[Index] = ParamType; - if (ParamType == typeof(ARegisters)) + if (ParamType == typeof(AThreadState)) { - RegistersArgIdx = Index; + StateArgIdx = Index; } else if (ParamType == typeof(AMemory)) { @@ -69,7 +69,7 @@ namespace ChocolArm64 } } - public long Execute(ARegisters Registers, AMemory Memory) + public long Execute(AThreadState ThreadState, AMemory Memory) { if (!HasDelegate) { @@ -83,7 +83,7 @@ namespace ChocolArm64 foreach (ARegister Reg in Params) { - Generator.EmitLdarg(RegistersArgIdx); + Generator.EmitLdarg(StateArgIdx); Generator.Emit(OpCodes.Ldfld, Reg.GetField()); } @@ -96,7 +96,7 @@ namespace ChocolArm64 HasDelegate = true; } - return ExecDelegate(Registers, Memory); + return ExecDelegate(ThreadState, Memory); } public void MarkForReJit() => NeedsReJit = true; diff --git a/Ryujinx/Cpu/ATranslator.cs b/Ryujinx/Cpu/ATranslator.cs index ba7f3df6f..96bbc89ea 100644 --- a/Ryujinx/Cpu/ATranslator.cs +++ b/Ryujinx/Cpu/ATranslator.cs @@ -8,10 +8,10 @@ namespace ChocolArm64 { class ATranslator { - private Dictionary CachedSubs; - public AThread Thread { get; private set; } + private Dictionary CachedSubs; + private bool KeepRunning; public ATranslator(AThread Parent) @@ -31,11 +31,11 @@ namespace ChocolArm64 { if (CachedSubs.TryGetValue(Position, out ATranslatedSub Sub) && !Sub.NeedsReJit) { - Position = Sub.Execute(Thread.Registers, Thread.Memory); + Position = Sub.Execute(Thread.ThreadState, Thread.Memory); } else { - Position = TranslateSubroutine(Position).Execute(Thread.Registers, Thread.Memory); + Position = TranslateSubroutine(Position).Execute(Thread.ThreadState, Thread.Memory); } } while (Position != 0 && KeepRunning); diff --git a/Ryujinx/Cpu/Decoder/AOpCodeCcmp.cs b/Ryujinx/Cpu/Decoder/AOpCodeCcmp.cs index ab7aa7545..d0c7f779c 100644 --- a/Ryujinx/Cpu/Decoder/AOpCodeCcmp.cs +++ b/Ryujinx/Cpu/Decoder/AOpCodeCcmp.cs @@ -25,7 +25,7 @@ namespace ChocolArm64.Decoder Cond = (ACond)((OpCode >> 12) & 0xf); RmImm = (OpCode >> 16) & 0x1f; - Rd = ARegisters.ZRIndex; + Rd = AThreadState.ZRIndex; } } } \ No newline at end of file diff --git a/Ryujinx/Cpu/Decoder/AOpCodeMem.cs b/Ryujinx/Cpu/Decoder/AOpCodeMem.cs index 5ec72125a..be5367cf6 100644 --- a/Ryujinx/Cpu/Decoder/AOpCodeMem.cs +++ b/Ryujinx/Cpu/Decoder/AOpCodeMem.cs @@ -4,10 +4,10 @@ namespace ChocolArm64.Decoder { class AOpCodeMem : AOpCode { - public int Rt { get; protected set; } - public int Rn { get; protected set; } - public int Size { get; protected set; } - public bool Extend64 { get; protected set; } + public int Rt { get; protected set; } + public int Rn { get; protected set; } + public int Size { get; protected set; } + public bool Extend64 { get; protected set; } public AOpCodeMem(AInst Inst, long Position, int OpCode) : base(Inst, Position, OpCode) { diff --git a/Ryujinx/Cpu/Decoder/AOpCodeSimdMemMs.cs b/Ryujinx/Cpu/Decoder/AOpCodeSimdMemMs.cs index 635ec91e4..9ea979ba7 100644 --- a/Ryujinx/Cpu/Decoder/AOpCodeSimdMemMs.cs +++ b/Ryujinx/Cpu/Decoder/AOpCodeSimdMemMs.cs @@ -3,12 +3,8 @@ using ChocolArm64.State; namespace ChocolArm64.Decoder { - class AOpCodeSimdMemMs : AOpCode, IAOpCodeSimd + class AOpCodeSimdMemMs : AOpCodeMemReg, IAOpCodeSimd { - public int Rt { get; private set; } - public int Rn { get; private set; } - public int Size { get; private set; } - public int Rm { get; private set; } public int Reps { get; private set; } public int SElems { get; private set; } public int Elems { get; private set; } @@ -29,10 +25,7 @@ namespace ChocolArm64.Decoder default: Inst = AInst.Undefined; return; } - Rt = (OpCode >> 0) & 0x1f; - Rn = (OpCode >> 5) & 0x1f; Size = (OpCode >> 10) & 0x3; - Rm = (OpCode >> 16) & 0x1f; WBack = ((OpCode >> 23) & 0x1) != 0; bool Q = ((OpCode >> 30) & 1) != 0; @@ -44,6 +37,8 @@ namespace ChocolArm64.Decoder return; } + Extend64 = false; + RegisterSize = Q ? ARegisterSize.SIMD128 : ARegisterSize.SIMD64; diff --git a/Ryujinx/Cpu/Decoder/AOpCodeSimdMemSs.cs b/Ryujinx/Cpu/Decoder/AOpCodeSimdMemSs.cs index 5bad95edd..be4a8cd98 100644 --- a/Ryujinx/Cpu/Decoder/AOpCodeSimdMemSs.cs +++ b/Ryujinx/Cpu/Decoder/AOpCodeSimdMemSs.cs @@ -3,12 +3,8 @@ using ChocolArm64.State; namespace ChocolArm64.Decoder { - class AOpCodeSimdMemSs : AOpCode, IAOpCodeSimd + class AOpCodeSimdMemSs : AOpCodeMemReg, IAOpCodeSimd { - public int Rt { get; private set; } - public int Rn { get; private set; } - public int Size { get; private set; } - public int Rm { get; private set; } public int SElems { get; private set; } public int Index { get; private set; } public bool Replicate { get; private set; } @@ -31,11 +27,9 @@ namespace ChocolArm64.Decoder switch (Scale) { - case 0: Index >>= 0; break; - case 1: { - if ((Index & 1) != 0) + if ((Size & 1) != 0) { Inst = AInst.Undefined; @@ -49,23 +43,23 @@ namespace ChocolArm64.Decoder case 2: { - if ((Index & 2) != 0 || - ((Index & 1) != 0 && S != 0)) + if ((Size & 2) != 0 || + ((Size & 1) != 0 && S != 0)) { Inst = AInst.Undefined; return; } - if ((Index & 1) != 0) + if ((Size & 1) != 0) { Index >>= 3; + + Scale = 3; } else { Index >>= 2; - - Scale = 3; } break; @@ -91,9 +85,8 @@ namespace ChocolArm64.Decoder this.SElems = SElems; this.Size = Scale; - Rt = (OpCode >> 0) & 0x1f; - Rn = (OpCode >> 5) & 0x1f; - Rm = (OpCode >> 16) & 0x1f; + Extend64 = false; + WBack = ((OpCode >> 23) & 0x1) != 0; RegisterSize = Q != 0 diff --git a/Ryujinx/Cpu/Decoder/AOpCodeSimdRegElem.cs b/Ryujinx/Cpu/Decoder/AOpCodeSimdRegElem.cs index 828fe7884..d878b12ea 100644 --- a/Ryujinx/Cpu/Decoder/AOpCodeSimdRegElem.cs +++ b/Ryujinx/Cpu/Decoder/AOpCodeSimdRegElem.cs @@ -2,17 +2,13 @@ using ChocolArm64.Instruction; namespace ChocolArm64.Decoder { - class AOpCodeSimdRegElem : AOpCodeSimd + class AOpCodeSimdRegElem : AOpCodeSimdReg { - public int Rm { get; private set; } public int Index { get; private set; } public AOpCodeSimdRegElem(AInst Inst, long Position, int OpCode) : base(Inst, Position, OpCode) { - Rm = (OpCode >> 16) & 0x1f; - Size = (OpCode >> 22) & 0x1; - - if (Size != 0) + if ((Size & 1) != 0) { Index = (OpCode >> 11) & 1; } diff --git a/Ryujinx/Cpu/Instruction/AInstEmitAlu.cs b/Ryujinx/Cpu/Instruction/AInstEmitAlu.cs index ac9c43c37..073a45e7a 100644 --- a/Ryujinx/Cpu/Instruction/AInstEmitAlu.cs +++ b/Ryujinx/Cpu/Instruction/AInstEmitAlu.cs @@ -60,6 +60,8 @@ namespace ChocolArm64.Instruction Context.Emit(OpCodes.And); + EmitZeroCVFlags(Context); + Context.EmitZNFlagCheck(); EmitDataStoreS(Context); @@ -79,6 +81,8 @@ namespace ChocolArm64.Instruction if (SetFlags) { + EmitZeroCVFlags(Context); + Context.EmitZNFlagCheck(); } @@ -334,6 +338,17 @@ namespace ChocolArm64.Instruction { Context.Emit(OpCodes.Conv_I4); } - } + } + + private static void EmitZeroCVFlags(AILEmitterCtx Context) + { + Context.EmitLdc_I4(0); + + Context.EmitStflg((int)APState.VBit); + + Context.EmitLdc_I4(0); + + Context.EmitStflg((int)APState.CBit); + } } } \ No newline at end of file diff --git a/Ryujinx/Cpu/Instruction/AInstEmitAluHelper.cs b/Ryujinx/Cpu/Instruction/AInstEmitAluHelper.cs index 03355ebad..57ec25dd0 100644 --- a/Ryujinx/Cpu/Instruction/AInstEmitAluHelper.cs +++ b/Ryujinx/Cpu/Instruction/AInstEmitAluHelper.cs @@ -21,22 +21,13 @@ namespace ChocolArm64.Instruction public static void EmitAddsVCheck(AILEmitterCtx Context) { - //V = (Rd ^ Rn) & (Rd ^ Rm) & ~(Rn ^ Rm) < 0 - Context.EmitSttmp(); - Context.EmitLdtmp(); - Context.EmitLdtmp(); + //V = (Rd ^ Rn) & ~(Rn ^ Rm) < 0 + Context.Emit(OpCodes.Dup); EmitDataLoadRn(Context); Context.Emit(OpCodes.Xor); - Context.EmitLdtmp(); - - EmitDataLoadOper2(Context); - - Context.Emit(OpCodes.Xor); - Context.Emit(OpCodes.And); - EmitDataLoadOpers(Context); Context.Emit(OpCodes.Xor); @@ -52,15 +43,14 @@ namespace ChocolArm64.Instruction public static void EmitSubsCCheck(AILEmitterCtx Context) { - //C = Rn == Rm || Rn > Rm + //C = Rn == Rm || Rn > Rm = !(Rn < Rm) EmitDataLoadOpers(Context); - Context.Emit(OpCodes.Ceq); + Context.Emit(OpCodes.Clt_Un); - EmitDataLoadOpers(Context); + Context.EmitLdc_I4(1); - Context.Emit(OpCodes.Cgt_Un); - Context.Emit(OpCodes.Or); + Context.Emit(OpCodes.Xor); Context.EmitStflg((int)APState.CBit); } diff --git a/Ryujinx/Cpu/Instruction/AInstEmitException.cs b/Ryujinx/Cpu/Instruction/AInstEmitException.cs index 085bb9aae..209ba56f5 100644 --- a/Ryujinx/Cpu/Instruction/AInstEmitException.cs +++ b/Ryujinx/Cpu/Instruction/AInstEmitException.cs @@ -11,12 +11,12 @@ namespace ChocolArm64.Instruction public static void Brk(AILEmitterCtx Context) { - EmitExceptionCall(Context, nameof(ARegisters.OnBreak)); + EmitExceptionCall(Context, nameof(AThreadState.OnBreak)); } public static void Svc(AILEmitterCtx Context) { - EmitExceptionCall(Context, nameof(ARegisters.OnSvcCall)); + EmitExceptionCall(Context, nameof(AThreadState.OnSvcCall)); } private static void EmitExceptionCall(AILEmitterCtx Context, string MthdName) @@ -25,11 +25,11 @@ namespace ChocolArm64.Instruction Context.EmitStoreState(); - Context.EmitLdarg(ATranslatedSub.RegistersArgIdx); + Context.EmitLdarg(ATranslatedSub.StateArgIdx); Context.EmitLdc_I4(Op.Id); - MethodInfo MthdInfo = typeof(ARegisters).GetMethod(MthdName, Binding); + MethodInfo MthdInfo = typeof(AThreadState).GetMethod(MthdName, Binding); Context.EmitCall(MthdInfo); @@ -45,14 +45,14 @@ namespace ChocolArm64.Instruction Context.EmitStoreState(); - Context.EmitLdarg(ATranslatedSub.RegistersArgIdx); + Context.EmitLdarg(ATranslatedSub.StateArgIdx); Context.EmitLdc_I8(Op.Position); Context.EmitLdc_I4(Op.RawOpCode); - string MthdName = nameof(ARegisters.OnUndefined); + string MthdName = nameof(AThreadState.OnUndefined); - MethodInfo MthdInfo = typeof(ARegisters).GetMethod(MthdName, Binding); + MethodInfo MthdInfo = typeof(AThreadState).GetMethod(MthdName, Binding); Context.EmitCall(MthdInfo); diff --git a/Ryujinx/Cpu/Instruction/AInstEmitFlow.cs b/Ryujinx/Cpu/Instruction/AInstEmitFlow.cs index 6fa19c9fc..be68aa6c9 100644 --- a/Ryujinx/Cpu/Instruction/AInstEmitFlow.cs +++ b/Ryujinx/Cpu/Instruction/AInstEmitFlow.cs @@ -26,7 +26,7 @@ namespace ChocolArm64.Instruction AOpCodeBImmAl Op = (AOpCodeBImmAl)Context.CurrOp; Context.EmitLdc_I(Op.Position + 4); - Context.EmitStint(ARegisters.LRIndex); + Context.EmitStint(AThreadState.LRIndex); Context.EmitStoreState(); if (Context.TryOptEmitSubroutineCall()) @@ -66,7 +66,7 @@ namespace ChocolArm64.Instruction AOpCodeBReg Op = (AOpCodeBReg)Context.CurrOp; Context.EmitLdc_I(Op.Position + 4); - Context.EmitStint(ARegisters.LRIndex); + Context.EmitStint(AThreadState.LRIndex); Context.EmitStoreState(); Context.EmitLdintzr(Op.Rn); @@ -99,7 +99,7 @@ namespace ChocolArm64.Instruction public static void Ret(AILEmitterCtx Context) { Context.EmitStoreState(); - Context.EmitLdint(ARegisters.LRIndex); + Context.EmitLdint(AThreadState.LRIndex); Context.Emit(OpCodes.Ret); } diff --git a/Ryujinx/Cpu/Instruction/AInstEmitMemory.cs b/Ryujinx/Cpu/Instruction/AInstEmitMemory.cs index ca0c82a37..af7de3ba6 100644 --- a/Ryujinx/Cpu/Instruction/AInstEmitMemory.cs +++ b/Ryujinx/Cpu/Instruction/AInstEmitMemory.cs @@ -20,7 +20,7 @@ namespace ChocolArm64.Instruction { AOpCodeAdr Op = (AOpCodeAdr)Context.CurrOp; - Context.EmitLdc_I((Op.Position & ~0xfff) + (Op.Imm << 12)); + Context.EmitLdc_I((Op.Position & ~0xfffL) + (Op.Imm << 12)); Context.EmitStintzr(Op.Rd); } diff --git a/Ryujinx/Cpu/Instruction/AInstEmitMemoryEx.cs b/Ryujinx/Cpu/Instruction/AInstEmitMemoryEx.cs index ebb6e9328..a339bb694 100644 --- a/Ryujinx/Cpu/Instruction/AInstEmitMemoryEx.cs +++ b/Ryujinx/Cpu/Instruction/AInstEmitMemoryEx.cs @@ -159,7 +159,7 @@ namespace ChocolArm64.Instruction private static void EmitMemoryCall(AILEmitterCtx Context, string Name, int Rn = -1) { Context.EmitLdarg(ATranslatedSub.MemoryArgIdx); - Context.EmitLdarg(ATranslatedSub.RegistersArgIdx); + Context.EmitLdarg(ATranslatedSub.StateArgIdx); if (Rn != -1) { diff --git a/Ryujinx/Cpu/Instruction/AInstEmitMemoryHelper.cs b/Ryujinx/Cpu/Instruction/AInstEmitMemoryHelper.cs index e05153d99..d5a0051b5 100644 --- a/Ryujinx/Cpu/Instruction/AInstEmitMemoryHelper.cs +++ b/Ryujinx/Cpu/Instruction/AInstEmitMemoryHelper.cs @@ -1,3 +1,4 @@ +using ChocolArm64.Decoder; using ChocolArm64.Memory; using ChocolArm64.Translation; using System; @@ -31,67 +32,107 @@ namespace ChocolArm64.Instruction private static void EmitReadCall(AILEmitterCtx Context, Extension Ext, int Size) { - if (Size < 0 || Size > 4) + bool IsSimd = GetIsSimd(Context); + + string Name = null; + + if (Size < 0 || Size > (IsSimd ? 4 : 3)) { throw new ArgumentOutOfRangeException(nameof(Size)); } - string Name = null; - - 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 4: Name = nameof(AMemory.ReadVector128); break; - } - - Context.EmitCall(typeof(AMemory), Name); - - if (Ext == Extension.Sx32 || - Ext == Extension.Sx64) + if (IsSimd) { switch (Size) { - case 0: Context.Emit(OpCodes.Conv_I1); break; - case 1: Context.Emit(OpCodes.Conv_I2); break; - case 2: Context.Emit(OpCodes.Conv_I4); break; + 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; + } + } + 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; } } - if (Size < 3) + Context.EmitCall(typeof(AMemory), Name); + + if (!IsSimd) { - Context.Emit(Ext == Extension.Sx64 - ? OpCodes.Conv_I8 - : OpCodes.Conv_U8); + if (Ext == Extension.Sx32 || + Ext == Extension.Sx64) + { + switch (Size) + { + case 0: Context.Emit(OpCodes.Conv_I1); break; + case 1: Context.Emit(OpCodes.Conv_I2); break; + case 2: Context.Emit(OpCodes.Conv_I4); break; + } + } + + if (Size < 3) + { + Context.Emit(Ext == Extension.Sx64 + ? OpCodes.Conv_I8 + : OpCodes.Conv_U8); + } } } public static void EmitWriteCall(AILEmitterCtx Context, int Size) { - if (Size < 0 || Size > 4) + bool IsSimd = GetIsSimd(Context); + + string Name = null; + + if (Size < 0 || Size > (IsSimd ? 4 : 3)) { throw new ArgumentOutOfRangeException(nameof(Size)); - } + } - if (Size < 3) + if (Size < 3 && !IsSimd) { Context.Emit(OpCodes.Conv_I4); } - string Name = null; - - switch (Size) + if (IsSimd) { - 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 4: Name = nameof(AMemory.WriteVector128); break; + 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; + } + } + 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; + } } Context.EmitCall(typeof(AMemory), Name); } + + private static bool GetIsSimd(AILEmitterCtx Context) + { + return Context.CurrOp is IAOpCodeSimd && + !(Context.CurrOp is AOpCodeSimdMemMs || + Context.CurrOp is AOpCodeSimdMemSs); + } } } \ No newline at end of file diff --git a/Ryujinx/Cpu/Instruction/AInstEmitScalar.cs b/Ryujinx/Cpu/Instruction/AInstEmitScalar.cs deleted file mode 100644 index ab4c2fd22..000000000 --- a/Ryujinx/Cpu/Instruction/AInstEmitScalar.cs +++ /dev/null @@ -1,719 +0,0 @@ -using ChocolArm64.Decoder; -using ChocolArm64.State; -using ChocolArm64.Translation; -using System; -using System.Reflection; -using System.Reflection.Emit; - -namespace ChocolArm64.Instruction -{ - static partial class AInstEmit - { - public static void Addp_S(AILEmitterCtx Context) - { - AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp; - - Context.EmitLdvec(Op.Rn); - Context.EmitLdc_I4(Op.Size); - - ASoftFallback.EmitCall(Context, nameof(ASoftFallback.Addp_S)); - - Context.EmitStvec(Op.Rd); - } - - public static void Dup_S(AILEmitterCtx Context) - { - AOpCodeSimdIns Op = (AOpCodeSimdIns)Context.CurrOp; - - Context.EmitLdvec(Op.Rn); - Context.EmitLdc_I4(Op.DstIndex); - Context.EmitLdc_I4(Op.Size); - - ASoftFallback.EmitCall(Context, nameof(ASoftFallback.Dup_S)); - - Context.EmitStvec(Op.Rd); - } - - public static void Fabs_S(AILEmitterCtx Context) - { - AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp; - - Context.EmitLdvecsf(Op.Rn); - - MethodInfo MthdInfo; - - if (Op.Size == 0) - { - MthdInfo = typeof(MathF).GetMethod(nameof(MathF.Abs), new Type[] { typeof(float) }); - } - else if (Op.Size == 1) - { - MthdInfo = typeof(Math).GetMethod(nameof(Math.Abs), new Type[] { typeof(double) }); - } - else - { - throw new InvalidOperationException(); - } - - Context.EmitCall(MthdInfo); - - Context.EmitStvecsf(Op.Rd); - } - - public static void Fadd_S(AILEmitterCtx Context) => EmitScalarOp(Context, OpCodes.Add); - - public static void Fccmp_S(AILEmitterCtx Context) - { - AOpCodeSimdFcond Op = (AOpCodeSimdFcond)Context.CurrOp; - - AILLabel LblTrue = new AILLabel(); - AILLabel LblEnd = new AILLabel(); - - Context.EmitCondBranch(LblTrue, Op.Cond); - - //TODO: Share this logic with Ccmp. - Context.EmitLdc_I4((Op.NZCV >> 0) & 1); - - Context.EmitStflg((int)APState.VBit); - - Context.EmitLdc_I4((Op.NZCV >> 1) & 1); - - Context.EmitStflg((int)APState.CBit); - - Context.EmitLdc_I4((Op.NZCV >> 2) & 1); - - Context.EmitStflg((int)APState.ZBit); - - Context.EmitLdc_I4((Op.NZCV >> 3) & 1); - - Context.EmitStflg((int)APState.NBit); - - Context.Emit(OpCodes.Br_S, LblEnd); - - Context.MarkLabel(LblTrue); - - Fcmp_S(Context); - - Context.MarkLabel(LblEnd); - } - - public static void Fcmp_S(AILEmitterCtx Context) - { - AOpCodeSimdReg Op = (AOpCodeSimdReg)Context.CurrOp; - - bool CmpWithZero = !(Op is AOpCodeSimdFcond) ? Op.Bit3 : false; - - //todo - //Context.TryMarkCondWithoutCmp(); - - void EmitLoadOpers() - { - Context.EmitLdvecsf(Op.Rn); - - if (CmpWithZero) - { - EmitLdcImmF(Context, 0, Op.Size); - } - else - { - Context.EmitLdvecsf(Op.Rm); - } - } - - //Z = Rn == Rm - EmitLoadOpers(); - - Context.Emit(OpCodes.Ceq); - Context.Emit(OpCodes.Dup); - - Context.EmitStflg((int)APState.ZBit); - - //C = Rn >= Rm - EmitLoadOpers(); - - Context.Emit(OpCodes.Cgt); - Context.Emit(OpCodes.Or); - - Context.EmitStflg((int)APState.CBit); - - //N = Rn < Rm - EmitLoadOpers(); - - Context.Emit(OpCodes.Clt); - - Context.EmitStflg((int)APState.NBit); - - //Handle NaN case. If any number is NaN, then NZCV = 0011. - AILLabel LblNotNaN = new AILLabel(); - - if (CmpWithZero) - { - EmitNaNCheck(Context, Op.Rn); - } - else - { - EmitNaNCheck(Context, Op.Rn); - EmitNaNCheck(Context, Op.Rm); - - Context.Emit(OpCodes.Or); - } - - Context.Emit(OpCodes.Brfalse_S, LblNotNaN); - - Context.EmitLdc_I4(1); - Context.EmitLdc_I4(1); - - Context.EmitStflg((int)APState.CBit); - Context.EmitStflg((int)APState.VBit); - - Context.MarkLabel(LblNotNaN); - } - - public static void Fcmpe_S(AILEmitterCtx Context) - { - //TODO: Raise exception if value is NaN, how to handle exceptions? - Fcmp_S(Context); - } - - public static void Fcsel_S(AILEmitterCtx Context) - { - AOpCodeSimdFcond Op = (AOpCodeSimdFcond)Context.CurrOp; - - AILLabel LblTrue = new AILLabel(); - AILLabel LblEnd = new AILLabel(); - - Context.EmitCondBranch(LblTrue, Op.Cond); - Context.EmitLdvecsf(Op.Rm); - Context.EmitStvecsf(Op.Rd); - - Context.Emit(OpCodes.Br_S, LblEnd); - - Context.MarkLabel(LblTrue); - - Context.EmitLdvecsf(Op.Rn); - Context.EmitStvecsf(Op.Rd); - - Context.MarkLabel(LblEnd); - } - - public static void Fcvt_S(AILEmitterCtx Context) - { - AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp; - - Context.EmitLdvecsf(Op.Rn); - - EmitFloatCast(Context, Op.Opc); - - Context.EmitStvecsf(Op.Rd); - } - - public static void Fcvtms_S(AILEmitterCtx Context) => EmitMathOpCvtToInt(Context, nameof(Math.Floor)); - public static void Fcvtps_S(AILEmitterCtx Context) => EmitMathOpCvtToInt(Context, nameof(Math.Ceiling)); - - public static void Fcvtzs_S(AILEmitterCtx Context) => EmitFcvtz_(Context, true); - public static void Fcvtzu_S(AILEmitterCtx Context) => EmitFcvtz_(Context, false); - - private static void EmitFcvtz_(AILEmitterCtx Context, bool Signed) - { - AOpCodeSimdCvt Op = (AOpCodeSimdCvt)Context.CurrOp; - - Context.EmitLdvecsf(Op.Rn); - - if (Signed) - { - EmitCvtToInt(Context, Op.Size); - } - else - { - EmitCvtToUInt(Context, Op.Size); - } - - Context.EmitStintzr(Op.Rd); - } - - public static void Fcvtzs_Fix(AILEmitterCtx Context) => EmitFcvtz__Fix(Context, true); - public static void Fcvtzu_Fix(AILEmitterCtx Context) => EmitFcvtz__Fix(Context, false); - - private static void EmitFcvtz__Fix(AILEmitterCtx Context, bool Signed) - { - AOpCodeSimdCvt Op = (AOpCodeSimdCvt)Context.CurrOp; - - Context.EmitLdvecsf(Op.Rn); - - EmitLdcImmF(Context, 1L << Op.FBits, Op.Size); - - Context.Emit(OpCodes.Mul); - - if (Signed) - { - EmitCvtToInt(Context, Op.Size); - } - else - { - EmitCvtToUInt(Context, Op.Size); - } - - Context.EmitStintzr(Op.Rd); - } - - public static void Fdiv_S(AILEmitterCtx Context) => EmitScalarOp(Context, OpCodes.Div); - - public static void Fmadd_S(AILEmitterCtx Context) - { - AOpCodeSimdReg Op = (AOpCodeSimdReg)Context.CurrOp; - - Context.EmitLdvecsf(Op.Ra); - Context.EmitLdvecsf(Op.Rn); - Context.EmitLdvecsf(Op.Rm); - - Context.Emit(OpCodes.Mul); - Context.Emit(OpCodes.Add); - - Context.EmitStvecsf(Op.Rd); - } - - public static void Fmax_S(AILEmitterCtx Context) => EmitMathOp3(Context, nameof(Math.Max)); - public static void Fmin_S(AILEmitterCtx Context) => EmitMathOp3(Context, nameof(Math.Min)); - - public static void Fmaxnm_S(AILEmitterCtx Context) => EmitMathOp3(Context, nameof(Math.Max)); - public static void Fminnm_S(AILEmitterCtx Context) => EmitMathOp3(Context, nameof(Math.Min)); - - public static void Fmov_S(AILEmitterCtx Context) - { - AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp; - - Context.EmitLdvecsf(Op.Rn); - Context.EmitStvecsf(Op.Rd); - } - - public static void Fmov_Si(AILEmitterCtx Context) - { - AOpCodeSimdFmov Op = (AOpCodeSimdFmov)Context.CurrOp; - - Context.EmitLdc_I8(Op.Imm); - Context.EmitLdc_I4(0); - Context.EmitLdc_I4(Op.Size + 2); - - ASoftFallback.EmitCall(Context, nameof(ASoftFallback.Fmov_S)); - - Context.EmitStvec(Op.Rd); - } - - public static void Fmov_Ftoi(AILEmitterCtx Context) - { - AOpCodeSimdCvt Op = (AOpCodeSimdCvt)Context.CurrOp; - - Context.EmitLdvecsi(Op.Rn); - Context.EmitStintzr(Op.Rd); - } - - public static void Fmov_Itof(AILEmitterCtx Context) - { - AOpCodeSimdCvt Op = (AOpCodeSimdCvt)Context.CurrOp; - - Context.EmitLdintzr(Op.Rn); - Context.EmitStvecsi(Op.Rd); - } - - public static void Fmov_Ftoi1(AILEmitterCtx Context) - { - AOpCodeSimdCvt Op = (AOpCodeSimdCvt)Context.CurrOp; - - Context.EmitLdvec(Op.Rn); - Context.EmitLdc_I4(1); - Context.EmitLdc_I4(3); - - ASoftFallback.EmitCall(Context, nameof(ASoftFallback.ExtractVec)); - - Context.EmitStintzr(Op.Rd); - } - - public static void Fmov_Itof1(AILEmitterCtx Context) - { - AOpCodeSimdCvt Op = (AOpCodeSimdCvt)Context.CurrOp; - - Context.EmitLdintzr(Op.Rn); - Context.EmitLdc_I4(1); - Context.EmitLdc_I4(3); - - ASoftFallback.EmitCall(Context, nameof(ASoftFallback.Fmov_S)); - - Context.EmitStvec(Op.Rd); - } - - public static void Fmsub_S(AILEmitterCtx Context) - { - AOpCodeSimdReg Op = (AOpCodeSimdReg)Context.CurrOp; - - Context.EmitLdvecsf(Op.Ra); - Context.EmitLdvecsf(Op.Rn); - - Context.Emit(OpCodes.Neg); - - Context.EmitLdvecsf(Op.Rm); - - Context.Emit(OpCodes.Mul); - Context.Emit(OpCodes.Sub); - - Context.EmitStvecsf(Op.Rd); - } - - public static void Fmul_S(AILEmitterCtx Context) => EmitScalarOp(Context, OpCodes.Mul); - - public static void Fneg_S(AILEmitterCtx Context) => EmitScalarOp(Context, OpCodes.Neg); - - public static void Fnmul_S(AILEmitterCtx Context) - { - AOpCodeSimdReg Op = (AOpCodeSimdReg)Context.CurrOp; - - Context.EmitLdvecsf(Op.Rn); - Context.EmitLdvecsf(Op.Rm); - - Context.Emit(OpCodes.Mul); - Context.Emit(OpCodes.Neg); - - Context.EmitStvecsf(Op.Rd); - } - - public static void Frinta_S(AILEmitterCtx Context) - { - AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp; - - Context.EmitLdvecsf(Op.Rn); - Context.EmitLdc_I4((int)MidpointRounding.AwayFromZero); - - MethodInfo MthdInfo; - - if (Op.Size == 0) - { - Type[] Types = new Type[] { typeof(float), typeof(MidpointRounding) }; - - MthdInfo = typeof(MathF).GetMethod(nameof(MathF.Round), Types); - } - else if (Op.Size == 1) - { - Type[] Types = new Type[] { typeof(double), typeof(MidpointRounding) }; - - MthdInfo = typeof(Math).GetMethod(nameof(Math.Round), Types); - } - else - { - throw new InvalidOperationException(); - } - - Context.EmitCall(MthdInfo); - - Context.EmitStvecsf(Op.Rd); - } - - public static void Frintm_S(AILEmitterCtx Context) - { - AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp; - - Context.EmitLdvecsf(Op.Rn); - - MethodInfo MthdInfo; - - if (Op.Size == 0) - { - MthdInfo = typeof(MathF).GetMethod(nameof(MathF.Floor), new Type[] { typeof(float) }); - } - else if (Op.Size == 1) - { - MthdInfo = typeof(Math).GetMethod(nameof(Math.Floor), new Type[] { typeof(double) }); - } - else - { - throw new InvalidOperationException(); - } - - Context.EmitCall(MthdInfo); - - Context.EmitStvecsf(Op.Rd); - } - - public static void Fsqrt_S(AILEmitterCtx Context) => EmitMathOp2(Context, nameof(Math.Sqrt)); - - public static void Fsub_S(AILEmitterCtx Context) => EmitScalarOp(Context, OpCodes.Sub); - - public static void Scvtf_Gp(AILEmitterCtx Context) - { - AOpCodeSimdCvt Op = (AOpCodeSimdCvt)Context.CurrOp; - - Context.EmitLdintzr(Op.Rn); - - EmitFloatCast(Context, Op.Size); - - Context.EmitStvecsf(Op.Rd); - } - - public static void Scvtf_S(AILEmitterCtx Context) - { - AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp; - - Context.EmitLdvecsi(Op.Rn); - - EmitFloatCast(Context, Op.Size); - - Context.EmitStvecsf(Op.Rd); - } - - public static void Shl_S(AILEmitterCtx Context) - { - AOpCodeSimdShImm Op = (AOpCodeSimdShImm)Context.CurrOp; - - Context.EmitLdvecsi(Op.Rn); - Context.EmitLdc_I4(Op.Imm - (8 << Op.Size)); - - Context.Emit(OpCodes.Shl); - - Context.EmitStvecsi(Op.Rd); - } - - public static void Sshr_S(AILEmitterCtx Context) - { - AOpCodeSimdShImm Op = (AOpCodeSimdShImm)Context.CurrOp; - - Context.EmitLdvecsi(Op.Rn); - Context.EmitLdc_I4((8 << (Op.Size + 1)) - Op.Imm); - - Context.Emit(OpCodes.Shr); - - Context.EmitStvecsi(Op.Rd); - } - - public static void Sub_S(AILEmitterCtx Context) - { - AOpCodeSimdReg Op = (AOpCodeSimdReg)Context.CurrOp; - - Context.EmitLdvecsi(Op.Rn); - Context.EmitLdvecsi(Op.Rm); - - Context.Emit(OpCodes.Sub); - - Context.EmitStvecsi(Op.Rd); - } - - public static void Ucvtf_Gp(AILEmitterCtx Context) - { - AOpCodeSimdCvt Op = (AOpCodeSimdCvt)Context.CurrOp; - - Context.EmitLdintzr(Op.Rn); - - Context.Emit(OpCodes.Conv_R_Un); - - EmitFloatCast(Context, Op.Size); - - Context.EmitStvecsf(Op.Rd); - } - - private static void EmitScalarOp(AILEmitterCtx Context, OpCode ILOp) - { - AOpCodeSimdReg Op = (AOpCodeSimdReg)Context.CurrOp; - - Context.EmitLdvecsf(Op.Rn); - - //Negate and Not are the only unary operations supported on IL. - //"Not" doesn't work with floats, so we don't need to compare it. - if (ILOp != OpCodes.Neg) - { - Context.EmitLdvecsf(Op.Rm); - } - - Context.Emit(ILOp); - - Context.EmitStvecsf(Op.Rd); - } - - private static void EmitMathOp2(AILEmitterCtx Context, string Name) - { - AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp; - - Context.EmitLdvecsf(Op.Rn); - - EmitMathOpCall(Context, Name); - - Context.EmitStvecsf(Op.Rd); - } - - private static void EmitMathOp3(AILEmitterCtx Context, string Name) - { - AOpCodeSimdReg Op = (AOpCodeSimdReg)Context.CurrOp; - - Context.EmitLdvecsf(Op.Rn); - Context.EmitLdvecsf(Op.Rm); - - MethodInfo MthdInfo; - - if (Op.Size == 0) - { - MthdInfo = typeof(MathF).GetMethod(Name, new Type[] { typeof(float), typeof(float) }); - } - else if (Op.Size == 1) - { - MthdInfo = typeof(Math).GetMethod(Name, new Type[] { typeof(double), typeof(double) }); - } - else - { - throw new InvalidOperationException(); - } - - Context.EmitCall(MthdInfo); - - Context.EmitStvecsf(Op.Rd); - } - - public static void EmitMathOpCvtToInt(AILEmitterCtx Context, string Name) - { - AOpCodeSimdCvt Op = (AOpCodeSimdCvt)Context.CurrOp; - - Context.EmitLdvecsf(Op.Rn); - - EmitMathOpCall(Context, Name); - - EmitCvtToInt(Context, Op.Size); - - Context.EmitStintzr(Op.Rd); - } - - private static void EmitMathOpCall(AILEmitterCtx Context, string Name) - { - IAOpCodeSimd Op = (IAOpCodeSimd)Context.CurrOp; - - MethodInfo MthdInfo; - - if (Op.Size == 0) - { - MthdInfo = typeof(MathF).GetMethod(Name); - } - else if (Op.Size == 1) - { - MthdInfo = typeof(Math).GetMethod(Name); - } - else - { - throw new InvalidOperationException(); - } - - Context.EmitCall(MthdInfo); - } - - private static void EmitCvtToInt(AILEmitterCtx Context, int Size) - { - if (Size < 0 || Size > 1) - { - throw new ArgumentOutOfRangeException(nameof(Size)); - } - - Context.EmitLdc_I4(0); - - if (Context.CurrOp.RegisterSize == ARegisterSize.Int32) - { - if (Size == 0) - { - ASoftFallback.EmitCall(Context, nameof(ASoftFallback.SatSingleToInt32)); - } - else /* if (Size == 1) */ - { - ASoftFallback.EmitCall(Context, nameof(ASoftFallback.SatDoubleToInt32)); - } - } - else - { - if (Size == 0) - { - ASoftFallback.EmitCall(Context, nameof(ASoftFallback.SatSingleToInt64)); - } - else /* if (Size == 1) */ - { - ASoftFallback.EmitCall(Context, nameof(ASoftFallback.SatDoubleToInt64)); - } - } - } - - private static void EmitCvtToUInt(AILEmitterCtx Context, int Size) - { - if (Size < 0 || Size > 1) - { - throw new ArgumentOutOfRangeException(nameof(Size)); - } - - Context.EmitLdc_I4(0); - - if (Context.CurrOp.RegisterSize == ARegisterSize.Int32) - { - if (Size == 0) - { - ASoftFallback.EmitCall(Context, nameof(ASoftFallback.SatSingleToUInt32)); - } - else /* if (Size == 1) */ - { - ASoftFallback.EmitCall(Context, nameof(ASoftFallback.SatDoubleToUInt32)); - } - } - else - { - if (Size == 0) - { - ASoftFallback.EmitCall(Context, nameof(ASoftFallback.SatSingleToUInt64)); - } - else /* if (Size == 1) */ - { - ASoftFallback.EmitCall(Context, nameof(ASoftFallback.SatDoubleToUInt64)); - } - } - } - - private static void EmitFloatCast(AILEmitterCtx Context, int Size) - { - if (Size == 0) - { - Context.Emit(OpCodes.Conv_R4); - } - else if (Size == 1) - { - Context.Emit(OpCodes.Conv_R8); - } - else - { - throw new ArgumentOutOfRangeException(nameof(Size)); - } - } - - private static void EmitLdcImmF(AILEmitterCtx Context, double ImmF, int Size) - { - if (Size == 0) - { - Context.EmitLdc_R4((float)ImmF); - } - else if (Size == 1) - { - Context.EmitLdc_R8(ImmF); - } - else - { - throw new ArgumentOutOfRangeException(nameof(Size)); - } - } - - private static void EmitNaNCheck(AILEmitterCtx Context, int Index) - { - IAOpCodeSimd Op = (IAOpCodeSimd)Context.CurrOp; - - Context.EmitLdvecsf(Index); - - if (Op.Size == 0) - { - Context.EmitCall(typeof(float), nameof(float.IsNaN)); - } - else if (Op.Size == 1) - { - Context.EmitCall(typeof(double), nameof(double.IsNaN)); - } - else - { - throw new InvalidOperationException(); - } - } - } -} \ No newline at end of file diff --git a/Ryujinx/Cpu/Instruction/AInstEmitSimd.cs b/Ryujinx/Cpu/Instruction/AInstEmitSimd.cs deleted file mode 100644 index 08fd5807e..000000000 --- a/Ryujinx/Cpu/Instruction/AInstEmitSimd.cs +++ /dev/null @@ -1,1270 +0,0 @@ -using ChocolArm64.Decoder; -using ChocolArm64.State; -using ChocolArm64.Translation; -using System; -using System.Reflection; -using System.Reflection.Emit; - -using static ChocolArm64.Instruction.AInstEmitMemoryHelper; - -namespace ChocolArm64.Instruction -{ - static partial class AInstEmit - { - public static void Add_V(AILEmitterCtx Context) => EmitVectorBinaryZx(Context, OpCodes.Add); - - public static void Addp_V(AILEmitterCtx Context) - { - AOpCodeSimdReg Op = (AOpCodeSimdReg)Context.CurrOp; - - Context.EmitLdvec(Op.Rn); - Context.EmitLdvec(Op.Rm); - Context.EmitLdc_I4(Op.Size); - - ASoftFallback.EmitCall(Context, - nameof(ASoftFallback.Addp64), - nameof(ASoftFallback.Addp128)); - - Context.EmitStvec(Op.Rd); - } - - public static void Addv_V(AILEmitterCtx Context) => EmitVectorAddv(Context); - - public static void And_V(AILEmitterCtx Context) => EmitVectorBinaryZx(Context, OpCodes.And); - - public static void Bic_V(AILEmitterCtx Context) => EmitVectorBic(Context); - public static void Bic_Vi(AILEmitterCtx Context) - { - AOpCodeSimdImm Op = (AOpCodeSimdImm)Context.CurrOp; - - Context.EmitLdvec(Op.Rd); - Context.EmitLdc_I8(Op.Imm); - Context.EmitLdc_I4(Op.Size); - - ASoftFallback.EmitCall(Context, - nameof(ASoftFallback.Bic_Vi64), - nameof(ASoftFallback.Bic_Vi128)); - - Context.EmitStvec(Op.Rd); - } - - public static void Bsl_V(AILEmitterCtx Context) => EmitVectorBsl(Context); - - public static void Cmeq_V(AILEmitterCtx Context) => EmitVectorCmp(Context, OpCodes.Beq_S); - public static void Cmge_V(AILEmitterCtx Context) => EmitVectorCmp(Context, OpCodes.Bge_S); - public static void Cmgt_V(AILEmitterCtx Context) => EmitVectorCmp(Context, OpCodes.Bgt_S); - public static void Cmhi_V(AILEmitterCtx Context) => EmitVectorCmp(Context, OpCodes.Bgt_Un_S); - public static void Cmhs_V(AILEmitterCtx Context) => EmitVectorCmp(Context, OpCodes.Bge_Un_S); - public static void Cmle_V(AILEmitterCtx Context) => EmitVectorCmp(Context, OpCodes.Ble_S); - public static void Cmlt_V(AILEmitterCtx Context) => EmitVectorCmp(Context, OpCodes.Blt_S); - - public static void Cnt_V(AILEmitterCtx Context) - { - AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp; - - Context.EmitLdvec(Op.Rn); - - ASoftFallback.EmitCall(Context, - nameof(ASoftFallback.Cnt64), - nameof(ASoftFallback.Cnt128)); - - Context.EmitStvec(Op.Rd); - } - - public static void Dup_Gp(AILEmitterCtx Context) - { - AOpCodeSimdIns Op = (AOpCodeSimdIns)Context.CurrOp; - - Context.EmitLdintzr(Op.Rn); - Context.EmitLdc_I4(Op.Size); - - ASoftFallback.EmitCall(Context, - nameof(ASoftFallback.Dup_Gp64), - nameof(ASoftFallback.Dup_Gp128)); - - Context.EmitStvec(Op.Rd); - } - - public static void Dup_V(AILEmitterCtx Context) - { - AOpCodeSimdIns Op = (AOpCodeSimdIns)Context.CurrOp; - - Context.EmitLdvec(Op.Rn); - Context.EmitLdc_I4(Op.DstIndex); - Context.EmitLdc_I4(Op.Size); - - ASoftFallback.EmitCall(Context, - nameof(ASoftFallback.Dup_V64), - nameof(ASoftFallback.Dup_V128)); - - Context.EmitStvec(Op.Rd); - } - - public static void Eor_V(AILEmitterCtx Context) => EmitVectorBinaryZx(Context, OpCodes.Xor); - - public static void Fadd_V(AILEmitterCtx Context) => EmitVectorBinaryFOp(Context, OpCodes.Add); - - public static void Fcvtzs_V(AILEmitterCtx Context) => EmitVectorFcvts(Context); - public static void Fcvtzu_V(AILEmitterCtx Context) => EmitVectorFcvtu(Context); - - public static void Fmla_V(AILEmitterCtx Context) - { - AOpCodeSimdReg Op = (AOpCodeSimdReg)Context.CurrOp; - - Context.EmitLdvec(Op.Rd); - Context.EmitLdvec(Op.Rn); - Context.EmitLdvec(Op.Rm); - Context.EmitLdc_I4(Op.SizeF); - - ASoftFallback.EmitCall(Context, - nameof(ASoftFallback.Fmla64), - nameof(ASoftFallback.Fmla128)); - - Context.EmitStvec(Op.Rd); - } - - public static void Fmla_Vs(AILEmitterCtx Context) - { - AOpCodeSimdRegElem Op = (AOpCodeSimdRegElem)Context.CurrOp; - - Context.EmitLdvec(Op.Rd); - Context.EmitLdvec(Op.Rn); - Context.EmitLdvec(Op.Rm); - Context.EmitLdc_I4(Op.Index); - Context.EmitLdc_I4(Op.SizeF); - - ASoftFallback.EmitCall(Context, - nameof(ASoftFallback.Fmla_Ve64), - nameof(ASoftFallback.Fmla_Ve128)); - - Context.EmitStvec(Op.Rd); - } - - public static void Fmov_V(AILEmitterCtx Context) - { - AOpCodeSimdImm Op = (AOpCodeSimdImm)Context.CurrOp; - - Context.EmitLdc_I8(Op.Imm); - Context.EmitLdc_I4(Op.Size + 2); - - ASoftFallback.EmitCall(Context, - nameof(ASoftFallback.Dup_Gp64), - nameof(ASoftFallback.Dup_Gp128)); - - Context.EmitStvec(Op.Rd); - } - - public static void Fmul_V(AILEmitterCtx Context) => EmitVectorBinaryFOp(Context, OpCodes.Mul); - - public static void Fmul_Vs(AILEmitterCtx Context) - { - AOpCodeSimdRegElem Op = (AOpCodeSimdRegElem)Context.CurrOp; - - Context.EmitLdvec(Op.Rn); - Context.EmitLdvec(Op.Rm); - Context.EmitLdc_I4(Op.Index); - Context.EmitLdc_I4(Op.SizeF); - - ASoftFallback.EmitCall(Context, - nameof(ASoftFallback.Fmul_Ve64), - nameof(ASoftFallback.Fmul_Ve128)); - - Context.EmitStvec(Op.Rd); - } - - public static void Fsub_V(AILEmitterCtx Context) => EmitVectorBinaryFOp(Context, OpCodes.Sub); - - public static void Ins_Gp(AILEmitterCtx Context) - { - AOpCodeSimdIns Op = (AOpCodeSimdIns)Context.CurrOp; - - Context.EmitLdvec(Op.Rd); - Context.EmitLdintzr(Op.Rn); - Context.EmitLdc_I4(Op.DstIndex); - Context.EmitLdc_I4(Op.Size); - - ASoftFallback.EmitCall(Context, nameof(ASoftFallback.Ins_Gp)); - - Context.EmitStvec(Op.Rd); - } - - public static void Ins_V(AILEmitterCtx Context) - { - AOpCodeSimdIns Op = (AOpCodeSimdIns)Context.CurrOp; - - Context.EmitLdvec(Op.Rd); - Context.EmitLdvec(Op.Rn); - Context.EmitLdc_I4(Op.SrcIndex); - Context.EmitLdc_I4(Op.DstIndex); - Context.EmitLdc_I4(Op.Size); - - ASoftFallback.EmitCall(Context, nameof(ASoftFallback.Ins_V)); - - Context.EmitStvec(Op.Rd); - } - - public static void Ld__Vms(AILEmitterCtx Context) => EmitSimdMemMs(Context, IsLoad: true); - public static void Ld__Vss(AILEmitterCtx Context) => EmitSimdMemSs(Context, IsLoad: true); - - public static void Mla_V(AILEmitterCtx Context) => EmitVectorMla(Context); - - public static void Movi_V(AILEmitterCtx Context) => EmitMovi_V(Context, false); - - public static void Mul_V(AILEmitterCtx Context) => EmitVectorBinaryZx(Context, OpCodes.Mul); - - public static void Mvni_V(AILEmitterCtx Context) => EmitMovi_V(Context, true); - - private static void EmitMovi_V(AILEmitterCtx Context, bool Not) - { - AOpCodeSimdImm Op = (AOpCodeSimdImm)Context.CurrOp; - - Context.EmitLdc_I8(Not ? ~Op.Imm : Op.Imm); - Context.EmitLdc_I4(Op.Size); - - ASoftFallback.EmitCall(Context, - nameof(ASoftFallback.Dup_Gp64), - nameof(ASoftFallback.Dup_Gp128)); - - Context.EmitStvec(Op.Rd); - } - - public static void Neg_V(AILEmitterCtx Context) => EmitVectorUnarySx(Context, OpCodes.Neg); - - public static void Not_V(AILEmitterCtx Context) => EmitVectorUnaryZx(Context, OpCodes.Not); - - public static void Orr_V(AILEmitterCtx Context) => EmitVectorBinaryZx(Context, OpCodes.Or); - - public static void Orr_Vi(AILEmitterCtx Context) - { - AOpCodeSimdImm Op = (AOpCodeSimdImm)Context.CurrOp; - - Context.EmitLdvec(Op.Rd); - Context.EmitLdc_I8(Op.Imm); - Context.EmitLdc_I4(Op.Size); - - ASoftFallback.EmitCall(Context, - nameof(ASoftFallback.Orr_Vi64), - nameof(ASoftFallback.Orr_Vi128)); - - Context.EmitStvec(Op.Rd); - } - - public static void Saddw_V(AILEmitterCtx Context) - { - AOpCodeSimdReg Op = (AOpCodeSimdReg)Context.CurrOp; - - Context.EmitLdvec(Op.Rn); - Context.EmitLdvec(Op.Rm); - Context.EmitLdc_I4(Op.Size); - - ASoftFallback.EmitCall(Context, - nameof(ASoftFallback.Saddw), - nameof(ASoftFallback.Saddw2)); - - Context.EmitStvec(Op.Rd); - } - - public static void Scvtf_V(AILEmitterCtx Context) => EmitVectorScvtf(Context); - - public static void Shl_V(AILEmitterCtx Context) - { - AOpCodeSimdShImm Op = (AOpCodeSimdShImm)Context.CurrOp; - - EmitVectorImmBinaryZx(Context, OpCodes.Shl, Op.Imm - (8 << Op.Size)); - } - - public static void Shrn_V(AILEmitterCtx Context) - { - AOpCodeSimdShImm Op = (AOpCodeSimdShImm)Context.CurrOp; - - EmitVectorImmNarrowBinaryZx(Context, OpCodes.Shr_Un, (8 << (Op.Size + 1)) - Op.Imm); - } - - public static void Smax_V(AILEmitterCtx Context) => EmitVectorSmax(Context); - public static void Smin_V(AILEmitterCtx Context) => EmitVectorSmin(Context); - - public static void Sshl_V(AILEmitterCtx Context) => EmitVectorSshl(Context); - - public static void Sshll_V(AILEmitterCtx Context) - { - AOpCodeSimdShImm Op = (AOpCodeSimdShImm)Context.CurrOp; - - Context.EmitLdvec(Op.Rn); - Context.EmitLdc_I4(Op.Imm - (8 << Op.Size)); - Context.EmitLdc_I4(Op.Size); - - ASoftFallback.EmitCall(Context, - nameof(ASoftFallback.Sshll), - nameof(ASoftFallback.Sshll2)); - - Context.EmitStvec(Op.Rd); - } - - public static void Sshr_V(AILEmitterCtx Context) - { - AOpCodeSimdShImm Op = (AOpCodeSimdShImm)Context.CurrOp; - - EmitVectorImmBinarySx(Context, OpCodes.Shr, (8 << (Op.Size + 1)) - Op.Imm); - } - - public static void St__Vms(AILEmitterCtx Context) => EmitSimdMemMs(Context, IsLoad: false); - public static void St__Vss(AILEmitterCtx Context) => EmitSimdMemSs(Context, IsLoad: false); - - public static void Sub_V(AILEmitterCtx Context) => EmitVectorBinaryZx(Context, OpCodes.Sub); - - public static void Tbl_V(AILEmitterCtx Context) - { - AOpCodeSimdTbl Op = (AOpCodeSimdTbl)Context.CurrOp; - - Context.EmitLdvec(Op.Rm); - - for (int Index = 0; Index < Op.Size; Index++) - { - Context.EmitLdvec((Op.Rn + Index) & 0x1f); - } - - switch (Op.Size) - { - case 1: ASoftFallback.EmitCall(Context, - nameof(ASoftFallback.Tbl1_V64), - nameof(ASoftFallback.Tbl1_V128)); break; - - case 2: ASoftFallback.EmitCall(Context, - nameof(ASoftFallback.Tbl2_V64), - nameof(ASoftFallback.Tbl2_V128)); break; - - case 3: ASoftFallback.EmitCall(Context, - nameof(ASoftFallback.Tbl3_V64), - nameof(ASoftFallback.Tbl3_V128)); break; - - case 4: ASoftFallback.EmitCall(Context, - nameof(ASoftFallback.Tbl4_V64), - nameof(ASoftFallback.Tbl4_V128)); break; - - default: throw new InvalidOperationException(); - } - - Context.EmitStvec(Op.Rd); - } - - public static void Uaddlv_V(AILEmitterCtx Context) - { - AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp; - - Context.EmitLdvec(Op.Rn); - Context.EmitLdc_I4(Op.Size); - - ASoftFallback.EmitCall(Context, - nameof(ASoftFallback.Uaddlv64), - nameof(ASoftFallback.Uaddlv128)); - - Context.EmitStvec(Op.Rd); - } - - public static void Uaddw_V(AILEmitterCtx Context) - { - AOpCodeSimdReg Op = (AOpCodeSimdReg)Context.CurrOp; - - Context.EmitLdvec(Op.Rn); - Context.EmitLdvec(Op.Rm); - Context.EmitLdc_I4(Op.Size); - - ASoftFallback.EmitCall(Context, - nameof(ASoftFallback.Uaddw), - nameof(ASoftFallback.Uaddw2)); - - Context.EmitStvec(Op.Rd); - } - - public static void Ucvtf_V(AILEmitterCtx Context) - { - AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp; - - Context.EmitLdvec(Op.Rn); - - if (Op.Size == 0) - { - ASoftFallback.EmitCall(Context, nameof(ASoftFallback.Ucvtf_V_F)); - } - else if (Op.Size == 1) - { - ASoftFallback.EmitCall(Context, nameof(ASoftFallback.Ucvtf_V_D)); - } - else - { - throw new InvalidOperationException(); - } - - Context.EmitStvec(Op.Rd); - } - - public static void Umov_S(AILEmitterCtx Context) - { - AOpCodeSimdIns Op = (AOpCodeSimdIns)Context.CurrOp; - - Context.EmitLdvec(Op.Rn); - Context.EmitLdc_I4(Op.DstIndex); - Context.EmitLdc_I4(Op.Size); - - ASoftFallback.EmitCall(Context, nameof(ASoftFallback.ExtractVec)); - - Context.EmitStintzr(Op.Rd); - } - - public static void Ushl_V(AILEmitterCtx Context) => EmitVectorUshl(Context); - - public static void Ushll_V(AILEmitterCtx Context) - { - AOpCodeSimdShImm Op = (AOpCodeSimdShImm)Context.CurrOp; - - Context.EmitLdvec(Op.Rn); - Context.EmitLdc_I4(Op.Imm - (8 << Op.Size)); - Context.EmitLdc_I4(Op.Size); - - ASoftFallback.EmitCall(Context, - nameof(ASoftFallback.Ushll), - nameof(ASoftFallback.Ushll2)); - - Context.EmitStvec(Op.Rd); - } - - public static void Ushr_V(AILEmitterCtx Context) - { - EmitVectorShr(Context, ShrFlags.None); - } - - public static void Usra_V(AILEmitterCtx Context) - { - EmitVectorShr(Context, ShrFlags.Accumulate); - } - - public static void Uzp1_V(AILEmitterCtx Context) - { - AOpCodeSimdReg Op = (AOpCodeSimdReg)Context.CurrOp; - - Context.EmitLdvec(Op.Rn); - Context.EmitLdvec(Op.Rm); - Context.EmitLdc_I4(Op.Size); - - ASoftFallback.EmitCall(Context, - nameof(ASoftFallback.Uzp1_V64), - nameof(ASoftFallback.Uzp1_V128)); - - Context.EmitStvec(Op.Rd); - } - - public static void Xtn_V(AILEmitterCtx Context) - { - AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp; - - Context.EmitLdvec(Op.Rn); - Context.EmitLdc_I4(Op.Size); - - ASoftFallback.EmitCall(Context, - nameof(ASoftFallback.Xtn), - nameof(ASoftFallback.Xtn2)); - - Context.EmitStvec(Op.Rd); - } - - private static void EmitSimdMemMs(AILEmitterCtx Context, bool IsLoad) - { - AOpCodeSimdMemMs Op = (AOpCodeSimdMemMs)Context.CurrOp; - - int Offset = 0; - - for (int Rep = 0; Rep < Op.Reps; Rep++) - for (int Elem = 0; Elem < Op.Elems; Elem++) - for (int SElem = 0; SElem < Op.SElems; SElem++) - { - int Rtt = (Op.Rt + Rep + SElem) & 0x1f; - - if (IsLoad) - { - Context.EmitLdvec(Rtt); - Context.EmitLdc_I4(Elem); - Context.EmitLdc_I4(Op.Size); - Context.EmitLdarg(ATranslatedSub.MemoryArgIdx); - Context.EmitLdint(Op.Rn); - Context.EmitLdc_I8(Offset); - - Context.Emit(OpCodes.Add); - - EmitReadZxCall(Context, Op.Size); - - ASoftFallback.EmitCall(Context, nameof(ASoftFallback.InsertVec)); - - Context.EmitStvec(Rtt); - - if (Op.RegisterSize == ARegisterSize.SIMD64 && Elem == Op.Elems - 1) - { - EmitVectorZeroUpper(Context, Rtt); - } - } - else - { - Context.EmitLdarg(ATranslatedSub.MemoryArgIdx); - Context.EmitLdint(Op.Rn); - Context.EmitLdc_I8(Offset); - - Context.Emit(OpCodes.Add); - - Context.EmitLdvec(Rtt); - Context.EmitLdc_I4(Elem); - Context.EmitLdc_I4(Op.Size); - - ASoftFallback.EmitCall(Context, nameof(ASoftFallback.ExtractVec)); - - EmitWriteCall(Context, Op.Size); - } - - Offset += 1 << Op.Size; - } - - if (Op.WBack) - { - Context.EmitLdint(Op.Rn); - - if (Op.Rm != ARegisters.ZRIndex) - { - Context.EmitLdint(Op.Rm); - } - else - { - Context.EmitLdc_I8(Offset); - } - - Context.Emit(OpCodes.Add); - - Context.EmitStint(Op.Rn); - } - } - - private static void EmitSimdMemSs(AILEmitterCtx Context, bool IsLoad) - { - AOpCodeSimdMemSs Op = (AOpCodeSimdMemSs)Context.CurrOp; - - //TODO: Replicate mode. - - int Offset = 0; - - for (int SElem = 0; SElem < Op.SElems; SElem++) - { - int Rt = (Op.Rt + SElem) & 0x1f; - - if (IsLoad) - { - Context.EmitLdvec(Rt); - Context.EmitLdc_I4(Op.Index); - Context.EmitLdc_I4(Op.Size); - Context.EmitLdarg(ATranslatedSub.MemoryArgIdx); - Context.EmitLdint(Op.Rn); - Context.EmitLdc_I8(Offset); - - Context.Emit(OpCodes.Add); - - EmitReadZxCall(Context, Op.Size); - - ASoftFallback.EmitCall(Context, nameof(ASoftFallback.InsertVec)); - - Context.EmitStvec(Rt); - - if (Op.RegisterSize == ARegisterSize.SIMD64) - { - EmitVectorZeroUpper(Context, Rt); - } - } - else - { - Context.EmitLdarg(ATranslatedSub.MemoryArgIdx); - Context.EmitLdint(Op.Rn); - Context.EmitLdc_I8(Offset); - - Context.Emit(OpCodes.Add); - - Context.EmitLdvec(Rt); - Context.EmitLdc_I4(Op.Index); - Context.EmitLdc_I4(Op.Size); - - ASoftFallback.EmitCall(Context, nameof(ASoftFallback.ExtractVec)); - - EmitWriteCall(Context, Op.Size); - } - - Offset += 1 << Op.Size; - } - - if (Op.WBack) - { - Context.EmitLdint(Op.Rn); - - if (Op.Rm != ARegisters.ZRIndex) - { - Context.EmitLdint(Op.Rm); - } - else - { - Context.EmitLdc_I8(Offset); - } - - Context.Emit(OpCodes.Add); - - Context.EmitStint(Op.Rn); - } - } - - private static void EmitVectorAddv(AILEmitterCtx Context) - { - AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp; - - int Bytes = Context.CurrOp.GetBitsCount() >> 3; - - EmitVectorZeroLower(Context, Op.Rd); - EmitVectorZeroUpper(Context, Op.Rd); - - Context.EmitLdvec(Op.Rd); - Context.EmitLdc_I4(0); - Context.EmitLdc_I4(Op.Size); - - EmitVectorExtractZx(Context, Op.Rn, 0, Op.Size); - - for (int Index = 1; Index < (Bytes >> Op.Size); Index++) - { - EmitVectorExtractZx(Context, Op.Rn, Index, Op.Size); - - Context.Emit(OpCodes.Add); - } - - ASoftFallback.EmitCall(Context, nameof(ASoftFallback.InsertVec)); - - Context.EmitStvec(Op.Rd); - } - - private static void EmitVectorBic(AILEmitterCtx Context) - { - EmitVectorBinaryZx(Context, () => - { - Context.Emit(OpCodes.Not); - Context.Emit(OpCodes.And); - }); - } - - private static void EmitVectorBsl(AILEmitterCtx Context) - { - EmitVectorTernaryZx(Context, () => - { - Context.EmitSttmp(); - Context.EmitLdtmp(); - - Context.Emit(OpCodes.Xor); - Context.Emit(OpCodes.And); - - Context.EmitLdtmp(); - - Context.Emit(OpCodes.Xor); - }); - } - - private static void EmitVectorMla(AILEmitterCtx Context) - { - EmitVectorTernaryZx(Context, () => - { - Context.Emit(OpCodes.Mul); - Context.Emit(OpCodes.Add); - }); - } - - private static void EmitVectorSmax(AILEmitterCtx Context) - { - Type[] Types = new Type[] { typeof(long), typeof(long) }; - - MethodInfo MthdInfo = typeof(Math).GetMethod(nameof(Math.Max), Types); - - EmitVectorBinarySx(Context, () => Context.EmitCall(MthdInfo)); - } - - private static void EmitVectorSmin(AILEmitterCtx Context) - { - Type[] Types = new Type[] { typeof(long), typeof(long) }; - - MethodInfo MthdInfo = typeof(Math).GetMethod(nameof(Math.Min), Types); - - EmitVectorBinarySx(Context, () => Context.EmitCall(MthdInfo)); - } - - private static void EmitVectorSshl(AILEmitterCtx Context) => EmitVectorShl(Context, true); - private static void EmitVectorUshl(AILEmitterCtx Context) => EmitVectorShl(Context, false); - - private static void EmitVectorShl(AILEmitterCtx Context, bool Signed) - { - //This instruction shifts the value on vector A by the number of bits - //specified on the signed, lower 8 bits of vector B. If the shift value - //is greater or equal to the data size of each lane, then the result is zero. - //Additionally, negative shifts produces right shifts by the negated shift value. - AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp; - - int MaxShift = 8 << Op.Size; - - Action Emit = () => - { - AILLabel LblShl = new AILLabel(); - AILLabel LblZero = new AILLabel(); - AILLabel LblEnd = new AILLabel(); - - void EmitShift(OpCode ILOp) - { - Context.Emit(OpCodes.Dup); - - Context.EmitLdc_I4(MaxShift); - - Context.Emit(OpCodes.Bge_S, LblZero); - Context.Emit(ILOp); - Context.Emit(OpCodes.Br_S, LblEnd); - } - - Context.Emit(OpCodes.Conv_I1); - Context.Emit(OpCodes.Dup); - - Context.EmitLdc_I4(0); - - Context.Emit(OpCodes.Bge_S, LblShl); - Context.Emit(OpCodes.Neg); - - EmitShift(Signed - ? OpCodes.Shr - : OpCodes.Shr_Un); - - Context.MarkLabel(LblShl); - - EmitShift(OpCodes.Shl); - - Context.MarkLabel(LblZero); - - Context.Emit(OpCodes.Pop); - Context.Emit(OpCodes.Pop); - - Context.EmitLdc_I8(0); - - Context.MarkLabel(LblEnd); - }; - - if (Signed) - { - EmitVectorBinarySx(Context, Emit); - } - else - { - EmitVectorBinaryZx(Context, Emit); - } - } - - private enum ShrFlags - { - None = 0, - Signed = 1 << 0, - Rounding = 1 << 1, - Accumulate = 1 << 2 - } - - private static void EmitVectorShr(AILEmitterCtx Context, ShrFlags Flags) - { - AOpCodeSimdShImm Op = (AOpCodeSimdShImm)Context.CurrOp; - - int Shift = (8 << (Op.Size + 1)) - Op.Imm; - - if (Flags.HasFlag(ShrFlags.Accumulate)) - { - Action Emit = () => - { - Context.EmitLdc_I4(Shift); - - Context.Emit(OpCodes.Shr_Un); - Context.Emit(OpCodes.Add); - }; - - EmitVectorOp(Context, Emit, OperFlags.RdRn, Signed: false); - } - else - { - EmitVectorUnaryZx(Context, () => - { - Context.EmitLdc_I4(Shift); - - Context.Emit(OpCodes.Shr_Un); - }); - } - } - - private static void EmitVectorFcvts(AILEmitterCtx Context) - { - EmitVectorFcvtOp(Context, Signed: true); - } - - private static void EmitVectorFcvtu(AILEmitterCtx Context) - { - EmitVectorFcvtOp(Context, Signed: false); - } - - private static void EmitVectorScvtf(AILEmitterCtx Context) - { - EmitVectorCvtfOp(Context, Signed: true); - } - - private static void EmitVectorUcvtf(AILEmitterCtx Context) - { - EmitVectorCvtfOp(Context, Signed: false); - } - - private static void EmitVectorFcvtOp(AILEmitterCtx Context, bool Signed) - { - AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp; - - int SizeF = Op.Size & 1; - int SizeI = SizeF + 2; - - int FBits = GetFBits(Context); - - int Bytes = Context.CurrOp.GetBitsCount() >> 3; - - for (int Index = 0; Index < (Bytes >> SizeI); Index++) - { - EmitVectorExtractF(Context, Op.Rn, Index, SizeF); - - Context.EmitLdc_I4(FBits); - - if (SizeF == 0) - { - ASoftFallback.EmitCall(Context, Signed - ? nameof(ASoftFallback.SatSingleToInt32) - : nameof(ASoftFallback.SatSingleToUInt32)); - } - else if (SizeF == 1) - { - ASoftFallback.EmitCall(Context, Signed - ? nameof(ASoftFallback.SatDoubleToInt64) - : nameof(ASoftFallback.SatDoubleToUInt64)); - } - - EmitVectorInsert(Context, Op.Rd, Index, SizeI); - } - - if (Op.RegisterSize == ARegisterSize.SIMD64) - { - EmitVectorZeroUpper(Context, Op.Rd); - } - } - - private static void EmitVectorCvtfOp(AILEmitterCtx Context, bool Signed) - { - AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp; - - int SizeF = Op.Size & 1; - int SizeI = SizeF + 2; - - int FBits = GetFBits(Context); - - int Bytes = Context.CurrOp.GetBitsCount() >> 3; - - for (int Index = 0; Index < (Bytes >> SizeI); Index++) - { - EmitVectorExtract(Context, Op.Rn, Index, SizeI, Signed); - - Context.EmitLdc_I4(FBits); - - if (SizeF == 0) - { - Context.Emit(OpCodes.Conv_I4); - - ASoftFallback.EmitCall(Context, Signed - ? nameof(ASoftFallback.Int32ToSingle) - : nameof(ASoftFallback.UInt32ToSingle)); - } - else if (SizeF == 1) - { - ASoftFallback.EmitCall(Context, Signed - ? nameof(ASoftFallback.Int64ToDouble) - : nameof(ASoftFallback.UInt64ToDouble)); - } - - EmitVectorInsertF(Context, Op.Rd, Index, SizeF); - } - - if (Op.RegisterSize == ARegisterSize.SIMD64) - { - EmitVectorZeroUpper(Context, Op.Rd); - } - } - - private static int GetFBits(AILEmitterCtx Context) - { - if (Context.CurrOp is AOpCodeSimdShImm Op) - { - return (8 << (Op.Size + 1)) - Op.Imm; - } - - return 0; - } - - private static void EmitVectorBinaryFOp(AILEmitterCtx Context, OpCode ILOp) - { - EmitVectorBinaryFOp(Context, () => Context.Emit(ILOp)); - } - - private static void EmitVectorBinaryFOp(AILEmitterCtx Context, Action Emit) - { - AOpCodeSimdReg Op = (AOpCodeSimdReg)Context.CurrOp; - - int SizeF = Op.Size & 1; - - int Bytes = Context.CurrOp.GetBitsCount() >> 3; - - for (int Index = 0; Index < (Bytes >> SizeF + 2); Index++) - { - EmitVectorExtractF(Context, Op.Rn, Index, SizeF); - EmitVectorExtractF(Context, Op.Rm, Index, SizeF); - - Emit(); - - EmitVectorInsertF(Context, Op.Rd, Index, SizeF); - } - - if (Op.RegisterSize == ARegisterSize.SIMD64) - { - EmitVectorZeroUpper(Context, Op.Rd); - } - } - - private static void EmitVectorUnarySx(AILEmitterCtx Context, OpCode ILOp) - { - EmitVectorUnarySx(Context, () => Context.Emit(ILOp)); - } - - private static void EmitVectorUnaryZx(AILEmitterCtx Context, OpCode ILOp) - { - EmitVectorUnaryZx(Context, () => Context.Emit(ILOp)); - } - - private static void EmitVectorBinaryZx(AILEmitterCtx Context, OpCode ILOp) - { - EmitVectorBinaryZx(Context, () => Context.Emit(ILOp)); - } - - private static void EmitVectorUnarySx(AILEmitterCtx Context, Action Emit) - { - EmitVectorOp(Context, Emit, OperFlags.Rn, true); - } - - private static void EmitVectorBinarySx(AILEmitterCtx Context, Action Emit) - { - EmitVectorOp(Context, Emit, OperFlags.RnRm, true); - } - - private static void EmitVectorUnaryZx(AILEmitterCtx Context, Action Emit) - { - EmitVectorOp(Context, Emit, OperFlags.Rn, false); - } - - private static void EmitVectorBinaryZx(AILEmitterCtx Context, Action Emit) - { - EmitVectorOp(Context, Emit, OperFlags.RnRm, false); - } - - private static void EmitVectorTernaryZx(AILEmitterCtx Context, Action Emit) - { - EmitVectorOp(Context, Emit, OperFlags.RdRnRm, false); - } - - [Flags] - private enum OperFlags - { - Rd = 1 << 0, - Rn = 1 << 1, - Rm = 1 << 2, - - RnRm = Rn | Rm, - RdRn = Rd | Rn, - RdRnRm = Rd | Rn | Rm - } - - private static void EmitVectorOp(AILEmitterCtx Context, Action Emit, OperFlags Opers, bool Signed) - { - AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp; - - int Bytes = Context.CurrOp.GetBitsCount() >> 3; - - for (int Index = 0; Index < (Bytes >> Op.Size); Index++) - { - if (Opers.HasFlag(OperFlags.Rd)) - { - EmitVectorExtract(Context, Op.Rd, Index, Op.Size, Signed); - } - - if (Opers.HasFlag(OperFlags.Rn)) - { - EmitVectorExtract(Context, Op.Rn, Index, Op.Size, Signed); - } - - if (Opers.HasFlag(OperFlags.Rm)) - { - EmitVectorExtract(Context, ((AOpCodeSimdReg)Op).Rm, Index, Op.Size, Signed); - } - - Emit(); - - EmitVectorInsert(Context, Op.Rd, Index, Op.Size); - } - - if (Op.RegisterSize == ARegisterSize.SIMD64) - { - EmitVectorZeroUpper(Context, Op.Rd); - } - } - - private static void EmitVectorImmBinarySx(AILEmitterCtx Context, OpCode ILOp, int Imm) - { - EmitVectorImmBinarySx(Context, () => Context.Emit(ILOp), Imm); - } - - private static void EmitVectorImmBinaryZx(AILEmitterCtx Context, OpCode ILOp, int Imm) - { - EmitVectorImmBinaryZx(Context, () => Context.Emit(ILOp), Imm); - } - - private static void EmitVectorImmBinarySx(AILEmitterCtx Context, Action Emit, int Imm) - { - EmitVectorImmBinaryOp(Context, Emit, Imm, true); - } - - private static void EmitVectorImmBinaryZx(AILEmitterCtx Context, Action Emit, int Imm) - { - EmitVectorImmBinaryOp(Context, Emit, Imm, false); - } - - private static void EmitVectorImmBinaryOp(AILEmitterCtx Context, Action Emit, int Imm, bool Signed) - { - AOpCodeSimdShImm Op = (AOpCodeSimdShImm)Context.CurrOp; - - int Bytes = Context.CurrOp.GetBitsCount() >> 3; - - for (int Index = 0; Index < (Bytes >> Op.Size); Index++) - { - EmitVectorExtract(Context, Op.Rn, Index, Op.Size, Signed); - - Context.EmitLdc_I4(Imm); - - Emit(); - - EmitVectorInsert(Context, Op.Rd, Index, Op.Size); - } - - if (Op.RegisterSize == ARegisterSize.SIMD64) - { - EmitVectorZeroUpper(Context, Op.Rd); - } - } - - private static void EmitVectorImmNarrowBinarySx(AILEmitterCtx Context, OpCode ILOp, int Imm) - { - EmitVectorImmNarrowBinarySx(Context, () => Context.Emit(ILOp), Imm); - } - - private static void EmitVectorImmNarrowBinaryZx(AILEmitterCtx Context, OpCode ILOp, int Imm) - { - EmitVectorImmNarrowBinaryZx(Context, () => Context.Emit(ILOp), Imm); - } - - private static void EmitVectorImmNarrowBinarySx(AILEmitterCtx Context, Action Emit, int Imm) - { - EmitVectorImmNarrowBinaryOp(Context, Emit, Imm, true); - } - - private static void EmitVectorImmNarrowBinaryZx(AILEmitterCtx Context, Action Emit, int Imm) - { - EmitVectorImmNarrowBinaryOp(Context, Emit, Imm, false); - } - - private static void EmitVectorImmNarrowBinaryOp(AILEmitterCtx Context, Action Emit, int Imm, bool Signed) - { - AOpCodeSimdShImm Op = (AOpCodeSimdShImm)Context.CurrOp; - - if (Op.Size < 0 || Op.Size > 2) - { - throw new InvalidOperationException(Op.Size.ToString()); - } - - int Elems = 8 >> Op.Size; - - int Part = Op.RegisterSize == ARegisterSize.SIMD128 ? Elems : 0; - - for (int Index = 0; Index < Elems; Index++) - { - EmitVectorExtract(Context, Op.Rn, Index, Op.Size + 1, Signed); - - Context.EmitLdc_I4(Imm); - - Emit(); - - EmitVectorInsert(Context, Op.Rd, Part + Index, Op.Size); - } - - if (Part == 0) - { - EmitVectorZeroUpper(Context, Op.Rd); - } - } - - private static void EmitVectorCmp(AILEmitterCtx Context, OpCode ILOp) - { - AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp; - - int Bytes = Context.CurrOp.GetBitsCount() >> 3; - - ulong SzMask = ulong.MaxValue >> (64 - (8 << Op.Size)); - - for (int Index = 0; Index < (Bytes >> Op.Size); Index++) - { - EmitVectorExtractSx(Context, Op.Rn, Index, Op.Size); - - if (Op is AOpCodeSimdReg BinOp) - { - EmitVectorExtractSx(Context, BinOp.Rm, Index, Op.Size); - } - else - { - Context.EmitLdc_I8(0); - } - - AILLabel LblTrue = new AILLabel(); - AILLabel LblEnd = new AILLabel(); - - Context.Emit(ILOp, LblTrue); - - EmitVectorInsert(Context, Op.Rd, Index, Op.Size, 0); - - Context.Emit(OpCodes.Br_S, LblEnd); - - Context.MarkLabel(LblTrue); - - EmitVectorInsert(Context, Op.Rd, Index, Op.Size, (long)SzMask); - - Context.MarkLabel(LblEnd); - } - - if (Op.RegisterSize == ARegisterSize.SIMD64) - { - EmitVectorZeroUpper(Context, Op.Rd); - } - } - - private static void EmitVectorExtractF(AILEmitterCtx Context, int Reg, int Index, int Size) - { - Context.EmitLdvec(Reg); - Context.EmitLdc_I4(Index); - - if (Size == 0) - { - ASoftFallback.EmitCall(Context, nameof(ASoftFallback.VectorExtractSingle)); - } - else if (Size == 1) - { - ASoftFallback.EmitCall(Context, nameof(ASoftFallback.VectorExtractDouble)); - } - else - { - throw new ArgumentOutOfRangeException(nameof(Size)); - } - } - - private static void EmitVectorExtractSx(AILEmitterCtx Context, int Reg, int Index, int Size) - { - EmitVectorExtract(Context, Reg, Index, Size, true); - } - - private static void EmitVectorExtractZx(AILEmitterCtx Context, int Reg, int Index, int Size) - { - EmitVectorExtract(Context, Reg, Index, Size, false); - } - - private static void EmitVectorExtract(AILEmitterCtx Context, int Reg, int Index, int Size, bool Signed) - { - if (Size < 0 || Size > 3) - { - throw new ArgumentOutOfRangeException(nameof(Size)); - } - - IAOpCodeSimd Op = (IAOpCodeSimd)Context.CurrOp; - - Context.EmitLdvec(Reg); - Context.EmitLdc_I4(Index); - Context.EmitLdc_I4(Size); - - ASoftFallback.EmitCall(Context, Signed - ? nameof(ASoftFallback.ExtractSVec) - : nameof(ASoftFallback.ExtractVec)); - } - - private static void EmitVectorZeroLower(AILEmitterCtx Context, int Rd) - { - EmitVectorInsert(Context, Rd, 0, 3, 0); - } - - private static void EmitVectorZeroUpper(AILEmitterCtx Context, int Rd) - { - EmitVectorInsert(Context, Rd, 1, 3, 0); - } - - private static void EmitVectorInsertF(AILEmitterCtx Context, int Reg, int Index, int Size) - { - Context.EmitLdvec(Reg); - Context.EmitLdc_I4(Index); - - if (Size == 0) - { - ASoftFallback.EmitCall(Context, nameof(ASoftFallback.VectorInsertSingle)); - } - else if (Size == 1) - { - ASoftFallback.EmitCall(Context, nameof(ASoftFallback.VectorInsertDouble)); - } - else - { - throw new ArgumentOutOfRangeException(nameof(Size)); - } - - Context.EmitStvec(Reg); - } - - private static void EmitVectorInsert(AILEmitterCtx Context, int Reg, int Index, int Size) - { - if (Size < 0 || Size > 3) - { - throw new ArgumentOutOfRangeException(nameof(Size)); - } - - Context.EmitLdvec(Reg); - Context.EmitLdc_I4(Index); - Context.EmitLdc_I4(Size); - - ASoftFallback.EmitCall(Context, nameof(ASoftFallback.VectorInsertInt)); - - Context.EmitStvec(Reg); - } - - private static void EmitVectorInsert(AILEmitterCtx Context, int Reg, int Index, int Size, long Value) - { - if (Size < 0 || Size > 3) - { - throw new ArgumentOutOfRangeException(nameof(Size)); - } - - Context.EmitLdvec(Reg); - Context.EmitLdc_I4(Index); - Context.EmitLdc_I4(Size); - Context.EmitLdc_I8(Value); - - ASoftFallback.EmitCall(Context, nameof(ASoftFallback.InsertVec)); - - Context.EmitStvec(Reg); - } - } -} \ No newline at end of file diff --git a/Ryujinx/Cpu/Instruction/AInstEmitSimdArithmetic.cs b/Ryujinx/Cpu/Instruction/AInstEmitSimdArithmetic.cs new file mode 100644 index 000000000..d6ebcc3ff --- /dev/null +++ b/Ryujinx/Cpu/Instruction/AInstEmitSimdArithmetic.cs @@ -0,0 +1,339 @@ +using ChocolArm64.Decoder; +using ChocolArm64.State; +using ChocolArm64.Translation; +using System; +using System.Reflection; +using System.Reflection.Emit; + +using static ChocolArm64.Instruction.AInstEmitSimdHelper; + +namespace ChocolArm64.Instruction +{ + static partial class AInstEmit + { + public static void Add_V(AILEmitterCtx Context) + { + EmitVectorBinaryOpZx(Context, () => Context.Emit(OpCodes.Add)); + } + + public static void Addp_S(AILEmitterCtx Context) + { + AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp; + + EmitVectorExtractZx(Context, Op.Rn, 0, Op.Size); + EmitVectorExtractZx(Context, Op.Rn, 1, Op.Size); + + Context.Emit(OpCodes.Add); + + EmitScalarSet(Context, Op.Rd, Op.Size); + } + + public static void Addp_V(AILEmitterCtx Context) + { + AOpCodeSimdReg Op = (AOpCodeSimdReg)Context.CurrOp; + + int Bytes = Context.CurrOp.GetBitsCount() >> 3; + + int Elems = Bytes >> Op.Size; + int Half = Elems >> 1; + + for (int Index = 0; Index < Elems; Index++) + { + int Elem = (Index & (Half - 1)) << 1; + + EmitVectorExtractZx(Context, Index < Half ? Op.Rn : Op.Rm, Elem + 0, Op.Size); + EmitVectorExtractZx(Context, Index < Half ? Op.Rn : Op.Rm, Elem + 1, Op.Size); + + Context.Emit(OpCodes.Add); + + EmitVectorInsertTmp(Context, Index, Op.Size); + } + + Context.EmitLdvectmp(); + Context.EmitStvec(Op.Rd); + + if (Op.RegisterSize == ARegisterSize.SIMD64) + { + EmitVectorZeroUpper(Context, Op.Rd); + } + } + + public static void Addv_V(AILEmitterCtx Context) + { + AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp; + + int Bytes = Context.CurrOp.GetBitsCount() >> 3; + + EmitVectorExtractZx(Context, Op.Rn, 0, Op.Size); + + for (int Index = 1; Index < (Bytes >> Op.Size); Index++) + { + EmitVectorExtractZx(Context, Op.Rn, Index, Op.Size); + + Context.Emit(OpCodes.Add); + } + + EmitScalarSet(Context, Op.Rd, Op.Size); + } + + public static void Cnt_V(AILEmitterCtx Context) + { + AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp; + + int Elems = Op.RegisterSize == ARegisterSize.SIMD128 ? 16 : 8; + + for (int Index = 0; Index < Elems; Index++) + { + EmitVectorExtractZx(Context, Op.Rn, Index, 0); + + Context.Emit(OpCodes.Conv_U1); + + ASoftFallback.EmitCall(Context, nameof(ASoftFallback.CountSetBits8)); + + Context.Emit(OpCodes.Conv_U8); + + EmitVectorInsert(Context, Op.Rd, Index, 0); + } + + if (Op.RegisterSize == ARegisterSize.SIMD64) + { + EmitVectorZeroUpper(Context, Op.Rd); + } + } + + public static void Fabs_S(AILEmitterCtx Context) + { + EmitScalarUnaryOpF(Context, () => + { + EmitUnaryMathCall(Context, nameof(Math.Abs)); + }); + } + + public static void Fadd_S(AILEmitterCtx Context) + { + EmitScalarBinaryOpF(Context, () => Context.Emit(OpCodes.Add)); + } + + public static void Fadd_V(AILEmitterCtx Context) + { + EmitVectorBinaryOpF(Context, () => Context.Emit(OpCodes.Add)); + } + + public static void Fdiv_S(AILEmitterCtx Context) + { + EmitScalarBinaryOpF(Context, () => Context.Emit(OpCodes.Div)); + } + + public static void Fmadd_S(AILEmitterCtx Context) + { + EmitScalarTernaryRaOpF(Context, () => + { + Context.Emit(OpCodes.Mul); + Context.Emit(OpCodes.Add); + }); + } + + public static void Fmax_S(AILEmitterCtx Context) + { + EmitScalarBinaryOpF(Context, () => + { + EmitBinaryMathCall(Context, nameof(Math.Max)); + }); + } + + public static void Fmin_S(AILEmitterCtx Context) + { + EmitScalarBinaryOpF(Context, () => + { + EmitBinaryMathCall(Context, nameof(Math.Min)); + }); + } + + public static void Fmaxnm_S(AILEmitterCtx Context) + { + Fmax_S(Context); + } + + public static void Fminnm_S(AILEmitterCtx Context) + { + Fmin_S(Context); + } + + public static void Fmla_V(AILEmitterCtx Context) + { + EmitVectorTernaryOpF(Context, () => + { + Context.Emit(OpCodes.Mul); + Context.Emit(OpCodes.Add); + }); + } + + public static void Fmla_Ve(AILEmitterCtx Context) + { + EmitVectorTernaryOpByElemF(Context, () => + { + Context.Emit(OpCodes.Mul); + Context.Emit(OpCodes.Add); + }); + } + + public static void Fmsub_S(AILEmitterCtx Context) + { + EmitScalarTernaryRaOpF(Context, () => + { + Context.Emit(OpCodes.Mul); + Context.Emit(OpCodes.Sub); + }); + } + + public static void Fmul_S(AILEmitterCtx Context) + { + EmitScalarBinaryOpF(Context, () => Context.Emit(OpCodes.Mul)); + } + + public static void Fmul_V(AILEmitterCtx Context) + { + EmitVectorBinaryOpF(Context, () => Context.Emit(OpCodes.Mul)); + } + + public static void Fmul_Ve(AILEmitterCtx Context) + { + EmitVectorBinaryOpByElemF(Context, () => Context.Emit(OpCodes.Mul)); + } + + public static void Fneg_S(AILEmitterCtx Context) + { + EmitScalarUnaryOpF(Context, () => Context.Emit(OpCodes.Neg)); + } + + public static void Fnmul_S(AILEmitterCtx Context) + { + EmitScalarBinaryOpF(Context, () => + { + Context.Emit(OpCodes.Mul); + Context.Emit(OpCodes.Neg); + }); + } + + public static void Frinta_S(AILEmitterCtx Context) + { + AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp; + + EmitVectorExtractF(Context, Op.Rn, 0, Op.Size); + + EmitRoundMathCall(Context, MidpointRounding.AwayFromZero); + + EmitScalarSetF(Context, Op.Rd, Op.Size); + } + + public static void Frintm_S(AILEmitterCtx Context) + { + EmitScalarUnaryOpF(Context, () => + { + EmitUnaryMathCall(Context, nameof(Math.Floor)); + }); + } + + public static void Fsqrt_S(AILEmitterCtx Context) + { + EmitScalarUnaryOpF(Context, () => + { + EmitUnaryMathCall(Context, nameof(Math.Sqrt)); + }); + } + + public static void Fsub_S(AILEmitterCtx Context) + { + EmitScalarBinaryOpF(Context, () => Context.Emit(OpCodes.Sub)); + } + + public static void Fsub_V(AILEmitterCtx Context) + { + EmitVectorBinaryOpF(Context, () => Context.Emit(OpCodes.Sub)); + } + + public static void Mla_V(AILEmitterCtx Context) + { + EmitVectorTernaryOpZx(Context, () => + { + Context.Emit(OpCodes.Mul); + Context.Emit(OpCodes.Add); + }); + } + + public static void Mls_V(AILEmitterCtx Context) + { + EmitVectorTernaryOpZx(Context, () => + { + Context.Emit(OpCodes.Mul); + Context.Emit(OpCodes.Sub); + }); + } + + public static void Mul_V(AILEmitterCtx Context) + { + EmitVectorBinaryOpZx(Context, () => Context.Emit(OpCodes.Mul)); + } + + public static void Neg_V(AILEmitterCtx Context) + { + EmitVectorUnaryOpSx(Context, () => Context.Emit(OpCodes.Neg)); + } + + public static void Saddw_V(AILEmitterCtx Context) + { + EmitVectorWidenBinaryOpSx(Context, () => Context.Emit(OpCodes.Add)); + } + + public static void Smax_V(AILEmitterCtx Context) + { + Type[] Types = new Type[] { typeof(long), typeof(long) }; + + MethodInfo MthdInfo = typeof(Math).GetMethod(nameof(Math.Max), Types); + + EmitVectorBinaryOpSx(Context, () => Context.EmitCall(MthdInfo)); + } + + public static void Smin_V(AILEmitterCtx Context) + { + Type[] Types = new Type[] { typeof(long), typeof(long) }; + + MethodInfo MthdInfo = typeof(Math).GetMethod(nameof(Math.Min), Types); + + EmitVectorBinaryOpSx(Context, () => Context.EmitCall(MthdInfo)); + } + + public static void Sub_S(AILEmitterCtx Context) + { + EmitScalarBinaryOpZx(Context, () => Context.Emit(OpCodes.Sub)); + } + + public static void Sub_V(AILEmitterCtx Context) + { + EmitVectorBinaryOpZx(Context, () => Context.Emit(OpCodes.Sub)); + } + + public static void Uaddlv_V(AILEmitterCtx Context) + { + AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp; + + int Bytes = Context.CurrOp.GetBitsCount() >> 3; + + EmitVectorExtractZx(Context, Op.Rn, 0, Op.Size); + + for (int Index = 1; Index < (Bytes >> Op.Size); Index++) + { + EmitVectorExtractZx(Context, Op.Rn, Index, Op.Size); + + Context.Emit(OpCodes.Add); + } + + EmitScalarSet(Context, Op.Rd, Op.Size + 1); + } + + public static void Uaddw_V(AILEmitterCtx Context) + { + EmitVectorWidenBinaryOpZx(Context, () => Context.Emit(OpCodes.Add)); + } + } +} \ No newline at end of file diff --git a/Ryujinx/Cpu/Instruction/AInstEmitSimdCmp.cs b/Ryujinx/Cpu/Instruction/AInstEmitSimdCmp.cs new file mode 100644 index 000000000..92361fbd3 --- /dev/null +++ b/Ryujinx/Cpu/Instruction/AInstEmitSimdCmp.cs @@ -0,0 +1,236 @@ +using ChocolArm64.Decoder; +using ChocolArm64.State; +using ChocolArm64.Translation; +using System; +using System.Reflection.Emit; + +using static ChocolArm64.Instruction.AInstEmitSimdHelper; + +namespace ChocolArm64.Instruction +{ + static partial class AInstEmit + { + public static void Cmeq_V(AILEmitterCtx Context) + { + EmitVectorCmp(Context, OpCodes.Beq_S); + } + + public static void Cmge_V(AILEmitterCtx Context) + { + EmitVectorCmp(Context, OpCodes.Bge_S); + } + + public static void Cmgt_V(AILEmitterCtx Context) + { + EmitVectorCmp(Context, OpCodes.Bgt_S); + } + + public static void Cmhi_V(AILEmitterCtx Context) + { + EmitVectorCmp(Context, OpCodes.Bgt_Un_S); + } + + public static void Cmhs_V(AILEmitterCtx Context) + { + EmitVectorCmp(Context, OpCodes.Bge_Un_S); + } + + public static void Cmle_V(AILEmitterCtx Context) + { + EmitVectorCmp(Context, OpCodes.Ble_S); + } + + public static void Cmlt_V(AILEmitterCtx Context) + { + EmitVectorCmp(Context, OpCodes.Blt_S); + } + + public static void Fccmp_S(AILEmitterCtx Context) + { + AOpCodeSimdFcond Op = (AOpCodeSimdFcond)Context.CurrOp; + + AILLabel LblTrue = new AILLabel(); + AILLabel LblEnd = new AILLabel(); + + Context.EmitCondBranch(LblTrue, Op.Cond); + + //TODO: Share this logic with Ccmp. + Context.EmitLdc_I4((Op.NZCV >> 0) & 1); + + Context.EmitStflg((int)APState.VBit); + + Context.EmitLdc_I4((Op.NZCV >> 1) & 1); + + Context.EmitStflg((int)APState.CBit); + + Context.EmitLdc_I4((Op.NZCV >> 2) & 1); + + Context.EmitStflg((int)APState.ZBit); + + Context.EmitLdc_I4((Op.NZCV >> 3) & 1); + + Context.EmitStflg((int)APState.NBit); + + Context.Emit(OpCodes.Br_S, LblEnd); + + Context.MarkLabel(LblTrue); + + Fcmp_S(Context); + + Context.MarkLabel(LblEnd); + } + + public static void Fcmp_S(AILEmitterCtx Context) + { + AOpCodeSimdReg Op = (AOpCodeSimdReg)Context.CurrOp; + + bool CmpWithZero = !(Op is AOpCodeSimdFcond) ? Op.Bit3 : false; + + void EmitLoadOpers() + { + EmitVectorExtractF(Context, Op.Rn, 0, Op.Size); + + if (CmpWithZero) + { + EmitLdcImmF(Context, 0, Op.Size); + } + else + { + EmitVectorExtractF(Context, Op.Rm, 0, Op.Size); + } + } + + //Z = Rn == Rm + EmitLoadOpers(); + + Context.Emit(OpCodes.Ceq); + Context.Emit(OpCodes.Dup); + + Context.EmitStflg((int)APState.ZBit); + + //C = Rn >= Rm + EmitLoadOpers(); + + Context.Emit(OpCodes.Cgt); + Context.Emit(OpCodes.Or); + + Context.EmitStflg((int)APState.CBit); + + //N = Rn < Rm + EmitLoadOpers(); + + Context.Emit(OpCodes.Clt); + + Context.EmitStflg((int)APState.NBit); + + //Handle NaN case. If any number is NaN, then NZCV = 0011. + AILLabel LblNotNaN = new AILLabel(); + + if (CmpWithZero) + { + EmitNaNCheck(Context, Op.Rn); + } + else + { + EmitNaNCheck(Context, Op.Rn); + EmitNaNCheck(Context, Op.Rm); + + Context.Emit(OpCodes.Or); + } + + Context.Emit(OpCodes.Brfalse_S, LblNotNaN); + + Context.EmitLdc_I4(1); + Context.EmitLdc_I4(1); + + Context.EmitStflg((int)APState.CBit); + Context.EmitStflg((int)APState.VBit); + + Context.MarkLabel(LblNotNaN); + } + + public static void Fcmpe_S(AILEmitterCtx Context) + { + Fcmp_S(Context); + } + + private static void EmitLdcImmF(AILEmitterCtx Context, double ImmF, int Size) + { + if (Size == 0) + { + Context.EmitLdc_R4((float)ImmF); + } + else if (Size == 1) + { + Context.EmitLdc_R8(ImmF); + } + else + { + throw new ArgumentOutOfRangeException(nameof(Size)); + } + } + + private static void EmitNaNCheck(AILEmitterCtx Context, int Reg) + { + IAOpCodeSimd Op = (IAOpCodeSimd)Context.CurrOp; + + EmitVectorExtractF(Context, Reg, 0, Op.Size); + + if (Op.Size == 0) + { + Context.EmitCall(typeof(float), nameof(float.IsNaN)); + } + else if (Op.Size == 1) + { + Context.EmitCall(typeof(double), nameof(double.IsNaN)); + } + else + { + throw new InvalidOperationException(); + } + } + + private static void EmitVectorCmp(AILEmitterCtx Context, OpCode ILOp) + { + AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp; + + int Bytes = Context.CurrOp.GetBitsCount() >> 3; + + ulong SzMask = ulong.MaxValue >> (64 - (8 << Op.Size)); + + for (int Index = 0; Index < (Bytes >> Op.Size); Index++) + { + EmitVectorExtractSx(Context, Op.Rn, Index, Op.Size); + + if (Op is AOpCodeSimdReg BinOp) + { + EmitVectorExtractSx(Context, BinOp.Rm, Index, Op.Size); + } + else + { + Context.EmitLdc_I8(0); + } + + AILLabel LblTrue = new AILLabel(); + AILLabel LblEnd = new AILLabel(); + + Context.Emit(ILOp, LblTrue); + + EmitVectorInsert(Context, Op.Rd, Index, Op.Size, 0); + + Context.Emit(OpCodes.Br_S, LblEnd); + + Context.MarkLabel(LblTrue); + + EmitVectorInsert(Context, Op.Rd, Index, Op.Size, (long)SzMask); + + Context.MarkLabel(LblEnd); + } + + if (Op.RegisterSize == ARegisterSize.SIMD64) + { + EmitVectorZeroUpper(Context, Op.Rd); + } + } + } +} \ No newline at end of file diff --git a/Ryujinx/Cpu/Instruction/AInstEmitSimdCvt.cs b/Ryujinx/Cpu/Instruction/AInstEmitSimdCvt.cs new file mode 100644 index 000000000..fbb0dfda5 --- /dev/null +++ b/Ryujinx/Cpu/Instruction/AInstEmitSimdCvt.cs @@ -0,0 +1,444 @@ +using ChocolArm64.Decoder; +using ChocolArm64.State; +using ChocolArm64.Translation; +using System; +using System.Reflection.Emit; + +using static ChocolArm64.Instruction.AInstEmitSimdHelper; + +namespace ChocolArm64.Instruction +{ + static partial class AInstEmit + { + public static void Fcvt_S(AILEmitterCtx Context) + { + AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp; + + EmitVectorExtractF(Context, Op.Rn, 0, Op.Size); + + EmitFloatCast(Context, Op.Opc); + + EmitScalarSetF(Context, Op.Rd, Op.Opc); + } + + public static void Fcvtas_Gp(AILEmitterCtx Context) + { + Fcvta__Gp(Context, Signed: true); + } + + public static void Fcvtau_Gp(AILEmitterCtx Context) + { + Fcvta__Gp(Context, Signed: false); + } + + public static void Fcvtms_Gp(AILEmitterCtx Context) + { + EmitFcvt_s_Gp(Context, nameof(Math.Floor)); + } + + public static void Fcvtps_Gp(AILEmitterCtx Context) + { + EmitFcvt_s_Gp(Context, nameof(Math.Ceiling)); + } + + public static void Fcvtzs_Gp(AILEmitterCtx Context) + { + EmitFcvtz__Gp(Context, Signed: true); + } + + public static void Fcvtzs_Gp_Fix(AILEmitterCtx Context) + { + EmitFcvtz__Gp_Fix(Context, Signed: true); + } + + public static void Fcvtzs_V(AILEmitterCtx Context) + { + EmitVectorFcvt(Context, Signed: true); + } + + public static void Fcvtzu_Gp(AILEmitterCtx Context) + { + EmitFcvtz__Gp(Context, Signed: false); + } + + public static void Fcvtzu_Gp_Fix(AILEmitterCtx Context) + { + EmitFcvtz__Gp_Fix(Context, Signed: false); + } + + public static void Fcvtzu_V(AILEmitterCtx Context) + { + EmitVectorFcvt(Context, Signed: false); + } + + public static void Scvtf_Gp(AILEmitterCtx Context) + { + AOpCodeSimdCvt Op = (AOpCodeSimdCvt)Context.CurrOp; + + Context.EmitLdintzr(Op.Rn); + + if (Context.CurrOp.RegisterSize == ARegisterSize.Int32) + { + Context.Emit(OpCodes.Conv_U4); + } + + EmitFloatCast(Context, Op.Size); + + EmitScalarSetF(Context, Op.Rd, Op.Size); + } + + public static void Scvtf_S(AILEmitterCtx Context) + { + AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp; + + EmitVectorExtractSx(Context, Op.Rd, 0, Op.Size + 2); + + EmitFloatCast(Context, Op.Size); + + EmitScalarSetF(Context, Op.Rd, Op.Size); + } + + public static void Scvtf_V(AILEmitterCtx Context) + { + EmitVectorCvtf(Context, Signed: true); + } + + public static void Ucvtf_Gp(AILEmitterCtx Context) + { + AOpCodeSimdCvt Op = (AOpCodeSimdCvt)Context.CurrOp; + + Context.EmitLdintzr(Op.Rn); + + if (Context.CurrOp.RegisterSize == ARegisterSize.Int32) + { + Context.Emit(OpCodes.Conv_U4); + } + + Context.Emit(OpCodes.Conv_R_Un); + + EmitFloatCast(Context, Op.Size); + + EmitScalarSetF(Context, Op.Rd, Op.Size); + } + + public static void Ucvtf_S(AILEmitterCtx Context) + { + AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp; + + EmitVectorExtractZx(Context, Op.Rn, 0, Op.Size + 2); + + Context.Emit(OpCodes.Conv_R_Un); + + EmitFloatCast(Context, Op.Size); + + EmitScalarSetF(Context, Op.Rd, Op.Size); + } + + public static void Ucvtf_V(AILEmitterCtx Context) + { + EmitVectorCvtf(Context, Signed: false); + } + + private static int GetFBits(AILEmitterCtx Context) + { + if (Context.CurrOp is AOpCodeSimdShImm Op) + { + return GetImmShr(Op); + } + + return 0; + } + + private static void EmitFloatCast(AILEmitterCtx Context, int Size) + { + if (Size == 0) + { + Context.Emit(OpCodes.Conv_R4); + } + else if (Size == 1) + { + Context.Emit(OpCodes.Conv_R8); + } + else + { + throw new ArgumentOutOfRangeException(nameof(Size)); + } + } + + private static void Fcvta__Gp(AILEmitterCtx Context, bool Signed) + { + AOpCodeSimdCvt Op = (AOpCodeSimdCvt)Context.CurrOp; + + EmitVectorExtractF(Context, Op.Rn, 0, Op.Size); + + EmitRoundMathCall(Context, MidpointRounding.AwayFromZero); + + if (Signed) + { + EmitScalarFcvts(Context, Op.Size, 0); + } + else + { + EmitScalarFcvtu(Context, Op.Size, 0); + } + + if (Context.CurrOp.RegisterSize == ARegisterSize.Int32) + { + Context.Emit(OpCodes.Conv_U8); + } + + Context.EmitStintzr(Op.Rd); + } + + private static void EmitFcvt_s_Gp(AILEmitterCtx Context, string Name) + { + AOpCodeSimdCvt Op = (AOpCodeSimdCvt)Context.CurrOp; + + EmitVectorExtractF(Context, Op.Rn, 0, Op.Size); + + EmitUnaryMathCall(Context, Name); + + EmitScalarFcvts(Context, Op.Size, 0); + + if (Context.CurrOp.RegisterSize == ARegisterSize.Int32) + { + Context.Emit(OpCodes.Conv_U8); + } + + Context.EmitStintzr(Op.Rd); + } + + private static void EmitFcvtz__Gp(AILEmitterCtx Context, bool Signed) + { + AOpCodeSimdCvt Op = (AOpCodeSimdCvt)Context.CurrOp; + + EmitVectorExtractF(Context, Op.Rn, 0, Op.Size); + + if (Signed) + { + EmitScalarFcvts(Context, Op.Size, 0); + } + else + { + EmitScalarFcvtu(Context, Op.Size, 0); + } + + if (Context.CurrOp.RegisterSize == ARegisterSize.Int32) + { + Context.Emit(OpCodes.Conv_U8); + } + + Context.EmitStintzr(Op.Rd); + } + + private static void EmitFcvtz__Gp_Fix(AILEmitterCtx Context, bool Signed) + { + AOpCodeSimdCvt Op = (AOpCodeSimdCvt)Context.CurrOp; + + EmitVectorExtractF(Context, Op.Rn, 0, Op.Size); + + if (Signed) + { + EmitScalarFcvts(Context, Op.Size, Op.FBits); + } + else + { + EmitScalarFcvtu(Context, Op.Size, Op.FBits); + } + + if (Context.CurrOp.RegisterSize == ARegisterSize.Int32) + { + Context.Emit(OpCodes.Conv_U8); + } + + Context.EmitStintzr(Op.Rd); + } + + private static void EmitVectorCvtf(AILEmitterCtx Context, bool Signed) + { + AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp; + + int SizeF = Op.Size & 1; + int SizeI = SizeF + 2; + + int FBits = GetFBits(Context); + + int Bytes = Context.CurrOp.GetBitsCount() >> 3; + + for (int Index = 0; Index < (Bytes >> SizeI); Index++) + { + EmitVectorExtract(Context, Op.Rn, Index, SizeI, Signed); + + if (!Signed) + { + Context.Emit(OpCodes.Conv_R_Un); + } + + Context.Emit(SizeF == 0 + ? OpCodes.Conv_R4 + : OpCodes.Conv_R8); + + EmitI2fFBitsMul(Context, SizeF, FBits); + + EmitVectorInsertF(Context, Op.Rd, Index, SizeF); + } + + if (Op.RegisterSize == ARegisterSize.SIMD64) + { + EmitVectorZeroUpper(Context, Op.Rd); + } + } + + private static void EmitVectorFcvt(AILEmitterCtx Context, bool Signed) + { + AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp; + + int SizeF = Op.Size & 1; + int SizeI = SizeF + 2; + + int FBits = GetFBits(Context); + + int Bytes = Context.CurrOp.GetBitsCount() >> 3; + + for (int Index = 0; Index < (Bytes >> SizeI); Index++) + { + EmitVectorExtractF(Context, Op.Rn, Index, SizeF); + + EmitF2iFBitsMul(Context, SizeF, FBits); + + if (SizeF == 0) + { + ASoftFallback.EmitCall(Context, Signed + ? nameof(ASoftFallback.SatF32ToS32) + : nameof(ASoftFallback.SatF32ToU32)); + } + else /* if (SizeF == 1) */ + { + ASoftFallback.EmitCall(Context, Signed + ? nameof(ASoftFallback.SatF64ToS64) + : nameof(ASoftFallback.SatF64ToU64)); + } + + if (SizeF == 0) + { + Context.Emit(OpCodes.Conv_U8); + } + + EmitVectorInsert(Context, Op.Rd, Index, SizeI); + } + + if (Op.RegisterSize == ARegisterSize.SIMD64) + { + EmitVectorZeroUpper(Context, Op.Rd); + } + } + + private static void EmitScalarFcvts(AILEmitterCtx Context, int Size, int FBits) + { + if (Size < 0 || Size > 1) + { + throw new ArgumentOutOfRangeException(nameof(Size)); + } + + EmitF2iFBitsMul(Context, Size, FBits); + + if (Context.CurrOp.RegisterSize == ARegisterSize.Int32) + { + if (Size == 0) + { + ASoftFallback.EmitCall(Context, nameof(ASoftFallback.SatF32ToS32)); + } + else /* if (Size == 1) */ + { + ASoftFallback.EmitCall(Context, nameof(ASoftFallback.SatF64ToS32)); + } + } + else + { + if (Size == 0) + { + ASoftFallback.EmitCall(Context, nameof(ASoftFallback.SatF32ToS64)); + } + else /* if (Size == 1) */ + { + ASoftFallback.EmitCall(Context, nameof(ASoftFallback.SatF64ToS64)); + } + } + } + + private static void EmitScalarFcvtu(AILEmitterCtx Context, int Size, int FBits) + { + if (Size < 0 || Size > 1) + { + throw new ArgumentOutOfRangeException(nameof(Size)); + } + + EmitF2iFBitsMul(Context, Size, FBits); + + if (Context.CurrOp.RegisterSize == ARegisterSize.Int32) + { + if (Size == 0) + { + ASoftFallback.EmitCall(Context, nameof(ASoftFallback.SatF32ToU32)); + } + else /* if (Size == 1) */ + { + ASoftFallback.EmitCall(Context, nameof(ASoftFallback.SatF64ToU32)); + } + } + else + { + if (Size == 0) + { + ASoftFallback.EmitCall(Context, nameof(ASoftFallback.SatF32ToU64)); + } + else /* if (Size == 1) */ + { + ASoftFallback.EmitCall(Context, nameof(ASoftFallback.SatF64ToU64)); + } + } + } + + private static void EmitF2iFBitsMul(AILEmitterCtx Context, int Size, int FBits) + { + if (FBits != 0) + { + if (Size == 0) + { + Context.EmitLdc_R4(MathF.Pow(2, FBits)); + } + else if (Size == 1) + { + Context.EmitLdc_R8(Math.Pow(2, FBits)); + } + else + { + throw new ArgumentOutOfRangeException(nameof(Size)); + } + + Context.Emit(OpCodes.Mul); + } + } + + private static void EmitI2fFBitsMul(AILEmitterCtx Context, int Size, int FBits) + { + if (FBits != 0) + { + if (Size == 0) + { + Context.EmitLdc_R4(1f / MathF.Pow(2, FBits)); + } + else if (Size == 1) + { + Context.EmitLdc_R8(1 / Math.Pow(2, FBits)); + } + else + { + throw new ArgumentOutOfRangeException(nameof(Size)); + } + + Context.Emit(OpCodes.Mul); + } + } + } +} \ No newline at end of file diff --git a/Ryujinx/Cpu/Instruction/AInstEmitSimdHelper.cs b/Ryujinx/Cpu/Instruction/AInstEmitSimdHelper.cs new file mode 100644 index 000000000..97f288161 --- /dev/null +++ b/Ryujinx/Cpu/Instruction/AInstEmitSimdHelper.cs @@ -0,0 +1,584 @@ +using ChocolArm64.Decoder; +using ChocolArm64.State; +using ChocolArm64.Translation; +using System; +using System.Reflection; + +namespace ChocolArm64.Instruction +{ + static class AInstEmitSimdHelper + { + [Flags] + public enum OperFlags + { + Rd = 1 << 0, + Rn = 1 << 1, + Rm = 1 << 2, + Ra = 1 << 3, + + RnRm = Rn | Rm, + RdRn = Rd | Rn, + RaRnRm = Ra | Rn | Rm, + RdRnRm = Rd | Rn | Rm + } + + public static int GetImmShl(AOpCodeSimdShImm Op) + { + return Op.Imm - (8 << Op.Size); + } + + public static int GetImmShr(AOpCodeSimdShImm Op) + { + return (8 << (Op.Size + 1)) - Op.Imm; + } + + public static void EmitUnaryMathCall(AILEmitterCtx Context, string Name) + { + IAOpCodeSimd Op = (IAOpCodeSimd)Context.CurrOp; + + MethodInfo MthdInfo; + + if (Op.Size == 0) + { + MthdInfo = typeof(MathF).GetMethod(Name, new Type[] { typeof(float) }); + } + else if (Op.Size == 1) + { + MthdInfo = typeof(Math).GetMethod(Name, new Type[] { typeof(double) }); + } + else + { + throw new InvalidOperationException(); + } + + Context.EmitCall(MthdInfo); + } + + public static void EmitBinaryMathCall(AILEmitterCtx Context, string Name) + { + IAOpCodeSimd Op = (IAOpCodeSimd)Context.CurrOp; + + MethodInfo MthdInfo; + + if (Op.Size == 0) + { + MthdInfo = typeof(MathF).GetMethod(Name, new Type[] { typeof(float), typeof(float) }); + } + else if (Op.Size == 1) + { + MthdInfo = typeof(Math).GetMethod(Name, new Type[] { typeof(double), typeof(double) }); + } + else + { + throw new InvalidOperationException(); + } + + Context.EmitCall(MthdInfo); + } + + public static void EmitRoundMathCall(AILEmitterCtx Context, MidpointRounding RoundMode) + { + IAOpCodeSimd Op = (IAOpCodeSimd)Context.CurrOp; + + Context.EmitLdc_I4((int)RoundMode); + + MethodInfo MthdInfo; + + Type[] Types = new Type[] { null, typeof(MidpointRounding) }; + + Types[0] = Op.Size == 0 + ? typeof(float) + : typeof(double); + + if (Op.Size == 0) + { + MthdInfo = typeof(MathF).GetMethod(nameof(MathF.Round), Types); + } + else if (Op.Size == 1) + { + MthdInfo = typeof(Math).GetMethod(nameof(Math.Round), Types); + } + else + { + throw new InvalidOperationException(); + } + + Context.EmitCall(MthdInfo); + } + + public static void EmitScalarUnaryOpSx(AILEmitterCtx Context, Action Emit) + { + EmitScalarOp(Context, Emit, OperFlags.Rn, true); + } + + public static void EmitScalarBinaryOpSx(AILEmitterCtx Context, Action Emit) + { + EmitScalarOp(Context, Emit, OperFlags.RnRm, true); + } + + public static void EmitScalarUnaryOpZx(AILEmitterCtx Context, Action Emit) + { + EmitScalarOp(Context, Emit, OperFlags.Rn, false); + } + + public static void EmitScalarBinaryOpZx(AILEmitterCtx Context, Action Emit) + { + EmitScalarOp(Context, Emit, OperFlags.RnRm, false); + } + + public static void EmitScalarTernaryOpZx(AILEmitterCtx Context, Action Emit) + { + EmitScalarOp(Context, Emit, OperFlags.RdRnRm, false); + } + + public static void EmitScalarOp(AILEmitterCtx Context, Action Emit, OperFlags Opers, bool Signed) + { + AOpCodeSimdReg Op = (AOpCodeSimdReg)Context.CurrOp; + + if (Opers.HasFlag(OperFlags.Rd)) + { + EmitVectorExtract(Context, Op.Rd, 0, Op.Size, Signed); + } + + if (Opers.HasFlag(OperFlags.Rn)) + { + EmitVectorExtract(Context, Op.Rn, 0, Op.Size, Signed); + } + + if (Opers.HasFlag(OperFlags.Rm)) + { + EmitVectorExtract(Context, Op.Rm, 0, Op.Size, Signed); + } + + Emit(); + + EmitScalarSet(Context, Op.Rd, Op.Size); + } + + public static void EmitScalarUnaryOpF(AILEmitterCtx Context, Action Emit) + { + EmitScalarOpF(Context, Emit, OperFlags.Rn); + } + + public static void EmitScalarBinaryOpF(AILEmitterCtx Context, Action Emit) + { + EmitScalarOpF(Context, Emit, OperFlags.RnRm); + } + + public static void EmitScalarTernaryRaOpF(AILEmitterCtx Context, Action Emit) + { + EmitScalarOpF(Context, Emit, OperFlags.RaRnRm); + } + + public static void EmitScalarOpF(AILEmitterCtx Context, Action Emit, OperFlags Opers) + { + AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp; + + int SizeF = Op.Size & 1; + + if (Opers.HasFlag(OperFlags.Ra)) + { + EmitVectorExtractF(Context, ((AOpCodeSimdReg)Op).Ra, 0, SizeF); + } + + if (Opers.HasFlag(OperFlags.Rn)) + { + EmitVectorExtractF(Context, Op.Rn, 0, SizeF); + } + + if (Opers.HasFlag(OperFlags.Rm)) + { + EmitVectorExtractF(Context, ((AOpCodeSimdReg)Op).Rm, 0, SizeF); + } + + Emit(); + + EmitScalarSetF(Context, Op.Rd, SizeF); + } + + public static void EmitVectorBinaryOpF(AILEmitterCtx Context, Action Emit) + { + EmitVectorOpF(Context, Emit, OperFlags.RnRm); + } + + public static void EmitVectorTernaryOpF(AILEmitterCtx Context, Action Emit) + { + 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; + + int SizeF = Op.Size & 1; + + int Bytes = Context.CurrOp.GetBitsCount() >> 3; + + for (int Index = 0; Index < (Bytes >> SizeF + 2); Index++) + { + if (Opers.HasFlag(OperFlags.Rd)) + { + EmitVectorExtractF(Context, Op.Rd, Index, SizeF); + } + + if (Opers.HasFlag(OperFlags.Rn)) + { + EmitVectorExtractF(Context, Op.Rn, Index, SizeF); + } + + if (Opers.HasFlag(OperFlags.Rm)) + { + EmitVectorExtractF(Context, Op.Rm, Index, SizeF); + } + + Emit(); + + EmitVectorInsertF(Context, Op.Rd, Index, SizeF); + } + + if (Op.RegisterSize == ARegisterSize.SIMD64) + { + EmitVectorZeroUpper(Context, Op.Rd); + } + } + + public static void EmitVectorOpByElemF(AILEmitterCtx Context, Action Emit, int Elem, bool Ternary) + { + AOpCodeSimdReg Op = (AOpCodeSimdReg)Context.CurrOp; + + int SizeF = Op.Size & 1; + + int Bytes = Context.CurrOp.GetBitsCount() >> 3; + + for (int Index = 0; Index < (Bytes >> SizeF + 2); Index++) + { + if (Ternary) + { + EmitVectorExtractF(Context, Op.Rd, Index, SizeF); + } + + EmitVectorExtractF(Context, Op.Rn, Index, SizeF); + EmitVectorExtractF(Context, Op.Rm, Elem, SizeF); + + Emit(); + + EmitVectorInsertTmpF(Context, Index, SizeF); + } + + Context.EmitLdvectmp(); + Context.EmitStvec(Op.Rd); + + if (Op.RegisterSize == ARegisterSize.SIMD64) + { + EmitVectorZeroUpper(Context, Op.Rd); + } + } + + public static void EmitVectorUnaryOpSx(AILEmitterCtx Context, Action Emit) + { + EmitVectorOp(Context, Emit, OperFlags.Rn, true); + } + + public static void EmitVectorBinaryOpSx(AILEmitterCtx Context, Action Emit) + { + EmitVectorOp(Context, Emit, OperFlags.RnRm, true); + } + + public static void EmitVectorUnaryOpZx(AILEmitterCtx Context, Action Emit) + { + EmitVectorOp(Context, Emit, OperFlags.Rn, false); + } + + public static void EmitVectorBinaryOpZx(AILEmitterCtx Context, Action Emit) + { + EmitVectorOp(Context, Emit, OperFlags.RnRm, false); + } + + public static void EmitVectorTernaryOpZx(AILEmitterCtx Context, Action Emit) + { + EmitVectorOp(Context, Emit, OperFlags.RdRnRm, false); + } + + public static void EmitVectorOp(AILEmitterCtx Context, Action Emit, OperFlags Opers, bool Signed) + { + AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp; + + int Bytes = Context.CurrOp.GetBitsCount() >> 3; + + for (int Index = 0; Index < (Bytes >> Op.Size); Index++) + { + if (Opers.HasFlag(OperFlags.Rd)) + { + EmitVectorExtract(Context, Op.Rd, Index, Op.Size, Signed); + } + + if (Opers.HasFlag(OperFlags.Rn)) + { + EmitVectorExtract(Context, Op.Rn, Index, Op.Size, Signed); + } + + if (Opers.HasFlag(OperFlags.Rm)) + { + EmitVectorExtract(Context, ((AOpCodeSimdReg)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); + } + + public static void EmitVectorImmBinaryOp(AILEmitterCtx Context, Action Emit) + { + EmitVectorImmOp(Context, Emit, true); + } + + public static void EmitVectorImmOp(AILEmitterCtx Context, Action Emit, bool Binary) + { + AOpCodeSimdImm Op = (AOpCodeSimdImm)Context.CurrOp; + + int Bytes = Context.CurrOp.GetBitsCount() >> 3; + + for (int Index = 0; Index < (Bytes >> Op.Size); Index++) + { + if (Binary) + { + EmitVectorExtractZx(Context, Op.Rd, Index, Op.Size); + } + + Context.EmitLdc_I8(Op.Imm); + + Emit(); + + EmitVectorInsert(Context, Op.Rd, Index, Op.Size); + } + + if (Op.RegisterSize == ARegisterSize.SIMD64) + { + EmitVectorZeroUpper(Context, Op.Rd); + } + } + + public static void EmitVectorWidenBinaryOpSx(AILEmitterCtx Context, Action Emit) + { + EmitVectorWidenBinaryOp(Context, Emit, true); + } + + public static void EmitVectorWidenBinaryOpZx(AILEmitterCtx Context, Action Emit) + { + EmitVectorWidenBinaryOp(Context, Emit, false); + } + + public static void EmitVectorWidenBinaryOp(AILEmitterCtx Context, Action Emit, bool Signed) + { + AOpCodeSimdReg Op = (AOpCodeSimdReg)Context.CurrOp; + + int Elems = 8 >> Op.Size; + + int Part = Op.RegisterSize == ARegisterSize.SIMD128 ? Elems : 0; + + for (int Index = 0; Index < Elems; Index++) + { + EmitVectorExtract(Context, Op.Rn, Index, Op.Size + 1, Signed); + EmitVectorExtract(Context, Op.Rm, Part + Index, Op.Size, Signed); + + Emit(); + + EmitVectorInsertTmp(Context, Index, Op.Size + 1); + } + + Context.EmitLdvectmp(); + Context.EmitStvec(Op.Rd); + } + + public static void EmitScalarSet(AILEmitterCtx Context, int Reg, int Size) + { + EmitVectorZeroAll(Context, Reg); + EmitVectorInsert(Context, Reg, 0, Size); + } + + public static void EmitScalarSetF(AILEmitterCtx Context, int Reg, int Size) + { + EmitVectorZeroAll(Context, Reg); + EmitVectorInsertF(Context, Reg, 0, Size); + } + + public static void EmitVectorExtractSx(AILEmitterCtx Context, int Reg, int Index, int Size) + { + EmitVectorExtract(Context, Reg, Index, Size, true); + } + + public static void EmitVectorExtractZx(AILEmitterCtx Context, int Reg, int Index, int Size) + { + EmitVectorExtract(Context, Reg, Index, Size, false); + } + + public static void EmitVectorExtract(AILEmitterCtx Context, int Reg, int Index, int Size, bool Signed) + { + if (Size < 0 || Size > 3) + { + throw new ArgumentOutOfRangeException(nameof(Size)); + } + + IAOpCodeSimd Op = (IAOpCodeSimd)Context.CurrOp; + + Context.EmitLdvec(Reg); + Context.EmitLdc_I4(Index); + Context.EmitLdc_I4(Size); + + ASoftFallback.EmitCall(Context, Signed + ? nameof(ASoftFallback.VectorExtractIntSx) + : nameof(ASoftFallback.VectorExtractIntZx)); + } + + public static void EmitVectorExtractF(AILEmitterCtx Context, int Reg, int Index, int Size) + { + Context.EmitLdvec(Reg); + Context.EmitLdc_I4(Index); + + if (Size == 0) + { + ASoftFallback.EmitCall(Context, nameof(ASoftFallback.VectorExtractSingle)); + } + else if (Size == 1) + { + ASoftFallback.EmitCall(Context, nameof(ASoftFallback.VectorExtractDouble)); + } + else + { + throw new ArgumentOutOfRangeException(nameof(Size)); + } + } + + public static void EmitVectorZeroAll(AILEmitterCtx Context, int Rd) + { + EmitVectorZeroLower(Context, Rd); + EmitVectorZeroUpper(Context, Rd); + } + + public static void EmitVectorZeroLower(AILEmitterCtx Context, int Rd) + { + EmitVectorInsert(Context, Rd, 0, 3, 0); + } + + public static void EmitVectorZeroUpper(AILEmitterCtx Context, int Rd) + { + EmitVectorInsert(Context, Rd, 1, 3, 0); + } + + public static void EmitVectorInsert(AILEmitterCtx Context, int Reg, int Index, int Size) + { + if (Size < 0 || Size > 3) + { + throw new ArgumentOutOfRangeException(nameof(Size)); + } + + Context.EmitLdvec(Reg); + Context.EmitLdc_I4(Index); + Context.EmitLdc_I4(Size); + + ASoftFallback.EmitCall(Context, nameof(ASoftFallback.VectorInsertInt)); + + Context.EmitStvec(Reg); + } + + public static void EmitVectorInsertTmp(AILEmitterCtx Context, int Index, int Size) + { + if (Size < 0 || Size > 3) + { + throw new ArgumentOutOfRangeException(nameof(Size)); + } + + Context.EmitLdvectmp(); + Context.EmitLdc_I4(Index); + Context.EmitLdc_I4(Size); + + ASoftFallback.EmitCall(Context, nameof(ASoftFallback.VectorInsertInt)); + + Context.EmitStvectmp(); + } + + public static void EmitVectorInsert(AILEmitterCtx Context, int Reg, int Index, int Size, long Value) + { + if (Size < 0 || Size > 3) + { + throw new ArgumentOutOfRangeException(nameof(Size)); + } + + Context.EmitLdc_I8(Value); + Context.EmitLdvec(Reg); + Context.EmitLdc_I4(Index); + Context.EmitLdc_I4(Size); + + ASoftFallback.EmitCall(Context, nameof(ASoftFallback.VectorInsertInt)); + + Context.EmitStvec(Reg); + } + + public static void EmitVectorInsertF(AILEmitterCtx Context, int Reg, int Index, int Size) + { + Context.EmitLdvec(Reg); + Context.EmitLdc_I4(Index); + + if (Size == 0) + { + ASoftFallback.EmitCall(Context, nameof(ASoftFallback.VectorInsertSingle)); + } + else if (Size == 1) + { + ASoftFallback.EmitCall(Context, nameof(ASoftFallback.VectorInsertDouble)); + } + else + { + throw new ArgumentOutOfRangeException(nameof(Size)); + } + + Context.EmitStvec(Reg); + } + + public static void EmitVectorInsertTmpF(AILEmitterCtx Context, int Index, int Size) + { + Context.EmitLdvectmp(); + Context.EmitLdc_I4(Index); + + if (Size == 0) + { + ASoftFallback.EmitCall(Context, nameof(ASoftFallback.VectorInsertSingle)); + } + else if (Size == 1) + { + ASoftFallback.EmitCall(Context, nameof(ASoftFallback.VectorInsertDouble)); + } + else + { + throw new ArgumentOutOfRangeException(nameof(Size)); + } + + Context.EmitStvectmp(); + } + } +} \ No newline at end of file diff --git a/Ryujinx/Cpu/Instruction/AInstEmitSimdLogical.cs b/Ryujinx/Cpu/Instruction/AInstEmitSimdLogical.cs new file mode 100644 index 000000000..ea4b17b3d --- /dev/null +++ b/Ryujinx/Cpu/Instruction/AInstEmitSimdLogical.cs @@ -0,0 +1,69 @@ +using ChocolArm64.Translation; +using System.Reflection.Emit; + +using static ChocolArm64.Instruction.AInstEmitSimdHelper; + +namespace ChocolArm64.Instruction +{ + static partial class AInstEmit + { + public static void And_V(AILEmitterCtx Context) + { + EmitVectorBinaryOpZx(Context, () => Context.Emit(OpCodes.And)); + } + + public static void Bic_V(AILEmitterCtx Context) + { + EmitVectorBinaryOpZx(Context, () => + { + Context.Emit(OpCodes.Not); + Context.Emit(OpCodes.And); + }); + } + + public static void Bic_Vi(AILEmitterCtx Context) + { + EmitVectorImmBinaryOp(Context, () => + { + Context.Emit(OpCodes.Not); + Context.Emit(OpCodes.And); + }); + } + + public static void Bsl_V(AILEmitterCtx Context) + { + EmitVectorTernaryOpZx(Context, () => + { + Context.EmitSttmp(); + Context.EmitLdtmp(); + + Context.Emit(OpCodes.Xor); + Context.Emit(OpCodes.And); + + Context.EmitLdtmp(); + + Context.Emit(OpCodes.Xor); + }); + } + + public static void Eor_V(AILEmitterCtx Context) + { + EmitVectorBinaryOpZx(Context, () => Context.Emit(OpCodes.Xor)); + } + + public static void Not_V(AILEmitterCtx Context) + { + EmitVectorUnaryOpZx(Context, () => Context.Emit(OpCodes.Not)); + } + + public static void Orr_V(AILEmitterCtx Context) + { + EmitVectorBinaryOpZx(Context, () => Context.Emit(OpCodes.Or)); + } + + public static void Orr_Vi(AILEmitterCtx Context) + { + EmitVectorImmBinaryOp(Context, () => Context.Emit(OpCodes.Or)); + } + } +} \ No newline at end of file diff --git a/Ryujinx/Cpu/Instruction/AInstEmitSimdMemory.cs b/Ryujinx/Cpu/Instruction/AInstEmitSimdMemory.cs new file mode 100644 index 000000000..d98ec012e --- /dev/null +++ b/Ryujinx/Cpu/Instruction/AInstEmitSimdMemory.cs @@ -0,0 +1,184 @@ +using ChocolArm64.Decoder; +using ChocolArm64.State; +using ChocolArm64.Translation; +using System; +using System.Reflection.Emit; + +using static ChocolArm64.Instruction.AInstEmitMemoryHelper; +using static ChocolArm64.Instruction.AInstEmitSimdHelper; + +namespace ChocolArm64.Instruction +{ + static partial class AInstEmit + { + public static void Ld__Vms(AILEmitterCtx Context) + { + EmitSimdMemMs(Context, IsLoad: true); + } + + public static void Ld__Vss(AILEmitterCtx Context) + { + EmitSimdMemSs(Context, IsLoad: true); + } + + public static void St__Vms(AILEmitterCtx Context) + { + EmitSimdMemMs(Context, IsLoad: false); + } + + public static void St__Vss(AILEmitterCtx Context) + { + EmitSimdMemSs(Context, IsLoad: false); + } + + private static void EmitSimdMemMs(AILEmitterCtx Context, bool IsLoad) + { + AOpCodeSimdMemMs Op = (AOpCodeSimdMemMs)Context.CurrOp; + + int Offset = 0; + + for (int Rep = 0; Rep < Op.Reps; Rep++) + for (int Elem = 0; Elem < Op.Elems; Elem++) + for (int SElem = 0; SElem < Op.SElems; SElem++) + { + int Rtt = (Op.Rt + Rep + SElem) & 0x1f; + + if (IsLoad) + { + Context.EmitLdarg(ATranslatedSub.MemoryArgIdx); + Context.EmitLdint(Op.Rn); + Context.EmitLdc_I8(Offset); + + Context.Emit(OpCodes.Add); + + EmitReadZxCall(Context, Op.Size); + + EmitVectorInsert(Context, Rtt, Elem, Op.Size); + + if (Op.RegisterSize == ARegisterSize.SIMD64 && Elem == Op.Elems - 1) + { + EmitVectorZeroUpper(Context, Rtt); + } + } + else + { + Context.EmitLdarg(ATranslatedSub.MemoryArgIdx); + Context.EmitLdint(Op.Rn); + Context.EmitLdc_I8(Offset); + + Context.Emit(OpCodes.Add); + + EmitVectorExtractZx(Context, Rtt, Elem, Op.Size); + + EmitWriteCall(Context, Op.Size); + } + + Offset += 1 << Op.Size; + } + + if (Op.WBack) + { + EmitSimdMemWBack(Context, Offset); + } + } + + private static void EmitSimdMemSs(AILEmitterCtx Context, bool IsLoad) + { + AOpCodeSimdMemSs Op = (AOpCodeSimdMemSs)Context.CurrOp; + + int Offset = 0; + + void EmitMemAddress() + { + Context.EmitLdarg(ATranslatedSub.MemoryArgIdx); + Context.EmitLdint(Op.Rn); + Context.EmitLdc_I8(Offset); + + Context.Emit(OpCodes.Add); + } + + if (Op.Replicate) + { + //Only loads uses the replicate mode. + if (!IsLoad) + { + throw new InvalidOperationException(); + } + + int Bytes = Context.CurrOp.GetBitsCount() >> 3; + + for (int SElem = 0; SElem < Op.SElems; SElem++) + { + int Rt = (Op.Rt + SElem) & 0x1f; + + for (int Index = 0; Index < (Bytes >> Op.Size); Index++) + { + EmitMemAddress(); + + EmitReadZxCall(Context, Op.Size); + + EmitVectorInsert(Context, Rt, Index, Op.Size); + } + + if (Op.RegisterSize == ARegisterSize.SIMD64) + { + EmitVectorZeroUpper(Context, Rt); + } + + Offset += 1 << Op.Size; + } + } + else + { + for (int SElem = 0; SElem < Op.SElems; SElem++) + { + int Rt = (Op.Rt + SElem) & 0x1f; + + if (IsLoad) + { + EmitMemAddress(); + + EmitReadZxCall(Context, Op.Size); + + EmitVectorInsert(Context, Rt, Op.Index, Op.Size); + } + else + { + EmitMemAddress(); + + EmitVectorExtractZx(Context, Rt, Op.Index, Op.Size); + + EmitWriteCall(Context, Op.Size); + } + + Offset += 1 << Op.Size; + } + } + + if (Op.WBack) + { + EmitSimdMemWBack(Context, Offset); + } + } + + private static void EmitSimdMemWBack(AILEmitterCtx Context, int Offset) + { + AOpCodeMemReg Op = (AOpCodeMemReg)Context.CurrOp; + + Context.EmitLdint(Op.Rn); + + if (Op.Rm != AThreadState.ZRIndex) + { + Context.EmitLdint(Op.Rm); + } + else + { + Context.EmitLdc_I8(Offset); + } + + Context.Emit(OpCodes.Add); + + Context.EmitStint(Op.Rn); + } + } +} \ No newline at end of file diff --git a/Ryujinx/Cpu/Instruction/AInstEmitSimdMove.cs b/Ryujinx/Cpu/Instruction/AInstEmitSimdMove.cs new file mode 100644 index 000000000..c8f690328 --- /dev/null +++ b/Ryujinx/Cpu/Instruction/AInstEmitSimdMove.cs @@ -0,0 +1,299 @@ +using ChocolArm64.Decoder; +using ChocolArm64.State; +using ChocolArm64.Translation; +using System; +using System.Reflection.Emit; + +using static ChocolArm64.Instruction.AInstEmitSimdHelper; + +namespace ChocolArm64.Instruction +{ + static partial class AInstEmit + { + public static void Dup_Gp(AILEmitterCtx Context) + { + AOpCodeSimdIns Op = (AOpCodeSimdIns)Context.CurrOp; + + int Bytes = Context.CurrOp.GetBitsCount() >> 3; + + for (int Index = 0; Index < (Bytes >> Op.Size); Index++) + { + Context.EmitLdintzr(Op.Rn); + + EmitVectorInsert(Context, Op.Rd, Index, Op.Size); + } + + if (Op.RegisterSize == ARegisterSize.SIMD64) + { + EmitVectorZeroUpper(Context, Op.Rd); + } + } + + public static void Dup_S(AILEmitterCtx Context) + { + AOpCodeSimdIns Op = (AOpCodeSimdIns)Context.CurrOp; + + EmitVectorExtractZx(Context, Op.Rn, Op.DstIndex, Op.Size); + + EmitScalarSet(Context, Op.Rd, Op.Size); + } + + public static void Dup_V(AILEmitterCtx Context) + { + AOpCodeSimdIns Op = (AOpCodeSimdIns)Context.CurrOp; + + int Bytes = Context.CurrOp.GetBitsCount() >> 3; + + for (int Index = 0; Index < (Bytes >> Op.Size); Index++) + { + EmitVectorExtractZx(Context, Op.Rn, Op.DstIndex, Op.Size); + + EmitVectorInsert(Context, Op.Rd, Index, Op.Size); + } + + if (Op.RegisterSize == ARegisterSize.SIMD64) + { + EmitVectorZeroUpper(Context, Op.Rd); + } + } + + public static void Fcsel_S(AILEmitterCtx Context) + { + AOpCodeSimdFcond Op = (AOpCodeSimdFcond)Context.CurrOp; + + AILLabel LblTrue = new AILLabel(); + AILLabel LblEnd = new AILLabel(); + + Context.EmitCondBranch(LblTrue, Op.Cond); + + EmitVectorExtractF(Context, Op.Rm, 0, Op.Size); + + Context.Emit(OpCodes.Br_S, LblEnd); + + Context.MarkLabel(LblTrue); + + EmitVectorExtractF(Context, Op.Rn, 0, Op.Size); + + Context.MarkLabel(LblEnd); + + EmitScalarSetF(Context, Op.Rd, Op.Size); + } + + public static void Fmov_Ftoi(AILEmitterCtx Context) + { + AOpCodeSimdCvt Op = (AOpCodeSimdCvt)Context.CurrOp; + + EmitVectorExtractZx(Context, Op.Rn, 0, 3); + + EmitIntZeroHigherIfNeeded(Context); + + Context.EmitStintzr(Op.Rd); + } + + public static void Fmov_Ftoi1(AILEmitterCtx Context) + { + AOpCodeSimdCvt Op = (AOpCodeSimdCvt)Context.CurrOp; + + EmitVectorExtractZx(Context, Op.Rn, 1, 3); + + EmitIntZeroHigherIfNeeded(Context); + + Context.EmitStintzr(Op.Rd); + } + + public static void Fmov_Itof(AILEmitterCtx Context) + { + AOpCodeSimdCvt Op = (AOpCodeSimdCvt)Context.CurrOp; + + Context.EmitLdintzr(Op.Rn); + + EmitIntZeroHigherIfNeeded(Context); + + EmitScalarSet(Context, Op.Rd, 3); + } + + public static void Fmov_Itof1(AILEmitterCtx Context) + { + AOpCodeSimdCvt Op = (AOpCodeSimdCvt)Context.CurrOp; + + Context.EmitLdintzr(Op.Rn); + + EmitIntZeroHigherIfNeeded(Context); + + EmitVectorInsert(Context, Op.Rd, 1, 3); + } + + public static void Fmov_S(AILEmitterCtx Context) + { + AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp; + + EmitVectorExtractF(Context, Op.Rn, 0, Op.Size); + + EmitScalarSetF(Context, Op.Rd, Op.Size); + } + + public static void Fmov_Si(AILEmitterCtx Context) + { + AOpCodeSimdFmov Op = (AOpCodeSimdFmov)Context.CurrOp; + + Context.EmitLdc_I8(Op.Imm); + + EmitScalarSet(Context, Op.Rd, Op.Size + 2); + } + + public static void Fmov_V(AILEmitterCtx Context) + { + AOpCodeSimdImm Op = (AOpCodeSimdImm)Context.CurrOp; + + int Elems = Op.RegisterSize == ARegisterSize.SIMD128 ? 4 : 2; + + for (int Index = 0; Index < (Elems >> Op.Size); Index++) + { + Context.EmitLdc_I8(Op.Imm); + + EmitVectorInsert(Context, Op.Rd, Index, Op.Size + 2); + } + + if (Op.RegisterSize == ARegisterSize.SIMD64) + { + EmitVectorZeroUpper(Context, Op.Rd); + } + } + + public static void Ins_Gp(AILEmitterCtx Context) + { + AOpCodeSimdIns Op = (AOpCodeSimdIns)Context.CurrOp; + + Context.EmitLdintzr(Op.Rn); + + EmitVectorInsert(Context, Op.Rd, Op.DstIndex, Op.Size); + } + + public static void Ins_V(AILEmitterCtx Context) + { + AOpCodeSimdIns Op = (AOpCodeSimdIns)Context.CurrOp; + + EmitVectorExtractZx(Context, Op.Rn, Op.SrcIndex, Op.Size); + + EmitVectorInsert(Context, Op.Rd, Op.DstIndex, Op.Size); + } + + public static void Movi_V(AILEmitterCtx Context) + { + EmitVectorImmUnaryOp(Context, () => { }); + } + + public static void Mvni_V(AILEmitterCtx Context) + { + EmitVectorImmUnaryOp(Context, () => Context.Emit(OpCodes.Not)); + } + + public static void Tbl_V(AILEmitterCtx Context) + { + AOpCodeSimdTbl Op = (AOpCodeSimdTbl)Context.CurrOp; + + Context.EmitLdvec(Op.Rm); + + for (int Index = 0; Index < Op.Size; Index++) + { + Context.EmitLdvec((Op.Rn + Index) & 0x1f); + } + + switch (Op.Size) + { + case 1: ASoftFallback.EmitCall(Context, + nameof(ASoftFallback.Tbl1_V64), + nameof(ASoftFallback.Tbl1_V128)); break; + + case 2: ASoftFallback.EmitCall(Context, + nameof(ASoftFallback.Tbl2_V64), + nameof(ASoftFallback.Tbl2_V128)); break; + + case 3: ASoftFallback.EmitCall(Context, + nameof(ASoftFallback.Tbl3_V64), + nameof(ASoftFallback.Tbl3_V128)); break; + + case 4: ASoftFallback.EmitCall(Context, + nameof(ASoftFallback.Tbl4_V64), + nameof(ASoftFallback.Tbl4_V128)); break; + + default: throw new InvalidOperationException(); + } + + Context.EmitStvec(Op.Rd); + } + + public static void Umov_S(AILEmitterCtx Context) + { + AOpCodeSimdIns Op = (AOpCodeSimdIns)Context.CurrOp; + + EmitVectorExtractZx(Context, Op.Rn, Op.DstIndex, Op.Size); + + Context.EmitStintzr(Op.Rd); + } + + public static void Uzp1_V(AILEmitterCtx Context) + { + EmitVectorUnzip(Context, Part: 0); + } + + public static void Uzp2_V(AILEmitterCtx Context) + { + EmitVectorUnzip(Context, Part: 1); + } + + public static void Xtn_V(AILEmitterCtx Context) + { + AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp; + + int Elems = 8 >> Op.Size; + + int Part = Op.RegisterSize == ARegisterSize.SIMD128 ? Elems : 0; + + for (int Index = 0; Index < Elems; Index++) + { + EmitVectorExtractZx(Context, Op.Rn, Index, Op.Size + 1); + + EmitVectorInsert(Context, Op.Rd, Part + Index, Op.Size); + } + + if (Part == 0) + { + EmitVectorZeroUpper(Context, Op.Rd); + } + } + + private static void EmitIntZeroHigherIfNeeded(AILEmitterCtx Context) + { + if (Context.CurrOp.RegisterSize == ARegisterSize.Int32) + { + Context.Emit(OpCodes.Conv_U4); + Context.Emit(OpCodes.Conv_U8); + } + } + + private static void EmitVectorUnzip(AILEmitterCtx Context, int Part) + { + AOpCodeSimdReg Op = (AOpCodeSimdReg)Context.CurrOp; + + int Bytes = Context.CurrOp.GetBitsCount() >> 3; + + int Elems = Bytes >> Op.Size; + int Half = Elems >> 1; + + for (int Index = 0; Index < Elems; Index++) + { + int Elem = Part + ((Index & (Half - 1)) << 1); + + EmitVectorExtractZx(Context, Index < Half ? Op.Rn : Op.Rm, Elem, Op.Size); + + EmitVectorInsert(Context, Op.Rd, Index, Op.Size); + } + + if (Op.RegisterSize == ARegisterSize.SIMD64) + { + EmitVectorZeroUpper(Context, Op.Rd); + } + } + } +} \ No newline at end of file diff --git a/Ryujinx/Cpu/Instruction/AInstEmitSimdShift.cs b/Ryujinx/Cpu/Instruction/AInstEmitSimdShift.cs new file mode 100644 index 000000000..165642342 --- /dev/null +++ b/Ryujinx/Cpu/Instruction/AInstEmitSimdShift.cs @@ -0,0 +1,306 @@ +using ChocolArm64.Decoder; +using ChocolArm64.State; +using ChocolArm64.Translation; +using System; +using System.Reflection.Emit; + +using static ChocolArm64.Instruction.AInstEmitSimdHelper; + +namespace ChocolArm64.Instruction +{ + static partial class AInstEmit + { + [Flags] + private enum ShrFlags + { + None = 0, + Signed = 1 << 0, + Rounding = 1 << 1, + Accumulate = 1 << 2 + } + + public static void Shl_S(AILEmitterCtx Context) + { + AOpCodeSimdShImm Op = (AOpCodeSimdShImm)Context.CurrOp; + + EmitVectorExtractZx(Context, Op.Rn, 0, Op.Size); + + Context.EmitLdc_I4(GetImmShl(Op)); + + Context.Emit(OpCodes.Shl); + + EmitScalarSet(Context, Op.Rd, Op.Size); + } + + public static void Shl_V(AILEmitterCtx Context) + { + AOpCodeSimdShImm Op = (AOpCodeSimdShImm)Context.CurrOp; + + int Shift = Op.Imm - (8 << Op.Size); + + EmitVectorShImmBinaryZx(Context, () => Context.Emit(OpCodes.Shl), Shift); + } + + public static void Shrn_V(AILEmitterCtx Context) + { + AOpCodeSimdShImm Op = (AOpCodeSimdShImm)Context.CurrOp; + + int Shift = (8 << (Op.Size + 1)) - Op.Imm; + + EmitVectorShImmNarrowBinaryZx(Context, () => Context.Emit(OpCodes.Shr_Un), Shift); + } + + public static void Sshl_V(AILEmitterCtx Context) + { + EmitVectorShl(Context, Signed: true); + } + + public static void Sshll_V(AILEmitterCtx Context) + { + AOpCodeSimdShImm Op = (AOpCodeSimdShImm)Context.CurrOp; + + int Shift = Op.Imm - (8 << Op.Size); + + EmitVectorShImmWidenBinarySx(Context, () => Context.Emit(OpCodes.Shl), Shift); + } + + public static void Sshr_S(AILEmitterCtx Context) + { + AOpCodeSimdShImm Op = (AOpCodeSimdShImm)Context.CurrOp; + + EmitVectorExtractSx(Context, Op.Rn, 0, Op.Size); + + Context.EmitLdc_I4(GetImmShr(Op)); + + Context.Emit(OpCodes.Shr); + + EmitScalarSet(Context, Op.Rd, Op.Size); + } + + public static void Sshr_V(AILEmitterCtx Context) + { + AOpCodeSimdShImm Op = (AOpCodeSimdShImm)Context.CurrOp; + + int Shift = (8 << (Op.Size + 1)) - Op.Imm; + + EmitVectorShImmBinarySx(Context, () => Context.Emit(OpCodes.Shr), Shift); + } + + public static void Ushl_V(AILEmitterCtx Context) + { + EmitVectorShl(Context, Signed: false); + } + + public static void Ushll_V(AILEmitterCtx Context) + { + AOpCodeSimdShImm Op = (AOpCodeSimdShImm)Context.CurrOp; + + int Shift = Op.Imm - (8 << Op.Size); + + EmitVectorShImmWidenBinaryZx(Context, () => Context.Emit(OpCodes.Shl), Shift); + } + + public static void Ushr_V(AILEmitterCtx Context) + { + EmitVectorShr(Context, ShrFlags.None); + } + + public static void Usra_V(AILEmitterCtx Context) + { + EmitVectorShr(Context, ShrFlags.Accumulate); + } + + private static void EmitVectorShl(AILEmitterCtx Context, bool Signed) + { + //This instruction shifts the value on vector A by the number of bits + //specified on the signed, lower 8 bits of vector B. If the shift value + //is greater or equal to the data size of each lane, then the result is zero. + //Additionally, negative shifts produces right shifts by the negated shift value. + AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp; + + int MaxShift = 8 << Op.Size; + + Action Emit = () => + { + AILLabel LblShl = new AILLabel(); + AILLabel LblZero = new AILLabel(); + AILLabel LblEnd = new AILLabel(); + + void EmitShift(OpCode ILOp) + { + Context.Emit(OpCodes.Dup); + + Context.EmitLdc_I4(MaxShift); + + Context.Emit(OpCodes.Bge_S, LblZero); + Context.Emit(ILOp); + Context.Emit(OpCodes.Br_S, LblEnd); + } + + Context.Emit(OpCodes.Conv_I1); + Context.Emit(OpCodes.Dup); + + Context.EmitLdc_I4(0); + + Context.Emit(OpCodes.Bge_S, LblShl); + Context.Emit(OpCodes.Neg); + + EmitShift(Signed + ? OpCodes.Shr + : OpCodes.Shr_Un); + + Context.MarkLabel(LblShl); + + EmitShift(OpCodes.Shl); + + Context.MarkLabel(LblZero); + + Context.Emit(OpCodes.Pop); + Context.Emit(OpCodes.Pop); + + Context.EmitLdc_I8(0); + + Context.MarkLabel(LblEnd); + }; + + if (Signed) + { + EmitVectorBinaryOpSx(Context, Emit); + } + else + { + EmitVectorBinaryOpZx(Context, Emit); + } + } + + private static void EmitVectorShr(AILEmitterCtx Context, ShrFlags Flags) + { + AOpCodeSimdShImm Op = (AOpCodeSimdShImm)Context.CurrOp; + + int Shift = (8 << (Op.Size + 1)) - Op.Imm; + + if (Flags.HasFlag(ShrFlags.Accumulate)) + { + Action Emit = () => + { + Context.EmitLdc_I4(Shift); + + Context.Emit(OpCodes.Shr_Un); + Context.Emit(OpCodes.Add); + }; + + EmitVectorOp(Context, Emit, OperFlags.RdRn, Signed: false); + } + else + { + EmitVectorUnaryOpZx(Context, () => + { + Context.EmitLdc_I4(Shift); + + Context.Emit(OpCodes.Shr_Un); + }); + } + } + + private static void EmitVectorShImmBinarySx(AILEmitterCtx Context, Action Emit, int Imm) + { + EmitVectorShImmBinaryOp(Context, Emit, Imm, true); + } + + private static void EmitVectorShImmBinaryZx(AILEmitterCtx Context, Action Emit, int Imm) + { + EmitVectorShImmBinaryOp(Context, Emit, Imm, false); + } + + private static void EmitVectorShImmBinaryOp(AILEmitterCtx Context, Action Emit, int Imm, bool Signed) + { + AOpCodeSimdShImm Op = (AOpCodeSimdShImm)Context.CurrOp; + + int Bytes = Context.CurrOp.GetBitsCount() >> 3; + + for (int Index = 0; Index < (Bytes >> Op.Size); Index++) + { + EmitVectorExtract(Context, Op.Rn, Index, Op.Size, Signed); + + Context.EmitLdc_I4(Imm); + + Emit(); + + EmitVectorInsert(Context, Op.Rd, Index, Op.Size); + } + + if (Op.RegisterSize == ARegisterSize.SIMD64) + { + EmitVectorZeroUpper(Context, Op.Rd); + } + } + + private static void EmitVectorShImmNarrowBinarySx(AILEmitterCtx Context, Action Emit, int Imm) + { + EmitVectorShImmNarrowBinaryOp(Context, Emit, Imm, true); + } + + private static void EmitVectorShImmNarrowBinaryZx(AILEmitterCtx Context, Action Emit, int Imm) + { + EmitVectorShImmNarrowBinaryOp(Context, Emit, Imm, false); + } + + private static void EmitVectorShImmNarrowBinaryOp(AILEmitterCtx Context, Action Emit, int Imm, bool Signed) + { + AOpCodeSimdShImm Op = (AOpCodeSimdShImm)Context.CurrOp; + + int Elems = 8 >> Op.Size; + + int Part = Op.RegisterSize == ARegisterSize.SIMD128 ? Elems : 0; + + for (int Index = 0; Index < Elems; Index++) + { + EmitVectorExtract(Context, Op.Rn, Index, Op.Size + 1, Signed); + + Context.EmitLdc_I4(Imm); + + Emit(); + + EmitVectorInsert(Context, Op.Rd, Part + Index, Op.Size); + } + + if (Part == 0) + { + EmitVectorZeroUpper(Context, Op.Rd); + } + } + + private static void EmitVectorShImmWidenBinarySx(AILEmitterCtx Context, Action Emit, int Imm) + { + EmitVectorShImmWidenBinaryOp(Context, Emit, Imm, true); + } + + private static void EmitVectorShImmWidenBinaryZx(AILEmitterCtx Context, Action Emit, int Imm) + { + EmitVectorShImmWidenBinaryOp(Context, Emit, Imm, false); + } + + private static void EmitVectorShImmWidenBinaryOp(AILEmitterCtx Context, Action Emit, int Imm, bool Signed) + { + AOpCodeSimdShImm Op = (AOpCodeSimdShImm)Context.CurrOp; + + int Elems = 8 >> Op.Size; + + int Part = Op.RegisterSize == ARegisterSize.SIMD128 ? Elems : 0; + + for (int Index = 0; Index < Elems; Index++) + { + EmitVectorExtract(Context, Op.Rn, Part + Index, Op.Size, Signed); + + Context.EmitLdc_I4(Imm); + + Emit(); + + EmitVectorInsertTmp(Context, Index, Op.Size + 1); + } + + Context.EmitLdvectmp(); + Context.EmitStvec(Op.Rd); + } + } +} \ No newline at end of file diff --git a/Ryujinx/Cpu/Instruction/AInstEmitSystem.cs b/Ryujinx/Cpu/Instruction/AInstEmitSystem.cs index 9a6333cd9..6754be7a8 100644 --- a/Ryujinx/Cpu/Instruction/AInstEmitSystem.cs +++ b/Ryujinx/Cpu/Instruction/AInstEmitSystem.cs @@ -13,26 +13,26 @@ namespace ChocolArm64.Instruction { AOpCodeSystem Op = (AOpCodeSystem)Context.CurrOp; - Context.EmitLdarg(ATranslatedSub.RegistersArgIdx); + Context.EmitLdarg(ATranslatedSub.StateArgIdx); string PropName; switch (GetPackedId(Op)) { - case 0b11_011_0000_0000_001: PropName = nameof(ARegisters.CtrEl0); break; - case 0b11_011_0000_0000_111: PropName = nameof(ARegisters.DczidEl0); break; - case 0b11_011_0100_0100_000: PropName = nameof(ARegisters.Fpcr); break; - case 0b11_011_0100_0100_001: PropName = nameof(ARegisters.Fpsr); break; - case 0b11_011_1101_0000_010: PropName = nameof(ARegisters.TpidrEl0); break; - case 0b11_011_1101_0000_011: PropName = nameof(ARegisters.Tpidr); break; - case 0b11_011_1110_0000_001: PropName = nameof(ARegisters.CntpctEl0); break; + case 0b11_011_0000_0000_001: PropName = nameof(AThreadState.CtrEl0); break; + case 0b11_011_0000_0000_111: PropName = nameof(AThreadState.DczidEl0); break; + case 0b11_011_0100_0100_000: PropName = nameof(AThreadState.Fpcr); break; + 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_001: PropName = nameof(AThreadState.CntpctEl0); break; default: throw new NotImplementedException($"Unknown MRS at {Op.Position:x16}"); } - Context.EmitCallPropGet(typeof(ARegisters), PropName); + Context.EmitCallPropGet(typeof(AThreadState), PropName); - PropertyInfo PropInfo = typeof(ARegisters).GetProperty(PropName); + PropertyInfo PropInfo = typeof(AThreadState).GetProperty(PropName); if (PropInfo.PropertyType != typeof(long) && PropInfo.PropertyType != typeof(ulong)) @@ -47,21 +47,21 @@ namespace ChocolArm64.Instruction { AOpCodeSystem Op = (AOpCodeSystem)Context.CurrOp; - Context.EmitLdarg(ATranslatedSub.RegistersArgIdx); + Context.EmitLdarg(ATranslatedSub.StateArgIdx); Context.EmitLdintzr(Op.Rt); string PropName; switch (GetPackedId(Op)) { - case 0b11_011_0100_0100_000: PropName = nameof(ARegisters.Fpcr); break; - case 0b11_011_0100_0100_001: PropName = nameof(ARegisters.Fpsr); break; - case 0b11_011_1101_0000_010: PropName = nameof(ARegisters.TpidrEl0); break; + case 0b11_011_0100_0100_000: PropName = nameof(AThreadState.Fpcr); break; + case 0b11_011_0100_0100_001: PropName = nameof(AThreadState.Fpsr); break; + case 0b11_011_1101_0000_010: PropName = nameof(AThreadState.TpidrEl0); break; default: throw new NotImplementedException($"Unknown MSR at {Op.Position:x16}"); } - PropertyInfo PropInfo = typeof(ARegisters).GetProperty(PropName); + PropertyInfo PropInfo = typeof(AThreadState).GetProperty(PropName); if (PropInfo.PropertyType != typeof(long) && PropInfo.PropertyType != typeof(ulong)) @@ -69,7 +69,7 @@ namespace ChocolArm64.Instruction Context.Emit(OpCodes.Conv_U4); } - Context.EmitCallPropSet(typeof(ARegisters), PropName); + Context.EmitCallPropSet(typeof(AThreadState), PropName); } public static void Nop(AILEmitterCtx Context) @@ -89,7 +89,7 @@ namespace ChocolArm64.Instruction case 0b11_011_0111_0100_001: { //DC ZVA - for (int Offs = 0; Offs < (4 << ARegisters.DczSizeLog2); Offs += 8) + for (int Offs = 0; Offs < (4 << AThreadState.DczSizeLog2); Offs += 8) { Context.EmitLdarg(ATranslatedSub.MemoryArgIdx); Context.EmitLdint(Op.Rt); diff --git a/Ryujinx/Cpu/Instruction/ASoftFallback.cs b/Ryujinx/Cpu/Instruction/ASoftFallback.cs index 2bd25793b..b70d0b4f4 100644 --- a/Ryujinx/Cpu/Instruction/ASoftFallback.cs +++ b/Ryujinx/Cpu/Instruction/ASoftFallback.cs @@ -1,6 +1,7 @@ using ChocolArm64.State; using ChocolArm64.Translation; using System; +using System.Runtime.CompilerServices; namespace ChocolArm64.Instruction { @@ -97,142 +98,62 @@ namespace ChocolArm64.Instruction throw new ArgumentException(nameof(Size)); } - public static int SatSingleToInt32(float Value, int FBits) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static int SatF32ToS32(float Value) { - if (FBits != 0) Value *= MathF.Pow(2, FBits); - return Value > int.MaxValue ? int.MaxValue : Value < int.MinValue ? int.MinValue : (int)Value; } - public static long SatSingleToInt64(float Value, int FBits) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static long SatF32ToS64(float Value) { - if (FBits != 0) Value *= MathF.Pow(2, FBits); - return Value > long.MaxValue ? long.MaxValue : Value < long.MinValue ? long.MinValue : (long)Value; } - public static uint SatSingleToUInt32(float Value, int FBits) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static uint SatF32ToU32(float Value) { - if (FBits != 0) Value *= MathF.Pow(2, FBits); - return Value > uint.MaxValue ? uint.MaxValue : Value < uint.MinValue ? uint.MinValue : (uint)Value; } - public static ulong SatSingleToUInt64(float Value, int FBits) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static ulong SatF32ToU64(float Value) { - if (FBits != 0) Value *= MathF.Pow(2, FBits); - return Value > ulong.MaxValue ? ulong.MaxValue : Value < ulong.MinValue ? ulong.MinValue : (ulong)Value; } - public static int SatDoubleToInt32(double Value, int FBits) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static int SatF64ToS32(double Value) { - if (FBits != 0) Value *= Math.Pow(2, FBits); - return Value > int.MaxValue ? int.MaxValue : Value < int.MinValue ? int.MinValue : (int)Value; } - public static long SatDoubleToInt64(double Value, int FBits) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static long SatF64ToS64(double Value) { - if (FBits != 0) Value *= Math.Pow(2, FBits); - return Value > long.MaxValue ? long.MaxValue : Value < long.MinValue ? long.MinValue : (long)Value; } - public static uint SatDoubleToUInt32(double Value, int FBits) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static uint SatF64ToU32(double Value) { - if (FBits != 0) Value *= Math.Pow(2, FBits); - return Value > uint.MaxValue ? uint.MaxValue : Value < uint.MinValue ? uint.MinValue : (uint)Value; } - public static ulong SatDoubleToUInt64(double Value, int FBits) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static ulong SatF64ToU64(double Value) { - if (FBits != 0) Value *= Math.Pow(2, FBits); - return Value > ulong.MaxValue ? ulong.MaxValue : Value < ulong.MinValue ? ulong.MinValue : (ulong)Value; } - public static float Int32ToSingle(int Value, int FBits) - { - float ValueF = Value; - - if (FBits != 0) ValueF *= 1 / MathF.Pow(2, FBits); - - return ValueF; - } - - public static float Int64ToSingle(long Value, int FBits) - { - float ValueF = Value; - - if (FBits != 0) ValueF *= 1 / MathF.Pow(2, FBits); - - return ValueF; - } - - public static float UInt32ToSingle(uint Value, int FBits) - { - float ValueF = Value; - - if (FBits != 0) ValueF *= 1 / MathF.Pow(2, FBits); - - return ValueF; - } - - public static float UInt64ToSingle(ulong Value, int FBits) - { - float ValueF = Value; - - if (FBits != 0) ValueF *= 1 / MathF.Pow(2, FBits); - - return ValueF; - } - - public static double Int32ToDouble(int Value, int FBits) - { - double ValueF = Value; - - if (FBits != 0) ValueF *= 1 / Math.Pow(2, FBits); - - return ValueF; - } - - public static double Int64ToDouble(long Value, int FBits) - { - double ValueF = Value; - - if (FBits != 0) ValueF *= 1 / Math.Pow(2, FBits); - - return ValueF; - } - - public static double UInt32ToDouble(uint Value, int FBits) - { - double ValueF = Value; - - if (FBits != 0) ValueF *= 1 / Math.Pow(2, FBits); - - return ValueF; - } - - public static double UInt64ToDouble(ulong Value, int FBits) - { - double ValueF = Value; - - if (FBits != 0) ValueF *= 1 / Math.Pow(2, FBits); - - return ValueF; - } - public static ulong SMulHi128(ulong LHS, ulong RHS) { long LLo = (uint)(LHS >> 0); @@ -269,116 +190,9 @@ namespace ChocolArm64.Instruction ulong ResHi = LHiRHi + (LHiRLo >> 32) + (LLoRHi >> 32) + Carry; return ResHi; - } - - public static AVec Addp_S(AVec Vector, int Size) - { - ulong Low = ExtractVec(Vector, 0, Size); - ulong High = ExtractVec(Vector, 1, Size); - - return InsertVec(new AVec(), 0, Size, Low + High); } - public static AVec Addp64(AVec LHS, AVec RHS, int Size) - { - return Addp(LHS, RHS, Size, 8); - } - - public static AVec Addp128(AVec LHS, AVec RHS, int Size) - { - return Addp(LHS, RHS, Size, 16); - } - - private static AVec Addp(AVec LHS, AVec RHS, int Size, int Bytes) - { - AVec Res = new AVec(); - - int Elems = Bytes >> Size; - int Half = Elems >> 1; - - for (int Index = 0; Index < Elems; Index++) - { - int Elem = (Index & (Half - 1)) << 1; - - ulong L = Index < Half - ? ExtractVec(LHS, Elem + 0, Size) - : ExtractVec(RHS, Elem + 0, Size); - - ulong R = Index < Half - ? ExtractVec(LHS, Elem + 1, Size) - : ExtractVec(RHS, Elem + 1, Size); - - Res = InsertVec(Res, Index, Size, L + R); - } - - return Res; - } - - public static AVec Bic_Vi64(AVec Res, ulong Imm, int Size) - { - return Bic_Vi(Res, Imm, Size, 8); - } - - public static AVec Bic_Vi128(AVec Res, ulong Imm, int Size) - { - return Bic_Vi(Res, Imm, Size, 16); - } - - private static AVec Bic_Vi(AVec Res, ulong Imm, int Size, int Bytes) - { - int Elems = Bytes >> Size; - - for (int Index = 0; Index < Elems; Index++) - { - ulong Value = ExtractVec(Res, Index, Size); - - Res = InsertVec(Res, Index, Size, Value & ~Imm); - } - - return Res; - } - - public static AVec Cnt64(AVec Vector) - { - AVec Res = new AVec(); - - Res.B0 = (byte)CountSetBits8(Vector.B0); - Res.B1 = (byte)CountSetBits8(Vector.B1); - Res.B2 = (byte)CountSetBits8(Vector.B2); - Res.B3 = (byte)CountSetBits8(Vector.B3); - Res.B4 = (byte)CountSetBits8(Vector.B4); - Res.B5 = (byte)CountSetBits8(Vector.B5); - Res.B6 = (byte)CountSetBits8(Vector.B6); - Res.B7 = (byte)CountSetBits8(Vector.B7); - - return Res; - } - - public static AVec Cnt128(AVec Vector) - { - AVec Res = new AVec(); - - Res.B0 = (byte)CountSetBits8(Vector.B0); - Res.B1 = (byte)CountSetBits8(Vector.B1); - Res.B2 = (byte)CountSetBits8(Vector.B2); - Res.B3 = (byte)CountSetBits8(Vector.B3); - Res.B4 = (byte)CountSetBits8(Vector.B4); - Res.B5 = (byte)CountSetBits8(Vector.B5); - Res.B6 = (byte)CountSetBits8(Vector.B6); - Res.B7 = (byte)CountSetBits8(Vector.B7); - Res.B8 = (byte)CountSetBits8(Vector.B8); - Res.B9 = (byte)CountSetBits8(Vector.B9); - Res.B10 = (byte)CountSetBits8(Vector.B10); - Res.B11 = (byte)CountSetBits8(Vector.B11); - Res.B12 = (byte)CountSetBits8(Vector.B12); - Res.B13 = (byte)CountSetBits8(Vector.B13); - Res.B14 = (byte)CountSetBits8(Vector.B14); - Res.B15 = (byte)CountSetBits8(Vector.B15); - - return Res; - } - - private static int CountSetBits8(byte Value) + public static int CountSetBits8(byte Value) { return (Value >> 0) & 1 + (Value >> 1) & 1 + (Value >> 2) & 1 + (Value >> 3) & 1 + @@ -386,275 +200,6 @@ namespace ChocolArm64.Instruction (Value >> 6) & 1 + (Value >> 7); } - public static AVec Dup_Gp64(ulong Value, int Size) - { - return Dup_Gp(Value, Size, 8); - } - - public static AVec Dup_Gp128(ulong Value, int Size) - { - return Dup_Gp(Value, Size, 16); - } - - private static AVec Dup_Gp(ulong Value, int Size, int Bytes) - { - AVec Res = new AVec(); - - for (int Index = 0; Index < (Bytes >> Size); Index++) - { - Res = InsertVec(Res, Index, Size, Value); - } - - return Res; - } - - public static AVec Dup_S(AVec Vector, int Elem, int Size) - { - return InsertVec(new AVec(), 0, Size, ExtractVec(Vector, Elem, Size)); - } - - public static AVec Dup_V64(AVec Vector, int Elem, int Size) - { - return Dup_V(Vector, Elem, Size, 8); - } - - public static AVec Dup_V128(AVec Vector, int Elem, int Size) - { - return Dup_V(Vector, Elem, Size, 16); - } - - private static AVec Dup_V(AVec Vector, int Elem, int Size, int Bytes) - { - AVec Res = new AVec(); - - ulong Value = ExtractVec(Vector, Elem, Size); - - for (Elem = 0; Elem < (Bytes >> Size); Elem++) - { - Res = InsertVec(Res, Elem, Size, Value); - } - - return Res; - } - - public static AVec Fmla64(AVec Res, AVec LHS, AVec RHS, int Size) - { - return Fmla(Res, LHS, RHS, Size, 2); - } - - public static AVec Fmla128(AVec Res, AVec LHS, AVec RHS, int Size) - { - return Fmla(Res, LHS, RHS, Size, 4); - } - - private static AVec Fmla(AVec Res, AVec LHS, AVec RHS, int Size, int Bytes) - { - int Elems = Bytes >> Size; - - if (Size == 0) - { - for (int Index = 0; Index < Elems; Index++) - { - float L = LHS.ExtractSingle(Index); - float R = RHS.ExtractSingle(Index); - float Addend = Res.ExtractSingle(Index); - - Res = AVec.InsertSingle(Res, Index, Addend + L * R); - } - } - else - { - for (int Index = 0; Index < Elems; Index++) - { - double L = LHS.ExtractDouble(Index); - double R = RHS.ExtractDouble(Index); - double Addend = Res.ExtractDouble(Index); - - Res = AVec.InsertDouble(Res, Index, Addend + L * R); - } - } - - return Res; - } - - public static AVec Fmla_Ve64(AVec Res, AVec LHS, AVec RHS, int SIdx, int Size) - { - return Fmla_Ve(Res, LHS, RHS, SIdx, Size, 2); - } - - public static AVec Fmla_Ve128(AVec Res, AVec LHS, AVec RHS, int SIdx, int Size) - { - return Fmla_Ve(Res, LHS, RHS, SIdx, Size, 4); - } - - private static AVec Fmla_Ve(AVec Res, AVec LHS, AVec RHS, int SIdx, int Size, int Bytes) - { - int Elems = Bytes >> Size; - - if (Size == 0) - { - float R = RHS.ExtractSingle(SIdx); - - for (int Index = 0; Index < Elems; Index++) - { - float L = LHS.ExtractSingle(Index); - float Addend = Res.ExtractSingle(Index); - - Res = AVec.InsertSingle(Res, Index, Addend + L * R); - } - } - else - { - double R = RHS.ExtractDouble(SIdx); - - for (int Index = 0; Index < Elems; Index++) - { - double L = LHS.ExtractDouble(Index); - double Addend = Res.ExtractDouble(Index); - - Res = AVec.InsertDouble(Res, Index, Addend + L * R); - } - } - - return Res; - } - - public static AVec Fmov_S(ulong Value, int Elem, int Size) - { - return InsertVec(new AVec(), Elem, Size, Value); - } - - public static AVec Fmul_Ve64(AVec LHS, AVec RHS, int SIdx, int Size) - { - return Fmul_Ve(LHS, RHS, SIdx, Size, 2); - } - - public static AVec Fmul_Ve128(AVec LHS, AVec RHS, int SIdx, int Size) - { - return Fmul_Ve(LHS, RHS, SIdx, Size, 4); - } - - private static AVec Fmul_Ve(AVec LHS, AVec RHS, int SIdx, int Size, int Bytes) - { - AVec Res = new AVec(); - - int Elems = Bytes >> Size; - - if (Size == 0) - { - float R = RHS.ExtractSingle(SIdx); - - for (int Index = 0; Index < Elems; Index++) - { - float L = LHS.ExtractSingle(Index); - - Res = AVec.InsertSingle(Res, Index, L * R); - } - } - else - { - double R = RHS.ExtractDouble(SIdx); - - for (int Index = 0; Index < Elems; Index++) - { - double L = LHS.ExtractDouble(Index); - - Res = AVec.InsertDouble(Res, Index, L * R); - } - } - - return Res; - } - - public static AVec Ins_Gp(AVec Res, ulong Value, int Elem, int Size) - { - return InsertVec(Res, Elem, Size, Value); - } - - public static AVec Ins_V(AVec Res, AVec Value, int Src, int Dst, int Size) - { - return InsertVec(Res, Dst, Size, ExtractVec(Value, Src, Size));; - } - - public static AVec Orr_Vi64(AVec Res, ulong Imm, int Size) - { - return Orr_Vi(Res, Imm, Size, 8); - } - - public static AVec Orr_Vi128(AVec Res, ulong Imm, int Size) - { - return Orr_Vi(Res, Imm, Size, 16); - } - - private static AVec Orr_Vi(AVec Res, ulong Imm, int Size, int Bytes) - { - int Elems = Bytes >> Size; - - for (int Index = 0; Index < Elems; Index++) - { - ulong Value = ExtractVec(Res, Index, Size); - - Res = InsertVec(Res, Index, Size, Value | Imm); - } - - return Res; - } - - public static AVec Saddw(AVec LHS, AVec RHS, int Size) - { - return Saddw_(LHS, RHS, Size, false); - } - - public static AVec Saddw2(AVec LHS, AVec RHS, int Size) - { - return Saddw_(LHS, RHS, Size, true); - } - - private static AVec Saddw_(AVec LHS, AVec RHS, int Size, bool High) - { - AVec Res = new AVec(); - - int Elems = 8 >> Size; - int Part = High ? Elems : 0; - - for (int Index = 0; Index < Elems; Index++) - { - long L = ExtractSVec(LHS, Index, Size + 1); - long R = ExtractSVec(RHS, Index + Part, Size); - - Res = InsertSVec(Res, Index, Size + 1, L + R); - } - - return Res; - } - - public static AVec Sshll(AVec Vector, int Shift, int Size) - { - return Sshll_(Vector, Shift, Size, false); - } - - public static AVec Sshll2(AVec Vector, int Shift, int Size) - { - return Sshll_(Vector, Shift, Size, true); - } - - private static AVec Sshll_(AVec Vector, int Shift, int Size, bool High) - { - AVec Res = new AVec(); - - int Elems = 8 >> Size; - int Part = High ? Elems : 0; - - for (int Index = 0; Index < Elems; Index++) - { - long Value = ExtractSVec(Vector, Index + Part, Size); - - Res = InsertSVec(Res, Index, Size + 1, Value << Shift); - } - - return Res; - } - public static AVec Tbl1_V64(AVec Vector, AVec Tb0) { return Tbl(Vector, 8, Tb0); @@ -704,194 +249,27 @@ namespace ChocolArm64.Instruction for (int Index = 0; Index < Tb.Length; Index++) for (int Index2 = 0; Index2 < 16; Index2++) { - Table[Index * 16 + Index2] = (byte)ExtractVec(Tb[Index], Index2, 0); + Table[Index * 16 + Index2] = (byte)VectorExtractIntZx(Tb[Index], Index2, 0); } for (int Index = 0; Index < Bytes; Index++) { - byte TblIdx = (byte)ExtractVec(Vector, Index, 0); + byte TblIdx = (byte)VectorExtractIntZx(Vector, Index, 0); if (TblIdx < Table.Length) { - Res = InsertVec(Res, Index, 0, Table[TblIdx]); + Res = VectorInsertInt(Table[TblIdx], Res, Index, 0); } } return Res; } - public static AVec Uaddlv64(AVec Vector, int Size) - { - return Uaddlv(Vector, Size, 8); - } - - public static AVec Uaddlv128(AVec Vector, int Size) - { - return Uaddlv(Vector, Size, 16); - } - - private static AVec Uaddlv(AVec Vector, int Size, int Bytes) - { - int Elems = Bytes >> Size; - - ulong Sum = 0; - - for (int Index = 0; Index < Elems; Index++) - { - Sum += ExtractVec(Vector, Index, Size); - } - - return InsertVec(new AVec(), 0, 3, Sum); - } - - public static AVec Uaddw(AVec LHS, AVec RHS, int Size) - { - return Uaddw_(LHS, RHS, Size, false); - } - - public static AVec Uaddw2(AVec LHS, AVec RHS, int Size) - { - return Uaddw_(LHS, RHS, Size, true); - } - - private static AVec Uaddw_(AVec LHS, AVec RHS, int Size, bool High) - { - AVec Res = new AVec(); - - int Elems = 8 >> Size; - int Part = High ? Elems : 0; - - for (int Index = 0; Index < Elems; Index++) - { - ulong L = ExtractVec(LHS, Index, Size + 1); - ulong R = ExtractVec(RHS, Index + Part, Size); - - Res = InsertVec(Res, Index, Size + 1, L + R); - } - - return Res; - } - - public static AVec Ucvtf_V_F(AVec Vector) - { - return new AVec() - { - S0 = (uint)Vector.W0, - S1 = (uint)Vector.W1, - S2 = (uint)Vector.W2, - S3 = (uint)Vector.W3 - }; - } - - public static AVec Ucvtf_V_D(AVec Vector) - { - return new AVec() - { - D0 = (ulong)Vector.X0, - D1 = (ulong)Vector.X1 - }; - } - - public static AVec Ushll(AVec Vector, int Shift, int Size) - { - return Ushll_(Vector, Shift, Size, false); - } - - public static AVec Ushll2(AVec Vector, int Shift, int Size) - { - return Ushll_(Vector, Shift, Size, true); - } - - private static AVec Ushll_(AVec Vector, int Shift, int Size, bool High) - { - AVec Res = new AVec(); - - int Elems = 8 >> Size; - int Part = High ? Elems : 0; - - for (int Index = 0; Index < Elems; Index++) - { - ulong Value = ExtractVec(Vector, Index + Part, Size); - - Res = InsertVec(Res, Index, Size + 1, Value << Shift); - } - - return Res; - } - - public static AVec Uzp1_V64(AVec LHS, AVec RHS, int Size) - { - return Uzp(LHS, RHS, Size, 0, 8); - } - - public static AVec Uzp1_V128(AVec LHS, AVec RHS, int Size) - { - return Uzp(LHS, RHS, Size, 0, 16); - } - - public static AVec Uzp2_V64(AVec LHS, AVec RHS, int Size) - { - return Uzp(LHS, RHS, Size, 1, 8); - } - - public static AVec Uzp2_V128(AVec LHS, AVec RHS, int Size) - { - return Uzp(LHS, RHS, Size, 1, 16); - } - - private static AVec Uzp(AVec LHS, AVec RHS, int Size, int Part, int Bytes) - { - AVec Res = new AVec(); - - int Elems = Bytes >> Size; - int Half = Elems >> 1; - - for (int Index = 0; Index < Elems; Index++) - { - int Elem = (Index & (Half - 1)) << 1; - - ulong Value = Index < Half - ? ExtractVec(LHS, Elem + Part, Size) - : ExtractVec(RHS, Elem + Part, Size); - - Res = InsertVec(Res, Index, Size, Value); - } - - return Res; - } - - public static AVec Xtn(AVec Vector, int Size) - { - return Xtn_(Vector, Size, false); - } - - public static AVec Xtn2(AVec Vector, int Size) - { - return Xtn_(Vector, Size, true); - } - - private static AVec Xtn_(AVec Vector, int Size, bool High) - { - AVec Res = new AVec(); - - int Elems = 8 >> Size; - int Part = High ? Elems : 0; - - for (int Index = 0; Index < Elems; Index++) - { - ulong Value = ExtractVec(Vector, Index, Size + 1); - - Res = InsertVec(Res, Index + Part, Size, Value); - } - - return Res; - } - - public static ulong ExtractVec(AVec Vector, int Index, int Size) + public static ulong VectorExtractIntZx(AVec Vector, int Index, int Size) { switch (Size) { - case 0: return Vector.ExtractByte(Index); + case 0: return Vector.ExtractByte (Index); case 1: return Vector.ExtractUInt16(Index); case 2: return Vector.ExtractUInt32(Index); case 3: return Vector.ExtractUInt64(Index); @@ -900,14 +278,14 @@ namespace ChocolArm64.Instruction throw new ArgumentOutOfRangeException(nameof(Size)); } - public static long ExtractSVec(AVec Vector, int Index, int Size) + public static long VectorExtractIntSx(AVec Vector, int Index, int Size) { switch (Size) { - case 0: return (sbyte)Vector.ExtractByte(Index); + case 0: return (sbyte)Vector.ExtractByte (Index); case 1: return (short)Vector.ExtractUInt16(Index); - case 2: return (int)Vector.ExtractUInt32(Index); - case 3: return (long)Vector.ExtractUInt64(Index); + case 2: return (int)Vector.ExtractUInt32(Index); + case 3: return (long)Vector.ExtractUInt64(Index); } throw new ArgumentOutOfRangeException(nameof(Size)); @@ -945,31 +323,5 @@ namespace ChocolArm64.Instruction throw new ArgumentOutOfRangeException(nameof(Size)); } - - public static AVec InsertVec(AVec Vector, int Index, int Size, ulong Value) - { - switch (Size) - { - case 0: return AVec.InsertByte(Vector, Index, (byte)Value); - case 1: return AVec.InsertUInt16(Vector, Index, (ushort)Value); - case 2: return AVec.InsertUInt32(Vector, Index, (uint)Value); - case 3: return AVec.InsertUInt64(Vector, Index, (ulong)Value); - } - - throw new ArgumentOutOfRangeException(nameof(Size)); - } - - public static AVec InsertSVec(AVec Vector, int Index, int Size, long Value) - { - switch (Size) - { - case 0: return AVec.InsertByte(Vector, Index, (byte)Value); - case 1: return AVec.InsertUInt16(Vector, Index, (ushort)Value); - case 2: return AVec.InsertUInt32(Vector, Index, (uint)Value); - case 3: return AVec.InsertUInt64(Vector, Index, (ulong)Value); - } - - throw new ArgumentOutOfRangeException(nameof(Size)); - } } } \ No newline at end of file diff --git a/Ryujinx/Cpu/Memory/AMemory.cs b/Ryujinx/Cpu/Memory/AMemory.cs index ead99f3f6..f2abffbf4 100644 --- a/Ryujinx/Cpu/Memory/AMemory.cs +++ b/Ryujinx/Cpu/Memory/AMemory.cs @@ -2,12 +2,13 @@ using ChocolArm64.Exceptions; using ChocolArm64.State; using System; using System.Collections.Generic; +using System.Runtime.CompilerServices; namespace ChocolArm64.Memory { public unsafe class AMemory { - private const long ErgMask = (4 << ARegisters.ErgSizeLog2) - 1; + private const long ErgMask = (4 << AThreadState.ErgSizeLog2) - 1; public AMemoryMgr Manager { get; private set; } @@ -64,13 +65,13 @@ namespace ChocolArm64.Memory } } - public void SetExclusive(ARegisters Registers, long Position) + public void SetExclusive(AThreadState ThreadState, long Position) { Position &= ~ErgMask; lock (Monitors) { - if (Monitors.TryGetValue(Registers.ThreadId, out ExMonitor Monitor)) + if (Monitors.TryGetValue(ThreadState.ThreadId, out ExMonitor Monitor)) { ExAddrs.Remove(Monitor.Position); } @@ -79,20 +80,20 @@ namespace ChocolArm64.Memory Monitor = new ExMonitor(Position, ExState); - if (!Monitors.TryAdd(Registers.ThreadId, Monitor)) + if (!Monitors.TryAdd(ThreadState.ThreadId, Monitor)) { - Monitors[Registers.ThreadId] = Monitor; + Monitors[ThreadState.ThreadId] = Monitor; } } } - public bool TestExclusive(ARegisters Registers, long Position) + public bool TestExclusive(AThreadState ThreadState, long Position) { Position &= ~ErgMask; lock (Monitors) { - if (!Monitors.TryGetValue(Registers.ThreadId, out ExMonitor Monitor)) + if (!Monitors.TryGetValue(ThreadState.ThreadId, out ExMonitor Monitor)) { return false; } @@ -101,11 +102,11 @@ namespace ChocolArm64.Memory } } - public void ClearExclusive(ARegisters Registers) + public void ClearExclusive(AThreadState ThreadState) { lock (Monitors) { - if (Monitors.TryGetValue(Registers.ThreadId, out ExMonitor Monitor)) + if (Monitors.TryGetValue(ThreadState.ThreadId, out ExMonitor Monitor)) { Monitor.Reset(); ExAddrs.Remove(Monitor.Position); @@ -138,6 +139,7 @@ namespace ChocolArm64.Memory public int ReadInt32(long Position) => (int)ReadUInt32(Position); public long ReadInt64(long Position) => (long)ReadUInt64(Position); + [MethodImpl(MethodImplOptions.AggressiveInlining)] public byte ReadByte(long Position) { #if DEBUG @@ -147,6 +149,7 @@ namespace ChocolArm64.Memory return *((byte*)(RamPtr + (uint)Position)); } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public ushort ReadUInt16(long Position) { #if DEBUG @@ -156,6 +159,7 @@ namespace ChocolArm64.Memory return *((ushort*)(RamPtr + (uint)Position)); } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public uint ReadUInt32(long Position) { #if DEBUG @@ -165,6 +169,7 @@ namespace ChocolArm64.Memory return *((uint*)(RamPtr + (uint)Position)); } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public ulong ReadUInt64(long Position) { #if DEBUG @@ -174,6 +179,47 @@ namespace ChocolArm64.Memory return *((ulong*)(RamPtr + (uint)Position)); } + [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 @@ -192,6 +238,7 @@ namespace ChocolArm64.Memory 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) { #if DEBUG @@ -201,6 +248,7 @@ namespace ChocolArm64.Memory *((byte*)(RamPtr + (uint)Position)) = Value; } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public void WriteUInt16(long Position, ushort Value) { #if DEBUG @@ -210,6 +258,7 @@ namespace ChocolArm64.Memory *((ushort*)(RamPtr + (uint)Position)) = Value; } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public void WriteUInt32(long Position, uint Value) { #if DEBUG @@ -219,6 +268,7 @@ namespace ChocolArm64.Memory *((uint*)(RamPtr + (uint)Position)) = Value; } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public void WriteUInt64(long Position, ulong Value) { #if DEBUG @@ -228,6 +278,47 @@ namespace ChocolArm64.Memory *((ulong*)(RamPtr + (uint)Position)) = Value; } + [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 diff --git a/Ryujinx/Cpu/Memory/AMemoryHelper.cs b/Ryujinx/Cpu/Memory/AMemoryHelper.cs index fb4316c54..219aeebf9 100644 --- a/Ryujinx/Cpu/Memory/AMemoryHelper.cs +++ b/Ryujinx/Cpu/Memory/AMemoryHelper.cs @@ -1,6 +1,5 @@ using System.IO; using System.Text; -using System.Threading; namespace ChocolArm64.Memory { @@ -21,32 +20,6 @@ namespace ChocolArm64.Memory } } - public static int ReadInt32Exclusive(AMemory Memory, long Position) - { - while (!Memory.AcquireAddress(Position)) - { - Thread.Yield(); - } - - int Value = Memory.ReadInt32(Position); - - Memory.ReleaseAddress(Position); - - return Value; - } - - public static void WriteInt32Exclusive(AMemory Memory, long Position, int Value) - { - while (!Memory.AcquireAddress(Position)) - { - Thread.Yield(); - } - - Memory.WriteInt32(Position, Value); - - Memory.ReleaseAddress(Position); - } - public static byte[] ReadBytes(AMemory Memory, long Position, int Size) { byte[] Data = new byte[Size]; diff --git a/Ryujinx/Cpu/State/ARegister.cs b/Ryujinx/Cpu/State/ARegister.cs index 5146bc313..5861db8c6 100644 --- a/Ryujinx/Cpu/State/ARegister.cs +++ b/Ryujinx/Cpu/State/ARegister.cs @@ -43,10 +43,10 @@ namespace ChocolArm64.State { switch ((APState)Index) { - case APState.VBit: return GetField(nameof(ARegisters.Overflow)); - case APState.CBit: return GetField(nameof(ARegisters.Carry)); - case APState.ZBit: return GetField(nameof(ARegisters.Zero)); - case APState.NBit: return GetField(nameof(ARegisters.Negative)); + case APState.VBit: return GetField(nameof(AThreadState.Overflow)); + case APState.CBit: return GetField(nameof(AThreadState.Carry)); + case APState.ZBit: return GetField(nameof(AThreadState.Zero)); + case APState.NBit: return GetField(nameof(AThreadState.Negative)); } throw new InvalidOperationException(); @@ -56,38 +56,38 @@ namespace ChocolArm64.State { switch (Index) { - case 0: return GetField(nameof(ARegisters.X0)); - case 1: return GetField(nameof(ARegisters.X1)); - case 2: return GetField(nameof(ARegisters.X2)); - case 3: return GetField(nameof(ARegisters.X3)); - case 4: return GetField(nameof(ARegisters.X4)); - case 5: return GetField(nameof(ARegisters.X5)); - case 6: return GetField(nameof(ARegisters.X6)); - case 7: return GetField(nameof(ARegisters.X7)); - case 8: return GetField(nameof(ARegisters.X8)); - case 9: return GetField(nameof(ARegisters.X9)); - case 10: return GetField(nameof(ARegisters.X10)); - case 11: return GetField(nameof(ARegisters.X11)); - case 12: return GetField(nameof(ARegisters.X12)); - case 13: return GetField(nameof(ARegisters.X13)); - case 14: return GetField(nameof(ARegisters.X14)); - case 15: return GetField(nameof(ARegisters.X15)); - case 16: return GetField(nameof(ARegisters.X16)); - case 17: return GetField(nameof(ARegisters.X17)); - case 18: return GetField(nameof(ARegisters.X18)); - case 19: return GetField(nameof(ARegisters.X19)); - case 20: return GetField(nameof(ARegisters.X20)); - case 21: return GetField(nameof(ARegisters.X21)); - case 22: return GetField(nameof(ARegisters.X22)); - case 23: return GetField(nameof(ARegisters.X23)); - case 24: return GetField(nameof(ARegisters.X24)); - case 25: return GetField(nameof(ARegisters.X25)); - case 26: return GetField(nameof(ARegisters.X26)); - case 27: return GetField(nameof(ARegisters.X27)); - case 28: return GetField(nameof(ARegisters.X28)); - case 29: return GetField(nameof(ARegisters.X29)); - case 30: return GetField(nameof(ARegisters.X30)); - case 31: return GetField(nameof(ARegisters.X31)); + case 0: return GetField(nameof(AThreadState.X0)); + case 1: return GetField(nameof(AThreadState.X1)); + case 2: return GetField(nameof(AThreadState.X2)); + case 3: return GetField(nameof(AThreadState.X3)); + case 4: return GetField(nameof(AThreadState.X4)); + case 5: return GetField(nameof(AThreadState.X5)); + case 6: return GetField(nameof(AThreadState.X6)); + case 7: return GetField(nameof(AThreadState.X7)); + case 8: return GetField(nameof(AThreadState.X8)); + case 9: return GetField(nameof(AThreadState.X9)); + case 10: return GetField(nameof(AThreadState.X10)); + case 11: return GetField(nameof(AThreadState.X11)); + case 12: return GetField(nameof(AThreadState.X12)); + case 13: return GetField(nameof(AThreadState.X13)); + case 14: return GetField(nameof(AThreadState.X14)); + case 15: return GetField(nameof(AThreadState.X15)); + case 16: return GetField(nameof(AThreadState.X16)); + case 17: return GetField(nameof(AThreadState.X17)); + case 18: return GetField(nameof(AThreadState.X18)); + case 19: return GetField(nameof(AThreadState.X19)); + case 20: return GetField(nameof(AThreadState.X20)); + case 21: return GetField(nameof(AThreadState.X21)); + case 22: return GetField(nameof(AThreadState.X22)); + case 23: return GetField(nameof(AThreadState.X23)); + case 24: return GetField(nameof(AThreadState.X24)); + case 25: return GetField(nameof(AThreadState.X25)); + case 26: return GetField(nameof(AThreadState.X26)); + case 27: return GetField(nameof(AThreadState.X27)); + case 28: return GetField(nameof(AThreadState.X28)); + case 29: return GetField(nameof(AThreadState.X29)); + case 30: return GetField(nameof(AThreadState.X30)); + case 31: return GetField(nameof(AThreadState.X31)); } throw new InvalidOperationException(); @@ -97,38 +97,38 @@ namespace ChocolArm64.State { switch (Index) { - case 0: return GetField(nameof(ARegisters.V0)); - case 1: return GetField(nameof(ARegisters.V1)); - case 2: return GetField(nameof(ARegisters.V2)); - case 3: return GetField(nameof(ARegisters.V3)); - case 4: return GetField(nameof(ARegisters.V4)); - case 5: return GetField(nameof(ARegisters.V5)); - case 6: return GetField(nameof(ARegisters.V6)); - case 7: return GetField(nameof(ARegisters.V7)); - case 8: return GetField(nameof(ARegisters.V8)); - case 9: return GetField(nameof(ARegisters.V9)); - case 10: return GetField(nameof(ARegisters.V10)); - case 11: return GetField(nameof(ARegisters.V11)); - case 12: return GetField(nameof(ARegisters.V12)); - case 13: return GetField(nameof(ARegisters.V13)); - case 14: return GetField(nameof(ARegisters.V14)); - case 15: return GetField(nameof(ARegisters.V15)); - case 16: return GetField(nameof(ARegisters.V16)); - case 17: return GetField(nameof(ARegisters.V17)); - case 18: return GetField(nameof(ARegisters.V18)); - case 19: return GetField(nameof(ARegisters.V19)); - case 20: return GetField(nameof(ARegisters.V20)); - case 21: return GetField(nameof(ARegisters.V21)); - case 22: return GetField(nameof(ARegisters.V22)); - case 23: return GetField(nameof(ARegisters.V23)); - case 24: return GetField(nameof(ARegisters.V24)); - case 25: return GetField(nameof(ARegisters.V25)); - case 26: return GetField(nameof(ARegisters.V26)); - case 27: return GetField(nameof(ARegisters.V27)); - case 28: return GetField(nameof(ARegisters.V28)); - case 29: return GetField(nameof(ARegisters.V29)); - case 30: return GetField(nameof(ARegisters.V30)); - case 31: return GetField(nameof(ARegisters.V31)); + case 0: return GetField(nameof(AThreadState.V0)); + case 1: return GetField(nameof(AThreadState.V1)); + case 2: return GetField(nameof(AThreadState.V2)); + case 3: return GetField(nameof(AThreadState.V3)); + case 4: return GetField(nameof(AThreadState.V4)); + case 5: return GetField(nameof(AThreadState.V5)); + case 6: return GetField(nameof(AThreadState.V6)); + case 7: return GetField(nameof(AThreadState.V7)); + case 8: return GetField(nameof(AThreadState.V8)); + case 9: return GetField(nameof(AThreadState.V9)); + case 10: return GetField(nameof(AThreadState.V10)); + case 11: return GetField(nameof(AThreadState.V11)); + case 12: return GetField(nameof(AThreadState.V12)); + case 13: return GetField(nameof(AThreadState.V13)); + case 14: return GetField(nameof(AThreadState.V14)); + case 15: return GetField(nameof(AThreadState.V15)); + case 16: return GetField(nameof(AThreadState.V16)); + case 17: return GetField(nameof(AThreadState.V17)); + case 18: return GetField(nameof(AThreadState.V18)); + case 19: return GetField(nameof(AThreadState.V19)); + case 20: return GetField(nameof(AThreadState.V20)); + case 21: return GetField(nameof(AThreadState.V21)); + case 22: return GetField(nameof(AThreadState.V22)); + case 23: return GetField(nameof(AThreadState.V23)); + case 24: return GetField(nameof(AThreadState.V24)); + case 25: return GetField(nameof(AThreadState.V25)); + case 26: return GetField(nameof(AThreadState.V26)); + case 27: return GetField(nameof(AThreadState.V27)); + case 28: return GetField(nameof(AThreadState.V28)); + case 29: return GetField(nameof(AThreadState.V29)); + case 30: return GetField(nameof(AThreadState.V30)); + case 31: return GetField(nameof(AThreadState.V31)); } throw new InvalidOperationException(); @@ -136,7 +136,7 @@ namespace ChocolArm64.State private FieldInfo GetField(string Name) { - return typeof(ARegisters).GetField(Name); + return typeof(AThreadState).GetField(Name); } } } \ No newline at end of file diff --git a/Ryujinx/Cpu/State/ARegisters.cs b/Ryujinx/Cpu/State/AThreadState.cs similarity index 98% rename from Ryujinx/Cpu/State/ARegisters.cs rename to Ryujinx/Cpu/State/AThreadState.cs index bb93699d5..cdab4034e 100644 --- a/Ryujinx/Cpu/State/ARegisters.cs +++ b/Ryujinx/Cpu/State/AThreadState.cs @@ -2,7 +2,7 @@ using System; namespace ChocolArm64.State { - public class ARegisters + public class AThreadState { internal const int LRIndex = 30; internal const int ZRIndex = 31; diff --git a/Ryujinx/Cpu/Translation/AILBlock.cs b/Ryujinx/Cpu/Translation/AILBlock.cs index 2746e4288..bed195aaf 100644 --- a/Ryujinx/Cpu/Translation/AILBlock.cs +++ b/Ryujinx/Cpu/Translation/AILBlock.cs @@ -26,7 +26,7 @@ namespace ChocolArm64.Translation { if (ILEmitter is AILOpCodeLoad Ld && AILEmitter.IsRegIndex(Ld.Index)) { - switch (Ld.IoType & AIoType.Mask) + switch (Ld.IoType) { case AIoType.Flag: IntInputs |= ((1L << Ld.Index) << 32) & ~IntOutputs; break; case AIoType.Int: IntInputs |= (1L << Ld.Index) & ~IntOutputs; break; @@ -37,7 +37,7 @@ namespace ChocolArm64.Translation { if (AILEmitter.IsRegIndex(St.Index)) { - switch (St.IoType & AIoType.Mask) + switch (St.IoType) { case AIoType.Flag: IntOutputs |= (1L << St.Index) << 32; break; case AIoType.Int: IntOutputs |= 1L << St.Index; break; diff --git a/Ryujinx/Cpu/Translation/AILConv.cs b/Ryujinx/Cpu/Translation/AILConv.cs deleted file mode 100644 index 8969dc4e9..000000000 --- a/Ryujinx/Cpu/Translation/AILConv.cs +++ /dev/null @@ -1,113 +0,0 @@ -using ChocolArm64.State; -using System; -using System.Reflection; -using System.Reflection.Emit; - -namespace ChocolArm64.Translation -{ - static class AILConv - { - public static void EmitConv(AILEmitter Context, Type SrcType, Type TgtType) - { - if (SrcType == TgtType) - { - //If both types are equal we don't need to cast anything. - return; - } - - if (SrcType.IsPrimitive) - { - if (TgtType == typeof(byte)) - { - Context.Generator.Emit(OpCodes.Conv_U1); - } - else if (TgtType == typeof(ushort)) - { - Context.Generator.Emit(OpCodes.Conv_U2); - } - else if (TgtType == typeof(uint)) - { - Context.Generator.Emit(OpCodes.Conv_U4); - } - else if (TgtType == typeof(ulong)) - { - Context.Generator.Emit(OpCodes.Conv_U8); - } - else if (TgtType == typeof(float)) - { - Context.Generator.Emit(OpCodes.Conv_R4); - } - else if (TgtType == typeof(double)) - { - Context.Generator.Emit(OpCodes.Conv_R8); - } - else if (TgtType == typeof(AVec)) - { - EmitMakeVec(Context, SrcType); - } - else - { - throw new ArgumentException(nameof(TgtType)); - } - } - else if (SrcType == typeof(AVec)) - { - if (TgtType == typeof(float)) - { - EmitScalarLdfld(Context, nameof(AVec.S0)); - } - else if (TgtType == typeof(double)) - { - EmitScalarLdfld(Context, nameof(AVec.D0)); - } - else if (TgtType == typeof(byte)) - { - EmitScalarLdfld(Context, nameof(AVec.B0)); - } - else if (TgtType == typeof(ushort)) - { - EmitScalarLdfld(Context, nameof(AVec.H0)); - } - else if (TgtType == typeof(uint)) - { - EmitScalarLdfld(Context, nameof(AVec.W0)); - } - else if (TgtType == typeof(ulong)) - { - EmitScalarLdfld(Context, nameof(AVec.X0)); - } - else - { - throw new ArgumentException(nameof(TgtType)); - } - } - else - { - throw new ArgumentException(nameof(SrcType)); - } - } - - private static void EmitScalarLdfld(AILEmitter Context,string FldName) - { - Context.Generator.Emit(OpCodes.Ldfld, typeof(AVec).GetField(FldName)); - } - - private static void EmitMakeVec(AILEmitter Context, Type SrcType) - { - string MthdName = nameof(MakeScalar); - - Type[] MthdTypes = new Type[] { SrcType }; - - MethodInfo MthdInfo = typeof(AILConv).GetMethod(MthdName, MthdTypes); - - Context.Generator.Emit(OpCodes.Call, MthdInfo); - } - - public static AVec MakeScalar(byte Value) => new AVec { B0 = Value }; - public static AVec MakeScalar(ushort Value) => new AVec { H0 = Value }; - public static AVec MakeScalar(uint Value) => new AVec { W0 = Value }; - public static AVec MakeScalar(float Value) => new AVec { S0 = Value }; - public static AVec MakeScalar(ulong Value) => new AVec { X0 = Value }; - public static AVec MakeScalar(double Value) => new AVec { D0 = Value }; - } -} \ No newline at end of file diff --git a/Ryujinx/Cpu/Translation/AILEmitter.cs b/Ryujinx/Cpu/Translation/AILEmitter.cs index 0619149c3..8f6e1210f 100644 --- a/Ryujinx/Cpu/Translation/AILEmitter.cs +++ b/Ryujinx/Cpu/Translation/AILEmitter.cs @@ -86,9 +86,6 @@ namespace ChocolArm64.Translation ARegister Reg = Subroutine.Params[Index]; Generator.EmitLdarg(Index + ParamsStart); - - AILConv.EmitConv(this, GetFieldType(Reg.Type), GetLocalType(Reg)); - Generator.EmitStloc(GetLocalIndex(Reg)); } } diff --git a/Ryujinx/Cpu/Translation/AILEmitterCtx.cs b/Ryujinx/Cpu/Translation/AILEmitterCtx.cs index 410308ff8..cf6445401 100644 --- a/Ryujinx/Cpu/Translation/AILEmitterCtx.cs +++ b/Ryujinx/Cpu/Translation/AILEmitterCtx.cs @@ -37,6 +37,7 @@ namespace ChocolArm64.Translation private const int Tmp2Index = -2; private const int Tmp3Index = -3; private const int Tmp4Index = -4; + private const int Tmp5Index = -5; public AILEmitterCtx(ATranslator Translator, ABlock[] Graph, ABlock Root) { @@ -91,7 +92,7 @@ namespace ChocolArm64.Translation } public bool TryOptEmitSubroutineCall() - { + { if (!Translator.TryGetCachedSub(CurrOp, out ATranslatedSub Sub)) { return false; @@ -149,8 +150,8 @@ namespace ChocolArm64.Translation if (LastCmpOp != null && LastFlagOp == LastCmpOp && BranchOps.ContainsKey(Cond)) { - Ldloc(Tmp3Index, AIoType.Int, GetIntType(LastCmpOp)); - Ldloc(Tmp4Index, AIoType.Int, GetIntType(LastCmpOp)); + Ldloc(Tmp3Index, AIoType.Int, LastCmpOp.RegisterSize); + Ldloc(Tmp4Index, AIoType.Int, LastCmpOp.RegisterSize); if (LastCmpOp.Emitter == AInstEmit.Adds) { @@ -308,7 +309,7 @@ namespace ChocolArm64.Translation public void EmitLdintzr(int Index) { - if (Index != ARegisters.ZRIndex) + if (Index != AThreadState.ZRIndex) { EmitLdint(Index); } @@ -320,7 +321,7 @@ namespace ChocolArm64.Translation public void EmitStintzr(int Index) { - if (Index != ARegisters.ZRIndex) + if (Index != AThreadState.ZRIndex) { EmitStint(Index); } @@ -343,18 +344,15 @@ namespace ChocolArm64.Translation public void EmitLdtmp() => EmitLdint(Tmp1Index); public void EmitSttmp() => EmitStint(Tmp1Index); + public void EmitLdvectmp() => EmitLdvec(Tmp5Index); + public void EmitStvectmp() => EmitStvec(Tmp5Index); + public void EmitLdint(int Index) => Ldloc(Index, AIoType.Int); public void EmitStint(int Index) => Stloc(Index, AIoType.Int); public void EmitLdvec(int Index) => Ldloc(Index, AIoType.Vector); public void EmitStvec(int Index) => Stloc(Index, AIoType.Vector); - public void EmitLdvecsi(int Index) => Ldloc(Index, AIoType.VectorI); - public void EmitStvecsi(int Index) => Stloc(Index, AIoType.VectorI); - - public void EmitLdvecsf(int Index) => Ldloc(Index, AIoType.VectorF); - public void EmitStvecsf(int Index) => Stloc(Index, AIoType.VectorF); - public void EmitLdflg(int Index) => Ldloc(Index, AIoType.Flag); public void EmitStflg(int Index) { @@ -365,106 +363,17 @@ namespace ChocolArm64.Translation private void Ldloc(int Index, AIoType IoType) { - ILBlock.Add(new AILOpCodeLoad(Index, IoType, GetOperType(IoType))); + ILBlock.Add(new AILOpCodeLoad(Index, IoType, CurrOp.RegisterSize)); } - private void Ldloc(int Index, AIoType IoType, Type Type) + private void Ldloc(int Index, AIoType IoType, ARegisterSize RegisterSize) { - ILBlock.Add(new AILOpCodeLoad(Index, IoType, Type)); + ILBlock.Add(new AILOpCodeLoad(Index, IoType, RegisterSize)); } private void Stloc(int Index, AIoType IoType) { - ILBlock.Add(new AILOpCodeStore(Index, IoType, GetOutOperType(IoType))); - } - - private Type GetOutOperType(AIoType IoType) - { - //This instruction is used to convert between floating point - //types, so the input and output types are different. - if (CurrOp.Emitter == AInstEmit.Fcvt_S) - { - return GetFloatType(((AOpCodeSimd)CurrOp).Opc); - } - else - { - return GetOperType(IoType); - } - } - - private Type GetOperType(AIoType IoType) - { - switch (IoType & AIoType.Mask) - { - case AIoType.Flag: return typeof(bool); - case AIoType.Int: return GetIntType(CurrOp); - case AIoType.Vector: return GetVecType(CurrOp, IoType); - } - - throw new ArgumentException(nameof(IoType)); - } - - private Type GetIntType(AOpCode OpCode) - { - //Always default to 64-bits. - return OpCode.RegisterSize == ARegisterSize.Int32 - ? typeof(uint) - : typeof(ulong); - } - - private Type GetVecType(AOpCode OpCode, AIoType IoType) - { - if (!(OpCode is IAOpCodeSimd Op)) - { - return typeof(AVec); - } - - int Size = Op.Size; - - if (Op.Emitter == AInstEmit.Fmov_Ftoi || - Op.Emitter == AInstEmit.Fmov_Itof) - { - Size |= 2; - } - - if (Op is AOpCodeMem || Op is IAOpCodeLit) - { - return Size < 4 ? typeof(ulong) : typeof(AVec); - } - else if (IoType == AIoType.VectorI) - { - return GetIntType(Size); - } - else if (IoType == AIoType.VectorF) - { - return GetFloatType(Size); - } - - return typeof(AVec); - } - - private static Type GetIntType(int Size) - { - switch (Size) - { - case 0: return typeof(byte); - case 1: return typeof(ushort); - case 2: return typeof(uint); - case 3: return typeof(ulong); - } - - throw new ArgumentOutOfRangeException(nameof(Size)); - } - - private static Type GetFloatType(int Size) - { - switch (Size) - { - case 0: return typeof(float); - case 1: return typeof(double); - } - - throw new ArgumentOutOfRangeException(nameof(Size)); + ILBlock.Add(new AILOpCodeStore(Index, IoType, CurrOp.RegisterSize)); } public void EmitCallPropGet(Type ObjType, string PropName) diff --git a/Ryujinx/Cpu/Translation/AILOpCodeLoad.cs b/Ryujinx/Cpu/Translation/AILOpCodeLoad.cs index 2169cc777..7cb431e2d 100644 --- a/Ryujinx/Cpu/Translation/AILOpCodeLoad.cs +++ b/Ryujinx/Cpu/Translation/AILOpCodeLoad.cs @@ -1,5 +1,4 @@ using ChocolArm64.State; -using System; using System.Reflection.Emit; namespace ChocolArm64.Translation @@ -10,43 +9,40 @@ namespace ChocolArm64.Translation public AIoType IoType { get; private set; } - public Type OperType { get; private set; } + public ARegisterSize RegisterSize { get; private set; } - public AILOpCodeLoad(int Index, AIoType IoType) : this(Index, IoType, null) { } + public AILOpCodeLoad(int Index, AIoType IoType) : this(Index, IoType, ARegisterSize.Int64) { } - public AILOpCodeLoad(int Index, AIoType IoType, Type OperType) + public AILOpCodeLoad(int Index, AIoType IoType, ARegisterSize RegisterSize) { - this.IoType = IoType; - this.Index = Index; - this.OperType = OperType; + this.IoType = IoType; + this.Index = Index; + this.RegisterSize = RegisterSize; } public void Emit(AILEmitter Context) { - switch (IoType & AIoType.Mask) + switch (IoType) { - case AIoType.Arg: EmitLdarg(Context, Index); break; - case AIoType.Fields: EmitLdfld(Context, Index); break; + case AIoType.Arg: Context.Generator.EmitLdarg(Index); break; + + case AIoType.Fields: + { + long IntInputs = Context.LocalAlloc.GetIntInputs(Context.GetILBlock(Index)); + long VecInputs = Context.LocalAlloc.GetVecInputs(Context.GetILBlock(Index)); + + LoadLocals(Context, IntInputs, ARegisterType.Int); + LoadLocals(Context, VecInputs, ARegisterType.Vector); + + break; + } + case AIoType.Flag: EmitLdloc(Context, Index, ARegisterType.Flag); break; case AIoType.Int: EmitLdloc(Context, Index, ARegisterType.Int); break; case AIoType.Vector: EmitLdloc(Context, Index, ARegisterType.Vector); break; } } - private void EmitLdarg(AILEmitter Context, int Index) - { - Context.Generator.EmitLdarg(Index); - } - - private void EmitLdfld(AILEmitter Context, int Index) - { - long IntInputs = Context.LocalAlloc.GetIntInputs(Context.GetILBlock(Index)); - long VecInputs = Context.LocalAlloc.GetVecInputs(Context.GetILBlock(Index)); - - LoadLocals(Context, IntInputs, ARegisterType.Int); - LoadLocals(Context, VecInputs, ARegisterType.Vector); - } - private void LoadLocals(AILEmitter Context, long Inputs, ARegisterType BaseType) { for (int Bit = 0; Bit < 64; Bit++) @@ -57,26 +53,25 @@ namespace ChocolArm64.Translation { ARegister Reg = AILEmitter.GetRegFromBit(Bit, BaseType); - Context.Generator.EmitLdarg(ATranslatedSub.RegistersArgIdx); + Context.Generator.EmitLdarg(ATranslatedSub.StateArgIdx); Context.Generator.Emit(OpCodes.Ldfld, Reg.GetField()); - AILConv.EmitConv( - Context, - Context.GetFieldType(Reg.Type), - Context.GetLocalType(Reg)); - Context.Generator.EmitStloc(Context.GetLocalIndex(Reg)); } } } - private void EmitLdloc(AILEmitter Context, int Index, ARegisterType Type) + private void EmitLdloc(AILEmitter Context, int Index, ARegisterType RegisterType) { - ARegister Reg = new ARegister(Index, Type); + ARegister Reg = new ARegister(Index, RegisterType); Context.Generator.EmitLdloc(Context.GetLocalIndex(Reg)); - AILConv.EmitConv(Context, Context.GetLocalType(Reg), OperType); + if (RegisterType == ARegisterType.Int && + RegisterSize == ARegisterSize.Int32) + { + Context.Generator.Emit(OpCodes.Conv_U4); + } } } } \ No newline at end of file diff --git a/Ryujinx/Cpu/Translation/AILOpCodeStore.cs b/Ryujinx/Cpu/Translation/AILOpCodeStore.cs index 012e24aec..34b26fe28 100644 --- a/Ryujinx/Cpu/Translation/AILOpCodeStore.cs +++ b/Ryujinx/Cpu/Translation/AILOpCodeStore.cs @@ -1,52 +1,48 @@ using ChocolArm64.State; -using System; using System.Reflection.Emit; namespace ChocolArm64.Translation { struct AILOpCodeStore : IAILEmit { - public AIoType IoType { get; private set; } - - public Type OperType { get; private set; } - public int Index { get; private set; } - public AILOpCodeStore(int Index, AIoType IoType) : this(Index, IoType, null) { } + public AIoType IoType { get; private set; } - public AILOpCodeStore(int Index, AIoType IoType, Type OperType) + public ARegisterSize RegisterSize { get; private set; } + + public AILOpCodeStore(int Index, AIoType IoType) : this(Index, IoType, ARegisterSize.Int64) { } + + public AILOpCodeStore(int Index, AIoType IoType, ARegisterSize RegisterSize) { - this.IoType = IoType; - this.Index = Index; - this.OperType = OperType; + this.IoType = IoType; + this.Index = Index; + this.RegisterSize = RegisterSize; } public void Emit(AILEmitter Context) { - switch (IoType & AIoType.Mask) + switch (IoType) { - case AIoType.Arg: EmitStarg(Context, Index); break; - case AIoType.Fields: EmitStfld(Context, Index); break; + case AIoType.Arg: Context.Generator.EmitStarg(Index); break; + + case AIoType.Fields: + { + long IntOutputs = Context.LocalAlloc.GetIntOutputs(Context.GetILBlock(Index)); + long VecOutputs = Context.LocalAlloc.GetVecOutputs(Context.GetILBlock(Index)); + + StoreLocals(Context, IntOutputs, ARegisterType.Int); + StoreLocals(Context, VecOutputs, ARegisterType.Vector); + + break; + } + case AIoType.Flag: EmitStloc(Context, Index, ARegisterType.Flag); break; case AIoType.Int: EmitStloc(Context, Index, ARegisterType.Int); break; case AIoType.Vector: EmitStloc(Context, Index, ARegisterType.Vector); break; } } - private void EmitStarg(AILEmitter Context, int Index) - { - Context.Generator.EmitStarg(Index); - } - - private void EmitStfld(AILEmitter Context, int Index) - { - long IntOutputs = Context.LocalAlloc.GetIntOutputs(Context.GetILBlock(Index)); - long VecOutputs = Context.LocalAlloc.GetVecOutputs(Context.GetILBlock(Index)); - - StoreLocals(Context, IntOutputs, ARegisterType.Int); - StoreLocals(Context, VecOutputs, ARegisterType.Vector); - } - private void StoreLocals(AILEmitter Context, long Outputs, ARegisterType BaseType) { for (int Bit = 0; Bit < 64; Bit++) @@ -57,24 +53,23 @@ namespace ChocolArm64.Translation { ARegister Reg = AILEmitter.GetRegFromBit(Bit, BaseType); - Context.Generator.EmitLdarg(ATranslatedSub.RegistersArgIdx); + Context.Generator.EmitLdarg(ATranslatedSub.StateArgIdx); Context.Generator.EmitLdloc(Context.GetLocalIndex(Reg)); - AILConv.EmitConv( - Context, - Context.GetLocalType(Reg), - Context.GetFieldType(Reg.Type)); - Context.Generator.Emit(OpCodes.Stfld, Reg.GetField()); } } } - private void EmitStloc(AILEmitter Context, int Index, ARegisterType Type) + private void EmitStloc(AILEmitter Context, int Index, ARegisterType RegisterType) { - ARegister Reg = new ARegister(Index, Type); + ARegister Reg = new ARegister(Index, RegisterType); - AILConv.EmitConv(Context, OperType, Context.GetLocalType(Reg)); + if (RegisterType == ARegisterType.Int && + RegisterSize == ARegisterSize.Int32) + { + Context.Generator.Emit(OpCodes.Conv_U8); + } Context.Generator.EmitStloc(Context.GetLocalIndex(Reg)); } diff --git a/Ryujinx/Cpu/Translation/AIoType.cs b/Ryujinx/Cpu/Translation/AIoType.cs index 34aa224e5..94f890814 100644 --- a/Ryujinx/Cpu/Translation/AIoType.cs +++ b/Ryujinx/Cpu/Translation/AIoType.cs @@ -10,9 +10,6 @@ namespace ChocolArm64.Translation Flag, Int, Float, - Vector, - Mask = 0xff, - VectorI = Vector | 1 << 8, - VectorF = Vector | 1 << 9 + Vector } } \ No newline at end of file diff --git a/Ryujinx/Hid.cs b/Ryujinx/Hid.cs new file mode 100644 index 000000000..c344ec587 --- /dev/null +++ b/Ryujinx/Hid.cs @@ -0,0 +1,185 @@ +using Ryujinx.OsHle; +using System; +using System.Runtime.InteropServices; + +namespace Ryujinx +{ + public class Hid + { + /* + Thanks to: + https://github.com/reswitched/libtransistor/blob/development/lib/hid.c + https://github.com/reswitched/libtransistor/blob/development/include/libtransistor/hid.h + https://github.com/switchbrew/libnx/blob/master/nx/source/services/hid.c + https://github.com/switchbrew/libnx/blob/master/nx/include/switch/services/hid.h + + struct HidSharedMemory + { + header[0x400]; + touchscreen[0x3000]; + mouse[0x400]; + keyboard[0x400]; + unkSection1[0x400]; + unkSection2[0x400]; + unkSection3[0x400]; + unkSection4[0x400]; + unkSection5[0x200]; + unkSection6[0x200]; + unkSection7[0x200]; + unkSection8[0x800]; + controllerSerials[0x4000]; + controllers[0x5000 * 10]; + unkSection9[0x4600]; + } + */ + + private const int Hid_Num_Entries = 16; + private Switch Ns; + private long SharedMemOffset; + + public Hid(Switch Ns) + { + this.Ns = Ns; + } + + public void Init(long HidOffset) + { + unsafe + { + if (HidOffset == 0 || HidOffset + Horizon.HidSize > uint.MaxValue) + { + return; + } + + SharedMemOffset = HidOffset; + + uint InnerOffset = (uint)Marshal.SizeOf(typeof(HidSharedMemHeader)); + + IntPtr HidPtr = new IntPtr(Ns.Ram.ToInt64() + (uint)SharedMemOffset + InnerOffset); + + HidTouchScreen TouchScreen = new HidTouchScreen(); + TouchScreen.Header.TimestampTicks = (ulong)Environment.TickCount; + TouchScreen.Header.NumEntries = (ulong)Hid_Num_Entries; + TouchScreen.Header.LatestEntry = 0; + TouchScreen.Header.MaxEntryIndex = (ulong)Hid_Num_Entries - 1; + TouchScreen.Header.Timestamp = (ulong)Environment.TickCount; + + //TODO: Write this structure when the input is implemented + //Marshal.StructureToPtr(TouchScreen, HidPtr, false); + + InnerOffset += (uint)Marshal.SizeOf(typeof(HidTouchScreen)); + HidPtr = new IntPtr(Ns.Ram.ToInt64() + (uint)SharedMemOffset + InnerOffset); + + HidMouse Mouse = new HidMouse(); + Mouse.Header.TimestampTicks = (ulong)Environment.TickCount; + Mouse.Header.NumEntries = (ulong)Hid_Num_Entries; + Mouse.Header.LatestEntry = 0; + Mouse.Header.MaxEntryIndex = (ulong)Hid_Num_Entries - 1; + + //TODO: Write this structure when the input is implemented + //Marshal.StructureToPtr(Mouse, HidPtr, false); + + InnerOffset += (uint)Marshal.SizeOf(typeof(HidMouse)); + HidPtr = new IntPtr(Ns.Ram.ToInt64() + (uint)SharedMemOffset + InnerOffset); + + HidKeyboard Keyboard = new HidKeyboard(); + Keyboard.Header.TimestampTicks = (ulong)Environment.TickCount; + Keyboard.Header.NumEntries = (ulong)Hid_Num_Entries; + Keyboard.Header.LatestEntry = 0; + Keyboard.Header.MaxEntryIndex = (ulong)Hid_Num_Entries - 1; + + //TODO: Write this structure when the input is implemented + //Marshal.StructureToPtr(Keyboard, HidPtr, false); + + InnerOffset += (uint)Marshal.SizeOf(typeof(HidKeyboard)) + + (uint)Marshal.SizeOf(typeof(HidUnknownSection1)) + + (uint)Marshal.SizeOf(typeof(HidUnknownSection2)) + + (uint)Marshal.SizeOf(typeof(HidUnknownSection3)) + + (uint)Marshal.SizeOf(typeof(HidUnknownSection4)) + + (uint)Marshal.SizeOf(typeof(HidUnknownSection5)) + + (uint)Marshal.SizeOf(typeof(HidUnknownSection6)) + + (uint)Marshal.SizeOf(typeof(HidUnknownSection7)) + + (uint)Marshal.SizeOf(typeof(HidUnknownSection8)) + + (uint)Marshal.SizeOf(typeof(HidControllerSerials)); + + //Increase the loop to initialize more controller. + for (int i = 8; i < Enum.GetNames(typeof(HidControllerID)).Length - 1; i++) + { + HidPtr = new IntPtr(Ns.Ram.ToInt64() + (uint)SharedMemOffset + InnerOffset + (uint)(Marshal.SizeOf(typeof(HidController)) * i)); + + HidController Controller = new HidController(); + Controller.Header.Type = (uint)(HidControllerType.ControllerType_Handheld | HidControllerType.ControllerType_JoyconPair); + Controller.Header.IsHalf = 0; + Controller.Header.SingleColorsDescriptor = (uint)(HidControllerColorDescription.ColorDesc_ColorsNonexistent); + Controller.Header.SingleColorBody = 0; + Controller.Header.SingleColorButtons = 0; + Controller.Header.SplitColorsDescriptor = 0; + Controller.Header.LeftColorBody = (uint)JoyConColor.Body_Neon_Red; + Controller.Header.LeftColorButtons = (uint)JoyConColor.Buttons_Neon_Red; + Controller.Header.RightColorBody = (uint)JoyConColor.Body_Neon_Blue; + Controller.Header.RightColorButtons = (uint)JoyConColor.Buttons_Neon_Blue; + + Controller.Layouts = new HidControllerLayout[Enum.GetNames(typeof(HidControllerLayouts)).Length]; + Controller.Layouts[(int)HidControllerLayouts.Main] = new HidControllerLayout(); + Controller.Layouts[(int)HidControllerLayouts.Main].Header.LatestEntry = (ulong)Hid_Num_Entries; + + Marshal.StructureToPtr(Controller, HidPtr, false); + } + + Logging.Info("HID Initialized!"); + } + } + + public void SendControllerButtons(HidControllerID ControllerId, + HidControllerLayouts Layout, + HidControllerKeys Buttons, + JoystickPosition LeftJoystick, + JoystickPosition RightJoystick) + { + uint InnerOffset = (uint)Marshal.SizeOf(typeof(HidSharedMemHeader)) + + (uint)Marshal.SizeOf(typeof(HidTouchScreen)) + + (uint)Marshal.SizeOf(typeof(HidMouse)) + + (uint)Marshal.SizeOf(typeof(HidKeyboard)) + + (uint)Marshal.SizeOf(typeof(HidUnknownSection1)) + + (uint)Marshal.SizeOf(typeof(HidUnknownSection2)) + + (uint)Marshal.SizeOf(typeof(HidUnknownSection3)) + + (uint)Marshal.SizeOf(typeof(HidUnknownSection4)) + + (uint)Marshal.SizeOf(typeof(HidUnknownSection5)) + + (uint)Marshal.SizeOf(typeof(HidUnknownSection6)) + + (uint)Marshal.SizeOf(typeof(HidUnknownSection7)) + + (uint)Marshal.SizeOf(typeof(HidUnknownSection8)) + + (uint)Marshal.SizeOf(typeof(HidControllerSerials)) + + ((uint)(Marshal.SizeOf(typeof(HidController)) * (int)ControllerId)) + + (uint)Marshal.SizeOf(typeof(HidControllerHeader)) + + (uint)Layout * (uint)Marshal.SizeOf(typeof(HidControllerLayout)); + + IntPtr HidPtr = new IntPtr(Ns.Ram.ToInt64() + (uint)SharedMemOffset + InnerOffset); + + HidControllerLayoutHeader OldControllerHeaderLayout = (HidControllerLayoutHeader)Marshal.PtrToStructure(HidPtr, typeof(HidControllerLayoutHeader)); + + HidControllerLayoutHeader ControllerLayoutHeader = new HidControllerLayoutHeader + { + TimestampTicks = (ulong)Environment.TickCount, + NumEntries = (ulong)Hid_Num_Entries, + MaxEntryIndex = (ulong)Hid_Num_Entries - 1, + LatestEntry = (OldControllerHeaderLayout.LatestEntry < (ulong)Hid_Num_Entries ? OldControllerHeaderLayout.LatestEntry + 1 : 0) + }; + + Marshal.StructureToPtr(ControllerLayoutHeader, HidPtr, false); + + InnerOffset += (uint)Marshal.SizeOf(typeof(HidControllerLayoutHeader)) + (uint)((uint)(ControllerLayoutHeader.LatestEntry) * Marshal.SizeOf(typeof(HidControllerInputEntry))); + HidPtr = new IntPtr(Ns.Ram.ToInt64() + (uint)SharedMemOffset + InnerOffset); + + HidControllerInputEntry ControllerInputEntry = new HidControllerInputEntry(); + ControllerInputEntry.Timestamp = (ulong)Environment.TickCount; + ControllerInputEntry.Timestamp_2 = (ulong)Environment.TickCount; + ControllerInputEntry.Buttons = (ulong)Buttons; + ControllerInputEntry.Joysticks = new JoystickPosition[(int)HidControllerJoystick.Joystick_Num_Sticks]; + ControllerInputEntry.Joysticks[(int)HidControllerJoystick.Joystick_Left] = LeftJoystick; + ControllerInputEntry.Joysticks[(int)HidControllerJoystick.Joystick_Right] = RightJoystick; + ControllerInputEntry.ConnectionState = (ulong)(HidControllerConnectionState.Controller_State_Connected | HidControllerConnectionState.Controller_State_Wired); + + Marshal.StructureToPtr(ControllerInputEntry, HidPtr, false); + } + } +} diff --git a/Ryujinx/Hid/HidController.cs b/Ryujinx/Hid/HidController.cs new file mode 100644 index 000000000..433d7b9ba --- /dev/null +++ b/Ryujinx/Hid/HidController.cs @@ -0,0 +1,184 @@ +using System.Runtime.InteropServices; + +namespace Ryujinx +{ + public enum HidControllerKeys + { + KEY_A = (1 << 0), + KEY_B = (1 << 1), + KEY_X = (1 << 2), + KEY_Y = (1 << 3), + KEY_LSTICK = (1 << 4), + KEY_RSTICK = (1 << 5), + KEY_L = (1 << 6), + KEY_R = (1 << 7), + KEY_ZL = (1 << 8), + KEY_ZR = (1 << 9), + KEY_PLUS = (1 << 10), + KEY_MINUS = (1 << 11), + KEY_DLEFT = (1 << 12), + KEY_DUP = (1 << 13), + KEY_DRIGHT = (1 << 14), + KEY_DDOWN = (1 << 15), + KEY_LSTICK_LEFT = (1 << 16), + KEY_LSTICK_UP = (1 << 17), + KEY_LSTICK_RIGHT = (1 << 18), + KEY_LSTICK_DOWN = (1 << 19), + KEY_RSTICK_LEFT = (1 << 20), + KEY_RSTICK_UP = (1 << 21), + KEY_RSTICK_RIGHT = (1 << 22), + KEY_RSTICK_DOWN = (1 << 23), + KEY_SL = (1 << 24), + KEY_SR = (1 << 25), + + // Pseudo-key for at least one finger on the touch screen + KEY_TOUCH = (1 << 26), + + // Buttons by orientation (for single Joy-Con), also works with Joy-Con pairs, Pro Controller + KEY_JOYCON_RIGHT = (1 << 0), + KEY_JOYCON_DOWN = (1 << 1), + KEY_JOYCON_UP = (1 << 2), + KEY_JOYCON_LEFT = (1 << 3), + + // Generic catch-all directions, also works for single Joy-Con + KEY_UP = KEY_DUP | KEY_LSTICK_UP | KEY_RSTICK_UP, + KEY_DOWN = KEY_DDOWN | KEY_LSTICK_DOWN | KEY_RSTICK_DOWN, + KEY_LEFT = KEY_DLEFT | KEY_LSTICK_LEFT | KEY_RSTICK_LEFT, + KEY_RIGHT = KEY_DRIGHT | KEY_LSTICK_RIGHT | KEY_RSTICK_RIGHT, + } + + public enum HidControllerID + { + CONTROLLER_PLAYER_1 = 0, + CONTROLLER_PLAYER_2 = 1, + CONTROLLER_PLAYER_3 = 2, + CONTROLLER_PLAYER_4 = 3, + CONTROLLER_PLAYER_5 = 4, + CONTROLLER_PLAYER_6 = 5, + CONTROLLER_PLAYER_7 = 6, + CONTROLLER_PLAYER_8 = 7, + CONTROLLER_HANDHELD = 8, + CONTROLLER_UNKNOWN = 9 + } + + public enum HidControllerJoystick + { + Joystick_Left = 0, + Joystick_Right = 1, + Joystick_Num_Sticks = 2 + } + + public enum HidControllerLayouts + { + Pro_Controller, + Handheld_Joined, + Joined, + Left, + Right, + Main_No_Analog, + Main + } + + public enum HidControllerConnectionState + { + Controller_State_Connected = (1 << 0), + Controller_State_Wired = (1 << 1) + } + + public enum HidControllerType + { + ControllerType_ProController = (1 << 0), + ControllerType_Handheld = (1 << 1), + ControllerType_JoyconPair = (1 << 2), + ControllerType_JoyconLeft = (1 << 3), + ControllerType_JoyconRight = (1 << 4) + } + + public enum HidControllerColorDescription + { + ColorDesc_ColorsNonexistent = (1 << 1), + } + + [StructLayout(LayoutKind.Sequential, Size = 0x8)] + public struct JoystickPosition + { + public int DX; + public int DY; + } + + [StructLayout(LayoutKind.Sequential, Size = 0x20)] + public struct HidControllerMAC + { + public ulong Timestamp; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)] + public byte[] MAC; + public ulong Unknown; + public ulong Timestamp_2; + } + + [StructLayout(LayoutKind.Sequential, Size = 0x28)] + public struct HidControllerHeader + { + public uint Type; + public uint IsHalf; + public uint SingleColorsDescriptor; + public uint SingleColorBody; + public uint SingleColorButtons; + public uint SplitColorsDescriptor; + public uint LeftColorBody; + public uint LeftColorButtons; + public uint RightColorBody; + public uint RightColorButtons; + } + + [StructLayout(LayoutKind.Sequential, Size = 0x20)] + public struct HidControllerLayoutHeader + { + public ulong TimestampTicks; + public ulong NumEntries; + public ulong LatestEntry; + public ulong MaxEntryIndex; + } + + [StructLayout(LayoutKind.Sequential, Size = 0x30)] + public struct HidControllerInputEntry + { + public ulong Timestamp; + public ulong Timestamp_2; + public ulong Buttons; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = (int)HidControllerJoystick.Joystick_Num_Sticks)] + public JoystickPosition[] Joysticks; + public ulong ConnectionState; + } + + [StructLayout(LayoutKind.Sequential, Size = 0x350)] + public struct HidControllerLayout + { + public HidControllerLayoutHeader Header; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 17)] + public HidControllerInputEntry[] Entries; + } + + [StructLayout(LayoutKind.Sequential, Size = 0x5000)] + public struct HidController + { + public HidControllerHeader Header; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 7)] + public HidControllerLayout[] Layouts; + /* + pro_controller + handheld_joined + joined + left + right + main_no_analog + main + */ + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 0x2A70)] + public byte[] Unknown_1; + public HidControllerMAC MacLeft; + public HidControllerMAC MacRight; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 0xDF8)] + public byte[] Unknown_2; + } +} diff --git a/Ryujinx/Hid/HidKeyboard.cs b/Ryujinx/Hid/HidKeyboard.cs new file mode 100644 index 000000000..2ee51bfa1 --- /dev/null +++ b/Ryujinx/Hid/HidKeyboard.cs @@ -0,0 +1,33 @@ +using System.Runtime.InteropServices; + +namespace Ryujinx +{ + [StructLayout(LayoutKind.Sequential, Size = 0x20)] + public struct HidKeyboardHeader + { + public ulong TimestampTicks; + public ulong NumEntries; + public ulong LatestEntry; + public ulong MaxEntryIndex; + } + + [StructLayout(LayoutKind.Sequential, Size = 0x38)] + public struct HidKeyboardEntry + { + public ulong Timestamp; + public ulong Timestamp_2; + public ulong Modifier; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)] + public uint[] Keys; + } + + [StructLayout(LayoutKind.Sequential, Size = 0x400)] + public struct HidKeyboard + { + public HidKeyboardHeader Header; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 17)] + public HidKeyboardEntry[] Entries; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 0x28)] + public byte[] Padding; + } +} diff --git a/Ryujinx/Hid/HidMouse.cs b/Ryujinx/Hid/HidMouse.cs new file mode 100644 index 000000000..db01e649c --- /dev/null +++ b/Ryujinx/Hid/HidMouse.cs @@ -0,0 +1,37 @@ +using System.Runtime.InteropServices; + +namespace Ryujinx +{ + [StructLayout(LayoutKind.Sequential, Size = 0x20)] + public struct HidMouseHeader + { + public ulong TimestampTicks; + public ulong NumEntries; + public ulong LatestEntry; + public ulong MaxEntryIndex; + } + + [StructLayout(LayoutKind.Sequential, Size = 0x30)] + public struct HidMouseEntry + { + public ulong Timestamp; + public ulong Timestamp_2; + public uint X; + public uint Y; + public uint VelocityX; + public uint VelocityY; + public uint ScrollVelocityX; + public uint ScrollVelocityY; + public ulong Buttons; + } + + [StructLayout(LayoutKind.Sequential, Size = 0x400)] + public struct HidMouse + { + public HidMouseHeader Header; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 17)] + public HidMouseEntry[] Entries; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 0xB0)] + public byte[] Padding; + } +} diff --git a/Ryujinx/Hid/HidTouchScreen.cs b/Ryujinx/Hid/HidTouchScreen.cs new file mode 100644 index 000000000..7fb022893 --- /dev/null +++ b/Ryujinx/Hid/HidTouchScreen.cs @@ -0,0 +1,54 @@ +using System.Runtime.InteropServices; + +namespace Ryujinx +{ + [StructLayout(LayoutKind.Sequential, Size = 0x28)] + public struct HidTouchScreenHeader + { + public ulong TimestampTicks; + public ulong NumEntries; + public ulong LatestEntry; + public ulong MaxEntryIndex; + public ulong Timestamp; + } + + [StructLayout(LayoutKind.Sequential, Size = 0x10)] + public struct HidTouchScreenEntryHeader + { + public ulong Timestamp; + public ulong NumTouches; + } + + [StructLayout(LayoutKind.Sequential, Size = 0x28)] + public struct HidTouchScreenEntryTouch + { + public ulong Timestamp; + public uint Padding; + public uint TouchIndex; + public uint X; + public uint Y; + public uint DiameterX; + public uint DiameterY; + public uint Angle; + public uint Padding_2; + } + + [StructLayout(LayoutKind.Sequential, Size = 0x298)] + public struct HidTouchScreenEntry + { + public HidTouchScreenEntryHeader Header; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)] + public HidTouchScreenEntryTouch[] Touches; + public ulong Unknown; + } + + [StructLayout(LayoutKind.Sequential, Size = 0x3000)] + public struct HidTouchScreen + { + public HidTouchScreenHeader Header; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 17)] + public HidTouchScreenEntry[] Entries; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 0x3C0)] + public byte[] Padding; + } +} diff --git a/Ryujinx/Hid/HidUnknown.cs b/Ryujinx/Hid/HidUnknown.cs new file mode 100644 index 000000000..ef2172d5b --- /dev/null +++ b/Ryujinx/Hid/HidUnknown.cs @@ -0,0 +1,81 @@ +using System.Runtime.InteropServices; + +namespace Ryujinx +{ + [StructLayout(LayoutKind.Sequential, Size = 0x400)] + public struct HidSharedMemHeader + { + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 0x400)] + public byte[] Padding; + } + + [StructLayout(LayoutKind.Sequential, Size = 0x400)] + public struct HidUnknownSection1 + { + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 0x400)] + public byte[] Padding; + } + + [StructLayout(LayoutKind.Sequential, Size = 0x400)] + public struct HidUnknownSection2 + { + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 0x400)] + public byte[] Padding; + } + + [StructLayout(LayoutKind.Sequential, Size = 0x400)] + public struct HidUnknownSection3 + { + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 0x400)] + public byte[] Padding; + } + + [StructLayout(LayoutKind.Sequential, Size = 0x400)] + public struct HidUnknownSection4 + { + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 0x400)] + public byte[] Padding; + } + + [StructLayout(LayoutKind.Sequential, Size = 0x200)] + public struct HidUnknownSection5 + { + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 0x200)] + public byte[] Padding; + } + + [StructLayout(LayoutKind.Sequential, Size = 0x200)] + public struct HidUnknownSection6 + { + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 0x200)] + public byte[] Padding; + } + + [StructLayout(LayoutKind.Sequential, Size = 0x200)] + public struct HidUnknownSection7 + { + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 0x200)] + public byte[] Padding; + } + + [StructLayout(LayoutKind.Sequential, Size = 0x800)] + public struct HidUnknownSection8 + { + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 0x800)] + public byte[] Padding; + } + + [StructLayout(LayoutKind.Sequential, Size = 0x4000)] + public struct HidControllerSerials + { + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 0x4000)] + public byte[] Padding; + } + + [StructLayout(LayoutKind.Sequential, Size = 0x4600)] + public struct HidUnknownSection9 + { + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 0x4600)] + public byte[] Padding; + } +} diff --git a/Ryujinx/Hid/JoyCon.cs b/Ryujinx/Hid/JoyCon.cs new file mode 100644 index 000000000..9dde2b70a --- /dev/null +++ b/Ryujinx/Hid/JoyCon.cs @@ -0,0 +1,63 @@ +namespace Ryujinx +{ + public enum JoyConColor //Thanks to CTCaer + { + Body_Grey = 0x828282, + Body_Neon_Blue = 0x0AB9E6, + Body_Neon_Red = 0xFF3C28, + Body_Neon_Yellow = 0xE6FF00, + Body_Neon_Pink = 0xFF3278, + Body_Neon_Green = 0x1EDC00, + Body_Red = 0xE10F00, + + Buttons_Grey = 0x0F0F0F, + Buttons_Neon_Blue = 0x001E1E, + Buttons_Neon_Red = 0x1E0A0A, + Buttons_Neon_Yellow = 0x142800, + Buttons_Neon_Pink = 0x28001E, + Buttons_Neon_Green = 0x002800, + Buttons_Red = 0x280A0A + } + + public struct JoyConLeft + { + public int StickUp; + public int StickDown; + public int StickLeft; + public int StickRight; + public int StickButton; + public int DPadUp; + public int DPadDown; + public int DPadLeft; + public int DPadRight; + public int ButtonMinus; + public int ButtonL; + public int ButtonZL; + public int ButtonSL; + public int ButtonSR; + } + + public struct JoyConRight + { + public int StickUp; + public int StickDown; + public int StickLeft; + public int StickRight; + public int StickButton; + public int ButtonA; + public int ButtonB; + public int ButtonX; + public int ButtonY; + public int ButtonPlus; + public int ButtonR; + public int ButtonZR; + public int ButtonSL; + public int ButtonSR; + } + + public struct JoyCon + { + public JoyConLeft Left; + public JoyConRight Right; + } +} diff --git a/Ryujinx/OsHle/CondVar.cs b/Ryujinx/OsHle/CondVar.cs index eba8e4b05..91ea37bdb 100644 --- a/Ryujinx/OsHle/CondVar.cs +++ b/Ryujinx/OsHle/CondVar.cs @@ -1,6 +1,6 @@ -using ChocolArm64.Memory; using Ryujinx.OsHle.Handles; using System.Collections.Generic; +using System.Threading; namespace Ryujinx.OsHle { @@ -11,6 +11,8 @@ namespace Ryujinx.OsHle private long CondVarAddress; private long Timeout; + private bool OwnsCondVarValue; + private List WaitingThreads; public CondVar(Process Process, long CondVarAddress, long Timeout) @@ -24,34 +26,43 @@ namespace Ryujinx.OsHle public void WaitForSignal(HThread Thread) { - int Count = ReadCondVarValue(); + int Count = Process.Memory.ReadInt32(CondVarAddress); if (Count <= 0) { - //FIXME: We shouldn't need to do that? - Process.Scheduler.Yield(Thread); + lock (WaitingThreads) + { + WaitingThreads.Add(Thread); + } - return; + if (Timeout == -1) + { + Process.Scheduler.WaitForSignal(Thread); + } + else + { + Process.Scheduler.WaitForSignal(Thread, (int)(Timeout / 1000000)); + + lock (WaitingThreads) + { + WaitingThreads.Remove(Thread); + } + } } - WriteCondVarValue(Count - 1); + AcquireCondVarValue(); - lock (WaitingThreads) + Count = Process.Memory.ReadInt32(CondVarAddress); + + if (Count > 0) { - WaitingThreads.Add(Thread); + Process.Memory.WriteInt32(CondVarAddress, Count - 1); } - if (Timeout != -1) - { - Process.Scheduler.WaitForSignal(Thread, (int)(Timeout / 1000000)); - } - else - { - Process.Scheduler.WaitForSignal(Thread); - } + ReleaseCondVarValue(); } - public void SetSignal(int Count) + public void SetSignal(HThread Thread, int Count) { lock (WaitingThreads) { @@ -59,7 +70,11 @@ namespace Ryujinx.OsHle { Process.Scheduler.Signal(WaitingThreads.ToArray()); - WriteCondVarValue(WaitingThreads.Count); + AcquireCondVarValue(); + + Process.Memory.WriteInt32(CondVarAddress, WaitingThreads.Count); + + ReleaseCondVarValue(); WaitingThreads.Clear(); } @@ -85,19 +100,39 @@ namespace Ryujinx.OsHle WaitingThreads.RemoveAt(HighestPrioIndex); } - WriteCondVarValue(Count); + AcquireCondVarValue(); + + Process.Memory.WriteInt32(CondVarAddress, Count); + + ReleaseCondVarValue(); } } + + Process.Scheduler.Suspend(Thread.ProcessorId); + Process.Scheduler.Resume(Thread); + } + + private void AcquireCondVarValue() + { + if (!OwnsCondVarValue) + { + while (!Process.Memory.AcquireAddress(CondVarAddress)) + { + Thread.Yield(); + } + + OwnsCondVarValue = true; + } } - private int ReadCondVarValue() + private void ReleaseCondVarValue() { - return AMemoryHelper.ReadInt32Exclusive(Process.Memory, CondVarAddress); - } + if (OwnsCondVarValue) + { + OwnsCondVarValue = false; - private void WriteCondVarValue(int Value) - { - AMemoryHelper.WriteInt32Exclusive(Process.Memory, CondVarAddress, Value); + Process.Memory.ReleaseAddress(CondVarAddress); + } } } } \ No newline at end of file diff --git a/Ryujinx/OsHle/Handles/HSharedMem.cs b/Ryujinx/OsHle/Handles/HSharedMem.cs index 2030ff0a4..a493276a2 100644 --- a/Ryujinx/OsHle/Handles/HSharedMem.cs +++ b/Ryujinx/OsHle/Handles/HSharedMem.cs @@ -1,13 +1,76 @@ +using System; +using System.Collections.Generic; + namespace Ryujinx.OsHle.Handles { class HSharedMem { - public long PhysPos { get; private set; } - public long VirtPos { get; set; } + private List Positions; + + public int PositionsCount => Positions.Count; + + public EventHandler MemoryMapped; + public EventHandler MemoryUnmapped; public HSharedMem(long PhysPos) { - this.PhysPos = PhysPos; + Positions = new List(); + } + + public void AddVirtualPosition(long Position) + { + lock (Positions) + { + Positions.Add(Position); + + if (MemoryMapped != null) + { + MemoryMapped(this, EventArgs.Empty); + } + } + } + + public void RemoveVirtualPosition(long Position) + { + lock (Positions) + { + Positions.Remove(Position); + + if (MemoryUnmapped != null) + { + MemoryUnmapped(this, EventArgs.Empty); + } + } + } + + public long GetVirtualPosition(int Index) + { + 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; + } } } } \ No newline at end of file diff --git a/Ryujinx/OsHle/Handles/KProcessScheduler.cs b/Ryujinx/OsHle/Handles/KProcessScheduler.cs index ca612de99..9044987fd 100644 --- a/Ryujinx/OsHle/Handles/KProcessScheduler.cs +++ b/Ryujinx/OsHle/Handles/KProcessScheduler.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Concurrent; using System.Collections.Generic; using System.Threading; @@ -6,19 +7,8 @@ namespace Ryujinx.OsHle.Handles { class KProcessScheduler : IDisposable { - private enum ThreadState - { - WaitingToRun, - WaitingSignal, - Running - } - private class SchedulerThread : IDisposable { - public bool Signaled { get; set; } - - public ThreadState State { get; set; } - public HThread Thread { get; private set; } public AutoResetEvent WaitEvent { get; private set; } @@ -44,9 +34,70 @@ namespace Ryujinx.OsHle.Handles } } - private Dictionary AllThreads; + private class ThreadQueue + { + private List Threads; - private Queue[] WaitingThreads; + public ThreadQueue() + { + Threads = new List(); + } + + public void Push(SchedulerThread Thread) + { + lock (Threads) + { + Threads.Add(Thread); + } + } + + public SchedulerThread Pop(int MinPriority = 0x40) + { + lock (Threads) + { + SchedulerThread SchedThread; + + int HighestPriority = MinPriority; + + int HighestPrioIndex = -1; + + for (int Index = 0; Index < Threads.Count; Index++) + { + SchedThread = Threads[Index]; + + if (HighestPriority > SchedThread.Thread.Priority) + { + HighestPriority = SchedThread.Thread.Priority; + + HighestPrioIndex = Index; + } + } + + if (HighestPrioIndex == -1) + { + return null; + } + + SchedThread = Threads[HighestPrioIndex]; + + Threads.RemoveAt(HighestPrioIndex); + + return SchedThread; + } + } + + public bool HasThread(SchedulerThread SchedThread) + { + lock (Threads) + { + return Threads.Contains(SchedThread); + } + } + } + + private ConcurrentDictionary AllThreads; + + private ThreadQueue[] WaitingToRun; private HashSet ActiveProcessors; @@ -54,13 +105,13 @@ namespace Ryujinx.OsHle.Handles public KProcessScheduler() { - AllThreads = new Dictionary(); + AllThreads = new ConcurrentDictionary(); - WaitingThreads = new Queue[4]; + WaitingToRun = new ThreadQueue[4]; - for (int Index = 0; Index < WaitingThreads.Length; Index++) + for (int Index = 0; Index < 4; Index++) { - WaitingThreads[Index] = new Queue(); + WaitingToRun[Index] = new ThreadQueue(); } ActiveProcessors = new HashSet(); @@ -72,132 +123,171 @@ namespace Ryujinx.OsHle.Handles { lock (SchedLock) { - if (AllThreads.ContainsKey(Thread)) + SchedulerThread SchedThread = new SchedulerThread(Thread); + + if (!AllThreads.TryAdd(Thread, SchedThread)) { return; } - SchedulerThread SchedThread = new SchedulerThread(Thread); - - AllThreads.Add(Thread, SchedThread); - if (!ActiveProcessors.Contains(Thread.ProcessorId)) { ActiveProcessors.Add(Thread.ProcessorId); Thread.Thread.Execute(); - SetThreadAsRunning(SchedThread); - - SchedThread.State = ThreadState.Running; + Logging.Debug($"{GetDbgThreadInfo(Thread)} running."); } else { - InsertSorted(SchedThread); - - SchedThread.State = ThreadState.WaitingToRun; + WaitingToRun[Thread.ProcessorId].Push(SchedThread); Logging.Debug($"{GetDbgThreadInfo(SchedThread.Thread)} waiting to run."); } } } - public void WaitForSignal(HThread Thread, int TimeoutMs) + public void Suspend(int ProcessorId) { - Logging.Debug($"{GetDbgThreadInfo(Thread)} entering signal wait state with timeout."); + lock (SchedLock) + { + SchedulerThread SchedThread = WaitingToRun[ProcessorId].Pop(); - PutThreadToWait(Thread, ThreadState.WaitingSignal, TimeoutMs); + if (SchedThread != null) + { + RunThread(SchedThread); + } + else + { + ActiveProcessors.Remove(ProcessorId); + } + } } - public void WaitForSignal(HThread Thread) + public void Resume(HThread CurrThread) { + SchedulerThread SchedThread; + + Logging.Debug($"{GetDbgThreadInfo(CurrThread)} entering ipc delay wait state."); + + lock (SchedLock) + { + if (!AllThreads.TryGetValue(CurrThread, out SchedThread)) + { + Logging.Error($"{GetDbgThreadInfo(CurrThread)} was not found on the scheduler queue!"); + + return; + } + } + + TryResumingExecution(SchedThread); + } + + public void WaitForSignal(HThread Thread, int Timeout = -1) + { + SchedulerThread SchedThread; + Logging.Debug($"{GetDbgThreadInfo(Thread)} entering signal wait state."); - PutThreadToWait(Thread, ThreadState.WaitingSignal); + lock (SchedLock) + { + SchedThread = WaitingToRun[Thread.ProcessorId].Pop(); + + if (SchedThread != null) + { + RunThread(SchedThread); + } + else + { + ActiveProcessors.Remove(Thread.ProcessorId); + } + + if (!AllThreads.TryGetValue(Thread, out SchedThread)) + { + Logging.Error($"{GetDbgThreadInfo(Thread)} was not found on the scheduler queue!"); + + return; + } + } + + if (Timeout >= 0) + { + Logging.Debug($"{GetDbgThreadInfo(Thread)} has wait timeout of {Timeout}ms."); + + SchedThread.WaitEvent.WaitOne(Timeout); + } + else + { + SchedThread.WaitEvent.WaitOne(); + } + + TryResumingExecution(SchedThread); + } + + private void TryResumingExecution(SchedulerThread SchedThread) + { + HThread Thread = SchedThread.Thread; + + lock (SchedLock) + { + if (ActiveProcessors.Add(Thread.ProcessorId)) + { + Logging.Debug($"{GetDbgThreadInfo(Thread)} resuming execution..."); + + return; + } + + WaitingToRun[Thread.ProcessorId].Push(SchedThread); + } + + SchedThread.WaitEvent.WaitOne(); + + Logging.Debug($"{GetDbgThreadInfo(Thread)} resuming execution..."); } public void Yield(HThread Thread) - { - Logging.Debug($"{GetDbgThreadInfo(Thread)} yielded execution."); - - if (WaitingThreads[Thread.ProcessorId].Count == 0) - { - Logging.Debug($"{GetDbgThreadInfo(Thread)} resumed because theres nothing to run."); - - return; - } - - PutThreadToWait(Thread, ThreadState.WaitingToRun); - } - - private void PutThreadToWait(HThread Thread, ThreadState State, int TimeoutMs = -1) { SchedulerThread SchedThread; + Logging.Debug($"{GetDbgThreadInfo(Thread)} yielded execution."); + lock (SchedLock) { + SchedThread = WaitingToRun[Thread.ProcessorId].Pop(Thread.Priority); + + if (SchedThread == null) + { + Logging.Debug($"{GetDbgThreadInfo(Thread)} resumed because theres nothing better to run."); + + return; + } + + RunThread(SchedThread); + if (!AllThreads.TryGetValue(Thread, out SchedThread)) { - return; - } - - if (SchedThread.Signaled && SchedThread.State == ThreadState.WaitingSignal) - { - SchedThread.Signaled = false; + Logging.Error($"{GetDbgThreadInfo(Thread)} was not found on the scheduler queue!"); return; } - ActiveProcessors.Remove(Thread.ProcessorId); - - SchedThread.State = State; - - TryRunningWaitingThead(SchedThread.Thread.ProcessorId); - - if (State == ThreadState.WaitingSignal) - { - InsertSorted(SchedThread); - } - else - { - InsertAtEnd(SchedThread); - } + WaitingToRun[Thread.ProcessorId].Push(SchedThread); } - if (TimeoutMs >= 0) - { - Logging.Debug($"{GetDbgThreadInfo(SchedThread.Thread)} waiting with timeout of {TimeoutMs}ms."); + SchedThread.WaitEvent.WaitOne(); - SchedThread.WaitEvent.WaitOne(TimeoutMs); + Logging.Debug($"{GetDbgThreadInfo(Thread)} resuming execution..."); + } + + private void RunThread(SchedulerThread SchedThread) + { + if (!SchedThread.Thread.Thread.Execute()) + { + SchedThread.WaitEvent.Set(); } else { - Logging.Debug($"{GetDbgThreadInfo(SchedThread.Thread)} waiting indefinitely."); - - SchedThread.WaitEvent.WaitOne(); - } - - while (true) - { - lock (SchedLock) - { - Logging.Debug($"Trying to run {GetDbgThreadInfo(SchedThread.Thread)}."); - - if (!ActiveProcessors.Contains(SchedThread.Thread.ProcessorId)) - { - SetThreadAsRunning(SchedThread); - - break; - } - else - { - SchedThread.State = ThreadState.WaitingToRun; - - Logging.Debug($"{GetDbgThreadInfo(SchedThread.Thread)} waiting to run."); - } - } - - SchedThread.WaitEvent.WaitOne(); + Logging.Debug($"{GetDbgThreadInfo(SchedThread.Thread)} running."); } } @@ -205,107 +295,21 @@ namespace Ryujinx.OsHle.Handles { lock (SchedLock) { - HashSet SignaledProcessorIds = new HashSet(); - foreach (HThread Thread in Threads) { - Logging.Debug($"{GetDbgThreadInfo(Thread)} signaled."); - if (AllThreads.TryGetValue(Thread, out SchedulerThread SchedThread)) { - if (SchedThread.State == ThreadState.WaitingSignal) + if (!WaitingToRun[Thread.ProcessorId].HasThread(SchedThread)) { - SchedThread.State = ThreadState.WaitingToRun; + Logging.Debug($"{GetDbgThreadInfo(Thread)} signaled."); - SignaledProcessorIds.Add(Thread.ProcessorId); + SchedThread.WaitEvent.Set(); } - - SchedThread.Signaled = true; } } - - foreach (int ProcessorId in SignaledProcessorIds) - { - TryRunningWaitingThead(ProcessorId); - } } } - private void TryRunningWaitingThead(int ProcessorId) - { - Logging.Debug($"TryRunningWaitingThead core {ProcessorId}."); - - lock (SchedLock) - { - if (!ActiveProcessors.Contains(ProcessorId) && WaitingThreads[ProcessorId].Count > 0) - { - SchedulerThread SchedThread = WaitingThreads[ProcessorId].Dequeue(); - - Logging.Debug($"Now trying to run {GetDbgThreadInfo(SchedThread.Thread)}."); - - if (!SchedThread.Thread.Thread.Execute()) - { - SchedThread.WaitEvent.Set(); - } - else - { - SetThreadAsRunning(SchedThread); - } - } - else - { - Logging.Debug($"Processor id {ProcessorId} already being used or no waiting threads."); - } - } - } - - private void SetThreadAsRunning(SchedulerThread SchedThread) - { - ActiveProcessors.Add(SchedThread.Thread.ProcessorId); - - SchedThread.State = ThreadState.Running; - - SchedThread.Signaled = false; - - Logging.Debug($"{GetDbgThreadInfo(SchedThread.Thread)} running."); - } - - private void InsertSorted(SchedulerThread SchedThread) - { - HThread Thread = SchedThread.Thread; - - Queue CoreQueue = WaitingThreads[Thread.ProcessorId]; - - Queue TempQueue = new Queue(CoreQueue.Count); - - while (CoreQueue.Count > 0) - { - if (CoreQueue.Peek().Thread.Priority >= Thread.Priority) - { - break; - } - - TempQueue.Enqueue(CoreQueue.Dequeue()); - } - - CoreQueue.Enqueue(SchedThread); - - while (CoreQueue.Count > 0) - { - TempQueue.Enqueue(CoreQueue.Dequeue()); - } - - while (TempQueue.Count > 0) - { - CoreQueue.Enqueue(TempQueue.Dequeue()); - } - } - - private void InsertAtEnd(SchedulerThread SchedThread) - { - WaitingThreads[SchedThread.Thread.ProcessorId].Enqueue(SchedThread); - } - private string GetDbgThreadInfo(HThread Thread) { return $"Thread {Thread.ThreadId} (core {Thread.ProcessorId}) prio {Thread.Priority}"; @@ -320,14 +324,9 @@ namespace Ryujinx.OsHle.Handles { if (Disposing) { - foreach (Queue SchedThreads in WaitingThreads) + foreach (SchedulerThread SchedThread in AllThreads.Values) { - foreach (SchedulerThread SchedThread in SchedThreads) - { - SchedThread.Dispose(); - } - - SchedThreads.Clear(); + SchedThread.Dispose(); } } } diff --git a/Ryujinx/OsHle/Horizon.cs b/Ryujinx/OsHle/Horizon.cs index dd5787302..e72c62dc0 100644 --- a/Ryujinx/OsHle/Horizon.cs +++ b/Ryujinx/OsHle/Horizon.cs @@ -4,6 +4,7 @@ using Ryujinx.OsHle.Handles; using Ryujinx.OsHle.Utilities; using System.Collections.Concurrent; using System.IO; +using System; namespace Ryujinx.OsHle { @@ -30,6 +31,8 @@ namespace Ryujinx.OsHle private ConcurrentDictionary Processes; + private HSharedMem HidSharedMem; + private AMemoryAlloc Allocator; private Switch Ns; @@ -55,7 +58,12 @@ namespace Ryujinx.OsHle HidOffset = Allocator.Alloc(HidSize); FontOffset = Allocator.Alloc(FontSize); - HidHandle = Handles.GenerateId(new HSharedMem(HidOffset)); + HidSharedMem = new HSharedMem(HidOffset); + + HidSharedMem.MemoryMapped += HidInit; + + HidHandle = Handles.GenerateId(HidSharedMem); + FontHandle = Handles.GenerateId(new HSharedMem(FontOffset)); } @@ -79,6 +87,8 @@ namespace Ryujinx.OsHle continue; } + Logging.Info($"Loading {Path.GetFileNameWithoutExtension(File)}..."); + using (FileStream Input = new FileStream(File, FileMode.Open)) { Nso Program = new Nso(Input); @@ -102,7 +112,7 @@ namespace Ryujinx.OsHle Processes.TryAdd(ProcessId, MainProcess); } - public void LoadProgram(string FileName) + public void LoadProgram(string FileName) { int ProcessId = IdGen.GenerateId(); @@ -136,6 +146,23 @@ namespace Ryujinx.OsHle } } + 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); + } + + return Success; + } + internal bool TryGetProcess(int ProcessId, out Process Process) { if (!Processes.TryGetValue(ProcessId, out Process)) @@ -161,11 +188,15 @@ namespace Ryujinx.OsHle Handles.Delete(Handle); } - public long GetVirtHidOffset() + private void HidInit(object sender, EventArgs e) { - HSharedMem HidSharedMem = Handles.GetData(HidHandle); + HSharedMem SharedMem = (HSharedMem)sender; - return HidSharedMem.VirtPos; + if (SharedMem.TryGetLastVirtualPosition(out long Position)) + { + Logging.Info($"HID shared memory successfully mapped to {Position:x16}!"); + Ns.Hid.Init(Position); + } } } } \ No newline at end of file diff --git a/Ryujinx/OsHle/Mutex.cs b/Ryujinx/OsHle/Mutex.cs index 60870baa5..43862d7b2 100644 --- a/Ryujinx/OsHle/Mutex.cs +++ b/Ryujinx/OsHle/Mutex.cs @@ -1,4 +1,3 @@ -using ChocolArm64.Memory; using Ryujinx.OsHle.Handles; using System.Collections.Concurrent; using System.Threading; @@ -13,6 +12,8 @@ namespace Ryujinx.OsHle private long MutexAddress; + private bool OwnsMutexValue; + private object EnterWaitLock; private ConcurrentQueue WaitingThreads; @@ -31,9 +32,11 @@ namespace Ryujinx.OsHle public void WaitForLock(HThread RequestingThread, int RequestingThreadHandle) { + AcquireMutexValue(); + lock (EnterWaitLock) { - int CurrentThreadHandle = ReadMutexValue() & ~MutexHasListenersMask; + int CurrentThreadHandle = Process.Memory.ReadInt32(MutexAddress) & ~MutexHasListenersMask; if (CurrentThreadHandle == RequestingThreadHandle || CurrentThreadHandle == 0) @@ -41,7 +44,9 @@ namespace Ryujinx.OsHle return; } - WriteMutexValue(CurrentThreadHandle | MutexHasListenersMask); + Process.Memory.WriteInt32(MutexAddress, CurrentThreadHandle | MutexHasListenersMask); + + ReleaseMutexValue(); WaitingThreads.Enqueue(RequestingThread); } @@ -51,24 +56,32 @@ namespace Ryujinx.OsHle public void GiveUpLock(int ThreadHandle) { + AcquireMutexValue(); + lock (EnterWaitLock) { - int CurrentThread = ReadMutexValue() & ~MutexHasListenersMask; + int CurrentThread = Process.Memory.ReadInt32(MutexAddress) & ~MutexHasListenersMask; if (CurrentThread == ThreadHandle) { Unlock(); } } + + ReleaseMutexValue(); } public void Unlock() { + AcquireMutexValue(); + lock (EnterWaitLock) { int HasListeners = WaitingThreads.Count > 1 ? MutexHasListenersMask : 0; - WriteMutexValue(HasListeners); + Process.Memory.WriteInt32(MutexAddress, HasListeners); + + ReleaseMutexValue(); HThread[] UnlockedThreads = new HThread[WaitingThreads.Count]; @@ -83,14 +96,27 @@ namespace Ryujinx.OsHle } } - private int ReadMutexValue() + private void AcquireMutexValue() { - return AMemoryHelper.ReadInt32Exclusive(Process.Memory, MutexAddress); + if (!OwnsMutexValue) + { + while (!Process.Memory.AcquireAddress(MutexAddress)) + { + Thread.Yield(); + } + + OwnsMutexValue = true; + } } - private void WriteMutexValue(int Value) + private void ReleaseMutexValue() { - AMemoryHelper.WriteInt32Exclusive(Process.Memory, MutexAddress, Value); + if (OwnsMutexValue) + { + OwnsMutexValue = false; + + Process.Memory.ReleaseAddress(MutexAddress); + } } } } \ No newline at end of file diff --git a/Ryujinx/OsHle/Objects/Aud/IAudioOut.cs b/Ryujinx/OsHle/Objects/Aud/IAudioOut.cs index 5feb67d89..9e55e30e8 100644 --- a/Ryujinx/OsHle/Objects/Aud/IAudioOut.cs +++ b/Ryujinx/OsHle/Objects/Aud/IAudioOut.cs @@ -101,17 +101,17 @@ namespace Ryujinx.OsHle.Objects.Aud KeysQueue.Enqueue(BufferId); - byte[] AudioOutBuffer = AMemoryHelper.ReadBytes(Context.Memory, Context.Request.SendBuff[0].Position, 0x28); + byte[] AudioOutBuffer = AMemoryHelper.ReadBytes(Context.Memory, Context.Request.SendBuff[0].Position, sizeof(long) * 5); using (MemoryStream MS = new MemoryStream(AudioOutBuffer)) { BinaryReader Reader = new BinaryReader(MS); - long PointerToSampleDataPointer = Reader.ReadInt64(); - long PointerToSampleData = Reader.ReadInt64(); - long CapacitySampleBuffer = Reader.ReadInt64(); - long SizeDataSampleBuffer = Reader.ReadInt64(); - long Unknown = Reader.ReadInt64(); + long PointerNextBuffer = Reader.ReadInt64(); + long PointerSampleBuffer = Reader.ReadInt64(); + long CapacitySampleBuffer = Reader.ReadInt64(); + long SizeDataInSampleBuffer = Reader.ReadInt64(); + long OffsetDataInSampleBuffer = Reader.ReadInt64(); - byte[] AudioSampleBuffer = AMemoryHelper.ReadBytes(Context.Memory, PointerToSampleData, (int)SizeDataSampleBuffer); + byte[] AudioSampleBuffer = AMemoryHelper.ReadBytes(Context.Memory, PointerSampleBuffer + OffsetDataInSampleBuffer, (int)SizeDataInSampleBuffer); if (OpenALInstalled) { @@ -145,8 +145,9 @@ namespace Ryujinx.OsHle.Objects.Aud if (KeysQueue.Count > 0) TempKey = KeysQueue.Dequeue(); AMemoryHelper.WriteBytes(Context.Memory, Context.Request.ReceiveBuff[0].Position, BitConverter.GetBytes(TempKey)); - - Context.ResponseData.Write((int)TempKey); + + int ReleasedBuffersCount = 1; + Context.ResponseData.Write(ReleasedBuffersCount); if (OpenALInstalled) { diff --git a/Ryujinx/OsHle/Objects/Time/ISystemClock.cs b/Ryujinx/OsHle/Objects/Time/ISystemClock.cs index 6705a5a19..1d9bae3bb 100644 --- a/Ryujinx/OsHle/Objects/Time/ISystemClock.cs +++ b/Ryujinx/OsHle/Objects/Time/ISystemClock.cs @@ -28,7 +28,7 @@ namespace Ryujinx.OsHle.Objects.Time { DateTime CurrentTime = DateTime.Now; - if (ClockType == SystemClockType.Standard || + if (ClockType == SystemClockType.User || ClockType == SystemClockType.Network) { CurrentTime = CurrentTime.ToUniversalTime(); diff --git a/Ryujinx/OsHle/Objects/Time/SystemClockType.cs b/Ryujinx/OsHle/Objects/Time/SystemClockType.cs index 052152b70..ad9675aab 100644 --- a/Ryujinx/OsHle/Objects/Time/SystemClockType.cs +++ b/Ryujinx/OsHle/Objects/Time/SystemClockType.cs @@ -2,7 +2,7 @@ namespace Ryujinx.OsHle.Objects.Time { enum SystemClockType { - Standard, + User, Network, Local } diff --git a/Ryujinx/OsHle/Process.cs b/Ryujinx/OsHle/Process.cs index 653d9dfda..4d07b94bd 100644 --- a/Ryujinx/OsHle/Process.cs +++ b/Ryujinx/OsHle/Process.cs @@ -70,6 +70,8 @@ namespace Ryujinx.OsHle public void LoadProgram(IExecutable Program) { + Logging.Info($"Image base at 0x{ImageBase:x16}."); + Executable Executable = new Executable(Program, Memory, ImageBase); Executables.Add(Executable); @@ -174,19 +176,19 @@ namespace Ryujinx.OsHle return -1; } - Thread.Registers.Break += BreakHandler; - Thread.Registers.SvcCall += SvcHandler.SvcCall; - Thread.Registers.Undefined += UndefinedHandler; - Thread.Registers.ProcessId = ProcessId; - Thread.Registers.ThreadId = Ns.Os.IdGen.GenerateId(); - Thread.Registers.Tpidr = TlsPageAddr + TlsSlot * TlsSize; - Thread.Registers.X0 = (ulong)ArgsPtr; - Thread.Registers.X1 = (ulong)Handle; - Thread.Registers.X31 = (ulong)StackTop; + 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.Tpidr = TlsPageAddr + TlsSlot * TlsSize; + Thread.ThreadState.X0 = (ulong)ArgsPtr; + Thread.ThreadState.X1 = (ulong)Handle; + Thread.ThreadState.X31 = (ulong)StackTop; Thread.WorkFinished += ThreadFinished; - ThreadsByTpidr.TryAdd(Thread.Registers.Tpidr, ThreadHnd); + ThreadsByTpidr.TryAdd(Thread.ThreadState.Tpidr, ThreadHnd); return Handle; } @@ -218,7 +220,7 @@ namespace Ryujinx.OsHle { if (sender is AThread Thread) { - TlsSlots.TryRemove(GetTlsSlot(Thread.Registers.Tpidr), out _); + TlsSlots.TryRemove(GetTlsSlot(Thread.ThreadState.Tpidr), out _); Ns.Os.IdGen.DeleteId(Thread.ThreadId); } @@ -229,9 +231,14 @@ namespace Ryujinx.OsHle return (int)((Position - TlsPageAddr) / TlsSize); } - public bool TryGetThread(long Tpidr, out HThread Thread) + public HThread GetThread(long Tpidr) { - return ThreadsByTpidr.TryGetValue(Tpidr, out Thread); + if (!ThreadsByTpidr.TryGetValue(Tpidr, out HThread Thread)) + { + Logging.Error($"Thread with TPIDR 0x{Tpidr:x16} not found!"); + } + + return Thread; } public void Dispose() diff --git a/Ryujinx/OsHle/Services/ServiceTime.cs b/Ryujinx/OsHle/Services/ServiceTime.cs index bcc046ef6..8a32aabc1 100644 --- a/Ryujinx/OsHle/Services/ServiceTime.cs +++ b/Ryujinx/OsHle/Services/ServiceTime.cs @@ -8,7 +8,7 @@ namespace Ryujinx.OsHle.Services { public static long TimeGetStandardUserSystemClock(ServiceCtx Context) { - MakeObject(Context, new ISystemClock(SystemClockType.Standard)); + MakeObject(Context, new ISystemClock(SystemClockType.User)); return 0; } diff --git a/Ryujinx/OsHle/Svc/SvcHandler.cs b/Ryujinx/OsHle/Svc/SvcHandler.cs index ad228c45d..3ab89d445 100644 --- a/Ryujinx/OsHle/Svc/SvcHandler.cs +++ b/Ryujinx/OsHle/Svc/SvcHandler.cs @@ -7,7 +7,7 @@ namespace Ryujinx.OsHle.Svc { partial class SvcHandler { - private delegate void SvcFunc(ARegisters Registers); + private delegate void SvcFunc(AThreadState ThreadState); private Dictionary SvcFuncs; @@ -25,6 +25,7 @@ namespace Ryujinx.OsHle.Svc { 0x03, SvcSetMemoryAttribute }, { 0x04, SvcMapMemory }, { 0x06, SvcQueryMemory }, + { 0x07, SvcExitProcess }, { 0x08, SvcCreateThread }, { 0x09, SvcStartThread }, { 0x0b, SvcSleepThread }, @@ -60,15 +61,15 @@ namespace Ryujinx.OsHle.Svc public void SvcCall(object sender, AInstExceptEventArgs e) { - ARegisters Registers = (ARegisters)sender; + AThreadState ThreadState = (AThreadState)sender; if (SvcFuncs.TryGetValue(e.Id, out SvcFunc Func)) { - Logging.Trace($"(Thread {Registers.ThreadId}) {Func.Method.Name} called."); + Logging.Trace($"(Thread {ThreadState.ThreadId}) {Func.Method.Name} called."); - Func(Registers); + Func(ThreadState); - Logging.Trace($"(Thread {Registers.ThreadId}) {Func.Method.Name} ended."); + Logging.Trace($"(Thread {ThreadState.ThreadId}) {Func.Method.Name} ended."); } else { diff --git a/Ryujinx/OsHle/Svc/SvcMemory.cs b/Ryujinx/OsHle/Svc/SvcMemory.cs index f6cef4cc9..fa91397c4 100644 --- a/Ryujinx/OsHle/Svc/SvcMemory.cs +++ b/Ryujinx/OsHle/Svc/SvcMemory.cs @@ -6,43 +6,43 @@ namespace Ryujinx.OsHle.Svc { partial class SvcHandler { - private void SvcSetHeapSize(ARegisters Registers) + private void SvcSetHeapSize(AThreadState ThreadState) { - uint Size = (uint)Registers.X1; + uint Size = (uint)ThreadState.X1; Memory.Manager.SetHeapSize(Size, (int)MemoryType.Heap); - Registers.X0 = (int)SvcResult.Success; - Registers.X1 = (ulong)Memory.Manager.HeapAddr; + ThreadState.X0 = (int)SvcResult.Success; + ThreadState.X1 = (ulong)Memory.Manager.HeapAddr; } - private void SvcSetMemoryAttribute(ARegisters Registers) + private void SvcSetMemoryAttribute(AThreadState ThreadState) { - long Position = (long)Registers.X0; - long Size = (long)Registers.X1; - int State0 = (int)Registers.X2; - int State1 = (int)Registers.X3; + long Position = (long)ThreadState.X0; + long Size = (long)ThreadState.X1; + int State0 = (int)ThreadState.X2; + int State1 = (int)ThreadState.X3; //TODO - Registers.X0 = (int)SvcResult.Success; + ThreadState.X0 = (int)SvcResult.Success; } - private void SvcMapMemory(ARegisters Registers) + private void SvcMapMemory(AThreadState ThreadState) { - long Dst = (long)Registers.X0; - long Src = (long)Registers.X1; - long Size = (long)Registers.X2; + long Dst = (long)ThreadState.X0; + long Src = (long)ThreadState.X1; + long Size = (long)ThreadState.X2; Memory.Manager.MapMirror(Src, Dst, Size, (int)MemoryType.MappedMemory); - Registers.X0 = (int)SvcResult.Success; + ThreadState.X0 = (int)SvcResult.Success; } - private void SvcQueryMemory(ARegisters Registers) + private void SvcQueryMemory(AThreadState ThreadState) { - long InfoPtr = (long)Registers.X0; - long Position = (long)Registers.X2; + long InfoPtr = (long)ThreadState.X0; + long Position = (long)ThreadState.X2; AMemoryMapInfo MapInfo = Memory.Manager.GetMapInfo(Position); @@ -59,55 +59,52 @@ namespace Ryujinx.OsHle.Svc //TODO: X1. - Registers.X0 = (int)SvcResult.Success; - Registers.X1 = 0; + ThreadState.X0 = (int)SvcResult.Success; + ThreadState.X1 = 0; } - private void SvcMapSharedMemory(ARegisters Registers) + private void SvcMapSharedMemory(AThreadState ThreadState) { - int Handle = (int)Registers.X0; - long Position = (long)Registers.X1; - long Size = (long)Registers.X2; - int Perm = (int)Registers.X3; + int Handle = (int)ThreadState.X0; + long Src = (long)ThreadState.X1; + long Size = (long)ThreadState.X2; + int Perm = (int)ThreadState.X3; - HSharedMem HndData = Ns.Os.Handles.GetData(Handle); + HSharedMem SharedMem = Ns.Os.Handles.GetData(Handle); - if (HndData != null) + if (SharedMem != null) { - long Src = Position; - long Dst = HndData.PhysPos; + SharedMem.AddVirtualPosition(Src); - HndData.VirtPos = Src; + Memory.Manager.MapPhys(Src, Size, (int)MemoryType.SharedMemory, (AMemoryPerm)Perm); - Memory.Manager.MapPhys(Position, Size, (int)MemoryType.SharedMemory, (AMemoryPerm)Perm); - - Registers.X0 = (int)SvcResult.Success; + ThreadState.X0 = (int)SvcResult.Success; } //TODO: Error codes. } - private void SvcUnmapSharedMemory(ARegisters Registers) + private void SvcUnmapSharedMemory(AThreadState ThreadState) { - int Handle = (int)Registers.X0; - long Position = (long)Registers.X1; - long Size = (long)Registers.X2; + int Handle = (int)ThreadState.X0; + long Position = (long)ThreadState.X1; + long Size = (long)ThreadState.X2; HSharedMem HndData = Ns.Os.Handles.GetData(Handle); if (HndData != null) { - Registers.X0 = (int)SvcResult.Success; + ThreadState.X0 = (int)SvcResult.Success; } //TODO: Error codes. } - private void SvcCreateTransferMemory(ARegisters Registers) + private void SvcCreateTransferMemory(AThreadState ThreadState) { - long Position = (long)Registers.X1; - long Size = (long)Registers.X2; - int Perm = (int)Registers.X3; + long Position = (long)ThreadState.X1; + long Size = (long)ThreadState.X2; + int Perm = (int)ThreadState.X3; AMemoryMapInfo MapInfo = Memory.Manager.GetMapInfo(Position); @@ -117,8 +114,8 @@ namespace Ryujinx.OsHle.Svc int Handle = Ns.Os.Handles.GenerateId(HndData); - Registers.X1 = (ulong)Handle; - Registers.X0 = (int)SvcResult.Success; + ThreadState.X1 = (ulong)Handle; + ThreadState.X0 = (int)SvcResult.Success; } } } \ No newline at end of file diff --git a/Ryujinx/OsHle/Svc/SvcSystem.cs b/Ryujinx/OsHle/Svc/SvcSystem.cs index 098ddb2f6..0570ccb09 100644 --- a/Ryujinx/OsHle/Svc/SvcSystem.cs +++ b/Ryujinx/OsHle/Svc/SvcSystem.cs @@ -4,60 +4,57 @@ using Ryujinx.OsHle.Exceptions; using Ryujinx.OsHle.Handles; using Ryujinx.OsHle.Ipc; using System; +using System.Threading; namespace Ryujinx.OsHle.Svc { partial class SvcHandler { - private void SvcCloseHandle(ARegisters Registers) + private void SvcExitProcess(AThreadState ThreadState) => Ns.Os.ExitProcess(ThreadState.ProcessId); + + private void SvcCloseHandle(AThreadState ThreadState) { - int Handle = (int)Registers.X0; + int Handle = (int)ThreadState.X0; Ns.Os.CloseHandle(Handle); - Registers.X0 = (int)SvcResult.Success; + ThreadState.X0 = (int)SvcResult.Success; } - private void SvcResetSignal(ARegisters Registers) + private void SvcResetSignal(AThreadState ThreadState) { - int Handle = (int)Registers.X0; + int Handle = (int)ThreadState.X0; //TODO: Implement events. - Registers.X0 = (int)SvcResult.Success; + ThreadState.X0 = (int)SvcResult.Success; } - private void SvcWaitSynchronization(ARegisters Registers) + private void SvcWaitSynchronization(AThreadState ThreadState) { - long HandlesPtr = (long)Registers.X0; - int HandlesCount = (int)Registers.X2; - long Timeout = (long)Registers.X3; + long HandlesPtr = (long)ThreadState.X0; + int HandlesCount = (int)ThreadState.X2; + long Timeout = (long)ThreadState.X3; //TODO: Implement events. - //Logging.Info($"SvcWaitSynchronization Thread {Registers.ThreadId}"); + HThread CurrThread = Process.GetThread(ThreadState.Tpidr); - if (Process.TryGetThread(Registers.Tpidr, out HThread Thread)) - { - Process.Scheduler.Yield(Thread); - } - else - { - Logging.Error($"Thread with TPIDR_EL0 0x{Registers.Tpidr:x16} not found!"); - } + Process.Scheduler.Suspend(CurrThread.ProcessorId); + Process.Scheduler.Resume(CurrThread); - Registers.X0 = (int)SvcResult.Success; + ThreadState.X0 = (int)SvcResult.Success; } - private void SvcGetSystemTick(ARegisters Registers) + private void SvcGetSystemTick(AThreadState ThreadState) { - Registers.X0 = (ulong)Registers.CntpctEl0; + ThreadState.X0 = (ulong)ThreadState.CntpctEl0; } - private void SvcConnectToNamedPort(ARegisters Registers) + private void SvcConnectToNamedPort(AThreadState ThreadState) { - long StackPtr = (long)Registers.X0; - long NamePtr = (long)Registers.X1; + long StackPtr = (long)ThreadState.X0; + long NamePtr = (long)ThreadState.X1; string Name = AMemoryHelper.ReadAsciiString(Memory, NamePtr, 8); @@ -66,37 +63,41 @@ namespace Ryujinx.OsHle.Svc HSession Session = new HSession(Name); - Registers.X1 = (ulong)Ns.Os.Handles.GenerateId(Session); - Registers.X0 = (int)SvcResult.Success; + ThreadState.X1 = (ulong)Ns.Os.Handles.GenerateId(Session); + ThreadState.X0 = (int)SvcResult.Success; } - private void SvcSendSyncRequest(ARegisters Registers) + private void SvcSendSyncRequest(AThreadState ThreadState) { - SendSyncRequest(Registers, false); + SendSyncRequest(ThreadState, false); } - private void SvcSendSyncRequestWithUserBuffer(ARegisters Registers) + private void SvcSendSyncRequestWithUserBuffer(AThreadState ThreadState) { - SendSyncRequest(Registers, true); + SendSyncRequest(ThreadState, true); } - private void SendSyncRequest(ARegisters Registers, bool UserBuffer) + private void SendSyncRequest(AThreadState ThreadState, bool UserBuffer) { - long CmdPtr = Registers.Tpidr; + long CmdPtr = ThreadState.Tpidr; long Size = 0x100; int Handle = 0; if (UserBuffer) { - CmdPtr = (long)Registers.X0; - Size = (long)Registers.X1; - Handle = (int)Registers.X2; + CmdPtr = (long)ThreadState.X0; + Size = (long)ThreadState.X1; + Handle = (int)ThreadState.X2; } else { - Handle = (int)Registers.X0; + Handle = (int)ThreadState.X0; } + HThread CurrThread = Process.GetThread(ThreadState.Tpidr); + + Process.Scheduler.Suspend(CurrThread.ProcessorId); + byte[] CmdData = AMemoryHelper.ReadBytes(Memory, CmdPtr, (int)Size); HSession Session = Ns.Os.Handles.GetData(Handle); @@ -109,69 +110,73 @@ namespace Ryujinx.OsHle.Svc byte[] Response = AMemoryHelper.ReadBytes(Memory, CmdPtr, (int)Size); - Registers.X0 = (int)SvcResult.Success; + ThreadState.X0 = (int)SvcResult.Success; } else { - Registers.X0 = (int)SvcResult.ErrBadIpcReq; + ThreadState.X0 = (int)SvcResult.ErrBadIpcReq; } + + Thread.Yield(); + + Process.Scheduler.Resume(CurrThread); } - private void SvcBreak(ARegisters Registers) + private void SvcBreak(AThreadState ThreadState) { - long Reason = (long)Registers.X0; - long Unknown = (long)Registers.X1; - long Info = (long)Registers.X2; + long Reason = (long)ThreadState.X0; + long Unknown = (long)ThreadState.X1; + long Info = (long)ThreadState.X2; throw new GuestBrokeExecutionException(); } - private void SvcOutputDebugString(ARegisters Registers) + private void SvcOutputDebugString(AThreadState ThreadState) { - long Position = (long)Registers.X0; - long Size = (long)Registers.X1; + long Position = (long)ThreadState.X0; + long Size = (long)ThreadState.X1; string Str = AMemoryHelper.ReadAsciiString(Memory, Position, (int)Size); Logging.Info($"SvcOutputDebugString: {Str}"); - Registers.X0 = (int)SvcResult.Success; + ThreadState.X0 = (int)SvcResult.Success; } - private void SvcGetInfo(ARegisters Registers) + private void SvcGetInfo(AThreadState ThreadState) { - long StackPtr = (long)Registers.X0; - int InfoType = (int)Registers.X1; - long Handle = (long)Registers.X2; - int InfoId = (int)Registers.X3; + long StackPtr = (long)ThreadState.X0; + int InfoType = (int)ThreadState.X1; + long Handle = (long)ThreadState.X2; + int InfoId = (int)ThreadState.X3; //Fail for info not available on older Kernel versions. if (InfoType == 18 || InfoType == 19) { - Registers.X0 = (int)SvcResult.ErrBadInfo; + ThreadState.X0 = (int)SvcResult.ErrBadInfo; return; } switch (InfoType) { - case 2: Registers.X1 = GetMapRegionBaseAddr(); break; - case 3: Registers.X1 = GetMapRegionSize(); break; - case 4: Registers.X1 = GetHeapRegionBaseAddr(); break; - case 5: Registers.X1 = GetHeapRegionSize(); break; - case 6: Registers.X1 = GetTotalMem(); break; - case 7: Registers.X1 = GetUsedMem(); break; - case 11: Registers.X1 = GetRnd64(); break; - case 12: Registers.X1 = GetAddrSpaceBaseAddr(); break; - case 13: Registers.X1 = GetAddrSpaceSize(); break; - case 14: Registers.X1 = GetMapRegionBaseAddr(); break; - case 15: Registers.X1 = GetMapRegionSize(); break; + case 2: ThreadState.X1 = GetMapRegionBaseAddr(); break; + case 3: ThreadState.X1 = GetMapRegionSize(); break; + case 4: ThreadState.X1 = GetHeapRegionBaseAddr(); break; + case 5: ThreadState.X1 = GetHeapRegionSize(); break; + case 6: ThreadState.X1 = GetTotalMem(); break; + case 7: ThreadState.X1 = GetUsedMem(); break; + case 11: ThreadState.X1 = GetRnd64(); break; + case 12: ThreadState.X1 = GetAddrSpaceBaseAddr(); break; + case 13: ThreadState.X1 = GetAddrSpaceSize(); break; + case 14: ThreadState.X1 = GetMapRegionBaseAddr(); break; + case 15: ThreadState.X1 = GetMapRegionSize(); break; default: throw new NotImplementedException($"SvcGetInfo: {InfoType} {Handle} {InfoId}"); } - Registers.X0 = (int)SvcResult.Success; + ThreadState.X0 = (int)SvcResult.Success; } private ulong GetTotalMem() diff --git a/Ryujinx/OsHle/Svc/SvcThread.cs b/Ryujinx/OsHle/Svc/SvcThread.cs index ac1ba7457..cc2bbb1eb 100644 --- a/Ryujinx/OsHle/Svc/SvcThread.cs +++ b/Ryujinx/OsHle/Svc/SvcThread.cs @@ -1,23 +1,23 @@ using ChocolArm64.State; using Ryujinx.OsHle.Handles; -using System.Threading; namespace Ryujinx.OsHle.Svc { partial class SvcHandler { - private void SvcCreateThread(ARegisters Registers) + private void SvcCreateThread(AThreadState ThreadState) { - long EntryPoint = (long)Registers.X1; - long ArgsPtr = (long)Registers.X2; - long StackTop = (long)Registers.X3; - int Priority = (int)Registers.X4; - int ProcessorId = (int)Registers.X5; + long EntryPoint = (long)ThreadState.X1; + long ArgsPtr = (long)ThreadState.X2; + long StackTop = (long)ThreadState.X3; + int Priority = (int)ThreadState.X4; + int ProcessorId = (int)ThreadState.X5; - if (Ns.Os.TryGetProcess(Registers.ProcessId, out Process Process)) + if (Ns.Os.TryGetProcess(ThreadState.ProcessId, out Process Process)) { if (ProcessorId == -2) { + //TODO: Get this value from the NPDM file. ProcessorId = 0; } @@ -28,16 +28,16 @@ namespace Ryujinx.OsHle.Svc Priority, ProcessorId); - Registers.X0 = (int)SvcResult.Success; - Registers.X1 = (ulong)Handle; + ThreadState.X0 = (int)SvcResult.Success; + ThreadState.X1 = (ulong)Handle; } //TODO: Error codes. } - private void SvcStartThread(ARegisters Registers) + private void SvcStartThread(AThreadState ThreadState) { - int Handle = (int)Registers.X0; + int Handle = (int)ThreadState.X0; HThread Thread = Ns.Os.Handles.GetData(Handle); @@ -45,38 +45,38 @@ namespace Ryujinx.OsHle.Svc { Process.Scheduler.StartThread(Thread); - Registers.X0 = (int)SvcResult.Success; + ThreadState.X0 = (int)SvcResult.Success; } //TODO: Error codes. } - private void SvcSleepThread(ARegisters Registers) + private void SvcSleepThread(AThreadState ThreadState) { - ulong NanoSecs = Registers.X0; + ulong NanoSecs = ThreadState.X0; - if (Process.TryGetThread(Registers.Tpidr, out HThread CurrThread)) + HThread CurrThread = Process.GetThread(ThreadState.Tpidr); + + if (NanoSecs == 0) { Process.Scheduler.Yield(CurrThread); } else { - Logging.Error($"Thread with TPIDR_EL0 0x{Registers.Tpidr:x16} not found!"); + Process.Scheduler.WaitForSignal(CurrThread, (int)(NanoSecs / 1000000)); } - - Thread.Sleep((int)(NanoSecs / 1000000)); } - private void SvcGetThreadPriority(ARegisters Registers) + private void SvcGetThreadPriority(AThreadState ThreadState) { - int Handle = (int)Registers.X1; + int Handle = (int)ThreadState.X1; HThread Thread = Ns.Os.Handles.GetData(Handle); if (Thread != null) { - Registers.X1 = (ulong)Thread.Priority; - Registers.X0 = (int)SvcResult.Success; + ThreadState.X1 = (ulong)Thread.Priority; + ThreadState.X0 = (int)SvcResult.Success; } //TODO: Error codes. diff --git a/Ryujinx/OsHle/Svc/SvcThreadSync.cs b/Ryujinx/OsHle/Svc/SvcThreadSync.cs index 96681df1c..f342f51d5 100644 --- a/Ryujinx/OsHle/Svc/SvcThreadSync.cs +++ b/Ryujinx/OsHle/Svc/SvcThreadSync.cs @@ -1,4 +1,3 @@ -using ChocolArm64; using ChocolArm64.State; using Ryujinx.OsHle.Handles; @@ -6,11 +5,11 @@ namespace Ryujinx.OsHle.Svc { partial class SvcHandler { - private void SvcArbitrateLock(ARegisters Registers) + private void SvcArbitrateLock(AThreadState ThreadState) { - int OwnerThreadHandle = (int)Registers.X0; - long MutexAddress = (long)Registers.X1; - int RequestingThreadHandle = (int)Registers.X2; + int OwnerThreadHandle = (int)ThreadState.X0; + long MutexAddress = (long)ThreadState.X1; + int RequestingThreadHandle = (int)ThreadState.X2; HThread RequestingThread = Ns.Os.Handles.GetData(RequestingThreadHandle); @@ -20,34 +19,35 @@ namespace Ryujinx.OsHle.Svc M.WaitForLock(RequestingThread, RequestingThreadHandle); - Registers.X0 = (int)SvcResult.Success; + ThreadState.X0 = (int)SvcResult.Success; } - private void SvcArbitrateUnlock(ARegisters Registers) + private void SvcArbitrateUnlock(AThreadState ThreadState) { - long MutexAddress = (long)Registers.X0; + long MutexAddress = (long)ThreadState.X0; if (Ns.Os.Mutexes.TryGetValue(MutexAddress, out Mutex M)) { M.Unlock(); } - Registers.X0 = (int)SvcResult.Success; + ThreadState.X0 = (int)SvcResult.Success; } - private void SvcWaitProcessWideKeyAtomic(ARegisters Registers) + private void SvcWaitProcessWideKeyAtomic(AThreadState ThreadState) { - long MutexAddress = (long)Registers.X0; - long CondVarAddress = (long)Registers.X1; - int ThreadHandle = (int)Registers.X2; - long Timeout = (long)Registers.X3; + long MutexAddress = (long)ThreadState.X0; + long CondVarAddress = (long)ThreadState.X1; + int ThreadHandle = (int)ThreadState.X2; + long Timeout = (long)ThreadState.X3; HThread Thread = Ns.Os.Handles.GetData(ThreadHandle); - if (Ns.Os.Mutexes.TryGetValue(MutexAddress, out Mutex M)) - { - M.GiveUpLock(ThreadHandle); - } + Mutex M = new Mutex(Process, MutexAddress, ThreadHandle); + + M = Ns.Os.Mutexes.GetOrAdd(MutexAddress, M); + + M.GiveUpLock(ThreadHandle); CondVar Cv = new CondVar(Process, CondVarAddress, Timeout); @@ -55,26 +55,24 @@ namespace Ryujinx.OsHle.Svc Cv.WaitForSignal(Thread); - M = new Mutex(Process, MutexAddress, ThreadHandle); - - M = Ns.Os.Mutexes.GetOrAdd(MutexAddress, M); - M.WaitForLock(Thread, ThreadHandle); - Registers.X0 = (int)SvcResult.Success; + ThreadState.X0 = (int)SvcResult.Success; } - private void SvcSignalProcessWideKey(ARegisters Registers) + private void SvcSignalProcessWideKey(AThreadState ThreadState) { - long CondVarAddress = (long)Registers.X0; - int Count = (int)Registers.X1; + long CondVarAddress = (long)ThreadState.X0; + int Count = (int)ThreadState.X1; + + HThread CurrThread = Process.GetThread(ThreadState.Tpidr); if (Ns.Os.CondVars.TryGetValue(CondVarAddress, out CondVar Cv)) { - Cv.SetSignal(Count); + Cv.SetSignal(CurrThread, Count); } - Registers.X0 = (int)SvcResult.Success; + ThreadState.X0 = (int)SvcResult.Success; } } } \ No newline at end of file diff --git a/Ryujinx/Ryujinx.conf b/Ryujinx/Ryujinx.conf new file mode 100644 index 000000000..8f791df31 --- /dev/null +++ b/Ryujinx/Ryujinx.conf @@ -0,0 +1,47 @@ +#Enabled print informations logs +Logging_Enable_Info = true + +#Enabled print trace logs +Logging_Enable_Trace = false + +#Enabled print debug logs +Logging_Enable_Debug = false + +#Enabled print warning logs +Logging_Enable_Warn = true + +#Enabled print error logs +Logging_Enable_Error = true + +#Enabled print fatal logs +Logging_Enable_Fatal = true + +#Saved logs into Ryujinx.log +Logging_Enable_LogFile = false + +#https://github.com/opentk/opentk/blob/develop/src/OpenTK/Input/Key.cs +Controls_Left_FakeJoycon_Stick_Up = 91 +Controls_Left_FakeJoycon_Stick_Down = 93 +Controls_Left_FakeJoycon_Stick_Left = 92 +Controls_Left_FakeJoycon_Stick_Right = 94 +Controls_Left_FakeJoycon_Stick_Button = 0 +Controls_Left_FakeJoycon_DPad_Up = 45 +Controls_Left_FakeJoycon_DPad_Down = 46 +Controls_Left_FakeJoycon_DPad_Left = 47 +Controls_Left_FakeJoycon_DPad_Right = 48 +Controls_Left_FakeJoycon_Button_Minus = 52 +Controls_Left_FakeJoycon_Button_L = 0 +Controls_Left_FakeJoycon_Button_ZL = 0 + +Controls_Right_FakeJoycon_Stick_Up = 45 +Controls_Right_FakeJoycon_Stick_Down = 46 +Controls_Right_FakeJoycon_Stick_Left = 47 +Controls_Right_FakeJoycon_Stick_Right = 48 +Controls_Right_FakeJoycon_Stick_Button = 0 +Controls_Right_FakeJoycon_Button_A = 83 +Controls_Right_FakeJoycon_Button_B = 101 +Controls_Right_FakeJoycon_Button_X = 106 +Controls_Right_FakeJoycon_Button_Y = 108 +Controls_Right_FakeJoycon_Button_Plus = 49 +Controls_Right_FakeJoycon_Button_R = 0 +Controls_Right_FakeJoycon_Button_ZR = 0 diff --git a/Ryujinx.csproj b/Ryujinx/Ryujinx.csproj similarity index 89% rename from Ryujinx.csproj rename to Ryujinx/Ryujinx.csproj index 9ad696d89..9b0e7396a 100644 --- a/Ryujinx.csproj +++ b/Ryujinx/Ryujinx.csproj @@ -2,8 +2,8 @@ Exe netcoreapp2.0 - win10-x64 true + win10-x64;osx-x64 diff --git a/Ryujinx/Switch.cs b/Ryujinx/Switch.cs index 9e5ea7d06..b5874051e 100644 --- a/Ryujinx/Switch.cs +++ b/Ryujinx/Switch.cs @@ -14,6 +14,9 @@ namespace Ryujinx internal NsGpu Gpu { get; private set; } internal Horizon Os { get; private set; } internal VirtualFs VFs { get; private set; } + internal Hid Hid { get; private set; } + + public event EventHandler Finish; public Switch(IGalRenderer Renderer) { @@ -22,6 +25,15 @@ namespace Ryujinx Gpu = new NsGpu(Renderer); Os = new Horizon(this); VFs = new VirtualFs(); + Hid = new Hid(this); + } + + internal virtual void OnFinish(EventArgs e) + { + if (Finish != null) + { + Finish(this, e); + } } public void Dispose() diff --git a/GLScreen.cs b/Ryujinx/Ui/GLScreen.cs similarity index 70% rename from GLScreen.cs rename to Ryujinx/Ui/GLScreen.cs index cd650f2c6..0f03d4a0f 100644 --- a/GLScreen.cs +++ b/Ryujinx/Ui/GLScreen.cs @@ -6,7 +6,6 @@ using Gal; using OpenTK; using OpenTK.Graphics; using OpenTK.Graphics.OpenGL; -using Ryujinx.OsHle; using System; namespace Ryujinx @@ -276,76 +275,62 @@ void main(void) { protected override void OnUpdateFrame(FrameEventArgs e) { - unsafe + HidControllerKeys CurrentButton = 0; + JoystickPosition LeftJoystick; + JoystickPosition RightJoystick; + + if (Keyboard[OpenTK.Input.Key.Escape]) this.Exit(); + + //RightJoystick + int LeftJoystickDX = 0; + int LeftJoystickDY = 0; + if (Keyboard[(OpenTK.Input.Key)Config.FakeJoyCon.Left.StickUp]) LeftJoystickDY = short.MaxValue; + if (Keyboard[(OpenTK.Input.Key)Config.FakeJoyCon.Left.StickDown]) LeftJoystickDY = -short.MaxValue; + if (Keyboard[(OpenTK.Input.Key)Config.FakeJoyCon.Left.StickLeft]) LeftJoystickDX = -short.MaxValue; + if (Keyboard[(OpenTK.Input.Key)Config.FakeJoyCon.Left.StickRight]) LeftJoystickDX = short.MaxValue; + if (Keyboard[(OpenTK.Input.Key)Config.FakeJoyCon.Left.StickButton]) CurrentButton |= HidControllerKeys.KEY_LSTICK; + + //LeftButtons + if (Keyboard[(OpenTK.Input.Key)Config.FakeJoyCon.Left.DPadUp]) CurrentButton |= HidControllerKeys.KEY_DUP; + if (Keyboard[(OpenTK.Input.Key)Config.FakeJoyCon.Left.DPadDown]) CurrentButton |= HidControllerKeys.KEY_DDOWN; + if (Keyboard[(OpenTK.Input.Key)Config.FakeJoyCon.Left.DPadLeft]) CurrentButton |= HidControllerKeys.KEY_DLEFT; + if (Keyboard[(OpenTK.Input.Key)Config.FakeJoyCon.Left.DPadRight]) CurrentButton |= HidControllerKeys.KEY_DRIGHT; + if (Keyboard[(OpenTK.Input.Key)Config.FakeJoyCon.Left.ButtonMinus]) CurrentButton |= HidControllerKeys.KEY_MINUS; + if (Keyboard[(OpenTK.Input.Key)Config.FakeJoyCon.Left.ButtonL]) CurrentButton |= HidControllerKeys.KEY_L; + if (Keyboard[(OpenTK.Input.Key)Config.FakeJoyCon.Left.ButtonZL]) CurrentButton |= HidControllerKeys.KEY_ZL; + + //RightJoystick + int RightJoystickDX = 0; + int RightJoystickDY = 0; + if (Keyboard[(OpenTK.Input.Key)Config.FakeJoyCon.Right.StickUp]) RightJoystickDY = short.MaxValue; + if (Keyboard[(OpenTK.Input.Key)Config.FakeJoyCon.Right.StickDown]) RightJoystickDY = -short.MaxValue; + if (Keyboard[(OpenTK.Input.Key)Config.FakeJoyCon.Right.StickLeft]) RightJoystickDX = -short.MaxValue; + if (Keyboard[(OpenTK.Input.Key)Config.FakeJoyCon.Right.StickRight]) RightJoystickDX = short.MaxValue; + if (Keyboard[(OpenTK.Input.Key)Config.FakeJoyCon.Right.StickButton]) CurrentButton |= HidControllerKeys.KEY_RSTICK; + + //RightButtons + if (Keyboard[(OpenTK.Input.Key)Config.FakeJoyCon.Right.ButtonA]) CurrentButton |= HidControllerKeys.KEY_A; + if (Keyboard[(OpenTK.Input.Key)Config.FakeJoyCon.Right.ButtonB]) CurrentButton |= HidControllerKeys.KEY_B; + if (Keyboard[(OpenTK.Input.Key)Config.FakeJoyCon.Right.ButtonX]) CurrentButton |= HidControllerKeys.KEY_X; + if (Keyboard[(OpenTK.Input.Key)Config.FakeJoyCon.Right.ButtonY]) CurrentButton |= HidControllerKeys.KEY_Y; + if (Keyboard[(OpenTK.Input.Key)Config.FakeJoyCon.Right.ButtonPlus]) CurrentButton |= HidControllerKeys.KEY_PLUS; + if (Keyboard[(OpenTK.Input.Key)Config.FakeJoyCon.Right.ButtonR]) CurrentButton |= HidControllerKeys.KEY_R; + if (Keyboard[(OpenTK.Input.Key)Config.FakeJoyCon.Right.ButtonZR]) CurrentButton |= HidControllerKeys.KEY_ZR; + + LeftJoystick = new JoystickPosition { - long HidOffset = Ns.Os.GetVirtHidOffset(); + DX = LeftJoystickDX, + DY = LeftJoystickDY + }; - if (HidOffset == 0 || HidOffset + Horizon.HidSize > uint.MaxValue) - { - return; - } - - byte* Ptr = (byte*)Ns.Ram + (uint)HidOffset; - - int State = 0; - - if (Keyboard[OpenTK.Input.Key.Up]) - { - State |= 0x2000; - } - - if (Keyboard[OpenTK.Input.Key.Down]) - { - State |= 0x8000; - } - - if (Keyboard[OpenTK.Input.Key.Left]) - { - State |= 0x1000; - } - - if (Keyboard[OpenTK.Input.Key.Right]) - { - State |= 0x4000; - } - - if (Keyboard[OpenTK.Input.Key.A]) - { - State |= 0x1; - } - - if (Keyboard[OpenTK.Input.Key.S]) - { - State |= 0x2; - } - - if (Keyboard[OpenTK.Input.Key.Z]) - { - State |= 0x4; - } - - if (Keyboard[OpenTK.Input.Key.X]) - { - State |= 0x8; - } - - if (Keyboard[OpenTK.Input.Key.Enter]) - { - State |= 0x400; - } - - if (Keyboard[OpenTK.Input.Key.Tab]) - { - State |= 0x800; - } - - *((int*)(Ptr + 0xae38)) = (int)State; - } - - if (Keyboard[OpenTK.Input.Key.Escape]) + RightJoystick = new JoystickPosition { - this.Exit(); - } + DX = RightJoystickDX, + DY = RightJoystickDY + }; + + //We just need one pair of JoyCon because it's emulate by the keyboard. + Ns.Hid.SendControllerButtons(HidControllerID.CONTROLLER_HANDHELD, HidControllerLayouts.Main, CurrentButton, LeftJoystick, RightJoystick); } protected override void OnRenderFrame(FrameEventArgs e) diff --git a/Program.cs b/Ryujinx/Ui/Program.cs similarity index 92% rename from Program.cs rename to Ryujinx/Ui/Program.cs index 3d4481aa5..2f29411a9 100644 --- a/Program.cs +++ b/Ryujinx/Ui/Program.cs @@ -50,6 +50,11 @@ namespace Ryujinx using (GLScreen Screen = new GLScreen(Ns, Renderer)) { + Ns.Finish += (Sender, Args) => + { + Screen.Exit(); + }; + Screen.Run(60.0); }