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);
}