commit
33cb70b5a1
76 changed files with 4582 additions and 3752 deletions
9
.travis.yml
Normal file
9
.travis.yml
Normal file
|
@ -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
|
75
CONFIG.md
Normal file
75
CONFIG.md
Normal file
|
@ -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).
|
44
README.md
44
README.md
|
@ -21,37 +21,23 @@ https://openal.org/downloads/OpenAL11CoreSDK.zip
|
||||||
- Arrows.
|
- Arrows.
|
||||||
- Enter > "Start" & Tab > "Select"
|
- Enter > "Start" & Tab > "Select"
|
||||||
- Qwerty:
|
- Qwerty:
|
||||||
- A > "A"
|
- A > "A" (QWERTY) / Q > "A" (AZERTY)
|
||||||
- S > "B"
|
- S > "B"
|
||||||
- Z > "X"
|
- Z > "X" (QWERTY) / W > "X" (AZERTY)
|
||||||
- X > "Y"
|
|
||||||
- Azerty:
|
|
||||||
- Q > "A"
|
|
||||||
- S > "B"
|
|
||||||
- W > "X"
|
|
||||||
- X > "Y"
|
- 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.
|
- Config File: `Ryujinx.conf` should be present in executable folder.
|
||||||
- Logging_Enable_Info (bool)
|
For more informations [you can go here](CONFIG.md).
|
||||||
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.
|
|
||||||
|
|
||||||
**Help**
|
**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\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!).
|
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
|
https://openal.org/downloads/OpenAL11CoreSDK.zip
|
||||||
|
|
||||||
**Lastest build**
|
**Lastest build**
|
||||||
|
|
73
Ryujinx.Tests/Cpu/CpuTest.cs
Normal file
73
Ryujinx.Tests/Cpu/CpuTest.cs
Normal file
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
65
Ryujinx.Tests/Cpu/CpuTestAlu.cs
Normal file
65
Ryujinx.Tests/Cpu/CpuTestAlu.cs
Normal file
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
30
Ryujinx.Tests/Cpu/CpuTestScalar.cs
Normal file
30
Ryujinx.Tests/Cpu/CpuTestScalar.cs
Normal file
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
17
Ryujinx.Tests/Ryujinx.Tests.csproj
Normal file
17
Ryujinx.Tests/Ryujinx.Tests.csproj
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFramework>netcoreapp2.0</TargetFramework>
|
||||||
|
<IsPackable>false</IsPackable>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup>
|
||||||
|
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
|
||||||
|
</PropertyGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.3.0-preview-20170628-02" />
|
||||||
|
<PackageReference Include="NUnit" Version="3.9.0" />
|
||||||
|
<PackageReference Include="NUnit3TestAdapter" Version="3.9.0" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\Ryujinx\Ryujinx.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
</Project>
|
20
Ryujinx.conf
20
Ryujinx.conf
|
@ -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
|
|
|
@ -3,7 +3,9 @@ Microsoft Visual Studio Solution File, Format Version 12.00
|
||||||
# Visual Studio 15
|
# Visual Studio 15
|
||||||
VisualStudioVersion = 15.0.26730.8
|
VisualStudioVersion = 15.0.26730.8
|
||||||
MinimumVisualStudioVersion = 10.0.40219.1
|
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
|
EndProject
|
||||||
Global
|
Global
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
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}.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.ActiveCfg = Release|Any CPU
|
||||||
{074045D4-3ED2-4711-9169-E385F2BFB5A0}.Release|Any CPU.Build.0 = 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
|
EndGlobalSection
|
||||||
GlobalSection(SolutionProperties) = preSolution
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
HideSolutionNode = FALSE
|
HideSolutionNode = FALSE
|
||||||
|
|
|
@ -16,6 +16,8 @@ namespace Ryujinx
|
||||||
public static bool LoggingEnableFatal { get; private set; }
|
public static bool LoggingEnableFatal { get; private set; }
|
||||||
public static bool LoggingEnableLogFile { get; private set; }
|
public static bool LoggingEnableLogFile { get; private set; }
|
||||||
|
|
||||||
|
public static JoyCon FakeJoyCon { get; private set; }
|
||||||
|
|
||||||
public static void Read()
|
public static void Read()
|
||||||
{
|
{
|
||||||
IniParser Parser = new IniParser(Path.Combine(Path.GetDirectoryName(Assembly.GetEntryAssembly().Location), "Ryujinx.conf"));
|
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"));
|
LoggingEnableError = Convert.ToBoolean(Parser.Value("Logging_Enable_Error"));
|
||||||
LoggingEnableFatal = Convert.ToBoolean(Parser.Value("Logging_Enable_Fatal"));
|
LoggingEnableFatal = Convert.ToBoolean(Parser.Value("Logging_Enable_Fatal"));
|
||||||
LoggingEnableLogFile = Convert.ToBoolean(Parser.Value("Logging_Enable_LogFile"));
|
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"))
|
||||||
|
}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -139,8 +139,8 @@ namespace ChocolArm64
|
||||||
Set("0>101110<<100000100110xxxxxxxxxx", AInstEmit.Cmle_V, typeof(AOpCodeSimd));
|
Set("0>101110<<100000100110xxxxxxxxxx", AInstEmit.Cmle_V, typeof(AOpCodeSimd));
|
||||||
Set("0>001110<<100000101010xxxxxxxxxx", AInstEmit.Cmlt_V, typeof(AOpCodeSimd));
|
Set("0>001110<<100000101010xxxxxxxxxx", AInstEmit.Cmlt_V, typeof(AOpCodeSimd));
|
||||||
Set("0x00111000100000010110xxxxxxxxxx", AInstEmit.Cnt_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("0x001110000xxxxx000011xxxxxxxxxx", AInstEmit.Dup_Gp, typeof(AOpCodeSimdIns));
|
||||||
|
Set("01011110000xxxxx000001xxxxxxxxxx", AInstEmit.Dup_S, typeof(AOpCodeSimdIns));
|
||||||
Set("0x001110000xxxxx000001xxxxxxxxxx", AInstEmit.Dup_V, typeof(AOpCodeSimdIns));
|
Set("0x001110000xxxxx000001xxxxxxxxxx", AInstEmit.Dup_V, typeof(AOpCodeSimdIns));
|
||||||
Set("0x101110001xxxxx000111xxxxxxxxxx", AInstEmit.Eor_V, typeof(AOpCodeSimdReg));
|
Set("0x101110001xxxxx000111xxxxxxxxxx", AInstEmit.Eor_V, typeof(AOpCodeSimdReg));
|
||||||
Set("00011110xx100000110000xxxxxxxxxx", AInstEmit.Fabs_S, typeof(AOpCodeSimd));
|
Set("00011110xx100000110000xxxxxxxxxx", AInstEmit.Fabs_S, typeof(AOpCodeSimd));
|
||||||
|
@ -151,14 +151,16 @@ namespace ChocolArm64
|
||||||
Set("00011110xx1xxxxx001000xxxxx1x000", AInstEmit.Fcmpe_S, typeof(AOpCodeSimdReg));
|
Set("00011110xx1xxxxx001000xxxxx1x000", AInstEmit.Fcmpe_S, typeof(AOpCodeSimdReg));
|
||||||
Set("00011110xx1xxxxxxxxx11xxxxxxxxxx", AInstEmit.Fcsel_S, typeof(AOpCodeSimdFcond));
|
Set("00011110xx1xxxxxxxxx11xxxxxxxxxx", AInstEmit.Fcsel_S, typeof(AOpCodeSimdFcond));
|
||||||
Set("00011110xx10001xx10000xxxxxxxxxx", AInstEmit.Fcvt_S, typeof(AOpCodeSimd));
|
Set("00011110xx10001xx10000xxxxxxxxxx", AInstEmit.Fcvt_S, typeof(AOpCodeSimd));
|
||||||
Set("x0011110xx110000000000xxxxxxxxxx", AInstEmit.Fcvtms_S, typeof(AOpCodeSimdCvt));
|
Set("x0011110xx100100000000xxxxxxxxxx", AInstEmit.Fcvtas_Gp, typeof(AOpCodeSimdCvt));
|
||||||
Set("x0011110xx101000000000xxxxxxxxxx", AInstEmit.Fcvtps_S, typeof(AOpCodeSimdCvt));
|
Set("x0011110xx100101000000xxxxxxxxxx", AInstEmit.Fcvtau_Gp, typeof(AOpCodeSimdCvt));
|
||||||
Set("x0011110xx111000000000xxxxxxxxxx", AInstEmit.Fcvtzs_S, typeof(AOpCodeSimdCvt));
|
Set("x0011110xx110000000000xxxxxxxxxx", AInstEmit.Fcvtms_Gp, typeof(AOpCodeSimdCvt));
|
||||||
Set("x0011110xx011000xxxxxxxxxxxxxxxx", AInstEmit.Fcvtzs_Fix, 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("0x0011101x100001101110xxxxxxxxxx", AInstEmit.Fcvtzs_V, typeof(AOpCodeSimd));
|
||||||
Set("0x0011110>>xxxxx111111xxxxxxxxxx", AInstEmit.Fcvtzs_V, typeof(AOpCodeSimdShImm));
|
Set("0x0011110>>xxxxx111111xxxxxxxxxx", AInstEmit.Fcvtzs_V, typeof(AOpCodeSimdShImm));
|
||||||
Set("x0011110xx111001000000xxxxxxxxxx", AInstEmit.Fcvtzu_S, typeof(AOpCodeSimdCvt));
|
Set("x0011110xx111001000000xxxxxxxxxx", AInstEmit.Fcvtzu_Gp, typeof(AOpCodeSimdCvt));
|
||||||
Set("x0011110xx011001xxxxxxxxxxxxxxxx", AInstEmit.Fcvtzu_Fix, typeof(AOpCodeSimdCvt));
|
Set("x0011110xx011001xxxxxxxxxxxxxxxx", AInstEmit.Fcvtzu_Gp_Fix, typeof(AOpCodeSimdCvt));
|
||||||
Set("0x1011101x100001101110xxxxxxxxxx", AInstEmit.Fcvtzu_V, typeof(AOpCodeSimd));
|
Set("0x1011101x100001101110xxxxxxxxxx", AInstEmit.Fcvtzu_V, typeof(AOpCodeSimd));
|
||||||
Set("0x1011110>>xxxxx111111xxxxxxxxxx", AInstEmit.Fcvtzu_V, typeof(AOpCodeSimdShImm));
|
Set("0x1011110>>xxxxx111111xxxxxxxxxx", AInstEmit.Fcvtzu_V, typeof(AOpCodeSimdShImm));
|
||||||
Set("00011110xx1xxxxx000110xxxxxxxxxx", AInstEmit.Fdiv_S, typeof(AOpCodeSimdReg));
|
Set("00011110xx1xxxxx000110xxxxxxxxxx", AInstEmit.Fdiv_S, typeof(AOpCodeSimdReg));
|
||||||
|
@ -168,7 +170,7 @@ namespace ChocolArm64
|
||||||
Set("00011110xx1xxxxx010110xxxxxxxxxx", AInstEmit.Fmin_S, typeof(AOpCodeSimdReg));
|
Set("00011110xx1xxxxx010110xxxxxxxxxx", AInstEmit.Fmin_S, typeof(AOpCodeSimdReg));
|
||||||
Set("00011110xx1xxxxx011110xxxxxxxxxx", AInstEmit.Fminnm_S, typeof(AOpCodeSimdReg));
|
Set("00011110xx1xxxxx011110xxxxxxxxxx", AInstEmit.Fminnm_S, typeof(AOpCodeSimdReg));
|
||||||
Set("0x0011100x1xxxxx110011xxxxxxxxxx", AInstEmit.Fmla_V, typeof(AOpCodeSimdReg));
|
Set("0x0011100x1xxxxx110011xxxxxxxxxx", AInstEmit.Fmla_V, typeof(AOpCodeSimdReg));
|
||||||
Set("0x0011111<<xxxxx0001x0xxxxxxxxxx", AInstEmit.Fmla_Vs, typeof(AOpCodeSimdRegElem));
|
Set("0x0011111<<xxxxx0001x0xxxxxxxxxx", AInstEmit.Fmla_Ve, typeof(AOpCodeSimdRegElem));
|
||||||
Set("00011110xx100000010000xxxxxxxxxx", AInstEmit.Fmov_S, typeof(AOpCodeSimd));
|
Set("00011110xx100000010000xxxxxxxxxx", AInstEmit.Fmov_S, typeof(AOpCodeSimd));
|
||||||
Set("00011110xx1xxxxxxxx100xxxxxxxxxx", AInstEmit.Fmov_Si, typeof(AOpCodeSimdFmov));
|
Set("00011110xx1xxxxxxxx100xxxxxxxxxx", AInstEmit.Fmov_Si, typeof(AOpCodeSimdFmov));
|
||||||
Set("0xx0111100000xxx111101xxxxxxxxxx", AInstEmit.Fmov_V, typeof(AOpCodeSimdImm));
|
Set("0xx0111100000xxx111101xxxxxxxxxx", AInstEmit.Fmov_V, typeof(AOpCodeSimdImm));
|
||||||
|
@ -179,7 +181,7 @@ namespace ChocolArm64
|
||||||
Set("00011111xx0xxxxx1xxxxxxxxxxxxxxx", AInstEmit.Fmsub_S, typeof(AOpCodeSimdReg));
|
Set("00011111xx0xxxxx1xxxxxxxxxxxxxxx", AInstEmit.Fmsub_S, typeof(AOpCodeSimdReg));
|
||||||
Set("00011110xx1xxxxx000010xxxxxxxxxx", AInstEmit.Fmul_S, typeof(AOpCodeSimdReg));
|
Set("00011110xx1xxxxx000010xxxxxxxxxx", AInstEmit.Fmul_S, typeof(AOpCodeSimdReg));
|
||||||
Set("0x1011100x1xxxxx110111xxxxxxxxxx", AInstEmit.Fmul_V, typeof(AOpCodeSimdReg));
|
Set("0x1011100x1xxxxx110111xxxxxxxxxx", AInstEmit.Fmul_V, typeof(AOpCodeSimdReg));
|
||||||
Set("0x0011111<<xxxxx1001x0xxxxxxxxxx", AInstEmit.Fmul_Vs, typeof(AOpCodeSimdRegElem));
|
Set("0x0011111<<xxxxx1001x0xxxxxxxxxx", AInstEmit.Fmul_Ve, typeof(AOpCodeSimdRegElem));
|
||||||
Set("00011110xx100001010000xxxxxxxxxx", AInstEmit.Fneg_S, typeof(AOpCodeSimdReg));
|
Set("00011110xx100001010000xxxxxxxxxx", AInstEmit.Fneg_S, typeof(AOpCodeSimdReg));
|
||||||
Set("00011110xx1xxxxx100010xxxxxxxxxx", AInstEmit.Fnmul_S, typeof(AOpCodeSimdReg));
|
Set("00011110xx1xxxxx100010xxxxxxxxxx", AInstEmit.Fnmul_S, typeof(AOpCodeSimdReg));
|
||||||
Set("00011110xx100110010000xxxxxxxxxx", AInstEmit.Frinta_S, typeof(AOpCodeSimd));
|
Set("00011110xx100110010000xxxxxxxxxx", AInstEmit.Frinta_S, typeof(AOpCodeSimd));
|
||||||
|
@ -201,6 +203,7 @@ namespace ChocolArm64
|
||||||
Set("xx111100x11xxxxxxxxx10xxxxxxxxxx", AInstEmit.Ldr, typeof(AOpCodeSimdMemReg));
|
Set("xx111100x11xxxxxxxxx10xxxxxxxxxx", AInstEmit.Ldr, typeof(AOpCodeSimdMemReg));
|
||||||
Set("xx011100xxxxxxxxxxxxxxxxxxxxxxxx", AInstEmit.LdrLit, typeof(AOpCodeSimdMemLit));
|
Set("xx011100xxxxxxxxxxxxxxxxxxxxxxxx", AInstEmit.LdrLit, typeof(AOpCodeSimdMemLit));
|
||||||
Set("0x001110<<1xxxxx100101xxxxxxxxxx", AInstEmit.Mla_V, typeof(AOpCodeSimdReg));
|
Set("0x001110<<1xxxxx100101xxxxxxxxxx", AInstEmit.Mla_V, typeof(AOpCodeSimdReg));
|
||||||
|
Set("0x101110<<1xxxxx100101xxxxxxxxxx", AInstEmit.Mls_V, typeof(AOpCodeSimdReg));
|
||||||
Set("0x00111100000xxx0xx001xxxxxxxxxx", AInstEmit.Movi_V, typeof(AOpCodeSimdImm));
|
Set("0x00111100000xxx0xx001xxxxxxxxxx", AInstEmit.Movi_V, typeof(AOpCodeSimdImm));
|
||||||
Set("0x00111100000xxx10x001xxxxxxxxxx", AInstEmit.Movi_V, typeof(AOpCodeSimdImm));
|
Set("0x00111100000xxx10x001xxxxxxxxxx", AInstEmit.Movi_V, typeof(AOpCodeSimdImm));
|
||||||
Set("0x00111100000xxx110x01xxxxxxxxxx", AInstEmit.Movi_V, typeof(AOpCodeSimdImm));
|
Set("0x00111100000xxx110x01xxxxxxxxxx", AInstEmit.Movi_V, typeof(AOpCodeSimdImm));
|
||||||
|
@ -243,13 +246,15 @@ namespace ChocolArm64
|
||||||
Set("01101110<<110000001110xxxxxxxxxx", AInstEmit.Uaddlv_V, typeof(AOpCodeSimd));
|
Set("01101110<<110000001110xxxxxxxxxx", AInstEmit.Uaddlv_V, typeof(AOpCodeSimd));
|
||||||
Set("0x101110<<1xxxxx000100xxxxxxxxxx", AInstEmit.Uaddw_V, typeof(AOpCodeSimdReg));
|
Set("0x101110<<1xxxxx000100xxxxxxxxxx", AInstEmit.Uaddw_V, typeof(AOpCodeSimdReg));
|
||||||
Set("x0011110xx100011000000xxxxxxxxxx", AInstEmit.Ucvtf_Gp, typeof(AOpCodeSimdCvt));
|
Set("x0011110xx100011000000xxxxxxxxxx", AInstEmit.Ucvtf_Gp, typeof(AOpCodeSimdCvt));
|
||||||
Set("011111100x100001110110xxxxxxxxxx", AInstEmit.Ucvtf_V, typeof(AOpCodeSimdReg));
|
Set("011111100x100001110110xxxxxxxxxx", AInstEmit.Ucvtf_S, typeof(AOpCodeSimd));
|
||||||
|
Set("0x1011100x100001110110xxxxxxxxxx", AInstEmit.Ucvtf_V, typeof(AOpCodeSimd));
|
||||||
Set("0x001110000xxxxx001111xxxxxxxxxx", AInstEmit.Umov_S, typeof(AOpCodeSimdIns));
|
Set("0x001110000xxxxx001111xxxxxxxxxx", AInstEmit.Umov_S, typeof(AOpCodeSimdIns));
|
||||||
Set("0>101110<<1xxxxx010001xxxxxxxxxx", AInstEmit.Ushl_V, typeof(AOpCodeSimdReg));
|
Set("0>101110<<1xxxxx010001xxxxxxxxxx", AInstEmit.Ushl_V, typeof(AOpCodeSimdReg));
|
||||||
Set("0x10111100>>>xxx101001xxxxxxxxxx", AInstEmit.Ushll_V, typeof(AOpCodeSimdShImm));
|
Set("0x10111100>>>xxx101001xxxxxxxxxx", AInstEmit.Ushll_V, typeof(AOpCodeSimdShImm));
|
||||||
Set("0x1011110>>>>xxx000001xxxxxxxxxx", AInstEmit.Ushr_V, typeof(AOpCodeSimdShImm));
|
Set("0x1011110>>>>xxx000001xxxxxxxxxx", AInstEmit.Ushr_V, typeof(AOpCodeSimdShImm));
|
||||||
Set("0x1011110>>>>xxx000101xxxxxxxxxx", AInstEmit.Usra_V, typeof(AOpCodeSimdShImm));
|
Set("0x1011110>>>>xxx000101xxxxxxxxxx", AInstEmit.Usra_V, typeof(AOpCodeSimdShImm));
|
||||||
Set("0x001110xx0xxxxx000110xxxxxxxxxx", AInstEmit.Uzp1_V, typeof(AOpCodeSimdReg));
|
Set("0x001110xx0xxxxx000110xxxxxxxxxx", AInstEmit.Uzp1_V, typeof(AOpCodeSimdReg));
|
||||||
|
Set("0x001110xx0xxxxx010110xxxxxxxxxx", AInstEmit.Uzp2_V, typeof(AOpCodeSimdReg));
|
||||||
Set("0x001110<<100001001010xxxxxxxxxx", AInstEmit.Xtn_V, typeof(AOpCodeSimd));
|
Set("0x001110<<100001001010xxxxxxxxxx", AInstEmit.Xtn_V, typeof(AOpCodeSimd));
|
||||||
#endregion
|
#endregion
|
||||||
}
|
}
|
||||||
|
@ -317,7 +322,7 @@ namespace ChocolArm64
|
||||||
}
|
}
|
||||||
else if (ZCount != 0 && OCount != 0)
|
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, ><<
|
//with > indicating 0, and < indicating 1. So, for example, ><<
|
||||||
//blacklists the pattern 011, but 000, 001, 010, 100, 101,
|
//blacklists the pattern 011, but 000, 001, 010, 100, 101,
|
||||||
//110 and 111 are valid.
|
//110 and 111 are valid.
|
||||||
|
|
|
@ -5,10 +5,10 @@ using System.Threading;
|
||||||
|
|
||||||
namespace ChocolArm64
|
namespace ChocolArm64
|
||||||
{
|
{
|
||||||
class AThread
|
public class AThread
|
||||||
{
|
{
|
||||||
public ARegisters Registers { get; private set; }
|
public AThreadState ThreadState { get; private set; }
|
||||||
public AMemory Memory { get; private set; }
|
public AMemory Memory { get; private set; }
|
||||||
|
|
||||||
public long EntryPoint { get; private set; }
|
public long EntryPoint { get; private set; }
|
||||||
|
|
||||||
|
@ -20,7 +20,7 @@ namespace ChocolArm64
|
||||||
|
|
||||||
public event EventHandler WorkFinished;
|
public event EventHandler WorkFinished;
|
||||||
|
|
||||||
public int ThreadId => Registers.ThreadId;
|
public int ThreadId => ThreadState.ThreadId;
|
||||||
|
|
||||||
public bool IsAlive => Work.IsAlive;
|
public bool IsAlive => Work.IsAlive;
|
||||||
|
|
||||||
|
@ -34,7 +34,7 @@ namespace ChocolArm64
|
||||||
this.Priority = Priority;
|
this.Priority = Priority;
|
||||||
this.EntryPoint = EntryPoint;
|
this.EntryPoint = EntryPoint;
|
||||||
|
|
||||||
Registers = new ARegisters();
|
ThreadState = new AThreadState();
|
||||||
Translator = new ATranslator(this);
|
Translator = new ATranslator(this);
|
||||||
ExecuteLock = new object();
|
ExecuteLock = new object();
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,7 +9,7 @@ namespace ChocolArm64
|
||||||
{
|
{
|
||||||
class ATranslatedSub
|
class ATranslatedSub
|
||||||
{
|
{
|
||||||
private delegate long AA64Subroutine(ARegisters Register, AMemory Memory);
|
private delegate long AA64Subroutine(AThreadState Register, AMemory Memory);
|
||||||
|
|
||||||
private AA64Subroutine ExecDelegate;
|
private AA64Subroutine ExecDelegate;
|
||||||
|
|
||||||
|
@ -17,8 +17,8 @@ namespace ChocolArm64
|
||||||
|
|
||||||
public static Type[] FixedArgTypes { get; private set; }
|
public static Type[] FixedArgTypes { get; private set; }
|
||||||
|
|
||||||
public static int RegistersArgIdx { get; private set; }
|
public static int StateArgIdx { get; private set; }
|
||||||
public static int MemoryArgIdx { get; private set; }
|
public static int MemoryArgIdx { get; private set; }
|
||||||
|
|
||||||
public DynamicMethod Method { get; private set; }
|
public DynamicMethod Method { get; private set; }
|
||||||
|
|
||||||
|
@ -58,9 +58,9 @@ namespace ChocolArm64
|
||||||
|
|
||||||
FixedArgTypes[Index] = ParamType;
|
FixedArgTypes[Index] = ParamType;
|
||||||
|
|
||||||
if (ParamType == typeof(ARegisters))
|
if (ParamType == typeof(AThreadState))
|
||||||
{
|
{
|
||||||
RegistersArgIdx = Index;
|
StateArgIdx = Index;
|
||||||
}
|
}
|
||||||
else if (ParamType == typeof(AMemory))
|
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)
|
if (!HasDelegate)
|
||||||
{
|
{
|
||||||
|
@ -83,7 +83,7 @@ namespace ChocolArm64
|
||||||
|
|
||||||
foreach (ARegister Reg in Params)
|
foreach (ARegister Reg in Params)
|
||||||
{
|
{
|
||||||
Generator.EmitLdarg(RegistersArgIdx);
|
Generator.EmitLdarg(StateArgIdx);
|
||||||
|
|
||||||
Generator.Emit(OpCodes.Ldfld, Reg.GetField());
|
Generator.Emit(OpCodes.Ldfld, Reg.GetField());
|
||||||
}
|
}
|
||||||
|
@ -96,7 +96,7 @@ namespace ChocolArm64
|
||||||
HasDelegate = true;
|
HasDelegate = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return ExecDelegate(Registers, Memory);
|
return ExecDelegate(ThreadState, Memory);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void MarkForReJit() => NeedsReJit = true;
|
public void MarkForReJit() => NeedsReJit = true;
|
||||||
|
|
|
@ -8,10 +8,10 @@ namespace ChocolArm64
|
||||||
{
|
{
|
||||||
class ATranslator
|
class ATranslator
|
||||||
{
|
{
|
||||||
private Dictionary<long, ATranslatedSub> CachedSubs;
|
|
||||||
|
|
||||||
public AThread Thread { get; private set; }
|
public AThread Thread { get; private set; }
|
||||||
|
|
||||||
|
private Dictionary<long, ATranslatedSub> CachedSubs;
|
||||||
|
|
||||||
private bool KeepRunning;
|
private bool KeepRunning;
|
||||||
|
|
||||||
public ATranslator(AThread Parent)
|
public ATranslator(AThread Parent)
|
||||||
|
@ -31,11 +31,11 @@ namespace ChocolArm64
|
||||||
{
|
{
|
||||||
if (CachedSubs.TryGetValue(Position, out ATranslatedSub Sub) && !Sub.NeedsReJit)
|
if (CachedSubs.TryGetValue(Position, out ATranslatedSub Sub) && !Sub.NeedsReJit)
|
||||||
{
|
{
|
||||||
Position = Sub.Execute(Thread.Registers, Thread.Memory);
|
Position = Sub.Execute(Thread.ThreadState, Thread.Memory);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Position = TranslateSubroutine(Position).Execute(Thread.Registers, Thread.Memory);
|
Position = TranslateSubroutine(Position).Execute(Thread.ThreadState, Thread.Memory);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
while (Position != 0 && KeepRunning);
|
while (Position != 0 && KeepRunning);
|
||||||
|
|
|
@ -25,7 +25,7 @@ namespace ChocolArm64.Decoder
|
||||||
Cond = (ACond)((OpCode >> 12) & 0xf);
|
Cond = (ACond)((OpCode >> 12) & 0xf);
|
||||||
RmImm = (OpCode >> 16) & 0x1f;
|
RmImm = (OpCode >> 16) & 0x1f;
|
||||||
|
|
||||||
Rd = ARegisters.ZRIndex;
|
Rd = AThreadState.ZRIndex;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -4,10 +4,10 @@ namespace ChocolArm64.Decoder
|
||||||
{
|
{
|
||||||
class AOpCodeMem : AOpCode
|
class AOpCodeMem : AOpCode
|
||||||
{
|
{
|
||||||
public int Rt { get; protected set; }
|
public int Rt { get; protected set; }
|
||||||
public int Rn { get; protected set; }
|
public int Rn { get; protected set; }
|
||||||
public int Size { get; protected set; }
|
public int Size { get; protected set; }
|
||||||
public bool Extend64 { get; protected set; }
|
public bool Extend64 { get; protected set; }
|
||||||
|
|
||||||
public AOpCodeMem(AInst Inst, long Position, int OpCode) : base(Inst, Position, OpCode)
|
public AOpCodeMem(AInst Inst, long Position, int OpCode) : base(Inst, Position, OpCode)
|
||||||
{
|
{
|
||||||
|
|
|
@ -3,12 +3,8 @@ using ChocolArm64.State;
|
||||||
|
|
||||||
namespace ChocolArm64.Decoder
|
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 Reps { get; private set; }
|
||||||
public int SElems { get; private set; }
|
public int SElems { get; private set; }
|
||||||
public int Elems { get; private set; }
|
public int Elems { get; private set; }
|
||||||
|
@ -29,10 +25,7 @@ namespace ChocolArm64.Decoder
|
||||||
default: Inst = AInst.Undefined; return;
|
default: Inst = AInst.Undefined; return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Rt = (OpCode >> 0) & 0x1f;
|
|
||||||
Rn = (OpCode >> 5) & 0x1f;
|
|
||||||
Size = (OpCode >> 10) & 0x3;
|
Size = (OpCode >> 10) & 0x3;
|
||||||
Rm = (OpCode >> 16) & 0x1f;
|
|
||||||
WBack = ((OpCode >> 23) & 0x1) != 0;
|
WBack = ((OpCode >> 23) & 0x1) != 0;
|
||||||
|
|
||||||
bool Q = ((OpCode >> 30) & 1) != 0;
|
bool Q = ((OpCode >> 30) & 1) != 0;
|
||||||
|
@ -44,6 +37,8 @@ namespace ChocolArm64.Decoder
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Extend64 = false;
|
||||||
|
|
||||||
RegisterSize = Q
|
RegisterSize = Q
|
||||||
? ARegisterSize.SIMD128
|
? ARegisterSize.SIMD128
|
||||||
: ARegisterSize.SIMD64;
|
: ARegisterSize.SIMD64;
|
||||||
|
|
|
@ -3,12 +3,8 @@ using ChocolArm64.State;
|
||||||
|
|
||||||
namespace ChocolArm64.Decoder
|
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 SElems { get; private set; }
|
||||||
public int Index { get; private set; }
|
public int Index { get; private set; }
|
||||||
public bool Replicate { get; private set; }
|
public bool Replicate { get; private set; }
|
||||||
|
@ -31,11 +27,9 @@ namespace ChocolArm64.Decoder
|
||||||
|
|
||||||
switch (Scale)
|
switch (Scale)
|
||||||
{
|
{
|
||||||
case 0: Index >>= 0; break;
|
|
||||||
|
|
||||||
case 1:
|
case 1:
|
||||||
{
|
{
|
||||||
if ((Index & 1) != 0)
|
if ((Size & 1) != 0)
|
||||||
{
|
{
|
||||||
Inst = AInst.Undefined;
|
Inst = AInst.Undefined;
|
||||||
|
|
||||||
|
@ -49,23 +43,23 @@ namespace ChocolArm64.Decoder
|
||||||
|
|
||||||
case 2:
|
case 2:
|
||||||
{
|
{
|
||||||
if ((Index & 2) != 0 ||
|
if ((Size & 2) != 0 ||
|
||||||
((Index & 1) != 0 && S != 0))
|
((Size & 1) != 0 && S != 0))
|
||||||
{
|
{
|
||||||
Inst = AInst.Undefined;
|
Inst = AInst.Undefined;
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((Index & 1) != 0)
|
if ((Size & 1) != 0)
|
||||||
{
|
{
|
||||||
Index >>= 3;
|
Index >>= 3;
|
||||||
|
|
||||||
|
Scale = 3;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Index >>= 2;
|
Index >>= 2;
|
||||||
|
|
||||||
Scale = 3;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
@ -91,9 +85,8 @@ namespace ChocolArm64.Decoder
|
||||||
this.SElems = SElems;
|
this.SElems = SElems;
|
||||||
this.Size = Scale;
|
this.Size = Scale;
|
||||||
|
|
||||||
Rt = (OpCode >> 0) & 0x1f;
|
Extend64 = false;
|
||||||
Rn = (OpCode >> 5) & 0x1f;
|
|
||||||
Rm = (OpCode >> 16) & 0x1f;
|
|
||||||
WBack = ((OpCode >> 23) & 0x1) != 0;
|
WBack = ((OpCode >> 23) & 0x1) != 0;
|
||||||
|
|
||||||
RegisterSize = Q != 0
|
RegisterSize = Q != 0
|
||||||
|
|
|
@ -2,17 +2,13 @@ using ChocolArm64.Instruction;
|
||||||
|
|
||||||
namespace ChocolArm64.Decoder
|
namespace ChocolArm64.Decoder
|
||||||
{
|
{
|
||||||
class AOpCodeSimdRegElem : AOpCodeSimd
|
class AOpCodeSimdRegElem : AOpCodeSimdReg
|
||||||
{
|
{
|
||||||
public int Rm { get; private set; }
|
|
||||||
public int Index { get; private set; }
|
public int Index { get; private set; }
|
||||||
|
|
||||||
public AOpCodeSimdRegElem(AInst Inst, long Position, int OpCode) : base(Inst, Position, OpCode)
|
public AOpCodeSimdRegElem(AInst Inst, long Position, int OpCode) : base(Inst, Position, OpCode)
|
||||||
{
|
{
|
||||||
Rm = (OpCode >> 16) & 0x1f;
|
if ((Size & 1) != 0)
|
||||||
Size = (OpCode >> 22) & 0x1;
|
|
||||||
|
|
||||||
if (Size != 0)
|
|
||||||
{
|
{
|
||||||
Index = (OpCode >> 11) & 1;
|
Index = (OpCode >> 11) & 1;
|
||||||
}
|
}
|
||||||
|
|
|
@ -60,6 +60,8 @@ namespace ChocolArm64.Instruction
|
||||||
|
|
||||||
Context.Emit(OpCodes.And);
|
Context.Emit(OpCodes.And);
|
||||||
|
|
||||||
|
EmitZeroCVFlags(Context);
|
||||||
|
|
||||||
Context.EmitZNFlagCheck();
|
Context.EmitZNFlagCheck();
|
||||||
|
|
||||||
EmitDataStoreS(Context);
|
EmitDataStoreS(Context);
|
||||||
|
@ -79,6 +81,8 @@ namespace ChocolArm64.Instruction
|
||||||
|
|
||||||
if (SetFlags)
|
if (SetFlags)
|
||||||
{
|
{
|
||||||
|
EmitZeroCVFlags(Context);
|
||||||
|
|
||||||
Context.EmitZNFlagCheck();
|
Context.EmitZNFlagCheck();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -335,5 +339,16 @@ namespace ChocolArm64.Instruction
|
||||||
Context.Emit(OpCodes.Conv_I4);
|
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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -21,22 +21,13 @@ namespace ChocolArm64.Instruction
|
||||||
|
|
||||||
public static void EmitAddsVCheck(AILEmitterCtx Context)
|
public static void EmitAddsVCheck(AILEmitterCtx Context)
|
||||||
{
|
{
|
||||||
//V = (Rd ^ Rn) & (Rd ^ Rm) & ~(Rn ^ Rm) < 0
|
//V = (Rd ^ Rn) & ~(Rn ^ Rm) < 0
|
||||||
Context.EmitSttmp();
|
Context.Emit(OpCodes.Dup);
|
||||||
Context.EmitLdtmp();
|
|
||||||
Context.EmitLdtmp();
|
|
||||||
|
|
||||||
EmitDataLoadRn(Context);
|
EmitDataLoadRn(Context);
|
||||||
|
|
||||||
Context.Emit(OpCodes.Xor);
|
Context.Emit(OpCodes.Xor);
|
||||||
|
|
||||||
Context.EmitLdtmp();
|
|
||||||
|
|
||||||
EmitDataLoadOper2(Context);
|
|
||||||
|
|
||||||
Context.Emit(OpCodes.Xor);
|
|
||||||
Context.Emit(OpCodes.And);
|
|
||||||
|
|
||||||
EmitDataLoadOpers(Context);
|
EmitDataLoadOpers(Context);
|
||||||
|
|
||||||
Context.Emit(OpCodes.Xor);
|
Context.Emit(OpCodes.Xor);
|
||||||
|
@ -52,15 +43,14 @@ namespace ChocolArm64.Instruction
|
||||||
|
|
||||||
public static void EmitSubsCCheck(AILEmitterCtx Context)
|
public static void EmitSubsCCheck(AILEmitterCtx Context)
|
||||||
{
|
{
|
||||||
//C = Rn == Rm || Rn > Rm
|
//C = Rn == Rm || Rn > Rm = !(Rn < Rm)
|
||||||
EmitDataLoadOpers(Context);
|
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.Xor);
|
||||||
Context.Emit(OpCodes.Or);
|
|
||||||
|
|
||||||
Context.EmitStflg((int)APState.CBit);
|
Context.EmitStflg((int)APState.CBit);
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,12 +11,12 @@ namespace ChocolArm64.Instruction
|
||||||
|
|
||||||
public static void Brk(AILEmitterCtx Context)
|
public static void Brk(AILEmitterCtx Context)
|
||||||
{
|
{
|
||||||
EmitExceptionCall(Context, nameof(ARegisters.OnBreak));
|
EmitExceptionCall(Context, nameof(AThreadState.OnBreak));
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void Svc(AILEmitterCtx Context)
|
public static void Svc(AILEmitterCtx Context)
|
||||||
{
|
{
|
||||||
EmitExceptionCall(Context, nameof(ARegisters.OnSvcCall));
|
EmitExceptionCall(Context, nameof(AThreadState.OnSvcCall));
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void EmitExceptionCall(AILEmitterCtx Context, string MthdName)
|
private static void EmitExceptionCall(AILEmitterCtx Context, string MthdName)
|
||||||
|
@ -25,11 +25,11 @@ namespace ChocolArm64.Instruction
|
||||||
|
|
||||||
Context.EmitStoreState();
|
Context.EmitStoreState();
|
||||||
|
|
||||||
Context.EmitLdarg(ATranslatedSub.RegistersArgIdx);
|
Context.EmitLdarg(ATranslatedSub.StateArgIdx);
|
||||||
|
|
||||||
Context.EmitLdc_I4(Op.Id);
|
Context.EmitLdc_I4(Op.Id);
|
||||||
|
|
||||||
MethodInfo MthdInfo = typeof(ARegisters).GetMethod(MthdName, Binding);
|
MethodInfo MthdInfo = typeof(AThreadState).GetMethod(MthdName, Binding);
|
||||||
|
|
||||||
Context.EmitCall(MthdInfo);
|
Context.EmitCall(MthdInfo);
|
||||||
|
|
||||||
|
@ -45,14 +45,14 @@ namespace ChocolArm64.Instruction
|
||||||
|
|
||||||
Context.EmitStoreState();
|
Context.EmitStoreState();
|
||||||
|
|
||||||
Context.EmitLdarg(ATranslatedSub.RegistersArgIdx);
|
Context.EmitLdarg(ATranslatedSub.StateArgIdx);
|
||||||
|
|
||||||
Context.EmitLdc_I8(Op.Position);
|
Context.EmitLdc_I8(Op.Position);
|
||||||
Context.EmitLdc_I4(Op.RawOpCode);
|
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);
|
Context.EmitCall(MthdInfo);
|
||||||
|
|
||||||
|
|
|
@ -26,7 +26,7 @@ namespace ChocolArm64.Instruction
|
||||||
AOpCodeBImmAl Op = (AOpCodeBImmAl)Context.CurrOp;
|
AOpCodeBImmAl Op = (AOpCodeBImmAl)Context.CurrOp;
|
||||||
|
|
||||||
Context.EmitLdc_I(Op.Position + 4);
|
Context.EmitLdc_I(Op.Position + 4);
|
||||||
Context.EmitStint(ARegisters.LRIndex);
|
Context.EmitStint(AThreadState.LRIndex);
|
||||||
Context.EmitStoreState();
|
Context.EmitStoreState();
|
||||||
|
|
||||||
if (Context.TryOptEmitSubroutineCall())
|
if (Context.TryOptEmitSubroutineCall())
|
||||||
|
@ -66,7 +66,7 @@ namespace ChocolArm64.Instruction
|
||||||
AOpCodeBReg Op = (AOpCodeBReg)Context.CurrOp;
|
AOpCodeBReg Op = (AOpCodeBReg)Context.CurrOp;
|
||||||
|
|
||||||
Context.EmitLdc_I(Op.Position + 4);
|
Context.EmitLdc_I(Op.Position + 4);
|
||||||
Context.EmitStint(ARegisters.LRIndex);
|
Context.EmitStint(AThreadState.LRIndex);
|
||||||
Context.EmitStoreState();
|
Context.EmitStoreState();
|
||||||
Context.EmitLdintzr(Op.Rn);
|
Context.EmitLdintzr(Op.Rn);
|
||||||
|
|
||||||
|
@ -99,7 +99,7 @@ namespace ChocolArm64.Instruction
|
||||||
public static void Ret(AILEmitterCtx Context)
|
public static void Ret(AILEmitterCtx Context)
|
||||||
{
|
{
|
||||||
Context.EmitStoreState();
|
Context.EmitStoreState();
|
||||||
Context.EmitLdint(ARegisters.LRIndex);
|
Context.EmitLdint(AThreadState.LRIndex);
|
||||||
|
|
||||||
Context.Emit(OpCodes.Ret);
|
Context.Emit(OpCodes.Ret);
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,7 +20,7 @@ namespace ChocolArm64.Instruction
|
||||||
{
|
{
|
||||||
AOpCodeAdr Op = (AOpCodeAdr)Context.CurrOp;
|
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);
|
Context.EmitStintzr(Op.Rd);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -159,7 +159,7 @@ namespace ChocolArm64.Instruction
|
||||||
private static void EmitMemoryCall(AILEmitterCtx Context, string Name, int Rn = -1)
|
private static void EmitMemoryCall(AILEmitterCtx Context, string Name, int Rn = -1)
|
||||||
{
|
{
|
||||||
Context.EmitLdarg(ATranslatedSub.MemoryArgIdx);
|
Context.EmitLdarg(ATranslatedSub.MemoryArgIdx);
|
||||||
Context.EmitLdarg(ATranslatedSub.RegistersArgIdx);
|
Context.EmitLdarg(ATranslatedSub.StateArgIdx);
|
||||||
|
|
||||||
if (Rn != -1)
|
if (Rn != -1)
|
||||||
{
|
{
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
using ChocolArm64.Decoder;
|
||||||
using ChocolArm64.Memory;
|
using ChocolArm64.Memory;
|
||||||
using ChocolArm64.Translation;
|
using ChocolArm64.Translation;
|
||||||
using System;
|
using System;
|
||||||
|
@ -31,67 +32,107 @@ namespace ChocolArm64.Instruction
|
||||||
|
|
||||||
private static void EmitReadCall(AILEmitterCtx Context, Extension Ext, int Size)
|
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));
|
throw new ArgumentOutOfRangeException(nameof(Size));
|
||||||
}
|
}
|
||||||
|
|
||||||
string Name = null;
|
if (IsSimd)
|
||||||
|
|
||||||
switch (Size)
|
|
||||||
{
|
{
|
||||||
case 0: Name = nameof(AMemory.ReadByte); break;
|
switch (Size)
|
||||||
case 1: Name = nameof(AMemory.ReadUInt16); break;
|
{
|
||||||
case 2: Name = nameof(AMemory.ReadUInt32); break;
|
case 0: Name = nameof(AMemory.ReadVector8); break;
|
||||||
case 3: Name = nameof(AMemory.ReadUInt64); break;
|
case 1: Name = nameof(AMemory.ReadVector16); break;
|
||||||
case 4: Name = nameof(AMemory.ReadVector128); 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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Context.EmitCall(typeof(AMemory), Name);
|
Context.EmitCall(typeof(AMemory), Name);
|
||||||
|
|
||||||
if (Ext == Extension.Sx32 ||
|
if (!IsSimd)
|
||||||
Ext == Extension.Sx64)
|
|
||||||
{
|
{
|
||||||
switch (Size)
|
if (Ext == Extension.Sx32 ||
|
||||||
|
Ext == Extension.Sx64)
|
||||||
{
|
{
|
||||||
case 0: Context.Emit(OpCodes.Conv_I1); break;
|
switch (Size)
|
||||||
case 1: Context.Emit(OpCodes.Conv_I2); break;
|
{
|
||||||
case 2: Context.Emit(OpCodes.Conv_I4); break;
|
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)
|
if (Size < 3)
|
||||||
{
|
{
|
||||||
Context.Emit(Ext == Extension.Sx64
|
Context.Emit(Ext == Extension.Sx64
|
||||||
? OpCodes.Conv_I8
|
? OpCodes.Conv_I8
|
||||||
: OpCodes.Conv_U8);
|
: OpCodes.Conv_U8);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void EmitWriteCall(AILEmitterCtx Context, int Size)
|
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));
|
throw new ArgumentOutOfRangeException(nameof(Size));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Size < 3)
|
if (Size < 3 && !IsSimd)
|
||||||
{
|
{
|
||||||
Context.Emit(OpCodes.Conv_I4);
|
Context.Emit(OpCodes.Conv_I4);
|
||||||
}
|
}
|
||||||
|
|
||||||
string Name = null;
|
if (IsSimd)
|
||||||
|
|
||||||
switch (Size)
|
|
||||||
{
|
{
|
||||||
case 0: Name = nameof(AMemory.WriteByte); break;
|
switch (Size)
|
||||||
case 1: Name = nameof(AMemory.WriteUInt16); break;
|
{
|
||||||
case 2: Name = nameof(AMemory.WriteUInt32); break;
|
case 0: Name = nameof(AMemory.WriteVector8); break;
|
||||||
case 3: Name = nameof(AMemory.WriteUInt64); break;
|
case 1: Name = nameof(AMemory.WriteVector16); break;
|
||||||
case 4: Name = nameof(AMemory.WriteVector128); 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);
|
Context.EmitCall(typeof(AMemory), Name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static bool GetIsSimd(AILEmitterCtx Context)
|
||||||
|
{
|
||||||
|
return Context.CurrOp is IAOpCodeSimd &&
|
||||||
|
!(Context.CurrOp is AOpCodeSimdMemMs ||
|
||||||
|
Context.CurrOp is AOpCodeSimdMemSs);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -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();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
File diff suppressed because it is too large
Load diff
339
Ryujinx/Cpu/Instruction/AInstEmitSimdArithmetic.cs
Normal file
339
Ryujinx/Cpu/Instruction/AInstEmitSimdArithmetic.cs
Normal file
|
@ -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));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
236
Ryujinx/Cpu/Instruction/AInstEmitSimdCmp.cs
Normal file
236
Ryujinx/Cpu/Instruction/AInstEmitSimdCmp.cs
Normal file
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
444
Ryujinx/Cpu/Instruction/AInstEmitSimdCvt.cs
Normal file
444
Ryujinx/Cpu/Instruction/AInstEmitSimdCvt.cs
Normal file
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
584
Ryujinx/Cpu/Instruction/AInstEmitSimdHelper.cs
Normal file
584
Ryujinx/Cpu/Instruction/AInstEmitSimdHelper.cs
Normal file
|
@ -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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
69
Ryujinx/Cpu/Instruction/AInstEmitSimdLogical.cs
Normal file
69
Ryujinx/Cpu/Instruction/AInstEmitSimdLogical.cs
Normal file
|
@ -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));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
184
Ryujinx/Cpu/Instruction/AInstEmitSimdMemory.cs
Normal file
184
Ryujinx/Cpu/Instruction/AInstEmitSimdMemory.cs
Normal file
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
299
Ryujinx/Cpu/Instruction/AInstEmitSimdMove.cs
Normal file
299
Ryujinx/Cpu/Instruction/AInstEmitSimdMove.cs
Normal file
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
306
Ryujinx/Cpu/Instruction/AInstEmitSimdShift.cs
Normal file
306
Ryujinx/Cpu/Instruction/AInstEmitSimdShift.cs
Normal file
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -13,26 +13,26 @@ namespace ChocolArm64.Instruction
|
||||||
{
|
{
|
||||||
AOpCodeSystem Op = (AOpCodeSystem)Context.CurrOp;
|
AOpCodeSystem Op = (AOpCodeSystem)Context.CurrOp;
|
||||||
|
|
||||||
Context.EmitLdarg(ATranslatedSub.RegistersArgIdx);
|
Context.EmitLdarg(ATranslatedSub.StateArgIdx);
|
||||||
|
|
||||||
string PropName;
|
string PropName;
|
||||||
|
|
||||||
switch (GetPackedId(Op))
|
switch (GetPackedId(Op))
|
||||||
{
|
{
|
||||||
case 0b11_011_0000_0000_001: PropName = nameof(ARegisters.CtrEl0); break;
|
case 0b11_011_0000_0000_001: PropName = nameof(AThreadState.CtrEl0); break;
|
||||||
case 0b11_011_0000_0000_111: PropName = nameof(ARegisters.DczidEl0); break;
|
case 0b11_011_0000_0000_111: PropName = nameof(AThreadState.DczidEl0); break;
|
||||||
case 0b11_011_0100_0100_000: PropName = nameof(ARegisters.Fpcr); break;
|
case 0b11_011_0100_0100_000: PropName = nameof(AThreadState.Fpcr); break;
|
||||||
case 0b11_011_0100_0100_001: PropName = nameof(ARegisters.Fpsr); break;
|
case 0b11_011_0100_0100_001: PropName = nameof(AThreadState.Fpsr); break;
|
||||||
case 0b11_011_1101_0000_010: PropName = nameof(ARegisters.TpidrEl0); break;
|
case 0b11_011_1101_0000_010: PropName = nameof(AThreadState.TpidrEl0); break;
|
||||||
case 0b11_011_1101_0000_011: PropName = nameof(ARegisters.Tpidr); break;
|
case 0b11_011_1101_0000_011: PropName = nameof(AThreadState.Tpidr); break;
|
||||||
case 0b11_011_1110_0000_001: PropName = nameof(ARegisters.CntpctEl0); break;
|
case 0b11_011_1110_0000_001: PropName = nameof(AThreadState.CntpctEl0); break;
|
||||||
|
|
||||||
default: throw new NotImplementedException($"Unknown MRS at {Op.Position:x16}");
|
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) &&
|
if (PropInfo.PropertyType != typeof(long) &&
|
||||||
PropInfo.PropertyType != typeof(ulong))
|
PropInfo.PropertyType != typeof(ulong))
|
||||||
|
@ -47,21 +47,21 @@ namespace ChocolArm64.Instruction
|
||||||
{
|
{
|
||||||
AOpCodeSystem Op = (AOpCodeSystem)Context.CurrOp;
|
AOpCodeSystem Op = (AOpCodeSystem)Context.CurrOp;
|
||||||
|
|
||||||
Context.EmitLdarg(ATranslatedSub.RegistersArgIdx);
|
Context.EmitLdarg(ATranslatedSub.StateArgIdx);
|
||||||
Context.EmitLdintzr(Op.Rt);
|
Context.EmitLdintzr(Op.Rt);
|
||||||
|
|
||||||
string PropName;
|
string PropName;
|
||||||
|
|
||||||
switch (GetPackedId(Op))
|
switch (GetPackedId(Op))
|
||||||
{
|
{
|
||||||
case 0b11_011_0100_0100_000: PropName = nameof(ARegisters.Fpcr); break;
|
case 0b11_011_0100_0100_000: PropName = nameof(AThreadState.Fpcr); break;
|
||||||
case 0b11_011_0100_0100_001: PropName = nameof(ARegisters.Fpsr); break;
|
case 0b11_011_0100_0100_001: PropName = nameof(AThreadState.Fpsr); break;
|
||||||
case 0b11_011_1101_0000_010: PropName = nameof(ARegisters.TpidrEl0); break;
|
case 0b11_011_1101_0000_010: PropName = nameof(AThreadState.TpidrEl0); break;
|
||||||
|
|
||||||
default: throw new NotImplementedException($"Unknown MSR at {Op.Position:x16}");
|
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) &&
|
if (PropInfo.PropertyType != typeof(long) &&
|
||||||
PropInfo.PropertyType != typeof(ulong))
|
PropInfo.PropertyType != typeof(ulong))
|
||||||
|
@ -69,7 +69,7 @@ namespace ChocolArm64.Instruction
|
||||||
Context.Emit(OpCodes.Conv_U4);
|
Context.Emit(OpCodes.Conv_U4);
|
||||||
}
|
}
|
||||||
|
|
||||||
Context.EmitCallPropSet(typeof(ARegisters), PropName);
|
Context.EmitCallPropSet(typeof(AThreadState), PropName);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void Nop(AILEmitterCtx Context)
|
public static void Nop(AILEmitterCtx Context)
|
||||||
|
@ -89,7 +89,7 @@ namespace ChocolArm64.Instruction
|
||||||
case 0b11_011_0111_0100_001:
|
case 0b11_011_0111_0100_001:
|
||||||
{
|
{
|
||||||
//DC ZVA
|
//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.EmitLdarg(ATranslatedSub.MemoryArgIdx);
|
||||||
Context.EmitLdint(Op.Rt);
|
Context.EmitLdint(Op.Rt);
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
using ChocolArm64.State;
|
using ChocolArm64.State;
|
||||||
using ChocolArm64.Translation;
|
using ChocolArm64.Translation;
|
||||||
using System;
|
using System;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
|
||||||
namespace ChocolArm64.Instruction
|
namespace ChocolArm64.Instruction
|
||||||
{
|
{
|
||||||
|
@ -97,142 +98,62 @@ namespace ChocolArm64.Instruction
|
||||||
throw new ArgumentException(nameof(Size));
|
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 :
|
return Value > int.MaxValue ? int.MaxValue :
|
||||||
Value < int.MinValue ? int.MinValue : (int)Value;
|
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 :
|
return Value > long.MaxValue ? long.MaxValue :
|
||||||
Value < long.MinValue ? long.MinValue : (long)Value;
|
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 :
|
return Value > uint.MaxValue ? uint.MaxValue :
|
||||||
Value < uint.MinValue ? uint.MinValue : (uint)Value;
|
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 :
|
return Value > ulong.MaxValue ? ulong.MaxValue :
|
||||||
Value < ulong.MinValue ? ulong.MinValue : (ulong)Value;
|
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 :
|
return Value > int.MaxValue ? int.MaxValue :
|
||||||
Value < int.MinValue ? int.MinValue : (int)Value;
|
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 :
|
return Value > long.MaxValue ? long.MaxValue :
|
||||||
Value < long.MinValue ? long.MinValue : (long)Value;
|
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 :
|
return Value > uint.MaxValue ? uint.MaxValue :
|
||||||
Value < uint.MinValue ? uint.MinValue : (uint)Value;
|
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 :
|
return Value > ulong.MaxValue ? ulong.MaxValue :
|
||||||
Value < ulong.MinValue ? ulong.MinValue : (ulong)Value;
|
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)
|
public static ulong SMulHi128(ulong LHS, ulong RHS)
|
||||||
{
|
{
|
||||||
long LLo = (uint)(LHS >> 0);
|
long LLo = (uint)(LHS >> 0);
|
||||||
|
@ -271,114 +192,7 @@ namespace ChocolArm64.Instruction
|
||||||
return ResHi;
|
return ResHi;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static AVec Addp_S(AVec Vector, int Size)
|
public static int CountSetBits8(byte Value)
|
||||||
{
|
|
||||||
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)
|
|
||||||
{
|
{
|
||||||
return (Value >> 0) & 1 + (Value >> 1) & 1 +
|
return (Value >> 0) & 1 + (Value >> 1) & 1 +
|
||||||
(Value >> 2) & 1 + (Value >> 3) & 1 +
|
(Value >> 2) & 1 + (Value >> 3) & 1 +
|
||||||
|
@ -386,275 +200,6 @@ namespace ChocolArm64.Instruction
|
||||||
(Value >> 6) & 1 + (Value >> 7);
|
(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)
|
public static AVec Tbl1_V64(AVec Vector, AVec Tb0)
|
||||||
{
|
{
|
||||||
return Tbl(Vector, 8, Tb0);
|
return Tbl(Vector, 8, Tb0);
|
||||||
|
@ -704,194 +249,27 @@ namespace ChocolArm64.Instruction
|
||||||
for (int Index = 0; Index < Tb.Length; Index++)
|
for (int Index = 0; Index < Tb.Length; Index++)
|
||||||
for (int Index2 = 0; Index2 < 16; Index2++)
|
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++)
|
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)
|
if (TblIdx < Table.Length)
|
||||||
{
|
{
|
||||||
Res = InsertVec(Res, Index, 0, Table[TblIdx]);
|
Res = VectorInsertInt(Table[TblIdx], Res, Index, 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return Res;
|
return Res;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static AVec Uaddlv64(AVec Vector, int Size)
|
public static ulong VectorExtractIntZx(AVec Vector, int Index, 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)
|
|
||||||
{
|
{
|
||||||
switch (Size)
|
switch (Size)
|
||||||
{
|
{
|
||||||
case 0: return Vector.ExtractByte(Index);
|
case 0: return Vector.ExtractByte (Index);
|
||||||
case 1: return Vector.ExtractUInt16(Index);
|
case 1: return Vector.ExtractUInt16(Index);
|
||||||
case 2: return Vector.ExtractUInt32(Index);
|
case 2: return Vector.ExtractUInt32(Index);
|
||||||
case 3: return Vector.ExtractUInt64(Index);
|
case 3: return Vector.ExtractUInt64(Index);
|
||||||
|
@ -900,14 +278,14 @@ namespace ChocolArm64.Instruction
|
||||||
throw new ArgumentOutOfRangeException(nameof(Size));
|
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)
|
switch (Size)
|
||||||
{
|
{
|
||||||
case 0: return (sbyte)Vector.ExtractByte(Index);
|
case 0: return (sbyte)Vector.ExtractByte (Index);
|
||||||
case 1: return (short)Vector.ExtractUInt16(Index);
|
case 1: return (short)Vector.ExtractUInt16(Index);
|
||||||
case 2: return (int)Vector.ExtractUInt32(Index);
|
case 2: return (int)Vector.ExtractUInt32(Index);
|
||||||
case 3: return (long)Vector.ExtractUInt64(Index);
|
case 3: return (long)Vector.ExtractUInt64(Index);
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new ArgumentOutOfRangeException(nameof(Size));
|
throw new ArgumentOutOfRangeException(nameof(Size));
|
||||||
|
@ -945,31 +323,5 @@ namespace ChocolArm64.Instruction
|
||||||
|
|
||||||
throw new ArgumentOutOfRangeException(nameof(Size));
|
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));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -2,12 +2,13 @@ using ChocolArm64.Exceptions;
|
||||||
using ChocolArm64.State;
|
using ChocolArm64.State;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
|
||||||
namespace ChocolArm64.Memory
|
namespace ChocolArm64.Memory
|
||||||
{
|
{
|
||||||
public unsafe class AMemory
|
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; }
|
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;
|
Position &= ~ErgMask;
|
||||||
|
|
||||||
lock (Monitors)
|
lock (Monitors)
|
||||||
{
|
{
|
||||||
if (Monitors.TryGetValue(Registers.ThreadId, out ExMonitor Monitor))
|
if (Monitors.TryGetValue(ThreadState.ThreadId, out ExMonitor Monitor))
|
||||||
{
|
{
|
||||||
ExAddrs.Remove(Monitor.Position);
|
ExAddrs.Remove(Monitor.Position);
|
||||||
}
|
}
|
||||||
|
@ -79,20 +80,20 @@ namespace ChocolArm64.Memory
|
||||||
|
|
||||||
Monitor = new ExMonitor(Position, ExState);
|
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;
|
Position &= ~ErgMask;
|
||||||
|
|
||||||
lock (Monitors)
|
lock (Monitors)
|
||||||
{
|
{
|
||||||
if (!Monitors.TryGetValue(Registers.ThreadId, out ExMonitor Monitor))
|
if (!Monitors.TryGetValue(ThreadState.ThreadId, out ExMonitor Monitor))
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -101,11 +102,11 @@ namespace ChocolArm64.Memory
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void ClearExclusive(ARegisters Registers)
|
public void ClearExclusive(AThreadState ThreadState)
|
||||||
{
|
{
|
||||||
lock (Monitors)
|
lock (Monitors)
|
||||||
{
|
{
|
||||||
if (Monitors.TryGetValue(Registers.ThreadId, out ExMonitor Monitor))
|
if (Monitors.TryGetValue(ThreadState.ThreadId, out ExMonitor Monitor))
|
||||||
{
|
{
|
||||||
Monitor.Reset();
|
Monitor.Reset();
|
||||||
ExAddrs.Remove(Monitor.Position);
|
ExAddrs.Remove(Monitor.Position);
|
||||||
|
@ -138,6 +139,7 @@ namespace ChocolArm64.Memory
|
||||||
public int ReadInt32(long Position) => (int)ReadUInt32(Position);
|
public int ReadInt32(long Position) => (int)ReadUInt32(Position);
|
||||||
public long ReadInt64(long Position) => (long)ReadUInt64(Position);
|
public long ReadInt64(long Position) => (long)ReadUInt64(Position);
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public byte ReadByte(long Position)
|
public byte ReadByte(long Position)
|
||||||
{
|
{
|
||||||
#if DEBUG
|
#if DEBUG
|
||||||
|
@ -147,6 +149,7 @@ namespace ChocolArm64.Memory
|
||||||
return *((byte*)(RamPtr + (uint)Position));
|
return *((byte*)(RamPtr + (uint)Position));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public ushort ReadUInt16(long Position)
|
public ushort ReadUInt16(long Position)
|
||||||
{
|
{
|
||||||
#if DEBUG
|
#if DEBUG
|
||||||
|
@ -156,6 +159,7 @@ namespace ChocolArm64.Memory
|
||||||
return *((ushort*)(RamPtr + (uint)Position));
|
return *((ushort*)(RamPtr + (uint)Position));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public uint ReadUInt32(long Position)
|
public uint ReadUInt32(long Position)
|
||||||
{
|
{
|
||||||
#if DEBUG
|
#if DEBUG
|
||||||
|
@ -165,6 +169,7 @@ namespace ChocolArm64.Memory
|
||||||
return *((uint*)(RamPtr + (uint)Position));
|
return *((uint*)(RamPtr + (uint)Position));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public ulong ReadUInt64(long Position)
|
public ulong ReadUInt64(long Position)
|
||||||
{
|
{
|
||||||
#if DEBUG
|
#if DEBUG
|
||||||
|
@ -174,6 +179,47 @@ namespace ChocolArm64.Memory
|
||||||
return *((ulong*)(RamPtr + (uint)Position));
|
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)
|
public AVec ReadVector128(long Position)
|
||||||
{
|
{
|
||||||
#if DEBUG
|
#if DEBUG
|
||||||
|
@ -192,6 +238,7 @@ namespace ChocolArm64.Memory
|
||||||
public void WriteInt32(long Position, int Value) => WriteUInt32(Position, (uint)Value);
|
public void WriteInt32(long Position, int Value) => WriteUInt32(Position, (uint)Value);
|
||||||
public void WriteInt64(long Position, long Value) => WriteUInt64(Position, (ulong)Value);
|
public void WriteInt64(long Position, long Value) => WriteUInt64(Position, (ulong)Value);
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public void WriteByte(long Position, byte Value)
|
public void WriteByte(long Position, byte Value)
|
||||||
{
|
{
|
||||||
#if DEBUG
|
#if DEBUG
|
||||||
|
@ -201,6 +248,7 @@ namespace ChocolArm64.Memory
|
||||||
*((byte*)(RamPtr + (uint)Position)) = Value;
|
*((byte*)(RamPtr + (uint)Position)) = Value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public void WriteUInt16(long Position, ushort Value)
|
public void WriteUInt16(long Position, ushort Value)
|
||||||
{
|
{
|
||||||
#if DEBUG
|
#if DEBUG
|
||||||
|
@ -210,6 +258,7 @@ namespace ChocolArm64.Memory
|
||||||
*((ushort*)(RamPtr + (uint)Position)) = Value;
|
*((ushort*)(RamPtr + (uint)Position)) = Value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public void WriteUInt32(long Position, uint Value)
|
public void WriteUInt32(long Position, uint Value)
|
||||||
{
|
{
|
||||||
#if DEBUG
|
#if DEBUG
|
||||||
|
@ -219,6 +268,7 @@ namespace ChocolArm64.Memory
|
||||||
*((uint*)(RamPtr + (uint)Position)) = Value;
|
*((uint*)(RamPtr + (uint)Position)) = Value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public void WriteUInt64(long Position, ulong Value)
|
public void WriteUInt64(long Position, ulong Value)
|
||||||
{
|
{
|
||||||
#if DEBUG
|
#if DEBUG
|
||||||
|
@ -228,6 +278,47 @@ namespace ChocolArm64.Memory
|
||||||
*((ulong*)(RamPtr + (uint)Position)) = Value;
|
*((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)
|
public void WriteVector128(long Position, AVec Value)
|
||||||
{
|
{
|
||||||
#if DEBUG
|
#if DEBUG
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Threading;
|
|
||||||
|
|
||||||
namespace ChocolArm64.Memory
|
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)
|
public static byte[] ReadBytes(AMemory Memory, long Position, int Size)
|
||||||
{
|
{
|
||||||
byte[] Data = new byte[Size];
|
byte[] Data = new byte[Size];
|
||||||
|
|
|
@ -43,10 +43,10 @@ namespace ChocolArm64.State
|
||||||
{
|
{
|
||||||
switch ((APState)Index)
|
switch ((APState)Index)
|
||||||
{
|
{
|
||||||
case APState.VBit: return GetField(nameof(ARegisters.Overflow));
|
case APState.VBit: return GetField(nameof(AThreadState.Overflow));
|
||||||
case APState.CBit: return GetField(nameof(ARegisters.Carry));
|
case APState.CBit: return GetField(nameof(AThreadState.Carry));
|
||||||
case APState.ZBit: return GetField(nameof(ARegisters.Zero));
|
case APState.ZBit: return GetField(nameof(AThreadState.Zero));
|
||||||
case APState.NBit: return GetField(nameof(ARegisters.Negative));
|
case APState.NBit: return GetField(nameof(AThreadState.Negative));
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new InvalidOperationException();
|
throw new InvalidOperationException();
|
||||||
|
@ -56,38 +56,38 @@ namespace ChocolArm64.State
|
||||||
{
|
{
|
||||||
switch (Index)
|
switch (Index)
|
||||||
{
|
{
|
||||||
case 0: return GetField(nameof(ARegisters.X0));
|
case 0: return GetField(nameof(AThreadState.X0));
|
||||||
case 1: return GetField(nameof(ARegisters.X1));
|
case 1: return GetField(nameof(AThreadState.X1));
|
||||||
case 2: return GetField(nameof(ARegisters.X2));
|
case 2: return GetField(nameof(AThreadState.X2));
|
||||||
case 3: return GetField(nameof(ARegisters.X3));
|
case 3: return GetField(nameof(AThreadState.X3));
|
||||||
case 4: return GetField(nameof(ARegisters.X4));
|
case 4: return GetField(nameof(AThreadState.X4));
|
||||||
case 5: return GetField(nameof(ARegisters.X5));
|
case 5: return GetField(nameof(AThreadState.X5));
|
||||||
case 6: return GetField(nameof(ARegisters.X6));
|
case 6: return GetField(nameof(AThreadState.X6));
|
||||||
case 7: return GetField(nameof(ARegisters.X7));
|
case 7: return GetField(nameof(AThreadState.X7));
|
||||||
case 8: return GetField(nameof(ARegisters.X8));
|
case 8: return GetField(nameof(AThreadState.X8));
|
||||||
case 9: return GetField(nameof(ARegisters.X9));
|
case 9: return GetField(nameof(AThreadState.X9));
|
||||||
case 10: return GetField(nameof(ARegisters.X10));
|
case 10: return GetField(nameof(AThreadState.X10));
|
||||||
case 11: return GetField(nameof(ARegisters.X11));
|
case 11: return GetField(nameof(AThreadState.X11));
|
||||||
case 12: return GetField(nameof(ARegisters.X12));
|
case 12: return GetField(nameof(AThreadState.X12));
|
||||||
case 13: return GetField(nameof(ARegisters.X13));
|
case 13: return GetField(nameof(AThreadState.X13));
|
||||||
case 14: return GetField(nameof(ARegisters.X14));
|
case 14: return GetField(nameof(AThreadState.X14));
|
||||||
case 15: return GetField(nameof(ARegisters.X15));
|
case 15: return GetField(nameof(AThreadState.X15));
|
||||||
case 16: return GetField(nameof(ARegisters.X16));
|
case 16: return GetField(nameof(AThreadState.X16));
|
||||||
case 17: return GetField(nameof(ARegisters.X17));
|
case 17: return GetField(nameof(AThreadState.X17));
|
||||||
case 18: return GetField(nameof(ARegisters.X18));
|
case 18: return GetField(nameof(AThreadState.X18));
|
||||||
case 19: return GetField(nameof(ARegisters.X19));
|
case 19: return GetField(nameof(AThreadState.X19));
|
||||||
case 20: return GetField(nameof(ARegisters.X20));
|
case 20: return GetField(nameof(AThreadState.X20));
|
||||||
case 21: return GetField(nameof(ARegisters.X21));
|
case 21: return GetField(nameof(AThreadState.X21));
|
||||||
case 22: return GetField(nameof(ARegisters.X22));
|
case 22: return GetField(nameof(AThreadState.X22));
|
||||||
case 23: return GetField(nameof(ARegisters.X23));
|
case 23: return GetField(nameof(AThreadState.X23));
|
||||||
case 24: return GetField(nameof(ARegisters.X24));
|
case 24: return GetField(nameof(AThreadState.X24));
|
||||||
case 25: return GetField(nameof(ARegisters.X25));
|
case 25: return GetField(nameof(AThreadState.X25));
|
||||||
case 26: return GetField(nameof(ARegisters.X26));
|
case 26: return GetField(nameof(AThreadState.X26));
|
||||||
case 27: return GetField(nameof(ARegisters.X27));
|
case 27: return GetField(nameof(AThreadState.X27));
|
||||||
case 28: return GetField(nameof(ARegisters.X28));
|
case 28: return GetField(nameof(AThreadState.X28));
|
||||||
case 29: return GetField(nameof(ARegisters.X29));
|
case 29: return GetField(nameof(AThreadState.X29));
|
||||||
case 30: return GetField(nameof(ARegisters.X30));
|
case 30: return GetField(nameof(AThreadState.X30));
|
||||||
case 31: return GetField(nameof(ARegisters.X31));
|
case 31: return GetField(nameof(AThreadState.X31));
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new InvalidOperationException();
|
throw new InvalidOperationException();
|
||||||
|
@ -97,38 +97,38 @@ namespace ChocolArm64.State
|
||||||
{
|
{
|
||||||
switch (Index)
|
switch (Index)
|
||||||
{
|
{
|
||||||
case 0: return GetField(nameof(ARegisters.V0));
|
case 0: return GetField(nameof(AThreadState.V0));
|
||||||
case 1: return GetField(nameof(ARegisters.V1));
|
case 1: return GetField(nameof(AThreadState.V1));
|
||||||
case 2: return GetField(nameof(ARegisters.V2));
|
case 2: return GetField(nameof(AThreadState.V2));
|
||||||
case 3: return GetField(nameof(ARegisters.V3));
|
case 3: return GetField(nameof(AThreadState.V3));
|
||||||
case 4: return GetField(nameof(ARegisters.V4));
|
case 4: return GetField(nameof(AThreadState.V4));
|
||||||
case 5: return GetField(nameof(ARegisters.V5));
|
case 5: return GetField(nameof(AThreadState.V5));
|
||||||
case 6: return GetField(nameof(ARegisters.V6));
|
case 6: return GetField(nameof(AThreadState.V6));
|
||||||
case 7: return GetField(nameof(ARegisters.V7));
|
case 7: return GetField(nameof(AThreadState.V7));
|
||||||
case 8: return GetField(nameof(ARegisters.V8));
|
case 8: return GetField(nameof(AThreadState.V8));
|
||||||
case 9: return GetField(nameof(ARegisters.V9));
|
case 9: return GetField(nameof(AThreadState.V9));
|
||||||
case 10: return GetField(nameof(ARegisters.V10));
|
case 10: return GetField(nameof(AThreadState.V10));
|
||||||
case 11: return GetField(nameof(ARegisters.V11));
|
case 11: return GetField(nameof(AThreadState.V11));
|
||||||
case 12: return GetField(nameof(ARegisters.V12));
|
case 12: return GetField(nameof(AThreadState.V12));
|
||||||
case 13: return GetField(nameof(ARegisters.V13));
|
case 13: return GetField(nameof(AThreadState.V13));
|
||||||
case 14: return GetField(nameof(ARegisters.V14));
|
case 14: return GetField(nameof(AThreadState.V14));
|
||||||
case 15: return GetField(nameof(ARegisters.V15));
|
case 15: return GetField(nameof(AThreadState.V15));
|
||||||
case 16: return GetField(nameof(ARegisters.V16));
|
case 16: return GetField(nameof(AThreadState.V16));
|
||||||
case 17: return GetField(nameof(ARegisters.V17));
|
case 17: return GetField(nameof(AThreadState.V17));
|
||||||
case 18: return GetField(nameof(ARegisters.V18));
|
case 18: return GetField(nameof(AThreadState.V18));
|
||||||
case 19: return GetField(nameof(ARegisters.V19));
|
case 19: return GetField(nameof(AThreadState.V19));
|
||||||
case 20: return GetField(nameof(ARegisters.V20));
|
case 20: return GetField(nameof(AThreadState.V20));
|
||||||
case 21: return GetField(nameof(ARegisters.V21));
|
case 21: return GetField(nameof(AThreadState.V21));
|
||||||
case 22: return GetField(nameof(ARegisters.V22));
|
case 22: return GetField(nameof(AThreadState.V22));
|
||||||
case 23: return GetField(nameof(ARegisters.V23));
|
case 23: return GetField(nameof(AThreadState.V23));
|
||||||
case 24: return GetField(nameof(ARegisters.V24));
|
case 24: return GetField(nameof(AThreadState.V24));
|
||||||
case 25: return GetField(nameof(ARegisters.V25));
|
case 25: return GetField(nameof(AThreadState.V25));
|
||||||
case 26: return GetField(nameof(ARegisters.V26));
|
case 26: return GetField(nameof(AThreadState.V26));
|
||||||
case 27: return GetField(nameof(ARegisters.V27));
|
case 27: return GetField(nameof(AThreadState.V27));
|
||||||
case 28: return GetField(nameof(ARegisters.V28));
|
case 28: return GetField(nameof(AThreadState.V28));
|
||||||
case 29: return GetField(nameof(ARegisters.V29));
|
case 29: return GetField(nameof(AThreadState.V29));
|
||||||
case 30: return GetField(nameof(ARegisters.V30));
|
case 30: return GetField(nameof(AThreadState.V30));
|
||||||
case 31: return GetField(nameof(ARegisters.V31));
|
case 31: return GetField(nameof(AThreadState.V31));
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new InvalidOperationException();
|
throw new InvalidOperationException();
|
||||||
|
@ -136,7 +136,7 @@ namespace ChocolArm64.State
|
||||||
|
|
||||||
private FieldInfo GetField(string Name)
|
private FieldInfo GetField(string Name)
|
||||||
{
|
{
|
||||||
return typeof(ARegisters).GetField(Name);
|
return typeof(AThreadState).GetField(Name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -2,7 +2,7 @@ using System;
|
||||||
|
|
||||||
namespace ChocolArm64.State
|
namespace ChocolArm64.State
|
||||||
{
|
{
|
||||||
public class ARegisters
|
public class AThreadState
|
||||||
{
|
{
|
||||||
internal const int LRIndex = 30;
|
internal const int LRIndex = 30;
|
||||||
internal const int ZRIndex = 31;
|
internal const int ZRIndex = 31;
|
|
@ -26,7 +26,7 @@ namespace ChocolArm64.Translation
|
||||||
{
|
{
|
||||||
if (ILEmitter is AILOpCodeLoad Ld && AILEmitter.IsRegIndex(Ld.Index))
|
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.Flag: IntInputs |= ((1L << Ld.Index) << 32) & ~IntOutputs; break;
|
||||||
case AIoType.Int: IntInputs |= (1L << Ld.Index) & ~IntOutputs; break;
|
case AIoType.Int: IntInputs |= (1L << Ld.Index) & ~IntOutputs; break;
|
||||||
|
@ -37,7 +37,7 @@ namespace ChocolArm64.Translation
|
||||||
{
|
{
|
||||||
if (AILEmitter.IsRegIndex(St.Index))
|
if (AILEmitter.IsRegIndex(St.Index))
|
||||||
{
|
{
|
||||||
switch (St.IoType & AIoType.Mask)
|
switch (St.IoType)
|
||||||
{
|
{
|
||||||
case AIoType.Flag: IntOutputs |= (1L << St.Index) << 32; break;
|
case AIoType.Flag: IntOutputs |= (1L << St.Index) << 32; break;
|
||||||
case AIoType.Int: IntOutputs |= 1L << St.Index; break;
|
case AIoType.Int: IntOutputs |= 1L << St.Index; break;
|
||||||
|
|
|
@ -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 };
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -86,9 +86,6 @@ namespace ChocolArm64.Translation
|
||||||
ARegister Reg = Subroutine.Params[Index];
|
ARegister Reg = Subroutine.Params[Index];
|
||||||
|
|
||||||
Generator.EmitLdarg(Index + ParamsStart);
|
Generator.EmitLdarg(Index + ParamsStart);
|
||||||
|
|
||||||
AILConv.EmitConv(this, GetFieldType(Reg.Type), GetLocalType(Reg));
|
|
||||||
|
|
||||||
Generator.EmitStloc(GetLocalIndex(Reg));
|
Generator.EmitStloc(GetLocalIndex(Reg));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -37,6 +37,7 @@ namespace ChocolArm64.Translation
|
||||||
private const int Tmp2Index = -2;
|
private const int Tmp2Index = -2;
|
||||||
private const int Tmp3Index = -3;
|
private const int Tmp3Index = -3;
|
||||||
private const int Tmp4Index = -4;
|
private const int Tmp4Index = -4;
|
||||||
|
private const int Tmp5Index = -5;
|
||||||
|
|
||||||
public AILEmitterCtx(ATranslator Translator, ABlock[] Graph, ABlock Root)
|
public AILEmitterCtx(ATranslator Translator, ABlock[] Graph, ABlock Root)
|
||||||
{
|
{
|
||||||
|
@ -149,8 +150,8 @@ namespace ChocolArm64.Translation
|
||||||
|
|
||||||
if (LastCmpOp != null && LastFlagOp == LastCmpOp && BranchOps.ContainsKey(Cond))
|
if (LastCmpOp != null && LastFlagOp == LastCmpOp && BranchOps.ContainsKey(Cond))
|
||||||
{
|
{
|
||||||
Ldloc(Tmp3Index, AIoType.Int, GetIntType(LastCmpOp));
|
Ldloc(Tmp3Index, AIoType.Int, LastCmpOp.RegisterSize);
|
||||||
Ldloc(Tmp4Index, AIoType.Int, GetIntType(LastCmpOp));
|
Ldloc(Tmp4Index, AIoType.Int, LastCmpOp.RegisterSize);
|
||||||
|
|
||||||
if (LastCmpOp.Emitter == AInstEmit.Adds)
|
if (LastCmpOp.Emitter == AInstEmit.Adds)
|
||||||
{
|
{
|
||||||
|
@ -308,7 +309,7 @@ namespace ChocolArm64.Translation
|
||||||
|
|
||||||
public void EmitLdintzr(int Index)
|
public void EmitLdintzr(int Index)
|
||||||
{
|
{
|
||||||
if (Index != ARegisters.ZRIndex)
|
if (Index != AThreadState.ZRIndex)
|
||||||
{
|
{
|
||||||
EmitLdint(Index);
|
EmitLdint(Index);
|
||||||
}
|
}
|
||||||
|
@ -320,7 +321,7 @@ namespace ChocolArm64.Translation
|
||||||
|
|
||||||
public void EmitStintzr(int Index)
|
public void EmitStintzr(int Index)
|
||||||
{
|
{
|
||||||
if (Index != ARegisters.ZRIndex)
|
if (Index != AThreadState.ZRIndex)
|
||||||
{
|
{
|
||||||
EmitStint(Index);
|
EmitStint(Index);
|
||||||
}
|
}
|
||||||
|
@ -343,18 +344,15 @@ namespace ChocolArm64.Translation
|
||||||
public void EmitLdtmp() => EmitLdint(Tmp1Index);
|
public void EmitLdtmp() => EmitLdint(Tmp1Index);
|
||||||
public void EmitSttmp() => EmitStint(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 EmitLdint(int Index) => Ldloc(Index, AIoType.Int);
|
||||||
public void EmitStint(int Index) => Stloc(Index, AIoType.Int);
|
public void EmitStint(int Index) => Stloc(Index, AIoType.Int);
|
||||||
|
|
||||||
public void EmitLdvec(int Index) => Ldloc(Index, AIoType.Vector);
|
public void EmitLdvec(int Index) => Ldloc(Index, AIoType.Vector);
|
||||||
public void EmitStvec(int Index) => Stloc(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 EmitLdflg(int Index) => Ldloc(Index, AIoType.Flag);
|
||||||
public void EmitStflg(int Index)
|
public void EmitStflg(int Index)
|
||||||
{
|
{
|
||||||
|
@ -365,106 +363,17 @@ namespace ChocolArm64.Translation
|
||||||
|
|
||||||
private void Ldloc(int Index, AIoType IoType)
|
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)
|
private void Stloc(int Index, AIoType IoType)
|
||||||
{
|
{
|
||||||
ILBlock.Add(new AILOpCodeStore(Index, IoType, GetOutOperType(IoType)));
|
ILBlock.Add(new AILOpCodeStore(Index, IoType, CurrOp.RegisterSize));
|
||||||
}
|
|
||||||
|
|
||||||
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));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void EmitCallPropGet(Type ObjType, string PropName)
|
public void EmitCallPropGet(Type ObjType, string PropName)
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
using ChocolArm64.State;
|
using ChocolArm64.State;
|
||||||
using System;
|
|
||||||
using System.Reflection.Emit;
|
using System.Reflection.Emit;
|
||||||
|
|
||||||
namespace ChocolArm64.Translation
|
namespace ChocolArm64.Translation
|
||||||
|
@ -10,43 +9,40 @@ namespace ChocolArm64.Translation
|
||||||
|
|
||||||
public AIoType IoType { get; private set; }
|
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.IoType = IoType;
|
||||||
this.Index = Index;
|
this.Index = Index;
|
||||||
this.OperType = OperType;
|
this.RegisterSize = RegisterSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Emit(AILEmitter Context)
|
public void Emit(AILEmitter Context)
|
||||||
{
|
{
|
||||||
switch (IoType & AIoType.Mask)
|
switch (IoType)
|
||||||
{
|
{
|
||||||
case AIoType.Arg: EmitLdarg(Context, Index); break;
|
case AIoType.Arg: Context.Generator.EmitLdarg(Index); break;
|
||||||
case AIoType.Fields: EmitLdfld(Context, 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.Flag: EmitLdloc(Context, Index, ARegisterType.Flag); break;
|
||||||
case AIoType.Int: EmitLdloc(Context, Index, ARegisterType.Int); break;
|
case AIoType.Int: EmitLdloc(Context, Index, ARegisterType.Int); break;
|
||||||
case AIoType.Vector: EmitLdloc(Context, Index, ARegisterType.Vector); 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)
|
private void LoadLocals(AILEmitter Context, long Inputs, ARegisterType BaseType)
|
||||||
{
|
{
|
||||||
for (int Bit = 0; Bit < 64; Bit++)
|
for (int Bit = 0; Bit < 64; Bit++)
|
||||||
|
@ -57,26 +53,25 @@ namespace ChocolArm64.Translation
|
||||||
{
|
{
|
||||||
ARegister Reg = AILEmitter.GetRegFromBit(Bit, BaseType);
|
ARegister Reg = AILEmitter.GetRegFromBit(Bit, BaseType);
|
||||||
|
|
||||||
Context.Generator.EmitLdarg(ATranslatedSub.RegistersArgIdx);
|
Context.Generator.EmitLdarg(ATranslatedSub.StateArgIdx);
|
||||||
Context.Generator.Emit(OpCodes.Ldfld, Reg.GetField());
|
Context.Generator.Emit(OpCodes.Ldfld, Reg.GetField());
|
||||||
|
|
||||||
AILConv.EmitConv(
|
|
||||||
Context,
|
|
||||||
Context.GetFieldType(Reg.Type),
|
|
||||||
Context.GetLocalType(Reg));
|
|
||||||
|
|
||||||
Context.Generator.EmitStloc(Context.GetLocalIndex(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));
|
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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,52 +1,48 @@
|
||||||
using ChocolArm64.State;
|
using ChocolArm64.State;
|
||||||
using System;
|
|
||||||
using System.Reflection.Emit;
|
using System.Reflection.Emit;
|
||||||
|
|
||||||
namespace ChocolArm64.Translation
|
namespace ChocolArm64.Translation
|
||||||
{
|
{
|
||||||
struct AILOpCodeStore : IAILEmit
|
struct AILOpCodeStore : IAILEmit
|
||||||
{
|
{
|
||||||
public AIoType IoType { get; private set; }
|
|
||||||
|
|
||||||
public Type OperType { get; private set; }
|
|
||||||
|
|
||||||
public int Index { 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.IoType = IoType;
|
||||||
this.Index = Index;
|
this.Index = Index;
|
||||||
this.OperType = OperType;
|
this.RegisterSize = RegisterSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Emit(AILEmitter Context)
|
public void Emit(AILEmitter Context)
|
||||||
{
|
{
|
||||||
switch (IoType & AIoType.Mask)
|
switch (IoType)
|
||||||
{
|
{
|
||||||
case AIoType.Arg: EmitStarg(Context, Index); break;
|
case AIoType.Arg: Context.Generator.EmitStarg(Index); break;
|
||||||
case AIoType.Fields: EmitStfld(Context, 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.Flag: EmitStloc(Context, Index, ARegisterType.Flag); break;
|
||||||
case AIoType.Int: EmitStloc(Context, Index, ARegisterType.Int); break;
|
case AIoType.Int: EmitStloc(Context, Index, ARegisterType.Int); break;
|
||||||
case AIoType.Vector: EmitStloc(Context, Index, ARegisterType.Vector); 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)
|
private void StoreLocals(AILEmitter Context, long Outputs, ARegisterType BaseType)
|
||||||
{
|
{
|
||||||
for (int Bit = 0; Bit < 64; Bit++)
|
for (int Bit = 0; Bit < 64; Bit++)
|
||||||
|
@ -57,24 +53,23 @@ namespace ChocolArm64.Translation
|
||||||
{
|
{
|
||||||
ARegister Reg = AILEmitter.GetRegFromBit(Bit, BaseType);
|
ARegister Reg = AILEmitter.GetRegFromBit(Bit, BaseType);
|
||||||
|
|
||||||
Context.Generator.EmitLdarg(ATranslatedSub.RegistersArgIdx);
|
Context.Generator.EmitLdarg(ATranslatedSub.StateArgIdx);
|
||||||
Context.Generator.EmitLdloc(Context.GetLocalIndex(Reg));
|
Context.Generator.EmitLdloc(Context.GetLocalIndex(Reg));
|
||||||
|
|
||||||
AILConv.EmitConv(
|
|
||||||
Context,
|
|
||||||
Context.GetLocalType(Reg),
|
|
||||||
Context.GetFieldType(Reg.Type));
|
|
||||||
|
|
||||||
Context.Generator.Emit(OpCodes.Stfld, Reg.GetField());
|
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));
|
Context.Generator.EmitStloc(Context.GetLocalIndex(Reg));
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,9 +10,6 @@ namespace ChocolArm64.Translation
|
||||||
Flag,
|
Flag,
|
||||||
Int,
|
Int,
|
||||||
Float,
|
Float,
|
||||||
Vector,
|
Vector
|
||||||
Mask = 0xff,
|
|
||||||
VectorI = Vector | 1 << 8,
|
|
||||||
VectorF = Vector | 1 << 9
|
|
||||||
}
|
}
|
||||||
}
|
}
|
185
Ryujinx/Hid.cs
Normal file
185
Ryujinx/Hid.cs
Normal file
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
184
Ryujinx/Hid/HidController.cs
Normal file
184
Ryujinx/Hid/HidController.cs
Normal file
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
33
Ryujinx/Hid/HidKeyboard.cs
Normal file
33
Ryujinx/Hid/HidKeyboard.cs
Normal file
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
37
Ryujinx/Hid/HidMouse.cs
Normal file
37
Ryujinx/Hid/HidMouse.cs
Normal file
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
54
Ryujinx/Hid/HidTouchScreen.cs
Normal file
54
Ryujinx/Hid/HidTouchScreen.cs
Normal file
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
81
Ryujinx/Hid/HidUnknown.cs
Normal file
81
Ryujinx/Hid/HidUnknown.cs
Normal file
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
63
Ryujinx/Hid/JoyCon.cs
Normal file
63
Ryujinx/Hid/JoyCon.cs
Normal file
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,6 +1,6 @@
|
||||||
using ChocolArm64.Memory;
|
|
||||||
using Ryujinx.OsHle.Handles;
|
using Ryujinx.OsHle.Handles;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Threading;
|
||||||
|
|
||||||
namespace Ryujinx.OsHle
|
namespace Ryujinx.OsHle
|
||||||
{
|
{
|
||||||
|
@ -11,6 +11,8 @@ namespace Ryujinx.OsHle
|
||||||
private long CondVarAddress;
|
private long CondVarAddress;
|
||||||
private long Timeout;
|
private long Timeout;
|
||||||
|
|
||||||
|
private bool OwnsCondVarValue;
|
||||||
|
|
||||||
private List<HThread> WaitingThreads;
|
private List<HThread> WaitingThreads;
|
||||||
|
|
||||||
public CondVar(Process Process, long CondVarAddress, long Timeout)
|
public CondVar(Process Process, long CondVarAddress, long Timeout)
|
||||||
|
@ -24,34 +26,43 @@ namespace Ryujinx.OsHle
|
||||||
|
|
||||||
public void WaitForSignal(HThread Thread)
|
public void WaitForSignal(HThread Thread)
|
||||||
{
|
{
|
||||||
int Count = ReadCondVarValue();
|
int Count = Process.Memory.ReadInt32(CondVarAddress);
|
||||||
|
|
||||||
if (Count <= 0)
|
if (Count <= 0)
|
||||||
{
|
{
|
||||||
//FIXME: We shouldn't need to do that?
|
lock (WaitingThreads)
|
||||||
Process.Scheduler.Yield(Thread);
|
{
|
||||||
|
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)
|
ReleaseCondVarValue();
|
||||||
{
|
|
||||||
Process.Scheduler.WaitForSignal(Thread, (int)(Timeout / 1000000));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Process.Scheduler.WaitForSignal(Thread);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SetSignal(int Count)
|
public void SetSignal(HThread Thread, int Count)
|
||||||
{
|
{
|
||||||
lock (WaitingThreads)
|
lock (WaitingThreads)
|
||||||
{
|
{
|
||||||
|
@ -59,7 +70,11 @@ namespace Ryujinx.OsHle
|
||||||
{
|
{
|
||||||
Process.Scheduler.Signal(WaitingThreads.ToArray());
|
Process.Scheduler.Signal(WaitingThreads.ToArray());
|
||||||
|
|
||||||
WriteCondVarValue(WaitingThreads.Count);
|
AcquireCondVarValue();
|
||||||
|
|
||||||
|
Process.Memory.WriteInt32(CondVarAddress, WaitingThreads.Count);
|
||||||
|
|
||||||
|
ReleaseCondVarValue();
|
||||||
|
|
||||||
WaitingThreads.Clear();
|
WaitingThreads.Clear();
|
||||||
}
|
}
|
||||||
|
@ -85,19 +100,39 @@ namespace Ryujinx.OsHle
|
||||||
WaitingThreads.RemoveAt(HighestPrioIndex);
|
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)
|
Process.Memory.ReleaseAddress(CondVarAddress);
|
||||||
{
|
}
|
||||||
AMemoryHelper.WriteInt32Exclusive(Process.Memory, CondVarAddress, Value);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,13 +1,76 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
namespace Ryujinx.OsHle.Handles
|
namespace Ryujinx.OsHle.Handles
|
||||||
{
|
{
|
||||||
class HSharedMem
|
class HSharedMem
|
||||||
{
|
{
|
||||||
public long PhysPos { get; private set; }
|
private List<long> Positions;
|
||||||
public long VirtPos { get; set; }
|
|
||||||
|
public int PositionsCount => Positions.Count;
|
||||||
|
|
||||||
|
public EventHandler<EventArgs> MemoryMapped;
|
||||||
|
public EventHandler<EventArgs> MemoryUnmapped;
|
||||||
|
|
||||||
public HSharedMem(long PhysPos)
|
public HSharedMem(long PhysPos)
|
||||||
{
|
{
|
||||||
this.PhysPos = PhysPos;
|
Positions = new List<long>();
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,4 +1,5 @@
|
||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Concurrent;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
|
|
||||||
|
@ -6,19 +7,8 @@ namespace Ryujinx.OsHle.Handles
|
||||||
{
|
{
|
||||||
class KProcessScheduler : IDisposable
|
class KProcessScheduler : IDisposable
|
||||||
{
|
{
|
||||||
private enum ThreadState
|
|
||||||
{
|
|
||||||
WaitingToRun,
|
|
||||||
WaitingSignal,
|
|
||||||
Running
|
|
||||||
}
|
|
||||||
|
|
||||||
private class SchedulerThread : IDisposable
|
private class SchedulerThread : IDisposable
|
||||||
{
|
{
|
||||||
public bool Signaled { get; set; }
|
|
||||||
|
|
||||||
public ThreadState State { get; set; }
|
|
||||||
|
|
||||||
public HThread Thread { get; private set; }
|
public HThread Thread { get; private set; }
|
||||||
|
|
||||||
public AutoResetEvent WaitEvent { get; private set; }
|
public AutoResetEvent WaitEvent { get; private set; }
|
||||||
|
@ -44,9 +34,70 @@ namespace Ryujinx.OsHle.Handles
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private Dictionary<HThread, SchedulerThread> AllThreads;
|
private class ThreadQueue
|
||||||
|
{
|
||||||
|
private List<SchedulerThread> Threads;
|
||||||
|
|
||||||
private Queue<SchedulerThread>[] WaitingThreads;
|
public ThreadQueue()
|
||||||
|
{
|
||||||
|
Threads = new List<SchedulerThread>();
|
||||||
|
}
|
||||||
|
|
||||||
|
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<HThread, SchedulerThread> AllThreads;
|
||||||
|
|
||||||
|
private ThreadQueue[] WaitingToRun;
|
||||||
|
|
||||||
private HashSet<int> ActiveProcessors;
|
private HashSet<int> ActiveProcessors;
|
||||||
|
|
||||||
|
@ -54,13 +105,13 @@ namespace Ryujinx.OsHle.Handles
|
||||||
|
|
||||||
public KProcessScheduler()
|
public KProcessScheduler()
|
||||||
{
|
{
|
||||||
AllThreads = new Dictionary<HThread, SchedulerThread>();
|
AllThreads = new ConcurrentDictionary<HThread, SchedulerThread>();
|
||||||
|
|
||||||
WaitingThreads = new Queue<SchedulerThread>[4];
|
WaitingToRun = new ThreadQueue[4];
|
||||||
|
|
||||||
for (int Index = 0; Index < WaitingThreads.Length; Index++)
|
for (int Index = 0; Index < 4; Index++)
|
||||||
{
|
{
|
||||||
WaitingThreads[Index] = new Queue<SchedulerThread>();
|
WaitingToRun[Index] = new ThreadQueue();
|
||||||
}
|
}
|
||||||
|
|
||||||
ActiveProcessors = new HashSet<int>();
|
ActiveProcessors = new HashSet<int>();
|
||||||
|
@ -72,132 +123,171 @@ namespace Ryujinx.OsHle.Handles
|
||||||
{
|
{
|
||||||
lock (SchedLock)
|
lock (SchedLock)
|
||||||
{
|
{
|
||||||
if (AllThreads.ContainsKey(Thread))
|
SchedulerThread SchedThread = new SchedulerThread(Thread);
|
||||||
|
|
||||||
|
if (!AllThreads.TryAdd(Thread, SchedThread))
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
SchedulerThread SchedThread = new SchedulerThread(Thread);
|
|
||||||
|
|
||||||
AllThreads.Add(Thread, SchedThread);
|
|
||||||
|
|
||||||
if (!ActiveProcessors.Contains(Thread.ProcessorId))
|
if (!ActiveProcessors.Contains(Thread.ProcessorId))
|
||||||
{
|
{
|
||||||
ActiveProcessors.Add(Thread.ProcessorId);
|
ActiveProcessors.Add(Thread.ProcessorId);
|
||||||
|
|
||||||
Thread.Thread.Execute();
|
Thread.Thread.Execute();
|
||||||
|
|
||||||
SetThreadAsRunning(SchedThread);
|
Logging.Debug($"{GetDbgThreadInfo(Thread)} running.");
|
||||||
|
|
||||||
SchedThread.State = ThreadState.Running;
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
InsertSorted(SchedThread);
|
WaitingToRun[Thread.ProcessorId].Push(SchedThread);
|
||||||
|
|
||||||
SchedThread.State = ThreadState.WaitingToRun;
|
|
||||||
|
|
||||||
Logging.Debug($"{GetDbgThreadInfo(SchedThread.Thread)} waiting to run.");
|
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.");
|
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)
|
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;
|
SchedulerThread SchedThread;
|
||||||
|
|
||||||
|
Logging.Debug($"{GetDbgThreadInfo(Thread)} yielded execution.");
|
||||||
|
|
||||||
lock (SchedLock)
|
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))
|
if (!AllThreads.TryGetValue(Thread, out SchedThread))
|
||||||
{
|
{
|
||||||
return;
|
Logging.Error($"{GetDbgThreadInfo(Thread)} was not found on the scheduler queue!");
|
||||||
}
|
|
||||||
|
|
||||||
if (SchedThread.Signaled && SchedThread.State == ThreadState.WaitingSignal)
|
|
||||||
{
|
|
||||||
SchedThread.Signaled = false;
|
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
ActiveProcessors.Remove(Thread.ProcessorId);
|
WaitingToRun[Thread.ProcessorId].Push(SchedThread);
|
||||||
|
|
||||||
SchedThread.State = State;
|
|
||||||
|
|
||||||
TryRunningWaitingThead(SchedThread.Thread.ProcessorId);
|
|
||||||
|
|
||||||
if (State == ThreadState.WaitingSignal)
|
|
||||||
{
|
|
||||||
InsertSorted(SchedThread);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
InsertAtEnd(SchedThread);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (TimeoutMs >= 0)
|
SchedThread.WaitEvent.WaitOne();
|
||||||
{
|
|
||||||
Logging.Debug($"{GetDbgThreadInfo(SchedThread.Thread)} waiting with timeout of {TimeoutMs}ms.");
|
|
||||||
|
|
||||||
SchedThread.WaitEvent.WaitOne(TimeoutMs);
|
Logging.Debug($"{GetDbgThreadInfo(Thread)} resuming execution...");
|
||||||
|
}
|
||||||
|
|
||||||
|
private void RunThread(SchedulerThread SchedThread)
|
||||||
|
{
|
||||||
|
if (!SchedThread.Thread.Thread.Execute())
|
||||||
|
{
|
||||||
|
SchedThread.WaitEvent.Set();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Logging.Debug($"{GetDbgThreadInfo(SchedThread.Thread)} waiting indefinitely.");
|
Logging.Debug($"{GetDbgThreadInfo(SchedThread.Thread)} running.");
|
||||||
|
|
||||||
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();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -205,107 +295,21 @@ namespace Ryujinx.OsHle.Handles
|
||||||
{
|
{
|
||||||
lock (SchedLock)
|
lock (SchedLock)
|
||||||
{
|
{
|
||||||
HashSet<int> SignaledProcessorIds = new HashSet<int>();
|
|
||||||
|
|
||||||
foreach (HThread Thread in Threads)
|
foreach (HThread Thread in Threads)
|
||||||
{
|
{
|
||||||
Logging.Debug($"{GetDbgThreadInfo(Thread)} signaled.");
|
|
||||||
|
|
||||||
if (AllThreads.TryGetValue(Thread, out SchedulerThread SchedThread))
|
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<SchedulerThread> CoreQueue = WaitingThreads[Thread.ProcessorId];
|
|
||||||
|
|
||||||
Queue<SchedulerThread> TempQueue = new Queue<SchedulerThread>(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)
|
private string GetDbgThreadInfo(HThread Thread)
|
||||||
{
|
{
|
||||||
return $"Thread {Thread.ThreadId} (core {Thread.ProcessorId}) prio {Thread.Priority}";
|
return $"Thread {Thread.ThreadId} (core {Thread.ProcessorId}) prio {Thread.Priority}";
|
||||||
|
@ -320,14 +324,9 @@ namespace Ryujinx.OsHle.Handles
|
||||||
{
|
{
|
||||||
if (Disposing)
|
if (Disposing)
|
||||||
{
|
{
|
||||||
foreach (Queue<SchedulerThread> SchedThreads in WaitingThreads)
|
foreach (SchedulerThread SchedThread in AllThreads.Values)
|
||||||
{
|
{
|
||||||
foreach (SchedulerThread SchedThread in SchedThreads)
|
SchedThread.Dispose();
|
||||||
{
|
|
||||||
SchedThread.Dispose();
|
|
||||||
}
|
|
||||||
|
|
||||||
SchedThreads.Clear();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,6 +4,7 @@ using Ryujinx.OsHle.Handles;
|
||||||
using Ryujinx.OsHle.Utilities;
|
using Ryujinx.OsHle.Utilities;
|
||||||
using System.Collections.Concurrent;
|
using System.Collections.Concurrent;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
using System;
|
||||||
|
|
||||||
namespace Ryujinx.OsHle
|
namespace Ryujinx.OsHle
|
||||||
{
|
{
|
||||||
|
@ -30,6 +31,8 @@ namespace Ryujinx.OsHle
|
||||||
|
|
||||||
private ConcurrentDictionary<int, Process> Processes;
|
private ConcurrentDictionary<int, Process> Processes;
|
||||||
|
|
||||||
|
private HSharedMem HidSharedMem;
|
||||||
|
|
||||||
private AMemoryAlloc Allocator;
|
private AMemoryAlloc Allocator;
|
||||||
|
|
||||||
private Switch Ns;
|
private Switch Ns;
|
||||||
|
@ -55,7 +58,12 @@ namespace Ryujinx.OsHle
|
||||||
HidOffset = Allocator.Alloc(HidSize);
|
HidOffset = Allocator.Alloc(HidSize);
|
||||||
FontOffset = Allocator.Alloc(FontSize);
|
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));
|
FontHandle = Handles.GenerateId(new HSharedMem(FontOffset));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -79,6 +87,8 @@ namespace Ryujinx.OsHle
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Logging.Info($"Loading {Path.GetFileNameWithoutExtension(File)}...");
|
||||||
|
|
||||||
using (FileStream Input = new FileStream(File, FileMode.Open))
|
using (FileStream Input = new FileStream(File, FileMode.Open))
|
||||||
{
|
{
|
||||||
Nso Program = new Nso(Input);
|
Nso Program = new Nso(Input);
|
||||||
|
@ -102,7 +112,7 @@ namespace Ryujinx.OsHle
|
||||||
Processes.TryAdd(ProcessId, MainProcess);
|
Processes.TryAdd(ProcessId, MainProcess);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void LoadProgram(string FileName)
|
public void LoadProgram(string FileName)
|
||||||
{
|
{
|
||||||
int ProcessId = IdGen.GenerateId();
|
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)
|
internal bool TryGetProcess(int ProcessId, out Process Process)
|
||||||
{
|
{
|
||||||
if (!Processes.TryGetValue(ProcessId, out Process))
|
if (!Processes.TryGetValue(ProcessId, out Process))
|
||||||
|
@ -161,11 +188,15 @@ namespace Ryujinx.OsHle
|
||||||
Handles.Delete(Handle);
|
Handles.Delete(Handle);
|
||||||
}
|
}
|
||||||
|
|
||||||
public long GetVirtHidOffset()
|
private void HidInit(object sender, EventArgs e)
|
||||||
{
|
{
|
||||||
HSharedMem HidSharedMem = Handles.GetData<HSharedMem>(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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,4 +1,3 @@
|
||||||
using ChocolArm64.Memory;
|
|
||||||
using Ryujinx.OsHle.Handles;
|
using Ryujinx.OsHle.Handles;
|
||||||
using System.Collections.Concurrent;
|
using System.Collections.Concurrent;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
|
@ -13,6 +12,8 @@ namespace Ryujinx.OsHle
|
||||||
|
|
||||||
private long MutexAddress;
|
private long MutexAddress;
|
||||||
|
|
||||||
|
private bool OwnsMutexValue;
|
||||||
|
|
||||||
private object EnterWaitLock;
|
private object EnterWaitLock;
|
||||||
|
|
||||||
private ConcurrentQueue<HThread> WaitingThreads;
|
private ConcurrentQueue<HThread> WaitingThreads;
|
||||||
|
@ -31,9 +32,11 @@ namespace Ryujinx.OsHle
|
||||||
|
|
||||||
public void WaitForLock(HThread RequestingThread, int RequestingThreadHandle)
|
public void WaitForLock(HThread RequestingThread, int RequestingThreadHandle)
|
||||||
{
|
{
|
||||||
|
AcquireMutexValue();
|
||||||
|
|
||||||
lock (EnterWaitLock)
|
lock (EnterWaitLock)
|
||||||
{
|
{
|
||||||
int CurrentThreadHandle = ReadMutexValue() & ~MutexHasListenersMask;
|
int CurrentThreadHandle = Process.Memory.ReadInt32(MutexAddress) & ~MutexHasListenersMask;
|
||||||
|
|
||||||
if (CurrentThreadHandle == RequestingThreadHandle ||
|
if (CurrentThreadHandle == RequestingThreadHandle ||
|
||||||
CurrentThreadHandle == 0)
|
CurrentThreadHandle == 0)
|
||||||
|
@ -41,7 +44,9 @@ namespace Ryujinx.OsHle
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
WriteMutexValue(CurrentThreadHandle | MutexHasListenersMask);
|
Process.Memory.WriteInt32(MutexAddress, CurrentThreadHandle | MutexHasListenersMask);
|
||||||
|
|
||||||
|
ReleaseMutexValue();
|
||||||
|
|
||||||
WaitingThreads.Enqueue(RequestingThread);
|
WaitingThreads.Enqueue(RequestingThread);
|
||||||
}
|
}
|
||||||
|
@ -51,24 +56,32 @@ namespace Ryujinx.OsHle
|
||||||
|
|
||||||
public void GiveUpLock(int ThreadHandle)
|
public void GiveUpLock(int ThreadHandle)
|
||||||
{
|
{
|
||||||
|
AcquireMutexValue();
|
||||||
|
|
||||||
lock (EnterWaitLock)
|
lock (EnterWaitLock)
|
||||||
{
|
{
|
||||||
int CurrentThread = ReadMutexValue() & ~MutexHasListenersMask;
|
int CurrentThread = Process.Memory.ReadInt32(MutexAddress) & ~MutexHasListenersMask;
|
||||||
|
|
||||||
if (CurrentThread == ThreadHandle)
|
if (CurrentThread == ThreadHandle)
|
||||||
{
|
{
|
||||||
Unlock();
|
Unlock();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ReleaseMutexValue();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Unlock()
|
public void Unlock()
|
||||||
{
|
{
|
||||||
|
AcquireMutexValue();
|
||||||
|
|
||||||
lock (EnterWaitLock)
|
lock (EnterWaitLock)
|
||||||
{
|
{
|
||||||
int HasListeners = WaitingThreads.Count > 1 ? MutexHasListenersMask : 0;
|
int HasListeners = WaitingThreads.Count > 1 ? MutexHasListenersMask : 0;
|
||||||
|
|
||||||
WriteMutexValue(HasListeners);
|
Process.Memory.WriteInt32(MutexAddress, HasListeners);
|
||||||
|
|
||||||
|
ReleaseMutexValue();
|
||||||
|
|
||||||
HThread[] UnlockedThreads = new HThread[WaitingThreads.Count];
|
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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -101,17 +101,17 @@ namespace Ryujinx.OsHle.Objects.Aud
|
||||||
|
|
||||||
KeysQueue.Enqueue(BufferId);
|
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))
|
using (MemoryStream MS = new MemoryStream(AudioOutBuffer))
|
||||||
{
|
{
|
||||||
BinaryReader Reader = new BinaryReader(MS);
|
BinaryReader Reader = new BinaryReader(MS);
|
||||||
long PointerToSampleDataPointer = Reader.ReadInt64();
|
long PointerNextBuffer = Reader.ReadInt64();
|
||||||
long PointerToSampleData = Reader.ReadInt64();
|
long PointerSampleBuffer = Reader.ReadInt64();
|
||||||
long CapacitySampleBuffer = Reader.ReadInt64();
|
long CapacitySampleBuffer = Reader.ReadInt64();
|
||||||
long SizeDataSampleBuffer = Reader.ReadInt64();
|
long SizeDataInSampleBuffer = Reader.ReadInt64();
|
||||||
long Unknown = 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)
|
if (OpenALInstalled)
|
||||||
{
|
{
|
||||||
|
@ -146,7 +146,8 @@ namespace Ryujinx.OsHle.Objects.Aud
|
||||||
|
|
||||||
AMemoryHelper.WriteBytes(Context.Memory, Context.Request.ReceiveBuff[0].Position, BitConverter.GetBytes(TempKey));
|
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)
|
if (OpenALInstalled)
|
||||||
{
|
{
|
||||||
|
|
|
@ -28,7 +28,7 @@ namespace Ryujinx.OsHle.Objects.Time
|
||||||
{
|
{
|
||||||
DateTime CurrentTime = DateTime.Now;
|
DateTime CurrentTime = DateTime.Now;
|
||||||
|
|
||||||
if (ClockType == SystemClockType.Standard ||
|
if (ClockType == SystemClockType.User ||
|
||||||
ClockType == SystemClockType.Network)
|
ClockType == SystemClockType.Network)
|
||||||
{
|
{
|
||||||
CurrentTime = CurrentTime.ToUniversalTime();
|
CurrentTime = CurrentTime.ToUniversalTime();
|
||||||
|
|
|
@ -2,7 +2,7 @@ namespace Ryujinx.OsHle.Objects.Time
|
||||||
{
|
{
|
||||||
enum SystemClockType
|
enum SystemClockType
|
||||||
{
|
{
|
||||||
Standard,
|
User,
|
||||||
Network,
|
Network,
|
||||||
Local
|
Local
|
||||||
}
|
}
|
||||||
|
|
|
@ -70,6 +70,8 @@ namespace Ryujinx.OsHle
|
||||||
|
|
||||||
public void LoadProgram(IExecutable Program)
|
public void LoadProgram(IExecutable Program)
|
||||||
{
|
{
|
||||||
|
Logging.Info($"Image base at 0x{ImageBase:x16}.");
|
||||||
|
|
||||||
Executable Executable = new Executable(Program, Memory, ImageBase);
|
Executable Executable = new Executable(Program, Memory, ImageBase);
|
||||||
|
|
||||||
Executables.Add(Executable);
|
Executables.Add(Executable);
|
||||||
|
@ -174,19 +176,19 @@ namespace Ryujinx.OsHle
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
Thread.Registers.Break += BreakHandler;
|
Thread.ThreadState.Break += BreakHandler;
|
||||||
Thread.Registers.SvcCall += SvcHandler.SvcCall;
|
Thread.ThreadState.SvcCall += SvcHandler.SvcCall;
|
||||||
Thread.Registers.Undefined += UndefinedHandler;
|
Thread.ThreadState.Undefined += UndefinedHandler;
|
||||||
Thread.Registers.ProcessId = ProcessId;
|
Thread.ThreadState.ProcessId = ProcessId;
|
||||||
Thread.Registers.ThreadId = Ns.Os.IdGen.GenerateId();
|
Thread.ThreadState.ThreadId = Ns.Os.IdGen.GenerateId();
|
||||||
Thread.Registers.Tpidr = TlsPageAddr + TlsSlot * TlsSize;
|
Thread.ThreadState.Tpidr = TlsPageAddr + TlsSlot * TlsSize;
|
||||||
Thread.Registers.X0 = (ulong)ArgsPtr;
|
Thread.ThreadState.X0 = (ulong)ArgsPtr;
|
||||||
Thread.Registers.X1 = (ulong)Handle;
|
Thread.ThreadState.X1 = (ulong)Handle;
|
||||||
Thread.Registers.X31 = (ulong)StackTop;
|
Thread.ThreadState.X31 = (ulong)StackTop;
|
||||||
|
|
||||||
Thread.WorkFinished += ThreadFinished;
|
Thread.WorkFinished += ThreadFinished;
|
||||||
|
|
||||||
ThreadsByTpidr.TryAdd(Thread.Registers.Tpidr, ThreadHnd);
|
ThreadsByTpidr.TryAdd(Thread.ThreadState.Tpidr, ThreadHnd);
|
||||||
|
|
||||||
return Handle;
|
return Handle;
|
||||||
}
|
}
|
||||||
|
@ -218,7 +220,7 @@ namespace Ryujinx.OsHle
|
||||||
{
|
{
|
||||||
if (sender is AThread Thread)
|
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);
|
Ns.Os.IdGen.DeleteId(Thread.ThreadId);
|
||||||
}
|
}
|
||||||
|
@ -229,9 +231,14 @@ namespace Ryujinx.OsHle
|
||||||
return (int)((Position - TlsPageAddr) / TlsSize);
|
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()
|
public void Dispose()
|
||||||
|
|
|
@ -8,7 +8,7 @@ namespace Ryujinx.OsHle.Services
|
||||||
{
|
{
|
||||||
public static long TimeGetStandardUserSystemClock(ServiceCtx Context)
|
public static long TimeGetStandardUserSystemClock(ServiceCtx Context)
|
||||||
{
|
{
|
||||||
MakeObject(Context, new ISystemClock(SystemClockType.Standard));
|
MakeObject(Context, new ISystemClock(SystemClockType.User));
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,7 +7,7 @@ namespace Ryujinx.OsHle.Svc
|
||||||
{
|
{
|
||||||
partial class SvcHandler
|
partial class SvcHandler
|
||||||
{
|
{
|
||||||
private delegate void SvcFunc(ARegisters Registers);
|
private delegate void SvcFunc(AThreadState ThreadState);
|
||||||
|
|
||||||
private Dictionary<int, SvcFunc> SvcFuncs;
|
private Dictionary<int, SvcFunc> SvcFuncs;
|
||||||
|
|
||||||
|
@ -25,6 +25,7 @@ namespace Ryujinx.OsHle.Svc
|
||||||
{ 0x03, SvcSetMemoryAttribute },
|
{ 0x03, SvcSetMemoryAttribute },
|
||||||
{ 0x04, SvcMapMemory },
|
{ 0x04, SvcMapMemory },
|
||||||
{ 0x06, SvcQueryMemory },
|
{ 0x06, SvcQueryMemory },
|
||||||
|
{ 0x07, SvcExitProcess },
|
||||||
{ 0x08, SvcCreateThread },
|
{ 0x08, SvcCreateThread },
|
||||||
{ 0x09, SvcStartThread },
|
{ 0x09, SvcStartThread },
|
||||||
{ 0x0b, SvcSleepThread },
|
{ 0x0b, SvcSleepThread },
|
||||||
|
@ -60,15 +61,15 @@ namespace Ryujinx.OsHle.Svc
|
||||||
|
|
||||||
public void SvcCall(object sender, AInstExceptEventArgs e)
|
public void SvcCall(object sender, AInstExceptEventArgs e)
|
||||||
{
|
{
|
||||||
ARegisters Registers = (ARegisters)sender;
|
AThreadState ThreadState = (AThreadState)sender;
|
||||||
|
|
||||||
if (SvcFuncs.TryGetValue(e.Id, out SvcFunc Func))
|
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
|
else
|
||||||
{
|
{
|
||||||
|
|
|
@ -6,43 +6,43 @@ namespace Ryujinx.OsHle.Svc
|
||||||
{
|
{
|
||||||
partial class SvcHandler
|
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);
|
Memory.Manager.SetHeapSize(Size, (int)MemoryType.Heap);
|
||||||
|
|
||||||
Registers.X0 = (int)SvcResult.Success;
|
ThreadState.X0 = (int)SvcResult.Success;
|
||||||
Registers.X1 = (ulong)Memory.Manager.HeapAddr;
|
ThreadState.X1 = (ulong)Memory.Manager.HeapAddr;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void SvcSetMemoryAttribute(ARegisters Registers)
|
private void SvcSetMemoryAttribute(AThreadState ThreadState)
|
||||||
{
|
{
|
||||||
long Position = (long)Registers.X0;
|
long Position = (long)ThreadState.X0;
|
||||||
long Size = (long)Registers.X1;
|
long Size = (long)ThreadState.X1;
|
||||||
int State0 = (int)Registers.X2;
|
int State0 = (int)ThreadState.X2;
|
||||||
int State1 = (int)Registers.X3;
|
int State1 = (int)ThreadState.X3;
|
||||||
|
|
||||||
//TODO
|
//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 Dst = (long)ThreadState.X0;
|
||||||
long Src = (long)Registers.X1;
|
long Src = (long)ThreadState.X1;
|
||||||
long Size = (long)Registers.X2;
|
long Size = (long)ThreadState.X2;
|
||||||
|
|
||||||
Memory.Manager.MapMirror(Src, Dst, Size, (int)MemoryType.MappedMemory);
|
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 InfoPtr = (long)ThreadState.X0;
|
||||||
long Position = (long)Registers.X2;
|
long Position = (long)ThreadState.X2;
|
||||||
|
|
||||||
AMemoryMapInfo MapInfo = Memory.Manager.GetMapInfo(Position);
|
AMemoryMapInfo MapInfo = Memory.Manager.GetMapInfo(Position);
|
||||||
|
|
||||||
|
@ -59,55 +59,52 @@ namespace Ryujinx.OsHle.Svc
|
||||||
|
|
||||||
//TODO: X1.
|
//TODO: X1.
|
||||||
|
|
||||||
Registers.X0 = (int)SvcResult.Success;
|
ThreadState.X0 = (int)SvcResult.Success;
|
||||||
Registers.X1 = 0;
|
ThreadState.X1 = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void SvcMapSharedMemory(ARegisters Registers)
|
private void SvcMapSharedMemory(AThreadState ThreadState)
|
||||||
{
|
{
|
||||||
int Handle = (int)Registers.X0;
|
int Handle = (int)ThreadState.X0;
|
||||||
long Position = (long)Registers.X1;
|
long Src = (long)ThreadState.X1;
|
||||||
long Size = (long)Registers.X2;
|
long Size = (long)ThreadState.X2;
|
||||||
int Perm = (int)Registers.X3;
|
int Perm = (int)ThreadState.X3;
|
||||||
|
|
||||||
HSharedMem HndData = Ns.Os.Handles.GetData<HSharedMem>(Handle);
|
HSharedMem SharedMem = Ns.Os.Handles.GetData<HSharedMem>(Handle);
|
||||||
|
|
||||||
if (HndData != null)
|
if (SharedMem != null)
|
||||||
{
|
{
|
||||||
long Src = Position;
|
SharedMem.AddVirtualPosition(Src);
|
||||||
long Dst = HndData.PhysPos;
|
|
||||||
|
|
||||||
HndData.VirtPos = Src;
|
Memory.Manager.MapPhys(Src, Size, (int)MemoryType.SharedMemory, (AMemoryPerm)Perm);
|
||||||
|
|
||||||
Memory.Manager.MapPhys(Position, Size, (int)MemoryType.SharedMemory, (AMemoryPerm)Perm);
|
ThreadState.X0 = (int)SvcResult.Success;
|
||||||
|
|
||||||
Registers.X0 = (int)SvcResult.Success;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//TODO: Error codes.
|
//TODO: Error codes.
|
||||||
}
|
}
|
||||||
|
|
||||||
private void SvcUnmapSharedMemory(ARegisters Registers)
|
private void SvcUnmapSharedMemory(AThreadState ThreadState)
|
||||||
{
|
{
|
||||||
int Handle = (int)Registers.X0;
|
int Handle = (int)ThreadState.X0;
|
||||||
long Position = (long)Registers.X1;
|
long Position = (long)ThreadState.X1;
|
||||||
long Size = (long)Registers.X2;
|
long Size = (long)ThreadState.X2;
|
||||||
|
|
||||||
HSharedMem HndData = Ns.Os.Handles.GetData<HSharedMem>(Handle);
|
HSharedMem HndData = Ns.Os.Handles.GetData<HSharedMem>(Handle);
|
||||||
|
|
||||||
if (HndData != null)
|
if (HndData != null)
|
||||||
{
|
{
|
||||||
Registers.X0 = (int)SvcResult.Success;
|
ThreadState.X0 = (int)SvcResult.Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
//TODO: Error codes.
|
//TODO: Error codes.
|
||||||
}
|
}
|
||||||
|
|
||||||
private void SvcCreateTransferMemory(ARegisters Registers)
|
private void SvcCreateTransferMemory(AThreadState ThreadState)
|
||||||
{
|
{
|
||||||
long Position = (long)Registers.X1;
|
long Position = (long)ThreadState.X1;
|
||||||
long Size = (long)Registers.X2;
|
long Size = (long)ThreadState.X2;
|
||||||
int Perm = (int)Registers.X3;
|
int Perm = (int)ThreadState.X3;
|
||||||
|
|
||||||
AMemoryMapInfo MapInfo = Memory.Manager.GetMapInfo(Position);
|
AMemoryMapInfo MapInfo = Memory.Manager.GetMapInfo(Position);
|
||||||
|
|
||||||
|
@ -117,8 +114,8 @@ namespace Ryujinx.OsHle.Svc
|
||||||
|
|
||||||
int Handle = Ns.Os.Handles.GenerateId(HndData);
|
int Handle = Ns.Os.Handles.GenerateId(HndData);
|
||||||
|
|
||||||
Registers.X1 = (ulong)Handle;
|
ThreadState.X1 = (ulong)Handle;
|
||||||
Registers.X0 = (int)SvcResult.Success;
|
ThreadState.X0 = (int)SvcResult.Success;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -4,60 +4,57 @@ using Ryujinx.OsHle.Exceptions;
|
||||||
using Ryujinx.OsHle.Handles;
|
using Ryujinx.OsHle.Handles;
|
||||||
using Ryujinx.OsHle.Ipc;
|
using Ryujinx.OsHle.Ipc;
|
||||||
using System;
|
using System;
|
||||||
|
using System.Threading;
|
||||||
|
|
||||||
namespace Ryujinx.OsHle.Svc
|
namespace Ryujinx.OsHle.Svc
|
||||||
{
|
{
|
||||||
partial class SvcHandler
|
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);
|
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.
|
//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;
|
long HandlesPtr = (long)ThreadState.X0;
|
||||||
int HandlesCount = (int)Registers.X2;
|
int HandlesCount = (int)ThreadState.X2;
|
||||||
long Timeout = (long)Registers.X3;
|
long Timeout = (long)ThreadState.X3;
|
||||||
|
|
||||||
//TODO: Implement events.
|
//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.Suspend(CurrThread.ProcessorId);
|
||||||
{
|
Process.Scheduler.Resume(CurrThread);
|
||||||
Process.Scheduler.Yield(Thread);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Logging.Error($"Thread with TPIDR_EL0 0x{Registers.Tpidr:x16} not found!");
|
|
||||||
}
|
|
||||||
|
|
||||||
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 StackPtr = (long)ThreadState.X0;
|
||||||
long NamePtr = (long)Registers.X1;
|
long NamePtr = (long)ThreadState.X1;
|
||||||
|
|
||||||
string Name = AMemoryHelper.ReadAsciiString(Memory, NamePtr, 8);
|
string Name = AMemoryHelper.ReadAsciiString(Memory, NamePtr, 8);
|
||||||
|
|
||||||
|
@ -66,37 +63,41 @@ namespace Ryujinx.OsHle.Svc
|
||||||
|
|
||||||
HSession Session = new HSession(Name);
|
HSession Session = new HSession(Name);
|
||||||
|
|
||||||
Registers.X1 = (ulong)Ns.Os.Handles.GenerateId(Session);
|
ThreadState.X1 = (ulong)Ns.Os.Handles.GenerateId(Session);
|
||||||
Registers.X0 = (int)SvcResult.Success;
|
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;
|
long Size = 0x100;
|
||||||
int Handle = 0;
|
int Handle = 0;
|
||||||
|
|
||||||
if (UserBuffer)
|
if (UserBuffer)
|
||||||
{
|
{
|
||||||
CmdPtr = (long)Registers.X0;
|
CmdPtr = (long)ThreadState.X0;
|
||||||
Size = (long)Registers.X1;
|
Size = (long)ThreadState.X1;
|
||||||
Handle = (int)Registers.X2;
|
Handle = (int)ThreadState.X2;
|
||||||
}
|
}
|
||||||
else
|
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);
|
byte[] CmdData = AMemoryHelper.ReadBytes(Memory, CmdPtr, (int)Size);
|
||||||
|
|
||||||
HSession Session = Ns.Os.Handles.GetData<HSession>(Handle);
|
HSession Session = Ns.Os.Handles.GetData<HSession>(Handle);
|
||||||
|
@ -109,69 +110,73 @@ namespace Ryujinx.OsHle.Svc
|
||||||
|
|
||||||
byte[] Response = AMemoryHelper.ReadBytes(Memory, CmdPtr, (int)Size);
|
byte[] Response = AMemoryHelper.ReadBytes(Memory, CmdPtr, (int)Size);
|
||||||
|
|
||||||
Registers.X0 = (int)SvcResult.Success;
|
ThreadState.X0 = (int)SvcResult.Success;
|
||||||
}
|
}
|
||||||
else
|
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 Reason = (long)ThreadState.X0;
|
||||||
long Unknown = (long)Registers.X1;
|
long Unknown = (long)ThreadState.X1;
|
||||||
long Info = (long)Registers.X2;
|
long Info = (long)ThreadState.X2;
|
||||||
|
|
||||||
throw new GuestBrokeExecutionException();
|
throw new GuestBrokeExecutionException();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void SvcOutputDebugString(ARegisters Registers)
|
private void SvcOutputDebugString(AThreadState ThreadState)
|
||||||
{
|
{
|
||||||
long Position = (long)Registers.X0;
|
long Position = (long)ThreadState.X0;
|
||||||
long Size = (long)Registers.X1;
|
long Size = (long)ThreadState.X1;
|
||||||
|
|
||||||
string Str = AMemoryHelper.ReadAsciiString(Memory, Position, (int)Size);
|
string Str = AMemoryHelper.ReadAsciiString(Memory, Position, (int)Size);
|
||||||
|
|
||||||
Logging.Info($"SvcOutputDebugString: {Str}");
|
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;
|
long StackPtr = (long)ThreadState.X0;
|
||||||
int InfoType = (int)Registers.X1;
|
int InfoType = (int)ThreadState.X1;
|
||||||
long Handle = (long)Registers.X2;
|
long Handle = (long)ThreadState.X2;
|
||||||
int InfoId = (int)Registers.X3;
|
int InfoId = (int)ThreadState.X3;
|
||||||
|
|
||||||
//Fail for info not available on older Kernel versions.
|
//Fail for info not available on older Kernel versions.
|
||||||
if (InfoType == 18 ||
|
if (InfoType == 18 ||
|
||||||
InfoType == 19)
|
InfoType == 19)
|
||||||
{
|
{
|
||||||
Registers.X0 = (int)SvcResult.ErrBadInfo;
|
ThreadState.X0 = (int)SvcResult.ErrBadInfo;
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (InfoType)
|
switch (InfoType)
|
||||||
{
|
{
|
||||||
case 2: Registers.X1 = GetMapRegionBaseAddr(); break;
|
case 2: ThreadState.X1 = GetMapRegionBaseAddr(); break;
|
||||||
case 3: Registers.X1 = GetMapRegionSize(); break;
|
case 3: ThreadState.X1 = GetMapRegionSize(); break;
|
||||||
case 4: Registers.X1 = GetHeapRegionBaseAddr(); break;
|
case 4: ThreadState.X1 = GetHeapRegionBaseAddr(); break;
|
||||||
case 5: Registers.X1 = GetHeapRegionSize(); break;
|
case 5: ThreadState.X1 = GetHeapRegionSize(); break;
|
||||||
case 6: Registers.X1 = GetTotalMem(); break;
|
case 6: ThreadState.X1 = GetTotalMem(); break;
|
||||||
case 7: Registers.X1 = GetUsedMem(); break;
|
case 7: ThreadState.X1 = GetUsedMem(); break;
|
||||||
case 11: Registers.X1 = GetRnd64(); break;
|
case 11: ThreadState.X1 = GetRnd64(); break;
|
||||||
case 12: Registers.X1 = GetAddrSpaceBaseAddr(); break;
|
case 12: ThreadState.X1 = GetAddrSpaceBaseAddr(); break;
|
||||||
case 13: Registers.X1 = GetAddrSpaceSize(); break;
|
case 13: ThreadState.X1 = GetAddrSpaceSize(); break;
|
||||||
case 14: Registers.X1 = GetMapRegionBaseAddr(); break;
|
case 14: ThreadState.X1 = GetMapRegionBaseAddr(); break;
|
||||||
case 15: Registers.X1 = GetMapRegionSize(); break;
|
case 15: ThreadState.X1 = GetMapRegionSize(); break;
|
||||||
|
|
||||||
default: throw new NotImplementedException($"SvcGetInfo: {InfoType} {Handle} {InfoId}");
|
default: throw new NotImplementedException($"SvcGetInfo: {InfoType} {Handle} {InfoId}");
|
||||||
}
|
}
|
||||||
|
|
||||||
Registers.X0 = (int)SvcResult.Success;
|
ThreadState.X0 = (int)SvcResult.Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
private ulong GetTotalMem()
|
private ulong GetTotalMem()
|
||||||
|
|
|
@ -1,23 +1,23 @@
|
||||||
using ChocolArm64.State;
|
using ChocolArm64.State;
|
||||||
using Ryujinx.OsHle.Handles;
|
using Ryujinx.OsHle.Handles;
|
||||||
using System.Threading;
|
|
||||||
|
|
||||||
namespace Ryujinx.OsHle.Svc
|
namespace Ryujinx.OsHle.Svc
|
||||||
{
|
{
|
||||||
partial class SvcHandler
|
partial class SvcHandler
|
||||||
{
|
{
|
||||||
private void SvcCreateThread(ARegisters Registers)
|
private void SvcCreateThread(AThreadState ThreadState)
|
||||||
{
|
{
|
||||||
long EntryPoint = (long)Registers.X1;
|
long EntryPoint = (long)ThreadState.X1;
|
||||||
long ArgsPtr = (long)Registers.X2;
|
long ArgsPtr = (long)ThreadState.X2;
|
||||||
long StackTop = (long)Registers.X3;
|
long StackTop = (long)ThreadState.X3;
|
||||||
int Priority = (int)Registers.X4;
|
int Priority = (int)ThreadState.X4;
|
||||||
int ProcessorId = (int)Registers.X5;
|
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)
|
if (ProcessorId == -2)
|
||||||
{
|
{
|
||||||
|
//TODO: Get this value from the NPDM file.
|
||||||
ProcessorId = 0;
|
ProcessorId = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -28,16 +28,16 @@ namespace Ryujinx.OsHle.Svc
|
||||||
Priority,
|
Priority,
|
||||||
ProcessorId);
|
ProcessorId);
|
||||||
|
|
||||||
Registers.X0 = (int)SvcResult.Success;
|
ThreadState.X0 = (int)SvcResult.Success;
|
||||||
Registers.X1 = (ulong)Handle;
|
ThreadState.X1 = (ulong)Handle;
|
||||||
}
|
}
|
||||||
|
|
||||||
//TODO: Error codes.
|
//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<HThread>(Handle);
|
HThread Thread = Ns.Os.Handles.GetData<HThread>(Handle);
|
||||||
|
|
||||||
|
@ -45,38 +45,38 @@ namespace Ryujinx.OsHle.Svc
|
||||||
{
|
{
|
||||||
Process.Scheduler.StartThread(Thread);
|
Process.Scheduler.StartThread(Thread);
|
||||||
|
|
||||||
Registers.X0 = (int)SvcResult.Success;
|
ThreadState.X0 = (int)SvcResult.Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
//TODO: Error codes.
|
//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);
|
Process.Scheduler.Yield(CurrThread);
|
||||||
}
|
}
|
||||||
else
|
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<HThread>(Handle);
|
HThread Thread = Ns.Os.Handles.GetData<HThread>(Handle);
|
||||||
|
|
||||||
if (Thread != null)
|
if (Thread != null)
|
||||||
{
|
{
|
||||||
Registers.X1 = (ulong)Thread.Priority;
|
ThreadState.X1 = (ulong)Thread.Priority;
|
||||||
Registers.X0 = (int)SvcResult.Success;
|
ThreadState.X0 = (int)SvcResult.Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
//TODO: Error codes.
|
//TODO: Error codes.
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
using ChocolArm64;
|
|
||||||
using ChocolArm64.State;
|
using ChocolArm64.State;
|
||||||
using Ryujinx.OsHle.Handles;
|
using Ryujinx.OsHle.Handles;
|
||||||
|
|
||||||
|
@ -6,11 +5,11 @@ namespace Ryujinx.OsHle.Svc
|
||||||
{
|
{
|
||||||
partial class SvcHandler
|
partial class SvcHandler
|
||||||
{
|
{
|
||||||
private void SvcArbitrateLock(ARegisters Registers)
|
private void SvcArbitrateLock(AThreadState ThreadState)
|
||||||
{
|
{
|
||||||
int OwnerThreadHandle = (int)Registers.X0;
|
int OwnerThreadHandle = (int)ThreadState.X0;
|
||||||
long MutexAddress = (long)Registers.X1;
|
long MutexAddress = (long)ThreadState.X1;
|
||||||
int RequestingThreadHandle = (int)Registers.X2;
|
int RequestingThreadHandle = (int)ThreadState.X2;
|
||||||
|
|
||||||
HThread RequestingThread = Ns.Os.Handles.GetData<HThread>(RequestingThreadHandle);
|
HThread RequestingThread = Ns.Os.Handles.GetData<HThread>(RequestingThreadHandle);
|
||||||
|
|
||||||
|
@ -20,34 +19,35 @@ namespace Ryujinx.OsHle.Svc
|
||||||
|
|
||||||
M.WaitForLock(RequestingThread, RequestingThreadHandle);
|
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))
|
if (Ns.Os.Mutexes.TryGetValue(MutexAddress, out Mutex M))
|
||||||
{
|
{
|
||||||
M.Unlock();
|
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 MutexAddress = (long)ThreadState.X0;
|
||||||
long CondVarAddress = (long)Registers.X1;
|
long CondVarAddress = (long)ThreadState.X1;
|
||||||
int ThreadHandle = (int)Registers.X2;
|
int ThreadHandle = (int)ThreadState.X2;
|
||||||
long Timeout = (long)Registers.X3;
|
long Timeout = (long)ThreadState.X3;
|
||||||
|
|
||||||
HThread Thread = Ns.Os.Handles.GetData<HThread>(ThreadHandle);
|
HThread Thread = Ns.Os.Handles.GetData<HThread>(ThreadHandle);
|
||||||
|
|
||||||
if (Ns.Os.Mutexes.TryGetValue(MutexAddress, out Mutex M))
|
Mutex M = new Mutex(Process, MutexAddress, ThreadHandle);
|
||||||
{
|
|
||||||
M.GiveUpLock(ThreadHandle);
|
M = Ns.Os.Mutexes.GetOrAdd(MutexAddress, M);
|
||||||
}
|
|
||||||
|
M.GiveUpLock(ThreadHandle);
|
||||||
|
|
||||||
CondVar Cv = new CondVar(Process, CondVarAddress, Timeout);
|
CondVar Cv = new CondVar(Process, CondVarAddress, Timeout);
|
||||||
|
|
||||||
|
@ -55,26 +55,24 @@ namespace Ryujinx.OsHle.Svc
|
||||||
|
|
||||||
Cv.WaitForSignal(Thread);
|
Cv.WaitForSignal(Thread);
|
||||||
|
|
||||||
M = new Mutex(Process, MutexAddress, ThreadHandle);
|
|
||||||
|
|
||||||
M = Ns.Os.Mutexes.GetOrAdd(MutexAddress, M);
|
|
||||||
|
|
||||||
M.WaitForLock(Thread, ThreadHandle);
|
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;
|
long CondVarAddress = (long)ThreadState.X0;
|
||||||
int Count = (int)Registers.X1;
|
int Count = (int)ThreadState.X1;
|
||||||
|
|
||||||
|
HThread CurrThread = Process.GetThread(ThreadState.Tpidr);
|
||||||
|
|
||||||
if (Ns.Os.CondVars.TryGetValue(CondVarAddress, out CondVar Cv))
|
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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
47
Ryujinx/Ryujinx.conf
Normal file
47
Ryujinx/Ryujinx.conf
Normal file
|
@ -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
|
|
@ -2,8 +2,8 @@
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<OutputType>Exe</OutputType>
|
<OutputType>Exe</OutputType>
|
||||||
<TargetFramework>netcoreapp2.0</TargetFramework>
|
<TargetFramework>netcoreapp2.0</TargetFramework>
|
||||||
<RuntimeIdentifier>win10-x64</RuntimeIdentifier>
|
|
||||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||||
|
<RuntimeIdentifiers>win10-x64;osx-x64</RuntimeIdentifiers>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="OpenTK.NETCore" Version="1.1.2749.6433" />
|
<PackageReference Include="OpenTK.NETCore" Version="1.1.2749.6433" />
|
|
@ -14,6 +14,9 @@ namespace Ryujinx
|
||||||
internal NsGpu Gpu { get; private set; }
|
internal NsGpu Gpu { get; private set; }
|
||||||
internal Horizon Os { get; private set; }
|
internal Horizon Os { get; private set; }
|
||||||
internal VirtualFs VFs { get; private set; }
|
internal VirtualFs VFs { get; private set; }
|
||||||
|
internal Hid Hid { get; private set; }
|
||||||
|
|
||||||
|
public event EventHandler Finish;
|
||||||
|
|
||||||
public Switch(IGalRenderer Renderer)
|
public Switch(IGalRenderer Renderer)
|
||||||
{
|
{
|
||||||
|
@ -22,6 +25,15 @@ namespace Ryujinx
|
||||||
Gpu = new NsGpu(Renderer);
|
Gpu = new NsGpu(Renderer);
|
||||||
Os = new Horizon(this);
|
Os = new Horizon(this);
|
||||||
VFs = new VirtualFs();
|
VFs = new VirtualFs();
|
||||||
|
Hid = new Hid(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal virtual void OnFinish(EventArgs e)
|
||||||
|
{
|
||||||
|
if (Finish != null)
|
||||||
|
{
|
||||||
|
Finish(this, e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
|
|
|
@ -6,7 +6,6 @@ using Gal;
|
||||||
using OpenTK;
|
using OpenTK;
|
||||||
using OpenTK.Graphics;
|
using OpenTK.Graphics;
|
||||||
using OpenTK.Graphics.OpenGL;
|
using OpenTK.Graphics.OpenGL;
|
||||||
using Ryujinx.OsHle;
|
|
||||||
using System;
|
using System;
|
||||||
|
|
||||||
namespace Ryujinx
|
namespace Ryujinx
|
||||||
|
@ -276,76 +275,62 @@ void main(void) {
|
||||||
|
|
||||||
protected override void OnUpdateFrame(FrameEventArgs e)
|
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)
|
RightJoystick = new JoystickPosition
|
||||||
{
|
|
||||||
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])
|
|
||||||
{
|
{
|
||||||
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)
|
protected override void OnRenderFrame(FrameEventArgs e)
|
|
@ -50,6 +50,11 @@ namespace Ryujinx
|
||||||
|
|
||||||
using (GLScreen Screen = new GLScreen(Ns, Renderer))
|
using (GLScreen Screen = new GLScreen(Ns, Renderer))
|
||||||
{
|
{
|
||||||
|
Ns.Finish += (Sender, Args) =>
|
||||||
|
{
|
||||||
|
Screen.Exit();
|
||||||
|
};
|
||||||
|
|
||||||
Screen.Run(60.0);
|
Screen.Run(60.0);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue