Merge pull request #12 from gdkchan/master

Working...
This commit is contained in:
Dr.Hacknik 2018-02-19 15:57:31 -05:00 committed by GitHub
commit 33cb70b5a1
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
76 changed files with 4582 additions and 3752 deletions

9
.travis.yml Normal file
View 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
View 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).

View file

@ -21,37 +21,23 @@ https://openal.org/downloads/OpenAL11CoreSDK.zip
- Arrows.
- Enter > "Start" & Tab > "Select"
- Qwerty:
- A > "A"
- A > "A" (QWERTY) / Q > "A" (AZERTY)
- S > "B"
- Z > "X"
- X > "Y"
- Azerty:
- Q > "A"
- S > "B"
- W > "X"
- Z > "X" (QWERTY) / W > "X" (AZERTY)
- X > "Y"
- Key_Up > "Right Stick Up"
- Key_Down > "Right Stick Down"
- Key_Left > "Right Stick Left"
- Key_Right > "Right Stick Right"
- I > "Left Stick Up"
- K > "Left Stick Down"
- J > "Left Stick Left"
- L > "Left Stick Right"
- Tab > "Minus"
- Enter > "Plus"
- Config File: `Ryujinx.conf` should be present on executable folder.
- Logging_Enable_Info (bool)
Enable the Informations Logging.
- Logging_Enable_Trace (bool)
Enable the Trace Logging (Enabled in Debug recommanded).
- Logging_Enable_Debug (bool)
Enable the Debug Logging (Enabled in Debug recommanded).
- Logging_Enable_Warn (bool)
Enable the Warning Logging (Enabled in Debug recommanded).
- Logging_Enable_Error (bool)
Enable the Error Logging (Enabled in Debug recommanded).
- Logging_Enable_Fatal (bool)
Enable the Fatal Logging (Enabled in Debug recommanded).
- Logging_Enable_LogFile (bool)
Enable writing the logging inside a Ryujinx.log file.
- Config File: `Ryujinx.conf` should be present in executable folder.
For more informations [you can go here](CONFIG.md).
**Help**
@ -68,7 +54,7 @@ To run this emulator, you need the .NET Core 2.0 (or higher) SDK.
Run `dotnet run -c Release -- path\to\homebrew.nro` inside the Ryujinx solution folder to run homebrew apps.
Run `dotnet run -c Release -- path\to\game_exefs_and_romfs_folder` to run official games (they need to be decrypted and extracted first!).
Audio is partially supported (glitched) on Windows, you need to install the OpenAL Core SDK:
Audio is partially supported (glitched) on Linux and macOS, for Windows you need to install the OpenAL Core SDK:
https://openal.org/downloads/OpenAL11CoreSDK.zip
**Lastest build**

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

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

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

View 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>

View file

@ -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

View file

@ -3,7 +3,9 @@ Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 15
VisualStudioVersion = 15.0.26730.8
MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx", "Ryujinx.csproj", "{074045D4-3ED2-4711-9169-E385F2BFB5A0}"
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ryujinx", "Ryujinx\Ryujinx.csproj", "{074045D4-3ED2-4711-9169-E385F2BFB5A0}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ryujinx.Tests", "Ryujinx.Tests\Ryujinx.Tests.csproj", "{EBB55AEA-C7D7-4DEB-BF96-FA1789E225E9}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
@ -15,6 +17,10 @@ Global
{074045D4-3ED2-4711-9169-E385F2BFB5A0}.Debug|Any CPU.Build.0 = Debug|Any CPU
{074045D4-3ED2-4711-9169-E385F2BFB5A0}.Release|Any CPU.ActiveCfg = Release|Any CPU
{074045D4-3ED2-4711-9169-E385F2BFB5A0}.Release|Any CPU.Build.0 = Release|Any CPU
{EBB55AEA-C7D7-4DEB-BF96-FA1789E225E9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{EBB55AEA-C7D7-4DEB-BF96-FA1789E225E9}.Debug|Any CPU.Build.0 = Debug|Any CPU
{EBB55AEA-C7D7-4DEB-BF96-FA1789E225E9}.Release|Any CPU.ActiveCfg = Release|Any CPU
{EBB55AEA-C7D7-4DEB-BF96-FA1789E225E9}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE

View file

@ -16,6 +16,8 @@ namespace Ryujinx
public static bool LoggingEnableFatal { get; private set; }
public static bool LoggingEnableLogFile { get; private set; }
public static JoyCon FakeJoyCon { get; private set; }
public static void Read()
{
IniParser Parser = new IniParser(Path.Combine(Path.GetDirectoryName(Assembly.GetEntryAssembly().Location), "Ryujinx.conf"));
@ -27,6 +29,41 @@ namespace Ryujinx
LoggingEnableError = Convert.ToBoolean(Parser.Value("Logging_Enable_Error"));
LoggingEnableFatal = Convert.ToBoolean(Parser.Value("Logging_Enable_Fatal"));
LoggingEnableLogFile = Convert.ToBoolean(Parser.Value("Logging_Enable_LogFile"));
FakeJoyCon = new JoyCon
{
Left = new JoyConLeft
{
StickUp = Convert.ToInt16(Parser.Value("Controls_Left_FakeJoycon_Stick_Up")),
StickDown = Convert.ToInt16(Parser.Value("Controls_Left_FakeJoycon_Stick_Down")),
StickLeft = Convert.ToInt16(Parser.Value("Controls_Left_FakeJoycon_Stick_Left")),
StickRight = Convert.ToInt16(Parser.Value("Controls_Left_FakeJoycon_Stick_Right")),
StickButton = Convert.ToInt16(Parser.Value("Controls_Left_FakeJoycon_Stick_Button")),
DPadUp = Convert.ToInt16(Parser.Value("Controls_Left_FakeJoycon_DPad_Up")),
DPadDown = Convert.ToInt16(Parser.Value("Controls_Left_FakeJoycon_DPad_Down")),
DPadLeft = Convert.ToInt16(Parser.Value("Controls_Left_FakeJoycon_DPad_Left")),
DPadRight = Convert.ToInt16(Parser.Value("Controls_Left_FakeJoycon_DPad_Right")),
ButtonMinus = Convert.ToInt16(Parser.Value("Controls_Left_FakeJoycon_Button_Minus")),
ButtonL = Convert.ToInt16(Parser.Value("Controls_Left_FakeJoycon_Button_L")),
ButtonZL = Convert.ToInt16(Parser.Value("Controls_Left_FakeJoycon_Button_ZL"))
},
Right = new JoyConRight
{
StickUp = Convert.ToInt16(Parser.Value("Controls_Right_FakeJoycon_Stick_Up")),
StickDown = Convert.ToInt16(Parser.Value("Controls_Right_FakeJoycon_Stick_Down")),
StickLeft = Convert.ToInt16(Parser.Value("Controls_Right_FakeJoycon_Stick_Left")),
StickRight = Convert.ToInt16(Parser.Value("Controls_Right_FakeJoycon_Stick_Right")),
StickButton = Convert.ToInt16(Parser.Value("Controls_Right_FakeJoycon_Stick_Button")),
ButtonA = Convert.ToInt16(Parser.Value("Controls_Right_FakeJoycon_Button_A")),
ButtonB = Convert.ToInt16(Parser.Value("Controls_Right_FakeJoycon_Button_B")),
ButtonX = Convert.ToInt16(Parser.Value("Controls_Right_FakeJoycon_Button_X")),
ButtonY = Convert.ToInt16(Parser.Value("Controls_Right_FakeJoycon_Button_Y")),
ButtonPlus = Convert.ToInt16(Parser.Value("Controls_Right_FakeJoycon_Button_Plus")),
ButtonR = Convert.ToInt16(Parser.Value("Controls_Right_FakeJoycon_Button_R")),
ButtonZR = Convert.ToInt16(Parser.Value("Controls_Right_FakeJoycon_Button_ZR"))
}
};
}
}

View file

@ -139,8 +139,8 @@ namespace ChocolArm64
Set("0>101110<<100000100110xxxxxxxxxx", AInstEmit.Cmle_V, typeof(AOpCodeSimd));
Set("0>001110<<100000101010xxxxxxxxxx", AInstEmit.Cmlt_V, typeof(AOpCodeSimd));
Set("0x00111000100000010110xxxxxxxxxx", AInstEmit.Cnt_V, typeof(AOpCodeSimd));
Set("01011110000xxxxx000001xxxxxxxxxx", AInstEmit.Dup_S, typeof(AOpCodeSimdIns));
Set("0x001110000xxxxx000011xxxxxxxxxx", AInstEmit.Dup_Gp, typeof(AOpCodeSimdIns));
Set("01011110000xxxxx000001xxxxxxxxxx", AInstEmit.Dup_S, typeof(AOpCodeSimdIns));
Set("0x001110000xxxxx000001xxxxxxxxxx", AInstEmit.Dup_V, typeof(AOpCodeSimdIns));
Set("0x101110001xxxxx000111xxxxxxxxxx", AInstEmit.Eor_V, typeof(AOpCodeSimdReg));
Set("00011110xx100000110000xxxxxxxxxx", AInstEmit.Fabs_S, typeof(AOpCodeSimd));
@ -151,14 +151,16 @@ namespace ChocolArm64
Set("00011110xx1xxxxx001000xxxxx1x000", AInstEmit.Fcmpe_S, typeof(AOpCodeSimdReg));
Set("00011110xx1xxxxxxxxx11xxxxxxxxxx", AInstEmit.Fcsel_S, typeof(AOpCodeSimdFcond));
Set("00011110xx10001xx10000xxxxxxxxxx", AInstEmit.Fcvt_S, typeof(AOpCodeSimd));
Set("x0011110xx110000000000xxxxxxxxxx", AInstEmit.Fcvtms_S, typeof(AOpCodeSimdCvt));
Set("x0011110xx101000000000xxxxxxxxxx", AInstEmit.Fcvtps_S, typeof(AOpCodeSimdCvt));
Set("x0011110xx111000000000xxxxxxxxxx", AInstEmit.Fcvtzs_S, typeof(AOpCodeSimdCvt));
Set("x0011110xx011000xxxxxxxxxxxxxxxx", AInstEmit.Fcvtzs_Fix, typeof(AOpCodeSimdCvt));
Set("x0011110xx100100000000xxxxxxxxxx", AInstEmit.Fcvtas_Gp, typeof(AOpCodeSimdCvt));
Set("x0011110xx100101000000xxxxxxxxxx", AInstEmit.Fcvtau_Gp, typeof(AOpCodeSimdCvt));
Set("x0011110xx110000000000xxxxxxxxxx", AInstEmit.Fcvtms_Gp, typeof(AOpCodeSimdCvt));
Set("x0011110xx101000000000xxxxxxxxxx", AInstEmit.Fcvtps_Gp, typeof(AOpCodeSimdCvt));
Set("x0011110xx111000000000xxxxxxxxxx", AInstEmit.Fcvtzs_Gp, typeof(AOpCodeSimdCvt));
Set("x0011110xx011000xxxxxxxxxxxxxxxx", AInstEmit.Fcvtzs_Gp_Fix, typeof(AOpCodeSimdCvt));
Set("0x0011101x100001101110xxxxxxxxxx", AInstEmit.Fcvtzs_V, typeof(AOpCodeSimd));
Set("0x0011110>>xxxxx111111xxxxxxxxxx", AInstEmit.Fcvtzs_V, typeof(AOpCodeSimdShImm));
Set("x0011110xx111001000000xxxxxxxxxx", AInstEmit.Fcvtzu_S, typeof(AOpCodeSimdCvt));
Set("x0011110xx011001xxxxxxxxxxxxxxxx", AInstEmit.Fcvtzu_Fix, typeof(AOpCodeSimdCvt));
Set("x0011110xx111001000000xxxxxxxxxx", AInstEmit.Fcvtzu_Gp, typeof(AOpCodeSimdCvt));
Set("x0011110xx011001xxxxxxxxxxxxxxxx", AInstEmit.Fcvtzu_Gp_Fix, typeof(AOpCodeSimdCvt));
Set("0x1011101x100001101110xxxxxxxxxx", AInstEmit.Fcvtzu_V, typeof(AOpCodeSimd));
Set("0x1011110>>xxxxx111111xxxxxxxxxx", AInstEmit.Fcvtzu_V, typeof(AOpCodeSimdShImm));
Set("00011110xx1xxxxx000110xxxxxxxxxx", AInstEmit.Fdiv_S, typeof(AOpCodeSimdReg));
@ -168,7 +170,7 @@ namespace ChocolArm64
Set("00011110xx1xxxxx010110xxxxxxxxxx", AInstEmit.Fmin_S, typeof(AOpCodeSimdReg));
Set("00011110xx1xxxxx011110xxxxxxxxxx", AInstEmit.Fminnm_S, typeof(AOpCodeSimdReg));
Set("0x0011100x1xxxxx110011xxxxxxxxxx", AInstEmit.Fmla_V, typeof(AOpCodeSimdReg));
Set("0x0011111<<xxxxx0001x0xxxxxxxxxx", AInstEmit.Fmla_Vs, typeof(AOpCodeSimdRegElem));
Set("0x0011111<<xxxxx0001x0xxxxxxxxxx", AInstEmit.Fmla_Ve, typeof(AOpCodeSimdRegElem));
Set("00011110xx100000010000xxxxxxxxxx", AInstEmit.Fmov_S, typeof(AOpCodeSimd));
Set("00011110xx1xxxxxxxx100xxxxxxxxxx", AInstEmit.Fmov_Si, typeof(AOpCodeSimdFmov));
Set("0xx0111100000xxx111101xxxxxxxxxx", AInstEmit.Fmov_V, typeof(AOpCodeSimdImm));
@ -179,7 +181,7 @@ namespace ChocolArm64
Set("00011111xx0xxxxx1xxxxxxxxxxxxxxx", AInstEmit.Fmsub_S, typeof(AOpCodeSimdReg));
Set("00011110xx1xxxxx000010xxxxxxxxxx", AInstEmit.Fmul_S, 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("00011110xx1xxxxx100010xxxxxxxxxx", AInstEmit.Fnmul_S, typeof(AOpCodeSimdReg));
Set("00011110xx100110010000xxxxxxxxxx", AInstEmit.Frinta_S, typeof(AOpCodeSimd));
@ -201,6 +203,7 @@ namespace ChocolArm64
Set("xx111100x11xxxxxxxxx10xxxxxxxxxx", AInstEmit.Ldr, typeof(AOpCodeSimdMemReg));
Set("xx011100xxxxxxxxxxxxxxxxxxxxxxxx", AInstEmit.LdrLit, typeof(AOpCodeSimdMemLit));
Set("0x001110<<1xxxxx100101xxxxxxxxxx", AInstEmit.Mla_V, typeof(AOpCodeSimdReg));
Set("0x101110<<1xxxxx100101xxxxxxxxxx", AInstEmit.Mls_V, typeof(AOpCodeSimdReg));
Set("0x00111100000xxx0xx001xxxxxxxxxx", AInstEmit.Movi_V, typeof(AOpCodeSimdImm));
Set("0x00111100000xxx10x001xxxxxxxxxx", 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("0x101110<<1xxxxx000100xxxxxxxxxx", AInstEmit.Uaddw_V, typeof(AOpCodeSimdReg));
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("0>101110<<1xxxxx010001xxxxxxxxxx", AInstEmit.Ushl_V, typeof(AOpCodeSimdReg));
Set("0x10111100>>>xxx101001xxxxxxxxxx", AInstEmit.Ushll_V, typeof(AOpCodeSimdShImm));
Set("0x1011110>>>>xxx000001xxxxxxxxxx", AInstEmit.Ushr_V, typeof(AOpCodeSimdShImm));
Set("0x1011110>>>>xxx000101xxxxxxxxxx", AInstEmit.Usra_V, typeof(AOpCodeSimdShImm));
Set("0x001110xx0xxxxx000110xxxxxxxxxx", AInstEmit.Uzp1_V, typeof(AOpCodeSimdReg));
Set("0x001110xx0xxxxx010110xxxxxxxxxx", AInstEmit.Uzp2_V, typeof(AOpCodeSimdReg));
Set("0x001110<<100001001010xxxxxxxxxx", AInstEmit.Xtn_V, typeof(AOpCodeSimd));
#endregion
}
@ -317,7 +322,7 @@ namespace ChocolArm64
}
else if (ZCount != 0 && OCount != 0)
{
//When both the > and the <, then a value is blacklisted,
//When both the > and the < are used, then a value is blacklisted,
//with > indicating 0, and < indicating 1. So, for example, ><<
//blacklists the pattern 011, but 000, 001, 010, 100, 101,
//110 and 111 are valid.

View file

@ -5,9 +5,9 @@ using System.Threading;
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 long EntryPoint { get; private set; }
@ -20,7 +20,7 @@ namespace ChocolArm64
public event EventHandler WorkFinished;
public int ThreadId => Registers.ThreadId;
public int ThreadId => ThreadState.ThreadId;
public bool IsAlive => Work.IsAlive;
@ -34,7 +34,7 @@ namespace ChocolArm64
this.Priority = Priority;
this.EntryPoint = EntryPoint;
Registers = new ARegisters();
ThreadState = new AThreadState();
Translator = new ATranslator(this);
ExecuteLock = new object();
}

View file

@ -9,7 +9,7 @@ namespace ChocolArm64
{
class ATranslatedSub
{
private delegate long AA64Subroutine(ARegisters Register, AMemory Memory);
private delegate long AA64Subroutine(AThreadState Register, AMemory Memory);
private AA64Subroutine ExecDelegate;
@ -17,7 +17,7 @@ namespace ChocolArm64
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 DynamicMethod Method { get; private set; }
@ -58,9 +58,9 @@ namespace ChocolArm64
FixedArgTypes[Index] = ParamType;
if (ParamType == typeof(ARegisters))
if (ParamType == typeof(AThreadState))
{
RegistersArgIdx = Index;
StateArgIdx = Index;
}
else if (ParamType == typeof(AMemory))
{
@ -69,7 +69,7 @@ namespace ChocolArm64
}
}
public long Execute(ARegisters Registers, AMemory Memory)
public long Execute(AThreadState ThreadState, AMemory Memory)
{
if (!HasDelegate)
{
@ -83,7 +83,7 @@ namespace ChocolArm64
foreach (ARegister Reg in Params)
{
Generator.EmitLdarg(RegistersArgIdx);
Generator.EmitLdarg(StateArgIdx);
Generator.Emit(OpCodes.Ldfld, Reg.GetField());
}
@ -96,7 +96,7 @@ namespace ChocolArm64
HasDelegate = true;
}
return ExecDelegate(Registers, Memory);
return ExecDelegate(ThreadState, Memory);
}
public void MarkForReJit() => NeedsReJit = true;

View file

@ -8,10 +8,10 @@ namespace ChocolArm64
{
class ATranslator
{
private Dictionary<long, ATranslatedSub> CachedSubs;
public AThread Thread { get; private set; }
private Dictionary<long, ATranslatedSub> CachedSubs;
private bool KeepRunning;
public ATranslator(AThread Parent)
@ -31,11 +31,11 @@ namespace ChocolArm64
{
if (CachedSubs.TryGetValue(Position, out ATranslatedSub Sub) && !Sub.NeedsReJit)
{
Position = Sub.Execute(Thread.Registers, Thread.Memory);
Position = Sub.Execute(Thread.ThreadState, Thread.Memory);
}
else
{
Position = TranslateSubroutine(Position).Execute(Thread.Registers, Thread.Memory);
Position = TranslateSubroutine(Position).Execute(Thread.ThreadState, Thread.Memory);
}
}
while (Position != 0 && KeepRunning);

View file

@ -25,7 +25,7 @@ namespace ChocolArm64.Decoder
Cond = (ACond)((OpCode >> 12) & 0xf);
RmImm = (OpCode >> 16) & 0x1f;
Rd = ARegisters.ZRIndex;
Rd = AThreadState.ZRIndex;
}
}
}

View file

@ -3,12 +3,8 @@ using ChocolArm64.State;
namespace ChocolArm64.Decoder
{
class AOpCodeSimdMemMs : AOpCode, IAOpCodeSimd
class AOpCodeSimdMemMs : AOpCodeMemReg, IAOpCodeSimd
{
public int Rt { get; private set; }
public int Rn { get; private set; }
public int Size { get; private set; }
public int Rm { get; private set; }
public int Reps { get; private set; }
public int SElems { get; private set; }
public int Elems { get; private set; }
@ -29,10 +25,7 @@ namespace ChocolArm64.Decoder
default: Inst = AInst.Undefined; return;
}
Rt = (OpCode >> 0) & 0x1f;
Rn = (OpCode >> 5) & 0x1f;
Size = (OpCode >> 10) & 0x3;
Rm = (OpCode >> 16) & 0x1f;
WBack = ((OpCode >> 23) & 0x1) != 0;
bool Q = ((OpCode >> 30) & 1) != 0;
@ -44,6 +37,8 @@ namespace ChocolArm64.Decoder
return;
}
Extend64 = false;
RegisterSize = Q
? ARegisterSize.SIMD128
: ARegisterSize.SIMD64;

View file

@ -3,12 +3,8 @@ using ChocolArm64.State;
namespace ChocolArm64.Decoder
{
class AOpCodeSimdMemSs : AOpCode, IAOpCodeSimd
class AOpCodeSimdMemSs : AOpCodeMemReg, IAOpCodeSimd
{
public int Rt { get; private set; }
public int Rn { get; private set; }
public int Size { get; private set; }
public int Rm { get; private set; }
public int SElems { get; private set; }
public int Index { get; private set; }
public bool Replicate { get; private set; }
@ -31,11 +27,9 @@ namespace ChocolArm64.Decoder
switch (Scale)
{
case 0: Index >>= 0; break;
case 1:
{
if ((Index & 1) != 0)
if ((Size & 1) != 0)
{
Inst = AInst.Undefined;
@ -49,23 +43,23 @@ namespace ChocolArm64.Decoder
case 2:
{
if ((Index & 2) != 0 ||
((Index & 1) != 0 && S != 0))
if ((Size & 2) != 0 ||
((Size & 1) != 0 && S != 0))
{
Inst = AInst.Undefined;
return;
}
if ((Index & 1) != 0)
if ((Size & 1) != 0)
{
Index >>= 3;
Scale = 3;
}
else
{
Index >>= 2;
Scale = 3;
}
break;
@ -91,9 +85,8 @@ namespace ChocolArm64.Decoder
this.SElems = SElems;
this.Size = Scale;
Rt = (OpCode >> 0) & 0x1f;
Rn = (OpCode >> 5) & 0x1f;
Rm = (OpCode >> 16) & 0x1f;
Extend64 = false;
WBack = ((OpCode >> 23) & 0x1) != 0;
RegisterSize = Q != 0

View file

@ -2,17 +2,13 @@ using ChocolArm64.Instruction;
namespace ChocolArm64.Decoder
{
class AOpCodeSimdRegElem : AOpCodeSimd
class AOpCodeSimdRegElem : AOpCodeSimdReg
{
public int Rm { get; private set; }
public int Index { get; private set; }
public AOpCodeSimdRegElem(AInst Inst, long Position, int OpCode) : base(Inst, Position, OpCode)
{
Rm = (OpCode >> 16) & 0x1f;
Size = (OpCode >> 22) & 0x1;
if (Size != 0)
if ((Size & 1) != 0)
{
Index = (OpCode >> 11) & 1;
}

View file

@ -60,6 +60,8 @@ namespace ChocolArm64.Instruction
Context.Emit(OpCodes.And);
EmitZeroCVFlags(Context);
Context.EmitZNFlagCheck();
EmitDataStoreS(Context);
@ -79,6 +81,8 @@ namespace ChocolArm64.Instruction
if (SetFlags)
{
EmitZeroCVFlags(Context);
Context.EmitZNFlagCheck();
}
@ -335,5 +339,16 @@ namespace ChocolArm64.Instruction
Context.Emit(OpCodes.Conv_I4);
}
}
private static void EmitZeroCVFlags(AILEmitterCtx Context)
{
Context.EmitLdc_I4(0);
Context.EmitStflg((int)APState.VBit);
Context.EmitLdc_I4(0);
Context.EmitStflg((int)APState.CBit);
}
}
}

View file

@ -21,22 +21,13 @@ namespace ChocolArm64.Instruction
public static void EmitAddsVCheck(AILEmitterCtx Context)
{
//V = (Rd ^ Rn) & (Rd ^ Rm) & ~(Rn ^ Rm) < 0
Context.EmitSttmp();
Context.EmitLdtmp();
Context.EmitLdtmp();
//V = (Rd ^ Rn) & ~(Rn ^ Rm) < 0
Context.Emit(OpCodes.Dup);
EmitDataLoadRn(Context);
Context.Emit(OpCodes.Xor);
Context.EmitLdtmp();
EmitDataLoadOper2(Context);
Context.Emit(OpCodes.Xor);
Context.Emit(OpCodes.And);
EmitDataLoadOpers(Context);
Context.Emit(OpCodes.Xor);
@ -52,15 +43,14 @@ namespace ChocolArm64.Instruction
public static void EmitSubsCCheck(AILEmitterCtx Context)
{
//C = Rn == Rm || Rn > Rm
//C = Rn == Rm || Rn > Rm = !(Rn < Rm)
EmitDataLoadOpers(Context);
Context.Emit(OpCodes.Ceq);
Context.Emit(OpCodes.Clt_Un);
EmitDataLoadOpers(Context);
Context.EmitLdc_I4(1);
Context.Emit(OpCodes.Cgt_Un);
Context.Emit(OpCodes.Or);
Context.Emit(OpCodes.Xor);
Context.EmitStflg((int)APState.CBit);
}

View file

@ -11,12 +11,12 @@ namespace ChocolArm64.Instruction
public static void Brk(AILEmitterCtx Context)
{
EmitExceptionCall(Context, nameof(ARegisters.OnBreak));
EmitExceptionCall(Context, nameof(AThreadState.OnBreak));
}
public static void Svc(AILEmitterCtx Context)
{
EmitExceptionCall(Context, nameof(ARegisters.OnSvcCall));
EmitExceptionCall(Context, nameof(AThreadState.OnSvcCall));
}
private static void EmitExceptionCall(AILEmitterCtx Context, string MthdName)
@ -25,11 +25,11 @@ namespace ChocolArm64.Instruction
Context.EmitStoreState();
Context.EmitLdarg(ATranslatedSub.RegistersArgIdx);
Context.EmitLdarg(ATranslatedSub.StateArgIdx);
Context.EmitLdc_I4(Op.Id);
MethodInfo MthdInfo = typeof(ARegisters).GetMethod(MthdName, Binding);
MethodInfo MthdInfo = typeof(AThreadState).GetMethod(MthdName, Binding);
Context.EmitCall(MthdInfo);
@ -45,14 +45,14 @@ namespace ChocolArm64.Instruction
Context.EmitStoreState();
Context.EmitLdarg(ATranslatedSub.RegistersArgIdx);
Context.EmitLdarg(ATranslatedSub.StateArgIdx);
Context.EmitLdc_I8(Op.Position);
Context.EmitLdc_I4(Op.RawOpCode);
string MthdName = nameof(ARegisters.OnUndefined);
string MthdName = nameof(AThreadState.OnUndefined);
MethodInfo MthdInfo = typeof(ARegisters).GetMethod(MthdName, Binding);
MethodInfo MthdInfo = typeof(AThreadState).GetMethod(MthdName, Binding);
Context.EmitCall(MthdInfo);

View file

@ -26,7 +26,7 @@ namespace ChocolArm64.Instruction
AOpCodeBImmAl Op = (AOpCodeBImmAl)Context.CurrOp;
Context.EmitLdc_I(Op.Position + 4);
Context.EmitStint(ARegisters.LRIndex);
Context.EmitStint(AThreadState.LRIndex);
Context.EmitStoreState();
if (Context.TryOptEmitSubroutineCall())
@ -66,7 +66,7 @@ namespace ChocolArm64.Instruction
AOpCodeBReg Op = (AOpCodeBReg)Context.CurrOp;
Context.EmitLdc_I(Op.Position + 4);
Context.EmitStint(ARegisters.LRIndex);
Context.EmitStint(AThreadState.LRIndex);
Context.EmitStoreState();
Context.EmitLdintzr(Op.Rn);
@ -99,7 +99,7 @@ namespace ChocolArm64.Instruction
public static void Ret(AILEmitterCtx Context)
{
Context.EmitStoreState();
Context.EmitLdint(ARegisters.LRIndex);
Context.EmitLdint(AThreadState.LRIndex);
Context.Emit(OpCodes.Ret);
}

View file

@ -20,7 +20,7 @@ namespace ChocolArm64.Instruction
{
AOpCodeAdr Op = (AOpCodeAdr)Context.CurrOp;
Context.EmitLdc_I((Op.Position & ~0xfff) + (Op.Imm << 12));
Context.EmitLdc_I((Op.Position & ~0xfffL) + (Op.Imm << 12));
Context.EmitStintzr(Op.Rd);
}

View file

@ -159,7 +159,7 @@ namespace ChocolArm64.Instruction
private static void EmitMemoryCall(AILEmitterCtx Context, string Name, int Rn = -1)
{
Context.EmitLdarg(ATranslatedSub.MemoryArgIdx);
Context.EmitLdarg(ATranslatedSub.RegistersArgIdx);
Context.EmitLdarg(ATranslatedSub.StateArgIdx);
if (Rn != -1)
{

View file

@ -1,3 +1,4 @@
using ChocolArm64.Decoder;
using ChocolArm64.Memory;
using ChocolArm64.Translation;
using System;
@ -31,24 +32,41 @@ namespace ChocolArm64.Instruction
private static void EmitReadCall(AILEmitterCtx Context, Extension Ext, int Size)
{
if (Size < 0 || Size > 4)
bool IsSimd = GetIsSimd(Context);
string Name = null;
if (Size < 0 || Size > (IsSimd ? 4 : 3))
{
throw new ArgumentOutOfRangeException(nameof(Size));
}
string Name = null;
if (IsSimd)
{
switch (Size)
{
case 0: Name = nameof(AMemory.ReadVector8); break;
case 1: Name = nameof(AMemory.ReadVector16); break;
case 2: Name = nameof(AMemory.ReadVector32); break;
case 3: Name = nameof(AMemory.ReadVector64); break;
case 4: Name = nameof(AMemory.ReadVector128); break;
}
}
else
{
switch (Size)
{
case 0: Name = nameof(AMemory.ReadByte); break;
case 1: Name = nameof(AMemory.ReadUInt16); break;
case 2: Name = nameof(AMemory.ReadUInt32); break;
case 3: Name = nameof(AMemory.ReadUInt64); break;
case 4: Name = nameof(AMemory.ReadVector128); break;
}
}
Context.EmitCall(typeof(AMemory), Name);
if (!IsSimd)
{
if (Ext == Extension.Sx32 ||
Ext == Extension.Sx64)
{
@ -67,31 +85,54 @@ namespace ChocolArm64.Instruction
: OpCodes.Conv_U8);
}
}
}
public static void EmitWriteCall(AILEmitterCtx Context, int Size)
{
if (Size < 0 || Size > 4)
bool IsSimd = GetIsSimd(Context);
string Name = null;
if (Size < 0 || Size > (IsSimd ? 4 : 3))
{
throw new ArgumentOutOfRangeException(nameof(Size));
}
if (Size < 3)
if (Size < 3 && !IsSimd)
{
Context.Emit(OpCodes.Conv_I4);
}
string Name = null;
if (IsSimd)
{
switch (Size)
{
case 0: Name = nameof(AMemory.WriteVector8); break;
case 1: Name = nameof(AMemory.WriteVector16); break;
case 2: Name = nameof(AMemory.WriteVector32); break;
case 3: Name = nameof(AMemory.WriteVector64); break;
case 4: Name = nameof(AMemory.WriteVector128); break;
}
}
else
{
switch (Size)
{
case 0: Name = nameof(AMemory.WriteByte); break;
case 1: Name = nameof(AMemory.WriteUInt16); break;
case 2: Name = nameof(AMemory.WriteUInt32); break;
case 3: Name = nameof(AMemory.WriteUInt64); break;
case 4: Name = nameof(AMemory.WriteVector128); break;
}
}
Context.EmitCall(typeof(AMemory), Name);
}
private static bool GetIsSimd(AILEmitterCtx Context)
{
return Context.CurrOp is IAOpCodeSimd &&
!(Context.CurrOp is AOpCodeSimdMemMs ||
Context.CurrOp is AOpCodeSimdMemSs);
}
}
}

View file

@ -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

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

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

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

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

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

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

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

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

View file

@ -13,26 +13,26 @@ namespace ChocolArm64.Instruction
{
AOpCodeSystem Op = (AOpCodeSystem)Context.CurrOp;
Context.EmitLdarg(ATranslatedSub.RegistersArgIdx);
Context.EmitLdarg(ATranslatedSub.StateArgIdx);
string PropName;
switch (GetPackedId(Op))
{
case 0b11_011_0000_0000_001: PropName = nameof(ARegisters.CtrEl0); break;
case 0b11_011_0000_0000_111: PropName = nameof(ARegisters.DczidEl0); break;
case 0b11_011_0100_0100_000: PropName = nameof(ARegisters.Fpcr); break;
case 0b11_011_0100_0100_001: PropName = nameof(ARegisters.Fpsr); break;
case 0b11_011_1101_0000_010: PropName = nameof(ARegisters.TpidrEl0); break;
case 0b11_011_1101_0000_011: PropName = nameof(ARegisters.Tpidr); break;
case 0b11_011_1110_0000_001: PropName = nameof(ARegisters.CntpctEl0); break;
case 0b11_011_0000_0000_001: PropName = nameof(AThreadState.CtrEl0); break;
case 0b11_011_0000_0000_111: PropName = nameof(AThreadState.DczidEl0); break;
case 0b11_011_0100_0100_000: PropName = nameof(AThreadState.Fpcr); break;
case 0b11_011_0100_0100_001: PropName = nameof(AThreadState.Fpsr); break;
case 0b11_011_1101_0000_010: PropName = nameof(AThreadState.TpidrEl0); break;
case 0b11_011_1101_0000_011: PropName = nameof(AThreadState.Tpidr); break;
case 0b11_011_1110_0000_001: PropName = nameof(AThreadState.CntpctEl0); break;
default: throw new NotImplementedException($"Unknown MRS at {Op.Position:x16}");
}
Context.EmitCallPropGet(typeof(ARegisters), PropName);
Context.EmitCallPropGet(typeof(AThreadState), PropName);
PropertyInfo PropInfo = typeof(ARegisters).GetProperty(PropName);
PropertyInfo PropInfo = typeof(AThreadState).GetProperty(PropName);
if (PropInfo.PropertyType != typeof(long) &&
PropInfo.PropertyType != typeof(ulong))
@ -47,21 +47,21 @@ namespace ChocolArm64.Instruction
{
AOpCodeSystem Op = (AOpCodeSystem)Context.CurrOp;
Context.EmitLdarg(ATranslatedSub.RegistersArgIdx);
Context.EmitLdarg(ATranslatedSub.StateArgIdx);
Context.EmitLdintzr(Op.Rt);
string PropName;
switch (GetPackedId(Op))
{
case 0b11_011_0100_0100_000: PropName = nameof(ARegisters.Fpcr); break;
case 0b11_011_0100_0100_001: PropName = nameof(ARegisters.Fpsr); break;
case 0b11_011_1101_0000_010: PropName = nameof(ARegisters.TpidrEl0); break;
case 0b11_011_0100_0100_000: PropName = nameof(AThreadState.Fpcr); break;
case 0b11_011_0100_0100_001: PropName = nameof(AThreadState.Fpsr); break;
case 0b11_011_1101_0000_010: PropName = nameof(AThreadState.TpidrEl0); break;
default: throw new NotImplementedException($"Unknown MSR at {Op.Position:x16}");
}
PropertyInfo PropInfo = typeof(ARegisters).GetProperty(PropName);
PropertyInfo PropInfo = typeof(AThreadState).GetProperty(PropName);
if (PropInfo.PropertyType != typeof(long) &&
PropInfo.PropertyType != typeof(ulong))
@ -69,7 +69,7 @@ namespace ChocolArm64.Instruction
Context.Emit(OpCodes.Conv_U4);
}
Context.EmitCallPropSet(typeof(ARegisters), PropName);
Context.EmitCallPropSet(typeof(AThreadState), PropName);
}
public static void Nop(AILEmitterCtx Context)
@ -89,7 +89,7 @@ namespace ChocolArm64.Instruction
case 0b11_011_0111_0100_001:
{
//DC ZVA
for (int Offs = 0; Offs < (4 << ARegisters.DczSizeLog2); Offs += 8)
for (int Offs = 0; Offs < (4 << AThreadState.DczSizeLog2); Offs += 8)
{
Context.EmitLdarg(ATranslatedSub.MemoryArgIdx);
Context.EmitLdint(Op.Rt);

View file

@ -1,6 +1,7 @@
using ChocolArm64.State;
using ChocolArm64.Translation;
using System;
using System.Runtime.CompilerServices;
namespace ChocolArm64.Instruction
{
@ -97,142 +98,62 @@ namespace ChocolArm64.Instruction
throw new ArgumentException(nameof(Size));
}
public static int SatSingleToInt32(float Value, int FBits)
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static int SatF32ToS32(float Value)
{
if (FBits != 0) Value *= MathF.Pow(2, FBits);
return Value > int.MaxValue ? int.MaxValue :
Value < int.MinValue ? int.MinValue : (int)Value;
}
public static long SatSingleToInt64(float Value, int FBits)
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static long SatF32ToS64(float Value)
{
if (FBits != 0) Value *= MathF.Pow(2, FBits);
return Value > long.MaxValue ? long.MaxValue :
Value < long.MinValue ? long.MinValue : (long)Value;
}
public static uint SatSingleToUInt32(float Value, int FBits)
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static uint SatF32ToU32(float Value)
{
if (FBits != 0) Value *= MathF.Pow(2, FBits);
return Value > uint.MaxValue ? uint.MaxValue :
Value < uint.MinValue ? uint.MinValue : (uint)Value;
}
public static ulong SatSingleToUInt64(float Value, int FBits)
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ulong SatF32ToU64(float Value)
{
if (FBits != 0) Value *= MathF.Pow(2, FBits);
return Value > ulong.MaxValue ? ulong.MaxValue :
Value < ulong.MinValue ? ulong.MinValue : (ulong)Value;
}
public static int SatDoubleToInt32(double Value, int FBits)
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static int SatF64ToS32(double Value)
{
if (FBits != 0) Value *= Math.Pow(2, FBits);
return Value > int.MaxValue ? int.MaxValue :
Value < int.MinValue ? int.MinValue : (int)Value;
}
public static long SatDoubleToInt64(double Value, int FBits)
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static long SatF64ToS64(double Value)
{
if (FBits != 0) Value *= Math.Pow(2, FBits);
return Value > long.MaxValue ? long.MaxValue :
Value < long.MinValue ? long.MinValue : (long)Value;
}
public static uint SatDoubleToUInt32(double Value, int FBits)
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static uint SatF64ToU32(double Value)
{
if (FBits != 0) Value *= Math.Pow(2, FBits);
return Value > uint.MaxValue ? uint.MaxValue :
Value < uint.MinValue ? uint.MinValue : (uint)Value;
}
public static ulong SatDoubleToUInt64(double Value, int FBits)
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ulong SatF64ToU64(double Value)
{
if (FBits != 0) Value *= Math.Pow(2, FBits);
return Value > ulong.MaxValue ? ulong.MaxValue :
Value < ulong.MinValue ? ulong.MinValue : (ulong)Value;
}
public static float Int32ToSingle(int Value, int FBits)
{
float ValueF = Value;
if (FBits != 0) ValueF *= 1 / MathF.Pow(2, FBits);
return ValueF;
}
public static float Int64ToSingle(long Value, int FBits)
{
float ValueF = Value;
if (FBits != 0) ValueF *= 1 / MathF.Pow(2, FBits);
return ValueF;
}
public static float UInt32ToSingle(uint Value, int FBits)
{
float ValueF = Value;
if (FBits != 0) ValueF *= 1 / MathF.Pow(2, FBits);
return ValueF;
}
public static float UInt64ToSingle(ulong Value, int FBits)
{
float ValueF = Value;
if (FBits != 0) ValueF *= 1 / MathF.Pow(2, FBits);
return ValueF;
}
public static double Int32ToDouble(int Value, int FBits)
{
double ValueF = Value;
if (FBits != 0) ValueF *= 1 / Math.Pow(2, FBits);
return ValueF;
}
public static double Int64ToDouble(long Value, int FBits)
{
double ValueF = Value;
if (FBits != 0) ValueF *= 1 / Math.Pow(2, FBits);
return ValueF;
}
public static double UInt32ToDouble(uint Value, int FBits)
{
double ValueF = Value;
if (FBits != 0) ValueF *= 1 / Math.Pow(2, FBits);
return ValueF;
}
public static double UInt64ToDouble(ulong Value, int FBits)
{
double ValueF = Value;
if (FBits != 0) ValueF *= 1 / Math.Pow(2, FBits);
return ValueF;
}
public static ulong SMulHi128(ulong LHS, ulong RHS)
{
long LLo = (uint)(LHS >> 0);
@ -271,114 +192,7 @@ namespace ChocolArm64.Instruction
return ResHi;
}
public static AVec Addp_S(AVec Vector, int Size)
{
ulong Low = ExtractVec(Vector, 0, Size);
ulong High = ExtractVec(Vector, 1, Size);
return InsertVec(new AVec(), 0, Size, Low + High);
}
public static AVec Addp64(AVec LHS, AVec RHS, int Size)
{
return Addp(LHS, RHS, Size, 8);
}
public static AVec Addp128(AVec LHS, AVec RHS, int Size)
{
return Addp(LHS, RHS, Size, 16);
}
private static AVec Addp(AVec LHS, AVec RHS, int Size, int Bytes)
{
AVec Res = new AVec();
int Elems = Bytes >> Size;
int Half = Elems >> 1;
for (int Index = 0; Index < Elems; Index++)
{
int Elem = (Index & (Half - 1)) << 1;
ulong L = Index < Half
? ExtractVec(LHS, Elem + 0, Size)
: ExtractVec(RHS, Elem + 0, Size);
ulong R = Index < Half
? ExtractVec(LHS, Elem + 1, Size)
: ExtractVec(RHS, Elem + 1, Size);
Res = InsertVec(Res, Index, Size, L + R);
}
return Res;
}
public static AVec Bic_Vi64(AVec Res, ulong Imm, int Size)
{
return Bic_Vi(Res, Imm, Size, 8);
}
public static AVec Bic_Vi128(AVec Res, ulong Imm, int Size)
{
return Bic_Vi(Res, Imm, Size, 16);
}
private static AVec Bic_Vi(AVec Res, ulong Imm, int Size, int Bytes)
{
int Elems = Bytes >> Size;
for (int Index = 0; Index < Elems; Index++)
{
ulong Value = ExtractVec(Res, Index, Size);
Res = InsertVec(Res, Index, Size, Value & ~Imm);
}
return Res;
}
public static AVec Cnt64(AVec Vector)
{
AVec Res = new AVec();
Res.B0 = (byte)CountSetBits8(Vector.B0);
Res.B1 = (byte)CountSetBits8(Vector.B1);
Res.B2 = (byte)CountSetBits8(Vector.B2);
Res.B3 = (byte)CountSetBits8(Vector.B3);
Res.B4 = (byte)CountSetBits8(Vector.B4);
Res.B5 = (byte)CountSetBits8(Vector.B5);
Res.B6 = (byte)CountSetBits8(Vector.B6);
Res.B7 = (byte)CountSetBits8(Vector.B7);
return Res;
}
public static AVec Cnt128(AVec Vector)
{
AVec Res = new AVec();
Res.B0 = (byte)CountSetBits8(Vector.B0);
Res.B1 = (byte)CountSetBits8(Vector.B1);
Res.B2 = (byte)CountSetBits8(Vector.B2);
Res.B3 = (byte)CountSetBits8(Vector.B3);
Res.B4 = (byte)CountSetBits8(Vector.B4);
Res.B5 = (byte)CountSetBits8(Vector.B5);
Res.B6 = (byte)CountSetBits8(Vector.B6);
Res.B7 = (byte)CountSetBits8(Vector.B7);
Res.B8 = (byte)CountSetBits8(Vector.B8);
Res.B9 = (byte)CountSetBits8(Vector.B9);
Res.B10 = (byte)CountSetBits8(Vector.B10);
Res.B11 = (byte)CountSetBits8(Vector.B11);
Res.B12 = (byte)CountSetBits8(Vector.B12);
Res.B13 = (byte)CountSetBits8(Vector.B13);
Res.B14 = (byte)CountSetBits8(Vector.B14);
Res.B15 = (byte)CountSetBits8(Vector.B15);
return Res;
}
private static int CountSetBits8(byte Value)
public static int CountSetBits8(byte Value)
{
return (Value >> 0) & 1 + (Value >> 1) & 1 +
(Value >> 2) & 1 + (Value >> 3) & 1 +
@ -386,275 +200,6 @@ namespace ChocolArm64.Instruction
(Value >> 6) & 1 + (Value >> 7);
}
public static AVec Dup_Gp64(ulong Value, int Size)
{
return Dup_Gp(Value, Size, 8);
}
public static AVec Dup_Gp128(ulong Value, int Size)
{
return Dup_Gp(Value, Size, 16);
}
private static AVec Dup_Gp(ulong Value, int Size, int Bytes)
{
AVec Res = new AVec();
for (int Index = 0; Index < (Bytes >> Size); Index++)
{
Res = InsertVec(Res, Index, Size, Value);
}
return Res;
}
public static AVec Dup_S(AVec Vector, int Elem, int Size)
{
return InsertVec(new AVec(), 0, Size, ExtractVec(Vector, Elem, Size));
}
public static AVec Dup_V64(AVec Vector, int Elem, int Size)
{
return Dup_V(Vector, Elem, Size, 8);
}
public static AVec Dup_V128(AVec Vector, int Elem, int Size)
{
return Dup_V(Vector, Elem, Size, 16);
}
private static AVec Dup_V(AVec Vector, int Elem, int Size, int Bytes)
{
AVec Res = new AVec();
ulong Value = ExtractVec(Vector, Elem, Size);
for (Elem = 0; Elem < (Bytes >> Size); Elem++)
{
Res = InsertVec(Res, Elem, Size, Value);
}
return Res;
}
public static AVec Fmla64(AVec Res, AVec LHS, AVec RHS, int Size)
{
return Fmla(Res, LHS, RHS, Size, 2);
}
public static AVec Fmla128(AVec Res, AVec LHS, AVec RHS, int Size)
{
return Fmla(Res, LHS, RHS, Size, 4);
}
private static AVec Fmla(AVec Res, AVec LHS, AVec RHS, int Size, int Bytes)
{
int Elems = Bytes >> Size;
if (Size == 0)
{
for (int Index = 0; Index < Elems; Index++)
{
float L = LHS.ExtractSingle(Index);
float R = RHS.ExtractSingle(Index);
float Addend = Res.ExtractSingle(Index);
Res = AVec.InsertSingle(Res, Index, Addend + L * R);
}
}
else
{
for (int Index = 0; Index < Elems; Index++)
{
double L = LHS.ExtractDouble(Index);
double R = RHS.ExtractDouble(Index);
double Addend = Res.ExtractDouble(Index);
Res = AVec.InsertDouble(Res, Index, Addend + L * R);
}
}
return Res;
}
public static AVec Fmla_Ve64(AVec Res, AVec LHS, AVec RHS, int SIdx, int Size)
{
return Fmla_Ve(Res, LHS, RHS, SIdx, Size, 2);
}
public static AVec Fmla_Ve128(AVec Res, AVec LHS, AVec RHS, int SIdx, int Size)
{
return Fmla_Ve(Res, LHS, RHS, SIdx, Size, 4);
}
private static AVec Fmla_Ve(AVec Res, AVec LHS, AVec RHS, int SIdx, int Size, int Bytes)
{
int Elems = Bytes >> Size;
if (Size == 0)
{
float R = RHS.ExtractSingle(SIdx);
for (int Index = 0; Index < Elems; Index++)
{
float L = LHS.ExtractSingle(Index);
float Addend = Res.ExtractSingle(Index);
Res = AVec.InsertSingle(Res, Index, Addend + L * R);
}
}
else
{
double R = RHS.ExtractDouble(SIdx);
for (int Index = 0; Index < Elems; Index++)
{
double L = LHS.ExtractDouble(Index);
double Addend = Res.ExtractDouble(Index);
Res = AVec.InsertDouble(Res, Index, Addend + L * R);
}
}
return Res;
}
public static AVec Fmov_S(ulong Value, int Elem, int Size)
{
return InsertVec(new AVec(), Elem, Size, Value);
}
public static AVec Fmul_Ve64(AVec LHS, AVec RHS, int SIdx, int Size)
{
return Fmul_Ve(LHS, RHS, SIdx, Size, 2);
}
public static AVec Fmul_Ve128(AVec LHS, AVec RHS, int SIdx, int Size)
{
return Fmul_Ve(LHS, RHS, SIdx, Size, 4);
}
private static AVec Fmul_Ve(AVec LHS, AVec RHS, int SIdx, int Size, int Bytes)
{
AVec Res = new AVec();
int Elems = Bytes >> Size;
if (Size == 0)
{
float R = RHS.ExtractSingle(SIdx);
for (int Index = 0; Index < Elems; Index++)
{
float L = LHS.ExtractSingle(Index);
Res = AVec.InsertSingle(Res, Index, L * R);
}
}
else
{
double R = RHS.ExtractDouble(SIdx);
for (int Index = 0; Index < Elems; Index++)
{
double L = LHS.ExtractDouble(Index);
Res = AVec.InsertDouble(Res, Index, L * R);
}
}
return Res;
}
public static AVec Ins_Gp(AVec Res, ulong Value, int Elem, int Size)
{
return InsertVec(Res, Elem, Size, Value);
}
public static AVec Ins_V(AVec Res, AVec Value, int Src, int Dst, int Size)
{
return InsertVec(Res, Dst, Size, ExtractVec(Value, Src, Size));;
}
public static AVec Orr_Vi64(AVec Res, ulong Imm, int Size)
{
return Orr_Vi(Res, Imm, Size, 8);
}
public static AVec Orr_Vi128(AVec Res, ulong Imm, int Size)
{
return Orr_Vi(Res, Imm, Size, 16);
}
private static AVec Orr_Vi(AVec Res, ulong Imm, int Size, int Bytes)
{
int Elems = Bytes >> Size;
for (int Index = 0; Index < Elems; Index++)
{
ulong Value = ExtractVec(Res, Index, Size);
Res = InsertVec(Res, Index, Size, Value | Imm);
}
return Res;
}
public static AVec Saddw(AVec LHS, AVec RHS, int Size)
{
return Saddw_(LHS, RHS, Size, false);
}
public static AVec Saddw2(AVec LHS, AVec RHS, int Size)
{
return Saddw_(LHS, RHS, Size, true);
}
private static AVec Saddw_(AVec LHS, AVec RHS, int Size, bool High)
{
AVec Res = new AVec();
int Elems = 8 >> Size;
int Part = High ? Elems : 0;
for (int Index = 0; Index < Elems; Index++)
{
long L = ExtractSVec(LHS, Index, Size + 1);
long R = ExtractSVec(RHS, Index + Part, Size);
Res = InsertSVec(Res, Index, Size + 1, L + R);
}
return Res;
}
public static AVec Sshll(AVec Vector, int Shift, int Size)
{
return Sshll_(Vector, Shift, Size, false);
}
public static AVec Sshll2(AVec Vector, int Shift, int Size)
{
return Sshll_(Vector, Shift, Size, true);
}
private static AVec Sshll_(AVec Vector, int Shift, int Size, bool High)
{
AVec Res = new AVec();
int Elems = 8 >> Size;
int Part = High ? Elems : 0;
for (int Index = 0; Index < Elems; Index++)
{
long Value = ExtractSVec(Vector, Index + Part, Size);
Res = InsertSVec(Res, Index, Size + 1, Value << Shift);
}
return Res;
}
public static AVec Tbl1_V64(AVec Vector, AVec Tb0)
{
return Tbl(Vector, 8, Tb0);
@ -704,194 +249,27 @@ namespace ChocolArm64.Instruction
for (int Index = 0; Index < Tb.Length; Index++)
for (int Index2 = 0; Index2 < 16; Index2++)
{
Table[Index * 16 + Index2] = (byte)ExtractVec(Tb[Index], Index2, 0);
Table[Index * 16 + Index2] = (byte)VectorExtractIntZx(Tb[Index], Index2, 0);
}
for (int Index = 0; Index < Bytes; Index++)
{
byte TblIdx = (byte)ExtractVec(Vector, Index, 0);
byte TblIdx = (byte)VectorExtractIntZx(Vector, Index, 0);
if (TblIdx < Table.Length)
{
Res = InsertVec(Res, Index, 0, Table[TblIdx]);
Res = VectorInsertInt(Table[TblIdx], Res, Index, 0);
}
}
return Res;
}
public static AVec Uaddlv64(AVec Vector, int Size)
{
return Uaddlv(Vector, Size, 8);
}
public static AVec Uaddlv128(AVec Vector, int Size)
{
return Uaddlv(Vector, Size, 16);
}
private static AVec Uaddlv(AVec Vector, int Size, int Bytes)
{
int Elems = Bytes >> Size;
ulong Sum = 0;
for (int Index = 0; Index < Elems; Index++)
{
Sum += ExtractVec(Vector, Index, Size);
}
return InsertVec(new AVec(), 0, 3, Sum);
}
public static AVec Uaddw(AVec LHS, AVec RHS, int Size)
{
return Uaddw_(LHS, RHS, Size, false);
}
public static AVec Uaddw2(AVec LHS, AVec RHS, int Size)
{
return Uaddw_(LHS, RHS, Size, true);
}
private static AVec Uaddw_(AVec LHS, AVec RHS, int Size, bool High)
{
AVec Res = new AVec();
int Elems = 8 >> Size;
int Part = High ? Elems : 0;
for (int Index = 0; Index < Elems; Index++)
{
ulong L = ExtractVec(LHS, Index, Size + 1);
ulong R = ExtractVec(RHS, Index + Part, Size);
Res = InsertVec(Res, Index, Size + 1, L + R);
}
return Res;
}
public static AVec Ucvtf_V_F(AVec Vector)
{
return new AVec()
{
S0 = (uint)Vector.W0,
S1 = (uint)Vector.W1,
S2 = (uint)Vector.W2,
S3 = (uint)Vector.W3
};
}
public static AVec Ucvtf_V_D(AVec Vector)
{
return new AVec()
{
D0 = (ulong)Vector.X0,
D1 = (ulong)Vector.X1
};
}
public static AVec Ushll(AVec Vector, int Shift, int Size)
{
return Ushll_(Vector, Shift, Size, false);
}
public static AVec Ushll2(AVec Vector, int Shift, int Size)
{
return Ushll_(Vector, Shift, Size, true);
}
private static AVec Ushll_(AVec Vector, int Shift, int Size, bool High)
{
AVec Res = new AVec();
int Elems = 8 >> Size;
int Part = High ? Elems : 0;
for (int Index = 0; Index < Elems; Index++)
{
ulong Value = ExtractVec(Vector, Index + Part, Size);
Res = InsertVec(Res, Index, Size + 1, Value << Shift);
}
return Res;
}
public static AVec Uzp1_V64(AVec LHS, AVec RHS, int Size)
{
return Uzp(LHS, RHS, Size, 0, 8);
}
public static AVec Uzp1_V128(AVec LHS, AVec RHS, int Size)
{
return Uzp(LHS, RHS, Size, 0, 16);
}
public static AVec Uzp2_V64(AVec LHS, AVec RHS, int Size)
{
return Uzp(LHS, RHS, Size, 1, 8);
}
public static AVec Uzp2_V128(AVec LHS, AVec RHS, int Size)
{
return Uzp(LHS, RHS, Size, 1, 16);
}
private static AVec Uzp(AVec LHS, AVec RHS, int Size, int Part, int Bytes)
{
AVec Res = new AVec();
int Elems = Bytes >> Size;
int Half = Elems >> 1;
for (int Index = 0; Index < Elems; Index++)
{
int Elem = (Index & (Half - 1)) << 1;
ulong Value = Index < Half
? ExtractVec(LHS, Elem + Part, Size)
: ExtractVec(RHS, Elem + Part, Size);
Res = InsertVec(Res, Index, Size, Value);
}
return Res;
}
public static AVec Xtn(AVec Vector, int Size)
{
return Xtn_(Vector, Size, false);
}
public static AVec Xtn2(AVec Vector, int Size)
{
return Xtn_(Vector, Size, true);
}
private static AVec Xtn_(AVec Vector, int Size, bool High)
{
AVec Res = new AVec();
int Elems = 8 >> Size;
int Part = High ? Elems : 0;
for (int Index = 0; Index < Elems; Index++)
{
ulong Value = ExtractVec(Vector, Index, Size + 1);
Res = InsertVec(Res, Index + Part, Size, Value);
}
return Res;
}
public static ulong ExtractVec(AVec Vector, int Index, int Size)
public static ulong VectorExtractIntZx(AVec Vector, int Index, int Size)
{
switch (Size)
{
case 0: return Vector.ExtractByte(Index);
case 0: return Vector.ExtractByte (Index);
case 1: return Vector.ExtractUInt16(Index);
case 2: return Vector.ExtractUInt32(Index);
case 3: return Vector.ExtractUInt64(Index);
@ -900,11 +278,11 @@ namespace ChocolArm64.Instruction
throw new ArgumentOutOfRangeException(nameof(Size));
}
public static long ExtractSVec(AVec Vector, int Index, int Size)
public static long VectorExtractIntSx(AVec Vector, int Index, int Size)
{
switch (Size)
{
case 0: return (sbyte)Vector.ExtractByte(Index);
case 0: return (sbyte)Vector.ExtractByte (Index);
case 1: return (short)Vector.ExtractUInt16(Index);
case 2: return (int)Vector.ExtractUInt32(Index);
case 3: return (long)Vector.ExtractUInt64(Index);
@ -945,31 +323,5 @@ namespace ChocolArm64.Instruction
throw new ArgumentOutOfRangeException(nameof(Size));
}
public static AVec InsertVec(AVec Vector, int Index, int Size, ulong Value)
{
switch (Size)
{
case 0: return AVec.InsertByte(Vector, Index, (byte)Value);
case 1: return AVec.InsertUInt16(Vector, Index, (ushort)Value);
case 2: return AVec.InsertUInt32(Vector, Index, (uint)Value);
case 3: return AVec.InsertUInt64(Vector, Index, (ulong)Value);
}
throw new ArgumentOutOfRangeException(nameof(Size));
}
public static AVec InsertSVec(AVec Vector, int Index, int Size, long Value)
{
switch (Size)
{
case 0: return AVec.InsertByte(Vector, Index, (byte)Value);
case 1: return AVec.InsertUInt16(Vector, Index, (ushort)Value);
case 2: return AVec.InsertUInt32(Vector, Index, (uint)Value);
case 3: return AVec.InsertUInt64(Vector, Index, (ulong)Value);
}
throw new ArgumentOutOfRangeException(nameof(Size));
}
}
}

View file

@ -2,12 +2,13 @@ using ChocolArm64.Exceptions;
using ChocolArm64.State;
using System;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
namespace ChocolArm64.Memory
{
public unsafe class AMemory
{
private const long ErgMask = (4 << ARegisters.ErgSizeLog2) - 1;
private const long ErgMask = (4 << AThreadState.ErgSizeLog2) - 1;
public AMemoryMgr Manager { get; private set; }
@ -64,13 +65,13 @@ namespace ChocolArm64.Memory
}
}
public void SetExclusive(ARegisters Registers, long Position)
public void SetExclusive(AThreadState ThreadState, long Position)
{
Position &= ~ErgMask;
lock (Monitors)
{
if (Monitors.TryGetValue(Registers.ThreadId, out ExMonitor Monitor))
if (Monitors.TryGetValue(ThreadState.ThreadId, out ExMonitor Monitor))
{
ExAddrs.Remove(Monitor.Position);
}
@ -79,20 +80,20 @@ namespace ChocolArm64.Memory
Monitor = new ExMonitor(Position, ExState);
if (!Monitors.TryAdd(Registers.ThreadId, Monitor))
if (!Monitors.TryAdd(ThreadState.ThreadId, Monitor))
{
Monitors[Registers.ThreadId] = Monitor;
Monitors[ThreadState.ThreadId] = Monitor;
}
}
}
public bool TestExclusive(ARegisters Registers, long Position)
public bool TestExclusive(AThreadState ThreadState, long Position)
{
Position &= ~ErgMask;
lock (Monitors)
{
if (!Monitors.TryGetValue(Registers.ThreadId, out ExMonitor Monitor))
if (!Monitors.TryGetValue(ThreadState.ThreadId, out ExMonitor Monitor))
{
return false;
}
@ -101,11 +102,11 @@ namespace ChocolArm64.Memory
}
}
public void ClearExclusive(ARegisters Registers)
public void ClearExclusive(AThreadState ThreadState)
{
lock (Monitors)
{
if (Monitors.TryGetValue(Registers.ThreadId, out ExMonitor Monitor))
if (Monitors.TryGetValue(ThreadState.ThreadId, out ExMonitor Monitor))
{
Monitor.Reset();
ExAddrs.Remove(Monitor.Position);
@ -138,6 +139,7 @@ namespace ChocolArm64.Memory
public int ReadInt32(long Position) => (int)ReadUInt32(Position);
public long ReadInt64(long Position) => (long)ReadUInt64(Position);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public byte ReadByte(long Position)
{
#if DEBUG
@ -147,6 +149,7 @@ namespace ChocolArm64.Memory
return *((byte*)(RamPtr + (uint)Position));
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public ushort ReadUInt16(long Position)
{
#if DEBUG
@ -156,6 +159,7 @@ namespace ChocolArm64.Memory
return *((ushort*)(RamPtr + (uint)Position));
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public uint ReadUInt32(long Position)
{
#if DEBUG
@ -165,6 +169,7 @@ namespace ChocolArm64.Memory
return *((uint*)(RamPtr + (uint)Position));
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public ulong ReadUInt64(long Position)
{
#if DEBUG
@ -174,6 +179,47 @@ namespace ChocolArm64.Memory
return *((ulong*)(RamPtr + (uint)Position));
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public AVec ReadVector8(long Position)
{
#if DEBUG
EnsureAccessIsValid(Position, AMemoryPerm.Read);
#endif
return new AVec() { B0 = ReadByte(Position) };
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public AVec ReadVector16(long Position)
{
#if DEBUG
EnsureAccessIsValid(Position, AMemoryPerm.Read);
#endif
return new AVec() { H0 = ReadUInt16(Position) };
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public AVec ReadVector32(long Position)
{
#if DEBUG
EnsureAccessIsValid(Position, AMemoryPerm.Read);
#endif
return new AVec() { W0 = ReadUInt32(Position) };
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public AVec ReadVector64(long Position)
{
#if DEBUG
EnsureAccessIsValid(Position, AMemoryPerm.Read);
#endif
return new AVec() { X0 = ReadUInt64(Position) };
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public AVec ReadVector128(long Position)
{
#if DEBUG
@ -192,6 +238,7 @@ namespace ChocolArm64.Memory
public void WriteInt32(long Position, int Value) => WriteUInt32(Position, (uint)Value);
public void WriteInt64(long Position, long Value) => WriteUInt64(Position, (ulong)Value);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void WriteByte(long Position, byte Value)
{
#if DEBUG
@ -201,6 +248,7 @@ namespace ChocolArm64.Memory
*((byte*)(RamPtr + (uint)Position)) = Value;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void WriteUInt16(long Position, ushort Value)
{
#if DEBUG
@ -210,6 +258,7 @@ namespace ChocolArm64.Memory
*((ushort*)(RamPtr + (uint)Position)) = Value;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void WriteUInt32(long Position, uint Value)
{
#if DEBUG
@ -219,6 +268,7 @@ namespace ChocolArm64.Memory
*((uint*)(RamPtr + (uint)Position)) = Value;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void WriteUInt64(long Position, ulong Value)
{
#if DEBUG
@ -228,6 +278,47 @@ namespace ChocolArm64.Memory
*((ulong*)(RamPtr + (uint)Position)) = Value;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void WriteVector8(long Position, AVec Value)
{
#if DEBUG
EnsureAccessIsValid(Position, AMemoryPerm.Write);
#endif
WriteByte(Position, Value.B0);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void WriteVector16(long Position, AVec Value)
{
#if DEBUG
EnsureAccessIsValid(Position, AMemoryPerm.Write);
#endif
WriteUInt16(Position, Value.H0);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void WriteVector32(long Position, AVec Value)
{
#if DEBUG
EnsureAccessIsValid(Position, AMemoryPerm.Write);
#endif
WriteUInt32(Position, Value.W0);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void WriteVector64(long Position, AVec Value)
{
#if DEBUG
EnsureAccessIsValid(Position, AMemoryPerm.Write);
#endif
WriteUInt64(Position, Value.X0);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void WriteVector128(long Position, AVec Value)
{
#if DEBUG

View file

@ -1,6 +1,5 @@
using System.IO;
using System.Text;
using System.Threading;
namespace ChocolArm64.Memory
{
@ -21,32 +20,6 @@ namespace ChocolArm64.Memory
}
}
public static int ReadInt32Exclusive(AMemory Memory, long Position)
{
while (!Memory.AcquireAddress(Position))
{
Thread.Yield();
}
int Value = Memory.ReadInt32(Position);
Memory.ReleaseAddress(Position);
return Value;
}
public static void WriteInt32Exclusive(AMemory Memory, long Position, int Value)
{
while (!Memory.AcquireAddress(Position))
{
Thread.Yield();
}
Memory.WriteInt32(Position, Value);
Memory.ReleaseAddress(Position);
}
public static byte[] ReadBytes(AMemory Memory, long Position, int Size)
{
byte[] Data = new byte[Size];

View file

@ -43,10 +43,10 @@ namespace ChocolArm64.State
{
switch ((APState)Index)
{
case APState.VBit: return GetField(nameof(ARegisters.Overflow));
case APState.CBit: return GetField(nameof(ARegisters.Carry));
case APState.ZBit: return GetField(nameof(ARegisters.Zero));
case APState.NBit: return GetField(nameof(ARegisters.Negative));
case APState.VBit: return GetField(nameof(AThreadState.Overflow));
case APState.CBit: return GetField(nameof(AThreadState.Carry));
case APState.ZBit: return GetField(nameof(AThreadState.Zero));
case APState.NBit: return GetField(nameof(AThreadState.Negative));
}
throw new InvalidOperationException();
@ -56,38 +56,38 @@ namespace ChocolArm64.State
{
switch (Index)
{
case 0: return GetField(nameof(ARegisters.X0));
case 1: return GetField(nameof(ARegisters.X1));
case 2: return GetField(nameof(ARegisters.X2));
case 3: return GetField(nameof(ARegisters.X3));
case 4: return GetField(nameof(ARegisters.X4));
case 5: return GetField(nameof(ARegisters.X5));
case 6: return GetField(nameof(ARegisters.X6));
case 7: return GetField(nameof(ARegisters.X7));
case 8: return GetField(nameof(ARegisters.X8));
case 9: return GetField(nameof(ARegisters.X9));
case 10: return GetField(nameof(ARegisters.X10));
case 11: return GetField(nameof(ARegisters.X11));
case 12: return GetField(nameof(ARegisters.X12));
case 13: return GetField(nameof(ARegisters.X13));
case 14: return GetField(nameof(ARegisters.X14));
case 15: return GetField(nameof(ARegisters.X15));
case 16: return GetField(nameof(ARegisters.X16));
case 17: return GetField(nameof(ARegisters.X17));
case 18: return GetField(nameof(ARegisters.X18));
case 19: return GetField(nameof(ARegisters.X19));
case 20: return GetField(nameof(ARegisters.X20));
case 21: return GetField(nameof(ARegisters.X21));
case 22: return GetField(nameof(ARegisters.X22));
case 23: return GetField(nameof(ARegisters.X23));
case 24: return GetField(nameof(ARegisters.X24));
case 25: return GetField(nameof(ARegisters.X25));
case 26: return GetField(nameof(ARegisters.X26));
case 27: return GetField(nameof(ARegisters.X27));
case 28: return GetField(nameof(ARegisters.X28));
case 29: return GetField(nameof(ARegisters.X29));
case 30: return GetField(nameof(ARegisters.X30));
case 31: return GetField(nameof(ARegisters.X31));
case 0: return GetField(nameof(AThreadState.X0));
case 1: return GetField(nameof(AThreadState.X1));
case 2: return GetField(nameof(AThreadState.X2));
case 3: return GetField(nameof(AThreadState.X3));
case 4: return GetField(nameof(AThreadState.X4));
case 5: return GetField(nameof(AThreadState.X5));
case 6: return GetField(nameof(AThreadState.X6));
case 7: return GetField(nameof(AThreadState.X7));
case 8: return GetField(nameof(AThreadState.X8));
case 9: return GetField(nameof(AThreadState.X9));
case 10: return GetField(nameof(AThreadState.X10));
case 11: return GetField(nameof(AThreadState.X11));
case 12: return GetField(nameof(AThreadState.X12));
case 13: return GetField(nameof(AThreadState.X13));
case 14: return GetField(nameof(AThreadState.X14));
case 15: return GetField(nameof(AThreadState.X15));
case 16: return GetField(nameof(AThreadState.X16));
case 17: return GetField(nameof(AThreadState.X17));
case 18: return GetField(nameof(AThreadState.X18));
case 19: return GetField(nameof(AThreadState.X19));
case 20: return GetField(nameof(AThreadState.X20));
case 21: return GetField(nameof(AThreadState.X21));
case 22: return GetField(nameof(AThreadState.X22));
case 23: return GetField(nameof(AThreadState.X23));
case 24: return GetField(nameof(AThreadState.X24));
case 25: return GetField(nameof(AThreadState.X25));
case 26: return GetField(nameof(AThreadState.X26));
case 27: return GetField(nameof(AThreadState.X27));
case 28: return GetField(nameof(AThreadState.X28));
case 29: return GetField(nameof(AThreadState.X29));
case 30: return GetField(nameof(AThreadState.X30));
case 31: return GetField(nameof(AThreadState.X31));
}
throw new InvalidOperationException();
@ -97,38 +97,38 @@ namespace ChocolArm64.State
{
switch (Index)
{
case 0: return GetField(nameof(ARegisters.V0));
case 1: return GetField(nameof(ARegisters.V1));
case 2: return GetField(nameof(ARegisters.V2));
case 3: return GetField(nameof(ARegisters.V3));
case 4: return GetField(nameof(ARegisters.V4));
case 5: return GetField(nameof(ARegisters.V5));
case 6: return GetField(nameof(ARegisters.V6));
case 7: return GetField(nameof(ARegisters.V7));
case 8: return GetField(nameof(ARegisters.V8));
case 9: return GetField(nameof(ARegisters.V9));
case 10: return GetField(nameof(ARegisters.V10));
case 11: return GetField(nameof(ARegisters.V11));
case 12: return GetField(nameof(ARegisters.V12));
case 13: return GetField(nameof(ARegisters.V13));
case 14: return GetField(nameof(ARegisters.V14));
case 15: return GetField(nameof(ARegisters.V15));
case 16: return GetField(nameof(ARegisters.V16));
case 17: return GetField(nameof(ARegisters.V17));
case 18: return GetField(nameof(ARegisters.V18));
case 19: return GetField(nameof(ARegisters.V19));
case 20: return GetField(nameof(ARegisters.V20));
case 21: return GetField(nameof(ARegisters.V21));
case 22: return GetField(nameof(ARegisters.V22));
case 23: return GetField(nameof(ARegisters.V23));
case 24: return GetField(nameof(ARegisters.V24));
case 25: return GetField(nameof(ARegisters.V25));
case 26: return GetField(nameof(ARegisters.V26));
case 27: return GetField(nameof(ARegisters.V27));
case 28: return GetField(nameof(ARegisters.V28));
case 29: return GetField(nameof(ARegisters.V29));
case 30: return GetField(nameof(ARegisters.V30));
case 31: return GetField(nameof(ARegisters.V31));
case 0: return GetField(nameof(AThreadState.V0));
case 1: return GetField(nameof(AThreadState.V1));
case 2: return GetField(nameof(AThreadState.V2));
case 3: return GetField(nameof(AThreadState.V3));
case 4: return GetField(nameof(AThreadState.V4));
case 5: return GetField(nameof(AThreadState.V5));
case 6: return GetField(nameof(AThreadState.V6));
case 7: return GetField(nameof(AThreadState.V7));
case 8: return GetField(nameof(AThreadState.V8));
case 9: return GetField(nameof(AThreadState.V9));
case 10: return GetField(nameof(AThreadState.V10));
case 11: return GetField(nameof(AThreadState.V11));
case 12: return GetField(nameof(AThreadState.V12));
case 13: return GetField(nameof(AThreadState.V13));
case 14: return GetField(nameof(AThreadState.V14));
case 15: return GetField(nameof(AThreadState.V15));
case 16: return GetField(nameof(AThreadState.V16));
case 17: return GetField(nameof(AThreadState.V17));
case 18: return GetField(nameof(AThreadState.V18));
case 19: return GetField(nameof(AThreadState.V19));
case 20: return GetField(nameof(AThreadState.V20));
case 21: return GetField(nameof(AThreadState.V21));
case 22: return GetField(nameof(AThreadState.V22));
case 23: return GetField(nameof(AThreadState.V23));
case 24: return GetField(nameof(AThreadState.V24));
case 25: return GetField(nameof(AThreadState.V25));
case 26: return GetField(nameof(AThreadState.V26));
case 27: return GetField(nameof(AThreadState.V27));
case 28: return GetField(nameof(AThreadState.V28));
case 29: return GetField(nameof(AThreadState.V29));
case 30: return GetField(nameof(AThreadState.V30));
case 31: return GetField(nameof(AThreadState.V31));
}
throw new InvalidOperationException();
@ -136,7 +136,7 @@ namespace ChocolArm64.State
private FieldInfo GetField(string Name)
{
return typeof(ARegisters).GetField(Name);
return typeof(AThreadState).GetField(Name);
}
}
}

View file

@ -2,7 +2,7 @@ using System;
namespace ChocolArm64.State
{
public class ARegisters
public class AThreadState
{
internal const int LRIndex = 30;
internal const int ZRIndex = 31;

View file

@ -26,7 +26,7 @@ namespace ChocolArm64.Translation
{
if (ILEmitter is AILOpCodeLoad Ld && AILEmitter.IsRegIndex(Ld.Index))
{
switch (Ld.IoType & AIoType.Mask)
switch (Ld.IoType)
{
case AIoType.Flag: IntInputs |= ((1L << Ld.Index) << 32) & ~IntOutputs; break;
case AIoType.Int: IntInputs |= (1L << Ld.Index) & ~IntOutputs; break;
@ -37,7 +37,7 @@ namespace ChocolArm64.Translation
{
if (AILEmitter.IsRegIndex(St.Index))
{
switch (St.IoType & AIoType.Mask)
switch (St.IoType)
{
case AIoType.Flag: IntOutputs |= (1L << St.Index) << 32; break;
case AIoType.Int: IntOutputs |= 1L << St.Index; break;

View file

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

View file

@ -86,9 +86,6 @@ namespace ChocolArm64.Translation
ARegister Reg = Subroutine.Params[Index];
Generator.EmitLdarg(Index + ParamsStart);
AILConv.EmitConv(this, GetFieldType(Reg.Type), GetLocalType(Reg));
Generator.EmitStloc(GetLocalIndex(Reg));
}
}

View file

@ -37,6 +37,7 @@ namespace ChocolArm64.Translation
private const int Tmp2Index = -2;
private const int Tmp3Index = -3;
private const int Tmp4Index = -4;
private const int Tmp5Index = -5;
public AILEmitterCtx(ATranslator Translator, ABlock[] Graph, ABlock Root)
{
@ -149,8 +150,8 @@ namespace ChocolArm64.Translation
if (LastCmpOp != null && LastFlagOp == LastCmpOp && BranchOps.ContainsKey(Cond))
{
Ldloc(Tmp3Index, AIoType.Int, GetIntType(LastCmpOp));
Ldloc(Tmp4Index, AIoType.Int, GetIntType(LastCmpOp));
Ldloc(Tmp3Index, AIoType.Int, LastCmpOp.RegisterSize);
Ldloc(Tmp4Index, AIoType.Int, LastCmpOp.RegisterSize);
if (LastCmpOp.Emitter == AInstEmit.Adds)
{
@ -308,7 +309,7 @@ namespace ChocolArm64.Translation
public void EmitLdintzr(int Index)
{
if (Index != ARegisters.ZRIndex)
if (Index != AThreadState.ZRIndex)
{
EmitLdint(Index);
}
@ -320,7 +321,7 @@ namespace ChocolArm64.Translation
public void EmitStintzr(int Index)
{
if (Index != ARegisters.ZRIndex)
if (Index != AThreadState.ZRIndex)
{
EmitStint(Index);
}
@ -343,18 +344,15 @@ namespace ChocolArm64.Translation
public void EmitLdtmp() => EmitLdint(Tmp1Index);
public void EmitSttmp() => EmitStint(Tmp1Index);
public void EmitLdvectmp() => EmitLdvec(Tmp5Index);
public void EmitStvectmp() => EmitStvec(Tmp5Index);
public void EmitLdint(int Index) => Ldloc(Index, AIoType.Int);
public void EmitStint(int Index) => Stloc(Index, AIoType.Int);
public void EmitLdvec(int Index) => Ldloc(Index, AIoType.Vector);
public void EmitStvec(int Index) => Stloc(Index, AIoType.Vector);
public void EmitLdvecsi(int Index) => Ldloc(Index, AIoType.VectorI);
public void EmitStvecsi(int Index) => Stloc(Index, AIoType.VectorI);
public void EmitLdvecsf(int Index) => Ldloc(Index, AIoType.VectorF);
public void EmitStvecsf(int Index) => Stloc(Index, AIoType.VectorF);
public void EmitLdflg(int Index) => Ldloc(Index, AIoType.Flag);
public void EmitStflg(int Index)
{
@ -365,106 +363,17 @@ namespace ChocolArm64.Translation
private void Ldloc(int Index, AIoType IoType)
{
ILBlock.Add(new AILOpCodeLoad(Index, IoType, GetOperType(IoType)));
ILBlock.Add(new AILOpCodeLoad(Index, IoType, CurrOp.RegisterSize));
}
private void Ldloc(int Index, AIoType IoType, Type Type)
private void Ldloc(int Index, AIoType IoType, ARegisterSize RegisterSize)
{
ILBlock.Add(new AILOpCodeLoad(Index, IoType, Type));
ILBlock.Add(new AILOpCodeLoad(Index, IoType, RegisterSize));
}
private void Stloc(int Index, AIoType IoType)
{
ILBlock.Add(new AILOpCodeStore(Index, IoType, GetOutOperType(IoType)));
}
private Type GetOutOperType(AIoType IoType)
{
//This instruction is used to convert between floating point
//types, so the input and output types are different.
if (CurrOp.Emitter == AInstEmit.Fcvt_S)
{
return GetFloatType(((AOpCodeSimd)CurrOp).Opc);
}
else
{
return GetOperType(IoType);
}
}
private Type GetOperType(AIoType IoType)
{
switch (IoType & AIoType.Mask)
{
case AIoType.Flag: return typeof(bool);
case AIoType.Int: return GetIntType(CurrOp);
case AIoType.Vector: return GetVecType(CurrOp, IoType);
}
throw new ArgumentException(nameof(IoType));
}
private Type GetIntType(AOpCode OpCode)
{
//Always default to 64-bits.
return OpCode.RegisterSize == ARegisterSize.Int32
? typeof(uint)
: typeof(ulong);
}
private Type GetVecType(AOpCode OpCode, AIoType IoType)
{
if (!(OpCode is IAOpCodeSimd Op))
{
return typeof(AVec);
}
int Size = Op.Size;
if (Op.Emitter == AInstEmit.Fmov_Ftoi ||
Op.Emitter == AInstEmit.Fmov_Itof)
{
Size |= 2;
}
if (Op is AOpCodeMem || Op is IAOpCodeLit)
{
return Size < 4 ? typeof(ulong) : typeof(AVec);
}
else if (IoType == AIoType.VectorI)
{
return GetIntType(Size);
}
else if (IoType == AIoType.VectorF)
{
return GetFloatType(Size);
}
return typeof(AVec);
}
private static Type GetIntType(int Size)
{
switch (Size)
{
case 0: return typeof(byte);
case 1: return typeof(ushort);
case 2: return typeof(uint);
case 3: return typeof(ulong);
}
throw new ArgumentOutOfRangeException(nameof(Size));
}
private static Type GetFloatType(int Size)
{
switch (Size)
{
case 0: return typeof(float);
case 1: return typeof(double);
}
throw new ArgumentOutOfRangeException(nameof(Size));
ILBlock.Add(new AILOpCodeStore(Index, IoType, CurrOp.RegisterSize));
}
public void EmitCallPropGet(Type ObjType, string PropName)

View file

@ -1,5 +1,4 @@
using ChocolArm64.State;
using System;
using System.Reflection.Emit;
namespace ChocolArm64.Translation
@ -10,41 +9,38 @@ namespace ChocolArm64.Translation
public AIoType IoType { get; private set; }
public Type OperType { get; private set; }
public ARegisterSize RegisterSize { get; private set; }
public AILOpCodeLoad(int Index, AIoType IoType) : this(Index, IoType, null) { }
public AILOpCodeLoad(int Index, AIoType IoType) : this(Index, IoType, ARegisterSize.Int64) { }
public AILOpCodeLoad(int Index, AIoType IoType, Type OperType)
public AILOpCodeLoad(int Index, AIoType IoType, ARegisterSize RegisterSize)
{
this.IoType = IoType;
this.Index = Index;
this.OperType = OperType;
this.RegisterSize = RegisterSize;
}
public void Emit(AILEmitter Context)
{
switch (IoType & AIoType.Mask)
switch (IoType)
{
case AIoType.Arg: EmitLdarg(Context, Index); break;
case AIoType.Fields: EmitLdfld(Context, Index); break;
case AIoType.Flag: EmitLdloc(Context, Index, ARegisterType.Flag); break;
case AIoType.Int: EmitLdloc(Context, Index, ARegisterType.Int); break;
case AIoType.Vector: EmitLdloc(Context, Index, ARegisterType.Vector); break;
}
}
case AIoType.Arg: Context.Generator.EmitLdarg(Index); break;
private void EmitLdarg(AILEmitter Context, int Index)
{
Context.Generator.EmitLdarg(Index);
}
private void EmitLdfld(AILEmitter Context, int Index)
case AIoType.Fields:
{
long IntInputs = Context.LocalAlloc.GetIntInputs(Context.GetILBlock(Index));
long VecInputs = Context.LocalAlloc.GetVecInputs(Context.GetILBlock(Index));
LoadLocals(Context, IntInputs, ARegisterType.Int);
LoadLocals(Context, VecInputs, ARegisterType.Vector);
break;
}
case AIoType.Flag: EmitLdloc(Context, Index, ARegisterType.Flag); break;
case AIoType.Int: EmitLdloc(Context, Index, ARegisterType.Int); break;
case AIoType.Vector: EmitLdloc(Context, Index, ARegisterType.Vector); break;
}
}
private void LoadLocals(AILEmitter Context, long Inputs, ARegisterType BaseType)
@ -57,26 +53,25 @@ namespace ChocolArm64.Translation
{
ARegister Reg = AILEmitter.GetRegFromBit(Bit, BaseType);
Context.Generator.EmitLdarg(ATranslatedSub.RegistersArgIdx);
Context.Generator.EmitLdarg(ATranslatedSub.StateArgIdx);
Context.Generator.Emit(OpCodes.Ldfld, Reg.GetField());
AILConv.EmitConv(
Context,
Context.GetFieldType(Reg.Type),
Context.GetLocalType(Reg));
Context.Generator.EmitStloc(Context.GetLocalIndex(Reg));
}
}
}
private void EmitLdloc(AILEmitter Context, int Index, ARegisterType Type)
private void EmitLdloc(AILEmitter Context, int Index, ARegisterType RegisterType)
{
ARegister Reg = new ARegister(Index, Type);
ARegister Reg = new ARegister(Index, RegisterType);
Context.Generator.EmitLdloc(Context.GetLocalIndex(Reg));
AILConv.EmitConv(Context, Context.GetLocalType(Reg), OperType);
if (RegisterType == ARegisterType.Int &&
RegisterSize == ARegisterSize.Int32)
{
Context.Generator.Emit(OpCodes.Conv_U4);
}
}
}
}

View file

@ -1,50 +1,46 @@
using ChocolArm64.State;
using System;
using System.Reflection.Emit;
namespace ChocolArm64.Translation
{
struct AILOpCodeStore : IAILEmit
{
public AIoType IoType { get; private set; }
public Type OperType { get; private set; }
public int Index { get; private set; }
public AILOpCodeStore(int Index, AIoType IoType) : this(Index, IoType, null) { }
public AIoType IoType { get; private set; }
public AILOpCodeStore(int Index, AIoType IoType, Type OperType)
public ARegisterSize RegisterSize { get; private set; }
public AILOpCodeStore(int Index, AIoType IoType) : this(Index, IoType, ARegisterSize.Int64) { }
public AILOpCodeStore(int Index, AIoType IoType, ARegisterSize RegisterSize)
{
this.IoType = IoType;
this.Index = Index;
this.OperType = OperType;
this.RegisterSize = RegisterSize;
}
public void Emit(AILEmitter Context)
{
switch (IoType & AIoType.Mask)
switch (IoType)
{
case AIoType.Arg: EmitStarg(Context, Index); break;
case AIoType.Fields: EmitStfld(Context, Index); break;
case AIoType.Flag: EmitStloc(Context, Index, ARegisterType.Flag); break;
case AIoType.Int: EmitStloc(Context, Index, ARegisterType.Int); break;
case AIoType.Vector: EmitStloc(Context, Index, ARegisterType.Vector); break;
}
}
case AIoType.Arg: Context.Generator.EmitStarg(Index); break;
private void EmitStarg(AILEmitter Context, int Index)
{
Context.Generator.EmitStarg(Index);
}
private void EmitStfld(AILEmitter Context, int Index)
case AIoType.Fields:
{
long IntOutputs = Context.LocalAlloc.GetIntOutputs(Context.GetILBlock(Index));
long VecOutputs = Context.LocalAlloc.GetVecOutputs(Context.GetILBlock(Index));
StoreLocals(Context, IntOutputs, ARegisterType.Int);
StoreLocals(Context, VecOutputs, ARegisterType.Vector);
break;
}
case AIoType.Flag: EmitStloc(Context, Index, ARegisterType.Flag); break;
case AIoType.Int: EmitStloc(Context, Index, ARegisterType.Int); break;
case AIoType.Vector: EmitStloc(Context, Index, ARegisterType.Vector); break;
}
}
private void StoreLocals(AILEmitter Context, long Outputs, ARegisterType BaseType)
@ -57,24 +53,23 @@ namespace ChocolArm64.Translation
{
ARegister Reg = AILEmitter.GetRegFromBit(Bit, BaseType);
Context.Generator.EmitLdarg(ATranslatedSub.RegistersArgIdx);
Context.Generator.EmitLdarg(ATranslatedSub.StateArgIdx);
Context.Generator.EmitLdloc(Context.GetLocalIndex(Reg));
AILConv.EmitConv(
Context,
Context.GetLocalType(Reg),
Context.GetFieldType(Reg.Type));
Context.Generator.Emit(OpCodes.Stfld, Reg.GetField());
}
}
}
private void EmitStloc(AILEmitter Context, int Index, ARegisterType Type)
private void EmitStloc(AILEmitter Context, int Index, ARegisterType RegisterType)
{
ARegister Reg = new ARegister(Index, Type);
ARegister Reg = new ARegister(Index, RegisterType);
AILConv.EmitConv(Context, OperType, Context.GetLocalType(Reg));
if (RegisterType == ARegisterType.Int &&
RegisterSize == ARegisterSize.Int32)
{
Context.Generator.Emit(OpCodes.Conv_U8);
}
Context.Generator.EmitStloc(Context.GetLocalIndex(Reg));
}

View file

@ -10,9 +10,6 @@ namespace ChocolArm64.Translation
Flag,
Int,
Float,
Vector,
Mask = 0xff,
VectorI = Vector | 1 << 8,
VectorF = Vector | 1 << 9
Vector
}
}

185
Ryujinx/Hid.cs Normal file
View 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);
}
}
}

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

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

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

View file

@ -1,6 +1,6 @@
using ChocolArm64.Memory;
using Ryujinx.OsHle.Handles;
using System.Collections.Generic;
using System.Threading;
namespace Ryujinx.OsHle
{
@ -11,6 +11,8 @@ namespace Ryujinx.OsHle
private long CondVarAddress;
private long Timeout;
private bool OwnsCondVarValue;
private List<HThread> WaitingThreads;
public CondVar(Process Process, long CondVarAddress, long Timeout)
@ -24,34 +26,43 @@ namespace Ryujinx.OsHle
public void WaitForSignal(HThread Thread)
{
int Count = ReadCondVarValue();
int Count = Process.Memory.ReadInt32(CondVarAddress);
if (Count <= 0)
{
//FIXME: We shouldn't need to do that?
Process.Scheduler.Yield(Thread);
return;
}
WriteCondVarValue(Count - 1);
lock (WaitingThreads)
{
WaitingThreads.Add(Thread);
}
if (Timeout != -1)
{
Process.Scheduler.WaitForSignal(Thread, (int)(Timeout / 1000000));
}
else
if (Timeout == -1)
{
Process.Scheduler.WaitForSignal(Thread);
}
else
{
Process.Scheduler.WaitForSignal(Thread, (int)(Timeout / 1000000));
lock (WaitingThreads)
{
WaitingThreads.Remove(Thread);
}
}
}
public void SetSignal(int Count)
AcquireCondVarValue();
Count = Process.Memory.ReadInt32(CondVarAddress);
if (Count > 0)
{
Process.Memory.WriteInt32(CondVarAddress, Count - 1);
}
ReleaseCondVarValue();
}
public void SetSignal(HThread Thread, int Count)
{
lock (WaitingThreads)
{
@ -59,7 +70,11 @@ namespace Ryujinx.OsHle
{
Process.Scheduler.Signal(WaitingThreads.ToArray());
WriteCondVarValue(WaitingThreads.Count);
AcquireCondVarValue();
Process.Memory.WriteInt32(CondVarAddress, WaitingThreads.Count);
ReleaseCondVarValue();
WaitingThreads.Clear();
}
@ -85,19 +100,39 @@ namespace Ryujinx.OsHle
WaitingThreads.RemoveAt(HighestPrioIndex);
}
WriteCondVarValue(Count);
}
AcquireCondVarValue();
Process.Memory.WriteInt32(CondVarAddress, Count);
ReleaseCondVarValue();
}
}
private int ReadCondVarValue()
{
return AMemoryHelper.ReadInt32Exclusive(Process.Memory, CondVarAddress);
Process.Scheduler.Suspend(Thread.ProcessorId);
Process.Scheduler.Resume(Thread);
}
private void WriteCondVarValue(int Value)
private void AcquireCondVarValue()
{
AMemoryHelper.WriteInt32Exclusive(Process.Memory, CondVarAddress, Value);
if (!OwnsCondVarValue)
{
while (!Process.Memory.AcquireAddress(CondVarAddress))
{
Thread.Yield();
}
OwnsCondVarValue = true;
}
}
private void ReleaseCondVarValue()
{
if (OwnsCondVarValue)
{
OwnsCondVarValue = false;
Process.Memory.ReleaseAddress(CondVarAddress);
}
}
}
}

View file

@ -1,13 +1,76 @@
using System;
using System.Collections.Generic;
namespace Ryujinx.OsHle.Handles
{
class HSharedMem
{
public long PhysPos { get; private set; }
public long VirtPos { get; set; }
private List<long> Positions;
public int PositionsCount => Positions.Count;
public EventHandler<EventArgs> MemoryMapped;
public EventHandler<EventArgs> MemoryUnmapped;
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;
}
}
}
}

View file

@ -1,4 +1,5 @@
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Threading;
@ -6,19 +7,8 @@ namespace Ryujinx.OsHle.Handles
{
class KProcessScheduler : IDisposable
{
private enum ThreadState
{
WaitingToRun,
WaitingSignal,
Running
}
private class SchedulerThread : IDisposable
{
public bool Signaled { get; set; }
public ThreadState State { get; set; }
public HThread Thread { get; private set; }
public AutoResetEvent WaitEvent { get; private set; }
@ -44,9 +34,70 @@ namespace Ryujinx.OsHle.Handles
}
}
private Dictionary<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;
@ -54,13 +105,13 @@ namespace Ryujinx.OsHle.Handles
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>();
@ -72,132 +123,171 @@ namespace Ryujinx.OsHle.Handles
{
lock (SchedLock)
{
if (AllThreads.ContainsKey(Thread))
SchedulerThread SchedThread = new SchedulerThread(Thread);
if (!AllThreads.TryAdd(Thread, SchedThread))
{
return;
}
SchedulerThread SchedThread = new SchedulerThread(Thread);
AllThreads.Add(Thread, SchedThread);
if (!ActiveProcessors.Contains(Thread.ProcessorId))
{
ActiveProcessors.Add(Thread.ProcessorId);
Thread.Thread.Execute();
SetThreadAsRunning(SchedThread);
SchedThread.State = ThreadState.Running;
Logging.Debug($"{GetDbgThreadInfo(Thread)} running.");
}
else
{
InsertSorted(SchedThread);
SchedThread.State = ThreadState.WaitingToRun;
WaitingToRun[Thread.ProcessorId].Push(SchedThread);
Logging.Debug($"{GetDbgThreadInfo(SchedThread.Thread)} waiting to run.");
}
}
}
public void WaitForSignal(HThread Thread, int TimeoutMs)
public void Suspend(int ProcessorId)
{
Logging.Debug($"{GetDbgThreadInfo(Thread)} entering signal wait state with timeout.");
lock (SchedLock)
{
SchedulerThread SchedThread = WaitingToRun[ProcessorId].Pop();
PutThreadToWait(Thread, ThreadState.WaitingSignal, TimeoutMs);
if (SchedThread != null)
{
RunThread(SchedThread);
}
else
{
ActiveProcessors.Remove(ProcessorId);
}
}
}
public void WaitForSignal(HThread Thread)
public void Resume(HThread CurrThread)
{
SchedulerThread SchedThread;
Logging.Debug($"{GetDbgThreadInfo(CurrThread)} entering ipc delay wait state.");
lock (SchedLock)
{
if (!AllThreads.TryGetValue(CurrThread, out SchedThread))
{
Logging.Error($"{GetDbgThreadInfo(CurrThread)} was not found on the scheduler queue!");
return;
}
}
TryResumingExecution(SchedThread);
}
public void WaitForSignal(HThread Thread, int Timeout = -1)
{
SchedulerThread SchedThread;
Logging.Debug($"{GetDbgThreadInfo(Thread)} entering signal wait state.");
PutThreadToWait(Thread, ThreadState.WaitingSignal);
lock (SchedLock)
{
SchedThread = WaitingToRun[Thread.ProcessorId].Pop();
if (SchedThread != null)
{
RunThread(SchedThread);
}
else
{
ActiveProcessors.Remove(Thread.ProcessorId);
}
if (!AllThreads.TryGetValue(Thread, out SchedThread))
{
Logging.Error($"{GetDbgThreadInfo(Thread)} was not found on the scheduler queue!");
return;
}
}
if (Timeout >= 0)
{
Logging.Debug($"{GetDbgThreadInfo(Thread)} has wait timeout of {Timeout}ms.");
SchedThread.WaitEvent.WaitOne(Timeout);
}
else
{
SchedThread.WaitEvent.WaitOne();
}
TryResumingExecution(SchedThread);
}
private void TryResumingExecution(SchedulerThread SchedThread)
{
HThread Thread = SchedThread.Thread;
lock (SchedLock)
{
if (ActiveProcessors.Add(Thread.ProcessorId))
{
Logging.Debug($"{GetDbgThreadInfo(Thread)} resuming execution...");
return;
}
WaitingToRun[Thread.ProcessorId].Push(SchedThread);
}
SchedThread.WaitEvent.WaitOne();
Logging.Debug($"{GetDbgThreadInfo(Thread)} resuming execution...");
}
public void Yield(HThread Thread)
{
Logging.Debug($"{GetDbgThreadInfo(Thread)} yielded execution.");
if (WaitingThreads[Thread.ProcessorId].Count == 0)
{
Logging.Debug($"{GetDbgThreadInfo(Thread)} resumed because theres nothing to run.");
return;
}
PutThreadToWait(Thread, ThreadState.WaitingToRun);
}
private void PutThreadToWait(HThread Thread, ThreadState State, int TimeoutMs = -1)
{
SchedulerThread SchedThread;
Logging.Debug($"{GetDbgThreadInfo(Thread)} yielded execution.");
lock (SchedLock)
{
SchedThread = WaitingToRun[Thread.ProcessorId].Pop(Thread.Priority);
if (SchedThread == null)
{
Logging.Debug($"{GetDbgThreadInfo(Thread)} resumed because theres nothing better to run.");
return;
}
RunThread(SchedThread);
if (!AllThreads.TryGetValue(Thread, out SchedThread))
{
return;
}
if (SchedThread.Signaled && SchedThread.State == ThreadState.WaitingSignal)
{
SchedThread.Signaled = false;
Logging.Error($"{GetDbgThreadInfo(Thread)} was not found on the scheduler queue!");
return;
}
ActiveProcessors.Remove(Thread.ProcessorId);
SchedThread.State = State;
TryRunningWaitingThead(SchedThread.Thread.ProcessorId);
if (State == ThreadState.WaitingSignal)
{
InsertSorted(SchedThread);
}
else
{
InsertAtEnd(SchedThread);
}
}
if (TimeoutMs >= 0)
{
Logging.Debug($"{GetDbgThreadInfo(SchedThread.Thread)} waiting with timeout of {TimeoutMs}ms.");
SchedThread.WaitEvent.WaitOne(TimeoutMs);
}
else
{
Logging.Debug($"{GetDbgThreadInfo(SchedThread.Thread)} waiting indefinitely.");
SchedThread.WaitEvent.WaitOne();
}
while (true)
{
lock (SchedLock)
{
Logging.Debug($"Trying to run {GetDbgThreadInfo(SchedThread.Thread)}.");
if (!ActiveProcessors.Contains(SchedThread.Thread.ProcessorId))
{
SetThreadAsRunning(SchedThread);
break;
}
else
{
SchedThread.State = ThreadState.WaitingToRun;
Logging.Debug($"{GetDbgThreadInfo(SchedThread.Thread)} waiting to run.");
}
WaitingToRun[Thread.ProcessorId].Push(SchedThread);
}
SchedThread.WaitEvent.WaitOne();
Logging.Debug($"{GetDbgThreadInfo(Thread)} resuming execution...");
}
private void RunThread(SchedulerThread SchedThread)
{
if (!SchedThread.Thread.Thread.Execute())
{
SchedThread.WaitEvent.Set();
}
else
{
Logging.Debug($"{GetDbgThreadInfo(SchedThread.Thread)} running.");
}
}
@ -205,105 +295,19 @@ namespace Ryujinx.OsHle.Handles
{
lock (SchedLock)
{
HashSet<int> SignaledProcessorIds = new HashSet<int>();
foreach (HThread Thread in Threads)
{
if (AllThreads.TryGetValue(Thread, out SchedulerThread SchedThread))
{
if (!WaitingToRun[Thread.ProcessorId].HasThread(SchedThread))
{
Logging.Debug($"{GetDbgThreadInfo(Thread)} signaled.");
if (AllThreads.TryGetValue(Thread, out SchedulerThread SchedThread))
{
if (SchedThread.State == ThreadState.WaitingSignal)
{
SchedThread.State = ThreadState.WaitingToRun;
SignaledProcessorIds.Add(Thread.ProcessorId);
}
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)
@ -320,15 +324,10 @@ namespace Ryujinx.OsHle.Handles
{
if (Disposing)
{
foreach (Queue<SchedulerThread> SchedThreads in WaitingThreads)
{
foreach (SchedulerThread SchedThread in SchedThreads)
foreach (SchedulerThread SchedThread in AllThreads.Values)
{
SchedThread.Dispose();
}
SchedThreads.Clear();
}
}
}
}

View file

@ -4,6 +4,7 @@ using Ryujinx.OsHle.Handles;
using Ryujinx.OsHle.Utilities;
using System.Collections.Concurrent;
using System.IO;
using System;
namespace Ryujinx.OsHle
{
@ -30,6 +31,8 @@ namespace Ryujinx.OsHle
private ConcurrentDictionary<int, Process> Processes;
private HSharedMem HidSharedMem;
private AMemoryAlloc Allocator;
private Switch Ns;
@ -55,7 +58,12 @@ namespace Ryujinx.OsHle
HidOffset = Allocator.Alloc(HidSize);
FontOffset = Allocator.Alloc(FontSize);
HidHandle = Handles.GenerateId(new HSharedMem(HidOffset));
HidSharedMem = new HSharedMem(HidOffset);
HidSharedMem.MemoryMapped += HidInit;
HidHandle = Handles.GenerateId(HidSharedMem);
FontHandle = Handles.GenerateId(new HSharedMem(FontOffset));
}
@ -79,6 +87,8 @@ namespace Ryujinx.OsHle
continue;
}
Logging.Info($"Loading {Path.GetFileNameWithoutExtension(File)}...");
using (FileStream Input = new FileStream(File, FileMode.Open))
{
Nso Program = new Nso(Input);
@ -136,6 +146,23 @@ namespace Ryujinx.OsHle
}
}
internal bool ExitProcess(int ProcessId)
{
bool Success = Processes.TryRemove(ProcessId, out Process Process);
if (Success)
{
Process.StopAllThreads();
}
if (Processes.Count == 0)
{
Ns.OnFinish(EventArgs.Empty);
}
return Success;
}
internal bool TryGetProcess(int ProcessId, out Process Process)
{
if (!Processes.TryGetValue(ProcessId, out Process))
@ -161,11 +188,15 @@ namespace Ryujinx.OsHle
Handles.Delete(Handle);
}
public long GetVirtHidOffset()
private void HidInit(object sender, EventArgs e)
{
HSharedMem HidSharedMem = Handles.GetData<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);
}
}
}
}

View file

@ -1,4 +1,3 @@
using ChocolArm64.Memory;
using Ryujinx.OsHle.Handles;
using System.Collections.Concurrent;
using System.Threading;
@ -13,6 +12,8 @@ namespace Ryujinx.OsHle
private long MutexAddress;
private bool OwnsMutexValue;
private object EnterWaitLock;
private ConcurrentQueue<HThread> WaitingThreads;
@ -31,9 +32,11 @@ namespace Ryujinx.OsHle
public void WaitForLock(HThread RequestingThread, int RequestingThreadHandle)
{
AcquireMutexValue();
lock (EnterWaitLock)
{
int CurrentThreadHandle = ReadMutexValue() & ~MutexHasListenersMask;
int CurrentThreadHandle = Process.Memory.ReadInt32(MutexAddress) & ~MutexHasListenersMask;
if (CurrentThreadHandle == RequestingThreadHandle ||
CurrentThreadHandle == 0)
@ -41,7 +44,9 @@ namespace Ryujinx.OsHle
return;
}
WriteMutexValue(CurrentThreadHandle | MutexHasListenersMask);
Process.Memory.WriteInt32(MutexAddress, CurrentThreadHandle | MutexHasListenersMask);
ReleaseMutexValue();
WaitingThreads.Enqueue(RequestingThread);
}
@ -51,24 +56,32 @@ namespace Ryujinx.OsHle
public void GiveUpLock(int ThreadHandle)
{
AcquireMutexValue();
lock (EnterWaitLock)
{
int CurrentThread = ReadMutexValue() & ~MutexHasListenersMask;
int CurrentThread = Process.Memory.ReadInt32(MutexAddress) & ~MutexHasListenersMask;
if (CurrentThread == ThreadHandle)
{
Unlock();
}
}
ReleaseMutexValue();
}
public void Unlock()
{
AcquireMutexValue();
lock (EnterWaitLock)
{
int HasListeners = WaitingThreads.Count > 1 ? MutexHasListenersMask : 0;
WriteMutexValue(HasListeners);
Process.Memory.WriteInt32(MutexAddress, HasListeners);
ReleaseMutexValue();
HThread[] UnlockedThreads = new HThread[WaitingThreads.Count];
@ -83,14 +96,27 @@ namespace Ryujinx.OsHle
}
}
private int ReadMutexValue()
private void AcquireMutexValue()
{
return AMemoryHelper.ReadInt32Exclusive(Process.Memory, MutexAddress);
if (!OwnsMutexValue)
{
while (!Process.Memory.AcquireAddress(MutexAddress))
{
Thread.Yield();
}
private void WriteMutexValue(int Value)
OwnsMutexValue = true;
}
}
private void ReleaseMutexValue()
{
AMemoryHelper.WriteInt32Exclusive(Process.Memory, MutexAddress, Value);
if (OwnsMutexValue)
{
OwnsMutexValue = false;
Process.Memory.ReleaseAddress(MutexAddress);
}
}
}
}

View file

@ -101,17 +101,17 @@ namespace Ryujinx.OsHle.Objects.Aud
KeysQueue.Enqueue(BufferId);
byte[] AudioOutBuffer = AMemoryHelper.ReadBytes(Context.Memory, Context.Request.SendBuff[0].Position, 0x28);
byte[] AudioOutBuffer = AMemoryHelper.ReadBytes(Context.Memory, Context.Request.SendBuff[0].Position, sizeof(long) * 5);
using (MemoryStream MS = new MemoryStream(AudioOutBuffer))
{
BinaryReader Reader = new BinaryReader(MS);
long PointerToSampleDataPointer = Reader.ReadInt64();
long PointerToSampleData = Reader.ReadInt64();
long PointerNextBuffer = Reader.ReadInt64();
long PointerSampleBuffer = Reader.ReadInt64();
long CapacitySampleBuffer = Reader.ReadInt64();
long SizeDataSampleBuffer = Reader.ReadInt64();
long Unknown = Reader.ReadInt64();
long SizeDataInSampleBuffer = Reader.ReadInt64();
long OffsetDataInSampleBuffer = Reader.ReadInt64();
byte[] AudioSampleBuffer = AMemoryHelper.ReadBytes(Context.Memory, PointerToSampleData, (int)SizeDataSampleBuffer);
byte[] AudioSampleBuffer = AMemoryHelper.ReadBytes(Context.Memory, PointerSampleBuffer + OffsetDataInSampleBuffer, (int)SizeDataInSampleBuffer);
if (OpenALInstalled)
{
@ -146,7 +146,8 @@ namespace Ryujinx.OsHle.Objects.Aud
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)
{

View file

@ -28,7 +28,7 @@ namespace Ryujinx.OsHle.Objects.Time
{
DateTime CurrentTime = DateTime.Now;
if (ClockType == SystemClockType.Standard ||
if (ClockType == SystemClockType.User ||
ClockType == SystemClockType.Network)
{
CurrentTime = CurrentTime.ToUniversalTime();

View file

@ -2,7 +2,7 @@ namespace Ryujinx.OsHle.Objects.Time
{
enum SystemClockType
{
Standard,
User,
Network,
Local
}

View file

@ -70,6 +70,8 @@ namespace Ryujinx.OsHle
public void LoadProgram(IExecutable Program)
{
Logging.Info($"Image base at 0x{ImageBase:x16}.");
Executable Executable = new Executable(Program, Memory, ImageBase);
Executables.Add(Executable);
@ -174,19 +176,19 @@ namespace Ryujinx.OsHle
return -1;
}
Thread.Registers.Break += BreakHandler;
Thread.Registers.SvcCall += SvcHandler.SvcCall;
Thread.Registers.Undefined += UndefinedHandler;
Thread.Registers.ProcessId = ProcessId;
Thread.Registers.ThreadId = Ns.Os.IdGen.GenerateId();
Thread.Registers.Tpidr = TlsPageAddr + TlsSlot * TlsSize;
Thread.Registers.X0 = (ulong)ArgsPtr;
Thread.Registers.X1 = (ulong)Handle;
Thread.Registers.X31 = (ulong)StackTop;
Thread.ThreadState.Break += BreakHandler;
Thread.ThreadState.SvcCall += SvcHandler.SvcCall;
Thread.ThreadState.Undefined += UndefinedHandler;
Thread.ThreadState.ProcessId = ProcessId;
Thread.ThreadState.ThreadId = Ns.Os.IdGen.GenerateId();
Thread.ThreadState.Tpidr = TlsPageAddr + TlsSlot * TlsSize;
Thread.ThreadState.X0 = (ulong)ArgsPtr;
Thread.ThreadState.X1 = (ulong)Handle;
Thread.ThreadState.X31 = (ulong)StackTop;
Thread.WorkFinished += ThreadFinished;
ThreadsByTpidr.TryAdd(Thread.Registers.Tpidr, ThreadHnd);
ThreadsByTpidr.TryAdd(Thread.ThreadState.Tpidr, ThreadHnd);
return Handle;
}
@ -218,7 +220,7 @@ namespace Ryujinx.OsHle
{
if (sender is AThread Thread)
{
TlsSlots.TryRemove(GetTlsSlot(Thread.Registers.Tpidr), out _);
TlsSlots.TryRemove(GetTlsSlot(Thread.ThreadState.Tpidr), out _);
Ns.Os.IdGen.DeleteId(Thread.ThreadId);
}
@ -229,9 +231,14 @@ namespace Ryujinx.OsHle
return (int)((Position - TlsPageAddr) / TlsSize);
}
public bool TryGetThread(long Tpidr, out HThread Thread)
public HThread GetThread(long Tpidr)
{
return ThreadsByTpidr.TryGetValue(Tpidr, out Thread);
if (!ThreadsByTpidr.TryGetValue(Tpidr, out HThread Thread))
{
Logging.Error($"Thread with TPIDR 0x{Tpidr:x16} not found!");
}
return Thread;
}
public void Dispose()

View file

@ -8,7 +8,7 @@ namespace Ryujinx.OsHle.Services
{
public static long TimeGetStandardUserSystemClock(ServiceCtx Context)
{
MakeObject(Context, new ISystemClock(SystemClockType.Standard));
MakeObject(Context, new ISystemClock(SystemClockType.User));
return 0;
}

View file

@ -7,7 +7,7 @@ namespace Ryujinx.OsHle.Svc
{
partial class SvcHandler
{
private delegate void SvcFunc(ARegisters Registers);
private delegate void SvcFunc(AThreadState ThreadState);
private Dictionary<int, SvcFunc> SvcFuncs;
@ -25,6 +25,7 @@ namespace Ryujinx.OsHle.Svc
{ 0x03, SvcSetMemoryAttribute },
{ 0x04, SvcMapMemory },
{ 0x06, SvcQueryMemory },
{ 0x07, SvcExitProcess },
{ 0x08, SvcCreateThread },
{ 0x09, SvcStartThread },
{ 0x0b, SvcSleepThread },
@ -60,15 +61,15 @@ namespace Ryujinx.OsHle.Svc
public void SvcCall(object sender, AInstExceptEventArgs e)
{
ARegisters Registers = (ARegisters)sender;
AThreadState ThreadState = (AThreadState)sender;
if (SvcFuncs.TryGetValue(e.Id, out SvcFunc Func))
{
Logging.Trace($"(Thread {Registers.ThreadId}) {Func.Method.Name} called.");
Logging.Trace($"(Thread {ThreadState.ThreadId}) {Func.Method.Name} called.");
Func(Registers);
Func(ThreadState);
Logging.Trace($"(Thread {Registers.ThreadId}) {Func.Method.Name} ended.");
Logging.Trace($"(Thread {ThreadState.ThreadId}) {Func.Method.Name} ended.");
}
else
{

View file

@ -6,43 +6,43 @@ namespace Ryujinx.OsHle.Svc
{
partial class SvcHandler
{
private void SvcSetHeapSize(ARegisters Registers)
private void SvcSetHeapSize(AThreadState ThreadState)
{
uint Size = (uint)Registers.X1;
uint Size = (uint)ThreadState.X1;
Memory.Manager.SetHeapSize(Size, (int)MemoryType.Heap);
Registers.X0 = (int)SvcResult.Success;
Registers.X1 = (ulong)Memory.Manager.HeapAddr;
ThreadState.X0 = (int)SvcResult.Success;
ThreadState.X1 = (ulong)Memory.Manager.HeapAddr;
}
private void SvcSetMemoryAttribute(ARegisters Registers)
private void SvcSetMemoryAttribute(AThreadState ThreadState)
{
long Position = (long)Registers.X0;
long Size = (long)Registers.X1;
int State0 = (int)Registers.X2;
int State1 = (int)Registers.X3;
long Position = (long)ThreadState.X0;
long Size = (long)ThreadState.X1;
int State0 = (int)ThreadState.X2;
int State1 = (int)ThreadState.X3;
//TODO
Registers.X0 = (int)SvcResult.Success;
ThreadState.X0 = (int)SvcResult.Success;
}
private void SvcMapMemory(ARegisters Registers)
private void SvcMapMemory(AThreadState ThreadState)
{
long Dst = (long)Registers.X0;
long Src = (long)Registers.X1;
long Size = (long)Registers.X2;
long Dst = (long)ThreadState.X0;
long Src = (long)ThreadState.X1;
long Size = (long)ThreadState.X2;
Memory.Manager.MapMirror(Src, Dst, Size, (int)MemoryType.MappedMemory);
Registers.X0 = (int)SvcResult.Success;
ThreadState.X0 = (int)SvcResult.Success;
}
private void SvcQueryMemory(ARegisters Registers)
private void SvcQueryMemory(AThreadState ThreadState)
{
long InfoPtr = (long)Registers.X0;
long Position = (long)Registers.X2;
long InfoPtr = (long)ThreadState.X0;
long Position = (long)ThreadState.X2;
AMemoryMapInfo MapInfo = Memory.Manager.GetMapInfo(Position);
@ -59,55 +59,52 @@ namespace Ryujinx.OsHle.Svc
//TODO: X1.
Registers.X0 = (int)SvcResult.Success;
Registers.X1 = 0;
ThreadState.X0 = (int)SvcResult.Success;
ThreadState.X1 = 0;
}
private void SvcMapSharedMemory(ARegisters Registers)
private void SvcMapSharedMemory(AThreadState ThreadState)
{
int Handle = (int)Registers.X0;
long Position = (long)Registers.X1;
long Size = (long)Registers.X2;
int Perm = (int)Registers.X3;
int Handle = (int)ThreadState.X0;
long Src = (long)ThreadState.X1;
long Size = (long)ThreadState.X2;
int Perm = (int)ThreadState.X3;
HSharedMem HndData = Ns.Os.Handles.GetData<HSharedMem>(Handle);
HSharedMem SharedMem = Ns.Os.Handles.GetData<HSharedMem>(Handle);
if (HndData != null)
if (SharedMem != null)
{
long Src = Position;
long Dst = HndData.PhysPos;
SharedMem.AddVirtualPosition(Src);
HndData.VirtPos = Src;
Memory.Manager.MapPhys(Src, Size, (int)MemoryType.SharedMemory, (AMemoryPerm)Perm);
Memory.Manager.MapPhys(Position, Size, (int)MemoryType.SharedMemory, (AMemoryPerm)Perm);
Registers.X0 = (int)SvcResult.Success;
ThreadState.X0 = (int)SvcResult.Success;
}
//TODO: Error codes.
}
private void SvcUnmapSharedMemory(ARegisters Registers)
private void SvcUnmapSharedMemory(AThreadState ThreadState)
{
int Handle = (int)Registers.X0;
long Position = (long)Registers.X1;
long Size = (long)Registers.X2;
int Handle = (int)ThreadState.X0;
long Position = (long)ThreadState.X1;
long Size = (long)ThreadState.X2;
HSharedMem HndData = Ns.Os.Handles.GetData<HSharedMem>(Handle);
if (HndData != null)
{
Registers.X0 = (int)SvcResult.Success;
ThreadState.X0 = (int)SvcResult.Success;
}
//TODO: Error codes.
}
private void SvcCreateTransferMemory(ARegisters Registers)
private void SvcCreateTransferMemory(AThreadState ThreadState)
{
long Position = (long)Registers.X1;
long Size = (long)Registers.X2;
int Perm = (int)Registers.X3;
long Position = (long)ThreadState.X1;
long Size = (long)ThreadState.X2;
int Perm = (int)ThreadState.X3;
AMemoryMapInfo MapInfo = Memory.Manager.GetMapInfo(Position);
@ -117,8 +114,8 @@ namespace Ryujinx.OsHle.Svc
int Handle = Ns.Os.Handles.GenerateId(HndData);
Registers.X1 = (ulong)Handle;
Registers.X0 = (int)SvcResult.Success;
ThreadState.X1 = (ulong)Handle;
ThreadState.X0 = (int)SvcResult.Success;
}
}
}

View file

@ -4,60 +4,57 @@ using Ryujinx.OsHle.Exceptions;
using Ryujinx.OsHle.Handles;
using Ryujinx.OsHle.Ipc;
using System;
using System.Threading;
namespace Ryujinx.OsHle.Svc
{
partial class SvcHandler
{
private void SvcCloseHandle(ARegisters Registers)
private void SvcExitProcess(AThreadState ThreadState) => Ns.Os.ExitProcess(ThreadState.ProcessId);
private void SvcCloseHandle(AThreadState ThreadState)
{
int Handle = (int)Registers.X0;
int Handle = (int)ThreadState.X0;
Ns.Os.CloseHandle(Handle);
Registers.X0 = (int)SvcResult.Success;
ThreadState.X0 = (int)SvcResult.Success;
}
private void SvcResetSignal(ARegisters Registers)
private void SvcResetSignal(AThreadState ThreadState)
{
int Handle = (int)Registers.X0;
int Handle = (int)ThreadState.X0;
//TODO: Implement events.
Registers.X0 = (int)SvcResult.Success;
ThreadState.X0 = (int)SvcResult.Success;
}
private void SvcWaitSynchronization(ARegisters Registers)
private void SvcWaitSynchronization(AThreadState ThreadState)
{
long HandlesPtr = (long)Registers.X0;
int HandlesCount = (int)Registers.X2;
long Timeout = (long)Registers.X3;
long HandlesPtr = (long)ThreadState.X0;
int HandlesCount = (int)ThreadState.X2;
long Timeout = (long)ThreadState.X3;
//TODO: Implement events.
//Logging.Info($"SvcWaitSynchronization Thread {Registers.ThreadId}");
HThread CurrThread = Process.GetThread(ThreadState.Tpidr);
if (Process.TryGetThread(Registers.Tpidr, out HThread Thread))
{
Process.Scheduler.Yield(Thread);
}
else
{
Logging.Error($"Thread with TPIDR_EL0 0x{Registers.Tpidr:x16} not found!");
Process.Scheduler.Suspend(CurrThread.ProcessorId);
Process.Scheduler.Resume(CurrThread);
ThreadState.X0 = (int)SvcResult.Success;
}
Registers.X0 = (int)SvcResult.Success;
private void SvcGetSystemTick(AThreadState ThreadState)
{
ThreadState.X0 = (ulong)ThreadState.CntpctEl0;
}
private void SvcGetSystemTick(ARegisters Registers)
private void SvcConnectToNamedPort(AThreadState ThreadState)
{
Registers.X0 = (ulong)Registers.CntpctEl0;
}
private void SvcConnectToNamedPort(ARegisters Registers)
{
long StackPtr = (long)Registers.X0;
long NamePtr = (long)Registers.X1;
long StackPtr = (long)ThreadState.X0;
long NamePtr = (long)ThreadState.X1;
string Name = AMemoryHelper.ReadAsciiString(Memory, NamePtr, 8);
@ -66,37 +63,41 @@ namespace Ryujinx.OsHle.Svc
HSession Session = new HSession(Name);
Registers.X1 = (ulong)Ns.Os.Handles.GenerateId(Session);
Registers.X0 = (int)SvcResult.Success;
ThreadState.X1 = (ulong)Ns.Os.Handles.GenerateId(Session);
ThreadState.X0 = (int)SvcResult.Success;
}
private void SvcSendSyncRequest(ARegisters Registers)
private void SvcSendSyncRequest(AThreadState ThreadState)
{
SendSyncRequest(Registers, false);
SendSyncRequest(ThreadState, false);
}
private void SvcSendSyncRequestWithUserBuffer(ARegisters Registers)
private void SvcSendSyncRequestWithUserBuffer(AThreadState ThreadState)
{
SendSyncRequest(Registers, true);
SendSyncRequest(ThreadState, true);
}
private void SendSyncRequest(ARegisters Registers, bool UserBuffer)
private void SendSyncRequest(AThreadState ThreadState, bool UserBuffer)
{
long CmdPtr = Registers.Tpidr;
long CmdPtr = ThreadState.Tpidr;
long Size = 0x100;
int Handle = 0;
if (UserBuffer)
{
CmdPtr = (long)Registers.X0;
Size = (long)Registers.X1;
Handle = (int)Registers.X2;
CmdPtr = (long)ThreadState.X0;
Size = (long)ThreadState.X1;
Handle = (int)ThreadState.X2;
}
else
{
Handle = (int)Registers.X0;
Handle = (int)ThreadState.X0;
}
HThread CurrThread = Process.GetThread(ThreadState.Tpidr);
Process.Scheduler.Suspend(CurrThread.ProcessorId);
byte[] CmdData = AMemoryHelper.ReadBytes(Memory, CmdPtr, (int)Size);
HSession Session = Ns.Os.Handles.GetData<HSession>(Handle);
@ -109,69 +110,73 @@ namespace Ryujinx.OsHle.Svc
byte[] Response = AMemoryHelper.ReadBytes(Memory, CmdPtr, (int)Size);
Registers.X0 = (int)SvcResult.Success;
ThreadState.X0 = (int)SvcResult.Success;
}
else
{
Registers.X0 = (int)SvcResult.ErrBadIpcReq;
}
ThreadState.X0 = (int)SvcResult.ErrBadIpcReq;
}
private void SvcBreak(ARegisters Registers)
Thread.Yield();
Process.Scheduler.Resume(CurrThread);
}
private void SvcBreak(AThreadState ThreadState)
{
long Reason = (long)Registers.X0;
long Unknown = (long)Registers.X1;
long Info = (long)Registers.X2;
long Reason = (long)ThreadState.X0;
long Unknown = (long)ThreadState.X1;
long Info = (long)ThreadState.X2;
throw new GuestBrokeExecutionException();
}
private void SvcOutputDebugString(ARegisters Registers)
private void SvcOutputDebugString(AThreadState ThreadState)
{
long Position = (long)Registers.X0;
long Size = (long)Registers.X1;
long Position = (long)ThreadState.X0;
long Size = (long)ThreadState.X1;
string Str = AMemoryHelper.ReadAsciiString(Memory, Position, (int)Size);
Logging.Info($"SvcOutputDebugString: {Str}");
Registers.X0 = (int)SvcResult.Success;
ThreadState.X0 = (int)SvcResult.Success;
}
private void SvcGetInfo(ARegisters Registers)
private void SvcGetInfo(AThreadState ThreadState)
{
long StackPtr = (long)Registers.X0;
int InfoType = (int)Registers.X1;
long Handle = (long)Registers.X2;
int InfoId = (int)Registers.X3;
long StackPtr = (long)ThreadState.X0;
int InfoType = (int)ThreadState.X1;
long Handle = (long)ThreadState.X2;
int InfoId = (int)ThreadState.X3;
//Fail for info not available on older Kernel versions.
if (InfoType == 18 ||
InfoType == 19)
{
Registers.X0 = (int)SvcResult.ErrBadInfo;
ThreadState.X0 = (int)SvcResult.ErrBadInfo;
return;
}
switch (InfoType)
{
case 2: Registers.X1 = GetMapRegionBaseAddr(); break;
case 3: Registers.X1 = GetMapRegionSize(); break;
case 4: Registers.X1 = GetHeapRegionBaseAddr(); break;
case 5: Registers.X1 = GetHeapRegionSize(); break;
case 6: Registers.X1 = GetTotalMem(); break;
case 7: Registers.X1 = GetUsedMem(); break;
case 11: Registers.X1 = GetRnd64(); break;
case 12: Registers.X1 = GetAddrSpaceBaseAddr(); break;
case 13: Registers.X1 = GetAddrSpaceSize(); break;
case 14: Registers.X1 = GetMapRegionBaseAddr(); break;
case 15: Registers.X1 = GetMapRegionSize(); break;
case 2: ThreadState.X1 = GetMapRegionBaseAddr(); break;
case 3: ThreadState.X1 = GetMapRegionSize(); break;
case 4: ThreadState.X1 = GetHeapRegionBaseAddr(); break;
case 5: ThreadState.X1 = GetHeapRegionSize(); break;
case 6: ThreadState.X1 = GetTotalMem(); break;
case 7: ThreadState.X1 = GetUsedMem(); break;
case 11: ThreadState.X1 = GetRnd64(); break;
case 12: ThreadState.X1 = GetAddrSpaceBaseAddr(); break;
case 13: ThreadState.X1 = GetAddrSpaceSize(); break;
case 14: ThreadState.X1 = GetMapRegionBaseAddr(); break;
case 15: ThreadState.X1 = GetMapRegionSize(); break;
default: throw new NotImplementedException($"SvcGetInfo: {InfoType} {Handle} {InfoId}");
}
Registers.X0 = (int)SvcResult.Success;
ThreadState.X0 = (int)SvcResult.Success;
}
private ulong GetTotalMem()

View file

@ -1,23 +1,23 @@
using ChocolArm64.State;
using Ryujinx.OsHle.Handles;
using System.Threading;
namespace Ryujinx.OsHle.Svc
{
partial class SvcHandler
{
private void SvcCreateThread(ARegisters Registers)
private void SvcCreateThread(AThreadState ThreadState)
{
long EntryPoint = (long)Registers.X1;
long ArgsPtr = (long)Registers.X2;
long StackTop = (long)Registers.X3;
int Priority = (int)Registers.X4;
int ProcessorId = (int)Registers.X5;
long EntryPoint = (long)ThreadState.X1;
long ArgsPtr = (long)ThreadState.X2;
long StackTop = (long)ThreadState.X3;
int Priority = (int)ThreadState.X4;
int ProcessorId = (int)ThreadState.X5;
if (Ns.Os.TryGetProcess(Registers.ProcessId, out Process Process))
if (Ns.Os.TryGetProcess(ThreadState.ProcessId, out Process Process))
{
if (ProcessorId == -2)
{
//TODO: Get this value from the NPDM file.
ProcessorId = 0;
}
@ -28,16 +28,16 @@ namespace Ryujinx.OsHle.Svc
Priority,
ProcessorId);
Registers.X0 = (int)SvcResult.Success;
Registers.X1 = (ulong)Handle;
ThreadState.X0 = (int)SvcResult.Success;
ThreadState.X1 = (ulong)Handle;
}
//TODO: Error codes.
}
private void SvcStartThread(ARegisters Registers)
private void SvcStartThread(AThreadState ThreadState)
{
int Handle = (int)Registers.X0;
int Handle = (int)ThreadState.X0;
HThread Thread = Ns.Os.Handles.GetData<HThread>(Handle);
@ -45,38 +45,38 @@ namespace Ryujinx.OsHle.Svc
{
Process.Scheduler.StartThread(Thread);
Registers.X0 = (int)SvcResult.Success;
ThreadState.X0 = (int)SvcResult.Success;
}
//TODO: Error codes.
}
private void SvcSleepThread(ARegisters Registers)
private void SvcSleepThread(AThreadState ThreadState)
{
ulong NanoSecs = Registers.X0;
ulong NanoSecs = ThreadState.X0;
if (Process.TryGetThread(Registers.Tpidr, out HThread CurrThread))
HThread CurrThread = Process.GetThread(ThreadState.Tpidr);
if (NanoSecs == 0)
{
Process.Scheduler.Yield(CurrThread);
}
else
{
Logging.Error($"Thread with TPIDR_EL0 0x{Registers.Tpidr:x16} not found!");
Process.Scheduler.WaitForSignal(CurrThread, (int)(NanoSecs / 1000000));
}
}
Thread.Sleep((int)(NanoSecs / 1000000));
}
private void SvcGetThreadPriority(ARegisters Registers)
private void SvcGetThreadPriority(AThreadState ThreadState)
{
int Handle = (int)Registers.X1;
int Handle = (int)ThreadState.X1;
HThread Thread = Ns.Os.Handles.GetData<HThread>(Handle);
if (Thread != null)
{
Registers.X1 = (ulong)Thread.Priority;
Registers.X0 = (int)SvcResult.Success;
ThreadState.X1 = (ulong)Thread.Priority;
ThreadState.X0 = (int)SvcResult.Success;
}
//TODO: Error codes.

View file

@ -1,4 +1,3 @@
using ChocolArm64;
using ChocolArm64.State;
using Ryujinx.OsHle.Handles;
@ -6,11 +5,11 @@ namespace Ryujinx.OsHle.Svc
{
partial class SvcHandler
{
private void SvcArbitrateLock(ARegisters Registers)
private void SvcArbitrateLock(AThreadState ThreadState)
{
int OwnerThreadHandle = (int)Registers.X0;
long MutexAddress = (long)Registers.X1;
int RequestingThreadHandle = (int)Registers.X2;
int OwnerThreadHandle = (int)ThreadState.X0;
long MutexAddress = (long)ThreadState.X1;
int RequestingThreadHandle = (int)ThreadState.X2;
HThread RequestingThread = Ns.Os.Handles.GetData<HThread>(RequestingThreadHandle);
@ -20,34 +19,35 @@ namespace Ryujinx.OsHle.Svc
M.WaitForLock(RequestingThread, RequestingThreadHandle);
Registers.X0 = (int)SvcResult.Success;
ThreadState.X0 = (int)SvcResult.Success;
}
private void SvcArbitrateUnlock(ARegisters Registers)
private void SvcArbitrateUnlock(AThreadState ThreadState)
{
long MutexAddress = (long)Registers.X0;
long MutexAddress = (long)ThreadState.X0;
if (Ns.Os.Mutexes.TryGetValue(MutexAddress, out Mutex M))
{
M.Unlock();
}
Registers.X0 = (int)SvcResult.Success;
ThreadState.X0 = (int)SvcResult.Success;
}
private void SvcWaitProcessWideKeyAtomic(ARegisters Registers)
private void SvcWaitProcessWideKeyAtomic(AThreadState ThreadState)
{
long MutexAddress = (long)Registers.X0;
long CondVarAddress = (long)Registers.X1;
int ThreadHandle = (int)Registers.X2;
long Timeout = (long)Registers.X3;
long MutexAddress = (long)ThreadState.X0;
long CondVarAddress = (long)ThreadState.X1;
int ThreadHandle = (int)ThreadState.X2;
long Timeout = (long)ThreadState.X3;
HThread Thread = Ns.Os.Handles.GetData<HThread>(ThreadHandle);
if (Ns.Os.Mutexes.TryGetValue(MutexAddress, out Mutex M))
{
Mutex M = new Mutex(Process, MutexAddress, ThreadHandle);
M = Ns.Os.Mutexes.GetOrAdd(MutexAddress, M);
M.GiveUpLock(ThreadHandle);
}
CondVar Cv = new CondVar(Process, CondVarAddress, Timeout);
@ -55,26 +55,24 @@ namespace Ryujinx.OsHle.Svc
Cv.WaitForSignal(Thread);
M = new Mutex(Process, MutexAddress, ThreadHandle);
M = Ns.Os.Mutexes.GetOrAdd(MutexAddress, M);
M.WaitForLock(Thread, ThreadHandle);
Registers.X0 = (int)SvcResult.Success;
ThreadState.X0 = (int)SvcResult.Success;
}
private void SvcSignalProcessWideKey(ARegisters Registers)
private void SvcSignalProcessWideKey(AThreadState ThreadState)
{
long CondVarAddress = (long)Registers.X0;
int Count = (int)Registers.X1;
long CondVarAddress = (long)ThreadState.X0;
int Count = (int)ThreadState.X1;
HThread CurrThread = Process.GetThread(ThreadState.Tpidr);
if (Ns.Os.CondVars.TryGetValue(CondVarAddress, out CondVar Cv))
{
Cv.SetSignal(Count);
Cv.SetSignal(CurrThread, Count);
}
Registers.X0 = (int)SvcResult.Success;
ThreadState.X0 = (int)SvcResult.Success;
}
}
}

47
Ryujinx/Ryujinx.conf Normal file
View 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

View file

@ -2,8 +2,8 @@
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp2.0</TargetFramework>
<RuntimeIdentifier>win10-x64</RuntimeIdentifier>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<RuntimeIdentifiers>win10-x64;osx-x64</RuntimeIdentifiers>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="OpenTK.NETCore" Version="1.1.2749.6433" />

View file

@ -14,6 +14,9 @@ namespace Ryujinx
internal NsGpu Gpu { get; private set; }
internal Horizon Os { get; private set; }
internal VirtualFs VFs { get; private set; }
internal Hid Hid { get; private set; }
public event EventHandler Finish;
public Switch(IGalRenderer Renderer)
{
@ -22,6 +25,15 @@ namespace Ryujinx
Gpu = new NsGpu(Renderer);
Os = new Horizon(this);
VFs = new VirtualFs();
Hid = new Hid(this);
}
internal virtual void OnFinish(EventArgs e)
{
if (Finish != null)
{
Finish(this, e);
}
}
public void Dispose()

View file

@ -6,7 +6,6 @@ using Gal;
using OpenTK;
using OpenTK.Graphics;
using OpenTK.Graphics.OpenGL;
using Ryujinx.OsHle;
using System;
namespace Ryujinx
@ -276,76 +275,62 @@ void main(void) {
protected override void OnUpdateFrame(FrameEventArgs e)
{
unsafe
HidControllerKeys CurrentButton = 0;
JoystickPosition LeftJoystick;
JoystickPosition RightJoystick;
if (Keyboard[OpenTK.Input.Key.Escape]) this.Exit();
//RightJoystick
int LeftJoystickDX = 0;
int LeftJoystickDY = 0;
if (Keyboard[(OpenTK.Input.Key)Config.FakeJoyCon.Left.StickUp]) LeftJoystickDY = short.MaxValue;
if (Keyboard[(OpenTK.Input.Key)Config.FakeJoyCon.Left.StickDown]) LeftJoystickDY = -short.MaxValue;
if (Keyboard[(OpenTK.Input.Key)Config.FakeJoyCon.Left.StickLeft]) LeftJoystickDX = -short.MaxValue;
if (Keyboard[(OpenTK.Input.Key)Config.FakeJoyCon.Left.StickRight]) LeftJoystickDX = short.MaxValue;
if (Keyboard[(OpenTK.Input.Key)Config.FakeJoyCon.Left.StickButton]) CurrentButton |= HidControllerKeys.KEY_LSTICK;
//LeftButtons
if (Keyboard[(OpenTK.Input.Key)Config.FakeJoyCon.Left.DPadUp]) CurrentButton |= HidControllerKeys.KEY_DUP;
if (Keyboard[(OpenTK.Input.Key)Config.FakeJoyCon.Left.DPadDown]) CurrentButton |= HidControllerKeys.KEY_DDOWN;
if (Keyboard[(OpenTK.Input.Key)Config.FakeJoyCon.Left.DPadLeft]) CurrentButton |= HidControllerKeys.KEY_DLEFT;
if (Keyboard[(OpenTK.Input.Key)Config.FakeJoyCon.Left.DPadRight]) CurrentButton |= HidControllerKeys.KEY_DRIGHT;
if (Keyboard[(OpenTK.Input.Key)Config.FakeJoyCon.Left.ButtonMinus]) CurrentButton |= HidControllerKeys.KEY_MINUS;
if (Keyboard[(OpenTK.Input.Key)Config.FakeJoyCon.Left.ButtonL]) CurrentButton |= HidControllerKeys.KEY_L;
if (Keyboard[(OpenTK.Input.Key)Config.FakeJoyCon.Left.ButtonZL]) CurrentButton |= HidControllerKeys.KEY_ZL;
//RightJoystick
int RightJoystickDX = 0;
int RightJoystickDY = 0;
if (Keyboard[(OpenTK.Input.Key)Config.FakeJoyCon.Right.StickUp]) RightJoystickDY = short.MaxValue;
if (Keyboard[(OpenTK.Input.Key)Config.FakeJoyCon.Right.StickDown]) RightJoystickDY = -short.MaxValue;
if (Keyboard[(OpenTK.Input.Key)Config.FakeJoyCon.Right.StickLeft]) RightJoystickDX = -short.MaxValue;
if (Keyboard[(OpenTK.Input.Key)Config.FakeJoyCon.Right.StickRight]) RightJoystickDX = short.MaxValue;
if (Keyboard[(OpenTK.Input.Key)Config.FakeJoyCon.Right.StickButton]) CurrentButton |= HidControllerKeys.KEY_RSTICK;
//RightButtons
if (Keyboard[(OpenTK.Input.Key)Config.FakeJoyCon.Right.ButtonA]) CurrentButton |= HidControllerKeys.KEY_A;
if (Keyboard[(OpenTK.Input.Key)Config.FakeJoyCon.Right.ButtonB]) CurrentButton |= HidControllerKeys.KEY_B;
if (Keyboard[(OpenTK.Input.Key)Config.FakeJoyCon.Right.ButtonX]) CurrentButton |= HidControllerKeys.KEY_X;
if (Keyboard[(OpenTK.Input.Key)Config.FakeJoyCon.Right.ButtonY]) CurrentButton |= HidControllerKeys.KEY_Y;
if (Keyboard[(OpenTK.Input.Key)Config.FakeJoyCon.Right.ButtonPlus]) CurrentButton |= HidControllerKeys.KEY_PLUS;
if (Keyboard[(OpenTK.Input.Key)Config.FakeJoyCon.Right.ButtonR]) CurrentButton |= HidControllerKeys.KEY_R;
if (Keyboard[(OpenTK.Input.Key)Config.FakeJoyCon.Right.ButtonZR]) CurrentButton |= HidControllerKeys.KEY_ZR;
LeftJoystick = new JoystickPosition
{
long HidOffset = Ns.Os.GetVirtHidOffset();
DX = LeftJoystickDX,
DY = LeftJoystickDY
};
if (HidOffset == 0 || HidOffset + Horizon.HidSize > uint.MaxValue)
RightJoystick = new JoystickPosition
{
return;
}
DX = RightJoystickDX,
DY = RightJoystickDY
};
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();
}
//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)

View file

@ -50,6 +50,11 @@ namespace Ryujinx
using (GLScreen Screen = new GLScreen(Ns, Renderer))
{
Ns.Finish += (Sender, Args) =>
{
Screen.Exit();
};
Screen.Run(60.0);
}